diff --git a/components/topbar/src/application-drawer.styles.ts b/components/topbar/src/application-drawer-v1.styles.ts
similarity index 98%
rename from components/topbar/src/application-drawer.styles.ts
rename to components/topbar/src/application-drawer-v1.styles.ts
index 805bdd37..82a65062 100644
--- a/components/topbar/src/application-drawer.styles.ts
+++ b/components/topbar/src/application-drawer-v1.styles.ts
@@ -2,7 +2,7 @@ import { makeStyles, shorthands, tokens } from "@fluentui/react-components";
const triggerSpacingWithGroup = tokens.spacingHorizontalS;
-export const useApplicationDrawrStyles = makeStyles({
+export const useApplicationDrawerStyles = makeStyles({
drawer: {
...shorthands.borderRight(0),
},
diff --git a/components/topbar/src/application-drawer-v1.tsx b/components/topbar/src/application-drawer-v1.tsx
new file mode 100644
index 00000000..bf8eb0a1
--- /dev/null
+++ b/components/topbar/src/application-drawer-v1.tsx
@@ -0,0 +1,302 @@
+import { ArrowRightRegular, Dismiss20Regular } from "@fluentui/react-icons";
+import React, { useState } from "react";
+import { ApplicationArea } from "./top-bar.types";
+import { useTranslation } from "./translation-context";
+import {
+ Body1,
+ Body1Strong,
+ Button,
+ Caption1Stronger,
+ Divider,
+ Drawer,
+ DrawerBody,
+ DrawerHeader,
+ Link,
+ mergeClasses,
+ tokens,
+} from "@fluentui/react-components";
+import { useApplicationDrawerStyles } from "./application-drawer-v1.styles";
+import {
+ ApplicationAreaFlaworedIcon,
+ ApplicationAreaIcon,
+ applicationAreaLabel,
+ findCurrent,
+} from "./application-utils";
+import {
+ ApplicationDrawerContent,
+ ApplicationDrawerProps,
+ SingleApplicationDrawerContent,
+} from "./application-drawer.types";
+import { useApplicationStyles } from "./application.styles";
+
+const DrawerTrigger = (
+ { setIsOpen, applicationArea, currentSelection }: {
+ setIsOpen: (isOpen: boolean) => void;
+ applicationArea: ApplicationArea;
+ currentSelection: SingleApplicationDrawerContent | undefined;
+ }
+) => {
+ const styles = useApplicationDrawerStyles();
+ const [hover, setHover] = useState(false);
+
+ return (
+
+
+
+ );
+};
+
+const ApplicationGroupTitle = ({ application }: {
+ application: ApplicationDrawerContent;
+}): JSX.Element => {
+ const styles = useApplicationDrawerStyles();
+ const appStyles = useApplicationStyles();
+
+ return (
+
+
+ {application.icon}
+
+
+ {application.label.toLocaleUpperCase()}
+
+
+ );
+};
+
+const iconConverter = (
+ icon: JSX.Element,
+ isCurrent: boolean,
+ applicationArea: ApplicationArea
+) => {
+ return isCurrent
+ ? (
+
+ )
+ : (
+ icon
+ );
+};
+
+const SingleApplication = ({
+ application,
+ currentSelectionId,
+ onChange,
+ applicationArea,
+}: {
+ application: SingleApplicationDrawerContent;
+ currentSelectionId: string;
+ onChange: (id: string) => void;
+ applicationArea: ApplicationArea;
+}): JSX.Element => {
+ const styles = useApplicationDrawerStyles();
+
+ return (
+
+ );
+};
+
+const ApplicationWithChildren = ({
+ application,
+ currentSelectionId,
+ onChange,
+ applicationArea,
+}: {
+ application: ApplicationDrawerContent;
+ currentSelectionId: string;
+ onChange: (id: string) => void;
+ applicationArea: ApplicationArea;
+}): JSX.Element => {
+ const styles = useApplicationDrawerStyles();
+
+ return (
+ <>
+
+
+ {application.children?.map((child) => {
+ return (
+
+ );
+ })}
+
+ >
+ );
+};
+
+export const ApplicationDrawerV1 = ({
+ link,
+ title,
+ applicationId,
+ content,
+ onChange,
+ applicationArea,
+}: ApplicationDrawerProps & { applicationArea: ApplicationArea }) => {
+ const { t } = useTranslation();
+ const [isOpen, setIsOpen] = useState(false);
+ const styles = useApplicationDrawerStyles();
+ const currentSelection = findCurrent(applicationId, content);
+
+ const onClickItem = (id: string) => {
+ if (id !== currentSelection?.id) {
+ onChange(id);
+ }
+ setIsOpen(false);
+ };
+
+ return (
+ <>
+
+
+ setIsOpen(open)}
+ >
+ setIsOpen(false)}
+ >
+
+
+
+ {applicationAreaLabel(t, applicationArea)}
+
+
+ }
+ onClick={() => setIsOpen(false)}
+ />
+
+
+
+ {link && (
+
+ )}
+
+ {title}
+
+ {content?.map((c) => {
+ return (
+
+ {c.children
+ ? (
+
+ )
+ : (
+
+ )}
+
+
+ );
+ })}
+
+
+
+ >
+ );
+};
diff --git a/components/topbar/src/application-drawer-v2.styles.ts b/components/topbar/src/application-drawer-v2.styles.ts
new file mode 100644
index 00000000..82a3aae6
--- /dev/null
+++ b/components/topbar/src/application-drawer-v2.styles.ts
@@ -0,0 +1,116 @@
+import {
+ makeStyles,
+ shorthands,
+ tabClassNames,
+ tokens,
+} from "@fluentui/react-components";
+import {
+ iconFilledClassName,
+ iconRegularClassName,
+} from "@fluentui/react-icons";
+
+export const useApplicationDrawerV2Styles = makeStyles({
+ drawer: {
+ ...shorthands.borderRight(0),
+ backgroundColor: tokens.colorNeutralBackground2,
+ },
+ drawerTriggerRoot: {
+ display: "flex",
+ alignItems: "center",
+ flexShrink: 0,
+ paddingLeft: tokens.spacingHorizontalSNudge,
+ color: tokens.colorNeutralForeground2,
+ },
+ drawerTriggerGap: {
+ width: "26px",
+ },
+ drawerTriggerLabel: {
+ marginLeft: tokens.spacingHorizontalXXS,
+ },
+ header: {
+ display: "flex",
+ flexDirection: "row",
+ justifyContent: "space-between",
+ alignItems: "center",
+ height: "46px",
+ padding: "0 8px 0 19px",
+ },
+ body: {
+ paddingLeft: tokens.spacingHorizontalM,
+ paddingRight: tokens.spacingHorizontalM,
+ },
+ linkWrapper: {
+ display: "flex",
+ justifyContent: "flex-end",
+ ...shorthands.padding(tokens.spacingVerticalS, 0),
+ },
+ link: {
+ display: "flex",
+ alignItems: "center",
+ ...shorthands.gap(tokens.spacingHorizontalXS),
+ },
+ content: {
+ display: "flex",
+ flexDirection: "column",
+ width: "100%",
+ paddingTop: "40px",
+ },
+ title: {
+ paddingLeft: tokens.spacingHorizontalSNudge,
+ paddingRight: tokens.spacingHorizontalSNudge,
+ paddingBottom: tokens.spacingVerticalL,
+ },
+ contentDivider: {
+ paddingTop: tokens.spacingVerticalS,
+ paddingBottom: tokens.spacingVerticalSNudge,
+ },
+ applicationGroupTitleText: {
+ color: tokens.colorNeutralForeground4,
+ paddingLeft: tokens.spacingHorizontalSNudge,
+ paddingTop: tokens.spacingVerticalS,
+ paddingBottom: tokens.spacingVerticalMNudge,
+ },
+ tabList: {
+ width: "100%",
+ marginRight: "32px",
+ },
+ tabWrap: {
+ width: "100%",
+ paddingLeft: tokens.spacingHorizontalSNudge,
+ boxSizing: "border-box",
+ },
+ tab: {
+ width: "100%",
+ display: "flex",
+ [`& .${tabClassNames.content}`]: {
+ display: "flex",
+ flex: 1,
+ },
+ padding: 0,
+ },
+ contentButton: {
+ flex: 1,
+ justifyContent: "flex-start",
+ "&:hover, &:active:hover": {
+ color: tokens.colorNeutralForeground2Hover,
+ },
+ },
+ regularIconOnHover: {
+ "&:hover": {
+ [`& .${iconFilledClassName}`]: {
+ display: "none",
+ },
+ [`& .${iconRegularClassName}`]: {
+ display: "inline",
+ },
+ },
+ },
+ filledIcon: {
+ [`& .${iconFilledClassName}`]: {
+ display: "inline",
+ },
+ [`& .${iconRegularClassName}`]: {
+ display: "none",
+ },
+ },
+});
diff --git a/components/topbar/src/application-drawer-v2.tsx b/components/topbar/src/application-drawer-v2.tsx
new file mode 100644
index 00000000..a095e790
--- /dev/null
+++ b/components/topbar/src/application-drawer-v2.tsx
@@ -0,0 +1,256 @@
+import React, { useState } from "react";
+import {
+ ApplicationDrawerContent,
+ ApplicationDrawerProps,
+ SingleApplicationDrawerContent,
+} from "./application-drawer.types";
+import { ApplicationArea } from "./top-bar.types";
+import {
+ Body1,
+ Body1Strong,
+ Button,
+ Caption1Stronger,
+ Divider,
+ Drawer,
+ DrawerBody,
+ DrawerHeader,
+ Link,
+ mergeClasses,
+ Tab,
+ TabList,
+ tokens,
+} from "@fluentui/react-components";
+import { useApplicationDrawerV2Styles } from "./application-drawer-v2.styles";
+import {
+ ArrowRightRegular,
+ bundleIcon,
+ Dismiss20Regular,
+ DividerTall16Filled,
+ GridDots20Filled,
+ GridDots20Regular,
+} from "@fluentui/react-icons";
+import { findCurrent } from "./application-utils";
+
+const GridDots20 = bundleIcon(GridDots20Filled, GridDots20Regular);
+
+const DrawerTrigger = ({ setIsOpen, currentSelection }: {
+ setIsOpen: (isOpen: boolean) => void;
+ currentSelection: SingleApplicationDrawerContent | undefined;
+}) => {
+ const styles = useApplicationDrawerV2Styles();
+ return (
+
+
}
+ onClick={() => setIsOpen(true)}
+ />
+
+
+ {currentSelection === undefined
+ ? null
+ : (
+ <>
+ {currentSelection?.triggerGroupShortName
+ ? (
+ <>
+
+ {currentSelection?.triggerGroupShortName}
+
+
+ >
+ )
+ : null}
+
+ {currentSelection.icon}
+
+ {currentSelection.label}
+
+ >
+ )}
+
+ );
+};
+
+const ApplicationGroupTitle = ({ application }: {
+ application: ApplicationDrawerContent;
+}): JSX.Element => {
+ const styles = useApplicationDrawerV2Styles();
+ return (
+
+ {application.label.toLocaleUpperCase()}
+
+ );
+};
+
+const SingleApplication = ({ application, onChange, isSelected }: {
+ application: SingleApplicationDrawerContent;
+ onChange: (id: string) => void;
+ isSelected: boolean;
+}): JSX.Element => {
+ const styles = useApplicationDrawerV2Styles();
+
+ const AppIcon = (): JSX.Element => {
+ return (
+
+ {application.icon}
+
+ );
+ };
+
+ const AppLabel = (): JSX.Element => {
+ return (isSelected
+ ? {application.label}
+ : {application.label});
+ };
+
+ return (
+
+
+ }
+ appearance="transparent"
+ onClick={(e) => {
+ e.preventDefault(); // Prevent navigation
+ onChange(application.id);
+ }}
+ >
+
+
+
+
+ );
+};
+
+const ApplicationWithChildren = ({ application, onChange, selectedId }: {
+ application: ApplicationDrawerContent;
+ onChange: (id: string) => void;
+ selectedId: string;
+}): JSX.Element => {
+ return (
+ <>
+
+ {application.children?.map((child) => {
+ return (
+
+ );
+ })}
+ >
+ );
+};
+
+export const ApplicationDrawerV2 = (
+ {
+ link,
+ title,
+ applicationId,
+ content,
+ onChange,
+ }: ApplicationDrawerProps & { applicationArea: ApplicationArea }
+) => {
+ const [isOpen, setIsOpen] = useState(false);
+ const styles = useApplicationDrawerV2Styles();
+ const currentSelection = findCurrent(applicationId, content);
+
+ const onClickItem = (id: string) => {
+ if (id !== currentSelection?.id) {
+ onChange(id);
+ }
+ setIsOpen(false);
+ };
+
+ return (
+ <>
+
+
+ setIsOpen(open)}
+ >
+
+
+ }
+ onClick={() => setIsOpen(false)}
+ />
+
+
+
+ {link && (
+
+ )}
+
+
{title}
+
+
+ {content?.map((c) => {
+ return (
+
+ {c.children
+ ? (
+
+ )
+ : (
+
+ )}
+
+
+
+ );
+ })}
+
+
+
+
+ >
+ );
+};
diff --git a/components/topbar/src/application-drawer.tsx b/components/topbar/src/application-drawer.tsx
index f8572d5a..a1e42aae 100644
--- a/components/topbar/src/application-drawer.tsx
+++ b/components/topbar/src/application-drawer.tsx
@@ -1,323 +1,15 @@
-import { ArrowRightRegular, Dismiss20Regular } from "@fluentui/react-icons";
-import React, { useState } from "react";
+import React from "react";
+import { ApplicationDrawerV1 } from "./application-drawer-v1";
+import { ApplicationDrawerProps } from "./application-drawer.types";
import { ApplicationArea } from "./top-bar.types";
-import { useTranslation } from "./translation-context";
-import {
- Body1,
- Body1Strong,
- Button,
- Caption1Stronger,
- Divider,
- Drawer,
- DrawerBody,
- DrawerHeader,
- Link,
- mergeClasses,
- tokens,
-} from "@fluentui/react-components";
-import { useApplicationDrawrStyles as useApplicationDrawerStyles } from "./application-drawer.styles";
-import {
- ApplicationAreaFlaworedIcon,
- ApplicationAreaIcon,
- applicationAreaLabel,
-} from "./application-utils";
-import {
- ApplicationDrawerContent,
- ApplicationDrawerProps,
- SingleApplicationDrawerContent,
-} from "./application-drawer.types";
-import { useApplicationStyles } from "./application.styles";
+import { ApplicationDrawerV2 } from "./application-drawer-v2";
-const DrawerTrigger = (
- { setIsOpen, applicationArea, currentSelection }: {
- setIsOpen: (isOpen: boolean) => void;
- applicationArea: ApplicationArea;
- currentSelection: SingleApplicationDrawerContent | undefined;
- }
+export const ApplicationDrawer = (
+ props: ApplicationDrawerProps & { applicationArea: ApplicationArea }
) => {
- const styles = useApplicationDrawerStyles();
- const [hover, setHover] = useState(false);
-
- return (
-
-
-
- );
-};
-
-const ApplicationGroupTitle = ({ application }: {
- application: ApplicationDrawerContent;
-}): JSX.Element => {
- const styles = useApplicationDrawerStyles();
- const appStyles = useApplicationStyles();
-
- return (
-
-
- {application.icon}
-
-
- {application.label.toLocaleUpperCase()}
-
-
- );
-};
-
-const findCurrent = (
- applicationId: string,
- content?: ApplicationDrawerContent[]
-): SingleApplicationDrawerContent | undefined => {
- if (!content) {
- return undefined;
+ if (props.version === "v2") {
+ return ;
+ } else {
+ return ;
}
-
- let currentApplication: SingleApplicationDrawerContent | undefined =
- undefined;
-
- content.forEach((c) => {
- if (c.id === applicationId) {
- currentApplication = c;
- }
- return c.children?.forEach((child) => {
- if (child.id === applicationId) {
- currentApplication = child;
- }
- });
- });
-
- return currentApplication;
-};
-
-const iconConverter = (
- icon: JSX.Element,
- isCurrent: boolean,
- applicationArea: ApplicationArea
-) => {
- return isCurrent
- ? (
-
- )
- : (
- icon
- );
-};
-
-const SingleApplication = ({
- application,
- currentSelectionId,
- onChange,
- applicationArea,
-}: {
- application: SingleApplicationDrawerContent;
- currentSelectionId: string;
- onChange: (id: string) => void;
- applicationArea: ApplicationArea;
-}): JSX.Element => {
- const styles = useApplicationDrawerStyles();
-
- return (
-
- );
-};
-
-const ApplicationWithChildren = ({
- application,
- currentSelectionId,
- onChange,
- applicationArea,
-}: {
- application: ApplicationDrawerContent;
- currentSelectionId: string;
- onChange: (id: string) => void;
- applicationArea: ApplicationArea;
-}): JSX.Element => {
- const styles = useApplicationDrawerStyles();
-
- return (
- <>
-
-
- {application.children?.map((child) => {
- return (
-
- );
- })}
-
- >
- );
-};
-
-export const ApplicationDrawer = ({
- link,
- title,
- applicationId,
- content,
- onChange,
- applicationArea,
-}: ApplicationDrawerProps & { applicationArea: ApplicationArea }) => {
- const { t } = useTranslation();
- const [isOpen, setIsOpen] = useState(false);
- const styles = useApplicationDrawerStyles();
- const currentSelection = findCurrent(applicationId, content);
-
- const onClickItem = (id: string) => {
- if (id !== currentSelection?.id) {
- onChange(id);
- }
- setIsOpen(false);
- };
-
- return (
- <>
-
-
- setIsOpen(open)}
- >
- setIsOpen(false)}
- >
-
-
-
- {applicationAreaLabel(t, applicationArea)}
-
-
- }
- onClick={() => setIsOpen(false)}
- />
-
-
-
- {link && (
-
- )}
-
- {title}
-
- {content?.map((c) => {
- return (
-
- {c.children
- ? (
-
- )
- : (
-
- )}
-
-
- );
- })}
-
-
-
- >
- );
};
diff --git a/components/topbar/src/application-drawer.types.ts b/components/topbar/src/application-drawer.types.ts
index e140401b..02869546 100644
--- a/components/topbar/src/application-drawer.types.ts
+++ b/components/topbar/src/application-drawer.types.ts
@@ -23,9 +23,19 @@ export type SingleApplicationDrawerContent = {
* Group label to show before triggerLabel
*/
triggerGroupShortName?: string;
+ /**
+ * Only available in `drawer v2`.
+ * If link is set, the selector in the drawer will have link properties.
+ */
+ link?: string;
};
export type ApplicationDrawerProps = {
+ /**
+ * Selecting version will change the style of the drawer.
+ * @default: `"v1"`
+ */
+ version?: "v1" | "v2";
link?: { text: string; url: string };
title: JSX.Element;
content?: ApplicationDrawerContent[];
diff --git a/components/topbar/src/application-utils.tsx b/components/topbar/src/application-utils.tsx
index 7164d3df..a1a429b8 100644
--- a/components/topbar/src/application-utils.tsx
+++ b/components/topbar/src/application-utils.tsx
@@ -13,6 +13,10 @@ import { ApplicationArea } from "./top-bar.types";
import { useApplicationStyles } from "./application.styles";
import { mergeClasses } from "@fluentui/react-components";
import { MySystems24Filled } from "@axiscommunications/fluent-icons";
+import {
+ ApplicationDrawerContent,
+ SingleApplicationDrawerContent,
+} from "./application-drawer.types";
enum appAreas {
MY_SYSTEMS = "mySystems",
@@ -108,3 +112,28 @@ export const applicationAreaLabel = (
) => {
return applicationArea ? t(applicationArea) : "";
};
+
+export const findCurrent = (
+ applicationId: string,
+ content?: ApplicationDrawerContent[]
+): SingleApplicationDrawerContent | undefined => {
+ if (!content) {
+ return undefined;
+ }
+
+ let currentApplication: SingleApplicationDrawerContent | undefined =
+ undefined;
+
+ content.forEach((c) => {
+ if (c.id === applicationId) {
+ currentApplication = c;
+ }
+ return c.children?.forEach((child) => {
+ if (child.id === applicationId) {
+ currentApplication = child;
+ }
+ });
+ });
+
+ return currentApplication;
+};
diff --git a/examples/src/components/top-bar.tsx b/examples/src/components/top-bar.tsx
index 133db898..af70de8f 100644
--- a/examples/src/components/top-bar.tsx
+++ b/examples/src/components/top-bar.tsx
@@ -4,6 +4,7 @@ import {
} from "@axiscommunications/fluent-theme";
import {
ApplicationDrawerContent,
+ ApplicationDrawerProps,
ApplicationOption,
LanguageOption,
OrganizationOption,
@@ -23,11 +24,11 @@ import {
} from "@fluentui/react-components";
import {
AddRegular,
- AddSubtractCircle20Filled,
- AddSubtractCircle20Regular,
AnimalCat20Filled,
AnimalCat20Regular,
+ BuildingBank20Regular,
bundleIcon,
+ Drawer24Filled,
FoodApple24Regular,
FoodCarrot20Filled,
FoodFish20Filled,
@@ -36,20 +37,16 @@ import {
Megaphone24Filled,
OpenRegular,
QuestionCircleRegular,
- ZoomFit16Filled,
- ZoomFit16Regular,
+ ZoomFit20Filled,
+ ZoomFit20Regular,
} from "@fluentui/react-icons";
import React, { useCallback, useState } from "react";
import { useAppContext } from "../context/ApplicationStateProvider";
const ApplicationIcon = bundleIcon(AnimalCat20Filled, AnimalCat20Regular);
-const ZoomIcon = bundleIcon(ZoomFit16Filled, ZoomFit16Regular);
+const ZoomIcon = bundleIcon(ZoomFit20Filled, ZoomFit20Regular);
const FishIcon = bundleIcon(FoodFish20Filled, FoodFish20Regular);
const CatIcon = bundleIcon(AnimalCat20Filled, AnimalCat20Regular);
-const AddSubIcon = bundleIcon(
- AddSubtractCircle20Filled,
- AddSubtractCircle20Regular
-);
const useStyles = makeStyles({
topBar: {
@@ -75,25 +72,27 @@ export const Navbar = () => {
const appDrawerContent: ApplicationDrawerContent[] = [
{
icon: ,
- label: "Zoo",
+ label: "Zoo camera station",
id: "zoo",
children: [
{
icon: ,
label: "Fisk",
id: "fisk",
+ link: "https://en.wikipedia.org/wiki/Bass_(fish)",
triggerGroupShortName: "FISH",
},
{
icon: ,
label: "Cat",
id: "cat",
+ link: "http://cats.com",
},
],
},
{
- icon: ,
- label: "Add",
+ icon: ,
+ label: "Add trigger trigger",
triggerLabel: "ADD trigger",
id: "add",
triggerGroupShortName: "ADD",
@@ -160,12 +159,16 @@ export const Navbar = () => {
);
const [showDrawer, setShowDrawer] = useState(true);
+ const [drawerVersion, setDrawerVersion] = useState<
+ ApplicationDrawerProps["version"]
+ >("v1");
return (
My Apps,
@@ -185,14 +188,30 @@ export const Navbar = () => {
}}
applicationArea={"mySystems"}
leftCustomContent={
- }
- iconPosition="before"
- onClick={() => setShowDrawer(!showDrawer)}
- >
- {showDrawer ? "Use application menu" : "Use application drawer"}
-
+ <>
+ }
+ iconPosition="before"
+ onClick={() => setShowDrawer(!showDrawer)}
+ >
+ {showDrawer ? "Use application menu" : "Use application drawer"}
+
+ }
+ onClick={() =>
+ setDrawerVersion((prev) => {
+ return prev === "v1"
+ ? "v2"
+ : prev === "v2"
+ ? undefined
+ : "v1";
+ })}
+ >
+ {`Drawer version: ${drawerVersion}`}
+
+ >
}
customContent={