@dorsk/tsumikit 0.57.0 → 0.58.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.
Files changed (50) hide show
  1. package/README.md +125 -10
  2. package/dist/avatar.d.ts +6 -0
  3. package/dist/avatar.js +17 -0
  4. package/dist/components/atoms/Avatar.svelte +140 -0
  5. package/dist/components/atoms/Avatar.svelte.d.ts +25 -0
  6. package/dist/components/atoms/Badge.svelte +30 -8
  7. package/dist/components/atoms/Badge.svelte.d.ts +7 -0
  8. package/dist/components/atoms/Button.svelte +72 -1
  9. package/dist/components/atoms/Button.svelte.d.ts +2 -0
  10. package/dist/components/atoms/Input.svelte +22 -4
  11. package/dist/components/atoms/Swatch.svelte +114 -0
  12. package/dist/components/atoms/Swatch.svelte.d.ts +23 -0
  13. package/dist/components/atoms/Textarea.svelte +33 -6
  14. package/dist/components/molecules/Accordion.svelte +80 -62
  15. package/dist/components/molecules/Accordion.svelte.d.ts +15 -2
  16. package/dist/components/molecules/Combobox.svelte +299 -0
  17. package/dist/components/molecules/Combobox.svelte.d.ts +108 -0
  18. package/dist/components/molecules/Composer.svelte +32 -29
  19. package/dist/components/molecules/Composer.svelte.d.ts +2 -0
  20. package/dist/components/molecules/Disclosure.svelte +155 -0
  21. package/dist/components/molecules/Disclosure.svelte.d.ts +26 -0
  22. package/dist/components/molecules/IconButton.svelte +8 -0
  23. package/dist/components/molecules/IconButton.svelte.d.ts +4 -0
  24. package/dist/components/molecules/InputGroup.svelte +159 -0
  25. package/dist/components/molecules/InputGroup.svelte.d.ts +23 -0
  26. package/dist/components/molecules/Menu.svelte +28 -19
  27. package/dist/components/molecules/Menu.svelte.d.ts +9 -1
  28. package/dist/components/molecules/OptionButton.svelte +42 -1
  29. package/dist/components/molecules/OptionButton.svelte.d.ts +8 -1
  30. package/dist/components/molecules/Popover.svelte +61 -2
  31. package/dist/components/molecules/Popover.svelte.d.ts +7 -1
  32. package/dist/components/molecules/RadioGroup.svelte +59 -7
  33. package/dist/components/molecules/RadioGroup.svelte.d.ts +4 -0
  34. package/dist/components/molecules/SplitButton.svelte +175 -0
  35. package/dist/components/molecules/SplitButton.svelte.d.ts +41 -0
  36. package/dist/components/molecules/Tabs.svelte +155 -27
  37. package/dist/components/molecules/Tabs.svelte.d.ts +16 -1
  38. package/dist/components/molecules/combobox-keyboard.d.ts +100 -0
  39. package/dist/components/molecules/combobox-keyboard.js +170 -0
  40. package/dist/components/molecules/menu-item.d.ts +11 -0
  41. package/dist/components/molecules/menu-item.js +9 -0
  42. package/dist/count.d.ts +2 -0
  43. package/dist/count.js +7 -0
  44. package/dist/floating.d.ts +4 -0
  45. package/dist/floating.js +4 -1
  46. package/dist/index.d.ts +9 -0
  47. package/dist/index.js +9 -0
  48. package/dist/input-group-context.d.ts +10 -0
  49. package/dist/input-group-context.js +8 -0
  50. package/package.json +1 -1
@@ -0,0 +1,100 @@
1
+ /**
2
+ * @typedef {{ type: 'move', index: number } | { type: 'select' } | { type: 'close' }} ComboboxAction
3
+ * @typedef {{ start: number, char: string, query: string }} ComboboxTrigger
4
+ */
5
+ /**
6
+ * Resolve a navigation key to the option it highlights. Arrows step by one and
7
+ * wrap when `loop` is set; Home/End jump to the ends only when `homeEnd` is set,
8
+ * since in a text field they move the caret by default. Unhandled keys return
9
+ * `undefined` so the field keeps its native behaviour.
10
+ *
11
+ * @param {number} current
12
+ * @param {number} count
13
+ * @param {string} key
14
+ * @param {{ loop?: boolean, homeEnd?: boolean }} [opts]
15
+ * @returns {number | undefined}
16
+ */
17
+ export function nextComboboxIndex(current: number, count: number, key: string, { loop, homeEnd }?: {
18
+ loop?: boolean;
19
+ homeEnd?: boolean;
20
+ }): number | undefined;
21
+ /**
22
+ * Map a keydown on the wrapped field to what the open listbox does with it.
23
+ * Nothing is consumed while closed. Escape always closes. Enter/Tab select the
24
+ * highlighted option only when one exists and no modifier is held, so
25
+ * Shift+Enter (newline), Shift+Tab (focus back) and Mod+Enter (submit) pass
26
+ * through. `undefined` means the field keeps the key.
27
+ *
28
+ * @param {{ key: string, shiftKey?: boolean, ctrlKey?: boolean, metaKey?: boolean, altKey?: boolean }} e
29
+ * @param {{ open: boolean, count: number, index: number, loop?: boolean, homeEnd?: boolean, selectOn?: readonly string[] }} state
30
+ * @returns {ComboboxAction | undefined}
31
+ */
32
+ export function comboboxAction(e: {
33
+ key: string;
34
+ shiftKey?: boolean;
35
+ ctrlKey?: boolean;
36
+ metaKey?: boolean;
37
+ altKey?: boolean;
38
+ }, { open, count, index, loop, homeEnd, selectOn }: {
39
+ open: boolean;
40
+ count: number;
41
+ index: number;
42
+ loop?: boolean;
43
+ homeEnd?: boolean;
44
+ selectOn?: readonly string[];
45
+ }): ComboboxAction | undefined;
46
+ /**
47
+ * Find a `@query`-style token ending at the caret. The trigger char must start
48
+ * the text or follow whitespace/punctuation (so `foo@bar` and `##` are not
49
+ * triggers), and the query may not contain whitespace.
50
+ *
51
+ * @param {string} text
52
+ * @param {number} caret
53
+ * @param {string | readonly string[]} [chars]
54
+ * @returns {ComboboxTrigger | null}
55
+ */
56
+ export function findTrigger(text: string, caret: number, chars?: string | readonly string[]): ComboboxTrigger | null;
57
+ /**
58
+ * Replace the trigger token under the caret with `replacement` (plus a trailing
59
+ * space unless `suffix` says otherwise) and report where the caret lands.
60
+ *
61
+ * @param {string} text
62
+ * @param {number} caret
63
+ * @param {ComboboxTrigger} trigger
64
+ * @param {string} replacement
65
+ * @param {string} [suffix]
66
+ * @returns {{ text: string, caret: number }}
67
+ */
68
+ export function applyTrigger(text: string, caret: number, trigger: ComboboxTrigger, replacement: string, suffix?: string): {
69
+ text: string;
70
+ caret: number;
71
+ };
72
+ /**
73
+ * Viewport rect of the caret in a text field, measured through an off-screen
74
+ * mirror of the field's text and typography (fields expose no caret geometry).
75
+ * Falls back to the field's own rect when the document cannot be measured.
76
+ *
77
+ * @param {HTMLInputElement | HTMLTextAreaElement} el
78
+ * @returns {{ top: number, left: number, bottom: number, right: number, width: number, height: number }}
79
+ */
80
+ export function caretRect(el: HTMLInputElement | HTMLTextAreaElement): {
81
+ top: number;
82
+ left: number;
83
+ bottom: number;
84
+ right: number;
85
+ width: number;
86
+ height: number;
87
+ };
88
+ export type ComboboxAction = {
89
+ type: "move";
90
+ index: number;
91
+ } | {
92
+ type: "select";
93
+ } | {
94
+ type: "close";
95
+ };
96
+ export type ComboboxTrigger = {
97
+ start: number;
98
+ char: string;
99
+ query: string;
100
+ };
@@ -0,0 +1,170 @@
1
+ /**
2
+ * @typedef {{ type: 'move', index: number } | { type: 'select' } | { type: 'close' }} ComboboxAction
3
+ * @typedef {{ start: number, char: string, query: string }} ComboboxTrigger
4
+ */
5
+
6
+ /**
7
+ * Resolve a navigation key to the option it highlights. Arrows step by one and
8
+ * wrap when `loop` is set; Home/End jump to the ends only when `homeEnd` is set,
9
+ * since in a text field they move the caret by default. Unhandled keys return
10
+ * `undefined` so the field keeps its native behaviour.
11
+ *
12
+ * @param {number} current
13
+ * @param {number} count
14
+ * @param {string} key
15
+ * @param {{ loop?: boolean, homeEnd?: boolean }} [opts]
16
+ * @returns {number | undefined}
17
+ */
18
+ export function nextComboboxIndex(current, count, key, { loop = true, homeEnd = false } = {}) {
19
+ if (count <= 0) return undefined;
20
+ const last = count - 1;
21
+ const cur = Number.isFinite(current) ? Math.min(Math.max(Math.trunc(current), 0), last) : 0;
22
+ if (key === 'ArrowDown') return cur >= last ? (loop ? 0 : last) : cur + 1;
23
+ if (key === 'ArrowUp') return cur <= 0 ? (loop ? last : 0) : cur - 1;
24
+ if (homeEnd && key === 'Home') return 0;
25
+ if (homeEnd && key === 'End') return last;
26
+ return undefined;
27
+ }
28
+
29
+ /**
30
+ * Map a keydown on the wrapped field to what the open listbox does with it.
31
+ * Nothing is consumed while closed. Escape always closes. Enter/Tab select the
32
+ * highlighted option only when one exists and no modifier is held, so
33
+ * Shift+Enter (newline), Shift+Tab (focus back) and Mod+Enter (submit) pass
34
+ * through. `undefined` means the field keeps the key.
35
+ *
36
+ * @param {{ key: string, shiftKey?: boolean, ctrlKey?: boolean, metaKey?: boolean, altKey?: boolean }} e
37
+ * @param {{ open: boolean, count: number, index: number, loop?: boolean, homeEnd?: boolean, selectOn?: readonly string[] }} state
38
+ * @returns {ComboboxAction | undefined}
39
+ */
40
+ export function comboboxAction(
41
+ e,
42
+ { open, count, index, loop, homeEnd, selectOn = ['Enter', 'Tab'] },
43
+ ) {
44
+ if (!open) return undefined;
45
+ if (e.key === 'Escape') return { type: 'close' };
46
+ if (e.shiftKey || e.ctrlKey || e.metaKey || e.altKey) return undefined;
47
+ if (selectOn.includes(e.key)) return count > 0 ? { type: 'select' } : undefined;
48
+ const next = nextComboboxIndex(index, count, e.key, { loop, homeEnd });
49
+ return next === undefined ? undefined : { type: 'move', index: next };
50
+ }
51
+
52
+ /**
53
+ * Find a `@query`-style token ending at the caret. The trigger char must start
54
+ * the text or follow whitespace/punctuation (so `foo@bar` and `##` are not
55
+ * triggers), and the query may not contain whitespace.
56
+ *
57
+ * @param {string} text
58
+ * @param {number} caret
59
+ * @param {string | readonly string[]} [chars]
60
+ * @returns {ComboboxTrigger | null}
61
+ */
62
+ export function findTrigger(text, caret, chars = '@') {
63
+ const before = text.slice(0, Math.max(0, caret));
64
+ const set = typeof chars === 'string' ? [...chars] : chars;
65
+ let best = null;
66
+ for (const char of set) {
67
+ const at = before.lastIndexOf(char);
68
+ if (at < 0 || (best && at < best.start)) continue;
69
+ const query = before.slice(at + 1);
70
+ if (/\s/.test(query)) continue;
71
+ if (at > 0 && /[\p{L}\p{N}_]/u.test(before[at - 1])) continue;
72
+ if (at > 0 && set.includes(before[at - 1])) continue;
73
+ best = { start: at, char, query };
74
+ }
75
+ return best;
76
+ }
77
+
78
+ /**
79
+ * Replace the trigger token under the caret with `replacement` (plus a trailing
80
+ * space unless `suffix` says otherwise) and report where the caret lands.
81
+ *
82
+ * @param {string} text
83
+ * @param {number} caret
84
+ * @param {ComboboxTrigger} trigger
85
+ * @param {string} replacement
86
+ * @param {string} [suffix]
87
+ * @returns {{ text: string, caret: number }}
88
+ */
89
+ export function applyTrigger(text, caret, trigger, replacement, suffix = ' ') {
90
+ const token = replacement + suffix;
91
+ return {
92
+ text: text.slice(0, trigger.start) + token + text.slice(caret),
93
+ caret: trigger.start + token.length,
94
+ };
95
+ }
96
+
97
+ const MIRROR_PROPS = [
98
+ 'box-sizing',
99
+ 'width',
100
+ 'height',
101
+ 'overflow-x',
102
+ 'overflow-y',
103
+ 'border-top-width',
104
+ 'border-right-width',
105
+ 'border-bottom-width',
106
+ 'border-left-width',
107
+ 'padding-top',
108
+ 'padding-right',
109
+ 'padding-bottom',
110
+ 'padding-left',
111
+ 'font-style',
112
+ 'font-variant',
113
+ 'font-weight',
114
+ 'font-stretch',
115
+ 'font-size',
116
+ 'font-size-adjust',
117
+ 'line-height',
118
+ 'font-family',
119
+ 'text-align',
120
+ 'text-transform',
121
+ 'text-indent',
122
+ 'text-decoration',
123
+ 'letter-spacing',
124
+ 'word-spacing',
125
+ 'tab-size',
126
+ 'white-space',
127
+ 'word-break',
128
+ 'overflow-wrap',
129
+ ];
130
+
131
+ /**
132
+ * Viewport rect of the caret in a text field, measured through an off-screen
133
+ * mirror of the field's text and typography (fields expose no caret geometry).
134
+ * Falls back to the field's own rect when the document cannot be measured.
135
+ *
136
+ * @param {HTMLInputElement | HTMLTextAreaElement} el
137
+ * @returns {{ top: number, left: number, bottom: number, right: number, width: number, height: number }}
138
+ */
139
+ export function caretRect(el) {
140
+ const rect = el.getBoundingClientRect();
141
+ const doc = el.ownerDocument;
142
+ const view = doc.defaultView;
143
+ if (!view || !doc.body) return rect;
144
+ const caret = el.selectionEnd ?? el.value.length;
145
+ const style = view.getComputedStyle(el);
146
+ const mirror = doc.createElement('div');
147
+ for (const prop of MIRROR_PROPS) mirror.style.setProperty(prop, style.getPropertyValue(prop));
148
+ mirror.style.position = 'fixed';
149
+ mirror.style.top = '0';
150
+ mirror.style.left = '-9999px';
151
+ mirror.style.visibility = 'hidden';
152
+ mirror.style.pointerEvents = 'none';
153
+ if (el instanceof view.HTMLInputElement) mirror.style.whiteSpace = 'pre';
154
+ else {
155
+ mirror.style.whiteSpace = 'pre-wrap';
156
+ mirror.style.overflowY = 'hidden';
157
+ }
158
+ mirror.textContent = el.value.slice(0, caret);
159
+ const marker = doc.createElement('span');
160
+ marker.textContent = el.value.slice(caret) || '.';
161
+ mirror.appendChild(marker);
162
+ doc.body.appendChild(mirror);
163
+ const m = mirror.getBoundingClientRect();
164
+ const s = marker.getBoundingClientRect();
165
+ mirror.remove();
166
+ const lineHeight = Number.parseFloat(style.lineHeight) || s.height || rect.height;
167
+ const top = rect.top + (s.top - m.top) - el.scrollTop;
168
+ const left = rect.left + (s.left - m.left) - el.scrollLeft;
169
+ return { top, left, width: 0, height: lineHeight, bottom: top + lineHeight, right: left };
170
+ }
@@ -0,0 +1,11 @@
1
+ export interface MenuItemGlyphs {
2
+ leading: 'icon' | 'check' | 'none';
3
+ trailingCheck: boolean;
4
+ }
5
+ export interface MenuItemShape {
6
+ icon?: string;
7
+ pressed?: boolean;
8
+ keepOpen?: boolean;
9
+ }
10
+ export declare function menuItemGlyphs(item: MenuItemShape): MenuItemGlyphs;
11
+ export declare function menuItemCloses(item: MenuItemShape, closeOnSelect: boolean): boolean;
@@ -0,0 +1,9 @@
1
+ export function menuItemGlyphs(item) {
2
+ const checkable = item.pressed !== undefined;
3
+ if (item.icon)
4
+ return { leading: 'icon', trailingCheck: checkable };
5
+ return { leading: checkable ? 'check' : 'none', trailingCheck: false };
6
+ }
7
+ export function menuItemCloses(item, closeOnSelect) {
8
+ return item.keepOpen === undefined ? closeOnSelect : !item.keepOpen;
9
+ }
@@ -0,0 +1,2 @@
1
+ export declare function hasCount(count: number | undefined | null): count is number;
2
+ export declare function formatCount(count: number, max?: number): string;
package/dist/count.js ADDED
@@ -0,0 +1,7 @@
1
+ export function hasCount(count) {
2
+ return typeof count === 'number' && Number.isFinite(count) && count > 0;
3
+ }
4
+ export function formatCount(count, max = 99) {
5
+ const n = Math.floor(count);
6
+ return n > max ? `${max}+` : String(n);
7
+ }
@@ -2,9 +2,13 @@ export type Side = 'top' | 'bottom' | 'left' | 'right';
2
2
  export type Align = 'start' | 'center' | 'end';
3
3
  /** `${side}-${align}`, e.g. `bottom-start` (Popover) or `left-center` (Tooltip). */
4
4
  export type Placement = `${Side}-${Align}`;
5
+ /** Viewport-relative box to float against: an element's rect or a virtual one (a text caret). */
6
+ export type AnchorRect = Pick<DOMRect, 'top' | 'left' | 'bottom' | 'right' | 'width' | 'height'>;
5
7
  /**
6
8
  * Position `floating` next to `trigger` and write the result to its inline
7
9
  * `top`/`left` (expects `floating` to be `position: fixed`). Call on open and on
8
10
  * scroll/resize while open.
9
11
  */
10
12
  export declare function place(trigger: HTMLElement, floating: HTMLElement, placement?: Placement, gap?: number): void;
13
+ /** `place` against a viewport rect instead of an element. */
14
+ export declare function placeAt(t: AnchorRect, floating: HTMLElement, placement?: Placement, gap?: number): void;
package/dist/floating.js CHANGED
@@ -18,7 +18,10 @@ function alignPos(align, start, size, fSize) {
18
18
  * scroll/resize while open.
19
19
  */
20
20
  export function place(trigger, floating, placement = 'bottom-start', gap = 6) {
21
- const t = trigger.getBoundingClientRect();
21
+ placeAt(trigger.getBoundingClientRect(), floating, placement, gap);
22
+ }
23
+ /** `place` against a viewport rect instead of an element. */
24
+ export function placeAt(t, floating, placement = 'bottom-start', gap = 6) {
22
25
  const f = floating.getBoundingClientRect();
23
26
  const vw = document.documentElement.clientWidth;
24
27
  const vh = document.documentElement.clientHeight;
package/dist/index.d.ts CHANGED
@@ -1,8 +1,10 @@
1
1
  export type { AnchorAttributes } from './anchor';
2
2
  export { artworkGradient, artworkHue, initials } from './artwork';
3
3
  export { autoresize } from './autoresize';
4
+ export { avatarHue, avatarInitial } from './avatar';
4
5
  export { copyToClipboard } from './clipboard';
5
6
  export { default as Artwork } from './components/atoms/Artwork.svelte';
7
+ export { default as Avatar } from './components/atoms/Avatar.svelte';
6
8
  export { default as Badge } from './components/atoms/Badge.svelte';
7
9
  export { default as Button } from './components/atoms/Button.svelte';
8
10
  export { default as Card } from './components/atoms/Card.svelte';
@@ -24,6 +26,7 @@ export { default as Select } from './components/atoms/Select.svelte';
24
26
  export { default as Skeleton } from './components/atoms/Skeleton.svelte';
25
27
  export { default as Slider } from './components/atoms/Slider.svelte';
26
28
  export { default as Spinner } from './components/atoms/Spinner.svelte';
29
+ export { default as Swatch } from './components/atoms/Swatch.svelte';
27
30
  export { default as Switch } from './components/atoms/Switch.svelte';
28
31
  export { default as Text } from './components/atoms/Text.svelte';
29
32
  export { default as Textarea } from './components/atoms/Textarea.svelte';
@@ -47,9 +50,12 @@ export { default as CapBar } from './components/molecules/CapBar.svelte';
47
50
  export { default as Carousel } from './components/molecules/Carousel.svelte';
48
51
  export { default as ChatBubble } from './components/molecules/ChatBubble.svelte';
49
52
  export { default as CodeBlock } from './components/molecules/CodeBlock.svelte';
53
+ export { default as Combobox } from './components/molecules/Combobox.svelte';
50
54
  export { default as Composer } from './components/molecules/Composer.svelte';
51
55
  export { default as ConfirmModal } from './components/molecules/ConfirmModal.svelte';
52
56
  export { default as CopyButton } from './components/molecules/CopyButton.svelte';
57
+ export { applyTrigger, comboboxAction, findTrigger, nextComboboxIndex, } from './components/molecules/combobox-keyboard.js';
58
+ export { type DisclosureChevron, type DisclosureHeaderContext, default as Disclosure, } from './components/molecules/Disclosure.svelte';
53
59
  export { default as Drawer } from './components/molecules/Drawer.svelte';
54
60
  export { default as Dropzone } from './components/molecules/Dropzone.svelte';
55
61
  export { default as EmojiPicker } from './components/molecules/EmojiPicker.svelte';
@@ -61,6 +67,7 @@ export { default as FilterInput, type FilterInputContext, } from './components/m
61
67
  export { default as FontScalePicker } from './components/molecules/FontScalePicker.svelte';
62
68
  export { type DiffStats, default as GitRef, type PrState, type PullRequest, } from './components/molecules/GitRef.svelte';
63
69
  export { default as IconButton } from './components/molecules/IconButton.svelte';
70
+ export { default as InputGroup } from './components/molecules/InputGroup.svelte';
64
71
  export { default as KeyValue, type KeyValueRow, type KeyValueTone, } from './components/molecules/KeyValue.svelte';
65
72
  export { default as LoadMore } from './components/molecules/LoadMore.svelte';
66
73
  export { default as Menu, type MenuItem } from './components/molecules/Menu.svelte';
@@ -75,6 +82,7 @@ export { default as RadioGroup, type RadioOption, } from './components/molecules
75
82
  export { default as SectionHeader } from './components/molecules/SectionHeader.svelte';
76
83
  export { default as SegmentedControl, type SegmentOption, } from './components/molecules/SegmentedControl.svelte';
77
84
  export { default as SelectButton } from './components/molecules/SelectButton.svelte';
85
+ export { default as SplitButton } from './components/molecules/SplitButton.svelte';
78
86
  export { default as Tabs, type TabItem } from './components/molecules/Tabs.svelte';
79
87
  export { default as ThemePicker } from './components/molecules/ThemePicker.svelte';
80
88
  export { default as Timestamp } from './components/molecules/Timestamp.svelte';
@@ -85,6 +93,7 @@ export { default as Truncate } from './components/molecules/Truncate.svelte';
85
93
  export { default as WorkingDir } from './components/molecules/WorkingDir.svelte';
86
94
  export { type Column, default as DataTable, type RowTone, } from './components/organisms/DataTable.svelte';
87
95
  export { default as FilterSearchBar } from './components/organisms/FilterSearchBar.svelte';
96
+ export { formatCount, hasCount } from './count';
88
97
  export { EMOJI_GROUPS, type EmojiEntry, type EmojiGroup, searchEmoji } from './emoji';
89
98
  export { FIELD_KEY, type FieldContext, getFieldContext, setFieldContext, warnUnlabelled, } from './field-context';
90
99
  export * as filterQuery from './query';
package/dist/index.js CHANGED
@@ -6,8 +6,10 @@
6
6
  // then use the components below.
7
7
  export { artworkGradient, artworkHue, initials } from './artwork';
8
8
  export { autoresize } from './autoresize';
9
+ export { avatarHue, avatarInitial } from './avatar';
9
10
  export { copyToClipboard } from './clipboard';
10
11
  export { default as Artwork } from './components/atoms/Artwork.svelte';
12
+ export { default as Avatar } from './components/atoms/Avatar.svelte';
11
13
  export { default as Badge } from './components/atoms/Badge.svelte';
12
14
  export { default as Button } from './components/atoms/Button.svelte';
13
15
  export { default as Card } from './components/atoms/Card.svelte';
@@ -27,6 +29,7 @@ export { default as Select } from './components/atoms/Select.svelte';
27
29
  export { default as Skeleton } from './components/atoms/Skeleton.svelte';
28
30
  export { default as Slider } from './components/atoms/Slider.svelte';
29
31
  export { default as Spinner } from './components/atoms/Spinner.svelte';
32
+ export { default as Swatch } from './components/atoms/Swatch.svelte';
30
33
  export { default as Switch } from './components/atoms/Switch.svelte';
31
34
  // ---- atoms ----
32
35
  export { default as Text } from './components/atoms/Text.svelte';
@@ -52,9 +55,12 @@ export { default as CapBar } from './components/molecules/CapBar.svelte';
52
55
  export { default as Carousel } from './components/molecules/Carousel.svelte';
53
56
  export { default as ChatBubble } from './components/molecules/ChatBubble.svelte';
54
57
  export { default as CodeBlock } from './components/molecules/CodeBlock.svelte';
58
+ export { default as Combobox } from './components/molecules/Combobox.svelte';
55
59
  export { default as Composer } from './components/molecules/Composer.svelte';
56
60
  export { default as ConfirmModal } from './components/molecules/ConfirmModal.svelte';
57
61
  export { default as CopyButton } from './components/molecules/CopyButton.svelte';
62
+ export { applyTrigger, comboboxAction, findTrigger, nextComboboxIndex, } from './components/molecules/combobox-keyboard.js';
63
+ export { default as Disclosure, } from './components/molecules/Disclosure.svelte';
58
64
  export { default as Drawer } from './components/molecules/Drawer.svelte';
59
65
  export { default as Dropzone } from './components/molecules/Dropzone.svelte';
60
66
  export { default as EmojiPicker } from './components/molecules/EmojiPicker.svelte';
@@ -67,6 +73,7 @@ export { default as FilterInput, } from './components/molecules/FilterInput.svel
67
73
  export { default as FontScalePicker } from './components/molecules/FontScalePicker.svelte';
68
74
  export { default as GitRef, } from './components/molecules/GitRef.svelte';
69
75
  export { default as IconButton } from './components/molecules/IconButton.svelte';
76
+ export { default as InputGroup } from './components/molecules/InputGroup.svelte';
70
77
  export { default as KeyValue, } from './components/molecules/KeyValue.svelte';
71
78
  export { default as LoadMore } from './components/molecules/LoadMore.svelte';
72
79
  export { default as Menu } from './components/molecules/Menu.svelte';
@@ -83,6 +90,7 @@ export { default as RadioGroup, } from './components/molecules/RadioGroup.svelte
83
90
  export { default as SectionHeader } from './components/molecules/SectionHeader.svelte';
84
91
  export { default as SegmentedControl, } from './components/molecules/SegmentedControl.svelte';
85
92
  export { default as SelectButton } from './components/molecules/SelectButton.svelte';
93
+ export { default as SplitButton } from './components/molecules/SplitButton.svelte';
86
94
  export { default as Tabs } from './components/molecules/Tabs.svelte';
87
95
  export { default as ThemePicker } from './components/molecules/ThemePicker.svelte';
88
96
  export { default as Timestamp } from './components/molecules/Timestamp.svelte';
@@ -94,6 +102,7 @@ export { default as WorkingDir } from './components/molecules/WorkingDir.svelte'
94
102
  // ---- organisms ----
95
103
  export { default as DataTable, } from './components/organisms/DataTable.svelte';
96
104
  export { default as FilterSearchBar } from './components/organisms/FilterSearchBar.svelte';
105
+ export { formatCount, hasCount } from './count';
97
106
  export { EMOJI_GROUPS, searchEmoji } from './emoji';
98
107
  export { FIELD_KEY, getFieldContext, setFieldContext, warnUnlabelled, } from './field-context';
99
108
  // ---- query core (headless: schema / parser / AST / suggest / compilers) ----
@@ -0,0 +1,10 @@
1
+ import type { ControlSize } from './size';
2
+ export declare const INPUT_GROUP_KEY: unique symbol;
3
+ /** Read through getters so the field tracks the group's props reactively. */
4
+ export type InputGroupContext = {
5
+ readonly size: ControlSize;
6
+ readonly disabled: boolean;
7
+ readonly invalid: boolean;
8
+ };
9
+ export declare function setInputGroupContext(ctx: InputGroupContext): InputGroupContext;
10
+ export declare function getInputGroupContext(): InputGroupContext | undefined;
@@ -0,0 +1,8 @@
1
+ import { getContext, setContext } from 'svelte';
2
+ export const INPUT_GROUP_KEY = Symbol('tsu-input-group');
3
+ export function setInputGroupContext(ctx) {
4
+ return setContext(INPUT_GROUP_KEY, ctx);
5
+ }
6
+ export function getInputGroupContext() {
7
+ return getContext(INPUT_GROUP_KEY);
8
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dorsk/tsumikit",
3
- "version": "0.57.0",
3
+ "version": "0.58.1",
4
4
  "description": "Minimal, dependency-free Svelte 5 + pure-CSS UI kit. Token-driven atoms, molecules & layouts with theming out of the box.",
5
5
  "type": "module",
6
6
  "license": "MIT",