@meshmakers/octo-ui 3.3.920 → 3.3.930

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meshmakers/octo-ui",
3
- "version": "3.3.920",
3
+ "version": "3.3.930",
4
4
  "peerDependencies": {
5
5
  "@ngx-translate/core": "^17.0.0",
6
6
  "@angular/animations": "^21.0.6",
@@ -42,6 +42,14 @@
42
42
  ".": {
43
43
  "types": "./types/meshmakers-octo-ui.d.ts",
44
44
  "default": "./fesm2022/meshmakers-octo-ui.mjs"
45
+ },
46
+ "./branding": {
47
+ "types": "./types/meshmakers-octo-ui-branding.d.ts",
48
+ "default": "./fesm2022/meshmakers-octo-ui-branding.mjs"
49
+ },
50
+ "./branding-settings": {
51
+ "types": "./types/meshmakers-octo-ui-branding-settings.d.ts",
52
+ "default": "./fesm2022/meshmakers-octo-ui-branding-settings.mjs"
45
53
  }
46
54
  },
47
55
  "type": "module"
@@ -0,0 +1,107 @@
1
+ import * as _progress_kendo_svg_icons from '@progress/kendo-svg-icons';
2
+ import * as _angular_core from '@angular/core';
3
+ import { OnInit } from '@angular/core';
4
+ import { FormGroup, FormControl } from '@angular/forms';
5
+ import { Routes } from '@angular/router';
6
+
7
+ /**
8
+ * Strings used by `SettingsPageComponent`. This is a **required** input on the
9
+ * component — there are too many keys to provide reasonable English defaults
10
+ * that would degrade gracefully. Host applications construct an instance from
11
+ * their own translation system (e.g. ngx-translate `instant()`) and pass it
12
+ * via `[messages]`.
13
+ *
14
+ * Suggested .resx key prefix: `Branding_Settings_*` (or `Settings_*` to match
15
+ * the existing maco-app keys).
16
+ */
17
+ interface BrandingSettingsMessages {
18
+ sectionGeneral: string;
19
+ sectionLogos: string;
20
+ sectionLightTheme: string;
21
+ sectionDarkTheme: string;
22
+ enableDarkTheme: string;
23
+ appName: string;
24
+ appTitle: string;
25
+ logoHeader: string;
26
+ logoFooter: string;
27
+ logoFavicon: string;
28
+ logoRemove: string;
29
+ uploadLogo: string;
30
+ uploadFavicon: string;
31
+ colorPrimary: string;
32
+ colorSecondary: string;
33
+ colorTertiary: string;
34
+ colorNeutral: string;
35
+ colorBackground: string;
36
+ gradientHeader: string;
37
+ gradientFooter: string;
38
+ gradientStart: string;
39
+ gradientEnd: string;
40
+ required: string;
41
+ save: string;
42
+ resetDefaults: string;
43
+ saveSuccess: string;
44
+ saveError: string;
45
+ resetSuccess: string;
46
+ loadError: string;
47
+ }
48
+
49
+ type LogoSlotName = 'header' | 'footer' | 'favicon';
50
+ declare class SettingsPageComponent implements OnInit {
51
+ readonly messages: _angular_core.InputSignal<BrandingSettingsMessages>;
52
+ private readonly fb;
53
+ private readonly branding;
54
+ private readonly messageService;
55
+ private readonly defaults;
56
+ protected readonly saveIcon: _progress_kendo_svg_icons.SVGIcon;
57
+ protected readonly undoIcon: _progress_kendo_svg_icons.SVGIcon;
58
+ protected readonly xIcon: _progress_kendo_svg_icons.SVGIcon;
59
+ protected readonly logoSlotNames: readonly LogoSlotName[];
60
+ protected readonly saving: _angular_core.WritableSignal<boolean>;
61
+ protected readonly settingsForm: FormGroup;
62
+ private readonly slotStates;
63
+ private readonly persistedUrls;
64
+ constructor();
65
+ protected darkThemeEnabled(): boolean;
66
+ ngOnInit(): Promise<void>;
67
+ private readonly dragOverSlot;
68
+ protected isDragOver(slot: LogoSlotName): boolean;
69
+ protected onLogoSelected(slot: LogoSlotName, event: Event): void;
70
+ protected onLogoDragOver(slot: LogoSlotName, event: DragEvent): void;
71
+ protected onLogoDragLeave(slot: LogoSlotName): void;
72
+ protected onLogoDropped(slot: LogoSlotName, event: DragEvent): void;
73
+ private applyLogoFile;
74
+ protected clearLogo(slot: LogoSlotName): void;
75
+ protected previewUrl(slot: LogoSlotName): string | null;
76
+ protected onSubmit(): Promise<void>;
77
+ protected onResetDefaults(): Promise<void>;
78
+ private hydrate;
79
+ private logoFileForSave;
80
+ private strValue;
81
+ private paletteFromForm;
82
+ protected colorCtrl(controlName: string): FormControl<string>;
83
+ /**
84
+ * Resolves a localized label for a given logo slot. The maco-app source used
85
+ * dynamic translate-key concatenation (`'Settings_Logo_' + slot | translate`);
86
+ * the library expresses the same idea by mapping slot names to fields on the
87
+ * `BrandingSettingsMessages` interface.
88
+ */
89
+ protected logoLabel(slot: LogoSlotName): string;
90
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<SettingsPageComponent, never>;
91
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<SettingsPageComponent, "mm-branding-settings", never, { "messages": { "alias": "messages"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
92
+ }
93
+
94
+ /**
95
+ * Routes for the branding/settings UI. Mount under any path:
96
+ *
97
+ * ```ts
98
+ * { path: 'settings', canActivate: [adminGuard], children: BRANDING_ROUTES }
99
+ * ```
100
+ *
101
+ * The `SettingsPageComponent` is lazy-loaded on first navigation regardless of
102
+ * how the host wires this — `loadComponent` returns a dynamic import.
103
+ */
104
+ declare const BRANDING_ROUTES: Routes;
105
+
106
+ export { BRANDING_ROUTES, SettingsPageComponent };
107
+ export type { BrandingSettingsMessages };
@@ -0,0 +1,285 @@
1
+ import * as i0 from '@angular/core';
2
+ import { InjectionToken, EnvironmentProviders, WritableSignal, Provider } from '@angular/core';
3
+ import * as _progress_kendo_svg_icons from '@progress/kendo-svg-icons';
4
+ import { TitleStrategy, RouterStateSnapshot } from '@angular/router';
5
+
6
+ /** Two-stop linear gradient; applied at 90deg by the theme application layer. */
7
+ interface ThemeGradient {
8
+ startColor: string;
9
+ endColor: string;
10
+ }
11
+ /** Palette consumed by the theme layer (Kendo + app CSS variables). */
12
+ interface ThemePalette {
13
+ primaryColor: string;
14
+ secondaryColor: string;
15
+ tertiaryColor: string;
16
+ neutralColor: string;
17
+ backgroundColor: string;
18
+ headerGradient: ThemeGradient;
19
+ footerGradient: ThemeGradient;
20
+ }
21
+
22
+ /** Domain-level branding shape consumed by the UI. */
23
+ interface BrandingData {
24
+ rtId: string | null;
25
+ appName: string;
26
+ appTitle: string;
27
+ headerLogoUrl: string | null;
28
+ footerLogoUrl: string | null;
29
+ faviconUrl: string | null;
30
+ lightTheme: ThemePalette;
31
+ /** When null, the tenant has disabled dark mode — switcher locks to light. */
32
+ darkTheme: ThemePalette | null;
33
+ }
34
+ /**
35
+ * Update descriptor for `BrandingDataSource.save`. File slots are tri-state:
36
+ * `File` replaces, `null` clears, `undefined` leaves the existing blob untouched.
37
+ */
38
+ interface BrandingUpdate {
39
+ appName: string;
40
+ appTitle: string;
41
+ headerLogoFile: File | null | undefined;
42
+ footerLogoFile: File | null | undefined;
43
+ faviconFile: File | null | undefined;
44
+ lightTheme: ThemePalette;
45
+ darkTheme: ThemePalette | null;
46
+ }
47
+
48
+ /**
49
+ * Bundled fallback URLs used when the tenant did not upload a logo / favicon.
50
+ * Any field omitted means the corresponding component renders nothing (text-only
51
+ * header, no <link rel="icon">, etc.) instead of breaking with a 404.
52
+ */
53
+ interface OctoBrandingFallbackAssets {
54
+ headerLogo?: string;
55
+ footerLogo?: string;
56
+ favicon?: string;
57
+ }
58
+ declare const OCTO_BRANDING_DEFAULTS: InjectionToken<BrandingData>;
59
+ declare const OCTO_BRANDING_FALLBACK_ASSETS: InjectionToken<OctoBrandingFallbackAssets>;
60
+ /**
61
+ * Library-shipped neutral fallback. Apps that don't supply their own defaults
62
+ * see a working but visually-neutral baseline ("configure me"), not a crash.
63
+ */
64
+ declare const NEUTRAL_BRANDING_DEFAULTS: BrandingData;
65
+ declare const NEUTRAL_FALLBACK_ASSETS: OctoBrandingFallbackAssets;
66
+
67
+ interface OctoBrandingConfig {
68
+ /** Caller-supplied defaults; merged shallowly over `NEUTRAL_BRANDING_DEFAULTS`. */
69
+ defaults?: Partial<BrandingData>;
70
+ /** Bundled URLs used when the tenant has not uploaded a logo / favicon. */
71
+ fallbackAssets?: OctoBrandingFallbackAssets;
72
+ /**
73
+ * When `true` (default) the library binds {@link AppTitleService} as the
74
+ * application's `TitleStrategy`, so `document.title` automatically reflects
75
+ * `<branding.appTitle> | <route.breadcrumb>` on every navigation. Set to
76
+ * `false` if the host already provides its own `TitleStrategy`.
77
+ */
78
+ registerTitleStrategy?: boolean;
79
+ }
80
+ /**
81
+ * Registers branding tokens, defaults, fallback assets, and (by default) the
82
+ * `TitleStrategy` that drives `document.title` from the branding signal.
83
+ *
84
+ * Does **not** load the tenant's branding record. Loading must happen after
85
+ * authentication is settled (Apollo's URI bakes in the tenant ID at the time
86
+ * the first request fires; loading too early targets
87
+ * `/tenants/undefined/GraphQL`). The host shell typically does:
88
+ *
89
+ * ```ts
90
+ * inject(BrandingApplicationService); // start the apply effect
91
+ * effect(() => {
92
+ * if (this.auth.isAuthenticated()) {
93
+ * this.brandingDataSource.load();
94
+ * }
95
+ * });
96
+ * ```
97
+ *
98
+ * `BrandingApplicationService` is `providedIn: 'root'`, so the host only has
99
+ * to inject it once to activate the apply effect.
100
+ */
101
+ declare function provideOctoBranding(config?: OctoBrandingConfig): EnvironmentProviders;
102
+
103
+ /**
104
+ * Industry-standard branding vocabulary controlling what a brand block renders.
105
+ * - 'signet' — symbol/mark only (the image)
106
+ * - 'logotype' — wordmark only (the text)
107
+ * - 'logo' — combined (image + text); falls back to text-only when image absent
108
+ */
109
+ type BrandDisplay = 'signet' | 'logotype' | 'logo';
110
+
111
+ interface ThemeSwitcherMessages {
112
+ toggleToDark: string;
113
+ toggleToLight: string;
114
+ /** Aria label when the tenant disabled dark mode (switcher is locked). */
115
+ unavailable: string;
116
+ }
117
+ declare const DEFAULT_THEME_SWITCHER_MESSAGES: ThemeSwitcherMessages;
118
+
119
+ declare class ThemeSwitcherComponent {
120
+ readonly messages: i0.InputSignal<ThemeSwitcherMessages>;
121
+ private readonly themeService;
122
+ private readonly branding;
123
+ protected readonly isDark: i0.Signal<boolean>;
124
+ protected readonly available: i0.Signal<boolean>;
125
+ protected readonly ariaLabel: i0.Signal<string>;
126
+ protected readonly lightIcon: _progress_kendo_svg_icons.SVGIcon;
127
+ protected readonly darkIcon: _progress_kendo_svg_icons.SVGIcon;
128
+ protected onToggle(): void;
129
+ static ɵfac: i0.ɵɵFactoryDeclaration<ThemeSwitcherComponent, never>;
130
+ static ɵcmp: i0.ɵɵComponentDeclaration<ThemeSwitcherComponent, "mm-theme-switcher", never, { "messages": { "alias": "messages"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
131
+ }
132
+
133
+ /**
134
+ * Per-tenant branding. Source of truth is the `SystemUIBranding`
135
+ * CK runtime entity (one record per tenant, `rtWellKnownName = 'Branding'`).
136
+ * Loaded via GraphQL, mirrored into a signal for consumers.
137
+ */
138
+ declare class BrandingDataSource {
139
+ private readonly getBrandingGQL;
140
+ private readonly createBrandingGQL;
141
+ private readonly updateBrandingGQL;
142
+ private readonly defaults;
143
+ private readonly state;
144
+ readonly branding: i0.Signal<BrandingData>;
145
+ load(): Promise<void>;
146
+ save(update: BrandingUpdate): Promise<BrandingData>;
147
+ resetToDefaults(): Promise<void>;
148
+ private buildInput;
149
+ private runCreate;
150
+ private runUpdate;
151
+ private paletteToInput;
152
+ private mapFromServer;
153
+ private paletteFromServer;
154
+ /**
155
+ * Convert a byte array from a GraphQL `[Byte]` field into a data URL.
156
+ * Returns null for null/undefined/empty input. MIME type is detected from
157
+ * the first few bytes (magic numbers) so <img src> works without a
158
+ * server-provided content-type.
159
+ */
160
+ private bytesToDataUrl;
161
+ private detectImageMime;
162
+ private blobToDataUrl;
163
+ /**
164
+ * Three-value binary resolution for save():
165
+ * - `null` → caller wants the asset cleared; forward null to the server.
166
+ * - `undefined` → caller left the slot untouched; omit the field so the
167
+ * server keeps the existing blob.
168
+ * - `File` → caller uploaded a new asset; serialize to byte array.
169
+ */
170
+ private prepareBinary;
171
+ static ɵfac: i0.ɵɵFactoryDeclaration<BrandingDataSource, never>;
172
+ static ɵprov: i0.ɵɵInjectableDeclaration<BrandingDataSource>;
173
+ }
174
+
175
+ /**
176
+ * Light/dark mode toggle backed by `localStorage` + `<html data-theme="...">`.
177
+ * Pure mode toggle; palette generation lives in `BrandingApplicationService`.
178
+ */
179
+ declare class ThemeService {
180
+ private readonly document;
181
+ private readonly renderer;
182
+ private readonly isDarkSignal;
183
+ readonly isDark: i0.Signal<boolean>;
184
+ constructor();
185
+ toggleTheme(): void;
186
+ setDark(isDark: boolean): void;
187
+ private applyTheme;
188
+ private getInitialThemeIsDark;
189
+ static ɵfac: i0.ɵɵFactoryDeclaration<ThemeService, never>;
190
+ static ɵprov: i0.ɵɵInjectableDeclaration<ThemeService>;
191
+ }
192
+
193
+ type ColorPaletteShades = Record<number, string>;
194
+ /**
195
+ * Writes tenant branding (Kendo color vars, gradients, favicon, document
196
+ * title) inline on <html>. An effect re-applies the correct palette when
197
+ * either the theme mode or the branding data changes, so mode toggles after
198
+ * a save don't leave stale inline values behind.
199
+ */
200
+ declare class BrandingApplicationService {
201
+ private document;
202
+ private themeService;
203
+ private brandingDataSource;
204
+ private fallbackAssets;
205
+ private static readonly BRANDED_VARS;
206
+ constructor();
207
+ apply(data: BrandingData, palette: ThemePalette): void;
208
+ applyThemeColors(palette: ThemePalette): void;
209
+ applyFavicon(faviconUrl: string | null): void;
210
+ private clearInlineBrandingVars;
211
+ private setIf;
212
+ private applyColorPalette;
213
+ private applyNeutralColors;
214
+ generatePalette(baseColor: string): ColorPaletteShades;
215
+ private hexToRgb;
216
+ private rgbToHsl;
217
+ private hslToHex;
218
+ private rgbToHex;
219
+ private getContrastColor;
220
+ static ɵfac: i0.ɵɵFactoryDeclaration<BrandingApplicationService, never>;
221
+ static ɵprov: i0.ɵɵInjectableDeclaration<BrandingApplicationService>;
222
+ }
223
+
224
+ /**
225
+ * Optional function the host supplies to translate a breadcrumb key from
226
+ * `route.data['breadcrumb']` into a localized string. The library has no i18n
227
+ * dependency; without a translator the breadcrumb key is rendered verbatim.
228
+ */
229
+ type OctoTitleTranslator = (key: string) => string;
230
+ declare const OCTO_TITLE_TRANSLATOR: InjectionToken<OctoTitleTranslator>;
231
+ /**
232
+ * `TitleStrategy` that composes `<branding.appTitle> | <route.breadcrumb>`
233
+ * into `document.title` on every navigation, and re-applies on
234
+ * `BrandingDataSource.branding()` changes (e.g. after Settings save).
235
+ *
236
+ * Wire as the application's `TitleStrategy`:
237
+ * ```ts
238
+ * { provide: TitleStrategy, useExisting: AppTitleService }
239
+ * ```
240
+ *
241
+ * Optional translation of the breadcrumb key is supplied through
242
+ * {@link OCTO_TITLE_TRANSLATOR}; without it the key from `route.data` is used
243
+ * as-is.
244
+ *
245
+ * `BrandingDataSource` is resolved lazily via `injector.get()` rather than
246
+ * field-injected. The router constructs `TitleStrategy` eagerly during
247
+ * `APP_INITIALIZER`, before the host's tenant config has loaded. A direct
248
+ * field-inject of `BrandingDataSource` here would chain into Apollo's
249
+ * factory, which reads `tenantId` while it is still `undefined` and bakes
250
+ * `/tenants/undefined/GraphQL` into the request URI. Lazy resolution defers
251
+ * the chain to the first navigation, by which time config is loaded.
252
+ */
253
+ declare class AppTitleService extends TitleStrategy {
254
+ private readonly title;
255
+ private readonly translator;
256
+ private readonly injector;
257
+ private lastRouterState?;
258
+ private brandingEffectInitialized;
259
+ updateTitle(routerState: RouterStateSnapshot): void;
260
+ private ensureBrandingSubscription;
261
+ private applyTitle;
262
+ private getDeepestBreadcrumb;
263
+ static ɵfac: i0.ɵɵFactoryDeclaration<AppTitleService, never>;
264
+ static ɵprov: i0.ɵɵInjectableDeclaration<AppTitleService>;
265
+ }
266
+
267
+ /**
268
+ * Framework-agnostic stub matching the runtime shape of `BrandingDataSource`.
269
+ * The async methods are plain functions; consumers wrap them in their own
270
+ * spy primitive (`jasmine.createSpy`, `jest.fn`, `vi.fn`) when they need
271
+ * call assertions. Casting `branding` back to `WritableSignal` lets specs
272
+ * drive state changes.
273
+ */
274
+ interface BrandingDataSourceStub {
275
+ branding: WritableSignal<BrandingData>;
276
+ load: () => Promise<void>;
277
+ save: (u: BrandingUpdate) => Promise<BrandingData>;
278
+ resetToDefaults: () => Promise<void>;
279
+ }
280
+ declare function createBrandingStub(overrides?: Partial<BrandingData>): BrandingDataSourceStub;
281
+ /** Convenience: returns a Provider that injects the stub as BrandingDataSource. */
282
+ declare function provideBrandingTesting(stub?: BrandingDataSourceStub): Provider;
283
+
284
+ export { AppTitleService, BrandingApplicationService, BrandingDataSource, DEFAULT_THEME_SWITCHER_MESSAGES, NEUTRAL_BRANDING_DEFAULTS, NEUTRAL_FALLBACK_ASSETS, OCTO_BRANDING_DEFAULTS, OCTO_BRANDING_FALLBACK_ASSETS, OCTO_TITLE_TRANSLATOR, ThemeService, ThemeSwitcherComponent, createBrandingStub, provideBrandingTesting, provideOctoBranding };
285
+ export type { BrandDisplay, BrandingData, BrandingDataSourceStub, BrandingUpdate, OctoBrandingConfig, OctoBrandingFallbackAssets, OctoTitleTranslator, ThemeGradient, ThemePalette, ThemeSwitcherMessages };