@elavarasanbititude/ng-cwr-sidebar 0.0.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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,28 @@
1
+ # Changelog
2
+
3
+ All notable changes to `ng-cwr-sidebar` will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ---
9
+
10
+ ## [1.0.0] - 2026-04-16
11
+
12
+ ### Added
13
+ - `Sidebar` standalone Angular component (`<cwr-sidebar>`) with:
14
+ - Collapsible sidebar with configurable width
15
+ - Left/right positioning support
16
+ - Nested menu items with expand/collapse
17
+ - Badge support on menu items
18
+ - Configurable colors (background, text, hover)
19
+ - Mobile overlay mode
20
+ - Toggle button
21
+ - `menuItemClicked` and `collapsedChange` output events
22
+ - `SidebarService` for programmatic control:
23
+ - `toggleCollapsed()`, `setCollapsed()`, `getCollapsed()`
24
+ - `setMenuItems()`, `getMenuItems()`, `addMenuItem()`, `removeMenuItem()`
25
+ - `setConfig()`, `getConfig()`
26
+ - Observable streams: `collapsed$`, `menuItems$`, `config$`
27
+ - `SidebarMenuItem` and `SidebarConfig` TypeScript interfaces
28
+ - Full SCSS theming with CSS custom properties
package/README.md ADDED
@@ -0,0 +1,348 @@
1
+ # @yourname/ng-cwr-sidebar
2
+
3
+ A modern, flexible, and fully customizable Angular sidebar component library built with Angular 21 and SCSS. Perfect for building responsive navigation sidebars with nested menus, icons, and badges.
4
+
5
+ ## Features
6
+
7
+ - ✨ **Fully Customizable** - Configure colors, width, animations, and more
8
+ - 📱 **Responsive Design** - Works seamlessly on desktop and mobile devices
9
+ - 🎨 **Beautiful Styling** - Built with SCSS with light and dark theme support
10
+ - 🔀 **Nested Menus** - Support for multi-level menu hierarchies
11
+ - 🏷️ **Badges** - Add notification badges to menu items
12
+ - 🎯 **Collapsible** - Smooth collapse/expand animations
13
+ - ♿ **Accessible** - Built with accessibility best practices
14
+ - 🚀 **TypeScript** - Fully typed with TypeScript interfaces
15
+ - 🔄 **Reactive** - Built on RxJS Observables
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install @yourname/ng-cwr-sidebar
21
+ ```
22
+
23
+ ## Quick Start
24
+
25
+ ### 1. Import the Component
26
+
27
+ ```typescript
28
+ import { Sidebar } from '@yourname/ng-cwr-sidebar';
29
+
30
+ @Component({
31
+ selector: 'app-root',
32
+ standalone: true,
33
+ imports: [Sidebar, CommonModule],
34
+ template: `
35
+ <cwr-sidebar
36
+ [menuItems]="menuItems"
37
+ [config]="sidebarConfig"
38
+ (menuItemClicked)="onMenuItemClicked($event)"
39
+ ></cwr-sidebar>
40
+ `,
41
+ })
42
+ export class AppComponent {
43
+ menuItems = [
44
+ {
45
+ label: 'Dashboard',
46
+ icon: '📊',
47
+ route: '/dashboard',
48
+ },
49
+ {
50
+ label: 'Settings',
51
+ icon: '⚙️',
52
+ children: [
53
+ { label: 'Profile', route: '/settings/profile' },
54
+ { label: 'Preferences', route: '/settings/preferences' },
55
+ ],
56
+ },
57
+ ];
58
+
59
+ sidebarConfig = {
60
+ width: '250px',
61
+ collapsedWidth: '60px',
62
+ };
63
+
64
+ onMenuItemClicked(item: any) {
65
+ console.log('Menu item clicked:', item);
66
+ }
67
+ }
68
+ ```
69
+
70
+ ### 2. Using the Sidebar Service
71
+
72
+ ```typescript
73
+ import { SidebarService } from '@yourname/ng-cwr-sidebar';
74
+
75
+ export class MyComponent {
76
+ constructor(private sidebarService: SidebarService) {}
77
+
78
+ toggleSidebar() {
79
+ this.sidebarService.toggleCollapsed();
80
+ }
81
+
82
+ addMenuItem() {
83
+ this.sidebarService.addMenuItem({
84
+ id: 'new-item',
85
+ label: 'New Menu Item',
86
+ icon: '✨',
87
+ route: '/new',
88
+ });
89
+ }
90
+ }
91
+ ```
92
+
93
+ ## Configuration
94
+
95
+ ### SidebarConfig Interface
96
+
97
+ ```typescript
98
+ interface SidebarConfig {
99
+ collapsed?: boolean; // Initially collapsed state
100
+ position?: 'left' | 'right'; // Sidebar position
101
+ width?: string; // Width when expanded (default: '250px')
102
+ collapsedWidth?: string; // Width when collapsed (default: '60px')
103
+ backgroundColor?: string; // Background color
104
+ textColor?: string; // Text color
105
+ hoverColor?: string; // Hover background color
106
+ toggleButton?: boolean; // Show toggle button (default: true)
107
+ overlayOnMobile?: boolean; // Show overlay on mobile (default: true)
108
+ }
109
+ ```
110
+
111
+ ### SidebarMenuItem Interface
112
+
113
+ ```typescript
114
+ interface SidebarMenuItem {
115
+ id?: string; // Unique identifier
116
+ label: string; // Display label
117
+ icon?: string; // Icon (emoji or HTML)
118
+ route?: string; // Router link
119
+ action?: () => void; // Click action function
120
+ children?: SidebarMenuItem[]; // Nested menu items
121
+ disabled?: boolean; // Disable menu item
122
+ badge?: {
123
+ text: string;
124
+ color?: string; // Badge background color
125
+ };
126
+ }
127
+ ```
128
+
129
+ ## Examples
130
+
131
+ ### Basic Menu
132
+
133
+ ```typescript
134
+ const menuItems: SidebarMenuItem[] = [
135
+ { label: 'Home', icon: '🏠', route: '/' },
136
+ { label: 'About', icon: 'ℹ️', route: '/about' },
137
+ { label: 'Contact', icon: '📧', route: '/contact' },
138
+ ];
139
+ ```
140
+
141
+ ### Menu with Nested Items
142
+
143
+ ```typescript
144
+ const menuItems: SidebarMenuItem[] = [
145
+ {
146
+ label: 'Products',
147
+ icon: '📦',
148
+ children: [
149
+ { label: 'Electronics', route: '/products/electronics' },
150
+ { label: 'Clothing', route: '/products/clothing' },
151
+ { label: 'Books', route: '/products/books' },
152
+ ],
153
+ },
154
+ ];
155
+ ```
156
+
157
+ ### Menu with Badges
158
+
159
+ ```typescript
160
+ const menuItems: SidebarMenuItem[] = [
161
+ {
162
+ label: 'Messages',
163
+ icon: '💬',
164
+ route: '/messages',
165
+ badge: {
166
+ text: '5 new',
167
+ color: '#ff6b6b',
168
+ },
169
+ },
170
+ ];
171
+ ```
172
+
173
+ ### Custom Actions
174
+
175
+ ```typescript
176
+ const menuItems: SidebarMenuItem[] = [
177
+ {
178
+ label: 'Logout',
179
+ icon: '🚪',
180
+ action: () => {
181
+ // Custom logout logic
182
+ console.log('Logging out...');
183
+ },
184
+ },
185
+ ];
186
+ ```
187
+
188
+ ### Themed Configuration
189
+
190
+ ```typescript
191
+ // Light theme
192
+ const lightConfig: SidebarConfig = {
193
+ backgroundColor: '#f5f5f5',
194
+ textColor: '#333',
195
+ hoverColor: '#e0e0e0',
196
+ };
197
+
198
+ // Dark theme
199
+ const darkConfig: SidebarConfig = {
200
+ backgroundColor: '#2c3e50',
201
+ textColor: '#ecf0f1',
202
+ hoverColor: '#34495e',
203
+ };
204
+ ```
205
+
206
+ ## Component API
207
+
208
+ ### Input Properties
209
+
210
+ - `@Input() menuItems: SidebarMenuItem[]` - Array of menu items to display
211
+ - `@Input() config: SidebarConfig` - Configuration object for the sidebar
212
+
213
+ ### Output Events
214
+
215
+ - `@Output() menuItemClicked: EventEmitter<SidebarMenuItem>` - Emits when a menu item is clicked
216
+ - `@Output() collapsedChange: EventEmitter<boolean>` - Emits when sidebar collapsed state changes
217
+
218
+ ## Service API
219
+
220
+ ### SidebarService
221
+
222
+ ```typescript
223
+ // Toggle collapsed state
224
+ sidebarService.toggleCollapsed(): void
225
+
226
+ // Set collapsed state
227
+ sidebarService.setCollapsed(collapsed: boolean): void
228
+
229
+ // Get collapsed state
230
+ sidebarService.getCollapsed(): boolean
231
+
232
+ // Get collapsed state as Observable
233
+ sidebarService.collapsed$: Observable<boolean>
234
+
235
+ // Set menu items
236
+ sidebarService.setMenuItems(items: SidebarMenuItem[]): void
237
+
238
+ // Get menu items
239
+ sidebarService.getMenuItems(): SidebarMenuItem[]
240
+
241
+ // Add a menu item
242
+ sidebarService.addMenuItem(item: SidebarMenuItem): void
243
+
244
+ // Remove a menu item by id
245
+ sidebarService.removeMenuItem(id: string): void
246
+
247
+ // Set configuration
248
+ sidebarService.setConfig(config: SidebarConfig): void
249
+
250
+ // Get configuration
251
+ sidebarService.getConfig(): SidebarConfig
252
+ ```
253
+
254
+ ## Styling
255
+
256
+ The component uses SCSS and includes both light and dark theme variants. You can customize the appearance by:
257
+
258
+ 1. **Inline Styles** - Use the `config` input property
259
+ 2. **CSS Classes** - Override default styles with your own CSS
260
+ 3. **Dark Theme** - Add the `dark-theme` class to enable dark mode
261
+
262
+ ### Custom Styling Example
263
+
264
+ ```scss
265
+ // In your global styles
266
+ .cwr-sidebar {
267
+ --primary-color: #your-color;
268
+
269
+ .menu-link:hover {
270
+ background-color: rgba(0, 0, 0, 0.1);
271
+ }
272
+ }
273
+ ```
274
+
275
+ ## Responsive Behavior
276
+
277
+ - **Desktop (>768px)**: Sidebar displays inline with toggle functionality
278
+ - **Mobile (<768px)**: Sidebar displays as overlay, can be toggled in/out of view
279
+
280
+ ## Accessibility
281
+
282
+ - Full keyboard navigation support
283
+ - ARIA labels for screen readers
284
+ - Focus management
285
+ - Semantic HTML structure
286
+
287
+ ## Browser Support
288
+
289
+ - Chrome (latest)
290
+ - Firefox (latest)
291
+ - Safari (latest)
292
+ - Edge (latest)
293
+
294
+ ## License
295
+
296
+ MIT
297
+
298
+ ## Contributing
299
+
300
+ Contributions are welcome! Please feel free to submit a Pull Request.
301
+
302
+ ## Support
303
+
304
+ For issues, questions, or suggestions, please open an issue on the [GitHub repository](https://github.com/yourusername/ng-cwr-sidebar/issues).
305
+
306
+ ## Changelog
307
+
308
+ ### Version 1.0.0
309
+ - Initial release
310
+ - Full sidebar component with nested menus
311
+ - Dark and light themes
312
+ - Responsive design
313
+ - Comprehensive service API
314
+
315
+ Once the project is built, you can publish your library by following these steps:
316
+
317
+ 1. Navigate to the `dist` directory:
318
+
319
+ ```bash
320
+ cd dist/ng-cwr-sidebar
321
+ ```
322
+
323
+ 2. Run the `npm publish` command to publish your library to the npm registry:
324
+ ```bash
325
+ npm publish
326
+ ```
327
+
328
+ ## Running unit tests
329
+
330
+ To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command:
331
+
332
+ ```bash
333
+ ng test
334
+ ```
335
+
336
+ ## Running end-to-end tests
337
+
338
+ For end-to-end (e2e) testing, run:
339
+
340
+ ```bash
341
+ ng e2e
342
+ ```
343
+
344
+ Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
345
+
346
+ ## Additional Resources
347
+
348
+ For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
@@ -0,0 +1,217 @@
1
+ import * as i0 from '@angular/core';
2
+ import { Injectable, EventEmitter, inject, Output, Input, ChangeDetectionStrategy, Component } from '@angular/core';
3
+ import * as i1 from '@angular/common';
4
+ import { CommonModule } from '@angular/common';
5
+ import * as i2 from '@angular/router';
6
+ import { Router, NavigationEnd, RouterModule } from '@angular/router';
7
+ import { BehaviorSubject } from 'rxjs';
8
+ import { filter } from 'rxjs/operators';
9
+
10
+ class SidebarService {
11
+ collapsedSubject = new BehaviorSubject(false);
12
+ menuItemsSubject = new BehaviorSubject([]);
13
+ configSubject = new BehaviorSubject({});
14
+ collapsed$ = this.collapsedSubject.asObservable();
15
+ menuItems$ = this.menuItemsSubject.asObservable();
16
+ config$ = this.configSubject.asObservable();
17
+ constructor() { }
18
+ /**
19
+ * Toggle the sidebar collapsed state
20
+ */
21
+ toggleCollapsed() {
22
+ this.collapsedSubject.next(!this.collapsedSubject.value);
23
+ }
24
+ /**
25
+ * Set the collapsed state
26
+ */
27
+ setCollapsed(collapsed) {
28
+ this.collapsedSubject.next(collapsed);
29
+ }
30
+ /**
31
+ * Get current collapsed state
32
+ */
33
+ getCollapsed() {
34
+ return this.collapsedSubject.value;
35
+ }
36
+ /**
37
+ * Set menu items
38
+ */
39
+ setMenuItems(items) {
40
+ this.menuItemsSubject.next(items);
41
+ }
42
+ /**
43
+ * Get menu items
44
+ */
45
+ getMenuItems() {
46
+ return this.menuItemsSubject.value;
47
+ }
48
+ /**
49
+ * Add a menu item
50
+ */
51
+ addMenuItem(item) {
52
+ const items = this.menuItemsSubject.value;
53
+ this.menuItemsSubject.next([...items, item]);
54
+ }
55
+ /**
56
+ * Remove a menu item by id
57
+ */
58
+ removeMenuItem(id) {
59
+ const items = this.menuItemsSubject.value.filter(item => item.id !== id);
60
+ this.menuItemsSubject.next(items);
61
+ }
62
+ /**
63
+ * Update sidebar configuration
64
+ */
65
+ setConfig(config) {
66
+ this.configSubject.next(config);
67
+ }
68
+ /**
69
+ * Get sidebar configuration
70
+ */
71
+ getConfig() {
72
+ return this.configSubject.value;
73
+ }
74
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: SidebarService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
75
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: SidebarService, providedIn: 'root' });
76
+ }
77
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: SidebarService, decorators: [{
78
+ type: Injectable,
79
+ args: [{
80
+ providedIn: 'root',
81
+ }]
82
+ }], ctorParameters: () => [] });
83
+
84
+ class Sidebar {
85
+ menuItems = [];
86
+ config = {};
87
+ collapsed = false;
88
+ logoUrl;
89
+ logoAlt = 'Logo';
90
+ showToggle = true;
91
+ menuItemClicked = new EventEmitter();
92
+ collapsedChange = new EventEmitter();
93
+ itemBadgeClick = new EventEmitter();
94
+ sidebarService = inject(SidebarService);
95
+ router = inject(Router);
96
+ get collapsed$() {
97
+ return this.sidebarService.collapsed$;
98
+ }
99
+ expandedItems = new Set();
100
+ isCollapsed = false;
101
+ defaultConfig = {
102
+ collapsed: false,
103
+ position: 'left',
104
+ width: '240px',
105
+ collapsedWidth: '60px',
106
+ backgroundColor: '#141F2D',
107
+ textColor: '#3A4D63',
108
+ hoverColor: '#FDFDFD',
109
+ activeColor: '#28B48C',
110
+ toggleButton: true,
111
+ overlayOnMobile: true,
112
+ };
113
+ ngOnInit() {
114
+ this.isCollapsed = this.collapsed;
115
+ this.sidebarService.setMenuItems(this.menuItems);
116
+ const mergedConfig = { ...this.defaultConfig, ...this.config };
117
+ this.sidebarService.setConfig(mergedConfig);
118
+ // Track router navigation for active state
119
+ this.router.events.pipe(filter(event => event instanceof NavigationEnd)).subscribe(() => {
120
+ // Trigger change detection for route changes
121
+ });
122
+ }
123
+ ngOnChanges(changes) {
124
+ if (changes['menuItems'] && !changes['menuItems'].firstChange) {
125
+ this.sidebarService.setMenuItems(this.menuItems);
126
+ }
127
+ if (changes['collapsed']) {
128
+ this.isCollapsed = this.collapsed;
129
+ }
130
+ if (changes['config'] && !changes['config'].firstChange) {
131
+ this.sidebarService.setConfig({ ...this.defaultConfig, ...this.config });
132
+ }
133
+ }
134
+ toggleSidebar() {
135
+ this.isCollapsed = !this.isCollapsed;
136
+ this.sidebarService.toggleCollapsed();
137
+ this.collapsedChange.emit(this.isCollapsed);
138
+ }
139
+ toggleExpand(item) {
140
+ if (item.children && item.children.length > 0) {
141
+ const itemId = item.id || item.label;
142
+ if (this.expandedItems.has(itemId)) {
143
+ this.expandedItems.delete(itemId);
144
+ }
145
+ else {
146
+ this.expandedItems.add(itemId);
147
+ }
148
+ }
149
+ }
150
+ isExpanded(item) {
151
+ const itemId = item.id || item.label;
152
+ // Check if explicitly expanded or should be expanded by default
153
+ return this.expandedItems.has(itemId) || item.expandedByDefault === true;
154
+ }
155
+ onMenuItemClick(item) {
156
+ if (item.action) {
157
+ item.action();
158
+ }
159
+ this.menuItemClicked.emit(item);
160
+ }
161
+ onBadgeClick(event, item) {
162
+ event.stopPropagation();
163
+ this.itemBadgeClick.emit(item);
164
+ }
165
+ hasChildren(item) {
166
+ return !!(item.children && item.children.length > 0);
167
+ }
168
+ isActiveRoute(item) {
169
+ if (item.isActive) {
170
+ return item.isActive();
171
+ }
172
+ return item.route ? this.router.url.includes(`/${item.route}`) : false;
173
+ }
174
+ getConfig() {
175
+ return this.sidebarService.getConfig();
176
+ }
177
+ closeSidebar() {
178
+ if (!this.isCollapsed) {
179
+ this.toggleSidebar();
180
+ }
181
+ }
182
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: Sidebar, deps: [], target: i0.ɵɵFactoryTarget.Component });
183
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.9", type: Sidebar, isStandalone: true, selector: "cwr-sidebar", inputs: { menuItems: "menuItems", config: "config", collapsed: "collapsed", logoUrl: "logoUrl", logoAlt: "logoAlt", showToggle: "showToggle" }, outputs: { menuItemClicked: "menuItemClicked", collapsedChange: "collapsedChange", itemBadgeClick: "itemBadgeClick" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"sidebar d-flex flex-column justify-content-between\" [class.isOpen]=\"!isCollapsed\">\n <div>\n <!-- Logo Section -->\n <div class=\"d-flex justify-content-between logo pe-3 w-100\" *ngIf=\"logoUrl || showToggle\">\n <img *ngIf=\"logoUrl\" [src]=\"logoUrl\" [alt]=\"logoAlt\" />\n <span\n class=\"icon-close_big d-md-none\"\n style=\"color: #fff; cursor: pointer\"\n [class.d-none]=\"isCollapsed\"\n (click)=\"closeSidebar()\"></span>\n </div>\n\n <!-- Navigation Wrapper -->\n <div class=\"nav-wrapper\" role=\"navigation\">\n <ul class=\"active\">\n <li \n *ngFor=\"let item of menuItems\"\n [class.active]=\"isActiveRoute(item)\"\n [class.disabled]=\"item.disabled\"\n [class.hidden]=\"item.visible && !item.visible()\"\n [ngClass]=\"item.cssClass\"\n [title]=\"item.tooltip || ''\"\n >\n <a \n class=\"d-flex align-items-center w-100\"\n [routerLink]=\"item.route\"\n [queryParams]=\"item.queryParams\"\n [queryParamsHandling]=\"item.queryParamsHandling\"\n [target]=\"item.target\"\n [routerLinkActive]=\"item.isActive ? '' : 'active'\"\n [routerLinkActiveOptions]=\"{ exact: false }\"\n (click)=\"onMenuItemClick(item)\"\n >\n <!-- Icon Section -->\n <div class=\"menu-icon-container\" *ngIf=\"item.icon\">\n <div [innerHTML]=\"item.icon\" class=\"svg-icon\"></div>\n </div>\n\n <!-- Label and Badge -->\n <div class=\"d-flex w-100 justify-content-between align-items-center\">\n <span class=\"nav-label\">{{ item.label }}</span>\n \n <!-- Badge -->\n <span \n class=\"badge-chip\"\n *ngIf=\"item.badge\"\n [style.background-color]=\"item.badge.color || '#28B48C'\"\n (click)=\"onBadgeClick($event, item)\"\n >\n {{ item.badge.text }}\n </span>\n </div>\n </a>\n\n <!-- Submenu -->\n <ul \n class=\"submenu-list\"\n *ngIf=\"hasChildren(item) && isExpanded(item)\"\n >\n <li \n *ngFor=\"let child of item.children\"\n [class.active]=\"isActiveRoute(child)\"\n [class.disabled]=\"child.disabled\"\n [class.hidden]=\"child.visible && !child.visible()\"\n [ngClass]=\"child.cssClass\"\n [title]=\"child.tooltip || ''\"\n >\n <a \n class=\"d-flex align-items-center submenu-link\"\n [routerLink]=\"child.route\"\n [queryParams]=\"child.queryParams\"\n [queryParamsHandling]=\"child.queryParamsHandling\"\n [target]=\"child.target\"\n [routerLinkActive]=\"child.isActive ? '' : 'active'\"\n [routerLinkActiveOptions]=\"{ exact: false }\"\n (click)=\"onMenuItemClick(child)\"\n >\n <span class=\"nav-label\">{{ child.label }}</span>\n </a>\n </li>\n </ul>\n\n <!-- Divider -->\n <div *ngIf=\"item.divider\" class=\"sidebar-divider\"></div>\n </li>\n </ul>\n </div>\n </div>\n\n <!-- Logout/Footer Section (optional) -->\n <div class=\"sidebar-footer\" *ngIf=\"!isCollapsed\">\n <slot name=\"footer\"></slot>\n </div>\n</div>\n", styles: [".sidebar{background:#141f2d;box-shadow:0 0 4px #0000001a;min-height:100vh;width:240px;border-right:1px solid #2C4057;font-family:Inter,sans-serif;position:fixed;top:0;left:0;z-index:300;transition:.5s;-webkit-transition:.5s;-moz-transition:.5s;display:flex;flex-direction:column;justify-content:space-between}.sidebar .d-flex{display:flex}.sidebar .flex-column{flex-direction:column}.sidebar .justify-content-between{justify-content:space-between}.sidebar .justify-content-center{justify-content:center}.sidebar .align-items-center{align-items:center}.sidebar .w-100{width:100%}.sidebar .pe-3{padding-right:1rem}.sidebar .d-md-none{display:none}.sidebar .d-none{display:none!important}.sidebar .me-12{margin-right:8px}.sidebar .badge-chip{display:flex;height:20px;padding:3px 6px;align-items:center;color:#fdfdfd;border-radius:4.5px;font-size:9px;font-weight:700;letter-spacing:.18px;text-align:center;white-space:nowrap;cursor:pointer;transition:all .3s ease}.sidebar .badge-chip:hover{opacity:.9;transform:scale(1.05)}.sidebar .fill-based-icons{fill:#3a4d63!important}.sidebar li.active .fill-based-icons{fill:#2c2b33!important}.sidebar li:not(.active) a:hover .fill-based-icons{fill:#fdfdfd!important}.sidebar .stroke-based-icons{stroke:#3a4d63!important}.sidebar li.active .stroke-based-icons{stroke:#2c2b33!important}.sidebar li:not(.active) a:hover .stroke-based-icons{stroke:#fdfdfd!important}.sidebar .nav-wrapper a{text-decoration:none;transition:all .3s ease}.sidebar .nav-wrapper .nav-label{font-weight:500;font-size:16px;line-height:19px;color:#3a4d63;transition:color .3s ease}.sidebar .nav-wrapper ul{list-style:none;padding:0;margin:0}.sidebar .nav-wrapper ul li{display:flex;justify-content:space-between;align-items:center;align-self:stretch;padding:8px 14px 8px 24px;height:52px;position:relative;transition:all .3s ease}.sidebar .nav-wrapper ul li.active{background:#28b48c;border-radius:0 8px 8px 0;margin-right:1.5rem}.sidebar .nav-wrapper ul li.active .nav-label,.sidebar .nav-wrapper ul li.active a:hover .nav-label{color:#2c2b33!important}.sidebar .nav-wrapper ul li:not(.active) a:hover .nav-label{color:#fdfdfd!important}.sidebar .nav-wrapper ul li.disabled{opacity:.5;cursor:not-allowed}.sidebar .nav-wrapper ul li.disabled a{pointer-events:none}.sidebar .nav-wrapper ul .submenu-list{list-style:none;padding:0;margin:0;background:#0003}.sidebar .nav-wrapper ul .submenu-list .submenu-item{padding:0;height:auto}.sidebar .nav-wrapper ul .submenu-list .submenu-item .submenu-link{padding:8px 14px 8px 48px;height:40px;display:flex;align-items:center;font-size:14px}.sidebar .nav-wrapper ul .submenu-list .submenu-item .submenu-link .nav-label{font-size:14px}.sidebar .logo{padding-top:36px;padding-left:24px;padding-bottom:66px}.sidebar .logo img{height:20px;object-fit:contain}.sidebar .menu-icon-container{display:flex;align-items:center;justify-content:center;min-width:24px}.sidebar .menu-icon-container .svg-icon{display:flex;align-items:center;justify-content:center}.sidebar .menu-icon-container .svg-icon svg{width:20px;height:20px}.sidebar .support{color:#3a4d63;padding:8px 26px;height:52px;font-weight:500;font-size:16px;cursor:pointer;text-align:center;text-decoration:none;transition:all .3s ease}.sidebar .support path,.sidebar .support circle{stroke:#3a4d63;transition:stroke .3s ease}.sidebar .support:hover{color:#fdfdfd!important}.sidebar .support:hover path,.sidebar .support:hover circle{stroke:#fdfdfd}.sidebar .logout{color:#ff677a;padding:8px 32px;font-weight:500;cursor:pointer;transition:all .3s ease}.sidebar .logout:hover{opacity:.8}.sidebar .icon-logout{color:#ff677a}.sidebar .sidebar-footer{padding:1rem;border-top:1px solid #2C4057}@media(max-width:768px){.sidebar{left:-240px;padding-bottom:50px;width:240px;transition:left .5s ease}.sidebar.isOpen{left:0!important;width:100%}.sidebar .d-md-none{display:block!important}.sidebar .icon-close_big{margin-right:1rem}}.d-flex{display:flex}.flex-column{flex-direction:column}.justify-content-between{justify-content:space-between}.align-items-center{align-items:center}.w-100{width:100%}.pe-3{padding-right:1rem}.sidebar-divider{height:1px;background-color:#ffffff1a;margin:8px 0;width:100%}li.hidden{display:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i2.RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
184
+ }
185
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: Sidebar, decorators: [{
186
+ type: Component,
187
+ args: [{ selector: 'cwr-sidebar', standalone: true, imports: [CommonModule, RouterModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"sidebar d-flex flex-column justify-content-between\" [class.isOpen]=\"!isCollapsed\">\n <div>\n <!-- Logo Section -->\n <div class=\"d-flex justify-content-between logo pe-3 w-100\" *ngIf=\"logoUrl || showToggle\">\n <img *ngIf=\"logoUrl\" [src]=\"logoUrl\" [alt]=\"logoAlt\" />\n <span\n class=\"icon-close_big d-md-none\"\n style=\"color: #fff; cursor: pointer\"\n [class.d-none]=\"isCollapsed\"\n (click)=\"closeSidebar()\"></span>\n </div>\n\n <!-- Navigation Wrapper -->\n <div class=\"nav-wrapper\" role=\"navigation\">\n <ul class=\"active\">\n <li \n *ngFor=\"let item of menuItems\"\n [class.active]=\"isActiveRoute(item)\"\n [class.disabled]=\"item.disabled\"\n [class.hidden]=\"item.visible && !item.visible()\"\n [ngClass]=\"item.cssClass\"\n [title]=\"item.tooltip || ''\"\n >\n <a \n class=\"d-flex align-items-center w-100\"\n [routerLink]=\"item.route\"\n [queryParams]=\"item.queryParams\"\n [queryParamsHandling]=\"item.queryParamsHandling\"\n [target]=\"item.target\"\n [routerLinkActive]=\"item.isActive ? '' : 'active'\"\n [routerLinkActiveOptions]=\"{ exact: false }\"\n (click)=\"onMenuItemClick(item)\"\n >\n <!-- Icon Section -->\n <div class=\"menu-icon-container\" *ngIf=\"item.icon\">\n <div [innerHTML]=\"item.icon\" class=\"svg-icon\"></div>\n </div>\n\n <!-- Label and Badge -->\n <div class=\"d-flex w-100 justify-content-between align-items-center\">\n <span class=\"nav-label\">{{ item.label }}</span>\n \n <!-- Badge -->\n <span \n class=\"badge-chip\"\n *ngIf=\"item.badge\"\n [style.background-color]=\"item.badge.color || '#28B48C'\"\n (click)=\"onBadgeClick($event, item)\"\n >\n {{ item.badge.text }}\n </span>\n </div>\n </a>\n\n <!-- Submenu -->\n <ul \n class=\"submenu-list\"\n *ngIf=\"hasChildren(item) && isExpanded(item)\"\n >\n <li \n *ngFor=\"let child of item.children\"\n [class.active]=\"isActiveRoute(child)\"\n [class.disabled]=\"child.disabled\"\n [class.hidden]=\"child.visible && !child.visible()\"\n [ngClass]=\"child.cssClass\"\n [title]=\"child.tooltip || ''\"\n >\n <a \n class=\"d-flex align-items-center submenu-link\"\n [routerLink]=\"child.route\"\n [queryParams]=\"child.queryParams\"\n [queryParamsHandling]=\"child.queryParamsHandling\"\n [target]=\"child.target\"\n [routerLinkActive]=\"child.isActive ? '' : 'active'\"\n [routerLinkActiveOptions]=\"{ exact: false }\"\n (click)=\"onMenuItemClick(child)\"\n >\n <span class=\"nav-label\">{{ child.label }}</span>\n </a>\n </li>\n </ul>\n\n <!-- Divider -->\n <div *ngIf=\"item.divider\" class=\"sidebar-divider\"></div>\n </li>\n </ul>\n </div>\n </div>\n\n <!-- Logout/Footer Section (optional) -->\n <div class=\"sidebar-footer\" *ngIf=\"!isCollapsed\">\n <slot name=\"footer\"></slot>\n </div>\n</div>\n", styles: [".sidebar{background:#141f2d;box-shadow:0 0 4px #0000001a;min-height:100vh;width:240px;border-right:1px solid #2C4057;font-family:Inter,sans-serif;position:fixed;top:0;left:0;z-index:300;transition:.5s;-webkit-transition:.5s;-moz-transition:.5s;display:flex;flex-direction:column;justify-content:space-between}.sidebar .d-flex{display:flex}.sidebar .flex-column{flex-direction:column}.sidebar .justify-content-between{justify-content:space-between}.sidebar .justify-content-center{justify-content:center}.sidebar .align-items-center{align-items:center}.sidebar .w-100{width:100%}.sidebar .pe-3{padding-right:1rem}.sidebar .d-md-none{display:none}.sidebar .d-none{display:none!important}.sidebar .me-12{margin-right:8px}.sidebar .badge-chip{display:flex;height:20px;padding:3px 6px;align-items:center;color:#fdfdfd;border-radius:4.5px;font-size:9px;font-weight:700;letter-spacing:.18px;text-align:center;white-space:nowrap;cursor:pointer;transition:all .3s ease}.sidebar .badge-chip:hover{opacity:.9;transform:scale(1.05)}.sidebar .fill-based-icons{fill:#3a4d63!important}.sidebar li.active .fill-based-icons{fill:#2c2b33!important}.sidebar li:not(.active) a:hover .fill-based-icons{fill:#fdfdfd!important}.sidebar .stroke-based-icons{stroke:#3a4d63!important}.sidebar li.active .stroke-based-icons{stroke:#2c2b33!important}.sidebar li:not(.active) a:hover .stroke-based-icons{stroke:#fdfdfd!important}.sidebar .nav-wrapper a{text-decoration:none;transition:all .3s ease}.sidebar .nav-wrapper .nav-label{font-weight:500;font-size:16px;line-height:19px;color:#3a4d63;transition:color .3s ease}.sidebar .nav-wrapper ul{list-style:none;padding:0;margin:0}.sidebar .nav-wrapper ul li{display:flex;justify-content:space-between;align-items:center;align-self:stretch;padding:8px 14px 8px 24px;height:52px;position:relative;transition:all .3s ease}.sidebar .nav-wrapper ul li.active{background:#28b48c;border-radius:0 8px 8px 0;margin-right:1.5rem}.sidebar .nav-wrapper ul li.active .nav-label,.sidebar .nav-wrapper ul li.active a:hover .nav-label{color:#2c2b33!important}.sidebar .nav-wrapper ul li:not(.active) a:hover .nav-label{color:#fdfdfd!important}.sidebar .nav-wrapper ul li.disabled{opacity:.5;cursor:not-allowed}.sidebar .nav-wrapper ul li.disabled a{pointer-events:none}.sidebar .nav-wrapper ul .submenu-list{list-style:none;padding:0;margin:0;background:#0003}.sidebar .nav-wrapper ul .submenu-list .submenu-item{padding:0;height:auto}.sidebar .nav-wrapper ul .submenu-list .submenu-item .submenu-link{padding:8px 14px 8px 48px;height:40px;display:flex;align-items:center;font-size:14px}.sidebar .nav-wrapper ul .submenu-list .submenu-item .submenu-link .nav-label{font-size:14px}.sidebar .logo{padding-top:36px;padding-left:24px;padding-bottom:66px}.sidebar .logo img{height:20px;object-fit:contain}.sidebar .menu-icon-container{display:flex;align-items:center;justify-content:center;min-width:24px}.sidebar .menu-icon-container .svg-icon{display:flex;align-items:center;justify-content:center}.sidebar .menu-icon-container .svg-icon svg{width:20px;height:20px}.sidebar .support{color:#3a4d63;padding:8px 26px;height:52px;font-weight:500;font-size:16px;cursor:pointer;text-align:center;text-decoration:none;transition:all .3s ease}.sidebar .support path,.sidebar .support circle{stroke:#3a4d63;transition:stroke .3s ease}.sidebar .support:hover{color:#fdfdfd!important}.sidebar .support:hover path,.sidebar .support:hover circle{stroke:#fdfdfd}.sidebar .logout{color:#ff677a;padding:8px 32px;font-weight:500;cursor:pointer;transition:all .3s ease}.sidebar .logout:hover{opacity:.8}.sidebar .icon-logout{color:#ff677a}.sidebar .sidebar-footer{padding:1rem;border-top:1px solid #2C4057}@media(max-width:768px){.sidebar{left:-240px;padding-bottom:50px;width:240px;transition:left .5s ease}.sidebar.isOpen{left:0!important;width:100%}.sidebar .d-md-none{display:block!important}.sidebar .icon-close_big{margin-right:1rem}}.d-flex{display:flex}.flex-column{flex-direction:column}.justify-content-between{justify-content:space-between}.align-items-center{align-items:center}.w-100{width:100%}.pe-3{padding-right:1rem}.sidebar-divider{height:1px;background-color:#ffffff1a;margin:8px 0;width:100%}li.hidden{display:none}\n"] }]
188
+ }], propDecorators: { menuItems: [{
189
+ type: Input
190
+ }], config: [{
191
+ type: Input
192
+ }], collapsed: [{
193
+ type: Input
194
+ }], logoUrl: [{
195
+ type: Input
196
+ }], logoAlt: [{
197
+ type: Input
198
+ }], showToggle: [{
199
+ type: Input
200
+ }], menuItemClicked: [{
201
+ type: Output
202
+ }], collapsedChange: [{
203
+ type: Output
204
+ }], itemBadgeClick: [{
205
+ type: Output
206
+ }] } });
207
+
208
+ /*
209
+ * Public API Surface of ng-cwr-sidebar
210
+ */
211
+
212
+ /**
213
+ * Generated bundle index. Do not edit.
214
+ */
215
+
216
+ export { Sidebar, SidebarService };
217
+ //# sourceMappingURL=elavarasanbititude-ng-cwr-sidebar.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"elavarasanbititude-ng-cwr-sidebar.mjs","sources":["../../../packages/ng-cwr-sidebar/src/lib/services/sidebar.service.ts","../../../packages/ng-cwr-sidebar/src/lib/sidebar/sidebar.ts","../../../packages/ng-cwr-sidebar/src/lib/sidebar/sidebar.html","../../../packages/ng-cwr-sidebar/src/public-api.ts","../../../packages/ng-cwr-sidebar/src/elavarasanbititude-ng-cwr-sidebar.ts"],"sourcesContent":["import { Injectable } from '@angular/core';\nimport { BehaviorSubject, Observable } from 'rxjs';\nimport { SidebarConfig, SidebarMenuItem } from '../models/sidebar.model';\n\n@Injectable({\n providedIn: 'root',\n})\nexport class SidebarService {\n private collapsedSubject = new BehaviorSubject<boolean>(false);\n private menuItemsSubject = new BehaviorSubject<SidebarMenuItem[]>([]);\n private configSubject = new BehaviorSubject<SidebarConfig>({});\n\n public collapsed$ = this.collapsedSubject.asObservable();\n public menuItems$ = this.menuItemsSubject.asObservable();\n public config$ = this.configSubject.asObservable();\n\n constructor() {}\n\n /**\n * Toggle the sidebar collapsed state\n */\n toggleCollapsed(): void {\n this.collapsedSubject.next(!this.collapsedSubject.value);\n }\n\n /**\n * Set the collapsed state\n */\n setCollapsed(collapsed: boolean): void {\n this.collapsedSubject.next(collapsed);\n }\n\n /**\n * Get current collapsed state\n */\n getCollapsed(): boolean {\n return this.collapsedSubject.value;\n }\n\n /**\n * Set menu items\n */\n setMenuItems(items: SidebarMenuItem[]): void {\n this.menuItemsSubject.next(items);\n }\n\n /**\n * Get menu items\n */\n getMenuItems(): SidebarMenuItem[] {\n return this.menuItemsSubject.value;\n }\n\n /**\n * Add a menu item\n */\n addMenuItem(item: SidebarMenuItem): void {\n const items = this.menuItemsSubject.value;\n this.menuItemsSubject.next([...items, item]);\n }\n\n /**\n * Remove a menu item by id\n */\n removeMenuItem(id: string): void {\n const items = this.menuItemsSubject.value.filter(item => item.id !== id);\n this.menuItemsSubject.next(items);\n }\n\n /**\n * Update sidebar configuration\n */\n setConfig(config: SidebarConfig): void {\n this.configSubject.next(config);\n }\n\n /**\n * Get sidebar configuration\n */\n getConfig(): SidebarConfig {\n return this.configSubject.value;\n }\n}\n","import { Component, OnInit, OnChanges, Input, Output, EventEmitter, ChangeDetectionStrategy, inject, SimpleChanges } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { RouterModule, Router, NavigationEnd } from '@angular/router';\nimport { SidebarService } from '../services/sidebar.service';\nimport { SidebarConfig, SidebarMenuItem } from '../models/sidebar.model';\nimport { filter } from 'rxjs/operators';\n\n@Component({\n selector: 'cwr-sidebar',\n standalone: true,\n imports: [CommonModule, RouterModule],\n templateUrl: './sidebar.html',\n styleUrl: './sidebar.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class Sidebar implements OnInit, OnChanges {\n @Input() menuItems: SidebarMenuItem[] = [];\n @Input() config: SidebarConfig = {};\n @Input() collapsed: boolean = false;\n @Input() logoUrl?: string;\n @Input() logoAlt: string = 'Logo';\n @Input() showToggle: boolean = true;\n @Output() menuItemClicked = new EventEmitter<SidebarMenuItem>();\n @Output() collapsedChange = new EventEmitter<boolean>();\n @Output() itemBadgeClick = new EventEmitter<SidebarMenuItem>();\n\n private sidebarService = inject(SidebarService);\n private router = inject(Router);\n\n get collapsed$() {\n return this.sidebarService.collapsed$;\n }\n\n expandedItems: Set<string> = new Set();\n isCollapsed = false;\n\n readonly defaultConfig: SidebarConfig = {\n collapsed: false,\n position: 'left',\n width: '240px',\n collapsedWidth: '60px',\n backgroundColor: '#141F2D',\n textColor: '#3A4D63',\n hoverColor: '#FDFDFD',\n activeColor: '#28B48C',\n toggleButton: true,\n overlayOnMobile: true,\n };\n\n ngOnInit(): void {\n this.isCollapsed = this.collapsed;\n this.sidebarService.setMenuItems(this.menuItems);\n const mergedConfig = { ...this.defaultConfig, ...this.config };\n this.sidebarService.setConfig(mergedConfig);\n\n // Track router navigation for active state\n this.router.events.pipe(filter(event => event instanceof NavigationEnd)).subscribe(() => {\n // Trigger change detection for route changes\n });\n }\n\n ngOnChanges(changes: SimpleChanges): void {\n if (changes['menuItems'] && !changes['menuItems'].firstChange) {\n this.sidebarService.setMenuItems(this.menuItems);\n }\n if (changes['collapsed']) {\n this.isCollapsed = this.collapsed;\n }\n if (changes['config'] && !changes['config'].firstChange) {\n this.sidebarService.setConfig({ ...this.defaultConfig, ...this.config });\n }\n }\n\n toggleSidebar(): void {\n this.isCollapsed = !this.isCollapsed;\n this.sidebarService.toggleCollapsed();\n this.collapsedChange.emit(this.isCollapsed);\n }\n\n toggleExpand(item: SidebarMenuItem): void {\n if (item.children && item.children.length > 0) {\n const itemId = item.id || item.label;\n if (this.expandedItems.has(itemId)) {\n this.expandedItems.delete(itemId);\n } else {\n this.expandedItems.add(itemId);\n }\n }\n }\n\n isExpanded(item: SidebarMenuItem): boolean {\n const itemId = item.id || item.label;\n // Check if explicitly expanded or should be expanded by default\n return this.expandedItems.has(itemId) || item.expandedByDefault === true;\n }\n\n onMenuItemClick(item: SidebarMenuItem): void {\n if (item.action) {\n item.action();\n }\n this.menuItemClicked.emit(item);\n }\n\n onBadgeClick(event: Event, item: SidebarMenuItem): void {\n event.stopPropagation();\n this.itemBadgeClick.emit(item);\n }\n\n hasChildren(item: SidebarMenuItem): boolean {\n return !!(item.children && item.children.length > 0);\n }\n\n isActiveRoute(item: SidebarMenuItem): boolean {\n if (item.isActive) {\n return item.isActive();\n }\n return item.route ? this.router.url.includes(`/${item.route}`) : false;\n }\n\n getConfig(): SidebarConfig {\n return this.sidebarService.getConfig();\n }\n\n closeSidebar(): void {\n if (!this.isCollapsed) {\n this.toggleSidebar();\n }\n }\n}\n","<div class=\"sidebar d-flex flex-column justify-content-between\" [class.isOpen]=\"!isCollapsed\">\n <div>\n <!-- Logo Section -->\n <div class=\"d-flex justify-content-between logo pe-3 w-100\" *ngIf=\"logoUrl || showToggle\">\n <img *ngIf=\"logoUrl\" [src]=\"logoUrl\" [alt]=\"logoAlt\" />\n <span\n class=\"icon-close_big d-md-none\"\n style=\"color: #fff; cursor: pointer\"\n [class.d-none]=\"isCollapsed\"\n (click)=\"closeSidebar()\"></span>\n </div>\n\n <!-- Navigation Wrapper -->\n <div class=\"nav-wrapper\" role=\"navigation\">\n <ul class=\"active\">\n <li \n *ngFor=\"let item of menuItems\"\n [class.active]=\"isActiveRoute(item)\"\n [class.disabled]=\"item.disabled\"\n [class.hidden]=\"item.visible && !item.visible()\"\n [ngClass]=\"item.cssClass\"\n [title]=\"item.tooltip || ''\"\n >\n <a \n class=\"d-flex align-items-center w-100\"\n [routerLink]=\"item.route\"\n [queryParams]=\"item.queryParams\"\n [queryParamsHandling]=\"item.queryParamsHandling\"\n [target]=\"item.target\"\n [routerLinkActive]=\"item.isActive ? '' : 'active'\"\n [routerLinkActiveOptions]=\"{ exact: false }\"\n (click)=\"onMenuItemClick(item)\"\n >\n <!-- Icon Section -->\n <div class=\"menu-icon-container\" *ngIf=\"item.icon\">\n <div [innerHTML]=\"item.icon\" class=\"svg-icon\"></div>\n </div>\n\n <!-- Label and Badge -->\n <div class=\"d-flex w-100 justify-content-between align-items-center\">\n <span class=\"nav-label\">{{ item.label }}</span>\n \n <!-- Badge -->\n <span \n class=\"badge-chip\"\n *ngIf=\"item.badge\"\n [style.background-color]=\"item.badge.color || '#28B48C'\"\n (click)=\"onBadgeClick($event, item)\"\n >\n {{ item.badge.text }}\n </span>\n </div>\n </a>\n\n <!-- Submenu -->\n <ul \n class=\"submenu-list\"\n *ngIf=\"hasChildren(item) && isExpanded(item)\"\n >\n <li \n *ngFor=\"let child of item.children\"\n [class.active]=\"isActiveRoute(child)\"\n [class.disabled]=\"child.disabled\"\n [class.hidden]=\"child.visible && !child.visible()\"\n [ngClass]=\"child.cssClass\"\n [title]=\"child.tooltip || ''\"\n >\n <a \n class=\"d-flex align-items-center submenu-link\"\n [routerLink]=\"child.route\"\n [queryParams]=\"child.queryParams\"\n [queryParamsHandling]=\"child.queryParamsHandling\"\n [target]=\"child.target\"\n [routerLinkActive]=\"child.isActive ? '' : 'active'\"\n [routerLinkActiveOptions]=\"{ exact: false }\"\n (click)=\"onMenuItemClick(child)\"\n >\n <span class=\"nav-label\">{{ child.label }}</span>\n </a>\n </li>\n </ul>\n\n <!-- Divider -->\n <div *ngIf=\"item.divider\" class=\"sidebar-divider\"></div>\n </li>\n </ul>\n </div>\n </div>\n\n <!-- Logout/Footer Section (optional) -->\n <div class=\"sidebar-footer\" *ngIf=\"!isCollapsed\">\n <slot name=\"footer\"></slot>\n </div>\n</div>\n","/*\n * Public API Surface of ng-cwr-sidebar\n */\n\nexport * from './lib/sidebar/sidebar';\nexport * from './lib/services/sidebar.service';\nexport * from './lib/models/sidebar.model';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@elavarasanbititude/ng-cwr-sidebar",
3
+ "version": "0.0.3",
4
+ "description": "A modern, flexible, and customizable Angular sidebar component library",
5
+ "author": "elavarasanbititude",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/yourusername/ng-cwr-sidebar"
10
+ },
11
+ "bugs": {
12
+ "url": "https://github.com/yourusername/ng-cwr-sidebar/issues"
13
+ },
14
+ "homepage": "https://github.com/yourusername/ng-cwr-sidebar#readme",
15
+ "keywords": [
16
+ "angular",
17
+ "sidebar",
18
+ "navigation",
19
+ "menu",
20
+ "component",
21
+ "responsive",
22
+ "scss"
23
+ ],
24
+ "peerDependencies": {
25
+ "@angular/common": "^21.2.0",
26
+ "@angular/core": "^21.2.0",
27
+ "@angular/router": "^21.2.0",
28
+ "rxjs": "~7.8.0"
29
+ },
30
+ "dependencies": {
31
+ "tslib": "^2.3.0"
32
+ },
33
+ "sideEffects": false,
34
+ "publishConfig": {
35
+ "access": "public"
36
+ },
37
+ "module": "fesm2022/elavarasanbititude-ng-cwr-sidebar.mjs",
38
+ "typings": "types/elavarasanbititude-ng-cwr-sidebar.d.ts",
39
+ "exports": {
40
+ "./package.json": {
41
+ "default": "./package.json"
42
+ },
43
+ ".": {
44
+ "types": "./types/elavarasanbititude-ng-cwr-sidebar.d.ts",
45
+ "default": "./fesm2022/elavarasanbititude-ng-cwr-sidebar.mjs"
46
+ }
47
+ },
48
+ "type": "module"
49
+ }
@@ -0,0 +1,140 @@
1
+ import * as rxjs from 'rxjs';
2
+ import { Observable } from 'rxjs';
3
+ import * as i0 from '@angular/core';
4
+ import { OnInit, OnChanges, EventEmitter, SimpleChanges } from '@angular/core';
5
+
6
+ /**
7
+ * Represents a menu item in the sidebar
8
+ */
9
+ interface SidebarMenuItem {
10
+ id?: string;
11
+ label: string;
12
+ icon?: string;
13
+ route?: string;
14
+ /** Query parameters to pass with the route */
15
+ queryParams?: Record<string, any>;
16
+ /** How to handle query parameters: 'merge', 'preserve' */
17
+ queryParamsHandling?: 'merge' | 'preserve';
18
+ action?: () => void;
19
+ children?: SidebarMenuItem[];
20
+ disabled?: boolean;
21
+ /** Show/hide menu item based on custom logic */
22
+ visible?: () => boolean;
23
+ /** Additional CSS classes to apply to the menu item */
24
+ cssClass?: string;
25
+ /** Link target attribute (_blank, _self, _parent, _top) */
26
+ target?: '_blank' | '_self' | '_parent' | '_top';
27
+ /** Badge configuration */
28
+ badge?: {
29
+ text: string;
30
+ color?: string;
31
+ };
32
+ /** Show a divider line after this menu item */
33
+ divider?: boolean;
34
+ /** Whether to expand nested children by default */
35
+ expandedByDefault?: boolean;
36
+ /** Tooltip text on hover */
37
+ tooltip?: string;
38
+ /**
39
+ * Custom function to determine if this item is active
40
+ * If provided, takes precedence over route-based active detection
41
+ */
42
+ isActive?: () => boolean;
43
+ }
44
+ /**
45
+ * Configuration for the sidebar component
46
+ */
47
+ interface SidebarConfig {
48
+ collapsed?: boolean;
49
+ position?: 'left' | 'right';
50
+ width?: string;
51
+ collapsedWidth?: string;
52
+ backgroundColor?: string;
53
+ textColor?: string;
54
+ hoverColor?: string;
55
+ activeColor?: string;
56
+ toggleButton?: boolean;
57
+ overlayOnMobile?: boolean;
58
+ }
59
+
60
+ declare class Sidebar implements OnInit, OnChanges {
61
+ menuItems: SidebarMenuItem[];
62
+ config: SidebarConfig;
63
+ collapsed: boolean;
64
+ logoUrl?: string;
65
+ logoAlt: string;
66
+ showToggle: boolean;
67
+ menuItemClicked: EventEmitter<SidebarMenuItem>;
68
+ collapsedChange: EventEmitter<boolean>;
69
+ itemBadgeClick: EventEmitter<SidebarMenuItem>;
70
+ private sidebarService;
71
+ private router;
72
+ get collapsed$(): rxjs.Observable<boolean>;
73
+ expandedItems: Set<string>;
74
+ isCollapsed: boolean;
75
+ readonly defaultConfig: SidebarConfig;
76
+ ngOnInit(): void;
77
+ ngOnChanges(changes: SimpleChanges): void;
78
+ toggleSidebar(): void;
79
+ toggleExpand(item: SidebarMenuItem): void;
80
+ isExpanded(item: SidebarMenuItem): boolean;
81
+ onMenuItemClick(item: SidebarMenuItem): void;
82
+ onBadgeClick(event: Event, item: SidebarMenuItem): void;
83
+ hasChildren(item: SidebarMenuItem): boolean;
84
+ isActiveRoute(item: SidebarMenuItem): boolean;
85
+ getConfig(): SidebarConfig;
86
+ closeSidebar(): void;
87
+ static ɵfac: i0.ɵɵFactoryDeclaration<Sidebar, never>;
88
+ static ɵcmp: i0.ɵɵComponentDeclaration<Sidebar, "cwr-sidebar", never, { "menuItems": { "alias": "menuItems"; "required": false; }; "config": { "alias": "config"; "required": false; }; "collapsed": { "alias": "collapsed"; "required": false; }; "logoUrl": { "alias": "logoUrl"; "required": false; }; "logoAlt": { "alias": "logoAlt"; "required": false; }; "showToggle": { "alias": "showToggle"; "required": false; }; }, { "menuItemClicked": "menuItemClicked"; "collapsedChange": "collapsedChange"; "itemBadgeClick": "itemBadgeClick"; }, never, never, true, never>;
89
+ }
90
+
91
+ declare class SidebarService {
92
+ private collapsedSubject;
93
+ private menuItemsSubject;
94
+ private configSubject;
95
+ collapsed$: Observable<boolean>;
96
+ menuItems$: Observable<SidebarMenuItem[]>;
97
+ config$: Observable<SidebarConfig>;
98
+ constructor();
99
+ /**
100
+ * Toggle the sidebar collapsed state
101
+ */
102
+ toggleCollapsed(): void;
103
+ /**
104
+ * Set the collapsed state
105
+ */
106
+ setCollapsed(collapsed: boolean): void;
107
+ /**
108
+ * Get current collapsed state
109
+ */
110
+ getCollapsed(): boolean;
111
+ /**
112
+ * Set menu items
113
+ */
114
+ setMenuItems(items: SidebarMenuItem[]): void;
115
+ /**
116
+ * Get menu items
117
+ */
118
+ getMenuItems(): SidebarMenuItem[];
119
+ /**
120
+ * Add a menu item
121
+ */
122
+ addMenuItem(item: SidebarMenuItem): void;
123
+ /**
124
+ * Remove a menu item by id
125
+ */
126
+ removeMenuItem(id: string): void;
127
+ /**
128
+ * Update sidebar configuration
129
+ */
130
+ setConfig(config: SidebarConfig): void;
131
+ /**
132
+ * Get sidebar configuration
133
+ */
134
+ getConfig(): SidebarConfig;
135
+ static ɵfac: i0.ɵɵFactoryDeclaration<SidebarService, never>;
136
+ static ɵprov: i0.ɵɵInjectableDeclaration<SidebarService>;
137
+ }
138
+
139
+ export { Sidebar, SidebarService };
140
+ export type { SidebarConfig, SidebarMenuItem };