@area8975/ui 0.2.2-dev.48 → 0.2.2-dev.50

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 (182) hide show
  1. package/README.md +3 -2
  2. package/fesm2022/area8975-ui-alert.mjs +113 -0
  3. package/fesm2022/area8975-ui-alert.mjs.map +1 -0
  4. package/fesm2022/area8975-ui-avatar.mjs +157 -0
  5. package/fesm2022/area8975-ui-avatar.mjs.map +1 -0
  6. package/fesm2022/area8975-ui-badge.mjs +116 -0
  7. package/fesm2022/area8975-ui-badge.mjs.map +1 -0
  8. package/fesm2022/area8975-ui-button.mjs +314 -0
  9. package/fesm2022/area8975-ui-button.mjs.map +1 -0
  10. package/fesm2022/area8975-ui-calendar.mjs +549 -0
  11. package/fesm2022/area8975-ui-calendar.mjs.map +1 -0
  12. package/fesm2022/area8975-ui-card-header.mjs +96 -0
  13. package/fesm2022/area8975-ui-card-header.mjs.map +1 -0
  14. package/fesm2022/area8975-ui-card.mjs +73 -0
  15. package/fesm2022/area8975-ui-card.mjs.map +1 -0
  16. package/fesm2022/area8975-ui-chart-palette.mjs +225 -0
  17. package/fesm2022/area8975-ui-chart-palette.mjs.map +1 -0
  18. package/fesm2022/area8975-ui-charts.mjs +2813 -0
  19. package/fesm2022/area8975-ui-charts.mjs.map +1 -0
  20. package/fesm2022/area8975-ui-checkbox.mjs +202 -0
  21. package/fesm2022/area8975-ui-checkbox.mjs.map +1 -0
  22. package/fesm2022/area8975-ui-colour.mjs +200 -0
  23. package/fesm2022/area8975-ui-colour.mjs.map +1 -0
  24. package/fesm2022/area8975-ui-container.mjs +40 -0
  25. package/fesm2022/area8975-ui-container.mjs.map +1 -0
  26. package/fesm2022/area8975-ui-counter.mjs +246 -0
  27. package/fesm2022/area8975-ui-counter.mjs.map +1 -0
  28. package/fesm2022/area8975-ui-date-range-picker.mjs +554 -0
  29. package/fesm2022/area8975-ui-date-range-picker.mjs.map +1 -0
  30. package/fesm2022/area8975-ui-datepicker.mjs +547 -0
  31. package/fesm2022/area8975-ui-datepicker.mjs.map +1 -0
  32. package/fesm2022/area8975-ui-disclosure.mjs +279 -0
  33. package/fesm2022/area8975-ui-disclosure.mjs.map +1 -0
  34. package/fesm2022/area8975-ui-drawer.mjs +106 -0
  35. package/fesm2022/area8975-ui-drawer.mjs.map +1 -0
  36. package/fesm2022/area8975-ui-dropdown-button.mjs +141 -0
  37. package/fesm2022/area8975-ui-dropdown-button.mjs.map +1 -0
  38. package/fesm2022/area8975-ui-dropdown-menu.mjs +96 -0
  39. package/fesm2022/area8975-ui-dropdown-menu.mjs.map +1 -0
  40. package/fesm2022/area8975-ui-dropdown.mjs +208 -0
  41. package/fesm2022/area8975-ui-dropdown.mjs.map +1 -0
  42. package/fesm2022/area8975-ui-dropzone.mjs +332 -0
  43. package/fesm2022/area8975-ui-dropzone.mjs.map +1 -0
  44. package/fesm2022/area8975-ui-field.mjs +191 -0
  45. package/fesm2022/area8975-ui-field.mjs.map +1 -0
  46. package/fesm2022/area8975-ui-file-input.mjs +340 -0
  47. package/fesm2022/area8975-ui-file-input.mjs.map +1 -0
  48. package/fesm2022/area8975-ui-file.mjs +47 -0
  49. package/fesm2022/area8975-ui-file.mjs.map +1 -0
  50. package/fesm2022/area8975-ui-format.mjs +70 -0
  51. package/fesm2022/area8975-ui-format.mjs.map +1 -0
  52. package/fesm2022/area8975-ui-input-addon.mjs +42 -0
  53. package/fesm2022/area8975-ui-input-addon.mjs.map +1 -0
  54. package/fesm2022/area8975-ui-input-group.mjs +86 -0
  55. package/fesm2022/area8975-ui-input-group.mjs.map +1 -0
  56. package/fesm2022/area8975-ui-input.mjs +682 -0
  57. package/fesm2022/area8975-ui-input.mjs.map +1 -0
  58. package/fesm2022/area8975-ui-kpi.mjs +167 -0
  59. package/fesm2022/area8975-ui-kpi.mjs.map +1 -0
  60. package/fesm2022/area8975-ui-layout.mjs +63 -0
  61. package/fesm2022/area8975-ui-layout.mjs.map +1 -0
  62. package/fesm2022/area8975-ui-loading.mjs +76 -0
  63. package/fesm2022/area8975-ui-loading.mjs.map +1 -0
  64. package/fesm2022/area8975-ui-modal-close-btn.mjs +89 -0
  65. package/fesm2022/area8975-ui-modal-close-btn.mjs.map +1 -0
  66. package/fesm2022/area8975-ui-modal.mjs +235 -0
  67. package/fesm2022/area8975-ui-modal.mjs.map +1 -0
  68. package/fesm2022/area8975-ui-money.mjs +230 -0
  69. package/fesm2022/area8975-ui-money.mjs.map +1 -0
  70. package/fesm2022/area8975-ui-overlay.mjs +317 -0
  71. package/fesm2022/area8975-ui-overlay.mjs.map +1 -0
  72. package/fesm2022/area8975-ui-pagination.mjs +152 -0
  73. package/fesm2022/area8975-ui-pagination.mjs.map +1 -0
  74. package/fesm2022/area8975-ui-private.mjs +63 -0
  75. package/fesm2022/area8975-ui-private.mjs.map +1 -0
  76. package/fesm2022/area8975-ui-progress.mjs +307 -0
  77. package/fesm2022/area8975-ui-progress.mjs.map +1 -0
  78. package/fesm2022/area8975-ui-radio.mjs +199 -0
  79. package/fesm2022/area8975-ui-radio.mjs.map +1 -0
  80. package/fesm2022/area8975-ui-rich-text-editor.mjs +778 -0
  81. package/fesm2022/area8975-ui-rich-text-editor.mjs.map +1 -0
  82. package/fesm2022/area8975-ui-search.mjs +266 -0
  83. package/fesm2022/area8975-ui-search.mjs.map +1 -0
  84. package/fesm2022/area8975-ui-segmented.mjs +118 -0
  85. package/fesm2022/area8975-ui-segmented.mjs.map +1 -0
  86. package/fesm2022/area8975-ui-select.mjs +926 -0
  87. package/fesm2022/area8975-ui-select.mjs.map +1 -0
  88. package/fesm2022/area8975-ui-sidebar-group.mjs +60 -0
  89. package/fesm2022/area8975-ui-sidebar-group.mjs.map +1 -0
  90. package/fesm2022/area8975-ui-sidebar-item.mjs +107 -0
  91. package/fesm2022/area8975-ui-sidebar-item.mjs.map +1 -0
  92. package/fesm2022/area8975-ui-sidebar.mjs +109 -0
  93. package/fesm2022/area8975-ui-sidebar.mjs.map +1 -0
  94. package/fesm2022/area8975-ui-skeleton.mjs +116 -0
  95. package/fesm2022/area8975-ui-skeleton.mjs.map +1 -0
  96. package/fesm2022/area8975-ui-stat.mjs +55 -0
  97. package/fesm2022/area8975-ui-stat.mjs.map +1 -0
  98. package/fesm2022/area8975-ui-switch.mjs +132 -0
  99. package/fesm2022/area8975-ui-switch.mjs.map +1 -0
  100. package/fesm2022/area8975-ui-tab.mjs +217 -0
  101. package/fesm2022/area8975-ui-tab.mjs.map +1 -0
  102. package/fesm2022/area8975-ui-table-filters.mjs +499 -0
  103. package/fesm2022/area8975-ui-table-filters.mjs.map +1 -0
  104. package/fesm2022/area8975-ui-table.mjs +1237 -0
  105. package/fesm2022/area8975-ui-table.mjs.map +1 -0
  106. package/fesm2022/area8975-ui-textarea.mjs +375 -0
  107. package/fesm2022/area8975-ui-textarea.mjs.map +1 -0
  108. package/fesm2022/area8975-ui-theme.mjs +246 -0
  109. package/fesm2022/area8975-ui-theme.mjs.map +1 -0
  110. package/fesm2022/area8975-ui-time-select.mjs +170 -0
  111. package/fesm2022/area8975-ui-time-select.mjs.map +1 -0
  112. package/fesm2022/area8975-ui-toast.mjs +269 -0
  113. package/fesm2022/area8975-ui-toast.mjs.map +1 -0
  114. package/fesm2022/area8975-ui-toaster.mjs +117 -0
  115. package/fesm2022/area8975-ui-toaster.mjs.map +1 -0
  116. package/fesm2022/area8975-ui-tooltip.mjs +196 -0
  117. package/fesm2022/area8975-ui-tooltip.mjs.map +1 -0
  118. package/fesm2022/area8975-ui-user-menu.mjs +186 -0
  119. package/fesm2022/area8975-ui-user-menu.mjs.map +1 -0
  120. package/fesm2022/area8975-ui.mjs +0 -16269
  121. package/package.json +237 -1
  122. package/types/area8975-ui-alert.d.ts +25 -0
  123. package/types/area8975-ui-avatar.d.ts +40 -0
  124. package/types/area8975-ui-badge.d.ts +31 -0
  125. package/types/area8975-ui-button.d.ts +129 -0
  126. package/types/area8975-ui-calendar.d.ts +177 -0
  127. package/types/area8975-ui-card-header.d.ts +28 -0
  128. package/types/area8975-ui-card.d.ts +18 -0
  129. package/types/area8975-ui-chart-palette.d.ts +176 -0
  130. package/types/area8975-ui-charts.d.ts +747 -0
  131. package/types/area8975-ui-checkbox.d.ts +61 -0
  132. package/types/area8975-ui-colour.d.ts +31 -0
  133. package/types/area8975-ui-container.d.ts +5 -0
  134. package/types/area8975-ui-counter.d.ts +43 -0
  135. package/types/area8975-ui-date-range-picker.d.ts +132 -0
  136. package/types/area8975-ui-datepicker.d.ts +124 -0
  137. package/types/area8975-ui-disclosure.d.ts +79 -0
  138. package/types/area8975-ui-drawer.d.ts +25 -0
  139. package/types/area8975-ui-dropdown-button.d.ts +32 -0
  140. package/types/area8975-ui-dropdown-menu.d.ts +25 -0
  141. package/types/area8975-ui-dropdown.d.ts +56 -0
  142. package/types/area8975-ui-dropzone.d.ts +96 -0
  143. package/types/area8975-ui-field.d.ts +72 -0
  144. package/types/area8975-ui-file-input.d.ts +72 -0
  145. package/types/area8975-ui-file.d.ts +21 -0
  146. package/types/area8975-ui-format.d.ts +37 -0
  147. package/types/area8975-ui-input-addon.d.ts +10 -0
  148. package/types/area8975-ui-input-group.d.ts +18 -0
  149. package/types/area8975-ui-input.d.ts +224 -0
  150. package/types/area8975-ui-kpi.d.ts +36 -0
  151. package/types/area8975-ui-layout.d.ts +7 -0
  152. package/types/area8975-ui-loading.d.ts +16 -0
  153. package/types/area8975-ui-modal-close-btn.d.ts +14 -0
  154. package/types/area8975-ui-modal.d.ts +45 -0
  155. package/types/area8975-ui-money.d.ts +77 -0
  156. package/types/area8975-ui-overlay.d.ts +75 -0
  157. package/types/area8975-ui-pagination.d.ts +27 -0
  158. package/types/area8975-ui-private.d.ts +20 -0
  159. package/types/area8975-ui-progress.d.ts +127 -0
  160. package/types/area8975-ui-radio.d.ts +61 -0
  161. package/types/area8975-ui-rich-text-editor.d.ts +165 -0
  162. package/types/area8975-ui-search.d.ts +56 -0
  163. package/types/area8975-ui-segmented.d.ts +41 -0
  164. package/types/area8975-ui-select.d.ts +293 -0
  165. package/types/area8975-ui-sidebar-group.d.ts +15 -0
  166. package/types/area8975-ui-sidebar-item.d.ts +20 -0
  167. package/types/area8975-ui-sidebar.d.ts +16 -0
  168. package/types/area8975-ui-skeleton.d.ts +35 -0
  169. package/types/area8975-ui-stat.d.ts +13 -0
  170. package/types/area8975-ui-switch.d.ts +32 -0
  171. package/types/area8975-ui-tab.d.ts +45 -0
  172. package/types/area8975-ui-table-filters.d.ts +138 -0
  173. package/types/area8975-ui-table.d.ts +371 -0
  174. package/types/area8975-ui-textarea.d.ts +126 -0
  175. package/types/area8975-ui-theme.d.ts +61 -0
  176. package/types/area8975-ui-time-select.d.ts +35 -0
  177. package/types/area8975-ui-toast.d.ts +78 -0
  178. package/types/area8975-ui-toaster.d.ts +23 -0
  179. package/types/area8975-ui-tooltip.d.ts +47 -0
  180. package/types/area8975-ui-user-menu.d.ts +39 -0
  181. package/types/area8975-ui.d.ts +1 -4464
  182. package/fesm2022/area8975-ui.mjs.map +0 -1
@@ -0,0 +1,340 @@
1
+ import * as i0 from "@angular/core";
2
+ import { Component, booleanAttribute, computed, input, model, output, signal, viewChild } from "@angular/core";
3
+ import { UiButtonComponent } from "@area8975/ui/button";
4
+ import { selectFiles } from "@area8975/ui/file";
5
+ import { UiInputComponent } from "@area8975/ui/input";
6
+ import { UiInputGroupComponent } from "@area8975/ui/input-group";
7
+ import { NgIcon, provideIcons } from "@ng-icons/core";
8
+ import { heroArrowUp, heroXMark } from "@ng-icons/heroicons/outline";
9
+ var UiFileInputComponent = class UiFileInputComponent {
10
+ value = model([], ...ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []);
11
+ id = input("", ...ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []);
12
+ accept = input("", ...ngDevMode ? [{ debugName: "accept" }] : /* istanbul ignore next */ []);
13
+ multiple = input(false, {
14
+ ...ngDevMode ? { debugName: "multiple" } : /* istanbul ignore next */ {},
15
+ transform: booleanAttribute
16
+ });
17
+ disabled = input(false, {
18
+ ...ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {},
19
+ transform: booleanAttribute
20
+ });
21
+ describedBy = input("", ...ngDevMode ? [{ debugName: "describedBy" }] : /* istanbul ignore next */ []);
22
+ maxSize = input(null, ...ngDevMode ? [{ debugName: "maxSize" }] : /* istanbul ignore next */ []);
23
+ placeholder = input("No file chosen", ...ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []);
24
+ buttonLabel = input("Browse", ...ngDevMode ? [{ debugName: "buttonLabel" }] : /* istanbul ignore next */ []);
25
+ rejected = output();
26
+ picker = viewChild.required("picker", ...ngDevMode ? [{ debugName: "picker" }] : /* istanbul ignore next */ []);
27
+ hasRejection = signal(false, ...ngDevMode ? [{ debugName: "hasRejection" }] : /* istanbul ignore next */ []);
28
+ summary = computed(() => {
29
+ const files = this.value();
30
+ return files.length > 1 ? `${files.length} files` : files[0]?.name ?? "";
31
+ }, ...ngDevMode ? [{ debugName: "summary" }] : /* istanbul ignore next */ []);
32
+ openPicker() {
33
+ if (this.disabled()) return;
34
+ const picker = this.picker().nativeElement;
35
+ if (picker.showPicker) picker.showPicker();
36
+ else picker.click();
37
+ }
38
+ handlePick(event) {
39
+ const target = event.target;
40
+ const { accepted, rejected } = selectFiles([...target.files ?? []], this.accept(), this.maxSize());
41
+ target.value = "";
42
+ this.hasRejection.set(rejected.length > 0);
43
+ if (rejected.length) this.rejected.emit(rejected);
44
+ if (accepted.length) this.value.set(accepted);
45
+ }
46
+ clear(event) {
47
+ event.stopPropagation();
48
+ this.value.set([]);
49
+ this.hasRejection.set(false);
50
+ }
51
+ static ɵfac = i0.ɵɵngDeclareFactory({
52
+ minVersion: "12.0.0",
53
+ version: "22.2.1",
54
+ ngImport: i0,
55
+ type: UiFileInputComponent,
56
+ deps: [],
57
+ target: i0.ɵɵFactoryTarget.Component
58
+ });
59
+ static ɵcmp = i0.ɵɵngDeclareComponent({
60
+ minVersion: "17.0.0",
61
+ version: "22.2.1",
62
+ type: UiFileInputComponent,
63
+ isStandalone: true,
64
+ selector: "ui-file-input",
65
+ inputs: {
66
+ value: {
67
+ classPropertyName: "value",
68
+ publicName: "value",
69
+ isSignal: true,
70
+ isRequired: false,
71
+ transformFunction: null
72
+ },
73
+ id: {
74
+ classPropertyName: "id",
75
+ publicName: "id",
76
+ isSignal: true,
77
+ isRequired: false,
78
+ transformFunction: null
79
+ },
80
+ accept: {
81
+ classPropertyName: "accept",
82
+ publicName: "accept",
83
+ isSignal: true,
84
+ isRequired: false,
85
+ transformFunction: null
86
+ },
87
+ multiple: {
88
+ classPropertyName: "multiple",
89
+ publicName: "multiple",
90
+ isSignal: true,
91
+ isRequired: false,
92
+ transformFunction: null
93
+ },
94
+ disabled: {
95
+ classPropertyName: "disabled",
96
+ publicName: "disabled",
97
+ isSignal: true,
98
+ isRequired: false,
99
+ transformFunction: null
100
+ },
101
+ describedBy: {
102
+ classPropertyName: "describedBy",
103
+ publicName: "describedBy",
104
+ isSignal: true,
105
+ isRequired: false,
106
+ transformFunction: null
107
+ },
108
+ maxSize: {
109
+ classPropertyName: "maxSize",
110
+ publicName: "maxSize",
111
+ isSignal: true,
112
+ isRequired: false,
113
+ transformFunction: null
114
+ },
115
+ placeholder: {
116
+ classPropertyName: "placeholder",
117
+ publicName: "placeholder",
118
+ isSignal: true,
119
+ isRequired: false,
120
+ transformFunction: null
121
+ },
122
+ buttonLabel: {
123
+ classPropertyName: "buttonLabel",
124
+ publicName: "buttonLabel",
125
+ isSignal: true,
126
+ isRequired: false,
127
+ transformFunction: null
128
+ }
129
+ },
130
+ outputs: {
131
+ value: "valueChange",
132
+ rejected: "rejected"
133
+ },
134
+ host: { properties: { "attr.id": "null" } },
135
+ providers: [provideIcons({
136
+ heroArrowUp,
137
+ heroXMark
138
+ })],
139
+ viewQueries: [{
140
+ propertyName: "picker",
141
+ first: true,
142
+ predicate: ["picker"],
143
+ descendants: true,
144
+ isSignal: true
145
+ }],
146
+ ngImport: i0,
147
+ template: "<ui-input-group [validationState]=\"hasRejection() ? false : null\">\n <ui-button\n appearance=\"white\"\n intent=\"secondary\"\n size=\"sm\"\n classes=\"h-full\"\n [disabled]=\"disabled()\"\n (click)=\"openPicker()\"\n >\n <ng-icon name=\"heroArrowUpTray\" class=\"shrink-0\" />\n {{ buttonLabel() }}\n </ui-button>\n\n <input\n #picker\n type=\"file\"\n class=\"sr-only\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n [accept]=\"accept()\"\n [multiple]=\"multiple()\"\n [disabled]=\"disabled()\"\n (change)=\"handlePick($event)\"\n />\n\n <ui-input\n [value]=\"summary()\"\n [id]=\"id()\"\n [disabled]=\"disabled()\"\n [placeholder]=\"placeholder()\"\n [describedBy]=\"describedBy()\"\n classes=\"truncate cursor-pointer\"\n readonly\n (click)=\"openPicker()\"\n >\n <ng-template #trailing>\n @if (value().length && !disabled()) {\n <button\n type=\"button\"\n class=\"inline-flex justify-center items-center w-8 self-stretch text-muted-foreground-1 hover:text-foreground\"\n aria-label=\"Clear files\"\n (click)=\"clear($event)\"\n >\n <ng-icon name=\"heroXMark\" class=\"shrink-0\" />\n </button>\n }\n </ng-template>\n </ui-input>\n</ui-input-group>\n",
148
+ dependencies: [
149
+ {
150
+ kind: "component",
151
+ type: UiInputComponent,
152
+ selector: "ui-input",
153
+ inputs: [
154
+ "value",
155
+ "classes",
156
+ "type",
157
+ "id",
158
+ "name",
159
+ "placeholder",
160
+ "autocomplete",
161
+ "inputmode",
162
+ "required",
163
+ "disabled",
164
+ "readonly",
165
+ "validationState",
166
+ "min",
167
+ "max",
168
+ "step",
169
+ "pattern",
170
+ "minlength",
171
+ "maxlength",
172
+ "autofocus",
173
+ "spellcheck",
174
+ "revealable",
175
+ "list",
176
+ "ariaLabel",
177
+ "describedBy",
178
+ "inputRole",
179
+ "ariaExpanded",
180
+ "ariaControls",
181
+ "ariaActiveDescendant",
182
+ "ariaAutocomplete",
183
+ "ariaHasPopup"
184
+ ],
185
+ outputs: [
186
+ "valueChange",
187
+ "validationStateChange",
188
+ "focused",
189
+ "blurred"
190
+ ]
191
+ },
192
+ {
193
+ kind: "component",
194
+ type: UiInputGroupComponent,
195
+ selector: "ui-input-group",
196
+ inputs: ["validationState"],
197
+ outputs: ["validationStateChange"]
198
+ },
199
+ {
200
+ kind: "component",
201
+ type: UiButtonComponent,
202
+ selector: "ui-button",
203
+ inputs: [
204
+ "appearance",
205
+ "intent",
206
+ "size",
207
+ "classes",
208
+ "type",
209
+ "ariaLabel",
210
+ "disabled",
211
+ "loading"
212
+ ]
213
+ },
214
+ {
215
+ kind: "component",
216
+ type: NgIcon,
217
+ selector: "ng-icon",
218
+ inputs: [
219
+ "name",
220
+ "svg",
221
+ "size",
222
+ "strokeWidth",
223
+ "color"
224
+ ]
225
+ }
226
+ ]
227
+ });
228
+ };
229
+ i0.ɵɵngDeclareClassMetadata({
230
+ minVersion: "12.0.0",
231
+ version: "22.2.1",
232
+ ngImport: i0,
233
+ type: UiFileInputComponent,
234
+ decorators: [{
235
+ type: Component,
236
+ args: [{
237
+ selector: "ui-file-input",
238
+ imports: [
239
+ UiInputComponent,
240
+ UiInputGroupComponent,
241
+ UiButtonComponent,
242
+ NgIcon
243
+ ],
244
+ providers: [provideIcons({
245
+ heroArrowUp,
246
+ heroXMark
247
+ })],
248
+ host: { "[attr.id]": "null" },
249
+ template: "<ui-input-group [validationState]=\"hasRejection() ? false : null\">\n <ui-button\n appearance=\"white\"\n intent=\"secondary\"\n size=\"sm\"\n classes=\"h-full\"\n [disabled]=\"disabled()\"\n (click)=\"openPicker()\"\n >\n <ng-icon name=\"heroArrowUpTray\" class=\"shrink-0\" />\n {{ buttonLabel() }}\n </ui-button>\n\n <input\n #picker\n type=\"file\"\n class=\"sr-only\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n [accept]=\"accept()\"\n [multiple]=\"multiple()\"\n [disabled]=\"disabled()\"\n (change)=\"handlePick($event)\"\n />\n\n <ui-input\n [value]=\"summary()\"\n [id]=\"id()\"\n [disabled]=\"disabled()\"\n [placeholder]=\"placeholder()\"\n [describedBy]=\"describedBy()\"\n classes=\"truncate cursor-pointer\"\n readonly\n (click)=\"openPicker()\"\n >\n <ng-template #trailing>\n @if (value().length && !disabled()) {\n <button\n type=\"button\"\n class=\"inline-flex justify-center items-center w-8 self-stretch text-muted-foreground-1 hover:text-foreground\"\n aria-label=\"Clear files\"\n (click)=\"clear($event)\"\n >\n <ng-icon name=\"heroXMark\" class=\"shrink-0\" />\n </button>\n }\n </ng-template>\n </ui-input>\n</ui-input-group>\n"
250
+ }]
251
+ }],
252
+ propDecorators: {
253
+ value: [{
254
+ type: i0.Input,
255
+ args: [{
256
+ isSignal: true,
257
+ alias: "value",
258
+ required: false
259
+ }]
260
+ }, {
261
+ type: i0.Output,
262
+ args: ["valueChange"]
263
+ }],
264
+ id: [{
265
+ type: i0.Input,
266
+ args: [{
267
+ isSignal: true,
268
+ alias: "id",
269
+ required: false
270
+ }]
271
+ }],
272
+ accept: [{
273
+ type: i0.Input,
274
+ args: [{
275
+ isSignal: true,
276
+ alias: "accept",
277
+ required: false
278
+ }]
279
+ }],
280
+ multiple: [{
281
+ type: i0.Input,
282
+ args: [{
283
+ isSignal: true,
284
+ alias: "multiple",
285
+ required: false
286
+ }]
287
+ }],
288
+ disabled: [{
289
+ type: i0.Input,
290
+ args: [{
291
+ isSignal: true,
292
+ alias: "disabled",
293
+ required: false
294
+ }]
295
+ }],
296
+ describedBy: [{
297
+ type: i0.Input,
298
+ args: [{
299
+ isSignal: true,
300
+ alias: "describedBy",
301
+ required: false
302
+ }]
303
+ }],
304
+ maxSize: [{
305
+ type: i0.Input,
306
+ args: [{
307
+ isSignal: true,
308
+ alias: "maxSize",
309
+ required: false
310
+ }]
311
+ }],
312
+ placeholder: [{
313
+ type: i0.Input,
314
+ args: [{
315
+ isSignal: true,
316
+ alias: "placeholder",
317
+ required: false
318
+ }]
319
+ }],
320
+ buttonLabel: [{
321
+ type: i0.Input,
322
+ args: [{
323
+ isSignal: true,
324
+ alias: "buttonLabel",
325
+ required: false
326
+ }]
327
+ }],
328
+ rejected: [{
329
+ type: i0.Output,
330
+ args: ["rejected"]
331
+ }],
332
+ picker: [{
333
+ type: i0.ViewChild,
334
+ args: ["picker", { isSignal: true }]
335
+ }]
336
+ }
337
+ });
338
+ export { UiFileInputComponent };
339
+
340
+ //# sourceMappingURL=area8975-ui-file-input.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"area8975-ui-file-input.mjs","names":[],"sources":["../../../projects/ui/file-input/file-input.component.ts","../../../projects/ui/file-input/file-input.component.html"],"sourcesContent":["import {\n booleanAttribute,\n Component,\n computed,\n ElementRef,\n input,\n model,\n output,\n signal,\n viewChild\n} from '@angular/core';\n\nimport { UiButtonComponent } from '@area8975/ui/button';\nimport type { FileRejection } from '@area8975/ui/file';\nimport { selectFiles } from '@area8975/ui/file';\nimport { UiInputComponent } from '@area8975/ui/input';\nimport { UiInputGroupComponent } from '@area8975/ui/input-group';\nimport { NgIcon, provideIcons } from '@ng-icons/core';\nimport { heroArrowUp, heroXMark } from '@ng-icons/heroicons/outline';\n\n\n@Component({\n selector: 'ui-file-input',\n templateUrl: './file-input.component.html',\n imports: [\n UiInputComponent,\n UiInputGroupComponent,\n UiButtonComponent,\n NgIcon\n ],\n providers: [provideIcons({ heroArrowUp, heroXMark })],\n host: { '[attr.id]': 'null' }\n})\nexport class UiFileInputComponent {\n readonly value = model<File[]>([]);\n\n readonly id = input<string>('');\n readonly accept = input<string>('');\n readonly multiple = input(false, { transform: booleanAttribute });\n readonly disabled = input(false, { transform: booleanAttribute });\n readonly describedBy = input<string>('');\n\n /** Max size per file in bytes. */\n readonly maxSize = input<number | null>(null);\n\n readonly placeholder = input<string>('No file chosen');\n readonly buttonLabel = input<string>('Browse');\n\n readonly rejected = output<FileRejection[]>();\n\n private readonly picker = viewChild.required<ElementRef<HTMLInputElement>>(\n 'picker'\n );\n\n protected readonly hasRejection = signal(false);\n\n protected readonly summary = computed(() => {\n const files = this.value();\n\n return files.length > 1\n ? `${files.length} files`\n : files[0]?.name ?? '';\n });\n\n protected openPicker(): void {\n if (this.disabled()) {\n return;\n }\n\n const picker = this.picker().nativeElement;\n\n if (picker.showPicker) {\n picker.showPicker();\n } else {\n picker.click();\n }\n }\n\n protected handlePick(event: Event): void {\n const target = event.target as HTMLInputElement;\n const { accepted, rejected } = selectFiles(\n [...target.files ?? []],\n this.accept(),\n this.maxSize()\n );\n\n // reset so picking the same file again still fires change\n target.value = '';\n\n this.hasRejection.set(rejected.length > 0);\n\n if (rejected.length) {\n this.rejected.emit(rejected);\n }\n\n if (accepted.length) {\n this.value.set(accepted);\n }\n }\n\n protected clear(event: Event): void {\n // stop the input underneath from opening the picker\n event.stopPropagation();\n\n this.value.set([]);\n this.hasRejection.set(false);\n }\n}\n","<ui-input-group [validationState]=\"hasRejection() ? false : null\">\n <ui-button\n appearance=\"white\"\n intent=\"secondary\"\n size=\"sm\"\n classes=\"h-full\"\n [disabled]=\"disabled()\"\n (click)=\"openPicker()\"\n >\n <ng-icon name=\"heroArrowUpTray\" class=\"shrink-0\" />\n {{ buttonLabel() }}\n </ui-button>\n\n <input\n #picker\n type=\"file\"\n class=\"sr-only\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n [accept]=\"accept()\"\n [multiple]=\"multiple()\"\n [disabled]=\"disabled()\"\n (change)=\"handlePick($event)\"\n />\n\n <ui-input\n [value]=\"summary()\"\n [id]=\"id()\"\n [disabled]=\"disabled()\"\n [placeholder]=\"placeholder()\"\n [describedBy]=\"describedBy()\"\n classes=\"truncate cursor-pointer\"\n readonly\n (click)=\"openPicker()\"\n >\n <ng-template #trailing>\n @if (value().length && !disabled()) {\n <button\n type=\"button\"\n class=\"inline-flex justify-center items-center w-8 self-stretch text-muted-foreground-1 hover:text-foreground\"\n aria-label=\"Clear files\"\n (click)=\"clear($event)\"\n >\n <ng-icon name=\"heroXMark\" class=\"shrink-0\" />\n </button>\n }\n </ng-template>\n </ui-input>\n</ui-input-group>\n"],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ const FileRejectionReasons = ["type", "size"];
2
+ const SIZE_UNITS = [
3
+ "B",
4
+ "KB",
5
+ "MB",
6
+ "GB"
7
+ ];
8
+ function matchesAccept(file, accept) {
9
+ const tokens = accept.split(",").map((token) => token.trim().toLowerCase()).filter(Boolean);
10
+ if (!tokens.length) return true;
11
+ const name = file.name.toLowerCase();
12
+ const type = file.type.toLowerCase();
13
+ return tokens.some((token) => {
14
+ if (token.startsWith(".")) return name.endsWith(token);
15
+ if (token.endsWith("/*")) return type.startsWith(token.slice(0, -1));
16
+ return type === token;
17
+ });
18
+ }
19
+ function selectFiles(files, accept, maxSize) {
20
+ const accepted = [];
21
+ const rejected = [];
22
+ for (const file of files) if (!matchesAccept(file, accept)) rejected.push({
23
+ file,
24
+ reason: "type"
25
+ });
26
+ else if (maxSize !== null && file.size > maxSize) rejected.push({
27
+ file,
28
+ reason: "size"
29
+ });
30
+ else accepted.push(file);
31
+ return {
32
+ accepted,
33
+ rejected
34
+ };
35
+ }
36
+ function formatFileSize(bytes) {
37
+ let size = bytes;
38
+ let unit = 0;
39
+ while (size >= 1024 && unit < SIZE_UNITS.length - 1) {
40
+ size /= 1024;
41
+ unit++;
42
+ }
43
+ return `${unit === 0 ? size : Math.round(size * 10) / 10} ${SIZE_UNITS[unit]}`;
44
+ }
45
+ export { FileRejectionReasons, formatFileSize, matchesAccept, selectFiles };
46
+
47
+ //# sourceMappingURL=area8975-ui-file.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"area8975-ui-file.mjs","names":[],"sources":["../../../projects/ui/file/file.types.ts","../../../projects/ui/file/file.utils.ts"],"sourcesContent":["/** Why a file was turned away by a file picker. */\nexport const FileRejectionReasons = ['type', 'size'] as const;\n\n/** @see {@link FileRejectionReasons} */\nexport type FileRejectionReason = typeof FileRejectionReasons[number];\n\nexport interface FileRejection {\n file: File;\n reason: FileRejectionReason;\n}\n\nexport interface FileSelection {\n accepted: File[];\n rejected: FileRejection[];\n}\n","import type { FileRejection, FileSelection } from './file.types';\n\n\nconst SIZE_UNITS = ['B', 'KB', 'MB', 'GB'];\n\n/**\n * Checks a file against a native `accept` string, e.g. \".csv,image/*\".\n * Dropped files skip the browser's own accept check, so we do it ourselves.\n */\nexport function matchesAccept(file: File, accept: string): boolean {\n const tokens = accept\n .split(',')\n .map(token => token.trim().toLowerCase())\n .filter(Boolean);\n\n if (!tokens.length) {\n return true;\n }\n\n const name = file.name.toLowerCase();\n const type = file.type.toLowerCase();\n\n return tokens.some(token => {\n if (token.startsWith('.')) {\n return name.endsWith(token);\n }\n\n if (token.endsWith('/*')) {\n return type.startsWith(token.slice(0, -1));\n }\n\n return type === token;\n });\n}\n\n/** Splits files into ones that pass the accept and size rules and ones that don't. */\nexport function selectFiles(\n files: File[],\n accept: string,\n maxSize: number | null\n): FileSelection {\n const accepted: File[] = [];\n const rejected: FileRejection[] = [];\n\n for (const file of files) {\n if (!matchesAccept(file, accept)) {\n rejected.push({ file, reason: 'type' });\n } else if (maxSize !== null && file.size > maxSize) {\n rejected.push({ file, reason: 'size' });\n } else {\n accepted.push(file);\n }\n }\n\n return { accepted, rejected };\n}\n\n/** Human readable size, e.g. 1536 -> \"1.5 KB\". */\nexport function formatFileSize(bytes: number): string {\n let size = bytes;\n let unit = 0;\n\n while (size >= 1024 && unit < SIZE_UNITS.length - 1) {\n size /= 1024;\n unit++;\n }\n\n const rounded = unit === 0 ? size : Math.round(size * 10) / 10;\n\n return `${rounded} ${SIZE_UNITS[unit]}`;\n}\n"],"mappings":"AACA,MAAa,uBAAuB,CAAC,QAAQ,MAAM;ACEnD,MAAM,aAAa;CAAC;CAAK;CAAM;CAAM;AAAI;AAMzC,SAAgB,cAAc,MAAY,QAAc;CACpD,MAAM,SAAS,OACV,MAAM,GAAG,CAAC,CACV,KAAI,UAAS,MAAM,KAAI,CAAE,CAAC,YAAW,CAAE,CAAC,CACxC,OAAO,OAAO;CAEnB,IAAI,CAAC,OAAO,QACR,OAAO;CAGX,MAAM,OAAO,KAAK,KAAK,YAAW;CAClC,MAAM,OAAO,KAAK,KAAK,YAAW;CAElC,OAAO,OAAO,MAAK,UAAQ;EACvB,IAAI,MAAM,WAAW,GAAG,GACpB,OAAO,KAAK,SAAS,KAAK;EAG9B,IAAI,MAAM,SAAS,IAAI,GACnB,OAAO,KAAK,WAAW,MAAM,MAAM,GAAG,EAAE,CAAC;EAG7C,OAAO,SAAS;CACpB,CAAC;AACL;AAGA,SAAgB,YACZ,OACA,QACA,SAAsB;CAEtB,MAAM,WAAmB,CAAA;CACzB,MAAM,WAA4B,CAAA;CAElC,KAAK,MAAM,QAAQ,OACf,IAAI,CAAC,cAAc,MAAM,MAAM,GAC3B,SAAS,KAAK;EAAE;EAAM,QAAQ;CAAM,CAAE;MACnC,IAAI,YAAY,QAAQ,KAAK,OAAO,SACvC,SAAS,KAAK;EAAE;EAAM,QAAQ;CAAM,CAAE;MAEtC,SAAS,KAAK,IAAI;CAI1B,OAAO;EAAE;EAAU;CAAQ;AAC/B;AAGA,SAAgB,eAAe,OAAa;CACxC,IAAI,OAAO;CACX,IAAI,OAAO;CAEX,OAAO,QAAQ,QAAQ,OAAO,WAAW,SAAS,GAAG;EACjD,QAAQ;EACR;CACJ;CAIA,OAAO,GAFS,SAAS,IAAI,OAAO,KAAK,MAAM,OAAO,EAAE,IAAI,GAE3C,GAAI,WAAW;AACpC"}
@@ -0,0 +1,70 @@
1
+ import { InjectionToken, inject, signal } from "@angular/core";
2
+ const UiPrivacies = [
3
+ "visible",
4
+ "discreet",
5
+ "hidden"
6
+ ];
7
+ const UI_PRIVACY = new InjectionToken("UI_PRIVACY", {
8
+ providedIn: "root",
9
+ factory: () => signal("visible").asReadonly()
10
+ });
11
+ function provideUiPrivacy(privacy) {
12
+ return {
13
+ provide: UI_PRIVACY,
14
+ useFactory: privacy
15
+ };
16
+ }
17
+ const PRIVATE_TEXT_MASK = "- - -";
18
+ const PRIVATE_TEXT_LABEL = "text hidden";
19
+ const PRIVACY_MASK = ` ${PRIVATE_TEXT_MASK}`;
20
+ function maskMoney(currency, locale) {
21
+ return new Intl.NumberFormat(locale, {
22
+ style: "currency",
23
+ currency
24
+ }).formatToParts(0).map((part) => part.type === "currency" || part.type === "literal" ? part.value : PRIVACY_MASK).filter((value, index, values) => value !== PRIVACY_MASK || values[index - 1] !== PRIVACY_MASK).join("");
25
+ }
26
+ function injectPrivateText() {
27
+ const privacy = inject(UI_PRIVACY);
28
+ const replace = (text, replacement) => !text ? text ?? "" : privacy() === "visible" ? text : replacement;
29
+ return Object.assign((text) => replace(text, PRIVATE_TEXT_MASK), { label: (text) => replace(text, PRIVATE_TEXT_LABEL) });
30
+ }
31
+ const UI_FORMAT_DEFAULTS = {
32
+ currency: "GBP",
33
+ locale: "en-GB"
34
+ };
35
+ const UI_FORMAT = new InjectionToken("UI_FORMAT", {
36
+ providedIn: "root",
37
+ factory: () => UI_FORMAT_DEFAULTS
38
+ });
39
+ function provideUiFormat(format) {
40
+ return {
41
+ provide: UI_FORMAT,
42
+ useValue: {
43
+ ...UI_FORMAT_DEFAULTS,
44
+ ...format
45
+ }
46
+ };
47
+ }
48
+ const MONEY = Symbol("money");
49
+ function moneyFormatterParts(formatter) {
50
+ return formatter[MONEY] ?? null;
51
+ }
52
+ function injectMoneyFormatter(options = {}) {
53
+ const { currency, locale } = inject(UI_FORMAT);
54
+ const privacy = inject(UI_PRIVACY);
55
+ const formatter = new Intl.NumberFormat(locale, {
56
+ style: "currency",
57
+ currency,
58
+ ...options
59
+ });
60
+ const resolved = formatter.resolvedOptions();
61
+ const mask = maskMoney(resolved.currency ?? currency, resolved.locale);
62
+ const format = (value) => formatter.format(value);
63
+ return Object.assign((value) => privacy() === "visible" ? format(value) : mask, { [MONEY]: {
64
+ format,
65
+ mask
66
+ } });
67
+ }
68
+ export { PRIVATE_TEXT_LABEL, PRIVATE_TEXT_MASK, UI_FORMAT, UI_FORMAT_DEFAULTS, UI_PRIVACY, UiPrivacies, injectMoneyFormatter, injectPrivateText, maskMoney, moneyFormatterParts, provideUiFormat, provideUiPrivacy };
69
+
70
+ //# sourceMappingURL=area8975-ui-format.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"area8975-ui-format.mjs","names":[],"sources":["../../../projects/ui/format/privacy.ts","../../../projects/ui/format/format.ts"],"sourcesContent":["import {\n inject,\n InjectionToken,\n type Provider,\n type Signal,\n signal\n} from '@angular/core';\n\n\nexport const UiPrivacies = [\n 'visible',\n 'discreet',\n 'hidden'\n] as const;\n\n/** Discreet hides amounts but keeps their shape, hidden hides everything about them. */\nexport type UiPrivacy = typeof UiPrivacies[number];\n\nexport const UI_PRIVACY = new InjectionToken<Signal<UiPrivacy>>('UI_PRIVACY', {\n providedIn: 'root',\n factory: () => signal<UiPrivacy>('visible').asReadonly()\n});\n\n/** The factory runs in an injection context, so it can return a signal from an app service. */\nexport function provideUiPrivacy(privacy: () => Signal<UiPrivacy>): Provider {\n return {\n provide: UI_PRIVACY,\n useFactory: privacy\n };\n}\n\n// #region Privacy\n\n/** The fixed mask for private text, e.g. a transaction's description. */\nexport const PRIVATE_TEXT_MASK = '- - -';\n\n/** What screen readers hear in place of private text. */\nexport const PRIVATE_TEXT_LABEL = 'text hidden';\n\nconst PRIVACY_MASK = ` ${PRIVATE_TEXT_MASK}`;\n\nexport function maskMoney(currency: string, locale: string): string {\n return new Intl.NumberFormat(locale, { style: 'currency', currency })\n .formatToParts(0)\n .map(part =>\n part.type === 'currency' || part.type === 'literal'\n ? part.value\n : PRIVACY_MASK\n )\n .filter((value, index, values) =>\n value !== PRIVACY_MASK || values[index - 1] !== PRIVACY_MASK\n )\n .join('');\n}\n\nexport interface PrivateTextFormatter {\n /** The text, or `PRIVATE_TEXT_MASK`. For visible attributes like `title`. */\n (text: string | null | undefined): string;\n\n /** The text, or `PRIVATE_TEXT_LABEL`. For `aria-*`, so screen readers don't read out dashes. */\n label: (text: string | null | undefined) => string;\n}\n\n/** Needs an injection context. Reads `UI_PRIVACY` on every call, so a template or computed calling it follows the mode. */\nexport function injectPrivateText(): PrivateTextFormatter {\n const privacy = inject(UI_PRIVACY);\n\n const replace = (text: string | null | undefined, replacement: string) =>\n !text ? text ?? '' : privacy() === 'visible' ? text : replacement;\n\n return Object.assign(\n (text: string | null | undefined) => replace(text, PRIVATE_TEXT_MASK),\n { label: (text: string | null | undefined) => replace(text, PRIVATE_TEXT_LABEL) }\n );\n}\n// #endregion\n","import { inject, InjectionToken, type Provider } from '@angular/core';\n\nimport { maskMoney, UI_PRIVACY } from './privacy';\n\n\nexport interface UiFormat {\n /** ISO 4217 code, e.g. \"GBP\". */\n currency: string;\n\n /** BCP 47 tag, e.g. \"en-GB\". */\n locale: string;\n}\n\nexport const UI_FORMAT_DEFAULTS: UiFormat = {\n currency: 'GBP',\n locale: 'en-GB'\n};\n\nexport const UI_FORMAT = new InjectionToken<UiFormat>('UI_FORMAT', {\n providedIn: 'root',\n factory: () => UI_FORMAT_DEFAULTS\n});\n\nexport function provideUiFormat(format: Partial<UiFormat>): Provider {\n return {\n provide: UI_FORMAT,\n useValue: { ...UI_FORMAT_DEFAULTS, ...format }\n };\n}\n\nconst MONEY = Symbol('money');\n\nexport interface MoneyFormatterParts {\n format: (value: number) => string;\n mask: string;\n}\n\n/** The unmasked formatter and mask behind a formatter from `injectMoneyFormatter`, null for any other function. */\nexport function moneyFormatterParts(formatter: (value: number) => string): MoneyFormatterParts | null {\n return (formatter as { [MONEY]?: MoneyFormatterParts })[MONEY] ?? null;\n}\n\n/** Needs an injection context. Reads `UI_PRIVACY` on every call, so a template or computed calling it follows the mode. */\nexport function injectMoneyFormatter(\n options: Intl.NumberFormatOptions = {}\n): (value: number) => string {\n const { currency, locale } = inject(UI_FORMAT);\n const privacy = inject(UI_PRIVACY);\n const formatter = new Intl.NumberFormat(locale, {\n style: 'currency',\n currency,\n ...options\n });\n const resolved = formatter.resolvedOptions();\n const mask = maskMoney(resolved.currency ?? currency, resolved.locale);\n\n const format = (value: number) => formatter.format(value);\n\n return Object.assign(\n (value: number) => privacy() === 'visible' ? format(value) : mask,\n { [MONEY]: { format, mask } }\n );\n}\n"],"mappings":";AASA,MAAa,cAAc;CACvB;CACA;CACA;AACM;AAKV,MAAa,aAAa,IAAI,eAAkC,cAAc;CAC1E,YAAY;CACZ,eAAe,OAAkB,SAAS,CAAC,CAAC,WAAU;AACzD,CAAA;AAGD,SAAgB,iBAAiB,SAAgC;CAC7D,OAAO;EACH,SAAS;EACT,YAAY;CACf;AACL;AAKA,MAAa,oBAAoB;AAGjC,MAAa,qBAAqB;AAElC,MAAM,eAAe,IAAI;AAEzB,SAAgB,UAAU,UAAkB,QAAc;CACtD,OAAO,IAAI,KAAK,aAAa,QAAQ;EAAE,OAAO;EAAY;CAAQ,CAAE,CAAC,CAChE,cAAc,CAAC,CAAC,CAChB,KAAI,SACD,KAAK,SAAS,cAAc,KAAK,SAAS,YACpC,KAAK,QACL,YAAY,CACrB,CACA,QAAQ,OAAO,OAAO,WACnB,UAAU,gBAAgB,OAAO,QAAQ,OAAO,YAAY,CAC/D,CACA,KAAK,EAAE;AAChB;AAWA,SAAgB,oBAAiB;CAC7B,MAAM,UAAU,OAAO,UAAU;CAEjC,MAAM,WAAW,MAAiC,gBAC9C,CAAC,OAAO,QAAQ,KAAK,QAAO,MAAO,YAAY,OAAO;CAE1D,OAAO,OAAO,QACT,SAAoC,QAAQ,MAAM,iBAAiB,GACpE,EAAE,QAAQ,SAAoC,QAAQ,MAAM,kBAAkB,EAAC,CAAE;AAEzF;AC7DA,MAAa,qBAA+B;CACxC,UAAU;CACV,QAAQ;AACX;AAED,MAAa,YAAY,IAAI,eAAyB,aAAa;CAC/D,YAAY;CACZ,eAAe;AAClB,CAAA;AAED,SAAgB,gBAAgB,QAAyB;CACrD,OAAO;EACH,SAAS;EACT,UAAU;GAAE,GAAG;GAAoB,GAAG;EAAM;CAC/C;AACL;AAEA,MAAM,QAAQ,OAAO,OAAO;AAQ5B,SAAgB,oBAAoB,WAAoC;CACpE,OAAQ,UAAgD,UAAU;AACtE;AAGA,SAAgB,qBACZ,UAAoC,CAAA,GAAE;CAEtC,MAAM,EAAE,UAAU,WAAW,OAAO,SAAS;CAC7C,MAAM,UAAU,OAAO,UAAU;CACjC,MAAM,YAAY,IAAI,KAAK,aAAa,QAAQ;EAC5C,OAAO;EACP;EACA,GAAG;CACN,CAAA;CACD,MAAM,WAAW,UAAU,gBAAe;CAC1C,MAAM,OAAO,UAAU,SAAS,YAAY,UAAU,SAAS,MAAM;CAErE,MAAM,UAAU,UAAkB,UAAU,OAAO,KAAK;CAExD,OAAO,OAAO,QACT,UAAkB,QAAO,MAAO,YAAY,OAAO,KAAK,IAAI,MAC7D,GAAG,QAAQ;EAAE;EAAQ;CAAI,EAAE,CAAE;AAErC"}
@@ -0,0 +1,42 @@
1
+ import * as i0 from "@angular/core";
2
+ import { Component } from "@angular/core";
3
+ var UiInputAddonComponent = class UiInputAddonComponent {
4
+ static ɵfac = i0.ɵɵngDeclareFactory({
5
+ minVersion: "12.0.0",
6
+ version: "22.2.1",
7
+ ngImport: i0,
8
+ type: UiInputAddonComponent,
9
+ deps: [],
10
+ target: i0.ɵɵFactoryTarget.Component
11
+ });
12
+ static ɵcmp = i0.ɵɵngDeclareComponent({
13
+ minVersion: "14.0.0",
14
+ version: "22.2.1",
15
+ type: UiInputAddonComponent,
16
+ isStandalone: true,
17
+ selector: "ui-input-addon",
18
+ host: { classAttribute: "ui-text-body-alt" },
19
+ ngImport: i0,
20
+ template: `<ng-content />`,
21
+ isInline: true,
22
+ styles: ["@layer properties;:host{display:inline-flex;flex-shrink:0;align-items:center;overflow:hidden;border-radius:var(--radius-md, .375rem);border-style:var(--tw-border-style);border-width:1px;border-color:var(--border-line-2);background-color:var(--muted);padding-inline:calc(var(--spacing, .25rem) * 3);padding-block:calc(var(--spacing, .25rem) * 2);color:var(--muted-foreground-1)}ui-input-group>:host{border-radius:0!important;border-style:var(--tw-border-style)!important;border-width:0px!important}ui-input-group>:host(:not(:last-child)){border-right-style:var(--tw-border-style)!important;border-right-width:1px!important;border-color:var(--border-line-2)!important}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style: solid}}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"]
23
+ });
24
+ };
25
+ i0.ɵɵngDeclareClassMetadata({
26
+ minVersion: "12.0.0",
27
+ version: "22.2.1",
28
+ ngImport: i0,
29
+ type: UiInputAddonComponent,
30
+ decorators: [{
31
+ type: Component,
32
+ args: [{
33
+ selector: "ui-input-addon",
34
+ template: `<ng-content />`,
35
+ host: { class: "ui-text-body-alt" },
36
+ styles: ["@layer properties;:host{display:inline-flex;flex-shrink:0;align-items:center;overflow:hidden;border-radius:var(--radius-md, .375rem);border-style:var(--tw-border-style);border-width:1px;border-color:var(--border-line-2);background-color:var(--muted);padding-inline:calc(var(--spacing, .25rem) * 3);padding-block:calc(var(--spacing, .25rem) * 2);color:var(--muted-foreground-1)}ui-input-group>:host{border-radius:0!important;border-style:var(--tw-border-style)!important;border-width:0px!important}ui-input-group>:host(:not(:last-child)){border-right-style:var(--tw-border-style)!important;border-right-width:1px!important;border-color:var(--border-line-2)!important}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style: solid}}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"]
37
+ }]
38
+ }]
39
+ });
40
+ export { UiInputAddonComponent };
41
+
42
+ //# sourceMappingURL=area8975-ui-input-addon.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"area8975-ui-input-addon.mjs","names":[],"sources":["../../../projects/ui/input-addon/input-addon.component.ts"],"sourcesContent":["import { Component } from '@angular/core';\n\n\n/**\n * A static and non-interactable text or symbol that can be combined with\n * a `ui-input` inside of a `ui-input-group`. Example: this could represent\n * a currency symbol or unit of measurement.\n */\n@Component({\n selector: 'ui-input-addon',\n template: `<ng-content />`,\n styleUrl: './input-addon.component.css',\n host: { class: 'ui-text-body-alt' }\n})\nexport class UiInputAddonComponent {}\n"],"mappings":";;AAcA,IAAa,wBAAb,MAAa,sBAAqB;CAArB,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAqB,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAArB,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAqB,cAAA;EAAA,UAAA;EAAA,MAAA,EAAA,gBAAA,mBAAA;EAAA,UAAA;EAAA,UAJpB;EAAgB,UAAA;EAAA,QAAA,CAAA,igCAAA;CAAA,CAAA;;AAIjB,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAqB,YAAA,CAAA;EANjC,MAAA;EACa,MAAA,CAAA;GAAA,UAAA;GAAgB,UAChB;GAAgB,MAEpB,EAAE,OAAO,mBAAkB;GAAE,QAAA,CAAA,igCAAA;EAAA,CAAA"}
@@ -0,0 +1,86 @@
1
+ import * as i0 from "@angular/core";
2
+ import { Component, contentChildren, effect, model } from "@angular/core";
3
+ import { UiInputComponent } from "@area8975/ui/input";
4
+ var UiInputGroupComponent = class UiInputGroupComponent {
5
+ inputs = contentChildren(UiInputComponent, {
6
+ ...ngDevMode ? { debugName: "inputs" } : /* istanbul ignore next */ {},
7
+ descendants: false
8
+ });
9
+ validationState = model(null, ...ngDevMode ? [{ debugName: "validationState" }] : /* istanbul ignore next */ []);
10
+ constructor() {
11
+ effect(() => {
12
+ const state = this.validationState();
13
+ for (const input of this.inputs()) input.validationState.set(state);
14
+ });
15
+ }
16
+ static ɵfac = i0.ɵɵngDeclareFactory({
17
+ minVersion: "12.0.0",
18
+ version: "22.2.1",
19
+ ngImport: i0,
20
+ type: UiInputGroupComponent,
21
+ deps: [],
22
+ target: i0.ɵɵFactoryTarget.Component
23
+ });
24
+ static ɵcmp = i0.ɵɵngDeclareComponent({
25
+ minVersion: "17.2.0",
26
+ version: "22.2.1",
27
+ type: UiInputGroupComponent,
28
+ isStandalone: true,
29
+ selector: "ui-input-group",
30
+ inputs: { validationState: {
31
+ classPropertyName: "validationState",
32
+ publicName: "validationState",
33
+ isSignal: true,
34
+ isRequired: false,
35
+ transformFunction: null
36
+ } },
37
+ outputs: { validationState: "validationStateChange" },
38
+ queries: [{
39
+ propertyName: "inputs",
40
+ predicate: UiInputComponent,
41
+ isSignal: true
42
+ }],
43
+ ngImport: i0,
44
+ template: "<ng-content />",
45
+ isInline: true,
46
+ styles: ["@layer properties;@layer components{:host{width:100%}}:host{display:inline-flex;overflow:hidden;border-radius:var(--radius-md, .375rem);border-style:var(--tw-border-style);border-width:1px;border-color:var(--border-line-2)}:host:focus-within{border-color:var(--primary-focus)}:host:focus-within{--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}:host:focus-within{--tw-ring-color: color-mix(in srgb, oklch(88.2% .059 254.128) 50%, transparent)}@supports (color: color-mix(in lab,red,red)){:host:focus-within{--tw-ring-color: color-mix(in oklab, var(--color-primary-200, var(--color-blue-200)) 50%, transparent)}}:host:has(.ui-input[data-invalid]){border-color:var(--color-danger-500, var(--color-red-500))}:host:has(.ui-input[data-invalid]):focus-within{--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}:host:has(.ui-input[data-invalid]):focus-within{--tw-ring-color: color-mix(in srgb, oklch(88.5% .062 18.334) 50%, transparent)}@supports (color: color-mix(in lab,red,red)){:host:has(.ui-input[data-invalid]):focus-within{--tw-ring-color: color-mix(in oklab, var(--color-danger-200, var(--color-red-200)) 50%, transparent)}}:host:has(.ui-input[data-valid]){border-color:var(--color-success-500, var(--color-teal-500))}:host:has(.ui-input[data-valid]):focus-within{--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}:host:has(.ui-input[data-valid]):focus-within{--tw-ring-color: color-mix(in srgb, oklch(91% .096 180.426) 50%, transparent)}@supports (color: color-mix(in lab,red,red)){:host:has(.ui-input[data-valid]):focus-within{--tw-ring-color: color-mix(in oklab, var(--color-success-200, var(--color-teal-200)) 50%, transparent)}}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-shadow-color{syntax: \"*\"; inherits: false;}@property --tw-shadow-alpha{syntax: \"<percentage>\"; inherits: false; initial-value: 100%;}@property --tw-inset-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-inset-shadow-color{syntax: \"*\"; inherits: false;}@property --tw-inset-shadow-alpha{syntax: \"<percentage>\"; inherits: false; initial-value: 100%;}@property --tw-ring-color{syntax: \"*\"; inherits: false;}@property --tw-ring-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-inset-ring-color{syntax: \"*\"; inherits: false;}@property --tw-inset-ring-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-ring-inset{syntax: \"*\"; inherits: false;}@property --tw-ring-offset-width{syntax: \"<length>\"; inherits: false; initial-value: 0px;}@property --tw-ring-offset-color{syntax: \"*\"; inherits: false; initial-value: #fff;}@property --tw-ring-offset-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style: solid;--tw-shadow: 0 0 #0000;--tw-shadow-color: initial;--tw-shadow-alpha: 100%;--tw-inset-shadow: 0 0 #0000;--tw-inset-shadow-color: initial;--tw-inset-shadow-alpha: 100%;--tw-ring-color: initial;--tw-ring-shadow: 0 0 #0000;--tw-inset-ring-color: initial;--tw-inset-ring-shadow: 0 0 #0000;--tw-ring-inset: initial;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-offset-shadow: 0 0 #0000}}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"]
47
+ });
48
+ };
49
+ i0.ɵɵngDeclareClassMetadata({
50
+ minVersion: "12.0.0",
51
+ version: "22.2.1",
52
+ ngImport: i0,
53
+ type: UiInputGroupComponent,
54
+ decorators: [{
55
+ type: Component,
56
+ args: [{
57
+ selector: "ui-input-group",
58
+ template: "<ng-content />",
59
+ styles: ["@layer properties;@layer components{:host{width:100%}}:host{display:inline-flex;overflow:hidden;border-radius:var(--radius-md, .375rem);border-style:var(--tw-border-style);border-width:1px;border-color:var(--border-line-2)}:host:focus-within{border-color:var(--primary-focus)}:host:focus-within{--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}:host:focus-within{--tw-ring-color: color-mix(in srgb, oklch(88.2% .059 254.128) 50%, transparent)}@supports (color: color-mix(in lab,red,red)){:host:focus-within{--tw-ring-color: color-mix(in oklab, var(--color-primary-200, var(--color-blue-200)) 50%, transparent)}}:host:has(.ui-input[data-invalid]){border-color:var(--color-danger-500, var(--color-red-500))}:host:has(.ui-input[data-invalid]):focus-within{--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}:host:has(.ui-input[data-invalid]):focus-within{--tw-ring-color: color-mix(in srgb, oklch(88.5% .062 18.334) 50%, transparent)}@supports (color: color-mix(in lab,red,red)){:host:has(.ui-input[data-invalid]):focus-within{--tw-ring-color: color-mix(in oklab, var(--color-danger-200, var(--color-red-200)) 50%, transparent)}}:host:has(.ui-input[data-valid]){border-color:var(--color-success-500, var(--color-teal-500))}:host:has(.ui-input[data-valid]):focus-within{--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}:host:has(.ui-input[data-valid]):focus-within{--tw-ring-color: color-mix(in srgb, oklch(91% .096 180.426) 50%, transparent)}@supports (color: color-mix(in lab,red,red)){:host:has(.ui-input[data-valid]):focus-within{--tw-ring-color: color-mix(in oklab, var(--color-success-200, var(--color-teal-200)) 50%, transparent)}}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-shadow-color{syntax: \"*\"; inherits: false;}@property --tw-shadow-alpha{syntax: \"<percentage>\"; inherits: false; initial-value: 100%;}@property --tw-inset-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-inset-shadow-color{syntax: \"*\"; inherits: false;}@property --tw-inset-shadow-alpha{syntax: \"<percentage>\"; inherits: false; initial-value: 100%;}@property --tw-ring-color{syntax: \"*\"; inherits: false;}@property --tw-ring-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-inset-ring-color{syntax: \"*\"; inherits: false;}@property --tw-inset-ring-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-ring-inset{syntax: \"*\"; inherits: false;}@property --tw-ring-offset-width{syntax: \"<length>\"; inherits: false; initial-value: 0px;}@property --tw-ring-offset-color{syntax: \"*\"; inherits: false; initial-value: #fff;}@property --tw-ring-offset-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style: solid;--tw-shadow: 0 0 #0000;--tw-shadow-color: initial;--tw-shadow-alpha: 100%;--tw-inset-shadow: 0 0 #0000;--tw-inset-shadow-color: initial;--tw-inset-shadow-alpha: 100%;--tw-ring-color: initial;--tw-ring-shadow: 0 0 #0000;--tw-inset-ring-color: initial;--tw-inset-ring-shadow: 0 0 #0000;--tw-ring-inset: initial;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-offset-shadow: 0 0 #0000}}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"]
60
+ }]
61
+ }],
62
+ ctorParameters: () => [],
63
+ propDecorators: {
64
+ inputs: [{
65
+ type: i0.ContentChildren,
66
+ args: [i0.forwardRef(() => UiInputComponent), {
67
+ descendants: false,
68
+ isSignal: true
69
+ }]
70
+ }],
71
+ validationState: [{
72
+ type: i0.Input,
73
+ args: [{
74
+ isSignal: true,
75
+ alias: "validationState",
76
+ required: false
77
+ }]
78
+ }, {
79
+ type: i0.Output,
80
+ args: ["validationStateChange"]
81
+ }]
82
+ }
83
+ });
84
+ export { UiInputGroupComponent };
85
+
86
+ //# sourceMappingURL=area8975-ui-input-group.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"area8975-ui-input-group.mjs","names":[],"sources":["../../../projects/ui/input-group/input-group.component.ts"],"sourcesContent":["import { Component, contentChildren, effect, model } from '@angular/core';\n\nimport { UiInputComponent } from '@area8975/ui/input';\nimport { InputValidation } from '@area8975/ui/input';\n\n\n@Component({\n selector: 'ui-input-group',\n template: '<ng-content />',\n styleUrl: './input-group.component.css'\n})\nexport class UiInputGroupComponent {\n readonly inputs = contentChildren(UiInputComponent, { descendants: false });\n\n /** true/false shows valid/invalid styling. null means not validated yet. */\n readonly validationState = model<InputValidation>(null);\n\n constructor() {\n effect(() => {\n const state = this.validationState();\n\n for (const input of this.inputs()) {\n input.validationState.set(state);\n }\n });\n }\n}\n"],"mappings":";;;AAWA,IAAa,wBAAb,MAAa,sBAAqB;CACrB,SAAS,gBAAgB,kBAAgB;EAAA,GAAA,YAAA,EAAA,WAAA,SAAA,+BAAA,CAAA;EAAI,aAAa;CAAK,CAAA;CAG/D,kBAAkB,MAAuB,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,kBAAA,CAAA,+BAAA,CAAA,CAAA;CAEvD,cAAA;EACI,aAAY;GACR,MAAM,QAAQ,KAAK,gBAAe;GAElC,KAAK,MAAM,SAAS,KAAK,OAAM,GAC3B,MAAM,gBAAgB,IAAI,KAAK;EAEvC,CAAC;CACL;CAdS,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAqB,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAArB,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAqB,cAAA;EAAA,UAAA;EAAA,QAAA,EAAA,iBAAA;GAAA,mBAAA;GAAA,YAAA;GAAA,UAAA;GAAA,YAAA;GAAA,mBAAA;EAAA,EAAA;EAAA,SAAA,EAAA,iBAAA,wBAAA;EAAA,SAAA,CAAA;GAAA,cAAA;GAAA,WACI;GAAgB,UAAA;EAAA,CAAA;EAAA,UAAA;EAAA,UAJxC;EAAgB,UAAA;EAAA,QAAA,CAAA,0gIAAA;CAAA,CAAA;;AAGjB,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAqB,YAAA,CAAA;EALjC,MAAA;EACa,MAAA,CAAA;GAAA,UAAA;GAAgB,UAChB;GAAgB,QAAA,CAAA,0gIAAA;EAAA,CAAA;CAIQ,CAAA;CAAA,sBAAA,CAAA;CAAA,gBAAA;EAAA,QAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA,GAAA,iBAAA,gBAAgB,GAAA;IAAI,aAAa;IAAO,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,iBAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,GAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA,uBAAA;EAAA,CAAA;CAAA;AAAA,CAAA"}