vira 28.19.5 → 28.19.6

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 (188) hide show
  1. package/package.json +12 -12
  2. package/src/elements/define-vira-element.ts +29 -0
  3. package/src/elements/form/vira-form-fields.ts +140 -0
  4. package/src/elements/form/vira-form.element.ts +204 -0
  5. package/src/elements/pop-up/pop-up-helpers.ts +85 -0
  6. package/{dist/elements/pop-up/pop-up-menu-item.d.ts → src/elements/pop-up/pop-up-menu-item.ts} +4 -3
  7. package/{dist/elements/pop-up/vira-menu-item.element.js → src/elements/pop-up/vira-menu-item.element.ts} +28 -11
  8. package/src/elements/pop-up/vira-menu-trigger.element.ts +158 -0
  9. package/{dist/elements/pop-up/vira-menu.element.js → src/elements/pop-up/vira-menu.element.ts} +76 -49
  10. package/{dist/elements/pop-up/vira-pop-up-menu.element.js → src/elements/pop-up/vira-pop-up-menu.element.ts} +34 -22
  11. package/{dist/elements/pop-up/vira-pop-up-trigger.element.js → src/elements/pop-up/vira-pop-up-trigger.element.ts} +199 -104
  12. package/src/elements/shared-text-input-logic.ts +173 -0
  13. package/{dist/elements/vira-bold-text.element.js → src/elements/vira-bold-text.element.ts} +8 -7
  14. package/{dist/elements/vira-button.element.js → src/elements/vira-button.element.ts} +64 -33
  15. package/{dist/elements/vira-card.element.js → src/elements/vira-card.element.ts} +16 -13
  16. package/{dist/elements/vira-checkbox.element.js → src/elements/vira-checkbox.element.ts} +71 -28
  17. package/{dist/elements/vira-collapsible-wrapper.element.js → src/elements/vira-collapsible-wrapper.element.ts} +18 -16
  18. package/{dist/elements/vira-dropdown.element.js → src/elements/vira-dropdown.element.ts} +94 -48
  19. package/{dist/elements/vira-error.element.js → src/elements/vira-error.element.ts} +6 -5
  20. package/{dist/elements/vira-icon.element.js → src/elements/vira-icon.element.ts} +13 -6
  21. package/{dist/elements/vira-image.element.js → src/elements/vira-image.element.ts} +63 -43
  22. package/{dist/elements/vira-input.element.js → src/elements/vira-input.element.ts} +151 -85
  23. package/{dist/elements/vira-link.element.js → src/elements/vira-link.element.ts} +62 -11
  24. package/{dist/elements/vira-modal.element.js → src/elements/vira-modal.element.ts} +89 -50
  25. package/src/elements/vira-overflow-switch.element.ts +137 -0
  26. package/{dist/elements/vira-progress.element.js → src/elements/vira-progress.element.ts} +26 -11
  27. package/{dist/elements/vira-select.element.js → src/elements/vira-select.element.ts} +96 -41
  28. package/src/icons/icon-color.test-helper.ts +9 -0
  29. package/{dist/icons/icon-css-vars.js → src/icons/icon-css-vars.ts} +2 -1
  30. package/src/icons/icon-svg.ts +71 -0
  31. package/{dist/icons/icon-svgs/bell-24.icon.js → src/icons/icon-svgs/bell-24.icon.ts} +5 -4
  32. package/{dist/icons/icon-svgs/chat-24.icon.js → src/icons/icon-svgs/chat-24.icon.ts} +5 -4
  33. package/{dist/icons/icon-svgs/check-24.icon.js → src/icons/icon-svgs/check-24.icon.ts} +5 -4
  34. package/{dist/icons/icon-svgs/chevron-down-24.icon.js → src/icons/icon-svgs/chevron-down-24.icon.ts} +5 -4
  35. package/{dist/icons/icon-svgs/chevron-up-24.icon.js → src/icons/icon-svgs/chevron-up-24.icon.ts} +5 -4
  36. package/{dist/icons/icon-svgs/close-x-24.icon.js → src/icons/icon-svgs/close-x-24.icon.ts} +5 -4
  37. package/{dist/icons/icon-svgs/commit-24.icon.js → src/icons/icon-svgs/commit-24.icon.ts} +5 -4
  38. package/{dist/icons/icon-svgs/copy-24.icon.js → src/icons/icon-svgs/copy-24.icon.ts} +5 -4
  39. package/{dist/icons/icon-svgs/document-24.icon.js → src/icons/icon-svgs/document-24.icon.ts} +5 -4
  40. package/{dist/icons/icon-svgs/document-search-24.icon.js → src/icons/icon-svgs/document-search-24.icon.ts} +5 -4
  41. package/{dist/icons/icon-svgs/double-chevron-24.icon.js → src/icons/icon-svgs/double-chevron-24.icon.ts} +5 -4
  42. package/{dist/icons/icon-svgs/element-16.icon.js → src/icons/icon-svgs/element-16.icon.ts} +5 -4
  43. package/{dist/icons/icon-svgs/element-24.icon.js → src/icons/icon-svgs/element-24.icon.ts} +5 -4
  44. package/{dist/icons/icon-svgs/external-link-24.icon.js → src/icons/icon-svgs/external-link-24.icon.ts} +5 -4
  45. package/{dist/icons/icon-svgs/eye-closed-24.icon.js → src/icons/icon-svgs/eye-closed-24.icon.ts} +5 -4
  46. package/{dist/icons/icon-svgs/eye-open-24.icon.js → src/icons/icon-svgs/eye-open-24.icon.ts} +5 -4
  47. package/{dist/icons/icon-svgs/filter-24.icon.js → src/icons/icon-svgs/filter-24.icon.ts} +5 -4
  48. package/{dist/icons/icon-svgs/link-24.icon.js → src/icons/icon-svgs/link-24.icon.ts} +5 -4
  49. package/{dist/icons/icon-svgs/loader-24.icon.js → src/icons/icon-svgs/loader-24.icon.ts} +5 -4
  50. package/{dist/icons/icon-svgs/loader-animated-24.icon.js → src/icons/icon-svgs/loader-animated-24.icon.ts} +8 -6
  51. package/{dist/icons/icon-svgs/lock-24.icon.js → src/icons/icon-svgs/lock-24.icon.ts} +5 -4
  52. package/{dist/icons/icon-svgs/options-24.icon.js → src/icons/icon-svgs/options-24.icon.ts} +5 -4
  53. package/{dist/icons/icon-svgs/pencil-24.icon.js → src/icons/icon-svgs/pencil-24.icon.ts} +5 -4
  54. package/{dist/icons/icon-svgs/shield-24.icon.js → src/icons/icon-svgs/shield-24.icon.ts} +5 -4
  55. package/{dist/icons/icon-svgs/sort-ascending-24.icon.js → src/icons/icon-svgs/sort-ascending-24.icon.ts} +5 -4
  56. package/{dist/icons/icon-svgs/sort-descending-24.icon.js → src/icons/icon-svgs/sort-descending-24.icon.ts} +5 -4
  57. package/{dist/icons/icon-svgs/speaker-loud-24.icon.js → src/icons/icon-svgs/speaker-loud-24.icon.ts} +5 -4
  58. package/{dist/icons/icon-svgs/speaker-medium-24.icon.js → src/icons/icon-svgs/speaker-medium-24.icon.ts} +5 -4
  59. package/{dist/icons/icon-svgs/speaker-muted-24.icon.js → src/icons/icon-svgs/speaker-muted-24.icon.ts} +5 -4
  60. package/{dist/icons/icon-svgs/speaker-quiet-24.icon.js → src/icons/icon-svgs/speaker-quiet-24.icon.ts} +5 -4
  61. package/{dist/icons/icon-svgs/star-24.icon.js → src/icons/icon-svgs/star-24.icon.ts} +5 -4
  62. package/{dist/icons/icon-svgs/status-failure-24.icon.js → src/icons/icon-svgs/status-failure-24.icon.ts} +5 -4
  63. package/{dist/icons/icon-svgs/status-in-progress-24.icon.js → src/icons/icon-svgs/status-in-progress-24.icon.ts} +5 -4
  64. package/{dist/icons/icon-svgs/status-success-24.icon.js → src/icons/icon-svgs/status-success-24.icon.ts} +5 -4
  65. package/{dist/icons/icon-svgs/status-unknown-24.icon.js → src/icons/icon-svgs/status-unknown-24.icon.ts} +5 -4
  66. package/{dist/icons/icon-svgs/status-warning-24.icon.js → src/icons/icon-svgs/status-warning-24.icon.ts} +5 -4
  67. package/{dist/icons/icon-svgs/upload-24.icon.js → src/icons/icon-svgs/upload-24.icon.ts} +5 -4
  68. package/{dist/icons/icon-svgs/x-24.icon.js → src/icons/icon-svgs/x-24.icon.ts} +5 -4
  69. package/{dist/icons/index.js → src/icons/index.ts} +43 -39
  70. package/{dist/styles/border.js → src/styles/border.ts} +2 -1
  71. package/{dist/styles/disabled.js → src/styles/disabled.ts} +3 -2
  72. package/{dist/styles/durations.js → src/styles/durations.ts} +2 -1
  73. package/{dist/styles/focus.js → src/styles/focus.ts} +32 -8
  74. package/{dist/styles/font.js → src/styles/font.ts} +2 -1
  75. package/{dist/styles/form-styles.js → src/styles/form-styles.ts} +6 -1
  76. package/{dist/styles/index.js → src/styles/index.ts} +1 -0
  77. package/{dist/styles/native-styles.js → src/styles/native-styles.ts} +5 -3
  78. package/{dist/styles/scrollbar.js → src/styles/scrollbar.ts} +4 -3
  79. package/{dist/styles/shadows.js → src/styles/shadows.ts} +8 -6
  80. package/{dist/styles/user-select.js → src/styles/user-select.ts} +3 -2
  81. package/{dist/styles/vira-color-palette.js → src/styles/vira-color-palette.ts} +11 -1
  82. package/src/styles/vira-color-theme.ts +1142 -0
  83. package/src/util/define-table.ts +279 -0
  84. package/src/util/dynamic-element.ts +129 -0
  85. package/src/util/pop-up-manager.ts +380 -0
  86. package/dist/elements/define-vira-element.d.ts +0 -18
  87. package/dist/elements/define-vira-element.js +0 -19
  88. package/dist/elements/form/vira-form-fields.d.ts +0 -100
  89. package/dist/elements/form/vira-form-fields.js +0 -60
  90. package/dist/elements/form/vira-form.element.d.ts +0 -35
  91. package/dist/elements/form/vira-form.element.js +0 -151
  92. package/dist/elements/index.js +0 -25
  93. package/dist/elements/pop-up/pop-up-helpers.d.ts +0 -33
  94. package/dist/elements/pop-up/pop-up-helpers.js +0 -58
  95. package/dist/elements/pop-up/pop-up-menu-item.js +0 -1
  96. package/dist/elements/pop-up/vira-menu-item.element.d.ts +0 -19
  97. package/dist/elements/pop-up/vira-menu-trigger.element.d.ts +0 -41
  98. package/dist/elements/pop-up/vira-menu-trigger.element.js +0 -121
  99. package/dist/elements/pop-up/vira-menu.element.d.ts +0 -38
  100. package/dist/elements/pop-up/vira-pop-up-menu.element.d.ts +0 -35
  101. package/dist/elements/pop-up/vira-pop-up-trigger.element.d.ts +0 -105
  102. package/dist/elements/shared-text-input-logic.d.ts +0 -63
  103. package/dist/elements/shared-text-input-logic.js +0 -101
  104. package/dist/elements/vira-bold-text.element.d.ts +0 -9
  105. package/dist/elements/vira-button.element.d.ts +0 -31
  106. package/dist/elements/vira-card.element.d.ts +0 -18
  107. package/dist/elements/vira-checkbox.element.d.ts +0 -34
  108. package/dist/elements/vira-collapsible-wrapper.element.d.ts +0 -14
  109. package/dist/elements/vira-dropdown.element.d.ts +0 -46
  110. package/dist/elements/vira-error.element.d.ts +0 -7
  111. package/dist/elements/vira-icon.element.d.ts +0 -13
  112. package/dist/elements/vira-image.element.d.ts +0 -45
  113. package/dist/elements/vira-input.element.d.ts +0 -62
  114. package/dist/elements/vira-link.element.d.ts +0 -73
  115. package/dist/elements/vira-modal.element.d.ts +0 -39
  116. package/dist/elements/vira-overflow-switch.element.d.ts +0 -21
  117. package/dist/elements/vira-overflow-switch.element.js +0 -110
  118. package/dist/elements/vira-progress.element.d.ts +0 -18
  119. package/dist/elements/vira-select.element.d.ts +0 -48
  120. package/dist/icons/icon-color.test-helper.d.ts +0 -6
  121. package/dist/icons/icon-color.test-helper.js +0 -9
  122. package/dist/icons/icon-css-vars.d.ts +0 -14
  123. package/dist/icons/icon-svg.d.ts +0 -27
  124. package/dist/icons/icon-svg.js +0 -48
  125. package/dist/icons/icon-svgs/bell-24.icon.d.ts +0 -8
  126. package/dist/icons/icon-svgs/chat-24.icon.d.ts +0 -8
  127. package/dist/icons/icon-svgs/check-24.icon.d.ts +0 -8
  128. package/dist/icons/icon-svgs/chevron-down-24.icon.d.ts +0 -8
  129. package/dist/icons/icon-svgs/chevron-up-24.icon.d.ts +0 -8
  130. package/dist/icons/icon-svgs/close-x-24.icon.d.ts +0 -8
  131. package/dist/icons/icon-svgs/commit-24.icon.d.ts +0 -8
  132. package/dist/icons/icon-svgs/copy-24.icon.d.ts +0 -8
  133. package/dist/icons/icon-svgs/document-24.icon.d.ts +0 -8
  134. package/dist/icons/icon-svgs/document-search-24.icon.d.ts +0 -8
  135. package/dist/icons/icon-svgs/double-chevron-24.icon.d.ts +0 -8
  136. package/dist/icons/icon-svgs/element-16.icon.d.ts +0 -8
  137. package/dist/icons/icon-svgs/element-24.icon.d.ts +0 -8
  138. package/dist/icons/icon-svgs/external-link-24.icon.d.ts +0 -8
  139. package/dist/icons/icon-svgs/eye-closed-24.icon.d.ts +0 -8
  140. package/dist/icons/icon-svgs/eye-open-24.icon.d.ts +0 -8
  141. package/dist/icons/icon-svgs/filter-24.icon.d.ts +0 -8
  142. package/dist/icons/icon-svgs/link-24.icon.d.ts +0 -8
  143. package/dist/icons/icon-svgs/loader-24.icon.d.ts +0 -8
  144. package/dist/icons/icon-svgs/loader-animated-24.icon.d.ts +0 -8
  145. package/dist/icons/icon-svgs/lock-24.icon.d.ts +0 -8
  146. package/dist/icons/icon-svgs/options-24.icon.d.ts +0 -8
  147. package/dist/icons/icon-svgs/pencil-24.icon.d.ts +0 -8
  148. package/dist/icons/icon-svgs/shield-24.icon.d.ts +0 -8
  149. package/dist/icons/icon-svgs/sort-ascending-24.icon.d.ts +0 -8
  150. package/dist/icons/icon-svgs/sort-descending-24.icon.d.ts +0 -8
  151. package/dist/icons/icon-svgs/speaker-loud-24.icon.d.ts +0 -8
  152. package/dist/icons/icon-svgs/speaker-medium-24.icon.d.ts +0 -8
  153. package/dist/icons/icon-svgs/speaker-muted-24.icon.d.ts +0 -8
  154. package/dist/icons/icon-svgs/speaker-quiet-24.icon.d.ts +0 -8
  155. package/dist/icons/icon-svgs/star-24.icon.d.ts +0 -8
  156. package/dist/icons/icon-svgs/status-failure-24.icon.d.ts +0 -8
  157. package/dist/icons/icon-svgs/status-in-progress-24.icon.d.ts +0 -8
  158. package/dist/icons/icon-svgs/status-success-24.icon.d.ts +0 -8
  159. package/dist/icons/icon-svgs/status-unknown-24.icon.d.ts +0 -8
  160. package/dist/icons/icon-svgs/status-warning-24.icon.d.ts +0 -8
  161. package/dist/icons/icon-svgs/upload-24.icon.d.ts +0 -8
  162. package/dist/icons/icon-svgs/x-24.icon.d.ts +0 -8
  163. package/dist/icons/index.d.ts +0 -86
  164. package/dist/index.js +0 -4
  165. package/dist/styles/border.d.ts +0 -9
  166. package/dist/styles/disabled.d.ts +0 -6
  167. package/dist/styles/durations.d.ts +0 -22
  168. package/dist/styles/focus.d.ts +0 -31
  169. package/dist/styles/font.d.ts +0 -9
  170. package/dist/styles/form-styles.d.ts +0 -20
  171. package/dist/styles/index.d.ts +0 -13
  172. package/dist/styles/native-styles.d.ts +0 -13
  173. package/dist/styles/scrollbar.d.ts +0 -6
  174. package/dist/styles/shadows.d.ts +0 -20
  175. package/dist/styles/user-select.d.ts +0 -6
  176. package/dist/styles/vira-color-palette.d.ts +0 -95
  177. package/dist/styles/vira-color-theme.d.ts +0 -1184
  178. package/dist/styles/vira-color-theme.js +0 -1137
  179. package/dist/util/define-table.d.ts +0 -110
  180. package/dist/util/define-table.js +0 -115
  181. package/dist/util/dynamic-element.d.ts +0 -63
  182. package/dist/util/dynamic-element.js +0 -61
  183. package/dist/util/index.js +0 -3
  184. package/dist/util/pop-up-manager.d.ts +0 -186
  185. package/dist/util/pop-up-manager.js +0 -214
  186. /package/{dist/elements/index.d.ts → src/elements/index.ts} +0 -0
  187. /package/{dist/index.d.ts → src/index.ts} +0 -0
  188. /package/{dist/util/index.d.ts → src/util/index.ts} +0 -0
@@ -1,39 +1,94 @@
1
- import { assert } from '@augment-vir/assert';
2
- import { walkActiveElement } from '@augment-vir/web';
3
- import { NavController } from 'device-navigation';
4
- import { classMap, css, defineElementEvent, html, listen, renderIf } from 'element-vir';
5
- import { createFocusStyles } from '../../styles/focus.js';
6
- import { noNativeFormStyles, noUserSelect, viraDisabledStyles } from '../../styles/index.js';
7
- import { HidePopUpEvent, isInputLikeElement, NavSelectEvent, PopUpManager, } from '../../util/pop-up-manager.js';
8
- import { defineViraElement } from '../define-vira-element.js';
9
- import { triggerPopUpState } from './pop-up-helpers.js';
1
+ import {assert} from '@augment-vir/assert';
2
+ import {type PartialWithUndefined} from '@augment-vir/common';
3
+ import {walkActiveElement} from '@augment-vir/web';
4
+ import {NavController, type Coords} from 'device-navigation';
5
+ import {classMap, css, defineElementEvent, html, listen, renderIf} from 'element-vir';
6
+ import {createFocusStyles} from '../../styles/focus.js';
7
+ import {noNativeFormStyles, noUserSelect, viraDisabledStyles} from '../../styles/index.js';
8
+ import {
9
+ HidePopUpEvent,
10
+ isInputLikeElement,
11
+ NavSelectEvent,
12
+ PopUpManager,
13
+ type ShowPopUpResult,
14
+ } from '../../util/pop-up-manager.js';
15
+ import {defineViraElement} from '../define-vira-element.js';
16
+ import {triggerPopUpState} from './pop-up-helpers.js';
17
+
18
+ /**
19
+ * Offsets applied to any menu opened by {@link ViraPopUpTrigger}.
20
+ *
21
+ * @category Internal
22
+ */
23
+ export type PopUpOffset = PartialWithUndefined<{
24
+ vertical: number;
25
+ right: number;
26
+ left: number;
27
+ }>;
28
+
10
29
  /**
11
30
  * Anchor options for pop-ups.
12
31
  *
13
32
  * @category Internal
14
33
  */
15
- export var HorizontalAnchor;
16
- (function (HorizontalAnchor) {
34
+ export enum HorizontalAnchor {
17
35
  /**
18
36
  * The left side of the pop-up will be anchored to the left side of the trigger, allowing the
19
37
  * pop-up to grow on the right side of the trigger.
20
38
  */
21
- HorizontalAnchor["Left"] = "left";
39
+ Left = 'left',
22
40
  /**
23
41
  * The Right side of the pop-up will be anchored to the right side of the trigger, allowing the
24
42
  * pop-up to grow on the left side of the trigger.
25
43
  */
26
- HorizontalAnchor["Right"] = "right";
44
+ Right = 'right',
27
45
  /** Restrict the pop-up on both sides. */
28
- HorizontalAnchor["Both"] = "both";
46
+ Both = 'both',
29
47
  /**
30
48
  * Automatically choose left or right based on available space, defaulting to anchoring on the
31
49
  * left side.
32
50
  *
33
51
  * This is the default anchor for {@link ViraPopUpTrigger}.
34
52
  */
35
- HorizontalAnchor["Auto"] = "auto";
36
- })(HorizontalAnchor || (HorizontalAnchor = {}));
53
+ Auto = 'auto',
54
+ }
55
+
56
+ /**
57
+ * Configs for {@link ViraPopUpTrigger} pop-up positioning and sizing.
58
+ *
59
+ * @category Internal
60
+ */
61
+ export type PopUpTriggerPosition = {
62
+ /**
63
+ * - `HorizontalAnchor.Left`: pop-up is anchored to the left side of the trigger and the pop-up
64
+ * can grow to the right.
65
+ * - `HorizontalAnchor.Right`: pop-up is anchored to the right side of the trigger and the pop-up
66
+ * can grow to the left.
67
+ * - `HorizontalAnchor.Both`: pop-up is anchored on both sides of the trigger and cannot grow
68
+ * beyond it.
69
+ * - `HorizontalAnchor.Auto`: automatically choose left or right anchor based on available space,
70
+ * defaulting to left anchor. (This is the default experience.)
71
+ *
72
+ * Note that when `HorizontalAnchor.Both` is _not_ used, this anchor will cancel out any
73
+ * `popUpOffset` for the direction _opposite_ of the chosen anchor.
74
+ */
75
+ horizontalAnchor: HorizontalAnchor;
76
+ /**
77
+ * When `true`, the pop-up will not have its maximum width constrained to fit within the
78
+ * overflow container. The positioning logic (left/right) will still be applied.
79
+ *
80
+ * @default false
81
+ */
82
+ ignoreMaxWidth: boolean;
83
+ /**
84
+ * When `true`, the pop-up will not have its maximum height constrained to fit within the
85
+ * overflow container. The positioning logic (up/down) will still be applied.
86
+ *
87
+ * @default false
88
+ */
89
+ ignoreMaxHeight: boolean;
90
+ };
91
+
37
92
  /**
38
93
  * An element with slots for a pop-up trigger and pop-up contents.
39
94
  *
@@ -41,13 +96,25 @@ export var HorizontalAnchor;
41
96
  * @category Elements
42
97
  * @see https://electrovir.github.io/vira/book/elements/vira-pop-up-trigger
43
98
  */
44
- export const ViraPopUpTrigger = defineViraElement()({
99
+ export const ViraPopUpTrigger = defineViraElement<
100
+ PartialWithUndefined<
101
+ PopUpTriggerPosition & {
102
+ isDisabled: boolean;
103
+ /** For debugging purposes only. Very bad for actual production code use. */
104
+ z_debug_forceOpenState: boolean;
105
+ /** Set to `true` to keep the pop-up open if it is interacted with. */
106
+ keepOpenAfterInteraction: boolean;
107
+ /** All values in px. */
108
+ popUpOffset: PopUpOffset;
109
+ }
110
+ >
111
+ >()({
45
112
  tagName: 'vira-pop-up-trigger',
46
- state({ host }) {
113
+ state({host}) {
47
114
  return {
48
115
  /** `undefined` means the pop up is not currently showing. */
49
- showPopUpResult: undefined,
50
- popUpManager: new PopUpManager(new NavController(host, { activateOnMouseUp: true })),
116
+ showPopUpResult: undefined as ShowPopUpResult | undefined,
117
+ popUpManager: new PopUpManager(new NavController(host, {activateOnMouseUp: true})),
51
118
  };
52
119
  },
53
120
  slotNames: [
@@ -55,9 +122,9 @@ export const ViraPopUpTrigger = defineViraElement()({
55
122
  'popUp',
56
123
  ],
57
124
  hostClasses: {
58
- 'vira-pop-up-trigger-disabled': ({ inputs }) => !!inputs.isDisabled,
125
+ 'vira-pop-up-trigger-disabled': ({inputs}) => !!inputs.isDisabled,
59
126
  },
60
- styles: ({ hostClasses }) => css `
127
+ styles: ({hostClasses}) => css`
61
128
  :host {
62
129
  display: inline-flex;
63
130
  box-sizing: border-box;
@@ -75,8 +142,8 @@ export const ViraPopUpTrigger = defineViraElement()({
75
142
  box-sizing: border-box;
76
143
 
77
144
  ${createFocusStyles({
78
- elementBorderSize: 1,
79
- })}
145
+ elementBorderSize: 1,
146
+ })}
80
147
  }
81
148
 
82
149
  .dropdown-trigger {
@@ -119,26 +186,35 @@ export const ViraPopUpTrigger = defineViraElement()({
119
186
  }
120
187
  `,
121
188
  events: {
122
- navSelect: defineElementEvent(),
189
+ navSelect: defineElementEvent<Coords>(),
123
190
  /**
124
191
  * - `undefined` indicates that the pop-up just closed.
125
192
  * - {@link ShowPopUpResult} indicates that the pop-up just opened.
126
193
  */
127
- openChange: defineElementEvent(),
128
- init: defineElementEvent(),
194
+ openChange: defineElementEvent<ShowPopUpResult | undefined>(),
195
+ init: defineElementEvent<{
196
+ navController: NavController;
197
+ popUpManager: PopUpManager;
198
+ }>(),
129
199
  },
130
- cleanup({ state, updateState }) {
131
- updateState({ showPopUpResult: undefined });
200
+ cleanup({state, updateState}) {
201
+ updateState({showPopUpResult: undefined});
132
202
  state.popUpManager.destroy();
133
203
  },
134
- init({ state, updateState, host, inputs, dispatch, events }) {
204
+ init({state, updateState, host, inputs, dispatch, events}) {
135
205
  /** Refocus the trigger and set the result to `undefined` when the pop up closes. */
136
206
  state.popUpManager.listen(HidePopUpEvent, () => {
137
- updateState({ showPopUpResult: undefined });
207
+ updateState({showPopUpResult: undefined});
138
208
  dispatch(new events.openChange(undefined));
139
209
  if (!inputs.isDisabled) {
140
210
  const dropdownWrapper = host.shadowRoot.querySelector('.dropdown-wrapper');
141
- assert.instanceOf(dropdownWrapper, HTMLButtonElement, 'failed to find dropdown wrapper child');
211
+
212
+ assert.instanceOf(
213
+ dropdownWrapper,
214
+ HTMLButtonElement,
215
+ 'failed to find dropdown wrapper child',
216
+ );
217
+
142
218
  dropdownWrapper.focus();
143
219
  }
144
220
  });
@@ -157,13 +233,19 @@ export const ViraPopUpTrigger = defineViraElement()({
157
233
  }
158
234
  dispatch(new events.navSelect(event.detail));
159
235
  });
160
- dispatch(new events.init({
161
- navController: state.popUpManager.navController,
162
- popUpManager: state.popUpManager,
163
- }));
236
+
237
+ dispatch(
238
+ new events.init({
239
+ navController: state.popUpManager.navController,
240
+ popUpManager: state.popUpManager,
241
+ }),
242
+ );
164
243
  },
165
- render({ dispatch, events, state, inputs, updateState, host, slotNames }) {
166
- function triggerPopUp({ emitEvent, open }, event) {
244
+ render({dispatch, events, state, inputs, updateState, host, slotNames}) {
245
+ function triggerPopUp(
246
+ {emitEvent, open}: {emitEvent: boolean; open: boolean},
247
+ event: Event | undefined,
248
+ ) {
167
249
  if (state.showPopUpResult && inputs.keepOpenAfterInteraction && event) {
168
250
  const dropdownTrigger = host.shadowRoot.querySelector('.dropdown-trigger');
169
251
  if (dropdownTrigger && !event.composedPath().includes(dropdownTrigger)) {
@@ -177,7 +259,7 @@ export const ViraPopUpTrigger = defineViraElement()({
177
259
  triggerPopUpState({
178
260
  open,
179
261
  callback(showPopUpResult) {
180
- updateState({ showPopUpResult });
262
+ updateState({showPopUpResult});
181
263
  if (emitEvent) {
182
264
  dispatch(new events.openChange(showPopUpResult));
183
265
  }
@@ -186,53 +268,60 @@ export const ViraPopUpTrigger = defineViraElement()({
186
268
  popUpManager: state.popUpManager,
187
269
  });
188
270
  }
271
+
189
272
  if (inputs.isDisabled) {
190
- triggerPopUp({ open: false, emitEvent: false }, undefined);
191
- }
192
- else if (inputs.z_debug_forceOpenState != undefined) {
273
+ triggerPopUp({open: false, emitEvent: false}, undefined);
274
+ } else if (inputs.z_debug_forceOpenState != undefined) {
193
275
  if (!inputs.z_debug_forceOpenState && state.showPopUpResult) {
194
- triggerPopUp({ emitEvent: false, open: false }, undefined);
195
- }
196
- else if (inputs.z_debug_forceOpenState && !state.showPopUpResult) {
197
- triggerPopUp({ emitEvent: false, open: true }, undefined);
276
+ triggerPopUp({emitEvent: false, open: false}, undefined);
277
+ } else if (inputs.z_debug_forceOpenState && !state.showPopUpResult) {
278
+ triggerPopUp({emitEvent: false, open: true}, undefined);
198
279
  }
199
280
  }
281
+
200
282
  /**
201
283
  * Resolve the effective horizontal anchor. For Auto, use the popRight calculation from
202
284
  * showPopUpResult to determine whether to anchor left or right.
203
285
  */
204
- const effectiveHorizontalAnchor = inputs.horizontalAnchor === HorizontalAnchor.Auto ||
286
+ const effectiveHorizontalAnchor: HorizontalAnchor =
287
+ inputs.horizontalAnchor === HorizontalAnchor.Auto ||
205
288
  inputs.horizontalAnchor === undefined
206
- ? state.showPopUpResult?.popRight
207
- ? HorizontalAnchor.Left
208
- : HorizontalAnchor.Right
209
- : inputs.horizontalAnchor;
210
- const leftCss = effectiveHorizontalAnchor === HorizontalAnchor.Right && state.showPopUpResult
211
- ? inputs.ignoreMaxWidth
212
- ? css `
289
+ ? state.showPopUpResult?.popRight
290
+ ? HorizontalAnchor.Left
291
+ : HorizontalAnchor.Right
292
+ : inputs.horizontalAnchor;
293
+
294
+ const leftCss =
295
+ effectiveHorizontalAnchor === HorizontalAnchor.Right && state.showPopUpResult
296
+ ? inputs.ignoreMaxWidth
297
+ ? css`
213
298
  left: unset;
214
299
  `
215
- : css `
300
+ : css`
216
301
  left: -${state.showPopUpResult.positions.diff.left}px;
217
302
  `
218
- : css `
303
+ : css`
219
304
  left: ${inputs.popUpOffset?.left || 0}px;
220
305
  `;
221
- const rightCss = state.showPopUpResult && effectiveHorizontalAnchor === HorizontalAnchor.Left
222
- ? inputs.ignoreMaxWidth
223
- ? css `
306
+
307
+ const rightCss =
308
+ state.showPopUpResult && effectiveHorizontalAnchor === HorizontalAnchor.Left
309
+ ? inputs.ignoreMaxWidth
310
+ ? css`
224
311
  right: unset;
225
312
  `
226
- : css `
313
+ : css`
227
314
  right: -${state.showPopUpResult.positions.diff.right}px;
228
315
  `
229
- : css `
316
+ : css`
230
317
  right: ${inputs.popUpOffset?.right || 0}px;
231
318
  `;
232
- const horizontalPositionStyle = css `
319
+
320
+ const horizontalPositionStyle = css`
233
321
  ${leftCss}
234
322
  ${rightCss}
235
323
  `;
324
+
236
325
  /**
237
326
  * These styles do _not_ account for window resizing while the menu is open. I decided this
238
327
  * was not a major enough problem to tackle. If it becomes major enough in the future,
@@ -242,72 +331,75 @@ export const ViraPopUpTrigger = defineViraElement()({
242
331
  const positionerStyles = state.showPopUpResult
243
332
  ? state.showPopUpResult.popDown
244
333
  ? /** Dropdown going down position. */
245
- inputs.ignoreMaxHeight
246
- ? css `
334
+ inputs.ignoreMaxHeight
335
+ ? css`
247
336
  bottom: unset;
248
337
  top: calc(100% + ${inputs.popUpOffset?.vertical || 0}px);
249
338
  ${horizontalPositionStyle}
250
339
  `
251
- : css `
340
+ : css`
252
341
  bottom: -${state.showPopUpResult.positions.diff.bottom}px;
253
342
  top: calc(100% + ${inputs.popUpOffset?.vertical || 0}px);
254
343
  ${horizontalPositionStyle}
255
344
  `
256
345
  : /** Dropdown going up position. */
257
- inputs.ignoreMaxHeight
258
- ? css `
346
+ inputs.ignoreMaxHeight
347
+ ? css`
259
348
  top: unset;
260
349
  bottom: calc(100% + ${inputs.popUpOffset?.vertical || 0}px);
261
350
  ${horizontalPositionStyle}
262
351
  `
263
- : css `
352
+ : css`
264
353
  top: -${state.showPopUpResult.positions.diff.top}px;
265
354
  bottom: calc(100% + ${inputs.popUpOffset?.vertical || 0}px);
266
355
  ${horizontalPositionStyle}
267
356
  `
268
357
  : undefined;
269
- function respondToClick(event) {
270
- triggerPopUp({ emitEvent: true, open: !state.showPopUpResult }, event);
358
+
359
+ function respondToClick(event: Event) {
360
+ triggerPopUp({emitEvent: true, open: !state.showPopUpResult}, event);
271
361
  }
272
- return html `
362
+
363
+ return html`
273
364
  <button
274
365
  ?disabled=${!!inputs.isDisabled}
275
366
  class="dropdown-wrapper ${classMap({
276
- open: !!state.showPopUpResult,
277
- 'open-upwards': !state.showPopUpResult?.popDown,
278
- })}"
367
+ open: !!state.showPopUpResult,
368
+ 'open-upwards': !state.showPopUpResult?.popDown,
369
+ })}"
279
370
  role="listbox"
280
371
  aria-expanded=${!!state.showPopUpResult}
281
372
  ${listen('keydown', (event) => {
282
- if (!state.showPopUpResult && event.code.startsWith('Arrow')) {
283
- triggerPopUp({ emitEvent: true, open: true }, event);
284
- }
285
- })}
286
- ${listen('click', (event) => {
287
- /** Detail is 0 if it was a keyboard key (like Enter) that triggered this click. */
288
- if (event.detail === 0) {
289
- let isTextActiveElement = false;
290
- walkActiveElement(({ element }) => {
291
- if (isInputLikeElement(element)) {
292
- isTextActiveElement = true;
293
- return true;
373
+ if (!state.showPopUpResult && event.code.startsWith('Arrow')) {
374
+ triggerPopUp({emitEvent: true, open: true}, event);
294
375
  }
295
- else {
296
- return false;
376
+ })}
377
+ ${listen('click', (event) => {
378
+ /** Detail is 0 if it was a keyboard key (like Enter) that triggered this click. */
379
+ if (event.detail === 0) {
380
+ let isTextActiveElement = false as boolean;
381
+
382
+ walkActiveElement(({element}) => {
383
+ if (isInputLikeElement(element)) {
384
+ isTextActiveElement = true;
385
+ return true;
386
+ } else {
387
+ return false;
388
+ }
389
+ });
390
+ if (isTextActiveElement) {
391
+ return;
392
+ }
393
+
394
+ respondToClick(event);
297
395
  }
298
- });
299
- if (isTextActiveElement) {
300
- return;
301
- }
302
- respondToClick(event);
303
- }
304
- })}
396
+ })}
305
397
  ${listen('mousedown', (event) => {
306
- /** Ignore any clicks that aren't the main button. */
307
- if (event.button === 0) {
308
- respondToClick(event);
309
- }
310
- })}
398
+ /** Ignore any clicks that aren't the main button. */
399
+ if (event.button === 0) {
400
+ respondToClick(event);
401
+ }
402
+ })}
311
403
  >
312
404
  <div class="dropdown-trigger">
313
405
  <slot name=${slotNames.trigger}></slot>
@@ -315,13 +407,16 @@ export const ViraPopUpTrigger = defineViraElement()({
315
407
 
316
408
  <div
317
409
  class="pop-up-positioner ${classMap({
318
- 'right-aligned': effectiveHorizontalAnchor === HorizontalAnchor.Right,
319
- })}"
410
+ 'right-aligned': effectiveHorizontalAnchor === HorizontalAnchor.Right,
411
+ })}"
320
412
  style=${positionerStyles}
321
413
  >
322
- ${renderIf(!!state.showPopUpResult, html `
414
+ ${renderIf(
415
+ !!state.showPopUpResult,
416
+ html`
323
417
  <slot name=${slotNames.popUp}></slot>
324
- `)}
418
+ `,
419
+ )}
325
420
  </div>
326
421
  </button>
327
422
  `;
@@ -0,0 +1,173 @@
1
+ import {check, checkWrap} from '@augment-vir/assert';
2
+ import {type PartialWithUndefined} from '@augment-vir/common';
3
+ import {extractEventTarget} from '@augment-vir/web';
4
+ import {type AttributeValues} from 'element-vir';
5
+
6
+ /**
7
+ * Inputs shared between the multiple input elements.
8
+ *
9
+ * @category Internal
10
+ */
11
+ export type SharedTextInputElementInputs = {
12
+ value: string;
13
+ } & PartialWithUndefined<{
14
+ /** Shown when no other text is present. Input restrictions do not apply to this property. */
15
+ placeholder: string;
16
+ /** Set to true to trigger disabled styles and to block all user input. */
17
+ disabled: boolean;
18
+ /**
19
+ * Only letters in the given string or matches to the given RegExp will be allowed.
20
+ * blockedInputs takes precedence over this input.
21
+ *
22
+ * For example: if allowedInputs is set to "abcd" and blockedInputs is set to "d", only "a",
23
+ * "b", or "c" letters will be allowed.
24
+ */
25
+ allowedInputs: string | RegExp;
26
+ /** Any letters in the given string or matches to the given RegExp will be blocked. */
27
+ blockedInputs: string | RegExp;
28
+ /** Disable all browser helps like spellchecking, autocomplete, etc. */
29
+ disableBrowserHelps: boolean;
30
+ /** Set this to true to make the whole element size to only fit the input text. */
31
+ fitText: boolean;
32
+ /** A set of attributes that will be applied to the inner native text element. */
33
+ attributePassthrough: AttributeValues;
34
+ }>;
35
+
36
+ function doesMatch({input, matcher}: {input: string; matcher: string | RegExp}): boolean {
37
+ if (!input || !matcher) {
38
+ return true;
39
+ }
40
+ if (input.length > 1) {
41
+ return input.split('').every((singleInput) => doesMatch({input: singleInput, matcher}));
42
+ }
43
+
44
+ if (matcher instanceof RegExp) {
45
+ return !!input.match(matcher);
46
+ } else {
47
+ return matcher.includes(input);
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Inputs used to check if the current input element value is allowed.
53
+ *
54
+ * @category Internal
55
+ */
56
+ export type IsAllowedInputs = {
57
+ value: string;
58
+ allowed: string | RegExp | undefined;
59
+ blocked: string | RegExp | undefined;
60
+ };
61
+
62
+ function isAllowed({value, allowed, blocked}: IsAllowedInputs) {
63
+ const isAllowedCharacter = allowed
64
+ ? doesMatch({
65
+ input: value,
66
+ matcher: allowed,
67
+ })
68
+ : true;
69
+ const isBlockedCharacter = blocked
70
+ ? doesMatch({
71
+ input: value,
72
+ matcher: blocked,
73
+ })
74
+ : false;
75
+
76
+ return isAllowedCharacter && !isBlockedCharacter;
77
+ }
78
+
79
+ /**
80
+ * Filters out blocked text from an input element's value.
81
+ *
82
+ * @category Internal
83
+ */
84
+ export function filterTextInputValue(inputs: IsAllowedInputs): {
85
+ filtered: string;
86
+ blocked: string;
87
+ } {
88
+ if (!inputs.value) {
89
+ return {filtered: inputs.value, blocked: ''};
90
+ }
91
+ const {filtered, blocked} = inputs.value.split('').reduce(
92
+ (accum, letter) => {
93
+ const allowed = isAllowed({...inputs, value: letter});
94
+
95
+ if (allowed) {
96
+ accum.filtered.push(letter);
97
+ } else {
98
+ accum.blocked.push(letter);
99
+ }
100
+ return accum;
101
+ },
102
+ {
103
+ filtered: [] as string[],
104
+ blocked: [] as string[],
105
+ },
106
+ );
107
+
108
+ return {
109
+ filtered: filtered.join(''),
110
+ blocked: blocked.join(''),
111
+ };
112
+ }
113
+
114
+ /**
115
+ * A function to be called when an input element's value changes.
116
+ *
117
+ * @category Internal
118
+ */
119
+ export function textInputListener({
120
+ inputs,
121
+ previousValue,
122
+ event,
123
+ inputBlockedCallback,
124
+ newValueCallback,
125
+ }: {
126
+ inputs: SharedTextInputElementInputs;
127
+ /** The value of the input element before this listener fired. */
128
+ previousValue: string;
129
+ event: Event;
130
+ inputBlockedCallback: (blockedInput: string) => void;
131
+ newValueCallback: (newValue: string) => void;
132
+ }) {
133
+ const inputElement = extractEventTarget(event, HTMLInputElement);
134
+ /**
135
+ * This is usually a single character, but can be a bunch of characters in some circumstances.
136
+ * For example, when a bunch of characters are pasted, this will be the entire pasted contents.
137
+ *
138
+ * When a password manager auto fills the password, at least for Safari + iCloud Keychain, it'll
139
+ * fire a `CustomEvent` (rather than the typical `InputEvent`) and `event.data` won't be
140
+ * populated.
141
+ */
142
+ const changedText: string =
143
+ (check.hasKey(event, 'data') && checkWrap.isString(event.data)) || '';
144
+
145
+ /**
146
+ * When changedText is falsy, that means an operation other than inserting characters happened.
147
+ * Such as: deleting, cutting the text, etc.
148
+ */
149
+ if (changedText) {
150
+ const {blocked} = filterTextInputValue({
151
+ value: changedText,
152
+ allowed: inputs.allowedInputs,
153
+ blocked: inputs.blockedInputs,
154
+ });
155
+ if (blocked.length) {
156
+ inputBlockedCallback(blocked);
157
+ }
158
+ }
159
+
160
+ const finalValue = filterTextInputValue({
161
+ value: inputElement.value,
162
+ allowed: inputs.allowedInputs,
163
+ blocked: inputs.blockedInputs,
164
+ }).filtered;
165
+
166
+ if (inputElement.value !== finalValue) {
167
+ // this prevents blocked inputs by simply overwriting them
168
+ inputElement.value = finalValue;
169
+ }
170
+ if (previousValue !== finalValue) {
171
+ newValueCallback(finalValue);
172
+ }
173
+ }
@@ -1,19 +1,20 @@
1
- import { css, html } from 'element-vir';
2
- import { defineViraElement } from './define-vira-element.js';
1
+ import {css, html} from 'element-vir';
2
+ import {defineViraElement} from './define-vira-element.js';
3
+
3
4
  /**
4
5
  * Use this element to reserve space for bolded text, even if it isn't bold yet.
5
6
  *
6
7
  * @category Elements
7
8
  */
8
- export const ViraBoldText = defineViraElement()({
9
+ export const ViraBoldText = defineViraElement<{bold: boolean; text: string}>()({
9
10
  tagName: 'vira-bold',
10
11
  cssVars: {
11
12
  'vira-bold-bold-weight': 'bold',
12
13
  },
13
14
  hostClasses: {
14
- 'vira-bold-bold': ({ inputs }) => inputs.bold,
15
+ 'vira-bold-bold': ({inputs}) => inputs.bold,
15
16
  },
16
- styles: ({ hostClasses, cssVars }) => css `
17
+ styles: ({hostClasses, cssVars}) => css`
17
18
  span {
18
19
  text-decoration: inherit;
19
20
  white-space: inherit;
@@ -51,8 +52,8 @@ export const ViraBoldText = defineViraElement()({
51
52
  z-index: unset;
52
53
  }
53
54
  `,
54
- render({ inputs }) {
55
- return html `
55
+ render({inputs}) {
56
+ return html`
56
57
  <span class="everything-wrapper">
57
58
  <span class="bold-wrapper">
58
59
  <span class="bold">${inputs.text}</span>