@ruckc/truss-ui 0.0.0-stage → 1.1.1
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/LICENSE +24 -0
- package/README.md +103 -2
- package/dist/color.d.ts +21 -0
- package/dist/color.js +32 -0
- package/dist/components/Accordion.svelte +70 -0
- package/dist/components/Accordion.svelte.d.ts +15 -0
- package/dist/components/Alert.svelte +46 -0
- package/dist/components/Alert.svelte.d.ts +15 -0
- package/dist/components/Avatar.svelte +32 -0
- package/dist/components/Avatar.svelte.d.ts +9 -0
- package/dist/components/AvatarGroup.svelte +20 -0
- package/dist/components/AvatarGroup.svelte.d.ts +11 -0
- package/dist/components/Badge.svelte +27 -0
- package/dist/components/Badge.svelte.d.ts +9 -0
- package/dist/components/Breadcrumbs.svelte +38 -0
- package/dist/components/Breadcrumbs.svelte.d.ts +12 -0
- package/dist/components/Button.svelte +119 -0
- package/dist/components/Button.svelte.d.ts +16 -0
- package/dist/components/ButtonGroup.svelte +23 -0
- package/dist/components/ButtonGroup.svelte.d.ts +9 -0
- package/dist/components/Calendar.svelte +166 -0
- package/dist/components/Calendar.svelte.d.ts +19 -0
- package/dist/components/Callout.svelte +50 -0
- package/dist/components/Callout.svelte.d.ts +14 -0
- package/dist/components/Card.svelte +50 -0
- package/dist/components/Card.svelte.d.ts +15 -0
- package/dist/components/Checkbox.svelte +37 -0
- package/dist/components/Checkbox.svelte.d.ts +16 -0
- package/dist/components/ColorPicker.svelte +164 -0
- package/dist/components/ColorPicker.svelte.d.ts +14 -0
- package/dist/components/Combobox.svelte +131 -0
- package/dist/components/Combobox.svelte.d.ts +19 -0
- package/dist/components/ContextMenu.svelte +51 -0
- package/dist/components/ContextMenu.svelte.d.ts +10 -0
- package/dist/components/DataTable.svelte +657 -0
- package/dist/components/DataTable.svelte.d.ts +76 -0
- package/dist/components/DatePicker.svelte +70 -0
- package/dist/components/DatePicker.svelte.d.ts +15 -0
- package/dist/components/DateRangePicker.svelte +92 -0
- package/dist/components/DateRangePicker.svelte.d.ts +15 -0
- package/dist/components/DateTimePicker.svelte +62 -0
- package/dist/components/DateTimePicker.svelte.d.ts +19 -0
- package/dist/components/DateTimeRangePicker.svelte +167 -0
- package/dist/components/DateTimeRangePicker.svelte.d.ts +22 -0
- package/dist/components/Dialog.svelte +70 -0
- package/dist/components/Dialog.svelte.d.ts +14 -0
- package/dist/components/DialogHost.svelte +8 -0
- package/dist/components/DialogHost.svelte.d.ts +3 -0
- package/dist/components/Divider.svelte +16 -0
- package/dist/components/Divider.svelte.d.ts +8 -0
- package/dist/components/Drawer.svelte +64 -0
- package/dist/components/Drawer.svelte.d.ts +13 -0
- package/dist/components/DurationInput.svelte +112 -0
- package/dist/components/DurationInput.svelte.d.ts +19 -0
- package/dist/components/EditableText.svelte +73 -0
- package/dist/components/EditableText.svelte.d.ts +14 -0
- package/dist/components/EmptyState.svelte +30 -0
- package/dist/components/EmptyState.svelte.d.ts +12 -0
- package/dist/components/FileUpload.svelte +143 -0
- package/dist/components/FileUpload.svelte.d.ts +18 -0
- package/dist/components/FormGroup.svelte +27 -0
- package/dist/components/FormGroup.svelte.d.ts +13 -0
- package/dist/components/Input.svelte +62 -0
- package/dist/components/Input.svelte.d.ts +15 -0
- package/dist/components/InputAddon.svelte +21 -0
- package/dist/components/InputAddon.svelte.d.ts +9 -0
- package/dist/components/InputGroup.svelte +70 -0
- package/dist/components/InputGroup.svelte.d.ts +16 -0
- package/dist/components/Menu.svelte +95 -0
- package/dist/components/Menu.svelte.d.ts +15 -0
- package/dist/components/MenuButton.svelte +45 -0
- package/dist/components/MenuButton.svelte.d.ts +16 -0
- package/dist/components/MultiSelect.svelte +129 -0
- package/dist/components/MultiSelect.svelte.d.ts +18 -0
- package/dist/components/Navbar.svelte +17 -0
- package/dist/components/Navbar.svelte.d.ts +10 -0
- package/dist/components/NumberInput.svelte +66 -0
- package/dist/components/NumberInput.svelte.d.ts +16 -0
- package/dist/components/OptionList.svelte +74 -0
- package/dist/components/OptionList.svelte.d.ts +15 -0
- package/dist/components/Popover.svelte +87 -0
- package/dist/components/Popover.svelte.d.ts +14 -0
- package/dist/components/ProgressBar.svelte +35 -0
- package/dist/components/ProgressBar.svelte.d.ts +10 -0
- package/dist/components/RadioGroup.svelte +39 -0
- package/dist/components/RadioGroup.svelte.d.ts +14 -0
- package/dist/components/Section.svelte +56 -0
- package/dist/components/Section.svelte.d.ts +15 -0
- package/dist/components/SegmentedControl.svelte +56 -0
- package/dist/components/SegmentedControl.svelte.d.ts +17 -0
- package/dist/components/Select.svelte +204 -0
- package/dist/components/Select.svelte.d.ts +41 -0
- package/dist/components/Shell.svelte +143 -0
- package/dist/components/Shell.svelte.d.ts +36 -0
- package/dist/components/Sidebar.svelte +84 -0
- package/dist/components/Sidebar.svelte.d.ts +24 -0
- package/dist/components/Skeleton.svelte +25 -0
- package/dist/components/Skeleton.svelte.d.ts +9 -0
- package/dist/components/Slider.svelte +121 -0
- package/dist/components/Slider.svelte.d.ts +16 -0
- package/dist/components/Spinner.svelte +30 -0
- package/dist/components/Spinner.svelte.d.ts +9 -0
- package/dist/components/SplitButton.svelte +113 -0
- package/dist/components/SplitButton.svelte.d.ts +14 -0
- package/dist/components/SplitPane.svelte +106 -0
- package/dist/components/SplitPane.svelte.d.ts +16 -0
- package/dist/components/Stepper.svelte +73 -0
- package/dist/components/Stepper.svelte.d.ts +26 -0
- package/dist/components/Switch.svelte +21 -0
- package/dist/components/Switch.svelte.d.ts +10 -0
- package/dist/components/Tabs.svelte +74 -0
- package/dist/components/Tabs.svelte.d.ts +18 -0
- package/dist/components/Tag.svelte +49 -0
- package/dist/components/Tag.svelte.d.ts +16 -0
- package/dist/components/TagInput.svelte +72 -0
- package/dist/components/TagInput.svelte.d.ts +17 -0
- package/dist/components/Textarea.svelte +37 -0
- package/dist/components/Textarea.svelte.d.ts +11 -0
- package/dist/components/TimeList.svelte +76 -0
- package/dist/components/TimeList.svelte.d.ts +10 -0
- package/dist/components/TimePicker.svelte +79 -0
- package/dist/components/TimePicker.svelte.d.ts +15 -0
- package/dist/components/TimeRangePicker.svelte +44 -0
- package/dist/components/TimeRangePicker.svelte.d.ts +17 -0
- package/dist/components/Timeline.svelte +38 -0
- package/dist/components/Timeline.svelte.d.ts +17 -0
- package/dist/components/Toaster.svelte +35 -0
- package/dist/components/Toaster.svelte.d.ts +18 -0
- package/dist/components/Tooltip.svelte +66 -0
- package/dist/components/Tooltip.svelte.d.ts +11 -0
- package/dist/components/Tree.svelte +147 -0
- package/dist/components/Tree.svelte.d.ts +16 -0
- package/dist/components/internal/DialogRequest.svelte +99 -0
- package/dist/components/internal/DialogRequest.svelte.d.ts +7 -0
- package/dist/date.d.ts +19 -0
- package/dist/date.js +43 -0
- package/dist/dialogs.svelte.d.ts +63 -0
- package/dist/dialogs.svelte.js +54 -0
- package/dist/duration.d.ts +11 -0
- package/dist/duration.js +44 -0
- package/dist/fonts/LICENSE-Inter.txt +93 -0
- package/dist/fonts/LICENSE-JetBrainsMono.txt +93 -0
- package/dist/fonts/inter-latin-wght-normal.woff2 +0 -0
- package/dist/fonts/jetbrains-mono-latin-wght-normal.woff2 +0 -0
- package/dist/icons/Icon.svelte +30 -0
- package/dist/icons/Icon.svelte.d.ts +9 -0
- package/dist/icons/paths.d.ts +39 -0
- package/dist/icons/paths.js +43 -0
- package/dist/index.d.ts +72 -0
- package/dist/index.js +67 -0
- package/dist/styles/base.css +71 -0
- package/dist/styles/fonts.css +21 -0
- package/dist/styles/index.css +3 -0
- package/dist/styles/tokens.css +143 -0
- package/dist/time.d.ts +7 -0
- package/dist/time.js +30 -0
- package/dist/toast.svelte.d.ts +22 -0
- package/dist/toast.svelte.js +19 -0
- package/dist/types.d.ts +70 -0
- package/dist/types.js +1 -0
- package/package.json +87 -4
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
let { vertical = false, fill = false, children }: { vertical?: boolean; fill?: boolean; children: Snippet } = $props();
|
|
4
|
+
</script>
|
|
5
|
+
|
|
6
|
+
<div class="bg" role="group" data-vertical={vertical} data-fill={fill}>{@render children()}</div>
|
|
7
|
+
|
|
8
|
+
<style>
|
|
9
|
+
.bg { display: inline-flex; }
|
|
10
|
+
.bg[data-fill='true'] { display: flex; }
|
|
11
|
+
.bg[data-fill='true'] > :global(.tr-btn) { flex: 1; }
|
|
12
|
+
.bg > :global(.tr-btn) { border-radius: 0; }
|
|
13
|
+
.bg:not([data-vertical='true']) > :global(.tr-btn:first-child) { border-start-start-radius: var(--tr-radius-sm); border-end-start-radius: var(--tr-radius-sm); }
|
|
14
|
+
.bg:not([data-vertical='true']) > :global(.tr-btn:last-child) { border-start-end-radius: var(--tr-radius-sm); border-end-end-radius: var(--tr-radius-sm); }
|
|
15
|
+
.bg:not([data-vertical='true']) > :global(.tr-btn + .tr-btn) { margin-left: -1px; }
|
|
16
|
+
.bg[data-vertical='true'] { flex-direction: column; }
|
|
17
|
+
.bg[data-vertical='true'] > :global(.tr-btn:first-child) { border-start-start-radius: var(--tr-radius-sm); border-start-end-radius: var(--tr-radius-sm); }
|
|
18
|
+
.bg[data-vertical='true'] > :global(.tr-btn:last-child) { border-end-start-radius: var(--tr-radius-sm); border-end-end-radius: var(--tr-radius-sm); }
|
|
19
|
+
.bg[data-vertical='true'] > :global(.tr-btn + .tr-btn) { margin-top: -1px; }
|
|
20
|
+
/* hairline seam between joined filled/tonal buttons */
|
|
21
|
+
.bg > :global(.tr-btn:hover), .bg > :global(.tr-btn:focus-visible) { z-index: 1; }
|
|
22
|
+
.bg > :global(.tr-btn[data-variant='filled'] + .tr-btn[data-variant='filled']) { box-shadow: inset 1px 0 0 color-mix(in srgb, var(--tr-surface) 35%, transparent), var(--tr-elevation-1); }
|
|
23
|
+
</style>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
vertical?: boolean;
|
|
4
|
+
fill?: boolean;
|
|
5
|
+
children: Snippet;
|
|
6
|
+
};
|
|
7
|
+
declare const ButtonGroup: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
8
|
+
type ButtonGroup = ReturnType<typeof ButtonGroup>;
|
|
9
|
+
export default ButtonGroup;
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { tick } from 'svelte';
|
|
3
|
+
import Button from './Button.svelte';
|
|
4
|
+
import { addDays, addMonths, monthGrid, parseISO, startOfMonth, toISO, today } from '../date.js';
|
|
5
|
+
import type { DateRange } from '../types.js';
|
|
6
|
+
|
|
7
|
+
let {
|
|
8
|
+
mode = 'single',
|
|
9
|
+
value = $bindable(),
|
|
10
|
+
range = $bindable({}),
|
|
11
|
+
min,
|
|
12
|
+
max,
|
|
13
|
+
weekStart = 0,
|
|
14
|
+
locale = 'en-US',
|
|
15
|
+
disabledDate,
|
|
16
|
+
onpick,
|
|
17
|
+
onrange
|
|
18
|
+
}: {
|
|
19
|
+
mode?: 'single' | 'range';
|
|
20
|
+
value?: string;
|
|
21
|
+
range?: DateRange;
|
|
22
|
+
min?: string;
|
|
23
|
+
max?: string;
|
|
24
|
+
/** 0 = Sunday … 6 = Saturday */
|
|
25
|
+
weekStart?: number;
|
|
26
|
+
locale?: string;
|
|
27
|
+
disabledDate?: (iso: string) => boolean;
|
|
28
|
+
/** Fires when a selection is complete (single pick, or range end chosen). */
|
|
29
|
+
onpick?: () => void;
|
|
30
|
+
/** Called with every range change (including the in-progress first click), for controlled use. */
|
|
31
|
+
onrange?: (r: DateRange) => void;
|
|
32
|
+
} = $props();
|
|
33
|
+
|
|
34
|
+
const seed = () => parseISO((mode === 'range' ? range.start : value) ?? '') ?? new Date();
|
|
35
|
+
let view = $state(startOfMonth(seed()));
|
|
36
|
+
let focused = $state(toISO(seed()));
|
|
37
|
+
let pending = $state<string>();
|
|
38
|
+
let hover = $state<string>();
|
|
39
|
+
let grid: HTMLDivElement;
|
|
40
|
+
|
|
41
|
+
// Follow external changes (typed input, presets)
|
|
42
|
+
$effect(() => {
|
|
43
|
+
const d = parseISO((mode === 'range' ? range.start : value) ?? '');
|
|
44
|
+
if (d) { view = startOfMonth(d); focused = toISO(d); }
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const weeks = $derived(monthGrid(view.getFullYear(), view.getMonth(), weekStart));
|
|
48
|
+
const monthLabel = $derived(new Intl.DateTimeFormat(locale, { month: 'long', year: 'numeric' }).format(view));
|
|
49
|
+
const dayLabel = $derived(new Intl.DateTimeFormat(locale, { dateStyle: 'full' }));
|
|
50
|
+
const weekdays = $derived(
|
|
51
|
+
Array.from({ length: 7 }, (_, i) =>
|
|
52
|
+
new Intl.DateTimeFormat(locale, { weekday: 'short' }).format(new Date(2023, 0, 1 + ((weekStart + i) % 7)))
|
|
53
|
+
)
|
|
54
|
+
);
|
|
55
|
+
const now = today();
|
|
56
|
+
|
|
57
|
+
const bounds = $derived.by(() => {
|
|
58
|
+
if (mode !== 'range') return { lo: undefined, hi: undefined };
|
|
59
|
+
if (pending) {
|
|
60
|
+
const o = hover ?? pending;
|
|
61
|
+
return o < pending ? { lo: o, hi: pending } : { lo: pending, hi: o };
|
|
62
|
+
}
|
|
63
|
+
return { lo: range.start, hi: range.end };
|
|
64
|
+
});
|
|
65
|
+
const isOff = (iso: string) => (!!min && iso < min) || (!!max && iso > max) || !!disabledDate?.(iso);
|
|
66
|
+
|
|
67
|
+
function pick(iso: string) {
|
|
68
|
+
if (isOff(iso)) return;
|
|
69
|
+
focused = iso;
|
|
70
|
+
if (mode === 'single') { value = iso; onpick?.(); return; }
|
|
71
|
+
if (!pending) { pending = iso; range = { start: iso }; onrange?.(range); return; }
|
|
72
|
+
const [s, e] = iso < pending ? [iso, pending] : [pending, iso];
|
|
73
|
+
range = { start: s, end: e };
|
|
74
|
+
onrange?.(range);
|
|
75
|
+
pending = hover = undefined;
|
|
76
|
+
onpick?.();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function jump(next: string, focus = false) {
|
|
80
|
+
focused = next;
|
|
81
|
+
view = startOfMonth(parseISO(next)!);
|
|
82
|
+
if (focus) tick().then(() => grid.querySelector<HTMLElement>(`[data-iso="${next}"]`)?.focus());
|
|
83
|
+
}
|
|
84
|
+
const shift = (months: number) => jump(toISO(addMonths(parseISO(focused)!, months)));
|
|
85
|
+
|
|
86
|
+
function onKey(e: KeyboardEvent) {
|
|
87
|
+
const d = parseISO(focused)!;
|
|
88
|
+
const weekOffset = (d.getDay() - weekStart + 7) % 7;
|
|
89
|
+
const moves: Record<string, Date> = {
|
|
90
|
+
ArrowLeft: addDays(d, -1), ArrowRight: addDays(d, 1), ArrowUp: addDays(d, -7), ArrowDown: addDays(d, 7),
|
|
91
|
+
Home: addDays(d, -weekOffset), End: addDays(d, 6 - weekOffset),
|
|
92
|
+
PageUp: addMonths(d, e.shiftKey ? -12 : -1), PageDown: addMonths(d, e.shiftKey ? 12 : 1)
|
|
93
|
+
};
|
|
94
|
+
if (!(e.key in moves)) return;
|
|
95
|
+
e.preventDefault();
|
|
96
|
+
jump(toISO(moves[e.key]), true);
|
|
97
|
+
}
|
|
98
|
+
</script>
|
|
99
|
+
|
|
100
|
+
<div class="cal">
|
|
101
|
+
<div class="head">
|
|
102
|
+
<Button variant="minimal" intent="neutral" size="sm" icon="chevrons-left" aria-label="Previous year" onclick={() => shift(-12)} />
|
|
103
|
+
<Button variant="minimal" intent="neutral" size="sm" icon="chevron-left" aria-label="Previous month" onclick={() => shift(-1)} />
|
|
104
|
+
<div class="title" aria-live="polite">{monthLabel}</div>
|
|
105
|
+
<Button variant="minimal" intent="neutral" size="sm" icon="chevron-right" aria-label="Next month" onclick={() => shift(1)} />
|
|
106
|
+
<Button variant="minimal" intent="neutral" size="sm" icon="chevrons-right" aria-label="Next year" onclick={() => shift(12)} />
|
|
107
|
+
</div>
|
|
108
|
+
|
|
109
|
+
<div class="grid" role="grid" aria-label={monthLabel} bind:this={grid} onkeydown={onKey} tabindex="-1">
|
|
110
|
+
<div class="row" role="row">
|
|
111
|
+
{#each weekdays as w}<div class="dow" role="columnheader">{w}</div>{/each}
|
|
112
|
+
</div>
|
|
113
|
+
{#each weeks as week}
|
|
114
|
+
<div class="row" role="row">
|
|
115
|
+
{#each week as iso (iso)}
|
|
116
|
+
{@const inRange = !!bounds.lo && !!bounds.hi && iso >= bounds.lo && iso <= bounds.hi}
|
|
117
|
+
{@const edge = iso === bounds.lo || iso === bounds.hi}
|
|
118
|
+
{@const sel = mode === 'single' ? iso === value : edge}
|
|
119
|
+
<div role="gridcell" aria-selected={sel}>
|
|
120
|
+
<button
|
|
121
|
+
type="button"
|
|
122
|
+
class="day"
|
|
123
|
+
data-iso={iso}
|
|
124
|
+
data-outside={iso.slice(0, 7) !== toISO(view).slice(0, 7)}
|
|
125
|
+
data-today={iso === now}
|
|
126
|
+
data-selected={sel}
|
|
127
|
+
data-range={inRange && !edge}
|
|
128
|
+
tabindex={iso === focused ? 0 : -1}
|
|
129
|
+
disabled={isOff(iso)}
|
|
130
|
+
aria-label={dayLabel.format(parseISO(iso)!)}
|
|
131
|
+
aria-current={iso === now ? 'date' : undefined}
|
|
132
|
+
onclick={() => pick(iso)}
|
|
133
|
+
onpointerenter={() => pending && (hover = iso)}
|
|
134
|
+
>{+iso.slice(8)}</button>
|
|
135
|
+
</div>
|
|
136
|
+
{/each}
|
|
137
|
+
</div>
|
|
138
|
+
{/each}
|
|
139
|
+
</div>
|
|
140
|
+
|
|
141
|
+
<div class="foot">
|
|
142
|
+
<Button variant="minimal" size="sm" onclick={() => jump(now, true)}>Today</Button>
|
|
143
|
+
</div>
|
|
144
|
+
</div>
|
|
145
|
+
|
|
146
|
+
<style>
|
|
147
|
+
.cal { width: 252px; padding: 8px; }
|
|
148
|
+
.head { display: flex; align-items: center; gap: 2px; margin-bottom: 6px; }
|
|
149
|
+
.title { flex: 1; text-align: center; font-weight: 600; }
|
|
150
|
+
.grid { outline: none; }
|
|
151
|
+
.row { display: grid; grid-template-columns: repeat(7, 1fr); }
|
|
152
|
+
.dow { height: 24px; display: grid; place-items: center; font-size: var(--tr-text-xs); font-weight: 700; text-transform: uppercase; color: var(--tr-on-surface-variant); }
|
|
153
|
+
[role='gridcell'] { display: contents; }
|
|
154
|
+
.day {
|
|
155
|
+
height: 32px; border: 0; background: none; color: var(--tr-on-surface); font: inherit; font-variant-numeric: tabular-nums;
|
|
156
|
+
border-radius: var(--tr-radius-sm); cursor: pointer; position: relative;
|
|
157
|
+
}
|
|
158
|
+
.day:hover:not(:disabled) { background: var(--tr-surface-container-highest); }
|
|
159
|
+
.day[data-outside='true'] { color: var(--tr-placeholder); }
|
|
160
|
+
.day[data-today='true'] { box-shadow: inset 0 0 0 1px var(--tr-outline); }
|
|
161
|
+
.day[data-range='true'] { background: var(--tr-primary-container); color: var(--tr-on-primary-container); border-radius: 0; }
|
|
162
|
+
.day[data-selected='true'] { background: var(--tr-primary); color: var(--tr-on-primary); font-weight: 700; }
|
|
163
|
+
.day:focus-visible { outline: none; box-shadow: var(--tr-focus-ring); z-index: 1; }
|
|
164
|
+
.day:disabled { opacity: 0.35; cursor: not-allowed; }
|
|
165
|
+
.foot { display: flex; justify-content: flex-end; margin-top: 4px; border-top: 1px solid var(--tr-outline-variant); padding-top: 6px; }
|
|
166
|
+
</style>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { DateRange } from '../types.js';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
mode?: 'single' | 'range';
|
|
4
|
+
value?: string;
|
|
5
|
+
range?: DateRange;
|
|
6
|
+
min?: string;
|
|
7
|
+
max?: string;
|
|
8
|
+
/** 0 = Sunday … 6 = Saturday */
|
|
9
|
+
weekStart?: number;
|
|
10
|
+
locale?: string;
|
|
11
|
+
disabledDate?: (iso: string) => boolean;
|
|
12
|
+
/** Fires when a selection is complete (single pick, or range end chosen). */
|
|
13
|
+
onpick?: () => void;
|
|
14
|
+
/** Called with every range change (including the in-progress first click), for controlled use. */
|
|
15
|
+
onrange?: (r: DateRange) => void;
|
|
16
|
+
};
|
|
17
|
+
declare const Calendar: import("svelte").Component<$$ComponentProps, {}, "value" | "range">;
|
|
18
|
+
type Calendar = ReturnType<typeof Calendar>;
|
|
19
|
+
export default Calendar;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import Icon from '../icons/Icon.svelte';
|
|
4
|
+
import Button from './Button.svelte';
|
|
5
|
+
import type { IconName } from '../icons/paths.js';
|
|
6
|
+
|
|
7
|
+
let {
|
|
8
|
+
intent = 'primary',
|
|
9
|
+
title,
|
|
10
|
+
icon,
|
|
11
|
+
onclose,
|
|
12
|
+
children,
|
|
13
|
+
actions
|
|
14
|
+
}: {
|
|
15
|
+
intent?: 'primary' | 'success' | 'warning' | 'danger' | 'neutral';
|
|
16
|
+
title?: string;
|
|
17
|
+
icon?: IconName;
|
|
18
|
+
/** Shows a dismiss button. */
|
|
19
|
+
onclose?: () => void;
|
|
20
|
+
children?: Snippet;
|
|
21
|
+
actions?: Snippet;
|
|
22
|
+
} = $props();
|
|
23
|
+
const glyph = { primary: 'info', success: 'success', warning: 'warning', danger: 'error', neutral: 'info' } as const;
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<div class="co" data-intent={intent} role={intent === 'danger' || intent === 'warning' ? 'alert' : 'note'}>
|
|
27
|
+
<span class="ic"><Icon name={icon ?? glyph[intent]} size={20} /></span>
|
|
28
|
+
<div class="body">
|
|
29
|
+
{#if title}<div class="title">{title}</div>{/if}
|
|
30
|
+
{#if children}<div class="text">{@render children()}</div>{/if}
|
|
31
|
+
{#if actions}<div class="actions">{@render actions()}</div>{/if}
|
|
32
|
+
</div>
|
|
33
|
+
{#if onclose}<Button variant="minimal" intent="neutral" size="sm" icon="cross" aria-label="Dismiss" onclick={onclose} />{/if}
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<style>
|
|
37
|
+
.co { --c: var(--tr-primary); --cc: var(--tr-primary-container); --on: var(--tr-on-primary-container); display: flex; gap: 10px; padding: 10px 10px 10px 14px; position: relative; background: var(--cc); color: var(--on); border-radius: var(--tr-radius-md); overflow: hidden; }
|
|
38
|
+
.co::before { content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 4px; background: var(--c); }
|
|
39
|
+
.co[data-intent='success'] { --c: var(--tr-success); --cc: var(--tr-success-container); --on: var(--tr-on-success-container); }
|
|
40
|
+
.co[data-intent='warning'] { --c: var(--tr-warning); --cc: var(--tr-warning-container); --on: var(--tr-on-warning-container); }
|
|
41
|
+
.co[data-intent='danger'] { --c: var(--tr-danger); --cc: var(--tr-danger-container); --on: var(--tr-on-danger-container); }
|
|
42
|
+
.co[data-intent='neutral'] { --c: var(--tr-outline); --cc: var(--tr-surface-container-high); --on: var(--tr-on-surface); }
|
|
43
|
+
.ic { flex: none; margin-top: 1px; color: var(--c); }
|
|
44
|
+
.co[data-intent='warning'] .ic, .co[data-intent='success'] .ic, .co[data-intent='danger'] .ic { color: var(--on); }
|
|
45
|
+
.body { flex: 1; min-width: 0; }
|
|
46
|
+
.title { font-weight: 700; }
|
|
47
|
+
.text { line-height: 1.45; }
|
|
48
|
+
.title + .text { margin-top: 2px; }
|
|
49
|
+
.actions { display: flex; gap: 8px; margin-top: 8px; }
|
|
50
|
+
</style>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { IconName } from '../icons/paths.js';
|
|
3
|
+
type $$ComponentProps = {
|
|
4
|
+
intent?: 'primary' | 'success' | 'warning' | 'danger' | 'neutral';
|
|
5
|
+
title?: string;
|
|
6
|
+
icon?: IconName;
|
|
7
|
+
/** Shows a dismiss button. */
|
|
8
|
+
onclose?: () => void;
|
|
9
|
+
children?: Snippet;
|
|
10
|
+
actions?: Snippet;
|
|
11
|
+
};
|
|
12
|
+
declare const Callout: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
13
|
+
type Callout = ReturnType<typeof Callout>;
|
|
14
|
+
export default Callout;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
let {
|
|
5
|
+
padding = 'md',
|
|
6
|
+
elevation = 1,
|
|
7
|
+
href,
|
|
8
|
+
onclick,
|
|
9
|
+
header,
|
|
10
|
+
footer,
|
|
11
|
+
children
|
|
12
|
+
}: {
|
|
13
|
+
padding?: 'none' | 'sm' | 'md';
|
|
14
|
+
elevation?: 0 | 1 | 2 | 3;
|
|
15
|
+
/** Makes the card a link. */
|
|
16
|
+
href?: string;
|
|
17
|
+
/** Makes the card a button. */
|
|
18
|
+
onclick?: (e: MouseEvent) => void;
|
|
19
|
+
header?: Snippet;
|
|
20
|
+
footer?: Snippet;
|
|
21
|
+
children?: Snippet;
|
|
22
|
+
} = $props();
|
|
23
|
+
const interactive = $derived(!!href || !!onclick);
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
{#snippet inner()}
|
|
27
|
+
{#if header}<div class="hd">{@render header()}</div>{/if}
|
|
28
|
+
{#if children}<div class="bd" data-padding={padding}>{@render children()}</div>{/if}
|
|
29
|
+
{#if footer}<div class="ft">{@render footer()}</div>{/if}
|
|
30
|
+
{/snippet}
|
|
31
|
+
|
|
32
|
+
{#if href}<a class="card" {href} data-interactive="true" data-elev={elevation}>{@render inner()}</a>
|
|
33
|
+
{:else if onclick}<button type="button" class="card" {onclick} data-interactive="true" data-elev={elevation}>{@render inner()}</button>
|
|
34
|
+
{:else}<div class="card" data-elev={elevation}>{@render inner()}</div>{/if}
|
|
35
|
+
|
|
36
|
+
<style>
|
|
37
|
+
.card { display: block; width: 100%; text-align: left; color: inherit; text-decoration: none; font: inherit; background: var(--tr-surface-container-low); border: 1px solid var(--tr-outline-variant); border-radius: var(--tr-radius-md); overflow: hidden; transition: box-shadow var(--tr-dur) var(--tr-ease), border-color var(--tr-dur) var(--tr-ease), transform var(--tr-dur-fast) var(--tr-ease); }
|
|
38
|
+
.card[data-elev='1'] { box-shadow: var(--tr-elevation-1); }
|
|
39
|
+
.card[data-elev='2'] { box-shadow: var(--tr-elevation-2); }
|
|
40
|
+
.card[data-elev='3'] { box-shadow: var(--tr-elevation-3); }
|
|
41
|
+
.card[data-interactive='true'] { cursor: pointer; }
|
|
42
|
+
.card[data-interactive='true']:hover { border-color: var(--tr-outline); box-shadow: var(--tr-elevation-2); }
|
|
43
|
+
.card[data-interactive='true']:active { transform: scale(0.995); }
|
|
44
|
+
.card[data-interactive='true']:focus-visible { outline: none; box-shadow: var(--tr-focus-ring); border-color: var(--tr-primary); }
|
|
45
|
+
.hd, .ft { padding: 8px 12px; background: var(--tr-surface-container); }
|
|
46
|
+
.hd { border-bottom: 1px solid var(--tr-outline-variant); font-weight: 700; }
|
|
47
|
+
.ft { border-top: 1px solid var(--tr-outline-variant); }
|
|
48
|
+
.bd[data-padding='md'] { padding: 16px; }
|
|
49
|
+
.bd[data-padding='sm'] { padding: 8px 12px; }
|
|
50
|
+
</style>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
padding?: 'none' | 'sm' | 'md';
|
|
4
|
+
elevation?: 0 | 1 | 2 | 3;
|
|
5
|
+
/** Makes the card a link. */
|
|
6
|
+
href?: string;
|
|
7
|
+
/** Makes the card a button. */
|
|
8
|
+
onclick?: (e: MouseEvent) => void;
|
|
9
|
+
header?: Snippet;
|
|
10
|
+
footer?: Snippet;
|
|
11
|
+
children?: Snippet;
|
|
12
|
+
};
|
|
13
|
+
declare const Card: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
14
|
+
type Card = ReturnType<typeof Card>;
|
|
15
|
+
export default Card;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import Icon from '../icons/Icon.svelte';
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
checked = $bindable(false),
|
|
7
|
+
indeterminate = false,
|
|
8
|
+
disabled = false,
|
|
9
|
+
hint,
|
|
10
|
+
onchange,
|
|
11
|
+
children,
|
|
12
|
+
...rest
|
|
13
|
+
}: { checked?: boolean; indeterminate?: boolean; disabled?: boolean; hint?: string; onchange?: (e: Event & { currentTarget: HTMLInputElement }) => void; children?: Snippet; 'aria-label'?: string; tabindex?: number } = $props();
|
|
14
|
+
|
|
15
|
+
let el: HTMLInputElement;
|
|
16
|
+
$effect(() => { if (el) el.indeterminate = indeterminate; });
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<label class="cb" data-disabled={disabled}>
|
|
20
|
+
<input bind:this={el} type="checkbox" class="tr-visually-hidden" bind:checked {disabled} {onchange} {...rest} />
|
|
21
|
+
<span class="box" data-on={checked || indeterminate}>
|
|
22
|
+
{#if indeterminate}<Icon name="minus" />{:else if checked}<Icon name="tick" />{/if}
|
|
23
|
+
</span>
|
|
24
|
+
{#if children}<span class="text">{@render children()}{#if hint}<small>{hint}</small>{/if}</span>{/if}
|
|
25
|
+
</label>
|
|
26
|
+
|
|
27
|
+
<style>
|
|
28
|
+
.cb { display: inline-flex; align-items: flex-start; gap: 8px; min-height: 24px; cursor: pointer; }
|
|
29
|
+
.cb[data-disabled='true'] { opacity: 0.5; cursor: not-allowed; }
|
|
30
|
+
.box { flex: none; display: grid; place-items: center; width: 18px; height: 18px; margin-top: 3px; border: 1px solid var(--tr-outline); border-radius: var(--tr-radius-sm); background: var(--tr-surface-container-low); color: var(--tr-on-primary); transition: background-color var(--tr-dur) var(--tr-ease), box-shadow var(--tr-dur) var(--tr-ease); }
|
|
31
|
+
.box :global(svg) { width: 14px; height: 14px; }
|
|
32
|
+
.cb:hover .box { background: var(--tr-surface-container-high); }
|
|
33
|
+
.box[data-on='true'] { background: var(--tr-primary); border-color: var(--tr-primary); }
|
|
34
|
+
input:focus-visible + .box { box-shadow: var(--tr-focus-ring); }
|
|
35
|
+
.text { display: flex; flex-direction: column; padding-top: 3px; }
|
|
36
|
+
small { color: var(--tr-on-surface-variant); font-size: var(--tr-text-sm); }
|
|
37
|
+
</style>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
checked?: boolean;
|
|
4
|
+
indeterminate?: boolean;
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
hint?: string;
|
|
7
|
+
onchange?: (e: Event & {
|
|
8
|
+
currentTarget: HTMLInputElement;
|
|
9
|
+
}) => void;
|
|
10
|
+
children?: Snippet;
|
|
11
|
+
'aria-label'?: string;
|
|
12
|
+
tabindex?: number;
|
|
13
|
+
};
|
|
14
|
+
declare const Checkbox: import("svelte").Component<$$ComponentProps, {}, "checked">;
|
|
15
|
+
type Checkbox = ReturnType<typeof Checkbox>;
|
|
16
|
+
export default Checkbox;
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { untrack } from 'svelte';
|
|
3
|
+
import Button from './Button.svelte';
|
|
4
|
+
import Popover from './Popover.svelte';
|
|
5
|
+
import { clamp, hsvToRgb, parseHex, rgbToHsv, toHex, type HSV } from '../color.js';
|
|
6
|
+
|
|
7
|
+
let {
|
|
8
|
+
value = $bindable('#1f5fbf'),
|
|
9
|
+
label,
|
|
10
|
+
ariaLabel,
|
|
11
|
+
alpha = false,
|
|
12
|
+
swatches = ['#1f5fbf', '#2a78d6', '#1baf7a', '#008300', '#eda100', '#eb6834', '#e34948', '#e87ba4', '#6250d6', '#1a1c22', '#7a7f8e', '#ffffff'],
|
|
13
|
+
disabled = false,
|
|
14
|
+
error
|
|
15
|
+
}: {
|
|
16
|
+
/** #rrggbb, or #rrggbbaa when `alpha` is on and opacity < 1. */
|
|
17
|
+
value?: string;
|
|
18
|
+
label?: string;
|
|
19
|
+
/** Accessible name when no visible `label` is shown. */
|
|
20
|
+
ariaLabel?: string;
|
|
21
|
+
alpha?: boolean;
|
|
22
|
+
swatches?: string[];
|
|
23
|
+
disabled?: boolean;
|
|
24
|
+
error?: string;
|
|
25
|
+
} = $props();
|
|
26
|
+
|
|
27
|
+
const id = $props.id();
|
|
28
|
+
let open = $state(false);
|
|
29
|
+
let hsv = $state<HSV>({ h: 210, s: 0.7, v: 0.75 });
|
|
30
|
+
let a = $state(1);
|
|
31
|
+
let text = $state('');
|
|
32
|
+
let bad = $state(false);
|
|
33
|
+
let input: HTMLInputElement;
|
|
34
|
+
let sv = $state<HTMLDivElement>();
|
|
35
|
+
let hueEl = $state<HTMLDivElement>();
|
|
36
|
+
let alphaEl = $state<HTMLDivElement>();
|
|
37
|
+
|
|
38
|
+
const serialize = (h: HSV = hsv, al = a) => toHex(hsvToRgb(h), alpha ? al : undefined);
|
|
39
|
+
const rgb = $derived(hsvToRgb(hsv));
|
|
40
|
+
const solid = $derived(`rgb(${rgb.r} ${rgb.g} ${rgb.b})`);
|
|
41
|
+
|
|
42
|
+
// Follow external value changes without clobbering hue when s/v collapse (grey, black)
|
|
43
|
+
$effect(() => {
|
|
44
|
+
const v = value;
|
|
45
|
+
untrack(() => {
|
|
46
|
+
if (v && v.toLowerCase() === serialize().toLowerCase()) { text = v; return; }
|
|
47
|
+
const p = v ? parseHex(v) : null;
|
|
48
|
+
if (p) { const n = rgbToHsv(p); hsv = { h: n.s === 0 || n.v === 0 ? hsv.h : n.h, s: n.s, v: n.v }; a = p.a; text = v; bad = false; }
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
function commit(h = hsv, al = a) { hsv = h; a = al; value = serialize(h, al); text = value; bad = false; }
|
|
52
|
+
|
|
53
|
+
function drag(el: HTMLElement | undefined, e: PointerEvent, apply: (fx: number, fy: number) => void) {
|
|
54
|
+
if (!el || disabled) return;
|
|
55
|
+
el.setPointerCapture(e.pointerId);
|
|
56
|
+
const go = (ev: PointerEvent) => {
|
|
57
|
+
const r = el.getBoundingClientRect();
|
|
58
|
+
apply(clamp((ev.clientX - r.left) / r.width), clamp((ev.clientY - r.top) / r.height));
|
|
59
|
+
};
|
|
60
|
+
go(e);
|
|
61
|
+
const up = () => { el.removeEventListener('pointermove', go); el.removeEventListener('pointerup', up); };
|
|
62
|
+
el.addEventListener('pointermove', go);
|
|
63
|
+
el.addEventListener('pointerup', up);
|
|
64
|
+
}
|
|
65
|
+
const step = (e: KeyboardEvent, small: number) => (e.shiftKey ? small * 10 : small);
|
|
66
|
+
|
|
67
|
+
function svKey(e: KeyboardEvent) {
|
|
68
|
+
const d = step(e, 0.01);
|
|
69
|
+
const m: Record<string, [number, number]> = { ArrowLeft: [-d, 0], ArrowRight: [d, 0], ArrowUp: [0, d], ArrowDown: [0, -d] };
|
|
70
|
+
if (!(e.key in m)) return;
|
|
71
|
+
e.preventDefault();
|
|
72
|
+
commit({ ...hsv, s: clamp(hsv.s + m[e.key][0]), v: clamp(hsv.v + m[e.key][1]) });
|
|
73
|
+
}
|
|
74
|
+
function railKey(e: KeyboardEvent, cur: number, max: number, small: number, set: (n: number) => void) {
|
|
75
|
+
const d = e.key === 'ArrowRight' || e.key === 'ArrowUp' ? step(e, small) : e.key === 'ArrowLeft' || e.key === 'ArrowDown' ? -step(e, small) : e.key === 'Home' ? -max : e.key === 'End' ? max : 0;
|
|
76
|
+
if (!d) return;
|
|
77
|
+
e.preventDefault();
|
|
78
|
+
set(clamp(cur + d, 0, max));
|
|
79
|
+
}
|
|
80
|
+
function commitText() {
|
|
81
|
+
const p = parseHex(text);
|
|
82
|
+
if (!p) { bad = !!text.trim(); return; }
|
|
83
|
+
const n = rgbToHsv(p);
|
|
84
|
+
commit({ h: n.s === 0 || n.v === 0 ? hsv.h : n.h, s: n.s, v: n.v }, alpha ? p.a : 1);
|
|
85
|
+
}
|
|
86
|
+
const hasDropper = typeof window !== 'undefined' && 'EyeDropper' in window;
|
|
87
|
+
async function pipette() {
|
|
88
|
+
try {
|
|
89
|
+
const r = await new (window as any).EyeDropper().open();
|
|
90
|
+
const p = parseHex(r.sRGBHex);
|
|
91
|
+
if (p) { const n = rgbToHsv(p); commit({ h: n.s === 0 || n.v === 0 ? hsv.h : n.h, s: n.s, v: n.v }); }
|
|
92
|
+
} catch { /* cancelled */ }
|
|
93
|
+
}
|
|
94
|
+
</script>
|
|
95
|
+
|
|
96
|
+
<div class="tr-field">
|
|
97
|
+
{#if label}<label class="tr-label" for={id}>{label}</label>{/if}
|
|
98
|
+
<div class="tr-anchor">
|
|
99
|
+
<div class="tr-control" data-invalid={!!error || bad} data-disabled={disabled}>
|
|
100
|
+
<button type="button" class="sw" {disabled} aria-label="Open color picker" aria-haspopup="dialog" aria-expanded={open} onclick={() => (open = !open)}>
|
|
101
|
+
<span class="chk"></span><span class="col" style:background={value}></span>
|
|
102
|
+
</button>
|
|
103
|
+
<input class="tr-bare mono" {id} aria-label={label ? undefined : ariaLabel} bind:this={input} bind:value={text} {disabled} spellcheck="false" autocomplete="off" maxlength="9"
|
|
104
|
+
aria-invalid={bad || undefined} onblur={commitText} onkeydown={(e) => e.key === 'Enter' && commitText()} />
|
|
105
|
+
</div>
|
|
106
|
+
<Popover bind:open onclose={(r) => r && input.focus()}>
|
|
107
|
+
<div class="panel">
|
|
108
|
+
<div
|
|
109
|
+
class="sv" bind:this={sv} role="slider" tabindex="0" aria-label="Saturation and brightness"
|
|
110
|
+
aria-valuetext="saturation {Math.round(hsv.s * 100)}%, brightness {Math.round(hsv.v * 100)}%" aria-valuenow={Math.round(hsv.s * 100)}
|
|
111
|
+
style:background-color="hsl({hsv.h} 100% 50%)" onpointerdown={(e) => drag(sv, e, (x, y) => commit({ ...hsv, s: x, v: 1 - y }))} onkeydown={svKey}
|
|
112
|
+
>
|
|
113
|
+
<span class="thumb" style:left="{hsv.s * 100}%" style:top="{(1 - hsv.v) * 100}%" style:background={solid}></span>
|
|
114
|
+
</div>
|
|
115
|
+
<div
|
|
116
|
+
class="rail hue" bind:this={hueEl} role="slider" tabindex="0" aria-label="Hue" aria-valuemin="0" aria-valuemax="360" aria-valuenow={Math.round(hsv.h)}
|
|
117
|
+
onpointerdown={(e) => drag(hueEl, e, (x) => commit({ ...hsv, h: x * 360 }))} onkeydown={(e) => railKey(e, hsv.h, 360, 1, (h) => commit({ ...hsv, h }))}
|
|
118
|
+
><span class="thumb" style:left="{(hsv.h / 360) * 100}%" style:background="hsl({hsv.h} 100% 50%)"></span></div>
|
|
119
|
+
{#if alpha}
|
|
120
|
+
<div
|
|
121
|
+
class="rail alpha" bind:this={alphaEl} role="slider" tabindex="0" aria-label="Opacity" aria-valuemin="0" aria-valuemax="100" aria-valuenow={Math.round(a * 100)}
|
|
122
|
+
style:--c={solid} onpointerdown={(e) => drag(alphaEl, e, (x) => commit(hsv, Math.round(x * 100) / 100))} onkeydown={(e) => railKey(e, a, 1, 0.01, (n) => commit(hsv, n))}
|
|
123
|
+
><span class="thumb" style:left="{a * 100}%" style:background={solid}></span></div>
|
|
124
|
+
{/if}
|
|
125
|
+
<div class="row">
|
|
126
|
+
<div class="prev"><span class="chk"></span><span class="col" style:background={value}></span></div>
|
|
127
|
+
<div class="rgb">{rgb.r}, {rgb.g}, {rgb.b}{alpha ? `, ${a.toFixed(2)}` : ''}</div>
|
|
128
|
+
{#if hasDropper}<Button size="sm" variant="minimal" intent="neutral" icon="dropper" aria-label="Pick color from screen" onclick={pipette} />{/if}
|
|
129
|
+
</div>
|
|
130
|
+
{#if swatches.length}
|
|
131
|
+
<div class="swatches" role="group" aria-label="Swatches">
|
|
132
|
+
{#each swatches as s}
|
|
133
|
+
<button type="button" class="s" style:background={s} aria-label={s} aria-pressed={value?.toLowerCase().slice(0, 7) === s.toLowerCase()} onclick={() => { const p = parseHex(s)!; const n = rgbToHsv(p); commit({ h: n.s === 0 || n.v === 0 ? hsv.h : n.h, s: n.s, v: n.v }); }}></button>
|
|
134
|
+
{/each}
|
|
135
|
+
</div>
|
|
136
|
+
{/if}
|
|
137
|
+
</div>
|
|
138
|
+
</Popover>
|
|
139
|
+
</div>
|
|
140
|
+
{#if error || bad}<div class="tr-hint" data-error="true">{error ?? 'Enter a hex color like #1f5fbf'}</div>{/if}
|
|
141
|
+
</div>
|
|
142
|
+
|
|
143
|
+
<style>
|
|
144
|
+
.chk { position: absolute; inset: 0; background: repeating-conic-gradient(var(--tr-outline-variant) 0 25%, transparent 0 50%) 0 0 / 8px 8px; }
|
|
145
|
+
.col { position: absolute; inset: 0; }
|
|
146
|
+
.sw { position: relative; flex: none; width: 20px; height: 20px; padding: 0; border: 1px solid var(--tr-outline); border-radius: var(--tr-radius-sm); overflow: hidden; cursor: pointer; }
|
|
147
|
+
.sw:focus-visible { outline: none; box-shadow: var(--tr-focus-ring); }
|
|
148
|
+
.mono { font-family: var(--tr-font-mono); text-transform: lowercase; }
|
|
149
|
+
.panel { width: 248px; padding: 10px; display: grid; gap: 10px; }
|
|
150
|
+
.sv { position: relative; height: 150px; border-radius: var(--tr-radius-sm); cursor: crosshair; touch-action: none; outline: none; background-image: linear-gradient(to top, #000, transparent), linear-gradient(to right, #fff, transparent); }
|
|
151
|
+
.rail { position: relative; height: 12px; border-radius: 6px; cursor: pointer; touch-action: none; outline: none; }
|
|
152
|
+
.hue { background: linear-gradient(to right, #f00, #ff0, #0f0, #0ff, #00f, #f0f, #f00); }
|
|
153
|
+
.alpha { background: linear-gradient(to right, transparent, var(--c)), repeating-conic-gradient(var(--tr-outline-variant) 0 25%, transparent 0 50%) 0 0 / 8px 8px; }
|
|
154
|
+
.thumb { position: absolute; width: 16px; height: 16px; margin: -8px 0 0 -8px; border-radius: 50%; border: 2px solid #fff; box-shadow: 0 0 0 1px rgb(0 0 0 / 0.45), var(--tr-elevation-1); pointer-events: none; }
|
|
155
|
+
.rail .thumb { top: 50%; }
|
|
156
|
+
.sv:focus-visible, .rail:focus-visible { box-shadow: var(--tr-focus-ring); }
|
|
157
|
+
.row { display: flex; align-items: center; gap: 8px; }
|
|
158
|
+
.prev { position: relative; width: 28px; height: 28px; border: 1px solid var(--tr-outline); border-radius: var(--tr-radius-sm); overflow: hidden; }
|
|
159
|
+
.rgb { flex: 1; font: var(--tr-text-sm) var(--tr-font-mono); color: var(--tr-on-surface-variant); }
|
|
160
|
+
.swatches { display: grid; grid-template-columns: repeat(6, 1fr); gap: 6px; }
|
|
161
|
+
.s { aspect-ratio: 1; border: 1px solid var(--tr-outline-variant); border-radius: var(--tr-radius-sm); cursor: pointer; }
|
|
162
|
+
.s[aria-pressed='true'] { box-shadow: 0 0 0 2px var(--tr-surface), 0 0 0 4px var(--tr-primary); }
|
|
163
|
+
.s:focus-visible { outline: none; box-shadow: var(--tr-focus-ring); }
|
|
164
|
+
</style>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
type $$ComponentProps = {
|
|
2
|
+
/** #rrggbb, or #rrggbbaa when `alpha` is on and opacity < 1. */
|
|
3
|
+
value?: string;
|
|
4
|
+
label?: string;
|
|
5
|
+
/** Accessible name when no visible `label` is shown. */
|
|
6
|
+
ariaLabel?: string;
|
|
7
|
+
alpha?: boolean;
|
|
8
|
+
swatches?: string[];
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
error?: string;
|
|
11
|
+
};
|
|
12
|
+
declare const ColorPicker: import("svelte").Component<$$ComponentProps, {}, "value">;
|
|
13
|
+
type ColorPicker = ReturnType<typeof ColorPicker>;
|
|
14
|
+
export default ColorPicker;
|