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.
Files changed (692) hide show
  1. package/experimental/FloatingWindow/FloatingWindow.cy.ts +328 -0
  2. package/experimental/FloatingWindow/FloatingWindow.vue +303 -0
  3. package/experimental/FloatingWindow/index.ts +8 -0
  4. package/experimental/FloatingWindow/types.ts +31 -0
  5. package/experimental/FloatingWindow/useFloatingWindow.ts +236 -0
  6. package/experimental/MultiEmailInput/MultiEmailInput.cy.ts +155 -0
  7. package/experimental/MultiEmailInput/MultiEmailInput.vue +472 -0
  8. package/experimental/MultiEmailInput/index.ts +17 -0
  9. package/experimental/MultiEmailInput/stories/AsyncSuggestions.vue +54 -0
  10. package/experimental/MultiEmailInput/stories/Basic.vue +18 -0
  11. package/experimental/MultiEmailInput/stories/CustomChip.vue +39 -0
  12. package/experimental/MultiEmailInput/stories/Labeling.vue +28 -0
  13. package/experimental/MultiEmailInput/types.ts +138 -0
  14. package/experimental/MultiEmailInput/utils.ts +60 -0
  15. package/experimental.ts +12 -0
  16. package/frappe/Billing/SignupBanner.vue +1 -1
  17. package/frappe/Billing/TrialBanner.vue +1 -1
  18. package/frappe/DataImport/DataImport.vue +1 -1
  19. package/frappe/DataImport/DataImportList.vue +1 -1
  20. package/frappe/DataImport/ImportSteps.vue +6 -6
  21. package/frappe/DataImport/MappingStep.vue +1 -1
  22. package/frappe/DataImport/PreviewStep.vue +9 -9
  23. package/frappe/DataImport/TemplateModal.vue +1 -1
  24. package/frappe/DataImport/UploadStep.vue +5 -5
  25. package/frappe/Filter/Filter.vue +1 -1
  26. package/frappe/Help/HelpModal.vue +1 -1
  27. package/frappe/Link/stories/Suffix.vue +2 -2
  28. package/frappe/Onboarding/GettingStartedBanner.vue +1 -1
  29. package/frappe/drive/components/AppSwitcher.vue +1 -1
  30. package/frappe/drive/components/InfoDialog.vue +3 -3
  31. package/frappe/drive/components/MoveDialog.vue +2 -2
  32. package/frappe/drive/components/Select/Select.vue +2 -2
  33. package/frappe/drive/components/ShareDialog.vue +1 -1
  34. package/frappe/telemetry/index.ts +88 -14
  35. package/frappe/telemetry/pulse.ts +78 -164
  36. package/frappe/telemetry/telemetry.test.ts +13 -79
  37. package/icons/IconPicker.vue +1 -1
  38. package/package.json +104 -34
  39. package/src/components/Alert/Alert.md +4 -0
  40. package/src/components/Alert/Alert.vue +4 -4
  41. package/src/components/Autocomplete/Autocomplete.vue +6 -6
  42. package/src/components/Avatar/Avatar.md +4 -0
  43. package/src/components/Avatar/Avatar.vue +23 -4
  44. package/src/components/Badge/Badge.api.md +1 -1
  45. package/src/components/Badge/Badge.cy.ts +34 -16
  46. package/src/components/Badge/Badge.md +20 -4
  47. package/src/components/Badge/Badge.vue +60 -45
  48. package/src/components/Badge/stories/CallMeta.vue +35 -0
  49. package/src/components/Badge/stories/EventStatus.vue +16 -0
  50. package/src/components/Badge/stories/ListStatus.vue +91 -0
  51. package/src/components/Badge/stories/Reactions.vue +33 -0
  52. package/src/components/Badge/stories/RowTags.vue +39 -0
  53. package/src/components/Badge/types.ts +1 -2
  54. package/src/components/BottomSheet/BottomSheet.api.md +57 -0
  55. package/src/components/BottomSheet/BottomSheet.vue +232 -0
  56. package/src/components/BottomSheet/index.ts +6 -0
  57. package/src/components/BottomSheet/types.ts +23 -0
  58. package/src/components/Breadcrumbs/Breadcrumbs.md +4 -0
  59. package/src/components/Breadcrumbs/Breadcrumbs.vue +3 -3
  60. package/src/components/Button/Button.api.md +37 -32
  61. package/src/components/Button/Button.cy.ts +18 -2
  62. package/src/components/Button/Button.md +20 -8
  63. package/src/components/Button/Button.vue +304 -284
  64. package/src/components/Button/stories/InlineActions.vue +10 -0
  65. package/src/components/Button/stories/LiveClassCard.vue +34 -0
  66. package/src/components/Button/stories/SectionAction.vue +17 -0
  67. package/src/components/Button/stories/SectionControls.vue +28 -0
  68. package/src/components/Button/stories/SelectionToolbar.vue +20 -0
  69. package/src/components/Button/stories/StackedActions.vue +10 -0
  70. package/src/components/Button/types.ts +38 -32
  71. package/src/components/Calendar/Calendar.api.md +16 -16
  72. package/src/components/Calendar/Calendar.vue +100 -73
  73. package/src/components/Calendar/CalendarDaily.vue +21 -20
  74. package/src/components/Calendar/CalendarMonthEvent.vue +30 -9
  75. package/src/components/Calendar/CalendarMonthly.vue +60 -39
  76. package/src/components/Calendar/CalendarTimeMarker.vue +9 -8
  77. package/src/components/Calendar/CalendarWeekDayEvent.vue +98 -42
  78. package/src/components/Calendar/CalendarWeekly.vue +77 -41
  79. package/src/components/Calendar/EventModalContent.vue +32 -16
  80. package/src/components/Calendar/NewEventModal.vue +19 -16
  81. package/src/components/Calendar/ShowMoreCalendarEvent.vue +11 -17
  82. package/src/components/Calendar/{calendarUtils.js → calendarUtils.ts} +76 -52
  83. package/src/components/Calendar/composables/{useCalendarData.js → useCalendarData.ts} +23 -11
  84. package/src/components/Calendar/composables/{useEventModal.js → useEventModal.ts} +16 -12
  85. package/src/components/Calendar/index.ts +12 -2
  86. package/src/components/Calendar/stories/CustomHeader.vue +1 -1
  87. package/src/components/Calendar/types.ts +106 -0
  88. package/src/components/Calendar/{useEventBase.js → useEventBase.ts} +40 -26
  89. package/src/components/Card.vue +2 -2
  90. package/src/components/Charts/ECharts.vue +1 -1
  91. package/src/components/Charts/NumberChart.vue +7 -7
  92. package/src/components/Charts/eChartOptions.ts +28 -13
  93. package/src/components/Checkbox/Checkbox.api.md +6 -0
  94. package/src/components/Checkbox/Checkbox.cy.ts +10 -2
  95. package/src/components/Checkbox/Checkbox.md +28 -0
  96. package/src/components/Checkbox/Checkbox.vue +9 -22
  97. package/src/components/Checkbox/stories/Group.vue +27 -0
  98. package/src/components/Checkbox/stories/HorizontalGroup.vue +24 -0
  99. package/src/components/Checkbox/stories/SettingRow.vue +23 -0
  100. package/src/components/Checkbox/stories/WithDescription.vue +27 -0
  101. package/src/components/Checkbox/types.ts +7 -0
  102. package/src/components/CodeEditor/CodeEditor.api.md +139 -0
  103. package/src/components/CodeEditor/CodeEditor.cy.ts +160 -0
  104. package/src/components/CodeEditor/CodeEditor.md +104 -0
  105. package/src/components/CodeEditor/CodeEditor.vue +556 -0
  106. package/src/components/CodeEditor/CodePreview.test.ts +49 -0
  107. package/src/components/CodeEditor/CodePreview.vue +45 -0
  108. package/src/components/CodeEditor/index.ts +17 -0
  109. package/src/components/CodeEditor/languages.ts +63 -0
  110. package/src/components/CodeEditor/stories/Default.vue +16 -0
  111. package/src/components/CodeEditor/stories/Labeling.vue +25 -0
  112. package/src/components/CodeEditor/stories/MaxHeight.vue +24 -0
  113. package/src/components/CodeEditor/stories/Preview.vue +36 -0
  114. package/src/components/CodeEditor/stories/Sizes.vue +15 -0
  115. package/src/components/CodeEditor/stories/Variants.vue +13 -0
  116. package/src/components/CodeEditor/theme.ts +272 -0
  117. package/src/components/CodeEditor/types.ts +69 -0
  118. package/src/components/Combobox/Combobox.api.md +19 -20
  119. package/src/components/Combobox/Combobox.cy.ts +54 -14
  120. package/src/components/Combobox/Combobox.md +4 -0
  121. package/src/components/Combobox/Combobox.vue +233 -247
  122. package/src/components/Combobox/ComboboxResults.vue +1 -1
  123. package/src/components/Combobox/stories/Clearable.vue +2 -2
  124. package/src/components/Combobox/stories/CreateNew.vue +1 -1
  125. package/src/components/Combobox/stories/Footer.vue +10 -2
  126. package/src/components/Combobox/stories/InDialog.vue +1 -1
  127. package/src/components/Combobox/types.ts +8 -29
  128. package/src/components/CommandPalette/CommandPaletteItem.vue +1 -1
  129. package/src/components/ContextMenu/ContextMenu.api.md +52 -0
  130. package/src/components/ContextMenu/ContextMenu.cy.ts +191 -0
  131. package/src/components/ContextMenu/ContextMenu.md +25 -0
  132. package/src/components/ContextMenu/ContextMenu.vue +106 -0
  133. package/src/components/ContextMenu/index.ts +2 -0
  134. package/src/components/ContextMenu/stories/FileList.vue +105 -0
  135. package/src/components/ContextMenu/stories/Groups.vue +95 -0
  136. package/src/components/ContextMenu/stories/Simple.vue +72 -0
  137. package/src/components/ContextMenu/types.ts +39 -0
  138. package/src/components/DatePicker/CalendarPanel.vue +128 -86
  139. package/src/components/DatePicker/DatePicker.vue +3 -9
  140. package/src/components/DatePicker/DateRangePicker.vue +4 -8
  141. package/src/components/DatePicker/DateTimePicker.cy.ts +4 -2
  142. package/src/components/DatePicker/DateTimePicker.vue +3 -9
  143. package/src/components/DatePicker/composables.ts +15 -37
  144. package/src/components/DatePicker/stories/Range.vue +1 -1
  145. package/src/components/DatePicker/types.ts +1 -1
  146. package/src/components/DesktopShell/DesktopShell.api.md +40 -0
  147. package/src/components/DesktopShell/DesktopShell.cy.ts +39 -0
  148. package/src/components/DesktopShell/DesktopShell.md +31 -0
  149. package/src/components/DesktopShell/DesktopShell.vue +81 -0
  150. package/src/components/DesktopShell/index.ts +3 -0
  151. package/src/components/DesktopShell/stories/Default.vue +97 -0
  152. package/src/components/DesktopShell/types.ts +2 -0
  153. package/src/components/Dialog/Dialog.api.md +7 -8
  154. package/src/components/Dialog/Dialog.md +4 -0
  155. package/src/components/Dialog/Dialog.vue +8 -7
  156. package/src/components/Dialog/stories/CommandPalette.vue +1 -1
  157. package/src/components/Dialog/stories/Wizard.vue +1 -1
  158. package/src/components/Dialogs.vue +11 -9
  159. package/src/components/Divider/Divider.api.md +1 -1
  160. package/src/components/Divider/Divider.md +4 -0
  161. package/src/components/Dropdown/Dropdown.api.md +10 -35
  162. package/src/components/Dropdown/Dropdown.md +10 -6
  163. package/src/components/Dropdown/Dropdown.vue +33 -47
  164. package/src/components/Dropdown/stories/{05_KebabMenu.vue → KebabMenu.vue} +3 -1
  165. package/src/components/Dropdown/stories/{06_UserMenu.vue → UserMenu.vue} +1 -1
  166. package/src/components/Dropdown/types.ts +25 -189
  167. package/src/components/Duration/Duration.api.md +92 -0
  168. package/src/components/Duration/Duration.cy.ts +183 -0
  169. package/src/components/Duration/Duration.md +98 -0
  170. package/src/components/Duration/Duration.vue +114 -0
  171. package/src/components/Duration/duration.ts +185 -0
  172. package/src/components/Duration/index.ts +8 -0
  173. package/src/components/Duration/stories/Default.vue +10 -0
  174. package/src/components/Duration/stories/Formats.vue +16 -0
  175. package/src/components/Duration/stories/RealWorld.vue +50 -0
  176. package/src/components/Duration/stories/Sizes.vue +15 -0
  177. package/src/components/Duration/stories/States.vue +21 -0
  178. package/src/components/Duration/types.ts +52 -0
  179. package/src/components/ErrorMessage/ErrorMessage.cy.ts +1 -1
  180. package/src/components/ErrorMessage/ErrorMessage.md +4 -0
  181. package/src/components/ErrorMessage/ErrorMessage.vue +1 -1
  182. package/src/components/FileUploader/FileUploader.api.md +36 -11
  183. package/src/components/FileUploader/FileUploader.cy.ts +66 -0
  184. package/src/components/FileUploader/FileUploader.md +4 -1
  185. package/src/components/FileUploader/FileUploader.vue +147 -106
  186. package/src/components/FileUploader/index.ts +6 -0
  187. package/src/components/FileUploader/types.ts +43 -0
  188. package/src/components/FormControl/FormControl.api.md +3 -4
  189. package/src/components/FormControl/FormControl.md +4 -0
  190. package/src/components/FormControl/stories/RealForm.vue +3 -3
  191. package/src/components/FormLabel.vue +5 -8
  192. package/src/components/HoverCard/HoverCard.api.md +100 -0
  193. package/src/components/HoverCard/HoverCard.cy.ts +103 -0
  194. package/src/components/HoverCard/HoverCard.md +52 -0
  195. package/src/components/HoverCard/HoverCard.vue +91 -0
  196. package/src/components/HoverCard/index.ts +2 -0
  197. package/src/components/HoverCard/stories/Arrow.vue +28 -0
  198. package/src/components/HoverCard/stories/Delays.vue +16 -0
  199. package/src/components/HoverCard/stories/Example.vue +28 -0
  200. package/src/components/HoverCard/types.ts +47 -0
  201. package/src/components/Icon/Icon.cy.ts +78 -0
  202. package/src/components/Icon/Icon.vue +50 -0
  203. package/src/components/Icon/index.ts +1 -0
  204. package/src/components/InputLabeling/InputDescription.vue +1 -1
  205. package/src/components/InputLabeling/InputError.vue +1 -1
  206. package/src/components/InputLabeling/InputLabel.vue +21 -7
  207. package/src/components/InputLabeling/RequiredIndicator.vue +1 -1
  208. package/src/components/ItemListRow/ItemListRow.vue +1 -1
  209. package/src/components/ItemListRow/stories/RowStates.vue +1 -1
  210. package/src/components/KeyboardShortcut.vue +4 -8
  211. package/src/components/KeyboardShortcutsModal/KeyboardShortcutsModal.api.md +22 -14
  212. package/src/components/KeyboardShortcutsModal/KeyboardShortcutsModal.vue +2 -2
  213. package/src/components/ListFilter/ListFilter.api.md +5 -6
  214. package/src/components/ListFilter/ListFilter.vue +161 -57
  215. package/src/components/ListFilter/NestedPopover.vue +17 -10
  216. package/src/components/ListFilter/SearchComplete.vue +31 -38
  217. package/src/components/ListItem.vue +1 -1
  218. package/src/components/ListView/ListEmptyState.vue +1 -1
  219. package/src/components/ListView/ListGroupHeader.vue +2 -2
  220. package/src/components/ListView/ListRow.vue +1 -1
  221. package/src/components/ListView/ListSelectBanner.vue +1 -1
  222. package/src/components/ListView/ListView.api.md +322 -6
  223. package/src/components/ListView/index.ts +25 -0
  224. package/src/components/ListView/stories/CustomList.vue +1 -1
  225. package/src/components/ListView/stories/GroupedRows.vue +1 -1
  226. package/src/components/LoadingIndicator.vue +13 -22
  227. package/src/components/LoadingText.vue +3 -13
  228. package/src/components/{Dropdown/DropdownMenuList.vue → Menu/Menu.vue} +110 -75
  229. package/src/components/{Dropdown/DropdownMenuItemContent.vue → Menu/MenuItemContent.vue} +23 -33
  230. package/src/components/{Dropdown/DropdownRenderContent.vue → Menu/MenuRenderContent.vue} +1 -1
  231. package/src/components/Menu/types.ts +225 -0
  232. package/src/components/{Dropdown → Menu}/utils.ts +36 -40
  233. package/src/components/MobileNav/MobileNav.api.md +70 -0
  234. package/src/components/MobileNav/MobileNav.cy.ts +89 -0
  235. package/src/components/MobileNav/MobileNav.md +34 -0
  236. package/src/components/MobileNav/MobileNav.vue +15 -0
  237. package/src/components/MobileNav/MobileNavItem.vue +125 -0
  238. package/src/components/MobileNav/index.ts +4 -0
  239. package/src/components/MobileNav/stories/Default.vue +40 -0
  240. package/src/components/MobileNav/types.ts +33 -0
  241. package/src/components/MobileShell/MobileShell.api.md +23 -0
  242. package/src/components/MobileShell/MobileShell.cy.ts +25 -0
  243. package/src/components/MobileShell/MobileShell.md +24 -0
  244. package/src/components/MobileShell/MobileShell.vue +41 -0
  245. package/src/components/MobileShell/index.ts +3 -0
  246. package/src/components/MobileShell/stories/Default.vue +75 -0
  247. package/src/components/MobileShell/types.ts +7 -0
  248. package/src/components/MonthPicker/MonthPicker.cy.ts +16 -0
  249. package/src/components/MonthPicker/MonthPicker.vue +19 -8
  250. package/src/components/MultiSelect/MultiSelect.api.md +3 -4
  251. package/src/components/MultiSelect/MultiSelect.md +42 -10
  252. package/src/components/MultiSelect/MultiSelect.vue +199 -227
  253. package/src/components/MultiSelect/MultiSelectResults.vue +1 -1
  254. package/src/components/MultiSelect/stories/Members.vue +1 -1
  255. package/src/components/MultiSelect/stories/TagsTrigger.vue +3 -3
  256. package/src/components/MultiSelect/types.ts +3 -3
  257. package/src/components/PageHeader/PageHeader.api.md +112 -0
  258. package/src/components/PageHeader/PageHeader.cy.ts +68 -0
  259. package/src/components/PageHeader/PageHeader.md +25 -0
  260. package/src/components/PageHeader/PageHeader.vue +13 -0
  261. package/src/components/PageHeader/PageHeaderBackButton.vue +29 -0
  262. package/src/components/PageHeader/PageHeaderBase.vue +51 -0
  263. package/src/components/PageHeader/PageHeaderMobile.vue +68 -0
  264. package/src/components/PageHeader/PageHeaderMobileTitle.vue +16 -0
  265. package/src/components/PageHeader/PageHeaderTarget.vue +13 -0
  266. package/src/components/PageHeader/PageHeaderTitle.vue +12 -0
  267. package/src/components/PageHeader/index.ts +7 -0
  268. package/src/components/PageHeader/stories/Default.vue +19 -0
  269. package/src/components/PageHeader/stories/Mobile.vue +24 -0
  270. package/src/components/PageHeader/target.ts +19 -0
  271. package/src/components/PageHeader/types.ts +19 -0
  272. package/src/components/Password/Password.md +4 -0
  273. package/src/components/Password/Password.vue +2 -2
  274. package/src/components/Popover/Popover.api.md +126 -42
  275. package/src/components/Popover/Popover.cy.ts +425 -41
  276. package/src/components/Popover/Popover.md +99 -4
  277. package/src/components/Popover/Popover.vue +288 -215
  278. package/src/components/Popover/index.ts +8 -1
  279. package/src/components/Popover/stories/Arrow.vue +16 -0
  280. package/src/components/Popover/stories/Bare.vue +20 -0
  281. package/src/components/Popover/stories/Click.vue +7 -4
  282. package/src/components/Popover/stories/Controlled.vue +22 -0
  283. package/src/components/Popover/stories/Dismissible.vue +19 -0
  284. package/src/components/Popover/stories/MatchTriggerWidth.vue +16 -0
  285. package/src/components/Popover/stories/SideAlign.vue +17 -0
  286. package/src/components/Popover/types.ts +101 -23
  287. package/src/components/Progress/Progress.cy.ts +2 -2
  288. package/src/components/Progress/Progress.md +4 -0
  289. package/src/components/Progress/Progress.vue +4 -4
  290. package/src/components/Rail/Rail.api.md +91 -0
  291. package/src/components/Rail/Rail.cy.ts +84 -0
  292. package/src/components/Rail/Rail.md +30 -0
  293. package/src/components/Rail/Rail.vue +16 -0
  294. package/src/components/Rail/RailItem.vue +134 -0
  295. package/src/components/Rail/RailItemBadge.vue +64 -0
  296. package/src/components/Rail/index.ts +2 -0
  297. package/src/components/Rail/stories/Default.vue +44 -0
  298. package/src/components/Rail/types.ts +37 -0
  299. package/src/components/Rating/Rating.md +4 -0
  300. package/src/components/Rating/Rating.vue +5 -8
  301. package/src/components/Rating/stories/CustomSlot.vue +2 -2
  302. package/src/components/ScrollArea/ScrollArea.api.md +59 -0
  303. package/src/components/ScrollArea/ScrollArea.vue +46 -0
  304. package/src/components/ScrollArea/ScrollBar.vue +101 -0
  305. package/src/components/ScrollArea/index.ts +2 -0
  306. package/src/components/Select/Select.cy.ts +16 -15
  307. package/src/components/Select/Select.md +4 -0
  308. package/src/components/Select/Select.vue +217 -203
  309. package/src/components/Select/stories/Example.vue +4 -3
  310. package/src/components/Select/stories/States.vue +1 -1
  311. package/src/components/Select/stories/TriggerSlots.vue +2 -2
  312. package/src/components/Select/utils.ts +4 -3
  313. package/src/components/SettingsDialog/SettingsBody.vue +23 -0
  314. package/src/components/SettingsDialog/SettingsContent.vue +7 -0
  315. package/src/components/SettingsDialog/SettingsDialog.api.md +273 -0
  316. package/src/components/SettingsDialog/SettingsDialog.cy.ts +126 -0
  317. package/src/components/SettingsDialog/SettingsDialog.md +43 -0
  318. package/src/components/SettingsDialog/SettingsDialog.vue +76 -0
  319. package/src/components/SettingsDialog/SettingsHeader.vue +36 -0
  320. package/src/components/SettingsDialog/SettingsNavGroup.vue +20 -0
  321. package/src/components/SettingsDialog/SettingsNavItem.vue +33 -0
  322. package/src/components/SettingsDialog/SettingsPanel.cy.ts +77 -0
  323. package/src/components/SettingsDialog/SettingsPanel.vue +26 -0
  324. package/src/components/SettingsDialog/SettingsRow.vue +52 -0
  325. package/src/components/SettingsDialog/SettingsSidebar.vue +16 -0
  326. package/src/components/SettingsDialog/index.ts +10 -0
  327. package/src/components/SettingsDialog/stories/Default.vue +99 -0
  328. package/src/components/SettingsDialog/stories/PanelBasic.vue +52 -0
  329. package/src/components/SettingsDialog/stories/panels/NotificationsPanel.vue +57 -0
  330. package/src/components/SettingsDialog/stories/panels/PreferencesPanel.vue +57 -0
  331. package/src/components/SettingsDialog/stories/panels/ProfilePanel.vue +38 -0
  332. package/src/components/SettingsDialog/stories/panels/UsersPanel.vue +83 -0
  333. package/src/components/SettingsDialog/types.ts +21 -0
  334. package/src/components/Sidebar/Sidebar.api.md +124 -65
  335. package/src/components/Sidebar/Sidebar.cy.ts +127 -76
  336. package/src/components/Sidebar/Sidebar.md +47 -3
  337. package/src/components/Sidebar/Sidebar.vue +64 -56
  338. package/src/components/Sidebar/SidebarCollapseToggle.vue +23 -0
  339. package/src/components/Sidebar/SidebarHeader.vue +76 -52
  340. package/src/components/Sidebar/SidebarItem.vue +159 -95
  341. package/src/components/Sidebar/SidebarItemIcon.vue +25 -0
  342. package/src/components/Sidebar/SidebarLabel.vue +34 -0
  343. package/src/components/Sidebar/SidebarSection.vue +29 -14
  344. package/src/components/Sidebar/index.ts +13 -2
  345. package/src/components/Sidebar/stories/Collapsed.vue +40 -0
  346. package/src/components/Sidebar/stories/Default.vue +153 -0
  347. package/src/components/Sidebar/stories/Legacy.vue +36 -0
  348. package/src/components/Sidebar/types.ts +105 -20
  349. package/src/components/Skeleton/Skeleton.cy.ts +21 -0
  350. package/src/components/Skeleton/Skeleton.md +21 -0
  351. package/src/components/Skeleton/Skeleton.vue +6 -0
  352. package/src/components/Skeleton/index.ts +1 -0
  353. package/src/components/Skeleton/stories/Card.vue +12 -0
  354. package/src/components/Skeleton/stories/List.vue +15 -0
  355. package/src/components/Skeleton/stories/Text.vue +11 -0
  356. package/src/components/Slider/Slider.api.md +1 -1
  357. package/src/components/Slider/Slider.cy.ts +29 -0
  358. package/src/components/Slider/Slider.md +10 -0
  359. package/src/components/Slider/Slider.vue +37 -9
  360. package/src/components/Slider/stories/NegativeValues.vue +12 -0
  361. package/src/components/Slider/types.ts +1 -1
  362. package/src/components/Spinner/Spinner.api.md +32 -0
  363. package/src/components/Spinner/Spinner.cy.ts +87 -0
  364. package/src/components/Spinner/Spinner.md +21 -0
  365. package/src/components/Spinner/Spinner.vue +168 -0
  366. package/src/components/Spinner/index.ts +2 -0
  367. package/src/components/Spinner/stories/InContext.vue +49 -0
  368. package/src/components/Spinner/stories/Sizes.vue +12 -0
  369. package/src/components/Spinner/stories/Themes.vue +10 -0
  370. package/src/components/Spinner/stories/Track.vue +12 -0
  371. package/src/components/Spinner/types.ts +14 -0
  372. package/src/components/Switch/Switch.md +4 -0
  373. package/src/components/Switch/Switch.vue +6 -23
  374. package/src/components/TabButtons/Pill.vue +161 -0
  375. package/src/components/TabButtons/TabButtons.api.md +52 -2
  376. package/src/components/TabButtons/TabButtons.cy.ts +18 -5
  377. package/src/components/TabButtons/TabButtons.md +49 -0
  378. package/src/components/TabButtons/TabButtons.vue +207 -109
  379. package/src/components/TabButtons/index.ts +11 -1
  380. package/src/components/TabButtons/pillTypes.ts +43 -0
  381. package/src/components/TabButtons/stories/PrefixSuffix.vue +65 -0
  382. package/src/components/TabButtons/stories/Sizes.vue +26 -0
  383. package/src/components/TabButtons/stories/Variants.vue +40 -0
  384. package/src/components/TabButtons/stories/Vertical.vue +64 -0
  385. package/src/components/TabButtons/types.ts +49 -0
  386. package/src/components/Tabs/Tabs.md +4 -0
  387. package/src/components/Tabs/Tabs.vue +1 -1
  388. package/src/components/TextEditor/TextEditor.cy.ts +1 -1
  389. package/src/components/TextEditor/TextEditor.md +3 -0
  390. package/src/components/TextEditor/TextEditor.vue +7 -4
  391. package/src/components/TextEditor/components/CodeBlockComponent.vue +4 -194
  392. package/src/components/TextEditor/components/FontColor.vue +1 -1
  393. package/src/components/TextEditor/components/LinkPopup.vue +1 -1
  394. package/src/components/TextEditor/components/MediaNodeView.vue +17 -17
  395. package/src/components/TextEditor/components/MentionList.vue +1 -1
  396. package/src/components/TextEditor/components/TextEditorBubbleMenu.vue +14 -5
  397. package/src/components/TextEditor/components/TextEditorFixedMenu.vue +3 -3
  398. package/src/components/TextEditor/components/{Menu.vue → TextEditorMenu.vue} +3 -3
  399. package/src/components/TextEditor/extensions/iframe/InsertIframe.vue +2 -2
  400. package/src/components/TextEditor/extensions/iframe/iframe-extension.ts +62 -19
  401. package/src/components/TextEditor/extensions/image-group/ImageGroupNodeView.vue +1 -1
  402. package/src/components/TextEditor/extensions/image-group/ImageGroupUploadDialog.vue +4 -4
  403. package/src/components/TextEditor/extensions/suggestion/SuggestionList.vue +1 -1
  404. package/src/components/TextEditor/extensions/toc-node/TocNodeView.vue +1 -1
  405. package/src/components/TextEditor/hljs-github.css +197 -0
  406. package/src/components/TextEditor/index.ts +13 -0
  407. package/src/components/TextInput/TextInput.cy.ts +8 -3
  408. package/src/components/TextInput/TextInput.md +4 -0
  409. package/src/components/TextInput/TextInput.vue +11 -15
  410. package/src/components/TextInput/stories/Slots.vue +1 -1
  411. package/src/components/Textarea/Textarea.cy.ts +1 -1
  412. package/src/components/Textarea/Textarea.md +4 -0
  413. package/src/components/Textarea/Textarea.vue +11 -15
  414. package/src/components/ThemeSwitcher/ThemePreview.vue +74 -0
  415. package/src/components/ThemeSwitcher/ThemeSwitcher.api.md +82 -0
  416. package/src/components/ThemeSwitcher/ThemeSwitcher.cy.ts +101 -0
  417. package/src/components/ThemeSwitcher/ThemeSwitcher.md +70 -0
  418. package/src/components/ThemeSwitcher/ThemeSwitcher.vue +145 -0
  419. package/src/components/ThemeSwitcher/index.ts +2 -0
  420. package/src/components/ThemeSwitcher/previews/PreviewWindow.vue +74 -0
  421. package/src/components/ThemeSwitcher/stories/Branded.vue +12 -0
  422. package/src/components/ThemeSwitcher/stories/Default.vue +7 -0
  423. package/src/components/ThemeSwitcher/stories/Menu.vue +53 -0
  424. package/src/components/ThemeSwitcher/stories/Toggle.vue +25 -0
  425. package/src/components/ThemeSwitcher/types.ts +35 -0
  426. package/src/components/TimePicker/TimePicker.api.md +67 -15
  427. package/src/components/TimePicker/TimePicker.cy.ts +130 -14
  428. package/src/components/TimePicker/TimePicker.md +6 -0
  429. package/src/components/TimePicker/TimePicker.vue +74 -19
  430. package/src/components/TimePicker/stories/Labeling.vue +21 -0
  431. package/src/components/TimePicker/stories/Range.vue +1 -1
  432. package/src/components/TimePicker/stories/SizesAndVariants.vue +23 -0
  433. package/src/components/TimePicker/stories/TwentyFour.vue +1 -1
  434. package/src/components/TimePicker/types.ts +15 -3
  435. package/src/components/TimePicker/utils.ts +82 -21
  436. package/src/components/Toast/Toast.md +1 -1
  437. package/src/components/Toast/Toast.sanitize.test.ts +50 -0
  438. package/src/components/Toast/Toast.test.ts +5 -0
  439. package/src/components/Toast/Toast.vue +7 -7
  440. package/src/components/Toast/ToastProvider.vue +18 -9
  441. package/src/components/Toast/stories/CustomIcon.vue +1 -1
  442. package/src/components/Toast/toast.ts +32 -8
  443. package/src/components/Tooltip/Tooltip.api.md +43 -5
  444. package/src/components/Tooltip/Tooltip.cy.ts +48 -0
  445. package/src/components/Tooltip/Tooltip.md +10 -0
  446. package/src/components/Tooltip/Tooltip.vue +20 -5
  447. package/src/components/Tooltip/TooltipBubble.vue +2 -2
  448. package/src/components/Tooltip/TooltipProvider.vue +53 -0
  449. package/src/components/Tooltip/index.ts +1 -0
  450. package/src/components/Tooltip/stories/Group.vue +22 -0
  451. package/src/components/Tooltip/stories/Slots.vue +2 -2
  452. package/src/components/Tooltip/types.ts +1 -1
  453. package/src/components/Tree/Tree.api.md +76 -18
  454. package/src/components/Tree/Tree.md +144 -2
  455. package/src/components/Tree/Tree.vue +276 -97
  456. package/src/components/Tree/TreeItem.vue +263 -0
  457. package/src/components/Tree/index.ts +9 -1
  458. package/src/components/Tree/stories/DragDrop.vue +94 -0
  459. package/src/components/Tree/stories/Example.vue +11 -34
  460. package/src/components/Tree/stories/ExpandAll.vue +48 -0
  461. package/src/components/Tree/stories/Guides.vue +49 -0
  462. package/src/components/Tree/stories/ListView.vue +74 -0
  463. package/src/components/Tree/tree.cy.ts +218 -90
  464. package/src/components/Tree/types.ts +114 -24
  465. package/src/components/Tree/useTreeDragDrop.ts +203 -0
  466. package/src/components/Tree/useTreeKeyboard.ts +109 -0
  467. package/src/components/VueGridLayout/Layout.vue +1 -1
  468. package/src/components/shared/picker/PickerShell.vue +20 -15
  469. package/src/components/shared/popover/PopoverPanel.vue +67 -0
  470. package/src/components/shared/popover/popoverPanel.css +102 -0
  471. package/src/components/shared/selection/utils.ts +6 -6
  472. package/src/composables/useAutofocusOnOpen.ts +2 -0
  473. package/src/composables/useScreenSize.ts +46 -0
  474. package/src/composables/useScrollContainer.ts +108 -0
  475. package/src/composables/useShortcut.ts +3 -0
  476. package/src/data-fetching/docStore.test.ts +74 -0
  477. package/src/data-fetching/docStore.ts +18 -7
  478. package/src/data-fetching/useCall/types.ts +1 -0
  479. package/src/data-fetching/useCall/useCall.test.ts +43 -0
  480. package/src/data-fetching/useCall/useCall.ts +17 -4
  481. package/src/data-fetching/useDoc/useDoc.test.ts +61 -2
  482. package/src/data-fetching/useDoc/useDoc.ts +35 -15
  483. package/src/data-fetching/useDoctype/useDoctype.ts +6 -5
  484. package/src/data-fetching/useFrappeFetch.ts +17 -4
  485. package/src/data-fetching/useList/types.ts +1 -0
  486. package/src/data-fetching/useList/useList.test.ts +40 -0
  487. package/src/data-fetching/useList/useList.ts +20 -13
  488. package/src/data-fetching/useNewDoc/useNewDoc.ts +9 -5
  489. package/src/directives/focus.ts +3 -3
  490. package/src/index.ts +105 -74
  491. package/src/mocks/handlers.ts +8 -0
  492. package/src/molecules/editor/Editor.cy.ts +6 -1
  493. package/src/molecules/editor/EditorBubbleMenu.vue +1 -1
  494. package/src/molecules/editor/EditorContent.vue +50 -9
  495. package/src/molecules/editor/EditorFixedMenu.vue +2 -1
  496. package/src/molecules/editor/EditorFloatingMenu.vue +1 -1
  497. package/src/molecules/editor/EditorTableMenu.vue +303 -0
  498. package/src/molecules/editor/MenuItems.vue +81 -69
  499. package/src/molecules/editor/commands.ts +10 -2
  500. package/src/molecules/editor/components/AttachmentNodeView.vue +115 -0
  501. package/src/molecules/editor/components/EditorDropZone.vue +105 -0
  502. package/src/molecules/editor/components/EditorPopover.vue +89 -0
  503. package/src/molecules/editor/components/MediaNodeView.vue +156 -49
  504. package/src/molecules/editor/components/MediaResizeHandles.vue +38 -0
  505. package/src/molecules/editor/components/MediaToolbar.vue +108 -54
  506. package/src/molecules/editor/components/TableContextMenu.vue +144 -0
  507. package/src/molecules/editor/components/UploadProgressIndicator.vue +33 -0
  508. package/src/molecules/editor/components/VideoControls.vue +208 -0
  509. package/src/molecules/editor/components/font-color/ColorSwatchGrid.vue +3 -5
  510. package/src/molecules/editor/components/font-color/fontColorController.ts +28 -23
  511. package/src/molecules/editor/components/font-color/swatches.ts +1 -1
  512. package/src/molecules/editor/components/media-node-view-controller.ts +4 -4
  513. package/src/molecules/editor/components/media-node-view-utils.test.ts +0 -13
  514. package/src/molecules/editor/components/media-node-view-utils.ts +0 -10
  515. package/src/molecules/editor/components/table-color/tableCellColorController.ts +80 -0
  516. package/src/molecules/editor/components/table-size-picker/TableSizePicker.vue +92 -0
  517. package/src/molecules/editor/components/table-size-picker/tableSizePickerController.ts +40 -0
  518. package/src/molecules/editor/composables/useEditorFileDrop.ts +117 -0
  519. package/src/molecules/editor/composables/useFloatingPopup.ts +46 -2
  520. package/src/molecules/editor/composables/useNodeViewResize.test.ts +60 -11
  521. package/src/molecules/editor/composables/useNodeViewResize.ts +80 -21
  522. package/src/molecules/editor/composables/useTableCellColorState.ts +78 -0
  523. package/src/molecules/editor/composables/useWindowFileDragging.ts +68 -0
  524. package/src/molecules/editor/extensions/attachment/attachment-engine.ts +250 -0
  525. package/src/molecules/editor/extensions/attachment/attachment-extension.ts +248 -0
  526. package/src/molecules/editor/extensions/attachment/attachment.flow.test.ts +80 -0
  527. package/src/molecules/editor/extensions/attachment/attachment.roundtrip.test.ts +50 -0
  528. package/src/molecules/editor/extensions/attachment/index.ts +5 -0
  529. package/src/molecules/editor/extensions/code-block/CodeBlockComponent.css +16 -26
  530. package/src/molecules/editor/extensions/code-block/CodeBlockComponent.vue +107 -22
  531. package/src/molecules/editor/extensions/content-paste/content-paste-extension.ts +5 -3
  532. package/src/molecules/editor/extensions/iframe/IframeInsertDialog.vue +34 -7
  533. package/src/molecules/editor/extensions/iframe/IframeNodeView.vue +54 -48
  534. package/src/molecules/editor/extensions/iframe/iframe-allowlist.ts +33 -4
  535. package/src/molecules/editor/extensions/iframe/iframe-commands.ts +49 -5
  536. package/src/molecules/editor/extensions/iframe/iframe-embed-utils.ts +30 -0
  537. package/src/molecules/editor/extensions/iframe/iframe-extension.ts +4 -4
  538. package/src/molecules/editor/extensions/iframe/iframeInsertDialogController.ts +12 -1
  539. package/src/molecules/editor/extensions/iframe/index.ts +4 -4
  540. package/src/molecules/editor/extensions/iframe/useIframeDialog.ts +25 -13
  541. package/src/molecules/editor/extensions/image/image-extension.ts +29 -11
  542. package/src/molecules/editor/extensions/image-group/ImageGroupGrid.vue +27 -11
  543. package/src/molecules/editor/extensions/image-group/ImageGroupGridCell.vue +55 -11
  544. package/src/molecules/editor/extensions/image-group/ImageGroupNodeView.vue +64 -31
  545. package/src/molecules/editor/extensions/image-group/ImageGroupUploadDialog.vue +84 -40
  546. package/src/molecules/editor/extensions/image-group/image-group-extension.ts +4 -3
  547. package/src/molecules/editor/extensions/image-group/useImageGroupDialog.ts +116 -17
  548. package/src/molecules/editor/extensions/link/LinkEditorPopup.vue +214 -71
  549. package/src/molecules/editor/extensions/link/link-click-plugin.ts +10 -15
  550. package/src/molecules/editor/extensions/link/link-commands.ts +24 -3
  551. package/src/molecules/editor/extensions/link/link-extension.ts +11 -2
  552. package/src/molecules/editor/extensions/link/link-popup-controller.test.ts +49 -0
  553. package/src/molecules/editor/extensions/link/link-popup-controller.ts +67 -2
  554. package/src/molecules/editor/extensions/link/link-shortcut-plugin.ts +7 -2
  555. package/src/molecules/editor/extensions/media-drop/media-drop-extension.ts +177 -0
  556. package/src/molecules/editor/extensions/shared/media-dimensions.ts +36 -3
  557. package/src/molecules/editor/extensions/shared/media-node-ops.ts +17 -0
  558. package/src/molecules/editor/extensions/shared/media-plugin.test.ts +60 -0
  559. package/src/molecules/editor/extensions/shared/media-plugin.ts +7 -37
  560. package/src/molecules/editor/extensions/shared/media-upload-engine.test.ts +39 -2
  561. package/src/molecules/editor/extensions/shared/media-upload-engine.ts +104 -10
  562. package/src/molecules/editor/extensions/shared/media-upload-state.ts +55 -0
  563. package/src/molecules/editor/extensions/shared/media-upload-types.ts +52 -1
  564. package/src/molecules/editor/extensions/shared/suggestion-helpers.ts +20 -0
  565. package/src/molecules/editor/extensions/shared/suggestion-types.ts +7 -0
  566. package/src/molecules/editor/extensions/shared/upload-types.ts +1 -0
  567. package/src/molecules/editor/extensions/slash-commands/slash-commands-extension.test.ts +38 -0
  568. package/src/molecules/editor/extensions/slash-commands/slash-commands-extension.ts +119 -55
  569. package/src/molecules/editor/extensions/suggestion/SuggestionList.vue +62 -22
  570. package/src/molecules/editor/extensions/suggestion/SuggestionListItem.vue +1 -1
  571. package/src/molecules/editor/extensions/suggestion/createSuggestionExtension.ts +4 -0
  572. package/src/molecules/editor/extensions/table/drag-scroll.ts +154 -0
  573. package/src/molecules/editor/extensions/table/table-cell-color.ts +188 -0
  574. package/src/molecules/editor/extensions/table/table-navigation.ts +560 -0
  575. package/src/molecules/editor/extensions/table/table-selection-overlay.ts +137 -0
  576. package/src/molecules/editor/extensions/toc-node/TocNodeView.vue +1 -1
  577. package/src/molecules/editor/extensions/toc-node/toc-render.ts +7 -3
  578. package/src/molecules/editor/extensions/video/video-extension.ts +72 -5
  579. package/src/molecules/editor/extensions.ts +176 -8
  580. package/src/molecules/editor/index.ts +9 -4
  581. package/src/molecules/editor/kits.test.ts +21 -1
  582. package/src/molecules/editor/kits.ts +56 -33
  583. package/src/molecules/editor/menu.test.ts +3 -1
  584. package/src/molecules/editor/menu.ts +180 -18
  585. package/src/molecules/editor/stories/Comment.vue +4 -4
  586. package/src/molecules/editor/stories/Inline.vue +1 -1
  587. package/src/molecules/editor/stories/Primitives.vue +1 -1
  588. package/src/molecules/editor/stories/RichText.vue +1 -1
  589. package/src/molecules/editor/style.css +80 -3
  590. package/src/molecules/editor/useEditor.test.ts +4 -0
  591. package/src/molecules/editor/useEditor.ts +7 -3
  592. package/src/molecules/list/List.cy.ts +626 -0
  593. package/src/molecules/list/List.vue +93 -0
  594. package/src/molecules/list/ListCell.vue +15 -0
  595. package/src/molecules/list/ListGroup.vue +40 -0
  596. package/src/molecules/list/ListHeader.vue +62 -0
  597. package/src/molecules/list/ListHeaderCell.vue +26 -0
  598. package/src/molecules/list/ListHeaderCellSort.vue +106 -0
  599. package/src/molecules/list/ListRow.vue +55 -0
  600. package/src/molecules/list/ListRowBase.vue +103 -0
  601. package/src/molecules/list/ListRows.vue +120 -0
  602. package/src/molecules/list/index.ts +24 -0
  603. package/src/molecules/list/list-context.ts +44 -0
  604. package/src/molecules/list/list.api.md +276 -0
  605. package/src/molecules/list/list.md +119 -0
  606. package/src/molecules/list/stories/Columns.vue +103 -0
  607. package/src/molecules/list/stories/Feed.vue +105 -0
  608. package/src/molecules/list/stories/Virtual.vue +31 -0
  609. package/src/molecules/list/style.css +173 -0
  610. package/src/molecules/list/types.ts +82 -0
  611. package/src/molecules/list/useVirtualRows.ts +80 -0
  612. package/src/resources/documentResource.d.ts +28 -0
  613. package/src/resources/documentResource.js +32 -4
  614. package/src/resources/documentResource.test.ts +111 -0
  615. package/src/resources/listResource.d.ts +33 -0
  616. package/src/resources/plugin.d.ts +7 -0
  617. package/src/resources/resources.d.ts +35 -0
  618. package/src/utils/{call.js → call.ts} +50 -9
  619. package/src/utils/config.ts +16 -1
  620. package/src/utils/confirmDialog.d.ts +17 -0
  621. package/src/utils/debounce.ts +6 -7
  622. package/src/utils/dialog.cy.ts +9 -9
  623. package/src/utils/fileSize.ts +36 -0
  624. package/src/utils/fileUploadHandler.ts +46 -6
  625. package/src/utils/frappeRequest.test.ts +79 -0
  626. package/src/utils/{frappeRequest.js → frappeRequest.ts} +76 -13
  627. package/src/utils/pageMeta.ts +1 -1
  628. package/src/utils/plugin.ts +58 -0
  629. package/src/utils/{request.js → request.ts} +28 -3
  630. package/src/utils/{socketio.js → socketio.ts} +11 -1
  631. package/src/utils/theme.ts +48 -57
  632. package/src/utils/useFileUpload.ts +80 -23
  633. package/tailwind/audit-token-drift.cjs +176 -0
  634. package/tailwind/colorPalette.js +69 -8
  635. package/tailwind/colors.json +1115 -397
  636. package/tailwind/figma-tokens-to-theme.js +364 -0
  637. package/tailwind/generated/colors.json +1217 -0
  638. package/tailwind/generated/effects.json +41 -0
  639. package/tailwind/generated/radius.json +20 -0
  640. package/tailwind/generated/typography.json +479 -0
  641. package/tailwind/migrate-tokens-v2.js +453 -0
  642. package/tailwind/migrate-tokens-v2.test.js +55 -0
  643. package/tailwind/plugin.js +220 -15
  644. package/tailwind/preset.js +15 -0
  645. package/tailwind/tokens.js +35 -37
  646. package/tsconfig.base.json +2 -2
  647. package/vitepress/components/Docs/CommandPalette.vue +214 -0
  648. package/vitepress/components/Docs/Demo.vue +60 -0
  649. package/vitepress/components/Docs/EmitsTable.vue +99 -0
  650. package/vitepress/components/Docs/MobileNavSheet.vue +148 -0
  651. package/vitepress/components/Docs/OnThisPage.vue +74 -0
  652. package/vitepress/components/Docs/PrevNextBtns.vue +58 -0
  653. package/vitepress/components/Docs/PropsTable.vue +185 -0
  654. package/vitepress/components/Docs/Search.vue +34 -0
  655. package/vitepress/components/Docs/Sidebar.vue +91 -0
  656. package/vitepress/components/Docs/SlotsTable.vue +99 -0
  657. package/vitepress/components/Docs/index.ts +13 -0
  658. package/vitepress/components/Docs/sidebarList.ts +33 -0
  659. package/vitepress/components/Layout.vue +58 -0
  660. package/vitepress/components/Navbar.vue +126 -0
  661. package/vitepress/components/index.ts +3 -0
  662. package/vitepress/composables/index.ts +3 -0
  663. package/vitepress/composables/useTheme.ts +39 -0
  664. package/vitepress/css/fonts/Inter-Italic.var.woff2 +0 -0
  665. package/vitepress/css/fonts/Inter.var.woff2 +0 -0
  666. package/vitepress/css/style.css +198 -0
  667. package/vitepress/index.d.ts +23 -0
  668. package/vitepress/index.node.ts +175 -0
  669. package/vitepress/index.ts +17 -0
  670. package/vitepress/plugins/colocatedComponentDocs.ts +164 -0
  671. package/vitepress/plugins/componentTransformer.ts +286 -0
  672. package/vitepress/plugins/index.ts +12 -0
  673. package/vitepress/state.ts +10 -0
  674. package/vitepress/theme/index.ts +21 -0
  675. package/frappe/telemetry/utils.ts +0 -15
  676. package/src/components/Badge/stories/Themes.vue +0 -10
  677. package/src/components/Badge/stories/Variants.vue +0 -10
  678. package/src/components/Button/stories/Icons.vue +0 -27
  679. package/src/components/Button/stories/Sizes.vue +0 -11
  680. package/src/components/Button/stories/Themes.vue +0 -10
  681. package/src/components/Button/stories/Variants.vue +0 -10
  682. package/src/components/Popover/stories/Hover.vue +0 -14
  683. package/src/components/Resource.vue +0 -21
  684. package/src/components/Sidebar/stories/Example.vue +0 -76
  685. package/src/components/Spinner.vue +0 -3
  686. package/src/components/TabButtons/TabButtons.story.vue +0 -22
  687. package/src/utils/plugin.js +0 -24
  688. /package/src/components/Dropdown/stories/{02_Shortcuts.vue → Shortcuts.vue} +0 -0
  689. /package/src/components/Dropdown/stories/{01_Simple.vue → Simple.vue} +0 -0
  690. /package/src/components/Dropdown/stories/{03_Submenus.vue → Submenus.vue} +0 -0
  691. /package/src/components/Dropdown/stories/{04_Switches.vue → Switches.vue} +0 -0
  692. /package/src/components/{Dropdown/DropdownRenderContentAsChild.vue → Menu/MenuRenderContentAsChild.vue} +0 -0
@@ -3,14 +3,15 @@ import { VueNodeViewRenderer } from '@tiptap/vue-3'
3
3
  import ImageGroupNodeView from './ImageGroupNodeView.vue'
4
4
  import { buildImageGroupCommands } from './image-group-commands'
5
5
  import { clampColumns, DEFAULT_COLUMNS } from './image-group-utils'
6
- import type { UploadedFile } from '#utils/useFileUpload'
6
+ import type { UploadFunction } from '#molecules/editor/extensions/shared/media-upload-engine'
7
7
 
8
8
  export interface ImageGroupOptions {
9
9
  /**
10
- * Function to handle image uploads.
10
+ * Function to handle image uploads. Receives optional request options
11
+ * (abort signal + progress callback) as the second argument.
11
12
  * @default null
12
13
  */
13
- uploadFunction: ((file: File) => Promise<UploadedFile>) | null
14
+ uploadFunction: UploadFunction | null
14
15
  HTMLAttributes: Record<string, unknown>
15
16
  }
16
17
 
@@ -1,9 +1,16 @@
1
1
  import { computed, onBeforeUnmount, ref } from 'vue'
2
2
  import type { Editor } from '@tiptap/core'
3
+ import { fileSizeLimitMessage } from '#utils/fileSize'
3
4
  import {
4
5
  resolveUploadOptions,
5
- uploadFilesParallel,
6
+ uploadFile,
6
7
  } from '#molecules/editor/extensions/shared/media-upload-engine'
8
+ import {
9
+ abortUpload,
10
+ deleteUploadProgress,
11
+ setUploadProgress,
12
+ updateUploadProgress,
13
+ } from '#molecules/editor/extensions/shared/media-upload-state'
7
14
  import type {
8
15
  ExistingImage,
9
16
  MediaUploadOptions,
@@ -15,7 +22,12 @@ import {
15
22
  filterImageFiles,
16
23
  } from './image-group-utils'
17
24
 
18
- /** A single grid item: either a staged file or an already-existing image. */
25
+ /**
26
+ * A single grid item: either a staged file or an already-existing image.
27
+ *
28
+ * Transient progress/cancel state is NOT stored here — it lives in the shared
29
+ * `uploadProgressMap` (keyed by `id`), the same mechanism media node views use.
30
+ */
19
31
  export interface ImageItem {
20
32
  type: 'file' | 'existing'
21
33
  id: string
@@ -23,6 +35,9 @@ export interface ImageItem {
23
35
  existing?: ExistingImage
24
36
  /** Caption for a not-yet-uploaded file (existing items carry it on `existing`). */
25
37
  alt?: string
38
+ status?: 'idle' | 'uploading' | 'uploaded' | 'failed'
39
+ error?: string
40
+ uploaded?: ExistingImage
26
41
  }
27
42
 
28
43
  export interface UseImageGroupDialogArgs {
@@ -32,7 +47,12 @@ export interface UseImageGroupDialogArgs {
32
47
 
33
48
  /** Build an `ImageItem` for a staged file (deterministic id). */
34
49
  function makeFileItem(file: File): ImageItem {
35
- return { type: 'file', file, id: fileItemId(file) }
50
+ return {
51
+ type: 'file',
52
+ file,
53
+ id: fileItemId(file),
54
+ status: 'idle',
55
+ }
36
56
  }
37
57
 
38
58
  /** Build an `ImageItem` for an existing image (deterministic id). */
@@ -75,9 +95,7 @@ export function useImageGroupDialog(args: UseImageGroupDialogArgs) {
75
95
  const existingItems = (opts.existing ?? []).map(makeExistingItem)
76
96
  const fileItems = opts.files.map(makeFileItem)
77
97
  images.value =
78
- args.mode() === 'edit'
79
- ? [...existingItems, ...fileItems]
80
- : fileItems
98
+ args.mode() === 'edit' ? [...existingItems, ...fileItems] : fileItems
81
99
  uploadedCount.value = 0
82
100
  totalCount.value = 0
83
101
  hasUploadError.value = false
@@ -152,19 +170,71 @@ export function useImageGroupDialog(args: UseImageGroupDialogArgs) {
152
170
  }
153
171
 
154
172
  /** Upload the staged files (in current UI order). Never throws. */
155
- async function uploadStagedFiles(files: File[]): Promise<UploadResult[]> {
156
- if (files.length === 0) return []
173
+ async function uploadItem(item: ImageItem): Promise<UploadResult> {
174
+ if (!item.file)
175
+ return { success: false, error: new Error('No file selected') }
176
+ if (item.uploaded)
177
+ return { success: true, file: { file_url: item.uploaded.src } }
157
178
  const options = resolveOptions()
158
- uploading.value = true
159
- hasUploadError.value = false
160
- totalCount.value = files.length
161
- uploadedCount.value = 0
179
+ const validationError = fileSizeLimitMessage(item.file)
180
+ if (validationError) {
181
+ item.status = 'failed'
182
+ item.error = validationError
183
+ hasUploadError.value = true
184
+ return { success: false, error: new Error(validationError) }
185
+ }
186
+
187
+ const abortController = new AbortController()
188
+ setUploadProgress(item.id, {
189
+ loaded: 0,
190
+ total: item.file.size,
191
+ percent: 0,
192
+ abort: () => abortController.abort(),
193
+ })
194
+ item.status = 'uploading'
195
+ item.error = ''
196
+
162
197
  try {
163
- return await uploadFilesParallel(files, options, {
164
- onProgress: (done) => {
165
- uploadedCount.value = done
198
+ const result = await uploadFile(item.file, options, {
199
+ signal: abortController.signal,
200
+ onProgress: (progress) => {
201
+ updateUploadProgress(item.id, progress)
166
202
  },
167
203
  })
204
+ item.status = 'uploaded'
205
+ item.uploaded = {
206
+ src: result.file_url,
207
+ alt: item.alt || (result.file_name as string) || '',
208
+ }
209
+ return { success: true, file: result }
210
+ } catch (error) {
211
+ item.status = 'failed'
212
+ item.error = abortController.signal.aborted
213
+ ? 'Upload cancelled'
214
+ : (error as Error)?.message || 'Upload failed'
215
+ hasUploadError.value = true
216
+ return { success: false, error: error as Error }
217
+ } finally {
218
+ deleteUploadProgress(item.id)
219
+ }
220
+ }
221
+
222
+ async function uploadStagedFiles(
223
+ items: ImageItem[],
224
+ ): Promise<UploadResult[]> {
225
+ if (items.length === 0) return []
226
+ uploading.value = true
227
+ hasUploadError.value = false
228
+ totalCount.value = items.length
229
+ uploadedCount.value = items.filter((item) => item.uploaded).length
230
+ try {
231
+ return await Promise.all(
232
+ items.map(async (item) => {
233
+ const result = await uploadItem(item)
234
+ uploadedCount.value += 1
235
+ return result
236
+ }),
237
+ )
168
238
  } finally {
169
239
  uploading.value = false
170
240
  }
@@ -176,8 +246,11 @@ export function useImageGroupDialog(args: UseImageGroupDialogArgs) {
176
246
  * UI order) and `hasUploadError` flags the failures so the dialog stays open.
177
247
  */
178
248
  async function buildFinalImages(): Promise<ExistingImage[]> {
179
- const staged = fileItems()
180
- const results = await uploadStagedFiles(staged)
249
+ const stagedItems = images.value.filter(
250
+ (item) => item.type === 'file' && item.file && !item.uploaded,
251
+ )
252
+ const staged = stagedItems.map((item) => item.file as File)
253
+ const results = await uploadStagedFiles(stagedItems)
181
254
 
182
255
  const fileToImage = new Map<File, ExistingImage>()
183
256
  staged.forEach((file, index) => {
@@ -199,6 +272,11 @@ export function useImageGroupDialog(args: UseImageGroupDialogArgs) {
199
272
  for (const item of images.value) {
200
273
  if (item.type === 'existing' && item.existing) {
201
274
  final.push(item.existing)
275
+ } else if (item.uploaded) {
276
+ final.push({
277
+ src: item.uploaded.src,
278
+ alt: item.alt ?? item.uploaded.alt,
279
+ })
202
280
  } else if (item.file) {
203
281
  const uploaded = fileToImage.get(item.file)
204
282
  if (uploaded) {
@@ -209,6 +287,25 @@ export function useImageGroupDialog(args: UseImageGroupDialogArgs) {
209
287
  return final
210
288
  }
211
289
 
290
+ async function retryImage(index: number) {
291
+ const item = images.value[index]
292
+ if (!item || item.type !== 'file') return
293
+ hasUploadError.value = false
294
+ uploading.value = true
295
+ totalCount.value = 1
296
+ uploadedCount.value = 0
297
+ try {
298
+ await uploadItem(item)
299
+ } finally {
300
+ uploadedCount.value = 1
301
+ uploading.value = false
302
+ }
303
+ }
304
+
305
+ function abortAll() {
306
+ images.value.forEach((item) => abortUpload(item.id))
307
+ }
308
+
212
309
  return {
213
310
  images,
214
311
  columns,
@@ -226,5 +323,7 @@ export function useImageGroupDialog(args: UseImageGroupDialogArgs) {
226
323
  fileItems,
227
324
  matchesFileSet,
228
325
  buildFinalImages,
326
+ retryImage,
327
+ abortAll,
229
328
  }
230
329
  }
@@ -1,74 +1,80 @@
1
1
  <template>
2
- <div
3
- class="p-2 w-72 flex items-center gap-2 bg-surface-white shadow-xl rounded"
2
+ <EditorPopover
3
+ dialog-label="Edit link"
4
+ content-class="flex min-w-60 max-w-80 items-center gap-1 rounded-md p-1"
5
+ :autofocus="false"
4
6
  >
5
- <TextInput
6
- v-if="edit"
7
- ref="input"
8
- type="text"
9
- class="w-full"
10
- placeholder="https://example.com"
11
- v-model="_href"
12
- @keydown.enter="submitLink"
13
- @keydown.esc="$emit('close')"
14
- />
15
- <a
16
- v-else
17
- class="text-ink-gray-700 underline text-sm flex-1 truncate pl-1"
18
- :title="_href"
19
- :href="_href"
20
- target="_blank"
21
- rel="noopener noreferrer"
22
- >
23
- {{ _href }}
24
- </a>
25
- <div class="shrink-0 flex items-center gap-1.5 ml-auto">
26
- <template v-if="edit">
27
- <Button
28
- @click="submitLink"
29
- tooltip="Submit"
30
- :icon="'lucide-check'"
31
- variant="subtle"
7
+ <template v-if="edit">
8
+ <div
9
+ class="flex-1"
10
+ :class="{ 'editor-link-shake': shake }"
11
+ @animationend="shake = false"
12
+ >
13
+ <TextInput
14
+ ref="input"
15
+ type="text"
16
+ class="w-full"
17
+ placeholder="https://example.com"
18
+ v-model="_href"
19
+ @keydown.enter="submitLink"
32
20
  />
33
- <Button
34
- @click="props.href ? (edit = false) : $emit('updateHref', '')"
35
- tooltip="Exit"
36
- :icon="'lucide-x'"
37
- variant="subtle"
38
- />
39
- </template>
40
- <template v-else>
41
- <Button
42
- @click="copyLink"
43
- tooltip="Copy"
44
- :icon="'lucide-copy'"
45
- variant="subtle"
46
- />
47
- <Button
48
- @click="edit = true"
49
- tooltip="Edit"
50
- :icon="'lucide-pencil'"
51
- variant="subtle"
52
- />
53
- <Button
54
- tooltip="Remove"
55
- variant="subtle"
56
- @click="$emit('updateHref', '')"
57
- :icon="'lucide-link-2-off'"
58
- />
59
- </template>
60
- </div>
61
- </div>
21
+ </div>
22
+ <Button
23
+ v-if="href"
24
+ size="xs"
25
+ variant="ghost"
26
+ tooltip="Remove link"
27
+ :icon="'lucide-link-2-off'"
28
+ @click="$emit('updateHref', '')"
29
+ />
30
+ <Button
31
+ size="xs"
32
+ variant="ghost"
33
+ tooltip="Apply"
34
+ :icon="'lucide-check'"
35
+ @click="submitLink"
36
+ />
37
+ </template>
38
+
39
+ <template v-else>
40
+ <span
41
+ class="lucide-globe ml-1.5 size-4 shrink-0 text-ink-gray-5"
42
+ aria-hidden="true"
43
+ />
44
+ <div class="pl-1.5 flex-1 min-w-0 flex">
45
+ <a
46
+ class="truncate text-sm text-ink-gray-9 border-b border-outline-gray-2 hover:border-outline-gray-3"
47
+ :title="_href"
48
+ :href="_href"
49
+ target="_blank"
50
+ rel="noopener noreferrer"
51
+ >
52
+ {{ _href }}
53
+ </a>
54
+ </div>
55
+ <Button
56
+ size="xs"
57
+ variant="ghost"
58
+ tooltip="Copy link"
59
+ :icon="'lucide-copy'"
60
+ @click="copyLink"
61
+ />
62
+ <Button size="xs" variant="ghost" @click="enterEditMode">Edit</Button>
63
+ </template>
64
+ </EditorPopover>
62
65
  </template>
63
66
 
64
67
  <script setup lang="ts">
65
- import { onMounted, ref, useTemplateRef, nextTick } from 'vue'
68
+ import { onBeforeUnmount, onMounted, ref, useTemplateRef, nextTick } from 'vue'
66
69
  import Button from '#components/Button/Button.vue'
67
70
  import TextInput from '#components/TextInput/TextInput.vue'
71
+ import EditorPopover from '#molecules/editor/components/EditorPopover.vue'
68
72
  import { isSafeUrl } from '#molecules/editor/extensions/shared/url-safety'
69
73
 
70
74
  const props = defineProps<{
71
75
  href: string
76
+ /** Open directly in edit mode. Defaults to "edit when there is no href". */
77
+ startInEdit?: boolean
72
78
  }>()
73
79
 
74
80
  const emit = defineEmits<{
@@ -76,15 +82,65 @@ const emit = defineEmits<{
76
82
  (e: 'close'): void
77
83
  }>()
78
84
 
85
+ const ALLOWED_SCHEMES = ['http', 'https', 'mailto', 'tel']
86
+
79
87
  const _href = ref(props.href)
80
88
  const input = useTemplateRef('input')
81
- const edit = ref(!props.href)
89
+ // New links open straight into edit mode; existing links open in the view —
90
+ // unless the caller forces it (e.g. ⌘K on a selected link → edit mode).
91
+ const edit = ref(props.startInEdit ?? !props.href)
92
+ const shake = ref(false)
93
+
94
+ /**
95
+ * Make a bare entry usable as a link: `github.com` → `https://github.com`.
96
+ * Anything that already carries a scheme (`http:`, `mailto:`, `tel:`, …) or is
97
+ * empty is left untouched, matching the extension's `defaultProtocol: 'https'`.
98
+ */
99
+ const normalizeHref = (value: string): string => {
100
+ const trimmed = value.trim()
101
+ if (!trimmed) return ''
102
+ if (/^[a-z][a-z0-9+.-]*:/i.test(trimmed)) return trimmed
103
+ return `https://${trimmed}`
104
+ }
105
+
106
+ /**
107
+ * Stricter than {@link isSafeUrl} alone: the URL parser accepts bare tokens
108
+ * (`asdf` → `https://asdf`) as valid hostnames, so for http(s) links we also
109
+ * require a dotted host (or localhost). Keeps "not a link" text from being
110
+ * accepted once auto-prefixed.
111
+ */
112
+ const isValidLinkHref = (href: string): boolean => {
113
+ if (!isSafeUrl(href, { allowedSchemes: ALLOWED_SCHEMES })) return false
114
+ let url: URL
115
+ try {
116
+ url = new URL(href)
117
+ } catch {
118
+ return false
119
+ }
120
+ if (url.protocol === 'http:' || url.protocol === 'https:') {
121
+ return url.hostname === 'localhost' || url.hostname.includes('.')
122
+ }
123
+ return true
124
+ }
125
+
126
+ const triggerShake = () => {
127
+ // Restart the animation even if it is mid-flight.
128
+ shake.value = false
129
+ requestAnimationFrame(() => {
130
+ shake.value = true
131
+ })
132
+ }
82
133
 
83
134
  const submitLink = () => {
84
- // Empty submits through as the "remove link" action; otherwise require a
85
- // well-formed http(s) URL (shared url-safety, not the legacy isValidUrl).
86
- if (_href.value === '' || isSafeUrl(_href.value, { allowedSchemes: ['http', 'https', 'mailto', 'tel'] })) {
87
- emit('updateHref', _href.value)
135
+ const normalized = normalizeHref(_href.value)
136
+ // Empty submits through as the "remove link" action; otherwise the URL must
137
+ // be well-formed and look like a real link (see isValidLinkHref).
138
+ if (normalized === '') {
139
+ emit('updateHref', '')
140
+ } else if (isValidLinkHref(normalized)) {
141
+ emit('updateHref', normalized)
142
+ } else {
143
+ triggerShake()
88
144
  }
89
145
  }
90
146
 
@@ -97,14 +153,101 @@ const copyLink = async () => {
97
153
  }
98
154
  }
99
155
 
100
- onMounted(async () => {
156
+ const focusInput = async () => {
101
157
  await nextTick()
102
- if (input.value?.el) {
103
- // preventScroll: the popup is teleported to <body> and positioned async by
104
- // floating-ui, so at focus time the input may still be at the top of the
105
- // document a plain focus() would scroll the page up to it.
106
- input.value.el.focus({ preventScroll: true })
107
- input.value.el.select()
158
+ const el = input.value?.el
159
+ if (!el) return
160
+ // preventScroll: the popup is teleported to <body> and positioned async by
161
+ // floating-ui, so at focus time the input may still be at the top of the
162
+ // document — a plain focus() would scroll the page up to it.
163
+ el.focus({ preventScroll: true })
164
+ el.select()
165
+ }
166
+
167
+ const enterEditMode = () => {
168
+ edit.value = true
169
+ focusInput()
170
+ }
171
+
172
+ /**
173
+ * When creating a brand-new link, offer a URL already on the clipboard as the
174
+ * default — the common "copy a URL, select some text, ⌘K" flow. The value is
175
+ * left selected so a keystroke replaces it.
176
+ */
177
+ const maybePrefillFromClipboard = async () => {
178
+ if (props.href || _href.value) return
179
+ // Clipboard read needs a secure context (https/localhost); it is unavailable
180
+ // over plain http (e.g. *.frappe.test dev), where this simply no-ops.
181
+ if (!navigator.clipboard?.readText) return
182
+ let text = ''
183
+ try {
184
+ text = (await navigator.clipboard.readText()).trim()
185
+ } catch {
186
+ return // No permission / not focused.
108
187
  }
188
+ const normalized = normalizeHref(text)
189
+ if (!isValidLinkHref(normalized)) return
190
+ _href.value = normalized
191
+ await nextTick()
192
+ input.value?.el?.select()
193
+ }
194
+
195
+ /**
196
+ * Escape is owned here (the floating popup is created with `closeOnEscape:
197
+ * false`). When editing an existing link the first Escape discards the edit
198
+ * and steps back to the view; in every other case it closes the popup.
199
+ */
200
+ const handleEscape = () => {
201
+ if (edit.value && props.href) {
202
+ _href.value = props.href
203
+ edit.value = false
204
+ return
205
+ }
206
+ emit('close')
207
+ }
208
+
209
+ const onDocumentKeydown = (event: KeyboardEvent) => {
210
+ if (event.key !== 'Escape') return
211
+ // Consume it so a single Escape doesn't also close a surrounding dialog.
212
+ event.preventDefault()
213
+ event.stopPropagation()
214
+ handleEscape()
215
+ }
216
+
217
+ onMounted(async () => {
218
+ document.addEventListener('keydown', onDocumentKeydown, true)
219
+ if (edit.value) {
220
+ focusInput()
221
+ await maybePrefillFromClipboard()
222
+ }
223
+ })
224
+
225
+ onBeforeUnmount(() => {
226
+ document.removeEventListener('keydown', onDocumentKeydown, true)
109
227
  })
110
228
  </script>
229
+
230
+ <style scoped>
231
+ .editor-link-shake {
232
+ animation: editor-link-shake 160ms ease-in-out;
233
+ }
234
+
235
+ @keyframes editor-link-shake {
236
+ 0%,
237
+ 100% {
238
+ transform: translateX(0);
239
+ }
240
+ 25% {
241
+ transform: translateX(-3px);
242
+ }
243
+ 75% {
244
+ transform: translateX(3px);
245
+ }
246
+ }
247
+
248
+ @media (prefers-reduced-motion: reduce) {
249
+ .editor-link-shake {
250
+ animation: none;
251
+ }
252
+ }
253
+ </style>
@@ -1,5 +1,4 @@
1
1
  import type { Editor } from '@tiptap/core'
2
- import { getMarkRange } from '@tiptap/core'
3
2
  import type { MarkType } from '@tiptap/pm/model'
4
3
  import { Plugin, PluginKey } from '@tiptap/pm/state'
5
4
 
@@ -17,41 +16,37 @@ export interface LinkClickPluginOptions {
17
16
  * - Not editable → `return false` (let the browser follow the link).
18
17
  * - Not on a link → `return false`.
19
18
  * - Cmd/Meta-click → open the href in a new tab; `return true`.
20
- * - Plain click → open the link editor popup; `return true`.
19
+ * - Plain click → `return false`; let the caret land where the user clicked so
20
+ * link text stays editable. The link editor is opened via the hover affordance
21
+ * / `Mod-k`, not by trapping a plain click.
21
22
  *
22
23
  * Always returns a boolean so ProseMirror knows whether the click was handled
23
24
  * (the legacy handler returned `undefined` from the editable/meta branches).
24
25
  */
25
26
  export function linkClickPlugin(options: LinkClickPluginOptions): Plugin {
26
- const { editor, type } = options
27
+ const { editor } = options
27
28
  return new Plugin({
28
29
  key: new PluginKey('handleLinkClick'),
29
30
  props: {
30
- handleClick: (view, pos, event): boolean => {
31
+ handleClick: (view, _pos, event): boolean => {
31
32
  if (!editor.isEditable) return false
32
33
 
33
34
  const target = event.target as HTMLElement | null
34
35
  const anchor = target?.closest('a[href]')
35
36
  if (!anchor || !view.dom.contains(anchor)) return false
36
37
 
37
- event.preventDefault()
38
-
39
38
  if (event.metaKey || event.ctrlKey) {
39
+ event.preventDefault()
40
40
  const url = anchor.getAttribute('href') || undefined
41
41
  if (url) window.open(url, '_blank', 'noopener,noreferrer')
42
42
  editor.commands.focus()
43
43
  return true
44
44
  }
45
45
 
46
- const resolved = view.state.doc.resolve(pos)
47
- const range = getMarkRange(resolved, type)
48
- if (range) {
49
- editor.chain().setTextSelection(range).run()
50
- }
51
- requestAnimationFrame(() => {
52
- if (!editor.isDestroyed) editor.commands.openLinkEditor()
53
- })
54
- return true
46
+ // Plain click: don't preventDefault or reselect the whole link range —
47
+ // that stole focus into the editor popup and trapped the caret. Let the
48
+ // browser place the caret where the user clicked.
49
+ return false
55
50
  },
56
51
  },
57
52
  })
@@ -4,10 +4,23 @@ import { mapStoredRange } from '#molecules/editor/extensions/shared/node-view'
4
4
  import { openLinkPopup } from './link-popup-controller'
5
5
  import type { VirtualReference } from '#molecules/editor/composables/useFloatingPopup'
6
6
 
7
+ /** Options accepted by the `openLinkEditor` command. */
8
+ export interface OpenLinkEditorOptions {
9
+ /**
10
+ * Force the popup to open in edit mode. When omitted it defaults to edit mode
11
+ * for new links and view mode for existing links. The ⌘K shortcut passes
12
+ * `true` for an explicit (non-collapsed) selection so editing a link's URL is
13
+ * one keystroke, while a plain click on a link stays in view mode.
14
+ */
15
+ startInEdit?: boolean
16
+ }
17
+
7
18
  /**
8
- * A TipTap command factory: `() => ({ editor }) => boolean`.
19
+ * A TipTap command factory: `(options?) => ({ editor }) => boolean`.
9
20
  */
10
- type LinkEditorCommand = () => (props: { editor: Editor }) => boolean
21
+ type LinkEditorCommand = (
22
+ options?: OpenLinkEditorOptions,
23
+ ) => (props: { editor: Editor }) => boolean
11
24
 
12
25
  /**
13
26
  * Build the `openLinkEditor` command for the given link mark type.
@@ -24,7 +37,7 @@ type LinkEditorCommand = () => (props: { editor: Editor }) => boolean
24
37
  * after the `await` is guarded by `editor.isDestroyed`.
25
38
  */
26
39
  export function buildOpenLinkEditor(markType: MarkType): LinkEditorCommand {
27
- return () =>
40
+ return (options?: OpenLinkEditorOptions) =>
28
41
  ({ editor }: { editor: Editor }): boolean => {
29
42
  const { state } = editor
30
43
  const { from, to } = state.selection
@@ -51,8 +64,16 @@ export function buildOpenLinkEditor(markType: MarkType): LinkEditorCommand {
51
64
  const show = () => {
52
65
  void openLinkPopup({
53
66
  href: existingHref,
67
+ startInEdit: options?.startInEdit ?? !existingHref,
54
68
  anchor: editor.view.dom,
55
69
  virtualReference: selectionReference(editor, range),
70
+ onEscape: () => {
71
+ // Escape leaves focus on <body>; return it to the editor so the
72
+ // user can keep typing where they left off.
73
+ if (!editor.isDestroyed) {
74
+ editor.commands.focus(null, { scrollIntoView: false })
75
+ }
76
+ },
56
77
  }).then((href) => {
57
78
  // Cancelled, or the editor went away while the popup was open.
58
79
  if (href === null || editor.isDestroyed) return