@ojiepermana/angular-theme 22.0.43 → 22.0.44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/ojiepermana-angular-theme-layout-services.mjs +6 -6
- package/fesm2022/ojiepermana-angular-theme-layout-wrapper.mjs +27 -27
- package/fesm2022/ojiepermana-angular-theme-layout.mjs +24 -24
- package/fesm2022/ojiepermana-angular-theme-page.mjs +507 -39
- package/fesm2022/ojiepermana-angular-theme-styles.mjs +15 -15
- package/package.json +3 -3
- package/page/README.md +157 -11
- package/types/ojiepermana-angular-theme-page.d.ts +188 -43
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { signal, computed, Injectable, inject, input,
|
|
2
|
+
import { effect, signal, computed, Injectable, inject, ElementRef, input, booleanAttribute, untracked, ChangeDetectionStrategy, Component, contentChild, output } from '@angular/core';
|
|
3
3
|
import { cn } from '@ojiepermana/angular-component/utils';
|
|
4
4
|
import { NavigationContainerComponent, NavigationFlyoutComponent, NavigationHeaderComponent, NavigationFooterComponent } from '@ojiepermana/angular-navigation';
|
|
5
5
|
import { NavigationService } from '@ojiepermana/angular-navigation/service';
|
|
@@ -7,6 +7,45 @@ import { LayoutService } from '@ojiepermana/angular-theme/layout';
|
|
|
7
7
|
import { LayoutIdentityService, LayoutBrand, LayoutUser } from '@ojiepermana/angular-theme/layout/wrapper';
|
|
8
8
|
import { DOCUMENT } from '@angular/common';
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Manajemen fokus untuk panel mengambang (drawer/overlay): saat terbuka, fokus dipindahkan ke
|
|
12
|
+
* panel; saat tertutup, fokus dikembalikan ke elemen pemicu (mis. tombol toggle). Memenuhi
|
|
13
|
+
* WCAG 2.4.3 (Focus Order). Hanya aktif untuk mode interaktif, aman saat SSR (tanpa `defaultView`),
|
|
14
|
+
* dan tidak mencuri fokus pada render awal.
|
|
15
|
+
*
|
|
16
|
+
* Harus dipanggil di dalam injection context (constructor) karena membuat `effect`.
|
|
17
|
+
*/
|
|
18
|
+
function setupOverlayFocusManagement(config) {
|
|
19
|
+
let initialized = false;
|
|
20
|
+
let wasOpen = false;
|
|
21
|
+
let returnFocus = null;
|
|
22
|
+
effect(() => {
|
|
23
|
+
const interactive = config.isInteractive();
|
|
24
|
+
const open = interactive && config.isOpen();
|
|
25
|
+
const view = config.document?.defaultView ?? null;
|
|
26
|
+
// Lewati render pertama agar tidak mencuri fokus saat panel sudah controlled-open sejak awal.
|
|
27
|
+
if (!initialized) {
|
|
28
|
+
initialized = true;
|
|
29
|
+
wasOpen = open;
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (view) {
|
|
33
|
+
if (open && !wasOpen) {
|
|
34
|
+
returnFocus = config.document?.activeElement ?? null;
|
|
35
|
+
view.requestAnimationFrame(() => config.host.focus());
|
|
36
|
+
}
|
|
37
|
+
else if (!open && wasOpen) {
|
|
38
|
+
const target = returnFocus;
|
|
39
|
+
returnFocus = null;
|
|
40
|
+
if (target && typeof target.focus === 'function') {
|
|
41
|
+
view.requestAnimationFrame(() => target.focus());
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
wasOpen = open;
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
10
49
|
const PAGE_VARIANTS = ['stacked', 'side'];
|
|
11
50
|
const PAGE_SIDE_POSITIONS = ['left', 'right'];
|
|
12
51
|
const PAGE_SIDE_MODES = ['sticky', 'drawer', 'overlay'];
|
|
@@ -14,6 +53,8 @@ const PAGE_SCROLL_VALUES = ['content', 'page'];
|
|
|
14
53
|
const PAGE_HEIGHT_VALUES = ['auto', 'fix'];
|
|
15
54
|
/** Visual appearance shared with the layout/navigation axes — unifies borders. */
|
|
16
55
|
const PAGE_APPEARANCES = ['flat', 'border-rail'];
|
|
56
|
+
/** Penempatan `PageFilter`: `stacked` = bar antara header & content; `side` = kolom di samping content. */
|
|
57
|
+
const PAGE_FILTER_PLACEMENTS = ['stacked', 'side'];
|
|
17
58
|
const PAGE_DEFAULT_VARIANT = 'stacked';
|
|
18
59
|
const PAGE_DEFAULT_SIDE_POSITION = 'left';
|
|
19
60
|
const PAGE_DEFAULT_SIDE_MODE = 'sticky';
|
|
@@ -21,6 +62,14 @@ const PAGE_DEFAULT_SCROLL = 'content';
|
|
|
21
62
|
const PAGE_DEFAULT_HEIGHT = 'auto';
|
|
22
63
|
const PAGE_DEFAULT_APPEARANCE = 'flat';
|
|
23
64
|
const PAGE_DEFAULT_SIDE_WIDTH = '16rem';
|
|
65
|
+
/** Placement default `PageFilter`. */
|
|
66
|
+
const PAGE_DEFAULT_FILTER_PLACEMENT = 'stacked';
|
|
67
|
+
/** Mode default `PageFilter` saat `placement="side"`; `stacked` selalu berperilaku `sticky`. */
|
|
68
|
+
const PAGE_DEFAULT_FILTER_MODE = 'sticky';
|
|
69
|
+
/** Posisi default kolom `PageFilter` saat `placement="side"`. */
|
|
70
|
+
const PAGE_DEFAULT_FILTER_POSITION = 'left';
|
|
71
|
+
/** Lebar default kolom `PageFilter` saat `placement="side"` (sticky/drawer/overlay). */
|
|
72
|
+
const PAGE_DEFAULT_FILTER_WIDTH = '18rem';
|
|
24
73
|
function isUiPageVariant(value) {
|
|
25
74
|
return value !== null && PAGE_VARIANTS.includes(value);
|
|
26
75
|
}
|
|
@@ -39,6 +88,9 @@ function isUiPageHeight(value) {
|
|
|
39
88
|
function isUiPageAppearance(value) {
|
|
40
89
|
return value !== null && PAGE_APPEARANCES.includes(value);
|
|
41
90
|
}
|
|
91
|
+
function isUiPageFilterPlacement(value) {
|
|
92
|
+
return value !== null && PAGE_FILTER_PLACEMENTS.includes(value);
|
|
93
|
+
}
|
|
42
94
|
|
|
43
95
|
class PageStateService {
|
|
44
96
|
variantState = signal(PAGE_DEFAULT_VARIANT, /* @ts-ignore */
|
|
@@ -68,6 +120,25 @@ class PageStateService {
|
|
|
68
120
|
/** Aktif saat apps-launcher mengambang di atas `PageHeader`; header memesan ruang kanan agar isinya tidak tertimpa. */
|
|
69
121
|
appsLauncherReserveState = signal(false, /* @ts-ignore */
|
|
70
122
|
...(ngDevMode ? [{ debugName: "appsLauncherReserveState" }] : /* istanbul ignore next */ []));
|
|
123
|
+
// Filter state — paralel dengan side state agar `PageFilter` punya open/close sendiri yang independen.
|
|
124
|
+
filterPlacementState = signal(PAGE_DEFAULT_FILTER_PLACEMENT, /* @ts-ignore */
|
|
125
|
+
...(ngDevMode ? [{ debugName: "filterPlacementState" }] : /* istanbul ignore next */ []));
|
|
126
|
+
filterModeState = signal(PAGE_DEFAULT_FILTER_MODE, /* @ts-ignore */
|
|
127
|
+
...(ngDevMode ? [{ debugName: "filterModeState" }] : /* istanbul ignore next */ []));
|
|
128
|
+
filterPositionState = signal(PAGE_DEFAULT_FILTER_POSITION, /* @ts-ignore */
|
|
129
|
+
...(ngDevMode ? [{ debugName: "filterPositionState" }] : /* istanbul ignore next */ []));
|
|
130
|
+
filterWidthState = signal(PAGE_DEFAULT_FILTER_WIDTH, /* @ts-ignore */
|
|
131
|
+
...(ngDevMode ? [{ debugName: "filterWidthState" }] : /* istanbul ignore next */ []));
|
|
132
|
+
filterIdState = signal(null, /* @ts-ignore */
|
|
133
|
+
...(ngDevMode ? [{ debugName: "filterIdState" }] : /* istanbul ignore next */ []));
|
|
134
|
+
internalFilterOpenState = signal(false, /* @ts-ignore */
|
|
135
|
+
...(ngDevMode ? [{ debugName: "internalFilterOpenState" }] : /* istanbul ignore next */ []));
|
|
136
|
+
controlledFilterOpenState = signal(null, /* @ts-ignore */
|
|
137
|
+
...(ngDevMode ? [{ debugName: "controlledFilterOpenState" }] : /* istanbul ignore next */ []));
|
|
138
|
+
filterOpenRequestState = signal(null, /* @ts-ignore */
|
|
139
|
+
...(ngDevMode ? [{ debugName: "filterOpenRequestState" }] : /* istanbul ignore next */ []));
|
|
140
|
+
filterOpenRequestVersionState = signal(0, /* @ts-ignore */
|
|
141
|
+
...(ngDevMode ? [{ debugName: "filterOpenRequestVersionState" }] : /* istanbul ignore next */ []));
|
|
71
142
|
variant = this.variantState.asReadonly();
|
|
72
143
|
height = this.heightState.asReadonly();
|
|
73
144
|
scroll = this.scrollState.asReadonly();
|
|
@@ -85,6 +156,19 @@ class PageStateService {
|
|
|
85
156
|
...(ngDevMode ? [{ debugName: "isSideInteractive" }] : /* istanbul ignore next */ []));
|
|
86
157
|
isSideVisible = computed(() => this.sideMode() === 'sticky' || this.sideOpen(), /* @ts-ignore */
|
|
87
158
|
...(ngDevMode ? [{ debugName: "isSideVisible" }] : /* istanbul ignore next */ []));
|
|
159
|
+
filterPlacement = this.filterPlacementState.asReadonly();
|
|
160
|
+
filterMode = this.filterModeState.asReadonly();
|
|
161
|
+
filterPosition = this.filterPositionState.asReadonly();
|
|
162
|
+
filterWidth = this.filterWidthState.asReadonly();
|
|
163
|
+
filterId = this.filterIdState.asReadonly();
|
|
164
|
+
filterOpenRequest = this.filterOpenRequestState.asReadonly();
|
|
165
|
+
filterOpenRequestVersion = this.filterOpenRequestVersionState.asReadonly();
|
|
166
|
+
filterOpen = computed(() => this.controlledFilterOpenState() ?? this.internalFilterOpenState(), /* @ts-ignore */
|
|
167
|
+
...(ngDevMode ? [{ debugName: "filterOpen" }] : /* istanbul ignore next */ []));
|
|
168
|
+
isFilterInteractive = computed(() => this.filterMode() === 'drawer' || this.filterMode() === 'overlay', /* @ts-ignore */
|
|
169
|
+
...(ngDevMode ? [{ debugName: "isFilterInteractive" }] : /* istanbul ignore next */ []));
|
|
170
|
+
isFilterVisible = computed(() => this.filterMode() === 'sticky' || this.filterOpen(), /* @ts-ignore */
|
|
171
|
+
...(ngDevMode ? [{ debugName: "isFilterVisible" }] : /* istanbul ignore next */ []));
|
|
88
172
|
registerRoot(config) {
|
|
89
173
|
this.variantState.set(config.variant);
|
|
90
174
|
this.heightState.set(config.height);
|
|
@@ -129,13 +213,211 @@ class PageStateService {
|
|
|
129
213
|
this.sideOpenRequestVersionState.update((version) => version + 1);
|
|
130
214
|
return open;
|
|
131
215
|
}
|
|
132
|
-
|
|
133
|
-
|
|
216
|
+
registerFilter(config) {
|
|
217
|
+
this.filterPlacementState.set(config.placement);
|
|
218
|
+
this.filterModeState.set(config.mode);
|
|
219
|
+
this.filterPositionState.set(config.position);
|
|
220
|
+
this.filterWidthState.set(config.width);
|
|
221
|
+
this.filterIdState.set(config.id);
|
|
222
|
+
}
|
|
223
|
+
setControlledFilterOpen(open) {
|
|
224
|
+
this.controlledFilterOpenState.set(open);
|
|
225
|
+
if (open !== null) {
|
|
226
|
+
this.internalFilterOpenState.set(open);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
setFilterOpen(open) {
|
|
230
|
+
this.internalFilterOpenState.set(open);
|
|
231
|
+
}
|
|
232
|
+
openFilter() {
|
|
233
|
+
return this.requestFilterOpenChange(true);
|
|
234
|
+
}
|
|
235
|
+
closeFilter() {
|
|
236
|
+
return this.requestFilterOpenChange(false);
|
|
237
|
+
}
|
|
238
|
+
toggleFilter() {
|
|
239
|
+
return this.requestFilterOpenChange(!this.filterOpen());
|
|
240
|
+
}
|
|
241
|
+
requestFilterOpenChange(open) {
|
|
242
|
+
if (this.controlledFilterOpenState() === null) {
|
|
243
|
+
this.internalFilterOpenState.set(open);
|
|
244
|
+
}
|
|
245
|
+
this.filterOpenRequestState.set(open);
|
|
246
|
+
this.filterOpenRequestVersionState.update((version) => version + 1);
|
|
247
|
+
return open;
|
|
248
|
+
}
|
|
249
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PageStateService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
250
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PageStateService });
|
|
134
251
|
}
|
|
135
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.
|
|
252
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PageStateService, decorators: [{
|
|
136
253
|
type: Injectable
|
|
137
254
|
}] });
|
|
138
255
|
|
|
256
|
+
let nextPageFilterId = 0;
|
|
257
|
+
/**
|
|
258
|
+
* `PageFilter` — slot filter di antara `PageHeader` dan `PageContent` (`placement="stacked"`)
|
|
259
|
+
* atau di samping `PageContent` (`placement="side"`). Pada `stacked`, filter mendorong content
|
|
260
|
+
* ke bawah dan content menyesuaikan tinggi sisa. Pada `side`, filter mendorong content ke samping
|
|
261
|
+
* dengan mode `sticky | drawer | overlay` (mengikuti pola `PageSide`).
|
|
262
|
+
*
|
|
263
|
+
* `placement="stacked"` selalu berperilaku `sticky` — `drawer`/`overlay` hanya berlaku untuk `side`.
|
|
264
|
+
*/
|
|
265
|
+
class PageFilterComponent {
|
|
266
|
+
document = inject(DOCUMENT, { optional: true });
|
|
267
|
+
host = inject((ElementRef));
|
|
268
|
+
page = inject(PageStateService);
|
|
269
|
+
resolvedId = `page-filter-${++nextPageFilterId}`;
|
|
270
|
+
placement = input(PAGE_DEFAULT_FILTER_PLACEMENT, /* @ts-ignore */
|
|
271
|
+
...(ngDevMode ? [{ debugName: "placement" }] : /* istanbul ignore next */ []));
|
|
272
|
+
mode = input(PAGE_DEFAULT_FILTER_MODE, /* @ts-ignore */
|
|
273
|
+
...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
|
|
274
|
+
position = input(null, /* @ts-ignore */
|
|
275
|
+
...(ngDevMode ? [{ debugName: "position" }] : /* istanbul ignore next */ []));
|
|
276
|
+
width = input(null, /* @ts-ignore */
|
|
277
|
+
...(ngDevMode ? [{ debugName: "width" }] : /* istanbul ignore next */ []));
|
|
278
|
+
closeOnEsc = input(true, /* @ts-ignore */
|
|
279
|
+
...(ngDevMode ? [{ debugName: "closeOnEsc" }] : /* istanbul ignore next */ []));
|
|
280
|
+
/**
|
|
281
|
+
* Khusus `placement="stacked"`: jadikan bar dapat dibuka/tutup lewat `PageFilterToggle`
|
|
282
|
+
* (default tertutup). Saat tertutup, bar disembunyikan dan content mengisi ruangnya kembali.
|
|
283
|
+
* Diabaikan untuk `placement="side"` (gunakan `mode="drawer"`/`overlay"` untuk side yang bisa ditutup).
|
|
284
|
+
*/
|
|
285
|
+
collapsible = input(false, { ...(ngDevMode ? { debugName: "collapsible" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
286
|
+
/** Nama aksesibilitas untuk panel saat mode `drawer`/`overlay` (dipasang sebagai `aria-label` dialog). */
|
|
287
|
+
ariaLabel = input('Filters', /* @ts-ignore */
|
|
288
|
+
...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
|
|
289
|
+
class = input('', /* @ts-ignore */
|
|
290
|
+
...(ngDevMode ? [{ debugName: "class" }] : /* istanbul ignore next */ []));
|
|
291
|
+
resolvedPlacement = computed(() => this.placement(), /* @ts-ignore */
|
|
292
|
+
...(ngDevMode ? [{ debugName: "resolvedPlacement" }] : /* istanbul ignore next */ []));
|
|
293
|
+
/** `stacked` adalah bar horizontal — tidak punya drawer/overlay, jadi mode efektifnya selalu `sticky`. */
|
|
294
|
+
effectiveMode = computed(() => this.resolvedPlacement() === 'stacked' ? 'sticky' : this.mode(), /* @ts-ignore */
|
|
295
|
+
...(ngDevMode ? [{ debugName: "effectiveMode" }] : /* istanbul ignore next */ []));
|
|
296
|
+
resolvedPosition = computed(() => this.position() ?? this.page.filterPosition(), /* @ts-ignore */
|
|
297
|
+
...(ngDevMode ? [{ debugName: "resolvedPosition" }] : /* istanbul ignore next */ []));
|
|
298
|
+
resolvedWidth = computed(() => this.width() ?? this.page.filterWidth() ?? PAGE_DEFAULT_FILTER_WIDTH, /* @ts-ignore */
|
|
299
|
+
...(ngDevMode ? [{ debugName: "resolvedWidth" }] : /* istanbul ignore next */ []));
|
|
300
|
+
resolvedScroll = computed(() => this.page.scroll() ?? PAGE_DEFAULT_SCROLL, /* @ts-ignore */
|
|
301
|
+
...(ngDevMode ? [{ debugName: "resolvedScroll" }] : /* istanbul ignore next */ []));
|
|
302
|
+
isStacked = computed(() => this.resolvedPlacement() === 'stacked', /* @ts-ignore */
|
|
303
|
+
...(ngDevMode ? [{ debugName: "isStacked" }] : /* istanbul ignore next */ []));
|
|
304
|
+
/** Bar stacked yang dapat ditutup dan sedang tertutup → disembunyikan dari layout. */
|
|
305
|
+
isCollapsedStacked = computed(() => this.isStacked() && this.collapsible() && !this.page.filterOpen(), /* @ts-ignore */
|
|
306
|
+
...(ngDevMode ? [{ debugName: "isCollapsedStacked" }] : /* istanbul ignore next */ []));
|
|
307
|
+
isSticky = computed(() => this.effectiveMode() === 'sticky', /* @ts-ignore */
|
|
308
|
+
...(ngDevMode ? [{ debugName: "isSticky" }] : /* istanbul ignore next */ []));
|
|
309
|
+
isDrawer = computed(() => this.effectiveMode() === 'drawer', /* @ts-ignore */
|
|
310
|
+
...(ngDevMode ? [{ debugName: "isDrawer" }] : /* istanbul ignore next */ []));
|
|
311
|
+
isOverlay = computed(() => this.effectiveMode() === 'overlay', /* @ts-ignore */
|
|
312
|
+
...(ngDevMode ? [{ debugName: "isOverlay" }] : /* istanbul ignore next */ []));
|
|
313
|
+
/** Mode mengambang (drawer/overlay) — bersifat dialog dan butuh manajemen fokus + `inert` saat tertutup. */
|
|
314
|
+
isInteractive = computed(() => this.isDrawer() || this.isOverlay(), /* @ts-ignore */
|
|
315
|
+
...(ngDevMode ? [{ debugName: "isInteractive" }] : /* istanbul ignore next */ []));
|
|
316
|
+
/** Drawer/overlay yang tertutup disembunyikan dari pohon aksesibilitas. */
|
|
317
|
+
ariaHidden = computed(() => !this.isSticky() && !this.page.filterOpen() ? 'true' : null, /* @ts-ignore */
|
|
318
|
+
...(ngDevMode ? [{ debugName: "ariaHidden" }] : /* istanbul ignore next */ []));
|
|
319
|
+
/** Saat panel mengambang tertutup, `inert` mengeluarkan isinya dari tab order & pohon aksesibilitas (AXE). */
|
|
320
|
+
inertWhenClosed = computed(() => (this.isInteractive() && !this.page.filterOpen() ? '' : null), /* @ts-ignore */
|
|
321
|
+
...(ngDevMode ? [{ debugName: "inertWhenClosed" }] : /* istanbul ignore next */ []));
|
|
322
|
+
dialogRole = computed(() => (this.isInteractive() ? 'dialog' : null), /* @ts-ignore */
|
|
323
|
+
...(ngDevMode ? [{ debugName: "dialogRole" }] : /* istanbul ignore next */ []));
|
|
324
|
+
ariaModal = computed(() => (this.isOverlay() ? 'true' : null), /* @ts-ignore */
|
|
325
|
+
...(ngDevMode ? [{ debugName: "ariaModal" }] : /* istanbul ignore next */ []));
|
|
326
|
+
dialogLabel = computed(() => (this.isInteractive() ? this.ariaLabel() : null), /* @ts-ignore */
|
|
327
|
+
...(ngDevMode ? [{ debugName: "dialogLabel" }] : /* istanbul ignore next */ []));
|
|
328
|
+
dialogTabindex = computed(() => (this.isInteractive() ? '-1' : null), /* @ts-ignore */
|
|
329
|
+
...(ngDevMode ? [{ debugName: "dialogTabindex" }] : /* istanbul ignore next */ []));
|
|
330
|
+
classes = computed(() => {
|
|
331
|
+
const position = this.resolvedPosition();
|
|
332
|
+
const scroll = this.resolvedScroll();
|
|
333
|
+
const open = this.page.filterOpen();
|
|
334
|
+
// STACKED: bar selebar content yang mendorong content ke bawah (shrink-0 = tinggi mengikuti isi).
|
|
335
|
+
if (this.isStacked()) {
|
|
336
|
+
return cn('block shrink-0 border-b border-border bg-background',
|
|
337
|
+
// Saat seluruh page yang scroll, filter dipin ke atas area scroll agar tetap terjangkau.
|
|
338
|
+
scroll === 'page' && 'sticky top-0 z-10', this.class(),
|
|
339
|
+
// Collapsible & tertutup → `hidden` (menang atas `block`); content mengisi ruangnya kembali.
|
|
340
|
+
this.isCollapsedStacked() && 'hidden');
|
|
341
|
+
}
|
|
342
|
+
// SIDE: kolom di samping content.
|
|
343
|
+
return cn('block min-h-0 border-border bg-background', scroll === 'content' && 'h-full overflow-auto', scroll === 'page' && 'overflow-visible',
|
|
344
|
+
// sticky → mendorong content (push); urutan kolom di-handle oleh `order-last` saat di kanan.
|
|
345
|
+
this.isSticky() && 'shrink-0 w-[var(--page-filter-width)]', this.isSticky() && position === 'left' && 'border-r', this.isSticky() && position === 'right' && 'order-last border-l',
|
|
346
|
+
// drawer → meluncur dari tepi content region, mengambang di atas content.
|
|
347
|
+
this.isDrawer() &&
|
|
348
|
+
'absolute inset-y-0 z-20 w-[var(--page-filter-width)] border shadow-sm transition-transform duration-200 ease-out', this.isDrawer() && position === 'left' && (open ? 'left-0 translate-x-0' : 'left-0 -translate-x-full'), this.isDrawer() && position === 'right' && (open ? 'right-0 translate-x-0' : 'right-0 translate-x-full'),
|
|
349
|
+
// overlay → seperti drawer, tapi di atas drawer (z lebih tinggi) dengan backdrop.
|
|
350
|
+
this.isOverlay() &&
|
|
351
|
+
'absolute inset-y-0 z-30 w-[var(--page-filter-width)] border shadow-md transition-transform duration-200 ease-out', this.isOverlay() && position === 'left' && (open ? 'left-0 translate-x-0' : 'left-0 -translate-x-full'), this.isOverlay() && position === 'right' && (open ? 'right-0 translate-x-0' : 'right-0 translate-x-full'), this.class());
|
|
352
|
+
}, /* @ts-ignore */
|
|
353
|
+
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
354
|
+
constructor() {
|
|
355
|
+
setupOverlayFocusManagement({
|
|
356
|
+
document: this.document,
|
|
357
|
+
host: this.host.nativeElement,
|
|
358
|
+
isOpen: this.page.filterOpen,
|
|
359
|
+
isInteractive: this.isInteractive,
|
|
360
|
+
});
|
|
361
|
+
effect(() => {
|
|
362
|
+
this.page.registerFilter({
|
|
363
|
+
placement: this.resolvedPlacement(),
|
|
364
|
+
mode: this.effectiveMode(),
|
|
365
|
+
position: this.resolvedPosition(),
|
|
366
|
+
width: this.resolvedWidth(),
|
|
367
|
+
id: this.resolvedId,
|
|
368
|
+
});
|
|
369
|
+
});
|
|
370
|
+
effect((onCleanup) => {
|
|
371
|
+
if (!this.closeOnEsc() || this.isSticky()) {
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
const defaultView = this.document?.defaultView;
|
|
375
|
+
if (!defaultView) {
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
const handler = (event) => {
|
|
379
|
+
if (event.key !== 'Escape') {
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
if (!untracked(() => this.page.filterOpen())) {
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
untracked(() => {
|
|
386
|
+
this.page.closeFilter();
|
|
387
|
+
});
|
|
388
|
+
};
|
|
389
|
+
defaultView.addEventListener('keydown', handler);
|
|
390
|
+
onCleanup(() => defaultView.removeEventListener('keydown', handler));
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PageFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
394
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.4", type: PageFilterComponent, isStandalone: true, selector: "PageFilter", inputs: { placement: { classPropertyName: "placement", publicName: "placement", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, closeOnEsc: { classPropertyName: "closeOnEsc", publicName: "closeOnEsc", isSignal: true, isRequired: false, transformFunction: null }, collapsible: { classPropertyName: "collapsible", publicName: "collapsible", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()", "attr.id": "resolvedId", "attr.data-page-slot": "\"filter\"", "attr.data-page-filter-placement": "resolvedPlacement()", "attr.data-page-filter-mode": "effectiveMode()", "attr.data-page-filter-open": "page.filterOpen()", "attr.data-page-position": "resolvedPosition()", "attr.aria-hidden": "ariaHidden()", "attr.inert": "inertWhenClosed()", "attr.role": "dialogRole()", "attr.aria-modal": "ariaModal()", "attr.aria-label": "dialogLabel()", "attr.tabindex": "dialogTabindex()", "style.--page-filter-width": "resolvedWidth()" } }, ngImport: i0, template: `<ng-content />`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
395
|
+
}
|
|
396
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PageFilterComponent, decorators: [{
|
|
397
|
+
type: Component,
|
|
398
|
+
args: [{
|
|
399
|
+
selector: 'PageFilter',
|
|
400
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
401
|
+
host: {
|
|
402
|
+
'[class]': 'classes()',
|
|
403
|
+
'[attr.id]': 'resolvedId',
|
|
404
|
+
'[attr.data-page-slot]': '"filter"',
|
|
405
|
+
'[attr.data-page-filter-placement]': 'resolvedPlacement()',
|
|
406
|
+
'[attr.data-page-filter-mode]': 'effectiveMode()',
|
|
407
|
+
'[attr.data-page-filter-open]': 'page.filterOpen()',
|
|
408
|
+
'[attr.data-page-position]': 'resolvedPosition()',
|
|
409
|
+
'[attr.aria-hidden]': 'ariaHidden()',
|
|
410
|
+
'[attr.inert]': 'inertWhenClosed()',
|
|
411
|
+
'[attr.role]': 'dialogRole()',
|
|
412
|
+
'[attr.aria-modal]': 'ariaModal()',
|
|
413
|
+
'[attr.aria-label]': 'dialogLabel()',
|
|
414
|
+
'[attr.tabindex]': 'dialogTabindex()',
|
|
415
|
+
'[style.--page-filter-width]': 'resolvedWidth()',
|
|
416
|
+
},
|
|
417
|
+
template: `<ng-content />`,
|
|
418
|
+
}]
|
|
419
|
+
}], ctorParameters: () => [], propDecorators: { placement: [{ type: i0.Input, args: [{ isSignal: true, alias: "placement", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], closeOnEsc: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnEsc", required: false }] }], collapsible: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsible", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
|
|
420
|
+
|
|
139
421
|
function buildPageBodyClasses(scroll, customClass) {
|
|
140
422
|
return cn('block min-w-0', scroll === 'content' && 'h-full min-h-0 overflow-auto', scroll === 'page' && 'overflow-visible', customClass);
|
|
141
423
|
}
|
|
@@ -157,10 +439,10 @@ class PageHeaderComponent {
|
|
|
157
439
|
// appearance unifies the border with the layout/nav: border-rail = 1.5px.
|
|
158
440
|
this.isBorderRail() ? 'border-b-[1.5px]' : 'border-b', this.class(), this.resolvedHeight() === 'fix' && 'h-12 overflow-hidden'), /* @ts-ignore */
|
|
159
441
|
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
160
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.
|
|
161
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.
|
|
442
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PageHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
443
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.4", type: PageHeaderComponent, isStandalone: true, selector: "PageHeader", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-page-slot": "header" }, properties: { "class": "classes()", "style.padding-right": "appsLauncherReservePadding()" } }, ngImport: i0, template: `<ng-content />`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
162
444
|
}
|
|
163
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.
|
|
445
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PageHeaderComponent, decorators: [{
|
|
164
446
|
type: Component,
|
|
165
447
|
args: [{
|
|
166
448
|
selector: 'PageHeader',
|
|
@@ -183,10 +465,10 @@ class PageContentComponent {
|
|
|
183
465
|
...(ngDevMode ? [{ debugName: "resolvedScroll" }] : /* istanbul ignore next */ []));
|
|
184
466
|
classes = computed(() => buildPageBodyClasses(this.resolvedScroll(), this.class()), /* @ts-ignore */
|
|
185
467
|
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
186
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.
|
|
187
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.
|
|
468
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PageContentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
469
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.4", type: PageContentComponent, isStandalone: true, selector: "PageContent", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-page-slot": "content" }, properties: { "class": "classes()" } }, ngImport: i0, template: `<ng-content />`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
188
470
|
}
|
|
189
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.
|
|
471
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PageContentComponent, decorators: [{
|
|
190
472
|
type: Component,
|
|
191
473
|
args: [{
|
|
192
474
|
selector: 'PageContent',
|
|
@@ -206,10 +488,10 @@ class PageDashboardComponent {
|
|
|
206
488
|
...(ngDevMode ? [{ debugName: "resolvedScroll" }] : /* istanbul ignore next */ []));
|
|
207
489
|
classes = computed(() => buildPageBodyClasses(this.resolvedScroll(), this.class()), /* @ts-ignore */
|
|
208
490
|
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
209
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.
|
|
210
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.
|
|
491
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PageDashboardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
492
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.4", type: PageDashboardComponent, isStandalone: true, selector: "PageDashboard", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-page-slot": "dashboard" }, properties: { "class": "classes()" } }, ngImport: i0, template: `<ng-content />`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
211
493
|
}
|
|
212
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.
|
|
494
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PageDashboardComponent, decorators: [{
|
|
213
495
|
type: Component,
|
|
214
496
|
args: [{
|
|
215
497
|
selector: 'PageDashboard',
|
|
@@ -233,10 +515,10 @@ class PageFooterComponent {
|
|
|
233
515
|
// appearance unifies the border with the layout/nav: border-rail = 1.5px.
|
|
234
516
|
this.isBorderRail() ? 'border-t-[1.5px]' : 'border-t', this.class(), this.resolvedHeight() === 'fix' && 'h-12 overflow-hidden'), /* @ts-ignore */
|
|
235
517
|
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
236
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.
|
|
237
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.
|
|
518
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PageFooterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
519
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.4", type: PageFooterComponent, isStandalone: true, selector: "PageFooter", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-page-slot": "footer" }, properties: { "class": "classes()" } }, ngImport: i0, template: `<ng-content />`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
238
520
|
}
|
|
239
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.
|
|
521
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PageFooterComponent, decorators: [{
|
|
240
522
|
type: Component,
|
|
241
523
|
args: [{
|
|
242
524
|
selector: 'PageFooter',
|
|
@@ -252,6 +534,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImpor
|
|
|
252
534
|
let nextPageSideId = 0;
|
|
253
535
|
class PageSideComponent {
|
|
254
536
|
document = inject(DOCUMENT, { optional: true });
|
|
537
|
+
host = inject((ElementRef));
|
|
255
538
|
page = inject(PageStateService);
|
|
256
539
|
resolvedId = `page-side-${++nextPageSideId}`;
|
|
257
540
|
mode = input('sticky', /* @ts-ignore */
|
|
@@ -262,6 +545,9 @@ class PageSideComponent {
|
|
|
262
545
|
...(ngDevMode ? [{ debugName: "width" }] : /* istanbul ignore next */ []));
|
|
263
546
|
closeOnEsc = input(true, /* @ts-ignore */
|
|
264
547
|
...(ngDevMode ? [{ debugName: "closeOnEsc" }] : /* istanbul ignore next */ []));
|
|
548
|
+
/** Nama aksesibilitas untuk panel saat mode `drawer`/`overlay` (dipasang sebagai `aria-label` dialog). */
|
|
549
|
+
ariaLabel = input('Side panel', /* @ts-ignore */
|
|
550
|
+
...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
|
|
265
551
|
class = input('', /* @ts-ignore */
|
|
266
552
|
...(ngDevMode ? [{ debugName: "class" }] : /* istanbul ignore next */ []));
|
|
267
553
|
resolvedMode = computed(() => this.mode() ?? this.page.sideMode(), /* @ts-ignore */
|
|
@@ -276,19 +562,41 @@ class PageSideComponent {
|
|
|
276
562
|
...(ngDevMode ? [{ debugName: "isDrawer" }] : /* istanbul ignore next */ []));
|
|
277
563
|
isOverlay = computed(() => this.resolvedMode() === 'overlay', /* @ts-ignore */
|
|
278
564
|
...(ngDevMode ? [{ debugName: "isOverlay" }] : /* istanbul ignore next */ []));
|
|
565
|
+
/** Mode mengambang (drawer/overlay) — bersifat dialog dan butuh manajemen fokus + `inert` saat tertutup. */
|
|
566
|
+
isInteractive = computed(() => this.isDrawer() || this.isOverlay(), /* @ts-ignore */
|
|
567
|
+
...(ngDevMode ? [{ debugName: "isInteractive" }] : /* istanbul ignore next */ []));
|
|
279
568
|
resolvedScroll = computed(() => this.page.scroll() ?? PAGE_DEFAULT_SCROLL, /* @ts-ignore */
|
|
280
569
|
...(ngDevMode ? [{ debugName: "resolvedScroll" }] : /* istanbul ignore next */ []));
|
|
281
570
|
ariaHidden = computed(() => (!this.isSticky() && !this.page.sideOpen() ? 'true' : null), /* @ts-ignore */
|
|
282
571
|
...(ngDevMode ? [{ debugName: "ariaHidden" }] : /* istanbul ignore next */ []));
|
|
572
|
+
/** Saat panel mengambang tertutup, `inert` mengeluarkan isinya dari tab order & pohon aksesibilitas (AXE). */
|
|
573
|
+
inertWhenClosed = computed(() => (this.isInteractive() && !this.page.sideOpen() ? '' : null), /* @ts-ignore */
|
|
574
|
+
...(ngDevMode ? [{ debugName: "inertWhenClosed" }] : /* istanbul ignore next */ []));
|
|
575
|
+
dialogRole = computed(() => (this.isInteractive() ? 'dialog' : null), /* @ts-ignore */
|
|
576
|
+
...(ngDevMode ? [{ debugName: "dialogRole" }] : /* istanbul ignore next */ []));
|
|
577
|
+
ariaModal = computed(() => (this.isOverlay() ? 'true' : null), /* @ts-ignore */
|
|
578
|
+
...(ngDevMode ? [{ debugName: "ariaModal" }] : /* istanbul ignore next */ []));
|
|
579
|
+
dialogLabel = computed(() => (this.isInteractive() ? this.ariaLabel() : null), /* @ts-ignore */
|
|
580
|
+
...(ngDevMode ? [{ debugName: "dialogLabel" }] : /* istanbul ignore next */ []));
|
|
581
|
+
dialogTabindex = computed(() => (this.isInteractive() ? '-1' : null), /* @ts-ignore */
|
|
582
|
+
...(ngDevMode ? [{ debugName: "dialogTabindex" }] : /* istanbul ignore next */ []));
|
|
283
583
|
classes = computed(() => {
|
|
284
584
|
const position = this.resolvedPosition();
|
|
285
585
|
const sideOpen = this.page.sideOpen();
|
|
286
|
-
return cn('block min-h-0 border-border bg-background', this.resolvedScroll() === 'content' && 'h-full overflow-auto', this.resolvedScroll() === 'page' && 'overflow-visible', this.isSticky() && 'shrink-0 w-[var(--page-side-width)]',
|
|
586
|
+
return cn('block min-h-0 border-border bg-background', this.resolvedScroll() === 'content' && 'h-full overflow-auto', this.resolvedScroll() === 'page' && 'overflow-visible', this.isSticky() && 'shrink-0 w-[var(--page-side-width)]',
|
|
587
|
+
// Urutan DOM body = rail-lalu-content; `left` tak perlu reorder, `right` mendorong rail ke kolom kedua.
|
|
588
|
+
this.isSticky() && position === 'left' && 'border-r', this.isSticky() && position === 'right' && 'order-last border-l', this.isDrawer() &&
|
|
287
589
|
'absolute inset-y-0 z-20 w-[var(--page-side-width)] border shadow-sm transition-transform duration-200 ease-out', this.isDrawer() && position === 'left' && (sideOpen ? 'left-0 translate-x-0' : 'left-0 -translate-x-full'), this.isDrawer() && position === 'right' && (sideOpen ? 'right-0 translate-x-0' : 'right-0 translate-x-full'), this.isOverlay() &&
|
|
288
590
|
'absolute inset-y-0 z-30 w-[var(--page-side-width)] border shadow-md transition-transform duration-200 ease-out', this.isOverlay() && position === 'left' && (sideOpen ? 'left-0 translate-x-0' : 'left-0 -translate-x-full'), this.isOverlay() && position === 'right' && (sideOpen ? 'right-0 translate-x-0' : 'right-0 translate-x-full'), this.class());
|
|
289
591
|
}, /* @ts-ignore */
|
|
290
592
|
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
291
593
|
constructor() {
|
|
594
|
+
setupOverlayFocusManagement({
|
|
595
|
+
document: this.document,
|
|
596
|
+
host: this.host.nativeElement,
|
|
597
|
+
isOpen: this.page.sideOpen,
|
|
598
|
+
isInteractive: this.isInteractive,
|
|
599
|
+
});
|
|
292
600
|
effect(() => {
|
|
293
601
|
this.page.registerSide({
|
|
294
602
|
mode: this.resolvedMode(),
|
|
@@ -320,10 +628,10 @@ class PageSideComponent {
|
|
|
320
628
|
onCleanup(() => defaultView.removeEventListener('keydown', handler));
|
|
321
629
|
});
|
|
322
630
|
}
|
|
323
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.
|
|
324
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.
|
|
631
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PageSideComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
632
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.4", type: PageSideComponent, isStandalone: true, selector: "PageSide", inputs: { mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, closeOnEsc: { classPropertyName: "closeOnEsc", publicName: "closeOnEsc", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()", "attr.id": "resolvedId", "attr.data-page-slot": "\"side\"", "attr.data-page-side-mode": "resolvedMode()", "attr.data-page-side-open": "page.sideOpen()", "attr.data-page-position": "resolvedPosition()", "attr.aria-hidden": "ariaHidden()", "attr.inert": "inertWhenClosed()", "attr.role": "dialogRole()", "attr.aria-modal": "ariaModal()", "attr.aria-label": "dialogLabel()", "attr.tabindex": "dialogTabindex()", "style.--page-side-width": "resolvedWidth()" } }, ngImport: i0, template: `<ng-content />`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
325
633
|
}
|
|
326
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.
|
|
634
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PageSideComponent, decorators: [{
|
|
327
635
|
type: Component,
|
|
328
636
|
args: [{
|
|
329
637
|
selector: 'PageSide',
|
|
@@ -336,11 +644,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImpor
|
|
|
336
644
|
'[attr.data-page-side-open]': 'page.sideOpen()',
|
|
337
645
|
'[attr.data-page-position]': 'resolvedPosition()',
|
|
338
646
|
'[attr.aria-hidden]': 'ariaHidden()',
|
|
647
|
+
'[attr.inert]': 'inertWhenClosed()',
|
|
648
|
+
'[attr.role]': 'dialogRole()',
|
|
649
|
+
'[attr.aria-modal]': 'ariaModal()',
|
|
650
|
+
'[attr.aria-label]': 'dialogLabel()',
|
|
651
|
+
'[attr.tabindex]': 'dialogTabindex()',
|
|
339
652
|
'[style.--page-side-width]': 'resolvedWidth()',
|
|
340
653
|
},
|
|
341
654
|
template: `<ng-content />`,
|
|
342
655
|
}]
|
|
343
|
-
}], ctorParameters: () => [], propDecorators: { mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], closeOnEsc: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnEsc", required: false }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
|
|
656
|
+
}], ctorParameters: () => [], propDecorators: { mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], closeOnEsc: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnEsc", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
|
|
344
657
|
|
|
345
658
|
/** Penghitung instance untuk id unik tiap apps-launcher (hindari bentrok `claimId` saat beberapa `Page` hidup). */
|
|
346
659
|
let pageAppsLauncherInstanceId = 0;
|
|
@@ -353,6 +666,8 @@ class PageComponent {
|
|
|
353
666
|
...(ngDevMode ? [{ debugName: "projectedSide" }] : /* istanbul ignore next */ []));
|
|
354
667
|
projectedHeader = contentChild(PageHeaderComponent, /* @ts-ignore */
|
|
355
668
|
...(ngDevMode ? [{ debugName: "projectedHeader" }] : /* istanbul ignore next */ []));
|
|
669
|
+
projectedFilter = contentChild(PageFilterComponent, /* @ts-ignore */
|
|
670
|
+
...(ngDevMode ? [{ debugName: "projectedFilter" }] : /* istanbul ignore next */ []));
|
|
356
671
|
variant = input(PAGE_DEFAULT_VARIANT, /* @ts-ignore */
|
|
357
672
|
...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
|
|
358
673
|
height = input(PAGE_DEFAULT_HEIGHT, /* @ts-ignore */
|
|
@@ -370,9 +685,13 @@ class PageComponent {
|
|
|
370
685
|
...(ngDevMode ? [{ debugName: "sideOpen" }] : /* istanbul ignore next */ []));
|
|
371
686
|
sideWidth = input(PAGE_DEFAULT_SIDE_WIDTH, /* @ts-ignore */
|
|
372
687
|
...(ngDevMode ? [{ debugName: "sideWidth" }] : /* istanbul ignore next */ []));
|
|
688
|
+
/** Controlled state untuk `PageFilter` drawer/overlay. `null` = uncontrolled (dikelola toggle/backdrop/Esc). */
|
|
689
|
+
filterOpen = input(null, /* @ts-ignore */
|
|
690
|
+
...(ngDevMode ? [{ debugName: "filterOpen" }] : /* istanbul ignore next */ []));
|
|
373
691
|
class = input('', /* @ts-ignore */
|
|
374
692
|
...(ngDevMode ? [{ debugName: "class" }] : /* istanbul ignore next */ []));
|
|
375
693
|
sideOpenChange = output();
|
|
694
|
+
filterOpenChange = output();
|
|
376
695
|
/** Saat layout `empty`, munculkan tombol apps (flyout main navigation) di pojok kanan-atas. Set `false` untuk menonaktifkan. */
|
|
377
696
|
appsLauncher = input(true, { ...(ngDevMode ? { debugName: "appsLauncher" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
378
697
|
/** Id navigasi yang disurface oleh apps-launcher (default `main`). */
|
|
@@ -394,7 +713,25 @@ class PageComponent {
|
|
|
394
713
|
...(ngDevMode ? [{ debugName: "isLeftSide" }] : /* istanbul ignore next */ []));
|
|
395
714
|
isRightSide = computed(() => this.resolvedPosition() === 'right', /* @ts-ignore */
|
|
396
715
|
...(ngDevMode ? [{ debugName: "isRightSide" }] : /* istanbul ignore next */ []));
|
|
397
|
-
|
|
716
|
+
// Filter — resolusi dibaca langsung dari `PageFilter` yang diprojeksikan (reactive content query).
|
|
717
|
+
// Resolusi filter dibaca dari `PageFilter` yang diprojeksikan, lalu fall back ke page-state
|
|
718
|
+
// (diisi `registerFilter`), lalu default — pola seragam agar Page & PageFilter selalu sepakat.
|
|
719
|
+
hasFilter = computed(() => this.projectedFilter() !== undefined, /* @ts-ignore */
|
|
720
|
+
...(ngDevMode ? [{ debugName: "hasFilter" }] : /* istanbul ignore next */ []));
|
|
721
|
+
filterPlacement = computed(() => this.projectedFilter()?.placement() ?? this.page.filterPlacement() ?? PAGE_DEFAULT_FILTER_PLACEMENT, /* @ts-ignore */
|
|
722
|
+
...(ngDevMode ? [{ debugName: "filterPlacement" }] : /* istanbul ignore next */ []));
|
|
723
|
+
/** Mode efektif: `stacked` selalu `sticky`; `side` memakai mode pada `PageFilter`. */
|
|
724
|
+
filterMode = computed(() => this.filterPlacement() === 'stacked' ? 'sticky' : (this.projectedFilter()?.mode() ?? this.page.filterMode()), /* @ts-ignore */
|
|
725
|
+
...(ngDevMode ? [{ debugName: "filterMode" }] : /* istanbul ignore next */ []));
|
|
726
|
+
filterPosition = computed(() => this.projectedFilter()?.position() ?? this.page.filterPosition(), /* @ts-ignore */
|
|
727
|
+
...(ngDevMode ? [{ debugName: "filterPosition" }] : /* istanbul ignore next */ []));
|
|
728
|
+
filterWidthVar = computed(() => this.projectedFilter()?.width() ?? this.page.filterWidth() ?? PAGE_DEFAULT_FILTER_WIDTH, /* @ts-ignore */
|
|
729
|
+
...(ngDevMode ? [{ debugName: "filterWidthVar" }] : /* istanbul ignore next */ []));
|
|
730
|
+
showsSideOverlayBackdrop = computed(() => this.variant() === 'side' && this.resolvedSideMode() === 'overlay' && this.page.sideOpen(), /* @ts-ignore */
|
|
731
|
+
...(ngDevMode ? [{ debugName: "showsSideOverlayBackdrop" }] : /* istanbul ignore next */ []));
|
|
732
|
+
showsFilterOverlayBackdrop = computed(() => this.hasFilter() && this.filterMode() === 'overlay' && this.page.filterOpen(), /* @ts-ignore */
|
|
733
|
+
...(ngDevMode ? [{ debugName: "showsFilterOverlayBackdrop" }] : /* istanbul ignore next */ []));
|
|
734
|
+
showsOverlayBackdrop = computed(() => this.showsSideOverlayBackdrop() || this.showsFilterOverlayBackdrop(), /* @ts-ignore */
|
|
398
735
|
...(ngDevMode ? [{ debugName: "showsOverlayBackdrop" }] : /* istanbul ignore next */ []));
|
|
399
736
|
/** Signal data untuk id yang dipilih; di-recompute hanya saat `appsNavId` berubah. */
|
|
400
737
|
appsNavSource = computed(() => this.navigation?.data(this.appsNavId()) ?? null, /* @ts-ignore */
|
|
@@ -437,6 +774,35 @@ class PageComponent {
|
|
|
437
774
|
return cn('relative min-w-0', this.scroll() === 'content' && 'min-h-0');
|
|
438
775
|
}, /* @ts-ignore */
|
|
439
776
|
...(ngDevMode ? [{ debugName: "bodyClasses" }] : /* istanbul ignore next */ []));
|
|
777
|
+
/**
|
|
778
|
+
* Wrapper di sekitar `PageFilter` + `PageContent`. Tanpa filter, memakai `display: contents`
|
|
779
|
+
* agar transparan (perilaku body lama dipertahankan persis). Dengan filter, menjadi grid/flex
|
|
780
|
+
* yang menata filter (baris untuk `stacked`, kolom untuk `side`); drawer/overlay menjadikannya
|
|
781
|
+
* `relative` sebagai positioning context untuk panel yang mengambang.
|
|
782
|
+
*/
|
|
783
|
+
contentRegionClasses = computed(() => {
|
|
784
|
+
if (!this.hasFilter()) {
|
|
785
|
+
return 'contents';
|
|
786
|
+
}
|
|
787
|
+
const scroll = this.scroll();
|
|
788
|
+
const mode = this.filterMode();
|
|
789
|
+
// drawer/overlay (side only): content mengisi penuh, filter mengambang di atasnya.
|
|
790
|
+
if (mode !== 'sticky') {
|
|
791
|
+
return cn('relative min-w-0', scroll === 'content' && 'min-h-0 h-full');
|
|
792
|
+
}
|
|
793
|
+
if (this.filterPlacement() === 'stacked') {
|
|
794
|
+
// content scroll: filter (auto) di atas, content (1fr) mengisi sisa tinggi & scroll sendiri.
|
|
795
|
+
// page scroll: aliran block biasa; filter dipin via `sticky top-0` pada elemennya.
|
|
796
|
+
return scroll === 'content'
|
|
797
|
+
? 'grid min-w-0 min-h-0 h-full grid-rows-[auto_minmax(0,1fr)]'
|
|
798
|
+
: 'block min-w-0';
|
|
799
|
+
}
|
|
800
|
+
// side + sticky: filter sebagai kolom; posisi kanan dibalik via `order-last` pada elemen filter.
|
|
801
|
+
return cn('grid min-w-0', scroll === 'content' && 'min-h-0 h-full', this.filterPosition() === 'left'
|
|
802
|
+
? 'grid-cols-[var(--page-filter-width)_minmax(0,1fr)]'
|
|
803
|
+
: 'grid-cols-[minmax(0,1fr)_var(--page-filter-width)]');
|
|
804
|
+
}, /* @ts-ignore */
|
|
805
|
+
...(ngDevMode ? [{ debugName: "contentRegionClasses" }] : /* istanbul ignore next */ []));
|
|
440
806
|
constructor() {
|
|
441
807
|
effect(() => {
|
|
442
808
|
this.page.registerRoot({
|
|
@@ -452,6 +818,9 @@ class PageComponent {
|
|
|
452
818
|
effect(() => {
|
|
453
819
|
this.page.setControlledSideOpen(this.sideOpen());
|
|
454
820
|
});
|
|
821
|
+
effect(() => {
|
|
822
|
+
this.page.setControlledFilterOpen(this.filterOpen());
|
|
823
|
+
});
|
|
455
824
|
// Header memesan ruang kanan hanya saat tombol apps benar-benar mengambang di atas header.
|
|
456
825
|
effect(() => {
|
|
457
826
|
this.page.setAppsLauncherReserve(this.showsAppsLauncher() && this.projectedHeader() !== undefined);
|
|
@@ -463,16 +832,28 @@ class PageComponent {
|
|
|
463
832
|
}
|
|
464
833
|
this.sideOpenChange.emit(this.page.sideOpenRequest() ?? false);
|
|
465
834
|
});
|
|
835
|
+
effect(() => {
|
|
836
|
+
const requestVersion = this.page.filterOpenRequestVersion();
|
|
837
|
+
if (requestVersion === 0) {
|
|
838
|
+
return;
|
|
839
|
+
}
|
|
840
|
+
this.filterOpenChange.emit(this.page.filterOpenRequest() ?? false);
|
|
841
|
+
});
|
|
466
842
|
}
|
|
467
843
|
handleBackdropClick() {
|
|
468
|
-
this.
|
|
844
|
+
if (this.showsSideOverlayBackdrop()) {
|
|
845
|
+
this.page.closeSide();
|
|
846
|
+
}
|
|
847
|
+
if (this.showsFilterOverlayBackdrop()) {
|
|
848
|
+
this.page.closeFilter();
|
|
849
|
+
}
|
|
469
850
|
}
|
|
470
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.
|
|
471
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.
|
|
851
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
852
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.4", type: PageComponent, isStandalone: true, selector: "Page", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, scroll: { classPropertyName: "scroll", publicName: "scroll", isSignal: true, isRequired: false, transformFunction: null }, appearance: { classPropertyName: "appearance", publicName: "appearance", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, sideMode: { classPropertyName: "sideMode", publicName: "sideMode", isSignal: true, isRequired: false, transformFunction: null }, sideOpen: { classPropertyName: "sideOpen", publicName: "sideOpen", isSignal: true, isRequired: false, transformFunction: null }, sideWidth: { classPropertyName: "sideWidth", publicName: "sideWidth", isSignal: true, isRequired: false, transformFunction: null }, filterOpen: { classPropertyName: "filterOpen", publicName: "filterOpen", isSignal: true, isRequired: false, transformFunction: null }, class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, appsLauncher: { classPropertyName: "appsLauncher", publicName: "appsLauncher", isSignal: true, isRequired: false, transformFunction: null }, appsNavId: { classPropertyName: "appsNavId", publicName: "appsNavId", isSignal: true, isRequired: false, transformFunction: null }, appsIcon: { classPropertyName: "appsIcon", publicName: "appsIcon", isSignal: true, isRequired: false, transformFunction: null }, appsLabel: { classPropertyName: "appsLabel", publicName: "appsLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sideOpenChange: "sideOpenChange", filterOpenChange: "filterOpenChange" }, host: { properties: { "class": "hostClasses()", "attr.data-page-variant": "variant()", "attr.data-page-height": "height()", "attr.data-page-scroll": "scroll()", "attr.data-page-appearance": "appearance()", "attr.data-page-position": "resolvedPosition()", "attr.data-page-side-mode": "resolvedSideMode()", "attr.data-page-side-open": "page.sideOpen()", "attr.data-page-filter-placement": "hasFilter() ? filterPlacement() : null", "attr.data-page-filter-mode": "hasFilter() ? filterMode() : null", "attr.data-page-filter-open": "hasFilter() ? page.filterOpen() : null", "style.--page-side-width": "sideWidth()", "style.--page-filter-width": "filterWidthVar()" } }, providers: [PageStateService], queries: [{ propertyName: "projectedSide", first: true, predicate: PageSideComponent, descendants: true, isSignal: true }, { propertyName: "projectedHeader", first: true, predicate: PageHeaderComponent, descendants: true, isSignal: true }, { propertyName: "projectedFilter", first: true, predicate: PageFilterComponent, descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
472
853
|
@if (showsOverlayBackdrop()) {
|
|
473
854
|
<button
|
|
474
855
|
type="button"
|
|
475
|
-
aria-label="Close
|
|
856
|
+
aria-label="Close overlay panel"
|
|
476
857
|
data-page-overlay-backdrop
|
|
477
858
|
class="absolute inset-0 z-20 bg-[hsl(var(--overlay-backdrop))]"
|
|
478
859
|
(click)="handleBackdropClick()"></button>
|
|
@@ -480,14 +861,17 @@ class PageComponent {
|
|
|
480
861
|
|
|
481
862
|
<div [class]="shellClasses()">
|
|
482
863
|
<ng-content select="PageHeader" />
|
|
483
|
-
<ng-content select="PageSideToggle" />
|
|
864
|
+
<ng-content select="PageSideToggle, PageFilterToggle" />
|
|
484
865
|
|
|
485
|
-
<div [class]="bodyClasses()">
|
|
866
|
+
<div [class]="bodyClasses()" data-page-body>
|
|
486
867
|
@if (variant() === 'side') {
|
|
487
868
|
<ng-content select="PageSide" />
|
|
488
869
|
}
|
|
489
870
|
|
|
490
|
-
<
|
|
871
|
+
<div [class]="contentRegionClasses()" data-page-content-region>
|
|
872
|
+
<ng-content select="PageFilter" />
|
|
873
|
+
<ng-content select="PageContent, PageDashboard" />
|
|
874
|
+
</div>
|
|
491
875
|
</div>
|
|
492
876
|
|
|
493
877
|
<ng-content select="PageFooter" />
|
|
@@ -520,7 +904,7 @@ class PageComponent {
|
|
|
520
904
|
}
|
|
521
905
|
`, isInline: true, dependencies: [{ kind: "component", type: NavigationContainerComponent, selector: "Navigation", inputs: ["id", "data", "ariaLabel", "compact", "collapse-tree", "class", "itemClass", "nav-group-class", "activeIds", "activeUrl", "openedIds"], outputs: ["openedIdsChange", "itemSelected"] }, { kind: "component", type: NavigationFlyoutComponent, selector: "NavigationFlyout", inputs: ["label", "icon", "icon-only", "icon-position", "trigger-variant", "trigger-floating", "trigger-class", "nav-position", "nav-appearance", "class"] }, { kind: "component", type: NavigationHeaderComponent, selector: "NavigationHeader", inputs: ["toggle", "class"] }, { kind: "component", type: NavigationFooterComponent, selector: "NavigationFooter", inputs: ["class"] }, { kind: "component", type: LayoutBrand, selector: "LayoutBrand", inputs: ["brand", "compact"] }, { kind: "component", type: LayoutUser, selector: "LayoutUser", inputs: ["user", "detailed", "logoutLabel", "logoutIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
522
906
|
}
|
|
523
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.
|
|
907
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PageComponent, decorators: [{
|
|
524
908
|
type: Component,
|
|
525
909
|
args: [{
|
|
526
910
|
selector: 'Page',
|
|
@@ -543,13 +927,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImpor
|
|
|
543
927
|
'[attr.data-page-position]': 'resolvedPosition()',
|
|
544
928
|
'[attr.data-page-side-mode]': 'resolvedSideMode()',
|
|
545
929
|
'[attr.data-page-side-open]': 'page.sideOpen()',
|
|
930
|
+
'[attr.data-page-filter-placement]': 'hasFilter() ? filterPlacement() : null',
|
|
931
|
+
'[attr.data-page-filter-mode]': 'hasFilter() ? filterMode() : null',
|
|
932
|
+
'[attr.data-page-filter-open]': 'hasFilter() ? page.filterOpen() : null',
|
|
546
933
|
'[style.--page-side-width]': 'sideWidth()',
|
|
934
|
+
'[style.--page-filter-width]': 'filterWidthVar()',
|
|
547
935
|
},
|
|
548
936
|
template: `
|
|
549
937
|
@if (showsOverlayBackdrop()) {
|
|
550
938
|
<button
|
|
551
939
|
type="button"
|
|
552
|
-
aria-label="Close
|
|
940
|
+
aria-label="Close overlay panel"
|
|
553
941
|
data-page-overlay-backdrop
|
|
554
942
|
class="absolute inset-0 z-20 bg-[hsl(var(--overlay-backdrop))]"
|
|
555
943
|
(click)="handleBackdropClick()"></button>
|
|
@@ -557,14 +945,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImpor
|
|
|
557
945
|
|
|
558
946
|
<div [class]="shellClasses()">
|
|
559
947
|
<ng-content select="PageHeader" />
|
|
560
|
-
<ng-content select="PageSideToggle" />
|
|
948
|
+
<ng-content select="PageSideToggle, PageFilterToggle" />
|
|
561
949
|
|
|
562
|
-
<div [class]="bodyClasses()">
|
|
950
|
+
<div [class]="bodyClasses()" data-page-body>
|
|
563
951
|
@if (variant() === 'side') {
|
|
564
952
|
<ng-content select="PageSide" />
|
|
565
953
|
}
|
|
566
954
|
|
|
567
|
-
<
|
|
955
|
+
<div [class]="contentRegionClasses()" data-page-content-region>
|
|
956
|
+
<ng-content select="PageFilter" />
|
|
957
|
+
<ng-content select="PageContent, PageDashboard" />
|
|
958
|
+
</div>
|
|
568
959
|
</div>
|
|
569
960
|
|
|
570
961
|
<ng-content select="PageFooter" />
|
|
@@ -597,7 +988,84 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImpor
|
|
|
597
988
|
}
|
|
598
989
|
`,
|
|
599
990
|
}]
|
|
600
|
-
}], ctorParameters: () => [], propDecorators: { projectedSide: [{ type: i0.ContentChild, args: [i0.forwardRef(() => PageSideComponent), { isSignal: true }] }], projectedHeader: [{ type: i0.ContentChild, args: [i0.forwardRef(() => PageHeaderComponent), { isSignal: true }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], scroll: [{ type: i0.Input, args: [{ isSignal: true, alias: "scroll", required: false }] }], appearance: [{ type: i0.Input, args: [{ isSignal: true, alias: "appearance", required: false }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }], sideMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "sideMode", required: false }] }], sideOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "sideOpen", required: false }] }], sideWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "sideWidth", required: false }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], sideOpenChange: [{ type: i0.Output, args: ["sideOpenChange"] }], appsLauncher: [{ type: i0.Input, args: [{ isSignal: true, alias: "appsLauncher", required: false }] }], appsNavId: [{ type: i0.Input, args: [{ isSignal: true, alias: "appsNavId", required: false }] }], appsIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "appsIcon", required: false }] }], appsLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "appsLabel", required: false }] }] } });
|
|
991
|
+
}], ctorParameters: () => [], propDecorators: { projectedSide: [{ type: i0.ContentChild, args: [i0.forwardRef(() => PageSideComponent), { isSignal: true }] }], projectedHeader: [{ type: i0.ContentChild, args: [i0.forwardRef(() => PageHeaderComponent), { isSignal: true }] }], projectedFilter: [{ type: i0.ContentChild, args: [i0.forwardRef(() => PageFilterComponent), { isSignal: true }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], scroll: [{ type: i0.Input, args: [{ isSignal: true, alias: "scroll", required: false }] }], appearance: [{ type: i0.Input, args: [{ isSignal: true, alias: "appearance", required: false }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }], sideMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "sideMode", required: false }] }], sideOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "sideOpen", required: false }] }], sideWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "sideWidth", required: false }] }], filterOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterOpen", required: false }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], sideOpenChange: [{ type: i0.Output, args: ["sideOpenChange"] }], filterOpenChange: [{ type: i0.Output, args: ["filterOpenChange"] }], appsLauncher: [{ type: i0.Input, args: [{ isSignal: true, alias: "appsLauncher", required: false }] }], appsNavId: [{ type: i0.Input, args: [{ isSignal: true, alias: "appsNavId", required: false }] }], appsIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "appsIcon", required: false }] }], appsLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "appsLabel", required: false }] }] } });
|
|
992
|
+
|
|
993
|
+
/**
|
|
994
|
+
* `PageFilterToggle` — tombol untuk membuka/menutup `PageFilter` dalam mode `drawer`/`overlay`.
|
|
995
|
+
* Menerima projected content untuk label/ikon kustom; fallback memakai ikon funnel bawaan.
|
|
996
|
+
*/
|
|
997
|
+
class PageFilterToggleComponent {
|
|
998
|
+
page = inject(PageStateService);
|
|
999
|
+
ariaLabel = input('Toggle page filter', /* @ts-ignore */
|
|
1000
|
+
...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
|
|
1001
|
+
class = input('', /* @ts-ignore */
|
|
1002
|
+
...(ngDevMode ? [{ debugName: "class" }] : /* istanbul ignore next */ []));
|
|
1003
|
+
toggled = output();
|
|
1004
|
+
hostClasses = computed(() => cn('inline-flex shrink-0', this.class()), /* @ts-ignore */
|
|
1005
|
+
...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
|
|
1006
|
+
buttonClasses = computed(() => cn('inline-flex h-9 min-w-9 items-center justify-center rounded-md border border-border bg-background px-3 text-sm text-foreground transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2'), /* @ts-ignore */
|
|
1007
|
+
...(ngDevMode ? [{ debugName: "buttonClasses" }] : /* istanbul ignore next */ []));
|
|
1008
|
+
handleClick() {
|
|
1009
|
+
this.toggled.emit(this.page.toggleFilter());
|
|
1010
|
+
}
|
|
1011
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PageFilterToggleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1012
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.4", type: PageFilterToggleComponent, isStandalone: true, selector: "PageFilterToggle", inputs: { ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { toggled: "toggled" }, host: { properties: { "class": "hostClasses()" } }, ngImport: i0, template: `
|
|
1013
|
+
<button
|
|
1014
|
+
type="button"
|
|
1015
|
+
[class]="buttonClasses()"
|
|
1016
|
+
[attr.aria-label]="ariaLabel()"
|
|
1017
|
+
[attr.aria-controls]="page.filterId()"
|
|
1018
|
+
[attr.aria-expanded]="page.filterOpen()"
|
|
1019
|
+
(click)="handleClick()">
|
|
1020
|
+
<ng-content>
|
|
1021
|
+
<svg
|
|
1022
|
+
aria-hidden="true"
|
|
1023
|
+
viewBox="0 0 24 24"
|
|
1024
|
+
class="h-4 w-4"
|
|
1025
|
+
fill="none"
|
|
1026
|
+
stroke="currentColor"
|
|
1027
|
+
stroke-width="2"
|
|
1028
|
+
stroke-linecap="round"
|
|
1029
|
+
stroke-linejoin="round">
|
|
1030
|
+
<path d="M3 5h18l-7 8v5l-4 2v-7z" />
|
|
1031
|
+
</svg>
|
|
1032
|
+
</ng-content>
|
|
1033
|
+
</button>
|
|
1034
|
+
`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1035
|
+
}
|
|
1036
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PageFilterToggleComponent, decorators: [{
|
|
1037
|
+
type: Component,
|
|
1038
|
+
args: [{
|
|
1039
|
+
selector: 'PageFilterToggle',
|
|
1040
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
1041
|
+
host: {
|
|
1042
|
+
'[class]': 'hostClasses()',
|
|
1043
|
+
},
|
|
1044
|
+
template: `
|
|
1045
|
+
<button
|
|
1046
|
+
type="button"
|
|
1047
|
+
[class]="buttonClasses()"
|
|
1048
|
+
[attr.aria-label]="ariaLabel()"
|
|
1049
|
+
[attr.aria-controls]="page.filterId()"
|
|
1050
|
+
[attr.aria-expanded]="page.filterOpen()"
|
|
1051
|
+
(click)="handleClick()">
|
|
1052
|
+
<ng-content>
|
|
1053
|
+
<svg
|
|
1054
|
+
aria-hidden="true"
|
|
1055
|
+
viewBox="0 0 24 24"
|
|
1056
|
+
class="h-4 w-4"
|
|
1057
|
+
fill="none"
|
|
1058
|
+
stroke="currentColor"
|
|
1059
|
+
stroke-width="2"
|
|
1060
|
+
stroke-linecap="round"
|
|
1061
|
+
stroke-linejoin="round">
|
|
1062
|
+
<path d="M3 5h18l-7 8v5l-4 2v-7z" />
|
|
1063
|
+
</svg>
|
|
1064
|
+
</ng-content>
|
|
1065
|
+
</button>
|
|
1066
|
+
`,
|
|
1067
|
+
}]
|
|
1068
|
+
}], propDecorators: { ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], toggled: [{ type: i0.Output, args: ["toggled"] }] } });
|
|
601
1069
|
|
|
602
1070
|
class PageSideToggleComponent {
|
|
603
1071
|
page = inject(PageStateService);
|
|
@@ -613,8 +1081,8 @@ class PageSideToggleComponent {
|
|
|
613
1081
|
handleClick() {
|
|
614
1082
|
this.toggled.emit(this.page.toggleSide());
|
|
615
1083
|
}
|
|
616
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.
|
|
617
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.
|
|
1084
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PageSideToggleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1085
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.4", type: PageSideToggleComponent, isStandalone: true, selector: "PageSideToggle", inputs: { ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { toggled: "toggled" }, host: { properties: { "class": "hostClasses()" } }, ngImport: i0, template: `
|
|
618
1086
|
<button
|
|
619
1087
|
type="button"
|
|
620
1088
|
[class]="buttonClasses()"
|
|
@@ -628,7 +1096,7 @@ class PageSideToggleComponent {
|
|
|
628
1096
|
</button>
|
|
629
1097
|
`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
630
1098
|
}
|
|
631
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.
|
|
1099
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PageSideToggleComponent, decorators: [{
|
|
632
1100
|
type: Component,
|
|
633
1101
|
args: [{
|
|
634
1102
|
selector: 'PageSideToggle',
|
|
@@ -656,4 +1124,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImpor
|
|
|
656
1124
|
* Generated bundle index. Do not edit.
|
|
657
1125
|
*/
|
|
658
1126
|
|
|
659
|
-
export { PAGE_APPEARANCES, PAGE_DEFAULT_APPEARANCE, PAGE_DEFAULT_HEIGHT, PAGE_DEFAULT_SCROLL, PAGE_DEFAULT_SIDE_MODE, PAGE_DEFAULT_SIDE_POSITION, PAGE_DEFAULT_SIDE_WIDTH, PAGE_DEFAULT_VARIANT, PAGE_HEIGHT_VALUES, PAGE_SCROLL_VALUES, PAGE_SIDE_MODES, PAGE_SIDE_POSITIONS, PAGE_VARIANTS, PageComponent, PageContentComponent, PageDashboardComponent, PageFooterComponent, PageHeaderComponent, PageSideComponent, PageSideToggleComponent, isUiPageAppearance, isUiPageHeight, isUiPageScroll, isUiPageSideMode, isUiPageSidePosition, isUiPageVariant };
|
|
1127
|
+
export { PAGE_APPEARANCES, PAGE_DEFAULT_APPEARANCE, PAGE_DEFAULT_FILTER_MODE, PAGE_DEFAULT_FILTER_PLACEMENT, PAGE_DEFAULT_FILTER_POSITION, PAGE_DEFAULT_FILTER_WIDTH, PAGE_DEFAULT_HEIGHT, PAGE_DEFAULT_SCROLL, PAGE_DEFAULT_SIDE_MODE, PAGE_DEFAULT_SIDE_POSITION, PAGE_DEFAULT_SIDE_WIDTH, PAGE_DEFAULT_VARIANT, PAGE_FILTER_PLACEMENTS, PAGE_HEIGHT_VALUES, PAGE_SCROLL_VALUES, PAGE_SIDE_MODES, PAGE_SIDE_POSITIONS, PAGE_VARIANTS, PageComponent, PageContentComponent, PageDashboardComponent, PageFilterComponent, PageFilterToggleComponent, PageFooterComponent, PageHeaderComponent, PageSideComponent, PageSideToggleComponent, isUiPageAppearance, isUiPageFilterPlacement, isUiPageHeight, isUiPageScroll, isUiPageSideMode, isUiPageSidePosition, isUiPageVariant };
|