frappe-ui 1.0.0-beta.2 → 1.0.0-beta.21
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/experimental/FloatingWindow/FloatingWindow.cy.ts +328 -0
- package/experimental/FloatingWindow/FloatingWindow.vue +303 -0
- package/experimental/FloatingWindow/index.ts +8 -0
- package/experimental/FloatingWindow/types.ts +31 -0
- package/experimental/FloatingWindow/useFloatingWindow.ts +236 -0
- package/experimental/MultiEmailInput/MultiEmailInput.cy.ts +155 -0
- package/experimental/MultiEmailInput/MultiEmailInput.vue +472 -0
- package/experimental/MultiEmailInput/index.ts +17 -0
- package/experimental/MultiEmailInput/stories/AsyncSuggestions.vue +54 -0
- package/experimental/MultiEmailInput/stories/Basic.vue +18 -0
- package/experimental/MultiEmailInput/stories/CustomChip.vue +39 -0
- package/experimental/MultiEmailInput/stories/Labeling.vue +28 -0
- package/experimental/MultiEmailInput/types.ts +138 -0
- package/experimental/MultiEmailInput/utils.ts +60 -0
- package/experimental.ts +12 -0
- package/frappe/Billing/SignupBanner.vue +1 -1
- package/frappe/Billing/TrialBanner.vue +1 -1
- package/frappe/DataImport/DataImport.vue +1 -1
- package/frappe/DataImport/DataImportList.vue +1 -1
- package/frappe/DataImport/ImportSteps.vue +6 -6
- package/frappe/DataImport/MappingStep.vue +1 -1
- package/frappe/DataImport/PreviewStep.vue +9 -9
- package/frappe/DataImport/TemplateModal.vue +1 -1
- package/frappe/DataImport/UploadStep.vue +5 -5
- package/frappe/Filter/Filter.vue +1 -1
- package/frappe/Help/HelpModal.vue +1 -1
- package/frappe/Link/stories/Suffix.vue +2 -2
- package/frappe/Onboarding/GettingStartedBanner.vue +1 -1
- package/frappe/drive/components/AppSwitcher.vue +1 -1
- package/frappe/drive/components/InfoDialog.vue +3 -3
- package/frappe/drive/components/MoveDialog.vue +2 -2
- package/frappe/drive/components/Select/Select.vue +2 -2
- package/frappe/drive/components/ShareDialog.vue +1 -1
- package/frappe/telemetry/index.ts +88 -14
- package/frappe/telemetry/pulse.ts +78 -164
- package/frappe/telemetry/telemetry.test.ts +13 -79
- package/icons/IconPicker.vue +1 -1
- package/package.json +104 -34
- package/src/components/Alert/Alert.md +4 -0
- package/src/components/Alert/Alert.vue +4 -4
- package/src/components/Autocomplete/Autocomplete.vue +6 -6
- package/src/components/Avatar/Avatar.md +4 -0
- package/src/components/Avatar/Avatar.vue +23 -4
- package/src/components/Badge/Badge.api.md +1 -1
- package/src/components/Badge/Badge.cy.ts +34 -16
- package/src/components/Badge/Badge.md +20 -4
- package/src/components/Badge/Badge.vue +60 -45
- package/src/components/Badge/stories/CallMeta.vue +35 -0
- package/src/components/Badge/stories/EventStatus.vue +16 -0
- package/src/components/Badge/stories/ListStatus.vue +91 -0
- package/src/components/Badge/stories/Reactions.vue +33 -0
- package/src/components/Badge/stories/RowTags.vue +39 -0
- package/src/components/Badge/types.ts +1 -2
- package/src/components/BottomSheet/BottomSheet.api.md +57 -0
- package/src/components/BottomSheet/BottomSheet.vue +232 -0
- package/src/components/BottomSheet/index.ts +6 -0
- package/src/components/BottomSheet/types.ts +23 -0
- package/src/components/Breadcrumbs/Breadcrumbs.md +4 -0
- package/src/components/Breadcrumbs/Breadcrumbs.vue +3 -3
- package/src/components/Button/Button.api.md +37 -32
- package/src/components/Button/Button.cy.ts +18 -2
- package/src/components/Button/Button.md +20 -8
- package/src/components/Button/Button.vue +304 -284
- package/src/components/Button/stories/InlineActions.vue +10 -0
- package/src/components/Button/stories/LiveClassCard.vue +34 -0
- package/src/components/Button/stories/SectionAction.vue +17 -0
- package/src/components/Button/stories/SectionControls.vue +28 -0
- package/src/components/Button/stories/SelectionToolbar.vue +20 -0
- package/src/components/Button/stories/StackedActions.vue +10 -0
- package/src/components/Button/types.ts +38 -32
- package/src/components/Calendar/Calendar.api.md +16 -16
- package/src/components/Calendar/Calendar.vue +100 -73
- package/src/components/Calendar/CalendarDaily.vue +21 -20
- package/src/components/Calendar/CalendarMonthEvent.vue +30 -9
- package/src/components/Calendar/CalendarMonthly.vue +60 -39
- package/src/components/Calendar/CalendarTimeMarker.vue +9 -8
- package/src/components/Calendar/CalendarWeekDayEvent.vue +98 -42
- package/src/components/Calendar/CalendarWeekly.vue +77 -41
- package/src/components/Calendar/EventModalContent.vue +32 -16
- package/src/components/Calendar/NewEventModal.vue +19 -16
- package/src/components/Calendar/ShowMoreCalendarEvent.vue +11 -17
- package/src/components/Calendar/{calendarUtils.js → calendarUtils.ts} +76 -52
- package/src/components/Calendar/composables/{useCalendarData.js → useCalendarData.ts} +23 -11
- package/src/components/Calendar/composables/{useEventModal.js → useEventModal.ts} +16 -12
- package/src/components/Calendar/index.ts +12 -2
- package/src/components/Calendar/stories/CustomHeader.vue +1 -1
- package/src/components/Calendar/types.ts +106 -0
- package/src/components/Calendar/{useEventBase.js → useEventBase.ts} +40 -26
- package/src/components/Card.vue +2 -2
- package/src/components/Charts/ECharts.vue +1 -1
- package/src/components/Charts/NumberChart.vue +7 -7
- package/src/components/Charts/eChartOptions.ts +28 -13
- package/src/components/Checkbox/Checkbox.api.md +6 -0
- package/src/components/Checkbox/Checkbox.cy.ts +10 -2
- package/src/components/Checkbox/Checkbox.md +28 -0
- package/src/components/Checkbox/Checkbox.vue +9 -22
- package/src/components/Checkbox/stories/Group.vue +27 -0
- package/src/components/Checkbox/stories/HorizontalGroup.vue +24 -0
- package/src/components/Checkbox/stories/SettingRow.vue +23 -0
- package/src/components/Checkbox/stories/WithDescription.vue +27 -0
- package/src/components/Checkbox/types.ts +7 -0
- package/src/components/CodeEditor/CodeEditor.api.md +139 -0
- package/src/components/CodeEditor/CodeEditor.cy.ts +160 -0
- package/src/components/CodeEditor/CodeEditor.md +104 -0
- package/src/components/CodeEditor/CodeEditor.vue +556 -0
- package/src/components/CodeEditor/CodePreview.test.ts +49 -0
- package/src/components/CodeEditor/CodePreview.vue +45 -0
- package/src/components/CodeEditor/index.ts +17 -0
- package/src/components/CodeEditor/languages.ts +63 -0
- package/src/components/CodeEditor/stories/Default.vue +16 -0
- package/src/components/CodeEditor/stories/Labeling.vue +25 -0
- package/src/components/CodeEditor/stories/MaxHeight.vue +24 -0
- package/src/components/CodeEditor/stories/Preview.vue +36 -0
- package/src/components/CodeEditor/stories/Sizes.vue +15 -0
- package/src/components/CodeEditor/stories/Variants.vue +13 -0
- package/src/components/CodeEditor/theme.ts +272 -0
- package/src/components/CodeEditor/types.ts +69 -0
- package/src/components/Combobox/Combobox.api.md +19 -20
- package/src/components/Combobox/Combobox.cy.ts +54 -14
- package/src/components/Combobox/Combobox.md +4 -0
- package/src/components/Combobox/Combobox.vue +233 -247
- package/src/components/Combobox/ComboboxResults.vue +1 -1
- package/src/components/Combobox/stories/Clearable.vue +2 -2
- package/src/components/Combobox/stories/CreateNew.vue +1 -1
- package/src/components/Combobox/stories/Footer.vue +10 -2
- package/src/components/Combobox/stories/InDialog.vue +1 -1
- package/src/components/Combobox/types.ts +8 -29
- package/src/components/CommandPalette/CommandPaletteItem.vue +1 -1
- package/src/components/ContextMenu/ContextMenu.api.md +52 -0
- package/src/components/ContextMenu/ContextMenu.cy.ts +191 -0
- package/src/components/ContextMenu/ContextMenu.md +25 -0
- package/src/components/ContextMenu/ContextMenu.vue +106 -0
- package/src/components/ContextMenu/index.ts +2 -0
- package/src/components/ContextMenu/stories/FileList.vue +105 -0
- package/src/components/ContextMenu/stories/Groups.vue +95 -0
- package/src/components/ContextMenu/stories/Simple.vue +72 -0
- package/src/components/ContextMenu/types.ts +39 -0
- package/src/components/DatePicker/CalendarPanel.vue +128 -86
- package/src/components/DatePicker/DatePicker.vue +3 -9
- package/src/components/DatePicker/DateRangePicker.vue +4 -8
- package/src/components/DatePicker/DateTimePicker.cy.ts +4 -2
- package/src/components/DatePicker/DateTimePicker.vue +3 -9
- package/src/components/DatePicker/composables.ts +15 -37
- package/src/components/DatePicker/stories/Range.vue +1 -1
- package/src/components/DatePicker/types.ts +1 -1
- package/src/components/DesktopShell/DesktopShell.api.md +40 -0
- package/src/components/DesktopShell/DesktopShell.cy.ts +39 -0
- package/src/components/DesktopShell/DesktopShell.md +31 -0
- package/src/components/DesktopShell/DesktopShell.vue +81 -0
- package/src/components/DesktopShell/index.ts +3 -0
- package/src/components/DesktopShell/stories/Default.vue +97 -0
- package/src/components/DesktopShell/types.ts +2 -0
- package/src/components/Dialog/Dialog.api.md +7 -8
- package/src/components/Dialog/Dialog.md +4 -0
- package/src/components/Dialog/Dialog.vue +8 -7
- package/src/components/Dialog/stories/CommandPalette.vue +1 -1
- package/src/components/Dialog/stories/Wizard.vue +1 -1
- package/src/components/Dialogs.vue +11 -9
- package/src/components/Divider/Divider.api.md +1 -1
- package/src/components/Divider/Divider.md +4 -0
- package/src/components/Dropdown/Dropdown.api.md +10 -35
- package/src/components/Dropdown/Dropdown.md +10 -6
- package/src/components/Dropdown/Dropdown.vue +33 -47
- package/src/components/Dropdown/stories/{05_KebabMenu.vue → KebabMenu.vue} +3 -1
- package/src/components/Dropdown/stories/{06_UserMenu.vue → UserMenu.vue} +1 -1
- package/src/components/Dropdown/types.ts +25 -189
- package/src/components/Duration/Duration.api.md +92 -0
- package/src/components/Duration/Duration.cy.ts +183 -0
- package/src/components/Duration/Duration.md +98 -0
- package/src/components/Duration/Duration.vue +114 -0
- package/src/components/Duration/duration.ts +185 -0
- package/src/components/Duration/index.ts +8 -0
- package/src/components/Duration/stories/Default.vue +10 -0
- package/src/components/Duration/stories/Formats.vue +16 -0
- package/src/components/Duration/stories/RealWorld.vue +50 -0
- package/src/components/Duration/stories/Sizes.vue +15 -0
- package/src/components/Duration/stories/States.vue +21 -0
- package/src/components/Duration/types.ts +52 -0
- package/src/components/ErrorMessage/ErrorMessage.cy.ts +1 -1
- package/src/components/ErrorMessage/ErrorMessage.md +4 -0
- package/src/components/ErrorMessage/ErrorMessage.vue +1 -1
- package/src/components/FileUploader/FileUploader.api.md +36 -11
- package/src/components/FileUploader/FileUploader.cy.ts +66 -0
- package/src/components/FileUploader/FileUploader.md +4 -1
- package/src/components/FileUploader/FileUploader.vue +147 -106
- package/src/components/FileUploader/index.ts +6 -0
- package/src/components/FileUploader/types.ts +43 -0
- package/src/components/FormControl/FormControl.api.md +3 -4
- package/src/components/FormControl/FormControl.md +4 -0
- package/src/components/FormControl/stories/RealForm.vue +3 -3
- package/src/components/FormLabel.vue +5 -8
- package/src/components/HoverCard/HoverCard.api.md +100 -0
- package/src/components/HoverCard/HoverCard.cy.ts +103 -0
- package/src/components/HoverCard/HoverCard.md +52 -0
- package/src/components/HoverCard/HoverCard.vue +91 -0
- package/src/components/HoverCard/index.ts +2 -0
- package/src/components/HoverCard/stories/Arrow.vue +28 -0
- package/src/components/HoverCard/stories/Delays.vue +16 -0
- package/src/components/HoverCard/stories/Example.vue +28 -0
- package/src/components/HoverCard/types.ts +47 -0
- package/src/components/Icon/Icon.cy.ts +78 -0
- package/src/components/Icon/Icon.vue +50 -0
- package/src/components/Icon/index.ts +1 -0
- package/src/components/InputLabeling/InputDescription.vue +1 -1
- package/src/components/InputLabeling/InputError.vue +1 -1
- package/src/components/InputLabeling/InputLabel.vue +21 -7
- package/src/components/InputLabeling/RequiredIndicator.vue +1 -1
- package/src/components/ItemListRow/ItemListRow.vue +1 -1
- package/src/components/ItemListRow/stories/RowStates.vue +1 -1
- package/src/components/KeyboardShortcut.vue +4 -8
- package/src/components/KeyboardShortcutsModal/KeyboardShortcutsModal.api.md +22 -14
- package/src/components/KeyboardShortcutsModal/KeyboardShortcutsModal.vue +2 -2
- package/src/components/ListFilter/ListFilter.api.md +5 -6
- package/src/components/ListFilter/ListFilter.vue +161 -57
- package/src/components/ListFilter/NestedPopover.vue +17 -10
- package/src/components/ListFilter/SearchComplete.vue +31 -38
- package/src/components/ListItem.vue +1 -1
- package/src/components/ListView/ListEmptyState.vue +1 -1
- package/src/components/ListView/ListGroupHeader.vue +2 -2
- package/src/components/ListView/ListRow.vue +1 -1
- package/src/components/ListView/ListSelectBanner.vue +1 -1
- package/src/components/ListView/ListView.api.md +322 -6
- package/src/components/ListView/index.ts +25 -0
- package/src/components/ListView/stories/CustomList.vue +1 -1
- package/src/components/ListView/stories/GroupedRows.vue +1 -1
- package/src/components/LoadingIndicator.vue +13 -22
- package/src/components/LoadingText.vue +3 -13
- package/src/components/{Dropdown/DropdownMenuList.vue → Menu/Menu.vue} +110 -75
- package/src/components/{Dropdown/DropdownMenuItemContent.vue → Menu/MenuItemContent.vue} +23 -33
- package/src/components/{Dropdown/DropdownRenderContent.vue → Menu/MenuRenderContent.vue} +1 -1
- package/src/components/Menu/types.ts +225 -0
- package/src/components/{Dropdown → Menu}/utils.ts +36 -40
- package/src/components/MobileNav/MobileNav.api.md +70 -0
- package/src/components/MobileNav/MobileNav.cy.ts +89 -0
- package/src/components/MobileNav/MobileNav.md +34 -0
- package/src/components/MobileNav/MobileNav.vue +15 -0
- package/src/components/MobileNav/MobileNavItem.vue +125 -0
- package/src/components/MobileNav/index.ts +4 -0
- package/src/components/MobileNav/stories/Default.vue +40 -0
- package/src/components/MobileNav/types.ts +33 -0
- package/src/components/MobileShell/MobileShell.api.md +23 -0
- package/src/components/MobileShell/MobileShell.cy.ts +25 -0
- package/src/components/MobileShell/MobileShell.md +24 -0
- package/src/components/MobileShell/MobileShell.vue +41 -0
- package/src/components/MobileShell/index.ts +3 -0
- package/src/components/MobileShell/stories/Default.vue +75 -0
- package/src/components/MobileShell/types.ts +7 -0
- package/src/components/MonthPicker/MonthPicker.cy.ts +16 -0
- package/src/components/MonthPicker/MonthPicker.vue +19 -8
- package/src/components/MultiSelect/MultiSelect.api.md +3 -4
- package/src/components/MultiSelect/MultiSelect.md +42 -10
- package/src/components/MultiSelect/MultiSelect.vue +199 -227
- package/src/components/MultiSelect/MultiSelectResults.vue +1 -1
- package/src/components/MultiSelect/stories/Members.vue +1 -1
- package/src/components/MultiSelect/stories/TagsTrigger.vue +3 -3
- package/src/components/MultiSelect/types.ts +3 -3
- package/src/components/PageHeader/PageHeader.api.md +112 -0
- package/src/components/PageHeader/PageHeader.cy.ts +68 -0
- package/src/components/PageHeader/PageHeader.md +25 -0
- package/src/components/PageHeader/PageHeader.vue +13 -0
- package/src/components/PageHeader/PageHeaderBackButton.vue +29 -0
- package/src/components/PageHeader/PageHeaderBase.vue +51 -0
- package/src/components/PageHeader/PageHeaderMobile.vue +68 -0
- package/src/components/PageHeader/PageHeaderMobileTitle.vue +16 -0
- package/src/components/PageHeader/PageHeaderTarget.vue +13 -0
- package/src/components/PageHeader/PageHeaderTitle.vue +12 -0
- package/src/components/PageHeader/index.ts +7 -0
- package/src/components/PageHeader/stories/Default.vue +19 -0
- package/src/components/PageHeader/stories/Mobile.vue +24 -0
- package/src/components/PageHeader/target.ts +19 -0
- package/src/components/PageHeader/types.ts +19 -0
- package/src/components/Password/Password.md +4 -0
- package/src/components/Password/Password.vue +2 -2
- package/src/components/Popover/Popover.api.md +126 -42
- package/src/components/Popover/Popover.cy.ts +425 -41
- package/src/components/Popover/Popover.md +99 -4
- package/src/components/Popover/Popover.vue +288 -215
- package/src/components/Popover/index.ts +8 -1
- package/src/components/Popover/stories/Arrow.vue +16 -0
- package/src/components/Popover/stories/Bare.vue +20 -0
- package/src/components/Popover/stories/Click.vue +7 -4
- package/src/components/Popover/stories/Controlled.vue +22 -0
- package/src/components/Popover/stories/Dismissible.vue +19 -0
- package/src/components/Popover/stories/MatchTriggerWidth.vue +16 -0
- package/src/components/Popover/stories/SideAlign.vue +17 -0
- package/src/components/Popover/types.ts +101 -23
- package/src/components/Progress/Progress.cy.ts +2 -2
- package/src/components/Progress/Progress.md +4 -0
- package/src/components/Progress/Progress.vue +4 -4
- package/src/components/Rail/Rail.api.md +91 -0
- package/src/components/Rail/Rail.cy.ts +84 -0
- package/src/components/Rail/Rail.md +30 -0
- package/src/components/Rail/Rail.vue +16 -0
- package/src/components/Rail/RailItem.vue +134 -0
- package/src/components/Rail/RailItemBadge.vue +64 -0
- package/src/components/Rail/index.ts +2 -0
- package/src/components/Rail/stories/Default.vue +44 -0
- package/src/components/Rail/types.ts +37 -0
- package/src/components/Rating/Rating.md +4 -0
- package/src/components/Rating/Rating.vue +5 -8
- package/src/components/Rating/stories/CustomSlot.vue +2 -2
- package/src/components/ScrollArea/ScrollArea.api.md +59 -0
- package/src/components/ScrollArea/ScrollArea.vue +46 -0
- package/src/components/ScrollArea/ScrollBar.vue +101 -0
- package/src/components/ScrollArea/index.ts +2 -0
- package/src/components/Select/Select.cy.ts +16 -15
- package/src/components/Select/Select.md +4 -0
- package/src/components/Select/Select.vue +217 -203
- package/src/components/Select/stories/Example.vue +4 -3
- package/src/components/Select/stories/States.vue +1 -1
- package/src/components/Select/stories/TriggerSlots.vue +2 -2
- package/src/components/Select/utils.ts +4 -3
- package/src/components/SettingsDialog/SettingsBody.vue +23 -0
- package/src/components/SettingsDialog/SettingsContent.vue +7 -0
- package/src/components/SettingsDialog/SettingsDialog.api.md +273 -0
- package/src/components/SettingsDialog/SettingsDialog.cy.ts +126 -0
- package/src/components/SettingsDialog/SettingsDialog.md +43 -0
- package/src/components/SettingsDialog/SettingsDialog.vue +76 -0
- package/src/components/SettingsDialog/SettingsHeader.vue +36 -0
- package/src/components/SettingsDialog/SettingsNavGroup.vue +20 -0
- package/src/components/SettingsDialog/SettingsNavItem.vue +33 -0
- package/src/components/SettingsDialog/SettingsPanel.cy.ts +77 -0
- package/src/components/SettingsDialog/SettingsPanel.vue +26 -0
- package/src/components/SettingsDialog/SettingsRow.vue +52 -0
- package/src/components/SettingsDialog/SettingsSidebar.vue +16 -0
- package/src/components/SettingsDialog/index.ts +10 -0
- package/src/components/SettingsDialog/stories/Default.vue +99 -0
- package/src/components/SettingsDialog/stories/PanelBasic.vue +52 -0
- package/src/components/SettingsDialog/stories/panels/NotificationsPanel.vue +57 -0
- package/src/components/SettingsDialog/stories/panels/PreferencesPanel.vue +57 -0
- package/src/components/SettingsDialog/stories/panels/ProfilePanel.vue +38 -0
- package/src/components/SettingsDialog/stories/panels/UsersPanel.vue +83 -0
- package/src/components/SettingsDialog/types.ts +21 -0
- package/src/components/Sidebar/Sidebar.api.md +124 -65
- package/src/components/Sidebar/Sidebar.cy.ts +127 -76
- package/src/components/Sidebar/Sidebar.md +47 -3
- package/src/components/Sidebar/Sidebar.vue +64 -56
- package/src/components/Sidebar/SidebarCollapseToggle.vue +23 -0
- package/src/components/Sidebar/SidebarHeader.vue +76 -52
- package/src/components/Sidebar/SidebarItem.vue +159 -95
- package/src/components/Sidebar/SidebarItemIcon.vue +25 -0
- package/src/components/Sidebar/SidebarLabel.vue +34 -0
- package/src/components/Sidebar/SidebarSection.vue +29 -14
- package/src/components/Sidebar/index.ts +13 -2
- package/src/components/Sidebar/stories/Collapsed.vue +40 -0
- package/src/components/Sidebar/stories/Default.vue +153 -0
- package/src/components/Sidebar/stories/Legacy.vue +36 -0
- package/src/components/Sidebar/types.ts +105 -20
- package/src/components/Skeleton/Skeleton.cy.ts +21 -0
- package/src/components/Skeleton/Skeleton.md +21 -0
- package/src/components/Skeleton/Skeleton.vue +6 -0
- package/src/components/Skeleton/index.ts +1 -0
- package/src/components/Skeleton/stories/Card.vue +12 -0
- package/src/components/Skeleton/stories/List.vue +15 -0
- package/src/components/Skeleton/stories/Text.vue +11 -0
- package/src/components/Slider/Slider.api.md +1 -1
- package/src/components/Slider/Slider.cy.ts +29 -0
- package/src/components/Slider/Slider.md +10 -0
- package/src/components/Slider/Slider.vue +37 -9
- package/src/components/Slider/stories/NegativeValues.vue +12 -0
- package/src/components/Slider/types.ts +1 -1
- package/src/components/Spinner/Spinner.api.md +32 -0
- package/src/components/Spinner/Spinner.cy.ts +87 -0
- package/src/components/Spinner/Spinner.md +21 -0
- package/src/components/Spinner/Spinner.vue +168 -0
- package/src/components/Spinner/index.ts +2 -0
- package/src/components/Spinner/stories/InContext.vue +49 -0
- package/src/components/Spinner/stories/Sizes.vue +12 -0
- package/src/components/Spinner/stories/Themes.vue +10 -0
- package/src/components/Spinner/stories/Track.vue +12 -0
- package/src/components/Spinner/types.ts +14 -0
- package/src/components/Switch/Switch.md +4 -0
- package/src/components/Switch/Switch.vue +6 -23
- package/src/components/TabButtons/Pill.vue +161 -0
- package/src/components/TabButtons/TabButtons.api.md +52 -2
- package/src/components/TabButtons/TabButtons.cy.ts +18 -5
- package/src/components/TabButtons/TabButtons.md +49 -0
- package/src/components/TabButtons/TabButtons.vue +207 -109
- package/src/components/TabButtons/index.ts +11 -1
- package/src/components/TabButtons/pillTypes.ts +43 -0
- package/src/components/TabButtons/stories/PrefixSuffix.vue +65 -0
- package/src/components/TabButtons/stories/Sizes.vue +26 -0
- package/src/components/TabButtons/stories/Variants.vue +40 -0
- package/src/components/TabButtons/stories/Vertical.vue +64 -0
- package/src/components/TabButtons/types.ts +49 -0
- package/src/components/Tabs/Tabs.md +4 -0
- package/src/components/Tabs/Tabs.vue +1 -1
- package/src/components/TextEditor/TextEditor.cy.ts +1 -1
- package/src/components/TextEditor/TextEditor.md +3 -0
- package/src/components/TextEditor/TextEditor.vue +7 -4
- package/src/components/TextEditor/components/CodeBlockComponent.vue +4 -194
- package/src/components/TextEditor/components/FontColor.vue +1 -1
- package/src/components/TextEditor/components/LinkPopup.vue +1 -1
- package/src/components/TextEditor/components/MediaNodeView.vue +17 -17
- package/src/components/TextEditor/components/MentionList.vue +1 -1
- package/src/components/TextEditor/components/TextEditorBubbleMenu.vue +14 -5
- package/src/components/TextEditor/components/TextEditorFixedMenu.vue +3 -3
- package/src/components/TextEditor/components/{Menu.vue → TextEditorMenu.vue} +3 -3
- package/src/components/TextEditor/extensions/iframe/InsertIframe.vue +2 -2
- package/src/components/TextEditor/extensions/iframe/iframe-extension.ts +62 -19
- package/src/components/TextEditor/extensions/image-group/ImageGroupNodeView.vue +1 -1
- package/src/components/TextEditor/extensions/image-group/ImageGroupUploadDialog.vue +4 -4
- package/src/components/TextEditor/extensions/suggestion/SuggestionList.vue +1 -1
- package/src/components/TextEditor/extensions/toc-node/TocNodeView.vue +1 -1
- package/src/components/TextEditor/hljs-github.css +197 -0
- package/src/components/TextEditor/index.ts +13 -0
- package/src/components/TextInput/TextInput.cy.ts +8 -3
- package/src/components/TextInput/TextInput.md +4 -0
- package/src/components/TextInput/TextInput.vue +11 -15
- package/src/components/TextInput/stories/Slots.vue +1 -1
- package/src/components/Textarea/Textarea.cy.ts +1 -1
- package/src/components/Textarea/Textarea.md +4 -0
- package/src/components/Textarea/Textarea.vue +11 -15
- package/src/components/ThemeSwitcher/ThemePreview.vue +74 -0
- package/src/components/ThemeSwitcher/ThemeSwitcher.api.md +82 -0
- package/src/components/ThemeSwitcher/ThemeSwitcher.cy.ts +101 -0
- package/src/components/ThemeSwitcher/ThemeSwitcher.md +70 -0
- package/src/components/ThemeSwitcher/ThemeSwitcher.vue +145 -0
- package/src/components/ThemeSwitcher/index.ts +2 -0
- package/src/components/ThemeSwitcher/previews/PreviewWindow.vue +74 -0
- package/src/components/ThemeSwitcher/stories/Branded.vue +12 -0
- package/src/components/ThemeSwitcher/stories/Default.vue +7 -0
- package/src/components/ThemeSwitcher/stories/Menu.vue +53 -0
- package/src/components/ThemeSwitcher/stories/Toggle.vue +25 -0
- package/src/components/ThemeSwitcher/types.ts +35 -0
- package/src/components/TimePicker/TimePicker.api.md +67 -15
- package/src/components/TimePicker/TimePicker.cy.ts +130 -14
- package/src/components/TimePicker/TimePicker.md +6 -0
- package/src/components/TimePicker/TimePicker.vue +74 -19
- package/src/components/TimePicker/stories/Labeling.vue +21 -0
- package/src/components/TimePicker/stories/Range.vue +1 -1
- package/src/components/TimePicker/stories/SizesAndVariants.vue +23 -0
- package/src/components/TimePicker/stories/TwentyFour.vue +1 -1
- package/src/components/TimePicker/types.ts +15 -3
- package/src/components/TimePicker/utils.ts +82 -21
- package/src/components/Toast/Toast.md +1 -1
- package/src/components/Toast/Toast.sanitize.test.ts +50 -0
- package/src/components/Toast/Toast.test.ts +5 -0
- package/src/components/Toast/Toast.vue +7 -7
- package/src/components/Toast/ToastProvider.vue +18 -9
- package/src/components/Toast/stories/CustomIcon.vue +1 -1
- package/src/components/Toast/toast.ts +32 -8
- package/src/components/Tooltip/Tooltip.api.md +43 -5
- package/src/components/Tooltip/Tooltip.cy.ts +48 -0
- package/src/components/Tooltip/Tooltip.md +10 -0
- package/src/components/Tooltip/Tooltip.vue +20 -5
- package/src/components/Tooltip/TooltipBubble.vue +2 -2
- package/src/components/Tooltip/TooltipProvider.vue +53 -0
- package/src/components/Tooltip/index.ts +1 -0
- package/src/components/Tooltip/stories/Group.vue +22 -0
- package/src/components/Tooltip/stories/Slots.vue +2 -2
- package/src/components/Tooltip/types.ts +1 -1
- package/src/components/Tree/Tree.api.md +76 -18
- package/src/components/Tree/Tree.md +144 -2
- package/src/components/Tree/Tree.vue +276 -97
- package/src/components/Tree/TreeItem.vue +263 -0
- package/src/components/Tree/index.ts +9 -1
- package/src/components/Tree/stories/DragDrop.vue +94 -0
- package/src/components/Tree/stories/Example.vue +11 -34
- package/src/components/Tree/stories/ExpandAll.vue +48 -0
- package/src/components/Tree/stories/Guides.vue +49 -0
- package/src/components/Tree/stories/ListView.vue +74 -0
- package/src/components/Tree/tree.cy.ts +218 -90
- package/src/components/Tree/types.ts +114 -24
- package/src/components/Tree/useTreeDragDrop.ts +203 -0
- package/src/components/Tree/useTreeKeyboard.ts +109 -0
- package/src/components/VueGridLayout/Layout.vue +1 -1
- package/src/components/shared/picker/PickerShell.vue +20 -15
- package/src/components/shared/popover/PopoverPanel.vue +67 -0
- package/src/components/shared/popover/popoverPanel.css +102 -0
- package/src/components/shared/selection/utils.ts +6 -6
- package/src/composables/useAutofocusOnOpen.ts +2 -0
- package/src/composables/useScreenSize.ts +46 -0
- package/src/composables/useScrollContainer.ts +108 -0
- package/src/composables/useShortcut.ts +3 -0
- package/src/data-fetching/docStore.test.ts +74 -0
- package/src/data-fetching/docStore.ts +18 -7
- package/src/data-fetching/useCall/types.ts +1 -0
- package/src/data-fetching/useCall/useCall.test.ts +43 -0
- package/src/data-fetching/useCall/useCall.ts +17 -4
- package/src/data-fetching/useDoc/useDoc.test.ts +61 -2
- package/src/data-fetching/useDoc/useDoc.ts +35 -15
- package/src/data-fetching/useDoctype/useDoctype.ts +6 -5
- package/src/data-fetching/useFrappeFetch.ts +17 -4
- package/src/data-fetching/useList/types.ts +1 -0
- package/src/data-fetching/useList/useList.test.ts +40 -0
- package/src/data-fetching/useList/useList.ts +20 -13
- package/src/data-fetching/useNewDoc/useNewDoc.ts +9 -5
- package/src/directives/focus.ts +3 -3
- package/src/index.ts +105 -74
- package/src/mocks/handlers.ts +8 -0
- package/src/molecules/editor/Editor.cy.ts +6 -1
- package/src/molecules/editor/EditorBubbleMenu.vue +1 -1
- package/src/molecules/editor/EditorContent.vue +50 -9
- package/src/molecules/editor/EditorFixedMenu.vue +2 -1
- package/src/molecules/editor/EditorFloatingMenu.vue +1 -1
- package/src/molecules/editor/EditorTableMenu.vue +303 -0
- package/src/molecules/editor/MenuItems.vue +81 -69
- package/src/molecules/editor/commands.ts +10 -2
- package/src/molecules/editor/components/AttachmentNodeView.vue +115 -0
- package/src/molecules/editor/components/EditorDropZone.vue +105 -0
- package/src/molecules/editor/components/EditorPopover.vue +89 -0
- package/src/molecules/editor/components/MediaNodeView.vue +156 -49
- package/src/molecules/editor/components/MediaResizeHandles.vue +38 -0
- package/src/molecules/editor/components/MediaToolbar.vue +108 -54
- package/src/molecules/editor/components/TableContextMenu.vue +144 -0
- package/src/molecules/editor/components/UploadProgressIndicator.vue +33 -0
- package/src/molecules/editor/components/VideoControls.vue +208 -0
- package/src/molecules/editor/components/font-color/ColorSwatchGrid.vue +3 -5
- package/src/molecules/editor/components/font-color/fontColorController.ts +28 -23
- package/src/molecules/editor/components/font-color/swatches.ts +1 -1
- package/src/molecules/editor/components/media-node-view-controller.ts +4 -4
- package/src/molecules/editor/components/media-node-view-utils.test.ts +0 -13
- package/src/molecules/editor/components/media-node-view-utils.ts +0 -10
- package/src/molecules/editor/components/table-color/tableCellColorController.ts +80 -0
- package/src/molecules/editor/components/table-size-picker/TableSizePicker.vue +92 -0
- package/src/molecules/editor/components/table-size-picker/tableSizePickerController.ts +40 -0
- package/src/molecules/editor/composables/useEditorFileDrop.ts +117 -0
- package/src/molecules/editor/composables/useFloatingPopup.ts +46 -2
- package/src/molecules/editor/composables/useNodeViewResize.test.ts +60 -11
- package/src/molecules/editor/composables/useNodeViewResize.ts +80 -21
- package/src/molecules/editor/composables/useTableCellColorState.ts +78 -0
- package/src/molecules/editor/composables/useWindowFileDragging.ts +68 -0
- package/src/molecules/editor/extensions/attachment/attachment-engine.ts +250 -0
- package/src/molecules/editor/extensions/attachment/attachment-extension.ts +248 -0
- package/src/molecules/editor/extensions/attachment/attachment.flow.test.ts +80 -0
- package/src/molecules/editor/extensions/attachment/attachment.roundtrip.test.ts +50 -0
- package/src/molecules/editor/extensions/attachment/index.ts +5 -0
- package/src/molecules/editor/extensions/code-block/CodeBlockComponent.css +16 -26
- package/src/molecules/editor/extensions/code-block/CodeBlockComponent.vue +107 -22
- package/src/molecules/editor/extensions/content-paste/content-paste-extension.ts +5 -3
- package/src/molecules/editor/extensions/iframe/IframeInsertDialog.vue +34 -7
- package/src/molecules/editor/extensions/iframe/IframeNodeView.vue +54 -48
- package/src/molecules/editor/extensions/iframe/iframe-allowlist.ts +33 -4
- package/src/molecules/editor/extensions/iframe/iframe-commands.ts +49 -5
- package/src/molecules/editor/extensions/iframe/iframe-embed-utils.ts +30 -0
- package/src/molecules/editor/extensions/iframe/iframe-extension.ts +4 -4
- package/src/molecules/editor/extensions/iframe/iframeInsertDialogController.ts +12 -1
- package/src/molecules/editor/extensions/iframe/index.ts +4 -4
- package/src/molecules/editor/extensions/iframe/useIframeDialog.ts +25 -13
- package/src/molecules/editor/extensions/image/image-extension.ts +29 -11
- package/src/molecules/editor/extensions/image-group/ImageGroupGrid.vue +27 -11
- package/src/molecules/editor/extensions/image-group/ImageGroupGridCell.vue +55 -11
- package/src/molecules/editor/extensions/image-group/ImageGroupNodeView.vue +64 -31
- package/src/molecules/editor/extensions/image-group/ImageGroupUploadDialog.vue +84 -40
- package/src/molecules/editor/extensions/image-group/image-group-extension.ts +4 -3
- package/src/molecules/editor/extensions/image-group/useImageGroupDialog.ts +116 -17
- package/src/molecules/editor/extensions/link/LinkEditorPopup.vue +214 -71
- package/src/molecules/editor/extensions/link/link-click-plugin.ts +10 -15
- package/src/molecules/editor/extensions/link/link-commands.ts +24 -3
- package/src/molecules/editor/extensions/link/link-extension.ts +11 -2
- package/src/molecules/editor/extensions/link/link-popup-controller.test.ts +49 -0
- package/src/molecules/editor/extensions/link/link-popup-controller.ts +67 -2
- package/src/molecules/editor/extensions/link/link-shortcut-plugin.ts +7 -2
- package/src/molecules/editor/extensions/media-drop/media-drop-extension.ts +177 -0
- package/src/molecules/editor/extensions/shared/media-dimensions.ts +36 -3
- package/src/molecules/editor/extensions/shared/media-node-ops.ts +17 -0
- package/src/molecules/editor/extensions/shared/media-plugin.test.ts +60 -0
- package/src/molecules/editor/extensions/shared/media-plugin.ts +7 -37
- package/src/molecules/editor/extensions/shared/media-upload-engine.test.ts +39 -2
- package/src/molecules/editor/extensions/shared/media-upload-engine.ts +104 -10
- package/src/molecules/editor/extensions/shared/media-upload-state.ts +55 -0
- package/src/molecules/editor/extensions/shared/media-upload-types.ts +52 -1
- package/src/molecules/editor/extensions/shared/suggestion-helpers.ts +20 -0
- package/src/molecules/editor/extensions/shared/suggestion-types.ts +7 -0
- package/src/molecules/editor/extensions/shared/upload-types.ts +1 -0
- package/src/molecules/editor/extensions/slash-commands/slash-commands-extension.test.ts +38 -0
- package/src/molecules/editor/extensions/slash-commands/slash-commands-extension.ts +119 -55
- package/src/molecules/editor/extensions/suggestion/SuggestionList.vue +62 -22
- package/src/molecules/editor/extensions/suggestion/SuggestionListItem.vue +1 -1
- package/src/molecules/editor/extensions/suggestion/createSuggestionExtension.ts +4 -0
- package/src/molecules/editor/extensions/table/drag-scroll.ts +154 -0
- package/src/molecules/editor/extensions/table/table-cell-color.ts +188 -0
- package/src/molecules/editor/extensions/table/table-navigation.ts +560 -0
- package/src/molecules/editor/extensions/table/table-selection-overlay.ts +137 -0
- package/src/molecules/editor/extensions/toc-node/TocNodeView.vue +1 -1
- package/src/molecules/editor/extensions/toc-node/toc-render.ts +7 -3
- package/src/molecules/editor/extensions/video/video-extension.ts +72 -5
- package/src/molecules/editor/extensions.ts +176 -8
- package/src/molecules/editor/index.ts +9 -4
- package/src/molecules/editor/kits.test.ts +21 -1
- package/src/molecules/editor/kits.ts +56 -33
- package/src/molecules/editor/menu.test.ts +3 -1
- package/src/molecules/editor/menu.ts +180 -18
- package/src/molecules/editor/stories/Comment.vue +4 -4
- package/src/molecules/editor/stories/Inline.vue +1 -1
- package/src/molecules/editor/stories/Primitives.vue +1 -1
- package/src/molecules/editor/stories/RichText.vue +1 -1
- package/src/molecules/editor/style.css +80 -3
- package/src/molecules/editor/useEditor.test.ts +4 -0
- package/src/molecules/editor/useEditor.ts +7 -3
- package/src/molecules/list/List.cy.ts +626 -0
- package/src/molecules/list/List.vue +93 -0
- package/src/molecules/list/ListCell.vue +15 -0
- package/src/molecules/list/ListGroup.vue +40 -0
- package/src/molecules/list/ListHeader.vue +62 -0
- package/src/molecules/list/ListHeaderCell.vue +26 -0
- package/src/molecules/list/ListHeaderCellSort.vue +106 -0
- package/src/molecules/list/ListRow.vue +55 -0
- package/src/molecules/list/ListRowBase.vue +103 -0
- package/src/molecules/list/ListRows.vue +120 -0
- package/src/molecules/list/index.ts +24 -0
- package/src/molecules/list/list-context.ts +44 -0
- package/src/molecules/list/list.api.md +276 -0
- package/src/molecules/list/list.md +119 -0
- package/src/molecules/list/stories/Columns.vue +103 -0
- package/src/molecules/list/stories/Feed.vue +105 -0
- package/src/molecules/list/stories/Virtual.vue +31 -0
- package/src/molecules/list/style.css +173 -0
- package/src/molecules/list/types.ts +82 -0
- package/src/molecules/list/useVirtualRows.ts +80 -0
- package/src/resources/documentResource.d.ts +28 -0
- package/src/resources/documentResource.js +32 -4
- package/src/resources/documentResource.test.ts +111 -0
- package/src/resources/listResource.d.ts +33 -0
- package/src/resources/plugin.d.ts +7 -0
- package/src/resources/resources.d.ts +35 -0
- package/src/utils/{call.js → call.ts} +50 -9
- package/src/utils/config.ts +16 -1
- package/src/utils/confirmDialog.d.ts +17 -0
- package/src/utils/debounce.ts +6 -7
- package/src/utils/dialog.cy.ts +9 -9
- package/src/utils/fileSize.ts +36 -0
- package/src/utils/fileUploadHandler.ts +46 -6
- package/src/utils/frappeRequest.test.ts +79 -0
- package/src/utils/{frappeRequest.js → frappeRequest.ts} +76 -13
- package/src/utils/pageMeta.ts +1 -1
- package/src/utils/plugin.ts +58 -0
- package/src/utils/{request.js → request.ts} +28 -3
- package/src/utils/{socketio.js → socketio.ts} +11 -1
- package/src/utils/theme.ts +48 -57
- package/src/utils/useFileUpload.ts +80 -23
- package/tailwind/audit-token-drift.cjs +176 -0
- package/tailwind/colorPalette.js +69 -8
- package/tailwind/colors.json +1115 -397
- package/tailwind/figma-tokens-to-theme.js +364 -0
- package/tailwind/generated/colors.json +1217 -0
- package/tailwind/generated/effects.json +41 -0
- package/tailwind/generated/radius.json +20 -0
- package/tailwind/generated/typography.json +479 -0
- package/tailwind/migrate-tokens-v2.js +453 -0
- package/tailwind/migrate-tokens-v2.test.js +55 -0
- package/tailwind/plugin.js +220 -15
- package/tailwind/preset.js +15 -0
- package/tailwind/tokens.js +35 -37
- package/tsconfig.base.json +2 -2
- package/vitepress/components/Docs/CommandPalette.vue +214 -0
- package/vitepress/components/Docs/Demo.vue +60 -0
- package/vitepress/components/Docs/EmitsTable.vue +99 -0
- package/vitepress/components/Docs/MobileNavSheet.vue +148 -0
- package/vitepress/components/Docs/OnThisPage.vue +74 -0
- package/vitepress/components/Docs/PrevNextBtns.vue +58 -0
- package/vitepress/components/Docs/PropsTable.vue +185 -0
- package/vitepress/components/Docs/Search.vue +34 -0
- package/vitepress/components/Docs/Sidebar.vue +91 -0
- package/vitepress/components/Docs/SlotsTable.vue +99 -0
- package/vitepress/components/Docs/index.ts +13 -0
- package/vitepress/components/Docs/sidebarList.ts +33 -0
- package/vitepress/components/Layout.vue +58 -0
- package/vitepress/components/Navbar.vue +126 -0
- package/vitepress/components/index.ts +3 -0
- package/vitepress/composables/index.ts +3 -0
- package/vitepress/composables/useTheme.ts +39 -0
- package/vitepress/css/fonts/Inter-Italic.var.woff2 +0 -0
- package/vitepress/css/fonts/Inter.var.woff2 +0 -0
- package/vitepress/css/style.css +198 -0
- package/vitepress/index.d.ts +23 -0
- package/vitepress/index.node.ts +175 -0
- package/vitepress/index.ts +17 -0
- package/vitepress/plugins/colocatedComponentDocs.ts +164 -0
- package/vitepress/plugins/componentTransformer.ts +286 -0
- package/vitepress/plugins/index.ts +12 -0
- package/vitepress/state.ts +10 -0
- package/vitepress/theme/index.ts +21 -0
- package/frappe/telemetry/utils.ts +0 -15
- package/src/components/Badge/stories/Themes.vue +0 -10
- package/src/components/Badge/stories/Variants.vue +0 -10
- package/src/components/Button/stories/Icons.vue +0 -27
- package/src/components/Button/stories/Sizes.vue +0 -11
- package/src/components/Button/stories/Themes.vue +0 -10
- package/src/components/Button/stories/Variants.vue +0 -10
- package/src/components/Popover/stories/Hover.vue +0 -14
- package/src/components/Resource.vue +0 -21
- package/src/components/Sidebar/stories/Example.vue +0 -76
- package/src/components/Spinner.vue +0 -3
- package/src/components/TabButtons/TabButtons.story.vue +0 -22
- package/src/utils/plugin.js +0 -24
- /package/src/components/Dropdown/stories/{02_Shortcuts.vue → Shortcuts.vue} +0 -0
- /package/src/components/Dropdown/stories/{01_Simple.vue → Simple.vue} +0 -0
- /package/src/components/Dropdown/stories/{03_Submenus.vue → Submenus.vue} +0 -0
- /package/src/components/Dropdown/stories/{04_Switches.vue → Switches.vue} +0 -0
- /package/src/components/{Dropdown/DropdownRenderContentAsChild.vue → Menu/MenuRenderContentAsChild.vue} +0 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { computed, reactive, type Ref } from 'vue'
|
|
2
|
+
import type {
|
|
3
|
+
DropInfo,
|
|
4
|
+
DropPosition,
|
|
5
|
+
MoveContext,
|
|
6
|
+
TreeKey,
|
|
7
|
+
TreeNode,
|
|
8
|
+
} from './types'
|
|
9
|
+
|
|
10
|
+
interface DragDropOptions {
|
|
11
|
+
keyOf: (node: TreeNode) => TreeKey
|
|
12
|
+
childrenOf: (node: TreeNode) => TreeNode[]
|
|
13
|
+
/** Siblings of a node — a parent's children, or the roots for top level. */
|
|
14
|
+
siblingsOf: (parent: TreeNode | null) => TreeNode[]
|
|
15
|
+
labelOf: (node: TreeNode) => string
|
|
16
|
+
disabled: Ref<boolean>
|
|
17
|
+
/** Live predicate gating a hovered drop target. */
|
|
18
|
+
move?: (ctx: MoveContext) => boolean
|
|
19
|
+
/** A drag was picked up. */
|
|
20
|
+
emitDragStart: (node: TreeNode) => void
|
|
21
|
+
/** A drag finished — `info` on a committed move, `null` on cancel. */
|
|
22
|
+
emitDragEnd: (info: DropInfo | null) => void
|
|
23
|
+
/** Announce drag state changes to assistive tech. */
|
|
24
|
+
announce: (message: string) => void
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface DragState {
|
|
28
|
+
source: TreeNode | null
|
|
29
|
+
sourceParent: TreeNode | null
|
|
30
|
+
target: TreeNode | null
|
|
31
|
+
position: DropPosition | null
|
|
32
|
+
x: number
|
|
33
|
+
y: number
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Owns the live drag state and resolves + validates drops for the tree.
|
|
38
|
+
* Pure logic — it never mutates `nodes`; it hands the caller a `DropInfo` via
|
|
39
|
+
* `drag-end` and lets them persist and update the model.
|
|
40
|
+
*/
|
|
41
|
+
export function useTreeDragDrop(options: DragDropOptions) {
|
|
42
|
+
const { keyOf, childrenOf, siblingsOf, disabled, move } = options
|
|
43
|
+
|
|
44
|
+
const state = reactive<DragState>({
|
|
45
|
+
source: null,
|
|
46
|
+
sourceParent: null,
|
|
47
|
+
target: null,
|
|
48
|
+
position: null,
|
|
49
|
+
x: 0,
|
|
50
|
+
y: 0,
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
// Resolved on a valid drop, emitted on the following `dragend`.
|
|
54
|
+
let pending: DropInfo | null = null
|
|
55
|
+
|
|
56
|
+
function isDescendant(node: TreeNode, key: TreeKey): boolean {
|
|
57
|
+
const stack = [...childrenOf(node)]
|
|
58
|
+
while (stack.length) {
|
|
59
|
+
const current = stack.pop() as TreeNode
|
|
60
|
+
if (keyOf(current) === key) return true
|
|
61
|
+
stack.push(...childrenOf(current))
|
|
62
|
+
}
|
|
63
|
+
return false
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Resolve where the cursor sits within a row into a drop position. */
|
|
67
|
+
function resolvePosition(e: DragEvent): DropPosition {
|
|
68
|
+
const row = e.currentTarget as HTMLElement
|
|
69
|
+
const rect = row.getBoundingClientRect()
|
|
70
|
+
const offset = e.clientY - rect.top
|
|
71
|
+
if (offset < rect.height * 0.25) return 'before'
|
|
72
|
+
if (offset > rect.height * 0.75) return 'after'
|
|
73
|
+
return 'inside'
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Whether a drop of `source` onto `target` at `position` is permitted. */
|
|
77
|
+
function isValidDrop(
|
|
78
|
+
source: TreeNode,
|
|
79
|
+
target: TreeNode,
|
|
80
|
+
position: DropPosition,
|
|
81
|
+
): boolean {
|
|
82
|
+
const sourceKey = keyOf(source)
|
|
83
|
+
const targetKey = keyOf(target)
|
|
84
|
+
if (sourceKey === targetKey) return false
|
|
85
|
+
if (isDescendant(source, targetKey)) return false
|
|
86
|
+
// Re-parenting onto the node's current parent is a no-op.
|
|
87
|
+
if (
|
|
88
|
+
position === 'inside' &&
|
|
89
|
+
state.sourceParent &&
|
|
90
|
+
keyOf(state.sourceParent) === targetKey
|
|
91
|
+
)
|
|
92
|
+
return false
|
|
93
|
+
if (move && !move({ node: source, target, position })) return false
|
|
94
|
+
return true
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function onDragStart(
|
|
98
|
+
e: DragEvent,
|
|
99
|
+
node: TreeNode,
|
|
100
|
+
parent: TreeNode | null,
|
|
101
|
+
): void {
|
|
102
|
+
if (disabled.value) return
|
|
103
|
+
state.source = node
|
|
104
|
+
state.sourceParent = parent
|
|
105
|
+
pending = null
|
|
106
|
+
if (e.dataTransfer) {
|
|
107
|
+
e.dataTransfer.effectAllowed = 'move'
|
|
108
|
+
e.dataTransfer.setData('text/plain', String(keyOf(node)))
|
|
109
|
+
}
|
|
110
|
+
options.emitDragStart(node)
|
|
111
|
+
options.announce(`Picked up ${options.labelOf(node)}`)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function onDragOver(e: DragEvent, node: TreeNode): void {
|
|
115
|
+
if (!state.source || disabled.value) return
|
|
116
|
+
const position = resolvePosition(e)
|
|
117
|
+
state.x = e.clientX
|
|
118
|
+
state.y = e.clientY
|
|
119
|
+
if (isValidDrop(state.source, node, position)) {
|
|
120
|
+
state.target = node
|
|
121
|
+
state.position = position
|
|
122
|
+
if (e.dataTransfer) e.dataTransfer.dropEffect = 'move'
|
|
123
|
+
} else {
|
|
124
|
+
state.target = null
|
|
125
|
+
state.position = null
|
|
126
|
+
if (e.dataTransfer) e.dataTransfer.dropEffect = 'none'
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function onDragLeave(node: TreeNode): void {
|
|
131
|
+
if (state.target && keyOf(state.target) === keyOf(node)) {
|
|
132
|
+
state.target = null
|
|
133
|
+
state.position = null
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function onDrop(target: TreeNode, targetParent: TreeNode | null): void {
|
|
138
|
+
const source = state.source
|
|
139
|
+
const position = state.position
|
|
140
|
+
if (!source || !position || !state.target) return
|
|
141
|
+
if (keyOf(state.target) !== keyOf(target)) return
|
|
142
|
+
|
|
143
|
+
const from = state.sourceParent ? keyOf(state.sourceParent) : null
|
|
144
|
+
const oldSiblings = siblingsOf(state.sourceParent)
|
|
145
|
+
const oldIndex = oldSiblings.findIndex((n) => keyOf(n) === keyOf(source))
|
|
146
|
+
|
|
147
|
+
let to: TreeKey | null
|
|
148
|
+
let newIndex: number
|
|
149
|
+
|
|
150
|
+
if (position === 'inside') {
|
|
151
|
+
to = keyOf(target)
|
|
152
|
+
newIndex = childrenOf(target).length
|
|
153
|
+
} else {
|
|
154
|
+
to = targetParent ? keyOf(targetParent) : null
|
|
155
|
+
const siblings = siblingsOf(targetParent)
|
|
156
|
+
const targetIndex = siblings.findIndex((n) => keyOf(n) === keyOf(target))
|
|
157
|
+
let insertIndex = position === 'before' ? targetIndex : targetIndex + 1
|
|
158
|
+
// When reordering within the same parent, the source still occupies a
|
|
159
|
+
// slot in `siblings`. Removing it first shifts later positions down by
|
|
160
|
+
// one, so the reported `newIndex` is the final post-removal position.
|
|
161
|
+
if (from === to && oldIndex > -1 && oldIndex < insertIndex)
|
|
162
|
+
insertIndex -= 1
|
|
163
|
+
newIndex = insertIndex
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
pending = { node: source, from, to, position, oldIndex, newIndex }
|
|
167
|
+
options.announce(`Moved ${options.labelOf(source)}`)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function reset(): void {
|
|
171
|
+
state.source = null
|
|
172
|
+
state.sourceParent = null
|
|
173
|
+
state.target = null
|
|
174
|
+
state.position = null
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function onDragEnd(): void {
|
|
178
|
+
if (!pending && state.source) options.announce('Cancelled move')
|
|
179
|
+
options.emitDragEnd(pending)
|
|
180
|
+
pending = null
|
|
181
|
+
reset()
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const dragSourceKey = computed(() =>
|
|
185
|
+
state.source ? keyOf(state.source) : null,
|
|
186
|
+
)
|
|
187
|
+
const dropTargetKey = computed(() =>
|
|
188
|
+
state.target ? keyOf(state.target) : null,
|
|
189
|
+
)
|
|
190
|
+
const dropPosition = computed(() => state.position)
|
|
191
|
+
|
|
192
|
+
return {
|
|
193
|
+
state,
|
|
194
|
+
dragSourceKey,
|
|
195
|
+
dropTargetKey,
|
|
196
|
+
dropPosition,
|
|
197
|
+
onDragStart,
|
|
198
|
+
onDragOver,
|
|
199
|
+
onDragLeave,
|
|
200
|
+
onDrop,
|
|
201
|
+
onDragEnd,
|
|
202
|
+
}
|
|
203
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import type { Ref } from 'vue'
|
|
2
|
+
import type { TreeKey, TreeNode } from './types'
|
|
3
|
+
|
|
4
|
+
/** One visible (un-collapsed) row, in render order. */
|
|
5
|
+
export interface FlatNode {
|
|
6
|
+
key: TreeKey
|
|
7
|
+
node: TreeNode
|
|
8
|
+
parentKey: TreeKey | null
|
|
9
|
+
level: number
|
|
10
|
+
hasChildren: boolean
|
|
11
|
+
expanded: boolean
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface KeyboardOptions {
|
|
15
|
+
/** Visible rows in render order; recomputed as expansion changes. */
|
|
16
|
+
flat: Ref<FlatNode[]>
|
|
17
|
+
focusedKey: Ref<TreeKey | null>
|
|
18
|
+
labelOf: (node: TreeNode) => string
|
|
19
|
+
focus: (key: TreeKey) => void
|
|
20
|
+
expand: (node: TreeNode) => void
|
|
21
|
+
collapse: (node: TreeNode) => void
|
|
22
|
+
toggle: (node: TreeNode) => void
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Roving-focus keyboard navigation for the tree, following the WAI-ARIA
|
|
27
|
+
* Tree View pattern (P12). Operates on the flattened list of visible rows.
|
|
28
|
+
*/
|
|
29
|
+
export function useTreeKeyboard(options: KeyboardOptions) {
|
|
30
|
+
const { flat, focusedKey, focus, expand, collapse, toggle } = options
|
|
31
|
+
|
|
32
|
+
let typeahead = ''
|
|
33
|
+
let typeaheadTimer: ReturnType<typeof setTimeout> | null = null
|
|
34
|
+
|
|
35
|
+
const indexOfFocused = () =>
|
|
36
|
+
flat.value.findIndex((f) => f.key === focusedKey.value)
|
|
37
|
+
|
|
38
|
+
function focusAt(index: number): void {
|
|
39
|
+
const item = flat.value[index]
|
|
40
|
+
if (item) focus(item.key)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function moveToParent(current: FlatNode): void {
|
|
44
|
+
if (current.parentKey == null) return
|
|
45
|
+
focus(current.parentKey)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function typeaheadSearch(char: string): void {
|
|
49
|
+
if (typeaheadTimer) clearTimeout(typeaheadTimer)
|
|
50
|
+
typeahead += char.toLowerCase()
|
|
51
|
+
typeaheadTimer = setTimeout(() => (typeahead = ''), 500)
|
|
52
|
+
|
|
53
|
+
const start = Math.max(indexOfFocused(), 0)
|
|
54
|
+
const ordered = [
|
|
55
|
+
...flat.value.slice(start + 1),
|
|
56
|
+
...flat.value.slice(0, start + 1),
|
|
57
|
+
]
|
|
58
|
+
const match = ordered.find((f) =>
|
|
59
|
+
options.labelOf(f.node).toLowerCase().startsWith(typeahead),
|
|
60
|
+
)
|
|
61
|
+
if (match) focus(match.key)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function onKeydown(e: KeyboardEvent): void {
|
|
65
|
+
const index = indexOfFocused()
|
|
66
|
+
const current = flat.value[index]
|
|
67
|
+
if (!current && !['Home', 'End'].includes(e.key)) return
|
|
68
|
+
|
|
69
|
+
switch (e.key) {
|
|
70
|
+
case 'ArrowDown':
|
|
71
|
+
e.preventDefault()
|
|
72
|
+
focusAt(Math.min(index + 1, flat.value.length - 1))
|
|
73
|
+
break
|
|
74
|
+
case 'ArrowUp':
|
|
75
|
+
e.preventDefault()
|
|
76
|
+
focusAt(Math.max(index - 1, 0))
|
|
77
|
+
break
|
|
78
|
+
case 'ArrowRight':
|
|
79
|
+
e.preventDefault()
|
|
80
|
+
if (current.hasChildren && !current.expanded) expand(current.node)
|
|
81
|
+
else if (current.hasChildren && current.expanded) focusAt(index + 1)
|
|
82
|
+
break
|
|
83
|
+
case 'ArrowLeft':
|
|
84
|
+
e.preventDefault()
|
|
85
|
+
if (current.hasChildren && current.expanded) collapse(current.node)
|
|
86
|
+
else moveToParent(current)
|
|
87
|
+
break
|
|
88
|
+
case 'Home':
|
|
89
|
+
e.preventDefault()
|
|
90
|
+
focusAt(0)
|
|
91
|
+
break
|
|
92
|
+
case 'End':
|
|
93
|
+
e.preventDefault()
|
|
94
|
+
focusAt(flat.value.length - 1)
|
|
95
|
+
break
|
|
96
|
+
case 'Enter':
|
|
97
|
+
case ' ':
|
|
98
|
+
e.preventDefault()
|
|
99
|
+
toggle(current.node)
|
|
100
|
+
break
|
|
101
|
+
default:
|
|
102
|
+
if (e.key.length === 1 && !e.metaKey && !e.ctrlKey && !e.altKey) {
|
|
103
|
+
typeaheadSearch(e.key)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return { onKeydown }
|
|
109
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<PopoverRoot v-model:open="open">
|
|
3
3
|
<PopoverAnchor :reference="anchorEl" as-child>
|
|
4
|
-
<div @keydown.down.prevent="onArrowDown">
|
|
4
|
+
<div v-bind="$attrs" @keydown.down.prevent="onArrowDown">
|
|
5
5
|
<slot name="trigger" v-bind="triggerSlotProps">
|
|
6
6
|
<slot name="target" v-bind="triggerSlotProps">
|
|
7
7
|
<TextInput
|
|
@@ -44,7 +44,6 @@
|
|
|
44
44
|
<PopoverPortal>
|
|
45
45
|
<PopoverContent
|
|
46
46
|
data-slot="content"
|
|
47
|
-
data-selection
|
|
48
47
|
class="z-[100]"
|
|
49
48
|
:side="side"
|
|
50
49
|
:align="align"
|
|
@@ -52,15 +51,14 @@
|
|
|
52
51
|
@open-auto-focus.prevent
|
|
53
52
|
@interact-outside="onInteractOutside"
|
|
54
53
|
>
|
|
55
|
-
<
|
|
56
|
-
ref="
|
|
57
|
-
|
|
58
|
-
:data-motion="motion"
|
|
54
|
+
<PopoverPanel
|
|
55
|
+
ref="popoverPanelRef"
|
|
56
|
+
:motion="motion"
|
|
59
57
|
:class="contentClass"
|
|
60
|
-
|
|
58
|
+
class="origin-[var(--reka-popover-content-transform-origin)]"
|
|
61
59
|
>
|
|
62
60
|
<slot :close="closePopover" />
|
|
63
|
-
</
|
|
61
|
+
</PopoverPanel>
|
|
64
62
|
</PopoverContent>
|
|
65
63
|
</PopoverPortal>
|
|
66
64
|
</PopoverRoot>
|
|
@@ -76,10 +74,10 @@ import {
|
|
|
76
74
|
} from 'reka-ui'
|
|
77
75
|
import { TextInput } from '../../TextInput'
|
|
78
76
|
import LucideChevronDown from '~icons/lucide/chevron-down'
|
|
77
|
+
import PopoverPanel from '../popover/PopoverPanel.vue'
|
|
79
78
|
import { usePopoverMotion } from '../../../composables/usePopoverMotion'
|
|
80
79
|
import type { InputSize, InputVariant } from '../../../composables/inputTypes'
|
|
81
80
|
import type { FrappeUIError } from '../../../composables/useInputLabeling'
|
|
82
|
-
import '../selection/popoverMotion.css'
|
|
83
81
|
|
|
84
82
|
interface Props {
|
|
85
83
|
// Positioning — already resolved by caller from side/align/placement.
|
|
@@ -107,7 +105,9 @@ interface Props {
|
|
|
107
105
|
// Display label exposed via trigger slot props for caller-rendered triggers.
|
|
108
106
|
displayLabel?: string
|
|
109
107
|
|
|
110
|
-
// Tailwind
|
|
108
|
+
// Extra Tailwind classes (layout/sizing such as `w-56`/`w-fit`) merged onto
|
|
109
|
+
// the `PopoverPanel` shell. The elevated shell itself (rounded/bg/shadow/ring)
|
|
110
|
+
// is owned by `PopoverPanel`, so callers no longer pass those here.
|
|
111
111
|
contentClass?: string
|
|
112
112
|
}
|
|
113
113
|
|
|
@@ -145,6 +145,8 @@ const slots = defineSlots<{
|
|
|
145
145
|
default?: (props: { close: () => void }) => any
|
|
146
146
|
}>()
|
|
147
147
|
|
|
148
|
+
defineOptions({ inheritAttrs: false })
|
|
149
|
+
|
|
148
150
|
const open = defineModel<boolean>('open', { default: false })
|
|
149
151
|
const inputValue = defineModel<string>('inputValue', { default: '' })
|
|
150
152
|
const typing = defineModel<boolean>('typing', { default: false })
|
|
@@ -159,7 +161,12 @@ interface TriggerSlotProps {
|
|
|
159
161
|
// Anchor the popover at the input element itself (not the labeling wrapper),
|
|
160
162
|
// so it sits below the input rather than below the description text.
|
|
161
163
|
const textInputRef = ref<{ el: HTMLElement | null } | null>(null)
|
|
162
|
-
const
|
|
164
|
+
const popoverPanelRef = ref<{ $el: HTMLElement } | null>(null)
|
|
165
|
+
|
|
166
|
+
// PopoverPanel renders a single root <div>, so its `$el` is the panel element
|
|
167
|
+
// we test focus containment against (Esc / selection should restore trigger
|
|
168
|
+
// focus; click-outside should not).
|
|
169
|
+
const panelEl = computed(() => popoverPanelRef.value?.$el ?? null)
|
|
163
170
|
|
|
164
171
|
const anchorEl = computed(() => {
|
|
165
172
|
if (slots.trigger || slots.target) return undefined
|
|
@@ -208,7 +215,7 @@ function onClick(e: MouseEvent) {
|
|
|
208
215
|
function onBlur(e: FocusEvent) {
|
|
209
216
|
// Clicks inside the popover panel re-focus options; treat those as still-focused.
|
|
210
217
|
const next = e.relatedTarget as Node | null
|
|
211
|
-
if (next &&
|
|
218
|
+
if (next && panelEl.value?.contains(next)) return
|
|
212
219
|
emit('blur')
|
|
213
220
|
typing.value = false
|
|
214
221
|
}
|
|
@@ -246,9 +253,7 @@ watch(open, (val, prev) => {
|
|
|
246
253
|
// Restore focus to the trigger input if the popover content had focus
|
|
247
254
|
// (Esc, date selection in auto-close mode). Click-outside leaves focus
|
|
248
255
|
// on the clicked element, so we skip in that case.
|
|
249
|
-
const hadFocusInside =
|
|
250
|
-
document.activeElement,
|
|
251
|
-
)
|
|
256
|
+
const hadFocusInside = panelEl.value?.contains(document.activeElement)
|
|
252
257
|
emit('close')
|
|
253
258
|
if (hadFocusInside) {
|
|
254
259
|
nextTick(() => textInputRef.value?.el?.focus())
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
data-slot="content-body"
|
|
4
|
+
data-panel
|
|
5
|
+
:data-motion="motion"
|
|
6
|
+
:data-state="state"
|
|
7
|
+
:class="shellClass"
|
|
8
|
+
>
|
|
9
|
+
<slot />
|
|
10
|
+
</div>
|
|
11
|
+
</template>
|
|
12
|
+
|
|
13
|
+
<script setup lang="ts">
|
|
14
|
+
/**
|
|
15
|
+
* Shared floating-panel shell for popover-style surfaces (Popover, Select,
|
|
16
|
+
* Combobox, DatePicker, TimePicker, …).
|
|
17
|
+
*
|
|
18
|
+
* It owns ONLY two things:
|
|
19
|
+
* 1. the visual shell — `rounded-lg bg-surface-elevation-2 shadow-2xl
|
|
20
|
+
* ring-1 ring-black ring-opacity-5` — matching what the selection family
|
|
21
|
+
* renders today, plus the `data-slot="content-body"` hook, and
|
|
22
|
+
* 2. the open/close motion wiring (`data-motion` + the co-located
|
|
23
|
+
* `popoverPanel.css`, supporting both the `animated` scale-from-trigger
|
|
24
|
+
* rhythm and the `instant` keyboard/Dropdown rhythm).
|
|
25
|
+
*
|
|
26
|
+
* It owns NO behavior and NO reka root: render it INSIDE a reka
|
|
27
|
+
* `PopoverContent` / `SelectContent` / `ComboboxContent` / `DropdownMenuContent`
|
|
28
|
+
* (or directly). Each consumer keeps its own reka `*Root`, `*Portal`,
|
|
29
|
+
* `*Content`, `FocusScope` and a11y wiring, and renders its contents in the
|
|
30
|
+
* default slot.
|
|
31
|
+
*
|
|
32
|
+
* It is a thin pass-through: Vue auto-merges any `class`/`style`/attrs onto the
|
|
33
|
+
* single root, so consumers add layout (flex/grid/divide/min-w) and the
|
|
34
|
+
* per-primitive `transform-origin` without losing the shell classes.
|
|
35
|
+
*/
|
|
36
|
+
import './popoverPanel.css'
|
|
37
|
+
|
|
38
|
+
withDefaults(
|
|
39
|
+
defineProps<{
|
|
40
|
+
/**
|
|
41
|
+
* Open/close rhythm:
|
|
42
|
+
* - `'animated'` (default): pointer opens → scale-from-trigger entrance.
|
|
43
|
+
* - `'instant'`: keyboard / programmatic opens → ~80ms opacity fade only.
|
|
44
|
+
* Drive this from `usePopoverMotion(open).motion`.
|
|
45
|
+
*/
|
|
46
|
+
motion?: 'animated' | 'instant'
|
|
47
|
+
/**
|
|
48
|
+
* The reka content's `data-state`, forwarded onto the panel element so the
|
|
49
|
+
* co-located motion CSS is self-contained. Omit when the parent reka
|
|
50
|
+
* `*Content` already exposes `data-state` and you rely on reka's own state
|
|
51
|
+
* (the motion CSS matches an ancestor `[data-state]` too).
|
|
52
|
+
*/
|
|
53
|
+
state?: 'open' | 'closed'
|
|
54
|
+
}>(),
|
|
55
|
+
{
|
|
56
|
+
motion: 'animated',
|
|
57
|
+
},
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
defineSlots<{ default?: () => any }>()
|
|
61
|
+
|
|
62
|
+
// The standard elevated floating-panel shell. Kept verbatim in sync with the
|
|
63
|
+
// selection family's `content-body` so every popover surface shares one look.
|
|
64
|
+
// Consumer `class` (layout, transform-origin) auto-merges after this.
|
|
65
|
+
const shellClass =
|
|
66
|
+
'overflow-hidden rounded-lg bg-surface-elevation-2 shadow-2xl ring-1 ring-black ring-opacity-5 will-change-[opacity,transform]'
|
|
67
|
+
</script>
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Open/close motion for the shared `PopoverPanel` shell. Imported once by
|
|
3
|
+
* `PopoverPanel.vue` so it is deduped at build time — consumers never import
|
|
4
|
+
* it directly.
|
|
5
|
+
*
|
|
6
|
+
* Every selector is gated on the compound `[data-slot='content-body'][data-panel]`
|
|
7
|
+
* so this stylesheet only applies to `PopoverPanel` instances and cannot leak
|
|
8
|
+
* onto other popovers sharing the `data-slot` hooks (e.g. Dropdown's
|
|
9
|
+
* `.menu-content`, or the legacy selection `popoverMotion.css`).
|
|
10
|
+
*
|
|
11
|
+
* `data-state` is read from one of two places, so the panel is drop-in for
|
|
12
|
+
* either wiring style:
|
|
13
|
+
* 1. the panel element itself, when the consumer passes the `state` prop
|
|
14
|
+
* (fully self-contained — preferred for new code), or
|
|
15
|
+
* 2. an ancestor reka `*Content` carrying `data-state`, when the consumer
|
|
16
|
+
* omits `state` and relies on reka's own state (matches how the legacy
|
|
17
|
+
* selection stylesheet worked).
|
|
18
|
+
* `data-motion` always lives on the panel element.
|
|
19
|
+
*
|
|
20
|
+
* `transform-origin` is intentionally NOT set here — each consumer owns it
|
|
21
|
+
* (Popover uses `--reka-popover-content-transform-origin`, Select the
|
|
22
|
+
* `--reka-select-*` var, Combobox the `--reka-combobox-*` var) by passing it
|
|
23
|
+
* through `class`/`style` on the panel.
|
|
24
|
+
*
|
|
25
|
+
* Two rhythms, selected by `data-motion`:
|
|
26
|
+
* - `animated` — scale-from-trigger entrance (pointer opens), 180ms enter /
|
|
27
|
+
* 140ms exit.
|
|
28
|
+
* - `instant` — keyboard / programmatic opens skip the scale + translate; a
|
|
29
|
+
* ~80ms opacity fade still runs on open to mask reka's 1-frame
|
|
30
|
+
* position-settle after mount, and no animation on close.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
[data-slot='content-body'][data-panel] {
|
|
34
|
+
animation-fill-mode: both;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
[data-slot='content-body'][data-panel][data-motion='animated'] {
|
|
38
|
+
backface-visibility: hidden;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/* (1) state on the panel itself (via the `state` prop) */
|
|
42
|
+
[data-slot='content-body'][data-panel][data-motion='animated'][data-state='open'],
|
|
43
|
+
/* (2) state on an ancestor reka *Content */
|
|
44
|
+
[data-state='open']
|
|
45
|
+
[data-slot='content-body'][data-panel][data-motion='animated'] {
|
|
46
|
+
animation: popover-panel-enter 180ms cubic-bezier(0.23, 1, 0.32, 1);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
[data-slot='content-body'][data-panel][data-motion='animated'][data-state='closed'],
|
|
50
|
+
[data-state='closed']
|
|
51
|
+
[data-slot='content-body'][data-panel][data-motion='animated'] {
|
|
52
|
+
animation: popover-panel-exit 140ms cubic-bezier(0.23, 1, 0.32, 1);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
[data-slot='content-body'][data-panel][data-motion='instant'][data-state='open'],
|
|
56
|
+
[data-state='open']
|
|
57
|
+
[data-slot='content-body'][data-panel][data-motion='instant'] {
|
|
58
|
+
animation: popover-panel-instant-fade 80ms linear;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
[data-slot='content-body'][data-panel][data-motion='instant'][data-state='closed'],
|
|
62
|
+
[data-state='closed']
|
|
63
|
+
[data-slot='content-body'][data-panel][data-motion='instant'] {
|
|
64
|
+
animation: none;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
@keyframes popover-panel-enter {
|
|
68
|
+
from {
|
|
69
|
+
opacity: 0;
|
|
70
|
+
transform: translateY(2px) scale(0.97);
|
|
71
|
+
}
|
|
72
|
+
to {
|
|
73
|
+
opacity: 1;
|
|
74
|
+
transform: translateY(0) scale(1);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
@keyframes popover-panel-exit {
|
|
79
|
+
from {
|
|
80
|
+
opacity: 1;
|
|
81
|
+
transform: translateY(0) scale(1);
|
|
82
|
+
}
|
|
83
|
+
to {
|
|
84
|
+
opacity: 0;
|
|
85
|
+
transform: translateY(1px) scale(0.985);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
@keyframes popover-panel-instant-fade {
|
|
90
|
+
from {
|
|
91
|
+
opacity: 0;
|
|
92
|
+
}
|
|
93
|
+
to {
|
|
94
|
+
opacity: 1;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
@media (prefers-reduced-motion: reduce) {
|
|
99
|
+
[data-slot='content-body'][data-panel] {
|
|
100
|
+
animation-duration: 0ms !important;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -25,7 +25,7 @@ export function inputFontSizeClasses(size: SelectionSize) {
|
|
|
25
25
|
sm: 'text-base',
|
|
26
26
|
md: 'text-base',
|
|
27
27
|
lg: 'text-lg',
|
|
28
|
-
xl: 'text-
|
|
28
|
+
xl: 'text-2xl',
|
|
29
29
|
}[size]
|
|
30
30
|
}
|
|
31
31
|
|
|
@@ -56,9 +56,9 @@ export function triggerVariantClasses(
|
|
|
56
56
|
|
|
57
57
|
return {
|
|
58
58
|
subtle:
|
|
59
|
-
'border border-[--surface-gray-2] bg-surface-gray-2 hover:border-outline-
|
|
59
|
+
'border border-[--surface-gray-2] bg-surface-gray-2 hover:border-outline-elevation-2 hover:bg-surface-gray-3',
|
|
60
60
|
outline:
|
|
61
|
-
'border border-outline-gray-2 bg-surface-
|
|
61
|
+
'border border-outline-gray-2 bg-surface-base hover:border-outline-gray-3',
|
|
62
62
|
ghost:
|
|
63
63
|
'border border-transparent bg-transparent hover:bg-surface-gray-3 focus-within:bg-surface-gray-3',
|
|
64
64
|
}[variant]
|
|
@@ -70,13 +70,13 @@ export function triggerVariantClasses(
|
|
|
70
70
|
* on the trigger itself.
|
|
71
71
|
*/
|
|
72
72
|
export const triggerBaseClassesFocusVisible =
|
|
73
|
-
'relative inline-flex items-center gap-2 text-left text-ink-gray-7
|
|
73
|
+
'relative inline-flex items-center gap-2 text-left text-ink-gray-7 transition-[background-color,border-color,box-shadow] duration-150 ease-[cubic-bezier(0.23,1,0.32,1)] data-[state=open]:focus-ring'
|
|
74
74
|
|
|
75
75
|
export const triggerBaseClassesFocusWithin =
|
|
76
|
-
'relative inline-flex items-center gap-2 text-left text-ink-gray-7 outline-none transition-[background-color,border-color,box-shadow] duration-150 ease-[cubic-bezier(0.23,1,0.32,1)] focus-within:ring
|
|
76
|
+
'relative inline-flex items-center gap-2 text-left text-ink-gray-7 outline-none transition-[background-color,border-color,box-shadow] duration-150 ease-[cubic-bezier(0.23,1,0.32,1)] focus-within:focus-ring data-[state=open]:focus-ring'
|
|
77
77
|
|
|
78
78
|
export const itemClasses =
|
|
79
|
-
'select-none rounded border-0 text-base text-ink-gray-9 transition-colors duration-100 ease-out data-[disabled]:text-ink-gray-4 data-[highlighted]:bg-surface-gray-2 data-[state=checked]:bg-surface-gray-3 data-[highlighted]:data-[state=checked]:bg-surface-gray-4'
|
|
79
|
+
'select-none rounded border-0 text-base text-ink-gray-9 transition-colors duration-100 ease-out data-[disabled]:text-ink-gray-4 data-[highlighted]:bg-surface-alpha-gray-2 data-[state=checked]:bg-surface-gray-3 data-[highlighted]:data-[state=checked]:bg-surface-gray-4'
|
|
80
80
|
|
|
81
81
|
/**
|
|
82
82
|
* Case-insensitive substring match against an option's label and value.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { computed, onMounted, onUnmounted, reactive, type ComputedRef } from 'vue'
|
|
2
|
+
|
|
3
|
+
const hasWindow = typeof window !== 'undefined'
|
|
4
|
+
|
|
5
|
+
export interface ScreenSize {
|
|
6
|
+
width: number
|
|
7
|
+
height: number
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Reactive viewport size, updated on window resize.
|
|
12
|
+
*
|
|
13
|
+
* Falls back to a desktop-ish size when there's no window (SSR / non-browser
|
|
14
|
+
* test env) so a `useIsMobile()` derived from it doesn't flash the mobile
|
|
15
|
+
* layout before hydration.
|
|
16
|
+
*/
|
|
17
|
+
export function useScreenSize(): ScreenSize {
|
|
18
|
+
const size = reactive<ScreenSize>({
|
|
19
|
+
width: hasWindow ? window.innerWidth : 1024,
|
|
20
|
+
height: hasWindow ? window.innerHeight : 768,
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
const onResize = () => {
|
|
24
|
+
size.width = window.innerWidth
|
|
25
|
+
size.height = window.innerHeight
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
onMounted(() => {
|
|
29
|
+
if (hasWindow) window.addEventListener('resize', onResize)
|
|
30
|
+
})
|
|
31
|
+
onUnmounted(() => {
|
|
32
|
+
if (hasWindow) window.removeEventListener('resize', onResize)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
return size
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Whether the viewport is narrower than `breakpoint` (default 640px — Tailwind's
|
|
40
|
+
* `sm`). Reactive; apps typically use it to choose between a desktop and a
|
|
41
|
+
* mobile shell.
|
|
42
|
+
*/
|
|
43
|
+
export function useIsMobile(breakpoint = 640): ComputedRef<boolean> {
|
|
44
|
+
const size = useScreenSize()
|
|
45
|
+
return computed(() => size.width < breakpoint)
|
|
46
|
+
}
|