@ojiepermana/angular 0.0.3 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +55 -9
- package/fesm2022/ojiepermana-angular-internal.mjs +433 -2
- package/fesm2022/ojiepermana-angular-internal.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-layout.mjs +52 -59
- package/fesm2022/ojiepermana-angular-layout.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-navigation-horizontal.mjs +721 -0
- package/fesm2022/ojiepermana-angular-navigation-horizontal.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-navigation-vertical.mjs +1647 -0
- package/fesm2022/ojiepermana-angular-navigation-vertical.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-navigation.mjs +472 -0
- package/fesm2022/ojiepermana-angular-navigation.mjs.map +1 -0
- package/fesm2022/ojiepermana-angular-shell.mjs +6 -1
- package/fesm2022/ojiepermana-angular-shell.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-theme-component.mjs +12 -26
- package/fesm2022/ojiepermana-angular-theme-component.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular-theme-service.mjs +2 -6
- package/fesm2022/ojiepermana-angular-theme-service.mjs.map +1 -1
- package/fesm2022/ojiepermana-angular.mjs.map +1 -1
- package/layout/README.md +3 -3
- package/{theme/styles/layout → layout/src/component/horizontal}/horizontal.css +39 -26
- package/{theme/styles/layout → layout/src/component/vertical}/vertical.css +10 -12
- package/{theme/styles/layout/index.css → layout/src/layout.css} +0 -3
- package/navigation/README.md +301 -0
- package/navigation/horizontal/README.md +49 -0
- package/navigation/vertical/README.md +0 -0
- package/package.json +13 -1
- package/shell/README.md +5 -1
- package/styles/index.css +1 -1
- package/theme/README.md +3 -6
- package/theme/styles/adapters/material-ui/index.css +1 -5
- package/theme/styles/presets/styles/flat.css +3 -6
- package/theme/styles/presets/styles/glass.css +1 -7
- package/theme/styles/presets/styles/index.css +1 -1
- package/theme/styles/roles/index.css +18 -0
- package/theme/styles/tokens/foundation.css +4 -7
- package/types/ojiepermana-angular-internal.d.ts +65 -1
- package/types/ojiepermana-angular-layout.d.ts +1 -1
- package/types/ojiepermana-angular-navigation-horizontal.d.ts +81 -0
- package/types/ojiepermana-angular-navigation-vertical.d.ts +262 -0
- package/types/ojiepermana-angular-navigation.d.ts +228 -0
- package/types/ojiepermana-angular-shell.d.ts +2 -0
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
# Navigation
|
|
2
|
+
|
|
3
|
+
Navigation for `@ojiepermana/angular` is split into three secondary entry points so consumers can import only the state helpers or component variants they need.
|
|
4
|
+
|
|
5
|
+
## Entry Points
|
|
6
|
+
|
|
7
|
+
- `@ojiepermana/angular/navigation` exposes the navigation item model, utility helpers, `NavigationService`, and `NavigationPreferencesService`.
|
|
8
|
+
- `@ojiepermana/angular/navigation/vertical` exposes the vertical navigation container variants and vertical item components.
|
|
9
|
+
- `@ojiepermana/angular/navigation/horizontal` exposes the horizontal navigation variants and branch item component.
|
|
10
|
+
|
|
11
|
+
Do not import consumer code from `@ojiepermana/angular/internal` or from source file paths under this folder. Those paths are implementation details and are not part of the public contract.
|
|
12
|
+
|
|
13
|
+
## Requirements
|
|
14
|
+
|
|
15
|
+
- Configure Angular Router before rendering the navigation components.
|
|
16
|
+
- Import `@ojiepermana/angular/styles/index.css` in the consuming application so the shared theme tokens are available.
|
|
17
|
+
- Use Lucide icon names or aliases in `NavigationItem.icon`. The navigation components resolve icons through `@lucide/angular`.
|
|
18
|
+
|
|
19
|
+
## Source Layout
|
|
20
|
+
|
|
21
|
+
```text
|
|
22
|
+
navigation/
|
|
23
|
+
├── data/
|
|
24
|
+
│ └── demo-navigation.data.ts
|
|
25
|
+
├── state/
|
|
26
|
+
│ ├── navigation.service.ts
|
|
27
|
+
│ ├── navigation.type.ts
|
|
28
|
+
│ └── navigation.utils.ts
|
|
29
|
+
├── horizontal/
|
|
30
|
+
│ ├── core/
|
|
31
|
+
│ │ ├── horizontal-navigation.base.ts
|
|
32
|
+
│ │ └── horizontal-navigation.shared.ts
|
|
33
|
+
│ ├── default/
|
|
34
|
+
│ │ ├── default.ts
|
|
35
|
+
│ │ ├── default.css
|
|
36
|
+
│ │ └── README.md
|
|
37
|
+
│ ├── mega/
|
|
38
|
+
│ │ ├── mega.ts
|
|
39
|
+
│ │ ├── mega.css
|
|
40
|
+
│ │ └── README.md
|
|
41
|
+
│ └── types/
|
|
42
|
+
│ ├── index.ts
|
|
43
|
+
│ ├── branch.ts
|
|
44
|
+
│ └── branch/
|
|
45
|
+
│ ├── branch.ts
|
|
46
|
+
│ └── README.md
|
|
47
|
+
├── shared/
|
|
48
|
+
│ ├── content/
|
|
49
|
+
│ └── tree/
|
|
50
|
+
└── vertical/
|
|
51
|
+
├── core/
|
|
52
|
+
│ ├── vertical-navigation.base.ts
|
|
53
|
+
│ └── vertical-navigation.shared.ts
|
|
54
|
+
├── default/
|
|
55
|
+
│ ├── default.ts
|
|
56
|
+
│ └── default.css
|
|
57
|
+
├── collapsible/
|
|
58
|
+
│ └── collapsible.ts
|
|
59
|
+
└── types/
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Quick Start
|
|
63
|
+
|
|
64
|
+
### 1. Define a navigation tree
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
import { NavigationItem } from '@ojiepermana/angular/navigation';
|
|
68
|
+
|
|
69
|
+
export const appNavigation = [
|
|
70
|
+
{
|
|
71
|
+
id: 'dashboard',
|
|
72
|
+
title: 'Dashboard',
|
|
73
|
+
type: 'basic',
|
|
74
|
+
icon: 'layout-dashboard',
|
|
75
|
+
link: '/dashboard',
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
id: 'workspace',
|
|
79
|
+
title: 'Workspace',
|
|
80
|
+
type: 'collapsable',
|
|
81
|
+
icon: 'folders',
|
|
82
|
+
children: [
|
|
83
|
+
{
|
|
84
|
+
id: 'workspace-overview',
|
|
85
|
+
title: 'Overview',
|
|
86
|
+
type: 'basic',
|
|
87
|
+
link: '/workspace/overview',
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
id: 'workspace-settings',
|
|
91
|
+
title: 'Settings',
|
|
92
|
+
type: 'basic',
|
|
93
|
+
link: '/workspace/settings',
|
|
94
|
+
badge: {
|
|
95
|
+
title: 'Beta',
|
|
96
|
+
classes: 'bg-primary text-primary-foreground',
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
},
|
|
101
|
+
] satisfies NavigationItem[];
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### 2. Render a vertical navigation
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
|
|
108
|
+
import { NavigationItem } from '@ojiepermana/angular/navigation';
|
|
109
|
+
import { VerticalNavigationDefaultComponent } from '@ojiepermana/angular/navigation/vertical';
|
|
110
|
+
|
|
111
|
+
import { appNavigation } from './app.navigation';
|
|
112
|
+
|
|
113
|
+
@Component({
|
|
114
|
+
selector: 'app-shell-navigation',
|
|
115
|
+
imports: [VerticalNavigationDefaultComponent],
|
|
116
|
+
template: `
|
|
117
|
+
<vertical-navigation [navigation]="navigation()" [opened]="true" mode="side" position="left"></vertical-navigation>
|
|
118
|
+
`,
|
|
119
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
120
|
+
})
|
|
121
|
+
export class ShellNavigationComponent {
|
|
122
|
+
readonly navigation = signal<NavigationItem[]>(appNavigation);
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Vertical variants use these selectors:
|
|
127
|
+
|
|
128
|
+
- `<vertical-navigation>` for the default width and density.
|
|
129
|
+
- `<vertical-navigation-collapsible>` for the upcoming collapsible variant contract. It currently reuses the default layout.
|
|
130
|
+
|
|
131
|
+
All vertical variants share the same public API surface inherited from the base component, including `navigation`, `opened`, `mode`, `position`, `autoCollapse`, `inner`, `transparentOverlay`, and the `openedChanged` output.
|
|
132
|
+
|
|
133
|
+
### 3. Render a horizontal navigation
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
|
|
137
|
+
import { NavigationItem } from '@ojiepermana/angular/navigation';
|
|
138
|
+
import { HorizontalNavigation } from '@ojiepermana/angular/navigation/horizontal';
|
|
139
|
+
|
|
140
|
+
import { appNavigation } from './app.navigation';
|
|
141
|
+
|
|
142
|
+
@Component({
|
|
143
|
+
selector: 'app-top-navigation',
|
|
144
|
+
imports: [HorizontalNavigation],
|
|
145
|
+
template: `
|
|
146
|
+
<horizontal-navigation [navigation]="navigation()" (itemClicked)="onItemClicked($event)"></horizontal-navigation>
|
|
147
|
+
`,
|
|
148
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
149
|
+
})
|
|
150
|
+
export class TopNavigationComponent {
|
|
151
|
+
readonly navigation = signal<NavigationItem[]>(appNavigation);
|
|
152
|
+
|
|
153
|
+
onItemClicked(item: NavigationItem): void {
|
|
154
|
+
console.log('Navigation item clicked:', item.id);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Horizontal variants use these selectors:
|
|
160
|
+
|
|
161
|
+
- `<horizontal-navigation>` for the default horizontal layout.
|
|
162
|
+
- `<horizontal-navigation-mega>` for a roomier mega-style horizontal layout.
|
|
163
|
+
|
|
164
|
+
### 4. Optionally mirror the tree into `NavigationService`
|
|
165
|
+
|
|
166
|
+
`NavigationService` is useful when other parts of the application need lookup helpers, active item state, or expanded item state. Vertical navigation renders from its `navigation` input. Horizontal navigation renders from its `navigation` input first, and falls back to the service only when the input array is empty.
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
import { ChangeDetectionStrategy, Component, inject, signal } from '@angular/core';
|
|
170
|
+
import { NavigationItem, NavigationService } from '@ojiepermana/angular/navigation';
|
|
171
|
+
|
|
172
|
+
import { appNavigation } from './app.navigation';
|
|
173
|
+
|
|
174
|
+
@Component({
|
|
175
|
+
selector: 'app-navigation-store',
|
|
176
|
+
template: '',
|
|
177
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
178
|
+
})
|
|
179
|
+
export class NavigationStoreComponent {
|
|
180
|
+
private readonly navigationService = inject(NavigationService);
|
|
181
|
+
readonly navigation = signal<NavigationItem[]>(appNavigation);
|
|
182
|
+
|
|
183
|
+
ngOnInit(): void {
|
|
184
|
+
this.navigationService.storeNavigation(this.navigation());
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Navigation Item Model
|
|
190
|
+
|
|
191
|
+
Every tree node is a `NavigationItem`. The public union is split across these item types:
|
|
192
|
+
|
|
193
|
+
- `basic` is a leaf item that can route, open an external link, or run an `action` callback.
|
|
194
|
+
- `aside` is a routable branch rendered through the vertical aside panel pattern.
|
|
195
|
+
- `collapsable` is a branch that expands and collapses inline.
|
|
196
|
+
- `group` is a non-routable section label with children.
|
|
197
|
+
- `divider` renders a visual separator.
|
|
198
|
+
- `spacer` renders flexible empty space.
|
|
199
|
+
|
|
200
|
+
Shared fields available on most item types:
|
|
201
|
+
|
|
202
|
+
- `id`, `title`, `subtitle`, `tooltip`, `icon`, `disabled`, and `meta`.
|
|
203
|
+
- `classes.wrapper`, `classes.icon`, `classes.title`, and `classes.subtitle` for per-item styling hooks.
|
|
204
|
+
- `badge.title` and `badge.classes` for optional item badges.
|
|
205
|
+
- `isHidden` for conditional visibility.
|
|
206
|
+
|
|
207
|
+
Routable item fields available on `basic`, `aside`, and `collapsable`:
|
|
208
|
+
|
|
209
|
+
- `link`, `fragment`, `preserveFragment`, `queryParams`, and `queryParamsHandling`.
|
|
210
|
+
- `externalLink` and `target` for external navigation.
|
|
211
|
+
- `exactMatch` and `isActiveMatchOptions` for route activity matching.
|
|
212
|
+
- `action` for click callbacks.
|
|
213
|
+
|
|
214
|
+
## Navigation Service
|
|
215
|
+
|
|
216
|
+
`NavigationService` is provided in root and exposes navigation state as signals plus a small lookup API.
|
|
217
|
+
|
|
218
|
+
Readonly state:
|
|
219
|
+
|
|
220
|
+
- `navigationItems`
|
|
221
|
+
- `activeItemId`
|
|
222
|
+
- `flatNavigation`
|
|
223
|
+
- `expandedItemIds`
|
|
224
|
+
|
|
225
|
+
Mutation and lookup methods:
|
|
226
|
+
|
|
227
|
+
- `storeNavigation()` and `deleteNavigation()`
|
|
228
|
+
- `setActiveItem()` and `clearActiveItem()`
|
|
229
|
+
- `expandItem()`, `collapseItem()`, `toggleItemExpanded()`, and `clearExpandedItems()`
|
|
230
|
+
- `getNavigation()`, `getActiveItem()`, `getFlatNavigation()`, `getItem()`, and `getItemParent()`
|
|
231
|
+
|
|
232
|
+
`activeItemId` and `expandedItemIds` now persist automatically through `NavigationPreferencesService`, while `NavigationService` stays focused on the navigation tree and lookup helpers.
|
|
233
|
+
|
|
234
|
+
## Navigation Preferences Service
|
|
235
|
+
|
|
236
|
+
`NavigationPreferencesService` is provided in root and stores persisted navigation preferences separately from the navigation tree itself.
|
|
237
|
+
|
|
238
|
+
`provideNgNavigation()` configures the defaults that are used before any persisted browser state exists.
|
|
239
|
+
|
|
240
|
+
Readonly state:
|
|
241
|
+
|
|
242
|
+
- `horizontalVariant`
|
|
243
|
+
- `verticalAppearance`
|
|
244
|
+
- `activeItemId`
|
|
245
|
+
- `expandedItemIds`
|
|
246
|
+
|
|
247
|
+
Mutation methods:
|
|
248
|
+
|
|
249
|
+
- `setHorizontalVariant()`
|
|
250
|
+
- `setVerticalAppearance()`
|
|
251
|
+
- `setActiveItem()` and `clearActiveItem()`
|
|
252
|
+
- `setExpandedItemIds()`, `expandItem()`, `collapseItem()`, `toggleExpandedItem()`, and `clearExpandedItems()`
|
|
253
|
+
- `syncWithNavigation()` and `reset()`
|
|
254
|
+
|
|
255
|
+
Persisted flat localStorage keys:
|
|
256
|
+
|
|
257
|
+
- `navigation-horizontal-variant`
|
|
258
|
+
- `navigation-vertical-appearance`
|
|
259
|
+
- `navigation-active-item`
|
|
260
|
+
- `navigation-expanded-items`
|
|
261
|
+
|
|
262
|
+
Legacy `ng-navigation:v1:vertical-appearance`, `ng-navigation:v1:active-item`, and `ng-navigation:v1:expanded-items` entries migrate automatically when they are read.
|
|
263
|
+
|
|
264
|
+
Use `horizontalVariant()` to choose which horizontal variant component to render in the consumer app, and `verticalAppearance()` to choose which vertical variant component to render.
|
|
265
|
+
|
|
266
|
+
Default provider config fields:
|
|
267
|
+
|
|
268
|
+
- `defaultHorizontalVariant`
|
|
269
|
+
- `defaultVerticalVariant`
|
|
270
|
+
|
|
271
|
+
## Styling Notes
|
|
272
|
+
|
|
273
|
+
- Pass Tailwind utility classes or other class names through the item `classes` and `badge.classes` fields when you need local visual overrides.
|
|
274
|
+
- Icons are resolved from Lucide. Use names such as `layout-dashboard`, `folders`, `settings-2`, or valid Lucide aliases.
|
|
275
|
+
- Active state depends on Angular Router. Use `exactMatch` or `isActiveMatchOptions` when the default subset matching is too broad.
|
|
276
|
+
|
|
277
|
+
## Troubleshooting
|
|
278
|
+
|
|
279
|
+
### Nothing renders
|
|
280
|
+
|
|
281
|
+
- Confirm the component receives a non-empty `navigation` input.
|
|
282
|
+
- If a horizontal navigation intentionally relies on service fallback, call `NavigationService.storeNavigation()` before it renders.
|
|
283
|
+
- Check whether `isHidden` returns `true` for the affected items.
|
|
284
|
+
|
|
285
|
+
### Icons do not appear
|
|
286
|
+
|
|
287
|
+
- Use Lucide icon names or aliases, not Material icon names.
|
|
288
|
+
- Check the `icon` value on the affected item.
|
|
289
|
+
|
|
290
|
+
### Active state is wrong
|
|
291
|
+
|
|
292
|
+
- Verify Router is configured and the application uses the expected route paths.
|
|
293
|
+
- Tune `exactMatch` or `isActiveMatchOptions` for the affected items.
|
|
294
|
+
|
|
295
|
+
## Notes For Contributors
|
|
296
|
+
|
|
297
|
+
- Keep `@ojiepermana/angular/navigation` focused on the item model, helpers, and `NavigationService`.
|
|
298
|
+
- Keep navigation persistence and UI preferences in `NavigationPreferencesService` instead of storing the full navigation tree in browser storage.
|
|
299
|
+
- Keep render components in `@ojiepermana/angular/navigation/vertical` and `@ojiepermana/angular/navigation/horizontal`.
|
|
300
|
+
- Move shared implementation helpers behind `@ojiepermana/angular/internal` instead of reaching across entry point boundaries with relative imports.
|
|
301
|
+
- Treat `projects/library/navigation/shared` as a local compatibility layer for tests and source organization, not as consumer API.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Horizontal Navigation
|
|
2
|
+
|
|
3
|
+
The horizontal entrypoint exposes shared top-bar navigation variants for `@ojiepermana/angular`.
|
|
4
|
+
|
|
5
|
+
## Public Imports
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { HorizontalNavigation, HorizontalNavigationMegaComponent } from '@ojiepermana/angular/navigation/horizontal';
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Variants
|
|
12
|
+
|
|
13
|
+
- `HorizontalNavigation` renders the default selector `<horizontal-navigation>`.
|
|
14
|
+
- `HorizontalNavigationMegaComponent` renders the mega selector `<horizontal-navigation-mega>`.
|
|
15
|
+
|
|
16
|
+
## Source Layout
|
|
17
|
+
|
|
18
|
+
```text
|
|
19
|
+
horizontal/
|
|
20
|
+
├── core/
|
|
21
|
+
│ ├── horizontal-navigation.base.ts
|
|
22
|
+
│ └── horizontal-navigation.shared.ts
|
|
23
|
+
├── default/
|
|
24
|
+
│ ├── default.ts
|
|
25
|
+
│ ├── default.css
|
|
26
|
+
│ └── README.md
|
|
27
|
+
├── mega/
|
|
28
|
+
│ ├── mega.ts
|
|
29
|
+
│ ├── mega.css
|
|
30
|
+
│ └── README.md
|
|
31
|
+
├── types/
|
|
32
|
+
│ ├── index.ts
|
|
33
|
+
│ ├── README.md
|
|
34
|
+
│ ├── branch.ts
|
|
35
|
+
│ └── branch/
|
|
36
|
+
│ ├── branch.ts
|
|
37
|
+
│ └── README.md
|
|
38
|
+
├── horizontal.ts
|
|
39
|
+
├── index.ts
|
|
40
|
+
├── ng-package.json
|
|
41
|
+
└── public-api.ts
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Notes
|
|
45
|
+
|
|
46
|
+
- `core/` contains variant-agnostic logic and template composition.
|
|
47
|
+
- `default/` and `mega/` contain variant-specific components and styles.
|
|
48
|
+
- `types/` contains reusable branch rendering primitives shared by all horizontal variants.
|
|
49
|
+
- `horizontal.ts` remains a compatibility barrel that points to the default variant implementation.
|
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ojiepermana/angular",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -34,6 +34,18 @@
|
|
|
34
34
|
"types": "./types/ojiepermana-angular-layout.d.ts",
|
|
35
35
|
"default": "./fesm2022/ojiepermana-angular-layout.mjs"
|
|
36
36
|
},
|
|
37
|
+
"./navigation": {
|
|
38
|
+
"types": "./types/ojiepermana-angular-navigation.d.ts",
|
|
39
|
+
"default": "./fesm2022/ojiepermana-angular-navigation.mjs"
|
|
40
|
+
},
|
|
41
|
+
"./navigation/horizontal": {
|
|
42
|
+
"types": "./types/ojiepermana-angular-navigation-horizontal.d.ts",
|
|
43
|
+
"default": "./fesm2022/ojiepermana-angular-navigation-horizontal.mjs"
|
|
44
|
+
},
|
|
45
|
+
"./navigation/vertical": {
|
|
46
|
+
"types": "./types/ojiepermana-angular-navigation-vertical.d.ts",
|
|
47
|
+
"default": "./fesm2022/ojiepermana-angular-navigation-vertical.mjs"
|
|
48
|
+
},
|
|
37
49
|
"./shell": {
|
|
38
50
|
"types": "./types/ojiepermana-angular-shell.d.ts",
|
|
39
51
|
"default": "./fesm2022/ojiepermana-angular-shell.mjs"
|
package/shell/README.md
CHANGED
|
@@ -29,9 +29,13 @@ export const appConfig: ApplicationConfig = {
|
|
|
29
29
|
defaultMode: 'vertical',
|
|
30
30
|
defaultContainer: 'boxed',
|
|
31
31
|
},
|
|
32
|
+
navigation: {
|
|
33
|
+
defaultHorizontalVariant: 'default',
|
|
34
|
+
defaultVerticalVariant: 'default',
|
|
35
|
+
},
|
|
32
36
|
}),
|
|
33
37
|
],
|
|
34
38
|
};
|
|
35
39
|
```
|
|
36
40
|
|
|
37
|
-
`provideNgShell()` is a convenience wrapper over `provideNgTheme()` and `
|
|
41
|
+
`provideNgShell()` is a convenience wrapper over `provideNgTheme()`, `provideNgLayout()`, and `provideNgNavigation()`. It does not introduce a separate runtime state layer.
|
package/styles/index.css
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
@import '../theme/styles/index.css';
|
|
2
|
-
@import '../
|
|
2
|
+
@import '../layout/src/layout.css';
|
package/theme/README.md
CHANGED
|
@@ -188,9 +188,9 @@ It imports the current structure in this order:
|
|
|
188
188
|
- `utilities/index.css`
|
|
189
189
|
- `adapters/material-ui/index.css`
|
|
190
190
|
|
|
191
|
-
Layout selectors now live in `projects/library/layout/
|
|
191
|
+
Layout-wide selectors now live in `projects/library/layout/src/layout.css` inside this workspace.
|
|
192
192
|
|
|
193
|
-
For applications that consume the published package, use the aggregate bundle at `projects/library/styles/index.css` in this workspace or `@ojiepermana/angular/styles/index.css` from the published package. The aggregate bundle imports theme first, then layout.
|
|
193
|
+
For applications that consume the published package, use the aggregate bundle at `projects/library/styles/index.css` in this workspace or `@ojiepermana/angular/styles/index.css` from the published package. The aggregate bundle imports theme first, then the layout-wide selectors. Horizontal and vertical shell CSS are loaded by their respective layout components.
|
|
194
194
|
|
|
195
195
|
Application-level resets are intentionally opt-in. If a consumer wants them, import `projects/library/styles/resets.css` in this workspace or `@ojiepermana/angular/styles/resets.css` from the published package in addition to the aggregate bundle.
|
|
196
196
|
|
|
@@ -310,10 +310,7 @@ For a package consumer, use the published aggregate bundle instead:
|
|
|
310
310
|
|
|
311
311
|
```ts
|
|
312
312
|
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
|
313
|
-
import {
|
|
314
|
-
LayoutContainerSwitcherComponent,
|
|
315
|
-
LayoutVerticalComponent,
|
|
316
|
-
} from '@ojiepermana/angular/layout';
|
|
313
|
+
import { LayoutContainerSwitcherComponent, LayoutVerticalComponent } from '@ojiepermana/angular/layout';
|
|
317
314
|
import {
|
|
318
315
|
StyleSwitcherComponent,
|
|
319
316
|
ColorPickerComponent,
|
|
@@ -28,11 +28,7 @@
|
|
|
28
28
|
--mat-sys-on-surface: var(--container-foreground);
|
|
29
29
|
--mat-sys-surface-container: var(--overlay-surface);
|
|
30
30
|
--mat-sys-surface-container-high: var(--overlay-surface);
|
|
31
|
-
--mat-sys-surface-container-highest: color-mix(
|
|
32
|
-
in oklab,
|
|
33
|
-
var(--overlay-surface) 90%,
|
|
34
|
-
var(--muted)
|
|
35
|
-
);
|
|
31
|
+
--mat-sys-surface-container-highest: color-mix(in oklab, var(--overlay-surface) 90%, var(--muted));
|
|
36
32
|
--mat-sys-surface-variant: var(--data-header-surface);
|
|
37
33
|
--mat-sys-on-surface-variant: var(--data-header-foreground);
|
|
38
34
|
--mat-sys-inverse-surface: var(--inverse);
|
|
@@ -25,15 +25,13 @@
|
|
|
25
25
|
--shell-foreground: var(--card-foreground);
|
|
26
26
|
--shell-border: var(--border);
|
|
27
27
|
--shell-shadow:
|
|
28
|
-
rgb(0 0 0 / 0.14) 0 28px 70px, rgb(0 0 0 / 0.1) 0 14px 32px,
|
|
29
|
-
oklab(0.263084 -0.00230259 0.0124794 / 0.1) 0 0 0 1px;
|
|
28
|
+
rgb(0 0 0 / 0.14) 0 28px 70px, rgb(0 0 0 / 0.1) 0 14px 32px, oklab(0.263084 -0.00230259 0.0124794 / 0.1) 0 0 0 1px;
|
|
30
29
|
--shell-backdrop: var(--backdrop);
|
|
31
30
|
--surface-elevated: var(--popover);
|
|
32
31
|
--surface-elevated-foreground: var(--popover-foreground);
|
|
33
32
|
--surface-elevated-border: oklab(0.263084 -0.00230259 0.0124794 / 0.2);
|
|
34
33
|
--surface-elevated-shadow:
|
|
35
|
-
rgb(0 0 0 / 0.12) 0 24px 52px, rgb(0 0 0 / 0.08) 0 10px 24px,
|
|
36
|
-
oklab(0.263084 -0.00230259 0.0124794 / 0.1) 0 0 0 1px;
|
|
34
|
+
rgb(0 0 0 / 0.12) 0 24px 52px, rgb(0 0 0 / 0.08) 0 10px 24px, oklab(0.263084 -0.00230259 0.0124794 / 0.1) 0 0 0 1px;
|
|
37
35
|
}
|
|
38
36
|
|
|
39
37
|
.dark[data-theme-style='flat'] {
|
|
@@ -54,8 +52,7 @@
|
|
|
54
52
|
--sidebar-primary-foreground: var(--foreground);
|
|
55
53
|
--sidebar-accent: color-mix(in oklab, var(--theme-primary) 18%, var(--sidebar));
|
|
56
54
|
--sidebar-accent-foreground: var(--foreground);
|
|
57
|
-
--shell-shadow:
|
|
58
|
-
rgb(0 0 0 / 0.44) 0 28px 70px, rgb(0 0 0 / 0.28) 0 14px 32px, rgb(235 231 222 / 0.08) 0 0 0 1px;
|
|
55
|
+
--shell-shadow: rgb(0 0 0 / 0.44) 0 28px 70px, rgb(0 0 0 / 0.28) 0 14px 32px, rgb(235 231 222 / 0.08) 0 0 0 1px;
|
|
59
56
|
--surface-elevated: #2b2820;
|
|
60
57
|
--surface-elevated-foreground: var(--foreground);
|
|
61
58
|
--surface-elevated-border: rgb(235 231 222 / 0.12);
|
|
@@ -7,13 +7,7 @@
|
|
|
7
7
|
ui-sans-serif,
|
|
8
8
|
sans-serif
|
|
9
9
|
);
|
|
10
|
-
--typeface-brand: var(
|
|
11
|
-
--theme-style-glass-brand-font,
|
|
12
|
-
ui-rounded,
|
|
13
|
-
'Avenir Next',
|
|
14
|
-
'Segoe UI',
|
|
15
|
-
sans-serif
|
|
16
|
-
);
|
|
10
|
+
--typeface-brand: var(--theme-style-glass-brand-font, ui-rounded, 'Avenir Next', 'Segoe UI', sans-serif);
|
|
17
11
|
--typeface-mono: var(
|
|
18
12
|
--theme-style-glass-mono-font,
|
|
19
13
|
'SFMono-Regular',
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
@import './flat.css';
|
|
2
|
-
@import './glass.css';
|
|
2
|
+
@import './glass.css';
|
|
@@ -3,6 +3,24 @@
|
|
|
3
3
|
--nav-surface: color-mix(in oklab, var(--shell-surface) 92%, transparent);
|
|
4
4
|
--nav-foreground: var(--shell-foreground);
|
|
5
5
|
--nav-border: var(--shell-border);
|
|
6
|
+
--nav-font-family: var(--typeface-plain);
|
|
7
|
+
--nav-item-font-size: 0.875rem;
|
|
8
|
+
--nav-item-line-height: 1.25rem;
|
|
9
|
+
--nav-item-font-weight: 500;
|
|
10
|
+
--nav-item-active-font-weight: 600;
|
|
11
|
+
--nav-subtitle-font-size: 0.75rem;
|
|
12
|
+
--nav-subtitle-line-height: 1rem;
|
|
13
|
+
--nav-subtitle-font-weight: 400;
|
|
14
|
+
--nav-badge-font-size: 0.75rem;
|
|
15
|
+
--nav-badge-line-height: 1rem;
|
|
16
|
+
--nav-badge-font-weight: 600;
|
|
17
|
+
--nav-group-title-font-size: 0.75rem;
|
|
18
|
+
--nav-group-title-line-height: 1rem;
|
|
19
|
+
--nav-group-title-font-weight: 600;
|
|
20
|
+
--nav-group-title-letter-spacing: 0.08em;
|
|
21
|
+
--nav-group-subtitle-font-size: 0.75rem;
|
|
22
|
+
--nav-group-subtitle-line-height: 1rem;
|
|
23
|
+
--nav-group-subtitle-font-weight: 400;
|
|
6
24
|
--nav-item-hover-surface: var(--tertiary-container);
|
|
7
25
|
--nav-item-hover-foreground: var(--tertiary-container-foreground);
|
|
8
26
|
--nav-item-active-surface: var(--secondary-container);
|
|
@@ -87,8 +87,7 @@
|
|
|
87
87
|
|
|
88
88
|
/* Keep these radius tokens in :root as well so non-Tailwind consumers get the same values as @theme. */
|
|
89
89
|
:root {
|
|
90
|
-
--typeface-plain:
|
|
91
|
-
ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
90
|
+
--typeface-plain: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
92
91
|
--typeface-brand: var(--typeface-plain);
|
|
93
92
|
--typeface-mono: 'SFMono-Regular', 'Cascadia Mono', 'Liberation Mono', ui-monospace, monospace;
|
|
94
93
|
--radius: 0.75rem;
|
|
@@ -115,7 +114,7 @@
|
|
|
115
114
|
--layout-shell-max-width: 100%;
|
|
116
115
|
--layout-shell-padding: 0rem;
|
|
117
116
|
--layout-shell-radius: 0rem;
|
|
118
|
-
--layout-sidebar-width:
|
|
117
|
+
--layout-sidebar-width: 280px;
|
|
119
118
|
--layout-header-height: 3.75rem;
|
|
120
119
|
--layout-header-display: grid;
|
|
121
120
|
--layout-sidebar-display: flex;
|
|
@@ -131,9 +130,7 @@
|
|
|
131
130
|
text-decoration-color var(--ngt-motion-duration-fast) var(--ngt-motion-ease-standard),
|
|
132
131
|
backdrop-filter var(--ngt-motion-duration-medium) var(--ngt-motion-ease-standard);
|
|
133
132
|
--ngt-shell-transition:
|
|
134
|
-
var(--ngt-chrome-transition),
|
|
135
|
-
border-radius var(--ngt-motion-duration-medium) var(--ngt-motion-ease-standard);
|
|
133
|
+
var(--ngt-chrome-transition), border-radius var(--ngt-motion-duration-medium) var(--ngt-motion-ease-standard);
|
|
136
134
|
--ngt-control-transition:
|
|
137
|
-
var(--ngt-chrome-transition),
|
|
138
|
-
transform var(--ngt-motion-duration-fast) var(--ngt-motion-ease-standard);
|
|
135
|
+
var(--ngt-chrome-transition), transform var(--ngt-motion-duration-fast) var(--ngt-motion-ease-standard);
|
|
139
136
|
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import * as _angular_core from '@angular/core';
|
|
2
|
+
import { TemplateRef } from '@angular/core';
|
|
3
|
+
import { LucideIcon } from '@lucide/angular';
|
|
4
|
+
import { NavigationItem } from '@ojiepermana/angular/navigation';
|
|
2
5
|
|
|
3
6
|
declare const libraryLucideConfigProvider: _angular_core.Provider;
|
|
4
7
|
|
|
@@ -18,9 +21,70 @@ declare class LocalStorageStateAdapter<Axis extends string> {
|
|
|
18
21
|
constructor(config: LocalStorageStateAdapterConfig<Axis>);
|
|
19
22
|
clear(axis: Axis): void;
|
|
20
23
|
persist(axis: Axis, value: string): void;
|
|
24
|
+
readValue(axis: Axis): string | null;
|
|
21
25
|
read<T extends string>(axis: Axis, fallback: T, isValid: (value: string) => value is T): T;
|
|
22
26
|
private key;
|
|
23
27
|
private legacyKey;
|
|
24
28
|
}
|
|
25
29
|
|
|
26
|
-
|
|
30
|
+
type NavigationItemContentVariant = 'horizontal' | 'vertical' | 'vertical-group';
|
|
31
|
+
declare class NavigationItemContent {
|
|
32
|
+
item: _angular_core.InputSignal<NavigationItem>;
|
|
33
|
+
level: _angular_core.InputSignal<number>;
|
|
34
|
+
variant: _angular_core.InputSignal<NavigationItemContentVariant>;
|
|
35
|
+
protected readonly resolvedIcon: _angular_core.Signal<LucideIcon | null>;
|
|
36
|
+
protected readonly horizontalIconClass: _angular_core.Signal<string | null>;
|
|
37
|
+
protected readonly verticalIconClass: _angular_core.Signal<string | null>;
|
|
38
|
+
protected readonly horizontalTitleClass: _angular_core.Signal<string | null>;
|
|
39
|
+
protected readonly titleClass: _angular_core.Signal<string | null>;
|
|
40
|
+
protected readonly subtitleClass: _angular_core.Signal<string | null>;
|
|
41
|
+
protected readonly horizontalBadgeClass: _angular_core.Signal<string | null>;
|
|
42
|
+
protected readonly verticalBadgeClass: _angular_core.Signal<string | null>;
|
|
43
|
+
protected readonly titleContainerClass: _angular_core.Signal<"kit-navigation-group-title" | "kit-navigation-item-title">;
|
|
44
|
+
protected readonly subtitleContainerClass: _angular_core.Signal<"kit-navigation-group-subtitle" | "kit-navigation-item-subtitle">;
|
|
45
|
+
private joinClasses;
|
|
46
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<NavigationItemContent, never>;
|
|
47
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<NavigationItemContent, "navigation-item-content", never, { "item": { "alias": "item"; "required": true; "isSignal": true; }; "level": { "alias": "level"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
type NavigationTreeTemplateData = Record<string, unknown>;
|
|
51
|
+
interface NavigationTreeItemContext {
|
|
52
|
+
$implicit: NavigationItem;
|
|
53
|
+
level: number;
|
|
54
|
+
parentKey: string;
|
|
55
|
+
isLast: boolean;
|
|
56
|
+
data: NavigationTreeTemplateData | null;
|
|
57
|
+
}
|
|
58
|
+
declare class NavigationTreeOutlet {
|
|
59
|
+
items: _angular_core.InputSignal<NavigationItem[]>;
|
|
60
|
+
level: _angular_core.InputSignal<number>;
|
|
61
|
+
parentKey: _angular_core.InputSignal<string>;
|
|
62
|
+
data: _angular_core.InputSignal<NavigationTreeTemplateData | null>;
|
|
63
|
+
branchTemplate: _angular_core.InputSignal<TemplateRef<NavigationTreeItemContext>>;
|
|
64
|
+
leafTemplate: _angular_core.InputSignal<TemplateRef<NavigationTreeItemContext>>;
|
|
65
|
+
groupTemplate: _angular_core.InputSignal<TemplateRef<NavigationTreeItemContext>>;
|
|
66
|
+
dividerTemplate: _angular_core.InputSignal<TemplateRef<NavigationTreeItemContext> | null>;
|
|
67
|
+
spacerTemplate: _angular_core.InputSignal<TemplateRef<NavigationTreeItemContext> | null>;
|
|
68
|
+
trackByFn: _angular_core.InputSignal<(index: number, item: NavigationItem) => string | number | undefined>;
|
|
69
|
+
shouldRenderItemFn: _angular_core.InputSignal<(item: NavigationItem) => boolean>;
|
|
70
|
+
hasChildrenFn: _angular_core.InputSignal<(item: NavigationItem) => boolean>;
|
|
71
|
+
resolveParentKeyFn: _angular_core.InputSignal<(parentKey: string, item: NavigationItem) => string>;
|
|
72
|
+
trackItem(index: number, item: NavigationItem): string | number | undefined;
|
|
73
|
+
shouldRender(item: NavigationItem): boolean;
|
|
74
|
+
hasChildren(item: NavigationItem): boolean;
|
|
75
|
+
resolveParentKey(parentKey: string, item: NavigationItem): string;
|
|
76
|
+
templateContext(item: NavigationItem, isLast: boolean): NavigationTreeItemContext;
|
|
77
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<NavigationTreeOutlet, never>;
|
|
78
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<NavigationTreeOutlet, "navigation-tree-outlet", never, { "items": { "alias": "items"; "required": false; "isSignal": true; }; "level": { "alias": "level"; "required": false; "isSignal": true; }; "parentKey": { "alias": "parentKey"; "required": false; "isSignal": true; }; "data": { "alias": "data"; "required": false; "isSignal": true; }; "branchTemplate": { "alias": "branchTemplate"; "required": true; "isSignal": true; }; "leafTemplate": { "alias": "leafTemplate"; "required": true; "isSignal": true; }; "groupTemplate": { "alias": "groupTemplate"; "required": true; "isSignal": true; }; "dividerTemplate": { "alias": "dividerTemplate"; "required": false; "isSignal": true; }; "spacerTemplate": { "alias": "spacerTemplate"; "required": false; "isSignal": true; }; "trackByFn": { "alias": "trackByFn"; "required": false; "isSignal": true; }; "shouldRenderItemFn": { "alias": "shouldRenderItemFn"; "required": false; "isSignal": true; }; "hasChildrenFn": { "alias": "hasChildrenFn"; "required": false; "isSignal": true; }; "resolveParentKeyFn": { "alias": "resolveParentKeyFn"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
type NavigationExpandedItemsByParent = Record<string, string>;
|
|
82
|
+
declare function trackNavigationTreeItem(index: number, item: NavigationItem): string | number | undefined;
|
|
83
|
+
declare function getNavigationTreePathKey(parentKey: string, item: NavigationItem): string;
|
|
84
|
+
declare function isNavigationTreeItemExpanded(expandedByParent: NavigationExpandedItemsByParent, parentKey: string, item: NavigationItem): boolean;
|
|
85
|
+
declare function toggleNavigationTreeItemExpanded(expandedByParent: NavigationExpandedItemsByParent, parentKey: string, item: NavigationItem): NavigationExpandedItemsByParent;
|
|
86
|
+
declare function hasActiveNavigationDescendant(item: NavigationItem, isItemActive: (item: NavigationItem) => boolean): boolean;
|
|
87
|
+
declare function isNavigationTreeDescendant(parent: NavigationItem, item: NavigationItem): boolean;
|
|
88
|
+
|
|
89
|
+
export { LocalStorageStateAdapter, NavigationItemContent, NavigationTreeOutlet, getNavigationTreePathKey, hasActiveNavigationDescendant, isNavigationTreeDescendant, isNavigationTreeItemExpanded, libraryLucideConfigProvider, toggleNavigationTreeItemExpanded, trackNavigationTreeItem };
|
|
90
|
+
export type { NavigationExpandedItemsByParent, NavigationTreeItemContext, NavigationTreeTemplateData };
|
|
@@ -43,7 +43,7 @@ declare class LayoutHostDirective {
|
|
|
43
43
|
|
|
44
44
|
declare class LayoutHorizontalComponent {
|
|
45
45
|
static ɵfac: i0.ɵɵFactoryDeclaration<LayoutHorizontalComponent, never>;
|
|
46
|
-
static ɵcmp: i0.ɵɵComponentDeclaration<LayoutHorizontalComponent, "horizontal", never, {}, {}, never, ["[
|
|
46
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<LayoutHorizontalComponent, "horizontal", never, {}, {}, never, ["[brand]", "[navigation]", "[action]"], true, never>;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
declare class LayoutVerticalComponent {
|