@heyramzi/ui 2.7.0 → 2.9.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.
- package/COMPONENTS.md +1 -1
- package/README.md +1 -1
- package/package.json +58 -74
- package/src/agents/vendor/draw.ts +4 -4
- package/src/ai/generation-progress/GenerationProgress.svelte +2 -2
- package/src/animations/README.md +1 -1
- package/src/animations/card-tilt.css +1 -1
- package/src/animations/digit-pop-in.css +1 -1
- package/src/animations/input-clear-dissolve.css +2 -2
- package/src/animations/learn-more-hover.css +2 -2
- package/src/animations/like-button.css +1 -1
- package/src/animations/plus-menu-morph.css +1 -1
- package/src/animations/success-check.css +5 -5
- package/src/canvas/headless/canvas-cleanup.test.ts +1 -1
- package/src/canvas/ops/ops.ts +1 -1
- package/src/charts/area-chart.svelte +11 -1
- package/src/charts/bar-chart.css +2 -1
- package/src/charts/bar-chart.svelte +18 -3
- package/src/charts/charts.css +2 -1
- package/src/charts/line-chart.svelte +11 -1
- package/src/components/data-table/data-table.ts +4 -5
- package/src/components/data-table/pagination.svelte +5 -3
- package/src/components/docs/on-this-page.svelte +82 -5
- package/src/components/docs/scroll-spy.svelte.ts +6 -1
- package/src/components/list-page/detail-header.svelte +7 -3
- package/src/components/list-page/empty-state.svelte +4 -2
- package/src/components/list-page/filter-popover.svelte +2 -2
- package/src/components/list-page/rail-row.svelte +5 -2
- package/src/components/mark/mark.svelte +2 -1
- package/src/components/section-card/section-card.svelte +3 -1
- package/src/components/section-header/section-header.svelte +5 -5
- package/src/components/segmented/segment.svelte +1 -1
- package/src/components/table-view/table-view.svelte +6 -1
- package/src/components/tags-input/tags-input.svelte +1 -1
- package/src/components/toolbar/toolbar.svelte +1 -1
- package/src/design/index.css +7 -5
- package/src/editor/anchors.ts +28 -0
- package/src/editor/doc-blocks.ts +168 -0
- package/src/editor/doc-page.css +389 -0
- package/src/editor/editor.svelte +47 -5
- package/src/editor/extensions.ts +26 -2
- package/src/editor/markdown.ts +27 -2
- package/src/editor/slash-items.ts +18 -0
- package/src/effects/reveal/reveal.css +1 -1
- package/src/icons/trades.ts +1 -1
- package/src/inbox/README.md +6 -4
- package/src/inbox/inbox.test.ts +1 -1
- package/src/inbox/index.ts +2 -2
- package/src/inbox/logic.ts +29 -5
- package/src/inbox/thread-inbox.svelte +219 -92
- package/src/inbox/types.ts +27 -1
- package/src/lib/initials.test.ts +25 -0
- package/src/lib/initials.ts +29 -0
- package/src/lib/theme-transition.ts +6 -2
- package/src/modules.test.ts +4 -64
- package/src/panel/README.md +1 -1
- package/src/record/README.md +1 -1
- package/src/record/info-list.svelte +5 -4
- package/src/record/next-up.svelte +1 -1
- package/src/record/project-list.svelte +1 -1
- package/src/record/steps-rail.svelte +3 -3
- package/src/shadcn/badge/badge.svelte +1 -1
- package/src/shadcn/card/card-title.svelte +1 -5
- package/src/shadcn/empty/empty-description.svelte +1 -1
- package/src/shadcn/progress/progress.svelte +8 -1
- package/src/shadcn/table/table-cell.svelte +10 -2
- package/src/slides/theme.css +2 -2
- package/src/status/README.md +1 -1
- package/src/status/status-dot.svelte +3 -3
- package/src/thread/README.md +2 -0
- package/src/thread/index.ts +9 -1
- package/src/thread/logic.ts +33 -3
- package/src/thread/thread-body.svelte +37 -0
- package/src/thread/thread-events.svelte +30 -0
- package/src/thread/thread.svelte +74 -59
- package/src/thread/types.ts +21 -0
- package/src/tokens/craft.css +7 -5
- package/src/{tasks → work}/README.md +11 -8
- package/src/{tasks → work}/detail/task-thread.svelte +8 -15
- package/src/work/docs/doc-editor-page.svelte +194 -0
- package/src/work/docs/doc-menu.svelte +333 -0
- package/src/work/docs/docs-demo.svelte +227 -0
- package/src/work/docs/edited-chip.svelte +125 -0
- package/src/work/docs/edited.ts +54 -0
- package/src/work/docs/index.ts +38 -0
- package/src/work/docs/labels.ts +70 -0
- package/src/work/docs/menu.ts +127 -0
- package/src/work/docs/outline.ts +60 -0
- package/src/{tasks → work}/gantt/gantt-grid.svelte +2 -2
- package/src/{tasks → work}/gantt/gantt-group-row.svelte +1 -1
- package/src/{tasks → work}/gantt/gantt.css +1 -1
- package/src/{tasks → work}/inbox/inbox-card.svelte +113 -50
- package/src/work/inbox/inbox-logic.ts +98 -0
- package/src/work/inbox/inbox-trace.svelte +58 -0
- package/src/{tasks → work}/inbox/inbox-types.ts +2 -0
- package/src/{tasks → work}/index.ts +0 -10
- package/src/{tasks → work}/kanban-board.svelte +1 -1
- package/src/{tasks → work}/kanban-column.svelte +1 -1
- package/src/{tasks → work}/task-list.svelte +2 -2
- package/src/{tasks → work}/time/time-ring.svelte +1 -1
- package/src/{tasks → work}/time/time-tracker.svelte +3 -6
- package/src/tasks/inbox/inbox-logic.ts +0 -21
- package/src/tasks/inbox/inbox-trace.svelte +0 -51
- /package/src/{tasks → work}/ambient.d.ts +0 -0
- /package/src/{tasks → work}/board-order.ts +0 -0
- /package/src/{tasks → work}/dates.ts +0 -0
- /package/src/{tasks → work}/demo.svelte +0 -0
- /package/src/{tasks → work}/detail/detail-demo.svelte +0 -0
- /package/src/{tasks → work}/detail/detail-types.ts +0 -0
- /package/src/{tasks → work}/detail/task-chip.svelte +0 -0
- /package/src/{tasks → work}/detail/task-detail.svelte +0 -0
- /package/src/{tasks → work}/detail/task-property.svelte +0 -0
- /package/src/{tasks → work}/gantt/gantt-bar.svelte +0 -0
- /package/src/{tasks → work}/gantt/gantt-header.svelte +0 -0
- /package/src/{tasks → work}/gantt/gantt-task-row.svelte +0 -0
- /package/src/{tasks → work}/gantt/gantt.test.ts +0 -0
- /package/src/{tasks → work}/gantt/logic.test.ts +0 -0
- /package/src/{tasks → work}/gantt/logic.ts +0 -0
- /package/src/{tasks → work}/inbox/inbox-draft.svelte +0 -0
- /package/src/{tasks → work}/inbox/inbox-statuses.ts +0 -0
- /package/src/{tasks → work}/inbox/index.ts +0 -0
- /package/src/{tasks → work}/kanban-drag.ts +0 -0
- /package/src/{tasks → work}/kanban-types.ts +0 -0
- /package/src/{tasks → work}/presets/sales.ts +0 -0
- /package/src/{tasks → work}/reorder.ts +0 -0
- /package/src/{tasks → work}/task-gantt.svelte +0 -0
- /package/src/{tasks → work}/task-types.ts +0 -0
- /package/src/{tasks → work}/time/duration.ts +0 -0
- /package/src/{tasks → work}/time/time-chip.svelte +0 -0
- /package/src/{tasks → work}/time/time-dot.svelte +0 -0
- /package/src/{tasks → work}/time/time-types.ts +0 -0
- /package/src/{tasks → work}/use-kanban-dnd.svelte.ts +0 -0
package/COMPONENTS.md
CHANGED
|
@@ -139,6 +139,6 @@ Generic pieces built on the primitives, imported from `@heyramzi/ui/components/<
|
|
|
139
139
|
|
|
140
140
|
---
|
|
141
141
|
|
|
142
|
-
Skipped on purpose: brand assets (logos, marks, brand renderer), kanban, list, gantt and status (they live in `@heyramzi/ui/
|
|
142
|
+
Skipped on purpose: brand assets (logos, marks, brand renderer), kanban, list, gantt and status (they live in `@heyramzi/ui/work`), and charts
|
|
143
143
|
(they live in `@heyramzi/ui/charts`). The bot avatars
|
|
144
144
|
are in `src/agents`, imported from `@heyramzi/ui/agents`.
|
package/README.md
CHANGED
|
@@ -65,7 +65,7 @@ One package, six larger parts, each imported from its own subpath:
|
|
|
65
65
|
|
|
66
66
|
| Subpath | What it is |
|
|
67
67
|
| --- | --- |
|
|
68
|
-
| `@heyramzi/ui/
|
|
68
|
+
| `@heyramzi/ui/work` | Board, list, gantt, status badges, task detail, record, time tracker, inbox |
|
|
69
69
|
| `@heyramzi/ui/charts` | LayerChart charts, sparklines, stat cards, and `/charts/dashboard` |
|
|
70
70
|
| `@heyramzi/ui/slides` | Decks on a fixed canvas, plus `/slides/model` and `/slides/export` |
|
|
71
71
|
| `@heyramzi/ui/editor` | The Tiptap editor, its renderer, `/editor/markdown`, `/editor/block-ops` |
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heyramzi/ui",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "Svelte 5 UI for apps: primitives, effects and animations, plus tasks, charts, slides, an editor, onboarding and a canvas as subpaths.",
|
|
3
|
+
"version": "2.9.0",
|
|
4
|
+
"description": "Svelte 5 UI for apps: primitives, effects and animations, plus work (tasks and docs), charts, slides, an editor, onboarding and a canvas as subpaths.",
|
|
5
5
|
"homepage": "https://ui.heyramzi.com",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"types": "./src/index.ts",
|
|
29
29
|
"default": "./src/index.ts"
|
|
30
30
|
},
|
|
31
|
+
"./package.json": "./package.json",
|
|
31
32
|
"./tokens.css": "./src/tokens/index.css",
|
|
32
33
|
"./brand": {
|
|
33
34
|
"svelte": "./src/brand/index.ts",
|
|
@@ -187,6 +188,7 @@
|
|
|
187
188
|
"types": "./src/record/index.ts",
|
|
188
189
|
"default": "./src/record/index.ts"
|
|
189
190
|
},
|
|
191
|
+
"./record/demo.svelte": "./src/record/record-demo.svelte",
|
|
190
192
|
"./thread": {
|
|
191
193
|
"svelte": "./src/thread/index.ts",
|
|
192
194
|
"types": "./src/thread/index.ts",
|
|
@@ -197,78 +199,58 @@
|
|
|
197
199
|
"types": "./src/inbox/index.ts",
|
|
198
200
|
"default": "./src/inbox/index.ts"
|
|
199
201
|
},
|
|
200
|
-
"./
|
|
201
|
-
"svelte": "./src/
|
|
202
|
-
"types": "./src/
|
|
203
|
-
"default": "./src/
|
|
204
|
-
},
|
|
205
|
-
"./
|
|
206
|
-
"svelte": "./src/
|
|
207
|
-
"types": "./src/
|
|
208
|
-
"default": "./src/
|
|
209
|
-
},
|
|
210
|
-
"./
|
|
211
|
-
"svelte": "./src/
|
|
212
|
-
"types": "./src/
|
|
213
|
-
"default": "./src/
|
|
214
|
-
},
|
|
215
|
-
"./
|
|
216
|
-
"svelte": "./src/
|
|
217
|
-
"types": "./src/
|
|
218
|
-
"default": "./src/
|
|
219
|
-
},
|
|
220
|
-
"./
|
|
221
|
-
"svelte": "./src/
|
|
222
|
-
"types": "./src/
|
|
223
|
-
"default": "./src/
|
|
224
|
-
},
|
|
225
|
-
"./
|
|
226
|
-
"svelte": "./src/
|
|
227
|
-
"types": "./src/
|
|
228
|
-
"default": "./src/
|
|
229
|
-
},
|
|
230
|
-
"./
|
|
231
|
-
"svelte": "./src/
|
|
232
|
-
"types": "./src/
|
|
233
|
-
"default": "./src/
|
|
234
|
-
},
|
|
235
|
-
"./
|
|
236
|
-
"svelte": "./src/
|
|
237
|
-
"types": "./src/
|
|
238
|
-
"default": "./src/
|
|
239
|
-
},
|
|
240
|
-
"./
|
|
241
|
-
"svelte": "./src/
|
|
242
|
-
"types": "./src/
|
|
243
|
-
"default": "./src/
|
|
244
|
-
},
|
|
245
|
-
"./
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
"
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
"types": "./src/tasks/presets/sales.ts",
|
|
253
|
-
"default": "./src/tasks/presets/sales.ts"
|
|
254
|
-
},
|
|
255
|
-
"./tasks/detail": {
|
|
256
|
-
"svelte": "./src/tasks/detail/task-detail.svelte",
|
|
257
|
-
"types": "./src/tasks/detail/task-detail.svelte",
|
|
258
|
-
"default": "./src/tasks/detail/task-detail.svelte"
|
|
259
|
-
},
|
|
260
|
-
"./tasks/detail-demo.svelte": "./src/tasks/detail/detail-demo.svelte",
|
|
261
|
-
"./tasks/record": {
|
|
262
|
-
"svelte": "./src/record/index.ts",
|
|
263
|
-
"types": "./src/record/index.ts",
|
|
264
|
-
"default": "./src/record/index.ts"
|
|
265
|
-
},
|
|
266
|
-
"./tasks/record-demo.svelte": "./src/record/record-demo.svelte",
|
|
267
|
-
"./tasks/demo": {
|
|
268
|
-
"svelte": "./src/tasks/demo.svelte",
|
|
269
|
-
"default": "./src/tasks/demo.svelte"
|
|
270
|
-
},
|
|
271
|
-
"./tasks/demo.svelte": "./src/tasks/demo.svelte",
|
|
202
|
+
"./work": {
|
|
203
|
+
"svelte": "./src/work/index.ts",
|
|
204
|
+
"types": "./src/work/index.ts",
|
|
205
|
+
"default": "./src/work/index.ts"
|
|
206
|
+
},
|
|
207
|
+
"./work/kanban": {
|
|
208
|
+
"svelte": "./src/work/kanban-board.svelte",
|
|
209
|
+
"types": "./src/work/kanban-board.svelte",
|
|
210
|
+
"default": "./src/work/kanban-board.svelte"
|
|
211
|
+
},
|
|
212
|
+
"./work/list": {
|
|
213
|
+
"svelte": "./src/work/task-list.svelte",
|
|
214
|
+
"types": "./src/work/task-list.svelte",
|
|
215
|
+
"default": "./src/work/task-list.svelte"
|
|
216
|
+
},
|
|
217
|
+
"./work/gantt": {
|
|
218
|
+
"svelte": "./src/work/task-gantt.svelte",
|
|
219
|
+
"types": "./src/work/task-gantt.svelte",
|
|
220
|
+
"default": "./src/work/task-gantt.svelte"
|
|
221
|
+
},
|
|
222
|
+
"./work/time": {
|
|
223
|
+
"svelte": "./src/work/time/time-tracker.svelte",
|
|
224
|
+
"types": "./src/work/time/time-tracker.svelte",
|
|
225
|
+
"default": "./src/work/time/time-tracker.svelte"
|
|
226
|
+
},
|
|
227
|
+
"./work/inbox": {
|
|
228
|
+
"svelte": "./src/work/inbox/index.ts",
|
|
229
|
+
"types": "./src/work/inbox/index.ts",
|
|
230
|
+
"default": "./src/work/inbox/index.ts"
|
|
231
|
+
},
|
|
232
|
+
"./work/presets/sales": {
|
|
233
|
+
"svelte": "./src/work/presets/sales.ts",
|
|
234
|
+
"types": "./src/work/presets/sales.ts",
|
|
235
|
+
"default": "./src/work/presets/sales.ts"
|
|
236
|
+
},
|
|
237
|
+
"./work/detail": {
|
|
238
|
+
"svelte": "./src/work/detail/task-detail.svelte",
|
|
239
|
+
"types": "./src/work/detail/task-detail.svelte",
|
|
240
|
+
"default": "./src/work/detail/task-detail.svelte"
|
|
241
|
+
},
|
|
242
|
+
"./work/docs": {
|
|
243
|
+
"svelte": "./src/work/docs/index.ts",
|
|
244
|
+
"types": "./src/work/docs/index.ts",
|
|
245
|
+
"default": "./src/work/docs/index.ts"
|
|
246
|
+
},
|
|
247
|
+
"./work/docs-demo.svelte": "./src/work/docs/docs-demo.svelte",
|
|
248
|
+
"./work/detail-demo.svelte": "./src/work/detail/detail-demo.svelte",
|
|
249
|
+
"./work/demo": {
|
|
250
|
+
"svelte": "./src/work/demo.svelte",
|
|
251
|
+
"default": "./src/work/demo.svelte"
|
|
252
|
+
},
|
|
253
|
+
"./work/demo.svelte": "./src/work/demo.svelte",
|
|
272
254
|
"./charts": {
|
|
273
255
|
"svelte": "./src/charts/index.ts",
|
|
274
256
|
"types": "./src/charts/index.ts",
|
|
@@ -365,7 +347,9 @@
|
|
|
365
347
|
"@tiptap/core": "^3.32.0",
|
|
366
348
|
"@tiptap/extension-drag-handle": "^3.32.0",
|
|
367
349
|
"@tiptap/extension-image": "^3.32.0",
|
|
350
|
+
"@tiptap/extension-list": "^3.32.0",
|
|
368
351
|
"@tiptap/extension-placeholder": "^3.32.0",
|
|
352
|
+
"@tiptap/extension-table": "^3.32.0",
|
|
369
353
|
"@tiptap/pm": "^3.32.0",
|
|
370
354
|
"@tiptap/starter-kit": "^3.32.0",
|
|
371
355
|
"@zag-js/svelte": "^1.46.0",
|
|
@@ -102,9 +102,9 @@ const HALF_DEPTH = 15;
|
|
|
102
102
|
const CAP = 0.9;
|
|
103
103
|
const profile = (z: number, cap: number) => cap + (1 - cap) * Math.sqrt(Math.max(0, 1 - z * z));
|
|
104
104
|
|
|
105
|
-
/* Big, plain
|
|
105
|
+
/* Big, plain eyes, a little taller than wide: a round dot reads as a button at 24px. */
|
|
106
106
|
const EYE_GAP = 25;
|
|
107
|
-
const EYE_RX = 6
|
|
107
|
+
const EYE_RX = 5.6;
|
|
108
108
|
const EYE_Y = { eyes: 1, mouth: -3.5 } as const;
|
|
109
109
|
|
|
110
110
|
interface Palette {
|
|
@@ -781,9 +781,9 @@ function drawFace(ctx: CanvasRenderingContext2D, pose: Pose, cfg: DrawConfig): [
|
|
|
781
781
|
kSleep = ws;
|
|
782
782
|
const x0 = kOpen * 0.01 + kShut * 5.4 + kLaugh * 6.2 + kSleep * 6;
|
|
783
783
|
const y0 =
|
|
784
|
-
kOpen *
|
|
784
|
+
kOpen * 2 * tall + kShut * 0.6 + kLaugh * (2.2 - lift * 1.5) + kSleep * (-1.4 + sag);
|
|
785
785
|
const cy =
|
|
786
|
-
kOpen * -
|
|
786
|
+
kOpen * -6 * tall + kShut * 0.6 + kLaugh * (-11.4 - 4 * lift) + kSleep * (5.4 + 2 * sag);
|
|
787
787
|
const w = kOpen * EYE_RX * 2 * wide + kShut * 2.8 + kLaugh * 4.4 + kSleep * 4;
|
|
788
788
|
/* the eyes drift toward the look when open, less so when shut */
|
|
789
789
|
const dx = lx * (kOpen + 0.5 * (kShut + kLaugh)),
|
|
@@ -124,7 +124,7 @@
|
|
|
124
124
|
--shimmer-highlight: color-mix(in oklab, black 45%, var(--color-brand));
|
|
125
125
|
--shimmer-band: 400%;
|
|
126
126
|
--shimmer-ease: linear;
|
|
127
|
-
--digit-dur: var(--duration-
|
|
127
|
+
--digit-dur: var(--duration-slow);
|
|
128
128
|
--digit-distance: var(--distance-base);
|
|
129
129
|
--digit-blur: var(--blur-small);
|
|
130
130
|
--digit-ease: var(--ease-smooth-out);
|
|
@@ -219,7 +219,7 @@
|
|
|
219
219
|
transform-origin: left;
|
|
220
220
|
border-radius: 9999px;
|
|
221
221
|
background: var(--color-brand);
|
|
222
|
-
transition: transform var(--duration-slow,
|
|
222
|
+
transition: transform var(--duration-slow, 300ms) var(--ease-smooth-out, ease);
|
|
223
223
|
}
|
|
224
224
|
|
|
225
225
|
/* A job with no step count still has to look like it is running, so the fill
|
package/src/animations/README.md
CHANGED
|
@@ -76,7 +76,7 @@ The two merged into components are the exception — `switch.svelte` and `checkb
|
|
|
76
76
|
Every transition reads semantic CSS variables (`--modal-open-dur`, `--tilt-follow`, `--think-swap`). Where the shared motion scale in `@heyramzi/ui/design` has a token at that exact value, the variable defaults to it and keeps the literal as the fallback:
|
|
77
77
|
|
|
78
78
|
```css
|
|
79
|
-
--toast-open: var(--duration-medium,
|
|
79
|
+
--toast-open: var(--duration-medium, 280ms);
|
|
80
80
|
```
|
|
81
81
|
|
|
82
82
|
So the scale is single-sourced when the design layer is imported, and the transition still runs at the shipped value when it is not. A value the scale has no token for — a 1000ms tilt return, a 90ms reel stagger — stays a literal rather than being rounded onto the nearest rung. Override any of them at `:root` or per-scope to retune without forking the CSS.
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
--tilt-perspective: 1000px;
|
|
6
6
|
--tilt-return: 1000ms;
|
|
7
7
|
--tilt-return-ease: var(--ease-smooth-out, cubic-bezier(0.22, 1, 0.36, 1));
|
|
8
|
-
--tilt-follow: var(--duration-slow,
|
|
8
|
+
--tilt-follow: var(--duration-slow, 300ms);
|
|
9
9
|
--tilt-follow-ease: var(--ease-smooth-out, cubic-bezier(0.22, 1, 0.36, 1));
|
|
10
10
|
--tilt-glare-opacity: 0.32;
|
|
11
11
|
--tilt-glare-fade: 300ms;
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
|
|
10
10
|
:root {
|
|
11
11
|
--clear-dur: 1000ms;
|
|
12
|
-
--clear-out-dur: var(--duration-slow,
|
|
13
|
-
--clear-in-dur: var(--duration-slow,
|
|
12
|
+
--clear-out-dur: var(--duration-slow, 300ms);
|
|
13
|
+
--clear-in-dur: var(--duration-slow, 300ms);
|
|
14
14
|
--clear-out-fly: 12px;
|
|
15
15
|
--clear-in-fly: 12px;
|
|
16
16
|
--clear-out-ease: var(--ease-smooth-out, cubic-bezier(0.22, 1, 0.36, 1));
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
:root {
|
|
4
4
|
--learn-shift: 2px;
|
|
5
5
|
--learn-spread: 8deg;
|
|
6
|
-
--learn-in: var(--duration-medium,
|
|
7
|
-
--learn-out: var(--duration-medium,
|
|
6
|
+
--learn-in: var(--duration-medium, 280ms);
|
|
7
|
+
--learn-out: var(--duration-medium, 280ms);
|
|
8
8
|
--learn-ease: var(--ease-smooth-out, cubic-bezier(0.22, 1, 0.36, 1));
|
|
9
9
|
}
|
|
10
10
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
:root {
|
|
4
4
|
--like-color: #f40051;
|
|
5
5
|
--like-fill: var(--duration-quick, 150ms);
|
|
6
|
-
--like-pop: var(--duration-medium,
|
|
6
|
+
--like-pop: var(--duration-medium, 280ms);
|
|
7
7
|
--like-pop-ease: var(--ease-smooth-out, cubic-bezier(0.22, 1, 0.36, 1));
|
|
8
8
|
--like-particle-dur: 600ms;
|
|
9
9
|
--like-particle-dist: 20px;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* transitions.dev `dropdown-menu-morph`: the trigger itself becomes the menu. Set the open
|
|
2
2
|
size on the consumer side, since these sizes are the upstream demo's. */
|
|
3
3
|
:root {
|
|
4
|
-
--morph-open-dur: var(--duration-medium,
|
|
4
|
+
--morph-open-dur: var(--duration-medium, 280ms);
|
|
5
5
|
--morph-close-dur: var(--duration-fast, 250ms);
|
|
6
6
|
--morph-ease: var(--ease-smooth-out);
|
|
7
7
|
--morph-close-ease: var(--ease-smooth-out, cubic-bezier(0.22, 1, 0.36, 1));
|
|
@@ -7,14 +7,14 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
:root {
|
|
10
|
-
--check-opacity-dur: var(--duration-
|
|
11
|
-
--check-rotate-dur: var(--duration-
|
|
10
|
+
--check-opacity-dur: var(--duration-slow, 300ms);
|
|
11
|
+
--check-rotate-dur: var(--duration-slow, 300ms);
|
|
12
12
|
--check-rotate-from: 80deg;
|
|
13
|
-
--check-bob-dur: var(--duration-
|
|
13
|
+
--check-bob-dur: var(--duration-slow, 300ms);
|
|
14
14
|
--check-y-amount: 40px;
|
|
15
|
-
--check-blur-dur: var(--duration-
|
|
15
|
+
--check-blur-dur: var(--duration-slow, 300ms);
|
|
16
16
|
--check-blur-from: 10px;
|
|
17
|
-
--check-path-dur: var(--duration-
|
|
17
|
+
--check-path-dur: var(--duration-slow, 300ms);
|
|
18
18
|
--check-path-delay: 80ms;
|
|
19
19
|
--check-ease-out: var(--ease-smooth-out, cubic-bezier(0.22, 1, 0.36, 1));
|
|
20
20
|
--check-ease-opacity: var(--ease-smooth-out, cubic-bezier(0.22, 1, 0.36, 1));
|
|
@@ -3,7 +3,7 @@ import { sortParentsFirst, stripCallbacks } from "./canvas-cleanup.js";
|
|
|
3
3
|
import { makeNode } from "../test-utils.js";
|
|
4
4
|
import type { CanvasNode } from "../types.js";
|
|
5
5
|
|
|
6
|
-
/** Read serialized data as a loose record: these tests exercise
|
|
6
|
+
/** Read serialized data as a loose record: these tests exercise old fields
|
|
7
7
|
* that are no longer part of any per-type shape but must survive the denylist. */
|
|
8
8
|
function loose(node: { data: unknown }): Record<string, unknown> {
|
|
9
9
|
return node.data as Record<string, unknown>;
|
package/src/canvas/ops/ops.ts
CHANGED
|
@@ -36,7 +36,7 @@ export const patchNodeOp = z.object({
|
|
|
36
36
|
kind: z.literal("patchNode"),
|
|
37
37
|
nodeId: z.string().min(1),
|
|
38
38
|
data: z.record(z.string(), z.unknown()).optional(),
|
|
39
|
-
// Merged into the node's style object. A
|
|
39
|
+
// Merged into the node's style object. A CSS-string style from an old save is replaced
|
|
40
40
|
// wholesale (there is nothing sane to merge into).
|
|
41
41
|
style: z.record(z.string(), z.unknown()).optional(),
|
|
42
42
|
width: z.number().optional(),
|
|
@@ -114,6 +114,16 @@
|
|
|
114
114
|
const fadeMask = $derived(fade > 0 ? `url(#${uid}-fade)` : undefined);
|
|
115
115
|
const fallback = $derived(data.length > 0 ? data[data.length - 1] : null);
|
|
116
116
|
const display = $derived(active ?? fallback);
|
|
117
|
+
|
|
118
|
+
// A long tick label ("1,000,000") falls back to "1M" so the left gutter holds it.
|
|
119
|
+
const compact = new Intl.NumberFormat(undefined, {
|
|
120
|
+
notation: "compact",
|
|
121
|
+
maximumFractionDigits: 1,
|
|
122
|
+
});
|
|
123
|
+
function valueTick(v: number): string {
|
|
124
|
+
const full = formatValue(v);
|
|
125
|
+
return full.length > 6 && Number.isFinite(v) ? compact.format(v) : full;
|
|
126
|
+
}
|
|
117
127
|
const figureRows = $derived(
|
|
118
128
|
display
|
|
119
129
|
? series.map((s) => ({ value: Number(display[s.key]), label: s.label, color: s.color }))
|
|
@@ -158,7 +168,7 @@
|
|
|
158
168
|
motion={DOMAIN_MOTION}
|
|
159
169
|
>
|
|
160
170
|
<Svg>
|
|
161
|
-
<Axis placement="left" format={(v) =>
|
|
171
|
+
<Axis placement="left" format={(v) => valueTick(Number(v))} ticks={4} />
|
|
162
172
|
<Axis placement="bottom" format={(v) => formatX(v)} ticks={xTicks} />
|
|
163
173
|
<g class="hr-area-grid">
|
|
164
174
|
<Axis placement="left" grid rule={false} ticks={4} format={() => ""} />
|
package/src/charts/bar-chart.css
CHANGED
|
@@ -55,7 +55,8 @@
|
|
|
55
55
|
transform-origin: bottom;
|
|
56
56
|
animation: hr-bar-grow var(--chart-motion-reveal, 1100ms)
|
|
57
57
|
var(--chart-ease-reveal, cubic-bezier(0.85, 0, 0.15, 1)) both;
|
|
58
|
-
transition: opacity var(--chart-motion-fast, 150ms)
|
|
58
|
+
transition: opacity var(--chart-motion-fast, 150ms)
|
|
59
|
+
var(--ease-in-out, cubic-bezier(0.77, 0, 0.175, 1));
|
|
59
60
|
}
|
|
60
61
|
.hr-bar-chart .hr-bar-bars:nth-of-type(2) {
|
|
61
62
|
animation-delay: var(--chart-stagger, 30ms);
|
|
@@ -75,7 +75,11 @@
|
|
|
75
75
|
// =========================
|
|
76
76
|
let active = $state<TData | null>(null);
|
|
77
77
|
|
|
78
|
-
// =========================
|
|
78
|
+
// =========================
|
|
79
|
+
// FUNCTIONS
|
|
80
|
+
// =========================
|
|
81
|
+
|
|
82
|
+
// WHY: the tooltip header calls this so it re-runs per active band; an unread {@const} runs once.
|
|
79
83
|
|
|
80
84
|
// The microtask defers the state write out of template evaluation.
|
|
81
85
|
function trackActive(d: TData): string {
|
|
@@ -128,6 +132,17 @@
|
|
|
128
132
|
return [...groups].map(([c, rows]) => ({ key: c, color: c, rows, at: undefined }));
|
|
129
133
|
});
|
|
130
134
|
const activeColor = $derived(active && colorOf ? colorOf(active) : color);
|
|
135
|
+
|
|
136
|
+
// WHY: "1,000,000" overflows the 48px gutter and the gap between ticks, so a long label
|
|
137
|
+
// falls back to "1M"; the tooltip keeps the full value.
|
|
138
|
+
const compact = new Intl.NumberFormat(undefined, {
|
|
139
|
+
notation: "compact",
|
|
140
|
+
maximumFractionDigits: 1,
|
|
141
|
+
});
|
|
142
|
+
function valueTick(v: number): string {
|
|
143
|
+
const full = formatValue(v);
|
|
144
|
+
return full.length > 6 && Number.isFinite(v) ? compact.format(v) : full;
|
|
145
|
+
}
|
|
131
146
|
</script>
|
|
132
147
|
|
|
133
148
|
<!-- =========================== -->
|
|
@@ -172,12 +187,12 @@
|
|
|
172
187
|
<Svg>
|
|
173
188
|
{#if horizontal}
|
|
174
189
|
<Axis placement="left" format={(v) => (formatTick ?? formatX)(v)} />
|
|
175
|
-
<Axis placement="bottom" format={(v) =>
|
|
190
|
+
<Axis placement="bottom" format={(v) => valueTick(Number(v))} ticks={4} />
|
|
176
191
|
<g class="hr-bar-grid">
|
|
177
192
|
<Axis placement="bottom" grid rule={false} ticks={4} format={() => ""} />
|
|
178
193
|
</g>
|
|
179
194
|
{:else}
|
|
180
|
-
<Axis placement="left" format={(v) =>
|
|
195
|
+
<Axis placement="left" format={(v) => valueTick(Number(v))} ticks={4} />
|
|
181
196
|
<Axis placement="bottom" format={(v) => (formatTick ?? formatX)(v)} />
|
|
182
197
|
<g class="hr-bar-grid">
|
|
183
198
|
<Axis placement="left" grid rule={false} ticks={4} format={() => ""} />
|
package/src/charts/charts.css
CHANGED
|
@@ -174,7 +174,8 @@
|
|
|
174
174
|
/* bklit-ui's hover: the series drops to 0.3 and hover-focus.svelte redraws the
|
|
175
175
|
stretch around the point at full strength. */
|
|
176
176
|
.hr-hover-dim {
|
|
177
|
-
transition: opacity
|
|
177
|
+
transition: opacity var(--duration-slow, 300ms)
|
|
178
|
+
var(--ease-in-out, cubic-bezier(0.77, 0, 0.175, 1));
|
|
178
179
|
}
|
|
179
180
|
.hr-hover-dim.hr-hover-dimmed {
|
|
180
181
|
opacity: 0.3;
|
|
@@ -117,6 +117,16 @@
|
|
|
117
117
|
const fadeMask = $derived(fade > 0 ? `url(#${uid}-fade)` : undefined);
|
|
118
118
|
const fallback = $derived(data.length > 0 ? data[data.length - 1] : null);
|
|
119
119
|
const display = $derived(active ?? fallback);
|
|
120
|
+
|
|
121
|
+
// A long tick label ("1,000,000") falls back to "1M" so the left gutter holds it.
|
|
122
|
+
const compact = new Intl.NumberFormat(undefined, {
|
|
123
|
+
notation: "compact",
|
|
124
|
+
maximumFractionDigits: 1,
|
|
125
|
+
});
|
|
126
|
+
function valueTick(v: number): string {
|
|
127
|
+
const full = formatValue(v);
|
|
128
|
+
return full.length > 6 && Number.isFinite(v) ? compact.format(v) : full;
|
|
129
|
+
}
|
|
120
130
|
// WHY: a dashed tail is two splines sharing the boundary point so the solid
|
|
121
131
|
// and dashed segments meet without a gap.
|
|
122
132
|
const hasTail = $derived(
|
|
@@ -189,7 +199,7 @@
|
|
|
189
199
|
motion={DOMAIN_MOTION}
|
|
190
200
|
>
|
|
191
201
|
<Svg>
|
|
192
|
-
<Axis placement="left" format={(v) =>
|
|
202
|
+
<Axis placement="left" format={(v) => valueTick(Number(v))} ticks={4} />
|
|
193
203
|
<Axis placement="bottom" format={(v) => formatX(v)} />
|
|
194
204
|
<g class="hr-line-grid">
|
|
195
205
|
<Axis placement="left" grid rule={false} ticks={4} format={() => ""} />
|
|
@@ -69,9 +69,8 @@ export function totalPages(total: number, pageSize: number): number {
|
|
|
69
69
|
|
|
70
70
|
export function formatCount(visible: number, total: number, label: string): string {
|
|
71
71
|
const singular = label.endsWith("s") ? label.slice(0, -1) : label;
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
return `${visible} of ${total} ${label}`;
|
|
72
|
+
// The noun agrees with the total: "0 of 1 item", not "0 of 1 items".
|
|
73
|
+
const noun = total === 1 ? singular : label;
|
|
74
|
+
if (visible === total) return `${total.toLocaleString()} ${noun}`;
|
|
75
|
+
return `${visible.toLocaleString()} of ${total.toLocaleString()} ${noun}`;
|
|
77
76
|
}
|
|
@@ -26,7 +26,9 @@
|
|
|
26
26
|
|
|
27
27
|
{#if pages > 1}
|
|
28
28
|
<div class="flex items-center justify-between border-t px-4 py-3 text-sm text-muted-foreground">
|
|
29
|
-
<span
|
|
29
|
+
<span class="tabular-nums"
|
|
30
|
+
>{start.toLocaleString()}-{end.toLocaleString()} of {total.toLocaleString()}</span
|
|
31
|
+
>
|
|
30
32
|
<div class="flex items-center gap-1">
|
|
31
33
|
{#if buildHref}
|
|
32
34
|
{#if hasPrev}
|
|
@@ -38,7 +40,7 @@
|
|
|
38
40
|
<CaretLeft class="size-4" />
|
|
39
41
|
</Button>
|
|
40
42
|
{/if}
|
|
41
|
-
<span class="mx-2 tabular-nums">{page} / {pages}</span>
|
|
43
|
+
<span class="mx-2 tabular-nums">{page.toLocaleString()} / {pages.toLocaleString()}</span>
|
|
42
44
|
{#if hasNext}
|
|
43
45
|
<Button variant="outline" size="sm" icon href={buildHref(page + 1)}>
|
|
44
46
|
<CaretRight class="size-4" />
|
|
@@ -58,7 +60,7 @@
|
|
|
58
60
|
>
|
|
59
61
|
<CaretLeft class="size-4" />
|
|
60
62
|
</Button>
|
|
61
|
-
<span class="mx-2 tabular-nums">{page} / {pages}</span>
|
|
63
|
+
<span class="mx-2 tabular-nums">{page.toLocaleString()} / {pages.toLocaleString()}</span>
|
|
62
64
|
<Button
|
|
63
65
|
variant="outline"
|
|
64
66
|
size="sm"
|
|
@@ -17,10 +17,10 @@
|
|
|
17
17
|
label: string;
|
|
18
18
|
onselect: (id: string) => void;
|
|
19
19
|
/**
|
|
20
|
-
* `rail`
|
|
21
|
-
*
|
|
20
|
+
* `rail` sits beside the document, `bar` replaces it on a narrow pane, and `ticks` marks
|
|
21
|
+
* each heading in the gutter with a card of headings on hover or focus.
|
|
22
22
|
*/
|
|
23
|
-
variant?: "rail" | "bar";
|
|
23
|
+
variant?: "rail" | "bar" | "ticks";
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
// =========================
|
|
@@ -28,6 +28,10 @@
|
|
|
28
28
|
// =========================
|
|
29
29
|
let { items, active, label, onselect, variant = "rail" }: Props = $props();
|
|
30
30
|
|
|
31
|
+
let hovering = $state(false);
|
|
32
|
+
let focusing = $state(false);
|
|
33
|
+
let showCard = $derived(hovering || focusing);
|
|
34
|
+
|
|
31
35
|
// =========================
|
|
32
36
|
// STATE
|
|
33
37
|
// =========================
|
|
@@ -101,7 +105,63 @@
|
|
|
101
105
|
<!-- =========================== -->
|
|
102
106
|
<!-- MARKUP -->
|
|
103
107
|
<!-- =========================== -->
|
|
104
|
-
{#if variant === "
|
|
108
|
+
{#if variant === "ticks"}
|
|
109
|
+
<nav
|
|
110
|
+
aria-label={label}
|
|
111
|
+
class="otp-ticks relative"
|
|
112
|
+
onpointerenter={() => (hovering = true)}
|
|
113
|
+
onpointerleave={() => (hovering = false)}
|
|
114
|
+
onfocusin={() => (focusing = true)}
|
|
115
|
+
onfocusout={(event) => {
|
|
116
|
+
if (!event.currentTarget.contains(event.relatedTarget as Node | null)) focusing = false;
|
|
117
|
+
}}
|
|
118
|
+
>
|
|
119
|
+
<ul class="flex flex-col py-2">
|
|
120
|
+
{#each items as item (item.id)}
|
|
121
|
+
{@const current = item.id === active}
|
|
122
|
+
<li>
|
|
123
|
+
<button
|
|
124
|
+
type="button"
|
|
125
|
+
aria-label={item.label}
|
|
126
|
+
aria-current={current ? "location" : undefined}
|
|
127
|
+
onclick={() => pick(item.id)}
|
|
128
|
+
class="otp-tick group/tick flex h-3.5 w-7 items-center pl-2 any-pointer-coarse:h-6"
|
|
129
|
+
>
|
|
130
|
+
<span
|
|
131
|
+
class="block h-0.5 rounded-full transition-[width,background-color] duration-[var(--duration-fast)] ease-[var(--ease-smooth-out)] {current
|
|
132
|
+
? 'w-3 bg-foreground'
|
|
133
|
+
: 'w-2 bg-foreground/25 group-hover/tick:bg-foreground/60'}"
|
|
134
|
+
></span>
|
|
135
|
+
</button>
|
|
136
|
+
</li>
|
|
137
|
+
{/each}
|
|
138
|
+
</ul>
|
|
139
|
+
<!-- The card starts inside the nav's own box, so moving from a tick onto it never leaves
|
|
140
|
+
the hover. Rows are for the pointer; the ticks are the keyboard path. -->
|
|
141
|
+
<div
|
|
142
|
+
inert={!showCard}
|
|
143
|
+
data-open={showCard}
|
|
144
|
+
class="otp-card thin-scrollbar absolute left-full top-1/2 z-30 max-h-[min(70svh,28rem)] w-max min-w-44 max-w-64 overflow-y-auto rounded-xl bg-popover p-1.5 shadow-raised ring-1 ring-foreground/10"
|
|
145
|
+
>
|
|
146
|
+
{#each items as item (item.id)}
|
|
147
|
+
{@const current = item.id === active}
|
|
148
|
+
<button
|
|
149
|
+
type="button"
|
|
150
|
+
tabindex="-1"
|
|
151
|
+
onclick={() => pick(item.id)}
|
|
152
|
+
class="block w-full truncate rounded-md py-1 pr-3 text-left text-[13px] leading-5 transition-colors duration-[var(--duration-fast)] ease-[var(--ease-smooth-out)] hover:bg-foreground/6 {item.level ===
|
|
153
|
+
3
|
|
154
|
+
? 'pl-5'
|
|
155
|
+
: 'pl-2'} {current
|
|
156
|
+
? 'font-medium text-foreground'
|
|
157
|
+
: 'text-muted-foreground hover:text-foreground'}"
|
|
158
|
+
>
|
|
159
|
+
{item.label}
|
|
160
|
+
</button>
|
|
161
|
+
{/each}
|
|
162
|
+
</div>
|
|
163
|
+
</nav>
|
|
164
|
+
{:else if variant === "rail"}
|
|
105
165
|
<nav aria-label={label} class="flex max-h-[calc(100svh-8rem)] flex-col gap-3">
|
|
106
166
|
<!-- Set like the map's own headings on the left, so the two rails read as one. -->
|
|
107
167
|
<p class="font-heading text-xs font-semibold uppercase tracking-wider text-muted-foreground">
|
|
@@ -221,9 +281,26 @@
|
|
|
221
281
|
);
|
|
222
282
|
}
|
|
223
283
|
|
|
284
|
+
/* Opacity and a short slide, no scale: the card is anchored to a line of ticks. */
|
|
285
|
+
.otp-card {
|
|
286
|
+
opacity: 0;
|
|
287
|
+
translate: -4px -50%;
|
|
288
|
+
pointer-events: none;
|
|
289
|
+
transition:
|
|
290
|
+
opacity var(--duration-fast) var(--ease-smooth-out),
|
|
291
|
+
translate var(--duration-fast) var(--ease-smooth-out);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
.otp-card[data-open="true"] {
|
|
295
|
+
opacity: 1;
|
|
296
|
+
translate: 0 -50%;
|
|
297
|
+
pointer-events: auto;
|
|
298
|
+
}
|
|
299
|
+
|
|
224
300
|
@media (prefers-reduced-motion: reduce) {
|
|
225
301
|
.otp-marker.is-ready,
|
|
226
|
-
.otp-progress
|
|
302
|
+
.otp-progress,
|
|
303
|
+
.otp-card {
|
|
227
304
|
transition: none;
|
|
228
305
|
}
|
|
229
306
|
}
|