@aurelia-ui-toolkits/headless 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (284) hide show
  1. package/dist/alert/ui-alert.html +19 -0
  2. package/dist/alert/ui-alert.js +67 -0
  3. package/dist/alert/ui-alert.js.map +1 -0
  4. package/dist/alert-service/alert-configuration.js +11 -0
  5. package/dist/alert-service/alert-configuration.js.map +1 -0
  6. package/dist/alert-service/alert-modal/alert-modal.html +12 -0
  7. package/dist/alert-service/alert-modal/alert-modal.js +31 -0
  8. package/dist/alert-service/alert-modal/alert-modal.js.map +1 -0
  9. package/dist/alert-service/alert-modal/i-alert-modal-payload.js +2 -0
  10. package/dist/alert-service/alert-modal/i-alert-modal-payload.js.map +1 -0
  11. package/dist/alert-service/alert-service.js +108 -0
  12. package/dist/alert-service/alert-service.js.map +1 -0
  13. package/dist/alert-service/decorators/confirm-action.js +14 -0
  14. package/dist/alert-service/decorators/confirm-action.js.map +1 -0
  15. package/dist/alert-service/decorators/using-progress.js +23 -0
  16. package/dist/alert-service/decorators/using-progress.js.map +1 -0
  17. package/dist/alert-service/exceptions-tracker.js +4 -0
  18. package/dist/alert-service/exceptions-tracker.js.map +1 -0
  19. package/dist/alert-service/prompt-dialog/prompt-dialog.html +13 -0
  20. package/dist/alert-service/prompt-dialog/prompt-dialog.js +39 -0
  21. package/dist/alert-service/prompt-dialog/prompt-dialog.js.map +1 -0
  22. package/dist/badge/ui-badge.html +6 -0
  23. package/dist/badge/ui-badge.js +44 -0
  24. package/dist/badge/ui-badge.js.map +1 -0
  25. package/dist/base/boolean-attr.js +4 -0
  26. package/dist/base/boolean-attr.js.map +1 -0
  27. package/dist/base/i-validated-element.js +2 -0
  28. package/dist/base/i-validated-element.js.map +1 -0
  29. package/dist/base/keys.js +14 -0
  30. package/dist/base/keys.js.map +1 -0
  31. package/dist/breadcrumbs/ui-breadcrumbs.html +10 -0
  32. package/dist/breadcrumbs/ui-breadcrumbs.js +31 -0
  33. package/dist/breadcrumbs/ui-breadcrumbs.js.map +1 -0
  34. package/dist/button/enhance-ui-button.js +25 -0
  35. package/dist/button/enhance-ui-button.js.map +1 -0
  36. package/dist/button/ui-button.html +20 -0
  37. package/dist/button/ui-button.js +77 -0
  38. package/dist/button/ui-button.js.map +1 -0
  39. package/dist/button/ui-icon-button.html +20 -0
  40. package/dist/button/ui-icon-button.js +77 -0
  41. package/dist/button/ui-icon-button.js.map +1 -0
  42. package/dist/checkbox/ui-checkbox.html +32 -0
  43. package/dist/checkbox/ui-checkbox.js +186 -0
  44. package/dist/checkbox/ui-checkbox.js.map +1 -0
  45. package/dist/chip/ui-chip.html +32 -0
  46. package/dist/chip/ui-chip.js +129 -0
  47. package/dist/chip/ui-chip.js.map +1 -0
  48. package/dist/combobox/enhance-ui-combobox.js +26 -0
  49. package/dist/combobox/enhance-ui-combobox.js.map +1 -0
  50. package/dist/combobox/ui-combobox.html +76 -0
  51. package/dist/combobox/ui-combobox.js +478 -0
  52. package/dist/combobox/ui-combobox.js.map +1 -0
  53. package/dist/disclosure/ui-disclosure.html +28 -0
  54. package/dist/disclosure/ui-disclosure.js +75 -0
  55. package/dist/disclosure/ui-disclosure.js.map +1 -0
  56. package/dist/drawer/ui-drawer.html +35 -0
  57. package/dist/drawer/ui-drawer.js +246 -0
  58. package/dist/drawer/ui-drawer.js.map +1 -0
  59. package/dist/index.js +135 -0
  60. package/dist/index.js.map +1 -0
  61. package/dist/input/enhance-ui-input.js +26 -0
  62. package/dist/input/enhance-ui-input.js.map +1 -0
  63. package/dist/input/ui-input.html +56 -0
  64. package/dist/input/ui-input.js +259 -0
  65. package/dist/input/ui-input.js.map +1 -0
  66. package/dist/list/ui-list-item.html +22 -0
  67. package/dist/list/ui-list-item.js +59 -0
  68. package/dist/list/ui-list-item.js.map +1 -0
  69. package/dist/list/ui-list.html +10 -0
  70. package/dist/list/ui-list.js +316 -0
  71. package/dist/list/ui-list.js.map +1 -0
  72. package/dist/menu/ui-menu.html +20 -0
  73. package/dist/menu/ui-menu.js +154 -0
  74. package/dist/menu/ui-menu.js.map +1 -0
  75. package/dist/popup/ui-popup.html +16 -0
  76. package/dist/popup/ui-popup.js +397 -0
  77. package/dist/popup/ui-popup.js.map +1 -0
  78. package/dist/progress/ui-progress.html +17 -0
  79. package/dist/progress/ui-progress.js +76 -0
  80. package/dist/progress/ui-progress.js.map +1 -0
  81. package/dist/radio/ui-radio-group.html +27 -0
  82. package/dist/radio/ui-radio-group.js +259 -0
  83. package/dist/radio/ui-radio-group.js.map +1 -0
  84. package/dist/radio/ui-radio.html +11 -0
  85. package/dist/radio/ui-radio.js +55 -0
  86. package/dist/radio/ui-radio.js.map +1 -0
  87. package/dist/segmented-control/ui-segment.html +10 -0
  88. package/dist/segmented-control/ui-segment.js +44 -0
  89. package/dist/segmented-control/ui-segment.js.map +1 -0
  90. package/dist/segmented-control/ui-segmented-control.html +9 -0
  91. package/dist/segmented-control/ui-segmented-control.js +134 -0
  92. package/dist/segmented-control/ui-segmented-control.js.map +1 -0
  93. package/dist/select/enhance-ui-select.js +26 -0
  94. package/dist/select/enhance-ui-select.js.map +1 -0
  95. package/dist/select/ui-select.html +70 -0
  96. package/dist/select/ui-select.js +323 -0
  97. package/dist/select/ui-select.js.map +1 -0
  98. package/dist/slider/ui-slider.html +69 -0
  99. package/dist/slider/ui-slider.js +362 -0
  100. package/dist/slider/ui-slider.js.map +1 -0
  101. package/dist/splitter/ui-splitter.html +14 -0
  102. package/dist/splitter/ui-splitter.js +257 -0
  103. package/dist/splitter/ui-splitter.js.map +1 -0
  104. package/dist/switch/ui-switch.html +23 -0
  105. package/dist/switch/ui-switch.js +121 -0
  106. package/dist/switch/ui-switch.js.map +1 -0
  107. package/dist/table/enhance-ui-table.js +25 -0
  108. package/dist/table/enhance-ui-table.js.map +1 -0
  109. package/dist/table/ui-table-column.html +17 -0
  110. package/dist/table/ui-table-column.js +127 -0
  111. package/dist/table/ui-table-column.js.map +1 -0
  112. package/dist/table/ui-table.html +56 -0
  113. package/dist/table/ui-table.js +225 -0
  114. package/dist/table/ui-table.js.map +1 -0
  115. package/dist/tabs/ui-tab.html +10 -0
  116. package/dist/tabs/ui-tab.js +52 -0
  117. package/dist/tabs/ui-tab.js.map +1 -0
  118. package/dist/tabs/ui-tabs.html +3 -0
  119. package/dist/tabs/ui-tabs.js +112 -0
  120. package/dist/tabs/ui-tabs.js.map +1 -0
  121. package/dist/toast/ui-toast-region.html +11 -0
  122. package/dist/toast/ui-toast-region.js +38 -0
  123. package/dist/toast/ui-toast-region.js.map +1 -0
  124. package/dist/toast/ui-toast-service.js +70 -0
  125. package/dist/toast/ui-toast-service.js.map +1 -0
  126. package/dist/tooltip/ui-tooltip-service.js +63 -0
  127. package/dist/tooltip/ui-tooltip-service.js.map +1 -0
  128. package/dist/tooltip/ui-tooltip.js +221 -0
  129. package/dist/tooltip/ui-tooltip.js.map +1 -0
  130. package/dist/top-app-bar/ui-top-app-bar.html +24 -0
  131. package/dist/top-app-bar/ui-top-app-bar.js +68 -0
  132. package/dist/top-app-bar/ui-top-app-bar.js.map +1 -0
  133. package/dist/tree/ui-tree.html +38 -0
  134. package/dist/tree/ui-tree.js +340 -0
  135. package/dist/tree/ui-tree.js.map +1 -0
  136. package/dist/types/alert/ui-alert.d.ts +11 -0
  137. package/dist/types/alert-service/alert-configuration.d.ts +8 -0
  138. package/dist/types/alert-service/alert-modal/alert-modal.d.ts +7 -0
  139. package/dist/types/alert-service/alert-modal/i-alert-modal-payload.d.ts +14 -0
  140. package/dist/types/alert-service/alert-service.d.ts +17 -0
  141. package/dist/types/alert-service/decorators/confirm-action.d.ts +3 -0
  142. package/dist/types/alert-service/decorators/using-progress.d.ts +6 -0
  143. package/dist/types/alert-service/exceptions-tracker.d.ts +3 -0
  144. package/dist/types/alert-service/prompt-dialog/prompt-dialog.d.ts +17 -0
  145. package/dist/types/badge/ui-badge.d.ts +7 -0
  146. package/dist/types/base/boolean-attr.d.ts +1 -0
  147. package/dist/types/base/i-validated-element.d.ts +10 -0
  148. package/dist/types/base/keys.d.ts +12 -0
  149. package/dist/types/breadcrumbs/ui-breadcrumbs.d.ts +8 -0
  150. package/dist/types/button/enhance-ui-button.d.ts +3 -0
  151. package/dist/types/button/ui-button.d.ts +16 -0
  152. package/dist/types/button/ui-icon-button.d.ts +18 -0
  153. package/dist/types/checkbox/ui-checkbox.d.ts +32 -0
  154. package/dist/types/chip/ui-chip.d.ts +26 -0
  155. package/dist/types/combobox/enhance-ui-combobox.d.ts +3 -0
  156. package/dist/types/combobox/ui-combobox.d.ts +79 -0
  157. package/dist/types/disclosure/ui-disclosure.d.ts +18 -0
  158. package/dist/types/drawer/ui-drawer.d.ts +38 -0
  159. package/dist/types/index.d.ts +91 -0
  160. package/dist/types/input/enhance-ui-input.d.ts +3 -0
  161. package/dist/types/input/ui-input.d.ts +44 -0
  162. package/dist/types/list/ui-list-item.d.ts +10 -0
  163. package/dist/types/list/ui-list.d.ts +45 -0
  164. package/dist/types/menu/ui-menu.d.ts +29 -0
  165. package/dist/types/popup/ui-popup.d.ts +61 -0
  166. package/dist/types/progress/ui-progress.d.ts +15 -0
  167. package/dist/types/radio/ui-radio-group.d.ts +37 -0
  168. package/dist/types/radio/ui-radio.d.ts +11 -0
  169. package/dist/types/segmented-control/ui-segment.d.ts +8 -0
  170. package/dist/types/segmented-control/ui-segmented-control.d.ts +17 -0
  171. package/dist/types/select/enhance-ui-select.d.ts +3 -0
  172. package/dist/types/select/ui-select.d.ts +50 -0
  173. package/dist/types/slider/ui-slider.d.ts +58 -0
  174. package/dist/types/splitter/ui-splitter.d.ts +39 -0
  175. package/dist/types/switch/ui-switch.d.ts +23 -0
  176. package/dist/types/table/enhance-ui-table.d.ts +3 -0
  177. package/dist/types/table/ui-table-column.d.ts +22 -0
  178. package/dist/types/table/ui-table.d.ts +51 -0
  179. package/dist/types/tabs/ui-tab.d.ts +10 -0
  180. package/dist/types/tabs/ui-tabs.d.ts +16 -0
  181. package/dist/types/toast/ui-toast-region.d.ts +6 -0
  182. package/dist/types/toast/ui-toast-service.d.ts +28 -0
  183. package/dist/types/tooltip/ui-tooltip-service.d.ts +23 -0
  184. package/dist/types/tooltip/ui-tooltip.d.ts +35 -0
  185. package/dist/types/top-app-bar/ui-top-app-bar.d.ts +9 -0
  186. package/dist/types/tree/ui-tree.d.ts +70 -0
  187. package/dist/types/validation/ui-validation-controller-factory.d.ts +7 -0
  188. package/dist/types/validation/ui-validation-result-presenter.d.ts +4 -0
  189. package/dist/types/validation/validate.d.ts +13 -0
  190. package/dist/validation/ui-validation-controller-factory.js +17 -0
  191. package/dist/validation/ui-validation-controller-factory.js.map +1 -0
  192. package/dist/validation/ui-validation-result-presenter.js +23 -0
  193. package/dist/validation/ui-validation-result-presenter.js.map +1 -0
  194. package/dist/validation/validate.js +24 -0
  195. package/dist/validation/validate.js.map +1 -0
  196. package/package.json +50 -0
  197. package/src/alert/ui-alert.html +19 -0
  198. package/src/alert/ui-alert.ts +33 -0
  199. package/src/alert-service/alert-configuration.ts +11 -0
  200. package/src/alert-service/alert-modal/alert-modal.html +12 -0
  201. package/src/alert-service/alert-modal/alert-modal.ts +19 -0
  202. package/src/alert-service/alert-modal/i-alert-modal-payload.ts +14 -0
  203. package/src/alert-service/alert-service.ts +116 -0
  204. package/src/alert-service/decorators/confirm-action.ts +18 -0
  205. package/src/alert-service/decorators/using-progress.ts +32 -0
  206. package/src/alert-service/exceptions-tracker.ts +3 -0
  207. package/src/alert-service/prompt-dialog/prompt-dialog.html +13 -0
  208. package/src/alert-service/prompt-dialog/prompt-dialog.ts +37 -0
  209. package/src/badge/ui-badge.html +6 -0
  210. package/src/badge/ui-badge.ts +17 -0
  211. package/src/base/boolean-attr.ts +3 -0
  212. package/src/base/i-validated-element.ts +11 -0
  213. package/src/base/keys.ts +12 -0
  214. package/src/breadcrumbs/ui-breadcrumbs.html +10 -0
  215. package/src/breadcrumbs/ui-breadcrumbs.ts +14 -0
  216. package/src/button/enhance-ui-button.ts +9 -0
  217. package/src/button/ui-button.html +20 -0
  218. package/src/button/ui-button.ts +60 -0
  219. package/src/button/ui-icon-button.html +20 -0
  220. package/src/button/ui-icon-button.ts +62 -0
  221. package/src/checkbox/ui-checkbox.html +32 -0
  222. package/src/checkbox/ui-checkbox.ts +188 -0
  223. package/src/chip/ui-chip.html +32 -0
  224. package/src/chip/ui-chip.ts +118 -0
  225. package/src/combobox/enhance-ui-combobox.ts +10 -0
  226. package/src/combobox/ui-combobox.html +76 -0
  227. package/src/combobox/ui-combobox.ts +450 -0
  228. package/src/disclosure/ui-disclosure.html +28 -0
  229. package/src/disclosure/ui-disclosure.ts +68 -0
  230. package/src/drawer/ui-drawer.html +35 -0
  231. package/src/drawer/ui-drawer.ts +219 -0
  232. package/src/index.ts +152 -0
  233. package/src/input/enhance-ui-input.ts +10 -0
  234. package/src/input/ui-input.html +56 -0
  235. package/src/input/ui-input.ts +225 -0
  236. package/src/list/ui-list-item.html +22 -0
  237. package/src/list/ui-list-item.ts +25 -0
  238. package/src/list/ui-list.html +10 -0
  239. package/src/list/ui-list.ts +330 -0
  240. package/src/menu/ui-menu.html +20 -0
  241. package/src/menu/ui-menu.ts +103 -0
  242. package/src/popup/ui-popup.html +16 -0
  243. package/src/popup/ui-popup.ts +391 -0
  244. package/src/progress/ui-progress.html +17 -0
  245. package/src/progress/ui-progress.ts +51 -0
  246. package/src/radio/ui-radio-group.html +27 -0
  247. package/src/radio/ui-radio-group.ts +250 -0
  248. package/src/radio/ui-radio.html +11 -0
  249. package/src/radio/ui-radio.ts +35 -0
  250. package/src/resource.d.ts +4 -0
  251. package/src/segmented-control/ui-segment.html +10 -0
  252. package/src/segmented-control/ui-segment.ts +20 -0
  253. package/src/segmented-control/ui-segmented-control.html +9 -0
  254. package/src/segmented-control/ui-segmented-control.ts +119 -0
  255. package/src/select/enhance-ui-select.ts +10 -0
  256. package/src/select/ui-select.html +70 -0
  257. package/src/select/ui-select.ts +294 -0
  258. package/src/slider/ui-slider.html +69 -0
  259. package/src/slider/ui-slider.ts +329 -0
  260. package/src/splitter/ui-splitter.html +14 -0
  261. package/src/splitter/ui-splitter.ts +249 -0
  262. package/src/switch/ui-switch.html +23 -0
  263. package/src/switch/ui-switch.ts +121 -0
  264. package/src/table/enhance-ui-table.ts +9 -0
  265. package/src/table/ui-table-column.html +17 -0
  266. package/src/table/ui-table-column.ts +108 -0
  267. package/src/table/ui-table.html +56 -0
  268. package/src/table/ui-table.ts +209 -0
  269. package/src/tabs/ui-tab.html +10 -0
  270. package/src/tabs/ui-tab.ts +30 -0
  271. package/src/tabs/ui-tabs.html +3 -0
  272. package/src/tabs/ui-tabs.ts +105 -0
  273. package/src/toast/ui-toast-region.html +11 -0
  274. package/src/toast/ui-toast-region.ts +18 -0
  275. package/src/toast/ui-toast-service.ts +100 -0
  276. package/src/tooltip/ui-tooltip-service.ts +84 -0
  277. package/src/tooltip/ui-tooltip.ts +200 -0
  278. package/src/top-app-bar/ui-top-app-bar.html +24 -0
  279. package/src/top-app-bar/ui-top-app-bar.ts +27 -0
  280. package/src/tree/ui-tree.html +38 -0
  281. package/src/tree/ui-tree.ts +363 -0
  282. package/src/validation/ui-validation-controller-factory.ts +20 -0
  283. package/src/validation/ui-validation-result-presenter.ts +26 -0
  284. package/src/validation/validate.ts +35 -0
@@ -0,0 +1,200 @@
1
+ import { bindable, customAttribute, INode, resolve } from 'aurelia';
2
+ import { booleanAttr } from '../base/boolean-attr';
3
+ import { Keys } from '../base/keys';
4
+ import { UiTooltipService, TooltipOptions, TooltipPlacement } from './ui-tooltip-service';
5
+
6
+ @customAttribute({
7
+ name: 'ui-tooltip',
8
+ defaultProperty: 'text'
9
+ })
10
+ export class UiTooltip implements EventListenerObject {
11
+ private readonly host = resolve(INode) as HTMLElement;
12
+ private readonly tooltipService = resolve(UiTooltipService);
13
+ private openTimer: ReturnType<typeof setTimeout> | undefined;
14
+ private closeTimer: ReturnType<typeof setTimeout> | undefined;
15
+ private pointerInside = false;
16
+ private focusInside = false;
17
+ private described = false;
18
+
19
+ @bindable
20
+ text: string | undefined;
21
+ textChanged(): void {
22
+ if (this.described && this.canOpen()) {
23
+ void this.tooltipService.show(this.getOptions());
24
+ } else if (this.described) {
25
+ this.closeNow();
26
+ }
27
+ }
28
+
29
+ @bindable
30
+ placement: TooltipPlacement = 'top-start';
31
+ placementChanged(): void {
32
+ if (this.described && this.canOpen()) {
33
+ void this.tooltipService.show(this.getOptions());
34
+ }
35
+ }
36
+
37
+ @bindable
38
+ offset: number = 6;
39
+ offsetChanged(): void {
40
+ if (this.described && this.canOpen()) {
41
+ void this.tooltipService.show(this.getOptions());
42
+ }
43
+ }
44
+
45
+ @bindable
46
+ openDelay: number = 300;
47
+
48
+ @bindable
49
+ closeDelay: number = 0;
50
+
51
+ @bindable({ set: booleanAttr })
52
+ disabled: boolean = false;
53
+ disabledChanged(): void {
54
+ if (this.disabled) {
55
+ this.closeNow();
56
+ }
57
+ }
58
+
59
+ attaching(): void {
60
+ this.host.addEventListener('pointerenter', this);
61
+ this.host.addEventListener('pointerleave', this);
62
+ this.host.addEventListener('focusin', this);
63
+ this.host.addEventListener('focusout', this);
64
+ this.host.addEventListener('keydown', this);
65
+ }
66
+
67
+ detaching(): void {
68
+ this.host.removeEventListener('pointerenter', this);
69
+ this.host.removeEventListener('pointerleave', this);
70
+ this.host.removeEventListener('focusin', this);
71
+ this.host.removeEventListener('focusout', this);
72
+ this.host.removeEventListener('keydown', this);
73
+ this.clearTimers();
74
+ this.closeNow();
75
+ }
76
+
77
+ handleEvent(event: Event): void {
78
+ switch (event.type) {
79
+ case 'pointerenter':
80
+ this.pointerInside = true;
81
+ this.scheduleOpen();
82
+ break;
83
+ case 'pointerleave':
84
+ this.pointerInside = false;
85
+ this.scheduleClose();
86
+ break;
87
+ case 'focusin':
88
+ this.focusInside = true;
89
+ this.scheduleOpen();
90
+ break;
91
+ case 'focusout':
92
+ this.focusInside = false;
93
+ this.scheduleClose();
94
+ break;
95
+ case 'keydown':
96
+ if ((event as KeyboardEvent).key === Keys.Escape) {
97
+ this.closeNow();
98
+ }
99
+ break;
100
+ }
101
+ }
102
+
103
+ private scheduleOpen(): void {
104
+ if (!this.canOpen()) {
105
+ return;
106
+ }
107
+
108
+ this.clearCloseTimer();
109
+ this.clearOpenTimer();
110
+ this.openTimer = setTimeout(() => {
111
+ this.addDescribedBy();
112
+ void this.tooltipService.show(this.getOptions());
113
+ }, this.getDelay(this.openDelay));
114
+ }
115
+
116
+ private scheduleClose(): void {
117
+ this.clearOpenTimer();
118
+ this.clearCloseTimer();
119
+ this.closeTimer = setTimeout(() => {
120
+ if (!this.pointerInside && !this.focusInside) {
121
+ this.closeNow();
122
+ }
123
+ }, this.getDelay(this.closeDelay));
124
+ }
125
+
126
+ private closeNow(): void {
127
+ this.clearTimers();
128
+ this.removeDescribedBy();
129
+ this.tooltipService.hide(this.host);
130
+ }
131
+
132
+ private getOptions(): TooltipOptions {
133
+ return {
134
+ anchor: this.host,
135
+ text: this.text ?? '',
136
+ placement: this.placement,
137
+ offset: Number(this.offset) || 0
138
+ };
139
+ }
140
+
141
+ private canOpen(): boolean {
142
+ return !this.disabled && !!this.text;
143
+ }
144
+
145
+ private getDelay(value: number): number {
146
+ return Math.max(0, Number(value) || 0);
147
+ }
148
+
149
+ private clearTimers(): void {
150
+ this.clearOpenTimer();
151
+ this.clearCloseTimer();
152
+ }
153
+
154
+ private clearOpenTimer(): void {
155
+ if (this.openTimer) {
156
+ clearTimeout(this.openTimer);
157
+ this.openTimer = undefined;
158
+ }
159
+ }
160
+
161
+ private clearCloseTimer(): void {
162
+ if (this.closeTimer) {
163
+ clearTimeout(this.closeTimer);
164
+ this.closeTimer = undefined;
165
+ }
166
+ }
167
+
168
+ private addDescribedBy(): void {
169
+ if (this.described) {
170
+ return;
171
+ }
172
+
173
+ const ids = this.getDescribedByIds();
174
+ if (!ids.includes(this.tooltipService.tooltipId)) {
175
+ ids.push(this.tooltipService.tooltipId);
176
+ this.host.setAttribute('aria-describedby', ids.join(' '));
177
+ }
178
+ this.described = true;
179
+ }
180
+
181
+ private removeDescribedBy(): void {
182
+ if (!this.described) {
183
+ return;
184
+ }
185
+
186
+ const ids = this.getDescribedByIds().filter(id => id !== this.tooltipService.tooltipId);
187
+ if (ids.length) {
188
+ this.host.setAttribute('aria-describedby', ids.join(' '));
189
+ } else {
190
+ this.host.removeAttribute('aria-describedby');
191
+ }
192
+ this.described = false;
193
+ }
194
+
195
+ private getDescribedByIds(): string[] {
196
+ return (this.host.getAttribute('aria-describedby') ?? '')
197
+ .split(/\s+/)
198
+ .filter(Boolean);
199
+ }
200
+ }
@@ -0,0 +1,24 @@
1
+ <template class="ui-top-app-bar"
2
+ data-sticky.attr="sticky ? '' : null"
3
+ data-fixed.attr="fixed ? '' : null"
4
+ data-dense.attr="dense ? '' : null"
5
+ data-elevated.attr="elevated ? '' : null">
6
+ <header class="ui-top-app-bar__header">
7
+ <div class="ui-top-app-bar__navigation">
8
+ <au-slot name="navigation"></au-slot>
9
+ </div>
10
+
11
+ <div class="ui-top-app-bar__content">
12
+ <div class="ui-top-app-bar__breadcrumbs">
13
+ <au-slot name="breadcrumbs"></au-slot>
14
+ </div>
15
+ <div class="ui-top-app-bar__title">
16
+ <au-slot></au-slot>
17
+ </div>
18
+ </div>
19
+
20
+ <div class="ui-top-app-bar__actions">
21
+ <au-slot name="actions"></au-slot>
22
+ </div>
23
+ </header>
24
+ </template>
@@ -0,0 +1,27 @@
1
+ import { bindable, customElement, slotted } from 'aurelia';
2
+ import { booleanAttr } from '../base/boolean-attr';
3
+ import template from './ui-top-app-bar.html?raw';
4
+
5
+ @customElement({ name: 'ui-top-app-bar', template })
6
+ export class UiTopAppBar {
7
+ @bindable({ set: booleanAttr })
8
+ sticky: boolean = false;
9
+
10
+ @bindable({ set: booleanAttr })
11
+ fixed: boolean = false;
12
+
13
+ @bindable({ set: booleanAttr })
14
+ dense: boolean = false;
15
+
16
+ @bindable({ set: booleanAttr })
17
+ elevated: boolean = false;
18
+
19
+ @slotted({ slotName: 'navigation' })
20
+ navigationNodes: readonly Node[] = [];
21
+
22
+ @slotted({ slotName: 'breadcrumbs' })
23
+ breadcrumbNodes: readonly Node[] = [];
24
+
25
+ @slotted({ slotName: 'actions' })
26
+ actionNodes: readonly Node[] = [];
27
+ }
@@ -0,0 +1,38 @@
1
+ <template class="ui-tree"
2
+ role="tree"
3
+ aria-label.attr="label || null"
4
+ aria-disabled.attr="disabled ? '' : null"
5
+ data-disabled.attr="disabled ? '' : null"
6
+ keydown.trigger="onKeyDown($event)">
7
+ <div class="ui-tree__item"
8
+ repeat.for="row of visibleRows"
9
+ ref="row.element"
10
+ role="treeitem"
11
+ tabindex.bind="!disabled && !row.disabled && activeValue === row.value ? 0 : -1"
12
+ aria-level.attr="row.depth + 1"
13
+ aria-posinset.attr="row.posInSet"
14
+ aria-setsize.attr="row.setSize"
15
+ aria-expanded.attr="row.children.length ? row.expanded ? 'true' : 'false' : null"
16
+ aria-selected.attr="selectionMode === 'none' ? null : value === row.value ? 'true' : 'false'"
17
+ aria-disabled.attr="disabled || row.disabled ? '' : null"
18
+ data-tree-row-id.attr="row.id"
19
+ data-depth.attr="row.depth"
20
+ data-expanded.attr="row.expanded ? '' : null"
21
+ data-selected.attr="value === row.value ? '' : null"
22
+ data-active.attr="activeValue === row.value ? '' : null"
23
+ data-disabled.attr="disabled || row.disabled ? '' : null"
24
+ style.bind="`--ui-tree-depth: ${row.depth}`"
25
+ click.trigger="onRowClick(row)">
26
+ <button class="ui-tree__toggle"
27
+ type="button"
28
+ tabindex="-1"
29
+ aria-hidden="true"
30
+ data-hidden.attr="row.children.length ? null : ''"
31
+ click.trigger="onToggleClick(row, $event)">
32
+ <span class="ui-tree__toggle-icon"></span>
33
+ </button>
34
+ <div class="ui-tree__content">
35
+ <au-slot name="item" expose.bind="row.slotHost">${row.label}</au-slot>
36
+ </div>
37
+ </div>
38
+ </template>
@@ -0,0 +1,363 @@
1
+ import { bindable, BindingMode, customElement, INode, resolve } from 'aurelia';
2
+ import { booleanAttr } from '../base/boolean-attr';
3
+ import { Keys } from '../base/keys';
4
+ import template from './ui-tree.html?raw';
5
+
6
+ type TreeField<T> = string | ((item: T) => unknown);
7
+ type TreeSelectionMode = 'single' | 'none';
8
+
9
+ export interface UiTreeRow {
10
+ id: string;
11
+ item: unknown;
12
+ value: unknown;
13
+ label: string;
14
+ depth: number;
15
+ parent: UiTreeRow | undefined;
16
+ children: UiTreeRow[];
17
+ expanded: boolean;
18
+ disabled: boolean;
19
+ posInSet: number;
20
+ setSize: number;
21
+ element: HTMLElement | undefined;
22
+ slotHost: UiTreeRowSlotHost;
23
+ }
24
+
25
+ export interface UiTreeRowSlotHost {
26
+ item: unknown;
27
+ row: UiTreeRow;
28
+ }
29
+
30
+ @customElement({ name: 'ui-tree', template })
31
+ export class UiTree {
32
+ private readonly host = resolve(INode) as HTMLElement;
33
+ private expandedValues = new Set<unknown>();
34
+
35
+ @bindable
36
+ items: unknown[] = [];
37
+ itemsChanged(): void {
38
+ this.rebuildRows();
39
+ }
40
+
41
+ @bindable({ mode: BindingMode.twoWay })
42
+ value: unknown;
43
+ valueChanged(): void {
44
+ if (this.value !== undefined) {
45
+ this.activeValue = this.value;
46
+ }
47
+ }
48
+
49
+ @bindable
50
+ label: string | undefined;
51
+
52
+ @bindable
53
+ childrenField: TreeField<unknown> = 'children';
54
+ childrenFieldChanged(): void {
55
+ this.rebuildRows();
56
+ }
57
+
58
+ @bindable
59
+ valueField: TreeField<unknown> = 'value';
60
+ valueFieldChanged(): void {
61
+ this.rebuildRows();
62
+ }
63
+
64
+ @bindable
65
+ labelField: TreeField<unknown> = 'label';
66
+ labelFieldChanged(): void {
67
+ this.rebuildRows();
68
+ }
69
+
70
+ @bindable
71
+ disabledField: TreeField<unknown> = 'disabled';
72
+ disabledFieldChanged(): void {
73
+ this.rebuildRows();
74
+ }
75
+
76
+ @bindable
77
+ expandedField: TreeField<unknown> = 'expanded';
78
+ expandedFieldChanged(): void {
79
+ this.rebuildRows(true);
80
+ }
81
+
82
+ @bindable
83
+ selectionMode: TreeSelectionMode = 'single';
84
+
85
+ @bindable({ set: booleanAttr })
86
+ disabled: boolean = false;
87
+
88
+ visibleRows: UiTreeRow[] = [];
89
+ activeValue: unknown;
90
+
91
+ binding(): void {
92
+ this.rebuildRows(true);
93
+ }
94
+
95
+ onRowClick(row: UiTreeRow): void {
96
+ this.focusRow(row);
97
+ this.selectRow(row);
98
+ }
99
+
100
+ onToggleClick(row: UiTreeRow, event: Event): void {
101
+ event.stopPropagation();
102
+ this.focusRow(row);
103
+ this.toggleRow(row);
104
+ }
105
+
106
+ onKeyDown(event: KeyboardEvent): void {
107
+ if (this.disabled) {
108
+ return;
109
+ }
110
+
111
+ const row = this.getFocusedRow();
112
+ if (!row) {
113
+ return;
114
+ }
115
+
116
+ if (event.key === Keys.ArrowDown) {
117
+ event.preventDefault();
118
+ this.focusSibling(row, 1);
119
+ return;
120
+ }
121
+
122
+ if (event.key === Keys.ArrowUp) {
123
+ event.preventDefault();
124
+ this.focusSibling(row, -1);
125
+ return;
126
+ }
127
+
128
+ if (event.key === Keys.ArrowRight) {
129
+ event.preventDefault();
130
+ this.expandOrEnter(row);
131
+ return;
132
+ }
133
+
134
+ if (event.key === Keys.ArrowLeft) {
135
+ event.preventDefault();
136
+ this.collapseOrExit(row);
137
+ return;
138
+ }
139
+
140
+ if (event.key === Keys.Home) {
141
+ event.preventDefault();
142
+ this.focusFirst();
143
+ return;
144
+ }
145
+
146
+ if (event.key === Keys.End) {
147
+ event.preventDefault();
148
+ this.focusLast();
149
+ return;
150
+ }
151
+
152
+ if (event.key === Keys.Enter || event.key === Keys.Space) {
153
+ event.preventDefault();
154
+ this.selectRow(row);
155
+ }
156
+ }
157
+
158
+ private rebuildRows(applyInitialExpanded = false): void {
159
+ if (applyInitialExpanded) {
160
+ this.expandedValues.clear();
161
+ }
162
+
163
+ const roots = Array.isArray(this.items) ? this.items : [];
164
+ const rows = this.buildRows(roots, 0, undefined, '0', applyInitialExpanded);
165
+ this.visibleRows = this.flattenVisibleRows(rows);
166
+ if (this.activeValue === undefined) {
167
+ this.activeValue = this.value ?? this.visibleRows.find(row => !row.disabled)?.value;
168
+ }
169
+ }
170
+
171
+ private buildRows(items: unknown[], depth: number, parent: UiTreeRow | undefined, path: string, applyInitialExpanded: boolean): UiTreeRow[] {
172
+ return items.map((item, index) => {
173
+ const value = this.getFieldValue(item, this.valueField) ?? item;
174
+ const children = this.getChildren(item);
175
+ if (applyInitialExpanded && this.getBooleanFieldValue(item, this.expandedField)) {
176
+ this.expandedValues.add(value);
177
+ }
178
+
179
+ const row = {
180
+ id: `${path}-${index}`,
181
+ item,
182
+ value,
183
+ label: this.getLabel(item),
184
+ depth,
185
+ parent,
186
+ children: [] as UiTreeRow[],
187
+ expanded: this.expandedValues.has(value),
188
+ disabled: this.getBooleanFieldValue(item, this.disabledField),
189
+ posInSet: index + 1,
190
+ setSize: items.length,
191
+ element: undefined,
192
+ slotHost: undefined as unknown as UiTreeRowSlotHost
193
+ } satisfies UiTreeRow;
194
+ row.slotHost = { item, row };
195
+ row.children = this.buildRows(children, depth + 1, row, row.id, applyInitialExpanded);
196
+ return row;
197
+ });
198
+ }
199
+
200
+ private flattenVisibleRows(rows: UiTreeRow[]): UiTreeRow[] {
201
+ const visible: UiTreeRow[] = [];
202
+ for (const row of rows) {
203
+ visible.push(row);
204
+ if (row.expanded && row.children.length) {
205
+ visible.push(...this.flattenVisibleRows(row.children));
206
+ }
207
+ }
208
+ return visible;
209
+ }
210
+
211
+ private getChildren(item: unknown): unknown[] {
212
+ const children = this.getFieldValue(item, this.childrenField);
213
+ return Array.isArray(children) ? children : [];
214
+ }
215
+
216
+ private getLabel(item: unknown): string {
217
+ const label = this.getFieldValue(item, this.labelField);
218
+ return label === undefined || label === null ? '' : String(label);
219
+ }
220
+
221
+ private getBooleanFieldValue(item: unknown, field: TreeField<unknown>): boolean {
222
+ return !!this.getFieldValue(item, field);
223
+ }
224
+
225
+ private getFieldValue(item: unknown, field: TreeField<unknown>): unknown {
226
+ if (typeof field === 'function') {
227
+ return field(item);
228
+ }
229
+
230
+ if (item && typeof item === 'object') {
231
+ return (item as Record<string, unknown>)[field];
232
+ }
233
+
234
+ return undefined;
235
+ }
236
+
237
+ private getFocusedRow(): UiTreeRow | undefined {
238
+ const element = document.activeElement instanceof HTMLElement ? document.activeElement.closest('[data-tree-row-id]') : null;
239
+ const id = element?.getAttribute('data-tree-row-id');
240
+ return this.visibleRows.find(row => row.id === id) ?? this.visibleRows.find(row => row.value === this.activeValue);
241
+ }
242
+
243
+ private focusSibling(row: UiTreeRow, direction: 1 | -1): void {
244
+ const rows = this.enabledRows;
245
+ const index = rows.indexOf(row);
246
+ if (index < 0) {
247
+ return;
248
+ }
249
+
250
+ this.focusRow(rows[Math.max(0, Math.min(rows.length - 1, index + direction))]);
251
+ }
252
+
253
+ private expandOrEnter(row: UiTreeRow): void {
254
+ if (!row.children.length) {
255
+ return;
256
+ }
257
+
258
+ if (!row.expanded) {
259
+ this.expandRow(row);
260
+ return;
261
+ }
262
+
263
+ const child = row.children.find(item => !item.disabled);
264
+ if (child) {
265
+ this.focusRow(child);
266
+ }
267
+ }
268
+
269
+ private collapseOrExit(row: UiTreeRow): void {
270
+ if (row.children.length && row.expanded) {
271
+ this.collapseRow(row);
272
+ return;
273
+ }
274
+
275
+ if (row.parent) {
276
+ this.focusRow(row.parent);
277
+ }
278
+ }
279
+
280
+ private focusFirst(): void {
281
+ const row = this.enabledRows[0];
282
+ if (row) {
283
+ this.focusRow(row);
284
+ }
285
+ }
286
+
287
+ private focusLast(): void {
288
+ const rows = this.enabledRows;
289
+ const row = rows[rows.length - 1];
290
+ if (row) {
291
+ this.focusRow(row);
292
+ }
293
+ }
294
+
295
+ private get enabledRows(): UiTreeRow[] {
296
+ return this.visibleRows.filter(row => !row.disabled);
297
+ }
298
+
299
+ private focusRow(row: UiTreeRow): void {
300
+ if (row.disabled) {
301
+ return;
302
+ }
303
+
304
+ this.activeValue = row.value;
305
+ row.element?.focus();
306
+ }
307
+
308
+ private selectRow(row: UiTreeRow): void {
309
+ if (this.disabled || row.disabled || this.selectionMode === 'none' || this.value === row.value) {
310
+ return;
311
+ }
312
+
313
+ this.value = row.value;
314
+ this.activeValue = row.value;
315
+ this.host.dispatchEvent(new CustomEvent('tree-select', { bubbles: true, detail: row.item }));
316
+ this.dispatchValueEvent('input');
317
+ this.dispatchValueEvent('change');
318
+ }
319
+
320
+ private toggleRow(row: UiTreeRow): void {
321
+ if (!row.children.length || row.disabled) {
322
+ return;
323
+ }
324
+
325
+ if (row.expanded) {
326
+ this.collapseRow(row);
327
+ } else {
328
+ this.expandRow(row);
329
+ }
330
+ }
331
+
332
+ private expandRow(row: UiTreeRow): void {
333
+ this.expandedValues.add(row.value);
334
+ this.host.dispatchEvent(new CustomEvent('tree-expand', { bubbles: true, detail: row.item }));
335
+ this.activeValue = row.value;
336
+ this.rebuildRows();
337
+ this.restoreFocus(row.value);
338
+ }
339
+
340
+ private collapseRow(row: UiTreeRow): void {
341
+ this.expandedValues.delete(row.value);
342
+ this.host.dispatchEvent(new CustomEvent('tree-collapse', { bubbles: true, detail: row.item }));
343
+ this.activeValue = row.value;
344
+ this.rebuildRows();
345
+ this.restoreFocus(row.value);
346
+ }
347
+
348
+ private restoreFocus(value: unknown): void {
349
+ const row = this.visibleRows.find(item => item.value === value);
350
+ if (row?.element) {
351
+ row.element.focus();
352
+ return;
353
+ }
354
+
355
+ queueMicrotask(() => {
356
+ this.visibleRows.find(item => item.value === value)?.element?.focus();
357
+ });
358
+ }
359
+
360
+ private dispatchValueEvent(type: 'input' | 'change'): void {
361
+ this.host.dispatchEvent(new Event(type, { bubbles: true }));
362
+ }
363
+ }
@@ -0,0 +1,20 @@
1
+ import { IContainer, IFactory, Constructable, Transformer, Key } from '@aurelia/kernel';
2
+ import { IValidationController, ValidationController } from '@aurelia/validation-html';
3
+ import { UiValidationResultPresenter } from './ui-validation-result-presenter';
4
+
5
+ export class UiValidationControllerFactory implements IFactory<Constructable<IValidationController>> {
6
+ public Type: Constructable<IValidationController> = (void 0)!;
7
+
8
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
9
+ public registerTransformer(_transformer: Transformer<Constructable<IValidationController>>): boolean {
10
+ return false;
11
+ }
12
+
13
+ public construct(_: IContainer, _dynamicDependencies?: Key[] | undefined): IValidationController {
14
+ const controller: IValidationController = _dynamicDependencies !== void 0
15
+ ? Reflect.construct(ValidationController, _dynamicDependencies)
16
+ : new ValidationController();
17
+ controller.addSubscriber(new UiValidationResultPresenter());
18
+ return controller;
19
+ }
20
+ }
@@ -0,0 +1,26 @@
1
+ import { IValidatedElement } from '../base/i-validated-element';
2
+ import { ValidationEvent, ValidationResultsSubscriber } from '@aurelia/validation-html';
3
+
4
+ export class UiValidationResultPresenter implements ValidationResultsSubscriber {
5
+ handleValidationEvent(event: ValidationEvent): void {
6
+ for (let i = 0; i < event.removedResults.length; ++i) {
7
+ const ri = event.removedResults[i];
8
+ for (let j = 0; j < ri.targets.length; ++j) {
9
+ const el = ri.targets[j] as IValidatedElement;
10
+ if (!ri.result.valid && Object.getOwnPropertyDescriptor(el, 'removeError')) {
11
+ el.removeError(ri.result);
12
+ }
13
+ }
14
+ }
15
+
16
+ for (let i = 0; i < event.addedResults.length; ++i) {
17
+ const ri = event.addedResults[i];
18
+ for (let j = 0; j < ri.targets.length; ++j) {
19
+ const el = ri.targets[j] as IValidatedElement;
20
+ if (!ri.result.valid && Object.getOwnPropertyDescriptor(el, 'addError')) {
21
+ el.addError(ri.result);
22
+ }
23
+ }
24
+ }
25
+ }
26
+ }