@fasteros/themes 1.0.0

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.
@@ -0,0 +1,3951 @@
1
+ import * as i0 from '@angular/core';
2
+ import { Component, Injectable, Directive, ContentChild, Input, Pipe, EventEmitter, inject, DestroyRef, ContentChildren, Output, ChangeDetectionStrategy, HostListener, NgModule } from '@angular/core';
3
+ import * as i1 from '@angular/common/http';
4
+ import { HttpHeaders, HttpClientModule } from '@angular/common/http';
5
+ import * as i2 from '@angular/common';
6
+ import { CommonModule } from '@angular/common';
7
+ import { firstValueFrom, Subject, fromEvent, merge, of } from 'rxjs';
8
+ import { debounceTime, map } from 'rxjs/operators';
9
+ import { trigger, state, transition, style, animate } from '@angular/animations';
10
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
11
+
12
+ class FasterThemesComponent {
13
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: FasterThemesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
14
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.15", type: FasterThemesComponent, isStandalone: false, selector: "lib-faster-themes", ngImport: i0, template: `
15
+ <p>
16
+ faster-themes works!
17
+ </p>
18
+ `, isInline: true }); }
19
+ }
20
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: FasterThemesComponent, decorators: [{
21
+ type: Component,
22
+ args: [{ selector: 'lib-faster-themes', template: `
23
+ <p>
24
+ faster-themes works!
25
+ </p>
26
+ `, standalone: false }]
27
+ }] });
28
+
29
+ // Base URL for the Napkin themes API. Update this value to point at your
30
+ // deployed backend; the apiUrl below is derived from it at module load.
31
+ const THEME_API_BASE_URL = 'https://bzw27udtrayx7u6pvbnexfdk4i0hcsia.lambda-url.us-east-1.on.aws/api';
32
+ // API-key-authenticated reads against /api/platform-objects/themes.
33
+ // The apiKey identifies the tenant; the backend scopes results accordingly.
34
+ class ThemeService {
35
+ constructor(http) {
36
+ this.http = http;
37
+ this.apiUrl = `${THEME_API_BASE_URL.replace(/\/+$/, '')}/platform-objects/themes`;
38
+ }
39
+ headers(apiKey) {
40
+ return new HttpHeaders({
41
+ 'X-API-Key': apiKey,
42
+ 'Content-Type': 'application/json',
43
+ });
44
+ }
45
+ getThemes(apiKey) {
46
+ return firstValueFrom(this.http.get(this.apiUrl, {
47
+ headers: this.headers(apiKey),
48
+ }));
49
+ }
50
+ getTheme(themeId, apiKey) {
51
+ return firstValueFrom(this.http.get(`${this.apiUrl}/${themeId}`, {
52
+ headers: this.headers(apiKey),
53
+ }));
54
+ }
55
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: ThemeService, deps: [{ token: i1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable }); }
56
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: ThemeService, providedIn: 'root' }); }
57
+ }
58
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: ThemeService, decorators: [{
59
+ type: Injectable,
60
+ args: [{ providedIn: 'root' }]
61
+ }], ctorParameters: () => [{ type: i1.HttpClient }] });
62
+
63
+ /* Copyright © 2023-2025 Napkin Apps Corp. All rights reserved. */
64
+ class FasterThemesService {
65
+ // Dead code - @HostListener has no effect on a service. Window resize is handled by listenToWindowSize().
66
+ // @HostListener('window:resize', ['$event'])
67
+ // onWindowResize() {
68
+ // this.screenWidth = window.innerWidth;
69
+ // this.screenHeight = window.innerHeight;
70
+ // }
71
+ constructor(
72
+ /*public global: Globals,*/ ngZone,
73
+ // Firebase removed - no longer used
74
+ // @Inject(FAT) private authPromise: Promise<Auth>,
75
+ // @Inject(INIT_THEMES) private initThemesPromise: Promise<any>,
76
+ themeService) {
77
+ this.ngZone = ngZone;
78
+ this.themeService = themeService;
79
+ this.screenWidth = window.innerWidth;
80
+ this.screenHeight = window.innerHeight;
81
+ this.parentTheme = {}; // most recent theme assignment
82
+ this.resize$ = new Subject();
83
+ this.allThemes = [];
84
+ // Firebase removed - no longer used
85
+ // private watching: boolean = false;
86
+ // private async watchThemes(docId: string) {
87
+ // const app: FirebaseApp = (await this.authPromise).app
88
+ // const themesQuery = query(collection(getFirestore(app), 'company', docId, 'themes'),
89
+ // where("removed","!=", true))
90
+ //
91
+ // onSnapshot(themesQuery, async (snapshot) => {
92
+ // snapshot.docChanges().forEach(async (change) => {
93
+ // const themeId = change.doc.id;
94
+ // //@ts-ignore: Ensure FasterTheme is correctly defined
95
+ // const themeData: FasterTheme = change.doc.data();
96
+ // themeData.id = themeId;
97
+ // if (change.type === 'added') {
98
+ // this.allThemes.push(themeData);
99
+ // } else if (change.type === 'modified') {
100
+ // const index = this.allThemes.findIndex((theme) => theme.id === themeId);
101
+ // if (index !== -1) {
102
+ // this.allThemes[index] = Object.assign(this.allThemes[index], themeData);
103
+ // }
104
+ // } else if (change.type === 'removed') {
105
+ // const index = this.allThemes.findIndex((theme) => theme.id === themeId);
106
+ // if (index !== -1) {
107
+ // this.allThemes.splice(index, 1);
108
+ // }
109
+ // }
110
+ // });
111
+ // },
112
+ // async (error: any) => {
113
+ // console.error("Error watching themes: ", error);
114
+ // console.log("using platform themes as fallback");
115
+ // const themesRef = collection(getFirestore(app), 'platform', 'OdCDgRWTZhTMzW8N0y9y', 'themes');
116
+ // const themes = await getDocs(themesRef);
117
+ // for (const doc of themes.docs) {
118
+ // const data = doc.data() as FasterTheme;
119
+ // data.id = doc.id;
120
+ // this.allThemes.push(data);
121
+ // }
122
+ // })
123
+ // }
124
+ this.defaultStyle = {
125
+ editedBy: "napkinPackage",
126
+ responsiveMarginMax: 175,
127
+ name: "Default",
128
+ responsiveFontSizeMin: 70,
129
+ responsiveName: "removeName",
130
+ companyId: "napkinPackage",
131
+ responsiveStandardWidth: 1980,
132
+ id: "napkinPackage",
133
+ responsivePaddingMax: 175,
134
+ removed: false,
135
+ responsivePaddingMin: 80,
136
+ palette: [
137
+ {
138
+ colorName: "BrandFocus",
139
+ backgroundColor: " #85FFBD",
140
+ gradient: "background-image: linear-gradient(45deg, #85FFBD 0%, #FFFB7D 100%)",
141
+ color: "#00695c"
142
+ },
143
+ {
144
+ colorName: "BrandFont",
145
+ color: "0xffaaff",
146
+ gradient: "background-image: linear-gradient(45deg, #45FA8D 0%, #00457D 100%)",
147
+ backgroundColor: "#e1f5fe"
148
+ },
149
+ {
150
+ backgroundColor: "#80cbc4",
151
+ colorName: "Primary",
152
+ color: "#5d4037"
153
+ },
154
+ {
155
+ gradient: "background-image: radial-gradient(circle, rgba(117, 194, 224, 1) 0%, rgba(87, 160, 199, 1) 50%, rgba(83, 104, 237, 1) 100%)",
156
+ backgroundColor: "#a6e4fc",
157
+ color: "0xffaaff",
158
+ colorName: "Settings"
159
+ },
160
+ {
161
+ backgroundColor: "#4e342e",
162
+ color: "#f5f5f5",
163
+ colorName: "whiteOnBrown"
164
+ },
165
+ {
166
+ colorName: "Share",
167
+ color: "#0288d1",
168
+ backgroundColor: "#adfcac",
169
+ gradient: ""
170
+ }
171
+ ],
172
+ share: {
173
+ writeAccess: "Private",
174
+ publicWriteAccess: "None",
175
+ readAccess: "Private",
176
+ publicReadAccess: "Anyone"
177
+ },
178
+ responsiveFontSizeMax: 120,
179
+ coreTheme: true,
180
+ accordions: [
181
+ {
182
+ headerClosedBackground: "#81d4fa",
183
+ accordionLayout: "row wrap",
184
+ accordionAlignment: "center",
185
+ headerOpenBackground: "#81d4fa",
186
+ accordionPadding: "12px",
187
+ accordionName: "base",
188
+ headerClosedType: "H4",
189
+ accordionExtra: "",
190
+ accordionBoxShadow: "",
191
+ headerClosedExtra: "",
192
+ headerOpenBorder: "1px solid white",
193
+ headerOpenType: "H4",
194
+ headerClosedBorder: "",
195
+ headerOpenMargin: "8px",
196
+ headerOpenLayout: "column",
197
+ headerClosedAlignment: "center",
198
+ accordionBorder: "",
199
+ headerOpenExtra: "",
200
+ accordionMargin: "12px",
201
+ headerClosedBoxShadow: "5px 5px 10px 2px rgba(0, 0, 0, 0.5)",
202
+ headerOpenAlignment: "Center",
203
+ headerClosedLayout: "row wrap",
204
+ accordionBackground: "0xffaaff",
205
+ headerClosedMargin: "6px",
206
+ headerOpenPadding: "8px",
207
+ headerClosedPadding: "8px"
208
+ },
209
+ {
210
+ headerOpenExtra: "",
211
+ accordionAlignment: "center",
212
+ accordionBoxShadow: "",
213
+ accordionExtra: "",
214
+ headerOpenLayout: "column",
215
+ accordionLayout: "row wrap",
216
+ headerOpenAlignment: "Center",
217
+ headerClosedBorder: "2px solid gray",
218
+ headerClosedType: "H4",
219
+ accordionName: "Level 1",
220
+ accordionPadding: "4px",
221
+ headerOpenMargin: "1px",
222
+ accordionMargin: "",
223
+ headerClosedPadding: "8px",
224
+ headerOpenBackground: "#d1c4e9",
225
+ accordionBackground: "#b39ddb",
226
+ headerClosedMargin: "2px",
227
+ accordionBorder: "1px solid black",
228
+ headerClosedAlignment: "center",
229
+ headerOpenType: "H4",
230
+ headerClosedBoxShadow: "",
231
+ headerClosedExtra: "",
232
+ headerOpenPadding: "8px",
233
+ headerClosedLayout: "row wrap",
234
+ headerClosedBackground: "#d1c4e9"
235
+ },
236
+ {
237
+ accordionExtra: "",
238
+ headerOpenMargin: "0px",
239
+ headerClosedExtra: "",
240
+ accordionBackground: "",
241
+ headerClosedBorder: "",
242
+ accordionName: "Level 2",
243
+ headerOpenExtra: "",
244
+ headerOpenAlignment: "Center",
245
+ headerClosedPadding: "14px 12px",
246
+ headerClosedBoxShadow: "inset -2px 0px 8px 6px #808080",
247
+ accordionBoxShadow: "",
248
+ headerClosedType: "H4",
249
+ accordionBorder: "",
250
+ headerClosedMargin: "6px",
251
+ headerOpenBoxShadow: "inset -2px 0px 8px 6px #808080",
252
+ headerOpenType: "H4",
253
+ headerOpenPadding: "8px",
254
+ headerClosedBackground: "#e3f2fd",
255
+ accordionMargin: "",
256
+ accordionAlignment: "space-evenly",
257
+ headerOpenLayout: "row wrap",
258
+ headerOpenBackground: "#e3f2fd",
259
+ accordionLayout: "row wrap",
260
+ headerClosedAlignment: "center",
261
+ headerClosedLayout: "row wrap"
262
+ },
263
+ {
264
+ headerClosedLayout: "row wrap",
265
+ accordionName: "Level 3",
266
+ headerOpenPadding: "",
267
+ accordionBackground: "",
268
+ accordionMargin: "0px",
269
+ headerClosedAlignment: "center",
270
+ headerOpenPalette: "Primary",
271
+ headerClosedPadding: "12px 6px",
272
+ headerOpenType: "H4",
273
+ headerOpenAlignment: "Center",
274
+ accordionLayout: "row wrap",
275
+ headerClosedBorder: "",
276
+ headerClosedType: "H4",
277
+ headerOpenBackground: "#b0cad5",
278
+ headerClosedBoxShadow: "",
279
+ headerOpenLayout: "row wrap",
280
+ headerOpenMargin: "8px",
281
+ accordionBoxShadow: "",
282
+ accordionExtra: "",
283
+ accordionAlignment: "space-evenly",
284
+ headerClosedMargin: "8px",
285
+ headerClosedExtra: "",
286
+ headerClosedBackground: "#b0cad5",
287
+ headerOpenExtra: ""
288
+ },
289
+ {
290
+ headerClosedType: "H4",
291
+ headerClosedBorder: "1px solid blue",
292
+ headerOpenAlignment: "Center",
293
+ accordionLayout: "row wrap",
294
+ accordionAlignment: "center",
295
+ headerOpenLayout: "row wrap",
296
+ headerClosedPadding: "14px",
297
+ accordionBackground: "#c8e6c9",
298
+ headerClosedBoxShadow: "3px 3px grey",
299
+ accordionName: "Level 4",
300
+ headerOpenPadding: "14px",
301
+ accordionBoxShadow: "4px yellow",
302
+ headerOpenType: "title",
303
+ headerClosedAlignment: "center",
304
+ headerClosedExtra: "",
305
+ headerOpenMargin: "0px",
306
+ headerClosedMargin: "2px",
307
+ accordionExtra: "",
308
+ headerOpenExtra: "",
309
+ headerClosedBackground: "#dcedc8",
310
+ headerClosedLayout: "row wrap",
311
+ headerOpenBackground: "#b2dfdb"
312
+ },
313
+ {
314
+ headerClosedAlignment: "center",
315
+ headerClosedBorder: "1px solid blue",
316
+ headerClosedType: "label",
317
+ accordionExtra: "",
318
+ headerClosedExtra: "",
319
+ accordionLayout: "column",
320
+ headerOpenAlignment: "Center",
321
+ headerOpenExtra: "",
322
+ accordionBackground: "#e1f5fe",
323
+ headerOpenLayout: "row wrap",
324
+ headerOpenType: "label",
325
+ accordionBoxShadow: "4px yellow",
326
+ headerOpenPadding: "0px",
327
+ headerClosedBoxShadow: "",
328
+ headerClosedPadding: "0px",
329
+ headerClosedBackground: "#b3e5fc",
330
+ accordionName: "Minimal",
331
+ accordionAlignment: "center",
332
+ headerClosedMargin: "0px",
333
+ headerOpenBackground: "#81d4fa",
334
+ headerClosedLayout: "row wrap",
335
+ headerOpenMargin: "0px"
336
+ },
337
+ {
338
+ headerOpenAlignment: "Center",
339
+ headerOpenBackground: "0xffaaff",
340
+ headerClosedBackground: "0xffaaff",
341
+ headerOpenWidth: "100vw",
342
+ headerOpenExtra: "",
343
+ headerOpenType: "H4",
344
+ headerClosedType: "H4",
345
+ headerOpenLayout: "row wrap",
346
+ headerClosedLayout: "row wrap",
347
+ accordionName: "Settings",
348
+ headerClosedWidth: "100vw",
349
+ headerClosedExtra: "",
350
+ accordionExtra: "",
351
+ accordionMargin: "0px",
352
+ accordionBoxShadow: "",
353
+ headerClosedAlignment: "center",
354
+ headerClosedPalette: "Settings",
355
+ headerOpenPalette: "Settings",
356
+ accordionLayout: "row wrap",
357
+ accordionBackground: "0xffaaff",
358
+ headerClosedMargin: "0px",
359
+ accordionWidth: "100vw",
360
+ accordionPalette: "Settings",
361
+ headerClosedBorder: "",
362
+ headerClosedPadding: "6px",
363
+ accordionPadding: "0px",
364
+ accordionAlignment: "center",
365
+ headerOpenMargin: "0px",
366
+ headerClosedBoxShadow: "",
367
+ headerOpenPadding: "0px"
368
+ },
369
+ {
370
+ headerClosedPadding: "8px",
371
+ headerOpenLayout: "row wrap",
372
+ headerClosedBackground: "#b5ffbb",
373
+ headerOpenMargin: "4px",
374
+ headerClosedBorder: "",
375
+ headerClosedExtra: "",
376
+ accordionLayout: "row wrap",
377
+ headerOpenType: "H4",
378
+ accordionBoxShadow: "",
379
+ headerClosedLayout: "row wrap",
380
+ headerClosedType: "H4",
381
+ accordionExtra: "",
382
+ headerOpenExtra: "",
383
+ headerClosedBoxShadow: "inset -2px 0px 7px 6px #aaaaaa99",
384
+ accordionBackground: "0xffaaff",
385
+ headerOpenBorder: "",
386
+ accordionAlignment: "space-evenly",
387
+ accordionName: "Object",
388
+ headerOpenPadding: "8px",
389
+ headerOpenBackground: "#b5ffbb",
390
+ headerClosedMargin: "6px",
391
+ headerOpenAlignment: "Center"
392
+ },
393
+ {
394
+ headerClosedExtra: "",
395
+ accordionLayout: "row wrap",
396
+ headerClosedPadding: "1px",
397
+ headerClosedType: "body",
398
+ accordionExtra: "",
399
+ headerOpenExtra: "",
400
+ headerClosedLayout: "row wrap",
401
+ headerOpenPadding: "1px",
402
+ headerClosedMargin: "0px",
403
+ accordionAlignment: "center",
404
+ headerOpenMargin: "0px",
405
+ headerClosedAlignment: "center",
406
+ headerOpenAlignment: "Center",
407
+ accordionBackground: "0xffaaff",
408
+ headerOpenBackground: "#eceff1",
409
+ headerOpenLayout: "row wrap",
410
+ accordionBoxShadow: "",
411
+ headerClosedBackground: "#b0bec5",
412
+ headerClosedBorder: "",
413
+ headerOpenType: "body",
414
+ accordionName: "Media Details",
415
+ headerClosedBoxShadow: ""
416
+ },
417
+ {
418
+ accordionBackground: "#e1f5fe",
419
+ headerClosedType: "H3",
420
+ headerOpenExtra: "",
421
+ accordionExtra: "",
422
+ headerOpenAlignment: "Center",
423
+ headerClosedLayout: "row wrap",
424
+ headerOpenMargin: "6px",
425
+ headerClosedBackground: "#03a9f4",
426
+ accordionName: "Docs",
427
+ headerOpenBackground: "#b3e5fc",
428
+ headerOpenType: "H3",
429
+ headerOpenLayout: "row wrap",
430
+ headerClosedAlignment: "center",
431
+ headerOpenPadding: "6px",
432
+ headerClosedPadding: "12px",
433
+ headerClosedMargin: "12px",
434
+ accordionLayout: "column",
435
+ headerClosedExtra: "",
436
+ accordionAlignment: "space-evenly"
437
+ },
438
+ {
439
+ headerOpenPadding: "0px",
440
+ accordionMargin: "0px",
441
+ headerOpenType: "label",
442
+ headerOpenBackground: "#94e4ed",
443
+ headerClosedAlignment: "left",
444
+ accordionPadding: "4px",
445
+ headerClosedMargin: "0px",
446
+ headerClosedPadding: "4px",
447
+ accordionExtra: "",
448
+ accordionName: "Create",
449
+ headerClosedExtra: "",
450
+ accordionWidth: "fit-content",
451
+ accordionAlignment: "left",
452
+ headerClosedType: "H5",
453
+ accordionLayout: "row wrap",
454
+ accordionBackground: "#c3fd95",
455
+ headerOpenExtra: "",
456
+ headerClosedBackground: "#c3fd95",
457
+ headerOpenMargin: "0px"
458
+ },
459
+ {
460
+ headerOpenBackground: "#81c784",
461
+ accordionLayout: "row wrap",
462
+ accordionWidth: "auto",
463
+ headerOpenType: "H4",
464
+ headerOpenExtra: "",
465
+ headerClosedType: "H4",
466
+ headerClosedMargin: "",
467
+ headerClosedExtra: "",
468
+ accordionBackground: "#c1e7ad",
469
+ headerClosedBackground: "#c1e7ad",
470
+ headerOpenPadding: "12px",
471
+ accordionExtra: "",
472
+ accordionPadding: "",
473
+ headerClosedBorder: "1px solid gray",
474
+ headerOpenWidth: "auto",
475
+ headerClosedWidth: "auto",
476
+ accordionName: "TeamChat",
477
+ headerClosedPadding: " 12px"
478
+ },
479
+ {
480
+ headerClosedAlignment: "center",
481
+ headerOpenType: "label",
482
+ accordionBackground: "#ffccbc",
483
+ headerOpenBackground: "#ffccbc",
484
+ headerClosedType: "label",
485
+ accordionWidth: "100vw",
486
+ accordionExtra: "",
487
+ headerOpenAlignment: "Center",
488
+ accordionName: "Style",
489
+ headerClosedBackground: "#ffccbc",
490
+ headerClosedExtra: "",
491
+ accordionLayout: "column",
492
+ headerOpenExtra: "",
493
+ accordionAlignment: "space-evenly"
494
+ },
495
+ {
496
+ accordionBackground: "0xffaaff",
497
+ accordionName: "Share",
498
+ headerOpenExtra: "",
499
+ accordionWidth: "100vw",
500
+ headerClosedPalette: "Share",
501
+ accordionLayout: "column",
502
+ headerOpenAlignment: "Center",
503
+ headerOpenBackground: "#b39ddb",
504
+ headerClosedAlignment: "center",
505
+ accordionAlignment: "space-evenly",
506
+ headerClosedType: "label",
507
+ accordionPalette: "Share",
508
+ headerClosedBackground: "0xffaaff",
509
+ accordionExtra: "",
510
+ headerOpenType: "label",
511
+ headerClosedExtra: ""
512
+ },
513
+ {
514
+ headerClosedBoxShadow: "rgba(50, 50, 93, 0.25) 0px 30px 60px -12px inset, rgba(0, 0, 0, 0.3) 0px 18px 36px -18px inset",
515
+ accordionAlignment: "center",
516
+ headerClosedType: "H5",
517
+ accordionBoxShadow: "rgba(50, 50, 93, 0.25) 0px 30px 60px -12px inset, rgba(0, 0, 0, 0.3) 0px 18px 36px -18px inset",
518
+ headerOpenMargin: "0px",
519
+ headerClosedBorder: "",
520
+ accordionExtra: "",
521
+ headerClosedLayout: "row wrap",
522
+ accordionPadding: "0px",
523
+ headerOpenExtra: "",
524
+ headerClosedBackground: "#80cbc4",
525
+ headerClosedMargin: "2px 2px",
526
+ headerClosedPadding: "12px 30px",
527
+ accordionName: "UI",
528
+ headerOpenType: "label",
529
+ accordionBackground: "#b2dfdb",
530
+ accordionLayout: "row wrap",
531
+ headerOpenBackground: "#80cbc4",
532
+ accordionMargin: "0px",
533
+ accordionBorder: "",
534
+ headerOpenLayout: "row wrap",
535
+ headerClosedExtra: "",
536
+ headerOpenPadding: "6px 12px"
537
+ },
538
+ {
539
+ accordionExtra: "",
540
+ headerOpenExtra: "",
541
+ headerOpenBackground: "#bcaaa4",
542
+ accordionBackground: "#d7ccc8",
543
+ headerClosedType: "H4",
544
+ accordionName: "Chat Bots",
545
+ headerClosedExtra: "",
546
+ headerOpenType: "H4",
547
+ headerClosedBackground: "#d7ccc8"
548
+ },
549
+ {
550
+ headerClosedExtra: "",
551
+ headerClosedBackground: "#81d4fa",
552
+ accordionAlignment: "space-evenly",
553
+ accordionExtra: "",
554
+ headerOpenMargin: "0px",
555
+ headerOpenExtra: "",
556
+ headerClosedMargin: "12px",
557
+ accordionPadding: "0px",
558
+ accordionMargin: "0px",
559
+ accordionLayout: "row wrap",
560
+ accordionPalette: "BrandFocus",
561
+ accordionName: "Team",
562
+ accordionBackground: "0xffaaff",
563
+ headerOpenPadding: "0px",
564
+ headerClosedType: "title",
565
+ headerOpenBackground: "0xffaaff",
566
+ headerClosedPadding: "12px",
567
+ headerOpenType: "label"
568
+ },
569
+ {
570
+ headerOpenType: "H3",
571
+ headerOpenBackground: "0xffaaff",
572
+ headerOpenMargin: "10px",
573
+ accordionExtra: "",
574
+ headerClosedPadding: "6px",
575
+ accordionLayout: "row wrap",
576
+ headerClosedBackground: "#ffe0b2",
577
+ accordionPadding: "20px",
578
+ accordionName: "Sheet",
579
+ headerOpenExtra: "",
580
+ headerClosedBorder: "2px groove #F54927",
581
+ accordionBackground: "#ede7f6",
582
+ accordionAlignment: "space-evenly",
583
+ headerClosedType: "H3",
584
+ headerClosedExtra: "",
585
+ headerClosedMargin: "10px"
586
+ },
587
+ {
588
+ headerClosedBackground: "#b2ebf2",
589
+ headerOpenLayout: "row wrap",
590
+ accordionExtra: "",
591
+ headerClosedExtra: "",
592
+ headerClosedLayout: "row wrap",
593
+ headerOpenType: "H4",
594
+ headerOpenBackground: "0xffaaff",
595
+ headerOpenBoxShadow: "",
596
+ accordionLayout: "row wrap",
597
+ accordionBoxShadow: "",
598
+ headerClosedPadding: "12px",
599
+ headerClosedType: "H4",
600
+ headerOpenAlignment: "Left",
601
+ headerClosedAlignment: "center",
602
+ accordionName: "App",
603
+ headerOpenWidth: "",
604
+ headerOpenExtra: "",
605
+ headerClosedMargin: "12px",
606
+ accordionBackground: "#b2dfdb",
607
+ accordionAlignment: "space-evenly",
608
+ headerClosedBoxShadow: "6px 4px 3px gray"
609
+ },
610
+ {
611
+ headerClosedType: "title",
612
+ headerClosedBoxShadow: "inset 0 0 10px rgba(0, 0, 0, 0.5)",
613
+ accordionLayout: "row wrap",
614
+ accordionBackground: "#bbdefb",
615
+ accordionAlignment: "center",
616
+ accordionExtra: "",
617
+ headerOpenExtra: "",
618
+ headerClosedMargin: "4px",
619
+ headerOpenBackground: "#e3f2fd",
620
+ headerOpenType: "title",
621
+ headerClosedExtra: "",
622
+ accordionBoxShadow: "",
623
+ headerClosedPadding: "2px 10px",
624
+ accordionName: "Report",
625
+ accordionPadding: "0px",
626
+ headerClosedBackground: "#90caf9",
627
+ accordionMargin: ""
628
+ }
629
+ ],
630
+ responsiveMarginMin: 80,
631
+ containers: [
632
+ {
633
+ containerBackground: "#eceff1",
634
+ containerLayout: "column",
635
+ containerAlignmentV: "center",
636
+ containerName: "Data Entry",
637
+ containerBorder: "none",
638
+ containerStyle: "border-radius: 9px;",
639
+ containerPadding: "12px 6px",
640
+ containerAlignment: "center",
641
+ containerMargin: "12px 6px",
642
+ containerBoxShadow: "2px 3px 3px gray",
643
+ containerPosition: "relative"
644
+ },
645
+ {
646
+ containerHeight: "80vh",
647
+ containerPadding: "4px",
648
+ containerName: "Tall",
649
+ containerBackground: "",
650
+ containerAlignment: "center",
651
+ containerPosition: "relative",
652
+ containerLineHeight: "150%",
653
+ containerStyle: "border-radius: 12px;",
654
+ containerWidth: "33vw",
655
+ containerBoxShadow: "3px 5px 5px gray",
656
+ containerAlignmentV: "center",
657
+ containerLayout: "column",
658
+ containerBorder: "1px solid gray",
659
+ containerMargin: "6px"
660
+ },
661
+ {
662
+ containerBackground: "0xffaaff",
663
+ containerStyle: "border-radius: 12px;",
664
+ containerAlignmentV: "center",
665
+ containerMargin: "0px",
666
+ containerPosition: "relative",
667
+ containerLayout: "row wrap",
668
+ containerHeight: "33vh",
669
+ containerBorder: "",
670
+ containerName: "Wide",
671
+ containerWidth: "100vw",
672
+ containerBoxShadow: "",
673
+ containerAlignment: "center",
674
+ containerPadding: "4px"
675
+ },
676
+ {
677
+ containerLayout: "row wrap",
678
+ containerPadding: "0px 0px",
679
+ containerOverflowX: "scroll",
680
+ containerBorder: "none",
681
+ containerAlignment: "center",
682
+ containerName: "Full Width",
683
+ containerBoxShadow: "",
684
+ containerWidth: "100vw",
685
+ containerPosition: "relative",
686
+ containerStyle: "",
687
+ // backgroundImage: "",
688
+ containerAlignmentV: "center",
689
+ containerBackground: "#cfdbe7",
690
+ containerBackgroundGradient: "",
691
+ containerOverflowY: "scroll",
692
+ containerMargin: "0px"
693
+ },
694
+ {
695
+ containerPosition: "relative",
696
+ containerBoxShadow: "3px 5px 5px gray",
697
+ containerBackground: "0xffaaff",
698
+ containerMargin: "6px",
699
+ containerPadding: "0px",
700
+ containerAlignmentV: "center",
701
+ containerLayout: "row wrap",
702
+ containerName: "Media",
703
+ containerAlignment: "center",
704
+ containerStyle: "border-radius: 12px;",
705
+ containerWidth: "25vw",
706
+ containerBorder: "none"
707
+ },
708
+ {
709
+ containerStyle: "",
710
+ containerPadding: "4px",
711
+ containerBoxShadow: "",
712
+ containerBackground: "0xffaaff",
713
+ containerLayout: "row wrap",
714
+ containerOverflowX: "hidden",
715
+ containerOverflowY: "auto",
716
+ containerHeight: "100px",
717
+ containerAlignmentV: "center",
718
+ containerPosition: "relative",
719
+ containerMargin: "",
720
+ containerAlignment: "center",
721
+ containerName: "Scroller",
722
+ containerBorder: "none",
723
+ containerWidth: "100vw"
724
+ },
725
+ {
726
+ containerStyle: "",
727
+ containerBackground: "#cfd8dc55",
728
+ containerPadding: "6px",
729
+ containerBoxShadow: "",
730
+ containerName: "Sticky",
731
+ containerAlignmentV: "center",
732
+ containerMargin: "6px",
733
+ containerBorder: "none",
734
+ containerLayout: "row wrap",
735
+ containerPositioning: "top: 0;",
736
+ containerAlignment: "center",
737
+ containerPosition: "sticky",
738
+ containerHeight: "auto"
739
+ },
740
+ {
741
+ containerBackground: "0xffaaff",
742
+ containerBoxShadow: "",
743
+ containerBorder: "",
744
+ containerLayout: "row wrap",
745
+ containerAlignmentV: "center",
746
+ containerName: "1/3 Width",
747
+ containerPadding: "0px",
748
+ containerWidth: "33%",
749
+ containerAlignment: "center",
750
+ containerBackgroundGradient: "",
751
+ containerMargin: "0px",
752
+ containerStyle: ""
753
+ },
754
+ {
755
+ containerLayout: "column",
756
+ containerName: "Messages",
757
+ containerBackground: "#ffb74d",
758
+ containerBorder: "1px solid white",
759
+ containerMargin: "12px 0px",
760
+ containerLineHeight: "150%"
761
+ },
762
+ {
763
+ containerAlignmentV: "center",
764
+ containerName: "Report",
765
+ containerLayout: "column",
766
+ containerOverflowX: "scroll",
767
+ containerHeight: "auto",
768
+ containerBackground: "#f0edf5",
769
+ containerOverflowY: "scroll",
770
+ containerMargin: "",
771
+ containerBorder: "2px solid black"
772
+ },
773
+ {
774
+ containerMargin: "0px",
775
+ containerPadding: "0px",
776
+ containerWidth: "66%",
777
+ containerName: "2/3 Width",
778
+ containerBackground: "0xffaaff"
779
+ },
780
+ {
781
+ containerBackground: "0xffaaff",
782
+ containerPadding: "6px",
783
+ containerMargin: "6px",
784
+ containerLayout: "column",
785
+ containerAlignmentV: "center",
786
+ containerBorder: "2px solid green",
787
+ containerPalette: "BrandFocus",
788
+ containerName: "teamApp",
789
+ containerAlignment: "center"
790
+ },
791
+ {
792
+ containerLayout: "column",
793
+ containerName: "reportHeader",
794
+ containerPalette: "BrandFocus",
795
+ containerMargin: "10px",
796
+ containerPadding: "8px",
797
+ containerBackground: "0xffaaff"
798
+ },
799
+ {
800
+ containerBackground: "#f5f5f5",
801
+ containerName: "Inactive"
802
+ },
803
+ {
804
+ containerName: "Full Screen Media",
805
+ containerBorder: "3px light blue",
806
+ containerLayout: "column",
807
+ containerHeight: "auto",
808
+ containerBackground: "0xffaaff",
809
+ containerWidth: "100vw",
810
+ containerPadding: "20px"
811
+ },
812
+ {
813
+ containerPadding: "15px",
814
+ containerWidth: "50%",
815
+ containerName: "Half Width",
816
+ containerBackground: "0xffaaff"
817
+ }
818
+ ],
819
+ buttonStyles: [
820
+ {
821
+ buttonPadding: "8px 6px",
822
+ buttonMargin: "6px",
823
+ buttonRadius: "10px",
824
+ buttonTypeStyle: "label",
825
+ buttonBorder: "1px light blue solid",
826
+ buttonName: "Default Style",
827
+ buttonBackground: "#283593",
828
+ buttonShadow: " 0 8px 16px 0 rgba(0,0,0,0.2), 0 6px 20px 0 rgba(0,0,0,0.19)",
829
+ buttonColor: "#ffffff"
830
+ },
831
+ {
832
+ buttonBorder: "1px solid gray",
833
+ buttonTypeStyle: "label",
834
+ buttonShadow: " 0 2px 4px 0 rgba(0,0,0,0.2), 0 3px 6px 0 rgba(0,0,0,0.19)",
835
+ buttonMargin: "0px",
836
+ buttonBackground: "#80deea",
837
+ buttonColor: "#000000",
838
+ buttonName: "Item Select",
839
+ buttonPadding: "8px 6px",
840
+ buttonRadius: "0px"
841
+ },
842
+ {
843
+ buttonShadow: " 0 8px 16px 0 rgba(0,0,0,0.2), 0 6px 20px 0 rgba(0,0,0,0.19)",
844
+ buttonColor: "#4527a0",
845
+ buttonPadding: "8px 6px",
846
+ buttonBorder: "none",
847
+ buttonTypeStyle: "label",
848
+ buttonRadius: "8px",
849
+ buttonName: "Create",
850
+ buttonMargin: "6px",
851
+ buttonBackground: "#b8ff82"
852
+ },
853
+ {
854
+ buttonTypeStyle: "label",
855
+ buttonBackground: "#ad1457",
856
+ buttonMargin: "8px",
857
+ buttonRadius: "8px",
858
+ buttonShadow: " 0 8px 16px 0 rgba(0,0,0,0.2), 0 6px 20px 0 rgba(0,0,0,0.19)",
859
+ buttonName: "Delete",
860
+ buttonColor: "#fafafa",
861
+ buttonPadding: "8px 6px",
862
+ buttonBorder: "none"
863
+ },
864
+ {
865
+ buttonRadius: "0px",
866
+ buttonMargin: "8px",
867
+ buttonTypeStyle: "label",
868
+ buttonColor: "#bdbdbd",
869
+ buttonShadow: "none",
870
+ buttonBorder: "none",
871
+ buttonName: "Disabled",
872
+ buttonBackground: "#e0e0e0",
873
+ buttonPadding: "8px 6px"
874
+ },
875
+ {
876
+ buttonName: "Highlight",
877
+ buttonShadow: " 0 8px 16px 0 rgba(0,0,0,0.2), 0 6px 20px 0 rgba(0,0,0,0.19)",
878
+ buttonBackground: "#BBDEFB",
879
+ buttonTypeStyle: "label",
880
+ buttonMargin: "8px",
881
+ buttonColor: "#ff8a65",
882
+ buttonPadding: "8px 6px",
883
+ buttonPalette: "BrandFocus",
884
+ buttonRadius: "3px",
885
+ buttonBorder: "none"
886
+ },
887
+ {
888
+ buttonMargin: "8px",
889
+ buttonColor: "#ffffff",
890
+ buttonShadow: " 0 8px 16px 0 rgba(0,0,0,0.2), 0 6px 20px 0 rgba(0,0,0,0.19)",
891
+ buttonName: "Function",
892
+ buttonPadding: "8px 6px",
893
+ buttonTypeStyle: "label",
894
+ buttonBorder: "none",
895
+ buttonBackground: "#039be5"
896
+ },
897
+ {
898
+ buttonRadius: "12px",
899
+ buttonMargin: "8px",
900
+ buttonColor: "0xffaaff",
901
+ buttonPadding: "8px 12px",
902
+ buttonBackground: "#c8e6c9",
903
+ buttonTypeStyle: "label",
904
+ buttonShadow: " 0 8px 16px 0 rgba(0,0,0,0.2), 0 6px 20px 0 rgba(0,0,0,0.19)",
905
+ buttonBorder: "none",
906
+ buttonName: "Edit"
907
+ },
908
+ {
909
+ buttonPadding: "8px 6px 8px 16px",
910
+ buttonMargin: "8px",
911
+ buttonBackground: "#455a64",
912
+ buttonRadius: "0px 18px 18px 0px",
913
+ buttonColor: "#fff59d",
914
+ buttonName: "Action",
915
+ buttonShadow: " 0 8px 16px 0 rgba(0,0,0,0.2), 0 6px 20px 0 rgba(0,0,0,0.19)",
916
+ buttonTypeStyle: "label"
917
+ },
918
+ {
919
+ buttonColor: "#000000",
920
+ buttonBackground: "#e0f7fa",
921
+ buttonBorder: "1px solid gray",
922
+ buttonShadow: "none",
923
+ buttonName: "cell",
924
+ buttonMargin: "0px",
925
+ buttonPadding: "0px",
926
+ buttonTypeStyle: "label"
927
+ },
928
+ {
929
+ buttonMargin: "6px",
930
+ buttonPadding: "8px 6px",
931
+ buttonColor: "#3949ab",
932
+ buttonName: "Logic",
933
+ buttonTypeStyle: "subtitle",
934
+ buttonBackground: "#bcaaa4",
935
+ buttonBorder: "2px solid black"
936
+ },
937
+ {
938
+ buttonTypeStyle: "Jade",
939
+ buttonName: "Jade",
940
+ buttonMargin: "3px",
941
+ buttonColor: "0xffaaff",
942
+ buttonBackground: "#f3e5f5",
943
+ buttonPadding: "8px 6px"
944
+ },
945
+ {
946
+ buttonMargin: "6px",
947
+ buttonBackground: "#ffecb3",
948
+ buttonRadius: "9px",
949
+ buttonName: "Cancel",
950
+ buttonPadding: "8px 6px",
951
+ buttonTypeStyle: "label",
952
+ buttonColor: "#546e7a"
953
+ }
954
+ ],
955
+ createdBy: "napkinPackage",
956
+ typeStyles: [
957
+ {
958
+ typeFont: "Noto Sans",
959
+ typeName: "H1",
960
+ typeBackground: "0xffaaff",
961
+ typePadding: "6px 4px",
962
+ typeFontColor: "#0e0000",
963
+ typeFontWeight: "700",
964
+ typeTextShadow: "1px 1px 2px gray",
965
+ typeFontSize: "35px"
966
+ },
967
+ {
968
+ typeFontColor: "#000000",
969
+ typeName: "H2",
970
+ typeBackground: "0xffaaff",
971
+ typeTextShadow: "1px 1px 2px gray",
972
+ typeFont: "Noto Sans",
973
+ typeFontWeight: "700",
974
+ typeFontSize: "28px",
975
+ typePadding: "6px 4px"
976
+ },
977
+ {
978
+ typeFontSize: "24px",
979
+ typeTextShadow: "1px 1px 2px gray",
980
+ typeFontColor: "#080000",
981
+ typeBackground: "0xffaaff",
982
+ typeName: "H3",
983
+ typePadding: "",
984
+ typeFont: "Noto Sans",
985
+ typeFontWeight: "600"
986
+ },
987
+ {
988
+ typeBackground: "0xffaaff",
989
+ typePadding: "",
990
+ typeFont: "Noto Sans",
991
+ typeName: "H4",
992
+ typeFontSize: "19px",
993
+ typeFontColor: "0xffaaff",
994
+ typeTextShadow: "1px 1px 2px gray",
995
+ typeFontWeight: "600"
996
+ },
997
+ {
998
+ typeFontAlignment: "center",
999
+ typeBackground: "0xffaaff",
1000
+ typeFontColor: "#070000",
1001
+ typeName: "H5",
1002
+ typeFontSize: "17px",
1003
+ typePadding: "",
1004
+ typeFontWeight: "600",
1005
+ typeFont: "Noto Sans",
1006
+ typeTextShadow: ""
1007
+ },
1008
+ {
1009
+ typeFontSize: "14px",
1010
+ typeLineHeight: "200%",
1011
+ typeBackground: "0xffaaff",
1012
+ typePadding: "",
1013
+ typeFont: "Noto Sans",
1014
+ typeFontColor: "0xffaaff",
1015
+ typeFontWeight: "500",
1016
+ typeTextShadow: "1px 1px 2px gray",
1017
+ typeName: "body",
1018
+ typeMargin: ""
1019
+ },
1020
+ {
1021
+ typeFontWeight: "500",
1022
+ typeName: "label",
1023
+ typeFont: "Noto Sans",
1024
+ typeMargin: "",
1025
+ typeFontColor: "#000000",
1026
+ typeFontAlignment: "center",
1027
+ typePadding: "",
1028
+ typeBackground: "0xffaaff",
1029
+ typeFontSize: "19px",
1030
+ typeTextShadow: "1px 1px 1px #cfd8dc"
1031
+ },
1032
+ {
1033
+ typeTextShadow: "1px 1px 2px gray",
1034
+ typeFont: "Noto Sans",
1035
+ typeBackground: "0xffaaff",
1036
+ typeFontWeight: "600",
1037
+ typeFontColor: "#303f9f",
1038
+ typeFontSize: "17px",
1039
+ typePadding: "4px",
1040
+ typeName: "user",
1041
+ typeMargin: "4px"
1042
+ },
1043
+ {
1044
+ typePadding: "6px 4px",
1045
+ typeTextShadow: "1px 1px 2px gray",
1046
+ typeFontWeight: "900",
1047
+ typeName: "display",
1048
+ typeFont: "Courier",
1049
+ typeFontSize: "14px",
1050
+ typeBackground: "0xffaaff",
1051
+ typeFontColor: "#00796b"
1052
+ },
1053
+ {
1054
+ typePadding: "2px 4px",
1055
+ typeTextShadow: "1px 1px 2px gray",
1056
+ typeFontWeight: "500",
1057
+ typeFontAlignment: "center",
1058
+ typeBackground: "0xffaaff",
1059
+ typeFont: "Noto Serif",
1060
+ typeFontColor: "#000000",
1061
+ typeName: "title",
1062
+ typeFontSize: "23px",
1063
+ typeMargin: "2px 4px"
1064
+ },
1065
+ {
1066
+ typePadding: "4px",
1067
+ typeBackground: "0xffaaff",
1068
+ typeFontColor: "0xffaaff",
1069
+ typeFontSize: "15px",
1070
+ typeTextShadow: "1px 1px 2px gray",
1071
+ typeFont: "Noto Serif",
1072
+ typeName: "subtitle",
1073
+ typeFontWeight: "600"
1074
+ },
1075
+ {
1076
+ typeMargin: "12px",
1077
+ typePalette: "BrandFocus",
1078
+ typeName: "Focus",
1079
+ typeBorder: "2px solid red",
1080
+ typeFontWeight: "600",
1081
+ typeBackground: "",
1082
+ typeFontColor: "",
1083
+ typePadding: "8px",
1084
+ typeFont: "Verdana",
1085
+ typeTextShadow: "1px 1px 2px gray",
1086
+ typeFontSize: "27px"
1087
+ },
1088
+ {
1089
+ typeBackground: "0xffaaff",
1090
+ typeFontColor: "#ffffff",
1091
+ typeName: "white",
1092
+ typeTextShadow: "1px 1px 2px gray",
1093
+ typeFontSize: "35px",
1094
+ typeFont: "Noto Sans",
1095
+ typePadding: "6px 4px",
1096
+ typeFontWeight: "700"
1097
+ },
1098
+ {
1099
+ typeMargin: "4px",
1100
+ typeFont: "courier",
1101
+ typeFontWeight: "600",
1102
+ typeLineHeight: "1.5",
1103
+ typeFontColor: "#1259c8",
1104
+ typePadding: "4px",
1105
+ typeBackground: "0xffaaff",
1106
+ typeName: "Jade",
1107
+ typeFontSize: "19px"
1108
+ }
1109
+ ],
1110
+ createdAt: 1720378730412,
1111
+ classes: [
1112
+ {
1113
+ class: "color: blue;",
1114
+ className: "color-blue"
1115
+ },
1116
+ {
1117
+ class: "justify-content: flex-end;\njustify-self: flex-end;",
1118
+ className: "justifyRight"
1119
+ }
1120
+ ],
1121
+ editedAt: 1720378730412
1122
+ };
1123
+ this.listenToWindowSize();
1124
+ }
1125
+ /*
1126
+ private analytics: Analytics | null = null;
1127
+
1128
+ private async getAnalytics(): Promise<Analytics | null> {
1129
+ if (!this.analytics) {
1130
+ this.analytics = await this.analyticsPromise;
1131
+ }
1132
+ return this.analytics;
1133
+ }
1134
+ */
1135
+ listenToWindowSize() {
1136
+ this.ngZone.runOutsideAngular(() => {
1137
+ fromEvent(window, 'resize').pipe(debounceTime(50)).subscribe(() => {
1138
+ this.screenWidth = window.innerWidth;
1139
+ this.screenHeight = window.innerHeight;
1140
+ this.ngZone.run(() => this.resize$.next());
1141
+ });
1142
+ });
1143
+ }
1144
+ /**
1145
+ * Fetches themes for the tenant identified by `apiKey` via the Napkin themes
1146
+ * API and writes them into `this.allThemes`. Fetch-once; no real-time updates.
1147
+ *
1148
+ * Delegates to `ThemeService`. The base URL is set by the
1149
+ * `THEME_API_BASE_URL` constant in theme.service.ts.
1150
+ *
1151
+ * If `themeId` is supplied, only that single theme is fetched and the result
1152
+ * replaces `allThemes` with a one-element array. Otherwise all themes for the
1153
+ * tenant are fetched.
1154
+ *
1155
+ * On any failure (missing key, network error, non-2xx, `success: false` from
1156
+ * the backend) the call logs and returns `[]` — `getTheme()` continues to
1157
+ * fall back to `defaultStyle`.
1158
+ *
1159
+ * @param apiKey tenant API key (required) — sent as Bearer token
1160
+ * @param themeId optional theme id; when present fetches only that one theme
1161
+ * @returns the loaded themes (the same array that was assigned to `allThemes`)
1162
+ */
1163
+ async initThemes(apiKey, themeId) {
1164
+ if (!apiKey) {
1165
+ console.warn('FasterThemesService.initThemes: apiKey is required.');
1166
+ return [];
1167
+ }
1168
+ try {
1169
+ if (themeId) {
1170
+ const resp = await this.themeService.getTheme(themeId, apiKey);
1171
+ if (resp.success && resp.data) {
1172
+ this.allThemes = [this.toFasterTheme(resp.data)];
1173
+ return this.allThemes;
1174
+ }
1175
+ console.warn('FasterThemesService.initThemes: themeService.getTheme returned no data.', resp.error);
1176
+ return [];
1177
+ }
1178
+ const resp = await this.themeService.getThemes(apiKey);
1179
+ if (resp.success && resp.data?.length) {
1180
+ this.allThemes = resp.data.map((t) => this.toFasterTheme(t));
1181
+ return this.allThemes;
1182
+ }
1183
+ console.warn('FasterThemesService.initThemes: themeService.getThemes returned no data.', resp.error);
1184
+ return [];
1185
+ }
1186
+ catch (err) {
1187
+ console.error('FasterThemesService.initThemes failed; falling back to defaultStyle.', err);
1188
+ return [];
1189
+ }
1190
+ }
1191
+ /** Map the API's wrapped Theme document to a flat FasterTheme. */
1192
+ toFasterTheme(t) {
1193
+ return {
1194
+ ...t.data,
1195
+ id: t.objectId,
1196
+ companyId: t.companyId,
1197
+ };
1198
+ }
1199
+ getTheme(themeName) {
1200
+ var self = this;
1201
+ var theme;
1202
+ // Check my themes first!!!
1203
+ if (themeName !== null && typeof (themeName) == 'object') {
1204
+ self.parentTheme = themeName;
1205
+ return themeName;
1206
+ }
1207
+ if (themeName && self.allThemes?.length) {
1208
+ theme = self.allThemes.find(p => p.name?.toLowerCase() == themeName.toLowerCase() || p.id == themeName);
1209
+ }
1210
+ if (theme) {
1211
+ self.parentTheme = theme;
1212
+ return theme;
1213
+ }
1214
+ if (self.parentTheme?.containers?.length)
1215
+ return self.parentTheme;
1216
+ return self.defaultStyle;
1217
+ }
1218
+ getPalette(themeName, paletteName) {
1219
+ var self = this;
1220
+ const theme = self.getTheme(themeName);
1221
+ if (!theme.palette || !paletteName?.length)
1222
+ return null;
1223
+ return theme.palette.find(p => p.colorName.toLowerCase() == paletteName.toLowerCase());
1224
+ }
1225
+ getContainer(themeName, container) {
1226
+ var self = this;
1227
+ const theme = self.getTheme(themeName);
1228
+ if (!theme.containers)
1229
+ return null;
1230
+ return theme.containers.find(p => p.containerName?.toLowerCase() == container.toLowerCase());
1231
+ }
1232
+ getButton(themeName, button) {
1233
+ var self = this;
1234
+ const theme = self.getTheme(themeName);
1235
+ if (theme?.buttonStyles) {
1236
+ const style = theme?.buttonStyles?.find(p => p.buttonName?.toLowerCase() == button?.toLowerCase());
1237
+ if (!style) {
1238
+ return theme.buttonStyles[0];
1239
+ }
1240
+ else {
1241
+ return style;
1242
+ }
1243
+ }
1244
+ if (self.defaultStyle?.buttonStyles && self.defaultStyle?.buttonStyles[0])
1245
+ return self.defaultStyle.buttonStyles[0];
1246
+ return null; // Should never happen
1247
+ }
1248
+ class(themeName, className) {
1249
+ var self = this;
1250
+ const theme = self.getTheme(themeName);
1251
+ if (!theme.classes)
1252
+ return "";
1253
+ const style = theme.classes.find(p => p.className?.toLowerCase() == className?.toLowerCase());
1254
+ if (style?.class)
1255
+ return style.class;
1256
+ else
1257
+ return "";
1258
+ }
1259
+ typeObject(themeName, typeName) {
1260
+ var str = "";
1261
+ var self = this;
1262
+ const theme = self.getTheme(themeName);
1263
+ if (!theme.typeStyles)
1264
+ return;
1265
+ var type;
1266
+ if (!typeName)
1267
+ type = theme.typeStyles[0]; // for now
1268
+ else {
1269
+ type = theme.typeStyles.find((p) => p.typeName.toLowerCase() == typeName.toLowerCase());
1270
+ if (!type)
1271
+ type = theme.typeStyles[0]; // for now
1272
+ }
1273
+ return type;
1274
+ }
1275
+ type(themeName, typeName) {
1276
+ var str = "";
1277
+ var self = this;
1278
+ const theme = self.getTheme(themeName);
1279
+ if (!theme.typeStyles)
1280
+ return str;
1281
+ var type;
1282
+ // synonyms
1283
+ if (typeName == "headline1")
1284
+ typeName = "H1";
1285
+ if (typeName == "headline2")
1286
+ typeName = "H2";
1287
+ if (typeName == "headline3")
1288
+ typeName = "H3";
1289
+ if (typeName == "headline4")
1290
+ typeName = "H4";
1291
+ if (typeName == "headline5")
1292
+ typeName = "H5";
1293
+ if (!typeName)
1294
+ type = theme.typeStyles[0]; // for now
1295
+ else {
1296
+ type = theme.typeStyles.find((p) => p.typeName.toLowerCase() == typeName.toLowerCase());
1297
+ if (!type)
1298
+ type = theme.typeStyles[0]; // for now
1299
+ }
1300
+ // Pallete takes precendence over local colors
1301
+ var colors;
1302
+ if (type.typePalette) {
1303
+ colors = this.setPalette(theme, type.typePalette);
1304
+ str += colors;
1305
+ }
1306
+ else if (!colors) {
1307
+ if (type.typeBackground)
1308
+ str += "background-color: " + type.typeBackground + ";";
1309
+ if (type?.typeFontColor)
1310
+ str += "color: " + type.typeFontColor + ";";
1311
+ }
1312
+ if (type.typeFont)
1313
+ str += "font-family: " + type.typeFont + ";";
1314
+ if (type.typeFontSize) {
1315
+ str += "font-size: " + self.responsiveType(theme, type) + "px;";
1316
+ }
1317
+ if (type.typeFontWeight)
1318
+ str += "font-weight: " + type.typeFontWeight + ";";
1319
+ if (type.typeFontAlignment)
1320
+ str += "text-align: " + type.typeFontAlignment + ";";
1321
+ if (type.typeTextShadow)
1322
+ str += "text-shadow: " + type.typeTextShadow + ";";
1323
+ if (type.typePadding)
1324
+ str += "padding: " + self.responsiveSpace(theme, type.typePadding, theme.responsivePaddingMin, theme.responsivePaddingMax) + ";";
1325
+ if (type.typeMargin)
1326
+ str += "margin: " + self.responsiveSpace(theme, type.typeMargin, theme.responsiveMarginMin, theme.responsiveMarginMax) + ';';
1327
+ if (type.typeLineHeight)
1328
+ str += "line-height: " + type.typeLineHeight + ";";
1329
+ // console.log ("Type ", type, typeName, str)
1330
+ return str;
1331
+ }
1332
+ setPalette(theme, paletteName) {
1333
+ var str = "";
1334
+ var palette = this.getPalette(theme, paletteName);
1335
+ if (palette?.backgroundColor)
1336
+ str += "background-color: " + palette.backgroundColor + ";";
1337
+ if (palette?.color)
1338
+ str += "color: " + palette.color + ";";
1339
+ if (palette?.gradient)
1340
+ str += palette.gradient + ';';
1341
+ str = str.replace(";;", ";");
1342
+ return str;
1343
+ }
1344
+ responsiveType(theme, type) {
1345
+ var self = this;
1346
+ // Let's first apply auto-sizing if enabled
1347
+ var str = type.typeFontSize;
1348
+ var numbers = str?.match(/\d+/g)?.map(Number);
1349
+ if (!numbers)
1350
+ numbers = [14];
1351
+ var size = Number(numbers[0]);
1352
+ var factor = self.typeFactor(theme, size);
1353
+ size = size * factor;
1354
+ // console.log("Font Size: ", size, factor);
1355
+ return size;
1356
+ }
1357
+ responsiveSpace(theme, margin, min, max) {
1358
+ var self = this;
1359
+ var str = margin;
1360
+ var marginArray = margin.split(' ');
1361
+ var standardWidth = 1280;
1362
+ var screenWidth = 1280;
1363
+ var result = "";
1364
+ // No range no update
1365
+ if (typeof (min) != 'number') {
1366
+ // console.log ("Not a number ", min)
1367
+ min = Number(min);
1368
+ // return margin
1369
+ }
1370
+ if (typeof (max) != 'number') {
1371
+ // console.log ("Not a number ", max)
1372
+ max = Number(max);
1373
+ // return margin
1374
+ }
1375
+ screenWidth = this.screenWidth;
1376
+ if (theme?.responsiveStandardWidth)
1377
+ standardWidth = theme.responsiveStandardWidth;
1378
+ marginArray.forEach(function (item) {
1379
+ var numbers = item.match(/\d+/g);
1380
+ var number;
1381
+ if (numbers?.length)
1382
+ number = Number(numbers[0]);
1383
+ var operator = item.replace(/\d/g, '');
1384
+ // console.log("Number/operator: ", number, operator)
1385
+ if (typeof (number) != 'number')
1386
+ result += operator + " ";
1387
+ else {
1388
+ var size = number;
1389
+ if (typeof (min) != 'number')
1390
+ min = 10;
1391
+ if (typeof (max) != 'number')
1392
+ max = 200;
1393
+ var factor = mapRange(screenWidth, 0, standardWidth, min / 100, max / 100);
1394
+ size = size * factor;
1395
+ size = roundToOneDecimal(size);
1396
+ // console.log("Factor: ", factor, );
1397
+ result += size.toString() + operator + " ";
1398
+ }
1399
+ });
1400
+ // if (max==200)
1401
+ // console.log("result: ", margin, result, min, max );
1402
+ return result;
1403
+ }
1404
+ typeFactor(theme, size) {
1405
+ var factor = (this.screenWidth);
1406
+ var min = 0.7;
1407
+ var max = 1.2;
1408
+ var standardWidth = 1980;
1409
+ if (theme?.responsiveStandardWidth)
1410
+ standardWidth = theme.responsiveStandardWidth;
1411
+ if (theme?.responsiveFontSizeMin)
1412
+ min = theme.responsiveFontSizeMin / 100;
1413
+ if (theme?.responsiveFontSizeMax)
1414
+ max = theme.responsiveFontSizeMax / 100;
1415
+ // console.log("Type; ", standardWidth, min, max)
1416
+ factor = mapRange(factor, 0, standardWidth, min, max);
1417
+ return Number(factor.toFixed(1));
1418
+ }
1419
+ container(themeName, containerName) {
1420
+ var str = "";
1421
+ var self = this;
1422
+ const theme = self.getTheme(themeName);
1423
+ // console.log ("THEME: ", theme)
1424
+ var container;
1425
+ if (containerName && theme.containers) {
1426
+ //@ts-ignore
1427
+ container = theme.containers.find((p) => p.containerName.toLowerCase() == containerName.toLowerCase());
1428
+ //if (!container && theme?.containers) container = theme.containers[0]
1429
+ // console.log ("Container ", container,containerName);
1430
+ }
1431
+ if (theme.containers && !container)
1432
+ container = theme.containers[0];
1433
+ if (!container)
1434
+ return str;
1435
+ // Palette takes precendence over local colors
1436
+ var colors;
1437
+ if (container.containerPalette) {
1438
+ colors = this.setPalette(theme, container.containerPalette);
1439
+ str += colors;
1440
+ }
1441
+ else if (!colors) {
1442
+ if (container?.containerBackgroundGradient) {
1443
+ str += "background-image: " + container.containerBackgroundGradient;
1444
+ }
1445
+ else if (container.containerBackground)
1446
+ str += "background-color: " + container.containerBackground + ";";
1447
+ else
1448
+ str += "background-color: rgba(255,255,255,0)";
1449
+ }
1450
+ if (container?.backgroundImage?.length) {
1451
+ str += "background-image: url(" + container.backgroundImage + ");";
1452
+ if (container.backgroundRepeat)
1453
+ str += "background-repeat: " + container.backgroundRepeat + ';';
1454
+ if (container.backgroundSize)
1455
+ str += "background-size: " + container.backgroundSize + ';';
1456
+ if (container.backgroundPosition)
1457
+ str += "background-position: " + container.backgroundPosition + ';';
1458
+ }
1459
+ if (container.containerBorder)
1460
+ str += "border: " + container.containerBorder + ";";
1461
+ if (container.containerBoxShadow)
1462
+ str += "box-shadow: " + container.containerBoxShadow + ";";
1463
+ if (container.containerMargin)
1464
+ str += "margin: " + self.responsiveSpace(theme, container.containerMargin, theme.responsiveMarginMin, theme.responsiveMarginMax) + ';';
1465
+ if (container.containerPadding)
1466
+ str += "padding: " + self.responsiveSpace(theme, container.containerPadding, theme.responsivePaddingMin, theme.responsivePaddingMax) + ";";
1467
+ if (container.containerAlignment && !container.containerAlignmentV) {
1468
+ str += "display: flex; justify-content: " + container.containerAlignment + ";";
1469
+ str += "align-items: " + container.containerAlignment + ";";
1470
+ }
1471
+ else if (container.containerAlignment && container.containerAlignmentV) {
1472
+ str += "display: flex; justify-content: " + container.containerAlignment + ";";
1473
+ str += "align-items: " + container.containerAlignmentV + ";";
1474
+ }
1475
+ if (container.containerLayout) {
1476
+ str += "flex-flow: " + container.containerLayout + ";";
1477
+ }
1478
+ if (container.containerWidth) {
1479
+ if (container.containerWidth == '100vw') {
1480
+ str += "width: " + this.screenWidth + 'px;';
1481
+ // console.log ("container style ", str)
1482
+ }
1483
+ else
1484
+ str += "width: " + container.containerWidth + ';';
1485
+ }
1486
+ if (container.containerHeight) {
1487
+ str += "height: " + container.containerHeight + ';';
1488
+ }
1489
+ if (container.containerLineHeight) {
1490
+ str += "line-height: " + container.containerLineHeight + ';';
1491
+ }
1492
+ if (container.containerOverflowX) {
1493
+ str += "overflow-x: " + container.containerOverflowX + ';';
1494
+ }
1495
+ if (container.containerOverflowY) {
1496
+ str += "overflow-y: " + container.containerOverflowY + ';';
1497
+ }
1498
+ if (container.containerPosition) {
1499
+ str += "position: " + container.containerPosition + ';';
1500
+ }
1501
+ /* Add left,right,top,bottom */
1502
+ if (container.containerPositioning) {
1503
+ str += container.containerPositioning;
1504
+ }
1505
+ if (container.containerStyle) {
1506
+ str += container.containerStyle;
1507
+ }
1508
+ return str;
1509
+ }
1510
+ /* not used for now
1511
+ card(themeName?: any, cardName?: any) {
1512
+ var str = "";
1513
+ var self = this;
1514
+
1515
+ const theme: any = self.getTheme(themeName)
1516
+
1517
+
1518
+ if (cardName && theme.cards) {
1519
+ const card = theme.cards.find((p: { cardName: any; }) => p.cardName == cardName)
1520
+
1521
+ if (!card) return str;
1522
+ if (card.cardBackground)
1523
+ str = "background-color: " + card.cardBackground + ";"
1524
+
1525
+ if (card.cardBorder)
1526
+ str += "border: " + card.cardBorder + ";"
1527
+
1528
+ if (card.cardBoxShadow)
1529
+ str += "box-shadow: " + card.cardBoxShadow + ";"
1530
+
1531
+ if (card.cardMargin)
1532
+ str += "margin: " + card.cardMargin + ";"
1533
+
1534
+ if (card.cardPadding)
1535
+ str += "padding: " + card.cardPadding + ";"
1536
+
1537
+ if (card.cardAlignment) {
1538
+ str += "display: flex; justify-content: " + card.cardAlignment + ";"
1539
+ str += "align-items: " + card.cardAlignment + ";"
1540
+ }
1541
+ if (card.cardLayout) {
1542
+ str += "flex-flow: " + card.cardLayout + ";"
1543
+ }
1544
+ return str;
1545
+ }
1546
+ else {
1547
+ if (theme.cardBackground)
1548
+ str = "background-color: " + theme.cardBackground + ";"
1549
+
1550
+ if (theme.cardBorder)
1551
+ str += "border: " + theme.cardBorder + ";"
1552
+
1553
+ if (theme.cardBoxShadow)
1554
+ str += "box-shadow: " + theme.cardBoxShadow + ";"
1555
+
1556
+ if (theme.cardMargin)
1557
+ str += "margin: " + theme.cardMargin + ";"
1558
+
1559
+ if (theme.cardPadding)
1560
+ str += "padding: " + theme.cardPadding + ";"
1561
+
1562
+ if (theme.cardAlignment) {
1563
+ str += "display: flex; justify-content: " + theme.cardAlignment + ";"
1564
+ str += "align-items: " + theme.cardAlignment + ";"
1565
+ }
1566
+ if (theme.cardLayout) {
1567
+ str += "flex-flow: " + theme.cardLayout + ";"
1568
+ }
1569
+ }
1570
+
1571
+ return str;
1572
+ }
1573
+ */
1574
+ accordionList(themeName) {
1575
+ var self = this;
1576
+ var list = [];
1577
+ const theme = self.getTheme(themeName);
1578
+ if (!theme)
1579
+ return list; // Should ALWAYS get theme
1580
+ if (theme.accordions?.length)
1581
+ theme.accordions.forEach(function (item) {
1582
+ //????? list.push(item.accordionName);
1583
+ });
1584
+ return list;
1585
+ }
1586
+ accordion(themeName, accordionName) {
1587
+ var str = "";
1588
+ var self = this;
1589
+ const theme = self.getTheme(themeName);
1590
+ // console.log ("New Accordion Theme ", theme, accordionName)
1591
+ var acc;
1592
+ if (accordionName && theme.accordions)
1593
+ acc = theme.accordions.find((p) => p.accordionName?.length && p.accordionName.toLowerCase() == accordionName.toLowerCase());
1594
+ if (theme.accordions && !acc)
1595
+ acc = theme.accordions[0]; //default
1596
+ if (acc) {
1597
+ // Palette takes precendence over local colors
1598
+ var colors;
1599
+ if (acc.accordionPalette) {
1600
+ colors = this.setPalette(theme, acc.accordionPalette);
1601
+ str += colors;
1602
+ }
1603
+ else if (!colors) {
1604
+ if (acc.accordionBackground)
1605
+ str += "background-color: " + acc.accordionBackground + ";";
1606
+ }
1607
+ if (acc.accordionBorder)
1608
+ str += "border: " + acc.accordionBorder + ";";
1609
+ if (acc.accordionBoxShadow)
1610
+ str += "box-shadow: " + acc.accordionBoxShadow + ";";
1611
+ if (acc.accordionWidth)
1612
+ str += "width: " + acc.accordionWidth + ";";
1613
+ if (theme.responsiveMarginMin) {
1614
+ if (acc.accordionMargin)
1615
+ str += "margin: " + self.responsiveSpace(theme, acc.accordionMargin, theme.responsiveMarginMin, theme.responsiveMarginMax) + ";";
1616
+ }
1617
+ if (theme.responsivePaddingMin) {
1618
+ if (acc.accordionPadding)
1619
+ str += "padding: " + self.responsiveSpace(theme, acc.accordionPadding, theme.responsivePaddingMin, theme.responsivePaddingMax) + ";";
1620
+ }
1621
+ if (acc.accordionAlignment) {
1622
+ str += "display: flex; justify-content: " + acc.accordionAlignment + ";";
1623
+ str += "align-items: " + acc.accordionAlignment + ";";
1624
+ }
1625
+ if (acc.accordionLayout) {
1626
+ str += "flex-flow: " + acc.accordionLayout + ";";
1627
+ }
1628
+ if (acc.accordionExtra)
1629
+ str += acc.accordionExtra;
1630
+ }
1631
+ return str;
1632
+ }
1633
+ header(themeName, accordionName, expanded, index) {
1634
+ var str = "cursor: pointer;";
1635
+ var self = this;
1636
+ // console.log ("Header: ", themeName, accordionName, expanded, index)
1637
+ const theme = self.getTheme(themeName);
1638
+ if (!theme) {
1639
+ console.log("WE CANNOT FIND YOUR THEME: ", themeName);
1640
+ return str;
1641
+ }
1642
+ if (accordionName && theme.accordions) {
1643
+ var acc = theme.accordions.find((p) => p.accordionName?.length && p.accordionName.toLowerCase() == accordionName.toLowerCase());
1644
+ // console.log ("Your Accordion: ", acc, theme, themeName, accordionName)
1645
+ }
1646
+ if (theme.accordions && !acc)
1647
+ acc = theme.accordions[0]; //default
1648
+ if (acc) {
1649
+ if (expanded) {
1650
+ if (acc.headerOpenWidth == '100vw') {
1651
+ str += "width: " + self.screenWidth + 'px;';
1652
+ }
1653
+ else
1654
+ str += "width: " + acc.headerOpenWidth + ';';
1655
+ if (acc.headerOpenType)
1656
+ str += self.type(themeName, acc.headerOpenType);
1657
+ // Palette takes precendence over local colors
1658
+ var colors;
1659
+ if (acc.headerOpenPalette) {
1660
+ colors = this.setPalette(theme, acc.headerOpenPalette);
1661
+ str += colors;
1662
+ }
1663
+ else if (!colors) {
1664
+ if (acc.headerOpenBackground)
1665
+ str += "background-color: " + acc.headerOpenBackground + ";";
1666
+ }
1667
+ if (acc.headerOpenBorder)
1668
+ str += "border: " + acc.headerOpenBorder + ";";
1669
+ if (acc.headerOpenBoxShadow)
1670
+ str += "box-shadow: " + acc.headerOpenBoxShadow + ";";
1671
+ if (acc.headerOpenMargin)
1672
+ str += "margin: " + self.responsiveSpace(theme, acc.headerOpenMargin, theme.responsiveMarginMin, theme.responsiveMarginMax) + ";";
1673
+ if (acc.headerOpenPadding)
1674
+ str += "padding: " + self.responsiveSpace(theme, acc.headerOpenPadding, theme.responsivePaddingMin, theme.responsivePaddingMax) + ";";
1675
+ if (acc.headerOpenAlignment) {
1676
+ str += "display: flex; justify-content: " + acc.headerOpenAlignment + ";";
1677
+ str += "align-items: " + acc.headerOpenAlignment + ";";
1678
+ }
1679
+ if (acc.headerOpenLayout) {
1680
+ str += "flex-flow: " + acc.headerOpenLayout + ";";
1681
+ }
1682
+ if (acc.headerOpenExtra)
1683
+ str += acc.headerOpenExtra;
1684
+ }
1685
+ else {
1686
+ if (acc.headerClosedWidth == '100vw') {
1687
+ str += "width: " + self.screenWidth + 'px;';
1688
+ }
1689
+ else
1690
+ str += "width: " + acc.headerClosedWidth + ';';
1691
+ if (acc.headerClosedType)
1692
+ str += self.type(themeName, acc.headerClosedType);
1693
+ // Palette takes precendence over local colors
1694
+ var colors;
1695
+ if (acc.headerClosedPalette) {
1696
+ colors = this.setPalette(theme, acc.headerClosedPalette);
1697
+ str += colors;
1698
+ }
1699
+ else if (!colors) {
1700
+ if (acc.headerClosedBackground)
1701
+ str += "background-color: " + acc.headerClosedBackground + ";";
1702
+ }
1703
+ if (acc.headerClosedBorder)
1704
+ str += "border: " + acc.headerClosedBorder + ";";
1705
+ if (acc.headerClosedBoxShadow)
1706
+ str += "box-shadow: " + acc.headerClosedBoxShadow + ";";
1707
+ if (acc.headerClosedMargin)
1708
+ str += "margin: " + self.responsiveSpace(theme, acc.headerClosedMargin, theme.responsiveMarginMin, theme.responsiveMarginMax) + ";";
1709
+ if (acc.headerClosedPadding)
1710
+ str += "padding: " + self.responsiveSpace(theme, acc.headerClosedPadding, theme.responsivePaddingMin, theme.responsivePaddingMax) + ";";
1711
+ if (acc.headerClosedAlignment) {
1712
+ str += "display: flex; justify-content: " + acc.headerClosedAlignment + ";";
1713
+ str += "align-items: " + acc.headerClosedAlignment + ";";
1714
+ }
1715
+ if (acc.headerClosedLayout) {
1716
+ str += "flex-flow: " + acc.headerClosedLayout + ";";
1717
+ }
1718
+ if (acc.headerClosedExtra)
1719
+ str += acc.headerClosedExtra;
1720
+ }
1721
+ }
1722
+ else {
1723
+ // DEFAULT ACCORDION STYLING?? Maybe not.
1724
+ console.log("No accordions!");
1725
+ }
1726
+ return str;
1727
+ }
1728
+ button(themeName, buttonName) {
1729
+ var str = "";
1730
+ var self = this;
1731
+ const theme = self.getTheme(themeName);
1732
+ const buttonStyle = self.getButton(theme, buttonName);
1733
+ if (!buttonStyle)
1734
+ return str;
1735
+ const type = self.typeObject(theme, buttonStyle.buttonTypeStyle) || {};
1736
+ // Pallete takes precendence over local colors
1737
+ var colors;
1738
+ if (buttonStyle.buttonPalette) {
1739
+ colors = self.setPalette(theme, buttonStyle.buttonPalette);
1740
+ str += colors;
1741
+ }
1742
+ if (!colors) {
1743
+ if (buttonStyle?.buttonBackground)
1744
+ str += "background-color: " + buttonStyle.buttonBackground + ";";
1745
+ if (buttonStyle?.buttonColor) {
1746
+ str += "color: " + buttonStyle.buttonColor + ";";
1747
+ }
1748
+ }
1749
+ if (buttonStyle?.buttonPadding)
1750
+ str += "padding: " + self.responsiveSpace(theme, buttonStyle.buttonPadding, theme.responsivePaddingMin, theme.responsivePaddingMax) + ";";
1751
+ if (buttonStyle?.buttonMargin)
1752
+ str += "margin: " + self.responsiveSpace(theme, buttonStyle.buttonMargin, theme.responsiveMarginMin, theme.responsiveMarginMax) + ";";
1753
+ if (buttonStyle?.buttonBorder) {
1754
+ str += "border: " + buttonStyle.buttonBorder + ";";
1755
+ }
1756
+ else
1757
+ str += "border: none;";
1758
+ if (buttonStyle?.buttonRadius)
1759
+ str += "border-radius: " + buttonStyle.buttonRadius + ";";
1760
+ if (buttonStyle?.buttonShadow)
1761
+ str += "box-shadow: " + buttonStyle.buttonShadow + ";";
1762
+ if (type.typeFontSize) {
1763
+ str += "font-size: " + self.responsiveType(theme, type) + "px;";
1764
+ }
1765
+ if (type.typeFont)
1766
+ str += "font-family: " + type.typeFont + ";";
1767
+ if (type.typeFontWeight)
1768
+ str += "font-weight: " + type.typeFontWeight + ";";
1769
+ str += "cursor: pointer; box-sizing: border-box;";
1770
+ return str;
1771
+ }
1772
+ paddingAdjust(theme, delta) {
1773
+ this.paddingAdjustment = delta;
1774
+ }
1775
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: FasterThemesService, deps: [{ token: i0.NgZone }, { token: ThemeService }], target: i0.ɵɵFactoryTarget.Injectable }); }
1776
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: FasterThemesService, providedIn: 'root' }); }
1777
+ }
1778
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: FasterThemesService, decorators: [{
1779
+ type: Injectable,
1780
+ args: [{
1781
+ providedIn: 'root'
1782
+ }]
1783
+ }], ctorParameters: () => [{ type: i0.NgZone }, { type: ThemeService }] });
1784
+ function mapRange(value, inMin, inMax, outMin, outMax) {
1785
+ return (value - inMin) * (outMax - outMin) / (inMax - inMin) + outMin;
1786
+ }
1787
+ function roundToOneDecimal(num) {
1788
+ return Math.round(num * 10) / 10;
1789
+ }
1790
+
1791
+ class AccordionContent {
1792
+ constructor(templateRef) {
1793
+ this.templateRef = templateRef;
1794
+ }
1795
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: AccordionContent, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive }); }
1796
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.15", type: AccordionContent, isStandalone: false, selector: "[accordionContent]", ngImport: i0 }); }
1797
+ }
1798
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: AccordionContent, decorators: [{
1799
+ type: Directive,
1800
+ args: [{
1801
+ selector: "[accordionContent]",
1802
+ standalone: false
1803
+ }]
1804
+ }], ctorParameters: () => [{ type: i0.TemplateRef }] });
1805
+
1806
+ class AccordionHeader {
1807
+ constructor(templateRef) {
1808
+ this.templateRef = templateRef;
1809
+ }
1810
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: AccordionHeader, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive }); }
1811
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.15", type: AccordionHeader, isStandalone: false, selector: "[accordionHeader]", ngImport: i0 }); }
1812
+ }
1813
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: AccordionHeader, decorators: [{
1814
+ type: Directive,
1815
+ args: [{
1816
+ selector: "[accordionHeader]",
1817
+ standalone: false
1818
+ }]
1819
+ }], ctorParameters: () => [{ type: i0.TemplateRef }] });
1820
+
1821
+ class AccordionTitle {
1822
+ constructor(templateRef) {
1823
+ this.templateRef = templateRef;
1824
+ }
1825
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: AccordionTitle, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive }); }
1826
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.15", type: AccordionTitle, isStandalone: false, selector: "[accordionTitle]", ngImport: i0 }); }
1827
+ }
1828
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: AccordionTitle, decorators: [{
1829
+ type: Directive,
1830
+ args: [{
1831
+ selector: "[accordionTitle]",
1832
+ standalone: false
1833
+ }]
1834
+ }], ctorParameters: () => [{ type: i0.TemplateRef }] });
1835
+
1836
+ class AccordionItem {
1837
+ constructor() {
1838
+ this.title = "";
1839
+ this.disabled = false;
1840
+ this.expanded = false;
1841
+ this.loaded = false;
1842
+ }
1843
+ collapse() {
1844
+ this.expanded = false;
1845
+ }
1846
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: AccordionItem, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1847
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.15", type: AccordionItem, isStandalone: false, selector: "accordion-item", inputs: { title: "title", disabled: "disabled", expanded: "expanded", closeOther: "closeOther", loaded: "loaded" }, queries: [{ propertyName: "content", first: true, predicate: AccordionContent, descendants: true }, { propertyName: "customTitle", first: true, predicate: AccordionTitle }, { propertyName: "customHeader", first: true, predicate: AccordionHeader }], ngImport: i0 }); }
1848
+ }
1849
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: AccordionItem, decorators: [{
1850
+ type: Directive,
1851
+ args: [{
1852
+ selector: "accordion-item",
1853
+ standalone: false
1854
+ }]
1855
+ }], propDecorators: { title: [{
1856
+ type: Input
1857
+ }], disabled: [{
1858
+ type: Input
1859
+ }], expanded: [{
1860
+ type: Input
1861
+ }], closeOther: [{
1862
+ type: Input
1863
+ }], loaded: [{
1864
+ type: Input
1865
+ }], content: [{
1866
+ type: ContentChild,
1867
+ args: [AccordionContent]
1868
+ }], customTitle: [{
1869
+ type: ContentChild,
1870
+ args: [AccordionTitle, { descendants: false }]
1871
+ }], customHeader: [{
1872
+ type: ContentChild,
1873
+ args: [AccordionHeader, { descendants: false }]
1874
+ }] } });
1875
+
1876
+ class TogglePipe {
1877
+ /**
1878
+ * Make the toggle function available to be called from
1879
+ * outside.
1880
+ * @param index - index of the accordion item
1881
+ */
1882
+ transform(i, toggleFn) {
1883
+ return () => toggleFn(i);
1884
+ }
1885
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: TogglePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
1886
+ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.15", ngImport: i0, type: TogglePipe, isStandalone: false, name: "getToggleFunction" }); }
1887
+ }
1888
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: TogglePipe, decorators: [{
1889
+ type: Pipe,
1890
+ args: [{
1891
+ name: 'getToggleFunction',
1892
+ standalone: false
1893
+ }]
1894
+ }] });
1895
+
1896
+ /* Copyright © 2023-2025 Napkin Apps Corp. All rights reserved. */
1897
+ class AccordionComponent {
1898
+ constructor(fasterThemes, cdr) {
1899
+ this.fasterThemes = fasterThemes;
1900
+ this.cdr = cdr;
1901
+ this.expanded = new Set();
1902
+ this.collapsing = false;
1903
+ this.focus = false;
1904
+ this.showCaret = true;
1905
+ this.headerClicked = new EventEmitter();
1906
+ this.destroyRef = inject(DestroyRef);
1907
+ this.toggleState = (index) => {
1908
+ const itemsArray = this.items.toArray();
1909
+ if (this.expanded.has(index)) {
1910
+ this.expanded.delete(index);
1911
+ if (this.headerClicked) {
1912
+ this.headerClicked.emit({ index: index, open: false });
1913
+ }
1914
+ return false;
1915
+ }
1916
+ else {
1917
+ if (this.collapsing) {
1918
+ this.expanded.clear();
1919
+ }
1920
+ this.expanded.add(index);
1921
+ if (!itemsArray[index]?.loaded) {
1922
+ itemsArray[index].loaded = true;
1923
+ }
1924
+ if (this.headerClicked) {
1925
+ this.headerClicked.emit({ index: index, open: true });
1926
+ }
1927
+ return true;
1928
+ }
1929
+ };
1930
+ }
1931
+ ngOnInit() {
1932
+ if (this.collapsing && this.focus)
1933
+ this.focus = false;
1934
+ const input = this.theme ?? this.themeName;
1935
+ if (typeof input === 'string') {
1936
+ this.themeName = this.fasterThemes?.getTheme(input);
1937
+ }
1938
+ else if (input != null) {
1939
+ this.themeName = input;
1940
+ }
1941
+ // If no theme provided, template calls fall back to parentTheme via getTheme(undefined)
1942
+ this.fasterThemes.resize$
1943
+ .pipe(takeUntilDestroyed(this.destroyRef))
1944
+ .subscribe(() => this.cdr.markForCheck());
1945
+ }
1946
+ ngAfterViewInit() {
1947
+ merge(this.items.changes, of(this.items))
1948
+ .pipe(map(() => this.items.toArray()), takeUntilDestroyed(this.destroyRef))
1949
+ .subscribe((items) => {
1950
+ items.forEach((item, index) => {
1951
+ if (item.expanded) {
1952
+ this.expanded.add(index);
1953
+ }
1954
+ });
1955
+ this.cdr.markForCheck();
1956
+ });
1957
+ }
1958
+ collapseAll() {
1959
+ this.expanded.clear();
1960
+ this.items?.forEach(item => item.collapse());
1961
+ this.cdr.markForCheck();
1962
+ }
1963
+ activeStyle(index) {
1964
+ return this.expanded.has(index);
1965
+ }
1966
+ onHeaderClick(event, index, item) {
1967
+ event.stopPropagation();
1968
+ let opened = false;
1969
+ if (!item.disabled) {
1970
+ opened = this.toggleState(index);
1971
+ }
1972
+ if (opened && item.closeOther) {
1973
+ this.closeOtherAccordion(item.closeOther);
1974
+ }
1975
+ }
1976
+ closeOtherAccordion(targetTitle) {
1977
+ this.items.forEach((accordion, i) => {
1978
+ if (accordion.title === targetTitle) {
1979
+ this.expanded.delete(i);
1980
+ accordion.collapse();
1981
+ }
1982
+ });
1983
+ }
1984
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: AccordionComponent, deps: [{ token: FasterThemesService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
1985
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.15", type: AccordionComponent, isStandalone: false, selector: "accordion", inputs: { name: "name", collapsing: "collapsing", theme: "theme", themeName: "themeName", accordionName: "accordionName", focus: "focus", showCaret: "showCaret" }, outputs: { headerClicked: "headerClicked" }, queries: [{ propertyName: "items", predicate: AccordionItem }], ngImport: i0, template: "<section [style]=\"fasterThemes.accordion(themeName, accordionName)\">\n <div *ngFor=\"let item of items;index as i\" \n [class.disabled]=\"item.disabled\" \n [class.active]=\"expanded.has(i)\"\n [class.hidden]=\"focus && expanded && expanded.size && \n expanded.size > 0 && !expanded.has(i)\">\n <ng-container \n [ngTemplateOutlet]=\"(item?.customHeader?.templateRef || defaultHeader)\"\n [ngTemplateOutletContext]=\"{$implicit: item, index: i, toggle: i | getToggleFunction: toggleState}\">\n </ng-container>\n <div [class.expanded]=\"expanded.has(i)\" \n [style.display]=\"expanded.has(i) ? 'initial' : 'none'\">\n <ng-container *ngIf=\"item.loaded || item.expanded\">\n <ng-container *ngTemplateOutlet=\"item?.content?.templateRef || null\">\n </ng-container>\n </ng-container>\n </div>\n </div>\n</section>\n\n<ng-template #defaultHeader let-item let-index=\"index\">\n <header [style]=\"fasterThemes.header(themeName, accordionName, activeStyle(index), index)\"\n (click)=\"onHeaderClick($event, index, item)\">\n <ng-container *ngTemplateOutlet=\"item?.customTitle?.templateRef || defaultTitle\"></ng-container>\n <span *ngIf=\"showCaret\" class=\"accordion__caret\" [class.accordion__caret--open]=\"expanded.has(index)\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 36 36\" width=\"36\" height=\"36\" aria-hidden=\"true\">\n <path d=\"M7 10l5 5 5-5z\" fill=\"currentColor\"/>\n </svg>\n </span>\n </header>\n <ng-template #defaultTitle>\n <p class=\"accordion__title\">{{item?.title}}</p>\n </ng-template>\n</ng-template>", styles: [":host{--accordion-header-height: 50px;--accordion-header-bg: hsl(0, 0%, 95%);--accordion-disabled: hsl(0, 0%, 60%);--accordion-border-radius: 8px}.accordion{border:1px solid hsl(0,0%,80%);border-radius:var(--accordion-border-radius)}.accordion__item:first-child .accordion__header{border-radius:var(--accordion-border-radius) var(--accordion-border-radius) 0 0}.accordion__item:last-child .accordion__header{border-radius:0 0 var(--accordion-border-radius) var(--accordion-border-radius)}.accordion__header{display:flex;cursor:pointer;background:var(--accordion-header-bg)}.accordion__toggle-btn{border:none;transition:transform .3s ease-in;background:none}.accordion__item.disabled .accordion__header{color:var(--accordion-disabled);cursor:not-allowed}.accordion__toggle-btn:disabled{cursor:not-allowed}.accordion__toggle-btn:disabled svg path:nth-child(2){fill:var(--accordion-disabled)}.accordion__item.active .accordion__toggle-btn{transform:rotate(180deg)}.hidden{display:none}.accordion__caret{display:inline-flex;align-items:center;margin-left:auto;transition:transform .2s ease;pointer-events:none}.accordion__caret--open{transform:rotate(180deg)}.accordion__item .accordion__header:focus-within{border-color:var(--primary);border-radius:var(--accordion-border-radius)}.accordion__toggle-btn:focus{outline:none}.accordion__content{visibility:hidden;opacity:0;height:0;overflow:hidden;transition:opacity .3s,height .3s ease-out}.expanded{visibility:visible!important;opacity:1;height:auto;overflow:visible}\n"], dependencies: [{ kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: TogglePipe, name: "getToggleFunction" }], animations: [
1986
+ trigger('contentExpansion', [
1987
+ state('expanded', style({ height: '*', opacity: 1, visibility: 'visible', overflow: 'visible' })),
1988
+ state('collapsed', style({ width: 'min-content', height: '0px', opacity: 0, visibility: 'hidden', overflow: 'hidden' })),
1989
+ transition('expanded <=> collapsed', animate('200ms cubic-bezier(.37,1.04,.68,.98)')),
1990
+ ]),
1991
+ ], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1992
+ }
1993
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: AccordionComponent, decorators: [{
1994
+ type: Component,
1995
+ args: [{ selector: 'accordion', changeDetection: ChangeDetectionStrategy.OnPush, animations: [
1996
+ trigger('contentExpansion', [
1997
+ state('expanded', style({ height: '*', opacity: 1, visibility: 'visible', overflow: 'visible' })),
1998
+ state('collapsed', style({ width: 'min-content', height: '0px', opacity: 0, visibility: 'hidden', overflow: 'hidden' })),
1999
+ transition('expanded <=> collapsed', animate('200ms cubic-bezier(.37,1.04,.68,.98)')),
2000
+ ]),
2001
+ ], standalone: false, template: "<section [style]=\"fasterThemes.accordion(themeName, accordionName)\">\n <div *ngFor=\"let item of items;index as i\" \n [class.disabled]=\"item.disabled\" \n [class.active]=\"expanded.has(i)\"\n [class.hidden]=\"focus && expanded && expanded.size && \n expanded.size > 0 && !expanded.has(i)\">\n <ng-container \n [ngTemplateOutlet]=\"(item?.customHeader?.templateRef || defaultHeader)\"\n [ngTemplateOutletContext]=\"{$implicit: item, index: i, toggle: i | getToggleFunction: toggleState}\">\n </ng-container>\n <div [class.expanded]=\"expanded.has(i)\" \n [style.display]=\"expanded.has(i) ? 'initial' : 'none'\">\n <ng-container *ngIf=\"item.loaded || item.expanded\">\n <ng-container *ngTemplateOutlet=\"item?.content?.templateRef || null\">\n </ng-container>\n </ng-container>\n </div>\n </div>\n</section>\n\n<ng-template #defaultHeader let-item let-index=\"index\">\n <header [style]=\"fasterThemes.header(themeName, accordionName, activeStyle(index), index)\"\n (click)=\"onHeaderClick($event, index, item)\">\n <ng-container *ngTemplateOutlet=\"item?.customTitle?.templateRef || defaultTitle\"></ng-container>\n <span *ngIf=\"showCaret\" class=\"accordion__caret\" [class.accordion__caret--open]=\"expanded.has(index)\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 36 36\" width=\"36\" height=\"36\" aria-hidden=\"true\">\n <path d=\"M7 10l5 5 5-5z\" fill=\"currentColor\"/>\n </svg>\n </span>\n </header>\n <ng-template #defaultTitle>\n <p class=\"accordion__title\">{{item?.title}}</p>\n </ng-template>\n</ng-template>", styles: [":host{--accordion-header-height: 50px;--accordion-header-bg: hsl(0, 0%, 95%);--accordion-disabled: hsl(0, 0%, 60%);--accordion-border-radius: 8px}.accordion{border:1px solid hsl(0,0%,80%);border-radius:var(--accordion-border-radius)}.accordion__item:first-child .accordion__header{border-radius:var(--accordion-border-radius) var(--accordion-border-radius) 0 0}.accordion__item:last-child .accordion__header{border-radius:0 0 var(--accordion-border-radius) var(--accordion-border-radius)}.accordion__header{display:flex;cursor:pointer;background:var(--accordion-header-bg)}.accordion__toggle-btn{border:none;transition:transform .3s ease-in;background:none}.accordion__item.disabled .accordion__header{color:var(--accordion-disabled);cursor:not-allowed}.accordion__toggle-btn:disabled{cursor:not-allowed}.accordion__toggle-btn:disabled svg path:nth-child(2){fill:var(--accordion-disabled)}.accordion__item.active .accordion__toggle-btn{transform:rotate(180deg)}.hidden{display:none}.accordion__caret{display:inline-flex;align-items:center;margin-left:auto;transition:transform .2s ease;pointer-events:none}.accordion__caret--open{transform:rotate(180deg)}.accordion__item .accordion__header:focus-within{border-color:var(--primary);border-radius:var(--accordion-border-radius)}.accordion__toggle-btn:focus{outline:none}.accordion__content{visibility:hidden;opacity:0;height:0;overflow:hidden;transition:opacity .3s,height .3s ease-out}.expanded{visibility:visible!important;opacity:1;height:auto;overflow:visible}\n"] }]
2002
+ }], ctorParameters: () => [{ type: FasterThemesService }, { type: i0.ChangeDetectorRef }], propDecorators: { name: [{
2003
+ type: Input
2004
+ }], collapsing: [{
2005
+ type: Input
2006
+ }], theme: [{
2007
+ type: Input
2008
+ }], themeName: [{
2009
+ type: Input
2010
+ }], accordionName: [{
2011
+ type: Input
2012
+ }], focus: [{
2013
+ type: Input
2014
+ }], showCaret: [{
2015
+ type: Input
2016
+ }], headerClicked: [{
2017
+ type: Output
2018
+ }], items: [{
2019
+ type: ContentChildren,
2020
+ args: [AccordionItem]
2021
+ }] } });
2022
+
2023
+ /* Copyright © 2023-2025 Napkin Apps Corp. All rights reserved. */
2024
+ class nColorDirective {
2025
+ // Selector-aliased form: <div nColor="Primary" [theme]="t">
2026
+ set nColor(v) {
2027
+ if (v !== undefined && v !== '')
2028
+ this.color = v;
2029
+ }
2030
+ constructor(el, fasterThemes) {
2031
+ this.el = el;
2032
+ this.fasterThemes = fasterThemes;
2033
+ this.appliedProps = new Set();
2034
+ }
2035
+ ngOnChanges() {
2036
+ this.clearAppliedStyles();
2037
+ this.apply();
2038
+ }
2039
+ apply() {
2040
+ const theme = this.fasterThemes.getTheme(this.theme);
2041
+ const palette = this.fasterThemes.getPalette(theme, this.color);
2042
+ if (!palette)
2043
+ return;
2044
+ if (palette.backgroundColor)
2045
+ this.setStyle('background-color', palette.backgroundColor);
2046
+ if (palette.color)
2047
+ this.setStyle('color', palette.color);
2048
+ if (palette.gradient?.startsWith('background-image:'))
2049
+ this.setStyle('background-image', palette.gradient.replace('background-image:', ''));
2050
+ else if (palette.gradient?.startsWith('background-color:'))
2051
+ this.setStyle('background-color', palette.gradient.replace('background-color:', ''));
2052
+ else if (palette.gradient?.startsWith('background:'))
2053
+ this.setStyle('background', palette.gradient.replace('background:', ''));
2054
+ }
2055
+ setStyle(prop, value) {
2056
+ this.el.nativeElement.style.setProperty(prop, value);
2057
+ this.appliedProps.add(prop);
2058
+ }
2059
+ clearAppliedStyles() {
2060
+ for (const prop of this.appliedProps) {
2061
+ this.el.nativeElement.style.removeProperty(prop);
2062
+ }
2063
+ this.appliedProps.clear();
2064
+ }
2065
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: nColorDirective, deps: [{ token: i0.ElementRef }, { token: FasterThemesService }], target: i0.ɵɵFactoryTarget.Directive }); }
2066
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.15", type: nColorDirective, isStandalone: false, selector: "[nColor]", inputs: { theme: "theme", color: "color", nColor: "nColor" }, usesOnChanges: true, ngImport: i0 }); }
2067
+ }
2068
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: nColorDirective, decorators: [{
2069
+ type: Directive,
2070
+ args: [{
2071
+ selector: '[nColor]',
2072
+ standalone: false
2073
+ }]
2074
+ }], ctorParameters: () => [{ type: i0.ElementRef }, { type: FasterThemesService }], propDecorators: { theme: [{
2075
+ type: Input
2076
+ }], color: [{
2077
+ type: Input
2078
+ }], nColor: [{
2079
+ type: Input,
2080
+ args: ['nColor']
2081
+ }] } });
2082
+
2083
+ /* Copyright © 2023-2025 Napkin Apps Corp. All rights reserved. */
2084
+ class nContainerDirective {
2085
+ // Selector-aliased form: <div nContainer="Wide" [theme]="t">
2086
+ set nContainer(v) {
2087
+ if (v !== undefined && v !== '')
2088
+ this.container = v;
2089
+ }
2090
+ constructor(el, fasterThemes) {
2091
+ this.el = el;
2092
+ this.fasterThemes = fasterThemes;
2093
+ this.appliedProps = new Set();
2094
+ this.resizeSub = this.fasterThemes.resize$.subscribe(() => this.apply());
2095
+ }
2096
+ ngOnDestroy() {
2097
+ this.resizeSub.unsubscribe();
2098
+ }
2099
+ ngOnChanges() {
2100
+ this.clearAppliedStyles();
2101
+ this.apply();
2102
+ }
2103
+ apply() {
2104
+ const theme = this.fasterThemes.getTheme(this.theme);
2105
+ const container = this.fasterThemes.getContainer(theme, this.container);
2106
+ if (!container)
2107
+ return;
2108
+ // Palette takes precedence over local colors
2109
+ const usedPalette = container.containerPalette
2110
+ ? this.applyPalette(theme, container.containerPalette)
2111
+ : false;
2112
+ if (!usedPalette) {
2113
+ if (container.containerBackgroundGradient)
2114
+ this.setStyle('background-image', container.containerBackgroundGradient);
2115
+ else if (container.containerBackground)
2116
+ this.setStyle('background-color', container.containerBackground);
2117
+ }
2118
+ // Image takes precedence over palette background
2119
+ if (container.backgroundImage?.length) {
2120
+ this.setStyle('background-image', `url(${container.backgroundImage})`);
2121
+ if (container.backgroundSize)
2122
+ this.setStyle('background-size', container.backgroundSize);
2123
+ }
2124
+ if (container.containerBorder)
2125
+ this.setStyle('border', container.containerBorder);
2126
+ if (container.containerBoxShadow)
2127
+ this.setStyle('box-shadow', container.containerBoxShadow);
2128
+ if (container.containerAlignment && !container.containerAlignmentV) {
2129
+ this.setStyle('display', 'flex');
2130
+ this.setStyle('justify-content', container.containerAlignment);
2131
+ this.setStyle('align-items', container.containerAlignment);
2132
+ }
2133
+ else if (container.containerAlignment && container.containerAlignmentV) {
2134
+ this.setStyle('display', 'flex');
2135
+ this.setStyle('justify-content', container.containerAlignment);
2136
+ this.setStyle('align-items', container.containerAlignmentV);
2137
+ }
2138
+ if (container.containerLayout)
2139
+ this.setStyle('flex-flow', container.containerLayout);
2140
+ if (container.containerWidth) {
2141
+ const width = container.containerWidth === '100vw'
2142
+ ? `${this.fasterThemes.screenWidth}px`
2143
+ : container.containerWidth;
2144
+ this.setStyle('width', width);
2145
+ }
2146
+ if (container.containerHeight)
2147
+ this.setStyle('height', container.containerHeight);
2148
+ if (container.containerOverflowX)
2149
+ this.setStyle('overflow-x', container.containerOverflowX);
2150
+ if (container.containerOverflowY)
2151
+ this.setStyle('overflow-y', container.containerOverflowY);
2152
+ if (container.containerPosition)
2153
+ this.setStyle('position', container.containerPosition);
2154
+ if (container.containerMargin)
2155
+ this.setStyle('margin', this.fasterThemes.responsiveSpace(theme, container.containerMargin, theme.responsiveMarginMin, theme.responsiveMarginMax));
2156
+ if (container.containerPadding)
2157
+ this.setStyle('padding', this.fasterThemes.responsiveSpace(theme, container.containerPadding, theme.responsivePaddingMin, theme.responsivePaddingMax));
2158
+ }
2159
+ applyPalette(theme, paletteName) {
2160
+ const palette = this.fasterThemes.getPalette(theme, paletteName);
2161
+ if (!palette)
2162
+ return false;
2163
+ if (palette.backgroundColor)
2164
+ this.setStyle('background-color', palette.backgroundColor);
2165
+ if (palette.color)
2166
+ this.setStyle('color', palette.color);
2167
+ if (palette.gradient?.startsWith('background-image:'))
2168
+ this.setStyle('background-image', palette.gradient.replace('background-image:', ''));
2169
+ else if (palette.gradient?.startsWith('background-color:'))
2170
+ this.setStyle('background-color', palette.gradient.replace('background-color:', ''));
2171
+ else if (palette.gradient?.startsWith('background:'))
2172
+ this.setStyle('background', palette.gradient.replace('background:', ''));
2173
+ return true;
2174
+ }
2175
+ setStyle(prop, value) {
2176
+ this.el.nativeElement.style.setProperty(prop, value);
2177
+ this.appliedProps.add(prop);
2178
+ }
2179
+ clearAppliedStyles() {
2180
+ for (const prop of this.appliedProps) {
2181
+ this.el.nativeElement.style.removeProperty(prop);
2182
+ }
2183
+ this.appliedProps.clear();
2184
+ }
2185
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: nContainerDirective, deps: [{ token: i0.ElementRef }, { token: FasterThemesService }], target: i0.ɵɵFactoryTarget.Directive }); }
2186
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.15", type: nContainerDirective, isStandalone: false, selector: "[nContainer]", inputs: { theme: "theme", container: "container", nContainer: "nContainer" }, usesOnChanges: true, ngImport: i0 }); }
2187
+ }
2188
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: nContainerDirective, decorators: [{
2189
+ type: Directive,
2190
+ args: [{
2191
+ selector: '[nContainer]',
2192
+ standalone: false
2193
+ }]
2194
+ }], ctorParameters: () => [{ type: i0.ElementRef }, { type: FasterThemesService }], propDecorators: { theme: [{
2195
+ type: Input
2196
+ }], container: [{
2197
+ type: Input
2198
+ }], nContainer: [{
2199
+ type: Input,
2200
+ args: ['nContainer']
2201
+ }] } });
2202
+
2203
+ /* Copyright © 2023-2025 Napkin Apps Corp. All rights reserved. */
2204
+ class nTypeDirective {
2205
+ // Selector-aliased form: <div nType="H1" [theme]="t">
2206
+ set nType(v) {
2207
+ if (v !== undefined && v !== '')
2208
+ this.type = v;
2209
+ }
2210
+ constructor(el, fasterThemes) {
2211
+ this.el = el;
2212
+ this.fasterThemes = fasterThemes;
2213
+ this.appliedProps = new Set();
2214
+ this.resizeSub = this.fasterThemes.resize$.subscribe(() => this.apply());
2215
+ }
2216
+ ngOnDestroy() {
2217
+ this.resizeSub.unsubscribe();
2218
+ }
2219
+ ngOnChanges() {
2220
+ this.clearAppliedStyles();
2221
+ this.apply();
2222
+ }
2223
+ apply() {
2224
+ const theme = this.fasterThemes.getTheme(this.theme);
2225
+ // typeface is a backwards-compat alias for type
2226
+ const name = this.type || this.typeface;
2227
+ const t = this.fasterThemes.typeObject(theme, name);
2228
+ if (!t)
2229
+ return;
2230
+ // Palette takes precedence over local colors
2231
+ const usedPalette = t.typePalette
2232
+ ? this.applyPalette(theme, t.typePalette)
2233
+ : false;
2234
+ if (!usedPalette) {
2235
+ if (t.typeBackground)
2236
+ this.setStyle('background-color', t.typeBackground);
2237
+ if (t.typeFontColor)
2238
+ this.setStyle('color', t.typeFontColor);
2239
+ }
2240
+ if (t.typeFont)
2241
+ this.setStyle('font-family', t.typeFont);
2242
+ if (t.typeFontSize)
2243
+ this.setStyle('font-size', `${this.fasterThemes.responsiveType(theme, t)}px`);
2244
+ if (t.typeFontWeight)
2245
+ this.setStyle('font-weight', t.typeFontWeight);
2246
+ if (t.typeFontAlignment)
2247
+ this.setStyle('text-align', t.typeFontAlignment);
2248
+ if (t.typeTextShadow)
2249
+ this.setStyle('text-shadow', t.typeTextShadow);
2250
+ if (t.typePadding)
2251
+ this.setStyle('padding', this.fasterThemes.responsiveSpace(theme, t.typePadding, theme.responsivePaddingMin, theme.responsivePaddingMax));
2252
+ if (t.typeMargin)
2253
+ this.setStyle('margin', this.fasterThemes.responsiveSpace(theme, t.typeMargin, theme.responsiveMarginMin, theme.responsiveMarginMax));
2254
+ }
2255
+ applyPalette(theme, paletteName) {
2256
+ const palette = this.fasterThemes.getPalette(theme, paletteName);
2257
+ if (!palette)
2258
+ return false;
2259
+ if (palette.backgroundColor)
2260
+ this.setStyle('background-color', palette.backgroundColor);
2261
+ if (palette.color)
2262
+ this.setStyle('color', palette.color);
2263
+ if (palette.gradient?.startsWith('background-image:'))
2264
+ this.setStyle('background-image', palette.gradient.replace('background-image:', ''));
2265
+ else if (palette.gradient?.startsWith('background-color:'))
2266
+ this.setStyle('background-color', palette.gradient.replace('background-color:', ''));
2267
+ else if (palette.gradient?.startsWith('background:'))
2268
+ this.setStyle('background', palette.gradient.replace('background:', ''));
2269
+ return true;
2270
+ }
2271
+ setStyle(prop, value) {
2272
+ this.el.nativeElement.style.setProperty(prop, value);
2273
+ this.appliedProps.add(prop);
2274
+ }
2275
+ clearAppliedStyles() {
2276
+ for (const prop of this.appliedProps) {
2277
+ this.el.nativeElement.style.removeProperty(prop);
2278
+ }
2279
+ this.appliedProps.clear();
2280
+ }
2281
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: nTypeDirective, deps: [{ token: i0.ElementRef }, { token: FasterThemesService }], target: i0.ɵɵFactoryTarget.Directive }); }
2282
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.15", type: nTypeDirective, isStandalone: false, selector: "[nType]", inputs: { theme: "theme", type: "type", typeface: "typeface", nType: "nType" }, usesOnChanges: true, ngImport: i0 }); }
2283
+ }
2284
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: nTypeDirective, decorators: [{
2285
+ type: Directive,
2286
+ args: [{
2287
+ selector: '[nType]',
2288
+ standalone: false
2289
+ }]
2290
+ }], ctorParameters: () => [{ type: i0.ElementRef }, { type: FasterThemesService }], propDecorators: { theme: [{
2291
+ type: Input
2292
+ }], type: [{
2293
+ type: Input
2294
+ }], typeface: [{
2295
+ type: Input
2296
+ }], nType: [{
2297
+ type: Input,
2298
+ args: ['nType']
2299
+ }] } });
2300
+
2301
+ class RippleDirective {
2302
+ constructor(el, renderer) {
2303
+ this.el = el;
2304
+ this.renderer = renderer;
2305
+ this.rippleDuration = ".6"; // Animation duration in ms
2306
+ this.rippleColor = "rgba(255, 255, 255, 0.5)";
2307
+ this.noRipple = false;
2308
+ }
2309
+ ngOnInit() {
2310
+ if (this.noRipple)
2311
+ return;
2312
+ // Ensure the parent element has required styles
2313
+ this.renderer.setStyle(this.el.nativeElement, 'position', 'relative');
2314
+ this.renderer.setStyle(this.el.nativeElement, 'overflow', 'hidden');
2315
+ if (!parseFloat(this.rippleDuration)) {
2316
+ this.rippleDuration = ".6";
2317
+ }
2318
+ }
2319
+ createRipple(event) {
2320
+ if (this.noRipple)
2321
+ return;
2322
+ const button = this.el.nativeElement;
2323
+ const ripple = this.renderer.createElement('span');
2324
+ // Get button dimensions
2325
+ const rect = button.getBoundingClientRect();
2326
+ const size = Math.max(rect.width, rect.height);
2327
+ const x = event.clientX - rect.left - size / 2;
2328
+ const y = event.clientY - rect.top - size / 2;
2329
+ // Apply styles dynamically
2330
+ this.renderer.setStyle(ripple, 'position', 'absolute');
2331
+ this.renderer.setStyle(ripple, 'border-radius', '50%');
2332
+ this.renderer.setStyle(ripple, 'background', this.rippleColor);
2333
+ this.renderer.setStyle(ripple, 'width', `${size}px`);
2334
+ this.renderer.setStyle(ripple, 'height', `${size}px`);
2335
+ this.renderer.setStyle(ripple, 'left', `${x}px`);
2336
+ this.renderer.setStyle(ripple, 'top', `${y}px`);
2337
+ this.renderer.setStyle(ripple, 'transform', 'scale(0)');
2338
+ this.renderer.setStyle(ripple, 'opacity', '1');
2339
+ this.renderer.setStyle(ripple, 'transition', `transform ${this.rippleDuration}s ease-out, opacity ${this.rippleDuration}s ease-out`);
2340
+ // Append ripple to the button
2341
+ this.renderer.appendChild(button, ripple);
2342
+ // Trigger animation
2343
+ setTimeout(() => {
2344
+ this.renderer.setStyle(ripple, 'transform', 'scale(4)');
2345
+ this.renderer.setStyle(ripple, 'opacity', '0');
2346
+ });
2347
+ // Remove ripple after animation completes
2348
+ setTimeout(() => {
2349
+ this.renderer.removeChild(button, ripple);
2350
+ }, parseFloat(this.rippleDuration) * 2000);
2351
+ }
2352
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: RippleDirective, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Directive }); }
2353
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.15", type: RippleDirective, isStandalone: true, selector: "[ripple]", inputs: { rippleDuration: "rippleDuration", rippleColor: "rippleColor", noRipple: "noRipple" }, host: { listeners: { "click": "createRipple($event)" } }, ngImport: i0 }); }
2354
+ }
2355
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: RippleDirective, decorators: [{
2356
+ type: Directive,
2357
+ args: [{
2358
+ selector: '[ripple]',
2359
+ standalone: true
2360
+ }]
2361
+ }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.Renderer2 }], propDecorators: { rippleDuration: [{
2362
+ type: Input
2363
+ }], rippleColor: [{
2364
+ type: Input
2365
+ }], noRipple: [{
2366
+ type: Input
2367
+ }], createRipple: [{
2368
+ type: HostListener,
2369
+ args: ['click', ['$event']]
2370
+ }] } });
2371
+
2372
+ /* Copyright © 2023-2025 Napkin Apps Corp. All rights reserved. */
2373
+ class nButtonDirective {
2374
+ // Selector-aliased form: <button nButton="Create" [theme]="t">
2375
+ set nButton(v) {
2376
+ if (v !== undefined && v !== '')
2377
+ this.button = v;
2378
+ }
2379
+ constructor(el, fasterThemes) {
2380
+ this.el = el;
2381
+ this.fasterThemes = fasterThemes;
2382
+ this.appliedProps = new Set();
2383
+ this.resizeSub = this.fasterThemes.resize$.subscribe(() => this.apply());
2384
+ }
2385
+ ngOnDestroy() {
2386
+ this.resizeSub.unsubscribe();
2387
+ }
2388
+ ngOnChanges() {
2389
+ this.clearAppliedStyles();
2390
+ this.apply();
2391
+ }
2392
+ apply() {
2393
+ const theme = this.fasterThemes.getTheme(this.theme);
2394
+ const name = this.button || this.buttonStyle;
2395
+ const buttonStyle = this.fasterThemes.getButton(theme, name);
2396
+ if (!buttonStyle)
2397
+ return;
2398
+ const type = buttonStyle.buttonTypeStyle
2399
+ ? this.fasterThemes.typeObject(theme, buttonStyle.buttonTypeStyle)
2400
+ : null;
2401
+ // Palette takes precedence over local colors
2402
+ const usedPalette = buttonStyle.buttonPalette
2403
+ ? this.applyPalette(theme, buttonStyle.buttonPalette)
2404
+ : false;
2405
+ if (!usedPalette) {
2406
+ if (buttonStyle.buttonBackground)
2407
+ this.setStyle('background-color', buttonStyle.buttonBackground);
2408
+ if (buttonStyle.buttonColor)
2409
+ this.setStyle('color', buttonStyle.buttonColor);
2410
+ }
2411
+ if (buttonStyle.buttonPadding)
2412
+ this.setStyle('padding', this.fasterThemes.responsiveSpace(theme, buttonStyle.buttonPadding, theme.responsivePaddingMin, theme.responsivePaddingMax));
2413
+ if (buttonStyle.buttonMargin)
2414
+ this.setStyle('margin', this.fasterThemes.responsiveSpace(theme, buttonStyle.buttonMargin, theme.responsiveMarginMin, theme.responsiveMarginMax));
2415
+ this.setStyle('border', buttonStyle.buttonBorder || 'none');
2416
+ if (buttonStyle.buttonRadius)
2417
+ this.setStyle('border-radius', buttonStyle.buttonRadius);
2418
+ if (buttonStyle.buttonShadow)
2419
+ this.setStyle('box-shadow', buttonStyle.buttonShadow);
2420
+ if (type?.typeFont)
2421
+ this.setStyle('font-family', type.typeFont);
2422
+ if (type?.typeFontSize)
2423
+ this.setStyle('font-size', `${this.fasterThemes.responsiveType(theme, type)}px`);
2424
+ if (type?.typeFontWeight)
2425
+ this.setStyle('font-weight', type.typeFontWeight);
2426
+ if (type?.typeFontAlignment)
2427
+ this.setStyle('text-align', type.typeFontAlignment);
2428
+ if (type?.typeTextShadow)
2429
+ this.setStyle('text-shadow', type.typeTextShadow);
2430
+ if (type?.typePadding)
2431
+ this.setStyle('padding', this.fasterThemes.responsiveSpace(theme, type.typePadding, theme.responsivePaddingMin, theme.responsivePaddingMax));
2432
+ if (type?.typeMargin)
2433
+ this.setStyle('margin', this.fasterThemes.responsiveSpace(theme, type.typeMargin, theme.responsiveMarginMin, theme.responsiveMarginMax));
2434
+ this.setStyle('cursor', 'pointer');
2435
+ this.setStyle('box-sizing', 'border-box');
2436
+ }
2437
+ applyPalette(theme, paletteName) {
2438
+ const palette = this.fasterThemes.getPalette(theme, paletteName);
2439
+ if (!palette)
2440
+ return false;
2441
+ if (palette.backgroundColor)
2442
+ this.setStyle('background-color', palette.backgroundColor);
2443
+ if (palette.color)
2444
+ this.setStyle('color', palette.color);
2445
+ if (palette.gradient?.startsWith('background-image:'))
2446
+ this.setStyle('background-image', palette.gradient.replace('background-image:', ''));
2447
+ else if (palette.gradient?.startsWith('background-color:'))
2448
+ this.setStyle('background-color', palette.gradient.replace('background-color:', ''));
2449
+ else if (palette.gradient?.startsWith('background:'))
2450
+ this.setStyle('background', palette.gradient.replace('background:', ''));
2451
+ return true;
2452
+ }
2453
+ setStyle(prop, value) {
2454
+ this.el.nativeElement.style.setProperty(prop, value);
2455
+ this.appliedProps.add(prop);
2456
+ }
2457
+ clearAppliedStyles() {
2458
+ for (const prop of this.appliedProps) {
2459
+ this.el.nativeElement.style.removeProperty(prop);
2460
+ }
2461
+ this.appliedProps.clear();
2462
+ }
2463
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: nButtonDirective, deps: [{ token: i0.ElementRef }, { token: FasterThemesService }], target: i0.ɵɵFactoryTarget.Directive }); }
2464
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.15", type: nButtonDirective, isStandalone: false, selector: "[nButton]", inputs: { theme: "theme", button: "button", buttonStyle: "buttonStyle", nButton: "nButton" }, usesOnChanges: true, hostDirectives: [{ directive: RippleDirective, inputs: ["rippleDuration", "rippleDuration", "rippleColor", "rippleColor", "noRipple", "noRipple"] }], ngImport: i0 }); }
2465
+ }
2466
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: nButtonDirective, decorators: [{
2467
+ type: Directive,
2468
+ args: [{
2469
+ selector: '[nButton]',
2470
+ hostDirectives: [
2471
+ {
2472
+ directive: RippleDirective,
2473
+ inputs: ['rippleDuration', 'rippleColor', 'noRipple']
2474
+ }
2475
+ ],
2476
+ standalone: false
2477
+ }]
2478
+ }], ctorParameters: () => [{ type: i0.ElementRef }, { type: FasterThemesService }], propDecorators: { theme: [{
2479
+ type: Input
2480
+ }], button: [{
2481
+ type: Input
2482
+ }], buttonStyle: [{
2483
+ type: Input
2484
+ }], nButton: [{
2485
+ type: Input,
2486
+ args: ['nButton']
2487
+ }] } });
2488
+
2489
+ /* Copyright © 2023-2025 Napkin Apps Corp. All rights reserved. */
2490
+ class nClassDirective {
2491
+ // Selector-aliased form: <div nClass="color-blue" [theme]="t">
2492
+ set nClass(v) {
2493
+ if (v !== undefined && v !== '')
2494
+ this.class = v;
2495
+ }
2496
+ constructor(el, fasterThemes) {
2497
+ this.el = el;
2498
+ this.fasterThemes = fasterThemes;
2499
+ this.appliedProps = new Set();
2500
+ }
2501
+ ngOnChanges() {
2502
+ this.clearAppliedStyles();
2503
+ this.apply();
2504
+ }
2505
+ apply() {
2506
+ const theme = this.fasterThemes.getTheme(this.theme);
2507
+ const cssText = this.fasterThemes.class(theme, this.class);
2508
+ if (!cssText?.length)
2509
+ return;
2510
+ // Parse "prop: value; prop2: value2;" into individual setProperty calls
2511
+ // so re-applying clears precisely the props we set instead of stomping
2512
+ // the entire style attribute.
2513
+ for (const decl of cssText.split(';')) {
2514
+ const colon = decl.indexOf(':');
2515
+ if (colon < 0)
2516
+ continue;
2517
+ const prop = decl.slice(0, colon).trim();
2518
+ const value = decl.slice(colon + 1).trim();
2519
+ if (prop && value)
2520
+ this.setStyle(prop, value);
2521
+ }
2522
+ }
2523
+ setStyle(prop, value) {
2524
+ this.el.nativeElement.style.setProperty(prop, value);
2525
+ this.appliedProps.add(prop);
2526
+ }
2527
+ clearAppliedStyles() {
2528
+ for (const prop of this.appliedProps) {
2529
+ this.el.nativeElement.style.removeProperty(prop);
2530
+ }
2531
+ this.appliedProps.clear();
2532
+ }
2533
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: nClassDirective, deps: [{ token: i0.ElementRef }, { token: FasterThemesService }], target: i0.ɵɵFactoryTarget.Directive }); }
2534
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.15", type: nClassDirective, isStandalone: false, selector: "[nClass]", inputs: { theme: "theme", class: "class", nClass: "nClass" }, usesOnChanges: true, ngImport: i0 }); }
2535
+ }
2536
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: nClassDirective, decorators: [{
2537
+ type: Directive,
2538
+ args: [{
2539
+ selector: '[nClass]',
2540
+ standalone: false
2541
+ }]
2542
+ }], ctorParameters: () => [{ type: i0.ElementRef }, { type: FasterThemesService }], propDecorators: { theme: [{
2543
+ type: Input
2544
+ }], class: [{
2545
+ type: Input
2546
+ }], nClass: [{
2547
+ type: Input,
2548
+ args: ['nClass']
2549
+ }] } });
2550
+
2551
+ // Firebase removed - no longer used
2552
+ // export interface firebaseConfig {
2553
+ // initThemes: any;
2554
+ // apiKey: string;
2555
+ // authDomain: string;
2556
+ // databaseURL: string;
2557
+ // projectId: string;
2558
+ // storageBucket: string;
2559
+ // messagingSenderId: string;
2560
+ // appId: string;
2561
+ // measurementId: string;
2562
+ // }
2563
+ // export const FBT = new InjectionToken<Promise<FirebaseApp>>('FBT');
2564
+ // export const FAT = new InjectionToken<Promise<Auth>>('FAT');
2565
+ // export const INIT_THEMES = new InjectionToken<Promise<string>>('INIT_THEMES');
2566
+ //
2567
+ // const test = localStorage.getItem('test')
2568
+ // const configPromise = fetch(`https://firebaseconfig-aq3tslzx7a-uc.a.run.app?${test}`)
2569
+ // .then(res => res.json());
2570
+ class FasterThemesModule {
2571
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: FasterThemesModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
2572
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.15", ngImport: i0, type: FasterThemesModule, declarations: [FasterThemesComponent,
2573
+ AccordionComponent,
2574
+ AccordionItem,
2575
+ AccordionContent,
2576
+ AccordionTitle,
2577
+ AccordionHeader,
2578
+ TogglePipe,
2579
+ nColorDirective,
2580
+ nContainerDirective,
2581
+ nTypeDirective,
2582
+ nButtonDirective,
2583
+ nClassDirective], imports: [CommonModule,
2584
+ HttpClientModule,
2585
+ RippleDirective], exports: [FasterThemesComponent,
2586
+ AccordionComponent,
2587
+ AccordionTitle,
2588
+ AccordionHeader,
2589
+ AccordionContent,
2590
+ AccordionItem,
2591
+ TogglePipe,
2592
+ RippleDirective,
2593
+ nColorDirective,
2594
+ nContainerDirective,
2595
+ nTypeDirective,
2596
+ nButtonDirective,
2597
+ nClassDirective] }); }
2598
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: FasterThemesModule, providers: [
2599
+ // Firebase removed - no longer used
2600
+ // {
2601
+ // provide: FBT,
2602
+ // useFactory: async (): Promise<FirebaseApp> => {
2603
+ // const config = (await configPromise).firebaseConfig;
2604
+ // const existingApp = getApps().find(app => app.name === "napkin");
2605
+ // return existingApp ?? initializeApp(config, "napkin");
2606
+ // }
2607
+ // },
2608
+ // {
2609
+ // provide: FAT,
2610
+ // useFactory: async (appPromise: Promise<FirebaseApp>): Promise<Auth> => {
2611
+ // const app = await appPromise;
2612
+ // return getAuth(app);
2613
+ // },
2614
+ // deps: [FBT]
2615
+ // },
2616
+ // {
2617
+ // provide: INIT_THEMES,
2618
+ // useFactory: async (): Promise<string> => {
2619
+ // const { initThemes } = await configPromise;
2620
+ // return initThemes;
2621
+ // }
2622
+ // },
2623
+ FasterThemesService,
2624
+ ], imports: [CommonModule,
2625
+ HttpClientModule] }); }
2626
+ }
2627
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: FasterThemesModule, decorators: [{
2628
+ type: NgModule,
2629
+ args: [{
2630
+ declarations: [
2631
+ FasterThemesComponent,
2632
+ AccordionComponent,
2633
+ AccordionItem,
2634
+ AccordionContent,
2635
+ AccordionTitle,
2636
+ AccordionHeader,
2637
+ TogglePipe,
2638
+ nColorDirective,
2639
+ nContainerDirective,
2640
+ nTypeDirective,
2641
+ nButtonDirective,
2642
+ nClassDirective,
2643
+ ],
2644
+ exports: [
2645
+ FasterThemesComponent,
2646
+ AccordionComponent,
2647
+ AccordionTitle,
2648
+ AccordionHeader,
2649
+ AccordionContent,
2650
+ AccordionItem,
2651
+ TogglePipe,
2652
+ RippleDirective,
2653
+ nColorDirective,
2654
+ nContainerDirective,
2655
+ nTypeDirective,
2656
+ nButtonDirective,
2657
+ nClassDirective
2658
+ ], imports: [
2659
+ CommonModule,
2660
+ HttpClientModule,
2661
+ RippleDirective
2662
+ ], providers: [
2663
+ // Firebase removed - no longer used
2664
+ // {
2665
+ // provide: FBT,
2666
+ // useFactory: async (): Promise<FirebaseApp> => {
2667
+ // const config = (await configPromise).firebaseConfig;
2668
+ // const existingApp = getApps().find(app => app.name === "napkin");
2669
+ // return existingApp ?? initializeApp(config, "napkin");
2670
+ // }
2671
+ // },
2672
+ // {
2673
+ // provide: FAT,
2674
+ // useFactory: async (appPromise: Promise<FirebaseApp>): Promise<Auth> => {
2675
+ // const app = await appPromise;
2676
+ // return getAuth(app);
2677
+ // },
2678
+ // deps: [FBT]
2679
+ // },
2680
+ // {
2681
+ // provide: INIT_THEMES,
2682
+ // useFactory: async (): Promise<string> => {
2683
+ // const { initThemes } = await configPromise;
2684
+ // return initThemes;
2685
+ // }
2686
+ // },
2687
+ FasterThemesService,
2688
+ ]
2689
+ }]
2690
+ }] });
2691
+
2692
+ /* Copyright © 2023-2025 Napkin Apps Corp. All rights reserved. */
2693
+ class ThemeGeneratorComponent {
2694
+ constructor() {
2695
+ this.themeStyle = 'modern';
2696
+ this.purpose = 'marketing';
2697
+ this.palette = 'ocean';
2698
+ this.layout = 'row wrap';
2699
+ this.elevation = 'medium';
2700
+ this.textShadow = 'none';
2701
+ this.showPreview = false;
2702
+ this.themeGenerated = new EventEmitter();
2703
+ }
2704
+ ngOnInit() {
2705
+ this.regenerate();
2706
+ }
2707
+ ngOnChanges(changes) {
2708
+ if (changes['themeStyle'] ||
2709
+ changes['purpose'] ||
2710
+ changes['palette'] ||
2711
+ changes['layout'] ||
2712
+ changes['elevation'] ||
2713
+ changes['textShadow'] ||
2714
+ changes['name'] ||
2715
+ changes['responsiveStandardWidth'] ||
2716
+ changes['responsiveFontSizeMin'] ||
2717
+ changes['responsiveFontSizeMax'] ||
2718
+ changes['responsiveMarginMin'] ||
2719
+ changes['responsiveMarginMax'] ||
2720
+ changes['responsivePaddingMin'] ||
2721
+ changes['responsivePaddingMax']) {
2722
+ this.regenerate();
2723
+ }
2724
+ }
2725
+ generate() {
2726
+ const palette = Array.isArray(this.palette)
2727
+ ? this.palette
2728
+ : this.buildPalette(this.palette);
2729
+ const style = this.styleProfileFor(this.themeStyle);
2730
+ const purpose = this.purposeProfileFor(this.purpose);
2731
+ const semantics = this.semanticColors();
2732
+ const elevationCss = this.elevationCss(this.elevation, this.themeStyle);
2733
+ const textShadowCss = this.textShadowCss(this.textShadow);
2734
+ const paletteLabel = typeof this.palette === 'string' ? this.capitalize(this.palette) : 'Custom';
2735
+ return {
2736
+ name: this.name ??
2737
+ `${this.capitalize(this.themeStyle)} ${paletteLabel} ${this.capitalize(this.purpose)}`,
2738
+ description: `Generated ${this.themeStyle} ${paletteLabel.toLowerCase()} theme for ${this.purpose}`,
2739
+ responsiveName: 'generated',
2740
+ responsiveStandardWidth: this.responsiveStandardWidth ?? 1440,
2741
+ responsiveFontSizeMin: this.responsiveFontSizeMin ?? 80,
2742
+ responsiveFontSizeMax: this.responsiveFontSizeMax ?? 120,
2743
+ responsiveMarginMin: this.responsiveMarginMin ?? 80,
2744
+ responsiveMarginMax: this.responsiveMarginMax ?? 150,
2745
+ responsivePaddingMin: this.responsivePaddingMin ?? 80,
2746
+ responsivePaddingMax: this.responsivePaddingMax ?? 150,
2747
+ palette,
2748
+ typeStyles: this.buildTypography(style, purpose, semantics, textShadowCss),
2749
+ containers: this.buildContainers(style, purpose, palette, this.layout, elevationCss),
2750
+ buttonStyles: this.buildButtons(style, purpose, palette, semantics, elevationCss),
2751
+ accordions: this.buildAccordions(style, purpose, palette, this.layout, elevationCss),
2752
+ classes: this.buildClasses(),
2753
+ };
2754
+ }
2755
+ /**
2756
+ * Map a ShadowLevel to a box-shadow CSS string. Playful style uses an offset
2757
+ * "hard" shadow that varies in offset rather than blur, so it gets its own
2758
+ * scale.
2759
+ */
2760
+ elevationCss(level, style) {
2761
+ if (level === 'none')
2762
+ return 'none';
2763
+ if (style === 'playful') {
2764
+ return {
2765
+ small: '2px 2px 0 rgba(0,0,0,0.12)',
2766
+ medium: '4px 4px 0 rgba(0,0,0,0.15)',
2767
+ large: '6px 6px 0 rgba(0,0,0,0.18)',
2768
+ }[level];
2769
+ }
2770
+ return {
2771
+ small: '0 1px 3px rgba(0,0,0,0.10)',
2772
+ medium: '0 2px 8px rgba(0,0,0,0.15)',
2773
+ large: '0 6px 16px rgba(0,0,0,0.20)',
2774
+ }[level];
2775
+ }
2776
+ /** Map a ShadowLevel to a text-shadow CSS string. Empty disables. */
2777
+ textShadowCss(level) {
2778
+ if (level === 'none')
2779
+ return '';
2780
+ return {
2781
+ small: '1px 1px 2px rgba(0,0,0,0.15)',
2782
+ medium: '1px 1px 3px rgba(0,0,0,0.25)',
2783
+ large: '2px 2px 4px rgba(0,0,0,0.35)',
2784
+ }[level];
2785
+ }
2786
+ regenerate() {
2787
+ this.theme = this.generate();
2788
+ this.themeGenerated.emit(this.theme);
2789
+ }
2790
+ resolveSwatch(p) {
2791
+ if (typeof p.backgroundColor === 'string')
2792
+ return p.backgroundColor;
2793
+ if (p.backgroundColor?.colorHex)
2794
+ return p.backgroundColor.colorHex;
2795
+ if (typeof p.color === 'string')
2796
+ return p.color;
2797
+ return '#cccccc';
2798
+ }
2799
+ // ────────────────────────────────────────────────────────────────────────
2800
+ // Profiles
2801
+ // ────────────────────────────────────────────────────────────────────────
2802
+ styleProfileFor(style) {
2803
+ const profiles = {
2804
+ modern: {
2805
+ fontFamily: '"Inter", system-ui, sans-serif',
2806
+ monoFamily: '"JetBrains Mono", "Courier New", monospace',
2807
+ baseFontSize: 16,
2808
+ headingWeight: '600',
2809
+ bodyWeight: '400',
2810
+ borderRadius: '8px',
2811
+ border: 'none',
2812
+ },
2813
+ classic: {
2814
+ fontFamily: '"Georgia", "Times New Roman", serif',
2815
+ monoFamily: '"Courier New", monospace',
2816
+ baseFontSize: 17,
2817
+ headingWeight: '700',
2818
+ bodyWeight: '400',
2819
+ borderRadius: '2px',
2820
+ border: '1px solid rgba(0,0,0,0.15)',
2821
+ },
2822
+ minimal: {
2823
+ fontFamily: '"Helvetica Neue", Arial, sans-serif',
2824
+ monoFamily: '"SF Mono", "Menlo", monospace',
2825
+ baseFontSize: 15,
2826
+ headingWeight: '500',
2827
+ bodyWeight: '300',
2828
+ borderRadius: '0px',
2829
+ border: '1px solid rgba(0,0,0,0.08)',
2830
+ },
2831
+ bold: {
2832
+ fontFamily: '"Montserrat", system-ui, sans-serif',
2833
+ monoFamily: '"Roboto Mono", monospace',
2834
+ baseFontSize: 18,
2835
+ headingWeight: '800',
2836
+ bodyWeight: '500',
2837
+ borderRadius: '4px',
2838
+ border: '2px solid rgba(0,0,0,0.85)',
2839
+ },
2840
+ playful: {
2841
+ fontFamily: '"Comic Neue", "Quicksand", sans-serif',
2842
+ monoFamily: '"Comic Mono", "Courier New", monospace',
2843
+ baseFontSize: 16,
2844
+ headingWeight: '700',
2845
+ bodyWeight: '400',
2846
+ borderRadius: '18px',
2847
+ border: '2px dashed rgba(0,0,0,0.3)',
2848
+ },
2849
+ };
2850
+ return profiles[style];
2851
+ }
2852
+ purposeProfileFor(purpose) {
2853
+ const profiles = {
2854
+ marketing: {
2855
+ containerPadding: '40px 32px',
2856
+ containerMargin: '0px',
2857
+ accordionMode: 'collapsing',
2858
+ emphasizeHeadings: true,
2859
+ buttonPadding: '14px 16px',
2860
+ density: 'roomy',
2861
+ },
2862
+ dashboard: {
2863
+ containerPadding: '16px',
2864
+ containerMargin: '8px',
2865
+ accordionMode: 'open',
2866
+ emphasizeHeadings: false,
2867
+ buttonPadding: '8px 16px',
2868
+ density: 'tight',
2869
+ },
2870
+ documentation: {
2871
+ containerPadding: '24px 32px',
2872
+ containerMargin: '12px 0px',
2873
+ accordionMode: 'collapsing',
2874
+ emphasizeHeadings: true,
2875
+ buttonPadding: '10px 12px',
2876
+ density: 'normal',
2877
+ },
2878
+ form: {
2879
+ containerPadding: '20px',
2880
+ containerMargin: '8px 0px',
2881
+ accordionMode: 'focus',
2882
+ emphasizeHeadings: false,
2883
+ buttonPadding: '12px 16px',
2884
+ density: 'normal',
2885
+ },
2886
+ gallery: {
2887
+ containerPadding: '8px',
2888
+ containerMargin: '4px',
2889
+ accordionMode: 'focus',
2890
+ emphasizeHeadings: false,
2891
+ buttonPadding: '10px 12px',
2892
+ density: 'tight',
2893
+ },
2894
+ };
2895
+ return profiles[purpose];
2896
+ }
2897
+ semanticColors() {
2898
+ return {
2899
+ danger: { bg: '#c62828', fg: '#ffffff' },
2900
+ success: { bg: '#2e7d32', fg: '#ffffff' },
2901
+ warning: { bg: '#f9a825', fg: '#3e2723' },
2902
+ info: { bg: '#0288d1', fg: '#ffffff' },
2903
+ disabled: { bg: '#e0e0e0', fg: '#9e9e9e' },
2904
+ inverse: { bg: '#212121', fg: '#fafafa' },
2905
+ };
2906
+ }
2907
+ // ────────────────────────────────────────────────────────────────────────
2908
+ // Palette
2909
+ // ────────────────────────────────────────────────────────────────────────
2910
+ buildPalette(preset) {
2911
+ // Every palette defines 8 semantic roles that blend (Primary→Secondary→
2912
+ // Accent→Highlight) and contrast (Surface↔Inverse) within a unified
2913
+ // color story. Consumers can reference any role by colorName.
2914
+ const presets = {
2915
+ // ─────────────── Aesthetic presets ───────────────
2916
+ ocean: [
2917
+ { colorName: 'Primary', backgroundColor: '#0277bd', color: '#ffffff' },
2918
+ { colorName: 'Secondary', backgroundColor: '#4fc3f7', color: '#01579b' },
2919
+ { colorName: 'Accent', backgroundColor: '#80deea', color: '#004d40' },
2920
+ { colorName: 'Highlight', backgroundColor: '#00bcd4', color: '#003c43' },
2921
+ { colorName: 'Surface', backgroundColor: '#e1f5fe', color: '#01579b' },
2922
+ { colorName: 'Muted', backgroundColor: '#cfd8dc', color: '#37474f' },
2923
+ { colorName: 'Subtle', backgroundColor: '#eceff1', color: '#455a64' },
2924
+ { colorName: 'Inverse', backgroundColor: '#01579b', color: '#e1f5fe' },
2925
+ ],
2926
+ forest: [
2927
+ { colorName: 'Primary', backgroundColor: '#2e7d32', color: '#ffffff' },
2928
+ { colorName: 'Secondary', backgroundColor: '#81c784', color: '#1b5e20' },
2929
+ { colorName: 'Accent', backgroundColor: '#aed581', color: '#33691e' },
2930
+ { colorName: 'Highlight', backgroundColor: '#fff176', color: '#5d4037' },
2931
+ { colorName: 'Surface', backgroundColor: '#f1f8e9', color: '#33691e' },
2932
+ { colorName: 'Muted', backgroundColor: '#d7ccc8', color: '#3e2723' },
2933
+ { colorName: 'Subtle', backgroundColor: '#efebe9', color: '#5d4037' },
2934
+ { colorName: 'Inverse', backgroundColor: '#1b5e20', color: '#f1f8e9' },
2935
+ ],
2936
+ sunset: [
2937
+ { colorName: 'Primary', backgroundColor: '#ef6c00', color: '#ffffff' },
2938
+ { colorName: 'Secondary', backgroundColor: '#ffb74d', color: '#bf360c' },
2939
+ { colorName: 'Accent', backgroundColor: '#f48fb1', color: '#880e4f' },
2940
+ { colorName: 'Highlight', backgroundColor: '#ffd54f', color: '#5d4037' },
2941
+ { colorName: 'Surface', backgroundColor: '#fff3e0', color: '#bf360c' },
2942
+ { colorName: 'Muted', backgroundColor: '#ffe0b2', color: '#5d4037' },
2943
+ { colorName: 'Subtle', backgroundColor: '#fff8e1', color: '#6d4c41' },
2944
+ { colorName: 'Inverse', backgroundColor: '#bf360c', color: '#fff3e0' },
2945
+ ],
2946
+ monochrome: [
2947
+ { colorName: 'Primary', backgroundColor: '#212121', color: '#fafafa' },
2948
+ { colorName: 'Secondary', backgroundColor: '#616161', color: '#ffffff' },
2949
+ { colorName: 'Accent', backgroundColor: '#9e9e9e', color: '#212121' },
2950
+ { colorName: 'Highlight', backgroundColor: '#ffeb3b', color: '#212121' },
2951
+ { colorName: 'Surface', backgroundColor: '#fafafa', color: '#212121' },
2952
+ { colorName: 'Muted', backgroundColor: '#e0e0e0', color: '#424242' },
2953
+ { colorName: 'Subtle', backgroundColor: '#f5f5f5', color: '#616161' },
2954
+ { colorName: 'Inverse', backgroundColor: '#000000', color: '#fafafa' },
2955
+ ],
2956
+ pastel: [
2957
+ { colorName: 'Primary', backgroundColor: '#b39ddb', color: '#311b92' },
2958
+ { colorName: 'Secondary', backgroundColor: '#f8bbd0', color: '#880e4f' },
2959
+ { colorName: 'Accent', backgroundColor: '#b2ebf2', color: '#006064' },
2960
+ { colorName: 'Highlight', backgroundColor: '#c5e1a5', color: '#33691e' },
2961
+ { colorName: 'Surface', backgroundColor: '#fff9c4', color: '#f57f17' },
2962
+ { colorName: 'Muted', backgroundColor: '#dcedc8', color: '#33691e' },
2963
+ { colorName: 'Subtle', backgroundColor: '#fce4ec', color: '#ad1457' },
2964
+ { colorName: 'Inverse', backgroundColor: '#4527a0', color: '#ede7f6' },
2965
+ ],
2966
+ // ─────────────── Business presets ───────────────
2967
+ // Corporate — navy + steel, conservative trust signals
2968
+ corporate: [
2969
+ { colorName: 'Primary', backgroundColor: '#1a237e', color: '#ffffff' },
2970
+ { colorName: 'Secondary', backgroundColor: '#3949ab', color: '#ffffff' },
2971
+ { colorName: 'Accent', backgroundColor: '#d84315', color: '#ffffff' },
2972
+ { colorName: 'Highlight', backgroundColor: '#fff176', color: '#3e2723' },
2973
+ { colorName: 'Surface', backgroundColor: '#fafafa', color: '#1a237e' },
2974
+ { colorName: 'Muted', backgroundColor: '#eceff1', color: '#37474f' },
2975
+ { colorName: 'Subtle', backgroundColor: '#f5f5f5', color: '#424242' },
2976
+ { colorName: 'Inverse', backgroundColor: '#263238', color: '#eceff1' },
2977
+ ],
2978
+ // Healthcare — calm teals + soft whites, clinical clarity
2979
+ healthcare: [
2980
+ { colorName: 'Primary', backgroundColor: '#00695c', color: '#ffffff' },
2981
+ { colorName: 'Secondary', backgroundColor: '#00897b', color: '#ffffff' },
2982
+ { colorName: 'Accent', backgroundColor: '#e91e63', color: '#ffffff' },
2983
+ { colorName: 'Highlight', backgroundColor: '#b2ebf2', color: '#006064' },
2984
+ { colorName: 'Surface', backgroundColor: '#ffffff', color: '#263238' },
2985
+ { colorName: 'Muted', backgroundColor: '#e0f2f1', color: '#004d40' },
2986
+ { colorName: 'Subtle', backgroundColor: '#f1f8e9', color: '#33691e' },
2987
+ { colorName: 'Inverse', backgroundColor: '#004d40', color: '#e0f2f1' },
2988
+ ],
2989
+ // Finance — deep green + gold, money & gravitas
2990
+ finance: [
2991
+ { colorName: 'Primary', backgroundColor: '#1b5e20', color: '#ffffff' },
2992
+ { colorName: 'Secondary', backgroundColor: '#2e7d32', color: '#ffffff' },
2993
+ { colorName: 'Accent', backgroundColor: '#ff8f00', color: '#1b1b1b' },
2994
+ { colorName: 'Highlight', backgroundColor: '#ffe082', color: '#5d4037' },
2995
+ { colorName: 'Surface', backgroundColor: '#fafafa', color: '#1b1b1b' },
2996
+ { colorName: 'Muted', backgroundColor: '#e8f5e9', color: '#1b5e20' },
2997
+ { colorName: 'Subtle', backgroundColor: '#f1f8e9', color: '#33691e' },
2998
+ { colorName: 'Inverse', backgroundColor: '#1b1b1b', color: '#c8e6c9' },
2999
+ ],
3000
+ // Tech / SaaS — violet + cyan, modern product feel
3001
+ tech: [
3002
+ { colorName: 'Primary', backgroundColor: '#4527a0', color: '#ffffff' },
3003
+ { colorName: 'Secondary', backgroundColor: '#7e57c2', color: '#ffffff' },
3004
+ { colorName: 'Accent', backgroundColor: '#00e5ff', color: '#00363a' },
3005
+ { colorName: 'Highlight', backgroundColor: '#c4b5fd', color: '#311b92' },
3006
+ { colorName: 'Surface', backgroundColor: '#fafafa', color: '#311b92' },
3007
+ { colorName: 'Muted', backgroundColor: '#ede7f6', color: '#311b92' },
3008
+ { colorName: 'Subtle', backgroundColor: '#f3e5f5', color: '#4a148c' },
3009
+ { colorName: 'Inverse', backgroundColor: '#1a1a2e', color: '#ede7f6' },
3010
+ ],
3011
+ // Legal — burgundy + cream, traditional and authoritative
3012
+ legal: [
3013
+ { colorName: 'Primary', backgroundColor: '#4a0e0e', color: '#fff8e1' },
3014
+ { colorName: 'Secondary', backgroundColor: '#5d4037', color: '#fff8e1' },
3015
+ { colorName: 'Accent', backgroundColor: '#b71c1c', color: '#ffecb3' },
3016
+ { colorName: 'Highlight', backgroundColor: '#ffd54f', color: '#3e2723' },
3017
+ { colorName: 'Surface', backgroundColor: '#fff8e1', color: '#3e2723' },
3018
+ { colorName: 'Muted', backgroundColor: '#efebe9', color: '#4e342e' },
3019
+ { colorName: 'Subtle', backgroundColor: '#f5f5f5', color: '#3e2723' },
3020
+ { colorName: 'Inverse', backgroundColor: '#3e2723', color: '#efebe9' },
3021
+ ],
3022
+ // Hospitality — warm coral + cream, welcoming and indulgent
3023
+ hospitality: [
3024
+ { colorName: 'Primary', backgroundColor: '#d84315', color: '#fff8e1' },
3025
+ { colorName: 'Secondary', backgroundColor: '#ff7043', color: '#fff8e1' },
3026
+ { colorName: 'Accent', backgroundColor: '#c2185b', color: '#ffffff' },
3027
+ { colorName: 'Highlight', backgroundColor: '#ffd180', color: '#4e342e' },
3028
+ { colorName: 'Surface', backgroundColor: '#fff8e1', color: '#3e2723' },
3029
+ { colorName: 'Muted', backgroundColor: '#fbe9e7', color: '#bf360c' },
3030
+ { colorName: 'Subtle', backgroundColor: '#fff3e0', color: '#5d4037' },
3031
+ { colorName: 'Inverse', backgroundColor: '#3e2723', color: '#fff8e1' },
3032
+ ],
3033
+ // Retail — vibrant + friendly, attention-grabbing pops
3034
+ retail: [
3035
+ { colorName: 'Primary', backgroundColor: '#d32f2f', color: '#ffffff' },
3036
+ { colorName: 'Secondary', backgroundColor: '#f57c00', color: '#ffffff' },
3037
+ { colorName: 'Accent', backgroundColor: '#00897b', color: '#ffffff' },
3038
+ { colorName: 'Highlight', backgroundColor: '#ffeb3b', color: '#5d4037' },
3039
+ { colorName: 'Surface', backgroundColor: '#fafafa', color: '#212121' },
3040
+ { colorName: 'Muted', backgroundColor: '#f5f5f5', color: '#424242' },
3041
+ { colorName: 'Subtle', backgroundColor: '#fffde7', color: '#5d4037' },
3042
+ { colorName: 'Inverse', backgroundColor: '#212121', color: '#ffeb3b' },
3043
+ ],
3044
+ // Education — friendly blue + warm accents, approachable
3045
+ education: [
3046
+ { colorName: 'Primary', backgroundColor: '#1976d2', color: '#ffffff' },
3047
+ { colorName: 'Secondary', backgroundColor: '#42a5f5', color: '#ffffff' },
3048
+ { colorName: 'Accent', backgroundColor: '#ff7043', color: '#ffffff' },
3049
+ { colorName: 'Highlight', backgroundColor: '#ffd54f', color: '#5d4037' },
3050
+ { colorName: 'Surface', backgroundColor: '#ffffff', color: '#1565c0' },
3051
+ { colorName: 'Muted', backgroundColor: '#e3f2fd', color: '#0d47a1' },
3052
+ { colorName: 'Subtle', backgroundColor: '#f5f5f5', color: '#424242' },
3053
+ { colorName: 'Inverse', backgroundColor: '#0d47a1', color: '#e3f2fd' },
3054
+ ],
3055
+ };
3056
+ return presets[preset];
3057
+ }
3058
+ bgOf(palette, name) {
3059
+ const p = palette.find(x => x.colorName === name) ?? palette[0];
3060
+ if (typeof p.backgroundColor === 'string')
3061
+ return p.backgroundColor;
3062
+ return p.backgroundColor?.colorHex ?? '#cccccc';
3063
+ }
3064
+ fgOf(palette, name) {
3065
+ const p = palette.find(x => x.colorName === name) ?? palette[0];
3066
+ if (typeof p.color === 'string')
3067
+ return p.color;
3068
+ return p.color?.colorHex ?? '#222222';
3069
+ }
3070
+ // ────────────────────────────────────────────────────────────────────────
3071
+ // Typography — H1-H5, title, subtitle, body, label, user, display, Focus, white, Jade
3072
+ // ────────────────────────────────────────────────────────────────────────
3073
+ buildTypography(style, purpose, semantics, textShadowCss) {
3074
+ const base = style.baseFontSize;
3075
+ const boost = purpose.emphasizeHeadings ? 1.15 : 1.0;
3076
+ const heading = (sizePx) => ({
3077
+ typeFont: style.fontFamily,
3078
+ typeFontSize: `${Math.round(sizePx * boost)}px`,
3079
+ typeFontWeight: style.headingWeight,
3080
+ typeLineHeight: '1.3',
3081
+ typeTextShadow: textShadowCss,
3082
+ });
3083
+ return [
3084
+ { typeName: 'H1', ...heading(base * 2.6), typePadding: '6px 4px' },
3085
+ { typeName: 'H2', ...heading(base * 2.1), typePadding: '6px 4px' },
3086
+ { typeName: 'H3', ...heading(base * 1.75) },
3087
+ { typeName: 'H4', ...heading(base * 1.4) },
3088
+ { typeName: 'H5', ...heading(base * 1.2), typeFontAlignment: 'center' },
3089
+ {
3090
+ typeName: 'title',
3091
+ typeFont: style.fontFamily,
3092
+ typeFontSize: `${Math.round(base * 1.55)}px`,
3093
+ typeFontWeight: style.headingWeight,
3094
+ typeFontAlignment: 'center',
3095
+ typeLineHeight: '1.3',
3096
+ typePadding: '2px 4px',
3097
+ typeMargin: '2px 4px',
3098
+ typeTextShadow: textShadowCss,
3099
+ },
3100
+ {
3101
+ typeName: 'subtitle',
3102
+ typeFont: style.fontFamily,
3103
+ typeFontSize: `${Math.round(base * 1.1)}px`,
3104
+ typeFontWeight: '500',
3105
+ typeLineHeight: '1.4',
3106
+ typePadding: '4px',
3107
+ typeTextShadow: textShadowCss,
3108
+ },
3109
+ {
3110
+ typeName: 'body',
3111
+ typeFont: style.fontFamily,
3112
+ typeFontSize: `${base}px`,
3113
+ typeFontWeight: style.bodyWeight,
3114
+ typeLineHeight: '1.6',
3115
+ },
3116
+ {
3117
+ typeName: 'label',
3118
+ typeFont: style.fontFamily,
3119
+ typeFontSize: `${Math.round(base * 0.9)}px`,
3120
+ typeFontWeight: '500',
3121
+ typeFontAlignment: 'center',
3122
+ typeLineHeight: '1.3',
3123
+ },
3124
+ {
3125
+ typeName: 'user',
3126
+ typeFont: style.fontFamily,
3127
+ typeFontSize: `${Math.round(base * 1.05)}px`,
3128
+ typeFontWeight: '600',
3129
+ typePadding: '4px',
3130
+ typeMargin: '4px',
3131
+ typeFontColor: '#303f9f',
3132
+ },
3133
+ {
3134
+ typeName: 'display',
3135
+ typeFont: style.monoFamily,
3136
+ typeFontSize: `${Math.round(base * 0.9)}px`,
3137
+ typeFontWeight: '900',
3138
+ typeLineHeight: '1.4',
3139
+ typePadding: '6px 4px',
3140
+ },
3141
+ {
3142
+ typeName: 'Focus',
3143
+ typeFont: style.fontFamily,
3144
+ typeFontSize: `${Math.round(base * 1.65)}px`,
3145
+ typeFontWeight: '600',
3146
+ typePalette: 'Accent',
3147
+ typeBorder: `2px solid ${semantics.danger.bg}`,
3148
+ typePadding: '8px',
3149
+ typeMargin: '12px',
3150
+ typeTextShadow: textShadowCss,
3151
+ },
3152
+ {
3153
+ typeName: 'white',
3154
+ typeFont: style.fontFamily,
3155
+ typeFontSize: `${Math.round(base * 2.2)}px`,
3156
+ typeFontWeight: '700',
3157
+ typeFontColor: '#ffffff',
3158
+ typeBackground: semantics.inverse.bg,
3159
+ typePadding: '6px 4px',
3160
+ typeTextShadow: textShadowCss,
3161
+ },
3162
+ {
3163
+ typeName: 'Jade',
3164
+ typeFont: style.monoFamily,
3165
+ typeFontSize: `${Math.round(base * 1.2)}px`,
3166
+ typeFontWeight: '600',
3167
+ typeLineHeight: '1.5',
3168
+ typeFontColor: '#1259c8',
3169
+ typePadding: '4px',
3170
+ typeMargin: '4px',
3171
+ },
3172
+ ];
3173
+ }
3174
+ // ────────────────────────────────────────────────────────────────────────
3175
+ // Containers — Page Structure: Page, Section, Hero, Header, Footer,
3176
+ // Sidebar, Toolbar | Cards & Panels: Card, Panel, Modal | Content Types:
3177
+ // Data Entry, Article, Feed, Table, Gallery, Dashboard | Width Variants: Narrow,
3178
+ // Half, Wide, Full Width, Full Screen | Functional: Media, Sticky,
3179
+ // Notification, Inactive, Callout
3180
+ // ────────────────────────────────────────────────────────────────────────
3181
+ buildContainers(style, purpose, palette, layout, elevationCss) {
3182
+ const surfaceBg = this.bgOf(palette, 'Surface');
3183
+ const mutedBg = this.bgOf(palette, 'Muted');
3184
+ const primaryBg = this.bgOf(palette, 'Primary');
3185
+ const accentBg = this.bgOf(palette, 'Accent');
3186
+ const tightPad = purpose.density === 'tight' ? '4px' : '6px';
3187
+ const contentPad = purpose.density === 'tight' ? '12px' : purpose.density === 'roomy' ? '32px' : '20px';
3188
+ const radius = style.borderRadius;
3189
+ return [
3190
+ // ── Page Structure ────────────────────────────────────────────────
3191
+ {
3192
+ containerName: 'Page',
3193
+ containerPalette: 'Surface',
3194
+ containerLayout: 'column',
3195
+ containerAlignment: 'center',
3196
+ containerWidth: '100vw',
3197
+ containerHeight: 'auto',
3198
+ containerPadding: '0px',
3199
+ containerMargin: '0px',
3200
+ containerBorder: 'none',
3201
+ containerBoxShadow: '',
3202
+ containerPosition: 'relative',
3203
+ },
3204
+ {
3205
+ containerName: 'Section',
3206
+ containerPalette: 'Surface',
3207
+ containerLayout: 'column',
3208
+ containerAlignment: 'center',
3209
+ containerWidth: '100vw',
3210
+ containerPadding: `${contentPad} 0px`,
3211
+ containerMargin: '0px',
3212
+ containerBorder: 'none',
3213
+ containerBoxShadow: '',
3214
+ containerPosition: 'relative',
3215
+ },
3216
+ {
3217
+ containerName: 'Hero',
3218
+ containerPalette: 'Accent',
3219
+ containerLayout: 'column',
3220
+ containerAlignment: 'center',
3221
+ containerWidth: '100vw',
3222
+ containerPadding: '64px 32px',
3223
+ containerMargin: '0px',
3224
+ containerBorder: 'none',
3225
+ containerBoxShadow: '',
3226
+ containerPosition: 'relative',
3227
+ },
3228
+ {
3229
+ containerName: 'Header',
3230
+ containerPalette: 'Primary',
3231
+ containerLayout: 'row',
3232
+ containerAlignment: 'center',
3233
+ containerWidth: '100vw',
3234
+ containerHeight: 'auto',
3235
+ containerPadding: '8px 16px',
3236
+ containerMargin: '0px',
3237
+ containerBorder: 'none',
3238
+ containerBoxShadow: elevationCss,
3239
+ containerPosition: 'sticky',
3240
+ containerPositioning: 'top: 0; z-index: 100;',
3241
+ },
3242
+ {
3243
+ containerName: 'Footer',
3244
+ containerPalette: 'Muted',
3245
+ containerLayout: 'column',
3246
+ containerAlignment: 'center',
3247
+ containerWidth: '100vw',
3248
+ containerPadding: `${contentPad} 16px`,
3249
+ containerMargin: '0px',
3250
+ containerBorder: 'none',
3251
+ containerBoxShadow: '',
3252
+ containerPosition: 'relative',
3253
+ },
3254
+ {
3255
+ containerName: 'Sidebar',
3256
+ containerPalette: 'Surface',
3257
+ containerLayout: 'column',
3258
+ containerAlignment: 'center',
3259
+ containerWidth: '25vw',
3260
+ containerHeight: '100vh',
3261
+ containerPadding: '12px',
3262
+ containerMargin: '0px',
3263
+ containerBorder: style.border === 'none' ? '1px solid rgba(0,0,0,0.1)' : style.border,
3264
+ containerBoxShadow: elevationCss,
3265
+ containerOverflowY: 'auto',
3266
+ containerPosition: 'relative',
3267
+ },
3268
+ {
3269
+ containerName: 'Toolbar',
3270
+ containerPalette: 'Primary',
3271
+ containerLayout: 'row',
3272
+ containerAlignment: 'center',
3273
+ containerWidth: '100vw',
3274
+ containerHeight: 'auto',
3275
+ containerPadding: '4px 12px',
3276
+ containerMargin: '0px',
3277
+ containerBorder: 'none',
3278
+ containerBoxShadow: elevationCss,
3279
+ containerPosition: 'relative',
3280
+ },
3281
+ // ── Cards & Panels ────────────────────────────────────────────────
3282
+ {
3283
+ containerName: 'Card',
3284
+ containerPalette: 'Surface',
3285
+ containerLayout: layout,
3286
+ containerAlignment: 'center',
3287
+ containerPadding: '16px',
3288
+ containerMargin: '8px',
3289
+ containerBorder: style.border,
3290
+ containerBoxShadow: elevationCss,
3291
+ containerPosition: 'relative',
3292
+ containerStyle: `border-radius: ${radius};`,
3293
+ },
3294
+ {
3295
+ containerName: 'Panel',
3296
+ containerPalette: 'Surface',
3297
+ containerLayout: 'column',
3298
+ containerAlignment: 'center',
3299
+ containerWidth: '33vw',
3300
+ containerHeight: '80vh',
3301
+ containerPadding: tightPad,
3302
+ containerMargin: '6px',
3303
+ containerBorder: style.border === 'none' ? '1px solid rgba(0,0,0,0.1)' : style.border,
3304
+ containerBoxShadow: elevationCss,
3305
+ containerLineHeight: '150%',
3306
+ containerPosition: 'relative',
3307
+ containerStyle: `border-radius: ${radius};`,
3308
+ },
3309
+ {
3310
+ containerName: 'Modal',
3311
+ containerPalette: 'Surface',
3312
+ containerLayout: 'column',
3313
+ containerAlignment: 'center',
3314
+ containerWidth: '480px',
3315
+ containerHeight: 'auto',
3316
+ containerPadding: '24px',
3317
+ containerMargin: '0px auto',
3318
+ containerBorder: style.border,
3319
+ containerBoxShadow: elevationCss || '0 8px 32px rgba(0,0,0,0.2)',
3320
+ containerPosition: 'relative',
3321
+ containerStyle: `border-radius: ${radius};`,
3322
+ },
3323
+ // ── Content Types ─────────────────────────────────────────────────
3324
+ {
3325
+ containerName: 'Data Entry',
3326
+ containerPalette: 'Surface',
3327
+ containerLayout: layout,
3328
+ containerAlignment: 'center',
3329
+ containerPadding: '16px 12px',
3330
+ containerMargin: '12px 6px',
3331
+ containerBorder: style.border,
3332
+ containerBoxShadow: elevationCss,
3333
+ containerPosition: 'relative',
3334
+ containerStyle: `border-radius: ${radius};`,
3335
+ },
3336
+ {
3337
+ containerName: 'Article',
3338
+ containerPalette: 'Surface',
3339
+ containerLayout: 'column',
3340
+ containerAlignment: 'start',
3341
+ containerPadding: contentPad,
3342
+ containerMargin: '0px auto',
3343
+ containerBorder: 'none',
3344
+ containerBoxShadow: '',
3345
+ containerLineHeight: '160%',
3346
+ containerPosition: 'relative',
3347
+ },
3348
+ {
3349
+ containerName: 'Feed',
3350
+ containerPalette: 'Surface',
3351
+ containerLayout: 'column',
3352
+ containerAlignment: 'center',
3353
+ containerWidth: '100%',
3354
+ containerHeight: '100%',
3355
+ containerOverflowX: 'hidden',
3356
+ containerOverflowY: 'auto',
3357
+ containerPadding: tightPad,
3358
+ containerBorder: 'none',
3359
+ containerPosition: 'relative',
3360
+ },
3361
+ {
3362
+ containerName: 'Table',
3363
+ containerPalette: 'Surface',
3364
+ containerLayout: 'column',
3365
+ containerHeight: 'auto',
3366
+ containerOverflowX: 'scroll',
3367
+ containerOverflowY: 'scroll',
3368
+ containerBorder: style.border,
3369
+ containerMargin: '0px',
3370
+ containerPadding: '0px',
3371
+ containerPosition: 'relative',
3372
+ },
3373
+ {
3374
+ containerName: 'Gallery',
3375
+ containerPalette: 'Muted',
3376
+ containerLayout: 'row wrap',
3377
+ containerAlignment: 'center',
3378
+ containerWidth: '100vw',
3379
+ containerPadding: tightPad,
3380
+ containerMargin: '0px',
3381
+ containerBorder: 'none',
3382
+ containerBoxShadow: '',
3383
+ containerPosition: 'relative',
3384
+ },
3385
+ {
3386
+ containerName: 'Dashboard',
3387
+ containerPalette: 'Surface',
3388
+ containerLayout: 'row wrap',
3389
+ containerAlignment: 'center',
3390
+ containerWidth: '100%',
3391
+ containerPadding: contentPad,
3392
+ containerMargin: '0px',
3393
+ containerBorder: 'none',
3394
+ containerBoxShadow: '',
3395
+ containerPosition: 'relative',
3396
+ },
3397
+ // ── Width Variants ────────────────────────────────────────────────
3398
+ {
3399
+ containerName: 'Narrow',
3400
+ containerPalette: 'Surface',
3401
+ containerLayout: layout,
3402
+ containerAlignment: 'center',
3403
+ containerWidth: '33%',
3404
+ containerPadding: '0px',
3405
+ containerMargin: '0px',
3406
+ containerBorder: style.border,
3407
+ containerBoxShadow: '',
3408
+ },
3409
+ {
3410
+ containerName: 'Half',
3411
+ containerPalette: 'Surface',
3412
+ containerLayout: layout,
3413
+ containerAlignment: 'center',
3414
+ containerWidth: '50%',
3415
+ containerPadding: '15px',
3416
+ containerBorder: style.border,
3417
+ },
3418
+ {
3419
+ containerName: 'Wide',
3420
+ containerPalette: 'Surface',
3421
+ containerLayout: layout,
3422
+ containerAlignment: 'center',
3423
+ containerWidth: '66%',
3424
+ containerPadding: '0px',
3425
+ containerMargin: '0px',
3426
+ containerBorder: style.border,
3427
+ },
3428
+ {
3429
+ containerName: 'Full Width',
3430
+ containerBackground: surfaceBg,
3431
+ containerLayout: layout,
3432
+ containerAlignment: 'center',
3433
+ containerWidth: '100vw',
3434
+ containerOverflowX: 'scroll',
3435
+ containerOverflowY: 'scroll',
3436
+ containerPadding: '0px',
3437
+ containerMargin: '0px',
3438
+ containerBorder: 'none',
3439
+ containerBoxShadow: '',
3440
+ containerPosition: 'relative',
3441
+ },
3442
+ {
3443
+ containerName: 'Full Screen',
3444
+ containerPalette: 'Surface',
3445
+ containerLayout: 'column',
3446
+ containerWidth: '100vw',
3447
+ containerHeight: '100vh',
3448
+ containerPadding: '0px',
3449
+ containerMargin: '0px',
3450
+ containerBorder: 'none',
3451
+ containerBoxShadow: '',
3452
+ containerPosition: 'relative',
3453
+ },
3454
+ // ── Functional / State ────────────────────────────────────────────
3455
+ {
3456
+ containerName: 'Media',
3457
+ containerPalette: 'Muted',
3458
+ containerLayout: layout,
3459
+ containerAlignment: 'center',
3460
+ containerWidth: '25vw',
3461
+ containerPadding: '0px',
3462
+ containerMargin: '6px',
3463
+ containerBorder: 'none',
3464
+ containerBoxShadow: elevationCss,
3465
+ containerPosition: 'relative',
3466
+ containerStyle: `border-radius: ${radius};`,
3467
+ },
3468
+ {
3469
+ containerName: 'Sticky',
3470
+ containerBackground: `${mutedBg}55`,
3471
+ containerLayout: layout,
3472
+ containerAlignment: 'center',
3473
+ containerHeight: 'auto',
3474
+ containerPadding: '6px',
3475
+ containerMargin: '6px',
3476
+ containerBorder: 'none',
3477
+ containerPosition: 'sticky',
3478
+ containerPositioning: 'top: 0; z-index: 50;',
3479
+ },
3480
+ {
3481
+ containerName: 'Notification',
3482
+ containerBackground: accentBg,
3483
+ containerLayout: 'column',
3484
+ containerBorder: '1px solid white',
3485
+ containerMargin: '12px 0px',
3486
+ containerPadding: '12px',
3487
+ containerLineHeight: '150%',
3488
+ containerStyle: `border-radius: ${radius};`,
3489
+ },
3490
+ {
3491
+ containerName: 'Report',
3492
+ // containerBackground: accentBg,
3493
+ containerLayout: 'column',
3494
+ containerBorder: '2px solid gray',
3495
+ // containerMargin: '12px 0px',
3496
+ containerPadding: '8px',
3497
+ // containerLineHeight: '150%',
3498
+ // containerStyle: `border-radius: ${radius};`,
3499
+ },
3500
+ {
3501
+ containerName: 'Inactive',
3502
+ containerPalette: 'Muted',
3503
+ containerPadding: tightPad,
3504
+ },
3505
+ {
3506
+ containerName: 'Callout',
3507
+ containerPalette: 'Primary',
3508
+ containerLayout: 'column',
3509
+ containerAlignment: 'center',
3510
+ containerPadding: contentPad,
3511
+ containerMargin: '12px 0px',
3512
+ containerBorder: `2px solid ${primaryBg}`,
3513
+ containerBoxShadow: '',
3514
+ containerStyle: `border-radius: ${radius};`,
3515
+ },
3516
+ ];
3517
+ }
3518
+ // ────────────────────────────────────────────────────────────────────────
3519
+ // Buttons — Default Style, Item Select, Create, Delete, Disabled, Highlight,
3520
+ // Function, Edit, Action, cell, Logic, Jade, Cancel
3521
+ // ────────────────────────────────────────────────────────────────────────
3522
+ buildButtons(style, purpose, palette, semantics, elevationCss) {
3523
+ const padding = purpose.buttonPadding;
3524
+ const radius = style.borderRadius;
3525
+ const shadow = elevationCss;
3526
+ const primaryBg = this.bgOf(palette, 'Primary');
3527
+ const primaryFg = this.fgOf(palette, 'Primary');
3528
+ const secondaryBg = this.bgOf(palette, 'Secondary');
3529
+ const secondaryFg = this.fgOf(palette, 'Secondary');
3530
+ const accentBg = this.bgOf(palette, 'Accent');
3531
+ const accentFg = this.fgOf(palette, 'Accent');
3532
+ const mutedBg = this.bgOf(palette, 'Muted');
3533
+ return [
3534
+ {
3535
+ buttonName: 'Default Style',
3536
+ buttonBackground: primaryBg,
3537
+ buttonColor: primaryFg,
3538
+ buttonPadding: padding,
3539
+ buttonMargin: '6px',
3540
+ buttonRadius: radius,
3541
+ buttonBorder: style.border === 'none' ? 'none' : style.border,
3542
+ buttonShadow: shadow,
3543
+ buttonTypeStyle: 'label',
3544
+ },
3545
+ {
3546
+ buttonName: 'Item Select',
3547
+ buttonBackground: secondaryBg,
3548
+ buttonColor: secondaryFg,
3549
+ buttonPadding: padding,
3550
+ buttonMargin: '0px',
3551
+ buttonRadius: '0px',
3552
+ buttonBorder: '1px solid rgba(0,0,0,0.25)',
3553
+ buttonShadow: 'none',
3554
+ buttonTypeStyle: 'label',
3555
+ },
3556
+ {
3557
+ buttonName: 'Create',
3558
+ buttonBackground: semantics.success.bg,
3559
+ buttonColor: semantics.success.fg,
3560
+ buttonPadding: padding,
3561
+ buttonMargin: '6px',
3562
+ buttonRadius: radius,
3563
+ buttonBorder: 'none',
3564
+ buttonShadow: shadow,
3565
+ buttonTypeStyle: 'label',
3566
+ },
3567
+ {
3568
+ buttonName: 'Delete',
3569
+ buttonBackground: semantics.danger.bg,
3570
+ buttonColor: semantics.danger.fg,
3571
+ buttonPadding: padding,
3572
+ buttonMargin: '8px',
3573
+ buttonRadius: radius,
3574
+ buttonBorder: 'none',
3575
+ buttonShadow: shadow,
3576
+ buttonTypeStyle: 'label',
3577
+ },
3578
+ {
3579
+ buttonName: 'Disabled',
3580
+ buttonBackground: semantics.disabled.bg,
3581
+ buttonColor: semantics.disabled.fg,
3582
+ buttonPadding: padding,
3583
+ buttonMargin: '8px',
3584
+ buttonRadius: '0px',
3585
+ buttonBorder: 'none',
3586
+ buttonShadow: 'none',
3587
+ buttonTypeStyle: 'label',
3588
+ },
3589
+ {
3590
+ buttonName: 'Highlight',
3591
+ buttonPalette: 'Highlight',
3592
+ buttonPadding: padding,
3593
+ buttonMargin: '8px',
3594
+ buttonRadius: radius,
3595
+ buttonBorder: 'none',
3596
+ buttonShadow: shadow,
3597
+ buttonTypeStyle: 'label',
3598
+ },
3599
+ {
3600
+ buttonName: 'Function',
3601
+ buttonBackground: semantics.info.bg,
3602
+ buttonColor: semantics.info.fg,
3603
+ buttonPadding: padding,
3604
+ buttonMargin: '8px',
3605
+ buttonRadius: radius,
3606
+ buttonBorder: 'none',
3607
+ buttonShadow: shadow,
3608
+ buttonTypeStyle: 'label',
3609
+ },
3610
+ {
3611
+ buttonName: 'Edit',
3612
+ buttonBackground: accentBg,
3613
+ buttonColor: accentFg,
3614
+ buttonPadding: padding,
3615
+ buttonMargin: '8px',
3616
+ buttonRadius: radius,
3617
+ buttonBorder: 'none',
3618
+ buttonShadow: shadow,
3619
+ buttonTypeStyle: 'label',
3620
+ },
3621
+ {
3622
+ buttonName: 'Action',
3623
+ buttonBackground: primaryBg,
3624
+ buttonColor: primaryFg,
3625
+ buttonPadding: '8px 6px 8px 16px',
3626
+ buttonMargin: '8px',
3627
+ buttonRadius: `0px ${radius} ${radius} 0px`,
3628
+ buttonBorder: 'none',
3629
+ buttonShadow: shadow,
3630
+ buttonTypeStyle: 'label',
3631
+ },
3632
+ {
3633
+ buttonName: 'cell',
3634
+ buttonBackground: this.bgOf(palette, 'Surface'),
3635
+ buttonColor: this.fgOf(palette, 'Surface'),
3636
+ buttonPadding: '0px',
3637
+ buttonMargin: '0px',
3638
+ buttonBorder: '1px solid rgba(0,0,0,0.2)',
3639
+ buttonShadow: 'none',
3640
+ buttonRadius: '0px',
3641
+ buttonTypeStyle: 'label',
3642
+ },
3643
+ {
3644
+ buttonName: 'Logic',
3645
+ buttonBackground: mutedBg,
3646
+ buttonColor: primaryBg,
3647
+ buttonPadding: padding,
3648
+ buttonMargin: '6px',
3649
+ buttonBorder: '2px solid rgba(0,0,0,0.7)',
3650
+ buttonRadius: radius,
3651
+ buttonTypeStyle: 'subtitle',
3652
+ },
3653
+ {
3654
+ buttonName: 'Jade',
3655
+ buttonBackground: this.bgOf(palette, 'Surface'),
3656
+ buttonColor: '#1259c8',
3657
+ buttonPadding: padding,
3658
+ buttonMargin: '3px',
3659
+ buttonRadius: radius,
3660
+ buttonTypeStyle: 'Jade',
3661
+ },
3662
+ {
3663
+ buttonName: 'Cancel',
3664
+ buttonBackground: mutedBg,
3665
+ buttonColor: this.fgOf(palette, 'Muted'),
3666
+ buttonPadding: padding,
3667
+ buttonMargin: '6px',
3668
+ buttonRadius: radius,
3669
+ buttonBorder: 'none',
3670
+ buttonShadow: 'none',
3671
+ buttonTypeStyle: 'label',
3672
+ },
3673
+ ];
3674
+ }
3675
+ // ────────────────────────────────────────────────────────────────────────
3676
+ // Accordions — base, Level 1-4, Minimal, Settings, Data row 1-4, Object,
3677
+ // Media Details, Docs, Create, TeamChat, Style, Share, UI, Chat Bots,
3678
+ // Team, Sheet, App, Report
3679
+ // ────────────────────────────────────────────────────────────────────────
3680
+ buildAccordions(style, purpose, palette, layout, elevationCss) {
3681
+ const headerPad = purpose.accordionMode === 'open' ? '8px 12px' : '12px 16px';
3682
+ const tight = purpose.density === 'tight';
3683
+ // Hierarchy palette walk: Primary → Secondary → Accent → Surface
3684
+ const hierarchy = ['Primary', 'Secondary', 'Accent', 'Surface'];
3685
+ const make = (name, overrides = {}) => ({
3686
+ accordionName: name,
3687
+ accordionPalette: 'Surface',
3688
+ accordionLayout: layout,
3689
+ accordionAlignment: 'stretch',
3690
+ accordionPadding: tight ? '4px' : '8px',
3691
+ accordionMargin: '0px',
3692
+ accordionBorder: style.border,
3693
+ accordionBoxShadow: elevationCss,
3694
+ accordionExtra: 'max-width: 100vw; box-sizing: border-box;',
3695
+ headerOpenPalette: 'Primary',
3696
+ headerOpenType: 'H4',
3697
+ headerOpenLayout: 'row wrap',
3698
+ headerOpenAlignment: 'space-between',
3699
+ headerOpenPadding: headerPad,
3700
+ headerOpenMargin: '0px',
3701
+ headerOpenBorder: 'none',
3702
+ headerClosedPalette: 'Surface',
3703
+ headerClosedType: 'H4',
3704
+ headerClosedLayout: 'row wrap',
3705
+ headerClosedAlignment: 'space-between',
3706
+ headerClosedPadding: headerPad,
3707
+ headerClosedMargin: tight ? '4px' : '6px',
3708
+ headerClosedBorder: style.border,
3709
+ headerClosedBoxShadow: elevationCss,
3710
+ ...overrides,
3711
+ });
3712
+ const primaryBg = this.bgOf(palette, 'Primary');
3713
+ return [
3714
+ make('base'),
3715
+ // Hierarchical levels — palette walks deeper at each level
3716
+ make('Level 1', { accordionPalette: hierarchy[0], headerOpenPalette: hierarchy[0], headerClosedPalette: hierarchy[0] }),
3717
+ make('Level 2', { accordionPalette: hierarchy[1], headerOpenPalette: hierarchy[1], headerClosedPalette: hierarchy[1], headerClosedMargin: '4px 0px 4px 12px' }),
3718
+ make('Level 3', { accordionPalette: hierarchy[2], headerOpenPalette: hierarchy[2], headerClosedPalette: hierarchy[2], headerClosedMargin: '4px 0px 4px 24px' }),
3719
+ make('Level 4', { accordionPalette: hierarchy[3], headerOpenPalette: hierarchy[3], headerClosedPalette: hierarchy[3], headerClosedMargin: '4px 0px 4px 36px' }),
3720
+ // Minimal — compact, no shadow
3721
+ make('Minimal', {
3722
+ accordionPadding: '0px',
3723
+ accordionMargin: '0px',
3724
+ accordionBorder: 'none',
3725
+ accordionBoxShadow: 'none',
3726
+ headerOpenType: 'label',
3727
+ headerClosedType: 'label',
3728
+ headerOpenPadding: '4px 8px',
3729
+ headerClosedPadding: '4px 8px',
3730
+ headerClosedBoxShadow: 'none',
3731
+ }),
3732
+ // Settings — full-width
3733
+ make('Settings', {
3734
+ accordionPalette: 'Secondary',
3735
+ accordionWidth: '100vw',
3736
+ accordionPadding: '0px',
3737
+ accordionMargin: '0px',
3738
+ accordionAlignment: "center",
3739
+ headerOpenWidth: '100vw',
3740
+ headerClosedWidth: '100vw',
3741
+ }),
3742
+ // Object — structured data accordion
3743
+ make('Object', {
3744
+ accordionAlignment: 'space-evenly',
3745
+ headerOpenPalette: 'Accent',
3746
+ headerClosedPalette: 'Surface',
3747
+ headerClosedBoxShadow: 'inset -2px 0px 7px 6px rgba(170,170,170,0.6)',
3748
+ }),
3749
+ // Media Details
3750
+ make('Media Details', {
3751
+ accordionPalette: 'Surface',
3752
+ headerOpenType: 'body',
3753
+ headerClosedType: 'body',
3754
+ }),
3755
+ // Docs
3756
+ make('Docs', {
3757
+ accordionPalette: 'Surface',
3758
+ accordionAlignment: 'space-evenly',
3759
+ headerOpenType: 'H3',
3760
+ headerClosedType: 'H3',
3761
+ headerClosedPadding: '12px',
3762
+ headerClosedMargin: '12px',
3763
+ }),
3764
+ // Create
3765
+ make('Create', {
3766
+ accordionWidth: 'fit-content',
3767
+ accordionAlignment: 'left',
3768
+ accordionPalette: 'Accent',
3769
+ accordionPadding: '4px',
3770
+ accordionMargin: '0px',
3771
+ headerOpenType: 'label',
3772
+ headerClosedType: 'H5',
3773
+ headerOpenPadding: '0px',
3774
+ headerClosedPadding: '4px',
3775
+ headerClosedAlignment: 'left',
3776
+ headerClosedMargin: '0px',
3777
+ }),
3778
+ // TeamChat
3779
+ make('TeamChat', {
3780
+ accordionWidth: 'auto',
3781
+ accordionPalette: 'Secondary',
3782
+ headerOpenPadding: '12px',
3783
+ headerClosedPadding: '12px',
3784
+ headerOpenWidth: 'auto',
3785
+ headerClosedWidth: 'auto',
3786
+ headerClosedBorder: '1px solid rgba(0,0,0,0.2)',
3787
+ }),
3788
+ // Style
3789
+ make('Style', {
3790
+ accordionWidth: '100vw',
3791
+ accordionAlignment: 'space-evenly',
3792
+ accordionPalette: 'Accent',
3793
+ headerOpenType: 'label',
3794
+ headerClosedType: 'label',
3795
+ }),
3796
+ // Share
3797
+ make('Share', {
3798
+ accordionWidth: '100vw',
3799
+ accordionAlignment: 'space-evenly',
3800
+ accordionPalette: 'Secondary',
3801
+ headerOpenPalette: 'Secondary',
3802
+ headerClosedPalette: 'Secondary',
3803
+ headerOpenType: 'label',
3804
+ headerClosedType: 'label',
3805
+ }),
3806
+ // UI
3807
+ make('UI', {
3808
+ accordionPalette: 'Accent',
3809
+ accordionPadding: '0px',
3810
+ accordionMargin: '0px',
3811
+ headerOpenType: 'label',
3812
+ headerClosedType: 'H5',
3813
+ headerClosedPadding: '12px 30px',
3814
+ headerOpenPadding: '6px 12px',
3815
+ headerClosedBoxShadow: 'rgba(50, 50, 93, 0.25) 0px 30px 60px -12px inset, rgba(0, 0, 0, 0.3) 0px 18px 36px -18px inset',
3816
+ }),
3817
+ // Chat Bots
3818
+ make('Chat Bots', {
3819
+ accordionPalette: 'Muted',
3820
+ }),
3821
+ // LabelBox tools — each has a distinct tinted border
3822
+ // Primary app views
3823
+ make('Team', {
3824
+ accordionPalette: 'Primary',
3825
+ accordionAlignment: 'space-evenly',
3826
+ accordionPadding: '0px',
3827
+ accordionMargin: '0px',
3828
+ headerClosedType: 'title',
3829
+ }),
3830
+ make('Sheet', {
3831
+ accordionPalette: 'Surface',
3832
+ headerOpenType: 'H4',
3833
+ headerClosedType: 'H4',
3834
+ }),
3835
+ make('App', {
3836
+ accordionPalette: 'Primary',
3837
+ accordionAlignment: 'center',
3838
+ headerOpenType: 'title',
3839
+ headerClosedType: 'title',
3840
+ }),
3841
+ make('Report', {
3842
+ accordionPalette: 'Surface',
3843
+ accordionLayout: 'column',
3844
+ headerOpenType: 'H3',
3845
+ headerClosedType: 'H3',
3846
+ headerClosedBoxShadow: `inset 0 -1px 0 ${primaryBg}`,
3847
+ }),
3848
+ ];
3849
+ }
3850
+ // ────────────────────────────────────────────────────────────────────────
3851
+ // Classes — color-blue, justifyRight
3852
+ // ────────────────────────────────────────────────────────────────────────
3853
+ buildClasses() {
3854
+ return [
3855
+ { className: 'color-blue', class: 'color: blue;' },
3856
+ { className: 'justifyRight', class: 'justify-content: flex-end;\njustify-self: flex-end;' },
3857
+ ];
3858
+ }
3859
+ capitalize(s) {
3860
+ return s.charAt(0).toUpperCase() + s.slice(1);
3861
+ }
3862
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: ThemeGeneratorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3863
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.15", type: ThemeGeneratorComponent, isStandalone: true, selector: "faster-theme-generator", inputs: { themeStyle: "themeStyle", purpose: "purpose", palette: "palette", layout: "layout", elevation: "elevation", textShadow: "textShadow", name: "name", showPreview: "showPreview", responsiveStandardWidth: "responsiveStandardWidth", responsiveFontSizeMin: "responsiveFontSizeMin", responsiveFontSizeMax: "responsiveFontSizeMax", responsiveMarginMin: "responsiveMarginMin", responsiveMarginMax: "responsiveMarginMax", responsivePaddingMin: "responsivePaddingMin", responsivePaddingMax: "responsivePaddingMax" }, outputs: { themeGenerated: "themeGenerated" }, usesOnChanges: true, ngImport: i0, template: `
3864
+ <div class="ntg-root" *ngIf="showPreview && theme">
3865
+ <div class="ntg-header">
3866
+ <strong>{{ theme.name }}</strong>
3867
+ <span class="ntg-meta">{{ themeStyle }} · {{ purpose }}</span>
3868
+ </div>
3869
+ <div class="ntg-swatches">
3870
+ <span
3871
+ *ngFor="let p of theme.palette"
3872
+ class="ntg-swatch"
3873
+ [title]="p.colorName"
3874
+ [style.background]="resolveSwatch(p)"
3875
+ ></span>
3876
+ </div>
3877
+ </div>
3878
+ `, isInline: true, styles: [".ntg-root{font-family:system-ui,sans-serif;padding:8px 12px;border:1px solid rgba(0,0,0,.1);border-radius:6px;display:inline-flex;flex-direction:column;gap:6px}.ntg-header{display:flex;gap:8px;align-items:baseline}.ntg-meta{font-size:11px;opacity:.6;text-transform:capitalize}.ntg-swatches{display:flex;gap:4px}.ntg-swatch{width:18px;height:18px;border-radius:50%;border:1px solid rgba(0,0,0,.1)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3879
+ }
3880
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: ThemeGeneratorComponent, decorators: [{
3881
+ type: Component,
3882
+ args: [{ selector: 'faster-theme-generator', standalone: true, imports: [CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: `
3883
+ <div class="ntg-root" *ngIf="showPreview && theme">
3884
+ <div class="ntg-header">
3885
+ <strong>{{ theme.name }}</strong>
3886
+ <span class="ntg-meta">{{ themeStyle }} · {{ purpose }}</span>
3887
+ </div>
3888
+ <div class="ntg-swatches">
3889
+ <span
3890
+ *ngFor="let p of theme.palette"
3891
+ class="ntg-swatch"
3892
+ [title]="p.colorName"
3893
+ [style.background]="resolveSwatch(p)"
3894
+ ></span>
3895
+ </div>
3896
+ </div>
3897
+ `, styles: [".ntg-root{font-family:system-ui,sans-serif;padding:8px 12px;border:1px solid rgba(0,0,0,.1);border-radius:6px;display:inline-flex;flex-direction:column;gap:6px}.ntg-header{display:flex;gap:8px;align-items:baseline}.ntg-meta{font-size:11px;opacity:.6;text-transform:capitalize}.ntg-swatches{display:flex;gap:4px}.ntg-swatch{width:18px;height:18px;border-radius:50%;border:1px solid rgba(0,0,0,.1)}\n"] }]
3898
+ }], propDecorators: { themeStyle: [{
3899
+ type: Input
3900
+ }], purpose: [{
3901
+ type: Input
3902
+ }], palette: [{
3903
+ type: Input
3904
+ }], layout: [{
3905
+ type: Input
3906
+ }], elevation: [{
3907
+ type: Input
3908
+ }], textShadow: [{
3909
+ type: Input
3910
+ }], name: [{
3911
+ type: Input
3912
+ }], showPreview: [{
3913
+ type: Input
3914
+ }], responsiveStandardWidth: [{
3915
+ type: Input
3916
+ }], responsiveFontSizeMin: [{
3917
+ type: Input
3918
+ }], responsiveFontSizeMax: [{
3919
+ type: Input
3920
+ }], responsiveMarginMin: [{
3921
+ type: Input
3922
+ }], responsiveMarginMax: [{
3923
+ type: Input
3924
+ }], responsivePaddingMin: [{
3925
+ type: Input
3926
+ }], responsivePaddingMax: [{
3927
+ type: Input
3928
+ }], themeGenerated: [{
3929
+ type: Output
3930
+ }] } });
3931
+
3932
+ // Shared theme types for the Angular service and the framework-agnostic
3933
+ // client. Defined locally (not imported from platform-object.service) so a
3934
+ // published package has no internal cross-file dependencies.
3935
+
3936
+ /*
3937
+ * Public API Surface of faster-themes
3938
+ */
3939
+ /* Napkin Tools */
3940
+ /*
3941
+ export * from './lib/napkin-tools/napkin-tools.module';
3942
+ export * from './lib/napkin-tools/button/button-component';
3943
+ export * from './lib/napkin-tools/nField.directive';
3944
+ */
3945
+
3946
+ /**
3947
+ * Generated bundle index. Do not edit.
3948
+ */
3949
+
3950
+ export { AccordionComponent, AccordionContent, AccordionHeader, AccordionItem, AccordionTitle, FasterThemesComponent, FasterThemesModule, FasterThemesService, RippleDirective, THEME_API_BASE_URL, ThemeGeneratorComponent, ThemeService, TogglePipe, nButtonDirective, nClassDirective, nColorDirective, nContainerDirective, nTypeDirective };
3951
+ //# sourceMappingURL=fasteros-themes.mjs.map