@heyramzi/ui 2.0.1 → 2.2.0

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 (341) hide show
  1. package/package.json +48 -24
  2. package/scripts/sync-shadcn.mjs +3 -4
  3. package/src/agents/agent-avatar.svelte +604 -575
  4. package/src/agents/agent-cast.svelte +179 -183
  5. package/src/agents/demo.svelte +125 -113
  6. package/src/agents/helpers.ts +23 -17
  7. package/src/agents/index.ts +4 -4
  8. package/src/agents/page-state.ts +6 -6
  9. package/src/agents/types.ts +6 -6
  10. package/src/ai/ai-button/AiButton.svelte +4 -12
  11. package/src/ai/generation-progress/GenerationProgress.svelte +10 -3
  12. package/src/ai/prompt-input/PromptInputTextarea.svelte +1 -2
  13. package/src/ai/response/Response.css +2 -3
  14. package/src/animations/avatar-group-hover.css +2 -16
  15. package/src/animations/card-tilt.css +3 -6
  16. package/src/animations/digit-pop-in.css +2 -15
  17. package/src/animations/dropdown.css +4 -17
  18. package/src/animations/input-clear-dissolve.css +5 -18
  19. package/src/animations/input-shake.css +9 -41
  20. package/src/animations/learn-more-hover.css +17 -9
  21. package/src/animations/like-button.css +2 -4
  22. package/src/animations/page-side-by-side.css +2 -10
  23. package/src/animations/plus-menu-morph.css +2 -5
  24. package/src/animations/shimmer-text.css +5 -18
  25. package/src/animations/spinning-counter.css +2 -6
  26. package/src/animations/success-check.css +2 -15
  27. package/src/animations/thinking-states.css +2 -7
  28. package/src/brand/brand.test.ts +21 -5
  29. package/src/brand/index.ts +4 -10
  30. package/src/canvas/README.md +9 -12
  31. package/src/canvas/card/card-frame.svelte +43 -39
  32. package/src/canvas/card/resize-handles.svelte +89 -80
  33. package/src/canvas/chrome/chrome-zoom.svelte +101 -100
  34. package/src/canvas/chrome/lock-badge.svelte +8 -8
  35. package/src/canvas/chrome/side-handles.svelte +24 -19
  36. package/src/canvas/chrome/size-chip.svelte +30 -30
  37. package/src/canvas/chrome/snap-guides.svelte +20 -20
  38. package/src/canvas/components/icons/ToolIcon.svelte +8 -8
  39. package/src/canvas/components/shared/svg/PreviewSvg.svelte +19 -19
  40. package/src/canvas/components/shared/svg/ShapeOutline.svelte +71 -48
  41. package/src/canvas/components/shared/svg/StrokeNode.svelte +21 -27
  42. package/src/canvas/components/shared/svg/TextRule.svelte +25 -17
  43. package/src/canvas/edges/bend-handle.svelte +46 -46
  44. package/src/canvas/edges/edge-gradient.svelte +10 -10
  45. package/src/canvas/edges/gradient-edge.svelte +45 -29
  46. package/src/canvas/editor/lock.test.ts +6 -1
  47. package/src/canvas/editor/style.ts +7 -1
  48. package/src/canvas/headless/svg-figure.test.ts +5 -1
  49. package/src/canvas/lod/lod-switch.svelte +111 -106
  50. package/src/canvas/pen/PenField.svelte +87 -81
  51. package/src/canvas/pen/PenOverlay.svelte +127 -127
  52. package/src/canvas/pen/ShapeNode.svelte +31 -32
  53. package/src/canvas/pen/TextNode.svelte +54 -53
  54. package/src/canvas/pen/host-field.test.svelte +7 -7
  55. package/src/canvas/pen/shapes.ts +7 -1
  56. package/src/canvas/screen/GlyphIcon.svelte +22 -17
  57. package/src/canvas/screen/PartView.svelte +138 -72
  58. package/src/canvas/screen/ScreenBody.svelte +81 -60
  59. package/src/canvas/screen/ScreenCard.svelte +20 -20
  60. package/src/canvas/ui/CanvasToolbar.svelte +173 -85
  61. package/src/canvas/ui/FrameLayer.svelte +59 -59
  62. package/src/canvas/ui/PenLayer.svelte +39 -30
  63. package/src/canvas/ui/StylePanel.svelte +119 -102
  64. package/src/canvas/ui/canvas-grid.svelte +137 -129
  65. package/src/charts/animated-value.svelte +1 -1
  66. package/src/charts/area-chart.css +2 -5
  67. package/src/charts/area-chart.svelte +1 -1
  68. package/src/charts/bar-chart.css +2 -3
  69. package/src/charts/bar-chart.svelte +1 -1
  70. package/src/charts/charts.css +5 -17
  71. package/src/charts/crosshair.svelte +1 -1
  72. package/src/charts/dashboard/dashboard-empty.svelte +25 -25
  73. package/src/charts/dashboard/dashboard-section.svelte +24 -24
  74. package/src/charts/dashboard/data-panel.svelte +50 -50
  75. package/src/charts/dashboard/data-sort-head.svelte +56 -56
  76. package/src/charts/dashboard/data-table.svelte +138 -138
  77. package/src/charts/dashboard/panel-grid.svelte +30 -30
  78. package/src/charts/dashboard/period-filter.svelte +25 -25
  79. package/src/charts/dashboard/rating-chip.svelte +19 -19
  80. package/src/charts/dashboard/stat-band.svelte +32 -32
  81. package/src/charts/dashboard/stat-tile.svelte +106 -99
  82. package/src/charts/donut-chart.svelte +1 -1
  83. package/src/charts/edge-fade.svelte +1 -1
  84. package/src/charts/figure.svelte +1 -1
  85. package/src/charts/gauge-chart.css +4 -7
  86. package/src/charts/gauge-chart.svelte +1 -1
  87. package/src/charts/hover-focus.svelte +1 -1
  88. package/src/charts/legend.css +1 -3
  89. package/src/charts/legend.svelte +1 -1
  90. package/src/charts/line-chart.svelte +1 -1
  91. package/src/charts/number-chart.css +6 -9
  92. package/src/charts/number-chart.svelte +1 -1
  93. package/src/charts/reference-line.svelte +19 -19
  94. package/src/charts/reveal.svelte +1 -1
  95. package/src/charts/ring-chart.css +2 -4
  96. package/src/charts/ring-chart.svelte +1 -1
  97. package/src/charts/sparkline.svelte +5 -2
  98. package/src/charts/stat-card.svelte +1 -1
  99. package/src/charts/time-series-chart.css +2 -5
  100. package/src/charts/trend-pill.css +2 -4
  101. package/src/charts/trend-pill.svelte +1 -1
  102. package/src/components/copy/copy-button.svelte +93 -98
  103. package/src/components/copy/copy-button.test.ts +10 -2
  104. package/src/components/data-table/infinite-scroll.svelte +42 -42
  105. package/src/components/data-table/pagination.svelte +34 -22
  106. package/src/components/data-table/row-arrow.svelte +19 -16
  107. package/src/components/data-table/row-context-menu.svelte +52 -52
  108. package/src/components/data-table/sortable-head.svelte +25 -25
  109. package/src/components/data-table/table-skeleton.svelte +41 -36
  110. package/src/components/docs/doc-page.svelte +18 -9
  111. package/src/components/docs/on-this-page.svelte +16 -3
  112. package/src/components/docs/scroll-spy.svelte.ts +2 -3
  113. package/src/components/field-row/field-row.svelte +10 -8
  114. package/src/components/hold-to-confirm/hold-to-confirm.svelte +5 -1
  115. package/src/components/icon/icon.svelte +14 -14
  116. package/src/components/language-flag/language-flag.svelte +3 -1
  117. package/src/components/list-page/detail-empty.svelte +34 -34
  118. package/src/components/list-page/detail-header.svelte +55 -47
  119. package/src/components/list-page/detail-panel.svelte +51 -59
  120. package/src/components/list-page/detail-row.svelte +19 -19
  121. package/src/components/list-page/detail-stack.svelte +21 -21
  122. package/src/components/list-page/empty-state.svelte +30 -30
  123. package/src/components/list-page/filter-item.svelte +20 -20
  124. package/src/components/list-page/filter-popover.svelte +60 -55
  125. package/src/components/list-page/grouped-list.svelte +4 -2
  126. package/src/components/list-page/index.ts +8 -4
  127. package/src/components/list-page/list-page-shell.svelte +2 -5
  128. package/src/components/list-page/master-detail.svelte +87 -88
  129. package/src/components/list-page/notice-bar.svelte +38 -38
  130. package/src/components/list-page/prev-next.svelte +63 -63
  131. package/src/components/list-page/rail-body.svelte +18 -18
  132. package/src/components/list-page/rail-empty.svelte +23 -30
  133. package/src/components/list-page/rail-head.svelte +24 -24
  134. package/src/components/list-page/rail-row.svelte +77 -73
  135. package/src/components/list-page/section-host.ts +2 -3
  136. package/src/components/locale-switch/locale-switch.svelte +41 -41
  137. package/src/components/mark/mark.svelte +34 -38
  138. package/src/components/open-in/index.ts +1 -5
  139. package/src/components/open-in/open-in.svelte +40 -40
  140. package/src/components/picker/mark-picker.svelte +49 -49
  141. package/src/components/picker/picker.svelte +5 -3
  142. package/src/components/section-header/index.ts +1 -0
  143. package/src/components/section-header/section-header.svelte +1 -1
  144. package/src/components/section-header/section-header.test.ts +7 -1
  145. package/src/components/section-header/section-label.svelte +39 -0
  146. package/src/components/segmented/animated-tabs.svelte +31 -2
  147. package/src/components/segmented/collapse.test.ts +2 -1
  148. package/src/components/segmented/index.ts +2 -0
  149. package/src/components/segmented/segment.svelte +52 -49
  150. package/src/components/segmented/segmented-filter.svelte +79 -80
  151. package/src/components/segmented/segmented-nav.svelte +34 -40
  152. package/src/components/segmented/segmented-overflow.svelte +53 -50
  153. package/src/components/segmented/segmented-track.svelte +62 -46
  154. package/src/components/segmented/segmented.ts +2 -3
  155. package/src/components/star-rating-input/star-rating-input.svelte +103 -103
  156. package/src/components/svg/svg.svelte +4 -4
  157. package/src/components/table-view/table-view.svelte +17 -2
  158. package/src/components/table-view/table-view.test.ts +7 -1
  159. package/src/components/table-view/types.ts +11 -11
  160. package/src/components/tags-input/index.ts +13 -0
  161. package/src/components/tags-input/tags-input.svelte +305 -0
  162. package/src/components/tags-input/tags.test.ts +64 -0
  163. package/src/components/tags-input/tags.ts +142 -0
  164. package/src/components/theme/index.ts +2 -4
  165. package/src/components/theme/theme-provider.svelte +1 -3
  166. package/src/components/toolbar/index.ts +2 -0
  167. package/src/components/{list-page → toolbar}/toolbar-search.svelte +25 -25
  168. package/src/components/toolbar/toolbar.svelte +13 -9
  169. package/src/components/toolbar/toolbar.test.ts +10 -1
  170. package/src/design/index.css +21 -78
  171. package/src/design/polish.css +26 -35
  172. package/src/editor/editor-toolbar.svelte +104 -99
  173. package/src/editor/editor.svelte +296 -266
  174. package/src/editor/image-toolbar.svelte +20 -22
  175. package/src/editor/index.ts +1 -4
  176. package/src/editor/inline-editor.svelte +280 -238
  177. package/src/editor/markdown.ts +15 -15
  178. package/src/editor/renderer.svelte +71 -71
  179. package/src/editor/slash-menu.svelte +24 -27
  180. package/src/effects/animated-beam/diagrams/BeamSvg.svelte +1 -1
  181. package/src/effects/image-comparison/ImageComparison.svelte +11 -1
  182. package/src/effects/interactive-hover-button/InteractiveHoverButton.svelte +59 -46
  183. package/src/effects/magic-card/MagicCard.css +14 -7
  184. package/src/effects/marquee/Marquee.svelte +3 -5
  185. package/src/effects/reveal/observe.ts +0 -3
  186. package/src/effects/spotlight/Spotlight.svelte +1 -1
  187. package/src/effects/theme-toggle/ThemeToggle.css +25 -22
  188. package/src/effects/theme-toggle/ThemeToggle.svelte +6 -7
  189. package/src/gallery.test.ts +14 -4
  190. package/src/inbox/README.md +27 -0
  191. package/src/inbox/inbox.test.ts +214 -0
  192. package/src/inbox/index.ts +31 -0
  193. package/src/inbox/logic.ts +169 -0
  194. package/src/inbox/thread-inbox.svelte +439 -0
  195. package/src/inbox/types.ts +161 -0
  196. package/src/lib/theme-transition.ts +2 -4
  197. package/src/lib/tone.ts +2 -4
  198. package/src/lib/utils.ts +2 -2
  199. package/src/modules.test.ts +105 -0
  200. package/src/onboarding/define-tours.test.ts +55 -40
  201. package/src/onboarding/define-tours.ts +23 -23
  202. package/src/onboarding/demo.svelte +24 -5
  203. package/src/onboarding/index.ts +1 -8
  204. package/src/onboarding/onboarding-provider.svelte +19 -7
  205. package/src/onboarding/onboarding.svelte.ts +16 -16
  206. package/src/onboarding/pick.test.ts +61 -52
  207. package/src/onboarding/pick.ts +16 -13
  208. package/src/onboarding/stores.test.ts +80 -80
  209. package/src/onboarding/stores.ts +41 -39
  210. package/src/onboarding/tour-card.svelte +30 -5
  211. package/src/onboarding/tour-runner.svelte +12 -3
  212. package/src/onboarding/types.ts +20 -12
  213. package/src/onboarding/wait.ts +22 -22
  214. package/src/panel/README.md +5 -0
  215. package/src/panel/index.ts +2 -0
  216. package/src/panel/side-panel.svelte +194 -0
  217. package/src/record/README.md +3 -0
  218. package/src/record/demo-data.ts +352 -0
  219. package/src/record/field-section.svelte +57 -0
  220. package/src/record/fields.ts +194 -0
  221. package/src/record/format.ts +94 -0
  222. package/src/{tasks/record → record}/index.ts +49 -35
  223. package/src/{tasks/record → record}/info-list.svelte +145 -114
  224. package/src/record/kinds.ts +42 -0
  225. package/src/record/logic.ts +326 -0
  226. package/src/record/next-up.svelte +81 -0
  227. package/src/{tasks/record → record}/project-list.svelte +56 -50
  228. package/src/{tasks/record → record}/record-demo.svelte +130 -110
  229. package/src/{tasks/record → record}/record-header.svelte +37 -28
  230. package/src/{tasks/record → record}/record-shell.svelte +54 -54
  231. package/src/record/record-stats.svelte +48 -0
  232. package/src/record/record-timeline.svelte +251 -0
  233. package/src/record/record.test.ts +471 -0
  234. package/src/{tasks/record → record}/steps-rail.svelte +225 -216
  235. package/src/record/types.ts +299 -0
  236. package/src/record/where-we-are.svelte +125 -0
  237. package/src/shadcn/button/button.svelte +1 -1
  238. package/src/shadcn/card/card-title.svelte +1 -5
  239. package/src/shadcn/hooks/use-highlighted-code.svelte.ts +2 -13
  240. package/src/shadcn/sheet/sheet-content.svelte +2 -4
  241. package/src/shadcn/table/table-cell.svelte +2 -3
  242. package/src/shadcn/utilities.css +3 -8
  243. package/src/slides/Controls.svelte +150 -124
  244. package/src/slides/Deck.svelte +243 -238
  245. package/src/slides/Fragment.svelte +64 -57
  246. package/src/slides/Overview.svelte +106 -101
  247. package/src/slides/Presenter.svelte +300 -266
  248. package/src/slides/Slide.svelte +185 -170
  249. package/src/slides/SlideList.svelte +16 -12
  250. package/src/slides/ambient.d.ts +1 -1
  251. package/src/slides/bridges.ts +42 -23
  252. package/src/slides/deck-controller.svelte.ts +47 -35
  253. package/src/slides/demo-slides.svelte +330 -306
  254. package/src/slides/demo.svelte +164 -160
  255. package/src/slides/export/capture.ts +20 -16
  256. package/src/slides/export/ffmpeg.ts +43 -38
  257. package/src/slides/export/index.ts +5 -5
  258. package/src/slides/export/launch.ts +3 -1
  259. package/src/slides/export/pdf.ts +30 -27
  260. package/src/slides/export/types.ts +10 -8
  261. package/src/slides/index.ts +11 -11
  262. package/src/slides/labels.ts +22 -19
  263. package/src/slides/layout.ts +9 -2
  264. package/src/slides/model.ts +4 -4
  265. package/src/slides/navigation.ts +43 -31
  266. package/src/slides/theme.css +5 -7
  267. package/src/slides/types.ts +9 -10
  268. package/src/status/README.md +18 -0
  269. package/src/status/define-statuses.ts +25 -0
  270. package/src/status/index.ts +11 -0
  271. package/src/{tasks → status}/status-badge.svelte +3 -1
  272. package/src/{tasks → status}/status-dot.svelte +1 -3
  273. package/src/{tasks → status}/status-icon.svelte +1 -1
  274. package/src/status/status-types.ts +40 -0
  275. package/src/tasks/README.md +1 -1
  276. package/src/tasks/board-order.ts +2 -4
  277. package/src/tasks/demo.svelte +192 -34
  278. package/src/tasks/detail/detail-demo.svelte +75 -15
  279. package/src/tasks/detail/detail-types.ts +45 -45
  280. package/src/tasks/detail/task-detail.svelte +40 -10
  281. package/src/tasks/detail/task-property.svelte +8 -2
  282. package/src/tasks/detail/task-thread.svelte +53 -12
  283. package/src/tasks/gantt/gantt-bar.svelte +17 -6
  284. package/src/tasks/gantt/gantt-grid.svelte +40 -9
  285. package/src/tasks/gantt/gantt-group-row.svelte +8 -2
  286. package/src/tasks/gantt/gantt-header.svelte +9 -3
  287. package/src/tasks/gantt/gantt-task-row.svelte +26 -6
  288. package/src/tasks/gantt/gantt.css +2 -1
  289. package/src/tasks/gantt/gantt.test.ts +4 -2
  290. package/src/tasks/gantt/logic.test.ts +110 -20
  291. package/src/tasks/gantt/logic.ts +42 -6
  292. package/src/tasks/inbox/inbox-card.svelte +15 -7
  293. package/src/tasks/inbox/inbox-draft.svelte +1 -1
  294. package/src/tasks/inbox/inbox-logic.ts +6 -40
  295. package/src/tasks/inbox/inbox-statuses.ts +24 -24
  296. package/src/tasks/inbox/inbox-trace.svelte +9 -3
  297. package/src/tasks/inbox/inbox-types.ts +54 -58
  298. package/src/tasks/inbox/index.ts +22 -21
  299. package/src/tasks/index.ts +52 -53
  300. package/src/tasks/kanban-board.svelte +16 -12
  301. package/src/tasks/kanban-types.ts +2 -3
  302. package/src/tasks/presets/sales.ts +7 -7
  303. package/src/tasks/task-gantt.svelte +28 -8
  304. package/src/tasks/task-list.svelte +41 -17
  305. package/src/tasks/time/duration.ts +40 -40
  306. package/src/tasks/time/time-chip.svelte +9 -2
  307. package/src/tasks/time/time-dot.svelte +1 -1
  308. package/src/tasks/time/time-ring.svelte +11 -3
  309. package/src/tasks/time/time-tracker.svelte +57 -15
  310. package/src/tasks/time/time-types.ts +57 -57
  311. package/src/tasks/use-kanban-dnd.svelte.ts +2 -3
  312. package/src/thread/README.md +31 -0
  313. package/src/thread/index.ts +12 -0
  314. package/src/thread/logic.ts +61 -0
  315. package/src/thread/thread.svelte +432 -0
  316. package/src/thread/thread.test.ts +73 -0
  317. package/src/thread/types.ts +93 -0
  318. package/src/tokens/craft.css +17 -53
  319. package/src/tokens/index.css +3 -13
  320. package/src/tokens/palette.css +13 -11
  321. package/src/tokens/primitives.css +10 -23
  322. package/src/components/list-page/section-label.svelte +0 -39
  323. package/src/tasks/define-statuses.ts +0 -32
  324. package/src/tasks/inbox/inbox.svelte +0 -203
  325. package/src/tasks/record/demo-data.ts +0 -286
  326. package/src/tasks/record/field-section.svelte +0 -53
  327. package/src/tasks/record/fields.ts +0 -142
  328. package/src/tasks/record/format.ts +0 -87
  329. package/src/tasks/record/kinds.ts +0 -42
  330. package/src/tasks/record/logic.ts +0 -324
  331. package/src/tasks/record/next-up.svelte +0 -70
  332. package/src/tasks/record/record-stats.svelte +0 -48
  333. package/src/tasks/record/record-timeline.svelte +0 -231
  334. package/src/tasks/record/record.test.ts +0 -350
  335. package/src/tasks/record/types.ts +0 -280
  336. package/src/tasks/record/where-we-are.svelte +0 -121
  337. package/src/tasks/side-panel.svelte +0 -193
  338. package/src/tasks/status-types.ts +0 -40
  339. /package/src/components/{list-page → toolbar}/toolbar-trigger.ts +0 -0
  340. /package/src/{tasks → panel}/side-panel-types.ts +0 -0
  341. /package/src/{tasks/record → record}/gutter.ts +0 -0
@@ -1,235 +1,236 @@
1
1
  <script lang="ts">
2
- // =========================
3
- // IMPORTS
4
- // =========================
5
- import {
6
- ArrowCounterClockwise,
7
- ArrowDown,
8
- ArrowUp,
9
- Check,
10
- DotsThreeVertical,
11
- Eye,
12
- EyeSlash,
13
- PencilSimple,
14
- } from "phosphor-svelte";
15
- import { cn } from "../../lib/utils.js";
16
- import { Button } from "../../shadcn/button/index.js";
17
- import { Input } from "../../shadcn/input/index.js";
18
- import { Skeleton } from "../../shadcn/skeleton/index.js";
19
- import * as Popover from "../../shadcn/popover/index.js";
20
- import * as ContextMenu from "../../shadcn/context-menu/index.js";
21
- import { todayIso } from "../dates.js";
22
- import { formatRange } from "./format.js";
23
- import {
24
- groupSteps,
25
- nudgeStep,
26
- reorderIds,
27
- stepMarkers,
28
- stepProgress,
29
- visibleSteps,
30
- } from "./logic.js";
31
- import {
32
- resolveLabels,
33
- type RecordLabels,
34
- type StepDef,
35
- type StepGroupDef,
36
- type StepMarker,
37
- } from "./types.js";
2
+ // =========================
3
+ // IMPORTS
4
+ // =========================
5
+ import {
6
+ ArrowCounterClockwise,
7
+ ArrowDown,
8
+ ArrowUp,
9
+ Check,
10
+ DotsThreeVertical,
11
+ Eye,
12
+ EyeSlash,
13
+ PencilSimple,
14
+ } from "phosphor-svelte";
15
+ import { cn } from "../lib/utils.js";
16
+ import { Button } from "../shadcn/button/index.js";
17
+ import { Input } from "../shadcn/input/index.js";
18
+ import { Skeleton } from "../shadcn/skeleton/index.js";
19
+ import * as Popover from "../shadcn/popover/index.js";
20
+ import * as ContextMenu from "../shadcn/context-menu/index.js";
21
+ import { todayIso } from "../tasks/dates.js";
22
+ import { formatRange } from "./format.js";
23
+ import {
24
+ groupSteps,
25
+ nudgeStep,
26
+ reorderIds,
27
+ stepMarkers,
28
+ stepProgress,
29
+ visibleSteps,
30
+ } from "./logic.js";
31
+ import {
32
+ resolveLabels,
33
+ type RecordLabels,
34
+ type StepDef,
35
+ type StepGroupDef,
36
+ type StepMarker,
37
+ } from "./types.js";
38
38
 
39
- // =========================
40
- // PROPS
41
- // =========================
42
- interface Props {
43
- steps: StepDef[];
44
- /** Group headers in the order to draw them, such as Sales then Delivery. */
45
- groups?: StepGroupDef[];
46
- activeId?: string | null;
47
- onSelect?: (id: string) => void;
48
- /** Show or hide a step. Without it the visibility menu isn't drawn. */
49
- onToggle?: (id: string, visible: boolean) => void;
50
- /** The whole new id order, after a drag or a keyboard move. */
51
- onReorder?: (ids: string[]) => void;
52
- /** An empty label resets the name. */
53
- onRename?: (id: string, label: string) => void;
54
- loading?: boolean;
55
- labels?: Partial<RecordLabels>;
56
- locale?: string;
57
- /** ISO date. Decides "late". Defaults to the viewer's today. */
58
- today?: string;
59
- class?: string;
60
- }
61
- let {
62
- steps,
63
- groups = [],
64
- activeId = null,
65
- onSelect,
66
- onToggle,
67
- onReorder,
68
- onRename,
69
- loading = false,
70
- labels,
71
- locale,
72
- today = todayIso(),
73
- class: className,
74
- }: Props = $props();
75
-
76
- // =========================
77
- // STATE
78
- // =========================
79
- let menuOpen = $state(false);
80
- // The order shown while the host round-trips. Cleared when the steps catch up,
81
- // or after five seconds so a failed save can't pin a wrong order.
82
- let pendingOrder = $state<string[] | null>(null);
83
- let pendingTimer: ReturnType<typeof setTimeout> | null = null;
84
- let draggedId = $state<string | null>(null);
85
- let draggedGroup = $state<string | undefined>(undefined);
86
- let overId = $state<string | null>(null);
87
- let insertBefore = $state(true);
88
- let renamingId = $state<string | null>(null);
89
- let renameValue = $state("");
90
- let renameInput = $state<HTMLInputElement | null>(null);
91
- // WHY: the context menu closing hands focus back and blurs a fresh input, which
92
- // would commit a rename nobody typed. Blur counts only once the input has had a frame.
93
- let renameArmed = $state(false);
94
-
95
- // =========================
96
- // DERIVED
97
- // =========================
98
- let t = $derived(resolveLabels(labels));
99
- let ordered = $derived.by(() => {
100
- if (!pendingOrder) return steps;
101
- const byId = new Map(steps.map((s) => [s.id, s]));
102
- return pendingOrder.flatMap((id) => byId.get(id) ?? []);
103
- });
104
- let visible = $derived(visibleSteps(ordered));
105
- let markers = $derived(stepMarkers(ordered, today));
106
- let buckets = $derived(groupSteps(visible, groups));
107
- let menuBuckets = $derived(groupSteps(ordered, groups));
108
- let progress = $derived(stepProgress(ordered));
109
- let hiddenCount = $derived(ordered.length - visible.length);
110
- let canReorder = $derived(Boolean(onReorder));
111
-
112
- // =========================
113
- // EFFECTS
114
- // =========================
115
- $effect(() => {
116
- if (!pendingOrder) return;
117
- if (steps.map((s) => s.id).join(",") === pendingOrder.join(",")) clearPending();
118
- });
119
-
120
- $effect(() => {
121
- if (renamingId && renameInput) {
122
- renameInput.focus();
123
- renameInput.select();
124
- const frame = requestAnimationFrame(() => (renameArmed = true));
125
- return () => {
126
- cancelAnimationFrame(frame);
127
- renameArmed = false;
128
- };
39
+ // =========================
40
+ // PROPS
41
+ // =========================
42
+ interface Props {
43
+ steps: StepDef[];
44
+ /** Group headers in the order to draw them, such as Sales then Delivery. */
45
+ groups?: StepGroupDef[];
46
+ activeId?: string | null;
47
+ onSelect?: (id: string) => void;
48
+ /** Show or hide a step. Without it the visibility menu isn't drawn. */
49
+ onToggle?: (id: string, visible: boolean) => void;
50
+ /** The whole new id order, after a drag or a keyboard move. */
51
+ onReorder?: (ids: string[]) => void;
52
+ /** An empty label resets the name. */
53
+ onRename?: (id: string, label: string) => void;
54
+ loading?: boolean;
55
+ labels?: Partial<RecordLabels>;
56
+ locale?: string;
57
+ /** ISO date. Decides "late". Defaults to the viewer's today. */
58
+ today?: string;
59
+ class?: string;
129
60
  }
130
- renameArmed = false;
131
- });
61
+ let {
62
+ steps,
63
+ groups = [],
64
+ activeId = null,
65
+ onSelect,
66
+ onToggle,
67
+ onReorder,
68
+ onRename,
69
+ loading = false,
70
+ labels,
71
+ locale,
72
+ today = todayIso(),
73
+ class: className,
74
+ }: Props = $props();
132
75
 
133
- // =========================
134
- // FUNCTIONS
135
- // =========================
136
- function clearPending() {
137
- pendingOrder = null;
138
- if (pendingTimer) clearTimeout(pendingTimer);
139
- pendingTimer = null;
140
- }
76
+ // =========================
77
+ // STATE
78
+ // =========================
79
+ let menuOpen = $state(false);
80
+ // The order shown while the host round-trips. Cleared when the steps catch up,
81
+ // or after five seconds so a failed save can't pin a wrong order.
82
+ let pendingOrder = $state<string[] | null>(null);
83
+ let pendingTimer: ReturnType<typeof setTimeout> | null = null;
84
+ let draggedId = $state<string | null>(null);
85
+ let draggedGroup = $state<string | undefined>(undefined);
86
+ let overId = $state<string | null>(null);
87
+ let insertBefore = $state(true);
88
+ let renamingId = $state<string | null>(null);
89
+ let renameValue = $state("");
90
+ let renameInput = $state<HTMLInputElement | null>(null);
91
+ // WHY: the context menu closing hands focus back and blurs a fresh input, which
92
+ // would commit a rename nobody typed. Blur counts only once the input has had a frame.
93
+ let renameArmed = $state(false);
141
94
 
142
- function applyOrder(ids: string[]) {
143
- if (ids.join(",") === ordered.map((s) => s.id).join(",")) return;
144
- pendingOrder = ids;
145
- if (pendingTimer) clearTimeout(pendingTimer);
146
- pendingTimer = setTimeout(clearPending, 5000);
147
- onReorder?.(ids);
148
- }
95
+ // =========================
96
+ // DERIVED
97
+ // =========================
98
+ let t = $derived(resolveLabels(labels));
99
+ let ordered = $derived.by(() => {
100
+ if (!pendingOrder) return steps;
101
+ const byId = new Map(steps.map((s) => [s.id, s]));
102
+ return pendingOrder.flatMap((id) => byId.get(id) ?? []);
103
+ });
104
+ let visible = $derived(visibleSteps(ordered));
105
+ let markers = $derived(stepMarkers(ordered, today));
106
+ let buckets = $derived(groupSteps(visible, groups));
107
+ let menuBuckets = $derived(groupSteps(ordered, groups));
108
+ let progress = $derived(stepProgress(ordered));
109
+ let hiddenCount = $derived(ordered.length - visible.length);
110
+ let canReorder = $derived(Boolean(onReorder));
149
111
 
150
- function dateLabel(step: StepDef): string | null {
151
- const from = step.start ?? step.end;
152
- return from ? formatRange(from, step.start ? step.end : null, locale) : null;
153
- }
112
+ // =========================
113
+ // EFFECTS
114
+ // =========================
115
+ $effect(() => {
116
+ if (!pendingOrder) return;
117
+ if (steps.map((s) => s.id).join(",") === pendingOrder.join(",")) clearPending();
118
+ });
154
119
 
155
- function resetDrag() {
156
- draggedId = null;
157
- draggedGroup = undefined;
158
- overId = null;
159
- }
120
+ $effect(() => {
121
+ if (renamingId && renameInput) {
122
+ renameInput.focus();
123
+ renameInput.select();
124
+ const frame = requestAnimationFrame(() => (renameArmed = true));
125
+ return () => {
126
+ cancelAnimationFrame(frame);
127
+ renameArmed = false;
128
+ };
129
+ }
130
+ renameArmed = false;
131
+ });
160
132
 
161
- function dragStart(event: DragEvent, step: StepDef) {
162
- draggedId = step.id;
163
- draggedGroup = step.group;
164
- if (event.dataTransfer) {
165
- event.dataTransfer.effectAllowed = "move";
166
- event.dataTransfer.setData("text/plain", step.id);
133
+ // =========================
134
+ // FUNCTIONS
135
+ // =========================
136
+ function clearPending() {
137
+ pendingOrder = null;
138
+ if (pendingTimer) clearTimeout(pendingTimer);
139
+ pendingTimer = null;
167
140
  }
168
- }
169
141
 
170
- // A step moves inside its own group only: a group is a phase, and dragging
171
- // between two would silently change what a step means.
172
- function dragOver(event: DragEvent, target: StepDef) {
173
- if (!draggedId || draggedId === target.id || target.group !== draggedGroup) return;
174
- event.preventDefault();
175
- if (event.dataTransfer) event.dataTransfer.dropEffect = "move";
176
- const box = (event.currentTarget as HTMLElement).getBoundingClientRect();
177
- const before = event.clientY < box.top + box.height / 2;
178
- if (overId !== target.id || insertBefore !== before) {
179
- overId = target.id;
180
- insertBefore = before;
142
+ function applyOrder(ids: string[]) {
143
+ if (ids.join(",") === ordered.map((s) => s.id).join(",")) return;
144
+ pendingOrder = ids;
145
+ if (pendingTimer) clearTimeout(pendingTimer);
146
+ pendingTimer = setTimeout(clearPending, 5000);
147
+ onReorder?.(ids);
181
148
  }
182
- }
183
149
 
184
- function drop(event: DragEvent, target: StepDef) {
185
- const moved = draggedId;
186
- const before = insertBefore;
187
- const sameGroup = target.group === draggedGroup;
188
- resetDrag();
189
- if (!moved || moved === target.id || !sameGroup) return;
190
- event.preventDefault();
191
- applyOrder(
192
- reorderIds(
193
- ordered.map((s) => s.id),
194
- moved,
195
- target.id,
196
- before,
197
- ),
198
- );
199
- }
150
+ function dateLabel(step: StepDef): string | null {
151
+ const from = step.start ?? step.end;
152
+ return from ? formatRange(from, step.start ? step.end : null, locale) : null;
153
+ }
200
154
 
201
- // Alt plus an arrow, not a bare arrow: the rail is walked with arrow keys and
202
- // taking them would lock keyboard users out.
203
- function keydown(event: KeyboardEvent, step: StepDef) {
204
- if (!canReorder || !event.altKey || (event.key !== "ArrowUp" && event.key !== "ArrowDown")) return;
205
- event.preventDefault();
206
- applyOrder(nudgeStep(ordered, step.id, event.key === "ArrowUp" ? -1 : 1));
207
- }
155
+ function resetDrag() {
156
+ draggedId = null;
157
+ draggedGroup = undefined;
158
+ overId = null;
159
+ }
208
160
 
209
- function startRename(step: StepDef) {
210
- renamingId = step.id;
211
- renameValue = step.label;
212
- }
161
+ function dragStart(event: DragEvent, step: StepDef) {
162
+ draggedId = step.id;
163
+ draggedGroup = step.group;
164
+ if (event.dataTransfer) {
165
+ event.dataTransfer.effectAllowed = "move";
166
+ event.dataTransfer.setData("text/plain", step.id);
167
+ }
168
+ }
213
169
 
214
- function commitRename() {
215
- if (!renamingId) return;
216
- const step = ordered.find((s) => s.id === renamingId);
217
- const next = renameValue.trim();
218
- renamingId = null;
219
- // WHY: an unchanged name sends nothing, so a stray blur can't mark a step renamed.
220
- if (!step || next === step.label) return;
221
- onRename?.(step.id, next);
222
- }
170
+ // A step moves inside its own group only: a group is a phase, and dragging
171
+ // between two would silently change what a step means.
172
+ function dragOver(event: DragEvent, target: StepDef) {
173
+ if (!draggedId || draggedId === target.id || target.group !== draggedGroup) return;
174
+ event.preventDefault();
175
+ if (event.dataTransfer) event.dataTransfer.dropEffect = "move";
176
+ const box = (event.currentTarget as HTMLElement).getBoundingClientRect();
177
+ const before = event.clientY < box.top + box.height / 2;
178
+ if (overId !== target.id || insertBefore !== before) {
179
+ overId = target.id;
180
+ insertBefore = before;
181
+ }
182
+ }
223
183
 
224
- function renameKey(event: KeyboardEvent) {
225
- if (event.key === "Enter") {
184
+ function drop(event: DragEvent, target: StepDef) {
185
+ const moved = draggedId;
186
+ const before = insertBefore;
187
+ const sameGroup = target.group === draggedGroup;
188
+ resetDrag();
189
+ if (!moved || moved === target.id || !sameGroup) return;
226
190
  event.preventDefault();
227
- commitRename();
228
- } else if (event.key === "Escape") {
191
+ applyOrder(
192
+ reorderIds(
193
+ ordered.map((s) => s.id),
194
+ moved,
195
+ target.id,
196
+ before,
197
+ ),
198
+ );
199
+ }
200
+
201
+ // Alt plus an arrow, not a bare arrow: the rail is walked with arrow keys and
202
+ // taking them would lock keyboard users out.
203
+ function keydown(event: KeyboardEvent, step: StepDef) {
204
+ if (!canReorder || !event.altKey || (event.key !== "ArrowUp" && event.key !== "ArrowDown"))
205
+ return;
229
206
  event.preventDefault();
207
+ applyOrder(nudgeStep(ordered, step.id, event.key === "ArrowUp" ? -1 : 1));
208
+ }
209
+
210
+ function startRename(step: StepDef) {
211
+ renamingId = step.id;
212
+ renameValue = step.label;
213
+ }
214
+
215
+ function commitRename() {
216
+ if (!renamingId) return;
217
+ const step = ordered.find((s) => s.id === renamingId);
218
+ const next = renameValue.trim();
230
219
  renamingId = null;
220
+ // WHY: an unchanged name sends nothing, so a stray blur can't mark a step renamed.
221
+ if (!step || next === step.label) return;
222
+ onRename?.(step.id, next);
223
+ }
224
+
225
+ function renameKey(event: KeyboardEvent) {
226
+ if (event.key === "Enter") {
227
+ event.preventDefault();
228
+ commitRename();
229
+ } else if (event.key === "Escape") {
230
+ event.preventDefault();
231
+ renamingId = null;
232
+ }
231
233
  }
232
- }
233
234
  </script>
234
235
 
235
236
  <!-- =========================== -->
@@ -289,7 +290,9 @@ function renameKey(event: KeyboardEvent) {
289
290
  <Popover.Content align="end" class="w-64 p-1">
290
291
  {#each menuBuckets as bucket (bucket.key)}
291
292
  {#if bucket.label}
292
- <p class="px-2 pt-2 pb-1 text-xs font-medium tracking-wide text-muted-foreground uppercase">
293
+ <p
294
+ class="px-2 pt-2 pb-1 text-xs font-medium tracking-wide text-muted-foreground uppercase"
295
+ >
293
296
  {bucket.label}
294
297
  </p>
295
298
  {/if}
@@ -316,7 +319,9 @@ function renameKey(event: KeyboardEvent) {
316
319
  </Button>
317
320
  {/each}
318
321
  {/each}
319
- <p class="mt-1 border-t border-border px-2 pt-2 pb-1 text-xs tabular-nums text-muted-foreground">
322
+ <p
323
+ class="mt-1 border-t border-border px-2 pt-2 pb-1 text-xs tabular-nums text-muted-foreground"
324
+ >
320
325
  {t.shownCount(visible.length, ordered.length)}
321
326
  </p>
322
327
  </Popover.Content>
@@ -337,7 +342,9 @@ function renameKey(event: KeyboardEvent) {
337
342
  {#each buckets as bucket (bucket.key)}
338
343
  <div class="flex flex-col">
339
344
  {#if bucket.label}
340
- <h3 class="mb-1 px-2 text-xs font-medium tracking-wide text-muted-foreground uppercase select-none">
345
+ <h3
346
+ class="mb-1 px-2 text-xs font-medium tracking-wide text-muted-foreground uppercase select-none"
347
+ >
341
348
  {bucket.label}
342
349
  </h3>
343
350
  {/if}
@@ -388,7 +395,9 @@ function renameKey(event: KeyboardEvent) {
388
395
  )}
389
396
  ></span>
390
397
  {/if}
391
- <span class="relative z-10 mt-0.5 flex size-4 shrink-0 items-center justify-center">
398
+ <span
399
+ class="relative z-10 mt-0.5 flex size-4 shrink-0 items-center justify-center"
400
+ >
392
401
  {@render marker(kind)}
393
402
  </span>
394
403
  <span class="min-w-0 flex-1">