@sixthshift/design-system 0.5.0 → 0.7.0
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/README.md +132 -87
- package/dist/components/Badge/Badge.d.ts +12 -4
- package/dist/components/Badge/Badge.d.ts.map +1 -1
- package/dist/components/Badge/Badge.js +8 -6
- package/dist/components/Badge/Badge.js.map +1 -1
- package/dist/components/Button/Button.d.ts +26 -3
- package/dist/components/Button/Button.d.ts.map +1 -1
- package/dist/components/Button/Button.js +14 -5
- package/dist/components/Button/Button.js.map +1 -1
- package/dist/components/Calendar/Calendar.d.ts.map +1 -1
- package/dist/components/Calendar/Calendar.js +1 -0
- package/dist/components/Calendar/Calendar.js.map +1 -1
- package/dist/components/Calendar/CalendarView.d.ts +0 -11
- package/dist/components/Calendar/CalendarView.d.ts.map +1 -1
- package/dist/components/Calendar/CalendarView.js +1 -0
- package/dist/components/Calendar/CalendarView.js.map +1 -1
- package/dist/components/Calendar/calendar.hooks.d.ts.map +1 -1
- package/dist/components/Calendar/calendar.hooks.js +1 -0
- package/dist/components/Calendar/calendar.hooks.js.map +1 -1
- package/dist/components/Card/Card.d.ts +2 -2
- package/dist/components/Card/Card.d.ts.map +1 -1
- package/dist/components/Card/Card.js +3 -2
- package/dist/components/Card/Card.js.map +1 -1
- package/dist/components/Checkbox/Checkbox.d.ts.map +1 -1
- package/dist/components/Checkbox/Checkbox.js +1 -0
- package/dist/components/Checkbox/Checkbox.js.map +1 -1
- package/dist/components/CheckboxGroup/CheckboxGroup.d.ts.map +1 -1
- package/dist/components/CheckboxGroup/CheckboxGroup.js +1 -0
- package/dist/components/CheckboxGroup/CheckboxGroup.js.map +1 -1
- package/dist/components/Code/Editor/Editor.d.ts.map +1 -1
- package/dist/components/Code/Editor/Editor.js +1 -0
- package/dist/components/Code/Editor/Editor.js.map +1 -1
- package/dist/components/Code/Workspace/StatusBar.d.ts.map +1 -1
- package/dist/components/Code/Workspace/StatusBar.js +1 -0
- package/dist/components/Code/Workspace/StatusBar.js.map +1 -1
- package/dist/components/Code/Workspace/Toolbar.d.ts.map +1 -1
- package/dist/components/Code/Workspace/Toolbar.js +1 -0
- package/dist/components/Code/Workspace/Toolbar.js.map +1 -1
- package/dist/components/Code/Workspace/Workspace.d.ts.map +1 -1
- package/dist/components/Code/Workspace/Workspace.js +1 -0
- package/dist/components/Code/Workspace/Workspace.js.map +1 -1
- package/dist/components/DatePicker/DatePicker.d.ts.map +1 -1
- package/dist/components/DatePicker/DatePicker.js +7 -1
- package/dist/components/DatePicker/DatePicker.js.map +1 -1
- package/dist/components/DatePicker/DateSegments.d.ts.map +1 -1
- package/dist/components/DatePicker/DateSegments.js +1 -0
- package/dist/components/DatePicker/DateSegments.js.map +1 -1
- package/dist/components/DatePicker/datepicker.hooks.d.ts.map +1 -1
- package/dist/components/DatePicker/datepicker.hooks.js +1 -0
- package/dist/components/DatePicker/datepicker.hooks.js.map +1 -1
- package/dist/components/DateRangePicker/DateRangePicker.d.ts.map +1 -1
- package/dist/components/DateRangePicker/DateRangePicker.js +1 -0
- package/dist/components/DateRangePicker/DateRangePicker.js.map +1 -1
- package/dist/components/DateTimePicker/DateTimePicker.d.ts.map +1 -1
- package/dist/components/DateTimePicker/DateTimePicker.js +7 -1
- package/dist/components/DateTimePicker/DateTimePicker.js.map +1 -1
- package/dist/components/DateTimePicker/DateTimeSegments.d.ts.map +1 -1
- package/dist/components/DateTimePicker/DateTimeSegments.js +1 -0
- package/dist/components/DateTimePicker/DateTimeSegments.js.map +1 -1
- package/dist/components/DateTimeRangePicker/DateTimeRangePicker.d.ts.map +1 -1
- package/dist/components/DateTimeRangePicker/DateTimeRangePicker.js +7 -1
- package/dist/components/DateTimeRangePicker/DateTimeRangePicker.js.map +1 -1
- package/dist/components/FormField/FormField.d.ts +31 -10
- package/dist/components/FormField/FormField.d.ts.map +1 -1
- package/dist/components/FormField/FormField.js +46 -17
- package/dist/components/FormField/FormField.js.map +1 -1
- package/dist/components/FormField/index.d.ts +2 -2
- package/dist/components/FormField/index.d.ts.map +1 -1
- package/dist/components/FormField/index.js +1 -1
- package/dist/components/FormField/index.js.map +1 -1
- package/dist/components/HoverCard/HoverCard.d.ts.map +1 -1
- package/dist/components/HoverCard/HoverCard.js +4 -0
- package/dist/components/HoverCard/HoverCard.js.map +1 -1
- package/dist/components/HoverCard/components/HoverCardContent.d.ts +16 -2
- package/dist/components/HoverCard/components/HoverCardContent.d.ts.map +1 -1
- package/dist/components/HoverCard/components/HoverCardContent.js +4 -3
- package/dist/components/HoverCard/components/HoverCardContent.js.map +1 -1
- package/dist/components/HoverCard/components/HoverCardContext.d.ts +2 -0
- package/dist/components/HoverCard/components/HoverCardContext.d.ts.map +1 -1
- package/dist/components/HoverCard/components/HoverCardContext.js +1 -0
- package/dist/components/HoverCard/components/HoverCardContext.js.map +1 -1
- package/dist/components/HoverCard/components/HoverCardTrigger.d.ts.map +1 -1
- package/dist/components/HoverCard/components/HoverCardTrigger.js +1 -0
- package/dist/components/HoverCard/components/HoverCardTrigger.js.map +1 -1
- package/dist/components/Message/Message.d.ts +5 -1
- package/dist/components/Message/Message.d.ts.map +1 -1
- package/dist/components/Message/Message.js +3 -3
- package/dist/components/Message/Message.js.map +1 -1
- package/dist/components/Modal/Modal.d.ts +26 -9
- package/dist/components/Modal/Modal.d.ts.map +1 -1
- package/dist/components/Modal/Modal.js +46 -21
- package/dist/components/Modal/Modal.js.map +1 -1
- package/dist/components/Modal/components/ModalContext.d.ts.map +1 -1
- package/dist/components/Modal/components/ModalContext.js +1 -0
- package/dist/components/Modal/components/ModalContext.js.map +1 -1
- package/dist/components/Modal/components/ModalHeader.d.ts.map +1 -1
- package/dist/components/Modal/components/ModalHeader.js +1 -0
- package/dist/components/Modal/components/ModalHeader.js.map +1 -1
- package/dist/components/Popover/Popover.d.ts +3 -1
- package/dist/components/Popover/Popover.d.ts.map +1 -1
- package/dist/components/Popover/Popover.js +8 -0
- package/dist/components/Popover/Popover.js.map +1 -1
- package/dist/components/Popover/components/PopoverBody.d.ts +16 -2
- package/dist/components/Popover/components/PopoverBody.d.ts.map +1 -1
- package/dist/components/Popover/components/PopoverBody.js +4 -3
- package/dist/components/Popover/components/PopoverBody.js.map +1 -1
- package/dist/components/Popover/components/PopoverClose.d.ts.map +1 -1
- package/dist/components/Popover/components/PopoverClose.js +1 -0
- package/dist/components/Popover/components/PopoverClose.js.map +1 -1
- package/dist/components/Popover/components/PopoverContext.d.ts +2 -0
- package/dist/components/Popover/components/PopoverContext.d.ts.map +1 -1
- package/dist/components/Popover/components/PopoverContext.js +1 -0
- package/dist/components/Popover/components/PopoverContext.js.map +1 -1
- package/dist/components/Popover/components/PopoverTrigger.d.ts.map +1 -1
- package/dist/components/Popover/components/PopoverTrigger.js +1 -0
- package/dist/components/Popover/components/PopoverTrigger.js.map +1 -1
- package/dist/components/RadioButton/RadioButton.d.ts.map +1 -1
- package/dist/components/RadioButton/RadioButton.js +1 -0
- package/dist/components/RadioButton/RadioButton.js.map +1 -1
- package/dist/components/RadioButtonGroup/RadioButtonGroup.d.ts.map +1 -1
- package/dist/components/RadioButtonGroup/RadioButtonGroup.js +1 -0
- package/dist/components/RadioButtonGroup/RadioButtonGroup.js.map +1 -1
- package/dist/components/SearchInput/SearchInput.d.ts +10 -7
- package/dist/components/SearchInput/SearchInput.d.ts.map +1 -1
- package/dist/components/SearchInput/SearchInput.js +11 -6
- package/dist/components/SearchInput/SearchInput.js.map +1 -1
- package/dist/components/Select/Select.d.ts +6 -0
- package/dist/components/Select/Select.d.ts.map +1 -1
- package/dist/components/Select/Select.js +11 -4
- package/dist/components/Select/Select.js.map +1 -1
- package/dist/components/Select/SelectDropdown.d.ts +3 -1
- package/dist/components/Select/SelectDropdown.d.ts.map +1 -1
- package/dist/components/Select/SelectDropdown.js +3 -2
- package/dist/components/Select/SelectDropdown.js.map +1 -1
- package/dist/components/Select/SelectTrigger.d.ts.map +1 -1
- package/dist/components/Select/SelectTrigger.js +5 -1
- package/dist/components/Select/SelectTrigger.js.map +1 -1
- package/dist/components/Select/useSelectKeyboard.d.ts.map +1 -1
- package/dist/components/Select/useSelectKeyboard.js +1 -0
- package/dist/components/Select/useSelectKeyboard.js.map +1 -1
- package/dist/components/Sheet/Sheet.d.ts +12 -0
- package/dist/components/Sheet/Sheet.d.ts.map +1 -1
- package/dist/components/Sheet/Sheet.js +26 -10
- package/dist/components/Sheet/Sheet.js.map +1 -1
- package/dist/components/Sheet/components/SheetContext.d.ts.map +1 -1
- package/dist/components/Sheet/components/SheetContext.js +1 -0
- package/dist/components/Sheet/components/SheetContext.js.map +1 -1
- package/dist/components/Sheet/components/SheetHeader.d.ts.map +1 -1
- package/dist/components/Sheet/components/SheetHeader.js +1 -0
- package/dist/components/Sheet/components/SheetHeader.js.map +1 -1
- package/dist/components/Spinner/Spinner.d.ts +2 -2
- package/dist/components/Spinner/Spinner.js +2 -2
- package/dist/components/Switch/Switch.d.ts.map +1 -1
- package/dist/components/Switch/Switch.js +1 -0
- package/dist/components/Switch/Switch.js.map +1 -1
- package/dist/components/Tabs/Tabs.d.ts.map +1 -1
- package/dist/components/Tabs/Tabs.js +1 -0
- package/dist/components/Tabs/Tabs.js.map +1 -1
- package/dist/components/Tabs/components/TabsContext.d.ts.map +1 -1
- package/dist/components/Tabs/components/TabsContext.js +1 -0
- package/dist/components/Tabs/components/TabsContext.js.map +1 -1
- package/dist/components/Tabs/components/TabsList.d.ts.map +1 -1
- package/dist/components/Tabs/components/TabsList.js +1 -0
- package/dist/components/Tabs/components/TabsList.js.map +1 -1
- package/dist/components/Tabs/components/TabsPanels.d.ts.map +1 -1
- package/dist/components/Tabs/components/TabsPanels.js +1 -0
- package/dist/components/Tabs/components/TabsPanels.js.map +1 -1
- package/dist/components/Tabs/components/TabsTrigger.d.ts.map +1 -1
- package/dist/components/Tabs/components/TabsTrigger.js +2 -1
- package/dist/components/Tabs/components/TabsTrigger.js.map +1 -1
- package/dist/components/TagChip/TagChip.d.ts.map +1 -1
- package/dist/components/TagChip/TagChip.js +1 -0
- package/dist/components/TagChip/TagChip.js.map +1 -1
- package/dist/components/TagInput/TagInput.d.ts +8 -2
- package/dist/components/TagInput/TagInput.d.ts.map +1 -1
- package/dist/components/TagInput/TagInput.js +8 -5
- package/dist/components/TagInput/TagInput.js.map +1 -1
- package/dist/components/Textarea/Textarea.d.ts +21 -5
- package/dist/components/Textarea/Textarea.d.ts.map +1 -1
- package/dist/components/Textarea/Textarea.js +31 -5
- package/dist/components/Textarea/Textarea.js.map +1 -1
- package/dist/components/TimePicker/PeriodSelector.d.ts.map +1 -1
- package/dist/components/TimePicker/PeriodSelector.js +1 -0
- package/dist/components/TimePicker/PeriodSelector.js.map +1 -1
- package/dist/components/TimePicker/TimeColumn.d.ts.map +1 -1
- package/dist/components/TimePicker/TimeColumn.js +1 -0
- package/dist/components/TimePicker/TimeColumn.js.map +1 -1
- package/dist/components/TimePicker/TimePicker.d.ts.map +1 -1
- package/dist/components/TimePicker/TimePicker.js +7 -1
- package/dist/components/TimePicker/TimePicker.js.map +1 -1
- package/dist/components/TimePicker/TimeSegments.d.ts.map +1 -1
- package/dist/components/TimePicker/TimeSegments.js +1 -0
- package/dist/components/TimePicker/TimeSegments.js.map +1 -1
- package/dist/components/Toast/Toast.d.ts +16 -2
- package/dist/components/Toast/Toast.d.ts.map +1 -1
- package/dist/components/Toast/Toast.js +17 -7
- package/dist/components/Toast/Toast.js.map +1 -1
- package/dist/components/Toggle/Toggle.d.ts +5 -1
- package/dist/components/Toggle/Toggle.d.ts.map +1 -1
- package/dist/components/Toggle/Toggle.js +4 -3
- package/dist/components/Toggle/Toggle.js.map +1 -1
- package/dist/components/ToggleGroup/ToggleGroupMultiple.d.ts +3 -0
- package/dist/components/ToggleGroup/ToggleGroupMultiple.d.ts.map +1 -1
- package/dist/components/ToggleGroup/ToggleGroupMultiple.js +4 -3
- package/dist/components/ToggleGroup/ToggleGroupMultiple.js.map +1 -1
- package/dist/components/ToggleGroup/ToggleGroupSingle.d.ts +3 -0
- package/dist/components/ToggleGroup/ToggleGroupSingle.d.ts.map +1 -1
- package/dist/components/ToggleGroup/ToggleGroupSingle.js +4 -3
- package/dist/components/ToggleGroup/ToggleGroupSingle.js.map +1 -1
- package/dist/components/ToggleGroup/toggleGroup.types.d.ts +13 -0
- package/dist/components/ToggleGroup/toggleGroup.types.d.ts.map +1 -1
- package/dist/components/Tooltip/Tooltip.d.ts +3 -1
- package/dist/components/Tooltip/Tooltip.d.ts.map +1 -1
- package/dist/components/Tooltip/Tooltip.js +4 -0
- package/dist/components/Tooltip/Tooltip.js.map +1 -1
- package/dist/components/Tooltip/components/TooltipBody.d.ts +16 -2
- package/dist/components/Tooltip/components/TooltipBody.d.ts.map +1 -1
- package/dist/components/Tooltip/components/TooltipBody.js +4 -3
- package/dist/components/Tooltip/components/TooltipBody.js.map +1 -1
- package/dist/components/Tooltip/components/TooltipContext.d.ts +2 -0
- package/dist/components/Tooltip/components/TooltipContext.d.ts.map +1 -1
- package/dist/components/Tooltip/components/TooltipContext.js +1 -0
- package/dist/components/Tooltip/components/TooltipContext.js.map +1 -1
- package/dist/components/Tooltip/components/TooltipTrigger.d.ts.map +1 -1
- package/dist/components/Tooltip/components/TooltipTrigger.js +1 -0
- package/dist/components/Tooltip/components/TooltipTrigger.js.map +1 -1
- package/dist/hooks/useCollapsible.d.ts.map +1 -1
- package/dist/hooks/useCollapsible.js +1 -0
- package/dist/hooks/useCollapsible.js.map +1 -1
- package/dist/hooks/useControllableState.d.ts.map +1 -1
- package/dist/hooks/useControllableState.js +1 -0
- package/dist/hooks/useControllableState.js.map +1 -1
- package/dist/hooks/useDebouncedCallback.d.ts.map +1 -1
- package/dist/hooks/useDebouncedCallback.js +1 -0
- package/dist/hooks/useDebouncedCallback.js.map +1 -1
- package/dist/hooks/useDebouncedState.d.ts.map +1 -1
- package/dist/hooks/useDebouncedState.js +1 -0
- package/dist/hooks/useDebouncedState.js.map +1 -1
- package/dist/hooks/useDeviceFormFactor.d.ts.map +1 -1
- package/dist/hooks/useDeviceFormFactor.js +1 -0
- package/dist/hooks/useDeviceFormFactor.js.map +1 -1
- package/dist/hooks/useLocalStorage.d.ts.map +1 -1
- package/dist/hooks/useLocalStorage.js +1 -0
- package/dist/hooks/useLocalStorage.js.map +1 -1
- package/dist/hooks/useMergedFloatingRef.d.ts.map +1 -1
- package/dist/hooks/useMergedFloatingRef.js +1 -0
- package/dist/hooks/useMergedFloatingRef.js.map +1 -1
- package/dist/hooks/usePresence.d.ts.map +1 -1
- package/dist/hooks/usePresence.js +1 -0
- package/dist/hooks/usePresence.js.map +1 -1
- package/dist/hooks/useStack.d.ts.map +1 -1
- package/dist/hooks/useStack.js +34 -10
- package/dist/hooks/useStack.js.map +1 -1
- package/dist/hooks/useTheme.d.ts.map +1 -1
- package/dist/hooks/useTheme.js +1 -0
- package/dist/hooks/useTheme.js.map +1 -1
- package/dist/internal/PickerField.d.ts.map +1 -1
- package/dist/internal/PickerField.js +1 -0
- package/dist/internal/PickerField.js.map +1 -1
- package/dist/internal/SegmentedField.d.ts.map +1 -1
- package/dist/internal/SegmentedField.js +1 -0
- package/dist/internal/SegmentedField.js.map +1 -1
- package/dist/internal/brandScope.d.ts +25 -0
- package/dist/internal/brandScope.d.ts.map +1 -0
- package/dist/internal/brandScope.js +37 -0
- package/dist/internal/brandScope.js.map +1 -0
- package/dist/internal/escapeLayers.d.ts +12 -0
- package/dist/internal/escapeLayers.d.ts.map +1 -0
- package/dist/internal/escapeLayers.js +48 -0
- package/dist/internal/escapeLayers.js.map +1 -0
- package/dist/lib/ErrorBoundary.d.ts.map +1 -1
- package/dist/lib/ErrorBoundary.js +1 -0
- package/dist/lib/ErrorBoundary.js.map +1 -1
- package/dist/lib/contexts/ComponentsContext/ComponentsContext.d.ts.map +1 -1
- package/dist/lib/contexts/ComponentsContext/ComponentsContext.js +1 -0
- package/dist/lib/contexts/ComponentsContext/ComponentsContext.js.map +1 -1
- package/dist/lib/contexts/OverlayContext/OverlayContext.d.ts +9 -4
- package/dist/lib/contexts/OverlayContext/OverlayContext.d.ts.map +1 -1
- package/dist/lib/contexts/OverlayContext/OverlayContext.js +25 -12
- package/dist/lib/contexts/OverlayContext/OverlayContext.js.map +1 -1
- package/dist/lib/contexts/OverlayContext/ToastStack.d.ts +20 -0
- package/dist/lib/contexts/OverlayContext/ToastStack.d.ts.map +1 -0
- package/dist/lib/contexts/OverlayContext/ToastStack.js +45 -0
- package/dist/lib/contexts/OverlayContext/ToastStack.js.map +1 -0
- package/dist/lib/contexts/OverlayContext/hooks/useModal.d.ts.map +1 -1
- package/dist/lib/contexts/OverlayContext/hooks/useModal.js +1 -0
- package/dist/lib/contexts/OverlayContext/hooks/useModal.js.map +1 -1
- package/dist/lib/contexts/OverlayContext/hooks/useToast.d.ts +27 -10
- package/dist/lib/contexts/OverlayContext/hooks/useToast.d.ts.map +1 -1
- package/dist/lib/contexts/OverlayContext/hooks/useToast.js +31 -49
- package/dist/lib/contexts/OverlayContext/hooks/useToast.js.map +1 -1
- package/dist/lib/contexts/OverlayContext/index.d.ts +2 -0
- package/dist/lib/contexts/OverlayContext/index.d.ts.map +1 -1
- package/dist/lib/contexts/OverlayContext/index.js +2 -0
- package/dist/lib/contexts/OverlayContext/index.js.map +1 -1
- package/dist/lib/contexts/OverlayContext/toastStore.d.ts +68 -0
- package/dist/lib/contexts/OverlayContext/toastStore.d.ts.map +1 -0
- package/dist/lib/contexts/OverlayContext/toastStore.js +69 -0
- package/dist/lib/contexts/OverlayContext/toastStore.js.map +1 -0
- package/dist/lib/withErrorBoundary.d.ts.map +1 -1
- package/dist/lib/withErrorBoundary.js +1 -0
- package/dist/lib/withErrorBoundary.js.map +1 -1
- package/dist/lib/withSuspenseAndErrorBoundary.d.ts.map +1 -1
- package/dist/lib/withSuspenseAndErrorBoundary.js +1 -0
- package/dist/lib/withSuspenseAndErrorBoundary.js.map +1 -1
- package/dist/{theme/schema.d.ts → theming/vocabulary.d.ts} +4 -4
- package/dist/theming/vocabulary.d.ts.map +1 -0
- package/dist/{theme/schema.js → theming/vocabulary.js} +4 -4
- package/dist/theming/vocabulary.js.map +1 -0
- package/package.json +18 -6
- package/src/components/Badge/Badge.tsx +12 -6
- package/src/components/Button/Button.tsx +25 -5
- package/src/{theme/recipes/button.css → components/Button/button.recipe.css} +2 -2
- package/src/components/Calendar/Calendar.tsx +2 -0
- package/src/components/Calendar/CalendarView.tsx +2 -0
- package/src/components/Calendar/calendar.hooks.ts +2 -0
- package/src/components/Card/Card.tsx +4 -2
- package/src/components/Checkbox/Checkbox.tsx +2 -0
- package/src/components/CheckboxGroup/CheckboxGroup.tsx +2 -0
- package/src/components/Code/Editor/Editor.tsx +2 -0
- package/src/components/Code/Workspace/StatusBar.tsx +2 -0
- package/src/components/Code/Workspace/Toolbar.tsx +2 -0
- package/src/components/Code/Workspace/Workspace.tsx +2 -0
- package/src/components/DatePicker/DatePicker.tsx +10 -0
- package/src/components/DatePicker/DateSegments.tsx +2 -0
- package/src/components/DatePicker/datepicker.hooks.ts +2 -0
- package/src/components/DateRangePicker/DateRangePicker.tsx +2 -0
- package/src/components/DateTimePicker/DateTimePicker.tsx +10 -1
- package/src/components/DateTimePicker/DateTimeSegments.tsx +2 -0
- package/src/components/DateTimeRangePicker/DateTimeRangePicker.tsx +10 -1
- package/src/components/FormField/FormField.tsx +94 -42
- package/src/components/FormField/index.ts +2 -2
- package/src/components/HoverCard/HoverCard.tsx +5 -0
- package/src/components/HoverCard/components/HoverCardContent.tsx +13 -3
- package/src/components/HoverCard/components/HoverCardContext.tsx +4 -0
- package/src/components/HoverCard/components/HoverCardTrigger.tsx +2 -0
- package/src/components/Message/Message.tsx +27 -23
- package/src/components/Modal/Modal.tsx +88 -51
- package/src/components/Modal/components/ModalContext.tsx +2 -0
- package/src/components/Modal/components/ModalHeader.tsx +2 -0
- package/src/components/Popover/Popover.tsx +10 -0
- package/src/components/Popover/components/PopoverBody.tsx +13 -3
- package/src/components/Popover/components/PopoverClose.tsx +2 -0
- package/src/components/Popover/components/PopoverContext.tsx +4 -0
- package/src/components/Popover/components/PopoverTrigger.tsx +2 -0
- package/src/components/RadioButton/RadioButton.tsx +2 -0
- package/src/components/RadioButtonGroup/RadioButtonGroup.tsx +2 -0
- package/src/components/SearchInput/SearchInput.tsx +15 -8
- package/src/components/Select/Select.tsx +27 -2
- package/src/components/Select/SelectDropdown.tsx +6 -0
- package/src/components/Select/SelectTrigger.tsx +5 -0
- package/src/components/Select/useSelectKeyboard.ts +2 -0
- package/src/components/Sheet/Sheet.tsx +59 -34
- package/src/components/Sheet/components/SheetContext.tsx +2 -0
- package/src/components/Sheet/components/SheetHeader.tsx +2 -0
- package/src/components/Spinner/Spinner.tsx +2 -2
- package/src/components/Switch/Switch.tsx +2 -0
- package/src/components/Tabs/Tabs.tsx +2 -0
- package/src/components/Tabs/components/TabsContext.tsx +2 -0
- package/src/components/Tabs/components/TabsList.tsx +2 -0
- package/src/components/Tabs/components/TabsPanels.tsx +2 -0
- package/src/components/Tabs/components/TabsTrigger.tsx +3 -1
- package/src/{theme/recipes/tabs.css → components/Tabs/tabs.recipe.css} +1 -1
- package/src/components/TagChip/TagChip.tsx +2 -0
- package/src/components/TagInput/TagInput.tsx +15 -6
- package/src/components/Textarea/Textarea.tsx +42 -6
- package/src/{theme/recipes/textarea.css → components/Textarea/textarea.recipe.css} +1 -1
- package/src/components/TimePicker/PeriodSelector.tsx +2 -0
- package/src/components/TimePicker/TimeColumn.tsx +2 -0
- package/src/components/TimePicker/TimePicker.tsx +10 -0
- package/src/components/TimePicker/TimeSegments.tsx +2 -0
- package/src/components/Toast/Toast.tsx +26 -6
- package/src/components/Toggle/Toggle.tsx +20 -3
- package/src/components/ToggleGroup/ToggleGroupMultiple.tsx +8 -1
- package/src/components/ToggleGroup/ToggleGroupSingle.tsx +8 -0
- package/src/components/ToggleGroup/toggleGroup.types.ts +13 -0
- package/src/components/Tooltip/Tooltip.tsx +5 -0
- package/src/components/Tooltip/components/TooltipBody.tsx +13 -3
- package/src/components/Tooltip/components/TooltipContext.tsx +4 -0
- package/src/components/Tooltip/components/TooltipTrigger.tsx +2 -0
- package/src/hooks/useCollapsible.ts +2 -0
- package/src/hooks/useControllableState.ts +2 -0
- package/src/hooks/useDebouncedCallback.ts +2 -0
- package/src/hooks/useDebouncedState.ts +2 -0
- package/src/hooks/useDeviceFormFactor.ts +2 -0
- package/src/hooks/useLocalStorage.ts +2 -0
- package/src/hooks/useMergedFloatingRef.ts +2 -0
- package/src/hooks/usePresence.ts +2 -0
- package/src/hooks/useStack.ts +35 -12
- package/src/hooks/useTheme.ts +2 -0
- package/src/internal/PickerField.tsx +2 -0
- package/src/internal/SegmentedField.tsx +2 -0
- package/src/internal/brandScope.ts +39 -0
- package/src/internal/escapeLayers.ts +53 -0
- package/src/lib/ErrorBoundary.tsx +2 -0
- package/src/lib/contexts/ComponentsContext/ComponentsContext.tsx +2 -0
- package/src/lib/contexts/OverlayContext/OverlayContext.tsx +42 -26
- package/src/lib/contexts/OverlayContext/ToastStack.tsx +71 -0
- package/src/lib/contexts/OverlayContext/hooks/useModal.tsx +2 -0
- package/src/lib/contexts/OverlayContext/hooks/useToast.ts +30 -73
- package/src/lib/contexts/OverlayContext/index.ts +2 -0
- package/src/lib/contexts/OverlayContext/toastStore.ts +138 -0
- package/src/lib/withErrorBoundary.tsx +2 -0
- package/src/lib/withSuspenseAndErrorBoundary.tsx +2 -0
- package/src/theme/linen/index.css +11 -0
- package/src/theme/linen/palette.css +87 -0
- package/src/theme/linen/theme.css +371 -0
- package/src/theming/tailwind.css +356 -0
- package/src/{theme/schema.ts → theming/vocabulary.ts} +3 -3
- package/dist/theme/index.d.ts +0 -9
- package/dist/theme/index.d.ts.map +0 -1
- package/dist/theme/index.js +0 -8
- package/dist/theme/index.js.map +0 -1
- package/dist/theme/schema.d.ts.map +0 -1
- package/dist/theme/schema.js.map +0 -1
- package/src/theme/index.ts +0 -39
- package/src/theme/recipes/index.css +0 -28
- package/src/theme/tokens.css +0 -824
- /package/src/{theme/recipes/badge.css → components/Badge/badge.recipe.css} +0 -0
- /package/src/{theme/recipes/card.css → components/Card/card.recipe.css} +0 -0
- /package/src/{theme/recipes/checkbox.css → components/Checkbox/checkbox.recipe.css} +0 -0
- /package/src/{theme/recipes/field.css → components/Field/field.recipe.css} +0 -0
- /package/src/{theme/recipes/input.css → components/Input/input.recipe.css} +0 -0
- /package/src/{theme/recipes/message.css → components/Message/message.recipe.css} +0 -0
- /package/src/{theme/recipes/modal.css → components/Modal/modal.recipe.css} +0 -0
- /package/src/{theme/recipes/radio-button.css → components/RadioButton/radio-button.recipe.css} +0 -0
- /package/src/{theme/recipes/search-input.css → components/SearchInput/search-input.recipe.css} +0 -0
- /package/src/{theme/recipes/select.css → components/Select/select.recipe.css} +0 -0
- /package/src/{theme/recipes/sheet.css → components/Sheet/sheet.recipe.css} +0 -0
- /package/src/{theme/recipes/spinner.css → components/Spinner/spinner.recipe.css} +0 -0
- /package/src/{theme/recipes/switch.css → components/Switch/switch.recipe.css} +0 -0
- /package/src/{theme/recipes/tag-chip.css → components/TagChip/tag-chip.recipe.css} +0 -0
- /package/src/{theme/recipes/toggle.css → components/Toggle/toggle.recipe.css} +0 -0
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import { Label } from "@sixthshift/design-system/label";
|
|
2
4
|
import { Message } from "@sixthshift/design-system/message";
|
|
3
5
|
import { Muted } from "@sixthshift/design-system/muted";
|
|
@@ -10,61 +12,111 @@ export type FormFieldFeedback = {
|
|
|
10
12
|
};
|
|
11
13
|
|
|
12
14
|
export type FormFieldProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
13
|
-
label:
|
|
14
|
-
description?:
|
|
15
|
+
label: React.ReactNode;
|
|
16
|
+
description?: React.ReactNode;
|
|
15
17
|
feedback?: FormFieldFeedback;
|
|
16
18
|
required?: boolean;
|
|
17
19
|
};
|
|
18
20
|
|
|
21
|
+
/** What FormField knows about the control it wraps — see `useFormField`. */
|
|
22
|
+
export type FormFieldContextValue = {
|
|
23
|
+
/** The id the control should carry, and the label's `htmlFor` target. */
|
|
24
|
+
id: string;
|
|
25
|
+
/** Space-separated ids of the description/feedback texts, for `aria-describedby`. `undefined` when there are none. */
|
|
26
|
+
describedBy: string | undefined;
|
|
27
|
+
/** True when `feedback.intent` is `"danger"` — the control should set `aria-invalid`. */
|
|
28
|
+
invalid: boolean;
|
|
29
|
+
/** Mirrors the `required` prop, for `aria-required`/`required` on the control. */
|
|
30
|
+
required: boolean;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const FormFieldContext = React.createContext<FormFieldContextValue | null>(null);
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* The wiring `FormField` provides to whatever control it wraps: `id`,
|
|
37
|
+
* `describedBy`, `invalid`, `required`. Direct element children get these
|
|
38
|
+
* cloned on automatically; a control nested deeper — inside a wrapper div, a
|
|
39
|
+
* render prop, a custom composite — can call this instead and apply them
|
|
40
|
+
* itself. Returns `null` outside a `FormField`.
|
|
41
|
+
*/
|
|
42
|
+
export const useFormField = () => React.useContext(FormFieldContext);
|
|
43
|
+
|
|
19
44
|
/**
|
|
20
45
|
* Wraps a form control with a label, optional description, and optional
|
|
21
46
|
* validation feedback — the composition point for `Input`, `Textarea`,
|
|
22
47
|
* `Select` and similar controls inside a form.
|
|
23
48
|
*
|
|
24
|
-
* Generates an `id` (or uses the one passed in) and
|
|
25
|
-
* `aria-describedby` (pointing at the description and/or feedback text)
|
|
26
|
-
* `aria-invalid` (when `feedback.intent` is `"danger"`)
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
49
|
+
* Generates an `id` (or uses the one passed in) and wires it, plus
|
|
50
|
+
* `aria-describedby` (pointing at the description and/or feedback text),
|
|
51
|
+
* `aria-invalid` (when `feedback.intent` is `"danger"`) and `aria-required`
|
|
52
|
+
* (when `required`), onto the first element child. A child's own attributes
|
|
53
|
+
* win: an explicit `id` is kept (and becomes the label's `htmlFor` target),
|
|
54
|
+
* an existing `aria-describedby` is merged with FormField's rather than
|
|
55
|
+
* replaced. Controls that aren't the first element child can self-wire via
|
|
56
|
+
* `useFormField`.
|
|
30
57
|
*
|
|
31
58
|
* Distinct from `Field`, which displays a read-only label/value pair rather
|
|
32
59
|
* than wrapping an interactive control.
|
|
33
60
|
*/
|
|
34
|
-
const FormField = React.forwardRef<HTMLDivElement, FormFieldProps>(
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
61
|
+
const FormField = React.forwardRef<HTMLDivElement, FormFieldProps>(
|
|
62
|
+
({ className, label, description, feedback, required = false, children, id, ...props }, ref) => {
|
|
63
|
+
const generatedId = React.useId();
|
|
64
|
+
const fieldId = id ?? generatedId;
|
|
65
|
+
|
|
66
|
+
// The label must point at the id the control actually carries: when the
|
|
67
|
+
// (first element) child brings its own id, that id wins over ours.
|
|
68
|
+
const childArray = React.Children.toArray(children);
|
|
69
|
+
const firstElementIndex = childArray.findIndex((child) => React.isValidElement(child));
|
|
70
|
+
const firstElement = firstElementIndex === -1 ? null : (childArray[firstElementIndex] as React.ReactElement<React.HTMLAttributes<HTMLElement>>);
|
|
71
|
+
const controlId = firstElement?.props.id ?? fieldId;
|
|
72
|
+
|
|
73
|
+
const descriptionId = description ? `${fieldId}-description` : undefined;
|
|
74
|
+
const feedbackId = feedback ? `${fieldId}-feedback` : undefined;
|
|
75
|
+
const invalid = feedback?.intent === "danger";
|
|
76
|
+
|
|
77
|
+
const contextValue = React.useMemo<FormFieldContextValue>(
|
|
78
|
+
() => ({
|
|
79
|
+
id: controlId,
|
|
80
|
+
describedBy: cn(descriptionId, feedbackId) || undefined,
|
|
81
|
+
invalid,
|
|
82
|
+
required,
|
|
83
|
+
}),
|
|
84
|
+
[controlId, descriptionId, feedbackId, invalid, required]
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
const wiredChildren = childArray.map((child, index) => {
|
|
88
|
+
if (index !== firstElementIndex || !React.isValidElement(child)) return child;
|
|
89
|
+
const childProps = child.props as React.HTMLAttributes<HTMLElement>;
|
|
90
|
+
return React.cloneElement(child, {
|
|
91
|
+
id: controlId,
|
|
92
|
+
// The child's own describedby (a tooltip, an external hint) is kept and
|
|
93
|
+
// FormField's ids are appended, rather than overwritten.
|
|
94
|
+
"aria-describedby": cn(childProps["aria-describedby"], descriptionId, feedbackId) || undefined,
|
|
95
|
+
"aria-invalid": childProps["aria-invalid"] ?? (invalid ? true : undefined),
|
|
96
|
+
"aria-required": childProps["aria-required"] ?? (required ? true : undefined),
|
|
97
|
+
} as React.HTMLAttributes<HTMLElement>);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
return (
|
|
101
|
+
<div ref={ref} className={cn("flex flex-col gap-1.5", className)} {...props}>
|
|
102
|
+
<Label htmlFor={controlId}>
|
|
103
|
+
{label}
|
|
104
|
+
{required && <span className="ml-0.5 text-fg-danger">*</span>}
|
|
105
|
+
</Label>
|
|
106
|
+
|
|
107
|
+
{description && <Muted id={descriptionId}>{description}</Muted>}
|
|
108
|
+
|
|
109
|
+
<FormFieldContext.Provider value={contextValue}>{wiredChildren}</FormFieldContext.Provider>
|
|
110
|
+
|
|
111
|
+
{feedback && (
|
|
112
|
+
<Message id={feedbackId} intent={feedback.intent} size="sm">
|
|
113
|
+
{feedback.message}
|
|
114
|
+
</Message>
|
|
115
|
+
)}
|
|
116
|
+
</div>
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
);
|
|
68
120
|
FormField.displayName = "FormField";
|
|
69
121
|
|
|
70
122
|
export { FormField };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export type { FormFieldFeedback, FormFieldProps } from "./FormField";
|
|
2
|
-
export { FormField } from "./FormField";
|
|
1
|
+
export type { FormFieldContextValue, FormFieldFeedback, FormFieldProps } from "./FormField";
|
|
2
|
+
export { FormField, useFormField } from "./FormField";
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import { autoUpdate, flip, offset, type Placement, safePolygon, shift, useDismiss, useFloating, useFocus, useHover, useInteractions } from "@floating-ui/react";
|
|
2
4
|
import { useControllableState } from "@sixthshift/design-system/hooks";
|
|
3
5
|
import type * as React from "react";
|
|
6
|
+
import { closestBrand } from "../../internal/brandScope";
|
|
4
7
|
import { HoverCardContent } from "./components/HoverCardContent";
|
|
5
8
|
import { HoverCardContext } from "./components/HoverCardContext";
|
|
6
9
|
import { HoverCardTrigger } from "./components/HoverCardTrigger";
|
|
@@ -65,6 +68,8 @@ const HoverCardRoot = ({
|
|
|
65
68
|
setFloating: refs.setFloating,
|
|
66
69
|
},
|
|
67
70
|
floatingStyles,
|
|
71
|
+
// Read at render: `domReference` is state, so this updates once the trigger mounts.
|
|
72
|
+
inheritedBrand: closestBrand(context.elements.domReference),
|
|
68
73
|
getReferenceProps,
|
|
69
74
|
getFloatingProps,
|
|
70
75
|
};
|
|
@@ -1,12 +1,21 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import { FloatingPortal } from "@floating-ui/react";
|
|
2
4
|
import { cn } from "@sixthshift/design-system/utils";
|
|
3
5
|
import * as React from "react";
|
|
4
6
|
import { useHoverCardContext } from "./HoverCardContext";
|
|
5
7
|
|
|
6
|
-
export type HoverCardContentProps = React.HTMLAttributes<HTMLDivElement
|
|
8
|
+
export type HoverCardContentProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
9
|
+
/**
|
|
10
|
+
* Name of a brand the theme defines, rendered as `data-brand`. Omitted, the
|
|
11
|
+
* body copies the brand scope of its trigger, which the portal would
|
|
12
|
+
* otherwise leave behind.
|
|
13
|
+
*/
|
|
14
|
+
brand?: string | undefined;
|
|
15
|
+
};
|
|
7
16
|
|
|
8
|
-
export const HoverCardContent = React.forwardRef<HTMLDivElement, HoverCardContentProps>(({ className, children, ...props }, forwardedRef) => {
|
|
9
|
-
const { open, refs, floatingStyles, getFloatingProps } = useHoverCardContext();
|
|
17
|
+
export const HoverCardContent = React.forwardRef<HTMLDivElement, HoverCardContentProps>(({ className, brand, children, ...props }, forwardedRef) => {
|
|
18
|
+
const { open, refs, floatingStyles, inheritedBrand, getFloatingProps } = useHoverCardContext();
|
|
10
19
|
|
|
11
20
|
if (!open) return null;
|
|
12
21
|
|
|
@@ -19,6 +28,7 @@ export const HoverCardContent = React.forwardRef<HTMLDivElement, HoverCardConten
|
|
|
19
28
|
else if (forwardedRef) forwardedRef.current = node;
|
|
20
29
|
}}
|
|
21
30
|
style={floatingStyles}
|
|
31
|
+
data-brand={brand ?? inheritedBrand}
|
|
22
32
|
className={cn("z-popover w-80 rounded-lg border border-border-normal bg-bg-normal p-4 shadow-lg", className)}
|
|
23
33
|
{...getFloatingProps(props)}
|
|
24
34
|
>
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import { createContext, useContext } from "react";
|
|
2
4
|
|
|
3
5
|
export type HoverCardContextValue = {
|
|
@@ -7,6 +9,8 @@ export type HoverCardContextValue = {
|
|
|
7
9
|
setFloating: (node: HTMLElement | null) => void;
|
|
8
10
|
};
|
|
9
11
|
floatingStyles: React.CSSProperties;
|
|
12
|
+
/** The trigger's brand scope, re-stamped on the portalled body. */
|
|
13
|
+
inheritedBrand: string | undefined;
|
|
10
14
|
getReferenceProps: (props?: React.HTMLProps<HTMLElement>) => Record<string, unknown>;
|
|
11
15
|
getFloatingProps: (props?: React.HTMLProps<HTMLElement>) => Record<string, unknown>;
|
|
12
16
|
};
|
|
@@ -10,7 +10,7 @@ import { MessageTitle } from "./components/MessageTitle";
|
|
|
10
10
|
* Geometry only, plus the three `--message-*` component tokens.
|
|
11
11
|
*
|
|
12
12
|
* Every colour reads a `--message-*` component token whose value is decided by
|
|
13
|
-
* src/
|
|
13
|
+
* src/components/Message/message.recipe.css. That file is the mapping from `intent` to a
|
|
14
14
|
* semantic token — the layer that used to be the `intent` entry in `variants`
|
|
15
15
|
* here, compiled into class-name literals and unreachable from outside.
|
|
16
16
|
* `size` stays here: it is pure geometry (padding, gap, font-size), not
|
|
@@ -48,6 +48,8 @@ export type MessageIntent = Loose<MessageIntentName>;
|
|
|
48
48
|
export type MessageProps = React.HTMLAttributes<HTMLDivElement> &
|
|
49
49
|
VariantProps<typeof messageVariants> & {
|
|
50
50
|
intent?: MessageIntent | undefined;
|
|
51
|
+
/** Name of a brand the theme defines, rendered as `data-brand`. Omitted, the message follows the nearest `data-brand` ancestor. */
|
|
52
|
+
brand?: string | undefined;
|
|
51
53
|
/** Simple API: Optional title displayed above children */
|
|
52
54
|
title?: React.ReactNode;
|
|
53
55
|
/** Simple API: Optional icon displayed on the left */
|
|
@@ -99,27 +101,29 @@ function intentRole(intent: MessageIntent): "status" | "alert" {
|
|
|
99
101
|
* simple API can't express. Message detects which one is in use by
|
|
100
102
|
* inspecting its children, so the two aren't meant to be mixed.
|
|
101
103
|
*/
|
|
102
|
-
export const Message = React.forwardRef<HTMLDivElement, MessageProps>(
|
|
103
|
-
|
|
104
|
-
(
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
104
|
+
export const Message = React.forwardRef<HTMLDivElement, MessageProps>(
|
|
105
|
+
({ className, intent = "neutral", brand, size, title, icon, children, ...props }, ref) => {
|
|
106
|
+
const hasCompoundChildren = React.Children.toArray(children).some(
|
|
107
|
+
(child) =>
|
|
108
|
+
React.isValidElement(child) &&
|
|
109
|
+
(child.type === MessageIcon || child.type === MessageBody || child.type === MessageTitle || child.type === MessageDescription)
|
|
110
|
+
);
|
|
108
111
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
112
|
+
return (
|
|
113
|
+
<div ref={ref} role={intentRole(intent)} data-intent={intent} data-brand={brand} className={cn(messageVariants({ size, className }))} {...props}>
|
|
114
|
+
{hasCompoundChildren ? (
|
|
115
|
+
children
|
|
116
|
+
) : (
|
|
117
|
+
<>
|
|
118
|
+
{icon && <MessageIcon>{icon}</MessageIcon>}
|
|
119
|
+
<MessageBody>
|
|
120
|
+
{title && <MessageTitle>{title}</MessageTitle>}
|
|
121
|
+
<MessageDescription>{children}</MessageDescription>
|
|
122
|
+
</MessageBody>
|
|
123
|
+
</>
|
|
124
|
+
)}
|
|
125
|
+
</div>
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
);
|
|
125
129
|
Message.displayName = "Message";
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { FloatingFocusManager, FloatingOverlay, FloatingPortal, useDismiss, useFloating, useInteractions, useRole } from "@floating-ui/react";
|
|
2
4
|
import { useMergedFloatingRef, usePresence } from "@sixthshift/design-system/hooks";
|
|
3
5
|
import { cn } from "@sixthshift/design-system/utils";
|
|
4
6
|
import { forwardRef, type HTMLAttributes, type ReactNode, useCallback, useContext, useEffect, useId, useMemo, useState } from "react";
|
|
7
|
+
import { useOpenerBrand } from "../../internal/brandScope";
|
|
8
|
+
import { hasOpenEscapeLayer, useEscapeDialog } from "../../internal/escapeLayers";
|
|
5
9
|
import { ModalBody, ModalContext, ModalFooter, ModalHeader } from "./components";
|
|
6
10
|
|
|
7
11
|
// =============================================================================
|
|
@@ -21,15 +25,23 @@ export type ModalProps = Pick<HTMLAttributes<HTMLDivElement>, "className" | "sty
|
|
|
21
25
|
/** Children - use Modal.Header, Modal.Body, Modal.Footer */
|
|
22
26
|
children: ReactNode;
|
|
23
27
|
/**
|
|
24
|
-
* When true (default), clicking outside
|
|
28
|
+
* When true (default), clicking outside — and, for a directly-mounted
|
|
29
|
+
* modal, pressing Escape — closes the modal.
|
|
25
30
|
* When false, the modal can only be closed programmatically.
|
|
26
|
-
* Note:
|
|
31
|
+
* Note: for modals opened via `useModal()`, Escape is handled by
|
|
32
|
+
* OverlayContext for proper stack ordering.
|
|
27
33
|
*/
|
|
28
34
|
dismissable?: boolean;
|
|
29
35
|
/** When true, shows a close (X) button in the modal header */
|
|
30
36
|
closable?: boolean;
|
|
31
37
|
/** Vertical alignment on desktop. "center" (default) or "top" (anchored near top, content grows down) */
|
|
32
38
|
align?: "center" | "top";
|
|
39
|
+
/**
|
|
40
|
+
* Name of a brand the theme defines, rendered as `data-brand` on the dialog.
|
|
41
|
+
* Omitted, the modal takes the brand scope of the control that opened it
|
|
42
|
+
* (whatever held focus), since the portal leaves the trigger's scope behind.
|
|
43
|
+
*/
|
|
44
|
+
brand?: string | undefined;
|
|
33
45
|
};
|
|
34
46
|
|
|
35
47
|
// =============================================================================
|
|
@@ -58,6 +70,7 @@ const ModalRoot = forwardRef<HTMLDivElement, ModalProps>(
|
|
|
58
70
|
dismissable = true,
|
|
59
71
|
closable,
|
|
60
72
|
align = "center",
|
|
73
|
+
brand,
|
|
61
74
|
"aria-label": ariaLabel,
|
|
62
75
|
"aria-labelledby": ariaLabelledBy,
|
|
63
76
|
"aria-describedby": ariaDescribedBy,
|
|
@@ -66,6 +79,9 @@ const ModalRoot = forwardRef<HTMLDivElement, ModalProps>(
|
|
|
66
79
|
) => {
|
|
67
80
|
// Prefer explicit prop over context (context is set by useModal's overlay system)
|
|
68
81
|
const contextValue = useContext(ModalContext);
|
|
82
|
+
// A context means useModal's stack owns this modal — and with it, Escape
|
|
83
|
+
// ordering. Without one the modal is mounted directly and handles its own.
|
|
84
|
+
const isStackManaged = contextValue != null;
|
|
69
85
|
// Context provides a parameterless onClose for programmatic modals; adapt to the boolean shape.
|
|
70
86
|
const handleDismiss = useCallback(() => {
|
|
71
87
|
if (onOpenChange) onOpenChange(false);
|
|
@@ -73,6 +89,8 @@ const ModalRoot = forwardRef<HTMLDivElement, ModalProps>(
|
|
|
73
89
|
}, [onOpenChange, contextValue]);
|
|
74
90
|
|
|
75
91
|
const { ref: presenceRef, state, isMounted, show, hide } = usePresence();
|
|
92
|
+
// Mount-driven, so the modal is open for as long as it exists.
|
|
93
|
+
const openerBrand = useOpenerBrand(true);
|
|
76
94
|
|
|
77
95
|
useEffect(() => {
|
|
78
96
|
show();
|
|
@@ -84,8 +102,12 @@ const ModalRoot = forwardRef<HTMLDivElement, ModalProps>(
|
|
|
84
102
|
|
|
85
103
|
const { refs, context } = useFloating({
|
|
86
104
|
open: true,
|
|
87
|
-
onOpenChange: (nextOpen) => {
|
|
88
|
-
if (
|
|
105
|
+
onOpenChange: (nextOpen, _event, reason) => {
|
|
106
|
+
if (nextOpen) return;
|
|
107
|
+
// A transient overlay (Select dropdown, Popover, picker) open above
|
|
108
|
+
// this modal consumes the Escape itself — don't also close the modal.
|
|
109
|
+
if (reason === "escape-key" && hasOpenEscapeLayer()) return;
|
|
110
|
+
handleClose();
|
|
89
111
|
},
|
|
90
112
|
});
|
|
91
113
|
|
|
@@ -93,8 +115,10 @@ const ModalRoot = forwardRef<HTMLDivElement, ModalProps>(
|
|
|
93
115
|
|
|
94
116
|
const dismiss = useDismiss(context, {
|
|
95
117
|
enabled: dismissable,
|
|
96
|
-
//
|
|
97
|
-
|
|
118
|
+
// Stack-managed modals leave Escape to OverlayContext, which closes
|
|
119
|
+
// topmost-first. A directly-mounted modal has no stack above it and
|
|
120
|
+
// handles its own Escape here.
|
|
121
|
+
escapeKey: !isStackManaged,
|
|
98
122
|
outsidePress: true,
|
|
99
123
|
outsidePressEvent: "mousedown",
|
|
100
124
|
});
|
|
@@ -119,52 +143,64 @@ const ModalRoot = forwardRef<HTMLDivElement, ModalProps>(
|
|
|
119
143
|
|
|
120
144
|
const modalContextValue = useMemo(() => ({ onClose: handleClose, closable, titleId, registerTitle }), [closable, handleClose, titleId, registerTitle]);
|
|
121
145
|
|
|
146
|
+
// While mounted (exit animation included), a non-modal Sheet underneath
|
|
147
|
+
// must leave Escape to this dialog.
|
|
148
|
+
useEscapeDialog(isMounted);
|
|
149
|
+
|
|
122
150
|
if (!isMounted) return null;
|
|
123
151
|
|
|
124
152
|
const isEntering = state === "entering";
|
|
125
153
|
const isExiting = state === "exiting";
|
|
126
154
|
|
|
127
155
|
return (
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
aria-labelledby={labelledBy}
|
|
139
|
-
aria-describedby={ariaDescribedBy}
|
|
140
|
-
tabIndex={-1}
|
|
141
|
-
data-state={state}
|
|
142
|
-
className={cn(
|
|
143
|
-
"modal fixed flex flex-col overflow-hidden outline-hidden",
|
|
144
|
-
"border-(color:--modal-border) rounded-xl border bg-(--modal-bg)",
|
|
145
|
-
// Size
|
|
146
|
-
sizeClasses[size],
|
|
147
|
-
// Cap height so ModalBody scrolls instead of overflowing the viewport
|
|
148
|
-
"max-h-[95%]",
|
|
149
|
-
// Mobile: slide up from bottom, full width
|
|
150
|
-
"max-sm:inset-x-0 max-sm:bottom-0 max-sm:rounded-b-none",
|
|
151
|
-
isEntering && "max-sm:animate-slide-up-in",
|
|
152
|
-
isExiting && "max-sm:animate-slide-up-out",
|
|
153
|
-
// Desktop: horizontal center + vertical alignment
|
|
154
|
-
"sm:left-1/2 sm:-translate-x-1/2",
|
|
155
|
-
align === "center" && "sm:top-1/2 sm:-translate-y-1/2",
|
|
156
|
-
align === "top" && "sm:top-[15vh]",
|
|
157
|
-
isEntering && "sm:animate-fade-in",
|
|
158
|
-
isExiting && "sm:animate-fade-out",
|
|
159
|
-
className
|
|
160
|
-
)}
|
|
161
|
-
style={style}
|
|
162
|
-
{...getFloatingProps({})}
|
|
156
|
+
// Self-portalled: `position: fixed` resolves against the nearest
|
|
157
|
+
// transformed/filtered ancestor, so a modal rendered in place inside a
|
|
158
|
+
// `transform` container would be positioned (and clipped) by it. Nested
|
|
159
|
+
// FloatingPortals attach to the parent portal's node, so a stack-managed
|
|
160
|
+
// modal stays inside the `OverlayProvider`'s custom `modal` root.
|
|
161
|
+
<FloatingPortal>
|
|
162
|
+
<FloatingFocusManager context={context} modal>
|
|
163
|
+
<FloatingOverlay
|
|
164
|
+
lockScroll
|
|
165
|
+
className={cn("modal-overlay fixed inset-0 z-modal bg-(--modal-overlay-bg)", isEntering && "animate-fade-in", isExiting && "animate-fade-out")}
|
|
163
166
|
>
|
|
164
|
-
<
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
167
|
+
<div
|
|
168
|
+
ref={mergedRef}
|
|
169
|
+
role="dialog"
|
|
170
|
+
aria-modal="true"
|
|
171
|
+
aria-label={ariaLabel}
|
|
172
|
+
aria-labelledby={labelledBy}
|
|
173
|
+
aria-describedby={ariaDescribedBy}
|
|
174
|
+
tabIndex={-1}
|
|
175
|
+
data-state={state}
|
|
176
|
+
data-brand={brand ?? openerBrand}
|
|
177
|
+
className={cn(
|
|
178
|
+
"modal fixed flex flex-col overflow-hidden outline-hidden",
|
|
179
|
+
"border-(color:--modal-border) rounded-xl border bg-(--modal-bg)",
|
|
180
|
+
// Size
|
|
181
|
+
sizeClasses[size],
|
|
182
|
+
// Cap height so ModalBody scrolls instead of overflowing the viewport
|
|
183
|
+
"max-h-[95%]",
|
|
184
|
+
// Mobile: slide up from bottom, full width
|
|
185
|
+
"max-sm:inset-x-0 max-sm:bottom-0 max-sm:rounded-b-none",
|
|
186
|
+
isEntering && "max-sm:animate-slide-up-in",
|
|
187
|
+
isExiting && "max-sm:animate-slide-up-out",
|
|
188
|
+
// Desktop: horizontal center + vertical alignment
|
|
189
|
+
"sm:left-1/2 sm:-translate-x-1/2",
|
|
190
|
+
align === "center" && "sm:top-1/2 sm:-translate-y-1/2",
|
|
191
|
+
align === "top" && "sm:top-[15vh]",
|
|
192
|
+
isEntering && "sm:animate-fade-in",
|
|
193
|
+
isExiting && "sm:animate-fade-out",
|
|
194
|
+
className
|
|
195
|
+
)}
|
|
196
|
+
style={style}
|
|
197
|
+
{...getFloatingProps({})}
|
|
198
|
+
>
|
|
199
|
+
<ModalContext.Provider value={modalContextValue}>{children}</ModalContext.Provider>
|
|
200
|
+
</div>
|
|
201
|
+
</FloatingOverlay>
|
|
202
|
+
</FloatingFocusManager>
|
|
203
|
+
</FloatingPortal>
|
|
168
204
|
);
|
|
169
205
|
}
|
|
170
206
|
);
|
|
@@ -192,11 +228,12 @@ ModalRoot.displayName = "Modal";
|
|
|
192
228
|
* Modal is mount-driven — a parent (or `useModal`'s stack) decides whether it
|
|
193
229
|
* renders at all, so there's no `open` prop. `onOpenChange` still exists,
|
|
194
230
|
* firing only with `false` on dismissal, so every overlay in this library
|
|
195
|
-
* shares one open/close shape. Escape
|
|
196
|
-
*
|
|
197
|
-
*
|
|
198
|
-
* `
|
|
199
|
-
*
|
|
231
|
+
* shares one open/close shape. Escape closes the modal: a directly-mounted
|
|
232
|
+
* Modal handles it itself (via `useDismiss`, so `dismissable={false}` turns
|
|
233
|
+
* it off along with outside-press), while one opened through `useModal()`
|
|
234
|
+
* defers to `OverlayProvider`, which closes a stack of modals topmost-first.
|
|
235
|
+
* Either way, an Escape pressed while a transient overlay (Select dropdown,
|
|
236
|
+
* Popover, picker) is open above the modal closes only that overlay.
|
|
200
237
|
*
|
|
201
238
|
* Focus moves into the modal on mount and is trapped there
|
|
202
239
|
* (`FloatingFocusManager modal`) until it unmounts, then returns to whatever
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import { autoUpdate, flip, offset, type Placement, shift, useClick, useDismiss, useFloating, useInteractions } from "@floating-ui/react";
|
|
2
4
|
import { useControllableState } from "@sixthshift/design-system/hooks";
|
|
3
5
|
import type * as React from "react";
|
|
4
6
|
import { useId } from "react";
|
|
7
|
+
import { closestBrand } from "../../internal/brandScope";
|
|
8
|
+
import { useEscapeLayer } from "../../internal/escapeLayers";
|
|
5
9
|
import { PopoverBody } from "./components/PopoverBody";
|
|
6
10
|
import { PopoverClose } from "./components/PopoverClose";
|
|
7
11
|
import { PopoverContext } from "./components/PopoverContext";
|
|
@@ -42,6 +46,10 @@ const PopoverRoot = ({ open: controlledOpen, onOpenChange, defaultOpen = false,
|
|
|
42
46
|
const dismiss = useDismiss(context);
|
|
43
47
|
const { getReferenceProps, getFloatingProps } = useInteractions([click, dismiss]);
|
|
44
48
|
|
|
49
|
+
// While open, an Escape press dismisses this popover and must not also
|
|
50
|
+
// close a Modal/Sheet underneath it.
|
|
51
|
+
useEscapeLayer(open);
|
|
52
|
+
|
|
45
53
|
const id = useId();
|
|
46
54
|
const contentId = `popover-content-${id}`;
|
|
47
55
|
// `Popover.Body` is a `role="dialog"`, and a dialog with no accessible name is
|
|
@@ -59,6 +67,8 @@ const PopoverRoot = ({ open: controlledOpen, onOpenChange, defaultOpen = false,
|
|
|
59
67
|
setFloating: refs.setFloating,
|
|
60
68
|
},
|
|
61
69
|
floatingStyles,
|
|
70
|
+
// Read at render: `domReference` is state, so this updates once the trigger mounts.
|
|
71
|
+
inheritedBrand: closestBrand(context.elements.domReference),
|
|
62
72
|
getReferenceProps,
|
|
63
73
|
getFloatingProps,
|
|
64
74
|
contentId,
|
|
@@ -1,12 +1,21 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import { FloatingPortal } from "@floating-ui/react";
|
|
2
4
|
import { cn } from "@sixthshift/design-system/utils";
|
|
3
5
|
import * as React from "react";
|
|
4
6
|
import { usePopoverContext } from "./PopoverContext";
|
|
5
7
|
|
|
6
|
-
export type PopoverBodyProps = React.HTMLAttributes<HTMLDivElement
|
|
8
|
+
export type PopoverBodyProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
9
|
+
/**
|
|
10
|
+
* Name of a brand the theme defines, rendered as `data-brand`. Omitted, the
|
|
11
|
+
* body copies the brand scope of its trigger, which the portal would
|
|
12
|
+
* otherwise leave behind.
|
|
13
|
+
*/
|
|
14
|
+
brand?: string | undefined;
|
|
15
|
+
};
|
|
7
16
|
|
|
8
|
-
export const PopoverBody = React.forwardRef<HTMLDivElement, PopoverBodyProps>(({ className, children, ...props }, forwardedRef) => {
|
|
9
|
-
const { open, refs, floatingStyles, getFloatingProps, contentId, triggerId } = usePopoverContext();
|
|
17
|
+
export const PopoverBody = React.forwardRef<HTMLDivElement, PopoverBodyProps>(({ className, brand, children, ...props }, forwardedRef) => {
|
|
18
|
+
const { open, refs, floatingStyles, inheritedBrand, getFloatingProps, contentId, triggerId } = usePopoverContext();
|
|
10
19
|
|
|
11
20
|
// `aria-labelledby` outranks `aria-label` in the name computation, so the
|
|
12
21
|
// trigger only names the dialog when the caller hasn't named it themselves.
|
|
@@ -26,6 +35,7 @@ export const PopoverBody = React.forwardRef<HTMLDivElement, PopoverBodyProps>(({
|
|
|
26
35
|
role="dialog"
|
|
27
36
|
aria-labelledby={hasOwnLabel ? undefined : triggerId}
|
|
28
37
|
style={floatingStyles}
|
|
38
|
+
data-brand={brand ?? inheritedBrand}
|
|
29
39
|
className={cn("z-popover rounded-lg border border-border-normal bg-bg-normal p-4 shadow-lg", className)}
|
|
30
40
|
{...getFloatingProps(props)}
|
|
31
41
|
>
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import { createContext, useContext } from "react";
|
|
2
4
|
|
|
3
5
|
export type PopoverContextValue = {
|
|
@@ -8,6 +10,8 @@ export type PopoverContextValue = {
|
|
|
8
10
|
setFloating: (node: HTMLElement | null) => void;
|
|
9
11
|
};
|
|
10
12
|
floatingStyles: React.CSSProperties;
|
|
13
|
+
/** The trigger's brand scope, re-stamped on the portalled body. */
|
|
14
|
+
inheritedBrand: string | undefined;
|
|
11
15
|
getReferenceProps: (props?: React.HTMLProps<HTMLElement>) => Record<string, unknown>;
|
|
12
16
|
getFloatingProps: (props?: React.HTMLProps<HTMLElement>) => Record<string, unknown>;
|
|
13
17
|
contentId: string;
|