vira 28.16.0 → 28.18.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.
@@ -1,4 +1,5 @@
1
- import { type PopUpManager, type ShowPopUpResult } from '../../util/pop-up-manager.js';
1
+ import { type PartialWithUndefined } from '@augment-vir/common';
2
+ import { type PopUpManager, type PopUpManagerOptions, type ShowPopUpResult } from '../../util/pop-up-manager.js';
2
3
  import { type MenuItem } from './pop-up-menu-item.js';
3
4
  /**
4
5
  * Verifies that all items have unique ids.
@@ -22,9 +23,11 @@ newItem: Readonly<MenuItem>, currentSelection?: ReadonlyArray<PropertyKey>, isMu
22
23
  *
23
24
  * @category Internal
24
25
  */
25
- export declare function triggerPopUpState({ open, callback, popUpManager, host, }: {
26
+ export declare function triggerPopUpState({ open, callback, popUpManager, host, options, }: Readonly<{
26
27
  open: boolean;
27
28
  popUpManager: PopUpManager;
28
29
  host: HTMLElement;
30
+ } & PartialWithUndefined<{
29
31
  callback?: ((showPopUpResult: ShowPopUpResult | undefined) => void) | undefined;
30
- }): void;
32
+ options?: Partial<PopUpManagerOptions> | undefined;
33
+ }>>): void;
@@ -46,9 +46,9 @@ newItem, currentSelection = [], isMultiSelect = false) {
46
46
  *
47
47
  * @category Internal
48
48
  */
49
- export function triggerPopUpState({ open, callback, popUpManager, host, }) {
49
+ export function triggerPopUpState({ open, callback, popUpManager, host, options, }) {
50
50
  if (open) {
51
- const showPopUpResult = popUpManager.showPopUp(host);
51
+ const showPopUpResult = popUpManager.showPopUp(host, options);
52
52
  callback?.(showPopUpResult);
53
53
  }
54
54
  else {
@@ -44,6 +44,14 @@ export declare const ViraMenuTrigger: import("element-vir").DeclarativeElementDe
44
44
  * @default HorizontalAnchor.Left
45
45
  */
46
46
  horizontalAnchor: HorizontalAnchor;
47
+ /**
48
+ * When `true`, the pop-up will not have its maximum height/width constrained to fit within
49
+ * the overflow container. The positioning logic (up/down, left/right) will still be
50
+ * applied.
51
+ *
52
+ * @default false
53
+ */
54
+ ignoreMaxDimensions: boolean;
47
55
  }>, {
48
56
  navController: undefined | NavController;
49
57
  popUpManager: undefined | PopUpManager;
@@ -56,6 +56,7 @@ export const ViraMenuTrigger = defineViraElement()({
56
56
  z_debug_forceOpenState: inputs.z_debug_forceOpenState,
57
57
  popUpOffset: inputs.popUpOffset,
58
58
  horizontalAnchor: inputs.horizontalAnchor || HorizontalAnchor.Left,
59
+ ignoreMaxDimensions: inputs.ignoreMaxDimensions,
59
60
  })}
60
61
  class=${classMap({
61
62
  open: !!state.showPopUpResult,
@@ -27,12 +27,15 @@ export declare enum HorizontalAnchor {
27
27
  * pop-up to grow on the left side of the trigger.
28
28
  */
29
29
  Right = "right",
30
+ /** Restrict the pop-up on both sides. */
31
+ Both = "both",
30
32
  /**
31
- * Restrict the pop-up on both sides.
33
+ * Automatically choose left or right based on available space, defaulting to anchoring on the
34
+ * left side.
32
35
  *
33
36
  * This is the default anchor for {@link ViraPopUpTrigger}.
34
37
  */
35
- Both = "both"
38
+ Auto = "auto"
36
39
  }
37
40
  /**
38
41
  * An element with slots for a pop-up trigger and pop-up contents.
@@ -48,21 +51,31 @@ export declare const ViraPopUpTrigger: import("element-vir").DeclarativeElementD
48
51
  /** Set to `true` to keep the pop-up open if it is interacted with. */
49
52
  keepOpenAfterInteraction: boolean;
50
53
  /** All values in px. */
51
- popUpOffset?: PopUpOffset;
54
+ popUpOffset: PopUpOffset;
52
55
  /**
53
56
  * - `HorizontalAnchor.Left`: pop-up is anchored to the left side of the trigger and the
54
57
  * pop-up can grow to the right.
55
58
  * - `HorizontalAnchor.Right`: pop-up is anchored to the right side of the trigger and the
56
59
  * pop-up can grow to the left.
57
60
  * - `HorizontalAnchor.Both`: pop-up is anchored on both sides of the trigger and cannot grow
58
- * beyond it. (This is the default experience.)
61
+ * beyond it.
62
+ * - `HorizontalAnchor.Auto`: automatically choose left or right anchor based on available
63
+ * space, defaulting to left anchor. (This is the default experience.)
59
64
  *
60
65
  * Note that when `HorizontalAnchor.Both` is _not_ used, this anchor will cancel out any
61
66
  * `popUpOffset` for the direction _opposite_ of the chosen anchor.
62
67
  *
63
- * @default HorizontalAnchor.Both
68
+ * @default HorizontalAnchor.Auto
64
69
  */
65
- horizontalAnchor?: HorizontalAnchor;
70
+ horizontalAnchor: HorizontalAnchor;
71
+ /**
72
+ * When `true`, the pop-up will not have its maximum height/width constrained to fit within
73
+ * the overflow container. The positioning logic (up/down, left/right) will still be
74
+ * applied.
75
+ *
76
+ * @default false
77
+ */
78
+ ignoreMaxDimensions: boolean;
66
79
  }>, {
67
80
  /** `undefined` means the pop up is not currently showing. */
68
81
  showPopUpResult: ShowPopUpResult | undefined;
@@ -23,12 +23,15 @@ export var HorizontalAnchor;
23
23
  * pop-up to grow on the left side of the trigger.
24
24
  */
25
25
  HorizontalAnchor["Right"] = "right";
26
+ /** Restrict the pop-up on both sides. */
27
+ HorizontalAnchor["Both"] = "both";
26
28
  /**
27
- * Restrict the pop-up on both sides.
29
+ * Automatically choose left or right based on available space, defaulting to anchoring on the
30
+ * left side.
28
31
  *
29
32
  * This is the default anchor for {@link ViraPopUpTrigger}.
30
33
  */
31
- HorizontalAnchor["Both"] = "both";
34
+ HorizontalAnchor["Auto"] = "auto";
32
35
  })(HorizontalAnchor || (HorizontalAnchor = {}));
33
36
  /**
34
37
  * An element with slots for a pop-up trigger and pop-up contents.
@@ -193,17 +196,35 @@ export const ViraPopUpTrigger = defineViraElement()({
193
196
  triggerPopUp({ emitEvent: false, open: true }, undefined);
194
197
  }
195
198
  }
196
- const leftCss = inputs.horizontalAnchor === HorizontalAnchor.Right && state.showPopUpResult
197
- ? css `
198
- left: -${state.showPopUpResult.positions.diff.left}px;
199
- `
199
+ /**
200
+ * Resolve the effective horizontal anchor. For Auto, use the popRight calculation from
201
+ * showPopUpResult to determine whether to anchor left or right.
202
+ */
203
+ const effectiveHorizontalAnchor = inputs.horizontalAnchor === HorizontalAnchor.Auto ||
204
+ inputs.horizontalAnchor === undefined
205
+ ? state.showPopUpResult?.popRight
206
+ ? HorizontalAnchor.Left
207
+ : HorizontalAnchor.Right
208
+ : inputs.horizontalAnchor;
209
+ const leftCss = effectiveHorizontalAnchor === HorizontalAnchor.Right && state.showPopUpResult
210
+ ? inputs.ignoreMaxDimensions
211
+ ? css `
212
+ left: unset;
213
+ `
214
+ : css `
215
+ left: -${state.showPopUpResult.positions.diff.left}px;
216
+ `
200
217
  : css `
201
218
  left: ${inputs.popUpOffset?.left || 0}px;
202
219
  `;
203
- const rightCss = state.showPopUpResult && inputs.horizontalAnchor === HorizontalAnchor.Left
204
- ? css `
205
- right: -${state.showPopUpResult.positions.diff.right}px;
206
- `
220
+ const rightCss = state.showPopUpResult && effectiveHorizontalAnchor === HorizontalAnchor.Left
221
+ ? inputs.ignoreMaxDimensions
222
+ ? css `
223
+ right: unset;
224
+ `
225
+ : css `
226
+ right: -${state.showPopUpResult.positions.diff.right}px;
227
+ `
207
228
  : css `
208
229
  right: ${inputs.popUpOffset?.right || 0}px;
209
230
  `;
@@ -220,17 +241,29 @@ export const ViraPopUpTrigger = defineViraElement()({
220
241
  const positionerStyles = state.showPopUpResult
221
242
  ? state.showPopUpResult.popDown
222
243
  ? /** Dropdown going down position. */
223
- css `
224
- bottom: -${state.showPopUpResult.positions.diff.bottom}px;
225
- top: calc(100% + ${inputs.popUpOffset?.vertical || 0}px);
226
- ${horizontalPositionStyle}
227
- `
244
+ inputs.ignoreMaxDimensions
245
+ ? css `
246
+ bottom: unset;
247
+ top: calc(100% + ${inputs.popUpOffset?.vertical || 0}px);
248
+ ${horizontalPositionStyle}
249
+ `
250
+ : css `
251
+ bottom: -${state.showPopUpResult.positions.diff.bottom}px;
252
+ top: calc(100% + ${inputs.popUpOffset?.vertical || 0}px);
253
+ ${horizontalPositionStyle}
254
+ `
228
255
  : /** Dropdown going up position. */
229
- css `
230
- top: -${state.showPopUpResult.positions.diff.top}px;
231
- bottom: calc(100% + ${inputs.popUpOffset?.vertical || 0}px);
232
- ${horizontalPositionStyle}
233
- `
256
+ inputs.ignoreMaxDimensions
257
+ ? css `
258
+ top: unset;
259
+ bottom: calc(100% + ${inputs.popUpOffset?.vertical || 0}px);
260
+ ${horizontalPositionStyle}
261
+ `
262
+ : css `
263
+ top: -${state.showPopUpResult.positions.diff.top}px;
264
+ bottom: calc(100% + ${inputs.popUpOffset?.vertical || 0}px);
265
+ ${horizontalPositionStyle}
266
+ `
234
267
  : undefined;
235
268
  function respondToClick(event) {
236
269
  triggerPopUp({ emitEvent: true, open: !state.showPopUpResult }, event);
@@ -268,7 +301,7 @@ export const ViraPopUpTrigger = defineViraElement()({
268
301
 
269
302
  <div
270
303
  class="pop-up-positioner ${classMap({
271
- 'right-aligned': inputs.horizontalAnchor === HorizontalAnchor.Right,
304
+ 'right-aligned': effectiveHorizontalAnchor === HorizontalAnchor.Right,
272
305
  })}"
273
306
  style=${positionerStyles}
274
307
  >
@@ -48,6 +48,14 @@ export declare const ViraDropdown: import("element-vir").DeclarativeElementDefin
48
48
  * @default HorizontalAnchor.Both
49
49
  */
50
50
  horizontalAnchor: HorizontalAnchor;
51
+ /**
52
+ * When `true`, the dropdown will not have its maximum height/width constrained to fit
53
+ * within the overflow container. The positioning logic (up/down, left/right) will still be
54
+ * applied.
55
+ *
56
+ * @default false
57
+ */
58
+ ignoreMaxDimensions: boolean;
51
59
  }>, {
52
60
  /** `undefined` means the pop up is not currently showing. */
53
61
  showPopUpResult: ShowPopUpResult | undefined;
@@ -142,6 +142,7 @@ export const ViraDropdown = defineViraElement()({
142
142
  right: 24,
143
143
  },
144
144
  horizontalAnchor: inputs.horizontalAnchor || HorizontalAnchor.Both,
145
+ ignoreMaxDimensions: inputs.ignoreMaxDimensions,
145
146
  })}
146
147
  ${listen(ViraMenuTrigger.events.openChange, (event) => {
147
148
  updateState({ showPopUpResult: event.detail });
@@ -0,0 +1,8 @@
1
+ /**
2
+ * An icon that represents copying something.
3
+ *
4
+ * @category Icon
5
+ * @category SVG
6
+ * @see https://electrovir.github.io/vira/book/icons/copy24icon
7
+ */
8
+ export declare const Copy24Icon: import("../icon-svg.js").ViraIconSvg;
@@ -0,0 +1,41 @@
1
+ import { html } from 'element-vir';
2
+ import { viraIconCssVars } from '../icon-css-vars.js';
3
+ import { defineIcon } from '../icon-svg.js';
4
+ /**
5
+ * An icon that represents copying something.
6
+ *
7
+ * @category Icon
8
+ * @category SVG
9
+ * @see https://electrovir.github.io/vira/book/icons/copy24icon
10
+ */
11
+ export const Copy24Icon = defineIcon({
12
+ name: 'Copy24Icon',
13
+ svgTemplate: html `
14
+ <svg
15
+ xmlns="http://www.w3.org/2000/svg"
16
+ xml:space="preserve"
17
+ style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round"
18
+ viewBox="0 0 24 24"
19
+ width="24"
20
+ height="24"
21
+ >
22
+ <path
23
+ d="M16 6v8a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6q.2-1.8 2-2h8a2 2 0 0 1 2 2"
24
+ stroke="none"
25
+ fill=${viraIconCssVars['vira-icon-fill-color'].value}
26
+ />
27
+ <path
28
+ d="M21 11v8a2 2 0 0 1-2 2h-8a2 2 0 0 1-2-2v-8q.2-1.8 2-2h8a2 2 0 0 1 2 2"
29
+ stroke=${viraIconCssVars['vira-icon-stroke-color'].value}
30
+ stroke-width=${viraIconCssVars['vira-icon-stroke-width'].value}
31
+ fill=${viraIconCssVars['vira-icon-fill-color'].value}
32
+ />
33
+ <path
34
+ d="M7 16H6a2 2 0 0 1-2-2V6q.2-1.8 2-2h8a2 2 0 0 1 2 2v1"
35
+ stroke=${viraIconCssVars['vira-icon-stroke-color'].value}
36
+ stroke-width=${viraIconCssVars['vira-icon-stroke-width'].value}
37
+ fill="none"
38
+ />
39
+ </svg>
40
+ `,
41
+ });
@@ -8,6 +8,7 @@ export * from './icon-svgs/chevron-down-24.icon.js';
8
8
  export * from './icon-svgs/chevron-up-24.icon.js';
9
9
  export * from './icon-svgs/close-x-24.icon.js';
10
10
  export * from './icon-svgs/commit-24.icon.js';
11
+ export * from './icon-svgs/copy-24.icon.js';
11
12
  export * from './icon-svgs/document-24.icon.js';
12
13
  export * from './icon-svgs/document-search-24.icon.js';
13
14
  export * from './icon-svgs/double-chevron-24.icon.js';
@@ -51,6 +52,7 @@ export declare const allIconsByName: {
51
52
  readonly ChevronUp24Icon: import("./icon-svg.js").ViraIconSvg;
52
53
  readonly CloseX24Icon: import("./icon-svg.js").ViraIconSvg;
53
54
  readonly Commit24Icon: import("./icon-svg.js").ViraIconSvg;
55
+ readonly Copy24Icon: import("./icon-svg.js").ViraIconSvg;
54
56
  readonly Document24Icon: import("./icon-svg.js").ViraIconSvg;
55
57
  readonly DocumentSearch24Icon: import("./icon-svg.js").ViraIconSvg;
56
58
  readonly DoubleChevron24Icon: import("./icon-svg.js").ViraIconSvg;
@@ -6,6 +6,7 @@ import { ChevronDown24Icon } from './icon-svgs/chevron-down-24.icon.js';
6
6
  import { ChevronUp24Icon } from './icon-svgs/chevron-up-24.icon.js';
7
7
  import { CloseX24Icon } from './icon-svgs/close-x-24.icon.js';
8
8
  import { Commit24Icon } from './icon-svgs/commit-24.icon.js';
9
+ import { Copy24Icon } from './icon-svgs/copy-24.icon.js';
9
10
  import { Document24Icon } from './icon-svgs/document-24.icon.js';
10
11
  import { DocumentSearch24Icon } from './icon-svgs/document-search-24.icon.js';
11
12
  import { DoubleChevron24Icon } from './icon-svgs/double-chevron-24.icon.js';
@@ -45,6 +46,7 @@ export * from './icon-svgs/chevron-down-24.icon.js';
45
46
  export * from './icon-svgs/chevron-up-24.icon.js';
46
47
  export * from './icon-svgs/close-x-24.icon.js';
47
48
  export * from './icon-svgs/commit-24.icon.js';
49
+ export * from './icon-svgs/copy-24.icon.js';
48
50
  export * from './icon-svgs/document-24.icon.js';
49
51
  export * from './icon-svgs/document-search-24.icon.js';
50
52
  export * from './icon-svgs/double-chevron-24.icon.js';
@@ -88,6 +90,7 @@ export const allIconsByName = {
88
90
  ChevronUp24Icon,
89
91
  CloseX24Icon,
90
92
  Commit24Icon,
93
+ Copy24Icon,
91
94
  Document24Icon,
92
95
  DocumentSearch24Icon,
93
96
  DoubleChevron24Icon,
@@ -38,6 +38,21 @@ export type PopUpManagerOptions = {
38
38
  * @default 200
39
39
  */
40
40
  minDownSpace: number;
41
+ /**
42
+ * The minimum number of pixels for allowing the pop-up to go rightwards. If the rightward
43
+ * available space is less than this, and if the leftwards available space is
44
+ * `horizontalDiffThreshold` more than the rightwards space, the pop-up will be directed
45
+ * leftwards.
46
+ *
47
+ * Equation:
48
+ *
49
+ * const directLeftwards =
50
+ * rightwardsSpace < minRightSpace &&
51
+ * leftwardsSpace > rightwardsSpace + horizontalDiffThreshold;
52
+ *
53
+ * @default 400
54
+ */
55
+ minRightSpace: number;
41
56
  /**
42
57
  * The number of pixels required for the upwards available space to be bigger than the downwards
43
58
  * available space before directing the pop-up upwards.
@@ -51,6 +66,19 @@ export type PopUpManagerOptions = {
51
66
  * @default 20
52
67
  */
53
68
  verticalDiffThreshold: number;
69
+ /**
70
+ * The number of pixels required for the leftwards available space to be bigger than the
71
+ * rightwards available space before directing the pop-up leftwards.
72
+ *
73
+ * Equation:
74
+ *
75
+ * const directLeftwards =
76
+ * rightwardsSpace < minRightSpace &&
77
+ * leftwardsSpace > rightwardsSpace + horizontalDiffThreshold;
78
+ *
79
+ * @default 100
80
+ */
81
+ horizontalDiffThreshold: number;
54
82
  /**
55
83
  * Supports navigation of the pop up via the `device-navigation` package.
56
84
  *
@@ -70,6 +98,12 @@ export type ShowPopUpResult = {
70
98
  * the root element.
71
99
  */
72
100
  popDown: boolean;
101
+ /**
102
+ * Indicates if the "pop up" should pop in the rightwards direction or not. If not, it should
103
+ * pop in the leftwards direction. This is determined by how much space is available on either
104
+ * side of the root element.
105
+ */
106
+ popRight: boolean;
73
107
  positions: Record<'root' | 'container' | 'diff', PositionRect>;
74
108
  };
75
109
  declare const HidePopUpEvent_base: (new (eventInitDict?: EventInit) => import("typed-event-target").TypedEvent<"hide-pop-up">) & Pick<{
@@ -40,7 +40,9 @@ export class PopUpManager {
40
40
  listenTarget = new ListenTarget();
41
41
  options = {
42
42
  minDownSpace: 200,
43
+ minRightSpace: 400,
43
44
  verticalDiffThreshold: 20,
45
+ horizontalDiffThreshold: 100,
44
46
  supportNavigation: true,
45
47
  };
46
48
  cleanupCallbacks = [];
@@ -174,9 +176,12 @@ export class PopUpManager {
174
176
  });
175
177
  const useUp = diff.top > diff.bottom + currentOptions.verticalDiffThreshold &&
176
178
  diff.bottom < currentOptions.minDownSpace;
179
+ const useLeft = diff.left > diff.right + currentOptions.horizontalDiffThreshold &&
180
+ diff.right < currentOptions.minRightSpace;
177
181
  this.attachGlobalListeners(container);
178
182
  return {
179
183
  popDown: !useUp,
184
+ popRight: !useLeft,
180
185
  positions: {
181
186
  container: containerPosition,
182
187
  root: rootPosition,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vira",
3
- "version": "28.16.0",
3
+ "version": "28.18.0",
4
4
  "description": "A simple and highly versatile design system using element-vir.",
5
5
  "keywords": [
6
6
  "design",