@simplysm/angular 14.0.19 → 14.0.21

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 (125) hide show
  1. package/dist/core/provideSdAngular.js +1 -1
  2. package/dist/core/providers/sd-activated-modal.provider.d.ts +13 -0
  3. package/dist/core/providers/sd-activated-modal.provider.d.ts.map +1 -0
  4. package/dist/core/providers/sd-activated-modal.provider.js +15 -0
  5. package/dist/core/providers/sd-app-structure.provider.d.ts +3 -64
  6. package/dist/core/providers/sd-app-structure.provider.d.ts.map +1 -1
  7. package/dist/core/providers/sd-app-structure.provider.js +1 -252
  8. package/dist/core/providers/sd-app-structure.types.d.ts +52 -0
  9. package/dist/core/providers/sd-app-structure.types.d.ts.map +1 -0
  10. package/dist/core/providers/sd-app-structure.types.js +1 -0
  11. package/dist/core/providers/sd-app-structure.utils.d.ts +13 -0
  12. package/dist/core/providers/sd-app-structure.utils.d.ts.map +1 -0
  13. package/dist/core/providers/sd-app-structure.utils.js +250 -0
  14. package/dist/{ui/overlay/busy → core/providers}/sd-busy.provider.d.ts +1 -1
  15. package/dist/core/providers/sd-busy.provider.d.ts.map +1 -0
  16. package/dist/{ui/overlay/busy → core/providers}/sd-busy.provider.js +1 -1
  17. package/dist/core/providers/sd-print.provider.js +1 -1
  18. package/dist/core/providers/sd-service-client-factory.provider.js +1 -1
  19. package/dist/{ui/overlay/toast → core/providers}/sd-toast.provider.d.ts +1 -1
  20. package/dist/core/providers/sd-toast.provider.d.ts.map +1 -0
  21. package/dist/{ui/overlay/toast → core/providers}/sd-toast.provider.js +3 -3
  22. package/dist/core/types/select-modal-output-result.d.ts +8 -0
  23. package/dist/core/types/select-modal-output-result.d.ts.map +1 -0
  24. package/dist/core/types/select-modal-output-result.js +1 -0
  25. package/dist/core/utils/setups/setupCanDeactivate.js +1 -1
  26. package/dist/core/utils/useViewTitleSignal.js +1 -1
  27. package/dist/core/utils/useViewTypeSignal.js +1 -1
  28. package/dist/features/base/sd-base-container.control.js +1 -1
  29. package/dist/features/data-view/sd-data-detail.control.js +1 -1
  30. package/dist/features/data-view/sd-data-sheet.control.d.ts +24 -37
  31. package/dist/features/data-view/sd-data-sheet.control.d.ts.map +1 -1
  32. package/dist/features/data-view/sd-data-sheet.control.js +98 -152
  33. package/dist/features/data-view/sd-data-sheet.types.d.ts +17 -0
  34. package/dist/features/data-view/sd-data-sheet.types.d.ts.map +1 -0
  35. package/dist/features/data-view/sd-data-sheet.types.js +1 -0
  36. package/dist/{core/utils/setups → features/data-view}/setupCloserWhenSingleSelectionChange.d.ts +1 -1
  37. package/dist/features/data-view/setupCloserWhenSingleSelectionChange.d.ts.map +1 -0
  38. package/dist/features/data-view/useDataSheetExcelManager.d.ts +14 -0
  39. package/dist/features/data-view/useDataSheetExcelManager.d.ts.map +1 -0
  40. package/dist/features/data-view/useDataSheetExcelManager.js +31 -0
  41. package/dist/features/data-view/useDataSheetFilterManager.d.ts +13 -0
  42. package/dist/features/data-view/useDataSheetFilterManager.d.ts.map +1 -0
  43. package/dist/features/data-view/useDataSheetFilterManager.js +22 -0
  44. package/dist/features/data-view/useDataSheetInlineEditManager.d.ts +26 -0
  45. package/dist/features/data-view/useDataSheetInlineEditManager.d.ts.map +1 -0
  46. package/dist/features/data-view/useDataSheetInlineEditManager.js +54 -0
  47. package/dist/features/data-view/useDataSheetModalEditManager.d.ts +19 -0
  48. package/dist/features/data-view/useDataSheetModalEditManager.d.ts.map +1 -0
  49. package/dist/features/data-view/useDataSheetModalEditManager.js +44 -0
  50. package/dist/features/data-view/useDataSheetRefreshManager.d.ts +25 -0
  51. package/dist/features/data-view/useDataSheetRefreshManager.d.ts.map +1 -0
  52. package/dist/features/data-view/useDataSheetRefreshManager.js +50 -0
  53. package/dist/features/permission-table/sd-permission-table.control.d.ts +1 -1
  54. package/dist/features/permission-table/sd-permission-table.control.d.ts.map +1 -1
  55. package/dist/index.d.ts +12 -6
  56. package/dist/index.d.ts.map +1 -1
  57. package/dist/index.js +10 -5
  58. package/dist/ui/data/sheet/sd-sheet.control.d.ts +22 -30
  59. package/dist/ui/data/sheet/sd-sheet.control.d.ts.map +1 -1
  60. package/dist/ui/data/sheet/sd-sheet.control.js +52 -210
  61. package/dist/ui/data/sheet/useSheetCellStyling.d.ts +22 -0
  62. package/dist/ui/data/sheet/useSheetCellStyling.d.ts.map +1 -0
  63. package/dist/ui/data/sheet/useSheetCellStyling.js +95 -0
  64. package/dist/ui/data/sheet/useSheetColumnResizing.d.ts +17 -0
  65. package/dist/ui/data/sheet/useSheetColumnResizing.d.ts.map +1 -0
  66. package/dist/ui/data/sheet/useSheetColumnResizing.js +65 -0
  67. package/dist/ui/data/sheet/useSheetDisplayPipeline.d.ts +24 -0
  68. package/dist/ui/data/sheet/useSheetDisplayPipeline.d.ts.map +1 -0
  69. package/dist/ui/data/sheet/useSheetDisplayPipeline.js +52 -0
  70. package/dist/ui/form/button/sd-modal-select-button.control.d.ts +1 -7
  71. package/dist/ui/form/button/sd-modal-select-button.control.d.ts.map +1 -1
  72. package/dist/ui/form/button/sd-modal-select-button.control.js +1 -1
  73. package/dist/ui/form/choice/sd-state-preset.control.js +1 -1
  74. package/dist/ui/navigation/menu-utils.d.ts +2 -7
  75. package/dist/ui/navigation/menu-utils.d.ts.map +1 -1
  76. package/dist/ui/overlay/busy/sd-busy-container.control.d.ts +1 -1
  77. package/dist/ui/overlay/busy/sd-busy-container.control.d.ts.map +1 -1
  78. package/dist/ui/overlay/busy/sd-busy-container.control.js +1 -1
  79. package/dist/ui/overlay/modal/sd-modal.control.js +1 -1
  80. package/dist/ui/overlay/modal/sd-modal.provider.d.ts +0 -10
  81. package/dist/ui/overlay/modal/sd-modal.provider.d.ts.map +1 -1
  82. package/dist/ui/overlay/modal/sd-modal.provider.js +1 -13
  83. package/dist/ui/overlay/toast/sd-toast.control.d.ts +1 -1
  84. package/dist/ui/overlay/toast/sd-toast.control.d.ts.map +1 -1
  85. package/package.json +5 -5
  86. package/src/core/provideSdAngular.ts +1 -1
  87. package/src/core/providers/sd-activated-modal.provider.ts +12 -0
  88. package/src/core/providers/sd-app-structure.provider.ts +2 -405
  89. package/src/core/providers/sd-app-structure.types.ts +60 -0
  90. package/src/core/providers/sd-app-structure.utils.ts +350 -0
  91. package/src/{ui/overlay/busy → core/providers}/sd-busy.provider.ts +1 -1
  92. package/src/core/providers/sd-print.provider.ts +1 -1
  93. package/src/core/providers/sd-service-client-factory.provider.ts +1 -1
  94. package/src/{ui/overlay/toast → core/providers}/sd-toast.provider.ts +4 -4
  95. package/src/core/types/select-modal-output-result.ts +7 -0
  96. package/src/core/utils/setups/setupCanDeactivate.ts +1 -1
  97. package/src/core/utils/useViewTitleSignal.ts +1 -1
  98. package/src/core/utils/useViewTypeSignal.ts +1 -1
  99. package/src/features/base/sd-base-container.control.ts +1 -1
  100. package/src/features/data-view/sd-data-detail.control.ts +1 -1
  101. package/src/features/data-view/sd-data-sheet.control.ts +117 -216
  102. package/src/features/data-view/sd-data-sheet.types.ts +18 -0
  103. package/src/{core/utils/setups → features/data-view}/setupCloserWhenSingleSelectionChange.ts +1 -1
  104. package/src/features/data-view/useDataSheetExcelManager.ts +57 -0
  105. package/src/features/data-view/useDataSheetFilterManager.ts +30 -0
  106. package/src/features/data-view/useDataSheetInlineEditManager.ts +89 -0
  107. package/src/features/data-view/useDataSheetModalEditManager.ts +76 -0
  108. package/src/features/data-view/useDataSheetRefreshManager.ts +90 -0
  109. package/src/features/permission-table/sd-permission-table.control.ts +1 -1
  110. package/src/index.ts +17 -11
  111. package/src/ui/data/sheet/sd-sheet.control.ts +50 -238
  112. package/src/ui/data/sheet/useSheetCellStyling.ts +113 -0
  113. package/src/ui/data/sheet/useSheetColumnResizing.ts +92 -0
  114. package/src/ui/data/sheet/useSheetDisplayPipeline.ts +64 -0
  115. package/src/ui/form/button/sd-modal-select-button.control.ts +1 -8
  116. package/src/ui/form/choice/sd-state-preset.control.ts +1 -1
  117. package/src/ui/navigation/menu-utils.ts +3 -7
  118. package/src/ui/overlay/busy/sd-busy-container.control.ts +1 -1
  119. package/src/ui/overlay/modal/sd-modal.control.ts +1 -1
  120. package/src/ui/overlay/modal/sd-modal.provider.ts +1 -10
  121. package/src/ui/overlay/toast/sd-toast.control.ts +1 -1
  122. package/dist/core/utils/setups/setupCloserWhenSingleSelectionChange.d.ts.map +0 -1
  123. package/dist/ui/overlay/busy/sd-busy.provider.d.ts.map +0 -1
  124. package/dist/ui/overlay/toast/sd-toast.provider.d.ts.map +0 -1
  125. /package/dist/{core/utils/setups → features/data-view}/setupCloserWhenSingleSelectionChange.js +0 -0
@@ -0,0 +1,76 @@
1
+ import {
2
+ type OutputEmitterRef,
3
+ type Signal,
4
+ type WritableSignal,
5
+ inject,
6
+ } from "@angular/core";
7
+ import { SdToastProvider } from "../../core/providers/sd-toast.provider";
8
+ import { withBusy } from "../../core/utils/withBusy";
9
+ import type { ISelectModalOutputResult } from "../../core/types/select-modal-output-result";
10
+
11
+ export function useDataSheetModalEditManager<TItem, TKey>(options: {
12
+ busyCount: WritableSignal<number>;
13
+ canEdit: () => boolean;
14
+ selectedItemKeys: Signal<TKey[]>;
15
+ selectedItems: Signal<TItem[]>;
16
+ close: OutputEmitterRef<ISelectModalOutputResult<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
+ }
@@ -0,0 +1,90 @@
1
+ import { type Signal, type WritableSignal, effect, inject } from "@angular/core";
2
+ import { type ArrayOneWayDiffResult, obj } from "@simplysm/core-common";
3
+ import { SdToastProvider } from "../../core/providers/sd-toast.provider";
4
+ import { SdSharedDataProvider } from "../../core/providers/sd-shared-data.provider";
5
+ import { withBusy } from "../../core/utils/withBusy";
6
+ import type { ISdDataSheetSearchResult } from "./sd-data-sheet.types";
7
+
8
+ export function useDataSheetRefreshManager<
9
+ TItem,
10
+ TKey extends string | number | undefined,
11
+ >(options: {
12
+ busyCount: WritableSignal<number>;
13
+ initialized: WritableSignal<boolean>;
14
+ canUse: () => boolean;
15
+ items: WritableSignal<TItem[]>;
16
+ selectedItems: WritableSignal<TItem[]>;
17
+ pageLength: WritableSignal<number>;
18
+ summaryData: WritableSignal<Partial<TItem>>;
19
+ page: Signal<number>;
20
+ lastFilter: Signal<unknown>;
21
+ sortingDefs: Signal<unknown[]>;
22
+ getItemInfoFn: (item: TItem) => { key: TKey };
23
+ search: (
24
+ usePagination: boolean,
25
+ ) => Promise<ISdDataSheetSearchResult<TItem>> | ISdDataSheetSearchResult<TItem>;
26
+ prepareRefreshEffect?: () => void;
27
+ getDiffsExcludes: () => string[] | undefined;
28
+ }) {
29
+ const sdToast = inject(SdToastProvider);
30
+ const sdSharedData = inject(SdSharedDataProvider);
31
+ let itemsSnapshot: TItem[] = [];
32
+
33
+ async function refresh(): Promise<void> {
34
+ const result = await options.search(true);
35
+ options.items.set(result.items);
36
+ itemsSnapshot = obj.clone(result.items);
37
+
38
+ options.pageLength.set(result.pageLength ?? 0);
39
+ options.summaryData.set(result.summary ?? {});
40
+
41
+ const selectedKeySet = new Set(
42
+ options.selectedItems().map((sel) => options.getItemInfoFn(sel).key),
43
+ );
44
+ options.selectedItems.set(
45
+ options.items().filter((item) => selectedKeySet.has(options.getItemInfoFn(item).key)),
46
+ );
47
+ }
48
+
49
+ function getDiffs(): ArrayOneWayDiffResult<TItem>[] {
50
+ const diffsExcludes = options.getDiffsExcludes();
51
+ return options
52
+ .items()
53
+ .oneWayDiffs(
54
+ itemsSnapshot,
55
+ (item) => options.getItemInfoFn(item).key,
56
+ diffsExcludes ? { excludes: diffsExcludes } : undefined,
57
+ )
58
+ .filter((d) => d.type !== "same");
59
+ }
60
+
61
+ effect((onCleanup) => {
62
+ options.page();
63
+ options.lastFilter();
64
+ options.sortingDefs();
65
+ options.prepareRefreshEffect?.();
66
+
67
+ let cancelled = false;
68
+ onCleanup(() => {
69
+ cancelled = true;
70
+ });
71
+
72
+ queueMicrotask(async () => {
73
+ if (cancelled) return;
74
+ if (!options.canUse()) {
75
+ options.initialized.set(true);
76
+ return;
77
+ }
78
+
79
+ await withBusy(options.busyCount, () =>
80
+ sdToast.try(async () => {
81
+ await sdSharedData.wait();
82
+ await refresh();
83
+ }),
84
+ );
85
+ options.initialized.set(true);
86
+ });
87
+ });
88
+
89
+ return { refresh, getDiffs };
90
+ }
@@ -10,7 +10,7 @@ import {
10
10
  ViewEncapsulation,
11
11
  } from "@angular/core";
12
12
  import { obj } from "@simplysm/core-common";
13
- import type { ISdPermission } from "../../core/providers/sd-app-structure.provider";
13
+ import type { ISdPermission } from "../../core/providers/sd-app-structure.types";
14
14
  import { SdCheckboxControl } from "../../ui/form/checkbox/sd-checkbox.control";
15
15
  import { SdCollapseIconControl } from "../../ui/navigation/collapse/sd-collapse-icon.control";
16
16
  import { SdTypedTemplateDirective } from "../../core/directives/sd-typed-template.directive";
package/src/index.ts CHANGED
@@ -32,14 +32,16 @@ export { SdAngularConfigProvider } from "./core/providers/sd-angular-config.prov
32
32
  export { SdSystemLogProvider } from "./core/providers/sd-system-log.provider";
33
33
  export {
34
34
  SdAppStructureProvider,
35
- SdAppStructureUtils,
36
35
  usePermsSignal,
37
- type TSdAppStructureItem,
38
- type ISdMenu,
39
- type ISdFlatMenu,
40
- type ISdPermission,
41
- type ISdFlatPermission,
42
36
  } from "./core/providers/sd-app-structure.provider";
37
+ export { SdAppStructureUtils } from "./core/providers/sd-app-structure.utils";
38
+ export type {
39
+ TSdAppStructureItem,
40
+ ISdMenu,
41
+ ISdFlatMenu,
42
+ ISdPermission,
43
+ ISdFlatPermission,
44
+ } from "./core/providers/sd-app-structure.types";
43
45
  export { SdFileDialogProvider } from "./core/providers/sd-file-dialog.provider";
44
46
  export { SdLocalStorageProvider } from "./core/providers/sd-local-storage.provider";
45
47
  export { SdSystemConfigProvider } from "./core/providers/sd-system-config.provider";
@@ -58,7 +60,7 @@ export { useViewTitleSignal } from "./core/utils/useViewTitleSignal";
58
60
  export { useViewTypeSignal, type TSdViewType } from "./core/utils/useViewTypeSignal";
59
61
  export { setupCanDeactivate } from "./core/utils/setups/setupCanDeactivate";
60
62
  export { setupCumulateSelectedKeys } from "./core/utils/setups/setupCumulateSelectedKeys";
61
- export { setupCloserWhenSingleSelectionChange } from "./core/utils/setups/setupCloserWhenSingleSelectionChange";
63
+ export { setupCloserWhenSingleSelectionChange } from "./features/data-view/setupCloserWhenSingleSelectionChange";
62
64
  export {
63
65
  useExpandingManager,
64
66
  type IExpandItemDef,
@@ -100,6 +102,7 @@ export {
100
102
  export { SdSharedDataSelectControl } from "./features/shared-data/sd-shared-data-select.control";
101
103
  export { SdSharedDataSelectButtonControl } from "./features/shared-data/sd-shared-data-select-button.control";
102
104
  export { SdSharedDataSelectListControl } from "./features/shared-data/sd-shared-data-select-list.control";
105
+ export { matchesSearchText } from "./features/shared-data/matchesSearchText";
103
106
 
104
107
  // ui/layout
105
108
  export { SdDockContainerControl } from "./ui/layout/dock/sd-dock-container.control";
@@ -125,9 +128,9 @@ export { SdAdditionalButtonControl } from "./ui/form/button/sd-additional-button
125
128
  export {
126
129
  SdModalSelectButtonControl,
127
130
  type ISdSelectModal,
128
- type ISelectModalOutputResult,
129
131
  type TSdSelectModalInfo,
130
132
  } from "./ui/form/button/sd-modal-select-button.control";
133
+ export type { ISelectModalOutputResult } from "./core/types/select-modal-output-result";
131
134
 
132
135
  // ui/form/input
133
136
  export { SdTextfieldControl } from "./ui/form/input/sd-textfield.control";
@@ -170,6 +173,9 @@ export { SdTabItemControl } from "./ui/navigation/tab/sd-tab-item.control";
170
173
  export { SdTabviewControl } from "./ui/navigation/tab/sd-tabview.control";
171
174
  export { SdTabviewItemControl } from "./ui/navigation/tab/sd-tabview-item.control";
172
175
 
176
+ // ui/navigation/menu
177
+ export { getMenuRouterLinkOption, getIsMenuSelected } from "./ui/navigation/menu-utils";
178
+
173
179
  // ui/navigation/pagination
174
180
  export { SdPaginationControl } from "./ui/navigation/pagination/sd-pagination.control";
175
181
 
@@ -223,11 +229,11 @@ export { SdDropdownPopupControl } from "./ui/overlay/dropdown/sd-dropdown-popup.
223
229
  export { SdModalControl } from "./ui/overlay/modal/sd-modal.control";
224
230
  export {
225
231
  SdModalProvider,
226
- SdActivatedModalProvider,
227
232
  type ISdModal,
228
233
  type ISdModalInfo,
229
234
  type ISdModalOptions,
230
235
  } from "./ui/overlay/modal/sd-modal.provider";
236
+ export { SdActivatedModalProvider } from "./core/providers/sd-activated-modal.provider";
231
237
  export { SdPromptModalControl } from "./ui/overlay/modal/sd-prompt-modal.control";
232
238
  export { SdConfirmModalControl } from "./ui/overlay/modal/sd-confirm-modal.control";
233
239
 
@@ -240,14 +246,14 @@ export {
240
246
  type TSdToastTheme,
241
247
  type ISdToast,
242
248
  type ISdToastInput,
243
- } from "./ui/overlay/toast/sd-toast.provider";
249
+ } from "./core/providers/sd-toast.provider";
244
250
 
245
251
  // ui/overlay/busy
246
252
  export { SdBusyContainerControl } from "./ui/overlay/busy/sd-busy-container.control";
247
253
  export {
248
254
  SdBusyProvider,
249
255
  type TSdBusyType,
250
- } from "./ui/overlay/busy/sd-busy.provider";
256
+ } from "./core/providers/sd-busy.provider";
251
257
 
252
258
  // core/providers (integration)
253
259
  export {
@@ -4,12 +4,10 @@ import {
4
4
  Component,
5
5
  computed,
6
6
  contentChildren,
7
- DestroyRef,
8
7
  inject,
9
8
  input,
10
9
  model,
11
10
  output,
12
- signal,
13
11
  ViewEncapsulation,
14
12
  } from "@angular/core";
15
13
  import { NgTemplateOutlet } from "@angular/common";
@@ -29,12 +27,10 @@ import { useSheetLayoutEngine } from "./useSheetLayoutEngine";
29
27
  import { useSheetColumnFixing } from "./useSheetColumnFixing";
30
28
  import { useSelectionManager } from "../../../core/utils/useSelectionManager";
31
29
  import { useSortingManager, type ISortingDef } from "../../../core/utils/useSortingManager";
32
- import { useExpandingManager } from "../../../core/utils/useExpandingManager";
33
30
  import { SdPaginationControl } from "../../navigation/pagination/sd-pagination.control";
34
31
  import { SdAnchorControl } from "../../form/button/sd-anchor.control";
35
32
  import { SdButtonControl } from "../../form/button/sd-button.control";
36
33
  import type {
37
- ISdSheetColumnDef,
38
34
  ISdSheetCellKeydownEventParam,
39
35
  ISdSheetConfig,
40
36
  ISdSheetHeaderDef,
@@ -43,6 +39,9 @@ import type {
43
39
  import { useSdSystemConfigResource } from "../../../core/utils/useSdSystemConfigResource";
44
40
  import { useSheetDomAccessor } from "./useSheetDomAccessor";
45
41
  import { useSheetCellAgent } from "./useSheetCellAgent";
42
+ import { useSheetColumnResizing } from "./useSheetColumnResizing";
43
+ import { useSheetDisplayPipeline } from "./useSheetDisplayPipeline";
44
+ import { useSheetCellStyling } from "./useSheetCellStyling";
46
45
  import { SdModalProvider } from "../../overlay/modal/sd-modal.provider";
47
46
  import { SdSheetConfigModal } from "./sd-sheet-config.modal";
48
47
 
@@ -370,29 +369,26 @@ export class SdSheetControl<T> {
370
369
 
371
370
  // Injected providers
372
371
  private readonly _modalProvider = inject(SdModalProvider);
373
- private readonly _destroyRef = inject(DestroyRef);
374
372
 
375
373
  // DOM accessor & cell agent
376
374
  domAccessor = useSheetDomAccessor();
377
375
  cellAgent = useSheetCellAgent({ domAccessor: this.domAccessor });
378
376
 
379
- constructor() {
380
- this._destroyRef.onDestroy(() => {
381
- this._resizingCleanup?.();
382
- });
383
- }
384
-
385
- // Resizing state
386
- _isResizing = signal(false);
387
- _resizeIndicatorLeft = signal(0);
388
- private _lastResizeEndTimeStamp = 0;
389
- private _resizingCleanup: (() => void) | null = null;
390
-
391
377
  // Config resource
392
378
  private readonly _configResource = useSdSystemConfigResource<ISdSheetConfig>({
393
379
  key: this.key,
394
380
  });
395
381
 
382
+ // Resizing composable
383
+ private readonly _resizing = useSheetColumnResizing({
384
+ domAccessor: this.domAccessor,
385
+ configResource: this._configResource,
386
+ });
387
+ _isResizing = this._resizing.isResizing;
388
+ _resizeIndicatorLeft = this._resizing.indicatorLeft;
389
+ onResizerMousedown = this._resizing.onMousedown;
390
+ onResizerDblClick = this._resizing.onDblClick;
391
+
396
392
  // Layout engine
397
393
  layout = useSheetLayoutEngine({
398
394
  columnControls: this.columnControls,
@@ -409,67 +405,36 @@ export class SdSheetControl<T> {
409
405
  sorts: this.sorts,
410
406
  });
411
407
 
412
- // Effective page count (from totalPageCount or computed from items/itemsPerPage)
413
- effectivePageCount = computed(() => {
414
- const total = this.totalPageCount();
415
- if (total > 0) return total;
416
- const perPage = this.itemsPerPage();
417
- if (perPage <= 0) return 0;
418
- return Math.ceil(this.items().length / perPage);
419
- });
420
-
421
- // Icons
422
- icons = {
423
- tablerArrowUp,
424
- tablerArrowDown,
425
- tablerChevronRight,
426
- tablerChevronDown,
427
- tablerChevronsRight,
428
- tablerChevronsDown,
429
- tablerSettings,
430
- };
431
-
432
- // Sorted items
433
- private readonly _sortedItems = computed(() => {
434
- const items = this.items();
435
- if (this.useAutoSort()) {
436
- return this.sorting.sort(items);
437
- }
438
- return items;
439
- });
440
-
441
- // Paged items (root-level only)
442
- private readonly _pagedItems = computed(() => {
443
- const items = this._sortedItems();
444
- const perPage = this.itemsPerPage();
445
- if (perPage <= 0) return items;
446
- const page = this.currentPage();
447
- const start = page * perPage;
448
- return items.slice(start, start + perPage);
449
- });
450
-
451
- // Expanding manager
452
- expanding = useExpandingManager<T>({
453
- items: this._pagedItems,
408
+ // Display pipeline (sort page expand → display)
409
+ private readonly _pipeline = useSheetDisplayPipeline<T>({
410
+ items: this.items,
411
+ useAutoSort: this.useAutoSort,
412
+ sortItems: (items) => this.sorting.sort(items),
413
+ itemsPerPage: this.itemsPerPage,
414
+ currentPage: this.currentPage,
415
+ totalPageCount: this.totalPageCount,
454
416
  expandedItems: this.expandedItems,
455
417
  getChildrenFn: this.getChildrenFn,
456
- sort: (items) => {
457
- if (this.useAutoSort()) {
458
- return this.sorting.sort(items);
459
- }
460
- return items;
461
- },
462
418
  });
419
+ effectivePageCount = this._pipeline.effectivePageCount;
420
+ expanding = this._pipeline.expanding;
421
+ displayItems = this._pipeline.displayItems;
463
422
 
464
- // Final display items (sorted → paged → expanded)
465
- displayItems = computed(() => {
466
- const getChildrenFn = this.getChildrenFn();
467
- if (getChildrenFn != null) {
468
- // With tree: only show visible items (parent expanded)
469
- return this.expanding.displayItems().filter((item) => this.expanding.isVisible(item));
470
- }
471
- return this._pagedItems();
423
+ // Cell styling composable
424
+ private readonly _styling = useSheetCellStyling<T>({
425
+ columnDefs: this.layout.columnDefs,
426
+ fixedLeftMap: this.fixing.fixedLeftMap,
427
+ getItemCellStyleFn: this.getItemCellStyleFn,
428
+ getItemCellClassFn: this.getItemCellClassFn,
429
+ getChildrenFn: this.getChildrenFn,
430
+ expandingDef: (item) => this.expanding.def(item),
431
+ isCellEditMode: (addr) => this.cellAgent.isCellEditMode(addr),
472
432
  });
433
+ getHeaderCellStyle = this._styling.getHeaderCellStyle;
434
+ getCellStyle = this._styling.getCellStyle;
435
+ getFixedCellStyle = this._styling.getFixedCellStyle;
436
+ getCellStyleWithIndent = this._styling.getCellStyleWithIndent;
437
+ getDataCellClass = this._styling.getDataCellClass;
473
438
 
474
439
  // Selection manager
475
440
  selection = useSelectionManager<T>({
@@ -479,6 +444,17 @@ export class SdSheetControl<T> {
479
444
  getItemSelectableFn: this.getItemSelectableFn,
480
445
  });
481
446
 
447
+ // Icons
448
+ icons = {
449
+ tablerArrowUp,
450
+ tablerArrowDown,
451
+ tablerChevronRight,
452
+ tablerChevronDown,
453
+ tablerChevronsRight,
454
+ tablerChevronsDown,
455
+ tablerSettings,
456
+ };
457
+
482
458
  private readonly _columnControlMap = computed(() => {
483
459
  const map = new Map<string, SdSheetColumnDirective>();
484
460
  for (const col of this.columnControls()) {
@@ -497,51 +473,6 @@ export class SdSheetControl<T> {
497
473
  return col?.summaryTplRef() ?? null;
498
474
  }
499
475
 
500
- // Pre-computed column styles: header/footer (fixed z-index:3)
501
- private readonly _headerColumnStyles = computed(() => {
502
- const map = new Map<string, string | null>();
503
- for (const colDef of this.layout.columnDefs()) {
504
- const parts: string[] = [];
505
- const colStyle = this._getColDefStyle(colDef);
506
- if (colStyle != null) parts.push(colStyle);
507
- const fixedStyle = this._getFixedStyle(colDef, 3, "var(--theme-secondary-lightest)");
508
- if (fixedStyle != null) parts.push(fixedStyle);
509
- map.set(colDef.key, parts.length > 0 ? parts.join("; ") : null);
510
- }
511
- return map;
512
- });
513
-
514
- // Pre-computed column styles: body (fixed z-index:1)
515
- private readonly _dataColumnBaseStyles = computed(() => {
516
- const map = new Map<string, string | null>();
517
- for (const colDef of this.layout.columnDefs()) {
518
- const parts: string[] = [];
519
- const colStyle = this._getColDefStyle(colDef);
520
- if (colStyle != null) parts.push(colStyle);
521
- const fixedStyle = this._getFixedStyle(colDef);
522
- if (fixedStyle != null) parts.push(fixedStyle);
523
- map.set(colDef.key, parts.length > 0 ? parts.join("; ") : null);
524
- }
525
- return map;
526
- });
527
-
528
- getHeaderCellStyle(cell: ISdSheetHeaderDef) {
529
- if (cell.colDef == null) return null;
530
- return this._headerColumnStyles().get(cell.colDef.key) ?? null;
531
- }
532
-
533
- getCellStyle(item: T, colDef: ISdSheetColumnDef) {
534
- const baseStyle = this._dataColumnBaseStyles().get(colDef.key) ?? null;
535
- const styleFn = this.getItemCellStyleFn();
536
- const customStyle = styleFn != null ? styleFn(item, colDef.key) : undefined;
537
- if (baseStyle != null && customStyle != null) return `${baseStyle}; ${customStyle}`;
538
- return customStyle ?? baseStyle ?? null;
539
- }
540
-
541
- getFixedCellStyle(colDef: ISdSheetColumnDef) {
542
- return this._getFixedStyle(colDef, 3);
543
- }
544
-
545
476
  getSelectableTooltip(item: T): string | null {
546
477
  const result = this.selection.getSelectable(item);
547
478
  if (typeof result === "string") return result;
@@ -572,7 +503,7 @@ export class SdSheetControl<T> {
572
503
  }
573
504
 
574
505
  onHeaderClick(event: MouseEvent, cell: ISdSheetHeaderDef): void {
575
- if (event.timeStamp - this._lastResizeEndTimeStamp < 50) return;
506
+ if (event.timeStamp - this._resizing.lastResizeEndTimeStamp() < 50) return;
576
507
  if (cell.colDef == null) return;
577
508
  if (cell.colDef.disableSorting) return;
578
509
  this.sorting.toggle(cell.colDef.key, event.shiftKey);
@@ -582,22 +513,6 @@ export class SdSheetControl<T> {
582
513
  return this.sorting.defMap().get(key) ?? null;
583
514
  }
584
515
 
585
- getCellStyleWithIndent(item: T, colDef: ISdSheetColumnDef, colIdx: number) {
586
- const parts: string[] = [];
587
- const cellStyle = this.getCellStyle(item, colDef);
588
- if (cellStyle != null) {
589
- parts.push(cellStyle);
590
- }
591
- // Add indent for tree depth on first data column
592
- if (colIdx === 0 && this.getChildrenFn() != null) {
593
- const itemDef = this.getItemDef(item);
594
- if (itemDef.depth > 0) {
595
- parts.push(`padding-left: calc(var(--gap-default) + ${itemDef.depth}em)`);
596
- }
597
- }
598
- return parts.length > 0 ? parts.join("; ") : null;
599
- }
600
-
601
516
  getItemDef(item: T) {
602
517
  return this.expanding.def(item);
603
518
  }
@@ -628,41 +543,6 @@ export class SdSheetControl<T> {
628
543
  return sortDef.desc ? "descending" : "ascending";
629
544
  }
630
545
 
631
- private _getColDefStyle(colDef: { width: string | undefined; collapse: boolean }): string | null {
632
- if (colDef.collapse) {
633
- return "padding: 0; width: 0; min-width: 0; max-width: 0; overflow: hidden; border: none";
634
- }
635
- if (colDef.width != null) {
636
- return `width: ${colDef.width}; min-width: ${colDef.width}; max-width: ${colDef.width}`;
637
- }
638
- return null;
639
- }
640
-
641
- private _getFixedStyle(
642
- colDef: ISdSheetColumnDef,
643
- zIndex: number = 1,
644
- background: string = "var(--control-color)",
645
- ): string | null {
646
- const fixedLeftMap = this.fixing.fixedLeftMap();
647
- const leftValue = fixedLeftMap.get(colDef.key);
648
- if (leftValue == null) return null;
649
-
650
- return `position: sticky; left: ${leftValue}px; z-index: ${zIndex}; background: ${background}`;
651
- }
652
-
653
- getDataCellClass(item: T, colDef: ISdSheetColumnDef, r: number, c: number): string | null {
654
- const parts: string[] = [];
655
- const classFn = this.getItemCellClassFn();
656
- const customClass = classFn != null ? classFn(item, colDef.key) : undefined;
657
- if (customClass != null) {
658
- parts.push(customClass);
659
- }
660
- if (this.cellAgent.isCellEditMode({ r, c })) {
661
- parts.push("_edit-mode");
662
- }
663
- return parts.length > 0 ? parts.join(" ") : null;
664
- }
665
-
666
546
  async onKeydownCapture(event: Event): Promise<void> {
667
547
  await this.cellAgent.handleKeydownCapture(event as KeyboardEvent);
668
548
  }
@@ -699,74 +579,6 @@ export class SdSheetControl<T> {
699
579
  }
700
580
  }
701
581
 
702
- // --- Resizing ---
703
-
704
- onResizerMousedown(event: MouseEvent, colDef: ISdSheetColumnDef): void {
705
- event.preventDefault();
706
- event.stopPropagation();
707
-
708
- const container = this.domAccessor.getContainer();
709
- const startX = event.clientX;
710
- const th = (event.target as HTMLElement).parentElement as HTMLElement;
711
- const startWidth = th.offsetWidth;
712
-
713
- this._isResizing.set(true);
714
- this._resizeIndicatorLeft.set(
715
- th.offsetLeft + th.offsetWidth - container.scrollLeft,
716
- );
717
-
718
- const onMouseMove = (e: MouseEvent) => {
719
- const deltaX = e.clientX - startX;
720
- const newWidth = Math.max(startWidth + deltaX, 5);
721
- this._resizeIndicatorLeft.set(
722
- th.offsetLeft + newWidth - container.scrollLeft,
723
- );
724
- };
725
-
726
- const onMouseUp = (e: MouseEvent) => {
727
- document.removeEventListener("mousemove", onMouseMove);
728
- document.removeEventListener("mouseup", onMouseUp);
729
- this._resizingCleanup = null;
730
-
731
- const deltaX = e.clientX - startX;
732
- const newWidth = Math.max(startWidth + deltaX, 5);
733
-
734
- this._isResizing.set(false);
735
- this._saveColumnConfig(colDef.key, { width: `${newWidth}px` });
736
-
737
- this._lastResizeEndTimeStamp = e.timeStamp;
738
- };
739
-
740
- document.addEventListener("mousemove", onMouseMove);
741
- document.addEventListener("mouseup", onMouseUp);
742
- this._resizingCleanup = () => {
743
- document.removeEventListener("mousemove", onMouseMove);
744
- document.removeEventListener("mouseup", onMouseUp);
745
- };
746
- }
747
-
748
- onResizerDblClick(event: MouseEvent, colDef: ISdSheetColumnDef): void {
749
- event.preventDefault();
750
- event.stopPropagation();
751
-
752
- // Remove the width from config to reset to column definition default
753
- const current = this._configResource.value() ?? { columnRecord: {} };
754
- const columnRecord = { ...current.columnRecord };
755
- const colConfig = { ...columnRecord[colDef.key] };
756
- delete colConfig.width;
757
- columnRecord[colDef.key] = colConfig;
758
- this._configResource.set({ ...current, columnRecord });
759
- }
760
-
761
- private _saveColumnConfig(
762
- colKey: string,
763
- changes: Partial<ISdSheetConfig["columnRecord"][string]>,
764
- ): void {
765
- const current = this._configResource.value() ?? { columnRecord: {} };
766
- const columnRecord = { ...current.columnRecord };
767
- columnRecord[colKey] = { ...columnRecord[colKey], ...changes };
768
- this._configResource.set({ ...current, columnRecord });
769
- }
770
582
  }
771
583
 
772
584
  // Re-export ISortingDef from useSortingManager for convenience