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,5 +1,28 @@
1
- import { attributes, css, html, ifDefined, listen, } from 'element-vir';
2
- import { defineViraElement } from './define-vira-element.js';
1
+ import {type PartialWithUndefined} from '@augment-vir/common';
2
+ import {
3
+ attributes,
4
+ css,
5
+ html,
6
+ ifDefined,
7
+ listen,
8
+ type AttributeValues,
9
+ type CSSResult,
10
+ } from 'element-vir';
11
+ import {type SpaRoute, type SpaRouter} from 'spa-router-vir';
12
+ import {type RequireExactlyOne} from 'type-fest';
13
+ import {defineViraElement} from './define-vira-element.js';
14
+
15
+ /**
16
+ * The route properties required for using {@link ViraLink} with a route.
17
+ *
18
+ * @category Internal
19
+ */
20
+ export type ViraLinkRoute = Readonly<{
21
+ route: SpaRoute<any, any, any>;
22
+ router: Pick<SpaRouter<any, any, any>, 'createRouteUrl' | 'setRouteOnDirectNavigation'>;
23
+ scrollToTop?: boolean;
24
+ }>;
25
+
3
26
  /**
4
27
  * A hyperlink wrapper element that can be configured to emit route change events rather than just
5
28
  * being a raw link.
@@ -8,12 +31,34 @@ import { defineViraElement } from './define-vira-element.js';
8
31
  * @category Elements
9
32
  * @see https://electrovir.github.io/vira/book/elements/vira-link
10
33
  */
11
- export const ViraLink = defineViraElement()({
34
+ export const ViraLink = defineViraElement<
35
+ RequireExactlyOne<{
36
+ /**
37
+ * A full raw URL link that will navigate the current window away or open a new tab. If this
38
+ * property is provided for the inputs, don't provide a route property.
39
+ */
40
+ link: {
41
+ url: string;
42
+ newTab: boolean;
43
+ };
44
+ /**
45
+ * A route that'll change that current page without navigating the window. If this property
46
+ * is provided for the inputs, don't provide a link property.
47
+ */
48
+ route: ViraLinkRoute;
49
+ }> &
50
+ PartialWithUndefined<{
51
+ /** Styles that will be applied directly to the inner elements. */
52
+ stylePassthrough: Readonly<PartialWithUndefined<{a: CSSResult}>>;
53
+ /** Attributes that will be applied directly to the inner elements. */
54
+ attributePassthrough: Readonly<PartialWithUndefined<{a: AttributeValues}>>;
55
+ }>
56
+ >()({
12
57
  tagName: 'vira-link',
13
58
  cssVars: {
14
59
  'vira-link-hover-color': 'currentColor',
15
60
  },
16
- styles: ({ cssVars }) => css `
61
+ styles: ({cssVars}) => css`
17
62
  :host {
18
63
  display: inline;
19
64
  text-decoration: underline;
@@ -36,12 +81,17 @@ export const ViraLink = defineViraElement()({
36
81
  color: ${cssVars['vira-link-hover-color'].value};
37
82
  }
38
83
  `,
39
- render({ inputs }) {
40
- function clickCallback(event) {
84
+ render({inputs}) {
85
+ function clickCallback(event: MouseEvent) {
41
86
  if (!inputs.route) {
42
87
  return;
43
88
  }
44
- const routed = inputs.route.router.setRouteOnDirectNavigation(inputs.route.route, event);
89
+
90
+ const routed = inputs.route.router.setRouteOnDirectNavigation(
91
+ inputs.route.route,
92
+ event,
93
+ );
94
+
45
95
  if (routed && inputs.route.scrollToTop) {
46
96
  window.scrollTo({
47
97
  left: 0,
@@ -50,9 +100,10 @@ export const ViraLink = defineViraElement()({
50
100
  });
51
101
  }
52
102
  }
103
+
53
104
  if (inputs.link?.newTab) {
54
105
  /** Noopener and noreferrer are needed for security reasons, do not remove! */
55
- return html `
106
+ return html`
56
107
  <a
57
108
  href=${inputs.link.url}
58
109
  target="_blank"
@@ -63,13 +114,13 @@ export const ViraLink = defineViraElement()({
63
114
  <slot></slot>
64
115
  </a>
65
116
  `;
66
- }
67
- else {
117
+ } else {
68
118
  const linkUrl = inputs.link
69
119
  ? inputs.link.url
70
120
  : inputs.route.router.createRouteUrl(inputs.route.route);
121
+
71
122
  /** Noopener and noreferrer are needed for security reasons, do not remove! */
72
- return html `
123
+ return html`
73
124
  <a
74
125
  href=${linkUrl}
75
126
  rel="noopener noreferrer"
@@ -1,41 +1,71 @@
1
- import { assertWrap } from '@augment-vir/assert';
2
- import { css, defineElementEvent, html, listen, nothing, onDomCreated } from 'element-vir';
3
- import { listenToGlobal } from 'typed-event-target';
4
- import { X24Icon } from '../icons/icon-svgs/x-24.icon.js';
5
- import { noNativeFormStyles, noNativeSpacing } from '../styles/native-styles.js';
6
- import { viraShadows } from '../styles/shadows.js';
7
- import { defineViraElement } from './define-vira-element.js';
8
- import { ViraIcon } from './vira-icon.element.js';
1
+ import {assertWrap} from '@augment-vir/assert';
2
+ import {type PartialWithUndefined} from '@augment-vir/common';
3
+ import {css, defineElementEvent, html, listen, nothing, onDomCreated} from 'element-vir';
4
+ import {listenToGlobal} from 'typed-event-target';
5
+ import {X24Icon} from '../icons/icon-svgs/x-24.icon.js';
6
+ import {noNativeFormStyles, noNativeSpacing} from '../styles/native-styles.js';
7
+ import {viraShadows} from '../styles/shadows.js';
8
+ import {defineViraElement} from './define-vira-element.js';
9
+ import {ViraIcon} from './vira-icon.element.js';
10
+
9
11
  const globalEventsToCloseModalOn = [
10
12
  'pagehide',
11
13
  'pageshow',
12
14
  'popstate',
13
- ];
15
+ ] as const satisfies (keyof WindowEventMap)[];
16
+
14
17
  /**
15
18
  * A modal element that uses the built-in `dialog` element.
16
19
  *
17
20
  * @category Elements
18
21
  * @see https://electrovir.github.io/vira/book/elements/vira-modal
19
22
  */
20
- export const ViraModal = defineViraElement()({
23
+ export const ViraModal = defineViraElement<
24
+ Readonly<
25
+ {
26
+ open: boolean;
27
+ } & PartialWithUndefined<{
28
+ /** If this isn't set, make sure to use the modal title slot to fill it in. */
29
+ modalTitle: string;
30
+ /**
31
+ * If `true`, the following conditions trigger the modal to close:
32
+ *
33
+ * - The user clicks the "x" close button
34
+ * - The `open` input is set to `false`
35
+ *
36
+ * If set to `false` (the default), the following conditions trigger the modal to close:
37
+ *
38
+ * - The user clicks outside of the modal
39
+ * - The user presses the "Escape" key
40
+ * - The user clicks the "x" close button
41
+ * - The `open` input is set to `false`
42
+ *
43
+ * @default false
44
+ */
45
+ blockLightDismissal: boolean;
46
+ modalSubtitle: string;
47
+ isMobileSize: boolean;
48
+ }>
49
+ >
50
+ >()({
21
51
  tagName: 'vira-modal',
22
52
  events: {
23
- modalClose: defineElementEvent(),
53
+ modalClose: defineElementEvent<void>(),
24
54
  },
25
55
  state() {
26
56
  return {
27
- dialogElement: undefined,
28
- contentElement: undefined,
29
- previousOpenValue: undefined,
57
+ dialogElement: undefined as HTMLDialogElement | undefined,
58
+ contentElement: undefined as HTMLDivElement | undefined,
59
+ previousOpenValue: undefined as undefined | boolean,
30
60
  /** Remove listeners. */
31
- cleanup: undefined,
61
+ cleanup: undefined as undefined | (() => void),
32
62
  };
33
63
  },
34
- cleanup({ state }) {
64
+ cleanup({state}) {
35
65
  state.cleanup?.();
36
66
  },
37
67
  hostClasses: {
38
- 'vira-modal-phone-size': ({ inputs }) => !!inputs.isMobileSize,
68
+ 'vira-modal-phone-size': ({inputs}) => !!inputs.isMobileSize,
39
69
  },
40
70
  slotNames: ['modalTitle'],
41
71
  cssVars: {
@@ -45,7 +75,7 @@ export const ViraModal = defineViraElement()({
45
75
  'vira-modal-close-button-hover-radius': '8px',
46
76
  'vira-modal-close-button-hover-background-color': '#E4E4E4',
47
77
  },
48
- styles: ({ hostClasses, cssVars }) => css `
78
+ styles: ({hostClasses, cssVars}) => css`
49
79
  :host {
50
80
  display: contents;
51
81
  min-width: 280px;
@@ -111,7 +141,9 @@ export const ViraModal = defineViraElement()({
111
141
  border-radius: ${cssVars['vira-modal-close-button-hover-radius'].value};
112
142
 
113
143
  &:hover {
114
- background-color: ${cssVars['vira-modal-close-button-hover-background-color'].value};
144
+ background-color: ${cssVars[
145
+ 'vira-modal-close-button-hover-background-color'
146
+ ].value};
115
147
  }
116
148
 
117
149
  & ${ViraIcon} {
@@ -134,22 +166,25 @@ export const ViraModal = defineViraElement()({
134
166
  }
135
167
  }
136
168
  `,
137
- render({ inputs, state, updateState, events, dispatch, slotNames }) {
169
+ render({inputs, state, updateState, events, dispatch, slotNames}) {
138
170
  if (state.dialogElement && inputs.open !== state.dialogElement.open) {
139
171
  if (inputs.open) {
140
172
  state.dialogElement.showModal();
141
- }
142
- else {
173
+ } else {
143
174
  state.dialogElement.close();
144
175
  }
145
176
  }
177
+
146
178
  if (state.previousOpenValue !== inputs.open) {
147
179
  state.cleanup?.();
148
- updateState({ previousOpenValue: inputs.open });
180
+ updateState({previousOpenValue: inputs.open});
149
181
  if (inputs.open) {
150
- const removers = globalEventsToCloseModalOn.map((eventName) => listenToGlobal(eventName, () => {
151
- dispatch(new events.modalClose());
152
- }));
182
+ const removers = globalEventsToCloseModalOn.map((eventName) =>
183
+ listenToGlobal(eventName, () => {
184
+ dispatch(new events.modalClose());
185
+ }),
186
+ );
187
+
153
188
  updateState({
154
189
  cleanup: () => {
155
190
  removers.forEach((remover) => remover());
@@ -157,65 +192,69 @@ export const ViraModal = defineViraElement()({
157
192
  });
158
193
  }
159
194
  }
195
+
160
196
  function close() {
161
197
  if (inputs.open) {
162
198
  state.cleanup?.();
163
199
  dispatch(new events.modalClose());
164
200
  }
165
201
  }
166
- return html `
202
+
203
+ return html`
167
204
  <dialog
168
205
  ${onDomCreated((element) => {
169
- updateState({ dialogElement: assertWrap.instanceOf(element, HTMLDialogElement) });
170
- })}
206
+ updateState({dialogElement: assertWrap.instanceOf(element, HTMLDialogElement)});
207
+ })}
171
208
  ${listen('close', () => {
172
- close();
173
- })}
209
+ close();
210
+ })}
174
211
  ${listen('click', (event) => {
175
- if (state.contentElement &&
176
- !event.composedPath().includes(state.contentElement) &&
177
- !inputs.blockLightDismissal) {
178
- /** Background click. */
179
- close();
180
- }
181
- })}
212
+ if (
213
+ state.contentElement &&
214
+ !event.composedPath().includes(state.contentElement) &&
215
+ !inputs.blockLightDismissal
216
+ ) {
217
+ /** Background click. */
218
+ close();
219
+ }
220
+ })}
182
221
  >
183
222
  <div
184
223
  class="modal-content-wrapper"
185
224
  ${onDomCreated((element) => {
186
- updateState({
187
- contentElement: assertWrap.instanceOf(element, HTMLDivElement),
188
- });
189
- })}
225
+ updateState({
226
+ contentElement: assertWrap.instanceOf(element, HTMLDivElement),
227
+ });
228
+ })}
190
229
  >
191
230
  <div class="header">
192
231
  <div class="header-text-wrapper">
193
232
  <h1><slot name=${slotNames.modalTitle}>${inputs.modalTitle}</slot></h1>
194
233
  ${inputs.modalSubtitle
195
- ? html `
234
+ ? html`
196
235
  <sub>${inputs.modalSubtitle}</sub>
197
236
  `
198
- : nothing}
237
+ : nothing}
199
238
  </div>
200
239
  <button
201
240
  class="close"
202
241
  aria-label="Close"
203
242
  ${listen('click', () => {
204
- state.dialogElement?.close();
205
- })}
243
+ state.dialogElement?.close();
244
+ })}
206
245
  >
207
246
  <${ViraIcon.assign({
208
- icon: X24Icon,
209
- })}></${ViraIcon}>
247
+ icon: X24Icon,
248
+ })}></${ViraIcon}>
210
249
  </button>
211
250
  </div>
212
251
  ${inputs.open
213
- ? html `
252
+ ? html`
214
253
  <div class="body">
215
254
  <slot></slot>
216
255
  </div>
217
256
  `
218
- : nothing}
257
+ : nothing}
219
258
  </div>
220
259
  </dialog>
221
260
  `;
@@ -0,0 +1,137 @@
1
+ import {css, html, onDomCreated} from 'element-vir';
2
+ import {type RequireExactlyOne} from 'type-fest';
3
+ import {listenTo} from 'typed-event-target';
4
+ import {defineViraElement} from './define-vira-element.js';
5
+
6
+ /**
7
+ * An element switches between two slots based on their overflow.
8
+ *
9
+ * @category Elements
10
+ * @see https://electrovir.github.io/vira/book/elements/vira-overflow-switch
11
+ */
12
+ export const ViraOverflowSwitch = defineViraElement<
13
+ Readonly<
14
+ RequireExactlyOne<{
15
+ automaticallySwitch: boolean;
16
+ useSmall: boolean;
17
+ }>
18
+ >
19
+ >()({
20
+ tagName: 'vira-overflow-switch',
21
+ slotNames: [
22
+ /** The child to render, if it fits. */
23
+ 'large',
24
+ /** The child to render if the large one does not fit. */
25
+ 'small',
26
+ ],
27
+ state() {
28
+ return {
29
+ isOverflowing: false,
30
+ resizeObserver: undefined as undefined | ResizeObserver,
31
+ /** Called on cleanup to clear all listeners. */
32
+ cleanup: undefined as undefined | (() => void),
33
+ };
34
+ },
35
+ hostClasses: {
36
+ 'vira-overflow-switch-show-small': ({state, inputs}) =>
37
+ state.isOverflowing || !!inputs.useSmall,
38
+ },
39
+ styles: ({hostClasses}) => css`
40
+ :host {
41
+ display: inline-block;
42
+ max-width: 100%;
43
+ }
44
+
45
+ .large,
46
+ .small {
47
+ display: inline-block;
48
+ }
49
+
50
+ .small {
51
+ display: none;
52
+ }
53
+
54
+ /**
55
+ * When the large content overflows, hide it but keep it in layout so we can measure it.
56
+ * The small content is then shown instead.
57
+ */
58
+ ${hostClasses['vira-overflow-switch-show-small'].selector} .large {
59
+ visibility: hidden;
60
+ position: absolute;
61
+ top: 0;
62
+ left: 0;
63
+ }
64
+
65
+ ${hostClasses['vira-overflow-switch-show-small'].selector} .small {
66
+ display: inline-block;
67
+ }
68
+ `,
69
+ cleanup({state, updateState}) {
70
+ state.cleanup?.();
71
+ updateState({
72
+ cleanup: undefined,
73
+ });
74
+ },
75
+ render({slotNames, updateState, inputs, host, state}) {
76
+ return html`
77
+ <div
78
+ class="large"
79
+ ${onDomCreated((largeElement) => {
80
+ if (!inputs.automaticallySwitch) {
81
+ return;
82
+ }
83
+
84
+ const overflowParams: Parameters<typeof updateOverflowing>[0] = {
85
+ elementToTest: largeElement,
86
+ host,
87
+ updateState,
88
+ };
89
+
90
+ const resizeObserver = new ResizeObserver(() => {
91
+ updateOverflowing(overflowParams);
92
+ });
93
+
94
+ resizeObserver.observe(host);
95
+
96
+ /**
97
+ * Also observe the large slot wrapper itself in case its own layout changes
98
+ * without host resizing.
99
+ */
100
+ resizeObserver.observe(largeElement);
101
+
102
+ const removeSlotChangeListener = listenTo(largeElement, 'slotchange', () => {
103
+ updateOverflowing(overflowParams);
104
+ });
105
+
106
+ /** Initial measurement: defer until after first layout. */
107
+ updateOverflowing(overflowParams);
108
+
109
+ state.cleanup?.();
110
+ updateState({
111
+ cleanup() {
112
+ resizeObserver.disconnect();
113
+ removeSlotChangeListener();
114
+ },
115
+ });
116
+ })}
117
+ >
118
+ <slot name=${slotNames.large}></slot>
119
+ </div>
120
+ <div class="small"><slot name=${slotNames.small}></slot></div>
121
+ `;
122
+ },
123
+ });
124
+
125
+ function updateOverflowing({
126
+ elementToTest,
127
+ host,
128
+ updateState,
129
+ }: {
130
+ host: Element;
131
+ updateState: (newState: Partial<{isOverflowing: boolean}>) => void;
132
+ elementToTest: Element;
133
+ }) {
134
+ const isOverflowing = elementToTest.scrollWidth > host.clientWidth;
135
+
136
+ updateState({isOverflowing});
137
+ }
@@ -1,7 +1,8 @@
1
- import { clamp } from '@augment-vir/common';
2
- import { applyAttributes } from 'device-navigation';
3
- import { css, html } from 'element-vir';
4
- import { defineViraElement } from './define-vira-element.js';
1
+ import {clamp, type PartialWithUndefined} from '@augment-vir/common';
2
+ import {applyAttributes} from 'device-navigation';
3
+ import {css, html} from 'element-vir';
4
+ import {defineViraElement} from './define-vira-element.js';
5
+
5
6
  /**
6
7
  * A [`<progress>`](https://developer.mozilla.org/docs/Web/HTML/Reference/Elements/progress)
7
8
  * alternative that supports custom styling in _all_ browsers via CSS vars _and_ prevents background
@@ -11,7 +12,18 @@ import { defineViraElement } from './define-vira-element.js';
11
12
  * @category Elements
12
13
  * @see https://electrovir.github.io/vira/book/elements/vira-progress
13
14
  */
14
- export const ViraProgress = defineViraElement()({
15
+ export const ViraProgress = defineViraElement<
16
+ Readonly<
17
+ {
18
+ value: number;
19
+ } & PartialWithUndefined<{
20
+ /** @default 0 */
21
+ min: number;
22
+ /** @default 100 */
23
+ max: number;
24
+ }>
25
+ >
26
+ >()({
15
27
  tagName: 'vira-progress',
16
28
  cssVars: {
17
29
  /**
@@ -22,7 +34,7 @@ export const ViraProgress = defineViraElement()({
22
34
  'vira-progress-background-color': '#eee',
23
35
  'vira-progress-foreground-color': 'dodgerblue',
24
36
  },
25
- styles: ({ cssVars }) => css `
37
+ styles: ({cssVars}) => css`
26
38
  :host {
27
39
  /* Default width that can easily be overridden because it's applied on the host. */
28
40
  width: 100px;
@@ -46,26 +58,29 @@ export const ViraProgress = defineViraElement()({
46
58
  flex-grow: 1;
47
59
  }
48
60
  `,
49
- render({ inputs, host }) {
61
+ render({inputs, host}) {
50
62
  const min = inputs.min || 0;
51
63
  const max = inputs.max || 100;
52
64
  const totalRange = max - min;
53
65
  const value = inputs.value - min;
54
- const percentFull = clamp(Math.round((value / totalRange) * 100), { min: 0, max: 100 });
66
+
67
+ const percentFull = clamp(Math.round((value / totalRange) * 100), {min: 0, max: 100});
68
+
55
69
  applyAttributes(host, {
56
70
  'aria-valuemin': inputs.min,
57
71
  'aria-valuemax': inputs.max,
58
72
  'aria-valuenow': inputs.value,
59
73
  'aria-role': 'progressbar',
60
74
  });
61
- return html `
75
+
76
+ return html`
62
77
  <div
63
78
  class="progress-bar"
64
79
  style=${percentFull
65
- ? css `
80
+ ? css`
66
81
  width: ${percentFull}%;
67
82
  `
68
- : css `
83
+ : css`
69
84
  display: none;
70
85
  `}
71
86
  ></div>