vira 28.19.4 → 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 +13 -13
  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} +200 -91
  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 -180
  185. package/dist/util/pop-up-manager.js +0 -203
  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,38 +1,94 @@
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 } from '../../styles/focus.js';
5
- import { noNativeFormStyles, noUserSelect, viraDisabledStyles } from '../../styles/index.js';
6
- import { HidePopUpEvent, NavSelectEvent, PopUpManager, } from '../../util/pop-up-manager.js';
7
- import { defineViraElement } from '../define-vira-element.js';
8
- 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
+
9
29
  /**
10
30
  * Anchor options for pop-ups.
11
31
  *
12
32
  * @category Internal
13
33
  */
14
- export var HorizontalAnchor;
15
- (function (HorizontalAnchor) {
34
+ export enum HorizontalAnchor {
16
35
  /**
17
36
  * The left side of the pop-up will be anchored to the left side of the trigger, allowing the
18
37
  * pop-up to grow on the right side of the trigger.
19
38
  */
20
- HorizontalAnchor["Left"] = "left";
39
+ Left = 'left',
21
40
  /**
22
41
  * The Right side of the pop-up will be anchored to the right side of the trigger, allowing the
23
42
  * pop-up to grow on the left side of the trigger.
24
43
  */
25
- HorizontalAnchor["Right"] = "right";
44
+ Right = 'right',
26
45
  /** Restrict the pop-up on both sides. */
27
- HorizontalAnchor["Both"] = "both";
46
+ Both = 'both',
28
47
  /**
29
48
  * Automatically choose left or right based on available space, defaulting to anchoring on the
30
49
  * left side.
31
50
  *
32
51
  * This is the default anchor for {@link ViraPopUpTrigger}.
33
52
  */
34
- HorizontalAnchor["Auto"] = "auto";
35
- })(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
+
36
92
  /**
37
93
  * An element with slots for a pop-up trigger and pop-up contents.
38
94
  *
@@ -40,13 +96,25 @@ export var HorizontalAnchor;
40
96
  * @category Elements
41
97
  * @see https://electrovir.github.io/vira/book/elements/vira-pop-up-trigger
42
98
  */
43
- 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
+ >()({
44
112
  tagName: 'vira-pop-up-trigger',
45
- state({ host }) {
113
+ state({host}) {
46
114
  return {
47
115
  /** `undefined` means the pop up is not currently showing. */
48
- showPopUpResult: undefined,
49
- popUpManager: new PopUpManager(new NavController(host, { activateOnMouseUp: true })),
116
+ showPopUpResult: undefined as ShowPopUpResult | undefined,
117
+ popUpManager: new PopUpManager(new NavController(host, {activateOnMouseUp: true})),
50
118
  };
51
119
  },
52
120
  slotNames: [
@@ -54,9 +122,9 @@ export const ViraPopUpTrigger = defineViraElement()({
54
122
  'popUp',
55
123
  ],
56
124
  hostClasses: {
57
- 'vira-pop-up-trigger-disabled': ({ inputs }) => !!inputs.isDisabled,
125
+ 'vira-pop-up-trigger-disabled': ({inputs}) => !!inputs.isDisabled,
58
126
  },
59
- styles: ({ hostClasses }) => css `
127
+ styles: ({hostClasses}) => css`
60
128
  :host {
61
129
  display: inline-flex;
62
130
  box-sizing: border-box;
@@ -74,8 +142,8 @@ export const ViraPopUpTrigger = defineViraElement()({
74
142
  box-sizing: border-box;
75
143
 
76
144
  ${createFocusStyles({
77
- elementBorderSize: 1,
78
- })}
145
+ elementBorderSize: 1,
146
+ })}
79
147
  }
80
148
 
81
149
  .dropdown-trigger {
@@ -118,26 +186,35 @@ export const ViraPopUpTrigger = defineViraElement()({
118
186
  }
119
187
  `,
120
188
  events: {
121
- navSelect: defineElementEvent(),
189
+ navSelect: defineElementEvent<Coords>(),
122
190
  /**
123
191
  * - `undefined` indicates that the pop-up just closed.
124
192
  * - {@link ShowPopUpResult} indicates that the pop-up just opened.
125
193
  */
126
- openChange: defineElementEvent(),
127
- init: defineElementEvent(),
194
+ openChange: defineElementEvent<ShowPopUpResult | undefined>(),
195
+ init: defineElementEvent<{
196
+ navController: NavController;
197
+ popUpManager: PopUpManager;
198
+ }>(),
128
199
  },
129
- cleanup({ state, updateState }) {
130
- updateState({ showPopUpResult: undefined });
200
+ cleanup({state, updateState}) {
201
+ updateState({showPopUpResult: undefined});
131
202
  state.popUpManager.destroy();
132
203
  },
133
- init({ state, updateState, host, inputs, dispatch, events }) {
204
+ init({state, updateState, host, inputs, dispatch, events}) {
134
205
  /** Refocus the trigger and set the result to `undefined` when the pop up closes. */
135
206
  state.popUpManager.listen(HidePopUpEvent, () => {
136
- updateState({ showPopUpResult: undefined });
207
+ updateState({showPopUpResult: undefined});
137
208
  dispatch(new events.openChange(undefined));
138
209
  if (!inputs.isDisabled) {
139
210
  const dropdownWrapper = host.shadowRoot.querySelector('.dropdown-wrapper');
140
- 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
+
141
218
  dropdownWrapper.focus();
142
219
  }
143
220
  });
@@ -156,13 +233,19 @@ export const ViraPopUpTrigger = defineViraElement()({
156
233
  }
157
234
  dispatch(new events.navSelect(event.detail));
158
235
  });
159
- dispatch(new events.init({
160
- navController: state.popUpManager.navController,
161
- popUpManager: state.popUpManager,
162
- }));
236
+
237
+ dispatch(
238
+ new events.init({
239
+ navController: state.popUpManager.navController,
240
+ popUpManager: state.popUpManager,
241
+ }),
242
+ );
163
243
  },
164
- render({ dispatch, events, state, inputs, updateState, host, slotNames }) {
165
- 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
+ ) {
166
249
  if (state.showPopUpResult && inputs.keepOpenAfterInteraction && event) {
167
250
  const dropdownTrigger = host.shadowRoot.querySelector('.dropdown-trigger');
168
251
  if (dropdownTrigger && !event.composedPath().includes(dropdownTrigger)) {
@@ -176,7 +259,7 @@ export const ViraPopUpTrigger = defineViraElement()({
176
259
  triggerPopUpState({
177
260
  open,
178
261
  callback(showPopUpResult) {
179
- updateState({ showPopUpResult });
262
+ updateState({showPopUpResult});
180
263
  if (emitEvent) {
181
264
  dispatch(new events.openChange(showPopUpResult));
182
265
  }
@@ -185,53 +268,60 @@ export const ViraPopUpTrigger = defineViraElement()({
185
268
  popUpManager: state.popUpManager,
186
269
  });
187
270
  }
271
+
188
272
  if (inputs.isDisabled) {
189
- triggerPopUp({ open: false, emitEvent: false }, undefined);
190
- }
191
- else if (inputs.z_debug_forceOpenState != undefined) {
273
+ triggerPopUp({open: false, emitEvent: false}, undefined);
274
+ } else if (inputs.z_debug_forceOpenState != undefined) {
192
275
  if (!inputs.z_debug_forceOpenState && state.showPopUpResult) {
193
- triggerPopUp({ emitEvent: false, open: false }, undefined);
194
- }
195
- else if (inputs.z_debug_forceOpenState && !state.showPopUpResult) {
196
- 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);
197
279
  }
198
280
  }
281
+
199
282
  /**
200
283
  * Resolve the effective horizontal anchor. For Auto, use the popRight calculation from
201
284
  * showPopUpResult to determine whether to anchor left or right.
202
285
  */
203
- const effectiveHorizontalAnchor = inputs.horizontalAnchor === HorizontalAnchor.Auto ||
286
+ const effectiveHorizontalAnchor: HorizontalAnchor =
287
+ inputs.horizontalAnchor === HorizontalAnchor.Auto ||
204
288
  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.ignoreMaxWidth
211
- ? 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`
212
298
  left: unset;
213
299
  `
214
- : css `
300
+ : css`
215
301
  left: -${state.showPopUpResult.positions.diff.left}px;
216
302
  `
217
- : css `
303
+ : css`
218
304
  left: ${inputs.popUpOffset?.left || 0}px;
219
305
  `;
220
- const rightCss = state.showPopUpResult && effectiveHorizontalAnchor === HorizontalAnchor.Left
221
- ? inputs.ignoreMaxWidth
222
- ? css `
306
+
307
+ const rightCss =
308
+ state.showPopUpResult && effectiveHorizontalAnchor === HorizontalAnchor.Left
309
+ ? inputs.ignoreMaxWidth
310
+ ? css`
223
311
  right: unset;
224
312
  `
225
- : css `
313
+ : css`
226
314
  right: -${state.showPopUpResult.positions.diff.right}px;
227
315
  `
228
- : css `
316
+ : css`
229
317
  right: ${inputs.popUpOffset?.right || 0}px;
230
318
  `;
231
- const horizontalPositionStyle = css `
319
+
320
+ const horizontalPositionStyle = css`
232
321
  ${leftCss}
233
322
  ${rightCss}
234
323
  `;
324
+
235
325
  /**
236
326
  * These styles do _not_ account for window resizing while the menu is open. I decided this
237
327
  * was not a major enough problem to tackle. If it becomes major enough in the future,
@@ -241,59 +331,75 @@ export const ViraPopUpTrigger = defineViraElement()({
241
331
  const positionerStyles = state.showPopUpResult
242
332
  ? state.showPopUpResult.popDown
243
333
  ? /** Dropdown going down position. */
244
- inputs.ignoreMaxHeight
245
- ? css `
334
+ inputs.ignoreMaxHeight
335
+ ? css`
246
336
  bottom: unset;
247
337
  top: calc(100% + ${inputs.popUpOffset?.vertical || 0}px);
248
338
  ${horizontalPositionStyle}
249
339
  `
250
- : css `
340
+ : css`
251
341
  bottom: -${state.showPopUpResult.positions.diff.bottom}px;
252
342
  top: calc(100% + ${inputs.popUpOffset?.vertical || 0}px);
253
343
  ${horizontalPositionStyle}
254
344
  `
255
345
  : /** Dropdown going up position. */
256
- inputs.ignoreMaxHeight
257
- ? css `
346
+ inputs.ignoreMaxHeight
347
+ ? css`
258
348
  top: unset;
259
349
  bottom: calc(100% + ${inputs.popUpOffset?.vertical || 0}px);
260
350
  ${horizontalPositionStyle}
261
351
  `
262
- : css `
352
+ : css`
263
353
  top: -${state.showPopUpResult.positions.diff.top}px;
264
354
  bottom: calc(100% + ${inputs.popUpOffset?.vertical || 0}px);
265
355
  ${horizontalPositionStyle}
266
356
  `
267
357
  : undefined;
268
- function respondToClick(event) {
269
- triggerPopUp({ emitEvent: true, open: !state.showPopUpResult }, event);
358
+
359
+ function respondToClick(event: Event) {
360
+ triggerPopUp({emitEvent: true, open: !state.showPopUpResult}, event);
270
361
  }
271
- return html `
362
+
363
+ return html`
272
364
  <button
273
365
  ?disabled=${!!inputs.isDisabled}
274
366
  class="dropdown-wrapper ${classMap({
275
- open: !!state.showPopUpResult,
276
- 'open-upwards': !state.showPopUpResult?.popDown,
277
- })}"
367
+ open: !!state.showPopUpResult,
368
+ 'open-upwards': !state.showPopUpResult?.popDown,
369
+ })}"
278
370
  role="listbox"
279
371
  aria-expanded=${!!state.showPopUpResult}
280
372
  ${listen('keydown', (event) => {
281
- if (!state.showPopUpResult && event.code.startsWith('Arrow')) {
282
- triggerPopUp({ emitEvent: true, open: true }, event);
283
- }
284
- })}
373
+ if (!state.showPopUpResult && event.code.startsWith('Arrow')) {
374
+ triggerPopUp({emitEvent: true, open: true}, event);
375
+ }
376
+ })}
285
377
  ${listen('click', (event) => {
286
- /** Detail is 0 if it was a keyboard key (like Enter) that triggered this click. */
287
- if (event.detail === 0) {
288
- respondToClick(event);
289
- }
290
- })}
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);
395
+ }
396
+ })}
291
397
  ${listen('mousedown', (event) => {
292
- /** Ignore any clicks that aren't the main button. */
293
- if (event.button === 0) {
294
- respondToClick(event);
295
- }
296
- })}
398
+ /** Ignore any clicks that aren't the main button. */
399
+ if (event.button === 0) {
400
+ respondToClick(event);
401
+ }
402
+ })}
297
403
  >
298
404
  <div class="dropdown-trigger">
299
405
  <slot name=${slotNames.trigger}></slot>
@@ -301,13 +407,16 @@ export const ViraPopUpTrigger = defineViraElement()({
301
407
 
302
408
  <div
303
409
  class="pop-up-positioner ${classMap({
304
- 'right-aligned': effectiveHorizontalAnchor === HorizontalAnchor.Right,
305
- })}"
410
+ 'right-aligned': effectiveHorizontalAnchor === HorizontalAnchor.Right,
411
+ })}"
306
412
  style=${positionerStyles}
307
413
  >
308
- ${renderIf(!!state.showPopUpResult, html `
414
+ ${renderIf(
415
+ !!state.showPopUpResult,
416
+ html`
309
417
  <slot name=${slotNames.popUp}></slot>
310
- `)}
418
+ `,
419
+ )}
311
420
  </div>
312
421
  </button>
313
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>