vira 28.5.0 → 28.6.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.
@@ -19,3 +19,4 @@ export * from './vira-input.element.js';
19
19
  export * from './vira-link.element.js';
20
20
  export * from './vira-modal.element.js';
21
21
  export * from './vira-progress.element.js';
22
+ export * from './vira-select.element.js';
@@ -19,3 +19,4 @@ export * from './vira-input.element.js';
19
19
  export * from './vira-link.element.js';
20
20
  export * from './vira-modal.element.js';
21
21
  export * from './vira-progress.element.js';
22
+ export * from './vira-select.element.js';
@@ -50,4 +50,4 @@ export declare const ViraInput: import("element-vir").DeclarativeElementDefiniti
50
50
  * that was blocked out of programmatic "value" property assignments.
51
51
  */
52
52
  inputBlocked: import("element-vir").DefineEvent<string>;
53
- }, "vira-input-disabled" | "vira-input-fit-text" | "vira-input-clear-button-shown" | "vira-input-error", "vira-input-background-color" | "vira-input-placeholder-color" | "vira-input-text-color" | "vira-input-border-color" | "vira-input-text-selection-color" | "vira-input-action-button-color" | "vira-input-clear-button-hover-color" | "vira-input-clear-button-active-color" | "vira-input-show-password-button-hover-color" | "vira-input-show-password-button-active-color" | "vira-input-padding-horizontal" | "vira-input-padding-vertical", readonly [], readonly []>;
53
+ }, "vira-input-disabled" | "vira-input-fit-text" | "vira-input-clear-button-shown" | "vira-input-error", "vira-input-action-button-color" | "vira-input-clear-button-hover-color" | "vira-input-clear-button-active-color" | "vira-input-show-password-button-hover-color" | "vira-input-show-password-button-active-color" | "vira-input-padding-horizontal" | "vira-input-padding-vertical", readonly [], readonly []>;
@@ -34,11 +34,6 @@ export var ViraInputType;
34
34
  export const ViraInput = defineViraElement()({
35
35
  tagName: 'vira-input',
36
36
  cssVars: {
37
- 'vira-input-background-color': 'white',
38
- 'vira-input-placeholder-color': '#cccccc',
39
- 'vira-input-text-color': '#000000',
40
- 'vira-input-border-color': '#cccccc',
41
- 'vira-input-text-selection-color': '#cfe9ff',
42
37
  'vira-input-action-button-color': '#aaaaaa',
43
38
  'vira-input-clear-button-hover-color': '#ff0000',
44
39
  'vira-input-clear-button-active-color': '#b30000',
@@ -56,7 +51,7 @@ export const ViraInput = defineViraElement()({
56
51
  display: inline-flex;
57
52
  width: 224px;
58
53
  box-sizing: border-box;
59
- color: ${cssVars['vira-input-text-color'].value};
54
+ color: ${viraFormCssVars['vira-form-foreground-color'].value};
60
55
  }
61
56
 
62
57
  label {
@@ -66,10 +61,13 @@ export const ViraInput = defineViraElement()({
66
61
  gap: 2px;
67
62
  width: 100%;
68
63
  max-width: 100%;
69
- }
70
64
 
71
- ${hostClasses['vira-input-disabled'].selector} {
72
- ${viraDisabledStyles};
65
+ & .input-label {
66
+ font-weight: ${viraFormCssVars['vira-form-label-font-weight'].value};
67
+ text-align: left;
68
+ flex-shrink: 0;
69
+ flex-wrap: wrap;
70
+ }
73
71
  }
74
72
 
75
73
  ${hostClasses['vira-input-fit-text'].selector} {
@@ -136,9 +134,7 @@ export const ViraInput = defineViraElement()({
136
134
  .wrapper-border {
137
135
  top: -1px;
138
136
  left: -1px;
139
- border: 1px solid ${cssVars['vira-input-border-color'].value};
140
- transition: border
141
- ${viraAnimationDurations['vira-interaction-animation-duration'].value};
137
+ border: 1px solid ${viraFormCssVars['vira-form-border-color'].value};
142
138
  }
143
139
 
144
140
  .input-wrapper {
@@ -151,7 +147,7 @@ export const ViraInput = defineViraElement()({
151
147
  position: relative;
152
148
  padding: 0 ${cssVars['vira-input-padding-horizontal'].value};
153
149
  border-radius: ${viraBorders['vira-form-input-radius'].value};
154
- background-color: ${cssVars['vira-input-background-color'].value};
150
+ background-color: ${viraFormCssVars['vira-form-background-color'].value};
155
151
  /*
156
152
  Border colors are actually applied via the .wrapper-border class. However, we must
157
153
  apply a border here still so that it takes up space.
@@ -187,11 +183,11 @@ export const ViraInput = defineViraElement()({
187
183
  }
188
184
 
189
185
  ::selection {
190
- background: ${cssVars['vira-input-text-selection-color']
186
+ background: ${viraFormCssVars['vira-form-text-selection-color']
191
187
  .value}; /* WebKit/Blink Browsers */
192
188
  }
193
189
  ::-moz-selection {
194
- background: ${cssVars['vira-input-text-selection-color']
190
+ background: ${viraFormCssVars['vira-form-text-selection-color']
195
191
  .value}; /* Gecko Browsers */
196
192
  }
197
193
 
@@ -201,7 +197,7 @@ export const ViraInput = defineViraElement()({
201
197
  }
202
198
 
203
199
  input::placeholder {
204
- color: ${cssVars['vira-input-placeholder-color'].value};
200
+ color: ${viraFormCssVars['vira-form-placeholder-color'].value};
205
201
  }
206
202
 
207
203
  .suffix {
@@ -243,6 +239,25 @@ export const ViraInput = defineViraElement()({
243
239
  border-color: ${viraFormCssVars['vira-form-error-foreground-color'].value};
244
240
  }
245
241
  }
242
+
243
+ ${hostClasses['vira-input-disabled'].selector} {
244
+ cursor: not-allowed;
245
+
246
+ & label,
247
+ & .input-wrapper {
248
+ cursor: not-allowed;
249
+ }
250
+
251
+ & input,
252
+ & .wrapper-border,
253
+ & input::placeholder {
254
+ ${viraDisabledStyles};
255
+ }
256
+
257
+ & .focus-border {
258
+ display: none;
259
+ }
260
+ }
246
261
  `;
247
262
  },
248
263
  events: {
@@ -291,16 +306,7 @@ export const ViraInput = defineViraElement()({
291
306
  width: ${state.forcedInputWidth}px;
292
307
  `
293
308
  : nothing;
294
- const shouldBlockBrowserHelps = inputs.disableBrowserHelps ||
295
- /**
296
- * Some browsers leaks passwords with their browser helps (like Chrome with
297
- * spellchecking).
298
- */
299
- inputs.type === ViraInputType.Password;
300
- const inputTemplate = html `
301
- <span
302
- class="input-wrapper"
303
- ${listen('mousedown', (event) => {
309
+ const mousedownListener = listen('mousedown', (event) => {
304
310
  const eventTarget = extractEventTarget(event, HTMLElement, {
305
311
  useOriginalTarget: true,
306
312
  });
@@ -309,8 +315,15 @@ export const ViraInput = defineViraElement()({
309
315
  event.preventDefault();
310
316
  inputElement.focus();
311
317
  }
312
- })}
313
- >
318
+ });
319
+ const shouldBlockBrowserHelps = inputs.disableBrowserHelps ||
320
+ /**
321
+ * Some browsers leaks passwords with their browser helps (like Chrome with
322
+ * spellchecking).
323
+ */
324
+ inputs.type === ViraInputType.Password;
325
+ const inputTemplate = html `
326
+ <span class="input-wrapper" ${inputs.label ? nothing : mousedownListener}>
314
327
  ${iconTemplate}
315
328
  ${renderIf(!!inputs.fitText, html `
316
329
  <span
@@ -400,7 +413,7 @@ export const ViraInput = defineViraElement()({
400
413
  `;
401
414
  if (inputs.label) {
402
415
  return html `
403
- <label for=${state.randomId}>
416
+ <label for=${state.randomId} ${mousedownListener}>
404
417
  <span class="input-label">${inputs.label}</span>
405
418
  ${inputTemplate}
406
419
  </label>
@@ -0,0 +1,48 @@
1
+ import { type PartialWithUndefined } from '@augment-vir/common';
2
+ import { type AttributeValues } from 'element-vir';
3
+ import { type ViraIconSvg } from '../icons/index.js';
4
+ /**
5
+ * Options for {@link ViraSelect}.
6
+ *
7
+ * @category Dropdown
8
+ * @category Elements
9
+ * @see https://electrovir.github.io/vira/book/elements/vira-select
10
+ */
11
+ export type ViraSelectOption = {
12
+ /** A value or id, used to keep track of which option is selected. */
13
+ value: string;
14
+ label: string;
15
+ } & PartialWithUndefined<{
16
+ disabled: boolean;
17
+ }>;
18
+ /**
19
+ * Similar to {@link ViraDropdown} but is, instead, simply a wrapper for `<select>` and nothing more.
20
+ *
21
+ * @category Dropdown
22
+ * @category Elements
23
+ * @see https://electrovir.github.io/vira/book/elements/vira-select
24
+ */
25
+ export declare const ViraSelect: import("element-vir").DeclarativeElementDefinition<"vira-select", Readonly<{
26
+ options: ReadonlyArray<Readonly<ViraSelectOption>>;
27
+ /** The currently selected option value. */
28
+ value: undefined | string;
29
+ } & PartialWithUndefined<{
30
+ icon: Readonly<ViraIconSvg>;
31
+ placeholder: string;
32
+ label: string;
33
+ disabled: boolean;
34
+ attributePassthrough: Readonly<PartialWithUndefined<{
35
+ label: AttributeValues;
36
+ select: AttributeValues;
37
+ option: AttributeValues;
38
+ }>>;
39
+ hasError: boolean;
40
+ }>>, {
41
+ /**
42
+ * Used to couple the label and select together. This is not applied if no label is
43
+ * provided.
44
+ */
45
+ randomId: string;
46
+ }, {
47
+ valueChange: import("element-vir").DefineEvent<string>;
48
+ }, "vira-select-disabled" | "vira-select-error", "vira-select-padding-horizontal" | "vira-select-padding-vertical" | "vira-select-icon-padding", readonly [], readonly []>;
@@ -0,0 +1,233 @@
1
+ import { randomString } from '@augment-vir/common';
2
+ import { extractEventTarget } from '@augment-vir/web';
3
+ import { attributes, classMap, css, defineElementEvent, html, ifDefined, listen, nothing, } from 'element-vir';
4
+ import { ChevronUp24Icon } from '../icons/index.js';
5
+ import { viraDisabledStyles } from '../styles/disabled.js';
6
+ import { createFocusStyles } from '../styles/focus.js';
7
+ import { viraFormCssVars } from '../styles/form-styles.js';
8
+ import { viraAnimationDurations, viraBorders } from '../styles/index.js';
9
+ import { noNativeFormStyles } from '../styles/native-styles.js';
10
+ import { defineViraElement } from './define-vira-element.js';
11
+ import { ViraIcon } from './vira-icon.element.js';
12
+ /**
13
+ * Similar to {@link ViraDropdown} but is, instead, simply a wrapper for `<select>` and nothing more.
14
+ *
15
+ * @category Dropdown
16
+ * @category Elements
17
+ * @see https://electrovir.github.io/vira/book/elements/vira-select
18
+ */
19
+ export const ViraSelect = defineViraElement()({
20
+ tagName: 'vira-select',
21
+ state() {
22
+ return {
23
+ /**
24
+ * Used to couple the label and select together. This is not applied if no label is
25
+ * provided.
26
+ */
27
+ randomId: randomString(32),
28
+ };
29
+ },
30
+ events: {
31
+ valueChange: defineElementEvent(),
32
+ },
33
+ cssVars: {
34
+ 'vira-select-padding-horizontal': '10px',
35
+ 'vira-select-padding-vertical': '6px',
36
+ 'vira-select-icon-padding': '44px',
37
+ },
38
+ hostClasses: {
39
+ 'vira-select-disabled': ({ inputs }) => !!inputs.disabled,
40
+ 'vira-select-error': ({ inputs }) => !!inputs.hasError,
41
+ },
42
+ styles: ({ hostClasses, cssVars }) => css `
43
+ :host {
44
+ position: relative;
45
+ display: inline-flex;
46
+ width: 223px;
47
+ box-sizing: border-box;
48
+ color: ${viraFormCssVars['vira-form-foreground-color'].value};
49
+ }
50
+
51
+ .select-wrapper {
52
+ ${noNativeFormStyles};
53
+ max-width: 100%;
54
+ flex-grow: 1;
55
+ display: inline-flex;
56
+ box-sizing: border-box;
57
+ align-items: center;
58
+ position: relative;
59
+ border-radius: ${viraBorders['vira-form-input-radius'].value};
60
+ background-color: ${viraFormCssVars['vira-form-background-color'].value};
61
+ /*
62
+ Border colors are actually applied via the .wrapper-border class. However, we must
63
+ apply a border here still so that it takes up space.
64
+ */
65
+ border: 1px solid transparent;
66
+ cursor: pointer;
67
+
68
+ & select {
69
+ appearance: none;
70
+ -webkit-appearance: none;
71
+ -moz-appearance: none;
72
+ font: inherit;
73
+ outline: none;
74
+ width: 100%;
75
+ border: none;
76
+ background: none;
77
+ border-radius: inherit;
78
+ padding: ${cssVars['vira-select-padding-vertical'].value} 31px
79
+ ${cssVars['vira-select-padding-vertical'].value}
80
+ ${cssVars['vira-select-padding-horizontal'].value};
81
+ cursor: pointer;
82
+ overflow: hidden;
83
+ text-overflow: ellipsis;
84
+
85
+ &:focus:focus-visible:not([aria-disabled='true']) ~ .focus-border {
86
+ ${createFocusStyles({
87
+ elementBorderSize: 0,
88
+ noNesting: true,
89
+ })}
90
+ }
91
+
92
+ &.placeholder {
93
+ color: ${viraFormCssVars['vira-form-placeholder-color'].value};
94
+ }
95
+
96
+ &.with-icon {
97
+ padding-left: ${cssVars['vira-select-icon-padding'].value};
98
+ }
99
+ }
100
+
101
+ & ${ViraIcon} {
102
+ position: absolute;
103
+ pointer-events: none;
104
+
105
+ &.trigger-icon {
106
+ transform: rotate(180deg);
107
+ right: 3px;
108
+ }
109
+
110
+ &.input-icon {
111
+ left: 10px;
112
+ }
113
+ }
114
+
115
+ & .border-style {
116
+ position: absolute;
117
+ top: 0;
118
+ left: 0;
119
+ width: 100%;
120
+ height: 100%;
121
+ border-radius: ${viraBorders['vira-form-input-radius'].value};
122
+ z-index: 0;
123
+ pointer-events: none;
124
+ }
125
+
126
+ & .wrapper-border {
127
+ top: -1px;
128
+ left: -1px;
129
+ border: 1px solid ${viraFormCssVars['vira-form-border-color'].value};
130
+ transition: border
131
+ ${viraAnimationDurations['vira-interaction-animation-duration'].value};
132
+ }
133
+ }
134
+
135
+ label {
136
+ display: flex;
137
+ flex-direction: column;
138
+ justify-content: flex-start;
139
+ gap: 2px;
140
+ width: 100%;
141
+ max-width: 100%;
142
+
143
+ & .select-label {
144
+ font-weight: ${viraFormCssVars['vira-form-label-font-weight'].value};
145
+ text-align: left;
146
+ flex-shrink: 0;
147
+ flex-wrap: wrap;
148
+ }
149
+ }
150
+
151
+ ${hostClasses['vira-select-disabled'].selector} {
152
+ cursor: not-allowed;
153
+
154
+ & label {
155
+ cursor: not-allowed;
156
+ }
157
+
158
+ & select,
159
+ & .wrapper-border {
160
+ ${viraDisabledStyles}
161
+ }
162
+ }
163
+
164
+ ${hostClasses['vira-select-error'].selector} {
165
+ & .wrapper-border {
166
+ border-color: ${viraFormCssVars['vira-form-error-foreground-color'].value};
167
+ }
168
+ }
169
+ `,
170
+ render({ inputs, state, dispatch, events }) {
171
+ const placeholderOptionTemplate = inputs.placeholder
172
+ ? html `
173
+ <option value="" disabled ?selected=${inputs.value == undefined}>
174
+ ${inputs.placeholder}
175
+ </option>
176
+ `
177
+ : nothing;
178
+ const selectTemplate = html `
179
+ <span class="select-wrapper">
180
+ <select
181
+ class=${classMap({
182
+ placeholder: !inputs.value && !!inputs.placeholder,
183
+ 'with-icon': !!inputs.icon,
184
+ })}
185
+ tabindex=${inputs.disabled ? -1 : 0}
186
+ id=${ifDefined(inputs.label ? state.randomId : undefined)}
187
+ aria-label=${ifDefined(inputs.label || undefined)}
188
+ aria-disabled=${ifDefined(inputs.disabled ? 'true' : undefined)}
189
+ ${listen('input', (event) => {
190
+ const element = extractEventTarget(event, HTMLSelectElement);
191
+ dispatch(new events.valueChange(element.value));
192
+ })}
193
+ ${attributes(inputs.attributePassthrough?.select)}
194
+ >
195
+ ${placeholderOptionTemplate}
196
+ ${inputs.options.map((option) => {
197
+ return html `
198
+ <option
199
+ ?selected=${option.value === inputs.value}
200
+ aria-label=${option.label}
201
+ ?disabled=${option.disabled}
202
+ >
203
+ ${option.label}
204
+ </option>
205
+ `;
206
+ })}
207
+ </select>
208
+ <!--
209
+ These separate style elements are necessary so that we can select them as
210
+ siblings of the focused <select> element.
211
+ -->
212
+
213
+ <div class="border-style focus-border"></div>
214
+
215
+ <div class="border-style wrapper-border"></div>
216
+
217
+ <${ViraIcon.assign({ icon: inputs.icon })} class="input-icon"></${ViraIcon}>
218
+ <${ViraIcon.assign({ icon: ChevronUp24Icon })} class="trigger-icon"></${ViraIcon}>
219
+ </span>
220
+ `;
221
+ if (inputs.label) {
222
+ return html `
223
+ <label for=${state.randomId} ${attributes(inputs.attributePassthrough?.label)}>
224
+ <span class="select-label">${inputs.label}</span>
225
+ ${selectTemplate}
226
+ </label>
227
+ `;
228
+ }
229
+ else {
230
+ return selectTemplate;
231
+ }
232
+ },
233
+ });
@@ -6,12 +6,15 @@
6
6
  */
7
7
  export declare const viraFormCssVars: import("lit-css-vars").CssVarDefinitions<{
8
8
  readonly 'vira-form-border-color': "#cccccc";
9
+ readonly 'vira-form-placeholder-color': "#cccccc";
9
10
  readonly 'vira-form-background-color': "white";
10
11
  readonly 'vira-form-foreground-color': "black";
12
+ readonly 'vira-form-text-selection-color': "#cfe9ff";
11
13
  readonly 'vira-form-selection-hover-background-color': "#d2eaff";
12
14
  readonly 'vira-form-selection-hover-foreground-color': "black";
13
15
  readonly 'vira-form-selection-active-background-color': "#d2eaff";
14
16
  readonly 'vira-form-selection-active-foreground-color': "black";
15
17
  readonly 'vira-form-error-foreground-color': "red";
16
18
  readonly 'vira-form-success-foreground-color': "green";
19
+ readonly 'vira-form-label-font-weight': "bold";
17
20
  }>;
@@ -7,12 +7,15 @@ import { defineCssVars } from 'lit-css-vars';
7
7
  */
8
8
  export const viraFormCssVars = defineCssVars({
9
9
  'vira-form-border-color': '#cccccc',
10
+ 'vira-form-placeholder-color': '#cccccc',
10
11
  'vira-form-background-color': 'white',
11
12
  'vira-form-foreground-color': 'black',
13
+ 'vira-form-text-selection-color': '#cfe9ff',
12
14
  'vira-form-selection-hover-background-color': '#d2eaff',
13
15
  'vira-form-selection-hover-foreground-color': 'black',
14
16
  'vira-form-selection-active-background-color': '#d2eaff',
15
17
  'vira-form-selection-active-foreground-color': 'black',
16
18
  'vira-form-error-foreground-color': 'red',
17
19
  'vira-form-success-foreground-color': 'green',
20
+ 'vira-form-label-font-weight': 'bold',
18
21
  });
@@ -20,6 +20,7 @@ export const noNativeFormStyles = css `
20
20
  background: none;
21
21
  border: none;
22
22
  font: inherit;
23
+ line-height: inherit;
23
24
  color: inherit;
24
25
  text-transform: inherit;
25
26
  text-decoration: inherit;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vira",
3
- "version": "28.5.0",
3
+ "version": "28.6.0",
4
4
  "description": "A simple and highly versatile design system using element-vir.",
5
5
  "keywords": [
6
6
  "design",