sdga-ui 1.0.3 → 1.0.5

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 (76) hide show
  1. package/.github/workflows/deploy.yml +57 -0
  2. package/.github/workflows/publish.yml +2 -4
  3. package/README.md +8 -0
  4. package/css/dga-ui.css +60 -30
  5. package/css/dga-ui.css.map +1 -1
  6. package/demo-angular/.editorconfig +17 -0
  7. package/demo-angular/.vscode/extensions.json +4 -0
  8. package/demo-angular/.vscode/launch.json +20 -0
  9. package/demo-angular/.vscode/tasks.json +42 -0
  10. package/demo-angular/README.md +59 -0
  11. package/demo-angular/angular.json +88 -0
  12. package/demo-angular/package-lock.json +10459 -0
  13. package/demo-angular/package.json +50 -0
  14. package/demo-angular/public/.nojekyll +0 -0
  15. package/demo-angular/public/favicon.ico +0 -0
  16. package/demo-angular/public/i18n/ar.json +239 -0
  17. package/demo-angular/public/i18n/en.json +239 -0
  18. package/demo-angular/src/app/app.config.ts +20 -0
  19. package/demo-angular/src/app/app.html +52 -0
  20. package/demo-angular/src/app/app.routes.ts +45 -0
  21. package/demo-angular/src/app/app.scss +430 -0
  22. package/demo-angular/src/app/app.spec.ts +23 -0
  23. package/demo-angular/src/app/app.ts +88 -0
  24. package/demo-angular/src/app/shared/code-example/code-example.component.html +30 -0
  25. package/demo-angular/src/app/shared/code-example/code-example.component.scss +183 -0
  26. package/demo-angular/src/app/shared/code-example/code-example.component.ts +78 -0
  27. package/demo-angular/src/app/views/alerts/alerts.component.html +155 -0
  28. package/demo-angular/src/app/views/alerts/alerts.component.scss +3 -0
  29. package/demo-angular/src/app/views/alerts/alerts.component.ts +134 -0
  30. package/demo-angular/src/app/views/bootstrap/bootstrap.component.html +14 -0
  31. package/demo-angular/src/app/views/bootstrap/bootstrap.component.scss +91 -0
  32. package/demo-angular/src/app/views/bootstrap/bootstrap.component.ts +23 -0
  33. package/demo-angular/src/app/views/buttons/buttons.component.html +289 -0
  34. package/demo-angular/src/app/views/buttons/buttons.component.scss +14 -0
  35. package/demo-angular/src/app/views/buttons/buttons.component.ts +155 -0
  36. package/demo-angular/src/app/views/cards/cards.component.html +156 -0
  37. package/demo-angular/src/app/views/cards/cards.component.html.backup +156 -0
  38. package/demo-angular/src/app/views/cards/cards.component.scss +11 -0
  39. package/demo-angular/src/app/views/cards/cards.component.ts +194 -0
  40. package/demo-angular/src/app/views/forms/forms.component.html +347 -0
  41. package/demo-angular/src/app/views/forms/forms.component.scss +3 -0
  42. package/demo-angular/src/app/views/forms/forms.component.ts +222 -0
  43. package/demo-angular/src/app/views/home/home.component.html +38 -0
  44. package/demo-angular/src/app/views/home/home.component.scss +35 -0
  45. package/demo-angular/src/app/views/home/home.component.ts +12 -0
  46. package/demo-angular/src/app/views/links/links.component.html +140 -0
  47. package/demo-angular/src/app/views/links/links.component.scss +60 -0
  48. package/demo-angular/src/app/views/links/links.component.ts +123 -0
  49. package/demo-angular/src/app/views/tables/tables.component.html +289 -0
  50. package/demo-angular/src/app/views/tables/tables.component.scss +3 -0
  51. package/demo-angular/src/app/views/tables/tables.component.ts +278 -0
  52. package/demo-angular/src/app/views/toasts/toasts.component.html +201 -0
  53. package/demo-angular/src/app/views/toasts/toasts.component.scss +0 -0
  54. package/demo-angular/src/app/views/toasts/toasts.component.ts +182 -0
  55. package/demo-angular/src/index.html +14 -0
  56. package/demo-angular/src/main.ts +6 -0
  57. package/demo-angular/src/styles.scss +4 -0
  58. package/demo-angular/tsconfig.app.json +15 -0
  59. package/demo-angular/tsconfig.json +33 -0
  60. package/demo-angular/tsconfig.spec.json +15 -0
  61. package/package.json +5 -6
  62. package/theme/components/_accordion.scss +1 -1
  63. package/theme/components/_buttons.scss +6 -6
  64. package/theme/components/_content.scss +3 -3
  65. package/theme/components/_dropdowns.scss +1 -1
  66. package/theme/components/_forms-check.scss +1 -1
  67. package/theme/components/_forms-inputs.scss +1 -1
  68. package/theme/components/_list-group.scss +1 -1
  69. package/theme/components/_modals.scss +1 -1
  70. package/theme/components/_navigation.scss +2 -2
  71. package/theme/components/_overlays.scss +2 -2
  72. package/theme/components/_pagination.scss +1 -1
  73. package/theme/components/_popovers.scss +1 -1
  74. package/theme/components/_toasts.scss +1 -1
  75. package/theme/customizations/_cards.scss +3 -3
  76. package/theme/customizations/_links.scss +72 -24
@@ -0,0 +1,430 @@
1
+ // App Layout
2
+ .app-layout {
3
+ display: flex;
4
+ min-height: 100vh;
5
+ background-color: #eee;
6
+ }
7
+
8
+ // Sidebar
9
+ .sidebar {
10
+ width: 280px;
11
+ background: linear-gradient(180deg, #1e293b 0%, #0f172a 100%);
12
+ color: #e2e8f0;
13
+ display: flex;
14
+ flex-direction: column;
15
+ position: fixed;
16
+ left: 0;
17
+ top: 0;
18
+ height: 100vh;
19
+ overflow-y: auto;
20
+ transition: transform 0.3s ease;
21
+ z-index: 1000;
22
+
23
+ &::-webkit-scrollbar {
24
+ width: 6px;
25
+ }
26
+
27
+ &::-webkit-scrollbar-track {
28
+ background: #1e293b;
29
+ }
30
+
31
+ &::-webkit-scrollbar-thumb {
32
+ background: #475569;
33
+ border-radius: 3px;
34
+ }
35
+ }
36
+
37
+ .sidebar-collapsed .sidebar {
38
+ transform: translateX(-280px);
39
+ }
40
+
41
+ .sidebar-header {
42
+ padding: 2rem 1.5rem;
43
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
44
+
45
+ .logo {
46
+ font-size: 2rem;
47
+ font-weight: 700;
48
+ margin: 0;
49
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
50
+ -webkit-background-clip: text;
51
+ -webkit-text-fill-color: transparent;
52
+ background-clip: text;
53
+ }
54
+
55
+ .tagline {
56
+ margin: 0.5rem 0 0;
57
+ font-size: 0.875rem;
58
+ color: #94a3b8;
59
+ }
60
+ }
61
+
62
+ .sidebar-nav {
63
+ flex: 1;
64
+ padding: 1rem 0;
65
+ }
66
+
67
+ .nav-group {
68
+ margin-bottom: 1.5rem;
69
+ }
70
+
71
+ .nav-group-title {
72
+ padding: 0.5rem 1.5rem;
73
+ margin: 0 0 0.5rem;
74
+ font-size: 0.75rem;
75
+ font-weight: 600;
76
+ text-transform: uppercase;
77
+ letter-spacing: 0.05em;
78
+ color: #64748b;
79
+ }
80
+
81
+ .nav-list {
82
+ list-style: none;
83
+ padding: 0;
84
+ margin: 0;
85
+ }
86
+
87
+ .nav-item {
88
+ display: flex;
89
+ align-items: center;
90
+ padding: 0.75rem 1.5rem;
91
+ cursor: pointer;
92
+ transition: all 0.2s ease;
93
+ color: #cbd5e1;
94
+ position: relative;
95
+
96
+ &:hover {
97
+ background-color: rgba(255, 255, 255, 0.05);
98
+ color: #fff;
99
+ }
100
+
101
+ &.active {
102
+ background-color: rgba(102, 126, 234, 0.15);
103
+ color: #fff;
104
+ font-weight: 500;
105
+
106
+ &::before {
107
+ content: '';
108
+ position: absolute;
109
+ left: 0;
110
+ top: 0;
111
+ bottom: 0;
112
+ width: 3px;
113
+ background: linear-gradient(180deg, #667eea 0%, #764ba2 100%);
114
+ }
115
+ }
116
+ }
117
+
118
+ .nav-icon {
119
+ margin-right: 0.75rem;
120
+ font-size: 1.25rem;
121
+ width: 24px;
122
+ display: inline-block;
123
+ text-align: center;
124
+ }
125
+
126
+ .nav-label {
127
+ font-size: 0.9375rem;
128
+ }
129
+
130
+ .sidebar-footer {
131
+ padding: 1rem 1.5rem;
132
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
133
+
134
+ .version {
135
+ margin: 0;
136
+ font-size: 0.75rem;
137
+ color: #64748b;
138
+ text-align: center;
139
+ }
140
+ }
141
+
142
+ // Main Content
143
+ .main-content {
144
+ flex: 1;
145
+ margin-left: 280px;
146
+ transition: margin-left 0.3s ease, margin-right 0.3s ease;
147
+ }
148
+
149
+ // RTL: Adjust margin for right-positioned sidebar
150
+ [dir="rtl"] .main-content {
151
+ margin-left: 0;
152
+ margin-right: 280px;
153
+ }
154
+
155
+ .sidebar-collapsed .main-content {
156
+ margin-left: 0;
157
+ }
158
+
159
+ // RTL: Reset right margin when collapsed
160
+ [dir="rtl"] .sidebar-collapsed .main-content {
161
+ margin-right: 0;
162
+ }
163
+
164
+ .top-bar {
165
+ background: #fff;
166
+ padding: 1rem 2rem;
167
+ display: flex;
168
+ align-items: center;
169
+ gap: 1rem;
170
+ position: sticky;
171
+ top: 0;
172
+ z-index: 100;
173
+ }
174
+
175
+ .menu-toggle {
176
+ background: none;
177
+ border: none;
178
+ font-size: 1.5rem;
179
+ cursor: pointer;
180
+ padding: 0.5rem;
181
+ color: #475569;
182
+ transition: color 0.2s ease;
183
+
184
+ &:hover {
185
+ color: #1e293b;
186
+ }
187
+
188
+ .hamburger {
189
+ display: block;
190
+ }
191
+ }
192
+
193
+ .page-title {
194
+ margin: 0;
195
+ font-size: 1.5rem;
196
+ font-weight: 600;
197
+ color: #1e293b;
198
+ text-transform: capitalize;
199
+ flex: 1;
200
+ }
201
+
202
+ .top-bar-actions {
203
+ display: flex;
204
+ align-items: center;
205
+ gap: 0.75rem;
206
+ }
207
+
208
+ .theme-selector {
209
+ display: flex;
210
+ align-items: center;
211
+ gap: 0.75rem;
212
+
213
+ select {
214
+ padding: 0.5rem 2rem 0.5rem 0.75rem;
215
+ border: 1px solid #e2e8f0;
216
+ border-radius: 0.5rem;
217
+ background-color: #fff;
218
+ color: #1e293b;
219
+ font-size: 0.875rem;
220
+ font-weight: 500;
221
+ cursor: pointer;
222
+ transition: all 0.2s ease;
223
+ appearance: none;
224
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%2364748b' d='M6 9L1 4h10z'/%3E%3C/svg%3E");
225
+ background-repeat: no-repeat;
226
+ background-position: right 0.5rem center;
227
+ min-width: 140px;
228
+
229
+ &:hover {
230
+ border-color: #cbd5e0;
231
+ }
232
+
233
+ &:focus {
234
+ outline: none;
235
+ border-color: #667eea;
236
+ }
237
+ }
238
+
239
+ .theme-note {
240
+ font-size: 0.75rem;
241
+ color: #94a3b8;
242
+ font-style: italic;
243
+ }
244
+ }
245
+
246
+ .content-area {
247
+ padding: 2rem;
248
+ }
249
+
250
+ // View Styles
251
+ .view {
252
+ max-width: 1200px;
253
+
254
+ h1 {
255
+ font-size: 2.5rem;
256
+ font-weight: 700;
257
+ color: #1e293b;
258
+ margin: 0 0 1rem;
259
+ }
260
+
261
+ .intro {
262
+ font-size: 1.25rem;
263
+ color: #64748b;
264
+ margin-bottom: 3rem;
265
+ }
266
+
267
+ h2 {
268
+ font-size: 1.75rem;
269
+ font-weight: 600;
270
+ color: #1e293b;
271
+ margin: 2rem 0 1rem;
272
+ }
273
+
274
+ h3 {
275
+ font-size: 1.25rem;
276
+ font-weight: 600;
277
+ color: #475569;
278
+ margin: 1.5rem 0 1rem;
279
+ }
280
+
281
+ p {
282
+ color: #64748b;
283
+ line-height: 1.6;
284
+ }
285
+
286
+ .info-note {
287
+ background: #f0f9ff;
288
+ border-left: 4px solid #3b82f6;
289
+ padding: 1rem 1.25rem;
290
+ margin-top: 1rem;
291
+ border-radius: 0.375rem;
292
+ color: #1e40af;
293
+ font-size: 0.9375rem;
294
+
295
+ code {
296
+ background: #dbeafe;
297
+ padding: 0.125rem 0.375rem;
298
+ border-radius: 0.25rem;
299
+ font-size: 0.875rem;
300
+ color: #1e3a8a;
301
+ }
302
+ }
303
+ }
304
+
305
+ .button-group {
306
+ display: flex;
307
+ flex-wrap: wrap;
308
+ gap: 1rem;
309
+ align-items: center;
310
+ margin: 1rem 0;
311
+ }
312
+
313
+ // Demo Showcase (component + code)
314
+ .demo-showcase {
315
+ display: flex;
316
+ flex-direction: column;
317
+ gap: 1.5rem;
318
+ }
319
+
320
+ .code-example {
321
+ background: #1e293b;
322
+ border-radius: 8px;
323
+ overflow: hidden;
324
+ border: 1px solid #334155;
325
+
326
+ .code-header {
327
+ background: #0f172a;
328
+ padding: 0.75rem 1.5rem;
329
+ border-bottom: 1px solid #334155;
330
+ display: flex;
331
+ align-items: center;
332
+ justify-content: space-between;
333
+
334
+ .code-label {
335
+ font-size: 0.75rem;
336
+ font-weight: 600;
337
+ text-transform: uppercase;
338
+ letter-spacing: 0.05em;
339
+ color: #94a3b8;
340
+ }
341
+ }
342
+
343
+ pre {
344
+ margin: 0;
345
+ padding: 1.5rem;
346
+ overflow-x: auto;
347
+ background: transparent;
348
+
349
+ code {
350
+ font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
351
+ font-size: 0.875rem;
352
+ line-height: 1.6;
353
+ color: #e2e8f0;
354
+ }
355
+ }
356
+ }
357
+
358
+ // Props Table
359
+ .props-table {
360
+ overflow-x: auto;
361
+
362
+ table {
363
+ width: 100%;
364
+ border-collapse: collapse;
365
+ font-size: 0.9375rem;
366
+
367
+ thead {
368
+ background: #f8fafc;
369
+ border-bottom: 2px solid #e2e8f0;
370
+
371
+ th {
372
+ padding: 1rem;
373
+ text-align: left;
374
+ font-weight: 600;
375
+ color: #1e293b;
376
+ font-size: 0.875rem;
377
+ text-transform: uppercase;
378
+ letter-spacing: 0.05em;
379
+ }
380
+ }
381
+
382
+ tbody {
383
+ tr {
384
+ border-bottom: 1px solid #e2e8f0;
385
+
386
+ &:hover {
387
+ background: #f8fafc;
388
+ }
389
+
390
+ td {
391
+ padding: 1rem;
392
+ color: #475569;
393
+
394
+ code {
395
+ background: #f1f5f9;
396
+ padding: 0.2rem 0.4rem;
397
+ border-radius: 4px;
398
+ font-size: 0.85rem;
399
+ color: #0f172a;
400
+ font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
401
+ }
402
+ }
403
+ }
404
+ }
405
+ }
406
+ }
407
+
408
+ // Code Sections
409
+ .code-section {
410
+ margin-bottom: 2rem;
411
+
412
+ h3 {
413
+ margin-bottom: 0.5rem;
414
+ }
415
+
416
+ pre {
417
+ background: #1e293b;
418
+ color: #e2e8f0;
419
+ padding: 1.5rem;
420
+ border-radius: 8px;
421
+ overflow-x: auto;
422
+ margin: 0;
423
+
424
+ code {
425
+ font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
426
+ font-size: 0.875rem;
427
+ line-height: 1.6;
428
+ }
429
+ }
430
+ }
@@ -0,0 +1,23 @@
1
+ import { TestBed } from '@angular/core/testing';
2
+ import { App } from './app';
3
+
4
+ describe('App', () => {
5
+ beforeEach(async () => {
6
+ await TestBed.configureTestingModule({
7
+ imports: [App],
8
+ }).compileComponents();
9
+ });
10
+
11
+ it('should create the app', () => {
12
+ const fixture = TestBed.createComponent(App);
13
+ const app = fixture.componentInstance;
14
+ expect(app).toBeTruthy();
15
+ });
16
+
17
+ it('should render title', async () => {
18
+ const fixture = TestBed.createComponent(App);
19
+ await fixture.whenStable();
20
+ const compiled = fixture.nativeElement as HTMLElement;
21
+ expect(compiled.querySelector('h1')?.textContent).toContain('Hello, demo-angular');
22
+ });
23
+ });
@@ -0,0 +1,88 @@
1
+ import { Component, signal, inject, effect } from '@angular/core';
2
+ import { RouterOutlet, Router } from '@angular/router';
3
+ import { TranslateService, TranslateModule } from '@ngx-translate/core';
4
+
5
+ @Component({
6
+ selector: 'app-root',
7
+ imports: [RouterOutlet, TranslateModule],
8
+ templateUrl: './app.html',
9
+ styleUrl: './app.scss'
10
+ })
11
+ export class App {
12
+ protected readonly router = inject(Router);
13
+ protected readonly translate = inject(TranslateService);
14
+
15
+ protected readonly sidebarOpen = signal(true);
16
+ protected readonly currentTheme = signal<string | null>(null);
17
+ protected readonly currentLang = signal<'en' | 'ar'>('en');
18
+ protected readonly isRTL = signal(false);
19
+
20
+ constructor() {
21
+ // Initialize translations
22
+ this.translate.setFallbackLang('en');
23
+ this.translate.use('en').subscribe();
24
+ }
25
+
26
+ protected readonly themes = [
27
+ { id: 'default', name: 'Default', description: 'Standard SDGA theme', file: null },
28
+ { id: 'dark', name: 'Dark', description: 'Modern dark color palette', file: 'dark.scss' },
29
+ { id: 'material', name: 'Material', description: 'Material Design inspired', file: 'material.scss' },
30
+ { id: 'rounded', name: 'Rounded', description: 'Soft and friendly', file: 'rounded.scss' },
31
+ { id: 'minimal', name: 'Minimal', description: 'Clean and simple', file: 'minimal.scss' },
32
+ { id: 'vibrant', name: 'Vibrant', description: 'Bold and colorful', file: 'vibrant.scss' },
33
+ { id: 'large', name: 'Large', description: 'Big and prominent', file: 'large.scss' }
34
+ ];
35
+
36
+ protected readonly menuItems = [
37
+ {
38
+ groupKey: 'nav.getting_started',
39
+ items: [
40
+ { id: 'home', labelKey: 'nav.home', icon: '🏠', route: '/home' }
41
+ ]
42
+ },
43
+ {
44
+ groupKey: 'nav.components',
45
+ items: [
46
+ { id: 'alerts', labelKey: 'nav.alerts', icon: '⚠️', route: '/alerts' },
47
+ { id: 'buttons', labelKey: 'nav.buttons', icon: '🔘', route: '/buttons' },
48
+ { id: 'cards', labelKey: 'nav.cards', icon: '🃏', route: '/cards' },
49
+ { id: 'forms', labelKey: 'nav.forms', icon: '📝', route: '/forms' },
50
+ { id: 'tables', labelKey: 'nav.tables', icon: '📊', route: '/tables' },
51
+ { id: 'toasts', labelKey: 'nav.toasts', icon: '🔔', route: '/toasts' },
52
+ { id: 'links', labelKey: 'nav.links', icon: '🔗', route: '/links' },
53
+ { id: 'utilities', labelKey: 'nav.utilities', icon: '🛠️', route: '/utilities' },
54
+ { id: 'bootstrap', labelKey: 'nav.all_components', icon: '🎨', route: '/bootstrap' }
55
+ ]
56
+ }
57
+ ];
58
+
59
+ navigateTo(route: string): void {
60
+ this.router.navigate([route]);
61
+ }
62
+
63
+ toggleSidebar(): void {
64
+ this.sidebarOpen.update(open => !open);
65
+ }
66
+
67
+ toggleLanguage(): void {
68
+ const newLang = this.currentLang() === 'en' ? 'ar' : 'en';
69
+ this.currentLang.set(newLang);
70
+ this.translate.use(newLang).subscribe(() => {
71
+ this.isRTL.set(newLang === 'ar');
72
+ document.documentElement.setAttribute('dir', newLang === 'ar' ? 'rtl' : 'ltr');
73
+ document.documentElement.setAttribute('lang', newLang);
74
+ });
75
+ }
76
+
77
+ applyTheme(themeId: string): void {
78
+ this.currentTheme.set(themeId === 'default' ? null : themeId);
79
+
80
+ const theme = this.themes.find(t => t.id === themeId);
81
+ if (theme?.file) {
82
+ console.log(`To apply the ${theme.name} theme, import it in your styles.scss:`);
83
+ console.log(`@import 'dist/SDGA/themes/${theme.file}';`);
84
+ } else {
85
+ console.log('Using default theme (no import needed)');
86
+ }
87
+ }
88
+ }
@@ -0,0 +1,30 @@
1
+ <div class="code-example-wrapper">
2
+ <div class="code-header">
3
+ @if (title) {
4
+ <span class="code-title">{{ title }}</span>
5
+ }
6
+ <button class="btn-toggle" (click)="toggleExpand()" type="button">
7
+ {{ (isExpanded() ? 'code.hide' : 'code.show') | translate }}
8
+ </button>
9
+ </div>
10
+
11
+ @if (isExpanded()) {
12
+ <div class="code-example">
13
+ <div class="code-tabs">
14
+ @for (tab of availableTabs; track tab) {
15
+ <button
16
+ class="tab-btn"
17
+ [class.active]="activeTab() === tab"
18
+ (click)="setActiveTab(tab)"
19
+ type="button">
20
+ {{ 'code.' + tab | translate }}
21
+ </button>
22
+ }
23
+ <button class="btn-copy" (click)="copyCode()" type="button">
24
+ {{ (copySuccess() ? 'code.copied' : 'code.copy') | translate }}
25
+ </button>
26
+ </div>
27
+ <pre><code [innerHTML]="highlightedCode"></code></pre>
28
+ </div>
29
+ }
30
+ </div>