@simplysm/angular 14.0.39 → 14.0.41
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/collapse/sd-collapse.d.ts +2 -1
- package/dist/controls/collapse/sd-collapse.d.ts.map +1 -1
- package/dist/controls/collapse/sd-collapse.js +6 -0
- package/dist/controls/dropdown/sd-dropdown.js +6 -6
- package/dist/controls/list/sd-list-item.d.ts.map +1 -1
- package/dist/controls/list/sd-list-item.js +3 -3
- package/dist/controls/select/sd-select.d.ts.map +1 -1
- package/dist/controls/select/sd-select.js +7 -11
- package/dist/core/app-structure/sd-app-structure.provider.d.ts +2 -2
- package/dist/core/app-structure/sd-app-structure.provider.d.ts.map +1 -1
- package/dist/core/app-structure/sd-app-structure.provider.js +4 -4
- package/dist/core/config/sd-local-storage.provider.d.ts +1 -1
- package/dist/core/config/sd-local-storage.provider.d.ts.map +1 -1
- package/dist/core/config/sd-local-storage.provider.js +4 -4
- package/dist/core/error-handler/sd-global-error-handler.plugin.d.ts +1 -1
- 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 +3 -3
- package/dist/core/modal/injectFocusTrap.js +4 -4
- package/dist/core/modal/sd-modal.d.ts +2 -2
- package/dist/core/modal/sd-modal.d.ts.map +1 -1
- package/dist/core/modal/sd-modal.js +8 -8
- package/dist/core/modal/sd-modal.provider.js +4 -4
- package/dist/core/routing/injectViewTitleSignal.js +3 -3
- package/dist/core/routing/injectViewTypeSignal.js +2 -2
- package/dist/core/routing/sd-router-link.d.ts +1 -1
- package/dist/core/routing/sd-router-link.d.ts.map +1 -1
- package/dist/core/routing/sd-router-link.js +4 -4
- package/dist/core/routing/setupCanDeactivate.js +3 -3
- package/dist/core/service-client/sd-service-client-factory.provider.d.ts +2 -2
- package/dist/core/service-client/sd-service-client-factory.provider.d.ts.map +1 -1
- package/dist/core/service-client/sd-service-client-factory.provider.js +4 -4
- package/dist/core/shared-data/sd-shared-data.provider.d.ts +4 -3
- package/dist/core/shared-data/sd-shared-data.provider.d.ts.map +1 -1
- package/dist/core/shared-data/sd-shared-data.provider.js +4 -4
- package/dist/core/toast/sd-toast-container.js +2 -2
- package/dist/core/toast/sd-toast.d.ts.map +1 -1
- package/dist/core/toast/sd-toast.js +30 -25
- package/dist/core/toast/sd-toast.provider.d.ts +3 -3
- package/dist/core/toast/sd-toast.provider.d.ts.map +1 -1
- package/dist/core/toast/sd-toast.provider.js +2 -2
- package/dist/core/validation/setupInvalid.js +6 -5
- package/dist/data/data-detail/sd-data-detail.base.d.ts +6 -4
- package/dist/data/data-detail/sd-data-detail.base.d.ts.map +1 -1
- package/dist/data/data-select-button/sd-data-select-button.base.d.ts +2 -2
- package/dist/data/data-select-button/sd-data-select-button.base.d.ts.map +1 -1
- package/dist/data/sheet/sd-sheet.d.ts +1 -1
- package/dist/data/sheet/sd-sheet.d.ts.map +1 -1
- package/dist/data/sheet/sd-sheet.js +2 -2
- package/dist/data/sheet/useSheetCellAgent.js +6 -6
- package/dist/layout/base-container/sd-base-container.d.ts +1 -1
- package/dist/layout/base-container/sd-base-container.d.ts.map +1 -1
- package/dist/layout/base-container/sd-base-container.js +2 -2
- package/dist/layout/sidebar/sd-sidebar-menu.js +3 -3
- package/dist/styles.css +1 -1
- package/package.json +5 -5
- package/scss/commons/_styles.scss +1 -1
- package/src/controls/collapse/sd-collapse.ts +6 -0
- package/src/controls/dropdown/sd-dropdown.ts +6 -6
- package/src/controls/list/sd-list-item.ts +0 -6
- package/src/controls/select/sd-select.ts +8 -13
- package/src/core/app-structure/sd-app-structure.provider.ts +4 -4
- package/src/core/config/sd-local-storage.provider.ts +4 -4
- package/src/core/error-handler/sd-global-error-handler.plugin.ts +3 -3
- package/src/core/modal/injectFocusTrap.ts +4 -4
- package/src/core/modal/sd-modal.provider.ts +4 -4
- package/src/core/modal/sd-modal.ts +8 -8
- package/src/core/routing/injectViewTitleSignal.ts +3 -3
- package/src/core/routing/injectViewTypeSignal.ts +2 -2
- package/src/core/routing/sd-router-link.ts +4 -4
- package/src/core/routing/setupCanDeactivate.ts +3 -3
- package/src/core/service-client/sd-service-client-factory.provider.ts +4 -4
- package/src/core/shared-data/sd-shared-data.provider.ts +5 -5
- package/src/core/toast/sd-toast-container.ts +8 -8
- package/src/core/toast/sd-toast.provider.ts +6 -6
- package/src/core/toast/sd-toast.ts +82 -49
- package/src/core/validation/setupInvalid.ts +7 -6
- package/src/data/data-detail/sd-data-detail.base.ts +3 -3
- package/src/data/data-select-button/sd-data-select-button.base.ts +1 -1
- package/src/data/sheet/sd-sheet.ts +2 -2
- package/src/data/sheet/useSheetCellAgent.ts +6 -6
- package/src/layout/base-container/sd-base-container.ts +2 -2
- package/src/layout/sidebar/sd-sidebar-menu.ts +1 -1
|
@@ -15,19 +15,19 @@ export function injectPermsSignal<K extends string>(viewCodes: string[], keys: K
|
|
|
15
15
|
// 메뉴는 모듈/권한 모두 체크함
|
|
16
16
|
@Injectable({ providedIn: "root" })
|
|
17
17
|
export class SdAppStructureProvider<TModule = unknown> {
|
|
18
|
-
private readonly
|
|
19
|
-
private readonly
|
|
18
|
+
private readonly _sdServiceClientFactory = inject(SdServiceClientFactoryProvider);
|
|
19
|
+
private readonly _sdAngularConfig = inject(SdAngularConfigProvider);
|
|
20
20
|
|
|
21
21
|
readonly usableModules = signal<TModule[] | undefined>(undefined);
|
|
22
22
|
readonly permRecord = signal<Record<string, boolean> | undefined>(undefined);
|
|
23
23
|
readonly items = signal<AppStructureItem<TModule>[]>([]);
|
|
24
24
|
|
|
25
25
|
async initialize(serviceKey: string): Promise<void> {
|
|
26
|
-
const client = this.
|
|
26
|
+
const client = this._sdServiceClientFactory.get(serviceKey);
|
|
27
27
|
const svc = client.getService<AppStructureService>("AppStructure");
|
|
28
28
|
const itemsMap = await svc.getItems();
|
|
29
29
|
this.items.set(
|
|
30
|
-
(itemsMap[this.
|
|
30
|
+
(itemsMap[this._sdAngularConfig.clientName] ?? []) as AppStructureItem<TModule>[],
|
|
31
31
|
);
|
|
32
32
|
}
|
|
33
33
|
|
|
@@ -3,14 +3,14 @@ import { SdAngularConfigProvider } from "./sd-angular-config.provider";
|
|
|
3
3
|
|
|
4
4
|
@Injectable({ providedIn: "root" })
|
|
5
5
|
export class SdLocalStorageProvider<T> {
|
|
6
|
-
private readonly
|
|
6
|
+
private readonly _sdAngularConfig = inject(SdAngularConfigProvider);
|
|
7
7
|
|
|
8
8
|
set<K extends keyof T & string>(key: K, value: T[K]) {
|
|
9
|
-
localStorage.setItem(`${this.
|
|
9
|
+
localStorage.setItem(`${this._sdAngularConfig.clientName}.${key}`, JSON.stringify(value));
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
get<K extends keyof T & string>(key: K): T[K] | undefined {
|
|
13
|
-
const json = localStorage.getItem(`${this.
|
|
13
|
+
const json = localStorage.getItem(`${this._sdAngularConfig.clientName}.${key}`);
|
|
14
14
|
if (json == null) return undefined;
|
|
15
15
|
try {
|
|
16
16
|
return JSON.parse(json);
|
|
@@ -20,6 +20,6 @@ export class SdLocalStorageProvider<T> {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
remove(key: keyof T & string) {
|
|
23
|
-
localStorage.removeItem(`${this.
|
|
23
|
+
localStorage.removeItem(`${this._sdAngularConfig.clientName}.${key}`);
|
|
24
24
|
}
|
|
25
25
|
}
|
|
@@ -11,7 +11,7 @@ import { SdSystemLogProvider } from "../config/sd-system-log.provider";
|
|
|
11
11
|
@Injectable({ providedIn: null })
|
|
12
12
|
export class SdGlobalErrorHandlerPlugin implements ErrorHandler {
|
|
13
13
|
private readonly _envInjector = inject(EnvironmentInjector);
|
|
14
|
-
private readonly
|
|
14
|
+
private readonly _sdSystemLog = inject(SdSystemLogProvider);
|
|
15
15
|
private _hasDisplayedError = false;
|
|
16
16
|
|
|
17
17
|
handleError(event: any) {
|
|
@@ -41,7 +41,7 @@ export class SdGlobalErrorHandlerPlugin implements ErrorHandler {
|
|
|
41
41
|
const { message, filename, lineno, colno, error } = event;
|
|
42
42
|
|
|
43
43
|
if (error == null) {
|
|
44
|
-
void this.
|
|
44
|
+
void this._sdSystemLog.writeAsync("warn", message);
|
|
45
45
|
return;
|
|
46
46
|
}
|
|
47
47
|
|
|
@@ -95,7 +95,7 @@ export class SdGlobalErrorHandlerPlugin implements ErrorHandler {
|
|
|
95
95
|
|
|
96
96
|
const paramLines = Object.keys(param).map((key) => key + ": " + param[key]);
|
|
97
97
|
|
|
98
|
-
void this.
|
|
98
|
+
void this._sdSystemLog.writeAsync("error", `[${title}]\n${paramLines.join("\n")}`);
|
|
99
99
|
|
|
100
100
|
const appRef = this._envInjector.get<ApplicationRef>(ApplicationRef);
|
|
101
101
|
appRef.destroy();
|
|
@@ -21,11 +21,11 @@ export function injectFocusTrap(): {
|
|
|
21
21
|
|
|
22
22
|
function handleTabTrap(event: KeyboardEvent): void {
|
|
23
23
|
const hostEl = elRef.nativeElement;
|
|
24
|
-
const
|
|
25
|
-
if (
|
|
24
|
+
const tabbableElements = getTabbableElements(hostEl);
|
|
25
|
+
if (tabbableElements.length === 0) return;
|
|
26
26
|
|
|
27
|
-
const first =
|
|
28
|
-
const last =
|
|
27
|
+
const first = tabbableElements[0];
|
|
28
|
+
const last = tabbableElements[tabbableElements.length - 1];
|
|
29
29
|
|
|
30
30
|
if (event.shiftKey) {
|
|
31
31
|
if (document.activeElement === first) {
|
|
@@ -170,11 +170,11 @@ export class SdModalProvider {
|
|
|
170
170
|
if (options?.noFirstControlFocusing === true) {
|
|
171
171
|
dialogEl.focus();
|
|
172
172
|
} else {
|
|
173
|
-
// 컨텐츠 영역에서 첫
|
|
173
|
+
// 컨텐츠 영역에서 첫 탭 이동 가능 요소 검색
|
|
174
174
|
const contentEl = modalEl.querySelector("._content");
|
|
175
|
-
const
|
|
176
|
-
if (
|
|
177
|
-
|
|
175
|
+
const firstTabbable = contentEl?.findFirstTabbableChild();
|
|
176
|
+
if (firstTabbable !== undefined) {
|
|
177
|
+
firstTabbable.focus();
|
|
178
178
|
} else {
|
|
179
179
|
dialogEl.focus();
|
|
180
180
|
}
|
|
@@ -285,8 +285,8 @@ import "@simplysm/core-browser";
|
|
|
285
285
|
})
|
|
286
286
|
export class SdModal {
|
|
287
287
|
private readonly _elRef = inject(ElementRef<HTMLElement>);
|
|
288
|
-
private readonly
|
|
289
|
-
private readonly
|
|
288
|
+
private readonly _sdActivatedModal = inject(SdActivatedModalProvider, { optional: true });
|
|
289
|
+
private readonly _sdSystemConfig = inject(SdSystemConfigProvider, { optional: true });
|
|
290
290
|
private readonly _errorHandler = inject(ErrorHandler);
|
|
291
291
|
private readonly _focusTrap = injectFocusTrap();
|
|
292
292
|
|
|
@@ -347,7 +347,7 @@ export class SdModal {
|
|
|
347
347
|
// key 기반 설정 복원
|
|
348
348
|
effect(() => {
|
|
349
349
|
const k = this.key();
|
|
350
|
-
if (k === undefined || this.
|
|
350
|
+
if (k === undefined || this._sdSystemConfig == null) return;
|
|
351
351
|
|
|
352
352
|
void this._restoreConfig(k).catch((err) => this._errorHandler.handleError(err));
|
|
353
353
|
});
|
|
@@ -389,7 +389,7 @@ export class SdModal {
|
|
|
389
389
|
}
|
|
390
390
|
|
|
391
391
|
private _requestClose(): void {
|
|
392
|
-
if (this.
|
|
392
|
+
if (this._sdActivatedModal !== null && !this._sdActivatedModal.canDeactivateFn()) {
|
|
393
393
|
return;
|
|
394
394
|
}
|
|
395
395
|
void this._saveConfig().catch((err) => this._errorHandler.handleError(err));
|
|
@@ -418,7 +418,7 @@ export class SdModal {
|
|
|
418
418
|
|
|
419
419
|
private async _saveConfig(): Promise<void> {
|
|
420
420
|
const k = this.key();
|
|
421
|
-
if (k === undefined || this.
|
|
421
|
+
if (k === undefined || this._sdSystemConfig == null) return;
|
|
422
422
|
|
|
423
423
|
const dialogEl = this._getDialogEl();
|
|
424
424
|
if (dialogEl === null) return;
|
|
@@ -429,13 +429,13 @@ export class SdModal {
|
|
|
429
429
|
if (dialogEl.style.left !== "") config["left"] = dialogEl.style.left;
|
|
430
430
|
if (dialogEl.style.top !== "") config["top"] = dialogEl.style.top;
|
|
431
431
|
|
|
432
|
-
await this.
|
|
432
|
+
await this._sdSystemConfig.setAsync(`sd-modal.${k}` as any, config as any);
|
|
433
433
|
}
|
|
434
434
|
|
|
435
435
|
private async _restoreConfig(k: string): Promise<void> {
|
|
436
|
-
if (this.
|
|
436
|
+
if (this._sdSystemConfig == null) return;
|
|
437
437
|
|
|
438
|
-
const config = (await this.
|
|
438
|
+
const config = (await this._sdSystemConfig.getAsync(`sd-modal.${k}` as any)) as
|
|
439
439
|
| Record<string, string | undefined>
|
|
440
440
|
| undefined;
|
|
441
441
|
if (config == null) return;
|
|
@@ -5,15 +5,15 @@ import { injectFullPageCodeSignal } from "./injectFullPageCodeSignal";
|
|
|
5
5
|
import { injectCurrentPageCodeSignal } from "./injectCurrentPageCodeSignal";
|
|
6
6
|
|
|
7
7
|
export function injectViewTitleSignal(): Signal<string> {
|
|
8
|
-
const
|
|
8
|
+
const sdActivatedModal = inject(SdActivatedModalProvider, { optional: true });
|
|
9
9
|
const sdAppStructure = inject(SdAppStructureProvider);
|
|
10
10
|
|
|
11
11
|
const fullPageCode = injectFullPageCodeSignal();
|
|
12
12
|
const currPageCode = injectCurrentPageCodeSignal();
|
|
13
13
|
|
|
14
14
|
return computed(() => {
|
|
15
|
-
if (
|
|
16
|
-
return
|
|
15
|
+
if (sdActivatedModal != null) {
|
|
16
|
+
return sdActivatedModal.modalComponent()?.title() ?? "";
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
try {
|
|
@@ -5,14 +5,14 @@ import { injectCurrentPageCodeSignal } from "./injectCurrentPageCodeSignal";
|
|
|
5
5
|
import { injectFullPageCodeSignal } from "./injectFullPageCodeSignal";
|
|
6
6
|
|
|
7
7
|
export function injectViewTypeSignal(getComp: () => object): Signal<SdViewType> {
|
|
8
|
-
const
|
|
8
|
+
const sdActivatedModal = inject(SdActivatedModalProvider, { optional: true });
|
|
9
9
|
const activatedRoute = inject(ActivatedRoute, { optional: true });
|
|
10
10
|
|
|
11
11
|
const fullPageCode = injectFullPageCodeSignal();
|
|
12
12
|
const currPageCode = injectCurrentPageCodeSignal();
|
|
13
13
|
|
|
14
14
|
return computed<SdViewType>(() => {
|
|
15
|
-
if (
|
|
15
|
+
if (sdActivatedModal != null) {
|
|
16
16
|
return "modal";
|
|
17
17
|
}
|
|
18
18
|
|
|
@@ -12,7 +12,7 @@ import { SdNavigateWindowProvider } from "./sd-navigate-window.provider";
|
|
|
12
12
|
})
|
|
13
13
|
export class SdRouterLink {
|
|
14
14
|
private readonly _router = inject(Router);
|
|
15
|
-
private readonly
|
|
15
|
+
private readonly _sdNavigateWindow = inject(SdNavigateWindowProvider);
|
|
16
16
|
|
|
17
17
|
option = input<
|
|
18
18
|
| {
|
|
@@ -39,13 +39,13 @@ export class SdRouterLink {
|
|
|
39
39
|
event.preventDefault();
|
|
40
40
|
event.stopPropagation();
|
|
41
41
|
|
|
42
|
-
if (this.
|
|
42
|
+
if (this._sdNavigateWindow.isWindow) {
|
|
43
43
|
const width = option.window?.width ?? 800;
|
|
44
44
|
const height = option.window?.height ?? 800;
|
|
45
45
|
const qp = option.queryParams
|
|
46
46
|
? "?" + new URLSearchParams(option.queryParams).toString()
|
|
47
47
|
: "";
|
|
48
|
-
this.
|
|
48
|
+
this._sdNavigateWindow.open(
|
|
49
49
|
option.link + qp,
|
|
50
50
|
option.params,
|
|
51
51
|
`width=${width},height=${height}`,
|
|
@@ -54,7 +54,7 @@ export class SdRouterLink {
|
|
|
54
54
|
const qp = option.queryParams
|
|
55
55
|
? "?" + new URLSearchParams(option.queryParams).toString()
|
|
56
56
|
: "";
|
|
57
|
-
this.
|
|
57
|
+
this._sdNavigateWindow.open(option.link + qp, option.params);
|
|
58
58
|
} else if (option.outletName === undefined) {
|
|
59
59
|
await this._router.navigate(
|
|
60
60
|
[option.link, ...(option.params ? [option.params] : [])],
|
|
@@ -3,12 +3,12 @@ import { ActivatedRoute, type CanDeactivateFn } from "@angular/router";
|
|
|
3
3
|
import { SdActivatedModalProvider } from "../modal/sd-activated-modal.provider";
|
|
4
4
|
|
|
5
5
|
export function setupCanDeactivate(fn: () => boolean): void {
|
|
6
|
-
const
|
|
6
|
+
const sdActivatedModal = inject(SdActivatedModalProvider, { optional: true });
|
|
7
7
|
const activatedRoute = inject(ActivatedRoute, { optional: true });
|
|
8
8
|
const elRef = inject(ElementRef);
|
|
9
9
|
|
|
10
|
-
if (
|
|
11
|
-
|
|
10
|
+
if (sdActivatedModal != null) {
|
|
11
|
+
sdActivatedModal.canDeactivateFn = fn;
|
|
12
12
|
return;
|
|
13
13
|
}
|
|
14
14
|
|
|
@@ -6,8 +6,8 @@ import { SdToastProvider } from "../toast/sd-toast.provider";
|
|
|
6
6
|
|
|
7
7
|
@Injectable({ providedIn: "root" })
|
|
8
8
|
export class SdServiceClientFactoryProvider {
|
|
9
|
-
private readonly
|
|
10
|
-
private readonly
|
|
9
|
+
private readonly _sdToast = inject(SdToastProvider);
|
|
10
|
+
private readonly _sdAngularConfig = inject(SdAngularConfigProvider);
|
|
11
11
|
private readonly _destroyRef = inject(DestroyRef);
|
|
12
12
|
|
|
13
13
|
private readonly _clientMap = new Map<string, ServiceClient>();
|
|
@@ -43,7 +43,7 @@ export class SdServiceClientFactoryProvider {
|
|
|
43
43
|
...options,
|
|
44
44
|
};
|
|
45
45
|
|
|
46
|
-
const client = createServiceClient(this.
|
|
46
|
+
const client = createServiceClient(this._sdAngularConfig.clientName, mergedOptions);
|
|
47
47
|
|
|
48
48
|
client.on("request-progress", (state: ServiceProgressState) => {
|
|
49
49
|
this._handleProgress("요청을 전송하는 중입니다.", state);
|
|
@@ -85,7 +85,7 @@ export class SdServiceClientFactoryProvider {
|
|
|
85
85
|
|
|
86
86
|
let progressSignal = this._progressMap.get(uuid);
|
|
87
87
|
if (progressSignal == null) {
|
|
88
|
-
progressSignal = this.
|
|
88
|
+
progressSignal = this._sdToast.info(message, true);
|
|
89
89
|
this._progressMap.set(uuid, progressSignal);
|
|
90
90
|
}
|
|
91
91
|
|
|
@@ -40,8 +40,8 @@ interface SharedDataEntry<T extends SharedDataBase<string | number>> {
|
|
|
40
40
|
|
|
41
41
|
@Injectable()
|
|
42
42
|
export abstract class SdSharedDataProvider<T extends Record<string, SharedDataBase<string | number>>> {
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
protected readonly _sdServiceClientFactory = inject(SdServiceClientFactoryProvider);
|
|
44
|
+
protected readonly _errorHandler = inject(ErrorHandler);
|
|
45
45
|
|
|
46
46
|
readonly loadingCount: WritableSignal<number> = signal(0);
|
|
47
47
|
|
|
@@ -54,7 +54,7 @@ export abstract class SdSharedDataProvider<T extends Record<string, SharedDataBa
|
|
|
54
54
|
if (existing != null) {
|
|
55
55
|
// 기존 리스너 키 초기화 + generation 증가로 이전 이벤트 무시
|
|
56
56
|
if (existing.listenerKey != null) {
|
|
57
|
-
const client = this.
|
|
57
|
+
const client = this._sdServiceClientFactory.get(existing.info.serviceKey);
|
|
58
58
|
void client.removeListener(existing.listenerKey);
|
|
59
59
|
existing.listenerKey = undefined;
|
|
60
60
|
}
|
|
@@ -109,7 +109,7 @@ export abstract class SdSharedDataProvider<T extends Record<string, SharedDataBa
|
|
|
109
109
|
throw new Error(`등록되지 않은 공유 데이터: ${name as string}`);
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
const client = this.
|
|
112
|
+
const client = this._sdServiceClientFactory.get(entry.info.serviceKey);
|
|
113
113
|
const event = client.getEvent<typeof SdSharedDataChangeEvent>("SdSharedDataChange");
|
|
114
114
|
await event.emit(
|
|
115
115
|
(item) => item.name === (name as string) && obj.equal(item.filter, entry.info.filter),
|
|
@@ -137,7 +137,7 @@ export abstract class SdSharedDataProvider<T extends Record<string, SharedDataBa
|
|
|
137
137
|
|
|
138
138
|
// 이벤트 리스너 등록
|
|
139
139
|
if (entry.listenerKey == null) {
|
|
140
|
-
const client = this.
|
|
140
|
+
const client = this._sdServiceClientFactory.get(entry.info.serviceKey);
|
|
141
141
|
const event = client.getEvent<typeof SdSharedDataChangeEvent>("SdSharedDataChange");
|
|
142
142
|
entry.listenerKey = await event.addListener(
|
|
143
143
|
{ name, filter: entry.info.filter },
|
|
@@ -18,18 +18,21 @@ import {
|
|
|
18
18
|
styles: [
|
|
19
19
|
/* language=SCSS */ `
|
|
20
20
|
@use "../../../scss/commons/variables";
|
|
21
|
+
@use "../../../scss/commons/mixins";
|
|
21
22
|
@use "sass:map";
|
|
22
23
|
|
|
23
24
|
sd-toast-container {
|
|
25
|
+
display: flex;
|
|
24
26
|
position: fixed;
|
|
25
27
|
top: 0;
|
|
26
28
|
left: 0;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
flex-direction: column;
|
|
30
|
-
align-items: center;
|
|
31
|
-
z-index: map.get(variables.$vars, z-index, toast);
|
|
29
|
+
height: 100%;
|
|
30
|
+
width: 100%;
|
|
32
31
|
pointer-events: none;
|
|
32
|
+
padding: var(--gap-xxl);
|
|
33
|
+
z-index: map.get(variables.$vars, z-index, toast);
|
|
34
|
+
|
|
35
|
+
@include mixins.flex-direction(column);
|
|
33
36
|
|
|
34
37
|
@media all and (max-width: variables.$breakpoint-mobile) {
|
|
35
38
|
flex-direction: column-reverse;
|
|
@@ -37,8 +40,6 @@ import {
|
|
|
37
40
|
|
|
38
41
|
&[data-sd-overlap] {
|
|
39
42
|
display: block;
|
|
40
|
-
bottom: 0;
|
|
41
|
-
height: 100%;
|
|
42
43
|
|
|
43
44
|
> sd-toast {
|
|
44
45
|
position: absolute;
|
|
@@ -46,7 +47,6 @@ import {
|
|
|
46
47
|
left: var(--gap-xxl);
|
|
47
48
|
right: var(--gap-xxl);
|
|
48
49
|
width: auto;
|
|
49
|
-
max-width: none;
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
}
|
|
@@ -36,7 +36,7 @@ export class SdToastProvider {
|
|
|
36
36
|
private readonly _appRef = inject(ApplicationRef);
|
|
37
37
|
private readonly _envInjector = inject(EnvironmentInjector);
|
|
38
38
|
private readonly _injector = inject(Injector);
|
|
39
|
-
private readonly
|
|
39
|
+
private readonly _sdSystemLog = inject(SdSystemLogProvider);
|
|
40
40
|
|
|
41
41
|
alertThemes = signal<SdToastSeverity[]>([]);
|
|
42
42
|
overlap = signal(false);
|
|
@@ -145,11 +145,11 @@ export class SdToastProvider {
|
|
|
145
145
|
});
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
-
async try<R>(fn: () => Promise<R
|
|
149
|
-
|
|
148
|
+
async try<R>(fn: () => Promise<R>, messageFn?: (err: Error) => string): Promise<R | undefined>;
|
|
149
|
+
try<R>(fn: () => R, messageFn?: (err: Error) => string): R | undefined;
|
|
150
150
|
async try<R>(
|
|
151
|
-
fn: () => Promise<R
|
|
152
|
-
messageFn?: (err:
|
|
151
|
+
fn: () => Promise<R> | R,
|
|
152
|
+
messageFn?: (err: Error) => string,
|
|
153
153
|
): Promise<R | undefined> {
|
|
154
154
|
try {
|
|
155
155
|
return await fn();
|
|
@@ -160,7 +160,7 @@ export class SdToastProvider {
|
|
|
160
160
|
|
|
161
161
|
const message = messageFn !== undefined ? messageFn(err) : err.message;
|
|
162
162
|
this.danger(message);
|
|
163
|
-
await this.
|
|
163
|
+
await this._sdSystemLog.writeAsync("error", err.stack ?? err.message);
|
|
164
164
|
return undefined;
|
|
165
165
|
}
|
|
166
166
|
}
|
|
@@ -18,21 +18,23 @@ const ASSERTIVE_THEMES: ReadonlySet<string> = new Set(["warning", "danger"]);
|
|
|
18
18
|
encapsulation: ViewEncapsulation.None,
|
|
19
19
|
standalone: true,
|
|
20
20
|
host: {
|
|
21
|
-
"[attr.data-sd-open]": "open()
|
|
21
|
+
"[attr.data-sd-open]": "open()",
|
|
22
22
|
"[attr.data-sd-theme]": "theme()",
|
|
23
23
|
"[attr.role]": "ariaRole()",
|
|
24
24
|
"[attr.aria-live]": "ariaLive()",
|
|
25
25
|
},
|
|
26
26
|
template: `
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
<div class="_progress
|
|
34
|
-
|
|
35
|
-
|
|
27
|
+
<div class="_block">
|
|
28
|
+
@if (message() !== undefined) {
|
|
29
|
+
<div class="_message">{{ message() }}</div>
|
|
30
|
+
}
|
|
31
|
+
<ng-content />
|
|
32
|
+
@if (useProgress()) {
|
|
33
|
+
<div class="_progress">
|
|
34
|
+
<div class="_progress-bar" [style.width.%]="progress()"></div>
|
|
35
|
+
</div>
|
|
36
|
+
}
|
|
37
|
+
</div>
|
|
36
38
|
`,
|
|
37
39
|
styles: [
|
|
38
40
|
/* language=SCSS */ `
|
|
@@ -42,59 +44,90 @@ const ASSERTIVE_THEMES: ReadonlySet<string> = new Set(["warning", "danger"]);
|
|
|
42
44
|
|
|
43
45
|
sd-toast {
|
|
44
46
|
display: block;
|
|
45
|
-
|
|
46
|
-
|
|
47
|
+
margin-bottom: var(--gap-sm);
|
|
48
|
+
text-align: center;
|
|
47
49
|
width: 100%;
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
50
|
+
pointer-events: none;
|
|
51
|
+
|
|
52
|
+
> ._block {
|
|
53
|
+
display: inline-block;
|
|
54
|
+
text-align: left;
|
|
55
|
+
color: var(--text-trans-rev-default);
|
|
56
|
+
transform: translateY(-100%);
|
|
57
|
+
border-radius: var(--border-radius-lg);
|
|
58
|
+
opacity: 0;
|
|
59
|
+
@include mixins.elevation(12);
|
|
60
|
+
pointer-events: auto;
|
|
61
|
+
|
|
62
|
+
> ._message {
|
|
63
|
+
padding: var(--gap-default) var(--gap-lg);
|
|
64
|
+
word-break: break-all;
|
|
65
|
+
white-space: pre-wrap;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
> ._progress {
|
|
69
|
+
background: var(--theme-gray-default);
|
|
70
|
+
height: 4px;
|
|
71
|
+
border-radius: var(--border-radius-xl);
|
|
72
|
+
margin: 0 4px 4px 4px;
|
|
73
|
+
|
|
74
|
+
> ._progress-bar {
|
|
75
|
+
border-radius: var(--border-radius-xl);
|
|
76
|
+
height: 4px;
|
|
77
|
+
transition: width 1s ease-out;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
64
80
|
}
|
|
65
81
|
|
|
66
82
|
@each $theme-name, $theme-map in map.get(variables.$vars, theme) {
|
|
67
83
|
&[data-sd-theme="#{$theme-name}"] {
|
|
68
|
-
|
|
84
|
+
> ._block {
|
|
85
|
+
background: map.get($theme-map, default);
|
|
86
|
+
|
|
87
|
+
> ._progress {
|
|
88
|
+
background: map.get($theme-map, darker);
|
|
89
|
+
|
|
90
|
+
> ._progress-bar {
|
|
91
|
+
background: map.get($theme-map, lighter);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
69
95
|
}
|
|
70
96
|
}
|
|
71
97
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
98
|
+
&[data-sd-open="true"] {
|
|
99
|
+
> ._block {
|
|
100
|
+
transform: none;
|
|
101
|
+
transition: var(--animation-duration) ease-out;
|
|
102
|
+
transition-property: transform, opacity;
|
|
103
|
+
opacity: 1;
|
|
104
|
+
}
|
|
75
105
|
}
|
|
76
106
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
background: var(--trans-lighter);
|
|
84
|
-
border-radius: 0 0 var(--border-radius-default) var(--border-radius-default);
|
|
85
|
-
overflow: hidden;
|
|
86
|
-
|
|
87
|
-
> ._progress-bar {
|
|
88
|
-
height: 100%;
|
|
89
|
-
background: var(--trans-default);
|
|
90
|
-
transition: width 0.3s ease;
|
|
107
|
+
&[data-sd-open="false"] {
|
|
108
|
+
> ._block {
|
|
109
|
+
transform: translateY(-100%);
|
|
110
|
+
transition: var(--animation-duration) ease-in;
|
|
111
|
+
transition-property: transform, opacity;
|
|
112
|
+
opacity: 0;
|
|
91
113
|
}
|
|
92
114
|
}
|
|
93
115
|
|
|
94
116
|
@media all and (max-width: variables.$breakpoint-mobile) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
117
|
+
> ._block {
|
|
118
|
+
border-radius: calc(var(--line-height) / 2);
|
|
119
|
+
transform: translateY(100%);
|
|
120
|
+
|
|
121
|
+
> ._message {
|
|
122
|
+
padding: var(--gap-xs) var(--gap-default);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
&[data-sd-open="false"] {
|
|
127
|
+
> ._block {
|
|
128
|
+
transform: translateY(100%);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
98
131
|
}
|
|
99
132
|
}
|
|
100
133
|
`,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { DestroyRef, effect, ElementRef, inject, Renderer2 } from "@angular/core";
|
|
2
2
|
import { setSafeStyle } from "../setSafeStyle";
|
|
3
3
|
import { Uuid } from "@simplysm/core-common";
|
|
4
|
-
import {
|
|
4
|
+
import { isTabbable } from "tabbable";
|
|
5
5
|
|
|
6
6
|
export function setupInvalid(getInvalidMessage: () => string): void {
|
|
7
7
|
const elRef = inject<ElementRef<HTMLElement>>(ElementRef);
|
|
@@ -65,6 +65,7 @@ function createInputHiddenEl(renderer: Renderer2, hostEl: HTMLElement): HTMLInpu
|
|
|
65
65
|
newEl.type = "text";
|
|
66
66
|
newEl.name = Uuid.generate().toString();
|
|
67
67
|
newEl.className = "sd-invalid-input";
|
|
68
|
+
newEl.tabIndex = -1;
|
|
68
69
|
setSafeStyle(renderer, newEl, {
|
|
69
70
|
position: "absolute",
|
|
70
71
|
left: "2px",
|
|
@@ -79,11 +80,11 @@ function createInputHiddenEl(renderer: Renderer2, hostEl: HTMLElement): HTMLInpu
|
|
|
79
80
|
});
|
|
80
81
|
|
|
81
82
|
renderer.listen(newEl, "focus", () => {
|
|
82
|
-
const
|
|
83
|
-
(
|
|
84
|
-
hostEl.
|
|
85
|
-
if (
|
|
86
|
-
|
|
83
|
+
const tabbableElement =
|
|
84
|
+
(isTabbable(hostEl) ? hostEl : hostEl.findFirstTabbableChild()) ??
|
|
85
|
+
hostEl.findTabbableParent();
|
|
86
|
+
if (tabbableElement !== undefined) {
|
|
87
|
+
tabbableElement.focus();
|
|
87
88
|
}
|
|
88
89
|
});
|
|
89
90
|
|
|
@@ -47,9 +47,9 @@ export abstract class SdDataDetailBase<T extends object, R = boolean>
|
|
|
47
47
|
|
|
48
48
|
//-- implement
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
protected readonly _sdToast = inject(SdToastProvider);
|
|
51
|
+
protected readonly _sdSharedData = inject(SdSharedDataProvider);
|
|
52
|
+
protected readonly _errorHandler = inject(ErrorHandler);
|
|
53
53
|
|
|
54
54
|
viewType = injectViewTypeSignal(() => this);
|
|
55
55
|
|
|
@@ -368,7 +368,7 @@ export class SdSheet<T> {
|
|
|
368
368
|
columnControls = contentChildren(SdSheetColumn);
|
|
369
369
|
|
|
370
370
|
// Injected providers
|
|
371
|
-
private readonly
|
|
371
|
+
private readonly _sdModal = inject(SdModalProvider);
|
|
372
372
|
|
|
373
373
|
// DOM accessor & cell agent
|
|
374
374
|
domAccessor = injectSheetDomAccessor();
|
|
@@ -564,7 +564,7 @@ export class SdSheet<T> {
|
|
|
564
564
|
}
|
|
565
565
|
|
|
566
566
|
async onConfigButtonClick(): Promise<void> {
|
|
567
|
-
const result = await this.
|
|
567
|
+
const result = await this._sdModal.showAsync(
|
|
568
568
|
{
|
|
569
569
|
title: "시트 설정",
|
|
570
570
|
type: SdSheetConfigModal,
|