@heyramzi/ui 3.1.0 → 3.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.
- package/dist/lib/sliding-pill.js +2 -2
- package/dist/record/measure-clamp.js +23 -0
- package/dist/shadcn/button-group/index.js +6 -0
- package/dist/shadcn/checkbox/checkbox-box.js +3 -0
- package/dist/shadcn/input-group/index.js +9 -0
- package/dist/shadcn/item/index.js +13 -0
- package/dist/shadcn/native-select/index.js +6 -0
- package/dist/shadcn/pagination/index.js +12 -0
- package/package.json +1 -1
- package/scripts/sync-shadcn.mjs +0 -5
- package/src/ai/generation-progress/GenerationProgress.svelte +14 -12
- package/src/canvas/pen/PenOverlay.svelte +1 -30
- package/src/canvas/screen/PartView.svelte +13 -8
- package/src/canvas/screen/ScreenBody.svelte +18 -11
- package/src/canvas/ui/BarTooltip.svelte +46 -0
- package/src/canvas/ui/BarTooltip.svelte.d.ts +17 -0
- package/src/canvas/ui/CanvasToolbar.svelte +170 -156
- package/src/canvas/ui/StylePanel.svelte +179 -102
- package/src/canvas/ui/canvas-ui.css +12 -76
- package/src/charts/dashboard/data-table.svelte +80 -80
- package/src/charts/dashboard/hint.svelte +34 -0
- package/src/charts/dashboard/hint.svelte.d.ts +10 -0
- package/src/charts/dashboard/rating-chip.svelte +4 -3
- package/src/charts/legend.css +0 -8
- package/src/charts/legend.svelte +26 -30
- package/src/components/copy/copy-button.svelte +10 -2
- package/src/components/copy/copy-button.test.ts +7 -0
- package/src/components/data-table/table-skeleton.svelte +15 -7
- package/src/components/data-table/table-skeleton.svelte.d.ts +4 -0
- package/src/components/docs/on-this-page.svelte +2 -2
- package/src/components/hold-to-confirm/hold-to-confirm.svelte +2 -1
- package/src/components/list-page/filter-item.svelte +20 -7
- package/src/components/list-page/filter-popover.svelte +41 -16
- package/src/components/list-page/filter-popover.svelte.d.ts +1 -1
- package/src/components/list-page/grouped-list.svelte +41 -23
- package/src/components/picker/picker.svelte +8 -3
- package/src/components/picker/picker.svelte.d.ts +2 -1
- package/src/components/segmented/segmented-filter.svelte +19 -10
- package/src/components/segmented/segmented-track.svelte +11 -3
- package/src/components/table-view/table-view.svelte +22 -6
- package/src/components/tags-input/tags-input.svelte +94 -77
- package/src/components/toolbar/toolbar.svelte +2 -1
- package/src/components/toolbar/toolbar.test.ts +5 -4
- package/src/components/work-parts/group-head.svelte +28 -9
- package/src/components/work-parts/group-head.svelte.d.ts +5 -2
- package/src/editor/editor-toolbar.svelte +223 -193
- package/src/editor/editor.svelte +56 -38
- package/src/editor/image-toolbar.svelte +99 -37
- package/src/editor/image-toolbar.svelte.d.ts +4 -5
- package/src/editor/url-dialog.svelte +96 -0
- package/src/editor/url-dialog.svelte.d.ts +15 -0
- package/src/effects/theme-toggle/ThemeToggle.css +8 -8
- package/src/effects/theme-toggle/ThemeToggle.svelte +20 -6
- package/src/inbox/thread-inbox.svelte +9 -1
- package/src/lib/live-region.svelte +12 -4
- package/src/lib/live-region.svelte.d.ts +5 -1
- package/src/lib/sliding-pill.ts +3 -2
- package/src/record/info-list.svelte +33 -59
- package/src/record/measure-clamp.ts +31 -0
- package/src/record/record-shell.svelte +38 -39
- package/src/record/record-timeline.svelte +10 -5
- package/src/record/steps-rail.svelte +50 -36
- package/src/record/where-we-are.svelte +5 -19
- package/src/shadcn/button-group/button-group-separator.svelte +23 -0
- package/src/shadcn/button-group/button-group-separator.svelte.d.ts +13 -0
- package/src/shadcn/button-group/button-group-text.svelte +28 -0
- package/src/shadcn/button-group/button-group-text.svelte.d.ts +11 -0
- package/src/shadcn/button-group/button-group.svelte +46 -0
- package/src/shadcn/button-group/button-group.svelte.d.ts +16 -0
- package/src/shadcn/button-group/index.ts +15 -0
- package/src/shadcn/checkbox/checkbox-box.ts +4 -0
- package/src/shadcn/checkbox/checkbox.svelte +2 -4
- package/src/shadcn/input-group/index.ts +22 -0
- package/src/shadcn/input-group/input-group-addon.svelte +51 -0
- package/src/shadcn/input-group/input-group-addon.svelte.d.ts +18 -0
- package/src/shadcn/input-group/input-group-button.svelte +49 -0
- package/src/shadcn/input-group/input-group-button.svelte.d.ts +18 -0
- package/src/shadcn/input-group/input-group-input.svelte +20 -0
- package/src/shadcn/input-group/input-group-input.svelte.d.ts +11 -0
- package/src/shadcn/input-group/input-group-text.svelte +19 -0
- package/src/shadcn/input-group/input-group-text.svelte.d.ts +5 -0
- package/src/shadcn/input-group/input-group-textarea.svelte +20 -0
- package/src/shadcn/input-group/input-group-textarea.svelte.d.ts +3 -0
- package/src/shadcn/input-group/input-group.svelte +24 -0
- package/src/shadcn/input-group/input-group.svelte.d.ts +5 -0
- package/src/shadcn/item/index.ts +34 -0
- package/src/shadcn/item/item-actions.svelte +20 -0
- package/src/shadcn/item/item-actions.svelte.d.ts +5 -0
- package/src/shadcn/item/item-content.svelte +23 -0
- package/src/shadcn/item/item-content.svelte.d.ts +5 -0
- package/src/shadcn/item/item-description.svelte +23 -0
- package/src/shadcn/item/item-description.svelte.d.ts +5 -0
- package/src/shadcn/item/item-footer.svelte +20 -0
- package/src/shadcn/item/item-footer.svelte.d.ts +5 -0
- package/src/shadcn/item/item-group.svelte +21 -0
- package/src/shadcn/item/item-group.svelte.d.ts +5 -0
- package/src/shadcn/item/item-header.svelte +20 -0
- package/src/shadcn/item/item-header.svelte.d.ts +5 -0
- package/src/shadcn/item/item-media.svelte +42 -0
- package/src/shadcn/item/item-media.svelte.d.ts +17 -0
- package/src/shadcn/item/item-separator.svelte +19 -0
- package/src/shadcn/item/item-separator.svelte.d.ts +13 -0
- package/src/shadcn/item/item-title.svelte +20 -0
- package/src/shadcn/item/item-title.svelte.d.ts +5 -0
- package/src/shadcn/item/item.svelte +61 -0
- package/src/shadcn/item/item.svelte.d.ts +28 -0
- package/src/shadcn/native-select/index.ts +13 -0
- package/src/shadcn/native-select/native-select-opt-group.svelte +20 -0
- package/src/shadcn/native-select/native-select-opt-group.svelte.d.ts +5 -0
- package/src/shadcn/native-select/native-select-option.svelte +20 -0
- package/src/shadcn/native-select/native-select-option.svelte.d.ts +5 -0
- package/src/shadcn/native-select/native-select.svelte +45 -0
- package/src/shadcn/native-select/native-select.svelte.d.ts +8 -0
- package/src/shadcn/pagination/index.ts +31 -0
- package/src/shadcn/pagination/pagination-content.svelte +20 -0
- package/src/shadcn/pagination/pagination-content.svelte.d.ts +5 -0
- package/src/shadcn/pagination/pagination-ellipsis.svelte +22 -0
- package/src/shadcn/pagination/pagination-ellipsis.svelte.d.ts +5 -0
- package/src/shadcn/pagination/pagination-item.svelte +14 -0
- package/src/shadcn/pagination/pagination-item.svelte.d.ts +5 -0
- package/src/shadcn/pagination/pagination-link.svelte +41 -0
- package/src/shadcn/pagination/pagination-link.svelte.d.ts +9 -0
- package/src/shadcn/pagination/pagination-next-button.svelte +34 -0
- package/src/shadcn/pagination/pagination-next-button.svelte.d.ts +4 -0
- package/src/shadcn/pagination/pagination-next.svelte +26 -0
- package/src/shadcn/pagination/pagination-next.svelte.d.ts +4 -0
- package/src/shadcn/pagination/pagination-prev-button.svelte +34 -0
- package/src/shadcn/pagination/pagination-prev-button.svelte.d.ts +4 -0
- package/src/shadcn/pagination/pagination-previous.svelte +30 -0
- package/src/shadcn/pagination/pagination-previous.svelte.d.ts +4 -0
- package/src/shadcn/pagination/pagination.svelte +27 -0
- package/src/shadcn/pagination/pagination.svelte.d.ts +4 -0
- package/src/slides/Controls.svelte +39 -36
- package/src/slides/Deck.svelte +11 -8
- package/src/thread/thread.svelte +15 -8
- package/src/work/detail/task-detail.svelte +1 -1
- package/src/work/gantt/gantt-task-row.svelte +3 -6
- package/src/work/inbox/inbox-card.svelte +7 -5
- package/src/work/inbox/inbox-trace.svelte +14 -10
- package/src/work/kanban-board.svelte +40 -25
- package/src/work/kanban-hidden-rail.svelte +67 -65
- package/src/work/task-list.svelte +145 -124
- package/src/work/task-list.svelte.test.ts +5 -2
- package/src/work/time/time-tracker.svelte +35 -10
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import {
|
|
2
|
+
import { Brain } from "phosphor-svelte";
|
|
3
|
+
import * as Trace from "../../shadcn/trace/index.js";
|
|
3
4
|
import type { InboxTrace } from "./inbox-types.js";
|
|
4
5
|
|
|
5
6
|
interface Props {
|
|
@@ -18,13 +19,16 @@
|
|
|
18
19
|
);
|
|
19
20
|
</script>
|
|
20
21
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
<
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
<Trace.Collapsible class="text-sm">
|
|
23
|
+
<Trace.CollapsibleTrigger class="gap-2">
|
|
24
|
+
<Trace.Line>
|
|
25
|
+
<Trace.Icon><Brain aria-hidden="true" /></Trace.Icon>
|
|
26
|
+
<Trace.Title
|
|
27
|
+
>Trace{count > 0 ? ` · ${count} ${count === 1 ? "quote" : "quotes"}` : ""}</Trace.Title
|
|
28
|
+
>
|
|
29
|
+
</Trace.Line>
|
|
30
|
+
</Trace.CollapsibleTrigger>
|
|
31
|
+
<Trace.CollapsibleContent class="mt-3 mb-0 ml-0 border-l-2 pr-0 pl-3">
|
|
28
32
|
<div class="flex flex-col gap-3">
|
|
29
33
|
{#if label || trace.source.href}
|
|
30
34
|
<p class="text-foreground">
|
|
@@ -54,5 +58,5 @@
|
|
|
54
58
|
<p><span class="text-foreground font-medium">Why:</span> {reasoning}</p>
|
|
55
59
|
{/if}
|
|
56
60
|
</div>
|
|
57
|
-
</
|
|
58
|
-
</
|
|
61
|
+
</Trace.CollapsibleContent>
|
|
62
|
+
</Trace.Collapsible>
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
type KanbanColumnView,
|
|
15
15
|
} from "./kanban-board-state.svelte.js";
|
|
16
16
|
import { Button } from "../shadcn/button/index.js";
|
|
17
|
+
import * as Collapsible from "../shadcn/collapsible/index.js";
|
|
17
18
|
import { workLooks } from "../components/work-parts/looks.js";
|
|
18
19
|
import type {
|
|
19
20
|
KanbanCardContext,
|
|
@@ -278,33 +279,47 @@
|
|
|
278
279
|
</div>
|
|
279
280
|
{/if}
|
|
280
281
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
282
|
+
<!-- The root renders no element, so the fold and its button stay items of the column's flex well. -->
|
|
283
|
+
<Collapsible.Root
|
|
284
|
+
bind:open={() => open, (next) => (next ? board.expand(column.id) : board.collapse(column.id))}
|
|
285
|
+
>
|
|
286
|
+
{#snippet child()}
|
|
287
|
+
{#if showTail}
|
|
288
|
+
<!-- The tail sits shut in a 0fr row, so opening and closing animate its height. -->
|
|
289
|
+
<Collapsible.Content forceMount>
|
|
290
|
+
{#snippet child({ props })}
|
|
291
|
+
<div {...props} class="kanban-fold" data-open={open} inert={!open}>
|
|
292
|
+
<div class="kanban-fold-inner" role="list">
|
|
293
|
+
{#each tail as item, index (getId(item))}
|
|
294
|
+
{@render card(column, item, head.length + index, list)}
|
|
295
|
+
{/each}
|
|
296
|
+
</div>
|
|
297
|
+
</div>
|
|
298
|
+
{/snippet}
|
|
299
|
+
</Collapsible.Content>
|
|
300
|
+
{/if}
|
|
291
301
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
302
|
+
{#if dnd.showIndicatorEnd(column.id) && board.idAt(shown, shown.length - 1) !== dnd.draggedId}
|
|
303
|
+
{@render dropSlot()}
|
|
304
|
+
{/if}
|
|
295
305
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
306
|
+
{#if tail.length > 0 || open}
|
|
307
|
+
<Collapsible.Trigger>
|
|
308
|
+
{#snippet child({ props })}
|
|
309
|
+
<Button
|
|
310
|
+
{...props}
|
|
311
|
+
variant="ghost"
|
|
312
|
+
size="sm"
|
|
313
|
+
class="shrink-0 justify-start gap-1.5 px-2 text-xs font-normal text-muted-foreground"
|
|
314
|
+
>
|
|
315
|
+
<KanbanFoldChevron {open} class="size-2.5" />
|
|
316
|
+
<span class="tabular-nums">{open ? lessLabel : moreLabel(tail.length)}</span>
|
|
317
|
+
</Button>
|
|
318
|
+
{/snippet}
|
|
319
|
+
</Collapsible.Trigger>
|
|
320
|
+
{/if}
|
|
321
|
+
{/snippet}
|
|
322
|
+
</Collapsible.Root>
|
|
308
323
|
|
|
309
324
|
{#if columnFooter}
|
|
310
325
|
{@render columnFooter(column, context)}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { CaretDown } from "phosphor-svelte";
|
|
4
4
|
import { cn } from "../lib/utils.js";
|
|
5
5
|
import { Button } from "../shadcn/button/index.js";
|
|
6
|
+
import * as Collapsible from "../shadcn/collapsible/index.js";
|
|
6
7
|
import { workLooks } from "../components/work-parts/looks.js";
|
|
7
8
|
import type { KanbanRailState } from "./kanban-rail-state.svelte.js";
|
|
8
9
|
import type { KanbanColumn as KanbanColumnDef } from "./kanban-types.js";
|
|
@@ -21,79 +22,80 @@
|
|
|
21
22
|
let { columns, rail, countOf, hiddenLabel, showAllLabel, hiddenItem }: Props = $props();
|
|
22
23
|
</script>
|
|
23
24
|
|
|
24
|
-
<
|
|
25
|
-
<
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"size-2.5 transition-transform duration-fast ease-smooth-out motion-reduce:transition-none [&>svg]:size-full",
|
|
35
|
-
!rail.railOpen && "-rotate-90",
|
|
36
|
-
)}
|
|
37
|
-
aria-hidden="true"><CaretDown aria-hidden="true" weight="bold" /></span
|
|
38
|
-
>
|
|
39
|
-
{hiddenLabel}
|
|
40
|
-
</Button>
|
|
41
|
-
|
|
42
|
-
{#if rail.railOpen}
|
|
43
|
-
<div class="flex flex-col gap-0.5">
|
|
44
|
-
{#each columns as column (column.id)}
|
|
45
|
-
{@const count = countOf(column.id)}
|
|
46
|
-
{@const over = rail.dragOverHidden === column.id}
|
|
47
|
-
{@const hue = column.hue ?? "var(--color-primary)"}
|
|
48
|
-
<div
|
|
49
|
-
role="region"
|
|
50
|
-
aria-label={column.ariaLabel ?? column.label}
|
|
25
|
+
<Collapsible.Root bind:open={rail.railOpen} class="kanban-rail flex w-40 shrink-0 flex-col gap-1">
|
|
26
|
+
<Collapsible.Trigger>
|
|
27
|
+
{#snippet child({ props })}
|
|
28
|
+
<Button
|
|
29
|
+
{...props}
|
|
30
|
+
variant="ghost"
|
|
31
|
+
size="sm"
|
|
32
|
+
class="justify-start px-2 text-xs font-normal text-muted-foreground"
|
|
33
|
+
>
|
|
34
|
+
<span
|
|
51
35
|
class={cn(
|
|
52
|
-
"
|
|
53
|
-
|
|
54
|
-
rail.leaving.has(column.id) && "kanban-rail-item-leaving",
|
|
36
|
+
"size-2.5 transition-transform duration-fast ease-smooth-out motion-reduce:transition-none [&>svg]:size-full",
|
|
37
|
+
!rail.railOpen && "-rotate-90",
|
|
55
38
|
)}
|
|
56
|
-
|
|
57
|
-
{over ? 10 : 0}%, transparent)"
|
|
58
|
-
style:outline="1px solid color-mix(in oklch, {hue}
|
|
59
|
-
{over ? 28 : 0}%, transparent)"
|
|
60
|
-
ondragover={(e) => rail.onHiddenDragOver(e, column.id)}
|
|
61
|
-
ondragleave={rail.onHiddenDragLeave}
|
|
62
|
-
ondrop={(e) => rail.onHiddenDrop(e, column.id)}
|
|
39
|
+
aria-hidden="true"><CaretDown aria-hidden="true" weight="bold" /></span
|
|
63
40
|
>
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
class="w-full justify-start gap-2 text-xs text-muted-foreground"
|
|
69
|
-
>
|
|
70
|
-
{#if hiddenItem}
|
|
71
|
-
{@render hiddenItem(column, { count, active: over })}
|
|
72
|
-
{:else}
|
|
73
|
-
<span class="truncate whitespace-nowrap">{column.label}</span>
|
|
74
|
-
{#if count > 0}
|
|
75
|
-
<span class="ms-auto text-xs text-muted-foreground/60 tabular-nums"
|
|
76
|
-
>{count.toLocaleString()}</span
|
|
77
|
-
>
|
|
78
|
-
{/if}
|
|
79
|
-
{/if}
|
|
80
|
-
</Button>
|
|
81
|
-
</div>
|
|
82
|
-
{/each}
|
|
41
|
+
{hiddenLabel}
|
|
42
|
+
</Button>
|
|
43
|
+
{/snippet}
|
|
44
|
+
</Collapsible.Trigger>
|
|
83
45
|
|
|
84
|
-
|
|
46
|
+
<Collapsible.Content class="flex flex-col gap-0.5">
|
|
47
|
+
{#each columns as column (column.id)}
|
|
48
|
+
{@const count = countOf(column.id)}
|
|
49
|
+
{@const over = rail.dragOverHidden === column.id}
|
|
50
|
+
{@const hue = column.hue ?? "var(--color-primary)"}
|
|
51
|
+
<div
|
|
52
|
+
role="region"
|
|
53
|
+
aria-label={column.ariaLabel ?? column.label}
|
|
54
|
+
class={cn(
|
|
55
|
+
"kanban-rail-item transition-[background-color,outline-color] duration-quick ease-smooth-out",
|
|
56
|
+
workLooks.row,
|
|
57
|
+
rail.leaving.has(column.id) && "kanban-rail-item-leaving",
|
|
58
|
+
)}
|
|
59
|
+
style:background="color-mix(in oklch, {hue}
|
|
60
|
+
{over ? 10 : 0}%, transparent)"
|
|
61
|
+
style:outline="1px solid color-mix(in oklch, {hue}
|
|
62
|
+
{over ? 28 : 0}%, transparent)"
|
|
63
|
+
ondragover={(e) => rail.onHiddenDragOver(e, column.id)}
|
|
64
|
+
ondragleave={rail.onHiddenDragLeave}
|
|
65
|
+
ondrop={(e) => rail.onHiddenDrop(e, column.id)}
|
|
66
|
+
>
|
|
85
67
|
<Button
|
|
86
68
|
variant="ghost"
|
|
87
|
-
size="
|
|
88
|
-
onclick={rail.
|
|
89
|
-
class="
|
|
69
|
+
size="lg"
|
|
70
|
+
onclick={() => rail.show(column.id)}
|
|
71
|
+
class="w-full justify-start gap-2 text-xs text-muted-foreground"
|
|
90
72
|
>
|
|
91
|
-
{
|
|
73
|
+
{#if hiddenItem}
|
|
74
|
+
{@render hiddenItem(column, { count, active: over })}
|
|
75
|
+
{:else}
|
|
76
|
+
<span class="truncate whitespace-nowrap">{column.label}</span>
|
|
77
|
+
{#if count > 0}
|
|
78
|
+
<span class="ms-auto text-xs text-muted-foreground/60 tabular-nums"
|
|
79
|
+
>{count.toLocaleString()}</span
|
|
80
|
+
>
|
|
81
|
+
{/if}
|
|
82
|
+
{/if}
|
|
92
83
|
</Button>
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
84
|
+
</div>
|
|
85
|
+
{/each}
|
|
86
|
+
|
|
87
|
+
{#if columns.length > 1}
|
|
88
|
+
<Button
|
|
89
|
+
variant="ghost"
|
|
90
|
+
size="sm"
|
|
91
|
+
onclick={rail.showAll}
|
|
92
|
+
class="mt-1 justify-start text-xs font-normal text-muted-foreground"
|
|
93
|
+
>
|
|
94
|
+
{showAllLabel}
|
|
95
|
+
</Button>
|
|
96
|
+
{/if}
|
|
97
|
+
</Collapsible.Content>
|
|
98
|
+
</Collapsible.Root>
|
|
97
99
|
|
|
98
100
|
<style>
|
|
99
101
|
/* Unlayered: the row's transition-[background-color,outline-color] utility would otherwise drop the fade out. */
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
import { createKanbanDnd } from "./use-kanban-dnd.svelte.js";
|
|
12
12
|
import { CaretRight } from "phosphor-svelte";
|
|
13
13
|
import { Button } from "../shadcn/button/index.js";
|
|
14
|
+
import * as Collapsible from "../shadcn/collapsible/index.js";
|
|
14
15
|
import GroupHead from "../components/work-parts/group-head.svelte";
|
|
15
16
|
import { workLooks } from "../components/work-parts/looks.js";
|
|
16
17
|
import type {
|
|
@@ -182,9 +183,10 @@
|
|
|
182
183
|
isDroppable: canDrop,
|
|
183
184
|
});
|
|
184
185
|
|
|
185
|
-
function
|
|
186
|
+
function setOpen(groupId: string, open: boolean): void {
|
|
187
|
+
if (open !== folded.has(groupId)) return;
|
|
186
188
|
const next = new Set(folded);
|
|
187
|
-
if (
|
|
189
|
+
if (open) next.delete(groupId);
|
|
188
190
|
else next.add(groupId);
|
|
189
191
|
folded = next;
|
|
190
192
|
onCollapsedChange?.([...next]);
|
|
@@ -255,134 +257,153 @@
|
|
|
255
257
|
{@const open = !folded.has(group.id)}
|
|
256
258
|
{@const hue = group.hue ?? "var(--color-primary)"}
|
|
257
259
|
{@const context = { count, active, open, depth, hasChildren: children.length > 0 }}
|
|
258
|
-
<
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
variant="ghost"
|
|
282
|
-
size="sm"
|
|
283
|
-
icon
|
|
284
|
-
aria-expanded={open}
|
|
285
|
-
aria-label="{open ? 'Fold' : 'Unfold'} {group.label}"
|
|
286
|
-
onclick={() => toggle(group.id)}
|
|
287
|
-
class="-ml-1.5 text-muted-foreground"
|
|
260
|
+
<Collapsible.Root bind:open={() => open, (next) => setOpen(group.id, next)}>
|
|
261
|
+
{#snippet child({ props })}
|
|
262
|
+
<section
|
|
263
|
+
{...props}
|
|
264
|
+
aria-label={group.ariaLabel ?? group.label}
|
|
265
|
+
class={cn(
|
|
266
|
+
"tl-group group/tl",
|
|
267
|
+
depth === 0 ? "border-b border-border last:border-b-0" : "border-t border-border/60",
|
|
268
|
+
)}
|
|
269
|
+
class:tl-group-active={active}
|
|
270
|
+
style:--tl-hue={hue}
|
|
271
|
+
ondragover={children.length ? undefined : (e) => dnd.onColumnDragOver(e, group.id)}
|
|
272
|
+
ondragleave={children.length ? undefined : dnd.onDragLeave}
|
|
273
|
+
ondrop={children.length ? undefined : (e) => dnd.onDrop(e, group.id)}
|
|
274
|
+
>
|
|
275
|
+
<div
|
|
276
|
+
class={cn(
|
|
277
|
+
"hover-row sticky z-10 flex h-10 items-center gap-2 px-3 backdrop-blur-sm",
|
|
278
|
+
flat ? "bg-card/90" : "bg-card/90",
|
|
279
|
+
depth === 0 && (head ? "top-9" : "top-0"),
|
|
280
|
+
)}
|
|
281
|
+
style:top={depth > 0 ? `${(head ? 2.25 : 0) + depth * 2.5}rem` : undefined}
|
|
282
|
+
style:padding-inline-start={depth > 0 ? `${0.75 + depth * 1.25}rem` : undefined}
|
|
288
283
|
>
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
<div class="tl-actions row-action ms-auto flex shrink-0 items-center gap-1">
|
|
311
|
-
{@render actions(group, context)}
|
|
312
|
-
</div>
|
|
313
|
-
{/if}
|
|
314
|
-
</div>
|
|
315
|
-
|
|
316
|
-
<!-- inert: a folded group's rows leave the Tab order and the accessibility tree. -->
|
|
317
|
-
<div class="tl-fold" class:tl-fold-open={open} inert={!open}>
|
|
318
|
-
<div class="tl-fold-inner">
|
|
319
|
-
<div role="list" aria-label={group.label} class="flex flex-col pb-1.5">
|
|
320
|
-
{#each rows as item, index (getId(item))}
|
|
321
|
-
{@const id = getId(item)}
|
|
322
|
-
{@const rowId = `${uid}-row-${encodeURIComponent(id)}`}
|
|
323
|
-
{@const dropBefore =
|
|
324
|
-
dnd.showIndicatorBefore(group.id, id) &&
|
|
325
|
-
!dnd.isDragSource(id) &&
|
|
326
|
-
idAt(rows, index - 1) !== dnd.draggedId}
|
|
327
|
-
<!-- WHY: a row is a list item that takes focus for the Alt+arrow move; making it a button would hide the list from screen readers. -->
|
|
328
|
-
<!-- svelte-ignore a11y_no_noninteractive_tabindex, a11y_no_noninteractive_element_interactions -->
|
|
329
|
-
<div
|
|
330
|
-
role="listitem"
|
|
331
|
-
tabindex={draggable ? 0 : undefined}
|
|
332
|
-
id={rowId}
|
|
333
|
-
data-task-row={id}
|
|
334
|
-
aria-label={getLabel?.(item)}
|
|
335
|
-
aria-labelledby={draggable && !getLabel ? rowId : undefined}
|
|
336
|
-
aria-describedby={draggable ? hintId : undefined}
|
|
337
|
-
aria-keyshortcuts={draggable ? "Alt+ArrowUp Alt+ArrowDown" : undefined}
|
|
338
|
-
draggable={draggable ? "true" : undefined}
|
|
339
|
-
animate:flip={{ duration: () => durationMs("fast"), easing: cubicOut }}
|
|
340
|
-
ondragstart={draggable
|
|
341
|
-
? (e) => {
|
|
342
|
-
liftDragImage(e);
|
|
343
|
-
dnd.onDragStart(e, id);
|
|
344
|
-
}
|
|
345
|
-
: undefined}
|
|
346
|
-
ondragend={draggable ? () => dnd.resetDrag() : undefined}
|
|
347
|
-
ondragover={draggable
|
|
348
|
-
? (e) => dnd.onItemDragOver(e, group.id, id, idAt(rows, index + 1))
|
|
349
|
-
: undefined}
|
|
350
|
-
onkeydown={draggable ? (e) => void onRowKeyDown(e, id) : undefined}
|
|
351
|
-
style:--tl-i={Math.min(index, STAGGER_CAP)}
|
|
352
|
-
class={cn(
|
|
353
|
-
"tl-row relative mx-1.5 outline-none focus-visible:shadow-(--ring-focus)",
|
|
354
|
-
workLooks.row,
|
|
355
|
-
workLooks.rowHeight,
|
|
356
|
-
draggable && "cursor-grab active:cursor-grabbing",
|
|
357
|
-
dropBefore && "tl-drop-before",
|
|
358
|
-
dnd.isDragSource(id) && "opacity-40",
|
|
359
|
-
)}
|
|
360
|
-
>
|
|
361
|
-
{@render renderRow(item, { groupId: group.id, index })}
|
|
362
|
-
</div>
|
|
363
|
-
{/each}
|
|
364
|
-
|
|
365
|
-
{#if dnd.showIndicatorEnd(group.id) && idAt(rows, rows.length - 1) !== dnd.draggedId}
|
|
366
|
-
<div class="tl-drop-end relative mx-1.5 h-2" aria-hidden="true"></div>
|
|
284
|
+
{#if collapsible}
|
|
285
|
+
<Collapsible.Trigger>
|
|
286
|
+
{#snippet child({ props })}
|
|
287
|
+
<Button
|
|
288
|
+
{...props}
|
|
289
|
+
variant="ghost"
|
|
290
|
+
size="sm"
|
|
291
|
+
icon
|
|
292
|
+
aria-label="{open ? 'Fold' : 'Unfold'} {group.label}"
|
|
293
|
+
class="-ml-1.5 text-muted-foreground"
|
|
294
|
+
>
|
|
295
|
+
<span
|
|
296
|
+
class="tl-chevron size-3.5 [&>svg]:size-full"
|
|
297
|
+
class:tl-chevron-open={open}
|
|
298
|
+
aria-hidden="true"
|
|
299
|
+
>
|
|
300
|
+
<CaretRight aria-hidden="true" weight="bold" />
|
|
301
|
+
</span>
|
|
302
|
+
</Button>
|
|
303
|
+
{/snippet}
|
|
304
|
+
</Collapsible.Trigger>
|
|
367
305
|
{/if}
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
306
|
+
{#if groupHeader}
|
|
307
|
+
{@render groupHeader(group, context)}
|
|
308
|
+
{:else}
|
|
309
|
+
<GroupHead
|
|
310
|
+
label={group.label}
|
|
311
|
+
{hue}
|
|
312
|
+
{count}
|
|
313
|
+
onAdd={onAdd ? () => onAdd(group.id) : undefined}
|
|
314
|
+
{addLabel}
|
|
315
|
+
/>
|
|
371
316
|
{/if}
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
</p>
|
|
317
|
+
{#if actions}
|
|
318
|
+
<div class="tl-actions row-action ms-auto flex shrink-0 items-center gap-1">
|
|
319
|
+
{@render actions(group, context)}
|
|
320
|
+
</div>
|
|
377
321
|
{/if}
|
|
378
322
|
</div>
|
|
379
323
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
324
|
+
<!-- inert: a folded group's rows leave the Tab order and the accessibility tree. -->
|
|
325
|
+
<Collapsible.Content forceMount>
|
|
326
|
+
{#snippet child({ props })}
|
|
327
|
+
<div {...props} class="tl-fold" class:tl-fold-open={open}>
|
|
328
|
+
<!-- On the inner box: beside the spread Svelte writes inert as an attribute, and
|
|
329
|
+
clearing it leaves the rows inert in the unit tests' DOM. -->
|
|
330
|
+
<div class="tl-fold-inner" inert={!open}>
|
|
331
|
+
<div role="list" aria-label={group.label} class="flex flex-col pb-1.5">
|
|
332
|
+
{#each rows as item, index (getId(item))}
|
|
333
|
+
{@const id = getId(item)}
|
|
334
|
+
{@const rowId = `${uid}-row-${encodeURIComponent(id)}`}
|
|
335
|
+
{@const dropBefore =
|
|
336
|
+
dnd.showIndicatorBefore(group.id, id) &&
|
|
337
|
+
!dnd.isDragSource(id) &&
|
|
338
|
+
idAt(rows, index - 1) !== dnd.draggedId}
|
|
339
|
+
<!-- WHY: a row is a list item that takes focus for the Alt+arrow move; making it a button would hide the list from screen readers. -->
|
|
340
|
+
<!-- svelte-ignore a11y_no_noninteractive_tabindex, a11y_no_noninteractive_element_interactions -->
|
|
341
|
+
<div
|
|
342
|
+
role="listitem"
|
|
343
|
+
tabindex={draggable ? 0 : undefined}
|
|
344
|
+
id={rowId}
|
|
345
|
+
data-task-row={id}
|
|
346
|
+
aria-label={getLabel?.(item)}
|
|
347
|
+
aria-labelledby={draggable && !getLabel ? rowId : undefined}
|
|
348
|
+
aria-describedby={draggable ? hintId : undefined}
|
|
349
|
+
aria-keyshortcuts={draggable ? "Alt+ArrowUp Alt+ArrowDown" : undefined}
|
|
350
|
+
draggable={draggable ? "true" : undefined}
|
|
351
|
+
animate:flip={{ duration: () => durationMs("fast"), easing: cubicOut }}
|
|
352
|
+
ondragstart={draggable
|
|
353
|
+
? (e) => {
|
|
354
|
+
liftDragImage(e);
|
|
355
|
+
dnd.onDragStart(e, id);
|
|
356
|
+
}
|
|
357
|
+
: undefined}
|
|
358
|
+
ondragend={draggable ? () => dnd.resetDrag() : undefined}
|
|
359
|
+
ondragover={draggable
|
|
360
|
+
? (e) => dnd.onItemDragOver(e, group.id, id, idAt(rows, index + 1))
|
|
361
|
+
: undefined}
|
|
362
|
+
onkeydown={draggable ? (e) => void onRowKeyDown(e, id) : undefined}
|
|
363
|
+
style:--tl-i={Math.min(index, STAGGER_CAP)}
|
|
364
|
+
class={cn(
|
|
365
|
+
"tl-row relative mx-1.5 outline-none focus-visible:shadow-(--ring-focus)",
|
|
366
|
+
workLooks.row,
|
|
367
|
+
workLooks.rowHeight,
|
|
368
|
+
draggable && "cursor-grab active:cursor-grabbing",
|
|
369
|
+
dropBefore && "tl-drop-before",
|
|
370
|
+
dnd.isDragSource(id) && "opacity-40",
|
|
371
|
+
)}
|
|
372
|
+
>
|
|
373
|
+
{@render renderRow(item, { groupId: group.id, index })}
|
|
374
|
+
</div>
|
|
375
|
+
{/each}
|
|
376
|
+
|
|
377
|
+
{#if dnd.showIndicatorEnd(group.id) && idAt(rows, rows.length - 1) !== dnd.draggedId}
|
|
378
|
+
<div class="tl-drop-end relative mx-1.5 h-2" aria-hidden="true"></div>
|
|
379
|
+
{/if}
|
|
380
|
+
|
|
381
|
+
{#if groupFooter}
|
|
382
|
+
{@render groupFooter(group, context)}
|
|
383
|
+
{/if}
|
|
384
|
+
|
|
385
|
+
{#if rows.length === 0 && children.length === 0 && !active}
|
|
386
|
+
<p
|
|
387
|
+
class={cn(
|
|
388
|
+
workLooks.rowHole,
|
|
389
|
+
"mx-1.5 px-3 py-3 text-xs text-muted-foreground",
|
|
390
|
+
)}
|
|
391
|
+
>
|
|
392
|
+
{emptyLabel}
|
|
393
|
+
</p>
|
|
394
|
+
{/if}
|
|
395
|
+
</div>
|
|
396
|
+
|
|
397
|
+
{#each children as sub (sub.id)}
|
|
398
|
+
{@render section(sub, depth + 1)}
|
|
399
|
+
{/each}
|
|
400
|
+
</div>
|
|
401
|
+
</div>
|
|
402
|
+
{/snippet}
|
|
403
|
+
</Collapsible.Content>
|
|
404
|
+
</section>
|
|
405
|
+
{/snippet}
|
|
406
|
+
</Collapsible.Root>
|
|
386
407
|
{/snippet}
|
|
387
408
|
|
|
388
409
|
<div
|
|
@@ -54,9 +54,12 @@ async function settle() {
|
|
|
54
54
|
flushSync();
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
//
|
|
57
|
+
// jsdom doesn't reflect inert between property and attribute, and Svelte sets the attribute
|
|
58
|
+
// when the element also takes a spread (the Collapsible's props), so read both.
|
|
58
59
|
function insideInert(el: HTMLElement | null): boolean {
|
|
59
|
-
for (let node = el; node; node = node.parentElement)
|
|
60
|
+
for (let node = el; node; node = node.parentElement) {
|
|
61
|
+
if (node.inert || node.hasAttribute("inert")) return true;
|
|
62
|
+
}
|
|
60
63
|
return false;
|
|
61
64
|
}
|
|
62
65
|
|