@basic-ui/core 0.0.59 → 0.0.61
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cjs/index.js.map +1 -1
- package/build/esm/Accordion/AccordionBody.d.ts.map +1 -1
- package/build/esm/Accordion/AccordionBody.js +6 -26
- package/build/esm/Accordion/AccordionBody.js.map +1 -1
- package/build/esm/Accordion/AccordionHeader.d.ts.map +1 -1
- package/build/esm/Accordion/AccordionHeader.js +21 -69
- package/build/esm/Accordion/AccordionHeader.js.map +1 -1
- package/build/esm/Accordion/AccordionItem.d.ts.map +1 -1
- package/build/esm/Accordion/AccordionItem.js +31 -18
- package/build/esm/Accordion/AccordionItem.js.map +1 -1
- package/build/esm/Accordion/context.d.ts +0 -8
- package/build/esm/Accordion/context.d.ts.map +1 -1
- package/build/esm/Accordion/context.js +0 -11
- package/build/esm/Accordion/context.js.map +1 -1
- package/build/esm/Accordion/scopeQuery.d.ts +1 -0
- package/build/esm/Accordion/scopeQuery.d.ts.map +1 -1
- package/build/esm/Accordion/scopeQuery.js +3 -0
- package/build/esm/Accordion/scopeQuery.js.map +1 -1
- package/build/esm/Collapsible/Collapsible.d.ts +13 -0
- package/build/esm/Collapsible/Collapsible.d.ts.map +1 -0
- package/build/esm/Collapsible/Collapsible.js +53 -0
- package/build/esm/Collapsible/Collapsible.js.map +1 -0
- package/build/esm/Collapsible/CollapsiblePanel.d.ts +10 -0
- package/build/esm/Collapsible/CollapsiblePanel.d.ts.map +1 -0
- package/build/esm/Collapsible/CollapsiblePanel.js +85 -0
- package/build/esm/Collapsible/CollapsiblePanel.js.map +1 -0
- package/build/esm/Collapsible/CollapsibleTrigger.d.ts +11 -0
- package/build/esm/Collapsible/CollapsibleTrigger.d.ts.map +1 -0
- package/build/esm/Collapsible/CollapsibleTrigger.js +51 -0
- package/build/esm/Collapsible/CollapsibleTrigger.js.map +1 -0
- package/build/esm/Collapsible/context.d.ts +16 -0
- package/build/esm/Collapsible/context.d.ts.map +1 -0
- package/build/esm/Collapsible/context.js +11 -0
- package/build/esm/Collapsible/context.js.map +1 -0
- package/build/esm/Collapsible/index.d.ts +4 -0
- package/build/esm/Collapsible/index.d.ts.map +1 -0
- package/build/esm/Collapsible/index.js +4 -0
- package/build/esm/Collapsible/index.js.map +1 -0
- package/build/esm/Menu/Menu.d.ts +3 -2
- package/build/esm/Menu/Menu.d.ts.map +1 -1
- package/build/esm/Menu/Menu.js +64 -4
- package/build/esm/Menu/Menu.js.map +1 -1
- package/build/esm/Menu/MenuButton.d.ts.map +1 -1
- package/build/esm/Menu/MenuButton.js +85 -8
- package/build/esm/Menu/MenuButton.js.map +1 -1
- package/build/esm/Menu/MenuItem.d.ts.map +1 -1
- package/build/esm/Menu/MenuItem.js +16 -4
- package/build/esm/Menu/MenuItem.js.map +1 -1
- package/build/esm/Menu/MenuList.d.ts.map +1 -1
- package/build/esm/Menu/MenuList.js +47 -12
- package/build/esm/Menu/MenuList.js.map +1 -1
- package/build/esm/Menu/MenuPopover.d.ts.map +1 -1
- package/build/esm/Menu/MenuPopover.js +12 -1
- package/build/esm/Menu/MenuPopover.js.map +1 -1
- package/build/esm/Menu/MenuSubmenuTrigger.d.ts +8 -0
- package/build/esm/Menu/MenuSubmenuTrigger.d.ts.map +1 -0
- package/build/esm/Menu/MenuSubmenuTrigger.js +131 -0
- package/build/esm/Menu/MenuSubmenuTrigger.js.map +1 -0
- package/build/esm/Menu/context.d.ts +13 -3
- package/build/esm/Menu/context.d.ts.map +1 -1
- package/build/esm/Menu/context.js +1 -0
- package/build/esm/Menu/context.js.map +1 -1
- package/build/esm/Menu/index.d.ts +3 -0
- package/build/esm/Menu/index.d.ts.map +1 -1
- package/build/esm/Menu/index.js +2 -0
- package/build/esm/Menu/index.js.map +1 -1
- package/build/esm/Menu/scope.d.ts +1 -0
- package/build/esm/Menu/scope.d.ts.map +1 -1
- package/build/esm/Menu/scope.js +2 -1
- package/build/esm/Menu/scope.js.map +1 -1
- package/build/esm/MenuBar/MenuBar.d.ts +11 -0
- package/build/esm/MenuBar/MenuBar.d.ts.map +1 -0
- package/build/esm/MenuBar/MenuBar.js +153 -0
- package/build/esm/MenuBar/MenuBar.js.map +1 -0
- package/build/esm/MenuBar/context.d.ts +29 -0
- package/build/esm/MenuBar/context.d.ts.map +1 -0
- package/build/esm/MenuBar/context.js +7 -0
- package/build/esm/MenuBar/context.js.map +1 -0
- package/build/esm/MenuBar/index.d.ts +2 -0
- package/build/esm/MenuBar/index.d.ts.map +1 -0
- package/build/esm/MenuBar/index.js +2 -0
- package/build/esm/MenuBar/index.js.map +1 -0
- package/build/esm/Slider/Slider.d.ts +47 -1
- package/build/esm/Slider/Slider.d.ts.map +1 -1
- package/build/esm/Slider/Slider.js +91 -5
- package/build/esm/Slider/Slider.js.map +1 -1
- package/build/esm/ToggleGroup/ToggleGroup.d.ts +40 -0
- package/build/esm/ToggleGroup/ToggleGroup.d.ts.map +1 -0
- package/build/esm/ToggleGroup/ToggleGroup.js +113 -0
- package/build/esm/ToggleGroup/ToggleGroup.js.map +1 -0
- package/build/esm/ToggleGroup/ToggleGroupContext.d.ts +10 -0
- package/build/esm/ToggleGroup/ToggleGroupContext.d.ts.map +1 -0
- package/build/esm/ToggleGroup/ToggleGroupContext.js +6 -0
- package/build/esm/ToggleGroup/ToggleGroupContext.js.map +1 -0
- package/build/esm/ToggleGroup/index.d.ts +3 -0
- package/build/esm/ToggleGroup/index.d.ts.map +1 -0
- package/build/esm/ToggleGroup/index.js +3 -0
- package/build/esm/ToggleGroup/index.js.map +1 -0
- package/build/esm/Tooltip/Tooltip.d.ts.map +1 -1
- package/build/esm/Tooltip/Tooltip.js +2 -2
- package/build/esm/Tooltip/Tooltip.js.map +1 -1
- package/build/esm/Tree/Tree.d.ts +3 -0
- package/build/esm/Tree/Tree.d.ts.map +1 -0
- package/build/esm/Tree/Tree.js +730 -0
- package/build/esm/Tree/Tree.js.map +1 -0
- package/build/esm/Tree/TreeHeader.d.ts +3 -0
- package/build/esm/Tree/TreeHeader.d.ts.map +1 -0
- package/build/esm/Tree/TreeHeader.js +5 -0
- package/build/esm/Tree/TreeHeader.js.map +1 -0
- package/build/esm/Tree/TreeItem.d.ts +3 -0
- package/build/esm/Tree/TreeItem.d.ts.map +1 -0
- package/build/esm/Tree/TreeItem.js +5 -0
- package/build/esm/Tree/TreeItem.js.map +1 -0
- package/build/esm/Tree/TreeItemContent.d.ts +3 -0
- package/build/esm/Tree/TreeItemContent.d.ts.map +1 -0
- package/build/esm/Tree/TreeItemContent.js +69 -0
- package/build/esm/Tree/TreeItemContent.js.map +1 -0
- package/build/esm/Tree/TreeSection.d.ts +3 -0
- package/build/esm/Tree/TreeSection.d.ts.map +1 -0
- package/build/esm/Tree/TreeSection.js +5 -0
- package/build/esm/Tree/TreeSection.js.map +1 -0
- package/build/esm/Tree/collection.d.ts +18 -0
- package/build/esm/Tree/collection.d.ts.map +1 -0
- package/build/esm/Tree/collection.js +252 -0
- package/build/esm/Tree/collection.js.map +1 -0
- package/build/esm/Tree/context.d.ts +3 -0
- package/build/esm/Tree/context.d.ts.map +1 -0
- package/build/esm/Tree/context.js +3 -0
- package/build/esm/Tree/context.js.map +1 -0
- package/build/esm/Tree/index.d.ts +8 -0
- package/build/esm/Tree/index.d.ts.map +1 -0
- package/build/esm/Tree/index.js +7 -0
- package/build/esm/Tree/index.js.map +1 -0
- package/build/esm/Tree/types.d.ts +128 -0
- package/build/esm/Tree/types.d.ts.map +1 -0
- package/build/esm/Tree/types.js +2 -0
- package/build/esm/Tree/types.js.map +1 -0
- package/build/esm/hooks/index.d.ts +1 -0
- package/build/esm/hooks/index.d.ts.map +1 -1
- package/build/esm/hooks/index.js +1 -0
- package/build/esm/hooks/index.js.map +1 -1
- package/build/esm/hooks/useTransitionStatus.d.ts +7 -0
- package/build/esm/hooks/useTransitionStatus.d.ts.map +1 -0
- package/build/esm/hooks/useTransitionStatus.js +48 -0
- package/build/esm/hooks/useTransitionStatus.js.map +1 -0
- package/build/esm/index.d.ts +5 -0
- package/build/esm/index.d.ts.map +1 -1
- package/build/esm/index.js +5 -0
- package/build/esm/index.js.map +1 -1
- package/build/esm/toggle/Toggle.d.ts +28 -0
- package/build/esm/toggle/Toggle.d.ts.map +1 -0
- package/build/esm/toggle/Toggle.js +55 -0
- package/build/esm/toggle/Toggle.js.map +1 -0
- package/build/esm/toggle/index.d.ts +2 -0
- package/build/esm/toggle/index.d.ts.map +1 -0
- package/build/esm/toggle/index.js +2 -0
- package/build/esm/toggle/index.js.map +1 -0
- package/build/esm/utils/assign-ref.d.ts +3 -3
- package/build/esm/utils/assign-ref.d.ts.map +1 -1
- package/build/esm/utils/assign-ref.js +1 -1
- package/build/esm/utils/assign-ref.js.map +1 -1
- package/build/tsconfig-build.tsbuildinfo +1 -1
- package/build/tsconfig.tsbuildinfo +1 -1
- package/package.json +7 -4
- package/src/Accordion/AccordionBody.tsx +6 -35
- package/src/Accordion/AccordionHeader.tsx +29 -103
- package/src/Accordion/AccordionItem.tsx +40 -29
- package/src/Accordion/context.ts +0 -18
- package/src/Accordion/scopeQuery.ts +4 -0
- package/src/Collapsible/Collapsible.story.tsx +153 -0
- package/src/Collapsible/Collapsible.tsx +79 -0
- package/src/Collapsible/CollapsiblePanel.tsx +103 -0
- package/src/Collapsible/CollapsibleTrigger.tsx +60 -0
- package/src/Collapsible/context.ts +28 -0
- package/src/Collapsible/index.ts +3 -0
- package/src/Menu/Menu.story.tsx +70 -1
- package/src/Menu/Menu.tsx +141 -65
- package/src/Menu/MenuButton.tsx +115 -9
- package/src/Menu/MenuItem.tsx +20 -3
- package/src/Menu/MenuList.tsx +50 -13
- package/src/Menu/MenuPopover.tsx +12 -2
- package/src/Menu/MenuSubmenuTrigger.tsx +167 -0
- package/src/Menu/context.ts +20 -10
- package/src/Menu/index.ts +3 -0
- package/src/Menu/scope.ts +4 -1
- package/src/Menu/styles.css +57 -22
- package/src/MenuBar/MenuBar.story.tsx +92 -0
- package/src/MenuBar/MenuBar.tsx +236 -0
- package/src/MenuBar/context.ts +46 -0
- package/src/MenuBar/index.ts +1 -0
- package/src/MenuBar/styles.css +78 -0
- package/src/Slider/Slider.story.tsx +1 -1
- package/src/Slider/Slider.tsx +145 -8
- package/src/Toggle/Toggle.story.tsx +42 -0
- package/src/Toggle/Toggle.tsx +95 -0
- package/src/Toggle/index.ts +1 -0
- package/src/Toggle/styles.css +39 -0
- package/src/ToggleGroup/ToggleGroup.story.tsx +86 -0
- package/src/ToggleGroup/ToggleGroup.tsx +185 -0
- package/src/ToggleGroup/ToggleGroupContext.ts +17 -0
- package/src/ToggleGroup/index.ts +2 -0
- package/src/ToggleGroup/styles.css +66 -0
- package/src/Tooltip/Tooltip.tsx +4 -4
- package/src/Tree/Tree.story.tsx +221 -0
- package/src/Tree/Tree.tsx +1081 -0
- package/src/Tree/TreeHeader.tsx +9 -0
- package/src/Tree/TreeItem.tsx +9 -0
- package/src/Tree/TreeItemContent.tsx +91 -0
- package/src/Tree/TreeSection.tsx +9 -0
- package/src/Tree/collection.tsx +371 -0
- package/src/Tree/context.ts +6 -0
- package/src/Tree/index.ts +7 -0
- package/src/Tree/styles.css +135 -0
- package/src/Tree/types.ts +161 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useTransitionStatus.ts +65 -0
- package/src/index.ts +5 -0
- package/src/utils/assign-ref.ts +4 -4
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import type { ElementType, HTMLAttributes } from 'react';
|
|
2
|
+
import { forwardRef, useCallback, useMemo, useRef, useState } from 'react';
|
|
3
|
+
|
|
4
|
+
import type {
|
|
5
|
+
MenuBarContextProps,
|
|
6
|
+
MenuBarOrientation,
|
|
7
|
+
MenuBarRegisteredMenu,
|
|
8
|
+
} from './context';
|
|
9
|
+
import { MenuBarProvider } from './context';
|
|
10
|
+
|
|
11
|
+
export interface MenuBarProps extends HTMLAttributes<HTMLDivElement> {
|
|
12
|
+
as?: ElementType<any>;
|
|
13
|
+
innerAs?: ElementType<any>;
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
loopFocus?: boolean;
|
|
16
|
+
orientation?: MenuBarOrientation;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
type RegisteredButton = {
|
|
20
|
+
id: string;
|
|
21
|
+
element: HTMLElement;
|
|
22
|
+
disabled: boolean;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const MenuBar = forwardRef<HTMLDivElement, MenuBarProps>(
|
|
26
|
+
function MenuBar(props, forwardedRef) {
|
|
27
|
+
const {
|
|
28
|
+
as: Comp = 'div',
|
|
29
|
+
innerAs,
|
|
30
|
+
disabled = false,
|
|
31
|
+
loopFocus = true,
|
|
32
|
+
orientation = 'horizontal',
|
|
33
|
+
...otherProps
|
|
34
|
+
} = props;
|
|
35
|
+
|
|
36
|
+
const menusRef = useRef(new Map<string, MenuBarRegisteredMenu>());
|
|
37
|
+
const buttonsRef = useRef(new Map<string, RegisteredButton>());
|
|
38
|
+
const [activeMenuId, setActiveMenuId] = useState<string | null>(null);
|
|
39
|
+
const [openMenuIdState, setOpenMenuIdState] = useState<string | null>(null);
|
|
40
|
+
|
|
41
|
+
const getEnabledButtons = useCallback(() => {
|
|
42
|
+
if (disabled) {
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return Array.from(buttonsRef.current.values()).filter(
|
|
47
|
+
(button) => !button.disabled && button.element.isConnected
|
|
48
|
+
);
|
|
49
|
+
}, [disabled]);
|
|
50
|
+
|
|
51
|
+
const syncActiveMenuId = useCallback(() => {
|
|
52
|
+
setActiveMenuId((currentId) => {
|
|
53
|
+
const current = currentId ? buttonsRef.current.get(currentId) : null;
|
|
54
|
+
if (current && !current.disabled && current.element.isConnected) {
|
|
55
|
+
return currentId;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return getEnabledButtons()[0]?.id ?? null;
|
|
59
|
+
});
|
|
60
|
+
}, [getEnabledButtons]);
|
|
61
|
+
|
|
62
|
+
const registerButton = useCallback<MenuBarContextProps['registerButton']>(
|
|
63
|
+
(id, element, buttonDisabled) => {
|
|
64
|
+
if (element) {
|
|
65
|
+
buttonsRef.current.set(id, {
|
|
66
|
+
id,
|
|
67
|
+
element,
|
|
68
|
+
disabled: buttonDisabled,
|
|
69
|
+
});
|
|
70
|
+
} else {
|
|
71
|
+
buttonsRef.current.delete(id);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
syncActiveMenuId();
|
|
75
|
+
},
|
|
76
|
+
[syncActiveMenuId]
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
const registerMenu = useCallback<MenuBarContextProps['registerMenu']>(
|
|
80
|
+
(menu) => {
|
|
81
|
+
menusRef.current.set(menu.id, menu);
|
|
82
|
+
|
|
83
|
+
return () => {
|
|
84
|
+
menusRef.current.delete(menu.id);
|
|
85
|
+
};
|
|
86
|
+
},
|
|
87
|
+
[]
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
const notifyMenuOpenChange = useCallback<
|
|
91
|
+
MenuBarContextProps['notifyMenuOpenChange']
|
|
92
|
+
>((id, isOpen) => {
|
|
93
|
+
setOpenMenuIdState((currentId) => {
|
|
94
|
+
if (isOpen) {
|
|
95
|
+
return id;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return currentId === id ? null : currentId;
|
|
99
|
+
});
|
|
100
|
+
}, []);
|
|
101
|
+
|
|
102
|
+
const focusMenu = useCallback((id: string) => {
|
|
103
|
+
const button = buttonsRef.current.get(id);
|
|
104
|
+
if (!button || button.disabled) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
setActiveMenuId(id);
|
|
109
|
+
button.element.focus({ preventScroll: true });
|
|
110
|
+
}, []);
|
|
111
|
+
|
|
112
|
+
const openMenu = useCallback<MenuBarContextProps['openMenu']>(
|
|
113
|
+
(id, e, focusKey = null) => {
|
|
114
|
+
if (disabled) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (openMenuIdState && openMenuIdState !== id) {
|
|
119
|
+
menusRef.current.get(openMenuIdState)?.onOpenChange(e, false);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const menu = menusRef.current.get(id);
|
|
123
|
+
if (!menu) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
menu.openWithArrowKeyRef.current = focusKey;
|
|
128
|
+
menu.onOpenChange(e, true);
|
|
129
|
+
setActiveMenuId(id);
|
|
130
|
+
setOpenMenuIdState(id);
|
|
131
|
+
},
|
|
132
|
+
[disabled, openMenuIdState]
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
const closeMenu = useCallback<MenuBarContextProps['closeMenu']>((id, e) => {
|
|
136
|
+
menusRef.current.get(id)?.onOpenChange(e, false);
|
|
137
|
+
setOpenMenuIdState((currentId) => (currentId === id ? null : currentId));
|
|
138
|
+
}, []);
|
|
139
|
+
|
|
140
|
+
const moveFocus = useCallback<MenuBarContextProps['moveFocus']>(
|
|
141
|
+
(id, delta, e, options) => {
|
|
142
|
+
const buttons = getEnabledButtons();
|
|
143
|
+
if (buttons.length === 0) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const currentIndex = Math.max(
|
|
148
|
+
0,
|
|
149
|
+
buttons.findIndex((button) => button.id === id)
|
|
150
|
+
);
|
|
151
|
+
let nextIndex = currentIndex + delta;
|
|
152
|
+
|
|
153
|
+
if (loopFocus) {
|
|
154
|
+
nextIndex = (nextIndex + buttons.length) % buttons.length;
|
|
155
|
+
} else {
|
|
156
|
+
nextIndex = Math.max(0, Math.min(nextIndex, buttons.length - 1));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const nextButton = buttons[nextIndex];
|
|
160
|
+
if (!nextButton || nextButton.id === id) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
focusMenu(nextButton.id);
|
|
165
|
+
|
|
166
|
+
if (options?.open) {
|
|
167
|
+
openMenu(nextButton.id, e, options.focusKey ?? 'ArrowDown');
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
[focusMenu, getEnabledButtons, loopFocus, openMenu]
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
const focusFirstMenu = useCallback(() => {
|
|
174
|
+
const firstButton = getEnabledButtons()[0];
|
|
175
|
+
if (firstButton) {
|
|
176
|
+
focusMenu(firstButton.id);
|
|
177
|
+
}
|
|
178
|
+
}, [focusMenu, getEnabledButtons]);
|
|
179
|
+
|
|
180
|
+
const focusLastMenu = useCallback(() => {
|
|
181
|
+
const buttons = getEnabledButtons();
|
|
182
|
+
const lastButton = buttons[buttons.length - 1];
|
|
183
|
+
if (lastButton) {
|
|
184
|
+
focusMenu(lastButton.id);
|
|
185
|
+
}
|
|
186
|
+
}, [focusMenu, getEnabledButtons]);
|
|
187
|
+
|
|
188
|
+
const value = useMemo<MenuBarContextProps>(
|
|
189
|
+
() => ({
|
|
190
|
+
activeMenuId,
|
|
191
|
+
disabled,
|
|
192
|
+
openMenuId: openMenuIdState,
|
|
193
|
+
orientation,
|
|
194
|
+
closeMenu,
|
|
195
|
+
focusFirstMenu,
|
|
196
|
+
focusLastMenu,
|
|
197
|
+
moveFocus,
|
|
198
|
+
notifyMenuOpenChange,
|
|
199
|
+
openMenu,
|
|
200
|
+
registerButton,
|
|
201
|
+
registerMenu,
|
|
202
|
+
setActiveMenuId,
|
|
203
|
+
}),
|
|
204
|
+
[
|
|
205
|
+
activeMenuId,
|
|
206
|
+
closeMenu,
|
|
207
|
+
disabled,
|
|
208
|
+
focusFirstMenu,
|
|
209
|
+
focusLastMenu,
|
|
210
|
+
moveFocus,
|
|
211
|
+
notifyMenuOpenChange,
|
|
212
|
+
openMenu,
|
|
213
|
+
openMenuIdState,
|
|
214
|
+
orientation,
|
|
215
|
+
registerButton,
|
|
216
|
+
registerMenu,
|
|
217
|
+
]
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
return (
|
|
221
|
+
<MenuBarProvider value={value}>
|
|
222
|
+
<Comp
|
|
223
|
+
ref={forwardedRef}
|
|
224
|
+
as={innerAs}
|
|
225
|
+
role="menubar"
|
|
226
|
+
aria-orientation={orientation}
|
|
227
|
+
data-menubar=""
|
|
228
|
+
data-orientation={orientation}
|
|
229
|
+
data-disabled={disabled ? '' : undefined}
|
|
230
|
+
data-has-submenu-open={openMenuIdState ? '' : undefined}
|
|
231
|
+
{...otherProps}
|
|
232
|
+
/>
|
|
233
|
+
</MenuBarProvider>
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
);
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { MutableRefObject } from 'react';
|
|
2
|
+
import { createContext, useContext } from 'react';
|
|
3
|
+
|
|
4
|
+
import type { MenuTriggerEvent } from '../Menu/context';
|
|
5
|
+
|
|
6
|
+
export type MenuBarOrientation = 'horizontal' | 'vertical';
|
|
7
|
+
|
|
8
|
+
export interface MenuBarRegisteredMenu {
|
|
9
|
+
id: string;
|
|
10
|
+
openWithArrowKeyRef: MutableRefObject<string | null>;
|
|
11
|
+
onOpenChange: (e: MenuTriggerEvent, isOpen: boolean) => void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface MenuBarContextProps {
|
|
15
|
+
activeMenuId: string | null;
|
|
16
|
+
disabled: boolean;
|
|
17
|
+
openMenuId: string | null;
|
|
18
|
+
orientation: MenuBarOrientation;
|
|
19
|
+
closeMenu: (id: string, e: MenuTriggerEvent) => void;
|
|
20
|
+
focusFirstMenu: () => void;
|
|
21
|
+
focusLastMenu: () => void;
|
|
22
|
+
moveFocus: (
|
|
23
|
+
id: string,
|
|
24
|
+
delta: 1 | -1,
|
|
25
|
+
e: MenuTriggerEvent,
|
|
26
|
+
options?: { open?: boolean; focusKey?: 'ArrowUp' | 'ArrowDown' | null }
|
|
27
|
+
) => void;
|
|
28
|
+
notifyMenuOpenChange: (id: string, isOpen: boolean) => void;
|
|
29
|
+
openMenu: (
|
|
30
|
+
id: string,
|
|
31
|
+
e: MenuTriggerEvent,
|
|
32
|
+
focusKey?: 'ArrowUp' | 'ArrowDown' | null
|
|
33
|
+
) => void;
|
|
34
|
+
registerButton: (
|
|
35
|
+
id: string,
|
|
36
|
+
element: HTMLElement | null,
|
|
37
|
+
disabled: boolean
|
|
38
|
+
) => void;
|
|
39
|
+
registerMenu: (menu: MenuBarRegisteredMenu) => () => void;
|
|
40
|
+
setActiveMenuId: (id: string | null) => void;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const menuBarContext = createContext<MenuBarContextProps | null>(null);
|
|
44
|
+
|
|
45
|
+
export const { Provider: MenuBarProvider } = menuBarContext;
|
|
46
|
+
export const useOptionalMenuBarContext = () => useContext(menuBarContext);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './MenuBar';
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
.MenuBarStory-frame {
|
|
2
|
+
min-height: 360px;
|
|
3
|
+
padding: 48px;
|
|
4
|
+
font-family: sans-serif;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.MenuBarStory-frame [data-menubar] {
|
|
8
|
+
display: flex;
|
|
9
|
+
align-items: center;
|
|
10
|
+
width: max-content;
|
|
11
|
+
border: 1px solid #d8d8d8;
|
|
12
|
+
background: white;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.MenuBarStory-frame [data-menubar-menu-button] {
|
|
16
|
+
box-sizing: border-box;
|
|
17
|
+
height: 2rem;
|
|
18
|
+
margin: 0;
|
|
19
|
+
padding: 0 0.75rem;
|
|
20
|
+
border: 0;
|
|
21
|
+
background: transparent;
|
|
22
|
+
color: #1f1f1f;
|
|
23
|
+
font: inherit;
|
|
24
|
+
cursor: default;
|
|
25
|
+
user-select: none;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.MenuBarStory-frame [data-menubar-menu-button]:hover:not([disabled]),
|
|
29
|
+
.MenuBarStory-frame [data-menubar-menu-button]:focus-visible,
|
|
30
|
+
.MenuBarStory-frame [data-menubar-menu-button][aria-expanded='true'] {
|
|
31
|
+
background: #f0f0f0;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.MenuBarStory-frame [data-popper-placement] {
|
|
35
|
+
outline: 0;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.MenuBarStory-frame [data-menu-list] {
|
|
39
|
+
min-width: 180px;
|
|
40
|
+
margin: 0;
|
|
41
|
+
padding: 4px 0;
|
|
42
|
+
border: 1px solid #d8d8d8;
|
|
43
|
+
background: white;
|
|
44
|
+
color: #1f1f1f;
|
|
45
|
+
box-shadow: 4px 4px 0 rgb(0 0 0 / 10%);
|
|
46
|
+
list-style: none;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.MenuBarStory-frame [data-menu-item],
|
|
50
|
+
.MenuBarStory-frame [data-menu-submenu-trigger] {
|
|
51
|
+
display: flex;
|
|
52
|
+
align-items: center;
|
|
53
|
+
gap: 1rem;
|
|
54
|
+
padding: 0.5rem 1rem;
|
|
55
|
+
outline: 0;
|
|
56
|
+
cursor: default;
|
|
57
|
+
user-select: none;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.MenuBarStory-frame [data-menu-submenu-trigger] {
|
|
61
|
+
padding-right: 0.5rem;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.MenuBarStory-frame [data-menu-submenu-trigger] > span:first-child {
|
|
65
|
+
margin-right: auto;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.MenuBarStory-frame [data-menu-item][data-highlighted],
|
|
69
|
+
.MenuBarStory-frame [data-menu-submenu-trigger][data-highlighted] {
|
|
70
|
+
background: #1f1f1f;
|
|
71
|
+
color: white;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.MenuBarStory-frame [role='separator'] {
|
|
75
|
+
height: 1px;
|
|
76
|
+
margin: 4px;
|
|
77
|
+
background: #d8d8d8;
|
|
78
|
+
}
|
package/src/Slider/Slider.tsx
CHANGED
|
@@ -49,8 +49,9 @@ const noop = () => {
|
|
|
49
49
|
type SliderOrientation = 'horizontal' | 'vertical';
|
|
50
50
|
type SliderHandleAlignment = 'center' | 'contain';
|
|
51
51
|
|
|
52
|
-
const
|
|
53
|
-
|
|
52
|
+
const _sliderCtx = createContext<ISliderContext>('Slider');
|
|
53
|
+
const SliderProvider = _sliderCtx[0];
|
|
54
|
+
export const useSliderContext = _sliderCtx[1];
|
|
54
55
|
|
|
55
56
|
////////////////////////////////////////////////////////////////////////////////
|
|
56
57
|
|
|
@@ -92,6 +93,10 @@ interface SliderProps {
|
|
|
92
93
|
* @see Docs https://reach.tech/slider#slider-defaultvalue
|
|
93
94
|
*/
|
|
94
95
|
defaultValue?: number;
|
|
96
|
+
/**
|
|
97
|
+
* Sets the value that the filled range starts from. Defaults to `min`.
|
|
98
|
+
*/
|
|
99
|
+
fillOffset?: number;
|
|
95
100
|
/**
|
|
96
101
|
* @see Docs https://reach.tech/slider#slider-disabled
|
|
97
102
|
*/
|
|
@@ -102,6 +107,11 @@ interface SliderProps {
|
|
|
102
107
|
* @see Docs https://reach.tech/slider#slider-value
|
|
103
108
|
*/
|
|
104
109
|
value?: number;
|
|
110
|
+
/**
|
|
111
|
+
* When set, double clicking or double tapping the slider handle will reset
|
|
112
|
+
* the value.
|
|
113
|
+
*/
|
|
114
|
+
resetValue?: number;
|
|
105
115
|
/**
|
|
106
116
|
* A function used to set a human-readable name for the slider.
|
|
107
117
|
*
|
|
@@ -209,6 +219,8 @@ const SliderInput = forwardRef(function SliderInput(
|
|
|
209
219
|
innerAs,
|
|
210
220
|
defaultValue,
|
|
211
221
|
disabled = false,
|
|
222
|
+
fillOffset,
|
|
223
|
+
resetValue,
|
|
212
224
|
value: controlledValue,
|
|
213
225
|
getAriaLabel,
|
|
214
226
|
getAriaValueText,
|
|
@@ -257,6 +269,16 @@ const SliderInput = forwardRef(function SliderInput(
|
|
|
257
269
|
);
|
|
258
270
|
const value = clamp(_value, min, max);
|
|
259
271
|
const trackPercent = valueToPercent(value, min, max);
|
|
272
|
+
const hasFillOffset = fillOffset !== undefined;
|
|
273
|
+
const fillOffsetPercent = valueToPercent(fillOffset ?? min, min, max);
|
|
274
|
+
const fillStartPercent = Math.min(trackPercent, fillOffsetPercent);
|
|
275
|
+
const fillEndPercent = Math.max(trackPercent, fillOffsetPercent);
|
|
276
|
+
const fillDirection =
|
|
277
|
+
!hasFillOffset || trackPercent === fillOffsetPercent
|
|
278
|
+
? 'none'
|
|
279
|
+
: trackPercent > fillOffsetPercent
|
|
280
|
+
? 'right'
|
|
281
|
+
: 'left';
|
|
260
282
|
const isVertical = orientation === 'vertical';
|
|
261
283
|
|
|
262
284
|
const handleSize = isVertical
|
|
@@ -343,7 +365,10 @@ const SliderInput = forwardRef(function SliderInput(
|
|
|
343
365
|
? getAriaValueText(value)
|
|
344
366
|
: ariaValueTextProp;
|
|
345
367
|
|
|
346
|
-
const rangeStyle = {
|
|
368
|
+
const rangeStyle = {
|
|
369
|
+
[isVertical ? 'bottom' : 'left']: `${fillStartPercent}%`,
|
|
370
|
+
[isVertical ? 'height' : 'width']: `${fillEndPercent - fillStartPercent}%`,
|
|
371
|
+
};
|
|
347
372
|
|
|
348
373
|
// Slide events!
|
|
349
374
|
// We will try to use pointer events if they are supported to leverage
|
|
@@ -577,8 +602,14 @@ const SliderInput = forwardRef(function SliderInput(
|
|
|
577
602
|
handlePosition={handlePosition}
|
|
578
603
|
handleRef={handleRef}
|
|
579
604
|
hasFocus={hasFocus}
|
|
605
|
+
fillDirection={fillDirection}
|
|
606
|
+
fillEndPercent={fillEndPercent}
|
|
607
|
+
fillStartPercent={fillStartPercent}
|
|
580
608
|
onKeyDown={onKeyDown}
|
|
581
609
|
setHasFocus={setHasFocus}
|
|
610
|
+
resetValue={
|
|
611
|
+
resetValue === undefined ? undefined : clamp(resetValue, min, max)
|
|
612
|
+
}
|
|
582
613
|
sliderId={id}
|
|
583
614
|
sliderMax={max}
|
|
584
615
|
sliderMin={min}
|
|
@@ -609,6 +640,10 @@ const SliderInput = forwardRef(function SliderInput(
|
|
|
609
640
|
min,
|
|
610
641
|
value,
|
|
611
642
|
ariaValueText,
|
|
643
|
+
fillEndPercent,
|
|
644
|
+
fillOffsetPercent,
|
|
645
|
+
fillStartPercent,
|
|
646
|
+
valuePercent: trackPercent,
|
|
612
647
|
})
|
|
613
648
|
: children}
|
|
614
649
|
{name && (
|
|
@@ -700,7 +735,8 @@ const SliderRangeImpl = forwardRef(function SliderRange(
|
|
|
700
735
|
{ as: Comp = 'div', innerAs, children, style = {}, ...props },
|
|
701
736
|
forwardedRef
|
|
702
737
|
) {
|
|
703
|
-
const { disabled, orientation, rangeStyle } =
|
|
738
|
+
const { disabled, fillDirection, orientation, rangeStyle } =
|
|
739
|
+
useSliderContext('SliderRange');
|
|
704
740
|
return (
|
|
705
741
|
<Comp
|
|
706
742
|
ref={forwardedRef}
|
|
@@ -710,6 +746,7 @@ const SliderRangeImpl = forwardRef(function SliderRange(
|
|
|
710
746
|
{...props}
|
|
711
747
|
data-reach-slider-range=""
|
|
712
748
|
data-disabled={disabled ? '' : undefined}
|
|
749
|
+
data-fill-direction={fillDirection}
|
|
713
750
|
data-orientation={orientation}
|
|
714
751
|
/>
|
|
715
752
|
);
|
|
@@ -762,12 +799,92 @@ const SliderHandleImpl = forwardRef(function SliderHandle(
|
|
|
762
799
|
isVertical,
|
|
763
800
|
handleKeyDown,
|
|
764
801
|
orientation,
|
|
802
|
+
resetValue,
|
|
765
803
|
setHasFocus,
|
|
766
804
|
sliderMin,
|
|
767
805
|
sliderMax,
|
|
806
|
+
updateValue,
|
|
768
807
|
value,
|
|
769
808
|
} = useSliderContext('SliderHandle');
|
|
770
809
|
|
|
810
|
+
const lastTouchEndRef = useRef<{
|
|
811
|
+
time: number;
|
|
812
|
+
x: number;
|
|
813
|
+
y: number;
|
|
814
|
+
} | null>(null);
|
|
815
|
+
const resetHandleValue = useStableLayoutCallback(
|
|
816
|
+
(event: MouseEvent | TouchEvent) => {
|
|
817
|
+
if (!disabled && resetValue !== undefined) {
|
|
818
|
+
updateValue(event, resetValue);
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
);
|
|
822
|
+
|
|
823
|
+
const handleResetTouchEnd = useStableLayoutCallback((event: TouchEvent) => {
|
|
824
|
+
if (disabled || resetValue === undefined) {
|
|
825
|
+
return;
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
const touch = event.changedTouches[0];
|
|
829
|
+
if (!touch) {
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
const now = event.timeStamp;
|
|
834
|
+
const lastTouchEnd = lastTouchEndRef.current;
|
|
835
|
+
|
|
836
|
+
if (
|
|
837
|
+
lastTouchEnd &&
|
|
838
|
+
now - lastTouchEnd.time <= 300 &&
|
|
839
|
+
Math.abs(touch.clientX - lastTouchEnd.x) <= 20 &&
|
|
840
|
+
Math.abs(touch.clientY - lastTouchEnd.y) <= 20
|
|
841
|
+
) {
|
|
842
|
+
event.stopPropagation();
|
|
843
|
+
event.stopImmediatePropagation();
|
|
844
|
+
lastTouchEndRef.current = null;
|
|
845
|
+
updateValue(event, resetValue);
|
|
846
|
+
event.preventDefault();
|
|
847
|
+
return;
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
lastTouchEndRef.current = {
|
|
851
|
+
time: now,
|
|
852
|
+
x: touch.clientX,
|
|
853
|
+
y: touch.clientY,
|
|
854
|
+
};
|
|
855
|
+
});
|
|
856
|
+
|
|
857
|
+
useEffect(() => {
|
|
858
|
+
const handle = handleRef.current;
|
|
859
|
+
if (!handle) {
|
|
860
|
+
return noop;
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
const mouseDownListener = (event: MouseEvent) => {
|
|
864
|
+
if (event.detail < 2) {
|
|
865
|
+
return;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
event.stopPropagation();
|
|
869
|
+
event.stopImmediatePropagation();
|
|
870
|
+
resetHandleValue(event);
|
|
871
|
+
event.preventDefault();
|
|
872
|
+
};
|
|
873
|
+
const doubleClickListener = (event: MouseEvent) => {
|
|
874
|
+
resetHandleValue(event);
|
|
875
|
+
};
|
|
876
|
+
|
|
877
|
+
handle.addEventListener('mousedown', mouseDownListener);
|
|
878
|
+
handle.addEventListener('dblclick', doubleClickListener);
|
|
879
|
+
handle.addEventListener('touchend', handleResetTouchEnd);
|
|
880
|
+
|
|
881
|
+
return () => {
|
|
882
|
+
handle.removeEventListener('mousedown', mouseDownListener);
|
|
883
|
+
handle.removeEventListener('dblclick', doubleClickListener);
|
|
884
|
+
handle.removeEventListener('touchend', handleResetTouchEnd);
|
|
885
|
+
};
|
|
886
|
+
}, [handleRef, handleResetTouchEnd, resetHandleValue]);
|
|
887
|
+
|
|
771
888
|
return (
|
|
772
889
|
<Comp
|
|
773
890
|
// @ts-ignore
|
|
@@ -860,6 +977,8 @@ const SliderMarkerImpl = forwardRef(function SliderMarker(
|
|
|
860
977
|
sliderMin,
|
|
861
978
|
sliderMax,
|
|
862
979
|
value: sliderValue,
|
|
980
|
+
fillEndPercent,
|
|
981
|
+
fillStartPercent,
|
|
863
982
|
} = useSliderContext('SliderMarker');
|
|
864
983
|
|
|
865
984
|
const inRange = value >= sliderMin && value <= sliderMax;
|
|
@@ -868,13 +987,18 @@ const SliderMarkerImpl = forwardRef(function SliderMarker(
|
|
|
868
987
|
sliderMin,
|
|
869
988
|
sliderMax
|
|
870
989
|
)}%`;
|
|
990
|
+
const markerPercent = valueToPercent(value, sliderMin, sliderMax);
|
|
991
|
+
const fillState =
|
|
992
|
+
markerPercent >= fillStartPercent && markerPercent <= fillEndPercent
|
|
993
|
+
? 'in-range'
|
|
994
|
+
: 'out-of-range';
|
|
871
995
|
|
|
872
996
|
const state =
|
|
873
997
|
value < sliderValue
|
|
874
998
|
? 'under-value'
|
|
875
999
|
: value === sliderValue
|
|
876
|
-
|
|
877
|
-
|
|
1000
|
+
? 'at-value'
|
|
1001
|
+
: 'over-value';
|
|
878
1002
|
|
|
879
1003
|
return inRange ? (
|
|
880
1004
|
<Comp
|
|
@@ -891,6 +1015,7 @@ const SliderMarkerImpl = forwardRef(function SliderMarker(
|
|
|
891
1015
|
{...props}
|
|
892
1016
|
data-reach-slider-marker=""
|
|
893
1017
|
data-disabled={disabled ? '' : undefined}
|
|
1018
|
+
data-fill-state={fillState}
|
|
894
1019
|
data-orientation={orientation}
|
|
895
1020
|
data-state={state}
|
|
896
1021
|
data-value={value}
|
|
@@ -1041,7 +1166,11 @@ function useDimensions(ref: React.RefObject<HTMLElement | null>) {
|
|
|
1041
1166
|
}
|
|
1042
1167
|
|
|
1043
1168
|
function valueToPercent(value: number, min: number, max: number) {
|
|
1044
|
-
|
|
1169
|
+
if (max === min) {
|
|
1170
|
+
return 0;
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
return Math.min(100, Math.max(0, ((value - min) * 100) / (max - min)));
|
|
1045
1174
|
}
|
|
1046
1175
|
|
|
1047
1176
|
////////////////////////////////////////////////////////////////////////////////
|
|
@@ -1052,7 +1181,7 @@ type HandleRef = React.RefObject<HTMLDivElement | null>;
|
|
|
1052
1181
|
type SliderRef = React.RefObject<HTMLDivElement | null>;
|
|
1053
1182
|
type TouchIdRef = React.MutableRefObject<number | undefined>;
|
|
1054
1183
|
|
|
1055
|
-
type SomePointerEvent = TouchEvent | MouseEvent;
|
|
1184
|
+
type SomePointerEvent = TouchEvent | MouseEvent | PointerEvent;
|
|
1056
1185
|
|
|
1057
1186
|
interface ISliderContext {
|
|
1058
1187
|
ariaLabel: string | undefined;
|
|
@@ -1065,8 +1194,12 @@ interface ISliderContext {
|
|
|
1065
1194
|
handlePosition: string;
|
|
1066
1195
|
handleRef: HandleRef;
|
|
1067
1196
|
hasFocus: boolean;
|
|
1197
|
+
fillDirection: 'left' | 'none' | 'right';
|
|
1198
|
+
fillEndPercent: number;
|
|
1199
|
+
fillStartPercent: number;
|
|
1068
1200
|
onKeyDown?: (event: React.KeyboardEvent<HTMLDivElement>) => void;
|
|
1069
1201
|
handleKeyDown: (event: React.KeyboardEvent<HTMLDivElement>) => void;
|
|
1202
|
+
resetValue: number | undefined;
|
|
1070
1203
|
setHasFocus: React.Dispatch<React.SetStateAction<boolean>>;
|
|
1071
1204
|
sliderId: string | undefined;
|
|
1072
1205
|
sliderMax: number;
|
|
@@ -1086,9 +1219,13 @@ type SliderChildrenRender = (props: {
|
|
|
1086
1219
|
hasFocus?: boolean;
|
|
1087
1220
|
id?: string | undefined;
|
|
1088
1221
|
sliderId?: string | undefined;
|
|
1222
|
+
fillEndPercent?: number;
|
|
1223
|
+
fillOffsetPercent?: number;
|
|
1224
|
+
fillStartPercent?: number;
|
|
1089
1225
|
max?: number;
|
|
1090
1226
|
min?: number;
|
|
1091
1227
|
value?: number;
|
|
1228
|
+
valuePercent?: number;
|
|
1092
1229
|
}) => ReactElement;
|
|
1093
1230
|
|
|
1094
1231
|
////////////////////////////////////////////////////////////////////////////////
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Toggle } from './Toggle';
|
|
3
|
+
import './styles.css';
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
title: 'components/Toggle',
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const Basic = () => (
|
|
10
|
+
<div className="toggle-demo">
|
|
11
|
+
<Toggle>Toggle me</Toggle>
|
|
12
|
+
</div>
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
export const Disabled = () => (
|
|
16
|
+
<div className="toggle-demo">
|
|
17
|
+
<Toggle disabled>Disabled Toggle</Toggle>
|
|
18
|
+
</div>
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
export const Controlled = () => {
|
|
22
|
+
const [pressed, setPressed] = React.useState(false);
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<div className="toggle-demo">
|
|
26
|
+
<Toggle pressed={pressed} onPressedChange={setPressed}>
|
|
27
|
+
{pressed ? 'ON' : 'OFF'}
|
|
28
|
+
</Toggle>
|
|
29
|
+
<p>State: {pressed ? 'Pressed' : 'Not Pressed'}</p>
|
|
30
|
+
</div>
|
|
31
|
+
);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const Multiple = () => (
|
|
35
|
+
<div className="toggle-demo">
|
|
36
|
+
<div style={{ display: 'flex', gap: 8 }}>
|
|
37
|
+
<Toggle>Bold</Toggle>
|
|
38
|
+
<Toggle>Italic</Toggle>
|
|
39
|
+
<Toggle>Underline</Toggle>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
);
|