입력 힌트 · ${this.metric?.name || ''}
값|라벨 (라벨 생략 시 값 사용)
import '@material/web/icon/icon.js' import { css, html, LitElement } from 'lit' import { customElement, property, state } from 'lit/decorators.js' import { CommonHeaderStyles } from '@operato/styles' /** * KpiMetric.meta.input(입력 위젯 힌트) 구조화 에디터. * * metric 관리 화면(kpi-metric-list-page)에서 팝업으로 열려 meta.input 을 편집한다. * raw JSON 을 직접 만지지 않고 위젯/필드를 폼으로 구성 → 내부적으로 meta 객체 생성. * meta 의 다른 키(kind/source…)는 보존하고 input 만 교체/삭제한다. * * - `metric-meta-save` detail:{ meta } — 저장 (widget 미선택 시 input 제거된 meta) * - `metric-meta-cancel` — 취소 */ @customElement('kpi-metric-meta-editor') export class KpiMetricMetaEditor extends LitElement { static styles = [ CommonHeaderStyles, css` :host { display: flex; flex-direction: column; height: 100%; box-sizing: border-box; width: 100%; min-width: 520px; font-size: 14px; color: #212529; } /* 스크롤되는 내용부 — 버튼은 아래에 고정 (kpi-metric-value-manual-entry-form 패턴) */ .content { flex: 1; overflow-y: auto; padding: 20px 24px 8px 24px; } h3 { margin: 0 0 4px 0; font-size: 15px; } .sub { color: #6b7684; margin-bottom: 14px; } .field { display: grid; grid-template-columns: 96px 1fr; align-items: center; gap: 8px 10px; margin-bottom: 10px; } label { color: #35618e; font-weight: 500; } input[type='text'], input[type='number'], select, textarea { background-color: var(--input-field-background, var(--md-sys-color-surface-container-highest)); color: var(--input-field-color, var(--md-sys-color-on-surface)); width: 100%; box-sizing: border-box; padding: 6px 8px; border: 1px solid #cdd5df; border-radius: 5px; font-size: 13px; } textarea { min-height: 72px; resize: vertical; font-family: monospace; } .checks { display: flex; gap: 16px; flex-wrap: wrap; margin: 4px 0 12px 0; } .checks label { display: inline-flex; align-items: center; gap: 5px; color: #212529; font-weight: 400; } .hint { color: #8b95a1; font-size: 11px; grid-column: 2; margin-top: -4px; } ` ] /** { name, meta, collectType } */ @property({ type: Object }) metric: any = {} @state() widget = '' @state() min = '' @state() max = '' @state() step = '' @state() decimals = '' @state() placeholder = '' @state() optionsText = '' @state() readonly = false @state() required = false @state() hasPlan = false connectedCallback() { super.connectedCallback() const input = this.metric?.meta?.input || {} this.widget = input.widget || '' this.min = input.min !== undefined ? String(input.min) : '' this.max = input.max !== undefined ? String(input.max) : '' this.step = input.step !== undefined ? String(input.step) : '' this.decimals = input.decimals !== undefined ? String(input.decimals) : '' this.placeholder = input.placeholder || '' this.optionsText = (input.options || []).map((o: any) => `${o.value}|${o.label ?? o.value}`).join('\n') this.readonly = !!input.readonly this.required = !!input.required this.hasPlan = !!input.hasPlan } render() { const w = this.widget return html`
값|라벨 (라벨 생략 시 값 사용)