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
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Aspect-ratio-locked resize-drag for media / embed node views.
|
|
3
3
|
*
|
|
4
4
|
* Behavior preserved from `MediaNodeView.vue` + `IframeNodeView.vue`:
|
|
5
|
-
* - `startResize` (on the handle's `
|
|
5
|
+
* - `startResize` (on the handle's `pointerdown`) records the start X and the
|
|
6
6
|
* element's current `offsetWidth`, locks the aspect ratio, and registers
|
|
7
7
|
* `mousemove` / `mouseup` listeners on `window` plus an `ew-resize` body cursor.
|
|
8
8
|
* - `mousemove` applies temporary inline `width`/`height` to the element for
|
|
@@ -29,22 +29,46 @@ export interface ResizeArgs {
|
|
|
29
29
|
* Optional — omit when there is no clipping wrapper.
|
|
30
30
|
*/
|
|
31
31
|
containerEl?: () => HTMLElement | null
|
|
32
|
-
/**
|
|
32
|
+
/**
|
|
33
|
+
* Fallback aspect ratio (height / width) used only when the element has no
|
|
34
|
+
* measurable rendered size at drag start. The drag normally locks the ratio
|
|
35
|
+
* from the element's live `offsetHeight / offsetWidth` so the shape on screen
|
|
36
|
+
* is preserved exactly — `getAspectRatio` can disagree with what's painted
|
|
37
|
+
* when a node stores only one dimension (the other coming from CSS
|
|
38
|
+
* `height: auto`), which used to distort the media mid-drag.
|
|
39
|
+
*/
|
|
33
40
|
getAspectRatio: () => number
|
|
34
41
|
/** TipTap v3 node-view `getPos`; the commit is skipped when it is invalid. */
|
|
35
42
|
getPos: () => number | undefined
|
|
36
43
|
/** Persist the committed size (e.g. via `updateAttributes`). */
|
|
37
44
|
onCommit: (size: { width: number; height: number }) => void
|
|
45
|
+
/**
|
|
46
|
+
* How the node view renders the media element's COMMITTED size:
|
|
47
|
+
* - 'attribute' (default): `width=`/`height=` attributes (img / video). The
|
|
48
|
+
* drag's inline styles are cleared after commit — they'd otherwise mask
|
|
49
|
+
* future attribute updates (keyboard resize, collab).
|
|
50
|
+
* - 'style': a Vue `:style` binding (e.g. the iframe's frameStyle). The
|
|
51
|
+
* drag's inline styles are LEFT IN PLACE: they already equal the committed
|
|
52
|
+
* size, and clearing them would wipe the binding's write (Vue only
|
|
53
|
+
* re-writes a style property when its bound VALUE changes).
|
|
54
|
+
*/
|
|
55
|
+
mediaSizing?: 'attribute' | 'style'
|
|
38
56
|
/** Minimum width in px. Default 50 (matches the image node view). */
|
|
39
57
|
minWidth?: number
|
|
40
58
|
/** Reserved px subtracted from the editor width as the max. Default 0. */
|
|
41
59
|
maxWidthPadding?: number
|
|
42
60
|
}
|
|
43
61
|
|
|
62
|
+
/** Which edge the drag started from; a left-edge drag inverts the delta. */
|
|
63
|
+
export type ResizeEdge = 'left' | 'right'
|
|
64
|
+
|
|
44
65
|
export function useNodeViewResize(
|
|
45
66
|
editor: Editor,
|
|
46
67
|
args: ResizeArgs,
|
|
47
|
-
): {
|
|
68
|
+
): {
|
|
69
|
+
isResizing: Ref<boolean>
|
|
70
|
+
startResize: (event: PointerEvent | MouseEvent, edge?: ResizeEdge) => void
|
|
71
|
+
} {
|
|
48
72
|
const isResizing = ref(false)
|
|
49
73
|
const minWidth = args.minWidth ?? 50
|
|
50
74
|
const maxWidthPadding = args.maxWidthPadding ?? 0
|
|
@@ -52,8 +76,12 @@ export function useNodeViewResize(
|
|
|
52
76
|
let startDragX = 0
|
|
53
77
|
let startWidth = 0
|
|
54
78
|
let aspectRatio = 1
|
|
79
|
+
let dragDirection = 1
|
|
55
80
|
|
|
56
|
-
function startResize(
|
|
81
|
+
function startResize(
|
|
82
|
+
event: PointerEvent | MouseEvent,
|
|
83
|
+
edge: ResizeEdge = 'right',
|
|
84
|
+
): void {
|
|
57
85
|
if (!editor.isEditable) return
|
|
58
86
|
const el = args.mediaEl()
|
|
59
87
|
if (!el) return
|
|
@@ -61,20 +89,31 @@ export function useNodeViewResize(
|
|
|
61
89
|
isResizing.value = true
|
|
62
90
|
startDragX = event.clientX
|
|
63
91
|
startWidth = el.offsetWidth
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
92
|
+
// Lock the ratio to the element's painted box so the drag preserves the
|
|
93
|
+
// exact shape on screen. Falls back to the supplied aspect only when the
|
|
94
|
+
// element isn't laid out yet (offsetWidth 0). Reading the rendered size
|
|
95
|
+
// avoids the stored-attrs mismatch (e.g. a node with width but no height,
|
|
96
|
+
// whose true height comes from CSS `height: auto`) that distorted media
|
|
97
|
+
// mid-drag — it became "wide" while dragging, then snapped back on release.
|
|
98
|
+
const renderedAspect = el.offsetWidth ? el.offsetHeight / el.offsetWidth : 0
|
|
99
|
+
aspectRatio = renderedAspect || args.getAspectRatio() || 1
|
|
100
|
+
// Dragging the LEFT handle outward moves the pointer left (negative
|
|
101
|
+
// clientX delta) but should grow the node — invert the delta.
|
|
102
|
+
dragDirection = edge === 'left' ? -1 : 1
|
|
103
|
+
|
|
104
|
+
window.addEventListener('pointermove', handleResize)
|
|
105
|
+
window.addEventListener('pointerup', stopResize)
|
|
106
|
+
window.addEventListener('pointercancel', stopResize)
|
|
68
107
|
document.body.style.cursor = 'ew-resize'
|
|
69
108
|
}
|
|
70
109
|
|
|
71
|
-
function handleResize(event:
|
|
110
|
+
function handleResize(event: PointerEvent): void {
|
|
72
111
|
if (!isResizing.value) return
|
|
73
112
|
const el = args.mediaEl()
|
|
74
113
|
if (!el) return
|
|
75
114
|
|
|
76
115
|
const editorWidth = editor.view.dom.clientWidth
|
|
77
|
-
const deltaX = event.clientX - startDragX
|
|
116
|
+
const deltaX = (event.clientX - startDragX) * dragDirection
|
|
78
117
|
const newWidth = Math.max(
|
|
79
118
|
minWidth,
|
|
80
119
|
Math.min(startWidth + deltaX, editorWidth - maxWidthPadding),
|
|
@@ -102,16 +141,35 @@ export function useNodeViewResize(
|
|
|
102
141
|
const width = el.offsetWidth
|
|
103
142
|
const height = el.offsetHeight
|
|
104
143
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
144
|
+
if (safeGetPos(args.getPos) === null) {
|
|
145
|
+
// No commit happened — drop the drag styles so the (possibly stale)
|
|
146
|
+
// view falls back to its bound size.
|
|
147
|
+
el.style.width = ''
|
|
148
|
+
el.style.height = ''
|
|
149
|
+
const container = args.containerEl?.()
|
|
150
|
+
if (container) container.style.width = ''
|
|
151
|
+
return
|
|
152
|
+
}
|
|
114
153
|
onCommitGuarded(width, height)
|
|
154
|
+
// Container width and a 'style'-sized media element are owned by Vue
|
|
155
|
+
// `:style` bindings on the SAME properties the drag wrote. Their inline
|
|
156
|
+
// values already equal the committed size, so leave them in place — the
|
|
157
|
+
// next binding VALUE change overwrites them. Clearing them here used to
|
|
158
|
+
// race the commit re-render: Vue wrote the committed width first, the
|
|
159
|
+
// deferred clear wiped it, and Vue never re-writes an unchanged value —
|
|
160
|
+
// leaving the block container at full editor width until some later
|
|
161
|
+
// value-changing render (the "huge ring around a small image" bug).
|
|
162
|
+
//
|
|
163
|
+
// Only an 'attribute'-sized media element (img/video, width=/height=)
|
|
164
|
+
// needs its inline styles cleared, or they'd mask future attribute
|
|
165
|
+
// updates. rAF defers that past the commit re-render so the old attribute
|
|
166
|
+
// size never paints (clearing synchronously flashed a page jump).
|
|
167
|
+
if ((args.mediaSizing ?? 'attribute') === 'attribute') {
|
|
168
|
+
requestAnimationFrame(() => {
|
|
169
|
+
el.style.width = ''
|
|
170
|
+
el.style.height = ''
|
|
171
|
+
})
|
|
172
|
+
}
|
|
115
173
|
}
|
|
116
174
|
|
|
117
175
|
function onCommitGuarded(width: number, height: number): void {
|
|
@@ -119,8 +177,9 @@ export function useNodeViewResize(
|
|
|
119
177
|
}
|
|
120
178
|
|
|
121
179
|
function removeWindowListeners(): void {
|
|
122
|
-
window.removeEventListener('
|
|
123
|
-
window.removeEventListener('
|
|
180
|
+
window.removeEventListener('pointermove', handleResize)
|
|
181
|
+
window.removeEventListener('pointerup', stopResize)
|
|
182
|
+
window.removeEventListener('pointercancel', stopResize)
|
|
124
183
|
}
|
|
125
184
|
|
|
126
185
|
onUnmounted(() => {
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { Editor } from '@tiptap/core'
|
|
2
|
+
import { onBeforeUnmount, onMounted, ref, type Ref } from 'vue'
|
|
3
|
+
import { CellSelection, isInTable, selectionCell } from '@tiptap/pm/tables'
|
|
4
|
+
import { PALETTE_NAMES } from '#molecules/editor/extensions/shared/color-palette'
|
|
5
|
+
|
|
6
|
+
export interface TableCellColorState {
|
|
7
|
+
/** Named background of the anchor cell, or `null` when none / not in a table. */
|
|
8
|
+
activeBackground: Ref<string | null>
|
|
9
|
+
/** Named text color active across the selection, or `null`. */
|
|
10
|
+
activeTextColor: Ref<string | null>
|
|
11
|
+
/** Fill the selected cell(s); `null` clears the fill. */
|
|
12
|
+
setBackground: (name: string | null) => void
|
|
13
|
+
/** Color all text in the selected cell(s); `null` clears it. */
|
|
14
|
+
setTextColor: (name: string | null) => void
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Reactive active-color state + setters for the table cell-color picker. Mirrors
|
|
19
|
+
* `useNamedColorState`'s leak-free subscription pattern (attach in `onMounted`,
|
|
20
|
+
* detach in `onBeforeUnmount`) and guards every read/write against a destroyed
|
|
21
|
+
* editor.
|
|
22
|
+
*/
|
|
23
|
+
export function useTableCellColorState(editor: Editor): TableCellColorState {
|
|
24
|
+
const activeBackground = ref<string | null>(null)
|
|
25
|
+
const activeTextColor = ref<string | null>(null)
|
|
26
|
+
|
|
27
|
+
const readBackground = (): string | null => {
|
|
28
|
+
const { state } = editor
|
|
29
|
+
if (!isInTable(state)) return null
|
|
30
|
+
const { selection } = state
|
|
31
|
+
// The anchor cell's value: for a uniform selection it's the shared color,
|
|
32
|
+
// otherwise just the anchor's — enough to drive the active swatch.
|
|
33
|
+
const $cell =
|
|
34
|
+
selection instanceof CellSelection
|
|
35
|
+
? selection.$anchorCell
|
|
36
|
+
: selectionCell(state)
|
|
37
|
+
const value = $cell?.nodeAfter?.attrs?.backgroundColor
|
|
38
|
+
return typeof value === 'string' ? value : null
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const readTextColor = (): string | null => {
|
|
42
|
+
for (const name of PALETTE_NAMES) {
|
|
43
|
+
if (editor.isActive('textStyle', { color: name })) return name
|
|
44
|
+
}
|
|
45
|
+
return null
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const sync = () => {
|
|
49
|
+
if (!editor || editor.isDestroyed) return
|
|
50
|
+
activeBackground.value = readBackground()
|
|
51
|
+
activeTextColor.value = readTextColor()
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
onMounted(() => {
|
|
55
|
+
sync()
|
|
56
|
+
if (!editor || editor.isDestroyed) return
|
|
57
|
+
editor.on('transaction', sync)
|
|
58
|
+
editor.on('selectionUpdate', sync)
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
onBeforeUnmount(() => {
|
|
62
|
+
if (!editor) return
|
|
63
|
+
editor.off('transaction', sync)
|
|
64
|
+
editor.off('selectionUpdate', sync)
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
const setBackground = (name: string | null) => {
|
|
68
|
+
if (!editor || editor.isDestroyed) return
|
|
69
|
+
editor.chain().focus().setCellBackground(name).run()
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const setTextColor = (name: string | null) => {
|
|
73
|
+
if (!editor || editor.isDestroyed) return
|
|
74
|
+
editor.chain().focus().setCellTextColor(name).run()
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return { activeBackground, activeTextColor, setBackground, setTextColor }
|
|
78
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { onBeforeUnmount, onMounted, ref, type Ref } from 'vue'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Shared, reference-counted signal that is `true` while the user is dragging
|
|
5
|
+
* file(s) anywhere over the window.
|
|
6
|
+
*
|
|
7
|
+
* Editor drop zones use it to reveal themselves the moment a file enters the
|
|
8
|
+
* page — not only once it is over the editor — so the drop targets are
|
|
9
|
+
* discoverable instead of invisible until hovered.
|
|
10
|
+
*
|
|
11
|
+
* Listeners are bound in the CAPTURE phase so the `drop`/`dragend` resets still
|
|
12
|
+
* fire when an inner handler (the media ProseMirror plugin, a scoped drop zone)
|
|
13
|
+
* calls `stopPropagation()` on the bubbling event. A single set of listeners is
|
|
14
|
+
* shared across all callers via the ref count.
|
|
15
|
+
*
|
|
16
|
+
* Detection only — it never calls `preventDefault`, so it does not turn the
|
|
17
|
+
* window into a drop target or otherwise change drop handling.
|
|
18
|
+
*/
|
|
19
|
+
const isWindowFileDragging = ref(false)
|
|
20
|
+
let refCount = 0
|
|
21
|
+
// Depth counter: dragenter on a child fires before dragleave on its parent, so
|
|
22
|
+
// the count stays > 0 while the pointer is anywhere inside the window and only
|
|
23
|
+
// hits 0 when the drag truly leaves it.
|
|
24
|
+
let depth = 0
|
|
25
|
+
|
|
26
|
+
const hasFiles = (event: DragEvent): boolean =>
|
|
27
|
+
!!event.dataTransfer && Array.from(event.dataTransfer.types).includes('Files')
|
|
28
|
+
|
|
29
|
+
const reset = () => {
|
|
30
|
+
depth = 0
|
|
31
|
+
isWindowFileDragging.value = false
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const onDragEnter = (event: DragEvent) => {
|
|
35
|
+
if (!hasFiles(event)) return
|
|
36
|
+
depth += 1
|
|
37
|
+
isWindowFileDragging.value = true
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const onDragLeave = () => {
|
|
41
|
+
depth = Math.max(0, depth - 1)
|
|
42
|
+
if (depth === 0) isWindowFileDragging.value = false
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function useWindowFileDragging(): Ref<boolean> {
|
|
46
|
+
onMounted(() => {
|
|
47
|
+
if (refCount === 0) {
|
|
48
|
+
window.addEventListener('dragenter', onDragEnter, true)
|
|
49
|
+
window.addEventListener('dragleave', onDragLeave, true)
|
|
50
|
+
window.addEventListener('drop', reset, true)
|
|
51
|
+
window.addEventListener('dragend', reset, true)
|
|
52
|
+
}
|
|
53
|
+
refCount += 1
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
onBeforeUnmount(() => {
|
|
57
|
+
refCount = Math.max(0, refCount - 1)
|
|
58
|
+
if (refCount === 0) {
|
|
59
|
+
window.removeEventListener('dragenter', onDragEnter, true)
|
|
60
|
+
window.removeEventListener('dragleave', onDragLeave, true)
|
|
61
|
+
window.removeEventListener('drop', reset, true)
|
|
62
|
+
window.removeEventListener('dragend', reset, true)
|
|
63
|
+
reset()
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
return isWindowFileDragging
|
|
68
|
+
}
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The attachment upload pipeline.
|
|
3
|
+
*
|
|
4
|
+
* Attachments are non-image, non-video files (pdf, docx, zip, csv, …) rendered
|
|
5
|
+
* as an inline chip/link. The lifecycle mirrors the image/video engine — stage
|
|
6
|
+
* the file, insert a loading placeholder, upload via the shared upload function,
|
|
7
|
+
* write the resulting `file_url` back onto the node — but it is deliberately
|
|
8
|
+
* leaner: no base64 preview and no intrinsic-dimension probe (a file chip has no
|
|
9
|
+
* dimensions). It reuses the shared placeholder / success / error doc ops and
|
|
10
|
+
* the shared upload/progress state so behaviour stays identical to media.
|
|
11
|
+
*
|
|
12
|
+
* Async safety (conventions §0.6): `editor.view` is resolved fresh at every
|
|
13
|
+
* dispatch site (the shared doc ops read it from the passed view), and every
|
|
14
|
+
* post-`await` write is guarded by an `editor.isDestroyed` check. The staged
|
|
15
|
+
* file is dropped only on the success path so `reupload` ("Try again") can
|
|
16
|
+
* re-read it after a terminal error.
|
|
17
|
+
*/
|
|
18
|
+
import type { Editor } from '@tiptap/core'
|
|
19
|
+
import { fileSizeLimitMessage } from '#utils/fileSize'
|
|
20
|
+
import { isSafeUrl } from '#molecules/editor/extensions/shared/url-safety'
|
|
21
|
+
import { findNodeByUploadId } from '#molecules/editor/extensions/shared/node-view'
|
|
22
|
+
import {
|
|
23
|
+
applyUploadError,
|
|
24
|
+
applyUploadSuccess,
|
|
25
|
+
insertPlaceholder,
|
|
26
|
+
removeNodeByUploadId,
|
|
27
|
+
} from '#molecules/editor/extensions/shared/media-node-ops'
|
|
28
|
+
import { createUploadId } from '#molecules/editor/extensions/shared/upload-id'
|
|
29
|
+
import {
|
|
30
|
+
deleteLocalFile,
|
|
31
|
+
deleteUploadProgress,
|
|
32
|
+
getLocalFile,
|
|
33
|
+
setLocalFile,
|
|
34
|
+
setUploadProgress,
|
|
35
|
+
updateUploadProgress,
|
|
36
|
+
} from '#molecules/editor/extensions/shared/media-upload-state'
|
|
37
|
+
import type { MediaUploadOptions } from '#molecules/editor/extensions/shared/media-upload-engine'
|
|
38
|
+
import type { InsertMode } from '#molecules/editor/extensions/shared/media-upload-types'
|
|
39
|
+
|
|
40
|
+
/** Schema node name this engine inserts/updates. */
|
|
41
|
+
export const ATTACHMENT_NODE_NAME = 'attachment'
|
|
42
|
+
|
|
43
|
+
/** Attachments have no intrinsic dimensions; the shared ops want a value. */
|
|
44
|
+
const NO_DIMENSIONS = { width: null, height: null }
|
|
45
|
+
|
|
46
|
+
/** The node attrs derived from a `File` before upload (filename/size/type). */
|
|
47
|
+
function attachmentAttrsFromFile(file: File): Record<string, unknown> {
|
|
48
|
+
return {
|
|
49
|
+
fileName: file.name,
|
|
50
|
+
fileSize: file.size,
|
|
51
|
+
mimeType: file.type || 'application/octet-stream',
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Core upload run: stage file -> placeholder -> upload -> write-back. Returns
|
|
57
|
+
* the uploadId so multi-upload sequencing can anchor the next insert.
|
|
58
|
+
*/
|
|
59
|
+
async function run(
|
|
60
|
+
file: File,
|
|
61
|
+
editor: Editor,
|
|
62
|
+
pos: number | null | undefined,
|
|
63
|
+
mode: InsertMode,
|
|
64
|
+
options: MediaUploadOptions,
|
|
65
|
+
placeholderAttrs: Record<string, unknown> = attachmentAttrsFromFile(file),
|
|
66
|
+
): Promise<string | null> {
|
|
67
|
+
if (!options.uploadFunction) {
|
|
68
|
+
console.error('uploadFunction option is not provided')
|
|
69
|
+
return null
|
|
70
|
+
}
|
|
71
|
+
// Yield one microtask before mutating the doc. These engine entry points run
|
|
72
|
+
// from a command callback that returns `true`, after which ProseMirror's
|
|
73
|
+
// CommandManager dispatches its own transaction for that tick. Inserting the
|
|
74
|
+
// placeholder synchronously here would dispatch a second transaction built on
|
|
75
|
+
// the pre-command state, which ProseMirror rejects with "Applying a mismatched
|
|
76
|
+
// transaction". The image/video engines get this yield implicitly from their
|
|
77
|
+
// `await probeOrNull(...)`; an attachment has no dimensions to probe.
|
|
78
|
+
await Promise.resolve()
|
|
79
|
+
const uploadId = createUploadId()
|
|
80
|
+
const abortController = new AbortController()
|
|
81
|
+
setUploadProgress(uploadId, {
|
|
82
|
+
loaded: 0,
|
|
83
|
+
total: file.size,
|
|
84
|
+
percent: 0,
|
|
85
|
+
abort: () => abortController.abort(),
|
|
86
|
+
})
|
|
87
|
+
try {
|
|
88
|
+
// Reject over-limit files before staging: insert an error placeholder with
|
|
89
|
+
// the file kept so "Try again" / "Choose another" still work.
|
|
90
|
+
const validationError = fileSizeLimitMessage(file)
|
|
91
|
+
if (validationError) {
|
|
92
|
+
setLocalFile(uploadId, { file })
|
|
93
|
+
if (editor.isDestroyed) {
|
|
94
|
+
deleteLocalFile(uploadId)
|
|
95
|
+
deleteUploadProgress(uploadId)
|
|
96
|
+
return uploadId
|
|
97
|
+
}
|
|
98
|
+
insertPlaceholder(
|
|
99
|
+
editor.view,
|
|
100
|
+
ATTACHMENT_NODE_NAME,
|
|
101
|
+
pos,
|
|
102
|
+
mode,
|
|
103
|
+
uploadId,
|
|
104
|
+
NO_DIMENSIONS,
|
|
105
|
+
placeholderAttrs,
|
|
106
|
+
)
|
|
107
|
+
applyUploadError(
|
|
108
|
+
editor.view,
|
|
109
|
+
ATTACHMENT_NODE_NAME,
|
|
110
|
+
uploadId,
|
|
111
|
+
validationError,
|
|
112
|
+
)
|
|
113
|
+
return uploadId
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
setLocalFile(uploadId, { file })
|
|
117
|
+
if (editor.isDestroyed) {
|
|
118
|
+
deleteLocalFile(uploadId)
|
|
119
|
+
deleteUploadProgress(uploadId)
|
|
120
|
+
return uploadId
|
|
121
|
+
}
|
|
122
|
+
insertPlaceholder(
|
|
123
|
+
editor.view,
|
|
124
|
+
ATTACHMENT_NODE_NAME,
|
|
125
|
+
pos,
|
|
126
|
+
mode,
|
|
127
|
+
uploadId,
|
|
128
|
+
NO_DIMENSIONS,
|
|
129
|
+
placeholderAttrs,
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
const uploaded = await options.uploadFunction(file, {
|
|
133
|
+
signal: abortController.signal,
|
|
134
|
+
onProgress: (progress) => {
|
|
135
|
+
updateUploadProgress(uploadId, {
|
|
136
|
+
...progress,
|
|
137
|
+
abort: () => abortController.abort(),
|
|
138
|
+
})
|
|
139
|
+
},
|
|
140
|
+
})
|
|
141
|
+
if (editor.isDestroyed) {
|
|
142
|
+
deleteLocalFile(uploadId)
|
|
143
|
+
deleteUploadProgress(uploadId)
|
|
144
|
+
return uploadId
|
|
145
|
+
}
|
|
146
|
+
if (
|
|
147
|
+
!uploaded?.file_url ||
|
|
148
|
+
!isSafeUrl(uploaded.file_url, { base: window.location.origin })
|
|
149
|
+
) {
|
|
150
|
+
applyUploadError(
|
|
151
|
+
editor.view,
|
|
152
|
+
ATTACHMENT_NODE_NAME,
|
|
153
|
+
uploadId,
|
|
154
|
+
'Upload returned no file URL',
|
|
155
|
+
)
|
|
156
|
+
return uploadId
|
|
157
|
+
}
|
|
158
|
+
// applyUploadSuccess preserves the existing attrs (fileName/fileSize/…) and
|
|
159
|
+
// only sets `src`, clears `loading`/`error`.
|
|
160
|
+
applyUploadSuccess(editor.view, ATTACHMENT_NODE_NAME, uploadId, uploaded)
|
|
161
|
+
deleteLocalFile(uploadId)
|
|
162
|
+
} catch (error) {
|
|
163
|
+
if (abortController.signal.aborted) {
|
|
164
|
+
if (!editor.isDestroyed) {
|
|
165
|
+
removeNodeByUploadId(editor.view, ATTACHMENT_NODE_NAME, uploadId)
|
|
166
|
+
}
|
|
167
|
+
deleteLocalFile(uploadId)
|
|
168
|
+
deleteUploadProgress(uploadId)
|
|
169
|
+
return uploadId
|
|
170
|
+
}
|
|
171
|
+
const message = (error as Error)?.message || 'Failed to upload attachment'
|
|
172
|
+
if (!editor.isDestroyed) {
|
|
173
|
+
applyUploadError(editor.view, ATTACHMENT_NODE_NAME, uploadId, message)
|
|
174
|
+
}
|
|
175
|
+
} finally {
|
|
176
|
+
if (!abortController.signal.aborted) {
|
|
177
|
+
deleteUploadProgress(uploadId)
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return uploadId
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** Upload a single file, replacing the current selection with the chip. */
|
|
184
|
+
export async function uploadAttachment(
|
|
185
|
+
file: File,
|
|
186
|
+
editor: Editor,
|
|
187
|
+
options: MediaUploadOptions,
|
|
188
|
+
): Promise<void> {
|
|
189
|
+
await run(file, editor, null, 'replace', options)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Doc-mutating multi-upload for drop/paste. Uploads sequentially, anchoring each
|
|
194
|
+
* insert immediately after the previous placeholder via its uploadId. Reads
|
|
195
|
+
* `editor.view` fresh each iteration (the host may swap it mid-flight).
|
|
196
|
+
*/
|
|
197
|
+
export async function uploadAttachmentFiles(
|
|
198
|
+
files: File[],
|
|
199
|
+
editor: Editor,
|
|
200
|
+
pos: number | null,
|
|
201
|
+
options: MediaUploadOptions,
|
|
202
|
+
): Promise<void> {
|
|
203
|
+
if (files.length === 0) return
|
|
204
|
+
if (files.length === 1) {
|
|
205
|
+
await run(files[0], editor, pos, 'replace', options)
|
|
206
|
+
return
|
|
207
|
+
}
|
|
208
|
+
let lastUploadId: string | null = null
|
|
209
|
+
for (const file of files) {
|
|
210
|
+
let insertPos = pos
|
|
211
|
+
if (lastUploadId) {
|
|
212
|
+
const prevPos = findNodeByUploadId(
|
|
213
|
+
editor.view,
|
|
214
|
+
ATTACHMENT_NODE_NAME,
|
|
215
|
+
lastUploadId,
|
|
216
|
+
)
|
|
217
|
+
if (prevPos !== null) {
|
|
218
|
+
const prevNode = editor.view.state.doc.nodeAt(prevPos)
|
|
219
|
+
insertPos = prevPos + (prevNode?.nodeSize ?? 1)
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
lastUploadId = await run(file, editor, insertPos, 'insert', options)
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/** Re-run a failed upload for the node at `pos` using its staged file. */
|
|
227
|
+
export async function reuploadAttachment(
|
|
228
|
+
editor: Editor,
|
|
229
|
+
pos: number,
|
|
230
|
+
options: MediaUploadOptions,
|
|
231
|
+
): Promise<void> {
|
|
232
|
+
const node = editor.view.state.doc.nodeAt(pos)
|
|
233
|
+
const uploadId = node?.attrs.uploadId as string | undefined
|
|
234
|
+
const entry = uploadId ? getLocalFile(uploadId) : undefined
|
|
235
|
+
if (!uploadId || !entry) {
|
|
236
|
+
console.error(`reuploadAttachment: no staged file for node at ${pos}`)
|
|
237
|
+
return
|
|
238
|
+
}
|
|
239
|
+
const replacementUploadId = await run(
|
|
240
|
+
entry.file,
|
|
241
|
+
editor,
|
|
242
|
+
pos,
|
|
243
|
+
'replace',
|
|
244
|
+
options,
|
|
245
|
+
node?.attrs ?? attachmentAttrsFromFile(entry.file),
|
|
246
|
+
)
|
|
247
|
+
if (replacementUploadId) {
|
|
248
|
+
deleteLocalFile(uploadId)
|
|
249
|
+
}
|
|
250
|
+
}
|