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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vira",
3
- "version": "28.19.4",
3
+ "version": "28.19.6",
4
4
  "description": "A simple and highly versatile design system using element-vir.",
5
5
  "keywords": [
6
6
  "design",
@@ -27,9 +27,9 @@
27
27
  },
28
28
  "sideEffects": false,
29
29
  "type": "module",
30
- "main": "dist/index.js",
31
- "module": "dist/index.js",
32
- "types": "dist/index.d.ts",
30
+ "main": "src/index.ts",
31
+ "module": "src/index.ts",
32
+ "types": "src/index.ts",
33
33
  "scripts": {
34
34
  "build": "npm run docs",
35
35
  "compile": "virmator compile",
@@ -38,21 +38,21 @@
38
38
  "test:docs": "virmator docs check"
39
39
  },
40
40
  "dependencies": {
41
- "@augment-vir/assert": "^31.57.5",
42
- "@augment-vir/common": "^31.57.5",
43
- "@augment-vir/web": "^31.57.5",
44
- "colorjs.io": "^0.6.0",
41
+ "@augment-vir/assert": "^31.59.0",
42
+ "@augment-vir/common": "^31.59.0",
43
+ "@augment-vir/web": "^31.59.0",
44
+ "colorjs.io": "^0.6.1",
45
45
  "date-vir": "^8.1.0",
46
46
  "device-navigation": "^4.5.5",
47
- "lit-css-vars": "^3.0.11",
47
+ "lit-css-vars": "^3.1.1",
48
48
  "observavir": "^2.3.1",
49
49
  "page-active": "^1.0.3",
50
50
  "spa-router-vir": "^6.4.1",
51
- "type-fest": "^5.3.1",
51
+ "type-fest": "^5.4.1",
52
52
  "typed-event-target": "^4.1.0"
53
53
  },
54
54
  "devDependencies": {
55
- "@augment-vir/test": "^31.57.5",
55
+ "@augment-vir/test": "^31.59.0",
56
56
  "@web/dev-server-esbuild": "^1.0.4",
57
57
  "@web/test-runner": "^0.20.2",
58
58
  "@web/test-runner-commands": "^0.9.0",
@@ -61,8 +61,8 @@
61
61
  "esbuild": "^0.27.2",
62
62
  "istanbul-smart-text-reporter": "^1.1.5",
63
63
  "markdown-code-example-inserter": "^3.0.3",
64
- "theme-vir": "^28.17.0",
65
- "typedoc": "^0.28.15",
64
+ "theme-vir": "^28.18.2",
65
+ "typedoc": "^0.28.16",
66
66
  "typescript": "5.9.3",
67
67
  "vite": "^7.3.1",
68
68
  "vite-tsconfig-paths": "^6.0.4"
@@ -0,0 +1,29 @@
1
+ import {wrapDefineElement} from 'element-vir';
2
+
3
+ /**
4
+ * Required prefix for all vira elements.
5
+ *
6
+ * @category Internal
7
+ */
8
+ export const ViraTagNamePrefix = `vira-`;
9
+ /**
10
+ * Required tag name for all vira elements. This includes {@link ViraTagNamePrefix} in it.
11
+ *
12
+ * @category Internal
13
+ */
14
+ export type ViraTagName = `${typeof ViraTagNamePrefix}${string}`;
15
+
16
+ /**
17
+ * Define a vira element with custom requirements (like the `vira-` element tag prefix).
18
+ *
19
+ * @category Internal
20
+ */
21
+ export const defineViraElement = wrapDefineElement<ViraTagName>({
22
+ assertInputs: (inputs) => {
23
+ if (!inputs.tagName.startsWith(ViraTagNamePrefix)) {
24
+ throw new Error(
25
+ `Tag name should start with '${ViraTagNamePrefix}' but got '${inputs.tagName}'`,
26
+ );
27
+ }
28
+ },
29
+ });
@@ -0,0 +1,140 @@
1
+ import {check} from '@augment-vir/assert';
2
+ import {addSuffix, getObjectTypedValues, type PartialWithUndefined} from '@augment-vir/common';
3
+ import {type ViraIconSvg} from '../../icons/icon-svg.js';
4
+ import {type ViraSelectOption} from '../vira-select.element.js';
5
+
6
+ /**
7
+ * Form field types for {@link ViraFormField}.
8
+ *
9
+ * @category Internal
10
+ */
11
+ export enum ViraFormFieldType {
12
+ Text = 'text',
13
+ /** Allows auto complete for _existing_ passwords used on this website (for login). */
14
+ ExistingPassword = 'existing-password',
15
+ /** Allows auto complete for _new_ passwords used on this website (for login). */
16
+ NewPassword = 'new-password',
17
+ /** Uses a password input without any attributes applied for auto complete hints. */
18
+ PlainPassword = 'plain-password',
19
+ Email = 'email',
20
+ Select = 'select',
21
+ Checkbox = 'checkbox',
22
+ }
23
+
24
+ /**
25
+ * {@link ViraFormField} properties that are shared between all field types.
26
+ *
27
+ * @category Internal
28
+ */
29
+ export type CommonViraFormFields = {
30
+ label: string;
31
+ } & PartialWithUndefined<{
32
+ /** Applies a test id to the form field element. */
33
+ testId: string;
34
+ /**
35
+ * When `true`, visually indicates the form field as required and affects form validation.
36
+ *
37
+ * @default false
38
+ */
39
+ isRequired: boolean;
40
+ /**
41
+ * When `true`, marks this form field element with error styling.
42
+ *
43
+ * @default false
44
+ */
45
+ hasError: boolean;
46
+ /**
47
+ * When `true`, hides this form field entirely.
48
+ *
49
+ * @default false
50
+ */
51
+ isHidden: boolean;
52
+ /**
53
+ * When `true`, continues showing the form field but prevents edits.
54
+ *
55
+ * @default false
56
+ */
57
+ isDisabled: boolean;
58
+ }>;
59
+
60
+ /**
61
+ * An individual form field for {@link ViraFormFields}.
62
+ *
63
+ * @category Internal
64
+ */
65
+ export type ViraFormField =
66
+ | ({
67
+ type:
68
+ | ViraFormFieldType.Text
69
+ | ViraFormFieldType.ExistingPassword
70
+ | ViraFormFieldType.NewPassword
71
+ | ViraFormFieldType.PlainPassword
72
+ | ViraFormFieldType.Email;
73
+ value: string | undefined;
74
+ } & PartialWithUndefined<{
75
+ placeholder: string;
76
+ icon: ViraIconSvg;
77
+ isUsername: boolean;
78
+ }> &
79
+ CommonViraFormFields)
80
+ | ({
81
+ type: ViraFormFieldType.Select;
82
+ value: string | undefined;
83
+ options: ReadonlyArray<Readonly<ViraSelectOption>>;
84
+ } & PartialWithUndefined<{
85
+ placeholder: string;
86
+ icon: ViraIconSvg;
87
+ }> &
88
+ CommonViraFormFields)
89
+ | ({
90
+ type: ViraFormFieldType.Checkbox;
91
+ value: boolean | undefined;
92
+ } & CommonViraFormFields);
93
+
94
+ /**
95
+ * A collection of form fields for `ViraForm`.
96
+ *
97
+ * @category Internal
98
+ */
99
+ export type ViraFormFields = Record<string, ViraFormField>;
100
+
101
+ /**
102
+ * Appends a `'*'` to a label if it exist sand if it is required.
103
+ *
104
+ * @category Internal
105
+ */
106
+ export function applyRequiredLabel(
107
+ label: string | undefined,
108
+ isRequired: boolean,
109
+ ): string | undefined {
110
+ if (label) {
111
+ if (isRequired) {
112
+ return addSuffix({value: label, suffix: '*'});
113
+ } else {
114
+ return label;
115
+ }
116
+ } else {
117
+ return undefined;
118
+ }
119
+ }
120
+
121
+ /**
122
+ * Checks if all the {@link ViraFormField} entries in a given {@link ViraFormFields} are valid through
123
+ * the following checks:
124
+ *
125
+ * - Checks that required fields are provided (not `undefined`)
126
+ * - Ignores hidden fields
127
+ *
128
+ * @category Internal
129
+ */
130
+ export function areFormFieldsValid(formFields: Readonly<ViraFormFields>) {
131
+ return getObjectTypedValues(formFields).every((formField) => {
132
+ if (formField.isHidden || !formField.isRequired) {
133
+ return true;
134
+ } else if (check.isString(formField.value)) {
135
+ return !!formField.value;
136
+ } else {
137
+ return formField.value != undefined;
138
+ }
139
+ });
140
+ }
@@ -0,0 +1,204 @@
1
+ import {getObjectTypedEntries, type PartialWithUndefined} from '@augment-vir/common';
2
+ import {css, defineElementEvent, html, listen, nothing, testId} from 'element-vir';
3
+ import {defineViraElement} from '../define-vira-element.js';
4
+ import {ViraCheckbox} from '../vira-checkbox.element.js';
5
+ import {ViraInput, ViraInputType} from '../vira-input.element.js';
6
+ import {ViraSelect} from '../vira-select.element.js';
7
+ import {
8
+ applyRequiredLabel,
9
+ areFormFieldsValid,
10
+ type ViraFormField,
11
+ type ViraFormFields,
12
+ ViraFormFieldType,
13
+ } from './vira-form-fields.js';
14
+
15
+ /**
16
+ * A form element.
17
+ *
18
+ * @category Elements
19
+ * @see https://electrovir.github.io/vira/book/elements/vira-form
20
+ */
21
+ export const ViraForm = defineViraElement<
22
+ Readonly<
23
+ {
24
+ fields: Readonly<ViraFormFields>;
25
+ } & PartialWithUndefined<{
26
+ showClearButtons: boolean;
27
+ /**
28
+ * When `true`, all fields in this form are disabled. Note that this will not (and can
29
+ * not) disable any child elements you've inserted via <slot>.
30
+ *
31
+ * @default false
32
+ */
33
+ isDisabled: boolean;
34
+ /**
35
+ * If true, no `'*'` is appended to required form field labels.
36
+ *
37
+ * @default false
38
+ */
39
+ hideRequiredMarkers: boolean;
40
+ }>
41
+ >
42
+ >()({
43
+ tagName: 'vira-form',
44
+ events: {
45
+ valueChange: defineElementEvent<
46
+ {
47
+ key: string;
48
+ } & ViraFormField
49
+ >(),
50
+ validChange: defineElementEvent<{
51
+ allFieldsAreValid: boolean;
52
+ }>(),
53
+ },
54
+ styles: css`
55
+ :host {
56
+ display: flex;
57
+ }
58
+
59
+ form {
60
+ display: flex;
61
+ flex-grow: 1;
62
+ flex-direction: column;
63
+ align-items: stretch;
64
+ gap: 10px;
65
+
66
+ > * {
67
+ width: unset;
68
+ }
69
+ }
70
+ `,
71
+ state() {
72
+ return {
73
+ lastIsValid: false,
74
+ };
75
+ },
76
+ render({inputs, dispatch, events, state, updateState}) {
77
+ const currentIsValid = areFormFieldsValid(inputs.fields);
78
+ if (currentIsValid !== state.lastIsValid) {
79
+ updateState({
80
+ lastIsValid: currentIsValid,
81
+ });
82
+ dispatch(new events.validChange({allFieldsAreValid: currentIsValid}));
83
+ }
84
+
85
+ const formFieldTemplates = getObjectTypedEntries(inputs.fields).map(
86
+ ([
87
+ key,
88
+ field,
89
+ ]) => {
90
+ if (field.isHidden) {
91
+ return nothing;
92
+ } else if (field.type === ViraFormFieldType.Checkbox) {
93
+ return html`
94
+ <${ViraCheckbox.assign({
95
+ value: field.value || false,
96
+ disabled: inputs.isDisabled || field.isDisabled,
97
+ hasError: field.hasError,
98
+ label: applyRequiredLabel(
99
+ field.label,
100
+ !!field.isRequired && !inputs.hideRequiredMarkers,
101
+ ),
102
+ })}
103
+ ${field.testId ? testId(field.testId) : nothing}
104
+ ${listen(ViraCheckbox.events.valueChange, (event) => {
105
+ dispatch(
106
+ new events.valueChange({
107
+ key,
108
+ ...field,
109
+ value: event.detail,
110
+ }),
111
+ );
112
+ })}
113
+ ></${ViraCheckbox}>
114
+ `;
115
+ } else if (field.type === ViraFormFieldType.Select) {
116
+ return html`
117
+ <${ViraSelect.assign({
118
+ options: field.options,
119
+ value: field.value,
120
+ placeholder: field.placeholder,
121
+ disabled: inputs.isDisabled || field.isDisabled,
122
+ label: applyRequiredLabel(
123
+ field.label,
124
+ !!field.isRequired && !inputs.hideRequiredMarkers,
125
+ ),
126
+ hasError: field.hasError,
127
+ icon: field.icon,
128
+ })}
129
+ ${field.testId ? testId(field.testId) : nothing}
130
+ ${listen(ViraSelect.events.valueChange, (event) => {
131
+ dispatch(
132
+ new events.valueChange({
133
+ key,
134
+ ...field,
135
+ value: event.detail,
136
+ }),
137
+ );
138
+ })}
139
+ ></${ViraSelect}>
140
+ `;
141
+ } else {
142
+ return html`
143
+ <${ViraInput.assign({
144
+ value: field.value || '',
145
+ disabled: inputs.isDisabled || field.isDisabled,
146
+ hasError: field.hasError,
147
+ icon: field.icon,
148
+ label: applyRequiredLabel(
149
+ field.label,
150
+ !!field.isRequired && !inputs.hideRequiredMarkers,
151
+ ),
152
+ placeholder: field.placeholder,
153
+ showClearButton: inputs.showClearButtons,
154
+ attributePassthrough: field.isUsername
155
+ ? {
156
+ autocomplete: 'username',
157
+ }
158
+ : field.type === ViraFormFieldType.NewPassword
159
+ ? {
160
+ autocomplete: 'new-password',
161
+ }
162
+ : field.type === ViraFormFieldType.ExistingPassword
163
+ ? {
164
+ autocomplete: 'password',
165
+ }
166
+ : field.type === ViraFormFieldType.Email
167
+ ? {
168
+ autocomplete: 'email',
169
+ }
170
+ : {},
171
+ type: [
172
+ ViraFormFieldType.NewPassword,
173
+ ViraFormFieldType.ExistingPassword,
174
+ ViraFormFieldType.PlainPassword,
175
+ ].includes(field.type)
176
+ ? ViraInputType.Password
177
+ : field.type === ViraFormFieldType.Email
178
+ ? ViraInputType.Email
179
+ : ViraInputType.Default,
180
+ })}
181
+ ${field.testId ? testId(field.testId) : nothing}
182
+ ${listen(ViraInput.events.valueChange, (event) => {
183
+ dispatch(
184
+ new events.valueChange({
185
+ key,
186
+ ...field,
187
+ value: event.detail,
188
+ }),
189
+ );
190
+ })}
191
+ ></${ViraInput}>
192
+ `;
193
+ }
194
+ },
195
+ );
196
+
197
+ return html`
198
+ <form ${listen('submit', (event) => event.preventDefault())}>
199
+ ${formFieldTemplates}
200
+ <slot></slot>
201
+ </form>
202
+ `;
203
+ },
204
+ });
@@ -0,0 +1,85 @@
1
+ import {joinWithFinalConjunction, type PartialWithUndefined} from '@augment-vir/common';
2
+ import {
3
+ type PopUpManager,
4
+ type PopUpManagerOptions,
5
+ type ShowPopUpResult,
6
+ } from '../../util/pop-up-manager.js';
7
+ import {type MenuItem} from './pop-up-menu-item.js';
8
+
9
+ /**
10
+ * Verifies that all items have unique ids.
11
+ *
12
+ * @category Internal
13
+ */
14
+ export function assertUniqueIdProps(items: ReadonlyArray<Readonly<{id: PropertyKey}>>) {
15
+ const usedIds = new Set<PropertyKey>();
16
+ const duplicateIds: PropertyKey[] = [];
17
+ items.forEach((option) => {
18
+ if (usedIds.has(option.id)) {
19
+ duplicateIds.push(option.id);
20
+ } else {
21
+ usedIds.add(option.id);
22
+ }
23
+ });
24
+
25
+ if (duplicateIds.length) {
26
+ throw new Error(
27
+ `Duplicate option ids were given: ${joinWithFinalConjunction(duplicateIds)}`,
28
+ );
29
+ }
30
+ }
31
+
32
+ /**
33
+ * Creates a new array of selections based on the current selection and new selection id. This
34
+ * behaves differently when multi select is enabled, hence this function.
35
+ *
36
+ * @category Internal
37
+ */
38
+ export function updateSelectedItems(
39
+ /** The item that should be newly toggled. */
40
+ newItem: Readonly<MenuItem>,
41
+ currentSelection: ReadonlyArray<PropertyKey> = [],
42
+ isMultiSelect: boolean = false,
43
+ ): PropertyKey[] {
44
+ if (isMultiSelect) {
45
+ return currentSelection.includes(newItem.id)
46
+ ? currentSelection.filter((entry) => entry !== newItem.id)
47
+ : [
48
+ ...currentSelection,
49
+ newItem.id,
50
+ ];
51
+ } else {
52
+ /** In single select, only the toggled item is allowed. */
53
+ return [newItem.id];
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Handles toggling pop up state for `ViraDropdown`.
59
+ *
60
+ * @category Internal
61
+ */
62
+ export function triggerPopUpState({
63
+ open,
64
+ callback,
65
+ popUpManager,
66
+ host,
67
+ options,
68
+ }: Readonly<
69
+ {
70
+ open: boolean;
71
+ popUpManager: PopUpManager;
72
+ host: HTMLElement;
73
+ } & PartialWithUndefined<{
74
+ callback?: ((showPopUpResult: ShowPopUpResult | undefined) => void) | undefined;
75
+ options?: Partial<PopUpManagerOptions> | undefined;
76
+ }>
77
+ >) {
78
+ if (open) {
79
+ const showPopUpResult = popUpManager.showPopUp(host, options);
80
+ callback?.(showPopUpResult);
81
+ } else {
82
+ popUpManager.removePopUp();
83
+ callback?.(undefined);
84
+ }
85
+ }
@@ -1,6 +1,7 @@
1
- import { type PartialWithUndefined } from '@augment-vir/common';
2
- import { type HTMLTemplateResult } from 'element-vir';
3
- import { type ViraLinkRoute } from '../vira-link.element.js';
1
+ import {type PartialWithUndefined} from '@augment-vir/common';
2
+ import {type HTMLTemplateResult} from 'element-vir';
3
+ import {type ViraLinkRoute} from '../vira-link.element.js';
4
+
4
5
  /**
5
6
  * An individual menu item consumed partially by `ViraMenuItem` and used by `ViraMenu`.
6
7
  *
@@ -1,20 +1,37 @@
1
- import { css, html } from 'element-vir';
2
- import { Check24Icon } from '../../icons/icon-svgs/check-24.icon.js';
3
- import { noUserSelect } from '../../styles/index.js';
4
- import { defineViraElement } from '../define-vira-element.js';
5
- import { ViraIcon } from '../vira-icon.element.js';
1
+ import {type PartialWithUndefined} from '@augment-vir/common';
2
+ import {css, html} from 'element-vir';
3
+ import {Check24Icon} from '../../icons/icon-svgs/check-24.icon.js';
4
+ import {noUserSelect} from '../../styles/index.js';
5
+ import {defineViraElement} from '../define-vira-element.js';
6
+ import {ViraIcon} from '../vira-icon.element.js';
7
+ import {type MenuItem} from './pop-up-menu-item.js';
8
+
6
9
  /**
7
10
  * An element for an individual menu item.
8
11
  *
9
12
  * @category PopUp
10
13
  * @category Elements
11
14
  */
12
- export const ViraMenuItem = defineViraElement()({
15
+ export const ViraMenuItem = defineViraElement<
16
+ Readonly<
17
+ {
18
+ selected: boolean;
19
+ } & PartialWithUndefined<{
20
+ /**
21
+ * The text to show in the menu item. If this is not provided, it is expected that you
22
+ * will instead utilize this element's `<slot>`.
23
+ */
24
+ label: MenuItem['label'];
25
+ /** If `true`, does not render the selected check icon. */
26
+ hideCheckIcon: boolean;
27
+ }>
28
+ >
29
+ >()({
13
30
  tagName: 'vira-menu-item',
14
31
  hostClasses: {
15
- 'vira-menu-item-selected': ({ inputs }) => !inputs.hideCheckIcon && inputs.selected,
32
+ 'vira-menu-item-selected': ({inputs}) => !inputs.hideCheckIcon && inputs.selected,
16
33
  },
17
- styles: ({ hostClasses }) => css `
34
+ styles: ({hostClasses}) => css`
18
35
  :host {
19
36
  display: flex;
20
37
  ${noUserSelect};
@@ -53,10 +70,10 @@ export const ViraMenuItem = defineViraElement()({
53
70
  visibility: hidden;
54
71
  }
55
72
  `,
56
- render({ inputs }) {
57
- return html `
73
+ render({inputs}) {
74
+ return html`
58
75
  <div class="item">
59
- <${ViraIcon.assign({ icon: Check24Icon })}></${ViraIcon}>
76
+ <${ViraIcon.assign({icon: Check24Icon})}></${ViraIcon}>
60
77
  <slot>${inputs.label}</slot>
61
78
  </div>
62
79
  `;