@dorsk/tsumikit 0.32.0 → 0.33.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.
@@ -14,21 +14,52 @@
14
14
  // click). Selecting an item runs its action and closes the menu. Focus moves
15
15
  // to the first item when the menu opens. Escape / click-outside close it
16
16
  // (inherited from the native popover).
17
- import type { Snippet } from 'svelte';
17
+ import type { ComponentProps, Snippet } from 'svelte';
18
18
  import Popover from './Popover.svelte';
19
19
  import Icon from '../atoms/Icon.svelte';
20
20
 
21
+ type PopoverProps = ComponentProps<typeof Popover>;
22
+ type TriggerChrome = Pick<
23
+ PopoverProps,
24
+ | 'variant'
25
+ | 'tone'
26
+ | 'size'
27
+ | 'box'
28
+ | 'control'
29
+ | 'block'
30
+ | 'triggerClass'
31
+ | 'bare'
32
+ | 'hitArea'
33
+ | 'disabled'
34
+ | 'gap'
35
+ | 'onopen'
36
+ | 'onclose'
37
+ >;
38
+
21
39
  let {
22
40
  label,
23
41
  items,
24
42
  trigger,
25
- placement = 'bottom-start'
43
+ placement = 'bottom-start',
44
+ variant,
45
+ tone,
46
+ size,
47
+ box,
48
+ control,
49
+ block,
50
+ triggerClass,
51
+ bare,
52
+ hitArea,
53
+ disabled,
54
+ gap,
55
+ onopen,
56
+ onclose
26
57
  }: {
27
58
  label: string;
28
59
  items: MenuItem[];
29
60
  trigger: Snippet;
30
61
  placement?: 'bottom-start' | 'bottom-end' | 'top-start' | 'top-end';
31
- } = $props();
62
+ } & TriggerChrome = $props();
32
63
 
33
64
  let listEl = $state<HTMLDivElement | null>(null);
34
65
 
@@ -66,7 +97,27 @@
66
97
  }
67
98
  </script>
68
99
 
69
- <Popover {label} {placement} {trigger} onopen={() => queueMicrotask(() => focusAt(0))}>
100
+ <Popover
101
+ {label}
102
+ {placement}
103
+ {trigger}
104
+ {variant}
105
+ {tone}
106
+ {size}
107
+ {box}
108
+ {control}
109
+ {block}
110
+ {triggerClass}
111
+ {bare}
112
+ {hitArea}
113
+ {disabled}
114
+ {gap}
115
+ {onclose}
116
+ onopen={() => {
117
+ queueMicrotask(() => focusAt(0));
118
+ onopen?.();
119
+ }}
120
+ >
70
121
  <div bind:this={listEl} role="menu" aria-label={label} class="menu" data-tsu="Menu" tabindex="-1" {onkeydown}>
71
122
  {#each items as item (item.label)}
72
123
  <button
@@ -5,13 +5,16 @@ export interface MenuItem {
5
5
  danger?: boolean;
6
6
  disabled?: boolean;
7
7
  }
8
- import type { Snippet } from 'svelte';
8
+ import type { ComponentProps, Snippet } from 'svelte';
9
+ import Popover from './Popover.svelte';
10
+ type PopoverProps = ComponentProps<typeof Popover>;
11
+ type TriggerChrome = Pick<PopoverProps, 'variant' | 'tone' | 'size' | 'box' | 'control' | 'block' | 'triggerClass' | 'bare' | 'hitArea' | 'disabled' | 'gap' | 'onopen' | 'onclose'>;
9
12
  type $$ComponentProps = {
10
13
  label: string;
11
14
  items: MenuItem[];
12
15
  trigger: Snippet;
13
16
  placement?: 'bottom-start' | 'bottom-end' | 'top-start' | 'top-end';
14
- };
17
+ } & TriggerChrome;
15
18
  declare const Menu: import("svelte").Component<$$ComponentProps, {}, "">;
16
19
  type Menu = ReturnType<typeof Menu>;
17
20
  export default Menu;
@@ -35,7 +35,11 @@
35
35
  label = 'Options',
36
36
  // `mobile` hides segment labels below the mobile breakpoint, collapsing
37
37
  // labelled segments to centered icon-only squares (aria-label preserved).
38
+ // `container` does the same when the nearest size container is at most
39
+ // 35rem wide (the breakpoint is fixed: `@container` cannot read a var).
38
40
  collapseLabels = 'never',
41
+ scroll = false,
42
+ block = false,
39
43
  class: klass = '',
40
44
  // Custom rendering per option (overrides the built-in label/count/icon).
41
45
  option: optionSnippet
@@ -48,7 +52,11 @@
48
52
  * `control`) so the whole segmented control height-matches its siblings. */
49
53
  control?: boolean;
50
54
  label?: string;
51
- collapseLabels?: 'never' | 'mobile';
55
+ collapseLabels?: 'never' | 'mobile' | 'container';
56
+ /** Single non-wrapping row that scrolls horizontally (scrollbar hidden). */
57
+ scroll?: boolean;
58
+ /** Fill the parent width, sharing it equally between segments. */
59
+ block?: boolean;
52
60
  class?: string;
53
61
  option?: Snippet<[SegmentOption, boolean]>;
54
62
  } = $props();
@@ -90,6 +98,9 @@
90
98
  class="seg seg-{variant} seg-{size} {klass}"
91
99
  class:seg-control={control}
92
100
  class:seg-collapse-mobile={collapseLabels === 'mobile'}
101
+ class:collapse-container={collapseLabels === 'container'}
102
+ class:seg-scroll={scroll}
103
+ class:seg-block={block}
93
104
  data-tsu="SegmentedControl"
94
105
  {onkeydown}
95
106
  >
@@ -132,6 +143,28 @@
132
143
  .seg-icon {
133
144
  border-radius: var(--r-md);
134
145
  }
146
+ .seg-scroll {
147
+ flex: 1 0 100%;
148
+ flex-wrap: nowrap;
149
+ max-width: 100%;
150
+ overflow-x: auto;
151
+ scrollbar-width: none;
152
+ -webkit-overflow-scrolling: touch;
153
+ }
154
+ .seg-scroll::-webkit-scrollbar {
155
+ display: none;
156
+ }
157
+ .seg-scroll .seg-item {
158
+ flex: none;
159
+ }
160
+ .seg-block {
161
+ display: flex;
162
+ width: 100%;
163
+ }
164
+ .seg-block .seg-item {
165
+ flex: 1 1 0;
166
+ justify-content: center;
167
+ }
135
168
 
136
169
  .seg-item {
137
170
  display: inline-flex;
@@ -209,6 +242,18 @@
209
242
  padding: 0.25rem;
210
243
  }
211
244
  }
245
+ @container (max-width: 35rem) {
246
+ .collapse-container .seg-item.has-label .seg-label {
247
+ display: none;
248
+ }
249
+ .collapse-container .seg-item.has-label {
250
+ gap: 0;
251
+ padding: 0.35rem;
252
+ }
253
+ .collapse-container.seg-sm .seg-item.has-label {
254
+ padding: 0.25rem;
255
+ }
256
+ }
212
257
 
213
258
  .seg-item:hover:not(:disabled):not(.selected) {
214
259
  color: var(--text);
@@ -19,7 +19,11 @@ type $$ComponentProps = {
19
19
  * `control`) so the whole segmented control height-matches its siblings. */
20
20
  control?: boolean;
21
21
  label?: string;
22
- collapseLabels?: 'never' | 'mobile';
22
+ collapseLabels?: 'never' | 'mobile' | 'container';
23
+ /** Single non-wrapping row that scrolls horizontally (scrollbar hidden). */
24
+ scroll?: boolean;
25
+ /** Fill the parent width, sharing it equally between segments. */
26
+ block?: boolean;
23
27
  class?: string;
24
28
  option?: Snippet<[SegmentOption, boolean]>;
25
29
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dorsk/tsumikit",
3
- "version": "0.32.0",
3
+ "version": "0.33.0",
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",