@operato/data-grist 10.10.1 → 10.10.3

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 (29) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/coverage/lcov-report/base.css +224 -0
  3. package/coverage/lcov-report/block-navigation.js +87 -0
  4. package/coverage/lcov-report/favicon.png +0 -0
  5. package/coverage/lcov-report/index.html +866 -0
  6. package/coverage/lcov-report/prettify.css +1 -0
  7. package/coverage/lcov-report/prettify.js +2 -0
  8. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  9. package/coverage/lcov-report/sorter.js +210 -0
  10. package/coverage/lcov-report/webpack/bootstrap.html +118 -0
  11. package/coverage/lcov-report/webpack/index.html +116 -0
  12. package/coverage/lcov-report/webpack/runtime/define property getters.html +118 -0
  13. package/coverage/lcov-report/webpack/runtime/global.html +118 -0
  14. package/coverage/lcov-report/webpack/runtime/hasOwnProperty shorthand.html +118 -0
  15. package/coverage/lcov-report/webpack/runtime/index.html +161 -0
  16. package/coverage/lcov-report/webpack/runtime/make namespace object.html +118 -0
  17. package/coverage/lcov.info +53451 -0
  18. package/dist/src/data-card/data-card-gutter.js +30 -0
  19. package/dist/src/data-card/data-card-gutter.js.map +1 -1
  20. package/dist/src/data-card/data-card.js +6 -1
  21. package/dist/src/data-card/data-card.js.map +1 -1
  22. package/dist/src/data-card/record-card.js +115 -11
  23. package/dist/src/data-card/record-card.js.map +1 -1
  24. package/dist/src/data-grid/data-grid-field.js +13 -0
  25. package/dist/src/data-grid/data-grid-field.js.map +1 -1
  26. package/dist/src/types.d.ts +15 -0
  27. package/dist/src/types.js.map +1 -1
  28. package/dist/tsconfig.tsbuildinfo +1 -1
  29. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"data-grid-field.js","sourceRoot":"","sources":["../../../src/data-grid/data-grid-field.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\n\nimport i18next from 'i18next'\nimport { css, html, LitElement, PropertyValues, TemplateResult } from 'lit'\n// import { guard } from 'lit/directives/guard.js'\nimport { customElement, property } from 'lit/decorators.js'\n\nimport { TooltipReactiveController } from '@operato/utils'\n\nimport { ZERO_COLUMN } from '../configure/zero-config.js'\nimport { editorOpensPopup } from '../editors/registry.js'\nimport { ColumnConfig, GristRecord } from '../types.js'\n\nconst DEFAULT_TEXT_ALIGN = 'left'\n\n@customElement('ox-grid-field')\nexport class DataGridField extends LitElement {\n static styles = [\n css`\n :host {\n display: flex;\n\n align-items: center;\n justify-content: var(--data-grid-field-justify-content, flex-start);\n position: relative;\n\n white-space: nowrap;\n background-color: var(--grid-record-background-color);\n padding: var(--grid-record-padding);\n border: 1px solid transparent;\n border-width: 1px 0;\n border-bottom: var(--grid-record-border-bottom);\n\n font-size: var(--grid-record-wide-fontsize);\n min-height: 30px;\n box-sizing: border-box;\n }\n\n /*\n * ── 팝업으로 고치는 칸에는 **표식이 있다** ──────────────────────────────\n *\n * 그런 칸은 글자 칸과 똑같이 생겼다. 열어 보기 전에는 고칠 수 있는지도, 어떻게 고치는지도\n * 모른다 — 「실제 더블클릭을 하기 전에는 모르는 막막함」(사용자, 2026-09-05).\n *\n * **마우스를 올렸을 때 보인다.** 늘 보이게 두었더니 한 화면에 54개가 깔려 표가 시끄러웠다\n * (2026-09-05 · 사용자 판단). 값을 읽으러 온 눈에 장치가 줄마다 끼어든다.\n *\n * **손가락 화면에서는 늘 보인다.** 거기엔 올리는 동작이 없어서 감추면 길이 아예 없어진다.\n * 그 화면들은 표가 성기므로 늘 보여도 시끄럽지 않다.\n *\n * ── 네모와 펜을 쓴다 ─────────────────────────────────────────────────\n * 처음에 삼각형(arrow_drop_down)을 썼다. 참조 칸만 생각한 선택이었다 — 「목록에서 고른다」.\n *\n * 그런데 이 표식은 **팝업으로 여는 편집기 전부**에 붙는다. JSON · 코드 · 권한 편집기도\n * 그렇고, 거기서 삼각형은 거짓말이다. 고를 목록이 없다.\n *\n * ── 모양이 **어디서 고치는지**를 말한다 ────────────────────────────────\n *\n * edit 칸 안에서 고친다\n * edit_square 팝업이 열린다\n *\n * 표식이 있으면 고칠 수 있는 칸이고, 없으면 못 고치는 칸이다. **읽기 전용에는 붙지 않는다.**\n *\n * 처음에 삼각형(arrow_drop_down)을 썼다 — 참조 칸만 생각한 선택이었다. 이 표식은 JSON ·\n * 코드 · 권한 편집기에도 붙고, 거기서 삼각형은 고를 목록이 있다는 거짓말이 된다.\n *\n * 누를 자리는 22px 이다. 33px 줄에서 더블클릭을 겨누는 것보다 한 번 누르는 편이 쉽다.\n */\n /* 마우스 화면 — 칸에 올렸을 때만. 자리는 늘 잡아 두므로 글자가 밀리지 않는다. */\n @media (pointer: fine) {\n [popup-hint] {\n opacity: 0;\n transition: opacity 0.12s ease;\n }\n\n :host(:hover) [popup-hint] {\n opacity: 1;\n }\n }\n\n /*\n * 손가락 화면 — **고른 칸에만 보인다.**\n *\n * 올리는 동작이 없으니 호버로는 아무것도 못 보여 준다. 그렇다고 늘 보이게 두면 좁은\n * 화면이 표식으로 뒤덮인다 — 시끄러우면 정작 필요한 표식도 안 보인다.\n *\n * 고른 칸 하나에만 두면 둘 다 풀린다. 두드려 고르는 것이 그 화면의 첫 동작이므로,\n * 고르는 순간 「이 칸은 이렇게 고친다」가 나타난다.\n */\n @media (pointer: coarse) {\n [popup-hint] {\n display: none;\n }\n\n :host([focused]) [popup-hint] {\n display: flex;\n }\n }\n\n /*\n * ── 표식은 늘 **칸의 끝**에 있다. 정렬을 따라가지 않는다 (2026-09-05) ──────\n *\n * 찾는 데 드는 값이 커진다 정렬은 열마다 다르다(이름은 왼쪽, 수량은 오른쪽). 표식이\n * 그것을 따라가면 자리가 열마다 달라져 눈이 매번 찾아야 한다.\n * 끝에 고정하면 규칙이 하나다 — 「손잡이는 칸 끝에 있다」\n * 남의 칸처럼 보인다 오른쪽 정렬 칸의 값 **앞**에 표식을 두면 왼쪽 칸의 값 뒤에\n * 붙어 보인다. 어느 칸의 손잡이인지 흐려진다\n *\n * ── **값이 없는 쪽**에 붙는다 ──────────────────────────────────────\n * 왼쪽으로 붙인 칸의 빈자리는 오른쪽이고, 오른쪽으로 붙인 칸(수량 · 금액)의 빈자리는\n * 왼쪽이다. 정렬의 반대쪽에 두면 표식이 값 위에 얹히는 일 자체가 없어진다.\n *\n * 자리를 비워 두는 방법도 해 봤다 — 칸의 오른쪽을 24px 늘 비우는 것. 되돌렸다.\n * **자료가 먼저다**(사용자, 2026-09-05): 마우스를 올렸을 때만 보이는 안내 때문에 고칠 수\n * 있는 열 전부가 영구히 좁아지는 것은 값을 잘못 매긴 거래다.\n *\n * 가운데 정렬 칸은 양쪽이 다 비므로 끝에 둔다.\n */\n [popup-hint] {\n position: absolute;\n inset-inline-end: 2px;\n top: 50%;\n transform: translateY(-50%);\n\n --md-icon-size: 16px;\n font-size: 16px;\n width: 16px;\n height: 16px;\n padding: 3px;\n border-radius: 50%;\n\n /*\n * ── 채우지 않는다 (2026-09-05) ──────────────────────────────────\n * md-icon 이 물려주는 값은 FILL 1 이라 글리프가 통짜로 나온다. 두 가지가 어긋난다.\n *\n * 뜻 채운 글리프는 「지금 그 상태다」를 말한다(고른 것 · 켠 것). 이 표식이 말하는\n * 것은 상태가 아니라 **누르면 할 수 있는 일**이다. 그건 윤곽선의 몫이다\n * 밀도 표는 글자밭이다. 같은 16px 에서 채운 글리프는 잉크가 몇 곱절이라 값보다\n * 먼저 눈에 들어온다. 안내가 자료보다 진해서는 안 된다\n *\n * edit_square 는 특히 그렇다 — 채우면 네모가 덩어리가 되어 「종이 위의 연필」이라는\n * 그림이 뭉개진다. 진하기는 색으로 조절한다(아래 :hover).\n */\n /*\n * 굵기는 한 단 올린다. 16px 에서 400 짜리 윤곽선은 획이 배경에 묻는다 — 채움을 뺀 만큼\n * 획으로 되받는다. 잉크가 늘지 않으면서 형태는 또렷해진다.\n */\n font-variation-settings:\n 'FILL' 0,\n 'wght' 500,\n 'opsz' 20;\n\n /*\n * ── 값 **위에 그려지되, 값보다 앞서지 않는다** ──────────────────\n * 자리가 겹치면 표식이 위에 얹힌다. 그래도 이것은 거들어 주는 것이지 읽을 것이 아니다 —\n * 본문 색으로 두었더니 값과 같은 무게가 되어 눈이 먼저 표식으로 갔다(사용자, 2026-09-05).\n * 한 단 낮춰 라벨 색으로 둔다. 손이 표식 위에 오면 그때 본문 색이 된다.\n */\n color: var(--ui-muted, #57676f);\n cursor: pointer;\n\n transition:\n background-color 0.12s ease,\n color 0.12s ease;\n }\n\n /* 오른쪽으로 붙인 칸 — 값이 오른쪽 끝에 있으므로 표식은 왼쪽의 빈자리로 간다. */\n :host([mark-start]) [popup-hint] {\n inset-inline-end: auto;\n inset-inline-start: 2px;\n }\n\n [popup-hint]:hover {\n color: var(--ui-ink, #17272f);\n background-color: var(--ui-hover, color-mix(in srgb, currentColor 12%, transparent));\n }\n\n :host([multiline]) {\n white-space: pre-line;\n }\n\n /*\n * ── 칸이 쓸 수 있는 용어 (2026-08-31) ──────────────────────────────────\n * 렌더러가 그린 것은 이 부품의 shadow DOM 안에 들어간다. 그래서 **화면의 CSS 가 닿지 않는다.**\n * 두 줄 칸이나 상태 알약을 그리려던 화면들이 그 벽에 막혀, 값에 인라인 style 을 박거나 칸마다\n * 부품을 새로 만들었다.\n *\n * 목록이면 어디서나 같은 것을 필요로 하므로 여기서 준다. 이름은 뜻으로 짓는다 —\n * 「늦었다」가 아니라 「심각하다(crit)」다. 늦은 것은 이 표의 일이 아니고 화면의 일이다.\n *\n * .sub 둘째 줄. 「1,200 EA」 아래의 「BOLT-8」처럼 딸린 사실\n * .mono 고정폭. 번호·시각·수량처럼 자릿수를 비교하는 값\n * .none 값이 없음. 빈 칸으로 두면 「아직 안 읽었다」와 구별되지 않는다\n * .pill 알약. good·warn·crit·info 로 뜻을 준다\n * .crit · .warn · .good 글자 강조\n *\n * 값은 --ui-* 토큰에서 오고 폴백을 둔다 — 테마가 없어도 무너지지 않는다.\n */\n :host([twoline]) {\n display: block;\n white-space: normal;\n line-height: 1.35;\n }\n\n .sub {\n display: block;\n margin-top: 2px;\n font-size: var(--ui-t-small, 11.5px);\n color: var(--ui-faint, #8a959c);\n }\n\n .mono {\n font-family: var(--ui-mono, ui-monospace, Menlo, monospace);\n font-variant-numeric: tabular-nums;\n }\n\n /*\n * 그 줄이 무엇인지 가리키는 값 — 작업지시 번호, 설비 이름처럼. 두 줄 칸의 첫 줄이 이것이고\n * 둘째 줄(.sub)이 딸린 사실이다. 굵기만 올린다. 고정폭은 쓰지 않는다 — 자릿수를 세로로 견줄\n * 값이 아니고, 같은 글자 수에도 폭이 넓어져 옆 칸을 밀어낸다.\n */\n .id {\n font-weight: 620;\n }\n\n .none {\n color: var(--ui-faint, #8a959c);\n }\n\n .crit {\n color: var(--ui-crit, #c93f30);\n font-weight: 640;\n }\n\n .warn {\n color: var(--ui-warn, #9f5e02);\n font-weight: 640;\n }\n\n .good {\n color: var(--ui-good, #297d39);\n font-weight: 640;\n }\n\n /*\n * 값과 표식을 한 줄에 담는 영역이다. **간격과 높이를 여기서 정한다.**\n *\n * 처음에는 :not(.pill) + .pill { margin-left } 로 띄우려 했는데 안 걸렸다. 렌더러가 값을\n * 텍스트 노드로 내므로 알약의 앞 형제가 **요소가 아니고**, CSS 형제 선택자는 텍스트 노드를 보지\n * 못한다. 알약이 첫 요소이기도 해서 :first-child 로도 가려지지 않는다(실측 확인).\n *\n * 높이도 같은 자리에서 잡는다. baseline 으로 두면 14px 글자와 11.5px 알약의 기준선을 맞추느라\n * 알약이 내려앉는다.\n */\n /* 값이 있지만 눈에 걸릴 것은 아닌 것 — 「보통」 같은 기본값. .none(값 없음)과 구별한다. */\n .quiet {\n color: var(--ui-muted, #57676f);\n }\n\n /*\n * 별 같은 작은 기호를 글자 크기로 맞춘다. md-icon 의 기본은 24px 이라 줄 높이를 밀어낸다.\n * 색은 물려받는다 — 개수가 크기를 말하고 색은 담는 쪽이 정한다.\n */\n .star {\n --md-icon-size: 15px;\n font-size: 15px;\n width: 15px;\n height: 15px;\n color: inherit;\n }\n\n /* 별처럼 여럿이 붙는 기호는 사이를 좁게 둔다. 단어 사이의 6px 은 여기서 너무 넓다. */\n .line:has(.star) {\n gap: 1px;\n }\n\n /*\n * 셀 안의 아이콘 버튼. 그 줄에 하는 동작(들어가기 · 열기)을 담는다.\n *\n * 셀의 클릭 경로를 타지 않고 스스로 받으려는 렌더러가 쓴다. 그 경로에는 동작이 견딜 수 없는 관문이\n * 둘 있다.\n *\n * data-grid-body-click-handler.ts if (this.editTarget) return\n * data-grid-body-click-handler.ts (e.target).closest(\"ox-grid-field\")\n *\n * 앞은 다른 칸이 편집 중이면 첫 누름을 먹고, 뒤는 셀 경계를 스치면 칸을 못 찾는다. 진짜 button 을\n * 그리면 둘을 다 피하고 키보드로도 닿는다.\n *\n * 이 규칙이 여기 있는 이유: 렌더러의 결과는 이 셀의 그림자 안에 그려지므로 **페이지의 스타일이\n * 닿지 않는다.** 셀 안의 어휘는 셀이 갖는다.\n *\n * host 자식으로 적은 이유: 이 파일 아래의 host 자식 button 규칙이 채운 버튼을 그리고, 특이도가\n * 같으면 **뒤에 오는 쪽이 이긴다.** 그것 없이 두면 이 버튼이 채운 버튼의 모양으로 나왔다\n * (어두운 바탕 · 흰 글자, 실측 rgb(38, 46, 56)).\n *\n * 둘의 뜻이 다르다. 채운 버튼은 **실행하는 동작**이고, 이 버튼은 **이동**이다. 그래서 평소에는\n * 흐리고 배경이 없다.\n *\n * 배경은 **칸을 채우지 않고 동그라미로** 그린다. 칸을 다 채우면 「이 셀이 골라졌다」로 읽힌다 —\n * 표에서 채워진 사각형은 선택의 뜻이다. 동그라미는 버튼의 뜻이다.\n */\n :host > button.icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 26px;\n height: 26px;\n flex: none;\n /*\n * 누를 자리는 26px 로 두되 **줄 높이는 밀지 않는다.**\n *\n * 이 버튼이 들어간 칸 하나 때문에 표의 모든 줄이 46px 이 됐다(글자만 있는 줄은 38px). 스무 줄이면\n * 240px 을 잃는다. [확인함] grid-template-rows: 46px 34px\n *\n * 그렇다고 26px 아래로 줄이면 손가락으로 못 누른다(가로세로 24px 이 최소다). 그래서 상자는 26px\n * 로 두고 위아래 3px 씩을 여백에서 되돌려, 자리는 20px 만 차지하게 한다 — 글자 한 줄과 같다.\n */\n margin-top: -3px;\n margin-bottom: -3px;\n border: none;\n border-radius: 50%;\n background: transparent;\n padding: 0;\n color: var(--ui-faint, #8a959c);\n cursor: pointer;\n transition:\n background-color 0.12s ease,\n color 0.12s ease;\n }\n\n :host > button.icon:hover {\n background-color: var(--ui-hover, color-mix(in srgb, currentColor 12%, transparent));\n color: var(--ui-ink, #17272f);\n }\n\n :host > button.icon:active {\n background-color: var(--ui-active, color-mix(in srgb, currentColor 22%, transparent));\n }\n\n /* 키보드로 온 초점만 테를 보인다. 선택해서 온 초점에는 보이지 않는다. */\n :host > button.icon:focus-visible {\n outline: 2px solid var(--ui-accent, #00727e);\n outline-offset: -2px;\n }\n\n /*\n * 값을 함께 담는 버튼. 「4 ›」처럼 숫자와 화살이 나란히 놓인다.\n *\n * 아이콘만으로는 그 줄에 무엇이 있는지 말하지 못한다. 값이 뜻을 나르고 화살이 「누를 수 있다」를\n * 말한다. 그래서 동그라미가 아니라 알약 모양이다.\n */\n :host > button.icon.wide {\n width: auto;\n min-width: 44px;\n gap: 0;\n padding: 0 2px 0 7px;\n border-radius: 999px;\n font: inherit;\n font-size: var(--grid-record-wide-fontsize);\n font-variant-numeric: tabular-nums;\n }\n\n :host > button.icon.wide md-icon {\n --md-icon-size: 16px;\n font-size: 16px;\n width: 16px;\n height: 16px;\n }\n\n :host > button.icon md-icon {\n --md-icon-size: 18px;\n font-size: 18px;\n width: 18px;\n height: 18px;\n }\n\n .line {\n display: flex;\n align-items: center;\n gap: 6px;\n min-width: 0;\n }\n\n /* 값이 길면 값이 줄고 표식은 그대로 남는다 — 표식이 먼저 잘리면 읽을 수 없다. */\n .line > :first-child {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n /* 표식 — 상태 같은 짧은 값에 두른다. 점이 색을 나르고 글자가 뜻을 나른다. */\n .pill {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n padding: 1px 8px;\n border: 1px solid var(--ui-line, #d5dce0);\n border-radius: 999px;\n font-size: var(--ui-t-small, 11.5px);\n font-weight: 400;\n color: var(--ui-muted, #57676f);\n white-space: nowrap;\n }\n\n /*\n * 색만으로 뜻을 전하지 않는다 — 알약 안의 글자가 함께 말한다. 점은 거드는 것이다.\n *\n * align-self: center 가 필요하다. 알약은 flex 이고 점은 그 안의 항목인데, 높이를 6px 로\n * 주어도 교차축에서 늘어나 **6×11.5 짜리 세로 타원**이 됐다(원이 아니라 길쭉했다).\n * .pill 의 align-items: center 는 부모 쪽 값이라 이 항목의 stretch 를 막지 못한다.\n */\n .pill .dot {\n align-self: center;\n width: 6px;\n height: 6px;\n /*\n * 이 파일 아래에 span, pre 의 min-height: 1em 이 있다. 값이 빈 칸에서도 줄 높이를\n * 유지하려는 규칙인데, 점도 span 이라 거기 걸려 **높이가 1em(11.5px)까지 늘어났다** —\n * 그래서 6px 을 줘도 6×11.5 세로 타원이 됐다. 여기서 되돌린다.\n */\n min-height: 0;\n border-radius: 50%;\n background: currentColor;\n flex: none;\n }\n\n .pill.good {\n color: var(--ui-good, #297d39);\n border-color: color-mix(in srgb, var(--ui-good, #297d39) 40%, transparent);\n font-weight: 400;\n }\n\n .pill.info {\n color: var(--ui-accent, #00727e);\n border-color: color-mix(in srgb, var(--ui-accent, #00727e) 40%, transparent);\n font-weight: 400;\n }\n\n .pill.warn {\n color: var(--ui-warn, #9f5e02);\n border-color: color-mix(in srgb, var(--ui-warn, #9f5e02) 40%, transparent);\n font-weight: 400;\n }\n\n .pill.crit {\n color: var(--ui-crit, #c93f30);\n border-color: color-mix(in srgb, var(--ui-crit, #c93f30) 40%, transparent);\n font-weight: 400;\n }\n\n .pill.quiet {\n color: var(--ui-faint, #8a959c);\n }\n\n :host([gutter]) {\n padding: var(--grid-gutter-padding);\n text-align: center;\n }\n\n :host([gutter]) * {\n cursor: default;\n overflow: hidden;\n }\n\n :host([editing]) {\n border-top: var(--grid-record-editing-border);\n border-bottom: var(--grid-record-editing-border);\n }\n\n /*\n * 초점이 갔다고 **줄을 나누는 선을 바꾸지 않는다.**\n *\n * 전에는 여기서 --grid-record-border-bottom 을 초점 표식 값으로 덮어썼다. 그래서 표식을\n * 감추려고 그 토큰을 none 이나 투명으로 두면 **그 셀만 줄 선이 사라졌다.** 한 토큰이 「초점\n * 표식」과 「줄을 나누는 선」 두 가지를 겸하고 있었던 것이다.\n *\n * 초점 표식은 outline 이 그린다(data-grid-body-style.ts). 줄 선은 줄 선으로 남는다.\n */\n\n :host([dirty])::after {\n content: '';\n position: absolute;\n right: 0;\n top: 0;\n\n width: 0px;\n height: 0px;\n border-top: 9px solid red;\n border-left: 9px solid transparent;\n }\n\n span,\n pre {\n display: block;\n text-overflow: ellipsis;\n overflow: hidden;\n min-height: 1em;\n }\n\n :host([multiline]) span,\n :host([multiline]) pre {\n text-overflow: initial;\n overflow: visible;\n white-space: pre-line;\n word-break: break-word;\n overflow-wrap: anywhere;\n }\n\n * {\n margin: 0;\n text-align: var(--data-grid-field-text-align);\n }\n\n *[center] {\n flex: none;\n margin: 0 auto;\n }\n\n :host > button {\n display: flex;\n gap: var(--spacing-small);\n border: 0;\n align-items: center;\n padding: var(--spacing-small);\n\n color: var(--md-sys-color-on-primary);\n background-color: var(--md-sys-color-primary);\n border-radius: var(--data-card-item-btn-border-radius, var(--md-sys-shape-corner-tiny));\n\n font-size: 0.8rem;\n line-height: 0.8rem;\n\n md-icon {\n border: 0;\n padding: 0;\n background-color: unset;\n }\n }\n\n :host([danger]) > button {\n color: var(--md-sys-color-on-primary);\n background-color: var(--md-sys-color-error);\n border-color: var(--md-sys-color-error);\n }\n\n :host([danger]) > button:hover {\n color: var(--md-sys-color-on-secondary);\n background-color: var(--md-sys-color-secondary);\n border-color: var(--md-sys-color-secondary);\n }\n `\n ]\n\n @property({ type: String, attribute: true }) align = DEFAULT_TEXT_ALIGN\n @property({ type: Object }) record: GristRecord = {}\n @property({ type: Object }) column: ColumnConfig = ZERO_COLUMN\n @property({ type: Number }) rowIndex = 0\n @property({ type: Number }) columnIndex = 0\n @property({ type: Boolean }) editing = false\n @property({ type: Boolean, reflect: true }) multiline = false\n @property({ type: Object }) value = {}\n @property({ type: String }) valueWith: string | null = null\n @property({ attribute: false }) emphasized: any = false\n @property({ type: String, attribute: true }) fixed?: string\n @property({ type: String }) type?: string\n @property({ type: Boolean }) isWorking: boolean = false\n\n /* check in tree의 변화에 대응하기 위함임. 만일, record control 오브젝트로 통일되면 대체될 것임. */\n @property({ type: String }) checked?: string\n\n private _editCancelled?: boolean\n private _onFieldChange: (e: Event) => void = e => {}\n private _onKeydownInEditingMode: (e: KeyboardEvent) => void = e => {}\n\n private tooltipController?: TooltipReactiveController = new TooltipReactiveController(this)\n\n render(): TemplateResult {\n if (!this.column) {\n return html``\n }\n\n var { value, column, record, rowIndex } = this\n var { renderer, editor } = column.record\n\n return html`\n ${this.editing\n ? editor?.call(this, value, column, record, rowIndex, this)\n : renderer?.call(this, value, column, record, rowIndex, this)}\n <!--\n **편집 중에도 표식을 남긴다.**\n\n 칸을 누르면 편집 상태가 되고, 그때는 렌더러 대신 편집기가 그려진다. 표식을 !editing 으로\n 묶어 두었더니 **칸에 들어간 순간 사라졌다** — 들어간 사람에게 여는 길이 없어지는 셈이고,\n 다시 더블클릭이나 Enter 를 짐작해야 한다. 그 짐작을 없애려고 만든 표식이다.\n\n 팝업 편집기에만 붙으므로(opensPopup) 글자를 치는 편집기를 가릴 일은 없다.\n -->\n <!--\n ── 칸 안에서 고치는 중에는 표식을 걷는다 ────────────────────────────────\n 입력칸이 이미 그 자리에 있다. 펜을 겹쳐 두면 글자를 가리고, 고치는 중인 사람에게\n 「고칠 수 있다」를 다시 말할 이유도 없다.\n\n 팝업 쪽은 남긴다 — 편집 상태에 들어가도 팝업은 아직 안 열렸고, 그 표식이 여는 길이다.\n -->\n ${this.editMark && !(this.editing && this.editMark === 'inline')\n ? html`<md-icon\n popup-hint\n ?inline=${this.editMark === 'inline'}\n title=${i18next.t('text.grid-open-editor', { defaultValue: 'press to edit' })}\n @pointerdown=${async (e: Event) => {\n /*\n * **click 이 아니라 pointerdown 이다.**\n *\n * 이 표에서는 click 이벤트가 나지 않는다 — 몸통이 pointer 단계에서 기본 동작을\n * 막는다. document 에 capture 로 click 을 걸어 두고 표식을 선택해 봐도 한 번도\n * 울리지 않았다(2026-09-05 계측).\n */\n e.stopPropagation()\n e.preventDefault()\n\n /*\n * ── 포커스는 여기서 변경하지 않는다 ──────────────────────────────\n *\n * 팝업이 닫히면 오버레이가 **열릴 때 포커스가 있던 자리**로 돌려준다. 그런데 위에서\n * preventDefault 를 하므로 브라우저가 이 칸으로 포커스를 옮겨 주지 않고, 그래서\n * 팝업을 닫으면 엉뚱한 곳으로 간다(페이지 검색창이었다).\n *\n * 두 가지를 해 봤고 **둘 다 팝업이 안 열리게 만들었다.**\n *\n * focus-change 이벤트 몸통이 그 갱신에서 editTarget 을 지운다(shouldUpdate)\n * ime-buffer 에 직접 focus 같은 결과\n *\n * 이 표는 포커스를 ime-buffer 라는 숨은 입력칸에 담아 두고, 편집 상태를 그 갱신\n * 주기에 얹고 있다. 그 둘이 얽혀 있어서 밖에서 포커스를 옮기면 편집이 취소된다.\n *\n * 표의 포커스 모델을 손대야 풀리는 문제라 여기서 우회하지 않는다.\n * [확인함] 2026-09-05 · 두 방법 모두 팝업이 안 열림\n */\n\n this.dispatchEvent(\n new CustomEvent('start-edit', {\n bubbles: true,\n composed: true,\n detail: { row: this.rowIndex, column: this.columnIndex }\n })\n )\n\n /*\n * 칸 안에서 고치는 편집기는 여기서 끝난다 — 편집이 열리면 그 자리에 입력칸이 뜬다.\n * 팝업 편집기는 한 걸음 더 필요하다.\n */\n if (this.editMark !== 'popup') {\n return\n }\n\n /*\n * ── 편집을 열고, **그다음** 더블클릭을 편집기에 보낸다 ──────────────\n *\n * 사람이 더블클릭할 때의 순서를 계측해 보면 이렇다.\n *\n * startEditTarget 첫 클릭이 편집을 연다 → 편집기가 그려진다\n * dblclick 그다음에 도착해 **이미 있는 편집기**가 받아 팝업을 연다\n *\n * 편집기마다 여는 메서드 이름이 달라서 부품이 직접 부를 수는 없다. 사람이 하던\n * 동작을 같은 순서로 보내면 경로가 하나로 남는다.\n */\n for (let i = 0; i < 5 && !this.editing; i++) {\n await new Promise(resolve => requestAnimationFrame(resolve))\n }\n\n await this.updateComplete\n\n const editor = this.renderRoot.firstElementChild as HTMLElement | null\n editor?.dispatchEvent(new MouseEvent('dblclick', { bubbles: true, composed: true }))\n }}\n >${this.editMark === 'popup' ? 'edit_square' : 'edit'}</md-icon\n >`\n : ''}\n `\n }\n\n updated(changes: PropertyValues<this>) {\n if (changes.has('editing')) {\n if (this.editing) {\n this._onKeydownInEditingMode = ((e: KeyboardEvent) => {\n if (e.key === 'Esc' || e.key === 'Escape') {\n /* 편집 취소 */\n this._editCancelled = true\n }\n }).bind(this)\n\n this._onFieldChange = ((e: Event) => {\n this._editCancelled && e.stopPropagation()\n }).bind(this)\n\n delete this._editCancelled\n this.addEventListener('field-change', this._onFieldChange)\n this.addEventListener('keydown', this._onKeydownInEditingMode)\n } else {\n this.removeEventListener('field-change', this._onFieldChange)\n this.removeEventListener('keydown', this._onKeydownInEditingMode)\n }\n }\n\n if (changes.has('column')) {\n /*\n * 정렬은 **넣을 때만이 아니라 되돌릴 때도** 손대야 한다.\n *\n * 이 요소는 재사용된다. 전에는 기본값이 아닐 때만 인라인 값을 넣고 지우지 않았다. 그래서 한 번\n * 오른쪽 정렬 칸을 그린 셀이 다음에 왼쪽 정렬 칸을 그려도 오른쪽에 남았다.\n *\n * [확인함] 품질 화면 「판단」 칸에서 머리글은 x=380(왼쪽)인데 값은 x=452(flex-end)였다. 숫자 칸을\n * 오른쪽으로 바꾼 뒤 드러났다 — 오른쪽 정렬 칸이 드물던 동안은 눈에 안 띄었다. 2026-09-02\n */\n var align = this.column.record.align || DEFAULT_TEXT_ALIGN\n\n if (align == DEFAULT_TEXT_ALIGN) {\n this.style.removeProperty('--data-grid-field-justify-content')\n this.style.removeProperty('--data-grid-field-text-align')\n } else {\n let justify = 'center'\n switch (align) {\n case 'right':\n justify = 'flex-end'\n break\n }\n this.style.setProperty('--data-grid-field-justify-content', justify)\n this.style.setProperty('--data-grid-field-text-align', align)\n }\n\n /* 표식은 값의 반대쪽 빈자리에 선다 — 위 :host([mark-start]) 주석 참고. */\n this.toggleAttribute('mark-start', align === 'right')\n\n const { danger } = this.column\n if (danger) {\n this.setAttribute('danger', '')\n } else {\n this.removeAttribute('danger')\n }\n }\n\n if (changes.has('fixed')) {\n if (this.fixed) {\n this.style.left = this.fixed + 'px'\n } else {\n this.style.left = 'unset'\n }\n }\n\n let disabled = this.column.record.disabled || false\n if (typeof disabled === 'function') {\n disabled = disabled.call(this, this.record[this.column.name], this.column, this.record, this.rowIndex, this)\n }\n\n if (!!disabled) {\n // 비황성화 여부 표시\n this.setAttribute('disabled', '')\n } else {\n // 비황성화 여부 제거\n this.removeAttribute('disabled')\n }\n\n const emphasized =\n /*\n * classifier 는 없을 수 있다. 열 빌더가 채워 주지만 손으로 만든 열에는 없고, 그때 여기서\n * 던진다. 갱신 중에 나는 예외라 화면이 오류보고없이 멈춘다.\n *\n * [확인함] 2026-09-05 · wtr 에서 Cannot read properties of undefined (reading 'call')\n * 팝업 표식을 넣어 칸이 전보다 자주 갱신되면서 드러났다. 원래 있던 자리다.\n */\n this.column.record.classifier?.call(this, this.record, this.rowIndex)?.emphasized || this.emphasized\n\n if (!!emphasized) {\n this.setAttribute('emphasized-row', '')\n\n if (Symbol.iterator in Object(emphasized)) {\n const [backgroundColor, foregroundColor] = emphasized as string[]\n backgroundColor && this.style.setProperty('--grid-record-emphasized-background-color', backgroundColor)\n foregroundColor && this.style.setProperty('--grid-record-emphasized-color', foregroundColor)\n }\n } else {\n this.removeAttribute('emphasized-row')\n }\n }\n\n /**\n * 이 칸에 **팝업 표식**을 붙이나 — 팝업으로 고쳐지고, 지금 고칠 수 있을 때만.\n *\n * 고칠 수 있는지는 열이 함수로 정할 수도 있다(줄마다 다른 경우). 그때는 몸통이 편집을 열 때\n * 하는 것과 같은 방식으로 물어본다. 표식은 있는데 선택해도 안 열리면 그것이 더 나쁘다.\n */\n get opensPopup(): boolean {\n return this.editMark === 'popup'\n }\n\n /**\n * 이 칸을 **고칠 수 있나, 그리고 어디서 고치나.**\n *\n * popup 팝업이 열린다 — 네모와 펜\n * inline 칸 안에서 고친다 — 펜\n * null 못 고친다 — 표식 없음\n *\n * 표식이 있으면 고칠 수 있는 칸이고, 모양이 어디서 고치는지를 말한다.\n *\n * 고칠 수 있는지는 열이 함수로 정할 수도 있다(줄마다 다른 경우). 그때는 몸통이 편집을 열 때\n * 하는 것과 같은 방식으로 물어본다. 표식은 있는데 선택해도 안 열리면 그것이 더 나쁘다.\n */\n get editMark(): 'popup' | 'inline' | null {\n const column = this.column\n\n if (!column) {\n return null\n }\n\n /* 거터에는 붙이지 않는다 — 순번과 고르기 칸은 고치는 자리가 아니다. */\n if (column.type === 'gutter') {\n return null\n }\n\n /*\n * 표 오른쪽의 **빈 채움 열**에도 붙이지 않는다. 남는 자리를 메우는 20px 짜리 빈 열이라\n * 이름도 타입도 없다 — 고칠 것이 없는데 표식만 떴다.\n *\n * [확인함] 2026-09-05 · 인원 화면 맨 끝 칸 · { type: '', name: '', label: false } · 폭 20\n */\n if (!column.name) {\n return null\n }\n\n let { editable } = column.record || ({} as any)\n\n if (typeof editable === 'function') {\n editable = editable.call(this, this.value, column, this.record, this.rowIndex, this)\n }\n\n /*\n * **밝히지 않은 열에는 붙이지 않는다.**\n *\n * 표 전체가 고칠 수 있는지를 판단하는 자리가 같은 기준을 쓴다.\n *\n * [읽었음] data-grid.ts · editableGrid = columns.some(c => !!c.record?.editable)\n *\n * 참일 때만 고칠 수 있는 열로 본다. 선언하지 않은 열까지 세면 **고르기만 하는 표**에도\n * 표식이 깔린다 — 참조 팝업의 목록이 그랬다(2026-09-05). 그 표의 칸은 고르는 자리이지\n * 고치는 자리가 아니다.\n */\n if (!editable) {\n return null\n }\n\n /*\n * 편집기를 **함수로 직접 준 열**은 타입으로 판단할 수 없다. 그리기를 다른 렌더러로 하려고\n * 타입을 바꾼 열이 그렇다 — 관계를 여럿 다는 칸이 `ops-list` 로 그려지면서 편집은 참조\n * 팝업으로 한다(ops-master 의 reference-list).\n *\n * 그런 열은 `record.opensPopup` 으로 스스로 밝힌다. 밝히지 않으면 타입으로 본다.\n *\n * [확인함] 2026-09-05 · 보유 자격 · 타입 ops-list · 팝업으로 열리는데 표식이 inline 이었다\n */\n const declared = (column.record as any)?.opensPopup\n\n if (typeof declared === 'boolean') {\n return declared ? 'popup' : 'inline'\n }\n\n return editorOpensPopup(column.type) ? 'popup' : 'inline'\n }\n\n get editableOnClick() {\n const renderer = this.renderRoot.firstElementChild as HTMLElement\n //@ts-ignore\n return renderer && 'editableOnClick' in renderer ? renderer.editableOnClick : true\n }\n}\n"]}
1
+ {"version":3,"file":"data-grid-field.js","sourceRoot":"","sources":["../../../src/data-grid/data-grid-field.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\n\nimport i18next from 'i18next'\nimport { css, html, LitElement, PropertyValues, TemplateResult } from 'lit'\n// import { guard } from 'lit/directives/guard.js'\nimport { customElement, property } from 'lit/decorators.js'\n\nimport { TooltipReactiveController } from '@operato/utils'\n\nimport { ZERO_COLUMN } from '../configure/zero-config.js'\nimport { editorOpensPopup } from '../editors/registry.js'\nimport { ColumnConfig, GristRecord } from '../types.js'\n\nconst DEFAULT_TEXT_ALIGN = 'left'\n\n@customElement('ox-grid-field')\nexport class DataGridField extends LitElement {\n static styles = [\n css`\n :host {\n display: flex;\n\n align-items: center;\n justify-content: var(--data-grid-field-justify-content, flex-start);\n position: relative;\n\n white-space: nowrap;\n background-color: var(--grid-record-background-color);\n padding: var(--grid-record-padding);\n border: 1px solid transparent;\n border-width: 1px 0;\n border-bottom: var(--grid-record-border-bottom);\n\n font-size: var(--grid-record-wide-fontsize);\n min-height: 30px;\n box-sizing: border-box;\n }\n\n /*\n * ── 팝업으로 고치는 칸에는 **표식이 있다** ──────────────────────────────\n *\n * 그런 칸은 글자 칸과 똑같이 생겼다. 열어 보기 전에는 고칠 수 있는지도, 어떻게 고치는지도\n * 모른다 — 「실제 더블클릭을 하기 전에는 모르는 막막함」(사용자, 2026-09-05).\n *\n * **마우스를 올렸을 때 보인다.** 늘 보이게 두었더니 한 화면에 54개가 깔려 표가 시끄러웠다\n * (2026-09-05 · 사용자 판단). 값을 읽으러 온 눈에 장치가 줄마다 끼어든다.\n *\n * **손가락 화면에서는 늘 보인다.** 거기엔 올리는 동작이 없어서 감추면 길이 아예 없어진다.\n * 그 화면들은 표가 성기므로 늘 보여도 시끄럽지 않다.\n *\n * ── 네모와 펜을 쓴다 ─────────────────────────────────────────────────\n * 처음에 삼각형(arrow_drop_down)을 썼다. 참조 칸만 생각한 선택이었다 — 「목록에서 고른다」.\n *\n * 그런데 이 표식은 **팝업으로 여는 편집기 전부**에 붙는다. JSON · 코드 · 권한 편집기도\n * 그렇고, 거기서 삼각형은 거짓말이다. 고를 목록이 없다.\n *\n * ── 모양이 **어디서 고치는지**를 말한다 ────────────────────────────────\n *\n * edit 칸 안에서 고친다\n * edit_square 팝업이 열린다\n *\n * 표식이 있으면 고칠 수 있는 칸이고, 없으면 못 고치는 칸이다. **읽기 전용에는 붙지 않는다.**\n *\n * 처음에 삼각형(arrow_drop_down)을 썼다 — 참조 칸만 생각한 선택이었다. 이 표식은 JSON ·\n * 코드 · 권한 편집기에도 붙고, 거기서 삼각형은 고를 목록이 있다는 거짓말이 된다.\n *\n * 누를 자리는 22px 이다. 33px 줄에서 더블클릭을 겨누는 것보다 한 번 누르는 편이 쉽다.\n */\n /* 마우스 화면 — 칸에 올렸을 때만. 자리는 늘 잡아 두므로 글자가 밀리지 않는다. */\n @media (pointer: fine) {\n [popup-hint] {\n opacity: 0;\n transition: opacity 0.12s ease;\n }\n\n :host(:hover) [popup-hint] {\n opacity: 1;\n }\n }\n\n /*\n * 손가락 화면 — **고른 칸에만 보인다.**\n *\n * 올리는 동작이 없으니 호버로는 아무것도 못 보여 준다. 그렇다고 늘 보이게 두면 좁은\n * 화면이 표식으로 뒤덮인다 — 시끄러우면 정작 필요한 표식도 안 보인다.\n *\n * 고른 칸 하나에만 두면 둘 다 풀린다. 두드려 고르는 것이 그 화면의 첫 동작이므로,\n * 고르는 순간 「이 칸은 이렇게 고친다」가 나타난다.\n */\n @media (pointer: coarse) {\n [popup-hint] {\n display: none;\n }\n\n :host([focused]) [popup-hint] {\n display: flex;\n }\n }\n\n /*\n * ── 표식은 늘 **칸의 끝**에 있다. 정렬을 따라가지 않는다 (2026-09-05) ──────\n *\n * 찾는 데 드는 값이 커진다 정렬은 열마다 다르다(이름은 왼쪽, 수량은 오른쪽). 표식이\n * 그것을 따라가면 자리가 열마다 달라져 눈이 매번 찾아야 한다.\n * 끝에 고정하면 규칙이 하나다 — 「손잡이는 칸 끝에 있다」\n * 남의 칸처럼 보인다 오른쪽 정렬 칸의 값 **앞**에 표식을 두면 왼쪽 칸의 값 뒤에\n * 붙어 보인다. 어느 칸의 손잡이인지 흐려진다\n *\n * ── **값이 없는 쪽**에 붙는다 ──────────────────────────────────────\n * 왼쪽으로 붙인 칸의 빈자리는 오른쪽이고, 오른쪽으로 붙인 칸(수량 · 금액)의 빈자리는\n * 왼쪽이다. 정렬의 반대쪽에 두면 표식이 값 위에 얹히는 일 자체가 없어진다.\n *\n * 자리를 비워 두는 방법도 해 봤다 — 칸의 오른쪽을 24px 늘 비우는 것. 되돌렸다.\n * **자료가 먼저다**(사용자, 2026-09-05): 마우스를 올렸을 때만 보이는 안내 때문에 고칠 수\n * 있는 열 전부가 영구히 좁아지는 것은 값을 잘못 매긴 거래다.\n *\n * 가운데 정렬 칸은 양쪽이 다 비므로 끝에 둔다.\n */\n [popup-hint] {\n position: absolute;\n inset-inline-end: 2px;\n top: 50%;\n transform: translateY(-50%);\n\n --md-icon-size: 16px;\n font-size: 16px;\n width: 16px;\n height: 16px;\n padding: 3px;\n border-radius: 50%;\n\n /*\n * ── 채우지 않는다 (2026-09-05) ──────────────────────────────────\n * md-icon 이 물려주는 값은 FILL 1 이라 글리프가 통짜로 나온다. 두 가지가 어긋난다.\n *\n * 뜻 채운 글리프는 「지금 그 상태다」를 말한다(고른 것 · 켠 것). 이 표식이 말하는\n * 것은 상태가 아니라 **누르면 할 수 있는 일**이다. 그건 윤곽선의 몫이다\n * 밀도 표는 글자밭이다. 같은 16px 에서 채운 글리프는 잉크가 몇 곱절이라 값보다\n * 먼저 눈에 들어온다. 안내가 자료보다 진해서는 안 된다\n *\n * edit_square 는 특히 그렇다 — 채우면 네모가 덩어리가 되어 「종이 위의 연필」이라는\n * 그림이 뭉개진다. 진하기는 색으로 조절한다(아래 :hover).\n */\n /*\n * 굵기는 한 단 올린다. 16px 에서 400 짜리 윤곽선은 획이 배경에 묻는다 — 채움을 뺀 만큼\n * 획으로 되받는다. 잉크가 늘지 않으면서 형태는 또렷해진다.\n */\n font-variation-settings:\n 'FILL' 0,\n 'wght' 500,\n 'opsz' 20;\n\n /*\n * ── 값 **위에 그려지되, 값보다 앞서지 않는다** ──────────────────\n * 자리가 겹치면 표식이 위에 얹힌다. 그래도 이것은 거들어 주는 것이지 읽을 것이 아니다 —\n * 본문 색으로 두었더니 값과 같은 무게가 되어 눈이 먼저 표식으로 갔다(사용자, 2026-09-05).\n * 한 단 낮춰 라벨 색으로 둔다. 손이 표식 위에 오면 그때 본문 색이 된다.\n */\n color: var(--ui-muted, #57676f);\n cursor: pointer;\n\n transition:\n background-color 0.12s ease,\n color 0.12s ease;\n }\n\n /* 오른쪽으로 붙인 칸 — 값이 오른쪽 끝에 있으므로 표식은 왼쪽의 빈자리로 간다. */\n :host([mark-start]) [popup-hint] {\n inset-inline-end: auto;\n inset-inline-start: 2px;\n }\n\n [popup-hint]:hover {\n color: var(--ui-ink, #17272f);\n background-color: var(--ui-hover, color-mix(in srgb, currentColor 12%, transparent));\n }\n\n :host([multiline]) {\n white-space: pre-line;\n }\n\n /*\n * ── 칸이 쓸 수 있는 용어 (2026-08-31) ──────────────────────────────────\n * 렌더러가 그린 것은 이 부품의 shadow DOM 안에 들어간다. 그래서 **화면의 CSS 가 닿지 않는다.**\n * 두 줄 칸이나 상태 알약을 그리려던 화면들이 그 벽에 막혀, 값에 인라인 style 을 박거나 칸마다\n * 부품을 새로 만들었다.\n *\n * 목록이면 어디서나 같은 것을 필요로 하므로 여기서 준다. 이름은 뜻으로 짓는다 —\n * 「늦었다」가 아니라 「심각하다(crit)」다. 늦은 것은 이 표의 일이 아니고 화면의 일이다.\n *\n * .sub 둘째 줄. 「1,200 EA」 아래의 「BOLT-8」처럼 딸린 사실\n * .mono 고정폭. 번호·시각·수량처럼 자릿수를 비교하는 값\n * .none 값이 없음. 빈 칸으로 두면 「아직 안 읽었다」와 구별되지 않는다\n * .pill 알약. good·warn·crit·info 로 뜻을 준다\n * .crit · .warn · .good 글자 강조\n *\n * 값은 --ui-* 토큰에서 오고 폴백을 둔다 — 테마가 없어도 무너지지 않는다.\n */\n :host([twoline]) {\n display: block;\n white-space: normal;\n line-height: 1.35;\n }\n\n .sub {\n display: block;\n margin-top: 2px;\n font-size: var(--ui-t-small, 11.5px);\n color: var(--ui-faint, #8a959c);\n }\n\n .mono {\n font-family: var(--ui-mono, ui-monospace, Menlo, monospace);\n font-variant-numeric: tabular-nums;\n }\n\n /*\n * 그 줄이 무엇인지 가리키는 값 — 작업지시 번호, 설비 이름처럼. 두 줄 칸의 첫 줄이 이것이고\n * 둘째 줄(.sub)이 딸린 사실이다. 굵기만 올린다. 고정폭은 쓰지 않는다 — 자릿수를 세로로 견줄\n * 값이 아니고, 같은 글자 수에도 폭이 넓어져 옆 칸을 밀어낸다.\n */\n .id {\n font-weight: 620;\n }\n\n .none {\n color: var(--ui-faint, #8a959c);\n }\n\n .crit {\n color: var(--ui-crit, #c93f30);\n font-weight: 640;\n }\n\n .warn {\n color: var(--ui-warn, #9f5e02);\n font-weight: 640;\n }\n\n .good {\n color: var(--ui-good, #297d39);\n font-weight: 640;\n }\n\n /*\n * 값과 표식을 한 줄에 담는 영역이다. **간격과 높이를 여기서 정한다.**\n *\n * 처음에는 :not(.pill) + .pill { margin-left } 로 띄우려 했는데 안 걸렸다. 렌더러가 값을\n * 텍스트 노드로 내므로 알약의 앞 형제가 **요소가 아니고**, CSS 형제 선택자는 텍스트 노드를 보지\n * 못한다. 알약이 첫 요소이기도 해서 :first-child 로도 가려지지 않는다(실측 확인).\n *\n * 높이도 같은 자리에서 잡는다. baseline 으로 두면 14px 글자와 11.5px 알약의 기준선을 맞추느라\n * 알약이 내려앉는다.\n */\n /* 값이 있지만 눈에 걸릴 것은 아닌 것 — 「보통」 같은 기본값. .none(값 없음)과 구별한다. */\n .quiet {\n color: var(--ui-muted, #57676f);\n }\n\n /*\n * 별 같은 작은 기호를 글자 크기로 맞춘다. md-icon 의 기본은 24px 이라 줄 높이를 밀어낸다.\n * 색은 물려받는다 — 개수가 크기를 말하고 색은 담는 쪽이 정한다.\n */\n .star {\n --md-icon-size: 15px;\n font-size: 15px;\n width: 15px;\n height: 15px;\n color: inherit;\n }\n\n /* 별처럼 여럿이 붙는 기호는 사이를 좁게 둔다. 단어 사이의 6px 은 여기서 너무 넓다. */\n .line:has(.star) {\n gap: 1px;\n }\n\n /*\n * 셀 안의 아이콘 버튼. 그 줄에 하는 동작(들어가기 · 열기)을 담는다.\n *\n * 셀의 클릭 경로를 타지 않고 스스로 받으려는 렌더러가 쓴다. 그 경로에는 동작이 견딜 수 없는 관문이\n * 둘 있다.\n *\n * data-grid-body-click-handler.ts if (this.editTarget) return\n * data-grid-body-click-handler.ts (e.target).closest(\"ox-grid-field\")\n *\n * 앞은 다른 칸이 편집 중이면 첫 누름을 먹고, 뒤는 셀 경계를 스치면 칸을 못 찾는다. 진짜 button 을\n * 그리면 둘을 다 피하고 키보드로도 닿는다.\n *\n * 이 규칙이 여기 있는 이유: 렌더러의 결과는 이 셀의 그림자 안에 그려지므로 **페이지의 스타일이\n * 닿지 않는다.** 셀 안의 어휘는 셀이 갖는다.\n *\n * host 자식으로 적은 이유: 이 파일 아래의 host 자식 button 규칙이 채운 버튼을 그리고, 특이도가\n * 같으면 **뒤에 오는 쪽이 이긴다.** 그것 없이 두면 이 버튼이 채운 버튼의 모양으로 나왔다\n * (어두운 바탕 · 흰 글자, 실측 rgb(38, 46, 56)).\n *\n * 둘의 뜻이 다르다. 채운 버튼은 **실행하는 동작**이고, 이 버튼은 **이동**이다. 그래서 평소에는\n * 흐리고 배경이 없다.\n *\n * 배경은 **칸을 채우지 않고 동그라미로** 그린다. 칸을 다 채우면 「이 셀이 골라졌다」로 읽힌다 —\n * 표에서 채워진 사각형은 선택의 뜻이다. 동그라미는 버튼의 뜻이다.\n */\n :host > button.icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 26px;\n height: 26px;\n flex: none;\n /*\n * 누를 자리는 26px 로 두되 **줄 높이는 밀지 않는다.**\n *\n * 이 버튼이 들어간 칸 하나 때문에 표의 모든 줄이 46px 이 됐다(글자만 있는 줄은 38px). 스무 줄이면\n * 240px 을 잃는다. [확인함] grid-template-rows: 46px 34px\n *\n * 그렇다고 26px 아래로 줄이면 손가락으로 못 누른다(가로세로 24px 이 최소다). 그래서 상자는 26px\n * 로 두고 위아래 3px 씩을 여백에서 되돌려, 자리는 20px 만 차지하게 한다 — 글자 한 줄과 같다.\n */\n margin-top: -3px;\n margin-bottom: -3px;\n border: none;\n border-radius: 50%;\n background: transparent;\n padding: 0;\n color: var(--ui-faint, #8a959c);\n cursor: pointer;\n transition:\n background-color 0.12s ease,\n color 0.12s ease;\n }\n\n :host > button.icon:hover {\n background-color: var(--ui-hover, color-mix(in srgb, currentColor 12%, transparent));\n color: var(--ui-ink, #17272f);\n }\n\n :host > button.icon:active {\n background-color: var(--ui-active, color-mix(in srgb, currentColor 22%, transparent));\n }\n\n /* 키보드로 온 초점만 테를 보인다. 선택해서 온 초점에는 보이지 않는다. */\n :host > button.icon:focus-visible {\n outline: 2px solid var(--ui-accent, #00727e);\n outline-offset: -2px;\n }\n\n /*\n * 값을 함께 담는 버튼. 「4 ›」처럼 숫자와 화살이 나란히 놓인다.\n *\n * 아이콘만으로는 그 줄에 무엇이 있는지 말하지 못한다. 값이 뜻을 나르고 화살이 「누를 수 있다」를\n * 말한다. 그래서 동그라미가 아니라 알약 모양이다.\n */\n :host > button.icon.wide {\n width: auto;\n min-width: 44px;\n gap: 0;\n padding: 0 2px 0 7px;\n border-radius: 999px;\n font: inherit;\n font-size: var(--grid-record-wide-fontsize);\n font-variant-numeric: tabular-nums;\n }\n\n :host > button.icon.wide md-icon {\n --md-icon-size: 16px;\n font-size: 16px;\n width: 16px;\n height: 16px;\n }\n\n :host > button.icon md-icon {\n --md-icon-size: 18px;\n font-size: 18px;\n width: 18px;\n height: 18px;\n }\n\n .line {\n display: flex;\n align-items: center;\n gap: 6px;\n min-width: 0;\n }\n\n /* 값이 길면 값이 줄고 표식은 그대로 남는다 — 표식이 먼저 잘리면 읽을 수 없다. */\n .line > :first-child {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n /* 표식 — 상태 같은 짧은 값에 두른다. 점이 색을 나르고 글자가 뜻을 나른다. */\n .pill {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n padding: 1px 8px;\n border: 1px solid var(--ui-line, #d5dce0);\n border-radius: 999px;\n font-size: var(--ui-t-small, 11.5px);\n font-weight: 400;\n color: var(--ui-muted, #57676f);\n white-space: nowrap;\n }\n\n /*\n * 색만으로 뜻을 전하지 않는다 — 알약 안의 글자가 함께 말한다. 점은 거드는 것이다.\n *\n * align-self: center 가 필요하다. 알약은 flex 이고 점은 그 안의 항목인데, 높이를 6px 로\n * 주어도 교차축에서 늘어나 **6×11.5 짜리 세로 타원**이 됐다(원이 아니라 길쭉했다).\n * .pill 의 align-items: center 는 부모 쪽 값이라 이 항목의 stretch 를 막지 못한다.\n */\n .pill .dot {\n align-self: center;\n width: 6px;\n height: 6px;\n /*\n * 이 파일 아래에 span, pre 의 min-height: 1em 이 있다. 값이 빈 칸에서도 줄 높이를\n * 유지하려는 규칙인데, 점도 span 이라 거기 걸려 **높이가 1em(11.5px)까지 늘어났다** —\n * 그래서 6px 을 줘도 6×11.5 세로 타원이 됐다. 여기서 되돌린다.\n */\n min-height: 0;\n border-radius: 50%;\n background: currentColor;\n flex: none;\n }\n\n .pill.good {\n color: var(--ui-good, #297d39);\n border-color: color-mix(in srgb, var(--ui-good, #297d39) 40%, transparent);\n font-weight: 400;\n }\n\n .pill.info {\n color: var(--ui-accent, #00727e);\n border-color: color-mix(in srgb, var(--ui-accent, #00727e) 40%, transparent);\n font-weight: 400;\n }\n\n .pill.warn {\n color: var(--ui-warn, #9f5e02);\n border-color: color-mix(in srgb, var(--ui-warn, #9f5e02) 40%, transparent);\n font-weight: 400;\n }\n\n .pill.crit {\n color: var(--ui-crit, #c93f30);\n border-color: color-mix(in srgb, var(--ui-crit, #c93f30) 40%, transparent);\n font-weight: 400;\n }\n\n .pill.quiet {\n color: var(--ui-faint, #8a959c);\n }\n\n :host([gutter]) {\n padding: var(--grid-gutter-padding);\n text-align: center;\n }\n\n :host([gutter]) * {\n cursor: default;\n overflow: hidden;\n }\n\n :host([editing]) {\n border-top: var(--grid-record-editing-border);\n border-bottom: var(--grid-record-editing-border);\n }\n\n /*\n * 초점이 갔다고 **줄을 나누는 선을 바꾸지 않는다.**\n *\n * 전에는 여기서 --grid-record-border-bottom 을 초점 표식 값으로 덮어썼다. 그래서 표식을\n * 감추려고 그 토큰을 none 이나 투명으로 두면 **그 셀만 줄 선이 사라졌다.** 한 토큰이 「초점\n * 표식」과 「줄을 나누는 선」 두 가지를 겸하고 있었던 것이다.\n *\n * 초점 표식은 outline 이 그린다(data-grid-body-style.ts). 줄 선은 줄 선으로 남는다.\n */\n\n :host([dirty])::after {\n content: '';\n position: absolute;\n right: 0;\n top: 0;\n\n width: 0px;\n height: 0px;\n border-top: 9px solid red;\n border-left: 9px solid transparent;\n }\n\n span,\n pre {\n display: block;\n text-overflow: ellipsis;\n overflow: hidden;\n min-height: 1em;\n }\n\n :host([multiline]) span,\n :host([multiline]) pre {\n text-overflow: initial;\n overflow: visible;\n white-space: pre-line;\n word-break: break-word;\n overflow-wrap: anywhere;\n }\n\n * {\n margin: 0;\n text-align: var(--data-grid-field-text-align);\n }\n\n *[center] {\n flex: none;\n margin: 0 auto;\n }\n\n :host > button {\n display: flex;\n gap: var(--spacing-small);\n border: 0;\n align-items: center;\n padding: var(--spacing-small);\n\n color: var(--md-sys-color-on-primary);\n background-color: var(--md-sys-color-primary);\n border-radius: var(--data-card-item-btn-border-radius, var(--md-sys-shape-corner-tiny));\n\n font-size: 0.8rem;\n line-height: 0.8rem;\n\n md-icon {\n border: 0;\n padding: 0;\n background-color: unset;\n }\n }\n\n :host([danger]) > button {\n color: var(--md-sys-color-on-primary);\n background-color: var(--md-sys-color-error);\n border-color: var(--md-sys-color-error);\n }\n\n :host([danger]) > button:hover {\n color: var(--md-sys-color-on-secondary);\n background-color: var(--md-sys-color-secondary);\n border-color: var(--md-sys-color-secondary);\n }\n `\n ]\n\n @property({ type: String, attribute: true }) align = DEFAULT_TEXT_ALIGN\n @property({ type: Object }) record: GristRecord = {}\n @property({ type: Object }) column: ColumnConfig = ZERO_COLUMN\n @property({ type: Number }) rowIndex = 0\n @property({ type: Number }) columnIndex = 0\n @property({ type: Boolean }) editing = false\n @property({ type: Boolean, reflect: true }) multiline = false\n @property({ type: Object }) value = {}\n @property({ type: String }) valueWith: string | null = null\n @property({ attribute: false }) emphasized: any = false\n @property({ type: String, attribute: true }) fixed?: string\n @property({ type: String }) type?: string\n @property({ type: Boolean }) isWorking: boolean = false\n\n /* check in tree의 변화에 대응하기 위함임. 만일, record control 오브젝트로 통일되면 대체될 것임. */\n @property({ type: String }) checked?: string\n\n private _editCancelled?: boolean\n private _onFieldChange: (e: Event) => void = e => {}\n private _onKeydownInEditingMode: (e: KeyboardEvent) => void = e => {}\n\n private tooltipController?: TooltipReactiveController = new TooltipReactiveController(this)\n\n render(): TemplateResult {\n if (!this.column) {\n return html``\n }\n\n var { value, column, record, rowIndex } = this\n var { renderer, editor } = column.record\n\n return html`\n ${this.editing\n ? editor?.call(this, value, column, record, rowIndex, this)\n : renderer?.call(this, value, column, record, rowIndex, this)}\n <!--\n **편집 중에도 표식을 남긴다.**\n\n 칸을 누르면 편집 상태가 되고, 그때는 렌더러 대신 편집기가 그려진다. 표식을 !editing 으로\n 묶어 두었더니 **칸에 들어간 순간 사라졌다** — 들어간 사람에게 여는 길이 없어지는 셈이고,\n 다시 더블클릭이나 Enter 를 짐작해야 한다. 그 짐작을 없애려고 만든 표식이다.\n\n 팝업 편집기에만 붙으므로(opensPopup) 글자를 치는 편집기를 가릴 일은 없다.\n -->\n <!--\n ── 칸 안에서 고치는 중에는 표식을 걷는다 ────────────────────────────────\n 입력칸이 이미 그 자리에 있다. 펜을 겹쳐 두면 글자를 가리고, 고치는 중인 사람에게\n 「고칠 수 있다」를 다시 말할 이유도 없다.\n\n 팝업 쪽은 남긴다 — 편집 상태에 들어가도 팝업은 아직 안 열렸고, 그 표식이 여는 길이다.\n -->\n ${this.editMark && !(this.editing && this.editMark === 'inline')\n ? html`<md-icon\n popup-hint\n ?inline=${this.editMark === 'inline'}\n title=${i18next.t('text.grid-open-editor', { defaultValue: 'press to edit' })}\n @pointerdown=${async (e: Event) => {\n /*\n * **click 이 아니라 pointerdown 이다.**\n *\n * 이 표에서는 click 이벤트가 나지 않는다 — 몸통이 pointer 단계에서 기본 동작을\n * 막는다. document 에 capture 로 click 을 걸어 두고 표식을 선택해 봐도 한 번도\n * 울리지 않았다(2026-09-05 계측).\n */\n e.stopPropagation()\n e.preventDefault()\n\n /*\n * ── 포커스는 여기서 변경하지 않는다 ──────────────────────────────\n *\n * 팝업이 닫히면 오버레이가 **열릴 때 포커스가 있던 자리**로 돌려준다. 그런데 위에서\n * preventDefault 를 하므로 브라우저가 이 칸으로 포커스를 옮겨 주지 않고, 그래서\n * 팝업을 닫으면 엉뚱한 곳으로 간다(페이지 검색창이었다).\n *\n * 두 가지를 해 봤고 **둘 다 팝업이 안 열리게 만들었다.**\n *\n * focus-change 이벤트 몸통이 그 갱신에서 editTarget 을 지운다(shouldUpdate)\n * ime-buffer 에 직접 focus 같은 결과\n *\n * 이 표는 포커스를 ime-buffer 라는 숨은 입력칸에 담아 두고, 편집 상태를 그 갱신\n * 주기에 얹고 있다. 그 둘이 얽혀 있어서 밖에서 포커스를 옮기면 편집이 취소된다.\n *\n * 표의 포커스 모델을 손대야 풀리는 문제라 여기서 우회하지 않는다.\n * [확인함] 2026-09-05 · 두 방법 모두 팝업이 안 열림\n */\n\n this.dispatchEvent(\n new CustomEvent('start-edit', {\n bubbles: true,\n composed: true,\n detail: { row: this.rowIndex, column: this.columnIndex }\n })\n )\n\n /*\n * 칸 안에서 고치는 편집기는 여기서 끝난다 — 편집이 열리면 그 자리에 입력칸이 뜬다.\n * 팝업 편집기는 한 걸음 더 필요하다.\n */\n if (this.editMark !== 'popup') {\n return\n }\n\n /*\n * ── 편집을 열고, **그다음** 더블클릭을 편집기에 보낸다 ──────────────\n *\n * 사람이 더블클릭할 때의 순서를 계측해 보면 이렇다.\n *\n * startEditTarget 첫 클릭이 편집을 연다 → 편집기가 그려진다\n * dblclick 그다음에 도착해 **이미 있는 편집기**가 받아 팝업을 연다\n *\n * 편집기마다 여는 메서드 이름이 달라서 부품이 직접 부를 수는 없다. 사람이 하던\n * 동작을 같은 순서로 보내면 경로가 하나로 남는다.\n */\n for (let i = 0; i < 5 && !this.editing; i++) {\n await new Promise(resolve => requestAnimationFrame(resolve))\n }\n\n await this.updateComplete\n\n /*\n * Both events, in the order a browser sends them.\n *\n * The editors split on which one they open from, and sending only `dblclick`\n * reached two of the eight:\n *\n * _onclick json5 · code · resource-code · privilege · json · resource-id\n * _ondblclick resource-object · resource-object-legacy\n *\n * Those last two define `_onclick` as a no-op, so nothing opens twice.\n * [확인함] 2026-09-07 · 사용자 보고 — 코드 편집기가 아이콘으로 안 열렸다\n */\n const editor = this.renderRoot.firstElementChild as HTMLElement | null\n editor?.dispatchEvent(new MouseEvent('click', { bubbles: true, composed: true }))\n editor?.dispatchEvent(new MouseEvent('dblclick', { bubbles: true, composed: true }))\n }}\n >${this.editMark === 'popup' ? 'edit_square' : 'edit'}</md-icon\n >`\n : ''}\n `\n }\n\n updated(changes: PropertyValues<this>) {\n if (changes.has('editing')) {\n if (this.editing) {\n this._onKeydownInEditingMode = ((e: KeyboardEvent) => {\n if (e.key === 'Esc' || e.key === 'Escape') {\n /* 편집 취소 */\n this._editCancelled = true\n }\n }).bind(this)\n\n this._onFieldChange = ((e: Event) => {\n this._editCancelled && e.stopPropagation()\n }).bind(this)\n\n delete this._editCancelled\n this.addEventListener('field-change', this._onFieldChange)\n this.addEventListener('keydown', this._onKeydownInEditingMode)\n } else {\n this.removeEventListener('field-change', this._onFieldChange)\n this.removeEventListener('keydown', this._onKeydownInEditingMode)\n }\n }\n\n if (changes.has('column')) {\n /*\n * 정렬은 **넣을 때만이 아니라 되돌릴 때도** 손대야 한다.\n *\n * 이 요소는 재사용된다. 전에는 기본값이 아닐 때만 인라인 값을 넣고 지우지 않았다. 그래서 한 번\n * 오른쪽 정렬 칸을 그린 셀이 다음에 왼쪽 정렬 칸을 그려도 오른쪽에 남았다.\n *\n * [확인함] 품질 화면 「판단」 칸에서 머리글은 x=380(왼쪽)인데 값은 x=452(flex-end)였다. 숫자 칸을\n * 오른쪽으로 바꾼 뒤 드러났다 — 오른쪽 정렬 칸이 드물던 동안은 눈에 안 띄었다. 2026-09-02\n */\n var align = this.column.record.align || DEFAULT_TEXT_ALIGN\n\n if (align == DEFAULT_TEXT_ALIGN) {\n this.style.removeProperty('--data-grid-field-justify-content')\n this.style.removeProperty('--data-grid-field-text-align')\n } else {\n let justify = 'center'\n switch (align) {\n case 'right':\n justify = 'flex-end'\n break\n }\n this.style.setProperty('--data-grid-field-justify-content', justify)\n this.style.setProperty('--data-grid-field-text-align', align)\n }\n\n /* 표식은 값의 반대쪽 빈자리에 선다 — 위 :host([mark-start]) 주석 참고. */\n this.toggleAttribute('mark-start', align === 'right')\n\n const { danger } = this.column\n if (danger) {\n this.setAttribute('danger', '')\n } else {\n this.removeAttribute('danger')\n }\n }\n\n if (changes.has('fixed')) {\n if (this.fixed) {\n this.style.left = this.fixed + 'px'\n } else {\n this.style.left = 'unset'\n }\n }\n\n let disabled = this.column.record.disabled || false\n if (typeof disabled === 'function') {\n disabled = disabled.call(this, this.record[this.column.name], this.column, this.record, this.rowIndex, this)\n }\n\n if (!!disabled) {\n // 비황성화 여부 표시\n this.setAttribute('disabled', '')\n } else {\n // 비황성화 여부 제거\n this.removeAttribute('disabled')\n }\n\n const emphasized =\n /*\n * classifier 는 없을 수 있다. 열 빌더가 채워 주지만 손으로 만든 열에는 없고, 그때 여기서\n * 던진다. 갱신 중에 나는 예외라 화면이 오류보고없이 멈춘다.\n *\n * [확인함] 2026-09-05 · wtr 에서 Cannot read properties of undefined (reading 'call')\n * 팝업 표식을 넣어 칸이 전보다 자주 갱신되면서 드러났다. 원래 있던 자리다.\n */\n this.column.record.classifier?.call(this, this.record, this.rowIndex)?.emphasized || this.emphasized\n\n if (!!emphasized) {\n this.setAttribute('emphasized-row', '')\n\n if (Symbol.iterator in Object(emphasized)) {\n const [backgroundColor, foregroundColor] = emphasized as string[]\n backgroundColor && this.style.setProperty('--grid-record-emphasized-background-color', backgroundColor)\n foregroundColor && this.style.setProperty('--grid-record-emphasized-color', foregroundColor)\n }\n } else {\n this.removeAttribute('emphasized-row')\n }\n }\n\n /**\n * 이 칸에 **팝업 표식**을 붙이나 — 팝업으로 고쳐지고, 지금 고칠 수 있을 때만.\n *\n * 고칠 수 있는지는 열이 함수로 정할 수도 있다(줄마다 다른 경우). 그때는 몸통이 편집을 열 때\n * 하는 것과 같은 방식으로 물어본다. 표식은 있는데 선택해도 안 열리면 그것이 더 나쁘다.\n */\n get opensPopup(): boolean {\n return this.editMark === 'popup'\n }\n\n /**\n * 이 칸을 **고칠 수 있나, 그리고 어디서 고치나.**\n *\n * popup 팝업이 열린다 — 네모와 펜\n * inline 칸 안에서 고친다 — 펜\n * null 못 고친다 — 표식 없음\n *\n * 표식이 있으면 고칠 수 있는 칸이고, 모양이 어디서 고치는지를 말한다.\n *\n * 고칠 수 있는지는 열이 함수로 정할 수도 있다(줄마다 다른 경우). 그때는 몸통이 편집을 열 때\n * 하는 것과 같은 방식으로 물어본다. 표식은 있는데 선택해도 안 열리면 그것이 더 나쁘다.\n */\n get editMark(): 'popup' | 'inline' | null {\n const column = this.column\n\n if (!column) {\n return null\n }\n\n /* 거터에는 붙이지 않는다 — 순번과 고르기 칸은 고치는 자리가 아니다. */\n if (column.type === 'gutter') {\n return null\n }\n\n /*\n * 표 오른쪽의 **빈 채움 열**에도 붙이지 않는다. 남는 자리를 메우는 20px 짜리 빈 열이라\n * 이름도 타입도 없다 — 고칠 것이 없는데 표식만 떴다.\n *\n * [확인함] 2026-09-05 · 인원 화면 맨 끝 칸 · { type: '', name: '', label: false } · 폭 20\n */\n if (!column.name) {\n return null\n }\n\n let { editable } = column.record || ({} as any)\n\n if (typeof editable === 'function') {\n editable = editable.call(this, this.value, column, this.record, this.rowIndex, this)\n }\n\n /*\n * **밝히지 않은 열에는 붙이지 않는다.**\n *\n * 표 전체가 고칠 수 있는지를 판단하는 자리가 같은 기준을 쓴다.\n *\n * [읽었음] data-grid.ts · editableGrid = columns.some(c => !!c.record?.editable)\n *\n * 참일 때만 고칠 수 있는 열로 본다. 선언하지 않은 열까지 세면 **고르기만 하는 표**에도\n * 표식이 깔린다 — 참조 팝업의 목록이 그랬다(2026-09-05). 그 표의 칸은 고르는 자리이지\n * 고치는 자리가 아니다.\n */\n if (!editable) {\n return null\n }\n\n /*\n * 편집기를 **함수로 직접 준 열**은 타입으로 판단할 수 없다. 그리기를 다른 렌더러로 하려고\n * 타입을 바꾼 열이 그렇다 — 관계를 여럿 다는 칸이 `ops-list` 로 그려지면서 편집은 참조\n * 팝업으로 한다(ops-master 의 reference-list).\n *\n * 그런 열은 `record.opensPopup` 으로 스스로 밝힌다. 밝히지 않으면 타입으로 본다.\n *\n * [확인함] 2026-09-05 · 보유 자격 · 타입 ops-list · 팝업으로 열리는데 표식이 inline 이었다\n */\n const declared = (column.record as any)?.opensPopup\n\n if (typeof declared === 'boolean') {\n return declared ? 'popup' : 'inline'\n }\n\n return editorOpensPopup(column.type) ? 'popup' : 'inline'\n }\n\n get editableOnClick() {\n const renderer = this.renderRoot.firstElementChild as HTMLElement\n //@ts-ignore\n return renderer && 'editableOnClick' in renderer ? renderer.editableOnClick : true\n }\n}\n"]}
@@ -520,13 +520,28 @@ export type GristEventHandlerSet = {
520
520
  *
521
521
  * @typedef {Object} ListConfig
522
522
  * @property {string} [thumbnail] - The name of the field to use as a thumbnail image for records in list mode.
523
+ * @property {object} [emptyThumbnail] - What to draw where a record has no thumbnail. The slot keeps its
524
+ * place either way so cards stay the same height; this says what it should say. Without it the slot is
525
+ * drawn empty.
523
526
  * @property {string[]} fields - An array of field names to display in the list mode.
524
527
  * @property {string[]} details - An array of field names to display as additional details in the list mode.
525
528
  */
526
529
  export type ListConfig = {
527
530
  thumbnail?: string;
531
+ emptyThumbnail?: {
532
+ /** Material symbol name. */
533
+ icon?: string;
534
+ /** One short line — already translated by the caller. */
535
+ label?: string;
536
+ };
528
537
  fields: string[];
529
538
  details: string[];
539
+ /**
540
+ * When gutter buttons show on a card. 'always' (default) keeps them visible — right where the
541
+ * button is the point of the card. 'hover' reveals them on hover or keyboard focus, for lists
542
+ * where picking is the point and a destructive button should not sit on every card.
543
+ */
544
+ gutters?: 'always' | 'hover';
530
545
  };
531
546
  /**
532
547
  * Configuration options for managing tree-like structures in a Grist table.
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AA6LA;;;;;;;;GAQG;AACH,MAAM,CAAN,IAAY,kBAIX;AAJD,WAAY,kBAAkB;IAC5B,mCAAa,CAAA;IACb,yCAAmB,CAAA;IACnB,mCAAa,CAAA;AACf,CAAC,EAJW,kBAAkB,KAAlB,kBAAkB,QAI7B;AAknBD,MAAM,CAAN,IAAY,gBAKX;AALD,WAAY,gBAAgB;IAC1B,2CAAuB,CAAA;IACvB,qCAAiB,CAAA;IACjB,mCAAe,CAAA;IACf,qCAAiB,CAAA;AACnB,CAAC,EALW,gBAAgB,KAAhB,gBAAgB,QAK3B","sourcesContent":["import { TemplateResult } from 'lit-html'\n\nimport { DataCardField } from './data-card/data-card-field.js'\nimport { DataCardGutter } from './data-card/data-card-gutter.js'\nimport { RecordCard } from './data-card/record-card.js'\nimport { DataGridField } from './data-grid/data-grid-field.js'\nimport { DataListField } from './data-list/data-list-field.js'\nimport { DataListGutter } from './data-list/data-list-gutter.js'\nimport { RecordPartial } from './data-list/record-partial.js'\nimport { DataReportField } from './data-report/data-report-field.js'\nimport { OxGristEditor } from './editors/index.js'\nimport { QueryFilter, OxFiltersForm } from './filters/index.js'\nimport { OxGristRenderer } from './renderers/ox-grist-renderer.js'\n\n/**\n * Configuration object for defining the structure and behavior of a Grist table.\n *\n * @typedef {Object} GristConfig\n * @property {ColumnConfig[]} columns - An array of column configurations.\n * @property {RowsConfig} rows - Configuration for rows and their behavior.\n * @property {ListConfig} list - Configuration for the list view.\n * @property {TreeConfig} tree - Configuration for tree-related settings.\n * @property {PaginationConfig} [pagination] - Configuration for pagination settings (optional).\n * @property {SortersConfig} [sorters] - Configuration for sorting settings (optional).\n * @property {FiltersConfig} [filters] - Configuration for filtering settings (optional).\n */\nexport type GristConfig = {\n columns: ColumnConfig[]\n rows: RowsConfig\n list: ListConfig\n tree: TreeConfig\n pagination?: PaginationConfig\n sorters?: SortersConfig\n filters?: FiltersConfig\n /**\n * 아직 아무것도 없을 때 화면이 하는 말.\n *\n * 전에는 「NO RECORDS」가 컴포넌트에 박혀 있었다. 그 문장은 다음에 무엇을 할지 알려 주지 않는다 —\n * **비어 있는 화면이야말로** 「여기서 무엇을 만들 수 있고 그것이 무엇에 쓰이나」를 말해야 하는 자리이고,\n * 처음 오는 사람이 가장 먼저 보는 화면이기도 하다.\n *\n * 정하지 않으면 예전 문장이 그대로 나온다.\n */\n emptyNote?: { title?: string; description?: string; icon?: string }\n}\n\n/**\n * Configuration object for specifying sorting options.\n *\n * @typedef {Object} SorterConfig\n * @property {string} name - The name of the sorting option.\n * @property {boolean} [desc] - Optional. Indicates whether the sorting is in descending order (optional).\n */\nexport type SorterConfig = { name: string; desc?: boolean }\n\n/**\n * Configuration array for specifying multiple sorting options.\n *\n * @typedef {SorterConfig[]} SortersConfig\n */\nexport type SortersConfig = SorterConfig[]\n\n/**\n * Enumeration of filter operators used to define filter conditions.\n *\n * @typedef {'search' | 'eq' | 'between' | 'gte' | 'lte' | 'is_not_true' | 'in' | 'like' | 'i_like' | 'noteq' |\n * 'is_empty_num_id' | 'is_blank' | 'is_present' | 'is_not_false' | 'is_true' | 'is_false' | 'is_not_null' |\n * 'is_null' | 'notin_with_null' | 'notin' | 'gt' | 'lt' | 'i_nlike' | 'nlike'} FilterOperator\n */\nexport type FilterOperator =\n | 'search'\n | 'eq'\n | 'between'\n | 'gte'\n | 'lte'\n | 'is_not_true'\n | 'in'\n | 'like'\n | 'i_like'\n | 'noteq'\n | 'is_empty_num_id'\n | 'is_blank'\n | 'is_present'\n | 'is_not_false'\n | 'is_true'\n | 'is_false'\n | 'is_not_null'\n | 'is_null'\n | 'notin_with_null'\n | 'notin'\n | 'gt'\n | 'lt'\n | 'i_nlike'\n | 'nlike'\n\nexport type SelectOptionObject =\n | {\n name: string\n display?: never\n value: string\n }\n | {\n display: string\n name?: never\n value: string\n }\n\nexport type SelectOption = SelectOptionObject | string\n\nexport type FilterChangedCallback = (value: any, form: OxFiltersForm) => boolean\n\n/**\n * Configuration object for specifying filter conditions.\n *\n * @typedef {Object} FilterConfigObject\n * @property {string} type - The type of the filter condition.\n * @property {FilterOperator} [operator] - The filter operator used to compare values (optional).\n * @property {Object} [options] - Additional options or parameters for the filter condition (optional).\n * @property {boolean|undefined} [hidden] - The hidden flag for the filter condition (optional).\n * @property {string|number|boolean|string[]|number[]|undefined} [value] - The value to compare with in the filter condition (optional).\n * @property {string} [label] - The label to display for the filter condition (optional).\n */\nexport type FilterConfigObject = {\n type: string\n operator?: FilterOperator\n options?: { [key: string]: any }\n value?: string | number | boolean | string[] | number[] | undefined\n label?: string\n boundTo?: string[]\n hidden?: boolean\n onchange?: FilterChangedCallback\n}\n\n/**\n * Configuration option for specifying filter conditions.\n *\n * The `FilterConfig` type can take one of the following forms:\n * - A `FilterConfigObject` representing a specific filter condition.\n * - A `FilterOperator` representing a predefined filter operator.\n * - A boolean value to enable or disable filtering for a specific column.\n *\n * @typedef {Object|FilterOperator|boolean} FilterConfig\n */\nexport type FilterConfig = FilterConfigObject | FilterOperator | boolean\n\n/**\n * Represents a filter condition with the following properties:\n *\n * - `name`: The name of the column or field being filtered.\n * - `operator`: The filter operator used for comparison.\n * - `value`: The value or values to compare against.\n *\n * @typedef {Object} FilterValue\n * @property {string} name - The name of the column or field.\n * @property {FilterOperator} operator - The filter operator.\n * @property {string|number|boolean|string[]|number[]|undefined} value - The value(s) to compare.\n */\nexport type FilterValue = {\n name: string\n operator: FilterOperator\n value: string | number | boolean | string[] | number[] | undefined\n}\n\n/**\n * Configuration options for filters.\n *\n * @typedef {Object} FiltersConfig\n * @property {boolean} [header] - Specifies whether to provide filtering functionality in the header.\n */\nexport type FiltersConfig = {\n header?: boolean\n}\n\n/**\n * Configuration options for pagination.\n *\n * @typedef {Object} PaginationConfig\n * @property {number} [page] - The current page number.\n * @property {number} [limit] - The maximum number of records to display per page.\n * @property {number[]} [pages] - An array of available page numbers.\n * @property {boolean} [infinite] - Specifies whether to use infinite scrolling for pagination.\n */\nexport type PaginationConfig = {\n page?: number\n limit?: number\n pages?: number[]\n infinite?: boolean\n}\n\n/**\n * Option to specify the inheritance behavior for including data from parent domains.\n *\n * - 'None': Do not include data from the parent domain.\n * - 'Include': Include data from the parent domain along with current domain data.\n * - 'Only': Include data only from the parent domain, excluding current domain data.\n *\n * @enum {string}\n */\nexport enum InheritedValueType {\n None = 'None',\n Include = 'Include',\n Only = 'Only'\n}\n\n/**\n * Configuration options for fetching data from a data source.\n *\n * @typedef {Object} FetchOption\n * @property {number} [page] - The page number to retrieve.\n * @property {number} [limit] - The maximum number of records to retrieve per page.\n * @property {SortersConfig} [sorters] - An array of sorting configurations for data. Only one of these options needs to be set.\n * @property {SortersConfig} [sortings] - An array of sorting configurations for data. Only one of these options needs to be set.\n * @property {QueryFilter[]} [filters] - An array of filtering criteria.\n * @property {InheritedValueType} [inherited] - Specifies the inheritance behavior for including data from parent domains.\n * @property {object} [options] - Additional options for customizing data fetching behavior.\n */\nexport type FetchOption = {\n page?: number\n limit?: number\n sorters?: SortersConfig\n sortings?: SortersConfig\n filters?: QueryFilter[]\n inherited?: InheritedValueType\n options?: object\n}\n\n/**\n * Represents the result of a data fetching operation.\n *\n * @typedef {Object} FetchResult\n * @property {number} [page] - The page number of the fetched data.\n * @property {number} [limit] - The maximum number of records per page.\n * @property {number} total - The total number of records in the data source.\n * @property {GristRecord[]} records - An array of fetched data records.\n */\nexport type FetchResult = {\n page?: number\n limit?: number\n total: number\n records: GristRecord[]\n} | void\n\n/**\n * A function that handles data fetching operations.\n *\n * @param param - The fetch options specifying how data should be retrieved.\n * @returns A promise that resolves to a `FetchResult`.\n */\nexport type FetchHandler = (param: FetchOption) => Promise<FetchResult>\n\n/**\n * Represents a Grist event handler function.\n *\n * @typedef {Function} GristEventHandler\n * @param {ColumnConfig[]} columns - An array of column configurations.\n * @param {GristData} [data] - The Grist data associated with the event.\n * @param {ColumnConfig} [column] - The column configuration related to the event (if applicable).\n * @param {GristRecord} [record] - The Grist record related to the event (if applicable).\n * @param {number} [rowIndex] - The index of the row related to the event (if applicable).\n * @param {any} [target] - The target element or object related to the event (if applicable).\n * @param {Event} [e] - The event object (if applicable).\n * @returns {void}\n */\nexport type GristEventHandler = (\n columns: ColumnConfig[],\n data?: GristData,\n column?: ColumnConfig,\n record?: GristRecord,\n rowIndex?: number,\n target?: any,\n e?: Event\n) => void\n\n/**\n * Represents an accumulator function for data aggregation and accumulation.\n *\n * @typedef {string|'sum'|'avg'|'count'|'min'|'max'|Function} AccumulatorFunc\n * @param {GristData} data - The Grist data for aggregation.\n * @param {ColumnConfig} column - The column configuration for aggregation.\n * @returns {string|number} - The aggregated result.\n */\nexport type AccumulatorFunc =\n | 'sum'\n | 'avg'\n | 'count'\n | 'min'\n | 'max'\n | ((data: GristData, column: ColumnConfig) => string | number)\n\n/**\n * Type representing an accumulator function or configuration object.\n *\n * An accumulator is used for data aggregation and accumulation purposes. It can be either\n * a predefined accumulator function (e.g., 'sum', 'avg', 'count', etc.) or a configuration\n * object specifying the type of accumulator and labeling options.\n *\n * @typedef {AccumulatorFunc|{type: AccumulatorFunc, tag?: boolean}} AccumulatorObject\n * @property {AccumulatorFunc} [type] - The type of accumulator function or predefined function name.\n * @property {boolean} [tag] - An option for adding a label to represent the result of an aggregation function. This label is added in front of the aggregated value.\n *\n * @example\n * // Using a predefined accumulator function:\n * const accumulator: AccumulatorObject = 'sum'; // Represents a 'sum' accumulator.\n *\n * // Using an accumulator configuration object:\n * const accumulatorConfig: AccumulatorObject = {\n * type: 'avg', // Specifies the type of accumulator ('avg' for average).\n * tag: true, // Option for adding a label to represent the result of an aggregation function. This label is added in front of the aggregated value.\n * };\n */\nexport type AccumulatorObject =\n | AccumulatorFunc\n | {\n type: AccumulatorFunc\n tag?: boolean\n }\n\n/**\n * Type representing column configuration information.\n *\n * @typedef {object} ColumnConfig\n * @property {string} type - Represents the data type of the column (e.g., 'text', 'number', 'date', 'gutter', etc.).\n * @property {string} name - Represents the name of the column.\n * @property {('button'|'sequence'|'row-selector'|'dirty')} [gutterName] - If the column type is 'gutter', specifies the name of one of the registered gutters (only set when the type is 'gutter').\n * @property {boolean} [fixed] - In case of a grid type column, specifies whether the column's position should be fixed and unaffected by horizontal scrolling (optional).\n * @property {HeaderConfig} header - Object representing the header configuration of the column.\n * @property {RecordConfig} record - Object representing the record configuration of the column.\n * @property {GristEventHandlerSet} handlers - Object representing the Grist event handler settings.\n * @property {LabelConfig} label - Object representing the label configuration of the column. This property is used to represent the label of the column's information in list mode, card mode, and when displaying the column in a filter form (ox-filters-form).\n * @property {boolean} [hidden] - Indicates whether the column is hidden or not (optional).\n * @property {boolean} [sortable] - Indicates whether the column is sortable or not (optional). If this setting is true, the header provides the ability to toggle the sorting direction.\n * @property {boolean} [resizable] - Indicates whether the column is resizable or not (optional). This setting determines whether the column's width can be adjusted.\n * @property {(number|string|ColumnWidthCallback)} [width] - Represents the width of the column (e.g., 100, '50%', or a callback function to calculate width).\n * @property {boolean} [forList] - Indicates whether the column is intended for display in list mode (optional, applicable only to gutter type columns).\n * @property {boolean} [forCard] - Indicates whether the column is intended for display in card mode (optional, applicable only to gutter type columns).\n * @property {ValidationCallback} [validation] - Callback function for column validation (optional). This function validates whether the value of the column is valid when edited.\n * @property {AccumulatorObject} [accumulator] - Object used for accumulating values of the column (optional, applicable only in grid mode). It specifies a function to automatically aggregate the values of the column for all records. The aggregated result is displayed in a row below the last record (accumulation record).\n * @property {FilterConfig} [filter] - Object representing the filter configuration of the column.\n * @property {(ImexConfig|boolean)} [imex] - Object or boolean value representing the configuration for importing and exporting column data.\n * @property {boolean} [multiple] - Indicates whether the column allows multiple selections or not (optional).\n * @property {boolean} [rowCount] - Indicates whether the column shows the row count or not (optional).\n */\nexport type ColumnConfig = {\n type: string\n name: string\n /**\n * \"gutter\" 타입의 컬럼인 경우, 등록된 gutter들 중의 하나의 이름을 명시합니다(type이 gutter인 경우에만 설정합니다).\n * (e.g., 'button', 'sequence', 'row-selector', 'dirty', etc)\n *\n * \"Gutter\" 는 테이블에서 각 행의 왼쪽이나 오른쪽에 놓이는 추가 열(column)을 뜻합니다.\n * 이 열은 주로 각 행(row)을 식별하거나 특정 작업을 수행하기 위한 버튼 또는 링크와 같은 요소를 표시하는 데 사용됩니다.\n * Gutter 열은 주로 데이터 행에 대한 조작 또는 행의 상태를 나타내는 데 사용됩니다.\n * 예를 들어, 테이블에서 각 행이 특정 작업을 수행하는 버튼을 포함할 때, 이러한 버튼이 Gutter 열에 배치될 수 있습니다.\n * 또한 Gutter 열은 행을 선택하거나 행의 상태를 나타내는 체크박스를 포함할 수도 있습니다.\n */\n gutterName?: 'button' | 'sequence' | 'row-selector' | 'dirty'\n title?: string\n fixed?: boolean\n /** [begin] only for button gutter column */\n icon?: string\n iconOnly?: boolean\n danger?: boolean\n /** [end] only for button gutter column */\n header: HeaderConfig\n record: RecordConfig\n handlers: GristEventHandlerSet\n label: LabelConfig\n hidden?: boolean\n sortable?: boolean\n resizable?: boolean\n width?: number | string | ColumnWidthCallback\n forList?: boolean\n forCard?: boolean\n validation?: ValidationCallback\n accumulator?: AccumulatorObject\n filter?: FilterConfig\n imex?: ImexConfig | boolean\n multiple?: boolean\n rowCount?: boolean\n /** 특정 도구에 의해서 사용되는 옵션으로, 일반적인 용도로 사용되지 않음을 표현할 수 있다. */\n unusable?: boolean\n multiline?: boolean // 멀티라인 여부\n}\n\n/**\n * Callback function type for column validation.\n *\n * This function is used to validate whether the value of a column is valid when edited.\n *\n * @callback ValidationCallback\n * @param {any} after - The new value of the column after editing.\n * @param {any} before - The previous value of the column before editing.\n * @param {GristRecord} record - The record being edited.\n * @param {ColumnConfig} column - The configuration of the column being edited.\n * @returns {boolean} - Returns `true` if the value is valid, `false` otherwise.\n */\nexport type ValidationCallback = (\n after: any,\n before: any,\n record: GristRecord,\n column: ColumnConfig\n) => boolean | Promise<boolean>\n\n/**\n * Configuration options for column labels.\n *\n * The label for a column can be customized using various options.\n *\n * @typedef {string|boolean|Object} LabelConfig\n * @property {LabelRenderer} [renderer] - A custom renderer function for the column label.\n *\n * @example\n * // Using a string as a label:\n * const label: LabelConfig = \"Column Label\"; // Sets the label as \"Column Label\".\n *\n * // Using a boolean to determine label visibility:\n * const label: LabelConfig = true; // Shows the default label based on the header configuration.\n *\n * // Using a custom renderer function for the label:\n * const label: LabelConfig = {\n * renderer: customLabelRenderer, // Specifies a custom renderer function.\n * };\n */\nexport type LabelConfig =\n | string\n | boolean\n | {\n renderer: LabelRenderer\n /** Same as HeaderConfig.text — the name itself, not what was drawn from it. */\n text?: string\n }\n\n/**\n * Custom label renderer function for generating column labels.\n *\n * @callback LabelRenderer\n * @param {ColumnConfig} column - The column configuration object for which the label is being generated.\n * @returns {void}\n */\nexport type LabelRenderer = (column: ColumnConfig) => void\n\n/**\n * Custom callback function for calculating column width.\n *\n * @callback ColumnWidthCallback\n * @param {ColumnConfig} column - The column configuration object for which the width is being calculated.\n * @returns {string} - The calculated width as a string (e.g., '100', '50%').\n */\nexport type ColumnWidthCallback = (column: ColumnConfig) => string\n\n/**\n * Configuration for defining the header of a column.\n *\n * @typedef {Object} HeaderConfig\n * @property {HeaderRenderer} renderer - The function used to render the column header.\n * @property {string} [style] - Optional CSS style for the column header.\n * @property {string} [group] - Optional group identifier for the column header.\n * @property {string} [groupStyle] - Optional CSS style for the header group.\n */\nexport type HeaderConfig = {\n renderer: HeaderRenderer\n /**\n * The column's name as plain text.\n *\n * `renderer` returns whatever gets drawn — a lit template, an icon — and the name cannot be read\n * back out of that. So a name given as a string in the config is kept here, for the places that\n * need it outside of rendering: the accessible name of a cell, and header filter inputs.\n */\n text?: string\n style?: string\n group?: string\n groupStyle?: string\n}\n\n/**\n * Function type for rendering column headers.\n *\n * @callback HeaderRenderer\n * @param {ColumnConfig} column - The column configuration object.\n * @returns {*} - The rendered header content.\n */\nexport type HeaderRenderer = (column: ColumnConfig) => any\n\n/**\n * Configuration for specifying default values for fields in a record.\n * Default values can be set using predefined value generator functions or custom user-defined functions.\n * This configuration allows you to define the default value generation method and any additional parameters.\n *\n * @typedef {Object|Function} DefaultValueFnConfig\n * @property {string} [name] - The name of the default value function to be used for the column.\n * @property {any[]} [params] - The parameters to be passed to the default value function.\n * @property {Function} [Function] - A custom user-defined function for generating default values.\n */\nexport type DefaultValueFnConfig =\n | {\n /**\n * The name of the default value function to be used for the column.\n */\n name: string\n\n /**\n * The parameters to be passed to the default value function.\n */\n params?: any[]\n }\n | Function\n\n/**\n * Configuration for defining fields within a record.\n * Each field can have various settings such as rendering, editing, alignment, etc.\n * The 'defaultValue' property is used to specify the default value generation method and parameters.\n *\n * @typedef {Object} RecordConfig\n * @property {FieldRenderer} renderer - The rendering function for the field.\n * @property {FieldEditor|undefined} [editor] - The editing function for the field (optional).\n * @property {boolean|Function} [editable] - Specifies if the field is editable. It can be a boolean or a function (optional).\n * @property {boolean} [mandatory] - Specifies if the field is mandatory (optional).\n * @property {GristClassifier} classifier - A classifier function for the field.\n * @property {'left'|'right'|'center'} [align] - The alignment of the field's content (optional).\n * @property {*} options - Additional options for the field.\n * @property {string|undefined} [rowOptionField] - The name of a field for row-level options associated with this field (optional).\n * @property {DefaultValueFnConfig|undefined} [defaultValue] - Configuration for specifying the default value for the field.\n * It can be a predefined value generator function or a custom user-defined function (optional).\n * @property {Object.<string, *>|undefined} [extended] - Additional extended properties that can be added as needed for specific field configurations (optional).\n * @property {boolean} [wide] - Specifies if the field should occupy a larger width within the viewer or editor.\n * When set to `true`, the field will attempt to span across multiple columns or occupy more space horizontally.\n */\nexport type RecordConfig = {\n renderer: FieldRenderer\n editor?: FieldEditor\n editable?: boolean | Function\n mandatory?: boolean\n disabled?: boolean | Function\n classifier: GristClassifier\n align?: 'left' | 'right' | 'center'\n options: any\n rowOptionField?: string\n defaultValue?: DefaultValueFnConfig\n wide?: boolean\n [extended: string]: any\n}\n\n/**\n * A function type used for rendering the content of a field in various components.\n *\n * @callback FieldRenderer\n * @param {*} value - The value of the field to be rendered.\n * @param {ColumnConfig} column - The configuration of the column to which the field belongs.\n * @param {GristRecord} record - The record object containing the field's data.\n * @param {number} rowIndex - The index of the row to which the field belongs.\n * @param {DataGridField|RecordCard|DataCardGutter|DataCardField|DataListGutter|DataListField|RecordPartial|DataReportField|Element} owner - The component or element that owns the field.\n * @returns {OxGristRenderer|TemplateResult|string|void} - The rendered content, which can be of different types depending on the implementation.\n */\nexport type FieldRenderer = (\n value: any,\n column: ColumnConfig,\n record: GristRecord,\n rowIndex: number,\n owner:\n | DataGridField\n | RecordCard\n | DataCardGutter\n | DataCardField\n | DataListGutter\n | DataListField\n | RecordPartial\n | DataReportField\n | Element\n) => OxGristRenderer | TemplateResult | string | void\n\n/**\n * A function type used for defining a custom editor for a field in a data grid.\n *\n * @callback FieldEditor\n * @param {*} value - The current value of the field.\n * @param {ColumnConfig} column - The configuration of the column to which the field belongs.\n * @param {GristRecord} record - The record object containing the field's data.\n * @param {number} rowIndex - The index of the row to which the field belongs.\n * @param {DataGridField} field - The data grid field that owns the editor.\n * @returns {OxGristEditor} - The custom editor component.\n */\nexport type FieldEditor = (\n value: any,\n column: ColumnConfig,\n record: GristRecord,\n rowIndex: number,\n field: DataGridField\n) => OxGristEditor\n\n/**\n * A function type used for rendering the filter select options in a filter form.\n *\n * @callback FilterSelectRenderer\n * @param {ColumnConfig} column - The configuration of the column for which the filter is being rendered.\n * @param {(string|number|boolean|string[]|number[]|any|undefined)} value - The current value of the filter.\n * @param {Element} owner - The DOM element that owns the filter select renderer.\n * @returns {(TemplateResult|string|void)} - The rendered filter select options as a Lit HTML template result, a string, or nothing.\n */\nexport type FilterSelectRenderer = (\n column: ColumnConfig,\n value: string | number | boolean | string[] | number[] | any | undefined,\n owner: Element\n) => TemplateResult | string | void\n\n/**\n * An object representing a set of event handler functions for various events in Grist.\n *\n * @typedef {Object} GristEventHandlerSet\n * @property {GristEventHandler} [click] - The click event handler function.\n * @property {GristEventHandler} [dblclick] - The double-click event handler function.\n * @property {GristEventHandler} [focus] - The focus event handler function.\n * @property {GristEventHandler} [contextmenu] - The context menu event handler function.\n */\nexport type GristEventHandlerSet = {\n click?: GristEventHandler\n dblclick?: GristEventHandler\n focus?: GristEventHandler\n contextmenu?: GristEventHandler\n}\n\n/**\n * Configuration options for defining how records are displayed in list mode in a Grist.\n *\n * @typedef {Object} ListConfig\n * @property {string} [thumbnail] - The name of the field to use as a thumbnail image for records in list mode.\n * @property {string[]} fields - An array of field names to display in the list mode.\n * @property {string[]} details - An array of field names to display as additional details in the list mode.\n */\nexport type ListConfig = {\n thumbnail?: string\n fields: string[]\n details: string[]\n}\n\n/**\n * Configuration options for managing tree-like structures in a Grist table.\n *\n * @typedef {Object} TreeConfig\n * @property {string} [childrenProperty] - The property name that specifies the children of a record.\n * @property {boolean | Function} [expanded] - Specifies whether tree nodes are initially expanded or a function to determine the initial expansion state for a given record.\n */\nexport type TreeConfig = {\n childrenProperty?: string\n expanded?: boolean | ((x: GristRecord) => boolean)\n}\n\n/**\n * Configuration options for importing and exporting column data.\n *\n * @typedef {Object} ImexConfig\n * @property {string} header - The header text for the column in import/export settings.\n * @property {string} key - The key or identifier for the column in import/export settings.\n * @property {number} width - The width of the column in import/export settings.\n * @property {string} type - The data type of the column in import/export settings.\n */\nexport type ImexConfig = {\n header: string\n key: string\n width: number\n type: string\n}\n\n/**\n * Configuration options for defining rows within a Grist table or view.\n *\n * @typedef {Object} RowsConfig\n * @property {boolean} [accumulator] - Specifies whether an accumulator row should be displayed.\n * @property {boolean} appendable - Indicates whether new rows can be appended to the table.\n * @property {boolean} insertable - Indicates whether new rows can be inserted into the table.\n * @property {RowSelectableConfig} [selectable] - Configuration for row selection options.\n * @property {GroupConfig[]} groups - An array of group configuration options.\n * @property {string[]} totals - An array of field names for which totals should be calculated and displayed.\n * @property {GristClassifier} classifier - A function that classifies rows based on their attributes.\n * @property {GristEventHandlerSet} handlers - An object containing event handler functions for various row-level events.\n */\nexport type RowsConfig = {\n accumulator?: boolean\n appendable: boolean\n editable: boolean\n insertable: boolean\n selectable?: RowSelectableConfig\n groups: GroupConfig[]\n totals: string[]\n classifier: GristClassifier\n handlers: GristEventHandlerSet\n}\n\n/**\n * A function used to classify and style individual records in a Grist table.\n *\n * @typedef {Function} GristClassifier\n *\n * @param {GristRecord} record - The record to be classified.\n * @param {number} rowIndex - The index of the row in the table.\n *\n * @returns {Object|void} An object containing classification properties or `void`.\n * - `emphasized` (optional): A boolean, string, or array of strings indicating the emphasis style.\n * - Additional properties can be added as needed for custom styling.\n */\nexport type GristClassifier = (\n record: GristRecord,\n rowIndex: number\n) => { emphasized?: boolean | string | string[]; [key: string]: any } | void\n\n/**\n * Configuration options for defining group headers in a Grist table.\n *\n * @typedef {Object} GroupConfig\n *\n * @property {string} align - The alignment of the group header ('left', 'center', or 'right').\n * @property {ColumnConfig} [titleColumn] - The column configuration used as the title of the group (optional).\n * @property {string} title - The title of the group.\n * @property {string} [value] - A value associated with the group (optional).\n * @property {string} [groupName] - The name of the group (optional).\n * @property {number} [row] - The row index where the group header should appear (optional).\n * @property {string|number} column - The column name or index where the group header should appear.\n * @property {number} rowspan - The number of rows the group header should span.\n * @property {number} [colspan] - The number of columns the group header should span (optional).\n */\nexport type GroupConfig = {\n align: string\n titleColumn?: ColumnConfig\n title: string\n value?: string\n groupName?: string\n row?: number\n column: string | number\n rowspan: number\n colspan?: number\n}\n\n/**\n * Configuration options for specifying row selectability in a Grist table.\n *\n * @typedef {Object} RowSelectableConfig\n *\n * @property {boolean} [multiple] - Indicates whether multiple rows can be selected (optional).\n */\nexport type RowSelectableConfig = {\n multiple?: boolean\n}\n\n/**\n * Represents a data record in Grist.\n *\n * @typedef {Object} GristRecord\n * @property {string} [id] - The unique identifier for the record.\n * @property {string} [name] - The name or label associated with the record.\n * @property {GristRecord[]} [children] - An array of child records, if applicable.\n * @property {number} [__seq__] - A numeric sequence identifier for the record.\n * @property {string} [__dirty__] - Indicates if the record is dirty (changed).\n * @property {boolean} [__selected__] - Indicates if the record is selected.\n * @property {object[]} [__changes__] - An array of changes made to the record.\n * @property {object} [__dirtyfields__] - An object representing dirty (changed) fields in the record.\n * @property {any} [__origin__] - The original data or source of the record.\n * @property {boolean} [__collapsed__] - Indicates if the record is collapsed (e.g., in a tree view).\n * @property {number} [__depth__] - The depth or level of the record in a hierarchical structure.\n * @property {'checked' | 'half-checked' | 'unchecked'} [__check_in_tree__] - Indicates the check state of the record in a tree view.\n * @property {GristRecord[]} [__children__] - An array of child records in a tree view.\n * @property {any} [key] - Additional custom properties specific to the record.\n */\nexport type GristRecord = {\n id?: string\n name?: string\n children?: GristRecord[]\n __seq__?: number\n __dirty__?: string\n __selected__?: boolean\n __changes__?: object[]\n __dirtyfields__?: { [key: string]: any }\n __origin__?: any\n __collapsed__?: boolean\n __depth__?: number\n __check_in_tree__?: 'checked' | 'half-checked' | 'unchecked'\n __children__?: GristRecord[]\n [key: string]: any\n}\n\n/**\n * Represents a set of data retrieved from a Grist table.\n *\n * @typedef {Object} GristData\n *\n * @property {number} [page] - The page number of the retrieved data (optional).\n * @property {number} [total] - The total number of records in the data source (optional).\n * @property {number} [limit] - The maximum number of records per page (optional).\n * @property {GristRecord[]} records - An array of fetched data records.\n */\nexport type GristData = {\n page?: number\n total?: number\n limit?: number\n records: GristRecord[]\n}\n\n/**\n * A function that determines whether a Grist record should be selected or not.\n *\n * @callback GristSelectFunction\n * @param {GristRecord} record - The Grist record to evaluate.\n * @returns {boolean} - `true` if the record should be selected, `false` otherwise.\n */\nexport type GristSelectFunction = (record: GristRecord) => boolean\n\n/**\n * Defines a type for personalized grid settings. It includes individual column settings and additional configurations specific to a user's grid view.\n *\n * @typedef {Object} PersonalGristPreference\n * @property {Partial<ColumnConfig>[]} [columns] - Partially defines the configuration for each column in the grid.\n * Each element can include only some properties of the `ColumnConfig`, used for optionally overriding column settings.\n * @property {any} [key] - Allows for storing additional user-defined properties with dynamic keys.\n * This property can include various custom settings beyond the grid configuration, and the keys can be freely defined.\n */\nexport type PersonalGristPreference = {\n columns?: Partial<ColumnConfig>[]\n filters?: FilterPreference[]\n pagination?: PaginationConfig\n sorters?: SortersConfig\n mode?: 'GRID' | 'LIST' | 'CARD'\n [key: string]: any\n}\n\nexport type FilterPreference = {\n name: string\n hidden?: boolean\n value?: any\n}\n\nexport enum ValidationReason {\n MANDATORY = 'MANDATORY',\n FORMAT = 'FORMAT',\n RANGE = 'RANGE',\n UNIQUE = 'UNIQUE'\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AA6LA;;;;;;;;GAQG;AACH,MAAM,CAAN,IAAY,kBAIX;AAJD,WAAY,kBAAkB;IAC5B,mCAAa,CAAA;IACb,yCAAmB,CAAA;IACnB,mCAAa,CAAA;AACf,CAAC,EAJW,kBAAkB,KAAlB,kBAAkB,QAI7B;AAioBD,MAAM,CAAN,IAAY,gBAKX;AALD,WAAY,gBAAgB;IAC1B,2CAAuB,CAAA;IACvB,qCAAiB,CAAA;IACjB,mCAAe,CAAA;IACf,qCAAiB,CAAA;AACnB,CAAC,EALW,gBAAgB,KAAhB,gBAAgB,QAK3B","sourcesContent":["import { TemplateResult } from 'lit-html'\n\nimport { DataCardField } from './data-card/data-card-field.js'\nimport { DataCardGutter } from './data-card/data-card-gutter.js'\nimport { RecordCard } from './data-card/record-card.js'\nimport { DataGridField } from './data-grid/data-grid-field.js'\nimport { DataListField } from './data-list/data-list-field.js'\nimport { DataListGutter } from './data-list/data-list-gutter.js'\nimport { RecordPartial } from './data-list/record-partial.js'\nimport { DataReportField } from './data-report/data-report-field.js'\nimport { OxGristEditor } from './editors/index.js'\nimport { QueryFilter, OxFiltersForm } from './filters/index.js'\nimport { OxGristRenderer } from './renderers/ox-grist-renderer.js'\n\n/**\n * Configuration object for defining the structure and behavior of a Grist table.\n *\n * @typedef {Object} GristConfig\n * @property {ColumnConfig[]} columns - An array of column configurations.\n * @property {RowsConfig} rows - Configuration for rows and their behavior.\n * @property {ListConfig} list - Configuration for the list view.\n * @property {TreeConfig} tree - Configuration for tree-related settings.\n * @property {PaginationConfig} [pagination] - Configuration for pagination settings (optional).\n * @property {SortersConfig} [sorters] - Configuration for sorting settings (optional).\n * @property {FiltersConfig} [filters] - Configuration for filtering settings (optional).\n */\nexport type GristConfig = {\n columns: ColumnConfig[]\n rows: RowsConfig\n list: ListConfig\n tree: TreeConfig\n pagination?: PaginationConfig\n sorters?: SortersConfig\n filters?: FiltersConfig\n /**\n * 아직 아무것도 없을 때 화면이 하는 말.\n *\n * 전에는 「NO RECORDS」가 컴포넌트에 박혀 있었다. 그 문장은 다음에 무엇을 할지 알려 주지 않는다 —\n * **비어 있는 화면이야말로** 「여기서 무엇을 만들 수 있고 그것이 무엇에 쓰이나」를 말해야 하는 자리이고,\n * 처음 오는 사람이 가장 먼저 보는 화면이기도 하다.\n *\n * 정하지 않으면 예전 문장이 그대로 나온다.\n */\n emptyNote?: { title?: string; description?: string; icon?: string }\n}\n\n/**\n * Configuration object for specifying sorting options.\n *\n * @typedef {Object} SorterConfig\n * @property {string} name - The name of the sorting option.\n * @property {boolean} [desc] - Optional. Indicates whether the sorting is in descending order (optional).\n */\nexport type SorterConfig = { name: string; desc?: boolean }\n\n/**\n * Configuration array for specifying multiple sorting options.\n *\n * @typedef {SorterConfig[]} SortersConfig\n */\nexport type SortersConfig = SorterConfig[]\n\n/**\n * Enumeration of filter operators used to define filter conditions.\n *\n * @typedef {'search' | 'eq' | 'between' | 'gte' | 'lte' | 'is_not_true' | 'in' | 'like' | 'i_like' | 'noteq' |\n * 'is_empty_num_id' | 'is_blank' | 'is_present' | 'is_not_false' | 'is_true' | 'is_false' | 'is_not_null' |\n * 'is_null' | 'notin_with_null' | 'notin' | 'gt' | 'lt' | 'i_nlike' | 'nlike'} FilterOperator\n */\nexport type FilterOperator =\n | 'search'\n | 'eq'\n | 'between'\n | 'gte'\n | 'lte'\n | 'is_not_true'\n | 'in'\n | 'like'\n | 'i_like'\n | 'noteq'\n | 'is_empty_num_id'\n | 'is_blank'\n | 'is_present'\n | 'is_not_false'\n | 'is_true'\n | 'is_false'\n | 'is_not_null'\n | 'is_null'\n | 'notin_with_null'\n | 'notin'\n | 'gt'\n | 'lt'\n | 'i_nlike'\n | 'nlike'\n\nexport type SelectOptionObject =\n | {\n name: string\n display?: never\n value: string\n }\n | {\n display: string\n name?: never\n value: string\n }\n\nexport type SelectOption = SelectOptionObject | string\n\nexport type FilterChangedCallback = (value: any, form: OxFiltersForm) => boolean\n\n/**\n * Configuration object for specifying filter conditions.\n *\n * @typedef {Object} FilterConfigObject\n * @property {string} type - The type of the filter condition.\n * @property {FilterOperator} [operator] - The filter operator used to compare values (optional).\n * @property {Object} [options] - Additional options or parameters for the filter condition (optional).\n * @property {boolean|undefined} [hidden] - The hidden flag for the filter condition (optional).\n * @property {string|number|boolean|string[]|number[]|undefined} [value] - The value to compare with in the filter condition (optional).\n * @property {string} [label] - The label to display for the filter condition (optional).\n */\nexport type FilterConfigObject = {\n type: string\n operator?: FilterOperator\n options?: { [key: string]: any }\n value?: string | number | boolean | string[] | number[] | undefined\n label?: string\n boundTo?: string[]\n hidden?: boolean\n onchange?: FilterChangedCallback\n}\n\n/**\n * Configuration option for specifying filter conditions.\n *\n * The `FilterConfig` type can take one of the following forms:\n * - A `FilterConfigObject` representing a specific filter condition.\n * - A `FilterOperator` representing a predefined filter operator.\n * - A boolean value to enable or disable filtering for a specific column.\n *\n * @typedef {Object|FilterOperator|boolean} FilterConfig\n */\nexport type FilterConfig = FilterConfigObject | FilterOperator | boolean\n\n/**\n * Represents a filter condition with the following properties:\n *\n * - `name`: The name of the column or field being filtered.\n * - `operator`: The filter operator used for comparison.\n * - `value`: The value or values to compare against.\n *\n * @typedef {Object} FilterValue\n * @property {string} name - The name of the column or field.\n * @property {FilterOperator} operator - The filter operator.\n * @property {string|number|boolean|string[]|number[]|undefined} value - The value(s) to compare.\n */\nexport type FilterValue = {\n name: string\n operator: FilterOperator\n value: string | number | boolean | string[] | number[] | undefined\n}\n\n/**\n * Configuration options for filters.\n *\n * @typedef {Object} FiltersConfig\n * @property {boolean} [header] - Specifies whether to provide filtering functionality in the header.\n */\nexport type FiltersConfig = {\n header?: boolean\n}\n\n/**\n * Configuration options for pagination.\n *\n * @typedef {Object} PaginationConfig\n * @property {number} [page] - The current page number.\n * @property {number} [limit] - The maximum number of records to display per page.\n * @property {number[]} [pages] - An array of available page numbers.\n * @property {boolean} [infinite] - Specifies whether to use infinite scrolling for pagination.\n */\nexport type PaginationConfig = {\n page?: number\n limit?: number\n pages?: number[]\n infinite?: boolean\n}\n\n/**\n * Option to specify the inheritance behavior for including data from parent domains.\n *\n * - 'None': Do not include data from the parent domain.\n * - 'Include': Include data from the parent domain along with current domain data.\n * - 'Only': Include data only from the parent domain, excluding current domain data.\n *\n * @enum {string}\n */\nexport enum InheritedValueType {\n None = 'None',\n Include = 'Include',\n Only = 'Only'\n}\n\n/**\n * Configuration options for fetching data from a data source.\n *\n * @typedef {Object} FetchOption\n * @property {number} [page] - The page number to retrieve.\n * @property {number} [limit] - The maximum number of records to retrieve per page.\n * @property {SortersConfig} [sorters] - An array of sorting configurations for data. Only one of these options needs to be set.\n * @property {SortersConfig} [sortings] - An array of sorting configurations for data. Only one of these options needs to be set.\n * @property {QueryFilter[]} [filters] - An array of filtering criteria.\n * @property {InheritedValueType} [inherited] - Specifies the inheritance behavior for including data from parent domains.\n * @property {object} [options] - Additional options for customizing data fetching behavior.\n */\nexport type FetchOption = {\n page?: number\n limit?: number\n sorters?: SortersConfig\n sortings?: SortersConfig\n filters?: QueryFilter[]\n inherited?: InheritedValueType\n options?: object\n}\n\n/**\n * Represents the result of a data fetching operation.\n *\n * @typedef {Object} FetchResult\n * @property {number} [page] - The page number of the fetched data.\n * @property {number} [limit] - The maximum number of records per page.\n * @property {number} total - The total number of records in the data source.\n * @property {GristRecord[]} records - An array of fetched data records.\n */\nexport type FetchResult = {\n page?: number\n limit?: number\n total: number\n records: GristRecord[]\n} | void\n\n/**\n * A function that handles data fetching operations.\n *\n * @param param - The fetch options specifying how data should be retrieved.\n * @returns A promise that resolves to a `FetchResult`.\n */\nexport type FetchHandler = (param: FetchOption) => Promise<FetchResult>\n\n/**\n * Represents a Grist event handler function.\n *\n * @typedef {Function} GristEventHandler\n * @param {ColumnConfig[]} columns - An array of column configurations.\n * @param {GristData} [data] - The Grist data associated with the event.\n * @param {ColumnConfig} [column] - The column configuration related to the event (if applicable).\n * @param {GristRecord} [record] - The Grist record related to the event (if applicable).\n * @param {number} [rowIndex] - The index of the row related to the event (if applicable).\n * @param {any} [target] - The target element or object related to the event (if applicable).\n * @param {Event} [e] - The event object (if applicable).\n * @returns {void}\n */\nexport type GristEventHandler = (\n columns: ColumnConfig[],\n data?: GristData,\n column?: ColumnConfig,\n record?: GristRecord,\n rowIndex?: number,\n target?: any,\n e?: Event\n) => void\n\n/**\n * Represents an accumulator function for data aggregation and accumulation.\n *\n * @typedef {string|'sum'|'avg'|'count'|'min'|'max'|Function} AccumulatorFunc\n * @param {GristData} data - The Grist data for aggregation.\n * @param {ColumnConfig} column - The column configuration for aggregation.\n * @returns {string|number} - The aggregated result.\n */\nexport type AccumulatorFunc =\n | 'sum'\n | 'avg'\n | 'count'\n | 'min'\n | 'max'\n | ((data: GristData, column: ColumnConfig) => string | number)\n\n/**\n * Type representing an accumulator function or configuration object.\n *\n * An accumulator is used for data aggregation and accumulation purposes. It can be either\n * a predefined accumulator function (e.g., 'sum', 'avg', 'count', etc.) or a configuration\n * object specifying the type of accumulator and labeling options.\n *\n * @typedef {AccumulatorFunc|{type: AccumulatorFunc, tag?: boolean}} AccumulatorObject\n * @property {AccumulatorFunc} [type] - The type of accumulator function or predefined function name.\n * @property {boolean} [tag] - An option for adding a label to represent the result of an aggregation function. This label is added in front of the aggregated value.\n *\n * @example\n * // Using a predefined accumulator function:\n * const accumulator: AccumulatorObject = 'sum'; // Represents a 'sum' accumulator.\n *\n * // Using an accumulator configuration object:\n * const accumulatorConfig: AccumulatorObject = {\n * type: 'avg', // Specifies the type of accumulator ('avg' for average).\n * tag: true, // Option for adding a label to represent the result of an aggregation function. This label is added in front of the aggregated value.\n * };\n */\nexport type AccumulatorObject =\n | AccumulatorFunc\n | {\n type: AccumulatorFunc\n tag?: boolean\n }\n\n/**\n * Type representing column configuration information.\n *\n * @typedef {object} ColumnConfig\n * @property {string} type - Represents the data type of the column (e.g., 'text', 'number', 'date', 'gutter', etc.).\n * @property {string} name - Represents the name of the column.\n * @property {('button'|'sequence'|'row-selector'|'dirty')} [gutterName] - If the column type is 'gutter', specifies the name of one of the registered gutters (only set when the type is 'gutter').\n * @property {boolean} [fixed] - In case of a grid type column, specifies whether the column's position should be fixed and unaffected by horizontal scrolling (optional).\n * @property {HeaderConfig} header - Object representing the header configuration of the column.\n * @property {RecordConfig} record - Object representing the record configuration of the column.\n * @property {GristEventHandlerSet} handlers - Object representing the Grist event handler settings.\n * @property {LabelConfig} label - Object representing the label configuration of the column. This property is used to represent the label of the column's information in list mode, card mode, and when displaying the column in a filter form (ox-filters-form).\n * @property {boolean} [hidden] - Indicates whether the column is hidden or not (optional).\n * @property {boolean} [sortable] - Indicates whether the column is sortable or not (optional). If this setting is true, the header provides the ability to toggle the sorting direction.\n * @property {boolean} [resizable] - Indicates whether the column is resizable or not (optional). This setting determines whether the column's width can be adjusted.\n * @property {(number|string|ColumnWidthCallback)} [width] - Represents the width of the column (e.g., 100, '50%', or a callback function to calculate width).\n * @property {boolean} [forList] - Indicates whether the column is intended for display in list mode (optional, applicable only to gutter type columns).\n * @property {boolean} [forCard] - Indicates whether the column is intended for display in card mode (optional, applicable only to gutter type columns).\n * @property {ValidationCallback} [validation] - Callback function for column validation (optional). This function validates whether the value of the column is valid when edited.\n * @property {AccumulatorObject} [accumulator] - Object used for accumulating values of the column (optional, applicable only in grid mode). It specifies a function to automatically aggregate the values of the column for all records. The aggregated result is displayed in a row below the last record (accumulation record).\n * @property {FilterConfig} [filter] - Object representing the filter configuration of the column.\n * @property {(ImexConfig|boolean)} [imex] - Object or boolean value representing the configuration for importing and exporting column data.\n * @property {boolean} [multiple] - Indicates whether the column allows multiple selections or not (optional).\n * @property {boolean} [rowCount] - Indicates whether the column shows the row count or not (optional).\n */\nexport type ColumnConfig = {\n type: string\n name: string\n /**\n * \"gutter\" 타입의 컬럼인 경우, 등록된 gutter들 중의 하나의 이름을 명시합니다(type이 gutter인 경우에만 설정합니다).\n * (e.g., 'button', 'sequence', 'row-selector', 'dirty', etc)\n *\n * \"Gutter\" 는 테이블에서 각 행의 왼쪽이나 오른쪽에 놓이는 추가 열(column)을 뜻합니다.\n * 이 열은 주로 각 행(row)을 식별하거나 특정 작업을 수행하기 위한 버튼 또는 링크와 같은 요소를 표시하는 데 사용됩니다.\n * Gutter 열은 주로 데이터 행에 대한 조작 또는 행의 상태를 나타내는 데 사용됩니다.\n * 예를 들어, 테이블에서 각 행이 특정 작업을 수행하는 버튼을 포함할 때, 이러한 버튼이 Gutter 열에 배치될 수 있습니다.\n * 또한 Gutter 열은 행을 선택하거나 행의 상태를 나타내는 체크박스를 포함할 수도 있습니다.\n */\n gutterName?: 'button' | 'sequence' | 'row-selector' | 'dirty'\n title?: string\n fixed?: boolean\n /** [begin] only for button gutter column */\n icon?: string\n iconOnly?: boolean\n danger?: boolean\n /** [end] only for button gutter column */\n header: HeaderConfig\n record: RecordConfig\n handlers: GristEventHandlerSet\n label: LabelConfig\n hidden?: boolean\n sortable?: boolean\n resizable?: boolean\n width?: number | string | ColumnWidthCallback\n forList?: boolean\n forCard?: boolean\n validation?: ValidationCallback\n accumulator?: AccumulatorObject\n filter?: FilterConfig\n imex?: ImexConfig | boolean\n multiple?: boolean\n rowCount?: boolean\n /** 특정 도구에 의해서 사용되는 옵션으로, 일반적인 용도로 사용되지 않음을 표현할 수 있다. */\n unusable?: boolean\n multiline?: boolean // 멀티라인 여부\n}\n\n/**\n * Callback function type for column validation.\n *\n * This function is used to validate whether the value of a column is valid when edited.\n *\n * @callback ValidationCallback\n * @param {any} after - The new value of the column after editing.\n * @param {any} before - The previous value of the column before editing.\n * @param {GristRecord} record - The record being edited.\n * @param {ColumnConfig} column - The configuration of the column being edited.\n * @returns {boolean} - Returns `true` if the value is valid, `false` otherwise.\n */\nexport type ValidationCallback = (\n after: any,\n before: any,\n record: GristRecord,\n column: ColumnConfig\n) => boolean | Promise<boolean>\n\n/**\n * Configuration options for column labels.\n *\n * The label for a column can be customized using various options.\n *\n * @typedef {string|boolean|Object} LabelConfig\n * @property {LabelRenderer} [renderer] - A custom renderer function for the column label.\n *\n * @example\n * // Using a string as a label:\n * const label: LabelConfig = \"Column Label\"; // Sets the label as \"Column Label\".\n *\n * // Using a boolean to determine label visibility:\n * const label: LabelConfig = true; // Shows the default label based on the header configuration.\n *\n * // Using a custom renderer function for the label:\n * const label: LabelConfig = {\n * renderer: customLabelRenderer, // Specifies a custom renderer function.\n * };\n */\nexport type LabelConfig =\n | string\n | boolean\n | {\n renderer: LabelRenderer\n /** Same as HeaderConfig.text — the name itself, not what was drawn from it. */\n text?: string\n }\n\n/**\n * Custom label renderer function for generating column labels.\n *\n * @callback LabelRenderer\n * @param {ColumnConfig} column - The column configuration object for which the label is being generated.\n * @returns {void}\n */\nexport type LabelRenderer = (column: ColumnConfig) => void\n\n/**\n * Custom callback function for calculating column width.\n *\n * @callback ColumnWidthCallback\n * @param {ColumnConfig} column - The column configuration object for which the width is being calculated.\n * @returns {string} - The calculated width as a string (e.g., '100', '50%').\n */\nexport type ColumnWidthCallback = (column: ColumnConfig) => string\n\n/**\n * Configuration for defining the header of a column.\n *\n * @typedef {Object} HeaderConfig\n * @property {HeaderRenderer} renderer - The function used to render the column header.\n * @property {string} [style] - Optional CSS style for the column header.\n * @property {string} [group] - Optional group identifier for the column header.\n * @property {string} [groupStyle] - Optional CSS style for the header group.\n */\nexport type HeaderConfig = {\n renderer: HeaderRenderer\n /**\n * The column's name as plain text.\n *\n * `renderer` returns whatever gets drawn — a lit template, an icon — and the name cannot be read\n * back out of that. So a name given as a string in the config is kept here, for the places that\n * need it outside of rendering: the accessible name of a cell, and header filter inputs.\n */\n text?: string\n style?: string\n group?: string\n groupStyle?: string\n}\n\n/**\n * Function type for rendering column headers.\n *\n * @callback HeaderRenderer\n * @param {ColumnConfig} column - The column configuration object.\n * @returns {*} - The rendered header content.\n */\nexport type HeaderRenderer = (column: ColumnConfig) => any\n\n/**\n * Configuration for specifying default values for fields in a record.\n * Default values can be set using predefined value generator functions or custom user-defined functions.\n * This configuration allows you to define the default value generation method and any additional parameters.\n *\n * @typedef {Object|Function} DefaultValueFnConfig\n * @property {string} [name] - The name of the default value function to be used for the column.\n * @property {any[]} [params] - The parameters to be passed to the default value function.\n * @property {Function} [Function] - A custom user-defined function for generating default values.\n */\nexport type DefaultValueFnConfig =\n | {\n /**\n * The name of the default value function to be used for the column.\n */\n name: string\n\n /**\n * The parameters to be passed to the default value function.\n */\n params?: any[]\n }\n | Function\n\n/**\n * Configuration for defining fields within a record.\n * Each field can have various settings such as rendering, editing, alignment, etc.\n * The 'defaultValue' property is used to specify the default value generation method and parameters.\n *\n * @typedef {Object} RecordConfig\n * @property {FieldRenderer} renderer - The rendering function for the field.\n * @property {FieldEditor|undefined} [editor] - The editing function for the field (optional).\n * @property {boolean|Function} [editable] - Specifies if the field is editable. It can be a boolean or a function (optional).\n * @property {boolean} [mandatory] - Specifies if the field is mandatory (optional).\n * @property {GristClassifier} classifier - A classifier function for the field.\n * @property {'left'|'right'|'center'} [align] - The alignment of the field's content (optional).\n * @property {*} options - Additional options for the field.\n * @property {string|undefined} [rowOptionField] - The name of a field for row-level options associated with this field (optional).\n * @property {DefaultValueFnConfig|undefined} [defaultValue] - Configuration for specifying the default value for the field.\n * It can be a predefined value generator function or a custom user-defined function (optional).\n * @property {Object.<string, *>|undefined} [extended] - Additional extended properties that can be added as needed for specific field configurations (optional).\n * @property {boolean} [wide] - Specifies if the field should occupy a larger width within the viewer or editor.\n * When set to `true`, the field will attempt to span across multiple columns or occupy more space horizontally.\n */\nexport type RecordConfig = {\n renderer: FieldRenderer\n editor?: FieldEditor\n editable?: boolean | Function\n mandatory?: boolean\n disabled?: boolean | Function\n classifier: GristClassifier\n align?: 'left' | 'right' | 'center'\n options: any\n rowOptionField?: string\n defaultValue?: DefaultValueFnConfig\n wide?: boolean\n [extended: string]: any\n}\n\n/**\n * A function type used for rendering the content of a field in various components.\n *\n * @callback FieldRenderer\n * @param {*} value - The value of the field to be rendered.\n * @param {ColumnConfig} column - The configuration of the column to which the field belongs.\n * @param {GristRecord} record - The record object containing the field's data.\n * @param {number} rowIndex - The index of the row to which the field belongs.\n * @param {DataGridField|RecordCard|DataCardGutter|DataCardField|DataListGutter|DataListField|RecordPartial|DataReportField|Element} owner - The component or element that owns the field.\n * @returns {OxGristRenderer|TemplateResult|string|void} - The rendered content, which can be of different types depending on the implementation.\n */\nexport type FieldRenderer = (\n value: any,\n column: ColumnConfig,\n record: GristRecord,\n rowIndex: number,\n owner:\n | DataGridField\n | RecordCard\n | DataCardGutter\n | DataCardField\n | DataListGutter\n | DataListField\n | RecordPartial\n | DataReportField\n | Element\n) => OxGristRenderer | TemplateResult | string | void\n\n/**\n * A function type used for defining a custom editor for a field in a data grid.\n *\n * @callback FieldEditor\n * @param {*} value - The current value of the field.\n * @param {ColumnConfig} column - The configuration of the column to which the field belongs.\n * @param {GristRecord} record - The record object containing the field's data.\n * @param {number} rowIndex - The index of the row to which the field belongs.\n * @param {DataGridField} field - The data grid field that owns the editor.\n * @returns {OxGristEditor} - The custom editor component.\n */\nexport type FieldEditor = (\n value: any,\n column: ColumnConfig,\n record: GristRecord,\n rowIndex: number,\n field: DataGridField\n) => OxGristEditor\n\n/**\n * A function type used for rendering the filter select options in a filter form.\n *\n * @callback FilterSelectRenderer\n * @param {ColumnConfig} column - The configuration of the column for which the filter is being rendered.\n * @param {(string|number|boolean|string[]|number[]|any|undefined)} value - The current value of the filter.\n * @param {Element} owner - The DOM element that owns the filter select renderer.\n * @returns {(TemplateResult|string|void)} - The rendered filter select options as a Lit HTML template result, a string, or nothing.\n */\nexport type FilterSelectRenderer = (\n column: ColumnConfig,\n value: string | number | boolean | string[] | number[] | any | undefined,\n owner: Element\n) => TemplateResult | string | void\n\n/**\n * An object representing a set of event handler functions for various events in Grist.\n *\n * @typedef {Object} GristEventHandlerSet\n * @property {GristEventHandler} [click] - The click event handler function.\n * @property {GristEventHandler} [dblclick] - The double-click event handler function.\n * @property {GristEventHandler} [focus] - The focus event handler function.\n * @property {GristEventHandler} [contextmenu] - The context menu event handler function.\n */\nexport type GristEventHandlerSet = {\n click?: GristEventHandler\n dblclick?: GristEventHandler\n focus?: GristEventHandler\n contextmenu?: GristEventHandler\n}\n\n/**\n * Configuration options for defining how records are displayed in list mode in a Grist.\n *\n * @typedef {Object} ListConfig\n * @property {string} [thumbnail] - The name of the field to use as a thumbnail image for records in list mode.\n * @property {object} [emptyThumbnail] - What to draw where a record has no thumbnail. The slot keeps its\n * place either way so cards stay the same height; this says what it should say. Without it the slot is\n * drawn empty.\n * @property {string[]} fields - An array of field names to display in the list mode.\n * @property {string[]} details - An array of field names to display as additional details in the list mode.\n */\nexport type ListConfig = {\n thumbnail?: string\n emptyThumbnail?: {\n /** Material symbol name. */\n icon?: string\n /** One short line — already translated by the caller. */\n label?: string\n }\n fields: string[]\n details: string[]\n /**\n * When gutter buttons show on a card. 'always' (default) keeps them visible — right where the\n * button is the point of the card. 'hover' reveals them on hover or keyboard focus, for lists\n * where picking is the point and a destructive button should not sit on every card.\n */\n gutters?: 'always' | 'hover'\n}\n\n/**\n * Configuration options for managing tree-like structures in a Grist table.\n *\n * @typedef {Object} TreeConfig\n * @property {string} [childrenProperty] - The property name that specifies the children of a record.\n * @property {boolean | Function} [expanded] - Specifies whether tree nodes are initially expanded or a function to determine the initial expansion state for a given record.\n */\nexport type TreeConfig = {\n childrenProperty?: string\n expanded?: boolean | ((x: GristRecord) => boolean)\n}\n\n/**\n * Configuration options for importing and exporting column data.\n *\n * @typedef {Object} ImexConfig\n * @property {string} header - The header text for the column in import/export settings.\n * @property {string} key - The key or identifier for the column in import/export settings.\n * @property {number} width - The width of the column in import/export settings.\n * @property {string} type - The data type of the column in import/export settings.\n */\nexport type ImexConfig = {\n header: string\n key: string\n width: number\n type: string\n}\n\n/**\n * Configuration options for defining rows within a Grist table or view.\n *\n * @typedef {Object} RowsConfig\n * @property {boolean} [accumulator] - Specifies whether an accumulator row should be displayed.\n * @property {boolean} appendable - Indicates whether new rows can be appended to the table.\n * @property {boolean} insertable - Indicates whether new rows can be inserted into the table.\n * @property {RowSelectableConfig} [selectable] - Configuration for row selection options.\n * @property {GroupConfig[]} groups - An array of group configuration options.\n * @property {string[]} totals - An array of field names for which totals should be calculated and displayed.\n * @property {GristClassifier} classifier - A function that classifies rows based on their attributes.\n * @property {GristEventHandlerSet} handlers - An object containing event handler functions for various row-level events.\n */\nexport type RowsConfig = {\n accumulator?: boolean\n appendable: boolean\n editable: boolean\n insertable: boolean\n selectable?: RowSelectableConfig\n groups: GroupConfig[]\n totals: string[]\n classifier: GristClassifier\n handlers: GristEventHandlerSet\n}\n\n/**\n * A function used to classify and style individual records in a Grist table.\n *\n * @typedef {Function} GristClassifier\n *\n * @param {GristRecord} record - The record to be classified.\n * @param {number} rowIndex - The index of the row in the table.\n *\n * @returns {Object|void} An object containing classification properties or `void`.\n * - `emphasized` (optional): A boolean, string, or array of strings indicating the emphasis style.\n * - Additional properties can be added as needed for custom styling.\n */\nexport type GristClassifier = (\n record: GristRecord,\n rowIndex: number\n) => { emphasized?: boolean | string | string[]; [key: string]: any } | void\n\n/**\n * Configuration options for defining group headers in a Grist table.\n *\n * @typedef {Object} GroupConfig\n *\n * @property {string} align - The alignment of the group header ('left', 'center', or 'right').\n * @property {ColumnConfig} [titleColumn] - The column configuration used as the title of the group (optional).\n * @property {string} title - The title of the group.\n * @property {string} [value] - A value associated with the group (optional).\n * @property {string} [groupName] - The name of the group (optional).\n * @property {number} [row] - The row index where the group header should appear (optional).\n * @property {string|number} column - The column name or index where the group header should appear.\n * @property {number} rowspan - The number of rows the group header should span.\n * @property {number} [colspan] - The number of columns the group header should span (optional).\n */\nexport type GroupConfig = {\n align: string\n titleColumn?: ColumnConfig\n title: string\n value?: string\n groupName?: string\n row?: number\n column: string | number\n rowspan: number\n colspan?: number\n}\n\n/**\n * Configuration options for specifying row selectability in a Grist table.\n *\n * @typedef {Object} RowSelectableConfig\n *\n * @property {boolean} [multiple] - Indicates whether multiple rows can be selected (optional).\n */\nexport type RowSelectableConfig = {\n multiple?: boolean\n}\n\n/**\n * Represents a data record in Grist.\n *\n * @typedef {Object} GristRecord\n * @property {string} [id] - The unique identifier for the record.\n * @property {string} [name] - The name or label associated with the record.\n * @property {GristRecord[]} [children] - An array of child records, if applicable.\n * @property {number} [__seq__] - A numeric sequence identifier for the record.\n * @property {string} [__dirty__] - Indicates if the record is dirty (changed).\n * @property {boolean} [__selected__] - Indicates if the record is selected.\n * @property {object[]} [__changes__] - An array of changes made to the record.\n * @property {object} [__dirtyfields__] - An object representing dirty (changed) fields in the record.\n * @property {any} [__origin__] - The original data or source of the record.\n * @property {boolean} [__collapsed__] - Indicates if the record is collapsed (e.g., in a tree view).\n * @property {number} [__depth__] - The depth or level of the record in a hierarchical structure.\n * @property {'checked' | 'half-checked' | 'unchecked'} [__check_in_tree__] - Indicates the check state of the record in a tree view.\n * @property {GristRecord[]} [__children__] - An array of child records in a tree view.\n * @property {any} [key] - Additional custom properties specific to the record.\n */\nexport type GristRecord = {\n id?: string\n name?: string\n children?: GristRecord[]\n __seq__?: number\n __dirty__?: string\n __selected__?: boolean\n __changes__?: object[]\n __dirtyfields__?: { [key: string]: any }\n __origin__?: any\n __collapsed__?: boolean\n __depth__?: number\n __check_in_tree__?: 'checked' | 'half-checked' | 'unchecked'\n __children__?: GristRecord[]\n [key: string]: any\n}\n\n/**\n * Represents a set of data retrieved from a Grist table.\n *\n * @typedef {Object} GristData\n *\n * @property {number} [page] - The page number of the retrieved data (optional).\n * @property {number} [total] - The total number of records in the data source (optional).\n * @property {number} [limit] - The maximum number of records per page (optional).\n * @property {GristRecord[]} records - An array of fetched data records.\n */\nexport type GristData = {\n page?: number\n total?: number\n limit?: number\n records: GristRecord[]\n}\n\n/**\n * A function that determines whether a Grist record should be selected or not.\n *\n * @callback GristSelectFunction\n * @param {GristRecord} record - The Grist record to evaluate.\n * @returns {boolean} - `true` if the record should be selected, `false` otherwise.\n */\nexport type GristSelectFunction = (record: GristRecord) => boolean\n\n/**\n * Defines a type for personalized grid settings. It includes individual column settings and additional configurations specific to a user's grid view.\n *\n * @typedef {Object} PersonalGristPreference\n * @property {Partial<ColumnConfig>[]} [columns] - Partially defines the configuration for each column in the grid.\n * Each element can include only some properties of the `ColumnConfig`, used for optionally overriding column settings.\n * @property {any} [key] - Allows for storing additional user-defined properties with dynamic keys.\n * This property can include various custom settings beyond the grid configuration, and the keys can be freely defined.\n */\nexport type PersonalGristPreference = {\n columns?: Partial<ColumnConfig>[]\n filters?: FilterPreference[]\n pagination?: PaginationConfig\n sorters?: SortersConfig\n mode?: 'GRID' | 'LIST' | 'CARD'\n [key: string]: any\n}\n\nexport type FilterPreference = {\n name: string\n hidden?: boolean\n value?: any\n}\n\nexport enum ValidationReason {\n MANDATORY = 'MANDATORY',\n FORMAT = 'FORMAT',\n RANGE = 'RANGE',\n UNIQUE = 'UNIQUE'\n}\n"]}