vira 25.14.0 → 26.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.
@@ -5,16 +5,15 @@ import { wrapDefineElement } from 'element-vir';
5
5
  * @category Internal
6
6
  */
7
7
  export const ViraTagNamePrefix = `vira-`;
8
- const { defineElement } = wrapDefineElement({
8
+ /**
9
+ * Define a vira element with custom requirements (like the `vira-` element tag prefix).
10
+ *
11
+ * @category Internal
12
+ */
13
+ export const defineViraElement = wrapDefineElement({
9
14
  assertInputs: (inputs) => {
10
15
  if (!inputs.tagName.startsWith(ViraTagNamePrefix)) {
11
16
  throw new Error(`Tag name should start with '${ViraTagNamePrefix}' but got '${inputs.tagName}'`);
12
17
  }
13
18
  },
14
19
  });
15
- /**
16
- * Define a vira element with custom requirements (like the `vira-` element tag prefix).
17
- *
18
- * @category Internal
19
- */
20
- export const defineViraElement = defineElement;
@@ -1,5 +1,6 @@
1
1
  import { type PartialWithUndefined } from '@augment-vir/common';
2
2
  import { type HTMLTemplateResult } from 'element-vir';
3
+ import { type ViraLinkRoute } from '../vira-link.element.js';
3
4
  /**
4
5
  * An individual menu item consumed partially by `ViraMenuItem` and used by `ViraMenu`.
5
6
  *
@@ -8,17 +9,18 @@ import { type HTMLTemplateResult } from 'element-vir';
8
9
  export type MenuItem = {
9
10
  /** Each `id` must be unique across all items in a single menu. */
10
11
  id: PropertyKey;
11
- /** The user-facing label for this menu item. */
12
- label: string;
13
- } & PartialWithUndefined<{
14
- disabled: boolean;
15
- /** Text assigned to the `title` HTML attribute that'll show on long hover. */
16
- titleText: string;
17
12
  /**
18
- * An optional custom template for this menu item. This will replace the menu item text and icon
13
+ * The user-facing label for this menu item.
14
+ *
15
+ * Optionally, this can be a custom HTML template. This will replace the menu item text and icon
19
16
  * content, but will still be styled correctly if used within `ViraMenu`. Feel free to use
20
17
  * `ViraMenuItem` as the template with a custom `<slot>` to keep the selected checkmark
21
18
  * functionality.
22
19
  */
23
- template: HTMLTemplateResult;
20
+ label: string | HTMLTemplateResult;
21
+ } & PartialWithUndefined<{
22
+ route: ViraLinkRoute;
23
+ disabled: boolean;
24
+ /** Text assigned to the `title` HTML attribute that'll show on long hover. */
25
+ titleText: string;
24
26
  }>;
@@ -77,7 +77,11 @@ export const ViraMenuTrigger = defineViraElement()({
77
77
  }
78
78
  dispatch(new events.itemActivate(updateSelectedItems(item, inputs.selected, inputs.isMultiSelect)));
79
79
  if (!inputs.isMultiSelect) {
80
- state.popUpManager?.removePopUp();
80
+ /**
81
+ * Defer pop up removal to prevent race conditions with element-internal
82
+ * click handlers.
83
+ */
84
+ globalThis.setTimeout(() => state.popUpManager?.removePopUp());
81
85
  }
82
86
  })}
83
87
  >
@@ -1,8 +1,10 @@
1
+ import { check } from '@augment-vir/assert';
1
2
  import { nav, navAttribute, NavController, NavValue } from 'device-navigation';
2
3
  import { classMap, css, html, ifDefined, testId } from 'element-vir';
3
4
  import { viraFormCssVars } from '../../styles/form-themes.js';
4
5
  import { noNativeFormStyles, viraDisabledStyles } from '../../styles/index.js';
5
6
  import { defineViraElement } from '../define-vira-element.js';
7
+ import { ViraLink } from '../vira-link.element.js';
6
8
  import { assertUniqueIdProps } from './pop-up-helpers.js';
7
9
  import { ViraMenuItem } from './vira-menu-item.element.js';
8
10
  /**
@@ -47,7 +49,8 @@ export const ViraMenu = defineViraElement()({
47
49
 
48
50
  .menu-item {
49
51
  ${noNativeFormStyles};
50
- background-color: white;
52
+ will-change: background-color;
53
+ background-color: inherit;
51
54
  outline: none;
52
55
  cursor: pointer;
53
56
  }
@@ -99,29 +102,50 @@ export const ViraMenu = defineViraElement()({
99
102
  assertUniqueIdProps(inputs.items);
100
103
  const itemTemplates = inputs.items.map((item) => {
101
104
  const selected = !!inputs.selected?.includes(item.id);
102
- const innerTemplate = item.template ||
103
- html `
104
- <${ViraMenuItem.assign({
105
+ const innerTemplate = check.isString(item.label)
106
+ ? html `
107
+ <${ViraMenuItem.assign({
105
108
  label: item.label,
106
109
  selected,
107
110
  hideCheckIcon: inputs.hideCheckIcons,
108
111
  })}></${ViraMenuItem}>
109
- `;
112
+ `
113
+ : item.label;
110
114
  const disabled = item.disabled || (!inputs.isMultiSelect && selected);
111
- return html `
112
- <button
113
- class="menu-item ${classMap({
114
- disabled: !!item.disabled,
115
- selected,
116
- })}"
117
- ${testId(viraMenuTestIds.item)}
118
- title=${ifDefined(item.titleText || undefined)}
119
- role="option"
120
- ${nav(state.internalNavController, { disabled })}
121
- >
122
- ${innerTemplate}
123
- </button>
124
- `;
115
+ if (item.route) {
116
+ return html `
117
+ <${ViraLink.assign({
118
+ route: item.route,
119
+ })}
120
+ class="menu-item ${classMap({
121
+ disabled: !!item.disabled,
122
+ selected,
123
+ })}"
124
+ ${testId(viraMenuTestIds.item)}
125
+ title=${ifDefined(item.titleText || undefined)}
126
+ role="option"
127
+ ${nav(state.internalNavController, { disabled })}
128
+ >
129
+ ${innerTemplate}
130
+ </${ViraLink}>
131
+ `;
132
+ }
133
+ else {
134
+ return html `
135
+ <button
136
+ class="menu-item ${classMap({
137
+ disabled: !!item.disabled,
138
+ selected,
139
+ })}"
140
+ ${testId(viraMenuTestIds.item)}
141
+ title=${ifDefined(item.titleText || undefined)}
142
+ role="option"
143
+ ${nav(state.internalNavController, { disabled })}
144
+ >
145
+ ${innerTemplate}
146
+ </button>
147
+ `;
148
+ }
125
149
  });
126
150
  return html `
127
151
  ${itemTemplates}
@@ -31,11 +31,6 @@ export declare const ViraDropdown: import("element-vir").DeclarativeElementDefin
31
31
  * multiple.
32
32
  */
33
33
  isMultiSelect: boolean;
34
- /**
35
- * Shows the selection quantity rather than a list of selections. Only used when
36
- * `isMultiSelect` is `true`.
37
- */
38
- showSelectionCount: boolean;
39
34
  icon: ViraIconSvg;
40
35
  selectionPrefix: string;
41
36
  isDisabled: boolean;
@@ -128,11 +128,9 @@ export const ViraDropdown = defineViraElement()({
128
128
  : nothing;
129
129
  const selectionDisplay = shouldUsePlaceholder
130
130
  ? inputs.placeholder || ''
131
- : inputs.isMultiSelect && inputs.showSelectionCount
131
+ : inputs.isMultiSelect && selectedOptions.length > 1
132
132
  ? `${selectedOptions.length} Selected`
133
- : inputs.isMultiSelect
134
- ? selectedOptions.map((item) => item.label).join(', ')
135
- : selectedOptions[0]?.label || '';
133
+ : selectedOptions[0]?.label || '';
136
134
  return html `
137
135
  <${ViraMenuTrigger.assign({
138
136
  items: inputs.options,
@@ -165,10 +163,7 @@ export const ViraDropdown = defineViraElement()({
165
163
  class="selection-display ${classMap({
166
164
  'using-placeholder': shouldUsePlaceholder,
167
165
  })}"
168
- title=${ifDefined(shouldUsePlaceholder ||
169
- (inputs.isMultiSelect && inputs.showSelectionCount)
170
- ? undefined
171
- : selectionDisplay)}
166
+ title=${ifDefined(shouldUsePlaceholder ? undefined : selectionDisplay)}
172
167
  >
173
168
  ${prefixTemplate} ${selectionDisplay}
174
169
  </span>
@@ -1,5 +1,16 @@
1
1
  import { type PartialWithUndefined } from '@augment-vir/common';
2
+ import { type AttributeValues, type CSSResult } from 'element-vir';
2
3
  import { type SpaRoute, type SpaRouter } from 'spa-router-vir';
4
+ /**
5
+ * The route properties required for using {@link ViraLink} with a route.
6
+ *
7
+ * @category Internal
8
+ */
9
+ export type ViraLinkRoute = Readonly<{
10
+ route: SpaRoute<any, any, any>;
11
+ router: Pick<SpaRouter<any, any, any>, 'createRouteUrl' | 'setRouteOnDirectNavigation'>;
12
+ scrollToTop?: boolean;
13
+ }>;
3
14
  /**
4
15
  * A hyperlink wrapper element that can be configured to emit route change events rather than just
5
16
  * being a raw link.
@@ -21,17 +32,14 @@ export declare const ViraLink: import("element-vir").DeclarativeElementDefinitio
21
32
  * A route that'll change that current page without navigating the window. If this property
22
33
  * is provided for the inputs, don't provide a link property.
23
34
  */
24
- route: {
25
- route: SpaRoute<any, any, any>;
26
- router: Pick<SpaRouter<any, any, any>, "createRouteUrl" | "setRouteOnDirectNavigation">;
27
- scrollToTop?: boolean;
28
- };
35
+ route: ViraLinkRoute;
29
36
  }, "link" | "route"> & PartialWithUndefined<{
30
- aria?: {
31
- /**
32
- * This label will be attached to the inner `<a>` element's `aria-label` attribute.
33
- * If none is provided, no `aria-label` attribute will be generated.
34
- */
35
- label: string;
36
- };
37
+ /** Styles that will be applied directly to the inner elements. */
38
+ stylePassthrough: Readonly<PartialWithUndefined<{
39
+ a: CSSResult;
40
+ }>>;
41
+ /** Attributes that will be applied directly to the inner elements. */
42
+ attributePassthrough: Readonly<PartialWithUndefined<{
43
+ a: AttributeValues;
44
+ }>>;
37
45
  }>, {}, {}, "vira-link-", "vira-link-hover-color", readonly []>;
@@ -1,4 +1,4 @@
1
- import { css, html, ifDefined, listen } from 'element-vir';
1
+ import { attributes, css, html, ifDefined, listen, nothing, } from 'element-vir';
2
2
  import { defineViraElement } from './define-vira-element.js';
3
3
  /**
4
4
  * A hyperlink wrapper element that can be configured to emit route change events rather than just
@@ -57,7 +57,10 @@ export const ViraLink = defineViraElement()({
57
57
  href=${inputs.link.url}
58
58
  target="_blank"
59
59
  rel="noopener noreferrer"
60
- aria-label=${ifDefined(inputs.aria?.label || undefined)}
60
+ ${inputs.attributePassthrough?.a
61
+ ? attributes(inputs.attributePassthrough.a)
62
+ : nothing}
63
+ style=${ifDefined(inputs.stylePassthrough?.a)}
61
64
  >
62
65
  <slot></slot>
63
66
  </a>
@@ -72,7 +75,10 @@ export const ViraLink = defineViraElement()({
72
75
  <a
73
76
  href=${linkUrl}
74
77
  rel="noopener noreferrer"
75
- aria-label=${ifDefined(inputs.aria?.label || undefined)}
78
+ ${inputs.attributePassthrough?.a
79
+ ? attributes(inputs.attributePassthrough.a)
80
+ : nothing}
81
+ style=${ifDefined(inputs.stylePassthrough?.a)}
76
82
  ${listen('click', clickCallback)}
77
83
  >
78
84
  <slot></slot>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vira",
3
- "version": "25.14.0",
3
+ "version": "26.0.0",
4
4
  "description": "A simple and highly versatile design system using element-vir.",
5
5
  "keywords": [
6
6
  "design",
@@ -67,7 +67,7 @@
67
67
  "vite-tsconfig-paths": "^5.1.4"
68
68
  },
69
69
  "peerDependencies": {
70
- "element-vir": "^25.14.0"
70
+ "element-vir": "^26.0.0"
71
71
  },
72
72
  "engines": {
73
73
  "node": ">=22"