@simplysm/angular 14.0.40 → 14.0.42

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 (86) hide show
  1. package/dist/controls/collapse/sd-collapse-icon.d.ts +2 -4
  2. package/dist/controls/collapse/sd-collapse-icon.d.ts.map +1 -1
  3. package/dist/controls/collapse/sd-collapse-icon.js +5 -5
  4. package/dist/core/app-structure/sd-app-structure.provider.d.ts +2 -2
  5. package/dist/core/app-structure/sd-app-structure.provider.d.ts.map +1 -1
  6. package/dist/core/app-structure/sd-app-structure.provider.js +4 -4
  7. package/dist/core/config/sd-local-storage.provider.d.ts +1 -1
  8. package/dist/core/config/sd-local-storage.provider.d.ts.map +1 -1
  9. package/dist/core/config/sd-local-storage.provider.js +4 -4
  10. package/dist/core/error-handler/sd-global-error-handler.plugin.d.ts +1 -1
  11. package/dist/core/error-handler/sd-global-error-handler.plugin.d.ts.map +1 -1
  12. package/dist/core/error-handler/sd-global-error-handler.plugin.js +3 -3
  13. package/dist/core/modal/sd-modal.d.ts +4 -3
  14. package/dist/core/modal/sd-modal.d.ts.map +1 -1
  15. package/dist/core/modal/sd-modal.js +14 -12
  16. package/dist/core/modal/sd-modal.provider.d.ts +1 -0
  17. package/dist/core/modal/sd-modal.provider.d.ts.map +1 -1
  18. package/dist/core/routing/injectViewTitleSignal.js +3 -3
  19. package/dist/core/routing/injectViewTypeSignal.js +2 -2
  20. package/dist/core/routing/sd-router-link.d.ts +1 -1
  21. package/dist/core/routing/sd-router-link.d.ts.map +1 -1
  22. package/dist/core/routing/sd-router-link.js +4 -4
  23. package/dist/core/routing/setupCanDeactivate.js +3 -3
  24. package/dist/core/service-client/sd-service-client-factory.provider.d.ts +2 -2
  25. package/dist/core/service-client/sd-service-client-factory.provider.d.ts.map +1 -1
  26. package/dist/core/service-client/sd-service-client-factory.provider.js +4 -4
  27. package/dist/core/shared-data/sd-shared-data.provider.d.ts +4 -3
  28. package/dist/core/shared-data/sd-shared-data.provider.d.ts.map +1 -1
  29. package/dist/core/shared-data/sd-shared-data.provider.js +4 -4
  30. package/dist/core/toast/sd-toast.provider.d.ts +3 -3
  31. package/dist/core/toast/sd-toast.provider.d.ts.map +1 -1
  32. package/dist/core/toast/sd-toast.provider.js +2 -2
  33. package/dist/data/data-detail/sd-data-detail.base.d.ts +6 -4
  34. package/dist/data/data-detail/sd-data-detail.base.d.ts.map +1 -1
  35. package/dist/data/data-select-button/sd-data-select-button.base.d.ts +2 -2
  36. package/dist/data/data-select-button/sd-data-select-button.base.d.ts.map +1 -1
  37. package/dist/data/data-sheet/sd-data-sheet.d.ts.map +1 -1
  38. package/dist/data/data-sheet/sd-data-sheet.js +75 -43
  39. package/dist/data/sheet/sd-sheet-column-cell-template.d.ts +9 -0
  40. package/dist/data/sheet/sd-sheet-column-cell-template.d.ts.map +1 -0
  41. package/dist/data/sheet/sd-sheet-column-cell-template.js +17 -0
  42. package/dist/data/sheet/sd-sheet-column.d.ts +10 -7
  43. package/dist/data/sheet/sd-sheet-column.d.ts.map +1 -1
  44. package/dist/data/sheet/sd-sheet-column.js +10 -6
  45. package/dist/data/sheet/sd-sheet-config.modal.d.ts +1 -1
  46. package/dist/data/sheet/sd-sheet-config.modal.d.ts.map +1 -1
  47. package/dist/data/sheet/sd-sheet.d.ts +4 -3
  48. package/dist/data/sheet/sd-sheet.d.ts.map +1 -1
  49. package/dist/data/sheet/sd-sheet.js +61 -36
  50. package/dist/data/sheet/types.d.ts +2 -0
  51. package/dist/data/sheet/types.d.ts.map +1 -1
  52. package/dist/data/sheet/useSheetCellStyling.d.ts.map +1 -1
  53. package/dist/data/sheet/useSheetCellStyling.js +7 -1
  54. package/dist/data/sheet/useSheetLayoutEngine.d.ts.map +1 -1
  55. package/dist/data/sheet/useSheetLayoutEngine.js +2 -0
  56. package/dist/index.d.ts +2 -1
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js +1 -0
  59. package/dist/layout/base-container/sd-base-container.d.ts +1 -1
  60. package/dist/layout/base-container/sd-base-container.d.ts.map +1 -1
  61. package/dist/layout/base-container/sd-base-container.js +2 -2
  62. package/package.json +6 -6
  63. package/src/controls/collapse/sd-collapse-icon.ts +2 -3
  64. package/src/core/app-structure/sd-app-structure.provider.ts +4 -4
  65. package/src/core/config/sd-local-storage.provider.ts +4 -4
  66. package/src/core/error-handler/sd-global-error-handler.plugin.ts +3 -3
  67. package/src/core/modal/sd-modal.provider.ts +1 -0
  68. package/src/core/modal/sd-modal.ts +10 -9
  69. package/src/core/routing/injectViewTitleSignal.ts +3 -3
  70. package/src/core/routing/injectViewTypeSignal.ts +2 -2
  71. package/src/core/routing/sd-router-link.ts +4 -4
  72. package/src/core/routing/setupCanDeactivate.ts +3 -3
  73. package/src/core/service-client/sd-service-client-factory.provider.ts +4 -4
  74. package/src/core/shared-data/sd-shared-data.provider.ts +5 -5
  75. package/src/core/toast/sd-toast.provider.ts +6 -6
  76. package/src/data/data-detail/sd-data-detail.base.ts +3 -3
  77. package/src/data/data-select-button/sd-data-select-button.base.ts +1 -1
  78. package/src/data/data-sheet/sd-data-sheet.ts +14 -5
  79. package/src/data/sheet/sd-sheet-column-cell-template.ts +17 -0
  80. package/src/data/sheet/sd-sheet-column.ts +12 -5
  81. package/src/data/sheet/sd-sheet.ts +14 -3
  82. package/src/data/sheet/types.ts +2 -0
  83. package/src/data/sheet/useSheetCellStyling.ts +5 -1
  84. package/src/data/sheet/useSheetLayoutEngine.ts +2 -0
  85. package/src/index.ts +2 -1
  86. package/src/layout/base-container/sd-base-container.ts +2 -2
@@ -38,7 +38,7 @@ import "@simplysm/core-browser";
38
38
  (keydown)="onDialogKeydown($event)"
39
39
  (focus)="onDialogFocus()">
40
40
  @if (!hideHeader()) {
41
- <div class="_header" (mousedown)="onHeaderMouseDown($event)">
41
+ <div class="_header" (mousedown)="onHeaderMouseDown($event)" [style]="headerStyle()">
42
42
  <span class="_title">{{ title() }}</span>
43
43
  @if (actionTplRef()) {
44
44
  <ng-container *ngTemplateOutlet="actionTplRef()!" />
@@ -285,8 +285,8 @@ import "@simplysm/core-browser";
285
285
  })
286
286
  export class SdModal {
287
287
  private readonly _elRef = inject(ElementRef<HTMLElement>);
288
- private readonly _activatedModal = inject(SdActivatedModalProvider, { optional: true });
289
- private readonly _systemConfig = inject(SdSystemConfigProvider, { optional: true });
288
+ private readonly _sdActivatedModal = inject(SdActivatedModalProvider, { optional: true });
289
+ private readonly _sdSystemConfig = inject(SdSystemConfigProvider, { optional: true });
290
290
  private readonly _errorHandler = inject(ErrorHandler);
291
291
  private readonly _focusTrap = injectFocusTrap();
292
292
 
@@ -297,6 +297,7 @@ export class SdModal {
297
297
  title = input("");
298
298
  hideHeader = input(false);
299
299
  hideCloseButton = input(false);
300
+ headerStyle = input<string | undefined>(undefined);
300
301
  useCloseByBackdrop = input(true);
301
302
  useCloseByEscapeKey = input(true);
302
303
  float = input(false);
@@ -347,7 +348,7 @@ export class SdModal {
347
348
  // key 기반 설정 복원
348
349
  effect(() => {
349
350
  const k = this.key();
350
- if (k === undefined || this._systemConfig == null) return;
351
+ if (k === undefined || this._sdSystemConfig == null) return;
351
352
 
352
353
  void this._restoreConfig(k).catch((err) => this._errorHandler.handleError(err));
353
354
  });
@@ -389,7 +390,7 @@ export class SdModal {
389
390
  }
390
391
 
391
392
  private _requestClose(): void {
392
- if (this._activatedModal !== null && !this._activatedModal.canDeactivateFn()) {
393
+ if (this._sdActivatedModal !== null && !this._sdActivatedModal.canDeactivateFn()) {
393
394
  return;
394
395
  }
395
396
  void this._saveConfig().catch((err) => this._errorHandler.handleError(err));
@@ -418,7 +419,7 @@ export class SdModal {
418
419
 
419
420
  private async _saveConfig(): Promise<void> {
420
421
  const k = this.key();
421
- if (k === undefined || this._systemConfig == null) return;
422
+ if (k === undefined || this._sdSystemConfig == null) return;
422
423
 
423
424
  const dialogEl = this._getDialogEl();
424
425
  if (dialogEl === null) return;
@@ -429,13 +430,13 @@ export class SdModal {
429
430
  if (dialogEl.style.left !== "") config["left"] = dialogEl.style.left;
430
431
  if (dialogEl.style.top !== "") config["top"] = dialogEl.style.top;
431
432
 
432
- await this._systemConfig.setAsync(`sd-modal.${k}` as any, config as any);
433
+ await this._sdSystemConfig.setAsync(`sd-modal.${k}` as any, config as any);
433
434
  }
434
435
 
435
436
  private async _restoreConfig(k: string): Promise<void> {
436
- if (this._systemConfig == null) return;
437
+ if (this._sdSystemConfig == null) return;
437
438
 
438
- const config = (await this._systemConfig.getAsync(`sd-modal.${k}` as any)) as
439
+ const config = (await this._sdSystemConfig.getAsync(`sd-modal.${k}` as any)) as
439
440
  | Record<string, string | undefined>
440
441
  | undefined;
441
442
  if (config == null) return;
@@ -5,15 +5,15 @@ import { injectFullPageCodeSignal } from "./injectFullPageCodeSignal";
5
5
  import { injectCurrentPageCodeSignal } from "./injectCurrentPageCodeSignal";
6
6
 
7
7
  export function injectViewTitleSignal(): Signal<string> {
8
- const activatedModal = inject(SdActivatedModalProvider, { optional: true });
8
+ const sdActivatedModal = inject(SdActivatedModalProvider, { optional: true });
9
9
  const sdAppStructure = inject(SdAppStructureProvider);
10
10
 
11
11
  const fullPageCode = injectFullPageCodeSignal();
12
12
  const currPageCode = injectCurrentPageCodeSignal();
13
13
 
14
14
  return computed(() => {
15
- if (activatedModal != null) {
16
- return activatedModal.modalComponent()?.title() ?? "";
15
+ if (sdActivatedModal != null) {
16
+ return sdActivatedModal.modalComponent()?.title() ?? "";
17
17
  }
18
18
 
19
19
  try {
@@ -5,14 +5,14 @@ import { injectCurrentPageCodeSignal } from "./injectCurrentPageCodeSignal";
5
5
  import { injectFullPageCodeSignal } from "./injectFullPageCodeSignal";
6
6
 
7
7
  export function injectViewTypeSignal(getComp: () => object): Signal<SdViewType> {
8
- const activatedModal = inject(SdActivatedModalProvider, { optional: true });
8
+ const sdActivatedModal = inject(SdActivatedModalProvider, { optional: true });
9
9
  const activatedRoute = inject(ActivatedRoute, { optional: true });
10
10
 
11
11
  const fullPageCode = injectFullPageCodeSignal();
12
12
  const currPageCode = injectCurrentPageCodeSignal();
13
13
 
14
14
  return computed<SdViewType>(() => {
15
- if (activatedModal != null) {
15
+ if (sdActivatedModal != null) {
16
16
  return "modal";
17
17
  }
18
18
 
@@ -12,7 +12,7 @@ import { SdNavigateWindowProvider } from "./sd-navigate-window.provider";
12
12
  })
13
13
  export class SdRouterLink {
14
14
  private readonly _router = inject(Router);
15
- private readonly _navWindow = inject(SdNavigateWindowProvider);
15
+ private readonly _sdNavigateWindow = inject(SdNavigateWindowProvider);
16
16
 
17
17
  option = input<
18
18
  | {
@@ -39,13 +39,13 @@ export class SdRouterLink {
39
39
  event.preventDefault();
40
40
  event.stopPropagation();
41
41
 
42
- if (this._navWindow.isWindow) {
42
+ if (this._sdNavigateWindow.isWindow) {
43
43
  const width = option.window?.width ?? 800;
44
44
  const height = option.window?.height ?? 800;
45
45
  const qp = option.queryParams
46
46
  ? "?" + new URLSearchParams(option.queryParams).toString()
47
47
  : "";
48
- this._navWindow.open(
48
+ this._sdNavigateWindow.open(
49
49
  option.link + qp,
50
50
  option.params,
51
51
  `width=${width},height=${height}`,
@@ -54,7 +54,7 @@ export class SdRouterLink {
54
54
  const qp = option.queryParams
55
55
  ? "?" + new URLSearchParams(option.queryParams).toString()
56
56
  : "";
57
- this._navWindow.open(option.link + qp, option.params);
57
+ this._sdNavigateWindow.open(option.link + qp, option.params);
58
58
  } else if (option.outletName === undefined) {
59
59
  await this._router.navigate(
60
60
  [option.link, ...(option.params ? [option.params] : [])],
@@ -3,12 +3,12 @@ import { ActivatedRoute, type CanDeactivateFn } from "@angular/router";
3
3
  import { SdActivatedModalProvider } from "../modal/sd-activated-modal.provider";
4
4
 
5
5
  export function setupCanDeactivate(fn: () => boolean): void {
6
- const activatedModal = inject(SdActivatedModalProvider, { optional: true });
6
+ const sdActivatedModal = inject(SdActivatedModalProvider, { optional: true });
7
7
  const activatedRoute = inject(ActivatedRoute, { optional: true });
8
8
  const elRef = inject(ElementRef);
9
9
 
10
- if (activatedModal != null) {
11
- activatedModal.canDeactivateFn = fn;
10
+ if (sdActivatedModal != null) {
11
+ sdActivatedModal.canDeactivateFn = fn;
12
12
  return;
13
13
  }
14
14
 
@@ -6,8 +6,8 @@ import { SdToastProvider } from "../toast/sd-toast.provider";
6
6
 
7
7
  @Injectable({ providedIn: "root" })
8
8
  export class SdServiceClientFactoryProvider {
9
- private readonly _toastProvider = inject(SdToastProvider);
10
- private readonly _configProvider = inject(SdAngularConfigProvider);
9
+ private readonly _sdToast = inject(SdToastProvider);
10
+ private readonly _sdAngularConfig = inject(SdAngularConfigProvider);
11
11
  private readonly _destroyRef = inject(DestroyRef);
12
12
 
13
13
  private readonly _clientMap = new Map<string, ServiceClient>();
@@ -43,7 +43,7 @@ export class SdServiceClientFactoryProvider {
43
43
  ...options,
44
44
  };
45
45
 
46
- const client = createServiceClient(this._configProvider.clientName, mergedOptions);
46
+ const client = createServiceClient(this._sdAngularConfig.clientName, mergedOptions);
47
47
 
48
48
  client.on("request-progress", (state: ServiceProgressState) => {
49
49
  this._handleProgress("요청을 전송하는 중입니다.", state);
@@ -85,7 +85,7 @@ export class SdServiceClientFactoryProvider {
85
85
 
86
86
  let progressSignal = this._progressMap.get(uuid);
87
87
  if (progressSignal == null) {
88
- progressSignal = this._toastProvider.info(message, true);
88
+ progressSignal = this._sdToast.info(message, true);
89
89
  this._progressMap.set(uuid, progressSignal);
90
90
  }
91
91
 
@@ -40,8 +40,8 @@ interface SharedDataEntry<T extends SharedDataBase<string | number>> {
40
40
 
41
41
  @Injectable()
42
42
  export abstract class SdSharedDataProvider<T extends Record<string, SharedDataBase<string | number>>> {
43
- private readonly _clientFactory = inject(SdServiceClientFactoryProvider);
44
- private readonly _errorHandler = inject(ErrorHandler);
43
+ protected readonly _sdServiceClientFactory = inject(SdServiceClientFactoryProvider);
44
+ protected readonly _errorHandler = inject(ErrorHandler);
45
45
 
46
46
  readonly loadingCount: WritableSignal<number> = signal(0);
47
47
 
@@ -54,7 +54,7 @@ export abstract class SdSharedDataProvider<T extends Record<string, SharedDataBa
54
54
  if (existing != null) {
55
55
  // 기존 리스너 키 초기화 + generation 증가로 이전 이벤트 무시
56
56
  if (existing.listenerKey != null) {
57
- const client = this._clientFactory.get(existing.info.serviceKey);
57
+ const client = this._sdServiceClientFactory.get(existing.info.serviceKey);
58
58
  void client.removeListener(existing.listenerKey);
59
59
  existing.listenerKey = undefined;
60
60
  }
@@ -109,7 +109,7 @@ export abstract class SdSharedDataProvider<T extends Record<string, SharedDataBa
109
109
  throw new Error(`등록되지 않은 공유 데이터: ${name as string}`);
110
110
  }
111
111
 
112
- const client = this._clientFactory.get(entry.info.serviceKey);
112
+ const client = this._sdServiceClientFactory.get(entry.info.serviceKey);
113
113
  const event = client.getEvent<typeof SdSharedDataChangeEvent>("SdSharedDataChange");
114
114
  await event.emit(
115
115
  (item) => item.name === (name as string) && obj.equal(item.filter, entry.info.filter),
@@ -137,7 +137,7 @@ export abstract class SdSharedDataProvider<T extends Record<string, SharedDataBa
137
137
 
138
138
  // 이벤트 리스너 등록
139
139
  if (entry.listenerKey == null) {
140
- const client = this._clientFactory.get(entry.info.serviceKey);
140
+ const client = this._sdServiceClientFactory.get(entry.info.serviceKey);
141
141
  const event = client.getEvent<typeof SdSharedDataChangeEvent>("SdSharedDataChange");
142
142
  entry.listenerKey = await event.addListener(
143
143
  { name, filter: entry.info.filter },
@@ -36,7 +36,7 @@ export class SdToastProvider {
36
36
  private readonly _appRef = inject(ApplicationRef);
37
37
  private readonly _envInjector = inject(EnvironmentInjector);
38
38
  private readonly _injector = inject(Injector);
39
- private readonly _systemLog = inject(SdSystemLogProvider);
39
+ private readonly _sdSystemLog = inject(SdSystemLogProvider);
40
40
 
41
41
  alertThemes = signal<SdToastSeverity[]>([]);
42
42
  overlap = signal(false);
@@ -145,11 +145,11 @@ export class SdToastProvider {
145
145
  });
146
146
  }
147
147
 
148
- async try<R>(fn: () => Promise<R>): Promise<R | undefined>;
149
- async try<R>(fn: () => Promise<R>, messageFn: (err: unknown) => string): Promise<R | undefined>;
148
+ async try<R>(fn: () => Promise<R>, messageFn?: (err: Error) => string): Promise<R | undefined>;
149
+ try<R>(fn: () => R, messageFn?: (err: Error) => string): R | undefined;
150
150
  async try<R>(
151
- fn: () => Promise<R>,
152
- messageFn?: (err: unknown) => string,
151
+ fn: () => Promise<R> | R,
152
+ messageFn?: (err: Error) => string,
153
153
  ): Promise<R | undefined> {
154
154
  try {
155
155
  return await fn();
@@ -160,7 +160,7 @@ export class SdToastProvider {
160
160
 
161
161
  const message = messageFn !== undefined ? messageFn(err) : err.message;
162
162
  this.danger(message);
163
- await this._systemLog.writeAsync("error", err.stack ?? err.message);
163
+ await this._sdSystemLog.writeAsync("error", err.stack ?? err.message);
164
164
  return undefined;
165
165
  }
166
166
  }
@@ -47,9 +47,9 @@ export abstract class SdDataDetailBase<T extends object, R = boolean>
47
47
 
48
48
  //-- implement
49
49
 
50
- private readonly _sdToast = inject(SdToastProvider);
51
- private readonly _sdSharedData = inject(SdSharedDataProvider);
52
- private readonly _errorHandler = inject(ErrorHandler);
50
+ protected readonly _sdToast = inject(SdToastProvider);
51
+ protected readonly _sdSharedData = inject(SdSharedDataProvider);
52
+ protected readonly _errorHandler = inject(ErrorHandler);
53
53
 
54
54
  viewType = injectViewTypeSignal(() => this);
55
55
 
@@ -32,7 +32,7 @@ export abstract class SdDataSelectButtonBase<
32
32
 
33
33
  //-- implement
34
34
 
35
- private readonly _sdModal = inject(SdModalProvider);
35
+ protected readonly _sdModal = inject(SdModalProvider);
36
36
 
37
37
  value = model<SelectModeValue<TKey>[TMode]>();
38
38
 
@@ -13,6 +13,7 @@ import {
13
13
  import { SdButton } from "../../controls/button/sd-button";
14
14
  import { SdForm } from "../../controls/form/sd-form";
15
15
  import { SdSheetColumn } from "../sheet/sd-sheet-column";
16
+ import { SdSheetColumnCellTemplate } from "../sheet/sd-sheet-column-cell-template";
16
17
  import { SdSheet } from "../sheet/sd-sheet";
17
18
  import { injectParent } from "../../core/injectParent";
18
19
  import { FormatPipe } from "../../core/format.pipe";
@@ -45,6 +46,7 @@ import { SdDataSheetBase } from "./sd-data-sheet.base";
45
46
  SdButton,
46
47
  SdSheet,
47
48
  SdSheetColumn,
49
+ SdSheetColumnCellTemplate,
48
50
  NgTemplateOutlet,
49
51
  SdBaseContainer,
50
52
  FormatPipe,
@@ -214,7 +216,7 @@ import { SdDataSheetBase } from "./sd-data-sheet.base";
214
216
  parent.itemPropInfo.isDeleted
215
217
  ) {
216
218
  <sd-sheet-column [fixed]="true" [key]="parent.itemPropInfo.isDeleted!">
217
- <ng-template #cellTpl let-item>
219
+ <ng-template [cell]="parent.items()" let-item="item">
218
220
  <div class="p-xs-sm tx-center">
219
221
  <sd-anchor
220
222
  [theme]="'danger'"
@@ -244,12 +246,19 @@ import { SdDataSheetBase } from "./sd-data-sheet.base";
244
246
  [key]="columnControl.key()"
245
247
  [fixed]="columnControl.fixed()"
246
248
  [header]="columnControl.header()"
249
+ [headerStyle]="columnControl.headerStyle()"
250
+ [tooltip]="columnControl.tooltip()"
247
251
  [width]="columnControl.width()"
248
252
  [disableSorting]="columnControl.disableSorting()"
249
253
  [disableResizing]="columnControl.disableResizing()"
250
254
  [hidden]="columnControl.hidden()"
251
255
  [collapse]="columnControl.collapse()"
252
256
  >
257
+ @if (columnControl.headerTplRef()) {
258
+ <ng-template #headerTpl>
259
+ <ng-template [ngTemplateOutlet]="columnControl.headerTplRef()!" />
260
+ </ng-template>
261
+ }
253
262
  @if (columnControl.summaryTplRef()) {
254
263
  <ng-template #summaryTpl>
255
264
  <ng-template [ngTemplateOutlet]="columnControl.summaryTplRef()!" />
@@ -257,8 +266,8 @@ import { SdDataSheetBase } from "./sd-data-sheet.base";
257
266
  }
258
267
 
259
268
  <ng-template
260
- #cellTpl
261
- let-item
269
+ [cell]="parent.items()"
270
+ let-item="item"
262
271
  let-index="index"
263
272
  let-depth="depth"
264
273
  let-edit="edit"
@@ -311,7 +320,7 @@ import { SdDataSheetBase } from "./sd-data-sheet.base";
311
320
  [key]="parent.itemPropInfo.lastModifiedAt!"
312
321
  [hidden]="true"
313
322
  >
314
- <ng-template #cellTpl let-item>
323
+ <ng-template [cell]="parent.items()" let-item="item">
315
324
  <div class="p-xs-sm tx-center">
316
325
  {{
317
326
  item[parent.itemPropInfo.lastModifiedAt!]
@@ -327,7 +336,7 @@ import { SdDataSheetBase } from "./sd-data-sheet.base";
327
336
  [key]="parent.itemPropInfo.lastModifiedBy!"
328
337
  [hidden]="true"
329
338
  >
330
- <ng-template #cellTpl let-item>
339
+ <ng-template [cell]="parent.items()" let-item="item">
331
340
  <div class="p-xs-sm tx-center">
332
341
  {{ item[parent.itemPropInfo.lastModifiedBy!] }}
333
342
  </div>
@@ -0,0 +1,17 @@
1
+ import { Directive, input } from "@angular/core";
2
+ import type { SdSheetCellContext } from "./sd-sheet-column";
3
+
4
+ @Directive({
5
+ selector: "ng-template[cell]",
6
+ standalone: true,
7
+ })
8
+ export class SdSheetColumnCellTemplate<T> {
9
+ cell = input.required<T[]>();
10
+
11
+ static ngTemplateContextGuard<TContextItem>(
12
+ _dir: SdSheetColumnCellTemplate<TContextItem>,
13
+ _ctx: unknown,
14
+ ): _ctx is SdSheetCellContext<TContextItem> {
15
+ return true;
16
+ }
17
+ }
@@ -5,10 +5,11 @@ import {
5
5
  input,
6
6
  TemplateRef,
7
7
  } from "@angular/core";
8
+ import { SdSheetColumnCellTemplate } from "./sd-sheet-column-cell-template";
8
9
 
9
- export interface SdSheetCellContext {
10
- $implicit: unknown;
11
- item: unknown;
10
+ export interface SdSheetCellContext<T = unknown> {
11
+ $implicit: T;
12
+ item: T;
12
13
  index: number;
13
14
  depth: number;
14
15
  edit: boolean;
@@ -18,9 +19,11 @@ export interface SdSheetCellContext {
18
19
  selector: "sd-sheet-column",
19
20
  standalone: true,
20
21
  })
21
- export class SdSheetColumn {
22
+ export class SdSheetColumn<T = unknown> {
22
23
  key = input.required<string>();
23
24
  header = input<string | string[]>("");
25
+ headerStyle = input<string>();
26
+ tooltip = input<string>();
24
27
  width = input<string>();
25
28
  fixed = input(false, { transform: booleanAttribute });
26
29
  hidden = input(false, { transform: booleanAttribute });
@@ -29,6 +32,10 @@ export class SdSheetColumn {
29
32
  disableResizing = input(false, { transform: booleanAttribute });
30
33
  ordering = input(0);
31
34
 
32
- cellTplRef = contentChild<TemplateRef<SdSheetCellContext>>("cellTpl");
35
+ cellTplRef = contentChild.required<any, TemplateRef<SdSheetCellContext<T>>>(
36
+ SdSheetColumnCellTemplate,
37
+ { read: TemplateRef },
38
+ );
39
+ headerTplRef = contentChild<TemplateRef<void>>("headerTpl");
33
40
  summaryTplRef = contentChild<TemplateRef<void>>("summaryTpl");
34
41
  }
@@ -99,10 +99,16 @@ import { SdSheetConfigModal } from "./sd-sheet-config.modal";
99
99
  [attr.colspan]="cell.colspan > 1 ? cell.colspan : null"
100
100
  [attr.rowspan]="cell.rowspan > 1 ? cell.rowspan : null"
101
101
  [attr.aria-sort]="getAriaSortValue(cell)"
102
+ [attr.title]="cell.colDef?.tooltip ?? cell.text"
103
+ [class.help]="cell.colDef?.tooltip"
102
104
  [style]="getHeaderCellStyle(cell)"
103
105
  (click)="onHeaderClick($event, cell)"
104
106
  >
105
- <span>{{ cell.text }}</span>
107
+ @if (cell.colDef && getColumnHeaderTpl(cell.colDef.key); as headerTpl) {
108
+ <ng-template [ngTemplateOutlet]="headerTpl" />
109
+ } @else {
110
+ <span>{{ cell.text }}</span>
111
+ }
106
112
  @if (cell.colDef && getSortDef(cell.colDef.key); as sortDef) {
107
113
  <ng-icon
108
114
  class="_sort-icon"
@@ -368,7 +374,7 @@ export class SdSheet<T> {
368
374
  columnControls = contentChildren(SdSheetColumn);
369
375
 
370
376
  // Injected providers
371
- private readonly _modalProvider = inject(SdModalProvider);
377
+ private readonly _sdModal = inject(SdModalProvider);
372
378
 
373
379
  // DOM accessor & cell agent
374
380
  domAccessor = injectSheetDomAccessor();
@@ -463,6 +469,11 @@ export class SdSheet<T> {
463
469
  return map;
464
470
  });
465
471
 
472
+ getColumnHeaderTpl(key: string) {
473
+ const col = this._columnControlMap().get(key);
474
+ return col?.headerTplRef() ?? null;
475
+ }
476
+
466
477
  getColumnCellTpl(key: string) {
467
478
  const col = this._columnControlMap().get(key);
468
479
  return col?.cellTplRef() ?? null;
@@ -564,7 +575,7 @@ export class SdSheet<T> {
564
575
  }
565
576
 
566
577
  async onConfigButtonClick(): Promise<void> {
567
- const result = await this._modalProvider.showAsync(
578
+ const result = await this._sdModal.showAsync(
568
579
  {
569
580
  title: "시트 설정",
570
581
  type: SdSheetConfigModal,
@@ -1,6 +1,8 @@
1
1
  export interface SdSheetColumnDef {
2
2
  key: string;
3
3
  header: string | string[];
4
+ headerStyle: string | undefined;
5
+ tooltip: string | undefined;
4
6
  width: string | undefined;
5
7
  fixed: boolean;
6
8
  hidden: boolean;
@@ -60,7 +60,11 @@ export function useSheetCellStyling<T>(options: {
60
60
 
61
61
  function getHeaderCellStyle(cell: SdSheetHeaderDef): string | null {
62
62
  if (cell.colDef == null) return null;
63
- return headerColumnStyles().get(cell.colDef.key) ?? null;
63
+ const parts: string[] = [];
64
+ const baseStyle = headerColumnStyles().get(cell.colDef.key);
65
+ if (baseStyle != null) parts.push(baseStyle);
66
+ if (cell.colDef.headerStyle != null) parts.push(cell.colDef.headerStyle);
67
+ return parts.length > 0 ? parts.join("; ") : null;
64
68
  }
65
69
 
66
70
  function getCellStyle(item: T, colDef: SdSheetColumnDef): string | null {
@@ -17,6 +17,8 @@ export function useSheetLayoutEngine(options: {
17
17
  return {
18
18
  key,
19
19
  header: col.header(),
20
+ headerStyle: col.headerStyle(),
21
+ tooltip: col.tooltip(),
20
22
  width: cfgCol?.width ?? col.width(),
21
23
  fixed: cfgCol?.fixed ?? col.fixed(),
22
24
  hidden: cfgCol?.hidden ?? col.hidden(),
package/src/index.ts CHANGED
@@ -224,7 +224,8 @@ export {
224
224
 
225
225
  // data/sheet
226
226
  export { SdSheet } from "./data/sheet/sd-sheet";
227
- export { SdSheetColumn } from "./data/sheet/sd-sheet-column";
227
+ export { SdSheetColumn, type SdSheetCellContext } from "./data/sheet/sd-sheet-column";
228
+ export { SdSheetColumnCellTemplate } from "./data/sheet/sd-sheet-column-cell-template";
228
229
  export { SdSheetConfigModal } from "./data/sheet/sd-sheet-config.modal";
229
230
  export type {
230
231
  SdSheetColumnDef,
@@ -82,7 +82,7 @@ import { tablerAlertTriangle } from "@ng-icons/tabler-icons";
82
82
  export class SdBaseContainer {
83
83
  private readonly _sdActivatedModal = inject(SdActivatedModalProvider, { optional: true });
84
84
  private readonly _sdAppStructure = inject(SdAppStructureProvider);
85
- private readonly _systemLog = inject(SdSystemLogProvider);
85
+ private readonly _sdSystemLog = inject(SdSystemLogProvider);
86
86
 
87
87
  private readonly _parent = injectParent();
88
88
 
@@ -107,7 +107,7 @@ export class SdBaseContainer {
107
107
  this._sdAppStructure.getTitleByFullCode(this._currPageCode?.() ?? this._fullPageCode())
108
108
  );
109
109
  } catch (err) {
110
- void this._systemLog.writeAsync("warn", `modalOrPageTitle 계산 중 에러: ${String(err)}`);
110
+ void this._sdSystemLog.writeAsync("warn", `modalOrPageTitle 계산 중 에러: ${String(err)}`);
111
111
  return "";
112
112
  }
113
113
  });