@heyramzi/ui 2.1.0 → 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 (312) hide show
  1. package/package.json +7 -8
  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/card/card-frame.svelte +43 -39
  31. package/src/canvas/card/resize-handles.svelte +89 -80
  32. package/src/canvas/chrome/chrome-zoom.svelte +101 -100
  33. package/src/canvas/chrome/lock-badge.svelte +8 -8
  34. package/src/canvas/chrome/side-handles.svelte +24 -19
  35. package/src/canvas/chrome/size-chip.svelte +30 -30
  36. package/src/canvas/chrome/snap-guides.svelte +20 -20
  37. package/src/canvas/components/icons/ToolIcon.svelte +8 -8
  38. package/src/canvas/components/shared/svg/PreviewSvg.svelte +19 -19
  39. package/src/canvas/components/shared/svg/ShapeOutline.svelte +71 -48
  40. package/src/canvas/components/shared/svg/StrokeNode.svelte +21 -27
  41. package/src/canvas/components/shared/svg/TextRule.svelte +25 -17
  42. package/src/canvas/edges/bend-handle.svelte +46 -46
  43. package/src/canvas/edges/edge-gradient.svelte +10 -10
  44. package/src/canvas/edges/gradient-edge.svelte +45 -29
  45. package/src/canvas/editor/lock.test.ts +6 -1
  46. package/src/canvas/editor/style.ts +7 -1
  47. package/src/canvas/headless/svg-figure.test.ts +5 -1
  48. package/src/canvas/lod/lod-switch.svelte +111 -106
  49. package/src/canvas/pen/PenField.svelte +87 -81
  50. package/src/canvas/pen/PenOverlay.svelte +127 -127
  51. package/src/canvas/pen/ShapeNode.svelte +31 -32
  52. package/src/canvas/pen/TextNode.svelte +54 -53
  53. package/src/canvas/pen/host-field.test.svelte +7 -7
  54. package/src/canvas/pen/shapes.ts +7 -1
  55. package/src/canvas/screen/GlyphIcon.svelte +22 -17
  56. package/src/canvas/screen/PartView.svelte +138 -72
  57. package/src/canvas/screen/ScreenBody.svelte +81 -60
  58. package/src/canvas/screen/ScreenCard.svelte +20 -20
  59. package/src/canvas/ui/CanvasToolbar.svelte +173 -85
  60. package/src/canvas/ui/FrameLayer.svelte +59 -59
  61. package/src/canvas/ui/PenLayer.svelte +39 -30
  62. package/src/canvas/ui/StylePanel.svelte +119 -102
  63. package/src/canvas/ui/canvas-grid.svelte +137 -129
  64. package/src/charts/animated-value.svelte +1 -1
  65. package/src/charts/area-chart.css +2 -5
  66. package/src/charts/area-chart.svelte +1 -1
  67. package/src/charts/bar-chart.css +2 -3
  68. package/src/charts/bar-chart.svelte +1 -1
  69. package/src/charts/charts.css +5 -17
  70. package/src/charts/crosshair.svelte +1 -1
  71. package/src/charts/dashboard/dashboard-empty.svelte +25 -25
  72. package/src/charts/dashboard/dashboard-section.svelte +24 -24
  73. package/src/charts/dashboard/data-panel.svelte +50 -50
  74. package/src/charts/dashboard/data-sort-head.svelte +56 -56
  75. package/src/charts/dashboard/data-table.svelte +138 -138
  76. package/src/charts/dashboard/panel-grid.svelte +30 -30
  77. package/src/charts/dashboard/period-filter.svelte +25 -25
  78. package/src/charts/dashboard/rating-chip.svelte +19 -19
  79. package/src/charts/dashboard/stat-band.svelte +32 -32
  80. package/src/charts/dashboard/stat-tile.svelte +106 -99
  81. package/src/charts/donut-chart.svelte +1 -1
  82. package/src/charts/edge-fade.svelte +1 -1
  83. package/src/charts/figure.svelte +1 -1
  84. package/src/charts/gauge-chart.css +4 -7
  85. package/src/charts/gauge-chart.svelte +1 -1
  86. package/src/charts/hover-focus.svelte +1 -1
  87. package/src/charts/legend.css +1 -3
  88. package/src/charts/legend.svelte +1 -1
  89. package/src/charts/line-chart.svelte +1 -1
  90. package/src/charts/number-chart.css +6 -9
  91. package/src/charts/number-chart.svelte +1 -1
  92. package/src/charts/reference-line.svelte +19 -19
  93. package/src/charts/reveal.svelte +1 -1
  94. package/src/charts/ring-chart.css +2 -4
  95. package/src/charts/ring-chart.svelte +1 -1
  96. package/src/charts/sparkline.svelte +5 -2
  97. package/src/charts/stat-card.svelte +1 -1
  98. package/src/charts/time-series-chart.css +2 -5
  99. package/src/charts/trend-pill.css +2 -4
  100. package/src/charts/trend-pill.svelte +1 -1
  101. package/src/components/copy/copy-button.svelte +93 -98
  102. package/src/components/copy/copy-button.test.ts +10 -2
  103. package/src/components/data-table/infinite-scroll.svelte +42 -42
  104. package/src/components/data-table/pagination.svelte +34 -22
  105. package/src/components/data-table/row-arrow.svelte +19 -16
  106. package/src/components/data-table/row-context-menu.svelte +52 -52
  107. package/src/components/data-table/sortable-head.svelte +25 -25
  108. package/src/components/data-table/table-skeleton.svelte +41 -36
  109. package/src/components/docs/doc-page.svelte +18 -9
  110. package/src/components/docs/on-this-page.svelte +16 -3
  111. package/src/components/docs/scroll-spy.svelte.ts +2 -3
  112. package/src/components/field-row/field-row.svelte +10 -8
  113. package/src/components/hold-to-confirm/hold-to-confirm.svelte +5 -1
  114. package/src/components/icon/icon.svelte +14 -14
  115. package/src/components/language-flag/language-flag.svelte +3 -1
  116. package/src/components/list-page/detail-empty.svelte +34 -34
  117. package/src/components/list-page/detail-header.svelte +55 -47
  118. package/src/components/list-page/detail-panel.svelte +51 -59
  119. package/src/components/list-page/detail-row.svelte +19 -19
  120. package/src/components/list-page/detail-stack.svelte +21 -21
  121. package/src/components/list-page/empty-state.svelte +30 -30
  122. package/src/components/list-page/filter-item.svelte +20 -20
  123. package/src/components/list-page/filter-popover.svelte +60 -55
  124. package/src/components/list-page/grouped-list.svelte +4 -2
  125. package/src/components/list-page/index.ts +5 -1
  126. package/src/components/list-page/list-page-shell.svelte +2 -5
  127. package/src/components/list-page/master-detail.svelte +87 -88
  128. package/src/components/list-page/notice-bar.svelte +38 -38
  129. package/src/components/list-page/prev-next.svelte +63 -63
  130. package/src/components/list-page/rail-body.svelte +18 -18
  131. package/src/components/list-page/rail-empty.svelte +23 -30
  132. package/src/components/list-page/rail-head.svelte +24 -24
  133. package/src/components/list-page/rail-row.svelte +77 -73
  134. package/src/components/list-page/section-host.ts +2 -3
  135. package/src/components/locale-switch/locale-switch.svelte +41 -41
  136. package/src/components/mark/mark.svelte +34 -38
  137. package/src/components/open-in/index.ts +1 -5
  138. package/src/components/open-in/open-in.svelte +40 -40
  139. package/src/components/picker/mark-picker.svelte +49 -49
  140. package/src/components/picker/picker.svelte +5 -3
  141. package/src/components/section-header/section-header.test.ts +7 -1
  142. package/src/components/section-header/section-label.svelte +20 -20
  143. package/src/components/segmented/animated-tabs.svelte +31 -2
  144. package/src/components/segmented/collapse.test.ts +2 -1
  145. package/src/components/segmented/index.ts +2 -0
  146. package/src/components/segmented/segment.svelte +52 -49
  147. package/src/components/segmented/segmented-filter.svelte +79 -80
  148. package/src/components/segmented/segmented-nav.svelte +34 -40
  149. package/src/components/segmented/segmented-overflow.svelte +53 -50
  150. package/src/components/segmented/segmented-track.svelte +62 -46
  151. package/src/components/segmented/segmented.ts +2 -3
  152. package/src/components/star-rating-input/star-rating-input.svelte +103 -103
  153. package/src/components/svg/svg.svelte +4 -4
  154. package/src/components/table-view/table-view.svelte +17 -2
  155. package/src/components/table-view/table-view.test.ts +7 -1
  156. package/src/components/table-view/types.ts +11 -11
  157. package/src/components/tags-input/index.ts +13 -0
  158. package/src/components/tags-input/tags-input.svelte +305 -0
  159. package/src/components/tags-input/tags.test.ts +64 -0
  160. package/src/components/tags-input/tags.ts +142 -0
  161. package/src/components/theme/index.ts +2 -4
  162. package/src/components/theme/theme-provider.svelte +1 -3
  163. package/src/components/toolbar/toolbar-search.svelte +25 -25
  164. package/src/components/toolbar/toolbar.svelte +11 -7
  165. package/src/components/toolbar/toolbar.test.ts +10 -1
  166. package/src/design/index.css +21 -78
  167. package/src/design/polish.css +26 -35
  168. package/src/editor/editor-toolbar.svelte +104 -99
  169. package/src/editor/editor.svelte +296 -266
  170. package/src/editor/image-toolbar.svelte +20 -22
  171. package/src/editor/index.ts +1 -4
  172. package/src/editor/inline-editor.svelte +280 -238
  173. package/src/editor/markdown.ts +15 -15
  174. package/src/editor/renderer.svelte +71 -71
  175. package/src/editor/slash-menu.svelte +24 -27
  176. package/src/effects/animated-beam/diagrams/BeamSvg.svelte +1 -1
  177. package/src/effects/image-comparison/ImageComparison.svelte +11 -1
  178. package/src/effects/interactive-hover-button/InteractiveHoverButton.svelte +59 -46
  179. package/src/effects/magic-card/MagicCard.css +14 -7
  180. package/src/effects/marquee/Marquee.svelte +3 -5
  181. package/src/effects/reveal/observe.ts +0 -3
  182. package/src/effects/spotlight/Spotlight.svelte +1 -1
  183. package/src/effects/theme-toggle/ThemeToggle.css +25 -22
  184. package/src/effects/theme-toggle/ThemeToggle.svelte +6 -7
  185. package/src/gallery.test.ts +14 -4
  186. package/src/inbox/README.md +1 -3
  187. package/src/inbox/inbox.test.ts +181 -138
  188. package/src/inbox/index.ts +20 -20
  189. package/src/inbox/logic.ts +114 -111
  190. package/src/inbox/thread-inbox.svelte +227 -151
  191. package/src/inbox/types.ts +130 -108
  192. package/src/lib/theme-transition.ts +2 -4
  193. package/src/lib/tone.ts +2 -4
  194. package/src/lib/utils.ts +2 -2
  195. package/src/modules.test.ts +79 -69
  196. package/src/onboarding/define-tours.test.ts +55 -40
  197. package/src/onboarding/define-tours.ts +23 -23
  198. package/src/onboarding/demo.svelte +24 -5
  199. package/src/onboarding/index.ts +1 -8
  200. package/src/onboarding/onboarding-provider.svelte +19 -7
  201. package/src/onboarding/onboarding.svelte.ts +16 -16
  202. package/src/onboarding/pick.test.ts +61 -52
  203. package/src/onboarding/pick.ts +16 -13
  204. package/src/onboarding/stores.test.ts +80 -80
  205. package/src/onboarding/stores.ts +41 -39
  206. package/src/onboarding/tour-card.svelte +30 -5
  207. package/src/onboarding/tour-runner.svelte +12 -3
  208. package/src/onboarding/types.ts +20 -12
  209. package/src/onboarding/wait.ts +22 -22
  210. package/src/panel/side-panel.svelte +94 -93
  211. package/src/record/demo-data.ts +291 -225
  212. package/src/record/field-section.svelte +38 -34
  213. package/src/record/fields.ts +148 -96
  214. package/src/record/format.ts +63 -56
  215. package/src/record/index.ts +49 -35
  216. package/src/record/info-list.svelte +145 -114
  217. package/src/record/kinds.ts +31 -31
  218. package/src/record/logic.ts +216 -214
  219. package/src/record/next-up.svelte +45 -34
  220. package/src/record/project-list.svelte +56 -50
  221. package/src/record/record-demo.svelte +130 -110
  222. package/src/record/record-header.svelte +37 -28
  223. package/src/record/record-shell.svelte +54 -54
  224. package/src/record/record-stats.svelte +25 -25
  225. package/src/record/record-timeline.svelte +134 -114
  226. package/src/record/record.test.ts +430 -309
  227. package/src/record/steps-rail.svelte +225 -216
  228. package/src/record/types.ts +234 -215
  229. package/src/record/where-we-are.svelte +62 -58
  230. package/src/shadcn/button/button.svelte +1 -1
  231. package/src/shadcn/card/card-title.svelte +1 -5
  232. package/src/shadcn/hooks/use-highlighted-code.svelte.ts +2 -13
  233. package/src/shadcn/sheet/sheet-content.svelte +2 -4
  234. package/src/shadcn/table/table-cell.svelte +2 -3
  235. package/src/shadcn/utilities.css +3 -8
  236. package/src/slides/Controls.svelte +150 -124
  237. package/src/slides/Deck.svelte +243 -238
  238. package/src/slides/Fragment.svelte +64 -57
  239. package/src/slides/Overview.svelte +106 -101
  240. package/src/slides/Presenter.svelte +300 -266
  241. package/src/slides/Slide.svelte +185 -170
  242. package/src/slides/SlideList.svelte +16 -12
  243. package/src/slides/ambient.d.ts +1 -1
  244. package/src/slides/bridges.ts +42 -23
  245. package/src/slides/deck-controller.svelte.ts +47 -35
  246. package/src/slides/demo-slides.svelte +330 -306
  247. package/src/slides/demo.svelte +164 -160
  248. package/src/slides/export/capture.ts +20 -16
  249. package/src/slides/export/ffmpeg.ts +43 -38
  250. package/src/slides/export/index.ts +5 -5
  251. package/src/slides/export/launch.ts +3 -1
  252. package/src/slides/export/pdf.ts +30 -27
  253. package/src/slides/export/types.ts +10 -8
  254. package/src/slides/index.ts +11 -11
  255. package/src/slides/labels.ts +22 -19
  256. package/src/slides/layout.ts +9 -2
  257. package/src/slides/model.ts +4 -4
  258. package/src/slides/navigation.ts +43 -31
  259. package/src/slides/theme.css +5 -7
  260. package/src/slides/types.ts +9 -10
  261. package/src/status/define-statuses.ts +17 -24
  262. package/src/status/index.ts +5 -5
  263. package/src/status/status-badge.svelte +3 -1
  264. package/src/status/status-dot.svelte +1 -3
  265. package/src/status/status-icon.svelte +1 -1
  266. package/src/status/status-types.ts +21 -21
  267. package/src/tasks/board-order.ts +2 -4
  268. package/src/tasks/demo.svelte +189 -31
  269. package/src/tasks/detail/detail-demo.svelte +73 -13
  270. package/src/tasks/detail/detail-types.ts +45 -45
  271. package/src/tasks/detail/task-detail.svelte +40 -10
  272. package/src/tasks/detail/task-property.svelte +8 -2
  273. package/src/tasks/detail/task-thread.svelte +53 -12
  274. package/src/tasks/gantt/gantt-bar.svelte +17 -6
  275. package/src/tasks/gantt/gantt-grid.svelte +40 -9
  276. package/src/tasks/gantt/gantt-group-row.svelte +8 -2
  277. package/src/tasks/gantt/gantt-header.svelte +9 -3
  278. package/src/tasks/gantt/gantt-task-row.svelte +26 -6
  279. package/src/tasks/gantt/gantt.css +2 -1
  280. package/src/tasks/gantt/gantt.test.ts +3 -1
  281. package/src/tasks/gantt/logic.test.ts +110 -20
  282. package/src/tasks/gantt/logic.ts +42 -6
  283. package/src/tasks/inbox/inbox-card.svelte +13 -5
  284. package/src/tasks/inbox/inbox-draft.svelte +1 -1
  285. package/src/tasks/inbox/inbox-logic.ts +6 -40
  286. package/src/tasks/inbox/inbox-statuses.ts +23 -23
  287. package/src/tasks/inbox/inbox-trace.svelte +9 -3
  288. package/src/tasks/inbox/inbox-types.ts +54 -58
  289. package/src/tasks/inbox/index.ts +22 -21
  290. package/src/tasks/index.ts +49 -52
  291. package/src/tasks/kanban-board.svelte +16 -12
  292. package/src/tasks/kanban-types.ts +2 -3
  293. package/src/tasks/presets/sales.ts +6 -6
  294. package/src/tasks/task-gantt.svelte +28 -8
  295. package/src/tasks/task-list.svelte +41 -17
  296. package/src/tasks/time/duration.ts +40 -40
  297. package/src/tasks/time/time-chip.svelte +9 -2
  298. package/src/tasks/time/time-dot.svelte +1 -1
  299. package/src/tasks/time/time-ring.svelte +11 -3
  300. package/src/tasks/time/time-tracker.svelte +57 -15
  301. package/src/tasks/time/time-types.ts +57 -57
  302. package/src/tasks/use-kanban-dnd.svelte.ts +2 -3
  303. package/src/thread/index.ts +7 -7
  304. package/src/thread/logic.ts +36 -33
  305. package/src/thread/thread.svelte +82 -16
  306. package/src/thread/thread.test.ts +58 -47
  307. package/src/thread/types.ts +68 -68
  308. package/src/tokens/craft.css +17 -53
  309. package/src/tokens/index.css +3 -13
  310. package/src/tokens/palette.css +13 -11
  311. package/src/tokens/primitives.css +10 -23
  312. package/src/tasks/inbox/inbox.svelte +0 -203
@@ -1,32 +1,32 @@
1
1
  <script lang="ts">
2
- // =========================
3
- // IMPORTS
4
- // =========================
5
- import type { Snippet } from "svelte";
6
- import { cn } from "../../lib/utils.js";
7
- import type { PhosphorIcon } from "../../lib/icon-types.js";
2
+ // =========================
3
+ // IMPORTS
4
+ // =========================
5
+ import type { Snippet } from "svelte";
6
+ import { cn } from "../../lib/utils.js";
7
+ import type { PhosphorIcon } from "../../lib/icon-types.js";
8
8
 
9
- // =========================
10
- // TYPES
11
- // =========================
12
- interface Props {
13
- // Not list-page's `EmptyState`: that one takes exactly one action, and a
14
- // dashboard can be empty for reasons (nothing connected, nothing synced)
15
- // that need two buttons.
9
+ // =========================
10
+ // TYPES
11
+ // =========================
12
+ interface Props {
13
+ // Not list-page's `EmptyState`: that one takes exactly one action, and a
14
+ // dashboard can be empty for reasons (nothing connected, nothing synced)
15
+ // that need two buttons.
16
16
 
17
- // The dashed edge marks truly empty, not loading.
18
- icon?: PhosphorIcon;
19
- title: string;
20
- description?: string;
21
- /** One or two buttons. Omit where there is nothing the reader can do. */
22
- actions?: Snippet;
23
- class?: string;
24
- };
17
+ // The dashed edge marks truly empty, not loading.
18
+ icon?: PhosphorIcon;
19
+ title: string;
20
+ description?: string;
21
+ /** One or two buttons. Omit where there is nothing the reader can do. */
22
+ actions?: Snippet;
23
+ class?: string;
24
+ }
25
25
 
26
- // =========================
27
- // PROPS
28
- // =========================
29
- let { icon: Icon, title, description, actions, class: className }: Props = $props();
26
+ // =========================
27
+ // PROPS
28
+ // =========================
29
+ let { icon: Icon, title, description, actions, class: className }: Props = $props();
30
30
  </script>
31
31
 
32
32
  <!-- =========================== -->
@@ -1,30 +1,30 @@
1
1
  <script lang="ts">
2
- // =========================
3
- // IMPORTS
4
- // =========================
5
- import { ArrowRight } from "phosphor-svelte";
6
- import type { Snippet } from "svelte";
7
- import { cn } from "../../lib/utils.js";
2
+ // =========================
3
+ // IMPORTS
4
+ // =========================
5
+ import { ArrowRight } from "phosphor-svelte";
6
+ import type { Snippet } from "svelte";
7
+ import { cn } from "../../lib/utils.js";
8
8
 
9
- // =========================
10
- // TYPES
11
- // =========================
12
- interface Props {
13
- title: string;
14
- /** One line on the right: a window, a provenance, a count. Never a sentence. */
15
- note?: string;
16
- /** The page that owns this area, when one page does. */
17
- href?: string;
18
- /** The link's wording. Translated at the call site. */
19
- openLabel?: string;
20
- children: Snippet;
21
- class?: string;
22
- };
9
+ // =========================
10
+ // TYPES
11
+ // =========================
12
+ interface Props {
13
+ title: string;
14
+ /** One line on the right: a window, a provenance, a count. Never a sentence. */
15
+ note?: string;
16
+ /** The page that owns this area, when one page does. */
17
+ href?: string;
18
+ /** The link's wording. Translated at the call site. */
19
+ openLabel?: string;
20
+ children: Snippet;
21
+ class?: string;
22
+ }
23
23
 
24
- // =========================
25
- // PROPS
26
- // =========================
27
- let { title, note, href, openLabel = "Open", children, class: className }: Props = $props();
24
+ // =========================
25
+ // PROPS
26
+ // =========================
27
+ let { title, note, href, openLabel = "Open", children, class: className }: Props = $props();
28
28
  </script>
29
29
 
30
30
  <!-- =========================== -->
@@ -1,56 +1,56 @@
1
1
  <script lang="ts">
2
- // =========================
3
- // IMPORTS
4
- // =========================
5
- import type { Snippet } from "svelte";
6
- import { cn } from "../../lib/utils.js";
7
- import { panelHeaderClass, panelShellClass } from "../../lib/panel.js";
8
- import { SectionLabel } from "../../components/section-header/index.js";
9
- import type { PhosphorIcon } from "../../lib/icon-types.js";
2
+ // =========================
3
+ // IMPORTS
4
+ // =========================
5
+ import type { Snippet } from "svelte";
6
+ import { cn } from "../../lib/utils.js";
7
+ import { panelHeaderClass, panelShellClass } from "../../lib/panel.js";
8
+ import { SectionLabel } from "../../components/section-header/index.js";
9
+ import type { PhosphorIcon } from "../../lib/icon-types.js";
10
10
 
11
- // =========================
12
- // TYPES
13
- // =========================
14
- interface Props {
15
- /** What this section of the dashboard is. Drawn as the app's one panel label. */
16
- label: string;
17
- labelIcon?: PhosphorIcon;
18
- /**
19
- * A row count, total or freshness stamp. It's a prop, not an action,
20
- * because hand-placed hints ended up at three type sizes on one screen.
21
- */
22
- hint?: string;
23
- /** Controls that act on this panel. Pinned right, after the hint. */
24
- actions?: Snippet;
25
- /** A line under the label, for the scope a number was measured over. */
26
- sublabel?: string;
27
- /** Off for a body that supplies its own padding: a table, a full-bleed list. */
28
- pad?: boolean;
29
- /**
30
- * The body scrolls sideways rather than squeezing. For a wide table, which is
31
- * every table on a dashboard once the window is narrower than its columns.
32
- */
33
- scroll?: boolean;
34
- children: Snippet;
35
- class?: string;
36
- bodyClass?: string;
37
- };
11
+ // =========================
12
+ // TYPES
13
+ // =========================
14
+ interface Props {
15
+ /** What this section of the dashboard is. Drawn as the app's one panel label. */
16
+ label: string;
17
+ labelIcon?: PhosphorIcon;
18
+ /**
19
+ * A row count, total or freshness stamp. It's a prop, not an action,
20
+ * because hand-placed hints ended up at three type sizes on one screen.
21
+ */
22
+ hint?: string;
23
+ /** Controls that act on this panel. Pinned right, after the hint. */
24
+ actions?: Snippet;
25
+ /** A line under the label, for the scope a number was measured over. */
26
+ sublabel?: string;
27
+ /** Off for a body that supplies its own padding: a table, a full-bleed list. */
28
+ pad?: boolean;
29
+ /**
30
+ * The body scrolls sideways rather than squeezing. For a wide table, which is
31
+ * every table on a dashboard once the window is narrower than its columns.
32
+ */
33
+ scroll?: boolean;
34
+ children: Snippet;
35
+ class?: string;
36
+ bodyClass?: string;
37
+ }
38
38
 
39
- // =========================
40
- // PROPS
41
- // =========================
42
- let {
43
- label,
44
- labelIcon,
45
- hint,
46
- actions,
47
- sublabel,
48
- pad = true,
49
- scroll = false,
50
- children,
51
- class: className,
52
- bodyClass,
53
- }: Props = $props();
39
+ // =========================
40
+ // PROPS
41
+ // =========================
42
+ let {
43
+ label,
44
+ labelIcon,
45
+ hint,
46
+ actions,
47
+ sublabel,
48
+ pad = true,
49
+ scroll = false,
50
+ children,
51
+ class: className,
52
+ bodyClass,
53
+ }: Props = $props();
54
54
  </script>
55
55
 
56
56
  <!-- =========================== -->
@@ -1,68 +1,68 @@
1
1
  <script lang="ts">
2
- /*
3
- * A column head that sorts the table under it. Sort buttons above a table
4
- * never say which column they rank, and read as filter tabs.
5
- */
2
+ /*
3
+ * A column head that sorts the table under it. Sort buttons above a table
4
+ * never say which column they rank, and read as filter tabs.
5
+ */
6
6
 
7
- // Every sortable head shows its caret, at 40%, solid on the active column.
8
- // Hover-only makes people hunt, and it shifts the label under the pointer.
7
+ // Every sortable head shows its caret, at 40%, solid on the active column.
8
+ // Hover-only makes people hunt, and it shifts the label under the pointer.
9
9
 
10
- // No padding of its own: it comes from the skin classes, so heads and cells
11
- // stay in line when a dense table tightens both.
10
+ // No padding of its own: it comes from the skin classes, so heads and cells
11
+ // stay in line when a dense table tightens both.
12
12
 
13
- // =========================
14
- // IMPORTS
15
- // =========================
16
- import type { Snippet } from "svelte";
17
- import { CaretDown, CaretUp } from "phosphor-svelte";
18
- import { cn } from "../../lib/utils.js";
19
- import { dataThClass, dataThNumClass } from "./data-table.js";
13
+ // =========================
14
+ // IMPORTS
15
+ // =========================
16
+ import type { Snippet } from "svelte";
17
+ import { CaretDown, CaretUp } from "phosphor-svelte";
18
+ import { cn } from "../../lib/utils.js";
19
+ import { dataThClass, dataThNumClass } from "./data-table.js";
20
20
 
21
- // =========================
22
- // TYPES
23
- // =========================
24
- interface Props {
25
- /** True when the table is currently ranked by this column. */
26
- active: boolean;
27
- /** Which way the active column runs. Ignored while inactive. */
28
- descending: boolean;
29
- /** Called on click and on Enter/Space. Toggling direction is the caller's. */
30
- onSort: () => void;
31
- /** Numbers right-align to their digits, so their caret leads the label. */
32
- numeric?: boolean;
33
- /** The long form of an abbreviated head, as the browser tooltip. */
34
- title?: string;
35
- class?: string;
36
- children: Snippet;
37
- };
21
+ // =========================
22
+ // TYPES
23
+ // =========================
24
+ interface Props {
25
+ /** True when the table is currently ranked by this column. */
26
+ active: boolean;
27
+ /** Which way the active column runs. Ignored while inactive. */
28
+ descending: boolean;
29
+ /** Called on click and on Enter/Space. Toggling direction is the caller's. */
30
+ onSort: () => void;
31
+ /** Numbers right-align to their digits, so their caret leads the label. */
32
+ numeric?: boolean;
33
+ /** The long form of an abbreviated head, as the browser tooltip. */
34
+ title?: string;
35
+ class?: string;
36
+ children: Snippet;
37
+ }
38
38
 
39
- // =========================
40
- // PROPS
41
- // =========================
42
- let {
43
- active,
44
- descending,
45
- onSort,
46
- numeric = false,
47
- title,
48
- class: className,
49
- children,
50
- }: Props = $props();
39
+ // =========================
40
+ // PROPS
41
+ // =========================
42
+ let {
43
+ active,
44
+ descending,
45
+ onSort,
46
+ numeric = false,
47
+ title,
48
+ class: className,
49
+ children,
50
+ }: Props = $props();
51
51
 
52
- // =========================
53
- // DERIVED
54
- // =========================
55
- const ariaSort = $derived(active ? (descending ? "descending" : "ascending") : "none");
52
+ // =========================
53
+ // DERIVED
54
+ // =========================
55
+ const ariaSort = $derived(active ? (descending ? "descending" : "ascending") : "none");
56
56
 
57
- // =========================
58
- // FUNCTIONS
59
- // =========================
60
- function handleKeydown(event: KeyboardEvent) {
61
- if (event.key === "Enter" || event.key === " ") {
62
- event.preventDefault();
63
- onSort();
57
+ // =========================
58
+ // FUNCTIONS
59
+ // =========================
60
+ function handleKeydown(event: KeyboardEvent) {
61
+ if (event.key === "Enter" || event.key === " ") {
62
+ event.preventDefault();
63
+ onSort();
64
+ }
64
65
  }
65
- }
66
66
  </script>
67
67
 
68
68
  <!-- =========================== -->
@@ -1,159 +1,159 @@
1
1
  <script lang="ts" module>
2
- import type { Snippet } from "svelte";
2
+ import type { Snippet } from "svelte";
3
3
 
4
- /** Narrow screens drop the columns that only add detail. */
5
- export type DataColumnBreakpoint = "sm" | "md" | "lg";
4
+ /** Narrow screens drop the columns that only add detail. */
5
+ export type DataColumnBreakpoint = "sm" | "md" | "lg";
6
6
 
7
- /** One column after the lead. Give it `value`, `rating` or a `cell` snippet. */
8
- export type DataColumn<T> = {
9
- key: string;
10
- label: string;
11
- /** The long form of the head, as its tooltip. */
12
- hint?: string;
13
- /** A Tailwind width class. Fixed widths are what keep the lead column wide. */
14
- width: string;
15
- /** Figures run to the end, so digits line up. Text, pills and bars start. */
16
- align?: "start" | "end";
17
- /** Makes the head rank the table. Higher is better; a new column starts descending. */
18
- sort?: (row: T) => number;
19
- /** Null draws the table's empty label, muted. */
20
- value?: (row: T) => string | null;
21
- rating?: (row: T) => { grade: string; hint?: string } | null;
22
- cell?: Snippet<[T]>;
23
- /** 0 to 1, a share-of-max track drawn before the value. */
24
- bar?: (row: T) => number;
25
- /** Cell tooltip. */
26
- title?: (row: T) => string | undefined;
27
- muted?: boolean | ((row: T) => boolean);
28
- /** A loss, an overrun: the value turns the destructive colour. */
29
- negative?: (row: T) => boolean;
30
- /** The column the eye lands on first. */
31
- strong?: boolean;
32
- /** A hairline before this column, opening a group. */
33
- rule?: boolean;
34
- /** Hidden under this breakpoint. */
35
- hideBelow?: DataColumnBreakpoint;
36
- };
7
+ /** One column after the lead. Give it `value`, `rating` or a `cell` snippet. */
8
+ export type DataColumn<T> = {
9
+ key: string;
10
+ label: string;
11
+ /** The long form of the head, as its tooltip. */
12
+ hint?: string;
13
+ /** A Tailwind width class. Fixed widths are what keep the lead column wide. */
14
+ width: string;
15
+ /** Figures run to the end, so digits line up. Text, pills and bars start. */
16
+ align?: "start" | "end";
17
+ /** Makes the head rank the table. Higher is better; a new column starts descending. */
18
+ sort?: (row: T) => number;
19
+ /** Null draws the table's empty label, muted. */
20
+ value?: (row: T) => string | null;
21
+ rating?: (row: T) => { grade: string; hint?: string } | null;
22
+ cell?: Snippet<[T]>;
23
+ /** 0 to 1, a share-of-max track drawn before the value. */
24
+ bar?: (row: T) => number;
25
+ /** Cell tooltip. */
26
+ title?: (row: T) => string | undefined;
27
+ muted?: boolean | ((row: T) => boolean);
28
+ /** A loss, an overrun: the value turns the destructive colour. */
29
+ negative?: (row: T) => boolean;
30
+ /** The column the eye lands on first. */
31
+ strong?: boolean;
32
+ /** A hairline before this column, opening a group. */
33
+ rule?: boolean;
34
+ /** Hidden under this breakpoint. */
35
+ hideBelow?: DataColumnBreakpoint;
36
+ };
37
37
  </script>
38
38
 
39
39
  <script lang="ts" generics="T">
40
- /*
41
- * The one data table: wide lead cell, fixed figure columns, sortable heads,
42
- * optional pager. A row opens by `rowHref` or `onRowClick`, stretched over the row from the lead.
43
- */
44
- import { cn } from "../../lib/utils.js";
45
- import DataSortHead from "./data-sort-head.svelte";
46
- import RatingChip from "./rating-chip.svelte";
47
- import { Pagination, paginateRows, totalPages } from "../../components/data-table/index.js";
48
- import {
49
- dataTableClass,
50
- dataTdClass,
51
- dataTdNumClass,
52
- dataThClass,
53
- dataThNumClass,
54
- dataTheadClass,
55
- dataTrClass,
56
- } from "./data-table.js";
40
+ /*
41
+ * The one data table: wide lead cell, fixed figure columns, sortable heads,
42
+ * optional pager. A row opens by `rowHref` or `onRowClick`, stretched over the row from the lead.
43
+ */
44
+ import { cn } from "../../lib/utils.js";
45
+ import DataSortHead from "./data-sort-head.svelte";
46
+ import RatingChip from "./rating-chip.svelte";
47
+ import { Pagination, paginateRows, totalPages } from "../../components/data-table/index.js";
48
+ import {
49
+ dataTableClass,
50
+ dataTdClass,
51
+ dataTdNumClass,
52
+ dataThClass,
53
+ dataThNumClass,
54
+ dataTheadClass,
55
+ dataTrClass,
56
+ } from "./data-table.js";
57
57
 
58
- interface Props {
59
- rows: T[];
60
- columns: DataColumn<T>[];
61
- rowKey: (row: T) => string;
62
- leadLabel: string;
63
- lead: Snippet<[T]>;
64
- /** Drawn where a value or rating is null. */
65
- emptyLabel: string;
66
- /** A Tailwind width class. Omit and the lead takes whatever the figures leave. */
67
- leadWidth?: string;
68
- /** A Tailwind min-width class, past which the panel scrolls sideways. */
69
- minWidth?: string;
70
- /** Omit for every row on one page. */
71
- pageSize?: number;
72
- sortKey?: string | null;
73
- descending?: boolean;
74
- page?: number;
75
- /** The row opens a page. The lead becomes the link. */
76
- rowHref?: (row: T) => string;
77
- /** The row opens in place: a panel, a detail view. The lead becomes the button. */
78
- onRowClick?: (row: T) => void;
79
- /** Placeholder rows while the data is on its way. */
80
- loading?: boolean;
81
- /** Drawn in place of the body when there are no rows. */
82
- empty?: Snippet;
83
- };
58
+ interface Props {
59
+ rows: T[];
60
+ columns: DataColumn<T>[];
61
+ rowKey: (row: T) => string;
62
+ leadLabel: string;
63
+ lead: Snippet<[T]>;
64
+ /** Drawn where a value or rating is null. */
65
+ emptyLabel: string;
66
+ /** A Tailwind width class. Omit and the lead takes whatever the figures leave. */
67
+ leadWidth?: string;
68
+ /** A Tailwind min-width class, past which the panel scrolls sideways. */
69
+ minWidth?: string;
70
+ /** Omit for every row on one page. */
71
+ pageSize?: number;
72
+ sortKey?: string | null;
73
+ descending?: boolean;
74
+ page?: number;
75
+ /** The row opens a page. The lead becomes the link. */
76
+ rowHref?: (row: T) => string;
77
+ /** The row opens in place: a panel, a detail view. The lead becomes the button. */
78
+ onRowClick?: (row: T) => void;
79
+ /** Placeholder rows while the data is on its way. */
80
+ loading?: boolean;
81
+ /** Drawn in place of the body when there are no rows. */
82
+ empty?: Snippet;
83
+ }
84
84
 
85
- let {
86
- rows,
87
- columns,
88
- rowKey,
89
- leadLabel,
90
- lead,
91
- emptyLabel,
92
- leadWidth,
93
- minWidth = "min-w-[48rem]",
94
- pageSize,
95
- sortKey = $bindable(null),
96
- descending = $bindable(true),
97
- page = $bindable(1),
98
- rowHref,
99
- onRowClick,
100
- loading = false,
101
- empty,
102
- }: Props = $props();
85
+ let {
86
+ rows,
87
+ columns,
88
+ rowKey,
89
+ leadLabel,
90
+ lead,
91
+ emptyLabel,
92
+ leadWidth,
93
+ minWidth = "min-w-[48rem]",
94
+ pageSize,
95
+ sortKey = $bindable(null),
96
+ descending = $bindable(true),
97
+ page = $bindable(1),
98
+ rowHref,
99
+ onRowClick,
100
+ loading = false,
101
+ empty,
102
+ }: Props = $props();
103
103
 
104
- const pad = "px-3";
105
- const LOADING_ROWS = 5;
104
+ const pad = "px-3";
105
+ const LOADING_ROWS = 5;
106
106
 
107
- // Literal classes, so Tailwind sees each one.
108
- const HIDE_BELOW: Record<DataColumnBreakpoint, string> = {
109
- sm: "hidden sm:table-cell",
110
- md: "hidden md:table-cell",
111
- lg: "hidden lg:table-cell",
112
- };
107
+ // Literal classes, so Tailwind sees each one.
108
+ const HIDE_BELOW: Record<DataColumnBreakpoint, string> = {
109
+ sm: "hidden sm:table-cell",
110
+ md: "hidden md:table-cell",
111
+ lg: "hidden lg:table-cell",
112
+ };
113
113
 
114
- // The ::after covers the whole row, so any cell opens it and the lead stays the one tab stop.
115
- const stretch =
116
- "text-left outline-none after:absolute after:inset-0 after:content-[''] focus-visible:after:ring-2 focus-visible:after:ring-inset focus-visible:after:ring-ring";
114
+ // The ::after covers the whole row, so any cell opens it and the lead stays the one tab stop.
115
+ const stretch =
116
+ "text-left outline-none after:absolute after:inset-0 after:content-[''] focus-visible:after:ring-2 focus-visible:after:ring-inset focus-visible:after:ring-ring";
117
117
 
118
- const sorted = $derived.by(() => {
119
- const by = columns.find((column) => column.key === sortKey)?.sort;
120
- if (!by) return rows;
121
- const direction = descending ? -1 : 1;
122
- return [...rows].sort((left, right) => (by(left) - by(right)) * direction);
123
- });
118
+ const sorted = $derived.by(() => {
119
+ const by = columns.find((column) => column.key === sortKey)?.sort;
120
+ if (!by) return rows;
121
+ const direction = descending ? -1 : 1;
122
+ return [...rows].sort((left, right) => (by(left) - by(right)) * direction);
123
+ });
124
124
 
125
- const visible = $derived(pageSize ? paginateRows(sorted, page, pageSize) : sorted);
126
- const opens = $derived(Boolean(rowHref || onRowClick));
125
+ const visible = $derived(pageSize ? paginateRows(sorted, page, pageSize) : sorted);
126
+ const opens = $derived(Boolean(rowHref || onRowClick));
127
127
 
128
- // Fewer rows than the page you stand on would draw an empty table.
129
- $effect(() => {
130
- if (!pageSize) return;
131
- const last = totalPages(sorted.length, pageSize);
132
- if (page > last) page = last;
133
- });
128
+ // Fewer rows than the page you stand on would draw an empty table.
129
+ $effect(() => {
130
+ if (!pageSize) return;
131
+ const last = totalPages(sorted.length, pageSize);
132
+ if (page > last) page = last;
133
+ });
134
134
 
135
- function rankBy(key: string) {
136
- page = 1;
137
- if (sortKey === key) {
138
- descending = !descending;
139
- return;
135
+ function rankBy(key: string) {
136
+ page = 1;
137
+ if (sortKey === key) {
138
+ descending = !descending;
139
+ return;
140
+ }
141
+ sortKey = key;
142
+ descending = true;
140
143
  }
141
- sortKey = key;
142
- descending = true;
143
- }
144
144
 
145
- function isMuted(column: DataColumn<T>, row: T) {
146
- return typeof column.muted === "function" ? column.muted(row) : Boolean(column.muted);
147
- }
145
+ function isMuted(column: DataColumn<T>, row: T) {
146
+ return typeof column.muted === "function" ? column.muted(row) : Boolean(column.muted);
147
+ }
148
148
 
149
- function columnClass(column: DataColumn<T>) {
150
- return cn(
151
- pad,
152
- column.align === "start" && "text-left",
153
- column.rule && "border-l border-border",
154
- column.hideBelow && HIDE_BELOW[column.hideBelow],
155
- );
156
- }
149
+ function columnClass(column: DataColumn<T>) {
150
+ return cn(
151
+ pad,
152
+ column.align === "start" && "text-left",
153
+ column.rule && "border-l border-border",
154
+ column.hideBelow && HIDE_BELOW[column.hideBelow],
155
+ );
156
+ }
157
157
  </script>
158
158
 
159
159
  <table class={cn(dataTableClass, minWidth, "table-fixed")}>