vira 25.7.3 → 25.9.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 (30) 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 +19 -0
  8. package/dist/elements/pop-up/vira-menu-item.element.js +58 -0
  9. package/dist/elements/pop-up/vira-menu-trigger.element.d.ts +39 -0
  10. package/dist/elements/pop-up/vira-menu-trigger.element.js +108 -0
  11. package/dist/elements/pop-up/vira-menu.element.d.ts +38 -0
  12. package/dist/elements/pop-up/vira-menu.element.js +130 -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-item.element.js +0 -69
  28. package/dist/elements/dropdown/vira-dropdown-options.element.d.ts +0 -29
  29. package/dist/elements/dropdown/vira-dropdown-options.element.js +0 -116
  30. package/dist/elements/dropdown/vira-dropdown.element.js +0 -324
@@ -0,0 +1,235 @@
1
+ import { assert } from '@augment-vir/assert';
2
+ import { NavController } from 'device-navigation';
3
+ import { classMap, css, defineElementEvent, html, listen, renderIf } from 'element-vir';
4
+ import { createFocusStyles, viraFocusCssVars } from '../../styles/focus.js';
5
+ import { viraFormCssVars } from '../../styles/form-themes.js';
6
+ import { noNativeFormStyles, noUserSelect, viraDisabledStyles } from '../../styles/index.js';
7
+ import { HidePopUpEvent, NavSelectEvent, PopUpManager, } from '../../util/pop-up-manager.js';
8
+ import { defineViraElement } from '../define-vira-element.js';
9
+ import { triggerPopUpState } from './pop-up-helpers.js';
10
+ /**
11
+ * An element with slots for a pop-up trigger and pop-up contents.
12
+ *
13
+ * @category PopUp
14
+ * @category Elements
15
+ * @see https://electrovir.github.io/element-vir/vira/book/elements/vira-pop-up-trigger
16
+ */
17
+ export const ViraPopUpTrigger = defineViraElement()({
18
+ tagName: 'vira-pop-up-trigger',
19
+ state({ host }) {
20
+ return {
21
+ /** `undefined` means the pop up is not currently showing. */
22
+ showPopUpResult: undefined,
23
+ popUpManager: new PopUpManager(new NavController(host, { activateOnMouseUp: true })),
24
+ };
25
+ },
26
+ slotNames: [
27
+ 'trigger',
28
+ 'popUp',
29
+ ],
30
+ hostClasses: {
31
+ 'vira-pop-up-trigger-disabled': ({ inputs }) => !!inputs.isDisabled,
32
+ },
33
+ styles: ({ hostClasses }) => css `
34
+ :host {
35
+ display: inline-flex;
36
+ box-sizing: border-box;
37
+ vertical-align: middle;
38
+ ${viraFocusCssVars['vira-focus-outline-color'].name}: ${viraFormCssVars['vira-form-focus-color'].value};
39
+ position: relative;
40
+ max-width: 100%;
41
+ }
42
+
43
+ .dropdown-wrapper {
44
+ ${noNativeFormStyles};
45
+ cursor: pointer;
46
+ max-width: 100%;
47
+ position: relative;
48
+ outline: none;
49
+ flex-grow: 1;
50
+ box-sizing: border-box;
51
+ }
52
+
53
+ ${createFocusStyles({
54
+ selector: '.dropdown-wrapper:focus',
55
+ elementBorderSize: 1,
56
+ })}
57
+
58
+ .dropdown-trigger {
59
+ box-sizing: border-box;
60
+ ${noUserSelect};
61
+ }
62
+
63
+ ${hostClasses['vira-pop-up-trigger-disabled'].selector} {
64
+ ${viraDisabledStyles}
65
+ pointer-events: auto;
66
+ }
67
+
68
+ ${hostClasses['vira-pop-up-trigger-disabled'].selector} .dropdown-wrapper {
69
+ pointer-events: none;
70
+ }
71
+
72
+ .pop-up-positioner {
73
+ position: absolute;
74
+ pointer-events: none;
75
+ display: flex;
76
+ flex-direction: column;
77
+
78
+ /* highest possible z-index */
79
+ z-index: 2147483647;
80
+ left: 0;
81
+ right: 0;
82
+
83
+ & > * {
84
+ pointer-events: auto;
85
+ }
86
+ }
87
+
88
+ .open-upwards .pop-up-positioner {
89
+ flex-direction: column-reverse;
90
+ }
91
+ `,
92
+ events: {
93
+ navSelect: defineElementEvent(),
94
+ /**
95
+ * - `undefined` indicates that the pop-up just closed.
96
+ * - {@link ShowPopUpResult} indicates that the pop-up just opened.
97
+ */
98
+ openChange: defineElementEvent(),
99
+ init: defineElementEvent(),
100
+ },
101
+ cleanup({ state, updateState }) {
102
+ updateState({ showPopUpResult: undefined });
103
+ state.popUpManager.destroy();
104
+ },
105
+ init({ state, updateState, host, inputs, dispatch, events }) {
106
+ /** Refocus the trigger and set the result to `undefined` when the pop up closes. */
107
+ state.popUpManager.listen(HidePopUpEvent, () => {
108
+ updateState({ showPopUpResult: undefined });
109
+ dispatch(new events.openChange(undefined));
110
+ if (!inputs.isDisabled) {
111
+ const dropdownWrapper = host.shadowRoot.querySelector('.dropdown-wrapper');
112
+ assert.instanceOf(dropdownWrapper, HTMLButtonElement, 'failed to find dropdown wrapper child');
113
+ dropdownWrapper.focus();
114
+ }
115
+ });
116
+ state.popUpManager.listen(NavSelectEvent, (event) => {
117
+ if (!inputs.keepOpenAfterInteraction) {
118
+ triggerPopUpState({
119
+ open: false,
120
+ callback(showPopUpResult) {
121
+ updateState({
122
+ showPopUpResult,
123
+ });
124
+ },
125
+ host,
126
+ popUpManager: state.popUpManager,
127
+ });
128
+ }
129
+ dispatch(new events.navSelect(event.detail));
130
+ });
131
+ dispatch(new events.init({
132
+ navController: state.popUpManager.navController,
133
+ popUpManager: state.popUpManager,
134
+ }));
135
+ },
136
+ render({ dispatch, events, state, inputs, updateState, host, slotNames }) {
137
+ function triggerPopUp({ emitEvent, open }, event) {
138
+ if (state.showPopUpResult && inputs.keepOpenAfterInteraction && event) {
139
+ const dropdownTrigger = host.shadowRoot.querySelector('.dropdown-trigger');
140
+ if (dropdownTrigger && !event.composedPath().includes(dropdownTrigger)) {
141
+ /**
142
+ * Prevent closing the pop-up when `keepOpenAfterInteraction` is turned on and
143
+ * the pop-up was interacted with.
144
+ */
145
+ return;
146
+ }
147
+ }
148
+ triggerPopUpState({
149
+ open,
150
+ callback(showPopUpResult) {
151
+ updateState({ showPopUpResult });
152
+ if (emitEvent) {
153
+ dispatch(new events.openChange(showPopUpResult));
154
+ }
155
+ },
156
+ host,
157
+ popUpManager: state.popUpManager,
158
+ });
159
+ }
160
+ if (inputs.isDisabled) {
161
+ triggerPopUp({ open: false, emitEvent: false }, undefined);
162
+ }
163
+ else if (inputs.z_debug_forceOpenState != undefined) {
164
+ if (!inputs.z_debug_forceOpenState && state.showPopUpResult) {
165
+ triggerPopUp({ emitEvent: false, open: false }, undefined);
166
+ }
167
+ else if (inputs.z_debug_forceOpenState && !state.showPopUpResult) {
168
+ triggerPopUp({ emitEvent: false, open: true }, undefined);
169
+ }
170
+ }
171
+ /**
172
+ * These styles do _not_ account for window resizing while the menu is open. I decided this
173
+ * was not a major enough problem to tackle. If it becomes major enough in the future,
174
+ * you'll need to hook into a window _or_ container resize listener inside `PopUpManager`
175
+ * and emit a new `ShowPopUpResult` instance when it changes.
176
+ */
177
+ const positionerStyles = state.showPopUpResult
178
+ ? state.showPopUpResult.popDown
179
+ ? /** Dropdown going down position. */
180
+ css `
181
+ bottom: -${state.showPopUpResult.positions.diff.bottom}px;
182
+ top: calc(100% + ${inputs.popUpOffset?.vertical || 0}px);
183
+ left: ${inputs.popUpOffset?.left || 0}px;
184
+ right: ${inputs.popUpOffset?.right || 0}px;
185
+ `
186
+ : /** Dropdown going up position. */
187
+ css `
188
+ top: -${state.showPopUpResult.positions.diff.top}px;
189
+ bottom: calc(100% + ${inputs.popUpOffset?.vertical || 0}px);
190
+ left: ${inputs.popUpOffset?.left || 0}px;
191
+ right: ${inputs.popUpOffset?.right || 0}px;
192
+ `
193
+ : undefined;
194
+ function respondToClick(event) {
195
+ triggerPopUp({ emitEvent: true, open: !state.showPopUpResult }, event);
196
+ }
197
+ return html `
198
+ <button
199
+ ?disabled=${!!inputs.isDisabled}
200
+ class="dropdown-wrapper ${classMap({
201
+ open: !!state.showPopUpResult,
202
+ 'open-upwards': !state.showPopUpResult?.popDown,
203
+ })}"
204
+ role="listbox"
205
+ aria-expanded=${!!state.showPopUpResult}
206
+ ${listen('keydown', (event) => {
207
+ if (!state.showPopUpResult && event.code.startsWith('Arrow')) {
208
+ triggerPopUp({ emitEvent: true, open: true }, event);
209
+ }
210
+ })}
211
+ ${listen('click', (event) => {
212
+ /** Detail is 0 if it was a keyboard key (like Enter) that triggered this click. */
213
+ if (event.detail === 0) {
214
+ respondToClick(event);
215
+ }
216
+ })}
217
+ ${listen('mousedown', (event) => {
218
+ /** Ignore any clicks that aren't the main button. */
219
+ if (event.button === 0) {
220
+ respondToClick(event);
221
+ }
222
+ })}
223
+ >
224
+ <div class="dropdown-trigger">
225
+ <slot name=${slotNames.trigger}></slot>
226
+ </div>
227
+ <div class="pop-up-positioner" style=${positionerStyles}>
228
+ ${renderIf(!!state.showPopUpResult, html `
229
+ <slot name=${slotNames.popUp}></slot>
230
+ `)}
231
+ </div>
232
+ </button>
233
+ `;
234
+ },
235
+ });
@@ -1,6 +1,7 @@
1
- import { css, defineElement, html } from 'element-vir';
1
+ import { css, html } from 'element-vir';
2
+ import { defineViraElement } from './define-vira-element.js';
2
3
  /** Use this element to reserve space for bolded text, even if it isn't bold yet. */
3
- export const ViraBoldText = defineElement()({
4
+ export const ViraBoldText = defineViraElement()({
4
5
  tagName: 'vira-bold',
5
6
  cssVars: {
6
7
  'vira-bold-bold-weight': 'bold',
@@ -1,7 +1,7 @@
1
1
  import { type PartialWithUndefined } from '@augment-vir/common';
2
- import { type ViraIconSvg } from '../../icons/icon-svg.js';
3
- import { PopUpManager, type ShowPopUpResult } from '../../util/pop-up-manager.js';
4
- import { type ViraDropdownOption } from './vira-dropdown-item.element.js';
2
+ import { type ViraIconSvg } from '../icons/icon-svg.js';
3
+ import { type ShowPopUpResult } from '../util/pop-up-manager.js';
4
+ import { type MenuItem } from './pop-up/pop-up-menu-item.js';
5
5
  /**
6
6
  * Test ids for {@link ViraDropdown}.
7
7
  *
@@ -11,17 +11,16 @@ export declare const viraDropdownTestIds: {
11
11
  trigger: string;
12
12
  icon: string;
13
13
  prefix: string;
14
- options: string;
15
14
  };
16
15
  /**
17
- * The main dropdown element that should be use directly.
16
+ * A dropdown element that uses pop-up menus.
18
17
  *
19
18
  * @category Dropdown
20
19
  * @category Elements
21
20
  * @see https://electrovir.github.io/element-vir/vira/book/elements/dropdown/vira-dropdown
22
21
  */
23
22
  export declare const ViraDropdown: import("element-vir").DeclarativeElementDefinition<"vira-dropdown", {
24
- options: ReadonlyArray<Readonly<ViraDropdownOption>>;
23
+ options: ReadonlyArray<Readonly<MenuItem>>;
25
24
  /** The selected id from the given options. */
26
25
  selected: ReadonlyArray<PropertyKey>;
27
26
  } & PartialWithUndefined<{
@@ -32,6 +31,11 @@ export declare const ViraDropdown: import("element-vir").DeclarativeElementDefin
32
31
  * multiple.
33
32
  */
34
33
  isMultiSelect: boolean;
34
+ /**
35
+ * Shows the selection quantity rather than a list of selections. Only used when
36
+ * `isMultiSelect` is `true`.
37
+ */
38
+ showSelectionCount: boolean;
35
39
  icon: ViraIconSvg;
36
40
  selectionPrefix: string;
37
41
  isDisabled: boolean;
@@ -40,8 +44,7 @@ export declare const ViraDropdown: import("element-vir").DeclarativeElementDefin
40
44
  }>, {
41
45
  /** `undefined` means the pop up is not currently showing. */
42
46
  showPopUpResult: ShowPopUpResult | undefined;
43
- popUpManager: PopUpManager;
44
47
  }, {
45
48
  selectedChange: import("element-vir").DefineEvent<PropertyKey[]>;
46
- openChange: import("element-vir").DefineEvent<boolean>;
47
- }, "vira-dropdown-disabled", "vira-dropdown-", readonly []>;
49
+ openChange: import("element-vir").DefineEvent<ShowPopUpResult | undefined>;
50
+ }, "vira-dropdown-", "vira-dropdown-", readonly []>;
@@ -0,0 +1,185 @@
1
+ import { check } from '@augment-vir/assert';
2
+ import { filterMap } from '@augment-vir/common';
3
+ import { classMap, css, defineElementEvent, html, ifDefined, listen, nothing, testId, } from 'element-vir';
4
+ import { ChevronUp24Icon } from '../icons/index.js';
5
+ import { viraBorders } from '../styles/border.js';
6
+ import { viraFocusCssVars } from '../styles/focus.js';
7
+ import { viraFormCssVars } from '../styles/form-themes.js';
8
+ import { noUserSelect, viraAnimationDurations } from '../styles/index.js';
9
+ import { defineViraElement } from './define-vira-element.js';
10
+ import { ViraMenuTrigger } from './pop-up/vira-menu-trigger.element.js';
11
+ import { ViraIcon } from './vira-icon.element.js';
12
+ /**
13
+ * Test ids for {@link ViraDropdown}.
14
+ *
15
+ * @category Internal
16
+ */
17
+ export const viraDropdownTestIds = {
18
+ trigger: 'dropdown-trigger',
19
+ icon: 'dropdown-icon',
20
+ prefix: 'dropdown-prefix',
21
+ };
22
+ /**
23
+ * A dropdown element that uses pop-up menus.
24
+ *
25
+ * @category Dropdown
26
+ * @category Elements
27
+ * @see https://electrovir.github.io/element-vir/vira/book/elements/dropdown/vira-dropdown
28
+ */
29
+ export const ViraDropdown = defineViraElement()({
30
+ tagName: 'vira-dropdown',
31
+ styles: css `
32
+ :host {
33
+ display: inline-flex;
34
+ vertical-align: middle;
35
+ width: 256px;
36
+ ${viraFocusCssVars['vira-focus-outline-color'].name}: ${viraFormCssVars['vira-form-focus-color'].value};
37
+ position: relative;
38
+ max-width: 100%;
39
+ }
40
+
41
+ ${ViraMenuTrigger} {
42
+ width: 100%;
43
+ }
44
+
45
+ .selection-display {
46
+ overflow: hidden;
47
+ text-overflow: ellipsis;
48
+ white-space: nowrap;
49
+ }
50
+
51
+ .trigger-icon {
52
+ align-self: flex-start;
53
+ will-change: transform;
54
+ transform: rotate(180deg);
55
+ transition: transform
56
+ ${viraAnimationDurations['vira-interaction-animation-duration'].value} linear;
57
+ }
58
+
59
+ .trigger-icon-wrapper {
60
+ flex-grow: 1;
61
+ display: flex;
62
+ justify-content: flex-end;
63
+ }
64
+
65
+ .open {
66
+ & .trigger-icon {
67
+ transform: rotate(0);
68
+ }
69
+
70
+ &:not(.open-upwards).dropdown-trigger {
71
+ border-bottom-left-radius: 0;
72
+ }
73
+
74
+ &.open-upwards.dropdown-trigger {
75
+ border-top-left-radius: 0;
76
+ }
77
+ }
78
+
79
+ .dropdown-trigger {
80
+ ${noUserSelect};
81
+ border: 1px solid ${viraFormCssVars['vira-form-border-color'].value};
82
+ height: 100%;
83
+ width: 100%;
84
+ box-sizing: border-box;
85
+ display: flex;
86
+ gap: 8px;
87
+ text-align: left;
88
+ align-items: center;
89
+ padding: 3px;
90
+ padding-left: 10px;
91
+ border-radius: ${viraBorders['vira-form-input-radius'].value};
92
+ background-color: ${viraFormCssVars['vira-form-background-color'].value};
93
+ color: ${viraFormCssVars['vira-form-foreground-color'].value};
94
+ }
95
+
96
+ .using-placeholder {
97
+ opacity: 0.4;
98
+ }
99
+ `,
100
+ events: {
101
+ selectedChange: defineElementEvent(),
102
+ openChange: defineElementEvent(),
103
+ },
104
+ state() {
105
+ return {
106
+ /** `undefined` means the pop up is not currently showing. */
107
+ showPopUpResult: undefined,
108
+ };
109
+ },
110
+ render({ state, inputs, dispatch, events, updateState }) {
111
+ const selectedOptions = filterMap(inputs.selected, (selectedId) => inputs.options.find((option) => option.id === selectedId), check.isTruthy);
112
+ const leadingIconTemplate = inputs.icon
113
+ ? html `
114
+ <${ViraIcon.assign({
115
+ icon: inputs.icon,
116
+ })}
117
+ ${testId(viraDropdownTestIds.icon)}
118
+ ></${ViraIcon}>
119
+ `
120
+ : nothing;
121
+ const shouldUsePlaceholder = !selectedOptions.length;
122
+ const prefixTemplate = inputs.selectionPrefix && !shouldUsePlaceholder
123
+ ? html `
124
+ <span class="selected-label-prefix" ${testId(viraDropdownTestIds.prefix)}>
125
+ ${inputs.selectionPrefix}
126
+ </span>
127
+ `
128
+ : nothing;
129
+ const selectionDisplay = shouldUsePlaceholder
130
+ ? inputs.placeholder || ''
131
+ : inputs.isMultiSelect && inputs.showSelectionCount
132
+ ? `${selectedOptions.length} Selected`
133
+ : inputs.isMultiSelect
134
+ ? selectedOptions.map((item) => item.label).join(', ')
135
+ : selectedOptions[0]?.label || '';
136
+ return html `
137
+ <${ViraMenuTrigger.assign({
138
+ items: inputs.options,
139
+ selected: inputs.selected,
140
+ isDisabled: inputs.isDisabled,
141
+ isMultiSelect: inputs.isMultiSelect,
142
+ z_debug_forceOpenState: inputs.z_debug_forceOpenState,
143
+ popUpOffset: {
144
+ vertical: -1,
145
+ right: 24,
146
+ },
147
+ })}
148
+ ${listen(ViraMenuTrigger.events.openChange, (event) => {
149
+ updateState({ showPopUpResult: event.detail });
150
+ dispatch(new events.openChange(event.detail));
151
+ })}
152
+ ${listen(ViraMenuTrigger.events.itemActivate, (event) => {
153
+ dispatch(new events.selectedChange(event.detail));
154
+ })}
155
+ >
156
+ <div
157
+ slot=${ViraMenuTrigger.slotNames.trigger}
158
+ class="dropdown-trigger ${classMap({
159
+ open: !!state.showPopUpResult,
160
+ 'open-upwards': !state.showPopUpResult?.popDown,
161
+ })}"
162
+ ${testId(viraDropdownTestIds.trigger)}
163
+ >
164
+ ${leadingIconTemplate}
165
+ <span
166
+ class="selection-display ${classMap({
167
+ 'using-placeholder': shouldUsePlaceholder,
168
+ })}"
169
+ title=${ifDefined(shouldUsePlaceholder ||
170
+ (inputs.isMultiSelect && inputs.showSelectionCount)
171
+ ? undefined
172
+ : selectionDisplay)}
173
+ >
174
+ ${prefixTemplate} ${selectionDisplay}
175
+ </span>
176
+ <span class="trigger-icon-wrapper">
177
+ <${ViraIcon.assign({ icon: ChevronUp24Icon })}
178
+ class="trigger-icon"
179
+ ></${ViraIcon}>
180
+ </span>
181
+ </div>
182
+ </${ViraMenuTrigger}>
183
+ `;
184
+ },
185
+ });
@@ -26,5 +26,4 @@ export declare function createFocusStyles({ selector, elementBorderSize, outline
26
26
  elementBorderSize: number;
27
27
  outlineGap?: number;
28
28
  outlineWidth?: number;
29
- borderRadius?: number;
30
29
  }): import("element-vir").CSSResult;
@@ -122,7 +122,7 @@ export type PopUpManagerEvents = HidePopUpEvent | NavSelectEvent;
122
122
  export declare class PopUpManager {
123
123
  readonly navController: NavController;
124
124
  private listenTarget;
125
- private options;
125
+ options: PopUpManagerOptions;
126
126
  private cleanupCallbacks;
127
127
  private lastRootElement;
128
128
  constructor(navController: NavController, options?: Partial<PopUpManagerOptions> | undefined);
@@ -1,7 +1,7 @@
1
1
  import { assert } from '@augment-vir/assert';
2
2
  import { mapObjectValues } from '@augment-vir/common';
3
3
  import { findOverflowAncestor } from '@augment-vir/web';
4
- import { NavDirection } from 'device-navigation';
4
+ import { NavActivateEvent, NavDirection } from 'device-navigation';
5
5
  import { listenToPageActivation } from 'page-active';
6
6
  import { ListenTarget, defineTypedCustomEvent, defineTypedEvent, listenToGlobal, } from 'typed-event-target';
7
7
  /**
@@ -56,6 +56,14 @@ export class PopUpManager {
56
56
  this.removePopUp();
57
57
  }
58
58
  }),
59
+ this.navController.listen(NavActivateEvent, (event) => {
60
+ if (event.detail.success) {
61
+ this.listenTarget.dispatch(new NavSelectEvent({ detail: event.detail.coords }));
62
+ this.navController.currentNavEntry?.entry.focus(true);
63
+ event.stopImmediatePropagation();
64
+ event.preventDefault();
65
+ }
66
+ }),
59
67
  listenToGlobal('mousedown', (event) => {
60
68
  if (this.lastRootElement &&
61
69
  event.composedPath().includes(this.lastRootElement)) {
@@ -102,13 +110,10 @@ export class PopUpManager {
102
110
  allowWrapping: false,
103
111
  });
104
112
  }
105
- else if (keyCode === 'Enter' || keyCode === 'Return') {
106
- const result = this.navController.enterInto({ fallbackToActivate: true });
107
- if (result.success) {
108
- this.listenTarget.dispatch(new NavSelectEvent({ detail: result.coords }));
109
- event.stopImmediatePropagation();
110
- event.preventDefault();
111
- }
113
+ else if ((keyCode === 'Enter' || keyCode === 'Return' || keyCode === 'Space') &&
114
+ this.navController.enterInto({ fallbackToActivate: true }).success) {
115
+ event.stopImmediatePropagation();
116
+ event.preventDefault();
112
117
  }
113
118
  }
114
119
  }),
@@ -152,7 +157,13 @@ export class PopUpManager {
152
157
  const diff = mapObjectValues(emptyPositionRect, (key) => {
153
158
  const containerDimension = containerPosition[key];
154
159
  const hostDimension = rootPosition[key];
155
- return Math.abs(containerDimension - hostDimension);
160
+ /**
161
+ * Chrome will trigger a scroll bar sometimes if the PopUp is too close to the bottom,
162
+ * even if clearly isn't overflowing. The value here was found by experimentation to be
163
+ * the lowest value that wouldn't trigger that.
164
+ */
165
+ const additionalOffset = key === 'bottom' ? -51 : 0;
166
+ return Math.abs(containerDimension - hostDimension + additionalOffset);
156
167
  });
157
168
  const useUp = diff.top > diff.bottom + currentOptions.verticalDiffThreshold &&
158
169
  diff.bottom < currentOptions.minDownSpace;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vira",
3
- "version": "25.7.3",
3
+ "version": "25.9.0",
4
4
  "description": "A simple and highly versatile design system using element-vir.",
5
5
  "keywords": [
6
6
  "design",
@@ -25,6 +25,7 @@
25
25
  "name": "electrovir",
26
26
  "url": "https://github.com/electrovir"
27
27
  },
28
+ "sideEffects": false,
28
29
  "type": "module",
29
30
  "main": "dist/index.js",
30
31
  "module": "dist/index.js",
@@ -37,36 +38,36 @@
37
38
  "test:docs": "virmator docs check"
38
39
  },
39
40
  "dependencies": {
40
- "@augment-vir/assert": "^31.20.0",
41
- "@augment-vir/common": "^31.20.0",
42
- "@augment-vir/web": "^31.20.0",
41
+ "@augment-vir/assert": "^31.21.0",
42
+ "@augment-vir/common": "^31.21.0",
43
+ "@augment-vir/web": "^31.21.0",
43
44
  "colorjs.io": "^0.5.2",
44
45
  "date-vir": "^7.3.1",
45
- "device-navigation": "^4.4.0",
46
+ "device-navigation": "^4.5.5",
46
47
  "lit-css-vars": "^3.0.11",
47
48
  "observavir": "^2.0.5",
48
49
  "page-active": "^1.0.1",
49
- "spa-router-vir": "^5.3.1",
50
+ "spa-router-vir": "^5.5.0",
50
51
  "type-fest": "^4.41.0",
51
52
  "typed-event-target": "^4.1.0"
52
53
  },
53
54
  "devDependencies": {
54
- "@augment-vir/test": "^31.20.0",
55
+ "@augment-vir/test": "^31.21.0",
55
56
  "@web/dev-server-esbuild": "^1.0.4",
56
57
  "@web/test-runner": "^0.20.2",
57
58
  "@web/test-runner-commands": "^0.9.0",
58
59
  "@web/test-runner-playwright": "^0.11.0",
59
60
  "@web/test-runner-visual-regression": "^0.10.0",
60
- "esbuild": "^0.25.4",
61
+ "esbuild": "^0.25.5",
61
62
  "istanbul-smart-text-reporter": "^1.1.5",
62
63
  "markdown-code-example-inserter": "^3.0.3",
63
- "typedoc": "^0.28.4",
64
+ "typedoc": "^0.28.5",
64
65
  "typescript": "5.8.3",
65
66
  "vite": "^6.3.5",
66
67
  "vite-tsconfig-paths": "^5.1.4"
67
68
  },
68
69
  "peerDependencies": {
69
- "element-vir": "^25.7.3"
70
+ "element-vir": "^25.9.0"
70
71
  },
71
72
  "engines": {
72
73
  "node": ">=22"
@@ -1,45 +0,0 @@
1
- import { type PopUpManager, type ShowPopUpResult } from '../../util/pop-up-manager.js';
2
- import { type ViraDropdownOption } from './vira-dropdown-item.element.js';
3
- /**
4
- * Filters an array of {@link ViraDropdownOption} based on the given selection.
5
- *
6
- * @category Internal
7
- */
8
- export declare function filterToSelectedOptions({ selected, options, isMultiSelect, }: Readonly<{
9
- selected: ReadonlyArray<PropertyKey>;
10
- isMultiSelect?: boolean | undefined;
11
- options: ReadonlyArray<Readonly<ViraDropdownOption>>;
12
- }>): ViraDropdownOption[];
13
- /**
14
- * Verifies that all options have unique ids.
15
- *
16
- * @category Internal
17
- */
18
- export declare function assertUniqueIdProps(options: ReadonlyArray<Readonly<{
19
- id: PropertyKey;
20
- }>>): void;
21
- /**
22
- * Creates a new array of selections based on the current selection and new selection id. This
23
- * behaves differently when multi select is enabled, hence this function.
24
- *
25
- * @category Internal
26
- */
27
- export declare function createNewSelection(
28
- /** The id of the option that should be newly selected. */
29
- id: PropertyKey, currentSelection: ReadonlyArray<PropertyKey>, isMultiSelect: boolean): PropertyKey[];
30
- /**
31
- * Handles toggles pop up state for `ViraDropdown`.
32
- *
33
- * @category Internal
34
- */
35
- export declare function triggerPopUpState({ open, emitEvent }: {
36
- open: boolean;
37
- emitEvent: boolean;
38
- }, { updateState, popUpManager, dispatch, host, }: {
39
- updateState: (params: {
40
- showPopUpResult: ShowPopUpResult | undefined;
41
- }) => void;
42
- popUpManager: PopUpManager;
43
- dispatch: (open: boolean) => void;
44
- host: HTMLElement;
45
- }): void;