@ng-prism/core 21.1.0 → 21.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -161,7 +161,7 @@ Full documentation with API reference, plugin guide, and advanced configuration:
161
161
 
162
162
  ## Requirements
163
163
 
164
- - Angular >= 21
164
+ - Angular >= 19
165
165
  - TypeScript >= 5.9
166
166
  - Components must use `input()` / `output()` signals (not `@Input()` / `@Output()` decorators)
167
167
 
@@ -1,14 +1,28 @@
1
1
  import type { NavigationItem } from '../services/navigation-item.types.js';
2
2
  import { PrismNavigationService } from '../services/prism-navigation.service.js';
3
3
  import * as i0 from "@angular/core";
4
+ interface SidebarCategory {
5
+ name: string;
6
+ items: NavigationItem[];
7
+ }
8
+ interface SidebarGroup {
9
+ name: string | null;
10
+ categories: SidebarCategory[];
11
+ }
4
12
  export declare class PrismSidebarComponent {
5
13
  protected readonly navigationService: PrismNavigationService;
6
- protected readonly entries: import("@angular/core").Signal<[string, NavigationItem[]][]>;
14
+ private readonly expandedSet;
15
+ protected readonly groups: import("@angular/core").Signal<SidebarGroup[]>;
16
+ protected isExpanded(key: string, index: number): boolean;
17
+ protected toggle(key: string, index: number): void;
7
18
  protected itemKey(item: NavigationItem): string;
8
19
  protected itemLabel(item: NavigationItem): string;
9
20
  protected isActive(item: NavigationItem): boolean;
10
21
  protected onSelect(item: NavigationItem): void;
22
+ private loadExpanded;
23
+ private saveExpanded;
11
24
  static ɵfac: i0.ɵɵFactoryDeclaration<PrismSidebarComponent, never>;
12
25
  static ɵcmp: i0.ɵɵComponentDeclaration<PrismSidebarComponent, "prism-sidebar", never, {}, {}, never, never, true, never>;
13
26
  }
27
+ export {};
14
28
  //# sourceMappingURL=prism-sidebar.component.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"prism-sidebar.component.d.ts","sourceRoot":"","sources":["../../../src/app/sidebar/prism-sidebar.component.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sCAAsC,CAAC;AAC3E,OAAO,EAAE,sBAAsB,EAAE,MAAM,yCAAyC,CAAC;;AAEjF,qBA0Fa,qBAAqB;IAChC,SAAS,CAAC,QAAQ,CAAC,iBAAiB,yBAAkC;IAEtE,SAAS,CAAC,QAAQ,CAAC,OAAO,+DAC8B;IAExD,SAAS,CAAC,OAAO,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM;IAM/C,SAAS,CAAC,SAAS,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM;IAMjD,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO;IASjD,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI;yCA3BnC,qBAAqB;2CAArB,qBAAqB;CAkCjC"}
1
+ {"version":3,"file":"prism-sidebar.component.d.ts","sourceRoot":"","sources":["../../../src/app/sidebar/prism-sidebar.component.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sCAAsC,CAAC;AAC3E,OAAO,EAAE,sBAAsB,EAAE,MAAM,yCAAyC,CAAC;;AAIjF,UAAU,eAAe;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,cAAc,EAAE,CAAC;CACzB;AAED,UAAU,YAAY;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,UAAU,EAAE,eAAe,EAAE,CAAC;CAC/B;AAED,qBA2Na,qBAAqB;IAChC,SAAS,CAAC,QAAQ,CAAC,iBAAiB,yBAAkC;IAEtE,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA4C;IAExE,SAAS,CAAC,QAAQ,CAAC,MAAM,iDAgCtB;IAEH,SAAS,CAAC,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO;IAOzD,SAAS,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAoBlD,SAAS,CAAC,OAAO,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM;IAM/C,SAAS,CAAC,SAAS,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM;IAMjD,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO;IASjD,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI;IAQ9C,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,YAAY;yCAxGT,qBAAqB;2CAArB,qBAAqB;CA6GjC"}
@@ -1,45 +1,194 @@
1
- import { Component, computed, inject } from '@angular/core';
1
+ import { Component, computed, inject, signal } from '@angular/core';
2
+ import { NgTemplateOutlet } from '@angular/common';
2
3
  import { PrismNavigationService } from '../services/prism-navigation.service.js';
3
4
  import * as i0 from "@angular/core";
4
- const _forTrack0 = ($index, $item) => $item[0];
5
- function _forTrack1($index, $item) { return this.itemKey($item); }
6
- function PrismSidebarComponent_For_2_For_4_Conditional_1_Template(rf, ctx) { if (rf & 1) {
7
- i0.ɵɵdomElementStart(0, "span", 5);
8
- i0.ɵɵtext(1, "\u25C8");
9
- i0.ɵɵdomElementEnd();
5
+ const _c0 = a0 => ({ cat: a0, nested: true });
6
+ const _c1 = a0 => ({ cat: a0, nested: false });
7
+ const _forTrack0 = ($index, $item) => $item.name ?? "__ungrouped";
8
+ const _forTrack1 = ($index, $item) => $item.name;
9
+ function _forTrack2($index, $item) { return this.itemKey($item); }
10
+ function PrismSidebarComponent_For_2_Conditional_0_Conditional_5_For_1_ng_container_0_Template(rf, ctx) { if (rf & 1) {
11
+ i0.ɵɵelementContainer(0);
12
+ } }
13
+ function PrismSidebarComponent_For_2_Conditional_0_Conditional_5_For_1_Template(rf, ctx) { if (rf & 1) {
14
+ i0.ɵɵtemplate(0, PrismSidebarComponent_For_2_Conditional_0_Conditional_5_For_1_ng_container_0_Template, 1, 0, "ng-container", 6);
15
+ } if (rf & 2) {
16
+ const cat_r6 = ctx.$implicit;
17
+ i0.ɵɵnextContext(4);
18
+ const categoryTpl_r7 = i0.ɵɵreference(4);
19
+ i0.ɵɵproperty("ngTemplateOutlet", categoryTpl_r7)("ngTemplateOutletContext", i0.ɵɵpureFunction1(2, _c0, cat_r6));
10
20
  } }
11
- function PrismSidebarComponent_For_2_For_4_Template(rf, ctx) { if (rf & 1) {
21
+ function PrismSidebarComponent_For_2_Conditional_0_Conditional_5_Template(rf, ctx) { if (rf & 1) {
22
+ i0.ɵɵrepeaterCreate(0, PrismSidebarComponent_For_2_Conditional_0_Conditional_5_For_1_Template, 1, 4, "ng-container", null, _forTrack1);
23
+ } if (rf & 2) {
24
+ const group_r3 = i0.ɵɵnextContext(2).$implicit;
25
+ i0.ɵɵrepeater(group_r3.categories);
26
+ } }
27
+ function PrismSidebarComponent_For_2_Conditional_0_Template(rf, ctx) { if (rf & 1) {
12
28
  const _r1 = i0.ɵɵgetCurrentView();
13
- i0.ɵɵdomElementStart(0, "button", 4);
14
- i0.ɵɵdomListener("click", function PrismSidebarComponent_For_2_For_4_Template_button_click_0_listener() { const item_r2 = i0.ɵɵrestoreView(_r1).$implicit; const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.onSelect(item_r2)); });
15
- i0.ɵɵconditionalCreate(1, PrismSidebarComponent_For_2_For_4_Conditional_1_Template, 2, 0, "span", 5);
16
- i0.ɵɵtext(2);
17
- i0.ɵɵdomElementEnd();
29
+ i0.ɵɵelementStart(0, "div", 2)(1, "button", 3);
30
+ i0.ɵɵlistener("click", function PrismSidebarComponent_For_2_Conditional_0_Template_button_click_1_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); const group_r3 = ctx_r1.$implicit; const ɵ$index_3_r4 = ctx_r1.$index; const ctx_r4 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r4.toggle("group:" + group_r3.name, ɵ$index_3_r4)); });
31
+ i0.ɵɵnamespaceSVG();
32
+ i0.ɵɵelementStart(2, "svg", 4);
33
+ i0.ɵɵelement(3, "path", 5);
34
+ i0.ɵɵelementEnd();
35
+ i0.ɵɵtext(4);
36
+ i0.ɵɵelementEnd();
37
+ i0.ɵɵconditionalCreate(5, PrismSidebarComponent_For_2_Conditional_0_Conditional_5_Template, 2, 0);
38
+ i0.ɵɵelementEnd();
18
39
  } if (rf & 2) {
19
- const item_r2 = ctx.$implicit;
20
- const ctx_r2 = i0.ɵɵnextContext(2);
21
- i0.ɵɵclassProp("prism-sidebar__item--active", ctx_r2.isActive(item_r2))("prism-sidebar__item--page", item_r2.kind === "page");
22
- i0.ɵɵadvance();
23
- i0.ɵɵconditional(item_r2.kind === "page" ? 1 : -1);
40
+ const ctx_r1 = i0.ɵɵnextContext();
41
+ const group_r3 = ctx_r1.$implicit;
42
+ const ɵ$index_3_r4 = ctx_r1.$index;
43
+ const ctx_r4 = i0.ɵɵnextContext();
44
+ i0.ɵɵadvance(2);
45
+ i0.ɵɵclassProp("prism-sidebar__chevron--expanded", ctx_r4.isExpanded("group:" + group_r3.name, ɵ$index_3_r4));
46
+ i0.ɵɵadvance(2);
47
+ i0.ɵɵtextInterpolate1(" ", group_r3.name, " ");
24
48
  i0.ɵɵadvance();
25
- i0.ɵɵtextInterpolate1(" ", ctx_r2.itemLabel(item_r2), " ");
49
+ i0.ɵɵconditional(ctx_r4.isExpanded("group:" + group_r3.name, ɵ$index_3_r4) ? 5 : -1);
50
+ } }
51
+ function PrismSidebarComponent_For_2_Conditional_1_For_1_ng_container_0_Template(rf, ctx) { if (rf & 1) {
52
+ i0.ɵɵelementContainer(0);
53
+ } }
54
+ function PrismSidebarComponent_For_2_Conditional_1_For_1_Template(rf, ctx) { if (rf & 1) {
55
+ i0.ɵɵtemplate(0, PrismSidebarComponent_For_2_Conditional_1_For_1_ng_container_0_Template, 1, 0, "ng-container", 6);
56
+ } if (rf & 2) {
57
+ const cat_r8 = ctx.$implicit;
58
+ i0.ɵɵnextContext(3);
59
+ const categoryTpl_r7 = i0.ɵɵreference(4);
60
+ i0.ɵɵproperty("ngTemplateOutlet", categoryTpl_r7)("ngTemplateOutletContext", i0.ɵɵpureFunction1(2, _c1, cat_r8));
61
+ } }
62
+ function PrismSidebarComponent_For_2_Conditional_1_Template(rf, ctx) { if (rf & 1) {
63
+ i0.ɵɵrepeaterCreate(0, PrismSidebarComponent_For_2_Conditional_1_For_1_Template, 1, 4, "ng-container", null, _forTrack1);
64
+ } if (rf & 2) {
65
+ const group_r3 = i0.ɵɵnextContext().$implicit;
66
+ i0.ɵɵrepeater(group_r3.categories);
26
67
  } }
27
68
  function PrismSidebarComponent_For_2_Template(rf, ctx) { if (rf & 1) {
28
- i0.ɵɵdomElementStart(0, "div", 1)(1, "h3", 2);
69
+ i0.ɵɵconditionalCreate(0, PrismSidebarComponent_For_2_Conditional_0_Template, 6, 4, "div", 2)(1, PrismSidebarComponent_For_2_Conditional_1_Template, 2, 0);
70
+ } if (rf & 2) {
71
+ const group_r3 = ctx.$implicit;
72
+ i0.ɵɵconditional(group_r3.name ? 0 : 1);
73
+ } }
74
+ function PrismSidebarComponent_ng_template_3_Conditional_5_For_1_Conditional_1_Template(rf, ctx) { if (rf & 1) {
75
+ i0.ɵɵelementStart(0, "span", 11);
76
+ i0.ɵɵtext(1, "\u25C8");
77
+ i0.ɵɵelementEnd();
78
+ } }
79
+ function PrismSidebarComponent_ng_template_3_Conditional_5_For_1_Template(rf, ctx) { if (rf & 1) {
80
+ const _r11 = i0.ɵɵgetCurrentView();
81
+ i0.ɵɵelementStart(0, "button", 10);
82
+ i0.ɵɵlistener("click", function PrismSidebarComponent_ng_template_3_Conditional_5_For_1_Template_button_click_0_listener() { const item_r12 = i0.ɵɵrestoreView(_r11).$implicit; const ctx_r4 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r4.onSelect(item_r12)); });
83
+ i0.ɵɵconditionalCreate(1, PrismSidebarComponent_ng_template_3_Conditional_5_For_1_Conditional_1_Template, 2, 0, "span", 11);
29
84
  i0.ɵɵtext(2);
30
- i0.ɵɵdomElementEnd();
31
- i0.ɵɵrepeaterCreate(3, PrismSidebarComponent_For_2_For_4_Template, 3, 6, "button", 3, _forTrack1, true);
32
- i0.ɵɵdomElementEnd();
85
+ i0.ɵɵelementEnd();
86
+ } if (rf & 2) {
87
+ const item_r12 = ctx.$implicit;
88
+ const nested_r13 = i0.ɵɵnextContext(2).nested;
89
+ const ctx_r4 = i0.ɵɵnextContext();
90
+ i0.ɵɵclassProp("prism-sidebar__item--active", ctx_r4.isActive(item_r12))("prism-sidebar__item--nested", nested_r13)("prism-sidebar__item--page", item_r12.kind === "page");
91
+ i0.ɵɵadvance();
92
+ i0.ɵɵconditional(item_r12.kind === "page" ? 1 : -1);
93
+ i0.ɵɵadvance();
94
+ i0.ɵɵtextInterpolate1(" ", ctx_r4.itemLabel(item_r12), " ");
95
+ } }
96
+ function PrismSidebarComponent_ng_template_3_Conditional_5_Template(rf, ctx) { if (rf & 1) {
97
+ i0.ɵɵrepeaterCreate(0, PrismSidebarComponent_ng_template_3_Conditional_5_For_1_Template, 3, 8, "button", 9, _forTrack2, true);
33
98
  } if (rf & 2) {
34
- const entry_r4 = ctx.$implicit;
99
+ const cat_r10 = i0.ɵɵnextContext().cat;
100
+ i0.ɵɵrepeater(cat_r10.items);
101
+ } }
102
+ function PrismSidebarComponent_ng_template_3_Template(rf, ctx) { if (rf & 1) {
103
+ const _r9 = i0.ɵɵgetCurrentView();
104
+ i0.ɵɵelementStart(0, "div", 7)(1, "button", 8);
105
+ i0.ɵɵlistener("click", function PrismSidebarComponent_ng_template_3_Template_button_click_1_listener() { const cat_r10 = i0.ɵɵrestoreView(_r9).cat; const ctx_r4 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r4.toggle("cat:" + cat_r10.name, 0)); });
106
+ i0.ɵɵnamespaceSVG();
107
+ i0.ɵɵelementStart(2, "svg", 4);
108
+ i0.ɵɵelement(3, "path", 5);
109
+ i0.ɵɵelementEnd();
110
+ i0.ɵɵtext(4);
111
+ i0.ɵɵelementEnd();
112
+ i0.ɵɵconditionalCreate(5, PrismSidebarComponent_ng_template_3_Conditional_5_Template, 2, 0);
113
+ i0.ɵɵelementEnd();
114
+ } if (rf & 2) {
115
+ const cat_r10 = ctx.cat;
116
+ const nested_r13 = ctx.nested;
117
+ const ctx_r4 = i0.ɵɵnextContext();
118
+ i0.ɵɵclassProp("prism-sidebar__category--nested", nested_r13);
119
+ i0.ɵɵadvance();
120
+ i0.ɵɵclassProp("prism-sidebar__category-title--nested", nested_r13);
121
+ i0.ɵɵadvance();
122
+ i0.ɵɵclassProp("prism-sidebar__chevron--expanded", ctx_r4.isExpanded("cat:" + cat_r10.name, 0));
35
123
  i0.ɵɵadvance(2);
36
- i0.ɵɵtextInterpolate(entry_r4[0]);
124
+ i0.ɵɵtextInterpolate1(" ", cat_r10.name, " ");
37
125
  i0.ɵɵadvance();
38
- i0.ɵɵrepeater(entry_r4[1]);
126
+ i0.ɵɵconditional(ctx_r4.isExpanded("cat:" + cat_r10.name, 0) ? 5 : -1);
39
127
  } }
128
+ const STORAGE_KEY = 'ng-prism-sidebar-expanded';
40
129
  export class PrismSidebarComponent {
41
130
  navigationService = inject(PrismNavigationService);
42
- entries = computed(() => [...this.navigationService.categoryTree().entries()], ...(ngDevMode ? [{ debugName: "entries" }] : []));
131
+ expandedSet = signal(this.loadExpanded(), ...(ngDevMode ? [{ debugName: "expandedSet" }] : []));
132
+ groups = computed(() => {
133
+ const tree = this.navigationService.categoryTree();
134
+ const groupMap = new Map();
135
+ for (const [fullCategory, items] of tree.entries()) {
136
+ const sepIdx = fullCategory.indexOf(' / ');
137
+ let groupName;
138
+ let catName;
139
+ if (sepIdx !== -1) {
140
+ groupName = fullCategory.substring(0, sepIdx).trim();
141
+ catName = fullCategory.substring(sepIdx + 3).trim();
142
+ }
143
+ else {
144
+ groupName = null;
145
+ catName = fullCategory;
146
+ }
147
+ const cats = groupMap.get(groupName) ?? [];
148
+ cats.push({ name: catName, items });
149
+ groupMap.set(groupName, cats);
150
+ }
151
+ const result = [];
152
+ const ungrouped = groupMap.get(null);
153
+ if (ungrouped) {
154
+ result.push({ name: null, categories: ungrouped });
155
+ groupMap.delete(null);
156
+ }
157
+ for (const [name, categories] of groupMap.entries()) {
158
+ result.push({ name, categories });
159
+ }
160
+ return result;
161
+ }, ...(ngDevMode ? [{ debugName: "groups" }] : []));
162
+ isExpanded(key, index) {
163
+ const set = this.expandedSet();
164
+ if (set.has(key))
165
+ return true;
166
+ if (set.has(`__collapsed:${key}`))
167
+ return false;
168
+ return index === 0;
169
+ }
170
+ toggle(key, index) {
171
+ this.expandedSet.update(prev => {
172
+ const next = new Set(prev);
173
+ const collapsedKey = `__collapsed:${key}`;
174
+ if (next.has(key)) {
175
+ next.delete(key);
176
+ next.add(collapsedKey);
177
+ }
178
+ else if (next.has(collapsedKey)) {
179
+ next.delete(collapsedKey);
180
+ next.add(key);
181
+ }
182
+ else if (index === 0) {
183
+ next.add(collapsedKey);
184
+ }
185
+ else {
186
+ next.add(key);
187
+ }
188
+ this.saveExpanded(next);
189
+ return next;
190
+ });
191
+ }
43
192
  itemKey(item) {
44
193
  return item.kind === 'component'
45
194
  ? item.data.meta.className
@@ -67,39 +216,100 @@ export class PrismSidebarComponent {
67
216
  this.navigationService.selectPage(item.data);
68
217
  }
69
218
  }
219
+ loadExpanded() {
220
+ try {
221
+ const stored = localStorage.getItem(STORAGE_KEY);
222
+ return stored ? new Set(JSON.parse(stored)) : new Set();
223
+ }
224
+ catch {
225
+ return new Set();
226
+ }
227
+ }
228
+ saveExpanded(set) {
229
+ try {
230
+ localStorage.setItem(STORAGE_KEY, JSON.stringify([...set]));
231
+ }
232
+ catch { /* ignore */ }
233
+ }
70
234
  static ɵfac = function PrismSidebarComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || PrismSidebarComponent)(); };
71
- static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: PrismSidebarComponent, selectors: [["prism-sidebar"]], decls: 3, vars: 0, consts: [[1, "prism-sidebar"], [1, "prism-sidebar__category"], [1, "prism-sidebar__category-title"], [1, "prism-sidebar__item", 3, "prism-sidebar__item--active", "prism-sidebar__item--page"], [1, "prism-sidebar__item", 3, "click"], [1, "prism-sidebar__page-icon"]], template: function PrismSidebarComponent_Template(rf, ctx) { if (rf & 1) {
72
- i0.ɵɵdomElementStart(0, "nav", 0);
73
- i0.ɵɵrepeaterCreate(1, PrismSidebarComponent_For_2_Template, 5, 1, "div", 1, _forTrack0);
74
- i0.ɵɵdomElementEnd();
235
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: PrismSidebarComponent, selectors: [["prism-sidebar"]], decls: 5, vars: 0, consts: [["categoryTpl", ""], [1, "prism-sidebar"], [1, "prism-sidebar__group"], [1, "prism-sidebar__group-title", 3, "click"], ["viewBox", "0 0 16 16", 1, "prism-sidebar__chevron"], ["d", "M6 4l4 4-4 4"], [4, "ngTemplateOutlet", "ngTemplateOutletContext"], [1, "prism-sidebar__category"], [1, "prism-sidebar__category-title", 3, "click"], [1, "prism-sidebar__item", 3, "prism-sidebar__item--active", "prism-sidebar__item--nested", "prism-sidebar__item--page"], [1, "prism-sidebar__item", 3, "click"], [1, "prism-sidebar__page-icon"]], template: function PrismSidebarComponent_Template(rf, ctx) { if (rf & 1) {
236
+ i0.ɵɵelementStart(0, "nav", 1);
237
+ i0.ɵɵrepeaterCreate(1, PrismSidebarComponent_For_2_Template, 2, 1, null, null, _forTrack0);
238
+ i0.ɵɵtemplate(3, PrismSidebarComponent_ng_template_3_Template, 6, 8, "ng-template", null, 0, i0.ɵɵtemplateRefExtractor);
239
+ i0.ɵɵelementEnd();
75
240
  } if (rf & 2) {
76
241
  i0.ɵɵadvance();
77
- i0.ɵɵrepeater(ctx.entries());
78
- } }, styles: [".prism-sidebar[_ngcontent-%COMP%] {\n background: var(--prism-bg);\n border-right: 1px solid var(--prism-border);\n overflow-y: auto;\n overflow-x: hidden;\n padding: 8px 0;\n height: 100%;\n }\n .prism-sidebar[_ngcontent-%COMP%]::-webkit-scrollbar { width: 4px; }\n .prism-sidebar[_ngcontent-%COMP%]::-webkit-scrollbar-track { background: transparent; }\n .prism-sidebar[_ngcontent-%COMP%]::-webkit-scrollbar-thumb { background: var(--prism-border-strong); border-radius: 2px; }\n\n .prism-sidebar__category[_ngcontent-%COMP%] { margin-bottom: 4px; }\n\n .prism-sidebar__category-title[_ngcontent-%COMP%] {\n margin: 0;\n padding: 10px 16px 4px;\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--prism-text-ghost);\n font-family: var(--prism-font-sans);\n }\n\n .prism-sidebar__item[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n width: 100%;\n padding: 6px 14px 6px 11px;\n font-size: 13px;\n font-family: var(--prism-font-sans);\n border: none;\n background: none;\n color: var(--prism-text-muted);\n cursor: pointer;\n border-left: 3px solid transparent;\n text-align: left;\n transition: background 0.1s, color 0.1s, border-color 0.1s;\n }\n\n .prism-sidebar__item[_ngcontent-%COMP%]:hover {\n background: color-mix(in srgb, var(--prism-primary) 6%, transparent);\n color: var(--prism-text-2);\n }\n\n .prism-sidebar__item--active[_ngcontent-%COMP%] {\n border-left-color: var(--prism-primary);\n background: color-mix(in srgb, var(--prism-primary) 10%, transparent);\n color: var(--prism-text);\n font-weight: 500;\n }\n\n .prism-sidebar__item--active[_ngcontent-%COMP%]:hover {\n background: color-mix(in srgb, var(--prism-primary) 12%, transparent);\n }\n\n .prism-sidebar__page-icon[_ngcontent-%COMP%] {\n font-size: 10px;\n opacity: 0.6;\n }"] });
242
+ i0.ɵɵrepeater(ctx.groups());
243
+ } }, dependencies: [NgTemplateOutlet], styles: [".prism-sidebar[_ngcontent-%COMP%] {\n background: var(--prism-sidebar-bg, var(--prism-bg));\n border-right: 1px solid var(--prism-border);\n overflow-y: auto;\n overflow-x: hidden;\n padding: 4px 0;\n height: 100%;\n }\n .prism-sidebar[_ngcontent-%COMP%]::-webkit-scrollbar { width: 4px; }\n .prism-sidebar[_ngcontent-%COMP%]::-webkit-scrollbar-track { background: transparent; }\n .prism-sidebar[_ngcontent-%COMP%]::-webkit-scrollbar-thumb { background: var(--prism-border-strong); border-radius: 2px; }\n\n .prism-sidebar__group[_ngcontent-%COMP%] { margin-bottom: 2px; }\n\n .prism-sidebar__group-title[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n width: 100%;\n margin: 0;\n padding: 8px 16px;\n font-size: 11px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--prism-text-2);\n font-family: var(--prism-font-sans);\n background: color-mix(in srgb, var(--prism-primary) 10%, transparent);\n border: none;\n border-left: 2px solid color-mix(in srgb, var(--prism-primary) 50%, transparent);\n cursor: pointer;\n text-align: left;\n transition: background 0.12s, color 0.12s, border-color 0.12s;\n }\n\n .prism-sidebar__group-title[_ngcontent-%COMP%]:hover {\n background: color-mix(in srgb, var(--prism-primary) 16%, transparent);\n color: var(--prism-text);\n border-left-color: var(--prism-primary);\n }\n\n .prism-sidebar__category[_ngcontent-%COMP%] { margin-bottom: 2px; }\n\n .prism-sidebar__category-title[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n width: 100%;\n margin: 0;\n padding: 7px 16px;\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--prism-text-2);\n font-family: var(--prism-font-sans);\n background: color-mix(in srgb, var(--prism-primary) 8%, transparent);\n border: none;\n border-left: 2px solid color-mix(in srgb, var(--prism-primary) 40%, transparent);\n cursor: pointer;\n text-align: left;\n transition: background 0.12s, color 0.12s, border-color 0.12s;\n }\n\n .prism-sidebar__category-title[_ngcontent-%COMP%]:hover {\n background: color-mix(in srgb, var(--prism-primary) 14%, transparent);\n color: var(--prism-text);\n border-left-color: var(--prism-primary);\n }\n\n .prism-sidebar__category-title--nested[_ngcontent-%COMP%] {\n padding-left: 28px;\n font-weight: 500;\n font-size: 11px;\n background: color-mix(in srgb, var(--prism-primary) 4%, transparent);\n border-left-color: color-mix(in srgb, var(--prism-primary) 25%, transparent);\n }\n\n .prism-sidebar__category-title--nested[_ngcontent-%COMP%]:hover {\n background: color-mix(in srgb, var(--prism-primary) 10%, transparent);\n }\n\n .prism-sidebar__chevron[_ngcontent-%COMP%] {\n width: 10px;\n height: 10px;\n flex-shrink: 0;\n transition: transform 0.15s;\n fill: none;\n stroke: currentColor;\n stroke-width: 1.5;\n stroke-linecap: round;\n stroke-linejoin: round;\n opacity: 0.5;\n }\n\n .prism-sidebar__group-title[_ngcontent-%COMP%]:hover .prism-sidebar__chevron[_ngcontent-%COMP%], \n .prism-sidebar__category-title[_ngcontent-%COMP%]:hover .prism-sidebar__chevron[_ngcontent-%COMP%] {\n opacity: 0.8;\n }\n\n .prism-sidebar__chevron--expanded[_ngcontent-%COMP%] {\n transform: rotate(90deg);\n }\n\n .prism-sidebar__item[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n width: 100%;\n padding: 6px 14px 6px 24px;\n font-size: 13px;\n font-family: var(--prism-font-sans);\n border: none;\n background: none;\n color: var(--prism-text-muted);\n cursor: pointer;\n border-left: 2px solid transparent;\n text-align: left;\n transition: background 0.1s, color 0.1s, border-color 0.1s;\n }\n\n .prism-sidebar__item--nested[_ngcontent-%COMP%] {\n padding-left: 36px;\n }\n\n .prism-sidebar__item[_ngcontent-%COMP%]:hover {\n background: color-mix(in srgb, var(--prism-primary) 6%, transparent);\n color: var(--prism-text-2);\n }\n\n .prism-sidebar__item--active[_ngcontent-%COMP%] {\n border-left-color: var(--prism-primary);\n background: color-mix(in srgb, var(--prism-primary) 10%, transparent);\n color: var(--prism-text);\n font-weight: 500;\n }\n\n .prism-sidebar__item--active[_ngcontent-%COMP%]:hover {\n background: color-mix(in srgb, var(--prism-primary) 12%, transparent);\n }\n\n .prism-sidebar__page-icon[_ngcontent-%COMP%] {\n font-size: 10px;\n opacity: 0.6;\n }"] });
79
244
  }
80
245
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(PrismSidebarComponent, [{
81
246
  type: Component,
82
- args: [{ selector: 'prism-sidebar', standalone: true, template: `
247
+ args: [{ selector: 'prism-sidebar', standalone: true, imports: [NgTemplateOutlet], template: `
83
248
  <nav class="prism-sidebar">
84
- @for (entry of entries(); track entry[0]) {
85
- <div class="prism-sidebar__category">
86
- <h3 class="prism-sidebar__category-title">{{ entry[0] }}</h3>
87
- @for (item of entry[1]; track itemKey(item)) {
249
+ @for (group of groups(); track group.name ?? '__ungrouped'; let gi = $index) {
250
+ @if (group.name) {
251
+ <div class="prism-sidebar__group">
88
252
  <button
89
- class="prism-sidebar__item"
90
- [class.prism-sidebar__item--active]="isActive(item)"
91
- [class.prism-sidebar__item--page]="item.kind === 'page'"
92
- (click)="onSelect(item)"
253
+ class="prism-sidebar__group-title"
254
+ (click)="toggle('group:' + group.name, gi)"
93
255
  >
94
- @if (item.kind === 'page') {
95
- <span class="prism-sidebar__page-icon">◈</span>
96
- }
97
- {{ itemLabel(item) }}
256
+ <svg
257
+ class="prism-sidebar__chevron"
258
+ [class.prism-sidebar__chevron--expanded]="isExpanded('group:' + group.name, gi)"
259
+ viewBox="0 0 16 16"
260
+ >
261
+ <path d="M6 4l4 4-4 4"/>
262
+ </svg>
263
+ {{ group.name }}
98
264
  </button>
265
+ @if (isExpanded('group:' + group.name, gi)) {
266
+ @for (cat of group.categories; track cat.name) {
267
+ <ng-container *ngTemplateOutlet="categoryTpl; context: { cat, nested: true }" />
268
+ }
269
+ }
270
+ </div>
271
+ } @else {
272
+ @for (cat of group.categories; track cat.name) {
273
+ <ng-container *ngTemplateOutlet="categoryTpl; context: { cat, nested: false }" />
99
274
  }
100
- </div>
275
+ }
101
276
  }
277
+
278
+ <ng-template #categoryTpl let-cat="cat" let-nested="nested">
279
+ <div class="prism-sidebar__category" [class.prism-sidebar__category--nested]="nested">
280
+ <button
281
+ class="prism-sidebar__category-title"
282
+ [class.prism-sidebar__category-title--nested]="nested"
283
+ (click)="toggle('cat:' + cat.name, 0)"
284
+ >
285
+ <svg
286
+ class="prism-sidebar__chevron"
287
+ [class.prism-sidebar__chevron--expanded]="isExpanded('cat:' + cat.name, 0)"
288
+ viewBox="0 0 16 16"
289
+ >
290
+ <path d="M6 4l4 4-4 4"/>
291
+ </svg>
292
+ {{ cat.name }}
293
+ </button>
294
+ @if (isExpanded('cat:' + cat.name, 0)) {
295
+ @for (item of cat.items; track itemKey(item)) {
296
+ <button
297
+ class="prism-sidebar__item"
298
+ [class.prism-sidebar__item--active]="isActive(item)"
299
+ [class.prism-sidebar__item--nested]="nested"
300
+ [class.prism-sidebar__item--page]="item.kind === 'page'"
301
+ (click)="onSelect(item)"
302
+ >
303
+ @if (item.kind === 'page') {
304
+ <span class="prism-sidebar__page-icon">◈</span>
305
+ }
306
+ {{ itemLabel(item) }}
307
+ </button>
308
+ }
309
+ }
310
+ </div>
311
+ </ng-template>
102
312
  </nav>
103
- `, styles: ["\n .prism-sidebar {\n background: var(--prism-bg);\n border-right: 1px solid var(--prism-border);\n overflow-y: auto;\n overflow-x: hidden;\n padding: 8px 0;\n height: 100%;\n }\n .prism-sidebar::-webkit-scrollbar { width: 4px; }\n .prism-sidebar::-webkit-scrollbar-track { background: transparent; }\n .prism-sidebar::-webkit-scrollbar-thumb { background: var(--prism-border-strong); border-radius: 2px; }\n\n .prism-sidebar__category { margin-bottom: 4px; }\n\n .prism-sidebar__category-title {\n margin: 0;\n padding: 10px 16px 4px;\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--prism-text-ghost);\n font-family: var(--prism-font-sans);\n }\n\n .prism-sidebar__item {\n display: flex;\n align-items: center;\n gap: 6px;\n width: 100%;\n padding: 6px 14px 6px 11px;\n font-size: 13px;\n font-family: var(--prism-font-sans);\n border: none;\n background: none;\n color: var(--prism-text-muted);\n cursor: pointer;\n border-left: 3px solid transparent;\n text-align: left;\n transition: background 0.1s, color 0.1s, border-color 0.1s;\n }\n\n .prism-sidebar__item:hover {\n background: color-mix(in srgb, var(--prism-primary) 6%, transparent);\n color: var(--prism-text-2);\n }\n\n .prism-sidebar__item--active {\n border-left-color: var(--prism-primary);\n background: color-mix(in srgb, var(--prism-primary) 10%, transparent);\n color: var(--prism-text);\n font-weight: 500;\n }\n\n .prism-sidebar__item--active:hover {\n background: color-mix(in srgb, var(--prism-primary) 12%, transparent);\n }\n\n .prism-sidebar__page-icon {\n font-size: 10px;\n opacity: 0.6;\n }\n "] }]
313
+ `, styles: ["\n .prism-sidebar {\n background: var(--prism-sidebar-bg, var(--prism-bg));\n border-right: 1px solid var(--prism-border);\n overflow-y: auto;\n overflow-x: hidden;\n padding: 4px 0;\n height: 100%;\n }\n .prism-sidebar::-webkit-scrollbar { width: 4px; }\n .prism-sidebar::-webkit-scrollbar-track { background: transparent; }\n .prism-sidebar::-webkit-scrollbar-thumb { background: var(--prism-border-strong); border-radius: 2px; }\n\n .prism-sidebar__group { margin-bottom: 2px; }\n\n .prism-sidebar__group-title {\n display: flex;\n align-items: center;\n gap: 6px;\n width: 100%;\n margin: 0;\n padding: 8px 16px;\n font-size: 11px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--prism-text-2);\n font-family: var(--prism-font-sans);\n background: color-mix(in srgb, var(--prism-primary) 10%, transparent);\n border: none;\n border-left: 2px solid color-mix(in srgb, var(--prism-primary) 50%, transparent);\n cursor: pointer;\n text-align: left;\n transition: background 0.12s, color 0.12s, border-color 0.12s;\n }\n\n .prism-sidebar__group-title:hover {\n background: color-mix(in srgb, var(--prism-primary) 16%, transparent);\n color: var(--prism-text);\n border-left-color: var(--prism-primary);\n }\n\n .prism-sidebar__category { margin-bottom: 2px; }\n\n .prism-sidebar__category-title {\n display: flex;\n align-items: center;\n gap: 6px;\n width: 100%;\n margin: 0;\n padding: 7px 16px;\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--prism-text-2);\n font-family: var(--prism-font-sans);\n background: color-mix(in srgb, var(--prism-primary) 8%, transparent);\n border: none;\n border-left: 2px solid color-mix(in srgb, var(--prism-primary) 40%, transparent);\n cursor: pointer;\n text-align: left;\n transition: background 0.12s, color 0.12s, border-color 0.12s;\n }\n\n .prism-sidebar__category-title:hover {\n background: color-mix(in srgb, var(--prism-primary) 14%, transparent);\n color: var(--prism-text);\n border-left-color: var(--prism-primary);\n }\n\n .prism-sidebar__category-title--nested {\n padding-left: 28px;\n font-weight: 500;\n font-size: 11px;\n background: color-mix(in srgb, var(--prism-primary) 4%, transparent);\n border-left-color: color-mix(in srgb, var(--prism-primary) 25%, transparent);\n }\n\n .prism-sidebar__category-title--nested:hover {\n background: color-mix(in srgb, var(--prism-primary) 10%, transparent);\n }\n\n .prism-sidebar__chevron {\n width: 10px;\n height: 10px;\n flex-shrink: 0;\n transition: transform 0.15s;\n fill: none;\n stroke: currentColor;\n stroke-width: 1.5;\n stroke-linecap: round;\n stroke-linejoin: round;\n opacity: 0.5;\n }\n\n .prism-sidebar__group-title:hover .prism-sidebar__chevron,\n .prism-sidebar__category-title:hover .prism-sidebar__chevron {\n opacity: 0.8;\n }\n\n .prism-sidebar__chevron--expanded {\n transform: rotate(90deg);\n }\n\n .prism-sidebar__item {\n display: flex;\n align-items: center;\n gap: 6px;\n width: 100%;\n padding: 6px 14px 6px 24px;\n font-size: 13px;\n font-family: var(--prism-font-sans);\n border: none;\n background: none;\n color: var(--prism-text-muted);\n cursor: pointer;\n border-left: 2px solid transparent;\n text-align: left;\n transition: background 0.1s, color 0.1s, border-color 0.1s;\n }\n\n .prism-sidebar__item--nested {\n padding-left: 36px;\n }\n\n .prism-sidebar__item:hover {\n background: color-mix(in srgb, var(--prism-primary) 6%, transparent);\n color: var(--prism-text-2);\n }\n\n .prism-sidebar__item--active {\n border-left-color: var(--prism-primary);\n background: color-mix(in srgb, var(--prism-primary) 10%, transparent);\n color: var(--prism-text);\n font-weight: 500;\n }\n\n .prism-sidebar__item--active:hover {\n background: color-mix(in srgb, var(--prism-primary) 12%, transparent);\n }\n\n .prism-sidebar__page-icon {\n font-size: 10px;\n opacity: 0.6;\n }\n "] }]
104
314
  }], null, null); })();
105
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PrismSidebarComponent, { className: "PrismSidebarComponent", filePath: "app/sidebar/prism-sidebar.component.ts", lineNumber: 95 }); })();
315
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PrismSidebarComponent, { className: "PrismSidebarComponent", filePath: "app/sidebar/prism-sidebar.component.ts", lineNumber: 237 }); })();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ng-prism/core",
3
- "version": "21.1.0",
3
+ "version": "21.3.0",
4
4
  "description": "Lightweight Angular-native component showcase — no story files needed.",
5
5
  "keywords": [
6
6
  "angular",