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
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
data-slot="content"
|
|
21
21
|
:data-motion="contentMotion"
|
|
22
22
|
:class="[
|
|
23
|
-
|
|
23
|
+
menuClasses.content,
|
|
24
24
|
{
|
|
25
25
|
'origin-top-left': align === 'start',
|
|
26
26
|
'origin-top-right': align === 'end',
|
|
@@ -30,12 +30,16 @@
|
|
|
30
30
|
:side="side"
|
|
31
31
|
:align="align"
|
|
32
32
|
:side-offset="offset"
|
|
33
|
+
:style="{
|
|
34
|
+
width: matchTriggerWidth ? 'var(--reka-dropdown-menu-trigger-width)' : undefined,
|
|
35
|
+
}"
|
|
33
36
|
>
|
|
34
|
-
<
|
|
37
|
+
<Menu
|
|
35
38
|
:groups="groups"
|
|
36
39
|
:portal-to="portalTo"
|
|
37
40
|
:close="close"
|
|
38
41
|
:slot-fns="slots"
|
|
42
|
+
:primitives="primitives"
|
|
39
43
|
/>
|
|
40
44
|
</DropdownMenuContent>
|
|
41
45
|
</DropdownMenuPortal>
|
|
@@ -46,14 +50,19 @@
|
|
|
46
50
|
import { computed, useAttrs, useSlots } from 'vue'
|
|
47
51
|
import {
|
|
48
52
|
DropdownMenuContent,
|
|
53
|
+
DropdownMenuItem,
|
|
54
|
+
DropdownMenuLabel,
|
|
49
55
|
DropdownMenuPortal,
|
|
50
56
|
DropdownMenuRoot,
|
|
57
|
+
DropdownMenuSub,
|
|
58
|
+
DropdownMenuSubContent,
|
|
59
|
+
DropdownMenuSubTrigger,
|
|
51
60
|
DropdownMenuTrigger,
|
|
52
61
|
} from 'reka-ui'
|
|
53
62
|
import { Button } from '../Button'
|
|
54
|
-
import
|
|
63
|
+
import Menu from '../Menu/Menu.vue'
|
|
55
64
|
import type { DropdownProps, DropdownSlots } from './types'
|
|
56
|
-
import {
|
|
65
|
+
import { menuClasses, normalizeMenuOptions } from '../Menu/utils'
|
|
57
66
|
import { usePopoverMotion } from '../../composables/usePopoverMotion'
|
|
58
67
|
|
|
59
68
|
defineOptions({
|
|
@@ -78,8 +87,17 @@ function close() {
|
|
|
78
87
|
openModel.value = false
|
|
79
88
|
}
|
|
80
89
|
|
|
90
|
+
const primitives = {
|
|
91
|
+
Item: DropdownMenuItem,
|
|
92
|
+
Label: DropdownMenuLabel,
|
|
93
|
+
Portal: DropdownMenuPortal,
|
|
94
|
+
Sub: DropdownMenuSub,
|
|
95
|
+
SubContent: DropdownMenuSubContent,
|
|
96
|
+
SubTrigger: DropdownMenuSubTrigger,
|
|
97
|
+
}
|
|
98
|
+
|
|
81
99
|
const groups = computed(() => {
|
|
82
|
-
return
|
|
100
|
+
return normalizeMenuOptions(props.options)
|
|
83
101
|
})
|
|
84
102
|
|
|
85
103
|
const align = computed(() => {
|
|
@@ -100,47 +118,21 @@ defineSlots<DropdownSlots>()
|
|
|
100
118
|
</script>
|
|
101
119
|
|
|
102
120
|
<style scoped>
|
|
103
|
-
@keyframes dropdown-in {
|
|
104
|
-
from {
|
|
105
|
-
opacity: 0;
|
|
106
|
-
transform: scale(0.95);
|
|
107
|
-
}
|
|
108
|
-
to {
|
|
109
|
-
opacity: 1;
|
|
110
|
-
transform: scale(1);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
@keyframes dropdown-out {
|
|
115
|
-
from {
|
|
116
|
-
opacity: 1;
|
|
117
|
-
transform: scale(1);
|
|
118
|
-
}
|
|
119
|
-
to {
|
|
120
|
-
opacity: 0;
|
|
121
|
-
transform: scale(0.95);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
:global(.dropdown-content[data-motion='animated'][data-state='open']) {
|
|
126
|
-
animation: dropdown-in 100ms ease-out;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
:global(.dropdown-content[data-motion='animated'][data-state='closed']) {
|
|
130
|
-
animation: dropdown-out 75ms ease-in;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
121
|
/*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
122
|
+
* Dropdown is the only menu with a keyboard-open path, so the `instant`
|
|
123
|
+
* motion lives here. Keyboard opens skip the scale entrance, but a tiny
|
|
124
|
+
* opacity fade still runs — it masks the 1-frame position-settle reka
|
|
125
|
+
* performs after mount. ~80ms is below the perception threshold for motion
|
|
126
|
+
* but long enough to hide the jump.
|
|
127
|
+
*
|
|
128
|
+
* The shared `animated` keyframes + `prefers-reduced-motion` reset live in
|
|
129
|
+
* Menu.vue (rendered by both Dropdown and ContextMenu).
|
|
138
130
|
*/
|
|
139
|
-
:global(.
|
|
131
|
+
:global(.menu-content[data-motion='instant'][data-state='open']) {
|
|
140
132
|
animation: dropdown-instant-fade 80ms linear;
|
|
141
133
|
}
|
|
142
134
|
|
|
143
|
-
:global(.
|
|
135
|
+
:global(.menu-content[data-motion='instant'][data-state='closed']) {
|
|
144
136
|
animation: none;
|
|
145
137
|
}
|
|
146
138
|
|
|
@@ -152,10 +144,4 @@ defineSlots<DropdownSlots>()
|
|
|
152
144
|
opacity: 1;
|
|
153
145
|
}
|
|
154
146
|
}
|
|
155
|
-
|
|
156
|
-
@media (prefers-reduced-motion: reduce) {
|
|
157
|
-
:global(.dropdown-content) {
|
|
158
|
-
animation-duration: 0ms !important;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
147
|
</style>
|
|
@@ -52,7 +52,9 @@ function rowActions(task: Task): DropdownOptions {
|
|
|
52
52
|
</script>
|
|
53
53
|
|
|
54
54
|
<template>
|
|
55
|
-
<div
|
|
55
|
+
<div
|
|
56
|
+
class="w-80 divide-y divide-outline-gray-1 rounded-lg border border-outline-gray-1 bg-surface-base"
|
|
57
|
+
>
|
|
56
58
|
<div
|
|
57
59
|
v-for="task in tasks"
|
|
58
60
|
:key="task.title"
|
|
@@ -114,7 +114,7 @@ const userMenuItems = computed(() => [
|
|
|
114
114
|
</svg>
|
|
115
115
|
<div class="ml-2 min-w-0">
|
|
116
116
|
<div
|
|
117
|
-
class="truncate text-base
|
|
117
|
+
class="truncate text-base-medium text-ink-gray-8 leading-none"
|
|
118
118
|
>
|
|
119
119
|
Gameplan
|
|
120
120
|
</div>
|
|
@@ -1,162 +1,34 @@
|
|
|
1
|
-
import type { Component, VNodeChild } from 'vue'
|
|
2
|
-
import type { RouteLocationRaw } from 'vue-router'
|
|
3
1
|
import type { ButtonProps } from '../Button'
|
|
2
|
+
import type { MenuOptions, MenuSlotProps, MenuSlots } from '../Menu/types'
|
|
3
|
+
|
|
4
|
+
export type {
|
|
5
|
+
MenuTheme as DropdownTheme,
|
|
6
|
+
MenuSlotFn as DropdownSlotFn,
|
|
7
|
+
MenuItemSlots as DropdownItemSlots,
|
|
8
|
+
MenuBaseOption as DropdownBaseOption,
|
|
9
|
+
MenuActionOption as DropdownActionOption,
|
|
10
|
+
MenuSwitchOption as DropdownSwitchOption,
|
|
11
|
+
MenuSubmenuOption as DropdownSubmenuOption,
|
|
12
|
+
MenuComponentOption as DropdownComponentOption,
|
|
13
|
+
MenuGroupOption as DropdownGroupOption,
|
|
14
|
+
MenuOption as DropdownOption,
|
|
15
|
+
MenuItem as DropdownItem,
|
|
16
|
+
MenuOptions as DropdownOptions,
|
|
17
|
+
MenuSlotProps as DropdownSlotProps,
|
|
18
|
+
MenuItemSlotProps as DropdownItemSlotProps,
|
|
19
|
+
MenuGroupSlotProps as DropdownGroupSlotProps,
|
|
20
|
+
} from '../Menu/types'
|
|
4
21
|
|
|
5
|
-
export type DropdownTheme = 'gray' | 'red'
|
|
6
22
|
export type DropdownPlacement = 'left' | 'right' | 'center'
|
|
7
23
|
export type DropdownSide = 'top' | 'right' | 'bottom' | 'left'
|
|
8
24
|
export type DropdownAlign = 'start' | 'center' | 'end'
|
|
9
25
|
|
|
10
|
-
export type DropdownSlotFn<TProps> = (props: TProps) => VNodeChild
|
|
11
|
-
|
|
12
|
-
export interface DropdownItemSlots<TProps> {
|
|
13
|
-
/** Replaces the prefix region of the standard row shell. */
|
|
14
|
-
prefix?: DropdownSlotFn<TProps>
|
|
15
|
-
|
|
16
|
-
/** Replaces the label region of the standard row shell. */
|
|
17
|
-
label?: DropdownSlotFn<TProps>
|
|
18
|
-
|
|
19
|
-
/** Replaces the suffix region of the standard row shell. */
|
|
20
|
-
suffix?: DropdownSlotFn<TProps>
|
|
21
|
-
|
|
22
|
-
/** Replaces the entire row; mutually exclusive with `prefix` / `label` / `suffix`. */
|
|
23
|
-
item?: DropdownSlotFn<TProps>
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export interface DropdownBaseOption {
|
|
27
|
-
/** Leading icon shown for the item row. */
|
|
28
|
-
icon?: string | Component | null
|
|
29
|
-
|
|
30
|
-
/** Secondary text shown below the label. */
|
|
31
|
-
description?: string
|
|
32
|
-
|
|
33
|
-
/** Marks the item as currently selected. */
|
|
34
|
-
selected?: boolean
|
|
35
|
-
|
|
36
|
-
/** Disables interaction for the item. */
|
|
37
|
-
disabled?: boolean
|
|
38
|
-
|
|
39
|
-
/** Visual theme applied to the item row. */
|
|
40
|
-
theme?: DropdownTheme
|
|
41
|
-
|
|
42
|
-
/** Named slot suffix used to resolve `item-*` slots dynamically. */
|
|
43
|
-
slot?: string
|
|
44
|
-
|
|
45
|
-
/** Per-item inline slot implementations for the row shell. */
|
|
46
|
-
slots?: DropdownItemSlots<DropdownItemSlotProps>
|
|
47
|
-
|
|
48
|
-
/** Condition used to omit an item from the final menu. */
|
|
49
|
-
condition?: () => boolean
|
|
50
|
-
|
|
51
|
-
[key: string]: any
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export interface DropdownActionOption extends DropdownBaseOption {
|
|
55
|
-
/** Primary label shown for the action item. */
|
|
56
|
-
label: string
|
|
57
|
-
|
|
58
|
-
/** Router destination to navigate to when the item is clicked. */
|
|
59
|
-
route?: RouteLocationRaw
|
|
60
|
-
|
|
61
|
-
/** Click handler invoked when the action item is selected. */
|
|
62
|
-
onClick?: (event: PointerEvent) => void
|
|
63
|
-
|
|
64
|
-
submenu?: never
|
|
65
|
-
switch?: never
|
|
66
|
-
switchValue?: never
|
|
67
|
-
component?: never
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export interface DropdownSwitchOption extends DropdownBaseOption {
|
|
71
|
-
/** Primary label shown for the switch item. */
|
|
72
|
-
label: string
|
|
73
|
-
|
|
74
|
-
/** Renders the item with a switch control. */
|
|
75
|
-
switch: true
|
|
76
|
-
|
|
77
|
-
/** Current boolean value for the switch item. */
|
|
78
|
-
switchValue?: boolean
|
|
79
|
-
|
|
80
|
-
/** Change handler invoked with the next switch value. */
|
|
81
|
-
onClick?: (value: boolean) => void
|
|
82
|
-
|
|
83
|
-
route?: never
|
|
84
|
-
submenu?: never
|
|
85
|
-
component?: never
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export interface DropdownSubmenuOption extends DropdownBaseOption {
|
|
89
|
-
/** Primary label shown for the submenu trigger. */
|
|
90
|
-
label: string
|
|
91
|
-
|
|
92
|
-
/** Nested menu items rendered in the submenu. */
|
|
93
|
-
submenu: DropdownOptions
|
|
94
|
-
|
|
95
|
-
route?: never
|
|
96
|
-
onClick?: never
|
|
97
|
-
switch?: never
|
|
98
|
-
switchValue?: never
|
|
99
|
-
component?: never
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
export interface DropdownComponentOption extends DropdownBaseOption {
|
|
103
|
-
/**
|
|
104
|
-
* Custom component rendered in place of the standard menu row.
|
|
105
|
-
* @deprecated use `slots.item` for the full-row escape hatch instead
|
|
106
|
-
*/
|
|
107
|
-
component: any
|
|
108
|
-
|
|
109
|
-
/** Optional label used by custom renderers. */
|
|
110
|
-
label?: string
|
|
111
|
-
|
|
112
|
-
route?: never
|
|
113
|
-
submenu?: never
|
|
114
|
-
switch?: never
|
|
115
|
-
switchValue?: never
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
export interface DropdownGroupOption {
|
|
119
|
-
/** Stable key for the group wrapper. */
|
|
120
|
-
key?: string | number
|
|
121
|
-
|
|
122
|
-
/** Label rendered above the grouped items. */
|
|
123
|
-
group: string
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Items rendered inside the group. Optional in the type so the
|
|
127
|
-
* deprecated `items` alias still typechecks; provide one of
|
|
128
|
-
* `options` or `items`.
|
|
129
|
-
*/
|
|
130
|
-
options?: DropdownOption[]
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* @deprecated use `options`. Accepted only as a back-compat alias for
|
|
134
|
-
* Dropdown's previous `{ group, items }` shape.
|
|
135
|
-
*/
|
|
136
|
-
items?: DropdownOption[]
|
|
137
|
-
|
|
138
|
-
/** Hides the group heading while preserving grouping. */
|
|
139
|
-
hideLabel?: boolean
|
|
140
|
-
|
|
141
|
-
/** Theme inherited by items in the group. */
|
|
142
|
-
theme?: DropdownTheme
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
export type DropdownOption =
|
|
146
|
-
| DropdownActionOption
|
|
147
|
-
| DropdownSwitchOption
|
|
148
|
-
| DropdownSubmenuOption
|
|
149
|
-
| DropdownComponentOption
|
|
150
|
-
|
|
151
|
-
export type DropdownItem = DropdownOption | DropdownGroupOption
|
|
152
|
-
export type DropdownOptions = Array<DropdownItem>
|
|
153
|
-
|
|
154
26
|
export interface DropdownProps {
|
|
155
27
|
/** Button configuration (label, icon, size, variant, etc.) */
|
|
156
28
|
button?: ButtonProps
|
|
157
29
|
|
|
158
30
|
/** Array of dropdown options or grouped options. */
|
|
159
|
-
options?:
|
|
31
|
+
options?: MenuOptions
|
|
160
32
|
|
|
161
33
|
/** Controls the visibility of the dropdown. */
|
|
162
34
|
open?: boolean
|
|
@@ -176,16 +48,14 @@ export interface DropdownProps {
|
|
|
176
48
|
/** Offset in pixels between trigger and dropdown. */
|
|
177
49
|
offset?: number
|
|
178
50
|
|
|
51
|
+
/** Whether the dropdown width should match the trigger element. */
|
|
52
|
+
matchTriggerWidth?: boolean
|
|
53
|
+
|
|
179
54
|
/** Teleport target for dropdown portal content. */
|
|
180
55
|
portalTo?: string | HTMLElement
|
|
181
56
|
}
|
|
182
57
|
|
|
183
|
-
export interface
|
|
184
|
-
/** Closes the dropdown menu. */
|
|
185
|
-
close: () => void
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
export interface DropdownTriggerSlotProps extends DropdownSlotProps {
|
|
58
|
+
export interface DropdownTriggerSlotProps extends MenuSlotProps {
|
|
189
59
|
/** Whether the dropdown menu is currently open. */
|
|
190
60
|
open: boolean
|
|
191
61
|
|
|
@@ -195,45 +65,11 @@ export interface DropdownTriggerSlotProps extends DropdownSlotProps {
|
|
|
195
65
|
[key: string]: any
|
|
196
66
|
}
|
|
197
67
|
|
|
198
|
-
export
|
|
199
|
-
/** Item currently being rendered. */
|
|
200
|
-
item: DropdownOption
|
|
201
|
-
|
|
202
|
-
/** Whether the item is currently selected. */
|
|
203
|
-
selected: boolean
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
export interface DropdownGroupSlotProps {
|
|
207
|
-
/** Group currently being rendered. */
|
|
208
|
-
group: DropdownGroupOption
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
export interface DropdownSlots {
|
|
68
|
+
export type DropdownSlots = Omit<MenuSlots, 'default' | 'trigger'> & {
|
|
212
69
|
/** Alternate trigger renderer. */
|
|
213
70
|
default?: (props: DropdownTriggerSlotProps) => any
|
|
214
|
-
|
|
215
71
|
/** Explicit trigger slot renderer. */
|
|
216
72
|
trigger?: (props: DropdownTriggerSlotProps) => any
|
|
217
|
-
|
|
218
|
-
/** Replaces the entire item row. */
|
|
219
|
-
item?: (props: DropdownItemSlotProps) => any
|
|
220
|
-
|
|
221
|
-
/** Content rendered before the standard item label. */
|
|
222
|
-
'item-prefix'?: (props: DropdownItemSlotProps) => any
|
|
223
|
-
|
|
224
|
-
/** Content rendered for the standard item label area. */
|
|
225
|
-
'item-label'?: (props: DropdownItemSlotProps) => any
|
|
226
|
-
|
|
227
|
-
/** Content rendered after the standard item label. */
|
|
228
|
-
'item-suffix'?: (props: DropdownItemSlotProps) => any
|
|
229
|
-
|
|
230
|
-
/** Custom renderer for group labels. */
|
|
231
|
-
'group-label'?: (props: DropdownGroupSlotProps) => any
|
|
232
|
-
|
|
233
|
-
/** Fallback content rendered when no items are available. */
|
|
234
|
-
empty?: () => any
|
|
235
|
-
|
|
236
|
-
[slotName: string]: ((props: any) => any) | undefined
|
|
237
73
|
}
|
|
238
74
|
|
|
239
75
|
export interface DropdownEmits {
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
<!-- Auto Generated by scripts/propsgen.ts -->
|
|
2
|
+
<script setup>
|
|
3
|
+
import PropsTable from '@/components/Docs/PropsTable.vue'
|
|
4
|
+
import SlotsTable from '@/components/Docs/SlotsTable.vue'
|
|
5
|
+
import EmitsTable from '@/components/Docs/EmitsTable.vue'
|
|
6
|
+
|
|
7
|
+
const propsData = [
|
|
8
|
+
{
|
|
9
|
+
name: 'modelValue',
|
|
10
|
+
description: 'The duration value in seconds (two-way via `v-model`).',
|
|
11
|
+
required: false,
|
|
12
|
+
type: 'number | null',
|
|
13
|
+
default: 'null'
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
name: 'placeholder',
|
|
17
|
+
description: 'Placeholder shown when the input is empty. Defaults to `1h 30m 45s`.',
|
|
18
|
+
required: false,
|
|
19
|
+
type: 'string',
|
|
20
|
+
default: '"1h 30m 45s"'
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: 'format',
|
|
24
|
+
description: 'How the saved value is rendered when not focused: a named preset\n(`short` | `long` | `colon`) or a token template (e.g. `h\'h\' m\'m\' s\'s\'`,\n`hh:mm:ss`). Defaults to `short`.',
|
|
25
|
+
required: false,
|
|
26
|
+
type: 'DurationFormat',
|
|
27
|
+
default: '"short"'
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: 'size',
|
|
31
|
+
description: 'Visual size of the input. Forwarded to the underlying `TextInput`.',
|
|
32
|
+
required: false,
|
|
33
|
+
type: 'InputSize'
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: 'variant',
|
|
37
|
+
description: 'Style variant of the input. Forwarded to the underlying `TextInput`.',
|
|
38
|
+
required: false,
|
|
39
|
+
type: 'InputVariant'
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: 'disabled',
|
|
43
|
+
description: 'Disables the input when true.',
|
|
44
|
+
required: false,
|
|
45
|
+
type: 'boolean'
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: 'label',
|
|
49
|
+
description: 'Label rendered above (or beside, for binary controls) the input.',
|
|
50
|
+
required: false,
|
|
51
|
+
type: 'string'
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: 'description',
|
|
55
|
+
description: 'Helper text rendered below the input.\nHidden when `error` is set.',
|
|
56
|
+
required: false,
|
|
57
|
+
type: 'string'
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: 'error',
|
|
61
|
+
description: 'Error message rendered below the input. When set, the control receives\n`aria-invalid="true"` and `data-state="invalid"`. May be either a string\nor an `Error` object whose `messages?: string[]` is rendered as stacked\nlines (with `Error.message` as the fallback).',
|
|
62
|
+
required: false,
|
|
63
|
+
type: 'string | FrappeUIError'
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: 'required',
|
|
67
|
+
description: 'Marks the field as required. Renders an asterisk next to the label and\nforwards `required` / `aria-required` to the underlying control.',
|
|
68
|
+
required: false,
|
|
69
|
+
type: 'boolean'
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: 'id',
|
|
73
|
+
description: 'HTML id of the underlying control. Auto-generated via `useId()` if omitted.',
|
|
74
|
+
required: false,
|
|
75
|
+
type: 'string'
|
|
76
|
+
}
|
|
77
|
+
]
|
|
78
|
+
|
|
79
|
+
const emitsData = [
|
|
80
|
+
{
|
|
81
|
+
name: 'update:modelValue',
|
|
82
|
+
description: 'Fired when the model value changes.',
|
|
83
|
+
type: '[value: number | null]'
|
|
84
|
+
}
|
|
85
|
+
]
|
|
86
|
+
</script>
|
|
87
|
+
## API Reference
|
|
88
|
+
|
|
89
|
+
<PropsTable name="Duration" :data="propsData"/>
|
|
90
|
+
|
|
91
|
+
<EmitsTable :data="emitsData"/>
|
|
92
|
+
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import Duration from './Duration.vue'
|
|
2
|
+
import { formatDuration, parseDuration } from './duration'
|
|
3
|
+
|
|
4
|
+
describe('Duration', () => {
|
|
5
|
+
it('renders a text input', () => {
|
|
6
|
+
cy.mount(Duration)
|
|
7
|
+
cy.get('input').should('exist')
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
it('displays the model value formatted (short by default)', () => {
|
|
11
|
+
cy.mount(Duration, { props: { modelValue: 5445 } })
|
|
12
|
+
cy.get('input').should('have.value', '1h 30m 45s')
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
it('renders the long and colon presets', () => {
|
|
16
|
+
cy.mount(Duration, { props: { modelValue: 5445, format: 'long' } })
|
|
17
|
+
cy.get('input').should('have.value', '1 hour 30 minutes 45 seconds')
|
|
18
|
+
|
|
19
|
+
cy.mount(Duration, { props: { modelValue: 5445, format: 'colon' } })
|
|
20
|
+
cy.get('input').should('have.value', '1:30:45')
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
it('renders a custom token template format', () => {
|
|
24
|
+
cy.mount(Duration, { props: { modelValue: 7323, format: 'hh:mm:ss' } })
|
|
25
|
+
cy.get('input').should('have.value', '02:02:03')
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('edits in canonical short notation regardless of display format', () => {
|
|
29
|
+
cy.mount(Duration, { props: { modelValue: 7323, format: 'hh:mm:ss' } })
|
|
30
|
+
cy.get('input').should('have.value', '02:02:03')
|
|
31
|
+
cy.get('input').focus().should('have.value', '2h 2m 3s')
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('commits a parsed value on blur', () => {
|
|
35
|
+
cy.mount(Duration, {
|
|
36
|
+
props: { 'onUpdate:modelValue': cy.spy().as('onUpdate') },
|
|
37
|
+
})
|
|
38
|
+
cy.get('input').type('1h 30m').blur()
|
|
39
|
+
cy.get('@onUpdate').should('be.calledWith', 5400)
|
|
40
|
+
cy.get('input').should('have.value', '1h 30m')
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('commits on Enter', () => {
|
|
44
|
+
cy.mount(Duration, {
|
|
45
|
+
props: { 'onUpdate:modelValue': cy.spy().as('onUpdate') },
|
|
46
|
+
})
|
|
47
|
+
cy.get('input').type('90s{enter}')
|
|
48
|
+
cy.get('@onUpdate').should('be.calledWith', 90)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('reverts to the saved value on Escape without committing', () => {
|
|
52
|
+
cy.mount(Duration, {
|
|
53
|
+
props: {
|
|
54
|
+
modelValue: 60,
|
|
55
|
+
'onUpdate:modelValue': cy.spy().as('onUpdate'),
|
|
56
|
+
},
|
|
57
|
+
})
|
|
58
|
+
cy.get('input').focus().clear().type('5h{esc}')
|
|
59
|
+
cy.get('@onUpdate').should('not.be.called')
|
|
60
|
+
cy.get('input').should('have.value', '1m')
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it('shows an internal error for invalid input and does not commit', () => {
|
|
64
|
+
cy.mount(Duration, {
|
|
65
|
+
props: { 'onUpdate:modelValue': cy.spy().as('onUpdate') },
|
|
66
|
+
})
|
|
67
|
+
cy.get('input').type('not a duration').blur()
|
|
68
|
+
cy.get('@onUpdate').should('not.be.called')
|
|
69
|
+
cy.get('input').should('have.attr', 'aria-invalid', 'true')
|
|
70
|
+
cy.contains('Invalid format').should('exist')
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
it('releases focus after an invalid commit but keeps the rejected text visible', () => {
|
|
74
|
+
cy.mount(Duration, {
|
|
75
|
+
props: { 'onUpdate:modelValue': cy.spy().as('onUpdate') },
|
|
76
|
+
})
|
|
77
|
+
cy.get('input').as('field').type('not a duration')
|
|
78
|
+
cy.get('@field').blur()
|
|
79
|
+
// Focus is not trapped — the user can click away.
|
|
80
|
+
cy.get('@field').should('have.value', 'not a duration').and('not.have.focus')
|
|
81
|
+
// Re-focusing and correcting still commits.
|
|
82
|
+
cy.get('@field').focus().clear().type('1h 30m').blur()
|
|
83
|
+
cy.get('@onUpdate').should('be.calledWith', 5400)
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
it('a caller-provided error takes precedence over the internal one', () => {
|
|
87
|
+
cy.mount(Duration, { props: { error: 'Field is required.' } })
|
|
88
|
+
cy.contains('Field is required.').should('exist')
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('clearing the field commits null', () => {
|
|
92
|
+
cy.mount(Duration, {
|
|
93
|
+
props: {
|
|
94
|
+
modelValue: 60,
|
|
95
|
+
'onUpdate:modelValue': cy.spy().as('onUpdate'),
|
|
96
|
+
},
|
|
97
|
+
})
|
|
98
|
+
cy.get('input').focus().clear().blur()
|
|
99
|
+
cy.get('@onUpdate').should('be.calledWith', null)
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
it('exposes the shared labeling contract', () => {
|
|
103
|
+
cy.mount(Duration, {
|
|
104
|
+
props: { label: 'Time spent', description: 'Logged hours', required: true },
|
|
105
|
+
})
|
|
106
|
+
cy.get('label').should('contain.text', 'Time spent')
|
|
107
|
+
cy.contains('Logged hours').should('exist')
|
|
108
|
+
cy.get('input').should('have.attr', 'aria-required', 'true')
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
it('forwards size and variant to the underlying input', () => {
|
|
112
|
+
cy.mount(Duration, { props: { size: 'lg', variant: 'outline' } })
|
|
113
|
+
cy.get('input').should('have.attr', 'data-size', 'lg')
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
it('disables the input', () => {
|
|
117
|
+
cy.mount(Duration, { props: { disabled: true } })
|
|
118
|
+
cy.get('input').should('be.disabled')
|
|
119
|
+
})
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
describe('parseDuration', () => {
|
|
123
|
+
it('parses unit syntax in any order', () => {
|
|
124
|
+
expect(parseDuration('1h 30m 45s')).to.equal(5445)
|
|
125
|
+
expect(parseDuration('45s 1h 30m')).to.equal(5445)
|
|
126
|
+
expect(parseDuration('1h30m45s')).to.equal(5445)
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
it('parses long-form units', () => {
|
|
130
|
+
expect(parseDuration('1 hour 30 minutes')).to.equal(5400)
|
|
131
|
+
expect(parseDuration('2hrs 15min')).to.equal(8100)
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
it('parses colon syntax', () => {
|
|
135
|
+
expect(parseDuration('1:30:45')).to.equal(5445)
|
|
136
|
+
expect(parseDuration('1:30')).to.equal(90)
|
|
137
|
+
expect(parseDuration(':45')).to.equal(45)
|
|
138
|
+
// Out-of-range groups overflow by design (stopwatch-style), not rejected.
|
|
139
|
+
expect(parseDuration('1:75')).to.equal(135)
|
|
140
|
+
expect(parseDuration('5:99')).to.equal(399)
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
it('treats a bare integer as seconds', () => {
|
|
144
|
+
expect(parseDuration('90')).to.equal(90)
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
it('rejects invalid input', () => {
|
|
148
|
+
expect(parseDuration('')).to.equal(null)
|
|
149
|
+
expect(parseDuration('abc')).to.equal(null)
|
|
150
|
+
expect(parseDuration('1h foo 2m')).to.equal(null)
|
|
151
|
+
expect(parseDuration('1h 2h')).to.equal(null) // duplicate unit
|
|
152
|
+
})
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
describe('formatDuration', () => {
|
|
156
|
+
it('formats each preset', () => {
|
|
157
|
+
expect(formatDuration(5445)).to.equal('1h 30m 45s')
|
|
158
|
+
expect(formatDuration(5445, 'long')).to.equal('1 hour 30 minutes 45 seconds')
|
|
159
|
+
expect(formatDuration(5445, 'colon')).to.equal('1:30:45')
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
it('the short preset omits zero parts', () => {
|
|
163
|
+
expect(formatDuration(90)).to.equal('1m 30s')
|
|
164
|
+
expect(formatDuration(3600)).to.equal('1h')
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
it('singularizes long-form units', () => {
|
|
168
|
+
expect(formatDuration(3661, 'long')).to.equal('1 hour 1 minute 1 second')
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
it('renders custom token templates literally', () => {
|
|
172
|
+
expect(formatDuration(5445, "h'h' m'm' s's'")).to.equal('1h 30m 45s')
|
|
173
|
+
expect(formatDuration(90, "h'h' m'm' s's'")).to.equal('0h 1m 30s')
|
|
174
|
+
expect(formatDuration(7323, 'hh:mm:ss')).to.equal('02:02:03')
|
|
175
|
+
expect(formatDuration(5445, "h 'hours'")).to.equal('1 hours')
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
it('renders zero and nullish values', () => {
|
|
179
|
+
expect(formatDuration(0)).to.equal('0s')
|
|
180
|
+
expect(formatDuration(null)).to.equal('')
|
|
181
|
+
expect(formatDuration(undefined)).to.equal('')
|
|
182
|
+
})
|
|
183
|
+
})
|