@nanahoshi/mona-ui 0.23.0 → 0.23.1
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.
- package/fesm2022/nanahoshi-mona-ui-dropdown-list.mjs +1 -1
- package/fesm2022/{nanahoshi-mona-ui-grid-grid-footer-row.component--nF_Q7Bd.mjs → nanahoshi-mona-ui-grid-grid-footer-row.component-CIMXz60d.mjs} +2 -2
- package/fesm2022/{nanahoshi-mona-ui-grid-grid-list.component-S2qxwP2M.mjs → nanahoshi-mona-ui-grid-grid-list.component-CpeheGXU.mjs} +4 -4
- package/fesm2022/{nanahoshi-mona-ui-grid-grid-row.component-Dwy8hCm7.mjs → nanahoshi-mona-ui-grid-grid-row.component-B_5ay_Nz.mjs} +2 -2
- package/fesm2022/{nanahoshi-mona-ui-grid-grid-virtual-list.component-DArxdAah.mjs → nanahoshi-mona-ui-grid-grid-virtual-list.component-BzKywU5b.mjs} +4 -4
- package/fesm2022/{nanahoshi-mona-ui-grid-nanahoshi-mona-ui-grid-bduDEm-x.mjs → nanahoshi-mona-ui-grid-nanahoshi-mona-ui-grid-IewqeT4m.mjs} +8 -8
- package/fesm2022/nanahoshi-mona-ui-grid.mjs +1 -1
- package/fesm2022/nanahoshi-mona-ui-internal-filter-input.mjs +47 -7
- package/fesm2022/nanahoshi-mona-ui-internal-list.mjs +386 -180
- package/fesm2022/nanahoshi-mona-ui-internal-tree.mjs +266 -99
- package/fesm2022/nanahoshi-mona-ui-internal.mjs +35 -32
- package/fesm2022/nanahoshi-mona-ui-locales.mjs +13 -0
- package/fesm2022/nanahoshi-mona-ui-popup-menu.mjs +1 -1
- package/fesm2022/nanahoshi-mona-ui-tree-view.mjs +1 -1
- package/package.json +1 -1
- package/types/nanahoshi-mona-ui-i18n.d.ts +1 -0
- package/types/nanahoshi-mona-ui-internal-filter-input.d.ts +35 -1
- package/types/nanahoshi-mona-ui-internal-list.d.ts +82 -16
- package/types/nanahoshi-mona-ui-internal-tree.d.ts +50 -3
- package/types/nanahoshi-mona-ui-internal.d.ts +18 -4
- package/types/nanahoshi-mona-ui-locales.d.ts +13 -0
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { inject, ElementRef, Renderer2, input, effect, Directive } from '@angular/core';
|
|
3
|
-
import { forEach
|
|
3
|
+
import { forEach } from '@mirei/ts-collections';
|
|
4
4
|
import { Observable, timer, take, debounceTime, map, merge, scan, filter } from 'rxjs';
|
|
5
5
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
6
|
-
import { isIterable } from 'rxjs/internal/util/isIterable';
|
|
7
6
|
|
|
8
7
|
class AttributeBinderDirective {
|
|
9
8
|
#element;
|
|
@@ -111,23 +110,16 @@ const hsva2rgba = (hue, saturation, value, alpha) => {
|
|
|
111
110
|
};
|
|
112
111
|
};
|
|
113
112
|
|
|
113
|
+
const toHexByte = (value) => {
|
|
114
|
+
const byte = Math.min(255, Math.max(0, Math.round(value)));
|
|
115
|
+
return byte.toString(16).padStart(2, "0");
|
|
116
|
+
};
|
|
114
117
|
const rgba2hex = (r, g, b, a) => {
|
|
115
118
|
if (r == null || g == null || b == null || a == null) {
|
|
116
119
|
return "";
|
|
117
120
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
let bHex = b.toString(16);
|
|
121
|
-
let aHex = a.toString(16);
|
|
122
|
-
if (rHex.length == 1)
|
|
123
|
-
rHex = "0" + rHex;
|
|
124
|
-
if (gHex.length == 1)
|
|
125
|
-
gHex = "0" + gHex;
|
|
126
|
-
if (bHex.length == 1)
|
|
127
|
-
bHex = "0" + bHex;
|
|
128
|
-
if (aHex.length == 1)
|
|
129
|
-
aHex = "0" + aHex;
|
|
130
|
-
return `#${rHex}${gHex}${bHex}${aHex.toUpperCase() !== "FF" ? aHex : ""}`;
|
|
121
|
+
const aHex = toHexByte(a);
|
|
122
|
+
return `#${toHexByte(r)}${toHexByte(g)}${toHexByte(b)}${aHex !== "ff" ? aHex : ""}`;
|
|
131
123
|
};
|
|
132
124
|
|
|
133
125
|
const rgba2hsla = (red, green, blue, alpha) => {
|
|
@@ -179,10 +171,9 @@ const rgba2hsva = (r, g, b, a) => {
|
|
|
179
171
|
const max = Math.max(r, g, b);
|
|
180
172
|
const min = Math.min(r, g, b);
|
|
181
173
|
let h = 0;
|
|
182
|
-
let s;
|
|
183
174
|
const v = max;
|
|
184
175
|
const d = max - min;
|
|
185
|
-
s = max === 0 ? 0 : d / max;
|
|
176
|
+
const s = max === 0 ? 0 : d / max;
|
|
186
177
|
if (max !== min) {
|
|
187
178
|
if (max === r) {
|
|
188
179
|
h = (g - b) / d + (g < b ? 6 : 0);
|
|
@@ -231,7 +222,8 @@ const string2rgba = (rgbText) => {
|
|
|
231
222
|
|
|
232
223
|
/**
|
|
233
224
|
* Generates a unique control ID for an element.
|
|
234
|
-
* The ID consists of
|
|
225
|
+
* The ID consists of the `mona-` prefix followed by a random hexadecimal string.
|
|
226
|
+
* It contains only characters that are safe in CSS selectors, so it can be used with `querySelector("#…")` unescaped.
|
|
235
227
|
* @param {number} [level=0] - The nesting level of the element. This increases the length of the generated ID.
|
|
236
228
|
* @param {number} [length=7] - The base length of the random part of the ID.
|
|
237
229
|
* @returns {string} A unique control ID string for use for elements.
|
|
@@ -245,14 +237,14 @@ const createElementControlId = (level = 0, length = 7) => {
|
|
|
245
237
|
const randomId = Array.from(array, byte => byte.toString(16).padStart(2, "0"))
|
|
246
238
|
.join("")
|
|
247
239
|
.substring(0, idLength);
|
|
248
|
-
return `${prefix}
|
|
240
|
+
return `${prefix}${randomId}`;
|
|
249
241
|
}
|
|
250
242
|
let hex = "";
|
|
251
243
|
const chars = "0123456789abcdef";
|
|
252
244
|
for (let i = 0; i < idLength; i++) {
|
|
253
245
|
hex += chars[Math.floor(Math.random() * 16)];
|
|
254
246
|
}
|
|
255
|
-
return `${prefix}
|
|
247
|
+
return `${prefix}${hex}`;
|
|
256
248
|
};
|
|
257
249
|
|
|
258
250
|
function focusElement(host, element) {
|
|
@@ -272,7 +264,7 @@ function focusElement(host, element) {
|
|
|
272
264
|
|
|
273
265
|
const rxFromResize = (target, options) => {
|
|
274
266
|
return new Observable(observer => {
|
|
275
|
-
const elements =
|
|
267
|
+
const elements = target instanceof Element ? [target] : Array.from(target ?? []);
|
|
276
268
|
for (const element of elements) {
|
|
277
269
|
if (!(element instanceof Element)) {
|
|
278
270
|
observer.error(new TypeError("target must be an Element or an iterable of Elements"));
|
|
@@ -348,18 +340,22 @@ const findScrollableElement = (container) => {
|
|
|
348
340
|
return null;
|
|
349
341
|
};
|
|
350
342
|
|
|
343
|
+
const toPixels = (value) => (value != null ? `${value}px` : "");
|
|
351
344
|
const setWindowStyles = (element, settings) => {
|
|
352
345
|
if (!element) {
|
|
353
346
|
return;
|
|
354
347
|
}
|
|
355
|
-
const classes =
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
const
|
|
361
|
-
const
|
|
362
|
-
const
|
|
348
|
+
const classes = typeof settings.windowClass === "string" ? [settings.windowClass] : (settings.windowClass ?? []);
|
|
349
|
+
classes
|
|
350
|
+
.flatMap(className => (className ?? "").split(/\s+/))
|
|
351
|
+
.filter(className => className !== "")
|
|
352
|
+
.forEach(className => element.classList.add(className));
|
|
353
|
+
const minWidth = toPixels(settings.minWidth);
|
|
354
|
+
const minHeight = toPixels(settings.minHeight);
|
|
355
|
+
const maxWidth = toPixels(settings.maxWidth);
|
|
356
|
+
const maxHeight = toPixels(settings.maxHeight);
|
|
357
|
+
const top = settings.top != null ? `${settings.top}px` : `calc(50% - ${element.getBoundingClientRect().height / 2}px)`;
|
|
358
|
+
const left = settings.left != null ? `${settings.left}px` : `calc(50% - ${element.getBoundingClientRect().width / 2}px)`;
|
|
363
359
|
const position = "absolute";
|
|
364
360
|
Object.assign(element.style, { left, maxHeight, maxWidth, minHeight, minWidth, position, top });
|
|
365
361
|
};
|
|
@@ -374,7 +370,7 @@ const setWindowStyles = (element, settings) => {
|
|
|
374
370
|
* @returns A CSS-compliant string value.
|
|
375
371
|
*/
|
|
376
372
|
const toCssValue = (value) => {
|
|
377
|
-
if (value == null || value === "") {
|
|
373
|
+
if (value == null || (typeof value === "string" && value.trim() === "")) {
|
|
378
374
|
return "";
|
|
379
375
|
}
|
|
380
376
|
if (typeof value === "number" || isFinite(Number(value))) {
|
|
@@ -402,8 +398,15 @@ const isNavigationKey = (key) => {
|
|
|
402
398
|
return NavigationKeysList.includes(key);
|
|
403
399
|
};
|
|
404
400
|
|
|
405
|
-
|
|
406
|
-
|
|
401
|
+
/**
|
|
402
|
+
* Returns whether a `KeyboardEvent.key` value should feed typeahead.
|
|
403
|
+
* Any single letter or digit in any script counts, plus space, `-` and `_`.
|
|
404
|
+
*/
|
|
405
|
+
const isTypeaheadKey = (key, modifiers) => {
|
|
406
|
+
if (modifiers?.ctrlKey || modifiers?.metaKey || modifiers?.altKey) {
|
|
407
|
+
return false;
|
|
408
|
+
}
|
|
409
|
+
return /^[\p{L}\p{M}\p{N}\-_\s]$/u.test(key);
|
|
407
410
|
};
|
|
408
411
|
const setupTypeahead = (typeaheadKey$) => {
|
|
409
412
|
const resetTrigger$ = typeaheadKey$.pipe(debounceTime(200), map(() => ({ type: "reset", value: "" })));
|
|
@@ -260,6 +260,7 @@ const AR_SA_MESSAGES = {
|
|
|
260
260
|
selectRow: (rowNumber) => `تحديد الصف ${formatInteger(rowNumber)}`
|
|
261
261
|
},
|
|
262
262
|
list: {
|
|
263
|
+
filterList: "تصفية القائمة",
|
|
263
264
|
noData: "لا توجد بيانات"
|
|
264
265
|
},
|
|
265
266
|
listBox: {
|
|
@@ -639,6 +640,7 @@ const ES_ES_MESSAGES = {
|
|
|
639
640
|
selectRow: (rowNumber) => `Seleccionar fila ${rowNumber}`
|
|
640
641
|
},
|
|
641
642
|
list: {
|
|
643
|
+
filterList: "Filtrar lista",
|
|
642
644
|
noData: "No hay datos"
|
|
643
645
|
},
|
|
644
646
|
listBox: {
|
|
@@ -1018,6 +1020,7 @@ const DE_DE_MESSAGES = {
|
|
|
1018
1020
|
selectRow: (rowNumber) => `Zeile ${rowNumber} auswählen`
|
|
1019
1021
|
},
|
|
1020
1022
|
list: {
|
|
1023
|
+
filterList: "Liste filtern",
|
|
1021
1024
|
noData: "Keine Daten"
|
|
1022
1025
|
},
|
|
1023
1026
|
listBox: {
|
|
@@ -1397,6 +1400,7 @@ const FR_FR_MESSAGES = {
|
|
|
1397
1400
|
selectRow: (rowNumber) => `Sélectionner la ligne ${rowNumber}`
|
|
1398
1401
|
},
|
|
1399
1402
|
list: {
|
|
1403
|
+
filterList: "Filtrer la liste",
|
|
1400
1404
|
noData: "Aucune donnée"
|
|
1401
1405
|
},
|
|
1402
1406
|
listBox: {
|
|
@@ -1776,6 +1780,7 @@ const IT_IT_MESSAGES = {
|
|
|
1776
1780
|
selectRow: (rowNumber) => `Seleziona riga ${rowNumber}`
|
|
1777
1781
|
},
|
|
1778
1782
|
list: {
|
|
1783
|
+
filterList: "Filtra elenco",
|
|
1779
1784
|
noData: "Nessun dato disponibile"
|
|
1780
1785
|
},
|
|
1781
1786
|
listBox: {
|
|
@@ -2155,6 +2160,7 @@ const JA_JP_MESSAGES = {
|
|
|
2155
2160
|
selectRow: (rowNumber) => `${rowNumber}行目を選択`
|
|
2156
2161
|
},
|
|
2157
2162
|
list: {
|
|
2163
|
+
filterList: "リストをフィルター",
|
|
2158
2164
|
noData: "データがありません"
|
|
2159
2165
|
},
|
|
2160
2166
|
listBox: {
|
|
@@ -2534,6 +2540,7 @@ const KO_KR_MESSAGES = {
|
|
|
2534
2540
|
selectRow: (rowNumber) => `${rowNumber}행 선택`
|
|
2535
2541
|
},
|
|
2536
2542
|
list: {
|
|
2543
|
+
filterList: "목록 필터",
|
|
2537
2544
|
noData: "데이터 없음"
|
|
2538
2545
|
},
|
|
2539
2546
|
listBox: {
|
|
@@ -2913,6 +2920,7 @@ const PT_BR_MESSAGES = {
|
|
|
2913
2920
|
selectRow: (rowNumber) => `Selecionar linha ${rowNumber}`
|
|
2914
2921
|
},
|
|
2915
2922
|
list: {
|
|
2923
|
+
filterList: "Filtrar lista",
|
|
2916
2924
|
noData: "Nenhum dado disponível"
|
|
2917
2925
|
},
|
|
2918
2926
|
listBox: {
|
|
@@ -3292,6 +3300,7 @@ const ZH_CN_MESSAGES = {
|
|
|
3292
3300
|
selectRow: (rowNumber) => `选择第 ${rowNumber} 行`
|
|
3293
3301
|
},
|
|
3294
3302
|
list: {
|
|
3303
|
+
filterList: "筛选列表",
|
|
3295
3304
|
noData: "没有数据"
|
|
3296
3305
|
},
|
|
3297
3306
|
listBox: {
|
|
@@ -3671,6 +3680,7 @@ const ZH_TW_MESSAGES = {
|
|
|
3671
3680
|
selectRow: (rowNumber) => `選取第 ${rowNumber} 個資料列`
|
|
3672
3681
|
},
|
|
3673
3682
|
list: {
|
|
3683
|
+
filterList: "篩選清單",
|
|
3674
3684
|
noData: "沒有資料"
|
|
3675
3685
|
},
|
|
3676
3686
|
listBox: {
|
|
@@ -4050,6 +4060,7 @@ const TR_TR_MESSAGES = {
|
|
|
4050
4060
|
selectRow: (rowNumber) => `${rowNumber}. satırı seç`
|
|
4051
4061
|
},
|
|
4052
4062
|
list: {
|
|
4063
|
+
filterList: "Listeyi filtrele",
|
|
4053
4064
|
noData: "Veri yok"
|
|
4054
4065
|
},
|
|
4055
4066
|
listBox: {
|
|
@@ -4429,6 +4440,7 @@ const ID_ID_MESSAGES = {
|
|
|
4429
4440
|
selectRow: (rowNumber) => `Pilih baris ${rowNumber}`
|
|
4430
4441
|
},
|
|
4431
4442
|
list: {
|
|
4443
|
+
filterList: "Saring daftar",
|
|
4432
4444
|
noData: "Tidak ada data"
|
|
4433
4445
|
},
|
|
4434
4446
|
listBox: {
|
|
@@ -4837,6 +4849,7 @@ const RU_RU_MESSAGES = {
|
|
|
4837
4849
|
selectRow: (rowNumber) => `Выбрать строку ${rowNumber}`
|
|
4838
4850
|
},
|
|
4839
4851
|
list: {
|
|
4852
|
+
filterList: "Фильтр списка",
|
|
4840
4853
|
noData: "Нет данных"
|
|
4841
4854
|
},
|
|
4842
4855
|
listBox: {
|
|
@@ -196,7 +196,7 @@ class TreeViewComponent {
|
|
|
196
196
|
this.treeService.blur$.pipe(takeUntilDestroyed(this.#destroyRef)).subscribe(e => this.blur.emit(e));
|
|
197
197
|
}
|
|
198
198
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: TreeViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
199
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.6", type: TreeViewComponent, isStandalone: true, selector: "mona-tree-view", inputs: { animate: { classPropertyName: "animate", publicName: "animate", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, children: { classPropertyName: "children", publicName: "children", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, hasChildren: { classPropertyName: "hasChildren", publicName: "hasChildren", isSignal: true, isRequired: false, transformFunction: null }, idField: { classPropertyName: "idField", publicName: "idField", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, parentIdField: { classPropertyName: "parentIdField", publicName: "parentIdField", isSignal: true, isRequired: false, transformFunction: null }, textField: { classPropertyName: "textField", publicName: "textField", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { blur: "blur", focus: "focus", nodeClick: "nodeClick" }, providers: [TreeService], queries: [{ propertyName: "nodeTemplate", first: true, predicate: TreeViewNodeTemplateDirective, descendants: true, read: TemplateRef, isSignal: true }], ngImport: i0, template: "@if (treeService.filterableOptions().enabled) {\n <div role=\"search\" [attr.aria-label]=\"filterAriaLabel()\">\n <mona-filter-input\n [filter]=\"treeService.filterText()\"\n [debounce]=\"treeService.filterableOptions().debounce\"\n [placeholder]=\"treeService.filterPlaceholder()\"\n (filterChange)=\"onFilterChange($event)\"></mona-filter-input>\n </div>\n}\n<div class=\"flex-1 overflow-auto\">\n <mona-tree [ariaLabel]=\"ariaLabel()\">\n @if (nodeTemplate()) {\n <ng-template monaTreeNodeTemplate let-dataItem let-element=\"element\">\n <ng-container\n [ngTemplateOutlet]=\"nodeTemplate() ?? null\"\n [ngTemplateOutletContext]=\"{ $implicit: dataItem, element }\"></ng-container>\n </ng-template>\n }\n </mona-tree>\n</div>\n", dependencies: [{ kind: "component", type: FilterInputComponent, selector: "mona-filter-input", inputs: ["debounce", "filter", "placeholder", "size"], outputs: ["filterChange", "inputBlur", "inputFocus"] }, { kind: "component", type: TreeComponent, selector: "mona-tree", inputs: ["ariaLabel", "data"] }, { kind: "directive", type: TreeNodeTemplateDirective, selector: "ng-template[monaTreeNodeTemplate]" }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] }); }
|
|
199
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.6", type: TreeViewComponent, isStandalone: true, selector: "mona-tree-view", inputs: { animate: { classPropertyName: "animate", publicName: "animate", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, children: { classPropertyName: "children", publicName: "children", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, hasChildren: { classPropertyName: "hasChildren", publicName: "hasChildren", isSignal: true, isRequired: false, transformFunction: null }, idField: { classPropertyName: "idField", publicName: "idField", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, parentIdField: { classPropertyName: "parentIdField", publicName: "parentIdField", isSignal: true, isRequired: false, transformFunction: null }, textField: { classPropertyName: "textField", publicName: "textField", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { blur: "blur", focus: "focus", nodeClick: "nodeClick" }, providers: [TreeService], queries: [{ propertyName: "nodeTemplate", first: true, predicate: TreeViewNodeTemplateDirective, descendants: true, read: TemplateRef, isSignal: true }], ngImport: i0, template: "@if (treeService.filterableOptions().enabled) {\n <div role=\"search\" [attr.aria-label]=\"filterAriaLabel()\">\n <mona-filter-input\n [filter]=\"treeService.filterText()\"\n [debounce]=\"treeService.filterableOptions().debounce\"\n [placeholder]=\"treeService.filterPlaceholder()\"\n (filterChange)=\"onFilterChange($event)\"></mona-filter-input>\n </div>\n}\n<div class=\"flex-1 overflow-auto\">\n <mona-tree [ariaLabel]=\"ariaLabel()\">\n @if (nodeTemplate()) {\n <ng-template monaTreeNodeTemplate let-dataItem let-element=\"element\">\n <ng-container\n [ngTemplateOutlet]=\"nodeTemplate() ?? null\"\n [ngTemplateOutletContext]=\"{ $implicit: dataItem, element }\"></ng-container>\n </ng-template>\n }\n </mona-tree>\n</div>\n", dependencies: [{ kind: "component", type: FilterInputComponent, selector: "mona-filter-input", inputs: ["activeDescendant", "ariaLabel", "controls", "debounce", "filter", "placeholder", "size"], outputs: ["filterChange", "inputBlur", "inputFocus"] }, { kind: "component", type: TreeComponent, selector: "mona-tree", inputs: ["ariaLabel", "data"] }, { kind: "directive", type: TreeNodeTemplateDirective, selector: "ng-template[monaTreeNodeTemplate]" }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] }); }
|
|
200
200
|
}
|
|
201
201
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: TreeViewComponent, decorators: [{
|
|
202
202
|
type: Component,
|
package/package.json
CHANGED
|
@@ -7,6 +7,40 @@ declare class FilterInputComponent implements OnInit {
|
|
|
7
7
|
#private;
|
|
8
8
|
protected readonly filter$: Subject<string>;
|
|
9
9
|
protected readonly filterText: _angular_core.WritableSignal<string>;
|
|
10
|
+
/**
|
|
11
|
+
* When the input controls a list, it acts as a combobox (WAI-ARIA APG): focus stays in the input
|
|
12
|
+
* and the highlighted option is exposed through `aria-activedescendant`.
|
|
13
|
+
*/
|
|
14
|
+
protected readonly inputAttributes: _angular_core.Signal<{
|
|
15
|
+
"aria-label": string | null;
|
|
16
|
+
role?: undefined;
|
|
17
|
+
"aria-activedescendant"?: undefined;
|
|
18
|
+
"aria-autocomplete"?: undefined;
|
|
19
|
+
"aria-controls"?: undefined;
|
|
20
|
+
"aria-expanded"?: undefined;
|
|
21
|
+
} | {
|
|
22
|
+
role: string;
|
|
23
|
+
"aria-label": string | null;
|
|
24
|
+
"aria-activedescendant": string | null;
|
|
25
|
+
"aria-autocomplete": string;
|
|
26
|
+
"aria-controls": string;
|
|
27
|
+
"aria-expanded": boolean;
|
|
28
|
+
}>;
|
|
29
|
+
/**
|
|
30
|
+
* @description Id of the highlighted option in the controlled list, exposed as `aria-activedescendant`.
|
|
31
|
+
* @default ""
|
|
32
|
+
*/
|
|
33
|
+
readonly activeDescendant: _angular_core.InputSignal<string | null>;
|
|
34
|
+
/**
|
|
35
|
+
* @description Accessible name of the input. The placeholder is not a reliable label, so set this whenever possible.
|
|
36
|
+
* @default ""
|
|
37
|
+
*/
|
|
38
|
+
readonly ariaLabel: _angular_core.InputSignal<string>;
|
|
39
|
+
/**
|
|
40
|
+
* @description Id of the list this input filters. When set, the input is exposed as a combobox controlling it.
|
|
41
|
+
* @default ""
|
|
42
|
+
*/
|
|
43
|
+
readonly controls: _angular_core.InputSignal<string>;
|
|
10
44
|
readonly debounce: _angular_core.InputSignal<number>;
|
|
11
45
|
readonly filter: _angular_core.InputSignal<string>;
|
|
12
46
|
readonly filterChange: _angular_core.OutputEmitterRef<FilterChangeEvent>;
|
|
@@ -19,7 +53,7 @@ declare class FilterInputComponent implements OnInit {
|
|
|
19
53
|
ngOnInit(): void;
|
|
20
54
|
private setSubscriptions;
|
|
21
55
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<FilterInputComponent, never>;
|
|
22
|
-
static ɵcmp: _angular_core.ɵɵComponentDeclaration<FilterInputComponent, "mona-filter-input", never, { "debounce": { "alias": "debounce"; "required": false; "isSignal": true; }; "filter": { "alias": "filter"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; }, { "filterChange": "filterChange"; "inputBlur": "inputBlur"; "inputFocus": "inputFocus"; }, never, never, true, never>;
|
|
56
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<FilterInputComponent, "mona-filter-input", never, { "activeDescendant": { "alias": "activeDescendant"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "controls": { "alias": "controls"; "required": false; "isSignal": true; }; "debounce": { "alias": "debounce"; "required": false; "isSignal": true; }; "filter": { "alias": "filter"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; }, { "filterChange": "filterChange"; "inputBlur": "inputBlur"; "inputFocus": "inputFocus"; }, never, never, true, never>;
|
|
23
57
|
}
|
|
24
58
|
|
|
25
59
|
export { FilterInputComponent };
|
|
@@ -10,10 +10,21 @@ import { Subject, ReplaySubject } from 'rxjs';
|
|
|
10
10
|
interface ListItemOptions<T> {
|
|
11
11
|
data: T;
|
|
12
12
|
header?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Marks the item as a group header. Defaults to `true` when a non-empty `header` is given.
|
|
15
|
+
* Set it explicitly for groups whose key is empty, which still need a header row.
|
|
16
|
+
*/
|
|
17
|
+
isHeader?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Reuses an existing identifier, so an item rebuilt for refreshed data keeps its DOM id and tracking.
|
|
20
|
+
* A new UUID is generated when omitted.
|
|
21
|
+
*/
|
|
22
|
+
uid?: string;
|
|
13
23
|
}
|
|
14
24
|
declare class ListItem<TData> {
|
|
15
25
|
readonly data: TData;
|
|
16
26
|
readonly header: string;
|
|
27
|
+
readonly isHeader: boolean;
|
|
17
28
|
readonly uid: string;
|
|
18
29
|
constructor(options: ListItemOptions<TData>);
|
|
19
30
|
}
|
|
@@ -108,11 +119,27 @@ declare class ListService<TData> {
|
|
|
108
119
|
readonly focus$: Subject<void>;
|
|
109
120
|
readonly focusableItem: _angular_core.Signal<ListItem<TData> | null>;
|
|
110
121
|
readonly groupBy: _angular_core.WritableSignal<ListKeySelector<TData>>;
|
|
122
|
+
/**
|
|
123
|
+
* Maps each option uid to the uid of the group header it is listed under.
|
|
124
|
+
*/
|
|
125
|
+
readonly groupHeaderUids: _angular_core.Signal<Map<string, string>>;
|
|
111
126
|
readonly groupableOptions: _angular_core.WritableSignal<GroupableOptions<TData, any>>;
|
|
127
|
+
/**
|
|
128
|
+
* The highlighted (active) item. When the data changes, it is re-resolved to the item with the same `uid`,
|
|
129
|
+
* or cleared when that item is gone.
|
|
130
|
+
*/
|
|
112
131
|
readonly highlightedItem: _angular_core.WritableSignal<ListItem<TData> | null>;
|
|
113
132
|
readonly itemFocusOut$: Subject<ListItem<TData>>;
|
|
114
|
-
|
|
133
|
+
/**
|
|
134
|
+
* Position of each option among all filtered options (not only the current page), for `aria-posinset`
|
|
135
|
+
* and `aria-setsize`.
|
|
136
|
+
*/
|
|
137
|
+
readonly itemPositions: _angular_core.Signal<Map<string, {
|
|
138
|
+
position: number;
|
|
139
|
+
total: number;
|
|
140
|
+
}>>;
|
|
115
141
|
readonly navigableOptions: _angular_core.WritableSignal<NavigableOptions>;
|
|
142
|
+
readonly navigatedItem$: Subject<ListItem<TData> | null>;
|
|
116
143
|
readonly pageState: _angular_core.WritableSignal<PageState>;
|
|
117
144
|
readonly pageableOptions: _angular_core.WritableSignal<PagerSettings>;
|
|
118
145
|
readonly scrollToItem$: ReplaySubject<{
|
|
@@ -126,16 +153,20 @@ declare class ListService<TData> {
|
|
|
126
153
|
readonly selectionChange$: Subject<ListItem<TData>>;
|
|
127
154
|
readonly textField: _angular_core.WritableSignal<string | Selector<TData, string>>;
|
|
128
155
|
readonly valueField: _angular_core.WritableSignal<string | Selector<TData, unknown>>;
|
|
156
|
+
/**
|
|
157
|
+
* Visible items in display order: filtered, ordered and grouped (with header rows), but not paged.
|
|
158
|
+
*/
|
|
129
159
|
readonly viewItems: _angular_core.Signal<ImmutableSet<ListItem<TData>>>;
|
|
130
160
|
readonly virtualScrollOptions: _angular_core.WritableSignal<VirtualScrollOptions>;
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
161
|
+
clearFilter(): void;
|
|
162
|
+
clearHighlightedItem(): void;
|
|
163
|
+
clearSelections(): void;
|
|
164
|
+
/**
|
|
165
|
+
* Finds the next item whose text starts with the typeahead buffer, starting after the active item.
|
|
166
|
+
* Group headers and disabled items are never matched.
|
|
167
|
+
*/
|
|
168
|
+
cycleThroughMatchedItems(buffer: string): ListItem<TData> | null;
|
|
169
|
+
deselectItems(item: Iterable<ListItem<TData>>): void;
|
|
139
170
|
/**
|
|
140
171
|
* Resolves the selection value of a data item through `valueField`.
|
|
141
172
|
*
|
|
@@ -146,14 +177,25 @@ declare class ListService<TData> {
|
|
|
146
177
|
* Falsy results such as `0`, `false`, and `""` are valid values and do not fall back to the item.
|
|
147
178
|
*/
|
|
148
179
|
getDataItemValue(dataItem: TData): unknown;
|
|
149
|
-
clearFilter(): void;
|
|
150
|
-
clearHighlightedItem(): void;
|
|
151
|
-
clearSelections(): void;
|
|
152
|
-
cycleThroughMatchedItems(buffer: string): ListItem<TData> | null;
|
|
153
|
-
deselectItems(item: Iterable<ListItem<TData>>): void;
|
|
154
180
|
getFilterDebounceDuration(): number;
|
|
155
181
|
getGroupField(item: ListItem<TData>): any;
|
|
182
|
+
/**
|
|
183
|
+
* Returns the uid of the group header an item is rendered under, or `null` when the list is not grouped.
|
|
184
|
+
*/
|
|
185
|
+
getGroupHeaderUid(item: ListItem<TData>): string | null;
|
|
186
|
+
getItemPosition(item: ListItem<TData>): {
|
|
187
|
+
position: number;
|
|
188
|
+
total: number;
|
|
189
|
+
} | null;
|
|
190
|
+
/**
|
|
191
|
+
* Resolves the display text of an item through `textField`.
|
|
192
|
+
* Non-string field values (numbers, dates, …) are converted with `String()`, and nullish values become `""`.
|
|
193
|
+
*/
|
|
156
194
|
getItemText(item: ListItem<TData>): string;
|
|
195
|
+
/**
|
|
196
|
+
* Finds the item that typed text should resolve to: an exact text match first (respecting `caseSensitive`),
|
|
197
|
+
* otherwise the first item accepted by the filter operator. Group headers and disabled items are skipped.
|
|
198
|
+
*/
|
|
157
199
|
getMatchingFilteredItem(text: string): ListItem<TData> | null;
|
|
158
200
|
isDisabled(item: ListItem<TData>): boolean;
|
|
159
201
|
isFilteringEnabled(): boolean;
|
|
@@ -162,6 +204,11 @@ declare class ListService<TData> {
|
|
|
162
204
|
navigate(direction: NavigationDirection, mode: NavigationMode, focus: boolean): ListItem<TData> | null;
|
|
163
205
|
notifySelectionChange(item: ListItem<TData>): void;
|
|
164
206
|
scrollToItem(item: ListItem<TData>, focus: boolean): void;
|
|
207
|
+
/**
|
|
208
|
+
* Selects every enabled item currently in view, keeping existing selections. Only applies in multiple mode.
|
|
209
|
+
* @returns whether the selection changed.
|
|
210
|
+
*/
|
|
211
|
+
selectAllItems(): boolean;
|
|
165
212
|
selectItem(item: ListItem<TData>, scroll?: boolean, focus?: boolean): void;
|
|
166
213
|
setData(data: Iterable<TData>): void;
|
|
167
214
|
setDisabledBy(disabledBy: string | Predicate<TData>): void;
|
|
@@ -179,11 +226,16 @@ declare class ListService<TData> {
|
|
|
179
226
|
setValueField(valueField: string | Selector<TData, unknown>): void;
|
|
180
227
|
setVirtualScrollOptions(options: VirtualScrollOptions): void;
|
|
181
228
|
private filterItem;
|
|
229
|
+
/**
|
|
230
|
+
* Returns the item when it is one of the navigable items, otherwise `null`.
|
|
231
|
+
* An anchor that was filtered out or removed must not be navigated from.
|
|
232
|
+
*/
|
|
233
|
+
private getItemInView;
|
|
182
234
|
private getOrderKey;
|
|
183
235
|
private getPreviousItemForNavigation;
|
|
184
236
|
private getSelectionKey;
|
|
185
|
-
private navigateForMultipleSelection;
|
|
186
237
|
private navigateFirstForSingleSelection;
|
|
238
|
+
private navigateForMultipleSelection;
|
|
187
239
|
private navigateForSingleSelection;
|
|
188
240
|
private navigateLastForSingleSelection;
|
|
189
241
|
private navigateNextForSingleSelection;
|
|
@@ -196,7 +248,15 @@ declare class ListService<TData> {
|
|
|
196
248
|
declare class ListComponent<TData> implements OnInit {
|
|
197
249
|
#private;
|
|
198
250
|
private readonly filterInput;
|
|
251
|
+
/**
|
|
252
|
+
* Id of the highlighted option, used as `aria-activedescendant` by the filter input.
|
|
253
|
+
*/
|
|
254
|
+
protected readonly activeDescendant: Signal<string | null>;
|
|
199
255
|
protected readonly classes: Signal<string>;
|
|
256
|
+
/**
|
|
257
|
+
* The `listId` input, or a generated id so the listbox can always be referenced by `aria-controls`.
|
|
258
|
+
*/
|
|
259
|
+
protected readonly effectiveListId: Signal<string>;
|
|
200
260
|
protected readonly footerTemplate: Signal<TemplateRef<any> | undefined>;
|
|
201
261
|
protected readonly groupHeaderClasses: Signal<string>;
|
|
202
262
|
protected readonly groupHeaderTemplate: Signal<TemplateRef<any> | undefined>;
|
|
@@ -235,10 +295,16 @@ declare class ListComponent<TData> implements OnInit {
|
|
|
235
295
|
constructor();
|
|
236
296
|
ngOnInit(): void;
|
|
237
297
|
protected onFilterChange(event: FilterChangeEvent): void;
|
|
238
|
-
protected onListItemCheck(item: ListItem<TData>): void;
|
|
239
298
|
protected onListItemClick(item: ListItem<TData>): void;
|
|
299
|
+
protected trackByUid(_: number, item: ListItem<TData>): string;
|
|
240
300
|
private cycleThroughMatchedItems;
|
|
241
301
|
private focusToItem;
|
|
302
|
+
/**
|
|
303
|
+
* Handles multiple-selection shortcuts from the WAI-ARIA listbox pattern:
|
|
304
|
+
* Space toggles the highlighted option and Ctrl/Cmd+A selects every enabled option.
|
|
305
|
+
* @returns whether the event was handled.
|
|
306
|
+
*/
|
|
307
|
+
private handleMultipleSelectionKeys;
|
|
242
308
|
private handleNavigation;
|
|
243
309
|
private handlePageScroll;
|
|
244
310
|
private scrollToItem;
|
|
@@ -67,6 +67,11 @@ declare class TreeNode<T> {
|
|
|
67
67
|
readonly data: T;
|
|
68
68
|
readonly loading: _angular_core.WritableSignal<boolean>;
|
|
69
69
|
readonly uid: string;
|
|
70
|
+
/**
|
|
71
|
+
* Whether children from an asynchronous children selector have been loaded. Loaded children are cached,
|
|
72
|
+
* so collapsing and re-expanding the node does not request them again.
|
|
73
|
+
*/
|
|
74
|
+
childrenLoaded: boolean;
|
|
70
75
|
index: string;
|
|
71
76
|
parent: TreeNode<T> | null;
|
|
72
77
|
constructor(data: T);
|
|
@@ -269,6 +274,15 @@ declare class TreeService<T> {
|
|
|
269
274
|
static flatten<T>(nodes: Iterable<TreeNode<T>>): List<TreeNode<T>>;
|
|
270
275
|
clearFilter(): void;
|
|
271
276
|
detachNode(node: TreeNode<T>): void;
|
|
277
|
+
/**
|
|
278
|
+
* Expands every expandable, enabled sibling of a node (the `*` key of the WAI-ARIA tree pattern).
|
|
279
|
+
*/
|
|
280
|
+
expandSiblings(node: TreeNode<T>): void;
|
|
281
|
+
/**
|
|
282
|
+
* Finds the next visible node, after the navigated node and wrapping around, whose text starts with `prefix`
|
|
283
|
+
* (case-insensitive). Used for typeahead.
|
|
284
|
+
*/
|
|
285
|
+
findNodeByText(prefix: string): TreeNode<T> | null;
|
|
272
286
|
getNodeByUid(uid: string): TreeNode<T> | null;
|
|
273
287
|
getNodeElementId(uid: string): string;
|
|
274
288
|
getNodeText(node: TreeNode<T>): string;
|
|
@@ -283,7 +297,15 @@ declare class TreeService<T> {
|
|
|
283
297
|
isSelected(node: TreeNode<T>): boolean;
|
|
284
298
|
isTreeFiltered(): boolean;
|
|
285
299
|
isVisible(node: TreeNode<T>): boolean;
|
|
286
|
-
|
|
300
|
+
/**
|
|
301
|
+
* Loads the children of a node from an asynchronous children selector.
|
|
302
|
+
* Children are loaded once and cached; pass `reload` to request them again. A new load cancels a pending one.
|
|
303
|
+
*/
|
|
304
|
+
loadNodeChildren(node: TreeNode<T>, reload?: boolean): void;
|
|
305
|
+
/**
|
|
306
|
+
* Moves a node relative to a target node. Moving a node onto itself or into its own subtree is ignored,
|
|
307
|
+
* because it would detach the subtree and create a parent cycle.
|
|
308
|
+
*/
|
|
287
309
|
moveNode(sourceNode: TreeNode<T>, targetNode: TreeNode<T>, position: DropPosition): void;
|
|
288
310
|
navigate(direction: "next" | "previous" | "first" | "last"): TreeNode<T> | null;
|
|
289
311
|
notifySelectionChange(node: TreeNode<T>): void;
|
|
@@ -314,21 +336,40 @@ declare class TreeService<T> {
|
|
|
314
336
|
setSelectedDataItems(dataItems: Iterable<T>): void;
|
|
315
337
|
setSelectedKeys(keys: Iterable<any>): void;
|
|
316
338
|
setTextField(selector: string | Selector<T, string>): void;
|
|
339
|
+
/**
|
|
340
|
+
* Expands or collapses a node the way user interaction does: disabled nodes are left alone, children of an
|
|
341
|
+
* asynchronous children selector are loaded on first expand, and `nodeExpand$` is notified.
|
|
342
|
+
* @returns whether the expand state changed.
|
|
343
|
+
*/
|
|
344
|
+
toggleNodeExpand(node: TreeNode<T>, expanded: boolean): boolean;
|
|
317
345
|
getCheckKey(node: TreeNode<T>): any;
|
|
318
346
|
getDisableKey(node: TreeNode<T>): any;
|
|
319
347
|
private applyCheckedKeysToNodes;
|
|
320
348
|
private computeVisibleUids;
|
|
321
349
|
private createFlatNodes;
|
|
350
|
+
/**
|
|
351
|
+
* Builds tree nodes for hierarchical data, reusing the existing node of each data item so node state survives
|
|
352
|
+
* a data refresh. A data object that appears more than once gets a node of its own for every extra occurrence.
|
|
353
|
+
*/
|
|
322
354
|
private createHierarchicalNodes;
|
|
323
355
|
private createNodeDictionary;
|
|
324
356
|
private createNodes;
|
|
357
|
+
/**
|
|
358
|
+
* The derived check state of a parent: `true` when all of its check-state children are checked,
|
|
359
|
+
* or `null` when it has none (its own state then stands).
|
|
360
|
+
*/
|
|
361
|
+
private deriveParentChecked;
|
|
362
|
+
/**
|
|
363
|
+
* Children that determine a parent's derived check state: those with a checkbox and, unless
|
|
364
|
+
* `checkDisabledChildren` is set, only enabled ones, because the user cannot change disabled children.
|
|
365
|
+
*/
|
|
366
|
+
private getCheckStateChildren;
|
|
325
367
|
private getExpandKey;
|
|
326
368
|
private getChildNodes;
|
|
327
369
|
private getDataItemSelectKey;
|
|
328
370
|
private getParentNodes;
|
|
329
371
|
private getSelectKey;
|
|
330
372
|
private isAnyParentCollapsed;
|
|
331
|
-
private isAnyParentDisabled;
|
|
332
373
|
private isFiltered;
|
|
333
374
|
private loadCheckedKeysForChildren;
|
|
334
375
|
private moveNodeAfter;
|
|
@@ -360,6 +401,7 @@ declare class TreeComponent<T> {
|
|
|
360
401
|
private setKeydownSubscription;
|
|
361
402
|
private setNodeDragHandlerSubscription;
|
|
362
403
|
private setSubscriptions;
|
|
404
|
+
private setTypeaheadSubscription;
|
|
363
405
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<TreeComponent<any>, never>;
|
|
364
406
|
static ɵcmp: _angular_core.ɵɵComponentDeclaration<TreeComponent<any>, "mona-tree", never, { "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "data": { "alias": "data"; "required": false; "isSignal": true; }; }, {}, ["nodeTemplate"], never, true, never>;
|
|
365
407
|
}
|
|
@@ -378,6 +420,8 @@ declare const TREE_DEFAULT_MESSAGES: MonaTreeViewMessages;
|
|
|
378
420
|
* @param idKey - The property key used to uniquely identify each node (e.g., 'id').
|
|
379
421
|
* @param parentIdKey - The property key holding the parent node's id, or null for roots (e.g., 'parentId').
|
|
380
422
|
* @returns A new array representing the updated flat tree, with the moved node's parentIdKey updated.
|
|
423
|
+
* The array is returned unchanged when the position is "outside" or the target is the dragged node or one of its
|
|
424
|
+
* descendants, which would otherwise create a parent cycle.
|
|
381
425
|
*/
|
|
382
426
|
declare function moveFlatTreeNode<T extends Record<string, any>>(treeData: Iterable<T>, event: NodeDropEvent<T>, idKey: keyof T, parentIdKey: keyof T): T[];
|
|
383
427
|
|
|
@@ -387,7 +431,10 @@ declare function moveFlatTreeNode<T extends Record<string, any>>(treeData: Itera
|
|
|
387
431
|
* @param event - The drag-and-drop event payload.
|
|
388
432
|
* @param idKey - The property key used to identify nodes (e.g., 'id').
|
|
389
433
|
* @param childrenKey - The property key containing the nested items (e.g., 'items').
|
|
390
|
-
* @returns A new array representing the updated tree structure.
|
|
434
|
+
* @returns A new top-level array representing the updated tree structure.
|
|
435
|
+
* Nested children arrays are updated in place, so untouched data items keep their identity (and their tree state).
|
|
436
|
+
* The move is skipped, returning the tree unchanged, when the position is "outside", the target is missing,
|
|
437
|
+
* or the target is the dragged node or one of its descendants, so a node is never lost or made its own ancestor.
|
|
391
438
|
*/
|
|
392
439
|
declare function moveTreeNode<T extends Record<string, any>>(treeData: Iterable<T>, event: NodeDropEvent<T>, idKey: keyof T, childrenKey: keyof T): T[];
|
|
393
440
|
|