@mohamedatia/fly-design-system 1.1.0 → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/mohamedatia-fly-design-system.mjs +138 -50
- package/fesm2022/mohamedatia-fly-design-system.mjs.map +1 -1
- package/package.json +1 -1
- package/scss/_fly-theme.scss +9 -0
- package/scss/_theme-auto.scss +8 -0
- package/scss/_theme-dark-vars.scss +38 -0
- package/scss/_theme-dark.scss +6 -0
- package/scss/_theme-light.scss +81 -0
- package/scss/_theme-transparent.scss +41 -0
- package/scss/_tokens.scss +126 -0
- package/types/mohamedatia-fly-design-system.d.ts +54 -35
- package/types/mohamedatia-fly-design-system.d.ts.map +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, signal, computed, Injectable, inject, Pipe } from '@angular/core';
|
|
2
|
+
import { InjectionToken, signal, computed, Injectable, inject, ErrorHandler, Pipe } from '@angular/core';
|
|
3
3
|
|
|
4
4
|
const WINDOW_DATA = new InjectionToken('WINDOW_DATA');
|
|
5
5
|
|
|
@@ -39,7 +39,7 @@ class AuthService {
|
|
|
39
39
|
}
|
|
40
40
|
/**
|
|
41
41
|
* Standalone mode: parse a JWT from localStorage and hydrate the user.
|
|
42
|
-
*
|
|
42
|
+
* Pass the storage key your app uses; embedded remotes typically set this in `APP_INITIALIZER`.
|
|
43
43
|
*/
|
|
44
44
|
initFromToken(storageKey = 'circles_token') {
|
|
45
45
|
const token = localStorage.getItem(storageKey);
|
|
@@ -81,69 +81,128 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
81
81
|
args: [{ providedIn: 'root' }]
|
|
82
82
|
}] });
|
|
83
83
|
|
|
84
|
-
|
|
84
|
+
/** Locales that use RTL layout for `dir` and DS `isRtl` / `direction`. */
|
|
85
|
+
const RTL_LOCALE_SET = new Set(['ar', 'ur']);
|
|
86
|
+
function isRtlLocale(lang) {
|
|
87
|
+
return RTL_LOCALE_SET.has(lang);
|
|
88
|
+
}
|
|
89
|
+
function escapeRegExp(s) {
|
|
90
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
91
|
+
}
|
|
92
|
+
/** Accepts flat or nested JSON objects; nested keys become dot paths. Rejects arrays and non-string leaves. */
|
|
93
|
+
function normalizeLocaleJson(raw) {
|
|
94
|
+
if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
95
|
+
throw new Error('Locale bundle must be a JSON object');
|
|
96
|
+
}
|
|
97
|
+
const out = {};
|
|
98
|
+
const walk = (obj, prefix) => {
|
|
99
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
100
|
+
const path = prefix ? `${prefix}.${k}` : k;
|
|
101
|
+
if (typeof v === 'string') {
|
|
102
|
+
out[path] = v;
|
|
103
|
+
}
|
|
104
|
+
else if (v !== null && typeof v === 'object' && !Array.isArray(v)) {
|
|
105
|
+
walk(v, path);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
throw new Error(`Locale bundle invalid value at "${path}" (expected string or nested object)`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
walk(raw, '');
|
|
113
|
+
return out;
|
|
114
|
+
}
|
|
85
115
|
/**
|
|
86
|
-
* Shared I18nService for Business Apps.
|
|
87
|
-
*
|
|
88
|
-
* Loads locale JSON files from a configurable base URL and exposes a `t()`
|
|
89
|
-
* method for key-based translation with optional interpolation.
|
|
116
|
+
* Shared I18nService for the shell and Business Apps.
|
|
90
117
|
*
|
|
91
|
-
*
|
|
92
|
-
* whenever the user switches language — the singleton is shared via Module
|
|
93
|
-
* Federation so Business Apps automatically see the updated translations.
|
|
118
|
+
* **Merge order** (later keys win): shell layer → remote bundles in registration order.
|
|
94
119
|
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
120
|
+
* - Shell: `setShellTranslations()` after loading `locale/{lang}.json` and API overrides.
|
|
121
|
+
* - Remotes: `loadBundle()` per manifest `localeBaseUrl`.
|
|
97
122
|
*/
|
|
98
123
|
class I18nService {
|
|
99
|
-
|
|
124
|
+
errorHandler = inject(ErrorHandler, { optional: true });
|
|
125
|
+
_shell = signal({}, ...(ngDevMode ? [{ debugName: "_shell" }] : /* istanbul ignore next */ []));
|
|
126
|
+
_bundles = signal({}, ...(ngDevMode ? [{ debugName: "_bundles" }] : /* istanbul ignore next */ []));
|
|
127
|
+
_bundleOrder = signal([], ...(ngDevMode ? [{ debugName: "_bundleOrder" }] : /* istanbul ignore next */ []));
|
|
100
128
|
_locale = signal('en', ...(ngDevMode ? [{ debugName: "_locale" }] : /* istanbul ignore next */ []));
|
|
129
|
+
_version = signal(0, ...(ngDevMode ? [{ debugName: "_version" }] : /* istanbul ignore next */ []));
|
|
130
|
+
_merged = computed(() => {
|
|
131
|
+
const shell = this._shell();
|
|
132
|
+
const order = this._bundleOrder();
|
|
133
|
+
const bundles = this._bundles();
|
|
134
|
+
let out = { ...shell };
|
|
135
|
+
for (const id of order) {
|
|
136
|
+
const b = bundles[id];
|
|
137
|
+
if (b)
|
|
138
|
+
out = { ...out, ...b };
|
|
139
|
+
}
|
|
140
|
+
return out;
|
|
141
|
+
}, ...(ngDevMode ? [{ debugName: "_merged" }] : /* istanbul ignore next */ []));
|
|
101
142
|
locale = this._locale.asReadonly();
|
|
102
|
-
|
|
103
|
-
|
|
143
|
+
version = this._version.asReadonly();
|
|
144
|
+
isRtl = computed(() => isRtlLocale(this._locale()), ...(ngDevMode ? [{ debugName: "isRtl" }] : /* istanbul ignore next */ []));
|
|
145
|
+
direction = computed(() => (isRtlLocale(this._locale()) ? 'rtl' : 'ltr'), ...(ngDevMode ? [{ debugName: "direction" }] : /* istanbul ignore next */ []));
|
|
146
|
+
bump() {
|
|
147
|
+
this._version.update((v) => v + 1);
|
|
148
|
+
}
|
|
149
|
+
/** Replace shell strings (desktop `locale/*.json` + `/api/i18n/desktop-shell/{lang}` merged by the shell). */
|
|
150
|
+
setShellTranslations(data, lang) {
|
|
151
|
+
this._shell.set(data);
|
|
152
|
+
this._locale.set(lang);
|
|
153
|
+
this.bump();
|
|
154
|
+
}
|
|
155
|
+
setLanguage(lang) {
|
|
156
|
+
this._locale.set(lang);
|
|
157
|
+
this.bump();
|
|
158
|
+
}
|
|
104
159
|
/**
|
|
105
|
-
*
|
|
106
|
-
* the current translation map. Subsequent calls for the same language
|
|
107
|
-
* replace the previous entries for that language.
|
|
108
|
-
*
|
|
109
|
-
* @param localeBaseUrl Base URL ending with `/`, e.g. `https://cdn.example.com/locale/`
|
|
110
|
-
* @param lang Locale code, e.g. `'en'`, `'ar'`
|
|
160
|
+
* Fetch `{baseUrl}{lang}.json` and store under `id`. New ids are appended to the merge order.
|
|
111
161
|
*/
|
|
112
|
-
async
|
|
162
|
+
async loadBundle(opts) {
|
|
163
|
+
const { id, baseUrl, lang, signal: abortSignal } = opts;
|
|
113
164
|
try {
|
|
114
|
-
const
|
|
115
|
-
const
|
|
165
|
+
const base = baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`;
|
|
166
|
+
const url = `${base}${lang}.json`;
|
|
167
|
+
const resp = await fetch(url, { signal: abortSignal });
|
|
116
168
|
if (!resp.ok)
|
|
117
169
|
throw new Error(`HTTP ${resp.status}`);
|
|
118
|
-
const data = await resp.json();
|
|
119
|
-
|
|
170
|
+
const data = normalizeLocaleJson(await resp.json());
|
|
171
|
+
if (abortSignal?.aborted)
|
|
172
|
+
return;
|
|
173
|
+
this._bundles.update((b) => ({ ...b, [id]: data }));
|
|
174
|
+
this._bundleOrder.update((order) => (order.includes(id) ? order : [...order, id]));
|
|
120
175
|
this._locale.set(lang);
|
|
176
|
+
this.bump();
|
|
121
177
|
}
|
|
122
|
-
catch {
|
|
123
|
-
|
|
178
|
+
catch (e) {
|
|
179
|
+
if (abortSignal?.aborted)
|
|
180
|
+
return;
|
|
181
|
+
this.errorHandler?.handleError(e);
|
|
124
182
|
}
|
|
125
183
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
184
|
+
removeBundle(id) {
|
|
185
|
+
this._bundles.update((b) => {
|
|
186
|
+
const next = { ...b };
|
|
187
|
+
delete next[id];
|
|
188
|
+
return next;
|
|
189
|
+
});
|
|
190
|
+
this._bundleOrder.update((order) => order.filter((x) => x !== id));
|
|
191
|
+
this.bump();
|
|
192
|
+
}
|
|
193
|
+
clearRemoteBundles() {
|
|
194
|
+
this._bundles.set({});
|
|
195
|
+
this._bundleOrder.set([]);
|
|
196
|
+
this.bump();
|
|
134
197
|
}
|
|
135
|
-
/**
|
|
136
|
-
* Translates a key, with optional `{{param}}` interpolation.
|
|
137
|
-
*
|
|
138
|
-
* @example
|
|
139
|
-
* i18n.t('circles.signals.title')
|
|
140
|
-
* i18n.t('circles.items_count', { n: '5' })
|
|
141
|
-
*/
|
|
142
198
|
t(key, params) {
|
|
143
|
-
const val = this.
|
|
199
|
+
const val = this._merged()[key] ?? key;
|
|
144
200
|
if (!params)
|
|
145
201
|
return val;
|
|
146
|
-
return Object.entries(params).reduce((result, [k, v]) =>
|
|
202
|
+
return Object.entries(params).reduce((result, [k, v]) => {
|
|
203
|
+
const safe = escapeRegExp(k);
|
|
204
|
+
return result.replace(new RegExp(`{{\\s*${safe}\\s*}}`, 'g'), String(v));
|
|
205
|
+
}, val);
|
|
147
206
|
}
|
|
148
207
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: I18nService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
149
208
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: I18nService, providedIn: 'root' });
|
|
@@ -153,6 +212,34 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
153
212
|
args: [{ providedIn: 'root' }]
|
|
154
213
|
}] });
|
|
155
214
|
|
|
215
|
+
/**
|
|
216
|
+
* Applies `html.light-theme` / `html.dark-theme` / `html.transparent-theme` for DS SCSS.
|
|
217
|
+
* Shell and standalone Business Apps use the same service (federation singleton when shared).
|
|
218
|
+
*/
|
|
219
|
+
class FlyThemeService {
|
|
220
|
+
theme = signal('light', ...(ngDevMode ? [{ debugName: "theme" }] : /* istanbul ignore next */ []));
|
|
221
|
+
applyTheme(mode) {
|
|
222
|
+
this.theme.set(mode);
|
|
223
|
+
const html = document.documentElement;
|
|
224
|
+
html.classList.remove('dark-theme', 'light-theme', 'transparent-theme');
|
|
225
|
+
if (mode === 'dark') {
|
|
226
|
+
html.classList.add('dark-theme');
|
|
227
|
+
}
|
|
228
|
+
else if (mode === 'transparent') {
|
|
229
|
+
html.classList.add('transparent-theme');
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
html.classList.add('light-theme');
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: FlyThemeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
236
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: FlyThemeService, providedIn: 'root' });
|
|
237
|
+
}
|
|
238
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: FlyThemeService, decorators: [{
|
|
239
|
+
type: Injectable,
|
|
240
|
+
args: [{ providedIn: 'root' }]
|
|
241
|
+
}] });
|
|
242
|
+
|
|
156
243
|
/**
|
|
157
244
|
* Abstract interface for the WindowManager.
|
|
158
245
|
* The FlyOS shell provides the concrete implementation; Business Apps
|
|
@@ -187,8 +274,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
187
274
|
* Translates a key using the shared I18nService.
|
|
188
275
|
*
|
|
189
276
|
* @example
|
|
190
|
-
* {{ '
|
|
191
|
-
* {{ '
|
|
277
|
+
* {{ 'myApp.section.title' | translate }}
|
|
278
|
+
* {{ 'myApp.items_count' | translate:{ n: count() } }}
|
|
192
279
|
*/
|
|
193
280
|
class TranslatePipe {
|
|
194
281
|
i18n = inject(I18nService);
|
|
@@ -217,13 +304,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
217
304
|
* NOTE: This package is published under @mohamedatia/fly-design-system until the @fly
|
|
218
305
|
* npm org is created. It will be republished as @fly/design-system once available.
|
|
219
306
|
*
|
|
220
|
-
* v1.
|
|
221
|
-
*
|
|
307
|
+
* v1.3.0: FlyThemeService (html theme classes), hardened I18nService (AbortSignal, ErrorHandler, RTL helpers).
|
|
308
|
+
* v1.3.1: loadBundle normalizes locale JSON (flat or nested objects; rejects invalid leaves).
|
|
309
|
+
* See BusinessAppsGuide/03-frontend-app.md.
|
|
222
310
|
*/
|
|
223
311
|
|
|
224
312
|
/**
|
|
225
313
|
* Generated bundle index. Do not edit.
|
|
226
314
|
*/
|
|
227
315
|
|
|
228
|
-
export { AuthService, I18nService, StandaloneWindowManagerService, TranslatePipe, WINDOW_DATA, WindowManagerService };
|
|
316
|
+
export { AuthService, FlyThemeService, I18nService, RTL_LOCALE_SET, StandaloneWindowManagerService, TranslatePipe, WINDOW_DATA, WindowManagerService, isRtlLocale };
|
|
229
317
|
//# sourceMappingURL=mohamedatia-fly-design-system.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mohamedatia-fly-design-system.mjs","sources":["../../../projects/design-system/src/lib/models/window.model.ts","../../../projects/design-system/src/lib/services/auth.service.ts","../../../projects/design-system/src/lib/services/i18n.service.ts","../../../projects/design-system/src/lib/services/window-manager.service.ts","../../../projects/design-system/src/lib/pipes/translate.pipe.ts","../../../projects/design-system/src/public-api.ts","../../../projects/design-system/src/mohamedatia-fly-design-system.ts"],"sourcesContent":["import { InjectionToken, Type } from '@angular/core';\r\n\r\nexport type WindowState = 'normal' | 'minimized' | 'maximized';\r\n\r\nexport interface ChildWindowData {\r\n childComponent: Type<unknown>;\r\n [key: string]: unknown;\r\n}\r\n\r\nexport interface WindowInstance {\r\n id: string;\r\n appId: string;\r\n title: string;\r\n icon: string;\r\n iconBg: string;\r\n state: WindowState;\r\n position: { x: number; y: number };\r\n size: { width: number; height: number };\r\n zIndex: number;\r\n isFocused: boolean;\r\n isClosing?: boolean;\r\n isMinimizing?: boolean;\r\n isRestoring?: boolean;\r\n isMaximizing?: boolean;\r\n isUnmaximizing?: boolean;\r\n parentWindowId?: string;\r\n childData?: ChildWindowData;\r\n}\r\n\r\nexport const WINDOW_DATA = new InjectionToken<WindowInstance>('WINDOW_DATA');\r\n","import { Injectable, computed, signal } from '@angular/core';\nimport { User } from '../models/user.model';\n\ninterface AuthSession {\n user: User;\n accessToken: string;\n expiresAt: number;\n}\n\n/**\n * Shared AuthService for Business Apps.\n *\n * This is a **read-only signal store** — Business Apps inject it to read the\n * current user and access token. The FlyOS shell populates it after PKCE login\n * and silent refresh. Business Apps must never call setSession() directly.\n *\n * When running as a Module Federation remote, the shell and the Business App\n * share this singleton via the `@mohamedatia/fly-design-system` shared mapping\n * in federation.config.js — so the shell's populated instance is the same\n * object the Business App reads.\n *\n * When running standalone (without the shell), call initFromToken() with a\n * JWT stored in localStorage to hydrate the user.\n */\n@Injectable({ providedIn: 'root' })\nexport class AuthService {\n private _session = signal<AuthSession | null>(null);\n\n readonly currentUser = computed(() => this._session()?.user ?? null);\n readonly accessToken = computed(() => this._session()?.accessToken ?? null);\n readonly isAuthenticated = computed(() => {\n const s = this._session();\n return s !== null && s.expiresAt > Date.now();\n });\n\n /**\n * Called by the shell after a successful login or silent refresh.\n * Business Apps should not call this directly.\n */\n setSession(user: User, accessToken: string, expiresAt: number): void {\n this._session.set({ user, accessToken, expiresAt });\n }\n\n /** Clears the in-memory session (called by the shell on logout). */\n clearSession(): void {\n this._session.set(null);\n }\n\n /**\n * Standalone mode: parse a JWT from localStorage and hydrate the user.\n * Key defaults to \"circles_token\" but can be overridden.\n */\n initFromToken(storageKey = 'circles_token'): void {\n const token = localStorage.getItem(storageKey);\n if (!token) return;\n try {\n const base64 = token.split('.')[1].replace(/-/g, '+').replace(/_/g, '/');\n const payload = JSON.parse(decodeURIComponent(\n atob(base64).split('').map(c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)).join('')\n )) as Record<string, unknown>;\n\n const rawRole = payload['role'];\n const roles = Array.isArray(rawRole) ? rawRole as string[] : rawRole ? [rawRole as string] : [];\n const rawApps = payload['apps'];\n const apps = Array.isArray(rawApps) ? rawApps as string[] : rawApps ? [rawApps as string] : [];\n const name = (payload['name'] as string) ?? '';\n const nameParts = name.split(' ');\n\n const user: User = {\n id: (payload['sub'] as string) ?? '',\n tenantId: (payload['tenant_id'] as string) ?? (payload['tid'] as string) ?? null,\n email: (payload['email'] as string) ?? '',\n fullName: name,\n firstName: nameParts[0] ?? '',\n lastName: nameParts.slice(1).join(' '),\n preferredLocale: (payload['locale'] as string) ?? 'en',\n roles,\n apps,\n ou: payload['ou'] as string | undefined,\n };\n\n const expiresAt = ((payload['exp'] as number) ?? 0) * 1000;\n this.setSession(user, token, expiresAt);\n } catch {\n // Invalid token — stay unauthenticated\n }\n }\n}\n","import { Injectable, computed, signal } from '@angular/core';\n\nconst RTL_LOCALES: ReadonlySet<string> = new Set(['ar', 'ur']);\n\n/**\n * Shared I18nService for Business Apps.\n *\n * Loads locale JSON files from a configurable base URL and exposes a `t()`\n * method for key-based translation with optional interpolation.\n *\n * In FlyOS-integrated mode the shell calls loadLocale() on startup and\n * whenever the user switches language — the singleton is shared via Module\n * Federation so Business Apps automatically see the updated translations.\n *\n * In standalone mode, call loadLocale() from your app bootstrap with the\n * locale base URL configured in your backend (Frontend:LocaleBaseUrl).\n */\n@Injectable({ providedIn: 'root' })\nexport class I18nService {\n private _translations = signal<Record<string, string>>({});\n private _locale = signal<string>('en');\n\n readonly locale = this._locale.asReadonly();\n readonly isRtl = computed(() => RTL_LOCALES.has(this._locale()));\n readonly direction = computed(() => RTL_LOCALES.has(this._locale()) ? 'rtl' : 'ltr');\n\n /**\n * Loads translations from `{localeBaseUrl}{lang}.json` and merges them into\n * the current translation map. Subsequent calls for the same language\n * replace the previous entries for that language.\n *\n * @param localeBaseUrl Base URL ending with `/`, e.g. `https://cdn.example.com/locale/`\n * @param lang Locale code, e.g. `'en'`, `'ar'`\n */\n async loadLocale(localeBaseUrl: string, lang: string): Promise<void> {\n try {\n const url = localeBaseUrl.endsWith('/') ? `${localeBaseUrl}${lang}.json` : `${localeBaseUrl}/${lang}.json`;\n const resp = await fetch(url);\n if (!resp.ok) throw new Error(`HTTP ${resp.status}`);\n const data = await resp.json() as Record<string, string>;\n this._translations.update(current => ({ ...current, ...data }));\n this._locale.set(lang);\n } catch {\n // Locale file not available — keep existing translations\n }\n }\n\n /**\n * Merges a pre-loaded translations object directly (useful when the shell\n * has already fetched the locale and wants to push it to Business Apps).\n */\n mergeTranslations(translations: Record<string, string>, lang?: string): void {\n this._translations.update(current => ({ ...current, ...translations }));\n if (lang) this._locale.set(lang);\n }\n\n /**\n * Translates a key, with optional `{{param}}` interpolation.\n *\n * @example\n * i18n.t('circles.signals.title')\n * i18n.t('circles.items_count', { n: '5' })\n */\n t(key: string, params?: Record<string, string | number>): string {\n const val = this._translations()[key] ?? key;\n if (!params) return val;\n return Object.entries(params).reduce(\n (result, [k, v]) => result.replace(new RegExp(`{{\\\\s*${k}\\\\s*}}`, 'g'), String(v)),\n val,\n );\n }\n}\n","import { Injectable } from '@angular/core';\n\n/** Options for opening a child window. */\nexport interface OpenWindowOptions {\n /** Unique identifier for the window instance. */\n windowId: string;\n /** Display title shown in the window chrome. */\n title: string;\n /** Route path or URL to load inside the window. */\n route: string;\n /** Initial width in pixels. Defaults to 900. */\n width?: number;\n /** Initial height in pixels. Defaults to 600. */\n height?: number;\n /** Whether the window can be resized. Defaults to true. */\n resizable?: boolean;\n /** Whether the window can be maximised. Defaults to true. */\n maximizable?: boolean;\n /** Arbitrary data passed to the child window via WINDOW_DATA. */\n data?: unknown;\n}\n\n/**\n * Abstract interface for the WindowManager.\n * The FlyOS shell provides the concrete implementation; Business Apps\n * inject this token to open, close, and focus windows without depending\n * on the shell's internal implementation.\n */\nexport abstract class WindowManagerService {\n abstract openChildWindow(options: OpenWindowOptions): void;\n abstract closeWindow(windowId: string): void;\n abstract focusWindow(windowId: string): void;\n}\n\n/**\n * No-op fallback implementation used when running a Business App in\n * standalone mode (outside the FlyOS shell). Logs a warning instead of\n * throwing so standalone dev/test flows are not broken.\n */\n@Injectable()\nexport class StandaloneWindowManagerService extends WindowManagerService {\n openChildWindow(options: OpenWindowOptions): void {\n console.warn('[WindowManagerService] openChildWindow called in standalone mode — no shell available.', options);\n }\n\n closeWindow(windowId: string): void {\n console.warn('[WindowManagerService] closeWindow called in standalone mode — no shell available.', windowId);\n }\n\n focusWindow(windowId: string): void {\n console.warn('[WindowManagerService] focusWindow called in standalone mode — no shell available.', windowId);\n }\n}\n\n","import { Pipe, PipeTransform, inject } from '@angular/core';\nimport { I18nService } from '../services/i18n.service';\n\n/**\n * Translates a key using the shared I18nService.\n *\n * @example\n * {{ 'circles.signals.title' | translate }}\n * {{ 'circles.items_count' | translate:{ n: count() } }}\n */\n@Pipe({\n name: 'translate',\n standalone: true,\n pure: false,\n})\nexport class TranslatePipe implements PipeTransform {\n private readonly i18n = inject(I18nService);\n\n transform(key: string, params?: Record<string, string | number>): string {\n return this.i18n.t(key, params);\n }\n}\n","/*\r\n * @mohamedatia/fly-design-system — Public API\r\n * https://www.npmjs.com/package/@mohamedatia/fly-design-system\r\n *\r\n * This is the single entry point for Business App developers.\r\n * Import everything from '@mohamedatia/fly-design-system' — never from relative shell paths.\r\n *\r\n * NOTE: This package is published under @mohamedatia/fly-design-system until the @fly\r\n * npm org is created. It will be republished as @fly/design-system once available.\r\n *\r\n * v1.1.0 adds AuthService, I18nService, WindowManagerService, TranslatePipe, and the\r\n * User model — the full set of shared services documented in BusinessAppsGuide/03-frontend-app.md.\r\n */\r\n\r\n// ─── Models ──────────────────────────────────────────────────────────────────\r\nexport type { DesktopApp } from './lib/models/app.model';\r\nexport type {\r\n WindowInstance,\r\n WindowState,\r\n ChildWindowData,\r\n} from './lib/models/window.model';\r\nexport { WINDOW_DATA } from './lib/models/window.model';\r\nexport type { User } from './lib/models/user.model';\r\n\r\n// ─── Remote App Registration ─────────────────────────────────────────────────\r\nexport type { RemoteAppDef } from './lib/models/remote-app.model';\r\n\r\n// ─── Services ────────────────────────────────────────────────────────────────\r\nexport { AuthService } from './lib/services/auth.service';\r\nexport { I18nService } from './lib/services/i18n.service';\r\nexport {\r\n WindowManagerService,\r\n StandaloneWindowManagerService,\r\n type OpenWindowOptions,\r\n} from './lib/services/window-manager.service';\r\n\r\n// ─── Pipes ───────────────────────────────────────────────────────────────────\r\nexport { TranslatePipe } from './lib/pipes/translate.pipe';\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mohamedatia-fly-design-system.mjs","sources":["../../../projects/design-system/src/lib/models/window.model.ts","../../../projects/design-system/src/lib/services/auth.service.ts","../../../projects/design-system/src/lib/services/i18n.service.ts","../../../projects/design-system/src/lib/services/fly-theme.service.ts","../../../projects/design-system/src/lib/services/window-manager.service.ts","../../../projects/design-system/src/lib/pipes/translate.pipe.ts","../../../projects/design-system/src/public-api.ts","../../../projects/design-system/src/mohamedatia-fly-design-system.ts"],"sourcesContent":["import { InjectionToken, Type } from '@angular/core';\r\n\r\nexport type WindowState = 'normal' | 'minimized' | 'maximized';\r\n\r\nexport interface ChildWindowData {\r\n childComponent: Type<unknown>;\r\n [key: string]: unknown;\r\n}\r\n\r\nexport interface WindowInstance {\r\n id: string;\r\n appId: string;\r\n title: string;\r\n icon: string;\r\n iconBg: string;\r\n state: WindowState;\r\n position: { x: number; y: number };\r\n size: { width: number; height: number };\r\n zIndex: number;\r\n isFocused: boolean;\r\n isClosing?: boolean;\r\n isMinimizing?: boolean;\r\n isRestoring?: boolean;\r\n isMaximizing?: boolean;\r\n isUnmaximizing?: boolean;\r\n parentWindowId?: string;\r\n childData?: ChildWindowData;\r\n /** Populated when remote `loadComponent()` fails (e.g. federation). */\r\n remoteLoadError?: string;\r\n}\r\n\r\nexport const WINDOW_DATA = new InjectionToken<WindowInstance>('WINDOW_DATA');\r\n","import { Injectable, computed, signal } from '@angular/core';\nimport { User } from '../models/user.model';\n\ninterface AuthSession {\n user: User;\n accessToken: string;\n expiresAt: number;\n}\n\n/**\n * Shared AuthService for Business Apps.\n *\n * This is a **read-only signal store** — Business Apps inject it to read the\n * current user and access token. The FlyOS shell populates it after PKCE login\n * and silent refresh. Business Apps must never call setSession() directly.\n *\n * When running as a Module Federation remote, the shell and the Business App\n * share this singleton via the `@mohamedatia/fly-design-system` shared mapping\n * in federation.config.js — so the shell's populated instance is the same\n * object the Business App reads.\n *\n * When running standalone (without the shell), call initFromToken() with a\n * JWT stored in localStorage to hydrate the user.\n */\n@Injectable({ providedIn: 'root' })\nexport class AuthService {\n private _session = signal<AuthSession | null>(null);\n\n readonly currentUser = computed(() => this._session()?.user ?? null);\n readonly accessToken = computed(() => this._session()?.accessToken ?? null);\n readonly isAuthenticated = computed(() => {\n const s = this._session();\n return s !== null && s.expiresAt > Date.now();\n });\n\n /**\n * Called by the shell after a successful login or silent refresh.\n * Business Apps should not call this directly.\n */\n setSession(user: User, accessToken: string, expiresAt: number): void {\n this._session.set({ user, accessToken, expiresAt });\n }\n\n /** Clears the in-memory session (called by the shell on logout). */\n clearSession(): void {\n this._session.set(null);\n }\n\n /**\n * Standalone mode: parse a JWT from localStorage and hydrate the user.\n * Pass the storage key your app uses; embedded remotes typically set this in `APP_INITIALIZER`.\n */\n initFromToken(storageKey = 'circles_token'): void {\n const token = localStorage.getItem(storageKey);\n if (!token) return;\n try {\n const base64 = token.split('.')[1].replace(/-/g, '+').replace(/_/g, '/');\n const payload = JSON.parse(decodeURIComponent(\n atob(base64).split('').map(c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)).join('')\n )) as Record<string, unknown>;\n\n const rawRole = payload['role'];\n const roles = Array.isArray(rawRole) ? rawRole as string[] : rawRole ? [rawRole as string] : [];\n const rawApps = payload['apps'];\n const apps = Array.isArray(rawApps) ? rawApps as string[] : rawApps ? [rawApps as string] : [];\n const name = (payload['name'] as string) ?? '';\n const nameParts = name.split(' ');\n\n const user: User = {\n id: (payload['sub'] as string) ?? '',\n tenantId: (payload['tenant_id'] as string) ?? (payload['tid'] as string) ?? null,\n email: (payload['email'] as string) ?? '',\n fullName: name,\n firstName: nameParts[0] ?? '',\n lastName: nameParts.slice(1).join(' '),\n preferredLocale: (payload['locale'] as string) ?? 'en',\n roles,\n apps,\n ou: payload['ou'] as string | undefined,\n };\n\n const expiresAt = ((payload['exp'] as number) ?? 0) * 1000;\n this.setSession(user, token, expiresAt);\n } catch {\n // Invalid token — stay unauthenticated\n }\n }\n}\n","import { Injectable, computed, signal, inject, ErrorHandler } from '@angular/core';\n\n/** Locales that use RTL layout for `dir` and DS `isRtl` / `direction`. */\nexport const RTL_LOCALE_SET: ReadonlySet<string> = new Set(['ar', 'ur']);\n\nexport function isRtlLocale(lang: string): boolean {\n return RTL_LOCALE_SET.has(lang);\n}\n\nfunction escapeRegExp(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n/** Accepts flat or nested JSON objects; nested keys become dot paths. Rejects arrays and non-string leaves. */\nfunction normalizeLocaleJson(raw: unknown): Record<string, string> {\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {\n throw new Error('Locale bundle must be a JSON object');\n }\n const out: Record<string, string> = {};\n const walk = (obj: Record<string, unknown>, prefix: string): void => {\n for (const [k, v] of Object.entries(obj)) {\n const path = prefix ? `${prefix}.${k}` : k;\n if (typeof v === 'string') {\n out[path] = v;\n } else if (v !== null && typeof v === 'object' && !Array.isArray(v)) {\n walk(v as Record<string, unknown>, path);\n } else {\n throw new Error(`Locale bundle invalid value at \"${path}\" (expected string or nested object)`);\n }\n }\n };\n walk(raw as Record<string, unknown>, '');\n return out;\n}\n\n/** Options for loading a remote or standalone locale JSON bundle. */\nexport interface LoadBundleOptions {\n /** Stable id (e.g. app id from manifest); later loads replace this bundle only. */\n id: string;\n /** Base URL ending with `/`, e.g. `https://app.example.com/locale/` or `/my-remote/locale/` */\n baseUrl: string;\n lang: string;\n /** When aborted, failures are ignored (e.g. superseded language load). */\n signal?: AbortSignal;\n}\n\n/**\n * Shared I18nService for the shell and Business Apps.\n *\n * **Merge order** (later keys win): shell layer → remote bundles in registration order.\n *\n * - Shell: `setShellTranslations()` after loading `locale/{lang}.json` and API overrides.\n * - Remotes: `loadBundle()` per manifest `localeBaseUrl`.\n */\n@Injectable({ providedIn: 'root' })\nexport class I18nService {\n private readonly errorHandler = inject(ErrorHandler, { optional: true });\n\n private readonly _shell = signal<Record<string, string>>({});\n private readonly _bundles = signal<Record<string, Record<string, string>>>({});\n private readonly _bundleOrder = signal<string[]>([]);\n private readonly _locale = signal<string>('en');\n private readonly _version = signal(0);\n\n private readonly _merged = computed(() => {\n const shell = this._shell();\n const order = this._bundleOrder();\n const bundles = this._bundles();\n let out: Record<string, string> = { ...shell };\n for (const id of order) {\n const b = bundles[id];\n if (b) out = { ...out, ...b };\n }\n return out;\n });\n\n readonly locale = this._locale.asReadonly();\n readonly version = this._version.asReadonly();\n readonly isRtl = computed(() => isRtlLocale(this._locale()));\n readonly direction = computed(() => (isRtlLocale(this._locale()) ? 'rtl' : 'ltr'));\n\n private bump(): void {\n this._version.update((v) => v + 1);\n }\n\n /** Replace shell strings (desktop `locale/*.json` + `/api/i18n/desktop-shell/{lang}` merged by the shell). */\n setShellTranslations(data: Record<string, string>, lang: string): void {\n this._shell.set(data);\n this._locale.set(lang);\n this.bump();\n }\n\n setLanguage(lang: string): void {\n this._locale.set(lang);\n this.bump();\n }\n\n /**\n * Fetch `{baseUrl}{lang}.json` and store under `id`. New ids are appended to the merge order.\n */\n async loadBundle(opts: LoadBundleOptions): Promise<void> {\n const { id, baseUrl, lang, signal: abortSignal } = opts;\n try {\n const base = baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`;\n const url = `${base}${lang}.json`;\n const resp = await fetch(url, { signal: abortSignal });\n if (!resp.ok) throw new Error(`HTTP ${resp.status}`);\n const data = normalizeLocaleJson(await resp.json());\n if (abortSignal?.aborted) return;\n this._bundles.update((b) => ({ ...b, [id]: data }));\n this._bundleOrder.update((order) => (order.includes(id) ? order : [...order, id]));\n this._locale.set(lang);\n this.bump();\n } catch (e) {\n if (abortSignal?.aborted) return;\n this.errorHandler?.handleError(e);\n }\n }\n\n removeBundle(id: string): void {\n this._bundles.update((b) => {\n const next = { ...b };\n delete next[id];\n return next;\n });\n this._bundleOrder.update((order) => order.filter((x) => x !== id));\n this.bump();\n }\n\n clearRemoteBundles(): void {\n this._bundles.set({});\n this._bundleOrder.set([]);\n this.bump();\n }\n\n t(key: string, params?: Record<string, string | number>): string {\n const val = this._merged()[key] ?? key;\n if (!params) return val;\n return Object.entries(params).reduce((result, [k, v]) => {\n const safe = escapeRegExp(k);\n return result.replace(new RegExp(`{{\\\\s*${safe}\\\\s*}}`, 'g'), String(v));\n }, val);\n }\n}\n","import { Injectable, signal } from '@angular/core';\n\nexport type FlyThemeMode = 'light' | 'dark' | 'transparent';\n\n/**\n * Applies `html.light-theme` / `html.dark-theme` / `html.transparent-theme` for DS SCSS.\n * Shell and standalone Business Apps use the same service (federation singleton when shared).\n */\n@Injectable({ providedIn: 'root' })\nexport class FlyThemeService {\n readonly theme = signal<FlyThemeMode>('light');\n\n applyTheme(mode: FlyThemeMode): void {\n this.theme.set(mode);\n const html = document.documentElement;\n html.classList.remove('dark-theme', 'light-theme', 'transparent-theme');\n if (mode === 'dark') {\n html.classList.add('dark-theme');\n } else if (mode === 'transparent') {\n html.classList.add('transparent-theme');\n } else {\n html.classList.add('light-theme');\n }\n }\n}\n","import { Injectable } from '@angular/core';\n\n/** Options for opening a child window. */\nexport interface OpenWindowOptions {\n /** Unique identifier for the window instance. */\n windowId: string;\n /** Display title shown in the window chrome. */\n title: string;\n /** Route path or URL to load inside the window. */\n route: string;\n /** Initial width in pixels. Defaults to 900. */\n width?: number;\n /** Initial height in pixels. Defaults to 600. */\n height?: number;\n /** Whether the window can be resized. Defaults to true. */\n resizable?: boolean;\n /** Whether the window can be maximised. Defaults to true. */\n maximizable?: boolean;\n /** Arbitrary data passed to the child window via WINDOW_DATA. */\n data?: unknown;\n}\n\n/**\n * Abstract interface for the WindowManager.\n * The FlyOS shell provides the concrete implementation; Business Apps\n * inject this token to open, close, and focus windows without depending\n * on the shell's internal implementation.\n */\nexport abstract class WindowManagerService {\n abstract openChildWindow(options: OpenWindowOptions): void;\n abstract closeWindow(windowId: string): void;\n abstract focusWindow(windowId: string): void;\n}\n\n/**\n * No-op fallback implementation used when running a Business App in\n * standalone mode (outside the FlyOS shell). Logs a warning instead of\n * throwing so standalone dev/test flows are not broken.\n */\n@Injectable()\nexport class StandaloneWindowManagerService extends WindowManagerService {\n openChildWindow(options: OpenWindowOptions): void {\n console.warn('[WindowManagerService] openChildWindow called in standalone mode — no shell available.', options);\n }\n\n closeWindow(windowId: string): void {\n console.warn('[WindowManagerService] closeWindow called in standalone mode — no shell available.', windowId);\n }\n\n focusWindow(windowId: string): void {\n console.warn('[WindowManagerService] focusWindow called in standalone mode — no shell available.', windowId);\n }\n}\n\n","import { Pipe, PipeTransform, inject } from '@angular/core';\nimport { I18nService } from '../services/i18n.service';\n\n/**\n * Translates a key using the shared I18nService.\n *\n * @example\n * {{ 'myApp.section.title' | translate }}\n * {{ 'myApp.items_count' | translate:{ n: count() } }}\n */\n@Pipe({\n name: 'translate',\n standalone: true,\n pure: false,\n})\nexport class TranslatePipe implements PipeTransform {\n private readonly i18n = inject(I18nService);\n\n transform(key: string, params?: Record<string, string | number>): string {\n return this.i18n.t(key, params);\n }\n}\n","/*\r\n * @mohamedatia/fly-design-system — Public API\r\n * https://www.npmjs.com/package/@mohamedatia/fly-design-system\r\n *\r\n * This is the single entry point for Business App developers.\r\n * Import everything from '@mohamedatia/fly-design-system' — never from relative shell paths.\r\n *\r\n * NOTE: This package is published under @mohamedatia/fly-design-system until the @fly\r\n * npm org is created. It will be republished as @fly/design-system once available.\r\n *\r\n * v1.3.0: FlyThemeService (html theme classes), hardened I18nService (AbortSignal, ErrorHandler, RTL helpers).\r\n * v1.3.1: loadBundle normalizes locale JSON (flat or nested objects; rejects invalid leaves).\r\n * See BusinessAppsGuide/03-frontend-app.md.\r\n */\r\n\r\n// ─── Models ──────────────────────────────────────────────────────────────────\r\nexport type { DesktopApp } from './lib/models/app.model';\r\nexport type {\r\n WindowInstance,\r\n WindowState,\r\n ChildWindowData,\r\n} from './lib/models/window.model';\r\nexport { WINDOW_DATA } from './lib/models/window.model';\r\nexport type { User } from './lib/models/user.model';\r\n\r\n// ─── Remote App Registration ─────────────────────────────────────────────────\r\nexport type { RemoteAppDef } from './lib/models/remote-app.model';\r\n\r\n// ─── Services ────────────────────────────────────────────────────────────────\r\nexport { AuthService } from './lib/services/auth.service';\r\nexport type { LoadBundleOptions } from './lib/services/i18n.service';\r\nexport { I18nService, RTL_LOCALE_SET, isRtlLocale } from './lib/services/i18n.service';\r\nexport { FlyThemeService, type FlyThemeMode } from './lib/services/fly-theme.service';\r\nexport {\r\n WindowManagerService,\r\n StandaloneWindowManagerService,\r\n type OpenWindowOptions,\r\n} from './lib/services/window-manager.service';\r\n\r\n// ─── Pipes ───────────────────────────────────────────────────────────────────\r\nexport { TranslatePipe } from './lib/pipes/translate.pipe';\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Entry: tokens + html.light-theme / .dark-theme / .transparent-theme + OS auto dark.
|
|
2
|
+
// Use from shell or Business Apps:
|
|
3
|
+
// stylePreprocessorOptions.includePaths → design-system src folder
|
|
4
|
+
// @use 'scss/fly-theme' or @use 'fly-theme' when scss is on the path.
|
|
5
|
+
@use 'tokens';
|
|
6
|
+
@use 'theme-light';
|
|
7
|
+
@use 'theme-dark';
|
|
8
|
+
@use 'theme-transparent';
|
|
9
|
+
@use 'theme-auto';
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// ─── Dark Theme Variables (shared mixin to avoid duplication) ────────────────
|
|
2
|
+
@mixin dark-theme-vars {
|
|
3
|
+
--primary-light: rgba(232, 115, 42, 0.18);
|
|
4
|
+
|
|
5
|
+
--surface-ground: #0e0e10;
|
|
6
|
+
--surface-section: rgba(28, 28, 30, 0.85);
|
|
7
|
+
--surface-card: rgba(28, 28, 30, 0.7);
|
|
8
|
+
--surface-border: rgba(255, 255, 255, 0.1);
|
|
9
|
+
--surface-hover: rgba(255, 255, 255, 0.08);
|
|
10
|
+
--text-color: #f2f2f7;
|
|
11
|
+
--text-color-secondary: rgba(242, 242, 247, 0.65);
|
|
12
|
+
|
|
13
|
+
--glass-bg: rgba(25, 25, 28, 0.72);
|
|
14
|
+
--glass-bg-elevated: rgba(38, 38, 42, 0.82);
|
|
15
|
+
--glass-border: rgba(255, 255, 255, 0.12);
|
|
16
|
+
--glass-shadow: 0 8px 40px rgba(0, 0, 0, 0.35);
|
|
17
|
+
--glass-inner-glow: inset 0 1px 0 rgba(255, 255, 255, 0.08);
|
|
18
|
+
|
|
19
|
+
--window-bg: rgba(22, 22, 24, 0.55);
|
|
20
|
+
--window-radius: 46px;
|
|
21
|
+
--window-shadow: 0 24px 80px rgba(0, 0, 0, 0.4);
|
|
22
|
+
--window-border: rgba(255, 255, 255, 0.12);
|
|
23
|
+
|
|
24
|
+
--titlebar-bg: transparent;
|
|
25
|
+
|
|
26
|
+
--dock-bg: rgba(20, 20, 22, 0.55);
|
|
27
|
+
--dock-border: rgba(255, 255, 255, 0.12);
|
|
28
|
+
--dock-blur: 50px;
|
|
29
|
+
|
|
30
|
+
--menubar-bg: rgba(14, 14, 16, 0.6);
|
|
31
|
+
--menubar-border: rgba(255, 255, 255, 0.06);
|
|
32
|
+
--menubar-text: rgba(242, 242, 247, 1);
|
|
33
|
+
|
|
34
|
+
--sidebar-bg: rgba(20, 20, 22, 0.6);
|
|
35
|
+
--sidebar-border: rgba(255, 255, 255, 0.08);
|
|
36
|
+
|
|
37
|
+
--focus-ring: rgba(232, 115, 42, 0.6);
|
|
38
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// ─── Light Theme (default) ───────────────────────────────────────────────────
|
|
2
|
+
:root,
|
|
3
|
+
html.light-theme {
|
|
4
|
+
--primary-color: #E8732A;
|
|
5
|
+
--primary-hover: #D4631E;
|
|
6
|
+
--primary-light: rgba(232, 115, 42, 0.12);
|
|
7
|
+
|
|
8
|
+
--accent-green: var(--system-green);
|
|
9
|
+
--accent-red: var(--system-red);
|
|
10
|
+
--accent-blue: var(--system-blue);
|
|
11
|
+
--accent-orange: #E8732A;
|
|
12
|
+
--accent-yellow: var(--system-yellow);
|
|
13
|
+
|
|
14
|
+
--surface-ground: #f8f9fa;
|
|
15
|
+
--surface-section: #ffffff;
|
|
16
|
+
--surface-card: rgba(255, 255, 255, 0.55);
|
|
17
|
+
--surface-border: rgba(0, 0, 0, 0.06);
|
|
18
|
+
--surface-hover: rgba(0, 0, 0, 0.04);
|
|
19
|
+
--text-color: #111827;
|
|
20
|
+
--text-color-secondary: rgba(17, 24, 39, 0.55);
|
|
21
|
+
|
|
22
|
+
--glass-bg: rgba(255, 255, 255, 0.55);
|
|
23
|
+
--glass-bg-elevated: rgba(255, 255, 255, 0.72);
|
|
24
|
+
--glass-blur: 40px;
|
|
25
|
+
--glass-border: rgba(255, 255, 255, 0.35);
|
|
26
|
+
--glass-shadow: 0 8px 40px rgba(0, 0, 0, 0.08);
|
|
27
|
+
--glass-inner-glow: inset 0 1px 0 rgba(255, 255, 255, 0.5);
|
|
28
|
+
|
|
29
|
+
--card-radius: 20px;
|
|
30
|
+
--card-padding: 20px;
|
|
31
|
+
--card-gap: 14px;
|
|
32
|
+
|
|
33
|
+
--window-bg: var(--material-glass);
|
|
34
|
+
--window-radius: 46px;
|
|
35
|
+
--window-blur: var(--material-glass-blur);
|
|
36
|
+
--window-shadow: 0 24px 80px rgba(0, 0, 0, 0.12);
|
|
37
|
+
--window-border: var(--material-glass-stroke);
|
|
38
|
+
--window-specular: var(--material-glass-specular);
|
|
39
|
+
|
|
40
|
+
--titlebar-bg: transparent;
|
|
41
|
+
--titlebar-height: 92px;
|
|
42
|
+
--titlebar-padding: 0 24px;
|
|
43
|
+
|
|
44
|
+
--dock-bg: rgba(255, 255, 255, 0.15);
|
|
45
|
+
--dock-border: rgba(255, 255, 255, 0.3);
|
|
46
|
+
--dock-blur: 50px;
|
|
47
|
+
|
|
48
|
+
--menubar-bg: rgba(255, 255, 255, 0.55);
|
|
49
|
+
--menubar-border: rgba(0, 0, 0, 0.06);
|
|
50
|
+
--menubar-text: rgba(17, 24, 39, 1);
|
|
51
|
+
--menubar-blur: 40px;
|
|
52
|
+
--menubar-radius: 24px;
|
|
53
|
+
|
|
54
|
+
--sidebar-bg: rgba(255, 255, 255, 0.35);
|
|
55
|
+
--sidebar-border: rgba(255, 255, 255, 0.25);
|
|
56
|
+
--sidebar-width: 60px;
|
|
57
|
+
--sidebar-icon-size: 36px;
|
|
58
|
+
--sidebar-radius: 28px;
|
|
59
|
+
|
|
60
|
+
--focus-ring: rgba(232, 115, 42, 0.5);
|
|
61
|
+
|
|
62
|
+
// Business Apps / opaque content: :root visionOS tokens use white labels; override for light surfaces.
|
|
63
|
+
--label-primary: #111827;
|
|
64
|
+
--label-secondary: rgba(17, 24, 39, 0.65);
|
|
65
|
+
--label-tertiary: rgba(17, 24, 39, 0.45);
|
|
66
|
+
--separator-primary: rgba(0, 0, 0, 0.1);
|
|
67
|
+
--separator: var(--separator-primary);
|
|
68
|
+
--fill-secondary: #e5e7eb;
|
|
69
|
+
--fill-tertiary: rgba(0, 0, 0, 0.04);
|
|
70
|
+
--material-glass: #ffffff;
|
|
71
|
+
|
|
72
|
+
// Aliases used by some Business App SCSS (not part of core visionOS tokens).
|
|
73
|
+
--fly-color-surface: #ffffff;
|
|
74
|
+
--fly-color-surface-alt: #f3f4f6;
|
|
75
|
+
--fly-color-border: #e5e7eb;
|
|
76
|
+
--fly-color-text-muted: rgba(17, 24, 39, 0.55);
|
|
77
|
+
--fly-color-primary: var(--primary-color);
|
|
78
|
+
--fly-color-primary-hover: var(--primary-hover);
|
|
79
|
+
--fly-color-danger: var(--system-red);
|
|
80
|
+
--fly-shadow-md: 0 4px 12px rgba(0, 0, 0, 0.08);
|
|
81
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// ─── Transparent / Spatial Theme (visionOS-style) ───────────────────────────
|
|
2
|
+
html.transparent-theme {
|
|
3
|
+
--primary-light: rgba(232, 115, 42, 0.2);
|
|
4
|
+
|
|
5
|
+
--surface-ground: transparent;
|
|
6
|
+
--surface-section: rgba(255, 255, 255, 0.18);
|
|
7
|
+
--surface-card: rgba(255, 255, 255, 0.22);
|
|
8
|
+
--surface-border: rgba(255, 255, 255, 0.16);
|
|
9
|
+
--surface-hover: rgba(0, 0, 0, 0.04);
|
|
10
|
+
--text-color: var(--label-primary);
|
|
11
|
+
--text-color-secondary: var(--label-secondary);
|
|
12
|
+
|
|
13
|
+
--glass-bg: var(--material-glass);
|
|
14
|
+
--glass-bg-elevated: rgba(128, 128, 128, 0.45);
|
|
15
|
+
--glass-blur: var(--material-glass-blur);
|
|
16
|
+
--glass-border: var(--material-glass-stroke);
|
|
17
|
+
--glass-shadow: 0 12px 48px rgba(0, 0, 0, 0.15);
|
|
18
|
+
--glass-inner-glow: var(--material-glass-specular);
|
|
19
|
+
|
|
20
|
+
--window-bg: var(--material-glass);
|
|
21
|
+
--window-radius: 46px;
|
|
22
|
+
--window-blur: var(--material-glass-blur);
|
|
23
|
+
--window-shadow: 0 24px 80px rgba(0, 0, 0, 0.15);
|
|
24
|
+
--window-border: var(--material-glass-stroke);
|
|
25
|
+
|
|
26
|
+
--titlebar-bg: transparent;
|
|
27
|
+
|
|
28
|
+
--dock-bg: rgba(128, 128, 128, 0.2);
|
|
29
|
+
--dock-border: var(--material-glass-stroke);
|
|
30
|
+
--dock-blur: var(--material-glass-blur);
|
|
31
|
+
|
|
32
|
+
--menubar-bg: rgba(128, 128, 128, 0.25);
|
|
33
|
+
--menubar-border: var(--material-glass-stroke);
|
|
34
|
+
--menubar-text: var(--label-primary);
|
|
35
|
+
--menubar-blur: var(--material-glass-blur);
|
|
36
|
+
|
|
37
|
+
--sidebar-bg: rgba(128, 128, 128, 0.2);
|
|
38
|
+
--sidebar-border: var(--material-glass-stroke);
|
|
39
|
+
|
|
40
|
+
--focus-ring: rgba(232, 115, 42, 0.6);
|
|
41
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// ─── visionOS System Colors & Design Tokens ─────────────────────────────────
|
|
2
|
+
:root {
|
|
3
|
+
--system-red: #FF453A;
|
|
4
|
+
--system-orange: #FF9F0A;
|
|
5
|
+
--system-yellow: #FFD60A;
|
|
6
|
+
--system-green: #32D74B;
|
|
7
|
+
--system-mint: #66D4CF;
|
|
8
|
+
--system-teal: #6AC4DC;
|
|
9
|
+
--system-cyan: #5AC8F5;
|
|
10
|
+
--system-blue: #0A84FF;
|
|
11
|
+
--system-indigo: #5E5CE6;
|
|
12
|
+
--system-purple: #BF5AF2;
|
|
13
|
+
--system-pink: #FF375F;
|
|
14
|
+
--system-brown: #AC8E68;
|
|
15
|
+
--system-gray: #98989D;
|
|
16
|
+
|
|
17
|
+
// ── Semantic Status Colors ──
|
|
18
|
+
--status-success: var(--system-green);
|
|
19
|
+
--status-warning: var(--system-orange);
|
|
20
|
+
--status-error: var(--system-red);
|
|
21
|
+
--status-info: var(--system-blue);
|
|
22
|
+
--status-pending: var(--system-yellow);
|
|
23
|
+
|
|
24
|
+
// ── visionOS Typography ──
|
|
25
|
+
--font-family: 'SF Pro Display', 'SF Pro', 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
26
|
+
--font-xl-title1: 700 48px/56px var(--font-family);
|
|
27
|
+
--font-xl-title2: 700 38px/46px var(--font-family);
|
|
28
|
+
--font-large-title: 700 29px/38px var(--font-family);
|
|
29
|
+
--font-title1: 700 24px/32px var(--font-family);
|
|
30
|
+
--font-title2: 700 22px/28px var(--font-family);
|
|
31
|
+
--font-title3: 700 19px/24px var(--font-family);
|
|
32
|
+
--font-headline: 700 17px/22px var(--font-family);
|
|
33
|
+
--font-body: 510 17px/22px var(--font-family);
|
|
34
|
+
--font-callout: 590 15px/20px var(--font-family);
|
|
35
|
+
--font-subheadline: 400 15px/20px var(--font-family);
|
|
36
|
+
--font-footnote: 510 13px/18px var(--font-family);
|
|
37
|
+
--font-caption1: 510 12px/16px var(--font-family);
|
|
38
|
+
--font-caption2: 510 12px/16px var(--font-family);
|
|
39
|
+
|
|
40
|
+
// ── visionOS Label Colors ──
|
|
41
|
+
--label-primary: rgba(255, 255, 255, 0.96);
|
|
42
|
+
--label-secondary: rgba(255, 255, 255, 0.56);
|
|
43
|
+
--label-tertiary: rgba(255, 255, 255, 0.36);
|
|
44
|
+
|
|
45
|
+
// ── visionOS Materials ──
|
|
46
|
+
--material-glass: rgba(128, 128, 128, 0.3);
|
|
47
|
+
--material-glass-blur: 50px;
|
|
48
|
+
--material-glass-stroke: rgba(255, 255, 255, 0.4);
|
|
49
|
+
--material-glass-specular: inset 0px -0.5px 1px rgba(255, 255, 255, 0.3),
|
|
50
|
+
inset 0px -0.5px 1px rgba(255, 255, 255, 0.25),
|
|
51
|
+
inset 1px 1.5px 4px rgba(0, 0, 0, 0.08),
|
|
52
|
+
inset 1px 1.5px 4px rgba(0, 0, 0, 0.1);
|
|
53
|
+
|
|
54
|
+
--material-recessed-bg: linear-gradient(rgba(208, 208, 208, 0.5), rgba(208, 208, 208, 0.5)),
|
|
55
|
+
linear-gradient(rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.1));
|
|
56
|
+
--material-recessed-shadow: inset 1px 1.5px 4px rgba(0, 0, 0, 0.1),
|
|
57
|
+
inset 1px 1.5px 4px rgba(0, 0, 0, 0.08),
|
|
58
|
+
inset 0px -0.5px 1px rgba(255, 255, 255, 0.25),
|
|
59
|
+
inset 0px -0.5px 1px rgba(255, 255, 255, 0.3);
|
|
60
|
+
|
|
61
|
+
--material-thin: rgba(255, 255, 255, 0.06);
|
|
62
|
+
--material-regular: rgba(255, 255, 255, 0.12);
|
|
63
|
+
--material-thick: rgba(255, 255, 255, 0.2);
|
|
64
|
+
|
|
65
|
+
--control-idle: rgba(255, 255, 255, 0.06);
|
|
66
|
+
--control-hover: rgba(255, 255, 255, 0.12);
|
|
67
|
+
--control-selected: rgba(255, 255, 255, 1);
|
|
68
|
+
--control-disabled: rgba(255, 255, 255, 0.04);
|
|
69
|
+
|
|
70
|
+
--separator: rgba(255, 255, 255, 0.12);
|
|
71
|
+
|
|
72
|
+
--blur-shadow-small: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
73
|
+
|
|
74
|
+
// ── visionOS Button Tokens ──
|
|
75
|
+
--btn-font-weight: 590;
|
|
76
|
+
--btn-symbol-font-weight: 510;
|
|
77
|
+
--btn-radius-capsule: 500px;
|
|
78
|
+
--btn-radius-rect-lg: 16px;
|
|
79
|
+
--btn-radius-rect-sm: 8px;
|
|
80
|
+
|
|
81
|
+
--btn-sm-height: 32px;
|
|
82
|
+
--btn-sm-padding: 0 12px;
|
|
83
|
+
--btn-sm-font-size: 15px;
|
|
84
|
+
--btn-sm-line-height: 20px;
|
|
85
|
+
--btn-sm-symbol-size: 17px;
|
|
86
|
+
--btn-sm-gap: 2px;
|
|
87
|
+
|
|
88
|
+
--btn-reg-height: 44px;
|
|
89
|
+
--btn-reg-padding: 0 20px;
|
|
90
|
+
--btn-reg-font-size: 17px;
|
|
91
|
+
--btn-reg-line-height: 22px;
|
|
92
|
+
--btn-reg-symbol-size: 19px;
|
|
93
|
+
--btn-reg-gap: 6px;
|
|
94
|
+
|
|
95
|
+
--btn-lg-height: 52px;
|
|
96
|
+
--btn-lg-padding: 0 25px;
|
|
97
|
+
--btn-lg-font-size: 19px;
|
|
98
|
+
--btn-lg-line-height: 24px;
|
|
99
|
+
--btn-lg-symbol-size: 22px;
|
|
100
|
+
--btn-lg-gap: 6px;
|
|
101
|
+
|
|
102
|
+
--btn-symbol-mini: 28px;
|
|
103
|
+
--btn-symbol-mini-font: 13px;
|
|
104
|
+
--btn-symbol-sm: 36px;
|
|
105
|
+
--btn-symbol-sm-font: 15px;
|
|
106
|
+
--btn-symbol-reg: 44px;
|
|
107
|
+
--btn-symbol-reg-font: 19px;
|
|
108
|
+
--btn-symbol-lg: 52px;
|
|
109
|
+
--btn-symbol-lg-font: 22px;
|
|
110
|
+
--btn-symbol-xl: 60px;
|
|
111
|
+
--btn-symbol-xl-font: 26px;
|
|
112
|
+
|
|
113
|
+
--btn-platter-lighten: rgba(255, 255, 255, 0.06);
|
|
114
|
+
--btn-platter-dodge: rgba(94, 94, 94, 0.18);
|
|
115
|
+
--btn-hover-grad-white: rgba(255, 255, 255, 0.07);
|
|
116
|
+
--btn-hover-grad-gray: rgba(94, 94, 94, 0.14);
|
|
117
|
+
--btn-disabled-lighten: rgba(255, 255, 255, 0.04);
|
|
118
|
+
--btn-disabled-dodge: rgba(94, 94, 94, 0.07);
|
|
119
|
+
--btn-disabled-text: rgba(112, 112, 112, 0.5);
|
|
120
|
+
--btn-selected-bg: rgba(255, 255, 255, 0.96);
|
|
121
|
+
--btn-selected-text: #000000;
|
|
122
|
+
|
|
123
|
+
--link-color: #3CD3FE;
|
|
124
|
+
--link-height: 26px;
|
|
125
|
+
--link-font-size: 13px;
|
|
126
|
+
}
|
|
@@ -50,6 +50,8 @@ interface WindowInstance {
|
|
|
50
50
|
isUnmaximizing?: boolean;
|
|
51
51
|
parentWindowId?: string;
|
|
52
52
|
childData?: ChildWindowData;
|
|
53
|
+
/** Populated when remote `loadComponent()` fails (e.g. federation). */
|
|
54
|
+
remoteLoadError?: string;
|
|
53
55
|
}
|
|
54
56
|
declare const WINDOW_DATA: InjectionToken<WindowInstance>;
|
|
55
57
|
|
|
@@ -77,6 +79,8 @@ interface RemoteAppDef {
|
|
|
77
79
|
remoteEntry: string;
|
|
78
80
|
/** The module key exposed by the remote's federation.config.js, e.g. "./MyAppComponent" */
|
|
79
81
|
exposedModule: string;
|
|
82
|
+
/** Base URL for locale JSON (`{base}{lang}.json`), e.g. `/my-remote/locale/` or `https://app/locale/` */
|
|
83
|
+
localeBaseUrl?: string;
|
|
80
84
|
nameKey: string;
|
|
81
85
|
descriptionKey: string;
|
|
82
86
|
icon: string;
|
|
@@ -123,58 +127,73 @@ declare class AuthService {
|
|
|
123
127
|
clearSession(): void;
|
|
124
128
|
/**
|
|
125
129
|
* Standalone mode: parse a JWT from localStorage and hydrate the user.
|
|
126
|
-
*
|
|
130
|
+
* Pass the storage key your app uses; embedded remotes typically set this in `APP_INITIALIZER`.
|
|
127
131
|
*/
|
|
128
132
|
initFromToken(storageKey?: string): void;
|
|
129
133
|
static ɵfac: i0.ɵɵFactoryDeclaration<AuthService, never>;
|
|
130
134
|
static ɵprov: i0.ɵɵInjectableDeclaration<AuthService>;
|
|
131
135
|
}
|
|
132
136
|
|
|
137
|
+
/** Locales that use RTL layout for `dir` and DS `isRtl` / `direction`. */
|
|
138
|
+
declare const RTL_LOCALE_SET: ReadonlySet<string>;
|
|
139
|
+
declare function isRtlLocale(lang: string): boolean;
|
|
140
|
+
/** Options for loading a remote or standalone locale JSON bundle. */
|
|
141
|
+
interface LoadBundleOptions {
|
|
142
|
+
/** Stable id (e.g. app id from manifest); later loads replace this bundle only. */
|
|
143
|
+
id: string;
|
|
144
|
+
/** Base URL ending with `/`, e.g. `https://app.example.com/locale/` or `/my-remote/locale/` */
|
|
145
|
+
baseUrl: string;
|
|
146
|
+
lang: string;
|
|
147
|
+
/** When aborted, failures are ignored (e.g. superseded language load). */
|
|
148
|
+
signal?: AbortSignal;
|
|
149
|
+
}
|
|
133
150
|
/**
|
|
134
|
-
* Shared I18nService for Business Apps.
|
|
135
|
-
*
|
|
136
|
-
* Loads locale JSON files from a configurable base URL and exposes a `t()`
|
|
137
|
-
* method for key-based translation with optional interpolation.
|
|
151
|
+
* Shared I18nService for the shell and Business Apps.
|
|
138
152
|
*
|
|
139
|
-
*
|
|
140
|
-
* whenever the user switches language — the singleton is shared via Module
|
|
141
|
-
* Federation so Business Apps automatically see the updated translations.
|
|
153
|
+
* **Merge order** (later keys win): shell layer → remote bundles in registration order.
|
|
142
154
|
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
155
|
+
* - Shell: `setShellTranslations()` after loading `locale/{lang}.json` and API overrides.
|
|
156
|
+
* - Remotes: `loadBundle()` per manifest `localeBaseUrl`.
|
|
145
157
|
*/
|
|
146
158
|
declare class I18nService {
|
|
147
|
-
private
|
|
148
|
-
private
|
|
159
|
+
private readonly errorHandler;
|
|
160
|
+
private readonly _shell;
|
|
161
|
+
private readonly _bundles;
|
|
162
|
+
private readonly _bundleOrder;
|
|
163
|
+
private readonly _locale;
|
|
164
|
+
private readonly _version;
|
|
165
|
+
private readonly _merged;
|
|
149
166
|
readonly locale: i0.Signal<string>;
|
|
167
|
+
readonly version: i0.Signal<number>;
|
|
150
168
|
readonly isRtl: i0.Signal<boolean>;
|
|
151
169
|
readonly direction: i0.Signal<"rtl" | "ltr">;
|
|
170
|
+
private bump;
|
|
171
|
+
/** Replace shell strings (desktop `locale/*.json` + `/api/i18n/desktop-shell/{lang}` merged by the shell). */
|
|
172
|
+
setShellTranslations(data: Record<string, string>, lang: string): void;
|
|
173
|
+
setLanguage(lang: string): void;
|
|
152
174
|
/**
|
|
153
|
-
*
|
|
154
|
-
* the current translation map. Subsequent calls for the same language
|
|
155
|
-
* replace the previous entries for that language.
|
|
156
|
-
*
|
|
157
|
-
* @param localeBaseUrl Base URL ending with `/`, e.g. `https://cdn.example.com/locale/`
|
|
158
|
-
* @param lang Locale code, e.g. `'en'`, `'ar'`
|
|
159
|
-
*/
|
|
160
|
-
loadLocale(localeBaseUrl: string, lang: string): Promise<void>;
|
|
161
|
-
/**
|
|
162
|
-
* Merges a pre-loaded translations object directly (useful when the shell
|
|
163
|
-
* has already fetched the locale and wants to push it to Business Apps).
|
|
164
|
-
*/
|
|
165
|
-
mergeTranslations(translations: Record<string, string>, lang?: string): void;
|
|
166
|
-
/**
|
|
167
|
-
* Translates a key, with optional `{{param}}` interpolation.
|
|
168
|
-
*
|
|
169
|
-
* @example
|
|
170
|
-
* i18n.t('circles.signals.title')
|
|
171
|
-
* i18n.t('circles.items_count', { n: '5' })
|
|
175
|
+
* Fetch `{baseUrl}{lang}.json` and store under `id`. New ids are appended to the merge order.
|
|
172
176
|
*/
|
|
177
|
+
loadBundle(opts: LoadBundleOptions): Promise<void>;
|
|
178
|
+
removeBundle(id: string): void;
|
|
179
|
+
clearRemoteBundles(): void;
|
|
173
180
|
t(key: string, params?: Record<string, string | number>): string;
|
|
174
181
|
static ɵfac: i0.ɵɵFactoryDeclaration<I18nService, never>;
|
|
175
182
|
static ɵprov: i0.ɵɵInjectableDeclaration<I18nService>;
|
|
176
183
|
}
|
|
177
184
|
|
|
185
|
+
type FlyThemeMode = 'light' | 'dark' | 'transparent';
|
|
186
|
+
/**
|
|
187
|
+
* Applies `html.light-theme` / `html.dark-theme` / `html.transparent-theme` for DS SCSS.
|
|
188
|
+
* Shell and standalone Business Apps use the same service (federation singleton when shared).
|
|
189
|
+
*/
|
|
190
|
+
declare class FlyThemeService {
|
|
191
|
+
readonly theme: i0.WritableSignal<FlyThemeMode>;
|
|
192
|
+
applyTheme(mode: FlyThemeMode): void;
|
|
193
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<FlyThemeService, never>;
|
|
194
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<FlyThemeService>;
|
|
195
|
+
}
|
|
196
|
+
|
|
178
197
|
/** Options for opening a child window. */
|
|
179
198
|
interface OpenWindowOptions {
|
|
180
199
|
/** Unique identifier for the window instance. */
|
|
@@ -222,8 +241,8 @@ declare class StandaloneWindowManagerService extends WindowManagerService {
|
|
|
222
241
|
* Translates a key using the shared I18nService.
|
|
223
242
|
*
|
|
224
243
|
* @example
|
|
225
|
-
* {{ '
|
|
226
|
-
* {{ '
|
|
244
|
+
* {{ 'myApp.section.title' | translate }}
|
|
245
|
+
* {{ 'myApp.items_count' | translate:{ n: count() } }}
|
|
227
246
|
*/
|
|
228
247
|
declare class TranslatePipe implements PipeTransform {
|
|
229
248
|
private readonly i18n;
|
|
@@ -232,6 +251,6 @@ declare class TranslatePipe implements PipeTransform {
|
|
|
232
251
|
static ɵpipe: i0.ɵɵPipeDeclaration<TranslatePipe, "translate", true>;
|
|
233
252
|
}
|
|
234
253
|
|
|
235
|
-
export { AuthService, I18nService, StandaloneWindowManagerService, TranslatePipe, WINDOW_DATA, WindowManagerService };
|
|
236
|
-
export type { ChildWindowData, DesktopApp, OpenWindowOptions, RemoteAppDef, User, WindowInstance, WindowState };
|
|
254
|
+
export { AuthService, FlyThemeService, I18nService, RTL_LOCALE_SET, StandaloneWindowManagerService, TranslatePipe, WINDOW_DATA, WindowManagerService, isRtlLocale };
|
|
255
|
+
export type { ChildWindowData, DesktopApp, FlyThemeMode, LoadBundleOptions, OpenWindowOptions, RemoteAppDef, User, WindowInstance, WindowState };
|
|
237
256
|
//# sourceMappingURL=mohamedatia-fly-design-system.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mohamedatia-fly-design-system.d.ts","sources":["../../../projects/design-system/src/lib/models/app.model.ts","../../../projects/design-system/src/lib/models/window.model.ts","../../../projects/design-system/src/lib/models/user.model.ts","../../../projects/design-system/src/lib/models/remote-app.model.ts","../../../projects/design-system/src/lib/services/auth.service.ts","../../../projects/design-system/src/lib/services/i18n.service.ts","../../../projects/design-system/src/lib/services/window-manager.service.ts","../../../projects/design-system/src/lib/pipes/translate.pipe.ts"],"mappings":";;;UAEiB,UAAU;;;;;;yBAMJ,OAAO,CAAC,IAAI;AACjC;;;;AACA;;;;;;;AAID;;ACZK,KAAM,WAAW;UAEN,eAAe;AAC9B,oBAAgB,IAAI;AACpB;AACD;UAEgB,cAAc;;;;;;WAMtB,WAAW;AAClB;;;;AACA;;;;;;;;;;;;gBASY,eAAe;
|
|
1
|
+
{"version":3,"file":"mohamedatia-fly-design-system.d.ts","sources":["../../../projects/design-system/src/lib/models/app.model.ts","../../../projects/design-system/src/lib/models/window.model.ts","../../../projects/design-system/src/lib/models/user.model.ts","../../../projects/design-system/src/lib/models/remote-app.model.ts","../../../projects/design-system/src/lib/services/auth.service.ts","../../../projects/design-system/src/lib/services/i18n.service.ts","../../../projects/design-system/src/lib/services/fly-theme.service.ts","../../../projects/design-system/src/lib/services/window-manager.service.ts","../../../projects/design-system/src/lib/pipes/translate.pipe.ts"],"mappings":";;;UAEiB,UAAU;;;;;;yBAMJ,OAAO,CAAC,IAAI;AACjC;;;;AACA;;;;;;;AAID;;ACZK,KAAM,WAAW;UAEN,eAAe;AAC9B,oBAAgB,IAAI;AACpB;AACD;UAEgB,cAAc;;;;;;WAMtB,WAAW;AAClB;;;;AACA;;;;;;;;;;;;gBASY,eAAe;;;AAG5B;AAED,cAAa,WAAW,EAAA,cAAA,CAAA,cAAA;;UC/BP,IAAI;;AAEnB;;;;;;;;;AASD;;ACTD;;;;AAIG;UACc,YAAY;;;;;;;;;;;;AAY3B;;;;AACA;;;;;AAEA,cAAU,UAAU;;AAErB;;ACfD;;;;;;;;;;;;;;AAcG;AACH,cACa,WAAW;;0BAGFA,EAAA,CAAA,MAAA,CAAA,IAAA;0BACAA,EAAA,CAAA,MAAA;8BACIA,EAAA,CAAA,MAAA;AAKxB;;;AAGG;AACH,qBAAiB,IAAI;;AAKrB;AAIA;;;AAGG;AACH;yCA3BW,WAAW;6CAAX,WAAW;AA8DvB;;ACrFD;AACA,cAAa,cAAc,EAAE,WAAW;AAExC,iBAAgB,WAAW;AA8B3B;UACiB,iBAAiB;;;;;;;aAOvB,WAAW;AACrB;AAED;;;;;;;AAOG;AACH,cACa,WAAW;AACtB;AAEA;AACA;AACA;AACA;AACA;AAEA;qBAYeA,EAAA,CAAA,MAAA;sBACCA,EAAA,CAAA,MAAA;oBACFA,EAAA,CAAA,MAAA;wBACIA,EAAA,CAAA,MAAA;AAElB;;AAKA,+BAA2B,MAAM;AAMjC;AAKA;;AAEG;qBACoB,iBAAiB,GAAG,OAAO;AAmBlD;AAUA;AAMA,4BAAwB,MAAM;yCAhFnB,WAAW;6CAAX,WAAW;AAwFvB;;AC7IK,KAAM,YAAY;AAExB;;;AAGG;AACH,cACa,eAAe;oBACZA,EAAA,CAAA,cAAA,CAAA,YAAA;AAEd,qBAAiB,YAAY;yCAHlB,eAAe;6CAAf,eAAe;AAe3B;;ACtBD;UACiB,iBAAiB;;;;;;;;;;;;;;;;;AAiBjC;AAED;;;;;AAKG;AACH,uBAAsB,oBAAoB;AACxC,sCAAkC,iBAAiB;AACnD;AACA;AACD;AAED;;;;AAIG;AACH,cACa,8BAA+B,SAAQ,oBAAoB;AACtE,6BAAyB,iBAAiB;AAI1C;AAIA;yCATW,8BAA8B;6CAA9B,8BAA8B;AAY1C;;ACjDD;;;;;;AAMG;AACH,cAKa,aAAc,YAAW,aAAa;AACjD;AAEA,oCAAgC,MAAM;yCAH3B,aAAa;uCAAb,aAAa;AAMzB;;;;","names":["_angular_core"]}
|