@kanso-protocol/sidebar 0.1.0 → 0.5.3
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.
|
@@ -66,9 +66,10 @@ class KpSidebarComponent {
|
|
|
66
66
|
<path d="M3 7l9-4 9 4v10l-9 4-9-4V7zM3 7l9 4 9-4M12 11v10"/>
|
|
67
67
|
</svg>
|
|
68
68
|
</span>
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
<span
|
|
70
|
+
class="kp-sidebar__logo-text"
|
|
71
|
+
[class.kp-sidebar__logo-text--hidden]="widthState === 'collapsed'"
|
|
72
|
+
[attr.aria-hidden]="widthState === 'collapsed' || null">{{ logoText }}</span>
|
|
72
73
|
</ng-content>
|
|
73
74
|
</div>
|
|
74
75
|
}
|
|
@@ -89,8 +90,11 @@ class KpSidebarComponent {
|
|
|
89
90
|
<nav class="kp-sidebar__nav">
|
|
90
91
|
@for (section of sections; track $index) {
|
|
91
92
|
<div class="kp-sidebar__section">
|
|
92
|
-
@if (showSectionLabels && section.label
|
|
93
|
-
<div
|
|
93
|
+
@if (showSectionLabels && section.label) {
|
|
94
|
+
<div
|
|
95
|
+
class="kp-sidebar__section-label"
|
|
96
|
+
[class.kp-sidebar__section-label--hidden]="widthState === 'collapsed'"
|
|
97
|
+
[attr.aria-hidden]="widthState === 'collapsed' || null">{{ section.label }}</div>
|
|
94
98
|
}
|
|
95
99
|
@for (item of section.items; track item.label) {
|
|
96
100
|
<kp-nav-item
|
|
@@ -130,20 +134,24 @@ class KpSidebarComponent {
|
|
|
130
134
|
</nav>
|
|
131
135
|
|
|
132
136
|
@if (showUserFooter && (userName || userInitials)) {
|
|
133
|
-
<div class="kp-sidebar__footer">
|
|
137
|
+
<div class="kp-sidebar__footer" [class.kp-sidebar__footer--collapsed]="widthState === 'collapsed'">
|
|
134
138
|
<kp-avatar size="md" [initials]="userInitials || null" [showStatus]="true" status="online"/>
|
|
135
|
-
|
|
136
|
-
<
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
139
|
+
<div class="kp-sidebar__footer-text" [attr.aria-hidden]="widthState === 'collapsed' || null">
|
|
140
|
+
@if (userName) { <span class="kp-sidebar__footer-name">{{ userName }}</span> }
|
|
141
|
+
@if (userEmail) { <span class="kp-sidebar__footer-email">{{ userEmail }}</span> }
|
|
142
|
+
</div>
|
|
143
|
+
<button
|
|
144
|
+
type="button"
|
|
145
|
+
class="kp-sidebar__footer-menu"
|
|
146
|
+
aria-label="User options"
|
|
147
|
+
[attr.aria-hidden]="widthState === 'collapsed' || null"
|
|
148
|
+
[tabindex]="widthState === 'collapsed' ? -1 : 0"
|
|
149
|
+
(click)="userMenuClick.emit()">
|
|
150
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="5" r="1"/><circle cx="12" cy="12" r="1"/><circle cx="12" cy="19" r="1"/></svg>
|
|
151
|
+
</button>
|
|
144
152
|
</div>
|
|
145
153
|
}
|
|
146
|
-
`, isInline: true, styles: [":host{box-sizing:border-box;display:flex;flex-direction:column;width:var(--kp-sidebar-w, 240px);height:100%;min-height:100vh;background:var(--kp-color-sidebar-bg
|
|
154
|
+
`, isInline: true, styles: [":host{box-sizing:border-box;display:flex;flex-direction:column;width:var(--kp-sidebar-w, 240px);height:100%;min-height:100vh;background:var(--kp-color-sidebar-bg);border-inline-end:1px solid var(--kp-color-sidebar-border);font-family:var(--kp-font-family-sans, \"Onest\", system-ui, sans-serif);transition:width var(--kp-motion-duration-normal) cubic-bezier(.4,0,.2,1)}:host(.kp-sidebar--expanded){--kp-sidebar-w: 240px}:host(.kp-sidebar--collapsed){--kp-sidebar-w: 64px}:host(.kp-sidebar--dark){background:var(--kp-color-sidebar-bg-dark);color:var(--kp-color-fg-on-dark-strong);border-right-color:var(--kp-color-border-on-dark-default)}.kp-sidebar__top{display:flex;flex-direction:column;gap:16px;padding:16px 16px 8px}.kp-sidebar__top-row{display:flex;align-items:center;gap:12px;min-height:40px}:host(.kp-sidebar--collapsed) .kp-sidebar__top-row{position:relative;justify-content:center}:host(.kp-sidebar--collapsed) .kp-sidebar__logo,:host(.kp-sidebar--collapsed) .kp-sidebar__toggle{transition:opacity var(--kp-motion-duration-fast) ease}:host(.kp-sidebar--collapsed) .kp-sidebar__top-row:has(.kp-sidebar__logo) .kp-sidebar__toggle{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);opacity:0;pointer-events:none}:host(.kp-sidebar--collapsed) .kp-sidebar__top-row:has(.kp-sidebar__logo):hover .kp-sidebar__logo{opacity:0}:host(.kp-sidebar--collapsed) .kp-sidebar__top-row:has(.kp-sidebar__logo):hover .kp-sidebar__toggle{opacity:1;pointer-events:auto}.kp-sidebar__logo{display:flex;align-items:center;gap:12px;flex:1 1 auto;min-width:0}.kp-sidebar__logo-mark{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:8px;background:var(--kp-color-primary-default-bg-rest);color:var(--kp-color-foreground-on-saturated);flex:0 0 auto}.kp-sidebar__logo-mark svg{width:60%;height:60%}.kp-sidebar__logo-text{font-size:16px;font-weight:600;flex:1 1 auto;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;opacity:1;max-width:200px;transition:opacity var(--kp-motion-duration-normal) cubic-bezier(.4,0,.2,1),max-width var(--kp-motion-duration-normal) cubic-bezier(.4,0,.2,1)}.kp-sidebar__logo-text--hidden{opacity:0;max-width:0;pointer-events:none}.kp-sidebar__toggle{all:unset;display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:6px;color:var(--kp-color-text-muted);cursor:pointer;transition:background var(--kp-motion-duration-fast) ease,color .12s ease}.kp-sidebar__toggle:hover{background:var(--kp-color-surface-muted);color:var(--kp-color-text-strong)}.kp-sidebar__toggle svg{width:16px;height:16px}:host(.kp-sidebar--dark) .kp-sidebar__toggle{color:var(--kp-color-fg-on-dark-muted)}:host(.kp-sidebar--dark) .kp-sidebar__toggle:hover{background:var(--kp-color-surface-on-dark-muted);color:var(--kp-color-foreground-on-saturated)}:host(.kp-sidebar--collapsed) .kp-sidebar__logo{justify-content:center}.kp-sidebar__nav{display:flex;flex-direction:column;gap:16px;padding:8px;flex:1 1 auto;overflow-y:auto}.kp-sidebar__section{display:flex;flex-direction:column;gap:2px}.kp-sidebar__nav ::ng-deep [kpNavItemIcon]{display:inline-flex;align-items:center;justify-content:center;line-height:0}.kp-sidebar__section-label{transition:opacity var(--kp-motion-duration-normal) cubic-bezier(.4,0,.2,1)}.kp-sidebar__section-label--hidden{opacity:0;pointer-events:none}.kp-sidebar__section-label{padding:8px 12px 4px;font-size:11px;font-weight:600;letter-spacing:.5px;text-transform:uppercase;color:var(--kp-color-sidebar-section-label)}.kp-sidebar__badge{display:inline-flex;align-items:center;justify-content:center;padding:1px 8px;height:20px;border-radius:999px;background:var(--kp-color-surface-muted);color:var(--kp-color-text-default);font-size:11px;font-weight:500}.kp-sidebar__footer{display:flex;align-items:center;gap:12px;padding:16px;border-top:1px solid var(--kp-color-sidebar-border)}:host(.kp-sidebar--dark) .kp-sidebar__footer{border-top-color:var(--kp-color-border-on-dark-default)}.kp-sidebar__footer-text{display:flex;flex-direction:column;gap:2px;flex:1 1 auto;min-width:0;opacity:1;max-width:200px;transition:opacity var(--kp-motion-duration-normal) cubic-bezier(.4,0,.2,1),max-width var(--kp-motion-duration-normal) cubic-bezier(.4,0,.2,1)}:host(.kp-sidebar--collapsed) .kp-sidebar__footer-text{opacity:0;max-width:0;pointer-events:none}.kp-sidebar__footer-name{font-size:13px;font-weight:500;color:var(--kp-color-text-strong);white-space:nowrap;text-overflow:ellipsis;overflow:hidden}:host(.kp-sidebar--dark) .kp-sidebar__footer-name{color:var(--kp-color-fg-on-dark-strong)}.kp-sidebar__footer-email{font-size:11px;color:var(--kp-color-text-muted);white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.kp-sidebar__footer-menu{all:unset;display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:4px;color:var(--kp-color-text-muted);cursor:pointer;opacity:1;max-width:28px;transition:opacity var(--kp-motion-duration-normal) cubic-bezier(.4,0,.2,1),max-width var(--kp-motion-duration-normal) cubic-bezier(.4,0,.2,1),background var(--kp-motion-duration-fast) ease,color var(--kp-motion-duration-fast) ease}.kp-sidebar__footer-menu:hover{color:var(--kp-color-text-strong);background:var(--kp-color-surface-muted)}.kp-sidebar__footer-menu svg{width:18px;height:18px}:host(.kp-sidebar--collapsed) .kp-sidebar__footer-menu{opacity:0;max-width:0;pointer-events:none}.kp-sidebar__footer{transition:padding var(--kp-motion-duration-normal) cubic-bezier(.4,0,.2,1)}:host(.kp-sidebar--collapsed) .kp-sidebar__footer{justify-content:center;padding-inline:0}\n"], dependencies: [{ kind: "component", type: KpAvatarComponent, selector: "kp-avatar", inputs: ["size", "shape", "appearance", "initials", "src", "alt", "showStatus", "status", "showRing", "ariaLabelOverride"] }, { kind: "component", type: KpIconComponent, selector: "kp-icon", inputs: ["name", "size"] }, { kind: "component", type: KpNavItemComponent, selector: "kp-nav-item", inputs: ["size", "depth", "label", "active", "disabled", "hasChildren", "expanded", "showIcon", "showBadge", "showActiveIndicator", "collapsed"], outputs: ["click$"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
147
155
|
}
|
|
148
156
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: KpSidebarComponent, decorators: [{
|
|
149
157
|
type: Component,
|
|
@@ -158,9 +166,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImpor
|
|
|
158
166
|
<path d="M3 7l9-4 9 4v10l-9 4-9-4V7zM3 7l9 4 9-4M12 11v10"/>
|
|
159
167
|
</svg>
|
|
160
168
|
</span>
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
169
|
+
<span
|
|
170
|
+
class="kp-sidebar__logo-text"
|
|
171
|
+
[class.kp-sidebar__logo-text--hidden]="widthState === 'collapsed'"
|
|
172
|
+
[attr.aria-hidden]="widthState === 'collapsed' || null">{{ logoText }}</span>
|
|
164
173
|
</ng-content>
|
|
165
174
|
</div>
|
|
166
175
|
}
|
|
@@ -181,8 +190,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImpor
|
|
|
181
190
|
<nav class="kp-sidebar__nav">
|
|
182
191
|
@for (section of sections; track $index) {
|
|
183
192
|
<div class="kp-sidebar__section">
|
|
184
|
-
@if (showSectionLabels && section.label
|
|
185
|
-
<div
|
|
193
|
+
@if (showSectionLabels && section.label) {
|
|
194
|
+
<div
|
|
195
|
+
class="kp-sidebar__section-label"
|
|
196
|
+
[class.kp-sidebar__section-label--hidden]="widthState === 'collapsed'"
|
|
197
|
+
[attr.aria-hidden]="widthState === 'collapsed' || null">{{ section.label }}</div>
|
|
186
198
|
}
|
|
187
199
|
@for (item of section.items; track item.label) {
|
|
188
200
|
<kp-nav-item
|
|
@@ -222,20 +234,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImpor
|
|
|
222
234
|
</nav>
|
|
223
235
|
|
|
224
236
|
@if (showUserFooter && (userName || userInitials)) {
|
|
225
|
-
<div class="kp-sidebar__footer">
|
|
237
|
+
<div class="kp-sidebar__footer" [class.kp-sidebar__footer--collapsed]="widthState === 'collapsed'">
|
|
226
238
|
<kp-avatar size="md" [initials]="userInitials || null" [showStatus]="true" status="online"/>
|
|
227
|
-
|
|
228
|
-
<
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
239
|
+
<div class="kp-sidebar__footer-text" [attr.aria-hidden]="widthState === 'collapsed' || null">
|
|
240
|
+
@if (userName) { <span class="kp-sidebar__footer-name">{{ userName }}</span> }
|
|
241
|
+
@if (userEmail) { <span class="kp-sidebar__footer-email">{{ userEmail }}</span> }
|
|
242
|
+
</div>
|
|
243
|
+
<button
|
|
244
|
+
type="button"
|
|
245
|
+
class="kp-sidebar__footer-menu"
|
|
246
|
+
aria-label="User options"
|
|
247
|
+
[attr.aria-hidden]="widthState === 'collapsed' || null"
|
|
248
|
+
[tabindex]="widthState === 'collapsed' ? -1 : 0"
|
|
249
|
+
(click)="userMenuClick.emit()">
|
|
250
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="5" r="1"/><circle cx="12" cy="12" r="1"/><circle cx="12" cy="19" r="1"/></svg>
|
|
251
|
+
</button>
|
|
236
252
|
</div>
|
|
237
253
|
}
|
|
238
|
-
`, styles: [":host{box-sizing:border-box;display:flex;flex-direction:column;width:var(--kp-sidebar-w, 240px);height:100%;min-height:100vh;background:var(--kp-color-sidebar-bg
|
|
254
|
+
`, styles: [":host{box-sizing:border-box;display:flex;flex-direction:column;width:var(--kp-sidebar-w, 240px);height:100%;min-height:100vh;background:var(--kp-color-sidebar-bg);border-inline-end:1px solid var(--kp-color-sidebar-border);font-family:var(--kp-font-family-sans, \"Onest\", system-ui, sans-serif);transition:width var(--kp-motion-duration-normal) cubic-bezier(.4,0,.2,1)}:host(.kp-sidebar--expanded){--kp-sidebar-w: 240px}:host(.kp-sidebar--collapsed){--kp-sidebar-w: 64px}:host(.kp-sidebar--dark){background:var(--kp-color-sidebar-bg-dark);color:var(--kp-color-fg-on-dark-strong);border-right-color:var(--kp-color-border-on-dark-default)}.kp-sidebar__top{display:flex;flex-direction:column;gap:16px;padding:16px 16px 8px}.kp-sidebar__top-row{display:flex;align-items:center;gap:12px;min-height:40px}:host(.kp-sidebar--collapsed) .kp-sidebar__top-row{position:relative;justify-content:center}:host(.kp-sidebar--collapsed) .kp-sidebar__logo,:host(.kp-sidebar--collapsed) .kp-sidebar__toggle{transition:opacity var(--kp-motion-duration-fast) ease}:host(.kp-sidebar--collapsed) .kp-sidebar__top-row:has(.kp-sidebar__logo) .kp-sidebar__toggle{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);opacity:0;pointer-events:none}:host(.kp-sidebar--collapsed) .kp-sidebar__top-row:has(.kp-sidebar__logo):hover .kp-sidebar__logo{opacity:0}:host(.kp-sidebar--collapsed) .kp-sidebar__top-row:has(.kp-sidebar__logo):hover .kp-sidebar__toggle{opacity:1;pointer-events:auto}.kp-sidebar__logo{display:flex;align-items:center;gap:12px;flex:1 1 auto;min-width:0}.kp-sidebar__logo-mark{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:8px;background:var(--kp-color-primary-default-bg-rest);color:var(--kp-color-foreground-on-saturated);flex:0 0 auto}.kp-sidebar__logo-mark svg{width:60%;height:60%}.kp-sidebar__logo-text{font-size:16px;font-weight:600;flex:1 1 auto;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;opacity:1;max-width:200px;transition:opacity var(--kp-motion-duration-normal) cubic-bezier(.4,0,.2,1),max-width var(--kp-motion-duration-normal) cubic-bezier(.4,0,.2,1)}.kp-sidebar__logo-text--hidden{opacity:0;max-width:0;pointer-events:none}.kp-sidebar__toggle{all:unset;display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:6px;color:var(--kp-color-text-muted);cursor:pointer;transition:background var(--kp-motion-duration-fast) ease,color .12s ease}.kp-sidebar__toggle:hover{background:var(--kp-color-surface-muted);color:var(--kp-color-text-strong)}.kp-sidebar__toggle svg{width:16px;height:16px}:host(.kp-sidebar--dark) .kp-sidebar__toggle{color:var(--kp-color-fg-on-dark-muted)}:host(.kp-sidebar--dark) .kp-sidebar__toggle:hover{background:var(--kp-color-surface-on-dark-muted);color:var(--kp-color-foreground-on-saturated)}:host(.kp-sidebar--collapsed) .kp-sidebar__logo{justify-content:center}.kp-sidebar__nav{display:flex;flex-direction:column;gap:16px;padding:8px;flex:1 1 auto;overflow-y:auto}.kp-sidebar__section{display:flex;flex-direction:column;gap:2px}.kp-sidebar__nav ::ng-deep [kpNavItemIcon]{display:inline-flex;align-items:center;justify-content:center;line-height:0}.kp-sidebar__section-label{transition:opacity var(--kp-motion-duration-normal) cubic-bezier(.4,0,.2,1)}.kp-sidebar__section-label--hidden{opacity:0;pointer-events:none}.kp-sidebar__section-label{padding:8px 12px 4px;font-size:11px;font-weight:600;letter-spacing:.5px;text-transform:uppercase;color:var(--kp-color-sidebar-section-label)}.kp-sidebar__badge{display:inline-flex;align-items:center;justify-content:center;padding:1px 8px;height:20px;border-radius:999px;background:var(--kp-color-surface-muted);color:var(--kp-color-text-default);font-size:11px;font-weight:500}.kp-sidebar__footer{display:flex;align-items:center;gap:12px;padding:16px;border-top:1px solid var(--kp-color-sidebar-border)}:host(.kp-sidebar--dark) .kp-sidebar__footer{border-top-color:var(--kp-color-border-on-dark-default)}.kp-sidebar__footer-text{display:flex;flex-direction:column;gap:2px;flex:1 1 auto;min-width:0;opacity:1;max-width:200px;transition:opacity var(--kp-motion-duration-normal) cubic-bezier(.4,0,.2,1),max-width var(--kp-motion-duration-normal) cubic-bezier(.4,0,.2,1)}:host(.kp-sidebar--collapsed) .kp-sidebar__footer-text{opacity:0;max-width:0;pointer-events:none}.kp-sidebar__footer-name{font-size:13px;font-weight:500;color:var(--kp-color-text-strong);white-space:nowrap;text-overflow:ellipsis;overflow:hidden}:host(.kp-sidebar--dark) .kp-sidebar__footer-name{color:var(--kp-color-fg-on-dark-strong)}.kp-sidebar__footer-email{font-size:11px;color:var(--kp-color-text-muted);white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.kp-sidebar__footer-menu{all:unset;display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:4px;color:var(--kp-color-text-muted);cursor:pointer;opacity:1;max-width:28px;transition:opacity var(--kp-motion-duration-normal) cubic-bezier(.4,0,.2,1),max-width var(--kp-motion-duration-normal) cubic-bezier(.4,0,.2,1),background var(--kp-motion-duration-fast) ease,color var(--kp-motion-duration-fast) ease}.kp-sidebar__footer-menu:hover{color:var(--kp-color-text-strong);background:var(--kp-color-surface-muted)}.kp-sidebar__footer-menu svg{width:18px;height:18px}:host(.kp-sidebar--collapsed) .kp-sidebar__footer-menu{opacity:0;max-width:0;pointer-events:none}.kp-sidebar__footer{transition:padding var(--kp-motion-duration-normal) cubic-bezier(.4,0,.2,1)}:host(.kp-sidebar--collapsed) .kp-sidebar__footer{justify-content:center;padding-inline:0}\n"] }]
|
|
239
255
|
}], propDecorators: { widthState: [{
|
|
240
256
|
type: Input
|
|
241
257
|
}], appearance: [{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kanso-protocol-sidebar.mjs","sources":["../../../../../packages/patterns/sidebar/src/sidebar.component.ts","../../../../../packages/patterns/sidebar/src/kanso-protocol-sidebar.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n EventEmitter,\n Input,\n Output,\n} from '@angular/core';\nimport { KpAvatarComponent } from '@kanso-protocol/avatar';\nimport { KpIconComponent } from '@kanso-protocol/icon';\nimport { KpNavItemComponent } from '@kanso-protocol/nav-item';\n\nexport type KpSidebarWidth = 'expanded' | 'collapsed';\nexport type KpSidebarAppearance = 'light' | 'dark';\n\nexport interface KpSidebarNavItem {\n label: string;\n icon?: string; // semantic icon key (consumer resolves)\n href?: string;\n active?: boolean;\n badge?: string;\n /** Nested children — rendered with depth=1 when parent is expanded */\n children?: KpSidebarNavItem[];\n expanded?: boolean;\n}\n\nexport interface KpSidebarSection {\n label?: string;\n items: KpSidebarNavItem[];\n}\n\n/**\n * Kanso Protocol — Sidebar\n *\n * App side navigation container. Expanded (240) or collapsed (64).\n * Data-driven sections, each with its own label and NavItems.\n * Optional logo, search slot, user footer with Avatar.\n *\n * Slots:\n * - `[kpSidebarLogo]` — custom logo area\n * - `[kpSidebarSearch]` — search field above sections\n * - each NavItem icon is rendered via `iconTemplateRef` if provided\n * (otherwise a generic placeholder)\n *\n * @example\n * <kp-sidebar\n * widthState=\"expanded\"\n * [sections]=\"nav\"\n * userName=\"Greg Black\"\n * userInitials=\"GB\"\n * userEmail=\"greg@example.com\"\n * />\n */\n@Component({\n selector: 'kp-sidebar',\n imports: [KpAvatarComponent, KpIconComponent, KpNavItemComponent],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { '[class]': 'hostClasses', role: 'navigation' },\n template: `\n <div class=\"kp-sidebar__top\">\n <div class=\"kp-sidebar__top-row\">\n @if (showLogo) {\n <div class=\"kp-sidebar__logo\">\n <ng-content select=\"[kpSidebarLogo]\">\n <span class=\"kp-sidebar__logo-mark\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.75\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M3 7l9-4 9 4v10l-9 4-9-4V7zM3 7l9 4 9-4M12 11v10\"/>\n </svg>\n </span>\n @if (widthState !== 'collapsed') {\n <span class=\"kp-sidebar__logo-text\">{{ logoText }}</span>\n }\n </ng-content>\n </div>\n }\n <button\n type=\"button\"\n class=\"kp-sidebar__toggle\"\n [attr.aria-label]=\"widthState === 'collapsed' ? 'Expand sidebar' : 'Collapse sidebar'\"\n (click)=\"onToggle()\"\n >\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.75\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"5\" width=\"18\" height=\"14\" rx=\"2\"/><path d=\"M9 5v14\"/></svg>\n </button>\n </div>\n @if (showSearch && widthState !== 'collapsed') {\n <ng-content select=\"[kpSidebarSearch]\"/>\n }\n </div>\n\n <nav class=\"kp-sidebar__nav\">\n @for (section of sections; track $index) {\n <div class=\"kp-sidebar__section\">\n @if (showSectionLabels && section.label && widthState !== 'collapsed') {\n <div class=\"kp-sidebar__section-label\">{{ section.label }}</div>\n }\n @for (item of section.items; track item.label) {\n <kp-nav-item\n size=\"md\"\n [label]=\"item.label\"\n [active]=\"!!item.active\"\n [showBadge]=\"!!item.badge\"\n [hasChildren]=\"!!item.children?.length\"\n [expanded]=\"!!item.expanded\"\n [collapsed]=\"widthState === 'collapsed'\"\n (click$)=\"onItemClick(item)\"\n >\n <span kpNavItemIcon>\n <kp-icon [name]=\"item.icon || 'circle'\" />\n </span>\n @if (item.badge) {\n <span kpNavItemBadge class=\"kp-sidebar__badge\">{{ item.badge }}</span>\n }\n </kp-nav-item>\n\n @if (item.children?.length && item.expanded && widthState !== 'collapsed') {\n @for (child of item.children; track child.label) {\n <kp-nav-item\n size=\"md\"\n [depth]=\"1\"\n [label]=\"child.label\"\n [active]=\"!!child.active\"\n [showIcon]=\"false\"\n style=\"--kp-nav-item-indent: 30px\"\n (click$)=\"onItemClick(child)\"\n />\n }\n }\n }\n </div>\n }\n </nav>\n\n @if (showUserFooter && (userName || userInitials)) {\n <div class=\"kp-sidebar__footer\">\n <kp-avatar size=\"md\" [initials]=\"userInitials || null\" [showStatus]=\"true\" status=\"online\"/>\n @if (widthState !== 'collapsed') {\n <div class=\"kp-sidebar__footer-text\">\n @if (userName) { <span class=\"kp-sidebar__footer-name\">{{ userName }}</span> }\n @if (userEmail) { <span class=\"kp-sidebar__footer-email\">{{ userEmail }}</span> }\n </div>\n <button type=\"button\" class=\"kp-sidebar__footer-menu\" aria-label=\"User options\" (click)=\"userMenuClick.emit()\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.75\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"5\" r=\"1\"/><circle cx=\"12\" cy=\"12\" r=\"1\"/><circle cx=\"12\" cy=\"19\" r=\"1\"/></svg>\n </button>\n }\n </div>\n }\n `,\n styles: [`\n :host {\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n width: var(--kp-sidebar-w, 240px);\n height: 100%;\n min-height: 100vh;\n background: var(--kp-color-sidebar-bg, var(--kp-color-white));\n border-inline-end: 1px solid var(--kp-color-sidebar-border, var(--kp-color-gray-200));\n font-family: var(--kp-font-family-sans, 'Onest', system-ui, sans-serif);\n transition: width var(--kp-motion-duration-fast) ease;\n }\n :host(.kp-sidebar--expanded) { --kp-sidebar-w: 240px; }\n :host(.kp-sidebar--collapsed) { --kp-sidebar-w: 64px; }\n\n :host(.kp-sidebar--dark) {\n background: var(--kp-color-sidebar-bg-dark, var(--kp-color-gray-900));\n color: var(--kp-color-fg-on-dark-strong);\n border-right-color: var(--kp-color-gray-800);\n }\n\n .kp-sidebar__top {\n display: flex;\n flex-direction: column;\n gap: 16px;\n padding: 16px;\n }\n .kp-sidebar__top-row {\n display: flex;\n align-items: center;\n gap: 12px;\n min-height: 40px;\n }\n :host(.kp-sidebar--collapsed) .kp-sidebar__top-row {\n position: relative;\n justify-content: center;\n }\n :host(.kp-sidebar--collapsed) .kp-sidebar__logo,\n :host(.kp-sidebar--collapsed) .kp-sidebar__toggle {\n transition: opacity var(--kp-motion-duration-fast) ease;\n }\n /* When collapsed AND logo is present, logo shows by default; toggle reveals on hover */\n :host(.kp-sidebar--collapsed) .kp-sidebar__top-row:has(.kp-sidebar__logo) .kp-sidebar__toggle {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n opacity: 0;\n pointer-events: none;\n }\n :host(.kp-sidebar--collapsed) .kp-sidebar__top-row:has(.kp-sidebar__logo):hover .kp-sidebar__logo { opacity: 0; }\n :host(.kp-sidebar--collapsed) .kp-sidebar__top-row:has(.kp-sidebar__logo):hover .kp-sidebar__toggle {\n opacity: 1;\n pointer-events: auto;\n }\n .kp-sidebar__logo {\n display: flex;\n align-items: center;\n gap: 12px;\n flex: 1 1 auto;\n min-width: 0;\n }\n .kp-sidebar__logo-mark {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n border-radius: 8px;\n background: var(--kp-color-blue-600, var(--kp-color-blue-600));\n color: var(--kp-color-white);\n flex: 0 0 auto;\n }\n .kp-sidebar__logo-mark svg { width: 60%; height: 60%; }\n .kp-sidebar__logo-text {\n font-size: 16px;\n font-weight: 600;\n flex: 1 1 auto;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n }\n .kp-sidebar__toggle {\n all: unset;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n border-radius: 6px;\n color: var(--kp-color-gray-500, var(--kp-color-gray-500));\n cursor: pointer;\n transition: background var(--kp-motion-duration-fast) ease, color 120ms ease;\n }\n .kp-sidebar__toggle:hover {\n background: var(--kp-color-gray-100, var(--kp-color-gray-100));\n color: var(--kp-color-gray-900, var(--kp-color-gray-900));\n }\n .kp-sidebar__toggle svg { width: 16px; height: 16px; }\n :host(.kp-sidebar--dark) .kp-sidebar__toggle { color: var(--kp-color-fg-on-dark-muted); }\n :host(.kp-sidebar--dark) .kp-sidebar__toggle:hover {\n background: var(--kp-color-gray-800);\n color: var(--kp-color-white);\n }\n\n :host(.kp-sidebar--collapsed) .kp-sidebar__logo { justify-content: center; }\n\n .kp-sidebar__nav {\n display: flex;\n flex-direction: column;\n gap: 16px;\n padding: 8px;\n flex: 1 1 auto;\n overflow-y: auto;\n }\n .kp-sidebar__section {\n display: flex;\n flex-direction: column;\n gap: 2px;\n }\n .kp-sidebar__section-label {\n padding: 8px 12px 4px;\n font-size: 11px;\n font-weight: 600;\n letter-spacing: 0.5px;\n text-transform: uppercase;\n color: var(--kp-color-sidebar-section-label, var(--kp-color-gray-500));\n }\n\n .kp-sidebar__badge {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: 1px 8px;\n height: 20px;\n border-radius: 999px;\n background: var(--kp-color-gray-100, var(--kp-color-gray-100));\n color: var(--kp-color-gray-700, var(--kp-color-gray-700));\n font-size: 11px;\n font-weight: 500;\n }\n\n .kp-sidebar__footer {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 16px;\n border-top: 1px solid var(--kp-color-sidebar-border, var(--kp-color-gray-200));\n }\n :host(.kp-sidebar--dark) .kp-sidebar__footer { border-top-color: var(--kp-color-gray-800); }\n .kp-sidebar__footer-text {\n display: flex;\n flex-direction: column;\n gap: 2px;\n flex: 1 1 auto;\n min-width: 0;\n }\n .kp-sidebar__footer-name {\n font-size: 13px;\n font-weight: 500;\n color: var(--kp-color-gray-900, var(--kp-color-gray-900));\n white-space: nowrap;\n text-overflow: ellipsis;\n overflow: hidden;\n }\n :host(.kp-sidebar--dark) .kp-sidebar__footer-name { color: var(--kp-color-white); }\n .kp-sidebar__footer-email {\n font-size: 11px;\n color: var(--kp-color-gray-500, var(--kp-color-gray-500));\n white-space: nowrap;\n text-overflow: ellipsis;\n overflow: hidden;\n }\n .kp-sidebar__footer-menu {\n all: unset;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n border-radius: 4px;\n color: var(--kp-color-gray-500, var(--kp-color-gray-500));\n cursor: pointer;\n }\n .kp-sidebar__footer-menu:hover { color: var(--kp-color-gray-900, var(--kp-color-gray-900)); background: var(--kp-color-gray-100, var(--kp-color-gray-100)); }\n .kp-sidebar__footer-menu svg { width: 18px; height: 18px; }\n\n :host(.kp-sidebar--collapsed) .kp-sidebar__footer { justify-content: center; padding-inline: 0; }\n `],\n})\nexport class KpSidebarComponent {\n @Input() widthState: KpSidebarWidth = 'expanded';\n @Input() appearance: KpSidebarAppearance = 'light';\n\n @Input() showLogo = true;\n @Input() logoText = 'Kanso Protocol';\n @Input() showSearch = false;\n @Input() showSectionLabels = true;\n @Input() showUserFooter = true;\n\n @Input() sections: KpSidebarSection[] = [];\n\n @Input() userName: string | null = null;\n @Input() userEmail: string | null = null;\n @Input() userInitials: string | null = null;\n\n @Output() toggle = new EventEmitter<KpSidebarWidth>();\n\n onToggle(): void {\n this.widthState = this.widthState === 'expanded' ? 'collapsed' : 'expanded';\n this.toggle.emit(this.widthState);\n }\n\n onItemClick(item: KpSidebarNavItem): void {\n if (item.children?.length) {\n item.expanded = !item.expanded;\n }\n this.itemClick.emit(item);\n }\n @Output() itemClick = new EventEmitter<KpSidebarNavItem>();\n @Output() userMenuClick = new EventEmitter<void>();\n\n get hostClasses(): string {\n return `kp-sidebar kp-sidebar--${this.widthState} kp-sidebar--${this.appearance}`;\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"kanso-protocol-sidebar.mjs","sources":["../../../../../packages/patterns/sidebar/src/sidebar.component.ts","../../../../../packages/patterns/sidebar/src/kanso-protocol-sidebar.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n EventEmitter,\n Input,\n Output,\n} from '@angular/core';\nimport { KpAvatarComponent } from '@kanso-protocol/avatar';\nimport { KpIconComponent } from '@kanso-protocol/icon';\nimport { KpNavItemComponent } from '@kanso-protocol/nav-item';\n\nexport type KpSidebarWidth = 'expanded' | 'collapsed';\nexport type KpSidebarAppearance = 'light' | 'dark';\n\nexport interface KpSidebarNavItem {\n label: string;\n icon?: string; // semantic icon key (consumer resolves)\n href?: string;\n active?: boolean;\n badge?: string;\n /** Nested children — rendered with depth=1 when parent is expanded */\n children?: KpSidebarNavItem[];\n expanded?: boolean;\n}\n\nexport interface KpSidebarSection {\n label?: string;\n items: KpSidebarNavItem[];\n}\n\n/**\n * Kanso Protocol — Sidebar\n *\n * App side navigation container. Expanded (240) or collapsed (64).\n * Data-driven sections, each with its own label and NavItems.\n * Optional logo, search slot, user footer with Avatar.\n *\n * Slots:\n * - `[kpSidebarLogo]` — custom logo area\n * - `[kpSidebarSearch]` — search field above sections\n * - each NavItem icon is rendered via `iconTemplateRef` if provided\n * (otherwise a generic placeholder)\n *\n * @example\n * <kp-sidebar\n * widthState=\"expanded\"\n * [sections]=\"nav\"\n * userName=\"Greg Black\"\n * userInitials=\"GB\"\n * userEmail=\"greg@example.com\"\n * />\n */\n@Component({\n selector: 'kp-sidebar',\n imports: [KpAvatarComponent, KpIconComponent, KpNavItemComponent],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { '[class]': 'hostClasses', role: 'navigation' },\n template: `\n <div class=\"kp-sidebar__top\">\n <div class=\"kp-sidebar__top-row\">\n @if (showLogo) {\n <div class=\"kp-sidebar__logo\">\n <ng-content select=\"[kpSidebarLogo]\">\n <span class=\"kp-sidebar__logo-mark\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.75\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M3 7l9-4 9 4v10l-9 4-9-4V7zM3 7l9 4 9-4M12 11v10\"/>\n </svg>\n </span>\n <span\n class=\"kp-sidebar__logo-text\"\n [class.kp-sidebar__logo-text--hidden]=\"widthState === 'collapsed'\"\n [attr.aria-hidden]=\"widthState === 'collapsed' || null\">{{ logoText }}</span>\n </ng-content>\n </div>\n }\n <button\n type=\"button\"\n class=\"kp-sidebar__toggle\"\n [attr.aria-label]=\"widthState === 'collapsed' ? 'Expand sidebar' : 'Collapse sidebar'\"\n (click)=\"onToggle()\"\n >\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.75\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"5\" width=\"18\" height=\"14\" rx=\"2\"/><path d=\"M9 5v14\"/></svg>\n </button>\n </div>\n @if (showSearch && widthState !== 'collapsed') {\n <ng-content select=\"[kpSidebarSearch]\"/>\n }\n </div>\n\n <nav class=\"kp-sidebar__nav\">\n @for (section of sections; track $index) {\n <div class=\"kp-sidebar__section\">\n @if (showSectionLabels && section.label) {\n <div\n class=\"kp-sidebar__section-label\"\n [class.kp-sidebar__section-label--hidden]=\"widthState === 'collapsed'\"\n [attr.aria-hidden]=\"widthState === 'collapsed' || null\">{{ section.label }}</div>\n }\n @for (item of section.items; track item.label) {\n <kp-nav-item\n size=\"md\"\n [label]=\"item.label\"\n [active]=\"!!item.active\"\n [showBadge]=\"!!item.badge\"\n [hasChildren]=\"!!item.children?.length\"\n [expanded]=\"!!item.expanded\"\n [collapsed]=\"widthState === 'collapsed'\"\n (click$)=\"onItemClick(item)\"\n >\n <span kpNavItemIcon>\n <kp-icon [name]=\"item.icon || 'circle'\" />\n </span>\n @if (item.badge) {\n <span kpNavItemBadge class=\"kp-sidebar__badge\">{{ item.badge }}</span>\n }\n </kp-nav-item>\n\n @if (item.children?.length && item.expanded && widthState !== 'collapsed') {\n @for (child of item.children; track child.label) {\n <kp-nav-item\n size=\"md\"\n [depth]=\"1\"\n [label]=\"child.label\"\n [active]=\"!!child.active\"\n [showIcon]=\"false\"\n style=\"--kp-nav-item-indent: 30px\"\n (click$)=\"onItemClick(child)\"\n />\n }\n }\n }\n </div>\n }\n </nav>\n\n @if (showUserFooter && (userName || userInitials)) {\n <div class=\"kp-sidebar__footer\" [class.kp-sidebar__footer--collapsed]=\"widthState === 'collapsed'\">\n <kp-avatar size=\"md\" [initials]=\"userInitials || null\" [showStatus]=\"true\" status=\"online\"/>\n <div class=\"kp-sidebar__footer-text\" [attr.aria-hidden]=\"widthState === 'collapsed' || null\">\n @if (userName) { <span class=\"kp-sidebar__footer-name\">{{ userName }}</span> }\n @if (userEmail) { <span class=\"kp-sidebar__footer-email\">{{ userEmail }}</span> }\n </div>\n <button\n type=\"button\"\n class=\"kp-sidebar__footer-menu\"\n aria-label=\"User options\"\n [attr.aria-hidden]=\"widthState === 'collapsed' || null\"\n [tabindex]=\"widthState === 'collapsed' ? -1 : 0\"\n (click)=\"userMenuClick.emit()\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.75\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"5\" r=\"1\"/><circle cx=\"12\" cy=\"12\" r=\"1\"/><circle cx=\"12\" cy=\"19\" r=\"1\"/></svg>\n </button>\n </div>\n }\n `,\n styles: [`\n :host {\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n width: var(--kp-sidebar-w, 240px);\n height: 100%;\n min-height: 100vh;\n background: var(--kp-color-sidebar-bg);\n border-inline-end: 1px solid var(--kp-color-sidebar-border);\n font-family: var(--kp-font-family-sans, 'Onest', system-ui, sans-serif);\n /* Width animates with the same cubic-bezier + duration that\n drives every inner element's collapse (labels fade + max-width\n to 0 in nav-item, logo text / footer chrome below). All pieces\n move in lockstep — no twitch. */\n transition: width var(--kp-motion-duration-normal) cubic-bezier(0.4, 0, 0.2, 1);\n }\n :host(.kp-sidebar--expanded) { --kp-sidebar-w: 240px; }\n :host(.kp-sidebar--collapsed) { --kp-sidebar-w: 64px; }\n\n :host(.kp-sidebar--dark) {\n background: var(--kp-color-sidebar-bg-dark);\n color: var(--kp-color-fg-on-dark-strong);\n border-right-color: var(--kp-color-border-on-dark-default);\n }\n\n .kp-sidebar__top {\n display: flex;\n flex-direction: column;\n gap: 16px;\n /* Bottom padding tightened (16 → 8) to reduce the vertical gap\n between the collapse toggle and the first nav section — the\n original 16+nav-pad+label-pad stack felt too airy. */\n padding: 16px 16px 8px;\n }\n .kp-sidebar__top-row {\n display: flex;\n align-items: center;\n gap: 12px;\n min-height: 40px;\n }\n :host(.kp-sidebar--collapsed) .kp-sidebar__top-row {\n position: relative;\n justify-content: center;\n }\n :host(.kp-sidebar--collapsed) .kp-sidebar__logo,\n :host(.kp-sidebar--collapsed) .kp-sidebar__toggle {\n transition: opacity var(--kp-motion-duration-fast) ease;\n }\n /* When collapsed AND logo is present, logo shows by default; toggle reveals on hover */\n :host(.kp-sidebar--collapsed) .kp-sidebar__top-row:has(.kp-sidebar__logo) .kp-sidebar__toggle {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n opacity: 0;\n pointer-events: none;\n }\n :host(.kp-sidebar--collapsed) .kp-sidebar__top-row:has(.kp-sidebar__logo):hover .kp-sidebar__logo { opacity: 0; }\n :host(.kp-sidebar--collapsed) .kp-sidebar__top-row:has(.kp-sidebar__logo):hover .kp-sidebar__toggle {\n opacity: 1;\n pointer-events: auto;\n }\n .kp-sidebar__logo {\n display: flex;\n align-items: center;\n gap: 12px;\n flex: 1 1 auto;\n min-width: 0;\n }\n .kp-sidebar__logo-mark {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n border-radius: 8px;\n background: var(--kp-color-primary-default-bg-rest);\n color: var(--kp-color-foreground-on-saturated);\n flex: 0 0 auto;\n }\n .kp-sidebar__logo-mark svg { width: 60%; height: 60%; }\n .kp-sidebar__logo-text {\n font-size: 16px;\n font-weight: 600;\n flex: 1 1 auto;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n opacity: 1;\n max-width: 200px;\n transition:\n opacity var(--kp-motion-duration-normal) cubic-bezier(0.4, 0, 0.2, 1),\n max-width var(--kp-motion-duration-normal) cubic-bezier(0.4, 0, 0.2, 1);\n }\n .kp-sidebar__logo-text--hidden {\n opacity: 0;\n max-width: 0;\n pointer-events: none;\n }\n .kp-sidebar__toggle {\n all: unset;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n border-radius: 6px;\n color: var(--kp-color-text-muted);\n cursor: pointer;\n transition: background var(--kp-motion-duration-fast) ease, color 120ms ease;\n }\n .kp-sidebar__toggle:hover {\n background: var(--kp-color-surface-muted);\n color: var(--kp-color-text-strong);\n }\n .kp-sidebar__toggle svg { width: 16px; height: 16px; }\n :host(.kp-sidebar--dark) .kp-sidebar__toggle { color: var(--kp-color-fg-on-dark-muted); }\n :host(.kp-sidebar--dark) .kp-sidebar__toggle:hover {\n background: var(--kp-color-surface-on-dark-muted);\n color: var(--kp-color-foreground-on-saturated);\n }\n\n :host(.kp-sidebar--collapsed) .kp-sidebar__logo { justify-content: center; }\n\n .kp-sidebar__nav {\n display: flex;\n flex-direction: column;\n gap: 16px;\n padding: 8px;\n flex: 1 1 auto;\n overflow-y: auto;\n }\n .kp-sidebar__section {\n display: flex;\n flex-direction: column;\n gap: 2px;\n }\n /* The icon slot inside each nav-item ends up wrapped in a <span>\n (the directive carrier for [kpNavItemIcon]). Span defaults to\n inline display, which adds a baseline gap that visually shifts\n the icon down by ~1-2px relative to the label. Force inline-flex\n on the wrapper so the icon centers exactly with the row text. */\n .kp-sidebar__nav ::ng-deep [kpNavItemIcon] {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n line-height: 0;\n }\n /* In collapsed mode the label text fades out but the box still\n reserves vertical space, so nav-items stay at the same Y offset\n as in the expanded layout (no jumping when toggling). Opacity\n transitioned together with the host width animation. */\n .kp-sidebar__section-label {\n transition: opacity var(--kp-motion-duration-normal) cubic-bezier(0.4, 0, 0.2, 1);\n }\n .kp-sidebar__section-label--hidden {\n opacity: 0;\n pointer-events: none;\n }\n .kp-sidebar__section-label {\n padding: 8px 12px 4px;\n font-size: 11px;\n font-weight: 600;\n letter-spacing: 0.5px;\n text-transform: uppercase;\n color: var(--kp-color-sidebar-section-label);\n }\n\n .kp-sidebar__badge {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: 1px 8px;\n height: 20px;\n border-radius: 999px;\n background: var(--kp-color-surface-muted);\n color: var(--kp-color-text-default);\n font-size: 11px;\n font-weight: 500;\n }\n\n .kp-sidebar__footer {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 16px;\n border-top: 1px solid var(--kp-color-sidebar-border);\n }\n :host(.kp-sidebar--dark) .kp-sidebar__footer { border-top-color: var(--kp-color-border-on-dark-default); }\n .kp-sidebar__footer-text {\n display: flex;\n flex-direction: column;\n gap: 2px;\n flex: 1 1 auto;\n min-width: 0;\n opacity: 1;\n max-width: 200px;\n transition:\n opacity var(--kp-motion-duration-normal) cubic-bezier(0.4, 0, 0.2, 1),\n max-width var(--kp-motion-duration-normal) cubic-bezier(0.4, 0, 0.2, 1);\n }\n :host(.kp-sidebar--collapsed) .kp-sidebar__footer-text {\n opacity: 0;\n max-width: 0;\n pointer-events: none;\n }\n .kp-sidebar__footer-name {\n font-size: 13px;\n font-weight: 500;\n color: var(--kp-color-text-strong);\n white-space: nowrap;\n text-overflow: ellipsis;\n overflow: hidden;\n }\n :host(.kp-sidebar--dark) .kp-sidebar__footer-name { color: var(--kp-color-fg-on-dark-strong); }\n .kp-sidebar__footer-email {\n font-size: 11px;\n color: var(--kp-color-text-muted);\n white-space: nowrap;\n text-overflow: ellipsis;\n overflow: hidden;\n }\n .kp-sidebar__footer-menu {\n all: unset;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n border-radius: 4px;\n color: var(--kp-color-text-muted);\n cursor: pointer;\n opacity: 1;\n max-width: 28px;\n transition:\n opacity var(--kp-motion-duration-normal) cubic-bezier(0.4, 0, 0.2, 1),\n max-width var(--kp-motion-duration-normal) cubic-bezier(0.4, 0, 0.2, 1),\n background var(--kp-motion-duration-fast) ease,\n color var(--kp-motion-duration-fast) ease;\n }\n .kp-sidebar__footer-menu:hover { color: var(--kp-color-text-strong); background: var(--kp-color-surface-muted); }\n .kp-sidebar__footer-menu svg { width: 18px; height: 18px; }\n :host(.kp-sidebar--collapsed) .kp-sidebar__footer-menu {\n opacity: 0;\n max-width: 0;\n pointer-events: none;\n }\n\n .kp-sidebar__footer {\n transition: padding var(--kp-motion-duration-normal) cubic-bezier(0.4, 0, 0.2, 1);\n }\n :host(.kp-sidebar--collapsed) .kp-sidebar__footer { justify-content: center; padding-inline: 0; }\n `],\n})\nexport class KpSidebarComponent {\n @Input() widthState: KpSidebarWidth = 'expanded';\n @Input() appearance: KpSidebarAppearance = 'light';\n\n @Input() showLogo = true;\n @Input() logoText = 'Kanso Protocol';\n @Input() showSearch = false;\n @Input() showSectionLabels = true;\n @Input() showUserFooter = true;\n\n @Input() sections: KpSidebarSection[] = [];\n\n @Input() userName: string | null = null;\n @Input() userEmail: string | null = null;\n @Input() userInitials: string | null = null;\n\n @Output() toggle = new EventEmitter<KpSidebarWidth>();\n\n onToggle(): void {\n this.widthState = this.widthState === 'expanded' ? 'collapsed' : 'expanded';\n this.toggle.emit(this.widthState);\n }\n\n onItemClick(item: KpSidebarNavItem): void {\n if (item.children?.length) {\n item.expanded = !item.expanded;\n }\n this.itemClick.emit(item);\n }\n @Output() itemClick = new EventEmitter<KpSidebarNavItem>();\n @Output() userMenuClick = new EventEmitter<void>();\n\n get hostClasses(): string {\n return `kp-sidebar kp-sidebar--${this.widthState} kp-sidebar--${this.appearance}`;\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kanso-protocol/sidebar",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.3",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"peerDependencies": {
|
|
6
6
|
"@angular/core": "^18.0.0",
|
|
7
7
|
"@angular/common": "^18.0.0",
|
|
8
|
-
"@kanso-protocol/core": "
|
|
9
|
-
"@kanso-protocol/avatar": ">=0.
|
|
10
|
-
"@kanso-protocol/nav-item": ">=0.
|
|
8
|
+
"@kanso-protocol/core": ">=0.5.3",
|
|
9
|
+
"@kanso-protocol/avatar": ">=0.5.3",
|
|
10
|
+
"@kanso-protocol/nav-item": ">=0.5.3"
|
|
11
11
|
},
|
|
12
12
|
"description": "Kanso Protocol — sidebar (pattern).",
|
|
13
13
|
"author": "GregNBlack",
|