@simplysm/angular 14.0.95 → 14.0.97
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/dist/controls/list/sd-list.js +2 -2
- package/dist/core/busy/sd-busy.provider.d.ts.map +1 -1
- package/dist/core/busy/sd-busy.provider.js +5 -1
- package/dist/core/config/sd-local-storage.provider.d.ts +2 -0
- package/dist/core/config/sd-local-storage.provider.d.ts.map +1 -1
- package/dist/core/config/sd-local-storage.provider.js +10 -1
- package/dist/core/error-handler/sd-global-error-handler.plugin.d.ts +1 -0
- package/dist/core/error-handler/sd-global-error-handler.plugin.d.ts.map +1 -1
- package/dist/core/error-handler/sd-global-error-handler.plugin.js +8 -1
- package/dist/core/provideSdAngular.d.ts.map +1 -1
- package/dist/core/provideSdAngular.js +11 -2
- package/dist/core/ripple/setupRipple.d.ts.map +1 -1
- package/dist/core/ripple/setupRipple.js +5 -1
- package/dist/core/setupBgTheme.d.ts.map +1 -1
- package/dist/core/setupBgTheme.js +5 -1
- package/dist/features/theme/sd-theme-provider.d.ts.map +1 -1
- package/dist/features/theme/sd-theme-provider.js +5 -1
- package/package.json +13 -13
- package/scss/commons/_variables.scss +6 -6
- package/src/controls/list/sd-list.ts +1 -1
- package/src/core/busy/sd-busy.provider.ts +5 -0
- package/src/core/config/sd-local-storage.provider.ts +8 -1
- package/src/core/error-handler/sd-global-error-handler.plugin.ts +9 -0
- package/src/core/provideSdAngular.ts +11 -1
- package/src/core/ripple/setupRipple.ts +5 -1
- package/src/core/setupBgTheme.ts +5 -1
- package/src/features/theme/sd-theme-provider.ts +5 -1
- package/styles.css +6 -6
|
@@ -9,7 +9,7 @@ export class SdList {
|
|
|
9
9
|
} }, inputs: { inset: [1, "inset"] }, ngContentSelectors: _c0, decls: 1, vars: 0, template: function SdList_Template(rf, ctx) { if (rf & 1) {
|
|
10
10
|
i0.ɵɵprojectionDef();
|
|
11
11
|
i0.ɵɵprojection(0);
|
|
12
|
-
} }, styles: ["sd-list {\n display: flex;\n flex-direction: column;\n user-select: none;\n background: var(--control-color);\n border: 1px solid var(--border-color-default);\n border-radius: var(--border-radius-default);\n padding: var(--gap-sm);\n gap: var(--gap-xs);\n}\nsd-list[data-sd-inset=true] {\n background: transparent;\n border: none;\n}\nsd-list[data-sd-inset=true] sd-list {\n background: transparent;\n}\nsd-list sd-list-item sd-list {\n border-color: transparent;\n border-radius: 0;\n padding:
|
|
12
|
+
} }, styles: ["sd-list {\n display: flex;\n flex-direction: column;\n user-select: none;\n background: var(--control-color);\n border: 1px solid var(--border-color-default);\n border-radius: var(--border-radius-default);\n padding: var(--gap-sm);\n gap: var(--gap-xs);\n}\nsd-list[data-sd-inset=true] {\n background: transparent;\n border: none;\n}\nsd-list[data-sd-inset=true] sd-list {\n background: transparent;\n}\nsd-list sd-list-item sd-list {\n border-color: transparent;\n border-radius: 0;\n padding: 0;\n background: transparent;\n}"], encapsulation: 2, changeDetection: 0 });
|
|
13
13
|
}
|
|
14
14
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(SdList, [{
|
|
15
15
|
type: Component,
|
|
@@ -17,6 +17,6 @@ export class SdList {
|
|
|
17
17
|
<ng-content />
|
|
18
18
|
`, host: {
|
|
19
19
|
"[attr.data-sd-inset]": "inset()",
|
|
20
|
-
}, styles: ["sd-list {\n display: flex;\n flex-direction: column;\n user-select: none;\n background: var(--control-color);\n border: 1px solid var(--border-color-default);\n border-radius: var(--border-radius-default);\n padding: var(--gap-sm);\n gap: var(--gap-xs);\n}\nsd-list[data-sd-inset=true] {\n background: transparent;\n border: none;\n}\nsd-list[data-sd-inset=true] sd-list {\n background: transparent;\n}\nsd-list sd-list-item sd-list {\n border-color: transparent;\n border-radius: 0;\n padding:
|
|
20
|
+
}, styles: ["sd-list {\n display: flex;\n flex-direction: column;\n user-select: none;\n background: var(--control-color);\n border: 1px solid var(--border-color-default);\n border-radius: var(--border-radius-default);\n padding: var(--gap-sm);\n gap: var(--gap-xs);\n}\nsd-list[data-sd-inset=true] {\n background: transparent;\n border: none;\n}\nsd-list[data-sd-inset=true] sd-list {\n background: transparent;\n}\nsd-list sd-list-item sd-list {\n border-color: transparent;\n border-radius: 0;\n padding: 0;\n background: transparent;\n}"] }]
|
|
21
21
|
}], null, { inset: [{ type: i0.Input, args: [{ isSignal: true, alias: "inset", required: false }] }] }); })();
|
|
22
22
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(SdList, { className: "SdList", filePath: "packages/angular/src/controls/list/sd-list.ts", lineNumber: 53 }); })();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sd-busy.provider.d.ts","sourceRoot":"","sources":["../../../src/core/busy/sd-busy.provider.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"sd-busy.provider.d.ts","sourceRoot":"","sources":["../../../src/core/busy/sd-busy.provider.ts"],"names":[],"mappings":"AAAA,OAAO,EASL,KAAK,YAAY,EAClB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;;AAEtD,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,KAAK,GAAG,MAAM,CAAC;AAEpD,qBACa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA0B;IAClD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA+B;IAE5D,IAAI,qDAA6B;IACjC,eAAe,iDAAa;IAE5B,OAAO,CAAC,aAAa,CAA4C;IAEjE,IAAI,YAAY,IAAI,YAAY,CAAC,eAAe,CAAC,CAiBhD;;yCA1BU,cAAc;6CAAd,cAAc;CA8C1B"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { ApplicationRef, createComponent, effect, EnvironmentInjector, inject, Injectable, signal, } from "@angular/core";
|
|
1
|
+
import { ApplicationRef, createComponent, effect, EnvironmentInjector, inject, Injectable, PLATFORM_ID, signal, } from "@angular/core";
|
|
2
|
+
import { isPlatformBrowser } from "@angular/common";
|
|
2
3
|
import { SdBusyContainer } from "./sd-busy-container";
|
|
3
4
|
import * as i0 from "@angular/core";
|
|
4
5
|
export class SdBusyProvider {
|
|
@@ -25,6 +26,9 @@ export class SdBusyProvider {
|
|
|
25
26
|
return this._containerRef;
|
|
26
27
|
}
|
|
27
28
|
constructor() {
|
|
29
|
+
// SSR(프리렌더) 가드: busy 오버레이는 document 직접 조작 — 브라우저 전용
|
|
30
|
+
if (!isPlatformBrowser(inject(PLATFORM_ID)))
|
|
31
|
+
return;
|
|
28
32
|
effect(() => {
|
|
29
33
|
const busy = this.globalBusyCount() > 0;
|
|
30
34
|
// busy 가 처음 true 가 되기 전엔 컨테이너를 생성하지 않음 (lazy)
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import * as i0 from "@angular/core";
|
|
2
2
|
export declare class SdLocalStorageProvider<T> {
|
|
3
3
|
private readonly _sdAngularConfig;
|
|
4
|
+
/** SSR(프리렌더) 가드: 서버에는 localStorage가 없음 — set/remove는 무동작, get은 "값 없음" */
|
|
5
|
+
private readonly _isBrowser;
|
|
4
6
|
set<K extends keyof T & string>(key: K, value: T[K]): void;
|
|
5
7
|
get<K extends keyof T & string>(key: K): T[K] | undefined;
|
|
6
8
|
remove(key: keyof T & string): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sd-local-storage.provider.d.ts","sourceRoot":"","sources":["../../../src/core/config/sd-local-storage.provider.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"sd-local-storage.provider.d.ts","sourceRoot":"","sources":["../../../src/core/config/sd-local-storage.provider.ts"],"names":[],"mappings":";AAIA,qBACa,sBAAsB,CAAC,CAAC;IACnC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAmC;IAEpE,yEAAyE;IACzE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA0C;IAErE,GAAG,CAAC,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;IAKnD,GAAG,CAAC,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,SAAS;IAWzD,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,MAAM;yCAtBjB,sBAAsB;6CAAtB,sBAAsB;CA0BlC"}
|
|
@@ -1,12 +1,19 @@
|
|
|
1
|
-
import { inject, Injectable } from "@angular/core";
|
|
1
|
+
import { inject, Injectable, PLATFORM_ID } from "@angular/core";
|
|
2
|
+
import { isPlatformBrowser } from "@angular/common";
|
|
2
3
|
import { SdAngularConfigProvider } from "./sd-angular-config.provider";
|
|
3
4
|
import * as i0 from "@angular/core";
|
|
4
5
|
export class SdLocalStorageProvider {
|
|
5
6
|
_sdAngularConfig = inject(SdAngularConfigProvider);
|
|
7
|
+
/** SSR(프리렌더) 가드: 서버에는 localStorage가 없음 — set/remove는 무동작, get은 "값 없음" */
|
|
8
|
+
_isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
|
|
6
9
|
set(key, value) {
|
|
10
|
+
if (!this._isBrowser)
|
|
11
|
+
return;
|
|
7
12
|
localStorage.setItem(`${this._sdAngularConfig.clientName}.${key}`, JSON.stringify(value));
|
|
8
13
|
}
|
|
9
14
|
get(key) {
|
|
15
|
+
if (!this._isBrowser)
|
|
16
|
+
return undefined;
|
|
10
17
|
const json = localStorage.getItem(`${this._sdAngularConfig.clientName}.${key}`);
|
|
11
18
|
if (json == null)
|
|
12
19
|
return undefined;
|
|
@@ -18,6 +25,8 @@ export class SdLocalStorageProvider {
|
|
|
18
25
|
}
|
|
19
26
|
}
|
|
20
27
|
remove(key) {
|
|
28
|
+
if (!this._isBrowser)
|
|
29
|
+
return;
|
|
21
30
|
localStorage.removeItem(`${this._sdAngularConfig.clientName}.${key}`);
|
|
22
31
|
}
|
|
23
32
|
static ɵfac = function SdLocalStorageProvider_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || SdLocalStorageProvider)(); };
|
|
@@ -3,6 +3,7 @@ import * as i0 from "@angular/core";
|
|
|
3
3
|
export declare class SdGlobalErrorHandlerPlugin implements ErrorHandler {
|
|
4
4
|
private readonly _envInjector;
|
|
5
5
|
private readonly _sdSystemLog;
|
|
6
|
+
private readonly _isBrowser;
|
|
6
7
|
private _hasDisplayedError;
|
|
7
8
|
handleError(event: unknown): false | undefined;
|
|
8
9
|
private _displayErrorMessage;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sd-global-error-handler.plugin.d.ts","sourceRoot":"","sources":["../../../src/core/error-handler/sd-global-error-handler.plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,YAAY,
|
|
1
|
+
{"version":3,"file":"sd-global-error-handler.plugin.d.ts","sourceRoot":"","sources":["../../../src/core/error-handler/sd-global-error-handler.plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,YAAY,EAKb,MAAM,eAAe,CAAC;;AAOvB,qBACa,0BAA2B,YAAW,YAAY;IAC7D,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA+B;IAC5D,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA+B;IAC5D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA0C;IACrE,OAAO,CAAC,kBAAkB,CAAS;IAEnC,WAAW,CAAC,KAAK,EAAE,OAAO;IAgF1B,OAAO,CAAC,oBAAoB;yCAtFjB,0BAA0B;6CAA1B,0BAA0B;CA8HtC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { ApplicationRef, EnvironmentInjector, ErrorHandler, inject, Injectable, isDevMode, } from "@angular/core";
|
|
1
|
+
import { ApplicationRef, EnvironmentInjector, ErrorHandler, inject, Injectable, isDevMode, PLATFORM_ID, } from "@angular/core";
|
|
2
|
+
import { isPlatformBrowser } from "@angular/common";
|
|
2
3
|
import { createLogger } from "@simplysm/core-common";
|
|
3
4
|
import { SdSystemLogProvider } from "../config/sd-system-log.provider";
|
|
4
5
|
import * as i0 from "@angular/core";
|
|
@@ -6,8 +7,14 @@ const logger = createLogger("angular:error-handler");
|
|
|
6
7
|
export class SdGlobalErrorHandlerPlugin {
|
|
7
8
|
_envInjector = inject(EnvironmentInjector);
|
|
8
9
|
_sdSystemLog = inject(SdSystemLogProvider);
|
|
10
|
+
_isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
|
|
9
11
|
_hasDisplayedError = false;
|
|
10
12
|
handleError(event) {
|
|
13
|
+
// SSR(프리렌더) 가드: 서버에는 화면 오버레이·브라우저 전역 이벤트 타입이 없음 — 로그만 남김
|
|
14
|
+
if (!this._isBrowser) {
|
|
15
|
+
logger.error(event);
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
11
18
|
try {
|
|
12
19
|
if (event instanceof PromiseRejectionEvent) {
|
|
13
20
|
const reason = event.reason;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provideSdAngular.d.ts","sourceRoot":"","sources":["../../src/core/provideSdAngular.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"provideSdAngular.d.ts","sourceRoot":"","sources":["../../src/core/provideSdAngular.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAmC1D,wBAAgB,gBAAgB,CAAC,GAAG,EAAE;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,GAAG,oBAAoB,CAqJlF"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { DestroyRef, effect, EnvironmentInjector, ErrorHandler, inject, makeEnvironmentProviders, provideAppInitializer, provideEnvironmentInitializer, provideZonelessChangeDetection, } from "@angular/core";
|
|
2
|
-
import { IMAGE_CONFIG } from "@angular/common";
|
|
1
|
+
import { DestroyRef, effect, EnvironmentInjector, ErrorHandler, inject, makeEnvironmentProviders, PLATFORM_ID, provideAppInitializer, provideEnvironmentInitializer, provideZonelessChangeDetection, } from "@angular/core";
|
|
2
|
+
import { IMAGE_CONFIG, isPlatformBrowser } from "@angular/common";
|
|
3
3
|
import { EVENT_MANAGER_PLUGINS } from "@angular/platform-browser";
|
|
4
4
|
import { toSignal } from "@angular/core/rxjs-interop";
|
|
5
5
|
import { NavigationCancel, NavigationEnd, NavigationError, NavigationStart, Router, } from "@angular/router";
|
|
@@ -27,6 +27,9 @@ export function provideSdAngular(opt) {
|
|
|
27
27
|
size: "1.33em",
|
|
28
28
|
}),
|
|
29
29
|
provideEnvironmentInitializer(() => {
|
|
30
|
+
// SSR(프리렌더) 가드: 테마 저장·복원은 브라우저 전용
|
|
31
|
+
if (!isPlatformBrowser(inject(PLATFORM_ID)))
|
|
32
|
+
return;
|
|
30
33
|
const sdTheme = inject(SdThemeProvider);
|
|
31
34
|
const sdLocalStorage = inject(SdLocalStorageProvider);
|
|
32
35
|
const savedDark = sdLocalStorage.get("sd-theme-dark");
|
|
@@ -55,6 +58,9 @@ export function provideSdAngular(opt) {
|
|
|
55
58
|
});
|
|
56
59
|
}),
|
|
57
60
|
provideEnvironmentInitializer(() => {
|
|
61
|
+
// SSR(프리렌더) 가드: window 전역 에러 리스너는 브라우저 전용
|
|
62
|
+
if (!isPlatformBrowser(inject(PLATFORM_ID)))
|
|
63
|
+
return;
|
|
58
64
|
const envInjector = inject(EnvironmentInjector);
|
|
59
65
|
const rejectionListener = (event) => {
|
|
60
66
|
event.preventDefault();
|
|
@@ -85,6 +91,9 @@ export function provideSdAngular(opt) {
|
|
|
85
91
|
{ provide: ErrorHandler, useClass: SdGlobalErrorHandlerPlugin },
|
|
86
92
|
provideZonelessChangeDetection(),
|
|
87
93
|
provideAppInitializer(() => {
|
|
94
|
+
// SSR(프리렌더) 가드: 서비스워커 업데이트 확인은 브라우저 전용
|
|
95
|
+
if (!isPlatformBrowser(inject(PLATFORM_ID)))
|
|
96
|
+
return;
|
|
88
97
|
const swUpdate = inject(SwUpdate, { optional: true });
|
|
89
98
|
const destroyRef = inject(DestroyRef);
|
|
90
99
|
let timerId;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setupRipple.d.ts","sourceRoot":"","sources":["../../../src/core/ripple/setupRipple.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"setupRipple.d.ts","sourceRoot":"","sources":["../../../src/core/ripple/setupRipple.ts"],"names":[],"mappings":"AAGA,wBAAgB,WAAW,CAAC,QAAQ,CAAC,EAAE,MAAM,OAAO,GAAG,IAAI,CA+E1D"}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import { effect, ElementRef, inject } from "@angular/core";
|
|
1
|
+
import { effect, ElementRef, inject, PLATFORM_ID } from "@angular/core";
|
|
2
|
+
import { isPlatformBrowser } from "@angular/common";
|
|
2
3
|
export function setupRipple(enableFn) {
|
|
4
|
+
// SSR(프리렌더) 가드: 포인터 인터랙션은 브라우저 전용
|
|
5
|
+
if (!isPlatformBrowser(inject(PLATFORM_ID)))
|
|
6
|
+
return;
|
|
3
7
|
const elRef = inject(ElementRef);
|
|
4
8
|
effect((onCleanup) => {
|
|
5
9
|
const el = elRef.nativeElement;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setupBgTheme.d.ts","sourceRoot":"","sources":["../../src/core/setupBgTheme.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"setupBgTheme.d.ts","sourceRoot":"","sources":["../../src/core/setupBgTheme.ts"],"names":[],"mappings":"AAGA,wBAAgB,YAAY,CAAC,OAAO,CAAC,EAAE;IACrC,KAAK,CAAC,EACF,SAAS,GACT,WAAW,GACX,MAAM,GACN,SAAS,GACT,SAAS,GACT,QAAQ,GACR,MAAM,GACN,WAAW,CAAC;IAChB,SAAS,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;CACpC,GAAG,IAAI,CAgBP"}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import { effect } from "@angular/core";
|
|
1
|
+
import { effect, inject, PLATFORM_ID } from "@angular/core";
|
|
2
|
+
import { isPlatformBrowser } from "@angular/common";
|
|
2
3
|
export function setupBgTheme(options) {
|
|
4
|
+
// SSR(프리렌더) 가드: document 반영은 브라우저 전용
|
|
5
|
+
if (!isPlatformBrowser(inject(PLATFORM_ID)))
|
|
6
|
+
return;
|
|
3
7
|
effect((onCleanup) => {
|
|
4
8
|
document.body.style.setProperty("--background-color", options?.theme
|
|
5
9
|
? `var(--theme-${options.theme}-${options.lightness ?? "lightest"})`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sd-theme-provider.d.ts","sourceRoot":"","sources":["../../../src/features/theme/sd-theme-provider.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"sd-theme-provider.d.ts","sourceRoot":"","sources":["../../../src/features/theme/sd-theme-provider.ts"],"names":[],"mappings":";AAGA,qBACa,eAAe;IAC1B,IAAI,kDAA0B;IAE9B,QAAQ,CAAC,eAAe,EAAE,SAAS,MAAM,EAAE,CAA4B;IAEvE,QAAQ,iDAAsB;;IAe9B,gBAAgB,IAAI,IAAI;IAOxB,gBAAgB,IAAI,IAAI;yCA3Bb,eAAe;6CAAf,eAAe;CAuC3B"}
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import { effect, Injectable, signal } from "@angular/core";
|
|
1
|
+
import { effect, inject, Injectable, PLATFORM_ID, signal } from "@angular/core";
|
|
2
|
+
import { isPlatformBrowser } from "@angular/common";
|
|
2
3
|
import * as i0 from "@angular/core";
|
|
3
4
|
export class SdThemeProvider {
|
|
4
5
|
dark = signal(false, ...(ngDevMode ? [{ debugName: "dark" }] : /* istanbul ignore next */ []));
|
|
5
6
|
fontSizePresets = [12, 14, 16, 20, 24, 28];
|
|
6
7
|
fontSize = signal(12, ...(ngDevMode ? [{ debugName: "fontSize" }] : /* istanbul ignore next */ []));
|
|
7
8
|
constructor() {
|
|
9
|
+
// SSR(프리렌더) 가드: document 반영은 브라우저 전용
|
|
10
|
+
if (!isPlatformBrowser(inject(PLATFORM_ID)))
|
|
11
|
+
return;
|
|
8
12
|
effect(() => {
|
|
9
13
|
document.body.classList.toggle("sd-theme-dark", this.dark());
|
|
10
14
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@simplysm/angular",
|
|
3
|
-
"version": "14.0.
|
|
3
|
+
"version": "14.0.97",
|
|
4
4
|
"description": "심플리즘 패키지 - Angular",
|
|
5
5
|
"author": "심플리즘",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -25,12 +25,12 @@
|
|
|
25
25
|
"./styles.css"
|
|
26
26
|
],
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@angular/animations": "^21.2.
|
|
29
|
-
"@angular/common": "^21.2.
|
|
30
|
-
"@angular/core": "^21.2.
|
|
31
|
-
"@angular/platform-browser": "^21.2.
|
|
32
|
-
"@angular/router": "^21.2.
|
|
33
|
-
"@angular/service-worker": "^21.2.
|
|
28
|
+
"@angular/animations": "^21.2.17",
|
|
29
|
+
"@angular/common": "^21.2.17",
|
|
30
|
+
"@angular/core": "^21.2.17",
|
|
31
|
+
"@angular/platform-browser": "^21.2.17",
|
|
32
|
+
"@angular/router": "^21.2.17",
|
|
33
|
+
"@angular/service-worker": "^21.2.17",
|
|
34
34
|
"@ng-icons/core": "^33.2.3",
|
|
35
35
|
"@ng-icons/tabler-icons": "^33.2.3",
|
|
36
36
|
"@tiptap/core": "^3.26.0",
|
|
@@ -51,14 +51,14 @@
|
|
|
51
51
|
"jspdf": "^4.2.1",
|
|
52
52
|
"rxjs": "^7.8.2",
|
|
53
53
|
"tabbable": "^6.4.0",
|
|
54
|
-
"@simplysm/core-
|
|
55
|
-
"@simplysm/
|
|
56
|
-
"@simplysm/
|
|
57
|
-
"@simplysm/service-
|
|
54
|
+
"@simplysm/core-browser": "14.0.97",
|
|
55
|
+
"@simplysm/core-common": "14.0.97",
|
|
56
|
+
"@simplysm/service-client": "14.0.97",
|
|
57
|
+
"@simplysm/service-common": "14.0.97"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
|
-
"@angular/compiler": "^21.2.
|
|
61
|
-
"@angular/platform-browser-dynamic": "^21.2.
|
|
60
|
+
"@angular/compiler": "^21.2.17",
|
|
61
|
+
"@angular/platform-browser-dynamic": "^21.2.17"
|
|
62
62
|
},
|
|
63
63
|
"scripts": {}
|
|
64
64
|
}
|
|
@@ -108,12 +108,12 @@ $vars: (
|
|
|
108
108
|
),
|
|
109
109
|
border-color: color-map(map.get($colors, gray), 5%),
|
|
110
110
|
border-radius: (
|
|
111
|
-
xs: var(--gap-
|
|
112
|
-
sm: var(--gap-
|
|
113
|
-
default: var(--gap-
|
|
114
|
-
lg: var(--gap-
|
|
115
|
-
xl: var(--gap-
|
|
116
|
-
xxl: var(--gap-
|
|
111
|
+
xs: var(--gap-xs),
|
|
112
|
+
sm: var(--gap-sm),
|
|
113
|
+
default: var(--gap-default),
|
|
114
|
+
lg: var(--gap-lg),
|
|
115
|
+
xl: var(--gap-xl),
|
|
116
|
+
xxl: var(--gap-xxl),
|
|
117
117
|
),
|
|
118
118
|
z-index: (
|
|
119
119
|
toast: 9999,
|
|
@@ -5,9 +5,11 @@ import {
|
|
|
5
5
|
EnvironmentInjector,
|
|
6
6
|
inject,
|
|
7
7
|
Injectable,
|
|
8
|
+
PLATFORM_ID,
|
|
8
9
|
signal,
|
|
9
10
|
type ComponentRef,
|
|
10
11
|
} from "@angular/core";
|
|
12
|
+
import { isPlatformBrowser } from "@angular/common";
|
|
11
13
|
import { SdBusyContainer } from "./sd-busy-container";
|
|
12
14
|
|
|
13
15
|
export type SdBusyType = "spinner" | "bar" | "cube";
|
|
@@ -42,6 +44,9 @@ export class SdBusyProvider {
|
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
constructor() {
|
|
47
|
+
// SSR(프리렌더) 가드: busy 오버레이는 document 직접 조작 — 브라우저 전용
|
|
48
|
+
if (!isPlatformBrowser(inject(PLATFORM_ID))) return;
|
|
49
|
+
|
|
45
50
|
effect(() => {
|
|
46
51
|
const busy = this.globalBusyCount() > 0;
|
|
47
52
|
|
|
@@ -1,15 +1,21 @@
|
|
|
1
|
-
import { inject, Injectable } from "@angular/core";
|
|
1
|
+
import { inject, Injectable, PLATFORM_ID } from "@angular/core";
|
|
2
|
+
import { isPlatformBrowser } from "@angular/common";
|
|
2
3
|
import { SdAngularConfigProvider } from "./sd-angular-config.provider";
|
|
3
4
|
|
|
4
5
|
@Injectable({ providedIn: "root" })
|
|
5
6
|
export class SdLocalStorageProvider<T> {
|
|
6
7
|
private readonly _sdAngularConfig = inject(SdAngularConfigProvider);
|
|
7
8
|
|
|
9
|
+
/** SSR(프리렌더) 가드: 서버에는 localStorage가 없음 — set/remove는 무동작, get은 "값 없음" */
|
|
10
|
+
private readonly _isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
|
|
11
|
+
|
|
8
12
|
set<K extends keyof T & string>(key: K, value: T[K]) {
|
|
13
|
+
if (!this._isBrowser) return;
|
|
9
14
|
localStorage.setItem(`${this._sdAngularConfig.clientName}.${key}`, JSON.stringify(value));
|
|
10
15
|
}
|
|
11
16
|
|
|
12
17
|
get<K extends keyof T & string>(key: K): T[K] | undefined {
|
|
18
|
+
if (!this._isBrowser) return undefined;
|
|
13
19
|
const json = localStorage.getItem(`${this._sdAngularConfig.clientName}.${key}`);
|
|
14
20
|
if (json == null) return undefined;
|
|
15
21
|
try {
|
|
@@ -20,6 +26,7 @@ export class SdLocalStorageProvider<T> {
|
|
|
20
26
|
}
|
|
21
27
|
|
|
22
28
|
remove(key: keyof T & string) {
|
|
29
|
+
if (!this._isBrowser) return;
|
|
23
30
|
localStorage.removeItem(`${this._sdAngularConfig.clientName}.${key}`);
|
|
24
31
|
}
|
|
25
32
|
}
|
|
@@ -5,7 +5,9 @@ import {
|
|
|
5
5
|
inject,
|
|
6
6
|
Injectable,
|
|
7
7
|
isDevMode,
|
|
8
|
+
PLATFORM_ID,
|
|
8
9
|
} from "@angular/core";
|
|
10
|
+
import { isPlatformBrowser } from "@angular/common";
|
|
9
11
|
import { createLogger } from "@simplysm/core-common";
|
|
10
12
|
import { SdSystemLogProvider } from "../config/sd-system-log.provider";
|
|
11
13
|
|
|
@@ -15,9 +17,16 @@ const logger = createLogger("angular:error-handler");
|
|
|
15
17
|
export class SdGlobalErrorHandlerPlugin implements ErrorHandler {
|
|
16
18
|
private readonly _envInjector = inject(EnvironmentInjector);
|
|
17
19
|
private readonly _sdSystemLog = inject(SdSystemLogProvider);
|
|
20
|
+
private readonly _isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
|
|
18
21
|
private _hasDisplayedError = false;
|
|
19
22
|
|
|
20
23
|
handleError(event: unknown) {
|
|
24
|
+
// SSR(프리렌더) 가드: 서버에는 화면 오버레이·브라우저 전역 이벤트 타입이 없음 — 로그만 남김
|
|
25
|
+
if (!this._isBrowser) {
|
|
26
|
+
logger.error(event);
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
|
|
21
30
|
try {
|
|
22
31
|
if (event instanceof PromiseRejectionEvent) {
|
|
23
32
|
const reason = event.reason;
|
|
@@ -6,11 +6,12 @@ import {
|
|
|
6
6
|
ErrorHandler,
|
|
7
7
|
inject,
|
|
8
8
|
makeEnvironmentProviders,
|
|
9
|
+
PLATFORM_ID,
|
|
9
10
|
provideAppInitializer,
|
|
10
11
|
provideEnvironmentInitializer,
|
|
11
12
|
provideZonelessChangeDetection,
|
|
12
13
|
} from "@angular/core";
|
|
13
|
-
import { IMAGE_CONFIG } from "@angular/common";
|
|
14
|
+
import { IMAGE_CONFIG, isPlatformBrowser } from "@angular/common";
|
|
14
15
|
import { EVENT_MANAGER_PLUGINS } from "@angular/platform-browser";
|
|
15
16
|
import { toSignal } from "@angular/core/rxjs-interop";
|
|
16
17
|
import {
|
|
@@ -46,6 +47,9 @@ export function provideSdAngular(opt: { clientName: string }): EnvironmentProvid
|
|
|
46
47
|
size: "1.33em",
|
|
47
48
|
}),
|
|
48
49
|
provideEnvironmentInitializer(() => {
|
|
50
|
+
// SSR(프리렌더) 가드: 테마 저장·복원은 브라우저 전용
|
|
51
|
+
if (!isPlatformBrowser(inject(PLATFORM_ID))) return;
|
|
52
|
+
|
|
49
53
|
const sdTheme = inject(SdThemeProvider);
|
|
50
54
|
const sdLocalStorage = inject(SdLocalStorageProvider);
|
|
51
55
|
|
|
@@ -78,6 +82,9 @@ export function provideSdAngular(opt: { clientName: string }): EnvironmentProvid
|
|
|
78
82
|
});
|
|
79
83
|
}),
|
|
80
84
|
provideEnvironmentInitializer(() => {
|
|
85
|
+
// SSR(프리렌더) 가드: window 전역 에러 리스너는 브라우저 전용
|
|
86
|
+
if (!isPlatformBrowser(inject(PLATFORM_ID))) return;
|
|
87
|
+
|
|
81
88
|
const envInjector = inject(EnvironmentInjector);
|
|
82
89
|
|
|
83
90
|
const rejectionListener = (event: PromiseRejectionEvent) => {
|
|
@@ -111,6 +118,9 @@ export function provideSdAngular(opt: { clientName: string }): EnvironmentProvid
|
|
|
111
118
|
{ provide: ErrorHandler, useClass: SdGlobalErrorHandlerPlugin },
|
|
112
119
|
provideZonelessChangeDetection(),
|
|
113
120
|
provideAppInitializer(() => {
|
|
121
|
+
// SSR(프리렌더) 가드: 서비스워커 업데이트 확인은 브라우저 전용
|
|
122
|
+
if (!isPlatformBrowser(inject(PLATFORM_ID))) return;
|
|
123
|
+
|
|
114
124
|
const swUpdate = inject(SwUpdate, { optional: true });
|
|
115
125
|
const destroyRef = inject(DestroyRef);
|
|
116
126
|
let timerId: ReturnType<typeof setTimeout> | undefined;
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
import { effect, ElementRef, inject } from "@angular/core";
|
|
1
|
+
import { effect, ElementRef, inject, PLATFORM_ID } from "@angular/core";
|
|
2
|
+
import { isPlatformBrowser } from "@angular/common";
|
|
2
3
|
|
|
3
4
|
export function setupRipple(enableFn?: () => boolean): void {
|
|
5
|
+
// SSR(프리렌더) 가드: 포인터 인터랙션은 브라우저 전용
|
|
6
|
+
if (!isPlatformBrowser(inject(PLATFORM_ID))) return;
|
|
7
|
+
|
|
4
8
|
const elRef = inject<ElementRef<HTMLElement>>(ElementRef);
|
|
5
9
|
|
|
6
10
|
effect((onCleanup) => {
|
package/src/core/setupBgTheme.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { effect } from "@angular/core";
|
|
1
|
+
import { effect, inject, PLATFORM_ID } from "@angular/core";
|
|
2
|
+
import { isPlatformBrowser } from "@angular/common";
|
|
2
3
|
|
|
3
4
|
export function setupBgTheme(options?: {
|
|
4
5
|
theme?:
|
|
@@ -12,6 +13,9 @@ export function setupBgTheme(options?: {
|
|
|
12
13
|
| "blue-gray";
|
|
13
14
|
lightness?: "lightest" | "lighter";
|
|
14
15
|
}): void {
|
|
16
|
+
// SSR(프리렌더) 가드: document 반영은 브라우저 전용
|
|
17
|
+
if (!isPlatformBrowser(inject(PLATFORM_ID))) return;
|
|
18
|
+
|
|
15
19
|
effect((onCleanup) => {
|
|
16
20
|
document.body.style.setProperty(
|
|
17
21
|
"--background-color",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { effect, Injectable, signal } from "@angular/core";
|
|
1
|
+
import { effect, inject, Injectable, PLATFORM_ID, signal } from "@angular/core";
|
|
2
|
+
import { isPlatformBrowser } from "@angular/common";
|
|
2
3
|
|
|
3
4
|
@Injectable({ providedIn: "root" })
|
|
4
5
|
export class SdThemeProvider {
|
|
@@ -9,6 +10,9 @@ export class SdThemeProvider {
|
|
|
9
10
|
fontSize = signal<number>(12);
|
|
10
11
|
|
|
11
12
|
constructor() {
|
|
13
|
+
// SSR(프리렌더) 가드: document 반영은 브라우저 전용
|
|
14
|
+
if (!isPlatformBrowser(inject(PLATFORM_ID))) return;
|
|
15
|
+
|
|
12
16
|
effect(() => {
|
|
13
17
|
document.body.classList.toggle("sd-theme-dark", this.dark());
|
|
14
18
|
});
|
package/styles.css
CHANGED
|
@@ -123,12 +123,12 @@
|
|
|
123
123
|
--border-color-dark: rgb(143.832095864, 152.5551796582, 167.5152083098);
|
|
124
124
|
--border-color-darker: rgb(116.3216443464, 127.2034085228, 145.8655591685);
|
|
125
125
|
--border-color-darkest: rgb(93.8151462793, 103.169427936, 119.2119565765);
|
|
126
|
-
--border-radius-xs: var(--gap-
|
|
127
|
-
--border-radius-sm: var(--gap-
|
|
128
|
-
--border-radius-default: var(--gap-
|
|
129
|
-
--border-radius-lg: var(--gap-
|
|
130
|
-
--border-radius-xl: var(--gap-
|
|
131
|
-
--border-radius-xxl: var(--gap-
|
|
126
|
+
--border-radius-xs: var(--gap-xs);
|
|
127
|
+
--border-radius-sm: var(--gap-sm);
|
|
128
|
+
--border-radius-default: var(--gap-default);
|
|
129
|
+
--border-radius-lg: var(--gap-lg);
|
|
130
|
+
--border-radius-xl: var(--gap-xl);
|
|
131
|
+
--border-radius-xxl: var(--gap-xxl);
|
|
132
132
|
--z-index-toast: 9999;
|
|
133
133
|
--z-index-busy: 9998;
|
|
134
134
|
--z-index-dropdown: 5000;
|