ng-magary 0.0.1 → 0.0.2

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.
Files changed (63) hide show
  1. package/LICENSE.md +21 -0
  2. package/bun.lock +290 -0
  3. package/ng-package.json +7 -0
  4. package/package.json +11 -12
  5. package/src/lib/Button/button/button.html +29 -0
  6. package/src/lib/Button/button/button.module.ts +9 -0
  7. package/src/lib/Button/button/button.scss +196 -0
  8. package/src/lib/Button/button/button.spec.ts +18 -0
  9. package/src/lib/Button/button/button.ts +72 -0
  10. package/src/lib/Button/speed-dial/speed-dial-item.interface.ts +9 -0
  11. package/src/lib/Button/speed-dial/speed-dial.html +57 -0
  12. package/src/lib/Button/speed-dial/speed-dial.module.ts +8 -0
  13. package/src/lib/Button/speed-dial/speed-dial.scss +247 -0
  14. package/src/lib/Button/speed-dial/speed-dial.spec.ts +18 -0
  15. package/src/lib/Button/speed-dial/speed-dial.ts +106 -0
  16. package/src/lib/Form/cascade-select/cascade-select.html +1 -0
  17. package/src/lib/Form/cascade-select/cascade-select.module.ts +8 -0
  18. package/src/lib/Form/cascade-select/cascade-select.scss +0 -0
  19. package/src/lib/Form/cascade-select/cascade-select.spec.ts +18 -0
  20. package/src/lib/Form/cascade-select/cascade-select.ts +9 -0
  21. package/src/lib/Form/input/input.html +66 -0
  22. package/src/lib/Form/input/input.module.ts +9 -0
  23. package/src/lib/Form/input/input.scss +193 -0
  24. package/src/lib/Form/input/input.spec.ts +22 -0
  25. package/src/lib/Form/input/input.ts +132 -0
  26. package/src/lib/Menu/panelmenu/panelmenu.html +259 -0
  27. package/src/lib/Menu/panelmenu/panelmenu.interface.ts +13 -0
  28. package/src/lib/Menu/panelmenu/panelmenu.module.ts +9 -0
  29. package/src/lib/Menu/panelmenu/panelmenu.scss +177 -0
  30. package/src/lib/Menu/panelmenu/panelmenu.spec.ts +18 -0
  31. package/src/lib/Menu/panelmenu/panelmenu.ts +134 -0
  32. package/src/lib/Menu/sidebar/sidebar.html +85 -0
  33. package/src/lib/Menu/sidebar/sidebar.module.ts +9 -0
  34. package/src/lib/Menu/sidebar/sidebar.scss +153 -0
  35. package/src/lib/Menu/sidebar/sidebar.spec.ts +18 -0
  36. package/src/lib/Menu/sidebar/sidebar.ts +64 -0
  37. package/src/lib/Misc/avatar/avatar.html +44 -0
  38. package/src/lib/Misc/avatar/avatar.module.ts +9 -0
  39. package/src/lib/Misc/avatar/avatar.scss +167 -0
  40. package/src/lib/Misc/avatar/avatar.spec.ts +18 -0
  41. package/src/lib/Misc/avatar/avatar.ts +93 -0
  42. package/src/lib/Panel/card/card.html +58 -0
  43. package/src/lib/Panel/card/card.module.ts +9 -0
  44. package/src/lib/Panel/card/card.scss +290 -0
  45. package/src/lib/Panel/card/card.spec.ts +18 -0
  46. package/src/lib/Panel/card/card.ts +126 -0
  47. package/src/lib/Panel/tabs/tab/tab.spec.ts +18 -0
  48. package/src/lib/Panel/tabs/tab/tab.ts +12 -0
  49. package/src/lib/Panel/tabs/tabs.html +26 -0
  50. package/src/lib/Panel/tabs/tabs.module.ts +10 -0
  51. package/src/lib/Panel/tabs/tabs.scss +58 -0
  52. package/src/lib/Panel/tabs/tabs.spec.ts +18 -0
  53. package/src/lib/Panel/tabs/tabs.ts +57 -0
  54. package/src/lib/ng-magary.spec.ts +18 -0
  55. package/src/lib/ng-magary.ts +43 -0
  56. package/src/public-api.ts +5 -0
  57. package/tsconfig.lib.json +22 -0
  58. package/tsconfig.lib.prod.json +11 -0
  59. package/tsconfig.spec.json +14 -0
  60. package/fesm2022/ng-magary.mjs +0 -811
  61. package/fesm2022/ng-magary.mjs.map +0 -1
  62. package/index.d.ts +0 -422
  63. package/index.d.ts.map +0 -1
@@ -0,0 +1,177 @@
1
+ .panel-menu {
2
+ --panel-bg: var(--magary-panel-bg, #ffffff);
3
+ --panel-text: var(--magary-panel-text, #1f2937);
4
+ --panel-hover: var(--magary-panel-hover, #007bff);
5
+ --panel-radius: var(--magary-panel-radius, 8px);
6
+ --panel-shadow: var(--magary-panel-shadow, 0 2px 4px rgba(0, 0, 0, 0.1));
7
+ --panel-header-bg: var(--magary-panel-header-bg, rgba(0, 0, 0, 0.02));
8
+ --panel-header-hover: var(--magary-panel-header-hover, rgba(0, 0, 0, 0.07));
9
+ --panel-transition: var(--magary-panel-transition, 0.2s ease);
10
+ margin-bottom: 1rem;
11
+ transition: box-shadow 0.3s;
12
+ background: var(--panel-bg);
13
+ color: var(--panel-text);
14
+ border-radius: var(--panel-radius);
15
+ box-shadow: var(--panel-shadow);
16
+ }
17
+ .panel-header {
18
+ font-weight: bold;
19
+ cursor: pointer;
20
+ display: flex;
21
+ justify-content: space-between;
22
+ align-items: center;
23
+ padding: 10px 20px;
24
+ user-select: none;
25
+ border-radius: inherit;
26
+ background: var(--panel-header-bg);
27
+ transition: background var(--panel-transition), color var(--panel-transition);
28
+ &:hover {
29
+ background: var(--panel-header-hover);
30
+ }
31
+ &.header-hovered {
32
+ background: var(--panel-hover);
33
+ color: #ffffff;
34
+ }
35
+ .arrow {
36
+ transition: transform 0.3s ease;
37
+ &.open {
38
+ transform: rotate(180deg);
39
+ }
40
+ }
41
+ }
42
+ .panel-items {
43
+ list-style: none;
44
+ padding-left: 2px;
45
+ margin-top: 0;
46
+ overflow: hidden;
47
+ max-height: 0;
48
+ opacity: 0;
49
+ transform: scaleY(0.95);
50
+ transform-origin: top;
51
+ transition: max-height 0.3s ease, opacity 0.3s ease, transform 0.3s ease, margin-top 0.3s ease;
52
+ &.expanded {
53
+ opacity: 1;
54
+ transform: scaleY(1);
55
+ margin-top: 0.5rem;
56
+ max-height: 1000px;
57
+ }
58
+ }
59
+ :host-context(.panel-menu:not(.open)) .panel-items {
60
+ max-height: 0;
61
+ opacity: 0;
62
+ pointer-events: none;
63
+ }
64
+ .menu-item-base {
65
+ display: flex;
66
+ align-items: center;
67
+ padding: 0.5rem 0.75rem;
68
+ border-radius: 6px;
69
+ transition: background var(--panel-transition), color var(--panel-transition), box-shadow var(--panel-transition);
70
+ cursor: pointer;
71
+ text-decoration: none;
72
+ color: inherit;
73
+ &:hover,
74
+ &.item-hovered {
75
+ color: #ffffff;
76
+ background: var(--panel-hover);
77
+ box-shadow: 0 2px 8px rgba(0, 123, 255, 0.12);
78
+ }
79
+ &.disabled {
80
+ opacity: 0.5;
81
+ cursor: not-allowed;
82
+ pointer-events: none;
83
+ }
84
+ .item-icon {
85
+ width: 16px;
86
+ text-align: center;
87
+ margin-right: 0.5rem;
88
+ flex-shrink: 0;
89
+ }
90
+ .item-label {
91
+ flex: 1;
92
+ }
93
+ }
94
+ .panel-items li a {
95
+ @extend .menu-item-base;
96
+ }
97
+ .menu-item {
98
+ padding-bottom: 10px;
99
+ }
100
+ .category-item, .sub-category-item {
101
+ @extend .menu-item-base;
102
+ justify-content: space-between;
103
+ font-weight: 500;
104
+ padding-left: 30px;
105
+ .sub-arrow {
106
+ transition: transform 0.3s ease;
107
+ flex-shrink: 0;
108
+ &.expanded {
109
+ transform: rotate(180deg);
110
+ }
111
+ }
112
+ }
113
+ .sub-category-item {
114
+ padding-left: 1.5rem;
115
+ font-weight: normal;
116
+ font-size: 0.95rem;
117
+ }
118
+ .item-icon {
119
+ width: 16px;
120
+ text-align: center;
121
+ margin-right: 0.5rem;
122
+ }
123
+ .sub-items {
124
+ list-style: none;
125
+ padding-left: 1rem;
126
+ margin: 0;
127
+ max-height: 0;
128
+ overflow: hidden;
129
+ opacity: 0;
130
+ transition: max-height 0.3s ease, opacity 0.3s ease, padding 0.3s ease;
131
+ &.expanded {
132
+ max-height: 500px;
133
+ opacity: 1;
134
+ padding-top: 0.25rem;
135
+ padding-bottom: 0.25rem;
136
+ }
137
+ li a {
138
+ padding-left: 1.5rem;
139
+ font-size: 0.9rem;
140
+ &:hover,
141
+ &.item-hovered {
142
+ background: rgba(0, 123, 255, 0.1);
143
+ color: var(--panel-hover);
144
+ box-shadow: none;
145
+ }
146
+ }
147
+ }
148
+ .sub-sub-items {
149
+ list-style: none;
150
+ padding-left: 1.5rem;
151
+ margin: 0;
152
+ max-height: 0;
153
+ overflow: hidden;
154
+ opacity: 0;
155
+ transition: max-height 0.3s ease, opacity 0.3s ease, padding 0.3s ease;
156
+ &.expanded {
157
+ max-height: 300px;
158
+ opacity: 1;
159
+ padding-top: 0.25rem;
160
+ padding-bottom: 0.25rem;
161
+ }
162
+ li a {
163
+ padding-left: 2rem;
164
+ font-size: 0.85rem;
165
+ &:hover,
166
+ &.item-hovered {
167
+ background: rgba(0, 123, 255, 0.08);
168
+ color: var(--panel-hover);
169
+ box-shadow: none;
170
+ }
171
+ }
172
+ }
173
+ @for $i from 1 through 7 {
174
+ .shadow-#{$i} {
175
+ box-shadow: 0 #{2 + $i * 2}px #{4 + $i * 2}px rgba(0, 0, 0, 0.1 + $i * 0.05);
176
+ }
177
+ }
@@ -0,0 +1,18 @@
1
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+ import { Panelmenu } from './panelmenu';
3
+ describe('Panelmenu', () => {
4
+ let component: Panelmenu;
5
+ let fixture: ComponentFixture<Panelmenu>;
6
+ beforeEach(async () => {
7
+ await TestBed.configureTestingModule({
8
+ imports: [Panelmenu]
9
+ })
10
+ .compileComponents();
11
+ fixture = TestBed.createComponent(Panelmenu);
12
+ component = fixture.componentInstance;
13
+ fixture.detectChanges();
14
+ });
15
+ it('should create', () => {
16
+ expect(component).toBeTruthy();
17
+ });
18
+ });
@@ -0,0 +1,134 @@
1
+ import { CommonModule } from '@angular/common';
2
+ import { Component, input, output, signal, computed } from '@angular/core';
3
+ import { RouterModule } from '@angular/router';
4
+ export interface MenuItem {
5
+ label: string;
6
+ route?: string;
7
+ icon?: string;
8
+ children?: MenuItem[];
9
+ disabled?: boolean;
10
+ metadata?: any;
11
+ }
12
+ export interface MenuItemClickEvent {
13
+ item: MenuItem;
14
+ level: number;
15
+ path: string[];
16
+ }
17
+ export interface MenuToggleEvent {
18
+ isOpen: boolean;
19
+ menuTitle: string;
20
+ }
21
+ @Component({
22
+ selector: 'magary-panelmenu',
23
+ imports: [CommonModule, RouterModule],
24
+ templateUrl: './panelmenu.html',
25
+ styleUrl: './panelmenu.scss',
26
+ })
27
+ export class MagaryPanelmenu {
28
+ public title = input<string>('Panel Menu');
29
+ public items = input<MenuItem[]>([]);
30
+ public backgroundColor = input<string>('#f9fafb');
31
+ public textColor = input<string>('#1f2937');
32
+ public borderRadius = input<string>('8px');
33
+ public shadow = input<number>(0);
34
+ public width = input<string>('100%');
35
+ public hoverColor = input<string>('#007bff');
36
+ public allowMultipleExpanded = input<boolean>(false);
37
+ public defaultOpen = input<boolean>(false);
38
+ public menuToggle = output<MenuToggleEvent>();
39
+ public itemClick = output<MenuItemClickEvent>();
40
+ public itemExpand = output<{ item: MenuItem; expanded: boolean }>();
41
+ public isOpen = signal(this.defaultOpen());
42
+ public hoveredItem = signal<string | null>(null);
43
+ public hoveredHeader = signal<boolean>(false);
44
+ public expandedItems = signal<Set<string>>(new Set());
45
+ public panelStyles = computed(() => ({
46
+ '--panel-bg': this.backgroundColor(),
47
+ '--panel-text': this.textColor(),
48
+ '--panel-hover': this.hoverColor(),
49
+ '--panel-radius': this.borderRadius(),
50
+ width: this.width(),
51
+ }));
52
+ constructor() {
53
+ if (this.defaultOpen()) {
54
+ this.isOpen.set(true);
55
+ }
56
+ }
57
+ toggle(): void {
58
+ const newOpenState = !this.isOpen();
59
+ this.isOpen.set(newOpenState);
60
+ this.menuToggle.emit({
61
+ isOpen: newOpenState,
62
+ menuTitle: this.title(),
63
+ });
64
+ }
65
+ toggleSubItem(itemKey: string, item?: MenuItem): void {
66
+ this.expandedItems.update((expanded) => {
67
+ const newSet = new Set(expanded);
68
+ if (!this.allowMultipleExpanded() && !newSet.has(itemKey)) {
69
+ const currentLevel = itemKey.split('/').length;
70
+ for (const key of newSet) {
71
+ if (key.split('/').length === currentLevel) {
72
+ newSet.delete(key);
73
+ }
74
+ }
75
+ }
76
+ const wasExpanded = newSet.has(itemKey);
77
+ if (wasExpanded) {
78
+ newSet.delete(itemKey);
79
+ for (const key of newSet) {
80
+ if (key.startsWith(itemKey + '/')) {
81
+ newSet.delete(key);
82
+ }
83
+ }
84
+ } else {
85
+ newSet.add(itemKey);
86
+ }
87
+ if (item) {
88
+ this.itemExpand.emit({
89
+ item,
90
+ expanded: !wasExpanded,
91
+ });
92
+ }
93
+ return newSet;
94
+ });
95
+ }
96
+ onItemClick(item: MenuItem, level: number = 0, path: string[] = []): void {
97
+ if (item.disabled) return;
98
+ this.itemClick.emit({
99
+ item,
100
+ level,
101
+ path: [...path, item.label],
102
+ });
103
+ }
104
+ isSubItemExpanded(itemKey: string): boolean {
105
+ return this.expandedItems().has(itemKey);
106
+ }
107
+ hasChildren(item: MenuItem): boolean {
108
+ return !!(item.children && item.children.length > 0);
109
+ }
110
+ onItemHover(itemId: string): void {
111
+ this.hoveredItem.set(itemId);
112
+ }
113
+ onItemLeave(): void {
114
+ this.hoveredItem.set(null);
115
+ }
116
+ onHeaderHover(isHovering: boolean): void {
117
+ this.hoveredHeader.set(isHovering);
118
+ }
119
+ getItemId(item: MenuItem, index: number, parentPath: string = ''): string {
120
+ const path = parentPath
121
+ ? `${parentPath}-${item.label}-${index}`
122
+ : `${item.label}-${index}`;
123
+ return path;
124
+ }
125
+ getUniqueItemKey(item: MenuItem, parentPath: string = ''): string {
126
+ return parentPath ? `${parentPath}/${item.label}` : item.label;
127
+ }
128
+ isItemHovered(itemId: string): boolean {
129
+ return this.hoveredItem() === itemId;
130
+ }
131
+ isItemDisabled(item: MenuItem): boolean {
132
+ return item.disabled === true;
133
+ }
134
+ }
@@ -0,0 +1,85 @@
1
+ <button class="hamburger-button" (click)="toggleMobileSidebar()">
2
+ <i class="fas fa-bars"></i>
3
+ </button>
4
+ <div class="sidebar" [class.open]="isMobileOpen()">
5
+ <div class="sidebar-header">
6
+ <img [src]="logoSrc()" alt="Logo" class="logo" />
7
+ <span class="title">{{ appTitle() }}</span>
8
+ </div>
9
+ <div class="sidebar-content">
10
+ <div class="menu-container">
11
+ @if (sections().length > 0) {
12
+ @for (section of sections(); track $index) {
13
+ <magary-panelmenu
14
+ [title]="section.title"
15
+ [items]="section.items"
16
+ [backgroundColor]="section.backgroundColor || '#ffffff'"
17
+ [textColor]="section.textColor || '#374151'"
18
+ [hoverColor]="section.hoverColor || '#3b82f6'"
19
+ [borderRadius]="'12px'"
20
+ [shadow]="2"
21
+ [width]="'100%'"
22
+ />
23
+ }
24
+ }
25
+ @if (menu().length > 0 && sections().length === 0) {
26
+ <magary-panelmenu
27
+ [title]="menuTitle()"
28
+ [items]="menu()"
29
+ [backgroundColor]="'#ffffff'"
30
+ [textColor]="'#374151'"
31
+ [hoverColor]="'#3b82f6'"
32
+ [borderRadius]="'12px'"
33
+ [shadow]="2"
34
+ [width]="'100%'"
35
+ />
36
+ }
37
+ </div>
38
+ @if (showUserSection()) {
39
+ <div class="user-section">
40
+ <div class="user-info">
41
+ @switch (avatarConfig().type) {
42
+ @case ("image") {
43
+ <magary-avatar
44
+ [image]="avatarConfig().image || ''"
45
+ [size]="avatarConfig().size || 'normal'"
46
+ [shape]="avatarConfig().shape || 'circle'"
47
+ [badgeValue]="avatarConfig().badgeValue"
48
+ [badgeSeverity]="avatarConfig().badgeSeverity || 'danger'"
49
+ class="user-avatar"
50
+ />
51
+ }
52
+ @case ("label") {
53
+ <magary-avatar
54
+ [label]="avatarConfig().label || userName().charAt(0)"
55
+ [size]="avatarConfig().size || 'normal'"
56
+ [shape]="avatarConfig().shape || 'circle'"
57
+ [badgeValue]="avatarConfig().badgeValue"
58
+ [badgeSeverity]="avatarConfig().badgeSeverity || 'danger'"
59
+ class="user-avatar"
60
+ />
61
+ }
62
+ @case ("icon") {
63
+ <magary-avatar
64
+ [icon]="avatarConfig().icon || 'user'"
65
+ [size]="avatarConfig().size || 'normal'"
66
+ [shape]="avatarConfig().shape || 'circle'"
67
+ [badgeValue]="avatarConfig().badgeValue"
68
+ [badgeSeverity]="avatarConfig().badgeSeverity || 'danger'"
69
+ class="user-avatar"
70
+ />
71
+ }
72
+ }
73
+ <div class="user-details">
74
+ <div class="username">{{ userName() }}</div>
75
+ <div class="email">{{ userEmail() }}</div>
76
+ </div>
77
+ </div>
78
+ <button class="logout-button" (click)="logout()">
79
+ <i class="fas fa-sign-out-alt"></i>
80
+ Cerrar sesión
81
+ </button>
82
+ </div>
83
+ }
84
+ </div>
85
+ </div>
@@ -0,0 +1,9 @@
1
+ import { NgModule } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import { Sidebar } from './sidebar';
4
+ import { RouterModule } from '@angular/router';
5
+ @NgModule({
6
+ imports: [CommonModule, Sidebar, RouterModule],
7
+ exports: [Sidebar],
8
+ })
9
+ export class SidebarModule {}
@@ -0,0 +1,153 @@
1
+ .sidebar {
2
+ display: flex;
3
+ flex-direction: column;
4
+ height: 100vh;
5
+ width: 280px;
6
+ background: #f8fafc;
7
+ border-right: 1px solid #e2e8f0;
8
+ box-shadow: 2px 0 8px rgba(0, 0, 0, 0.1);
9
+ }
10
+ .sidebar-header {
11
+ display: flex;
12
+ align-items: center;
13
+ padding-left: 30px;
14
+ border-bottom: 1px solid #e2e8f0;
15
+ background: #ffffff;
16
+ height: 60px;
17
+ gap: 12px;
18
+ .logo {
19
+ width: 30px;
20
+ height: 30px;
21
+ }
22
+ .title {
23
+ font-size: 1.25rem;
24
+ font-weight: 700;
25
+ color: #1f2937;
26
+ letter-spacing: -0.025em;
27
+ }
28
+ }
29
+ .sidebar-content {
30
+ flex: 1;
31
+ display: flex;
32
+ flex-direction: column;
33
+ overflow-y: auto;
34
+ padding: 1rem;
35
+ &::-webkit-scrollbar {
36
+ width: 6px;
37
+ }
38
+ &::-webkit-scrollbar-track {
39
+ background: transparent;
40
+ }
41
+ &::-webkit-scrollbar-thumb {
42
+ background: #cbd5e1;
43
+ border-radius: 3px;
44
+ &:hover {
45
+ background: #94a3b8;
46
+ }
47
+ }
48
+ }
49
+ .menu-container {
50
+ flex: 1;
51
+ overflow-y: auto;
52
+ }
53
+ .user-section {
54
+ margin-top: auto;
55
+ padding: 1rem;
56
+ border-top: 1px solid #e2e8f0;
57
+ background: #ffffff;
58
+ border-radius: 12px;
59
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
60
+ flex-shrink: 0;
61
+ }
62
+ .user-info {
63
+ display: flex;
64
+ align-items: center;
65
+ margin-bottom: 1rem;
66
+ gap: 25px;
67
+ .avatar {
68
+ width: 40px;
69
+ height: 40px;
70
+ border-radius: 50%;
71
+ margin-right: 0.75rem;
72
+ border: 2px solid #e2e8f0;
73
+ }
74
+ .user-details {
75
+ flex: 1;
76
+ .username {
77
+ font-weight: 600;
78
+ color: #1f2937;
79
+ font-size: 0.875rem;
80
+ margin-bottom: 0.125rem;
81
+ }
82
+ .email {
83
+ color: #6b7280;
84
+ font-size: 0.75rem;
85
+ }
86
+ }
87
+ }
88
+ .logout-button {
89
+ width: 100%;
90
+ display: flex;
91
+ align-items: center;
92
+ justify-content: center;
93
+ gap: 0.5rem;
94
+ padding: 0.75rem 1rem;
95
+ background: #ef4444;
96
+ color: white;
97
+ border: none;
98
+ border-radius: 8px;
99
+ font-size: 0.875rem;
100
+ font-weight: 500;
101
+ cursor: pointer;
102
+ transition: all 0.2s ease;
103
+ &:hover {
104
+ background: #dc2626;
105
+ transform: translateY(-1px);
106
+ box-shadow: 0 4px 12px rgba(239, 68, 68, 0.3);
107
+ }
108
+ &:active {
109
+ transform: translateY(0);
110
+ }
111
+ i {
112
+ font-size: 0.875rem;
113
+ }
114
+ }
115
+ .hamburger-button {
116
+ display: none;
117
+ position: fixed;
118
+ top: 15px;
119
+ left: 15px;
120
+ z-index: 1100;
121
+ background: white;
122
+ border: 1px solid #e5e7eb;
123
+ border-radius: 8px;
124
+ padding: 0.5rem 0.75rem;
125
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
126
+ cursor: pointer;
127
+ i {
128
+ font-size: 1.25rem;
129
+ color: #1f2937;
130
+ }
131
+ }
132
+ @media (max-width: 768px) {
133
+ .hamburger-button {
134
+ display: block;
135
+ }
136
+ }
137
+ @media (max-width: 768px) {
138
+ .sidebar {
139
+ width: 100%;
140
+ position: fixed;
141
+ top: 0;
142
+ left: 0;
143
+ z-index: 1000;
144
+ transform: translateX(-100%);
145
+ transition: transform 0.3s ease;
146
+ &.open {
147
+ transform: translateX(0);
148
+ }
149
+ }
150
+ .sidebar-header{
151
+ padding: 20px 20px 20px 90px;
152
+ }
153
+ }
@@ -0,0 +1,18 @@
1
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+ import { Sidebar } from './sidebar';
3
+ describe('Sidebar', () => {
4
+ let component: Sidebar;
5
+ let fixture: ComponentFixture<Sidebar>;
6
+ beforeEach(async () => {
7
+ await TestBed.configureTestingModule({
8
+ imports: [Sidebar]
9
+ })
10
+ .compileComponents();
11
+ fixture = TestBed.createComponent(Sidebar);
12
+ component = fixture.componentInstance;
13
+ fixture.detectChanges();
14
+ });
15
+ it('should create', () => {
16
+ expect(component).toBeTruthy();
17
+ });
18
+ });
@@ -0,0 +1,64 @@
1
+ import { CommonModule } from '@angular/common';
2
+ import { Component, input, output, signal } from '@angular/core';
3
+ import { RouterModule } from '@angular/router';
4
+ import { MagaryPanelmenu } from '../panelmenu/panelmenu';
5
+ import { MenuItem } from '../panelmenu/panelmenu.interface';
6
+ import {
7
+ MagaryAvatar,
8
+ BadgeSeverity,
9
+ AvatarSize,
10
+ AvatarShape,
11
+ } from '../../Misc/avatar/avatar';
12
+ interface SidebarSection {
13
+ title: string;
14
+ items: MenuItem[];
15
+ backgroundColor?: string;
16
+ textColor?: string;
17
+ hoverColor?: string;
18
+ }
19
+ type AvatarType = 'image' | 'label' | 'icon';
20
+ interface AvatarConfig {
21
+ type: AvatarType;
22
+ size?: AvatarSize;
23
+ shape?: AvatarShape;
24
+ image?: string;
25
+ label?: string;
26
+ icon?: string;
27
+ badgeValue?: string;
28
+ badgeSeverity?: BadgeSeverity | undefined;
29
+ }
30
+ @Component({
31
+ selector: 'magary-sidebar',
32
+ imports: [CommonModule, RouterModule, MagaryPanelmenu, MagaryAvatar],
33
+ templateUrl: './sidebar.html',
34
+ styleUrl: './sidebar.scss',
35
+ })
36
+ export class Sidebar {
37
+ public sections = input<SidebarSection[]>([]);
38
+ public menu = input<MenuItem[]>([]);
39
+ public menuTitle = input<string>('Menu');
40
+ public showUserSection = input<boolean>(false);
41
+ public userName = input<string>('John Doe');
42
+ public userEmail = input<string>('user@example.com');
43
+ public avatarConfig = input<AvatarConfig>({
44
+ type: 'label',
45
+ label: 'U',
46
+ size: 'normal',
47
+ shape: 'circle',
48
+ badgeSeverity: 'danger',
49
+ });
50
+ public logoSrc = input<string>('assets/logo.svg');
51
+ public appTitle = input<string>('PRIMENG');
52
+ public isMobileOpen = signal(false);
53
+ Logout = output<void>();
54
+ closeSidebar = output<void>();
55
+ toggleMobileSidebar() {
56
+ this.isMobileOpen.update((open) => !open);
57
+ }
58
+ closeMobileSidebar() {
59
+ this.isMobileOpen.set(false);
60
+ }
61
+ logout() {
62
+ this.Logout.emit();
63
+ }
64
+ }
@@ -0,0 +1,44 @@
1
+ <div
2
+ class="avatar-wrapper"
3
+ [ngClass]="avatarClasses()"
4
+ [ngStyle]="customStyle()"
5
+ [attr.aria-label]="alt()"
6
+ [attr.role]="clickable() ? 'button' : null"
7
+ [attr.tabindex]="clickable() && !disabled() ? '0' : null"
8
+ (click)="clickable() && onAvatarClick()"
9
+ (keydown.enter)="clickable() && onAvatarClick()"
10
+ (keydown.space)="clickable() && onAvatarClick()"
11
+ >
12
+ @if (loading()) {
13
+ <div class="avatar-loading">
14
+ <i class="fas fa-spinner fa-spin"></i>
15
+ </div>
16
+ } @else {
17
+ @if (shouldShowImage()) {
18
+ <img
19
+ [src]="image()"
20
+ [alt]="alt()"
21
+ class="avatar-img"
22
+ [ngClass]="shape()"
23
+ (error)="onImageError()"
24
+ (load)="onImageLoad()"
25
+ />
26
+ }
27
+ @if (shouldShowIcon()) {
28
+ <i [class]="'fas fa-' + icon()" class="avatar-icon"></i>
29
+ }
30
+ @if (shouldShowLabel()) {
31
+ <span class="avatar-label">{{ displayLabel() }}</span>
32
+ }
33
+ }
34
+ @if (badgeValue()) {
35
+ <div
36
+ class="badge"
37
+ [ngClass]="badgeSeverity()"
38
+ [attr.aria-label]="'Badge: ' + badgeValue()"
39
+ (click)="onBadgeClick($event)"
40
+ >
41
+ {{ badgeValue() }}
42
+ </div>
43
+ }
44
+ </div>