@vobs/ui 0.1.0 → 0.3.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.
package/dist/tokens.css CHANGED
@@ -8,11 +8,15 @@
8
8
  --radius-10: 10px;
9
9
  --radius-full: 9999px;
10
10
  --spacer-0: 0px;
11
+ --spacer-2: 2px;
11
12
  --spacer-4: 4px;
12
13
  --spacer-6: 6px;
13
14
  --spacer-8: 8px;
15
+ --spacer-10: 10px;
14
16
  --spacer-12: 12px;
17
+ --spacer-14: 14px;
15
18
  --spacer-16: 16px;
19
+ --spacer-20: 20px;
16
20
  --spacer-24: 24px;
17
21
  --spacer-32: 32px;
18
22
  --spacer-40: 40px;
@@ -99,9 +103,11 @@
99
103
  --code-terminal-font-weight: 450;
100
104
  --code-terminal-line-height: 18px;
101
105
  --font-weight-default: 400;
106
+ --font-weight-regular: var(--font-weight-default);
102
107
  --font-weight-code: 450;
103
108
  --font-weight-medium: 500;
104
109
  --font-weight-strong: 600;
110
+ --font-weight-semibold: var(--font-weight-strong);
105
111
  --z-index-dropdown: 2000;
106
112
  --z-index-drawer: 2500;
107
113
  --z-index-modal: 3000;
@@ -231,6 +237,9 @@
231
237
  --color-warning: var(--status-warning-default);
232
238
  --color-error: var(--status-error-default);
233
239
  --color-error-soft: var(--status-error-surface-l1);
240
+ --color-foreground-muted: var(--text-secondary);
241
+ --accent-blue: var(--status-primary-default);
242
+ --code-string: var(--code-text);
234
243
  }
235
244
 
236
245
  [data-theme="light"] {
@@ -238,22 +247,40 @@
238
247
  --bg-base-default: #FFFFFF;
239
248
  --bg-base-secondary: #F5F6F8;
240
249
  --bg-base-tertiary: #EDF0F5;
250
+ --bg-tooltip: #0F1117;
251
+ --bg-menu: #FFFFFF;
252
+ --bg-invert: #0F1117;
253
+ --bg-invert-hover: #1F2937;
254
+ --bg-invert-active: #374151;
255
+ --bg-invert-disabled: rgba(15, 17, 23, 0.12);
241
256
  --bg-overlay-l1: rgba(0, 0, 0, 0.04);
242
257
  --bg-overlay-l2: rgba(0, 0, 0, 0.06);
243
258
  --bg-overlay-l3: rgba(0, 0, 0, 0.08);
244
259
  --bg-overlay-l4: rgba(0, 0, 0, 0.12);
245
260
  --text-default: #0F1117;
261
+ --text-default-hover: #000000;
262
+ --text-default-active: #000000;
246
263
  --text-secondary: #4B5563;
264
+ --text-secondary-hover: #1F2937;
265
+ --text-secondary-active: #1F2937;
247
266
  --text-tertiary: #5B6370;
248
267
  --text-disabled: #525A68;
249
268
  --text-onbrand: #FFFFFF;
269
+ --text-onaccent: #FFFFFF;
250
270
  --icon-default: #0F1117;
271
+ --icon-default-hover: #000000;
272
+ --icon-default-active: #000000;
251
273
  --icon-secondary: #4B5563;
274
+ --icon-secondary-hover: #1F2937;
275
+ --icon-secondary-active: #1F2937;
252
276
  --icon-tertiary: #5B6370;
253
277
  --icon-disabled: #525A68;
278
+ --icon-onbrand: #FFFFFF;
279
+ --icon-onaccent: #FFFFFF;
254
280
  --border-neutral-l1: rgba(0, 0, 0, 0.12);
255
281
  --border-neutral-l2: rgba(0, 0, 0, 0.18);
256
282
  --border-neutral-l3: rgba(0, 0, 0, 0.25);
283
+ --border-contrast: #0F1117;
257
284
  --status-success-default: #16A34A;
258
285
  --status-warning-default: #CA8A04;
259
286
  --status-error-default: #DC2626;
@@ -294,6 +321,9 @@
294
321
  --color-warning: var(--status-warning-default);
295
322
  --color-error: var(--status-error-default);
296
323
  --color-error-soft: var(--status-error-surface-l1);
324
+ --color-foreground-muted: var(--text-secondary);
325
+ --accent-blue: var(--status-primary-default);
326
+ --code-string: var(--code-text);
297
327
  }
298
328
 
299
329
  :root,
@@ -312,6 +342,16 @@
312
342
  --icon-onbrand: #0C0C0D;
313
343
  }
314
344
 
345
+ [data-theme="light"],
346
+ [data-theme="light"][data-brand="green"],
347
+ [data-theme="light"] [data-brand="green"] {
348
+ --text-brand: #1C874F;
349
+ --text-brand-hover: #09884A;
350
+ --icon-brand: #1C874F;
351
+ --icon-brand-hover: #09884A;
352
+ --border-brand: #1C874F;
353
+ }
354
+
315
355
  [data-brand="blue"] {
316
356
  --bg-brand: #80C1FF;
317
357
  --bg-brand-hover: #6EA6DB;
@@ -327,6 +367,15 @@
327
367
  --icon-onbrand: #0C0C0D;
328
368
  }
329
369
 
370
+ [data-theme="light"][data-brand="blue"],
371
+ [data-theme="light"] [data-brand="blue"] {
372
+ --text-brand: #517AA2;
373
+ --text-brand-hover: #517AA2;
374
+ --icon-brand: #517AA2;
375
+ --icon-brand-hover: #517AA2;
376
+ --border-brand: #517AA2;
377
+ }
378
+
330
379
  [data-brand="purple"] {
331
380
  --bg-brand: #8E80FF;
332
381
  --bg-brand-hover: #9E92FF;
@@ -342,6 +391,15 @@
342
391
  --icon-onbrand: #0C0C0D;
343
392
  }
344
393
 
394
+ [data-theme="light"][data-brand="purple"],
395
+ [data-theme="light"] [data-brand="purple"] {
396
+ --text-brand: #7468D0;
397
+ --text-brand-hover: #756CBE;
398
+ --icon-brand: #7468D0;
399
+ --icon-brand-hover: #756CBE;
400
+ --border-brand: #7468D0;
401
+ }
402
+
345
403
  [data-brand="yellow"] {
346
404
  --bg-brand: #FFD080;
347
405
  --bg-brand-hover: #DBB36E;
@@ -357,6 +415,15 @@
357
415
  --icon-onbrand: #0C0C0D;
358
416
  }
359
417
 
418
+ [data-theme="light"][data-brand="yellow"],
419
+ [data-theme="light"] [data-brand="yellow"] {
420
+ --text-brand: #8C7347;
421
+ --text-brand-hover: #8C7347;
422
+ --icon-brand: #8C7347;
423
+ --icon-brand-hover: #8C7347;
424
+ --border-brand: #8C7347;
425
+ }
426
+
360
427
  [data-brand="red"] {
361
428
  --bg-brand: #FF8080;
362
429
  --bg-brand-hover: #DB6E6E;
@@ -372,6 +439,15 @@
372
439
  --icon-onbrand: #0C0C0D;
373
440
  }
374
441
 
442
+ [data-theme="light"][data-brand="red"],
443
+ [data-theme="light"] [data-brand="red"] {
444
+ --text-brand: #B65B5B;
445
+ --text-brand-hover: #B65B5B;
446
+ --icon-brand: #B65B5B;
447
+ --icon-brand-hover: #B65B5B;
448
+ --border-brand: #B65B5B;
449
+ }
450
+
375
451
  [data-brand="teal"] {
376
452
  --bg-brand: #2DD288;
377
453
  --bg-brand-hover: #27B575;
@@ -387,6 +463,15 @@
387
463
  --icon-onbrand: #0C0C0D;
388
464
  }
389
465
 
466
+ [data-theme="light"][data-brand="teal"],
467
+ [data-theme="light"] [data-brand="teal"] {
468
+ --text-brand: #1D8758;
469
+ --text-brand-hover: #1D8758;
470
+ --icon-brand: #1D8758;
471
+ --icon-brand-hover: #1D8758;
472
+ --border-brand: #1D8758;
473
+ }
474
+
390
475
  [data-brand="cyan"] {
391
476
  --bg-brand: #04CBE5;
392
477
  --bg-brand-hover: #27D2E9;
@@ -402,6 +487,15 @@
402
487
  --icon-onbrand: #0C0C0D;
403
488
  }
404
489
 
490
+ [data-theme="light"][data-brand="cyan"],
491
+ [data-theme="light"] [data-brand="cyan"] {
492
+ --text-brand: #038293;
493
+ --text-brand-hover: #188291;
494
+ --icon-brand: #038293;
495
+ --icon-brand-hover: #188291;
496
+ --border-brand: #038293;
497
+ }
498
+
405
499
  [data-brand="violet"] {
406
500
  --bg-brand: #BFA5FF;
407
501
  --bg-brand-hover: #A48EDB;
@@ -417,6 +511,15 @@
417
511
  --icon-onbrand: #0C0C0D;
418
512
  }
419
513
 
514
+ [data-theme="light"][data-brand="violet"],
515
+ [data-theme="light"] [data-brand="violet"] {
516
+ --text-brand: #7F6DA9;
517
+ --text-brand-hover: #7E6DA9;
518
+ --icon-brand: #7F6DA9;
519
+ --icon-brand-hover: #7E6DA9;
520
+ --border-brand: #7F6DA9;
521
+ }
522
+
420
523
  [data-brand="slate"] {
421
524
  --bg-brand: #AFB9CF;
422
525
  --bg-brand-hover: #979FB2;
@@ -432,6 +535,15 @@
432
535
  --icon-onbrand: #0C0C0D;
433
536
  }
434
537
 
538
+ [data-theme="light"][data-brand="slate"],
539
+ [data-theme="light"] [data-brand="slate"] {
540
+ --text-brand: #707785;
541
+ --text-brand-hover: #717685;
542
+ --icon-brand: #707785;
543
+ --icon-brand-hover: #717685;
544
+ --border-brand: #707785;
545
+ }
546
+
435
547
  [data-brand="neutral"] {
436
548
  --bg-brand: #BABABA;
437
549
  --bg-brand-hover: #A0A0A0;
@@ -446,4 +558,13 @@
446
558
  --text-onbrand: #0C0C0D;
447
559
  --icon-onbrand: #0C0C0D;
448
560
  }
561
+
562
+ [data-theme="light"][data-brand="neutral"],
563
+ [data-theme="light"] [data-brand="neutral"] {
564
+ --text-brand: #767676;
565
+ --text-brand-hover: #767676;
566
+ --icon-brand: #767676;
567
+ --icon-brand-hover: #767676;
568
+ --border-brand: #767676;
569
+ }
449
570
  }
package/dist/tree.js CHANGED
@@ -102,13 +102,17 @@ export function createTreeLazyLoadStateContract(options) {
102
102
  const loadedIds = normalizeTreeIds(options.loadedIds, knownIds);
103
103
  const errorIds = normalizeTreeIds(options.errorIds, knownIds);
104
104
  const retryableIds = normalizeTreeIds(options.retryableIds, knownIds);
105
+ const loadingSet = new Set(loadingIds);
106
+ const loadedSet = new Set(loadedIds);
107
+ const errorSet = new Set(errorIds);
108
+ const retryableSet = new Set(retryableIds);
105
109
  const nodeContracts = options.nodes.map((node) => treeLazyLoadNodeContract(node, {
106
110
  disabled,
107
111
  error: options.errors?.[node.id],
108
- loading: loadingIds.includes(node.id),
109
- loaded: loadedIds.includes(node.id),
110
- errored: errorIds.includes(node.id),
111
- retryable: retryableIds.includes(node.id),
112
+ loading: loadingSet.has(node.id),
113
+ loaded: loadedSet.has(node.id),
114
+ errored: errorSet.has(node.id),
115
+ retryable: retryableSet.has(node.id),
112
116
  }));
113
117
  const nodes = options.nodes.map((node, index) => treeNodeWithLazyContract(node, nodeContracts[index]));
114
118
  const loadableCount = nodeContracts.filter((node) => node.lazy).length;
@@ -405,9 +409,11 @@ function normalizeTreeIds(ids, knownIds) {
405
409
  if (ids === undefined)
406
410
  return [];
407
411
  const result = [];
412
+ const seen = new Set();
408
413
  for (const id of ids) {
409
- if (!knownIds.has(id) || result.includes(id))
414
+ if (!knownIds.has(id) || seen.has(id))
410
415
  continue;
416
+ seen.add(id);
411
417
  result.push(id);
412
418
  }
413
419
  return result;
@@ -90,6 +90,7 @@ export function createVirtualListResizeMeasurementContract(options) {
90
90
  }))
91
91
  .filter((entry) => entry.height !== undefined && Number.isFinite(entry.height) && entry.height > 0)
92
92
  .map((entry) => entry.key);
93
+ const measuredKeySet = new Set(measuredKeys);
93
94
  const measuringKeys = uniqueKeys(options.measuringIds ?? []);
94
95
  const plans = createDynamicVirtualListPlans(options.getMeasuredItemHeight === undefined
95
96
  ? options
@@ -99,7 +100,7 @@ export function createVirtualListResizeMeasurementContract(options) {
99
100
  });
100
101
  const estimatedKeys = options.items
101
102
  .map((_, index) => keyFor(options, index))
102
- .filter((key) => !measuredKeys.includes(key));
103
+ .filter((key) => !measuredKeySet.has(key));
103
104
  return {
104
105
  plans,
105
106
  measuredKeys,
@@ -174,8 +175,7 @@ export function calculateDynamicVirtualListState(options) {
174
175
  if (count === 0) {
175
176
  return createDynamicState(options, 0, 0, 0, -1, 0, []);
176
177
  }
177
- const metrics = dynamicItemMetrics(options, estimatedItemHeight);
178
- const totalSize = metrics.at(-1)?.end ?? 0;
178
+ const { metrics, measuredCount, totalSize } = dynamicItemMetrics(options, estimatedItemHeight);
179
179
  const viewportHeight = Math.max(0, options.viewportHeight);
180
180
  const scrollOffset = clamp(options.scrollOffset ?? 0, 0, Math.max(totalSize - viewportHeight, 0));
181
181
  const visibleStart = firstDynamicIndexAfter(metrics, scrollOffset);
@@ -186,7 +186,7 @@ export function calculateDynamicVirtualListState(options) {
186
186
  const end = ssrCount === undefined
187
187
  ? Math.min(count - 1, visibleEnd + (options.overscan ?? 2))
188
188
  : fallbackEnd;
189
- return createDynamicState(options, totalSize, scrollOffset, visibleStart, visibleEnd, metrics.filter((metric) => metric.measured).length, dynamicRangeItems(options, metrics, start, end));
189
+ return createDynamicState(options, totalSize, scrollOffset, visibleStart, visibleEnd, measuredCount, dynamicRangeItems(options, metrics, start, end));
190
190
  }
191
191
  export function createFixedVirtualListController(options) {
192
192
  let current = normalizeControllerOptions(options);
@@ -398,14 +398,17 @@ function dynamicItemFromMetric(options, index, metric) {
398
398
  function dynamicItemMetrics(options, estimatedItemHeight) {
399
399
  const metrics = [];
400
400
  let start = 0;
401
+ let measuredCount = 0;
401
402
  for (let index = 0; index < options.items.length; index++) {
402
403
  const measuredHeight = options.getItemHeight?.(options.items[index], index);
403
404
  const measured = measuredHeight !== undefined && Number.isFinite(measuredHeight) && measuredHeight > 0;
404
405
  const size = measured ? measuredHeight : estimatedItemHeight;
405
406
  metrics.push({ start, size, measured, end: start + size });
406
407
  start += size;
408
+ if (measured)
409
+ measuredCount += 1;
407
410
  }
408
- return metrics;
411
+ return { metrics, measuredCount, totalSize: start };
409
412
  }
410
413
  function createLayoutState(options, rowCount, columnCount, totalWidth, totalHeight, scrollLeft, scrollTop, visibleStartIndex, visibleEndIndex, visibleRowStart, visibleRowEnd, items, visibleColumnStart = 0, visibleColumnEnd = -1) {
411
414
  return {
@@ -468,14 +471,28 @@ function layoutItemFromRange(options, index, rowIndex, columnIndex, width, heigh
468
471
  };
469
472
  }
470
473
  function firstDynamicIndexAfter(metrics, offset) {
471
- const index = metrics.findIndex((metric) => metric.end > offset);
472
- return index === -1 ? Math.max(0, metrics.length - 1) : index;
474
+ let low = 0;
475
+ let high = metrics.length;
476
+ while (low < high) {
477
+ const mid = low + Math.floor((high - low) / 2);
478
+ if (metrics[mid].end > offset)
479
+ high = mid;
480
+ else
481
+ low = mid + 1;
482
+ }
483
+ return low === metrics.length ? Math.max(0, metrics.length - 1) : low;
473
484
  }
474
485
  function lastDynamicIndexBefore(metrics, offset) {
475
- const index = metrics.findIndex((metric) => metric.start >= offset);
476
- if (index === -1)
477
- return metrics.length - 1;
478
- return Math.max(0, index);
486
+ let low = 0;
487
+ let high = metrics.length;
488
+ while (low < high) {
489
+ const mid = low + Math.floor((high - low) / 2);
490
+ if (metrics[mid].start >= offset)
491
+ high = mid;
492
+ else
493
+ low = mid + 1;
494
+ }
495
+ return low === metrics.length ? metrics.length - 1 : Math.max(0, low);
479
496
  }
480
497
  function normalizeControllerOptions(options) {
481
498
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vobs/ui",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Optional design tokens, layouts and UI components for vobs.",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -41,6 +41,14 @@
41
41
  "types": "./dist/data-display.d.ts",
42
42
  "import": "./dist/data-display.js"
43
43
  },
44
+ "./page-state": {
45
+ "types": "./dist/page-state.d.ts",
46
+ "import": "./dist/page-state.js"
47
+ },
48
+ "./dialog": {
49
+ "types": "./dist/dialog.d.ts",
50
+ "import": "./dist/dialog.js"
51
+ },
44
52
  "./icons": {
45
53
  "types": "./dist/icons.d.ts",
46
54
  "import": "./dist/icons.js"
@@ -96,18 +104,18 @@
96
104
  "main": "./dist/index.js",
97
105
  "dependencies": {
98
106
  "@tanstack/virtual-core": "^3.17.7",
99
- "@vobs/forms": "0.1.0",
100
- "@vobs/i18n": "0.1.0",
101
- "@vobs/icons": "0.1.0",
102
- "@vobs/runtime-dom": "0.1.0",
103
- "@vobs/router": "0.1.0",
104
- "@vobs/server-renderer": "0.1.0",
105
- "@vobs/reactivity": "0.1.0"
107
+ "@vobs/icons": "0.3.0",
108
+ "@vobs/i18n": "0.3.0",
109
+ "@vobs/forms": "0.3.0",
110
+ "@vobs/runtime-dom": "0.3.0",
111
+ "@vobs/router": "0.3.0",
112
+ "@vobs/reactivity": "0.3.0",
113
+ "@vobs/server-renderer": "0.3.0"
106
114
  },
107
115
  "sideEffects": [
108
116
  "./dist/*.css"
109
117
  ],
110
118
  "engines": {
111
- "node": ">=20.19.0"
119
+ "node": ">=22.12.0"
112
120
  }
113
121
  }