vira 31.15.2 → 31.16.1

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.
@@ -1,6 +1,7 @@
1
1
  import { type PartialWithUndefined } from '@augment-vir/common';
2
2
  import { type ViraIconSvg } from '../icons/index.js';
3
3
  import { ViraColorVariant, ViraEmphasis, ViraSize } from '../styles/index.js';
4
+ import { ViraThemeColorName } from '../styles/vira-color-theme-object.js';
4
5
  /**
5
6
  * A custom button with default styling.
6
7
  *
@@ -27,16 +28,17 @@ export declare const ViraButton: import("element-vir").DeclarativeElementDefinit
27
28
  */
28
29
  buttonSize: ViraSize;
29
30
  /**
30
- * Set a predefined color variant. Set to `ViraColorVariant.None` for maximum customization.
31
+ * Set a predefined color variant or a raw {@link ViraThemeColorName} (e.g.,
32
+ * `ViraThemeColorName.blue`). Set to `ViraColorVariant.Custom` for maximum customization.
31
33
  * In that case, you will need to use this element's CSS vars to customize the colors.
32
34
  *
33
- * @default ViraColorVariant.Info,
35
+ * @default ViraColorVariant.Plain
34
36
  */
35
- colorVariant: ViraColorVariant;
37
+ color: ViraColorVariant | ViraThemeColorName;
36
38
  /**
37
39
  * Set to `true`
38
40
  *
39
41
  * @default false
40
42
  */
41
43
  showMenuCaret: boolean;
42
- }>, {}, {}, "vira-button-with-menu-caret" | "vira-button-size-large" | "vira-button-size-medium" | "vira-button-size-small" | "vira-button-emphasis-standard" | "vira-button-emphasis-subtle" | "vira-button-color-info" | "vira-button-color-plain" | "vira-button-color-neutral" | "vira-button-color-danger" | "vira-button-color-warning" | "vira-button-color-positive" | "vira-button-color-brand" | "vira-button-color-special" | "vira-button-disabled" | "vira-button-icon-only", "vira-button-text-color" | "vira-button-background-color" | "vira-button-border-color" | "vira-button-hover-text-color" | "vira-button-hover-background-color" | "vira-button-hover-border-color" | "vira-button-active-text-color" | "vira-button-active-background-color" | "vira-button-active-border-color" | "vira-button-disabled-text-color" | "vira-button-disabled-background-color" | "vira-button-disabled-border-color" | "vira-button-border-width" | "vira-button-border-radius", readonly [], readonly []>;
44
+ }>, {}, {}, "vira-button-with-menu-caret" | "vira-button-size-large" | "vira-button-size-medium" | "vira-button-size-small" | "vira-button-emphasis-standard" | "vira-button-emphasis-subtle" | "vira-button-color-red" | "vira-button-color-yellow" | "vira-button-color-green" | "vira-button-color-blue" | "vira-button-color-brand" | "vira-button-color-purple" | "vira-button-color-plain" | "vira-button-color-neutral" | "vira-button-color-teal" | "vira-button-color-pink" | "vira-button-color-grey" | "vira-button-disabled" | "vira-button-icon-only", "vira-button-text-color" | "vira-button-background-color" | "vira-button-border-color" | "vira-button-hover-text-color" | "vira-button-hover-background-color" | "vira-button-hover-border-color" | "vira-button-active-text-color" | "vira-button-active-background-color" | "vira-button-active-border-color" | "vira-button-disabled-text-color" | "vira-button-disabled-background-color" | "vira-button-disabled-border-color" | "vira-button-border-width" | "vira-button-border-radius", readonly [], readonly []>;
@@ -1,21 +1,20 @@
1
- import { arrayToObject, mapEnumToObject } from '@augment-vir/common';
1
+ import { arrayToObject, getObjectTypedKeys } from '@augment-vir/common';
2
2
  import { ContrastLevelName } from '@electrovir/color/dist/data/contrast/contrast.js';
3
3
  import { css, html, nothing, unsafeCSS } from 'element-vir';
4
4
  import { themeDefaultKey } from 'theme-vir/dist/color-theme/color-theme.js';
5
5
  import { ChevronDown16Icon } from '../icons/index.js';
6
6
  import { createFocusStyles } from '../styles/focus.js';
7
7
  import { viraFormCssVars } from '../styles/form-styles.js';
8
- import { viraColorVariants, viraColorVariantToColorName, viraEmphasisVariants, viraSizeHeights, viraSizeVariants, } from '../styles/form-variants.js';
8
+ import { standaloneThemeColorNames, viraColorVariantToHostClassKey, viraEmphasisVariants, viraSizeHeights, viraSizeVariants, } from '../styles/form-variants.js';
9
9
  import { noUserSelect, ViraColorVariant, ViraEmphasis, ViraSize, viraTheme, } from '../styles/index.js';
10
10
  import { noNativeFormStyles } from '../styles/native-styles.js';
11
- import { viraThemeByKeys } from '../styles/vira-color-theme-object.js';
11
+ import { viraThemeByKeys, ViraThemeColorName } from '../styles/vira-color-theme-object.js';
12
12
  import { defineViraElement } from '../util/define-vira-element.js';
13
13
  import { ViraIcon } from './vira-icon.element.js';
14
14
  const transparentColor = {
15
15
  value: css `transparent`,
16
16
  };
17
- function buildThemedButtonColors(colorVariant) {
18
- const colorName = viraColorVariantToColorName[colorVariant];
17
+ function buildThemedButtonColors(colorName) {
19
18
  const behindBg = viraThemeByKeys[colorName]['behind-bg'];
20
19
  const foreground = viraThemeByKeys[colorName].foreground;
21
20
  const onSelf = viraThemeByKeys[colorName]['on-self'];
@@ -56,66 +55,61 @@ function buildThemedButtonColors(colorVariant) {
56
55
  },
57
56
  };
58
57
  }
59
- const colorVariantColors = {
60
- ...mapEnumToObject(ViraColorVariant, (colorVariant) => {
61
- return buildThemedButtonColors(colorVariant);
62
- }),
63
- [ViraColorVariant.Plain]: {
64
- [ViraEmphasis.Standard]: {
65
- idle: {
66
- backgroundColor: viraTheme.inverse[themeDefaultKey].background,
67
- textColor: viraTheme.inverse[themeDefaultKey].foreground,
68
- borderColor: viraTheme.inverse[themeDefaultKey].background,
69
- },
70
- hover: {
71
- backgroundColor: viraTheme.colors['vira-grey-behind-bg-non-body'].background,
72
- textColor: viraTheme.colors['vira-grey-behind-bg-non-body'].foreground,
73
- borderColor: viraTheme.inverse[themeDefaultKey].background,
74
- },
75
- active: {
76
- backgroundColor: viraTheme.colors['vira-grey-behind-bg-body'].background,
77
- textColor: viraTheme.colors['vira-grey-behind-bg-body'].foreground,
78
- borderColor: viraTheme.inverse[themeDefaultKey].background,
79
- },
58
+ const plainButtonColors = {
59
+ [ViraEmphasis.Standard]: {
60
+ idle: {
61
+ backgroundColor: viraTheme.inverse[themeDefaultKey].background,
62
+ textColor: viraTheme.inverse[themeDefaultKey].foreground,
63
+ borderColor: viraTheme.inverse[themeDefaultKey].background,
80
64
  },
81
- [ViraEmphasis.Subtle]: {
82
- idle: {
83
- backgroundColor: transparentColor,
84
- textColor: viraTheme.colors[themeDefaultKey].foreground,
85
- borderColor: transparentColor,
86
- },
87
- hover: {
88
- backgroundColor: viraTheme.colors['vira-grey-on-self-body'].background,
89
- textColor: viraTheme.colors['vira-grey-on-self-body'].foreground,
90
- borderColor: viraTheme.colors['vira-grey-on-self-body'].foreground,
91
- },
92
- active: {
93
- backgroundColor: viraTheme.colors['vira-grey-on-self-non-body'].background,
94
- textColor: viraTheme.colors['vira-grey-on-self-non-body'].foreground,
95
- borderColor: viraTheme.colors['vira-grey-on-self-non-body'].foreground,
96
- },
65
+ hover: {
66
+ backgroundColor: viraTheme.colors['vira-grey-behind-bg-non-body'].background,
67
+ textColor: viraTheme.colors['vira-grey-behind-bg-non-body'].foreground,
68
+ borderColor: viraTheme.inverse[themeDefaultKey].background,
69
+ },
70
+ active: {
71
+ backgroundColor: viraTheme.colors['vira-grey-behind-bg-body'].background,
72
+ textColor: viraTheme.colors['vira-grey-behind-bg-body'].foreground,
73
+ borderColor: viraTheme.inverse[themeDefaultKey].background,
97
74
  },
98
75
  },
99
- [ViraColorVariant.Neutral]: {
100
- [ViraEmphasis.Standard]: {
101
- idle: {
102
- backgroundColor: viraTheme.colors[themeDefaultKey].background,
103
- textColor: viraTheme.colors[themeDefaultKey].foreground,
104
- borderColor: viraFormCssVars['vira-form-border-color'],
105
- },
106
- hover: {
107
- backgroundColor: viraTheme.colors['vira-grey-behind-fg-small-body'].background,
108
- textColor: viraTheme.colors['vira-grey-behind-fg-small-body'].foreground,
109
- borderColor: viraFormCssVars['vira-form-border-color'],
110
- },
111
- active: {
112
- backgroundColor: viraTheme.colors['vira-grey-behind-fg-body'].background,
113
- textColor: viraTheme.colors['vira-grey-behind-fg-body'].foreground,
114
- borderColor: viraFormCssVars['vira-form-border-color'],
115
- },
76
+ [ViraEmphasis.Subtle]: {
77
+ idle: {
78
+ backgroundColor: transparentColor,
79
+ textColor: viraTheme.colors[themeDefaultKey].foreground,
80
+ borderColor: transparentColor,
81
+ },
82
+ hover: {
83
+ backgroundColor: viraTheme.colors['vira-grey-on-self-body'].background,
84
+ textColor: viraTheme.colors['vira-grey-on-self-body'].foreground,
85
+ borderColor: viraTheme.colors['vira-grey-on-self-body'].foreground,
86
+ },
87
+ active: {
88
+ backgroundColor: viraTheme.colors['vira-grey-on-self-non-body'].background,
89
+ textColor: viraTheme.colors['vira-grey-on-self-non-body'].foreground,
90
+ borderColor: viraTheme.colors['vira-grey-on-self-non-body'].foreground,
91
+ },
92
+ },
93
+ };
94
+ const neutralButtonColors = {
95
+ [ViraEmphasis.Standard]: {
96
+ idle: {
97
+ backgroundColor: viraTheme.colors[themeDefaultKey].background,
98
+ textColor: viraTheme.colors[themeDefaultKey].foreground,
99
+ borderColor: viraFormCssVars['vira-form-border-color'],
100
+ },
101
+ hover: {
102
+ backgroundColor: viraTheme.colors['vira-grey-behind-fg-small-body'].background,
103
+ textColor: viraTheme.colors['vira-grey-behind-fg-small-body'].foreground,
104
+ borderColor: viraFormCssVars['vira-form-border-color'],
105
+ },
106
+ active: {
107
+ backgroundColor: viraTheme.colors['vira-grey-behind-fg-body'].background,
108
+ textColor: viraTheme.colors['vira-grey-behind-fg-body'].foreground,
109
+ borderColor: viraFormCssVars['vira-form-border-color'],
116
110
  },
117
- [ViraEmphasis.Subtle]: buildThemedButtonColors(ViraColorVariant.Neutral)[ViraEmphasis.Subtle],
118
111
  },
112
+ [ViraEmphasis.Subtle]: buildThemedButtonColors(ViraThemeColorName.grey)[ViraEmphasis.Subtle],
119
113
  };
120
114
  /**
121
115
  * A custom button with default styling.
@@ -133,13 +127,24 @@ export const ViraButton = defineViraElement()({
133
127
  'vira-button-size-small': ({ inputs }) => inputs.buttonSize === ViraSize.Small,
134
128
  'vira-button-emphasis-standard': ({ inputs }) => !inputs.buttonEmphasis || inputs.buttonEmphasis === ViraEmphasis.Standard,
135
129
  'vira-button-emphasis-subtle': ({ inputs }) => inputs.buttonEmphasis === ViraEmphasis.Subtle,
136
- ...arrayToObject(viraColorVariants, (colorVariant) => {
130
+ ...arrayToObject(getObjectTypedKeys(viraColorVariantToHostClassKey), (colorVariant) => {
131
+ const colorKey = viraColorVariantToHostClassKey[colorVariant];
137
132
  return {
138
- key: `vira-button-color-${colorVariant}`,
133
+ key: `vira-button-color-${colorKey}`,
139
134
  value: ({ inputs, }) => {
140
- return colorVariant === ViraColorVariant.Plain
141
- ? !inputs.colorVariant || inputs.colorVariant === colorVariant
142
- : inputs.colorVariant === colorVariant;
135
+ return inputs.color === colorVariant || inputs.color === colorKey;
136
+ },
137
+ };
138
+ }, {
139
+ useRequired: true,
140
+ }),
141
+ 'vira-button-color-plain': ({ inputs, }) => !inputs.color || inputs.color === ViraColorVariant.Plain,
142
+ 'vira-button-color-neutral': ({ inputs, }) => inputs.color === ViraColorVariant.Neutral,
143
+ ...arrayToObject(standaloneThemeColorNames, (colorName) => {
144
+ return {
145
+ key: `vira-button-color-${colorName}`,
146
+ value: ({ inputs, }) => {
147
+ return inputs.color === colorName;
143
148
  },
144
149
  };
145
150
  }, {
@@ -165,37 +170,52 @@ export const ViraButton = defineViraElement()({
165
170
  'vira-button-border-radius': viraFormCssVars['vira-form-radius'].value,
166
171
  },
167
172
  styles: ({ hostClasses, cssVars }) => {
168
- function generateVariantCss() {
169
- const allStyles = viraEmphasisVariants.flatMap((emphasis) => {
170
- return viraColorVariants.map((colorVariant) => {
171
- const colors = colorVariantColors[colorVariant][emphasis];
172
- const variantSelector = hostClasses[`vira-button-color-${colorVariant}`].selector;
173
- const emphasisSelector = hostClasses[`vira-button-emphasis-${emphasis}`].selector;
174
- return css `
175
- ${variantSelector}${emphasisSelector} {
176
- ${cssVars['vira-button-background-color'].name}: ${colors.idle
177
- .backgroundColor.value};
178
- ${cssVars['vira-button-text-color'].name}: ${colors.idle.textColor
179
- .value};
180
- ${cssVars['vira-button-border-color'].name}: ${colors.idle.borderColor
181
- .value};
173
+ function buildVariantCssRule(variantSelector, emphasisSelector, colors) {
174
+ return css `
175
+ ${variantSelector}${emphasisSelector} {
176
+ ${cssVars['vira-button-background-color'].name}: ${colors.idle.backgroundColor
177
+ .value};
178
+ ${cssVars['vira-button-text-color'].name}: ${colors.idle.textColor.value};
179
+ ${cssVars['vira-button-border-color'].name}: ${colors.idle.borderColor.value};
182
180
 
183
- ${cssVars['vira-button-hover-background-color'].name}: ${colors.hover
184
- .backgroundColor.value};
185
- ${cssVars['vira-button-hover-text-color'].name}: ${colors.hover
186
- .textColor.value};
187
- ${cssVars['vira-button-hover-border-color'].name}: ${colors.hover
188
- .borderColor.value};
181
+ ${cssVars['vira-button-hover-background-color'].name}: ${colors.hover
182
+ .backgroundColor.value};
183
+ ${cssVars['vira-button-hover-text-color'].name}: ${colors.hover.textColor
184
+ .value};
185
+ ${cssVars['vira-button-hover-border-color'].name}: ${colors.hover.borderColor
186
+ .value};
189
187
 
190
- ${cssVars['vira-button-active-background-color'].name}: ${colors.active
191
- .backgroundColor.value};
192
- ${cssVars['vira-button-active-text-color'].name}: ${colors.active
193
- .textColor.value};
194
- ${cssVars['vira-button-active-border-color'].name}: ${colors.active
195
- .borderColor.value};
196
- }
197
- `;
188
+ ${cssVars['vira-button-active-background-color'].name}: ${colors.active
189
+ .backgroundColor.value};
190
+ ${cssVars['vira-button-active-text-color'].name}: ${colors.active.textColor
191
+ .value};
192
+ ${cssVars['vira-button-active-border-color'].name}: ${colors.active.borderColor
193
+ .value};
194
+ }
195
+ `;
196
+ }
197
+ function generateVariantCss() {
198
+ const allStyles = viraEmphasisVariants.flatMap((emphasis) => {
199
+ const emphasisSelector = hostClasses[`vira-button-emphasis-${emphasis}`].selector;
200
+ const themedStyles = getObjectTypedKeys(viraColorVariantToHostClassKey).map((colorVariant) => {
201
+ const colorKey = viraColorVariantToHostClassKey[colorVariant];
202
+ const colors = buildThemedButtonColors(colorKey)[emphasis];
203
+ const variantSelector = hostClasses[`vira-button-color-${colorKey}`].selector;
204
+ return buildVariantCssRule(variantSelector, emphasisSelector, colors);
205
+ });
206
+ const plainStyle = buildVariantCssRule(hostClasses['vira-button-color-plain'].selector, emphasisSelector, plainButtonColors[emphasis]);
207
+ const neutralStyle = buildVariantCssRule(hostClasses['vira-button-color-neutral'].selector, emphasisSelector, neutralButtonColors[emphasis]);
208
+ const standaloneStyles = standaloneThemeColorNames.map((colorName) => {
209
+ const colors = buildThemedButtonColors(colorName)[emphasis];
210
+ const variantSelector = hostClasses[`vira-button-color-${colorName}`].selector;
211
+ return buildVariantCssRule(variantSelector, emphasisSelector, colors);
198
212
  });
213
+ return [
214
+ ...themedStyles,
215
+ plainStyle,
216
+ neutralStyle,
217
+ ...standaloneStyles,
218
+ ];
199
219
  });
200
220
  return unsafeCSS(allStyles.join('\n'));
201
221
  }
@@ -2,7 +2,7 @@ import { type PartialWithUndefined } from '@augment-vir/common';
2
2
  import { type FullSpaRoute, type GenericTreePaths, type SpaRouter } from 'spa-router-vir';
3
3
  import { type ViraIconSvg } from '../icons/icon-svg.js';
4
4
  import { ViraColorVariant } from '../styles/form-variants.js';
5
- import { type HorizontalAnchor, type PopUpOffset } from './pop-up/vira-pop-up-trigger.element.js';
5
+ import { ViraThemeColorName } from '../styles/vira-color-theme-object.js';
6
6
  /**
7
7
  * Controls which edge of the tab the selection indicator bar appears on.
8
8
  *
@@ -45,7 +45,7 @@ export type ViraTab = {
45
45
  */
46
46
  export declare const ViraTabs: import("element-vir").DeclarativeElementDefinition<"vira-tabs", {
47
47
  tabs: ReadonlyArray<Readonly<ViraTab>>;
48
- router: Pick<SpaRouter<any, any, any>, "createRouteUrl" | "setRouteOnDirectNavigation">;
48
+ router: Pick<SpaRouter<any, any, any>, "createRouteUrl" | "setRouteOnDirectNavigation" | "setRoute">;
49
49
  currentRoute: Readonly<FullSpaRoute>;
50
50
  } & PartialWithUndefined<{
51
51
  /**
@@ -55,34 +55,32 @@ export declare const ViraTabs: import("element-vir").DeclarativeElementDefinitio
55
55
  */
56
56
  barDirection: ViraTabsBarDirection;
57
57
  /**
58
- * Color variant for the tab selection indicator and active tab text.
58
+ * Color variant for the tab selection indicator and active tab text. Accepts any
59
+ * {@link ViraColorVariant} or a {@link ViraThemeColorName} (e.g.,
60
+ * `ViraThemeColorName.blue`).
59
61
  *
60
- * @default ViraColorVariant.Info
62
+ * @default ViraColorVariant.Plain
61
63
  */
62
- colorVariant: ViraColorVariant;
64
+ color: ViraColorVariant | ViraThemeColorName;
63
65
  /**
64
66
  * Layout direction for icons relative to their label text.
65
67
  *
66
68
  * @default ViraTabsIconLayout.Vertical
67
69
  */
68
70
  iconLayout: ViraTabsIconLayout;
69
- /**
70
- * Horizontal anchor for the dropdown menu. Only used when tabs overflow into a dropdown.
71
- *
72
- * @default HorizontalAnchor.Left
73
- */
74
- menuHorizontalAnchor: HorizontalAnchor;
75
- /** Whether the dropdown trigger is disabled. Only used when tabs overflow into a dropdown. */
76
- menuIsDisabled: boolean;
77
- /** Offset for the dropdown pop-up. Only used when tabs overflow into a dropdown. */
78
- menuPopUpOffset: Readonly<PopUpOffset>;
79
71
  /** When true, tabs and their container expand to fill all available horizontal space. */
80
72
  shouldFillWidth: boolean;
81
73
  }>, {
82
74
  isOverflowing: boolean;
83
75
  /** A callback to remove all internal observers. */
84
76
  cleanupObserver: undefined | (() => void);
77
+ /**
78
+ * Captured at the moment the overflow select is opened (mousedown or keydown on the
79
+ * select). Once the native picker opens, the OS owns keyboard input, so modifier state
80
+ * must be captured before that.
81
+ */
82
+ modifierKeyHeld: boolean;
85
83
  }, {
86
84
  /** Fires when a tab is clicked with the corresponding tab entry. */
87
85
  tabSelect: import("element-vir").DefineEvent<Readonly<ViraTab>>;
88
- }, "vira-tabs-bar-top" | "vira-tabs-bar-bottom" | "vira-tabs-bar-left" | "vira-tabs-bar-right" | "vira-tabs-color-info" | "vira-tabs-color-plain" | "vira-tabs-color-neutral" | "vira-tabs-color-danger" | "vira-tabs-color-warning" | "vira-tabs-color-positive" | "vira-tabs-color-brand" | "vira-tabs-color-special" | "vira-tabs-icon-layout-vertical" | "vira-tabs-icon-layout-horizontal" | "vira-tabs-overflowing" | "vira-tabs-fill-width", "vira-tabs-active-color" | "vira-tabs-active-hover-color" | "vira-tabs-inactive-color" | "vira-tabs-inactive-hover-color" | "vira-tabs-bar-thickness", readonly [], readonly []>;
86
+ }, "vira-tabs-bar-top" | "vira-tabs-bar-bottom" | "vira-tabs-bar-left" | "vira-tabs-bar-right" | "vira-tabs-color-red" | "vira-tabs-color-yellow" | "vira-tabs-color-green" | "vira-tabs-color-blue" | "vira-tabs-color-brand" | "vira-tabs-color-purple" | "vira-tabs-color-plain" | "vira-tabs-color-neutral" | "vira-tabs-color-teal" | "vira-tabs-color-pink" | "vira-tabs-color-grey" | "vira-tabs-icon-layout-vertical" | "vira-tabs-icon-layout-horizontal" | "vira-tabs-overflowing" | "vira-tabs-fill-width", "vira-tabs-active-color" | "vira-tabs-active-hover-color" | "vira-tabs-inactive-color" | "vira-tabs-inactive-hover-color" | "vira-tabs-bar-thickness", readonly [], readonly []>;
@@ -1,21 +1,18 @@
1
1
  import { check } from '@augment-vir/assert';
2
- import { arrayToObject, filterMap } from '@augment-vir/common';
2
+ import { arrayToObject, filterMap, getObjectTypedKeys, } from '@augment-vir/common';
3
3
  import { ContrastLevelName } from '@electrovir/color/dist/data/contrast/contrast.js';
4
4
  import { classMap, css, defineElementEvent, html, listen, nothing, onDomCreated, unsafeCSS, } from 'element-vir';
5
5
  import { routeHasPaths, } from 'spa-router-vir';
6
6
  import { createFocusStyles } from '../styles/focus.js';
7
7
  import { viraFormCssVars } from '../styles/form-styles.js';
8
- import { ViraColorVariant, viraColorVariantToColorName, viraColorVariants, } from '../styles/form-variants.js';
8
+ import { standaloneThemeColorNames, ViraColorVariant, viraColorVariantToHostClassKey, } from '../styles/form-variants.js';
9
9
  import { noNativeFormStyles, noUserSelect, viraDisabledStyles, viraTheme } from '../styles/index.js';
10
- import { viraThemeByKeys } from '../styles/vira-color-theme-object.js';
10
+ import { viraThemeByKeys, ViraThemeColorName } from '../styles/vira-color-theme-object.js';
11
11
  import { defineViraElement } from '../util/define-vira-element.js';
12
12
  import { createOverflowObserver } from '../util/overflow-observer.js';
13
- import { renderMenuItemEntries } from '../util/pop-up-helpers.js';
14
- import { ViraMenuTrigger } from './pop-up/vira-menu-trigger.element.js';
15
- import { ViraMenuCornerStyle } from './pop-up/vira-menu.element.js';
16
- import { ViraButton } from './vira-button.element.js';
17
13
  import { ViraIcon } from './vira-icon.element.js';
18
14
  import { ViraLink } from './vira-link.element.js';
15
+ import { ViraSelect } from './vira-select.element.js';
19
16
  /**
20
17
  * Controls which edge of the tab the selection indicator bar appears on.
21
18
  *
@@ -56,6 +53,12 @@ export const ViraTabs = defineViraElement()({
56
53
  isOverflowing: false,
57
54
  /** A callback to remove all internal observers. */
58
55
  cleanupObserver: undefined,
56
+ /**
57
+ * Captured at the moment the overflow select is opened (mousedown or keydown on the
58
+ * select). Once the native picker opens, the OS owns keyboard input, so modifier state
59
+ * must be captured before that.
60
+ */
61
+ modifierKeyHeld: false,
59
62
  };
60
63
  },
61
64
  hostClasses: {
@@ -63,13 +66,24 @@ export const ViraTabs = defineViraElement()({
63
66
  'vira-tabs-bar-bottom': ({ inputs }) => !inputs.barDirection || inputs.barDirection === ViraTabsBarDirection.Bottom,
64
67
  'vira-tabs-bar-left': ({ inputs }) => inputs.barDirection === ViraTabsBarDirection.Left,
65
68
  'vira-tabs-bar-right': ({ inputs }) => inputs.barDirection === ViraTabsBarDirection.Right,
66
- ...arrayToObject(viraColorVariants, (colorVariant) => {
69
+ ...arrayToObject(getObjectTypedKeys(viraColorVariantToHostClassKey), (colorVariant) => {
70
+ const colorKey = viraColorVariantToHostClassKey[colorVariant];
67
71
  return {
68
- key: `vira-tabs-color-${colorVariant}`,
72
+ key: `vira-tabs-color-${colorKey}`,
69
73
  value: ({ inputs, }) => {
70
- return colorVariant === ViraColorVariant.Plain
71
- ? !inputs.colorVariant || inputs.colorVariant === colorVariant
72
- : inputs.colorVariant === colorVariant;
74
+ return inputs.color === colorVariant || inputs.color === colorKey;
75
+ },
76
+ };
77
+ }, {
78
+ useRequired: true,
79
+ }),
80
+ 'vira-tabs-color-plain': ({ inputs, }) => !inputs.color || inputs.color === ViraColorVariant.Plain,
81
+ 'vira-tabs-color-neutral': ({ inputs, }) => inputs.color === ViraColorVariant.Neutral,
82
+ ...arrayToObject(standaloneThemeColorNames, (colorName) => {
83
+ return {
84
+ key: `vira-tabs-color-${colorName}`,
85
+ value: ({ inputs, }) => {
86
+ return inputs.color === colorName;
73
87
  },
74
88
  };
75
89
  }, {
@@ -81,40 +95,57 @@ export const ViraTabs = defineViraElement()({
81
95
  'vira-tabs-fill-width': ({ inputs }) => !!inputs.shouldFillWidth,
82
96
  },
83
97
  cssVars: {
84
- 'vira-tabs-active-color': viraThemeByKeys[viraColorVariantToColorName[ViraColorVariant.Info]]['behind-bg'][ContrastLevelName.NonBodyText].background.value,
85
- 'vira-tabs-active-hover-color': viraThemeByKeys[viraColorVariantToColorName[ViraColorVariant.Info]]['behind-bg'][ContrastLevelName.Header].background.value,
98
+ 'vira-tabs-active-color': viraThemeByKeys[viraColorVariantToHostClassKey[ViraColorVariant.Info]]['behind-bg'][ContrastLevelName.NonBodyText].background.value,
99
+ 'vira-tabs-active-hover-color': viraThemeByKeys[viraColorVariantToHostClassKey[ViraColorVariant.Info]]['behind-bg'][ContrastLevelName.Header].background.value,
86
100
  'vira-tabs-inactive-color': viraTheme.colors['vira-grey-foreground-header'].foreground.value,
87
101
  'vira-tabs-inactive-hover-color': viraTheme.colors['vira-grey-foreground-non-body'].foreground.value,
88
102
  'vira-tabs-bar-thickness': '3px',
89
103
  },
90
104
  styles: ({ hostClasses, cssVars }) => {
105
+ function buildThemedTabsColors(colorName) {
106
+ return {
107
+ active: viraThemeByKeys[colorName]['behind-bg'][ContrastLevelName.NonBodyText]
108
+ .background,
109
+ hover: viraThemeByKeys[colorName]['behind-bg'][ContrastLevelName.Header].background,
110
+ };
111
+ }
112
+ function buildVariantCssRule(variantSelector, colors) {
113
+ return css `
114
+ ${variantSelector} {
115
+ ${cssVars['vira-tabs-active-color'].name}: ${colors.active.value};
116
+ ${cssVars['vira-tabs-active-hover-color'].name}: ${colors.hover.value};
117
+ }
118
+ `;
119
+ }
91
120
  function generateVariantCss() {
92
121
  const plainColors = {
93
122
  active: viraTheme.colors['vira-grey-foreground-small-body'].foreground,
94
123
  hover: viraTheme.colors['vira-grey-foreground-body'].foreground,
95
124
  };
96
- const allStyles = viraColorVariants
97
- .map((colorVariant) => {
98
- const variantSelector = hostClasses[`vira-tabs-color-${colorVariant}`].selector;
99
- const colors = colorVariant === ViraColorVariant.Plain
100
- ? plainColors
101
- : {
102
- active: viraThemeByKeys[viraColorVariantToColorName[colorVariant]]['behind-bg'][ContrastLevelName.NonBodyText].background,
103
- hover: viraThemeByKeys[viraColorVariantToColorName[colorVariant]]['behind-bg'][ContrastLevelName.Header].background,
104
- };
105
- return css `
106
- ${variantSelector} {
107
- ${cssVars['vira-tabs-active-color'].name}: ${colors.active.value};
108
- ${cssVars['vira-tabs-active-hover-color'].name}: ${colors.hover.value};
109
- }
110
- `;
111
- })
112
- .join('\n');
113
- return unsafeCSS(allStyles);
125
+ const themedStyles = getObjectTypedKeys(viraColorVariantToHostClassKey).map((colorVariant) => {
126
+ const colorKey = viraColorVariantToHostClassKey[colorVariant];
127
+ const variantSelector = hostClasses[`vira-tabs-color-${colorKey}`].selector;
128
+ const colors = buildThemedTabsColors(colorKey);
129
+ return buildVariantCssRule(variantSelector, colors);
130
+ });
131
+ const plainStyle = buildVariantCssRule(hostClasses['vira-tabs-color-plain'].selector, plainColors);
132
+ const neutralStyle = buildVariantCssRule(hostClasses['vira-tabs-color-neutral'].selector, buildThemedTabsColors(ViraThemeColorName.grey));
133
+ const standaloneStyles = standaloneThemeColorNames.map((colorName) => {
134
+ const variantSelector = hostClasses[`vira-tabs-color-${colorName}`].selector;
135
+ const colors = buildThemedTabsColors(colorName);
136
+ return buildVariantCssRule(variantSelector, colors);
137
+ });
138
+ return unsafeCSS([
139
+ ...themedStyles,
140
+ plainStyle,
141
+ neutralStyle,
142
+ ...standaloneStyles,
143
+ ].join('\n'));
114
144
  }
115
145
  return css `
116
146
  :host {
117
147
  display: flex;
148
+ position: relative;
118
149
  box-sizing: border-box;
119
150
  ${noUserSelect};
120
151
  width: 100%;
@@ -265,18 +296,19 @@ export const ViraTabs = defineViraElement()({
265
296
 
266
297
  ${hostClasses['vira-tabs-overflowing'].selector} .tabs-container {
267
298
  visibility: hidden;
299
+ position: absolute;
268
300
  height: 0;
269
301
  }
270
302
 
271
- .overflow-menu {
303
+ ${ViraSelect} {
272
304
  display: none;
273
305
  }
274
306
 
275
- ${hostClasses['vira-tabs-overflowing'].selector} .overflow-menu {
276
- display: flex;
307
+ ${hostClasses['vira-tabs-overflowing'].selector} ${ViraSelect} {
308
+ display: inline-flex;
277
309
  align-items: center;
278
310
  width: fit-content;
279
- padding-left: 8px;
311
+ max-width: 100%;
280
312
  }
281
313
 
282
314
  ${hostClasses['vira-tabs-fill-width'].selector} {
@@ -302,10 +334,6 @@ export const ViraTabs = defineViraElement()({
302
334
  display: flex;
303
335
  padding: 8px 16px;
304
336
  }
305
-
306
- ${ViraMenuTrigger} {
307
- margin: 3px 0;
308
- }
309
337
  `;
310
338
  },
311
339
  cleanup({ state }) {
@@ -365,53 +393,53 @@ export const ViraTabs = defineViraElement()({
365
393
  `;
366
394
  }, check.isTruthy);
367
395
  const selectedTab = inputs.tabs.find((tab) => routeHasPaths(inputs.currentRoute, tab.paths));
368
- const menuItems = renderMenuItemEntries(filterMap(inputs.tabs, (tab) => {
396
+ const selectOptions = filterMap(inputs.tabs, (tab) => {
369
397
  if (tab.isHidden) {
370
398
  return undefined;
371
399
  }
372
- const isSelected = routeHasPaths(inputs.currentRoute, tab.paths);
373
400
  return {
374
- content: html `
375
- <${ViraLink.assign({
376
- route: {
377
- router: inputs.router,
378
- route: {
379
- paths: tab.paths.fullPaths,
380
- },
381
- scrollToTop: true,
382
- },
383
- disableLinkStyles: true,
384
- })}>
385
- ${tab.label}
386
- </${ViraLink}>
387
- `,
388
- selected: isSelected,
401
+ value: tab.paths.fullPaths.join('/'),
402
+ label: tab.label,
389
403
  disabled: tab.isDisabled,
390
- onClick() {
391
- if (!tab.isDisabled) {
392
- dispatch(new events.tabSelect(tab));
393
- }
394
- },
395
404
  };
396
- }, check.isTruthy));
405
+ }, check.isTruthy);
406
+ function captureModifierState(event) {
407
+ updateState({
408
+ modifierKeyHeld: event.ctrlKey || event.metaKey || event.shiftKey,
409
+ });
410
+ }
397
411
  return html `
398
- <${ViraMenuTrigger.assign({
399
- horizontalAnchor: inputs.menuHorizontalAnchor,
400
- isDisabled: inputs.menuIsDisabled,
401
- popUpOffset: inputs.menuPopUpOffset,
402
- menuCornerStyle: ViraMenuCornerStyle.AllRounded,
412
+ <${ViraSelect.assign({
413
+ options: selectOptions,
414
+ value: selectedTab?.paths.fullPaths.join('/'),
403
415
  })}
404
- class="overflow-menu"
405
- >
406
- <${ViraButton.assign({
407
- text: selectedTab?.label || '',
408
- showMenuCaret: true,
409
- colorVariant: ViraColorVariant.Neutral,
416
+ ${listen('mousedown', captureModifierState)}
417
+ ${listen('keydown', captureModifierState)}
418
+ ${listen(ViraSelect.events.valueChange, (event) => {
419
+ try {
420
+ const selectedValue = event.detail;
421
+ const tab = inputs.tabs.find((tab) => tab.paths.fullPaths.join('/') === selectedValue);
422
+ if (!tab || tab.isDisabled) {
423
+ return;
424
+ }
425
+ dispatch(new events.tabSelect(tab));
426
+ const route = {
427
+ paths: tab.paths.fullPaths,
428
+ };
429
+ if (state.modifierKeyHeld) {
430
+ globalThis.open(inputs.router.createRouteUrl(route), '_blank', 'noopener,noreferrer');
431
+ }
432
+ else {
433
+ inputs.router.setRoute(route);
434
+ }
435
+ }
436
+ finally {
437
+ updateState({
438
+ modifierKeyHeld: false,
439
+ });
440
+ }
410
441
  })}
411
- slot=${ViraMenuTrigger.slotNames.trigger}
412
- ></${ViraButton}>
413
- ${menuItems}
414
- </${ViraMenuTrigger}>
442
+ ></${ViraSelect}>
415
443
  <ul
416
444
  class="tabs-container"
417
445
  role="tablist"
@@ -1,6 +1,7 @@
1
1
  import { type PartialWithUndefined } from '@augment-vir/common';
2
2
  import { type Primitive, type RequireExactlyOne } from 'type-fest';
3
3
  import { ViraColorVariant, ViraEmphasis, ViraSize } from '../styles/form-variants.js';
4
+ import { ViraThemeColorName } from '../styles/vira-color-theme-object.js';
4
5
  /**
5
6
  * A "tag" or "label" or "pill" element. Supports many variations including non-clickable,
6
7
  * selectable, and cancellable variations.
@@ -26,10 +27,15 @@ export declare const ViraTag: import("element-vir").DeclarativeElementDefinition
26
27
  size: ViraSize;
27
28
  /** @default ViraEmphasis.Standard */
28
29
  emphasis: ViraEmphasis;
29
- /** @default ViraColorVariant.Info */
30
- colorVariant: ViraColorVariant;
30
+ /**
31
+ * Color scheme. Accepts any {@link ViraColorVariant} or a {@link ViraThemeColorName} (e.g.,
32
+ * `ViraThemeColorName.blue`).
33
+ *
34
+ * @default ViraColorVariant.Plain
35
+ */
36
+ color: ViraColorVariant | ViraThemeColorName;
31
37
  disabled: boolean;
32
38
  }>, {}, {
33
39
  toggle: import("element-vir").DefineEvent<boolean>;
34
40
  cancel: import("element-vir").DefineEvent<void>;
35
- }, "vira-tag-selectable" | "vira-tag-checked" | "vira-tag-not-checked" | "vira-tag-cancellable" | "vira-tag-not-clickable" | "vira-tag-disabled" | "vira-tag-size-large" | "vira-tag-size-medium" | "vira-tag-size-small" | "vira-tag-emphasis-standard" | "vira-tag-emphasis-subtle" | "vira-tag-color-info" | "vira-tag-color-plain" | "vira-tag-color-neutral" | "vira-tag-color-danger" | "vira-tag-color-warning" | "vira-tag-color-positive" | "vira-tag-color-brand" | "vira-tag-color-special", "vira-tag-text-color" | "vira-tag-background-color" | "vira-tag-border-color" | "vira-tag-hover-text-color" | "vira-tag-hover-background-color" | "vira-tag-hover-border-color" | "vira-tag-active-text-color" | "vira-tag-active-background-color" | "vira-tag-active-border-color" | "vira-tag-disabled-text-color" | "vira-tag-disabled-background-color" | "vira-tag-disabled-border-color" | "vira-tag-border-radius" | "vira-tag-gap" | "vira-tag-horizontal-padding" | "vira-tag-border-width", readonly [], readonly []>;
41
+ }, "vira-tag-selectable" | "vira-tag-checked" | "vira-tag-not-checked" | "vira-tag-cancellable" | "vira-tag-not-clickable" | "vira-tag-disabled" | "vira-tag-size-large" | "vira-tag-size-medium" | "vira-tag-size-small" | "vira-tag-emphasis-standard" | "vira-tag-emphasis-subtle" | "vira-tag-color-red" | "vira-tag-color-yellow" | "vira-tag-color-green" | "vira-tag-color-blue" | "vira-tag-color-brand" | "vira-tag-color-purple" | "vira-tag-color-plain" | "vira-tag-color-neutral" | "vira-tag-color-teal" | "vira-tag-color-pink" | "vira-tag-color-grey", "vira-tag-text-color" | "vira-tag-background-color" | "vira-tag-border-color" | "vira-tag-hover-text-color" | "vira-tag-hover-background-color" | "vira-tag-hover-border-color" | "vira-tag-active-text-color" | "vira-tag-active-background-color" | "vira-tag-active-border-color" | "vira-tag-disabled-text-color" | "vira-tag-disabled-background-color" | "vira-tag-disabled-border-color" | "vira-tag-border-radius" | "vira-tag-gap" | "vira-tag-horizontal-padding" | "vira-tag-border-width", readonly [], readonly []>;
@@ -1,23 +1,22 @@
1
1
  import { check } from '@augment-vir/assert';
2
- import { arrayToObject, mapEnumToObject } from '@augment-vir/common';
2
+ import { arrayToObject, getObjectTypedKeys } from '@augment-vir/common';
3
3
  import { ContrastLevelName } from '@electrovir/color/dist/data/contrast/contrast.js';
4
4
  import { css, defineElementEvent, html, listen, unsafeCSS } from 'element-vir';
5
5
  import { themeDefaultKey } from 'theme-vir/dist/color-theme/color-theme.js';
6
6
  import { Check16Icon } from '../icons/icon-svgs/16/check-16.icon.js';
7
7
  import { X16Icon } from '../icons/icon-svgs/16/x-16.icon.js';
8
8
  import { viraFormCssVars } from '../styles/form-styles.js';
9
- import { ViraColorVariant, viraColorVariants, viraColorVariantToColorName, ViraEmphasis, viraEmphasisVariants, ViraSize, viraSizeHeights, viraSizeVariants, } from '../styles/form-variants.js';
9
+ import { standaloneThemeColorNames, ViraColorVariant, viraColorVariantToHostClassKey, ViraEmphasis, viraEmphasisVariants, ViraSize, viraSizeHeights, viraSizeVariants, } from '../styles/form-variants.js';
10
10
  import { noNativeFormStyles } from '../styles/native-styles.js';
11
11
  import { noUserSelect } from '../styles/user-select.js';
12
- import { viraThemeByKeys } from '../styles/vira-color-theme-object.js';
12
+ import { viraThemeByKeys, ViraThemeColorName } from '../styles/vira-color-theme-object.js';
13
13
  import { viraTheme } from '../styles/vira-color-theme.js';
14
14
  import { defineViraElement } from '../util/define-vira-element.js';
15
15
  import { ViraIcon } from './vira-icon.element.js';
16
16
  const transparentColor = {
17
17
  value: css `transparent`,
18
18
  };
19
- function buildThemedTagColors(colorVariant) {
20
- const colorName = viraColorVariantToColorName[colorVariant];
19
+ function buildThemedTagColors(colorName) {
21
20
  const behindBg = viraThemeByKeys[colorName]['behind-bg'];
22
21
  const onSelf = viraThemeByKeys[colorName]['on-self'];
23
22
  return {
@@ -57,8 +56,7 @@ function buildThemedTagColors(colorVariant) {
57
56
  },
58
57
  };
59
58
  }
60
- function buildThemedNotCheckedColors(colorVariant) {
61
- const colorName = viraColorVariantToColorName[colorVariant];
59
+ function buildThemedNotCheckedColors(colorName) {
62
60
  const onSelfBodyText = viraThemeByKeys[colorName]['on-self'][ContrastLevelName.BodyText];
63
61
  return {
64
62
  idle: {
@@ -78,69 +76,59 @@ function buildThemedNotCheckedColors(colorVariant) {
78
76
  },
79
77
  };
80
78
  }
81
- const tagColorVariantColors = {
82
- ...mapEnumToObject(ViraColorVariant, (colorVariant) => {
83
- return buildThemedTagColors(colorVariant);
84
- }),
85
- [ViraColorVariant.Plain]: {
86
- [ViraEmphasis.Standard]: {
87
- idle: {
88
- backgroundColor: viraTheme.colors[themeDefaultKey].foreground,
89
- textColor: viraTheme.colors[themeDefaultKey].background,
90
- borderColor: viraTheme.colors[themeDefaultKey].foreground,
91
- },
92
- hover: {
93
- backgroundColor: viraTheme.colors['vira-grey-behind-bg-body'].background,
94
- textColor: viraTheme.colors['vira-grey-behind-bg-body'].foreground,
95
- borderColor: viraTheme.colors['vira-grey-behind-bg-body'].background,
96
- },
97
- active: {
98
- backgroundColor: viraTheme.colors[themeDefaultKey].foreground,
99
- textColor: viraTheme.colors[themeDefaultKey].background,
100
- borderColor: viraTheme.colors[themeDefaultKey].foreground,
101
- },
79
+ const plainTagColors = {
80
+ [ViraEmphasis.Standard]: {
81
+ idle: {
82
+ backgroundColor: viraTheme.colors[themeDefaultKey].foreground,
83
+ textColor: viraTheme.colors[themeDefaultKey].background,
84
+ borderColor: viraTheme.colors[themeDefaultKey].foreground,
102
85
  },
103
- [ViraEmphasis.Subtle]: {
104
- idle: {
105
- backgroundColor: transparentColor,
106
- textColor: viraTheme.colors[themeDefaultKey].foreground,
107
- borderColor: transparentColor,
108
- },
109
- hover: {
110
- backgroundColor: viraTheme.colors['vira-grey-behind-fg-small-body'].background,
111
- textColor: viraTheme.colors['vira-grey-behind-fg-small-body'].foreground,
112
- borderColor: viraTheme.colors['vira-grey-behind-fg-small-body'].background,
113
- },
114
- active: {
115
- backgroundColor: viraTheme.colors['vira-grey-behind-fg-body'].background,
116
- textColor: viraTheme.colors['vira-grey-behind-fg-body'].foreground,
117
- borderColor: viraTheme.colors['vira-grey-behind-fg-body'].background,
118
- },
86
+ hover: {
87
+ backgroundColor: viraTheme.colors['vira-grey-behind-bg-body'].background,
88
+ textColor: viraTheme.colors['vira-grey-behind-bg-body'].foreground,
89
+ borderColor: viraTheme.colors['vira-grey-behind-bg-body'].background,
90
+ },
91
+ active: {
92
+ backgroundColor: viraTheme.colors[themeDefaultKey].foreground,
93
+ textColor: viraTheme.colors[themeDefaultKey].background,
94
+ borderColor: viraTheme.colors[themeDefaultKey].foreground,
119
95
  },
120
96
  },
121
- };
122
- const tagNotCheckedColors = {
123
- ...mapEnumToObject(ViraColorVariant, (colorVariant) => {
124
- return buildThemedNotCheckedColors(colorVariant);
125
- }),
126
- [ViraColorVariant.Plain]: {
97
+ [ViraEmphasis.Subtle]: {
127
98
  idle: {
128
- textColor: viraTheme.colors[themeDefaultKey].foreground,
129
99
  backgroundColor: transparentColor,
130
- borderColor: viraTheme.colors['vira-grey-on-self-body'].background,
100
+ textColor: viraTheme.colors[themeDefaultKey].foreground,
101
+ borderColor: transparentColor,
131
102
  },
132
103
  hover: {
133
104
  backgroundColor: viraTheme.colors['vira-grey-behind-fg-small-body'].background,
134
105
  textColor: viraTheme.colors['vira-grey-behind-fg-small-body'].foreground,
135
- borderColor: viraTheme.colors['vira-grey-on-self-body'].background,
106
+ borderColor: viraTheme.colors['vira-grey-behind-fg-small-body'].background,
136
107
  },
137
108
  active: {
138
109
  backgroundColor: viraTheme.colors['vira-grey-behind-fg-body'].background,
139
110
  textColor: viraTheme.colors['vira-grey-behind-fg-body'].foreground,
140
- borderColor: viraTheme.colors['vira-grey-on-self-body'].background,
111
+ borderColor: viraTheme.colors['vira-grey-behind-fg-body'].background,
141
112
  },
142
113
  },
143
114
  };
115
+ const plainNotCheckedColors = {
116
+ idle: {
117
+ textColor: viraTheme.colors[themeDefaultKey].foreground,
118
+ backgroundColor: transparentColor,
119
+ borderColor: viraTheme.colors['vira-grey-on-self-body'].background,
120
+ },
121
+ hover: {
122
+ backgroundColor: viraTheme.colors['vira-grey-behind-fg-small-body'].background,
123
+ textColor: viraTheme.colors['vira-grey-behind-fg-small-body'].foreground,
124
+ borderColor: viraTheme.colors['vira-grey-on-self-body'].background,
125
+ },
126
+ active: {
127
+ backgroundColor: viraTheme.colors['vira-grey-behind-fg-body'].background,
128
+ textColor: viraTheme.colors['vira-grey-behind-fg-body'].foreground,
129
+ borderColor: viraTheme.colors['vira-grey-on-self-body'].background,
130
+ },
131
+ };
144
132
  /**
145
133
  * A "tag" or "label" or "pill" element. Supports many variations including non-clickable,
146
134
  * selectable, and cancellable variations.
@@ -184,13 +172,24 @@ export const ViraTag = defineViraElement()({
184
172
  'vira-tag-size-small': ({ inputs }) => inputs.size === ViraSize.Small,
185
173
  'vira-tag-emphasis-standard': ({ inputs }) => !inputs.emphasis || inputs.emphasis === ViraEmphasis.Standard,
186
174
  'vira-tag-emphasis-subtle': ({ inputs }) => inputs.emphasis === ViraEmphasis.Subtle,
187
- ...arrayToObject(viraColorVariants, (colorVariant) => {
175
+ ...arrayToObject(getObjectTypedKeys(viraColorVariantToHostClassKey), (colorVariant) => {
176
+ const colorKey = viraColorVariantToHostClassKey[colorVariant];
177
+ return {
178
+ key: `vira-tag-color-${colorKey}`,
179
+ value: ({ inputs, }) => {
180
+ return inputs.color === colorVariant || inputs.color === colorKey;
181
+ },
182
+ };
183
+ }, {
184
+ useRequired: true,
185
+ }),
186
+ 'vira-tag-color-plain': ({ inputs, }) => !inputs.color || inputs.color === ViraColorVariant.Plain,
187
+ 'vira-tag-color-neutral': ({ inputs, }) => inputs.color === ViraColorVariant.Neutral,
188
+ ...arrayToObject(standaloneThemeColorNames, (colorName) => {
188
189
  return {
189
- key: `vira-tag-color-${colorVariant}`,
190
+ key: `vira-tag-color-${colorName}`,
190
191
  value: ({ inputs, }) => {
191
- return colorVariant === ViraColorVariant.Plain
192
- ? !inputs.colorVariant || inputs.colorVariant === colorVariant
193
- : inputs.colorVariant === colorVariant;
192
+ return inputs.color === colorName;
194
193
  },
195
194
  };
196
195
  }, {
@@ -198,68 +197,96 @@ export const ViraTag = defineViraElement()({
198
197
  }),
199
198
  },
200
199
  styles: ({ cssVars, hostClasses }) => {
201
- function generateVariantCss() {
202
- const allStyles = viraEmphasisVariants.flatMap((emphasis) => {
203
- return viraColorVariants.map((colorVariant) => {
204
- const colors = tagColorVariantColors[colorVariant][emphasis];
205
- const variantSelector = hostClasses[`vira-tag-color-${colorVariant}`].selector;
206
- const emphasisSelector = hostClasses[`vira-tag-emphasis-${emphasis}`].selector;
207
- return css `
208
- ${variantSelector}${emphasisSelector} {
209
- ${cssVars['vira-tag-background-color'].name}: ${colors.idle
210
- .backgroundColor.value};
211
- ${cssVars['vira-tag-text-color'].name}: ${colors.idle.textColor.value};
212
- ${cssVars['vira-tag-border-color'].name}: ${colors.idle.borderColor
213
- .value};
200
+ function buildVariantCssRule(variantSelector, emphasisSelector, colors) {
201
+ return css `
202
+ ${variantSelector}${emphasisSelector} {
203
+ ${cssVars['vira-tag-background-color'].name}: ${colors.idle.backgroundColor
204
+ .value};
205
+ ${cssVars['vira-tag-text-color'].name}: ${colors.idle.textColor.value};
206
+ ${cssVars['vira-tag-border-color'].name}: ${colors.idle.borderColor.value};
214
207
 
215
- ${cssVars['vira-tag-hover-background-color'].name}: ${colors.hover
216
- .backgroundColor.value};
217
- ${cssVars['vira-tag-hover-text-color'].name}: ${colors.hover.textColor
218
- .value};
219
- ${cssVars['vira-tag-hover-border-color'].name}: ${colors.hover
220
- .borderColor.value};
208
+ ${cssVars['vira-tag-hover-background-color'].name}: ${colors.hover
209
+ .backgroundColor.value};
210
+ ${cssVars['vira-tag-hover-text-color'].name}: ${colors.hover.textColor.value};
211
+ ${cssVars['vira-tag-hover-border-color'].name}: ${colors.hover.borderColor
212
+ .value};
221
213
 
222
- ${cssVars['vira-tag-active-background-color'].name}: ${colors.active
223
- .backgroundColor.value};
224
- ${cssVars['vira-tag-active-text-color'].name}: ${colors.active.textColor
225
- .value};
226
- ${cssVars['vira-tag-active-border-color'].name}: ${colors.active
227
- .borderColor.value};
228
- }
229
- `;
214
+ ${cssVars['vira-tag-active-background-color'].name}: ${colors.active
215
+ .backgroundColor.value};
216
+ ${cssVars['vira-tag-active-text-color'].name}: ${colors.active.textColor.value};
217
+ ${cssVars['vira-tag-active-border-color'].name}: ${colors.active.borderColor
218
+ .value};
219
+ }
220
+ `;
221
+ }
222
+ function generateVariantCss() {
223
+ const allStyles = viraEmphasisVariants.flatMap((emphasis) => {
224
+ const emphasisSelector = hostClasses[`vira-tag-emphasis-${emphasis}`].selector;
225
+ const themedStyles = getObjectTypedKeys(viraColorVariantToHostClassKey).map((colorVariant) => {
226
+ const colorKey = viraColorVariantToHostClassKey[colorVariant];
227
+ const colors = buildThemedTagColors(colorKey)[emphasis];
228
+ const variantSelector = hostClasses[`vira-tag-color-${colorKey}`].selector;
229
+ return buildVariantCssRule(variantSelector, emphasisSelector, colors);
230
+ });
231
+ const plainStyle = buildVariantCssRule(hostClasses['vira-tag-color-plain'].selector, emphasisSelector, plainTagColors[emphasis]);
232
+ const neutralStyle = buildVariantCssRule(hostClasses['vira-tag-color-neutral'].selector, emphasisSelector, buildThemedTagColors(ViraThemeColorName.grey)[emphasis]);
233
+ const standaloneStyles = standaloneThemeColorNames.map((colorName) => {
234
+ const colors = buildThemedTagColors(colorName)[emphasis];
235
+ const variantSelector = hostClasses[`vira-tag-color-${colorName}`].selector;
236
+ return buildVariantCssRule(variantSelector, emphasisSelector, colors);
230
237
  });
238
+ return [
239
+ ...themedStyles,
240
+ plainStyle,
241
+ neutralStyle,
242
+ ...standaloneStyles,
243
+ ];
231
244
  });
232
245
  return unsafeCSS(allStyles.join('\n'));
233
246
  }
234
- function generateNotCheckedCss() {
235
- const allStyles = viraColorVariants.map((colorVariant) => {
236
- const colors = tagNotCheckedColors[colorVariant];
237
- const variantSelector = hostClasses[`vira-tag-color-${colorVariant}`].selector;
238
- const notCheckedSelector = hostClasses['vira-tag-not-checked'].selector;
239
- return css `
240
- ${variantSelector}${notCheckedSelector}${notCheckedSelector}${notCheckedSelector} {
241
- ${cssVars['vira-tag-background-color'].name}: ${colors.idle.backgroundColor
242
- .value};
243
- ${cssVars['vira-tag-text-color'].name}: ${colors.idle.textColor.value};
244
- ${cssVars['vira-tag-border-color'].name}: ${colors.idle.borderColor.value};
247
+ function buildNotCheckedCssRule(variantSelector, colors) {
248
+ const notCheckedSelector = hostClasses['vira-tag-not-checked'].selector;
249
+ return css `
250
+ ${variantSelector}${notCheckedSelector}${notCheckedSelector}${notCheckedSelector} {
251
+ ${cssVars['vira-tag-background-color'].name}: ${colors.idle.backgroundColor
252
+ .value};
253
+ ${cssVars['vira-tag-text-color'].name}: ${colors.idle.textColor.value};
254
+ ${cssVars['vira-tag-border-color'].name}: ${colors.idle.borderColor.value};
245
255
 
246
- ${cssVars['vira-tag-hover-background-color'].name}: ${colors.hover
247
- .backgroundColor.value};
248
- ${cssVars['vira-tag-hover-text-color'].name}: ${colors.hover.textColor
249
- .value};
250
- ${cssVars['vira-tag-hover-border-color'].name}: ${colors.hover.borderColor
251
- .value};
256
+ ${cssVars['vira-tag-hover-background-color'].name}: ${colors.hover
257
+ .backgroundColor.value};
258
+ ${cssVars['vira-tag-hover-text-color'].name}: ${colors.hover.textColor.value};
259
+ ${cssVars['vira-tag-hover-border-color'].name}: ${colors.hover.borderColor
260
+ .value};
252
261
 
253
- ${cssVars['vira-tag-active-background-color'].name}: ${colors.active
254
- .backgroundColor.value};
255
- ${cssVars['vira-tag-active-text-color'].name}: ${colors.active.textColor
256
- .value};
257
- ${cssVars['vira-tag-active-border-color'].name}: ${colors.active.borderColor
258
- .value};
259
- }
260
- `;
262
+ ${cssVars['vira-tag-active-background-color'].name}: ${colors.active
263
+ .backgroundColor.value};
264
+ ${cssVars['vira-tag-active-text-color'].name}: ${colors.active.textColor.value};
265
+ ${cssVars['vira-tag-active-border-color'].name}: ${colors.active.borderColor
266
+ .value};
267
+ }
268
+ `;
269
+ }
270
+ function generateNotCheckedCss() {
271
+ const themedStyles = getObjectTypedKeys(viraColorVariantToHostClassKey).map((colorVariant) => {
272
+ const colorKey = viraColorVariantToHostClassKey[colorVariant];
273
+ const colors = buildThemedNotCheckedColors(colorKey);
274
+ const variantSelector = hostClasses[`vira-tag-color-${colorKey}`].selector;
275
+ return buildNotCheckedCssRule(variantSelector, colors);
261
276
  });
262
- return unsafeCSS(allStyles.join('\n'));
277
+ const plainStyle = buildNotCheckedCssRule(hostClasses['vira-tag-color-plain'].selector, plainNotCheckedColors);
278
+ const neutralStyle = buildNotCheckedCssRule(hostClasses['vira-tag-color-neutral'].selector, buildThemedNotCheckedColors(ViraThemeColorName.grey));
279
+ const standaloneStyles = standaloneThemeColorNames.map((colorName) => {
280
+ const colors = buildThemedNotCheckedColors(colorName);
281
+ const variantSelector = hostClasses[`vira-tag-color-${colorName}`].selector;
282
+ return buildNotCheckedCssRule(variantSelector, colors);
283
+ });
284
+ return unsafeCSS([
285
+ ...themedStyles,
286
+ plainStyle,
287
+ neutralStyle,
288
+ ...standaloneStyles,
289
+ ].join('\n'));
263
290
  }
264
291
  function generateSizeVariantCss() {
265
292
  const styles = viraSizeVariants.map((sizeVariant) => {
@@ -1,17 +1,16 @@
1
- import { ViraThemeColorName } from './vira-color-theme-object.js';
2
1
  /**
3
2
  * All available variants for controlling vira form colors.
4
3
  *
5
4
  * @category Internal
6
5
  */
7
6
  export declare enum ViraColorVariant {
7
+ /** @default blue colored */
8
+ Info = "info",
8
9
  /**
9
- * This is the default.
10
+ * Applied when no `color` input is set on a color-aware element.
10
11
  *
11
- * @default blue colored
12
+ * @default black colored
12
13
  */
13
- Info = "info",
14
- /** @default black colored */
15
14
  Plain = "plain",
16
15
  /** @default grey colored */
17
16
  Neutral = "neutral",
@@ -26,24 +25,41 @@ export declare enum ViraColorVariant {
26
25
  /** @default purple colored */
27
26
  Special = "special",
28
27
  /**
29
- * No color variant styles will be applied at all. All related CSS vars are free to customize to
30
- * your wishes.
28
+ * No color variant styles will be applied at all, allowing the element's colors to be fully
29
+ * customized via its CSS vars.
31
30
  */
32
- None = "none"
31
+ Custom = "custom"
33
32
  }
34
33
  /**
35
- * Maps {@link ViraColorVariant} values that support colors to their respective vira theme color
36
- * keys.
34
+ * All defined color variants starting with the default.
35
+ *
36
+ * @category Internal
37
+ */
38
+ export declare const viraColorVariants: readonly [ViraColorVariant.Info, ViraColorVariant.Plain, ViraColorVariant.Neutral, ViraColorVariant.Danger, ViraColorVariant.Warning, ViraColorVariant.Positive, ViraColorVariant.Brand, ViraColorVariant.Special];
39
+ /**
40
+ * Maps themed {@link ViraColorVariant} members to the {@link ViraThemeColorName} used as their host
41
+ * class suffix on color-aware elements (`vira-*-color-${themeColor}`). `Plain` and `Neutral` are
42
+ * intentionally omitted: they are always special-cased by each element so their host classes stay
43
+ * distinct from any theme color class (including `grey`).
37
44
  *
38
45
  * @category Internal
39
46
  */
40
- export declare const viraColorVariantToColorName: Record<ViraColorVariant, ViraThemeColorName>;
47
+ export declare const viraColorVariantToHostClassKey: {
48
+ readonly info: "blue";
49
+ readonly danger: "red";
50
+ readonly warning: "yellow";
51
+ readonly positive: "green";
52
+ readonly brand: "brand";
53
+ readonly special: "purple";
54
+ };
41
55
  /**
42
- * All defined color variants starting with the default.
56
+ * {@link ViraThemeColorName} values that have no corresponding themed {@link ViraColorVariant} in
57
+ * {@link viraColorVariantToHostClassKey}. Color-aware elements generate a standalone host class for
58
+ * each so every {@link ViraThemeColorName} (including `grey`) can be passed as a `color` input.
43
59
  *
44
60
  * @category Internal
45
61
  */
46
- export declare const viraColorVariants: readonly [ViraColorVariant.Info, ViraColorVariant.Plain, ViraColorVariant.Neutral, ViraColorVariant.Danger, ViraColorVariant.Warning, ViraColorVariant.Positive, ViraColorVariant.Brand, ViraColorVariant.Special];
62
+ export declare const standaloneThemeColorNames: ("red" | "yellow" | "green" | "teal" | "blue" | "brand" | "purple" | "pink" | "grey")[];
47
63
  /**
48
64
  * All available variants for controlling vira form sizes.
49
65
  *
@@ -1,3 +1,5 @@
1
+ import { check } from '@augment-vir/assert';
2
+ import { getObjectTypedValues } from '@augment-vir/common';
1
3
  import { ViraThemeColorName } from './vira-color-theme-object.js';
2
4
  /**
3
5
  * All available variants for controlling vira form colors.
@@ -6,13 +8,13 @@ import { ViraThemeColorName } from './vira-color-theme-object.js';
6
8
  */
7
9
  export var ViraColorVariant;
8
10
  (function (ViraColorVariant) {
11
+ /** @default blue colored */
12
+ ViraColorVariant["Info"] = "info";
9
13
  /**
10
- * This is the default.
14
+ * Applied when no `color` input is set on a color-aware element.
11
15
  *
12
- * @default blue colored
16
+ * @default black colored
13
17
  */
14
- ViraColorVariant["Info"] = "info";
15
- /** @default black colored */
16
18
  ViraColorVariant["Plain"] = "plain";
17
19
  /** @default grey colored */
18
20
  ViraColorVariant["Neutral"] = "neutral";
@@ -27,28 +29,11 @@ export var ViraColorVariant;
27
29
  /** @default purple colored */
28
30
  ViraColorVariant["Special"] = "special";
29
31
  /**
30
- * No color variant styles will be applied at all. All related CSS vars are free to customize to
31
- * your wishes.
32
+ * No color variant styles will be applied at all, allowing the element's colors to be fully
33
+ * customized via its CSS vars.
32
34
  */
33
- ViraColorVariant["None"] = "none";
35
+ ViraColorVariant["Custom"] = "custom";
34
36
  })(ViraColorVariant || (ViraColorVariant = {}));
35
- /**
36
- * Maps {@link ViraColorVariant} values that support colors to their respective vira theme color
37
- * keys.
38
- *
39
- * @category Internal
40
- */
41
- export const viraColorVariantToColorName = {
42
- [ViraColorVariant.Info]: ViraThemeColorName.blue,
43
- [ViraColorVariant.Neutral]: ViraThemeColorName.grey,
44
- [ViraColorVariant.Danger]: ViraThemeColorName.red,
45
- [ViraColorVariant.Warning]: ViraThemeColorName.yellow,
46
- [ViraColorVariant.Positive]: ViraThemeColorName.green,
47
- [ViraColorVariant.Brand]: ViraThemeColorName.brand,
48
- [ViraColorVariant.Special]: ViraThemeColorName.purple,
49
- [ViraColorVariant.Plain]: ViraThemeColorName.grey,
50
- [ViraColorVariant.None]: ViraThemeColorName.grey,
51
- };
52
37
  /**
53
38
  * All defined color variants starting with the default.
54
39
  *
@@ -64,6 +49,30 @@ export const viraColorVariants = [
64
49
  ViraColorVariant.Brand,
65
50
  ViraColorVariant.Special,
66
51
  ];
52
+ /**
53
+ * Maps themed {@link ViraColorVariant} members to the {@link ViraThemeColorName} used as their host
54
+ * class suffix on color-aware elements (`vira-*-color-${themeColor}`). `Plain` and `Neutral` are
55
+ * intentionally omitted: they are always special-cased by each element so their host classes stay
56
+ * distinct from any theme color class (including `grey`).
57
+ *
58
+ * @category Internal
59
+ */
60
+ export const viraColorVariantToHostClassKey = {
61
+ [ViraColorVariant.Info]: ViraThemeColorName.blue,
62
+ [ViraColorVariant.Danger]: ViraThemeColorName.red,
63
+ [ViraColorVariant.Warning]: ViraThemeColorName.yellow,
64
+ [ViraColorVariant.Positive]: ViraThemeColorName.green,
65
+ [ViraColorVariant.Brand]: ViraThemeColorName.brand,
66
+ [ViraColorVariant.Special]: ViraThemeColorName.purple,
67
+ };
68
+ /**
69
+ * {@link ViraThemeColorName} values that have no corresponding themed {@link ViraColorVariant} in
70
+ * {@link viraColorVariantToHostClassKey}. Color-aware elements generate a standalone host class for
71
+ * each so every {@link ViraThemeColorName} (including `grey`) can be passed as a `color` input.
72
+ *
73
+ * @category Internal
74
+ */
75
+ export const standaloneThemeColorNames = getObjectTypedValues(ViraThemeColorName).filter((colorName) => !check.hasValue(getObjectTypedValues(viraColorVariantToHostClassKey), colorName));
67
76
  /**
68
77
  * All available variants for controlling vira form sizes.
69
78
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vira",
3
- "version": "31.15.2",
3
+ "version": "31.16.1",
4
4
  "description": "A simple and highly versatile design system using element-vir.",
5
5
  "keywords": [
6
6
  "design",