@heyramzi/ui 2.20.0 → 2.21.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/README.md +2 -0
- package/dist/charts/dashboard/data-table.js +1 -2
- package/dist/components/list-page/filter-section.js +1 -1
- package/dist/components/list-page/list-table.js +5 -6
- package/dist/components/list-page/queue-rail.js +2 -1
- package/dist/components/picker/trigger.js +3 -3
- package/dist/components/segmented/segmented.js +5 -5
- package/dist/components/work-parts/looks.js +1 -1
- package/dist/shadcn/dialog/index.js +3 -2
- package/dist/shadcn/dropdown-menu/menu-item.js +1 -1
- package/dist/shadcn/sheet/index.js +3 -2
- package/dist/shadcn/skeleton/index.js +3 -2
- package/dist/status/index.js +1 -0
- package/dist/work/kanban-drag.js +13 -7
- package/package.json +1 -1
- package/src/ai/confirmation/ConfirmationTitle.svelte +1 -4
- package/src/ai/loading-state/PixelLoader.svelte +1 -1
- package/src/animations/dropdown.css +19 -10
- package/src/animations/modal.css +7 -2
- package/src/charts/charts.css +3 -5
- package/src/charts/dashboard/dashboard-section.svelte +1 -1
- package/src/charts/dashboard/data-table.ts +1 -2
- package/src/charts/dashboard/stat-tile.svelte +1 -1
- package/src/charts/number-chart.css +8 -7
- package/src/charts/pop-value.svelte +68 -0
- package/src/charts/primitives/chart-tooltip.svelte +1 -1
- package/src/charts/reference-line.css +1 -1
- package/src/charts/stat-card.css +4 -4
- package/src/components/copy/copy-button.svelte +11 -10
- package/src/components/data-table/row-arrow.svelte +1 -1
- package/src/components/data-table/row-context-menu.svelte +1 -1
- package/src/components/data-table/sortable-head.svelte +2 -2
- package/src/components/data-table/table-skeleton.svelte +5 -5
- package/src/components/docs/doc-page.svelte +1 -3
- package/src/components/docs/on-this-page.svelte +4 -4
- package/src/components/field-row/field-row.svelte +1 -1
- package/src/components/list-page/detail-empty.svelte +3 -8
- package/src/components/list-page/detail-header.svelte +1 -1
- package/src/components/list-page/detail-row.svelte +2 -1
- package/src/components/list-page/empty-state.svelte +15 -7
- package/src/components/list-page/filter-item.svelte +2 -2
- package/src/components/list-page/filter-popover.svelte +1 -1
- package/src/components/list-page/filter-section.ts +1 -4
- package/src/components/list-page/grouped-list.svelte +37 -8
- package/src/components/list-page/list-page-shell.svelte +1 -1
- package/src/components/list-page/list-table.ts +6 -6
- package/src/components/list-page/master-detail.svelte +1 -1
- package/src/components/list-page/queue-rail.ts +2 -2
- package/src/components/list-page/rail-empty.svelte +1 -1
- package/src/components/list-page/rail-row.svelte +5 -5
- package/src/components/open-in/open-in.svelte +2 -1
- package/src/components/picker/trigger.ts +6 -5
- package/src/components/section-card/section-card.svelte +1 -1
- package/src/components/section-header/section-header.svelte +4 -2
- package/src/components/section-header/section-label.svelte +2 -2
- package/src/components/segmented/animated-tabs.svelte +1 -1
- package/src/components/segmented/section-runs.svelte +1 -1
- package/src/components/segmented/segment.svelte +4 -6
- package/src/components/segmented/segmented-track.svelte +18 -52
- package/src/components/segmented/segmented.ts +5 -5
- package/src/components/table-view/table-view.svelte +43 -24
- package/src/components/tags-input/tags-input.svelte +17 -31
- package/src/components/work-parts/looks.ts +1 -1
- package/src/editor/content.css +3 -3
- package/src/editor/editor.svelte +1 -1
- package/src/inbox/thread-inbox.svelte +2 -9
- package/src/panel/side-panel.svelte +9 -4
- package/src/record/info-list.svelte +7 -8
- package/src/record/project-list.svelte +1 -1
- package/src/record/record-demo.svelte +2 -4
- package/src/record/record-shell.svelte +1 -1
- package/src/record/record-stats.svelte +9 -3
- package/src/record/steps-rail.svelte +7 -11
- package/src/shadcn/accordion/accordion-trigger.svelte +1 -1
- package/src/shadcn/alert-dialog/alert-dialog-content.svelte +5 -1
- package/src/shadcn/alert-dialog/alert-dialog-footer.svelte +12 -2
- package/src/shadcn/alert-dialog/alert-dialog-header.svelte +1 -1
- package/src/shadcn/alert-dialog/alert-dialog-title.svelte +1 -1
- package/src/shadcn/badge/badge.svelte +3 -1
- package/src/shadcn/bubble/bubble-content.svelte +1 -1
- package/src/shadcn/bubble/bubble.svelte +1 -1
- package/src/shadcn/button/button.svelte +11 -5
- package/src/shadcn/calendar/calendar-month-select.svelte +1 -1
- package/src/shadcn/calendar/calendar-year-select.svelte +1 -1
- package/src/shadcn/calendar/calendar.svelte +1 -1
- package/src/shadcn/card/card.svelte +2 -1
- package/src/shadcn/checkbox/checkbox.svelte +1 -1
- package/src/shadcn/code-block/code-block-actions.svelte +1 -1
- package/src/shadcn/command/command-dialog.svelte +2 -2
- package/src/shadcn/command/command-group.svelte +1 -1
- package/src/shadcn/command/command-input.svelte +1 -1
- package/src/shadcn/command/command-item.svelte +1 -1
- package/src/shadcn/dialog/dialog-body.svelte +22 -0
- package/src/shadcn/dialog/dialog-content.svelte +19 -4
- package/src/shadcn/dialog/dialog-footer.svelte +19 -1
- package/src/shadcn/dialog/dialog-header.svelte +6 -1
- package/src/shadcn/dialog/dialog-title.svelte +2 -1
- package/src/shadcn/dialog/index.ts +3 -0
- package/src/shadcn/dropdown-menu/dropdown-menu-content.svelte +5 -1
- package/src/shadcn/dropdown-menu/dropdown-menu-sub-content.svelte +1 -1
- package/src/shadcn/dropdown-menu/menu-item.ts +1 -1
- package/src/shadcn/field/field-separator.svelte +1 -1
- package/src/shadcn/hover-card/hover-card-content.svelte +1 -1
- package/src/shadcn/input/input.svelte +2 -2
- package/src/shadcn/input-otp/input-otp-slot.svelte +1 -1
- package/src/shadcn/message-scroller/message-scroller-button.svelte +1 -1
- package/src/shadcn/popover/popover-content.svelte +1 -1
- package/src/shadcn/radio-group/radio-group-item.svelte +1 -1
- package/src/shadcn/scroll-area/scroll-area.svelte +1 -1
- package/src/shadcn/select/select-content.svelte +6 -2
- package/src/shadcn/select/select-item.svelte +1 -1
- package/src/shadcn/select/select-trigger.svelte +1 -1
- package/src/shadcn/sheet/index.ts +3 -0
- package/src/shadcn/sheet/sheet-body.svelte +22 -0
- package/src/shadcn/sheet/sheet-content.svelte +27 -5
- package/src/shadcn/sheet/sheet-footer.svelte +14 -2
- package/src/shadcn/sheet/sheet-header.svelte +1 -1
- package/src/shadcn/sheet/sheet-title.svelte +1 -1
- package/src/shadcn/sheet/sheet.css +11 -5
- package/src/shadcn/sidebar/sidebar-group-action.svelte +1 -1
- package/src/shadcn/sidebar/sidebar-group-label.svelte +1 -1
- package/src/shadcn/sidebar/sidebar-inset.svelte +1 -1
- package/src/shadcn/sidebar/sidebar-menu-action.svelte +1 -1
- package/src/shadcn/sidebar/sidebar-menu-button.svelte +2 -2
- package/src/shadcn/sidebar/sidebar-menu-sub-button.svelte +1 -1
- package/src/shadcn/sidebar/sidebar-menu.svelte +1 -1
- package/src/shadcn/sidebar/sidebar.svelte +1 -1
- package/src/shadcn/skeleton/index.ts +3 -0
- package/src/shadcn/skeleton/skeleton-delay.svelte +63 -0
- package/src/shadcn/skeleton/skeleton-delay.svelte.test.ts +57 -0
- package/src/shadcn/skeleton/skeleton.svelte +5 -1
- package/src/shadcn/slider/slider.svelte +1 -1
- package/src/shadcn/switch/switch.svelte +2 -2
- package/src/shadcn/table/table-row.svelte +1 -1
- package/src/shadcn/tabs/tabs-list.svelte +1 -1
- package/src/shadcn/tabs/tabs-trigger.svelte +2 -1
- package/src/shadcn/textarea/textarea.svelte +1 -1
- package/src/shadcn/toggle/toggle.svelte +2 -2
- package/src/shadcn/toggle-group/toggle-group.svelte +1 -1
- package/src/shadcn/tooltip/tooltip-content.svelte +1 -1
- package/src/shadcn/trace/trace-collapsible-trigger.svelte +1 -1
- package/src/shadcn/trace/trace.svelte +1 -1
- package/src/status/README.md +1 -1
- package/src/status/index.ts +1 -0
- package/src/status/status-badge.svelte +21 -7
- package/src/status/status-dot.svelte +6 -11
- package/src/status/status-pill.svelte +50 -0
- package/src/thread/thread.svelte +54 -136
- package/src/tokens/craft.css +55 -36
- package/src/tokens/palette.css +16 -9
- package/src/work/demo.svelte +48 -42
- package/src/work/detail/detail-demo.svelte +33 -24
- package/src/work/detail/task-chip.svelte +4 -4
- package/src/work/detail/task-detail.svelte +41 -108
- package/src/work/detail/task-property.svelte +7 -13
- package/src/work/detail/task-thread.svelte +48 -119
- package/src/work/docs/doc-editor-page.svelte +5 -4
- package/src/work/docs/doc-menu.svelte +9 -4
- package/src/work/docs/docs-demo.svelte +4 -4
- package/src/work/docs/edited-chip.svelte +11 -4
- package/src/work/gantt/gantt-bar.svelte +18 -14
- package/src/work/gantt/gantt-grid.svelte +7 -7
- package/src/work/gantt/gantt-group-row.svelte +7 -10
- package/src/work/gantt/gantt-header.svelte +3 -1
- package/src/work/gantt/gantt-task-row.svelte +19 -9
- package/src/work/gantt/gantt.css +4 -5
- package/src/work/inbox/inbox-card.svelte +32 -10
- package/src/work/inbox/inbox-draft.svelte +2 -2
- package/src/work/kanban-column.svelte +7 -15
- package/src/work/kanban-drag.ts +13 -8
- package/src/work/task-gantt.svelte +2 -1
- package/src/work/task-list.svelte +54 -53
- package/src/work/time/time-chip.svelte +16 -19
- package/src/work/time/time-dot.svelte +2 -1
- package/src/work/time/time-ring.svelte +2 -3
- package/src/work/time/time-tracker.svelte +72 -166
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
<script lang="ts" generics="T">
|
|
2
|
+
import { ganttLooks } from "../../components/work-parts/looks.js";
|
|
3
|
+
import { Button } from "../../shadcn/button/index.js";
|
|
4
|
+
import { cn } from "../../lib/utils.js";
|
|
2
5
|
import type { Snippet } from "svelte";
|
|
3
6
|
import { CaretRight, FlagPennant } from "phosphor-svelte";
|
|
4
7
|
import type { GanttChange, GanttLabelContext } from "../task-types.js";
|
|
@@ -79,12 +82,14 @@
|
|
|
79
82
|
>
|
|
80
83
|
{#if nested}
|
|
81
84
|
{#if row.hasChildren}
|
|
82
|
-
<
|
|
83
|
-
|
|
85
|
+
<Button
|
|
86
|
+
variant="ghost"
|
|
87
|
+
size="sm"
|
|
88
|
+
icon
|
|
84
89
|
aria-expanded={row.open}
|
|
85
90
|
aria-label="{row.open ? 'Fold' : 'Unfold'} {title}"
|
|
86
91
|
onclick={() => onToggle(row.id)}
|
|
87
|
-
class="-ml-1
|
|
92
|
+
class="-ml-1.5 size-6 text-muted-foreground"
|
|
88
93
|
>
|
|
89
94
|
<span
|
|
90
95
|
class="g-chevron size-3.5 [&>svg]:size-full"
|
|
@@ -93,7 +98,7 @@
|
|
|
93
98
|
>
|
|
94
99
|
<CaretRight weight="bold" />
|
|
95
100
|
</span>
|
|
96
|
-
</
|
|
101
|
+
</Button>
|
|
97
102
|
{:else}
|
|
98
103
|
<span class="-ml-1 size-5 shrink-0" aria-hidden="true"></span>
|
|
99
104
|
{/if}
|
|
@@ -117,7 +122,10 @@
|
|
|
117
122
|
type="button"
|
|
118
123
|
aria-label="{title}, {describe(span)}"
|
|
119
124
|
title="{title}, {describe(span)}"
|
|
120
|
-
class=
|
|
125
|
+
class={cn(
|
|
126
|
+
"g-flag absolute top-1/2 grid size-6 -translate-x-1/2 -translate-y-1/2 place-items-center outline-none focus-visible:shadow-(--ring-focus)",
|
|
127
|
+
ganttLooks.mark,
|
|
128
|
+
)}
|
|
121
129
|
style:--g-hue={hue}
|
|
122
130
|
style:left="{dayX(layout, span.end) + layout.dayWidth / 2}px"
|
|
123
131
|
onclick={() => onSelect?.(row.id)}
|
|
@@ -135,7 +143,10 @@
|
|
|
135
143
|
tabindex="0"
|
|
136
144
|
aria-label="{title}, {describe(span)}"
|
|
137
145
|
title="{title}, {describe(span)}"
|
|
138
|
-
class=
|
|
146
|
+
class={cn(
|
|
147
|
+
"g-derived g-enter absolute top-1/2 -translate-y-1/2 outline-none focus-visible:shadow-(--ring-focus)",
|
|
148
|
+
ganttLooks.mark,
|
|
149
|
+
)}
|
|
139
150
|
style:--g-hue={hue}
|
|
140
151
|
style:--g-i={Math.min(row.order, staggerCap)}
|
|
141
152
|
style:left="{dayX(layout, span.start) + layout.inset}px"
|
|
@@ -172,7 +183,7 @@
|
|
|
172
183
|
|
|
173
184
|
<style>
|
|
174
185
|
.g-row {
|
|
175
|
-
transition: background-color var(--duration-quick
|
|
186
|
+
transition: background-color var(--duration-quick) var(--ease-smooth-out);
|
|
176
187
|
}
|
|
177
188
|
|
|
178
189
|
.g-row-hot {
|
|
@@ -180,8 +191,7 @@
|
|
|
180
191
|
}
|
|
181
192
|
|
|
182
193
|
.g-chevron {
|
|
183
|
-
transition: rotate var(--duration-fast
|
|
184
|
-
var(--ease-smooth-out, cubic-bezier(0.22, 1, 0.36, 1));
|
|
194
|
+
transition: rotate var(--duration-fast) var(--ease-smooth-out);
|
|
185
195
|
}
|
|
186
196
|
|
|
187
197
|
.g-chevron-open {
|
package/src/work/gantt/gantt.css
CHANGED
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
/* Shared by the bar, the derived bar and the group summary: they all slide in the same way. */
|
|
2
2
|
.g-enter {
|
|
3
|
-
animation: g-bar-in var(--duration-slow
|
|
4
|
-
|
|
5
|
-
animation-delay: calc(var(--g-i, 0) * var(--duration-stagger, 40ms));
|
|
3
|
+
animation: g-bar-in var(--duration-slow) var(--ease-smooth-out) backwards;
|
|
4
|
+
animation-delay: calc(var(--g-i, 0) * var(--duration-stagger));
|
|
6
5
|
}
|
|
7
6
|
|
|
8
7
|
@keyframes g-bar-in {
|
|
9
8
|
from {
|
|
10
9
|
opacity: 0;
|
|
11
|
-
clip-path: inset(0 100% 0 0 round
|
|
10
|
+
clip-path: inset(0 100% 0 0 round var(--radius-lg));
|
|
12
11
|
}
|
|
13
12
|
to {
|
|
14
13
|
opacity: 1;
|
|
15
|
-
clip-path: inset(0 0 0 0 round
|
|
14
|
+
clip-path: inset(0 0 0 0 round var(--radius-lg));
|
|
16
15
|
}
|
|
17
16
|
}
|
|
18
17
|
|
|
@@ -26,6 +26,9 @@
|
|
|
26
26
|
inboxStates,
|
|
27
27
|
} from "./inbox-statuses.js";
|
|
28
28
|
import InboxTraceView from "./inbox-trace.svelte";
|
|
29
|
+
import { TagChip } from "../../components/chip/index.js";
|
|
30
|
+
import { workLooks } from "../../components/work-parts/looks.js";
|
|
31
|
+
import { FolderSimple } from "phosphor-svelte";
|
|
29
32
|
import type { InboxDraftEdits, InboxItem, InboxVerdict } from "./inbox-types.js";
|
|
30
33
|
|
|
31
34
|
interface Props {
|
|
@@ -173,8 +176,9 @@
|
|
|
173
176
|
);
|
|
174
177
|
</script>
|
|
175
178
|
|
|
176
|
-
<!-- WHY a
|
|
177
|
-
|
|
179
|
+
<!-- WHY a card only while it waits: a decided or automatic item is a line in a log, so it
|
|
180
|
+
drops to the grey ground and the queue stays scannable. The card step draws its own
|
|
181
|
+
hairline, so a border on top of it would show a 2px edge. -->
|
|
178
182
|
<!-- WHY a focusable article: j and k move focus card to card, and a screen reader
|
|
179
183
|
reads the card as one labelled unit. -->
|
|
180
184
|
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
|
@@ -186,12 +190,10 @@
|
|
|
186
190
|
onfocusin={onselect}
|
|
187
191
|
onpointerdown={onselect}
|
|
188
192
|
class={cn(
|
|
189
|
-
"flex flex-col gap-3
|
|
190
|
-
"focus-visible:ring-
|
|
191
|
-
open
|
|
192
|
-
|
|
193
|
-
: "bg-muted/30 border border-transparent",
|
|
194
|
-
selected && open && "border-ring",
|
|
193
|
+
"flex flex-col gap-3 p-3 outline-none transition-[background-color,box-shadow] duration-quick ease-smooth-out motion-reduce:transition-none",
|
|
194
|
+
"focus-visible:shadow-(--ring-focus)",
|
|
195
|
+
open ? workLooks.panel : cn(workLooks.card, "bg-muted/30"),
|
|
196
|
+
selected && open && "ring-ring ring-1",
|
|
195
197
|
)}
|
|
196
198
|
>
|
|
197
199
|
<header class="flex items-start gap-2.5">
|
|
@@ -214,7 +216,7 @@
|
|
|
214
216
|
{#if facts.command}
|
|
215
217
|
<div class="flex flex-col items-start gap-1">
|
|
216
218
|
<!-- The padding sits on a wrapper: on the clamped box itself, the 4th line showed through it. -->
|
|
217
|
-
<div class="
|
|
219
|
+
<div class={cn(workLooks.inset, "w-full px-2.5 py-2")}>
|
|
218
220
|
<pre
|
|
219
221
|
class={cn(
|
|
220
222
|
"text-foreground font-mono text-xs leading-5 break-words whitespace-pre-wrap",
|
|
@@ -263,6 +265,26 @@
|
|
|
263
265
|
{SOURCE_LABEL[item.trace.source.type]}
|
|
264
266
|
</span>
|
|
265
267
|
{/if}
|
|
268
|
+
<span class={FACT}>
|
|
269
|
+
<!-- The linked record is a chip inside the line, a link when the project has a page. -->
|
|
270
|
+
{#if item.project.href}
|
|
271
|
+
<a
|
|
272
|
+
href={item.project.href}
|
|
273
|
+
class={workLooks.chipLink}
|
|
274
|
+
aria-label="Project: {item.project.name}"
|
|
275
|
+
>
|
|
276
|
+
<TagChip class="hover:bg-muted">
|
|
277
|
+
{#snippet icon()}<FolderSimple aria-hidden="true" />{/snippet}
|
|
278
|
+
{item.project.name}
|
|
279
|
+
</TagChip>
|
|
280
|
+
</a>
|
|
281
|
+
{:else}
|
|
282
|
+
<TagChip>
|
|
283
|
+
{#snippet icon()}<FolderSimple aria-hidden="true" />{/snippet}
|
|
284
|
+
{item.project.name}
|
|
285
|
+
</TagChip>
|
|
286
|
+
{/if}
|
|
287
|
+
</span>
|
|
266
288
|
{#if time}
|
|
267
289
|
<span class={cn(FACT, "tabular-nums")}>{time}</span>
|
|
268
290
|
{/if}
|
|
@@ -323,7 +345,7 @@
|
|
|
323
345
|
onkeydown={onNoteKeydown}
|
|
324
346
|
placeholder="Tell the agent what to do instead"
|
|
325
347
|
aria-label="Note for the agent"
|
|
326
|
-
class="bg-
|
|
348
|
+
class="bg-card min-h-20 text-sm"
|
|
327
349
|
/>
|
|
328
350
|
<div class="flex flex-wrap items-center gap-2">
|
|
329
351
|
<Button size="sm" onclick={submitSteer} disabled={!note.trim()}>Send to agent</Button>
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
oninput={(event) => write({ subject: event.currentTarget.value })}
|
|
69
69
|
aria-label="Subject"
|
|
70
70
|
{disabled}
|
|
71
|
-
class="bg-
|
|
71
|
+
class="bg-card"
|
|
72
72
|
/>
|
|
73
73
|
<Textarea
|
|
74
74
|
bind:ref={bodyEl}
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
oninput={(event) => write({ body: event.currentTarget.value })}
|
|
77
77
|
aria-label="Email body"
|
|
78
78
|
{disabled}
|
|
79
|
-
class="bg-
|
|
79
|
+
class="bg-card min-h-28 text-sm"
|
|
80
80
|
/>
|
|
81
81
|
<p class="text-muted-foreground text-xs" aria-live="polite">
|
|
82
82
|
{edited ? "Edited by you. Send uses your text." : "The agent's draft. Edit it before you send."}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import type { Snippet } from "svelte";
|
|
3
3
|
import type { HTMLAttributes } from "svelte/elements";
|
|
4
4
|
import { cn } from "../lib/utils.js";
|
|
5
|
+
import WorkWell from "../components/work-parts/work-well.svelte";
|
|
5
6
|
|
|
6
7
|
interface Props extends HTMLAttributes<HTMLDivElement> {
|
|
7
8
|
/** Any CSS color. Only shows while a card is held over the column. */
|
|
@@ -34,13 +35,6 @@
|
|
|
34
35
|
bodyClass,
|
|
35
36
|
...restProps
|
|
36
37
|
}: Props = $props();
|
|
37
|
-
|
|
38
|
-
// A column rests neutral. Its hue shows only while a card is held over it.
|
|
39
|
-
let style = $derived(
|
|
40
|
-
hue && active
|
|
41
|
-
? `background: color-mix(in oklch, ${hue} 8%, var(--color-muted, transparent)); outline: 1px solid color-mix(in oklch, ${hue} 30%, transparent);`
|
|
42
|
-
: undefined,
|
|
43
|
-
);
|
|
44
38
|
</script>
|
|
45
39
|
|
|
46
40
|
<div
|
|
@@ -53,14 +47,12 @@
|
|
|
53
47
|
className,
|
|
54
48
|
)}
|
|
55
49
|
>
|
|
56
|
-
<
|
|
50
|
+
<WorkWell
|
|
57
51
|
role="region"
|
|
58
52
|
aria-label={ariaLabel}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
)}
|
|
63
|
-
{style}
|
|
53
|
+
{hue}
|
|
54
|
+
{active}
|
|
55
|
+
class={cn("flex-1", bodyClass)}
|
|
64
56
|
{ondragover}
|
|
65
57
|
{ondragleave}
|
|
66
58
|
{ondrop}
|
|
@@ -69,7 +61,7 @@
|
|
|
69
61
|
{@render header()}
|
|
70
62
|
</div>
|
|
71
63
|
{@render children()}
|
|
72
|
-
</
|
|
64
|
+
</WorkWell>
|
|
73
65
|
</div>
|
|
74
66
|
|
|
75
67
|
<style>
|
|
@@ -79,7 +71,7 @@
|
|
|
79
71
|
flex: 1 1 0;
|
|
80
72
|
min-width: var(--kanban-column-min, 280px);
|
|
81
73
|
max-width: var(--kanban-column-max, 420px);
|
|
82
|
-
transition: opacity var(--duration-quick
|
|
74
|
+
transition: opacity var(--duration-quick) var(--ease-smooth-out);
|
|
83
75
|
}
|
|
84
76
|
|
|
85
77
|
/* A board under 40rem wide (a phone, a side panel) shows one column and a peek of the next, as 82vw did on a phone. */
|
package/src/work/kanban-drag.ts
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
const FRAME_PAD =
|
|
1
|
+
const FRAME_PAD = 24;
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Native drag shows a flat, faded snapshot. This hands the browser a
|
|
5
|
-
*
|
|
4
|
+
* Native drag shows a flat, faded snapshot. This hands the browser a lifted copy instead: the
|
|
5
|
+
* raised elevation step and a 2% grow, so the held card reads as picked up. The browser moves the
|
|
6
|
+
* image with the pointer itself, so there's no transition while it moves. Call it from
|
|
7
|
+
* `ondragstart`. `tilt` rotates the copy in degrees, 0 by default.
|
|
6
8
|
*/
|
|
7
|
-
export function liftDragImage(e: DragEvent, tilt =
|
|
9
|
+
export function liftDragImage(e: DragEvent, tilt = 0): void {
|
|
8
10
|
const source = e.currentTarget as HTMLElement | null;
|
|
9
11
|
if (!source || !e.dataTransfer) return;
|
|
10
12
|
const rect = source.getBoundingClientRect();
|
|
11
13
|
|
|
12
|
-
// The padded frame leaves room for the
|
|
14
|
+
// The padded frame leaves room for the shadow and the grow, or the snapshot crops them.
|
|
13
15
|
const frame = document.createElement("div");
|
|
14
16
|
Object.assign(frame.style, {
|
|
15
17
|
position: "fixed",
|
|
@@ -20,13 +22,16 @@ export function liftDragImage(e: DragEvent, tilt = -3): void {
|
|
|
20
22
|
});
|
|
21
23
|
|
|
22
24
|
const ghost = source.cloneNode(true) as HTMLElement;
|
|
25
|
+
// The copy's first child is the card the consumer drew, so the shadow sits on its corners.
|
|
26
|
+
const card = (ghost.firstElementChild as HTMLElement | null) ?? ghost;
|
|
23
27
|
Object.assign(ghost.style, {
|
|
24
28
|
width: `${rect.width}px`,
|
|
25
29
|
opacity: "1",
|
|
26
|
-
rotate: `${tilt}deg
|
|
27
|
-
|
|
28
|
-
|
|
30
|
+
rotate: tilt ? `${tilt}deg` : "",
|
|
31
|
+
// 2 minus the tooltip's shrink step is a 1.02 grow, kept on the motion scale.
|
|
32
|
+
scale: "calc(2 - var(--scale-small))",
|
|
29
33
|
});
|
|
34
|
+
card.style.boxShadow = "var(--elev-raised)";
|
|
30
35
|
frame.appendChild(ghost);
|
|
31
36
|
document.body.appendChild(frame);
|
|
32
37
|
e.dataTransfer.setDragImage(
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script lang="ts" generics="T">
|
|
2
|
+
import { workLooks } from "../components/work-parts/looks.js";
|
|
2
3
|
import type { Snippet } from "svelte";
|
|
3
4
|
import { cn } from "../lib/utils.js";
|
|
4
5
|
import { dayNumber, isoFromDay, todayIso } from "./dates.js";
|
|
@@ -193,7 +194,7 @@
|
|
|
193
194
|
bind:this={scroller}
|
|
194
195
|
class={cn(
|
|
195
196
|
"task-gantt relative min-h-0 flex-1 overflow-auto overscroll-x-contain",
|
|
196
|
-
flat ? "bg-
|
|
197
|
+
flat ? "bg-card" : workLooks.panel,
|
|
197
198
|
drag && "select-none",
|
|
198
199
|
className,
|
|
199
200
|
)}
|
|
@@ -2,11 +2,15 @@
|
|
|
2
2
|
import type { Snippet } from "svelte";
|
|
3
3
|
import { flip } from "svelte/animate";
|
|
4
4
|
import { cubicOut } from "svelte/easing";
|
|
5
|
+
import { durationMs } from "../lib/motion.js";
|
|
5
6
|
import { liftDragImage } from "./kanban-drag.js";
|
|
6
7
|
import { boardOrderAt } from "./board-order.js";
|
|
7
8
|
import { cn } from "../lib/utils.js";
|
|
8
9
|
import { createKanbanDnd } from "./use-kanban-dnd.svelte.js";
|
|
9
10
|
import { CaretRight } from "phosphor-svelte";
|
|
11
|
+
import { Button } from "../shadcn/button/index.js";
|
|
12
|
+
import GroupHead from "../components/work-parts/group-head.svelte";
|
|
13
|
+
import { workLooks } from "../components/work-parts/looks.js";
|
|
10
14
|
import type {
|
|
11
15
|
KanbanColumn as KanbanColumnDef,
|
|
12
16
|
KanbanMove,
|
|
@@ -55,6 +59,10 @@
|
|
|
55
59
|
/** Group ids that start folded. */
|
|
56
60
|
collapsed?: string[];
|
|
57
61
|
emptyLabel?: string;
|
|
62
|
+
/** Shows a plus in each default group head that adds a row to that group. */
|
|
63
|
+
onAdd?: (groupId: string) => void;
|
|
64
|
+
/** The plus button's accessible name, followed by the group label. */
|
|
65
|
+
addLabel?: string;
|
|
58
66
|
isDroppable?: (groupId: string) => boolean;
|
|
59
67
|
/** Drops the card chrome (border, radius, fill) so the list sits flat on the page. Group dividers stay. */
|
|
60
68
|
flat?: boolean;
|
|
@@ -83,6 +91,8 @@
|
|
|
83
91
|
collapsible = true,
|
|
84
92
|
collapsed = [],
|
|
85
93
|
emptyLabel = "Nothing here yet",
|
|
94
|
+
onAdd,
|
|
95
|
+
addLabel = "Add a task",
|
|
86
96
|
isDroppable,
|
|
87
97
|
flat = false,
|
|
88
98
|
class: className,
|
|
@@ -222,20 +232,22 @@
|
|
|
222
232
|
>
|
|
223
233
|
<div
|
|
224
234
|
class={cn(
|
|
225
|
-
"sticky z-10 flex h-10 items-center gap-2 px-3 backdrop-blur-sm",
|
|
226
|
-
flat ? "bg-
|
|
235
|
+
"hover-row sticky z-10 flex h-10 items-center gap-2 px-3 backdrop-blur-sm",
|
|
236
|
+
flat ? "bg-card/90" : "bg-card/90",
|
|
227
237
|
depth === 0 && (head ? "top-9" : "top-0"),
|
|
228
238
|
)}
|
|
229
239
|
style:top={depth > 0 ? `${(head ? 2.25 : 0) + depth * 2.5}rem` : undefined}
|
|
230
240
|
style:padding-inline-start={depth > 0 ? `${0.75 + depth * 1.25}rem` : undefined}
|
|
231
241
|
>
|
|
232
242
|
{#if collapsible}
|
|
233
|
-
<
|
|
234
|
-
|
|
243
|
+
<Button
|
|
244
|
+
variant="ghost"
|
|
245
|
+
size="sm"
|
|
246
|
+
icon
|
|
235
247
|
aria-expanded={open}
|
|
236
248
|
aria-label="{open ? 'Fold' : 'Unfold'} {group.label}"
|
|
237
249
|
onclick={() => toggle(group.id)}
|
|
238
|
-
class="-ml-1
|
|
250
|
+
class="-ml-1.5 text-muted-foreground"
|
|
239
251
|
>
|
|
240
252
|
<span
|
|
241
253
|
class="tl-chevron size-3.5 [&>svg]:size-full"
|
|
@@ -244,23 +256,21 @@
|
|
|
244
256
|
>
|
|
245
257
|
<CaretRight weight="bold" />
|
|
246
258
|
</span>
|
|
247
|
-
</
|
|
259
|
+
</Button>
|
|
248
260
|
{/if}
|
|
249
261
|
{#if groupHeader}
|
|
250
262
|
{@render groupHeader(group, context)}
|
|
251
263
|
{:else}
|
|
252
|
-
<
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
{
|
|
258
|
-
|
|
264
|
+
<GroupHead
|
|
265
|
+
label={group.label}
|
|
266
|
+
{hue}
|
|
267
|
+
{count}
|
|
268
|
+
onAdd={onAdd ? () => onAdd(group.id) : undefined}
|
|
269
|
+
{addLabel}
|
|
270
|
+
/>
|
|
259
271
|
{/if}
|
|
260
272
|
{#if actions}
|
|
261
|
-
<div
|
|
262
|
-
class="tl-actions ms-auto flex shrink-0 items-center gap-1 opacity-0 transition-opacity duration-[var(--duration-quick,150ms)] focus-within:opacity-100 any-pointer-coarse:opacity-100"
|
|
263
|
-
>
|
|
273
|
+
<div class="tl-actions row-action ms-auto flex shrink-0 items-center gap-1">
|
|
264
274
|
{@render actions(group, context)}
|
|
265
275
|
</div>
|
|
266
276
|
{/if}
|
|
@@ -286,10 +296,10 @@
|
|
|
286
296
|
: undefined)}
|
|
287
297
|
aria-keyshortcuts={draggable ? "Alt+ArrowUp Alt+ArrowDown" : undefined}
|
|
288
298
|
draggable={draggable ? "true" : undefined}
|
|
289
|
-
animate:flip={{ duration:
|
|
299
|
+
animate:flip={{ duration: () => durationMs("fast"), easing: cubicOut }}
|
|
290
300
|
ondragstart={draggable
|
|
291
301
|
? (e) => {
|
|
292
|
-
liftDragImage(e
|
|
302
|
+
liftDragImage(e);
|
|
293
303
|
dnd.onDragStart(e, id);
|
|
294
304
|
}
|
|
295
305
|
: undefined}
|
|
@@ -300,7 +310,9 @@
|
|
|
300
310
|
onkeydown={draggable ? (e) => onRowKeyDown(e, id) : undefined}
|
|
301
311
|
style:--tl-i={Math.min(index, STAGGER_CAP)}
|
|
302
312
|
class={cn(
|
|
303
|
-
"tl-row relative mx-1.5
|
|
313
|
+
"tl-row relative mx-1.5 outline-none focus-visible:shadow-(--ring-focus)",
|
|
314
|
+
workLooks.row,
|
|
315
|
+
workLooks.rowHeight,
|
|
304
316
|
draggable && "cursor-grab active:cursor-grabbing",
|
|
305
317
|
dropBefore && "tl-drop-before",
|
|
306
318
|
dnd.isDragSource(id) && "opacity-40",
|
|
@@ -319,9 +331,7 @@
|
|
|
319
331
|
{/if}
|
|
320
332
|
|
|
321
333
|
{#if rows.length === 0 && children.length === 0 && !active}
|
|
322
|
-
<p
|
|
323
|
-
class="mx-1.5 rounded-lg border border-dashed border-border px-3 py-3 text-xs text-muted-foreground"
|
|
324
|
-
>
|
|
334
|
+
<p class={cn(workLooks.rowHole, "mx-1.5 px-3 py-3 text-xs text-muted-foreground")}>
|
|
325
335
|
{emptyLabel}
|
|
326
336
|
</p>
|
|
327
337
|
{/if}
|
|
@@ -338,7 +348,7 @@
|
|
|
338
348
|
<div
|
|
339
349
|
class={cn(
|
|
340
350
|
"task-list flex min-h-0 flex-1 flex-col overflow-y-auto",
|
|
341
|
-
flat ? "bg-
|
|
351
|
+
flat ? "bg-card" : workLooks.panel,
|
|
342
352
|
className,
|
|
343
353
|
)}
|
|
344
354
|
>
|
|
@@ -346,7 +356,7 @@
|
|
|
346
356
|
<div
|
|
347
357
|
class={cn(
|
|
348
358
|
"sticky top-0 z-20 flex h-9 shrink-0 items-center border-b border-border text-xs font-medium text-muted-foreground backdrop-blur-sm",
|
|
349
|
-
flat ? "bg-
|
|
359
|
+
flat ? "bg-card/90" : "bg-card/90",
|
|
350
360
|
)}
|
|
351
361
|
>
|
|
352
362
|
{@render head()}
|
|
@@ -360,17 +370,7 @@
|
|
|
360
370
|
|
|
361
371
|
<style>
|
|
362
372
|
.tl-group {
|
|
363
|
-
transition: background-color var(--duration-quick
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
.tl-group :global(.tl-actions:focus-within) {
|
|
367
|
-
opacity: 1;
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
@media (hover: hover) and (pointer: fine) {
|
|
371
|
-
.tl-group:hover :global(.tl-actions) {
|
|
372
|
-
opacity: 1;
|
|
373
|
-
}
|
|
373
|
+
transition: background-color var(--duration-quick) var(--ease-smooth-out);
|
|
374
374
|
}
|
|
375
375
|
|
|
376
376
|
.tl-group-active {
|
|
@@ -378,8 +378,7 @@
|
|
|
378
378
|
}
|
|
379
379
|
|
|
380
380
|
.tl-chevron {
|
|
381
|
-
transition: rotate var(--duration-fast
|
|
382
|
-
var(--ease-smooth-out, cubic-bezier(0.22, 1, 0.36, 1));
|
|
381
|
+
transition: rotate var(--duration-fast) var(--ease-smooth-out);
|
|
383
382
|
}
|
|
384
383
|
|
|
385
384
|
.tl-chevron-open {
|
|
@@ -392,9 +391,8 @@
|
|
|
392
391
|
grid-template-rows: 0fr;
|
|
393
392
|
opacity: 0;
|
|
394
393
|
transition:
|
|
395
|
-
grid-template-rows var(--duration-fast
|
|
396
|
-
|
|
397
|
-
opacity var(--duration-quick, 150ms) var(--ease-smooth-out, ease-out);
|
|
394
|
+
grid-template-rows var(--duration-fast) var(--ease-smooth-out),
|
|
395
|
+
opacity var(--duration-quick) var(--ease-smooth-out);
|
|
398
396
|
}
|
|
399
397
|
|
|
400
398
|
.tl-fold-open {
|
|
@@ -408,15 +406,14 @@
|
|
|
408
406
|
}
|
|
409
407
|
|
|
410
408
|
.tl-row {
|
|
411
|
-
animation: tl-row-in var(--duration-slow
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
transition: background-color var(--duration-quick, 150ms) var(--ease-smooth-out, ease-out);
|
|
409
|
+
animation: tl-row-in var(--duration-slow) var(--ease-smooth-out) backwards;
|
|
410
|
+
animation-delay: calc(var(--tl-i, 0) * var(--duration-stagger));
|
|
411
|
+
transition: background-color var(--duration-quick) var(--ease-smooth-out);
|
|
415
412
|
}
|
|
416
413
|
|
|
417
414
|
@media (hover: hover) and (pointer: fine) {
|
|
418
415
|
.tl-row:hover {
|
|
419
|
-
background: color-mix(in oklab, var(--color-foreground
|
|
416
|
+
background: color-mix(in oklab, var(--color-foreground) 4%, transparent);
|
|
420
417
|
}
|
|
421
418
|
}
|
|
422
419
|
|
|
@@ -428,9 +425,8 @@
|
|
|
428
425
|
left: 0.5rem;
|
|
429
426
|
right: 0.5rem;
|
|
430
427
|
height: 2px;
|
|
431
|
-
border-radius: 1px;
|
|
432
428
|
background: var(--tl-hue);
|
|
433
|
-
animation: tl-line-in var(--duration-quick
|
|
429
|
+
animation: tl-line-in var(--duration-quick) var(--ease-smooth-out) both;
|
|
434
430
|
}
|
|
435
431
|
|
|
436
432
|
/* A ring where the line starts, so the drop point reads at a glance. */
|
|
@@ -441,10 +437,15 @@
|
|
|
441
437
|
left: 0.25rem;
|
|
442
438
|
width: 8px;
|
|
443
439
|
height: 8px;
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
440
|
+
/* A ring drawn as a gradient, so the line needs no radius or border of its own. */
|
|
441
|
+
background: radial-gradient(
|
|
442
|
+
circle closest-side,
|
|
443
|
+
var(--color-card) calc(50% - 0.5px),
|
|
444
|
+
var(--tl-hue) 50%,
|
|
445
|
+
var(--tl-hue) calc(100% - 0.5px),
|
|
446
|
+
transparent 100%
|
|
447
|
+
);
|
|
448
|
+
animation: tl-line-in var(--duration-quick) var(--ease-smooth-out) both;
|
|
448
449
|
}
|
|
449
450
|
|
|
450
451
|
.tl-drop-before::before {
|
|
@@ -466,8 +467,8 @@
|
|
|
466
467
|
@keyframes tl-row-in {
|
|
467
468
|
from {
|
|
468
469
|
opacity: 0;
|
|
469
|
-
transform: translateY(var(--distance-micro
|
|
470
|
-
filter: blur(var(--blur-small
|
|
470
|
+
transform: translateY(var(--distance-micro));
|
|
471
|
+
filter: blur(var(--blur-small));
|
|
471
472
|
}
|
|
472
473
|
}
|
|
473
474
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { cn } from "../../lib/utils.js";
|
|
3
|
-
import {
|
|
3
|
+
import { TONE_TEXT } from "../../lib/tone.js";
|
|
4
|
+
import StatusPill from "../../status/status-pill.svelte";
|
|
4
5
|
import { budgetTone, formatMinutes } from "./duration.js";
|
|
5
6
|
import TimeDot from "./time-dot.svelte";
|
|
6
7
|
import TimeRing from "./time-ring.svelte";
|
|
@@ -34,27 +35,23 @@
|
|
|
34
35
|
);
|
|
35
36
|
</script>
|
|
36
37
|
|
|
38
|
+
<!-- Logged time is a status of the task's budget, so it's a StatusPill: the icon sits in the tint. -->
|
|
37
39
|
{#if visible}
|
|
38
|
-
<
|
|
40
|
+
<StatusPill
|
|
39
41
|
role="img"
|
|
40
42
|
aria-label={spoken}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
running
|
|
44
|
-
? TONE_CLASS.accent
|
|
45
|
-
: tone === "neutral"
|
|
46
|
-
? "border-transparent text-muted-foreground"
|
|
47
|
-
: TONE_CLASS[tone],
|
|
48
|
-
className,
|
|
49
|
-
)}
|
|
43
|
+
tone={running ? "accent" : tone}
|
|
44
|
+
class={cn("tabular-nums", className)}
|
|
50
45
|
>
|
|
51
|
-
{#
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
46
|
+
{#snippet icon()}
|
|
47
|
+
{#if running}
|
|
48
|
+
<TimeDot running size={6} />
|
|
49
|
+
{:else if estimate}
|
|
50
|
+
<TimeRing value={minutes} max={estimate} size={12} stroke={6} />
|
|
51
|
+
{:else}
|
|
52
|
+
<Clock aria-hidden="true" />
|
|
53
|
+
{/if}
|
|
54
|
+
{/snippet}
|
|
58
55
|
<span aria-hidden="true">
|
|
59
56
|
{formatMinutes(minutes)}{#if estimate}<span
|
|
60
57
|
class={cn(
|
|
@@ -65,5 +62,5 @@
|
|
|
65
62
|
/ {formatMinutes(estimate)}</span
|
|
66
63
|
>{/if}
|
|
67
64
|
</span>
|
|
68
|
-
</
|
|
65
|
+
</StatusPill>
|
|
69
66
|
{/if}
|
|
@@ -65,9 +65,8 @@
|
|
|
65
65
|
<style>
|
|
66
66
|
.time-ring-arc {
|
|
67
67
|
transition:
|
|
68
|
-
stroke-dasharray var(--duration-slow
|
|
69
|
-
|
|
70
|
-
color var(--duration-fast, 250ms) var(--ease-smooth-out, cubic-bezier(0.22, 1, 0.36, 1));
|
|
68
|
+
stroke-dasharray var(--duration-slow) var(--ease-smooth-out),
|
|
69
|
+
color var(--duration-fast) var(--ease-smooth-out);
|
|
71
70
|
}
|
|
72
71
|
|
|
73
72
|
.time-ring-arc.is-empty {
|