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
@@ -0,0 +1,560 @@
1
+ /**
2
+ * Spreadsheet-style cell navigation for tables.
3
+ *
4
+ * Two modes, distinguished purely by the selection type (no extra state):
5
+ * - **Navigate** — the selection is a single-cell `CellSelection`. The cell is
6
+ * highlighted (prosemirror-tables draws `.selectedCell`; styled in style.css),
7
+ * there's no caret, and arrow keys move between cells.
8
+ * - **Edit** — a normal `TextSelection` inside the cell. Arrows move the caret
9
+ * as usual; Escape returns to navigate.
10
+ *
11
+ * Flow: click a cell → navigate (highlight). Enter (or a second click, or
12
+ * typing) → edit. Edit mode is sticky: clicks inside the cell only move the
13
+ * caret; it exits only via Enter, Escape (→ navigate) or Tab (→ next cell).
14
+ * Arrow keys in navigate mode → move the active cell; at the top/bottom edge
15
+ * they leave the table (so keyboard users are never trapped inside it).
16
+ *
17
+ * Runs at a high priority so its `handleKeyDown` precedes the Table extension's
18
+ * built-in arrow handling (which would otherwise collapse a CellSelection to a
19
+ * caret on arrow press).
20
+ */
21
+ import { Extension } from '@tiptap/core'
22
+ import type { Editor } from '@tiptap/core'
23
+ import { Plugin, Selection, TextSelection } from '@tiptap/pm/state'
24
+ import type { EditorView } from '@tiptap/pm/view'
25
+ import type { ResolvedPos } from '@tiptap/pm/model'
26
+ import {
27
+ CellSelection,
28
+ TableMap,
29
+ cellAround,
30
+ columnResizingPluginKey,
31
+ goToNextCell,
32
+ isInTable,
33
+ nextCell,
34
+ selectionCell,
35
+ } from '@tiptap/pm/tables'
36
+ import { trackPointerDrag } from './drag-scroll'
37
+
38
+ type Axis = 'horiz' | 'vert'
39
+
40
+ const ARROWS = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'] as const
41
+
42
+ const LONG_PRESS_MS = 500
43
+ const LONG_PRESS_SLOP = 8
44
+
45
+ /**
46
+ * In edit mode, true when a plain arrow press would move the caret out of the
47
+ * current cell (so we can swallow it — the cell is a sandbox; leave via Escape or
48
+ * Tab). Caret movement that stays inside the cell is left to the default.
49
+ */
50
+ function arrowLeavesCell(view: EditorView, event: KeyboardEvent): boolean {
51
+ if (event.metaKey || event.ctrlKey || event.altKey || event.shiftKey) {
52
+ return false
53
+ }
54
+ const { state } = view
55
+ if (!state.selection.empty) return false
56
+ const $head = state.selection.$head
57
+ const $cell = cellAround($head)
58
+ if (!$cell) return false
59
+ const cell = $cell.nodeAfter
60
+ if (!cell) return false
61
+ // First / last caret positions inside the cell, and their textblocks.
62
+ const firstPos = Selection.near(state.doc.resolve($cell.pos + 1), 1).from
63
+ const lastPos = Selection.near(
64
+ state.doc.resolve($cell.pos + cell.nodeSize - 1),
65
+ -1,
66
+ ).from
67
+ switch (event.key) {
68
+ case 'ArrowLeft':
69
+ return $head.pos <= firstPos
70
+ case 'ArrowRight':
71
+ return $head.pos >= lastPos
72
+ case 'ArrowUp':
73
+ // At the top line of the cell's first block.
74
+ return (
75
+ view.endOfTextblock('up') &&
76
+ $head.start() === state.doc.resolve(firstPos).start()
77
+ )
78
+ case 'ArrowDown':
79
+ // At the bottom line of the cell's last block.
80
+ return (
81
+ view.endOfTextblock('down') &&
82
+ $head.start() === state.doc.resolve(lastPos).start()
83
+ )
84
+ default:
85
+ return false
86
+ }
87
+ }
88
+
89
+ /** Select a whole cell (navigate mode), highlighting it. */
90
+ function selectCell(editor: Editor, pos: number): boolean {
91
+ const { state, view } = editor
92
+ view.dispatch(
93
+ state.tr
94
+ .setSelection(CellSelection.create(state.doc, pos))
95
+ .scrollIntoView(),
96
+ )
97
+ return true
98
+ }
99
+
100
+ /** Drop the caret into a cell (edit mode), at the end of its content. */
101
+ function editCell(editor: Editor, $cell: ResolvedPos, at?: number): boolean {
102
+ const { state, view } = editor
103
+ const cell = $cell.nodeAfter
104
+ if (!cell) return false
105
+ const pos = at ?? $cell.pos + cell.nodeSize - 1
106
+ const selection = TextSelection.near(state.doc.resolve(pos), -1)
107
+ view.dispatch(state.tr.setSelection(selection).scrollIntoView())
108
+ return true
109
+ }
110
+
111
+ /** The table node enclosing a cell position, with its map and start offset. */
112
+ function tableAround($cell: ResolvedPos) {
113
+ const table = $cell.node(-1)
114
+ return { table, start: $cell.start(-1), map: TableMap.get(table) }
115
+ }
116
+
117
+ /**
118
+ * Leave the table vertically: put the caret in the nearest textblock before/
119
+ * after it, creating an adjacent paragraph when the table is the document's
120
+ * first/last node — so navigate mode is never a keyboard trap.
121
+ */
122
+ function exitTable(editor: Editor, dir: 1 | -1): boolean {
123
+ const { state, view } = editor
124
+ const $cell = selectionCell(state)
125
+ let depth = $cell.depth
126
+ while (depth > 0 && $cell.node(depth).type.name !== 'table') depth--
127
+ if (!depth) return true
128
+ const pos = dir === 1 ? $cell.after(depth) : $cell.before(depth)
129
+ const target = Selection.findFrom(state.doc.resolve(pos), dir, true)
130
+ if (target) {
131
+ view.dispatch(state.tr.setSelection(target).scrollIntoView())
132
+ return true
133
+ }
134
+ const paragraph = state.schema.nodes.paragraph
135
+ if (!paragraph) return true
136
+ const tr = state.tr.insert(pos, paragraph.create())
137
+ tr.setSelection(TextSelection.create(tr.doc, pos + 1)).scrollIntoView()
138
+ view.dispatch(tr)
139
+ return true
140
+ }
141
+
142
+ /**
143
+ * Move the active cell one step along an axis. A horizontal edge is a consumed
144
+ * no-op; a vertical edge exits the table.
145
+ */
146
+ function moveCell(editor: Editor, axis: Axis, dir: number): boolean {
147
+ const { state } = editor
148
+ const $next = nextCell(selectionCell(state), axis, dir)
149
+ if ($next) return selectCell(editor, $next.pos)
150
+ if (axis === 'vert') return exitTable(editor, dir as 1 | -1)
151
+ return true
152
+ }
153
+
154
+ /**
155
+ * Grow/shrink the cell selection one step along an axis (spreadsheet-style
156
+ * Shift-Arrow): keep the anchor cell fixed, move only the head cell. The
157
+ * selection is always rectangular, so moving the head fills in the block.
158
+ * No-op (but consumed) at an edge.
159
+ */
160
+ function extendCell(editor: Editor, axis: Axis, dir: number): boolean {
161
+ const { state, view } = editor
162
+ const sel = state.selection
163
+ if (!(sel instanceof CellSelection)) return false
164
+ const $next = nextCell(sel.$headCell, axis, dir)
165
+ if ($next) {
166
+ view.dispatch(
167
+ state.tr
168
+ .setSelection(new CellSelection(sel.$anchorCell, $next))
169
+ .scrollIntoView(),
170
+ )
171
+ }
172
+ return true
173
+ }
174
+
175
+ /**
176
+ * Tab / Shift-Tab: move to the next/previous cell and land in navigate mode
177
+ * (highlight, no caret), exiting edit mode. Wraps across rows via the table's
178
+ * own `goToNextCell`; Tab on the very last cell appends a row first
179
+ * (spreadsheet convention, and what the stock TipTap Tab shortcut did before
180
+ * this plugin intercepted it).
181
+ */
182
+ function tabToCell(editor: Editor, dir: 1 | -1): boolean {
183
+ const { state, view } = editor
184
+ if (!isInTable(state)) return false
185
+ if (!goToNextCell(dir)(state, view.dispatch)) {
186
+ if (dir !== 1) return true
187
+ if (!editor.can().addRowAfter()) return true
188
+ editor.chain().addRowAfter().run()
189
+ if (!goToNextCell(dir)(editor.state, view.dispatch)) return true
190
+ }
191
+ const $cell = cellAround(editor.state.selection.$head)
192
+ if ($cell) selectCell(editor, $cell.pos)
193
+ return true
194
+ }
195
+
196
+ /** Select every cell of the table containing `$cell`. */
197
+ function selectWholeTable(editor: Editor, $cell: ResolvedPos): boolean {
198
+ const { state, view } = editor
199
+ const { start, map } = tableAround($cell)
200
+ view.dispatch(
201
+ state.tr.setSelection(
202
+ new CellSelection(
203
+ state.doc.resolve(start + map.map[0]),
204
+ state.doc.resolve(start + map.map[map.map.length - 1]),
205
+ ),
206
+ ),
207
+ )
208
+ return true
209
+ }
210
+
211
+ /**
212
+ * Mod+A inside a table escalates instead of jumping straight to the document:
213
+ * cell content → whole table → (default select-all) whole doc. Keeps the
214
+ * cell-as-sandbox model consistent for selection too.
215
+ */
216
+ function escalateSelectAll(editor: Editor): boolean {
217
+ const { state, view } = editor
218
+ const sel = state.selection
219
+ if (sel instanceof CellSelection) {
220
+ const { start, map } = tableAround(sel.$anchorCell)
221
+ const rect = map.rectBetween(
222
+ sel.$anchorCell.pos - start,
223
+ sel.$headCell.pos - start,
224
+ )
225
+ const wholeTable =
226
+ rect.left === 0 &&
227
+ rect.top === 0 &&
228
+ rect.right === map.width &&
229
+ rect.bottom === map.height
230
+ if (wholeTable) return false
231
+ return selectWholeTable(editor, sel.$anchorCell)
232
+ }
233
+ const $cell = cellAround(sel.$head)
234
+ if (!$cell) return false
235
+ const cell = $cell.nodeAfter
236
+ if (!cell) return false
237
+ const from = Selection.near(state.doc.resolve($cell.pos + 1), 1).from
238
+ const to = Selection.near(
239
+ state.doc.resolve($cell.pos + cell.nodeSize - 1),
240
+ -1,
241
+ ).from
242
+ if (sel.from === from && sel.to === to) {
243
+ return selectWholeTable(editor, $cell)
244
+ }
245
+ view.dispatch(state.tr.setSelection(TextSelection.create(state.doc, from, to)))
246
+ return true
247
+ }
248
+
249
+ /** A single-cell CellSelection sitting on exactly the given cell. */
250
+ function isOnCell(selection: unknown, $cell: ResolvedPos): boolean {
251
+ return (
252
+ selection instanceof CellSelection &&
253
+ selection.$anchorCell.pos === $cell.pos &&
254
+ selection.$headCell.pos === $cell.pos
255
+ )
256
+ }
257
+
258
+ /** The cell (if any) under a pointer event. */
259
+ function cellAtCoords(
260
+ view: EditorView,
261
+ event: { clientX: number; clientY: number },
262
+ ): ResolvedPos | null {
263
+ const coords = view.posAtCoords({ left: event.clientX, top: event.clientY })
264
+ if (!coords) return null
265
+ return cellAround(view.state.doc.resolve(coords.pos))
266
+ }
267
+
268
+ /**
269
+ * Touch: leave taps to the default (caret + keyboard — navigate mode has
270
+ * little value without physical arrow keys), but surface the table context
271
+ * menu on long-press, since iOS never fires `contextmenu` on its own. Android
272
+ * does — the capture listener below cancels the timer so it can't double-open.
273
+ */
274
+ function watchLongPress(event: PointerEvent): boolean {
275
+ const { clientX, clientY, pointerId } = event
276
+ const target = event.target
277
+ if (!(target instanceof Element)) return false
278
+
279
+ const cleanup = () => {
280
+ clearTimeout(timer)
281
+ document.removeEventListener('pointermove', onMove)
282
+ document.removeEventListener('pointerup', onEnd)
283
+ document.removeEventListener('pointercancel', onEnd)
284
+ document.removeEventListener('contextmenu', onNativeMenu, true)
285
+ }
286
+ const timer = window.setTimeout(() => {
287
+ cleanup()
288
+ target.dispatchEvent(
289
+ new MouseEvent('contextmenu', {
290
+ bubbles: true,
291
+ cancelable: true,
292
+ clientX,
293
+ clientY,
294
+ }),
295
+ )
296
+ }, LONG_PRESS_MS)
297
+ const onMove = (e: PointerEvent) => {
298
+ if (e.pointerId !== pointerId) return
299
+ if (
300
+ Math.hypot(e.clientX - clientX, e.clientY - clientY) > LONG_PRESS_SLOP
301
+ ) {
302
+ cleanup()
303
+ }
304
+ }
305
+ const onEnd = (e: PointerEvent) => {
306
+ if (e.pointerId === pointerId) cleanup()
307
+ }
308
+ const onNativeMenu = () => cleanup()
309
+ document.addEventListener('pointermove', onMove)
310
+ document.addEventListener('pointerup', onEnd)
311
+ document.addEventListener('pointercancel', onEnd)
312
+ document.addEventListener('contextmenu', onNativeMenu, true)
313
+ return false
314
+ }
315
+
316
+ export const TableNavigation = Extension.create({
317
+ name: 'tableNavigation',
318
+ // Beat the Table extension's own arrow/keymap handling.
319
+ priority: 200,
320
+
321
+ addProseMirrorPlugins() {
322
+ const editor = this.editor
323
+
324
+ return [
325
+ new Plugin({
326
+ props: {
327
+ handleKeyDown(view, event) {
328
+ // Read-only: leave caret/selection to the default. No cell nav.
329
+ if (!editor.isEditable) return false
330
+ const { state } = view
331
+ if (!isInTable(state)) return false
332
+ const navigating = state.selection instanceof CellSelection
333
+
334
+ // Mod+A escalates: cell content → whole table → whole doc.
335
+ if (
336
+ (event.metaKey || event.ctrlKey) &&
337
+ !event.altKey &&
338
+ !event.shiftKey &&
339
+ event.key.toLowerCase() === 'a'
340
+ ) {
341
+ return escalateSelectAll(editor)
342
+ }
343
+
344
+ // Tab moves to the next/previous cell in navigate mode, exiting edit
345
+ // mode (works from either mode). Consume it in a table either way so
346
+ // focus never leaves the cell grid.
347
+ if (event.key === 'Tab') {
348
+ if (event.metaKey || event.ctrlKey || event.altKey) return false
349
+ tabToCell(editor, event.shiftKey ? -1 : 1)
350
+ return true
351
+ }
352
+
353
+ // Shift+Arrow grows the cell selection (spreadsheet-style), anchor
354
+ // fixed. Only once a CellSelection exists (navigate mode); in edit
355
+ // mode let the default extend the text selection within the cell.
356
+ if (
357
+ event.shiftKey &&
358
+ !event.metaKey &&
359
+ !event.ctrlKey &&
360
+ !event.altKey &&
361
+ (ARROWS as readonly string[]).includes(event.key)
362
+ ) {
363
+ if (!navigating) return false
364
+ switch (event.key) {
365
+ case 'ArrowRight':
366
+ return extendCell(editor, 'horiz', 1)
367
+ case 'ArrowLeft':
368
+ return extendCell(editor, 'horiz', -1)
369
+ case 'ArrowDown':
370
+ return extendCell(editor, 'vert', 1)
371
+ case 'ArrowUp':
372
+ return extendCell(editor, 'vert', -1)
373
+ }
374
+ }
375
+
376
+ // In edit mode, keep arrows inside the cell (no escaping to the next
377
+ // cell or out of the table); leave the cell via Escape or Tab.
378
+ if (
379
+ !navigating &&
380
+ (ARROWS as readonly string[]).includes(event.key)
381
+ ) {
382
+ return arrowLeavesCell(view, event)
383
+ }
384
+
385
+ switch (event.key) {
386
+ case 'ArrowRight':
387
+ return navigating ? moveCell(editor, 'horiz', 1) : false
388
+ case 'ArrowLeft':
389
+ return navigating ? moveCell(editor, 'horiz', -1) : false
390
+ case 'ArrowDown':
391
+ return navigating ? moveCell(editor, 'vert', 1) : false
392
+ case 'ArrowUp':
393
+ return navigating ? moveCell(editor, 'vert', -1) : false
394
+ case 'Enter':
395
+ if (navigating) {
396
+ return editCell(
397
+ editor,
398
+ (state.selection as CellSelection).$anchorCell,
399
+ )
400
+ }
401
+ // Plain Enter in edit mode returns to navigate mode
402
+ // (spreadsheet-style); Shift+Enter still inserts a line break.
403
+ if (
404
+ !event.shiftKey &&
405
+ !event.metaKey &&
406
+ !event.ctrlKey &&
407
+ !event.altKey
408
+ ) {
409
+ return selectCell(editor, selectionCell(state).pos)
410
+ }
411
+ return false
412
+ case 'Escape':
413
+ if (!navigating)
414
+ return selectCell(editor, selectionCell(state).pos)
415
+ return false
416
+ default:
417
+ return false
418
+ }
419
+ },
420
+
421
+ handleDOMEvents: {
422
+ // Select the cell on pointerdown — before the browser places a text
423
+ // caret — so a click lands straight in navigate mode with no caret
424
+ // flash. A second click on the already-active cell falls through to
425
+ // the default (caret) to start editing.
426
+ pointerdown(view, event: PointerEvent) {
427
+ // Read-only: don't hijack clicks into cell selection.
428
+ if (!editor.isEditable) return false
429
+
430
+ // Touch: taps keep the default caret behavior (and scrolling);
431
+ // a long-press opens the table context menu.
432
+ if (event.pointerType === 'touch') {
433
+ if (!cellAtCoords(view, event)) return false
434
+ return watchLongPress(event)
435
+ }
436
+
437
+ // Pointer is over a column-resize border (the resize plugin set its
438
+ // active handle on the preceding mousemove). Bail so its own,
439
+ // lower-priority mousedown can start the drag instead of us
440
+ // swallowing it as a cell selection.
441
+ const resizeState = columnResizingPluginKey.getState(view.state)
442
+ if (resizeState && resizeState.activeHandle > -1) return false
443
+ if (
444
+ event.button !== 0 ||
445
+ event.shiftKey ||
446
+ event.ctrlKey ||
447
+ event.metaKey ||
448
+ event.altKey
449
+ ) {
450
+ return false
451
+ }
452
+ const $cell = cellAtCoords(view, event)
453
+ if (!$cell) return false
454
+ // Clicking the active cell again → let the default place the caret.
455
+ if (isOnCell(view.state.selection, $cell)) return false
456
+ // Already editing this cell → let the default move the caret;
457
+ // clicks never exit edit mode (only Enter / Escape / Tab do).
458
+ const $editing = cellAround(view.state.selection.$head)
459
+ if (
460
+ !(view.state.selection instanceof CellSelection) &&
461
+ $editing &&
462
+ $editing.pos === $cell.pos
463
+ ) {
464
+ return false
465
+ }
466
+
467
+ event.preventDefault()
468
+ const anchorPos = $cell.pos
469
+ view.dispatch(
470
+ view.state.tr.setSelection(
471
+ CellSelection.create(view.state.doc, anchorPos),
472
+ ),
473
+ )
474
+ if (!view.hasFocus()) view.focus()
475
+
476
+ // Drag to extend into a multi-cell selection (e.g. for merge),
477
+ // with edge auto-scroll so wide tables can be selected past the
478
+ // wrapper's visible window.
479
+ const anchorDom = view.nodeDOM(anchorPos)
480
+ const wrapper =
481
+ anchorDom instanceof HTMLElement
482
+ ? anchorDom.closest<HTMLElement>('.tableWrapper')
483
+ : null
484
+ trackPointerDrag({
485
+ event,
486
+ area: wrapper,
487
+ onPoint(x, y) {
488
+ const $head = cellAtCoords(view, { clientX: x, clientY: y })
489
+ if (!$head) return
490
+ // Never extend across table boundaries.
491
+ const $anchor = view.state.doc.resolve(anchorPos)
492
+ if ($head.start(-1) !== $anchor.start(-1)) return
493
+ const next = CellSelection.create(
494
+ view.state.doc,
495
+ anchorPos,
496
+ $head.pos,
497
+ )
498
+ if (!view.state.selection.eq(next)) {
499
+ view.dispatch(view.state.tr.setSelection(next))
500
+ }
501
+ },
502
+ })
503
+ return true
504
+ },
505
+
506
+ // IME input never reaches handleTextInput, so drop into edit mode
507
+ // before the composition starts — composing against a CellSelection
508
+ // (which has no DOM caret) drops or mangles the input.
509
+ compositionstart(view) {
510
+ if (!editor.isEditable) return false
511
+ const { selection } = view.state
512
+ if (!(selection instanceof CellSelection)) return false
513
+ editCell(editor, selection.$anchorCell)
514
+ return false
515
+ },
516
+ },
517
+
518
+ // Typing while a cell is selected starts editing (append) instead of
519
+ // letting a CellSelection swallow the input. Input lands in the
520
+ // anchor cell — the active cell of a multi-cell selection.
521
+ handleTextInput(view, _from, _to, text) {
522
+ if (!editor.isEditable) return false
523
+ const { selection } = view.state
524
+ if (!(selection instanceof CellSelection)) return false
525
+ const $cell = selection.$anchorCell
526
+ const cell = $cell.nodeAfter
527
+ if (!cell) return false
528
+ const end = $cell.pos + cell.nodeSize - 1
529
+ view.dispatch(
530
+ view.state.tr
531
+ .setSelection(
532
+ TextSelection.near(view.state.doc.resolve(end), -1),
533
+ )
534
+ .insertText(text)
535
+ .scrollIntoView(),
536
+ )
537
+ return true
538
+ },
539
+
540
+ // Match type-to-append: pasting non-table content on a selected cell
541
+ // drops the caret at the end of the anchor cell and pastes there,
542
+ // instead of replacing the cell's content. Table-shaped slices keep
543
+ // prosemirror-tables' structured cell paste.
544
+ handlePaste(view, _event, slice) {
545
+ if (!editor.isEditable) return false
546
+ const { selection } = view.state
547
+ if (!(selection instanceof CellSelection)) return false
548
+ let tableContent = false
549
+ slice.content.forEach((node) => {
550
+ if (node.type.spec.tableRole) tableContent = true
551
+ })
552
+ if (tableContent) return false
553
+ editCell(editor, selection.$anchorCell)
554
+ return false
555
+ },
556
+ },
557
+ }),
558
+ ]
559
+ },
560
+ })
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Draws ONE rounded box around a multi-cell selection instead of ringing each
3
+ * cell on its own.
4
+ *
5
+ * prosemirror-tables flags every selected cell with `.selectedCell`, which by
6
+ * default we paint with a translucent fill + a per-cell ring (see style.css).
7
+ * For a 2+ cell selection that reads as a grid of boxes. Here we instead:
8
+ * - add a `table-cell-range` class to the editor so the per-cell ring is
9
+ * suppressed (the fill stays, so the region reads as one block), and
10
+ * - overlay a single element spanning the union of the selected cells' rects,
11
+ * appended to the `.tableWrapper` (a positioned, scrollable ancestor) so it
12
+ * tracks horizontal scroll for free.
13
+ *
14
+ * A single selected cell (navigate mode) is left untouched — it keeps its ring.
15
+ */
16
+ import { Extension } from '@tiptap/core'
17
+ import { Plugin } from '@tiptap/pm/state'
18
+ import { CellSelection } from '@tiptap/pm/tables'
19
+ import type { EditorView } from '@tiptap/pm/view'
20
+
21
+ interface Rect {
22
+ left: number
23
+ top: number
24
+ right: number
25
+ bottom: number
26
+ }
27
+
28
+ class SelectionOverlay {
29
+ private el: HTMLElement | null = null
30
+ // The box is measured from live cell rects, but only transactions reach
31
+ // `update` — re-measure when the window reflows or the table itself resizes
32
+ // (column drag, image load) so the box doesn't drift between edits.
33
+ private resizeObserver: ResizeObserver | null = null
34
+ private observed: HTMLElement | null = null
35
+ private onResize = () => this.update(this.view)
36
+
37
+ constructor(private view: EditorView) {
38
+ window.addEventListener('resize', this.onResize)
39
+ if (typeof ResizeObserver !== 'undefined') {
40
+ this.resizeObserver = new ResizeObserver(() => this.update(this.view))
41
+ }
42
+ this.update(view)
43
+ }
44
+
45
+ update(view: EditorView): void {
46
+ this.view = view
47
+ const { selection } = view.state
48
+ const isRange =
49
+ selection instanceof CellSelection &&
50
+ selection.$anchorCell.pos !== selection.$headCell.pos
51
+ view.dom.classList.toggle('table-cell-range', isRange)
52
+ if (!isRange) {
53
+ this.remove()
54
+ return
55
+ }
56
+ this.draw(view, selection as CellSelection)
57
+ }
58
+
59
+ private draw(view: EditorView, selection: CellSelection): void {
60
+ let union: Rect | null = null
61
+ selection.forEachCell((_cell, pos) => {
62
+ const dom = view.nodeDOM(pos)
63
+ if (!(dom instanceof HTMLElement)) return
64
+ const r = dom.getBoundingClientRect()
65
+ union = union
66
+ ? {
67
+ left: Math.min(union.left, r.left),
68
+ top: Math.min(union.top, r.top),
69
+ right: Math.max(union.right, r.right),
70
+ bottom: Math.max(union.bottom, r.bottom),
71
+ }
72
+ : { left: r.left, top: r.top, right: r.right, bottom: r.bottom }
73
+ })
74
+ if (!union) {
75
+ this.remove()
76
+ return
77
+ }
78
+
79
+ const anchorDom = view.nodeDOM(selection.$anchorCell.pos)
80
+ const host =
81
+ (anchorDom instanceof HTMLElement
82
+ ? (anchorDom.closest('.tableWrapper') as HTMLElement | null)
83
+ : null) ?? (view.dom as HTMLElement)
84
+ const hostRect = host.getBoundingClientRect()
85
+ this.observe(host)
86
+
87
+ // Convert viewport rect → host content coordinates (so the box scrolls with
88
+ // the wrapper's horizontal overflow).
89
+ const box: Rect = union
90
+ const el = this.ensure(host)
91
+ el.style.left = `${box.left - hostRect.left + host.scrollLeft}px`
92
+ el.style.top = `${box.top - hostRect.top + host.scrollTop}px`
93
+ el.style.width = `${box.right - box.left}px`
94
+ el.style.height = `${box.bottom - box.top}px`
95
+ }
96
+
97
+ private ensure(host: HTMLElement): HTMLElement {
98
+ if (this.el && this.el.parentElement === host) return this.el
99
+ this.remove()
100
+ const el = document.createElement('div')
101
+ el.className = 'table-selection-box'
102
+ el.setAttribute('aria-hidden', 'true')
103
+ host.appendChild(el)
104
+ this.el = el
105
+ return el
106
+ }
107
+
108
+ private observe(host: HTMLElement): void {
109
+ if (!this.resizeObserver || this.observed === host) return
110
+ this.resizeObserver.disconnect()
111
+ this.resizeObserver.observe(host)
112
+ this.observed = host
113
+ }
114
+
115
+ private remove(): void {
116
+ this.el?.remove()
117
+ this.el = null
118
+ this.resizeObserver?.disconnect()
119
+ this.observed = null
120
+ }
121
+
122
+ destroy(): void {
123
+ this.view.dom.classList.remove('table-cell-range')
124
+ this.remove()
125
+ window.removeEventListener('resize', this.onResize)
126
+ this.resizeObserver = null
127
+ }
128
+ }
129
+
130
+ export const TableSelectionOverlay = Extension.create({
131
+ name: 'tableSelectionOverlay',
132
+ addProseMirrorPlugins() {
133
+ return [new Plugin({ view: (view) => new SelectionOverlay(view) })]
134
+ },
135
+ })
136
+
137
+ export { TableSelectionOverlay as default }
@@ -20,7 +20,7 @@
20
20
  <button
21
21
  v-if="isEditable"
22
22
  @click="removeNode"
23
- class="absolute top-2 right-2 bg-black/65 hover:bg-black/80 text-ink-gray-4 hover:text-ink-white p-1 rounded opacity-0 group-hover:opacity-100 transition-opacity"
23
+ class="absolute top-2 right-2 bg-black/65 hover:bg-black/80 text-ink-gray-4 hover:text-ink-base p-1 rounded opacity-0 group-hover:opacity-100 transition-opacity"
24
24
  title="Remove table of contents"
25
25
  >
26
26
  <span class="lucide-x size-4" />