vira 32.3.0 → 33.0.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 (65) hide show
  1. package/dist/directives/index.d.ts +2 -0
  2. package/dist/directives/index.js +2 -0
  3. package/dist/directives/popover.directive.d.ts +29 -0
  4. package/dist/directives/popover.directive.js +45 -0
  5. package/dist/directives/tooltip.directive.d.ts +60 -0
  6. package/dist/directives/tooltip.directive.js +99 -0
  7. package/dist/elements/color/vira-color-picker.element.d.ts +1 -1
  8. package/dist/elements/color/vira-color-picker.element.js +17 -15
  9. package/dist/elements/index.d.ts +5 -5
  10. package/dist/elements/index.js +5 -5
  11. package/dist/elements/{pop-up → popover}/vira-menu-item.element.d.ts +4 -4
  12. package/dist/elements/{pop-up → popover}/vira-menu-item.element.js +3 -3
  13. package/dist/elements/popover/vira-menu-trigger.element.d.ts +56 -0
  14. package/dist/elements/{pop-up → popover}/vira-menu-trigger.element.js +19 -30
  15. package/dist/elements/popover/vira-menu.element.d.ts +35 -0
  16. package/dist/elements/popover/vira-menu.element.js +262 -0
  17. package/dist/elements/popover/vira-popover-trigger.element.d.ts +49 -0
  18. package/dist/elements/popover/vira-popover-trigger.element.js +301 -0
  19. package/dist/elements/popover/vira-tooltip.element.d.ts +10 -0
  20. package/dist/elements/popover/vira-tooltip.element.js +176 -0
  21. package/dist/elements/vira-checkbox.element.d.ts +3 -3
  22. package/dist/elements/vira-checkbox.element.js +4 -3
  23. package/dist/elements/vira-date-input.element.d.ts +2 -1
  24. package/dist/elements/vira-date-input.element.js +3 -2
  25. package/dist/elements/vira-drawer.element.js +1 -1
  26. package/dist/elements/vira-dropdown.element.d.ts +29 -12
  27. package/dist/elements/vira-dropdown.element.js +173 -60
  28. package/dist/elements/vira-form.element.js +13 -22
  29. package/dist/elements/vira-input.element.d.ts +2 -1
  30. package/dist/elements/vira-input.element.js +2 -2
  31. package/dist/elements/vira-json-form.element.js +40 -36
  32. package/dist/elements/vira-modal.element.js +1 -1
  33. package/dist/elements/vira-tabs.element.d.ts +3 -9
  34. package/dist/elements/vira-tabs.element.js +5 -6
  35. package/dist/elements/vira-text-area.element.d.ts +2 -1
  36. package/dist/elements/vira-text-area.element.js +3 -2
  37. package/dist/elements/vira-theme-switcher.element.js +3 -3
  38. package/dist/index.d.ts +1 -0
  39. package/dist/index.js +1 -0
  40. package/dist/util/anchored-popover.d.ts +40 -0
  41. package/dist/util/anchored-popover.js +70 -0
  42. package/dist/util/index.d.ts +4 -3
  43. package/dist/util/index.js +4 -3
  44. package/dist/util/{pop-up-helpers.d.ts → menu-helpers.d.ts} +4 -18
  45. package/dist/util/{pop-up-helpers.js → menu-helpers.js} +2 -17
  46. package/dist/util/popover-manager.d.ts +123 -0
  47. package/dist/util/popover-manager.js +412 -0
  48. package/dist/util/vira-dropdown-option.d.ts +30 -0
  49. package/dist/util/vira-dropdown-option.js +8 -0
  50. package/dist/util/vira-form-fields.d.ts +5 -10
  51. package/dist/util/vira-form-fields.js +15 -11
  52. package/dist/util/vira-theme-client.d.ts +1 -1
  53. package/dist/util/vira-theme-client.js +3 -3
  54. package/package.json +2 -1
  55. package/dist/elements/pop-up/vira-menu-trigger.element.d.ts +0 -45
  56. package/dist/elements/pop-up/vira-menu.element.d.ts +0 -36
  57. package/dist/elements/pop-up/vira-menu.element.js +0 -88
  58. package/dist/elements/pop-up/vira-pop-up-trigger.element.d.ts +0 -109
  59. package/dist/elements/pop-up/vira-pop-up-trigger.element.js +0 -408
  60. package/dist/elements/vira-select.element.d.ts +0 -44
  61. package/dist/elements/vira-select.element.js +0 -418
  62. package/dist/util/pop-up-manager.d.ts +0 -200
  63. package/dist/util/pop-up-manager.js +0 -258
  64. package/dist/util/vira-select-option.d.ts +0 -31
  65. package/dist/util/vira-select-option.js +0 -8
@@ -0,0 +1,412 @@
1
+ import { check } from '@augment-vir/assert';
2
+ import { walkActiveElement } from '@augment-vir/web';
3
+ import { convertDuration } from 'date-vir';
4
+ import { NavActivateEvent, NavDirection } from 'device-navigation';
5
+ import { listenToPageActivation } from 'page-active';
6
+ import { listenTo, listenToGlobal } from 'typed-event-target';
7
+ import { anchoredPopoverRetryClass } from './anchored-popover.js';
8
+ /**
9
+ * Used to prevent popovers from closing when a text input is active.
10
+ *
11
+ * @category Internal
12
+ */
13
+ export function isInputLikeElement(element) {
14
+ return ((element instanceof HTMLInputElement &&
15
+ (element.type === 'text' ||
16
+ element.type === 'search' ||
17
+ element.type === 'email' ||
18
+ element.type === 'url' ||
19
+ element.type === 'tel' ||
20
+ element.type === 'password' ||
21
+ element.type === 'number')) ||
22
+ element instanceof HTMLTextAreaElement ||
23
+ (element instanceof HTMLElement && element.isContentEditable));
24
+ }
25
+ /**
26
+ * Whether a keyboard click (a click with `detail` of `0`) came from pressing Enter in a text input,
27
+ * which browsers turn into a click on the form's first button.
28
+ *
29
+ * @category Internal
30
+ */
31
+ export function isTextInputActive() {
32
+ let isActive = false;
33
+ walkActiveElement(({ element }) => {
34
+ isActive = isInputLikeElement(element);
35
+ return isActive;
36
+ });
37
+ return isActive;
38
+ }
39
+ /**
40
+ * Determines if a mouse event landed on a scrollbar rather than on element content. Scrollbar
41
+ * interactions fire mouse events on the scrolling element itself (or on `<html>` for the page
42
+ * scrollbar), so an outside-click check would otherwise treat dragging a scrollbar as a click away
43
+ * from the popover and dismiss it mid-drag.
44
+ *
45
+ * @category Internal
46
+ */
47
+ export function isMouseEventOnScrollbar(event) {
48
+ const target = event.composedPath()[0];
49
+ if (!(target instanceof Element)) {
50
+ return false;
51
+ }
52
+ const rect = target.getBoundingClientRect();
53
+ /**
54
+ * `clientLeft` and `clientTop` are the border widths, which also include the scrollbar itself
55
+ * when it's rendered on the leading edge (RTL layouts).
56
+ */
57
+ const clientLeft = rect.left + target.clientLeft;
58
+ const clientTop = rect.top + target.clientTop;
59
+ const outsideHorizontally = event.clientX < clientLeft || event.clientX >= clientLeft + target.clientWidth;
60
+ const outsideVertically = event.clientY < clientTop || event.clientY >= clientTop + target.clientHeight;
61
+ return ((outsideHorizontally && target.scrollHeight > target.clientHeight) ||
62
+ (outsideVertically && target.scrollWidth > target.clientWidth));
63
+ }
64
+ /**
65
+ * The interaction on the anchor element that shows and hides a popover.
66
+ *
67
+ * @category Popover
68
+ */
69
+ export var PopoverTrigger;
70
+ (function (PopoverTrigger) {
71
+ /**
72
+ * Shows the popover shortly after the anchor is hovered or focused and hides it when the anchor
73
+ * is no longer hovered or focused.
74
+ */
75
+ PopoverTrigger["Hover"] = "hover";
76
+ /** Toggles the popover when the anchor is clicked. An arrow key opens it. */
77
+ PopoverTrigger["Click"] = "click";
78
+ /**
79
+ * Toggles the popover as soon as the mouse is pressed on the anchor, so it can be dragged over
80
+ * the popover's contents and released on one of them. Keyboard clicks and arrow keys also
81
+ * work.
82
+ */
83
+ PopoverTrigger["Mousedown"] = "mousedown";
84
+ })(PopoverTrigger || (PopoverTrigger = {}));
85
+ const defaultDelays = {
86
+ [PopoverTrigger.Hover]: {
87
+ milliseconds: 300,
88
+ },
89
+ };
90
+ function listenToAnchor(anchor, eventName, listener) {
91
+ return listenTo(anchor, eventName, (event) => {
92
+ listener(event);
93
+ });
94
+ }
95
+ const anchorListeners = {
96
+ [PopoverTrigger.Hover](popoverManager, anchor) {
97
+ return [
98
+ listenToAnchor(anchor, 'mouseenter', () => {
99
+ popoverManager.scheduleShow();
100
+ }),
101
+ listenToAnchor(anchor, 'focusin', () => {
102
+ popoverManager.scheduleShow();
103
+ }),
104
+ listenToAnchor(anchor, 'mouseleave', () => {
105
+ popoverManager.hide();
106
+ }),
107
+ listenToAnchor(anchor, 'focusout', () => {
108
+ popoverManager.hide();
109
+ }),
110
+ ];
111
+ },
112
+ [PopoverTrigger.Click](popoverManager, anchor) {
113
+ return [
114
+ listenToAnchor(anchor, 'click', (event) => {
115
+ if (event.detail !== 0 || !isTextInputActive()) {
116
+ popoverManager.toggle();
117
+ }
118
+ }),
119
+ listenToAnchor(anchor, 'keydown', (event) => {
120
+ popoverManager.openOnArrow(event);
121
+ }),
122
+ ];
123
+ },
124
+ [PopoverTrigger.Mousedown](popoverManager, anchor) {
125
+ return [
126
+ listenToAnchor(anchor, 'mousedown', (event) => {
127
+ if (event.button === 0 && !isMouseEventOnScrollbar(event)) {
128
+ const target = event.composedPath()[0];
129
+ if (!(target instanceof Element && isInputLikeElement(target))) {
130
+ /**
131
+ * Without this, WebKit scrolls the anchor's scroll containers when the
132
+ * pointer is dragged past their edges on the way to a popover item.
133
+ */
134
+ event.preventDefault();
135
+ /** Preventing the default also prevents the focus that mousedown gives. */
136
+ event
137
+ .composedPath()
138
+ .find((eventTarget) => {
139
+ return (eventTarget instanceof HTMLElement && eventTarget.tabIndex >= 0);
140
+ })
141
+ ?.focus({
142
+ preventScroll: true,
143
+ });
144
+ }
145
+ popoverManager.toggle();
146
+ }
147
+ }),
148
+ listenToAnchor(anchor, 'click', (event) => {
149
+ /** Mouse clicks were already handled by the mousedown. */
150
+ if (event.detail === 0 && !isTextInputActive()) {
151
+ popoverManager.toggle();
152
+ }
153
+ }),
154
+ listenToAnchor(anchor, 'keydown', (event) => {
155
+ popoverManager.openOnArrow(event);
156
+ }),
157
+ ];
158
+ },
159
+ };
160
+ /**
161
+ * Opens and closes a single `popover` element that is anchored to another element: it opens the
162
+ * popover in response to the anchor's interactions (see {@link PopoverTrigger}) and closes it on
163
+ * Escape, an outside mousedown, or the page going inactive. Style the popover with
164
+ * `createAnchoredPopoverStyles`.
165
+ *
166
+ * @category Popover
167
+ */
168
+ export class PopoverManager {
169
+ navController;
170
+ anchor;
171
+ options;
172
+ /** The most recently opened popover. */
173
+ popover;
174
+ showTimeout;
175
+ /** Closes the popover after `options.timeout`. */
176
+ hideTimeout;
177
+ /** Removes the listeners for the current anchor and `trigger`. */
178
+ removeAnchorListeners;
179
+ /** Removes the global listeners attached while the popover is open. */
180
+ removeOpenListeners;
181
+ constructor(
182
+ /**
183
+ * Enables arrow key navigation within the open popover. Omit this for popovers that have
184
+ * nothing to navigate, like tooltips.
185
+ */
186
+ navController) {
187
+ this.navController = navController;
188
+ }
189
+ /** Whether the popover is open. */
190
+ isOpen() {
191
+ return !!this.popover?.matches(':popover-open');
192
+ }
193
+ /** Call this each time the anchor or options might have changed. The `popover` directive does. */
194
+ update(anchor, options) {
195
+ const previousTrigger = this.options?.trigger;
196
+ this.options = options;
197
+ if (anchor === this.anchor && options.trigger === previousTrigger) {
198
+ return;
199
+ }
200
+ this.removeAnchorListeners?.();
201
+ this.anchor = anchor;
202
+ const removeListeners = check.isBoolean(options.trigger)
203
+ ? []
204
+ : anchorListeners[options.trigger](this, anchor);
205
+ this.removeAnchorListeners = () => {
206
+ removeListeners.forEach((removeListener) => removeListener());
207
+ };
208
+ this.applyForcedState();
209
+ }
210
+ /** Opens or closes the popover when `trigger` is `true` or `false`. */
211
+ applyForcedState() {
212
+ if (this.options?.trigger === true) {
213
+ /**
214
+ * Deferred because on first render lit updates directives before inserting the template
215
+ * into the document, and a disconnected popover cannot open.
216
+ */
217
+ queueMicrotask(() => {
218
+ if (this.options?.trigger === true) {
219
+ this.scheduleShow();
220
+ }
221
+ });
222
+ }
223
+ else if (this.options?.trigger === false) {
224
+ this.close();
225
+ }
226
+ }
227
+ /** Opens the popover right away. */
228
+ show() {
229
+ clearTimeout(this.showTimeout);
230
+ this.showTimeout = undefined;
231
+ if (!this.anchor || !this.options || this.isOpen()) {
232
+ return;
233
+ }
234
+ this.popover = this.options.getPopover();
235
+ this.popover.showPopover({
236
+ source: this.anchor,
237
+ });
238
+ this.removeOpenListeners = this.attachOpenListeners();
239
+ if (this.options.timeout) {
240
+ this.hideTimeout = setTimeout(() => {
241
+ this.hide();
242
+ }, convertDuration(this.options.timeout, {
243
+ milliseconds: true,
244
+ }).milliseconds);
245
+ }
246
+ }
247
+ /** Closes the popover, unless `trigger` forces it open, and cancels a scheduled show. */
248
+ hide() {
249
+ if (this.options?.trigger !== true) {
250
+ this.close();
251
+ }
252
+ }
253
+ /** Opens the popover after `options.delay`. */
254
+ scheduleShow() {
255
+ if (this.showTimeout || this.isOpen() || !this.options) {
256
+ return;
257
+ }
258
+ const rawDelay = this.options.delay ||
259
+ (check.isBoolean(this.options.trigger) ? {} : defaultDelays[this.options.trigger]);
260
+ const delay = rawDelay &&
261
+ convertDuration(rawDelay, {
262
+ milliseconds: true,
263
+ });
264
+ if (delay && delay.milliseconds > 0) {
265
+ this.showTimeout = setTimeout(() => {
266
+ this.show();
267
+ }, delay.milliseconds);
268
+ }
269
+ else {
270
+ this.show();
271
+ }
272
+ }
273
+ /** Opens the popover, or closes it (and cancels a delayed open). */
274
+ toggle() {
275
+ if (this.isOpen() || this.showTimeout) {
276
+ this.hide();
277
+ }
278
+ else {
279
+ this.scheduleShow();
280
+ }
281
+ }
282
+ /** Opens a closed popover when an arrow key is pressed. */
283
+ openOnArrow(event) {
284
+ if (event.code.startsWith('Arrow')) {
285
+ this.scheduleShow();
286
+ }
287
+ }
288
+ /** Closes the popover and removes all listeners. */
289
+ destroy() {
290
+ this.removeAnchorListeners?.();
291
+ this.removeAnchorListeners = undefined;
292
+ this.anchor = undefined;
293
+ this.close();
294
+ }
295
+ /** Closes the popover even when `trigger` forces it open. */
296
+ close() {
297
+ clearTimeout(this.showTimeout);
298
+ this.showTimeout = undefined;
299
+ clearTimeout(this.hideTimeout);
300
+ this.hideTimeout = undefined;
301
+ this.removeOpenListeners?.();
302
+ this.removeOpenListeners = undefined;
303
+ if (this.isOpen()) {
304
+ this.popover?.hidePopover();
305
+ }
306
+ }
307
+ /** Attaches the global listeners that only matter while the popover is open. */
308
+ attachOpenListeners() {
309
+ const navController = this.navController;
310
+ const removeListeners = [
311
+ listenToGlobal('resize', () => {
312
+ this.popover?.classList.toggle(anchoredPopoverRetryClass);
313
+ /** `nearest` does not scroll at all when the anchor is already fully visible. */
314
+ this.anchor?.scrollIntoView({
315
+ block: 'nearest',
316
+ inline: 'nearest',
317
+ });
318
+ }, {
319
+ passive: true,
320
+ }),
321
+ listenToGlobal('scroll', () => {
322
+ this.popover?.classList.toggle(anchoredPopoverRetryClass);
323
+ }, {
324
+ /** Scroll events do not bubble, so this catches scrolling in any element. */
325
+ capture: true,
326
+ passive: true,
327
+ }),
328
+ listenToPageActivation(false, (isPageActive) => {
329
+ if (!isPageActive) {
330
+ this.hide();
331
+ }
332
+ }),
333
+ navController?.listen(NavActivateEvent, (event) => {
334
+ const target = event.composedPath()[0];
335
+ if (target instanceof Element && isInputLikeElement(target)) {
336
+ return;
337
+ }
338
+ if (event.detail.success) {
339
+ this.options?.onNavSelect?.(event.detail.coords);
340
+ navController.currentNavEntry?.entry.focus(true);
341
+ event.stopImmediatePropagation();
342
+ event.preventDefault();
343
+ }
344
+ }),
345
+ listenToGlobal('mousedown', (event) => {
346
+ const path = event.composedPath();
347
+ if ((this.anchor && path.includes(this.anchor)) ||
348
+ (this.popover && path.includes(this.popover)) ||
349
+ isMouseEventOnScrollbar(event)) {
350
+ return;
351
+ }
352
+ this.hide();
353
+ }, {
354
+ passive: true,
355
+ }),
356
+ listenToGlobal('keydown', (event) => {
357
+ const keyCode = event.code;
358
+ if (keyCode === 'Escape') {
359
+ this.hide();
360
+ }
361
+ else if (navController) {
362
+ const target = event.composedPath()[0];
363
+ if (target instanceof Element && isInputLikeElement(target)) {
364
+ return;
365
+ }
366
+ if (keyCode === 'ArrowDown') {
367
+ event.stopImmediatePropagation();
368
+ event.preventDefault();
369
+ navController.navigate({
370
+ direction: NavDirection.Down,
371
+ allowWrapping: false,
372
+ });
373
+ }
374
+ else if (keyCode === 'ArrowUp') {
375
+ event.stopImmediatePropagation();
376
+ event.preventDefault();
377
+ navController.navigate({
378
+ direction: NavDirection.Up,
379
+ allowWrapping: false,
380
+ });
381
+ }
382
+ else if (keyCode === 'ArrowLeft') {
383
+ event.stopImmediatePropagation();
384
+ event.preventDefault();
385
+ navController.navigate({
386
+ direction: NavDirection.Left,
387
+ allowWrapping: false,
388
+ });
389
+ }
390
+ else if (keyCode === 'ArrowRight') {
391
+ event.stopImmediatePropagation();
392
+ event.preventDefault();
393
+ navController.navigate({
394
+ direction: NavDirection.Right,
395
+ allowWrapping: false,
396
+ });
397
+ }
398
+ else if ((keyCode === 'Enter' || keyCode === 'Return' || keyCode === 'Space') &&
399
+ navController.enterInto({
400
+ fallbackToActivate: true,
401
+ }).success) {
402
+ event.stopImmediatePropagation();
403
+ event.preventDefault();
404
+ }
405
+ }
406
+ }),
407
+ ].filter(check.isDefined);
408
+ return () => {
409
+ removeListeners.forEach((removeListener) => removeListener());
410
+ };
411
+ }
412
+ }
@@ -0,0 +1,30 @@
1
+ import { type PartialWithUndefined } from '@augment-vir/common';
2
+ /**
3
+ * Options for `ViraDropdown`.
4
+ *
5
+ * @category Dropdown
6
+ * @category Elements
7
+ */
8
+ export type ViraDropdownOption = {
9
+ /** A value or id, used to keep track of which option is selected. */
10
+ value: string;
11
+ label: string;
12
+ } & PartialWithUndefined<{
13
+ disabled: boolean;
14
+ }>;
15
+ /**
16
+ * A group of options for `ViraDropdown`, rendered under a group label.
17
+ *
18
+ * @category Dropdown
19
+ * @category Elements
20
+ */
21
+ export type ViraDropdownOptionGroup = {
22
+ groupName: string;
23
+ options: ReadonlyArray<Readonly<ViraDropdownOption>>;
24
+ };
25
+ /**
26
+ * Type guard to determine if a `ViraDropdown` options entry is a group.
27
+ *
28
+ * @category Internal
29
+ */
30
+ export declare function isViraDropdownOptionGroup(entry: Readonly<ViraDropdownOption> | Readonly<ViraDropdownOptionGroup>): entry is Readonly<ViraDropdownOptionGroup>;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Type guard to determine if a `ViraDropdown` options entry is a group.
3
+ *
4
+ * @category Internal
5
+ */
6
+ export function isViraDropdownOptionGroup(entry) {
7
+ return 'groupName' in entry;
8
+ }
@@ -2,7 +2,7 @@ import { type PartialWithUndefined } from '@augment-vir/common';
2
2
  import { type FullDate, type Timezone } from 'date-vir';
3
3
  import { type HtmlInterpolation } from 'element-vir';
4
4
  import { type ViraIconSvg } from '../icons/icon-svg.js';
5
- import { type ViraSelectOption } from './vira-select-option.js';
5
+ import { type ViraDropdownOption } from './vira-dropdown-option.js';
6
6
  /**
7
7
  * Form field types for {@link ViraFormField}.
8
8
  *
@@ -29,7 +29,7 @@ export declare enum ViraFormFieldType {
29
29
  * @category Internal
30
30
  */
31
31
  export type CommonViraFormFields = {
32
- label: string;
32
+ label: string | HtmlInterpolation;
33
33
  } & PartialWithUndefined<{
34
34
  /** Applies a test id to the form field element. */
35
35
  testId: string;
@@ -73,24 +73,19 @@ export type ViraFormField = ({
73
73
  }> & CommonViraFormFields) | ({
74
74
  type: ViraFormFieldType.Select;
75
75
  value: string | undefined;
76
- options: ReadonlyArray<Readonly<ViraSelectOption>>;
76
+ options: ReadonlyArray<Readonly<ViraDropdownOption>>;
77
77
  } & PartialWithUndefined<{
78
78
  placeholder: string;
79
79
  icon: ViraIconSvg;
80
80
  }> & CommonViraFormFields) | ({
81
81
  type: ViraFormFieldType.Checkbox;
82
82
  value: boolean | undefined;
83
- /**
84
- * Label for the checkbox. Unlike other field types, this accepts arbitrary HTML so the
85
- * checkbox's label slot can be filled with rich content.
86
- */
87
- label: HtmlInterpolation;
88
83
  } & PartialWithUndefined<{
89
84
  /** The checkbox will be filled with a form selection color when it is checked. */
90
85
  fillWhenChecked: boolean;
91
86
  /** The checkbox will be filled with a form error color when it is unchecked. */
92
87
  fillWhenUnchecked: boolean;
93
- }> & Omit<CommonViraFormFields, 'label'>) | ({
88
+ }> & CommonViraFormFields) | ({
94
89
  type: ViraFormFieldType.Number;
95
90
  value: number | undefined;
96
91
  } & PartialWithUndefined<{
@@ -132,7 +127,7 @@ export type ViraFormFields = Record<string, ViraFormField>;
132
127
  *
133
128
  * @category Internal
134
129
  */
135
- export declare function applyRequiredLabel(label: string | undefined, isRequired: boolean): string | undefined;
130
+ export declare function applyRequiredLabel(label: string | HtmlInterpolation | undefined, isRequired: boolean): string | HtmlInterpolation | undefined;
136
131
  /**
137
132
  * Checks if all the {@link ViraFormField} entries in a given {@link ViraFormFields} are valid through
138
133
  * the following checks:
@@ -1,5 +1,6 @@
1
1
  import { check } from '@augment-vir/assert';
2
2
  import { addSuffix, getObjectTypedValues } from '@augment-vir/common';
3
+ import { html } from 'element-vir';
3
4
  /**
4
5
  * Form field types for {@link ViraFormField}.
5
6
  *
@@ -27,19 +28,22 @@ export var ViraFormFieldType;
27
28
  * @category Internal
28
29
  */
29
30
  export function applyRequiredLabel(label, isRequired) {
30
- if (label) {
31
- if (isRequired) {
32
- return addSuffix({
33
- value: label,
34
- suffix: '*',
35
- });
36
- }
37
- else {
38
- return label;
39
- }
31
+ if (!label) {
32
+ return undefined;
33
+ }
34
+ else if (!isRequired) {
35
+ return label;
36
+ }
37
+ else if (check.isString(label)) {
38
+ return addSuffix({
39
+ value: label,
40
+ suffix: '*',
41
+ });
40
42
  }
41
43
  else {
42
- return undefined;
44
+ return html `
45
+ ${label}*
46
+ `;
43
47
  }
44
48
  }
45
49
  /**
@@ -72,7 +72,7 @@ export declare class ViraThemeClient {
72
72
  * The currently selected theme. If you use multiple clients to set the same theme, this might
73
73
  * get out of sync.
74
74
  */
75
- get currentTheme(): ViraThemeSelection;
75
+ getCurrentTheme(): ViraThemeSelection;
76
76
  /** Set the selected theme. */
77
77
  setSelectedTheme(selection: ViraThemeSelection): void;
78
78
  /** Cleanup internal state and listeners. */
@@ -47,7 +47,7 @@ export class ViraThemeClient {
47
47
  /** A callback to remove the global theme preference listener. */
48
48
  removeThemePreferenceListener = listenTo(globalThis.matchMedia(darkSchemeMediaQuery), 'change', (event) => {
49
49
  assert.instanceOf(event, MediaQueryListEvent);
50
- if (this.currentTheme === ViraThemeSelection.Auto) {
50
+ if (this.getCurrentTheme() === ViraThemeSelection.Auto) {
51
51
  void this.applyThemeCallback({
52
52
  useDarkTheme: event.matches,
53
53
  });
@@ -60,13 +60,13 @@ export class ViraThemeClient {
60
60
  this.localStorageClient = new LocalStorageClient(themeStorageShapes, {
61
61
  storeName: params.storeName || 'vira-theme',
62
62
  });
63
- this.applySelection(this.currentTheme);
63
+ this.applySelection(this.getCurrentTheme());
64
64
  }
65
65
  /**
66
66
  * The currently selected theme. If you use multiple clients to set the same theme, this might
67
67
  * get out of sync.
68
68
  */
69
- get currentTheme() {
69
+ getCurrentTheme() {
70
70
  return this.localStorageClient.get.selectedTheme()?.theme || ViraThemeSelection.Auto;
71
71
  }
72
72
  /** Set the selected theme. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vira",
3
- "version": "32.3.0",
3
+ "version": "33.0.0",
4
4
  "description": "A simple and highly versatile design system using element-vir.",
5
5
  "keywords": [
6
6
  "design",
@@ -46,6 +46,7 @@
46
46
  "date-vir": "^9.1.1",
47
47
  "device-navigation": "^5.0.0",
48
48
  "json-schema-to-ts": "^3.1.1",
49
+ "lit": "^3.3.3",
49
50
  "lit-css-vars": "^3.6.3",
50
51
  "object-shape-tester": "^6.15.0",
51
52
  "observavir": "^2.3.3",
@@ -1,45 +0,0 @@
1
- import { type PartialWithUndefined } from '@augment-vir/common';
2
- import { type NavController } from 'device-navigation';
3
- import { type PopUpManager, type ShowPopUpResult } from '../../util/pop-up-manager.js';
4
- import { type ViraMenuCornerStyle } from './vira-menu.element.js';
5
- import { type PopUpOffset, type PopUpTriggerPosition } from './vira-pop-up-trigger.element.js';
6
- /**
7
- * Test ids for {@link ViraMenuTrigger}.
8
- *
9
- * @category Internal
10
- */
11
- export declare const viraMenuTriggerTestIds: {
12
- menu: string;
13
- };
14
- /**
15
- * A more specific wrapper of `ViraPopUpTrigger` that always opens a menu.
16
- *
17
- * @category PopUp
18
- * @category Elements
19
- */
20
- export declare const ViraMenuTrigger: import("element-vir").DeclarativeElementDefinition<`vira-${string}`, PartialWithUndefined<{
21
- isDisabled: boolean;
22
- z_debug_forceOpenState: boolean;
23
- popUpOffset: PopUpOffset;
24
- keepOpenAfterInteraction: boolean;
25
- menuCornerStyle: ViraMenuCornerStyle;
26
- /**
27
- * If true, the focus outline is moved inside the element.
28
- *
29
- * @default false
30
- */
31
- useInsideFocus: boolean;
32
- /**
33
- * When `true`, the trigger will focus itself when the pop-up closes.
34
- *
35
- * @default false
36
- */
37
- focusOnClose: boolean;
38
- } & PopUpTriggerPosition>, {
39
- navController: undefined | NavController;
40
- popUpManager: undefined | PopUpManager;
41
- /** `undefined` means the pop up is not currently showing. */
42
- showPopUpResult: ShowPopUpResult | undefined;
43
- }, {
44
- openChange: import("element-vir").DefineElementEvent<ShowPopUpResult | undefined>;
45
- }, `vira-${string}-`, `vira-${string}-`, readonly ["vira-menu-trigger-trigger"], readonly []>;
@@ -1,36 +0,0 @@
1
- import { type PartialWithUndefined } from '@augment-vir/common';
2
- /**
3
- * Possible corner styles for {@link ViraMenu}.
4
- *
5
- * @category Internal
6
- */
7
- export declare enum ViraMenuCornerStyle {
8
- /** Rounding of corners depends on the open direction of the menu. */
9
- Directional = "directional",
10
- /** All of the menus corners should be rounded. */
11
- AllRounded = "all-rounded",
12
- /** None of the menus corners should be rounded. */
13
- AllSquare = "all-square"
14
- }
15
- /**
16
- * Menu pop-up directions available for {@link ViraMenu}.
17
- *
18
- * @category Internal
19
- */
20
- export declare enum ViraMenuPopUpDirection {
21
- Downwards = "downwards",
22
- Upwards = "upwards"
23
- }
24
- /**
25
- * A simple default style wrapper for pop-up menus. Consider using `renderMenuItemEntries` to help
26
- * rendering many menu items.
27
- *
28
- * @category PopUp
29
- * @category Elements
30
- */
31
- export declare const ViraMenu: import("element-vir").DeclarativeElementDefinition<"vira-menu", PartialWithUndefined<{
32
- /** @default PopUpMenuDirection.Downwards */
33
- direction: ViraMenuPopUpDirection;
34
- /** @default PopUpMenuCornerStyle.Directional */
35
- cornerStyle: ViraMenuCornerStyle;
36
- }>, {}, {}, "vira-menu-open-upwards" | "vira-menu-rounded" | "vira-menu-square", "vira-menu-", readonly [], readonly []>;