@nexterajs/ui 0.0.2 → 0.1.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.
- package/README.md +279 -34
- package/fesm2022/nexterajs-ui-charts.mjs +1096 -0
- package/fesm2022/nexterajs-ui-charts.mjs.map +1 -0
- package/fesm2022/nexterajs-ui-core.mjs +652 -12
- package/fesm2022/nexterajs-ui-core.mjs.map +1 -1
- package/fesm2022/nexterajs-ui-display.mjs +1142 -0
- package/fesm2022/nexterajs-ui-display.mjs.map +1 -0
- package/fesm2022/nexterajs-ui-dropdowns.mjs +321 -33
- package/fesm2022/nexterajs-ui-dropdowns.mjs.map +1 -1
- package/fesm2022/nexterajs-ui-file-uploader.mjs +572 -10
- package/fesm2022/nexterajs-ui-file-uploader.mjs.map +1 -1
- package/fesm2022/nexterajs-ui-forms.mjs +2027 -136
- package/fesm2022/nexterajs-ui-forms.mjs.map +1 -1
- package/fesm2022/nexterajs-ui-grid.mjs +1222 -188
- package/fesm2022/nexterajs-ui-grid.mjs.map +1 -1
- package/fesm2022/nexterajs-ui-intl.mjs +103 -11
- package/fesm2022/nexterajs-ui-intl.mjs.map +1 -1
- package/fesm2022/nexterajs-ui-navigation.mjs +1054 -0
- package/fesm2022/nexterajs-ui-navigation.mjs.map +1 -0
- package/fesm2022/nexterajs-ui-notify.mjs +46 -44
- package/fesm2022/nexterajs-ui-notify.mjs.map +1 -1
- package/fesm2022/nexterajs-ui-overlay.mjs +1671 -0
- package/fesm2022/nexterajs-ui-overlay.mjs.map +1 -0
- package/fesm2022/nexterajs-ui-pivot.mjs +51 -26
- package/fesm2022/nexterajs-ui-pivot.mjs.map +1 -1
- package/fesm2022/nexterajs-ui-shell.mjs +542 -0
- package/fesm2022/nexterajs-ui-shell.mjs.map +1 -0
- package/fesm2022/nexterajs-ui-tree-view.mjs +5 -2
- package/fesm2022/nexterajs-ui-tree-view.mjs.map +1 -1
- package/fesm2022/nexterajs-ui-validation.mjs +42 -11
- package/fesm2022/nexterajs-ui-validation.mjs.map +1 -1
- package/fesm2022/nexterajs-ui.mjs +5 -0
- package/fesm2022/nexterajs-ui.mjs.map +1 -1
- package/package.json +33 -2
- package/styles/_dropdown.scss +5 -5
- package/styles/_editor.scss +52 -17
- package/styles/_mixins.scss +1 -1
- package/styles/_scrollbar.scss +5 -5
- package/styles/_status.scss +21 -0
- package/styles/_toast.scss +14 -14
- package/styles/_tokens.scss +420 -3
- package/styles/_widget.scss +14 -3
- package/styles/index.scss +6 -4
- package/types/nexterajs-ui-charts.d.ts +446 -0
- package/types/nexterajs-ui-core.d.ts +387 -22
- package/types/nexterajs-ui-display.d.ts +720 -0
- package/types/nexterajs-ui-dropdowns.d.ts +191 -48
- package/types/nexterajs-ui-file-uploader.d.ts +236 -7
- package/types/nexterajs-ui-forms.d.ts +675 -30
- package/types/nexterajs-ui-grid.d.ts +448 -93
- package/types/nexterajs-ui-intl.d.ts +39 -4
- package/types/nexterajs-ui-navigation.d.ts +531 -0
- package/types/nexterajs-ui-notify.d.ts +4 -2
- package/types/nexterajs-ui-overlay.d.ts +703 -0
- package/types/nexterajs-ui-pivot.d.ts +56 -48
- package/types/nexterajs-ui-shell.d.ts +294 -0
- package/types/nexterajs-ui-tree-view.d.ts +6 -3
- package/types/nexterajs-ui-validation.d.ts +20 -11
- package/types/nexterajs-ui.d.ts +5 -0
|
@@ -0,0 +1,1142 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { input, booleanAttribute, ViewEncapsulation, ChangeDetectionStrategy, Component, Directive, numberAttribute, computed, inject, contentChild, viewChild, contentChildren, InjectionToken, linkedSignal, forwardRef, TemplateRef, model } from '@angular/core';
|
|
3
|
+
import { NxeMessageService } from '@nexterajs/ui/core';
|
|
4
|
+
import { NgTemplateOutlet } from '@angular/common';
|
|
5
|
+
import { DateTime } from 'luxon';
|
|
6
|
+
import { toObservable, takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
7
|
+
|
|
8
|
+
/** Every family, in catalogue order. For demos, specs and pickers. */
|
|
9
|
+
const NXE_STATUS_FAMILIES = [
|
|
10
|
+
'neutral',
|
|
11
|
+
'info',
|
|
12
|
+
'attention',
|
|
13
|
+
'success',
|
|
14
|
+
'danger',
|
|
15
|
+
'muted',
|
|
16
|
+
];
|
|
17
|
+
/**
|
|
18
|
+
* `nxe-tag` renders one status, with colour from the status family rather than ad hoc:
|
|
19
|
+
*
|
|
20
|
+
* ```html
|
|
21
|
+
* <nxe-tag family="success">Disetujui</nxe-tag>
|
|
22
|
+
* <nxe-tag family="attention" dot>Perlu tindakan</nxe-tag>
|
|
23
|
+
* <nxe-tag family="info"><svg nxeTagIcon>...</svg> Terkirim</nxe-tag>
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* Colour is never the only carrier of meaning: the label is the status. The dot is decorative
|
|
27
|
+
* (`aria-hidden`). All six families keep their text at >= 4.5:1 on their fill, in light and dark.
|
|
28
|
+
*/
|
|
29
|
+
class NxeTagComponent {
|
|
30
|
+
/** Status family: sets the colours. */
|
|
31
|
+
family = input('neutral', /* @ts-ignore */
|
|
32
|
+
...(ngDevMode ? [{ debugName: "family" }] : /* istanbul ignore next */ []));
|
|
33
|
+
/** `subtle` (default), `solid` or `outlined`. */
|
|
34
|
+
stylingMode = input('subtle', /* @ts-ignore */
|
|
35
|
+
...(ngDevMode ? [{ debugName: "stylingMode" }] : /* istanbul ignore next */ []));
|
|
36
|
+
/** `md` (default) or `sm`. */
|
|
37
|
+
size = input('md', /* @ts-ignore */
|
|
38
|
+
...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
39
|
+
/** Leading status dot, in the tag's text colour. */
|
|
40
|
+
dot = input(false, { ...(ngDevMode ? { debugName: "dot" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
41
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeTagComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
42
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: NxeTagComponent, isStandalone: true, selector: "nxe-tag", inputs: { family: { classPropertyName: "family", publicName: "family", isSignal: true, isRequired: false, transformFunction: null }, stylingMode: { classPropertyName: "stylingMode", publicName: "stylingMode", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, dot: { classPropertyName: "dot", publicName: "dot", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "'nxe-tag-' + family() + ' nxe-tag-mode-' + stylingMode() + ' nxe-tag-size-' + size()", "class.nxe-tag-has-dot": "dot()" }, classAttribute: "nxe-tag" }, ngImport: i0, template: "@if (dot()) {\r\n <span class=\"nxe-tag-dot\" aria-hidden=\"true\"></span>\r\n}\r\n<ng-content select=\"[nxeTagIcon], [nxeIcon]\" />\r\n<span class=\"nxe-tag-label\"><ng-content /></span>\r\n", styles: ["nxe-tag.nxe-tag{--nxe-status-border-style: solid;display:inline-flex;align-items:center;gap:var(--nxe-ui-space-1);box-sizing:border-box;max-width:100%;vertical-align:middle;padding:1px var(--nxe-ui-space-2);border:1px var(--nxe-status-border-style) var(--nxe-status-border);border-radius:var(--nxe-ui-radius-sm);background-color:var(--nxe-status-bg);color:var(--nxe-status-fg);font-family:var(--nxe-ui-font-family);font-size:var(--nxe-ui-font-size-sm);font-weight:var(--nxe-ui-font-weight-medium);line-height:var(--nxe-ui-line-height-sm);white-space:nowrap}nxe-tag.nxe-tag.nxe-tag-size-sm{padding-block:0;padding-inline:calc(var(--nxe-ui-space-1) + 2px);font-size:var(--nxe-ui-font-size-xs);line-height:var(--nxe-ui-line-height-xs)}nxe-tag.nxe-tag.nxe-tag-mode-solid{border-color:var(--nxe-status-fg);background-color:var(--nxe-status-fg);color:var(--nxe-status-bg)}nxe-tag.nxe-tag.nxe-tag-mode-outlined{border-color:var(--nxe-status-fg);background-color:transparent;color:var(--nxe-status-fg)}nxe-tag.nxe-tag .nxe-tag-label{overflow:hidden;text-overflow:ellipsis}nxe-tag.nxe-tag .nxe-tag-dot{flex:none;width:6px;height:6px;border-radius:50%;background-color:currentColor}nxe-tag.nxe-tag>svg,nxe-tag.nxe-tag>.nxe-icon{flex:none;width:1em;height:1em;fill:currentColor}nxe-tag.nxe-tag-neutral{--nxe-status-fg: var(--nxe-ui-status-neutral-fg);--nxe-status-bg: var(--nxe-ui-status-neutral-bg);--nxe-status-border: var(--nxe-ui-status-neutral-border)}nxe-tag.nxe-tag-info{--nxe-status-fg: var(--nxe-ui-status-info-fg);--nxe-status-bg: var(--nxe-ui-status-info-bg);--nxe-status-border: var(--nxe-ui-status-info-border)}nxe-tag.nxe-tag-attention{--nxe-status-fg: var(--nxe-ui-status-attention-fg);--nxe-status-bg: var(--nxe-ui-status-attention-bg);--nxe-status-border: var(--nxe-ui-status-attention-border)}nxe-tag.nxe-tag-success{--nxe-status-fg: var(--nxe-ui-status-success-fg);--nxe-status-bg: var(--nxe-ui-status-success-bg);--nxe-status-border: var(--nxe-ui-status-success-border)}nxe-tag.nxe-tag-danger{--nxe-status-fg: var(--nxe-ui-status-danger-fg);--nxe-status-bg: var(--nxe-ui-status-danger-bg);--nxe-status-border: var(--nxe-ui-status-danger-border)}nxe-tag.nxe-tag-muted{--nxe-status-fg: var(--nxe-ui-status-muted-fg);--nxe-status-bg: var(--nxe-ui-status-muted-bg);--nxe-status-border: var(--nxe-ui-status-muted-border)}nxe-tag.nxe-tag-muted{--nxe-status-border-style: dashed}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
43
|
+
}
|
|
44
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeTagComponent, decorators: [{
|
|
45
|
+
type: Component,
|
|
46
|
+
args: [{ selector: 'nxe-tag', host: {
|
|
47
|
+
class: 'nxe-tag',
|
|
48
|
+
'[class]': "'nxe-tag-' + family() + ' nxe-tag-mode-' + stylingMode() + ' nxe-tag-size-' + size()",
|
|
49
|
+
'[class.nxe-tag-has-dot]': 'dot()',
|
|
50
|
+
}, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "@if (dot()) {\r\n <span class=\"nxe-tag-dot\" aria-hidden=\"true\"></span>\r\n}\r\n<ng-content select=\"[nxeTagIcon], [nxeIcon]\" />\r\n<span class=\"nxe-tag-label\"><ng-content /></span>\r\n", styles: ["nxe-tag.nxe-tag{--nxe-status-border-style: solid;display:inline-flex;align-items:center;gap:var(--nxe-ui-space-1);box-sizing:border-box;max-width:100%;vertical-align:middle;padding:1px var(--nxe-ui-space-2);border:1px var(--nxe-status-border-style) var(--nxe-status-border);border-radius:var(--nxe-ui-radius-sm);background-color:var(--nxe-status-bg);color:var(--nxe-status-fg);font-family:var(--nxe-ui-font-family);font-size:var(--nxe-ui-font-size-sm);font-weight:var(--nxe-ui-font-weight-medium);line-height:var(--nxe-ui-line-height-sm);white-space:nowrap}nxe-tag.nxe-tag.nxe-tag-size-sm{padding-block:0;padding-inline:calc(var(--nxe-ui-space-1) + 2px);font-size:var(--nxe-ui-font-size-xs);line-height:var(--nxe-ui-line-height-xs)}nxe-tag.nxe-tag.nxe-tag-mode-solid{border-color:var(--nxe-status-fg);background-color:var(--nxe-status-fg);color:var(--nxe-status-bg)}nxe-tag.nxe-tag.nxe-tag-mode-outlined{border-color:var(--nxe-status-fg);background-color:transparent;color:var(--nxe-status-fg)}nxe-tag.nxe-tag .nxe-tag-label{overflow:hidden;text-overflow:ellipsis}nxe-tag.nxe-tag .nxe-tag-dot{flex:none;width:6px;height:6px;border-radius:50%;background-color:currentColor}nxe-tag.nxe-tag>svg,nxe-tag.nxe-tag>.nxe-icon{flex:none;width:1em;height:1em;fill:currentColor}nxe-tag.nxe-tag-neutral{--nxe-status-fg: var(--nxe-ui-status-neutral-fg);--nxe-status-bg: var(--nxe-ui-status-neutral-bg);--nxe-status-border: var(--nxe-ui-status-neutral-border)}nxe-tag.nxe-tag-info{--nxe-status-fg: var(--nxe-ui-status-info-fg);--nxe-status-bg: var(--nxe-ui-status-info-bg);--nxe-status-border: var(--nxe-ui-status-info-border)}nxe-tag.nxe-tag-attention{--nxe-status-fg: var(--nxe-ui-status-attention-fg);--nxe-status-bg: var(--nxe-ui-status-attention-bg);--nxe-status-border: var(--nxe-ui-status-attention-border)}nxe-tag.nxe-tag-success{--nxe-status-fg: var(--nxe-ui-status-success-fg);--nxe-status-bg: var(--nxe-ui-status-success-bg);--nxe-status-border: var(--nxe-ui-status-success-border)}nxe-tag.nxe-tag-danger{--nxe-status-fg: var(--nxe-ui-status-danger-fg);--nxe-status-bg: var(--nxe-ui-status-danger-bg);--nxe-status-border: var(--nxe-ui-status-danger-border)}nxe-tag.nxe-tag-muted{--nxe-status-fg: var(--nxe-ui-status-muted-fg);--nxe-status-bg: var(--nxe-ui-status-muted-bg);--nxe-status-border: var(--nxe-ui-status-muted-border)}nxe-tag.nxe-tag-muted{--nxe-status-border-style: dashed}\n"] }]
|
|
51
|
+
}], propDecorators: { family: [{ type: i0.Input, args: [{ isSignal: true, alias: "family", required: false }] }], stylingMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "stylingMode", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], dot: [{ type: i0.Input, args: [{ isSignal: true, alias: "dot", required: false }] }] } });
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Marks the element a badge is attached to. It becomes the positioning context, so a child
|
|
55
|
+
* `<nxe-badge overlay>` sits on its top-right corner without being clipped by the element it
|
|
56
|
+
* decorates (`nxe-button` clips its overflow, so the badge goes beside it, not inside it):
|
|
57
|
+
*
|
|
58
|
+
* ```html
|
|
59
|
+
* <span nxeBadgeAnchor>
|
|
60
|
+
* <nxe-button text="Inbox"></nxe-button>
|
|
61
|
+
* <nxe-badge overlay [value]="unread()" label="unread messages"></nxe-badge>
|
|
62
|
+
* </span>
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
class NxeBadgeAnchorDirective {
|
|
66
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeBadgeAnchorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
67
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.5", type: NxeBadgeAnchorDirective, isStandalone: true, selector: "[nxeBadgeAnchor]", host: { classAttribute: "nxe-badge-anchor" }, ngImport: i0 });
|
|
68
|
+
}
|
|
69
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeBadgeAnchorDirective, decorators: [{
|
|
70
|
+
type: Directive,
|
|
71
|
+
args: [{
|
|
72
|
+
selector: '[nxeBadgeAnchor]',
|
|
73
|
+
host: { class: 'nxe-badge-anchor' },
|
|
74
|
+
}]
|
|
75
|
+
}] });
|
|
76
|
+
/**
|
|
77
|
+
* `nxe-badge` shows a count, or a bare dot, in a status family colour (default `danger`, solid).
|
|
78
|
+
*
|
|
79
|
+
* - **Count** (`[value]`): a number, capped at `max` (`99+`). Hidden when 0 unless `showZero`, and
|
|
80
|
+
* hidden when `value` is not set.
|
|
81
|
+
* - **Dot** (`dot`): no number, just "something is new". Shown unless `value` is 0 (and not
|
|
82
|
+
* `showZero`).
|
|
83
|
+
* - **Standalone** it is inline, so it can follow a label (`Perlu tindakan <nxe-badge [value]="5">`),
|
|
84
|
+
* which is how tab and nav counts are written. With `overlay` and a {@link NxeBadgeAnchorDirective}
|
|
85
|
+
* parent it pins to that element's top-right corner.
|
|
86
|
+
*
|
|
87
|
+
* The visible number is the accessible name unless `label` is given, which is read instead
|
|
88
|
+
* ("5 unread messages"). A dot has no text, so it is `aria-hidden` unless it has a `label`.
|
|
89
|
+
*/
|
|
90
|
+
class NxeBadgeComponent {
|
|
91
|
+
/** The count. */
|
|
92
|
+
value = input(undefined, { ...(ngDevMode ? { debugName: "value" } : /* istanbul ignore next */ {}), transform: (v) => (v === null || v === undefined || v === '' ? undefined : numberAttribute(v, NaN)) });
|
|
93
|
+
/** Largest number shown whole; above it the badge reads `<max>+`. */
|
|
94
|
+
max = input(99, { ...(ngDevMode ? { debugName: "max" } : /* istanbul ignore next */ {}), transform: (v) => numberAttribute(v, 99) });
|
|
95
|
+
/** A dot instead of a number. */
|
|
96
|
+
dot = input(false, { ...(ngDevMode ? { debugName: "dot" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
97
|
+
/** Show the badge when the count is 0. */
|
|
98
|
+
showZero = input(false, { ...(ngDevMode ? { debugName: "showZero" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
99
|
+
/** Status family; `danger` by default. Always drawn solid. */
|
|
100
|
+
family = input('danger', /* @ts-ignore */
|
|
101
|
+
...(ngDevMode ? [{ debugName: "family" }] : /* istanbul ignore next */ []));
|
|
102
|
+
/** Pin to the top-right corner of the nearest `nxeBadgeAnchor` ancestor. */
|
|
103
|
+
overlay = input(false, { ...(ngDevMode ? { debugName: "overlay" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
104
|
+
/** Accessible name; replaces the number for assistive tech ("5 unread messages"). */
|
|
105
|
+
label = input('', /* @ts-ignore */
|
|
106
|
+
...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
107
|
+
/** What the badge says: the number, or `<max>+`. Empty in dot mode. */
|
|
108
|
+
text = computed(() => {
|
|
109
|
+
const value = this.value();
|
|
110
|
+
if (this.dot() || value == null || Number.isNaN(value)) {
|
|
111
|
+
return '';
|
|
112
|
+
}
|
|
113
|
+
return value > this.max() ? `${this.max()}+` : String(value);
|
|
114
|
+
}, /* @ts-ignore */
|
|
115
|
+
...(ngDevMode ? [{ debugName: "text" }] : /* istanbul ignore next */ []));
|
|
116
|
+
visible = computed(() => {
|
|
117
|
+
const value = this.value();
|
|
118
|
+
if (value == null || Number.isNaN(value)) {
|
|
119
|
+
return this.dot();
|
|
120
|
+
}
|
|
121
|
+
return value > 0 || this.showZero();
|
|
122
|
+
}, /* @ts-ignore */
|
|
123
|
+
...(ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []));
|
|
124
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeBadgeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
125
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.5", type: NxeBadgeComponent, isStandalone: true, selector: "nxe-badge", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, dot: { classPropertyName: "dot", publicName: "dot", isSignal: true, isRequired: false, transformFunction: null }, showZero: { classPropertyName: "showZero", publicName: "showZero", isSignal: true, isRequired: false, transformFunction: null }, family: { classPropertyName: "family", publicName: "family", isSignal: true, isRequired: false, transformFunction: null }, overlay: { classPropertyName: "overlay", publicName: "overlay", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "'nxe-badge-' + family()", "class.nxe-badge-dot": "dot()", "class.nxe-badge-overlay": "overlay()", "hidden": "!visible()", "attr.role": "dot() ? (label() ? 'img' : null) : 'status'", "attr.aria-label": "label() || null", "attr.aria-hidden": "dot() && !label() ? true : null" }, classAttribute: "nxe-badge" }, ngImport: i0, template: "{{ text() }}\r\n", styles: ["nxe-badge.nxe-badge{display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;min-width:calc(var(--nxe-ui-line-height-xs) + 2px);height:calc(var(--nxe-ui-line-height-xs) + 2px);padding-inline:5px;border-radius:var(--nxe-ui-radius-pill);background-color:var(--nxe-status-fg);color:var(--nxe-status-bg);font-family:var(--nxe-ui-font-family);font-size:var(--nxe-ui-font-size-xs);font-weight:var(--nxe-ui-font-weight-semibold);font-variant-numeric:tabular-nums;line-height:var(--nxe-ui-line-height-xs);vertical-align:middle;white-space:nowrap}nxe-badge.nxe-badge[hidden]{display:none}nxe-badge.nxe-badge.nxe-badge-dot{min-width:0;width:8px;height:8px;padding:0}nxe-badge.nxe-badge.nxe-badge-overlay{position:absolute;top:0;inset-inline-end:0;transform:translate(50%,-50%);box-shadow:0 0 0 2px var(--nxe-ui-surface)}nxe-badge.nxe-badge.nxe-badge-overlay:dir(rtl){transform:translate(-50%,-50%)}.nxe-badge-anchor{position:relative;display:inline-flex;vertical-align:middle}nxe-badge.nxe-badge-neutral{--nxe-status-fg: var(--nxe-ui-status-neutral-fg);--nxe-status-bg: var(--nxe-ui-status-neutral-bg);--nxe-status-border: var(--nxe-ui-status-neutral-border)}nxe-badge.nxe-badge-info{--nxe-status-fg: var(--nxe-ui-status-info-fg);--nxe-status-bg: var(--nxe-ui-status-info-bg);--nxe-status-border: var(--nxe-ui-status-info-border)}nxe-badge.nxe-badge-attention{--nxe-status-fg: var(--nxe-ui-status-attention-fg);--nxe-status-bg: var(--nxe-ui-status-attention-bg);--nxe-status-border: var(--nxe-ui-status-attention-border)}nxe-badge.nxe-badge-success{--nxe-status-fg: var(--nxe-ui-status-success-fg);--nxe-status-bg: var(--nxe-ui-status-success-bg);--nxe-status-border: var(--nxe-ui-status-success-border)}nxe-badge.nxe-badge-danger{--nxe-status-fg: var(--nxe-ui-status-danger-fg);--nxe-status-bg: var(--nxe-ui-status-danger-bg);--nxe-status-border: var(--nxe-ui-status-danger-border)}nxe-badge.nxe-badge-muted{--nxe-status-fg: var(--nxe-ui-status-muted-fg);--nxe-status-bg: var(--nxe-ui-status-muted-bg);--nxe-status-border: var(--nxe-ui-status-muted-border)}nxe-badge.nxe-badge-muted{--nxe-status-border-style: dashed}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
126
|
+
}
|
|
127
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeBadgeComponent, decorators: [{
|
|
128
|
+
type: Component,
|
|
129
|
+
args: [{ selector: 'nxe-badge', host: {
|
|
130
|
+
class: 'nxe-badge',
|
|
131
|
+
'[class]': "'nxe-badge-' + family()",
|
|
132
|
+
'[class.nxe-badge-dot]': 'dot()',
|
|
133
|
+
'[class.nxe-badge-overlay]': 'overlay()',
|
|
134
|
+
'[hidden]': '!visible()',
|
|
135
|
+
'[attr.role]': "dot() ? (label() ? 'img' : null) : 'status'",
|
|
136
|
+
'[attr.aria-label]': 'label() || null',
|
|
137
|
+
'[attr.aria-hidden]': 'dot() && !label() ? true : null',
|
|
138
|
+
}, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "{{ text() }}\r\n", styles: ["nxe-badge.nxe-badge{display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;min-width:calc(var(--nxe-ui-line-height-xs) + 2px);height:calc(var(--nxe-ui-line-height-xs) + 2px);padding-inline:5px;border-radius:var(--nxe-ui-radius-pill);background-color:var(--nxe-status-fg);color:var(--nxe-status-bg);font-family:var(--nxe-ui-font-family);font-size:var(--nxe-ui-font-size-xs);font-weight:var(--nxe-ui-font-weight-semibold);font-variant-numeric:tabular-nums;line-height:var(--nxe-ui-line-height-xs);vertical-align:middle;white-space:nowrap}nxe-badge.nxe-badge[hidden]{display:none}nxe-badge.nxe-badge.nxe-badge-dot{min-width:0;width:8px;height:8px;padding:0}nxe-badge.nxe-badge.nxe-badge-overlay{position:absolute;top:0;inset-inline-end:0;transform:translate(50%,-50%);box-shadow:0 0 0 2px var(--nxe-ui-surface)}nxe-badge.nxe-badge.nxe-badge-overlay:dir(rtl){transform:translate(-50%,-50%)}.nxe-badge-anchor{position:relative;display:inline-flex;vertical-align:middle}nxe-badge.nxe-badge-neutral{--nxe-status-fg: var(--nxe-ui-status-neutral-fg);--nxe-status-bg: var(--nxe-ui-status-neutral-bg);--nxe-status-border: var(--nxe-ui-status-neutral-border)}nxe-badge.nxe-badge-info{--nxe-status-fg: var(--nxe-ui-status-info-fg);--nxe-status-bg: var(--nxe-ui-status-info-bg);--nxe-status-border: var(--nxe-ui-status-info-border)}nxe-badge.nxe-badge-attention{--nxe-status-fg: var(--nxe-ui-status-attention-fg);--nxe-status-bg: var(--nxe-ui-status-attention-bg);--nxe-status-border: var(--nxe-ui-status-attention-border)}nxe-badge.nxe-badge-success{--nxe-status-fg: var(--nxe-ui-status-success-fg);--nxe-status-bg: var(--nxe-ui-status-success-bg);--nxe-status-border: var(--nxe-ui-status-success-border)}nxe-badge.nxe-badge-danger{--nxe-status-fg: var(--nxe-ui-status-danger-fg);--nxe-status-bg: var(--nxe-ui-status-danger-bg);--nxe-status-border: var(--nxe-ui-status-danger-border)}nxe-badge.nxe-badge-muted{--nxe-status-fg: var(--nxe-ui-status-muted-fg);--nxe-status-bg: var(--nxe-ui-status-muted-bg);--nxe-status-border: var(--nxe-ui-status-muted-border)}nxe-badge.nxe-badge-muted{--nxe-status-border-style: dashed}\n"] }]
|
|
139
|
+
}], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], dot: [{ type: i0.Input, args: [{ isSignal: true, alias: "dot", required: false }] }], showZero: [{ type: i0.Input, args: [{ isSignal: true, alias: "showZero", required: false }] }], family: [{ type: i0.Input, args: [{ isSignal: true, alias: "family", required: false }] }], overlay: [{ type: i0.Input, args: [{ isSignal: true, alias: "overlay", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }] } });
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* `nxe-spinner` is the inline "working" indicator: a ring that turns, announced as a polite live
|
|
143
|
+
* status.
|
|
144
|
+
*
|
|
145
|
+
* ```html
|
|
146
|
+
* <nxe-spinner size="sm" label="Menyimpan" />
|
|
147
|
+
* ```
|
|
148
|
+
*
|
|
149
|
+
* The ring takes the text colour set by `--nxe-ui-ink-strong` over the `--nxe-ui-spinner-track`
|
|
150
|
+
* track; set `color` on it (or a parent, via `--nxe-spinner-color`) to recolour. Under
|
|
151
|
+
* `prefers-reduced-motion: reduce` the ring turns slowly instead of at speed, so it still reads as
|
|
152
|
+
* "in progress" without the vestibular cost. Use `nxe-skeleton` for the first load of a page and
|
|
153
|
+
* this for a pending action or a region that is refreshing.
|
|
154
|
+
*/
|
|
155
|
+
class NxeSpinnerComponent {
|
|
156
|
+
/** `sm` (16px), `md` (24px, default) or `lg` (40px). */
|
|
157
|
+
size = input('md', /* @ts-ignore */
|
|
158
|
+
...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
159
|
+
/** What is happening, for assistive tech. Translate it; the default is English. */
|
|
160
|
+
label = input('Loading', /* @ts-ignore */
|
|
161
|
+
...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
162
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeSpinnerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
163
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.5", type: NxeSpinnerComponent, isStandalone: true, selector: "nxe-spinner", inputs: { size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "status" }, properties: { "attr.aria-label": "label()", "class.nxe-spinner-sm": "size() === 'sm'", "class.nxe-spinner-md": "size() === 'md'", "class.nxe-spinner-lg": "size() === 'lg'" }, classAttribute: "nxe-spinner" }, ngImport: i0, template: "<span class=\"nxe-spinner-ring\" aria-hidden=\"true\"></span>\r\n", styles: ["nxe-spinner.nxe-spinner{--nxe-spinner-size: var(--nxe-ui-space-6);display:inline-flex;flex:none;width:var(--nxe-spinner-size);height:var(--nxe-spinner-size);vertical-align:middle;color:var(--nxe-spinner-color, var(--nxe-ui-ink-strong))}nxe-spinner.nxe-spinner.nxe-spinner-sm{--nxe-spinner-size: var(--nxe-ui-space-4)}nxe-spinner.nxe-spinner.nxe-spinner-lg{--nxe-spinner-size: calc(var(--nxe-ui-space-7) + var(--nxe-ui-space-2))}nxe-spinner.nxe-spinner .nxe-spinner-ring{box-sizing:border-box;width:100%;height:100%;border:max(2px,var(--nxe-spinner-size) / 10) solid var(--nxe-ui-spinner-track);border-top-color:currentColor;border-radius:50%;animation:nxe-spinner-turn .8s linear infinite}@keyframes nxe-spinner-turn{to{transform:rotate(360deg)}}@media(prefers-reduced-motion:reduce){nxe-spinner.nxe-spinner .nxe-spinner-ring{animation-duration:3s}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
164
|
+
}
|
|
165
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeSpinnerComponent, decorators: [{
|
|
166
|
+
type: Component,
|
|
167
|
+
args: [{ selector: 'nxe-spinner', host: {
|
|
168
|
+
class: 'nxe-spinner',
|
|
169
|
+
role: 'status',
|
|
170
|
+
'[attr.aria-label]': 'label()',
|
|
171
|
+
'[class.nxe-spinner-sm]': "size() === 'sm'",
|
|
172
|
+
'[class.nxe-spinner-md]': "size() === 'md'",
|
|
173
|
+
'[class.nxe-spinner-lg]': "size() === 'lg'",
|
|
174
|
+
}, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<span class=\"nxe-spinner-ring\" aria-hidden=\"true\"></span>\r\n", styles: ["nxe-spinner.nxe-spinner{--nxe-spinner-size: var(--nxe-ui-space-6);display:inline-flex;flex:none;width:var(--nxe-spinner-size);height:var(--nxe-spinner-size);vertical-align:middle;color:var(--nxe-spinner-color, var(--nxe-ui-ink-strong))}nxe-spinner.nxe-spinner.nxe-spinner-sm{--nxe-spinner-size: var(--nxe-ui-space-4)}nxe-spinner.nxe-spinner.nxe-spinner-lg{--nxe-spinner-size: calc(var(--nxe-ui-space-7) + var(--nxe-ui-space-2))}nxe-spinner.nxe-spinner .nxe-spinner-ring{box-sizing:border-box;width:100%;height:100%;border:max(2px,var(--nxe-spinner-size) / 10) solid var(--nxe-ui-spinner-track);border-top-color:currentColor;border-radius:50%;animation:nxe-spinner-turn .8s linear infinite}@keyframes nxe-spinner-turn{to{transform:rotate(360deg)}}@media(prefers-reduced-motion:reduce){nxe-spinner.nxe-spinner .nxe-spinner-ring{animation-duration:3s}}\n"] }]
|
|
175
|
+
}], propDecorators: { size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }] } });
|
|
176
|
+
|
|
177
|
+
/** A bare number (or numeric string) means CSS px; anything else (`'50%'`, `'12em'`) passes through. */
|
|
178
|
+
function cssSize(value) {
|
|
179
|
+
if (value === undefined || value === null || value === '') {
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
return /^-?\d+(\.\d+)?$/.test(String(value)) ? `${value}px` : String(value);
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* `nxe-skeleton` is a grey placeholder in the shape of content that has not arrived yet, for the
|
|
186
|
+
* first load of a page:
|
|
187
|
+
*
|
|
188
|
+
* ```html
|
|
189
|
+
* <nxe-skeleton variant="text" [lines]="3" />
|
|
190
|
+
* <nxe-skeleton variant="rect" width="100%" [height]="120" />
|
|
191
|
+
* <nxe-skeleton variant="circle" [width]="40" />
|
|
192
|
+
* ```
|
|
193
|
+
*
|
|
194
|
+
* It is `aria-hidden`: a placeholder says nothing. Put `aria-busy="true"` on the region that is
|
|
195
|
+
* loading (and drop it when the content lands) so assistive tech knows to wait. The shimmer stops
|
|
196
|
+
* under `prefers-reduced-motion: reduce`, leaving a still grey block. Colours come from the neutral
|
|
197
|
+
* ramp, so it follows light and dark.
|
|
198
|
+
*/
|
|
199
|
+
class NxeSkeletonComponent {
|
|
200
|
+
/** `text` (default), `rect` or `circle`. */
|
|
201
|
+
variant = input('text', /* @ts-ignore */
|
|
202
|
+
...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
|
|
203
|
+
/** CSS width: a number is px. `text`: of the whole block (default 100%). `circle`: the diameter (default 40). */
|
|
204
|
+
width = input(undefined, /* @ts-ignore */
|
|
205
|
+
...(ngDevMode ? [{ debugName: "width" }] : /* istanbul ignore next */ []));
|
|
206
|
+
/** CSS height: a number is px. `text`: of one line (default 12). `rect`: the block (default 48). `circle`: defaults to the width. */
|
|
207
|
+
height = input(undefined, /* @ts-ignore */
|
|
208
|
+
...(ngDevMode ? [{ debugName: "height" }] : /* istanbul ignore next */ []));
|
|
209
|
+
/** Number of lines, for `text`. The last of several is shorter, as a paragraph ends. */
|
|
210
|
+
lines = input(1, { ...(ngDevMode ? { debugName: "lines" } : /* istanbul ignore next */ {}), transform: (v) => Math.max(1, Math.floor(Number(v)) || 1) });
|
|
211
|
+
/** Shimmer. Off for a still placeholder; always off under reduced motion. */
|
|
212
|
+
animated = input(true, { ...(ngDevMode ? { debugName: "animated" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
213
|
+
lineIndexes = computed(() => Array.from({ length: this.lines() }, (_, i) => i), /* @ts-ignore */
|
|
214
|
+
...(ngDevMode ? [{ debugName: "lineIndexes" }] : /* istanbul ignore next */ []));
|
|
215
|
+
/** `text` applies the height to each line, not to the block, so the block sizes itself. */
|
|
216
|
+
widthStyle = computed(() => {
|
|
217
|
+
const width = cssSize(this.width());
|
|
218
|
+
return this.variant() === 'circle' ? (width ?? '40px') : width;
|
|
219
|
+
}, /* @ts-ignore */
|
|
220
|
+
...(ngDevMode ? [{ debugName: "widthStyle" }] : /* istanbul ignore next */ []));
|
|
221
|
+
heightStyle = computed(() => {
|
|
222
|
+
const variant = this.variant();
|
|
223
|
+
if (variant === 'text') {
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
const height = cssSize(this.height());
|
|
227
|
+
if (variant === 'circle') {
|
|
228
|
+
return height ?? this.widthStyle();
|
|
229
|
+
}
|
|
230
|
+
return height;
|
|
231
|
+
}, /* @ts-ignore */
|
|
232
|
+
...(ngDevMode ? [{ debugName: "heightStyle" }] : /* istanbul ignore next */ []));
|
|
233
|
+
lineHeight = computed(() => cssSize(this.height()), /* @ts-ignore */
|
|
234
|
+
...(ngDevMode ? [{ debugName: "lineHeight" }] : /* istanbul ignore next */ []));
|
|
235
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeSkeletonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
236
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: NxeSkeletonComponent, isStandalone: true, selector: "nxe-skeleton", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, lines: { classPropertyName: "lines", publicName: "lines", isSignal: true, isRequired: false, transformFunction: null }, animated: { classPropertyName: "animated", publicName: "animated", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "aria-hidden": "true" }, properties: { "class.nxe-skeleton-text": "variant() === 'text'", "class.nxe-skeleton-rect": "variant() === 'rect'", "class.nxe-skeleton-circle": "variant() === 'circle'", "class.nxe-skeleton-static": "!animated()", "style.width": "widthStyle()", "style.height": "heightStyle()" }, classAttribute: "nxe-skeleton" }, ngImport: i0, template: "@if (variant() === 'text') {\r\n @for (line of lineIndexes(); track line) {\r\n <span\r\n class=\"nxe-skeleton-line\"\r\n [class.nxe-skeleton-line-last]=\"lineIndexes().length > 1 && line === lineIndexes().length - 1\"\r\n [style.height]=\"lineHeight()\"></span>\r\n }\r\n}\r\n", styles: ["nxe-skeleton.nxe-skeleton{--nxe-skeleton-base: var(--nxe-ui-neutral-100);--nxe-skeleton-highlight: var(--nxe-ui-neutral-150);display:block;box-sizing:border-box;max-width:100%}nxe-skeleton.nxe-skeleton.nxe-skeleton-rect,nxe-skeleton.nxe-skeleton.nxe-skeleton-circle,nxe-skeleton.nxe-skeleton .nxe-skeleton-line{background-color:var(--nxe-skeleton-base);background-image:linear-gradient(90deg,var(--nxe-skeleton-base) 0%,var(--nxe-skeleton-highlight) 50%,var(--nxe-skeleton-base) 100%);background-repeat:no-repeat;background-size:200% 100%;animation:nxe-skeleton-shimmer 1.4s ease-in-out infinite}nxe-skeleton.nxe-skeleton.nxe-skeleton-static.nxe-skeleton-rect,nxe-skeleton.nxe-skeleton.nxe-skeleton-static.nxe-skeleton-circle,nxe-skeleton.nxe-skeleton.nxe-skeleton-static .nxe-skeleton-line{background-image:none;animation:none}nxe-skeleton.nxe-skeleton.nxe-skeleton-rect{width:100%;height:var(--nxe-ui-space-8);border-radius:var(--nxe-ui-radius-sm)}nxe-skeleton.nxe-skeleton.nxe-skeleton-circle{flex:none;border-radius:50%}nxe-skeleton.nxe-skeleton.nxe-skeleton-text{width:100%}nxe-skeleton.nxe-skeleton .nxe-skeleton-line{display:block;height:var(--nxe-ui-space-3);border-radius:var(--nxe-ui-radius-sm)}nxe-skeleton.nxe-skeleton .nxe-skeleton-line+.nxe-skeleton-line{margin-top:var(--nxe-ui-space-2)}nxe-skeleton.nxe-skeleton .nxe-skeleton-line-last{width:60%}@keyframes nxe-skeleton-shimmer{0%{background-position:100% 0}to{background-position:-100% 0}}@media(prefers-reduced-motion:reduce){nxe-skeleton.nxe-skeleton.nxe-skeleton-rect,nxe-skeleton.nxe-skeleton.nxe-skeleton-circle,nxe-skeleton.nxe-skeleton .nxe-skeleton-line{background-image:none;animation:none}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
237
|
+
}
|
|
238
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeSkeletonComponent, decorators: [{
|
|
239
|
+
type: Component,
|
|
240
|
+
args: [{ selector: 'nxe-skeleton', host: {
|
|
241
|
+
class: 'nxe-skeleton',
|
|
242
|
+
'aria-hidden': 'true',
|
|
243
|
+
'[class.nxe-skeleton-text]': "variant() === 'text'",
|
|
244
|
+
'[class.nxe-skeleton-rect]': "variant() === 'rect'",
|
|
245
|
+
'[class.nxe-skeleton-circle]': "variant() === 'circle'",
|
|
246
|
+
'[class.nxe-skeleton-static]': '!animated()',
|
|
247
|
+
'[style.width]': 'widthStyle()',
|
|
248
|
+
'[style.height]': 'heightStyle()',
|
|
249
|
+
}, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "@if (variant() === 'text') {\r\n @for (line of lineIndexes(); track line) {\r\n <span\r\n class=\"nxe-skeleton-line\"\r\n [class.nxe-skeleton-line-last]=\"lineIndexes().length > 1 && line === lineIndexes().length - 1\"\r\n [style.height]=\"lineHeight()\"></span>\r\n }\r\n}\r\n", styles: ["nxe-skeleton.nxe-skeleton{--nxe-skeleton-base: var(--nxe-ui-neutral-100);--nxe-skeleton-highlight: var(--nxe-ui-neutral-150);display:block;box-sizing:border-box;max-width:100%}nxe-skeleton.nxe-skeleton.nxe-skeleton-rect,nxe-skeleton.nxe-skeleton.nxe-skeleton-circle,nxe-skeleton.nxe-skeleton .nxe-skeleton-line{background-color:var(--nxe-skeleton-base);background-image:linear-gradient(90deg,var(--nxe-skeleton-base) 0%,var(--nxe-skeleton-highlight) 50%,var(--nxe-skeleton-base) 100%);background-repeat:no-repeat;background-size:200% 100%;animation:nxe-skeleton-shimmer 1.4s ease-in-out infinite}nxe-skeleton.nxe-skeleton.nxe-skeleton-static.nxe-skeleton-rect,nxe-skeleton.nxe-skeleton.nxe-skeleton-static.nxe-skeleton-circle,nxe-skeleton.nxe-skeleton.nxe-skeleton-static .nxe-skeleton-line{background-image:none;animation:none}nxe-skeleton.nxe-skeleton.nxe-skeleton-rect{width:100%;height:var(--nxe-ui-space-8);border-radius:var(--nxe-ui-radius-sm)}nxe-skeleton.nxe-skeleton.nxe-skeleton-circle{flex:none;border-radius:50%}nxe-skeleton.nxe-skeleton.nxe-skeleton-text{width:100%}nxe-skeleton.nxe-skeleton .nxe-skeleton-line{display:block;height:var(--nxe-ui-space-3);border-radius:var(--nxe-ui-radius-sm)}nxe-skeleton.nxe-skeleton .nxe-skeleton-line+.nxe-skeleton-line{margin-top:var(--nxe-ui-space-2)}nxe-skeleton.nxe-skeleton .nxe-skeleton-line-last{width:60%}@keyframes nxe-skeleton-shimmer{0%{background-position:100% 0}to{background-position:-100% 0}}@media(prefers-reduced-motion:reduce){nxe-skeleton.nxe-skeleton.nxe-skeleton-rect,nxe-skeleton.nxe-skeleton.nxe-skeleton-circle,nxe-skeleton.nxe-skeleton .nxe-skeleton-line{background-image:none;animation:none}}\n"] }]
|
|
250
|
+
}], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], lines: [{ type: i0.Input, args: [{ isSignal: true, alias: "lines", required: false }] }], animated: [{ type: i0.Input, args: [{ isSignal: true, alias: "animated", required: false }] }] } });
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* `nxe-empty-state` invites the next action when there is nothing to show: an icon, a heading, one
|
|
254
|
+
* sentence, and the action.
|
|
255
|
+
*
|
|
256
|
+
* ```html
|
|
257
|
+
* <nxe-empty-state heading="Belum ada PO" description="Buat PO dari daftar harga pemasok.">
|
|
258
|
+
* <svg nxeEmptyStateIcon viewBox="0 0 24 24">...</svg>
|
|
259
|
+
* <nxe-button nxeEmptyStateActions type="default" text="Buat PO" />
|
|
260
|
+
* </nxe-empty-state>
|
|
261
|
+
* ```
|
|
262
|
+
*
|
|
263
|
+
* Slots, by content projection: `[nxeEmptyStateIcon]` above the heading, any other content under the
|
|
264
|
+
* description, and `[nxeEmptyStateActions]` (one or more buttons, laid out in a row) last. The
|
|
265
|
+
* heading input is `heading` rather than `title` so it does not also become a native tooltip on the
|
|
266
|
+
* host. It is exposed as a heading at `headingLevel` (3 by default), so set the level that fits the
|
|
267
|
+
* page's outline.
|
|
268
|
+
*/
|
|
269
|
+
class NxeEmptyStateComponent {
|
|
270
|
+
/** The headline: what is missing. */
|
|
271
|
+
heading = input('', /* @ts-ignore */
|
|
272
|
+
...(ngDevMode ? [{ debugName: "heading" }] : /* istanbul ignore next */ []));
|
|
273
|
+
/** One sentence on why, or what to do about it. */
|
|
274
|
+
description = input('', /* @ts-ignore */
|
|
275
|
+
...(ngDevMode ? [{ debugName: "description" }] : /* istanbul ignore next */ []));
|
|
276
|
+
/** `aria-level` of the heading, 1 to 6. */
|
|
277
|
+
headingLevel = input(3, { ...(ngDevMode ? { debugName: "headingLevel" } : /* istanbul ignore next */ {}), transform: (v) => Math.min(6, Math.max(1, Math.floor(Number(v)) || 3)) });
|
|
278
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeEmptyStateComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
279
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: NxeEmptyStateComponent, isStandalone: true, selector: "nxe-empty-state", inputs: { heading: { classPropertyName: "heading", publicName: "heading", isSignal: true, isRequired: false, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, headingLevel: { classPropertyName: "headingLevel", publicName: "headingLevel", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "nxe-empty-state" }, ngImport: i0, template: "<div class=\"nxe-empty-state-icon\"><ng-content select=\"[nxeEmptyStateIcon]\" /></div>\r\n@if (heading()) {\r\n <div class=\"nxe-empty-state-heading\" role=\"heading\" [attr.aria-level]=\"headingLevel()\">{{ heading() }}</div>\r\n}\r\n@if (description()) {\r\n <p class=\"nxe-empty-state-description\">{{ description() }}</p>\r\n}\r\n<div class=\"nxe-empty-state-body\"><ng-content /></div>\r\n<div class=\"nxe-empty-state-actions\"><ng-content select=\"[nxeEmptyStateActions]\" /></div>\r\n", styles: ["nxe-empty-state.nxe-empty-state{display:flex;flex-direction:column;align-items:center;gap:var(--nxe-ui-space-2);box-sizing:border-box;padding:var(--nxe-ui-space-8) var(--nxe-ui-space-4);text-align:center;color:var(--nxe-ui-text);font-family:var(--nxe-ui-font-family)}nxe-empty-state.nxe-empty-state .nxe-empty-state-icon:empty,nxe-empty-state.nxe-empty-state .nxe-empty-state-body:empty,nxe-empty-state.nxe-empty-state .nxe-empty-state-actions:empty{display:none}nxe-empty-state.nxe-empty-state .nxe-empty-state-icon{display:flex;color:var(--nxe-ui-neutral-450)}nxe-empty-state.nxe-empty-state .nxe-empty-state-icon>svg,nxe-empty-state.nxe-empty-state .nxe-empty-state-icon>.nxe-icon{width:var(--nxe-ui-space-8);height:var(--nxe-ui-space-8);fill:currentColor}nxe-empty-state.nxe-empty-state .nxe-empty-state-heading{font-size:var(--nxe-ui-font-size-lg);font-weight:var(--nxe-ui-font-weight-semibold);line-height:var(--nxe-ui-line-height-lg)}nxe-empty-state.nxe-empty-state .nxe-empty-state-description{max-width:44ch;margin:0;color:var(--nxe-ui-neutral-600);font-size:var(--nxe-ui-font-size-md);line-height:var(--nxe-ui-line-height-md)}nxe-empty-state.nxe-empty-state .nxe-empty-state-body{max-width:44ch}nxe-empty-state.nxe-empty-state .nxe-empty-state-actions{display:flex;flex-wrap:wrap;justify-content:center;gap:var(--nxe-ui-space-2);margin-top:var(--nxe-ui-space-2)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
280
|
+
}
|
|
281
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeEmptyStateComponent, decorators: [{
|
|
282
|
+
type: Component,
|
|
283
|
+
args: [{ selector: 'nxe-empty-state', host: { class: 'nxe-empty-state' }, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<div class=\"nxe-empty-state-icon\"><ng-content select=\"[nxeEmptyStateIcon]\" /></div>\r\n@if (heading()) {\r\n <div class=\"nxe-empty-state-heading\" role=\"heading\" [attr.aria-level]=\"headingLevel()\">{{ heading() }}</div>\r\n}\r\n@if (description()) {\r\n <p class=\"nxe-empty-state-description\">{{ description() }}</p>\r\n}\r\n<div class=\"nxe-empty-state-body\"><ng-content /></div>\r\n<div class=\"nxe-empty-state-actions\"><ng-content select=\"[nxeEmptyStateActions]\" /></div>\r\n", styles: ["nxe-empty-state.nxe-empty-state{display:flex;flex-direction:column;align-items:center;gap:var(--nxe-ui-space-2);box-sizing:border-box;padding:var(--nxe-ui-space-8) var(--nxe-ui-space-4);text-align:center;color:var(--nxe-ui-text);font-family:var(--nxe-ui-font-family)}nxe-empty-state.nxe-empty-state .nxe-empty-state-icon:empty,nxe-empty-state.nxe-empty-state .nxe-empty-state-body:empty,nxe-empty-state.nxe-empty-state .nxe-empty-state-actions:empty{display:none}nxe-empty-state.nxe-empty-state .nxe-empty-state-icon{display:flex;color:var(--nxe-ui-neutral-450)}nxe-empty-state.nxe-empty-state .nxe-empty-state-icon>svg,nxe-empty-state.nxe-empty-state .nxe-empty-state-icon>.nxe-icon{width:var(--nxe-ui-space-8);height:var(--nxe-ui-space-8);fill:currentColor}nxe-empty-state.nxe-empty-state .nxe-empty-state-heading{font-size:var(--nxe-ui-font-size-lg);font-weight:var(--nxe-ui-font-weight-semibold);line-height:var(--nxe-ui-line-height-lg)}nxe-empty-state.nxe-empty-state .nxe-empty-state-description{max-width:44ch;margin:0;color:var(--nxe-ui-neutral-600);font-size:var(--nxe-ui-font-size-md);line-height:var(--nxe-ui-line-height-md)}nxe-empty-state.nxe-empty-state .nxe-empty-state-body{max-width:44ch}nxe-empty-state.nxe-empty-state .nxe-empty-state-actions{display:flex;flex-wrap:wrap;justify-content:center;gap:var(--nxe-ui-space-2);margin-top:var(--nxe-ui-space-2)}\n"] }]
|
|
284
|
+
}], propDecorators: { heading: [{ type: i0.Input, args: [{ isSignal: true, alias: "heading", required: false }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], headingLevel: [{ type: i0.Input, args: [{ isSignal: true, alias: "headingLevel", required: false }] }] } });
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* `nxe-card` groups related content on a surface: an optional header, the body, an optional footer.
|
|
288
|
+
*
|
|
289
|
+
* ```html
|
|
290
|
+
* <nxe-card heading="PO-2026-0412" subheading="Diterbitkan 12 Okt 2026">
|
|
291
|
+
* <nxe-tag nxeCardActions family="attention" dot>Menunggu konfirmasi</nxe-tag>
|
|
292
|
+
* <nxe-button nxeCardActions text="Setujui" type="default" />
|
|
293
|
+
*
|
|
294
|
+
* Body content goes here.
|
|
295
|
+
*
|
|
296
|
+
* <div nxeCardFooter>Total: Rp 12.500.000</div>
|
|
297
|
+
* </nxe-card>
|
|
298
|
+
* ```
|
|
299
|
+
*
|
|
300
|
+
* Slots, by content projection: `heading` and `subheading` inputs (or `[nxeCardTitle]` and
|
|
301
|
+
* `[nxeCardSubtitle]` for markup), `[nxeCardActions]` at the end of the header, anything else as the
|
|
302
|
+
* body and `[nxeCardFooter]` last. A header or footer with nothing in it draws nothing. The heading
|
|
303
|
+
* input is `heading`, not `title`, so it does not also become a native tooltip, and it is exposed as a
|
|
304
|
+
* heading at `headingLevel` (3 by default).
|
|
305
|
+
*
|
|
306
|
+
* Padding is half the control height, so it tightens under `.nxe-density-compact` with the controls;
|
|
307
|
+
* set `--nxe-card-padding` on the card to override.
|
|
308
|
+
*/
|
|
309
|
+
class NxeCardComponent {
|
|
310
|
+
/** `outlined` (default) or `elevated`. */
|
|
311
|
+
variant = input('outlined', /* @ts-ignore */
|
|
312
|
+
...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
|
|
313
|
+
/** The card's title. */
|
|
314
|
+
heading = input('', /* @ts-ignore */
|
|
315
|
+
...(ngDevMode ? [{ debugName: "heading" }] : /* istanbul ignore next */ []));
|
|
316
|
+
/** A line under the title. */
|
|
317
|
+
subheading = input('', /* @ts-ignore */
|
|
318
|
+
...(ngDevMode ? [{ debugName: "subheading" }] : /* istanbul ignore next */ []));
|
|
319
|
+
/** `aria-level` of the heading, 1 to 6. */
|
|
320
|
+
headingLevel = input(3, { ...(ngDevMode ? { debugName: "headingLevel" } : /* istanbul ignore next */ {}), transform: (v) => Math.min(6, Math.max(1, Math.floor(Number(v)) || 3)) });
|
|
321
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
322
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: NxeCardComponent, isStandalone: true, selector: "nxe-card", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, heading: { classPropertyName: "heading", publicName: "heading", isSignal: true, isRequired: false, transformFunction: null }, subheading: { classPropertyName: "subheading", publicName: "subheading", isSignal: true, isRequired: false, transformFunction: null }, headingLevel: { classPropertyName: "headingLevel", publicName: "headingLevel", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "'nxe-card-' + variant()" }, classAttribute: "nxe-card" }, ngImport: i0, template: "<div class=\"nxe-card-header\">\r\n <div class=\"nxe-card-titles\">\r\n @if (heading()) {\r\n <div class=\"nxe-card-heading\" role=\"heading\" [attr.aria-level]=\"headingLevel()\">{{ heading() }}</div>\r\n }\r\n <div class=\"nxe-card-title-slot\"><ng-content select=\"[nxeCardTitle]\" /></div>\r\n @if (subheading()) {\r\n <div class=\"nxe-card-subheading\">{{ subheading() }}</div>\r\n }\r\n <div class=\"nxe-card-subtitle-slot\"><ng-content select=\"[nxeCardSubtitle]\" /></div>\r\n </div>\r\n <div class=\"nxe-card-actions\"><ng-content select=\"[nxeCardActions]\" /></div>\r\n</div>\r\n<div class=\"nxe-card-body\"><ng-content /></div>\r\n<div class=\"nxe-card-footer\"><ng-content select=\"[nxeCardFooter]\" /></div>\r\n", styles: ["nxe-card.nxe-card{--nxe-card-pad: var(--nxe-card-padding, calc(var(--nxe-ui-control-min-height) / 2));display:block;box-sizing:border-box;border-radius:var(--nxe-ui-radius-lg);background-color:var(--nxe-ui-surface);color:var(--nxe-ui-text);font-family:var(--nxe-ui-font-family)}nxe-card.nxe-card.nxe-card-outlined{border:1px solid var(--nxe-ui-border)}nxe-card.nxe-card.nxe-card-elevated{border:1px solid transparent;box-shadow:var(--nxe-ui-shadow-sm)}nxe-card.nxe-card .nxe-card-title-slot:empty,nxe-card.nxe-card .nxe-card-subtitle-slot:empty,nxe-card.nxe-card .nxe-card-actions:empty,nxe-card.nxe-card .nxe-card-body:empty,nxe-card.nxe-card .nxe-card-footer:empty{display:none}nxe-card.nxe-card .nxe-card-header:not(:has(.nxe-card-heading,.nxe-card-subheading,.nxe-card-title-slot:not(:empty),.nxe-card-subtitle-slot:not(:empty),.nxe-card-actions:not(:empty))){display:none}nxe-card.nxe-card .nxe-card-header{display:flex;align-items:flex-start;justify-content:space-between;flex-wrap:wrap;gap:var(--nxe-ui-space-2) var(--nxe-ui-space-4);padding:var(--nxe-card-pad);border-bottom:1px solid var(--nxe-ui-border)}nxe-card.nxe-card .nxe-card-titles{display:flex;flex-direction:column;gap:2px;min-width:0}nxe-card.nxe-card .nxe-card-heading,nxe-card.nxe-card .nxe-card-title-slot{font-size:var(--nxe-ui-font-size-lg);font-weight:var(--nxe-ui-font-weight-semibold);line-height:var(--nxe-ui-line-height-lg)}nxe-card.nxe-card .nxe-card-subheading,nxe-card.nxe-card .nxe-card-subtitle-slot{color:var(--nxe-ui-neutral-600);font-size:var(--nxe-ui-font-size-md);line-height:var(--nxe-ui-line-height-md)}nxe-card.nxe-card .nxe-card-actions{display:flex;flex-wrap:wrap;align-items:center;gap:var(--nxe-ui-space-2);margin-inline-start:auto}nxe-card.nxe-card .nxe-card-body{padding:var(--nxe-card-pad)}nxe-card.nxe-card .nxe-card-footer{padding:var(--nxe-card-pad);border-top:1px solid var(--nxe-ui-border)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
323
|
+
}
|
|
324
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeCardComponent, decorators: [{
|
|
325
|
+
type: Component,
|
|
326
|
+
args: [{ selector: 'nxe-card', host: {
|
|
327
|
+
class: 'nxe-card',
|
|
328
|
+
'[class]': "'nxe-card-' + variant()",
|
|
329
|
+
}, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<div class=\"nxe-card-header\">\r\n <div class=\"nxe-card-titles\">\r\n @if (heading()) {\r\n <div class=\"nxe-card-heading\" role=\"heading\" [attr.aria-level]=\"headingLevel()\">{{ heading() }}</div>\r\n }\r\n <div class=\"nxe-card-title-slot\"><ng-content select=\"[nxeCardTitle]\" /></div>\r\n @if (subheading()) {\r\n <div class=\"nxe-card-subheading\">{{ subheading() }}</div>\r\n }\r\n <div class=\"nxe-card-subtitle-slot\"><ng-content select=\"[nxeCardSubtitle]\" /></div>\r\n </div>\r\n <div class=\"nxe-card-actions\"><ng-content select=\"[nxeCardActions]\" /></div>\r\n</div>\r\n<div class=\"nxe-card-body\"><ng-content /></div>\r\n<div class=\"nxe-card-footer\"><ng-content select=\"[nxeCardFooter]\" /></div>\r\n", styles: ["nxe-card.nxe-card{--nxe-card-pad: var(--nxe-card-padding, calc(var(--nxe-ui-control-min-height) / 2));display:block;box-sizing:border-box;border-radius:var(--nxe-ui-radius-lg);background-color:var(--nxe-ui-surface);color:var(--nxe-ui-text);font-family:var(--nxe-ui-font-family)}nxe-card.nxe-card.nxe-card-outlined{border:1px solid var(--nxe-ui-border)}nxe-card.nxe-card.nxe-card-elevated{border:1px solid transparent;box-shadow:var(--nxe-ui-shadow-sm)}nxe-card.nxe-card .nxe-card-title-slot:empty,nxe-card.nxe-card .nxe-card-subtitle-slot:empty,nxe-card.nxe-card .nxe-card-actions:empty,nxe-card.nxe-card .nxe-card-body:empty,nxe-card.nxe-card .nxe-card-footer:empty{display:none}nxe-card.nxe-card .nxe-card-header:not(:has(.nxe-card-heading,.nxe-card-subheading,.nxe-card-title-slot:not(:empty),.nxe-card-subtitle-slot:not(:empty),.nxe-card-actions:not(:empty))){display:none}nxe-card.nxe-card .nxe-card-header{display:flex;align-items:flex-start;justify-content:space-between;flex-wrap:wrap;gap:var(--nxe-ui-space-2) var(--nxe-ui-space-4);padding:var(--nxe-card-pad);border-bottom:1px solid var(--nxe-ui-border)}nxe-card.nxe-card .nxe-card-titles{display:flex;flex-direction:column;gap:2px;min-width:0}nxe-card.nxe-card .nxe-card-heading,nxe-card.nxe-card .nxe-card-title-slot{font-size:var(--nxe-ui-font-size-lg);font-weight:var(--nxe-ui-font-weight-semibold);line-height:var(--nxe-ui-line-height-lg)}nxe-card.nxe-card .nxe-card-subheading,nxe-card.nxe-card .nxe-card-subtitle-slot{color:var(--nxe-ui-neutral-600);font-size:var(--nxe-ui-font-size-md);line-height:var(--nxe-ui-line-height-md)}nxe-card.nxe-card .nxe-card-actions{display:flex;flex-wrap:wrap;align-items:center;gap:var(--nxe-ui-space-2);margin-inline-start:auto}nxe-card.nxe-card .nxe-card-body{padding:var(--nxe-card-pad)}nxe-card.nxe-card .nxe-card-footer{padding:var(--nxe-card-pad);border-top:1px solid var(--nxe-ui-border)}\n"] }]
|
|
330
|
+
}], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], heading: [{ type: i0.Input, args: [{ isSignal: true, alias: "heading", required: false }] }], subheading: [{ type: i0.Input, args: [{ isSignal: true, alias: "subheading", required: false }] }], headingLevel: [{ type: i0.Input, args: [{ isSignal: true, alias: "headingLevel", required: false }] }] } });
|
|
331
|
+
|
|
332
|
+
/** Clamps to an integer in `[min, max]`; `fallback` when it is not a number. */
|
|
333
|
+
function clampInt(value, min, max, fallback) {
|
|
334
|
+
const n = Math.floor(Number(value));
|
|
335
|
+
return Number.isFinite(n) ? Math.min(max, Math.max(min, n)) : fallback;
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* `nxe-description-list` shows label and value pairs, the way a document's details read: PO number,
|
|
339
|
+
* supplier, dates. It is a real `<dl>` with a `<dt>` and `<dd>` per pair.
|
|
340
|
+
*
|
|
341
|
+
* ```html
|
|
342
|
+
* <nxe-description-list [columns]="3">
|
|
343
|
+
* <nxe-description-item label="PO number">PO-2026-0412</nxe-description-item>
|
|
344
|
+
* <nxe-description-item label="Supplier">PT Sinar Jaya</nxe-description-item>
|
|
345
|
+
* <nxe-description-item label="Notes" [span]="3">{{ po.notes }}</nxe-description-item>
|
|
346
|
+
* </nxe-description-list>
|
|
347
|
+
*
|
|
348
|
+
* <nxe-description-list [items]="[{ label: 'PO number', value: po.number }]" />
|
|
349
|
+
* ```
|
|
350
|
+
*
|
|
351
|
+
* - **Columns** 1 to 4 (`columns`, default 2). The list answers to its own width, not the window's:
|
|
352
|
+
* under 48rem three and four columns fall to two, under 28rem everything is one column, so the same
|
|
353
|
+
* list works in a wide page, a narrow card or a drawer.
|
|
354
|
+
* - **Layout** `horizontal` (default) puts the label in a `labelWidth` column (default `10rem`) beside
|
|
355
|
+
* the value; `vertical` stacks them.
|
|
356
|
+
* - **Empty values** show the `placeholder` (default an em dash), which reads as "No value" to a
|
|
357
|
+
* screen reader instead of "em dash". An item is empty when it has no content at all, so project
|
|
358
|
+
* `{{ value }}` with nothing around it.
|
|
359
|
+
* - **Content.** Use `nxe-description-item` for a value that is markup (a tag, a link) and `items` for
|
|
360
|
+
* plain text. They can be mixed; `items` come first.
|
|
361
|
+
*/
|
|
362
|
+
class NxeDescriptionListComponent {
|
|
363
|
+
messages = inject(NxeMessageService);
|
|
364
|
+
/** Label and value pairs; rendered before any projected `nxe-description-item`. */
|
|
365
|
+
items = input([], /* @ts-ignore */
|
|
366
|
+
...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
|
|
367
|
+
/** Columns, 1 to 4. */
|
|
368
|
+
columns = input(2, { ...(ngDevMode ? { debugName: "columns" } : /* istanbul ignore next */ {}), transform: (v) => clampInt(v, 1, 4, 2) });
|
|
369
|
+
/** `horizontal` (default) or `vertical`. */
|
|
370
|
+
layout = input('horizontal', /* @ts-ignore */
|
|
371
|
+
...(ngDevMode ? [{ debugName: "layout" }] : /* istanbul ignore next */ []));
|
|
372
|
+
/** Width of the label column in the horizontal layout: a number is px, a string any CSS length. */
|
|
373
|
+
labelWidth = input('10rem', /* @ts-ignore */
|
|
374
|
+
...(ngDevMode ? [{ debugName: "labelWidth" }] : /* istanbul ignore next */ []));
|
|
375
|
+
/** Shown for an empty value. */
|
|
376
|
+
placeholder = input('—', /* @ts-ignore */
|
|
377
|
+
...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
|
|
378
|
+
labelWidthCss = computed(() => {
|
|
379
|
+
const width = this.labelWidth();
|
|
380
|
+
return typeof width === 'number' ? `${width}px` : width;
|
|
381
|
+
}, /* @ts-ignore */
|
|
382
|
+
...(ngDevMode ? [{ debugName: "labelWidthCss" }] : /* istanbul ignore next */ []));
|
|
383
|
+
/** What assistive tech reads in place of the placeholder glyph. */
|
|
384
|
+
placeholderLabel = computed(() => this.messages.t('display.noValue'), /* @ts-ignore */
|
|
385
|
+
...(ngDevMode ? [{ debugName: "placeholderLabel" }] : /* istanbul ignore next */ []));
|
|
386
|
+
text(entry) {
|
|
387
|
+
const value = entry.value;
|
|
388
|
+
return value === null || value === undefined ? '' : String(value).trim();
|
|
389
|
+
}
|
|
390
|
+
span(entry) {
|
|
391
|
+
return clampInt(entry.span, 1, 4, 1);
|
|
392
|
+
}
|
|
393
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeDescriptionListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
394
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: NxeDescriptionListComponent, isStandalone: true, selector: "nxe-description-list", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, layout: { classPropertyName: "layout", publicName: "layout", isSignal: true, isRequired: false, transformFunction: null }, labelWidth: { classPropertyName: "labelWidth", publicName: "labelWidth", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "nxe-description-list" }, ngImport: i0, template: "<dl\r\n class=\"nxe-dl\"\r\n [class]=\"'nxe-dl nxe-dl-cols-' + columns() + ' nxe-dl-' + layout()\"\r\n [style.--nxe-dl-label-width]=\"labelWidthCss()\">\r\n @for (entry of items(); track $index) {\r\n <div class=\"nxe-dl-row\" [class]=\"'nxe-dl-row nxe-dl-span-' + span(entry)\">\r\n <dt class=\"nxe-dl-label\">{{ entry.label }}</dt>\r\n <dd\r\n class=\"nxe-dl-value\"\r\n [attr.data-placeholder]=\"placeholder()\"\r\n [attr.data-placeholder-label]=\"placeholderLabel()\">{{ text(entry) }}</dd>\r\n </div>\r\n }\r\n <ng-content />\r\n</dl>\r\n", styles: ["nxe-description-list.nxe-description-list{display:block;container:nxe-dl/inline-size;color:var(--nxe-ui-text);font-family:var(--nxe-ui-font-family);font-size:var(--nxe-ui-font-size-md);line-height:var(--nxe-ui-line-height-md)}nxe-description-list.nxe-description-list .nxe-dl{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--nxe-ui-space-3) var(--nxe-ui-space-6);margin:0}nxe-description-list.nxe-description-list .nxe-dl-row{display:grid;gap:var(--nxe-ui-space-1) var(--nxe-ui-space-3);min-width:0}nxe-description-list.nxe-description-list .nxe-dl-horizontal .nxe-dl-row{grid-template-columns:var(--nxe-dl-label-width, 10rem) minmax(0,1fr)}nxe-description-list.nxe-description-list .nxe-dl-vertical .nxe-dl-row{grid-template-columns:minmax(0,1fr)}nxe-description-list.nxe-description-list .nxe-dl-label{margin:0;color:var(--nxe-ui-neutral-600)}nxe-description-list.nxe-description-list .nxe-dl-value{margin:0;min-width:0;overflow-wrap:anywhere}nxe-description-list.nxe-description-list .nxe-dl-value:empty:before{content:attr(data-placeholder);content:attr(data-placeholder)/attr(data-placeholder-label);color:var(--nxe-ui-neutral-600)}nxe-description-list.nxe-description-list .nxe-dl-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}nxe-description-list.nxe-description-list .nxe-dl-span-1{grid-column:span 1}nxe-description-list.nxe-description-list .nxe-dl-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}nxe-description-list.nxe-description-list .nxe-dl-span-2{grid-column:span 2}nxe-description-list.nxe-description-list .nxe-dl-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}nxe-description-list.nxe-description-list .nxe-dl-span-3{grid-column:span 3}nxe-description-list.nxe-description-list .nxe-dl-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}nxe-description-list.nxe-description-list .nxe-dl-span-4{grid-column:span 4}nxe-description-list.nxe-description-list nxe-description-item{display:contents}@container nxe-dl (max-width: 48rem){nxe-description-list.nxe-description-list .nxe-dl-cols-3,nxe-description-list.nxe-description-list .nxe-dl-cols-4{grid-template-columns:repeat(2,minmax(0,1fr))}nxe-description-list.nxe-description-list .nxe-dl-span-3,nxe-description-list.nxe-description-list .nxe-dl-span-4{grid-column:span 2}}@container nxe-dl (max-width: 28rem){nxe-description-list.nxe-description-list .nxe-dl-cols-2,nxe-description-list.nxe-description-list .nxe-dl-cols-3,nxe-description-list.nxe-description-list .nxe-dl-cols-4{grid-template-columns:minmax(0,1fr)}nxe-description-list.nxe-description-list .nxe-dl-span-2,nxe-description-list.nxe-description-list .nxe-dl-span-3,nxe-description-list.nxe-description-list .nxe-dl-span-4{grid-column:auto}nxe-description-list.nxe-description-list .nxe-dl-horizontal .nxe-dl-row{grid-template-columns:minmax(0,1fr)}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
395
|
+
}
|
|
396
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeDescriptionListComponent, decorators: [{
|
|
397
|
+
type: Component,
|
|
398
|
+
args: [{ selector: 'nxe-description-list', host: { class: 'nxe-description-list' }, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<dl\r\n class=\"nxe-dl\"\r\n [class]=\"'nxe-dl nxe-dl-cols-' + columns() + ' nxe-dl-' + layout()\"\r\n [style.--nxe-dl-label-width]=\"labelWidthCss()\">\r\n @for (entry of items(); track $index) {\r\n <div class=\"nxe-dl-row\" [class]=\"'nxe-dl-row nxe-dl-span-' + span(entry)\">\r\n <dt class=\"nxe-dl-label\">{{ entry.label }}</dt>\r\n <dd\r\n class=\"nxe-dl-value\"\r\n [attr.data-placeholder]=\"placeholder()\"\r\n [attr.data-placeholder-label]=\"placeholderLabel()\">{{ text(entry) }}</dd>\r\n </div>\r\n }\r\n <ng-content />\r\n</dl>\r\n", styles: ["nxe-description-list.nxe-description-list{display:block;container:nxe-dl/inline-size;color:var(--nxe-ui-text);font-family:var(--nxe-ui-font-family);font-size:var(--nxe-ui-font-size-md);line-height:var(--nxe-ui-line-height-md)}nxe-description-list.nxe-description-list .nxe-dl{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--nxe-ui-space-3) var(--nxe-ui-space-6);margin:0}nxe-description-list.nxe-description-list .nxe-dl-row{display:grid;gap:var(--nxe-ui-space-1) var(--nxe-ui-space-3);min-width:0}nxe-description-list.nxe-description-list .nxe-dl-horizontal .nxe-dl-row{grid-template-columns:var(--nxe-dl-label-width, 10rem) minmax(0,1fr)}nxe-description-list.nxe-description-list .nxe-dl-vertical .nxe-dl-row{grid-template-columns:minmax(0,1fr)}nxe-description-list.nxe-description-list .nxe-dl-label{margin:0;color:var(--nxe-ui-neutral-600)}nxe-description-list.nxe-description-list .nxe-dl-value{margin:0;min-width:0;overflow-wrap:anywhere}nxe-description-list.nxe-description-list .nxe-dl-value:empty:before{content:attr(data-placeholder);content:attr(data-placeholder)/attr(data-placeholder-label);color:var(--nxe-ui-neutral-600)}nxe-description-list.nxe-description-list .nxe-dl-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}nxe-description-list.nxe-description-list .nxe-dl-span-1{grid-column:span 1}nxe-description-list.nxe-description-list .nxe-dl-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}nxe-description-list.nxe-description-list .nxe-dl-span-2{grid-column:span 2}nxe-description-list.nxe-description-list .nxe-dl-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}nxe-description-list.nxe-description-list .nxe-dl-span-3{grid-column:span 3}nxe-description-list.nxe-description-list .nxe-dl-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}nxe-description-list.nxe-description-list .nxe-dl-span-4{grid-column:span 4}nxe-description-list.nxe-description-list nxe-description-item{display:contents}@container nxe-dl (max-width: 48rem){nxe-description-list.nxe-description-list .nxe-dl-cols-3,nxe-description-list.nxe-description-list .nxe-dl-cols-4{grid-template-columns:repeat(2,minmax(0,1fr))}nxe-description-list.nxe-description-list .nxe-dl-span-3,nxe-description-list.nxe-description-list .nxe-dl-span-4{grid-column:span 2}}@container nxe-dl (max-width: 28rem){nxe-description-list.nxe-description-list .nxe-dl-cols-2,nxe-description-list.nxe-description-list .nxe-dl-cols-3,nxe-description-list.nxe-description-list .nxe-dl-cols-4{grid-template-columns:minmax(0,1fr)}nxe-description-list.nxe-description-list .nxe-dl-span-2,nxe-description-list.nxe-description-list .nxe-dl-span-3,nxe-description-list.nxe-description-list .nxe-dl-span-4{grid-column:auto}nxe-description-list.nxe-description-list .nxe-dl-horizontal .nxe-dl-row{grid-template-columns:minmax(0,1fr)}}\n"] }]
|
|
399
|
+
}], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], layout: [{ type: i0.Input, args: [{ isSignal: true, alias: "layout", required: false }] }], labelWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelWidth", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }] } });
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* One label and value of an {@link NxeDescriptionListComponent}; the projected content is the value.
|
|
403
|
+
* It draws a `<dt>` and `<dd>` inside the list's `<dl>` and takes no box of its own.
|
|
404
|
+
*
|
|
405
|
+
* ```html
|
|
406
|
+
* <nxe-description-item label="Status"><nxe-tag family="success">Disetujui</nxe-tag></nxe-description-item>
|
|
407
|
+
* ```
|
|
408
|
+
*
|
|
409
|
+
* With nothing projected the list's placeholder shows instead.
|
|
410
|
+
*/
|
|
411
|
+
class NxeDescriptionItemComponent {
|
|
412
|
+
list = inject(NxeDescriptionListComponent, { optional: true });
|
|
413
|
+
/** The term. */
|
|
414
|
+
label = input('', /* @ts-ignore */
|
|
415
|
+
...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
416
|
+
/** Columns the pair spans, 1 to 4. */
|
|
417
|
+
span = input(1, { ...(ngDevMode ? { debugName: "span" } : /* istanbul ignore next */ {}), transform: (v) => {
|
|
418
|
+
const n = Math.floor(Number(v));
|
|
419
|
+
return Number.isFinite(n) ? Math.min(4, Math.max(1, n)) : 1;
|
|
420
|
+
} });
|
|
421
|
+
placeholder = computed(() => this.list?.placeholder() ?? '—', /* @ts-ignore */
|
|
422
|
+
...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
|
|
423
|
+
placeholderLabel = computed(() => this.list?.placeholderLabel() ?? '', /* @ts-ignore */
|
|
424
|
+
...(ngDevMode ? [{ debugName: "placeholderLabel" }] : /* istanbul ignore next */ []));
|
|
425
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeDescriptionItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
426
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.5", type: NxeDescriptionItemComponent, isStandalone: true, selector: "nxe-description-item", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, span: { classPropertyName: "span", publicName: "span", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "none" }, classAttribute: "nxe-description-item" }, ngImport: i0, template: "<div class=\"nxe-dl-row\" [class]=\"'nxe-dl-row nxe-dl-span-' + span()\">\r\n <dt class=\"nxe-dl-label\">{{ label() }}</dt>\r\n <dd\r\n class=\"nxe-dl-value\"\r\n [attr.data-placeholder]=\"placeholder()\"\r\n [attr.data-placeholder-label]=\"placeholderLabel()\"><ng-content /></dd>\r\n</div>\r\n", changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
427
|
+
}
|
|
428
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeDescriptionItemComponent, decorators: [{
|
|
429
|
+
type: Component,
|
|
430
|
+
args: [{ selector: 'nxe-description-item', host: { class: 'nxe-description-item', role: 'none' }, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<div class=\"nxe-dl-row\" [class]=\"'nxe-dl-row nxe-dl-span-' + span()\">\r\n <dt class=\"nxe-dl-label\">{{ label() }}</dt>\r\n <dd\r\n class=\"nxe-dl-value\"\r\n [attr.data-placeholder]=\"placeholder()\"\r\n [attr.data-placeholder-label]=\"placeholderLabel()\"><ng-content /></dd>\r\n</div>\r\n" }]
|
|
431
|
+
}], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], span: [{ type: i0.Input, args: [{ isSignal: true, alias: "span", required: false }] }] } });
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Marks an element (an inline `<svg>`, an `<i>`) as the icon inside a timeline item's dot, in place of
|
|
435
|
+
* the plain dot. It is decorative: the title says what happened.
|
|
436
|
+
*
|
|
437
|
+
* ```html
|
|
438
|
+
* <nxe-timeline-item title="Approved" family="success">
|
|
439
|
+
* <svg nxeTimelineIcon viewBox="0 0 16 16"><path d="M3 8.5l3 3L13 5" /></svg>
|
|
440
|
+
* </nxe-timeline-item>
|
|
441
|
+
* ```
|
|
442
|
+
*/
|
|
443
|
+
class NxeTimelineIconDirective {
|
|
444
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeTimelineIconDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
445
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.5", type: NxeTimelineIconDirective, isStandalone: true, selector: "[nxeTimelineIcon]", ngImport: i0 });
|
|
446
|
+
}
|
|
447
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeTimelineIconDirective, decorators: [{
|
|
448
|
+
type: Directive,
|
|
449
|
+
args: [{
|
|
450
|
+
selector: '[nxeTimelineIcon]',
|
|
451
|
+
}]
|
|
452
|
+
}] });
|
|
453
|
+
|
|
454
|
+
/** Any accepted time as a valid Luxon `DateTime`, or `null` when it is missing or unparseable. */
|
|
455
|
+
function toTimelineDateTime(value) {
|
|
456
|
+
if (value === null || value === undefined || value === '') {
|
|
457
|
+
return null;
|
|
458
|
+
}
|
|
459
|
+
let parsed;
|
|
460
|
+
if (DateTime.isDateTime(value)) {
|
|
461
|
+
parsed = value;
|
|
462
|
+
}
|
|
463
|
+
else if (value instanceof Date) {
|
|
464
|
+
parsed = DateTime.fromJSDate(value);
|
|
465
|
+
}
|
|
466
|
+
else if (typeof value === 'number') {
|
|
467
|
+
parsed = DateTime.fromMillis(value);
|
|
468
|
+
}
|
|
469
|
+
else {
|
|
470
|
+
parsed = DateTime.fromISO(value);
|
|
471
|
+
if (!parsed.isValid) {
|
|
472
|
+
parsed = DateTime.fromJSDate(new Date(value));
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
return parsed.isValid ? parsed : null;
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* One entry of an {@link NxeTimelineComponent}. It draws nothing itself: the timeline renders the
|
|
479
|
+
* `<li>` from the inputs and content declared here, so the list stays a real `<ol>` of `<li>`s.
|
|
480
|
+
*
|
|
481
|
+
* ```html
|
|
482
|
+
* <nxe-timeline-item
|
|
483
|
+
* title="PO confirmed"
|
|
484
|
+
* actor="CV Pagoda Jaya"
|
|
485
|
+
* [time]="event.at"
|
|
486
|
+
* family="success"
|
|
487
|
+
* description="Delivery set for 9 Oct">
|
|
488
|
+
* <a href="/pos/42">PO-HWG-2610-0042</a>
|
|
489
|
+
* </nxe-timeline-item>
|
|
490
|
+
* <nxe-timeline-item title="Delivery" pending />
|
|
491
|
+
* ```
|
|
492
|
+
*
|
|
493
|
+
* Projected content is the item's body under the description; `[nxeTimelineIcon]` is the dot's icon.
|
|
494
|
+
*/
|
|
495
|
+
class NxeTimelineItemComponent {
|
|
496
|
+
/** What happened. */
|
|
497
|
+
title = input('', /* @ts-ignore */
|
|
498
|
+
...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
|
|
499
|
+
/** A line under the title. */
|
|
500
|
+
description = input('', /* @ts-ignore */
|
|
501
|
+
...(ngDevMode ? [{ debugName: "description" }] : /* istanbul ignore next */ []));
|
|
502
|
+
/** Who did it: a person, a company or "System". */
|
|
503
|
+
actor = input('', /* @ts-ignore */
|
|
504
|
+
...(ngDevMode ? [{ debugName: "actor" }] : /* istanbul ignore next */ []));
|
|
505
|
+
/** When it happened. */
|
|
506
|
+
time = input(undefined, /* @ts-ignore */
|
|
507
|
+
...(ngDevMode ? [{ debugName: "time" }] : /* istanbul ignore next */ []));
|
|
508
|
+
/** Status family of the dot (and of the status tag, when `statusLabel` is set). */
|
|
509
|
+
family = input('neutral', /* @ts-ignore */
|
|
510
|
+
...(ngDevMode ? [{ debugName: "family" }] : /* istanbul ignore next */ []));
|
|
511
|
+
/** Shown as a status tag beside the title, so the status is words and not only a colour. */
|
|
512
|
+
statusLabel = input('', /* @ts-ignore */
|
|
513
|
+
...(ngDevMode ? [{ debugName: "statusLabel" }] : /* istanbul ignore next */ []));
|
|
514
|
+
/** Something still to come: a dashed, hollow dot and "Pending" for assistive technology. */
|
|
515
|
+
pending = input(false, { ...(ngDevMode ? { debugName: "pending" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
516
|
+
iconDirective = contentChild(NxeTimelineIconDirective, /* @ts-ignore */
|
|
517
|
+
...(ngDevMode ? [{ debugName: "iconDirective" }] : /* istanbul ignore next */ []));
|
|
518
|
+
iconTemplate = viewChild.required('icon', /* @ts-ignore */
|
|
519
|
+
...(ngDevMode ? [{ debugName: "iconTemplate" }] : /* istanbul ignore next */ []));
|
|
520
|
+
bodyTemplate = viewChild.required('body', /* @ts-ignore */
|
|
521
|
+
...(ngDevMode ? [{ debugName: "bodyTemplate" }] : /* istanbul ignore next */ []));
|
|
522
|
+
/** The time as a Luxon `DateTime`, or `null`. */
|
|
523
|
+
dateTime = computed(() => toTimelineDateTime(this.time()), /* @ts-ignore */
|
|
524
|
+
...(ngDevMode ? [{ debugName: "dateTime" }] : /* istanbul ignore next */ []));
|
|
525
|
+
/** True when an `[nxeTimelineIcon]` was projected. */
|
|
526
|
+
hasIcon = computed(() => this.iconDirective() !== undefined, /* @ts-ignore */
|
|
527
|
+
...(ngDevMode ? [{ debugName: "hasIcon" }] : /* istanbul ignore next */ []));
|
|
528
|
+
/** The icon slot, to stamp into the dot. */
|
|
529
|
+
icon = computed(() => this.iconTemplate(), /* @ts-ignore */
|
|
530
|
+
...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
|
|
531
|
+
/** The body slot, to stamp under the description. */
|
|
532
|
+
body = computed(() => this.bodyTemplate(), /* @ts-ignore */
|
|
533
|
+
...(ngDevMode ? [{ debugName: "body" }] : /* istanbul ignore next */ []));
|
|
534
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeTimelineItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
535
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.5", type: NxeTimelineItemComponent, isStandalone: true, selector: "nxe-timeline-item", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, actor: { classPropertyName: "actor", publicName: "actor", isSignal: true, isRequired: false, transformFunction: null }, time: { classPropertyName: "time", publicName: "time", isSignal: true, isRequired: false, transformFunction: null }, family: { classPropertyName: "family", publicName: "family", isSignal: true, isRequired: false, transformFunction: null }, statusLabel: { classPropertyName: "statusLabel", publicName: "statusLabel", isSignal: true, isRequired: false, transformFunction: null }, pending: { classPropertyName: "pending", publicName: "pending", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "nxe-timeline-item-def" }, queries: [{ propertyName: "iconDirective", first: true, predicate: NxeTimelineIconDirective, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "iconTemplate", first: true, predicate: ["icon"], descendants: true, isSignal: true }, { propertyName: "bodyTemplate", first: true, predicate: ["body"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
536
|
+
<ng-template #icon><ng-content select="[nxeTimelineIcon]" /></ng-template>
|
|
537
|
+
<ng-template #body><ng-content /></ng-template>
|
|
538
|
+
`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
539
|
+
}
|
|
540
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeTimelineItemComponent, decorators: [{
|
|
541
|
+
type: Component,
|
|
542
|
+
args: [{
|
|
543
|
+
selector: 'nxe-timeline-item',
|
|
544
|
+
template: `
|
|
545
|
+
<ng-template #icon><ng-content select="[nxeTimelineIcon]" /></ng-template>
|
|
546
|
+
<ng-template #body><ng-content /></ng-template>
|
|
547
|
+
`,
|
|
548
|
+
host: { class: 'nxe-timeline-item-def' },
|
|
549
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
550
|
+
}]
|
|
551
|
+
}], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], actor: [{ type: i0.Input, args: [{ isSignal: true, alias: "actor", required: false }] }], time: [{ type: i0.Input, args: [{ isSignal: true, alias: "time", required: false }] }], family: [{ type: i0.Input, args: [{ isSignal: true, alias: "family", required: false }] }], statusLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "statusLabel", required: false }] }], pending: [{ type: i0.Input, args: [{ isSignal: true, alias: "pending", required: false }] }], iconDirective: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NxeTimelineIconDirective), { isSignal: true }] }], iconTemplate: [{ type: i0.ViewChild, args: ['icon', { isSignal: true }] }], bodyTemplate: [{ type: i0.ViewChild, args: ['body', { isSignal: true }] }] } });
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* `nxe-timeline` lists what happened to something, newest or oldest first as you give it: a document's
|
|
555
|
+
* status history, an approval trail, an activity log.
|
|
556
|
+
*
|
|
557
|
+
* ```html
|
|
558
|
+
* <nxe-timeline label="Activity" timeFormat="auto">
|
|
559
|
+
* <nxe-timeline-item title="PO confirmed" actor="CV Pagoda Jaya" [time]="at" family="success" />
|
|
560
|
+
* <nxe-timeline-item title="PO sent" actor="Siti A." [time]="earlier" family="info" />
|
|
561
|
+
* <nxe-timeline-item title="Delivery" pending />
|
|
562
|
+
* </nxe-timeline>
|
|
563
|
+
* ```
|
|
564
|
+
*
|
|
565
|
+
* It is a real `<ol>` of `<li>`, each time is a `<time datetime>`, and the dot is decorative: the title (and
|
|
566
|
+
* `statusLabel`, which adds a status tag) carries the meaning, and a `pending` entry is announced as pending.
|
|
567
|
+
* Time text follows the active locale (`NxeMessageService`, through Luxon), so it switches language with the rest.
|
|
568
|
+
*
|
|
569
|
+
* `layout="alternate"` puts the entries on both sides of a centre rail once the timeline is 520px wide or
|
|
570
|
+
* more, and falls back to the left layout below that.
|
|
571
|
+
*/
|
|
572
|
+
class NxeTimelineComponent {
|
|
573
|
+
/** `left` (default) or `alternate`. */
|
|
574
|
+
layout = input('left', /* @ts-ignore */
|
|
575
|
+
...(ngDevMode ? [{ debugName: "layout" }] : /* istanbul ignore next */ []));
|
|
576
|
+
/** `comfortable` (default) or `compact`. */
|
|
577
|
+
density = input('comfortable', /* @ts-ignore */
|
|
578
|
+
...(ngDevMode ? [{ debugName: "density" }] : /* istanbul ignore next */ []));
|
|
579
|
+
/** `absolute` (default), `relative` or `auto`. */
|
|
580
|
+
timeFormat = input('absolute', /* @ts-ignore */
|
|
581
|
+
...(ngDevMode ? [{ debugName: "timeFormat" }] : /* istanbul ignore next */ []));
|
|
582
|
+
/** Luxon format string for absolute times (`'dd MMM yyyy HH:mm'`); unset uses the locale's medium date and time. */
|
|
583
|
+
absoluteFormat = input(undefined, /* @ts-ignore */
|
|
584
|
+
...(ngDevMode ? [{ debugName: "absoluteFormat" }] : /* istanbul ignore next */ []));
|
|
585
|
+
/** What "now" is for relative times. Unset, the moment each time is formatted. */
|
|
586
|
+
now = input(undefined, /* @ts-ignore */
|
|
587
|
+
...(ngDevMode ? [{ debugName: "now" }] : /* istanbul ignore next */ []));
|
|
588
|
+
/** Accessible name of the list. */
|
|
589
|
+
label = input('', /* @ts-ignore */
|
|
590
|
+
...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
591
|
+
items = contentChildren(NxeTimelineItemComponent, /* @ts-ignore */
|
|
592
|
+
...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
|
|
593
|
+
messages = inject(NxeMessageService);
|
|
594
|
+
pendingText = computed(() => this.messages.t('timeline.pending'), /* @ts-ignore */
|
|
595
|
+
...(ngDevMode ? [{ debugName: "pendingText" }] : /* istanbul ignore next */ []));
|
|
596
|
+
views = computed(() => {
|
|
597
|
+
const locale = this.messages.locale();
|
|
598
|
+
const mode = this.timeFormat();
|
|
599
|
+
const absoluteFormat = this.absoluteFormat();
|
|
600
|
+
const now = toTimelineDateTime(this.now()) ?? DateTime.now();
|
|
601
|
+
return this.items().map((item) => {
|
|
602
|
+
const dt = item.dateTime()?.setLocale(locale) ?? null;
|
|
603
|
+
if (!dt) {
|
|
604
|
+
return { item, iso: null, text: '', tooltip: null };
|
|
605
|
+
}
|
|
606
|
+
const absolute = absoluteFormat ? dt.toFormat(absoluteFormat) : dt.toLocaleString(DateTime.DATETIME_MED);
|
|
607
|
+
const relative = mode === 'absolute' || (mode === 'auto' && Math.abs(dt.diff(now, 'days').days) >= 7)
|
|
608
|
+
? null
|
|
609
|
+
: dt.toRelative({ base: now });
|
|
610
|
+
return {
|
|
611
|
+
item,
|
|
612
|
+
iso: dt.toISO(),
|
|
613
|
+
text: relative ?? absolute,
|
|
614
|
+
tooltip: relative ? absolute : null,
|
|
615
|
+
};
|
|
616
|
+
});
|
|
617
|
+
}, /* @ts-ignore */
|
|
618
|
+
...(ngDevMode ? [{ debugName: "views" }] : /* istanbul ignore next */ []));
|
|
619
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeTimelineComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
620
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: NxeTimelineComponent, isStandalone: true, selector: "nxe-timeline", inputs: { layout: { classPropertyName: "layout", publicName: "layout", isSignal: true, isRequired: false, transformFunction: null }, density: { classPropertyName: "density", publicName: "density", isSignal: true, isRequired: false, transformFunction: null }, timeFormat: { classPropertyName: "timeFormat", publicName: "timeFormat", isSignal: true, isRequired: false, transformFunction: null }, absoluteFormat: { classPropertyName: "absoluteFormat", publicName: "absoluteFormat", isSignal: true, isRequired: false, transformFunction: null }, now: { classPropertyName: "now", publicName: "now", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "'nxe-timeline-layout-' + layout() + ' nxe-timeline-density-' + density()" }, classAttribute: "nxe-timeline" }, queries: [{ propertyName: "items", predicate: NxeTimelineItemComponent, isSignal: true }], ngImport: i0, template: "<ol class=\"nxe-timeline-list\" [attr.aria-label]=\"label() || null\">\r\n @for (view of views(); track view.item) {\r\n <li\r\n class=\"nxe-timeline-entry nxe-timeline-family-{{ view.item.family() }}\"\r\n [class.nxe-timeline-pending]=\"view.item.pending()\"\r\n [class.nxe-timeline-has-icon]=\"view.item.hasIcon()\">\r\n <div class=\"nxe-timeline-rail\" aria-hidden=\"true\">\r\n <span class=\"nxe-timeline-dot\">\r\n @if (view.item.hasIcon()) {\r\n <ng-container [ngTemplateOutlet]=\"view.item.icon()\" />\r\n }\r\n </span>\r\n </div>\r\n <div class=\"nxe-timeline-body\">\r\n <div class=\"nxe-timeline-head\">\r\n @if (view.item.pending()) {\r\n <span class=\"nxe-timeline-hidden\">{{ pendingText() }}: </span>\r\n }\r\n <span class=\"nxe-timeline-title\">{{ view.item.title() }}</span>\r\n @if (view.item.statusLabel()) {\r\n <nxe-tag size=\"sm\" [family]=\"view.item.family()\" dot>{{ view.item.statusLabel() }}</nxe-tag>\r\n }\r\n </div>\r\n @if (view.iso || view.item.actor()) {\r\n <div class=\"nxe-timeline-meta\">\r\n @if (view.iso) {\r\n <time class=\"nxe-timeline-time\" [attr.datetime]=\"view.iso\" [attr.title]=\"view.tooltip\">{{ view.text }}</time>\r\n }\r\n @if (view.iso && view.item.actor()) {\r\n <span class=\"nxe-timeline-sep\" aria-hidden=\"true\">·</span>\r\n }\r\n @if (view.item.actor()) {\r\n <span class=\"nxe-timeline-actor\">{{ view.item.actor() }}</span>\r\n }\r\n </div>\r\n }\r\n @if (view.item.description()) {\r\n <p class=\"nxe-timeline-description\">{{ view.item.description() }}</p>\r\n }\r\n <div class=\"nxe-timeline-content\"><ng-container [ngTemplateOutlet]=\"view.item.body()\" /></div>\r\n </div>\r\n </li>\r\n }\r\n</ol>\r\n", styles: ["nxe-timeline.nxe-timeline{--nxe-timeline-dot: var(--nxe-ui-space-3);--nxe-timeline-rail: var(--nxe-ui-space-6);--nxe-timeline-gap: var(--nxe-ui-space-5);--nxe-status-border-style: solid;display:block;container-type:inline-size;color:var(--nxe-ui-text);font-family:var(--nxe-ui-font-family)}nxe-timeline.nxe-timeline.nxe-timeline-density-compact{--nxe-timeline-gap: var(--nxe-ui-space-2)}nxe-timeline.nxe-timeline .nxe-timeline-hidden{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}nxe-timeline.nxe-timeline .nxe-timeline-list{margin:0;padding:0;list-style:none}nxe-timeline.nxe-timeline .nxe-timeline-family-neutral{--nxe-status-fg: var(--nxe-ui-status-neutral-fg);--nxe-status-bg: var(--nxe-ui-status-neutral-bg);--nxe-status-border: var(--nxe-ui-status-neutral-border)}nxe-timeline.nxe-timeline .nxe-timeline-family-info{--nxe-status-fg: var(--nxe-ui-status-info-fg);--nxe-status-bg: var(--nxe-ui-status-info-bg);--nxe-status-border: var(--nxe-ui-status-info-border)}nxe-timeline.nxe-timeline .nxe-timeline-family-attention{--nxe-status-fg: var(--nxe-ui-status-attention-fg);--nxe-status-bg: var(--nxe-ui-status-attention-bg);--nxe-status-border: var(--nxe-ui-status-attention-border)}nxe-timeline.nxe-timeline .nxe-timeline-family-success{--nxe-status-fg: var(--nxe-ui-status-success-fg);--nxe-status-bg: var(--nxe-ui-status-success-bg);--nxe-status-border: var(--nxe-ui-status-success-border)}nxe-timeline.nxe-timeline .nxe-timeline-family-danger{--nxe-status-fg: var(--nxe-ui-status-danger-fg);--nxe-status-bg: var(--nxe-ui-status-danger-bg);--nxe-status-border: var(--nxe-ui-status-danger-border)}nxe-timeline.nxe-timeline .nxe-timeline-family-muted{--nxe-status-fg: var(--nxe-ui-status-muted-fg);--nxe-status-bg: var(--nxe-ui-status-muted-bg);--nxe-status-border: var(--nxe-ui-status-muted-border)}nxe-timeline.nxe-timeline .nxe-timeline-family-muted{--nxe-status-border-style: dashed}nxe-timeline.nxe-timeline .nxe-timeline-entry{display:grid;grid-template-columns:var(--nxe-timeline-rail) minmax(0,1fr);column-gap:var(--nxe-ui-space-2)}nxe-timeline.nxe-timeline .nxe-timeline-rail{position:relative;grid-column:1;grid-row:1;display:flex;justify-content:center}nxe-timeline.nxe-timeline .nxe-timeline-rail:after{content:\"\";position:absolute;inset-block:calc(var(--nxe-timeline-dot) + var(--nxe-ui-space-2) + var(--nxe-ui-space-1)) 0;inset-inline-start:50%;border-inline-start:1px solid var(--nxe-ui-border)}nxe-timeline.nxe-timeline .nxe-timeline-entry:last-child .nxe-timeline-rail:after{display:none}nxe-timeline.nxe-timeline .nxe-timeline-dot{display:inline-flex;flex:0 0 auto;align-items:center;justify-content:center;box-sizing:border-box;width:var(--nxe-timeline-dot);height:var(--nxe-timeline-dot);margin-top:var(--nxe-ui-space-1);border-radius:var(--nxe-ui-radius-pill);background:var(--nxe-status-fg);box-shadow:0 0 0 var(--nxe-ui-space-1) var(--nxe-ui-surface);color:var(--nxe-status-fg)}nxe-timeline.nxe-timeline .nxe-timeline-has-icon .nxe-timeline-dot{width:calc(var(--nxe-ui-space-5) + var(--nxe-ui-space-1));height:calc(var(--nxe-ui-space-5) + var(--nxe-ui-space-1));margin-top:0;border:1px solid var(--nxe-status-border);background:var(--nxe-status-bg)}nxe-timeline.nxe-timeline .nxe-timeline-has-icon .nxe-timeline-dot svg,nxe-timeline.nxe-timeline .nxe-timeline-has-icon .nxe-timeline-dot i{width:var(--nxe-ui-font-size-md);height:var(--nxe-ui-font-size-md);fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}nxe-timeline.nxe-timeline .nxe-timeline-has-icon .nxe-timeline-rail:after{inset-block-start:calc(var(--nxe-ui-space-5) + var(--nxe-ui-space-1) + var(--nxe-ui-space-1))}nxe-timeline.nxe-timeline .nxe-timeline-pending .nxe-timeline-dot{border:2px dashed var(--nxe-status-fg);background:var(--nxe-ui-surface)}nxe-timeline.nxe-timeline .nxe-timeline-pending .nxe-timeline-rail:after{border-inline-start-style:dashed}nxe-timeline.nxe-timeline .nxe-timeline-pending .nxe-timeline-title{color:var(--nxe-ui-text-muted);font-weight:var(--nxe-ui-font-weight)}nxe-timeline.nxe-timeline .nxe-timeline-body{grid-column:2;grid-row:1;min-width:0;padding-bottom:var(--nxe-timeline-gap)}nxe-timeline.nxe-timeline .nxe-timeline-entry:last-child .nxe-timeline-body{padding-bottom:0}nxe-timeline.nxe-timeline .nxe-timeline-head{display:flex;flex-wrap:wrap;align-items:center;gap:var(--nxe-ui-space-2)}nxe-timeline.nxe-timeline .nxe-timeline-title{font-weight:var(--nxe-ui-font-weight-semibold)}nxe-timeline.nxe-timeline .nxe-timeline-meta{display:flex;flex-wrap:wrap;gap:var(--nxe-ui-space-1) var(--nxe-ui-space-2);color:var(--nxe-ui-text-muted);font-size:var(--nxe-ui-font-size-sm)}nxe-timeline.nxe-timeline .nxe-timeline-time{font-variant-numeric:tabular-nums}nxe-timeline.nxe-timeline .nxe-timeline-description{margin:var(--nxe-ui-space-1) 0 0;font-size:var(--nxe-ui-font-size-sm)}nxe-timeline.nxe-timeline .nxe-timeline-content{margin-top:var(--nxe-ui-space-2);font-size:var(--nxe-ui-font-size-sm)}nxe-timeline.nxe-timeline .nxe-timeline-content:empty{display:none}@container (min-width: 520px){nxe-timeline.nxe-timeline-layout-alternate .nxe-timeline-entry{grid-template-columns:minmax(0,1fr) var(--nxe-timeline-rail) minmax(0,1fr)}nxe-timeline.nxe-timeline-layout-alternate .nxe-timeline-rail{grid-column:2}nxe-timeline.nxe-timeline-layout-alternate .nxe-timeline-entry:nth-child(odd) .nxe-timeline-body{grid-column:1;text-align:end}nxe-timeline.nxe-timeline-layout-alternate .nxe-timeline-entry:nth-child(odd) .nxe-timeline-body .nxe-timeline-head,nxe-timeline.nxe-timeline-layout-alternate .nxe-timeline-entry:nth-child(odd) .nxe-timeline-body .nxe-timeline-meta{justify-content:flex-end}nxe-timeline.nxe-timeline-layout-alternate .nxe-timeline-entry:nth-child(2n) .nxe-timeline-body{grid-column:3}}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: NxeTagComponent, selector: "nxe-tag", inputs: ["family", "stylingMode", "size", "dot"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
621
|
+
}
|
|
622
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeTimelineComponent, decorators: [{
|
|
623
|
+
type: Component,
|
|
624
|
+
args: [{ selector: 'nxe-timeline', imports: [NgTemplateOutlet, NxeTagComponent], host: {
|
|
625
|
+
class: 'nxe-timeline',
|
|
626
|
+
'[class]': "'nxe-timeline-layout-' + layout() + ' nxe-timeline-density-' + density()",
|
|
627
|
+
}, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<ol class=\"nxe-timeline-list\" [attr.aria-label]=\"label() || null\">\r\n @for (view of views(); track view.item) {\r\n <li\r\n class=\"nxe-timeline-entry nxe-timeline-family-{{ view.item.family() }}\"\r\n [class.nxe-timeline-pending]=\"view.item.pending()\"\r\n [class.nxe-timeline-has-icon]=\"view.item.hasIcon()\">\r\n <div class=\"nxe-timeline-rail\" aria-hidden=\"true\">\r\n <span class=\"nxe-timeline-dot\">\r\n @if (view.item.hasIcon()) {\r\n <ng-container [ngTemplateOutlet]=\"view.item.icon()\" />\r\n }\r\n </span>\r\n </div>\r\n <div class=\"nxe-timeline-body\">\r\n <div class=\"nxe-timeline-head\">\r\n @if (view.item.pending()) {\r\n <span class=\"nxe-timeline-hidden\">{{ pendingText() }}: </span>\r\n }\r\n <span class=\"nxe-timeline-title\">{{ view.item.title() }}</span>\r\n @if (view.item.statusLabel()) {\r\n <nxe-tag size=\"sm\" [family]=\"view.item.family()\" dot>{{ view.item.statusLabel() }}</nxe-tag>\r\n }\r\n </div>\r\n @if (view.iso || view.item.actor()) {\r\n <div class=\"nxe-timeline-meta\">\r\n @if (view.iso) {\r\n <time class=\"nxe-timeline-time\" [attr.datetime]=\"view.iso\" [attr.title]=\"view.tooltip\">{{ view.text }}</time>\r\n }\r\n @if (view.iso && view.item.actor()) {\r\n <span class=\"nxe-timeline-sep\" aria-hidden=\"true\">·</span>\r\n }\r\n @if (view.item.actor()) {\r\n <span class=\"nxe-timeline-actor\">{{ view.item.actor() }}</span>\r\n }\r\n </div>\r\n }\r\n @if (view.item.description()) {\r\n <p class=\"nxe-timeline-description\">{{ view.item.description() }}</p>\r\n }\r\n <div class=\"nxe-timeline-content\"><ng-container [ngTemplateOutlet]=\"view.item.body()\" /></div>\r\n </div>\r\n </li>\r\n }\r\n</ol>\r\n", styles: ["nxe-timeline.nxe-timeline{--nxe-timeline-dot: var(--nxe-ui-space-3);--nxe-timeline-rail: var(--nxe-ui-space-6);--nxe-timeline-gap: var(--nxe-ui-space-5);--nxe-status-border-style: solid;display:block;container-type:inline-size;color:var(--nxe-ui-text);font-family:var(--nxe-ui-font-family)}nxe-timeline.nxe-timeline.nxe-timeline-density-compact{--nxe-timeline-gap: var(--nxe-ui-space-2)}nxe-timeline.nxe-timeline .nxe-timeline-hidden{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}nxe-timeline.nxe-timeline .nxe-timeline-list{margin:0;padding:0;list-style:none}nxe-timeline.nxe-timeline .nxe-timeline-family-neutral{--nxe-status-fg: var(--nxe-ui-status-neutral-fg);--nxe-status-bg: var(--nxe-ui-status-neutral-bg);--nxe-status-border: var(--nxe-ui-status-neutral-border)}nxe-timeline.nxe-timeline .nxe-timeline-family-info{--nxe-status-fg: var(--nxe-ui-status-info-fg);--nxe-status-bg: var(--nxe-ui-status-info-bg);--nxe-status-border: var(--nxe-ui-status-info-border)}nxe-timeline.nxe-timeline .nxe-timeline-family-attention{--nxe-status-fg: var(--nxe-ui-status-attention-fg);--nxe-status-bg: var(--nxe-ui-status-attention-bg);--nxe-status-border: var(--nxe-ui-status-attention-border)}nxe-timeline.nxe-timeline .nxe-timeline-family-success{--nxe-status-fg: var(--nxe-ui-status-success-fg);--nxe-status-bg: var(--nxe-ui-status-success-bg);--nxe-status-border: var(--nxe-ui-status-success-border)}nxe-timeline.nxe-timeline .nxe-timeline-family-danger{--nxe-status-fg: var(--nxe-ui-status-danger-fg);--nxe-status-bg: var(--nxe-ui-status-danger-bg);--nxe-status-border: var(--nxe-ui-status-danger-border)}nxe-timeline.nxe-timeline .nxe-timeline-family-muted{--nxe-status-fg: var(--nxe-ui-status-muted-fg);--nxe-status-bg: var(--nxe-ui-status-muted-bg);--nxe-status-border: var(--nxe-ui-status-muted-border)}nxe-timeline.nxe-timeline .nxe-timeline-family-muted{--nxe-status-border-style: dashed}nxe-timeline.nxe-timeline .nxe-timeline-entry{display:grid;grid-template-columns:var(--nxe-timeline-rail) minmax(0,1fr);column-gap:var(--nxe-ui-space-2)}nxe-timeline.nxe-timeline .nxe-timeline-rail{position:relative;grid-column:1;grid-row:1;display:flex;justify-content:center}nxe-timeline.nxe-timeline .nxe-timeline-rail:after{content:\"\";position:absolute;inset-block:calc(var(--nxe-timeline-dot) + var(--nxe-ui-space-2) + var(--nxe-ui-space-1)) 0;inset-inline-start:50%;border-inline-start:1px solid var(--nxe-ui-border)}nxe-timeline.nxe-timeline .nxe-timeline-entry:last-child .nxe-timeline-rail:after{display:none}nxe-timeline.nxe-timeline .nxe-timeline-dot{display:inline-flex;flex:0 0 auto;align-items:center;justify-content:center;box-sizing:border-box;width:var(--nxe-timeline-dot);height:var(--nxe-timeline-dot);margin-top:var(--nxe-ui-space-1);border-radius:var(--nxe-ui-radius-pill);background:var(--nxe-status-fg);box-shadow:0 0 0 var(--nxe-ui-space-1) var(--nxe-ui-surface);color:var(--nxe-status-fg)}nxe-timeline.nxe-timeline .nxe-timeline-has-icon .nxe-timeline-dot{width:calc(var(--nxe-ui-space-5) + var(--nxe-ui-space-1));height:calc(var(--nxe-ui-space-5) + var(--nxe-ui-space-1));margin-top:0;border:1px solid var(--nxe-status-border);background:var(--nxe-status-bg)}nxe-timeline.nxe-timeline .nxe-timeline-has-icon .nxe-timeline-dot svg,nxe-timeline.nxe-timeline .nxe-timeline-has-icon .nxe-timeline-dot i{width:var(--nxe-ui-font-size-md);height:var(--nxe-ui-font-size-md);fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}nxe-timeline.nxe-timeline .nxe-timeline-has-icon .nxe-timeline-rail:after{inset-block-start:calc(var(--nxe-ui-space-5) + var(--nxe-ui-space-1) + var(--nxe-ui-space-1))}nxe-timeline.nxe-timeline .nxe-timeline-pending .nxe-timeline-dot{border:2px dashed var(--nxe-status-fg);background:var(--nxe-ui-surface)}nxe-timeline.nxe-timeline .nxe-timeline-pending .nxe-timeline-rail:after{border-inline-start-style:dashed}nxe-timeline.nxe-timeline .nxe-timeline-pending .nxe-timeline-title{color:var(--nxe-ui-text-muted);font-weight:var(--nxe-ui-font-weight)}nxe-timeline.nxe-timeline .nxe-timeline-body{grid-column:2;grid-row:1;min-width:0;padding-bottom:var(--nxe-timeline-gap)}nxe-timeline.nxe-timeline .nxe-timeline-entry:last-child .nxe-timeline-body{padding-bottom:0}nxe-timeline.nxe-timeline .nxe-timeline-head{display:flex;flex-wrap:wrap;align-items:center;gap:var(--nxe-ui-space-2)}nxe-timeline.nxe-timeline .nxe-timeline-title{font-weight:var(--nxe-ui-font-weight-semibold)}nxe-timeline.nxe-timeline .nxe-timeline-meta{display:flex;flex-wrap:wrap;gap:var(--nxe-ui-space-1) var(--nxe-ui-space-2);color:var(--nxe-ui-text-muted);font-size:var(--nxe-ui-font-size-sm)}nxe-timeline.nxe-timeline .nxe-timeline-time{font-variant-numeric:tabular-nums}nxe-timeline.nxe-timeline .nxe-timeline-description{margin:var(--nxe-ui-space-1) 0 0;font-size:var(--nxe-ui-font-size-sm)}nxe-timeline.nxe-timeline .nxe-timeline-content{margin-top:var(--nxe-ui-space-2);font-size:var(--nxe-ui-font-size-sm)}nxe-timeline.nxe-timeline .nxe-timeline-content:empty{display:none}@container (min-width: 520px){nxe-timeline.nxe-timeline-layout-alternate .nxe-timeline-entry{grid-template-columns:minmax(0,1fr) var(--nxe-timeline-rail) minmax(0,1fr)}nxe-timeline.nxe-timeline-layout-alternate .nxe-timeline-rail{grid-column:2}nxe-timeline.nxe-timeline-layout-alternate .nxe-timeline-entry:nth-child(odd) .nxe-timeline-body{grid-column:1;text-align:end}nxe-timeline.nxe-timeline-layout-alternate .nxe-timeline-entry:nth-child(odd) .nxe-timeline-body .nxe-timeline-head,nxe-timeline.nxe-timeline-layout-alternate .nxe-timeline-entry:nth-child(odd) .nxe-timeline-body .nxe-timeline-meta{justify-content:flex-end}nxe-timeline.nxe-timeline-layout-alternate .nxe-timeline-entry:nth-child(2n) .nxe-timeline-body{grid-column:3}}\n"] }]
|
|
628
|
+
}], propDecorators: { layout: [{ type: i0.Input, args: [{ isSignal: true, alias: "layout", required: false }] }], density: [{ type: i0.Input, args: [{ isSignal: true, alias: "density", required: false }] }], timeFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "timeFormat", required: false }] }], absoluteFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "absoluteFormat", required: false }] }], now: [{ type: i0.Input, args: [{ isSignal: true, alias: "now", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], items: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => NxeTimelineItemComponent), { isSignal: true }] }] } });
|
|
629
|
+
|
|
630
|
+
/** @internal */
|
|
631
|
+
const NXE_AVATAR_GROUP = new InjectionToken('NXE_AVATAR_GROUP');
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Words that are a form of address or a legal form rather than part of the name: Indonesian and
|
|
635
|
+
* English honorifics (`Bapak`, `Ibu`, `Dr.`, `Ir.`, `H.`, `Hj.`, `Prof.`, `Drs.`) and company prefixes
|
|
636
|
+
* (`PT`, `CV`, `UD`, `PD`). `PT Maju Jaya` is "MJ", not "PM".
|
|
637
|
+
*/
|
|
638
|
+
const NOISE = new Set([
|
|
639
|
+
'bapak', 'bpk', 'ibu', 'sdr', 'sdri', 'tn', 'ny', 'nn',
|
|
640
|
+
'dr', 'drs', 'dra', 'ir', 'prof', 'h', 'hj', 'kh', 'mr', 'mrs', 'ms', 'mx',
|
|
641
|
+
'pt', 'cv', 'ud', 'pd', 'yayasan', 'tbk',
|
|
642
|
+
]);
|
|
643
|
+
/** First letter or digit of a word, upper-cased; `''` when there is none. */
|
|
644
|
+
function firstGlyph(word) {
|
|
645
|
+
const match = word.match(/[\p{L}\p{N}]/u);
|
|
646
|
+
return match ? match[0].toLocaleUpperCase() : '';
|
|
647
|
+
}
|
|
648
|
+
/**
|
|
649
|
+
* Up to two initials for a person's or company's name.
|
|
650
|
+
*
|
|
651
|
+
* - First and last word: `Siti Nur Aisyah` is "SA".
|
|
652
|
+
* - **A single name** (common in Indonesia: `Suharto`, `Megawati`) gives its first two letters,
|
|
653
|
+
* "SU", because one letter tells a list of avatars nothing apart. A one-letter name gives one.
|
|
654
|
+
* - A title or legal form is skipped (`Dr. Ir. H. Budi Santoso` is "BS", `PT Maju Jaya` is "MJ"),
|
|
655
|
+
* and so is everything after the first comma (`Budi Santoso, S.Kom., M.T.` is "BS").
|
|
656
|
+
* - Letters from any script are kept (`李雷` is "李雷"); characters outside the BMP are not split.
|
|
657
|
+
*
|
|
658
|
+
* Returns `''` for an empty or symbol-only name, where the avatar draws a person glyph instead.
|
|
659
|
+
*/
|
|
660
|
+
function nxeAvatarInitials(name) {
|
|
661
|
+
const head = (name ?? '').split(',')[0];
|
|
662
|
+
const words = head
|
|
663
|
+
.split(/[\s._]+/u)
|
|
664
|
+
.map((word) => word.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu, ''))
|
|
665
|
+
.filter((word) => word !== '' && /[\p{L}\p{N}]/u.test(word));
|
|
666
|
+
const meaningful = words.filter((word) => !NOISE.has(word.toLocaleLowerCase()));
|
|
667
|
+
const used = meaningful.length > 0 ? meaningful : words;
|
|
668
|
+
if (used.length === 0) {
|
|
669
|
+
return '';
|
|
670
|
+
}
|
|
671
|
+
if (used.length === 1) {
|
|
672
|
+
const letters = Array.from(used[0]).filter((glyph) => /[\p{L}\p{N}]/u.test(glyph));
|
|
673
|
+
return letters.slice(0, 2).join('').toLocaleUpperCase();
|
|
674
|
+
}
|
|
675
|
+
return firstGlyph(used[0]) + firstGlyph(used[used.length - 1]);
|
|
676
|
+
}
|
|
677
|
+
/**
|
|
678
|
+
* The twelve avatar colours: the six status families, each subtle and solid, interleaved so that
|
|
679
|
+
* neighbouring hash values differ in hue. Both modes pair a family's `fg` with its `bg` (swapped for
|
|
680
|
+
* solid), the same pairs `nxe-tag` ships, so every entry keeps its initials at 4.5:1 or more in the
|
|
681
|
+
* light and the dark theme (`nxe-avatar-contrast.spec.ts` measures all twelve).
|
|
682
|
+
*/
|
|
683
|
+
const NXE_AVATAR_TONES = [
|
|
684
|
+
{ family: 'info', mode: 'solid' },
|
|
685
|
+
{ family: 'success', mode: 'subtle' },
|
|
686
|
+
{ family: 'attention', mode: 'solid' },
|
|
687
|
+
{ family: 'neutral', mode: 'subtle' },
|
|
688
|
+
{ family: 'danger', mode: 'solid' },
|
|
689
|
+
{ family: 'info', mode: 'subtle' },
|
|
690
|
+
{ family: 'success', mode: 'solid' },
|
|
691
|
+
{ family: 'attention', mode: 'subtle' },
|
|
692
|
+
{ family: 'muted', mode: 'solid' },
|
|
693
|
+
{ family: 'danger', mode: 'subtle' },
|
|
694
|
+
{ family: 'neutral', mode: 'solid' },
|
|
695
|
+
{ family: 'muted', mode: 'subtle' },
|
|
696
|
+
];
|
|
697
|
+
/**
|
|
698
|
+
* Which of {@link NXE_AVATAR_TONES} a name gets: a 32-bit FNV-1a hash of the trimmed, case-folded
|
|
699
|
+
* name, modulo twelve. The same name always gives the same colour, in every session and on every
|
|
700
|
+
* device, and `Siti` and `siti ` agree.
|
|
701
|
+
*/
|
|
702
|
+
function nxeAvatarToneIndex(name) {
|
|
703
|
+
const text = (name ?? '').trim().toLocaleLowerCase();
|
|
704
|
+
let hash = 0x811c9dc5;
|
|
705
|
+
for (let i = 0; i < text.length; i++) {
|
|
706
|
+
hash ^= text.charCodeAt(i);
|
|
707
|
+
hash = Math.imul(hash, 0x01000193) >>> 0;
|
|
708
|
+
}
|
|
709
|
+
return hash % NXE_AVATAR_TONES.length;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
const STATUS_FAMILY = {
|
|
713
|
+
online: 'success',
|
|
714
|
+
away: 'attention',
|
|
715
|
+
busy: 'danger',
|
|
716
|
+
offline: 'muted',
|
|
717
|
+
};
|
|
718
|
+
/**
|
|
719
|
+
* `nxe-avatar` shows who or what something belongs to: a picture, or when there is none (or it fails
|
|
720
|
+
* to load) the initials of the name on a colour the name always gets.
|
|
721
|
+
*
|
|
722
|
+
* ```html
|
|
723
|
+
* <nxe-avatar name="Siti Nur Aisyah" src="/photos/siti.jpg" />
|
|
724
|
+
* <nxe-avatar name="PT Maju Jaya" shape="square" size="lg" />
|
|
725
|
+
* <nxe-avatar name="Budi" status="online" />
|
|
726
|
+
* ```
|
|
727
|
+
*
|
|
728
|
+
* **Initials.** From `name`: first and last word, skipping titles and company forms (`Dr. Ir. H. Budi
|
|
729
|
+
* Santoso, M.T.` is "BS", `PT Maju Jaya` is "MJ"); a single name, common in Indonesia, gives its first
|
|
730
|
+
* two letters (`Suharto` is "SU"). `initials` overrides. With no name it draws a person glyph.
|
|
731
|
+
*
|
|
732
|
+
* **Colour.** The name picks one of twelve status-family pairs (six families, tinted or inverted) by a
|
|
733
|
+
* stable hash, so a person is the same colour everywhere. Every pair keeps the initials at 4.5:1 or
|
|
734
|
+
* more in both themes. `color` fixes a family instead. No colour carries meaning: the name does.
|
|
735
|
+
*
|
|
736
|
+
* **Accessibility.** The avatar is a labelled image, `role="img"`, named by `alt`, else by `name`
|
|
737
|
+
* (plus the status, if any: "Siti Nur Aisyah, Online"). Next to the person's name in text it adds
|
|
738
|
+
* nothing, so set `decorative` (or `alt=""`) and it is hidden from assistive technology. With neither
|
|
739
|
+
* a label nor a name it is hidden too. The picture itself is always presentational.
|
|
740
|
+
*/
|
|
741
|
+
class NxeAvatarComponent {
|
|
742
|
+
messages = inject(NxeMessageService);
|
|
743
|
+
group = inject(NXE_AVATAR_GROUP, { optional: true });
|
|
744
|
+
/** The person's or company's name: source of the initials, the colour and the accessible name. */
|
|
745
|
+
name = input('', /* @ts-ignore */
|
|
746
|
+
...(ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []));
|
|
747
|
+
/** Picture URL. When it is empty or fails to load, the initials show. */
|
|
748
|
+
src = input(null, /* @ts-ignore */
|
|
749
|
+
...(ngDevMode ? [{ debugName: "src" }] : /* istanbul ignore next */ []));
|
|
750
|
+
/** Accessible name. Default: `name`. `''` makes the avatar decorative. */
|
|
751
|
+
alt = input(undefined, /* @ts-ignore */
|
|
752
|
+
...(ngDevMode ? [{ debugName: "alt" }] : /* istanbul ignore next */ []));
|
|
753
|
+
/** Hide from assistive technology: use beside the name written out as text. */
|
|
754
|
+
decorative = input(false, { ...(ngDevMode ? { debugName: "decorative" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
755
|
+
/** Initials to show instead of those derived from `name`. */
|
|
756
|
+
initials = input(undefined, /* @ts-ignore */
|
|
757
|
+
...(ngDevMode ? [{ debugName: "initials" }] : /* istanbul ignore next */ []));
|
|
758
|
+
/** `xs`, `sm`, `md` (default), `lg`, `xl`. In an `nxe-avatar-group` the group's size. */
|
|
759
|
+
sizeInput = input(undefined, { ...(ngDevMode ? { debugName: "sizeInput" } : /* istanbul ignore next */ {}), alias: 'size' });
|
|
760
|
+
/** `circle` (default) or `square`. In an `nxe-avatar-group` the group's shape. */
|
|
761
|
+
shapeInput = input(undefined, { ...(ngDevMode ? { debugName: "shapeInput" } : /* istanbul ignore next */ {}), alias: 'shape' });
|
|
762
|
+
/** Presence dot at the bottom corner. Its word joins the accessible name. */
|
|
763
|
+
status = input(null, /* @ts-ignore */
|
|
764
|
+
...(ngDevMode ? [{ debugName: "status" }] : /* istanbul ignore next */ []));
|
|
765
|
+
/** `auto` (default) derives the colour from the name; a status family fixes it. */
|
|
766
|
+
color = input('auto', /* @ts-ignore */
|
|
767
|
+
...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
|
|
768
|
+
size = computed(() => this.sizeInput() ?? this.group?.size() ?? 'md', /* @ts-ignore */
|
|
769
|
+
...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
770
|
+
shape = computed(() => this.shapeInput() ?? this.group?.shape() ?? 'circle', /* @ts-ignore */
|
|
771
|
+
...(ngDevMode ? [{ debugName: "shape" }] : /* istanbul ignore next */ []));
|
|
772
|
+
/** True once the picture failed to load; a new `src` gets a fresh try. */
|
|
773
|
+
failed = linkedSignal({ ...(ngDevMode ? { debugName: "failed" } : /* istanbul ignore next */ {}), source: this.src,
|
|
774
|
+
computation: () => false });
|
|
775
|
+
showImage = computed(() => !!this.src() && !this.failed(), /* @ts-ignore */
|
|
776
|
+
...(ngDevMode ? [{ debugName: "showImage" }] : /* istanbul ignore next */ []));
|
|
777
|
+
text = computed(() => {
|
|
778
|
+
const given = this.initials();
|
|
779
|
+
return given ? given : nxeAvatarInitials(this.name());
|
|
780
|
+
}, /* @ts-ignore */
|
|
781
|
+
...(ngDevMode ? [{ debugName: "text" }] : /* istanbul ignore next */ []));
|
|
782
|
+
/** Beyond the group's `max`: not drawn. */
|
|
783
|
+
overflow = computed(() => !!this.group?.isOverflow(this), /* @ts-ignore */
|
|
784
|
+
...(ngDevMode ? [{ debugName: "overflow" }] : /* istanbul ignore next */ []));
|
|
785
|
+
toneClass = computed(() => {
|
|
786
|
+
const color = this.color();
|
|
787
|
+
if (color !== 'auto') {
|
|
788
|
+
return `nxe-avatar-${color} nxe-avatar-mode-subtle`;
|
|
789
|
+
}
|
|
790
|
+
const tone = NXE_AVATAR_TONES[nxeAvatarToneIndex(this.name())];
|
|
791
|
+
return `nxe-avatar-${tone.family} nxe-avatar-mode-${tone.mode}`;
|
|
792
|
+
}, /* @ts-ignore */
|
|
793
|
+
...(ngDevMode ? [{ debugName: "toneClass" }] : /* istanbul ignore next */ []));
|
|
794
|
+
statusFamily = computed(() => {
|
|
795
|
+
const status = this.status();
|
|
796
|
+
return status ? STATUS_FAMILY[status] : null;
|
|
797
|
+
}, /* @ts-ignore */
|
|
798
|
+
...(ngDevMode ? [{ debugName: "statusFamily" }] : /* istanbul ignore next */ []));
|
|
799
|
+
/** What assistive technology reads, or `''` when the avatar is decorative or unnamed. */
|
|
800
|
+
label = computed(() => {
|
|
801
|
+
if (this.decorative()) {
|
|
802
|
+
return '';
|
|
803
|
+
}
|
|
804
|
+
const alt = this.alt();
|
|
805
|
+
const base = (alt ?? this.name() ?? '').trim();
|
|
806
|
+
if (alt === '' || base === '') {
|
|
807
|
+
return '';
|
|
808
|
+
}
|
|
809
|
+
const status = this.status();
|
|
810
|
+
return status ? `${base}, ${this.messages.t(`avatar.status.${status}`)}` : base;
|
|
811
|
+
}, /* @ts-ignore */
|
|
812
|
+
...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
813
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeAvatarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
814
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: NxeAvatarComponent, isStandalone: true, selector: "nxe-avatar", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, src: { classPropertyName: "src", publicName: "src", isSignal: true, isRequired: false, transformFunction: null }, alt: { classPropertyName: "alt", publicName: "alt", isSignal: true, isRequired: false, transformFunction: null }, decorative: { classPropertyName: "decorative", publicName: "decorative", isSignal: true, isRequired: false, transformFunction: null }, initials: { classPropertyName: "initials", publicName: "initials", isSignal: true, isRequired: false, transformFunction: null }, sizeInput: { classPropertyName: "sizeInput", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, shapeInput: { classPropertyName: "shapeInput", publicName: "shape", isSignal: true, isRequired: false, transformFunction: null }, status: { classPropertyName: "status", publicName: "status", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "toneClass()", "class.nxe-avatar-circle": "shape() === 'circle'", "class.nxe-avatar-square": "shape() === 'square'", "class.nxe-avatar-overflow": "overflow()", "attr.data-size": "size()", "attr.role": "label() ? 'img' : null", "attr.aria-label": "label() || null", "attr.aria-hidden": "label() ? null : 'true'" }, classAttribute: "nxe-avatar" }, exportAs: ["nxeAvatar"], ngImport: i0, template: "@if (text(); as letters) {\r\n <span class=\"nxe-avatar-initials\" aria-hidden=\"true\">{{ letters }}</span>\r\n} @else {\r\n <svg class=\"nxe-avatar-glyph\" viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\">\r\n <circle cx=\"12\" cy=\"8.5\" r=\"4\" />\r\n <path d=\"M4 21c0-4.4 3.6-7 8-7s8 2.6 8 7z\" />\r\n </svg>\r\n}\r\n@if (showImage()) {\r\n <img class=\"nxe-avatar-image\" alt=\"\" [src]=\"src()\" (error)=\"failed.set(true)\" />\r\n}\r\n@if (statusFamily(); as family) {\r\n <span class=\"nxe-avatar-status nxe-avatar-status-{{ family }}\" aria-hidden=\"true\"></span>\r\n}\r\n", styles: ["nxe-avatar.nxe-avatar{--nxe-avatar-size: 40px;position:relative;display:inline-flex;flex:none;align-items:center;justify-content:center;box-sizing:border-box;width:var(--nxe-avatar-size);height:var(--nxe-avatar-size);vertical-align:middle;border:1px solid var(--nxe-status-border);background-color:var(--nxe-status-bg);color:var(--nxe-status-fg);font-family:var(--nxe-ui-font-family);font-size:calc(var(--nxe-avatar-size) * .4);font-weight:var(--nxe-ui-font-weight-semibold);line-height:1;letter-spacing:.02em;-webkit-user-select:none;user-select:none}nxe-avatar.nxe-avatar[data-size=xs]{--nxe-avatar-size: 24px}nxe-avatar.nxe-avatar[data-size=sm]{--nxe-avatar-size: 32px}nxe-avatar.nxe-avatar[data-size=md]{--nxe-avatar-size: 40px}nxe-avatar.nxe-avatar[data-size=lg]{--nxe-avatar-size: 56px}nxe-avatar.nxe-avatar[data-size=xl]{--nxe-avatar-size: 80px}nxe-avatar.nxe-avatar.nxe-avatar-circle{border-radius:50%}nxe-avatar.nxe-avatar.nxe-avatar-square{border-radius:var(--nxe-ui-radius-md)}nxe-avatar.nxe-avatar.nxe-avatar-mode-solid{border-color:var(--nxe-status-fg);background-color:var(--nxe-status-fg);color:var(--nxe-status-bg)}nxe-avatar.nxe-avatar.nxe-avatar-overflow{display:none}nxe-avatar.nxe-avatar .nxe-avatar-initials{text-transform:uppercase;white-space:nowrap}nxe-avatar.nxe-avatar .nxe-avatar-glyph{width:62%;height:62%;fill:currentColor}nxe-avatar.nxe-avatar .nxe-avatar-image{position:absolute;inset:0;width:100%;height:100%;border-radius:inherit;object-fit:cover;background-color:var(--nxe-ui-surface)}nxe-avatar.nxe-avatar .nxe-avatar-status{position:absolute;inset-block-end:-1px;inset-inline-end:-1px;box-sizing:border-box;width:max(8px,var(--nxe-avatar-size) * .28);height:max(8px,var(--nxe-avatar-size) * .28);border:2px solid var(--nxe-ui-surface);border-radius:50%}nxe-avatar.nxe-avatar.nxe-avatar-square .nxe-avatar-status{inset-block-end:-3px;inset-inline-end:-3px}nxe-avatar.nxe-avatar-neutral{--nxe-status-fg: var(--nxe-ui-status-neutral-fg);--nxe-status-bg: var(--nxe-ui-status-neutral-bg);--nxe-status-border: var(--nxe-ui-status-neutral-border)}nxe-avatar.nxe-avatar-info{--nxe-status-fg: var(--nxe-ui-status-info-fg);--nxe-status-bg: var(--nxe-ui-status-info-bg);--nxe-status-border: var(--nxe-ui-status-info-border)}nxe-avatar.nxe-avatar-attention{--nxe-status-fg: var(--nxe-ui-status-attention-fg);--nxe-status-bg: var(--nxe-ui-status-attention-bg);--nxe-status-border: var(--nxe-ui-status-attention-border)}nxe-avatar.nxe-avatar-success{--nxe-status-fg: var(--nxe-ui-status-success-fg);--nxe-status-bg: var(--nxe-ui-status-success-bg);--nxe-status-border: var(--nxe-ui-status-success-border)}nxe-avatar.nxe-avatar-danger{--nxe-status-fg: var(--nxe-ui-status-danger-fg);--nxe-status-bg: var(--nxe-ui-status-danger-bg);--nxe-status-border: var(--nxe-ui-status-danger-border)}nxe-avatar.nxe-avatar-muted{--nxe-status-fg: var(--nxe-ui-status-muted-fg);--nxe-status-bg: var(--nxe-ui-status-muted-bg);--nxe-status-border: var(--nxe-ui-status-muted-border)}nxe-avatar.nxe-avatar-muted{--nxe-status-border-style: dashed}nxe-avatar.nxe-avatar .nxe-avatar-status-success{background-color:var(--nxe-ui-status-success-fg)}nxe-avatar.nxe-avatar .nxe-avatar-status-attention{background-color:var(--nxe-ui-status-attention-fg)}nxe-avatar.nxe-avatar .nxe-avatar-status-danger{background-color:var(--nxe-ui-status-danger-fg)}nxe-avatar.nxe-avatar .nxe-avatar-status-muted{background-color:var(--nxe-ui-status-muted-fg)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
815
|
+
}
|
|
816
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeAvatarComponent, decorators: [{
|
|
817
|
+
type: Component,
|
|
818
|
+
args: [{ selector: 'nxe-avatar', exportAs: 'nxeAvatar', host: {
|
|
819
|
+
class: 'nxe-avatar',
|
|
820
|
+
'[class]': 'toneClass()',
|
|
821
|
+
'[class.nxe-avatar-circle]': "shape() === 'circle'",
|
|
822
|
+
'[class.nxe-avatar-square]': "shape() === 'square'",
|
|
823
|
+
'[class.nxe-avatar-overflow]': 'overflow()',
|
|
824
|
+
'[attr.data-size]': 'size()',
|
|
825
|
+
'[attr.role]': "label() ? 'img' : null",
|
|
826
|
+
'[attr.aria-label]': 'label() || null',
|
|
827
|
+
'[attr.aria-hidden]': "label() ? null : 'true'",
|
|
828
|
+
}, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "@if (text(); as letters) {\r\n <span class=\"nxe-avatar-initials\" aria-hidden=\"true\">{{ letters }}</span>\r\n} @else {\r\n <svg class=\"nxe-avatar-glyph\" viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\">\r\n <circle cx=\"12\" cy=\"8.5\" r=\"4\" />\r\n <path d=\"M4 21c0-4.4 3.6-7 8-7s8 2.6 8 7z\" />\r\n </svg>\r\n}\r\n@if (showImage()) {\r\n <img class=\"nxe-avatar-image\" alt=\"\" [src]=\"src()\" (error)=\"failed.set(true)\" />\r\n}\r\n@if (statusFamily(); as family) {\r\n <span class=\"nxe-avatar-status nxe-avatar-status-{{ family }}\" aria-hidden=\"true\"></span>\r\n}\r\n", styles: ["nxe-avatar.nxe-avatar{--nxe-avatar-size: 40px;position:relative;display:inline-flex;flex:none;align-items:center;justify-content:center;box-sizing:border-box;width:var(--nxe-avatar-size);height:var(--nxe-avatar-size);vertical-align:middle;border:1px solid var(--nxe-status-border);background-color:var(--nxe-status-bg);color:var(--nxe-status-fg);font-family:var(--nxe-ui-font-family);font-size:calc(var(--nxe-avatar-size) * .4);font-weight:var(--nxe-ui-font-weight-semibold);line-height:1;letter-spacing:.02em;-webkit-user-select:none;user-select:none}nxe-avatar.nxe-avatar[data-size=xs]{--nxe-avatar-size: 24px}nxe-avatar.nxe-avatar[data-size=sm]{--nxe-avatar-size: 32px}nxe-avatar.nxe-avatar[data-size=md]{--nxe-avatar-size: 40px}nxe-avatar.nxe-avatar[data-size=lg]{--nxe-avatar-size: 56px}nxe-avatar.nxe-avatar[data-size=xl]{--nxe-avatar-size: 80px}nxe-avatar.nxe-avatar.nxe-avatar-circle{border-radius:50%}nxe-avatar.nxe-avatar.nxe-avatar-square{border-radius:var(--nxe-ui-radius-md)}nxe-avatar.nxe-avatar.nxe-avatar-mode-solid{border-color:var(--nxe-status-fg);background-color:var(--nxe-status-fg);color:var(--nxe-status-bg)}nxe-avatar.nxe-avatar.nxe-avatar-overflow{display:none}nxe-avatar.nxe-avatar .nxe-avatar-initials{text-transform:uppercase;white-space:nowrap}nxe-avatar.nxe-avatar .nxe-avatar-glyph{width:62%;height:62%;fill:currentColor}nxe-avatar.nxe-avatar .nxe-avatar-image{position:absolute;inset:0;width:100%;height:100%;border-radius:inherit;object-fit:cover;background-color:var(--nxe-ui-surface)}nxe-avatar.nxe-avatar .nxe-avatar-status{position:absolute;inset-block-end:-1px;inset-inline-end:-1px;box-sizing:border-box;width:max(8px,var(--nxe-avatar-size) * .28);height:max(8px,var(--nxe-avatar-size) * .28);border:2px solid var(--nxe-ui-surface);border-radius:50%}nxe-avatar.nxe-avatar.nxe-avatar-square .nxe-avatar-status{inset-block-end:-3px;inset-inline-end:-3px}nxe-avatar.nxe-avatar-neutral{--nxe-status-fg: var(--nxe-ui-status-neutral-fg);--nxe-status-bg: var(--nxe-ui-status-neutral-bg);--nxe-status-border: var(--nxe-ui-status-neutral-border)}nxe-avatar.nxe-avatar-info{--nxe-status-fg: var(--nxe-ui-status-info-fg);--nxe-status-bg: var(--nxe-ui-status-info-bg);--nxe-status-border: var(--nxe-ui-status-info-border)}nxe-avatar.nxe-avatar-attention{--nxe-status-fg: var(--nxe-ui-status-attention-fg);--nxe-status-bg: var(--nxe-ui-status-attention-bg);--nxe-status-border: var(--nxe-ui-status-attention-border)}nxe-avatar.nxe-avatar-success{--nxe-status-fg: var(--nxe-ui-status-success-fg);--nxe-status-bg: var(--nxe-ui-status-success-bg);--nxe-status-border: var(--nxe-ui-status-success-border)}nxe-avatar.nxe-avatar-danger{--nxe-status-fg: var(--nxe-ui-status-danger-fg);--nxe-status-bg: var(--nxe-ui-status-danger-bg);--nxe-status-border: var(--nxe-ui-status-danger-border)}nxe-avatar.nxe-avatar-muted{--nxe-status-fg: var(--nxe-ui-status-muted-fg);--nxe-status-bg: var(--nxe-ui-status-muted-bg);--nxe-status-border: var(--nxe-ui-status-muted-border)}nxe-avatar.nxe-avatar-muted{--nxe-status-border-style: dashed}nxe-avatar.nxe-avatar .nxe-avatar-status-success{background-color:var(--nxe-ui-status-success-fg)}nxe-avatar.nxe-avatar .nxe-avatar-status-attention{background-color:var(--nxe-ui-status-attention-fg)}nxe-avatar.nxe-avatar .nxe-avatar-status-danger{background-color:var(--nxe-ui-status-danger-fg)}nxe-avatar.nxe-avatar .nxe-avatar-status-muted{background-color:var(--nxe-ui-status-muted-fg)}\n"] }]
|
|
829
|
+
}], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], src: [{ type: i0.Input, args: [{ isSignal: true, alias: "src", required: false }] }], alt: [{ type: i0.Input, args: [{ isSignal: true, alias: "alt", required: false }] }], decorative: [{ type: i0.Input, args: [{ isSignal: true, alias: "decorative", required: false }] }], initials: [{ type: i0.Input, args: [{ isSignal: true, alias: "initials", required: false }] }], sizeInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], shapeInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "shape", required: false }] }], status: [{ type: i0.Input, args: [{ isSignal: true, alias: "status", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }] } });
|
|
830
|
+
|
|
831
|
+
/**
|
|
832
|
+
* `nxe-avatar-group` overlaps a row of avatars and, past `max`, folds the rest into a "+N" chip.
|
|
833
|
+
*
|
|
834
|
+
* ```html
|
|
835
|
+
* <nxe-avatar-group [max]="3" size="sm" aria-label="Penyetuju">
|
|
836
|
+
* @for (person of approvers(); track person.id) {
|
|
837
|
+
* <nxe-avatar [name]="person.name" [src]="person.photo" />
|
|
838
|
+
* }
|
|
839
|
+
* </nxe-avatar-group>
|
|
840
|
+
* ```
|
|
841
|
+
*
|
|
842
|
+
* `max` is how many avatars are drawn (default 5; `null` draws all), so `max=3` over five avatars
|
|
843
|
+
* shows three and "+2". `size` and `shape` apply to every avatar that does not set its own. The
|
|
844
|
+
* avatars past `max` are hidden from everyone, not only from the eye. The group is a labelled
|
|
845
|
+
* `role="group"` when it has an `aria-label`, and the chip reads "2 more".
|
|
846
|
+
*/
|
|
847
|
+
class NxeAvatarGroupComponent {
|
|
848
|
+
messages = inject(NxeMessageService);
|
|
849
|
+
/** Avatars drawn before the "+N" chip. Default 5; `null` draws every avatar. */
|
|
850
|
+
max = input(5, { ...(ngDevMode ? { debugName: "max" } : /* istanbul ignore next */ {}), transform: (value) => (value === null || value === undefined || value === '' ? null : numberAttribute(value, 5)) });
|
|
851
|
+
/** Size of every avatar in the group that has none of its own. Default `md`. */
|
|
852
|
+
size = input('md', /* @ts-ignore */
|
|
853
|
+
...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
854
|
+
/** Shape of every avatar that has none of its own. Default `circle`. */
|
|
855
|
+
shape = input('circle', /* @ts-ignore */
|
|
856
|
+
...(ngDevMode ? [{ debugName: "shape" }] : /* istanbul ignore next */ []));
|
|
857
|
+
/** Accessible name of the group, such as "Penyetuju". */
|
|
858
|
+
ariaLabel = input(undefined, { ...(ngDevMode ? { debugName: "ariaLabel" } : /* istanbul ignore next */ {}), alias: 'aria-label' });
|
|
859
|
+
avatars = contentChildren(NxeAvatarComponent, /* @ts-ignore */
|
|
860
|
+
...(ngDevMode ? [{ debugName: "avatars" }] : /* istanbul ignore next */ []));
|
|
861
|
+
/** How many avatars are folded into the chip. */
|
|
862
|
+
hidden = computed(() => {
|
|
863
|
+
const max = this.max();
|
|
864
|
+
return max === null ? 0 : Math.max(0, this.avatars().length - Math.max(0, max));
|
|
865
|
+
}, /* @ts-ignore */
|
|
866
|
+
...(ngDevMode ? [{ debugName: "hidden" }] : /* istanbul ignore next */ []));
|
|
867
|
+
moreLabel = computed(() => this.messages.t('avatar.more', this.hidden()), /* @ts-ignore */
|
|
868
|
+
...(ngDevMode ? [{ debugName: "moreLabel" }] : /* istanbul ignore next */ []));
|
|
869
|
+
isOverflow(avatar) {
|
|
870
|
+
const max = this.max();
|
|
871
|
+
return max !== null && this.avatars().indexOf(avatar) >= Math.max(0, max);
|
|
872
|
+
}
|
|
873
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeAvatarGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
874
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: NxeAvatarGroupComponent, isStandalone: true, selector: "nxe-avatar-group", inputs: { max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, shape: { classPropertyName: "shape", publicName: "shape", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.role": "ariaLabel() ? \"group\" : null", "attr.aria-label": "ariaLabel() || null", "attr.data-size": "size()" }, classAttribute: "nxe-avatar-group" }, providers: [{ provide: NXE_AVATAR_GROUP, useExisting: forwardRef(() => NxeAvatarGroupComponent) }], queries: [{ propertyName: "avatars", predicate: NxeAvatarComponent, isSignal: true }], ngImport: i0, template: "<ng-content />\r\n@if (hidden() > 0) {\r\n <span class=\"nxe-avatar-more\" role=\"img\" [attr.aria-label]=\"moreLabel()\">+{{ hidden() }}</span>\r\n}\r\n", styles: ["nxe-avatar-group.nxe-avatar-group{--nxe-avatar-size: 40px;--nxe-avatar-overlap: calc(var(--nxe-avatar-size) * -.25);display:inline-flex;align-items:center;vertical-align:middle}nxe-avatar-group.nxe-avatar-group[data-size=xs]{--nxe-avatar-size: 24px}nxe-avatar-group.nxe-avatar-group[data-size=sm]{--nxe-avatar-size: 32px}nxe-avatar-group.nxe-avatar-group[data-size=md]{--nxe-avatar-size: 40px}nxe-avatar-group.nxe-avatar-group[data-size=lg]{--nxe-avatar-size: 56px}nxe-avatar-group.nxe-avatar-group[data-size=xl]{--nxe-avatar-size: 80px}nxe-avatar-group.nxe-avatar-group>nxe-avatar.nxe-avatar,nxe-avatar-group.nxe-avatar-group>.nxe-avatar-more{box-shadow:0 0 0 2px var(--nxe-ui-surface)}nxe-avatar-group.nxe-avatar-group>nxe-avatar.nxe-avatar:not(:first-child),nxe-avatar-group.nxe-avatar-group>.nxe-avatar-more{margin-inline-start:var(--nxe-avatar-overlap)}nxe-avatar-group.nxe-avatar-group>.nxe-avatar-more{display:inline-flex;flex:none;align-items:center;justify-content:center;box-sizing:border-box;width:var(--nxe-avatar-size);height:var(--nxe-avatar-size);border:1px solid var(--nxe-ui-status-neutral-border);border-radius:50%;background-color:var(--nxe-ui-status-neutral-bg);color:var(--nxe-ui-status-neutral-fg);font-family:var(--nxe-ui-font-family);font-size:calc(var(--nxe-avatar-size) * .34);font-weight:var(--nxe-ui-font-weight-semibold);line-height:1;-webkit-user-select:none;user-select:none}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
875
|
+
}
|
|
876
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeAvatarGroupComponent, decorators: [{
|
|
877
|
+
type: Component,
|
|
878
|
+
args: [{ selector: 'nxe-avatar-group', providers: [{ provide: NXE_AVATAR_GROUP, useExisting: forwardRef(() => NxeAvatarGroupComponent) }], host: {
|
|
879
|
+
class: 'nxe-avatar-group',
|
|
880
|
+
'[attr.role]': 'ariaLabel() ? "group" : null',
|
|
881
|
+
'[attr.aria-label]': 'ariaLabel() || null',
|
|
882
|
+
'[attr.data-size]': 'size()',
|
|
883
|
+
}, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<ng-content />\r\n@if (hidden() > 0) {\r\n <span class=\"nxe-avatar-more\" role=\"img\" [attr.aria-label]=\"moreLabel()\">+{{ hidden() }}</span>\r\n}\r\n", styles: ["nxe-avatar-group.nxe-avatar-group{--nxe-avatar-size: 40px;--nxe-avatar-overlap: calc(var(--nxe-avatar-size) * -.25);display:inline-flex;align-items:center;vertical-align:middle}nxe-avatar-group.nxe-avatar-group[data-size=xs]{--nxe-avatar-size: 24px}nxe-avatar-group.nxe-avatar-group[data-size=sm]{--nxe-avatar-size: 32px}nxe-avatar-group.nxe-avatar-group[data-size=md]{--nxe-avatar-size: 40px}nxe-avatar-group.nxe-avatar-group[data-size=lg]{--nxe-avatar-size: 56px}nxe-avatar-group.nxe-avatar-group[data-size=xl]{--nxe-avatar-size: 80px}nxe-avatar-group.nxe-avatar-group>nxe-avatar.nxe-avatar,nxe-avatar-group.nxe-avatar-group>.nxe-avatar-more{box-shadow:0 0 0 2px var(--nxe-ui-surface)}nxe-avatar-group.nxe-avatar-group>nxe-avatar.nxe-avatar:not(:first-child),nxe-avatar-group.nxe-avatar-group>.nxe-avatar-more{margin-inline-start:var(--nxe-avatar-overlap)}nxe-avatar-group.nxe-avatar-group>.nxe-avatar-more{display:inline-flex;flex:none;align-items:center;justify-content:center;box-sizing:border-box;width:var(--nxe-avatar-size);height:var(--nxe-avatar-size);border:1px solid var(--nxe-ui-status-neutral-border);border-radius:50%;background-color:var(--nxe-ui-status-neutral-bg);color:var(--nxe-ui-status-neutral-fg);font-family:var(--nxe-ui-font-family);font-size:calc(var(--nxe-avatar-size) * .34);font-weight:var(--nxe-ui-font-weight-semibold);line-height:1;-webkit-user-select:none;user-select:none}\n"] }]
|
|
884
|
+
}], propDecorators: { max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], shape: [{ type: i0.Input, args: [{ isSignal: true, alias: "shape", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-label", required: false }] }], avatars: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => NxeAvatarComponent), { isSignal: true }] }] } });
|
|
885
|
+
|
|
886
|
+
/** @internal */
|
|
887
|
+
const NXE_ACCORDION = new InjectionToken('NXE_ACCORDION');
|
|
888
|
+
|
|
889
|
+
/**
|
|
890
|
+
* Marks the `<ng-template>` that holds an accordion item's lazy content. The template is stamped out
|
|
891
|
+
* the first time the item is opened, not before, so a heavy panel (a grid, a chart, a request in its
|
|
892
|
+
* constructor) costs nothing until someone asks for it:
|
|
893
|
+
*
|
|
894
|
+
* ```html
|
|
895
|
+
* <nxe-accordion-item heading="Riwayat">
|
|
896
|
+
* <ng-template nxeAccordionContent><app-history /></ng-template>
|
|
897
|
+
* </nxe-accordion-item>
|
|
898
|
+
* ```
|
|
899
|
+
*
|
|
900
|
+
* Whether it is destroyed again when the item closes is `keepAlive` on the item or the accordion.
|
|
901
|
+
*/
|
|
902
|
+
class NxeAccordionContentDirective {
|
|
903
|
+
/** The template to render. */
|
|
904
|
+
template = inject(TemplateRef);
|
|
905
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeAccordionContentDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
906
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.5", type: NxeAccordionContentDirective, isStandalone: true, selector: "ng-template[nxeAccordionContent]", ngImport: i0 });
|
|
907
|
+
}
|
|
908
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeAccordionContentDirective, decorators: [{
|
|
909
|
+
type: Directive,
|
|
910
|
+
args: [{
|
|
911
|
+
selector: 'ng-template[nxeAccordionContent]',
|
|
912
|
+
}]
|
|
913
|
+
}] });
|
|
914
|
+
|
|
915
|
+
let itemSequence = 0;
|
|
916
|
+
/**
|
|
917
|
+
* One section of an {@link NxeAccordionComponent}: a heading that is a button, and the panel it
|
|
918
|
+
* opens.
|
|
919
|
+
*
|
|
920
|
+
* ```html
|
|
921
|
+
* <nxe-accordion-item heading="Pengiriman" [(expanded)]="shippingOpen">
|
|
922
|
+
* Alamat dan jadwal pengiriman.
|
|
923
|
+
* </nxe-accordion-item>
|
|
924
|
+
*
|
|
925
|
+
* <nxe-accordion-item heading="Riwayat" [disabled]="!hasHistory()">
|
|
926
|
+
* <ng-template nxeAccordionContent><app-history /></ng-template>
|
|
927
|
+
* </nxe-accordion-item>
|
|
928
|
+
*
|
|
929
|
+
* <nxe-accordion-item>
|
|
930
|
+
* <span nxeAccordionHeading>Lampiran <nxe-badge [value]="3" /></span>
|
|
931
|
+
* ...
|
|
932
|
+
* </nxe-accordion-item>
|
|
933
|
+
* ```
|
|
934
|
+
*
|
|
935
|
+
* The heading is `role="heading"` at the accordion's `headingLevel`, and holds one `<button
|
|
936
|
+
* aria-expanded aria-controls>`. The panel is a `role="region"` labelled by that button, hidden from
|
|
937
|
+
* keyboard and screen reader while closed.
|
|
938
|
+
*/
|
|
939
|
+
class NxeAccordionItemComponent {
|
|
940
|
+
accordion = inject(NXE_ACCORDION, { optional: true });
|
|
941
|
+
/** The heading text. For markup, project an element with `nxeAccordionHeading` instead. */
|
|
942
|
+
heading = input('', /* @ts-ignore */
|
|
943
|
+
...(ngDevMode ? [{ debugName: "heading" }] : /* istanbul ignore next */ []));
|
|
944
|
+
/** Whether the panel is open. Two-way: `[(expanded)]`. */
|
|
945
|
+
expanded = model(false, /* @ts-ignore */
|
|
946
|
+
...(ngDevMode ? [{ debugName: "expanded" }] : /* istanbul ignore next */ []));
|
|
947
|
+
/** The heading is not a control: not focusable, skipped by the arrow keys. A disabled open item stays open. */
|
|
948
|
+
disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
949
|
+
/** Heading level of this item, 1 to 6. Default: the accordion's. */
|
|
950
|
+
headingLevel = input(undefined, { ...(ngDevMode ? { debugName: "headingLevel" } : /* istanbul ignore next */ {}), transform: (value) => {
|
|
951
|
+
const level = numberAttribute(value, 0);
|
|
952
|
+
return level >= 1 && level <= 6 ? level : undefined;
|
|
953
|
+
} });
|
|
954
|
+
/** Keep lazy content after the item closes. Default: the accordion's (`true`). Plain content is always kept. */
|
|
955
|
+
keepAlive = input(undefined, { ...(ngDevMode ? { debugName: "keepAlive" } : /* istanbul ignore next */ {}), transform: (value) => (value === undefined || value === null || value === '' ? undefined : booleanAttribute(value)) });
|
|
956
|
+
lazy = contentChild(NxeAccordionContentDirective, /* @ts-ignore */
|
|
957
|
+
...(ngDevMode ? [{ debugName: "lazy" }] : /* istanbul ignore next */ []));
|
|
958
|
+
trigger = viewChild.required('trigger', /* @ts-ignore */
|
|
959
|
+
...(ngDevMode ? [{ debugName: "trigger" }] : /* istanbul ignore next */ []));
|
|
960
|
+
uid = `nxe-accordion-item-${++itemSequence}`;
|
|
961
|
+
/** @internal */
|
|
962
|
+
triggerId = `${this.uid}-header`;
|
|
963
|
+
/** @internal */
|
|
964
|
+
panelId = `${this.uid}-panel`;
|
|
965
|
+
level = computed(() => this.headingLevel() ?? this.accordion?.headingLevel() ?? 3, /* @ts-ignore */
|
|
966
|
+
...(ngDevMode ? [{ debugName: "level" }] : /* istanbul ignore next */ []));
|
|
967
|
+
region = computed(() => this.accordion?.regions() ?? true, /* @ts-ignore */
|
|
968
|
+
...(ngDevMode ? [{ debugName: "region" }] : /* istanbul ignore next */ []));
|
|
969
|
+
lazyTemplate = computed(() => this.lazy()?.template ?? null, /* @ts-ignore */
|
|
970
|
+
...(ngDevMode ? [{ debugName: "lazyTemplate" }] : /* istanbul ignore next */ []));
|
|
971
|
+
/** True once the item has been opened, for lazy content that stays after closing. */
|
|
972
|
+
visited = linkedSignal({ ...(ngDevMode ? { debugName: "visited" } : /* istanbul ignore next */ {}), source: this.expanded,
|
|
973
|
+
computation: (open, previous) => open || !!previous?.value });
|
|
974
|
+
/** Whether the lazy template exists right now: open, or opened before and kept. */
|
|
975
|
+
renderLazy = computed(() => {
|
|
976
|
+
// Read first, not short-circuited: a linkedSignal only starts remembering once something reads it.
|
|
977
|
+
const visited = this.visited();
|
|
978
|
+
return this.expanded() || (visited && (this.keepAlive() ?? this.accordion?.keepAlive() ?? true));
|
|
979
|
+
}, /* @ts-ignore */
|
|
980
|
+
...(ngDevMode ? [{ debugName: "renderLazy" }] : /* istanbul ignore next */ []));
|
|
981
|
+
constructor() {
|
|
982
|
+
// However an item comes to be open (a click, a binding, `expand()`), a single-open accordion hears about it.
|
|
983
|
+
toObservable(this.expanded)
|
|
984
|
+
.pipe(takeUntilDestroyed())
|
|
985
|
+
.subscribe((open) => open && this.accordion?.itemExpanded(this));
|
|
986
|
+
}
|
|
987
|
+
/** Opens or closes the panel. A disabled item ignores it. */
|
|
988
|
+
toggle() {
|
|
989
|
+
if (!this.disabled()) {
|
|
990
|
+
this.expanded.update((open) => !open);
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
expand() {
|
|
994
|
+
this.expanded.set(true);
|
|
995
|
+
}
|
|
996
|
+
collapse() {
|
|
997
|
+
this.expanded.set(false);
|
|
998
|
+
}
|
|
999
|
+
/** Moves focus to the heading button. */
|
|
1000
|
+
focus() {
|
|
1001
|
+
this.trigger().nativeElement.focus();
|
|
1002
|
+
}
|
|
1003
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeAccordionItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1004
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: NxeAccordionItemComponent, isStandalone: true, selector: "nxe-accordion-item", inputs: { heading: { classPropertyName: "heading", publicName: "heading", isSignal: true, isRequired: false, transformFunction: null }, expanded: { classPropertyName: "expanded", publicName: "expanded", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, headingLevel: { classPropertyName: "headingLevel", publicName: "headingLevel", isSignal: true, isRequired: false, transformFunction: null }, keepAlive: { classPropertyName: "keepAlive", publicName: "keepAlive", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { expanded: "expandedChange" }, host: { properties: { "class.nxe-accordion-item-open": "expanded()", "class.nxe-accordion-item-disabled": "disabled()" }, classAttribute: "nxe-accordion-item" }, queries: [{ propertyName: "lazy", first: true, predicate: NxeAccordionContentDirective, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "trigger", first: true, predicate: ["trigger"], descendants: true, isSignal: true }], exportAs: ["nxeAccordionItem"], ngImport: i0, template: "<div class=\"nxe-accordion-heading\" role=\"heading\" [attr.aria-level]=\"level()\">\r\n <button\r\n #trigger\r\n type=\"button\"\r\n class=\"nxe-accordion-trigger\"\r\n [id]=\"triggerId\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-expanded]=\"expanded()\"\r\n [attr.aria-controls]=\"panelId\"\r\n (click)=\"toggle()\">\r\n <span class=\"nxe-accordion-title\">{{ heading() }}<ng-content select=\"[nxeAccordionHeading]\" /></span>\r\n <svg class=\"nxe-accordion-chevron\" viewBox=\"0 0 16 16\" aria-hidden=\"true\" focusable=\"false\">\r\n <path d=\"M3.5 6l4.5 4.5L12.5 6\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\r\n </svg>\r\n </button>\r\n</div>\r\n<div\r\n class=\"nxe-accordion-panel\"\r\n [id]=\"panelId\"\r\n [attr.role]=\"region() ? 'region' : null\"\r\n [attr.aria-labelledby]=\"region() ? triggerId : null\">\r\n <div class=\"nxe-accordion-panel-clip\">\r\n <div class=\"nxe-accordion-body\">\r\n @if (lazyTemplate(); as template) {\r\n @if (renderLazy()) {\r\n <ng-container *ngTemplateOutlet=\"template\" />\r\n }\r\n }\r\n <ng-content />\r\n </div>\r\n </div>\r\n</div>\r\n", styles: ["nxe-accordion-item.nxe-accordion-item{display:block;border-bottom:1px solid var(--nxe-ui-border);color:var(--nxe-ui-text)}nxe-accordion-item.nxe-accordion-item:last-child{border-bottom:0}nxe-accordion-item.nxe-accordion-item .nxe-accordion-heading{margin:0;font-size:inherit;font-weight:inherit}nxe-accordion-item.nxe-accordion-item .nxe-accordion-trigger{display:flex;align-items:center;justify-content:space-between;gap:var(--nxe-ui-space-3);box-sizing:border-box;width:100%;margin:0;padding:var(--nxe-ui-space-3) var(--nxe-ui-space-4);border:0;border-radius:0;background:transparent;color:inherit;font:inherit;font-size:var(--nxe-ui-font-size-md);font-weight:var(--nxe-ui-font-weight-semibold);line-height:var(--nxe-ui-line-height-md);text-align:start;cursor:pointer}nxe-accordion-item.nxe-accordion-item .nxe-accordion-trigger:hover:not(:disabled){background:var(--nxe-ui-surface-alt)}nxe-accordion-item.nxe-accordion-item .nxe-accordion-trigger:focus-visible{outline:none;box-shadow:inset var(--nxe-ui-focus-ring)}nxe-accordion-item.nxe-accordion-item .nxe-accordion-trigger:disabled{cursor:not-allowed;opacity:.5}nxe-accordion-item.nxe-accordion-item .nxe-accordion-title{display:inline-flex;flex:1 1 auto;flex-wrap:wrap;align-items:center;gap:var(--nxe-ui-space-2);min-width:0}nxe-accordion-item.nxe-accordion-item .nxe-accordion-chevron{flex:none;width:16px;height:16px;color:var(--nxe-ui-neutral-600)}nxe-accordion-item.nxe-accordion-item .nxe-accordion-panel{display:grid;grid-template-rows:0fr;visibility:hidden}nxe-accordion-item.nxe-accordion-item .nxe-accordion-panel-clip{min-height:0;overflow:hidden}nxe-accordion-item.nxe-accordion-item .nxe-accordion-body{padding:var(--nxe-ui-space-1) var(--nxe-ui-space-4) var(--nxe-ui-space-4);font-size:var(--nxe-ui-font-size-md);line-height:var(--nxe-ui-line-height-md)}nxe-accordion-item.nxe-accordion-item.nxe-accordion-item-open .nxe-accordion-panel{grid-template-rows:1fr;visibility:visible}nxe-accordion-item.nxe-accordion-item.nxe-accordion-item-open .nxe-accordion-chevron{transform:rotate(180deg)}@media(prefers-reduced-motion:no-preference){nxe-accordion-item.nxe-accordion-item .nxe-accordion-panel{transition:grid-template-rows .2s ease,visibility 0s linear .2s}nxe-accordion-item.nxe-accordion-item.nxe-accordion-item-open .nxe-accordion-panel{transition:grid-template-rows .2s ease,visibility 0s linear 0s}nxe-accordion-item.nxe-accordion-item .nxe-accordion-chevron{transition:transform .2s ease}}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
1005
|
+
}
|
|
1006
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeAccordionItemComponent, decorators: [{
|
|
1007
|
+
type: Component,
|
|
1008
|
+
args: [{ selector: 'nxe-accordion-item', exportAs: 'nxeAccordionItem', imports: [NgTemplateOutlet], host: {
|
|
1009
|
+
class: 'nxe-accordion-item',
|
|
1010
|
+
'[class.nxe-accordion-item-open]': 'expanded()',
|
|
1011
|
+
'[class.nxe-accordion-item-disabled]': 'disabled()',
|
|
1012
|
+
}, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<div class=\"nxe-accordion-heading\" role=\"heading\" [attr.aria-level]=\"level()\">\r\n <button\r\n #trigger\r\n type=\"button\"\r\n class=\"nxe-accordion-trigger\"\r\n [id]=\"triggerId\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-expanded]=\"expanded()\"\r\n [attr.aria-controls]=\"panelId\"\r\n (click)=\"toggle()\">\r\n <span class=\"nxe-accordion-title\">{{ heading() }}<ng-content select=\"[nxeAccordionHeading]\" /></span>\r\n <svg class=\"nxe-accordion-chevron\" viewBox=\"0 0 16 16\" aria-hidden=\"true\" focusable=\"false\">\r\n <path d=\"M3.5 6l4.5 4.5L12.5 6\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\r\n </svg>\r\n </button>\r\n</div>\r\n<div\r\n class=\"nxe-accordion-panel\"\r\n [id]=\"panelId\"\r\n [attr.role]=\"region() ? 'region' : null\"\r\n [attr.aria-labelledby]=\"region() ? triggerId : null\">\r\n <div class=\"nxe-accordion-panel-clip\">\r\n <div class=\"nxe-accordion-body\">\r\n @if (lazyTemplate(); as template) {\r\n @if (renderLazy()) {\r\n <ng-container *ngTemplateOutlet=\"template\" />\r\n }\r\n }\r\n <ng-content />\r\n </div>\r\n </div>\r\n</div>\r\n", styles: ["nxe-accordion-item.nxe-accordion-item{display:block;border-bottom:1px solid var(--nxe-ui-border);color:var(--nxe-ui-text)}nxe-accordion-item.nxe-accordion-item:last-child{border-bottom:0}nxe-accordion-item.nxe-accordion-item .nxe-accordion-heading{margin:0;font-size:inherit;font-weight:inherit}nxe-accordion-item.nxe-accordion-item .nxe-accordion-trigger{display:flex;align-items:center;justify-content:space-between;gap:var(--nxe-ui-space-3);box-sizing:border-box;width:100%;margin:0;padding:var(--nxe-ui-space-3) var(--nxe-ui-space-4);border:0;border-radius:0;background:transparent;color:inherit;font:inherit;font-size:var(--nxe-ui-font-size-md);font-weight:var(--nxe-ui-font-weight-semibold);line-height:var(--nxe-ui-line-height-md);text-align:start;cursor:pointer}nxe-accordion-item.nxe-accordion-item .nxe-accordion-trigger:hover:not(:disabled){background:var(--nxe-ui-surface-alt)}nxe-accordion-item.nxe-accordion-item .nxe-accordion-trigger:focus-visible{outline:none;box-shadow:inset var(--nxe-ui-focus-ring)}nxe-accordion-item.nxe-accordion-item .nxe-accordion-trigger:disabled{cursor:not-allowed;opacity:.5}nxe-accordion-item.nxe-accordion-item .nxe-accordion-title{display:inline-flex;flex:1 1 auto;flex-wrap:wrap;align-items:center;gap:var(--nxe-ui-space-2);min-width:0}nxe-accordion-item.nxe-accordion-item .nxe-accordion-chevron{flex:none;width:16px;height:16px;color:var(--nxe-ui-neutral-600)}nxe-accordion-item.nxe-accordion-item .nxe-accordion-panel{display:grid;grid-template-rows:0fr;visibility:hidden}nxe-accordion-item.nxe-accordion-item .nxe-accordion-panel-clip{min-height:0;overflow:hidden}nxe-accordion-item.nxe-accordion-item .nxe-accordion-body{padding:var(--nxe-ui-space-1) var(--nxe-ui-space-4) var(--nxe-ui-space-4);font-size:var(--nxe-ui-font-size-md);line-height:var(--nxe-ui-line-height-md)}nxe-accordion-item.nxe-accordion-item.nxe-accordion-item-open .nxe-accordion-panel{grid-template-rows:1fr;visibility:visible}nxe-accordion-item.nxe-accordion-item.nxe-accordion-item-open .nxe-accordion-chevron{transform:rotate(180deg)}@media(prefers-reduced-motion:no-preference){nxe-accordion-item.nxe-accordion-item .nxe-accordion-panel{transition:grid-template-rows .2s ease,visibility 0s linear .2s}nxe-accordion-item.nxe-accordion-item.nxe-accordion-item-open .nxe-accordion-panel{transition:grid-template-rows .2s ease,visibility 0s linear 0s}nxe-accordion-item.nxe-accordion-item .nxe-accordion-chevron{transition:transform .2s ease}}\n"] }]
|
|
1013
|
+
}], ctorParameters: () => [], propDecorators: { heading: [{ type: i0.Input, args: [{ isSignal: true, alias: "heading", required: false }] }], expanded: [{ type: i0.Input, args: [{ isSignal: true, alias: "expanded", required: false }] }, { type: i0.Output, args: ["expandedChange"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], headingLevel: [{ type: i0.Input, args: [{ isSignal: true, alias: "headingLevel", required: false }] }], keepAlive: [{ type: i0.Input, args: [{ isSignal: true, alias: "keepAlive", required: false }] }], lazy: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NxeAccordionContentDirective), { isSignal: true }] }], trigger: [{ type: i0.ViewChild, args: ['trigger', { isSignal: true }] }] } });
|
|
1014
|
+
|
|
1015
|
+
/** Past this many items that can be open together, panels stop being landmark regions (WAI-ARIA APG). */
|
|
1016
|
+
const MAX_REGIONS = 6;
|
|
1017
|
+
/**
|
|
1018
|
+
* `nxe-accordion` stacks sections that open and close: grouped settings, an FAQ, a long form in parts.
|
|
1019
|
+
*
|
|
1020
|
+
* ```html
|
|
1021
|
+
* <nxe-accordion [multi]="true" [headingLevel]="2">
|
|
1022
|
+
* <nxe-accordion-item heading="Umum" [expanded]="true">...</nxe-accordion-item>
|
|
1023
|
+
* <nxe-accordion-item heading="Pengiriman">...</nxe-accordion-item>
|
|
1024
|
+
* <nxe-accordion-item heading="Riwayat" [disabled]="!hasHistory()">
|
|
1025
|
+
* <ng-template nxeAccordionContent><app-history /></ng-template>
|
|
1026
|
+
* </nxe-accordion-item>
|
|
1027
|
+
* </nxe-accordion>
|
|
1028
|
+
* ```
|
|
1029
|
+
*
|
|
1030
|
+
* **One open or many.** By default opening an item closes the others. `multi` lets any number stay open.
|
|
1031
|
+
* Each item's `expanded` is a two-way model, so a binding, a click and `expand()` all agree. If bindings
|
|
1032
|
+
* open several items of a single-open accordion at once, the first in order stays open.
|
|
1033
|
+
*
|
|
1034
|
+
* **Lazy content.** Content in an `<ng-template nxeAccordionContent>` is created the first time its item
|
|
1035
|
+
* opens; `keepAlive` (default `true`) keeps it after closing, `false` destroys it.
|
|
1036
|
+
*
|
|
1037
|
+
* **Keyboard and screen readers** follow the WAI-ARIA accordion pattern. Every heading is a `button` inside
|
|
1038
|
+
* an element with `role="heading"` and `aria-level` (`headingLevel`, default 3: pick the level under the
|
|
1039
|
+
* page's own heading). The button has `aria-expanded` and `aria-controls`; the panel is a `role="region"`
|
|
1040
|
+
* labelled by the button. Enter and Space toggle; ArrowDown and ArrowUp move to the next and previous
|
|
1041
|
+
* heading (wrapping), Home and End to the first and last; disabled headings are skipped. In a `multi`
|
|
1042
|
+
* accordion of more than six items the panels drop `role="region"`, as the pattern advises, so as not to
|
|
1043
|
+
* flood the landmark list.
|
|
1044
|
+
*
|
|
1045
|
+
* The panel animates its height over 0.2s, and not at all under `prefers-reduced-motion: reduce`.
|
|
1046
|
+
*/
|
|
1047
|
+
class NxeAccordionComponent {
|
|
1048
|
+
/** Let several items stay open. Default: opening one closes the others. */
|
|
1049
|
+
multi = input(false, { ...(ngDevMode ? { debugName: "multi" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1050
|
+
/** `aria-level` of the headings, 1 to 6. Default 3. An item can set its own. */
|
|
1051
|
+
headingLevel = input(3, { ...(ngDevMode ? { debugName: "headingLevel" } : /* istanbul ignore next */ {}), transform: (value) => {
|
|
1052
|
+
const level = numberAttribute(value, 3);
|
|
1053
|
+
return (level >= 1 && level <= 6 ? level : 3);
|
|
1054
|
+
} });
|
|
1055
|
+
/** Keep lazy content after its item closes. Default `true`; an item can override. */
|
|
1056
|
+
keepAlive = input(true, { ...(ngDevMode ? { debugName: "keepAlive" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1057
|
+
items = contentChildren(NxeAccordionItemComponent, /* @ts-ignore */
|
|
1058
|
+
...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
|
|
1059
|
+
regions = computed(() => !this.multi() || this.items().length <= MAX_REGIONS, /* @ts-ignore */
|
|
1060
|
+
...(ngDevMode ? [{ debugName: "regions" }] : /* istanbul ignore next */ []));
|
|
1061
|
+
constructor() {
|
|
1062
|
+
// Going from many open to one open leaves the first.
|
|
1063
|
+
toObservable(this.multi)
|
|
1064
|
+
.pipe(takeUntilDestroyed())
|
|
1065
|
+
.subscribe((multi) => {
|
|
1066
|
+
if (!multi) {
|
|
1067
|
+
const [keep] = this.items().filter((item) => item.expanded());
|
|
1068
|
+
this.collapseOthers(keep);
|
|
1069
|
+
}
|
|
1070
|
+
});
|
|
1071
|
+
}
|
|
1072
|
+
/** @internal */
|
|
1073
|
+
itemExpanded(item) {
|
|
1074
|
+
if (!this.multi()) {
|
|
1075
|
+
this.collapseOthers(item);
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
/** Opens every enabled item. Only a `multi` accordion can have them all open. */
|
|
1079
|
+
expandAll() {
|
|
1080
|
+
if (this.multi()) {
|
|
1081
|
+
this.items().filter((item) => !item.disabled()).forEach((item) => item.expand());
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
collapseAll() {
|
|
1085
|
+
this.items().forEach((item) => item.collapse());
|
|
1086
|
+
}
|
|
1087
|
+
collapseOthers(keep) {
|
|
1088
|
+
for (const item of this.items()) {
|
|
1089
|
+
if (item !== keep && item.expanded()) {
|
|
1090
|
+
item.collapse();
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
onKeydown(event) {
|
|
1095
|
+
if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) {
|
|
1096
|
+
return;
|
|
1097
|
+
}
|
|
1098
|
+
const headings = this.items().filter((item) => !item.disabled());
|
|
1099
|
+
const current = headings.findIndex((item) => event.target?.id === item.triggerId);
|
|
1100
|
+
if (current < 0) {
|
|
1101
|
+
// A key typed in a panel (or in a nested accordion) is not ours.
|
|
1102
|
+
return;
|
|
1103
|
+
}
|
|
1104
|
+
let target;
|
|
1105
|
+
switch (event.key) {
|
|
1106
|
+
case 'ArrowDown':
|
|
1107
|
+
target = (current + 1) % headings.length;
|
|
1108
|
+
break;
|
|
1109
|
+
case 'ArrowUp':
|
|
1110
|
+
target = (current - 1 + headings.length) % headings.length;
|
|
1111
|
+
break;
|
|
1112
|
+
case 'Home':
|
|
1113
|
+
target = 0;
|
|
1114
|
+
break;
|
|
1115
|
+
case 'End':
|
|
1116
|
+
target = headings.length - 1;
|
|
1117
|
+
break;
|
|
1118
|
+
default:
|
|
1119
|
+
return;
|
|
1120
|
+
}
|
|
1121
|
+
event.preventDefault();
|
|
1122
|
+
headings[target].focus();
|
|
1123
|
+
}
|
|
1124
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeAccordionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1125
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.5", type: NxeAccordionComponent, isStandalone: true, selector: "nxe-accordion", inputs: { multi: { classPropertyName: "multi", publicName: "multi", isSignal: true, isRequired: false, transformFunction: null }, headingLevel: { classPropertyName: "headingLevel", publicName: "headingLevel", isSignal: true, isRequired: false, transformFunction: null }, keepAlive: { classPropertyName: "keepAlive", publicName: "keepAlive", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "keydown": "onKeydown($event)" }, classAttribute: "nxe-accordion" }, providers: [{ provide: NXE_ACCORDION, useExisting: forwardRef(() => NxeAccordionComponent) }], queries: [{ propertyName: "items", predicate: NxeAccordionItemComponent, isSignal: true }], exportAs: ["nxeAccordion"], ngImport: i0, template: '<ng-content />', isInline: true, styles: ["nxe-accordion.nxe-accordion{display:block;overflow:hidden;border:1px solid var(--nxe-ui-border);border-radius:var(--nxe-ui-radius-md);background:var(--nxe-ui-surface);font-family:var(--nxe-ui-font-family)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
1126
|
+
}
|
|
1127
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeAccordionComponent, decorators: [{
|
|
1128
|
+
type: Component,
|
|
1129
|
+
args: [{ selector: 'nxe-accordion', exportAs: 'nxeAccordion', template: '<ng-content />', providers: [{ provide: NXE_ACCORDION, useExisting: forwardRef(() => NxeAccordionComponent) }], host: {
|
|
1130
|
+
class: 'nxe-accordion',
|
|
1131
|
+
'(keydown)': 'onKeydown($event)',
|
|
1132
|
+
}, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, styles: ["nxe-accordion.nxe-accordion{display:block;overflow:hidden;border:1px solid var(--nxe-ui-border);border-radius:var(--nxe-ui-radius-md);background:var(--nxe-ui-surface);font-family:var(--nxe-ui-font-family)}\n"] }]
|
|
1133
|
+
}], ctorParameters: () => [], propDecorators: { multi: [{ type: i0.Input, args: [{ isSignal: true, alias: "multi", required: false }] }], headingLevel: [{ type: i0.Input, args: [{ isSignal: true, alias: "headingLevel", required: false }] }], keepAlive: [{ type: i0.Input, args: [{ isSignal: true, alias: "keepAlive", required: false }] }], items: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => NxeAccordionItemComponent), { isSignal: true }] }] } });
|
|
1134
|
+
|
|
1135
|
+
// @nexterajs/ui/display — public API.
|
|
1136
|
+
|
|
1137
|
+
/**
|
|
1138
|
+
* Generated bundle index. Do not edit.
|
|
1139
|
+
*/
|
|
1140
|
+
|
|
1141
|
+
export { NXE_ACCORDION, NXE_AVATAR_GROUP, NXE_AVATAR_TONES, NXE_STATUS_FAMILIES, NxeAccordionComponent, NxeAccordionContentDirective, NxeAccordionItemComponent, NxeAvatarComponent, NxeAvatarGroupComponent, NxeBadgeAnchorDirective, NxeBadgeComponent, NxeCardComponent, NxeDescriptionItemComponent, NxeDescriptionListComponent, NxeEmptyStateComponent, NxeSkeletonComponent, NxeSpinnerComponent, NxeTagComponent, NxeTimelineComponent, NxeTimelineIconDirective, NxeTimelineItemComponent, nxeAvatarInitials, nxeAvatarToneIndex, toTimelineDateTime };
|
|
1142
|
+
//# sourceMappingURL=nexterajs-ui-display.mjs.map
|