@simplysm/angular 14.0.6 → 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.
- package/dist/core/directives/sd-router-link.directive.d.ts.map +1 -1
- package/dist/core/directives/sd-router-link.directive.js +5 -10
- package/dist/core/plugins/events/sd-resize-event.plugin.d.ts.map +1 -1
- package/dist/core/plugins/events/sd-resize-event.plugin.js +3 -0
- package/dist/core/plugins/sd-global-error-handler.plugin.d.ts +1 -0
- package/dist/core/plugins/sd-global-error-handler.plugin.d.ts.map +1 -1
- package/dist/core/plugins/sd-global-error-handler.plugin.js +4 -0
- package/dist/core/providers/sd-app-structure.provider.d.ts.map +1 -1
- package/dist/core/providers/sd-app-structure.provider.js +4 -1
- package/dist/core/providers/sd-navigate-window.provider.d.ts +2 -1
- package/dist/core/providers/sd-navigate-window.provider.d.ts.map +1 -1
- package/dist/core/providers/sd-navigate-window.provider.js +13 -10
- package/dist/core/utils/injectParent.d.ts +7 -0
- package/dist/core/utils/injectParent.d.ts.map +1 -0
- package/dist/core/utils/injectParent.js +26 -0
- package/dist/core/utils/setups/setupCanDeactivate.d.ts.map +1 -1
- package/dist/core/utils/setups/setupCanDeactivate.js +9 -2
- package/dist/core/utils/setups/setupInvalid.d.ts.map +1 -1
- package/dist/core/utils/setups/setupInvalid.js +4 -0
- package/dist/core/utils/setups/setupModelHook.d.ts +1 -1
- package/dist/core/utils/setups/setupModelHook.d.ts.map +1 -1
- package/dist/core/utils/setups/setupModelHook.js +5 -1
- package/dist/core/utils/setups/setupRipple.d.ts.map +1 -1
- package/dist/core/utils/setups/setupRipple.js +3 -2
- package/dist/core/utils/useExpandingManager.d.ts.map +1 -1
- package/dist/core/utils/useExpandingManager.js +2 -1
- package/dist/core/utils/useSdSystemConfigResource.d.ts.map +1 -1
- package/dist/core/utils/useSdSystemConfigResource.js +6 -3
- package/dist/features/base/sd-base-container.control.d.ts +26 -0
- package/dist/features/base/sd-base-container.control.d.ts.map +1 -0
- package/dist/features/base/sd-base-container.control.js +183 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/ui/data/list/sd-list-item.control.js +1 -1
- package/dist/ui/data/sheet/sd-sheet.control.d.ts +2 -1
- package/dist/ui/data/sheet/sd-sheet.control.d.ts.map +1 -1
- package/dist/ui/data/sheet/sd-sheet.control.js +16 -13
- package/dist/ui/data/sheet/useSheetCellAgent.d.ts.map +1 -1
- package/dist/ui/data/sheet/useSheetCellAgent.js +1 -2
- package/dist/ui/data/sheet/useSheetLayoutEngine.d.ts.map +1 -1
- package/dist/ui/data/sheet/useSheetLayoutEngine.js +1 -0
- package/dist/ui/form/editor/sd-tiptap-editor.control.d.ts +5 -5
- package/dist/ui/form/editor/sd-tiptap-editor.control.d.ts.map +1 -1
- package/dist/ui/form/editor/sd-tiptap-editor.control.js +79 -78
- package/dist/ui/form/input/sd-date-range.picker.d.ts.map +1 -1
- package/dist/ui/form/input/sd-date-range.picker.js +4 -3
- package/dist/ui/form/input/sd-textfield-type-handlers.d.ts.map +1 -1
- package/dist/ui/form/input/sd-textfield-type-handlers.js +21 -2
- package/dist/ui/form/input/sd-textfield.control.d.ts.map +1 -1
- package/dist/ui/form/input/sd-textfield.control.js +1 -0
- package/dist/ui/layout/dock/sd-dock.control.d.ts +2 -0
- package/dist/ui/layout/dock/sd-dock.control.d.ts.map +1 -1
- package/dist/ui/layout/dock/sd-dock.control.js +13 -4
- package/dist/ui/layout/kanban/sd-kanban-lane.control.d.ts.map +1 -1
- package/dist/ui/layout/kanban/sd-kanban-lane.control.js +7 -1
- package/dist/ui/layout/sd-gap.control.d.ts +2 -2
- package/dist/ui/navigation/pagination/sd-pagination.control.d.ts.map +1 -1
- package/dist/ui/navigation/pagination/sd-pagination.control.js +7 -3
- package/dist/ui/navigation/sidebar/sd-sidebar-container.control.js +1 -1
- package/dist/ui/overlay/dropdown/sd-dropdown-popup.control.js +1 -1
- package/dist/ui/overlay/modal/sd-modal.control.d.ts +1 -0
- package/dist/ui/overlay/modal/sd-modal.control.d.ts.map +1 -1
- package/dist/ui/overlay/modal/sd-modal.control.js +11 -3
- package/dist/ui/overlay/modal/sd-modal.provider.d.ts +1 -1
- package/dist/ui/overlay/modal/sd-modal.provider.js +1 -1
- package/dist/ui/overlay/toast/sd-toast.provider.d.ts.map +1 -1
- package/dist/ui/overlay/toast/sd-toast.provider.js +7 -3
- package/dist/ui/visual/sd-progress.control.d.ts +1 -0
- package/dist/ui/visual/sd-progress.control.d.ts.map +1 -1
- package/dist/ui/visual/sd-progress.control.js +5 -4
- package/docs/providers.md +1 -1
- package/docs/setup-functions.md +1 -1
- package/package.json +7 -10
- package/src/core/directives/sd-router-link.directive.ts +5 -13
- package/src/core/plugins/events/sd-resize-event.plugin.ts +3 -0
- package/src/core/plugins/sd-global-error-handler.plugin.ts +4 -0
- package/src/core/providers/sd-app-structure.provider.ts +4 -1
- package/src/core/providers/sd-navigate-window.provider.ts +12 -13
- package/src/core/utils/injectParent.ts +41 -0
- package/src/core/utils/setups/setupCanDeactivate.ts +10 -2
- package/src/core/utils/setups/setupInvalid.ts +5 -0
- package/src/core/utils/setups/setupModelHook.ts +5 -2
- package/src/core/utils/setups/setupRipple.ts +3 -2
- package/src/core/utils/useExpandingManager.ts +2 -1
- package/src/core/utils/useSdSystemConfigResource.ts +6 -3
- package/src/features/base/sd-base-container.control.ts +113 -0
- package/src/index.ts +4 -0
- package/src/ui/data/list/sd-list-item.control.ts +1 -1
- package/src/ui/data/sheet/sd-sheet.control.ts +17 -13
- package/src/ui/data/sheet/useSheetCellAgent.ts +1 -2
- package/src/ui/data/sheet/useSheetLayoutEngine.ts +1 -0
- package/src/ui/form/editor/sd-tiptap-editor.control.ts +64 -61
- package/src/ui/form/input/sd-date-range.picker.ts +4 -3
- package/src/ui/form/input/sd-textfield-type-handlers.ts +25 -5
- package/src/ui/form/input/sd-textfield.control.ts +1 -0
- package/src/ui/layout/dock/sd-dock.control.ts +15 -2
- package/src/ui/layout/kanban/sd-kanban-lane.control.ts +6 -3
- package/src/ui/navigation/pagination/sd-pagination.control.ts +5 -3
- package/src/ui/navigation/sidebar/sd-sidebar-container.control.ts +1 -1
- package/src/ui/overlay/dropdown/sd-dropdown-popup.control.ts +1 -1
- package/src/ui/overlay/modal/sd-modal.control.ts +10 -1
- package/src/ui/overlay/modal/sd-modal.provider.ts +1 -1
- package/src/ui/overlay/toast/sd-toast.provider.ts +9 -3
- package/src/ui/visual/sd-progress.control.ts +4 -1
|
@@ -9,8 +9,10 @@ import {
|
|
|
9
9
|
inject,
|
|
10
10
|
input,
|
|
11
11
|
model,
|
|
12
|
+
signal,
|
|
12
13
|
untracked,
|
|
13
14
|
ViewEncapsulation,
|
|
15
|
+
type WritableSignal,
|
|
14
16
|
} from "@angular/core";
|
|
15
17
|
import { setupInvalid } from "../../../core/utils/setups/setupInvalid";
|
|
16
18
|
import { Editor, type AnyExtension } from "@tiptap/core";
|
|
@@ -41,55 +43,55 @@ const DEFAULT_EXTENSIONS: AnyExtension[] = [
|
|
|
41
43
|
@if (!disabled()) {
|
|
42
44
|
<div class="_toolbar">
|
|
43
45
|
<div class="_btn-group">
|
|
44
|
-
<button type="button" data-cmd="h1" [class._active]="activeStates.h1"
|
|
46
|
+
<button type="button" data-cmd="h1" [class._active]="activeStates().h1"
|
|
45
47
|
(click)="execCmd('h1')">H1</button>
|
|
46
|
-
<button type="button" data-cmd="h2" [class._active]="activeStates.h2"
|
|
48
|
+
<button type="button" data-cmd="h2" [class._active]="activeStates().h2"
|
|
47
49
|
(click)="execCmd('h2')">H2</button>
|
|
48
50
|
</div>
|
|
49
51
|
<div class="_btn-group">
|
|
50
|
-
<button type="button" data-cmd="bold" [class._active]="activeStates.bold"
|
|
52
|
+
<button type="button" data-cmd="bold" [class._active]="activeStates().bold"
|
|
51
53
|
(click)="execCmd('bold')">B</button>
|
|
52
|
-
<button type="button" data-cmd="italic" [class._active]="activeStates.italic"
|
|
54
|
+
<button type="button" data-cmd="italic" [class._active]="activeStates().italic"
|
|
53
55
|
(click)="execCmd('italic')">I</button>
|
|
54
|
-
<button type="button" data-cmd="underline" [class._active]="activeStates.underline"
|
|
56
|
+
<button type="button" data-cmd="underline" [class._active]="activeStates().underline"
|
|
55
57
|
(click)="execCmd('underline')">U</button>
|
|
56
|
-
<button type="button" data-cmd="strike" [class._active]="activeStates.strike"
|
|
58
|
+
<button type="button" data-cmd="strike" [class._active]="activeStates().strike"
|
|
57
59
|
(click)="execCmd('strike')">S</button>
|
|
58
60
|
</div>
|
|
59
61
|
<div class="_btn-group">
|
|
60
62
|
<button type="button" data-cmd="textColor" class="_color-btn"
|
|
61
63
|
(click)="toggleColorPicker('text')">
|
|
62
|
-
<span class="_color-indicator" [style.background-color]="activeColor"></span>
|
|
64
|
+
<span class="_color-indicator" [style.background-color]="activeColor()"></span>
|
|
63
65
|
A
|
|
64
66
|
</button>
|
|
65
67
|
<button type="button" data-cmd="bgColor" class="_color-btn"
|
|
66
68
|
(click)="toggleColorPicker('bg')">
|
|
67
|
-
<span class="_color-indicator" [style.background-color]="activeBgColor"></span>
|
|
69
|
+
<span class="_color-indicator" [style.background-color]="activeBgColor()"></span>
|
|
68
70
|
BG
|
|
69
71
|
</button>
|
|
70
72
|
</div>
|
|
71
73
|
<div class="_btn-group">
|
|
72
|
-
<button type="button" data-cmd="bulletList" [class._active]="activeStates.bulletList"
|
|
74
|
+
<button type="button" data-cmd="bulletList" [class._active]="activeStates().bulletList"
|
|
73
75
|
(click)="execCmd('bulletList')">UL</button>
|
|
74
|
-
<button type="button" data-cmd="orderedList" [class._active]="activeStates.orderedList"
|
|
76
|
+
<button type="button" data-cmd="orderedList" [class._active]="activeStates().orderedList"
|
|
75
77
|
(click)="execCmd('orderedList')">OL</button>
|
|
76
78
|
<button type="button" data-cmd="indent" (click)="execCmd('indent')">→</button>
|
|
77
79
|
<button type="button" data-cmd="outdent" (click)="execCmd('outdent')">←</button>
|
|
78
80
|
</div>
|
|
79
81
|
<div class="_btn-group">
|
|
80
|
-
<button type="button" data-cmd="blockquote" [class._active]="activeStates.blockquote"
|
|
82
|
+
<button type="button" data-cmd="blockquote" [class._active]="activeStates().blockquote"
|
|
81
83
|
(click)="execCmd('blockquote')">❝</button>
|
|
82
|
-
<button type="button" data-cmd="codeBlock" [class._active]="activeStates.codeBlock"
|
|
84
|
+
<button type="button" data-cmd="codeBlock" [class._active]="activeStates().codeBlock"
|
|
83
85
|
(click)="execCmd('codeBlock')"></></button>
|
|
84
86
|
</div>
|
|
85
87
|
<div class="_btn-group">
|
|
86
|
-
<button type="button" data-cmd="alignLeft" [class._active]="activeStates.alignLeft"
|
|
88
|
+
<button type="button" data-cmd="alignLeft" [class._active]="activeStates().alignLeft"
|
|
87
89
|
(click)="execCmd('alignLeft')">≡L</button>
|
|
88
|
-
<button type="button" data-cmd="alignCenter" [class._active]="activeStates.alignCenter"
|
|
90
|
+
<button type="button" data-cmd="alignCenter" [class._active]="activeStates().alignCenter"
|
|
89
91
|
(click)="execCmd('alignCenter')">≡C</button>
|
|
90
|
-
<button type="button" data-cmd="alignRight" [class._active]="activeStates.alignRight"
|
|
92
|
+
<button type="button" data-cmd="alignRight" [class._active]="activeStates().alignRight"
|
|
91
93
|
(click)="execCmd('alignRight')">≡R</button>
|
|
92
|
-
<button type="button" data-cmd="alignJustify" [class._active]="activeStates.alignJustify"
|
|
94
|
+
<button type="button" data-cmd="alignJustify" [class._active]="activeStates().alignJustify"
|
|
93
95
|
(click)="execCmd('alignJustify')">≡J</button>
|
|
94
96
|
</div>
|
|
95
97
|
<div class="_btn-group">
|
|
@@ -249,13 +251,13 @@ export class SdTiptapEditorControl {
|
|
|
249
251
|
"#f9cb9c", "#ffe599", "#b6d7a8", "#a2c4c9", "#a4c2f4", "#9fc5e8", "#b4a7d6", "#d5a6bd",
|
|
250
252
|
];
|
|
251
253
|
|
|
252
|
-
activeStates: Record<string, boolean
|
|
253
|
-
activeColor = "";
|
|
254
|
-
activeBgColor = "";
|
|
254
|
+
activeStates: WritableSignal<Record<string, boolean>> = signal({});
|
|
255
|
+
activeColor = signal("");
|
|
256
|
+
activeBgColor = signal("");
|
|
255
257
|
colorPickerMode: "text" | "bg" | undefined;
|
|
256
258
|
|
|
257
259
|
/** @internal -- TipTap Editor 인스턴스. 테스트 및 고급 사용자용 */
|
|
258
|
-
editor: Editor | undefined;
|
|
260
|
+
editor: WritableSignal<Editor | undefined> = signal(undefined);
|
|
259
261
|
private updatingFromEditor = false;
|
|
260
262
|
private lastExtensions: AnyExtension[] | undefined;
|
|
261
263
|
|
|
@@ -288,17 +290,19 @@ export class SdTiptapEditorControl {
|
|
|
288
290
|
}
|
|
289
291
|
|
|
290
292
|
// Sync value to existing editor
|
|
291
|
-
|
|
292
|
-
|
|
293
|
+
const currentEditor = untracked(() => this.editor());
|
|
294
|
+
if (currentEditor == null) return;
|
|
295
|
+
const currentHtml = this.getEditorHtmlFrom(currentEditor);
|
|
293
296
|
if (currentHtml === val) return;
|
|
294
|
-
|
|
297
|
+
currentEditor.commands.setContent(val ?? "", { emitUpdate: false });
|
|
295
298
|
});
|
|
296
299
|
|
|
297
300
|
// disabled/readonly → editor.setEditable()
|
|
298
301
|
effect(() => {
|
|
299
|
-
|
|
302
|
+
const ed = this.editor();
|
|
303
|
+
if (ed == null) return;
|
|
300
304
|
const editable = !this.disabled() && !this.readonly();
|
|
301
|
-
|
|
305
|
+
ed.setEditable(editable);
|
|
302
306
|
});
|
|
303
307
|
|
|
304
308
|
// setupInvalid for form validation
|
|
@@ -329,7 +333,7 @@ export class SdTiptapEditorControl {
|
|
|
329
333
|
const container = this.elRef.nativeElement.querySelector("._editor-container");
|
|
330
334
|
if (container == null) return;
|
|
331
335
|
|
|
332
|
-
this.editor
|
|
336
|
+
this.editor.set(new Editor({
|
|
333
337
|
element: container,
|
|
334
338
|
extensions,
|
|
335
339
|
content: initialContent ?? "",
|
|
@@ -343,21 +347,17 @@ export class SdTiptapEditorControl {
|
|
|
343
347
|
onTransaction: () => {
|
|
344
348
|
this.refreshActiveStates();
|
|
345
349
|
},
|
|
346
|
-
});
|
|
350
|
+
}));
|
|
347
351
|
}
|
|
348
352
|
|
|
349
353
|
private destroyEditor(): void {
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
354
|
+
const ed = untracked(() => this.editor());
|
|
355
|
+
if (ed != null) {
|
|
356
|
+
ed.destroy();
|
|
357
|
+
this.editor.set(undefined);
|
|
353
358
|
}
|
|
354
359
|
}
|
|
355
360
|
|
|
356
|
-
private getEditorHtml(): string | undefined {
|
|
357
|
-
if (this.editor == null) return undefined;
|
|
358
|
-
return this.getEditorHtmlFrom(this.editor);
|
|
359
|
-
}
|
|
360
|
-
|
|
361
361
|
private getEditorHtmlFrom(editor: Editor): string | undefined {
|
|
362
362
|
const html = editor.getHTML();
|
|
363
363
|
if (editor.isEmpty) return undefined;
|
|
@@ -365,9 +365,10 @@ export class SdTiptapEditorControl {
|
|
|
365
365
|
}
|
|
366
366
|
|
|
367
367
|
execCmd(cmd: string): void {
|
|
368
|
-
|
|
368
|
+
const ed = this.editor();
|
|
369
|
+
if (ed == null) return;
|
|
369
370
|
|
|
370
|
-
const chain =
|
|
371
|
+
const chain = ed.chain().focus();
|
|
371
372
|
switch (cmd) {
|
|
372
373
|
case "bold":
|
|
373
374
|
chain.toggleBold().run();
|
|
@@ -428,9 +429,10 @@ export class SdTiptapEditorControl {
|
|
|
428
429
|
}
|
|
429
430
|
|
|
430
431
|
applyColor(color: string | undefined): void {
|
|
431
|
-
|
|
432
|
+
const ed = this.editor();
|
|
433
|
+
if (ed == null) return;
|
|
432
434
|
|
|
433
|
-
const chain =
|
|
435
|
+
const chain = ed.chain().focus();
|
|
434
436
|
if (this.colorPickerMode === "text") {
|
|
435
437
|
if (color !== undefined) {
|
|
436
438
|
chain.setColor(color).run();
|
|
@@ -448,27 +450,28 @@ export class SdTiptapEditorControl {
|
|
|
448
450
|
}
|
|
449
451
|
|
|
450
452
|
private refreshActiveStates(): void {
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
453
|
+
const ed = this.editor();
|
|
454
|
+
if (ed == null) return;
|
|
455
|
+
this.activeStates.set({
|
|
456
|
+
bold: ed.isActive("bold"),
|
|
457
|
+
italic: ed.isActive("italic"),
|
|
458
|
+
underline: ed.isActive("underline"),
|
|
459
|
+
strike: ed.isActive("strike"),
|
|
460
|
+
h1: ed.isActive("heading", { level: 1 }),
|
|
461
|
+
h2: ed.isActive("heading", { level: 2 }),
|
|
462
|
+
bulletList: ed.isActive("bulletList"),
|
|
463
|
+
orderedList: ed.isActive("orderedList"),
|
|
464
|
+
blockquote: ed.isActive("blockquote"),
|
|
465
|
+
codeBlock: ed.isActive("codeBlock"),
|
|
466
|
+
alignLeft: ed.isActive({ textAlign: "left" }),
|
|
467
|
+
alignCenter: ed.isActive({ textAlign: "center" }),
|
|
468
|
+
alignRight: ed.isActive({ textAlign: "right" }),
|
|
469
|
+
alignJustify: ed.isActive({ textAlign: "justify" }),
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
const textColor = ed.getAttributes("textStyle")["color"];
|
|
473
|
+
this.activeColor.set(typeof textColor === "string" ? textColor : "");
|
|
474
|
+
const bgColor = ed.getAttributes("highlight")["color"];
|
|
475
|
+
this.activeBgColor.set(typeof bgColor === "string" ? bgColor : "");
|
|
473
476
|
}
|
|
474
477
|
}
|
|
@@ -70,8 +70,9 @@ export class SdDateRangePicker {
|
|
|
70
70
|
if (this.periodType() === "월") {
|
|
71
71
|
const fromDate = this.from();
|
|
72
72
|
if (fromDate) {
|
|
73
|
-
|
|
74
|
-
this.
|
|
73
|
+
const firstOfMonth = fromDate.setDay(1);
|
|
74
|
+
this.from.set(firstOfMonth);
|
|
75
|
+
this.to.set(firstOfMonth.addMonths(1).addDays(-1));
|
|
75
76
|
} else {
|
|
76
77
|
this.to.set(undefined);
|
|
77
78
|
}
|
|
@@ -83,7 +84,7 @@ export class SdDateRangePicker {
|
|
|
83
84
|
handleFromDateChanged(): void {
|
|
84
85
|
if (this.periodType() === "월") {
|
|
85
86
|
const fromDate = this.from();
|
|
86
|
-
this.to.set(fromDate?.
|
|
87
|
+
this.to.set(fromDate?.setDay(1).addMonths(1).addDays(-1));
|
|
87
88
|
} else if (this.periodType() === "일") {
|
|
88
89
|
this.to.set(this.from());
|
|
89
90
|
} else if (
|
|
@@ -103,7 +103,7 @@ function createStringHandler(type: "text" | "password" | "email" | "color"): ITe
|
|
|
103
103
|
return errors;
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
if (type
|
|
106
|
+
if (type !== "color") {
|
|
107
107
|
const str = value as string;
|
|
108
108
|
if (opts.minlength !== undefined && opts.minlength > str.length) {
|
|
109
109
|
errors.push(`문자의 길이가 ${opts.minlength}보다 길거나 같아야 합니다.`);
|
|
@@ -213,10 +213,10 @@ function createFormatHandler(): ITextfieldTypeHandler {
|
|
|
213
213
|
(v, i, a) => a.indexOf(v) === i,
|
|
214
214
|
);
|
|
215
215
|
if (nonFormatChars != null && nonFormatChars.length > 0) {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
);
|
|
216
|
+
const escaped = nonFormatChars.map((ch) =>
|
|
217
|
+
ch === "]" || ch === "\\" || ch === "^" || ch === "-" ? "\\" + ch : ch,
|
|
218
|
+
).join("");
|
|
219
|
+
return raw.replace(new RegExp(`[${escaped}]`, "g"), "");
|
|
220
220
|
}
|
|
221
221
|
return raw;
|
|
222
222
|
},
|
|
@@ -371,6 +371,16 @@ function createDateTimeHandler(withSeconds: boolean): ITextfieldTypeHandler {
|
|
|
371
371
|
|
|
372
372
|
if (!(value instanceof DateTime)) {
|
|
373
373
|
errors.push("날짜 및 시간을 입력하세요");
|
|
374
|
+
return errors;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
const min = opts.min;
|
|
378
|
+
const max = opts.max;
|
|
379
|
+
if (min instanceof DateTime && min.tick > value.tick) {
|
|
380
|
+
errors.push(`${min}보다 크거나 같아야 합니다.`);
|
|
381
|
+
}
|
|
382
|
+
if (max instanceof DateTime && max.tick < value.tick) {
|
|
383
|
+
errors.push(`${max}보다 작거나 같아야 합니다.`);
|
|
374
384
|
}
|
|
375
385
|
|
|
376
386
|
return errors;
|
|
@@ -415,6 +425,16 @@ function createTimeHandler(withSeconds: boolean): ITextfieldTypeHandler {
|
|
|
415
425
|
|
|
416
426
|
if (!(value instanceof Time)) {
|
|
417
427
|
errors.push("시간을 입력하세요");
|
|
428
|
+
return errors;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
const min = opts.min;
|
|
432
|
+
const max = opts.max;
|
|
433
|
+
if (min instanceof Time && min.tick > value.tick) {
|
|
434
|
+
errors.push(`${min}보다 크거나 같아야 합니다.`);
|
|
435
|
+
}
|
|
436
|
+
if (max instanceof Time && max.tick < value.tick) {
|
|
437
|
+
errors.push(`${max}보다 작거나 같아야 합니다.`);
|
|
418
438
|
}
|
|
419
439
|
|
|
420
440
|
return errors;
|
|
@@ -379,6 +379,7 @@ export class SdTextfieldControl<K extends keyof TSdTextfieldTypes> {
|
|
|
379
379
|
}
|
|
380
380
|
|
|
381
381
|
onInputPaste(event: ClipboardEvent): void {
|
|
382
|
+
event.preventDefault();
|
|
382
383
|
const text = event.clipboardData?.getData("text/plain").trim();
|
|
383
384
|
if (text == null || text === "") {
|
|
384
385
|
this.value.set(undefined);
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ChangeDetectionStrategy,
|
|
3
3
|
Component,
|
|
4
|
+
DestroyRef,
|
|
4
5
|
effect,
|
|
5
6
|
ElementRef,
|
|
6
7
|
inject,
|
|
@@ -62,6 +63,7 @@ export class SdModalControl {
|
|
|
62
63
|
private readonly _elRef = inject(ElementRef<HTMLElement>);
|
|
63
64
|
private readonly _activatedModal = inject(SdActivatedModalProvider, { optional: true });
|
|
64
65
|
private readonly _systemConfig = inject(SdSystemConfigProvider, { optional: true });
|
|
66
|
+
private readonly _destroyRef = inject(DestroyRef);
|
|
65
67
|
|
|
66
68
|
open = model(false);
|
|
67
69
|
key = input<string | undefined>(undefined);
|
|
@@ -147,6 +149,11 @@ export class SdModalControl {
|
|
|
147
149
|
void this._saveConfig();
|
|
148
150
|
}
|
|
149
151
|
};
|
|
152
|
+
|
|
153
|
+
this._destroyRef.onDestroy(() => {
|
|
154
|
+
document.removeEventListener("mousemove", this._onDocumentMouseMove);
|
|
155
|
+
document.removeEventListener("mouseup", this._onDocumentMouseUp);
|
|
156
|
+
});
|
|
150
157
|
}
|
|
151
158
|
|
|
152
159
|
onResizeMouseDown(event: MouseEvent, dir: string): void {
|
|
@@ -208,7 +215,7 @@ export class SdModalControl {
|
|
|
208
215
|
}
|
|
209
216
|
|
|
210
217
|
private _requestClose(): void {
|
|
211
|
-
if (this._activatedModal !== null && !this._activatedModal.
|
|
218
|
+
if (this._activatedModal !== null && !this._activatedModal.canDeactiveFn()) {
|
|
212
219
|
return;
|
|
213
220
|
}
|
|
214
221
|
void this._saveConfig();
|
|
@@ -262,6 +269,8 @@ export class SdModalControl {
|
|
|
262
269
|
maxZ = z;
|
|
263
270
|
}
|
|
264
271
|
}
|
|
272
|
+
const currentZ = parseInt(hostEl.style.zIndex, 10);
|
|
273
|
+
if (currentZ >= maxZ) return;
|
|
265
274
|
hostEl.style.zIndex = String(maxZ + 1);
|
|
266
275
|
}
|
|
267
276
|
|
|
@@ -65,7 +65,7 @@ export interface ISdModalOptions {
|
|
|
65
65
|
export class SdActivatedModalProvider<T extends ISdModal<any> = ISdModal<any>> {
|
|
66
66
|
modalComponent = signal<any>(undefined);
|
|
67
67
|
contentComponent = signal<T | undefined>(undefined);
|
|
68
|
-
|
|
68
|
+
canDeactiveFn: () => boolean = () => true;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
/**
|
|
@@ -201,12 +201,13 @@ export class SdToastProvider {
|
|
|
201
201
|
const progressSignal = toastRef.instance.progress;
|
|
202
202
|
|
|
203
203
|
effect(
|
|
204
|
-
() => {
|
|
204
|
+
(onCleanup) => {
|
|
205
205
|
const val = progressSignal();
|
|
206
206
|
if (val >= 100) {
|
|
207
|
-
setTimeout(() => {
|
|
207
|
+
const timerId = setTimeout(() => {
|
|
208
208
|
this._dismissToast(toastRef);
|
|
209
209
|
}, 1000);
|
|
210
|
+
onCleanup(() => clearTimeout(timerId));
|
|
210
211
|
}
|
|
211
212
|
},
|
|
212
213
|
{ injector: toastRef.injector },
|
|
@@ -260,14 +261,19 @@ export class SdToastProvider {
|
|
|
260
261
|
toastRef.instance.open.set(false);
|
|
261
262
|
|
|
262
263
|
const el = toastRef.location.nativeElement as HTMLElement;
|
|
264
|
+
let fallbackId: ReturnType<typeof setTimeout>;
|
|
265
|
+
|
|
263
266
|
const onTransitionEnd = () => {
|
|
267
|
+
clearTimeout(fallbackId);
|
|
264
268
|
el.removeEventListener("transitionend", onTransitionEnd);
|
|
265
269
|
this._destroyToast(toastRef);
|
|
266
270
|
};
|
|
271
|
+
|
|
267
272
|
el.addEventListener("transitionend", onTransitionEnd);
|
|
268
273
|
|
|
269
274
|
// fallback: transition이 없는 경우 300ms 후 파괴
|
|
270
|
-
setTimeout(() => {
|
|
275
|
+
fallbackId = setTimeout(() => {
|
|
276
|
+
el.removeEventListener("transitionend", onTransitionEnd);
|
|
271
277
|
this._destroyToast(toastRef);
|
|
272
278
|
}, 300);
|
|
273
279
|
}
|
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
booleanAttribute,
|
|
3
3
|
ChangeDetectionStrategy,
|
|
4
4
|
Component,
|
|
5
|
+
computed,
|
|
5
6
|
input,
|
|
6
7
|
ViewEncapsulation,
|
|
7
8
|
} from "@angular/core";
|
|
@@ -22,7 +23,7 @@ import { PercentPipe } from "@angular/common";
|
|
|
22
23
|
<div class="_content tx-right">
|
|
23
24
|
{{ value() | percent: "1.0-2" }}
|
|
24
25
|
</div>
|
|
25
|
-
<div class="_progress" [style.width]="
|
|
26
|
+
<div class="_progress" [style.width]="_barWidth()"></div>
|
|
26
27
|
`,
|
|
27
28
|
styles: [
|
|
28
29
|
/* language=SCSS */ `
|
|
@@ -85,4 +86,6 @@ export class SdProgressControl {
|
|
|
85
86
|
>();
|
|
86
87
|
|
|
87
88
|
value = input.required<number>();
|
|
89
|
+
|
|
90
|
+
_barWidth = computed(() => Math.min(this.value() * 100, 100) + "%");
|
|
88
91
|
}
|