@simplysm/angular 14.0.47 → 14.0.48

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.
Files changed (128) hide show
  1. package/README.md +468 -0
  2. package/dist/controls/dropdown/sd-dropdown-popup.d.ts +1 -0
  3. package/dist/controls/dropdown/sd-dropdown-popup.d.ts.map +1 -1
  4. package/dist/controls/dropdown/sd-dropdown-popup.js +6 -1
  5. package/dist/controls/dropdown/sd-dropdown.d.ts.map +1 -1
  6. package/dist/controls/dropdown/sd-dropdown.js +6 -2
  7. package/dist/controls/select/sd-select.d.ts.map +1 -1
  8. package/dist/controls/select/sd-select.js +12 -34
  9. package/dist/core/routing/injectViewTypeSignal.d.ts +1 -1
  10. package/dist/core/routing/injectViewTypeSignal.d.ts.map +1 -1
  11. package/dist/core/routing/injectViewTypeSignal.js +15 -7
  12. package/dist/core/selection/useSelectionManager.d.ts +1 -0
  13. package/dist/core/selection/useSelectionManager.d.ts.map +1 -1
  14. package/dist/core/selection/useSelectionManager.js +54 -14
  15. package/dist/data/shared-data/sd-shared-data-select-button.d.ts +11 -5
  16. package/dist/data/shared-data/sd-shared-data-select-button.d.ts.map +1 -1
  17. package/dist/data/shared-data/sd-shared-data-select-button.js +68 -29
  18. package/dist/data/sheet/sd-sheet.d.ts +4 -1
  19. package/dist/data/sheet/sd-sheet.d.ts.map +1 -1
  20. package/dist/data/sheet/sd-sheet.js +23 -4
  21. package/dist/index.d.ts +0 -13
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +0 -17
  24. package/docs/bootstrap.md +38 -0
  25. package/docs/directives.md +236 -0
  26. package/docs/features.md +169 -0
  27. package/docs/pipes.md +32 -0
  28. package/docs/plugins.md +37 -0
  29. package/docs/provider-types.md +283 -0
  30. package/docs/providers.md +379 -0
  31. package/docs/recipes/crud-detail.md +860 -0
  32. package/docs/recipes/crud-list.md +779 -0
  33. package/docs/recipes/data-select-button.md +319 -0
  34. package/docs/recipes/page-modal-container.md +178 -0
  35. package/docs/styling.md +222 -0
  36. package/docs/type-utilities.md +250 -0
  37. package/docs/ui-data.md +333 -0
  38. package/docs/ui-form.md +502 -0
  39. package/docs/ui-layout.md +140 -0
  40. package/docs/ui-navigation.md +273 -0
  41. package/docs/ui-overlay.md +157 -0
  42. package/docs/ui-visual.md +127 -0
  43. package/docs/utils.md +244 -0
  44. package/package.json +19 -18
  45. package/src/controls/dropdown/sd-dropdown-popup.ts +6 -2
  46. package/src/controls/dropdown/sd-dropdown.ts +3 -4
  47. package/src/controls/select/sd-select.ts +6 -30
  48. package/src/core/routing/injectViewTypeSignal.ts +17 -10
  49. package/src/core/selection/useSelectionManager.ts +62 -12
  50. package/src/data/shared-data/sd-shared-data-select-button.ts +49 -14
  51. package/src/data/sheet/sd-sheet.ts +21 -0
  52. package/src/index.ts +0 -29
  53. package/dist/core/commons.d.ts +0 -2
  54. package/dist/core/commons.d.ts.map +0 -1
  55. package/dist/core/commons.js +0 -4
  56. package/dist/core/injectParent.d.ts +0 -7
  57. package/dist/core/injectParent.d.ts.map +0 -1
  58. package/dist/core/injectParent.js +0 -30
  59. package/dist/core/selection/setupCumulateSelectedKeys.d.ts +0 -9
  60. package/dist/core/selection/setupCumulateSelectedKeys.d.ts.map +0 -1
  61. package/dist/core/selection/setupCumulateSelectedKeys.js +0 -27
  62. package/dist/core/withBusy.d.ts +0 -3
  63. package/dist/core/withBusy.d.ts.map +0 -1
  64. package/dist/core/withBusy.js +0 -9
  65. package/dist/data/data-detail/sd-data-detail.base.d.ts +0 -51
  66. package/dist/data/data-detail/sd-data-detail.base.d.ts.map +0 -1
  67. package/dist/data/data-detail/sd-data-detail.base.js +0 -127
  68. package/dist/data/data-detail/sd-data-detail.d.ts +0 -30
  69. package/dist/data/data-detail/sd-data-detail.d.ts.map +0 -1
  70. package/dist/data/data-detail/sd-data-detail.js +0 -495
  71. package/dist/data/data-select-button/sd-data-select-button.base.d.ts +0 -24
  72. package/dist/data/data-select-button/sd-data-select-button.base.d.ts.map +0 -1
  73. package/dist/data/data-select-button/sd-data-select-button.base.js +0 -68
  74. package/dist/data/data-select-button/sd-data-select-button.d.ts +0 -15
  75. package/dist/data/data-select-button/sd-data-select-button.d.ts.map +0 -1
  76. package/dist/data/data-select-button/sd-data-select-button.js +0 -152
  77. package/dist/data/data-sheet/injectDataSheetExcelManager.d.ts +0 -14
  78. package/dist/data/data-sheet/injectDataSheetExcelManager.d.ts.map +0 -1
  79. package/dist/data/data-sheet/injectDataSheetExcelManager.js +0 -31
  80. package/dist/data/data-sheet/injectDataSheetInlineEditManager.d.ts +0 -26
  81. package/dist/data/data-sheet/injectDataSheetInlineEditManager.d.ts.map +0 -1
  82. package/dist/data/data-sheet/injectDataSheetInlineEditManager.js +0 -54
  83. package/dist/data/data-sheet/injectDataSheetModalEditManager.d.ts +0 -19
  84. package/dist/data/data-sheet/injectDataSheetModalEditManager.d.ts.map +0 -1
  85. package/dist/data/data-sheet/injectDataSheetModalEditManager.js +0 -44
  86. package/dist/data/data-sheet/injectDataSheetRefreshManager.d.ts +0 -25
  87. package/dist/data/data-sheet/injectDataSheetRefreshManager.d.ts.map +0 -1
  88. package/dist/data/data-sheet/injectDataSheetRefreshManager.js +0 -50
  89. package/dist/data/data-sheet/sd-data-sheet-column.d.ts +0 -8
  90. package/dist/data/data-sheet/sd-data-sheet-column.d.ts.map +0 -1
  91. package/dist/data/data-sheet/sd-data-sheet-column.js +0 -15
  92. package/dist/data/data-sheet/sd-data-sheet.base.d.ts +0 -75
  93. package/dist/data/data-sheet/sd-data-sheet.base.d.ts.map +0 -1
  94. package/dist/data/data-sheet/sd-data-sheet.base.js +0 -188
  95. package/dist/data/data-sheet/sd-data-sheet.d.ts +0 -49
  96. package/dist/data/data-sheet/sd-data-sheet.d.ts.map +0 -1
  97. package/dist/data/data-sheet/sd-data-sheet.js +0 -968
  98. package/dist/data/data-sheet/sd-data-sheet.types.d.ts +0 -17
  99. package/dist/data/data-sheet/sd-data-sheet.types.d.ts.map +0 -1
  100. package/dist/data/data-sheet/sd-data-sheet.types.js +0 -1
  101. package/dist/data/data-sheet/setupCloserWhenSingleSelectionChange.d.ts +0 -9
  102. package/dist/data/data-sheet/setupCloserWhenSingleSelectionChange.d.ts.map +0 -1
  103. package/dist/data/data-sheet/setupCloserWhenSingleSelectionChange.js +0 -20
  104. package/dist/data/data-sheet/useDataSheetFilterManager.d.ts +0 -13
  105. package/dist/data/data-sheet/useDataSheetFilterManager.d.ts.map +0 -1
  106. package/dist/data/data-sheet/useDataSheetFilterManager.js +0 -19
  107. package/dist/layout/base-container/sd-base-container.d.ts +0 -27
  108. package/dist/layout/base-container/sd-base-container.d.ts.map +0 -1
  109. package/dist/layout/base-container/sd-base-container.js +0 -193
  110. package/src/core/commons.ts +0 -4
  111. package/src/core/injectParent.ts +0 -45
  112. package/src/core/selection/setupCumulateSelectedKeys.ts +0 -38
  113. package/src/core/withBusy.ts +0 -13
  114. package/src/data/data-detail/sd-data-detail.base.ts +0 -187
  115. package/src/data/data-detail/sd-data-detail.ts +0 -237
  116. package/src/data/data-select-button/sd-data-select-button.base.ts +0 -104
  117. package/src/data/data-select-button/sd-data-select-button.ts +0 -105
  118. package/src/data/data-sheet/injectDataSheetExcelManager.ts +0 -57
  119. package/src/data/data-sheet/injectDataSheetInlineEditManager.ts +0 -89
  120. package/src/data/data-sheet/injectDataSheetModalEditManager.ts +0 -76
  121. package/src/data/data-sheet/injectDataSheetRefreshManager.ts +0 -90
  122. package/src/data/data-sheet/sd-data-sheet-column.ts +0 -10
  123. package/src/data/data-sheet/sd-data-sheet.base.ts +0 -294
  124. package/src/data/data-sheet/sd-data-sheet.ts +0 -479
  125. package/src/data/data-sheet/sd-data-sheet.types.ts +0 -18
  126. package/src/data/data-sheet/setupCloserWhenSingleSelectionChange.ts +0 -29
  127. package/src/data/data-sheet/useDataSheetFilterManager.ts +0 -30
  128. package/src/layout/base-container/sd-base-container.ts +0 -121
@@ -1,237 +0,0 @@
1
- import { NgTemplateOutlet } from "@angular/common";
2
- import {
3
- ChangeDetectionStrategy,
4
- Component,
5
- contentChild,
6
- effect,
7
- TemplateRef,
8
- viewChild,
9
- ViewEncapsulation,
10
- } from "@angular/core";
11
- import { FormatPipe } from "../../core/format.pipe";
12
- import { injectParent } from "../../core/injectParent";
13
- import { SdButton } from "../../controls/button/sd-button";
14
- import { SdAnchor } from "../../controls/button/sd-anchor";
15
- import { SdForm } from "../../controls/form/sd-form";
16
- import { SdBaseContainer } from "../../layout/base-container/sd-base-container";
17
- import { NgIcon } from "@ng-icons/core";
18
- import {
19
- tablerDeviceFloppy,
20
- tablerEraser,
21
- tablerRefresh,
22
- tablerRestore,
23
- } from "@ng-icons/tabler-icons";
24
- import { SdDataDetailBase } from "./sd-data-detail.base";
25
- import { SdCommandDirective } from "../../core/commands/sd-command";
26
-
27
- export type { SdDataDetailDataInfo } from "./sd-data-detail.base";
28
-
29
- //#region SdDataDetail
30
-
31
- @Component({
32
- selector: "sd-data-detail",
33
- changeDetection: ChangeDetectionStrategy.OnPush,
34
- encapsulation: ViewEncapsulation.None,
35
- standalone: true,
36
- imports: [
37
- SdBaseContainer,
38
- SdForm,
39
- SdButton,
40
- NgTemplateOutlet,
41
- FormatPipe,
42
- SdAnchor,
43
- NgIcon,
44
- ],
45
- hostDirectives: [
46
- { directive: SdCommandDirective, outputs: ["sdRefreshCommand", "sdSaveCommand"] },
47
- ],
48
- host: {
49
- "(sdRefreshCommand)": "onRefreshButtonClick()",
50
- "(sdSaveCommand)": "onSubmitButtonClick()",
51
- },
52
- template: `
53
- <sd-base-container
54
- [busy]="parent.busyCount() > 0"
55
- [busyMessage]="parent.busyMessage()"
56
- [viewType]="parent.viewType()"
57
- [initialized]="parent.initialized()"
58
- [restricted]="!parent.canUse()"
59
- >
60
- <ng-template #pageTopbarTpl>
61
- @if (parent.canEdit() && parent.submit) {
62
- <sd-button [theme]="'link-primary'" (click)="onSubmitButtonClick()">
63
- <ng-icon [svg]="icons.tablerDeviceFloppy" />
64
- 저장
65
- <small>(CTRL+S)</small>
66
- </sd-button>
67
- }
68
- <sd-button [theme]="'link-info'" (click)="onRefreshButtonClick()">
69
- <ng-icon [svg]="icons.tablerRefresh" />
70
- 새로고침
71
- <small>(CTRL+ALT+L)</small>
72
- </sd-button>
73
- </ng-template>
74
-
75
- <ng-template #contentTpl>
76
- <div class="flex-column fill">
77
- @if ((parent.viewType() === "control" && parent.canEdit()) || toolTplRef()) {
78
- <div class="p-default flex-row gap-default bdb bdb-theme-gray-lightest">
79
- @if (parent.viewType() === "control" && parent.canEdit()) {
80
- @if (parent.submit) {
81
- <sd-button [theme]="'primary'" (click)="onSubmitButtonClick()">
82
- <ng-icon [svg]="icons.tablerDeviceFloppy" />
83
- 저장
84
- <small>(CTRL+S)</small>
85
- </sd-button>
86
- <sd-button [theme]="'info'" (click)="onRefreshButtonClick()">
87
- <ng-icon [svg]="icons.tablerRefresh" />
88
- 새로고침
89
- <small>(CTRL+ALT+L)</small>
90
- </sd-button>
91
- }
92
- @if (
93
- !parent.dataInfo()?.isNew &&
94
- parent.toggleDelete &&
95
- (!parent.canDelete || parent.canDelete())
96
- ) {
97
- @if (parent.dataInfo()?.isDeleted) {
98
- <sd-button [theme]="'warning'" (click)="onRestoreButtonClick()">
99
- <ng-icon [svg]="icons.tablerRestore" />
100
- 복구
101
- </sd-button>
102
- } @else {
103
- <sd-button [theme]="'danger'" (click)="onDeleteButtonClick()">
104
- <ng-icon [svg]="icons.tablerEraser" />
105
- 삭제
106
- </sd-button>
107
- }
108
- }
109
- }
110
-
111
- <ng-template [ngTemplateOutlet]="toolTplRef() ?? null" />
112
- </div>
113
- }
114
-
115
- @if (prevTplRef()) {
116
- <div>
117
- <ng-template [ngTemplateOutlet]="prevTplRef() ?? null" />
118
- </div>
119
- }
120
-
121
- <div class="flex-fill">
122
- <sd-form #formCtrl (formSubmit)="onSubmit()">
123
- <ng-template [ngTemplateOutlet]="contentTplRef()" />
124
- </sd-form>
125
- </div>
126
-
127
- @if (parent.dataInfo()?.lastModifiedAt || parent.dataInfo()?.lastModifiedBy) {
128
- <div
129
- class="p-sm-default"
130
- [class.bg-theme-gray-lightest]="parent.viewType() === 'modal'"
131
- >
132
- 최종수정:
133
- @if (parent.dataInfo()?.lastModifiedAt) {
134
- {{ parent.dataInfo()!.lastModifiedAt | format: "yyyy-MM-dd HH:mm" }}
135
- }
136
- @if (parent.dataInfo()?.lastModifiedBy) {
137
- ({{ parent.dataInfo()?.lastModifiedBy }})
138
- }
139
- </div>
140
- }
141
-
142
- @if (nextTplRef()) {
143
- <div>
144
- <ng-template [ngTemplateOutlet]="nextTplRef() ?? null" />
145
- </div>
146
- }
147
- </div>
148
- </ng-template>
149
-
150
- @if (parent.canEdit()) {
151
- <ng-template #modalBottomTpl>
152
- <div class="p-sm-default flex-row gap-sm">
153
- @if (
154
- !parent.dataInfo()?.isNew &&
155
- parent.toggleDelete &&
156
- (!parent.canDelete || parent.canDelete())
157
- ) {
158
- @if (parent.dataInfo()?.isDeleted) {
159
- <sd-button [size]="'sm'" [theme]="'warning'" (click)="onRestoreButtonClick()">
160
- 복구
161
- </sd-button>
162
- } @else {
163
- <sd-button [size]="'sm'" [theme]="'danger'" (click)="onDeleteButtonClick()">
164
- 삭제
165
- </sd-button>
166
- }
167
- }
168
-
169
- <div class="flex-fill flex-row gap-sm main-align-end">
170
- <sd-button [size]="'sm'" [theme]="'primary'" (click)="onSubmitButtonClick()">
171
- 확인
172
- </sd-button>
173
- </div>
174
- </div>
175
- </ng-template>
176
-
177
- <ng-template #modalActionTpl>
178
- <sd-anchor
179
- [theme]="'gray'"
180
- class="p-sm-default"
181
- (click)="onRefreshButtonClick()"
182
- title="새로고침(CTRL+ALT+L)"
183
- >
184
- <ng-icon [svg]="icons.tablerRefresh" />
185
- </sd-anchor>
186
- </ng-template>
187
- }
188
- </sd-base-container>
189
- `,
190
- })
191
- export class SdDataDetail {
192
- parent = injectParent<SdDataDetailBase<any>>();
193
-
194
- formCtrl = viewChild<SdForm>("formCtrl");
195
-
196
- toolTplRef = contentChild("toolTpl", { read: TemplateRef });
197
- prevTplRef = contentChild("prevTpl", { read: TemplateRef });
198
- contentTplRef = contentChild.required("contentTpl", { read: TemplateRef });
199
- nextTplRef = contentChild("nextTpl", { read: TemplateRef });
200
-
201
- modalActionTplRef = viewChild("modalActionTpl", { read: TemplateRef });
202
-
203
- constructor() {
204
- effect(() => {
205
- this.parent.actionTplRef = this.modalActionTplRef();
206
- });
207
- }
208
-
209
- async onRefreshButtonClick() {
210
- await this.parent.doRefresh();
211
- }
212
-
213
- async onDeleteButtonClick() {
214
- await this.parent.doToggleDelete(true);
215
- }
216
-
217
- async onRestoreButtonClick() {
218
- await this.parent.doToggleDelete(false);
219
- }
220
-
221
- onSubmitButtonClick() {
222
- this.formCtrl()?.requestSubmit();
223
- }
224
-
225
- async onSubmit() {
226
- await this.parent.doSubmit({ permCheck: true });
227
- }
228
-
229
- protected readonly icons = {
230
- tablerDeviceFloppy,
231
- tablerRefresh,
232
- tablerRestore,
233
- tablerEraser,
234
- };
235
- }
236
-
237
- //#endregion
@@ -1,104 +0,0 @@
1
- import {
2
- booleanAttribute,
3
- computed,
4
- Directive,
5
- effect,
6
- inject,
7
- input,
8
- model,
9
- signal,
10
- type Signal,
11
- } from "@angular/core";
12
- import type { SelectModeValue } from "../../controls/select/sd-select";
13
- import {
14
- SdModalProvider,
15
- type SdModalOptions,
16
- } from "../../core/modal/sd-modal.provider";
17
- import type {
18
- SdSelectModal,
19
- SdSelectModalInfo,
20
- } from "../../controls/button/sd-modal-select-button";
21
- import { setupInvalid } from "../../core/validation/setupInvalid";
22
-
23
- @Directive()
24
- export abstract class SdDataSelectButtonBase<
25
- TItem extends object,
26
- TKey,
27
- TMode extends keyof SelectModeValue<TKey> = keyof SelectModeValue<TKey>,
28
- > {
29
- //-- abstract
30
- abstract modal: Signal<SdSelectModalInfo<SdSelectModal<any>>>;
31
- abstract load(keys: TKey[]): Promise<TItem[]> | TItem[];
32
-
33
- //-- implement
34
-
35
- protected readonly _sdModal = inject(SdModalProvider);
36
-
37
- value = model<SelectModeValue<TKey>[TMode]>();
38
-
39
- disabled = input(false, { transform: booleanAttribute });
40
- required = input(false, { transform: booleanAttribute });
41
- inset = input(false, { transform: booleanAttribute });
42
- size = input<"sm" | "lg">();
43
- selectMode = input<TMode>("single" as TMode);
44
-
45
- isNoValue = computed(() => {
46
- const v = this.value();
47
- if (v == null) return true;
48
- if (Array.isArray(v)) return v.length === 0;
49
- return false;
50
- });
51
-
52
- selectedItems = signal<TItem[]>([]);
53
-
54
- constructor() {
55
- setupInvalid(() => (this.required() && this.value() == null ? "값을 입력하세요." : ""));
56
-
57
- effect(() => {
58
- const value = this.value();
59
- const selectMode = this.selectMode();
60
-
61
- queueMicrotask(async () => {
62
- if (
63
- selectMode === "multi" &&
64
- Array.isArray(value) &&
65
- value.filterExists().length > 0
66
- ) {
67
- this.selectedItems.set(await this.load(value.filterExists() as TKey[]));
68
- } else if (selectMode === "single" && !Array.isArray(value) && value != null) {
69
- this.selectedItems.set(await this.load([value as TKey]));
70
- } else {
71
- this.selectedItems.set([]);
72
- }
73
- });
74
- });
75
- }
76
-
77
- async doShowModal(options?: SdModalOptions): Promise<void> {
78
- const modal = this.modal();
79
- const result = await this._sdModal.showAsync(
80
- {
81
- ...modal,
82
- inputs: {
83
- selectMode: this.selectMode(),
84
- selectedItemKeys: (this.selectMode() === "multi"
85
- ? ((this.value() as any[] | undefined) ?? [])
86
- : [this.value()]
87
- ).filterExists(),
88
- ...modal.inputs,
89
- },
90
- },
91
- options,
92
- );
93
-
94
- if (result) {
95
- const newValue =
96
- this.selectMode() === "multi" ? result.selectedItemKeys : result.selectedItemKeys[0];
97
- this.value.set(newValue);
98
- }
99
- }
100
-
101
- doInitialValue(): void {
102
- this.value.set((this.selectMode() === "multi" ? [] : undefined) as any);
103
- }
104
- }
@@ -1,105 +0,0 @@
1
- import {
2
- ChangeDetectionStrategy,
3
- Component,
4
- contentChild,
5
- TemplateRef,
6
- ViewEncapsulation,
7
- } from "@angular/core";
8
- import {
9
- SdItemOfTemplate,
10
- type SdItemOfTemplateContext,
11
- } from "../../core/template/sd-item-of-template";
12
- import { NgTemplateOutlet } from "@angular/common";
13
- import { injectParent } from "../../core/injectParent";
14
- import { SdAdditionalButton } from "../../controls/button/sd-additional-button";
15
- import { SdAnchor } from "../../controls/button/sd-anchor";
16
- import { SdButton } from "../../controls/button/sd-button";
17
- import { NgIcon } from "@ng-icons/core";
18
- import { tablerEraser, tablerSearch } from "@ng-icons/tabler-icons";
19
- import { SdDataSelectButtonBase } from "./sd-data-select-button.base";
20
-
21
- //#region SdDataSelectButton
22
-
23
- @Component({
24
- selector: "sd-data-select-button",
25
- changeDetection: ChangeDetectionStrategy.OnPush,
26
- encapsulation: ViewEncapsulation.None,
27
- standalone: true,
28
- imports: [
29
- SdAdditionalButton,
30
- NgTemplateOutlet,
31
- SdAnchor,
32
- SdButton,
33
- NgIcon,
34
- ],
35
- template: `
36
- <sd-additional-button [inset]="parent.inset()" [size]="parent.size()">
37
- @if (itemTplRef()) {
38
- @for (item of parent.selectedItems(); track item; let index = $index) {
39
- @if (index !== 0) {
40
- <div style="display: inline-block">,&nbsp;</div>
41
- }
42
- <div style="display: inline-block">
43
- <ng-template
44
- [ngTemplateOutlet]="itemTplRef()!"
45
- [ngTemplateOutletContext]="{
46
- $implicit: item,
47
- item: item,
48
- index: index,
49
- depth: 0,
50
- }"
51
- ></ng-template>
52
- </div>
53
- }
54
- }
55
- <ng-content />
56
-
57
- @if (!parent.disabled() && !parent.isNoValue() && !parent.required()) {
58
- <sd-anchor [theme]="'danger'" (click)="onEraseClick()">
59
- <ng-icon [svg]="tablerEraser" />
60
- </sd-anchor>
61
- }
62
-
63
- @if (!parent.disabled()) {
64
- <sd-button (click)="onSearchClick($event)" [inset]="true">
65
- <ng-icon [svg]="tablerSearch" />
66
- </sd-button>
67
- }
68
- </sd-additional-button>
69
- `,
70
- styles: [
71
- /* language=SCSS */ `
72
- sd-data-select-button {
73
- display: block;
74
- width: 100%;
75
- min-width: 3em;
76
- }
77
- `,
78
- ],
79
- host: {
80
- "[attr.data-sd-disabled]": "parent.disabled()",
81
- },
82
- })
83
- export class SdDataSelectButton {
84
- parent = injectParent<SdDataSelectButtonBase<any, any>>();
85
-
86
- itemTplRef = contentChild<any, TemplateRef<SdItemOfTemplateContext<any>>>(
87
- SdItemOfTemplate,
88
- { read: TemplateRef },
89
- );
90
-
91
- async onSearchClick(event: MouseEvent): Promise<void> {
92
- event.preventDefault();
93
- event.stopPropagation();
94
- await this.parent.doShowModal();
95
- }
96
-
97
- onEraseClick(): void {
98
- this.parent.doInitialValue();
99
- }
100
-
101
- protected readonly tablerEraser = tablerEraser;
102
- protected readonly tablerSearch = tablerSearch;
103
- }
104
-
105
- //#endregion
@@ -1,57 +0,0 @@
1
- import { type WritableSignal, inject } from "@angular/core";
2
- import { SdToastProvider } from "../../core/toast/sd-toast.provider";
3
- import { SdFileDialogProvider } from "../../core/file-dialog/sd-file-dialog.provider";
4
- import { withBusy } from "../../core/withBusy";
5
- import type { SdDataSheetSearchResult } from "./sd-data-sheet.types";
6
-
7
- export function injectDataSheetExcelManager<TItem>(options: {
8
- busyCount: WritableSignal<number>;
9
- search: (
10
- usePagination: boolean,
11
- ) => Promise<SdDataSheetSearchResult<TItem>> | SdDataSheetSearchResult<TItem>;
12
- refresh: () => Promise<void>;
13
- getDownloadExcelFn: () =>
14
- | ((items: TItem[]) => Promise<void> | void)
15
- | undefined;
16
- getUploadExcelFn: () => ((file: File) => Promise<void> | void) | undefined;
17
- errorMessageFn: (err: unknown) => string;
18
- }) {
19
- const sdToast = inject(SdToastProvider);
20
- const sdFileDialog = inject(SdFileDialogProvider);
21
-
22
- async function doDownloadExcel(): Promise<void> {
23
- const downloadExcelFn = options.getDownloadExcelFn();
24
- if (!downloadExcelFn) return;
25
-
26
- await withBusy(options.busyCount, () =>
27
- sdToast.try(async () => {
28
- const items = (await options.search(false)).items;
29
- await downloadExcelFn(items);
30
- }),
31
- );
32
- }
33
-
34
- async function doUploadExcel(): Promise<void> {
35
- const uploadExcelFn = options.getUploadExcelFn();
36
- if (!uploadExcelFn) return;
37
-
38
- const file = await sdFileDialog.showAsync(
39
- false,
40
- "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
41
- );
42
- if (!file) return;
43
-
44
- await withBusy(options.busyCount, () =>
45
- sdToast.try(
46
- async () => {
47
- await uploadExcelFn(file);
48
- await options.refresh();
49
- sdToast.success("엑셀 업로드가 완료되었습니다.");
50
- },
51
- (err) => options.errorMessageFn(err),
52
- ),
53
- );
54
- }
55
-
56
- return { doDownloadExcel, doUploadExcel };
57
- }
@@ -1,89 +0,0 @@
1
- import {
2
- type OutputEmitterRef,
3
- type WritableSignal,
4
- inject,
5
- } from "@angular/core";
6
- import type { ArrayOneWayDiffResult } from "@simplysm/core-common";
7
- import { mark } from "../../core/mark";
8
- import { SdToastProvider } from "../../core/toast/sd-toast.provider";
9
- import { withBusy } from "../../core/withBusy";
10
- import type { SdDataSheetItemPropInfo } from "./sd-data-sheet.types";
11
-
12
- export function injectDataSheetInlineEditManager<TItem, TKey>(options: {
13
- busyCount: WritableSignal<number>;
14
- canEdit: () => boolean;
15
- items: WritableSignal<TItem[]>;
16
- submitted: OutputEmitterRef<boolean>;
17
- itemPropInfo: () => SdDataSheetItemPropInfo<TItem>;
18
- getItemInfoFn: (item: TItem) => { key: TKey };
19
- getDiffs: () => ArrayOneWayDiffResult<TItem>[];
20
- refresh: () => Promise<void>;
21
- getNewItemFn: () => (() => Promise<TItem> | TItem) | undefined;
22
- getSubmitFn: () =>
23
- | ((diffs: ArrayOneWayDiffResult<TItem>[]) => Promise<boolean> | boolean)
24
- | undefined;
25
- errorMessageFn: (err: unknown) => string;
26
- }) {
27
- const sdToast = inject(SdToastProvider);
28
-
29
- async function doAddItem(): Promise<void> {
30
- const newItemFn = options.getNewItemFn();
31
- if (!newItemFn) return;
32
-
33
- await withBusy(options.busyCount, () =>
34
- sdToast.try(async () => {
35
- const newItem = await newItemFn();
36
- options.items.update((items) => [newItem, ...items]);
37
- }),
38
- );
39
- }
40
-
41
- async function doSubmit(opt?: {
42
- permCheck?: boolean;
43
- hideNoChangeMessage?: boolean;
44
- }): Promise<void> {
45
- if (options.busyCount() > 0) return;
46
- if (opt?.permCheck && !options.canEdit()) return;
47
- const submitFn = options.getSubmitFn();
48
- if (!submitFn) return;
49
-
50
- const diffs = options.getDiffs();
51
-
52
- if (diffs.length === 0) {
53
- if (!opt?.hideNoChangeMessage) {
54
- sdToast.info("변경사항이 없습니다.");
55
- }
56
- return;
57
- }
58
-
59
- await withBusy(options.busyCount, () =>
60
- sdToast.try(
61
- async () => {
62
- const result = await submitFn(diffs);
63
- if (!result) return;
64
-
65
- sdToast.success("저장되었습니다.");
66
- await options.refresh();
67
- options.submitted.emit(true);
68
- },
69
- (err) => options.errorMessageFn(err),
70
- ),
71
- );
72
- }
73
-
74
- function doToggleDeleteItem(item: TItem): void {
75
- if (!options.canEdit()) return;
76
- const propInfo = options.itemPropInfo();
77
- if (propInfo.isDeleted == null) return;
78
-
79
- if (options.getItemInfoFn(item).key == null) {
80
- options.items.update((items) => items.filter((item1) => item1 !== item));
81
- return;
82
- }
83
-
84
- (item[propInfo.isDeleted] as boolean) = !(item[propInfo.isDeleted] as boolean);
85
- mark(options.items);
86
- }
87
-
88
- return { doAddItem, doSubmit, doToggleDeleteItem };
89
- }
@@ -1,76 +0,0 @@
1
- import {
2
- type OutputEmitterRef,
3
- type Signal,
4
- type WritableSignal,
5
- inject,
6
- } from "@angular/core";
7
- import { SdToastProvider } from "../../core/toast/sd-toast.provider";
8
- import { withBusy } from "../../core/withBusy";
9
- import type { SelectModalOutputResult } from "../../core/select-modal-output-result";
10
-
11
- export function injectDataSheetModalEditManager<TItem, TKey>(options: {
12
- busyCount: WritableSignal<number>;
13
- canEdit: () => boolean;
14
- selectedItemKeys: Signal<TKey[]>;
15
- selectedItems: Signal<TItem[]>;
16
- close: OutputEmitterRef<SelectModalOutputResult<TItem>>;
17
- refresh: () => Promise<void>;
18
- getEditItemFn: () =>
19
- | ((item?: TItem) => Promise<boolean | undefined> | boolean | undefined)
20
- | undefined;
21
- getToggleDeleteItemsFn: () =>
22
- | ((del: boolean) => Promise<boolean>)
23
- | undefined;
24
- errorMessageFn: (err: unknown) => string;
25
- }) {
26
- const sdToast = inject(SdToastProvider);
27
-
28
- async function doEditItem(item?: TItem): Promise<void> {
29
- const editItemFn = options.getEditItemFn();
30
- if (!editItemFn) return;
31
-
32
- const result = await editItemFn(item);
33
- if (!result) return;
34
-
35
- await withBusy(options.busyCount, () =>
36
- sdToast.try(async () => {
37
- await options.refresh();
38
- }),
39
- );
40
- }
41
-
42
- async function doToggleDeleteItems(del: boolean): Promise<void> {
43
- if (!options.canEdit()) return;
44
- const toggleDeleteItemsFn = options.getToggleDeleteItemsFn();
45
- if (!toggleDeleteItemsFn) return;
46
-
47
- await withBusy(options.busyCount, () =>
48
- sdToast.try(
49
- async () => {
50
- const result = await toggleDeleteItemsFn(del);
51
- if (!result) return;
52
-
53
- await options.refresh();
54
- sdToast.success(`${del ? "삭제" : "복구"}되었습니다.`);
55
- },
56
- (err) => options.errorMessageFn(err),
57
- ),
58
- );
59
- }
60
-
61
- function doModalConfirm(): void {
62
- options.close.emit({
63
- selectedItemKeys: options.selectedItemKeys(),
64
- selectedItems: options.selectedItems(),
65
- });
66
- }
67
-
68
- function doModalCancel(): void {
69
- options.close.emit({
70
- selectedItemKeys: [],
71
- selectedItems: [],
72
- });
73
- }
74
-
75
- return { doEditItem, doToggleDeleteItems, doModalConfirm, doModalCancel };
76
- }