@simplysm/angular 14.0.8 → 14.0.9

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 (272) hide show
  1. package/README.md +234 -246
  2. package/dist/core/commons.d.ts.map +1 -1
  3. package/dist/core/directives/sd-events.directive.d.ts.map +1 -1
  4. package/dist/core/directives/sd-invalid.directive.d.ts.map +1 -1
  5. package/dist/core/directives/sd-item-of-template.directive.d.ts.map +1 -1
  6. package/dist/core/directives/sd-ripple.directive.d.ts.map +1 -1
  7. package/dist/core/directives/sd-router-link.directive.d.ts.map +1 -1
  8. package/dist/core/directives/sd-show-effect.directive.d.ts.map +1 -1
  9. package/dist/core/directives/sd-typed-template.directive.d.ts.map +1 -1
  10. package/dist/core/pipes/format.pipe.d.ts.map +1 -1
  11. package/dist/core/plugins/commands/findTopOpenModalEl.d.ts.map +1 -1
  12. package/dist/core/plugins/commands/sd-insert-command-event.plugin.d.ts.map +1 -1
  13. package/dist/core/plugins/commands/sd-refresh-command-event.plugin.d.ts.map +1 -1
  14. package/dist/core/plugins/commands/sd-save-command-event.plugin.d.ts.map +1 -1
  15. package/dist/core/plugins/events/sd-intersection-event.plugin.d.ts.map +1 -1
  16. package/dist/core/plugins/events/sd-option-event.plugin.d.ts.map +1 -1
  17. package/dist/core/plugins/events/sd-resize-event.plugin.d.ts.map +1 -1
  18. package/dist/core/plugins/sd-global-error-handler.plugin.d.ts.map +1 -1
  19. package/dist/core/provideSdAngular.d.ts.map +1 -1
  20. package/dist/core/providers/sd-angular-config.provider.d.ts.map +1 -1
  21. package/dist/core/providers/sd-app-structure.provider.d.ts.map +1 -1
  22. package/dist/core/providers/sd-file-dialog.provider.d.ts.map +1 -1
  23. package/dist/core/providers/sd-local-storage.provider.d.ts.map +1 -1
  24. package/dist/core/providers/sd-navigate-window.provider.d.ts.map +1 -1
  25. package/dist/core/providers/sd-print.provider.d.ts +7 -2
  26. package/dist/core/providers/sd-print.provider.d.ts.map +1 -1
  27. package/dist/core/providers/sd-service-client-factory.provider.d.ts.map +1 -1
  28. package/dist/core/providers/sd-shared-data.provider.d.ts +3 -0
  29. package/dist/core/providers/sd-shared-data.provider.d.ts.map +1 -1
  30. package/dist/core/providers/sd-system-config.provider.d.ts.map +1 -1
  31. package/dist/core/providers/sd-system-log.provider.d.ts.map +1 -1
  32. package/dist/core/providers/sd-theme-provider.d.ts.map +1 -1
  33. package/dist/core/utils/TDirectiveInputSignals.d.ts +4 -0
  34. package/dist/core/utils/TDirectiveInputSignals.d.ts.map +1 -1
  35. package/dist/core/utils/injectParent.d.ts.map +1 -1
  36. package/dist/core/utils/setSafeStyle.d.ts.map +1 -1
  37. package/dist/core/utils/setups/setupBgTheme.d.ts.map +1 -1
  38. package/dist/core/utils/setups/setupCanDeactivate.d.ts.map +1 -1
  39. package/dist/core/utils/setups/setupCloserWhenSingleSelectionChange.d.ts +9 -0
  40. package/dist/core/utils/setups/setupCloserWhenSingleSelectionChange.d.ts.map +1 -0
  41. package/dist/core/utils/setups/setupCloserWhenSingleSelectionChange.js +20 -0
  42. package/dist/core/utils/setups/setupCumulateSelectedKeys.d.ts +9 -0
  43. package/dist/core/utils/setups/setupCumulateSelectedKeys.d.ts.map +1 -0
  44. package/dist/core/utils/setups/setupCumulateSelectedKeys.js +27 -0
  45. package/dist/core/utils/setups/setupInvalid.d.ts.map +1 -1
  46. package/dist/core/utils/setups/setupModelHook.d.ts.map +1 -1
  47. package/dist/core/utils/setups/setupRevealOnShow.d.ts.map +1 -1
  48. package/dist/core/utils/setups/setupRipple.d.ts.map +1 -1
  49. package/dist/core/utils/useCurrentPageCodeSignal.d.ts.map +1 -1
  50. package/dist/core/utils/useExpandingManager.d.ts.map +1 -1
  51. package/dist/core/utils/useFullPageCodeSignal.d.ts.map +1 -1
  52. package/dist/core/utils/useSdSystemConfigResource.d.ts.map +1 -1
  53. package/dist/core/utils/useSelectionManager.d.ts.map +1 -1
  54. package/dist/core/utils/useSortingManager.d.ts.map +1 -1
  55. package/dist/core/utils/useViewTitleSignal.d.ts.map +1 -1
  56. package/dist/core/utils/useViewTypeSignal.d.ts.map +1 -1
  57. package/dist/features/address/sd-address-search.modal.d.ts +19 -0
  58. package/dist/features/address/sd-address-search.modal.d.ts.map +1 -0
  59. package/dist/features/address/sd-address-search.modal.js +88 -0
  60. package/dist/features/base/sd-base-container.control.d.ts.map +1 -1
  61. package/dist/features/base/sd-base-container.control.js +1 -1
  62. package/dist/features/data-view/sd-data-detail.control.d.ts +70 -0
  63. package/dist/features/data-view/sd-data-detail.control.d.ts.map +1 -0
  64. package/dist/features/data-view/sd-data-detail.control.js +606 -0
  65. package/dist/features/data-view/sd-data-select-button.control.d.ts +35 -0
  66. package/dist/features/data-view/sd-data-select-button.control.d.ts.map +1 -0
  67. package/dist/features/data-view/sd-data-select-button.control.js +219 -0
  68. package/dist/features/data-view/sd-data-sheet-column.directive.d.ts +8 -0
  69. package/dist/features/data-view/sd-data-sheet-column.directive.d.ts.map +1 -0
  70. package/dist/features/data-view/sd-data-sheet-column.directive.js +15 -0
  71. package/dist/features/data-view/sd-data-sheet.control.d.ts +131 -0
  72. package/dist/features/data-view/sd-data-sheet.control.d.ts.map +1 -0
  73. package/dist/features/data-view/sd-data-sheet.control.js +1152 -0
  74. package/dist/features/shared-data/sd-shared-data-select-button.control.d.ts +16 -0
  75. package/dist/features/shared-data/sd-shared-data-select-button.control.d.ts.map +1 -0
  76. package/dist/features/shared-data/sd-shared-data-select-button.control.js +66 -0
  77. package/dist/features/shared-data/sd-shared-data-select-list.control.d.ts +33 -0
  78. package/dist/features/shared-data/sd-shared-data-select-list.control.d.ts.map +1 -0
  79. package/dist/features/shared-data/sd-shared-data-select-list.control.js +346 -0
  80. package/dist/features/shared-data/sd-shared-data-select.control.d.ts +50 -0
  81. package/dist/features/shared-data/sd-shared-data-select.control.d.ts.map +1 -0
  82. package/dist/features/shared-data/sd-shared-data-select.control.js +425 -0
  83. package/dist/index.d.ts +11 -1
  84. package/dist/index.d.ts.map +1 -1
  85. package/dist/index.js +12 -0
  86. package/dist/ui/data/list/sd-list-item.control.d.ts +3 -1
  87. package/dist/ui/data/list/sd-list-item.control.d.ts.map +1 -1
  88. package/dist/ui/data/list/sd-list-item.control.js +8 -4
  89. package/dist/ui/data/list/sd-list.control.d.ts.map +1 -1
  90. package/dist/ui/data/list/sd-list.control.js +1 -1
  91. package/dist/ui/data/sheet/sd-sheet-column.directive.d.ts +8 -1
  92. package/dist/ui/data/sheet/sd-sheet-column.directive.d.ts.map +1 -1
  93. package/dist/ui/data/sheet/sd-sheet-config.modal.d.ts.map +1 -1
  94. package/dist/ui/data/sheet/sd-sheet-config.modal.js +1 -1
  95. package/dist/ui/data/sheet/sd-sheet.control.d.ts +4 -4
  96. package/dist/ui/data/sheet/sd-sheet.control.d.ts.map +1 -1
  97. package/dist/ui/data/sheet/sd-sheet.control.js +3 -4
  98. package/dist/ui/data/sheet/types.d.ts.map +1 -1
  99. package/dist/ui/data/sheet/useSheetCellAgent.d.ts.map +1 -1
  100. package/dist/ui/data/sheet/useSheetColumnFixing.d.ts.map +1 -1
  101. package/dist/ui/data/sheet/useSheetDomAccessor.d.ts.map +1 -1
  102. package/dist/ui/data/sheet/useSheetLayoutEngine.d.ts.map +1 -1
  103. package/dist/ui/form/button/sd-additional-button.control.d.ts.map +1 -1
  104. package/dist/ui/form/button/sd-additional-button.control.js +1 -1
  105. package/dist/ui/form/button/sd-anchor.control.d.ts.map +1 -1
  106. package/dist/ui/form/button/sd-anchor.control.js +1 -1
  107. package/dist/ui/form/button/sd-button.control.d.ts.map +1 -1
  108. package/dist/ui/form/button/sd-button.control.js +1 -1
  109. package/dist/ui/form/button/sd-modal-select-button.control.d.ts +16 -18
  110. package/dist/ui/form/button/sd-modal-select-button.control.d.ts.map +1 -1
  111. package/dist/ui/form/button/sd-modal-select-button.control.js +22 -39
  112. package/dist/ui/form/checkbox/sd-checkbox-group-item.control.d.ts.map +1 -1
  113. package/dist/ui/form/checkbox/sd-checkbox-group-item.control.js +1 -1
  114. package/dist/ui/form/checkbox/sd-checkbox-group.control.d.ts.map +1 -1
  115. package/dist/ui/form/checkbox/sd-checkbox-group.control.js +1 -1
  116. package/dist/ui/form/checkbox/sd-checkbox.control.d.ts.map +1 -1
  117. package/dist/ui/form/checkbox/sd-checkbox.control.js +1 -1
  118. package/dist/ui/form/checkbox/sd-switch.control.d.ts.map +1 -1
  119. package/dist/ui/form/checkbox/sd-switch.control.js +1 -1
  120. package/dist/ui/form/choice/sd-state-preset.control.d.ts.map +1 -1
  121. package/dist/ui/form/choice/sd-state-preset.control.js +1 -1
  122. package/dist/ui/form/editor/sd-tiptap-editor.control.d.ts +18 -1
  123. package/dist/ui/form/editor/sd-tiptap-editor.control.d.ts.map +1 -1
  124. package/dist/ui/form/editor/sd-tiptap-editor.control.js +18 -2
  125. package/dist/ui/form/input/sd-date-range.picker.d.ts.map +1 -1
  126. package/dist/ui/form/input/sd-date-range.picker.js +1 -1
  127. package/dist/ui/form/input/sd-numpad.control.d.ts.map +1 -1
  128. package/dist/ui/form/input/sd-numpad.control.js +1 -1
  129. package/dist/ui/form/input/sd-range.control.d.ts.map +1 -1
  130. package/dist/ui/form/input/sd-range.control.js +1 -1
  131. package/dist/ui/form/input/sd-textarea.control.d.ts.map +1 -1
  132. package/dist/ui/form/input/sd-textarea.control.js +1 -1
  133. package/dist/ui/form/input/sd-textfield-type-handlers.d.ts.map +1 -1
  134. package/dist/ui/form/input/sd-textfield.control.d.ts.map +1 -1
  135. package/dist/ui/form/input/sd-textfield.control.js +1 -1
  136. package/dist/ui/form/sd-form.control.d.ts.map +1 -1
  137. package/dist/ui/form/sd-form.control.js +1 -1
  138. package/dist/ui/form/select/sd-select-button.control.d.ts.map +1 -1
  139. package/dist/ui/form/select/sd-select-button.control.js +1 -1
  140. package/dist/ui/form/select/sd-select-item.control.d.ts +1 -1
  141. package/dist/ui/form/select/sd-select-item.control.d.ts.map +1 -1
  142. package/dist/ui/form/select/sd-select-item.control.js +1 -1
  143. package/dist/ui/form/select/sd-select.control.d.ts +9 -6
  144. package/dist/ui/form/select/sd-select.control.d.ts.map +1 -1
  145. package/dist/ui/form/select/sd-select.control.js +1 -1
  146. package/dist/ui/layout/dock/sd-dock-container.control.d.ts.map +1 -1
  147. package/dist/ui/layout/dock/sd-dock-container.control.js +1 -1
  148. package/dist/ui/layout/dock/sd-dock.control.d.ts.map +1 -1
  149. package/dist/ui/layout/dock/sd-dock.control.js +1 -1
  150. package/dist/ui/layout/kanban/sd-kanban-board.control.d.ts.map +1 -1
  151. package/dist/ui/layout/kanban/sd-kanban-board.control.js +1 -1
  152. package/dist/ui/layout/kanban/sd-kanban-lane.control.d.ts.map +1 -1
  153. package/dist/ui/layout/kanban/sd-kanban-lane.control.js +1 -1
  154. package/dist/ui/layout/kanban/sd-kanban.control.d.ts.map +1 -1
  155. package/dist/ui/layout/kanban/sd-kanban.control.js +1 -1
  156. package/dist/ui/layout/sd-card.directive.d.ts.map +1 -1
  157. package/dist/ui/layout/sd-gap.control.d.ts.map +1 -1
  158. package/dist/ui/layout/sd-gap.control.js +1 -1
  159. package/dist/ui/layout/sd-pane.directive.d.ts.map +1 -1
  160. package/dist/ui/layout/view/sd-view-item.control.d.ts.map +1 -1
  161. package/dist/ui/layout/view/sd-view-item.control.js +1 -1
  162. package/dist/ui/layout/view/sd-view.control.d.ts.map +1 -1
  163. package/dist/ui/layout/view/sd-view.control.js +1 -1
  164. package/dist/ui/navigation/collapse/sd-collapse-icon.control.d.ts.map +1 -1
  165. package/dist/ui/navigation/collapse/sd-collapse-icon.control.js +1 -1
  166. package/dist/ui/navigation/collapse/sd-collapse.control.d.ts.map +1 -1
  167. package/dist/ui/navigation/collapse/sd-collapse.control.js +1 -1
  168. package/dist/ui/navigation/menu-utils.d.ts.map +1 -1
  169. package/dist/ui/navigation/pagination/sd-pagination.control.d.ts.map +1 -1
  170. package/dist/ui/navigation/pagination/sd-pagination.control.js +1 -1
  171. package/dist/ui/navigation/sidebar/sd-sidebar-container.control.d.ts.map +1 -1
  172. package/dist/ui/navigation/sidebar/sd-sidebar-container.control.js +1 -1
  173. package/dist/ui/navigation/sidebar/sd-sidebar-menu.control.d.ts.map +1 -1
  174. package/dist/ui/navigation/sidebar/sd-sidebar-menu.control.js +1 -1
  175. package/dist/ui/navigation/sidebar/sd-sidebar-user.control.d.ts.map +1 -1
  176. package/dist/ui/navigation/sidebar/sd-sidebar-user.control.js +1 -1
  177. package/dist/ui/navigation/sidebar/sd-sidebar.control.d.ts.map +1 -1
  178. package/dist/ui/navigation/sidebar/sd-sidebar.control.js +1 -1
  179. package/dist/ui/navigation/tab/sd-tab-item.control.d.ts.map +1 -1
  180. package/dist/ui/navigation/tab/sd-tab-item.control.js +1 -1
  181. package/dist/ui/navigation/tab/sd-tab.control.d.ts.map +1 -1
  182. package/dist/ui/navigation/tab/sd-tab.control.js +1 -1
  183. package/dist/ui/navigation/tab/sd-tabview-item.control.d.ts.map +1 -1
  184. package/dist/ui/navigation/tab/sd-tabview-item.control.js +1 -1
  185. package/dist/ui/navigation/tab/sd-tabview.control.d.ts.map +1 -1
  186. package/dist/ui/navigation/tab/sd-tabview.control.js +1 -1
  187. package/dist/ui/navigation/topbar/sd-topbar-container.control.d.ts.map +1 -1
  188. package/dist/ui/navigation/topbar/sd-topbar-container.control.js +1 -1
  189. package/dist/ui/navigation/topbar/sd-topbar-menu.control.d.ts.map +1 -1
  190. package/dist/ui/navigation/topbar/sd-topbar-menu.control.js +1 -1
  191. package/dist/ui/navigation/topbar/sd-topbar-user.control.d.ts.map +1 -1
  192. package/dist/ui/navigation/topbar/sd-topbar-user.control.js +1 -1
  193. package/dist/ui/navigation/topbar/sd-topbar.control.d.ts.map +1 -1
  194. package/dist/ui/navigation/topbar/sd-topbar.control.js +1 -1
  195. package/dist/ui/overlay/busy/sd-busy-container.control.d.ts.map +1 -1
  196. package/dist/ui/overlay/busy/sd-busy-container.control.js +1 -1
  197. package/dist/ui/overlay/busy/sd-busy.provider.d.ts.map +1 -1
  198. package/dist/ui/overlay/dropdown/sd-dropdown-popup.control.d.ts.map +1 -1
  199. package/dist/ui/overlay/dropdown/sd-dropdown-popup.control.js +1 -1
  200. package/dist/ui/overlay/dropdown/sd-dropdown.control.d.ts.map +1 -1
  201. package/dist/ui/overlay/dropdown/sd-dropdown.control.js +1 -1
  202. package/dist/ui/overlay/modal/sd-confirm-modal.control.d.ts.map +1 -1
  203. package/dist/ui/overlay/modal/sd-confirm-modal.control.js +1 -1
  204. package/dist/ui/overlay/modal/sd-modal.control.d.ts.map +1 -1
  205. package/dist/ui/overlay/modal/sd-modal.control.js +1 -1
  206. package/dist/ui/overlay/modal/sd-modal.provider.d.ts +9 -3
  207. package/dist/ui/overlay/modal/sd-modal.provider.d.ts.map +1 -1
  208. package/dist/ui/overlay/modal/sd-prompt-modal.control.d.ts.map +1 -1
  209. package/dist/ui/overlay/modal/sd-prompt-modal.control.js +1 -1
  210. package/dist/ui/overlay/toast/sd-toast-container.control.d.ts.map +1 -1
  211. package/dist/ui/overlay/toast/sd-toast-container.control.js +1 -1
  212. package/dist/ui/overlay/toast/sd-toast.control.d.ts.map +1 -1
  213. package/dist/ui/overlay/toast/sd-toast.control.js +1 -1
  214. package/dist/ui/overlay/toast/sd-toast.provider.d.ts.map +1 -1
  215. package/dist/ui/visual/sd-barcode.control.d.ts.map +1 -1
  216. package/dist/ui/visual/sd-barcode.control.js +1 -1
  217. package/dist/ui/visual/sd-calendar.control.d.ts.map +1 -1
  218. package/dist/ui/visual/sd-calendar.control.js +1 -1
  219. package/dist/ui/visual/sd-echarts.control.d.ts.map +1 -1
  220. package/dist/ui/visual/sd-echarts.control.js +1 -1
  221. package/dist/ui/visual/sd-label.control.d.ts.map +1 -1
  222. package/dist/ui/visual/sd-label.control.js +1 -1
  223. package/dist/ui/visual/sd-note.control.d.ts.map +1 -1
  224. package/dist/ui/visual/sd-note.control.js +1 -1
  225. package/dist/ui/visual/sd-progress.control.d.ts.map +1 -1
  226. package/dist/ui/visual/sd-progress.control.js +1 -1
  227. package/docs/core.md +877 -0
  228. package/docs/features.md +377 -0
  229. package/docs/styling.md +309 -0
  230. package/docs/ui-data.md +233 -0
  231. package/docs/ui-form.md +421 -0
  232. package/docs/ui-layout.md +216 -0
  233. package/docs/ui-navigation.md +279 -0
  234. package/docs/ui-overlay.md +330 -0
  235. package/docs/ui-visual.md +131 -0
  236. package/package.json +5 -5
  237. package/src/core/providers/sd-print.provider.ts +10 -2
  238. package/src/core/providers/sd-shared-data.provider.ts +3 -0
  239. package/src/core/utils/TDirectiveInputSignals.ts +5 -0
  240. package/src/core/utils/setups/setupCloserWhenSingleSelectionChange.ts +29 -0
  241. package/src/core/utils/setups/setupCumulateSelectedKeys.ts +38 -0
  242. package/src/features/address/sd-address-search.modal.ts +119 -0
  243. package/src/features/data-view/sd-data-detail.control.ts +394 -0
  244. package/src/features/data-view/sd-data-select-button.control.ts +210 -0
  245. package/src/features/data-view/sd-data-sheet-column.directive.ts +10 -0
  246. package/src/features/data-view/sd-data-sheet.control.ts +850 -0
  247. package/src/features/shared-data/sd-shared-data-select-button.control.ts +64 -0
  248. package/src/features/shared-data/sd-shared-data-select-list.control.ts +245 -0
  249. package/src/features/shared-data/sd-shared-data-select.control.ts +364 -0
  250. package/src/index.ts +35 -1
  251. package/src/scss.d.ts +5 -0
  252. package/src/ui/data/list/sd-list-item.control.ts +3 -1
  253. package/src/ui/data/sheet/sd-sheet-column.directive.ts +9 -1
  254. package/src/ui/data/sheet/sd-sheet.control.ts +6 -6
  255. package/src/ui/form/button/sd-modal-select-button.control.ts +41 -59
  256. package/src/ui/form/editor/sd-tiptap-editor.control.ts +35 -1
  257. package/src/ui/form/select/sd-select-item.control.ts +1 -1
  258. package/src/ui/form/select/sd-select.control.ts +7 -4
  259. package/src/ui/overlay/modal/sd-modal.provider.ts +15 -3
  260. package/docs/commons-and-configuration.md +0 -34
  261. package/docs/data-display-components.md +0 -146
  262. package/docs/directives.md +0 -158
  263. package/docs/event-plugins.md +0 -109
  264. package/docs/form-components.md +0 -362
  265. package/docs/hooks-and-utilities.md +0 -175
  266. package/docs/layout-components.md +0 -140
  267. package/docs/navigation-components.md +0 -200
  268. package/docs/overlay-components.md +0 -193
  269. package/docs/pipes.md +0 -25
  270. package/docs/providers.md +0 -448
  271. package/docs/setup-functions.md +0 -66
  272. package/docs/visual-components.md +0 -89
@@ -0,0 +1,850 @@
1
+ import { NgTemplateOutlet } from "@angular/common";
2
+ import {
3
+ ChangeDetectionStrategy,
4
+ Component,
5
+ computed,
6
+ contentChild,
7
+ contentChildren,
8
+ Directive,
9
+ effect,
10
+ inject,
11
+ input,
12
+ type InputSignal,
13
+ model,
14
+ output,
15
+ reflectComponentType,
16
+ signal,
17
+ type Signal,
18
+ TemplateRef,
19
+ viewChild,
20
+ ViewEncapsulation,
21
+ } from "@angular/core";
22
+ import { type ArrayOneWayDiffResult, obj } from "@simplysm/core-common";
23
+ import { SdButtonControl } from "../../ui/form/button/sd-button.control";
24
+ import { SdFormControl } from "../../ui/form/sd-form.control";
25
+ import { SdSheetColumnDirective } from "../../ui/data/sheet/sd-sheet-column.directive";
26
+ import { SdSheetControl, type ISortingDef } from "../../ui/data/sheet/sd-sheet.control";
27
+ import { SdFileDialogProvider } from "../../core/providers/sd-file-dialog.provider";
28
+ import { SdToastProvider } from "../../ui/overlay/toast/sd-toast.provider";
29
+ import { SdSharedDataProvider } from "../../core/providers/sd-shared-data.provider";
30
+ import { useViewTypeSignal } from "../../core/utils/useViewTypeSignal";
31
+ import { setupCumulateSelectedKeys } from "../../core/utils/setups/setupCumulateSelectedKeys";
32
+ import { setupCloserWhenSingleSelectionChange } from "../../core/utils/setups/setupCloserWhenSingleSelectionChange";
33
+ import { setupCanDeactivate } from "../../core/utils/setups/setupCanDeactivate";
34
+ import { injectParent } from "../../core/utils/injectParent";
35
+ import { FormatPipe } from "../../core/pipes/format.pipe";
36
+ import { TXT_CHANGE_IGNORE_CONFIRM } from "../../core/commons";
37
+ import { SdBaseContainerControl } from "../base/sd-base-container.control";
38
+ import { SdDataSheetColumnDirective } from "./sd-data-sheet-column.directive";
39
+ import type {
40
+ ISdSelectModal,
41
+ ISelectModalOutputResult,
42
+ } from "../../ui/form/button/sd-modal-select-button.control";
43
+ import { SdAnchorControl } from "../../ui/form/button/sd-anchor.control";
44
+ import { NgIcon } from "@ng-icons/core";
45
+ import {
46
+ tablerDeviceFloppy,
47
+ tablerEdit,
48
+ tablerEraser,
49
+ tablerCirclePlus,
50
+ tablerFileExcel,
51
+ tablerRefresh,
52
+ tablerRestore,
53
+ tablerSearch,
54
+ tablerUpload,
55
+ } from "@ng-icons/tabler-icons";
56
+
57
+ //#region Interfaces
58
+
59
+ export interface ISdDataSheetItemPropInfo<I> {
60
+ isDeleted: (keyof I & string) | undefined;
61
+ lastModifiedAt: (keyof I & string) | undefined;
62
+ lastModifiedBy: (keyof I & string) | undefined;
63
+ }
64
+
65
+ export interface ISdDataSheetItemInfo<K> {
66
+ key: K;
67
+ canSelect: boolean;
68
+ canEdit: boolean;
69
+ canDelete: boolean;
70
+ }
71
+
72
+ export interface ISdDataSheetSearchResult<I> {
73
+ items: I[];
74
+ pageLength?: number;
75
+ summary?: Partial<I>;
76
+ }
77
+
78
+ //#endregion
79
+
80
+ //#region AbsSdDataSheet
81
+
82
+ @Directive()
83
+ export abstract class AbsSdDataSheet<
84
+ TFilter extends Record<string, any>,
85
+ TItem,
86
+ TKey extends string | number | undefined,
87
+ > implements ISdSelectModal<TItem> {
88
+ //-- abstract
89
+ abstract canUse: Signal<boolean>;
90
+ abstract canEdit: Signal<boolean>;
91
+ hideTool?: Signal<boolean>;
92
+
93
+ abstract editMode: "inline" | "modal" | undefined;
94
+ abstract selectMode: InputSignal<"single" | "multi" | undefined>;
95
+
96
+ diffsExcludes?: string[];
97
+
98
+ abstract bindFilter(): TFilter;
99
+
100
+ abstract itemPropInfo: ISdDataSheetItemPropInfo<TItem>;
101
+ abstract getItemInfoFn: (item: TItem) => ISdDataSheetItemInfo<TKey>;
102
+
103
+ prepareRefreshEffect?(): void;
104
+
105
+ abstract search(
106
+ usePagination: boolean,
107
+ ): Promise<ISdDataSheetSearchResult<TItem>> | ISdDataSheetSearchResult<TItem>;
108
+
109
+ //-- optional methods (consuming code implements)
110
+ editItem?(item?: TItem): Promise<boolean | undefined> | boolean | undefined;
111
+ toggleDeleteItems?(del: boolean): Promise<boolean>;
112
+ newItem?(): Promise<TItem> | TItem;
113
+ submit?(diffs: ArrayOneWayDiffResult<TItem>[]): Promise<boolean> | boolean;
114
+ downloadExcel?(items: TItem[]): Promise<void> | void;
115
+ uploadExcel?(file: File): Promise<void> | void;
116
+
117
+ //-- injected
118
+ private readonly _sdToast = inject(SdToastProvider);
119
+ private readonly _sdSharedData = inject(SdSharedDataProvider);
120
+ private readonly _sdFileDialog = inject(SdFileDialogProvider);
121
+
122
+ //-- state
123
+ key = reflectComponentType(this.constructor as any)?.selector;
124
+
125
+ viewType = useViewTypeSignal(() => this);
126
+
127
+ busyCount = signal(0);
128
+ busyMessage = signal<string | undefined>(undefined);
129
+ initialized = signal(false);
130
+ close = output<ISelectModalOutputResult<TItem>>();
131
+ submitted = output<boolean>();
132
+ selectedItemKeys = model<TKey[]>([]);
133
+ actionTplRef?: TemplateRef<any>;
134
+
135
+ autoSelect = computed<"click" | undefined>(() =>
136
+ (!this.canEdit() || this.editMode === "modal") && this.selectMode() === "single"
137
+ ? "click"
138
+ : undefined,
139
+ );
140
+
141
+ items = signal<TItem[]>([]);
142
+ summaryData = signal<Partial<TItem>>({});
143
+ selectedItems = signal<TItem[]>([]);
144
+
145
+ trackByFn = (item: TItem): TKey | TItem => this.getItemInfoFn(item).key ?? item;
146
+
147
+ page = signal(0);
148
+ pageLength = signal(0);
149
+ sortingDefs = signal<ISortingDef[]>([]);
150
+
151
+ filter = signal<TFilter>({} as TFilter);
152
+ lastFilter = signal<TFilter>({} as TFilter);
153
+
154
+ //-- change tracking
155
+ private _itemsSnapshot: TItem[] = [];
156
+
157
+ //-- computed
158
+ isSelectedItemsHasDeleted = computed(() =>
159
+ this.selectedItems().some(
160
+ (item) =>
161
+ this.itemPropInfo.isDeleted != null &&
162
+ (item[this.itemPropInfo.isDeleted] as boolean),
163
+ ),
164
+ );
165
+
166
+ isSelectedItemsHasNotDeleted = computed(() =>
167
+ this.selectedItems().some(
168
+ (item) =>
169
+ this.itemPropInfo.isDeleted == null ||
170
+ !(item[this.itemPropInfo.isDeleted] as boolean),
171
+ ),
172
+ );
173
+
174
+ getItemCellStyleFn = (item: TItem): string | undefined =>
175
+ this.itemPropInfo.isDeleted != null && (item[this.itemPropInfo.isDeleted] as boolean)
176
+ ? "text-decoration: line-through;"
177
+ : undefined;
178
+
179
+ getItemSelectableFn = (item: TItem): boolean => this.getItemInfoFn(item).canSelect;
180
+
181
+ constructor() {
182
+ setupCumulateSelectedKeys({
183
+ items: this.items,
184
+ selectedItems: this.selectedItems,
185
+ selectedItemKeys: this.selectedItemKeys,
186
+ selectMode: () => this.selectMode(),
187
+ keySelectorFn: (item) => this.getItemInfoFn(item).key,
188
+ });
189
+
190
+ setupCloserWhenSingleSelectionChange({
191
+ selectedItemKeys: this.selectedItemKeys,
192
+ selectedItems: this.selectedItems,
193
+ selectMode: () => this.selectMode(),
194
+ close: this.close,
195
+ });
196
+
197
+ effect(() => {
198
+ const filter = this.bindFilter();
199
+ this.filter.set(filter);
200
+ this.lastFilter.set(obj.clone(filter));
201
+ });
202
+
203
+ effect(() => {
204
+ this.page();
205
+ this.lastFilter();
206
+ this.sortingDefs();
207
+ this.prepareRefreshEffect?.();
208
+
209
+ queueMicrotask(async () => {
210
+ if (!this.canUse()) {
211
+ this.initialized.set(true);
212
+ return;
213
+ }
214
+
215
+ this.busyCount.update((v) => v + 1);
216
+ await this._sdToast.try(async () => {
217
+ await this._sdSharedData.wait();
218
+ await this.refresh();
219
+ });
220
+ this.busyCount.update((v) => v - 1);
221
+ this.initialized.set(true);
222
+ });
223
+ });
224
+
225
+ setupCanDeactivate(() => this.viewType() === "modal" || this.checkIgnoreChanges());
226
+ }
227
+
228
+ //-- query
229
+
230
+ checkIgnoreChanges() {
231
+ return this._getDiffs().length === 0 || confirm(TXT_CHANGE_IGNORE_CONFIRM);
232
+ }
233
+
234
+ doFilterSubmit() {
235
+ if (this.busyCount() > 0) return;
236
+ if (!this.canUse()) return;
237
+ if (!this.checkIgnoreChanges()) return;
238
+
239
+ this.page.set(0);
240
+ this.lastFilter.set(obj.clone(this.filter()));
241
+ }
242
+
243
+ doRefresh() {
244
+ if (this.busyCount() > 0) return;
245
+ if (!this.canUse()) return;
246
+ if (!this.checkIgnoreChanges()) return;
247
+
248
+ this.lastFilter.update((v) => ({ ...v }));
249
+ }
250
+
251
+ async refresh() {
252
+ const result = await this.search(true);
253
+ this.items.set(result.items);
254
+ this._itemsSnapshot = obj.clone(result.items);
255
+
256
+ this.pageLength.set(result.pageLength ?? 0);
257
+ this.summaryData.set(result.summary ?? {});
258
+
259
+ this.selectedItems.set(
260
+ this.items().filter((item) =>
261
+ this.selectedItems().some(
262
+ (sel) => this.getItemInfoFn(sel).key === this.getItemInfoFn(item).key,
263
+ ),
264
+ ),
265
+ );
266
+ }
267
+
268
+ //-- inline edit
269
+
270
+ async doAddItem() {
271
+ if (!this.newItem) return;
272
+
273
+ this.busyCount.update((v) => v + 1);
274
+ await this._sdToast.try(async () => {
275
+ const newItem = await this.newItem!();
276
+ this.items.update((items) => [newItem, ...items]);
277
+ });
278
+ this.busyCount.update((v) => v - 1);
279
+ }
280
+
281
+ async doSubmit(opt?: { permCheck?: boolean; hideNoChangeMessage?: boolean }) {
282
+ if (this.busyCount() > 0) return;
283
+ if (opt?.permCheck && !this.canEdit()) return;
284
+ if (!this.submit) return;
285
+
286
+ const diffs = this._getDiffs();
287
+
288
+ if (diffs.length === 0) {
289
+ if (!opt?.hideNoChangeMessage) {
290
+ this._sdToast.info("변경사항이 없습니다.");
291
+ }
292
+ return;
293
+ }
294
+
295
+ this.busyCount.update((v) => v + 1);
296
+ await this._sdToast.try(
297
+ async () => {
298
+ const result = await this.submit!(diffs);
299
+ if (!result) return;
300
+
301
+ this._sdToast.success("저장되었습니다.");
302
+ await this.refresh();
303
+ },
304
+ (err) => this._getOrmDataEditToastErrorMessage(err),
305
+ );
306
+ this.busyCount.update((v) => v - 1);
307
+
308
+ this.submitted.emit(true);
309
+ }
310
+
311
+ doToggleDeleteItem(item: TItem) {
312
+ if (!this.canEdit()) return;
313
+ if (this.itemPropInfo.isDeleted == null) return;
314
+
315
+ if (this.getItemInfoFn(item).key == null) {
316
+ this.items.update((items) => items.filter((item1) => item1 !== item));
317
+ return;
318
+ }
319
+
320
+ (item[this.itemPropInfo.isDeleted] as boolean) = !(item[
321
+ this.itemPropInfo.isDeleted
322
+ ] as boolean);
323
+ this.items.update((v) => [...v]);
324
+ }
325
+
326
+ //-- modal edit
327
+
328
+ async doEditItem(item?: TItem) {
329
+ if (!this.editItem) return;
330
+
331
+ const result = await this.editItem(item);
332
+ if (!result) return;
333
+
334
+ this.busyCount.update((v) => v + 1);
335
+ await this._sdToast.try(async () => {
336
+ await this.refresh();
337
+ });
338
+ this.busyCount.update((v) => v - 1);
339
+ }
340
+
341
+ async doToggleDeleteItems(del: boolean) {
342
+ if (!this.canEdit()) return;
343
+ if (!this.toggleDeleteItems) return;
344
+
345
+ this.busyCount.update((v) => v + 1);
346
+ await this._sdToast.try(
347
+ async () => {
348
+ const result = await this.toggleDeleteItems!(del);
349
+ if (!result) return;
350
+
351
+ await this.refresh();
352
+ this._sdToast.success(`${del ? "삭제" : "복구"} 되었습니다.`);
353
+ },
354
+ (err) => this._getOrmDataEditToastErrorMessage(err),
355
+ );
356
+ this.busyCount.update((v) => v - 1);
357
+ }
358
+
359
+ //-- excel
360
+
361
+ async doDownloadExcel() {
362
+ if (!this.downloadExcel) return;
363
+
364
+ this.busyCount.update((v) => v + 1);
365
+ await this._sdToast.try(async () => {
366
+ const items = (await this.search(false)).items;
367
+ await this.downloadExcel!(items);
368
+ });
369
+ this.busyCount.update((v) => v - 1);
370
+ }
371
+
372
+ async doUploadExcel() {
373
+ if (!this.uploadExcel) return;
374
+
375
+ const file = await this._sdFileDialog.showAsync(
376
+ false,
377
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
378
+ );
379
+ if (!file) return;
380
+
381
+ this.busyCount.update((v) => v + 1);
382
+ await this._sdToast.try(
383
+ async () => {
384
+ await this.uploadExcel!(file);
385
+ await this.refresh();
386
+ this._sdToast.success("엑셀 업로드가 완료 되었습니다.");
387
+ },
388
+ (err) => this._getOrmDataEditToastErrorMessage(err),
389
+ );
390
+ this.busyCount.update((v) => v - 1);
391
+ }
392
+
393
+ //-- modal selection
394
+
395
+ doModalConfirm() {
396
+ this.close.emit({
397
+ selectedItemKeys: this.selectedItemKeys(),
398
+ selectedItems: this.selectedItems(),
399
+ });
400
+ }
401
+
402
+ doModalCancel() {
403
+ this.close.emit({
404
+ selectedItemKeys: [],
405
+ selectedItems: [],
406
+ });
407
+ }
408
+
409
+ //-- private
410
+
411
+ private _getDiffs(): ArrayOneWayDiffResult<TItem>[] {
412
+ return this.items().oneWayDiffs(
413
+ this._itemsSnapshot,
414
+ (item) => this.getItemInfoFn(item).key,
415
+ this.diffsExcludes ? { excludes: this.diffsExcludes } : undefined,
416
+ ).filter((d) => d.type !== "same");
417
+ }
418
+
419
+ private _getOrmDataEditToastErrorMessage(err: unknown) {
420
+ const message = err instanceof Error ? err.message : String(err);
421
+ if (
422
+ message.includes("a parent row: a foreign key constraint") ||
423
+ message.includes("conflicted with the REFERENCE")
424
+ ) {
425
+ return "경고! 연결된 작업에 의한 처리 거부. 후속작업 확인 요망";
426
+ }
427
+ return message;
428
+ }
429
+ }
430
+
431
+ //#endregion
432
+
433
+ //#region SdDataSheetControl
434
+
435
+ @Component({
436
+ selector: "sd-data-sheet",
437
+ changeDetection: ChangeDetectionStrategy.OnPush,
438
+ encapsulation: ViewEncapsulation.None,
439
+ standalone: true,
440
+ imports: [
441
+ SdFormControl,
442
+ SdButtonControl,
443
+ SdSheetControl,
444
+ SdSheetColumnDirective,
445
+ NgTemplateOutlet,
446
+ SdBaseContainerControl,
447
+ FormatPipe,
448
+ SdAnchorControl,
449
+ NgIcon,
450
+ ],
451
+ host: {
452
+ "(sdRefreshCommand)": "onRefreshButtonClick()",
453
+ "(sdSaveCommand)": "onSubmitButtonClick()",
454
+ },
455
+ template: `
456
+ <sd-base-container
457
+ [busy]="parent.busyCount() > 0"
458
+ [busyMessage]="parent.busyMessage()"
459
+ [viewType]="parent.viewType()"
460
+ [initialized]="parent.initialized()"
461
+ [restricted]="!parent.canUse()"
462
+ >
463
+ <ng-template #pageTopbarTpl>
464
+ @if (parent.canEdit() && parent.submit) {
465
+ <sd-button [theme]="'link-primary'" (click)="onSubmitButtonClick()">
466
+ <ng-icon [svg]="icons.tablerDeviceFloppy" />
467
+ 저장
468
+ <small>(CTRL+S)</small>
469
+ </sd-button>
470
+ }
471
+ <sd-button [theme]="'link-info'" (click)="onRefreshButtonClick()">
472
+ <ng-icon [svg]="icons.tablerRefresh" />
473
+ 새로고침
474
+ <small>(CTRL+ALT+L)</small>
475
+ </sd-button>
476
+
477
+ <ng-template [ngTemplateOutlet]="pageTopbarTplRef() ?? null" />
478
+ </ng-template>
479
+
480
+ <ng-template #contentTpl>
481
+ <div class="flex-column fill">
482
+ @if (parent.canEdit() && parent.submit && parent.viewType() === "control") {
483
+ <div class="flex-row gap-sm p-default bdb bdb-theme-gray-lightest">
484
+ <sd-button [size]="'sm'" [theme]="'primary'" (click)="onSubmitButtonClick()">
485
+ <ng-icon [svg]="icons.tablerDeviceFloppy" />
486
+ 저장
487
+ <small>(CTRL+S)</small>
488
+ </sd-button>
489
+ <sd-button [size]="'sm'" [theme]="'info'" (click)="onRefreshButtonClick()">
490
+ <ng-icon [svg]="icons.tablerRefresh" />
491
+ 새로고침
492
+ <small>(CTRL+ALT+L)</small>
493
+ </sd-button>
494
+ <ng-template [ngTemplateOutlet]="prevTplRef() ?? null" />
495
+ </div>
496
+ } @else {
497
+ <ng-template [ngTemplateOutlet]="prevTplRef() ?? null" />
498
+ }
499
+
500
+ @if (filterTplRef()) {
501
+ <div class="p-default">
502
+ <sd-form (formSubmit)="onFilterSubmit()">
503
+ <div class="form-box-inline">
504
+ <div>
505
+ <sd-button [type]="'submit'" [theme]="'info'">
506
+ <ng-icon [svg]="icons.tablerSearch" />
507
+ 조회
508
+ </sd-button>
509
+ </div>
510
+ <ng-template [ngTemplateOutlet]="filterTplRef()!" />
511
+ </div>
512
+ </sd-form>
513
+ </div>
514
+ }
515
+
516
+ @if (!parent.hideTool || !parent.hideTool()) {
517
+ @if (parent.canEdit() || beforeToolTplRef() || parent.downloadExcel) {
518
+ <div class="flex-row gap-sm p-xs-default">
519
+ @if (parent.canEdit()) {
520
+ @if (parent.editMode === "modal" && parent.editItem) {
521
+ <sd-button
522
+ [size]="'sm'"
523
+ [theme]="'link-primary'"
524
+ (click)="onCreateItemButtonClick()"
525
+ >
526
+ <ng-icon [svg]="icons.tablerCirclePlus" />
527
+ {{ insertText() ?? "등록" }}
528
+ </sd-button>
529
+ } @else if (parent.editMode === "inline" && parent.newItem) {
530
+ <sd-button
531
+ [size]="'sm'"
532
+ [theme]="'link-primary'"
533
+ (click)="onAddItemButtonClick()"
534
+ >
535
+ <ng-icon [svg]="icons.tablerCirclePlus" />
536
+ 행 추가
537
+ </sd-button>
538
+ }
539
+ }
540
+
541
+ <ng-template [ngTemplateOutlet]="beforeToolTplRef() ?? null" />
542
+
543
+ @if (parent.canEdit()) {
544
+ @if (parent.editMode === "modal" && parent.toggleDeleteItems) {
545
+ <sd-button
546
+ [size]="'sm'"
547
+ [theme]="'link-danger'"
548
+ (click)="onToggleDeleteItemsButtonClick(true)"
549
+ [disabled]="!parent.isSelectedItemsHasNotDeleted()"
550
+ >
551
+ <ng-icon [svg]="deleteIcon()" />
552
+ 선택 {{ deleteText() ?? "삭제" }}
553
+ </sd-button>
554
+ @if (parent.isSelectedItemsHasDeleted()) {
555
+ <sd-button
556
+ [size]="'sm'"
557
+ [theme]="'link-warning'"
558
+ (click)="onToggleDeleteItemsButtonClick(false)"
559
+ >
560
+ <ng-icon [svg]="restoreIcon()" />
561
+ 선택 {{ restoreText() ?? "복구" }}
562
+ </sd-button>
563
+ }
564
+ }
565
+
566
+ @if (parent.uploadExcel) {
567
+ <sd-button
568
+ [size]="'sm'"
569
+ [theme]="'link-success'"
570
+ (click)="onUploadExcelButtonClick()"
571
+ >
572
+ <ng-icon [svg]="icons.tablerUpload" />
573
+ 엑셀 업로드
574
+ </sd-button>
575
+ }
576
+ }
577
+
578
+ @if (parent.downloadExcel) {
579
+ <sd-button
580
+ [size]="'sm'"
581
+ [theme]="'link-success'"
582
+ (click)="onDownloadExcelButtonClick()"
583
+ >
584
+ <ng-icon [svg]="icons.tablerFileExcel" />
585
+ 엑셀 다운로드
586
+ </sd-button>
587
+ }
588
+
589
+ <ng-template [ngTemplateOutlet]="toolTplRef() ?? null" />
590
+ </div>
591
+ }
592
+ }
593
+
594
+ <sd-form #formCtrl (formSubmit)="onSubmit()" class="flex-fill p-default pt-0">
595
+ <sd-sheet
596
+ [key]="parent.key + '-sheet'"
597
+ [items]="parent.items()"
598
+ [(currentPage)]="parent.page"
599
+ [totalPageCount]="parent.pageLength()"
600
+ [(sorts)]="parent.sortingDefs"
601
+ [selectMode]="parent.selectMode()"
602
+ [autoSelect]="parent.autoSelect()"
603
+ [(selectedItems)]="parent.selectedItems"
604
+ [trackByFn]="parent.trackByFn"
605
+ [getItemCellStyleFn]="parent.getItemCellStyleFn"
606
+ [getItemSelectableFn]="parent.getItemSelectableFn"
607
+ >
608
+ @if (
609
+ parent.editMode === "inline" &&
610
+ parent.canEdit() &&
611
+ parent.itemPropInfo.isDeleted
612
+ ) {
613
+ <sd-sheet-column [fixed]="true" [key]="parent.itemPropInfo.isDeleted!">
614
+ <ng-template #cellTpl let-item>
615
+ <div class="p-xs-sm tx-center">
616
+ <sd-anchor
617
+ [theme]="'danger'"
618
+ (click)="onToggleDeleteItemButtonClick(item)"
619
+ [disabled]="!parent.getItemInfoFn(item).canDelete"
620
+ >
621
+ <ng-icon
622
+ [svg]="
623
+ item[parent.itemPropInfo.isDeleted!]
624
+ ? restoreIcon()
625
+ : deleteIcon()
626
+ "
627
+ />
628
+ {{
629
+ item[parent.itemPropInfo.isDeleted!]
630
+ ? (restoreText() ?? "복구")
631
+ : (deleteText() ?? "삭제")
632
+ }}
633
+ </sd-anchor>
634
+ </div>
635
+ </ng-template>
636
+ </sd-sheet-column>
637
+ }
638
+
639
+ @for (columnControl of columnControls(); track columnControl.key()) {
640
+ <sd-sheet-column
641
+ [key]="columnControl.key()"
642
+ [fixed]="columnControl.fixed()"
643
+ [header]="columnControl.header()"
644
+ [width]="columnControl.width()"
645
+ [disableSorting]="columnControl.disableSorting()"
646
+ [disableResizing]="columnControl.disableResizing()"
647
+ [hidden]="columnControl.hidden()"
648
+ [collapse]="columnControl.collapse()"
649
+ >
650
+ @if (columnControl.summaryTplRef()) {
651
+ <ng-template #summaryTpl>
652
+ <ng-template [ngTemplateOutlet]="columnControl.summaryTplRef()!" />
653
+ </ng-template>
654
+ }
655
+
656
+ <ng-template
657
+ #cellTpl
658
+ let-item
659
+ let-index="index"
660
+ let-depth="depth"
661
+ let-edit="edit"
662
+ >
663
+ @if (
664
+ parent.editMode === "modal" &&
665
+ parent.canEdit() &&
666
+ columnControl.edit() &&
667
+ parent.getItemInfoFn(item).canEdit
668
+ ) {
669
+ <sd-anchor
670
+ (click)="onEditItemButtonClick(item, index, $event)"
671
+ class="flex-row"
672
+ >
673
+ <div class="p-xs-sm">
674
+ <ng-icon [svg]="icons.tablerEdit" />
675
+ </div>
676
+ <div class="flex-fill">
677
+ <ng-template
678
+ [ngTemplateOutlet]="columnControl.cellTplRef()"
679
+ [ngTemplateOutletContext]="{
680
+ $implicit: item,
681
+ item: item,
682
+ index: index,
683
+ depth: depth,
684
+ edit: edit,
685
+ }"
686
+ />
687
+ </div>
688
+ </sd-anchor>
689
+ } @else {
690
+ <ng-template
691
+ [ngTemplateOutlet]="columnControl.cellTplRef()"
692
+ [ngTemplateOutletContext]="{
693
+ $implicit: item,
694
+ item: item,
695
+ index: index,
696
+ depth: depth,
697
+ edit: edit,
698
+ }"
699
+ />
700
+ }
701
+ </ng-template>
702
+ </sd-sheet-column>
703
+ }
704
+
705
+ @if (parent.itemPropInfo.lastModifiedAt) {
706
+ <sd-sheet-column
707
+ [header]="'수정일시'"
708
+ [key]="parent.itemPropInfo.lastModifiedAt!"
709
+ [hidden]="true"
710
+ >
711
+ <ng-template #cellTpl let-item>
712
+ <div class="p-xs-sm tx-center">
713
+ {{
714
+ item[parent.itemPropInfo.lastModifiedAt!]
715
+ | format: "yyyy-MM-dd HH:mm"
716
+ }}
717
+ </div>
718
+ </ng-template>
719
+ </sd-sheet-column>
720
+ }
721
+ @if (parent.itemPropInfo.lastModifiedBy) {
722
+ <sd-sheet-column
723
+ [header]="'수정자'"
724
+ [key]="parent.itemPropInfo.lastModifiedBy!"
725
+ [hidden]="true"
726
+ >
727
+ <ng-template #cellTpl let-item>
728
+ <div class="p-xs-sm tx-center">
729
+ {{ item[parent.itemPropInfo.lastModifiedBy!] }}
730
+ </div>
731
+ </ng-template>
732
+ </sd-sheet-column>
733
+ }
734
+ </sd-sheet>
735
+ </sd-form>
736
+ </div>
737
+ </ng-template>
738
+
739
+ @if (parent.selectMode()) {
740
+ <ng-template #modalBottomTpl>
741
+ <div class="p-sm-default flex-row gap-sm">
742
+ <div class="flex-fill flex-row gap-sm">
743
+ @if (modalBottomTplRef()) {
744
+ <ng-template [ngTemplateOutlet]="modalBottomTplRef()!" />
745
+ }
746
+ </div>
747
+
748
+ @if (parent.selectedItemKeys().length > 0) {
749
+ <sd-button [size]="'sm'" [theme]="'danger'" (click)="onCancelButtonClick()">
750
+ {{ parent.selectMode() === "multi" ? "모두" : "선택" }}
751
+ 해제
752
+ </sd-button>
753
+ }
754
+ @if (parent.selectMode() === "multi") {
755
+ <sd-button [size]="'sm'" [theme]="'primary'" (click)="onConfirmButtonClick()">
756
+ 확인({{ parent.selectedItemKeys().length }})
757
+ </sd-button>
758
+ }
759
+ </div>
760
+ </ng-template>
761
+ }
762
+ </sd-base-container>
763
+ `,
764
+ })
765
+ export class SdDataSheetControl {
766
+ parent = injectParent<AbsSdDataSheet<any, any, any>>();
767
+
768
+ formCtrl = viewChild<SdFormControl>("formCtrl");
769
+
770
+ insertText = input<string>();
771
+ deleteText = input<string>();
772
+ restoreText = input<string>();
773
+ deleteIcon = input(tablerEraser);
774
+ restoreIcon = input(tablerRestore);
775
+
776
+ pageTopbarTplRef = contentChild("pageTopbarTpl", { read: TemplateRef });
777
+ prevTplRef = contentChild("prevTpl", { read: TemplateRef });
778
+ filterTplRef = contentChild("filterTpl", { read: TemplateRef });
779
+ beforeToolTplRef = contentChild("beforeToolTpl", { read: TemplateRef });
780
+ toolTplRef = contentChild("toolTpl", { read: TemplateRef });
781
+ modalBottomTplRef = contentChild("modalBottomTpl", { read: TemplateRef });
782
+
783
+ columnControls = contentChildren(SdDataSheetColumnDirective);
784
+
785
+ protected readonly icons = {
786
+ tablerRefresh,
787
+ tablerDeviceFloppy,
788
+ tablerSearch,
789
+ tablerCirclePlus,
790
+ tablerUpload,
791
+ tablerFileExcel,
792
+ tablerEdit,
793
+ };
794
+
795
+ onFilterSubmit() {
796
+ this.parent.doFilterSubmit();
797
+ }
798
+
799
+ onRefreshButtonClick() {
800
+ this.parent.doRefresh();
801
+ }
802
+
803
+ async onCreateItemButtonClick() {
804
+ await this.parent.doEditItem();
805
+ }
806
+
807
+ async onEditItemButtonClick(item: any, index: number, event: MouseEvent) {
808
+ event.preventDefault();
809
+ event.stopPropagation();
810
+ await this.parent.doEditItem(item);
811
+ }
812
+
813
+ async onToggleDeleteItemsButtonClick(del: boolean) {
814
+ await this.parent.doToggleDeleteItems(del);
815
+ }
816
+
817
+ onToggleDeleteItemButtonClick(item: any) {
818
+ this.parent.doToggleDeleteItem(item);
819
+ }
820
+
821
+ onSubmitButtonClick() {
822
+ this.formCtrl()?.requestSubmit();
823
+ }
824
+
825
+ async onSubmit() {
826
+ await this.parent.doSubmit({ permCheck: true });
827
+ }
828
+
829
+ async onAddItemButtonClick() {
830
+ await this.parent.doAddItem();
831
+ }
832
+
833
+ async onDownloadExcelButtonClick() {
834
+ await this.parent.doDownloadExcel();
835
+ }
836
+
837
+ async onUploadExcelButtonClick() {
838
+ await this.parent.doUploadExcel();
839
+ }
840
+
841
+ onConfirmButtonClick() {
842
+ this.parent.doModalConfirm();
843
+ }
844
+
845
+ onCancelButtonClick() {
846
+ this.parent.doModalCancel();
847
+ }
848
+ }
849
+
850
+ //#endregion