@heyramzi/ui 2.23.0 → 2.24.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 +3 -6
- package/dist/ai/confirmation/index.js +3 -6
- package/dist/ai/index.js +1 -5
- package/dist/charts/dashboard/index.js +1 -3
- package/dist/charts/primitives/chart-utils.js +14 -0
- package/dist/components/chip/index.js +1 -0
- package/dist/components/language-flag/flags.js +5 -0
- package/dist/components/language-flag/index.js +1 -0
- package/dist/components/list-page/index.js +1 -3
- package/dist/components/work-parts/index.js +1 -1
- package/dist/editor/doc-blocks.js +2 -1
- package/dist/effects/animated-beam/path-calculator.svelte.js +13 -6
- package/dist/lib/dates.js +41 -0
- package/dist/lib/panel.js +2 -2
- package/dist/lib/tone.js +13 -0
- package/dist/record/format.js +2 -18
- package/dist/record/logic.js +3 -6
- package/dist/status/index.js +1 -1
- package/dist/work/dates.js +2 -3
- package/dist/work/detail/detail-types.js +0 -8
- package/dist/work/docs/edited.js +8 -7
- package/dist/work/inbox/inbox-logic.js +5 -20
- package/dist/work/index.js +0 -2
- package/dist/work/time/duration.js +2 -3
- package/package.json +1 -21
- package/src/ai/confirmation/Confirmation.svelte +2 -2
- package/src/ai/confirmation/{ConfirmationRequest.svelte → ConfirmationState.svelte} +6 -3
- package/src/ai/confirmation/confirmation-context.svelte.ts +2 -2
- package/src/ai/confirmation/confirmation-parts.test.svelte +17 -0
- package/src/ai/confirmation/confirmation-parts.test.ts +23 -0
- package/src/ai/confirmation/index.ts +6 -21
- package/src/ai/index.ts +3 -13
- package/src/ai/typing-dots/TypingDots.svelte +32 -12
- package/src/ai/typing-dots/typing-dots.test.ts +36 -0
- package/src/canvas/ui/canvas-ui.css +12 -0
- package/src/charts/README.md +2 -2
- package/src/charts/area-chart.svelte +3 -10
- package/src/charts/bar-chart.svelte +4 -12
- package/src/charts/dashboard/data-table.svelte +21 -21
- package/src/charts/dashboard/index.ts +1 -3
- package/src/charts/dashboard/rating-chip.svelte +9 -11
- package/src/charts/dashboard/stat-tile.svelte +3 -4
- package/src/charts/line-chart.svelte +3 -10
- package/src/charts/primitives/chart-utils.test.ts +17 -0
- package/src/charts/primitives/chart-utils.ts +16 -0
- package/src/charts/time-series-chart.svelte +8 -4
- package/src/components/chip/count-chip.svelte +35 -0
- package/src/components/chip/index.ts +1 -0
- package/src/components/data-table/data-table.ts +1 -1
- package/src/components/data-table/row-context-menu.svelte +3 -4
- package/src/components/data-table/sortable-head.svelte +45 -10
- package/src/components/language-flag/flags.ts +5 -0
- package/src/components/language-flag/index.ts +1 -0
- package/src/components/language-flag/language-flag.svelte +3 -11
- package/src/components/list-page/empty-state.svelte +90 -12
- package/src/components/list-page/filter-popover.svelte +2 -5
- package/src/components/list-page/grouped-list.svelte +2 -4
- package/src/components/list-page/index.ts +1 -3
- package/src/components/section-header/section-header.svelte +2 -6
- package/src/components/segmented/segment.svelte +13 -19
- package/src/components/segmented/segmented-filter.svelte +1 -2
- package/src/components/table-view/table-view.svelte +31 -45
- package/src/components/table-view/table-view.test.ts +6 -3
- package/src/components/work-parts/group-head.svelte +1 -1
- package/src/components/work-parts/index.ts +1 -1
- package/src/editor/doc-blocks.ts +2 -1
- package/src/effects/animated-beam/path-calculator.svelte.ts +19 -5
- package/src/effects/marquee/Marquee.css +6 -3
- package/src/effects/marquee/Marquee.svelte +6 -2
- package/src/inbox/thread-inbox.svelte +6 -4
- package/src/lib/dates.test.ts +63 -0
- package/src/lib/dates.ts +63 -0
- package/src/lib/panel.svelte +116 -0
- package/src/lib/panel.ts +2 -2
- package/src/lib/tone.ts +15 -0
- package/src/panel/side-panel.svelte +4 -4
- package/src/record/format.ts +3 -15
- package/src/record/logic.ts +2 -6
- package/src/record/record-stats.svelte +4 -3
- package/src/record/steps-rail.svelte +7 -4
- package/src/status/index.ts +1 -1
- package/src/status/status-pill.svelte +2 -8
- package/src/thread/thread.svelte +3 -6
- package/src/work/README.md +0 -1
- package/src/work/dates.ts +3 -3
- package/src/work/demo.svelte +9 -1
- package/src/work/detail/detail-demo.svelte +30 -27
- package/src/work/detail/detail-types.ts +1 -29
- package/src/work/detail/task-detail.svelte +18 -17
- package/src/work/docs/doc-menu.svelte +2 -1
- package/src/work/docs/edited.ts +4 -7
- package/src/work/inbox/inbox-card.svelte +4 -9
- package/src/work/inbox/inbox-logic.ts +4 -16
- package/src/work/index.ts +1 -3
- package/src/work/time/duration.ts +2 -3
- package/dist/ai/thinking-dots/index.js +0 -3
- package/dist/components/icon/index.js +0 -1
- package/dist/components/locale-switch/index.js +0 -1
- package/dist/icons/trades.js +0 -49
- package/dist/status/pill-tint.js +0 -13
- package/dist/work/presets/sales.js +0 -39
- package/src/ai/confirmation/ConfirmationAccepted.svelte +0 -23
- package/src/ai/confirmation/ConfirmationFailed.svelte +0 -23
- package/src/ai/confirmation/ConfirmationRejected.svelte +0 -23
- package/src/ai/thinking-dots/ThinkingDots.svelte +0 -42
- package/src/ai/thinking-dots/index.ts +0 -5
- package/src/charts/dashboard/dashboard-empty.svelte +0 -55
- package/src/charts/dashboard/data-panel.svelte +0 -81
- package/src/charts/dashboard/data-sort-head.svelte +0 -102
- package/src/components/icon/icon.svelte +0 -23
- package/src/components/icon/index.ts +0 -1
- package/src/components/list-page/detail-empty.svelte +0 -45
- package/src/components/list-page/detail-panel.svelte +0 -82
- package/src/components/list-page/rail-empty.svelte +0 -44
- package/src/components/locale-switch/index.ts +0 -1
- package/src/components/locale-switch/locale-switch.svelte +0 -59
- package/src/components/work-parts/count-chip.svelte +0 -20
- package/src/icons/TradeIcon.svelte +0 -16
- package/src/icons/trades.ts +0 -89
- package/src/status/pill-tint.ts +0 -16
- package/src/work/detail/task-property.svelte +0 -17
- package/src/work/detail/task-thread.svelte +0 -221
- package/src/work/presets/sales.ts +0 -16
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
// =========================
|
|
3
|
-
// IMPORTS
|
|
4
|
-
// =========================
|
|
5
|
-
import type { Snippet } from "svelte";
|
|
6
|
-
import { cn } from "../../lib/utils.js";
|
|
7
|
-
import type { PhosphorIcon } from "../../lib/icon-types.js";
|
|
8
|
-
|
|
9
|
-
// =========================
|
|
10
|
-
// TYPES
|
|
11
|
-
// =========================
|
|
12
|
-
interface Props {
|
|
13
|
-
// Not list-page's `EmptyState`: that one takes exactly one action, and a
|
|
14
|
-
// dashboard can be empty for reasons (nothing connected, nothing synced)
|
|
15
|
-
// that need two buttons.
|
|
16
|
-
|
|
17
|
-
// The dashed edge marks truly empty, not loading.
|
|
18
|
-
icon?: PhosphorIcon;
|
|
19
|
-
title: string;
|
|
20
|
-
description?: string;
|
|
21
|
-
/** One or two buttons. Omit where there is nothing the reader can do. */
|
|
22
|
-
actions?: Snippet;
|
|
23
|
-
class?: string;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// =========================
|
|
27
|
-
// PROPS
|
|
28
|
-
// =========================
|
|
29
|
-
let { icon: Icon, title, description, actions, class: className }: Props = $props();
|
|
30
|
-
</script>
|
|
31
|
-
|
|
32
|
-
<!-- =========================== -->
|
|
33
|
-
<!-- MARKUP -->
|
|
34
|
-
<!-- =========================== -->
|
|
35
|
-
<div
|
|
36
|
-
class={cn(
|
|
37
|
-
"rounded-xl border-2 border-dashed border-border bg-transparent p-10 text-center",
|
|
38
|
-
className,
|
|
39
|
-
)}
|
|
40
|
-
>
|
|
41
|
-
<div class="mx-auto flex max-w-md flex-col items-center gap-4">
|
|
42
|
-
{#if Icon}
|
|
43
|
-
<Icon class="size-10 text-muted-foreground" aria-hidden="true" />
|
|
44
|
-
{/if}
|
|
45
|
-
<div class="space-y-1.5">
|
|
46
|
-
<h2 class="font-heading text-lg font-semibold">{title}</h2>
|
|
47
|
-
{#if description}
|
|
48
|
-
<p class="text-sm text-muted-foreground">{description}</p>
|
|
49
|
-
{/if}
|
|
50
|
-
</div>
|
|
51
|
-
{#if actions}
|
|
52
|
-
<div class="flex flex-wrap justify-center gap-2">{@render actions()}</div>
|
|
53
|
-
{/if}
|
|
54
|
-
</div>
|
|
55
|
-
</div>
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
// =========================
|
|
3
|
-
// IMPORTS
|
|
4
|
-
// =========================
|
|
5
|
-
import type { Snippet } from "svelte";
|
|
6
|
-
import { cn } from "../../lib/utils.js";
|
|
7
|
-
import { panelHeaderClass, panelShellClass } from "../../lib/panel.js";
|
|
8
|
-
import { SectionLabel } from "../../components/section-header/index.js";
|
|
9
|
-
import type { PhosphorIcon } from "../../lib/icon-types.js";
|
|
10
|
-
|
|
11
|
-
// =========================
|
|
12
|
-
// TYPES
|
|
13
|
-
// =========================
|
|
14
|
-
interface Props {
|
|
15
|
-
/** What this section of the dashboard is. Drawn as the app's one panel label. */
|
|
16
|
-
label: string;
|
|
17
|
-
labelIcon?: PhosphorIcon;
|
|
18
|
-
/**
|
|
19
|
-
* A row count, total or freshness stamp. It's a prop, not an action,
|
|
20
|
-
* because hand-placed hints ended up at three type sizes on one screen.
|
|
21
|
-
*/
|
|
22
|
-
hint?: string;
|
|
23
|
-
/** Controls that act on this panel. Pinned right, after the hint. */
|
|
24
|
-
actions?: Snippet;
|
|
25
|
-
/** A line under the label, for the scope a number was measured over. */
|
|
26
|
-
sublabel?: string;
|
|
27
|
-
/** Off for a body that supplies its own padding: a table, a full-bleed list. */
|
|
28
|
-
pad?: boolean;
|
|
29
|
-
/**
|
|
30
|
-
* The body scrolls sideways rather than squeezing. For a wide table, which is
|
|
31
|
-
* every table on a dashboard once the window is narrower than its columns.
|
|
32
|
-
*/
|
|
33
|
-
scroll?: boolean;
|
|
34
|
-
children: Snippet;
|
|
35
|
-
class?: string;
|
|
36
|
-
bodyClass?: string;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// =========================
|
|
40
|
-
// PROPS
|
|
41
|
-
// =========================
|
|
42
|
-
let {
|
|
43
|
-
label,
|
|
44
|
-
labelIcon,
|
|
45
|
-
hint,
|
|
46
|
-
actions,
|
|
47
|
-
sublabel,
|
|
48
|
-
pad = true,
|
|
49
|
-
scroll = false,
|
|
50
|
-
children,
|
|
51
|
-
class: className,
|
|
52
|
-
bodyClass,
|
|
53
|
-
}: Props = $props();
|
|
54
|
-
</script>
|
|
55
|
-
|
|
56
|
-
<!-- =========================== -->
|
|
57
|
-
<!-- MARKUP -->
|
|
58
|
-
<!-- =========================== -->
|
|
59
|
-
<section class={cn(panelShellClass, className)}>
|
|
60
|
-
<div class={panelHeaderClass}>
|
|
61
|
-
<div class="flex min-w-0 flex-col gap-0.5">
|
|
62
|
-
<SectionLabel icon={labelIcon}>{label}</SectionLabel>
|
|
63
|
-
{#if sublabel}
|
|
64
|
-
<span class="truncate text-xs text-muted-foreground">{sublabel}</span>
|
|
65
|
-
{/if}
|
|
66
|
-
</div>
|
|
67
|
-
{#if hint || actions}
|
|
68
|
-
<div class="flex min-w-0 shrink-0 items-center gap-3">
|
|
69
|
-
{#if hint}
|
|
70
|
-
<span class="text-xs tabular-nums text-muted-foreground">{hint}</span>
|
|
71
|
-
{/if}
|
|
72
|
-
{#if actions}
|
|
73
|
-
{@render actions()}
|
|
74
|
-
{/if}
|
|
75
|
-
</div>
|
|
76
|
-
{/if}
|
|
77
|
-
</div>
|
|
78
|
-
<div class={cn(pad && "p-4", scroll && "overflow-x-auto", bodyClass)}>
|
|
79
|
-
{@render children()}
|
|
80
|
-
</div>
|
|
81
|
-
</section>
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
/*
|
|
3
|
-
* A column head that sorts the table under it. Sort buttons above a table
|
|
4
|
-
* never say which column they rank, and read as filter tabs.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
// Every sortable head shows its caret, at 40%, solid on the active column.
|
|
8
|
-
// Hover-only makes people hunt, and it shifts the label under the pointer.
|
|
9
|
-
|
|
10
|
-
// No padding of its own: it comes from the skin classes, so heads and cells
|
|
11
|
-
// stay in line when a dense table tightens both.
|
|
12
|
-
|
|
13
|
-
// =========================
|
|
14
|
-
// IMPORTS
|
|
15
|
-
// =========================
|
|
16
|
-
import type { Snippet } from "svelte";
|
|
17
|
-
import { CaretDown, CaretUp } from "phosphor-svelte";
|
|
18
|
-
import { cn } from "../../lib/utils.js";
|
|
19
|
-
import { dataThClass, dataThNumClass } from "./data-table.js";
|
|
20
|
-
|
|
21
|
-
// =========================
|
|
22
|
-
// TYPES
|
|
23
|
-
// =========================
|
|
24
|
-
interface Props {
|
|
25
|
-
/** True when the table is currently ranked by this column. */
|
|
26
|
-
active: boolean;
|
|
27
|
-
/** Which way the active column runs. Ignored while inactive. */
|
|
28
|
-
descending: boolean;
|
|
29
|
-
/** Called on click and on Enter/Space. Toggling direction is the caller's. */
|
|
30
|
-
onSort: () => void;
|
|
31
|
-
/** Numbers right-align to their digits, so their caret leads the label. */
|
|
32
|
-
numeric?: boolean;
|
|
33
|
-
/** The long form of an abbreviated head, as the browser tooltip. */
|
|
34
|
-
title?: string;
|
|
35
|
-
class?: string;
|
|
36
|
-
children: Snippet;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// =========================
|
|
40
|
-
// PROPS
|
|
41
|
-
// =========================
|
|
42
|
-
let {
|
|
43
|
-
active,
|
|
44
|
-
descending,
|
|
45
|
-
onSort,
|
|
46
|
-
numeric = false,
|
|
47
|
-
title,
|
|
48
|
-
class: className,
|
|
49
|
-
children,
|
|
50
|
-
}: Props = $props();
|
|
51
|
-
|
|
52
|
-
// =========================
|
|
53
|
-
// DERIVED
|
|
54
|
-
// =========================
|
|
55
|
-
const ariaSort = $derived(active ? (descending ? "descending" : "ascending") : "none");
|
|
56
|
-
|
|
57
|
-
// =========================
|
|
58
|
-
// FUNCTIONS
|
|
59
|
-
// =========================
|
|
60
|
-
function handleKeydown(event: KeyboardEvent) {
|
|
61
|
-
if (event.key === "Enter" || event.key === " ") {
|
|
62
|
-
event.preventDefault();
|
|
63
|
-
onSort();
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
</script>
|
|
67
|
-
|
|
68
|
-
<!-- =========================== -->
|
|
69
|
-
<!-- MARKUP -->
|
|
70
|
-
<!-- =========================== -->
|
|
71
|
-
<th
|
|
72
|
-
scope="col"
|
|
73
|
-
{title}
|
|
74
|
-
tabindex="0"
|
|
75
|
-
aria-sort={ariaSort}
|
|
76
|
-
onclick={onSort}
|
|
77
|
-
onkeydown={handleKeydown}
|
|
78
|
-
class={cn(
|
|
79
|
-
numeric ? dataThNumClass : dataThClass,
|
|
80
|
-
"cursor-pointer select-none transition-colors hover:text-foreground",
|
|
81
|
-
active && "text-foreground",
|
|
82
|
-
className,
|
|
83
|
-
)}
|
|
84
|
-
>
|
|
85
|
-
<span class="inline-flex items-center gap-0.5 whitespace-nowrap">
|
|
86
|
-
{#if numeric}
|
|
87
|
-
{@render caret()}{@render children()}
|
|
88
|
-
{:else}
|
|
89
|
-
{@render children()}{@render caret()}
|
|
90
|
-
{/if}
|
|
91
|
-
</span>
|
|
92
|
-
</th>
|
|
93
|
-
|
|
94
|
-
{#snippet caret()}
|
|
95
|
-
{#if active && !descending}
|
|
96
|
-
<CaretUp class="size-3 shrink-0" weight="bold" />
|
|
97
|
-
{:else if active}
|
|
98
|
-
<CaretDown class="size-3 shrink-0" weight="bold" />
|
|
99
|
-
{:else}
|
|
100
|
-
<CaretDown class="size-3 shrink-0 text-muted-foreground/40" />
|
|
101
|
-
{/if}
|
|
102
|
-
{/snippet}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import type { PhosphorIcon, IconWeight } from "../../lib/icon-types.js";
|
|
3
|
-
|
|
4
|
-
// =========================
|
|
5
|
-
// TYPES
|
|
6
|
-
// =========================
|
|
7
|
-
interface Props {
|
|
8
|
-
icon: PhosphorIcon;
|
|
9
|
-
size?: number;
|
|
10
|
-
class?: string;
|
|
11
|
-
weight?: IconWeight;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
// =========================
|
|
15
|
-
// PROPS
|
|
16
|
-
// =========================
|
|
17
|
-
let { icon: IconComp, size = 24, class: className = "", weight }: Props = $props();
|
|
18
|
-
</script>
|
|
19
|
-
|
|
20
|
-
<!-- =========================== -->
|
|
21
|
-
<!-- MARKUP -->
|
|
22
|
-
<!-- =========================== -->
|
|
23
|
-
<IconComp {size} class={className} {weight} />
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default as Icon } from "./icon.svelte";
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
// =========================
|
|
3
|
-
// IMPORTS
|
|
4
|
-
// =========================
|
|
5
|
-
import EmptyState from "./empty-state.svelte";
|
|
6
|
-
import { cn } from "../../lib/utils.js";
|
|
7
|
-
import type { PhosphorIcon, IconWeight } from "../../lib/icon-types.js";
|
|
8
|
-
|
|
9
|
-
// =========================
|
|
10
|
-
// TYPES
|
|
11
|
-
// =========================
|
|
12
|
-
interface Props {
|
|
13
|
-
// Shown when nothing is picked or the board is empty. It sits on the white
|
|
14
|
-
// panel with no frame of its own: a dashed edge drew a box round nothing.
|
|
15
|
-
icon?: PhosphorIcon;
|
|
16
|
-
iconWeight?: IconWeight;
|
|
17
|
-
title: string;
|
|
18
|
-
description?: string;
|
|
19
|
-
actionLabel?: string;
|
|
20
|
-
actionHref?: string;
|
|
21
|
-
onAction?: () => void;
|
|
22
|
-
class?: string;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// =========================
|
|
26
|
-
// PROPS
|
|
27
|
-
// =========================
|
|
28
|
-
let {
|
|
29
|
-
icon,
|
|
30
|
-
iconWeight,
|
|
31
|
-
title,
|
|
32
|
-
description,
|
|
33
|
-
actionLabel,
|
|
34
|
-
actionHref,
|
|
35
|
-
onAction,
|
|
36
|
-
class: className,
|
|
37
|
-
}: Props = $props();
|
|
38
|
-
</script>
|
|
39
|
-
|
|
40
|
-
<!-- =========================== -->
|
|
41
|
-
<!-- MARKUP -->
|
|
42
|
-
<!-- =========================== -->
|
|
43
|
-
<div class={cn("flex h-full items-center justify-center rounded-xl bg-card", className)}>
|
|
44
|
-
<EmptyState {icon} {iconWeight} {title} {description} {actionLabel} {actionHref} {onAction} />
|
|
45
|
-
</div>
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
// =========================
|
|
3
|
-
// IMPORTS
|
|
4
|
-
// =========================
|
|
5
|
-
import type { Snippet } from "svelte";
|
|
6
|
-
import SectionLabel from "../section-header/section-label.svelte";
|
|
7
|
-
import { cn } from "../../lib/utils.js";
|
|
8
|
-
import { panelHeaderClass, panelShellClass } from "../../lib/panel.js";
|
|
9
|
-
import type { PhosphorIcon } from "../../lib/icon-types.js";
|
|
10
|
-
|
|
11
|
-
// =========================
|
|
12
|
-
// TYPES
|
|
13
|
-
// =========================
|
|
14
|
-
interface Props {
|
|
15
|
-
// One panel per idea in a detail pane. Hand-rolled copies drifted between
|
|
16
|
-
// 8px and 12px corners, so rounded-xl lives here once.
|
|
17
|
-
label?: string;
|
|
18
|
-
labelIcon?: PhosphorIcon;
|
|
19
|
-
// A mark the icon set does not carry (a platform logo), drawn ahead of the
|
|
20
|
-
// label at the same optical size.
|
|
21
|
-
labelLead?: Snippet;
|
|
22
|
-
// The right side of the header bar: a count, a hint, a button cluster.
|
|
23
|
-
actions?: Snippet;
|
|
24
|
-
// WHY: a panel whose body is a media grid or a full-bleed list supplies its
|
|
25
|
-
// own padding, so the shell must be able to step out of the way.
|
|
26
|
-
pad?: boolean;
|
|
27
|
-
// Takes the height the panels above it did not use, inside a `fill` stack.
|
|
28
|
-
// One panel per stack, and it is the one the reader works in.
|
|
29
|
-
grow?: boolean;
|
|
30
|
-
children: Snippet;
|
|
31
|
-
class?: string;
|
|
32
|
-
bodyClass?: string;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// =========================
|
|
36
|
-
// PROPS
|
|
37
|
-
// =========================
|
|
38
|
-
let {
|
|
39
|
-
label,
|
|
40
|
-
labelIcon,
|
|
41
|
-
labelLead,
|
|
42
|
-
actions,
|
|
43
|
-
pad = true,
|
|
44
|
-
grow = false,
|
|
45
|
-
children,
|
|
46
|
-
class: className,
|
|
47
|
-
bodyClass,
|
|
48
|
-
}: Props = $props();
|
|
49
|
-
|
|
50
|
-
// =========================
|
|
51
|
-
// DERIVED
|
|
52
|
-
// =========================
|
|
53
|
-
let hasHeader = $derived(Boolean(label || labelLead || actions));
|
|
54
|
-
</script>
|
|
55
|
-
|
|
56
|
-
<!-- =========================== -->
|
|
57
|
-
<!-- MARKUP -->
|
|
58
|
-
<!-- =========================== -->
|
|
59
|
-
<section class={cn(panelShellClass, grow && "flex min-h-0 flex-1 flex-col", className)}>
|
|
60
|
-
{#if hasHeader}
|
|
61
|
-
<div class={panelHeaderClass}>
|
|
62
|
-
{#if label || labelLead}
|
|
63
|
-
<SectionLabel icon={labelIcon}>
|
|
64
|
-
{#if labelLead}
|
|
65
|
-
{@render labelLead()}
|
|
66
|
-
{/if}
|
|
67
|
-
{label}
|
|
68
|
-
</SectionLabel>
|
|
69
|
-
{:else}
|
|
70
|
-
<span></span>
|
|
71
|
-
{/if}
|
|
72
|
-
{#if actions}
|
|
73
|
-
<div class="flex min-w-0 flex-wrap items-center justify-end gap-2">
|
|
74
|
-
{@render actions()}
|
|
75
|
-
</div>
|
|
76
|
-
{/if}
|
|
77
|
-
</div>
|
|
78
|
-
{/if}
|
|
79
|
-
<div class={cn(pad && "p-4", grow && "flex min-h-0 flex-1 flex-col", bodyClass)}>
|
|
80
|
-
{@render children()}
|
|
81
|
-
</div>
|
|
82
|
-
</section>
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
// =========================
|
|
3
|
-
// IMPORTS
|
|
4
|
-
// =========================
|
|
5
|
-
import { cn } from "../../lib/utils.js";
|
|
6
|
-
import * as Empty from "../../shadcn/empty/index.js";
|
|
7
|
-
import type { PhosphorIcon, IconWeight } from "../../lib/icon-types.js";
|
|
8
|
-
|
|
9
|
-
// =========================
|
|
10
|
-
// TYPES
|
|
11
|
-
// =========================
|
|
12
|
-
interface Props {
|
|
13
|
-
// Not `EmptyState`, which has a 48px icon and an action. This says a filter
|
|
14
|
-
// came back empty, in one sentence that fits a 280px rail.
|
|
15
|
-
icon?: PhosphorIcon;
|
|
16
|
-
iconWeight?: IconWeight;
|
|
17
|
-
iconClass?: string;
|
|
18
|
-
title: string;
|
|
19
|
-
hint?: string;
|
|
20
|
-
class?: string;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// =========================
|
|
24
|
-
// PROPS
|
|
25
|
-
// =========================
|
|
26
|
-
let { icon: Icon, iconWeight, iconClass, title, hint, class: className }: Props = $props();
|
|
27
|
-
</script>
|
|
28
|
-
|
|
29
|
-
<!-- =========================== -->
|
|
30
|
-
<!-- MARKUP -->
|
|
31
|
-
<!-- =========================== -->
|
|
32
|
-
<Empty.Root class={cn("h-full gap-2 px-6 py-12", className)}>
|
|
33
|
-
{#if Icon}
|
|
34
|
-
<Icon
|
|
35
|
-
weight={iconWeight}
|
|
36
|
-
class={cn("size-6", iconClass ?? "text-muted-foreground")}
|
|
37
|
-
aria-hidden="true"
|
|
38
|
-
/>
|
|
39
|
-
{/if}
|
|
40
|
-
<Empty.Title>{title}</Empty.Title>
|
|
41
|
-
{#if hint}
|
|
42
|
-
<Empty.Description class="max-w-56 text-xs leading-relaxed">{hint}</Empty.Description>
|
|
43
|
-
{/if}
|
|
44
|
-
</Empty.Root>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default as LocaleSwitch } from "./locale-switch.svelte";
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
// =========================
|
|
3
|
-
// IMPORTS
|
|
4
|
-
// =========================
|
|
5
|
-
import { SegmentedFilter, type TabDef } from "../segmented/index.js";
|
|
6
|
-
|
|
7
|
-
// =========================
|
|
8
|
-
// TYPES
|
|
9
|
-
// =========================
|
|
10
|
-
type LocaleOption = {
|
|
11
|
-
value: string;
|
|
12
|
-
label: string;
|
|
13
|
-
flag: string;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
interface Props {
|
|
17
|
-
// Wraps `SegmentedFilter` so it shares one track with every other strip. It
|
|
18
|
-
// only adds each language's flag.
|
|
19
|
-
value: string;
|
|
20
|
-
onchange: (value: string) => void;
|
|
21
|
-
options?: LocaleOption[];
|
|
22
|
-
/** Names the control for a screen reader. Without it the group is two bare letters. */
|
|
23
|
-
ariaLabel?: string;
|
|
24
|
-
class?: string;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// =========================
|
|
28
|
-
// PROPS
|
|
29
|
-
// =========================
|
|
30
|
-
let {
|
|
31
|
-
value,
|
|
32
|
-
onchange,
|
|
33
|
-
options = [
|
|
34
|
-
{ value: "en", label: "EN", flag: "/flags/US.svg" },
|
|
35
|
-
{ value: "fr", label: "FR", flag: "/flags/FR.svg" },
|
|
36
|
-
],
|
|
37
|
-
ariaLabel = "Language",
|
|
38
|
-
class: className,
|
|
39
|
-
}: Props = $props();
|
|
40
|
-
|
|
41
|
-
// =========================
|
|
42
|
-
// DERIVED
|
|
43
|
-
// =========================
|
|
44
|
-
let segments = $derived<TabDef<string>[]>(
|
|
45
|
-
options.map((option) => ({ value: option.value, label: option.label, image: option.flag })),
|
|
46
|
-
);
|
|
47
|
-
</script>
|
|
48
|
-
|
|
49
|
-
<!-- =========================== -->
|
|
50
|
-
<!-- MARKUP -->
|
|
51
|
-
<!-- =========================== -->
|
|
52
|
-
<SegmentedFilter
|
|
53
|
-
{value}
|
|
54
|
-
onValueChange={onchange}
|
|
55
|
-
options={segments}
|
|
56
|
-
block={false}
|
|
57
|
-
{ariaLabel}
|
|
58
|
-
class={className}
|
|
59
|
-
/>
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { cn } from "../../lib/utils.js";
|
|
3
|
-
|
|
4
|
-
interface Props {
|
|
5
|
-
count: number;
|
|
6
|
-
class?: string;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
let { count, class: className }: Props = $props();
|
|
10
|
-
</script>
|
|
11
|
-
|
|
12
|
-
<!-- A count beside a heading: a quiet square, never a pill, so it reads as a number and not a status. -->
|
|
13
|
-
<span
|
|
14
|
-
class={cn(
|
|
15
|
-
"grid h-5 min-w-5 shrink-0 place-items-center rounded-sm bg-foreground/5 px-1 text-xs font-medium text-muted-foreground tabular-nums",
|
|
16
|
-
className,
|
|
17
|
-
)}
|
|
18
|
-
>
|
|
19
|
-
{count.toLocaleString()}
|
|
20
|
-
</span>
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { tradeIconMap, type TradeSlug } from "./trades.js";
|
|
3
|
-
|
|
4
|
-
let {
|
|
5
|
-
trade,
|
|
6
|
-
size = 14,
|
|
7
|
-
stroke = 2,
|
|
8
|
-
class: className = "",
|
|
9
|
-
}: { trade: TradeSlug; size?: number; stroke?: number; class?: string } = $props();
|
|
10
|
-
|
|
11
|
-
let Icon = $derived(tradeIconMap[trade]);
|
|
12
|
-
</script>
|
|
13
|
-
|
|
14
|
-
{#if Icon}
|
|
15
|
-
<Icon {size} {stroke} class={className} aria-hidden="true" focusable="false" />
|
|
16
|
-
{/if}
|
package/src/icons/trades.ts
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Trade icon map. Tabler covers the domain Lucide and Phosphor miss.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
/*
|
|
6
|
-
* Lucide/Phosphor gap audit (Sept 2026) against the 14 trade pills in the "NOT SURE? TAP WHAT YOU DO" strip. Lucide has `hammer`, `wrench`, `hard-hat`, `bolt`, `brick-wall`,
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
/*
|
|
10
|
-
* `building`, `car`, but no `trowel`, no tiling grid, no `window` cleaning `spray/squeegee`, no `crane`, no dedicated `handyman` toolset. Phosphor has `hammer`, `wrench`, `hard-hat`, `paint-roller`, and also no `trowel`.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
/*
|
|
14
|
-
* Tabler 3.46 (6184 icons, MIT) has all 14 at a consistent 2px stroke on 24×24: droplet/pipe, bolt, home (roof), tools, key, window,
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
/*
|
|
18
|
-
* car-garage, hammer / wave-saw-tool, helmet, trowel, grid-dots, leaf, car, crane. Svelte 5 port is ` @tabler/icons-svelte-runes` (runes, not the Svelte 4 ` @tabler/icons-svelte`).
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
import {
|
|
22
|
-
IconBolt,
|
|
23
|
-
IconCar,
|
|
24
|
-
IconCarGarage,
|
|
25
|
-
IconCrane,
|
|
26
|
-
IconDroplet,
|
|
27
|
-
IconGridDots,
|
|
28
|
-
IconHammer,
|
|
29
|
-
IconHelmet,
|
|
30
|
-
IconHome,
|
|
31
|
-
IconKey,
|
|
32
|
-
IconLeaf,
|
|
33
|
-
IconTools,
|
|
34
|
-
IconTrowel,
|
|
35
|
-
IconWindow,
|
|
36
|
-
} from "@tabler/icons-svelte-runes";
|
|
37
|
-
import type { Component } from "svelte";
|
|
38
|
-
|
|
39
|
-
export type TradeSlug =
|
|
40
|
-
| "plumbers"
|
|
41
|
-
| "electricians"
|
|
42
|
-
| "roofers"
|
|
43
|
-
| "handymen"
|
|
44
|
-
| "locksmiths"
|
|
45
|
-
| "window-cleaners"
|
|
46
|
-
| "garages"
|
|
47
|
-
| "carpenters"
|
|
48
|
-
| "builders"
|
|
49
|
-
| "plasterers"
|
|
50
|
-
| "tilers"
|
|
51
|
-
| "gardeners"
|
|
52
|
-
| "mobile-mechanics"
|
|
53
|
-
| "construction-companies";
|
|
54
|
-
|
|
55
|
-
export const tradeIconMap: Record<TradeSlug, Component> = {
|
|
56
|
-
plumbers: IconDroplet,
|
|
57
|
-
electricians: IconBolt,
|
|
58
|
-
roofers: IconHome,
|
|
59
|
-
handymen: IconTools,
|
|
60
|
-
locksmiths: IconKey,
|
|
61
|
-
"window-cleaners": IconWindow,
|
|
62
|
-
garages: IconCarGarage,
|
|
63
|
-
carpenters: IconHammer,
|
|
64
|
-
builders: IconHelmet,
|
|
65
|
-
plasterers: IconTrowel,
|
|
66
|
-
tilers: IconGridDots,
|
|
67
|
-
gardeners: IconLeaf,
|
|
68
|
-
"mobile-mechanics": IconCar,
|
|
69
|
-
"construction-companies": IconCrane,
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
export const tradeLabels: Record<TradeSlug, string> = {
|
|
73
|
-
plumbers: "PLUMBERS",
|
|
74
|
-
electricians: "ELECTRICIANS",
|
|
75
|
-
roofers: "ROOFERS",
|
|
76
|
-
handymen: "HANDYMEN",
|
|
77
|
-
locksmiths: "LOCKSMITHS",
|
|
78
|
-
"window-cleaners": "WINDOW CLEANERS",
|
|
79
|
-
garages: "GARAGES",
|
|
80
|
-
carpenters: "CARPENTERS",
|
|
81
|
-
builders: "BUILDERS",
|
|
82
|
-
plasterers: "PLASTERERS",
|
|
83
|
-
tilers: "TILERS",
|
|
84
|
-
gardeners: "GARDENERS",
|
|
85
|
-
"mobile-mechanics": "MOBILE MECHANICS",
|
|
86
|
-
"construction-companies": "CONSTRUCTION COMPANIES",
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
export const tradeSlugs: TradeSlug[] = Object.keys(tradeIconMap) as TradeSlug[];
|
package/src/status/pill-tint.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { Tone } from "../lib/tone.js";
|
|
2
|
-
|
|
3
|
-
// A quiet tint and no border: the icon and the text carry the tone, the fill only groups
|
|
4
|
-
// them. Neutral reads off the foreground, not `bg-muted`, because muted sits 0.005 L from
|
|
5
|
-
// the canvas and vanishes there (tokens board, "what fights the new scale").
|
|
6
|
-
// It lives in a .ts module, not status-pill.svelte's module script, so a plain tsc consumer
|
|
7
|
-
// can import it: tsc's `*.svelte` shim only knows a default export.
|
|
8
|
-
export const PILL_TINT: Record<Tone, string> = {
|
|
9
|
-
neutral: "bg-foreground/6 text-muted-foreground",
|
|
10
|
-
accent:
|
|
11
|
-
"bg-[color-mix(in_oklab,var(--tone-accent,var(--color-primary))_12%,transparent)] text-[color:var(--tone-accent,var(--color-primary))]",
|
|
12
|
-
info: "bg-info/12 text-info-800 dark:text-info-400",
|
|
13
|
-
success: "bg-success/12 text-success-800 dark:text-success-400",
|
|
14
|
-
warning: "bg-warning/14 text-warning-800 dark:text-warning-400",
|
|
15
|
-
destructive: "bg-destructive/10 text-destructive-ink",
|
|
16
|
-
};
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import type { Component, Snippet } from "svelte";
|
|
3
|
-
import PropertyRow from "../../components/work-parts/property-row.svelte";
|
|
4
|
-
|
|
5
|
-
interface Props {
|
|
6
|
-
label: string;
|
|
7
|
-
/** A Phosphor icon before the label, shown when the rail is wide. */
|
|
8
|
-
icon?: Component<{ class?: string; "aria-hidden"?: boolean | "true" | "false" }>;
|
|
9
|
-
children: Snippet;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
let { label, icon, children }: Props = $props();
|
|
13
|
-
</script>
|
|
14
|
-
|
|
15
|
-
<!-- A labelled row in the rail: icon, label, value. In the narrow chip row the label hides and
|
|
16
|
-
names the group for assistive tech. -->
|
|
17
|
-
<PropertyRow {label} {icon} layout="responsive">{@render children()}</PropertyRow>
|