@dorsk/tsumikit 0.73.1 → 0.73.2

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/README.md CHANGED
@@ -286,7 +286,8 @@ its icon leading and shows its check trailing, or uses the leading check slot wh
286
286
  no icon; selecting closes the menu unless `closeOnSelect={false}` or the item sets `keepOpen`;
287
287
  rest props land on the trigger, `count`/`countMax` paint its corner badge and `panelStyle`
288
288
  sizes the panel; an item with a `control` snippet renders as a non-button row hosting a native
289
- `<select>` or nested Popover trigger, keeping the icon column, row height and ↑/↓ navigation),
289
+ `<select>` or nested Popover trigger, keeping the icon column, row height and ↑/↓ navigation;
290
+ labels may repeat, and an optional `id` keeps a row's identity when the list reorders),
290
291
  Tabs (omit `panel` for a strip-only tablist of document tabs: `closable` + `onclose(id)`
291
292
  close through the ✕, Delete and middle-click, per-tab `leading` snippet, `title`,
292
293
  trailing `actions` snippet; `--tab-max-width` truncates long labels),
@@ -96,7 +96,7 @@
96
96
  aria-valuenow={indeterminate ? undefined : value}
97
97
  >
98
98
  <div class="bar" style={indeterminate ? undefined : `width: ${pct}%`}></div>
99
- {#each markers as m (m.at)}
99
+ {#each markers as m, i (`${i}:${m.at}`)}
100
100
  <span
101
101
  class="marker tone-{m.tone ? canonicalTone(m.tone) : 'text'}"
102
102
  style="left: {markerPct(m.at)}%"
@@ -112,7 +112,7 @@
112
112
 
113
113
  {#snippet optionList()}
114
114
  {#if options}
115
- {#each sections as section, i (section.group ?? `-${i}`)}
115
+ {#each sections as section, i (`${i}:${section.group ?? ''}`)}
116
116
  {#if section.group === undefined}
117
117
  {#each section.options as o (o.value)}
118
118
  <option value={o.value} disabled={o.disabled}>{optionText(o)}</option>
@@ -89,7 +89,7 @@
89
89
  />
90
90
  {#if tickValues.length > 0}
91
91
  <span class="ticks" aria-hidden="true">
92
- {#each tickValues as t (t)}
92
+ {#each tickValues as t, i (`${i}:${t}`)}
93
93
  <i class="tick" class:reached={t <= Number(value)} style="--f: {frac(t)}"></i>
94
94
  {/each}
95
95
  </span>
@@ -100,7 +100,7 @@
100
100
  {/if}
101
101
  {#if marks.length > 0}
102
102
  <span class="marks" aria-hidden="true">
103
- {#each marks as m (m.value)}
103
+ {#each marks as m, i (`${i}:${m.value}`)}
104
104
  <button type="button" class="mark" class:on={Number(value) === m.value} style="--f: {frac(m.value)}" tabindex="-1" onclick={() => (value = m.value)}>{m.label}</button>
105
105
  {/each}
106
106
  </span>
@@ -173,7 +173,7 @@
173
173
  onclick={(e) => onGridClick(e, close)}
174
174
  onkeydown={(e) => onGridKeydown(e, close)}
175
175
  >
176
- {#each shown as x, i (x.emoji)}
176
+ {#each shown as x, i (`${i}:${x.emoji}`)}
177
177
  <div
178
178
  role="option"
179
179
  class="cell"
@@ -429,7 +429,7 @@
429
429
  {#if open && menu}
430
430
  <div bind:this={menuEl} id={listboxId} class="fi__menu" role="listbox" aria-label={kindLabel[menu.kind]}>
431
431
  <div class="fi__menu-head">{kindLabel[menu.kind]}</div>
432
- {#each menu.items as item, i (item.insert + i)}
432
+ {#each menu.items as item, i (`${i}:${item.insert}`)}
433
433
  <button
434
434
  type="button"
435
435
  id="{uid}-opt-{i}"
@@ -1,6 +1,8 @@
1
1
  <script lang="ts" module>
2
2
  export interface MenuItem {
3
3
  label: string;
4
+ /** Stable row identity; labels may repeat. */
5
+ id?: string;
4
6
  /** Required for button rows; a `control` row's hosted control acts on its own. */
5
7
  onselect?: () => void;
6
8
  icon?: import('../atoms/Icon.svelte').IconName;
@@ -202,7 +204,7 @@
202
204
  >
203
205
  {#snippet children({ close }: { close: () => void })}
204
206
  <div bind:this={listEl} role="none" class="menu" data-tsu="Menu" tabindex="-1" {onkeydown}>
205
- {#each items as item (item.label)}
207
+ {#each items as item, i (item.id ?? `${i}:${item.label}`)}
206
208
  {#if item.control}
207
209
  <div {...item.attrs as HTMLAttributes<HTMLDivElement>} role="none" class="menu-item menu-row" class:danger={item.danger} inert={item.disabled}>
208
210
  {#if item.icon}<Icon name={item.icon} />{/if}
@@ -1,5 +1,7 @@
1
1
  export interface MenuItem {
2
2
  label: string;
3
+ /** Stable row identity; labels may repeat. */
4
+ id?: string;
3
5
  /** Required for button rows; a `control` row's hosted control acts on its own. */
4
6
  onselect?: () => void;
5
7
  icon?: import('../atoms/Icon.svelte').IconName;
@@ -390,7 +390,7 @@
390
390
  {/if}
391
391
  <div id={menuId} role="menu" aria-label={label} class="radial-menu">
392
392
  {#if open}
393
- {#each petals as item, i (item.label)}
393
+ {#each petals as item, i (item.id ?? `${i}:${item.label}`)}
394
394
  {@const p = points[i]}
395
395
  {#if p}
396
396
  {@const glyphs = menuItemGlyphs(item)}
@@ -92,7 +92,7 @@
92
92
  onchange={(e) => onchange((e.currentTarget as HTMLSelectElement).value)}
93
93
  >
94
94
  {#if groups}
95
- {#each groups as g (g.label)}
95
+ {#each groups as g, i (`${i}:${g.label}`)}
96
96
  <optgroup label={g.label}>
97
97
  {#each g.options as o (o.value)}
98
98
  <option value={o.value}>{o.label}</option>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dorsk/tsumikit",
3
- "version": "0.73.1",
3
+ "version": "0.73.2",
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",