@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 };
|