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
@@ -20,7 +20,7 @@
20
20
  data-slot="content"
21
21
  :data-motion="contentMotion"
22
22
  :class="[
23
- dropdownClasses.content,
23
+ menuClasses.content,
24
24
  {
25
25
  'origin-top-left': align === 'start',
26
26
  'origin-top-right': align === 'end',
@@ -30,12 +30,16 @@
30
30
  :side="side"
31
31
  :align="align"
32
32
  :side-offset="offset"
33
+ :style="{
34
+ width: matchTriggerWidth ? 'var(--reka-dropdown-menu-trigger-width)' : undefined,
35
+ }"
33
36
  >
34
- <DropdownMenuList
37
+ <Menu
35
38
  :groups="groups"
36
39
  :portal-to="portalTo"
37
40
  :close="close"
38
41
  :slot-fns="slots"
42
+ :primitives="primitives"
39
43
  />
40
44
  </DropdownMenuContent>
41
45
  </DropdownMenuPortal>
@@ -46,14 +50,19 @@
46
50
  import { computed, useAttrs, useSlots } from 'vue'
47
51
  import {
48
52
  DropdownMenuContent,
53
+ DropdownMenuItem,
54
+ DropdownMenuLabel,
49
55
  DropdownMenuPortal,
50
56
  DropdownMenuRoot,
57
+ DropdownMenuSub,
58
+ DropdownMenuSubContent,
59
+ DropdownMenuSubTrigger,
51
60
  DropdownMenuTrigger,
52
61
  } from 'reka-ui'
53
62
  import { Button } from '../Button'
54
- import DropdownMenuList from './DropdownMenuList.vue'
63
+ import Menu from '../Menu/Menu.vue'
55
64
  import type { DropdownProps, DropdownSlots } from './types'
56
- import { dropdownClasses, normalizeDropdownOptions } from './utils'
65
+ import { menuClasses, normalizeMenuOptions } from '../Menu/utils'
57
66
  import { usePopoverMotion } from '../../composables/usePopoverMotion'
58
67
 
59
68
  defineOptions({
@@ -78,8 +87,17 @@ function close() {
78
87
  openModel.value = false
79
88
  }
80
89
 
90
+ const primitives = {
91
+ Item: DropdownMenuItem,
92
+ Label: DropdownMenuLabel,
93
+ Portal: DropdownMenuPortal,
94
+ Sub: DropdownMenuSub,
95
+ SubContent: DropdownMenuSubContent,
96
+ SubTrigger: DropdownMenuSubTrigger,
97
+ }
98
+
81
99
  const groups = computed(() => {
82
- return normalizeDropdownOptions(props.options)
100
+ return normalizeMenuOptions(props.options)
83
101
  })
84
102
 
85
103
  const align = computed(() => {
@@ -100,47 +118,21 @@ defineSlots<DropdownSlots>()
100
118
  </script>
101
119
 
102
120
  <style scoped>
103
- @keyframes dropdown-in {
104
- from {
105
- opacity: 0;
106
- transform: scale(0.95);
107
- }
108
- to {
109
- opacity: 1;
110
- transform: scale(1);
111
- }
112
- }
113
-
114
- @keyframes dropdown-out {
115
- from {
116
- opacity: 1;
117
- transform: scale(1);
118
- }
119
- to {
120
- opacity: 0;
121
- transform: scale(0.95);
122
- }
123
- }
124
-
125
- :global(.dropdown-content[data-motion='animated'][data-state='open']) {
126
- animation: dropdown-in 100ms ease-out;
127
- }
128
-
129
- :global(.dropdown-content[data-motion='animated'][data-state='closed']) {
130
- animation: dropdown-out 75ms ease-in;
131
- }
132
-
133
121
  /*
134
- * Keyboard opens skip the scale entrance, but a tiny opacity fade still
135
- * runs it masks the 1-frame position-settle reka performs after mount.
136
- * ~80ms is below the perception threshold for motion but long enough to
137
- * hide the jump.
122
+ * Dropdown is the only menu with a keyboard-open path, so the `instant`
123
+ * motion lives here. Keyboard opens skip the scale entrance, but a tiny
124
+ * opacity fade still runs it masks the 1-frame position-settle reka
125
+ * performs after mount. ~80ms is below the perception threshold for motion
126
+ * but long enough to hide the jump.
127
+ *
128
+ * The shared `animated` keyframes + `prefers-reduced-motion` reset live in
129
+ * Menu.vue (rendered by both Dropdown and ContextMenu).
138
130
  */
139
- :global(.dropdown-content[data-motion='instant'][data-state='open']) {
131
+ :global(.menu-content[data-motion='instant'][data-state='open']) {
140
132
  animation: dropdown-instant-fade 80ms linear;
141
133
  }
142
134
 
143
- :global(.dropdown-content[data-motion='instant'][data-state='closed']) {
135
+ :global(.menu-content[data-motion='instant'][data-state='closed']) {
144
136
  animation: none;
145
137
  }
146
138
 
@@ -152,10 +144,4 @@ defineSlots<DropdownSlots>()
152
144
  opacity: 1;
153
145
  }
154
146
  }
155
-
156
- @media (prefers-reduced-motion: reduce) {
157
- :global(.dropdown-content) {
158
- animation-duration: 0ms !important;
159
- }
160
- }
161
147
  </style>
@@ -52,7 +52,9 @@ function rowActions(task: Task): DropdownOptions {
52
52
  </script>
53
53
 
54
54
  <template>
55
- <div class="w-80 divide-y divide-outline-gray-1 rounded-lg border border-outline-gray-1 bg-surface-white">
55
+ <div
56
+ class="w-80 divide-y divide-outline-gray-1 rounded-lg border border-outline-gray-1 bg-surface-base"
57
+ >
56
58
  <div
57
59
  v-for="task in tasks"
58
60
  :key="task.title"
@@ -114,7 +114,7 @@ const userMenuItems = computed(() => [
114
114
  </svg>
115
115
  <div class="ml-2 min-w-0">
116
116
  <div
117
- class="truncate text-base font-medium text-ink-gray-8 leading-none"
117
+ class="truncate text-base-medium text-ink-gray-8 leading-none"
118
118
  >
119
119
  Gameplan
120
120
  </div>
@@ -1,162 +1,34 @@
1
- import type { Component, VNodeChild } from 'vue'
2
- import type { RouteLocationRaw } from 'vue-router'
3
1
  import type { ButtonProps } from '../Button'
2
+ import type { MenuOptions, MenuSlotProps, MenuSlots } from '../Menu/types'
3
+
4
+ export type {
5
+ MenuTheme as DropdownTheme,
6
+ MenuSlotFn as DropdownSlotFn,
7
+ MenuItemSlots as DropdownItemSlots,
8
+ MenuBaseOption as DropdownBaseOption,
9
+ MenuActionOption as DropdownActionOption,
10
+ MenuSwitchOption as DropdownSwitchOption,
11
+ MenuSubmenuOption as DropdownSubmenuOption,
12
+ MenuComponentOption as DropdownComponentOption,
13
+ MenuGroupOption as DropdownGroupOption,
14
+ MenuOption as DropdownOption,
15
+ MenuItem as DropdownItem,
16
+ MenuOptions as DropdownOptions,
17
+ MenuSlotProps as DropdownSlotProps,
18
+ MenuItemSlotProps as DropdownItemSlotProps,
19
+ MenuGroupSlotProps as DropdownGroupSlotProps,
20
+ } from '../Menu/types'
4
21
 
5
- export type DropdownTheme = 'gray' | 'red'
6
22
  export type DropdownPlacement = 'left' | 'right' | 'center'
7
23
  export type DropdownSide = 'top' | 'right' | 'bottom' | 'left'
8
24
  export type DropdownAlign = 'start' | 'center' | 'end'
9
25
 
10
- export type DropdownSlotFn<TProps> = (props: TProps) => VNodeChild
11
-
12
- export interface DropdownItemSlots<TProps> {
13
- /** Replaces the prefix region of the standard row shell. */
14
- prefix?: DropdownSlotFn<TProps>
15
-
16
- /** Replaces the label region of the standard row shell. */
17
- label?: DropdownSlotFn<TProps>
18
-
19
- /** Replaces the suffix region of the standard row shell. */
20
- suffix?: DropdownSlotFn<TProps>
21
-
22
- /** Replaces the entire row; mutually exclusive with `prefix` / `label` / `suffix`. */
23
- item?: DropdownSlotFn<TProps>
24
- }
25
-
26
- export interface DropdownBaseOption {
27
- /** Leading icon shown for the item row. */
28
- icon?: string | Component | null
29
-
30
- /** Secondary text shown below the label. */
31
- description?: string
32
-
33
- /** Marks the item as currently selected. */
34
- selected?: boolean
35
-
36
- /** Disables interaction for the item. */
37
- disabled?: boolean
38
-
39
- /** Visual theme applied to the item row. */
40
- theme?: DropdownTheme
41
-
42
- /** Named slot suffix used to resolve `item-*` slots dynamically. */
43
- slot?: string
44
-
45
- /** Per-item inline slot implementations for the row shell. */
46
- slots?: DropdownItemSlots<DropdownItemSlotProps>
47
-
48
- /** Condition used to omit an item from the final menu. */
49
- condition?: () => boolean
50
-
51
- [key: string]: any
52
- }
53
-
54
- export interface DropdownActionOption extends DropdownBaseOption {
55
- /** Primary label shown for the action item. */
56
- label: string
57
-
58
- /** Router destination to navigate to when the item is clicked. */
59
- route?: RouteLocationRaw
60
-
61
- /** Click handler invoked when the action item is selected. */
62
- onClick?: (event: PointerEvent) => void
63
-
64
- submenu?: never
65
- switch?: never
66
- switchValue?: never
67
- component?: never
68
- }
69
-
70
- export interface DropdownSwitchOption extends DropdownBaseOption {
71
- /** Primary label shown for the switch item. */
72
- label: string
73
-
74
- /** Renders the item with a switch control. */
75
- switch: true
76
-
77
- /** Current boolean value for the switch item. */
78
- switchValue?: boolean
79
-
80
- /** Change handler invoked with the next switch value. */
81
- onClick?: (value: boolean) => void
82
-
83
- route?: never
84
- submenu?: never
85
- component?: never
86
- }
87
-
88
- export interface DropdownSubmenuOption extends DropdownBaseOption {
89
- /** Primary label shown for the submenu trigger. */
90
- label: string
91
-
92
- /** Nested menu items rendered in the submenu. */
93
- submenu: DropdownOptions
94
-
95
- route?: never
96
- onClick?: never
97
- switch?: never
98
- switchValue?: never
99
- component?: never
100
- }
101
-
102
- export interface DropdownComponentOption extends DropdownBaseOption {
103
- /**
104
- * Custom component rendered in place of the standard menu row.
105
- * @deprecated use `slots.item` for the full-row escape hatch instead
106
- */
107
- component: any
108
-
109
- /** Optional label used by custom renderers. */
110
- label?: string
111
-
112
- route?: never
113
- submenu?: never
114
- switch?: never
115
- switchValue?: never
116
- }
117
-
118
- export interface DropdownGroupOption {
119
- /** Stable key for the group wrapper. */
120
- key?: string | number
121
-
122
- /** Label rendered above the grouped items. */
123
- group: string
124
-
125
- /**
126
- * Items rendered inside the group. Optional in the type so the
127
- * deprecated `items` alias still typechecks; provide one of
128
- * `options` or `items`.
129
- */
130
- options?: DropdownOption[]
131
-
132
- /**
133
- * @deprecated use `options`. Accepted only as a back-compat alias for
134
- * Dropdown's previous `{ group, items }` shape.
135
- */
136
- items?: DropdownOption[]
137
-
138
- /** Hides the group heading while preserving grouping. */
139
- hideLabel?: boolean
140
-
141
- /** Theme inherited by items in the group. */
142
- theme?: DropdownTheme
143
- }
144
-
145
- export type DropdownOption =
146
- | DropdownActionOption
147
- | DropdownSwitchOption
148
- | DropdownSubmenuOption
149
- | DropdownComponentOption
150
-
151
- export type DropdownItem = DropdownOption | DropdownGroupOption
152
- export type DropdownOptions = Array<DropdownItem>
153
-
154
26
  export interface DropdownProps {
155
27
  /** Button configuration (label, icon, size, variant, etc.) */
156
28
  button?: ButtonProps
157
29
 
158
30
  /** Array of dropdown options or grouped options. */
159
- options?: DropdownOptions
31
+ options?: MenuOptions
160
32
 
161
33
  /** Controls the visibility of the dropdown. */
162
34
  open?: boolean
@@ -176,16 +48,14 @@ export interface DropdownProps {
176
48
  /** Offset in pixels between trigger and dropdown. */
177
49
  offset?: number
178
50
 
51
+ /** Whether the dropdown width should match the trigger element. */
52
+ matchTriggerWidth?: boolean
53
+
179
54
  /** Teleport target for dropdown portal content. */
180
55
  portalTo?: string | HTMLElement
181
56
  }
182
57
 
183
- export interface DropdownSlotProps {
184
- /** Closes the dropdown menu. */
185
- close: () => void
186
- }
187
-
188
- export interface DropdownTriggerSlotProps extends DropdownSlotProps {
58
+ export interface DropdownTriggerSlotProps extends MenuSlotProps {
189
59
  /** Whether the dropdown menu is currently open. */
190
60
  open: boolean
191
61
 
@@ -195,45 +65,11 @@ export interface DropdownTriggerSlotProps extends DropdownSlotProps {
195
65
  [key: string]: any
196
66
  }
197
67
 
198
- export interface DropdownItemSlotProps extends DropdownSlotProps {
199
- /** Item currently being rendered. */
200
- item: DropdownOption
201
-
202
- /** Whether the item is currently selected. */
203
- selected: boolean
204
- }
205
-
206
- export interface DropdownGroupSlotProps {
207
- /** Group currently being rendered. */
208
- group: DropdownGroupOption
209
- }
210
-
211
- export interface DropdownSlots {
68
+ export type DropdownSlots = Omit<MenuSlots, 'default' | 'trigger'> & {
212
69
  /** Alternate trigger renderer. */
213
70
  default?: (props: DropdownTriggerSlotProps) => any
214
-
215
71
  /** Explicit trigger slot renderer. */
216
72
  trigger?: (props: DropdownTriggerSlotProps) => any
217
-
218
- /** Replaces the entire item row. */
219
- item?: (props: DropdownItemSlotProps) => any
220
-
221
- /** Content rendered before the standard item label. */
222
- 'item-prefix'?: (props: DropdownItemSlotProps) => any
223
-
224
- /** Content rendered for the standard item label area. */
225
- 'item-label'?: (props: DropdownItemSlotProps) => any
226
-
227
- /** Content rendered after the standard item label. */
228
- 'item-suffix'?: (props: DropdownItemSlotProps) => any
229
-
230
- /** Custom renderer for group labels. */
231
- 'group-label'?: (props: DropdownGroupSlotProps) => any
232
-
233
- /** Fallback content rendered when no items are available. */
234
- empty?: () => any
235
-
236
- [slotName: string]: ((props: any) => any) | undefined
237
73
  }
238
74
 
239
75
  export interface DropdownEmits {
@@ -0,0 +1,92 @@
1
+ <!-- Auto Generated by scripts/propsgen.ts -->
2
+ <script setup>
3
+ import PropsTable from '@/components/Docs/PropsTable.vue'
4
+ import SlotsTable from '@/components/Docs/SlotsTable.vue'
5
+ import EmitsTable from '@/components/Docs/EmitsTable.vue'
6
+
7
+ const propsData = [
8
+ {
9
+ name: 'modelValue',
10
+ description: 'The duration value in seconds (two-way via `v-model`).',
11
+ required: false,
12
+ type: 'number | null',
13
+ default: 'null'
14
+ },
15
+ {
16
+ name: 'placeholder',
17
+ description: 'Placeholder shown when the input is empty. Defaults to `1h 30m 45s`.',
18
+ required: false,
19
+ type: 'string',
20
+ default: '"1h 30m 45s"'
21
+ },
22
+ {
23
+ name: 'format',
24
+ description: 'How the saved value is rendered when not focused: a named preset\n(`short` | `long` | `colon`) or a token template (e.g. `h\'h\' m\'m\' s\'s\'`,\n`hh:mm:ss`). Defaults to `short`.',
25
+ required: false,
26
+ type: 'DurationFormat',
27
+ default: '"short"'
28
+ },
29
+ {
30
+ name: 'size',
31
+ description: 'Visual size of the input. Forwarded to the underlying `TextInput`.',
32
+ required: false,
33
+ type: 'InputSize'
34
+ },
35
+ {
36
+ name: 'variant',
37
+ description: 'Style variant of the input. Forwarded to the underlying `TextInput`.',
38
+ required: false,
39
+ type: 'InputVariant'
40
+ },
41
+ {
42
+ name: 'disabled',
43
+ description: 'Disables the input when true.',
44
+ required: false,
45
+ type: 'boolean'
46
+ },
47
+ {
48
+ name: 'label',
49
+ description: 'Label rendered above (or beside, for binary controls) the input.',
50
+ required: false,
51
+ type: 'string'
52
+ },
53
+ {
54
+ name: 'description',
55
+ description: 'Helper text rendered below the input.\nHidden when `error` is set.',
56
+ required: false,
57
+ type: 'string'
58
+ },
59
+ {
60
+ name: 'error',
61
+ description: 'Error message rendered below the input. When set, the control receives\n`aria-invalid="true"` and `data-state="invalid"`. May be either a string\nor an `Error` object whose `messages?: string[]` is rendered as stacked\nlines (with `Error.message` as the fallback).',
62
+ required: false,
63
+ type: 'string | FrappeUIError'
64
+ },
65
+ {
66
+ name: 'required',
67
+ description: 'Marks the field as required. Renders an asterisk next to the label and\nforwards `required` / `aria-required` to the underlying control.',
68
+ required: false,
69
+ type: 'boolean'
70
+ },
71
+ {
72
+ name: 'id',
73
+ description: 'HTML id of the underlying control. Auto-generated via `useId()` if omitted.',
74
+ required: false,
75
+ type: 'string'
76
+ }
77
+ ]
78
+
79
+ const emitsData = [
80
+ {
81
+ name: 'update:modelValue',
82
+ description: 'Fired when the model value changes.',
83
+ type: '[value: number | null]'
84
+ }
85
+ ]
86
+ </script>
87
+ ## API Reference
88
+
89
+ <PropsTable name="Duration" :data="propsData"/>
90
+
91
+ <EmitsTable :data="emitsData"/>
92
+
@@ -0,0 +1,183 @@
1
+ import Duration from './Duration.vue'
2
+ import { formatDuration, parseDuration } from './duration'
3
+
4
+ describe('Duration', () => {
5
+ it('renders a text input', () => {
6
+ cy.mount(Duration)
7
+ cy.get('input').should('exist')
8
+ })
9
+
10
+ it('displays the model value formatted (short by default)', () => {
11
+ cy.mount(Duration, { props: { modelValue: 5445 } })
12
+ cy.get('input').should('have.value', '1h 30m 45s')
13
+ })
14
+
15
+ it('renders the long and colon presets', () => {
16
+ cy.mount(Duration, { props: { modelValue: 5445, format: 'long' } })
17
+ cy.get('input').should('have.value', '1 hour 30 minutes 45 seconds')
18
+
19
+ cy.mount(Duration, { props: { modelValue: 5445, format: 'colon' } })
20
+ cy.get('input').should('have.value', '1:30:45')
21
+ })
22
+
23
+ it('renders a custom token template format', () => {
24
+ cy.mount(Duration, { props: { modelValue: 7323, format: 'hh:mm:ss' } })
25
+ cy.get('input').should('have.value', '02:02:03')
26
+ })
27
+
28
+ it('edits in canonical short notation regardless of display format', () => {
29
+ cy.mount(Duration, { props: { modelValue: 7323, format: 'hh:mm:ss' } })
30
+ cy.get('input').should('have.value', '02:02:03')
31
+ cy.get('input').focus().should('have.value', '2h 2m 3s')
32
+ })
33
+
34
+ it('commits a parsed value on blur', () => {
35
+ cy.mount(Duration, {
36
+ props: { 'onUpdate:modelValue': cy.spy().as('onUpdate') },
37
+ })
38
+ cy.get('input').type('1h 30m').blur()
39
+ cy.get('@onUpdate').should('be.calledWith', 5400)
40
+ cy.get('input').should('have.value', '1h 30m')
41
+ })
42
+
43
+ it('commits on Enter', () => {
44
+ cy.mount(Duration, {
45
+ props: { 'onUpdate:modelValue': cy.spy().as('onUpdate') },
46
+ })
47
+ cy.get('input').type('90s{enter}')
48
+ cy.get('@onUpdate').should('be.calledWith', 90)
49
+ })
50
+
51
+ it('reverts to the saved value on Escape without committing', () => {
52
+ cy.mount(Duration, {
53
+ props: {
54
+ modelValue: 60,
55
+ 'onUpdate:modelValue': cy.spy().as('onUpdate'),
56
+ },
57
+ })
58
+ cy.get('input').focus().clear().type('5h{esc}')
59
+ cy.get('@onUpdate').should('not.be.called')
60
+ cy.get('input').should('have.value', '1m')
61
+ })
62
+
63
+ it('shows an internal error for invalid input and does not commit', () => {
64
+ cy.mount(Duration, {
65
+ props: { 'onUpdate:modelValue': cy.spy().as('onUpdate') },
66
+ })
67
+ cy.get('input').type('not a duration').blur()
68
+ cy.get('@onUpdate').should('not.be.called')
69
+ cy.get('input').should('have.attr', 'aria-invalid', 'true')
70
+ cy.contains('Invalid format').should('exist')
71
+ })
72
+
73
+ it('releases focus after an invalid commit but keeps the rejected text visible', () => {
74
+ cy.mount(Duration, {
75
+ props: { 'onUpdate:modelValue': cy.spy().as('onUpdate') },
76
+ })
77
+ cy.get('input').as('field').type('not a duration')
78
+ cy.get('@field').blur()
79
+ // Focus is not trapped — the user can click away.
80
+ cy.get('@field').should('have.value', 'not a duration').and('not.have.focus')
81
+ // Re-focusing and correcting still commits.
82
+ cy.get('@field').focus().clear().type('1h 30m').blur()
83
+ cy.get('@onUpdate').should('be.calledWith', 5400)
84
+ })
85
+
86
+ it('a caller-provided error takes precedence over the internal one', () => {
87
+ cy.mount(Duration, { props: { error: 'Field is required.' } })
88
+ cy.contains('Field is required.').should('exist')
89
+ })
90
+
91
+ it('clearing the field commits null', () => {
92
+ cy.mount(Duration, {
93
+ props: {
94
+ modelValue: 60,
95
+ 'onUpdate:modelValue': cy.spy().as('onUpdate'),
96
+ },
97
+ })
98
+ cy.get('input').focus().clear().blur()
99
+ cy.get('@onUpdate').should('be.calledWith', null)
100
+ })
101
+
102
+ it('exposes the shared labeling contract', () => {
103
+ cy.mount(Duration, {
104
+ props: { label: 'Time spent', description: 'Logged hours', required: true },
105
+ })
106
+ cy.get('label').should('contain.text', 'Time spent')
107
+ cy.contains('Logged hours').should('exist')
108
+ cy.get('input').should('have.attr', 'aria-required', 'true')
109
+ })
110
+
111
+ it('forwards size and variant to the underlying input', () => {
112
+ cy.mount(Duration, { props: { size: 'lg', variant: 'outline' } })
113
+ cy.get('input').should('have.attr', 'data-size', 'lg')
114
+ })
115
+
116
+ it('disables the input', () => {
117
+ cy.mount(Duration, { props: { disabled: true } })
118
+ cy.get('input').should('be.disabled')
119
+ })
120
+ })
121
+
122
+ describe('parseDuration', () => {
123
+ it('parses unit syntax in any order', () => {
124
+ expect(parseDuration('1h 30m 45s')).to.equal(5445)
125
+ expect(parseDuration('45s 1h 30m')).to.equal(5445)
126
+ expect(parseDuration('1h30m45s')).to.equal(5445)
127
+ })
128
+
129
+ it('parses long-form units', () => {
130
+ expect(parseDuration('1 hour 30 minutes')).to.equal(5400)
131
+ expect(parseDuration('2hrs 15min')).to.equal(8100)
132
+ })
133
+
134
+ it('parses colon syntax', () => {
135
+ expect(parseDuration('1:30:45')).to.equal(5445)
136
+ expect(parseDuration('1:30')).to.equal(90)
137
+ expect(parseDuration(':45')).to.equal(45)
138
+ // Out-of-range groups overflow by design (stopwatch-style), not rejected.
139
+ expect(parseDuration('1:75')).to.equal(135)
140
+ expect(parseDuration('5:99')).to.equal(399)
141
+ })
142
+
143
+ it('treats a bare integer as seconds', () => {
144
+ expect(parseDuration('90')).to.equal(90)
145
+ })
146
+
147
+ it('rejects invalid input', () => {
148
+ expect(parseDuration('')).to.equal(null)
149
+ expect(parseDuration('abc')).to.equal(null)
150
+ expect(parseDuration('1h foo 2m')).to.equal(null)
151
+ expect(parseDuration('1h 2h')).to.equal(null) // duplicate unit
152
+ })
153
+ })
154
+
155
+ describe('formatDuration', () => {
156
+ it('formats each preset', () => {
157
+ expect(formatDuration(5445)).to.equal('1h 30m 45s')
158
+ expect(formatDuration(5445, 'long')).to.equal('1 hour 30 minutes 45 seconds')
159
+ expect(formatDuration(5445, 'colon')).to.equal('1:30:45')
160
+ })
161
+
162
+ it('the short preset omits zero parts', () => {
163
+ expect(formatDuration(90)).to.equal('1m 30s')
164
+ expect(formatDuration(3600)).to.equal('1h')
165
+ })
166
+
167
+ it('singularizes long-form units', () => {
168
+ expect(formatDuration(3661, 'long')).to.equal('1 hour 1 minute 1 second')
169
+ })
170
+
171
+ it('renders custom token templates literally', () => {
172
+ expect(formatDuration(5445, "h'h' m'm' s's'")).to.equal('1h 30m 45s')
173
+ expect(formatDuration(90, "h'h' m'm' s's'")).to.equal('0h 1m 30s')
174
+ expect(formatDuration(7323, 'hh:mm:ss')).to.equal('02:02:03')
175
+ expect(formatDuration(5445, "h 'hours'")).to.equal('1 hours')
176
+ })
177
+
178
+ it('renders zero and nullish values', () => {
179
+ expect(formatDuration(0)).to.equal('0s')
180
+ expect(formatDuration(null)).to.equal('')
181
+ expect(formatDuration(undefined)).to.equal('')
182
+ })
183
+ })