vira 31.20.0 → 31.21.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.
@@ -17,10 +17,10 @@ export type ViraCheckboxInputs = {
17
17
  } & PartialWithUndefined<{
18
18
  stylePassthrough: Partial<Record<ViraCheckboxInnerElements, CSSResult>>;
19
19
  attributePassthrough: Partial<Record<ViraCheckboxInnerElements, AttributeValues>>;
20
- disabled: boolean;
20
+ isDisabled: boolean;
21
21
  label: string;
22
22
  hasError: boolean;
23
- horizontal: boolean;
23
+ useHorizontalLabel: boolean;
24
24
  /** The checkbox will be filled with a form selection color when it is checked. */
25
25
  fillWhenChecked: boolean;
26
26
  /** The checkbox will be filled with a form error color when it is unchecked. */
@@ -15,7 +15,7 @@ import { ViraIcon } from './vira-icon.element.js';
15
15
  export const ViraCheckbox = defineViraElement()({
16
16
  tagName: 'vira-checkbox',
17
17
  hostClasses: {
18
- 'vira-checkbox-horizontal': ({ inputs }) => !!inputs.horizontal,
18
+ 'vira-checkbox-horizontal': ({ inputs }) => !!inputs.useHorizontalLabel,
19
19
  'vira-checkbox-filled-checked': ({ inputs }) => !!inputs.fillWhenChecked,
20
20
  'vira-checkbox-filled-unchecked': ({ inputs }) => !!inputs.fillWhenUnchecked,
21
21
  },
@@ -126,8 +126,8 @@ export const ViraCheckbox = defineViraElement()({
126
126
  }
127
127
 
128
128
  ${hostClasses['vira-checkbox-horizontal'].selector} label {
129
- flex-direction: row-reverse;
130
- align-items: flex-start;
129
+ flex-direction: row;
130
+ align-items: center;
131
131
  gap: 8px;
132
132
 
133
133
  & .label-text {
@@ -140,7 +140,7 @@ export const ViraCheckbox = defineViraElement()({
140
140
  },
141
141
  render({ inputs, dispatch, events }) {
142
142
  function updateValue() {
143
- if (!inputs.disabled) {
143
+ if (!inputs.isDisabled) {
144
144
  dispatch(new events.valueChange(!inputs.value));
145
145
  }
146
146
  }
@@ -158,7 +158,7 @@ export const ViraCheckbox = defineViraElement()({
158
158
  return html `
159
159
  <label
160
160
  class=${classMap({
161
- disabled: !!inputs.disabled,
161
+ disabled: !!inputs.isDisabled,
162
162
  })}
163
163
  ${attributes(inputs.attributePassthrough?.label)}
164
164
  style=${ifDefined(inputs.stylePassthrough?.label)}
@@ -168,14 +168,14 @@ export const ViraCheckbox = defineViraElement()({
168
168
  <span
169
169
  class="custom-checkbox ${classMap({
170
170
  checked: inputs.value,
171
- disabled: !!inputs.disabled,
171
+ disabled: !!inputs.isDisabled,
172
172
  error: !!inputs.hasError,
173
173
  })}"
174
174
  role="checkbox"
175
175
  aria-label=${ifDefined(inputs.label || undefined)}
176
176
  aria-checked=${inputs.value ? 'true' : 'false'}
177
- aria-disabled=${inputs.disabled ? 'true' : 'false'}
178
- tabindex=${inputs.disabled ? '-1' : '0'}
177
+ aria-disabled=${inputs.isDisabled ? 'true' : 'false'}
178
+ tabindex=${inputs.isDisabled ? '-1' : '0'}
179
179
  ${attributes(inputs.attributePassthrough?.['custom-checkbox'])}
180
180
  style=${ifDefined(inputs.stylePassthrough?.['custom-checkbox'])}
181
181
  ${listenToActivate(updateValue)}
@@ -29,6 +29,21 @@ export declare const ViraForm: import("element-vir").DeclarativeElementDefinitio
29
29
  * @default false
30
30
  */
31
31
  horizontalCheckboxes: boolean;
32
+ /**
33
+ * When `true`, all form field labels render to the left of their inputs instead of above
34
+ * them.
35
+ *
36
+ * @default false
37
+ */
38
+ useHorizontalLabels: boolean;
39
+ /**
40
+ * When `true`, all fields in this form are prevented from user edits. Inputs, selects, and
41
+ * text areas render their current value as plain text; checkboxes render disabled since
42
+ * they have no native readonly mode.
43
+ *
44
+ * @default false
45
+ */
46
+ isReadonly: boolean;
32
47
  }>, {
33
48
  lastIsValid: boolean;
34
49
  }, {
@@ -1,5 +1,6 @@
1
1
  import { getObjectTypedEntries } from '@augment-vir/common';
2
- import { css, defineElementEvent, html, listen, nothing, testId } from 'element-vir';
2
+ import { css, defineElementEvent, html, listen, nothing, testId, } from 'element-vir';
3
+ import { viraFormCssVars } from '../styles/form-styles.js';
3
4
  import { defineViraElement } from '../util/define-vira-element.js';
4
5
  import { applyRequiredLabel, areFormFieldsValid, ViraFormFieldType, } from '../util/vira-form-fields.js';
5
6
  import { ViraCheckbox } from './vira-checkbox.element.js';
@@ -14,6 +15,11 @@ import { ViraTextArea } from './vira-text-area.element.js';
14
15
  */
15
16
  export const ViraForm = defineViraElement()({
16
17
  tagName: 'vira-form',
18
+ state() {
19
+ return {
20
+ lastIsValid: false,
21
+ };
22
+ },
17
23
  events: {
18
24
  valueChange: defineElementEvent(),
19
25
  validChange: defineElementEvent(),
@@ -34,12 +40,35 @@ export const ViraForm = defineViraElement()({
34
40
  width: unset;
35
41
  }
36
42
  }
43
+
44
+ .horizontal-fields {
45
+ width: 100%;
46
+ border-collapse: separate;
47
+ border-spacing: 0 10px;
48
+
49
+ & th,
50
+ & td {
51
+ padding: 0;
52
+ }
53
+
54
+ & th {
55
+ padding: 0 8px;
56
+ vertical-align: middle;
57
+ white-space: nowrap;
58
+ font-weight: ${viraFormCssVars['vira-form-label-font-weight'].value};
59
+ text-align: right;
60
+ }
61
+
62
+ & td {
63
+ width: 100%;
64
+ vertical-align: top;
65
+
66
+ & > ${ViraCheckbox}, & > ${ViraInput}, & > ${ViraSelect}, & > ${ViraTextArea} {
67
+ width: 100%;
68
+ }
69
+ }
70
+ }
37
71
  `,
38
- state() {
39
- return {
40
- lastIsValid: false,
41
- };
42
- },
43
72
  render({ inputs, dispatch, events, state, updateState }) {
44
73
  const currentIsValid = areFormFieldsValid(inputs.fields);
45
74
  if (currentIsValid !== state.lastIsValid) {
@@ -50,168 +79,236 @@ export const ViraForm = defineViraElement()({
50
79
  allFieldsAreValid: currentIsValid,
51
80
  }));
52
81
  }
82
+ function wrapFormField({ fieldTemplate, label, }) {
83
+ if (inputs.useHorizontalLabels) {
84
+ return html `
85
+ <tr>
86
+ <th scope="row">${label}</th>
87
+ <td>${fieldTemplate}</td>
88
+ </tr>
89
+ `;
90
+ }
91
+ else {
92
+ return fieldTemplate;
93
+ }
94
+ }
53
95
  const formFieldTemplates = getObjectTypedEntries(inputs.fields).map(([key, field,]) => {
96
+ const label = applyRequiredLabel(field.label, !!field.isRequired && !inputs.hideRequiredMarkers);
97
+ const isDisabled = !!(inputs.isDisabled || field.isDisabled);
98
+ const childLabel = inputs.useHorizontalLabels ? undefined : label;
99
+ const horizontalLabelAttributes = inputs.useHorizontalLabels && label
100
+ ? {
101
+ 'aria-label': label,
102
+ }
103
+ : {};
54
104
  if (field.isHidden) {
55
105
  return nothing;
56
106
  }
57
107
  else if (field.type === ViraFormFieldType.Checkbox) {
58
- return html `
59
- <${ViraCheckbox.assign({
60
- value: field.value || false,
61
- disabled: inputs.isDisabled || field.isDisabled,
62
- hasError: field.hasError,
63
- horizontal: inputs.horizontalCheckboxes,
64
- label: applyRequiredLabel(field.label, !!field.isRequired && !inputs.hideRequiredMarkers),
65
- })}
66
- ${field.testId ? testId(field.testId) : nothing}
67
- ${listen(ViraCheckbox.events.valueChange, (event) => {
68
- dispatch(new events.valueChange({
69
- key,
70
- ...field,
71
- value: event.detail,
72
- }));
73
- })}
74
- ></${ViraCheckbox}>
75
- `;
108
+ return wrapFormField({
109
+ label,
110
+ fieldTemplate: html `
111
+ <${ViraCheckbox.assign({
112
+ value: field.value || false,
113
+ isDisabled: !!(isDisabled || inputs.isReadonly),
114
+ hasError: field.hasError,
115
+ useHorizontalLabel: inputs.horizontalCheckboxes,
116
+ fillWhenChecked: field.fillWhenChecked,
117
+ fillWhenUnchecked: field.fillWhenUnchecked,
118
+ label: childLabel,
119
+ ...(inputs.useHorizontalLabels && label
120
+ ? {
121
+ attributePassthrough: {
122
+ 'custom-checkbox': horizontalLabelAttributes,
123
+ },
124
+ }
125
+ : {}),
126
+ })}
127
+ ${field.testId ? testId(field.testId) : nothing}
128
+ ${listen(ViraCheckbox.events.valueChange, (event) => {
129
+ dispatch(new events.valueChange({
130
+ key,
131
+ ...field,
132
+ value: event.detail,
133
+ }));
134
+ })}
135
+ ></${ViraCheckbox}>
136
+ `,
137
+ });
76
138
  }
77
139
  else if (field.type === ViraFormFieldType.Select) {
78
- return html `
79
- <${ViraSelect.assign({
80
- options: field.options,
81
- value: field.value,
82
- placeholder: field.placeholder,
83
- disabled: inputs.isDisabled || field.isDisabled,
84
- label: applyRequiredLabel(field.label, !!field.isRequired && !inputs.hideRequiredMarkers),
85
- hasError: field.hasError,
86
- icon: field.icon,
87
- })}
88
- ${field.testId ? testId(field.testId) : nothing}
89
- ${listen(ViraSelect.events.valueChange, (event) => {
90
- dispatch(new events.valueChange({
91
- key,
92
- ...field,
93
- value: event.detail,
94
- }));
95
- })}
96
- ></${ViraSelect}>
97
- `;
140
+ return wrapFormField({
141
+ label,
142
+ fieldTemplate: html `
143
+ <${ViraSelect.assign({
144
+ options: field.options,
145
+ value: field.value,
146
+ placeholder: field.placeholder,
147
+ disabled: isDisabled,
148
+ isReadonly: inputs.isReadonly,
149
+ label: childLabel,
150
+ hasError: field.hasError,
151
+ icon: field.icon,
152
+ ...(inputs.useHorizontalLabels && label
153
+ ? {
154
+ attributePassthrough: {
155
+ select: horizontalLabelAttributes,
156
+ },
157
+ }
158
+ : {}),
159
+ })}
160
+ ${field.testId ? testId(field.testId) : nothing}
161
+ ${listen(ViraSelect.events.valueChange, (event) => {
162
+ dispatch(new events.valueChange({
163
+ key,
164
+ ...field,
165
+ value: event.detail,
166
+ }));
167
+ })}
168
+ ></${ViraSelect}>
169
+ `,
170
+ });
98
171
  }
99
172
  else if (field.type === ViraFormFieldType.TextArea) {
100
- return html `
101
- <${ViraTextArea.assign({
102
- value: field.value || '',
103
- disabled: inputs.isDisabled || field.isDisabled,
104
- hasError: field.hasError,
105
- label: applyRequiredLabel(field.label, !!field.isRequired && !inputs.hideRequiredMarkers),
106
- placeholder: field.placeholder,
107
- rows: field.rows,
108
- preventResize: field.preventResize,
109
- })}
110
- ${field.testId ? testId(field.testId) : nothing}
111
- ${listen(ViraTextArea.events.valueChange, (event) => {
112
- dispatch(new events.valueChange({
113
- key,
114
- ...field,
115
- value: event.detail,
116
- }));
117
- })}
118
- ></${ViraTextArea}>
119
- `;
173
+ return wrapFormField({
174
+ label,
175
+ fieldTemplate: html `
176
+ <${ViraTextArea.assign({
177
+ value: field.value || '',
178
+ disabled: isDisabled,
179
+ hasError: field.hasError,
180
+ isReadonly: inputs.isReadonly,
181
+ label: childLabel,
182
+ placeholder: field.placeholder,
183
+ rows: field.rows,
184
+ preventResize: field.preventResize,
185
+ attributePassthrough: horizontalLabelAttributes,
186
+ })}
187
+ ${field.testId ? testId(field.testId) : nothing}
188
+ ${listen(ViraTextArea.events.valueChange, (event) => {
189
+ dispatch(new events.valueChange({
190
+ key,
191
+ ...field,
192
+ value: event.detail,
193
+ }));
194
+ })}
195
+ ></${ViraTextArea}>
196
+ `,
197
+ });
120
198
  }
121
199
  else if (field.type === ViraFormFieldType.Number) {
122
- return html `
123
- <${ViraInput.assign({
124
- value: field.value?.toString() || '',
125
- disabled: inputs.isDisabled || field.isDisabled,
126
- allowedInputs: /\d/,
127
- hasError: field.hasError,
128
- icon: field.icon,
129
- label: applyRequiredLabel(field.label, !!field.isRequired && !inputs.hideRequiredMarkers),
130
- placeholder: field.placeholder,
131
- showClearButton: inputs.showClearButtons,
132
- type: ViraInputType.Number,
133
- attributePassthrough: {
134
- ...(field.min === undefined
135
- ? {}
136
- : {
137
- min: String(field.min),
138
- }),
139
- ...(field.max === undefined
140
- ? {}
141
- : {
142
- max: String(field.max),
143
- }),
144
- ...(field.step === undefined
145
- ? {}
146
- : {
147
- step: String(field.step),
148
- }),
149
- },
150
- })}
151
- ${field.testId ? testId(field.testId) : nothing}
152
- ${listen(ViraInput.events.valueChange, (event) => {
153
- const numericValue = event.detail === '' ? undefined : Number(event.detail);
154
- dispatch(new events.valueChange({
155
- key,
156
- ...field,
157
- value: numericValue,
158
- }));
159
- })}
160
- ></${ViraInput}>
161
- `;
200
+ return wrapFormField({
201
+ label,
202
+ fieldTemplate: html `
203
+ <${ViraInput.assign({
204
+ value: field.value?.toString() || '',
205
+ disabled: isDisabled,
206
+ allowedInputs: /\d/,
207
+ hasError: field.hasError,
208
+ icon: field.icon,
209
+ isReadonly: inputs.isReadonly,
210
+ label: childLabel,
211
+ placeholder: field.placeholder,
212
+ showClearButton: inputs.showClearButtons,
213
+ type: ViraInputType.Number,
214
+ attributePassthrough: {
215
+ ...horizontalLabelAttributes,
216
+ ...(field.min === undefined
217
+ ? {}
218
+ : {
219
+ min: String(field.min),
220
+ }),
221
+ ...(field.max === undefined
222
+ ? {}
223
+ : {
224
+ max: String(field.max),
225
+ }),
226
+ ...(field.step === undefined
227
+ ? {}
228
+ : {
229
+ step: String(field.step),
230
+ }),
231
+ },
232
+ })}
233
+ ${field.testId ? testId(field.testId) : nothing}
234
+ ${listen(ViraInput.events.valueChange, (event) => {
235
+ const numericValue = event.detail === '' ? undefined : Number(event.detail);
236
+ dispatch(new events.valueChange({
237
+ key,
238
+ ...field,
239
+ value: numericValue,
240
+ }));
241
+ })}
242
+ ></${ViraInput}>
243
+ `,
244
+ });
162
245
  }
163
246
  else {
164
- return html `
165
- <${ViraInput.assign({
166
- value: field.value || '',
167
- disabled: inputs.isDisabled || field.isDisabled,
168
- hasError: field.hasError,
169
- icon: field.icon,
170
- label: applyRequiredLabel(field.label, !!field.isRequired && !inputs.hideRequiredMarkers),
171
- placeholder: field.placeholder,
172
- showClearButton: inputs.showClearButtons,
173
- attributePassthrough: field.isUsername
174
- ? {
175
- autocomplete: 'username',
176
- }
177
- : field.type === ViraFormFieldType.NewPassword
178
- ? {
179
- autocomplete: 'new-password',
180
- }
181
- : field.type === ViraFormFieldType.ExistingPassword
247
+ return wrapFormField({
248
+ label,
249
+ fieldTemplate: html `
250
+ <${ViraInput.assign({
251
+ value: field.value || '',
252
+ disabled: isDisabled,
253
+ hasError: field.hasError,
254
+ icon: field.icon,
255
+ isReadonly: inputs.isReadonly,
256
+ label: childLabel,
257
+ placeholder: field.placeholder,
258
+ showClearButton: inputs.showClearButtons,
259
+ attributePassthrough: {
260
+ ...horizontalLabelAttributes,
261
+ ...(field.isUsername
182
262
  ? {
183
- autocomplete: 'password',
263
+ autocomplete: 'username',
184
264
  }
185
- : field.type === ViraFormFieldType.Email
265
+ : field.type === ViraFormFieldType.NewPassword
186
266
  ? {
187
- autocomplete: 'email',
267
+ autocomplete: 'new-password',
188
268
  }
189
- : {},
190
- type: [
191
- ViraFormFieldType.NewPassword,
192
- ViraFormFieldType.ExistingPassword,
193
- ViraFormFieldType.PlainPassword,
194
- ].includes(field.type)
195
- ? ViraInputType.Password
196
- : field.type === ViraFormFieldType.Email
197
- ? ViraInputType.Email
198
- : ViraInputType.Default,
199
- })}
200
- ${field.testId ? testId(field.testId) : nothing}
201
- ${listen(ViraInput.events.valueChange, (event) => {
202
- dispatch(new events.valueChange({
203
- key,
204
- ...field,
205
- value: event.detail,
206
- }));
207
- })}
208
- ></${ViraInput}>
209
- `;
269
+ : field.type === ViraFormFieldType.ExistingPassword
270
+ ? {
271
+ autocomplete: 'password',
272
+ }
273
+ : field.type === ViraFormFieldType.Email
274
+ ? {
275
+ autocomplete: 'email',
276
+ }
277
+ : {}),
278
+ },
279
+ type: [
280
+ ViraFormFieldType.NewPassword,
281
+ ViraFormFieldType.ExistingPassword,
282
+ ViraFormFieldType.PlainPassword,
283
+ ].includes(field.type)
284
+ ? ViraInputType.Password
285
+ : field.type === ViraFormFieldType.Email
286
+ ? ViraInputType.Email
287
+ : ViraInputType.Default,
288
+ })}
289
+ ${field.testId ? testId(field.testId) : nothing}
290
+ ${listen(ViraInput.events.valueChange, (event) => {
291
+ dispatch(new events.valueChange({
292
+ key,
293
+ ...field,
294
+ value: event.detail,
295
+ }));
296
+ })}
297
+ ></${ViraInput}>
298
+ `,
299
+ });
210
300
  }
211
301
  });
302
+ const formFieldsWrapper = inputs.useHorizontalLabels
303
+ ? html `
304
+ <table class="horizontal-fields">
305
+ <tbody>${formFieldTemplates}</tbody>
306
+ </table>
307
+ `
308
+ : formFieldTemplates;
212
309
  return html `
213
310
  <form ${listen('submit', (event) => event.preventDefault())}>
214
- ${formFieldTemplates}
311
+ ${formFieldsWrapper}
215
312
  <slot></slot>
216
313
  </form>
217
314
  `;
@@ -35,6 +35,7 @@ export declare const ViraInput: import("element-vir").DeclarativeElementDefiniti
35
35
  } & PartialWithUndefined<{
36
36
  placeholder: string;
37
37
  disabled: boolean;
38
+ isReadonly: boolean;
38
39
  allowedInputs: string | RegExp;
39
40
  blockedInputs: string | RegExp;
40
41
  disableBrowserHelps: boolean;
@@ -6,6 +6,7 @@ import { CloseX24Icon } from '../icons/icon-svgs/24/close-x-24.icon.js';
6
6
  import { EyeClosed24Icon, EyeOpen24Icon } from '../icons/index.js';
7
7
  import { createFocusStyles } from '../styles/focus.js';
8
8
  import { viraFormCssVars } from '../styles/form-styles.js';
9
+ import { ViraSize, viraSizeHeights } from '../styles/form-variants.js';
9
10
  import { noUserSelect, viraAnimationDurations, viraDisabledStyles } from '../styles/index.js';
10
11
  import { noNativeFormStyles } from '../styles/native-styles.js';
11
12
  import { defineViraElement } from '../util/define-vira-element.js';
@@ -36,7 +37,7 @@ export const ViraInput = defineViraElement()({
36
37
  tagName: 'vira-input',
37
38
  cssVars: {
38
39
  'vira-input-padding-horizontal': '10px',
39
- 'vira-input-padding-vertical': '6px',
40
+ 'vira-input-padding-vertical': '5px',
40
41
  },
41
42
  styles: ({ hostClasses, cssVars }) => {
42
43
  return css `
@@ -137,6 +138,7 @@ export const ViraInput = defineViraElement()({
137
138
  max-width: 100%;
138
139
  flex-grow: 1;
139
140
  display: inline-flex;
141
+ min-height: ${viraSizeHeights[ViraSize.Medium]}px;
140
142
  box-sizing: border-box;
141
143
  align-items: center;
142
144
  position: relative;
@@ -156,6 +158,10 @@ export const ViraInput = defineViraElement()({
156
158
  margin-right: calc(${cssVars['vira-input-padding-horizontal'].value} - 4px);
157
159
  }
158
160
 
161
+ .readonly-value {
162
+ overflow-wrap: anywhere;
163
+ }
164
+
159
165
  input {
160
166
  ${noNativeFormStyles};
161
167
  cursor: text;
@@ -293,6 +299,22 @@ export const ViraInput = defineViraElement()({
293
299
  allowed: inputs.allowedInputs,
294
300
  blocked: inputs.blockedInputs,
295
301
  });
302
+ if (inputs.isReadonly) {
303
+ const readonlyValueTemplate = html `
304
+ <span class="readonly-value">${filteredValue}</span>
305
+ `;
306
+ if (inputs.label) {
307
+ return html `
308
+ <label>
309
+ <span class="input-label">${inputs.label}</span>
310
+ ${readonlyValueTemplate}
311
+ </label>
312
+ `;
313
+ }
314
+ else {
315
+ return readonlyValueTemplate;
316
+ }
317
+ }
296
318
  const iconTemplate = inputs.icon
297
319
  ? html `
298
320
  <${ViraIcon.assign({
@@ -290,7 +290,7 @@ export const ViraJsonForm = defineViraElement()({
290
290
  return html `
291
291
  <${ViraCheckbox.assign({
292
292
  value: value === true,
293
- disabled: isDisabled,
293
+ isDisabled,
294
294
  })}
295
295
  ${listen(ViraCheckbox.events.valueChange, (event) => {
296
296
  emitReplaceAt(path, event.detail);
@@ -20,6 +20,11 @@ export declare const ViraSelect: import("element-vir").DeclarativeElementDefinit
20
20
  /** If set to `true`, only minimal styles are applied. */
21
21
  rawSelect: boolean;
22
22
  disabled: boolean;
23
+ /**
24
+ * When `true`, the currently selected option's label is rendered as plain text with no
25
+ * wrapper, border, or focus styles.
26
+ */
27
+ isReadonly: boolean;
23
28
  attributePassthrough: Readonly<PartialWithUndefined<{
24
29
  label: AttributeValues;
25
30
  select: AttributeValues;
@@ -185,6 +185,10 @@ export const ViraSelect = defineViraElement()({
185
185
  }
186
186
  }
187
187
 
188
+ .readonly-value {
189
+ overflow-wrap: anywhere;
190
+ }
191
+
188
192
  ${hostClasses['vira-select-disabled'].selector} {
189
193
  cursor: not-allowed;
190
194
 
@@ -252,6 +256,27 @@ export const ViraSelect = defineViraElement()({
252
256
  },
253
257
  render({ inputs, state, dispatch, events }) {
254
258
  const value = inputs.value || undefined;
259
+ if (inputs.isReadonly) {
260
+ const selectedOption = inputs.options
261
+ .flatMap((entry) => (isViraSelectOptionGroup(entry) ? [...entry.options] : [entry]))
262
+ .find((option) => option.value === value);
263
+ const readonlyValueTemplate = html `
264
+ <span class="readonly-value">
265
+ ${selectedOption?.label || inputs.placeholder || ''}
266
+ </span>
267
+ `;
268
+ if (inputs.label) {
269
+ return html `
270
+ <label ${attributes(inputs.attributePassthrough?.label)}>
271
+ <span class="select-label">${inputs.label}</span>
272
+ ${readonlyValueTemplate}
273
+ </label>
274
+ `;
275
+ }
276
+ else {
277
+ return readonlyValueTemplate;
278
+ }
279
+ }
255
280
  const placeholderOptionTemplate = inputs.placeholder || value == undefined
256
281
  ? html `
257
282
  <option value="" disabled ?selected=${value == undefined}>
@@ -21,6 +21,7 @@ export declare const ViraTextArea: import("element-vir").DeclarativeElementDefin
21
21
  } & PartialWithUndefined<{
22
22
  placeholder: string;
23
23
  disabled: boolean;
24
+ isReadonly: boolean;
24
25
  allowedInputs: string | RegExp;
25
26
  blockedInputs: string | RegExp;
26
27
  disableBrowserHelps: boolean;
@@ -97,6 +97,11 @@ export const ViraTextArea = defineViraElement()({
97
97
  pointer-events: none;
98
98
  }
99
99
 
100
+ .readonly-value {
101
+ white-space: pre-wrap;
102
+ overflow-wrap: anywhere;
103
+ }
104
+
100
105
  ${hostClasses['vira-text-area-prevent-resize'].selector} textarea {
101
106
  resize: none;
102
107
  }
@@ -154,6 +159,22 @@ export const ViraTextArea = defineViraElement()({
154
159
  allowed: inputs.allowedInputs,
155
160
  blocked: inputs.blockedInputs,
156
161
  });
162
+ if (inputs.isReadonly) {
163
+ const readonlyValueTemplate = html `
164
+ <span class="readonly-value">${filteredValue}</span>
165
+ `;
166
+ if (inputs.label) {
167
+ return html `
168
+ <label>
169
+ <span class="text-area-label">${inputs.label}</span>
170
+ ${readonlyValueTemplate}
171
+ </label>
172
+ `;
173
+ }
174
+ else {
175
+ return readonlyValueTemplate;
176
+ }
177
+ }
157
178
  const textAreaTemplate = html `
158
179
  <span class="text-area-wrapper">
159
180
  <textarea
@@ -12,6 +12,7 @@ export type SharedTextInputElementInputs = {
12
12
  placeholder: string;
13
13
  /** Set to true to trigger disabled styles and to block all user input. */
14
14
  disabled: boolean;
15
+ isReadonly: boolean;
15
16
  /**
16
17
  * Only letters in the given string or matches to the given RegExp will be allowed.
17
18
  * blockedInputs takes precedence over this input.
@@ -77,7 +77,12 @@ export type ViraFormField = ({
77
77
  }> & CommonViraFormFields) | ({
78
78
  type: ViraFormFieldType.Checkbox;
79
79
  value: boolean | undefined;
80
- } & CommonViraFormFields) | ({
80
+ } & PartialWithUndefined<{
81
+ /** The checkbox will be filled with a form selection color when it is checked. */
82
+ fillWhenChecked: boolean;
83
+ /** The checkbox will be filled with a form error color when it is unchecked. */
84
+ fillWhenUnchecked: boolean;
85
+ }> & CommonViraFormFields) | ({
81
86
  type: ViraFormFieldType.Number;
82
87
  value: number | undefined;
83
88
  } & PartialWithUndefined<{
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vira",
3
- "version": "31.20.0",
3
+ "version": "31.21.1",
4
4
  "description": "A simple and highly versatile design system using element-vir.",
5
5
  "keywords": [
6
6
  "design",