@ojiepermana/angular-theme 22.0.41 → 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.
@@ -198,10 +198,10 @@ class ThemeModeService {
198
198
  return;
199
199
  }
200
200
  }
201
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: ThemeModeService, deps: [], target: i0.ɵɵFactoryTarget.Service });
202
- static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.0.3", ngImport: i0, type: ThemeModeService });
201
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: ThemeModeService, deps: [], target: i0.ɵɵFactoryTarget.Service });
202
+ static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.0.4", ngImport: i0, type: ThemeModeService });
203
203
  }
204
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: ThemeModeService, decorators: [{
204
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: ThemeModeService, decorators: [{
205
205
  type: Service
206
206
  }], ctorParameters: () => [] });
207
207
 
@@ -312,10 +312,10 @@ class ThemeBrandService {
312
312
  return;
313
313
  }
314
314
  }
315
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: ThemeBrandService, deps: [], target: i0.ɵɵFactoryTarget.Service });
316
- static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.0.3", ngImport: i0, type: ThemeBrandService });
315
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: ThemeBrandService, deps: [], target: i0.ɵɵFactoryTarget.Service });
316
+ static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.0.4", ngImport: i0, type: ThemeBrandService });
317
317
  }
318
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: ThemeBrandService, decorators: [{
318
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: ThemeBrandService, decorators: [{
319
319
  type: Service
320
320
  }], ctorParameters: () => [] });
321
321
 
@@ -394,10 +394,10 @@ class ThemeColorService {
394
394
  return;
395
395
  }
396
396
  }
397
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: ThemeColorService, deps: [], target: i0.ɵɵFactoryTarget.Service });
398
- static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.0.3", ngImport: i0, type: ThemeColorService });
397
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: ThemeColorService, deps: [], target: i0.ɵɵFactoryTarget.Service });
398
+ static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.0.4", ngImport: i0, type: ThemeColorService });
399
399
  }
400
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: ThemeColorService, decorators: [{
400
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: ThemeColorService, decorators: [{
401
401
  type: Service
402
402
  }], ctorParameters: () => [] });
403
403
 
@@ -472,10 +472,10 @@ class ThemeRadiusService {
472
472
  return;
473
473
  }
474
474
  }
475
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: ThemeRadiusService, deps: [], target: i0.ɵɵFactoryTarget.Service });
476
- static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.0.3", ngImport: i0, type: ThemeRadiusService });
475
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: ThemeRadiusService, deps: [], target: i0.ɵɵFactoryTarget.Service });
476
+ static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.0.4", ngImport: i0, type: ThemeRadiusService });
477
477
  }
478
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: ThemeRadiusService, decorators: [{
478
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: ThemeRadiusService, decorators: [{
479
479
  type: Service
480
480
  }], ctorParameters: () => [] });
481
481
 
@@ -532,10 +532,10 @@ class ThemeSpaceService {
532
532
  return;
533
533
  }
534
534
  }
535
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: ThemeSpaceService, deps: [], target: i0.ɵɵFactoryTarget.Service });
536
- static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.0.3", ngImport: i0, type: ThemeSpaceService });
535
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: ThemeSpaceService, deps: [], target: i0.ɵɵFactoryTarget.Service });
536
+ static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.0.4", ngImport: i0, type: ThemeSpaceService });
537
537
  }
538
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: ThemeSpaceService, decorators: [{
538
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: ThemeSpaceService, decorators: [{
539
539
  type: Service
540
540
  }], ctorParameters: () => [] });
541
541
 
package/layout/README.md CHANGED
@@ -324,8 +324,11 @@ Nilai `surface` yang tersedia berasal dari `LAYOUT_SURFACES`.
324
324
  | Nilai | Efek visual |
325
325
  | ------------------- | -------------------------------------------------------------------------- |
326
326
  | `'flat'` | Background polos `bg-background`. |
327
- | `'grid'` | Pola grid tipis dua arah. |
328
- | `'honeycome'` | Pola radial rapat. Nama API dieja `honeycome` agar sesuai export saat ini. |
327
+ | `'grid'` | Pola grid tipis dua arah; warna garis (`--layout-grid-color`) mengikuti Accent dengan kecerahan tetap (fallback neutral). |
328
+ | `'grid-line'` | Mosaik kotak: grid tipis + kotak highlight menyala. Mengikuti warna Accent (hue dari `--accent-foreground`, kecerahan dikunci `--layout-grid-highlight-l`), invert light/dark. Memaksa sudut frame kotak (`rounded-none`, seperti `border-rail`) + border frame warna Accent. Pada appearance `border-rail`, garis rail + border frame (tebal `--layout-rail-width` = 1.21px) ikut warna Accent (`--layout-rail-color`) dengan kecerahan dinaikkan (`--layout-rail-opacity`), bukan `--border` yang soft. |
329
+ | `'honeycomb'` | Pola heksagon honeycomb tipis (outline), token-colored mengikuti tema. |
330
+ | `'matrix'` | Field biner (0/1) acak tipis, tinted brand `--primary`, recolor mengikuti tema. |
331
+ | `'circuit'` | Tekstur PCB / papan sirkuit yang ter-tile: jalur (trace) berbelok 45°, node via, dan pad cincin konsentris. Dua lapisan mask — trace tembaga netral (`--layout-grid-line`) + node aksen brand (`--primary`) — jadi papannya grayscale tapi node-nya menyala mengikuti tema; recolor light/dark. |
329
332
  | `'line-vertical'` | Garis vertikal berulang. |
330
333
  | `'line-horizontal'` | Garis horizontal berulang. |
331
334
 
@@ -431,27 +434,27 @@ Seluruh symbol berikut diekspor dari `@ojiepermana/angular-theme/layout/types`.
431
434
  | Symbol | Nilai |
432
435
  | -------------------- | -------------------------------------------------------------------------------------------------- |
433
436
  | `LayoutType` | `vertical`, `horizontal`, `empty`, `fluid` |
434
- | `LayoutSurface` | `flat`, `grid`, `honeycome`, `line-vertical`, `line-horizontal` |
437
+ | `LayoutSurface` | `flat`, `grid`, `grid-line`, `honeycomb`, `matrix`, `circuit`, `line-vertical`, `line-horizontal` |
435
438
  | `LayoutAppearance` | `flat`, `border-rail` |
436
439
  | `LayoutWidth` | `full`, `wide`, `container`, `fluid` |
437
440
  | `LayoutContextValue` | Objekt berbentuk `{ surface, type, appearance, width }` yang masing-masing berupa readonly signal. |
438
441
 
439
442
  ### Constants
440
443
 
441
- | Symbol | Nilai |
442
- | ------------------------------- | ------------------------------------------------------------------- |
443
- | `LAYOUT_TYPES` | `['vertical', 'horizontal', 'empty', 'fluid']` |
444
- | `LAYOUT_SURFACES` | `['flat', 'grid', 'honeycome', 'line-vertical', 'line-horizontal']` |
445
- | `LAYOUT_APPEARANCES` | `['flat', 'border-rail']` |
446
- | `LAYOUT_WIDTHS` | `['full', 'wide', 'container', 'fluid']` |
447
- | `LAYOUT_DEFAULT_SURFACE` | `'flat'` |
448
- | `LAYOUT_DEFAULT_TYPE` | `'vertical'` |
449
- | `LAYOUT_DEFAULT_APPEARANCE` | `'flat'` |
450
- | `LAYOUT_DEFAULT_WIDTH` | `'full'` |
451
- | `LAYOUT_SURFACE_STORAGE_KEY` | `'layout-surface'` |
452
- | `LAYOUT_APPEARANCE_STORAGE_KEY` | `'layout-appearance'` |
453
- | `LAYOUT_TYPE_STORAGE_KEY` | `'layout-type'` |
454
- | `LAYOUT_WIDTH_STORAGE_KEY` | `'layout-width'` |
444
+ | Symbol | Nilai |
445
+ | ------------------------------- | ------------------------------------------------------------------------------------------ |
446
+ | `LAYOUT_TYPES` | `['vertical', 'horizontal', 'empty', 'fluid']` |
447
+ | `LAYOUT_SURFACES` | `['flat', 'grid', 'grid-line', 'honeycomb', 'matrix', 'circuit', 'line-vertical', 'line-horizontal']` |
448
+ | `LAYOUT_APPEARANCES` | `['flat', 'border-rail']` |
449
+ | `LAYOUT_WIDTHS` | `['full', 'wide', 'container', 'fluid']` |
450
+ | `LAYOUT_DEFAULT_SURFACE` | `'flat'` |
451
+ | `LAYOUT_DEFAULT_TYPE` | `'vertical'` |
452
+ | `LAYOUT_DEFAULT_APPEARANCE` | `'flat'` |
453
+ | `LAYOUT_DEFAULT_WIDTH` | `'full'` |
454
+ | `LAYOUT_SURFACE_STORAGE_KEY` | `'layout-surface'` |
455
+ | `LAYOUT_APPEARANCE_STORAGE_KEY` | `'layout-appearance'` |
456
+ | `LAYOUT_TYPE_STORAGE_KEY` | `'layout-type'` |
457
+ | `LAYOUT_WIDTH_STORAGE_KEY` | `'layout-width'` |
455
458
 
456
459
  ### Guards
457
460
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ojiepermana/angular-theme",
3
- "version": "22.0.41",
3
+ "version": "22.0.44",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/edsis/angular.git"
@@ -13,8 +13,8 @@
13
13
  "@angular/common": ">=22.0.0",
14
14
  "@angular/core": ">=22.0.0",
15
15
  "@angular/router": ">=22.0.0",
16
- "@ojiepermana/angular-navigation": "^22.0.41",
17
- "@ojiepermana/angular-component": "^22.0.41",
16
+ "@ojiepermana/angular-navigation": "^22.0.44",
17
+ "@ojiepermana/angular-component": "^22.0.44",
18
18
  "rxjs": ">=7.8.0"
19
19
  },
20
20
  "dependencies": {
package/page/README.md CHANGED
@@ -13,9 +13,12 @@ Dokumentasi ini mengikuti API publik terbaru dari `@ojiepermana/angular-theme/pa
13
13
  - `PageFooter`
14
14
  - `PageSide`
15
15
  - `PageSideToggle`
16
+ - `PageFilter`
17
+ - `PageFilterToggle`
16
18
 
17
19
  `PageSideToggle` menerima projected content untuk label atau ikon kustom. Jika tidak ada projected content, komponen akan memakai ikon fallback bawaan.
18
20
  `PageDashboard` mengikuti perilaku layout dan scroll yang sama dengan `PageContent`, tetapi disediakan sebagai slot semantik untuk dashboard, board, dan surface analitik.
21
+ `PageFilter` adalah slot filter yang bisa diletakkan **stacked** (bar di antara `PageHeader` dan `PageContent`) atau **side** (kolom di samping `PageContent`). `PageFilterToggle` mengontrol `PageFilter` saat memakai mode `drawer`/`overlay`.
19
22
 
20
23
  ## Root API
21
24
 
@@ -31,13 +34,15 @@ Dokumentasi ini mengikuti API publik terbaru dari `@ojiepermana/angular-theme/pa
31
34
  | `sideMode` | `'sticky' \| 'drawer' \| 'overlay'` | `'sticky'` | Mode default untuk `PageSide` jika side tidak memberi override sendiri. |
32
35
  | `sideWidth` | `string` | `'16rem'` | Lebar default side rail; dipakai juga untuk drawer dan overlay. |
33
36
  | `sideOpen` | `boolean \| null` | `null` | Saat `null`, state side tidak dikontrol dari parent. Saat `true/false`, komponen masuk controlled mode. |
37
+ | `filterOpen` | `boolean \| null` | `null` | Controlled state untuk `PageFilter` drawer/overlay. `null` = uncontrolled (dikelola toggle/backdrop/Esc). |
34
38
  | `class` | `string` | `''` | Class tambahan pada host `Page`. |
35
39
 
36
- | Output | Payload | Keterangan |
37
- | ---------------- | --------- | -------------------------------------------------------------------------------------------- |
38
- | `sideOpenChange` | `boolean` | Emit setiap ada permintaan buka/tutup side, baik dari toggle, backdrop, maupun tombol `Esc`. |
40
+ | Output | Payload | Keterangan |
41
+ | ------------------ | --------- | ------------------------------------------------------------------------------------------------- |
42
+ | `sideOpenChange` | `boolean` | Emit setiap ada permintaan buka/tutup side, baik dari toggle, backdrop, maupun tombol `Esc`. |
43
+ | `filterOpenChange` | `boolean` | Emit setiap ada permintaan buka/tutup filter (drawer/overlay), baik dari toggle, backdrop, `Esc`. |
39
44
 
40
- `Page` juga menambahkan atribut host `data-page-variant`, `data-page-height`, `data-page-scroll`, `data-page-appearance`, `data-page-position`, `data-page-side-mode`, dan `data-page-side-open` untuk styling atau inspeksi.
45
+ `Page` juga menambahkan atribut host `data-page-variant`, `data-page-height`, `data-page-scroll`, `data-page-appearance`, `data-page-position`, `data-page-side-mode`, dan `data-page-side-open` untuk styling atau inspeksi. Saat `PageFilter` aktif, ditambahkan pula `data-page-filter-placement`, `data-page-filter-mode`, dan `data-page-filter-open`.
41
46
 
42
47
  ### Apps launcher (layout `empty`)
43
48
 
@@ -77,13 +82,14 @@ Tombol hanya muncul ketika layout aktif `empty` dan nav target punya data. Di la
77
82
 
78
83
  ### `PageSide`
79
84
 
80
- | Input | Type | Default | Keterangan |
81
- | ------------ | ----------------------------------- | ---------- | ----------------------------------------------------------------------------------------------- |
82
- | `mode` | `'sticky' \| 'drawer' \| 'overlay'` | `'sticky'` | Override mode side untuk instance tersebut. |
83
- | `position` | `'left' \| 'right' \| null` | `null` | Override posisi side untuk instance tersebut. Jika `null`, memakai nilai dari root `Page`. |
84
- | `width` | `string \| null` | `null` | Override lebar side untuk instance tersebut. Jika `null`, memakai `sideWidth` dari root `Page`. |
85
- | `closeOnEsc` | `boolean` | `true` | Berlaku untuk `drawer` dan `overlay`; menutup side ketika tombol `Esc` ditekan. |
86
- | `class` | `string` | `''` | Class tambahan pada host `PageSide`. |
85
+ | Input | Type | Default | Keterangan |
86
+ | ------------ | ----------------------------------- | -------------- | ------------------------------------------------------------------------------------------------------ |
87
+ | `mode` | `'sticky' \| 'drawer' \| 'overlay'` | `'sticky'` | Override mode side untuk instance tersebut. |
88
+ | `position` | `'left' \| 'right' \| null` | `null` | Override posisi side untuk instance tersebut. Jika `null`, memakai nilai dari root `Page`. |
89
+ | `width` | `string \| null` | `null` | Override lebar side untuk instance tersebut. Jika `null`, memakai `sideWidth` dari root `Page`. |
90
+ | `closeOnEsc` | `boolean` | `true` | Berlaku untuk `drawer` dan `overlay`; menutup side ketika tombol `Esc` ditekan. |
91
+ | `ariaLabel` | `string` | `'Side panel'` | Nama aksesibilitas panel saat `drawer`/`overlay` (dipasang sebagai `aria-label` pada `role="dialog"`). |
92
+ | `class` | `string` | `''` | Class tambahan pada host `PageSide`. |
87
93
 
88
94
  ### `PageSideToggle`
89
95
 
@@ -93,6 +99,31 @@ Tombol hanya muncul ketika layout aktif `empty` dan nav target punya data. Di la
93
99
  | `class` | `string` | `''` | Class tambahan pada host toggle. |
94
100
  | `toggled` | `boolean` | - | Emit nilai open state terbaru setelah tombol toggle diklik. |
95
101
 
102
+ ### `PageFilter`
103
+
104
+ | Input | Type | Default | Keterangan |
105
+ | ------------- | ----------------------------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------- |
106
+ | `placement` | `'stacked' \| 'side'` | `'stacked'` | `stacked` = bar di antara `PageHeader` dan `PageContent`; `side` = kolom di samping `PageContent`. |
107
+ | `mode` | `'sticky' \| 'drawer' \| 'overlay'` | `'sticky'` | Hanya berlaku untuk `placement="side"`. `placement="stacked"` selalu `sticky`. |
108
+ | `position` | `'left' \| 'right' \| null` | `null` | Posisi kolom saat `side`. Jika `null`, memakai default (`left`). |
109
+ | `width` | `string \| null` | `null` | Lebar kolom saat `side`. Jika `null`, memakai `18rem`. |
110
+ | `closeOnEsc` | `boolean` | `true` | Berlaku untuk `drawer`/`overlay`; menutup filter ketika tombol `Esc` ditekan. |
111
+ | `collapsible` | `boolean` | `false` | Khusus `placement="stacked"`: bar bisa dibuka/tutup lewat `PageFilterToggle`, default tertutup. Diabaikan untuk `side`. |
112
+ | `ariaLabel` | `string` | `'Filters'` | Nama aksesibilitas panel saat `drawer`/`overlay` (dipasang sebagai `aria-label` pada `role="dialog"`). |
113
+ | `class` | `string` | `''` | Class tambahan pada host `PageFilter`. |
114
+
115
+ `PageFilter` menambahkan atribut host `data-page-slot="filter"`, `data-page-filter-placement`, `data-page-filter-mode`, `data-page-filter-open`, dan `data-page-position`.
116
+
117
+ Aksesibilitas: pada mode `drawer`/`overlay`, panel menjadi `role="dialog"` (dengan `aria-modal` untuk `overlay`), fokus dipindahkan ke panel saat terbuka dan dikembalikan ke pemicu saat tertutup, dan saat tertutup panel diberi `inert` sehingga isinya keluar dari tab order. `PageSide` memakai pola yang sama. Menekan `Esc` (atau mengklik backdrop overlay) menutup semua panel mengambang yang sedang terbuka.
118
+
119
+ ### `PageFilterToggle`
120
+
121
+ | Input / Output | Type | Default | Keterangan |
122
+ | -------------- | --------- | ---------------------- | ----------------------------------------------------------- |
123
+ | `ariaLabel` | `string` | `'Toggle page filter'` | Label aksesibilitas untuk tombol toggle filter. |
124
+ | `class` | `string` | `''` | Class tambahan pada host toggle. |
125
+ | `toggled` | `boolean` | - | Emit nilai open state terbaru setelah tombol toggle diklik. |
126
+
96
127
  ## Variants
97
128
 
98
129
  - `stacked`
@@ -130,6 +161,15 @@ Saat memakai `height="fix"`, slot berikut tidak boleh melebihi tinggi tetap yang
130
161
  - `drawer`
131
162
  - `overlay`
132
163
 
164
+ ## Filter Placement
165
+
166
+ `PageFilter` punya dua placement yang orthogonal terhadap `variant` `Page`:
167
+
168
+ - `stacked` (default): filter menjadi bar selebar content di antara `PageHeader` dan `PageContent`. Filter mendorong content ke bawah; pada `scroll="content"`, content mengisi tinggi sisa dan scroll sendiri (filter `shrink-0`). Pada `scroll="page"`, bar dipin (`sticky top-0`) agar tetap terjangkau saat page scroll.
169
+ - `side`: filter menjadi kolom di samping content. Mode `sticky` mendorong content ke samping (push); mode `drawer`/`overlay` mengambang di atas content dan dikontrol via `PageFilterToggle`, backdrop, atau tombol `Esc`.
170
+
171
+ Karena independen dari `variant`, side filter bisa hidup berdampingan dengan `PageSide` — `PageSide` tetap menjadi rail terluar dan `PageFilter` menjadi kolom yang lebih dekat ke content (`[PageSide | PageFilter | PageContent]`).
172
+
133
173
  ## Examples
134
174
 
135
175
  ### Stacked with content scroll
@@ -275,3 +315,109 @@ Saat memakai `height="fix"`, slot berikut tidak boleh melebihi tinggi tetap yang
275
315
  </PageDashboard>
276
316
  </Page>
277
317
  ```
318
+
319
+ ### Stacked filter (bar antara header dan content)
320
+
321
+ ```html
322
+ <Page variant="stacked">
323
+ <PageHeader>
324
+ <h1>Orders</h1>
325
+ </PageHeader>
326
+
327
+ <PageFilter placement="stacked">
328
+ <input type="search" aria-label="Cari order" placeholder="Cari…" />
329
+ <button type="button">Status</button>
330
+ <button type="button">Tanggal</button>
331
+ </PageFilter>
332
+
333
+ <PageContent>
334
+ <p>Content mengisi tinggi sisa dan scroll sendiri di bawah bar filter.</p>
335
+ </PageContent>
336
+ </Page>
337
+ ```
338
+
339
+ ### Collapsible stacked filter
340
+
341
+ ```html
342
+ <Page variant="stacked">
343
+ <PageHeader>
344
+ <PageFilterToggle ariaLabel="Toggle filters">
345
+ <span>Filters</span>
346
+ </PageFilterToggle>
347
+ <h1>Orders</h1>
348
+ </PageHeader>
349
+
350
+ <!-- Default tertutup; toggle membuka/menutup bar. -->
351
+ <PageFilter placement="stacked" collapsible>
352
+ <input type="search" aria-label="Cari order" placeholder="Cari…" />
353
+ <button type="button">Status</button>
354
+ </PageFilter>
355
+
356
+ <PageContent>
357
+ <p>Saat filter tertutup, content mengisi kembali ruang bar.</p>
358
+ </PageContent>
359
+ </Page>
360
+ ```
361
+
362
+ ### Side filter (sticky push)
363
+
364
+ ```html
365
+ <Page variant="stacked">
366
+ <PageHeader>
367
+ <h1>Catalog</h1>
368
+ </PageHeader>
369
+
370
+ <PageFilter placement="side" mode="sticky" position="left" width="18rem">
371
+ <p>Facet filter; mendorong content ke samping.</p>
372
+ </PageFilter>
373
+
374
+ <PageContent>
375
+ <p>Hasil produk.</p>
376
+ </PageContent>
377
+ </Page>
378
+ ```
379
+
380
+ ### Side filter (controlled drawer)
381
+
382
+ ```html
383
+ <Page [filterOpen]="filterOpen()" (filterOpenChange)="filterOpen.set($event)">
384
+ <PageHeader>
385
+ <PageFilterToggle ariaLabel="Toggle filter">
386
+ <span>Filter</span>
387
+ </PageFilterToggle>
388
+ <h1>Catalog</h1>
389
+ </PageHeader>
390
+
391
+ <PageFilter placement="side" mode="drawer" position="right">
392
+ <p>Drawer filter mengikuti state dari parent.</p>
393
+ </PageFilter>
394
+
395
+ <PageContent>
396
+ <p>Hasil produk.</p>
397
+ </PageContent>
398
+ </Page>
399
+ ```
400
+
401
+ ### Filter dan side rail berdampingan
402
+
403
+ `PageSide` (rail terluar) dan `PageFilter` (`placement="side"`) bisa aktif bersamaan: `[PageSide | PageFilter | PageContent]`.
404
+
405
+ ```html
406
+ <Page variant="side" position="left">
407
+ <PageHeader>
408
+ <h1>Workspace</h1>
409
+ </PageHeader>
410
+
411
+ <PageSide mode="sticky">
412
+ <p>Navigasi.</p>
413
+ </PageSide>
414
+
415
+ <PageFilter placement="side" position="left">
416
+ <p>Filter.</p>
417
+ </PageFilter>
418
+
419
+ <PageContent>
420
+ <p>Content.</p>
421
+ </PageContent>
422
+ </Page>
423
+ ```