vira 31.22.2 → 31.24.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.
@@ -18,6 +18,10 @@ export type ViraCheckboxInputs = {
18
18
  stylePassthrough: Partial<Record<ViraCheckboxInnerElements, CSSResult>>;
19
19
  attributePassthrough: Partial<Record<ViraCheckboxInnerElements, AttributeValues>>;
20
20
  isDisabled: boolean;
21
+ /**
22
+ * Text label for the checkbox. Used as the fallback when the `vira-checkbox-label` slot is not
23
+ * filled.
24
+ */
21
25
  label: string;
22
26
  hasError: boolean;
23
27
  useHorizontalLabel: boolean;
@@ -33,6 +37,8 @@ export type ViraCheckboxInputs = {
33
37
  * @category Elements
34
38
  * @see https://electrovir.github.io/vira/book/elements/vira-checkbox
35
39
  */
36
- export declare const ViraCheckbox: import("element-vir").DeclarativeElementDefinition<"vira-checkbox", Readonly<ViraCheckboxInputs>, {}, {
40
+ export declare const ViraCheckbox: import("element-vir").DeclarativeElementDefinition<`vira-${string}`, Readonly<ViraCheckboxInputs>, {
41
+ hasSlottedLabel: boolean;
42
+ }, {
37
43
  valueChange: import("element-vir").DefineEvent<boolean>;
38
- }, "vira-checkbox-horizontal" | "vira-checkbox-filled-checked" | "vira-checkbox-filled-unchecked", "vira-checkbox-", readonly [], readonly []>;
44
+ }, "vira-checkbox-horizontal" | "vira-checkbox-filled-checked" | "vira-checkbox-filled-unchecked", `vira-${string}-`, readonly ["vira-checkbox-label"], readonly []>;
@@ -1,4 +1,5 @@
1
- import { attributes, classMap, css, defineElementEvent, html, ifDefined, listen, listenToActivate, nothing, } from 'element-vir';
1
+ import { extractEventTarget } from '@augment-vir/web';
2
+ import { attributes, classMap, css, defineElementEvent, html, ifDefined, listen, listenToActivate, } from 'element-vir';
2
3
  import { Check24Icon, viraIconCssVars } from '../icons/index.js';
3
4
  import { viraDisabledStyles } from '../styles/disabled.js';
4
5
  import { createFocusStyles } from '../styles/focus.js';
@@ -14,6 +15,14 @@ import { ViraIcon } from './vira-icon.element.js';
14
15
  */
15
16
  export const ViraCheckbox = defineViraElement()({
16
17
  tagName: 'vira-checkbox',
18
+ slotNames: [
19
+ 'vira-checkbox-label',
20
+ ],
21
+ state() {
22
+ return {
23
+ hasSlottedLabel: false,
24
+ };
25
+ },
17
26
  hostClasses: {
18
27
  'vira-checkbox-horizontal': ({ inputs }) => !!inputs.useHorizontalLabel,
19
28
  'vira-checkbox-filled-checked': ({ inputs }) => !!inputs.fillWhenChecked,
@@ -86,6 +95,10 @@ export const ViraCheckbox = defineViraElement()({
86
95
  & .label-text {
87
96
  cursor: pointer;
88
97
  font-weight: ${viraFormCssVars['vira-form-label-font-weight'].value};
98
+
99
+ &.empty {
100
+ display: none;
101
+ }
89
102
  }
90
103
 
91
104
  &:not(.disabled):hover .custom-checkbox {
@@ -126,7 +139,7 @@ export const ViraCheckbox = defineViraElement()({
126
139
  }
127
140
 
128
141
  ${hostClasses['vira-checkbox-horizontal'].selector} label {
129
- flex-direction: row;
142
+ flex-direction: row-reverse;
130
143
  align-items: center;
131
144
  gap: 8px;
132
145
 
@@ -138,23 +151,34 @@ export const ViraCheckbox = defineViraElement()({
138
151
  events: {
139
152
  valueChange: defineElementEvent(),
140
153
  },
141
- render({ inputs, dispatch, events }) {
154
+ render({ inputs, dispatch, events, slotNames, state, updateState }) {
142
155
  function updateValue() {
143
156
  if (!inputs.isDisabled) {
144
157
  dispatch(new events.valueChange(!inputs.value));
145
158
  }
146
159
  }
147
- const textLabel = inputs.label
148
- ? html `
149
- <span
150
- class="label-text"
151
- ${attributes(inputs.attributePassthrough?.['text'])}
152
- style=${ifDefined(inputs.stylePassthrough?.['text'])}
153
- >
154
- ${inputs.label}
155
- </span>
156
- `
157
- : nothing;
160
+ const hasLabel = !!inputs.label || state.hasSlottedLabel;
161
+ const textLabel = html `
162
+ <span
163
+ class="label-text ${classMap({
164
+ empty: !hasLabel,
165
+ })}"
166
+ ${attributes(inputs.attributePassthrough?.['text'])}
167
+ style=${ifDefined(inputs.stylePassthrough?.['text'])}
168
+ >
169
+ <slot
170
+ name=${slotNames['vira-checkbox-label']}
171
+ ${listen('slotchange', (event) => {
172
+ const slotElement = extractEventTarget(event, HTMLSlotElement);
173
+ updateState({
174
+ hasSlottedLabel: !!slotElement.assignedNodes().length,
175
+ });
176
+ })}
177
+ >
178
+ ${inputs.label}
179
+ </slot>
180
+ </span>
181
+ `;
158
182
  return html `
159
183
  <label
160
184
  class=${classMap({
@@ -37,6 +37,13 @@ export declare const ViraDropdown: import("element-vir").DeclarativeElementDefin
37
37
  */
38
38
  randomId: string;
39
39
  }, {
40
+ /**
41
+ * @deprecated Use `selectedValuesChange` instead. `selectedChange` is broken for
42
+ * multi-select (it doesn't emit all the currently selected values) but is temporarily
43
+ * left for backwards compatibility purposes. It will be removed entirely soon.
44
+ */
40
45
  selectedChange: import("element-vir").DefineEvent<string[]>;
46
+ /** Emits all currently selected values. */
47
+ selectedValuesChange: import("element-vir").DefineEvent<string[]>;
41
48
  openChange: import("element-vir").DefineEvent<ShowPopUpResult | undefined>;
42
49
  }, "vira-dropdown-", "vira-dropdown-", readonly [], readonly ["leadingIcon", "prefixText", "trigger"]>;
@@ -110,7 +110,14 @@ export const ViraDropdown = defineViraElement()({
110
110
  }
111
111
  `,
112
112
  events: {
113
+ /**
114
+ * @deprecated Use `selectedValuesChange` instead. `selectedChange` is broken for
115
+ * multi-select (it doesn't emit all the currently selected values) but is temporarily
116
+ * left for backwards compatibility purposes. It will be removed entirely soon.
117
+ */
113
118
  selectedChange: defineElementEvent(),
119
+ /** Emits all currently selected values. */
120
+ selectedValuesChange: defineElementEvent(),
114
121
  openChange: defineElementEvent(),
115
122
  },
116
123
  state() {
@@ -160,7 +167,17 @@ export const ViraDropdown = defineViraElement()({
160
167
  return {
161
168
  content: option.label,
162
169
  onClick() {
170
+ const newSelectedValues = inputs.isMultiSelect
171
+ ? selectedOptions.includes(option)
172
+ ? filterMap(selectedOptions, (selectedOption) => selectedOption.value, (value, selectedOption) => selectedOption !== option)
173
+ : [
174
+ ...selectedOptions.map((selectedOption) => selectedOption.value),
175
+ option.value,
176
+ ]
177
+ : [option.value];
178
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
163
179
  dispatch(new events.selectedChange([option.value]));
180
+ dispatch(new events.selectedValuesChange(newSelectedValues));
164
181
  },
165
182
  disabled: option.disabled,
166
183
  selected: selectedOptions.includes(option),
@@ -171,6 +188,7 @@ export const ViraDropdown = defineViraElement()({
171
188
  const triggerTemplate = html `
172
189
  <${ViraPopUpTrigger.assign({
173
190
  ...inputs,
191
+ keepOpenAfterInteraction: inputs.isMultiSelect,
174
192
  focusOnClose: true,
175
193
  popUpOffset: {
176
194
  vertical: -1,
@@ -103,20 +103,19 @@ export const ViraForm = defineViraElement()({
103
103
  }
104
104
  }
105
105
  const formFieldTemplates = getObjectTypedEntries(inputs.fields).map(([key, field,]) => {
106
- const label = applyRequiredLabel(field.label, !!field.isRequired && !inputs.hideRequiredMarkers);
107
106
  const isDisabled = !!(inputs.isDisabled || field.isDisabled);
108
- const childLabel = inputs.useHorizontalLabels ? undefined : label;
109
- const horizontalLabelAttributes = inputs.useHorizontalLabels && label
110
- ? {
111
- 'aria-label': label,
112
- }
113
- : {};
107
+ const showRequiredMarker = !!field.isRequired && !inputs.hideRequiredMarkers;
114
108
  if (field.isHidden) {
115
109
  return nothing;
116
110
  }
117
111
  else if (field.type === ViraFormFieldType.Checkbox) {
112
+ const checkboxLabel = showRequiredMarker
113
+ ? html `
114
+ ${field.label}*
115
+ `
116
+ : field.label;
118
117
  return wrapFormField({
119
- label,
118
+ label: checkboxLabel,
120
119
  fieldTemplate: html `
121
120
  <${ViraCheckbox.assign({
122
121
  value: field.value || false,
@@ -125,14 +124,6 @@ export const ViraForm = defineViraElement()({
125
124
  useHorizontalLabel: inputs.horizontalCheckboxes,
126
125
  fillWhenChecked: field.fillWhenChecked,
127
126
  fillWhenUnchecked: field.fillWhenUnchecked,
128
- label: childLabel,
129
- ...(inputs.useHorizontalLabels && label
130
- ? {
131
- attributePassthrough: {
132
- 'custom-checkbox': horizontalLabelAttributes,
133
- },
134
- }
135
- : {}),
136
127
  })}
137
128
  ${field.testId ? testId(field.testId) : nothing}
138
129
  ${listen(ViraCheckbox.events.valueChange, (event) => {
@@ -142,11 +133,28 @@ export const ViraForm = defineViraElement()({
142
133
  value: event.detail,
143
134
  }));
144
135
  })}
145
- ></${ViraCheckbox}>
136
+ >
137
+ ${inputs.useHorizontalLabels
138
+ ? nothing
139
+ : html `
140
+ <span
141
+ slot=${ViraCheckbox.slotNames['vira-checkbox-label']}
142
+ >
143
+ ${checkboxLabel}
144
+ </span>
145
+ `}
146
+ </${ViraCheckbox}>
146
147
  `,
147
148
  });
148
149
  }
149
- else if (field.type === ViraFormFieldType.Select) {
150
+ const label = applyRequiredLabel(field.label, showRequiredMarker);
151
+ const childLabel = inputs.useHorizontalLabels ? undefined : label;
152
+ const horizontalLabelAttributes = inputs.useHorizontalLabels && label
153
+ ? {
154
+ 'aria-label': label,
155
+ }
156
+ : {};
157
+ if (field.type === ViraFormFieldType.Select) {
150
158
  return wrapFormField({
151
159
  label,
152
160
  fieldTemplate: html `
@@ -1,5 +1,6 @@
1
1
  import { type PartialWithUndefined } from '@augment-vir/common';
2
2
  import { type FullDate } from 'date-vir';
3
+ import { type HtmlInterpolation } from 'element-vir';
3
4
  import { type ViraIconSvg } from '../icons/icon-svg.js';
4
5
  import { type ViraSelectOption } from './vira-select-option.js';
5
6
  /**
@@ -79,12 +80,17 @@ export type ViraFormField = ({
79
80
  }> & CommonViraFormFields) | ({
80
81
  type: ViraFormFieldType.Checkbox;
81
82
  value: boolean | undefined;
83
+ /**
84
+ * Label for the checkbox. Unlike other field types, this accepts arbitrary HTML so the
85
+ * checkbox's label slot can be filled with rich content.
86
+ */
87
+ label: HtmlInterpolation;
82
88
  } & PartialWithUndefined<{
83
89
  /** The checkbox will be filled with a form selection color when it is checked. */
84
90
  fillWhenChecked: boolean;
85
91
  /** The checkbox will be filled with a form error color when it is unchecked. */
86
92
  fillWhenUnchecked: boolean;
87
- }> & CommonViraFormFields) | ({
93
+ }> & Omit<CommonViraFormFields, 'label'>) | ({
88
94
  type: ViraFormFieldType.Number;
89
95
  value: number | undefined;
90
96
  } & PartialWithUndefined<{
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vira",
3
- "version": "31.22.2",
3
+ "version": "31.24.0",
4
4
  "description": "A simple and highly versatile design system using element-vir.",
5
5
  "keywords": [
6
6
  "design",