@simplysm/angular 14.0.96 → 14.0.98
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/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/dist/layout/sidebar/sd-sidebar-menu.d.ts.map +1 -1
- package/dist/layout/sidebar/sd-sidebar-menu.js +1 -1
- package/package.json +13 -13
- 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/src/layout/sidebar/sd-sidebar-menu.ts +1 -1
|
@@ -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
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sd-sidebar-menu.d.ts","sourceRoot":"","sources":["../../../src/layout/sidebar/sd-sidebar-menu.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,MAAM,EAGZ,MAAM,+BAA+B,CAAC;;AAQvC,qBAyEa,aAAa;IACxB,KAAK,gDAAuB;IAC5B,MAAM,+FAAwD;IAC9D,mBAAmB,8CAAgB,MAAM,KAAK,OAAO,eAAI;IAEzD,YAAY,yCAA8B;IAE1C,UAAU,
|
|
1
|
+
{"version":3,"file":"sd-sidebar-menu.d.ts","sourceRoot":"","sources":["../../../src/layout/sidebar/sd-sidebar-menu.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,MAAM,EAGZ,MAAM,+BAA+B,CAAC;;AAQvC,qBAyEa,aAAa;IACxB,KAAK,gDAAuB;IAC5B,MAAM,+FAAwD;IAC9D,mBAAmB,8CAAgB,MAAM,KAAK,OAAO,eAAI;IAEzD,YAAY,yCAA8B;IAE1C,UAAU,8EAAsF;IAGhG,eAAe,0CAA8D;IAE7E,uBAAuB,CACrB,IAAI,EAAE,MAAM,GACX;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAA;KAAE,GAAG,SAAS;IAIhF,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAIxC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAM/B,SAAS,CAAC,QAAQ,CAAC,gBAAgB,EAAG;QACpC,KAAK,EAAE,MAAM,EAAE,CAAC;QAChB,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;yCA/BS,aAAa;2CAAb,aAAa;CAgCzB"}
|
|
@@ -63,7 +63,7 @@ export class SdSidebarMenu {
|
|
|
63
63
|
layout = input(...(ngDevMode ? [undefined, { debugName: "layout" }] : /* istanbul ignore next */ []));
|
|
64
64
|
getMenuIsSelectedFn = input(...(ngDevMode ? [undefined, { debugName: "getMenuIsSelectedFn" }] : /* istanbul ignore next */ []));
|
|
65
65
|
fullPageCode = injectFullPageCodeSignal();
|
|
66
|
-
rootLayout = computed(() => this.layout() ?? (this.menus().length <= 3 ? "
|
|
66
|
+
rootLayout = computed(() => this.layout() ?? (this.menus().length <= 3 ? "flat" : "accordion"), ...(ngDevMode ? [{ debugName: "rootLayout" }] : /* istanbul ignore next */ []));
|
|
67
67
|
// accordion-expanded: 모든 깊이 항목을 펼친 채로 시작(이후 클릭 토글). 그 외는 접힘 시작.
|
|
68
68
|
expandInitially = computed(() => this.rootLayout() === "accordion-expanded", ...(ngDevMode ? [{ debugName: "expandInitially" }] : /* istanbul ignore next */ []));
|
|
69
69
|
getMenuRouterLinkOption(menu) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@simplysm/angular",
|
|
3
|
-
"version": "14.0.
|
|
3
|
+
"version": "14.0.98",
|
|
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-browser": "14.0.
|
|
55
|
-
"@simplysm/
|
|
56
|
-
"@simplysm/
|
|
57
|
-
"@simplysm/service-common": "14.0.
|
|
54
|
+
"@simplysm/core-browser": "14.0.98",
|
|
55
|
+
"@simplysm/service-client": "14.0.98",
|
|
56
|
+
"@simplysm/core-common": "14.0.98",
|
|
57
|
+
"@simplysm/service-common": "14.0.98"
|
|
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
|
}
|
|
@@ -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
|
});
|
|
@@ -92,7 +92,7 @@ export class SdSidebarMenu {
|
|
|
92
92
|
|
|
93
93
|
fullPageCode = injectFullPageCodeSignal();
|
|
94
94
|
|
|
95
|
-
rootLayout = computed(() => this.layout() ?? (this.menus().length <= 3 ? "
|
|
95
|
+
rootLayout = computed(() => this.layout() ?? (this.menus().length <= 3 ? "flat" : "accordion"));
|
|
96
96
|
|
|
97
97
|
// accordion-expanded: 모든 깊이 항목을 펼친 채로 시작(이후 클릭 토글). 그 외는 접힘 시작.
|
|
98
98
|
expandInitially = computed(() => this.rootLayout() === "accordion-expanded");
|