@ruckc/truss-ui 0.0.0-stage → 1.0.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.
Files changed (155) hide show
  1. package/LICENSE +24 -0
  2. package/README.md +91 -2
  3. package/dist/color.d.ts +21 -0
  4. package/dist/color.js +32 -0
  5. package/dist/components/Accordion.svelte +70 -0
  6. package/dist/components/Accordion.svelte.d.ts +15 -0
  7. package/dist/components/Alert.svelte +46 -0
  8. package/dist/components/Alert.svelte.d.ts +15 -0
  9. package/dist/components/Avatar.svelte +32 -0
  10. package/dist/components/Avatar.svelte.d.ts +9 -0
  11. package/dist/components/AvatarGroup.svelte +20 -0
  12. package/dist/components/AvatarGroup.svelte.d.ts +11 -0
  13. package/dist/components/Badge.svelte +27 -0
  14. package/dist/components/Badge.svelte.d.ts +9 -0
  15. package/dist/components/Breadcrumbs.svelte +38 -0
  16. package/dist/components/Breadcrumbs.svelte.d.ts +12 -0
  17. package/dist/components/Button.svelte +119 -0
  18. package/dist/components/Button.svelte.d.ts +16 -0
  19. package/dist/components/ButtonGroup.svelte +23 -0
  20. package/dist/components/ButtonGroup.svelte.d.ts +9 -0
  21. package/dist/components/Calendar.svelte +166 -0
  22. package/dist/components/Calendar.svelte.d.ts +19 -0
  23. package/dist/components/Callout.svelte +50 -0
  24. package/dist/components/Callout.svelte.d.ts +14 -0
  25. package/dist/components/Card.svelte +50 -0
  26. package/dist/components/Card.svelte.d.ts +15 -0
  27. package/dist/components/Checkbox.svelte +37 -0
  28. package/dist/components/Checkbox.svelte.d.ts +16 -0
  29. package/dist/components/ColorPicker.svelte +164 -0
  30. package/dist/components/ColorPicker.svelte.d.ts +14 -0
  31. package/dist/components/Combobox.svelte +131 -0
  32. package/dist/components/Combobox.svelte.d.ts +19 -0
  33. package/dist/components/ContextMenu.svelte +51 -0
  34. package/dist/components/ContextMenu.svelte.d.ts +10 -0
  35. package/dist/components/DataTable.svelte +657 -0
  36. package/dist/components/DataTable.svelte.d.ts +76 -0
  37. package/dist/components/DatePicker.svelte +70 -0
  38. package/dist/components/DatePicker.svelte.d.ts +15 -0
  39. package/dist/components/DateRangePicker.svelte +92 -0
  40. package/dist/components/DateRangePicker.svelte.d.ts +15 -0
  41. package/dist/components/DateTimePicker.svelte +62 -0
  42. package/dist/components/DateTimePicker.svelte.d.ts +19 -0
  43. package/dist/components/DateTimeRangePicker.svelte +167 -0
  44. package/dist/components/DateTimeRangePicker.svelte.d.ts +22 -0
  45. package/dist/components/Dialog.svelte +70 -0
  46. package/dist/components/Dialog.svelte.d.ts +14 -0
  47. package/dist/components/Divider.svelte +16 -0
  48. package/dist/components/Divider.svelte.d.ts +8 -0
  49. package/dist/components/Drawer.svelte +64 -0
  50. package/dist/components/Drawer.svelte.d.ts +13 -0
  51. package/dist/components/DurationInput.svelte +112 -0
  52. package/dist/components/DurationInput.svelte.d.ts +19 -0
  53. package/dist/components/EditableText.svelte +73 -0
  54. package/dist/components/EditableText.svelte.d.ts +14 -0
  55. package/dist/components/EmptyState.svelte +30 -0
  56. package/dist/components/EmptyState.svelte.d.ts +12 -0
  57. package/dist/components/FileUpload.svelte +143 -0
  58. package/dist/components/FileUpload.svelte.d.ts +18 -0
  59. package/dist/components/FormGroup.svelte +27 -0
  60. package/dist/components/FormGroup.svelte.d.ts +13 -0
  61. package/dist/components/Input.svelte +62 -0
  62. package/dist/components/Input.svelte.d.ts +15 -0
  63. package/dist/components/InputAddon.svelte +21 -0
  64. package/dist/components/InputAddon.svelte.d.ts +9 -0
  65. package/dist/components/InputGroup.svelte +70 -0
  66. package/dist/components/InputGroup.svelte.d.ts +16 -0
  67. package/dist/components/Menu.svelte +95 -0
  68. package/dist/components/Menu.svelte.d.ts +15 -0
  69. package/dist/components/MenuButton.svelte +45 -0
  70. package/dist/components/MenuButton.svelte.d.ts +16 -0
  71. package/dist/components/MultiSelect.svelte +129 -0
  72. package/dist/components/MultiSelect.svelte.d.ts +18 -0
  73. package/dist/components/Navbar.svelte +17 -0
  74. package/dist/components/Navbar.svelte.d.ts +10 -0
  75. package/dist/components/NumberInput.svelte +66 -0
  76. package/dist/components/NumberInput.svelte.d.ts +16 -0
  77. package/dist/components/OptionList.svelte +74 -0
  78. package/dist/components/OptionList.svelte.d.ts +15 -0
  79. package/dist/components/Popover.svelte +87 -0
  80. package/dist/components/Popover.svelte.d.ts +14 -0
  81. package/dist/components/ProgressBar.svelte +35 -0
  82. package/dist/components/ProgressBar.svelte.d.ts +10 -0
  83. package/dist/components/RadioGroup.svelte +39 -0
  84. package/dist/components/RadioGroup.svelte.d.ts +14 -0
  85. package/dist/components/Section.svelte +56 -0
  86. package/dist/components/Section.svelte.d.ts +15 -0
  87. package/dist/components/SegmentedControl.svelte +56 -0
  88. package/dist/components/SegmentedControl.svelte.d.ts +17 -0
  89. package/dist/components/Select.svelte +204 -0
  90. package/dist/components/Select.svelte.d.ts +41 -0
  91. package/dist/components/Shell.svelte +143 -0
  92. package/dist/components/Shell.svelte.d.ts +36 -0
  93. package/dist/components/Sidebar.svelte +84 -0
  94. package/dist/components/Sidebar.svelte.d.ts +24 -0
  95. package/dist/components/Skeleton.svelte +25 -0
  96. package/dist/components/Skeleton.svelte.d.ts +9 -0
  97. package/dist/components/Slider.svelte +121 -0
  98. package/dist/components/Slider.svelte.d.ts +16 -0
  99. package/dist/components/Spinner.svelte +30 -0
  100. package/dist/components/Spinner.svelte.d.ts +9 -0
  101. package/dist/components/SplitButton.svelte +113 -0
  102. package/dist/components/SplitButton.svelte.d.ts +14 -0
  103. package/dist/components/SplitPane.svelte +106 -0
  104. package/dist/components/SplitPane.svelte.d.ts +16 -0
  105. package/dist/components/Stepper.svelte +73 -0
  106. package/dist/components/Stepper.svelte.d.ts +26 -0
  107. package/dist/components/Switch.svelte +21 -0
  108. package/dist/components/Switch.svelte.d.ts +10 -0
  109. package/dist/components/Tabs.svelte +74 -0
  110. package/dist/components/Tabs.svelte.d.ts +18 -0
  111. package/dist/components/Tag.svelte +49 -0
  112. package/dist/components/Tag.svelte.d.ts +16 -0
  113. package/dist/components/TagInput.svelte +72 -0
  114. package/dist/components/TagInput.svelte.d.ts +17 -0
  115. package/dist/components/Textarea.svelte +37 -0
  116. package/dist/components/Textarea.svelte.d.ts +11 -0
  117. package/dist/components/TimeList.svelte +76 -0
  118. package/dist/components/TimeList.svelte.d.ts +10 -0
  119. package/dist/components/TimePicker.svelte +79 -0
  120. package/dist/components/TimePicker.svelte.d.ts +15 -0
  121. package/dist/components/TimeRangePicker.svelte +44 -0
  122. package/dist/components/TimeRangePicker.svelte.d.ts +17 -0
  123. package/dist/components/Timeline.svelte +38 -0
  124. package/dist/components/Timeline.svelte.d.ts +17 -0
  125. package/dist/components/Toaster.svelte +35 -0
  126. package/dist/components/Toaster.svelte.d.ts +18 -0
  127. package/dist/components/Tooltip.svelte +66 -0
  128. package/dist/components/Tooltip.svelte.d.ts +11 -0
  129. package/dist/components/Tree.svelte +147 -0
  130. package/dist/components/Tree.svelte.d.ts +16 -0
  131. package/dist/date.d.ts +19 -0
  132. package/dist/date.js +43 -0
  133. package/dist/duration.d.ts +11 -0
  134. package/dist/duration.js +44 -0
  135. package/dist/fonts/LICENSE-Inter.txt +93 -0
  136. package/dist/fonts/LICENSE-JetBrainsMono.txt +93 -0
  137. package/dist/fonts/inter-latin-wght-normal.woff2 +0 -0
  138. package/dist/fonts/jetbrains-mono-latin-wght-normal.woff2 +0 -0
  139. package/dist/icons/Icon.svelte +30 -0
  140. package/dist/icons/Icon.svelte.d.ts +9 -0
  141. package/dist/icons/paths.d.ts +39 -0
  142. package/dist/icons/paths.js +43 -0
  143. package/dist/index.d.ts +69 -0
  144. package/dist/index.js +65 -0
  145. package/dist/styles/base.css +71 -0
  146. package/dist/styles/fonts.css +21 -0
  147. package/dist/styles/index.css +3 -0
  148. package/dist/styles/tokens.css +143 -0
  149. package/dist/time.d.ts +7 -0
  150. package/dist/time.js +30 -0
  151. package/dist/toast.svelte.d.ts +22 -0
  152. package/dist/toast.svelte.js +19 -0
  153. package/dist/types.d.ts +70 -0
  154. package/dist/types.js +1 -0
  155. package/package.json +76 -5
@@ -0,0 +1,131 @@
1
+ <script lang="ts">
2
+ import { SvelteMap } from 'svelte/reactivity';
3
+ import Button from './Button.svelte';
4
+ import Popover from './Popover.svelte';
5
+ import OptionList from './OptionList.svelte';
6
+ import type { Option } from '../types.js';
7
+
8
+ let {
9
+ options = [],
10
+ loadOptions,
11
+ value = $bindable(),
12
+ label,
13
+ ariaLabel,
14
+ placeholder = 'Type to search…',
15
+ allowCustom = false,
16
+ disabled = false,
17
+ error,
18
+ debounce = 200
19
+ }: {
20
+ options?: Option[];
21
+ /** Async source (server search). Receives the query and an AbortSignal for stale requests. */
22
+ loadOptions?: (query: string, signal: AbortSignal) => Promise<Option[]>;
23
+ value?: string;
24
+ label?: string;
25
+ /** Accessible name when no visible `label` is shown. */
26
+ ariaLabel?: string;
27
+ placeholder?: string;
28
+ /** Accept free text as the value when nothing is picked. */
29
+ allowCustom?: boolean;
30
+ disabled?: boolean;
31
+ error?: string;
32
+ debounce?: number;
33
+ } = $props();
34
+
35
+ const id = $props.id();
36
+ let open = $state(false);
37
+ let text = $state('');
38
+ let dirty = $state(false);
39
+ let active = $state(0);
40
+ let items = $state<Option[]>([]);
41
+ let loading = $state(false);
42
+ let input: HTMLInputElement;
43
+ const known = new SvelteMap<string, string>();
44
+
45
+ const labelOf = (v: string) => known.get(v) ?? v;
46
+ $effect(() => { for (const o of options) known.set(String(o.value), o.label); });
47
+ $effect(() => { if (!dirty) text = value ? labelOf(value) : ''; });
48
+
49
+ const shown = $derived(
50
+ loadOptions ? items : dirty ? options.filter((o) => o.label.toLowerCase().includes(text.trim().toLowerCase())) : options
51
+ );
52
+
53
+ let timer: ReturnType<typeof setTimeout>;
54
+ let ctrl: AbortController | undefined;
55
+ /** A typed query has not produced results yet (debouncing or in flight): the list on screen is stale. */
56
+ let pending = $state(false);
57
+ $effect(() => () => { clearTimeout(timer); ctrl?.abort(); });
58
+
59
+ async function run(q: string) {
60
+ ctrl?.abort();
61
+ const c = (ctrl = new AbortController());
62
+ loading = true;
63
+ try {
64
+ const r = await loadOptions!(q, c.signal);
65
+ if (c.signal.aborted) return;
66
+ items = r;
67
+ for (const o of r) known.set(String(o.value), o.label);
68
+ } catch {
69
+ if (!c.signal.aborted) items = [];
70
+ } finally {
71
+ if (!c.signal.aborted) { loading = false; pending = false; }
72
+ }
73
+ }
74
+
75
+ function onInput() {
76
+ dirty = true; open = true; active = 0;
77
+ if (loadOptions) { pending = true; ctrl?.abort(); clearTimeout(timer); timer = setTimeout(() => run(text.trim()), debounce); }
78
+ }
79
+ function openList() {
80
+ if (disabled) return;
81
+ open = true;
82
+ if (loadOptions && !dirty) run('');
83
+ }
84
+ function choose(o: Option) {
85
+ value = String(o.value); known.set(value, o.label);
86
+ text = o.label; dirty = false; open = false;
87
+ if (loadOptions) { ctrl?.abort(); pending = false; items = []; } // the next open starts from the full list
88
+ }
89
+ function settle() {
90
+ if (dirty) {
91
+ const t = text.trim();
92
+ if (!t) value = undefined;
93
+ else if (allowCustom) value = t;
94
+ }
95
+ dirty = false;
96
+ text = value ? labelOf(value) : '';
97
+ }
98
+ function onKey(e: KeyboardEvent) {
99
+ const n = shown.length;
100
+ if (e.key === 'ArrowDown') { e.preventDefault(); if (!open) openList(); else active = (active + 1) % Math.max(n, 1); }
101
+ else if (e.key === 'ArrowUp') { e.preventDefault(); if (!open) openList(); else active = (active - 1 + n) % Math.max(n, 1); }
102
+ else if (e.key === 'Enter') {
103
+ if (pending) { e.preventDefault(); if (allowCustom) settle(); return; } // don't pick from the previous query's list
104
+ if (open && shown[active]) { e.preventDefault(); choose(shown[active]); }
105
+ else if (allowCustom) settle();
106
+ } else if (e.key === 'Escape' && (open || dirty)) { open = false; dirty = false; text = value ? labelOf(value) : ''; }
107
+ }
108
+ </script>
109
+
110
+ <div class="tr-field">
111
+ {#if label}<label class="tr-label" for={id}>{label}</label>{/if}
112
+ <div class="tr-anchor">
113
+ <div class="tr-control" data-trail={!!value && !disabled} data-invalid={!!error} data-disabled={disabled}>
114
+ <input
115
+ class="tr-bare" {id} aria-label={label ? undefined : ariaLabel} bind:this={input} bind:value={text} {placeholder} {disabled} autocomplete="off"
116
+ role="combobox" aria-autocomplete="list" aria-expanded={open} aria-controls="{id}-list"
117
+ aria-activedescendant={open && shown.length ? `${id}-list-opt-${active}` : undefined}
118
+ onclick={openList} oninput={onInput} onkeydown={onKey} onblur={settle}
119
+ />
120
+ {#if value && !disabled}
121
+ <Button variant="minimal" intent="neutral" size="sm" icon="cross" aria-label="Clear" tabindex={-1}
122
+ onclick={() => { value = undefined; text = ''; dirty = false; input.focus(); }} />
123
+ {/if}
124
+ </div>
125
+ <Popover bind:open>
126
+ <OptionList id="{id}-list" options={shown} {active} selected={value ? [value] : []} query={dirty ? text.trim() : ''} {loading}
127
+ emptyText={allowCustom ? 'Press Enter to use this text' : 'No matches'} onchoose={choose} onhover={(i) => (active = i)} />
128
+ </Popover>
129
+ </div>
130
+ {#if error}<div class="tr-hint" data-error="true">{error}</div>{/if}
131
+ </div>
@@ -0,0 +1,19 @@
1
+ import type { Option } from '../types.js';
2
+ type $$ComponentProps = {
3
+ options?: Option[];
4
+ /** Async source (server search). Receives the query and an AbortSignal for stale requests. */
5
+ loadOptions?: (query: string, signal: AbortSignal) => Promise<Option[]>;
6
+ value?: string;
7
+ label?: string;
8
+ /** Accessible name when no visible `label` is shown. */
9
+ ariaLabel?: string;
10
+ placeholder?: string;
11
+ /** Accept free text as the value when nothing is picked. */
12
+ allowCustom?: boolean;
13
+ disabled?: boolean;
14
+ error?: string;
15
+ debounce?: number;
16
+ };
17
+ declare const Combobox: import("svelte").Component<$$ComponentProps, {}, "value">;
18
+ type Combobox = ReturnType<typeof Combobox>;
19
+ export default Combobox;
@@ -0,0 +1,51 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import Menu from './Menu.svelte';
4
+ import type { MenuEntry } from '../types.js';
5
+
6
+ let { items, children, label = 'Context menu' }: { items: MenuEntry[]; children: Snippet; label?: string } = $props();
7
+
8
+ let at = $state<{ x: number; y: number } | null>(null);
9
+ let box = $state<HTMLDivElement>();
10
+ let host: HTMLDivElement;
11
+ let opener: HTMLElement | null = null;
12
+
13
+ function openAt(x: number, y: number) { opener = document.activeElement as HTMLElement | null; at = { x, y }; }
14
+ function onContext(e: MouseEvent) { e.preventDefault(); openAt(e.clientX, e.clientY); }
15
+ function onKey(e: KeyboardEvent) {
16
+ if (e.key === 'ContextMenu' || (e.shiftKey && e.key === 'F10')) {
17
+ e.preventDefault();
18
+ const r = ((e.target as HTMLElement | null) ?? host).getBoundingClientRect();
19
+ openAt(r.left + 16, r.top + 16);
20
+ }
21
+ }
22
+ function close() { at = null; opener?.focus(); }
23
+
24
+ // Keep the menu inside the viewport once measured
25
+ $effect(() => {
26
+ if (!at || !box) return;
27
+ const r = box.getBoundingClientRect();
28
+ if (r.right > innerWidth - 4) box.style.left = `${Math.max(4, innerWidth - r.width - 4)}px`;
29
+ if (r.bottom > innerHeight - 4) box.style.top = `${Math.max(4, innerHeight - r.height - 4)}px`;
30
+ });
31
+ </script>
32
+
33
+ <svelte:window
34
+ onpointerdown={(e) => at && !box?.contains(e.target as Node) && (at = null)}
35
+ onkeydown={(e) => e.key === 'Escape' && at && close()}
36
+ onblur={() => (at = null)}
37
+ onscroll={() => (at = null)}
38
+ />
39
+
40
+ <!-- svelte-ignore a11y_no_noninteractive_tabindex -->
41
+ <div class="cm" bind:this={host} tabindex="-1" role="presentation" oncontextmenu={onContext} onkeydown={onKey}>{@render children()}</div>
42
+ {#if at}
43
+ <div class="pop" bind:this={box} style:left="{at.x}px" style:top="{at.y}px">
44
+ <Menu {items} {label} autofocus onclose={close} />
45
+ </div>
46
+ {/if}
47
+
48
+ <style>
49
+ .cm { display: contents; }
50
+ .pop { position: fixed; z-index: 300; background: var(--tr-surface-container-low); border: 1px solid var(--tr-outline-variant); border-radius: var(--tr-radius-md); box-shadow: var(--tr-elevation-3); }
51
+ </style>
@@ -0,0 +1,10 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { MenuEntry } from '../types.js';
3
+ type $$ComponentProps = {
4
+ items: MenuEntry[];
5
+ children: Snippet;
6
+ label?: string;
7
+ };
8
+ declare const ContextMenu: import("svelte").Component<$$ComponentProps, {}, "">;
9
+ type ContextMenu = ReturnType<typeof ContextMenu>;
10
+ export default ContextMenu;