@operato/data-grist 10.8.1 → 10.9.0

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 (40) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/src/data-card/data-card.d.ts +6 -0
  3. package/dist/src/data-card/data-card.js +12 -3
  4. package/dist/src/data-card/data-card.js.map +1 -1
  5. package/dist/src/data-grid/data-grid-body.d.ts +7 -0
  6. package/dist/src/data-grid/data-grid-body.js +15 -0
  7. package/dist/src/data-grid/data-grid-body.js.map +1 -1
  8. package/dist/src/data-grid/data-grid-field.d.ts +21 -0
  9. package/dist/src/data-grid/data-grid-field.js +311 -4
  10. package/dist/src/data-grid/data-grid-field.js.map +1 -1
  11. package/dist/src/data-grid/data-grid-header.js +16 -28
  12. package/dist/src/data-grid/data-grid-header.js.map +1 -1
  13. package/dist/src/data-grid/data-grid.d.ts +6 -0
  14. package/dist/src/data-grid/data-grid.js +9 -18
  15. package/dist/src/data-grid/data-grid.js.map +1 -1
  16. package/dist/src/data-grist.d.ts +25 -0
  17. package/dist/src/data-grist.js +74 -2
  18. package/dist/src/data-grist.js.map +1 -1
  19. package/dist/src/data-list/data-list.d.ts +6 -0
  20. package/dist/src/data-list/data-list.js +12 -3
  21. package/dist/src/data-list/data-list.js.map +1 -1
  22. package/dist/src/editors/ox-grist-editor-json5.d.ts +1 -0
  23. package/dist/src/editors/ox-grist-editor-json5.js +2 -0
  24. package/dist/src/editors/ox-grist-editor-json5.js.map +1 -1
  25. package/dist/src/editors/ox-grist-editor.d.ts +14 -0
  26. package/dist/src/editors/ox-grist-editor.js +36 -0
  27. package/dist/src/editors/ox-grist-editor.js.map +1 -1
  28. package/dist/src/editors/registry.d.ts +10 -0
  29. package/dist/src/editors/registry.js +16 -0
  30. package/dist/src/editors/registry.js.map +1 -1
  31. package/dist/test/row-hover-visible.test.js +26 -14
  32. package/dist/test/row-hover-visible.test.js.map +1 -1
  33. package/dist/tsconfig.tsbuildinfo +1 -1
  34. package/package.json +6 -6
  35. package/test/row-hover-visible.test.ts +30 -14
  36. package/translations/en.json +12 -10
  37. package/translations/ja.json +11 -9
  38. package/translations/ko.json +12 -10
  39. package/translations/ms.json +11 -9
  40. package/translations/zh.json +11 -9
@@ -41,6 +41,28 @@ const STYLE = css `
41
41
  color: var(--md-sys-color-on-background);
42
42
  }
43
43
 
44
+ /*
45
+ * ── 편집기도 칸과 같은 쪽으로 붙는다 (2026-09-05) ──────────────────────────
46
+ *
47
+ * 정렬은 host 에 놓인다(firstUpdated 의 this.style.textAlign). 그런데 입력 요소는 그것을
48
+ * 물려받지 못한다 — 브라우저 기본 스타일시트가 text-align: start 를 **직접 선언**해 두고,
49
+ * 직접 선언은 상속값을 이긴다.
50
+ *
51
+ * [재봤음] 2026-09-05 · 검사 명세의 「하한」 칸
52
+ * host text-align: right
53
+ * input text-align: start
54
+ *
55
+ * 그래서 숫자 칸은 읽을 때 오른쪽 끝에 있다가 고치려고 들어가면 왼쪽으로 튀었고, 값이 없는
56
+ * 칸에서는 캐럿이 왼쪽 끝에서 깜박였다 — 숫자가 설 자리와 어긋난다.
57
+ *
58
+ * inherit 라고 적어 두면 그 선언이 기본 선언을 밀어내고 host 의 값을 그대로 받는다.
59
+ */
60
+ :host > input,
61
+ :host > select,
62
+ :host > textarea {
63
+ text-align: inherit;
64
+ }
65
+
44
66
  :host > input[type='checkbox'] {
45
67
  width: initial;
46
68
  margin: 0;
@@ -174,6 +196,20 @@ let OxGristEditor = class OxGristEditor extends LitElement {
174
196
  }
175
197
  };
176
198
  OxGristEditor.styles = [STYLE];
199
+ /**
200
+ * 이 편집기가 **팝업을 여나.**
201
+ *
202
+ * ── 왜 필요한가 (2026-09-05) ────────────────────────────────────────────
203
+ * 팝업으로 고치는 칸은 글자 칸과 똑같이 생겼다. 열어 보기 전에는 고칠 수 있는지도, 어떻게
204
+ * 고치는지도 모른다. 사용자의 말 그대로 — 「실제 더블클릭을 하기 전에는 모르는 막막함」.
205
+ *
206
+ * 그래서 그런 칸에는 표식을 둔다. 어느 칸이 그런지는 **편집기가 스스로 밝힌다** — 칸이
207
+ * 목록을 들고 판정하면 팝업 편집기가 새로 생길 때마다 그 목록을 고쳐야 하고, 그러면
208
+ * 반드시 빠뜨린다.
209
+ *
210
+ * 팝업을 여는 편집기가 이 값을 참으로 둔다.
211
+ */
212
+ OxGristEditor.opensPopup = false;
177
213
  __decorate([
178
214
  property({ type: Object })
179
215
  ], OxGristEditor.prototype, "value", void 0);
@@ -1 +1 @@
1
- {"version":3,"file":"ox-grist-editor.js","sourceRoot":"","sources":["../../../src/editors/ox-grist-editor.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAkB,MAAM,KAAK,CAAA;AAC3D,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAE3D,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAA;AAGtE,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAE1D,MAAM,KAAK,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoEhB,CAAA;AAGM,IAAM,aAAa,GAAnB,MAAM,aAAc,SAAQ,UAAU;IAAtC;;QAIuB,WAAM,GAAiB,WAAW,CAAA;QAClC,WAAM,GAAgB,WAAW,CAAA;IA4H/D,CAAC;IArHC,MAAM;QACJ,OAAO,IAAI,CAAC,cAAc,CAAA;IAC5B,CAAC;IAED,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,UAAU,CAAC,iBAAgC,CAAA;IACzD,CAAC;IAED,IAAI,cAAc;QAChB,OAAO,KAAK,CAAA;IACd,CAAC;IAED,KAAK,CAAC,YAAY;;QAChB,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;QACrE,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,UAAU,EAAE,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;QACzE,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;QACxD,IAAI,CAAC,gBAAgB,CAAC,UAAU,EAAE,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;QAC9D,IAAI,CAAC,gBAAgB,CAAC,SAAS,EAAE,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;QAE5D,wFAAwF;QACxF,IAAI,CAAC,gBAAgB,CAAC,aAAa,EAAE,CAAC,CAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,eAAe,EAAE,CAAC,CAAA;QACvE,IAAI,CAAC,gBAAgB,CAAC,aAAa,EAAE,CAAC,CAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,eAAe,EAAE,CAAC,CAAA;QACvE,IAAI,CAAC,gBAAgB,CAAC,WAAW,EAAE,CAAC,CAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,eAAe,EAAE,CAAC,CAAA;QAErE,MAAM,EAAE,IAAI,GAAG,EAAE,EAAE,GAAG,IAAI,CAAC,MAAM,CAAA;QACjC,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAA;QAElD,IAAI,KAAK,EAAE,CAAC;YACV,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,KAAK,CAAA;YAC5B,IAAI,CAAC,KAAK,CAAC,aAAa,GAAG,KAAK,CAAA,CAAC,gBAAgB;QACnD,CAAC;QAED,IAAI,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;QAEpC,IAAI,OAAO,YAAY,IAAI,WAAW,IAAI,YAAY,EAAE,CAAC;YACvD,YAAY,GAAG,eAAe,CAAC,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;QAC3D,CAAC;QAED,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,eAAe,CAAC,YAAY,CAAC,CAAA;QAElE,oFAAoF;QACpF,MAAM,SAAS,GAAG,IAAI,CAAC,cAAc,KAAI,MAAA,IAAI,CAAC,KAAK,0CAAE,SAAS,CAAA,CAAA;QAC9D,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,YAAY,CAAC,CAAA;QAEjH,qBAAqB,CAAC,GAAG,EAAE;YACzB,IAAI,CAAC,KAAK,EAAE,CAAA;YACZ,CAAC,SAAS,IAAI,IAAI,CAAC,MAAM,EAAE,CAAA;QAC7B,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,MAAM;QACJ,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAA;QAC1B,IAAK,MAA2B,aAA3B,MAAM,uBAAN,MAAM,CAAuB,MAAM,EAAE,CAAC;YACzC,OAAQ,MAA2B,CAAC,MAAM,EAAE,CAAA;QAC9C,CAAC;IACH,CAAC;IAED,KAAK;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAA;QAC1B,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,CAAC,KAAK,EAAE,CAAA;QAChB,CAAC;IACH,CAAC;IAED,eAAe,CAAC,KAAU;QACxB,OAAO,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAA;IACnC,CAAC;IAED,gBAAgB,CAAC,KAAU;QACzB,OAAO,KAAK,CAAA;IACd,CAAC;IAED,WAAW;;QACT,oCAAoC;QACpC,IAAI,CAAA,MAAA,IAAI,CAAC,KAAK,0CAAE,SAAS,MAAK,IAAI;YAAE,OAAM;QAE1C,MAAM,EAAE,IAAI,GAAG,EAAE,EAAE,GAAG,IAAI,CAAC,MAAM,CAAA;QACjC,IAAI,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAA;QAE1C,IAAI,IAAI,CAAC,WAAW,KAAK,YAAY,EAAE,CAAC;YACtC,IAAI,CAAC,aAAa,CAChB,IAAI,WAAW,CAAC,cAAc,EAAE;gBAC9B,OAAO,EAAE,IAAI;gBACb,QAAQ,EAAE,IAAI;gBACd,MAAM,EAAE;oBACN,MAAM,EAAE,YAAY;oBACpB,KAAK,EAAE,IAAI,CAAC,WAAW;oBACvB,MAAM,EAAE,IAAI,CAAC,MAAM;oBACnB,MAAM,EAAE,IAAI,CAAC,MAAM;oBACnB,GAAG,EAAE,IAAI,CAAC,GAAG;iBACd;aACF,CAAC,CACH,CAAA;QACH,CAAC;IACH,CAAC;IAED,SAAS,CAAC,CAAQ;;QAChB,CAAC,CAAC,eAAe,EAAE,CAAA;QAEnB,MAAM,KAAK,GAAG,MAAC,CAAC,CAAC,MAAc,0CAAE,KAAK,CAAA;QAEtC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAA;IACjD,CAAC;IAED,UAAU,CAAC,CAAQ,IAAS,CAAC;IAE7B,QAAQ,CAAC,CAAQ;QACf,CAAC,CAAC,eAAe,EAAE,CAAA;IACrB,CAAC;IAED,WAAW,CAAC,CAAQ;QAClB,CAAC,CAAC,eAAe,EAAE,CAAA;IACrB,CAAC;IAED,IAAI,cAAc;QAChB,OAAO,IAAI,CAAA,EAAE,CAAA;IACf,CAAC;;AA/HM,oBAAM,GAAG,CAAC,KAAK,CAAC,AAAV,CAAU;AAEK;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;4CAAY;AACX;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;6CAAmC;AAClC;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;6CAAkC;AACjC;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;+CAAkB;AACjB;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;4CAAsB;AACrB;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;0CAAa;AAR7B,aAAa;IADzB,aAAa,CAAC,iBAAiB,CAAC;GACpB,aAAa,CAiIzB","sourcesContent":["import { css, html, LitElement, PropertyValues } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\n\nimport { ZERO_COLUMN, ZERO_RECORD } from '../configure/zero-config.js'\nimport { DataGridField } from '../data-grid/data-grid-field.js'\nimport { ColumnConfig, GristRecord } from '../types.js'\nimport { getDefaultValue } from '@operato/time-calculator'\n\nconst STYLE = css`\n :host {\n display: flex;\n\n align-items: center;\n\n width: 100%;\n height: 100%;\n\n border: 0;\n background-color: transparent;\n\n overflow: hidden;\n justify-content: var(--data-grid-field-justify-content, flex-start);\n }\n\n :host > * {\n display: flex;\n\n width: 100%;\n height: 100%;\n\n border: 0;\n background-color: transparent;\n\n box-sizing: border-box;\n\n align-items: center;\n }\n\n :host > style {\n display: none;\n }\n\n :host > input {\n color: var(--md-sys-color-on-background);\n }\n\n :host > input[type='checkbox'] {\n width: initial;\n margin: 0;\n }\n *:focus {\n outline: none;\n }\n\n input[type='file'] {\n opacity: 0%;\n }\n\n @media screen and (max-width: 460px) {\n :host > * {\n border: initial;\n background-color: initial;\n }\n\n *:focus {\n outline: none;\n }\n\n input,\n select,\n textarea {\n background-color: var(--input-field-background, var(--md-sys-color-surface-container-highest));\n color: var(--input-field-color, var(--md-sys-color-on-surface));\n font-size: 16px;\n }\n }\n`\n\n@customElement('ox-grist-editor')\nexport class OxGristEditor extends LitElement {\n static styles = [STYLE]\n\n @property({ type: Object }) value?: any\n @property({ type: Object }) column: ColumnConfig = ZERO_COLUMN\n @property({ type: Object }) record: GristRecord = ZERO_RECORD\n @property({ type: Number }) rowIndex?: number\n @property({ type: Object }) field?: DataGridField\n @property({ type: Number }) row?: number\n\n protected _dirtyValue?: any\n\n render() {\n return this.editorTemplate\n }\n\n get editor(): HTMLElement | null {\n return this.renderRoot.firstElementChild as HTMLElement\n }\n\n get inlineEditable() {\n return false\n }\n\n async firstUpdated() {\n this.renderRoot.addEventListener('change', this._onchange.bind(this))\n this.renderRoot.addEventListener('focusout', this._onfocusout.bind(this))\n this.addEventListener('click', this._onclick.bind(this))\n this.addEventListener('dblclick', this._ondblclick.bind(this))\n this.addEventListener('keydown', this._onkeydown.bind(this))\n\n /* editor mode 인 경우의 마우스 움직임이, grist-body의 이벤트 처리에 의해서 에디터를 리셋시킬 수 있으므로, 이벤트 전파를 막는다. */\n this.addEventListener('pointerdown', (e: Event) => e.stopPropagation())\n this.addEventListener('pointermove', (e: Event) => e.stopPropagation())\n this.addEventListener('pointerup', (e: Event) => e.stopPropagation())\n\n const { name = '' } = this.column\n const { align, defaultValue } = this.column.record\n\n if (align) {\n this.style.textAlign = align\n this.style.textAlignLast = align /* for select */\n }\n\n var currentValue = this.record[name]\n\n if (typeof currentValue == 'undefined' && defaultValue) {\n currentValue = getDefaultValue(defaultValue, this.record)\n }\n\n this.value = this._dirtyValue = this.formatForEditor(currentValue)\n\n // 입력을 위한 키를 누르면서 편집모드가 될때는 누른 키가 처음에 입력되도록, enter 같은 것을 눌러서 편집모드가 되면 현재 값으로 편집모드 전환\n const valueWith = this.inlineEditable && this.field?.valueWith\n this.value = this._dirtyValue = valueWith ? this.formatFromEditor(valueWith) : this.formatForEditor(currentValue)\n\n requestAnimationFrame(() => {\n this.focus()\n !valueWith && this.select()\n })\n }\n\n select() {\n const editor = this.editor\n if ((editor as HTMLInputElement)?.select) {\n return (editor as HTMLInputElement).select()\n }\n }\n\n focus() {\n const editor = this.editor\n if (editor) {\n editor.focus()\n }\n }\n\n formatForEditor(value: any): any {\n return value == null ? '' : value\n }\n\n formatFromEditor(value: any): any {\n return value\n }\n\n _onfocusout() {\n // paste시 field-change는 Body에서 하므로 X\n if (this.field?.isWorking === true) return\n\n const { name = '' } = this.column\n var currentValue = this.record[name] || ''\n\n if (this._dirtyValue !== currentValue) {\n this.dispatchEvent(\n new CustomEvent('field-change', {\n bubbles: true,\n composed: true,\n detail: {\n before: currentValue,\n after: this._dirtyValue,\n column: this.column,\n record: this.record,\n row: this.row\n }\n })\n )\n }\n }\n\n _onchange(e: Event): void {\n e.stopPropagation()\n\n const value = (e.target as any)?.value\n\n this._dirtyValue = this.formatFromEditor(value)\n }\n\n _onkeydown(e: Event): void {}\n\n _onclick(e: Event): void {\n e.stopPropagation()\n }\n\n _ondblclick(e: Event): void {\n e.stopPropagation()\n }\n\n get editorTemplate() {\n return html``\n }\n}\n"]}
1
+ {"version":3,"file":"ox-grist-editor.js","sourceRoot":"","sources":["../../../src/editors/ox-grist-editor.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, LitElement, PropertyValues } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\n\nimport { ZERO_COLUMN, ZERO_RECORD } from '../configure/zero-config.js'\nimport { DataGridField } from '../data-grid/data-grid-field.js'\nimport { ColumnConfig, GristRecord } from '../types.js'\nimport { getDefaultValue } from '@operato/time-calculator'\n\nconst STYLE = css`\n :host {\n display: flex;\n\n align-items: center;\n\n width: 100%;\n height: 100%;\n\n border: 0;\n background-color: transparent;\n\n overflow: hidden;\n justify-content: var(--data-grid-field-justify-content, flex-start);\n }\n\n :host > * {\n display: flex;\n\n width: 100%;\n height: 100%;\n\n border: 0;\n background-color: transparent;\n\n box-sizing: border-box;\n\n align-items: center;\n }\n\n :host > style {\n display: none;\n }\n\n :host > input {\n color: var(--md-sys-color-on-background);\n }\n\n /*\n * ── 편집기도 칸과 같은 쪽으로 붙는다 (2026-09-05) ──────────────────────────\n *\n * 정렬은 host 에 놓인다(firstUpdated 의 this.style.textAlign). 그런데 입력 요소는 그것을\n * 물려받지 못한다 — 브라우저 기본 스타일시트가 text-align: start 를 **직접 선언**해 두고,\n * 직접 선언은 상속값을 이긴다.\n *\n * [재봤음] 2026-09-05 · 검사 명세의 「하한」 칸\n * host text-align: right\n * input text-align: start\n *\n * 그래서 숫자 칸은 읽을 때 오른쪽 끝에 있다가 고치려고 들어가면 왼쪽으로 튀었고, 값이 없는\n * 칸에서는 캐럿이 왼쪽 끝에서 깜박였다 — 숫자가 설 자리와 어긋난다.\n *\n * inherit 라고 적어 두면 그 선언이 기본 선언을 밀어내고 host 의 값을 그대로 받는다.\n */\n :host > input,\n :host > select,\n :host > textarea {\n text-align: inherit;\n }\n\n :host > input[type='checkbox'] {\n width: initial;\n margin: 0;\n }\n *:focus {\n outline: none;\n }\n\n input[type='file'] {\n opacity: 0%;\n }\n\n @media screen and (max-width: 460px) {\n :host > * {\n border: initial;\n background-color: initial;\n }\n\n *:focus {\n outline: none;\n }\n\n input,\n select,\n textarea {\n background-color: var(--input-field-background, var(--md-sys-color-surface-container-highest));\n color: var(--input-field-color, var(--md-sys-color-on-surface));\n font-size: 16px;\n }\n }\n`\n\n@customElement('ox-grist-editor')\nexport class OxGristEditor extends LitElement {\n static styles = [STYLE]\n\n /**\n * 이 편집기가 **팝업을 여나.**\n *\n * ── 왜 필요한가 (2026-09-05) ────────────────────────────────────────────\n * 팝업으로 고치는 칸은 글자 칸과 똑같이 생겼다. 열어 보기 전에는 고칠 수 있는지도, 어떻게\n * 고치는지도 모른다. 사용자의 말 그대로 — 「실제 더블클릭을 하기 전에는 모르는 막막함」.\n *\n * 그래서 그런 칸에는 표식을 둔다. 어느 칸이 그런지는 **편집기가 스스로 밝힌다** — 칸이\n * 목록을 들고 판정하면 팝업 편집기가 새로 생길 때마다 그 목록을 고쳐야 하고, 그러면\n * 반드시 빠뜨린다.\n *\n * 팝업을 여는 편집기가 이 값을 참으로 둔다.\n */\n static opensPopup: boolean = false\n\n @property({ type: Object }) value?: any\n @property({ type: Object }) column: ColumnConfig = ZERO_COLUMN\n @property({ type: Object }) record: GristRecord = ZERO_RECORD\n @property({ type: Number }) rowIndex?: number\n @property({ type: Object }) field?: DataGridField\n @property({ type: Number }) row?: number\n\n protected _dirtyValue?: any\n\n render() {\n return this.editorTemplate\n }\n\n get editor(): HTMLElement | null {\n return this.renderRoot.firstElementChild as HTMLElement\n }\n\n get inlineEditable() {\n return false\n }\n\n async firstUpdated() {\n this.renderRoot.addEventListener('change', this._onchange.bind(this))\n this.renderRoot.addEventListener('focusout', this._onfocusout.bind(this))\n this.addEventListener('click', this._onclick.bind(this))\n this.addEventListener('dblclick', this._ondblclick.bind(this))\n this.addEventListener('keydown', this._onkeydown.bind(this))\n\n /* editor mode 인 경우의 마우스 움직임이, grist-body의 이벤트 처리에 의해서 에디터를 리셋시킬 수 있으므로, 이벤트 전파를 막는다. */\n this.addEventListener('pointerdown', (e: Event) => e.stopPropagation())\n this.addEventListener('pointermove', (e: Event) => e.stopPropagation())\n this.addEventListener('pointerup', (e: Event) => e.stopPropagation())\n\n const { name = '' } = this.column\n const { align, defaultValue } = this.column.record\n\n if (align) {\n this.style.textAlign = align\n this.style.textAlignLast = align /* for select */\n }\n\n var currentValue = this.record[name]\n\n if (typeof currentValue == 'undefined' && defaultValue) {\n currentValue = getDefaultValue(defaultValue, this.record)\n }\n\n this.value = this._dirtyValue = this.formatForEditor(currentValue)\n\n // 입력을 위한 키를 누르면서 편집모드가 될때는 누른 키가 처음에 입력되도록, enter 같은 것을 눌러서 편집모드가 되면 현재 값으로 편집모드 전환\n const valueWith = this.inlineEditable && this.field?.valueWith\n this.value = this._dirtyValue = valueWith ? this.formatFromEditor(valueWith) : this.formatForEditor(currentValue)\n\n requestAnimationFrame(() => {\n this.focus()\n !valueWith && this.select()\n })\n }\n\n select() {\n const editor = this.editor\n if ((editor as HTMLInputElement)?.select) {\n return (editor as HTMLInputElement).select()\n }\n }\n\n focus() {\n const editor = this.editor\n if (editor) {\n editor.focus()\n }\n }\n\n formatForEditor(value: any): any {\n return value == null ? '' : value\n }\n\n formatFromEditor(value: any): any {\n return value\n }\n\n _onfocusout() {\n // paste시 field-change는 Body에서 하므로 X\n if (this.field?.isWorking === true) return\n\n const { name = '' } = this.column\n var currentValue = this.record[name] || ''\n\n if (this._dirtyValue !== currentValue) {\n this.dispatchEvent(\n new CustomEvent('field-change', {\n bubbles: true,\n composed: true,\n detail: {\n before: currentValue,\n after: this._dirtyValue,\n column: this.column,\n record: this.record,\n row: this.row\n }\n })\n )\n }\n }\n\n _onchange(e: Event): void {\n e.stopPropagation()\n\n const value = (e.target as any)?.value\n\n this._dirtyValue = this.formatFromEditor(value)\n }\n\n _onkeydown(e: Event): void {}\n\n _onclick(e: Event): void {\n e.stopPropagation()\n }\n\n _ondblclick(e: Event): void {\n e.stopPropagation()\n }\n\n get editorTemplate() {\n return html``\n }\n}\n"]}
@@ -9,4 +9,14 @@ export declare function getEditors(): {
9
9
  new (): OxGristEditor;
10
10
  };
11
11
  };
12
+ /**
13
+ * 이 열이 **팝업으로 고쳐지나.**
14
+ *
15
+ * 칸이 표식을 붙일지 판정할 때 쓴다. 편집기가 스스로 밝힌 값을 그대로 읽는다 — 칸이 목록을
16
+ * 들고 있으면 팝업 편집기가 새로 등록될 때마다 그 목록을 고쳐야 하고, 그러면 빠뜨린다.
17
+ *
18
+ * 편집기를 함수로 직접 준 열은 판정하지 않는다. 그 함수가 무엇을 여는지 알 길이 없고,
19
+ * 지어내는 것보다 표식을 안 붙이는 편이 낫다.
20
+ */
21
+ export declare function editorOpensPopup(type: unknown): boolean;
12
22
  export declare function getEditor(type: string): FieldEditor;
@@ -61,6 +61,22 @@ export function unregisterEditor(type) {
61
61
  export function getEditors() {
62
62
  return { ...EDITORS };
63
63
  }
64
+ /**
65
+ * 이 열이 **팝업으로 고쳐지나.**
66
+ *
67
+ * 칸이 표식을 붙일지 판정할 때 쓴다. 편집기가 스스로 밝힌 값을 그대로 읽는다 — 칸이 목록을
68
+ * 들고 있으면 팝업 편집기가 새로 등록될 때마다 그 목록을 고쳐야 하고, 그러면 빠뜨린다.
69
+ *
70
+ * 편집기를 함수로 직접 준 열은 판정하지 않는다. 그 함수가 무엇을 여는지 알 길이 없고,
71
+ * 지어내는 것보다 표식을 안 붙이는 편이 낫다.
72
+ */
73
+ export function editorOpensPopup(type) {
74
+ var _a;
75
+ if (typeof type !== 'string') {
76
+ return false;
77
+ }
78
+ return !!((_a = EDITORS[type]) === null || _a === void 0 ? void 0 : _a.opensPopup);
79
+ }
64
80
  export function getEditor(type) {
65
81
  if (typeof type == 'function') {
66
82
  return type;
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/editors/registry.ts"],"names":[],"mappings":"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","sourcesContent":["import { DataGridField } from '../data-grid/data-grid-field.js'\nimport { ColumnConfig, FieldEditor, GristRecord } from '../types.js'\nimport { OxGristEditor } from './ox-grist-editor.js'\nimport { OxGristEditorCheckbox } from './ox-grist-editor-checkbox.js'\nimport { OxGristEditorColor } from './ox-grist-editor-color.js'\nimport { OxGristEditorDate } from './ox-grist-editor-date.js'\nimport { OxGristEditorDateTime } from './ox-grist-editor-datetime.js'\nimport { OxGristEditorEmail } from './ox-grist-editor-email.js'\nimport { OxGristEditorFile } from './ox-grist-editor-file.js'\nimport { OxGristEditorImage } from './ox-grist-editor-image.js'\nimport { OxGristEditorMonth } from './ox-grist-editor-month.js'\nimport { OxGristEditorMultipleSelect } from './ox-grist-editor-multiple-select.js'\nimport { OxGristEditorNumber } from './ox-grist-editor-number.js'\nimport { OxGristEditorPassword } from './ox-grist-editor-password.js'\nimport { OxGristEditorSelect } from './ox-grist-editor-select.js'\nimport { OxGristEditorTel } from './ox-grist-editor-tel.js'\nimport { OxGristEditorText } from './ox-grist-editor-text.js'\nimport { OxGristEditorTextarea } from './ox-grist-editor-textarea.js'\nimport { OxGristEditorTime } from './ox-grist-editor-time.js'\nimport { OxGristEditorTree } from './ox-grist-editor-tree.js'\nimport { OxGristEditorWeek } from './ox-grist-editor-week.js'\nimport { OxGristEditorVarname } from './ox-grist-editor-varname.js'\nimport { OxGristEditorJson5 } from './ox-grist-editor-json5.js'\nimport { OxGristEditorTimezone } from './ox-grist-editor-timezone.js'\nimport { OxGristEditorSecret } from './ox-grist-editor-secret.js'\nimport { OxGristEditorTextSelect } from './ox-grist-editor-text-select.js'\n\nvar EDITORS: { [name: string]: { new (): OxGristEditor } } = {\n string: OxGristEditorText,\n text: OxGristEditorText,\n textarea: OxGristEditorTextarea,\n email: OxGristEditorEmail,\n tel: OxGristEditorTel,\n password: OxGristEditorPassword,\n integer: OxGristEditorNumber,\n float: OxGristEditorNumber,\n number: OxGristEditorNumber,\n select: OxGristEditorSelect,\n boolean: OxGristEditorCheckbox,\n checkbox: OxGristEditorCheckbox,\n month: OxGristEditorMonth,\n week: OxGristEditorWeek,\n date: OxGristEditorDate,\n time: OxGristEditorTime,\n datetime: OxGristEditorDateTime,\n color: OxGristEditorColor,\n progress: OxGristEditorNumber,\n link: OxGristEditorText,\n image: OxGristEditorImage,\n file: OxGristEditorFile,\n 'string[]': OxGristEditorMultipleSelect,\n tree: OxGristEditorTree,\n varname: OxGristEditorVarname,\n json5: OxGristEditorJson5,\n timezone: OxGristEditorTimezone,\n secret: OxGristEditorSecret,\n 'text-select': OxGristEditorTextSelect\n}\n\nexport function registerEditor(type: string, editor: { new (): OxGristEditor }) {\n EDITORS[type] = editor\n}\n\nexport function unregisterEditor(type: string) {\n delete EDITORS[type]\n}\n\nexport function getEditors(): { [name: string]: { new (): OxGristEditor } } {\n return { ...EDITORS }\n}\n\nexport function getEditor(type: string): FieldEditor {\n if (typeof type == 'function') {\n return type\n }\n\n return function (value: any, column: ColumnConfig, record: GristRecord, rowIndex: number, field: DataGridField) {\n var clazz = EDITORS[type || 'text'] || OxGristEditorText\n\n var element = new clazz()\n\n element.value = value\n element.record = record\n element.column = column\n element.row = rowIndex\n element.field = field\n\n return element\n }\n}\n"]}
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/editors/registry.ts"],"names":[],"mappings":"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","sourcesContent":["import { DataGridField } from '../data-grid/data-grid-field.js'\nimport { ColumnConfig, FieldEditor, GristRecord } from '../types.js'\nimport { OxGristEditor } from './ox-grist-editor.js'\nimport { OxGristEditorCheckbox } from './ox-grist-editor-checkbox.js'\nimport { OxGristEditorColor } from './ox-grist-editor-color.js'\nimport { OxGristEditorDate } from './ox-grist-editor-date.js'\nimport { OxGristEditorDateTime } from './ox-grist-editor-datetime.js'\nimport { OxGristEditorEmail } from './ox-grist-editor-email.js'\nimport { OxGristEditorFile } from './ox-grist-editor-file.js'\nimport { OxGristEditorImage } from './ox-grist-editor-image.js'\nimport { OxGristEditorMonth } from './ox-grist-editor-month.js'\nimport { OxGristEditorMultipleSelect } from './ox-grist-editor-multiple-select.js'\nimport { OxGristEditorNumber } from './ox-grist-editor-number.js'\nimport { OxGristEditorPassword } from './ox-grist-editor-password.js'\nimport { OxGristEditorSelect } from './ox-grist-editor-select.js'\nimport { OxGristEditorTel } from './ox-grist-editor-tel.js'\nimport { OxGristEditorText } from './ox-grist-editor-text.js'\nimport { OxGristEditorTextarea } from './ox-grist-editor-textarea.js'\nimport { OxGristEditorTime } from './ox-grist-editor-time.js'\nimport { OxGristEditorTree } from './ox-grist-editor-tree.js'\nimport { OxGristEditorWeek } from './ox-grist-editor-week.js'\nimport { OxGristEditorVarname } from './ox-grist-editor-varname.js'\nimport { OxGristEditorJson5 } from './ox-grist-editor-json5.js'\nimport { OxGristEditorTimezone } from './ox-grist-editor-timezone.js'\nimport { OxGristEditorSecret } from './ox-grist-editor-secret.js'\nimport { OxGristEditorTextSelect } from './ox-grist-editor-text-select.js'\n\nvar EDITORS: { [name: string]: { new (): OxGristEditor } } = {\n string: OxGristEditorText,\n text: OxGristEditorText,\n textarea: OxGristEditorTextarea,\n email: OxGristEditorEmail,\n tel: OxGristEditorTel,\n password: OxGristEditorPassword,\n integer: OxGristEditorNumber,\n float: OxGristEditorNumber,\n number: OxGristEditorNumber,\n select: OxGristEditorSelect,\n boolean: OxGristEditorCheckbox,\n checkbox: OxGristEditorCheckbox,\n month: OxGristEditorMonth,\n week: OxGristEditorWeek,\n date: OxGristEditorDate,\n time: OxGristEditorTime,\n datetime: OxGristEditorDateTime,\n color: OxGristEditorColor,\n progress: OxGristEditorNumber,\n link: OxGristEditorText,\n image: OxGristEditorImage,\n file: OxGristEditorFile,\n 'string[]': OxGristEditorMultipleSelect,\n tree: OxGristEditorTree,\n varname: OxGristEditorVarname,\n json5: OxGristEditorJson5,\n timezone: OxGristEditorTimezone,\n secret: OxGristEditorSecret,\n 'text-select': OxGristEditorTextSelect\n}\n\nexport function registerEditor(type: string, editor: { new (): OxGristEditor }) {\n EDITORS[type] = editor\n}\n\nexport function unregisterEditor(type: string) {\n delete EDITORS[type]\n}\n\nexport function getEditors(): { [name: string]: { new (): OxGristEditor } } {\n return { ...EDITORS }\n}\n\n/**\n * 이 열이 **팝업으로 고쳐지나.**\n *\n * 칸이 표식을 붙일지 판정할 때 쓴다. 편집기가 스스로 밝힌 값을 그대로 읽는다 — 칸이 목록을\n * 들고 있으면 팝업 편집기가 새로 등록될 때마다 그 목록을 고쳐야 하고, 그러면 빠뜨린다.\n *\n * 편집기를 함수로 직접 준 열은 판정하지 않는다. 그 함수가 무엇을 여는지 알 길이 없고,\n * 지어내는 것보다 표식을 안 붙이는 편이 낫다.\n */\nexport function editorOpensPopup(type: unknown): boolean {\n if (typeof type !== 'string') {\n return false\n }\n\n return !!(EDITORS[type] as any)?.opensPopup\n}\n\nexport function getEditor(type: string): FieldEditor {\n if (typeof type == 'function') {\n return type\n }\n\n return function (value: any, column: ColumnConfig, record: GristRecord, rowIndex: number, field: DataGridField) {\n var clazz = EDITORS[type || 'text'] || OxGristEditorText\n\n var element = new clazz()\n\n element.value = value\n element.record = record\n element.column = column\n element.row = rowIndex\n element.field = field\n\n return element\n }\n}\n"]}
@@ -1,5 +1,4 @@
1
- import { expect, fixture } from '@open-wc/testing';
2
- import { html } from 'lit';
1
+ import { expect } from '@open-wc/testing';
3
2
  import { DataGrist } from '../src/data-grist.js';
4
3
  import { DataGrid } from '../src/data-grid/data-grid.js';
5
4
  /**
@@ -59,14 +58,27 @@ const 대비 = (a, b) => {
59
58
  const [x, y] = [lum(a), lum(b)].sort((p, q) => q - p);
60
59
  return (x + 0.05) / (y + 0.05);
61
60
  };
62
- /** 그 팔레트에서 선언값이 실제로 무슨 색이 되는지 — 브라우저에게 계산시킨다. */
63
- async function 칠해보다(값, 팔레트) {
64
- const probe = (await fixture(html `
65
- <div
66
- style="${팔레트.사다리} --grid-record-background-color:${팔레트.줄}; --grid-record-color:${팔레트.잉크}; background-color:${값};"
67
- ></div>
68
- `));
69
- return getComputedStyle(probe).backgroundColor;
61
+ /**
62
+ * 팔레트에서 선언값이 실제로 무슨 색이 되는지 — 브라우저에게 계산시킨다.
63
+ *
64
+ * ── 기다리지 않는다 (2026-09-05) ──────────────────────────────────────────
65
+ * 전에는 fixture() 로 세우고 await 했다. 잴 것은 div 하나의 계산된 색뿐이라 렌더링을 기다릴
66
+ * 이유가 없는데, 그 await 가 mocha 의 2초 문턱에 걸려 **기계가 바쁠 때만 붉어졌다** —
67
+ * 빌드와 웹팩이 함께 돌던 동안 어두운 팔레트 두 건이 시간초과로 넘어갔고, 혼자 돌리면 초록이었다.
68
+ * 색 판정은 한 번도 실패한 적이 없다.
69
+ *
70
+ * 붙이고 재고 떼는 것으로 충분하다. 기다림이 없으니 부하와 무관하게 같은 답이 나온다.
71
+ */
72
+ function 칠해보다(값, 팔레트) {
73
+ const probe = document.createElement('div');
74
+ probe.setAttribute('style', `${팔레트.사다리} --grid-record-background-color:${팔레트.줄}; --grid-record-color:${팔레트.잉크}; background-color:${값};`);
75
+ document.body.appendChild(probe);
76
+ try {
77
+ return getComputedStyle(probe).backgroundColor;
78
+ }
79
+ finally {
80
+ probe.remove();
81
+ }
70
82
  }
71
83
  /**
72
84
  * operato-plant 의 두 팔레트에서 실제로 쓰는 값이다.
@@ -101,13 +113,13 @@ describe('마우스가 지나는 줄이 보인다', () => {
101
113
  expect(값, '--grid-record-hover-background-color 를 못 찾았다').to.be.a('string');
102
114
  });
103
115
  Object.entries(팔레트들).forEach(([이름, 팔레트]) => {
104
- it(`${이름} 팔레트에서 줄과 띠가 갈린다`, async () => {
105
- const 띠 = await 칠해보다(값, 팔레트);
116
+ it(`${이름} 팔레트에서 줄과 띠가 갈린다`, () => {
117
+ const 띠 = 칠해보다(값, 팔레트);
106
118
  const 벌어짐 = 대비(띠, 팔레트.줄);
107
119
  expect(벌어짐, `줄 ${팔레트.줄} · 띠 ${픽셀(띠).join(' ')} → 대비 ${벌어짐.toFixed(3)}. 이만큼이면 마우스가 어느 줄에 있는지 안 보인다`).to.be.greaterThan(최소분리);
108
120
  });
109
- it(`${이름} 팔레트에서 띠가 줄을 덮지 않는다 — 옅어야 한다`, async () => {
110
- const 띠 = await 칠해보다(값, 팔레트);
121
+ it(`${이름} 팔레트에서 띠가 줄을 덮지 않는다 — 옅어야 한다`, () => {
122
+ const 띠 = 칠해보다(값, 팔레트);
111
123
  expect(대비(띠, 팔레트.줄), '고른 줄과 같은 무게가 되면 무엇이 골라졌는지 흐려진다').to.be.lessThan(1.6);
112
124
  });
113
125
  });
@@ -1 +1 @@
1
- {"version":3,"file":"row-hover-visible.test.js","sourceRoot":"","sources":["../../test/row-hover-visible.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect, fixture } from '@open-wc/testing'\nimport { html } from 'lit'\n\nimport { DataGrist } from '../src/data-grist.js'\nimport { DataGrid } from '../src/data-grid/data-grid.js'\n\n/**\n * 마우스가 지나는 줄이 **보이는가.**\n *\n * ## 왜 이 시험이 따로 있나\n *\n * `fixed-column-opaque.test.ts` 는 색이 **같은지**를 잰다. 그것만으로는 「띠가 보인다」를 못 지킨다 —\n * 줄과 띠가 나란히 안 보여도 그 시험은 전부 초록이다. 실제로 그랬다.\n *\n * ```\n * [재봤음] 2026-09-04 · operato-plant 인원 화면(어두운 모드)\n * 줄 rgb(30 34 40) · 띠 rgb(26 30 35) → 대비 1.048 보이지 않는다\n * 그때 고정 열 시험 열여섯 건은 전부 초록이었다\n * ```\n *\n * 원인은 색이 아니라 **어디서 색을 가져오나**였다. 면 사다리에서 한 단 옮겨 오면, 그 사다리의\n * 밝기 순서가 앱마다 맞아야 한다. operato-plant 의 어두운 팔레트는 그 순서가 밝은 모드와 같아서\n * 띠가 줄보다 어두워지는 쪽으로 4단 움직였을 뿐이다.\n *\n * 그래서 지금은 **줄 자신의 색에서 만든다.** 이 시험은 그 규칙이 두 테마에서 다 서는지를 잰다.\n */\n\n/** 스타일 뭉치에서 그 토큰의 선언값을 꺼낸다 — 시험이 값을 베끼지 않게. */\nfunction 선언값(styles: unknown, token: string) {\n const cssText = (Array.isArray(styles) ? styles : [styles])\n .map(style => String((style as { cssText?: string })?.cssText ?? style))\n .join('\\n')\n\n /* 여러 줄에 걸친 color-mix(...) 도 담기게 다음 세미콜론까지 가져온다. */\n const found = cssText.match(new RegExp(`${token}\\\\s*:\\\\s*([^;]+);`))\n\n return found?.[1].replace(/\\s+/g, ' ').trim()\n}\n\n/**\n * 색 문자열을 0~255 세 값으로 만든다 — **캔버스에 칠해서 읽는다.**\n *\n * 문자열을 직접 파싱하면 안 된다. `color-mix` 는 계산되면 `color(srgb 0.954 0.957 0.958)` 로\n * 나오고, 그 숫자를 0~255 로 읽으면 거의 검정이 된다. 실제로 그렇게 한 번 재서 이 시험이\n * 「대비 20.9」라는 헛것을 냈다. 캔버스는 어떤 표기든 브라우저가 칠한 픽셀로 돌려준다.\n */\nconst 칠 = document.createElement('canvas').getContext('2d', { willReadFrequently: true })!\n\nfunction 픽셀(color: string) {\n 칠.clearRect(0, 0, 1, 1)\n 칠.fillStyle = '#000'\n 칠.fillStyle = color\n 칠.fillRect(0, 0, 1, 1)\n\n const [r, g, b] = 칠.getImageData(0, 0, 1, 1).data\n\n return [r, g, b]\n}\n\nconst lum = (color: string) => {\n const [r, g, b] = 픽셀(color)\n const f = (v: number) => {\n const x = v / 255\n return x <= 0.03928 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4)\n }\n\n return 0.2126 * f(r) + 0.7152 * f(g) + 0.0722 * f(b)\n}\n\nconst 대비 = (a: string, b: string) => {\n const [x, y] = [lum(a), lum(b)].sort((p, q) => q - p)\n return (x + 0.05) / (y + 0.05)\n}\n\n/** 그 팔레트에서 선언값이 실제로 무슨 색이 되는지 — 브라우저에게 계산시킨다. */\nasync function 칠해보다(값: string, 팔레트: { 줄: string; 잉크: string; 사다리: string }) {\n const probe = (await fixture(html`\n <div\n style=\"${팔레트.사다리} --grid-record-background-color:${팔레트.줄}; --grid-record-color:${팔레트.잉크}; background-color:${값};\"\n ></div>\n `)) as HTMLElement\n\n return getComputedStyle(probe).backgroundColor\n}\n\n/**\n * operato-plant 의 두 팔레트에서 실제로 쓰는 값이다.\n *\n * 면 사다리도 함께 준다 — 선언이 사다리에서 색을 가져오는 방식으로 되돌아가도 **그 앱에서 실제로\n * 나오는 색**으로 재기 위해서다. 주지 않으면 리터럴 폴백으로 떨어져 두 테마가 같은 색이 되고,\n * 어두운 모드에서 띠가 사라지던 그 일을 이 시험이 못 잡는다.\n */\nconst 팔레트들 = {\n 밝은: {\n 줄: 'rgb(255, 255, 255)',\n 잉크: 'rgb(23, 36, 44)',\n 사다리: '--md-sys-color-surface-container-lowest: rgb(255,255,255); --md-sys-color-surface-container-low: rgb(247,248,250);'\n },\n 어두운: {\n 줄: 'rgb(30, 34, 40)',\n 잉크: 'rgb(228, 232, 236)',\n 사다리: '--md-sys-color-surface-container-lowest: rgb(30,34,40); --md-sys-color-surface-container-low: rgb(26,30,35);'\n }\n}\n\n/**\n * 얼마나 벌어져야 보이나.\n *\n * 지나는 줄 표시는 **훨씬 옅어야 한다** — 고른 줄과 같은 무게로 그리면 무엇이 골라졌는지 흐려진다.\n * 그래서 큰 값을 요구하지 않는다. 다만 1.05 는 눈에 안 들어온다(위 [재봤음]). 눈으로 확인한 값이\n * 1.11 · 1.14 이므로 그 아래에 문턱을 둔다.\n */\nconst 최소분리 = 1.08\n\ndescribe('마우스가 지나는 줄이 보인다', () => {\n const 값 = 선언값(DataGrist.styles, '--grid-record-hover-background-color')\n\n it('그 토큰이 선언되어 있다 — 이름이 바뀌면 아래 시험들이 헛돈다', () => {\n expect(값, '--grid-record-hover-background-color 를 못 찾았다').to.be.a('string')\n })\n\n Object.entries(팔레트들).forEach(([이름, 팔레트]) => {\n it(`${이름} 팔레트에서 줄과 띠가 갈린다`, async () => {\n const 띠 = await 칠해보다(값!, 팔레트)\n const 벌어짐 = 대비(띠, 팔레트.줄)\n\n expect(\n 벌어짐,\n `줄 ${팔레트.줄} · 띠 ${픽셀(띠).join(' ')} → 대비 ${벌어짐.toFixed(3)}. 이만큼이면 마우스가 어느 줄에 있는지 안 보인다`\n ).to.be.greaterThan(최소분리)\n })\n\n it(`${이름} 팔레트에서 띠가 줄을 덮지 않는다 — 옅어야 한다`, async () => {\n const 띠 = await 칠해보다(값!, 팔레트)\n\n expect(대비(띠, 팔레트.줄), '고른 줄과 같은 무게가 되면 무엇이 골라졌는지 흐려진다').to.be.lessThan(1.6)\n })\n })\n\n /**\n * **줄 상태의 색을 `transparent` 로 두지 않는다.**\n *\n * 그 값은 「칠하지 않는다」가 아니라 「칸의 바탕을 지운다」다. 지워진 자리에는 표 뒤에 있는 것이\n * 비치고, 그것은 표가 정한 색이 아니다 — 어두운 페이지에 표를 놓으면 지나는 줄이 검게 된다.\n * 그리고 고정 열은 불투명해야 하므로 구멍이 안 뚫려, 줄 하나가 두 색으로 갈린다.\n *\n * 실제로 고치는 표가 그렇게 두고 있었다. 앞의 시험들은 **자기가 세운 표에 자기가 값을 넣으므로**\n * 이것을 못 잡는다 — 그 줄이 되돌아와도 초록이다. 그래서 선언 자체를 본다.\n */\n ;[\n ['DataGrist', DataGrist.styles],\n ['DataGrid', DataGrid.styles]\n ].forEach(([이름, styles]) => {\n it(`${이름} 이 줄 상태의 색을 비우지 않는다`, () => {\n const cssText = (styles as unknown[])\n .map(style => String((style as { cssText?: string })?.cssText ?? style))\n .join('\\n')\n\n const 비운것 = cssText.match(/--grid-record-[a-z-]*background-color\\s*:\\s*transparent/g)\n\n expect(비운것, `${비운것?.join(' · ')} — 비우는 대신 그 줄의 색을 주어야 한다`).to.be.null\n })\n })\n})\n"]}
1
+ {"version":3,"file":"row-hover-visible.test.js","sourceRoot":"","sources":["../../test/row-hover-visible.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect } from '@open-wc/testing'\n\nimport { DataGrist } from '../src/data-grist.js'\nimport { DataGrid } from '../src/data-grid/data-grid.js'\n\n/**\n * 마우스가 지나는 줄이 **보이는가.**\n *\n * ## 왜 이 시험이 따로 있나\n *\n * `fixed-column-opaque.test.ts` 는 색이 **같은지**를 잰다. 그것만으로는 「띠가 보인다」를 못 지킨다 —\n * 줄과 띠가 나란히 안 보여도 그 시험은 전부 초록이다. 실제로 그랬다.\n *\n * ```\n * [재봤음] 2026-09-04 · operato-plant 인원 화면(어두운 모드)\n * 줄 rgb(30 34 40) · 띠 rgb(26 30 35) → 대비 1.048 보이지 않는다\n * 그때 고정 열 시험 열여섯 건은 전부 초록이었다\n * ```\n *\n * 원인은 색이 아니라 **어디서 색을 가져오나**였다. 면 사다리에서 한 단 옮겨 오면, 그 사다리의\n * 밝기 순서가 앱마다 맞아야 한다. operato-plant 의 어두운 팔레트는 그 순서가 밝은 모드와 같아서\n * 띠가 줄보다 어두워지는 쪽으로 4단 움직였을 뿐이다.\n *\n * 그래서 지금은 **줄 자신의 색에서 만든다.** 이 시험은 그 규칙이 두 테마에서 다 서는지를 잰다.\n */\n\n/** 스타일 뭉치에서 그 토큰의 선언값을 꺼낸다 — 시험이 값을 베끼지 않게. */\nfunction 선언값(styles: unknown, token: string) {\n const cssText = (Array.isArray(styles) ? styles : [styles])\n .map(style => String((style as { cssText?: string })?.cssText ?? style))\n .join('\\n')\n\n /* 여러 줄에 걸친 color-mix(...) 도 담기게 다음 세미콜론까지 가져온다. */\n const found = cssText.match(new RegExp(`${token}\\\\s*:\\\\s*([^;]+);`))\n\n return found?.[1].replace(/\\s+/g, ' ').trim()\n}\n\n/**\n * 색 문자열을 0~255 세 값으로 만든다 — **캔버스에 칠해서 읽는다.**\n *\n * 문자열을 직접 파싱하면 안 된다. `color-mix` 는 계산되면 `color(srgb 0.954 0.957 0.958)` 로\n * 나오고, 그 숫자를 0~255 로 읽으면 거의 검정이 된다. 실제로 그렇게 한 번 재서 이 시험이\n * 「대비 20.9」라는 헛것을 냈다. 캔버스는 어떤 표기든 브라우저가 칠한 픽셀로 돌려준다.\n */\nconst 칠 = document.createElement('canvas').getContext('2d', { willReadFrequently: true })!\n\nfunction 픽셀(color: string) {\n 칠.clearRect(0, 0, 1, 1)\n 칠.fillStyle = '#000'\n 칠.fillStyle = color\n 칠.fillRect(0, 0, 1, 1)\n\n const [r, g, b] = 칠.getImageData(0, 0, 1, 1).data\n\n return [r, g, b]\n}\n\nconst lum = (color: string) => {\n const [r, g, b] = 픽셀(color)\n const f = (v: number) => {\n const x = v / 255\n return x <= 0.03928 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4)\n }\n\n return 0.2126 * f(r) + 0.7152 * f(g) + 0.0722 * f(b)\n}\n\nconst 대비 = (a: string, b: string) => {\n const [x, y] = [lum(a), lum(b)].sort((p, q) => q - p)\n return (x + 0.05) / (y + 0.05)\n}\n\n/**\n * 그 팔레트에서 선언값이 실제로 무슨 색이 되는지 — 브라우저에게 계산시킨다.\n *\n * ── 기다리지 않는다 (2026-09-05) ──────────────────────────────────────────\n * 전에는 fixture() 로 세우고 await 했다. 잴 것은 div 하나의 계산된 색뿐이라 렌더링을 기다릴\n * 이유가 없는데, 그 await 가 mocha 의 2초 문턱에 걸려 **기계가 바쁠 때만 붉어졌다** —\n * 빌드와 웹팩이 함께 돌던 동안 어두운 팔레트 두 건이 시간초과로 넘어갔고, 혼자 돌리면 초록이었다.\n * 색 판정은 한 번도 실패한 적이 없다.\n *\n * 붙이고 재고 떼는 것으로 충분하다. 기다림이 없으니 부하와 무관하게 같은 답이 나온다.\n */\nfunction 칠해보다(값: string, 팔레트: { 줄: string; 잉크: string; 사다리: string }) {\n const probe = document.createElement('div')\n\n probe.setAttribute(\n 'style',\n `${팔레트.사다리} --grid-record-background-color:${팔레트.줄}; --grid-record-color:${팔레트.잉크}; background-color:${값};`\n )\n\n document.body.appendChild(probe)\n\n try {\n return getComputedStyle(probe).backgroundColor\n } finally {\n probe.remove()\n }\n}\n\n/**\n * operato-plant 의 두 팔레트에서 실제로 쓰는 값이다.\n *\n * 면 사다리도 함께 준다 — 선언이 사다리에서 색을 가져오는 방식으로 되돌아가도 **그 앱에서 실제로\n * 나오는 색**으로 재기 위해서다. 주지 않으면 리터럴 폴백으로 떨어져 두 테마가 같은 색이 되고,\n * 어두운 모드에서 띠가 사라지던 그 일을 이 시험이 못 잡는다.\n */\nconst 팔레트들 = {\n 밝은: {\n 줄: 'rgb(255, 255, 255)',\n 잉크: 'rgb(23, 36, 44)',\n 사다리: '--md-sys-color-surface-container-lowest: rgb(255,255,255); --md-sys-color-surface-container-low: rgb(247,248,250);'\n },\n 어두운: {\n 줄: 'rgb(30, 34, 40)',\n 잉크: 'rgb(228, 232, 236)',\n 사다리: '--md-sys-color-surface-container-lowest: rgb(30,34,40); --md-sys-color-surface-container-low: rgb(26,30,35);'\n }\n}\n\n/**\n * 얼마나 벌어져야 보이나.\n *\n * 지나는 줄 표시는 **훨씬 옅어야 한다** — 고른 줄과 같은 무게로 그리면 무엇이 골라졌는지 흐려진다.\n * 그래서 큰 값을 요구하지 않는다. 다만 1.05 는 눈에 안 들어온다(위 [재봤음]). 눈으로 확인한 값이\n * 1.11 · 1.14 이므로 그 아래에 문턱을 둔다.\n */\nconst 최소분리 = 1.08\n\ndescribe('마우스가 지나는 줄이 보인다', () => {\n const 값 = 선언값(DataGrist.styles, '--grid-record-hover-background-color')\n\n it('그 토큰이 선언되어 있다 — 이름이 바뀌면 아래 시험들이 헛돈다', () => {\n expect(값, '--grid-record-hover-background-color 를 못 찾았다').to.be.a('string')\n })\n\n Object.entries(팔레트들).forEach(([이름, 팔레트]) => {\n it(`${이름} 팔레트에서 줄과 띠가 갈린다`, () => {\n const 띠 = 칠해보다(값!, 팔레트)\n const 벌어짐 = 대비(띠, 팔레트.줄)\n\n expect(\n 벌어짐,\n `줄 ${팔레트.줄} · 띠 ${픽셀(띠).join(' ')} → 대비 ${벌어짐.toFixed(3)}. 이만큼이면 마우스가 어느 줄에 있는지 안 보인다`\n ).to.be.greaterThan(최소분리)\n })\n\n it(`${이름} 팔레트에서 띠가 줄을 덮지 않는다 — 옅어야 한다`, () => {\n const 띠 = 칠해보다(값!, 팔레트)\n\n expect(대비(띠, 팔레트.줄), '고른 줄과 같은 무게가 되면 무엇이 골라졌는지 흐려진다').to.be.lessThan(1.6)\n })\n })\n\n /**\n * **줄 상태의 색을 `transparent` 로 두지 않는다.**\n *\n * 그 값은 「칠하지 않는다」가 아니라 「칸의 바탕을 지운다」다. 지워진 자리에는 표 뒤에 있는 것이\n * 비치고, 그것은 표가 정한 색이 아니다 — 어두운 페이지에 표를 놓으면 지나는 줄이 검게 된다.\n * 그리고 고정 열은 불투명해야 하므로 구멍이 안 뚫려, 줄 하나가 두 색으로 갈린다.\n *\n * 실제로 고치는 표가 그렇게 두고 있었다. 앞의 시험들은 **자기가 세운 표에 자기가 값을 넣으므로**\n * 이것을 못 잡는다 — 그 줄이 되돌아와도 초록이다. 그래서 선언 자체를 본다.\n */\n ;[\n ['DataGrist', DataGrist.styles],\n ['DataGrid', DataGrid.styles]\n ].forEach(([이름, styles]) => {\n it(`${이름} 이 줄 상태의 색을 비우지 않는다`, () => {\n const cssText = (styles as unknown[])\n .map(style => String((style as { cssText?: string })?.cssText ?? style))\n .join('\\n')\n\n const 비운것 = cssText.match(/--grid-record-[a-z-]*background-color\\s*:\\s*transparent/g)\n\n expect(비운것, `${비운것?.join(' · ')} — 비우는 대신 그 줄의 색을 주어야 한다`).to.be.null\n })\n })\n})\n"]}