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
@@ -76,7 +76,7 @@ const DEFAULT_TEXT_SWATCH: ColorSwatch = {
76
76
  const DEFAULT_HIGHLIGHT_SWATCH: ColorSwatch = {
77
77
  value: null,
78
78
  label: 'Default',
79
- class: 'border-outline-gray-modals',
79
+ class: 'border-outline-elevation-2',
80
80
  }
81
81
 
82
82
  /** Text-color swatches, "Default" first, then the palette in canonical order. */
@@ -58,16 +58,16 @@ export function selectMedia(editor: Editor, getPos: GetPos): void {
58
58
  }
59
59
 
60
60
  /**
61
- * Set image alignment. Guarded `isVideo` here so callers cannot accidentally
62
- * dispatch the image-only `setImageAlign` command for a video node.
61
+ * Set media alignment on the hosted node. Both node types declare an `align`
62
+ * attribute, so this dispatches a plain `updateAttributes` against whichever
63
+ * type the node view hosts (no per-type command needed).
63
64
  */
64
65
  export function setMediaAlign(
65
66
  editor: Editor,
66
67
  isVideo: boolean,
67
68
  align: MediaAlign,
68
69
  ): void {
69
- if (isVideo) return
70
- editor.commands.setImageAlign(align)
70
+ editor.commands.updateAttributes(isVideo ? 'video' : 'image', { align })
71
71
  }
72
72
 
73
73
  export interface CaptionKeydownActions {
@@ -2,7 +2,6 @@ import { describe, it, expect } from 'vitest'
2
2
  import {
3
3
  wrapperClasses,
4
4
  containerClasses,
5
- alignIcon,
6
5
  aspectRatioFrom,
7
6
  heightOverWidth,
8
7
  } from './media-node-view-utils'
@@ -35,18 +34,6 @@ describe('containerClasses', () => {
35
34
  })
36
35
  })
37
36
 
38
- describe('alignIcon', () => {
39
- it('maps known alignments', () => {
40
- expect(alignIcon('center')).toBe('lucide-align-center')
41
- expect(alignIcon('right')).toBe('lucide-align-right')
42
- })
43
-
44
- it('defaults to left', () => {
45
- expect(alignIcon(null)).toBe('lucide-align-left')
46
- expect(alignIcon(undefined)).toBe('lucide-align-left')
47
- })
48
- })
49
-
50
37
  describe('aspectRatioFrom', () => {
51
38
  it('builds a CSS aspect-ratio when both dims present', () => {
52
39
  expect(aspectRatioFrom(640, 480)).toBe('640 / 480')
@@ -42,16 +42,6 @@ export function containerClasses(
42
42
  ]
43
43
  }
44
44
 
45
- /** Lucide icon name for the current alignment (defaults to left). */
46
- export function alignIcon(align: MediaAlign | null | undefined): string {
47
- const map: Record<MediaAlign, string> = {
48
- left: 'lucide-align-left',
49
- center: 'lucide-align-center',
50
- right: 'lucide-align-right',
51
- }
52
- return (align && map[align]) || 'lucide-align-left'
53
- }
54
-
55
45
  /**
56
46
  * CSS `aspect-ratio` value (`"W / H"`) from the node's stored intrinsic size,
57
47
  * or `undefined` when either dimension is missing.
@@ -0,0 +1,80 @@
1
+ import { defineComponent, h } from 'vue'
2
+ import type { Editor } from '@tiptap/core'
3
+ import { useFloatingPopup } from '#molecules/editor/composables/useFloatingPopup'
4
+ import { useTableCellColorState } from '#molecules/editor/composables/useTableCellColorState'
5
+ import EditorPopover from '#molecules/editor/components/EditorPopover.vue'
6
+ import ColorSwatchGrid from '../font-color/ColorSwatchGrid.vue'
7
+ import { highlightSwatches, textSwatches } from '../font-color/swatches'
8
+
9
+ let activePopup: ReturnType<typeof useFloatingPopup> | null = null
10
+
11
+ const TableCellColorPanel = defineComponent({
12
+ props: {
13
+ editor: { type: Object, required: true },
14
+ onClose: { type: Function, required: true },
15
+ },
16
+ setup(props) {
17
+ const { activeBackground, activeTextColor, setBackground, setTextColor } =
18
+ useTableCellColorState(props.editor as Editor)
19
+
20
+ const pickBackground = (value: string | null) => {
21
+ setBackground(value)
22
+ props.onClose()
23
+ }
24
+ const pickText = (value: string | null) => {
25
+ setTextColor(value)
26
+ props.onClose()
27
+ }
28
+
29
+ return () =>
30
+ h(
31
+ EditorPopover,
32
+ { dialogLabel: 'Cell color', contentClass: 'rounded-md p-2.5' },
33
+ {
34
+ default: () => [
35
+ h('div', { 'data-slot': 'table-cell-color-panel' }, [
36
+ h('div', { class: 'text-sm text-ink-gray-7' }, 'Cell color'),
37
+ h(ColorSwatchGrid, {
38
+ swatches: highlightSwatches,
39
+ active: activeBackground.value,
40
+ variant: 'highlight',
41
+ onSelect: pickBackground,
42
+ }),
43
+ h('div', { class: 'mt-4 text-sm text-ink-gray-7' }, 'Text color'),
44
+ h(ColorSwatchGrid, {
45
+ swatches: textSwatches,
46
+ active: activeTextColor.value,
47
+ variant: 'text',
48
+ onSelect: pickText,
49
+ }),
50
+ ]),
51
+ ],
52
+ },
53
+ )
54
+ },
55
+ })
56
+
57
+ /**
58
+ * Open the cell color picker anchored to a trigger button.
59
+ *
60
+ * The trigger rect is snapshotted into a virtual reference because the table
61
+ * context-menu button is removed from the DOM when the menu closes on run —
62
+ * anchoring to the live element would leave Floating UI tracking a detached node.
63
+ */
64
+ export function openTableCellColorPicker(args: {
65
+ editor: Editor
66
+ anchor: HTMLElement
67
+ }): void {
68
+ activePopup?.destroy()
69
+ const rect = args.anchor.getBoundingClientRect()
70
+ activePopup = useFloatingPopup({
71
+ anchor: args.anchor,
72
+ component: TableCellColorPanel,
73
+ props: {
74
+ editor: args.editor,
75
+ onClose: () => activePopup?.destroy(),
76
+ },
77
+ virtualReference: { getBoundingClientRect: () => rect },
78
+ floatingOptions: { placement: 'bottom-start' },
79
+ })
80
+ }
@@ -0,0 +1,92 @@
1
+ <script setup lang="ts">
2
+ import { nextTick, onMounted, ref, useTemplateRef } from 'vue'
3
+ import EditorPopover from '../EditorPopover.vue'
4
+
5
+ /**
6
+ * "Pick N×M" hover grid for inserting a table. Pointer users sweep the grid;
7
+ * keyboard users grow/shrink the size with arrows and confirm with Enter.
8
+ */
9
+ const MAX_COLS = 8
10
+ const MAX_ROWS = 6
11
+
12
+ const props = defineProps<{
13
+ onPick: (size: { rows: number; cols: number }) => void
14
+ }>()
15
+
16
+ const rows = ref(3)
17
+ const cols = ref(3)
18
+ const grid = useTemplateRef<HTMLElement>('grid')
19
+
20
+ // Self-managed focus (EditorPopover gets autofocus=false): the grid div is the
21
+ // keyboard surface; FocusScope's first-tabbable scan would land elsewhere.
22
+ onMounted(() => void nextTick(() => grid.value?.focus()))
23
+
24
+ function setSize(r: number, c: number) {
25
+ rows.value = r
26
+ cols.value = c
27
+ }
28
+
29
+ function pick(r: number, c: number) {
30
+ props.onPick({ rows: r, cols: c })
31
+ }
32
+
33
+ function onKeydown(event: KeyboardEvent) {
34
+ const step: Record<string, [number, number]> = {
35
+ ArrowDown: [1, 0],
36
+ ArrowUp: [-1, 0],
37
+ ArrowRight: [0, 1],
38
+ ArrowLeft: [0, -1],
39
+ }
40
+ const delta = step[event.key]
41
+ if (delta) {
42
+ event.preventDefault()
43
+ rows.value = Math.min(Math.max(rows.value + delta[0], 1), MAX_ROWS)
44
+ cols.value = Math.min(Math.max(cols.value + delta[1], 1), MAX_COLS)
45
+ } else if (event.key === 'Enter') {
46
+ event.preventDefault()
47
+ pick(rows.value, cols.value)
48
+ }
49
+ }
50
+ </script>
51
+
52
+ <template>
53
+ <EditorPopover
54
+ dialog-label="Insert table"
55
+ content-class="rounded-md p-2.5"
56
+ :autofocus="false"
57
+ >
58
+ <div data-slot="table-size-picker">
59
+ <div
60
+ ref="grid"
61
+ tabindex="0"
62
+ role="grid"
63
+ :aria-label="`Table size, ${rows} rows by ${cols} columns`"
64
+ class="grid w-max gap-1 outline-none"
65
+ :style="{ gridTemplateColumns: `repeat(${MAX_COLS}, minmax(0, 1fr))` }"
66
+ @keydown="onKeydown"
67
+ >
68
+ <template v-for="r in MAX_ROWS" :key="r">
69
+ <button
70
+ v-for="c in MAX_COLS"
71
+ :key="c"
72
+ type="button"
73
+ tabindex="-1"
74
+ class="size-4 rounded-sm border"
75
+ :class="
76
+ r <= rows && c <= cols
77
+ ? 'border-outline-gray-3 bg-surface-gray-4'
78
+ : 'border-outline-gray-2 bg-surface-gray-1'
79
+ "
80
+ :aria-label="`${r} × ${c}`"
81
+ @pointerenter="setSize(r, c)"
82
+ @focus="setSize(r, c)"
83
+ @click="pick(r, c)"
84
+ />
85
+ </template>
86
+ </div>
87
+ <div class="mt-2 text-center text-sm text-ink-gray-7">
88
+ {{ rows }} × {{ cols }}
89
+ </div>
90
+ </div>
91
+ </EditorPopover>
92
+ </template>
@@ -0,0 +1,40 @@
1
+ import type { Editor } from '@tiptap/core'
2
+ import { useFloatingPopup } from '#molecules/editor/composables/useFloatingPopup'
3
+ import TableSizePicker from './TableSizePicker.vue'
4
+
5
+ let activePopup: ReturnType<typeof useFloatingPopup> | null = null
6
+
7
+ /**
8
+ * Open the insert-table size picker, anchored to a trigger button (toolbar) or
9
+ * an arbitrary rect (the caret, for the slash command). The anchor rect is
10
+ * snapshotted into a virtual reference — the trigger may leave the DOM while
11
+ * the picker is open (e.g. a closing menu), and the slash command's caret
12
+ * coordinates aren't an element at all.
13
+ */
14
+ export function openTableSizePicker(args: {
15
+ editor: Editor
16
+ anchor?: HTMLElement
17
+ reference?: DOMRect
18
+ }): void {
19
+ activePopup?.destroy()
20
+ const anchor = args.anchor ?? (args.editor.view.dom as HTMLElement)
21
+ const rect = args.reference ?? anchor.getBoundingClientRect()
22
+ activePopup = useFloatingPopup({
23
+ anchor,
24
+ component: TableSizePicker,
25
+ props: {
26
+ onPick: ({ rows, cols }: { rows: number; cols: number }) => {
27
+ const editor = args.editor
28
+ activePopup?.destroy()
29
+ activePopup = null
30
+ editor
31
+ .chain()
32
+ .focus()
33
+ .insertTable({ rows, cols, withHeaderRow: true })
34
+ .run()
35
+ },
36
+ },
37
+ virtualReference: { getBoundingClientRect: () => rect },
38
+ floatingOptions: { placement: 'bottom-start' },
39
+ })
40
+ }
@@ -0,0 +1,117 @@
1
+ import { onBeforeUnmount, ref, watch, type Ref } from 'vue'
2
+ import { useWindowFileDragging } from './useWindowFileDragging'
3
+
4
+ /**
5
+ * File drop tracking for a single editor drop zone.
6
+ *
7
+ * Exposes two signals so the zone can show a two-stage affordance:
8
+ * - `isWindowDragging` — a file is being dragged anywhere over the window; the
9
+ * zone reveals itself as a hint so the user can see where to drop.
10
+ * - `isOverZone` — the file is over THIS zone specifically; the zone emphasises
11
+ * itself as the active target.
12
+ *
13
+ * `onFiles` fires only for file drops that land inside `root` (the surrounding
14
+ * editor chrome). Drops on the prose itself are handled by the media ProseMirror
15
+ * plugin, which stop-propagates them — so our drop handler never runs for those,
16
+ * and `isOverZone` is instead cleared by watching the window signal (which flips
17
+ * false on any drop, captured before the inner `stopPropagation`).
18
+ *
19
+ * @param root Ref to the zone element. May be null until mounted.
20
+ * @param onFiles Called with dropped `File[]` for drops inside `root`.
21
+ */
22
+ export function useEditorFileDrop(
23
+ root: Ref<HTMLElement | null>,
24
+ onFiles: (files: File[]) => void,
25
+ ): {
26
+ isWindowDragging: Ref<boolean>
27
+ isOverZone: Ref<boolean>
28
+ draggedTypes: Ref<string[]>
29
+ } {
30
+ const isWindowDragging = useWindowFileDragging()
31
+ const isOverZone = ref(false)
32
+ const draggedTypes = ref<string[]>([])
33
+ let depth = 0
34
+
35
+ const hasFiles = (event: DragEvent): boolean =>
36
+ !!event.dataTransfer &&
37
+ Array.from(event.dataTransfer.types).includes('Files')
38
+
39
+ const reset = () => {
40
+ depth = 0
41
+ isOverZone.value = false
42
+ draggedTypes.value = []
43
+ }
44
+
45
+ const updateDraggedTypes = (event: DragEvent) => {
46
+ draggedTypes.value = Array.from(event.dataTransfer?.items ?? [])
47
+ .filter((item) => item.kind === 'file')
48
+ .map((item) => item.type)
49
+ .filter(Boolean)
50
+ }
51
+
52
+ const onDragEnter = (event: DragEvent) => {
53
+ if (!hasFiles(event)) return
54
+ depth += 1
55
+ updateDraggedTypes(event)
56
+ isOverZone.value = true
57
+ }
58
+
59
+ // No updateDraggedTypes here: the dragged payload cannot change mid-drag, and
60
+ // dragover fires continuously — re-parsing would write a fresh array to the
61
+ // ref on every event and churn downstream computeds.
62
+ const onDragOver = (event: DragEvent) => {
63
+ if (!hasFiles(event)) return
64
+ event.preventDefault()
65
+ }
66
+
67
+ const onDragLeave = () => {
68
+ depth = Math.max(0, depth - 1)
69
+ if (depth === 0) isOverZone.value = false
70
+ }
71
+
72
+ const onDrop = (event: DragEvent) => {
73
+ reset()
74
+ const files = Array.from(event.dataTransfer?.files ?? [])
75
+ if (files.length === 0) return
76
+ event.preventDefault()
77
+ event.stopPropagation()
78
+ onFiles(files)
79
+ }
80
+
81
+ // The window signal flips false on any drop (capture phase) or when the drag
82
+ // leaves the window. Mirror that here so a prose drop — whose event never
83
+ // reaches our handler — can't leave the zone stuck highlighted.
84
+ watch(isWindowDragging, (dragging) => {
85
+ if (!dragging) reset()
86
+ })
87
+
88
+ const bind = (el: HTMLElement) => {
89
+ el.addEventListener('dragenter', onDragEnter)
90
+ el.addEventListener('dragover', onDragOver)
91
+ el.addEventListener('dragleave', onDragLeave)
92
+ el.addEventListener('drop', onDrop)
93
+ }
94
+
95
+ const unbind = (el: HTMLElement) => {
96
+ el.removeEventListener('dragenter', onDragEnter)
97
+ el.removeEventListener('dragover', onDragOver)
98
+ el.removeEventListener('dragleave', onDragLeave)
99
+ el.removeEventListener('drop', onDrop)
100
+ }
101
+
102
+ watch(
103
+ root,
104
+ (el, prev) => {
105
+ if (prev) unbind(prev)
106
+ reset()
107
+ if (el) bind(el)
108
+ },
109
+ { immediate: true },
110
+ )
111
+
112
+ onBeforeUnmount(() => {
113
+ if (root.value) unbind(root.value)
114
+ })
115
+
116
+ return { isWindowDragging, isOverZone, draggedTypes }
117
+ }
@@ -19,6 +19,19 @@ export interface FloatingPopupOptions<P extends Record<string, unknown>> {
19
19
  props: P
20
20
  virtualReference?: VirtualReference
21
21
  closeOnAnchorPointerDown?: boolean
22
+ /**
23
+ * Close the popup when Escape is pressed. Defaults to `true`.
24
+ * Set `false` when the mounted component needs to handle Escape itself
25
+ * (e.g. a multi-step popup where Escape first steps back, then closes).
26
+ */
27
+ closeOnEscape?: boolean
28
+ /**
29
+ * Play a fast origin-aware appear animation on the floating wrapper (scales
30
+ * out from the edge nearest the anchor, using the *resolved* placement). Leave
31
+ * off for popups that wrap their content in `EditorPopover`, which animates
32
+ * itself. Defaults to `false`.
33
+ */
34
+ animate?: boolean
22
35
  floatingOptions?: {
23
36
  placement?: Placement
24
37
  strategy?: Strategy
@@ -56,6 +69,7 @@ export function useFloatingPopup<P extends Record<string, unknown>>(
56
69
 
57
70
  let cleanupAutoUpdate: (() => void) | null = null
58
71
  let destroyed = false
72
+ let animated = false
59
73
 
60
74
  const update = () => {
61
75
  if (destroyed) return
@@ -67,13 +81,21 @@ export function useFloatingPopup<P extends Record<string, unknown>>(
67
81
  flip(),
68
82
  shift({ padding: 8 }),
69
83
  ],
70
- }).then(({ x, y, strategy }) => {
84
+ }).then(({ x, y, strategy, placement }) => {
71
85
  if (destroyed) return
72
86
  Object.assign(floating.style, {
73
87
  position: strategy,
74
88
  left: `${x}px`,
75
89
  top: `${y}px`,
76
90
  })
91
+ // First positioned frame: set the origin from the resolved placement and
92
+ // trigger the enter animation once (so it scales from the edge nearest the
93
+ // anchor, not from 0,0 before positioning).
94
+ if (options.animate && !animated) {
95
+ animated = true
96
+ floating.style.transformOrigin = transformOriginFor(placement)
97
+ floating.classList.add('editor-floating-pop')
98
+ }
77
99
  })
78
100
  }
79
101
 
@@ -103,11 +125,12 @@ export function useFloatingPopup<P extends Record<string, unknown>>(
103
125
  }
104
126
 
105
127
  function onKeydown(event: KeyboardEvent) {
106
- if (event.key === 'Escape') handle.destroy()
128
+ if (event.key === 'Escape' && options.closeOnEscape !== false) handle.destroy()
107
129
  }
108
130
 
109
131
  cleanupAutoUpdate = autoUpdate(reference, floating, update)
110
132
  requestAnimationFrame(() => {
133
+ if (destroyed) return
111
134
  document.addEventListener('pointerdown', onPointerDown, true)
112
135
  document.addEventListener('keydown', onKeydown, true)
113
136
  })
@@ -120,3 +143,24 @@ function normalizeOffset(value: number | [number, number] | undefined) {
120
143
  if (Array.isArray(value)) return { mainAxis: value[1], crossAxis: value[0] }
121
144
  return value ?? 4
122
145
  }
146
+
147
+ /**
148
+ * CSS `transform-origin` for a popup at the given (resolved) placement: the
149
+ * point on the floating box nearest the anchor. A menu placed `right-start`
150
+ * grows from its `left top` corner; `top` grows from `center bottom`, etc.
151
+ */
152
+ function transformOriginFor(placement: Placement): string {
153
+ const [side, align] = placement.split('-')
154
+ const opposite: Record<string, string> = {
155
+ top: 'bottom',
156
+ bottom: 'top',
157
+ left: 'right',
158
+ right: 'left',
159
+ }
160
+ if (side === 'top' || side === 'bottom') {
161
+ const x = align === 'start' ? 'left' : align === 'end' ? 'right' : 'center'
162
+ return `${x} ${opposite[side]}`
163
+ }
164
+ const y = align === 'start' ? 'top' : align === 'end' ? 'bottom' : 'center'
165
+ return `${opposite[side]} ${y}`
166
+ }
@@ -35,10 +35,13 @@ function makeEditor(editable = true): Editor {
35
35
  } as unknown as Editor
36
36
  }
37
37
 
38
- function makeEl(): HTMLElement {
38
+ function makeEl(width = 200, height = 100): HTMLElement {
39
39
  const el = document.createElement('img')
40
- Object.defineProperty(el, 'offsetWidth', { value: 200, configurable: true })
41
- Object.defineProperty(el, 'offsetHeight', { value: 100, configurable: true })
40
+ Object.defineProperty(el, 'offsetWidth', { value: width, configurable: true })
41
+ Object.defineProperty(el, 'offsetHeight', {
42
+ value: height,
43
+ configurable: true,
44
+ })
42
45
  return el
43
46
  }
44
47
 
@@ -48,7 +51,7 @@ beforeEach(() => {
48
51
  })
49
52
 
50
53
  describe('useNodeViewResize', () => {
51
- it('removes window listeners and resets cursor on stop (mouseup)', () => {
54
+ it('removes window listeners and resets cursor on stop (pointerup)', () => {
52
55
  const remove = vi.spyOn(window, 'removeEventListener')
53
56
  const el = makeEl()
54
57
  const { api, unmount } = mountResize(makeEditor(), {
@@ -61,10 +64,12 @@ describe('useNodeViewResize', () => {
61
64
  api.startResize({ clientX: 0 } as MouseEvent)
62
65
  expect(api.isResizing.value).toBe(true)
63
66
 
64
- window.dispatchEvent(new MouseEvent('mouseup'))
67
+ // jsdom has no PointerEvent constructor; type string is all that matters.
68
+ window.dispatchEvent(new MouseEvent('pointerup'))
65
69
  expect(api.isResizing.value).toBe(false)
66
- expect(remove).toHaveBeenCalledWith('mousemove', expect.any(Function))
67
- expect(remove).toHaveBeenCalledWith('mouseup', expect.any(Function))
70
+ expect(remove).toHaveBeenCalledWith('pointermove', expect.any(Function))
71
+ expect(remove).toHaveBeenCalledWith('pointerup', expect.any(Function))
72
+ expect(remove).toHaveBeenCalledWith('pointercancel', expect.any(Function))
68
73
  expect(document.body.style.cursor).toBe('')
69
74
  unmount()
70
75
  })
@@ -82,8 +87,9 @@ describe('useNodeViewResize', () => {
82
87
  api.startResize({ clientX: 0 } as MouseEvent)
83
88
  unmount()
84
89
 
85
- expect(remove).toHaveBeenCalledWith('mousemove', expect.any(Function))
86
- expect(remove).toHaveBeenCalledWith('mouseup', expect.any(Function))
90
+ expect(remove).toHaveBeenCalledWith('pointermove', expect.any(Function))
91
+ expect(remove).toHaveBeenCalledWith('pointerup', expect.any(Function))
92
+ expect(remove).toHaveBeenCalledWith('pointercancel', expect.any(Function))
87
93
  expect(document.body.style.cursor).toBe('')
88
94
  })
89
95
 
@@ -98,7 +104,7 @@ describe('useNodeViewResize', () => {
98
104
  })
99
105
 
100
106
  api.startResize({ clientX: 0 } as MouseEvent)
101
- window.dispatchEvent(new MouseEvent('mouseup'))
107
+ window.dispatchEvent(new MouseEvent('pointerup'))
102
108
 
103
109
  expect(onCommit).not.toHaveBeenCalled()
104
110
  unmount()
@@ -115,12 +121,55 @@ describe('useNodeViewResize', () => {
115
121
  })
116
122
 
117
123
  api.startResize({ clientX: 0 } as MouseEvent)
118
- window.dispatchEvent(new MouseEvent('mouseup'))
124
+ window.dispatchEvent(new MouseEvent('pointerup'))
119
125
 
120
126
  expect(onCommit).toHaveBeenCalledWith({ width: 200, height: 100 })
121
127
  unmount()
122
128
  })
123
129
 
130
+ it('locks the aspect to the rendered box, not getAspectRatio', () => {
131
+ // Element paints 3:1 tall (200×600); the supplied ratio is a WRONG 1:1 —
132
+ // the case where a node stores only width and the height comes from CSS
133
+ // `height: auto`. The drag must preserve the painted shape (3), so a width
134
+ // grown to 300 yields height 900, never the 300 that ratio 1 would give.
135
+ const el = makeEl(200, 600)
136
+ const { api, unmount } = mountResize(makeEditor(), {
137
+ mediaEl: () => el,
138
+ getAspectRatio: () => 1,
139
+ getPos: () => 0,
140
+ onCommit: vi.fn(),
141
+ })
142
+
143
+ api.startResize({ clientX: 0 } as MouseEvent)
144
+ window.dispatchEvent(new MouseEvent('pointermove', { clientX: 100 }))
145
+
146
+ expect(el.style.width).toBe('300px')
147
+ expect(el.style.height).toBe('900px')
148
+ window.dispatchEvent(new MouseEvent('pointerup'))
149
+ unmount()
150
+ })
151
+
152
+ it('falls back to getAspectRatio when the element has no rendered size', () => {
153
+ // Pre-layout element (offsetWidth 0): the rendered ratio is unavailable, so
154
+ // the supplied aspect (0.5) drives the drag instead.
155
+ const el = makeEl(0, 0)
156
+ const { api, unmount } = mountResize(makeEditor(), {
157
+ mediaEl: () => el,
158
+ getAspectRatio: () => 0.5,
159
+ getPos: () => 0,
160
+ onCommit: vi.fn(),
161
+ })
162
+
163
+ api.startResize({ clientX: 0 } as MouseEvent)
164
+ window.dispatchEvent(new MouseEvent('pointermove', { clientX: 100 }))
165
+
166
+ // newWidth = max(50, 0 + 100) = 100; height = 100 * 0.5 = 50.
167
+ expect(el.style.width).toBe('100px')
168
+ expect(el.style.height).toBe('50px')
169
+ window.dispatchEvent(new MouseEvent('pointerup'))
170
+ unmount()
171
+ })
172
+
124
173
  it('no-ops startResize when the editor is not editable', () => {
125
174
  const onCommit = vi.fn()
126
175
  const { api, unmount } = mountResize(makeEditor(false), {