@neuravision/ng-construct 0.1.0 → 0.2.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.
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { input, output, signal, computed, ChangeDetectionStrategy, Component, inject, forwardRef, model, booleanAttribute, viewChild, contentChildren, effect,
|
|
2
|
+
import { input, output, signal, computed, ChangeDetectionStrategy, Component, inject, forwardRef, model, booleanAttribute, viewChild, contentChildren, effect, contentChild, ElementRef, TemplateRef, Directive, Injectable, viewChildren, Renderer2, InjectionToken, DOCUMENT, numberAttribute, Pipe } from '@angular/core';
|
|
3
3
|
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
|
4
4
|
import { NgTemplateOutlet } from '@angular/common';
|
|
5
5
|
|
|
@@ -1200,36 +1200,65 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImpor
|
|
|
1200
1200
|
`, styles: [":host{display:block}\n"] }]
|
|
1201
1201
|
}], propDecorators: { disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }, { type: i0.Output, args: ["disabledChange"] }], indeterminate: [{ type: i0.Input, args: [{ isSignal: true, alias: "indeterminate", required: false }] }, { type: i0.Output, args: ["indeterminateChange"] }], checked: [{ type: i0.Input, args: [{ isSignal: true, alias: "checked", required: false }] }, { type: i0.Output, args: ["checkedChange"] }] } });
|
|
1202
1202
|
|
|
1203
|
+
// ── Radio Button ─────────────────────────────────────────────────────────────
|
|
1203
1204
|
/**
|
|
1204
|
-
*
|
|
1205
|
+
* Individual radio button. Use inside `af-radio-group` for full
|
|
1206
|
+
* accessibility, or standalone with its own `ControlValueAccessor`.
|
|
1205
1207
|
*
|
|
1206
1208
|
* @example
|
|
1207
|
-
* <af-radio
|
|
1208
|
-
* Standard
|
|
1209
|
-
* </af-radio>
|
|
1210
|
-
*
|
|
1211
|
-
* Premium
|
|
1212
|
-
* </af-radio>
|
|
1209
|
+
* <af-radio-group ariaLabel="Plan" name="plan" [(ngModel)]="plan">
|
|
1210
|
+
* <af-radio value="standard">Standard</af-radio>
|
|
1211
|
+
* <af-radio value="premium">Premium</af-radio>
|
|
1212
|
+
* </af-radio-group>
|
|
1213
1213
|
*/
|
|
1214
1214
|
class AfRadioComponent {
|
|
1215
|
-
|
|
1215
|
+
group = inject(forwardRef(() => AfRadioGroupComponent), { optional: true });
|
|
1216
|
+
/** Radio group name (only used without `af-radio-group`). */
|
|
1216
1217
|
name = input('', ...(ngDevMode ? [{ debugName: "name" }] : []));
|
|
1217
|
-
/** Radio value */
|
|
1218
|
+
/** Radio value. */
|
|
1218
1219
|
value = input(undefined, ...(ngDevMode ? [{ debugName: "value" }] : []));
|
|
1219
|
-
/** Whether radio is disabled */
|
|
1220
|
+
/** Whether this radio is disabled. */
|
|
1220
1221
|
disabled = model(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
|
|
1222
|
+
inputRef = viewChild.required('inputEl');
|
|
1221
1223
|
modelValue = signal(undefined, ...(ngDevMode ? [{ debugName: "modelValue" }] : []));
|
|
1222
1224
|
onChangeCallback = () => { };
|
|
1223
1225
|
onTouched = () => { };
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
const
|
|
1227
|
-
|
|
1226
|
+
resolvedName = computed(() => this.group?.name() || this.name(), ...(ngDevMode ? [{ debugName: "resolvedName" }] : []));
|
|
1227
|
+
isChecked = computed(() => {
|
|
1228
|
+
const selected = this.group ? this.group.selectedValue() : this.modelValue();
|
|
1229
|
+
return selected === this.value();
|
|
1230
|
+
}, ...(ngDevMode ? [{ debugName: "isChecked" }] : []));
|
|
1231
|
+
isDisabled = computed(() => {
|
|
1232
|
+
if (this.group?.disabled())
|
|
1233
|
+
return true;
|
|
1234
|
+
return this.disabled();
|
|
1235
|
+
}, ...(ngDevMode ? [{ debugName: "isDisabled" }] : []));
|
|
1236
|
+
resolvedTabindex = computed(() => {
|
|
1237
|
+
if (!this.group)
|
|
1238
|
+
return null;
|
|
1239
|
+
return this.group.tabindexFor(this);
|
|
1240
|
+
}, ...(ngDevMode ? [{ debugName: "resolvedTabindex" }] : []));
|
|
1241
|
+
/** Focuses the native input element. */
|
|
1242
|
+
focus() {
|
|
1243
|
+
this.inputRef().nativeElement.focus();
|
|
1244
|
+
}
|
|
1245
|
+
onChangeEvent() {
|
|
1246
|
+
if (this.group) {
|
|
1247
|
+
this.group.selectRadio(this);
|
|
1248
|
+
}
|
|
1249
|
+
else {
|
|
1228
1250
|
this.modelValue.set(this.value());
|
|
1229
1251
|
this.onChangeCallback(this.value());
|
|
1230
1252
|
}
|
|
1231
1253
|
}
|
|
1232
|
-
|
|
1254
|
+
onFocus() {
|
|
1255
|
+
this.group?.onRadioFocus(this);
|
|
1256
|
+
}
|
|
1257
|
+
onKeydown(event) {
|
|
1258
|
+
if (this.group) {
|
|
1259
|
+
this.group.onRadioKeydown(event, this);
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1233
1262
|
writeValue(value) {
|
|
1234
1263
|
this.modelValue.set(value);
|
|
1235
1264
|
}
|
|
@@ -1243,26 +1272,29 @@ class AfRadioComponent {
|
|
|
1243
1272
|
this.disabled.set(isDisabled);
|
|
1244
1273
|
}
|
|
1245
1274
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: AfRadioComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1246
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.
|
|
1275
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.1.2", type: AfRadioComponent, isStandalone: true, selector: "af-radio", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { disabled: "disabledChange" }, providers: [
|
|
1247
1276
|
{
|
|
1248
1277
|
provide: NG_VALUE_ACCESSOR,
|
|
1249
1278
|
useExisting: forwardRef(() => AfRadioComponent),
|
|
1250
|
-
multi: true
|
|
1251
|
-
}
|
|
1252
|
-
], ngImport: i0, template: `
|
|
1279
|
+
multi: true,
|
|
1280
|
+
},
|
|
1281
|
+
], viewQueries: [{ propertyName: "inputRef", first: true, predicate: ["inputEl"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
1253
1282
|
<label class="ct-radio">
|
|
1254
1283
|
<input
|
|
1284
|
+
#inputEl
|
|
1255
1285
|
class="ct-radio__input"
|
|
1256
1286
|
type="radio"
|
|
1257
|
-
[name]="
|
|
1287
|
+
[name]="resolvedName()"
|
|
1258
1288
|
[value]="value()"
|
|
1259
1289
|
[checked]="isChecked()"
|
|
1260
|
-
[disabled]="
|
|
1261
|
-
|
|
1290
|
+
[disabled]="isDisabled()"
|
|
1291
|
+
[attr.tabindex]="resolvedTabindex()"
|
|
1292
|
+
(change)="onChangeEvent()"
|
|
1262
1293
|
(blur)="onTouched()"
|
|
1263
|
-
|
|
1294
|
+
(focus)="onFocus()"
|
|
1295
|
+
(keydown)="onKeydown($event)" />
|
|
1264
1296
|
<span class="ct-radio__label">
|
|
1265
|
-
<ng-content
|
|
1297
|
+
<ng-content />
|
|
1266
1298
|
</span>
|
|
1267
1299
|
</label>
|
|
1268
1300
|
`, isInline: true, styles: [":host{display:block}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
@@ -1273,26 +1305,172 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImpor
|
|
|
1273
1305
|
{
|
|
1274
1306
|
provide: NG_VALUE_ACCESSOR,
|
|
1275
1307
|
useExisting: forwardRef(() => AfRadioComponent),
|
|
1276
|
-
multi: true
|
|
1277
|
-
}
|
|
1308
|
+
multi: true,
|
|
1309
|
+
},
|
|
1278
1310
|
], template: `
|
|
1279
1311
|
<label class="ct-radio">
|
|
1280
1312
|
<input
|
|
1313
|
+
#inputEl
|
|
1281
1314
|
class="ct-radio__input"
|
|
1282
1315
|
type="radio"
|
|
1283
|
-
[name]="
|
|
1316
|
+
[name]="resolvedName()"
|
|
1284
1317
|
[value]="value()"
|
|
1285
1318
|
[checked]="isChecked()"
|
|
1286
|
-
[disabled]="
|
|
1287
|
-
|
|
1319
|
+
[disabled]="isDisabled()"
|
|
1320
|
+
[attr.tabindex]="resolvedTabindex()"
|
|
1321
|
+
(change)="onChangeEvent()"
|
|
1288
1322
|
(blur)="onTouched()"
|
|
1289
|
-
|
|
1323
|
+
(focus)="onFocus()"
|
|
1324
|
+
(keydown)="onKeydown($event)" />
|
|
1290
1325
|
<span class="ct-radio__label">
|
|
1291
|
-
<ng-content
|
|
1326
|
+
<ng-content />
|
|
1292
1327
|
</span>
|
|
1293
1328
|
</label>
|
|
1294
1329
|
`, styles: [":host{display:block}\n"] }]
|
|
1295
|
-
}], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }, { type: i0.Output, args: ["disabledChange"] }] } });
|
|
1330
|
+
}], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }, { type: i0.Output, args: ["disabledChange"] }], inputRef: [{ type: i0.ViewChild, args: ['inputEl', { isSignal: true }] }] } });
|
|
1331
|
+
// ── Radio Group ──────────────────────────────────────────────────────────────
|
|
1332
|
+
/**
|
|
1333
|
+
* Groups `af-radio` components with `role="radiogroup"`, ARIA labeling,
|
|
1334
|
+
* roving tabindex, and arrow-key navigation per WAI-ARIA Radio Group Pattern.
|
|
1335
|
+
*
|
|
1336
|
+
* Implements `ControlValueAccessor` so the group value can be bound via
|
|
1337
|
+
* `[(ngModel)]` or reactive forms.
|
|
1338
|
+
*
|
|
1339
|
+
* @example
|
|
1340
|
+
* <af-radio-group ariaLabel="Select plan" name="plan" [(ngModel)]="plan">
|
|
1341
|
+
* <af-radio value="standard">Standard</af-radio>
|
|
1342
|
+
* <af-radio value="premium">Premium</af-radio>
|
|
1343
|
+
* </af-radio-group>
|
|
1344
|
+
*/
|
|
1345
|
+
class AfRadioGroupComponent {
|
|
1346
|
+
/** Shared `name` attribute for all child radios. */
|
|
1347
|
+
name = input.required(...(ngDevMode ? [{ debugName: "name" }] : []));
|
|
1348
|
+
/** Accessible label for the radio group. */
|
|
1349
|
+
ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
|
|
1350
|
+
/** ID of an external element labeling this group. */
|
|
1351
|
+
ariaLabelledBy = input('', ...(ngDevMode ? [{ debugName: "ariaLabelledBy" }] : []));
|
|
1352
|
+
/** Disables all radios in the group. */
|
|
1353
|
+
disabled = model(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
|
|
1354
|
+
radios = contentChildren(AfRadioComponent, ...(ngDevMode ? [{ debugName: "radios" }] : []));
|
|
1355
|
+
selectedValue = signal(undefined, ...(ngDevMode ? [{ debugName: "selectedValue" }] : []));
|
|
1356
|
+
focusedIndex = signal(0, ...(ngDevMode ? [{ debugName: "focusedIndex" }] : []));
|
|
1357
|
+
onChangeCallback = () => { };
|
|
1358
|
+
onTouchedCallback = () => { };
|
|
1359
|
+
syncEffect = effect(() => {
|
|
1360
|
+
const radios = this.radios();
|
|
1361
|
+
const value = this.selectedValue();
|
|
1362
|
+
const checkedIdx = radios.findIndex((r) => r.value() === value);
|
|
1363
|
+
this.focusedIndex.set(checkedIdx >= 0 ? checkedIdx : 0);
|
|
1364
|
+
}, ...(ngDevMode ? [{ debugName: "syncEffect" }] : []));
|
|
1365
|
+
/** Returns the tabindex a child radio should use for roving tabindex. */
|
|
1366
|
+
tabindexFor(radio) {
|
|
1367
|
+
const radios = this.enabledRadios();
|
|
1368
|
+
const idx = radios.indexOf(radio);
|
|
1369
|
+
if (idx === -1)
|
|
1370
|
+
return -1;
|
|
1371
|
+
return idx === this.focusedIndex() ? 0 : -1;
|
|
1372
|
+
}
|
|
1373
|
+
/** Selects a radio and propagates the value. */
|
|
1374
|
+
selectRadio(radio) {
|
|
1375
|
+
const value = radio.value();
|
|
1376
|
+
this.selectedValue.set(value);
|
|
1377
|
+
this.onChangeCallback(value);
|
|
1378
|
+
this.onTouchedCallback();
|
|
1379
|
+
}
|
|
1380
|
+
/** Called when a child radio receives focus. */
|
|
1381
|
+
onRadioFocus(radio) {
|
|
1382
|
+
const idx = this.enabledRadios().indexOf(radio);
|
|
1383
|
+
if (idx >= 0) {
|
|
1384
|
+
this.focusedIndex.set(idx);
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
/** Handles keyboard navigation within the group. */
|
|
1388
|
+
onRadioKeydown(event, _current) {
|
|
1389
|
+
const enabled = this.enabledRadios();
|
|
1390
|
+
if (enabled.length === 0)
|
|
1391
|
+
return;
|
|
1392
|
+
let nextIndex = null;
|
|
1393
|
+
switch (event.key) {
|
|
1394
|
+
case 'ArrowDown':
|
|
1395
|
+
case 'ArrowRight':
|
|
1396
|
+
event.preventDefault();
|
|
1397
|
+
nextIndex = (this.focusedIndex() + 1) % enabled.length;
|
|
1398
|
+
break;
|
|
1399
|
+
case 'ArrowUp':
|
|
1400
|
+
case 'ArrowLeft':
|
|
1401
|
+
event.preventDefault();
|
|
1402
|
+
nextIndex = (this.focusedIndex() - 1 + enabled.length) % enabled.length;
|
|
1403
|
+
break;
|
|
1404
|
+
case 'Home':
|
|
1405
|
+
event.preventDefault();
|
|
1406
|
+
nextIndex = 0;
|
|
1407
|
+
break;
|
|
1408
|
+
case 'End':
|
|
1409
|
+
event.preventDefault();
|
|
1410
|
+
nextIndex = enabled.length - 1;
|
|
1411
|
+
break;
|
|
1412
|
+
case ' ':
|
|
1413
|
+
event.preventDefault();
|
|
1414
|
+
this.selectRadio(enabled[this.focusedIndex()]);
|
|
1415
|
+
return;
|
|
1416
|
+
}
|
|
1417
|
+
if (nextIndex !== null) {
|
|
1418
|
+
this.focusedIndex.set(nextIndex);
|
|
1419
|
+
const target = enabled[nextIndex];
|
|
1420
|
+
target.focus();
|
|
1421
|
+
this.selectRadio(target);
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
writeValue(value) {
|
|
1425
|
+
this.selectedValue.set(value);
|
|
1426
|
+
}
|
|
1427
|
+
registerOnChange(fn) {
|
|
1428
|
+
this.onChangeCallback = fn;
|
|
1429
|
+
}
|
|
1430
|
+
registerOnTouched(fn) {
|
|
1431
|
+
this.onTouchedCallback = fn;
|
|
1432
|
+
}
|
|
1433
|
+
setDisabledState(isDisabled) {
|
|
1434
|
+
this.disabled.set(isDisabled);
|
|
1435
|
+
}
|
|
1436
|
+
enabledRadios() {
|
|
1437
|
+
return this.radios().filter((r) => !r.isDisabled());
|
|
1438
|
+
}
|
|
1439
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: AfRadioGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1440
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.1.2", type: AfRadioGroupComponent, isStandalone: true, selector: "af-radio-group", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: true, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, ariaLabelledBy: { classPropertyName: "ariaLabelledBy", publicName: "ariaLabelledBy", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { disabled: "disabledChange" }, providers: [
|
|
1441
|
+
{
|
|
1442
|
+
provide: NG_VALUE_ACCESSOR,
|
|
1443
|
+
useExisting: forwardRef(() => AfRadioGroupComponent),
|
|
1444
|
+
multi: true,
|
|
1445
|
+
},
|
|
1446
|
+
], queries: [{ propertyName: "radios", predicate: AfRadioComponent, isSignal: true }], ngImport: i0, template: `
|
|
1447
|
+
<div
|
|
1448
|
+
role="radiogroup"
|
|
1449
|
+
[attr.aria-label]="ariaLabel() || null"
|
|
1450
|
+
[attr.aria-labelledby]="ariaLabelledBy() || null"
|
|
1451
|
+
[attr.aria-disabled]="disabled() || null">
|
|
1452
|
+
<ng-content />
|
|
1453
|
+
</div>
|
|
1454
|
+
`, isInline: true, styles: [":host{display:block}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1455
|
+
}
|
|
1456
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: AfRadioGroupComponent, decorators: [{
|
|
1457
|
+
type: Component,
|
|
1458
|
+
args: [{ selector: 'af-radio-group', changeDetection: ChangeDetectionStrategy.OnPush, providers: [
|
|
1459
|
+
{
|
|
1460
|
+
provide: NG_VALUE_ACCESSOR,
|
|
1461
|
+
useExisting: forwardRef(() => AfRadioGroupComponent),
|
|
1462
|
+
multi: true,
|
|
1463
|
+
},
|
|
1464
|
+
], template: `
|
|
1465
|
+
<div
|
|
1466
|
+
role="radiogroup"
|
|
1467
|
+
[attr.aria-label]="ariaLabel() || null"
|
|
1468
|
+
[attr.aria-labelledby]="ariaLabelledBy() || null"
|
|
1469
|
+
[attr.aria-disabled]="disabled() || null">
|
|
1470
|
+
<ng-content />
|
|
1471
|
+
</div>
|
|
1472
|
+
`, styles: [":host{display:block}\n"] }]
|
|
1473
|
+
}], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: true }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], ariaLabelledBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabelledBy", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }, { type: i0.Output, args: ["disabledChange"] }], radios: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => AfRadioComponent), { isSignal: true }] }] } });
|
|
1296
1474
|
|
|
1297
1475
|
/**
|
|
1298
1476
|
* Switch/Toggle component with form control support
|
|
@@ -1303,7 +1481,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImpor
|
|
|
1303
1481
|
* </af-switch>
|
|
1304
1482
|
*/
|
|
1305
1483
|
class AfSwitchComponent {
|
|
1306
|
-
/**
|
|
1484
|
+
/** Accessible label for icon-only or unlabeled switches. */
|
|
1485
|
+
ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
|
|
1486
|
+
/** Whether switch is disabled. */
|
|
1307
1487
|
disabled = model(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
|
|
1308
1488
|
/** Checked state - supports two-way binding via [(checked)] */
|
|
1309
1489
|
checked = model(false, ...(ngDevMode ? [{ debugName: "checked" }] : []));
|
|
@@ -1328,7 +1508,7 @@ class AfSwitchComponent {
|
|
|
1328
1508
|
this.disabled.set(isDisabled);
|
|
1329
1509
|
}
|
|
1330
1510
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: AfSwitchComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1331
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.1.2", type: AfSwitchComponent, isStandalone: true, selector: "af-switch", inputs: { disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, checked: { classPropertyName: "checked", publicName: "checked", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { disabled: "disabledChange", checked: "checkedChange" }, providers: [
|
|
1511
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.1.2", type: AfSwitchComponent, isStandalone: true, selector: "af-switch", inputs: { ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, checked: { classPropertyName: "checked", publicName: "checked", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { disabled: "disabledChange", checked: "checkedChange" }, providers: [
|
|
1332
1512
|
{
|
|
1333
1513
|
provide: NG_VALUE_ACCESSOR,
|
|
1334
1514
|
useExisting: forwardRef(() => AfSwitchComponent),
|
|
@@ -1342,9 +1522,9 @@ class AfSwitchComponent {
|
|
|
1342
1522
|
role="switch"
|
|
1343
1523
|
[checked]="checked()"
|
|
1344
1524
|
[disabled]="disabled()"
|
|
1525
|
+
[attr.aria-label]="ariaLabel() || null"
|
|
1345
1526
|
(change)="onChange($event)"
|
|
1346
|
-
(blur)="onTouched()"
|
|
1347
|
-
/>
|
|
1527
|
+
(blur)="onTouched()" />
|
|
1348
1528
|
<span class="ct-switch__label">
|
|
1349
1529
|
<ng-content></ng-content>
|
|
1350
1530
|
</span>
|
|
@@ -1367,57 +1547,58 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImpor
|
|
|
1367
1547
|
role="switch"
|
|
1368
1548
|
[checked]="checked()"
|
|
1369
1549
|
[disabled]="disabled()"
|
|
1550
|
+
[attr.aria-label]="ariaLabel() || null"
|
|
1370
1551
|
(change)="onChange($event)"
|
|
1371
|
-
(blur)="onTouched()"
|
|
1372
|
-
/>
|
|
1552
|
+
(blur)="onTouched()" />
|
|
1373
1553
|
<span class="ct-switch__label">
|
|
1374
1554
|
<ng-content></ng-content>
|
|
1375
1555
|
</span>
|
|
1376
1556
|
</label>
|
|
1377
1557
|
`, styles: [":host{display:block}\n"] }]
|
|
1378
|
-
}], propDecorators: { disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }, { type: i0.Output, args: ["disabledChange"] }], checked: [{ type: i0.Input, args: [{ isSignal: true, alias: "checked", required: false }] }, { type: i0.Output, args: ["checkedChange"] }] } });
|
|
1558
|
+
}], propDecorators: { ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }, { type: i0.Output, args: ["disabledChange"] }], checked: [{ type: i0.Input, args: [{ isSignal: true, alias: "checked", required: false }] }, { type: i0.Output, args: ["checkedChange"] }] } });
|
|
1379
1559
|
|
|
1380
1560
|
/**
|
|
1381
|
-
* Card component for containing content
|
|
1561
|
+
* Card component for containing content.
|
|
1562
|
+
*
|
|
1563
|
+
* When `interactive` is set the card becomes keyboard-accessible with
|
|
1564
|
+
* `role="button"`, roving `tabindex`, and Enter/Space activation.
|
|
1382
1565
|
*
|
|
1383
1566
|
* @example
|
|
1384
1567
|
* <af-card elevation="md" padding="lg">
|
|
1385
|
-
* <div header>
|
|
1386
|
-
*
|
|
1387
|
-
*
|
|
1388
|
-
*
|
|
1389
|
-
*
|
|
1390
|
-
* </
|
|
1568
|
+
* <div header><h3>Title</h3></div>
|
|
1569
|
+
* <div body><p>Card content</p></div>
|
|
1570
|
+
* </af-card>
|
|
1571
|
+
*
|
|
1572
|
+
* <af-card interactive ariaLabel="Open project" (cardClick)="open()">
|
|
1573
|
+
* <p body>Click me</p>
|
|
1391
1574
|
* </af-card>
|
|
1392
1575
|
*/
|
|
1393
1576
|
class AfCardComponent {
|
|
1394
|
-
/**
|
|
1577
|
+
/** Makes the card interactive (clickable, keyboard-accessible). */
|
|
1395
1578
|
interactive = input(false, ...(ngDevMode ? [{ debugName: "interactive" }] : []));
|
|
1396
|
-
/** Shadow elevation level */
|
|
1579
|
+
/** Shadow elevation level. */
|
|
1397
1580
|
elevation = input(null, ...(ngDevMode ? [{ debugName: "elevation" }] : []));
|
|
1398
|
-
/** Content padding level */
|
|
1581
|
+
/** Content padding level. */
|
|
1399
1582
|
padding = input(null, ...(ngDevMode ? [{ debugName: "padding" }] : []));
|
|
1400
|
-
/**
|
|
1583
|
+
/** Accessible label for interactive cards. */
|
|
1584
|
+
ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
|
|
1585
|
+
/** Emitted when an interactive card is activated (click, Enter, or Space). */
|
|
1401
1586
|
cardClick = output();
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
}
|
|
1407
|
-
set footerContent(value) {
|
|
1408
|
-
this.hasFooter.set(!!value);
|
|
1409
|
-
}
|
|
1587
|
+
headerRef = contentChild('[header]', { ...(ngDevMode ? { debugName: "headerRef" } : {}), read: ElementRef });
|
|
1588
|
+
footerRef = contentChild('[footer]', { ...(ngDevMode ? { debugName: "footerRef" } : {}), read: ElementRef });
|
|
1589
|
+
hasHeader = computed(() => !!this.headerRef(), ...(ngDevMode ? [{ debugName: "hasHeader" }] : []));
|
|
1590
|
+
hasFooter = computed(() => !!this.footerRef(), ...(ngDevMode ? [{ debugName: "hasFooter" }] : []));
|
|
1410
1591
|
static ELEVATION_MAP = {
|
|
1411
1592
|
none: 'none',
|
|
1412
1593
|
sm: '0 1px 3px rgba(0, 0, 0, 0.08)',
|
|
1413
1594
|
md: '0 4px 12px rgba(0, 0, 0, 0.08)',
|
|
1414
|
-
lg: '0 8px 24px rgba(0, 0, 0, 0.12)'
|
|
1595
|
+
lg: '0 8px 24px rgba(0, 0, 0, 0.12)',
|
|
1415
1596
|
};
|
|
1416
1597
|
static PADDING_MAP = {
|
|
1417
1598
|
none: '0',
|
|
1418
1599
|
sm: 'var(--space-3, 0.75rem)',
|
|
1419
1600
|
md: 'var(--space-5, 1.25rem)',
|
|
1420
|
-
lg: 'var(--space-7, 2rem)'
|
|
1601
|
+
lg: 'var(--space-7, 2rem)',
|
|
1421
1602
|
};
|
|
1422
1603
|
cardClasses = computed(() => {
|
|
1423
1604
|
const classes = ['ct-card'];
|
|
@@ -1441,24 +1622,36 @@ class AfCardComponent {
|
|
|
1441
1622
|
this.cardClick.emit();
|
|
1442
1623
|
}
|
|
1443
1624
|
}
|
|
1625
|
+
onCardKeydown(event) {
|
|
1626
|
+
if (!this.interactive())
|
|
1627
|
+
return;
|
|
1628
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
1629
|
+
event.preventDefault();
|
|
1630
|
+
this.cardClick.emit();
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1444
1633
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: AfCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1445
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.2", type: AfCardComponent, isStandalone: true, selector: "af-card", inputs: { interactive: { classPropertyName: "interactive", publicName: "interactive", isSignal: true, isRequired: false, transformFunction: null }, elevation: { classPropertyName: "elevation", publicName: "elevation", isSignal: true, isRequired: false, transformFunction: null }, padding: { classPropertyName: "padding", publicName: "padding", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { cardClick: "cardClick" }, queries: [{ propertyName: "
|
|
1634
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.2", type: AfCardComponent, isStandalone: true, selector: "af-card", inputs: { interactive: { classPropertyName: "interactive", publicName: "interactive", isSignal: true, isRequired: false, transformFunction: null }, elevation: { classPropertyName: "elevation", publicName: "elevation", isSignal: true, isRequired: false, transformFunction: null }, padding: { classPropertyName: "padding", publicName: "padding", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { cardClick: "cardClick" }, queries: [{ propertyName: "headerRef", first: true, predicate: ["[header]"], descendants: true, read: ElementRef, isSignal: true }, { propertyName: "footerRef", first: true, predicate: ["[footer]"], descendants: true, read: ElementRef, isSignal: true }], ngImport: i0, template: `
|
|
1446
1635
|
<section
|
|
1447
1636
|
[class]="cardClasses()"
|
|
1448
1637
|
[style]="cardStyles()"
|
|
1449
|
-
|
|
1638
|
+
[attr.role]="interactive() ? 'button' : null"
|
|
1639
|
+
[attr.tabindex]="interactive() ? 0 : null"
|
|
1640
|
+
[attr.aria-label]="ariaLabel() || null"
|
|
1641
|
+
(click)="onCardClick()"
|
|
1642
|
+
(keydown)="onCardKeydown($event)">
|
|
1450
1643
|
@if (hasHeader()) {
|
|
1451
1644
|
<div class="ct-card__header">
|
|
1452
|
-
<ng-content select="[header]"
|
|
1645
|
+
<ng-content select="[header]" />
|
|
1453
1646
|
</div>
|
|
1454
1647
|
}
|
|
1455
1648
|
<div class="ct-card__body">
|
|
1456
|
-
<ng-content select="[body]"
|
|
1457
|
-
<ng-content
|
|
1649
|
+
<ng-content select="[body]" />
|
|
1650
|
+
<ng-content />
|
|
1458
1651
|
</div>
|
|
1459
1652
|
@if (hasFooter()) {
|
|
1460
1653
|
<div class="ct-card__footer">
|
|
1461
|
-
<ng-content select="[footer]"
|
|
1654
|
+
<ng-content select="[footer]" />
|
|
1462
1655
|
</div>
|
|
1463
1656
|
}
|
|
1464
1657
|
</section>
|
|
@@ -1470,30 +1663,28 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImpor
|
|
|
1470
1663
|
<section
|
|
1471
1664
|
[class]="cardClasses()"
|
|
1472
1665
|
[style]="cardStyles()"
|
|
1473
|
-
|
|
1666
|
+
[attr.role]="interactive() ? 'button' : null"
|
|
1667
|
+
[attr.tabindex]="interactive() ? 0 : null"
|
|
1668
|
+
[attr.aria-label]="ariaLabel() || null"
|
|
1669
|
+
(click)="onCardClick()"
|
|
1670
|
+
(keydown)="onCardKeydown($event)">
|
|
1474
1671
|
@if (hasHeader()) {
|
|
1475
1672
|
<div class="ct-card__header">
|
|
1476
|
-
<ng-content select="[header]"
|
|
1673
|
+
<ng-content select="[header]" />
|
|
1477
1674
|
</div>
|
|
1478
1675
|
}
|
|
1479
1676
|
<div class="ct-card__body">
|
|
1480
|
-
<ng-content select="[body]"
|
|
1481
|
-
<ng-content
|
|
1677
|
+
<ng-content select="[body]" />
|
|
1678
|
+
<ng-content />
|
|
1482
1679
|
</div>
|
|
1483
1680
|
@if (hasFooter()) {
|
|
1484
1681
|
<div class="ct-card__footer">
|
|
1485
|
-
<ng-content select="[footer]"
|
|
1682
|
+
<ng-content select="[footer]" />
|
|
1486
1683
|
</div>
|
|
1487
1684
|
}
|
|
1488
1685
|
</section>
|
|
1489
1686
|
`, styles: [":host{display:block}\n"] }]
|
|
1490
|
-
}], propDecorators: { interactive: [{ type: i0.Input, args: [{ isSignal: true, alias: "interactive", required: false }] }], elevation: [{ type: i0.Input, args: [{ isSignal: true, alias: "elevation", required: false }] }], padding: [{ type: i0.Input, args: [{ isSignal: true, alias: "padding", required: false }] }], cardClick: [{ type: i0.Output, args: ["cardClick"] }],
|
|
1491
|
-
type: ContentChild,
|
|
1492
|
-
args: ['[header]', { read: ElementRef }]
|
|
1493
|
-
}], footerContent: [{
|
|
1494
|
-
type: ContentChild,
|
|
1495
|
-
args: ['[footer]', { read: ElementRef }]
|
|
1496
|
-
}] } });
|
|
1687
|
+
}], propDecorators: { interactive: [{ type: i0.Input, args: [{ isSignal: true, alias: "interactive", required: false }] }], elevation: [{ type: i0.Input, args: [{ isSignal: true, alias: "elevation", required: false }] }], padding: [{ type: i0.Input, args: [{ isSignal: true, alias: "padding", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], cardClick: [{ type: i0.Output, args: ["cardClick"] }], headerRef: [{ type: i0.ContentChild, args: ['[header]', { ...{ read: ElementRef }, isSignal: true }] }], footerRef: [{ type: i0.ContentChild, args: ['[footer]', { ...{ read: ElementRef }, isSignal: true }] }] } });
|
|
1497
1688
|
|
|
1498
1689
|
/**
|
|
1499
1690
|
* Directive for defining custom cell templates in AfDataTableComponent.
|
|
@@ -1721,6 +1912,85 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImpor
|
|
|
1721
1912
|
args: [{ selector: 'af-data-table', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgTemplateOutlet], template: "<div class=\"ct-data-table ct-data-table--simple\">\n <div class=\"ct-data-table__table\">\n <table [class]=\"tableClasses()\">\n <thead>\n <tr>\n @if (isSelectable()) {\n <th scope=\"col\" class=\"ct-table__cell--checkbox\">\n <label class=\"ct-check\">\n <input\n class=\"ct-check__input\"\n type=\"checkbox\"\n [checked]=\"allSelected\"\n [indeterminate]=\"someSelected\"\n (change)=\"toggleAll($any($event.target).checked)\"\n aria-label=\"Select all rows\" />\n </label>\n </th>\n }\n @for (col of columns(); track col.key) {\n <th scope=\"col\" [attr.aria-sort]=\"getAriaSort(col)\">\n @if (col.sortable) {\n <button class=\"ct-table__sort\" type=\"button\" (click)=\"toggleSort(col)\">\n {{ col.header }}\n <span class=\"ct-table__sort-indicator\" aria-hidden=\"true\"></span>\n </button>\n } @else {\n {{ col.header }}\n }\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @for (row of sortedData(); track row) {\n <tr (click)=\"onRowClick(row)\">\n @if (isSelectable()) {\n <td class=\"ct-table__cell--checkbox\">\n <label class=\"ct-check\">\n <input\n class=\"ct-check__input\"\n type=\"checkbox\"\n [checked]=\"isSelected(row)\"\n (change)=\"toggleSelection(row, $event)\"\n aria-label=\"Select row\" />\n </label>\n </td>\n }\n @for (col of columns(); track col.key) {\n <td [class]=\"col.cellClass || ''\">\n @if (getCellTemplate(col.key); as tmpl) {\n <ng-container *ngTemplateOutlet=\"tmpl; context: { $implicit: row, column: col }\"></ng-container>\n } @else {\n {{ row[col.key] }}\n }\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n</div>\n", styles: [":host{display:block}.ct-data-table__table tr{cursor:pointer}.ct-data-table__table tr:hover{background-color:var(--color-neutral-50)}\n"] }]
|
|
1722
1913
|
}], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: false }] }], sort: [{ type: i0.Input, args: [{ isSignal: true, alias: "sort", required: false }] }], rowId: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowId", required: false }] }], rowClick: [{ type: i0.Output, args: ["rowClick"] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], sortChange: [{ type: i0.Output, args: ["sortChange"] }], cellDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => AfCellDefDirective), { isSignal: true }] }] } });
|
|
1723
1914
|
|
|
1915
|
+
const FOCUSABLE_SELECTORS = [
|
|
1916
|
+
'a[href]',
|
|
1917
|
+
'area[href]',
|
|
1918
|
+
'button:not([disabled])',
|
|
1919
|
+
'input:not([disabled])',
|
|
1920
|
+
'select:not([disabled])',
|
|
1921
|
+
'textarea:not([disabled])',
|
|
1922
|
+
'[tabindex]:not([tabindex="-1"])',
|
|
1923
|
+
].join(',');
|
|
1924
|
+
/**
|
|
1925
|
+
* Manages focus trapping within a container element.
|
|
1926
|
+
*
|
|
1927
|
+
* Handles Tab/Shift+Tab cycling, save/restore of the previously focused
|
|
1928
|
+
* element, and initial focus placement. Create one instance per overlay
|
|
1929
|
+
* (modal, drawer, popover, etc.).
|
|
1930
|
+
*/
|
|
1931
|
+
class FocusTrap {
|
|
1932
|
+
previousActiveElement = null;
|
|
1933
|
+
/** Saves the currently focused element for later restoration. */
|
|
1934
|
+
saveFocus() {
|
|
1935
|
+
this.previousActiveElement = document.activeElement;
|
|
1936
|
+
}
|
|
1937
|
+
/** Restores focus to the element saved via `saveFocus()`. */
|
|
1938
|
+
restoreFocus() {
|
|
1939
|
+
if (this.previousActiveElement) {
|
|
1940
|
+
this.previousActiveElement.focus();
|
|
1941
|
+
this.previousActiveElement = null;
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1944
|
+
/** Overrides the saved focus target (e.g. to return focus to a specific trigger). */
|
|
1945
|
+
setReturnFocus(element) {
|
|
1946
|
+
this.previousActiveElement = element;
|
|
1947
|
+
}
|
|
1948
|
+
/**
|
|
1949
|
+
* Focuses the first focusable element inside the container,
|
|
1950
|
+
* or the fallback element if no focusable children exist.
|
|
1951
|
+
*/
|
|
1952
|
+
focusFirst(container, fallback) {
|
|
1953
|
+
const elements = queryFocusableElements(container);
|
|
1954
|
+
const first = elements[0];
|
|
1955
|
+
if (first) {
|
|
1956
|
+
first.focus();
|
|
1957
|
+
}
|
|
1958
|
+
else {
|
|
1959
|
+
fallback?.focus();
|
|
1960
|
+
}
|
|
1961
|
+
}
|
|
1962
|
+
/**
|
|
1963
|
+
* Handles a Tab keydown event to trap focus within the container.
|
|
1964
|
+
* Call this from a `(keydown)` handler when the key is `Tab`.
|
|
1965
|
+
*/
|
|
1966
|
+
handleTab(event, container, fallback) {
|
|
1967
|
+
const elements = queryFocusableElements(container);
|
|
1968
|
+
if (elements.length === 0) {
|
|
1969
|
+
event.preventDefault();
|
|
1970
|
+
fallback?.focus();
|
|
1971
|
+
return;
|
|
1972
|
+
}
|
|
1973
|
+
const first = elements[0];
|
|
1974
|
+
const last = elements[elements.length - 1];
|
|
1975
|
+
const active = document.activeElement;
|
|
1976
|
+
if (event.shiftKey && active === first) {
|
|
1977
|
+
event.preventDefault();
|
|
1978
|
+
last.focus();
|
|
1979
|
+
}
|
|
1980
|
+
else if (!event.shiftKey && active === last) {
|
|
1981
|
+
event.preventDefault();
|
|
1982
|
+
first.focus();
|
|
1983
|
+
}
|
|
1984
|
+
}
|
|
1985
|
+
}
|
|
1986
|
+
/** Queries all visible, enabled, focusable elements within a container. */
|
|
1987
|
+
function queryFocusableElements(container) {
|
|
1988
|
+
if (!container)
|
|
1989
|
+
return [];
|
|
1990
|
+
return Array.from(container.querySelectorAll(FOCUSABLE_SELECTORS)).filter((el) => !el.hasAttribute('disabled') &&
|
|
1991
|
+
el.getAttribute('aria-hidden') !== 'true');
|
|
1992
|
+
}
|
|
1993
|
+
|
|
1724
1994
|
/**
|
|
1725
1995
|
* Modal/Dialog component with accessibility features
|
|
1726
1996
|
*
|
|
@@ -1740,6 +2010,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImpor
|
|
|
1740
2010
|
*/
|
|
1741
2011
|
class AfModalComponent {
|
|
1742
2012
|
static nextId = 0;
|
|
2013
|
+
focusTrap = new FocusTrap();
|
|
1743
2014
|
/** Whether modal is open */
|
|
1744
2015
|
open = input(false, ...(ngDevMode ? [{ debugName: "open" }] : []));
|
|
1745
2016
|
/** Modal title */
|
|
@@ -1752,12 +2023,8 @@ class AfModalComponent {
|
|
|
1752
2023
|
closed = output();
|
|
1753
2024
|
/** Unique title ID for aria-labelledby */
|
|
1754
2025
|
titleId = `af-modal-title-${AfModalComponent.nextId++}`;
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
this.hasFooter.set(!!value);
|
|
1758
|
-
}
|
|
1759
|
-
previousActiveElement = null;
|
|
1760
|
-
focusableElements = [];
|
|
2026
|
+
footerRef = contentChild('[footer]', { ...(ngDevMode ? { debugName: "footerRef" } : {}), read: ElementRef });
|
|
2027
|
+
hasFooter = computed(() => !!this.footerRef(), ...(ngDevMode ? [{ debugName: "hasFooter" }] : []));
|
|
1761
2028
|
viewInitialized = signal(false, ...(ngDevMode ? [{ debugName: "viewInitialized" }] : []));
|
|
1762
2029
|
dialogRef = viewChild('dialog', ...(ngDevMode ? [{ debugName: "dialogRef" }] : []));
|
|
1763
2030
|
openEffect = effect(() => {
|
|
@@ -1767,14 +2034,14 @@ class AfModalComponent {
|
|
|
1767
2034
|
this.onOpen();
|
|
1768
2035
|
}
|
|
1769
2036
|
else if (!isOpen) {
|
|
1770
|
-
this.restoreFocus();
|
|
2037
|
+
this.focusTrap.restoreFocus();
|
|
1771
2038
|
}
|
|
1772
2039
|
}, ...(ngDevMode ? [{ debugName: "openEffect" }] : []));
|
|
1773
2040
|
ngAfterViewInit() {
|
|
1774
2041
|
this.viewInitialized.set(true);
|
|
1775
2042
|
}
|
|
1776
2043
|
ngOnDestroy() {
|
|
1777
|
-
this.restoreFocus();
|
|
2044
|
+
this.focusTrap.restoreFocus();
|
|
1778
2045
|
}
|
|
1779
2046
|
onEscapeKey() {
|
|
1780
2047
|
if (this.open()) {
|
|
@@ -1784,23 +2051,7 @@ class AfModalComponent {
|
|
|
1784
2051
|
onKeydown(event) {
|
|
1785
2052
|
if (!this.open() || event.key !== 'Tab')
|
|
1786
2053
|
return;
|
|
1787
|
-
this.
|
|
1788
|
-
if (this.focusableElements.length === 0) {
|
|
1789
|
-
event.preventDefault();
|
|
1790
|
-
this.dialogRef()?.nativeElement.focus();
|
|
1791
|
-
return;
|
|
1792
|
-
}
|
|
1793
|
-
const first = this.focusableElements[0];
|
|
1794
|
-
const last = this.focusableElements[this.focusableElements.length - 1];
|
|
1795
|
-
const active = document.activeElement;
|
|
1796
|
-
if (event.shiftKey && active === first) {
|
|
1797
|
-
event.preventDefault();
|
|
1798
|
-
last.focus();
|
|
1799
|
-
}
|
|
1800
|
-
else if (!event.shiftKey && active === last) {
|
|
1801
|
-
event.preventDefault();
|
|
1802
|
-
first.focus();
|
|
1803
|
-
}
|
|
2054
|
+
this.focusTrap.handleTab(event, this.dialogRef()?.nativeElement, this.dialogRef()?.nativeElement);
|
|
1804
2055
|
}
|
|
1805
2056
|
onBackdropClick(event) {
|
|
1806
2057
|
if (this.closeOnBackdropClick() && event.target === event.currentTarget) {
|
|
@@ -1811,46 +2062,15 @@ class AfModalComponent {
|
|
|
1811
2062
|
this.closed.emit();
|
|
1812
2063
|
}
|
|
1813
2064
|
onOpen() {
|
|
1814
|
-
this.
|
|
1815
|
-
this.refreshFocusableElements();
|
|
2065
|
+
this.focusTrap.saveFocus();
|
|
1816
2066
|
queueMicrotask(() => {
|
|
1817
2067
|
if (!this.open())
|
|
1818
2068
|
return;
|
|
1819
|
-
|
|
1820
|
-
if (first) {
|
|
1821
|
-
first.focus();
|
|
1822
|
-
}
|
|
1823
|
-
else {
|
|
1824
|
-
this.dialogRef()?.nativeElement.focus();
|
|
1825
|
-
}
|
|
2069
|
+
this.focusTrap.focusFirst(this.dialogRef()?.nativeElement, this.dialogRef()?.nativeElement);
|
|
1826
2070
|
});
|
|
1827
2071
|
}
|
|
1828
|
-
restoreFocus() {
|
|
1829
|
-
if (this.previousActiveElement) {
|
|
1830
|
-
this.previousActiveElement.focus();
|
|
1831
|
-
this.previousActiveElement = null;
|
|
1832
|
-
}
|
|
1833
|
-
}
|
|
1834
|
-
refreshFocusableElements() {
|
|
1835
|
-
const dialog = this.dialogRef()?.nativeElement;
|
|
1836
|
-
if (!dialog) {
|
|
1837
|
-
this.focusableElements = [];
|
|
1838
|
-
return;
|
|
1839
|
-
}
|
|
1840
|
-
const selectors = [
|
|
1841
|
-
'a[href]',
|
|
1842
|
-
'area[href]',
|
|
1843
|
-
'button:not([disabled])',
|
|
1844
|
-
'input:not([disabled])',
|
|
1845
|
-
'select:not([disabled])',
|
|
1846
|
-
'textarea:not([disabled])',
|
|
1847
|
-
'[tabindex]:not([tabindex="-1"])'
|
|
1848
|
-
];
|
|
1849
|
-
this.focusableElements = Array.from(dialog.querySelectorAll(selectors.join(',')))
|
|
1850
|
-
.filter(el => !el.hasAttribute('disabled') && el.getAttribute('aria-hidden') !== 'true');
|
|
1851
|
-
}
|
|
1852
2072
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: AfModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1853
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.2", type: AfModalComponent, isStandalone: true, selector: "af-modal", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, showCloseButton: { classPropertyName: "showCloseButton", publicName: "showCloseButton", isSignal: true, isRequired: false, transformFunction: null }, closeOnBackdropClick: { classPropertyName: "closeOnBackdropClick", publicName: "closeOnBackdropClick", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closed: "closed" }, host: { listeners: { "document:keydown.escape": "onEscapeKey()", "document:keydown": "onKeydown($event)" } }, queries: [{ propertyName: "
|
|
2073
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.2", type: AfModalComponent, isStandalone: true, selector: "af-modal", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, showCloseButton: { classPropertyName: "showCloseButton", publicName: "showCloseButton", isSignal: true, isRequired: false, transformFunction: null }, closeOnBackdropClick: { classPropertyName: "closeOnBackdropClick", publicName: "closeOnBackdropClick", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closed: "closed" }, host: { listeners: { "document:keydown.escape": "onEscapeKey()", "document:keydown": "onKeydown($event)" } }, queries: [{ propertyName: "footerRef", first: true, predicate: ["[footer]"], descendants: true, read: ElementRef, isSignal: true }], viewQueries: [{ propertyName: "dialogRef", first: true, predicate: ["dialog"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
1854
2074
|
@if (open()) {
|
|
1855
2075
|
<div
|
|
1856
2076
|
class="ct-modal"
|
|
@@ -1932,10 +2152,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImpor
|
|
|
1932
2152
|
</div>
|
|
1933
2153
|
}
|
|
1934
2154
|
`, styles: [":host{display:contents}\n"] }]
|
|
1935
|
-
}], propDecorators: { open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], showCloseButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCloseButton", required: false }] }], closeOnBackdropClick: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnBackdropClick", required: false }] }], closed: [{ type: i0.Output, args: ["closed"] }],
|
|
1936
|
-
type: ContentChild,
|
|
1937
|
-
args: ['[footer]', { read: ElementRef }]
|
|
1938
|
-
}], dialogRef: [{ type: i0.ViewChild, args: ['dialog', { isSignal: true }] }] } });
|
|
2155
|
+
}], propDecorators: { open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], showCloseButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCloseButton", required: false }] }], closeOnBackdropClick: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnBackdropClick", required: false }] }], closed: [{ type: i0.Output, args: ["closed"] }], footerRef: [{ type: i0.ContentChild, args: ['[footer]', { ...{ read: ElementRef }, isSignal: true }] }], dialogRef: [{ type: i0.ViewChild, args: ['dialog', { isSignal: true }] }] } });
|
|
1939
2156
|
|
|
1940
2157
|
/**
|
|
1941
2158
|
* Toast notification service
|
|
@@ -2297,7 +2514,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImpor
|
|
|
2297
2514
|
}], propDecorators: { activeTab: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeTab", required: false }] }, { type: i0.Output, args: ["activeTabChange"] }], panels: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => AfTabPanelComponent), { isSignal: true }] }], tabButtons: [{ type: i0.ViewChildren, args: ['tabButton', { isSignal: true }] }] } });
|
|
2298
2515
|
|
|
2299
2516
|
/**
|
|
2300
|
-
* Dropdown menu component
|
|
2517
|
+
* Dropdown menu component implementing the WAI-ARIA Menu Pattern.
|
|
2518
|
+
*
|
|
2519
|
+
* Provides full keyboard navigation (Arrow keys, Home/End, type-ahead),
|
|
2520
|
+
* proper ARIA roles (`menu` / `menuitem`), and roving tabindex focus management.
|
|
2301
2521
|
*
|
|
2302
2522
|
* @example
|
|
2303
2523
|
* <af-dropdown
|
|
@@ -2308,19 +2528,25 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImpor
|
|
|
2308
2528
|
*/
|
|
2309
2529
|
class AfDropdownComponent {
|
|
2310
2530
|
static nextId = 0;
|
|
2311
|
-
/** Dropdown button label */
|
|
2531
|
+
/** Dropdown button label. */
|
|
2312
2532
|
label = input('Actions', ...(ngDevMode ? [{ debugName: "label" }] : []));
|
|
2313
|
-
/** Menu items */
|
|
2533
|
+
/** Menu items. */
|
|
2314
2534
|
items = input([], ...(ngDevMode ? [{ debugName: "items" }] : []));
|
|
2315
|
-
/**
|
|
2535
|
+
/** Emits the selected item's value. */
|
|
2316
2536
|
itemSelected = output();
|
|
2317
2537
|
triggerRef = viewChild('trigger', ...(ngDevMode ? [{ debugName: "triggerRef" }] : []));
|
|
2538
|
+
menuRef = viewChild('menu', ...(ngDevMode ? [{ debugName: "menuRef" }] : []));
|
|
2318
2539
|
itemButtons = viewChildren('itemButton', ...(ngDevMode ? [{ debugName: "itemButtons" }] : []));
|
|
2319
2540
|
isOpen = signal(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : []));
|
|
2320
|
-
|
|
2541
|
+
focusedItemIndex = signal(0, ...(ngDevMode ? [{ debugName: "focusedItemIndex" }] : []));
|
|
2542
|
+
instanceId = AfDropdownComponent.nextId++;
|
|
2543
|
+
menuId = `af-dropdown-menu-${this.instanceId}`;
|
|
2544
|
+
triggerId = `af-dropdown-trigger-${this.instanceId}`;
|
|
2545
|
+
typeAheadBuffer = '';
|
|
2546
|
+
typeAheadTimer = null;
|
|
2321
2547
|
toggle() {
|
|
2322
2548
|
if (this.isOpen()) {
|
|
2323
|
-
this.close();
|
|
2549
|
+
this.close(true);
|
|
2324
2550
|
}
|
|
2325
2551
|
else {
|
|
2326
2552
|
this.open();
|
|
@@ -2332,46 +2558,180 @@ class AfDropdownComponent {
|
|
|
2332
2558
|
this.close(true);
|
|
2333
2559
|
}
|
|
2334
2560
|
}
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2561
|
+
/** Handles keyboard events on the trigger button. */
|
|
2562
|
+
onTriggerKeydown(event) {
|
|
2563
|
+
switch (event.key) {
|
|
2564
|
+
case 'ArrowDown':
|
|
2565
|
+
case 'Enter':
|
|
2566
|
+
case ' ':
|
|
2567
|
+
event.preventDefault();
|
|
2568
|
+
if (!this.isOpen()) {
|
|
2569
|
+
this.open();
|
|
2570
|
+
}
|
|
2571
|
+
break;
|
|
2572
|
+
case 'ArrowUp':
|
|
2573
|
+
event.preventDefault();
|
|
2574
|
+
if (!this.isOpen()) {
|
|
2575
|
+
this.open(true);
|
|
2576
|
+
}
|
|
2577
|
+
break;
|
|
2343
2578
|
}
|
|
2344
2579
|
}
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2580
|
+
/** Handles keyboard events within the open menu. */
|
|
2581
|
+
onMenuKeydown(event) {
|
|
2582
|
+
const actionableItems = this.getActionableItems();
|
|
2583
|
+
if (actionableItems.length === 0)
|
|
2584
|
+
return;
|
|
2585
|
+
switch (event.key) {
|
|
2586
|
+
case 'ArrowDown': {
|
|
2587
|
+
event.preventDefault();
|
|
2588
|
+
const next = this.nextEnabledIndex(this.focusedItemIndex(), 1);
|
|
2589
|
+
this.focusItem(next);
|
|
2590
|
+
break;
|
|
2591
|
+
}
|
|
2592
|
+
case 'ArrowUp': {
|
|
2593
|
+
event.preventDefault();
|
|
2594
|
+
const prev = this.nextEnabledIndex(this.focusedItemIndex(), -1);
|
|
2595
|
+
this.focusItem(prev);
|
|
2596
|
+
break;
|
|
2597
|
+
}
|
|
2598
|
+
case 'Home': {
|
|
2599
|
+
event.preventDefault();
|
|
2600
|
+
const first = this.nextEnabledIndex(-1, 1);
|
|
2601
|
+
this.focusItem(first);
|
|
2602
|
+
break;
|
|
2603
|
+
}
|
|
2604
|
+
case 'End': {
|
|
2605
|
+
event.preventDefault();
|
|
2606
|
+
const last = this.nextEnabledIndex(actionableItems.length, -1);
|
|
2607
|
+
this.focusItem(last);
|
|
2608
|
+
break;
|
|
2609
|
+
}
|
|
2610
|
+
case 'Escape':
|
|
2611
|
+
event.preventDefault();
|
|
2612
|
+
this.close(true);
|
|
2613
|
+
break;
|
|
2614
|
+
case 'Tab':
|
|
2615
|
+
this.close(false);
|
|
2616
|
+
break;
|
|
2617
|
+
case 'Enter':
|
|
2618
|
+
case ' ': {
|
|
2619
|
+
event.preventDefault();
|
|
2620
|
+
const item = actionableItems[this.focusedItemIndex()];
|
|
2621
|
+
if (item && !item.disabled) {
|
|
2622
|
+
this.selectItem(item);
|
|
2623
|
+
}
|
|
2624
|
+
break;
|
|
2625
|
+
}
|
|
2626
|
+
default:
|
|
2627
|
+
if (event.key.length === 1 && !event.ctrlKey && !event.metaKey) {
|
|
2628
|
+
this.handleTypeAhead(event.key);
|
|
2629
|
+
}
|
|
2630
|
+
}
|
|
2348
2631
|
}
|
|
2349
2632
|
onDocumentClick(event) {
|
|
2350
2633
|
const target = event.target;
|
|
2351
2634
|
if (!target.closest('.ct-dropdown')) {
|
|
2352
|
-
this.close();
|
|
2635
|
+
this.close(false);
|
|
2353
2636
|
}
|
|
2354
2637
|
}
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2638
|
+
/**
|
|
2639
|
+
* Returns the index of a non-separator item within the list of
|
|
2640
|
+
* actionable (non-separator) items.
|
|
2641
|
+
*/
|
|
2642
|
+
getActionableIndex(item) {
|
|
2643
|
+
return this.getActionableItems().indexOf(item);
|
|
2644
|
+
}
|
|
2645
|
+
open(focusLast = false) {
|
|
2646
|
+
this.isOpen.set(true);
|
|
2647
|
+
const actionableItems = this.getActionableItems();
|
|
2648
|
+
const startIndex = focusLast
|
|
2649
|
+
? this.nextEnabledIndex(actionableItems.length, -1)
|
|
2650
|
+
: this.nextEnabledIndex(-1, 1);
|
|
2651
|
+
this.focusedItemIndex.set(startIndex);
|
|
2652
|
+
queueMicrotask(() => this.focusCurrent());
|
|
2653
|
+
}
|
|
2654
|
+
close(returnFocus) {
|
|
2655
|
+
if (!this.isOpen())
|
|
2656
|
+
return;
|
|
2657
|
+
this.isOpen.set(false);
|
|
2658
|
+
this.typeAheadBuffer = '';
|
|
2659
|
+
if (returnFocus) {
|
|
2660
|
+
this.triggerRef()?.nativeElement.focus();
|
|
2661
|
+
}
|
|
2662
|
+
}
|
|
2663
|
+
focusItem(index) {
|
|
2664
|
+
this.focusedItemIndex.set(index);
|
|
2665
|
+
this.focusCurrent();
|
|
2666
|
+
}
|
|
2667
|
+
focusCurrent() {
|
|
2668
|
+
const buttons = this.itemButtons();
|
|
2669
|
+
const idx = this.focusedItemIndex();
|
|
2670
|
+
buttons[idx]?.nativeElement.focus();
|
|
2671
|
+
}
|
|
2672
|
+
nextEnabledIndex(from, direction) {
|
|
2673
|
+
const actionableItems = this.getActionableItems();
|
|
2674
|
+
const len = actionableItems.length;
|
|
2675
|
+
if (len === 0)
|
|
2676
|
+
return 0;
|
|
2677
|
+
let idx = from + direction;
|
|
2678
|
+
for (let i = 0; i < len; i++) {
|
|
2679
|
+
if (idx < 0)
|
|
2680
|
+
idx = len - 1;
|
|
2681
|
+
if (idx >= len)
|
|
2682
|
+
idx = 0;
|
|
2683
|
+
if (!actionableItems[idx].disabled)
|
|
2684
|
+
return idx;
|
|
2685
|
+
idx += direction;
|
|
2686
|
+
}
|
|
2687
|
+
return from;
|
|
2688
|
+
}
|
|
2689
|
+
getActionableItems() {
|
|
2690
|
+
return this.items().filter((item) => !item.separator);
|
|
2691
|
+
}
|
|
2692
|
+
handleTypeAhead(char) {
|
|
2693
|
+
if (this.typeAheadTimer) {
|
|
2694
|
+
clearTimeout(this.typeAheadTimer);
|
|
2695
|
+
}
|
|
2696
|
+
this.typeAheadBuffer += char.toLowerCase();
|
|
2697
|
+
this.typeAheadTimer = setTimeout(() => {
|
|
2698
|
+
this.typeAheadBuffer = '';
|
|
2699
|
+
this.typeAheadTimer = null;
|
|
2700
|
+
}, 500);
|
|
2701
|
+
const actionableItems = this.getActionableItems();
|
|
2702
|
+
const startIndex = this.focusedItemIndex() + 1;
|
|
2703
|
+
for (let i = 0; i < actionableItems.length; i++) {
|
|
2704
|
+
const idx = (startIndex + i) % actionableItems.length;
|
|
2705
|
+
const item = actionableItems[idx];
|
|
2706
|
+
if (!item.disabled && item.label.toLowerCase().startsWith(this.typeAheadBuffer)) {
|
|
2707
|
+
this.focusItem(idx);
|
|
2708
|
+
return;
|
|
2709
|
+
}
|
|
2358
2710
|
}
|
|
2359
2711
|
}
|
|
2360
2712
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: AfDropdownComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2361
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.2", type: AfDropdownComponent, isStandalone: true, selector: "af-dropdown", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { itemSelected: "itemSelected" }, host: { listeners: { "document:click": "onDocumentClick($event)",
|
|
2713
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.2", type: AfDropdownComponent, isStandalone: true, selector: "af-dropdown", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { itemSelected: "itemSelected" }, host: { listeners: { "document:click": "onDocumentClick($event)" } }, viewQueries: [{ propertyName: "triggerRef", first: true, predicate: ["trigger"], descendants: true, isSignal: true }, { propertyName: "menuRef", first: true, predicate: ["menu"], descendants: true, isSignal: true }, { propertyName: "itemButtons", predicate: ["itemButton"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
2362
2714
|
<div class="ct-dropdown" [attr.data-state]="isOpen() ? 'open' : 'closed'">
|
|
2363
2715
|
<button
|
|
2364
2716
|
#trigger
|
|
2365
2717
|
class="ct-button ct-button--secondary ct-dropdown__trigger"
|
|
2366
2718
|
[attr.aria-expanded]="isOpen()"
|
|
2367
2719
|
[attr.aria-controls]="menuId"
|
|
2368
|
-
|
|
2720
|
+
aria-haspopup="menu"
|
|
2369
2721
|
type="button"
|
|
2370
|
-
(click)="toggle()"
|
|
2722
|
+
(click)="toggle()"
|
|
2723
|
+
(keydown)="onTriggerKeydown($event)">
|
|
2371
2724
|
{{ label() }}
|
|
2372
2725
|
</button>
|
|
2373
2726
|
@if (isOpen()) {
|
|
2374
|
-
<div
|
|
2727
|
+
<div
|
|
2728
|
+
#menu
|
|
2729
|
+
class="ct-dropdown__menu"
|
|
2730
|
+
[id]="menuId"
|
|
2731
|
+
role="menu"
|
|
2732
|
+
aria-orientation="vertical"
|
|
2733
|
+
[attr.aria-labelledby]="triggerId"
|
|
2734
|
+
(keydown)="onMenuKeydown($event)">
|
|
2375
2735
|
@for (item of items(); track $index) {
|
|
2376
2736
|
@if (item.separator) {
|
|
2377
2737
|
<div class="ct-dropdown__separator" role="separator"></div>
|
|
@@ -2379,8 +2739,9 @@ class AfDropdownComponent {
|
|
|
2379
2739
|
<button
|
|
2380
2740
|
#itemButton
|
|
2381
2741
|
class="ct-dropdown__item"
|
|
2382
|
-
|
|
2383
|
-
[attr.
|
|
2742
|
+
role="menuitem"
|
|
2743
|
+
[attr.tabindex]="focusedItemIndex() === getActionableIndex(item) ? 0 : -1"
|
|
2744
|
+
[attr.aria-disabled]="item.disabled ? 'true' : null"
|
|
2384
2745
|
type="button"
|
|
2385
2746
|
(click)="selectItem(item)">
|
|
2386
2747
|
{{ item.label }}
|
|
@@ -2396,7 +2757,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImpor
|
|
|
2396
2757
|
type: Component,
|
|
2397
2758
|
args: [{ selector: 'af-dropdown', changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
2398
2759
|
'(document:click)': 'onDocumentClick($event)',
|
|
2399
|
-
'(document:keydown.escape)': 'onEscape()',
|
|
2400
2760
|
}, template: `
|
|
2401
2761
|
<div class="ct-dropdown" [attr.data-state]="isOpen() ? 'open' : 'closed'">
|
|
2402
2762
|
<button
|
|
@@ -2404,13 +2764,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImpor
|
|
|
2404
2764
|
class="ct-button ct-button--secondary ct-dropdown__trigger"
|
|
2405
2765
|
[attr.aria-expanded]="isOpen()"
|
|
2406
2766
|
[attr.aria-controls]="menuId"
|
|
2407
|
-
|
|
2767
|
+
aria-haspopup="menu"
|
|
2408
2768
|
type="button"
|
|
2409
|
-
(click)="toggle()"
|
|
2769
|
+
(click)="toggle()"
|
|
2770
|
+
(keydown)="onTriggerKeydown($event)">
|
|
2410
2771
|
{{ label() }}
|
|
2411
2772
|
</button>
|
|
2412
2773
|
@if (isOpen()) {
|
|
2413
|
-
<div
|
|
2774
|
+
<div
|
|
2775
|
+
#menu
|
|
2776
|
+
class="ct-dropdown__menu"
|
|
2777
|
+
[id]="menuId"
|
|
2778
|
+
role="menu"
|
|
2779
|
+
aria-orientation="vertical"
|
|
2780
|
+
[attr.aria-labelledby]="triggerId"
|
|
2781
|
+
(keydown)="onMenuKeydown($event)">
|
|
2414
2782
|
@for (item of items(); track $index) {
|
|
2415
2783
|
@if (item.separator) {
|
|
2416
2784
|
<div class="ct-dropdown__separator" role="separator"></div>
|
|
@@ -2418,8 +2786,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImpor
|
|
|
2418
2786
|
<button
|
|
2419
2787
|
#itemButton
|
|
2420
2788
|
class="ct-dropdown__item"
|
|
2421
|
-
|
|
2422
|
-
[attr.
|
|
2789
|
+
role="menuitem"
|
|
2790
|
+
[attr.tabindex]="focusedItemIndex() === getActionableIndex(item) ? 0 : -1"
|
|
2791
|
+
[attr.aria-disabled]="item.disabled ? 'true' : null"
|
|
2423
2792
|
type="button"
|
|
2424
2793
|
(click)="selectItem(item)">
|
|
2425
2794
|
{{ item.label }}
|
|
@@ -2430,7 +2799,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImpor
|
|
|
2430
2799
|
}
|
|
2431
2800
|
</div>
|
|
2432
2801
|
`, styles: [":host{display:inline-block}\n"] }]
|
|
2433
|
-
}], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], itemSelected: [{ type: i0.Output, args: ["itemSelected"] }], triggerRef: [{ type: i0.ViewChild, args: ['trigger', { isSignal: true }] }], itemButtons: [{ type: i0.ViewChildren, args: ['itemButton', { isSignal: true }] }] } });
|
|
2802
|
+
}], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], itemSelected: [{ type: i0.Output, args: ["itemSelected"] }], triggerRef: [{ type: i0.ViewChild, args: ['trigger', { isSignal: true }] }], menuRef: [{ type: i0.ViewChild, args: ['menu', { isSignal: true }] }], itemButtons: [{ type: i0.ViewChildren, args: ['itemButton', { isSignal: true }] }] } });
|
|
2434
2803
|
|
|
2435
2804
|
/**
|
|
2436
2805
|
* Pagination component
|
|
@@ -3490,7 +3859,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImpor
|
|
|
3490
3859
|
* <af-badge variant="danger">Blocked</af-badge>
|
|
3491
3860
|
*/
|
|
3492
3861
|
class AfBadgeComponent {
|
|
3493
|
-
/**
|
|
3862
|
+
/** Accessible label, useful when the badge has no visible text. */
|
|
3863
|
+
ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
|
|
3864
|
+
/** Color variant. */
|
|
3494
3865
|
variant = input('default', ...(ngDevMode ? [{ debugName: "variant" }] : []));
|
|
3495
3866
|
/** Icon character to display */
|
|
3496
3867
|
icon = input('', ...(ngDevMode ? [{ debugName: "icon" }] : []));
|
|
@@ -3507,32 +3878,32 @@ class AfBadgeComponent {
|
|
|
3507
3878
|
return classes.join(' ');
|
|
3508
3879
|
}, ...(ngDevMode ? [{ debugName: "badgeClasses" }] : []));
|
|
3509
3880
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: AfBadgeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3510
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.2", type: AfBadgeComponent, isStandalone: true, selector: "af-badge", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, dot: { classPropertyName: "dot", publicName: "dot", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
|
|
3511
|
-
<span [class]="badgeClasses()">
|
|
3881
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.2", type: AfBadgeComponent, isStandalone: true, selector: "af-badge", inputs: { ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, dot: { classPropertyName: "dot", publicName: "dot", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
|
|
3882
|
+
<span [class]="badgeClasses()" [attr.aria-label]="ariaLabel() || null">
|
|
3512
3883
|
@if (icon()) {
|
|
3513
3884
|
<span class="ct-badge__icon" aria-hidden="true">{{ icon() }}</span>
|
|
3514
3885
|
}
|
|
3515
3886
|
@if (dot()) {
|
|
3516
3887
|
<span class="ct-badge__dot" aria-hidden="true"></span>
|
|
3517
3888
|
}
|
|
3518
|
-
<ng-content
|
|
3889
|
+
<ng-content />
|
|
3519
3890
|
</span>
|
|
3520
3891
|
`, isInline: true, styles: [":host{display:inline-block}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
3521
3892
|
}
|
|
3522
3893
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: AfBadgeComponent, decorators: [{
|
|
3523
3894
|
type: Component,
|
|
3524
3895
|
args: [{ selector: 'af-badge', changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
3525
|
-
<span [class]="badgeClasses()">
|
|
3896
|
+
<span [class]="badgeClasses()" [attr.aria-label]="ariaLabel() || null">
|
|
3526
3897
|
@if (icon()) {
|
|
3527
3898
|
<span class="ct-badge__icon" aria-hidden="true">{{ icon() }}</span>
|
|
3528
3899
|
}
|
|
3529
3900
|
@if (dot()) {
|
|
3530
3901
|
<span class="ct-badge__dot" aria-hidden="true"></span>
|
|
3531
3902
|
}
|
|
3532
|
-
<ng-content
|
|
3903
|
+
<ng-content />
|
|
3533
3904
|
</span>
|
|
3534
3905
|
`, styles: [":host{display:inline-block}\n"] }]
|
|
3535
|
-
}], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], dot: [{ type: i0.Input, args: [{ isSignal: true, alias: "dot", required: false }] }] } });
|
|
3906
|
+
}], propDecorators: { ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], dot: [{ type: i0.Input, args: [{ isSignal: true, alias: "dot", required: false }] }] } });
|
|
3536
3907
|
|
|
3537
3908
|
/**
|
|
3538
3909
|
* Progress bar for showing completion state
|
|
@@ -4130,6 +4501,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImpor
|
|
|
4130
4501
|
*/
|
|
4131
4502
|
class AfDrawerComponent {
|
|
4132
4503
|
static nextId = 0;
|
|
4504
|
+
focusTrap = new FocusTrap();
|
|
4133
4505
|
/** Two-way bindable open state */
|
|
4134
4506
|
open = model(false, ...(ngDevMode ? [{ debugName: "open" }] : []));
|
|
4135
4507
|
/** Slide-in position */
|
|
@@ -4149,8 +4521,6 @@ class AfDrawerComponent {
|
|
|
4149
4521
|
/** Unique ID for aria-labelledby fallback */
|
|
4150
4522
|
titleId = `af-drawer-title-${AfDrawerComponent.nextId++}`;
|
|
4151
4523
|
panelRef = viewChild('panel', ...(ngDevMode ? [{ debugName: "panelRef" }] : []));
|
|
4152
|
-
previousActiveElement = null;
|
|
4153
|
-
focusableElements = [];
|
|
4154
4524
|
containerClasses = computed(() => {
|
|
4155
4525
|
const classes = ['ct-drawer'];
|
|
4156
4526
|
const pos = this.position();
|
|
@@ -4174,7 +4544,7 @@ class AfDrawerComponent {
|
|
|
4174
4544
|
}, ...(ngDevMode ? [{ debugName: "openEffect" }] : []));
|
|
4175
4545
|
ngOnDestroy() {
|
|
4176
4546
|
this.unlockBodyScroll();
|
|
4177
|
-
this.restoreFocus();
|
|
4547
|
+
this.focusTrap.restoreFocus();
|
|
4178
4548
|
}
|
|
4179
4549
|
/** Closes the drawer and emits the closed event */
|
|
4180
4550
|
close() {
|
|
@@ -4194,53 +4564,23 @@ class AfDrawerComponent {
|
|
|
4194
4564
|
return;
|
|
4195
4565
|
}
|
|
4196
4566
|
if (event.key === 'Tab') {
|
|
4197
|
-
this.
|
|
4567
|
+
const panel = this.panelRef()?.nativeElement;
|
|
4568
|
+
this.focusTrap.handleTab(event, panel, panel);
|
|
4198
4569
|
}
|
|
4199
4570
|
}
|
|
4200
4571
|
onOpen() {
|
|
4201
|
-
this.
|
|
4572
|
+
this.focusTrap.saveFocus();
|
|
4202
4573
|
this.lockBodyScroll();
|
|
4203
4574
|
queueMicrotask(() => {
|
|
4204
4575
|
if (!this.open())
|
|
4205
4576
|
return;
|
|
4206
|
-
this.
|
|
4207
|
-
|
|
4208
|
-
if (first) {
|
|
4209
|
-
first.focus();
|
|
4210
|
-
}
|
|
4211
|
-
else {
|
|
4212
|
-
this.panelRef()?.nativeElement.focus();
|
|
4213
|
-
}
|
|
4577
|
+
const panel = this.panelRef()?.nativeElement;
|
|
4578
|
+
this.focusTrap.focusFirst(panel, panel);
|
|
4214
4579
|
});
|
|
4215
4580
|
}
|
|
4216
4581
|
onClose() {
|
|
4217
4582
|
this.unlockBodyScroll();
|
|
4218
|
-
this.restoreFocus();
|
|
4219
|
-
}
|
|
4220
|
-
trapFocus(event) {
|
|
4221
|
-
this.refreshFocusableElements();
|
|
4222
|
-
if (this.focusableElements.length === 0) {
|
|
4223
|
-
event.preventDefault();
|
|
4224
|
-
this.panelRef()?.nativeElement.focus();
|
|
4225
|
-
return;
|
|
4226
|
-
}
|
|
4227
|
-
const first = this.focusableElements[0];
|
|
4228
|
-
const last = this.focusableElements[this.focusableElements.length - 1];
|
|
4229
|
-
const active = document.activeElement;
|
|
4230
|
-
if (event.shiftKey && active === first) {
|
|
4231
|
-
event.preventDefault();
|
|
4232
|
-
last.focus();
|
|
4233
|
-
}
|
|
4234
|
-
else if (!event.shiftKey && active === last) {
|
|
4235
|
-
event.preventDefault();
|
|
4236
|
-
first.focus();
|
|
4237
|
-
}
|
|
4238
|
-
}
|
|
4239
|
-
restoreFocus() {
|
|
4240
|
-
if (this.previousActiveElement) {
|
|
4241
|
-
this.previousActiveElement.focus();
|
|
4242
|
-
this.previousActiveElement = null;
|
|
4243
|
-
}
|
|
4583
|
+
this.focusTrap.restoreFocus();
|
|
4244
4584
|
}
|
|
4245
4585
|
lockBodyScroll() {
|
|
4246
4586
|
document.body.style.overflow = 'hidden';
|
|
@@ -4248,24 +4588,6 @@ class AfDrawerComponent {
|
|
|
4248
4588
|
unlockBodyScroll() {
|
|
4249
4589
|
document.body.style.overflow = '';
|
|
4250
4590
|
}
|
|
4251
|
-
refreshFocusableElements() {
|
|
4252
|
-
const panel = this.panelRef()?.nativeElement;
|
|
4253
|
-
if (!panel) {
|
|
4254
|
-
this.focusableElements = [];
|
|
4255
|
-
return;
|
|
4256
|
-
}
|
|
4257
|
-
const selectors = [
|
|
4258
|
-
'a[href]',
|
|
4259
|
-
'area[href]',
|
|
4260
|
-
'button:not([disabled])',
|
|
4261
|
-
'input:not([disabled])',
|
|
4262
|
-
'select:not([disabled])',
|
|
4263
|
-
'textarea:not([disabled])',
|
|
4264
|
-
'[tabindex]:not([tabindex="-1"])',
|
|
4265
|
-
];
|
|
4266
|
-
this.focusableElements = Array.from(panel.querySelectorAll(selectors.join(','))).filter((el) => !el.hasAttribute('disabled') &&
|
|
4267
|
-
el.getAttribute('aria-hidden') !== 'true');
|
|
4268
|
-
}
|
|
4269
4591
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: AfDrawerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4270
4592
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.2", type: AfDrawerComponent, isStandalone: true, selector: "af-drawer", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, showCloseButton: { classPropertyName: "showCloseButton", publicName: "showCloseButton", isSignal: true, isRequired: false, transformFunction: null }, closeOnBackdropClick: { classPropertyName: "closeOnBackdropClick", publicName: "closeOnBackdropClick", isSignal: true, isRequired: false, transformFunction: null }, closeButtonAriaLabel: { classPropertyName: "closeButtonAriaLabel", publicName: "closeButtonAriaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { open: "openChange", closed: "closed" }, host: { listeners: { "document:keydown": "onKeydown($event)" } }, viewQueries: [{ propertyName: "panelRef", first: true, predicate: ["panel"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
4271
4593
|
<div
|
|
@@ -5234,7 +5556,7 @@ class AfFileUploadComponent {
|
|
|
5234
5556
|
{{ liveAnnouncement() }}
|
|
5235
5557
|
</span>
|
|
5236
5558
|
</div>
|
|
5237
|
-
`, isInline: true, styles: [":host{display:block}.af-file-upload__sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n"], dependencies: [{ kind: "component", type: AfButtonComponent, selector: "af-button", inputs: ["variant", "size", "type", "disabled", "iconOnly", "ariaLabel", "title"], outputs: ["clicked"] }, { kind: "component", type: AfBadgeComponent, selector: "af-badge", inputs: ["variant", "icon", "dot"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
5559
|
+
`, isInline: true, styles: [":host{display:block}.af-file-upload__sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n"], dependencies: [{ kind: "component", type: AfButtonComponent, selector: "af-button", inputs: ["variant", "size", "type", "disabled", "iconOnly", "ariaLabel", "title"], outputs: ["clicked"] }, { kind: "component", type: AfBadgeComponent, selector: "af-badge", inputs: ["ariaLabel", "variant", "icon", "dot"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
5238
5560
|
}
|
|
5239
5561
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: AfFileUploadComponent, decorators: [{
|
|
5240
5562
|
type: Component,
|
|
@@ -6347,6 +6669,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImpor
|
|
|
6347
6669
|
*/
|
|
6348
6670
|
class AfPopoverComponent {
|
|
6349
6671
|
static nextId = 0;
|
|
6672
|
+
focusTrap = new FocusTrap();
|
|
6350
6673
|
/** Two-way bindable open state. */
|
|
6351
6674
|
open = model(false, ...(ngDevMode ? [{ debugName: "open" }] : []));
|
|
6352
6675
|
/** Preferred position relative to the trigger. Flips automatically when space is insufficient. */
|
|
@@ -6370,8 +6693,6 @@ class AfPopoverComponent {
|
|
|
6370
6693
|
wrapperRef = viewChild('wrapper', ...(ngDevMode ? [{ debugName: "wrapperRef" }] : []));
|
|
6371
6694
|
contentRef = viewChild('popoverContent', ...(ngDevMode ? [{ debugName: "contentRef" }] : []));
|
|
6372
6695
|
triggerDirective = contentChild(AfPopoverTriggerDirective, ...(ngDevMode ? [{ debugName: "triggerDirective" }] : []));
|
|
6373
|
-
previousActiveElement = null;
|
|
6374
|
-
focusableElements = [];
|
|
6375
6696
|
flippedSide = signal(null, ...(ngDevMode ? [{ debugName: "flippedSide" }] : []));
|
|
6376
6697
|
/** Effective side after auto-flip evaluation. */
|
|
6377
6698
|
activeSide = computed(() => this.flippedSide() ?? this.position(), ...(ngDevMode ? [{ debugName: "activeSide" }] : []));
|
|
@@ -6392,7 +6713,7 @@ class AfPopoverComponent {
|
|
|
6392
6713
|
}
|
|
6393
6714
|
}, ...(ngDevMode ? [{ debugName: "openEffect" }] : []));
|
|
6394
6715
|
ngOnDestroy() {
|
|
6395
|
-
this.restoreFocus();
|
|
6716
|
+
this.focusTrap.restoreFocus();
|
|
6396
6717
|
}
|
|
6397
6718
|
/** Toggle the popover open state. */
|
|
6398
6719
|
toggle() {
|
|
@@ -6421,59 +6742,29 @@ class AfPopoverComponent {
|
|
|
6421
6742
|
return;
|
|
6422
6743
|
const trigger = this.triggerDirective()?.elementRef.nativeElement;
|
|
6423
6744
|
if (trigger) {
|
|
6424
|
-
this.
|
|
6745
|
+
this.focusTrap.setReturnFocus(trigger);
|
|
6425
6746
|
}
|
|
6426
6747
|
this.close();
|
|
6427
6748
|
}
|
|
6428
6749
|
onKeydown(event) {
|
|
6429
6750
|
if (!this.open() || event.key !== 'Tab')
|
|
6430
6751
|
return;
|
|
6431
|
-
this.
|
|
6752
|
+
const content = this.contentRef()?.nativeElement;
|
|
6753
|
+
this.focusTrap.handleTab(event, content, content);
|
|
6432
6754
|
}
|
|
6433
6755
|
onOpen() {
|
|
6434
|
-
this.
|
|
6756
|
+
this.focusTrap.saveFocus();
|
|
6435
6757
|
this.flippedSide.set(this.computeFlippedSide());
|
|
6436
6758
|
queueMicrotask(() => {
|
|
6437
6759
|
if (!this.open())
|
|
6438
6760
|
return;
|
|
6439
|
-
this.
|
|
6440
|
-
|
|
6441
|
-
if (first) {
|
|
6442
|
-
first.focus();
|
|
6443
|
-
}
|
|
6444
|
-
else {
|
|
6445
|
-
this.contentRef()?.nativeElement.focus();
|
|
6446
|
-
}
|
|
6761
|
+
const content = this.contentRef()?.nativeElement;
|
|
6762
|
+
this.focusTrap.focusFirst(content, content);
|
|
6447
6763
|
});
|
|
6448
6764
|
}
|
|
6449
6765
|
onClose() {
|
|
6450
6766
|
this.flippedSide.set(null);
|
|
6451
|
-
this.restoreFocus();
|
|
6452
|
-
}
|
|
6453
|
-
restoreFocus() {
|
|
6454
|
-
if (this.previousActiveElement) {
|
|
6455
|
-
this.previousActiveElement.focus();
|
|
6456
|
-
this.previousActiveElement = null;
|
|
6457
|
-
}
|
|
6458
|
-
}
|
|
6459
|
-
trapFocus(event) {
|
|
6460
|
-
this.refreshFocusableElements();
|
|
6461
|
-
if (this.focusableElements.length === 0) {
|
|
6462
|
-
event.preventDefault();
|
|
6463
|
-
this.contentRef()?.nativeElement.focus();
|
|
6464
|
-
return;
|
|
6465
|
-
}
|
|
6466
|
-
const first = this.focusableElements[0];
|
|
6467
|
-
const last = this.focusableElements[this.focusableElements.length - 1];
|
|
6468
|
-
const active = document.activeElement;
|
|
6469
|
-
if (event.shiftKey && active === first) {
|
|
6470
|
-
event.preventDefault();
|
|
6471
|
-
last.focus();
|
|
6472
|
-
}
|
|
6473
|
-
else if (!event.shiftKey && active === last) {
|
|
6474
|
-
event.preventDefault();
|
|
6475
|
-
first.focus();
|
|
6476
|
-
}
|
|
6767
|
+
this.focusTrap.restoreFocus();
|
|
6477
6768
|
}
|
|
6478
6769
|
computeFlippedSide() {
|
|
6479
6770
|
const trigger = this.triggerDirective()?.elementRef.nativeElement;
|
|
@@ -6508,24 +6799,6 @@ class AfPopoverComponent {
|
|
|
6508
6799
|
return opposite;
|
|
6509
6800
|
return preferred;
|
|
6510
6801
|
}
|
|
6511
|
-
refreshFocusableElements() {
|
|
6512
|
-
const content = this.contentRef()?.nativeElement;
|
|
6513
|
-
if (!content) {
|
|
6514
|
-
this.focusableElements = [];
|
|
6515
|
-
return;
|
|
6516
|
-
}
|
|
6517
|
-
const selectors = [
|
|
6518
|
-
'a[href]',
|
|
6519
|
-
'area[href]',
|
|
6520
|
-
'button:not([disabled])',
|
|
6521
|
-
'input:not([disabled])',
|
|
6522
|
-
'select:not([disabled])',
|
|
6523
|
-
'textarea:not([disabled])',
|
|
6524
|
-
'[tabindex]:not([tabindex="-1"])',
|
|
6525
|
-
];
|
|
6526
|
-
this.focusableElements = Array.from(content.querySelectorAll(selectors.join(','))).filter((el) => !el.hasAttribute('disabled') &&
|
|
6527
|
-
el.getAttribute('aria-hidden') !== 'true');
|
|
6528
|
-
}
|
|
6529
6802
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: AfPopoverComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
6530
6803
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.2", type: AfPopoverComponent, isStandalone: true, selector: "af-popover", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, align: { classPropertyName: "align", publicName: "align", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, showArrow: { classPropertyName: "showArrow", publicName: "showArrow", isSignal: true, isRequired: false, transformFunction: null }, closeOnClickOutside: { classPropertyName: "closeOnClickOutside", publicName: "closeOnClickOutside", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { open: "openChange", closed: "closed" }, host: { listeners: { "document:click": "onDocumentClick($event)", "document:keydown.escape": "onEscapeKey()", "document:keydown": "onKeydown($event)" } }, providers: [
|
|
6531
6804
|
{ provide: AF_POPOVER, useExisting: forwardRef(() => AfPopoverComponent) },
|
|
@@ -7385,5 +7658,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImpor
|
|
|
7385
7658
|
* Generated bundle index. Do not edit.
|
|
7386
7659
|
*/
|
|
7387
7660
|
|
|
7388
|
-
export { AfAccordionComponent, AfAccordionItemComponent, AfAlertComponent, AfAvatarComponent, AfBadgeComponent, AfBannerComponent, AfBreadcrumbsComponent, AfButtonComponent, AfCardComponent, AfCellDefDirective, AfCheckboxComponent, AfChipInputComponent, AfComboboxComponent, AfDataTableComponent, AfDatepickerComponent, AfDividerComponent, AfDrawerComponent, AfDropdownComponent, AfEmptyStateComponent, AfFieldComponent, AfFileUploadComponent, AfFormatLabelPipe, AfIconComponent, AfInputComponent, AfModalComponent, AfNavItemComponent, AfNavbarComponent, AfPaginationComponent, AfPopoverComponent, AfPopoverTriggerDirective, AfProgressBarComponent, AfRadioComponent, AfSelectComponent, AfSelectMenuComponent, AfSidebarComponent, AfSkeletonComponent, AfSkipLinkComponent, AfSliderComponent, AfSpinnerComponent, AfSwitchComponent, AfTabPanelComponent, AfTableBodyComponent, AfTableCellComponent, AfTableComponent, AfTableHeaderCellComponent, AfTableHeaderComponent, AfTableRowComponent, AfTabsComponent, AfTextareaComponent, AfToastContainerComponent, AfToastService, AfToggleGroupComponent, AfToolbarComponent, AfTooltipDirective };
|
|
7661
|
+
export { AfAccordionComponent, AfAccordionItemComponent, AfAlertComponent, AfAvatarComponent, AfBadgeComponent, AfBannerComponent, AfBreadcrumbsComponent, AfButtonComponent, AfCardComponent, AfCellDefDirective, AfCheckboxComponent, AfChipInputComponent, AfComboboxComponent, AfDataTableComponent, AfDatepickerComponent, AfDividerComponent, AfDrawerComponent, AfDropdownComponent, AfEmptyStateComponent, AfFieldComponent, AfFileUploadComponent, AfFormatLabelPipe, AfIconComponent, AfInputComponent, AfModalComponent, AfNavItemComponent, AfNavbarComponent, AfPaginationComponent, AfPopoverComponent, AfPopoverTriggerDirective, AfProgressBarComponent, AfRadioComponent, AfRadioGroupComponent, AfSelectComponent, AfSelectMenuComponent, AfSidebarComponent, AfSkeletonComponent, AfSkipLinkComponent, AfSliderComponent, AfSpinnerComponent, AfSwitchComponent, AfTabPanelComponent, AfTableBodyComponent, AfTableCellComponent, AfTableComponent, AfTableHeaderCellComponent, AfTableHeaderComponent, AfTableRowComponent, AfTabsComponent, AfTextareaComponent, AfToastContainerComponent, AfToastService, AfToggleGroupComponent, AfToolbarComponent, AfTooltipDirective };
|
|
7389
7662
|
//# sourceMappingURL=neuravision-ng-construct.mjs.map
|