@joanroucoux/cairn-ui 0.7.2 → 0.8.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 (110) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/fesm2022/joanroucoux-cairn-ui-action-bar.mjs +54 -0
  3. package/fesm2022/joanroucoux-cairn-ui-action-bar.mjs.map +1 -0
  4. package/fesm2022/joanroucoux-cairn-ui-alert.mjs +129 -0
  5. package/fesm2022/joanroucoux-cairn-ui-alert.mjs.map +1 -0
  6. package/fesm2022/joanroucoux-cairn-ui-amount.mjs +114 -0
  7. package/fesm2022/joanroucoux-cairn-ui-amount.mjs.map +1 -0
  8. package/fesm2022/joanroucoux-cairn-ui-async.mjs +282 -0
  9. package/fesm2022/joanroucoux-cairn-ui-async.mjs.map +1 -0
  10. package/fesm2022/joanroucoux-cairn-ui-avatar.mjs +71 -0
  11. package/fesm2022/joanroucoux-cairn-ui-avatar.mjs.map +1 -0
  12. package/fesm2022/joanroucoux-cairn-ui-back-link.mjs +79 -0
  13. package/fesm2022/joanroucoux-cairn-ui-back-link.mjs.map +1 -0
  14. package/fesm2022/joanroucoux-cairn-ui-badge.mjs +48 -0
  15. package/fesm2022/joanroucoux-cairn-ui-badge.mjs.map +1 -0
  16. package/fesm2022/joanroucoux-cairn-ui-button.mjs +128 -0
  17. package/fesm2022/joanroucoux-cairn-ui-button.mjs.map +1 -0
  18. package/fesm2022/joanroucoux-cairn-ui-card.mjs +146 -0
  19. package/fesm2022/joanroucoux-cairn-ui-card.mjs.map +1 -0
  20. package/fesm2022/joanroucoux-cairn-ui-choice-chips.mjs +172 -0
  21. package/fesm2022/joanroucoux-cairn-ui-choice-chips.mjs.map +1 -0
  22. package/fesm2022/joanroucoux-cairn-ui-control.mjs +18 -0
  23. package/fesm2022/joanroucoux-cairn-ui-control.mjs.map +1 -0
  24. package/fesm2022/joanroucoux-cairn-ui-delta.mjs +103 -0
  25. package/fesm2022/joanroucoux-cairn-ui-delta.mjs.map +1 -0
  26. package/fesm2022/joanroucoux-cairn-ui-dialog.mjs +626 -0
  27. package/fesm2022/joanroucoux-cairn-ui-dialog.mjs.map +1 -0
  28. package/fesm2022/joanroucoux-cairn-ui-donut.mjs +347 -0
  29. package/fesm2022/joanroucoux-cairn-ui-donut.mjs.map +1 -0
  30. package/fesm2022/joanroucoux-cairn-ui-empty.mjs +47 -0
  31. package/fesm2022/joanroucoux-cairn-ui-empty.mjs.map +1 -0
  32. package/fesm2022/joanroucoux-cairn-ui-external-link.mjs +65 -0
  33. package/fesm2022/joanroucoux-cairn-ui-external-link.mjs.map +1 -0
  34. package/fesm2022/joanroucoux-cairn-ui-fact.mjs +99 -0
  35. package/fesm2022/joanroucoux-cairn-ui-fact.mjs.map +1 -0
  36. package/fesm2022/joanroucoux-cairn-ui-field.mjs +232 -0
  37. package/fesm2022/joanroucoux-cairn-ui-field.mjs.map +1 -0
  38. package/fesm2022/joanroucoux-cairn-ui-filter-chips.mjs +89 -0
  39. package/fesm2022/joanroucoux-cairn-ui-filter-chips.mjs.map +1 -0
  40. package/fesm2022/joanroucoux-cairn-ui-input.mjs +90 -0
  41. package/fesm2022/joanroucoux-cairn-ui-input.mjs.map +1 -0
  42. package/fesm2022/joanroucoux-cairn-ui-line-chart.mjs +844 -0
  43. package/fesm2022/joanroucoux-cairn-ui-line-chart.mjs.map +1 -0
  44. package/fesm2022/joanroucoux-cairn-ui-menu.mjs +241 -0
  45. package/fesm2022/joanroucoux-cairn-ui-menu.mjs.map +1 -0
  46. package/fesm2022/joanroucoux-cairn-ui-meter.mjs +86 -0
  47. package/fesm2022/joanroucoux-cairn-ui-meter.mjs.map +1 -0
  48. package/fesm2022/joanroucoux-cairn-ui-motion.mjs +415 -0
  49. package/fesm2022/joanroucoux-cairn-ui-motion.mjs.map +1 -0
  50. package/fesm2022/joanroucoux-cairn-ui-nav-item.mjs +45 -0
  51. package/fesm2022/joanroucoux-cairn-ui-nav-item.mjs.map +1 -0
  52. package/fesm2022/joanroucoux-cairn-ui-row.mjs +164 -0
  53. package/fesm2022/joanroucoux-cairn-ui-row.mjs.map +1 -0
  54. package/fesm2022/joanroucoux-cairn-ui-segmented.mjs +165 -0
  55. package/fesm2022/joanroucoux-cairn-ui-segmented.mjs.map +1 -0
  56. package/fesm2022/joanroucoux-cairn-ui-select.mjs +69 -0
  57. package/fesm2022/joanroucoux-cairn-ui-select.mjs.map +1 -0
  58. package/fesm2022/joanroucoux-cairn-ui-skeleton.mjs +91 -0
  59. package/fesm2022/joanroucoux-cairn-ui-skeleton.mjs.map +1 -0
  60. package/fesm2022/joanroucoux-cairn-ui-stale-link.mjs +43 -0
  61. package/fesm2022/joanroucoux-cairn-ui-stale-link.mjs.map +1 -0
  62. package/fesm2022/joanroucoux-cairn-ui-stat.mjs +96 -0
  63. package/fesm2022/joanroucoux-cairn-ui-stat.mjs.map +1 -0
  64. package/fesm2022/joanroucoux-cairn-ui-switch.mjs +37 -0
  65. package/fesm2022/joanroucoux-cairn-ui-switch.mjs.map +1 -0
  66. package/fesm2022/joanroucoux-cairn-ui-tab-bar.mjs +75 -0
  67. package/fesm2022/joanroucoux-cairn-ui-tab-bar.mjs.map +1 -0
  68. package/fesm2022/joanroucoux-cairn-ui-table.mjs +321 -0
  69. package/fesm2022/joanroucoux-cairn-ui-table.mjs.map +1 -0
  70. package/fesm2022/joanroucoux-cairn-ui-toast.mjs +153 -0
  71. package/fesm2022/joanroucoux-cairn-ui-toast.mjs.map +1 -0
  72. package/fesm2022/joanroucoux-cairn-ui.mjs +0 -5462
  73. package/fesm2022/joanroucoux-cairn-ui.mjs.map +1 -1
  74. package/package.json +141 -1
  75. package/types/joanroucoux-cairn-ui-action-bar.d.ts +22 -0
  76. package/types/joanroucoux-cairn-ui-alert.d.ts +23 -0
  77. package/types/joanroucoux-cairn-ui-amount.d.ts +45 -0
  78. package/types/joanroucoux-cairn-ui-async.d.ts +58 -0
  79. package/types/joanroucoux-cairn-ui-avatar.d.ts +35 -0
  80. package/types/joanroucoux-cairn-ui-back-link.d.ts +22 -0
  81. package/types/joanroucoux-cairn-ui-badge.d.ts +23 -0
  82. package/types/joanroucoux-cairn-ui-button.d.ts +29 -0
  83. package/types/joanroucoux-cairn-ui-card.d.ts +36 -0
  84. package/types/joanroucoux-cairn-ui-choice-chips.d.ts +46 -0
  85. package/types/joanroucoux-cairn-ui-control.d.ts +27 -0
  86. package/types/joanroucoux-cairn-ui-delta.d.ts +38 -0
  87. package/types/joanroucoux-cairn-ui-dialog.d.ts +72 -0
  88. package/types/joanroucoux-cairn-ui-donut.d.ts +59 -0
  89. package/types/joanroucoux-cairn-ui-empty.d.ts +19 -0
  90. package/types/joanroucoux-cairn-ui-external-link.d.ts +15 -0
  91. package/types/joanroucoux-cairn-ui-fact.d.ts +44 -0
  92. package/types/joanroucoux-cairn-ui-field.d.ts +84 -0
  93. package/types/joanroucoux-cairn-ui-filter-chips.d.ts +28 -0
  94. package/types/joanroucoux-cairn-ui-input.d.ts +51 -0
  95. package/types/joanroucoux-cairn-ui-line-chart.d.ts +114 -0
  96. package/types/joanroucoux-cairn-ui-menu.d.ts +66 -0
  97. package/types/joanroucoux-cairn-ui-meter.d.ts +30 -0
  98. package/types/joanroucoux-cairn-ui-motion.d.ts +124 -0
  99. package/types/joanroucoux-cairn-ui-nav-item.d.ts +21 -0
  100. package/types/joanroucoux-cairn-ui-row.d.ts +78 -0
  101. package/types/joanroucoux-cairn-ui-segmented.d.ts +36 -0
  102. package/types/joanroucoux-cairn-ui-select.d.ts +23 -0
  103. package/types/joanroucoux-cairn-ui-skeleton.d.ts +26 -0
  104. package/types/joanroucoux-cairn-ui-stale-link.d.ts +24 -0
  105. package/types/joanroucoux-cairn-ui-stat.d.ts +32 -0
  106. package/types/joanroucoux-cairn-ui-switch.d.ts +17 -0
  107. package/types/joanroucoux-cairn-ui-tab-bar.d.ts +31 -0
  108. package/types/joanroucoux-cairn-ui-table.d.ts +139 -0
  109. package/types/joanroucoux-cairn-ui-toast.d.ts +51 -0
  110. package/types/joanroucoux-cairn-ui.d.ts +1 -1440
@@ -0,0 +1,626 @@
1
+ import * as i0 from '@angular/core';
2
+ import { input, booleanAttribute, output, signal, computed, inject, ElementRef, DestroyRef, afterNextRender, afterRenderEffect, Component } from '@angular/core';
3
+
4
+ const GRACE = 50;
5
+ function afterExit(element, done) {
6
+ const style = getComputedStyle(element);
7
+ const wait = longest(style.transitionDuration) + longest(style.transitionDelay);
8
+ if (wait === 0) {
9
+ done();
10
+ return () => undefined;
11
+ }
12
+ const onEnd = (event) => {
13
+ if (event.target === element && !event.pseudoElement) {
14
+ cancel();
15
+ done();
16
+ }
17
+ };
18
+ const timer = setTimeout(() => {
19
+ cancel();
20
+ done();
21
+ }, wait + GRACE);
22
+ const cancel = () => {
23
+ clearTimeout(timer);
24
+ element.removeEventListener('transitionend', onEnd);
25
+ };
26
+ element.addEventListener('transitionend', onEnd);
27
+ return cancel;
28
+ }
29
+ function longest(list) {
30
+ return Math.max(0, ...list.split(',').map(toMs));
31
+ }
32
+ function toMs(value) {
33
+ const amount = Number.parseFloat(value);
34
+ if (Number.isNaN(amount)) {
35
+ return 0;
36
+ }
37
+ return value.trim().endsWith('ms') ? amount : amount * 1000;
38
+ }
39
+
40
+ const DIALOG_STYLES = `
41
+ dialog {
42
+ --dialog-ease: var(--ease-sheet);
43
+ transition:
44
+ transform var(--duration-base) var(--dialog-ease),
45
+ overlay var(--duration-base) allow-discrete,
46
+ display var(--duration-base) allow-discrete;
47
+ transform: translateY(var(--drag-y, 0px));
48
+ }
49
+
50
+ dialog:not([open]) {
51
+ transform: translateY(100%);
52
+ transition-duration: var(--duration-exit);
53
+ }
54
+
55
+ @starting-style {
56
+ dialog[open] {
57
+ transform: translateY(100%);
58
+ }
59
+ }
60
+
61
+ dialog[data-dragging] {
62
+ transition: none;
63
+ }
64
+
65
+ @media (prefers-reduced-motion: reduce) {
66
+ dialog {
67
+ transition:
68
+ opacity var(--duration-base) var(--dialog-ease),
69
+ overlay var(--duration-base) allow-discrete,
70
+ display var(--duration-base) allow-discrete;
71
+ opacity: 1;
72
+ }
73
+
74
+ dialog:not([open]) {
75
+ opacity: 0;
76
+ transform: translateY(var(--drag-y, 0px));
77
+ }
78
+
79
+ @starting-style {
80
+ dialog[open] {
81
+ opacity: 0;
82
+ transform: translateY(0);
83
+ }
84
+ }
85
+ }
86
+
87
+ @media (min-width: 64rem) {
88
+ dialog {
89
+ --dialog-ease: var(--ease-out);
90
+ transition:
91
+ opacity var(--duration-base) var(--dialog-ease),
92
+ transform var(--duration-base) var(--dialog-ease),
93
+ overlay var(--duration-base) allow-discrete,
94
+ display var(--duration-base) allow-discrete;
95
+ opacity: 1;
96
+ transform: scale(1);
97
+ }
98
+
99
+ dialog:not([open]) {
100
+ opacity: 0;
101
+ transform: scale(var(--enter-scale));
102
+ }
103
+
104
+ @starting-style {
105
+ dialog[open] {
106
+ opacity: 0;
107
+ transform: scale(var(--enter-scale));
108
+ }
109
+ }
110
+ }
111
+
112
+ dialog::backdrop {
113
+ transition: opacity var(--duration-base) var(--ease-out);
114
+ opacity: 1;
115
+ }
116
+
117
+ dialog:not([open])::backdrop {
118
+ opacity: 0;
119
+ transition-duration: var(--duration-exit);
120
+ }
121
+
122
+ @starting-style {
123
+ dialog[open]::backdrop {
124
+ opacity: 0;
125
+ }
126
+ }
127
+ `;
128
+
129
+ const DISMISS_RATIO = 0.3;
130
+ const DISMISS_SPEED = 0.5;
131
+ const SPEED_WINDOW = 100;
132
+ const SLOP = 6;
133
+ const CONTROLS = 'button, a, input, select, textarea, [role="button"]';
134
+ class SheetDrag {
135
+ #panel;
136
+ #grips;
137
+ #dismiss;
138
+ #enabled;
139
+ #gesture = null;
140
+ constructor(panel, grips, dismiss, enabled) {
141
+ this.#panel = panel;
142
+ this.#grips = grips;
143
+ this.#dismiss = dismiss;
144
+ this.#enabled = enabled;
145
+ for (const grip of grips) {
146
+ grip.addEventListener('pointerdown', this.#onDown);
147
+ grip.addEventListener('pointermove', this.#onMove);
148
+ grip.addEventListener('pointerup', this.#onUp);
149
+ grip.addEventListener('pointercancel', this.#onCancel);
150
+ }
151
+ }
152
+ reset() {
153
+ this.#gesture = null;
154
+ this.#panel.removeAttribute('data-dragging');
155
+ this.#panel.style.removeProperty('--drag-y');
156
+ }
157
+ destroy() {
158
+ for (const grip of this.#grips) {
159
+ grip.removeEventListener('pointerdown', this.#onDown);
160
+ grip.removeEventListener('pointermove', this.#onMove);
161
+ grip.removeEventListener('pointerup', this.#onUp);
162
+ grip.removeEventListener('pointercancel', this.#onCancel);
163
+ }
164
+ }
165
+ #onDown = (event) => {
166
+ const grip = event.currentTarget;
167
+ if (event.button !== 0 || !this.#enabled() || !isSheet() || event.target.closest(CONTROLS)) {
168
+ return;
169
+ }
170
+ grip.setPointerCapture(event.pointerId);
171
+ this.#gesture = {
172
+ id: event.pointerId,
173
+ startX: event.clientX,
174
+ startY: event.clientY,
175
+ dragging: false,
176
+ height: 0,
177
+ offset: 0,
178
+ samples: [{ y: event.clientY, at: event.timeStamp }],
179
+ };
180
+ };
181
+ #onMove = (event) => {
182
+ const gesture = this.#current(event);
183
+ if (!gesture) {
184
+ return;
185
+ }
186
+ const dx = event.clientX - gesture.startX;
187
+ const dy = event.clientY - gesture.startY;
188
+ if (!gesture.dragging) {
189
+ if (Math.max(Math.abs(dx), Math.abs(dy)) < SLOP) {
190
+ return;
191
+ }
192
+ if (Math.abs(dx) > Math.abs(dy)) {
193
+ this.#gesture = null;
194
+ return;
195
+ }
196
+ gesture.dragging = true;
197
+ gesture.height = this.#panel.getBoundingClientRect().height;
198
+ this.#panel.setAttribute('data-dragging', '');
199
+ }
200
+ gesture.offset = Math.max(0, dy);
201
+ gesture.samples.push({ y: event.clientY, at: event.timeStamp });
202
+ this.#panel.style.setProperty('--drag-y', `${gesture.offset}px`);
203
+ };
204
+ #onUp = (event) => {
205
+ const gesture = this.#current(event);
206
+ if (!gesture) {
207
+ return;
208
+ }
209
+ this.#gesture = null;
210
+ if (!gesture.dragging) {
211
+ return;
212
+ }
213
+ this.#panel.removeAttribute('data-dragging');
214
+ const release = { y: event.clientY, at: event.timeStamp };
215
+ if (gesture.offset > gesture.height * DISMISS_RATIO || releaseSpeed(gesture.samples, release) > DISMISS_SPEED) {
216
+ this.#dismiss();
217
+ }
218
+ else {
219
+ this.reset();
220
+ }
221
+ };
222
+ #onCancel = (event) => {
223
+ if (this.#current(event)) {
224
+ this.#gesture = null;
225
+ this.#panel.removeAttribute('data-dragging');
226
+ this.reset();
227
+ }
228
+ };
229
+ #current(event) {
230
+ return this.#gesture?.id === event.pointerId ? this.#gesture : null;
231
+ }
232
+ }
233
+ function isSheet() {
234
+ return typeof matchMedia === 'function' && !matchMedia('(min-width: 64rem)').matches;
235
+ }
236
+ function releaseSpeed(samples, last) {
237
+ const first = samples.find((sample) => sample.at >= last.at - SPEED_WINDOW) ?? last;
238
+ const elapsed = last.at - first.at;
239
+ return elapsed > 0 ? (last.y - first.y) / elapsed : 0;
240
+ }
241
+
242
+ /** Named dialog widths; any other CSS length is accepted as is. `DialogWidth` is derived from this tuple. */
243
+ const DIALOG_WIDTHS = ['md', 'lg'];
244
+ const WIDTH_VALUES = {
245
+ md: '28rem',
246
+ lg: '560px',
247
+ };
248
+ /** Spacing presets. `DialogLayout` is derived from this tuple. */
249
+ const DIALOG_LAYOUTS = ['trade', 'form', 'list', 'confirm'];
250
+ const LAYOUTS = {
251
+ trade: {
252
+ dialog: '',
253
+ header: 'items-start lg:pt-6',
254
+ title: 'pt-1 lg:pt-0',
255
+ cross: 'lg:-mt-1.5',
256
+ icon: 'lg:size-5',
257
+ handle: 'pt-[7.5px]',
258
+ body: 'pt-3 pb-4 lg:pt-5 lg:pb-5',
259
+ footer: 'max-lg:pb-[calc(8px+env(safe-area-inset-bottom))] lg:pb-6',
260
+ },
261
+ form: {
262
+ dialog: '',
263
+ header: 'items-center lg:pt-6',
264
+ title: '',
265
+ cross: '',
266
+ icon: 'lg:size-[22px]',
267
+ handle: 'pt-[7.5px]',
268
+ body: 'pt-2 pb-5 lg:pt-4 lg:pb-5',
269
+ footer: 'max-lg:pb-[calc(12px+env(safe-area-inset-bottom))] lg:pb-5',
270
+ },
271
+ list: {
272
+ dialog: 'max-lg:h-[calc(100dvh-58px)] lg:mt-24 lg:mb-auto lg:max-h-[min(760px,calc(100dvh-96px-32px))]',
273
+ header: 'items-center lg:pt-5',
274
+ title: '',
275
+ cross: '',
276
+ icon: 'lg:size-[22px]',
277
+ handle: 'pt-[7.5px]',
278
+ body: 'pt-2 pb-4 lg:pt-4 lg:pb-5',
279
+ footer: 'pt-3 shadow-[0_-1px_0_var(--hairline)] max-lg:pb-[calc(8px+env(safe-area-inset-bottom))] lg:pt-4 lg:pb-4',
280
+ },
281
+ confirm: {
282
+ dialog: '',
283
+ header: 'items-center pt-3 lg:pt-6',
284
+ title: '',
285
+ cross: '',
286
+ icon: 'lg:size-[22px]',
287
+ handle: 'pt-[11.5px]',
288
+ body: 'pt-3 pb-5 lg:pt-3 lg:pb-6',
289
+ footer: 'max-lg:pb-[calc(8px+env(safe-area-inset-bottom))] lg:pb-6',
290
+ },
291
+ };
292
+ /** What started a close, as `closed` reports it. `DialogCloseReason` is derived from this tuple. */
293
+ const DIALOG_CLOSE_REASONS = ['escape', 'backdrop', 'cross', 'drag', 'programmatic'];
294
+ const nextId = (() => {
295
+ let count = 0;
296
+ return () => `ui-dialog-${++count}`;
297
+ })();
298
+ /**
299
+ * Modal dialog on the native <dialog>: a bottom sheet under `64rem`, centered above it.
300
+ *
301
+ * `busy` keeps it open while its action runs: Escape, a click on the backdrop and a drag of the sheet do
302
+ * nothing and the cross is disabled. The owner disables its own buttons and still closes it through `open`.
303
+ *
304
+ * @example
305
+ * <ui-dialog heading="Buy" description="Ferrari" closeLabel="Close" width="528px" [open]="buying()" (dismissed)="buying.set(false)">
306
+ * <p>Body</p>
307
+ * <button dialogActions ui-button variant="outline" class="max-lg:hidden" (click)="buying.set(false)">Cancel</button>
308
+ * <button dialogActions ui-button>Buy 40 shares</button>
309
+ * </ui-dialog>
310
+ */
311
+ class UiDialog {
312
+ heading = input.required(/* @ts-ignore */
313
+ ...(ngDevMode ? [{ debugName: "heading" }] : /* istanbul ignore next */ []));
314
+ description = input(/* @ts-ignore */
315
+ ...(ngDevMode ? [undefined, { debugName: "description" }] : /* istanbul ignore next */ []));
316
+ closeLabel = input(/* @ts-ignore */
317
+ ...(ngDevMode ? [undefined, { debugName: "closeLabel" }] : /* istanbul ignore next */ []));
318
+ layout = input('trade', /* @ts-ignore */
319
+ ...(ngDevMode ? [{ debugName: "layout" }] : /* istanbul ignore next */ []));
320
+ truncateDescription = input(false, { ...(ngDevMode ? { debugName: "truncateDescription" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
321
+ width = input('lg', /* @ts-ignore */
322
+ ...(ngDevMode ? [{ debugName: "width" }] : /* istanbul ignore next */ []));
323
+ open = input(false, { ...(ngDevMode ? { debugName: "open" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
324
+ busy = input(false, { ...(ngDevMode ? { debugName: "busy" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
325
+ dismissed = output();
326
+ closed = output();
327
+ headingId = nextId();
328
+ descriptionId = `${this.headingId}-description`;
329
+ bodyId = `${this.headingId}-body`;
330
+ scrollable = signal(false, /* @ts-ignore */
331
+ ...(ngDevMode ? [{ debugName: "scrollable" }] : /* istanbul ignore next */ []));
332
+ describedBy = computed(() => {
333
+ if (this.description()) {
334
+ return this.descriptionId;
335
+ }
336
+ return this.layout() === 'confirm' ? this.bodyId : null;
337
+ }, /* @ts-ignore */
338
+ ...(ngDevMode ? [{ debugName: "describedBy" }] : /* istanbul ignore next */ []));
339
+ widthValue = computed(() => {
340
+ const width = this.width();
341
+ return Object.hasOwn(WIDTH_VALUES, width) ? WIDTH_VALUES[width] : width;
342
+ }, /* @ts-ignore */
343
+ ...(ngDevMode ? [{ debugName: "widthValue" }] : /* istanbul ignore next */ []));
344
+ spec = computed(() => LAYOUTS[this.layout()], /* @ts-ignore */
345
+ ...(ngDevMode ? [{ debugName: "spec" }] : /* istanbul ignore next */ []));
346
+ headerClasses = computed(() => `flex gap-2 pl-4 max-lg:touch-none lg:gap-3 lg:pl-6 ${this.spec().header} ${this.closeLabel() ? 'pr-2 lg:pr-4' : 'pr-4 lg:pr-6'}`, /* @ts-ignore */
347
+ ...(ngDevMode ? [{ debugName: "headerClasses" }] : /* istanbul ignore next */ []));
348
+ titleClasses = computed(() => this.spec().title, /* @ts-ignore */
349
+ ...(ngDevMode ? [{ debugName: "titleClasses" }] : /* istanbul ignore next */ []));
350
+ crossClasses = computed(() => `rounded-pill lg:rounded-control grid size-11 flex-none cursor-pointer place-items-center text-(--muted-foreground) transition-[scale,background-color,color] [transition-duration:var(--duration-press),var(--duration-fast),var(--duration-fast)] ease-out outline-none focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-(--ring) active:scale-(--press-scale) active:bg-(--soft) disabled:pointer-events-none disabled:opacity-40 lg:size-9 lg:hover:bg-(--glow) lg:hover:text-(--foreground) ${this.spec().cross}`, /* @ts-ignore */
351
+ ...(ngDevMode ? [{ debugName: "crossClasses" }] : /* istanbul ignore next */ []));
352
+ iconClasses = computed(() => this.spec().icon, /* @ts-ignore */
353
+ ...(ngDevMode ? [{ debugName: "iconClasses" }] : /* istanbul ignore next */ []));
354
+ footerClasses = computed(() => `max-lg:[&>[ui-button]]:text-body flex justify-end gap-2 px-4 empty:hidden max-lg:flex-col-reverse max-lg:[&>[ui-button]]:h-[50px] max-lg:[&>[ui-button]]:w-full lg:px-6 ${this.spec().footer}`, /* @ts-ignore */
355
+ ...(ngDevMode ? [{ debugName: "footerClasses" }] : /* istanbul ignore next */ []));
356
+ classes = computed(() => `open:flex w-full flex-col overflow-hidden m-auto mt-auto max-h-[calc(100dvh-2rem)] max-lg:mb-0 max-lg:max-w-none max-lg:rounded-b-none lg:max-w-(--dialog-width) rounded-container bg-(--card) text-(--foreground) shadow-[0_0_0_1px_var(--border),0_24px_64px_rgb(0_0_0/0.24)] max-lg:shadow-[0_-1px_0_var(--border),0_-12px_32px_rgb(0_0_0/0.16)] backdrop:bg-black/[0.36] ${this.spec().dialog}`, /* @ts-ignore */
357
+ ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
358
+ #host = inject(ElementRef);
359
+ constructor() {
360
+ const destroyRef = inject(DestroyRef);
361
+ afterNextRender(() => {
362
+ const host = this.#host.nativeElement;
363
+ const grips = ['[data-dialog-handle]', '[data-dialog-header]'].map((grip) => host.querySelector(grip));
364
+ this.#drag = new SheetDrag(this.#dialog, grips, () => this.close('drag'), () => !this.busy());
365
+ destroyRef.onDestroy(() => {
366
+ this.#drag?.destroy();
367
+ this.#cancelExit();
368
+ });
369
+ if (typeof ResizeObserver === 'undefined') {
370
+ return;
371
+ }
372
+ const body = this.#host.nativeElement.querySelector('[data-dialog-body]');
373
+ const observer = new ResizeObserver(() => this.scrollable.set(body.scrollHeight > body.clientHeight));
374
+ observer.observe(body);
375
+ observer.observe(body.firstElementChild);
376
+ destroyRef.onDestroy(() => observer.disconnect());
377
+ });
378
+ afterRenderEffect(() => {
379
+ // viewChild.required can't target a #private field (NG1053); safe because the template has exactly one <dialog>.
380
+ const dialog = this.#host.nativeElement.querySelector('dialog');
381
+ if (this.open() && !dialog.open) {
382
+ this.#cancelExit();
383
+ this.#drag?.reset();
384
+ dialog.showModal();
385
+ }
386
+ else if (!this.open() && dialog.open) {
387
+ this.close('programmatic');
388
+ }
389
+ });
390
+ }
391
+ #pressedOnBackdrop = false;
392
+ #drag = null;
393
+ #reason = null;
394
+ #cancelExit = () => undefined;
395
+ close(reason) {
396
+ if (this.#dialog.open) {
397
+ this.#reason = reason;
398
+ this.#dialog.close();
399
+ }
400
+ }
401
+ onCancel(event) {
402
+ if (event.target !== event.currentTarget) {
403
+ return;
404
+ }
405
+ event.preventDefault();
406
+ if (!this.busy()) {
407
+ this.close('escape');
408
+ }
409
+ }
410
+ onKeydown(event) {
411
+ if (event.key === 'Escape' && this.busy()) {
412
+ event.preventDefault();
413
+ }
414
+ }
415
+ onPointerDown(event) {
416
+ this.#pressedOnBackdrop = event.target === this.#dialog && this.#outside(event);
417
+ }
418
+ onClick(event) {
419
+ if (this.#pressedOnBackdrop && !this.busy() && event.target === this.#dialog && this.#outside(event)) {
420
+ this.close('backdrop');
421
+ }
422
+ this.#pressedOnBackdrop = false;
423
+ }
424
+ get #dialog() {
425
+ return this.#host.nativeElement.querySelector('dialog');
426
+ }
427
+ #outside(event) {
428
+ const rect = this.#dialog.getBoundingClientRect();
429
+ return (event.clientX < rect.left || event.clientX > rect.right || event.clientY < rect.top || event.clientY > rect.bottom);
430
+ }
431
+ onNativeClose() {
432
+ const asked = this.#reason;
433
+ this.#reason = null;
434
+ if (this.#dialog.open) {
435
+ return;
436
+ }
437
+ if (asked === null && this.busy()) {
438
+ this.#dialog.showModal();
439
+ return;
440
+ }
441
+ const reason = asked ?? 'escape';
442
+ if (reason !== 'programmatic') {
443
+ this.dismissed.emit();
444
+ }
445
+ this.#awaitExit(reason);
446
+ }
447
+ #awaitExit(reason) {
448
+ this.#cancelExit();
449
+ this.#cancelExit = afterExit(this.#dialog, () => {
450
+ this.#cancelExit = () => undefined;
451
+ this.#drag?.reset();
452
+ this.closed.emit(reason);
453
+ });
454
+ }
455
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiDialog, deps: [], target: i0.ɵɵFactoryTarget.Component });
456
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: UiDialog, isStandalone: true, selector: "ui-dialog", inputs: { heading: { classPropertyName: "heading", publicName: "heading", isSignal: true, isRequired: true, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, closeLabel: { classPropertyName: "closeLabel", publicName: "closeLabel", isSignal: true, isRequired: false, transformFunction: null }, layout: { classPropertyName: "layout", publicName: "layout", isSignal: true, isRequired: false, transformFunction: null }, truncateDescription: { classPropertyName: "truncateDescription", publicName: "truncateDescription", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, busy: { classPropertyName: "busy", publicName: "busy", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { dismissed: "dismissed", closed: "closed" }, host: { listeners: { "pointerdown": "onPointerDown($event)", "click": "onClick($event)", "keydown": "onKeydown($event)" } }, ngImport: i0, template: `
457
+ <dialog
458
+ #dlg
459
+ [attr.aria-busy]="busy() || null"
460
+ [attr.aria-describedby]="describedBy()"
461
+ [attr.aria-labelledby]="headingId"
462
+ [attr.role]="layout() === 'confirm' ? 'alertdialog' : 'dialog'"
463
+ [class]="classes()"
464
+ [style.--dialog-width]="widthValue()"
465
+ (cancel)="onCancel($event)"
466
+ (close)="onNativeClose()"
467
+ >
468
+ <div
469
+ aria-hidden="true"
470
+ class="flex h-5 touch-none justify-center lg:hidden"
471
+ data-dialog-handle
472
+ [class]="spec().handle"
473
+ >
474
+ <span class="rounded-pill h-[5px] w-9 bg-(--border)"></span>
475
+ </div>
476
+
477
+ <div data-dialog-header [class]="headerClasses()">
478
+ <div class="flex min-w-0 flex-1 flex-col" [class]="titleClasses()">
479
+ <h2 class="text-title font-semibold" [id]="headingId">{{ heading() }}</h2>
480
+
481
+ @if (description()) {
482
+ <p
483
+ class="text-label text-(--muted-foreground)"
484
+ [class.truncate]="truncateDescription()"
485
+ [id]="descriptionId"
486
+ >
487
+ {{ description() }}
488
+ </p>
489
+ }
490
+ </div>
491
+
492
+ @if (closeLabel()) {
493
+ <button
494
+ type="button"
495
+ [attr.aria-label]="closeLabel()"
496
+ [class]="crossClasses()"
497
+ [disabled]="busy()"
498
+ (click)="close('cross')"
499
+ >
500
+ <svg
501
+ aria-hidden="true"
502
+ class="block size-[22px] fill-none stroke-current stroke-[1.75]"
503
+ stroke-linecap="round"
504
+ stroke-linejoin="round"
505
+ viewBox="0 0 24 24"
506
+ [class]="iconClasses()"
507
+ >
508
+ <path d="M18 6 6 18" />
509
+ <path d="m6 6 12 12" />
510
+ </svg>
511
+ </button>
512
+ }
513
+ </div>
514
+
515
+ <div
516
+ class="min-h-0 flex-1 overflow-y-auto px-4 outline-none focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-(--ring) lg:px-6"
517
+ data-dialog-body
518
+ [attr.aria-labelledby]="scrollable() ? headingId : null"
519
+ [attr.role]="scrollable() ? 'region' : null"
520
+ [attr.tabindex]="scrollable() ? 0 : null"
521
+ [id]="bodyId"
522
+ >
523
+ <!-- empty:hidden keeps a body-less dialog from carrying the body's vertical padding. -->
524
+ <div class="flex flex-col gap-4 empty:hidden" [class]="spec().body">
525
+ <ng-content />
526
+ </div>
527
+ </div>
528
+
529
+ <div data-dialog-footer [class]="footerClasses()">
530
+ <ng-content select="[dialogActions]" />
531
+ </div>
532
+ </dialog>
533
+ `, isInline: true, styles: ["dialog{--dialog-ease: var(--ease-sheet);transition:transform var(--duration-base) var(--dialog-ease),overlay var(--duration-base) allow-discrete,display var(--duration-base) allow-discrete;transform:translateY(var(--drag-y, 0px))}dialog:not([open]){transform:translateY(100%);transition-duration:var(--duration-exit)}@starting-style{dialog[open]{transform:translateY(100%)}}dialog[data-dragging]{transition:none}@media(prefers-reduced-motion:reduce){dialog{transition:opacity var(--duration-base) var(--dialog-ease),overlay var(--duration-base) allow-discrete,display var(--duration-base) allow-discrete;opacity:1}dialog:not([open]){opacity:0;transform:translateY(var(--drag-y, 0px))}@starting-style{dialog[open]{opacity:0;transform:translateY(0)}}}@media(min-width:64rem){dialog{--dialog-ease: var(--ease-out);transition:opacity var(--duration-base) var(--dialog-ease),transform var(--duration-base) var(--dialog-ease),overlay var(--duration-base) allow-discrete,display var(--duration-base) allow-discrete;opacity:1;transform:scale(1)}dialog:not([open]){opacity:0;transform:scale(var(--enter-scale))}@starting-style{dialog[open]{opacity:0;transform:scale(var(--enter-scale))}}}dialog::backdrop{transition:opacity var(--duration-base) var(--ease-out);opacity:1}dialog:not([open])::backdrop{opacity:0;transition-duration:var(--duration-exit)}@starting-style{dialog[open]::backdrop{opacity:0}}\n"] });
534
+ }
535
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiDialog, decorators: [{
536
+ type: Component,
537
+ args: [{ selector: 'ui-dialog', host: {
538
+ '(pointerdown)': 'onPointerDown($event)',
539
+ '(click)': 'onClick($event)',
540
+ '(keydown)': 'onKeydown($event)',
541
+ }, template: `
542
+ <dialog
543
+ #dlg
544
+ [attr.aria-busy]="busy() || null"
545
+ [attr.aria-describedby]="describedBy()"
546
+ [attr.aria-labelledby]="headingId"
547
+ [attr.role]="layout() === 'confirm' ? 'alertdialog' : 'dialog'"
548
+ [class]="classes()"
549
+ [style.--dialog-width]="widthValue()"
550
+ (cancel)="onCancel($event)"
551
+ (close)="onNativeClose()"
552
+ >
553
+ <div
554
+ aria-hidden="true"
555
+ class="flex h-5 touch-none justify-center lg:hidden"
556
+ data-dialog-handle
557
+ [class]="spec().handle"
558
+ >
559
+ <span class="rounded-pill h-[5px] w-9 bg-(--border)"></span>
560
+ </div>
561
+
562
+ <div data-dialog-header [class]="headerClasses()">
563
+ <div class="flex min-w-0 flex-1 flex-col" [class]="titleClasses()">
564
+ <h2 class="text-title font-semibold" [id]="headingId">{{ heading() }}</h2>
565
+
566
+ @if (description()) {
567
+ <p
568
+ class="text-label text-(--muted-foreground)"
569
+ [class.truncate]="truncateDescription()"
570
+ [id]="descriptionId"
571
+ >
572
+ {{ description() }}
573
+ </p>
574
+ }
575
+ </div>
576
+
577
+ @if (closeLabel()) {
578
+ <button
579
+ type="button"
580
+ [attr.aria-label]="closeLabel()"
581
+ [class]="crossClasses()"
582
+ [disabled]="busy()"
583
+ (click)="close('cross')"
584
+ >
585
+ <svg
586
+ aria-hidden="true"
587
+ class="block size-[22px] fill-none stroke-current stroke-[1.75]"
588
+ stroke-linecap="round"
589
+ stroke-linejoin="round"
590
+ viewBox="0 0 24 24"
591
+ [class]="iconClasses()"
592
+ >
593
+ <path d="M18 6 6 18" />
594
+ <path d="m6 6 12 12" />
595
+ </svg>
596
+ </button>
597
+ }
598
+ </div>
599
+
600
+ <div
601
+ class="min-h-0 flex-1 overflow-y-auto px-4 outline-none focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-(--ring) lg:px-6"
602
+ data-dialog-body
603
+ [attr.aria-labelledby]="scrollable() ? headingId : null"
604
+ [attr.role]="scrollable() ? 'region' : null"
605
+ [attr.tabindex]="scrollable() ? 0 : null"
606
+ [id]="bodyId"
607
+ >
608
+ <!-- empty:hidden keeps a body-less dialog from carrying the body's vertical padding. -->
609
+ <div class="flex flex-col gap-4 empty:hidden" [class]="spec().body">
610
+ <ng-content />
611
+ </div>
612
+ </div>
613
+
614
+ <div data-dialog-footer [class]="footerClasses()">
615
+ <ng-content select="[dialogActions]" />
616
+ </div>
617
+ </dialog>
618
+ `, styles: ["dialog{--dialog-ease: var(--ease-sheet);transition:transform var(--duration-base) var(--dialog-ease),overlay var(--duration-base) allow-discrete,display var(--duration-base) allow-discrete;transform:translateY(var(--drag-y, 0px))}dialog:not([open]){transform:translateY(100%);transition-duration:var(--duration-exit)}@starting-style{dialog[open]{transform:translateY(100%)}}dialog[data-dragging]{transition:none}@media(prefers-reduced-motion:reduce){dialog{transition:opacity var(--duration-base) var(--dialog-ease),overlay var(--duration-base) allow-discrete,display var(--duration-base) allow-discrete;opacity:1}dialog:not([open]){opacity:0;transform:translateY(var(--drag-y, 0px))}@starting-style{dialog[open]{opacity:0;transform:translateY(0)}}}@media(min-width:64rem){dialog{--dialog-ease: var(--ease-out);transition:opacity var(--duration-base) var(--dialog-ease),transform var(--duration-base) var(--dialog-ease),overlay var(--duration-base) allow-discrete,display var(--duration-base) allow-discrete;opacity:1;transform:scale(1)}dialog:not([open]){opacity:0;transform:scale(var(--enter-scale))}@starting-style{dialog[open]{opacity:0;transform:scale(var(--enter-scale))}}}dialog::backdrop{transition:opacity var(--duration-base) var(--ease-out);opacity:1}dialog:not([open])::backdrop{opacity:0;transition-duration:var(--duration-exit)}@starting-style{dialog[open]::backdrop{opacity:0}}\n"] }]
619
+ }], ctorParameters: () => [], propDecorators: { heading: [{ type: i0.Input, args: [{ isSignal: true, alias: "heading", required: true }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], closeLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeLabel", required: false }] }], layout: [{ type: i0.Input, args: [{ isSignal: true, alias: "layout", required: false }] }], truncateDescription: [{ type: i0.Input, args: [{ isSignal: true, alias: "truncateDescription", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }], busy: [{ type: i0.Input, args: [{ isSignal: true, alias: "busy", required: false }] }], dismissed: [{ type: i0.Output, args: ["dismissed"] }], closed: [{ type: i0.Output, args: ["closed"] }] } });
620
+
621
+ /**
622
+ * Generated bundle index. Do not edit.
623
+ */
624
+
625
+ export { DIALOG_CLOSE_REASONS, DIALOG_LAYOUTS, DIALOG_WIDTHS, UiDialog };
626
+ //# sourceMappingURL=joanroucoux-cairn-ui-dialog.mjs.map