@simplysm/angular 14.0.19 → 14.0.22

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 (143) 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 +19 -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-config.modal.d.ts +1 -2
  59. package/dist/ui/data/sheet/sd-sheet-config.modal.d.ts.map +1 -1
  60. package/dist/ui/data/sheet/sd-sheet-config.modal.js +8 -11
  61. package/dist/ui/data/sheet/sd-sheet.control.d.ts +22 -30
  62. package/dist/ui/data/sheet/sd-sheet.control.d.ts.map +1 -1
  63. package/dist/ui/data/sheet/sd-sheet.control.js +52 -210
  64. package/dist/ui/data/sheet/useSheetCellStyling.d.ts +22 -0
  65. package/dist/ui/data/sheet/useSheetCellStyling.d.ts.map +1 -0
  66. package/dist/ui/data/sheet/useSheetCellStyling.js +95 -0
  67. package/dist/ui/data/sheet/useSheetColumnResizing.d.ts +17 -0
  68. package/dist/ui/data/sheet/useSheetColumnResizing.d.ts.map +1 -0
  69. package/dist/ui/data/sheet/useSheetColumnResizing.js +65 -0
  70. package/dist/ui/data/sheet/useSheetDisplayPipeline.d.ts +24 -0
  71. package/dist/ui/data/sheet/useSheetDisplayPipeline.d.ts.map +1 -0
  72. package/dist/ui/data/sheet/useSheetDisplayPipeline.js +52 -0
  73. package/dist/ui/form/button/sd-modal-select-button.control.d.ts +1 -7
  74. package/dist/ui/form/button/sd-modal-select-button.control.d.ts.map +1 -1
  75. package/dist/ui/form/button/sd-modal-select-button.control.js +1 -1
  76. package/dist/ui/form/choice/sd-state-preset.control.js +1 -1
  77. package/dist/ui/form/select/sd-select.control.d.ts +1 -1
  78. package/dist/ui/form/select/sd-select.control.d.ts.map +1 -1
  79. package/dist/ui/form/select/sd-select.control.js +23 -27
  80. package/dist/ui/layout/sd-gap.control.d.ts +1 -2
  81. package/dist/ui/layout/sd-gap.control.d.ts.map +1 -1
  82. package/dist/ui/layout/sd-gap.control.js +22 -24
  83. package/dist/ui/navigation/menu-utils.d.ts +2 -7
  84. package/dist/ui/navigation/menu-utils.d.ts.map +1 -1
  85. package/dist/ui/overlay/busy/sd-busy-container.control.d.ts +1 -1
  86. package/dist/ui/overlay/busy/sd-busy-container.control.d.ts.map +1 -1
  87. package/dist/ui/overlay/busy/sd-busy-container.control.js +1 -1
  88. package/dist/ui/overlay/modal/sd-confirm-modal.control.d.ts.map +1 -1
  89. package/dist/ui/overlay/modal/sd-confirm-modal.control.js +29 -23
  90. package/dist/ui/overlay/modal/sd-modal.control.js +1 -1
  91. package/dist/ui/overlay/modal/sd-modal.provider.d.ts +0 -10
  92. package/dist/ui/overlay/modal/sd-modal.provider.d.ts.map +1 -1
  93. package/dist/ui/overlay/modal/sd-modal.provider.js +1 -13
  94. package/dist/ui/overlay/modal/sd-prompt-modal.control.d.ts.map +1 -1
  95. package/dist/ui/overlay/modal/sd-prompt-modal.control.js +40 -33
  96. package/dist/ui/overlay/toast/sd-toast.control.d.ts +1 -1
  97. package/dist/ui/overlay/toast/sd-toast.control.d.ts.map +1 -1
  98. package/package.json +5 -5
  99. package/src/core/provideSdAngular.ts +1 -1
  100. package/src/core/providers/sd-activated-modal.provider.ts +12 -0
  101. package/src/core/providers/sd-app-structure.provider.ts +2 -405
  102. package/src/core/providers/sd-app-structure.types.ts +60 -0
  103. package/src/core/providers/sd-app-structure.utils.ts +350 -0
  104. package/src/{ui/overlay/busy → core/providers}/sd-busy.provider.ts +1 -1
  105. package/src/core/providers/sd-print.provider.ts +1 -1
  106. package/src/core/providers/sd-service-client-factory.provider.ts +1 -1
  107. package/src/{ui/overlay/toast → core/providers}/sd-toast.provider.ts +4 -4
  108. package/src/core/types/select-modal-output-result.ts +7 -0
  109. package/src/core/utils/setups/setupCanDeactivate.ts +1 -1
  110. package/src/core/utils/useViewTitleSignal.ts +1 -1
  111. package/src/core/utils/useViewTypeSignal.ts +1 -1
  112. package/src/features/base/sd-base-container.control.ts +1 -1
  113. package/src/features/data-view/sd-data-detail.control.ts +1 -1
  114. package/src/features/data-view/sd-data-sheet.control.ts +117 -216
  115. package/src/features/data-view/sd-data-sheet.types.ts +18 -0
  116. package/src/{core/utils/setups → features/data-view}/setupCloserWhenSingleSelectionChange.ts +1 -1
  117. package/src/features/data-view/useDataSheetExcelManager.ts +57 -0
  118. package/src/features/data-view/useDataSheetFilterManager.ts +30 -0
  119. package/src/features/data-view/useDataSheetInlineEditManager.ts +89 -0
  120. package/src/features/data-view/useDataSheetModalEditManager.ts +76 -0
  121. package/src/features/data-view/useDataSheetRefreshManager.ts +90 -0
  122. package/src/features/permission-table/sd-permission-table.control.ts +1 -1
  123. package/src/index.ts +17 -11
  124. package/src/ui/data/sheet/sd-sheet-config.modal.ts +7 -11
  125. package/src/ui/data/sheet/sd-sheet.control.ts +50 -238
  126. package/src/ui/data/sheet/useSheetCellStyling.ts +113 -0
  127. package/src/ui/data/sheet/useSheetColumnResizing.ts +92 -0
  128. package/src/ui/data/sheet/useSheetDisplayPipeline.ts +64 -0
  129. package/src/ui/form/button/sd-modal-select-button.control.ts +1 -8
  130. package/src/ui/form/choice/sd-state-preset.control.ts +1 -1
  131. package/src/ui/form/select/sd-select.control.ts +21 -26
  132. package/src/ui/layout/sd-gap.control.ts +17 -21
  133. package/src/ui/navigation/menu-utils.ts +3 -7
  134. package/src/ui/overlay/busy/sd-busy-container.control.ts +1 -1
  135. package/src/ui/overlay/modal/sd-confirm-modal.control.ts +8 -26
  136. package/src/ui/overlay/modal/sd-modal.control.ts +1 -1
  137. package/src/ui/overlay/modal/sd-modal.provider.ts +1 -10
  138. package/src/ui/overlay/modal/sd-prompt-modal.control.ts +17 -43
  139. package/src/ui/overlay/toast/sd-toast.control.ts +1 -1
  140. package/dist/core/utils/setups/setupCloserWhenSingleSelectionChange.d.ts.map +0 -1
  141. package/dist/ui/overlay/busy/sd-busy.provider.d.ts.map +0 -1
  142. package/dist/ui/overlay/toast/sd-toast.provider.d.ts.map +0 -1
  143. /package/dist/{core/utils/setups → features/data-view}/setupCloserWhenSingleSelectionChange.js +0 -0
@@ -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
@@ -0,0 +1,113 @@
1
+ import { computed, type Signal } from "@angular/core";
2
+ import type { ISdSheetColumnDef, ISdSheetHeaderDef } from "./types";
3
+ import type { IExpandItemDef } from "../../../core/utils/useExpandingManager";
4
+
5
+ export function useSheetCellStyling<T>(options: {
6
+ columnDefs: Signal<ISdSheetColumnDef[]>;
7
+ fixedLeftMap: Signal<Map<string, number>>;
8
+ getItemCellStyleFn: Signal<((item: T, colKey: string) => string | undefined) | undefined>;
9
+ getItemCellClassFn: Signal<((item: T, colKey: string) => string) | undefined>;
10
+ getChildrenFn: Signal<((item: T, index: number) => T[] | undefined) | undefined>;
11
+ expandingDef: (item: T) => IExpandItemDef<T>;
12
+ isCellEditMode: (addr: { r: number; c: number }) => boolean;
13
+ }) {
14
+ function getColDefStyle(colDef: { width: string | undefined; collapse: boolean }): string | null {
15
+ if (colDef.collapse) {
16
+ return "padding: 0; width: 0; min-width: 0; max-width: 0; overflow: hidden; border: none";
17
+ }
18
+ if (colDef.width != null) {
19
+ return `width: ${colDef.width}; min-width: ${colDef.width}; max-width: ${colDef.width}`;
20
+ }
21
+ return null;
22
+ }
23
+
24
+ function getFixedStyle(
25
+ colDef: ISdSheetColumnDef,
26
+ zIndex: number = 1,
27
+ background: string = "var(--control-color)",
28
+ ): string | null {
29
+ const leftValue = options.fixedLeftMap().get(colDef.key);
30
+ if (leftValue == null) return null;
31
+
32
+ return `position: sticky; left: ${leftValue}px; z-index: ${zIndex}; background: ${background}`;
33
+ }
34
+
35
+ const headerColumnStyles = computed(() => {
36
+ const map = new Map<string, string | null>();
37
+ for (const colDef of options.columnDefs()) {
38
+ const parts: string[] = [];
39
+ const colStyle = getColDefStyle(colDef);
40
+ if (colStyle != null) parts.push(colStyle);
41
+ const fixedStyle = getFixedStyle(colDef, 3, "var(--theme-secondary-lightest)");
42
+ if (fixedStyle != null) parts.push(fixedStyle);
43
+ map.set(colDef.key, parts.length > 0 ? parts.join("; ") : null);
44
+ }
45
+ return map;
46
+ });
47
+
48
+ const dataColumnBaseStyles = computed(() => {
49
+ const map = new Map<string, string | null>();
50
+ for (const colDef of options.columnDefs()) {
51
+ const parts: string[] = [];
52
+ const colStyle = getColDefStyle(colDef);
53
+ if (colStyle != null) parts.push(colStyle);
54
+ const fixedStyle = getFixedStyle(colDef);
55
+ if (fixedStyle != null) parts.push(fixedStyle);
56
+ map.set(colDef.key, parts.length > 0 ? parts.join("; ") : null);
57
+ }
58
+ return map;
59
+ });
60
+
61
+ function getHeaderCellStyle(cell: ISdSheetHeaderDef): string | null {
62
+ if (cell.colDef == null) return null;
63
+ return headerColumnStyles().get(cell.colDef.key) ?? null;
64
+ }
65
+
66
+ function getCellStyle(item: T, colDef: ISdSheetColumnDef): string | null {
67
+ const baseStyle = dataColumnBaseStyles().get(colDef.key) ?? null;
68
+ const styleFn = options.getItemCellStyleFn();
69
+ const customStyle = styleFn != null ? styleFn(item, colDef.key) : undefined;
70
+ if (baseStyle != null && customStyle != null) return `${baseStyle}; ${customStyle}`;
71
+ return customStyle ?? baseStyle ?? null;
72
+ }
73
+
74
+ function getFixedCellStyle(colDef: ISdSheetColumnDef): string | null {
75
+ return getFixedStyle(colDef, 3);
76
+ }
77
+
78
+ function getCellStyleWithIndent(item: T, colDef: ISdSheetColumnDef, colIdx: number): string | null {
79
+ const parts: string[] = [];
80
+ const cellStyle = getCellStyle(item, colDef);
81
+ if (cellStyle != null) {
82
+ parts.push(cellStyle);
83
+ }
84
+ if (colIdx === 0 && options.getChildrenFn() != null) {
85
+ const itemDef = options.expandingDef(item);
86
+ if (itemDef.depth > 0) {
87
+ parts.push(`padding-left: calc(var(--gap-default) + ${itemDef.depth}em)`);
88
+ }
89
+ }
90
+ return parts.length > 0 ? parts.join("; ") : null;
91
+ }
92
+
93
+ function getDataCellClass(item: T, colDef: ISdSheetColumnDef, r: number, c: number): string | null {
94
+ const parts: string[] = [];
95
+ const classFn = options.getItemCellClassFn();
96
+ const customClass = classFn != null ? classFn(item, colDef.key) : undefined;
97
+ if (customClass != null) {
98
+ parts.push(customClass);
99
+ }
100
+ if (options.isCellEditMode({ r, c })) {
101
+ parts.push("_edit-mode");
102
+ }
103
+ return parts.length > 0 ? parts.join(" ") : null;
104
+ }
105
+
106
+ return {
107
+ getHeaderCellStyle,
108
+ getCellStyle,
109
+ getFixedCellStyle,
110
+ getCellStyleWithIndent,
111
+ getDataCellClass,
112
+ };
113
+ }
@@ -0,0 +1,92 @@
1
+ import { DestroyRef, inject, signal, type Signal } from "@angular/core";
2
+ import type { ISdSheetColumnDef, ISdSheetConfig } from "./types";
3
+ import type { useSheetDomAccessor } from "./useSheetDomAccessor";
4
+
5
+ export function useSheetColumnResizing(options: {
6
+ domAccessor: ReturnType<typeof useSheetDomAccessor>;
7
+ configResource: {
8
+ value: Signal<ISdSheetConfig | undefined>;
9
+ set: (v: ISdSheetConfig) => void;
10
+ };
11
+ }) {
12
+ const destroyRef = inject(DestroyRef);
13
+
14
+ const isResizing = signal(false);
15
+ const indicatorLeft = signal(0);
16
+ const lastResizeEndTimeStamp = signal(0);
17
+ let resizingCleanup: (() => void) | null = null;
18
+
19
+ destroyRef.onDestroy(() => {
20
+ resizingCleanup?.();
21
+ });
22
+
23
+ function saveColumnConfig(
24
+ colKey: string,
25
+ changes: Partial<ISdSheetConfig["columnRecord"][string]>,
26
+ ): void {
27
+ const current = options.configResource.value() ?? { columnRecord: {} };
28
+ const columnRecord = { ...current.columnRecord };
29
+ columnRecord[colKey] = { ...columnRecord[colKey], ...changes };
30
+ options.configResource.set({ ...current, columnRecord });
31
+ }
32
+
33
+ function onMousedown(event: MouseEvent, colDef: ISdSheetColumnDef): void {
34
+ event.preventDefault();
35
+ event.stopPropagation();
36
+
37
+ const container = options.domAccessor.getContainer();
38
+ const startX = event.clientX;
39
+ const th = (event.target as HTMLElement).parentElement as HTMLElement;
40
+ const startWidth = th.offsetWidth;
41
+
42
+ isResizing.set(true);
43
+ indicatorLeft.set(th.offsetLeft + th.offsetWidth - container.scrollLeft);
44
+
45
+ const onMouseMove = (e: MouseEvent) => {
46
+ const deltaX = e.clientX - startX;
47
+ const newWidth = Math.max(startWidth + deltaX, 5);
48
+ indicatorLeft.set(th.offsetLeft + newWidth - container.scrollLeft);
49
+ };
50
+
51
+ const onMouseUp = (e: MouseEvent) => {
52
+ document.removeEventListener("mousemove", onMouseMove);
53
+ document.removeEventListener("mouseup", onMouseUp);
54
+ resizingCleanup = null;
55
+
56
+ const deltaX = e.clientX - startX;
57
+ const newWidth = Math.max(startWidth + deltaX, 5);
58
+
59
+ isResizing.set(false);
60
+ saveColumnConfig(colDef.key, { width: `${newWidth}px` });
61
+
62
+ lastResizeEndTimeStamp.set(e.timeStamp);
63
+ };
64
+
65
+ document.addEventListener("mousemove", onMouseMove);
66
+ document.addEventListener("mouseup", onMouseUp);
67
+ resizingCleanup = () => {
68
+ document.removeEventListener("mousemove", onMouseMove);
69
+ document.removeEventListener("mouseup", onMouseUp);
70
+ };
71
+ }
72
+
73
+ function onDblClick(event: MouseEvent, colDef: ISdSheetColumnDef): void {
74
+ event.preventDefault();
75
+ event.stopPropagation();
76
+
77
+ const current = options.configResource.value() ?? { columnRecord: {} };
78
+ const columnRecord = { ...current.columnRecord };
79
+ const colConfig = { ...columnRecord[colDef.key] };
80
+ delete colConfig.width;
81
+ columnRecord[colDef.key] = colConfig;
82
+ options.configResource.set({ ...current, columnRecord });
83
+ }
84
+
85
+ return {
86
+ isResizing,
87
+ indicatorLeft,
88
+ lastResizeEndTimeStamp,
89
+ onMousedown,
90
+ onDblClick,
91
+ };
92
+ }
@@ -0,0 +1,64 @@
1
+ import { computed, type Signal, type WritableSignal } from "@angular/core";
2
+ import { useExpandingManager } from "../../../core/utils/useExpandingManager";
3
+
4
+ export function useSheetDisplayPipeline<T>(options: {
5
+ items: Signal<T[]>;
6
+ useAutoSort: Signal<boolean>;
7
+ sortItems: (items: T[]) => T[];
8
+ itemsPerPage: Signal<number>;
9
+ currentPage: Signal<number>;
10
+ totalPageCount: Signal<number>;
11
+ expandedItems: WritableSignal<T[]>;
12
+ getChildrenFn: Signal<((item: T, index: number) => T[] | undefined) | undefined>;
13
+ }) {
14
+ const effectivePageCount = computed(() => {
15
+ const total = options.totalPageCount();
16
+ if (total > 0) return total;
17
+ const perPage = options.itemsPerPage();
18
+ if (perPage <= 0) return 0;
19
+ return Math.ceil(options.items().length / perPage);
20
+ });
21
+
22
+ const sortedItems = computed(() => {
23
+ const items = options.items();
24
+ if (options.useAutoSort()) {
25
+ return options.sortItems(items);
26
+ }
27
+ return items;
28
+ });
29
+
30
+ const pagedItems = computed(() => {
31
+ const items = sortedItems();
32
+ const perPage = options.itemsPerPage();
33
+ if (perPage <= 0) return items;
34
+ const page = options.currentPage();
35
+ const start = page * perPage;
36
+ return items.slice(start, start + perPage);
37
+ });
38
+
39
+ const expanding = useExpandingManager<T>({
40
+ items: pagedItems,
41
+ expandedItems: options.expandedItems,
42
+ getChildrenFn: options.getChildrenFn,
43
+ sort: (items) => {
44
+ if (options.useAutoSort()) {
45
+ return options.sortItems(items);
46
+ }
47
+ return items;
48
+ },
49
+ });
50
+
51
+ const displayItems = computed(() => {
52
+ const getChildrenFn = options.getChildrenFn();
53
+ if (getChildrenFn != null) {
54
+ return expanding.displayItems().filter((item) => expanding.isVisible(item));
55
+ }
56
+ return pagedItems();
57
+ });
58
+
59
+ return {
60
+ effectivePageCount,
61
+ displayItems,
62
+ expanding,
63
+ };
64
+ }
@@ -19,6 +19,7 @@ import type { TSelectModeValue } from "../select/sd-select.control";
19
19
  import { SdAnchorControl } from "./sd-anchor.control";
20
20
  import { SdButtonControl } from "./sd-button.control";
21
21
  import { setupInvalid } from "../../../core/utils/setups/setupInvalid";
22
+ import type { ISelectModalOutputResult } from "../../../core/types/select-modal-output-result";
22
23
  import { NgIcon } from "@ng-icons/core";
23
24
  import { tablerSearch, tablerEraser } from "@ng-icons/tabler-icons";
24
25
 
@@ -31,14 +32,6 @@ export interface ISdSelectModal<T> extends ISdModal<ISelectModalOutputResult<T>>
31
32
  selectedItemKeys: InputSignal<any[]>;
32
33
  }
33
34
 
34
- /**
35
- * 모달 선택 결과
36
- */
37
- export interface ISelectModalOutputResult<T> {
38
- selectedItemKeys: any[];
39
- selectedItems: T[];
40
- }
41
-
42
35
  /**
43
36
  * 모달 선택 정보 (selectMode/selectedItemKeys를 제외한 inputs)
44
37
  */
@@ -8,7 +8,7 @@ import {
8
8
  ViewEncapsulation,
9
9
  } from "@angular/core";
10
10
  import { SdModalProvider } from "../../overlay/modal/sd-modal.provider";
11
- import { SdToastProvider } from "../../overlay/toast/sd-toast.provider";
11
+ import { SdToastProvider } from "../../../core/providers/sd-toast.provider";
12
12
  import { SdPromptModalControl } from "../../overlay/modal/sd-prompt-modal.control";
13
13
  import { SdConfirmModalControl } from "../../overlay/modal/sd-confirm-modal.control";
14
14
  import { useSdSystemConfigResource } from "../../../core/utils/useSdSystemConfigResource";