Skip to content

Commit 9f8f478

Browse files
author
Marcin Wojciechowski
committed
Toolbar refactoring
1 parent 6338e3d commit 9f8f478

File tree

6 files changed

+208
-201
lines changed

6 files changed

+208
-201
lines changed
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import {
2+
Box,
3+
ButtonContent,
4+
Tooltip,
5+
Position,
6+
tooltipAsLabelBehavior
7+
} from "@fluentui/react-northstar";
8+
import { cloneDeep } from "@microsoft/sp-lodash-subset";
9+
import { Icon } from "office-ui-fabric-react/lib/components/Icon/Icon";
10+
import * as React from "react";
11+
import { TAction } from "../../common/model/TAction";
12+
import styles from "./Toolbar.module.scss";
13+
import { TToolbarLayout } from "./ToolbarActionsUtils";
14+
15+
interface IInFlowToolbarItemProps {
16+
action: TAction;
17+
layout: TToolbarLayout;
18+
}
19+
20+
export const toolbarMenuProps = {
21+
offset: [0, 4] as [number, number],
22+
position: "below" as Position,
23+
};
24+
25+
const toolbarActionTooltipProps = (() => {
26+
const props = cloneDeep(toolbarMenuProps);
27+
props.offset[1] += 10;
28+
return props;
29+
})();
30+
31+
export const InFlowToolbarItem = ({ action, layout }: IInFlowToolbarItemProps) => {
32+
const { iconName, title } = action;
33+
const contentIcon = iconName && (
34+
<Box className={"extended-toolbar__near-side__item__icon " + styles.inFlowToolbarItemBox} >
35+
<Icon iconName={iconName} />
36+
</Box>
37+
);
38+
39+
switch (layout) {
40+
case "verbose":
41+
return (
42+
<>
43+
{contentIcon}
44+
<ButtonContent content={title} />
45+
</>
46+
);
47+
default:
48+
case "compact":
49+
return (
50+
<Tooltip
51+
{...toolbarActionTooltipProps}
52+
trigger={contentIcon}
53+
content={title}
54+
accessibility={tooltipAsLabelBehavior}
55+
/>
56+
);
57+
}
58+
};
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
.toolbarButtonStyles {
2+
padding: .5rem;
3+
border-width: 1px;
4+
margin-top: 0;
5+
margin-bottom: 0;
6+
height: "3rem";
7+
min-width: 0;
8+
9+
&:focus:before {
10+
top: calc(.5rem - 1px);
11+
bottom: calc(.5rem - 1px);
12+
}
13+
14+
&:focus:after {
15+
top: calc(.5rem - 1px);
16+
bottom: calc(.5rem - 1px);
17+
}
18+
}
19+
20+
.inFlowToolbarItemBox {
21+
width: "1rem";
22+
display: "inline-flex";
23+
justify-content: "center";
24+
align-items: "center";
25+
26+
@media (min-width: 640px) {
27+
margin-right: ".5rem",
28+
}
29+
}

src/controls/toolbar/Toolbar.tsx

Lines changed: 12 additions & 189 deletions
Original file line numberDiff line numberDiff line change
@@ -4,43 +4,22 @@ import cloneDeep from "lodash/cloneDeep";
44

55
import {
66
Box,
7-
ButtonContent,
8-
ComponentEventHandler,
9-
ObjectShorthandCollection,
10-
Position,
117
PropsOfElement,
128
ProviderConsumer as FluentUIThemeConsumer,
13-
ShorthandCollection,
149
Toolbar as FluentUIToolbar,
15-
ToolbarItemProps,
16-
ToolbarItemShorthandKinds,
17-
Tooltip,
18-
TreeItemProps,
19-
tooltipAsLabelBehavior,
2010
teamsTheme,
2111
} from "@fluentui/react-northstar";
2212

2313
import { SiteVariablesPrepared } from "@fluentui/styles";
24-
25-
import { TAction, TActions } from "../../common/model/TAction";
26-
2714
import { ToolbarFilter } from "./ToolbarFilter";
2815
import { ToolbarFind } from "./ToolbarFind";
2916
import { ToolbarTheme } from "./ToolbarTheme";
3017

3118
import "./toolbar.css";
3219
import { Icon } from "office-ui-fabric-react/lib/components/Icon/Icon";
33-
34-
type TToolbarItems = ShorthandCollection<
35-
ToolbarItemProps,
36-
ToolbarItemShorthandKinds
37-
>;
38-
39-
export type TActionGroups = {
40-
[slug: string]: TActions;
41-
};
42-
43-
export type TFilters = ObjectShorthandCollection<TreeItemProps, never>;
20+
import { InFlowToolbarItem, toolbarMenuProps } from "./InFlowToolbarItem";
21+
import styles from "./Toolbar.module.scss";
22+
import { flattenedActions, getInFlowToolbarItems, getOverflowToolbarItems, TActionGroups, TFilters, TToolbarItems, TToolbarLayout } from "./ToolbarActionsUtils";
4423

4524
export interface IToolbarProps extends PropsOfElement<"div"> {
4625
actionGroups: TActionGroups;
@@ -49,116 +28,8 @@ export interface IToolbarProps extends PropsOfElement<"div"> {
4928
filtersSingleSelect?: boolean;
5029
onSelectedFiltersChange?: (selectedFilters: string[]) => string[];
5130
onFindQueryChange?: (findQuery: string) => string;
52-
__internal_callbacks__?: {
53-
[callbackId: string]: ComponentEventHandler<ToolbarItemProps>;
54-
};
5531
}
5632

57-
export type TToolbarLayout = "compact" | "verbose";
58-
59-
const slugSeparator = "__";
60-
61-
const toolbarMenuProps = {
62-
offset: [0, 4] as [number, number],
63-
position: "below" as Position,
64-
};
65-
66-
const toolbarActionTooltipProps = (() => {
67-
const props = cloneDeep(toolbarMenuProps);
68-
props.offset[1] += 10;
69-
return props;
70-
})();
71-
72-
const toolbarButtonStyles = {
73-
padding: ".5rem",
74-
borderWidth: "1px",
75-
marginTop: 0,
76-
marginBottom: 0,
77-
height: "3rem",
78-
minWidth: 0,
79-
"&:focus:before": {
80-
top: "calc(.5rem - 1px)",
81-
bottom: "calc(.5rem - 1px)",
82-
},
83-
"&:focus:after": {
84-
top: "calc(.5rem - 1px)",
85-
bottom: "calc(.5rem - 1px)",
86-
},
87-
};
88-
89-
function flattenedActions(actionGroups: TActionGroups): TActions {
90-
return Object.keys(actionGroups).reduce(
91-
(acc_i: TActions, actionGroupSlug: string) => {
92-
const actionGroup = actionGroups[actionGroupSlug];
93-
return Object.keys(actionGroup).reduce((acc_j, actionSlug) => {
94-
const action = actionGroup[actionSlug];
95-
acc_j[`${actionGroupSlug}${slugSeparator}${actionSlug}`] = action;
96-
return acc_j;
97-
}, acc_i);
98-
},
99-
{}
100-
);
101-
}
102-
103-
function needsSeparator(
104-
actionSlug: string,
105-
index: number,
106-
actionSlugs: string[]
107-
): boolean {
108-
if (index === 0) {
109-
return false;
110-
}
111-
else if (actionSlugs[index - 1]) {
112-
return actionSlugs[index - 1].split(slugSeparator)[0] !==
113-
actionSlug.split(slugSeparator)[0];
114-
}
115-
}
116-
117-
interface IInFlowToolbarItemProps {
118-
action: TAction;
119-
layout: TToolbarLayout;
120-
}
121-
122-
const InFlowToolbarItem = ({ action, layout }: IInFlowToolbarItemProps) => {
123-
const { iconName, title } = action;
124-
const contentIcon = iconName && (
125-
<Box
126-
styles={{
127-
width: "1rem",
128-
display: "inline-flex",
129-
justifyContent: "center",
130-
alignItems: "center",
131-
"@media (min-width: 640px)": {
132-
marginRight: ".5rem",
133-
},
134-
}}
135-
className="extended-toolbar__near-side__item__icon"
136-
>
137-
<Icon iconName={iconName} />
138-
</Box>
139-
);
140-
141-
switch (layout) {
142-
case "verbose":
143-
return (
144-
<>
145-
{contentIcon}
146-
<ButtonContent content={title} />
147-
</>
148-
);
149-
default:
150-
case "compact":
151-
return (
152-
<Tooltip
153-
{...toolbarActionTooltipProps}
154-
trigger={contentIcon}
155-
content={title}
156-
accessibility={tooltipAsLabelBehavior}
157-
/>
158-
);
159-
}
160-
};
161-
16233
export const Toolbar = (props: IToolbarProps) => {
16334
const { actionGroups, filters, filtersSingleSelect, find } = props;
16435

@@ -189,57 +60,10 @@ export const Toolbar = (props: IToolbarProps) => {
18960
};
19061
});
19162

192-
const inFlowToolbarItems: TToolbarItems = Object.keys(allActions).reduce(
193-
(acc: TToolbarItems, actionSlug, index, actionSlugs) => {
194-
const action = allActions[actionSlug];
195-
acc.push({
196-
...action,
197-
key: actionSlug,
198-
children: <InFlowToolbarItem action={action} layout={layout} />,
199-
title: action.title,
200-
"aria-label": action.title,
201-
className: "extended-toolbar__near-side__item",
202-
styles: {
203-
...toolbarButtonStyles,
204-
flex: "0 0 auto",
205-
margin: "0 .0625rem",
206-
display: "inline-flex",
207-
justifyContent: "center",
208-
alignItems: "center",
209-
},
210-
});
211-
if (needsSeparator(actionSlug, index, actionSlugs))
212-
acc.push({
213-
key: `divider${slugSeparator}${index}`,
214-
kind: "divider",
215-
});
216-
return acc;
217-
},
218-
[]
219-
);
220-
221-
const overflowToolbarItems: TToolbarItems = Object.keys(allActions).reduce(
222-
(acc: TToolbarItems, actionSlug, index, actionSlugs) => {
223-
const action = allActions[actionSlug];
224-
acc.push({
225-
key: actionSlug,
226-
content: action.title,
227-
icon: <Icon iconName={action.iconName} />,
228-
title: action.title,
229-
"aria-label": action.title,
230-
styles: { padding: ".375rem .5rem" },
231-
});
232-
if (needsSeparator(actionSlug, index, actionSlugs))
233-
acc.push({
234-
key: `divider${slugSeparator}${index}`,
235-
kind: "divider",
236-
styles: { margin: ".25rem 0", "&:first-child": { display: "none" } },
237-
});
238-
return acc;
239-
},
240-
[]
241-
);
63+
const inFlowToolbarItems: TToolbarItems = getInFlowToolbarItems(allActions, (action) => <InFlowToolbarItem action={action} layout={layout} />);
24264

65+
const overflowToolbarItems: TToolbarItems = getOverflowToolbarItems(allActions, (action)=><Icon iconName={action.iconName} />);
66+
24367
const displayFindOnly = find && layout === "compact" && findActive;
24468

24569
return (
@@ -277,14 +101,13 @@ export const Toolbar = (props: IToolbarProps) => {
277101
{!displayFindOnly && (
278102
<FluentUIToolbar
279103
aria-label="Extended toolbar"
280-
className="extended-toolbar__near-side"
104+
className={"extended-toolbar__near-side " + styles.toolbarButtonStyles}
281105
items={inFlowToolbarItems}
282106
overflow
283107
overflowOpen={overflowOpen}
284108
overflowItem={{
285109
title: "More",
286110
menu: toolbarMenuProps,
287-
styles: toolbarButtonStyles,
288111
}}
289112
onOverflowOpenChange={(event, changeProps) => {
290113
if (changeProps && changeProps.overflowOpen) {
@@ -324,14 +147,15 @@ export const Toolbar = (props: IToolbarProps) => {
324147
onOpenChange={(_e, changeProps) => {
325148
if (changeProps.open) {
326149
setFiltersOpen(changeProps.open);
327-
if (changeProps.open) {
328-
setOverflowOpen(false);
329-
}
150+
setOverflowOpen(false);
151+
}
152+
else {
153+
setFiltersOpen(false);
154+
setOverflowOpen(!false);
330155
}
331156
}}
332157
onSelectedFiltersChange={props.onSelectedFiltersChange}
333158
toolbarMenuProps={toolbarMenuProps}
334-
toolbarButtonStyles={toolbarButtonStyles}
335159
/>
336160
)}
337161
{find && (
@@ -340,7 +164,6 @@ export const Toolbar = (props: IToolbarProps) => {
340164
layout,
341165
findActive,
342166
setFindActive,
343-
toolbarButtonStyles,
344167
onFindQueryChange: props.onFindQueryChange,
345168
}}
346169
/>

0 commit comments

Comments
 (0)