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,173 @@
|
|
|
1
|
+
/* Structural rules for the list family. Geometry only — colors and type live
|
|
2
|
+
* in the components as semantic-token classes.
|
|
3
|
+
*
|
|
4
|
+
* `--list-columns` and `--list-gap` are public styling hooks: apps override
|
|
5
|
+
* them with plain (responsive) classes on <List>. Every rule that sets a
|
|
6
|
+
* hook default is wrapped in :where() so any consumer class wins on
|
|
7
|
+
* specificity, regardless of stylesheet order. */
|
|
8
|
+
|
|
9
|
+
:where([data-slot='list']) {
|
|
10
|
+
/* --list-columns-default carries the `columns` prop (inline style on the
|
|
11
|
+
* List). The indirection keeps --list-columns itself free for class
|
|
12
|
+
* overrides like `max-sm:list-cols-[auto_minmax(0,1fr)_auto]` (the
|
|
13
|
+
* tailwind preset's sugar over the var). */
|
|
14
|
+
--list-columns: var(--list-columns-default, auto minmax(0, 1fr) auto);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/* Attribute (not :where) specificity, so element-level resets — Tailwind
|
|
18
|
+
* preflight's `button { padding: 0 }` for button rows — don't beat the
|
|
19
|
+
* padding below. `text-align: start` undoes the UA button centering. */
|
|
20
|
+
[data-slot='list-row'],
|
|
21
|
+
[data-slot='list-header'] {
|
|
22
|
+
display: grid;
|
|
23
|
+
grid-template-columns: var(--list-columns);
|
|
24
|
+
column-gap: var(--list-gap, 0.5rem);
|
|
25
|
+
align-items: center;
|
|
26
|
+
text-align: start;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/* The checkbox column is not a grid track (a 0px track would still get a
|
|
30
|
+
* column gap next to it). It's start padding — 0px or 32px, set by the
|
|
31
|
+
* List — so grid line numbers never shift and the reveal is one animated
|
|
32
|
+
* padding. The checkbox itself is an overlay in the padding area. */
|
|
33
|
+
[data-slot='list-row'] {
|
|
34
|
+
padding-inline-start: calc(
|
|
35
|
+
var(--list-checkbox-width, 0px) + var(--_list-row-pad, 0px)
|
|
36
|
+
);
|
|
37
|
+
padding-inline-end: var(--_list-row-pad, 0px);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* The header inset comes from the *public* --list-row-padding-x hook (not the
|
|
41
|
+
* private --_list-row-pad, which is only ever set on interactive rows and so
|
|
42
|
+
* left the header flush-left beside padded rows). Defaults to 0 — the neutral
|
|
43
|
+
* flush edge — because a header can't tell whether its rows are interactive
|
|
44
|
+
* (0.75rem inset) or static (flush): the consumer declares the inset once on
|
|
45
|
+
* the List, e.g. `list-row-px-3`, and it flows to both header and rows so they
|
|
46
|
+
* can't drift. In :where() (zero specificity) so that class wins — and unlike a
|
|
47
|
+
* raw `px-3`, the hook composes with the checkbox column instead of clobbering
|
|
48
|
+
* the padding-inline-start calc below. */
|
|
49
|
+
:where([data-slot='list-header']) {
|
|
50
|
+
padding-inline-start: calc(
|
|
51
|
+
var(--list-checkbox-width, 0px) + var(--list-row-padding-x, 0px)
|
|
52
|
+
);
|
|
53
|
+
padding-inline-end: var(--list-row-padding-x, 0px);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/* Group header aligns its label with the rows' content: same checkbox-column +
|
|
57
|
+
* row inset as the header/rows, so a section title sits directly above the text
|
|
58
|
+
* it groups. In :where() so consumer classes win. */
|
|
59
|
+
:where([data-slot='list-group-header']) {
|
|
60
|
+
padding-inline-start: calc(
|
|
61
|
+
var(--list-checkbox-width, 0px) + var(--list-row-padding-x, 0px)
|
|
62
|
+
);
|
|
63
|
+
padding-inline-end: var(--list-row-padding-x, 0px);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/* Positioned so dividers/checkbox overlays anchor to the row. In :where()
|
|
67
|
+
* (unlike the block above, which needs attribute specificity only to beat
|
|
68
|
+
* preflight's button resets) so a consumer `sticky` class wins — e.g. a
|
|
69
|
+
* header pinned inside its scroll container. */
|
|
70
|
+
:where([data-slot='list-row'], [data-slot='list-header']) {
|
|
71
|
+
position: relative;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
:where([data-slot='list-row']) {
|
|
75
|
+
height: var(--list-row-height, auto);
|
|
76
|
+
/* padding animates the checkbox-column reveal; background-color fades the
|
|
77
|
+
* hover surface (a transition-colors class here would clobber the padding
|
|
78
|
+
* transition, so both live in one declaration). */
|
|
79
|
+
transition:
|
|
80
|
+
padding 200ms ease-out,
|
|
81
|
+
background-color 150ms;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/* Interactive rows get the rounded hover surface, so their content needs an
|
|
85
|
+
* inset from the surface edge. Override via --list-row-padding-x. Width lives
|
|
86
|
+
* here (not as a class) so a consumer class can narrow a row — e.g. to leave
|
|
87
|
+
* room for an action button beside it. */
|
|
88
|
+
:where([data-slot='list-row'][data-interactive]) {
|
|
89
|
+
--_list-row-pad: var(--list-row-padding-x, 0.75rem);
|
|
90
|
+
width: 100%;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
:where([data-slot='list-row-checkbox'], [data-slot='list-header-checkbox']) {
|
|
94
|
+
position: absolute;
|
|
95
|
+
top: 0;
|
|
96
|
+
bottom: 0;
|
|
97
|
+
/* Row checkboxes sit inside the interactive row's hover inset; the header
|
|
98
|
+
* has no such inset, so it anchors to its own content padding instead. Both
|
|
99
|
+
* land the 32px checkbox column in the same place so they align vertically. */
|
|
100
|
+
inset-inline-start: var(--_list-row-pad, 0px);
|
|
101
|
+
display: flex;
|
|
102
|
+
align-items: center;
|
|
103
|
+
width: 32px;
|
|
104
|
+
overflow: hidden;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
:where([data-slot='list-header-checkbox']) {
|
|
108
|
+
inset-inline-start: var(--list-row-padding-x, 0px);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/* The header's bottom border is a grid child spanning all tracks, so it
|
|
112
|
+
* aligns with the rows' content (and their full-width dividers) whatever
|
|
113
|
+
* padding the header carries — a border on the header box itself would run
|
|
114
|
+
* through the padding and overshoot the dividers on both sides. */
|
|
115
|
+
:where([data-slot='list-header-border']) {
|
|
116
|
+
position: absolute;
|
|
117
|
+
/* grid-row stays auto so bottom: 0 is the header's own bottom edge (see
|
|
118
|
+
* the list-divider rule below), while grid-column tracks the columns. */
|
|
119
|
+
grid-column: 1 / -1;
|
|
120
|
+
bottom: 0;
|
|
121
|
+
inset-inline-start: 0;
|
|
122
|
+
inset-inline-end: 0;
|
|
123
|
+
height: 0;
|
|
124
|
+
pointer-events: none;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/* Dividers are absolutely-positioned grid children: their grid-column
|
|
128
|
+
* placement (inset `2 / -1`, full `1 / -1` — set by ListRow) resolves
|
|
129
|
+
* against the shared template, so the inset divider starts at the content
|
|
130
|
+
* column's own edge with no magic offsets, without occupying cells that
|
|
131
|
+
* would derail the cells' auto-placement. */
|
|
132
|
+
:where([data-slot='list-divider']) {
|
|
133
|
+
position: absolute;
|
|
134
|
+
/* grid-row stays auto: per spec an abs-positioned grid child with auto
|
|
135
|
+
* placement in an axis anchors to the padding box in that axis, so top: 0
|
|
136
|
+
* is the row's own top edge. Anchoring to grid-row 1 would put the divider
|
|
137
|
+
* below the row's padding-top, displacing it into padded rows. */
|
|
138
|
+
top: 0;
|
|
139
|
+
inset-inline-start: 0;
|
|
140
|
+
inset-inline-end: 0;
|
|
141
|
+
height: 0;
|
|
142
|
+
pointer-events: none;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/* Between-ness is pure CSS: only a row following another row shows its
|
|
146
|
+
* divider-above. The first row never shows one, trailing non-row content
|
|
147
|
+
* (e.g. a "Load more" button) doesn't break it, and it keeps working inside
|
|
148
|
+
* the virtualizer wrapper because windowed rows stay siblings. */
|
|
149
|
+
[data-slot='list-row'] [data-slot='list-divider'] {
|
|
150
|
+
opacity: 0;
|
|
151
|
+
}
|
|
152
|
+
[data-slot='list-row'] + [data-slot='list-row'] [data-slot='list-divider'] {
|
|
153
|
+
opacity: 1;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/* Hover hides both edges of the hovered row so the rounded hover surface
|
|
157
|
+
* floats free. Gated on data-interactive so static rows keep dividers. */
|
|
158
|
+
[data-slot='list-row'][data-interactive]:hover [data-slot='list-divider'],
|
|
159
|
+
[data-slot='list-row'][data-interactive]:hover
|
|
160
|
+
+ [data-slot='list-row']
|
|
161
|
+
[data-slot='list-divider'] {
|
|
162
|
+
opacity: 0;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/* The active row (v-model:active) is a persistent version of the same idea:
|
|
166
|
+
* its rounded highlight surface floats free of the rules above and below it.
|
|
167
|
+
* Hides its own divider-above and the next row's (the divider below it). */
|
|
168
|
+
[data-slot='list-row'][data-active] [data-slot='list-divider'],
|
|
169
|
+
[data-slot='list-row'][data-active]
|
|
170
|
+
+ [data-slot='list-row']
|
|
171
|
+
[data-slot='list-divider'] {
|
|
172
|
+
opacity: 0;
|
|
173
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { RouteLocationRaw } from 'vue-router'
|
|
2
|
+
|
|
3
|
+
export type ListDivider = 'inset' | 'full' | 'none'
|
|
4
|
+
export type ListSortDirection = 'asc' | 'desc'
|
|
5
|
+
|
|
6
|
+
export interface ListProps {
|
|
7
|
+
/**
|
|
8
|
+
* Grid track sizes, written to the `--list-columns` CSS var shared by the
|
|
9
|
+
* header and every row. Defaults to the feed template
|
|
10
|
+
* `['auto', 'minmax(0,1fr)', 'auto']` (leading media, content, trailing).
|
|
11
|
+
* Table-style lists must pass deterministic track sizes — `auto` tracks
|
|
12
|
+
* size independently per row.
|
|
13
|
+
*/
|
|
14
|
+
columns?: string[]
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Divider treatment between rows: `inset` starts at the content column
|
|
18
|
+
* (the text edge), `full` spans all columns. Defaults to `inset` with the
|
|
19
|
+
* default feed template, `full` when `columns` is set.
|
|
20
|
+
*/
|
|
21
|
+
divider?: ListDivider
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Reveals the animated checkbox column and switches row click from
|
|
25
|
+
* navigate to toggle. Selected values surface via `v-model:selection`.
|
|
26
|
+
*/
|
|
27
|
+
selectable?: boolean
|
|
28
|
+
|
|
29
|
+
// Two more models live on List but aren't plain props (so they're not in
|
|
30
|
+
// this interface): `v-model:selection` (string[], the checkbox set) and
|
|
31
|
+
// `v-model:active` (string, the single open/highlighted row — the List
|
|
32
|
+
// styles it and hides the dividers hugging it). See List.vue.
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Fixed row height in px (sets `--list-row-height`). Required for
|
|
36
|
+
* virtualization; without it rows size to their content. Responsive
|
|
37
|
+
* heights are non-virtual — set them with classes on the rows instead.
|
|
38
|
+
*/
|
|
39
|
+
rowHeight?: number
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface ListRowProps {
|
|
43
|
+
/**
|
|
44
|
+
* Renders the row as a RouterLink. Without `to`, a row with a click
|
|
45
|
+
* listener renders as a button; otherwise a plain div.
|
|
46
|
+
*/
|
|
47
|
+
to?: RouteLocationRaw
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Row key — the `selection` key when `selectable` and the `v-model:active`
|
|
51
|
+
* key. Required whenever the list uses either.
|
|
52
|
+
*/
|
|
53
|
+
value?: string
|
|
54
|
+
|
|
55
|
+
/** Fired when the row is activated, unless selection mode claims the click. */
|
|
56
|
+
onClick?: (event: MouseEvent) => void
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface ListHeaderCellSortProps {
|
|
60
|
+
/**
|
|
61
|
+
* Active sort direction for this column, `null`/omitted when inactive.
|
|
62
|
+
* The cell is controlled — sort state and toggle rules are app-owned:
|
|
63
|
+
* update whatever drives your ordering in the `click` handler.
|
|
64
|
+
*/
|
|
65
|
+
direction?: ListSortDirection | null
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Horizontal alignment of the header content. `'end'` right-aligns the cell
|
|
69
|
+
* (for numeric/right-aligned columns) *and* moves the sort glyph to the
|
|
70
|
+
* leading side, so the label stays flush with the column's right edge and
|
|
71
|
+
* lines up with the values below. Defaults to `'start'`.
|
|
72
|
+
*/
|
|
73
|
+
align?: 'start' | 'end'
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface ListVirtualOptions {
|
|
77
|
+
/** Row height in px. Defaults to the List's `rowHeight`. */
|
|
78
|
+
itemHeight?: number
|
|
79
|
+
|
|
80
|
+
/** Rows rendered beyond the visible window on each side. */
|
|
81
|
+
overscan?: number
|
|
82
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import {
|
|
2
|
+
computed,
|
|
3
|
+
ref,
|
|
4
|
+
toValue,
|
|
5
|
+
watchEffect,
|
|
6
|
+
type MaybeRefOrGetter,
|
|
7
|
+
type Ref,
|
|
8
|
+
} from 'vue'
|
|
9
|
+
import { useEventListener, useVirtualList } from '@vueuse/core'
|
|
10
|
+
|
|
11
|
+
export interface UseVirtualRowsOptions {
|
|
12
|
+
/** Enables DOM scroll-container lookup and scroll listener registration. */
|
|
13
|
+
enabled?: MaybeRefOrGetter<boolean>
|
|
14
|
+
|
|
15
|
+
/** Row height in px. */
|
|
16
|
+
itemHeight: MaybeRefOrGetter<number>
|
|
17
|
+
|
|
18
|
+
/** Rows rendered beyond the visible window on each side. */
|
|
19
|
+
overscan?: number
|
|
20
|
+
|
|
21
|
+
/** Explicit scroll container. Defaults to the nearest scrollable ancestor of `anchor`. */
|
|
22
|
+
scrollContainer?: MaybeRefOrGetter<HTMLElement | null | undefined>
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Windowing for list rows whose scroll container is an ancestor the app owns
|
|
27
|
+
* (a settings body, the page itself) rather than an element the virtualizer
|
|
28
|
+
* renders. Wraps vueuse's useVirtualList: points its container ref at the
|
|
29
|
+
* discovered ancestor and forwards that element's scroll events, so the
|
|
30
|
+
* app keeps its own scroll container and styled scrollbar.
|
|
31
|
+
*
|
|
32
|
+
* Bind `anchor` to the element wrapping the windowed rows (`wrapperProps`
|
|
33
|
+
* carries the height/offset styles vueuse computes for it).
|
|
34
|
+
*/
|
|
35
|
+
export function useVirtualRows<T>(
|
|
36
|
+
items: MaybeRefOrGetter<T[]>,
|
|
37
|
+
options: UseVirtualRowsOptions,
|
|
38
|
+
) {
|
|
39
|
+
const source = computed(() => toValue(items))
|
|
40
|
+
const { list, containerProps, wrapperProps } = useVirtualList(
|
|
41
|
+
source as Ref<T[]>,
|
|
42
|
+
{
|
|
43
|
+
itemHeight: () => toValue(options.itemHeight),
|
|
44
|
+
overscan: options.overscan ?? 6,
|
|
45
|
+
},
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
const anchor = ref<HTMLElement | null>(null)
|
|
49
|
+
watchEffect(() => {
|
|
50
|
+
if (!toValue(options.enabled ?? true)) {
|
|
51
|
+
containerProps.ref.value = null
|
|
52
|
+
return
|
|
53
|
+
}
|
|
54
|
+
const explicit = toValue(options.scrollContainer)
|
|
55
|
+
containerProps.ref.value = explicit ?? findScrollContainer(anchor.value)
|
|
56
|
+
})
|
|
57
|
+
useEventListener(
|
|
58
|
+
() => (toValue(options.enabled ?? true) ? containerProps.ref.value : null),
|
|
59
|
+
'scroll',
|
|
60
|
+
() => containerProps.onScroll(),
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
return { rows: list, wrapperProps, anchor }
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function findScrollContainer(el: HTMLElement | null): HTMLElement | null {
|
|
67
|
+
let node = el?.parentElement ?? null
|
|
68
|
+
while (node) {
|
|
69
|
+
const { overflowY } = getComputedStyle(node)
|
|
70
|
+
if (
|
|
71
|
+
overflowY === 'auto' ||
|
|
72
|
+
overflowY === 'scroll' ||
|
|
73
|
+
overflowY === 'overlay'
|
|
74
|
+
) {
|
|
75
|
+
return node
|
|
76
|
+
}
|
|
77
|
+
node = node.parentElement
|
|
78
|
+
}
|
|
79
|
+
return null
|
|
80
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Resource, ResourceOptions } from './resources'
|
|
2
|
+
|
|
3
|
+
export interface DocumentResource<TDoc = any> {
|
|
4
|
+
doctype: string
|
|
5
|
+
name: string
|
|
6
|
+
doc: TDoc | null
|
|
7
|
+
originalDoc: TDoc | null
|
|
8
|
+
isDirty: boolean
|
|
9
|
+
auto: boolean
|
|
10
|
+
get: Resource<TDoc>
|
|
11
|
+
setValue: Resource<TDoc>
|
|
12
|
+
setValueDebounced: Resource<TDoc>
|
|
13
|
+
save: Resource<TDoc>
|
|
14
|
+
delete: Resource<TDoc>
|
|
15
|
+
reload: () => Promise<TDoc | null>
|
|
16
|
+
setDoc: (doc: TDoc | ((current: TDoc | null) => TDoc)) => void
|
|
17
|
+
[key: string]: any
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function createDocumentResource<TDoc = any>(
|
|
21
|
+
options: ResourceOptions & { doctype: string; name: string },
|
|
22
|
+
vm?: any,
|
|
23
|
+
): DocumentResource<TDoc>
|
|
24
|
+
|
|
25
|
+
export function getCachedDocumentResource<TDoc = any>(
|
|
26
|
+
doctype: string,
|
|
27
|
+
name: string,
|
|
28
|
+
): DocumentResource<TDoc> | null
|
|
@@ -108,13 +108,10 @@ export function createDocumentResource(options, vm) {
|
|
|
108
108
|
{
|
|
109
109
|
...setValueOptions,
|
|
110
110
|
makeParams() {
|
|
111
|
-
let values = JSON.parse(JSON.stringify(out.doc))
|
|
112
|
-
delete values.doctype
|
|
113
|
-
delete values.name
|
|
114
111
|
return {
|
|
115
112
|
doctype: out.doctype,
|
|
116
113
|
name: out.name,
|
|
117
|
-
fieldname:
|
|
114
|
+
fieldname: getChangedFields(),
|
|
118
115
|
}
|
|
119
116
|
},
|
|
120
117
|
},
|
|
@@ -143,6 +140,18 @@ export function createDocumentResource(options, vm) {
|
|
|
143
140
|
setDoc,
|
|
144
141
|
})
|
|
145
142
|
|
|
143
|
+
// skip the network round-trip when save is called with no pending changes
|
|
144
|
+
const saveFetch = out.save.fetch
|
|
145
|
+
function saveIfChanged(...args) {
|
|
146
|
+
if (Object.keys(getChangedFields()).length === 0) {
|
|
147
|
+
return Promise.resolve(out.doc)
|
|
148
|
+
}
|
|
149
|
+
return saveFetch(...args)
|
|
150
|
+
}
|
|
151
|
+
out.save.fetch = saveIfChanged
|
|
152
|
+
out.save.reload = saveIfChanged
|
|
153
|
+
out.save.submit = saveIfChanged
|
|
154
|
+
|
|
146
155
|
// keep track of isDirty as doc changes, use effectScope to handle isDirty state reactivity for cached data
|
|
147
156
|
const scope = effectScope(true)
|
|
148
157
|
scope.run(() => {
|
|
@@ -217,6 +226,25 @@ export function createDocumentResource(options, vm) {
|
|
|
217
226
|
)
|
|
218
227
|
}
|
|
219
228
|
|
|
229
|
+
// diff out.doc against the loaded doc and return only the changed fields, so
|
|
230
|
+
// read-only standard fields (owner, creation, modified, docstatus, idx, ...)
|
|
231
|
+
// are not included in the set_value payload, which the server rejects with
|
|
232
|
+
// "Cannot edit standard fields"
|
|
233
|
+
function getChangedFields() {
|
|
234
|
+
let doc = JSON.parse(JSON.stringify(out.doc || {}))
|
|
235
|
+
let originalDoc = out.originalDoc || {}
|
|
236
|
+
let values = {}
|
|
237
|
+
for (let key in doc) {
|
|
238
|
+
if (JSON.stringify(doc[key]) !== JSON.stringify(originalDoc[key])) {
|
|
239
|
+
values[key] = doc[key]
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
// never send identity/meta fields even if they appear changed
|
|
243
|
+
delete values.doctype
|
|
244
|
+
delete values.name
|
|
245
|
+
return values
|
|
246
|
+
}
|
|
247
|
+
|
|
220
248
|
function reload() {
|
|
221
249
|
return out.get.fetch()
|
|
222
250
|
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @vitest-environment node
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { nextTick } from 'vue'
|
|
6
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
|
|
7
|
+
import { createDocumentResource } from './documentResource'
|
|
8
|
+
import { setConfig } from '../utils/config'
|
|
9
|
+
|
|
10
|
+
// A doc as returned by frappe.client.get — includes read-only standard fields
|
|
11
|
+
const STANDARD_FIELDS = {
|
|
12
|
+
owner: 'Administrator',
|
|
13
|
+
creation: '2026-06-26 17:25:10',
|
|
14
|
+
modified: '2026-06-26 17:25:10',
|
|
15
|
+
modified_by: 'Administrator',
|
|
16
|
+
docstatus: 0,
|
|
17
|
+
idx: 0,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function makeServerDoc(doctype: string, name: string, overrides = {}) {
|
|
21
|
+
return {
|
|
22
|
+
doctype,
|
|
23
|
+
name,
|
|
24
|
+
...STANDARD_FIELDS,
|
|
25
|
+
status: 'Open',
|
|
26
|
+
title: 'Original title',
|
|
27
|
+
...overrides,
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
describe('documentResource save', () => {
|
|
32
|
+
// captures the params passed to frappe.client.set_value by the save resource
|
|
33
|
+
let capturedSetValueParams: any = null
|
|
34
|
+
|
|
35
|
+
beforeEach(() => {
|
|
36
|
+
capturedSetValueParams = null
|
|
37
|
+
setConfig('resourceFetcher', async (options: any) => {
|
|
38
|
+
if (options.url === 'frappe.client.set_value') {
|
|
39
|
+
capturedSetValueParams = options.params
|
|
40
|
+
// server echoes back the full doc on update
|
|
41
|
+
return makeServerDoc(
|
|
42
|
+
options.params.doctype,
|
|
43
|
+
options.params.name,
|
|
44
|
+
options.params.fieldname,
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
if (options.url === 'frappe.client.get') {
|
|
48
|
+
return makeServerDoc(options.params.doctype, options.params.name)
|
|
49
|
+
}
|
|
50
|
+
throw new Error(`unexpected request to ${options.url}`)
|
|
51
|
+
})
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
afterEach(() => {
|
|
55
|
+
setConfig('resourceFetcher', undefined)
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it('sends only changed fields and strips standard fields', async () => {
|
|
59
|
+
const doc = createDocumentResource(
|
|
60
|
+
{ doctype: 'CRM Deal', name: 'DEAL-0001', auto: false },
|
|
61
|
+
{},
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
await doc.get.fetch()
|
|
65
|
+
expect(doc.doc.owner).toBe('Administrator') // standard fields are present locally
|
|
66
|
+
|
|
67
|
+
doc.doc.status = 'Proposal/Quotation'
|
|
68
|
+
await nextTick()
|
|
69
|
+
|
|
70
|
+
await doc.save.submit()
|
|
71
|
+
|
|
72
|
+
const fieldname = capturedSetValueParams.fieldname
|
|
73
|
+
// only the changed field is sent
|
|
74
|
+
expect(fieldname).toEqual({ status: 'Proposal/Quotation' })
|
|
75
|
+
// none of the standard fields leak into the payload
|
|
76
|
+
for (const key of Object.keys(STANDARD_FIELDS)) {
|
|
77
|
+
expect(fieldname).not.toHaveProperty(key)
|
|
78
|
+
}
|
|
79
|
+
// identity fields are not part of fieldname
|
|
80
|
+
expect(fieldname).not.toHaveProperty('doctype')
|
|
81
|
+
expect(fieldname).not.toHaveProperty('name')
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
it('does not send a request when nothing changed', async () => {
|
|
85
|
+
const doc = createDocumentResource(
|
|
86
|
+
{ doctype: 'CRM Deal', name: 'DEAL-0002', auto: false },
|
|
87
|
+
{},
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
await doc.get.fetch()
|
|
91
|
+
await doc.save.submit()
|
|
92
|
+
|
|
93
|
+
// save short-circuits with no pending changes, so set_value is never hit
|
|
94
|
+
expect(capturedSetValueParams).toBe(null)
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
it('sends only the changed subset when multiple fields exist', async () => {
|
|
98
|
+
const doc = createDocumentResource(
|
|
99
|
+
{ doctype: 'CRM Deal', name: 'DEAL-0003', auto: false },
|
|
100
|
+
{},
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
await doc.get.fetch()
|
|
104
|
+
doc.doc.title = 'Updated title'
|
|
105
|
+
await nextTick()
|
|
106
|
+
|
|
107
|
+
await doc.save.submit()
|
|
108
|
+
|
|
109
|
+
expect(capturedSetValueParams.fieldname).toEqual({ title: 'Updated title' })
|
|
110
|
+
})
|
|
111
|
+
})
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Resource, ResourceOptions } from './resources'
|
|
2
|
+
|
|
3
|
+
export interface ListResource<TRow = any> {
|
|
4
|
+
doctype: string
|
|
5
|
+
data: TRow[] | null
|
|
6
|
+
originalData: TRow[] | null
|
|
7
|
+
dataMap: Record<string, TRow>
|
|
8
|
+
list: Resource<TRow[]>
|
|
9
|
+
fetchOne: Resource<TRow[]>
|
|
10
|
+
insert: Resource<TRow>
|
|
11
|
+
setValue: Resource<TRow>
|
|
12
|
+
delete: Resource
|
|
13
|
+
runDocMethod: Resource
|
|
14
|
+
update: (options: ResourceOptions) => void
|
|
15
|
+
fetch: () => void
|
|
16
|
+
reload: () => Promise<TRow[] | null>
|
|
17
|
+
setData: (data: TRow[] | ((current: TRow[] | null) => TRow[])) => void
|
|
18
|
+
getRow: (name: string | number) => TRow | undefined
|
|
19
|
+
[key: string]: any
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function createListResource<TRow = any>(
|
|
23
|
+
options: ResourceOptions,
|
|
24
|
+
vm?: any,
|
|
25
|
+
): ListResource<TRow>
|
|
26
|
+
|
|
27
|
+
export function getCachedListResource<TRow = any>(
|
|
28
|
+
cacheKey: unknown,
|
|
29
|
+
): ListResource<TRow> | null
|
|
30
|
+
|
|
31
|
+
export function updateRowInListResource(doctype: string, doc: any): void
|
|
32
|
+
export function deleteRowInListResource(doctype: string, docname: string): void
|
|
33
|
+
export function revertRowInListResource(doctype: string, doc: any): void
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export interface ResourceOptions {
|
|
2
|
+
[key: string]: any
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export interface Resource<TData = any> {
|
|
6
|
+
method?: string
|
|
7
|
+
url?: string
|
|
8
|
+
data: TData | null
|
|
9
|
+
previousData: TData | null
|
|
10
|
+
loading: boolean
|
|
11
|
+
fetched: boolean
|
|
12
|
+
error: unknown
|
|
13
|
+
promise: Promise<TData> | null
|
|
14
|
+
auto?: boolean
|
|
15
|
+
params: unknown
|
|
16
|
+
fetch: (...args: any[]) => Promise<TData | null>
|
|
17
|
+
reload: (...args: any[]) => Promise<TData | null>
|
|
18
|
+
submit: (...args: any[]) => Promise<TData | null>
|
|
19
|
+
abort: () => void
|
|
20
|
+
reset: () => void
|
|
21
|
+
update: (options: ResourceOptions) => void
|
|
22
|
+
setData: (data: TData | ((current: TData | null) => TData)) => void
|
|
23
|
+
[key: string]: any
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function createResource<TData = any>(
|
|
27
|
+
options: string | ResourceOptions,
|
|
28
|
+
vm?: any,
|
|
29
|
+
): Resource<TData>
|
|
30
|
+
|
|
31
|
+
export function getCacheKey(cacheKey: unknown): string | null
|
|
32
|
+
|
|
33
|
+
export function getCachedResource<TData = any>(
|
|
34
|
+
cacheKey: unknown,
|
|
35
|
+
): Resource<TData> | null
|