vira 25.7.2 → 25.8.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.
Files changed (29) hide show
  1. package/dist/elements/index.d.ts +8 -3
  2. package/dist/elements/index.js +8 -3
  3. package/dist/elements/pop-up/pop-up-helpers.d.ts +30 -0
  4. package/dist/elements/pop-up/pop-up-helpers.js +58 -0
  5. package/dist/elements/pop-up/pop-up-menu-item.d.ts +24 -0
  6. package/dist/elements/pop-up/pop-up-menu-item.js +1 -0
  7. package/dist/elements/pop-up/vira-menu-item.element.d.ts +15 -0
  8. package/dist/elements/{dropdown/vira-dropdown-item.element.js → pop-up/vira-menu-item.element.js} +9 -22
  9. package/dist/elements/pop-up/vira-menu-trigger.element.d.ts +37 -0
  10. package/dist/elements/pop-up/vira-menu-trigger.element.js +107 -0
  11. package/dist/elements/pop-up/vira-menu.element.d.ts +36 -0
  12. package/dist/elements/pop-up/vira-menu.element.js +129 -0
  13. package/dist/elements/pop-up/vira-pop-up-menu.element.d.ts +10 -0
  14. package/dist/elements/pop-up/vira-pop-up-menu.element.js +48 -0
  15. package/dist/elements/pop-up/vira-pop-up-trigger.element.d.ts +44 -0
  16. package/dist/elements/pop-up/vira-pop-up-trigger.element.js +235 -0
  17. package/dist/elements/vira-bold-text.element.js +3 -2
  18. package/dist/elements/{dropdown/vira-dropdown.element.d.ts → vira-dropdown.element.d.ts} +12 -9
  19. package/dist/elements/vira-dropdown.element.js +185 -0
  20. package/dist/styles/focus.d.ts +0 -1
  21. package/dist/util/pop-up-manager.d.ts +1 -1
  22. package/dist/util/pop-up-manager.js +20 -9
  23. package/package.json +11 -10
  24. package/dist/elements/dropdown/dropdown-helpers.d.ts +0 -45
  25. package/dist/elements/dropdown/dropdown-helpers.js +0 -78
  26. package/dist/elements/dropdown/vira-dropdown-item.element.d.ts +0 -27
  27. package/dist/elements/dropdown/vira-dropdown-options.element.d.ts +0 -29
  28. package/dist/elements/dropdown/vira-dropdown-options.element.js +0 -116
  29. package/dist/elements/dropdown/vira-dropdown.element.js +0 -324
@@ -1,78 +0,0 @@
1
- import { joinWithFinalConjunction } from '@augment-vir/common';
2
- /**
3
- * Filters an array of {@link ViraDropdownOption} based on the given selection.
4
- *
5
- * @category Internal
6
- */
7
- export function filterToSelectedOptions({ selected, options, isMultiSelect, }) {
8
- if (selected.length && options.length) {
9
- const selectedOptions = options.filter((option) => selected.includes(option.id));
10
- if (selectedOptions.length > 1 && !isMultiSelect) {
11
- console.error(`vira-dropdown has multiple selections but \`isMultiSelect\` is not \`true\`. Truncating to the first selection.`);
12
- return selectedOptions.slice(0, 1);
13
- }
14
- else {
15
- return selectedOptions;
16
- }
17
- }
18
- else {
19
- return [];
20
- }
21
- }
22
- /**
23
- * Verifies that all options have unique ids.
24
- *
25
- * @category Internal
26
- */
27
- export function assertUniqueIdProps(options) {
28
- const usedIds = new Set();
29
- const duplicateIds = [];
30
- options.forEach((option) => {
31
- if (usedIds.has(option.id)) {
32
- duplicateIds.push(option.id);
33
- }
34
- else {
35
- usedIds.add(option.id);
36
- }
37
- });
38
- if (duplicateIds.length) {
39
- throw new Error(`Duplicate option ids were given to ViraDropdown: ${joinWithFinalConjunction(duplicateIds)}`);
40
- }
41
- }
42
- /**
43
- * Creates a new array of selections based on the current selection and new selection id. This
44
- * behaves differently when multi select is enabled, hence this function.
45
- *
46
- * @category Internal
47
- */
48
- export function createNewSelection(
49
- /** The id of the option that should be newly selected. */
50
- id, currentSelection, isMultiSelect) {
51
- if (isMultiSelect) {
52
- return currentSelection.includes(id)
53
- ? currentSelection.filter((entry) => entry !== id)
54
- : [
55
- ...currentSelection,
56
- id,
57
- ];
58
- }
59
- else {
60
- return [id];
61
- }
62
- }
63
- /**
64
- * Handles toggles pop up state for `ViraDropdown`.
65
- *
66
- * @category Internal
67
- */
68
- export function triggerPopUpState({ open, emitEvent }, { updateState, popUpManager, dispatch, host, }) {
69
- if (open) {
70
- updateState({ showPopUpResult: popUpManager.showPopUp(host) });
71
- }
72
- else {
73
- popUpManager.removePopUp();
74
- }
75
- if (emitEvent) {
76
- dispatch(open);
77
- }
78
- }
@@ -1,27 +0,0 @@
1
- import { type PartialWithUndefined } from '@augment-vir/common';
2
- import { type HTMLTemplateResult } from 'element-vir';
3
- /**
4
- * An individual option for ViraDropdown.
5
- *
6
- * @category Dropdown
7
- */
8
- export type ViraDropdownOption = {
9
- /** Each `id` must be unique across all options. */
10
- id: PropertyKey;
11
- label: string;
12
- } & PartialWithUndefined<{
13
- disabled: boolean;
14
- hoverText: string;
15
- /** An optional custom template for this option. */
16
- template: HTMLTemplateResult;
17
- }>;
18
- /**
19
- * An element for an individual item in the ViraDropdown menu.
20
- *
21
- * @category Dropdown
22
- * @category Elements
23
- */
24
- export declare const ViraDropdownItem: import("element-vir").DeclarativeElementDefinition<"vira-dropdown-item", {
25
- label: string;
26
- selected: boolean;
27
- }, {}, {}, "vira-dropdown-item-selected", "vira-dropdown-item-", readonly []>;
@@ -1,29 +0,0 @@
1
- import { type NavController } from 'device-navigation';
2
- import { type ViraDropdownOption } from './vira-dropdown-item.element.js';
3
- /**
4
- * Test ids for {@link ViraDropdownOptions}.
5
- *
6
- * @category Internal
7
- */
8
- export declare const viraDropdownOptionsTestIds: {
9
- option: string;
10
- };
11
- /**
12
- * The dropdown menu portion of `ViraDropdown`.
13
- *
14
- * @category Dropdown
15
- * @category Elements
16
- */
17
- export declare const ViraDropdownOptions: import("element-vir").DeclarativeElementDefinition<"vira-dropdown-options", Readonly<{
18
- navController: NavController;
19
- /** All dropdown options to show to the user. */
20
- options: ReadonlyArray<Readonly<ViraDropdownOption>>;
21
- /**
22
- * The currently selected dropdown options. Note that this must be a reference subset of the
23
- * options input. Meaning, entries in this array must be the exact same objects (by
24
- * reference) as entries in the `options` input array for them to be marked as selected.
25
- */
26
- selectedOptions: ReadonlyArray<Readonly<ViraDropdownOption>>;
27
- }>, {}, {
28
- selectionChange: import("element-vir").DefineEvent<Readonly<ViraDropdownOption>>;
29
- }, "vira-dropdown-options-", "vira-dropdown-options-", readonly []>;
@@ -1,116 +0,0 @@
1
- import { nav, navAttribute, NavValue } from 'device-navigation';
2
- import { classMap, css, defineElementEvent, html, ifDefined, listen, testId } from 'element-vir';
3
- import { viraBorders } from '../../styles/border.js';
4
- import { viraFormCssVars } from '../../styles/form-themes.js';
5
- import { viraDisabledStyles } from '../../styles/index.js';
6
- import { viraShadows } from '../../styles/shadows.js';
7
- import { defineViraElement } from '../define-vira-element.js';
8
- import { ViraDropdownItem } from './vira-dropdown-item.element.js';
9
- /**
10
- * Test ids for {@link ViraDropdownOptions}.
11
- *
12
- * @category Internal
13
- */
14
- export const viraDropdownOptionsTestIds = {
15
- option: 'dropdown-option',
16
- };
17
- /**
18
- * The dropdown menu portion of `ViraDropdown`.
19
- *
20
- * @category Dropdown
21
- * @category Elements
22
- */
23
- export const ViraDropdownOptions = defineViraElement()({
24
- tagName: 'vira-dropdown-options',
25
- events: {
26
- selectionChange: defineElementEvent(),
27
- },
28
- styles: css `
29
- :host {
30
- display: flex;
31
- flex-direction: column;
32
-
33
- pointer-events: auto;
34
- width: 100%;
35
- max-height: 100%;
36
- overflow-y: auto;
37
- z-index: 99;
38
- border-radius: ${viraBorders['vira-form-input-radius'].value};
39
- border-top-left-radius: 0;
40
- border-top-right-radius: 0;
41
- background-color: ${viraFormCssVars['vira-form-background-color'].value};
42
- border: 1px solid ${viraFormCssVars['vira-form-border-color'].value};
43
- color: ${viraFormCssVars['vira-form-foreground-color'].value};
44
- ${viraShadows.menuShadow}
45
- }
46
-
47
- .dropdown-item {
48
- background-color: white;
49
- outline: none;
50
- }
51
-
52
- ${navAttribute.css({
53
- baseSelector: '.dropdown-item:not(.disabled):not(.selected)',
54
- navValue: NavValue.Focused,
55
- })} {
56
- background-color: ${viraFormCssVars['vira-form-selection-hover-background-color']
57
- .value};
58
- outline: none;
59
- }
60
-
61
- ${ViraDropdownItem} {
62
- pointer-events: none;
63
- }
64
-
65
- .dropdown-item.disabled {
66
- ${viraDisabledStyles};
67
- pointer-events: auto;
68
- }
69
- `,
70
- render({ inputs, dispatch, events }) {
71
- const optionTemplates = inputs.options.map((option) => {
72
- const selected = inputs.selectedOptions.includes(option);
73
- const innerTemplate = option.template ||
74
- html `
75
- <${ViraDropdownItem.assign({
76
- label: option.label,
77
- selected,
78
- })}></${ViraDropdownItem}>
79
- `;
80
- return html `
81
- <div
82
- class="dropdown-item ${classMap({
83
- disabled: !!option.disabled,
84
- selected,
85
- })}"
86
- ${testId(viraDropdownOptionsTestIds.option)}
87
- title=${ifDefined(option.hoverText || undefined)}
88
- role="option"
89
- ${nav(inputs.navController, { disabled: option.disabled || selected })}
90
- ${listen('mousedown', (event) => {
91
- /**
92
- * Prevent this mousedown event from propagating to the window, which would
93
- * then trigger the dropdown to close.
94
- */
95
- event.stopPropagation();
96
- })}
97
- ${listen('mouseup', (event) => {
98
- /**
99
- * Prevent this event from propagating to the window, which would then
100
- * trigger the dropdown to close.
101
- */
102
- event.stopPropagation();
103
- if (!option.disabled) {
104
- dispatch(new events.selectionChange(option));
105
- }
106
- })}
107
- >
108
- ${innerTemplate}
109
- </div>
110
- `;
111
- });
112
- return html `
113
- <slot>${optionTemplates}</slot>
114
- `;
115
- },
116
- });
@@ -1,324 +0,0 @@
1
- import { assert } from '@augment-vir/assert';
2
- import { NavController } from 'device-navigation';
3
- import { classMap, css, defineElementEvent, html, ifDefined, listen, nothing, renderIf, testId, } from 'element-vir';
4
- import { ChevronUp24Icon } from '../../icons/index.js';
5
- import { viraBorders } from '../../styles/border.js';
6
- import { createFocusStyles, viraFocusCssVars } from '../../styles/focus.js';
7
- import { viraFormCssVars } from '../../styles/form-themes.js';
8
- import { noNativeFormStyles, noUserSelect, viraAnimationDurations, viraDisabledStyles, } from '../../styles/index.js';
9
- import { viraShadows } from '../../styles/shadows.js';
10
- import { HidePopUpEvent, NavSelectEvent, PopUpManager, } from '../../util/pop-up-manager.js';
11
- import { defineViraElement } from '../define-vira-element.js';
12
- import { ViraIcon } from '../vira-icon.element.js';
13
- import { assertUniqueIdProps, createNewSelection, filterToSelectedOptions, triggerPopUpState, } from './dropdown-helpers.js';
14
- import { ViraDropdownOptions } from './vira-dropdown-options.element.js';
15
- /**
16
- * Test ids for {@link ViraDropdown}.
17
- *
18
- * @category Internal
19
- */
20
- export const viraDropdownTestIds = {
21
- trigger: 'dropdown-trigger',
22
- icon: 'dropdown-icon',
23
- prefix: 'dropdown-prefix',
24
- options: 'dropdown-options',
25
- };
26
- /**
27
- * The main dropdown element that should be use directly.
28
- *
29
- * @category Dropdown
30
- * @category Elements
31
- * @see https://electrovir.github.io/element-vir/vira/book/elements/dropdown/vira-dropdown
32
- */
33
- export const ViraDropdown = defineViraElement()({
34
- tagName: 'vira-dropdown',
35
- state({ host }) {
36
- return {
37
- /** `undefined` means the pop up is not currently showing. */
38
- showPopUpResult: undefined,
39
- popUpManager: new PopUpManager(new NavController(host)),
40
- };
41
- },
42
- hostClasses: {
43
- 'vira-dropdown-disabled': ({ inputs }) => !!inputs.isDisabled,
44
- },
45
- styles: ({ hostClasses }) => css `
46
- :host {
47
- display: inline-flex;
48
- vertical-align: middle;
49
- width: 256px;
50
- ${viraFocusCssVars['vira-focus-outline-color'].name}: ${viraFormCssVars['vira-form-focus-color'].value};
51
- position: relative;
52
- max-width: 100%;
53
- }
54
-
55
- .dropdown-wrapper {
56
- ${noNativeFormStyles};
57
- max-width: 100%;
58
- align-self: stretch;
59
- flex-grow: 1;
60
- position: relative;
61
- border-radius: ${viraBorders['vira-form-input-radius'].value};
62
- transition: border-radius
63
- ${viraAnimationDurations['vira-interaction-animation-duration'].value};
64
- outline: none;
65
- }
66
-
67
- ${createFocusStyles({
68
- selector: '.dropdown-wrapper:focus',
69
- elementBorderSize: 1,
70
- })}
71
-
72
- .selection-display {
73
- overflow: hidden;
74
- text-overflow: ellipsis;
75
- white-space: nowrap;
76
- }
77
-
78
- .trigger-icon {
79
- transform: rotate(180deg);
80
- transition: ${viraAnimationDurations['vira-interaction-animation-duration'].value}
81
- linear transform;
82
- align-self: flex-start;
83
- }
84
-
85
- .trigger-icon-wrapper {
86
- flex-grow: 1;
87
- display: flex;
88
- justify-content: flex-end;
89
- }
90
-
91
- .dropdown-wrapper.open .trigger-icon {
92
- transform: rotate(0);
93
- }
94
-
95
- .dropdown-wrapper.open:not(.open-upwards) {
96
- border-bottom-left-radius: 0;
97
- }
98
-
99
- .open-upwards.dropdown-wrapper.open {
100
- border-top-left-radius: 0;
101
- }
102
-
103
- .dropdown-trigger {
104
- border: 1px solid ${viraFormCssVars['vira-form-border-color'].value};
105
- height: 100%;
106
- width: 100%;
107
- transition: inherit;
108
- box-sizing: border-box;
109
- display: flex;
110
- gap: 8px;
111
- text-align: left;
112
- align-items: center;
113
- padding: 3px;
114
- padding-left: 10px;
115
- ${noUserSelect};
116
- border-radius: inherit;
117
- background-color: ${viraFormCssVars['vira-form-background-color'].value};
118
- color: ${viraFormCssVars['vira-form-foreground-color'].value};
119
- }
120
-
121
- .open-upwards ${ViraDropdownOptions} {
122
- border-bottom-left-radius: 0;
123
- border-bottom-right-radius: 0;
124
- ${viraShadows.menuShadowReversed}
125
- }
126
-
127
- ${hostClasses['vira-dropdown-disabled'].selector} {
128
- ${viraDisabledStyles}
129
- pointer-events: auto;
130
- }
131
-
132
- ${hostClasses['vira-dropdown-disabled'].selector} .dropdown-wrapper {
133
- pointer-events: none;
134
- }
135
-
136
- .pop-up-positioner {
137
- position: absolute;
138
- pointer-events: none;
139
- display: flex;
140
- flex-direction: column;
141
-
142
- /* highest possible z-index */
143
- z-index: 2147483647;
144
- /* space for the caret icon */
145
- right: 28px;
146
- /* minus the border width */
147
- top: calc(100% - 1px);
148
- left: 0;
149
- }
150
-
151
- .using-placeholder {
152
- opacity: 0.4;
153
- }
154
-
155
- .open-upwards .pop-up-positioner {
156
- flex-direction: column-reverse;
157
- /* minus the border width */
158
- bottom: calc(100% - 1px);
159
- }
160
- `,
161
- events: {
162
- selectedChange: defineElementEvent(),
163
- openChange: defineElementEvent(),
164
- },
165
- cleanup({ state, updateState }) {
166
- updateState({ showPopUpResult: undefined });
167
- state.popUpManager.destroy();
168
- },
169
- init({ state, updateState, host, inputs, dispatch, events }) {
170
- state.popUpManager.listen(HidePopUpEvent, () => {
171
- updateState({ showPopUpResult: undefined });
172
- if (!inputs.isDisabled) {
173
- const dropdownWrapper = host.shadowRoot.querySelector('.dropdown-wrapper');
174
- assert.instanceOf(dropdownWrapper, HTMLButtonElement, 'failed to find dropdown wrapper child');
175
- dropdownWrapper.focus();
176
- }
177
- });
178
- state.popUpManager.listen(NavSelectEvent, (event) => {
179
- const optionIndex = event.detail.x;
180
- const option = inputs.options[optionIndex];
181
- if (!option) {
182
- throw new Error(`Found no dropdown option at index '${optionIndex}'`);
183
- }
184
- /** Only close upon option selection if the dropdown is not multi select. */
185
- if (!inputs.isMultiSelect) {
186
- triggerPopUpState({ emitEvent: true, open: false }, {
187
- dispatch: (openState) => {
188
- dispatch(new events.openChange(openState));
189
- },
190
- host,
191
- popUpManager: state.popUpManager,
192
- updateState,
193
- });
194
- }
195
- dispatch(new events.selectedChange(createNewSelection(option.id, inputs.selected, !!inputs.isMultiSelect)));
196
- });
197
- },
198
- render({ dispatch, events, state, inputs, updateState, host }) {
199
- assertUniqueIdProps(inputs.options);
200
- function triggerPopUp(param) {
201
- triggerPopUpState(param, {
202
- dispatch: (openState) => {
203
- dispatch(new events.openChange(openState));
204
- },
205
- host,
206
- popUpManager: state.popUpManager,
207
- updateState,
208
- });
209
- }
210
- if (inputs.isDisabled) {
211
- triggerPopUp({ open: false, emitEvent: false });
212
- }
213
- else if (inputs.z_debug_forceOpenState != undefined) {
214
- if (!inputs.z_debug_forceOpenState && state.showPopUpResult) {
215
- triggerPopUp({ emitEvent: false, open: false });
216
- }
217
- else if (inputs.z_debug_forceOpenState && !state.showPopUpResult) {
218
- triggerPopUp({ emitEvent: false, open: true });
219
- }
220
- }
221
- const selectedOptions = filterToSelectedOptions(inputs);
222
- const leadingIconTemplate = inputs.icon
223
- ? html `
224
- <${ViraIcon.assign({
225
- icon: inputs.icon,
226
- })}
227
- ${testId(viraDropdownTestIds.icon)}
228
- ></${ViraIcon}>
229
- `
230
- : nothing;
231
- const positionerStyles = state.showPopUpResult
232
- ? state.showPopUpResult.popDown
233
- ? /** Dropdown going down position. */
234
- css `
235
- bottom: -${state.showPopUpResult.positions.diff.bottom}px;
236
- `
237
- : /** Dropdown going up position. */
238
- css `
239
- top: -${state.showPopUpResult.positions.diff.top}px;
240
- `
241
- : undefined;
242
- function respondToClick() {
243
- triggerPopUp({ emitEvent: true, open: !state.showPopUpResult });
244
- }
245
- const shouldUsePlaceholder = !selectedOptions.length;
246
- const prefixTemplate = inputs.selectionPrefix && !shouldUsePlaceholder
247
- ? html `
248
- <span class="selected-label-prefix" ${testId(viraDropdownTestIds.prefix)}>
249
- ${inputs.selectionPrefix}
250
- </span>
251
- `
252
- : nothing;
253
- const selectionDisplay = shouldUsePlaceholder
254
- ? inputs.placeholder || ''
255
- : selectedOptions.map((item) => item.label).join(', ');
256
- return html `
257
- <button
258
- ?disabled=${!!inputs.isDisabled}
259
- class="dropdown-wrapper ${classMap({
260
- open: !!state.showPopUpResult,
261
- 'open-upwards': !state.showPopUpResult?.popDown,
262
- })}"
263
- ${testId(viraDropdownTestIds.trigger)}
264
- role="listbox"
265
- aria-expanded=${!!state.showPopUpResult}
266
- ${listen('keydown', (event) => {
267
- if (!state.showPopUpResult && event.code.startsWith('Arrow')) {
268
- triggerPopUp({ emitEvent: true, open: true });
269
- }
270
- })}
271
- ${listen('click', (event) => {
272
- /** Detail is 0 if it was a keyboard key (like Enter) that triggered this click. */
273
- if (event.detail === 0) {
274
- respondToClick();
275
- }
276
- })}
277
- ${listen('mousedown', (event) => {
278
- /** Ignore any clicks that aren't the main button. */
279
- if (event.button === 0) {
280
- respondToClick();
281
- }
282
- })}
283
- >
284
- <div class="dropdown-trigger">
285
- ${leadingIconTemplate}
286
- <span
287
- class="selection-display ${classMap({
288
- 'using-placeholder': shouldUsePlaceholder,
289
- })}"
290
- title=${ifDefined(shouldUsePlaceholder ? selectionDisplay : undefined)}
291
- >
292
- ${prefixTemplate} ${selectionDisplay}
293
- </span>
294
- <span class="trigger-icon-wrapper">
295
- <${ViraIcon.assign({ icon: ChevronUp24Icon })}
296
- class="trigger-icon"
297
- ></${ViraIcon}>
298
- </span>
299
- </div>
300
- <div class="pop-up-positioner" style=${positionerStyles}>
301
- ${renderIf(!!state.showPopUpResult, html `
302
- <${ViraDropdownOptions.assign({
303
- options: inputs.options,
304
- selectedOptions,
305
- navController: state.popUpManager.navController,
306
- })}
307
- ${listen(ViraDropdownOptions.events.selectionChange, (event) => {
308
- /**
309
- * Only close upon option selection if the dropdown is not multi
310
- * select.
311
- */
312
- if (!inputs.isMultiSelect) {
313
- triggerPopUp({ emitEvent: true, open: false });
314
- }
315
- dispatch(new events.selectedChange(createNewSelection(event.detail.id, inputs.selected, !!inputs.isMultiSelect)));
316
- })}
317
- ${testId(viraDropdownTestIds.options)}
318
- ></${ViraDropdownOptions}>
319
- `)}
320
- </div>
321
- </button>
322
- `;
323
- },
324
- });