@simplysm/angular 14.0.37 → 14.0.39

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 (98) hide show
  1. package/dist/controls/checkbox/sd-checkbox.d.ts +1 -1
  2. package/dist/controls/checkbox/sd-checkbox.d.ts.map +1 -1
  3. package/dist/controls/checkbox/sd-checkbox.js +8 -5
  4. package/dist/controls/checkbox/sd-switch.d.ts +3 -1
  5. package/dist/controls/checkbox/sd-switch.d.ts.map +1 -1
  6. package/dist/controls/checkbox/sd-switch.js +11 -5
  7. package/dist/controls/dropdown/sd-dropdown.js +8 -8
  8. package/dist/controls/input/{sd-date-range.picker.d.ts → sd-date-range-picker.d.ts} +1 -1
  9. package/dist/controls/input/{sd-date-range.picker.d.ts.map → sd-date-range-picker.d.ts.map} +1 -1
  10. package/dist/controls/input/{sd-date-range.picker.js → sd-date-range-picker.js} +1 -1
  11. package/dist/controls/input/sd-textfield.d.ts.map +1 -1
  12. package/dist/controls/input/sd-textfield.js +1 -0
  13. package/dist/core/error-handler/sd-global-error-handler.plugin.d.ts.map +1 -1
  14. package/dist/core/error-handler/sd-global-error-handler.plugin.js +7 -2
  15. package/dist/core/modal/sd-activated-modal.provider.d.ts +1 -1
  16. package/dist/core/modal/sd-activated-modal.provider.d.ts.map +1 -1
  17. package/dist/core/modal/sd-activated-modal.provider.js +1 -1
  18. package/dist/core/modal/sd-modal.d.ts +2 -3
  19. package/dist/core/modal/sd-modal.d.ts.map +1 -1
  20. package/dist/core/modal/sd-modal.js +9 -10
  21. package/dist/core/modal/sd-modal.provider.d.ts +0 -1
  22. package/dist/core/modal/sd-modal.provider.d.ts.map +1 -1
  23. package/dist/core/modal/sd-modal.provider.js +14 -6
  24. package/dist/core/routing/sd-navigate-window.provider.d.ts.map +1 -1
  25. package/dist/core/routing/sd-navigate-window.provider.js +3 -1
  26. package/dist/core/routing/setupCanDeactivate.js +1 -1
  27. package/dist/core/selection/useSelectionManager.d.ts.map +1 -1
  28. package/dist/core/selection/useSelectionManager.js +4 -3
  29. package/dist/core/setupModelHook.d.ts.map +1 -1
  30. package/dist/core/setupModelHook.js +18 -1
  31. package/dist/core/shared-data/sd-shared-data.provider.d.ts.map +1 -1
  32. package/dist/core/shared-data/sd-shared-data.provider.js +12 -3
  33. package/dist/data/data-detail/sd-data-detail.base.d.ts +1 -1
  34. package/dist/data/data-detail/sd-data-detail.base.d.ts.map +1 -1
  35. package/dist/data/data-detail/sd-data-detail.base.js +24 -19
  36. package/dist/data/data-sheet/injectDataSheetExcelManager.js +1 -1
  37. package/dist/data/data-sheet/injectDataSheetModalEditManager.js +1 -1
  38. package/dist/data/data-sheet/sd-data-sheet.base.d.ts +1 -2
  39. package/dist/data/data-sheet/sd-data-sheet.base.d.ts.map +1 -1
  40. package/dist/data/data-sheet/sd-data-sheet.base.js +5 -13
  41. package/dist/data/getOrmDataEditToastErrorMessage.d.ts +2 -0
  42. package/dist/data/getOrmDataEditToastErrorMessage.d.ts.map +1 -0
  43. package/dist/data/getOrmDataEditToastErrorMessage.js +8 -0
  44. package/dist/data/permission-table/sd-permission-table.d.ts +1 -1
  45. package/dist/data/permission-table/sd-permission-table.d.ts.map +1 -1
  46. package/dist/data/permission-table/sd-permission-table.js +6 -5
  47. package/dist/data/shared-data/sd-shared-data-select.d.ts +1 -0
  48. package/dist/data/shared-data/sd-shared-data-select.d.ts.map +1 -1
  49. package/dist/data/shared-data/sd-shared-data-select.js +15 -7
  50. package/dist/data/sheet/injectSheetColumnResizing.d.ts.map +1 -1
  51. package/dist/data/sheet/injectSheetColumnResizing.js +1 -0
  52. package/dist/data/state-preset/sd-state-preset.d.ts.map +1 -1
  53. package/dist/data/state-preset/sd-state-preset.js +4 -0
  54. package/dist/features/address/sd-address-search.modal.d.ts +2 -1
  55. package/dist/features/address/sd-address-search.modal.d.ts.map +1 -1
  56. package/dist/features/address/sd-address-search.modal.js +69 -23
  57. package/dist/features/editor/sd-tiptap-editor.d.ts.map +1 -1
  58. package/dist/features/editor/sd-tiptap-editor.js +3 -1
  59. package/dist/features/visual/sd-barcode.d.ts.map +1 -1
  60. package/dist/features/visual/sd-barcode.js +10 -5
  61. package/dist/index.d.ts +2 -1
  62. package/dist/index.d.ts.map +1 -1
  63. package/dist/index.js +3 -1
  64. package/dist/layout/base-container/sd-base-container.d.ts +1 -0
  65. package/dist/layout/base-container/sd-base-container.d.ts.map +1 -1
  66. package/dist/layout/base-container/sd-base-container.js +5 -2
  67. package/dist/layout/dock/sd-dock.d.ts.map +1 -1
  68. package/dist/layout/dock/sd-dock.js +1 -0
  69. package/package.json +8 -7
  70. package/src/controls/checkbox/sd-checkbox.ts +7 -3
  71. package/src/controls/checkbox/sd-switch.ts +8 -2
  72. package/src/controls/dropdown/sd-dropdown.ts +8 -8
  73. package/src/controls/input/sd-textfield.ts +1 -0
  74. package/src/core/error-handler/sd-global-error-handler.plugin.ts +6 -2
  75. package/src/core/modal/sd-activated-modal.provider.ts +1 -1
  76. package/src/core/modal/sd-modal.provider.ts +13 -7
  77. package/src/core/modal/sd-modal.ts +6 -6
  78. package/src/core/routing/sd-navigate-window.provider.ts +3 -1
  79. package/src/core/routing/setupCanDeactivate.ts +1 -1
  80. package/src/core/selection/useSelectionManager.ts +5 -3
  81. package/src/core/setupModelHook.ts +21 -1
  82. package/src/core/shared-data/sd-shared-data.provider.ts +13 -2
  83. package/src/data/data-detail/sd-data-detail.base.ts +27 -23
  84. package/src/data/data-sheet/injectDataSheetExcelManager.ts +1 -1
  85. package/src/data/data-sheet/injectDataSheetModalEditManager.ts +1 -1
  86. package/src/data/data-sheet/sd-data-sheet.base.ts +5 -16
  87. package/src/data/getOrmDataEditToastErrorMessage.ts +10 -0
  88. package/src/data/permission-table/sd-permission-table.ts +7 -6
  89. package/src/data/shared-data/sd-shared-data-select.ts +24 -13
  90. package/src/data/sheet/injectSheetColumnResizing.ts +2 -0
  91. package/src/data/state-preset/sd-state-preset.ts +6 -0
  92. package/src/features/address/sd-address-search.modal.ts +75 -31
  93. package/src/features/editor/sd-tiptap-editor.ts +2 -0
  94. package/src/features/visual/sd-barcode.ts +9 -5
  95. package/src/index.ts +4 -1
  96. package/src/layout/base-container/sd-base-container.ts +4 -1
  97. package/src/layout/dock/sd-dock.ts +2 -0
  98. /package/src/controls/input/{sd-date-range.picker.ts → sd-date-range-picker.ts} +0 -0
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  Directive,
3
3
  effect,
4
+ ErrorHandler,
4
5
  inject,
5
6
  output,
6
7
  signal,
@@ -15,6 +16,7 @@ import { injectViewTypeSignal } from "../../core/routing/injectViewTypeSignal";
15
16
  import { withBusy } from "../../core/withBusy";
16
17
  import { type SdModalContentDef } from "../../core/modal/sd-modal.provider";
17
18
  import { SdToastProvider } from "../../core/toast/sd-toast.provider";
19
+ import { getOrmDataEditToastErrorMessage } from "../getOrmDataEditToastErrorMessage";
18
20
 
19
21
  export interface SdDataDetailDataInfo {
20
22
  isNew: boolean;
@@ -47,6 +49,7 @@ export abstract class SdDataDetailBase<T extends object, R = boolean>
47
49
 
48
50
  private readonly _sdToast = inject(SdToastProvider);
49
51
  private readonly _sdSharedData = inject(SdSharedDataProvider);
52
+ private readonly _errorHandler = inject(ErrorHandler);
50
53
 
51
54
  viewType = injectViewTypeSignal(() => this);
52
55
 
@@ -63,22 +66,33 @@ export abstract class SdDataDetailBase<T extends object, R = boolean>
63
66
  private _dataSnapshot?: T;
64
67
 
65
68
  constructor() {
66
- effect(() => {
69
+ effect((onCleanup) => {
67
70
  this.prepareRefreshEffect?.();
68
71
 
72
+ let cancelled = false;
73
+ onCleanup(() => {
74
+ cancelled = true;
75
+ });
76
+
69
77
  queueMicrotask(async () => {
70
- if (!this.canUse()) {
78
+ try {
79
+ if (cancelled) return;
80
+
81
+ if (!this.canUse()) {
82
+ this.initialized.set(true);
83
+ return;
84
+ }
85
+
86
+ await withBusy(this.busyCount, () =>
87
+ this._sdToast.try(async () => {
88
+ await this._sdSharedData.wait();
89
+ await this.refresh();
90
+ }),
91
+ );
71
92
  this.initialized.set(true);
72
- return;
93
+ } catch (err) {
94
+ this._errorHandler.handleError(err);
73
95
  }
74
-
75
- await withBusy(this.busyCount, () =>
76
- this._sdToast.try(async () => {
77
- await this._sdSharedData.wait();
78
- await this.refresh();
79
- }),
80
- );
81
- this.initialized.set(true);
82
96
  });
83
97
  });
84
98
 
@@ -130,7 +144,7 @@ export abstract class SdDataDetailBase<T extends object, R = boolean>
130
144
 
131
145
  this.close.emit(result);
132
146
  },
133
- (err) => this._getOrmDataEditToastErrorMessage(err),
147
+ (err) => getOrmDataEditToastErrorMessage(err),
134
148
  ),
135
149
  );
136
150
  }
@@ -165,19 +179,9 @@ export abstract class SdDataDetailBase<T extends object, R = boolean>
165
179
 
166
180
  await this.refresh();
167
181
  },
168
- (err) => this._getOrmDataEditToastErrorMessage(err),
182
+ (err) => getOrmDataEditToastErrorMessage(err),
169
183
  ),
170
184
  );
171
185
  }
172
186
 
173
- private _getOrmDataEditToastErrorMessage(err: unknown) {
174
- const message = err instanceof Error ? err.message : String(err);
175
- if (
176
- message.includes("a parent row: a foreign key constraint") ||
177
- message.includes("conflicted with the REFERENCE")
178
- ) {
179
- return "경고! 연결된 작업에 의한 처리 거부. 후속작업 확인 요망";
180
- }
181
- return message;
182
- }
183
187
  }
@@ -46,7 +46,7 @@ export function injectDataSheetExcelManager<TItem>(options: {
46
46
  async () => {
47
47
  await uploadExcelFn(file);
48
48
  await options.refresh();
49
- sdToast.success("엑셀 업로드가 완료 되었습니다.");
49
+ sdToast.success("엑셀 업로드가 완료되었습니다.");
50
50
  },
51
51
  (err) => options.errorMessageFn(err),
52
52
  ),
@@ -51,7 +51,7 @@ export function injectDataSheetModalEditManager<TItem, TKey>(options: {
51
51
  if (!result) return;
52
52
 
53
53
  await options.refresh();
54
- sdToast.success(`${del ? "삭제" : "복구"} 되었습니다.`);
54
+ sdToast.success(`${del ? "삭제" : "복구"}되었습니다.`);
55
55
  },
56
56
  (err) => options.errorMessageFn(err),
57
57
  ),
@@ -25,6 +25,7 @@ import { injectDataSheetRefreshManager } from "./injectDataSheetRefreshManager";
25
25
  import { injectDataSheetInlineEditManager } from "./injectDataSheetInlineEditManager";
26
26
  import { injectDataSheetModalEditManager } from "./injectDataSheetModalEditManager";
27
27
  import { injectDataSheetExcelManager } from "./injectDataSheetExcelManager";
28
+ import { getOrmDataEditToastErrorMessage } from "../getOrmDataEditToastErrorMessage";
28
29
 
29
30
  export type {
30
31
  SdDataSheetItemPropInfo,
@@ -84,7 +85,7 @@ export abstract class SdDataSheetBase<
84
85
  private readonly _excelMgr: ReturnType<typeof injectDataSheetExcelManager<TItem>>;
85
86
 
86
87
  //-- shared state (D1: class 소유)
87
- key = reflectComponentType(this.constructor as any)?.selector;
88
+ key = reflectComponentType(this.constructor as any)?.selector ?? this.constructor.name;
88
89
 
89
90
  viewType = injectViewTypeSignal(() => this);
90
91
 
@@ -197,7 +198,7 @@ export abstract class SdDataSheetBase<
197
198
  refresh: () => this._refreshMgr.refresh(),
198
199
  getNewItemFn: () => this.newItem?.bind(this),
199
200
  getSubmitFn: () => this.submit?.bind(this),
200
- errorMessageFn: (err) => this._getOrmDataEditToastErrorMessage(err),
201
+ errorMessageFn: (err) => getOrmDataEditToastErrorMessage(err),
201
202
  });
202
203
 
203
204
  //-- modal edit composable
@@ -210,7 +211,7 @@ export abstract class SdDataSheetBase<
210
211
  refresh: () => this._refreshMgr.refresh(),
211
212
  getEditItemFn: () => this.editItem?.bind(this),
212
213
  getToggleDeleteItemsFn: () => this.toggleDeleteItems?.bind(this),
213
- errorMessageFn: (err) => this._getOrmDataEditToastErrorMessage(err),
214
+ errorMessageFn: (err) => getOrmDataEditToastErrorMessage(err),
214
215
  });
215
216
 
216
217
  //-- excel composable
@@ -220,7 +221,7 @@ export abstract class SdDataSheetBase<
220
221
  refresh: () => this._refreshMgr.refresh(),
221
222
  getDownloadExcelFn: () => this.downloadExcel?.bind(this),
222
223
  getUploadExcelFn: () => this.uploadExcel?.bind(this),
223
- errorMessageFn: (err) => this._getOrmDataEditToastErrorMessage(err),
224
+ errorMessageFn: (err) => getOrmDataEditToastErrorMessage(err),
224
225
  });
225
226
 
226
227
  setupCanDeactivate(() => this.viewType() === "modal" || this.checkIgnoreChanges());
@@ -290,16 +291,4 @@ export abstract class SdDataSheetBase<
290
291
  await this._excelMgr.doUploadExcel();
291
292
  }
292
293
 
293
- //-- private
294
-
295
- private _getOrmDataEditToastErrorMessage(err: unknown) {
296
- const message = err instanceof Error ? err.message : String(err);
297
- if (
298
- message.includes("a parent row: a foreign key constraint") ||
299
- message.includes("conflicted with the REFERENCE")
300
- ) {
301
- return "경고! 연결된 작업에 의한 처리 거부. 후속작업 확인 요망";
302
- }
303
- return message;
304
- }
305
294
  }
@@ -0,0 +1,10 @@
1
+ export function getOrmDataEditToastErrorMessage(err: unknown): string {
2
+ const message = err instanceof Error ? err.message : String(err);
3
+ if (
4
+ message.includes("a parent row: a foreign key constraint") ||
5
+ message.includes("conflicted with the REFERENCE")
6
+ ) {
7
+ return "경고! 연결된 작업에 의한 처리 거부. 후속작업 확인 요망";
8
+ }
9
+ return message;
10
+ }
@@ -215,7 +215,7 @@ export class SdPermissionTable<TModule> {
215
215
  items = input<SdPermission<TModule>[]>([]);
216
216
  disabled = input(false, { transform: booleanAttribute });
217
217
 
218
- collapsedItems = signal(new Set<SdPermission<TModule>>());
218
+ collapsedItems = signal(new Set<string>());
219
219
 
220
220
  depthLength = computed(() => {
221
221
  return this._getDepthLength(this.items(), 0);
@@ -347,7 +347,7 @@ export class SdPermissionTable<TModule> {
347
347
  }
348
348
 
349
349
  getIsPermCollapsed(item: SdPermission<TModule>): boolean {
350
- return this.collapsedItems().has(item);
350
+ return this.collapsedItems().has(item.codeChain.join("."));
351
351
  }
352
352
 
353
353
  getAllChildren(item: SdPermission<TModule>): SdPermission<TModule>[] {
@@ -367,15 +367,16 @@ export class SdPermissionTable<TModule> {
367
367
  }
368
368
 
369
369
  onPermCollapseToggle(item: SdPermission<TModule>) {
370
+ const key = item.codeChain.join(".");
370
371
  this.collapsedItems.update((v) => {
371
372
  const r = new Set(v);
372
- if (r.has(item)) {
373
- r.delete(item);
373
+ if (r.has(key)) {
374
+ r.delete(key);
374
375
  } else {
375
- r.add(item);
376
+ r.add(key);
376
377
  const allChildren = this.getAllChildren(item);
377
378
  for (const allChild of allChildren) {
378
- r.add(allChild);
379
+ r.add(allChild.codeChain.join("."));
379
380
  }
380
381
  }
381
382
  return r;
@@ -321,23 +321,34 @@ export class SdSharedDataSelect<
321
321
  return this._searchTextMatchCache().get(item.__valueKey) ?? false;
322
322
  }
323
323
 
324
- getChildren = (item: SharedDataBase<string | number>): TItem[] => {
325
- let result =
326
- this.itemByParentKeyMap()?.get(item.__valueKey as TItem["__valueKey"]) ??
327
- [];
324
+ private readonly _sortedChildrenMap = computed(() => {
325
+ const parentMap = this.itemByParentKeyMap();
326
+ if (parentMap == null) return undefined;
328
327
 
329
328
  const orderProp = this.displayOrderKeyProp();
330
- if (orderProp != null) {
331
- result = [...result].sort((a, b) => {
332
- const aVal = (a as any)[orderProp];
333
- const bVal = (b as any)[orderProp];
334
- if (aVal < bVal) return -1;
335
- if (aVal > bVal) return 1;
336
- return 0;
337
- });
329
+ if (orderProp == null) return parentMap;
330
+
331
+ const sorted = new Map<TItem["__valueKey"] | undefined, TItem[]>();
332
+ for (const [key, children] of parentMap) {
333
+ sorted.set(
334
+ key,
335
+ [...children].sort((a, b) => {
336
+ const aVal = (a as any)[orderProp];
337
+ const bVal = (b as any)[orderProp];
338
+ if (aVal < bVal) return -1;
339
+ if (aVal > bVal) return 1;
340
+ return 0;
341
+ }),
342
+ );
338
343
  }
344
+ return sorted;
345
+ });
339
346
 
340
- return result;
347
+ getChildren = (item: SharedDataBase<string | number>): TItem[] => {
348
+ return (
349
+ this._sortedChildrenMap()?.get(item.__valueKey as TItem["__valueKey"]) ??
350
+ []
351
+ );
341
352
  };
342
353
 
343
354
  onUndefinedClick(): void {
@@ -31,6 +31,8 @@ export function injectSheetColumnResizing(options: {
31
31
  }
32
32
 
33
33
  function onMousedown(event: MouseEvent, colDef: SdSheetColumnDef): void {
34
+ resizingCleanup?.();
35
+
34
36
  event.preventDefault();
35
37
  event.stopPropagation();
36
38
 
@@ -130,6 +130,12 @@ export class SdStatePreset {
130
130
  if (name == null) return;
131
131
 
132
132
  const currentPresets = this._presets();
133
+
134
+ if (currentPresets.some((p) => p.name === name)) {
135
+ this._sdToast.warning("이미 존재하는 프리셋 이름입니다.");
136
+ return;
137
+ }
138
+
133
139
  const newPreset: SdStatePresetDef = {
134
140
  name,
135
141
  state: obj.clone(this.state()),
@@ -11,17 +11,19 @@ import type { OnInit } from "@angular/core";
11
11
  import { SdBusyContainer } from "../../core/busy/sd-busy-container";
12
12
  import type { SdModalContentDef } from "../../core/modal/sd-modal.provider";
13
13
 
14
- declare const daum: {
15
- postcode: {
16
- load(callback: () => void): void;
17
- };
18
- Postcode: new (options: {
19
- oncomplete: (data: DaumPostcodeData) => void;
20
- onresize: (size: { height: number }) => void;
21
- width: string;
22
- height: string;
23
- }) => { embed(el: HTMLElement, options: { autoClose: boolean }): void };
24
- };
14
+ declare const daum:
15
+ | {
16
+ postcode: {
17
+ load(callback: () => void): void;
18
+ };
19
+ Postcode: new (options: {
20
+ oncomplete: (data: DaumPostcodeData) => void;
21
+ onresize: (size: { height: number }) => void;
22
+ width: string;
23
+ height: string;
24
+ }) => { embed(el: HTMLElement, options: { autoClose: boolean }): void };
25
+ }
26
+ | undefined;
25
27
 
26
28
  interface DaumPostcodeData {
27
29
  zonecode: string;
@@ -39,6 +41,51 @@ export interface Address {
39
41
  buildingName: string | undefined;
40
42
  }
41
43
 
44
+ function loadDaumPostcodeScript(): Promise<void> {
45
+ const existing = document.getElementById("daum_address");
46
+ if (existing != null) {
47
+ if (typeof daum !== "undefined") {
48
+ return Promise.resolve();
49
+ }
50
+ return new Promise<void>((resolve, reject) => {
51
+ existing.addEventListener(
52
+ "load",
53
+ () => {
54
+ daum!.postcode.load(() => {
55
+ resolve();
56
+ });
57
+ },
58
+ { once: true },
59
+ );
60
+ existing.addEventListener(
61
+ "error",
62
+ () => {
63
+ existing.remove();
64
+ reject(new Error("주소 검색 스크립트를 불러올 수 없습니다."));
65
+ },
66
+ { once: true },
67
+ );
68
+ });
69
+ }
70
+
71
+ return new Promise<void>((resolve, reject) => {
72
+ const scriptEl = document.createElement("script");
73
+ scriptEl.src = "//t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js";
74
+ scriptEl.setAttribute("id", "daum_address");
75
+
76
+ scriptEl.onload = (): void => {
77
+ daum!.postcode.load(() => {
78
+ resolve();
79
+ });
80
+ };
81
+ scriptEl.onerror = (): void => {
82
+ scriptEl.remove();
83
+ reject(new Error("주소 검색 스크립트를 불러올 수 없습니다."));
84
+ };
85
+ document.head.appendChild(scriptEl);
86
+ });
87
+ }
88
+
42
89
  @Component({
43
90
  selector: "sd-address-search-modal",
44
91
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -47,45 +94,42 @@ export interface Address {
47
94
  imports: [SdBusyContainer],
48
95
  template: `
49
96
  <sd-busy-container [busy]="!initialized()">
50
- <div #content style="min-height: 100px;"></div>
97
+ @if (errorMessage() !== null) {
98
+ <div class="_error">{{ errorMessage() }}</div>
99
+ } @else {
100
+ <div #content style="min-height: 100px;"></div>
101
+ }
51
102
  </sd-busy-container>
52
103
  `,
53
104
  })
54
105
  export class SdAddressSearchModal implements SdModalContentDef<Address>, OnInit {
55
- contentElRef = viewChild.required<"content", ElementRef<HTMLElement>>("content", {
106
+ contentElRef = viewChild<"content", ElementRef<HTMLElement>>("content", {
56
107
  read: ElementRef,
57
108
  });
58
109
 
59
110
  close = output<Address>();
60
111
 
61
112
  initialized = signal(false);
113
+ errorMessage = signal<string | null>(null);
62
114
 
63
115
  ngOnInit() {
64
116
  void this._initAsync();
65
117
  }
66
118
 
67
119
  private async _initAsync() {
68
- if (!document.getElementById("daum_address")) {
69
- await new Promise<void>((resolve, reject) => {
70
- const scriptEl = document.createElement("script");
71
- scriptEl.src = "//t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js";
72
- scriptEl.setAttribute("id", "daum_address");
73
-
74
- scriptEl.onload = (): void => {
75
- daum.postcode.load(() => {
76
- resolve();
77
- });
78
- };
79
- scriptEl.onerror = (): void => {
80
- reject(new Error("주소 검색 스크립트를 불러올 수 없습니다."));
81
- };
82
- document.head.appendChild(scriptEl);
83
- });
120
+ try {
121
+ await loadDaumPostcodeScript();
122
+ } catch (err) {
123
+ this.errorMessage.set(err instanceof Error ? err.message : String(err));
124
+ this.initialized.set(true);
125
+ return;
84
126
  }
85
127
 
86
- const contentEl = this.contentElRef().nativeElement;
128
+ const contentElRef = this.contentElRef();
129
+ if (contentElRef == null) return;
130
+ const contentEl = contentElRef.nativeElement;
87
131
 
88
- new daum.Postcode({
132
+ new daum!.Postcode({
89
133
  oncomplete: (data: DaumPostcodeData): void => {
90
134
  const addr = data.userSelectedType === "R" ? data.roadAddress : data.jibunAddress;
91
135
 
@@ -22,6 +22,7 @@ import Color from "@tiptap/extension-color";
22
22
  import Highlight from "@tiptap/extension-highlight";
23
23
  import TextAlign from "@tiptap/extension-text-align";
24
24
  import Image from "@tiptap/extension-image";
25
+ import Underline from "@tiptap/extension-underline";
25
26
  import Placeholder from "@tiptap/extension-placeholder";
26
27
  import { useTiptapToolbar } from "./useTiptapToolbar";
27
28
 
@@ -32,6 +33,7 @@ const DEFAULT_EXTENSIONS: AnyExtension[] = [
32
33
  Highlight.configure({ multicolor: true }),
33
34
  TextAlign.configure({ types: ["heading", "paragraph"] }),
34
35
  Image.configure({ inline: false, allowBase64: true }),
36
+ Underline,
35
37
  ];
36
38
 
37
39
  @Component({
@@ -32,11 +32,15 @@ export class SdBarcode {
32
32
  if (text == null || text === "") {
33
33
  return "";
34
34
  }
35
- const svg = bwipjs.toSVG({
36
- bcid: this.type(),
37
- text,
38
- });
39
- return this._sanitizer.bypassSecurityTrustHtml(svg);
35
+ try {
36
+ const svg = bwipjs.toSVG({
37
+ bcid: this.type(),
38
+ text,
39
+ });
40
+ return this._sanitizer.bypassSecurityTrustHtml(svg);
41
+ } catch {
42
+ return "";
43
+ }
40
44
  });
41
45
  }
42
46
 
package/src/index.ts CHANGED
@@ -159,7 +159,7 @@ export { type SdTextfieldTypes, sdTextfieldTypes } from "./controls/input/sd-tex
159
159
  export { SdTextarea } from "./controls/input/sd-textarea";
160
160
  export { SdNumpad } from "./controls/input/sd-numpad";
161
161
  export { SdRange } from "./controls/input/sd-range";
162
- export { SdDateRangePicker } from "./controls/input/sd-date-range.picker";
162
+ export { SdDateRangePicker } from "./controls/input/sd-date-range-picker";
163
163
 
164
164
  // controls/checkbox
165
165
  export { SdCheckbox } from "./controls/checkbox/sd-checkbox";
@@ -252,6 +252,9 @@ export {
252
252
  type SdDataDetailDataInfo,
253
253
  } from "./data/data-detail/sd-data-detail.base";
254
254
 
255
+ // data/utils
256
+ export { getOrmDataEditToastErrorMessage } from "./data/getOrmDataEditToastErrorMessage";
257
+
255
258
  // data/data-select-button
256
259
  export { SdDataSelectButton } from "./data/data-select-button/sd-data-select-button";
257
260
  export { SdDataSelectButtonBase } from "./data/data-select-button/sd-data-select-button.base";
@@ -22,6 +22,7 @@ import {
22
22
  type SdViewType,
23
23
  } from "../../core/routing/injectViewTypeSignal";
24
24
  import { injectParent } from "../../core/injectParent";
25
+ import { SdSystemLogProvider } from "../../core/config/sd-system-log.provider";
25
26
  import { NgIcon } from "@ng-icons/core";
26
27
  import { tablerAlertTriangle } from "@ng-icons/tabler-icons";
27
28
 
@@ -81,6 +82,7 @@ import { tablerAlertTriangle } from "@ng-icons/tabler-icons";
81
82
  export class SdBaseContainer {
82
83
  private readonly _sdActivatedModal = inject(SdActivatedModalProvider, { optional: true });
83
84
  private readonly _sdAppStructure = inject(SdAppStructureProvider);
85
+ private readonly _systemLog = inject(SdSystemLogProvider);
84
86
 
85
87
  private readonly _parent = injectParent();
86
88
 
@@ -104,7 +106,8 @@ export class SdBaseContainer {
104
106
  this._sdActivatedModal?.modalComponent()?.title() ??
105
107
  this._sdAppStructure.getTitleByFullCode(this._currPageCode?.() ?? this._fullPageCode())
106
108
  );
107
- } catch {
109
+ } catch (err) {
110
+ void this._systemLog.writeAsync("warn", `modalOrPageTitle 계산 중 에러: ${String(err)}`);
108
111
  return "";
109
112
  }
110
113
  });
@@ -132,6 +132,8 @@ export class SdDock {
132
132
  }
133
133
 
134
134
  onResizeBarMousedown(event: MouseEvent) {
135
+ this._dragCleanup?.();
136
+
135
137
  const thisEl = this._elRef.nativeElement;
136
138
 
137
139
  const startX = event.clientX;