import { ChangeDetectionStrategy, Component, computed, inject, input, model, ViewEncapsulation, } from "@angular/core"; import { SdModalProvider } from "../../core/modal/sd-modal.provider"; import { SdToastProvider } from "../../core/toast/sd-toast.provider"; import { SdPromptModal } from "../../core/modal/sd-prompt-modal"; import { SdConfirmModal } from "../../core/modal/sd-confirm-modal"; import { injectSdSystemConfigResource } from "../../core/config/injectSdSystemConfigResource"; import { SdAnchor } from "../../controls/button/sd-anchor"; import { NgIcon } from "@ng-icons/core"; import { tablerStar, tablerDeviceFloppy, tablerX } from "@ng-icons/tabler-icons"; import { obj } from "@simplysm/core-common"; export interface SdStatePresetDef { name: string; state: any; } @Component({ selector: "sd-state-preset", changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, standalone: true, imports: [SdAnchor, NgIcon], template: `
@for (preset of _presets(); track preset.name) {
{{ preset.name }}
}
`, styles: [ /* language=SCSS */ ` sd-state-preset { display: inline-block; vertical-align: top; > ._sd-state-preset { display: flex; align-items: center; gap: var(--gap-sm); flex-wrap: wrap; > ._add-btn { line-height: var(--line-height); border: 1px solid transparent; padding: var(--gap-sm) var(--gap-default); } > ._preset-item { display: inline-flex; align-items: center; gap: var(--gap-xs); line-height: var(--line-height); border: 1px solid transparent; border-radius: var(--border-radius-lg); padding: var(--gap-sm) var(--gap-default); background: var(--theme-gray-lightest); &:hover { background: var(--theme-gray-lighter); } > sd-anchor { padding: 0 var(--gap-sm); } } } &[data-sd-size="sm"] > ._sd-state-preset { > ._add-btn { padding: var(--gap-xs) var(--gap-default); } > ._preset-item { padding: var(--gap-xs) var(--gap-default); } } &[data-sd-size="lg"] > ._sd-state-preset { > ._add-btn { padding: var(--gap-default) var(--gap-lg); } > ._preset-item { padding: var(--gap-default) var(--gap-lg); } } } `, ], host: { "[attr.data-sd-size]": "size()", }, }) export class SdStatePreset { private readonly _sdModal = inject(SdModalProvider); private readonly _sdToast = inject(SdToastProvider); key = input.required(); state = model(); size = input<"sm" | "lg">(); protected readonly tablerStar = tablerStar; protected readonly tablerDeviceFloppy = tablerDeviceFloppy; protected readonly tablerX = tablerX; private readonly _configResource = injectSdSystemConfigResource({ key: this.key, }); _presets = computed(() => this._configResource.value() ?? []); async onAddClick(): Promise { const name = await this._sdModal.showAsync( { title: "프리셋 추가", type: SdPromptModal, inputs: { message: "프리셋 이름을 입력하세요." }, }, { useCloseByBackdrop: false }, ); if (name == null) return; const currentPresets = this._presets(); if (currentPresets.some((p) => p.name === name)) { this._sdToast.warning("이미 존재하는 프리셋 이름입니다."); return; } const newPreset: SdStatePresetDef = { name, state: obj.clone(this.state()), }; this._configResource.set([...currentPresets, newPreset]); this._sdToast.info(`현재 상태가 '${name}'에 저장되었습니다.`); } onPresetClick(preset: SdStatePresetDef): void { const currentState = this.state(); if (obj.equal(currentState, preset.state)) return; this.state.set(obj.clone(preset.state)); } onSaveClick(preset: SdStatePresetDef): void { const currentPresets = this._presets(); const updated = currentPresets.map((p) => p.name === preset.name ? { ...p, state: obj.clone(this.state()) } : p, ); this._configResource.set(updated); this._sdToast.info(`현재 상태가 ${preset.name}에 저장되었습니다.`); } async onDeleteClick(preset: SdStatePresetDef): Promise { const confirmed = await this._sdModal.showAsync( { title: "프리셋 삭제", type: SdConfirmModal, inputs: { message: `저장된 '${preset.name}' 상태가 삭제됩니다.` }, }, { useCloseByBackdrop: false }, ); if (confirmed !== true) return; const currentPresets = this._presets(); const filtered = currentPresets.filter((p) => p.name !== preset.name); this._configResource.set(filtered); } }