@simplysm/angular 14.0.85 → 14.0.86
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/app-structure/sd-app-structure.provider.d.ts +1 -3
- package/dist/core/app-structure/sd-app-structure.provider.d.ts.map +1 -1
- package/dist/core/app-structure/sd-app-structure.provider.js +2 -9
- package/dist/core/shared-data/sd-shared-data.provider.js +2 -2
- package/dist/data/crud/sd-base-container.d.ts.map +1 -1
- package/dist/data/crud/sd-base-container.js +56 -54
- package/package.json +28 -28
- package/src/core/app-structure/sd-app-structure.provider.ts +3 -13
- package/src/core/shared-data/sd-shared-data.provider.ts +2 -2
- package/src/data/crud/sd-base-container.ts +120 -118
|
@@ -4,12 +4,10 @@ import type { SdFlatMenu } from "./sd-app-structure.types";
|
|
|
4
4
|
import * as i0 from "@angular/core";
|
|
5
5
|
export declare function injectPermsSignal<K extends string>(viewCodes: string[], keys: K[]): Signal<K[]>;
|
|
6
6
|
export declare class SdAppStructureProvider<TModule = unknown> {
|
|
7
|
-
private readonly _sdServiceClientFactory;
|
|
8
|
-
private readonly _sdAngularConfig;
|
|
9
7
|
readonly usableModules: import("@angular/core").WritableSignal<TModule[] | undefined>;
|
|
10
8
|
readonly permRecord: import("@angular/core").WritableSignal<Record<string, boolean> | undefined>;
|
|
11
9
|
readonly items: import("@angular/core").WritableSignal<AppStructureItem<TModule>[]>;
|
|
12
|
-
initialize(
|
|
10
|
+
initialize(items: AppStructureItem<TModule>[]): void;
|
|
13
11
|
usableMenus: Signal<import("./sd-app-structure.types").SdMenu[]>;
|
|
14
12
|
usableFlatMenus: Signal<SdFlatMenu<TModule>[]>;
|
|
15
13
|
getPermissionsByStructure(items: AppStructureItem<TModule>[], codeChain?: string[]): import("./sd-app-structure.types").SdPermission<TModule>[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sd-app-structure.provider.d.ts","sourceRoot":"","sources":["../../../src/core/app-structure/sd-app-structure.provider.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,KAAK,EAAE,gBAAgB,
|
|
1
|
+
{"version":3,"file":"sd-app-structure.provider.d.ts","sourceRoot":"","sources":["../../../src/core/app-structure/sd-app-structure.provider.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;;AAG3D,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,MAAM,CAAC,CAAC,EAAE,CAAC,CAG/F;AAID,qBACa,sBAAsB,CAAC,OAAO,GAAG,OAAO;IACnD,QAAQ,CAAC,aAAa,gEAA4C;IAClE,QAAQ,CAAC,UAAU,8EAA0D;IAC7E,QAAQ,CAAC,KAAK,sEAA2C;IAEzD,UAAU,CAAC,KAAK,EAAE,gBAAgB,CAAC,OAAO,CAAC,EAAE,GAAG,IAAI;IAIpD,WAAW,sDAET;IACF,eAAe,gCAEb;IAEF,yBAAyB,CAAC,KAAK,EAAE,gBAAgB,CAAC,OAAO,CAAC,EAAE,EAAE,SAAS,GAAE,MAAM,EAAO;IAItF,kBAAkB,CAAC,QAAQ,EAAE,MAAM;IAInC,sBAAsB,CAAC,QAAQ,EAAE,MAAM;IAIvC,kBAAkB,CAAC,CAAC,SAAS,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE;yCA5BlE,sBAAsB;6CAAtB,sBAAsB;CAoClC"}
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { computed, inject, Injectable, signal } from "@angular/core";
|
|
2
2
|
import { SdAppStructureUtils } from "./sd-app-structure.utils";
|
|
3
|
-
import { SdServiceClientFactoryProvider } from "../service-client/sd-service-client-factory.provider";
|
|
4
|
-
import { SdAngularConfigProvider } from "../config/sd-angular-config.provider";
|
|
5
3
|
import * as i0 from "@angular/core";
|
|
6
4
|
export function injectPermsSignal(viewCodes, keys) {
|
|
7
5
|
const sdAppStructure = inject(SdAppStructureProvider);
|
|
@@ -10,16 +8,11 @@ export function injectPermsSignal(viewCodes, keys) {
|
|
|
10
8
|
// 권한은 모듈만 체크하고
|
|
11
9
|
// 메뉴는 모듈/권한 모두 체크함
|
|
12
10
|
export class SdAppStructureProvider {
|
|
13
|
-
_sdServiceClientFactory = inject(SdServiceClientFactoryProvider);
|
|
14
|
-
_sdAngularConfig = inject(SdAngularConfigProvider);
|
|
15
11
|
usableModules = signal(undefined, ...(ngDevMode ? [{ debugName: "usableModules" }] : /* istanbul ignore next */ []));
|
|
16
12
|
permRecord = signal(undefined, ...(ngDevMode ? [{ debugName: "permRecord" }] : /* istanbul ignore next */ []));
|
|
17
13
|
items = signal([], ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const svc = client.getService("AppStructure");
|
|
21
|
-
const itemsMap = await svc.getItems();
|
|
22
|
-
this.items.set((itemsMap[this._sdAngularConfig.clientName] ?? []));
|
|
14
|
+
initialize(items) {
|
|
15
|
+
this.items.set(items);
|
|
23
16
|
}
|
|
24
17
|
usableMenus = computed(() => SdAppStructureUtils.getMenus(this.items(), [], this.usableModules(), this.permRecord()), ...(ngDevMode ? [{ debugName: "usableMenus" }] : /* istanbul ignore next */ []));
|
|
25
18
|
usableFlatMenus = computed(() => SdAppStructureUtils.getFlatMenus(this.items(), this.usableModules(), this.permRecord()), ...(ngDevMode ? [{ debugName: "usableFlatMenus" }] : /* istanbul ignore next */ []));
|
|
@@ -61,7 +61,7 @@ export class SdSharedDataProvider {
|
|
|
61
61
|
throw new Error(`등록되지 않은 공유 데이터: ${name}`);
|
|
62
62
|
}
|
|
63
63
|
const client = this._sdServiceClientFactory.get(entry.info.serviceKey);
|
|
64
|
-
const event = client.getEvent(
|
|
64
|
+
const event = client.getEvent(SdSharedDataChangeEvent);
|
|
65
65
|
await event.emit((item) => item.name === name && obj.equal(item.filter, entry.info.filter), changeKeys);
|
|
66
66
|
}
|
|
67
67
|
async wait() {
|
|
@@ -82,7 +82,7 @@ export class SdSharedDataProvider {
|
|
|
82
82
|
// 이벤트 리스너 등록
|
|
83
83
|
if (entry.listenerKey == null) {
|
|
84
84
|
const client = this._sdServiceClientFactory.get(entry.info.serviceKey);
|
|
85
|
-
const event = client.getEvent(
|
|
85
|
+
const event = client.getEvent(SdSharedDataChangeEvent);
|
|
86
86
|
entry.listenerKey = await event.addListener({ name, filter: entry.info.filter }, async (changeKeys) => {
|
|
87
87
|
await this._onEvent(name, entry, changeKeys, capturedGeneration);
|
|
88
88
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sd-base-container.d.ts","sourceRoot":"","sources":["../../../src/data/crud/sd-base-container.ts"],"names":[],"mappings":"AAAA,OAAO,EAQL,KAAK,WAAW,EAGjB,MAAM,eAAe,CAAC;AAOvB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yCAAyC,CAAC;;AAK1E,qBAwDa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,aAAa,
|
|
1
|
+
{"version":3,"file":"sd-base-container.d.ts","sourceRoot":"","sources":["../../../src/data/crud/sd-base-container.ts"],"names":[],"mappings":"AAAA,OAAO,EAQL,KAAK,WAAW,EAGjB,MAAM,eAAe,CAAC;AAOvB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yCAAyC,CAAC;;AAK1E,qBAwDa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAoD;IAClF,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA2B;IAEpD,SAAS,yCAA2B;IAEpC,KAAK,+CAAgB;IACrB,WAAW,+CAAgB;IAC3B,SAAS,8CAAY;IAErB,UAAU,+CAAgB;IAE1B,QAAQ,kDAAgC;IAExC,YAAY,gEAAgD;IAC5D,aAAa,gEAAiD;IAC9D,aAAa,gEAAiD;IAC9D,mBAAmB,gEAAuD;;IAsB1E,SAAS,CAAC,QAAQ,CAAC,mBAAmB,ifAAuB;yCAvClD,eAAe;2CAAf,eAAe;CAwC3B"}
|
|
@@ -104,7 +104,7 @@ function SdBaseContainer_ng_template_2_Template(rf, ctx) { if (rf & 1) {
|
|
|
104
104
|
i0.ɵɵconditional(ctx_r0.bottomCommandTplRef() ? 4 : -1);
|
|
105
105
|
} }
|
|
106
106
|
export class SdBaseContainer {
|
|
107
|
-
_sdSharedData = inject(SdSharedDataProvider);
|
|
107
|
+
_sdSharedData = inject(SdSharedDataProvider, { optional: true });
|
|
108
108
|
_sdToast = inject(SdToastProvider);
|
|
109
109
|
viewTitle = injectViewTitleSignal();
|
|
110
110
|
ready = model(false, ...(ngDevMode ? [{ debugName: "ready" }] : /* istanbul ignore next */ []));
|
|
@@ -123,11 +123,13 @@ export class SdBaseContainer {
|
|
|
123
123
|
return;
|
|
124
124
|
}
|
|
125
125
|
void untracked(async () => {
|
|
126
|
-
this.
|
|
127
|
-
|
|
128
|
-
await this.
|
|
129
|
-
|
|
130
|
-
|
|
126
|
+
if (this._sdSharedData) {
|
|
127
|
+
this.busyCount.update((v) => v + 1);
|
|
128
|
+
await this._sdToast.try(async () => {
|
|
129
|
+
await this._sdSharedData.wait();
|
|
130
|
+
});
|
|
131
|
+
this.busyCount.update((v) => v - 1);
|
|
132
|
+
}
|
|
131
133
|
this.ready.set(true);
|
|
132
134
|
});
|
|
133
135
|
});
|
|
@@ -157,54 +159,54 @@ export class SdBaseContainer {
|
|
|
157
159
|
encapsulation: ViewEncapsulation.None,
|
|
158
160
|
standalone: true,
|
|
159
161
|
imports: [SdBusyContainer, NgIcon, SdTopbarContainer, NgTemplateOutlet, SdTopbar],
|
|
160
|
-
template: `
|
|
161
|
-
<sd-busy-container [busy]="initialized() && busyCount() > 0">
|
|
162
|
-
@if (initialized()) {
|
|
163
|
-
@if (restricted()) {
|
|
164
|
-
<div class="fill tx-theme-gray-light p-xxl tx-center">
|
|
165
|
-
<br />
|
|
166
|
-
<ng-icon [svg]="tablerAlertTriangle" [size]="'5rem'" />
|
|
167
|
-
<br />
|
|
168
|
-
<br />
|
|
169
|
-
'{{ viewTitle() }}'에 대한 사용권한이 없습니다. 시스템 관리자에게 문의하세요.
|
|
170
|
-
</div>
|
|
171
|
-
} @else if (viewType() === "page") {
|
|
172
|
-
<sd-topbar-container>
|
|
173
|
-
<sd-topbar>
|
|
174
|
-
<h4>{{ viewTitle() }}</h4>
|
|
175
|
-
|
|
176
|
-
<ng-template [ngTemplateOutlet]="topbarTplRef()" />
|
|
177
|
-
</sd-topbar>
|
|
178
|
-
|
|
179
|
-
<ng-template [ngTemplateOutlet]="content" />
|
|
180
|
-
</sd-topbar-container>
|
|
181
|
-
} @else {
|
|
182
|
-
<ng-template [ngTemplateOutlet]="content" />
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
</sd-busy-container>
|
|
186
|
-
|
|
187
|
-
<ng-template #content>
|
|
188
|
-
<div class="flex-column fill">
|
|
189
|
-
@if (commandTplRef()) {
|
|
190
|
-
<div class="p-default flex-row gap-default bdb bdb-theme-gray-lightest">
|
|
191
|
-
<ng-template [ngTemplateOutlet]="commandTplRef()" />
|
|
192
|
-
</div>
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
<div class="flex-fill">
|
|
196
|
-
@if (contentTplRef()) {
|
|
197
|
-
<ng-template [ngTemplateOutlet]="contentTplRef()" />
|
|
198
|
-
}
|
|
199
|
-
</div>
|
|
200
|
-
|
|
201
|
-
@if (bottomCommandTplRef()) {
|
|
202
|
-
<div class="p-sm-default flex-row main-align-end gap-sm bdt bdt-theme-gray-lightest">
|
|
203
|
-
<ng-template [ngTemplateOutlet]="bottomCommandTplRef()" />
|
|
204
|
-
</div>
|
|
205
|
-
}
|
|
206
|
-
</div>
|
|
207
|
-
</ng-template>
|
|
162
|
+
template: `
|
|
163
|
+
<sd-busy-container [busy]="initialized() && busyCount() > 0">
|
|
164
|
+
@if (initialized()) {
|
|
165
|
+
@if (restricted()) {
|
|
166
|
+
<div class="fill tx-theme-gray-light p-xxl tx-center">
|
|
167
|
+
<br />
|
|
168
|
+
<ng-icon [svg]="tablerAlertTriangle" [size]="'5rem'" />
|
|
169
|
+
<br />
|
|
170
|
+
<br />
|
|
171
|
+
'{{ viewTitle() }}'에 대한 사용권한이 없습니다. 시스템 관리자에게 문의하세요.
|
|
172
|
+
</div>
|
|
173
|
+
} @else if (viewType() === "page") {
|
|
174
|
+
<sd-topbar-container>
|
|
175
|
+
<sd-topbar>
|
|
176
|
+
<h4>{{ viewTitle() }}</h4>
|
|
177
|
+
|
|
178
|
+
<ng-template [ngTemplateOutlet]="topbarTplRef()" />
|
|
179
|
+
</sd-topbar>
|
|
180
|
+
|
|
181
|
+
<ng-template [ngTemplateOutlet]="content" />
|
|
182
|
+
</sd-topbar-container>
|
|
183
|
+
} @else {
|
|
184
|
+
<ng-template [ngTemplateOutlet]="content" />
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
</sd-busy-container>
|
|
188
|
+
|
|
189
|
+
<ng-template #content>
|
|
190
|
+
<div class="flex-column fill">
|
|
191
|
+
@if (commandTplRef()) {
|
|
192
|
+
<div class="p-default flex-row gap-default bdb bdb-theme-gray-lightest">
|
|
193
|
+
<ng-template [ngTemplateOutlet]="commandTplRef()" />
|
|
194
|
+
</div>
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
<div class="flex-fill">
|
|
198
|
+
@if (contentTplRef()) {
|
|
199
|
+
<ng-template [ngTemplateOutlet]="contentTplRef()" />
|
|
200
|
+
}
|
|
201
|
+
</div>
|
|
202
|
+
|
|
203
|
+
@if (bottomCommandTplRef()) {
|
|
204
|
+
<div class="p-sm-default flex-row main-align-end gap-sm bdt bdt-theme-gray-lightest">
|
|
205
|
+
<ng-template [ngTemplateOutlet]="bottomCommandTplRef()" />
|
|
206
|
+
</div>
|
|
207
|
+
}
|
|
208
|
+
</div>
|
|
209
|
+
</ng-template>
|
|
208
210
|
`,
|
|
209
211
|
}]
|
|
210
212
|
}], () => [], { ready: [{ type: i0.Input, args: [{ isSignal: true, alias: "ready", required: false }] }, { type: i0.Output, args: ["readyChange"] }], initialized: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialized", required: false }] }], busyCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "busyCount", required: false }] }, { type: i0.Output, args: ["busyCountChange"] }], restricted: [{ type: i0.Input, args: [{ isSignal: true, alias: "restricted", required: false }] }], viewType: [{ type: i0.Input, args: [{ isSignal: true, alias: "viewType", required: true }] }], topbarTplRef: [{ type: i0.ContentChild, args: ["topbarTpl", { isSignal: true }] }], commandTplRef: [{ type: i0.ContentChild, args: ["commandTpl", { isSignal: true }] }], contentTplRef: [{ type: i0.ContentChild, args: ["contentTpl", { isSignal: true }] }], bottomCommandTplRef: [{ type: i0.ContentChild, args: ["bottomCommandTpl", { isSignal: true }] }] }); })();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@simplysm/angular",
|
|
3
|
-
"version": "14.0.
|
|
3
|
+
"version": "14.0.86",
|
|
4
4
|
"description": "심플리즘 패키지 - Angular",
|
|
5
5
|
"author": "심플리즘",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -24,40 +24,40 @@
|
|
|
24
24
|
"./dist/**/*.css"
|
|
25
25
|
],
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@angular/animations": "^21.2.
|
|
28
|
-
"@angular/common": "^21.2.
|
|
29
|
-
"@angular/core": "^21.2.
|
|
30
|
-
"@angular/platform-browser": "^21.2.
|
|
31
|
-
"@angular/router": "^21.2.
|
|
32
|
-
"@angular/service-worker": "^21.2.
|
|
33
|
-
"@ng-icons/core": "^33.2.
|
|
34
|
-
"@ng-icons/tabler-icons": "^33.2.
|
|
35
|
-
"@tiptap/core": "^3.
|
|
36
|
-
"@tiptap/extension-color": "^3.
|
|
37
|
-
"@tiptap/extension-document": "^3.
|
|
38
|
-
"@tiptap/extension-highlight": "^3.
|
|
39
|
-
"@tiptap/extension-image": "^3.
|
|
40
|
-
"@tiptap/extension-paragraph": "^3.
|
|
41
|
-
"@tiptap/extension-placeholder": "^3.
|
|
42
|
-
"@tiptap/extension-text": "^3.
|
|
43
|
-
"@tiptap/extension-text-align": "^3.
|
|
44
|
-
"@tiptap/extension-text-style": "^3.
|
|
45
|
-
"@tiptap/extension-underline": "^3.
|
|
46
|
-
"@tiptap/starter-kit": "^3.
|
|
47
|
-
"bwip-js": "^4.
|
|
27
|
+
"@angular/animations": "^21.2.15",
|
|
28
|
+
"@angular/common": "^21.2.15",
|
|
29
|
+
"@angular/core": "^21.2.15",
|
|
30
|
+
"@angular/platform-browser": "^21.2.15",
|
|
31
|
+
"@angular/router": "^21.2.15",
|
|
32
|
+
"@angular/service-worker": "^21.2.15",
|
|
33
|
+
"@ng-icons/core": "^33.2.3",
|
|
34
|
+
"@ng-icons/tabler-icons": "^33.2.3",
|
|
35
|
+
"@tiptap/core": "^3.24.0",
|
|
36
|
+
"@tiptap/extension-color": "^3.24.0",
|
|
37
|
+
"@tiptap/extension-document": "^3.24.0",
|
|
38
|
+
"@tiptap/extension-highlight": "^3.24.0",
|
|
39
|
+
"@tiptap/extension-image": "^3.24.0",
|
|
40
|
+
"@tiptap/extension-paragraph": "^3.24.0",
|
|
41
|
+
"@tiptap/extension-placeholder": "^3.24.0",
|
|
42
|
+
"@tiptap/extension-text": "^3.24.0",
|
|
43
|
+
"@tiptap/extension-text-align": "^3.24.0",
|
|
44
|
+
"@tiptap/extension-text-style": "^3.24.0",
|
|
45
|
+
"@tiptap/extension-underline": "^3.24.0",
|
|
46
|
+
"@tiptap/starter-kit": "^3.24.0",
|
|
47
|
+
"bwip-js": "^4.11.1",
|
|
48
48
|
"echarts": "^6.1.0",
|
|
49
49
|
"html-to-image": "^1.11.13",
|
|
50
50
|
"jspdf": "^4.2.1",
|
|
51
51
|
"rxjs": "^7.8.2",
|
|
52
52
|
"tabbable": "^6.4.0",
|
|
53
|
-
"@simplysm/core-
|
|
54
|
-
"@simplysm/
|
|
55
|
-
"@simplysm/service-client": "14.0.
|
|
56
|
-
"@simplysm/
|
|
53
|
+
"@simplysm/core-browser": "14.0.86",
|
|
54
|
+
"@simplysm/core-common": "14.0.86",
|
|
55
|
+
"@simplysm/service-client": "14.0.86",
|
|
56
|
+
"@simplysm/service-common": "14.0.86"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
|
-
"@angular/compiler": "^21.2.
|
|
60
|
-
"@angular/platform-browser-dynamic": "^21.2.
|
|
59
|
+
"@angular/compiler": "^21.2.15",
|
|
60
|
+
"@angular/platform-browser-dynamic": "^21.2.15"
|
|
61
61
|
},
|
|
62
62
|
"scripts": {}
|
|
63
63
|
}
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { computed, inject, Injectable, signal } from "@angular/core";
|
|
2
2
|
import type { Signal } from "@angular/core";
|
|
3
|
-
import type { AppStructureItem
|
|
3
|
+
import type { AppStructureItem } from "@simplysm/service-common";
|
|
4
4
|
import type { SdFlatMenu } from "./sd-app-structure.types";
|
|
5
5
|
import { SdAppStructureUtils } from "./sd-app-structure.utils";
|
|
6
|
-
import { SdServiceClientFactoryProvider } from "../service-client/sd-service-client-factory.provider";
|
|
7
|
-
import { SdAngularConfigProvider } from "../config/sd-angular-config.provider";
|
|
8
6
|
|
|
9
7
|
export function injectPermsSignal<K extends string>(viewCodes: string[], keys: K[]): Signal<K[]> {
|
|
10
8
|
const sdAppStructure = inject(SdAppStructureProvider);
|
|
@@ -15,20 +13,12 @@ export function injectPermsSignal<K extends string>(viewCodes: string[], keys: K
|
|
|
15
13
|
// 메뉴는 모듈/권한 모두 체크함
|
|
16
14
|
@Injectable({ providedIn: "root" })
|
|
17
15
|
export class SdAppStructureProvider<TModule = unknown> {
|
|
18
|
-
private readonly _sdServiceClientFactory = inject(SdServiceClientFactoryProvider);
|
|
19
|
-
private readonly _sdAngularConfig = inject(SdAngularConfigProvider);
|
|
20
|
-
|
|
21
16
|
readonly usableModules = signal<TModule[] | undefined>(undefined);
|
|
22
17
|
readonly permRecord = signal<Record<string, boolean> | undefined>(undefined);
|
|
23
18
|
readonly items = signal<AppStructureItem<TModule>[]>([]);
|
|
24
19
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const svc = client.getService<AppStructureService>("AppStructure");
|
|
28
|
-
const itemsMap = await svc.getItems();
|
|
29
|
-
this.items.set(
|
|
30
|
-
(itemsMap[this._sdAngularConfig.clientName] ?? []) as AppStructureItem<TModule>[],
|
|
31
|
-
);
|
|
20
|
+
initialize(items: AppStructureItem<TModule>[]): void {
|
|
21
|
+
this.items.set(items);
|
|
32
22
|
}
|
|
33
23
|
|
|
34
24
|
usableMenus = computed(() =>
|
|
@@ -110,7 +110,7 @@ export abstract class SdSharedDataProvider<T extends Record<string, SharedDataBa
|
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
const client = this._sdServiceClientFactory.get(entry.info.serviceKey);
|
|
113
|
-
const event = client.getEvent
|
|
113
|
+
const event = client.getEvent(SdSharedDataChangeEvent);
|
|
114
114
|
await event.emit(
|
|
115
115
|
(item) => item.name === (name as string) && obj.equal(item.filter, entry.info.filter),
|
|
116
116
|
changeKeys,
|
|
@@ -138,7 +138,7 @@ export abstract class SdSharedDataProvider<T extends Record<string, SharedDataBa
|
|
|
138
138
|
// 이벤트 리스너 등록
|
|
139
139
|
if (entry.listenerKey == null) {
|
|
140
140
|
const client = this._sdServiceClientFactory.get(entry.info.serviceKey);
|
|
141
|
-
const event = client.getEvent
|
|
141
|
+
const event = client.getEvent(SdSharedDataChangeEvent);
|
|
142
142
|
entry.listenerKey = await event.addListener(
|
|
143
143
|
{ name, filter: entry.info.filter },
|
|
144
144
|
async (changeKeys) => {
|
|
@@ -1,118 +1,120 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ChangeDetectionStrategy,
|
|
3
|
-
Component,
|
|
4
|
-
contentChild,
|
|
5
|
-
effect,
|
|
6
|
-
inject,
|
|
7
|
-
input,
|
|
8
|
-
model,
|
|
9
|
-
type TemplateRef,
|
|
10
|
-
untracked,
|
|
11
|
-
ViewEncapsulation,
|
|
12
|
-
} from "@angular/core";
|
|
13
|
-
import { injectViewTitleSignal } from "../../core/routing/injectViewTitleSignal";
|
|
14
|
-
import { SdBusyContainer } from "../../core/busy/sd-busy-container";
|
|
15
|
-
import { SdSharedDataProvider } from "../../core/shared-data/sd-shared-data.provider";
|
|
16
|
-
import { SdToastProvider } from "../../core/toast/sd-toast.provider";
|
|
17
|
-
import { SdTopbar } from "../../layout/topbar/sd-topbar";
|
|
18
|
-
import { SdTopbarContainer } from "../../layout/topbar/sd-topbar-container";
|
|
19
|
-
import type { SdViewType } from "../../core/routing/injectViewTypeSignal";
|
|
20
|
-
import { NgIcon } from "@ng-icons/core";
|
|
21
|
-
import { tablerAlertTriangle } from "@ng-icons/tabler-icons";
|
|
22
|
-
import { NgTemplateOutlet } from "@angular/common";
|
|
23
|
-
|
|
24
|
-
@Component({
|
|
25
|
-
selector: "sd-base-container",
|
|
26
|
-
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
27
|
-
encapsulation: ViewEncapsulation.None,
|
|
28
|
-
standalone: true,
|
|
29
|
-
imports: [SdBusyContainer, NgIcon, SdTopbarContainer, NgTemplateOutlet, SdTopbar],
|
|
30
|
-
template: `
|
|
31
|
-
<sd-busy-container [busy]="initialized() && busyCount() > 0">
|
|
32
|
-
@if (initialized()) {
|
|
33
|
-
@if (restricted()) {
|
|
34
|
-
<div class="fill tx-theme-gray-light p-xxl tx-center">
|
|
35
|
-
<br />
|
|
36
|
-
<ng-icon [svg]="tablerAlertTriangle" [size]="'5rem'" />
|
|
37
|
-
<br />
|
|
38
|
-
<br />
|
|
39
|
-
'{{ viewTitle() }}'에 대한 사용권한이 없습니다. 시스템 관리자에게 문의하세요.
|
|
40
|
-
</div>
|
|
41
|
-
} @else if (viewType() === "page") {
|
|
42
|
-
<sd-topbar-container>
|
|
43
|
-
<sd-topbar>
|
|
44
|
-
<h4>{{ viewTitle() }}</h4>
|
|
45
|
-
|
|
46
|
-
<ng-template [ngTemplateOutlet]="topbarTplRef()" />
|
|
47
|
-
</sd-topbar>
|
|
48
|
-
|
|
49
|
-
<ng-template [ngTemplateOutlet]="content" />
|
|
50
|
-
</sd-topbar-container>
|
|
51
|
-
} @else {
|
|
52
|
-
<ng-template [ngTemplateOutlet]="content" />
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
</sd-busy-container>
|
|
56
|
-
|
|
57
|
-
<ng-template #content>
|
|
58
|
-
<div class="flex-column fill">
|
|
59
|
-
@if (commandTplRef()) {
|
|
60
|
-
<div class="p-default flex-row gap-default bdb bdb-theme-gray-lightest">
|
|
61
|
-
<ng-template [ngTemplateOutlet]="commandTplRef()" />
|
|
62
|
-
</div>
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
<div class="flex-fill">
|
|
66
|
-
@if (contentTplRef()) {
|
|
67
|
-
<ng-template [ngTemplateOutlet]="contentTplRef()" />
|
|
68
|
-
}
|
|
69
|
-
</div>
|
|
70
|
-
|
|
71
|
-
@if (bottomCommandTplRef()) {
|
|
72
|
-
<div class="p-sm-default flex-row main-align-end gap-sm bdt bdt-theme-gray-lightest">
|
|
73
|
-
<ng-template [ngTemplateOutlet]="bottomCommandTplRef()" />
|
|
74
|
-
</div>
|
|
75
|
-
}
|
|
76
|
-
</div>
|
|
77
|
-
</ng-template>
|
|
78
|
-
`,
|
|
79
|
-
})
|
|
80
|
-
export class SdBaseContainer {
|
|
81
|
-
private readonly _sdSharedData = inject(SdSharedDataProvider);
|
|
82
|
-
private readonly _sdToast = inject(SdToastProvider);
|
|
83
|
-
|
|
84
|
-
viewTitle = injectViewTitleSignal();
|
|
85
|
-
|
|
86
|
-
ready = model(false);
|
|
87
|
-
initialized = input(false);
|
|
88
|
-
busyCount = model(0);
|
|
89
|
-
|
|
90
|
-
restricted = input(false);
|
|
91
|
-
|
|
92
|
-
viewType = input.required<SdViewType>();
|
|
93
|
-
|
|
94
|
-
topbarTplRef = contentChild<TemplateRef<void>>("topbarTpl");
|
|
95
|
-
commandTplRef = contentChild<TemplateRef<void>>("commandTpl");
|
|
96
|
-
contentTplRef = contentChild<TemplateRef<void>>("contentTpl");
|
|
97
|
-
bottomCommandTplRef = contentChild<TemplateRef<void>>("bottomCommandTpl");
|
|
98
|
-
|
|
99
|
-
constructor() {
|
|
100
|
-
effect(() => {
|
|
101
|
-
if (this.restricted()) {
|
|
102
|
-
this.ready.set(true);
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
void untracked(async () => {
|
|
107
|
-
this.
|
|
108
|
-
|
|
109
|
-
await this.
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
1
|
+
import {
|
|
2
|
+
ChangeDetectionStrategy,
|
|
3
|
+
Component,
|
|
4
|
+
contentChild,
|
|
5
|
+
effect,
|
|
6
|
+
inject,
|
|
7
|
+
input,
|
|
8
|
+
model,
|
|
9
|
+
type TemplateRef,
|
|
10
|
+
untracked,
|
|
11
|
+
ViewEncapsulation,
|
|
12
|
+
} from "@angular/core";
|
|
13
|
+
import { injectViewTitleSignal } from "../../core/routing/injectViewTitleSignal";
|
|
14
|
+
import { SdBusyContainer } from "../../core/busy/sd-busy-container";
|
|
15
|
+
import { SdSharedDataProvider } from "../../core/shared-data/sd-shared-data.provider";
|
|
16
|
+
import { SdToastProvider } from "../../core/toast/sd-toast.provider";
|
|
17
|
+
import { SdTopbar } from "../../layout/topbar/sd-topbar";
|
|
18
|
+
import { SdTopbarContainer } from "../../layout/topbar/sd-topbar-container";
|
|
19
|
+
import type { SdViewType } from "../../core/routing/injectViewTypeSignal";
|
|
20
|
+
import { NgIcon } from "@ng-icons/core";
|
|
21
|
+
import { tablerAlertTriangle } from "@ng-icons/tabler-icons";
|
|
22
|
+
import { NgTemplateOutlet } from "@angular/common";
|
|
23
|
+
|
|
24
|
+
@Component({
|
|
25
|
+
selector: "sd-base-container",
|
|
26
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
27
|
+
encapsulation: ViewEncapsulation.None,
|
|
28
|
+
standalone: true,
|
|
29
|
+
imports: [SdBusyContainer, NgIcon, SdTopbarContainer, NgTemplateOutlet, SdTopbar],
|
|
30
|
+
template: `
|
|
31
|
+
<sd-busy-container [busy]="initialized() && busyCount() > 0">
|
|
32
|
+
@if (initialized()) {
|
|
33
|
+
@if (restricted()) {
|
|
34
|
+
<div class="fill tx-theme-gray-light p-xxl tx-center">
|
|
35
|
+
<br />
|
|
36
|
+
<ng-icon [svg]="tablerAlertTriangle" [size]="'5rem'" />
|
|
37
|
+
<br />
|
|
38
|
+
<br />
|
|
39
|
+
'{{ viewTitle() }}'에 대한 사용권한이 없습니다. 시스템 관리자에게 문의하세요.
|
|
40
|
+
</div>
|
|
41
|
+
} @else if (viewType() === "page") {
|
|
42
|
+
<sd-topbar-container>
|
|
43
|
+
<sd-topbar>
|
|
44
|
+
<h4>{{ viewTitle() }}</h4>
|
|
45
|
+
|
|
46
|
+
<ng-template [ngTemplateOutlet]="topbarTplRef()" />
|
|
47
|
+
</sd-topbar>
|
|
48
|
+
|
|
49
|
+
<ng-template [ngTemplateOutlet]="content" />
|
|
50
|
+
</sd-topbar-container>
|
|
51
|
+
} @else {
|
|
52
|
+
<ng-template [ngTemplateOutlet]="content" />
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
</sd-busy-container>
|
|
56
|
+
|
|
57
|
+
<ng-template #content>
|
|
58
|
+
<div class="flex-column fill">
|
|
59
|
+
@if (commandTplRef()) {
|
|
60
|
+
<div class="p-default flex-row gap-default bdb bdb-theme-gray-lightest">
|
|
61
|
+
<ng-template [ngTemplateOutlet]="commandTplRef()" />
|
|
62
|
+
</div>
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
<div class="flex-fill">
|
|
66
|
+
@if (contentTplRef()) {
|
|
67
|
+
<ng-template [ngTemplateOutlet]="contentTplRef()" />
|
|
68
|
+
}
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
@if (bottomCommandTplRef()) {
|
|
72
|
+
<div class="p-sm-default flex-row main-align-end gap-sm bdt bdt-theme-gray-lightest">
|
|
73
|
+
<ng-template [ngTemplateOutlet]="bottomCommandTplRef()" />
|
|
74
|
+
</div>
|
|
75
|
+
}
|
|
76
|
+
</div>
|
|
77
|
+
</ng-template>
|
|
78
|
+
`,
|
|
79
|
+
})
|
|
80
|
+
export class SdBaseContainer {
|
|
81
|
+
private readonly _sdSharedData = inject(SdSharedDataProvider, { optional: true });
|
|
82
|
+
private readonly _sdToast = inject(SdToastProvider);
|
|
83
|
+
|
|
84
|
+
viewTitle = injectViewTitleSignal();
|
|
85
|
+
|
|
86
|
+
ready = model(false);
|
|
87
|
+
initialized = input(false);
|
|
88
|
+
busyCount = model(0);
|
|
89
|
+
|
|
90
|
+
restricted = input(false);
|
|
91
|
+
|
|
92
|
+
viewType = input.required<SdViewType>();
|
|
93
|
+
|
|
94
|
+
topbarTplRef = contentChild<TemplateRef<void>>("topbarTpl");
|
|
95
|
+
commandTplRef = contentChild<TemplateRef<void>>("commandTpl");
|
|
96
|
+
contentTplRef = contentChild<TemplateRef<void>>("contentTpl");
|
|
97
|
+
bottomCommandTplRef = contentChild<TemplateRef<void>>("bottomCommandTpl");
|
|
98
|
+
|
|
99
|
+
constructor() {
|
|
100
|
+
effect(() => {
|
|
101
|
+
if (this.restricted()) {
|
|
102
|
+
this.ready.set(true);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
void untracked(async () => {
|
|
107
|
+
if (this._sdSharedData) {
|
|
108
|
+
this.busyCount.update((v) => v + 1);
|
|
109
|
+
await this._sdToast.try(async () => {
|
|
110
|
+
await this._sdSharedData!.wait();
|
|
111
|
+
});
|
|
112
|
+
this.busyCount.update((v) => v - 1);
|
|
113
|
+
}
|
|
114
|
+
this.ready.set(true);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
protected readonly tablerAlertTriangle = tablerAlertTriangle;
|
|
120
|
+
}
|