@acorex/cdk 22.1.0-next.2 → 22.1.0-next.21
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.
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { model, output, Component, inject, ElementRef, signal, afterNextRender, Injectable, ChangeDetectorRef, ViewContainerRef,
|
|
2
|
+
import { model, output, Component, inject, ElementRef, signal, afterNextRender, Injectable, EventEmitter, PLATFORM_ID, ChangeDetectorRef, ViewContainerRef, DOCUMENT as DOCUMENT$1, Renderer2, Input, Directive, NgModule, InjectionToken, Output, input, effect } from '@angular/core';
|
|
3
3
|
import { AXHtmlUtil } from '@acorex/core/utils';
|
|
4
|
-
import { flatten, cloneDeep, isEqual, omit, get } from 'lodash-es';
|
|
5
4
|
import { isBrowser } from '@acorex/core/platform';
|
|
6
5
|
import { AXValidationService } from '@acorex/core/validation';
|
|
6
|
+
import { DOCUMENT, isPlatformBrowser } from '@angular/common';
|
|
7
|
+
import { omit, isEqual, flatten, cloneDeep, get } from 'lodash-es';
|
|
7
8
|
import { coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coercion';
|
|
8
|
-
import { isPlatformBrowser } from '@angular/common';
|
|
9
9
|
import { noop, Subscription, BehaviorSubject, debounceTime, distinctUntilChanged, Subject, fromEvent, skip } from 'rxjs';
|
|
10
10
|
import { DataSource } from '@angular/cdk/collections';
|
|
11
11
|
import { EventManager } from '@angular/platform-browser';
|
|
@@ -66,6 +66,13 @@ class NXComponent {
|
|
|
66
66
|
get nativeElement() {
|
|
67
67
|
return this.#elementRef.nativeElement;
|
|
68
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* Gets the host HTML element of the component.
|
|
71
|
+
* Same contract as classic editors so `ax-label` / `ax-form-field` can associate the field.
|
|
72
|
+
*/
|
|
73
|
+
getHostElement() {
|
|
74
|
+
return this.nativeElement;
|
|
75
|
+
}
|
|
69
76
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NXComponent, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
70
77
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NXComponent }); }
|
|
71
78
|
}
|
|
@@ -136,25 +143,155 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
|
|
|
136
143
|
}]
|
|
137
144
|
}], propDecorators: { look: [{ type: i0.Input, args: [{ isSignal: true, alias: "look", required: false }] }, { type: i0.Output, args: ["lookChange"] }] } });
|
|
138
145
|
|
|
139
|
-
|
|
146
|
+
/**
|
|
147
|
+
* Signal-based value editor with the same `ax-validation-rule` / `ax-form` contract as the classic value mixins.
|
|
148
|
+
*/
|
|
149
|
+
class NXValueComponent extends NXComponent {
|
|
140
150
|
constructor() {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
this
|
|
144
|
-
|
|
145
|
-
this
|
|
146
|
-
|
|
147
|
-
this
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
+
super(...arguments);
|
|
152
|
+
this.validationRulesChange = new EventEmitter();
|
|
153
|
+
this.#validationRules = [];
|
|
154
|
+
this.#state = 'clear';
|
|
155
|
+
this.#validationService = inject(AXValidationService);
|
|
156
|
+
this.#document = inject(DOCUMENT);
|
|
157
|
+
this.#platformId = inject(PLATFORM_ID);
|
|
158
|
+
this.#registerValidation = afterNextRender(() => {
|
|
159
|
+
this.registerValidation();
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
#validationRules;
|
|
163
|
+
#state;
|
|
164
|
+
#validationService;
|
|
165
|
+
#document;
|
|
166
|
+
#platformId;
|
|
167
|
+
#registerValidation;
|
|
168
|
+
get isRequired() {
|
|
169
|
+
return this.#validationRules.some((rule) => rule.rule == 'required');
|
|
170
|
+
}
|
|
171
|
+
get state() {
|
|
172
|
+
return this.#state;
|
|
173
|
+
}
|
|
174
|
+
registerValidation() {
|
|
175
|
+
this.nativeElement.querySelectorAll('[ax-form-item="true"]').forEach((node) => {
|
|
176
|
+
node.removeAttribute('ax-form-item');
|
|
177
|
+
});
|
|
178
|
+
this.nativeElement.setAttribute('ax-form-item', 'true');
|
|
179
|
+
}
|
|
180
|
+
addValidationRule(rule) {
|
|
181
|
+
this.#validationRules.push(rule);
|
|
182
|
+
this.validationRulesChange.emit(this.#validationRules);
|
|
183
|
+
}
|
|
184
|
+
removeValidationRule(ruleToRemove) {
|
|
185
|
+
this.#validationRules = this.#validationRules.filter((rule) => {
|
|
186
|
+
if (rule.rule !== ruleToRemove.rule) {
|
|
187
|
+
return true;
|
|
188
|
+
}
|
|
189
|
+
const ruleOptions = omit(rule.options, ['title']);
|
|
190
|
+
const removeOptions = omit(ruleToRemove.options, ['title']);
|
|
191
|
+
return !isEqual(ruleOptions['pattern'], removeOptions['pattern']);
|
|
192
|
+
});
|
|
193
|
+
this.validationRulesChange.emit(this.#validationRules);
|
|
194
|
+
this.setState('clear');
|
|
195
|
+
}
|
|
196
|
+
removeValidationRuleByType(type) {
|
|
197
|
+
this.#validationRules = this.#validationRules.filter((rule) => rule.rule !== type);
|
|
198
|
+
this.validationRulesChange.emit(this.#validationRules);
|
|
199
|
+
this.setState('clear');
|
|
200
|
+
}
|
|
201
|
+
async validate() {
|
|
202
|
+
const containerClasses = ['ax-editor-container', 'ax-checkbox', 'ax-radio'];
|
|
203
|
+
const container = containerClasses.some((className) => this.nativeElement.classList.contains(className))
|
|
204
|
+
? this.nativeElement
|
|
205
|
+
: this.nativeElement.querySelector('.ax-editor-container');
|
|
206
|
+
const formField = (container || this.nativeElement).closest('ax-form-field') || container?.closest('ax-checkbox');
|
|
207
|
+
const label = formField?.querySelector('ax-label') || formField?.querySelector('label');
|
|
208
|
+
const rules = this.#validationService.ruleFor(this.#currentValue());
|
|
209
|
+
this.#validationRules.forEach((rule) => {
|
|
210
|
+
rule.options ??= {};
|
|
211
|
+
rule.options['title'] = (label?.firstChild?.textContent ?? this.#currentName())?.replace('*', '').trim();
|
|
212
|
+
rules.addRule(rule.rule, rule.options);
|
|
213
|
+
});
|
|
214
|
+
const result = await rules.validate();
|
|
215
|
+
if (result.result) {
|
|
216
|
+
this.setState('clear');
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
const message = result.rules.find((rule) => !rule.result)?.message;
|
|
220
|
+
this.setState('error', message);
|
|
221
|
+
}
|
|
222
|
+
return result;
|
|
223
|
+
}
|
|
224
|
+
setState(state, ...args) {
|
|
225
|
+
if (!isBrowser()) {
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
const container = this.nativeElement.classList.contains('ax-editor-container')
|
|
229
|
+
? this.nativeElement
|
|
230
|
+
: this.nativeElement.querySelector('.ax-editor-container');
|
|
231
|
+
const formField = (container || this.nativeElement).closest('ax-form-field');
|
|
232
|
+
const label = formField?.querySelector('ax-label') || formField?.querySelector('label');
|
|
233
|
+
let target = container || this.nativeElement.firstElementChild;
|
|
234
|
+
if (target?.querySelector('.ax-error-container')) {
|
|
235
|
+
target = target.querySelector('.ax-error-container');
|
|
236
|
+
}
|
|
237
|
+
const parent = target?.parentElement;
|
|
238
|
+
if (!target || !parent) {
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
switch (state) {
|
|
242
|
+
case 'clear':
|
|
243
|
+
this.#state = 'clear';
|
|
244
|
+
parent.querySelector('span.ax-error-message')?.remove();
|
|
245
|
+
label?.classList.remove('ax-state-error');
|
|
246
|
+
target.classList.remove('ax-state-error', 'ax-state-success');
|
|
247
|
+
break;
|
|
248
|
+
case 'success':
|
|
249
|
+
this.setState('clear');
|
|
250
|
+
target.classList.add('ax-state-success');
|
|
251
|
+
this.#state = 'success';
|
|
252
|
+
break;
|
|
253
|
+
case 'error':
|
|
254
|
+
this.setState('clear');
|
|
255
|
+
if (isPlatformBrowser(this.#platformId)) {
|
|
256
|
+
const message = args[0] ?? '';
|
|
257
|
+
if (message) {
|
|
258
|
+
const span = this.#document.createElement('span');
|
|
259
|
+
span.innerHTML = message;
|
|
260
|
+
span.classList.add('ax-error-message');
|
|
261
|
+
parent.appendChild(span);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
label?.classList.add('ax-state-error');
|
|
265
|
+
target.classList.add('ax-state-error');
|
|
266
|
+
this.#state = 'error';
|
|
267
|
+
break;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
resetErrors() {
|
|
271
|
+
this.setState('clear');
|
|
272
|
+
}
|
|
273
|
+
commitValue(value, _isUserInteraction = false) {
|
|
274
|
+
const model = this.#valueModel();
|
|
275
|
+
model?.set?.(value);
|
|
276
|
+
}
|
|
277
|
+
#currentValue() {
|
|
278
|
+
return this.#resolve(this.value);
|
|
279
|
+
}
|
|
280
|
+
#currentName() {
|
|
281
|
+
return String(this.#resolve(this.name) ?? '');
|
|
282
|
+
}
|
|
283
|
+
#valueModel() {
|
|
284
|
+
return this.value;
|
|
285
|
+
}
|
|
286
|
+
#resolve(prop) {
|
|
287
|
+
return typeof prop === 'function' ? prop() : prop;
|
|
288
|
+
}
|
|
289
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NXValueComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
290
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NXValueComponent }); }
|
|
151
291
|
}
|
|
152
292
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NXValueComponent, decorators: [{
|
|
153
|
-
type:
|
|
154
|
-
|
|
155
|
-
template: '',
|
|
156
|
-
}]
|
|
157
|
-
}], propDecorators: { readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }, { type: i0.Output, args: ["readonlyChange"] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }, { type: i0.Output, args: ["nameChange"] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], onValueChanged: [{ type: i0.Output, args: ["onValueChanged"] }] } });
|
|
293
|
+
type: Injectable
|
|
294
|
+
}] });
|
|
158
295
|
|
|
159
296
|
const defaultOffset = 3;
|
|
160
297
|
function convertToPlacement(value) {
|
|
@@ -575,7 +712,7 @@ class MXValueComponent extends MXInteractiveComponent {
|
|
|
575
712
|
this._isUserInteraction = false;
|
|
576
713
|
this.stateChange = new EventEmitter();
|
|
577
714
|
this._state = 'clear';
|
|
578
|
-
this.document = inject(DOCUMENT);
|
|
715
|
+
this.document = inject(DOCUMENT$1);
|
|
579
716
|
this.platformID = inject(PLATFORM_ID);
|
|
580
717
|
this.validationService = inject(AXValidationService);
|
|
581
718
|
this.validationRulesChange = new EventEmitter();
|
|
@@ -1047,6 +1184,10 @@ class AXListDataSource extends DataSource {
|
|
|
1047
1184
|
.pipe(distinctUntilChanged())
|
|
1048
1185
|
.subscribe((range) => {
|
|
1049
1186
|
this._range = range;
|
|
1187
|
+
if (range.end <= range.start) {
|
|
1188
|
+
this.source.setPage(0);
|
|
1189
|
+
return;
|
|
1190
|
+
}
|
|
1050
1191
|
const startPage = this.getPageForIndex(range.start);
|
|
1051
1192
|
const endPage = this.getPageForIndex(range.end - 1);
|
|
1052
1193
|
for (let i = startPage; i <= endPage; i++) {
|
|
@@ -1094,6 +1235,7 @@ class AXDataSource {
|
|
|
1094
1235
|
this._totalCount = 0;
|
|
1095
1236
|
this._items = [];
|
|
1096
1237
|
this.fetchedPages = new Set();
|
|
1238
|
+
this.inflightPages = new Set();
|
|
1097
1239
|
this._page = 0;
|
|
1098
1240
|
this._query = { skip: 0, take: 0, sort: [] };
|
|
1099
1241
|
this.onChanged = new Subject();
|
|
@@ -1106,24 +1248,35 @@ class AXDataSource {
|
|
|
1106
1248
|
this._items = new Array(0);
|
|
1107
1249
|
this._query.take = this.config.pageSize;
|
|
1108
1250
|
}
|
|
1251
|
+
pageHasCachedItems(page) {
|
|
1252
|
+
const start = page * this.config.pageSize;
|
|
1253
|
+
return this._cachedItems.slice(start, start + this.config.pageSize).some((item) => item != null);
|
|
1254
|
+
}
|
|
1109
1255
|
load() {
|
|
1110
1256
|
const page = this._page;
|
|
1111
|
-
if (this.
|
|
1257
|
+
if (this.inflightPages.has(page)) {
|
|
1258
|
+
return;
|
|
1259
|
+
}
|
|
1260
|
+
if (this.fetchedPages.has(page) && this.useCache && this.pageHasCachedItems(page)) {
|
|
1112
1261
|
return;
|
|
1113
1262
|
}
|
|
1114
1263
|
this.setLoadingState(true);
|
|
1115
|
-
this.
|
|
1264
|
+
this.inflightPages.add(page);
|
|
1116
1265
|
this._query.take = this.config.pageSize;
|
|
1117
1266
|
this._query.skip = page * this.config.pageSize;
|
|
1118
1267
|
this.config
|
|
1119
1268
|
.load({ ...this._query })
|
|
1120
1269
|
.then((result) => {
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
this.
|
|
1270
|
+
const items = Array.isArray(result?.items) ? result.items : [];
|
|
1271
|
+
const reportedTotal = Number(result?.total);
|
|
1272
|
+
const total = Number.isFinite(reportedTotal) && reportedTotal > 0
|
|
1273
|
+
? Math.max(reportedTotal, items.length)
|
|
1274
|
+
: items.length;
|
|
1275
|
+
this.fetchedPages.add(page);
|
|
1276
|
+
this._totalCount = total;
|
|
1277
|
+
this._cachedItems = new Array(Math.max(total, 0));
|
|
1278
|
+
this._items = items;
|
|
1279
|
+
this._cachedItems.splice(page * this.config.pageSize, this.config.pageSize, ...items);
|
|
1127
1280
|
const event = {
|
|
1128
1281
|
cachedItems: this.cachedItems,
|
|
1129
1282
|
items: this.items,
|
|
@@ -1132,8 +1285,12 @@ class AXDataSource {
|
|
|
1132
1285
|
totalPages: this.totalPages,
|
|
1133
1286
|
};
|
|
1134
1287
|
this.onChanged.next(event);
|
|
1288
|
+
})
|
|
1289
|
+
.catch(() => {
|
|
1290
|
+
this.fetchedPages.delete(page);
|
|
1135
1291
|
})
|
|
1136
1292
|
.finally(() => {
|
|
1293
|
+
this.inflightPages.delete(page);
|
|
1137
1294
|
this.setLoadingState(false);
|
|
1138
1295
|
});
|
|
1139
1296
|
}
|
|
@@ -1161,15 +1318,16 @@ class AXDataSource {
|
|
|
1161
1318
|
this._query.sort = value;
|
|
1162
1319
|
}
|
|
1163
1320
|
clearFilter() {
|
|
1164
|
-
this._query.filter
|
|
1321
|
+
delete this._query.filter;
|
|
1165
1322
|
}
|
|
1166
1323
|
reset() {
|
|
1167
|
-
this._query.filter =
|
|
1324
|
+
this._query.filter = undefined;
|
|
1168
1325
|
this._query.sort = [];
|
|
1169
1326
|
this._query.skip = 0;
|
|
1170
1327
|
this._items = new Array(0);
|
|
1171
1328
|
this._cachedItems = new Array(this.config.pageSize);
|
|
1172
1329
|
this.fetchedPages.clear();
|
|
1330
|
+
this.inflightPages.clear();
|
|
1173
1331
|
this._page = 0;
|
|
1174
1332
|
this._totalCount = 0;
|
|
1175
1333
|
}
|
|
@@ -1263,7 +1421,7 @@ class AXAutoFocusDirective {
|
|
|
1263
1421
|
this._host = inject(AXFocusableComponent, { host: true, self: true, optional: true });
|
|
1264
1422
|
this.autoFocus = true;
|
|
1265
1423
|
this.time = 50;
|
|
1266
|
-
this.document = inject(DOCUMENT);
|
|
1424
|
+
this.document = inject(DOCUMENT$1);
|
|
1267
1425
|
this.platformID = inject(PLATFORM_ID);
|
|
1268
1426
|
}
|
|
1269
1427
|
ngAfterViewInit() {
|
|
@@ -1309,7 +1467,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
|
|
|
1309
1467
|
class AXHotkeysService {
|
|
1310
1468
|
constructor() {
|
|
1311
1469
|
this.eventManager = inject(EventManager);
|
|
1312
|
-
this.document = inject(DOCUMENT);
|
|
1470
|
+
this.document = inject(DOCUMENT$1);
|
|
1313
1471
|
this.defaults = {
|
|
1314
1472
|
element: this.document,
|
|
1315
1473
|
};
|
|
@@ -2491,7 +2649,7 @@ class AXRippleDirective {
|
|
|
2491
2649
|
this.cdr = inject(ChangeDetectorRef);
|
|
2492
2650
|
this.enabled = true;
|
|
2493
2651
|
this.axRippleColor = 'var(--ax-comp-ripple-color, rgba(255, 255, 255, 0.3))';
|
|
2494
|
-
this.document = inject(DOCUMENT);
|
|
2652
|
+
this.document = inject(DOCUMENT$1);
|
|
2495
2653
|
this.platformID = inject(PLATFORM_ID);
|
|
2496
2654
|
}
|
|
2497
2655
|
ngOnInit() {
|