@mks2508/mks-ui 0.5.1 → 0.5.2
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/dist/react-ui/index.js +3 -1
- package/dist/react-ui/ui/DynamicToggle/DynamicToggle.styles.d.ts +23 -0
- package/dist/react-ui/ui/DynamicToggle/DynamicToggle.styles.d.ts.map +1 -0
- package/dist/react-ui/ui/DynamicToggle/DynamicToggle.styles.js +39 -0
- package/dist/react-ui/ui/DynamicToggle/DynamicToggle.types.d.ts +89 -0
- package/dist/react-ui/ui/DynamicToggle/DynamicToggle.types.d.ts.map +1 -0
- package/dist/react-ui/ui/DynamicToggle/index.d.ts +29 -0
- package/dist/react-ui/ui/DynamicToggle/index.d.ts.map +1 -0
- package/dist/react-ui/ui/DynamicToggle/index.js +169 -0
- package/dist/react-ui/ui/index.d.ts +1 -0
- package/dist/react-ui/ui/index.d.ts.map +1 -1
- package/dist/react-ui/ui/index.js +2 -0
- package/package.json +1 -1
- package/src/react-ui/ui/DynamicToggle/DynamicToggle.css +150 -0
- package/src/react-ui/ui/DynamicToggle/DynamicToggle.styles.ts +65 -0
- package/src/react-ui/ui/DynamicToggle/DynamicToggle.types.ts +93 -0
- package/src/react-ui/ui/DynamicToggle/index.tsx +240 -0
- package/src/react-ui/ui/index.ts +3 -0
- /package/dist/react-ui/blocks/Terminal/panel/{terminal-filter-dropdown.module-CNVWCefU.css → terminal-filter-dropdown.module-DAcl_XQZ.css} +0 -0
- /package/dist/react-ui/blocks/Terminal/panel/{terminal-session-tabs.module-cmyJ11jP.css → terminal-session-tabs.module-DNAop5e3.css} +0 -0
- /package/dist/react-ui/components/MorphingPopover/{morphing-popover.module-BycNI8nU.css → morphing-popover.module-BJrjXisF.css} +0 -0
package/dist/react-ui/index.js
CHANGED
|
@@ -68,6 +68,8 @@ import { dataCardStyles, dataCardVariants } from "./ui/DataCard/DataCard.styles.
|
|
|
68
68
|
import { DataCard, DataCardActions, DataCardBracket, DataCardLabel, DataCardToggle, DataCardValue, useDataCard } from "./ui/DataCard/index.js";
|
|
69
69
|
import { useListFormat } from "./hooks/Formatting/UseListFormat.js";
|
|
70
70
|
import { TextFlow } from "./ui/TextFlow/index.js";
|
|
71
|
+
import { dynamicToggleStyles } from "./ui/DynamicToggle/DynamicToggle.styles.js";
|
|
72
|
+
import { DynamicToggle, DynamicToggleGroup, DynamicToggleOption } from "./ui/DynamicToggle/index.js";
|
|
71
73
|
import "./ui/index.js";
|
|
72
74
|
import { MorphingPopover, MorphingPopoverWithTarget } from "./components/MorphingPopover/index.js";
|
|
73
75
|
import "./components/index.js";
|
|
@@ -108,4 +110,4 @@ import { HugeIcons } from "./icons/index.js";
|
|
|
108
110
|
import { IconWrapper } from "./lib/icon-wrapper.js";
|
|
109
111
|
import "./lib/index.js";
|
|
110
112
|
|
|
111
|
-
export { ANIMATION_CONFIGS, ANIMATION_DEFAULTS, Accordion, AccordionHeader, AccordionItem, AccordionPanel, AccordionStyles, AccordionTrigger, ActivityIcon, AlertDialog, AlertDialogBackdrop, AlertDialogClose, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogPopup, AlertDialogPortal, AlertDialogStyles, AlertDialogTitle, AlertDialogTrigger, ArrowDownToLineIcon, ArrowUpIcon, AutoHeight, Badge, BellElectricIcon, BellIcon, BotIcon, BoxIcon, Button, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Check, Checkbox, CheckboxIndicator, CircleCheckIcon, Combobox, ComboboxChip, ComboboxChips, ComboboxChipsInput, ComboboxCollection, ComboboxContent, ComboboxEmpty, ComboboxGroup, ComboboxInput, ComboboxItem, ComboboxLabel, ComboboxList, ComboboxSeparator, ComboboxTrigger, ComboboxValue, CornerBracket, CountingNumber, DataCard, DataCardActions, DataCardBracket, DataCardLabel, DataCardToggle, DataCardValue, DeleteIcon, Dialog, DialogBackdrop, DialogClose, DialogDescription, DialogFooter, DialogHeader, DialogPopup, DialogPortal, DialogTitle, DialogTrigger, DownloadIcon, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EASINGS, EFFECTS, Edit2, Field, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldLegend, FieldSeparator, FieldSet, FieldTitle, Globe, Highlight, HighlightItem, HomeIcon, HugeIcons, IconWrapper, Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea, Label, Layers, LayoutPanelTopIcon, ListIcon, Menu, MenuArrow, MenuCheckboxItem, MenuCheckboxItemIndicator, MenuGroup, MenuGroupLabel, MenuHighlight, MenuHighlightItem, MenuItem, MenuPopup, MenuPortal, MenuPositioner, MenuRadioGroup, MenuRadioItem, MenuRadioItemIndicator, MenuSeparator, MenuShortcut, MenuSubmenu, MenuSubmenuTrigger, MenuTrigger, Morph, MorphingPopover, MorphingPopoverWithTarget, PRESETS, Package, Palette, PlusIcon, Popover, PopoverArrow, PopoverBackdrop, PopoverClose, PopoverDescription, PopoverPopup, PopoverPortal, PopoverPositioner, PopoverTitle, PopoverTrigger, Progress, ProgressIndicator, ProgressLabel, ProgressTrack, ProgressValue, RESPONSIVE_CONFIGS, RefreshCw, ReorderRoot as Reorder, Rocket, Save, SearchIcon, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, SettingsIcon, SlidingNumber, SlidingText, Slot, Switch, SwitchIcon, SwitchThumb, TIMING, TRANSFORMS, Tabs, TabsHighlight, TabsHighlightItem, TabsList, TabsPanel, TabsPanels, TabsTab, TerminalIcon, TextFlow, Textarea, Tooltip, TooltipArrow, TooltipPopup, TooltipPortal, TooltipPositioner, TooltipProvider, TooltipTrigger, Trash2, TrendingDownIcon, TrendingUpIcon, Type, Upload, XIcon, accordionVariants, badgeVariants, bracketVariants, buttonStateStyles, buttonVariants, calculateSeparatorCoordination, cardVariants, checkboxStyles, cn, dataCardStyles, dataCardVariants, dialogStyles, fieldVariants, getResponsiveDuration, getResponsiveStagger, getStrictContext, inputGroupAddonVariants, inputGroupButtonVariants, popoverStyles, progressStyles, shouldShowSeparator, switchStyles, tabsIndicatorVariants, tabsListVariants, tabsTabVariants, tooltipStyles, useAccordionItem, useAlertDialog, useAnimationOrchestrator, useAutoHeight, useCSSGridMorph, useCheckbox, useComboboxAnchor, useControlledState, useDataCard, useDataState, useDialog, useElementRegistry, useFLIPAnimation, useFLIPClipPath, useHighlight, useIsInView, useListFormat, useMenu, useMenuActiveValue, useMorph, useMorphContext, usePopover, usePositionCapture, useProgress, useReorder, useReorderPresence, useSwitch, useTooltip, useViewTransitions };
|
|
113
|
+
export { ANIMATION_CONFIGS, ANIMATION_DEFAULTS, Accordion, AccordionHeader, AccordionItem, AccordionPanel, AccordionStyles, AccordionTrigger, ActivityIcon, AlertDialog, AlertDialogBackdrop, AlertDialogClose, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogPopup, AlertDialogPortal, AlertDialogStyles, AlertDialogTitle, AlertDialogTrigger, ArrowDownToLineIcon, ArrowUpIcon, AutoHeight, Badge, BellElectricIcon, BellIcon, BotIcon, BoxIcon, Button, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Check, Checkbox, CheckboxIndicator, CircleCheckIcon, Combobox, ComboboxChip, ComboboxChips, ComboboxChipsInput, ComboboxCollection, ComboboxContent, ComboboxEmpty, ComboboxGroup, ComboboxInput, ComboboxItem, ComboboxLabel, ComboboxList, ComboboxSeparator, ComboboxTrigger, ComboboxValue, CornerBracket, CountingNumber, DataCard, DataCardActions, DataCardBracket, DataCardLabel, DataCardToggle, DataCardValue, DeleteIcon, Dialog, DialogBackdrop, DialogClose, DialogDescription, DialogFooter, DialogHeader, DialogPopup, DialogPortal, DialogTitle, DialogTrigger, DownloadIcon, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, DynamicToggle, DynamicToggleGroup, DynamicToggleOption, EASINGS, EFFECTS, Edit2, Field, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldLegend, FieldSeparator, FieldSet, FieldTitle, Globe, Highlight, HighlightItem, HomeIcon, HugeIcons, IconWrapper, Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea, Label, Layers, LayoutPanelTopIcon, ListIcon, Menu, MenuArrow, MenuCheckboxItem, MenuCheckboxItemIndicator, MenuGroup, MenuGroupLabel, MenuHighlight, MenuHighlightItem, MenuItem, MenuPopup, MenuPortal, MenuPositioner, MenuRadioGroup, MenuRadioItem, MenuRadioItemIndicator, MenuSeparator, MenuShortcut, MenuSubmenu, MenuSubmenuTrigger, MenuTrigger, Morph, MorphingPopover, MorphingPopoverWithTarget, PRESETS, Package, Palette, PlusIcon, Popover, PopoverArrow, PopoverBackdrop, PopoverClose, PopoverDescription, PopoverPopup, PopoverPortal, PopoverPositioner, PopoverTitle, PopoverTrigger, Progress, ProgressIndicator, ProgressLabel, ProgressTrack, ProgressValue, RESPONSIVE_CONFIGS, RefreshCw, ReorderRoot as Reorder, Rocket, Save, SearchIcon, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, SettingsIcon, SlidingNumber, SlidingText, Slot, Switch, SwitchIcon, SwitchThumb, TIMING, TRANSFORMS, Tabs, TabsHighlight, TabsHighlightItem, TabsList, TabsPanel, TabsPanels, TabsTab, TerminalIcon, TextFlow, Textarea, Tooltip, TooltipArrow, TooltipPopup, TooltipPortal, TooltipPositioner, TooltipProvider, TooltipTrigger, Trash2, TrendingDownIcon, TrendingUpIcon, Type, Upload, XIcon, accordionVariants, badgeVariants, bracketVariants, buttonStateStyles, buttonVariants, calculateSeparatorCoordination, cardVariants, checkboxStyles, cn, dataCardStyles, dataCardVariants, dialogStyles, dynamicToggleStyles, fieldVariants, getResponsiveDuration, getResponsiveStagger, getStrictContext, inputGroupAddonVariants, inputGroupButtonVariants, popoverStyles, progressStyles, shouldShowSeparator, switchStyles, tabsIndicatorVariants, tabsListVariants, tabsTabVariants, tooltipStyles, useAccordionItem, useAlertDialog, useAnimationOrchestrator, useAutoHeight, useCSSGridMorph, useCheckbox, useComboboxAnchor, useControlledState, useDataCard, useDataState, useDialog, useElementRegistry, useFLIPAnimation, useFLIPClipPath, useHighlight, useIsInView, useListFormat, useMenu, useMenuActiveValue, useMorph, useMorphContext, usePopover, usePositionCapture, useProgress, useReorder, useReorderPresence, useSwitch, useTooltip, useViewTransitions };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DynamicToggle style slots and types.
|
|
3
|
+
*
|
|
4
|
+
* Uses semantic CSS variables for all colors.
|
|
5
|
+
* Animation handled in DynamicToggle.css (clip-path, transitions).
|
|
6
|
+
*
|
|
7
|
+
* @module @mks2508/mks-ui/react/ui/DynamicToggle
|
|
8
|
+
*/
|
|
9
|
+
import type { StyleSlots } from '../../../core/types';
|
|
10
|
+
/** Slot names for DynamicToggle */
|
|
11
|
+
export type DynamicToggleSlot = 'root' | 'track' | 'option' | 'indicator' | 'group' | 'groupLabel' | 'groupIndicator';
|
|
12
|
+
/**
|
|
13
|
+
* Default styles for each DynamicToggle slot.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```tsx
|
|
17
|
+
* <DynamicToggle slots={{ root: 'w-72', indicator: 'bg-primary' }}>
|
|
18
|
+
* ...
|
|
19
|
+
* </DynamicToggle>
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export declare const dynamicToggleStyles: StyleSlots<DynamicToggleSlot>;
|
|
23
|
+
//# sourceMappingURL=DynamicToggle.styles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DynamicToggle.styles.d.ts","sourceRoot":"","sources":["../../../../src/react-ui/ui/DynamicToggle/DynamicToggle.styles.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C,mCAAmC;AACnC,MAAM,MAAM,iBAAiB,GACzB,MAAM,GACN,OAAO,GACP,QAAQ,GACR,WAAW,GACX,OAAO,GACP,YAAY,GACZ,gBAAgB,CAAC;AAErB;;;;;;;;;GASG;AACH,eAAO,MAAM,mBAAmB,EAAE,UAAU,CAAC,iBAAiB,CAiC7D,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
//#region src/react-ui/ui/DynamicToggle/DynamicToggle.styles.ts
|
|
2
|
+
/**
|
|
3
|
+
* Default styles for each DynamicToggle slot.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```tsx
|
|
7
|
+
* <DynamicToggle slots={{ root: 'w-72', indicator: 'bg-primary' }}>
|
|
8
|
+
* ...
|
|
9
|
+
* </DynamicToggle>
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
const dynamicToggleStyles = {
|
|
13
|
+
root: ["relative rounded-full border border-border bg-card", "p-[2px] select-none"].join(" "),
|
|
14
|
+
track: ["relative grid place-items-center", "w-full h-full"].join(" "),
|
|
15
|
+
option: [
|
|
16
|
+
"inline-grid place-items-center cursor-pointer",
|
|
17
|
+
"text-xs font-medium z-[2] h-full w-full",
|
|
18
|
+
"transition-[color,opacity] duration-[220ms]"
|
|
19
|
+
].join(" "),
|
|
20
|
+
indicator: [
|
|
21
|
+
"absolute w-1/2 left-0 top-0 bottom-0",
|
|
22
|
+
"bg-foreground rounded-full",
|
|
23
|
+
"pointer-events-none z-0"
|
|
24
|
+
].join(" "),
|
|
25
|
+
group: ["relative w-full h-full grid", "border border-transparent"].join(" "),
|
|
26
|
+
groupLabel: [
|
|
27
|
+
"absolute left-1/2 top-1/2 z-[2]",
|
|
28
|
+
"text-xs font-medium text-foreground",
|
|
29
|
+
"pointer-events-none"
|
|
30
|
+
].join(" "),
|
|
31
|
+
groupIndicator: [
|
|
32
|
+
"absolute left-1/2 top-0 bottom-0",
|
|
33
|
+
"bg-foreground rounded-full",
|
|
34
|
+
"pointer-events-none z-0"
|
|
35
|
+
].join(" ")
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
//#endregion
|
|
39
|
+
export { dynamicToggleStyles };
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DynamicToggle type definitions.
|
|
3
|
+
*
|
|
4
|
+
* A CSS-animated toggle where one option can expand into sub-options.
|
|
5
|
+
* Uses hidden radio inputs + `:has(:checked)` for zero-JS animation.
|
|
6
|
+
*
|
|
7
|
+
* @module @mks2508/mks-ui/react/ui/DynamicToggle
|
|
8
|
+
*/
|
|
9
|
+
import type { SlotOverrides } from '../../../core/types';
|
|
10
|
+
import type { DynamicToggleSlot } from './DynamicToggle.styles';
|
|
11
|
+
/**
|
|
12
|
+
* Context shared between DynamicToggle root and its children.
|
|
13
|
+
*/
|
|
14
|
+
export type DynamicToggleContextType = {
|
|
15
|
+
/** Current selected value */
|
|
16
|
+
value: string;
|
|
17
|
+
/** Update selected value */
|
|
18
|
+
setValue: (value: string) => void;
|
|
19
|
+
/** Auto-generated radio group name */
|
|
20
|
+
groupName: string;
|
|
21
|
+
/** Whether a group child is active */
|
|
22
|
+
groupActive: boolean;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Props for the DynamicToggle root container.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```tsx
|
|
29
|
+
* <DynamicToggle value="tree" onValueChange={setVal}>
|
|
30
|
+
* <DynamicToggleOption value="tree">Tree</DynamicToggleOption>
|
|
31
|
+
* <DynamicToggleGroup label="Changes">
|
|
32
|
+
* <DynamicToggleOption value="flat">Flat</DynamicToggleOption>
|
|
33
|
+
* <DynamicToggleOption value="grouped">Grouped</DynamicToggleOption>
|
|
34
|
+
* </DynamicToggleGroup>
|
|
35
|
+
* </DynamicToggle>
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export interface IDynamicToggleProps {
|
|
39
|
+
/** Controlled value */
|
|
40
|
+
value?: string;
|
|
41
|
+
/** Uncontrolled default value */
|
|
42
|
+
defaultValue?: string;
|
|
43
|
+
/** Change callback */
|
|
44
|
+
onValueChange?: (value: string) => void;
|
|
45
|
+
/** Slot class overrides */
|
|
46
|
+
slots?: SlotOverrides<DynamicToggleSlot>;
|
|
47
|
+
/** Additional class for the root */
|
|
48
|
+
className?: string;
|
|
49
|
+
/** DynamicToggleOption and DynamicToggleGroup children */
|
|
50
|
+
children: React.ReactNode;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Props for a single toggle option (top-level or inside a group).
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```tsx
|
|
57
|
+
* <DynamicToggleOption value="tree">Tree</DynamicToggleOption>
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
export interface IDynamicToggleOptionProps {
|
|
61
|
+
/** Value this option represents */
|
|
62
|
+
value: string;
|
|
63
|
+
/** Label content */
|
|
64
|
+
children: React.ReactNode;
|
|
65
|
+
/** Additional class */
|
|
66
|
+
className?: string;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Props for an expandable group of options.
|
|
70
|
+
* When none of the group's options are active, shows the collapsed `label`.
|
|
71
|
+
* When one is active, expands to show all sub-options.
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```tsx
|
|
75
|
+
* <DynamicToggleGroup label="Changes">
|
|
76
|
+
* <DynamicToggleOption value="flat">Flat</DynamicToggleOption>
|
|
77
|
+
* <DynamicToggleOption value="grouped">Grouped</DynamicToggleOption>
|
|
78
|
+
* </DynamicToggleGroup>
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
export interface IDynamicToggleGroupProps {
|
|
82
|
+
/** Label shown when collapsed */
|
|
83
|
+
label: string;
|
|
84
|
+
/** DynamicToggleOption children */
|
|
85
|
+
children: React.ReactNode;
|
|
86
|
+
/** Additional class */
|
|
87
|
+
className?: string;
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=DynamicToggle.types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DynamicToggle.types.d.ts","sourceRoot":"","sources":["../../../../src/react-ui/ui/DynamicToggle/DynamicToggle.types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEhE;;GAEG;AACH,MAAM,MAAM,wBAAwB,GAAG;IACrC,6BAA6B;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,4BAA4B;IAC5B,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,sCAAsC;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,sCAAsC;IACtC,WAAW,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,mBAAmB;IAClC,uBAAuB;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iCAAiC;IACjC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,sBAAsB;IACtB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,2BAA2B;IAC3B,KAAK,CAAC,EAAE,aAAa,CAAC,iBAAiB,CAAC,CAAC;IACzC,oCAAoC;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0DAA0D;IAC1D,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,yBAAyB;IACxC,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,oBAAoB;IACpB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,uBAAuB;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,wBAAwB;IACvC,iCAAiC;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,mCAAmC;IACnC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,uBAAuB;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { IDynamicToggleProps, IDynamicToggleOptionProps, IDynamicToggleGroupProps } from './DynamicToggle.types';
|
|
2
|
+
/**
|
|
3
|
+
* Root container — pill-shaped toggle control.
|
|
4
|
+
*
|
|
5
|
+
* @param props - Component props
|
|
6
|
+
* @returns React component
|
|
7
|
+
*/
|
|
8
|
+
declare function DynamicToggle({ slots, className, children, ...props }: IDynamicToggleProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
/**
|
|
10
|
+
* A single toggle option — renders a hidden radio + visible label.
|
|
11
|
+
* Can be used at the top level or inside a DynamicToggleGroup.
|
|
12
|
+
*
|
|
13
|
+
* @param props - Component props
|
|
14
|
+
* @returns React component
|
|
15
|
+
*/
|
|
16
|
+
declare function DynamicToggleOption({ value, children, className }: IDynamicToggleOptionProps): import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
/**
|
|
18
|
+
* An expandable group of options. Shows `label` when collapsed,
|
|
19
|
+
* expands to show sub-options when one is active.
|
|
20
|
+
*
|
|
21
|
+
* @param props - Component props
|
|
22
|
+
* @returns React component
|
|
23
|
+
*/
|
|
24
|
+
declare function DynamicToggleGroup({ label, children, className }: IDynamicToggleGroupProps): import("react/jsx-runtime").JSX.Element;
|
|
25
|
+
export { DynamicToggle, DynamicToggleOption, DynamicToggleGroup };
|
|
26
|
+
export type { IDynamicToggleProps, IDynamicToggleOptionProps, IDynamicToggleGroupProps, DynamicToggleContextType, } from './DynamicToggle.types';
|
|
27
|
+
export type { DynamicToggleSlot } from './DynamicToggle.styles';
|
|
28
|
+
export { dynamicToggleStyles } from './DynamicToggle.styles';
|
|
29
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/react-ui/ui/DynamicToggle/index.tsx"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAEV,mBAAmB,EACnB,yBAAyB,EACzB,wBAAwB,EACzB,MAAM,uBAAuB,CAAC;AAyD/B;;;;;GAKG;AACH,iBAAS,aAAa,CAAC,EACrB,KAAK,EACL,SAAS,EACT,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,mBAAmB,2CAiDrB;AAMD;;;;;;GAMG;AACH,iBAAS,mBAAmB,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,yBAAyB,2CA0BrF;AAMD;;;;;;GAMG;AACH,iBAAS,kBAAkB,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,wBAAwB,2CAqBnF;AAMD,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,CAAC;AAElE,YAAY,EACV,mBAAmB,EACnB,yBAAyB,EACzB,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,uBAAuB,CAAC;AAE/B,YAAY,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { cn } from "../../lib/utils.js";
|
|
4
|
+
import { getStrictContext } from "../../lib/get-strict-context.js";
|
|
5
|
+
import { useControlledState } from "../../hooks/State/UseControlledState.js";
|
|
6
|
+
import { dynamicToggleStyles } from "./DynamicToggle.styles.js";
|
|
7
|
+
import * as React$1 from "react";
|
|
8
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
|
|
10
|
+
//#region src/react-ui/ui/DynamicToggle/index.tsx
|
|
11
|
+
/**
|
|
12
|
+
* DynamicToggle — CSS-animated toggle where one option expands into sub-options.
|
|
13
|
+
*
|
|
14
|
+
* Uses hidden radio inputs + `:has(:checked)` for zero-JS animation.
|
|
15
|
+
* The indicator slides between options, and group options expand/collapse
|
|
16
|
+
* with a clip-path reveal animation.
|
|
17
|
+
*
|
|
18
|
+
* @module @mks2508/mks-ui/react/ui/DynamicToggle
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```tsx
|
|
22
|
+
* <DynamicToggle value="tree" onValueChange={setVal}>
|
|
23
|
+
* <DynamicToggleOption value="tree">Tree</DynamicToggleOption>
|
|
24
|
+
* <DynamicToggleGroup label="Changes">
|
|
25
|
+
* <DynamicToggleOption value="flat">Flat</DynamicToggleOption>
|
|
26
|
+
* <DynamicToggleOption value="grouped">Grouped</DynamicToggleOption>
|
|
27
|
+
* </DynamicToggleGroup>
|
|
28
|
+
* </DynamicToggle>
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
const DT_CSS_ID = "mks-dynamic-toggle-css";
|
|
32
|
+
/** Reads the CSS file content at build time via rolldown */
|
|
33
|
+
const DT_CSS = `
|
|
34
|
+
:root{--dt-duration:0.22s;--dt-ease:cubic-bezier(0.22,0.61,0.36,1);--dt-drop-off:0.45}
|
|
35
|
+
[data-slot="dt-track"]{grid-template-columns:repeat(4,1fr)}
|
|
36
|
+
[data-slot="dt-track"]>label:first-of-type,[data-slot="dt-group"]{grid-column:span 2}
|
|
37
|
+
[data-slot="dt-indicator"]{transition:translate var(--dt-duration) var(--dt-ease)}
|
|
38
|
+
[data-slot="dt-track"]:has(>input:checked) [data-slot="dt-indicator"]{translate:0 0}
|
|
39
|
+
[data-slot="dt-track"]:not(:has(>input:checked)) [data-slot="dt-indicator"]{translate:100% 0}
|
|
40
|
+
[data-slot="dt-track"]:has(>input:checked)>label{color:var(--card)}
|
|
41
|
+
[data-slot="dt-track"]:not(:has(>input:checked))>label{color:var(--foreground);opacity:var(--dt-drop-off)}
|
|
42
|
+
[data-slot="dt-group"]{container-type:size;grid-template-columns:1fr 1fr}
|
|
43
|
+
[data-slot="dt-group-label"]{translate:-50% -80%;transition:translate var(--dt-duration) var(--dt-ease),scale var(--dt-duration) var(--dt-ease)}
|
|
44
|
+
[data-slot="dt-group"]:has(input:checked) [data-slot="dt-group-label"]{translate:-50% -250%;scale:0.85}
|
|
45
|
+
[data-slot="dt-group-indicator"]{transition:translate var(--dt-duration) var(--dt-ease),clip-path var(--dt-duration) var(--dt-ease),background var(--dt-duration) var(--dt-ease);clip-path:inset(73cqh calc(50% + 1px) calc(27cqh - 2px) calc(50% - 3px) round 100px);translate:-50% 0;background:var(--foreground)}
|
|
46
|
+
[data-slot="dt-group"]:has(input:checked) [data-slot="dt-group-indicator"]{background:var(--card);clip-path:inset(0 0 0 0 round 100px)}
|
|
47
|
+
[data-slot="dt-group"]:has(input:nth-of-type(1):checked) [data-slot="dt-group-indicator"]{translate:-100% 0}
|
|
48
|
+
[data-slot="dt-group"]:has(input:nth-of-type(2):checked) [data-slot="dt-group-indicator"]{translate:0 0}
|
|
49
|
+
[data-slot="dt-group"] label{color:var(--muted-foreground);transition:color var(--dt-duration) var(--dt-ease),opacity var(--dt-duration) var(--dt-ease)}
|
|
50
|
+
[data-slot="dt-group"] label span{transition:scale var(--dt-duration) var(--dt-ease)}
|
|
51
|
+
[data-slot="dt-track"]:has(>input:checked) [data-slot="dt-group"] label{color:var(--muted-foreground)}
|
|
52
|
+
[data-slot="dt-group"]:has(input:checked) label{color:var(--muted-foreground);opacity:0.75}
|
|
53
|
+
[data-slot="dt-group"]:has(input:nth-of-type(1):checked) label:nth-of-type(1),[data-slot="dt-group"]:has(input:nth-of-type(2):checked) label:nth-of-type(2){color:var(--foreground);opacity:1}
|
|
54
|
+
[data-slot="dt-group"] label:nth-of-type(1) span{scale:0.75;transform-origin:150% 150%}
|
|
55
|
+
[data-slot="dt-group"] label:nth-of-type(2) span{scale:0.75;transform-origin:-65% 150%}
|
|
56
|
+
[data-slot="dt-group"]:has(input:checked) label span{scale:1}
|
|
57
|
+
[data-slot="dt-radio"]{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}
|
|
58
|
+
`;
|
|
59
|
+
function useDynamicToggleCSS() {
|
|
60
|
+
React$1.useEffect(() => {
|
|
61
|
+
if (typeof document === "undefined") return;
|
|
62
|
+
if (document.getElementById(DT_CSS_ID)) return;
|
|
63
|
+
const style = document.createElement("style");
|
|
64
|
+
style.id = DT_CSS_ID;
|
|
65
|
+
style.textContent = DT_CSS;
|
|
66
|
+
document.head.appendChild(style);
|
|
67
|
+
}, []);
|
|
68
|
+
}
|
|
69
|
+
const [DynamicToggleProvider, useDynamicToggle] = getStrictContext("DynamicToggleContext");
|
|
70
|
+
/**
|
|
71
|
+
* Root container — pill-shaped toggle control.
|
|
72
|
+
*
|
|
73
|
+
* @param props - Component props
|
|
74
|
+
* @returns React component
|
|
75
|
+
*/
|
|
76
|
+
function DynamicToggle({ slots, className, children, ...props }) {
|
|
77
|
+
useDynamicToggleCSS();
|
|
78
|
+
const [value, setValue] = useControlledState({
|
|
79
|
+
value: props.value,
|
|
80
|
+
defaultValue: props.defaultValue ?? "",
|
|
81
|
+
onChange: props.onValueChange
|
|
82
|
+
});
|
|
83
|
+
const groupName = React$1.useId();
|
|
84
|
+
const groupActive = React$1.useMemo(() => {
|
|
85
|
+
const vals = [];
|
|
86
|
+
React$1.Children.forEach(children, (child) => {
|
|
87
|
+
if (React$1.isValidElement(child) && child.type === DynamicToggleGroup) React$1.Children.forEach(child.props.children, (gc) => {
|
|
88
|
+
if (React$1.isValidElement(gc) && gc.props.value) vals.push(gc.props.value);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
return vals;
|
|
92
|
+
}, [children]).includes(value);
|
|
93
|
+
return /* @__PURE__ */ jsx(DynamicToggleProvider, {
|
|
94
|
+
value: {
|
|
95
|
+
value,
|
|
96
|
+
setValue,
|
|
97
|
+
groupName,
|
|
98
|
+
groupActive
|
|
99
|
+
},
|
|
100
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
101
|
+
"data-slot": "dt-root",
|
|
102
|
+
"data-group-active": groupActive || void 0,
|
|
103
|
+
className: cn(dynamicToggleStyles.root, slots?.root, className),
|
|
104
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
105
|
+
"data-slot": "dt-track",
|
|
106
|
+
className: cn(dynamicToggleStyles.track, slots?.track),
|
|
107
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
108
|
+
"data-slot": "dt-indicator",
|
|
109
|
+
className: cn(dynamicToggleStyles.indicator, slots?.indicator)
|
|
110
|
+
}), children]
|
|
111
|
+
})
|
|
112
|
+
})
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* A single toggle option — renders a hidden radio + visible label.
|
|
117
|
+
* Can be used at the top level or inside a DynamicToggleGroup.
|
|
118
|
+
*
|
|
119
|
+
* @param props - Component props
|
|
120
|
+
* @returns React component
|
|
121
|
+
*/
|
|
122
|
+
function DynamicToggleOption({ value, children, className }) {
|
|
123
|
+
const ctx = useDynamicToggle();
|
|
124
|
+
const id = React$1.useId();
|
|
125
|
+
const isActive = ctx.value === value;
|
|
126
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("label", {
|
|
127
|
+
htmlFor: id,
|
|
128
|
+
"data-slot": "dt-option",
|
|
129
|
+
"data-active": isActive || void 0,
|
|
130
|
+
className: cn(dynamicToggleStyles.option, className),
|
|
131
|
+
children: /* @__PURE__ */ jsx("span", { children })
|
|
132
|
+
}), /* @__PURE__ */ jsx("input", {
|
|
133
|
+
"data-slot": "dt-radio",
|
|
134
|
+
type: "radio",
|
|
135
|
+
name: ctx.groupName,
|
|
136
|
+
id,
|
|
137
|
+
value,
|
|
138
|
+
checked: isActive,
|
|
139
|
+
onChange: () => ctx.setValue(value)
|
|
140
|
+
})] });
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* An expandable group of options. Shows `label` when collapsed,
|
|
144
|
+
* expands to show sub-options when one is active.
|
|
145
|
+
*
|
|
146
|
+
* @param props - Component props
|
|
147
|
+
* @returns React component
|
|
148
|
+
*/
|
|
149
|
+
function DynamicToggleGroup({ label, children, className }) {
|
|
150
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
151
|
+
"data-slot": "dt-group",
|
|
152
|
+
className: cn(dynamicToggleStyles.group, className),
|
|
153
|
+
children: [
|
|
154
|
+
/* @__PURE__ */ jsx("span", {
|
|
155
|
+
"data-slot": "dt-group-label",
|
|
156
|
+
className: dynamicToggleStyles.groupLabel,
|
|
157
|
+
children: label
|
|
158
|
+
}),
|
|
159
|
+
/* @__PURE__ */ jsx("div", {
|
|
160
|
+
"data-slot": "dt-group-indicator",
|
|
161
|
+
className: dynamicToggleStyles.groupIndicator
|
|
162
|
+
}),
|
|
163
|
+
children
|
|
164
|
+
]
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
//#endregion
|
|
169
|
+
export { DynamicToggle, DynamicToggleGroup, DynamicToggleOption };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/react-ui/ui/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,WAAW,CAAC;AAG1B,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAG3B,cAAc,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/react-ui/ui/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,WAAW,CAAC;AAG1B,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAG3B,cAAc,YAAY,CAAC;AAG3B,cAAc,iBAAiB,CAAC"}
|
|
@@ -39,3 +39,5 @@ import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScro
|
|
|
39
39
|
import { dataCardStyles, dataCardVariants } from "./DataCard/DataCard.styles.js";
|
|
40
40
|
import { DataCard, DataCardActions, DataCardBracket, DataCardLabel, DataCardToggle, DataCardValue, useDataCard } from "./DataCard/index.js";
|
|
41
41
|
import { TextFlow } from "./TextFlow/index.js";
|
|
42
|
+
import { dynamicToggleStyles } from "./DynamicToggle/DynamicToggle.styles.js";
|
|
43
|
+
import { DynamicToggle, DynamicToggleGroup, DynamicToggleOption } from "./DynamicToggle/index.js";
|
package/package.json
CHANGED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DynamicToggle — CSS-only animated transitions.
|
|
3
|
+
*
|
|
4
|
+
* Ported from @jh3yy codepen. All colors via CSS variables (semantic tokens).
|
|
5
|
+
* Uses :has(:checked) for state-driven animation.
|
|
6
|
+
* container-type: size on group for cqh units in clip-path.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
:root {
|
|
10
|
+
--dt-duration: 0.22s;
|
|
11
|
+
--dt-ease: cubic-bezier(0.22, 0.61, 0.36, 1);
|
|
12
|
+
--dt-drop-off: 0.45;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/* ── Track layout ── */
|
|
16
|
+
[data-slot="dt-track"] {
|
|
17
|
+
grid-template-columns: repeat(4, 1fr);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
[data-slot="dt-track"] > label:first-of-type,
|
|
21
|
+
[data-slot="dt-group"] {
|
|
22
|
+
grid-column: span 2;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/* ── Main indicator slide ── */
|
|
26
|
+
[data-slot="dt-indicator"] {
|
|
27
|
+
transition: translate var(--dt-duration) var(--dt-ease);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/* When primary (non-group) is checked → indicator on left */
|
|
31
|
+
[data-slot="dt-track"]:has(> input:checked) [data-slot="dt-indicator"] {
|
|
32
|
+
translate: 0 0;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/* When nothing in track is checked → group is active → indicator slides right */
|
|
36
|
+
[data-slot="dt-track"]:not(:has(> input:checked)) [data-slot="dt-indicator"] {
|
|
37
|
+
translate: 100% 0;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* ── Primary option text ── */
|
|
41
|
+
[data-slot="dt-track"]:has(> input:checked) > label {
|
|
42
|
+
color: var(--card);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
[data-slot="dt-track"]:not(:has(> input:checked)) > label {
|
|
46
|
+
color: var(--foreground);
|
|
47
|
+
opacity: var(--dt-drop-off);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/* ── Group container ── */
|
|
51
|
+
[data-slot="dt-group"] {
|
|
52
|
+
container-type: size;
|
|
53
|
+
grid-template-columns: 1fr 1fr;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/* ── Group collapsed label ("Changes") ── */
|
|
57
|
+
[data-slot="dt-group-label"] {
|
|
58
|
+
translate: -50% -80%;
|
|
59
|
+
transition: translate var(--dt-duration) var(--dt-ease),
|
|
60
|
+
scale var(--dt-duration) var(--dt-ease);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* When group is active → label moves up and shrinks */
|
|
64
|
+
[data-slot="dt-group"]:has(input:checked) [data-slot="dt-group-label"] {
|
|
65
|
+
translate: -50% -250%;
|
|
66
|
+
scale: 0.85;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/* ── Group internal indicator ── */
|
|
70
|
+
[data-slot="dt-group-indicator"] {
|
|
71
|
+
transition: translate var(--dt-duration) var(--dt-ease),
|
|
72
|
+
clip-path var(--dt-duration) var(--dt-ease),
|
|
73
|
+
background var(--dt-duration) var(--dt-ease);
|
|
74
|
+
/* Collapsed: clip to tiny pill in center */
|
|
75
|
+
clip-path: inset(73cqh calc(50% + 1px) calc(27cqh - 2px) calc(50% - 3px) round 100px);
|
|
76
|
+
translate: -50% 0;
|
|
77
|
+
background: var(--foreground);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/* When group is active → indicator expands and positions */
|
|
81
|
+
[data-slot="dt-group"]:has(input:checked) [data-slot="dt-group-indicator"] {
|
|
82
|
+
background: var(--card);
|
|
83
|
+
clip-path: inset(0 0 0 0 round 100px);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/* First sub-option checked → indicator left */
|
|
87
|
+
[data-slot="dt-group"]:has(input:nth-of-type(1):checked) [data-slot="dt-group-indicator"] {
|
|
88
|
+
translate: -100% 0;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/* Second sub-option checked → indicator right */
|
|
92
|
+
[data-slot="dt-group"]:has(input:nth-of-type(2):checked) [data-slot="dt-group-indicator"] {
|
|
93
|
+
translate: 0 0;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* ── Group option labels ── */
|
|
97
|
+
[data-slot="dt-group"] label {
|
|
98
|
+
color: var(--muted-foreground);
|
|
99
|
+
transition: color var(--dt-duration) var(--dt-ease),
|
|
100
|
+
opacity var(--dt-duration) var(--dt-ease);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
[data-slot="dt-group"] label span {
|
|
104
|
+
transition: scale var(--dt-duration) var(--dt-ease);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/* When group collapsed: hide sub-option labels */
|
|
108
|
+
[data-slot="dt-track"]:has(> input:checked) [data-slot="dt-group"] label {
|
|
109
|
+
color: var(--muted-foreground);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/* When group active: show labels, highlight active one */
|
|
113
|
+
[data-slot="dt-group"]:has(input:checked) label {
|
|
114
|
+
color: var(--muted-foreground);
|
|
115
|
+
opacity: 0.75;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
[data-slot="dt-group"]:has(input:nth-of-type(1):checked) label:nth-of-type(1),
|
|
119
|
+
[data-slot="dt-group"]:has(input:nth-of-type(2):checked) label:nth-of-type(2) {
|
|
120
|
+
color: var(--foreground);
|
|
121
|
+
opacity: 1;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/* Sub-option label scale animation */
|
|
125
|
+
[data-slot="dt-group"] label:nth-of-type(1) span {
|
|
126
|
+
scale: 0.75;
|
|
127
|
+
transform-origin: 150% 150%;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
[data-slot="dt-group"] label:nth-of-type(2) span {
|
|
131
|
+
scale: 0.75;
|
|
132
|
+
transform-origin: -65% 150%;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
[data-slot="dt-group"]:has(input:checked) label span {
|
|
136
|
+
scale: 1;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/* ── Screen reader only (hidden radios) ── */
|
|
140
|
+
[data-slot="dt-radio"] {
|
|
141
|
+
position: absolute;
|
|
142
|
+
width: 1px;
|
|
143
|
+
height: 1px;
|
|
144
|
+
padding: 0;
|
|
145
|
+
margin: -1px;
|
|
146
|
+
overflow: hidden;
|
|
147
|
+
clip: rect(0, 0, 0, 0);
|
|
148
|
+
white-space: nowrap;
|
|
149
|
+
border-width: 0;
|
|
150
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DynamicToggle style slots and types.
|
|
3
|
+
*
|
|
4
|
+
* Uses semantic CSS variables for all colors.
|
|
5
|
+
* Animation handled in DynamicToggle.css (clip-path, transitions).
|
|
6
|
+
*
|
|
7
|
+
* @module @mks2508/mks-ui/react/ui/DynamicToggle
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { StyleSlots } from '@/core/types';
|
|
11
|
+
|
|
12
|
+
/** Slot names for DynamicToggle */
|
|
13
|
+
export type DynamicToggleSlot =
|
|
14
|
+
| 'root'
|
|
15
|
+
| 'track'
|
|
16
|
+
| 'option'
|
|
17
|
+
| 'indicator'
|
|
18
|
+
| 'group'
|
|
19
|
+
| 'groupLabel'
|
|
20
|
+
| 'groupIndicator';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Default styles for each DynamicToggle slot.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```tsx
|
|
27
|
+
* <DynamicToggle slots={{ root: 'w-72', indicator: 'bg-primary' }}>
|
|
28
|
+
* ...
|
|
29
|
+
* </DynamicToggle>
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export const dynamicToggleStyles: StyleSlots<DynamicToggleSlot> = {
|
|
33
|
+
root: [
|
|
34
|
+
'relative rounded-full border border-border bg-card',
|
|
35
|
+
'p-[2px] select-none',
|
|
36
|
+
].join(' '),
|
|
37
|
+
track: [
|
|
38
|
+
'relative grid place-items-center',
|
|
39
|
+
'w-full h-full',
|
|
40
|
+
].join(' '),
|
|
41
|
+
option: [
|
|
42
|
+
'inline-grid place-items-center cursor-pointer',
|
|
43
|
+
'text-xs font-medium z-[2] h-full w-full',
|
|
44
|
+
'transition-[color,opacity] duration-[220ms]',
|
|
45
|
+
].join(' '),
|
|
46
|
+
indicator: [
|
|
47
|
+
'absolute w-1/2 left-0 top-0 bottom-0',
|
|
48
|
+
'bg-foreground rounded-full',
|
|
49
|
+
'pointer-events-none z-0',
|
|
50
|
+
].join(' '),
|
|
51
|
+
group: [
|
|
52
|
+
'relative w-full h-full grid',
|
|
53
|
+
'border border-transparent',
|
|
54
|
+
].join(' '),
|
|
55
|
+
groupLabel: [
|
|
56
|
+
'absolute left-1/2 top-1/2 z-[2]',
|
|
57
|
+
'text-xs font-medium text-foreground',
|
|
58
|
+
'pointer-events-none',
|
|
59
|
+
].join(' '),
|
|
60
|
+
groupIndicator: [
|
|
61
|
+
'absolute left-1/2 top-0 bottom-0',
|
|
62
|
+
'bg-foreground rounded-full',
|
|
63
|
+
'pointer-events-none z-0',
|
|
64
|
+
].join(' '),
|
|
65
|
+
};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DynamicToggle type definitions.
|
|
3
|
+
*
|
|
4
|
+
* A CSS-animated toggle where one option can expand into sub-options.
|
|
5
|
+
* Uses hidden radio inputs + `:has(:checked)` for zero-JS animation.
|
|
6
|
+
*
|
|
7
|
+
* @module @mks2508/mks-ui/react/ui/DynamicToggle
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { SlotOverrides } from '@/core/types';
|
|
11
|
+
import type { DynamicToggleSlot } from './DynamicToggle.styles';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Context shared between DynamicToggle root and its children.
|
|
15
|
+
*/
|
|
16
|
+
export type DynamicToggleContextType = {
|
|
17
|
+
/** Current selected value */
|
|
18
|
+
value: string;
|
|
19
|
+
/** Update selected value */
|
|
20
|
+
setValue: (value: string) => void;
|
|
21
|
+
/** Auto-generated radio group name */
|
|
22
|
+
groupName: string;
|
|
23
|
+
/** Whether a group child is active */
|
|
24
|
+
groupActive: boolean;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Props for the DynamicToggle root container.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```tsx
|
|
32
|
+
* <DynamicToggle value="tree" onValueChange={setVal}>
|
|
33
|
+
* <DynamicToggleOption value="tree">Tree</DynamicToggleOption>
|
|
34
|
+
* <DynamicToggleGroup label="Changes">
|
|
35
|
+
* <DynamicToggleOption value="flat">Flat</DynamicToggleOption>
|
|
36
|
+
* <DynamicToggleOption value="grouped">Grouped</DynamicToggleOption>
|
|
37
|
+
* </DynamicToggleGroup>
|
|
38
|
+
* </DynamicToggle>
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export interface IDynamicToggleProps {
|
|
42
|
+
/** Controlled value */
|
|
43
|
+
value?: string;
|
|
44
|
+
/** Uncontrolled default value */
|
|
45
|
+
defaultValue?: string;
|
|
46
|
+
/** Change callback */
|
|
47
|
+
onValueChange?: (value: string) => void;
|
|
48
|
+
/** Slot class overrides */
|
|
49
|
+
slots?: SlotOverrides<DynamicToggleSlot>;
|
|
50
|
+
/** Additional class for the root */
|
|
51
|
+
className?: string;
|
|
52
|
+
/** DynamicToggleOption and DynamicToggleGroup children */
|
|
53
|
+
children: React.ReactNode;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Props for a single toggle option (top-level or inside a group).
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```tsx
|
|
61
|
+
* <DynamicToggleOption value="tree">Tree</DynamicToggleOption>
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export interface IDynamicToggleOptionProps {
|
|
65
|
+
/** Value this option represents */
|
|
66
|
+
value: string;
|
|
67
|
+
/** Label content */
|
|
68
|
+
children: React.ReactNode;
|
|
69
|
+
/** Additional class */
|
|
70
|
+
className?: string;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Props for an expandable group of options.
|
|
75
|
+
* When none of the group's options are active, shows the collapsed `label`.
|
|
76
|
+
* When one is active, expands to show all sub-options.
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* ```tsx
|
|
80
|
+
* <DynamicToggleGroup label="Changes">
|
|
81
|
+
* <DynamicToggleOption value="flat">Flat</DynamicToggleOption>
|
|
82
|
+
* <DynamicToggleOption value="grouped">Grouped</DynamicToggleOption>
|
|
83
|
+
* </DynamicToggleGroup>
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
export interface IDynamicToggleGroupProps {
|
|
87
|
+
/** Label shown when collapsed */
|
|
88
|
+
label: string;
|
|
89
|
+
/** DynamicToggleOption children */
|
|
90
|
+
children: React.ReactNode;
|
|
91
|
+
/** Additional class */
|
|
92
|
+
className?: string;
|
|
93
|
+
}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* DynamicToggle — CSS-animated toggle where one option expands into sub-options.
|
|
5
|
+
*
|
|
6
|
+
* Uses hidden radio inputs + `:has(:checked)` for zero-JS animation.
|
|
7
|
+
* The indicator slides between options, and group options expand/collapse
|
|
8
|
+
* with a clip-path reveal animation.
|
|
9
|
+
*
|
|
10
|
+
* @module @mks2508/mks-ui/react/ui/DynamicToggle
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```tsx
|
|
14
|
+
* <DynamicToggle value="tree" onValueChange={setVal}>
|
|
15
|
+
* <DynamicToggleOption value="tree">Tree</DynamicToggleOption>
|
|
16
|
+
* <DynamicToggleGroup label="Changes">
|
|
17
|
+
* <DynamicToggleOption value="flat">Flat</DynamicToggleOption>
|
|
18
|
+
* <DynamicToggleOption value="grouped">Grouped</DynamicToggleOption>
|
|
19
|
+
* </DynamicToggleGroup>
|
|
20
|
+
* </DynamicToggle>
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import * as React from 'react';
|
|
25
|
+
import { useControlledState } from '@/react-ui/hooks/State/UseControlledState';
|
|
26
|
+
import { getStrictContext } from '@/react-ui/lib/get-strict-context';
|
|
27
|
+
import { cn } from '@/react-ui/lib/utils';
|
|
28
|
+
import { dynamicToggleStyles } from './DynamicToggle.styles';
|
|
29
|
+
import type {
|
|
30
|
+
DynamicToggleContextType,
|
|
31
|
+
IDynamicToggleProps,
|
|
32
|
+
IDynamicToggleOptionProps,
|
|
33
|
+
IDynamicToggleGroupProps,
|
|
34
|
+
} from './DynamicToggle.types';
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
// CSS injection (same pattern as Tabs — inject once into document head)
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
|
|
39
|
+
const DT_CSS_ID = 'mks-dynamic-toggle-css';
|
|
40
|
+
|
|
41
|
+
/** Reads the CSS file content at build time via rolldown */
|
|
42
|
+
const DT_CSS = `
|
|
43
|
+
:root{--dt-duration:0.22s;--dt-ease:cubic-bezier(0.22,0.61,0.36,1);--dt-drop-off:0.45}
|
|
44
|
+
[data-slot="dt-track"]{grid-template-columns:repeat(4,1fr)}
|
|
45
|
+
[data-slot="dt-track"]>label:first-of-type,[data-slot="dt-group"]{grid-column:span 2}
|
|
46
|
+
[data-slot="dt-indicator"]{transition:translate var(--dt-duration) var(--dt-ease)}
|
|
47
|
+
[data-slot="dt-track"]:has(>input:checked) [data-slot="dt-indicator"]{translate:0 0}
|
|
48
|
+
[data-slot="dt-track"]:not(:has(>input:checked)) [data-slot="dt-indicator"]{translate:100% 0}
|
|
49
|
+
[data-slot="dt-track"]:has(>input:checked)>label{color:var(--card)}
|
|
50
|
+
[data-slot="dt-track"]:not(:has(>input:checked))>label{color:var(--foreground);opacity:var(--dt-drop-off)}
|
|
51
|
+
[data-slot="dt-group"]{container-type:size;grid-template-columns:1fr 1fr}
|
|
52
|
+
[data-slot="dt-group-label"]{translate:-50% -80%;transition:translate var(--dt-duration) var(--dt-ease),scale var(--dt-duration) var(--dt-ease)}
|
|
53
|
+
[data-slot="dt-group"]:has(input:checked) [data-slot="dt-group-label"]{translate:-50% -250%;scale:0.85}
|
|
54
|
+
[data-slot="dt-group-indicator"]{transition:translate var(--dt-duration) var(--dt-ease),clip-path var(--dt-duration) var(--dt-ease),background var(--dt-duration) var(--dt-ease);clip-path:inset(73cqh calc(50% + 1px) calc(27cqh - 2px) calc(50% - 3px) round 100px);translate:-50% 0;background:var(--foreground)}
|
|
55
|
+
[data-slot="dt-group"]:has(input:checked) [data-slot="dt-group-indicator"]{background:var(--card);clip-path:inset(0 0 0 0 round 100px)}
|
|
56
|
+
[data-slot="dt-group"]:has(input:nth-of-type(1):checked) [data-slot="dt-group-indicator"]{translate:-100% 0}
|
|
57
|
+
[data-slot="dt-group"]:has(input:nth-of-type(2):checked) [data-slot="dt-group-indicator"]{translate:0 0}
|
|
58
|
+
[data-slot="dt-group"] label{color:var(--muted-foreground);transition:color var(--dt-duration) var(--dt-ease),opacity var(--dt-duration) var(--dt-ease)}
|
|
59
|
+
[data-slot="dt-group"] label span{transition:scale var(--dt-duration) var(--dt-ease)}
|
|
60
|
+
[data-slot="dt-track"]:has(>input:checked) [data-slot="dt-group"] label{color:var(--muted-foreground)}
|
|
61
|
+
[data-slot="dt-group"]:has(input:checked) label{color:var(--muted-foreground);opacity:0.75}
|
|
62
|
+
[data-slot="dt-group"]:has(input:nth-of-type(1):checked) label:nth-of-type(1),[data-slot="dt-group"]:has(input:nth-of-type(2):checked) label:nth-of-type(2){color:var(--foreground);opacity:1}
|
|
63
|
+
[data-slot="dt-group"] label:nth-of-type(1) span{scale:0.75;transform-origin:150% 150%}
|
|
64
|
+
[data-slot="dt-group"] label:nth-of-type(2) span{scale:0.75;transform-origin:-65% 150%}
|
|
65
|
+
[data-slot="dt-group"]:has(input:checked) label span{scale:1}
|
|
66
|
+
[data-slot="dt-radio"]{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}
|
|
67
|
+
`;
|
|
68
|
+
|
|
69
|
+
function useDynamicToggleCSS() {
|
|
70
|
+
React.useEffect(() => {
|
|
71
|
+
if (typeof document === 'undefined') return;
|
|
72
|
+
if (document.getElementById(DT_CSS_ID)) return;
|
|
73
|
+
const style = document.createElement('style');
|
|
74
|
+
style.id = DT_CSS_ID;
|
|
75
|
+
style.textContent = DT_CSS;
|
|
76
|
+
document.head.appendChild(style);
|
|
77
|
+
}, []);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
// Context
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
|
|
84
|
+
const [DynamicToggleProvider, useDynamicToggle] =
|
|
85
|
+
getStrictContext<DynamicToggleContextType>('DynamicToggleContext');
|
|
86
|
+
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
// DynamicToggle (Root)
|
|
89
|
+
// ---------------------------------------------------------------------------
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Root container — pill-shaped toggle control.
|
|
93
|
+
*
|
|
94
|
+
* @param props - Component props
|
|
95
|
+
* @returns React component
|
|
96
|
+
*/
|
|
97
|
+
function DynamicToggle({
|
|
98
|
+
slots,
|
|
99
|
+
className,
|
|
100
|
+
children,
|
|
101
|
+
...props
|
|
102
|
+
}: IDynamicToggleProps) {
|
|
103
|
+
useDynamicToggleCSS();
|
|
104
|
+
|
|
105
|
+
const [value, setValue] = useControlledState({
|
|
106
|
+
value: props.value,
|
|
107
|
+
defaultValue: props.defaultValue ?? '',
|
|
108
|
+
onChange: props.onValueChange,
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const groupName = React.useId();
|
|
112
|
+
|
|
113
|
+
// Detect if group child is active (check children for group values)
|
|
114
|
+
const groupValues = React.useMemo(() => {
|
|
115
|
+
const vals: string[] = [];
|
|
116
|
+
React.Children.forEach(children, (child) => {
|
|
117
|
+
if (React.isValidElement<IDynamicToggleGroupProps>(child) && child.type === DynamicToggleGroup) {
|
|
118
|
+
React.Children.forEach(child.props.children, (gc) => {
|
|
119
|
+
if (React.isValidElement<IDynamicToggleOptionProps>(gc) && gc.props.value) {
|
|
120
|
+
vals.push(gc.props.value);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
return vals;
|
|
126
|
+
}, [children]);
|
|
127
|
+
|
|
128
|
+
const groupActive = groupValues.includes(value);
|
|
129
|
+
|
|
130
|
+
return (
|
|
131
|
+
<DynamicToggleProvider value={{ value, setValue, groupName, groupActive }}>
|
|
132
|
+
<div
|
|
133
|
+
data-slot="dt-root"
|
|
134
|
+
data-group-active={groupActive || undefined}
|
|
135
|
+
className={cn(dynamicToggleStyles.root, slots?.root, className)}
|
|
136
|
+
>
|
|
137
|
+
<div
|
|
138
|
+
data-slot="dt-track"
|
|
139
|
+
className={cn(dynamicToggleStyles.track, slots?.track)}
|
|
140
|
+
>
|
|
141
|
+
{/* Main sliding indicator */}
|
|
142
|
+
<div
|
|
143
|
+
data-slot="dt-indicator"
|
|
144
|
+
className={cn(dynamicToggleStyles.indicator, slots?.indicator)}
|
|
145
|
+
/>
|
|
146
|
+
{children}
|
|
147
|
+
</div>
|
|
148
|
+
</div>
|
|
149
|
+
</DynamicToggleProvider>
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// ---------------------------------------------------------------------------
|
|
154
|
+
// DynamicToggleOption
|
|
155
|
+
// ---------------------------------------------------------------------------
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* A single toggle option — renders a hidden radio + visible label.
|
|
159
|
+
* Can be used at the top level or inside a DynamicToggleGroup.
|
|
160
|
+
*
|
|
161
|
+
* @param props - Component props
|
|
162
|
+
* @returns React component
|
|
163
|
+
*/
|
|
164
|
+
function DynamicToggleOption({ value, children, className }: IDynamicToggleOptionProps) {
|
|
165
|
+
const ctx = useDynamicToggle();
|
|
166
|
+
const id = React.useId();
|
|
167
|
+
const isActive = ctx.value === value;
|
|
168
|
+
|
|
169
|
+
return (
|
|
170
|
+
<>
|
|
171
|
+
<label
|
|
172
|
+
htmlFor={id}
|
|
173
|
+
data-slot="dt-option"
|
|
174
|
+
data-active={isActive || undefined}
|
|
175
|
+
className={cn(dynamicToggleStyles.option, className)}
|
|
176
|
+
>
|
|
177
|
+
<span>{children}</span>
|
|
178
|
+
</label>
|
|
179
|
+
<input
|
|
180
|
+
data-slot="dt-radio"
|
|
181
|
+
type="radio"
|
|
182
|
+
name={ctx.groupName}
|
|
183
|
+
id={id}
|
|
184
|
+
value={value}
|
|
185
|
+
checked={isActive}
|
|
186
|
+
onChange={() => ctx.setValue(value)}
|
|
187
|
+
/>
|
|
188
|
+
</>
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// ---------------------------------------------------------------------------
|
|
193
|
+
// DynamicToggleGroup
|
|
194
|
+
// ---------------------------------------------------------------------------
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* An expandable group of options. Shows `label` when collapsed,
|
|
198
|
+
* expands to show sub-options when one is active.
|
|
199
|
+
*
|
|
200
|
+
* @param props - Component props
|
|
201
|
+
* @returns React component
|
|
202
|
+
*/
|
|
203
|
+
function DynamicToggleGroup({ label, children, className }: IDynamicToggleGroupProps) {
|
|
204
|
+
return (
|
|
205
|
+
<div
|
|
206
|
+
data-slot="dt-group"
|
|
207
|
+
className={cn(dynamicToggleStyles.group, className)}
|
|
208
|
+
>
|
|
209
|
+
{/* Collapsed label */}
|
|
210
|
+
<span
|
|
211
|
+
data-slot="dt-group-label"
|
|
212
|
+
className={dynamicToggleStyles.groupLabel}
|
|
213
|
+
>
|
|
214
|
+
{label}
|
|
215
|
+
</span>
|
|
216
|
+
{/* Internal sliding indicator */}
|
|
217
|
+
<div
|
|
218
|
+
data-slot="dt-group-indicator"
|
|
219
|
+
className={dynamicToggleStyles.groupIndicator}
|
|
220
|
+
/>
|
|
221
|
+
{children}
|
|
222
|
+
</div>
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// ---------------------------------------------------------------------------
|
|
227
|
+
// Exports
|
|
228
|
+
// ---------------------------------------------------------------------------
|
|
229
|
+
|
|
230
|
+
export { DynamicToggle, DynamicToggleOption, DynamicToggleGroup };
|
|
231
|
+
|
|
232
|
+
export type {
|
|
233
|
+
IDynamicToggleProps,
|
|
234
|
+
IDynamicToggleOptionProps,
|
|
235
|
+
IDynamicToggleGroupProps,
|
|
236
|
+
DynamicToggleContextType,
|
|
237
|
+
} from './DynamicToggle.types';
|
|
238
|
+
|
|
239
|
+
export type { DynamicToggleSlot } from './DynamicToggle.styles';
|
|
240
|
+
export { dynamicToggleStyles } from './DynamicToggle.styles';
|
package/src/react-ui/ui/index.ts
CHANGED
|
File without changes
|
|
File without changes
|