@simplysm/angular 14.0.7 → 14.0.8

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 (51) hide show
  1. package/dist/core/utils/injectParent.d.ts +7 -0
  2. package/dist/core/utils/injectParent.d.ts.map +1 -0
  3. package/dist/core/utils/injectParent.js +26 -0
  4. package/dist/features/base/sd-base-container.control.d.ts +26 -0
  5. package/dist/features/base/sd-base-container.control.d.ts.map +1 -0
  6. package/dist/features/base/sd-base-container.control.js +183 -0
  7. package/dist/index.d.ts +2 -0
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +3 -0
  10. package/dist/ui/data/list/sd-list-item.control.js +1 -1
  11. package/dist/ui/data/sheet/sd-sheet.control.d.ts +2 -1
  12. package/dist/ui/data/sheet/sd-sheet.control.d.ts.map +1 -1
  13. package/dist/ui/data/sheet/sd-sheet.control.js +16 -13
  14. package/dist/ui/data/sheet/useSheetCellAgent.d.ts.map +1 -1
  15. package/dist/ui/data/sheet/useSheetCellAgent.js +1 -2
  16. package/dist/ui/data/sheet/useSheetLayoutEngine.d.ts.map +1 -1
  17. package/dist/ui/data/sheet/useSheetLayoutEngine.js +1 -0
  18. package/dist/ui/layout/dock/sd-dock.control.d.ts +2 -0
  19. package/dist/ui/layout/dock/sd-dock.control.d.ts.map +1 -1
  20. package/dist/ui/layout/dock/sd-dock.control.js +13 -4
  21. package/dist/ui/layout/kanban/sd-kanban-lane.control.d.ts.map +1 -1
  22. package/dist/ui/layout/kanban/sd-kanban-lane.control.js +7 -1
  23. package/dist/ui/layout/sd-gap.control.d.ts +2 -2
  24. package/dist/ui/navigation/pagination/sd-pagination.control.d.ts.map +1 -1
  25. package/dist/ui/navigation/pagination/sd-pagination.control.js +7 -3
  26. package/dist/ui/navigation/sidebar/sd-sidebar-container.control.js +1 -1
  27. package/dist/ui/overlay/dropdown/sd-dropdown-popup.control.js +1 -1
  28. package/dist/ui/overlay/modal/sd-modal.control.d.ts +1 -0
  29. package/dist/ui/overlay/modal/sd-modal.control.d.ts.map +1 -1
  30. package/dist/ui/overlay/modal/sd-modal.control.js +10 -2
  31. package/dist/ui/overlay/toast/sd-toast.provider.d.ts.map +1 -1
  32. package/dist/ui/overlay/toast/sd-toast.provider.js +7 -3
  33. package/dist/ui/visual/sd-progress.control.d.ts +1 -0
  34. package/dist/ui/visual/sd-progress.control.d.ts.map +1 -1
  35. package/dist/ui/visual/sd-progress.control.js +5 -4
  36. package/package.json +5 -5
  37. package/src/core/utils/injectParent.ts +41 -0
  38. package/src/features/base/sd-base-container.control.ts +113 -0
  39. package/src/index.ts +4 -0
  40. package/src/ui/data/list/sd-list-item.control.ts +1 -1
  41. package/src/ui/data/sheet/sd-sheet.control.ts +17 -13
  42. package/src/ui/data/sheet/useSheetCellAgent.ts +1 -2
  43. package/src/ui/data/sheet/useSheetLayoutEngine.ts +1 -0
  44. package/src/ui/layout/dock/sd-dock.control.ts +15 -2
  45. package/src/ui/layout/kanban/sd-kanban-lane.control.ts +6 -3
  46. package/src/ui/navigation/pagination/sd-pagination.control.ts +5 -3
  47. package/src/ui/navigation/sidebar/sd-sidebar-container.control.ts +1 -1
  48. package/src/ui/overlay/dropdown/sd-dropdown-popup.control.ts +1 -1
  49. package/src/ui/overlay/modal/sd-modal.control.ts +9 -0
  50. package/src/ui/overlay/toast/sd-toast.provider.ts +9 -3
  51. package/src/ui/visual/sd-progress.control.ts +4 -1
@@ -22,7 +22,7 @@ export class SdPaginationControl {
22
22
  totalPageCount = input(0, { ...(ngDevMode ? { debugName: "totalPageCount" } : /* istanbul ignore next */ {}), transform: numberAttribute });
23
23
  visiblePageCount = input(10, { ...(ngDevMode ? { debugName: "visiblePageCount" } : /* istanbul ignore next */ {}), transform: numberAttribute });
24
24
  groupIndex = computed(() => {
25
- return Math.floor(this.currentPage() / this.visiblePageCount());
25
+ return Math.floor(this.currentPage() / Math.max(this.visiblePageCount(), 1));
26
26
  }, ...(ngDevMode ? [{ debugName: "groupIndex" }] : /* istanbul ignore next */ []));
27
27
  hasPrev = computed(() => {
28
28
  if (this.totalPageCount() === 0) {
@@ -35,7 +35,7 @@ export class SdPaginationControl {
35
35
  if (totalPageCount === 0) {
36
36
  return false;
37
37
  }
38
- const lastGroupIndex = Math.floor((totalPageCount - 1) / this.visiblePageCount());
38
+ const lastGroupIndex = Math.floor((totalPageCount - 1) / Math.max(this.visiblePageCount(), 1));
39
39
  return this.groupIndex() < lastGroupIndex;
40
40
  }, ...(ngDevMode ? [{ debugName: "hasNext" }] : /* istanbul ignore next */ []));
41
41
  displayPages = computed(() => {
@@ -59,12 +59,16 @@ export class SdPaginationControl {
59
59
  this.currentPage.set((this.groupIndex() + 1) * this.visiblePageCount());
60
60
  }
61
61
  goToPrevGroup() {
62
- this.currentPage.set((this.groupIndex() - 1) * this.visiblePageCount());
62
+ if (!this.hasPrev())
63
+ return;
64
+ this.currentPage.set((this.groupIndex() - 1) * Math.max(this.visiblePageCount(), 1));
63
65
  }
64
66
  goToFirst() {
65
67
  this.currentPage.set(0);
66
68
  }
67
69
  goToLast() {
70
+ if (this.totalPageCount() === 0)
71
+ return;
68
72
  this.currentPage.set(this.totalPageCount() - 1);
69
73
  }
70
74
  static ɵfac = function SdPaginationControl_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || SdPaginationControl)(); };
@@ -21,7 +21,7 @@ export class SdSidebarContainerControl {
21
21
  }
22
22
  }
23
23
  onBackdropClick() {
24
- this.toggle.update((v) => !v);
24
+ this.toggle.set(false);
25
25
  }
26
26
  static ɵfac = function SdSidebarContainerControl_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || SdSidebarContainerControl)(); };
27
27
  static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: SdSidebarContainerControl, selectors: [["sd-sidebar-container"]], hostVars: 1, hostBindings: function SdSidebarContainerControl_HostBindings(rf, ctx) { if (rf & 2) {
@@ -13,7 +13,7 @@ export class SdDropdownPopupControl {
13
13
  const el = this._elRef.nativeElement;
14
14
  const divEl = el.firstElementChild;
15
15
  if (divEl.clientHeight > 300) {
16
- el.style.height = "18.75rem";
16
+ el.style.height = "300px";
17
17
  }
18
18
  else {
19
19
  el.style.height = "";
@@ -5,6 +5,7 @@ export declare class SdModalControl {
5
5
  private readonly _elRef;
6
6
  private readonly _activatedModal;
7
7
  private readonly _systemConfig;
8
+ private readonly _destroyRef;
8
9
  open: import("@angular/core").ModelSignal<boolean>;
9
10
  key: import("@angular/core").InputSignal<string | undefined>;
10
11
  title: import("@angular/core").InputSignal<string>;
@@ -1 +1 @@
1
- {"version":3,"file":"sd-modal.control.d.ts","sourceRoot":"","sources":["..\\..\\..\\..\\src\\ui\\overlay\\modal\\sd-modal.control.ts"],"names":[],"mappings":"AAAA,OAAO,EASL,KAAK,WAAW,EAEjB,MAAM,eAAe,CAAC;AAKvB,OAAO,wBAAwB,CAAC;;AAEhC,qBA0Ca,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAmC;IAC1D,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAwD;IACxF,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAsD;IAEpF,IAAI,+CAAgB;IACpB,GAAG,0DAAwC;IAC3C,KAAK,8CAAa;IAClB,UAAU,+CAAgB;IAC1B,eAAe,+CAAgB;IAC/B,kBAAkB,+CAAe;IACjC,mBAAmB,+CAAe;IAClC,KAAK,+CAAgB;IACrB,IAAI,+CAAgB;IACpB,SAAS,+CAAgB;IACzB,OAAO,+CAAgB;IACvB,QAAQ,gFAA8D;IACtE,WAAW,0DAAwC;IACnD,UAAU,0DAAwC;IAClD,QAAQ,0DAAwC;IAChD,OAAO,0DAAwC;IAC/C,WAAW,0DAAwC;IACnD,sBAAsB,+CAAgB;IACtC,YAAY,oEAAkD;IAE9D,YAAY,iDAAkB;IAE9B,OAAO,CAAC,UAAU,CAEJ;IAEd,OAAO,CAAC,YAAY,CAUN;IAEd,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAA8B;IACnE,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAa;;IA+ChD,iBAAiB,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAkBvD,iBAAiB,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAkB1C,eAAe,IAAI,IAAI;IAKvB,kBAAkB,IAAI,IAAI;IAI1B,eAAe,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI;IAS3C,aAAa,IAAI,IAAI;IAIrB,OAAO,CAAC,aAAa;IAQrB,OAAO,CAAC,cAAc;IAuBtB,OAAO,CAAC,oBAAoB;IAa5B,OAAO,CAAC,aAAa;IAcrB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,YAAY;IA4CpB,OAAO,CAAC,UAAU;YAcJ,WAAW;YAgBX,cAAc;yCA7RjB,cAAc;2CAAd,cAAc;CA6S1B"}
1
+ {"version":3,"file":"sd-modal.control.d.ts","sourceRoot":"","sources":["..\\..\\..\\..\\src\\ui\\overlay\\modal\\sd-modal.control.ts"],"names":[],"mappings":"AAAA,OAAO,EAUL,KAAK,WAAW,EAEjB,MAAM,eAAe,CAAC;AAKvB,OAAO,wBAAwB,CAAC;;AAEhC,qBA0Ca,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAmC;IAC1D,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAwD;IACxF,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAsD;IACpF,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAsB;IAElD,IAAI,+CAAgB;IACpB,GAAG,0DAAwC;IAC3C,KAAK,8CAAa;IAClB,UAAU,+CAAgB;IAC1B,eAAe,+CAAgB;IAC/B,kBAAkB,+CAAe;IACjC,mBAAmB,+CAAe;IAClC,KAAK,+CAAgB;IACrB,IAAI,+CAAgB;IACpB,SAAS,+CAAgB;IACzB,OAAO,+CAAgB;IACvB,QAAQ,gFAA8D;IACtE,WAAW,0DAAwC;IACnD,UAAU,0DAAwC;IAClD,QAAQ,0DAAwC;IAChD,OAAO,0DAAwC;IAC/C,WAAW,0DAAwC;IACnD,sBAAsB,+CAAgB;IACtC,YAAY,oEAAkD;IAE9D,YAAY,iDAAkB;IAE9B,OAAO,CAAC,UAAU,CAEJ;IAEd,OAAO,CAAC,YAAY,CAUN;IAEd,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAA8B;IACnE,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAa;;IAoDhD,iBAAiB,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAkBvD,iBAAiB,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAkB1C,eAAe,IAAI,IAAI;IAKvB,kBAAkB,IAAI,IAAI;IAI1B,eAAe,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI;IAS3C,aAAa,IAAI,IAAI;IAIrB,OAAO,CAAC,aAAa;IAQrB,OAAO,CAAC,cAAc;IAuBtB,OAAO,CAAC,oBAAoB;IAa5B,OAAO,CAAC,aAAa;IAgBrB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,YAAY;IA4CpB,OAAO,CAAC,UAAU;YAcJ,WAAW;YAgBX,cAAc;yCArSjB,cAAc;2CAAd,cAAc;CAqT1B"}
@@ -1,4 +1,4 @@
1
- import { ChangeDetectionStrategy, Component, effect, ElementRef, inject, input, model, output, ViewEncapsulation, } from "@angular/core";
1
+ import { ChangeDetectionStrategy, Component, DestroyRef, effect, ElementRef, inject, input, model, output, ViewEncapsulation, } from "@angular/core";
2
2
  import { isTabbable } from "tabbable";
3
3
  import { NgTemplateOutlet } from "@angular/common";
4
4
  import { SdActivatedModalProvider } from "./sd-modal.provider";
@@ -72,6 +72,7 @@ export class SdModalControl {
72
72
  _elRef = inject((ElementRef));
73
73
  _activatedModal = inject(SdActivatedModalProvider, { optional: true });
74
74
  _systemConfig = inject(SdSystemConfigProvider, { optional: true });
75
+ _destroyRef = inject(DestroyRef);
75
76
  open = model(false, ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
76
77
  key = input(undefined, ...(ngDevMode ? [{ debugName: "key" }] : /* istanbul ignore next */ []));
77
78
  title = input("", ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
@@ -137,6 +138,10 @@ export class SdModalControl {
137
138
  void this._saveConfig();
138
139
  }
139
140
  };
141
+ this._destroyRef.onDestroy(() => {
142
+ document.removeEventListener("mousemove", this._onDocumentMouseMove);
143
+ document.removeEventListener("mouseup", this._onDocumentMouseUp);
144
+ });
140
145
  }
141
146
  onResizeMouseDown(event, dir) {
142
147
  event.preventDefault();
@@ -247,6 +252,9 @@ export class SdModalControl {
247
252
  maxZ = z;
248
253
  }
249
254
  }
255
+ const currentZ = parseInt(hostEl.style.zIndex, 10);
256
+ if (currentZ >= maxZ)
257
+ return;
250
258
  hostEl.style.zIndex = String(maxZ + 1);
251
259
  }
252
260
  _getDialogEl() {
@@ -408,4 +416,4 @@ export class SdModalControl {
408
416
  `,
409
417
  }]
410
418
  }], () => [], { open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }, { type: i0.Output, args: ["openChange"] }], key: [{ type: i0.Input, args: [{ isSignal: true, alias: "key", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], hideHeader: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideHeader", required: false }] }], hideCloseButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideCloseButton", required: false }] }], useCloseByBackdrop: [{ type: i0.Input, args: [{ isSignal: true, alias: "useCloseByBackdrop", required: false }] }], useCloseByEscapeKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "useCloseByEscapeKey", required: false }] }], float: [{ type: i0.Input, args: [{ isSignal: true, alias: "float", required: false }] }], fill: [{ type: i0.Input, args: [{ isSignal: true, alias: "fill", required: false }] }], resizable: [{ type: i0.Input, args: [{ isSignal: true, alias: "resizable", required: false }] }], movable: [{ type: i0.Input, args: [{ isSignal: true, alias: "movable", required: false }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }], minHeightPx: [{ type: i0.Input, args: [{ isSignal: true, alias: "minHeightPx", required: false }] }], minWidthPx: [{ type: i0.Input, args: [{ isSignal: true, alias: "minWidthPx", required: false }] }], heightPx: [{ type: i0.Input, args: [{ isSignal: true, alias: "heightPx", required: false }] }], widthPx: [{ type: i0.Input, args: [{ isSignal: true, alias: "widthPx", required: false }] }], headerStyle: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerStyle", required: false }] }], noFirstControlFocusing: [{ type: i0.Input, args: [{ isSignal: true, alias: "noFirstControlFocusing", required: false }] }], actionTplRef: [{ type: i0.Input, args: [{ isSignal: true, alias: "actionTplRef", required: false }] }], closeRequest: [{ type: i0.Output, args: ["closeRequest"] }] }); })();
411
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(SdModalControl, { className: "SdModalControl", filePath: "packages/angular/src/ui/overlay/modal/sd-modal.control.ts", lineNumber: 61 }); })();
419
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(SdModalControl, { className: "SdModalControl", filePath: "packages/angular/src/ui/overlay/modal/sd-modal.control.ts", lineNumber: 62 }); })();
@@ -1 +1 @@
1
- {"version":3,"file":"sd-toast.provider.d.ts","sourceRoot":"","sources":["..\\..\\..\\..\\src\\ui\\overlay\\toast\\sd-toast.provider.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sd-toast.provider.d.ts","sourceRoot":"","sources":["..\\..\\..\\..\\src\\ui\\overlay\\toast\\sd-toast.provider.ts"],"names":[],"mappings":"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"}
@@ -129,12 +129,13 @@ export class SdToastProvider {
129
129
  if (useProgress) {
130
130
  // progress 모드: effect로 100% 감지 후 1초 뒤 자동 해제
131
131
  const progressSignal = toastRef.instance.progress;
132
- effect(() => {
132
+ effect((onCleanup) => {
133
133
  const val = progressSignal();
134
134
  if (val >= 100) {
135
- setTimeout(() => {
135
+ const timerId = setTimeout(() => {
136
136
  this._dismissToast(toastRef);
137
137
  }, 1000);
138
+ onCleanup(() => clearTimeout(timerId));
138
139
  }
139
140
  }, { injector: toastRef.injector });
140
141
  return progressSignal;
@@ -176,13 +177,16 @@ export class SdToastProvider {
176
177
  _dismissToast(toastRef) {
177
178
  toastRef.instance.open.set(false);
178
179
  const el = toastRef.location.nativeElement;
180
+ let fallbackId;
179
181
  const onTransitionEnd = () => {
182
+ clearTimeout(fallbackId);
180
183
  el.removeEventListener("transitionend", onTransitionEnd);
181
184
  this._destroyToast(toastRef);
182
185
  };
183
186
  el.addEventListener("transitionend", onTransitionEnd);
184
187
  // fallback: transition이 없는 경우 300ms 후 파괴
185
- setTimeout(() => {
188
+ fallbackId = setTimeout(() => {
189
+ el.removeEventListener("transitionend", onTransitionEnd);
186
190
  this._destroyToast(toastRef);
187
191
  }, 300);
188
192
  }
@@ -4,6 +4,7 @@ export declare class SdProgressControl {
4
4
  size: import("@angular/core").InputSignal<"sm" | "lg" | undefined>;
5
5
  theme: import("@angular/core").InputSignal<"info" | "success" | "primary" | "secondary" | "warning" | "danger" | "gray" | "blue-gray">;
6
6
  value: import("@angular/core").InputSignal<number>;
7
+ _barWidth: import("@angular/core").Signal<string>;
7
8
  static ɵfac: i0.ɵɵFactoryDeclaration<SdProgressControl, never>;
8
9
  static ɵcmp: i0.ɵɵComponentDeclaration<SdProgressControl, "sd-progress", never, { "inset": { "alias": "inset"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "theme": { "alias": "theme"; "required": true; "isSignal": true; }; "value": { "alias": "value"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
9
10
  }
@@ -1 +1 @@
1
- {"version":3,"file":"sd-progress.control.d.ts","sourceRoot":"","sources":["..\\..\\..\\src\\ui\\visual\\sd-progress.control.ts"],"names":[],"mappings":";AASA,qBAsEa,iBAAiB;IAC5B,KAAK,qEAAiD;IACtD,IAAI,+DAAwB;IAC5B,KAAK,kIAED;IAEJ,KAAK,8CAA4B;yCAPtB,iBAAiB;2CAAjB,iBAAiB;CAQ7B"}
1
+ {"version":3,"file":"sd-progress.control.d.ts","sourceRoot":"","sources":["..\\..\\..\\src\\ui\\visual\\sd-progress.control.ts"],"names":[],"mappings":";AAUA,qBAsEa,iBAAiB;IAC5B,KAAK,qEAAiD;IACtD,IAAI,+DAAwB;IAC5B,KAAK,kIAED;IAEJ,KAAK,8CAA4B;IAEjC,SAAS,yCAA2D;yCATzD,iBAAiB;2CAAjB,iBAAiB;CAU7B"}
@@ -1,4 +1,4 @@
1
- import { booleanAttribute, ChangeDetectionStrategy, Component, input, ViewEncapsulation, } from "@angular/core";
1
+ import { booleanAttribute, ChangeDetectionStrategy, Component, computed, input, ViewEncapsulation, } from "@angular/core";
2
2
  import { PercentPipe } from "@angular/common";
3
3
  import * as i0 from "@angular/core";
4
4
  export class SdProgressControl {
@@ -6,6 +6,7 @@ export class SdProgressControl {
6
6
  size = input(...(ngDevMode ? [undefined, { debugName: "size" }] : /* istanbul ignore next */ []));
7
7
  theme = input.required(...(ngDevMode ? [{ debugName: "theme" }] : /* istanbul ignore next */ []));
8
8
  value = input.required(...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
9
+ _barWidth = computed(() => Math.min(this.value() * 100, 100) + "%", ...(ngDevMode ? [{ debugName: "_barWidth" }] : /* istanbul ignore next */ []));
9
10
  static ɵfac = function SdProgressControl_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || SdProgressControl)(); };
10
11
  static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: SdProgressControl, selectors: [["sd-progress"]], hostVars: 3, hostBindings: function SdProgressControl_HostBindings(rf, ctx) { if (rf & 2) {
11
12
  i0.ɵɵattribute("data-sd-inset", ctx.inset())("data-sd-size", ctx.size())("data-sd-theme", ctx.theme());
@@ -19,7 +20,7 @@ export class SdProgressControl {
19
20
  i0.ɵɵadvance();
20
21
  i0.ɵɵtextInterpolate1(" ", i0.ɵɵpipeBind2(2, 3, ctx.value(), "1.0-2"), " ");
21
22
  i0.ɵɵadvance(2);
22
- i0.ɵɵstyleProp("width", ctx.value() * 100 + "%");
23
+ i0.ɵɵstyleProp("width", ctx._barWidth());
23
24
  } }, dependencies: [PercentPipe], styles: ["sd-progress {\n position: relative;\n display: block;\n width: 100%;\n white-space: nowrap;\n background: var(--theme-gray-lightest);\n border: 1px solid var(--theme-gray-lightest);\n border-radius: var(--border-radius-default);\n overflow: hidden;\n}\nsd-progress > ._content {\n position: relative;\n z-index: 2;\n padding: var(--gap-lg) var(--gap-default);\n}\nsd-progress > ._progress {\n position: absolute;\n z-index: 1;\n top: 0;\n left: 0;\n height: 100%;\n}\nsd-progress[data-sd-theme=gray] > ._progress {\n background: var(--theme-gray-default);\n}\nsd-progress[data-sd-theme=blue-gray] > ._progress {\n background: var(--theme-blue-gray-default);\n}\nsd-progress[data-sd-theme=primary] > ._progress {\n background: var(--theme-primary-default);\n}\nsd-progress[data-sd-theme=secondary] > ._progress {\n background: var(--theme-secondary-default);\n}\nsd-progress[data-sd-theme=info] > ._progress {\n background: var(--theme-info-default);\n}\nsd-progress[data-sd-theme=success] > ._progress {\n background: var(--theme-success-default);\n}\nsd-progress[data-sd-theme=warning] > ._progress {\n background: var(--theme-warning-default);\n}\nsd-progress[data-sd-theme=danger] > ._progress {\n background: var(--theme-danger-default);\n}\nsd-progress[data-sd-size=sm] > ._content {\n padding: var(--gap-xs) var(--gap-default);\n}\nsd-progress[data-sd-size=lg] > ._content {\n padding: var(--gap-default) var(--gap-xl);\n}\nsd-progress[data-sd-inset=true] {\n border-radius: 0;\n border: none;\n background: var(--control-color);\n}"], encapsulation: 2, changeDetection: 0 });
24
25
  }
25
26
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(SdProgressControl, [{
@@ -32,7 +33,7 @@ export class SdProgressControl {
32
33
  <div class="_content tx-right">
33
34
  {{ value() | percent: "1.0-2" }}
34
35
  </div>
35
- <div class="_progress" [style.width]="value() * 100 + '%'"></div>
36
+ <div class="_progress" [style.width]="_barWidth()"></div>
36
37
  `, styles: ["sd-progress {\n position: relative;\n display: block;\n width: 100%;\n white-space: nowrap;\n background: var(--theme-gray-lightest);\n border: 1px solid var(--theme-gray-lightest);\n border-radius: var(--border-radius-default);\n overflow: hidden;\n}\nsd-progress > ._content {\n position: relative;\n z-index: 2;\n padding: var(--gap-lg) var(--gap-default);\n}\nsd-progress > ._progress {\n position: absolute;\n z-index: 1;\n top: 0;\n left: 0;\n height: 100%;\n}\nsd-progress[data-sd-theme=gray] > ._progress {\n background: var(--theme-gray-default);\n}\nsd-progress[data-sd-theme=blue-gray] > ._progress {\n background: var(--theme-blue-gray-default);\n}\nsd-progress[data-sd-theme=primary] > ._progress {\n background: var(--theme-primary-default);\n}\nsd-progress[data-sd-theme=secondary] > ._progress {\n background: var(--theme-secondary-default);\n}\nsd-progress[data-sd-theme=info] > ._progress {\n background: var(--theme-info-default);\n}\nsd-progress[data-sd-theme=success] > ._progress {\n background: var(--theme-success-default);\n}\nsd-progress[data-sd-theme=warning] > ._progress {\n background: var(--theme-warning-default);\n}\nsd-progress[data-sd-theme=danger] > ._progress {\n background: var(--theme-danger-default);\n}\nsd-progress[data-sd-size=sm] > ._content {\n padding: var(--gap-xs) var(--gap-default);\n}\nsd-progress[data-sd-size=lg] > ._content {\n padding: var(--gap-default) var(--gap-xl);\n}\nsd-progress[data-sd-inset=true] {\n border-radius: 0;\n border: none;\n background: var(--control-color);\n}"] }]
37
38
  }], null, { inset: [{ type: i0.Input, args: [{ isSignal: true, alias: "inset", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], theme: [{ type: i0.Input, args: [{ isSignal: true, alias: "theme", required: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }] }); })();
38
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(SdProgressControl, { className: "SdProgressControl", filePath: "packages/angular/src/ui/visual/sd-progress.control.ts", lineNumber: 80 }); })();
39
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(SdProgressControl, { className: "SdProgressControl", filePath: "packages/angular/src/ui/visual/sd-progress.control.ts", lineNumber: 81 }); })();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simplysm/angular",
3
- "version": "14.0.7",
3
+ "version": "14.0.8",
4
4
  "description": "심플리즘 패키지 - Angular",
5
5
  "author": "심플리즘",
6
6
  "license": "Apache-2.0",
@@ -50,10 +50,10 @@
50
50
  "jspdf": "^4.2.1",
51
51
  "rxjs": "^7.8.2",
52
52
  "tabbable": "^6.4.0",
53
- "@simplysm/core-browser": "14.0.7",
54
- "@simplysm/service-client": "14.0.7",
55
- "@simplysm/core-common": "14.0.7",
56
- "@simplysm/service-common": "14.0.7"
53
+ "@simplysm/core-common": "14.0.8",
54
+ "@simplysm/service-common": "14.0.8",
55
+ "@simplysm/core-browser": "14.0.8",
56
+ "@simplysm/service-client": "14.0.8"
57
57
  },
58
58
  "devDependencies": {
59
59
  "@angular/compiler": "^21.2.6",
@@ -0,0 +1,41 @@
1
+ import { type AbstractType, inject, Injector, ViewContainerRef } from "@angular/core";
2
+
3
+ export function injectParent<T = object>(): T;
4
+ export function injectParent<T = object>(type: AbstractType<T>): T;
5
+ export function injectParent<T = object>(
6
+ type: AbstractType<T>,
7
+ options: { optional: true },
8
+ ): T | undefined;
9
+ export function injectParent<T = object>(
10
+ type?: AbstractType<T>,
11
+ options?: { optional: true },
12
+ ): T | undefined {
13
+ let currentInjector: Injector | undefined = inject(ViewContainerRef).injector;
14
+ while (currentInjector != null) {
15
+ // Angular internal: NodeInjector._lView[CONTEXT=8]에 컴포넌트 인스턴스 저장
16
+ const lView: unknown[] | undefined = (
17
+ currentInjector as unknown as { _lView?: unknown[] }
18
+ )._lView;
19
+ if (lView == null) {
20
+ break;
21
+ }
22
+
23
+ const comp = lView[8] as object;
24
+ if (type == null) {
25
+ return comp as T;
26
+ }
27
+ if (comp instanceof type) {
28
+ return comp as T;
29
+ }
30
+
31
+ currentInjector = currentInjector.get(Injector, undefined, {
32
+ skipSelf: true,
33
+ optional: true,
34
+ }) as Injector | undefined;
35
+ }
36
+
37
+ if (options?.optional) {
38
+ return undefined;
39
+ }
40
+ throw new Error("부모 컴포넌트를 찾을 수 없습니다.");
41
+ }
@@ -0,0 +1,113 @@
1
+ import { NgTemplateOutlet } from "@angular/common";
2
+ import {
3
+ booleanAttribute,
4
+ ChangeDetectionStrategy,
5
+ Component,
6
+ computed,
7
+ contentChild,
8
+ inject,
9
+ input,
10
+ TemplateRef,
11
+ ViewEncapsulation,
12
+ } from "@angular/core";
13
+ import { SdBusyContainerControl } from "../../ui/overlay/busy/sd-busy-container.control";
14
+ import { SdTopbarContainerControl } from "../../ui/navigation/topbar/sd-topbar-container.control";
15
+ import { SdTopbarControl } from "../../ui/navigation/topbar/sd-topbar.control";
16
+ import { SdAppStructureProvider } from "../../core/providers/sd-app-structure.provider";
17
+ import { SdActivatedModalProvider } from "../../ui/overlay/modal/sd-modal.provider";
18
+ import { useCurrentPageCodeSignal } from "../../core/utils/useCurrentPageCodeSignal";
19
+ import { useFullPageCodeSignal } from "../../core/utils/useFullPageCodeSignal";
20
+ import {
21
+ useViewTypeSignal,
22
+ type TSdViewType,
23
+ } from "../../core/utils/useViewTypeSignal";
24
+ import { injectParent } from "../../core/utils/injectParent";
25
+ import { NgIcon } from "@ng-icons/core";
26
+ import { tablerAlertTriangle } from "@ng-icons/tabler-icons";
27
+
28
+ @Component({
29
+ selector: "sd-base-container",
30
+ changeDetection: ChangeDetectionStrategy.OnPush,
31
+ encapsulation: ViewEncapsulation.None,
32
+ standalone: true,
33
+ imports: [
34
+ SdBusyContainerControl,
35
+ SdTopbarContainerControl,
36
+ SdTopbarControl,
37
+ NgTemplateOutlet,
38
+ NgIcon,
39
+ ],
40
+ template: `
41
+ <sd-busy-container [busy]="busy()" [message]="busyMessage()">
42
+ @if (initialized() === undefined || initialized()) {
43
+ @if (restricted()) {
44
+ <div class="fill tx-theme-gray-light p-xxl tx-center">
45
+ <br />
46
+ <ng-icon [svg]="tablerAlertTriangle" [size]="'5em'" />
47
+ <br />
48
+ <br />
49
+ '{{ modalOrPageTitle() }}'에 대한 사용권한이 없습니다. 시스템 관리자에게 문의하세요.
50
+ </div>
51
+ } @else if (currViewType() === "page") {
52
+ <sd-topbar-container>
53
+ <sd-topbar>
54
+ <h4>{{ modalOrPageTitle() }}</h4>
55
+
56
+ <ng-template [ngTemplateOutlet]="pageTopbarTplRef() ?? null" />
57
+ </sd-topbar>
58
+
59
+ <div class="fill">
60
+ <ng-template [ngTemplateOutlet]="contentTplRef()" />
61
+ </div>
62
+ </sd-topbar-container>
63
+ } @else if (currViewType() === "modal") {
64
+ <div class="flex-column fill">
65
+ <div class="flex-fill">
66
+ <ng-template [ngTemplateOutlet]="contentTplRef()" />
67
+ </div>
68
+ @if (modalBottomTplRef()) {
69
+ <div class="bdt bdt-theme-gray-lightest">
70
+ <ng-template [ngTemplateOutlet]="modalBottomTplRef() ?? null" />
71
+ </div>
72
+ }
73
+ </div>
74
+ } @else {
75
+ <ng-template [ngTemplateOutlet]="contentTplRef()" />
76
+ }
77
+ }
78
+ </sd-busy-container>
79
+ `,
80
+ })
81
+ export class SdBaseContainerControl {
82
+ private readonly _sdActivatedModal = inject(SdActivatedModalProvider, { optional: true });
83
+ private readonly _sdAppStructure = inject(SdAppStructureProvider);
84
+
85
+ private readonly _parent = injectParent();
86
+
87
+ private readonly _fullPageCode = useFullPageCodeSignal();
88
+ private readonly _currPageCode = useCurrentPageCodeSignal();
89
+
90
+ contentTplRef = contentChild.required("contentTpl", { read: TemplateRef });
91
+
92
+ pageTopbarTplRef = contentChild("pageTopbarTpl", { read: TemplateRef });
93
+ modalBottomTplRef = contentChild("modalBottomTpl", { read: TemplateRef });
94
+
95
+ private readonly _parentViewType = useViewTypeSignal(() => this._parent);
96
+ viewType = input<TSdViewType>();
97
+ currViewType = computed(() => this.viewType() ?? this._parentViewType());
98
+
99
+ header = input<string>();
100
+ modalOrPageTitle = computed(
101
+ () =>
102
+ this.header() ??
103
+ this._sdActivatedModal?.modalComponent()?.title() ??
104
+ this._sdAppStructure.getTitleByFullCode(this._currPageCode?.() ?? this._fullPageCode()),
105
+ );
106
+
107
+ initialized = input<boolean | undefined>(undefined);
108
+ restricted = input(false, { transform: booleanAttribute });
109
+ busy = input(false, { transform: booleanAttribute });
110
+ busyMessage = input<string>();
111
+
112
+ protected readonly tablerAlertTriangle = tablerAlertTriangle;
113
+ }
package/src/index.ts CHANGED
@@ -62,6 +62,10 @@ export {
62
62
  type IExpandItemDef,
63
63
  } from "./core/utils/useExpandingManager";
64
64
  export { useSelectionManager } from "./core/utils/useSelectionManager";
65
+ export { injectParent } from "./core/utils/injectParent";
66
+
67
+ // features
68
+ export { SdBaseContainerControl } from "./features/base/sd-base-container.control";
65
69
 
66
70
  // ui/layout
67
71
  export { SdDockContainerControl } from "./ui/layout/dock/sd-dock-container.control";
@@ -131,7 +131,7 @@ export class SdListItemControl {
131
131
  });
132
132
 
133
133
  constructor() {
134
- setupRipple(() => !this.readonly() && !(this.layout() === "flat" && this.hasChildren()));
134
+ setupRipple(() => !this.readonly() && this.layout() !== "flat");
135
135
  }
136
136
 
137
137
  onContentClick(): void {
@@ -384,7 +384,7 @@ export class SdSheetControl<T> {
384
384
  // Resizing state
385
385
  _isResizing = signal(false);
386
386
  _resizeIndicatorLeft = signal(0);
387
- private _isOnResizing = false;
387
+ private _lastResizeEndTimeStamp = 0;
388
388
  private _resizingCleanup: (() => void) | null = null;
389
389
 
390
390
  // Config resource
@@ -478,13 +478,21 @@ export class SdSheetControl<T> {
478
478
  getItemSelectableFn: this.getItemSelectableFn,
479
479
  });
480
480
 
481
+ private readonly _columnControlMap = computed(() => {
482
+ const map = new Map<string, SdSheetColumnDirective>();
483
+ for (const col of this.columnControls()) {
484
+ map.set(col.key(), col);
485
+ }
486
+ return map;
487
+ });
488
+
481
489
  getColumnCellTpl(key: string) {
482
- const col = this.columnControls().find((c) => c.key() === key);
490
+ const col = this._columnControlMap().get(key);
483
491
  return col?.cellTplRef() ?? null;
484
492
  }
485
493
 
486
494
  getColumnSummaryTpl(key: string) {
487
- const col = this.columnControls().find((c) => c.key() === key);
495
+ const col = this._columnControlMap().get(key);
488
496
  return col?.summaryTplRef() ?? null;
489
497
  }
490
498
 
@@ -513,9 +521,8 @@ export class SdSheetControl<T> {
513
521
  if (fixedStyle != null) {
514
522
  parts.push(fixedStyle);
515
523
  }
516
- const customStyle = this.getItemCellStyleFn()
517
- ? this.getItemCellStyleFn()!(item, colDef.key)
518
- : undefined;
524
+ const styleFn = this.getItemCellStyleFn();
525
+ const customStyle = styleFn != null ? styleFn(item, colDef.key) : undefined;
519
526
  if (customStyle != null) {
520
527
  parts.push(customStyle);
521
528
  }
@@ -556,7 +563,7 @@ export class SdSheetControl<T> {
556
563
  }
557
564
 
558
565
  onHeaderClick(event: MouseEvent, cell: ISdSheetHeaderDef): void {
559
- if (this._isOnResizing) return;
566
+ if (event.timeStamp - this._lastResizeEndTimeStamp < 50) return;
560
567
  if (cell.colDef == null) return;
561
568
  if (cell.colDef.disableSorting) return;
562
569
  this.sorting.toggle(cell.colDef.key, event.shiftKey);
@@ -642,7 +649,8 @@ export class SdSheetControl<T> {
642
649
 
643
650
  getDataCellClass(item: T, colDef: ISdSheetColumnDef, r: number, c: number): string | null {
644
651
  const parts: string[] = [];
645
- const customClass = this.getItemCellClassFn() ? this.getItemCellClassFn()!(item, colDef.key) : undefined;
652
+ const classFn = this.getItemCellClassFn();
653
+ const customClass = classFn != null ? classFn(item, colDef.key) : undefined;
646
654
  if (customClass != null) {
647
655
  parts.push(customClass);
648
656
  }
@@ -700,7 +708,6 @@ export class SdSheetControl<T> {
700
708
  const startWidth = th.offsetWidth;
701
709
 
702
710
  this._isResizing.set(true);
703
- this._isOnResizing = true;
704
711
  this._resizeIndicatorLeft.set(
705
712
  th.offsetLeft + th.offsetWidth - container.scrollLeft,
706
713
  );
@@ -724,10 +731,7 @@ export class SdSheetControl<T> {
724
731
  this._isResizing.set(false);
725
732
  this._saveColumnConfig(colDef.key, { width: `${newWidth}px` });
726
733
 
727
- // Prevent sorting for 300ms after resize
728
- setTimeout(() => {
729
- this._isOnResizing = false;
730
- }, 300);
734
+ this._lastResizeEndTimeStamp = e.timeStamp;
731
735
  };
732
736
 
733
737
  document.addEventListener("mousemove", onMouseMove);
@@ -192,8 +192,7 @@ export function useSheetCellAgent(options: {
192
192
  }
193
193
 
194
194
  event.preventDefault();
195
- const text = td.textContent;
196
- await navigator.clipboard.writeText(text);
195
+ await navigator.clipboard.writeText(td.textContent);
197
196
  return;
198
197
  }
199
198
 
@@ -112,6 +112,7 @@ export function useSheetLayoutEngine(options: {
112
112
 
113
113
  if (canMerge && lastCell != null) {
114
114
  lastCell.colspan += 1;
115
+ spanStartHeaders[row] = headers;
115
116
  } else {
116
117
  table[row].push({
117
118
  text,
@@ -2,6 +2,7 @@ import {
2
2
  booleanAttribute,
3
3
  ChangeDetectionStrategy,
4
4
  Component,
5
+ DestroyRef,
5
6
  effect,
6
7
  ElementRef,
7
8
  inject,
@@ -87,6 +88,9 @@ import type { ISdResizeEvent } from "../../../core/plugins/events/sd-resize-even
87
88
  })
88
89
  export class SdDockControl {
89
90
  private readonly _elRef = inject(ElementRef<HTMLElement>);
91
+ private readonly _destroyRef = inject(DestroyRef);
92
+
93
+ private _dragCleanup: (() => void) | undefined;
90
94
 
91
95
  key = input<string>();
92
96
  position = input<"top" | "bottom" | "right" | "left">("top");
@@ -97,6 +101,10 @@ export class SdDockControl {
97
101
  private readonly _config = useSdSystemConfigResource<{ size?: string }>({ key: this.key });
98
102
 
99
103
  constructor() {
104
+ this._destroyRef.onDestroy(() => {
105
+ this._dragCleanup?.();
106
+ });
107
+
100
108
  effect(() => {
101
109
  const conf = this._config.value();
102
110
  if (this.resizable() && conf && conf.size != null) {
@@ -151,8 +159,7 @@ export class SdDockControl {
151
159
  e.stopPropagation();
152
160
  e.preventDefault();
153
161
 
154
- document.removeEventListener("mousemove", doDrag);
155
- document.removeEventListener("mouseup", stopDrag);
162
+ this._dragCleanup?.();
156
163
 
157
164
  if (this.key() != null) {
158
165
  const newConf: { size?: string } = {};
@@ -167,5 +174,11 @@ export class SdDockControl {
167
174
 
168
175
  document.addEventListener("mousemove", doDrag);
169
176
  document.addEventListener("mouseup", stopDrag);
177
+
178
+ this._dragCleanup = () => {
179
+ document.removeEventListener("mousemove", doDrag);
180
+ document.removeEventListener("mouseup", stopDrag);
181
+ this._dragCleanup = undefined;
182
+ };
170
183
  }
171
184
  }
@@ -136,9 +136,10 @@ export class SdKanbanLaneControl<L, T> implements ISdKanbanDropTarget<L, T> {
136
136
  toolTplRef = contentChild<any, TemplateRef<void>>("toolTpl", { read: TemplateRef });
137
137
  titleTplRef = contentChild<any, TemplateRef<void>>("titleTpl", { read: TemplateRef });
138
138
 
139
- isAllSelected = computed(() =>
140
- this.kanbanControls().length > 0 && this.kanbanControls().every((ctrl) => ctrl.selected()),
141
- );
139
+ isAllSelected = computed(() => {
140
+ const selectableControls = this.kanbanControls().filter((ctrl) => ctrl.selectable());
141
+ return selectableControls.length > 0 && selectableControls.every((ctrl) => ctrl.selected());
142
+ });
142
143
 
143
144
  dragKanban = computed(() => this._boardControl.dragKanban());
144
145
 
@@ -161,6 +162,7 @@ export class SdKanbanLaneControl<L, T> implements ISdKanbanDropTarget<L, T> {
161
162
  this._boardControl.selectedValues.update((v) => {
162
163
  const r = [...v];
163
164
  for (const ctrl of this.kanbanControls()) {
165
+ if (!ctrl.selectable()) continue;
164
166
  if (ctrl.value() == null) continue;
165
167
  if (!v.includes(ctrl.value()!)) {
166
168
  r.push(ctrl.value()!);
@@ -171,6 +173,7 @@ export class SdKanbanLaneControl<L, T> implements ISdKanbanDropTarget<L, T> {
171
173
  } else {
172
174
  this._boardControl.selectedValues.update((v) => {
173
175
  const kanbanValues = this.kanbanControls()
176
+ .filter((ctrl) => ctrl.selectable())
174
177
  .map((ctrl) => ctrl.value())
175
178
  .filter((v2): v2 is T => v2 != null);
176
179
  const result = v.filter((item) => !kanbanValues.includes(item));
@@ -67,7 +67,7 @@ export class SdPaginationControl {
67
67
  visiblePageCount = input(10, { transform: numberAttribute });
68
68
 
69
69
  private readonly groupIndex = computed(() => {
70
- return Math.floor(this.currentPage() / this.visiblePageCount());
70
+ return Math.floor(this.currentPage() / Math.max(this.visiblePageCount(), 1));
71
71
  });
72
72
 
73
73
  hasPrev = computed(() => {
@@ -82,7 +82,7 @@ export class SdPaginationControl {
82
82
  if (totalPageCount === 0) {
83
83
  return false;
84
84
  }
85
- const lastGroupIndex = Math.floor((totalPageCount - 1) / this.visiblePageCount());
85
+ const lastGroupIndex = Math.floor((totalPageCount - 1) / Math.max(this.visiblePageCount(), 1));
86
86
  return this.groupIndex() < lastGroupIndex;
87
87
  });
88
88
 
@@ -112,7 +112,8 @@ export class SdPaginationControl {
112
112
  }
113
113
 
114
114
  goToPrevGroup(): void {
115
- this.currentPage.set((this.groupIndex() - 1) * this.visiblePageCount());
115
+ if (!this.hasPrev()) return;
116
+ this.currentPage.set((this.groupIndex() - 1) * Math.max(this.visiblePageCount(), 1));
116
117
  }
117
118
 
118
119
  goToFirst(): void {
@@ -120,6 +121,7 @@ export class SdPaginationControl {
120
121
  }
121
122
 
122
123
  goToLast(): void {
124
+ if (this.totalPageCount() === 0) return;
123
125
  this.currentPage.set(this.totalPageCount() - 1);
124
126
  }
125
127
  }