ngwr 15.4.0 → 15.5.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 (131) hide show
  1. package/comment/styles/_index.scss +270 -0
  2. package/dialog/styles/_index.scss +6 -0
  3. package/fesm2022/ngwr-comment-testing.mjs +60 -0
  4. package/fesm2022/ngwr-comment-testing.mjs.map +1 -0
  5. package/fesm2022/ngwr-comment.mjs +516 -0
  6. package/fesm2022/ngwr-comment.mjs.map +1 -0
  7. package/fesm2022/ngwr-dialog-testing.mjs +14 -0
  8. package/fesm2022/ngwr-dialog-testing.mjs.map +1 -1
  9. package/fesm2022/ngwr-dialog.mjs +293 -134
  10. package/fesm2022/ngwr-dialog.mjs.map +1 -1
  11. package/fesm2022/ngwr-drawer.mjs +2 -0
  12. package/fesm2022/ngwr-drawer.mjs.map +1 -1
  13. package/fesm2022/ngwr-editor.mjs +1 -1
  14. package/fesm2022/ngwr-hotkey.mjs +86 -6
  15. package/fesm2022/ngwr-hotkey.mjs.map +1 -1
  16. package/fesm2022/ngwr-i18n-ar.mjs +19 -0
  17. package/fesm2022/ngwr-i18n-ar.mjs.map +1 -1
  18. package/fesm2022/ngwr-i18n-cs.mjs +19 -0
  19. package/fesm2022/ngwr-i18n-cs.mjs.map +1 -1
  20. package/fesm2022/ngwr-i18n-de.mjs +19 -0
  21. package/fesm2022/ngwr-i18n-de.mjs.map +1 -1
  22. package/fesm2022/ngwr-i18n-en.mjs +19 -0
  23. package/fesm2022/ngwr-i18n-en.mjs.map +1 -1
  24. package/fesm2022/ngwr-i18n-es.mjs +19 -0
  25. package/fesm2022/ngwr-i18n-es.mjs.map +1 -1
  26. package/fesm2022/ngwr-i18n-fr.mjs +19 -0
  27. package/fesm2022/ngwr-i18n-fr.mjs.map +1 -1
  28. package/fesm2022/ngwr-i18n-he.mjs +19 -0
  29. package/fesm2022/ngwr-i18n-he.mjs.map +1 -1
  30. package/fesm2022/ngwr-i18n-hi.mjs +19 -0
  31. package/fesm2022/ngwr-i18n-hi.mjs.map +1 -1
  32. package/fesm2022/ngwr-i18n-id.mjs +19 -0
  33. package/fesm2022/ngwr-i18n-id.mjs.map +1 -1
  34. package/fesm2022/ngwr-i18n-it.mjs +19 -0
  35. package/fesm2022/ngwr-i18n-it.mjs.map +1 -1
  36. package/fesm2022/ngwr-i18n-ja.mjs +19 -0
  37. package/fesm2022/ngwr-i18n-ja.mjs.map +1 -1
  38. package/fesm2022/ngwr-i18n-ko.mjs +19 -0
  39. package/fesm2022/ngwr-i18n-ko.mjs.map +1 -1
  40. package/fesm2022/ngwr-i18n-nl.mjs +19 -0
  41. package/fesm2022/ngwr-i18n-nl.mjs.map +1 -1
  42. package/fesm2022/ngwr-i18n-pl.mjs +19 -0
  43. package/fesm2022/ngwr-i18n-pl.mjs.map +1 -1
  44. package/fesm2022/ngwr-i18n-pt.mjs +19 -0
  45. package/fesm2022/ngwr-i18n-pt.mjs.map +1 -1
  46. package/fesm2022/ngwr-i18n-ru.mjs +19 -0
  47. package/fesm2022/ngwr-i18n-ru.mjs.map +1 -1
  48. package/fesm2022/ngwr-i18n-sv.mjs +19 -0
  49. package/fesm2022/ngwr-i18n-sv.mjs.map +1 -1
  50. package/fesm2022/ngwr-i18n-tr.mjs +19 -0
  51. package/fesm2022/ngwr-i18n-tr.mjs.map +1 -1
  52. package/fesm2022/ngwr-i18n-uk.mjs +19 -0
  53. package/fesm2022/ngwr-i18n-uk.mjs.map +1 -1
  54. package/fesm2022/ngwr-i18n-vi.mjs +19 -0
  55. package/fesm2022/ngwr-i18n-vi.mjs.map +1 -1
  56. package/fesm2022/ngwr-i18n-zh-tw.mjs +19 -0
  57. package/fesm2022/ngwr-i18n-zh-tw.mjs.map +1 -1
  58. package/fesm2022/ngwr-i18n-zh.mjs +19 -0
  59. package/fesm2022/ngwr-i18n-zh.mjs.map +1 -1
  60. package/fesm2022/ngwr-input-number-testing.mjs.map +1 -1
  61. package/fesm2022/ngwr-input-number.mjs +109 -12
  62. package/fesm2022/ngwr-input-number.mjs.map +1 -1
  63. package/fesm2022/ngwr-keyboard.mjs +78 -18
  64. package/fesm2022/ngwr-keyboard.mjs.map +1 -1
  65. package/fesm2022/ngwr-platform.mjs +134 -2
  66. package/fesm2022/ngwr-platform.mjs.map +1 -1
  67. package/fesm2022/ngwr-schema-form.mjs +2 -1
  68. package/fesm2022/ngwr-schema-form.mjs.map +1 -1
  69. package/fesm2022/ngwr-select.mjs.map +1 -1
  70. package/fesm2022/ngwr-storage.mjs +54 -33
  71. package/fesm2022/ngwr-storage.mjs.map +1 -1
  72. package/fesm2022/ngwr-table.mjs +90 -34
  73. package/fesm2022/ngwr-table.mjs.map +1 -1
  74. package/fesm2022/ngwr-transfer.mjs +1 -1
  75. package/fesm2022/ngwr-utils-router.mjs +165 -0
  76. package/fesm2022/ngwr-utils-router.mjs.map +1 -0
  77. package/fesm2022/ngwr-utils.mjs +311 -3
  78. package/fesm2022/ngwr-utils.mjs.map +1 -1
  79. package/fesm2022/ngwr-version.mjs +1 -1
  80. package/fesm2022/ngwr-version.mjs.map +1 -1
  81. package/i18n/ar.json +19 -0
  82. package/i18n/cs.json +19 -0
  83. package/i18n/de.json +19 -0
  84. package/i18n/en.json +19 -0
  85. package/i18n/es.json +19 -0
  86. package/i18n/fr.json +19 -0
  87. package/i18n/he.json +19 -0
  88. package/i18n/hi.json +19 -0
  89. package/i18n/id.json +19 -0
  90. package/i18n/it.json +19 -0
  91. package/i18n/ja.json +19 -0
  92. package/i18n/ko.json +19 -0
  93. package/i18n/nl.json +19 -0
  94. package/i18n/pl.json +19 -0
  95. package/i18n/pt.json +19 -0
  96. package/i18n/ru.json +19 -0
  97. package/i18n/sv.json +19 -0
  98. package/i18n/tr.json +19 -0
  99. package/i18n/uk.json +19 -0
  100. package/i18n/vi.json +19 -0
  101. package/i18n/zh-TW.json +19 -0
  102. package/i18n/zh.json +19 -0
  103. package/llms-full.txt +30 -14
  104. package/package.json +14 -1
  105. package/schematics/use/symbol-map.json +2 -0
  106. package/skills/ngwr/SKILL.md +1 -1
  107. package/skills/ngwr/references/catalog.md +3 -0
  108. package/table/styles/_index.scss +17 -0
  109. package/types/ngwr-badge.d.ts +2 -2
  110. package/types/ngwr-button.d.ts +1 -1
  111. package/types/ngwr-comment-testing.d.ts +90 -0
  112. package/types/ngwr-comment.d.ts +304 -0
  113. package/types/ngwr-dialog-testing.d.ts +7 -0
  114. package/types/ngwr-dialog.d.ts +109 -6
  115. package/types/ngwr-divider.d.ts +1 -1
  116. package/types/ngwr-drawer.d.ts +1 -1
  117. package/types/ngwr-hotkey.d.ts +60 -3
  118. package/types/ngwr-image-cropper.d.ts +1 -1
  119. package/types/ngwr-input-number-testing.d.ts +5 -0
  120. package/types/ngwr-input-number.d.ts +91 -12
  121. package/types/ngwr-keyboard.d.ts +28 -1
  122. package/types/ngwr-platform.d.ts +73 -7
  123. package/types/ngwr-popconfirm.d.ts +2 -2
  124. package/types/ngwr-progress.d.ts +1 -1
  125. package/types/ngwr-select.d.ts +19 -13
  126. package/types/ngwr-skeleton.d.ts +1 -1
  127. package/types/ngwr-storage.d.ts +33 -3
  128. package/types/ngwr-table.d.ts +145 -44
  129. package/types/ngwr-utils-router.d.ts +95 -0
  130. package/types/ngwr-utils.d.ts +262 -2
  131. package/types/ngwr-version.d.ts +1 -1
@@ -0,0 +1,516 @@
1
+ import * as i0 from "@angular/core";
2
+ import { Component, ElementRef, ViewEncapsulation, computed, inject, input, model, output, viewChild } from "@angular/core";
3
+ import { coerceBooleanProperty, coerceNumberProperty } from "@angular/cdk/coercion";
4
+ import { useI18nText } from "ngwr/i18n";
5
+ import { KEYS, isComposing } from "ngwr/utils";
6
+ import { WrButton } from "ngwr/button";
7
+ import { WrTextarea } from "ngwr/textarea";
8
+ /**
9
+ * @license
10
+ *
11
+ * Use of this source code is governed by an MIT-style license that can be
12
+ * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
13
+ */
14
+ var WrComment = class WrComment {
15
+ author = input("", ...ngDevMode ? [{ debugName: "author" }] : /* istanbul ignore next */ []);
16
+ time = input("", ...ngDevMode ? [{ debugName: "time" }] : /* istanbul ignore next */ []);
17
+ datetime = input(null, ...ngDevMode ? [{ debugName: "datetime" }] : /* istanbul ignore next */ []);
18
+ variant = input("row", ...ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []);
19
+ side = input("start", ...ngDevMode ? [{ debugName: "side" }] : /* istanbul ignore next */ []);
20
+ repliesLabel = input(null, ...ngDevMode ? [{ debugName: "repliesLabel" }] : /* istanbul ignore next */ []);
21
+ resolvedRepliesLabel = useI18nText(this.repliesLabel, "comment.replies", "Replies");
22
+ isoDatetime = computed(() => {
23
+ const value = this.datetime();
24
+ if (value === null) return null;
25
+ if (value instanceof Date) return Number.isNaN(value.getTime()) ? null : value.toISOString();
26
+ return value || null;
27
+ }, ...ngDevMode ? [{ debugName: "isoDatetime" }] : /* istanbul ignore next */ []);
28
+ classes = computed(() => {
29
+ const parts = ["wr-comment"];
30
+ if (this.variant() === "bubble") parts.push("wr-comment--bubble", `wr-comment--${this.side()}`);
31
+ return parts.join(" ");
32
+ }, ...ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []);
33
+ static ɵfac = i0.ɵɵngDeclareFactory({
34
+ minVersion: "12.0.0",
35
+ version: "22.2.4",
36
+ ngImport: i0,
37
+ type: WrComment,
38
+ deps: [],
39
+ target: i0.ɵɵFactoryTarget.Component
40
+ });
41
+ static ɵcmp = i0.ɵɵngDeclareComponent({
42
+ minVersion: "17.0.0",
43
+ version: "22.2.4",
44
+ type: WrComment,
45
+ isStandalone: true,
46
+ selector: "wr-comment",
47
+ inputs: {
48
+ author: {
49
+ classPropertyName: "author",
50
+ publicName: "author",
51
+ isSignal: true,
52
+ isRequired: false,
53
+ transformFunction: null
54
+ },
55
+ time: {
56
+ classPropertyName: "time",
57
+ publicName: "time",
58
+ isSignal: true,
59
+ isRequired: false,
60
+ transformFunction: null
61
+ },
62
+ datetime: {
63
+ classPropertyName: "datetime",
64
+ publicName: "datetime",
65
+ isSignal: true,
66
+ isRequired: false,
67
+ transformFunction: null
68
+ },
69
+ variant: {
70
+ classPropertyName: "variant",
71
+ publicName: "variant",
72
+ isSignal: true,
73
+ isRequired: false,
74
+ transformFunction: null
75
+ },
76
+ side: {
77
+ classPropertyName: "side",
78
+ publicName: "side",
79
+ isSignal: true,
80
+ isRequired: false,
81
+ transformFunction: null
82
+ },
83
+ repliesLabel: {
84
+ classPropertyName: "repliesLabel",
85
+ publicName: "repliesLabel",
86
+ isSignal: true,
87
+ isRequired: false,
88
+ transformFunction: null
89
+ }
90
+ },
91
+ host: {
92
+ attributes: { "role": "article" },
93
+ properties: {
94
+ "class": "classes()",
95
+ "attr.aria-label": "author() || null"
96
+ }
97
+ },
98
+ ngImport: i0,
99
+ template: "<div class=\"wr-comment__avatar\">\n <ng-content select=\"wr-avatar, [wrCommentAvatar]\" />\n</div>\n\n<div class=\"wr-comment__main\">\n <div class=\"wr-comment__head\">\n @if (author(); as name) {\n <span class=\"wr-comment__author\">{{ name }}</span>\n }\n <ng-content select=\"[wrCommentAuthor]\" />\n @if (time(); as printed) {\n @if (isoDatetime(); as iso) {\n <time class=\"wr-comment__time\" [attr.datetime]=\"iso\">{{ printed }}</time>\n } @else {\n <span class=\"wr-comment__time\">{{ printed }}</span>\n }\n }\n </div>\n\n <div class=\"wr-comment__body\"><ng-content /></div>\n\n <div class=\"wr-comment__actions\">\n <ng-content select=\"[wrCommentActions]\" />\n </div>\n</div>\n\n<div class=\"wr-comment__replies\" role=\"group\" [attr.aria-label]=\"resolvedRepliesLabel()\">\n <ng-content select=\"wr-comment, [wrCommentReplies]\" />\n</div>\n",
100
+ encapsulation: i0.ViewEncapsulation.None
101
+ });
102
+ };
103
+ i0.ɵɵngDeclareClassMetadata({
104
+ minVersion: "12.0.0",
105
+ version: "22.2.4",
106
+ ngImport: i0,
107
+ type: WrComment,
108
+ decorators: [{
109
+ type: Component,
110
+ args: [{
111
+ selector: "wr-comment",
112
+ encapsulation: ViewEncapsulation.None,
113
+ host: {
114
+ "[class]": "classes()",
115
+ role: "article",
116
+ "[attr.aria-label]": "author() || null"
117
+ },
118
+ template: "<div class=\"wr-comment__avatar\">\n <ng-content select=\"wr-avatar, [wrCommentAvatar]\" />\n</div>\n\n<div class=\"wr-comment__main\">\n <div class=\"wr-comment__head\">\n @if (author(); as name) {\n <span class=\"wr-comment__author\">{{ name }}</span>\n }\n <ng-content select=\"[wrCommentAuthor]\" />\n @if (time(); as printed) {\n @if (isoDatetime(); as iso) {\n <time class=\"wr-comment__time\" [attr.datetime]=\"iso\">{{ printed }}</time>\n } @else {\n <span class=\"wr-comment__time\">{{ printed }}</span>\n }\n }\n </div>\n\n <div class=\"wr-comment__body\"><ng-content /></div>\n\n <div class=\"wr-comment__actions\">\n <ng-content select=\"[wrCommentActions]\" />\n </div>\n</div>\n\n<div class=\"wr-comment__replies\" role=\"group\" [attr.aria-label]=\"resolvedRepliesLabel()\">\n <ng-content select=\"wr-comment, [wrCommentReplies]\" />\n</div>\n"
119
+ }]
120
+ }],
121
+ propDecorators: {
122
+ author: [{
123
+ type: i0.Input,
124
+ args: [{
125
+ isSignal: true,
126
+ alias: "author",
127
+ required: false
128
+ }]
129
+ }],
130
+ time: [{
131
+ type: i0.Input,
132
+ args: [{
133
+ isSignal: true,
134
+ alias: "time",
135
+ required: false
136
+ }]
137
+ }],
138
+ datetime: [{
139
+ type: i0.Input,
140
+ args: [{
141
+ isSignal: true,
142
+ alias: "datetime",
143
+ required: false
144
+ }]
145
+ }],
146
+ variant: [{
147
+ type: i0.Input,
148
+ args: [{
149
+ isSignal: true,
150
+ alias: "variant",
151
+ required: false
152
+ }]
153
+ }],
154
+ side: [{
155
+ type: i0.Input,
156
+ args: [{
157
+ isSignal: true,
158
+ alias: "side",
159
+ required: false
160
+ }]
161
+ }],
162
+ repliesLabel: [{
163
+ type: i0.Input,
164
+ args: [{
165
+ isSignal: true,
166
+ alias: "repliesLabel",
167
+ required: false
168
+ }]
169
+ }]
170
+ }
171
+ });
172
+ /**
173
+ * @license
174
+ *
175
+ * Use of this source code is governed by an MIT-style license that can be
176
+ * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
177
+ */
178
+ var WrCommentComposer = class WrCommentComposer {
179
+ placeholder = input("", ...ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []);
180
+ ariaLabel = input(null, ...ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []);
181
+ sendLabel = input(null, ...ngDevMode ? [{ debugName: "sendLabel" }] : /* istanbul ignore next */ []);
182
+ rows = input(1, {
183
+ ...ngDevMode ? { debugName: "rows" } : /* istanbul ignore next */ {},
184
+ transform: (v) => coerceNumberProperty(v, 1)
185
+ });
186
+ maxRows = input(6, {
187
+ ...ngDevMode ? { debugName: "maxRows" } : /* istanbul ignore next */ {},
188
+ transform: (v) => coerceNumberProperty(v, 6)
189
+ });
190
+ disabled = input(false, {
191
+ ...ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {},
192
+ transform: coerceBooleanProperty
193
+ });
194
+ readonly = input(false, {
195
+ ...ngDevMode ? { debugName: "readonly" } : /* istanbul ignore next */ {},
196
+ transform: coerceBooleanProperty
197
+ });
198
+ sending = input(false, {
199
+ ...ngDevMode ? { debugName: "sending" } : /* istanbul ignore next */ {},
200
+ transform: coerceBooleanProperty
201
+ });
202
+ enterSends = input(true, {
203
+ ...ngDevMode ? { debugName: "enterSends" } : /* istanbul ignore next */ {},
204
+ transform: coerceBooleanProperty
205
+ });
206
+ clearOnSend = input(true, {
207
+ ...ngDevMode ? { debugName: "clearOnSend" } : /* istanbul ignore next */ {},
208
+ transform: coerceBooleanProperty
209
+ });
210
+ value = model("", ...ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []);
211
+ touch = output();
212
+ send = output();
213
+ resolvedSendLabel = useI18nText(this.sendLabel, "comment.send", "Send");
214
+ canSend = computed(() => this.value().trim() !== "" && !this.disabled() && !this.readonly() && !this.sending(), ...ngDevMode ? [{ debugName: "canSend" }] : /* istanbul ignore next */ []);
215
+ classes = computed(() => {
216
+ const parts = ["wr-comment-composer"];
217
+ if (this.disabled()) parts.push("wr-comment-composer--disabled");
218
+ if (this.readonly()) parts.push("wr-comment-composer--readonly");
219
+ if (this.sending()) parts.push("wr-comment-composer--sending");
220
+ return parts.join(" ");
221
+ }, ...ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []);
222
+ field = viewChild.required(WrTextarea, ...ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []);
223
+ host = inject(ElementRef);
224
+ focus(options) {
225
+ this.field().focus(options);
226
+ }
227
+ submit() {
228
+ if (!this.canSend()) return;
229
+ const text = this.value().trim();
230
+ const hadFocus = this.host.nativeElement.contains(this.host.nativeElement.ownerDocument.activeElement);
231
+ if (this.clearOnSend()) this.value.set("");
232
+ this.send.emit(text);
233
+ if (hadFocus && this.host.nativeElement.contains(this.host.nativeElement.ownerDocument.activeElement)) this.focus();
234
+ }
235
+ onKeydown(event) {
236
+ if (event.key !== KEYS.ENTER || isComposing(event)) return;
237
+ if (!(event.ctrlKey || event.metaKey) && (event.shiftKey || event.altKey || !this.enterSends())) return;
238
+ event.preventDefault();
239
+ this.submit();
240
+ }
241
+ static ɵfac = i0.ɵɵngDeclareFactory({
242
+ minVersion: "12.0.0",
243
+ version: "22.2.4",
244
+ ngImport: i0,
245
+ type: WrCommentComposer,
246
+ deps: [],
247
+ target: i0.ɵɵFactoryTarget.Component
248
+ });
249
+ static ɵcmp = i0.ɵɵngDeclareComponent({
250
+ minVersion: "17.2.0",
251
+ version: "22.2.4",
252
+ type: WrCommentComposer,
253
+ isStandalone: true,
254
+ selector: "wr-comment-composer",
255
+ inputs: {
256
+ placeholder: {
257
+ classPropertyName: "placeholder",
258
+ publicName: "placeholder",
259
+ isSignal: true,
260
+ isRequired: false,
261
+ transformFunction: null
262
+ },
263
+ ariaLabel: {
264
+ classPropertyName: "ariaLabel",
265
+ publicName: "ariaLabel",
266
+ isSignal: true,
267
+ isRequired: false,
268
+ transformFunction: null
269
+ },
270
+ sendLabel: {
271
+ classPropertyName: "sendLabel",
272
+ publicName: "sendLabel",
273
+ isSignal: true,
274
+ isRequired: false,
275
+ transformFunction: null
276
+ },
277
+ rows: {
278
+ classPropertyName: "rows",
279
+ publicName: "rows",
280
+ isSignal: true,
281
+ isRequired: false,
282
+ transformFunction: null
283
+ },
284
+ maxRows: {
285
+ classPropertyName: "maxRows",
286
+ publicName: "maxRows",
287
+ isSignal: true,
288
+ isRequired: false,
289
+ transformFunction: null
290
+ },
291
+ disabled: {
292
+ classPropertyName: "disabled",
293
+ publicName: "disabled",
294
+ isSignal: true,
295
+ isRequired: false,
296
+ transformFunction: null
297
+ },
298
+ readonly: {
299
+ classPropertyName: "readonly",
300
+ publicName: "readonly",
301
+ isSignal: true,
302
+ isRequired: false,
303
+ transformFunction: null
304
+ },
305
+ sending: {
306
+ classPropertyName: "sending",
307
+ publicName: "sending",
308
+ isSignal: true,
309
+ isRequired: false,
310
+ transformFunction: null
311
+ },
312
+ enterSends: {
313
+ classPropertyName: "enterSends",
314
+ publicName: "enterSends",
315
+ isSignal: true,
316
+ isRequired: false,
317
+ transformFunction: null
318
+ },
319
+ clearOnSend: {
320
+ classPropertyName: "clearOnSend",
321
+ publicName: "clearOnSend",
322
+ isSignal: true,
323
+ isRequired: false,
324
+ transformFunction: null
325
+ },
326
+ value: {
327
+ classPropertyName: "value",
328
+ publicName: "value",
329
+ isSignal: true,
330
+ isRequired: false,
331
+ transformFunction: null
332
+ }
333
+ },
334
+ outputs: {
335
+ value: "valueChange",
336
+ touch: "touch",
337
+ send: "send"
338
+ },
339
+ host: { properties: {
340
+ "class": "classes()",
341
+ "attr.aria-busy": "sending() ? \"true\" : null"
342
+ } },
343
+ viewQueries: [{
344
+ propertyName: "field",
345
+ first: true,
346
+ predicate: WrTextarea,
347
+ descendants: true,
348
+ isSignal: true
349
+ }],
350
+ ngImport: i0,
351
+ template: "<wr-textarea\n class=\"wr-comment-composer__field\"\n autosize\n [rows]=\"rows()\"\n [maxRows]=\"maxRows()\"\n [resizable]=\"false\"\n [placeholder]=\"placeholder()\"\n [ariaLabel]=\"ariaLabel()\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n [(value)]=\"value\"\n (touch)=\"touch.emit()\"\n (keydown)=\"onKeydown($event)\"\n/>\n\n<div class=\"wr-comment-composer__actions\">\n <ng-content select=\"[wrComposerActions]\" />\n</div>\n\n<button\n wr-btn\n type=\"button\"\n color=\"primary\"\n shape=\"pill\"\n class=\"wr-comment-composer__send\"\n [attr.aria-label]=\"resolvedSendLabel()\"\n [disabled]=\"!canSend()\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"submit()\"\n>\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M3.714 3.048a.498.498 0 0 0-.683.627l2.843 7.627a2 2 0 0 1 0 1.396l-2.842 7.627a.498.498 0 0 0 .682.627l18-8.5a.5.5 0 0 0 0-.904z\"\n />\n <path d=\"M6 12h16\" />\n </svg>\n</button>\n",
352
+ dependencies: [{
353
+ kind: "component",
354
+ type: WrButton,
355
+ selector: "wr-btn, button[wr-btn], a[wr-btn]",
356
+ inputs: [
357
+ "color",
358
+ "size",
359
+ "shape",
360
+ "icon",
361
+ "iconPosition",
362
+ "disabled",
363
+ "outlined",
364
+ "block",
365
+ "loading",
366
+ "disabledWhenLoading"
367
+ ]
368
+ }, {
369
+ kind: "component",
370
+ type: WrTextarea,
371
+ selector: "wr-textarea",
372
+ inputs: [
373
+ "placeholder",
374
+ "ariaLabel",
375
+ "size",
376
+ "rows",
377
+ "resizable",
378
+ "resize",
379
+ "readonly",
380
+ "autosize",
381
+ "maxRows",
382
+ "disabled",
383
+ "value"
384
+ ],
385
+ outputs: ["valueChange", "touch"]
386
+ }],
387
+ encapsulation: i0.ViewEncapsulation.None
388
+ });
389
+ };
390
+ i0.ɵɵngDeclareClassMetadata({
391
+ minVersion: "12.0.0",
392
+ version: "22.2.4",
393
+ ngImport: i0,
394
+ type: WrCommentComposer,
395
+ decorators: [{
396
+ type: Component,
397
+ args: [{
398
+ selector: "wr-comment-composer",
399
+ imports: [WrButton, WrTextarea],
400
+ encapsulation: ViewEncapsulation.None,
401
+ host: {
402
+ "[class]": "classes()",
403
+ "[attr.aria-busy]": "sending() ? \"true\" : null"
404
+ },
405
+ template: "<wr-textarea\n class=\"wr-comment-composer__field\"\n autosize\n [rows]=\"rows()\"\n [maxRows]=\"maxRows()\"\n [resizable]=\"false\"\n [placeholder]=\"placeholder()\"\n [ariaLabel]=\"ariaLabel()\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n [(value)]=\"value\"\n (touch)=\"touch.emit()\"\n (keydown)=\"onKeydown($event)\"\n/>\n\n<div class=\"wr-comment-composer__actions\">\n <ng-content select=\"[wrComposerActions]\" />\n</div>\n\n<button\n wr-btn\n type=\"button\"\n color=\"primary\"\n shape=\"pill\"\n class=\"wr-comment-composer__send\"\n [attr.aria-label]=\"resolvedSendLabel()\"\n [disabled]=\"!canSend()\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"submit()\"\n>\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M3.714 3.048a.498.498 0 0 0-.683.627l2.843 7.627a2 2 0 0 1 0 1.396l-2.842 7.627a.498.498 0 0 0 .682.627l18-8.5a.5.5 0 0 0 0-.904z\"\n />\n <path d=\"M6 12h16\" />\n </svg>\n</button>\n"
406
+ }]
407
+ }],
408
+ propDecorators: {
409
+ placeholder: [{
410
+ type: i0.Input,
411
+ args: [{
412
+ isSignal: true,
413
+ alias: "placeholder",
414
+ required: false
415
+ }]
416
+ }],
417
+ ariaLabel: [{
418
+ type: i0.Input,
419
+ args: [{
420
+ isSignal: true,
421
+ alias: "ariaLabel",
422
+ required: false
423
+ }]
424
+ }],
425
+ sendLabel: [{
426
+ type: i0.Input,
427
+ args: [{
428
+ isSignal: true,
429
+ alias: "sendLabel",
430
+ required: false
431
+ }]
432
+ }],
433
+ rows: [{
434
+ type: i0.Input,
435
+ args: [{
436
+ isSignal: true,
437
+ alias: "rows",
438
+ required: false
439
+ }]
440
+ }],
441
+ maxRows: [{
442
+ type: i0.Input,
443
+ args: [{
444
+ isSignal: true,
445
+ alias: "maxRows",
446
+ required: false
447
+ }]
448
+ }],
449
+ disabled: [{
450
+ type: i0.Input,
451
+ args: [{
452
+ isSignal: true,
453
+ alias: "disabled",
454
+ required: false
455
+ }]
456
+ }],
457
+ readonly: [{
458
+ type: i0.Input,
459
+ args: [{
460
+ isSignal: true,
461
+ alias: "readonly",
462
+ required: false
463
+ }]
464
+ }],
465
+ sending: [{
466
+ type: i0.Input,
467
+ args: [{
468
+ isSignal: true,
469
+ alias: "sending",
470
+ required: false
471
+ }]
472
+ }],
473
+ enterSends: [{
474
+ type: i0.Input,
475
+ args: [{
476
+ isSignal: true,
477
+ alias: "enterSends",
478
+ required: false
479
+ }]
480
+ }],
481
+ clearOnSend: [{
482
+ type: i0.Input,
483
+ args: [{
484
+ isSignal: true,
485
+ alias: "clearOnSend",
486
+ required: false
487
+ }]
488
+ }],
489
+ value: [{
490
+ type: i0.Input,
491
+ args: [{
492
+ isSignal: true,
493
+ alias: "value",
494
+ required: false
495
+ }]
496
+ }, {
497
+ type: i0.Output,
498
+ args: ["valueChange"]
499
+ }],
500
+ touch: [{
501
+ type: i0.Output,
502
+ args: ["touch"]
503
+ }],
504
+ send: [{
505
+ type: i0.Output,
506
+ args: ["send"]
507
+ }],
508
+ field: [{
509
+ type: i0.ViewChild,
510
+ args: [i0.forwardRef(() => WrTextarea), { isSignal: true }]
511
+ }]
512
+ }
513
+ });
514
+ export { WrComment, WrCommentComposer };
515
+
516
+ //# sourceMappingURL=ngwr-comment.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ngwr-comment.mjs","names":[],"sources":["../../../projects/lib/comment/comment.ts","../../../projects/lib/comment/comment.html","../../../projects/lib/comment/comment-composer.ts","../../../projects/lib/comment/comment-composer.html"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { Component, ViewEncapsulation, computed, input } from '@angular/core';\n\nimport { useI18nText } from 'ngwr/i18n';\n\nimport type { WrCommentSide, WrCommentVariant } from './types';\n\n/**\n * One entry in a discussion or a chat: avatar, author, time, body, actions and\n * nested replies. The data stays with the app: the component takes no array,\n * fetches nothing and sorts nothing. Everything is projected or passed as a\n * string, and the app formats the time itself (`wrDate` is the usual route).\n *\n * Slots, each optional:\n *\n * - `<wr-avatar>` or `[wrCommentAvatar]` for the avatar column\n * - `[wrCommentAuthor]` for an author drawn by the app (a profile link) in\n * place of the `author` input\n * - default content for the body\n * - `[wrCommentActions]` for the action row under the body\n * - nested `<wr-comment>` elements, or a `[wrCommentReplies]` wrapper, for\n * replies; they indent twice, and deeper levels sit flush with their parent\n * so a phone keeps room for the body\n *\n * The host is an `article` named after the author, replies sit in a `group`\n * named by `repliesLabel`, and no heading level is assumed: the page owns the\n * outline. Wrap a thread in a list and a chat in `role=\"log\"`.\n *\n * `variant=\"bubble\"` draws the body as a chat bubble; `side=\"end\"` puts it on\n * the reader's own side of the log.\n *\n * @example\n * ```html\n * <wr-comment author=\"Ann\" time=\"2 hours ago\" [datetime]=\"posted\">\n * <wr-avatar size=\"2.5rem\" alt=\"\">AN</wr-avatar>\n * Looks good to me.\n * <div wrCommentActions><button wr-btn size=\"sm\">Reply</button></div>\n * <wr-comment author=\"Bob\" time=\"an hour ago\">Thanks!</wr-comment>\n * </wr-comment>\n *\n * <wr-comment variant=\"bubble\" side=\"end\" time=\"09:41\">On my way.</wr-comment>\n * ```\n *\n * @see https://ngwr.dev/reference/components/comment\n */\n@Component({\n selector: 'wr-comment',\n templateUrl: './comment.html',\n encapsulation: ViewEncapsulation.None,\n host: {\n '[class]': 'classes()',\n role: 'article',\n '[attr.aria-label]': 'author() || null',\n },\n})\nexport class WrComment {\n /**\n * Who wrote it. Also the article's accessible name. Leave it empty when a\n * `[wrCommentAuthor]` element draws the name instead.\n *\n * @default ''\n */\n readonly author = input<string>('');\n\n /**\n * When it was written, already formatted: the component does no date\n * formatting. Run a `Date` through the `wrDate` pipe on the way in.\n *\n * @default ''\n */\n readonly time = input<string>('');\n\n /**\n * The same moment, machine-readable. With it the time renders as a\n * `<time datetime>`: a `Date` is written as ISO 8601, a string as given.\n *\n * @default null\n */\n readonly datetime = input<Date | string | null>(null);\n\n /**\n * `row` is a discussion entry; `bubble` is a chat message, which takes no replies.\n *\n * @default 'row'\n */\n readonly variant = input<WrCommentVariant>('row');\n\n /**\n * Which inline side a bubble sits on. `end` is the reader's own message.\n * Ignored by the `row` variant.\n *\n * @default 'start'\n */\n readonly side = input<WrCommentSide>('start');\n\n /** Accessible name of the replies group. Falls back to `comment.replies`, then `'Replies'`. */\n readonly repliesLabel = input<string | null>(null);\n\n protected readonly resolvedRepliesLabel = useI18nText(this.repliesLabel, 'comment.replies', 'Replies');\n\n protected readonly isoDatetime = computed(() => {\n const value = this.datetime();\n if (value === null) return null;\n if (value instanceof Date) return Number.isNaN(value.getTime()) ? null : value.toISOString();\n return value || null;\n });\n\n protected readonly classes = computed(() => {\n const parts = ['wr-comment'];\n if (this.variant() === 'bubble') parts.push('wr-comment--bubble', `wr-comment--${this.side()}`);\n return parts.join(' ');\n });\n}\n","<div class=\"wr-comment__avatar\">\n <ng-content select=\"wr-avatar, [wrCommentAvatar]\" />\n</div>\n\n<div class=\"wr-comment__main\">\n <div class=\"wr-comment__head\">\n @if (author(); as name) {\n <span class=\"wr-comment__author\">{{ name }}</span>\n }\n <ng-content select=\"[wrCommentAuthor]\" />\n @if (time(); as printed) {\n @if (isoDatetime(); as iso) {\n <time class=\"wr-comment__time\" [attr.datetime]=\"iso\">{{ printed }}</time>\n } @else {\n <span class=\"wr-comment__time\">{{ printed }}</span>\n }\n }\n </div>\n\n <div class=\"wr-comment__body\"><ng-content /></div>\n\n <div class=\"wr-comment__actions\">\n <ng-content select=\"[wrCommentActions]\" />\n </div>\n</div>\n\n<div class=\"wr-comment__replies\" role=\"group\" [attr.aria-label]=\"resolvedRepliesLabel()\">\n <ng-content select=\"wr-comment, [wrCommentReplies]\" />\n</div>\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coercion';\nimport {\n Component,\n ElementRef,\n ViewEncapsulation,\n computed,\n inject,\n input,\n model,\n output,\n viewChild,\n} from '@angular/core';\nimport type { FormValueControl } from '@angular/forms/signals';\n\nimport { WrButton } from 'ngwr/button';\nimport { useI18nText } from 'ngwr/i18n';\nimport { WrTextarea } from 'ngwr/textarea';\nimport { KEYS, isComposing } from 'ngwr/utils';\n\n/**\n * A chat composer: an autosizing field with a send button. Enter sends,\n * Shift+Enter adds a line, Ctrl+Enter and Cmd+Enter always send. The text is\n * emitted through `send` and the composer decides nothing about where it goes.\n *\n * A signal-forms native control: it implements `FormValueControl<string>`, so\n * `[formField]` binds straight to its `value` model. `[(value)]` works\n * standalone.\n *\n * What sends and what does not:\n *\n * - Whitespace-only text never sends; the button is disabled until there is\n * something to send, and Enter does nothing.\n * - `disabled`, `readonly` and `sending` all block sending and keep the text.\n * `sending` also marks the composer `aria-busy`; the field stays editable so\n * the next message can be typed.\n * - An Enter that confirms an IME composition never sends. Both signals are\n * read, `isComposing` and `keyCode === 229`, because Safari fires\n * `compositionend` before the keydown, so `isComposing` is already `false`\n * on the committing keystroke.\n *\n * The text is trimmed and, with `clearOnSend` (the default), the field is\n * cleared BEFORE `send` emits: an app whose request fails puts the text back\n * through the composer's own model, `composer.value.set(text)`, at any time,\n * and nothing overwrites it. A model bound with `[(value)]` takes that write\n * only once change detection has run in between: written back synchronously\n * from the handler, the bound expression still holds the value Angular last\n * bound, so the input is skipped. An app that would rather keep the text until\n * the request succeeds sets `[clearOnSend]=\"false\"` and clears the model\n * itself. Focus stays in the field after sending, unless the `send` handler\n * moved it.\n *\n * @example\n * ```html\n * <wr-comment-composer placeholder=\"Message\" [(value)]=\"draft\" (send)=\"post($event)\" />\n *\n * <wr-comment-composer [sending]=\"posting()\" [clearOnSend]=\"false\" (send)=\"post($event)\">\n * <button wrComposerActions wr-btn size=\"sm\">Attach</button>\n * </wr-comment-composer>\n * ```\n *\n * @see https://ngwr.dev/reference/components/comment\n */\n@Component({\n selector: 'wr-comment-composer',\n templateUrl: './comment-composer.html',\n imports: [WrButton, WrTextarea],\n encapsulation: ViewEncapsulation.None,\n host: {\n '[class]': 'classes()',\n '[attr.aria-busy]': 'sending() ? \"true\" : null',\n },\n})\nexport class WrCommentComposer implements FormValueControl<string> {\n /** Native placeholder text. @default '' */\n readonly placeholder = input<string>('');\n\n /**\n * Accessible name for the field. Falls back to the placeholder: the native\n * `<textarea>` lives inside the component, so a `<label for>` outside cannot\n * reach it.\n */\n readonly ariaLabel = input<string | null>(null);\n\n /** Accessible name of the send button. Falls back to `comment.send`, then `'Send'`. */\n readonly sendLabel = input<string | null>(null);\n\n /** Visible row count while the field is empty. @default 1 */\n readonly rows = input(1, { transform: (v: unknown): number => coerceNumberProperty(v, 1) });\n\n /** Rows the field grows to before it scrolls on its own. @default 6 */\n readonly maxRows = input(6, { transform: (v: unknown): number => coerceNumberProperty(v, 6) });\n\n /**\n * Disable the field and the button. Bound automatically from the field's\n * disabled state when used with `[formField]`. The text is kept.\n *\n * @default false\n */\n readonly disabled = input(false, { transform: coerceBooleanProperty });\n\n /**\n * Read-only state: the text is shown and announced, keeps its tab stop and\n * cannot be edited or sent.\n *\n * @default false\n */\n readonly readonly = input(false, { transform: coerceBooleanProperty });\n\n /**\n * A send is in flight: the button is disabled, the host is `aria-busy` and\n * Enter does nothing. The field stays editable and the text is kept.\n *\n * @default false\n */\n readonly sending = input(false, { transform: coerceBooleanProperty });\n\n /**\n * `true`: Enter sends and Shift+Enter adds a line. `false`: Enter adds a line\n * and only Ctrl+Enter or Cmd+Enter sends.\n *\n * @default true\n */\n readonly enterSends = input(true, { transform: coerceBooleanProperty });\n\n /**\n * Clear the field before `send` emits. With `false` the text stays until the\n * app clears the model itself.\n *\n * @default true\n */\n readonly clearOnSend = input(true, { transform: coerceBooleanProperty });\n\n /** The draft. Bound by `[formField]`, or two-way via `[(value)]`. */\n readonly value = model<string>('');\n\n /** Emitted on blur so a bound field can mark itself touched. */\n readonly touch = output<void>();\n\n /** The trimmed text, once per send. Where it goes is the app's business. */\n readonly send = output<string>();\n\n protected readonly resolvedSendLabel = useI18nText(this.sendLabel, 'comment.send', 'Send');\n\n /** Whether a send would go through right now. */\n protected readonly canSend = computed(\n () => this.value().trim() !== '' && !this.disabled() && !this.readonly() && !this.sending()\n );\n\n protected readonly classes = computed(() => {\n const parts = ['wr-comment-composer'];\n if (this.disabled()) parts.push('wr-comment-composer--disabled');\n if (this.readonly()) parts.push('wr-comment-composer--readonly');\n if (this.sending()) parts.push('wr-comment-composer--sending');\n return parts.join(' ');\n });\n\n private readonly field = viewChild.required(WrTextarea);\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n\n /** Focus the field, which is what Signal Forms calls to focus this control. */\n focus(options?: FocusOptions): void {\n this.field().focus(options);\n }\n\n /**\n * Send the current text, exactly as the button and Enter do: nothing happens\n * for whitespace-only text or while `disabled`, `readonly` or `sending`.\n */\n submit(): void {\n if (!this.canSend()) return;\n const text = this.value().trim();\n const hadFocus = this.host.nativeElement.contains(this.host.nativeElement.ownerDocument.activeElement);\n if (this.clearOnSend()) this.value.set('');\n this.send.emit(text);\n // A keyboard user is on the button, which sending just disabled: hand focus\n // back unless the handler moved it. A pointer never took it (see the template).\n if (hadFocus && this.host.nativeElement.contains(this.host.nativeElement.ownerDocument.activeElement)) {\n this.focus();\n }\n }\n\n protected onKeydown(event: KeyboardEvent): void {\n if (event.key !== KEYS.ENTER || isComposing(event)) return;\n const modifier = event.ctrlKey || event.metaKey;\n if (!modifier && (event.shiftKey || event.altKey || !this.enterSends())) return;\n // A sending Enter never reaches the field: on empty text it would only add\n // blank lines that the next send trims away.\n event.preventDefault();\n this.submit();\n }\n}\n","<wr-textarea\n class=\"wr-comment-composer__field\"\n autosize\n [rows]=\"rows()\"\n [maxRows]=\"maxRows()\"\n [resizable]=\"false\"\n [placeholder]=\"placeholder()\"\n [ariaLabel]=\"ariaLabel()\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n [(value)]=\"value\"\n (touch)=\"touch.emit()\"\n (keydown)=\"onKeydown($event)\"\n/>\n\n<div class=\"wr-comment-composer__actions\">\n <ng-content select=\"[wrComposerActions]\" />\n</div>\n\n<button\n wr-btn\n type=\"button\"\n color=\"primary\"\n shape=\"pill\"\n class=\"wr-comment-composer__send\"\n [attr.aria-label]=\"resolvedSendLabel()\"\n [disabled]=\"!canSend()\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"submit()\"\n>\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M3.714 3.048a.498.498 0 0 0-.683.627l2.843 7.627a2 2 0 0 1 0 1.396l-2.842 7.627a.498.498 0 0 0 .682.627l18-8.5a.5.5 0 0 0 0-.904z\"\n />\n <path d=\"M6 12h16\" />\n </svg>\n</button>\n"],"mappings":";;;;;;;;;;;;;AA6DA,IAAa,YAAb,MAAa,UAAS;CAOX,SAAS,MAAc,IAAG,GAAA,YAAA,CAAA,EAAA,WAAA,SAAA,CAAA,+BAAA,CAAA,CAAA;CAQ1B,OAAO,MAAc,IAAG,GAAA,YAAA,CAAA,EAAA,WAAA,OAAA,CAAA,+BAAA,CAAA,CAAA;CAQxB,WAAW,MAA4B,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,WAAA,CAAA,+BAAA,CAAA,CAAA;CAO5C,UAAU,MAAwB,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,UAAA,CAAA,+BAAA,CAAA,CAAA;CAQxC,OAAO,MAAqB,SAAQ,GAAA,YAAA,CAAA,EAAA,WAAA,OAAA,CAAA,+BAAA,CAAA,CAAA;CAGpC,eAAe,MAAqB,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,eAAA,CAAA,+BAAA,CAAA,CAAA;CAE/B,uBAAuB,YAAY,KAAK,cAAc,mBAAmB,SAAS;CAElF,cAAc,eAAc;EAC7C,MAAM,QAAQ,KAAK,SAAQ;EAC3B,IAAI,UAAU,MAAM,OAAO;EAC3B,IAAI,iBAAiB,MAAM,OAAO,OAAO,MAAM,MAAM,QAAO,CAAE,IAAI,OAAO,MAAM,YAAW;EAC1F,OAAO,SAAS;CAClB,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,cAAA,CAAA,+BAAA,CAAA,CAAA;CAEiB,UAAU,eAAc;EACzC,MAAM,QAAQ,CAAC,YAAY;EAC3B,IAAI,KAAK,QAAO,MAAO,UAAU,MAAM,KAAK,sBAAsB,eAAe,KAAK,KAAI,GAAI;EAC9F,OAAO,MAAM,KAAK,GAAG;CACvB,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,UAAA,CAAA,+BAAA,CAAA,CAAA;CAxDS,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAS,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAT,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAS,cAAA;EAAA,UAAA;EAAA,QAAA;GAAA,QAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,MAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,UAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,SAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,MAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,cAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;EAAA;EAAA,MAAA;GAAA,YAAA,EAAA,QAAA,UAAA;GAAA,YAAA;IAAA,SAAA;IAAA,mBAAA;GAAA;EAAA;EAAA,UAAA;EAAA,UC7DtB;EA6BA,eAAA,GAAA,kBAAA;CAAA,CAAA;;ADgCa,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAS,YAAA,CAAA;EAVrB,MAAA;EACW,MAAA,CAAA;GAAA,UAAA;GAAY,eAEP,kBAAkB;GAAI,MAC/B;IACJ,WAAW;IACX,MAAM;IACN,qBAAqB;GACtB;GAAA,UAAA;EAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AEoBH,IAAa,oBAAb,MAAa,kBAAiB;CAEnB,cAAc,MAAc,IAAG,GAAA,YAAA,CAAA,EAAA,WAAA,cAAA,CAAA,+BAAA,CAAA,CAAA;CAO/B,YAAY,MAAqB,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,+BAAA,CAAA,CAAA;CAGtC,YAAY,MAAqB,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,+BAAA,CAAA,CAAA;CAGtC,OAAO,MAAM,GAAC;EAAA,GAAA,YAAA,EAAA,WAAA,OAAA,+BAAA,CAAA;EAAI,YAAY,MAAuB,qBAAqB,GAAG,CAAC;CAAC,CAAA;CAG/E,UAAU,MAAM,GAAC;EAAA,GAAA,YAAA,EAAA,WAAA,UAAA,+BAAA,CAAA;EAAI,YAAY,MAAuB,qBAAqB,GAAG,CAAC;CAAC,CAAA;CAQlF,WAAW,MAAM,OAAK;EAAA,GAAA,YAAA,EAAA,WAAA,WAAA,+BAAA,CAAA;EAAI,WAAW;CAAqB,CAAA;CAQ1D,WAAW,MAAM,OAAK;EAAA,GAAA,YAAA,EAAA,WAAA,WAAA,+BAAA,CAAA;EAAI,WAAW;CAAqB,CAAA;CAQ1D,UAAU,MAAM,OAAK;EAAA,GAAA,YAAA,EAAA,WAAA,UAAA,+BAAA,CAAA;EAAI,WAAW;CAAqB,CAAA;CAQzD,aAAa,MAAM,MAAI;EAAA,GAAA,YAAA,EAAA,WAAA,aAAA,+BAAA,CAAA;EAAI,WAAW;CAAqB,CAAA;CAQ3D,cAAc,MAAM,MAAI;EAAA,GAAA,YAAA,EAAA,WAAA,cAAA,+BAAA,CAAA;EAAI,WAAW;CAAqB,CAAA;CAG5D,QAAQ,MAAc,IAAG,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;CAGzB,QAAQ,OAAM;CAGd,OAAO,OAAM;CAEH,oBAAoB,YAAY,KAAK,WAAW,gBAAgB,MAAM;CAGtE,UAAU,eACrB,KAAK,MAAK,CAAE,CAAC,KAAI,MAAO,MAAM,CAAC,KAAK,SAAQ,KAAM,CAAC,KAAK,SAAQ,KAAM,CAAC,KAAK,QAAO,GAC1F,GAAA,YAAA,CAAA,EAAA,WAAA,UAAA,CAAA,+BAAA,CAAA,CAAA;CAEkB,UAAU,eAAc;EACzC,MAAM,QAAQ,CAAC,qBAAqB;EACpC,IAAI,KAAK,SAAQ,GAAI,MAAM,KAAK,+BAA+B;EAC/D,IAAI,KAAK,SAAQ,GAAI,MAAM,KAAK,+BAA+B;EAC/D,IAAI,KAAK,QAAO,GAAI,MAAM,KAAK,8BAA8B;EAC7D,OAAO,MAAM,KAAK,GAAG;CACvB,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,UAAA,CAAA,+BAAA,CAAA,CAAA;CAEe,QAAQ,UAAU,SAAS,YAAW,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;CACtC,OAAO,OAAgC,UAAU;CAGlE,MAAM,SAAsB;EAC1B,KAAK,MAAK,CAAE,CAAC,MAAM,OAAO;CAC5B;CAMA,SAAM;EACJ,IAAI,CAAC,KAAK,QAAO,GAAI;EACrB,MAAM,OAAO,KAAK,MAAK,CAAE,CAAC,KAAI;EAC9B,MAAM,WAAW,KAAK,KAAK,cAAc,SAAS,KAAK,KAAK,cAAc,cAAc,aAAa;EACrG,IAAI,KAAK,YAAW,GAAI,KAAK,MAAM,IAAI,EAAE;EACzC,KAAK,KAAK,KAAK,IAAI;EAGnB,IAAI,YAAY,KAAK,KAAK,cAAc,SAAS,KAAK,KAAK,cAAc,cAAc,aAAa,GAClG,KAAK,MAAK;CAEd;CAEU,UAAU,OAAoB;EACtC,IAAI,MAAM,QAAQ,KAAK,SAAS,YAAY,KAAK,GAAG;EAEpD,IAAI,EADa,MAAM,WAAW,MAAM,aACtB,MAAM,YAAY,MAAM,UAAU,CAAC,KAAK,WAAU,IAAK;EAGzE,MAAM,eAAc;EACpB,KAAK,OAAM;CACb;CArHW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAiB,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAjB,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAiB,cAAA;EAAA,UAAA;EAAA,QAAA;GAAA,aAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,WAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,WAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,MAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,SAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,UAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,UAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,SAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,YAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,aAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,OAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;EAAA;EAAA,SAAA;GAAA,OAAA;GAAA,OAAA;GAAA,MAAA;EAAA;EAAA,MAAA,EAAA,YAAA;GAAA,SAAA;GAAA,kBAAA;EAAA,EAAA;EAAA,aAAA,CAAA;GAAA,cAAA;GAAA,OAAA;GAAA,WAoFgB;GAAU,aAAA;GAAA,UAAA;EAAA,CAAA;EAAA,UAAA;EAAA,UCnKxD;EA+CA,cAAA,CAAA;GAAA,MAAA;GAAA,MDyBY;GAAQ,UAAA;GAAA,QAAA;IAAA;IAAA;IAAA;IAAA;IAAA;IAAA;IAAA;IAAA;IAAA;IAAA;GAAA;EAAA,GAAA;GAAA,MAAA;GAAA,MAAE;GAAU,UAAA;GAAA,QAAA;IAAA;IAAA;IAAA;IAAA;IAAA;IAAA;IAAA;IAAA;IAAA;IAAA;IAAA;GAAA;GAAA,SAAA,CAAA,eAAA,OAAA;EAAA,CAAA;EAAA,eAAA,GAAA,kBAAA;CAAA,CAAA;;AAOnB,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAiB,YAAA,CAAA;EAV7B,MAAA;EACW,MAAA,CAAA;GAAA,UAAA;GAAqB,SAEtB,CAAC,UAAU,UAAU;GAAC,eAChB,kBAAkB;GAAI,MAC/B;IACJ,WAAW;IACX,oBAAoB;GACrB;GAAA,UAAA;EAAA,CAAA;CAsF2C,CAAA;CAAA,gBAAA;EAAA,aAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,WAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,WAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,MAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,SAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,UAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,UAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,SAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,YAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,aAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,OAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,GAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA,aAAA;EAAA,CAAA;EAAA,OAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA,OAAA;EAAA,CAAA;EAAA,MAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA,MAAA;EAAA,CAAA;EAAA,OAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA,GAAA,iBAAA,UAAU,GAAA,EAAA,UAAA,KAAA,CAAA;EAAA,CAAA;CAAA;AAAA,CAAA"}
@@ -37,6 +37,20 @@ var WrDialogHarness = class WrDialogHarness extends ContentContainerComponentHar
37
37
  async sendEscape() {
38
38
  await (await this.host()).sendKeys(TestKey.ESCAPE);
39
39
  }
40
+ async isConfirm() {
41
+ return await this.locatorForOptional(".wr-dialog-confirm")() !== null;
42
+ }
43
+ async confirm() {
44
+ await (await this.answer("confirm")).click();
45
+ }
46
+ async cancel() {
47
+ await (await this.answer("cancel")).click();
48
+ }
49
+ async answer(which) {
50
+ const button = await this.locatorForOptional(`.wr-dialog-confirm__action--${which}`)();
51
+ if (!button) throw new Error(`WrDialogHarness: ${which}() answers a WrDialog.confirm() dialog; this one was opened with open()`);
52
+ return button;
53
+ }
40
54
  async isFocusTrapped() {
41
55
  return (await this.host()).matchesSelector(":focus-within");
42
56
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ngwr-dialog-testing.mjs","names":[],"sources":["../../../projects/lib/dialog/testing/wr-dialog-harness.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ContentContainerComponentHarness, HarnessPredicate, TestKey } from '@angular/cdk/testing';\n\nimport type { WrDialogHarnessFilters } from './types';\n\n/**\n * Test harness for a dialog opened with `WrDialog.open()`.\n *\n * A dialog is never inside the fixture — `WrDialog` attaches it to the overlay\n * container — so load this from `TestbedHarnessEnvironment.documentRootLoader()`\n * rather than from `loader()`. The harness is a CONTENT CONTAINER, which is what\n * makes the interesting assertions possible: the buttons and fields it holds are\n * the consumer's own components, so `dialog.getHarness(WrButtonHarness…)` reads\n * them without the spec ever touching the overlay.\n *\n * @example\n * ```ts\n * const rootLoader = TestbedHarnessEnvironment.documentRootLoader(fixture);\n * const dialog = await rootLoader.getHarness(WrDialogHarness);\n *\n * expect(await dialog.getTitleText()).toBe('Delete this item?');\n * await (await dialog.getHarness(WrButtonHarness.with({ text: 'Delete' }))).click();\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrDialogHarness extends ContentContainerComponentHarness {\n /** The overlay panel `WrDialog` creates. */\n static hostSelector = '.wr-dialog-panel';\n\n /** Build a predicate that narrows the query — useful when two dialogs are stacked. */\n static with(options: WrDialogHarnessFilters = {}): HarnessPredicate<WrDialogHarness> {\n return new HarnessPredicate(WrDialogHarness, options).addOption('title', options.title, (harness, title) =>\n HarnessPredicate.stringMatches(harness.getTitleText(), title)\n );\n }\n\n /** The `[wrDialogTitle]` text, or `null` when the dialog has no title. */\n async getTitleText(): Promise<string | null> {\n const title = await this.locatorForOptional('.wr-dialog__title')();\n return title ? title.text() : null;\n }\n\n /** The `[wrDialogContent]` text, or `null` when the dialog projects no content region. */\n async getContentText(): Promise<string | null> {\n const content = await this.locatorForOptional('.wr-dialog__content')();\n return content ? content.text() : null;\n }\n\n /**\n * The role and modality the dialog announces.\n *\n * Worth asserting rather than assuming: `WrDialog` sets these on the OVERLAY\n * element, not on the component it renders, so a consumer looking for them on\n * their own host would not find them.\n */\n async getRole(): Promise<string | null> {\n return (await this.host()).getAttribute('role');\n }\n\n /** Whether the dialog is marked `aria-modal`. */\n async isModal(): Promise<boolean> {\n return (await (await this.host()).getAttribute('aria-modal')) === 'true';\n }\n\n /** Whether the built-in dismiss button is present (`closable`, the default). */\n async isClosable(): Promise<boolean> {\n return (await this.locatorForOptional('.wr-dialog__close')()) !== null;\n }\n\n /** The dismiss button's accessible name — the string a screen reader reads out. */\n async getCloseLabel(): Promise<string | null> {\n const close = await this.locatorForOptional('.wr-dialog__close')();\n return close ? close.getAttribute('aria-label') : null;\n }\n\n /** Click the built-in dismiss button. Throws when the dialog was opened `closable: false`. */\n async close(): Promise<void> {\n await (await this.locatorFor('.wr-dialog__close')()).click();\n }\n\n /** Press Escape. A dialog opened `closeOnEscape: false` ignores it — assert, do not assume. */\n async sendEscape(): Promise<void> {\n await (await this.host()).sendKeys(TestKey.ESCAPE);\n }\n\n /** Whether focus is currently somewhere inside the dialog, where the trap should keep it. */\n async isFocusTrapped(): Promise<boolean> {\n return (await this.host()).matchesSelector(':focus-within');\n }\n}\n"],"mappings":";;;;;;;AAgCA,IAAa,kBAAb,MAAa,wBAAwB,iCAAgC;CAEnE,OAAO,eAAe;CAGtB,OAAO,KAAK,UAAkC,CAAA,GAAE;EAC9C,OAAO,IAAI,iBAAiB,iBAAiB,OAAO,CAAC,CAAC,UAAU,SAAS,QAAQ,QAAQ,SAAS,UAChG,iBAAiB,cAAc,QAAQ,aAAY,GAAI,KAAK,CAAC;CAEjE;CAGA,MAAM,eAAY;EAChB,MAAM,QAAQ,MAAM,KAAK,mBAAmB,mBAAmB,CAAC,CAAA;EAChE,OAAO,QAAQ,MAAM,KAAI,IAAK;CAChC;CAGA,MAAM,iBAAc;EAClB,MAAM,UAAU,MAAM,KAAK,mBAAmB,qBAAqB,CAAC,CAAA;EACpE,OAAO,UAAU,QAAQ,KAAI,IAAK;CACpC;CASA,MAAM,UAAO;EACX,QAAQ,MAAM,KAAK,KAAI,EAAA,CAAI,aAAa,MAAM;CAChD;CAGA,MAAM,UAAO;EACX,OAAQ,OAAO,MAAM,KAAK,KAAI,EAAA,CAAI,aAAa,YAAY,MAAO;CACpE;CAGA,MAAM,aAAU;EACd,OAAQ,MAAM,KAAK,mBAAmB,mBAAmB,CAAC,CAAA,MAAQ;CACpE;CAGA,MAAM,gBAAa;EACjB,MAAM,QAAQ,MAAM,KAAK,mBAAmB,mBAAmB,CAAC,CAAA;EAChE,OAAO,QAAQ,MAAM,aAAa,YAAY,IAAI;CACpD;CAGA,MAAM,QAAK;EACT,OAAO,MAAM,KAAK,WAAW,mBAAmB,CAAC,CAAA,EAAA,CAAI,MAAK;CAC5D;CAGA,MAAM,aAAU;EACd,OAAO,MAAM,KAAK,KAAI,EAAA,CAAI,SAAS,QAAQ,MAAM;CACnD;CAGA,MAAM,iBAAc;EAClB,QAAQ,MAAM,KAAK,KAAI,EAAA,CAAI,gBAAgB,eAAe;CAC5D"}
1
+ {"version":3,"file":"ngwr-dialog-testing.mjs","names":[],"sources":["../../../projects/lib/dialog/testing/wr-dialog-harness.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ContentContainerComponentHarness, HarnessPredicate, type TestElement, TestKey } from '@angular/cdk/testing';\n\nimport type { WrDialogHarnessFilters } from './types';\n\n/**\n * Test harness for a dialog opened with `WrDialog.open()`.\n *\n * A dialog is never inside the fixture — `WrDialog` attaches it to the overlay\n * container — so load this from `TestbedHarnessEnvironment.documentRootLoader()`\n * rather than from `loader()`. The harness is a CONTENT CONTAINER, which is what\n * makes the interesting assertions possible: the buttons and fields it holds are\n * the consumer's own components, so `dialog.getHarness(WrButtonHarness…)` reads\n * them without the spec ever touching the overlay.\n *\n * @example\n * ```ts\n * const rootLoader = TestbedHarnessEnvironment.documentRootLoader(fixture);\n * const dialog = await rootLoader.getHarness(WrDialogHarness);\n *\n * expect(await dialog.getTitleText()).toBe('Delete this item?');\n * await (await dialog.getHarness(WrButtonHarness.with({ text: 'Delete' }))).click();\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrDialogHarness extends ContentContainerComponentHarness {\n /** The overlay panel `WrDialog` creates. */\n static hostSelector = '.wr-dialog-panel';\n\n /** Build a predicate that narrows the query — useful when two dialogs are stacked. */\n static with(options: WrDialogHarnessFilters = {}): HarnessPredicate<WrDialogHarness> {\n return new HarnessPredicate(WrDialogHarness, options).addOption('title', options.title, (harness, title) =>\n HarnessPredicate.stringMatches(harness.getTitleText(), title)\n );\n }\n\n /** The `[wrDialogTitle]` text, or `null` when the dialog has no title. */\n async getTitleText(): Promise<string | null> {\n const title = await this.locatorForOptional('.wr-dialog__title')();\n return title ? title.text() : null;\n }\n\n /** The `[wrDialogContent]` text, or `null` when the dialog projects no content region. */\n async getContentText(): Promise<string | null> {\n const content = await this.locatorForOptional('.wr-dialog__content')();\n return content ? content.text() : null;\n }\n\n /**\n * The role and modality the dialog announces.\n *\n * Worth asserting rather than assuming: `WrDialog` sets these on the OVERLAY\n * element, not on the component it renders, so a consumer looking for them on\n * their own host would not find them.\n */\n async getRole(): Promise<string | null> {\n return (await this.host()).getAttribute('role');\n }\n\n /** Whether the dialog is marked `aria-modal`. */\n async isModal(): Promise<boolean> {\n return (await (await this.host()).getAttribute('aria-modal')) === 'true';\n }\n\n /** Whether the built-in dismiss button is present (`closable`, the default). */\n async isClosable(): Promise<boolean> {\n return (await this.locatorForOptional('.wr-dialog__close')()) !== null;\n }\n\n /** The dismiss button's accessible name — the string a screen reader reads out. */\n async getCloseLabel(): Promise<string | null> {\n const close = await this.locatorForOptional('.wr-dialog__close')();\n return close ? close.getAttribute('aria-label') : null;\n }\n\n /** Click the built-in dismiss button. Throws when the dialog was opened `closable: false`. */\n async close(): Promise<void> {\n await (await this.locatorFor('.wr-dialog__close')()).click();\n }\n\n /** Press Escape. A dialog opened `closeOnEscape: false` ignores it — assert, do not assume. */\n async sendEscape(): Promise<void> {\n await (await this.host()).sendKeys(TestKey.ESCAPE);\n }\n\n /** Whether this is a `WrDialog.confirm()` dialog, the one `confirm()` and `cancel()` answer. */\n async isConfirm(): Promise<boolean> {\n return (await this.locatorForOptional('.wr-dialog-confirm')()) !== null;\n }\n\n /** Press the confirm button of a `WrDialog.confirm()` dialog. Throws on any other dialog. */\n async confirm(): Promise<void> {\n await (await this.answer('confirm')).click();\n }\n\n /** Press the cancel button of a `WrDialog.confirm()` dialog. Throws on any other dialog. */\n async cancel(): Promise<void> {\n await (await this.answer('cancel')).click();\n }\n\n private async answer(which: 'confirm' | 'cancel'): Promise<TestElement> {\n const button = await this.locatorForOptional(`.wr-dialog-confirm__action--${which}`)();\n if (!button) {\n throw new Error(\n `WrDialogHarness: ${which}() answers a WrDialog.confirm() dialog; this one was opened with open()`\n );\n }\n return button;\n }\n\n /** Whether focus is currently somewhere inside the dialog, where the trap should keep it. */\n async isFocusTrapped(): Promise<boolean> {\n return (await this.host()).matchesSelector(':focus-within');\n }\n}\n"],"mappings":";;;;;;;AAgCA,IAAa,kBAAb,MAAa,wBAAwB,iCAAgC;CAEnE,OAAO,eAAe;CAGtB,OAAO,KAAK,UAAkC,CAAA,GAAE;EAC9C,OAAO,IAAI,iBAAiB,iBAAiB,OAAO,CAAC,CAAC,UAAU,SAAS,QAAQ,QAAQ,SAAS,UAChG,iBAAiB,cAAc,QAAQ,aAAY,GAAI,KAAK,CAAC;CAEjE;CAGA,MAAM,eAAY;EAChB,MAAM,QAAQ,MAAM,KAAK,mBAAmB,mBAAmB,CAAC,CAAA;EAChE,OAAO,QAAQ,MAAM,KAAI,IAAK;CAChC;CAGA,MAAM,iBAAc;EAClB,MAAM,UAAU,MAAM,KAAK,mBAAmB,qBAAqB,CAAC,CAAA;EACpE,OAAO,UAAU,QAAQ,KAAI,IAAK;CACpC;CASA,MAAM,UAAO;EACX,QAAQ,MAAM,KAAK,KAAI,EAAA,CAAI,aAAa,MAAM;CAChD;CAGA,MAAM,UAAO;EACX,OAAQ,OAAO,MAAM,KAAK,KAAI,EAAA,CAAI,aAAa,YAAY,MAAO;CACpE;CAGA,MAAM,aAAU;EACd,OAAQ,MAAM,KAAK,mBAAmB,mBAAmB,CAAC,CAAA,MAAQ;CACpE;CAGA,MAAM,gBAAa;EACjB,MAAM,QAAQ,MAAM,KAAK,mBAAmB,mBAAmB,CAAC,CAAA;EAChE,OAAO,QAAQ,MAAM,aAAa,YAAY,IAAI;CACpD;CAGA,MAAM,QAAK;EACT,OAAO,MAAM,KAAK,WAAW,mBAAmB,CAAC,CAAA,EAAA,CAAI,MAAK;CAC5D;CAGA,MAAM,aAAU;EACd,OAAO,MAAM,KAAK,KAAI,EAAA,CAAI,SAAS,QAAQ,MAAM;CACnD;CAGA,MAAM,YAAS;EACb,OAAQ,MAAM,KAAK,mBAAmB,oBAAoB,CAAC,CAAA,MAAQ;CACrE;CAGA,MAAM,UAAO;EACX,OAAO,MAAM,KAAK,OAAO,SAAS,EAAA,CAAG,MAAK;CAC5C;CAGA,MAAM,SAAM;EACV,OAAO,MAAM,KAAK,OAAO,QAAQ,EAAA,CAAG,MAAK;CAC3C;CAEQ,MAAM,OAAO,OAA2B;EAC9C,MAAM,SAAS,MAAM,KAAK,mBAAmB,+BAA+B,OAAO,CAAC,CAAA;EACpF,IAAI,CAAC,QACH,MAAM,IAAI,MACR,oBAAoB,MAAK,wEAAyE;EAGtG,OAAO;CACT;CAGA,MAAM,iBAAc;EAClB,QAAQ,MAAM,KAAK,KAAI,EAAA,CAAI,gBAAgB,eAAe;CAC5D"}