Skip to content

Commit e3a7b22

Browse files
add fold & unfold handler on dashboard
1 parent 1652798 commit e3a7b22

9 files changed

Lines changed: 331 additions & 86 deletions

File tree

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import React from "react";
2+
import styled from "@emotion/styled";
3+
4+
export const IconButton = React.forwardRef(function (
5+
{ children, ...props }: React.PropsWithChildren<{}>,
6+
ref: React.Ref<HTMLButtonElement>
7+
) {
8+
return (
9+
<IconButtonContainer ref={ref} {...props}>
10+
{children}
11+
</IconButtonContainer>
12+
);
13+
});
14+
15+
const IconButtonContainer = styled.button`
16+
display: flex;
17+
align-items: center;
18+
justify-content: center;
19+
background: none;
20+
border: none;
21+
cursor: pointer;
22+
padding: 8px;
23+
border-radius: 4px;
24+
color: white;
25+
26+
&:hover {
27+
background: rgba(255, 255, 255, 0.1);
28+
}
29+
30+
&:active {
31+
background: rgba(255, 255, 255, 0.2);
32+
}
33+
34+
&:focus {
35+
outline: 1px solid rgba(255, 255, 255, 0.5);
36+
}
37+
`;
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
export * from "./scene-card";
2+
export * from "./icon-button";
23
export * from "./hierarchy-item";
4+
export * from "./section-header";
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import React from "react";
2+
import styled from "@emotion/styled";
3+
import {
4+
ChevronRightIcon,
5+
ChevronDownIcon,
6+
DotsVerticalIcon,
7+
} from "@radix-ui/react-icons";
8+
import {
9+
DropdownMenu,
10+
DropdownMenuTrigger,
11+
DropdownMenuContent,
12+
DropdownMenuItem,
13+
} from "@editor-ui/dropdown-menu";
14+
import { IconButton } from "../components";
15+
import Highlighter from "react-highlight-words";
16+
import * as Collapsible from "@radix-ui/react-collapsible";
17+
18+
export type SectionHeaderAction = {
19+
label: string;
20+
handler: () => void;
21+
};
22+
23+
export function SectionHeader({
24+
label,
25+
expanded = true,
26+
q,
27+
id,
28+
actions = [],
29+
}: {
30+
expanded?: boolean;
31+
label: string;
32+
q?: string;
33+
id: string;
34+
actions?: SectionHeaderAction[];
35+
}) {
36+
const iconprops = {
37+
color: "white",
38+
};
39+
40+
const ToggleIcon = expanded ? ChevronDownIcon : ChevronRightIcon;
41+
42+
return (
43+
<SectionHeaderContainer id={id}>
44+
<Collapsible.Trigger asChild>
45+
<div className="leading">
46+
<div className="toggle">
47+
<ToggleIcon />
48+
</div>
49+
<Highlighter
50+
className="label"
51+
highlightClassName="label"
52+
searchWords={q ? [q] : []}
53+
textToHighlight={label}
54+
autoEscape // required to escape regex special characters, like, `+`, `(`, `)`, etc.
55+
/>
56+
<div style={{ flex: 1 }} />
57+
</div>
58+
</Collapsible.Trigger>
59+
<div className="actions">
60+
{actions.length > 0 && (
61+
<DropdownMenu>
62+
<DropdownMenuTrigger asChild>
63+
<IconButton>
64+
<DotsVerticalIcon />
65+
</IconButton>
66+
</DropdownMenuTrigger>
67+
<DropdownMenuContent>
68+
{actions.map((action, i) => (
69+
<DropdownMenuItem
70+
key={i}
71+
onSelect={(e) => {
72+
e.stopPropagation();
73+
e.stopImmediatePropagation();
74+
action.handler();
75+
}}
76+
>
77+
{action.label}
78+
</DropdownMenuItem>
79+
))}
80+
</DropdownMenuContent>
81+
</DropdownMenu>
82+
)}
83+
</div>
84+
</SectionHeaderContainer>
85+
);
86+
}
87+
88+
const SectionHeaderContainer = styled.div`
89+
display: flex;
90+
align-items: center;
91+
flex-direction: row;
92+
margin-bottom: 16px;
93+
border-radius: 4px;
94+
cursor: pointer;
95+
96+
background: transparent;
97+
98+
&:hover {
99+
background: rgba(255, 255, 255, 0.05);
100+
}
101+
102+
.leading {
103+
padding: 16px;
104+
display: flex;
105+
flex-direction: row;
106+
align-items: center;
107+
flex: 1;
108+
}
109+
110+
.toggle {
111+
margin-right: 16px;
112+
color: white;
113+
}
114+
115+
.actions {
116+
padding: 16px;
117+
margin-left: auto;
118+
}
119+
120+
.label {
121+
user-select: none;
122+
display: inline-block;
123+
opacity: 0.8;
124+
color: white;
125+
font-size: 18px;
126+
font-weight: 500;
127+
mark {
128+
background: white;
129+
color: black;
130+
}
131+
}
132+
`;

editor-packages/editor-dashboard/core/action.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
export type Action = NewSectionAction | NewFolderAction | FilterAction;
1+
export type Action =
2+
| NewSectionAction
3+
| NewFolderAction
4+
| FilterAction
5+
| FoldUnfoldAction
6+
| FoldUnfoldAllAction;
27

38
export type ActionTypes = Action["type"];
49

@@ -16,3 +21,25 @@ export type NewFolderAction = {
1621
type: "hierarchy/new-directory";
1722
path: string;
1823
};
24+
25+
export type FoldUnfoldAllAction = FoldAllAction | UnfoldAllAction;
26+
27+
export type FoldAllAction = {
28+
type: "hierarchy/fold-all";
29+
};
30+
31+
export type UnfoldAllAction = {
32+
type: "hierarchy/unfold-all";
33+
};
34+
35+
export type FoldUnfoldAction = FoldAction | UnfoldAction;
36+
37+
export type FoldAction = {
38+
type: "hierarchy/fold";
39+
path: string;
40+
};
41+
42+
export type UnfoldAction = {
43+
type: "hierarchy/unfold";
44+
path: string;
45+
};

editor-packages/editor-dashboard/core/initial.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,14 @@ import { group } from "../q";
1010
export function initialDashboardState(
1111
design: FigmaReflectRepository
1212
): DashboardState {
13+
const hierarchy = initialHierarchy(design);
1314
return {
1415
selection: [],
1516
filter: {
1617
query: "",
1718
},
18-
hierarchy: initialHierarchy(design),
19+
hierarchy: hierarchy,
20+
hierarchyFoldings: [],
1921
};
2022
}
2123

editor-packages/editor-dashboard/core/provider.tsx

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,12 +104,48 @@ export function useDashboard() {
104104
});
105105
}, [editordispatch]);
106106

107+
const foldAll = useCallback(() => {
108+
dispatch({
109+
type: "hierarchy/fold-all",
110+
});
111+
}, [dispatch]);
112+
113+
const unfoldAll = useCallback(() => {
114+
dispatch({
115+
type: "hierarchy/unfold-all",
116+
});
117+
}, [dispatch]);
118+
119+
const fold = useCallback(
120+
(path: string) => {
121+
dispatch({
122+
type: "hierarchy/fold",
123+
path,
124+
});
125+
},
126+
[dispatch]
127+
);
128+
129+
const unfold = useCallback(
130+
(path: string) => {
131+
dispatch({
132+
type: "hierarchy/unfold",
133+
path,
134+
});
135+
},
136+
[dispatch]
137+
);
138+
107139
return {
108140
...state,
109141
selection: editorState.selectedNodes,
110142
dispatch,
111143
selectNode,
112144
enterNode,
113145
blurSelection,
146+
foldAll,
147+
unfoldAll,
148+
fold,
149+
unfold,
114150
};
115151
}

editor-packages/editor-dashboard/core/reducer.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@ import produce from "immer";
22
import type {
33
Action,
44
FilterAction,
5+
FoldAction,
6+
FoldAllAction,
57
NewFolderAction,
68
NewSectionAction,
9+
UnfoldAction,
10+
UnfoldAllAction,
711
} from "./action";
8-
import type { DashboardState } from "./state";
12+
import type { DashboardFolderItem, DashboardState } from "./state";
913

1014
export function reducer(state: DashboardState, action: Action): DashboardState {
1115
switch (action.type) {
@@ -50,6 +54,36 @@ export function reducer(state: DashboardState, action: Action): DashboardState {
5054
}
5155
});
5256
}
57+
58+
case "hierarchy/fold": {
59+
const { path } = <FoldAction>action;
60+
return produce(state, (draft) => {
61+
draft.hierarchyFoldings.push(path);
62+
});
63+
}
64+
65+
case "hierarchy/unfold": {
66+
const { path } = <UnfoldAction>action;
67+
return produce(state, (draft) => {
68+
draft.hierarchyFoldings = draft.hierarchyFoldings.filter(
69+
(p) => p !== path
70+
);
71+
});
72+
}
73+
74+
case "hierarchy/fold-all": {
75+
const {} = <FoldAllAction>action;
76+
return produce(state, (draft) => {
77+
draft.hierarchyFoldings = state.hierarchy.sections.map((s) => s.path);
78+
});
79+
}
80+
81+
case "hierarchy/unfold-all": {
82+
const {} = <UnfoldAllAction>action;
83+
return produce(state, (draft) => {
84+
draft.hierarchyFoldings.length = 0;
85+
});
86+
}
5387
}
5488

5589
throw new Error(

editor-packages/editor-dashboard/core/state.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export interface DashboardState {
22
selection: Array<string>;
33
hierarchy: DashboardHierarchy;
4+
hierarchyFoldings: ReadonlyArray<string>;
45
filter: DashboardFilter;
56
}
67

0 commit comments

Comments
 (0)