@dorsk/tsumikit 0.77.0 → 0.78.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.
@@ -289,6 +289,7 @@
289
289
  }
290
290
  .shell-menu-btn {
291
291
  display: inline-flex;
292
+ flex: none;
292
293
  align-items: center;
293
294
  }
294
295
  .shell-main {
@@ -311,6 +311,9 @@
311
311
  text-overflow: ellipsis;
312
312
  white-space: nowrap;
313
313
  }
314
+ .stacked .navbar-label {
315
+ max-width: 100%;
316
+ }
314
317
  .navbar-badge {
315
318
  position: absolute;
316
319
  top: -0.35rem;
@@ -90,19 +90,19 @@
90
90
  gap: var(--sp-3);
91
91
  padding: var(--sp-3);
92
92
  }
93
- :global(.callout-ok) {
93
+ :global(.card.callout-ok) {
94
94
  --callout-tone: var(--ok);
95
95
  }
96
- :global(.callout-warn) {
96
+ :global(.card.callout-warn) {
97
97
  --callout-tone: var(--warn);
98
98
  }
99
- :global(.callout-danger) {
99
+ :global(.card.callout-danger) {
100
100
  --callout-tone: var(--danger);
101
101
  }
102
- :global(.callout-info) {
102
+ :global(.card.callout-info) {
103
103
  --callout-tone: var(--info);
104
104
  }
105
- :global(.callout-accent) {
105
+ :global(.card.callout-accent) {
106
106
  --callout-tone: var(--accent);
107
107
  }
108
108
 
@@ -180,5 +180,9 @@
180
180
  -webkit-line-clamp: var(--clamp);
181
181
  line-clamp: var(--clamp);
182
182
  overflow: hidden;
183
+ padding: calc(var(--focus-ring-width) + var(--focus-ring-offset));
184
+ padding-block-end: 0;
185
+ margin: calc(-1 * (var(--focus-ring-width) + var(--focus-ring-offset)));
186
+ margin-block-end: 0;
183
187
  }
184
188
  </style>
@@ -101,8 +101,15 @@
101
101
  font-family: var(--font-mono);
102
102
  color: var(--text-muted);
103
103
  }
104
+ .cb-name {
105
+ min-width: 0;
106
+ overflow: hidden;
107
+ text-overflow: ellipsis;
108
+ white-space: nowrap;
109
+ }
104
110
  .cb-body {
105
111
  display: flex;
112
+ align-items: flex-start;
106
113
  overflow: auto;
107
114
  -webkit-overflow-scrolling: touch;
108
115
  }
@@ -467,6 +467,7 @@
467
467
  .fi__bar {
468
468
  --fi-icon: var(--control-icon-md);
469
469
  display: flex;
470
+ flex-wrap: wrap;
470
471
  align-items: center;
471
472
  gap: var(--sp-2);
472
473
  padding: 0 var(--control-px-md);
@@ -522,7 +523,7 @@
522
523
  position: relative;
523
524
  display: flex;
524
525
  flex: 1;
525
- min-width: 0;
526
+ min-width: min(100%, 6rem);
526
527
  }
527
528
  .fi__display {
528
529
  position: absolute;
@@ -74,6 +74,8 @@
74
74
  .status-label {
75
75
  flex: 1;
76
76
  min-width: 0;
77
+ padding-block: calc(var(--focus-ring-width) + var(--focus-ring-offset));
78
+ margin-block: calc(-1 * (var(--focus-ring-width) + var(--focus-ring-offset)));
77
79
  overflow: hidden;
78
80
  text-overflow: ellipsis;
79
81
  white-space: nowrap;
@@ -9,6 +9,7 @@
9
9
  // observed and the longest candidate that fits is shown (mode `path`
10
10
  // abbreviates fish-style). `copyable` copies the full text on click.
11
11
  import { copyToClipboard } from '../../clipboard';
12
+ import { untrack } from 'svelte';
12
13
  import { pathCandidates, truncate, type TruncateMode } from '../../truncate';
13
14
  import Tooltip from './Tooltip.svelte';
14
15
 
@@ -72,15 +73,18 @@
72
73
  let probe = $state<HTMLSpanElement | null>(null);
73
74
  let avail = $state(Number.POSITIVE_INFINITY);
74
75
  let chPx = $state(8);
76
+ let cap = $state(Number.POSITIVE_INFINITY);
75
77
 
76
78
  $effect(() => {
77
79
  if (!fit || !rail) return;
78
80
  const el = rail;
79
81
  const measure = () => {
80
- avail = el.clientWidth;
82
+ const w = el.clientWidth;
83
+ if (w > avail) cap = Number.POSITIVE_INFINITY;
84
+ avail = w;
81
85
  if (probe) chPx = probe.getBoundingClientRect().width / 10 || chPx;
82
86
  };
83
- measure();
87
+ untrack(measure);
84
88
  const ro = new ResizeObserver(measure);
85
89
  ro.observe(el);
86
90
  return () => ro.disconnect();
@@ -88,7 +92,7 @@
88
92
 
89
93
  const shown = $derived.by(() => {
90
94
  if (fit) {
91
- const budget = Math.max(0, avail - 1);
95
+ const budget = Math.max(0, Math.min(avail - 1, cap));
92
96
  return candidates.find((c) => [...c].length * chPx <= budget) ?? candidates[candidates.length - 1];
93
97
  }
94
98
  if (max === undefined) return text;
@@ -96,6 +100,17 @@
96
100
  });
97
101
  const isTruncated = $derived(shown !== text);
98
102
 
103
+ // The per-character estimate can undershoot (glyph fallbacks, late fonts): cap the budget below the spilling candidate.
104
+ $effect(() => {
105
+ if (!fit || !rail || shown === candidates[candidates.length - 1]) return;
106
+ const el = rail;
107
+ const id = requestAnimationFrame(() => {
108
+ const over = el.scrollWidth - el.clientWidth;
109
+ if (over > 0) cap = Math.min(untrack(() => cap), [...shown].length * chPx - over);
110
+ });
111
+ return () => cancelAnimationFrame(id);
112
+ });
113
+
99
114
  let status = $state('');
100
115
  let timer: ReturnType<typeof setTimeout> | undefined;
101
116
  async function copy(e: MouseEvent) {
@@ -173,6 +188,9 @@
173
188
  max-width: 100%;
174
189
  overflow: hidden;
175
190
  }
191
+ .rail .trunc:focus-visible {
192
+ outline-offset: var(--focus-ring-offset-inset);
193
+ }
176
194
  .grow {
177
195
  flex: 1 1 0;
178
196
  }
@@ -154,6 +154,7 @@
154
154
  min-height: 0;
155
155
  }
156
156
  .feed {
157
+ position: relative;
157
158
  flex: 1;
158
159
  min-height: 0;
159
160
  overflow: auto;
@@ -97,14 +97,20 @@
97
97
  {#if showChips && (chips.length || text)}
98
98
  <div class="fsb__chips">
99
99
  {#each chips as f (f.span[0])}
100
- <Badge tone="info" removable onremove={() => remove(f.span)}>
100
+ <Badge
101
+ tone="info"
102
+ truncate
103
+ title="{labelFor(f.field)} {operatorById(f.op)?.label} {f.values.join(', ') || '∅'}"
104
+ removable
105
+ onremove={() => remove(f.span)}
106
+ >
101
107
  <span class="fsb__chip-field">{labelFor(f.field)}</span>
102
108
  <span class="fsb__chip-op">{operatorById(f.op)?.label}</span>
103
109
  <span class="fsb__chip-val">{f.values.join(', ') || '∅'}</span>
104
110
  </Badge>
105
111
  {/each}
106
112
  {#if text}
107
- <Badge tone="neutral">
113
+ <Badge tone="neutral" truncate title="text {text}">
108
114
  <span class="fsb__chip-op">text</span>
109
115
  <span class="fsb__chip-val">{text}</span>
110
116
  </Badge>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dorsk/tsumikit",
3
- "version": "0.77.0",
3
+ "version": "0.78.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",