vira 33.6.0 → 33.8.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.
Files changed (34) hide show
  1. package/dist/elements/vira-dropdown.element.d.ts +14 -1
  2. package/dist/elements/vira-dropdown.element.js +49 -4
  3. package/dist/icons/lucide/belarusian-ruble.icon.d.ts +8 -0
  4. package/dist/icons/lucide/belarusian-ruble.icon.js +13 -0
  5. package/dist/icons/lucide/calendar-chevrons-left.icon.d.ts +8 -0
  6. package/dist/icons/lucide/calendar-chevrons-left.icon.js +13 -0
  7. package/dist/icons/lucide/circle-fading-play.icon.d.ts +8 -0
  8. package/dist/icons/lucide/circle-fading-play.icon.js +13 -0
  9. package/dist/icons/lucide/cpu-ai.icon.d.ts +8 -0
  10. package/dist/icons/lucide/cpu-ai.icon.js +13 -0
  11. package/dist/icons/lucide/folder-user.icon.d.ts +8 -0
  12. package/dist/icons/lucide/folder-user.icon.js +13 -0
  13. package/dist/icons/lucide/folder-users.icon.d.ts +8 -0
  14. package/dist/icons/lucide/folder-users.icon.js +13 -0
  15. package/dist/icons/lucide/graph-star.icon.d.ts +8 -0
  16. package/dist/icons/lucide/graph-star.icon.js +13 -0
  17. package/dist/icons/lucide/guitar-electric.icon.d.ts +8 -0
  18. package/dist/icons/lucide/guitar-electric.icon.js +13 -0
  19. package/dist/icons/lucide/index.d.ts +13 -0
  20. package/dist/icons/lucide/index.js +13 -0
  21. package/dist/icons/lucide/message-circle-text.icon.d.ts +8 -0
  22. package/dist/icons/lucide/message-circle-text.icon.js +13 -0
  23. package/dist/icons/lucide/perfume-bottle.icon.d.ts +8 -0
  24. package/dist/icons/lucide/perfume-bottle.icon.js +13 -0
  25. package/dist/icons/lucide/server-nas.icon.d.ts +8 -0
  26. package/dist/icons/lucide/server-nas.icon.js +13 -0
  27. package/dist/icons/lucide/users-collaboration.icon.d.ts +8 -0
  28. package/dist/icons/lucide/users-collaboration.icon.js +13 -0
  29. package/dist/icons/lucide/users-round-collaboration.icon.d.ts +8 -0
  30. package/dist/icons/lucide/users-round-collaboration.icon.js +13 -0
  31. package/dist/util/shared-text-input-logic.js +3 -4
  32. package/dist/util/vira-theme-client.d.ts +56 -7
  33. package/dist/util/vira-theme-client.js +60 -19
  34. package/package.json +7 -7
@@ -52,6 +52,19 @@ export declare const ViraDropdown: import("element-vir").DeclarativeElementDefin
52
52
  * @default 24
53
53
  */
54
54
  menuHoverScrollSpeed: number;
55
+ /**
56
+ * When `isSearchable` is `true`, renders at most this many of the matching options (counted
57
+ * across groups, in their given order) followed by a hint row stating how many more exist.
58
+ * When omitted, all matching options are rendered.
59
+ */
60
+ maxShownOptions: number;
61
+ /**
62
+ * Creates the text of the hint row shown when `maxShownOptions` hides some matching
63
+ * options.
64
+ *
65
+ * @default hiddenCount => `+${hiddenCount}, type to narrow your search`
66
+ */
67
+ hiddenOptionsText: (hiddenCount: number) => string;
55
68
  }>, {
56
69
  isOpen: boolean;
57
70
  shouldSelectOnMouseUp: boolean;
@@ -69,4 +82,4 @@ export declare const ViraDropdown: import("element-vir").DeclarativeElementDefin
69
82
  selectedValuesChange: import("element-vir").DefineElementEvent<string[]>;
70
83
  /** `true` when the popover just opened, `false` when it just closed. */
71
84
  openChange: import("element-vir").DefineElementEvent<boolean>;
72
- }, "vira-dropdown-", "vira-dropdown-", readonly [], readonly ["leadingIcon", "prefixText", "searchInput", "trigger"]>;
85
+ }, "vira-dropdown-", "vira-dropdown-", readonly [], readonly ["hiddenOptionsHint", "leadingIcon", "prefixText", "searchInput", "trigger"]>;
@@ -26,6 +26,7 @@ import { ViraIcon } from './vira-icon.element.js';
26
26
  export const ViraDropdown = defineViraElement()({
27
27
  tagName: 'vira-dropdown',
28
28
  testIds: [
29
+ 'hiddenOptionsHint',
29
30
  'leadingIcon',
30
31
  'prefixText',
31
32
  'searchInput',
@@ -147,7 +148,8 @@ export const ViraDropdown = defineViraElement()({
147
148
  }
148
149
 
149
150
  .option-group-label,
150
- .no-options {
151
+ .no-options,
152
+ .hidden-options-hint {
151
153
  ${noUserSelect};
152
154
  text-align: left;
153
155
  }
@@ -158,7 +160,8 @@ export const ViraDropdown = defineViraElement()({
158
160
  font-weight: ${viraFormCssVars['vira-form-label-font-weight'].value};
159
161
  }
160
162
 
161
- .no-options {
163
+ .no-options,
164
+ .hidden-options-hint {
162
165
  cursor: default;
163
166
  opacity: 0.6;
164
167
  /* Aligns with the label text of each ViraMenuItem, past its check icon. */
@@ -304,6 +307,29 @@ export const ViraDropdown = defineViraElement()({
304
307
  const filteredFlatOptions = filteredOptions.flatMap((entry) => {
305
308
  return isViraDropdownOptionGroup(entry) ? entry.options : [entry];
306
309
  });
310
+ let remainingShownCount = inputs.isSearchable
311
+ ? (inputs.maxShownOptions ?? Infinity)
312
+ : Infinity;
313
+ /** `filteredOptions` truncated to `maxShownOptions` options, dropping emptied groups. */
314
+ const shownOptions = remainingShownCount < filteredFlatOptions.length
315
+ ? filterMap(filteredOptions, (entry) => {
316
+ const options = (isViraDropdownOptionGroup(entry) ? entry.options : [entry]).slice(0, Math.max(remainingShownCount, 0));
317
+ remainingShownCount -= options.length;
318
+ if (!options.length) {
319
+ return undefined;
320
+ }
321
+ return isViraDropdownOptionGroup(entry)
322
+ ? {
323
+ ...entry,
324
+ options,
325
+ }
326
+ : entry;
327
+ }, check.isTruthy)
328
+ : filteredOptions;
329
+ const hiddenOptionsCount = filteredFlatOptions.length -
330
+ shownOptions.flatMap((entry) => {
331
+ return isViraDropdownOptionGroup(entry) ? entry.options : [entry];
332
+ }).length;
307
333
  function findSearchInput() {
308
334
  const searchInput = host.shadowRoot.querySelector('.search-input');
309
335
  return searchInput instanceof HTMLInputElement ? searchInput : undefined;
@@ -352,7 +378,7 @@ export const ViraDropdown = defineViraElement()({
352
378
  `;
353
379
  }
354
380
  /** Drag-select actions in the same order as the rendered menu items. */
355
- const menuItemActions = filteredOptions.flatMap((entry) => {
381
+ const menuItemActions = shownOptions.flatMap((entry) => {
356
382
  const options = isViraDropdownOptionGroup(entry) ? entry.options : [entry];
357
383
  const optionActions = options.map((option) => {
358
384
  return option.disabled
@@ -404,7 +430,7 @@ export const ViraDropdown = defineViraElement()({
404
430
  })}
405
431
  >
406
432
  ${filteredFlatOptions.length
407
- ? filteredOptions.map((entry) => {
433
+ ? shownOptions.map((entry) => {
408
434
  return isViraDropdownOptionGroup(entry)
409
435
  ? html `
410
436
  ${renderGroupLabel(entry)} ${renderOptionEntries(entry.options)}
@@ -414,6 +440,25 @@ export const ViraDropdown = defineViraElement()({
414
440
  : html `
415
441
  <div class="no-options">${inputs.noOptionsText}</div>
416
442
  `}
443
+ ${hiddenOptionsCount
444
+ ? html `
445
+ <div
446
+ class="hidden-options-hint"
447
+ ${testId(testIds.hiddenOptionsHint)}
448
+ ${listen('mousedown', (event) => {
449
+ /** Keeps focus in the search input. */
450
+ event.preventDefault();
451
+ })}
452
+ ${listen('click', (event) => {
453
+ /** ViraPopoverTrigger closes on any click inside its popover. */
454
+ event.stopPropagation();
455
+ })}
456
+ >
457
+ ${inputs.hiddenOptionsText?.(hiddenOptionsCount) ??
458
+ `+${hiddenOptionsCount}, type to narrow your search`}
459
+ </div>
460
+ `
461
+ : nothing}
417
462
  </${ViraMenu}>
418
463
  `;
419
464
  const triggerTemplate = html `
@@ -0,0 +1,8 @@
1
+ /** This file is automatically updated by generate-lucide-icons.ts */
2
+ /**
3
+ * Lucide's `BelarusianRuble` icon, rendered at 16px.
4
+ *
5
+ * @category Lucide Icon
6
+ * @see https://lucide.dev/icons/belarusian-ruble
7
+ */
8
+ export declare const LucideBelarusianRubleIcon: import("../icon-svg.js").ViraIconSvg<"BelarusianRuble">;
@@ -0,0 +1,13 @@
1
+ /** This file is automatically updated by generate-lucide-icons.ts */
2
+ import { BelarusianRuble as svg } from 'lucide-static';
3
+ import { createLucideIcon } from '../lucide-icon.js';
4
+ /**
5
+ * Lucide's `BelarusianRuble` icon, rendered at 16px.
6
+ *
7
+ * @category Lucide Icon
8
+ * @see https://lucide.dev/icons/belarusian-ruble
9
+ */
10
+ export const LucideBelarusianRubleIcon = createLucideIcon({
11
+ name: 'BelarusianRuble',
12
+ svg,
13
+ });
@@ -0,0 +1,8 @@
1
+ /** This file is automatically updated by generate-lucide-icons.ts */
2
+ /**
3
+ * Lucide's `CalendarChevronsLeft` icon, rendered at 16px.
4
+ *
5
+ * @category Lucide Icon
6
+ * @see https://lucide.dev/icons/calendar-chevrons-left
7
+ */
8
+ export declare const LucideCalendarChevronsLeftIcon: import("../icon-svg.js").ViraIconSvg<"CalendarChevronsLeft">;
@@ -0,0 +1,13 @@
1
+ /** This file is automatically updated by generate-lucide-icons.ts */
2
+ import { CalendarChevronsLeft as svg } from 'lucide-static';
3
+ import { createLucideIcon } from '../lucide-icon.js';
4
+ /**
5
+ * Lucide's `CalendarChevronsLeft` icon, rendered at 16px.
6
+ *
7
+ * @category Lucide Icon
8
+ * @see https://lucide.dev/icons/calendar-chevrons-left
9
+ */
10
+ export const LucideCalendarChevronsLeftIcon = createLucideIcon({
11
+ name: 'CalendarChevronsLeft',
12
+ svg,
13
+ });
@@ -0,0 +1,8 @@
1
+ /** This file is automatically updated by generate-lucide-icons.ts */
2
+ /**
3
+ * Lucide's `CircleFadingPlay` icon, rendered at 16px.
4
+ *
5
+ * @category Lucide Icon
6
+ * @see https://lucide.dev/icons/circle-fading-play
7
+ */
8
+ export declare const LucideCircleFadingPlayIcon: import("../icon-svg.js").ViraIconSvg<"CircleFadingPlay">;
@@ -0,0 +1,13 @@
1
+ /** This file is automatically updated by generate-lucide-icons.ts */
2
+ import { CircleFadingPlay as svg } from 'lucide-static';
3
+ import { createLucideIcon } from '../lucide-icon.js';
4
+ /**
5
+ * Lucide's `CircleFadingPlay` icon, rendered at 16px.
6
+ *
7
+ * @category Lucide Icon
8
+ * @see https://lucide.dev/icons/circle-fading-play
9
+ */
10
+ export const LucideCircleFadingPlayIcon = createLucideIcon({
11
+ name: 'CircleFadingPlay',
12
+ svg,
13
+ });
@@ -0,0 +1,8 @@
1
+ /** This file is automatically updated by generate-lucide-icons.ts */
2
+ /**
3
+ * Lucide's `CpuAi` icon, rendered at 16px.
4
+ *
5
+ * @category Lucide Icon
6
+ * @see https://lucide.dev/icons/cpu-ai
7
+ */
8
+ export declare const LucideCpuAiIcon: import("../icon-svg.js").ViraIconSvg<"CpuAi">;
@@ -0,0 +1,13 @@
1
+ /** This file is automatically updated by generate-lucide-icons.ts */
2
+ import { CpuAi as svg } from 'lucide-static';
3
+ import { createLucideIcon } from '../lucide-icon.js';
4
+ /**
5
+ * Lucide's `CpuAi` icon, rendered at 16px.
6
+ *
7
+ * @category Lucide Icon
8
+ * @see https://lucide.dev/icons/cpu-ai
9
+ */
10
+ export const LucideCpuAiIcon = createLucideIcon({
11
+ name: 'CpuAi',
12
+ svg,
13
+ });
@@ -0,0 +1,8 @@
1
+ /** This file is automatically updated by generate-lucide-icons.ts */
2
+ /**
3
+ * Lucide's `FolderUser` icon, rendered at 16px.
4
+ *
5
+ * @category Lucide Icon
6
+ * @see https://lucide.dev/icons/folder-user
7
+ */
8
+ export declare const LucideFolderUserIcon: import("../icon-svg.js").ViraIconSvg<"FolderUser">;
@@ -0,0 +1,13 @@
1
+ /** This file is automatically updated by generate-lucide-icons.ts */
2
+ import { FolderUser as svg } from 'lucide-static';
3
+ import { createLucideIcon } from '../lucide-icon.js';
4
+ /**
5
+ * Lucide's `FolderUser` icon, rendered at 16px.
6
+ *
7
+ * @category Lucide Icon
8
+ * @see https://lucide.dev/icons/folder-user
9
+ */
10
+ export const LucideFolderUserIcon = createLucideIcon({
11
+ name: 'FolderUser',
12
+ svg,
13
+ });
@@ -0,0 +1,8 @@
1
+ /** This file is automatically updated by generate-lucide-icons.ts */
2
+ /**
3
+ * Lucide's `FolderUsers` icon, rendered at 16px.
4
+ *
5
+ * @category Lucide Icon
6
+ * @see https://lucide.dev/icons/folder-users
7
+ */
8
+ export declare const LucideFolderUsersIcon: import("../icon-svg.js").ViraIconSvg<"FolderUsers">;
@@ -0,0 +1,13 @@
1
+ /** This file is automatically updated by generate-lucide-icons.ts */
2
+ import { FolderUsers as svg } from 'lucide-static';
3
+ import { createLucideIcon } from '../lucide-icon.js';
4
+ /**
5
+ * Lucide's `FolderUsers` icon, rendered at 16px.
6
+ *
7
+ * @category Lucide Icon
8
+ * @see https://lucide.dev/icons/folder-users
9
+ */
10
+ export const LucideFolderUsersIcon = createLucideIcon({
11
+ name: 'FolderUsers',
12
+ svg,
13
+ });
@@ -0,0 +1,8 @@
1
+ /** This file is automatically updated by generate-lucide-icons.ts */
2
+ /**
3
+ * Lucide's `GraphStar` icon, rendered at 16px.
4
+ *
5
+ * @category Lucide Icon
6
+ * @see https://lucide.dev/icons/graph-star
7
+ */
8
+ export declare const LucideGraphStarIcon: import("../icon-svg.js").ViraIconSvg<"GraphStar">;
@@ -0,0 +1,13 @@
1
+ /** This file is automatically updated by generate-lucide-icons.ts */
2
+ import { GraphStar as svg } from 'lucide-static';
3
+ import { createLucideIcon } from '../lucide-icon.js';
4
+ /**
5
+ * Lucide's `GraphStar` icon, rendered at 16px.
6
+ *
7
+ * @category Lucide Icon
8
+ * @see https://lucide.dev/icons/graph-star
9
+ */
10
+ export const LucideGraphStarIcon = createLucideIcon({
11
+ name: 'GraphStar',
12
+ svg,
13
+ });
@@ -0,0 +1,8 @@
1
+ /** This file is automatically updated by generate-lucide-icons.ts */
2
+ /**
3
+ * Lucide's `GuitarElectric` icon, rendered at 16px.
4
+ *
5
+ * @category Lucide Icon
6
+ * @see https://lucide.dev/icons/guitar-electric
7
+ */
8
+ export declare const LucideGuitarElectricIcon: import("../icon-svg.js").ViraIconSvg<"GuitarElectric">;
@@ -0,0 +1,13 @@
1
+ /** This file is automatically updated by generate-lucide-icons.ts */
2
+ import { GuitarElectric as svg } from 'lucide-static';
3
+ import { createLucideIcon } from '../lucide-icon.js';
4
+ /**
5
+ * Lucide's `GuitarElectric` icon, rendered at 16px.
6
+ *
7
+ * @category Lucide Icon
8
+ * @see https://lucide.dev/icons/guitar-electric
9
+ */
10
+ export const LucideGuitarElectricIcon = createLucideIcon({
11
+ name: 'GuitarElectric',
12
+ svg,
13
+ });
@@ -157,6 +157,7 @@ export * from './beef-off.icon.js';
157
157
  export * from './beef.icon.js';
158
158
  export * from './beer-off.icon.js';
159
159
  export * from './beer.icon.js';
160
+ export * from './belarusian-ruble.icon.js';
160
161
  export * from './bell-check.icon.js';
161
162
  export * from './bell-dot.icon.js';
162
163
  export * from './bell-electric.icon.js';
@@ -269,6 +270,7 @@ export * from './calendar-arrow-down.icon.js';
269
270
  export * from './calendar-arrow-up.icon.js';
270
271
  export * from './calendar-check-2.icon.js';
271
272
  export * from './calendar-check.icon.js';
273
+ export * from './calendar-chevrons-left.icon.js';
272
274
  export * from './calendar-chevrons-right.icon.js';
273
275
  export * from './calendar-clock.icon.js';
274
276
  export * from './calendar-cog.icon.js';
@@ -393,6 +395,7 @@ export * from './circle-ellipsis.icon.js';
393
395
  export * from './circle-equal.icon.js';
394
396
  export * from './circle-euro.icon.js';
395
397
  export * from './circle-fading-arrow-up.icon.js';
398
+ export * from './circle-fading-play.icon.js';
396
399
  export * from './circle-fading-plus.icon.js';
397
400
  export * from './circle-gauge.icon.js';
398
401
  export * from './circle-minus.icon.js';
@@ -518,6 +521,7 @@ export * from './corner-right-down.icon.js';
518
521
  export * from './corner-right-up.icon.js';
519
522
  export * from './corner-up-left.icon.js';
520
523
  export * from './corner-up-right.icon.js';
524
+ export * from './cpu-ai.icon.js';
521
525
  export * from './cpu.icon.js';
522
526
  export * from './creative-commons.icon.js';
523
527
  export * from './credit-card-check.icon.js';
@@ -744,6 +748,8 @@ export * from './folder-symlink.icon.js';
744
748
  export * from './folder-sync.icon.js';
745
749
  export * from './folder-tree.icon.js';
746
750
  export * from './folder-up.icon.js';
751
+ export * from './folder-user.icon.js';
752
+ export * from './folder-users.icon.js';
747
753
  export * from './folder-x.icon.js';
748
754
  export * from './folder.icon.js';
749
755
  export * from './folders.icon.js';
@@ -805,6 +811,7 @@ export * from './goal.icon.js';
805
811
  export * from './gpu.icon.js';
806
812
  export * from './graduation-cap.icon.js';
807
813
  export * from './grape.icon.js';
814
+ export * from './graph-star.icon.js';
808
815
  export * from './grid-2x2-check.icon.js';
809
816
  export * from './grid-2x2-plus.icon.js';
810
817
  export * from './grid-2x2-x.icon.js';
@@ -816,6 +823,7 @@ export * from './grip-vertical.icon.js';
816
823
  export * from './grip.icon.js';
817
824
  export * from './groceries.icon.js';
818
825
  export * from './group.icon.js';
826
+ export * from './guitar-electric.icon.js';
819
827
  export * from './guitar.icon.js';
820
828
  export * from './ham.icon.js';
821
829
  export * from './hamburger.icon.js';
@@ -1059,6 +1067,7 @@ export * from './message-circle-off.icon.js';
1059
1067
  export * from './message-circle-plus.icon.js';
1060
1068
  export * from './message-circle-question-mark.icon.js';
1061
1069
  export * from './message-circle-reply.icon.js';
1070
+ export * from './message-circle-text.icon.js';
1062
1071
  export * from './message-circle-warning.icon.js';
1063
1072
  export * from './message-circle-x.icon.js';
1064
1073
  export * from './message-circle.icon.js';
@@ -1234,6 +1243,7 @@ export * from './pencil-sparkles.icon.js';
1234
1243
  export * from './pencil.icon.js';
1235
1244
  export * from './pentagon.icon.js';
1236
1245
  export * from './percent.icon.js';
1246
+ export * from './perfume-bottle.icon.js';
1237
1247
  export * from './person-standing.icon.js';
1238
1248
  export * from './phi.icon.js';
1239
1249
  export * from './philippine-peso.icon.js';
@@ -1417,6 +1427,7 @@ export * from './separator-horizontal.icon.js';
1417
1427
  export * from './separator-vertical.icon.js';
1418
1428
  export * from './server-cog.icon.js';
1419
1429
  export * from './server-crash.icon.js';
1430
+ export * from './server-nas.icon.js';
1420
1431
  export * from './server-off.icon.js';
1421
1432
  export * from './server-plus.icon.js';
1422
1433
  export * from './server.icon.js';
@@ -1781,6 +1792,8 @@ export * from './user-shield.icon.js';
1781
1792
  export * from './user-star.icon.js';
1782
1793
  export * from './user-x.icon.js';
1783
1794
  export * from './user.icon.js';
1795
+ export * from './users-collaboration.icon.js';
1796
+ export * from './users-round-collaboration.icon.js';
1784
1797
  export * from './users-round.icon.js';
1785
1798
  export * from './users.icon.js';
1786
1799
  export * from './utensils-crossed.icon.js';
@@ -157,6 +157,7 @@ export * from './beef-off.icon.js';
157
157
  export * from './beef.icon.js';
158
158
  export * from './beer-off.icon.js';
159
159
  export * from './beer.icon.js';
160
+ export * from './belarusian-ruble.icon.js';
160
161
  export * from './bell-check.icon.js';
161
162
  export * from './bell-dot.icon.js';
162
163
  export * from './bell-electric.icon.js';
@@ -269,6 +270,7 @@ export * from './calendar-arrow-down.icon.js';
269
270
  export * from './calendar-arrow-up.icon.js';
270
271
  export * from './calendar-check-2.icon.js';
271
272
  export * from './calendar-check.icon.js';
273
+ export * from './calendar-chevrons-left.icon.js';
272
274
  export * from './calendar-chevrons-right.icon.js';
273
275
  export * from './calendar-clock.icon.js';
274
276
  export * from './calendar-cog.icon.js';
@@ -393,6 +395,7 @@ export * from './circle-ellipsis.icon.js';
393
395
  export * from './circle-equal.icon.js';
394
396
  export * from './circle-euro.icon.js';
395
397
  export * from './circle-fading-arrow-up.icon.js';
398
+ export * from './circle-fading-play.icon.js';
396
399
  export * from './circle-fading-plus.icon.js';
397
400
  export * from './circle-gauge.icon.js';
398
401
  export * from './circle-minus.icon.js';
@@ -518,6 +521,7 @@ export * from './corner-right-down.icon.js';
518
521
  export * from './corner-right-up.icon.js';
519
522
  export * from './corner-up-left.icon.js';
520
523
  export * from './corner-up-right.icon.js';
524
+ export * from './cpu-ai.icon.js';
521
525
  export * from './cpu.icon.js';
522
526
  export * from './creative-commons.icon.js';
523
527
  export * from './credit-card-check.icon.js';
@@ -744,6 +748,8 @@ export * from './folder-symlink.icon.js';
744
748
  export * from './folder-sync.icon.js';
745
749
  export * from './folder-tree.icon.js';
746
750
  export * from './folder-up.icon.js';
751
+ export * from './folder-user.icon.js';
752
+ export * from './folder-users.icon.js';
747
753
  export * from './folder-x.icon.js';
748
754
  export * from './folder.icon.js';
749
755
  export * from './folders.icon.js';
@@ -805,6 +811,7 @@ export * from './goal.icon.js';
805
811
  export * from './gpu.icon.js';
806
812
  export * from './graduation-cap.icon.js';
807
813
  export * from './grape.icon.js';
814
+ export * from './graph-star.icon.js';
808
815
  export * from './grid-2x2-check.icon.js';
809
816
  export * from './grid-2x2-plus.icon.js';
810
817
  export * from './grid-2x2-x.icon.js';
@@ -816,6 +823,7 @@ export * from './grip-vertical.icon.js';
816
823
  export * from './grip.icon.js';
817
824
  export * from './groceries.icon.js';
818
825
  export * from './group.icon.js';
826
+ export * from './guitar-electric.icon.js';
819
827
  export * from './guitar.icon.js';
820
828
  export * from './ham.icon.js';
821
829
  export * from './hamburger.icon.js';
@@ -1059,6 +1067,7 @@ export * from './message-circle-off.icon.js';
1059
1067
  export * from './message-circle-plus.icon.js';
1060
1068
  export * from './message-circle-question-mark.icon.js';
1061
1069
  export * from './message-circle-reply.icon.js';
1070
+ export * from './message-circle-text.icon.js';
1062
1071
  export * from './message-circle-warning.icon.js';
1063
1072
  export * from './message-circle-x.icon.js';
1064
1073
  export * from './message-circle.icon.js';
@@ -1234,6 +1243,7 @@ export * from './pencil-sparkles.icon.js';
1234
1243
  export * from './pencil.icon.js';
1235
1244
  export * from './pentagon.icon.js';
1236
1245
  export * from './percent.icon.js';
1246
+ export * from './perfume-bottle.icon.js';
1237
1247
  export * from './person-standing.icon.js';
1238
1248
  export * from './phi.icon.js';
1239
1249
  export * from './philippine-peso.icon.js';
@@ -1417,6 +1427,7 @@ export * from './separator-horizontal.icon.js';
1417
1427
  export * from './separator-vertical.icon.js';
1418
1428
  export * from './server-cog.icon.js';
1419
1429
  export * from './server-crash.icon.js';
1430
+ export * from './server-nas.icon.js';
1420
1431
  export * from './server-off.icon.js';
1421
1432
  export * from './server-plus.icon.js';
1422
1433
  export * from './server.icon.js';
@@ -1781,6 +1792,8 @@ export * from './user-shield.icon.js';
1781
1792
  export * from './user-star.icon.js';
1782
1793
  export * from './user-x.icon.js';
1783
1794
  export * from './user.icon.js';
1795
+ export * from './users-collaboration.icon.js';
1796
+ export * from './users-round-collaboration.icon.js';
1784
1797
  export * from './users-round.icon.js';
1785
1798
  export * from './users.icon.js';
1786
1799
  export * from './utensils-crossed.icon.js';
@@ -0,0 +1,8 @@
1
+ /** This file is automatically updated by generate-lucide-icons.ts */
2
+ /**
3
+ * Lucide's `MessageCircleText` icon, rendered at 16px.
4
+ *
5
+ * @category Lucide Icon
6
+ * @see https://lucide.dev/icons/message-circle-text
7
+ */
8
+ export declare const LucideMessageCircleTextIcon: import("../icon-svg.js").ViraIconSvg<"MessageCircleText">;
@@ -0,0 +1,13 @@
1
+ /** This file is automatically updated by generate-lucide-icons.ts */
2
+ import { MessageCircleText as svg } from 'lucide-static';
3
+ import { createLucideIcon } from '../lucide-icon.js';
4
+ /**
5
+ * Lucide's `MessageCircleText` icon, rendered at 16px.
6
+ *
7
+ * @category Lucide Icon
8
+ * @see https://lucide.dev/icons/message-circle-text
9
+ */
10
+ export const LucideMessageCircleTextIcon = createLucideIcon({
11
+ name: 'MessageCircleText',
12
+ svg,
13
+ });
@@ -0,0 +1,8 @@
1
+ /** This file is automatically updated by generate-lucide-icons.ts */
2
+ /**
3
+ * Lucide's `PerfumeBottle` icon, rendered at 16px.
4
+ *
5
+ * @category Lucide Icon
6
+ * @see https://lucide.dev/icons/perfume-bottle
7
+ */
8
+ export declare const LucidePerfumeBottleIcon: import("../icon-svg.js").ViraIconSvg<"PerfumeBottle">;
@@ -0,0 +1,13 @@
1
+ /** This file is automatically updated by generate-lucide-icons.ts */
2
+ import { PerfumeBottle as svg } from 'lucide-static';
3
+ import { createLucideIcon } from '../lucide-icon.js';
4
+ /**
5
+ * Lucide's `PerfumeBottle` icon, rendered at 16px.
6
+ *
7
+ * @category Lucide Icon
8
+ * @see https://lucide.dev/icons/perfume-bottle
9
+ */
10
+ export const LucidePerfumeBottleIcon = createLucideIcon({
11
+ name: 'PerfumeBottle',
12
+ svg,
13
+ });
@@ -0,0 +1,8 @@
1
+ /** This file is automatically updated by generate-lucide-icons.ts */
2
+ /**
3
+ * Lucide's `ServerNas` icon, rendered at 16px.
4
+ *
5
+ * @category Lucide Icon
6
+ * @see https://lucide.dev/icons/server-nas
7
+ */
8
+ export declare const LucideServerNasIcon: import("../icon-svg.js").ViraIconSvg<"ServerNas">;
@@ -0,0 +1,13 @@
1
+ /** This file is automatically updated by generate-lucide-icons.ts */
2
+ import { ServerNas as svg } from 'lucide-static';
3
+ import { createLucideIcon } from '../lucide-icon.js';
4
+ /**
5
+ * Lucide's `ServerNas` icon, rendered at 16px.
6
+ *
7
+ * @category Lucide Icon
8
+ * @see https://lucide.dev/icons/server-nas
9
+ */
10
+ export const LucideServerNasIcon = createLucideIcon({
11
+ name: 'ServerNas',
12
+ svg,
13
+ });
@@ -0,0 +1,8 @@
1
+ /** This file is automatically updated by generate-lucide-icons.ts */
2
+ /**
3
+ * Lucide's `UsersCollaboration` icon, rendered at 16px.
4
+ *
5
+ * @category Lucide Icon
6
+ * @see https://lucide.dev/icons/users-collaboration
7
+ */
8
+ export declare const LucideUsersCollaborationIcon: import("../icon-svg.js").ViraIconSvg<"UsersCollaboration">;
@@ -0,0 +1,13 @@
1
+ /** This file is automatically updated by generate-lucide-icons.ts */
2
+ import { UsersCollaboration as svg } from 'lucide-static';
3
+ import { createLucideIcon } from '../lucide-icon.js';
4
+ /**
5
+ * Lucide's `UsersCollaboration` icon, rendered at 16px.
6
+ *
7
+ * @category Lucide Icon
8
+ * @see https://lucide.dev/icons/users-collaboration
9
+ */
10
+ export const LucideUsersCollaborationIcon = createLucideIcon({
11
+ name: 'UsersCollaboration',
12
+ svg,
13
+ });
@@ -0,0 +1,8 @@
1
+ /** This file is automatically updated by generate-lucide-icons.ts */
2
+ /**
3
+ * Lucide's `UsersRoundCollaboration` icon, rendered at 16px.
4
+ *
5
+ * @category Lucide Icon
6
+ * @see https://lucide.dev/icons/users-round-collaboration
7
+ */
8
+ export declare const LucideUsersRoundCollaborationIcon: import("../icon-svg.js").ViraIconSvg<"UsersRoundCollaboration">;
@@ -0,0 +1,13 @@
1
+ /** This file is automatically updated by generate-lucide-icons.ts */
2
+ import { UsersRoundCollaboration as svg } from 'lucide-static';
3
+ import { createLucideIcon } from '../lucide-icon.js';
4
+ /**
5
+ * Lucide's `UsersRoundCollaboration` icon, rendered at 16px.
6
+ *
7
+ * @category Lucide Icon
8
+ * @see https://lucide.dev/icons/users-round-collaboration
9
+ */
10
+ export const LucideUsersRoundCollaborationIcon = createLucideIcon({
11
+ name: 'UsersRoundCollaboration',
12
+ svg,
13
+ });
@@ -26,12 +26,11 @@ function isAllowed({ value: rawValue, allowed, blocked }) {
26
26
  input: value,
27
27
  matcher: allowed,
28
28
  });
29
- const isBlockedCharacter = blocked
30
- ? doesMatch({
29
+ const isBlockedCharacter = !!blocked &&
30
+ doesMatch({
31
31
  input: value,
32
32
  matcher: blocked,
33
- })
34
- : false;
33
+ });
35
34
  return isAllowedCharacter && !isBlockedCharacter;
36
35
  }
37
36
  /**
@@ -1,5 +1,6 @@
1
- import { type MaybePromise, type PartialWithUndefined } from '@augment-vir/common';
1
+ import { type MaybePromise, type PartialWithUndefined, type RequireOneOrNone } from '@augment-vir/common';
2
2
  import { LocalStorageClient } from '@electrovir/local-storage-client';
3
+ import { ListenTarget } from 'typed-event-target';
3
4
  /**
4
5
  * A user-facing selection of which theme to display. `Auto` follows the system color scheme; the
5
6
  * other values force a specific theme regardless of system preference.
@@ -11,6 +12,39 @@ export declare enum ViraThemeSelection {
11
12
  Dark = "dark",
12
13
  Auto = "auto"
13
14
  }
15
+ declare const ViraThemeAppliedEvent_base: (new (eventInitDict: {
16
+ bubbles?: boolean;
17
+ cancelable?: boolean;
18
+ composed?: boolean;
19
+ detail: ViraThemeSelection.Light | ViraThemeSelection.Dark;
20
+ }) => import("typed-event-target").TypedCustomEvent<ViraThemeSelection.Light | ViraThemeSelection.Dark, "vira-theme-applied">) & Pick<{
21
+ new (type: string, eventInitDict?: EventInit): Event;
22
+ prototype: Event;
23
+ readonly NONE: 0;
24
+ readonly CAPTURING_PHASE: 1;
25
+ readonly AT_TARGET: 2;
26
+ readonly BUBBLING_PHASE: 3;
27
+ }, "NONE" | "CAPTURING_PHASE" | "AT_TARGET" | "BUBBLING_PHASE" | "prototype"> & Pick<import("typed-event-target").TypedCustomEvent<ViraThemeSelection.Light | ViraThemeSelection.Dark, "vira-theme-applied">, "type">;
28
+ /**
29
+ * Fired by {@link ViraThemeClient} whenever its applied theme changes, including when the system
30
+ * color scheme flips while the selection is `Auto`. Not fired when a new selection leaves the
31
+ * applied theme the same (such as switching from `Dark` to `Auto` on a dark system).
32
+ *
33
+ * @category Internal
34
+ */
35
+ export declare class ViraThemeAppliedEvent extends ViraThemeAppliedEvent_base {
36
+ }
37
+ /**
38
+ * Persists the selection of a {@link ViraThemeClient}.
39
+ *
40
+ * @category Internal
41
+ */
42
+ export type ViraThemeStorage = {
43
+ /** The previously saved selection. `undefined` (nothing saved yet) is treated as `Auto`. */
44
+ init: ViraThemeSelection | undefined;
45
+ /** Called after the selection has already been applied, so it can be async. */
46
+ save(selection: ViraThemeSelection): MaybePromise<void>;
47
+ };
14
48
  /**
15
49
  * Used by {@link ViraThemeClient} to apply themes. By default, vira themes will be applied (via
16
50
  * {@link defaultApplyThemeCallback}).
@@ -38,6 +72,7 @@ export type ViraThemeClientParams = PartialWithUndefined<{
38
72
  * will be used.
39
73
  */
40
74
  applyTheme: ApplyThemeCallback;
75
+ }> & RequireOneOrNone<{
41
76
  /**
42
77
  * Override the LocalStorage store name used for theme persistence. Useful if a single page
43
78
  * hosts multiple isolated theme clients.
@@ -45,6 +80,11 @@ export type ViraThemeClientParams = PartialWithUndefined<{
45
80
  * @default 'vira-theme'
46
81
  */
47
82
  storeName: string;
83
+ /**
84
+ * Where to persist the selected theme. If not provided, the selection is persisted in
85
+ * LocalStorage under `storeName`.
86
+ */
87
+ storage: ViraThemeStorage;
48
88
  }>;
49
89
  declare const themeStorageShapes: {
50
90
  selectedTheme: import("object-shape-tester").Shape<{
@@ -53,18 +93,25 @@ declare const themeStorageShapes: {
53
93
  };
54
94
  /**
55
95
  * Tracks the user's {@link ViraThemeSelection} and bridges it to a consumer-supplied `applyTheme`
56
- * callback. Persists the selection in LocalStorage via an internal `LocalStorageClient`, and
57
- * listens for system color-scheme changes to re-apply the theme in auto mode without persisting.
96
+ * callback. Persists the selection through `storage` (LocalStorage by default), and listens for
97
+ * system color-scheme changes to re-apply the theme in auto mode without persisting. Listen for
98
+ * {@link ViraThemeAppliedEvent} to react to the displayed theme changing.
58
99
  *
59
100
  * The initial theme is applied during construction.
60
101
  *
61
102
  * @category Util
62
103
  */
63
- export declare class ViraThemeClient {
104
+ export declare class ViraThemeClient extends ListenTarget<ViraThemeAppliedEvent> {
64
105
  /** The callback that will be called to apply a new theme. */
65
106
  protected readonly applyThemeCallback: ApplyThemeCallback;
66
- /** Contains the user's last selected theme, saving and loading it to disk for persistence. */
67
- protected readonly localStorageClient: LocalStorageClient<typeof themeStorageShapes>;
107
+ /** The default storage, only created when no `storage` param is given. */
108
+ protected readonly localStorageClient: LocalStorageClient<typeof themeStorageShapes> | undefined;
109
+ /** Persists the user's selected theme. */
110
+ protected readonly storage: ViraThemeStorage;
111
+ /** The user's selected theme. */
112
+ protected selectedTheme: ViraThemeSelection;
113
+ /** The theme that is currently displayed. */
114
+ protected appliedTheme: Exclude<ViraThemeSelection, ViraThemeSelection.Auto> | undefined;
68
115
  /** A callback to remove the global theme preference listener. */
69
116
  protected readonly removeThemePreferenceListener: () => void;
70
117
  constructor(params?: Readonly<ViraThemeClientParams>);
@@ -73,11 +120,13 @@ export declare class ViraThemeClient {
73
120
  * get out of sync.
74
121
  */
75
122
  getCurrentTheme(): ViraThemeSelection;
123
+ /** The theme that is currently displayed, with `Auto` resolved from the system color scheme. */
124
+ getAppliedTheme(): Exclude<ViraThemeSelection, ViraThemeSelection.Auto>;
76
125
  /** Set the selected theme. */
77
126
  setSelectedTheme(selection: ViraThemeSelection): void;
78
127
  /** Cleanup internal state and listeners. */
79
128
  destroy(): void;
80
- /** Apply the currently selected theme. */
129
+ /** Apply the given selection and fire {@link ViraThemeAppliedEvent} if the result changed. */
81
130
  protected applySelection(selection: ViraThemeSelection): void;
82
131
  }
83
132
  export {};
@@ -2,7 +2,7 @@ import { assert } from '@augment-vir/assert';
2
2
  import { LocalStorageClient } from '@electrovir/local-storage-client';
3
3
  import { defineShape, enumShape } from 'object-shape-tester';
4
4
  import { applyColorThemeViaStyleElement } from 'theme-vir/dist/color-theme/apply-color-theme.js';
5
- import { listenTo } from 'typed-event-target';
5
+ import { defineTypedCustomEvent, ListenTarget, listenTo } from 'typed-event-target';
6
6
  import { viraTheme, viraThemeDarkOverride } from '../styles/vira-color-theme.js';
7
7
  /**
8
8
  * A user-facing selection of which theme to display. `Auto` follows the system color scheme; the
@@ -16,6 +16,15 @@ export var ViraThemeSelection;
16
16
  ViraThemeSelection["Dark"] = "dark";
17
17
  ViraThemeSelection["Auto"] = "auto";
18
18
  })(ViraThemeSelection || (ViraThemeSelection = {}));
19
+ /**
20
+ * Fired by {@link ViraThemeClient} whenever its applied theme changes, including when the system
21
+ * color scheme flips while the selection is `Auto`. Not fired when a new selection leaves the
22
+ * applied theme the same (such as switching from `Dark` to `Auto` on a dark system).
23
+ *
24
+ * @category Internal
25
+ */
26
+ export class ViraThemeAppliedEvent extends defineTypedCustomEvent()('vira-theme-applied') {
27
+ }
19
28
  /**
20
29
  * Default implementation of {@link ApplyThemeCallback}, which simply applies Vira themes.
21
30
  *
@@ -32,62 +41,94 @@ const themeStorageShapes = {
32
41
  };
33
42
  /**
34
43
  * Tracks the user's {@link ViraThemeSelection} and bridges it to a consumer-supplied `applyTheme`
35
- * callback. Persists the selection in LocalStorage via an internal `LocalStorageClient`, and
36
- * listens for system color-scheme changes to re-apply the theme in auto mode without persisting.
44
+ * callback. Persists the selection through `storage` (LocalStorage by default), and listens for
45
+ * system color-scheme changes to re-apply the theme in auto mode without persisting. Listen for
46
+ * {@link ViraThemeAppliedEvent} to react to the displayed theme changing.
37
47
  *
38
48
  * The initial theme is applied during construction.
39
49
  *
40
50
  * @category Util
41
51
  */
42
- export class ViraThemeClient {
52
+ export class ViraThemeClient extends ListenTarget {
43
53
  /** The callback that will be called to apply a new theme. */
44
54
  applyThemeCallback = defaultApplyThemeCallback;
45
- /** Contains the user's last selected theme, saving and loading it to disk for persistence. */
55
+ /** The default storage, only created when no `storage` param is given. */
46
56
  localStorageClient;
57
+ /** Persists the user's selected theme. */
58
+ storage;
59
+ /** The user's selected theme. */
60
+ selectedTheme;
61
+ /** The theme that is currently displayed. */
62
+ appliedTheme;
47
63
  /** A callback to remove the global theme preference listener. */
48
64
  removeThemePreferenceListener = listenTo(globalThis.matchMedia(darkSchemeMediaQuery), 'change', (event) => {
49
65
  assert.instanceOf(event, MediaQueryListEvent);
50
- if (this.getCurrentTheme() === ViraThemeSelection.Auto) {
51
- void this.applyThemeCallback({
52
- useDarkTheme: event.matches,
53
- });
66
+ if (this.selectedTheme === ViraThemeSelection.Auto) {
67
+ this.applySelection(ViraThemeSelection.Auto);
54
68
  }
55
69
  });
56
70
  constructor(params = {}) {
71
+ super();
57
72
  if (params.applyTheme) {
58
73
  this.applyThemeCallback = params.applyTheme;
59
74
  }
60
- this.localStorageClient = new LocalStorageClient(themeStorageShapes, {
61
- storeName: params.storeName || 'vira-theme',
62
- });
63
- this.applySelection(this.getCurrentTheme());
75
+ if (params.storage) {
76
+ this.storage = params.storage;
77
+ }
78
+ else {
79
+ const localStorageClient = new LocalStorageClient(themeStorageShapes, {
80
+ storeName: params.storeName || 'vira-theme',
81
+ });
82
+ this.localStorageClient = localStorageClient;
83
+ this.storage = {
84
+ init: localStorageClient.get.selectedTheme()?.theme,
85
+ save(theme) {
86
+ localStorageClient.set.selectedTheme({
87
+ theme,
88
+ });
89
+ },
90
+ };
91
+ }
92
+ this.selectedTheme = this.storage.init || ViraThemeSelection.Auto;
93
+ this.applySelection(this.selectedTheme);
64
94
  }
65
95
  /**
66
96
  * The currently selected theme. If you use multiple clients to set the same theme, this might
67
97
  * get out of sync.
68
98
  */
69
99
  getCurrentTheme() {
70
- return this.localStorageClient.get.selectedTheme()?.theme || ViraThemeSelection.Auto;
100
+ return this.selectedTheme;
101
+ }
102
+ /** The theme that is currently displayed, with `Auto` resolved from the system color scheme. */
103
+ getAppliedTheme() {
104
+ return this.appliedTheme || ViraThemeSelection.Light;
71
105
  }
72
106
  /** Set the selected theme. */
73
107
  setSelectedTheme(selection) {
108
+ this.selectedTheme = selection;
74
109
  this.applySelection(selection);
75
- this.localStorageClient.set.selectedTheme({
76
- theme: selection,
77
- });
110
+ void this.storage.save(selection);
78
111
  }
79
112
  /** Cleanup internal state and listeners. */
80
113
  destroy() {
81
114
  this.removeThemePreferenceListener();
82
- this.localStorageClient.destroy();
115
+ this.localStorageClient?.destroy();
116
+ super.destroy();
83
117
  }
84
- /** Apply the currently selected theme. */
118
+ /** Apply the given selection and fire {@link ViraThemeAppliedEvent} if the result changed. */
85
119
  applySelection(selection) {
86
120
  const useDarkTheme = selection === ViraThemeSelection.Dark ||
87
121
  (selection === ViraThemeSelection.Auto &&
88
122
  globalThis.matchMedia(darkSchemeMediaQuery).matches);
123
+ const appliedTheme = useDarkTheme ? ViraThemeSelection.Dark : ViraThemeSelection.Light;
89
124
  void this.applyThemeCallback({
90
125
  useDarkTheme,
91
126
  });
127
+ if (appliedTheme !== this.appliedTheme) {
128
+ this.appliedTheme = appliedTheme;
129
+ this.dispatch(new ViraThemeAppliedEvent({
130
+ detail: appliedTheme,
131
+ }));
132
+ }
92
133
  }
93
134
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vira",
3
- "version": "33.6.0",
3
+ "version": "33.8.0",
4
4
  "description": "A simple and highly versatile design system using element-vir.",
5
5
  "keywords": [
6
6
  "design",
@@ -38,9 +38,9 @@
38
38
  "test:docs": "virmator docs check"
39
39
  },
40
40
  "dependencies": {
41
- "@augment-vir/assert": "^32.5.0",
42
- "@augment-vir/common": "^32.5.0",
43
- "@augment-vir/web": "^32.5.0",
41
+ "@augment-vir/assert": "^32.6.0",
42
+ "@augment-vir/common": "^32.6.0",
43
+ "@augment-vir/web": "^32.6.0",
44
44
  "@electrovir/color": "^2.0.0",
45
45
  "@electrovir/local-storage-client": "^0.1.1",
46
46
  "date-vir": "^9.4.0",
@@ -48,15 +48,15 @@
48
48
  "json-schema-to-ts": "^3.1.1",
49
49
  "lit": "^3.3.3",
50
50
  "lit-css-vars": "^3.6.3",
51
- "lucide-static": "1.55.0",
51
+ "lucide-static": "1.57.0",
52
52
  "object-shape-tester": "^6.15.1",
53
53
  "observavir": "^2.4.2",
54
54
  "page-active": "^1.0.4",
55
- "spa-router-vir": "^6.8.0",
55
+ "spa-router-vir": "^6.9.0",
56
56
  "typed-event-target": "^4.3.4"
57
57
  },
58
58
  "devDependencies": {
59
- "@augment-vir/test": "^32.5.0",
59
+ "@augment-vir/test": "^32.6.0",
60
60
  "@web/dev-server-esbuild": "^2.0.0",
61
61
  "@web/test-runner": "^1.0.0",
62
62
  "@web/test-runner-commands": "^1.0.1",