@operato/data-grist 10.10.2 → 10.10.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/dist/src/data-card/data-card-gutter.js +30 -0
- package/dist/src/data-card/data-card-gutter.js.map +1 -1
- package/dist/src/data-card/data-card.js +39 -9
- package/dist/src/data-card/data-card.js.map +1 -1
- package/dist/src/data-card/record-card.js +93 -15
- package/dist/src/data-card/record-card.js.map +1 -1
- package/dist/src/data-grist.d.ts +0 -3
- package/dist/src/data-grist.js +32 -26
- package/dist/src/data-grist.js.map +1 -1
- package/dist/src/data-list/data-list.js +21 -8
- package/dist/src/data-list/data-list.js.map +1 -1
- package/dist/src/types.d.ts +18 -0
- package/dist/src/types.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/yarn-error.log +16971 -0
- package/coverage/lcov-report/base.css +0 -224
- package/coverage/lcov-report/block-navigation.js +0 -87
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +0 -866
- package/coverage/lcov-report/prettify.css +0 -1
- package/coverage/lcov-report/prettify.js +0 -2
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +0 -210
- package/coverage/lcov-report/webpack/bootstrap.html +0 -118
- package/coverage/lcov-report/webpack/index.html +0 -116
- package/coverage/lcov-report/webpack/runtime/define property getters.html +0 -118
- package/coverage/lcov-report/webpack/runtime/global.html +0 -118
- package/coverage/lcov-report/webpack/runtime/hasOwnProperty shorthand.html +0 -118
- package/coverage/lcov-report/webpack/runtime/index.html +0 -161
- package/coverage/lcov-report/webpack/runtime/make namespace object.html +0 -118
- package/coverage/lcov.info +0 -53451
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,24 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
### [10.10.4](https://github.com/hatiolab/operato/compare/v10.10.3...v10.10.4) (2026-09-10)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### :bug: Bug Fix
|
|
10
|
+
|
|
11
|
+
* **data-grist:** stop reserving a band the list cannot use, and fix the scroll-to-top button ([9beae1d](https://github.com/hatiolab/operato/commit/9beae1db4fa17ff7d6a2b015b534c44b2a6735ad))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### [10.10.3](https://github.com/hatiolab/operato/compare/v10.10.2...v10.10.3) (2026-09-09)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### :bug: Bug Fix
|
|
19
|
+
|
|
20
|
+
* **data-grist:** make CARD mode usable for an image-first catalog ([c332d5a](https://github.com/hatiolab/operato/commit/c332d5a131abe991647047731708e43b3a69d9d1))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
6
24
|
### [10.10.2](https://github.com/hatiolab/operato/compare/v10.10.1...v10.10.2) (2026-09-07)
|
|
7
25
|
|
|
8
26
|
|
|
@@ -62,6 +62,36 @@ DataCardGutter.styles = [
|
|
|
62
62
|
padding: var(--spacing-small);
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
/*
|
|
66
|
+
── 아이콘만 있는 gutter 는 상자에 담지 않는다 ──────────────────────────
|
|
67
|
+
위의 별표 규칙은 글자 단추(iconOnly: false)를 위한 것이다. 그것이 아이콘 하나에도 걸리면
|
|
68
|
+
두 가지가 함께 잘못된다 — 아이콘이 label 글자 크기(0.8rem)로 줄어 13px 짜리 글리프가 되고,
|
|
69
|
+
그 작은 글리프가 색칠된 상자에 갇혀 답답해 보인다.
|
|
70
|
+
|
|
71
|
+
아이콘 단추는 아이콘 크기로 그리고, 바탕은 손이 갈 때만 든다.
|
|
72
|
+
*/
|
|
73
|
+
md-icon {
|
|
74
|
+
--md-icon-size: var(--data-card-gutter-icon-size, 20px);
|
|
75
|
+
font-size: var(--data-card-gutter-icon-size, 20px);
|
|
76
|
+
line-height: 1;
|
|
77
|
+
padding: var(--spacing-tiny);
|
|
78
|
+
color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));
|
|
79
|
+
background-color: transparent;
|
|
80
|
+
border-radius: var(--md-sys-shape-corner-small);
|
|
81
|
+
cursor: pointer;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
md-icon:hover {
|
|
85
|
+
color: var(--md-sys-color-on-secondary-container);
|
|
86
|
+
background-color: var(--md-sys-color-secondary-container);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/* 되돌릴 수 없는 것은 손이 갔을 때 색으로 말한다 — 늘 붉게 두면 목록이 시끄럽다 */
|
|
90
|
+
:host([danger]) md-icon:hover {
|
|
91
|
+
color: var(--md-sys-color-on-error, #fff);
|
|
92
|
+
background-color: var(--md-sys-color-error);
|
|
93
|
+
}
|
|
94
|
+
|
|
65
95
|
/* button hover 효과는 button 자체에만 — *:hover 로 광범위 적용 시 자식
|
|
66
96
|
* (span/md-icon) 의 individual hover 가 secondary background 작은 박스를
|
|
67
97
|
* 그려서 부자연. 자식들은 button hover 의 효과를 상속받기만 한다. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-card-gutter.js","sourceRoot":"","sources":["../../../src/data-card/data-card-gutter.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAkC,MAAM,KAAK,CAAA;AAC3E,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAE3D,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAA;AAI/D,IAAM,cAAc,GAApB,MAAM,cAAe,SAAQ,UAAU;IAAvC;;
|
|
1
|
+
{"version":3,"file":"data-card-gutter.js","sourceRoot":"","sources":["../../../src/data-card/data-card-gutter.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAkC,MAAM,KAAK,CAAA;AAC3E,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAE3D,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAA;AAI/D,IAAM,cAAc,GAApB,MAAM,cAAe,SAAQ,UAAU;IAAvC;;QA0GuB,WAAM,GAAgB,WAAW,CAAA;QACjC,WAAM,GAAiB,WAAW,CAAA;QAClC,aAAQ,GAAW,CAAC,CAAC,CAAA;IAgCnD,CAAC;IA7BC,MAAM;QACJ,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACjB,OAAO,IAAI,CAAA,EAAE,CAAA;QACf,CAAC;QAED,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAA;QAC9C,IAAI,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC,MAAM,CAAA;QAEhC,oEAAoE;QACpE,OAAO,IAAI,CAAA,IAAI,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAA;IACnE,CAAC;IAED,OAAO,CAAC,OAA6B;QACnC,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC1B,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,CAAA;YAE1C,IAAI,UAAU,IAAI,cAAc,EAAE,CAAC;gBACjC,IAAI,CAAC,YAAY,CAAC,cAAc,EAAE,EAAE,CAAC,CAAA;YACvC,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,eAAe,CAAC,cAAc,CAAC,CAAA;YACtC,CAAC;YAED,IAAI,MAAM,EAAE,CAAC;gBACX,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAA;YACjC,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAA;YAChC,CAAC;QACH,CAAC;IACH,CAAC;;AA1IM,qBAAM,GAAG;IACd,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAoGF;CACF,AAtGY,CAsGZ;AAE8B;IAA9B,QAAQ,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;6CAAe;AACjB;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;8CAAkC;AACjC;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;8CAAmC;AAClC;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;gDAAsB;AACrB;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;6CAAe;AA7G/B,cAAc;IAD1B,aAAa,CAAC,gBAAgB,CAAC;GACnB,cAAc,CA4I1B","sourcesContent":["import { css, html, LitElement, PropertyValues, TemplateResult } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\n\nimport { ZERO_COLUMN, ZERO_RECORD } from '../configure/zero-config.js'\nimport { ColumnConfig, GristRecord } from '../types.js'\n\n@customElement('ox-card-gutter')\nexport class DataCardGutter extends LitElement {\n static styles = [\n css`\n :host {\n display: flex;\n background-color: transparent;\n align-content: center;\n align-items: center;\n text-overflow: ellipsis;\n }\n\n :host([row-selector]) {\n margin-right: auto;\n }\n\n * {\n border: 0;\n font-size: var(--md-sys-typescale-label-medium-size, 0.8rem);\n line-height: var(--md-sys-typescale-label-medium-size, 0.8rem);\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 padding: var(--spacing-small);\n }\n\n /*\n ── 아이콘만 있는 gutter 는 상자에 담지 않는다 ──────────────────────────\n 위의 별표 규칙은 글자 단추(iconOnly: false)를 위한 것이다. 그것이 아이콘 하나에도 걸리면\n 두 가지가 함께 잘못된다 — 아이콘이 label 글자 크기(0.8rem)로 줄어 13px 짜리 글리프가 되고,\n 그 작은 글리프가 색칠된 상자에 갇혀 답답해 보인다.\n\n 아이콘 단추는 아이콘 크기로 그리고, 바탕은 손이 갈 때만 든다.\n */\n md-icon {\n --md-icon-size: var(--data-card-gutter-icon-size, 20px);\n font-size: var(--data-card-gutter-icon-size, 20px);\n line-height: 1;\n padding: var(--spacing-tiny);\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n background-color: transparent;\n border-radius: var(--md-sys-shape-corner-small);\n cursor: pointer;\n }\n\n md-icon:hover {\n color: var(--md-sys-color-on-secondary-container);\n background-color: var(--md-sys-color-secondary-container);\n }\n\n /* 되돌릴 수 없는 것은 손이 갔을 때 색으로 말한다 — 늘 붉게 두면 목록이 시끄럽다 */\n :host([danger]) md-icon:hover {\n color: var(--md-sys-color-on-error, #fff);\n background-color: var(--md-sys-color-error);\n }\n\n /* button hover 효과는 button 자체에만 — *:hover 로 광범위 적용 시 자식\n * (span/md-icon) 의 individual hover 가 secondary background 작은 박스를\n * 그려서 부자연. 자식들은 button hover 의 효과를 상속받기만 한다. */\n button:hover {\n cursor: pointer;\n color: var(--md-sys-color-on-secondary);\n background-color: var(--md-sys-color-secondary);\n }\n\n :host([danger]) button {\n color: var(--md-sys-color-on-error);\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-error);\n background-color: var(--md-sys-color-on-secondary);\n border-color: var(--md-sys-color-on-secondary);\n }\n\n button {\n display: flex;\n gap: var(--spacing-small);\n border: 0;\n align-items: center;\n }\n\n /* 자식 (span / md-icon) 은 button 의 color 를 inherit — button hover / danger\n * 상태에서 color 가 변하면 자식 글자 / 아이콘 색도 함께 반전. 상위의 *{...}\n * 가 모든 자식에 color 박아서 inherit 안 되던 문제 보수. */\n button > * {\n color: inherit;\n }\n\n button md-icon {\n border: 0;\n padding: 0;\n background-color: unset;\n }\n\n button span {\n padding: 0;\n background-color: transparent;\n }\n `\n ]\n\n @property({ attribute: true }) align?: string\n @property({ type: Object }) record: GristRecord = ZERO_RECORD\n @property({ type: Object }) column: ColumnConfig = ZERO_COLUMN\n @property({ type: Number }) rowIndex: number = -1\n @property({ type: Object }) value?: object\n\n render(): TemplateResult {\n if (!this.column) {\n return html``\n }\n\n var { value, column, record, rowIndex } = this\n var { renderer } = column.record\n\n /* renderer가 html template이 아니고 단순한 값인 경우가 있으므로, html 템플릿으로 감싸준다. */\n return html` ${renderer(value, column, record, rowIndex, this)} `\n }\n\n updated(changes: PropertyValues<this>) {\n if (changes.has('column')) {\n const { gutterName, danger } = this.column\n\n if (gutterName == 'row-selector') {\n this.setAttribute('row-selector', '')\n } else {\n this.removeAttribute('row-selector')\n }\n\n if (danger) {\n this.setAttribute('danger', '')\n } else {\n this.removeAttribute('danger')\n }\n }\n }\n}\n"]}
|
|
@@ -93,7 +93,12 @@ DataCard.styles = [
|
|
|
93
93
|
padding: var(--spacing-medium);
|
|
94
94
|
|
|
95
95
|
display: grid;
|
|
96
|
-
|
|
96
|
+
/*
|
|
97
|
+
카드 한 장의 최소 폭. 화면마다 실리는 것이 다르므로 값을 밖에서 정할 수 있어야 한다 —
|
|
98
|
+
그림이 주인공인 목록(자산 카탈로그)은 좁게 여러 장을, 글이 많은 목록은 넓게 몇 장을 놓는다.
|
|
99
|
+
기본값은 지금까지의 값 그대로다.
|
|
100
|
+
*/
|
|
101
|
+
grid-template-columns: repeat(auto-fill, minmax(var(--data-card-min-width, 300px), 1fr));
|
|
97
102
|
grid-template-rows: min-content;
|
|
98
103
|
grid-gap: var(--spacing-large);
|
|
99
104
|
}
|
|
@@ -102,16 +107,41 @@ DataCard.styles = [
|
|
|
102
107
|
background-color: var(--grid-record-selected-background-color);
|
|
103
108
|
}
|
|
104
109
|
|
|
110
|
+
/*
|
|
111
|
+
── 맨 위로 가는 단추 ──────────────────────────────────────────────────
|
|
112
|
+
자리를 먹지 않는다(absolute). 기준 상자는 ox-grist 의 host 인데(그쪽이 position: relative),
|
|
113
|
+
그래서 **목록과 함께 스크롤되지 않고** 상자에 붙어 있다.
|
|
114
|
+
|
|
115
|
+
고친 것 둘.
|
|
116
|
+
|
|
117
|
+
1. 위가 아니라 **아래**다. 오른쪽 위는 머리말·검색·필터가 있는 자리여서 단추가 그것들과
|
|
118
|
+
겹쳤다. 맨 위로 보내는 단추는 사람이 아래에 있을 때만 나오므로 손이 있는 쪽에 둔다.
|
|
119
|
+
2. rgba(255,255,255,0.7) 이 박혀 있었다 — 어두운 테마에서 흰 알약이 떠 있고, 아이콘은
|
|
120
|
+
--icon-size-large 라 알약보다 커 보였다. 테마 토큰으로 그리고 크기를 정한다.
|
|
121
|
+
*/
|
|
105
122
|
#upward {
|
|
106
|
-
--md-icon-size:
|
|
123
|
+
--md-icon-size: 20px;
|
|
107
124
|
position: absolute;
|
|
108
|
-
|
|
109
|
-
right: var(--data-card-fab-position-horizontal);
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
125
|
+
bottom: var(--data-card-fab-position-vertical, var(--spacing-large));
|
|
126
|
+
right: var(--data-card-fab-position-horizontal, var(--spacing-large));
|
|
127
|
+
display: grid;
|
|
128
|
+
place-items: center;
|
|
129
|
+
width: 36px;
|
|
130
|
+
height: 36px;
|
|
131
|
+
box-sizing: border-box;
|
|
132
|
+
color: var(--data-card-fab-color, var(--md-sys-color-on-surface-variant));
|
|
133
|
+
background-color: var(--data-card-fab-background-color, var(--md-sys-color-surface-container-high));
|
|
134
|
+
/* 떠 있는 단추의 모서리는 알약이다 — 담는 면(6px)이 아니라 그 자신의 격을 따른다 */
|
|
135
|
+
border-radius: var(--md-sys-shape-corner-full, 50%);
|
|
136
|
+
border: 1px solid var(--md-sys-color-outline-variant);
|
|
137
|
+
box-shadow: var(--data-card-fab-shadow, 0 1px 3px rgba(0, 0, 0, 0.18));
|
|
138
|
+
cursor: pointer;
|
|
139
|
+
z-index: 9;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
#upward:hover {
|
|
143
|
+
color: var(--md-sys-color-on-secondary-container);
|
|
144
|
+
background-color: var(--md-sys-color-secondary-container);
|
|
115
145
|
}
|
|
116
146
|
|
|
117
147
|
slot {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-card.js","sourceRoot":"","sources":["../../../src/data-card/data-card.ts"],"names":[],"mappings":";AACA,OAAO,4BAA4B,CAAA;AACnC,OAAO,kBAAkB,CAAA;AACzB,OAAO,kBAAkB,CAAA;AAEzB,OAAO,EAAkB,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,KAAK,CAAA;AACxD,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAElE,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAIjD,IAAM,QAAQ,GAAd,MAAM,QAAS,SAAQ,eAAe;IAAtC;;
|
|
1
|
+
{"version":3,"file":"data-card.js","sourceRoot":"","sources":["../../../src/data-card/data-card.ts"],"names":[],"mappings":"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","sourcesContent":["import i18next from 'i18next'\nimport '@material/web/icon/icon.js'\nimport '../empty-note.js'\nimport './record-card.js'\n\nimport { PropertyValues, css, html, nothing } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { DataManipulator } from '../data-manipulator.js'\nimport { GristRecord } from '../types.js'\n\n@customElement('ox-card')\nexport class DataCard extends DataManipulator {\n static styles = [\n css`\n :host {\n overflow-y: auto;\n padding: var(--spacing-medium);\n\n display: grid;\n /*\n 카드 한 장의 최소 폭. 화면마다 실리는 것이 다르므로 값을 밖에서 정할 수 있어야 한다 —\n 그림이 주인공인 목록(자산 카탈로그)은 좁게 여러 장을, 글이 많은 목록은 넓게 몇 장을 놓는다.\n 기본값은 지금까지의 값 그대로다.\n */\n grid-template-columns: repeat(auto-fill, minmax(var(--data-card-min-width, 300px), 1fr));\n grid-template-rows: min-content;\n grid-gap: var(--spacing-large);\n }\n\n [selected-row] {\n background-color: var(--grid-record-selected-background-color);\n }\n\n /*\n ── 맨 위로 가는 단추 ──────────────────────────────────────────────────\n 자리를 먹지 않는다(absolute). 기준 상자는 ox-grist 의 host 인데(그쪽이 position: relative),\n 그래서 **목록과 함께 스크롤되지 않고** 상자에 붙어 있다.\n\n 고친 것 둘.\n\n 1. 위가 아니라 **아래**다. 오른쪽 위는 머리말·검색·필터가 있는 자리여서 단추가 그것들과\n 겹쳤다. 맨 위로 보내는 단추는 사람이 아래에 있을 때만 나오므로 손이 있는 쪽에 둔다.\n 2. rgba(255,255,255,0.7) 이 박혀 있었다 — 어두운 테마에서 흰 알약이 떠 있고, 아이콘은\n --icon-size-large 라 알약보다 커 보였다. 테마 토큰으로 그리고 크기를 정한다.\n */\n #upward {\n --md-icon-size: 20px;\n position: absolute;\n bottom: var(--data-card-fab-position-vertical, var(--spacing-large));\n right: var(--data-card-fab-position-horizontal, var(--spacing-large));\n display: grid;\n place-items: center;\n width: 36px;\n height: 36px;\n box-sizing: border-box;\n color: var(--data-card-fab-color, var(--md-sys-color-on-surface-variant));\n background-color: var(--data-card-fab-background-color, var(--md-sys-color-surface-container-high));\n /* 떠 있는 단추의 모서리는 알약이다 — 담는 면(6px)이 아니라 그 자신의 격을 따른다 */\n border-radius: var(--md-sys-shape-corner-full, 50%);\n border: 1px solid var(--md-sys-color-outline-variant);\n box-shadow: var(--data-card-fab-shadow, 0 1px 3px rgba(0, 0, 0, 0.18));\n cursor: pointer;\n z-index: 9;\n }\n\n #upward:hover {\n color: var(--md-sys-color-on-secondary-container);\n background-color: var(--md-sys-color-secondary-container);\n }\n\n slot {\n width: 100%;\n }\n\n ox-empty-note {\n display: block;\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n }\n\n [card][emphasized-row],\n [card][emphasized-row][focused] {\n background-color: var(--grid-record-emphasized-background-color);\n color: var(--grid-record-emphasized-color);\n }\n\n @media screen and (max-width: 460px) {\n #setting {\n grid-template-columns: 1fr;\n grid-template-rows: 1fr 1fr;\n }\n }\n `\n ]\n\n @property({ type: Boolean }) empty?: boolean\n\n /** 비었을 때 무슨 말을 하나 — 판단은 data-grist 가 하고 여기서는 그리기만 한다. */\n @property({ type: Object }) emptyNote?: { icon?: string; title: string; description?: string }\n\n @state() private _records: GristRecord[] = []\n @state() private isTop: boolean = false\n\n private _total: number = 0\n private _limit: number = 20\n private _page: number = 1\n\n firstUpdated(changes: PropertyValues<this>) {\n this.isTop = true\n\n /* infinite scrolling */\n this.addEventListener('scroll', e => {\n const totalScrollHeight = this.scrollHeight\n const screenHeight = this.offsetHeight\n const currentScrollTop = this.scrollTop\n\n if (totalScrollHeight <= screenHeight + currentScrollTop + 1) {\n /* 마지막 페이지까지 계속 페이지를 증가시킨다. */\n var lastPage = Math.ceil(this._total / this._limit)\n\n if (this._page < lastPage) {\n this.dispatchEvent(new CustomEvent('attach-page', { bubbles: true, composed: true }))\n }\n }\n\n this.isTop = this.scrollTop == 0\n })\n }\n\n updated(changes: PropertyValues<this>) {\n if (changes.has('config')) {\n this._records = []\n this._page = 1\n }\n\n if (changes.has('data')) {\n this._records = this.data.records\n this._total = this.data.total || 0\n this._limit = this.data.limit || 20\n this._page = this.data.page || 0\n }\n }\n\n render() {\n var { classifier } = this.config.rows\n var records = this._records || []\n\n if (this.config && this.config.rows.appendable) {\n records = [...records, { __dirty__: '+' }]\n }\n\n return html`\n ${records.map((record, rowIndex) => {\n var { emphasized } = classifier.call(null, record, rowIndex) || {}\n\n return html`\n <ox-record-card\n .config=${this.config}\n .data=${this.data}\n .record=${record}\n .rowIndex=${rowIndex}\n .emphasized=${emphasized}\n ?selected-row=${record['__selected__']}\n ?dirty=${record['__dirty__']}\n card\n ></ox-record-card>\n `\n })}\n ${this.empty\n ? html`<ox-empty-note\n icon=${this.emptyNote?.icon ?? nothing}\n title=${this.emptyNote?.title ?? ''}\n description=${this.emptyNote?.description ?? nothing}\n ></ox-empty-note>`\n : html``}\n ${this.isTop\n ? html``\n : html` <md-icon id=\"upward\" @click=${(e: Event) => this.gotoTop(e)}>arrow_upward</md-icon> `}\n `\n }\n\n gotoTop(e: Event) {\n this.scrollTop = 0\n\n e.stopPropagation()\n }\n\n get pullToRefreshTarget() {\n return this\n }\n}\n"]}
|
|
@@ -76,10 +76,11 @@ let RecordCard = class RecordCard extends LitElement {
|
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
render() {
|
|
79
|
+
var _a;
|
|
79
80
|
const record = this.record;
|
|
80
81
|
const rowIndex = this.rowIndex;
|
|
81
82
|
const { columns, list } = this.config;
|
|
82
|
-
const { thumbnail, fields, details } = list || {};
|
|
83
|
+
const { thumbnail, fields, details, emptyThumbnail } = list || {};
|
|
83
84
|
const briefFields = fields.map(field => columns.find(column => column.name == field)).filter(column => column) || [];
|
|
84
85
|
const detailFields = details.map(field => columns.find(column => column.name == field)).filter(column => column) || [];
|
|
85
86
|
const thumbnailColumn = thumbnail ? columns.find(column => column.name == thumbnail) : undefined;
|
|
@@ -91,9 +92,15 @@ let RecordCard = class RecordCard extends LitElement {
|
|
|
91
92
|
* 제목은 잘렸다. 없는 것의 자리를 비워 두는 것이다.
|
|
92
93
|
*/
|
|
93
94
|
const thumbnailValue = thumbnailColumn ? record === null || record === void 0 ? void 0 : record[thumbnailColumn.name] : undefined;
|
|
94
|
-
const hasThumbnail =
|
|
95
|
-
|
|
96
|
-
|
|
95
|
+
const hasThumbnail =
|
|
96
|
+
/*
|
|
97
|
+
* 자기가 그리겠다고 한 renderer 에게는 값이 비어도 자리를 준다. 그림이 없다는 말도, 그림
|
|
98
|
+
* 위에 얹는 것들(상태 표시 · 단추)도 renderer 안에 있고, 그 사정을 아는 것은 renderer 뿐이다.
|
|
99
|
+
*/
|
|
100
|
+
((_a = thumbnailColumn === null || thumbnailColumn === void 0 ? void 0 : thumbnailColumn.record) === null || _a === void 0 ? void 0 : _a.renderEmpty) ||
|
|
101
|
+
(Array.isArray(thumbnailValue)
|
|
102
|
+
? thumbnailValue.length > 0
|
|
103
|
+
: thumbnailValue !== null && thumbnailValue !== undefined && thumbnailValue !== '');
|
|
97
104
|
const gutters = (columns || []).filter(column => column.type == 'gutter' && column.forList);
|
|
98
105
|
if (this.hasAttribute('dirty')) {
|
|
99
106
|
var dirtyIcon;
|
|
@@ -111,14 +118,19 @@ let RecordCard = class RecordCard extends LitElement {
|
|
|
111
118
|
}
|
|
112
119
|
return html `
|
|
113
120
|
${dirtyIcon ? html ` <md-icon dirty>${dirtyIcon}</md-icon> ` : html ``}
|
|
114
|
-
${thumbnailColumn
|
|
115
|
-
?
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
121
|
+
${thumbnailColumn
|
|
122
|
+
? hasThumbnail
|
|
123
|
+
? html ` <ox-card-field
|
|
124
|
+
thumbnail
|
|
125
|
+
.rowIndex=${rowIndex}
|
|
126
|
+
.column=${thumbnailColumn}
|
|
127
|
+
.record=${record}
|
|
128
|
+
.value=${record[(thumbnailColumn === null || thumbnailColumn === void 0 ? void 0 : thumbnailColumn.name) || '']}
|
|
129
|
+
></ox-card-field>`
|
|
130
|
+
: html `<div thumbnail empty>
|
|
131
|
+
${(emptyThumbnail === null || emptyThumbnail === void 0 ? void 0 : emptyThumbnail.icon) ? html `<md-icon>${emptyThumbnail.icon}</md-icon>` : ''}
|
|
132
|
+
${(emptyThumbnail === null || emptyThumbnail === void 0 ? void 0 : emptyThumbnail.label) || ''}
|
|
133
|
+
</div>`
|
|
122
134
|
: html ``}
|
|
123
135
|
|
|
124
136
|
<div content>
|
|
@@ -191,7 +203,11 @@ RecordCard.styles = [
|
|
|
191
203
|
border-radius: var(--data-card-record-card-border-radius, var(--md-sys-shape-corner-medium));
|
|
192
204
|
border: var(--data-card-record-card-border);
|
|
193
205
|
height: min-content;
|
|
194
|
-
|
|
206
|
+
/*
|
|
207
|
+
그림이 카드 끝까지 닿아야 하는 목록은 이 여백을 0 으로 두고 그림 칸의 radius 로 위쪽
|
|
208
|
+
모서리를 맞춘다. 글이 실리는 카드는 지금까지의 값 그대로다.
|
|
209
|
+
*/
|
|
210
|
+
padding: var(--data-card-record-card-padding, var(--spacing-small) var(--spacing-none));
|
|
195
211
|
}
|
|
196
212
|
|
|
197
213
|
:host(:hover) {
|
|
@@ -230,8 +246,24 @@ RecordCard.styles = [
|
|
|
230
246
|
}
|
|
231
247
|
|
|
232
248
|
[thumbnail] {
|
|
233
|
-
|
|
234
|
-
|
|
249
|
+
/*
|
|
250
|
+
높이 대신 **비율**로 잡을 수 있다.
|
|
251
|
+
|
|
252
|
+
그림이 자산인 목록에서는 카드 폭이 달라져도 그림 칸의 비율이 같아야 나란히 읽힌다. 높이만
|
|
253
|
+
있으면 좁은 카드에서는 납작하고 넓은 카드에서는 홀쭉해진다. 둘 다 안 주면 지금까지처럼
|
|
254
|
+
내용 높이를 따른다.
|
|
255
|
+
*/
|
|
256
|
+
height: var(--data-card-thumbnail-height, auto);
|
|
257
|
+
aspect-ratio: var(--data-card-thumbnail-aspect, auto);
|
|
258
|
+
/*
|
|
259
|
+
그림 안쪽 여백과 바탕도 밖에서 정한다.
|
|
260
|
+
|
|
261
|
+
목록마다 그림이 하는 일이 다르다. 사람 얼굴(직원 목록)은 여백이 있어야 카드 안에 놓인
|
|
262
|
+
것처럼 보이고, 자산 카탈로그의 그림은 **그림이 주인공**이라 여백이 그림을 작게 만든다.
|
|
263
|
+
바탕도 마찬가지다 — 배경이 없는 그림(투명 png · 3D 미리보기)은 놓일 면이 있어야 한다.
|
|
264
|
+
*/
|
|
265
|
+
padding: var(--data-card-thumbnail-padding, var(--spacing-large));
|
|
266
|
+
background-color: var(--data-card-thumbnail-background, transparent);
|
|
235
267
|
box-sizing: border-box;
|
|
236
268
|
/* 동심 안쪽 radius — Material 3 shape token (외곽 = corner-medium, 안쪽 =
|
|
237
269
|
* corner-small). board-ui 카드와 동일 톤. */
|
|
@@ -239,6 +271,26 @@ RecordCard.styles = [
|
|
|
239
271
|
overflow: hidden;
|
|
240
272
|
}
|
|
241
273
|
|
|
274
|
+
/*
|
|
275
|
+
그림이 없는 것도 **자리는 지킨다.**
|
|
276
|
+
|
|
277
|
+
전에는 값이 없으면 썸네일 요소를 아예 그리지 않았다. 그러면 한 목록 안에서 카드 높이가
|
|
278
|
+
들쭉날쭉해져 눈이 줄을 못 잡고, 「아직 그림이 없다」는 사실을 말할 자리도 사라진다.
|
|
279
|
+
빈 자리는 조용한 바탕으로 두고, 무엇이라고 부를지는 쓰는 쪽이 정한다.
|
|
280
|
+
*/
|
|
281
|
+
[thumbnail][empty] {
|
|
282
|
+
display: flex;
|
|
283
|
+
align-items: center;
|
|
284
|
+
justify-content: center;
|
|
285
|
+
background-color: var(--data-card-thumbnail-empty-background, var(--md-sys-color-surface-container));
|
|
286
|
+
color: var(--data-card-thumbnail-empty-color, var(--md-sys-color-outline));
|
|
287
|
+
font: var(--data-card-item-etc-font);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
[thumbnail][empty] md-icon {
|
|
291
|
+
--md-icon-size: 28px;
|
|
292
|
+
}
|
|
293
|
+
|
|
242
294
|
[content] {
|
|
243
295
|
display: flex;
|
|
244
296
|
flex-direction: column;
|
|
@@ -253,6 +305,32 @@ RecordCard.styles = [
|
|
|
253
305
|
overflow: hidden;
|
|
254
306
|
}
|
|
255
307
|
|
|
308
|
+
/*
|
|
309
|
+
곁들이는 칸들 — 이 자리에 규칙이 아예 없었다.
|
|
310
|
+
|
|
311
|
+
details 는 「이름 : 값」이 여러 줄 서는 자리인데 그 줄들을 세우는 규칙이 없어서, 카드가
|
|
312
|
+
글자 크기에 따라 줄이 붙어 버리고 제목 묶음(brief)과도 구분되지 않았다. 세로로 세우고
|
|
313
|
+
간격을 준다 — 카드에서 「무엇인가」와 「어떤가」는 다른 묶음이다.
|
|
314
|
+
|
|
315
|
+
제목과는 옅은 선으로 가른다 — 카드에서 「무엇인가」와 「어떤가」는 다른 묶음이다. 값이
|
|
316
|
+
**전부** 빌 수 있는 목록이라면 선 아래에 아무것도 없는 카드가 생기므로, 그런 화면은
|
|
317
|
+
--data-card-detail-border-top: none 으로 끈다.
|
|
318
|
+
|
|
319
|
+
「이름 : 값」이 아니라 짧은 표식 몇 개(상태 · 점수)를 다는 목록에서는 세로로 세우는 것이
|
|
320
|
+
자리만 먹는다. 그때는 화면이 한 줄로 뉘일 수 있다(--data-card-detail-direction: row).
|
|
321
|
+
*/
|
|
322
|
+
[detail] {
|
|
323
|
+
display: flex;
|
|
324
|
+
flex-direction: var(--data-card-detail-direction, column);
|
|
325
|
+
flex-wrap: wrap;
|
|
326
|
+
align-items: baseline;
|
|
327
|
+
gap: var(--spacing-tiny) var(--spacing-medium);
|
|
328
|
+
margin-top: var(--spacing-small);
|
|
329
|
+
padding-top: var(--spacing-small);
|
|
330
|
+
border-top: var(--data-card-detail-border-top, 1px solid var(--md-sys-color-outline-variant));
|
|
331
|
+
overflow: hidden;
|
|
332
|
+
}
|
|
333
|
+
|
|
256
334
|
[gutters] {
|
|
257
335
|
display: flex;
|
|
258
336
|
flex-direction: row;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"record-card.js","sourceRoot":"","sources":["../../../src/data-card/record-card.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport '../record-view/record-view.js'\nimport './data-card-field.js'\nimport './data-card-gutter.js'\nimport './data-card-gutter-menu.js'\n\nimport { LitElement, PropertyValues, css, html } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\n\nimport { GristConfig, GristData, GristRecord } from '../types.js'\nimport { ZERO_CONFIG, ZERO_DATA, ZERO_RECORD } from '../configure/zero-config.js'\nimport { RecordViewHandler } from '../record-view/record-view-handler.js'\nimport { recordCardClickHandler } from './event-handlers/record-card-click-handler.js'\nimport { recordCardDblClickHandler } from './event-handlers/record-card-dblclick-handler.js'\n\n// TODO 로케일 설정에 따라서 포맷이 바뀌도록 해야한다.\nconst OPTIONS: Intl.DateTimeFormatOptions = {\n year: 'numeric',\n month: 'numeric',\n day: 'numeric',\n hour: 'numeric',\n minute: 'numeric',\n second: 'numeric',\n hour12: false\n // timeZone: 'America/Los_Angeles'\n}\n\nfunction getSafeFormatter(locale: string, options: Intl.DateTimeFormatOptions) {\n try {\n const safeLocale = locale || 'en-US'\n return new Intl.DateTimeFormat(safeLocale, options)\n } catch (e) {\n return new Intl.DateTimeFormat('en-US', options)\n }\n}\n\nconst formatter = getSafeFormatter(navigator.language, OPTIONS)\n\n@customElement('ox-record-card')\nexport class RecordCard extends LitElement {\n static styles = [\n css`\n :host {\n display: flex;\n flex-direction: column;\n align-items: stretch;\n position: relative;\n background-color: var(--data-card-record-card-background-color);\n /* Theme 정의가 누락된 application 에서도 합리적 radius 보장. */\n border-radius: var(--data-card-record-card-border-radius, var(--md-sys-shape-corner-medium));\n border: var(--data-card-record-card-border);\n height: min-content;\n padding: var(--spacing-small) var(--spacing-none);\n }\n\n :host(:hover) {\n background-color: var(--md-sys-color-surface);\n border: var(--data-card-record-card-border-hover);\n box-shadow: var(--data-card-record-card-boxshadow-hover);\n }\n\n :host([dirty])::before {\n content: '';\n position: absolute;\n left: var(--spacing-none);\n top: var(--spacing-none);\n\n width: var(--spacing-none);\n height: var(--spacing-none);\n border-top: var(--grid-record-dirty-border-top);\n border-right: var(--grid-record-dirty-border-left);\n }\n\n :host[emphasized-row] {\n background-color: var(--grid-record-emphasized-background-color);\n color: var(--grid-record-emphasized-color);\n }\n\n [dirty] {\n position: absolute;\n margin: var(--spacing-none);\n height: 20px;\n\n font: var(--grid-record-dirty-icon-font);\n text-indent: 1px;\n left: var(--spacing-none);\n top: var(--spacing-none);\n color: var(--grid-record-dirty-color, var(--md-sys-color-error));\n }\n\n [thumbnail] {\n height: var(--data-card-thumbnail-height);\n padding: var(--spacing-large);\n box-sizing: border-box;\n /* 동심 안쪽 radius — Material 3 shape token (외곽 = corner-medium, 안쪽 =\n * corner-small). board-ui 카드와 동일 톤. */\n border-radius: var(--data-card-thumbnail-radius, var(--md-sys-shape-corner-small));\n overflow: hidden;\n }\n\n [content] {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-tiny);\n margin: var(--data-card-item-margin);\n }\n\n [brief] {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-tiny);\n overflow: hidden;\n }\n\n [gutters] {\n display: flex;\n flex-direction: row;\n gap: var(--spacing-small);\n margin: var(--spacing-small) var(--spacing-medium);\n justify-content: end;\n align-items: stretch;\n }\n\n ox-card-field {\n font: var(--data-card-item-etc-font);\n color: var(--data-card-item-etc-color, var(--md-sys-color-on-surface));\n }\n\n ox-card-field[name] {\n font: var(--data-card-item-name-font);\n color: var(--data-card-item-name-color, var(--md-sys-color-secondary));\n }\n\n ox-card-field[desc] {\n font: var(--data-card-item-disc-font);\n color: var(--data-card-item-disc-color, var(--md-sys-color-tertiary));\n }\n `\n ]\n\n @property({ type: Object }) config: GristConfig = ZERO_CONFIG\n @property({ type: Object }) data: GristData = ZERO_DATA\n @property({ type: Object }) record: GristRecord = ZERO_RECORD\n @property({ type: Number }) rowIndex: number = -1\n /*\n * row-selector를 사용자가 변경할 때, record-card의 update를 유도하기 위해 selected-row attribute를 property에 추가함.\n * (이를 해주지 않으면, 리스트 refresh 경우에 selected-row checkbox가 클리어되지 않는 현상이 발생함.)\n */\n @property({ type: Boolean, attribute: 'selected-row' }) selectedRow: boolean = false\n @property({ attribute: false }) emphasized: any = false\n\n private _recordView: any\n private clickHandler = recordCardClickHandler.bind(this) as EventListener\n private dblclickHandler = recordCardDblClickHandler.bind(this) as EventListener\n\n firstUpdated() {\n /*\n long-press\n TODO. performance를 확인한 후에 활성화하자.\n */\n // longpressable(this.renderRoot.querySelector('[content]'))\n // this.renderRoot.addEventListener('long-press', recordPartialLongPressHandler.bind(this))\n this.renderRoot.addEventListener('click', this.clickHandler)\n this.renderRoot.addEventListener('dblclick', this.dblclickHandler)\n\n this.addEventListener('show-record-view', () => this.popupRecordView())\n }\n\n updated(changes: PropertyValues<this>) {\n if (changes.has('record') && this._recordView) {\n this._recordView.record = this.record\n }\n\n const 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 render() {\n const record = this.record\n const rowIndex = this.rowIndex\n const { columns, list } = this.config\n const { thumbnail, fields, details } = list || {}\n\n const briefFields = fields.map(field => columns.find(column => column.name == field)).filter(column => column) || []\n const detailFields =\n details.map(field => columns.find(column => column.name == field)).filter(column => column) || []\n\n const thumbnailColumn = thumbnail ? columns.find(column => column.name == thumbnail) : undefined\n /*\n * 값이 없으면 썸네일 자리를 만들지 않는다.\n *\n * 전에는 설정에 썸네일 컬럼 이름이 있으면 **레코드에 값이 있는지 보지 않고** 그렸다. 그래서 이미지를\n * 갖지 않는 목록(결재함 · 업무함)에서 카드마다 깨진 이미지 표시가 세로의 절반을 차지하고, 정작\n * 제목은 잘렸다. 없는 것의 자리를 비워 두는 것이다.\n */\n const thumbnailValue = thumbnailColumn ? record?.[thumbnailColumn.name] : undefined\n const hasThumbnail = Array.isArray(thumbnailValue)\n ? thumbnailValue.length > 0\n : thumbnailValue !== null && thumbnailValue !== undefined && thumbnailValue !== ''\n\n const gutters = (columns || []).filter(column => column.type == 'gutter' && column.forList)\n\n if (this.hasAttribute('dirty')) {\n var dirtyIcon\n\n switch (this.record['__dirty__']) {\n case 'M':\n dirtyIcon = 'done'\n break\n case '+':\n dirtyIcon = 'add'\n break\n case '-':\n dirtyIcon = 'remove'\n break\n }\n }\n\n return html`\n ${dirtyIcon ? html` <md-icon dirty>${dirtyIcon}</md-icon> ` : html``}\n ${thumbnailColumn && hasThumbnail\n ? html` <ox-card-field\n thumbnail\n .rowIndex=${rowIndex}\n .column=${thumbnailColumn}\n .record=${record}\n .value=${record[thumbnailColumn?.name || '']}\n ></ox-card-field>`\n : html``}\n\n <div content>\n <div brief>\n ${briefFields.map(\n (column, idx) => html`\n <ox-card-field\n .rowIndex=${rowIndex}\n .column=${column}\n .record=${record}\n .value=${record[column?.name || '']}\n ?name=${idx == 0}\n ?desc=${idx == 1}\n ></ox-card-field>\n `\n )}\n </div>\n ${detailFields.length > 0\n ? html`\n <div detail>\n ${detailFields.map(\n (column, idx) => html`\n <ox-card-field\n .rowIndex=${rowIndex}\n .column=${column}\n .record=${record}\n .value=${record[column?.name || '']}\n ></ox-card-field>\n `\n )}\n </div>\n `\n : html``}\n </div>\n\n <div gutters>\n ${gutters.map(gutter => {\n return html`\n <ox-card-gutter\n .rowIndex=${rowIndex}\n .column=${gutter}\n .record=${record}\n .value=${record[gutter.name]}\n ></ox-card-gutter>\n `\n })}\n </div>\n `\n }\n\n popupRecordView() {\n var titleField = this.config.list.fields[0] || 'name'\n var title = this.record[titleField]\n\n /* field가 오브젝트형인 경우에는 렌더러를 타이틀로 사용한다. */\n if (typeof title == 'object') {\n var column = this.config.columns.find(column => column.name == titleField)\n title = column?.record.renderer(title, column, this.record, this.rowIndex, this /* cautious */)\n }\n\n this._recordView = RecordViewHandler(\n this.config.columns,\n this.record,\n this.rowIndex,\n this,\n {\n title\n },\n () => {\n delete this._recordView\n }\n )\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"record-card.js","sourceRoot":"","sources":["../../../src/data-card/record-card.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport '../record-view/record-view.js'\nimport './data-card-field.js'\nimport './data-card-gutter.js'\nimport './data-card-gutter-menu.js'\n\nimport { LitElement, PropertyValues, css, html } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\n\nimport { GristConfig, GristData, GristRecord } from '../types.js'\nimport { ZERO_CONFIG, ZERO_DATA, ZERO_RECORD } from '../configure/zero-config.js'\nimport { RecordViewHandler } from '../record-view/record-view-handler.js'\nimport { recordCardClickHandler } from './event-handlers/record-card-click-handler.js'\nimport { recordCardDblClickHandler } from './event-handlers/record-card-dblclick-handler.js'\n\n// TODO 로케일 설정에 따라서 포맷이 바뀌도록 해야한다.\nconst OPTIONS: Intl.DateTimeFormatOptions = {\n year: 'numeric',\n month: 'numeric',\n day: 'numeric',\n hour: 'numeric',\n minute: 'numeric',\n second: 'numeric',\n hour12: false\n // timeZone: 'America/Los_Angeles'\n}\n\nfunction getSafeFormatter(locale: string, options: Intl.DateTimeFormatOptions) {\n try {\n const safeLocale = locale || 'en-US'\n return new Intl.DateTimeFormat(safeLocale, options)\n } catch (e) {\n return new Intl.DateTimeFormat('en-US', options)\n }\n}\n\nconst formatter = getSafeFormatter(navigator.language, OPTIONS)\n\n@customElement('ox-record-card')\nexport class RecordCard extends LitElement {\n static styles = [\n css`\n :host {\n display: flex;\n flex-direction: column;\n align-items: stretch;\n position: relative;\n background-color: var(--data-card-record-card-background-color);\n /* Theme 정의가 누락된 application 에서도 합리적 radius 보장. */\n border-radius: var(--data-card-record-card-border-radius, var(--md-sys-shape-corner-medium));\n border: var(--data-card-record-card-border);\n height: min-content;\n /*\n 그림이 카드 끝까지 닿아야 하는 목록은 이 여백을 0 으로 두고 그림 칸의 radius 로 위쪽\n 모서리를 맞춘다. 글이 실리는 카드는 지금까지의 값 그대로다.\n */\n padding: var(--data-card-record-card-padding, var(--spacing-small) var(--spacing-none));\n }\n\n :host(:hover) {\n background-color: var(--md-sys-color-surface);\n border: var(--data-card-record-card-border-hover);\n box-shadow: var(--data-card-record-card-boxshadow-hover);\n }\n\n :host([dirty])::before {\n content: '';\n position: absolute;\n left: var(--spacing-none);\n top: var(--spacing-none);\n\n width: var(--spacing-none);\n height: var(--spacing-none);\n border-top: var(--grid-record-dirty-border-top);\n border-right: var(--grid-record-dirty-border-left);\n }\n\n :host[emphasized-row] {\n background-color: var(--grid-record-emphasized-background-color);\n color: var(--grid-record-emphasized-color);\n }\n\n [dirty] {\n position: absolute;\n margin: var(--spacing-none);\n height: 20px;\n\n font: var(--grid-record-dirty-icon-font);\n text-indent: 1px;\n left: var(--spacing-none);\n top: var(--spacing-none);\n color: var(--grid-record-dirty-color, var(--md-sys-color-error));\n }\n\n [thumbnail] {\n /*\n 높이 대신 **비율**로 잡을 수 있다.\n\n 그림이 자산인 목록에서는 카드 폭이 달라져도 그림 칸의 비율이 같아야 나란히 읽힌다. 높이만\n 있으면 좁은 카드에서는 납작하고 넓은 카드에서는 홀쭉해진다. 둘 다 안 주면 지금까지처럼\n 내용 높이를 따른다.\n */\n height: var(--data-card-thumbnail-height, auto);\n aspect-ratio: var(--data-card-thumbnail-aspect, auto);\n /*\n 그림 안쪽 여백과 바탕도 밖에서 정한다.\n\n 목록마다 그림이 하는 일이 다르다. 사람 얼굴(직원 목록)은 여백이 있어야 카드 안에 놓인\n 것처럼 보이고, 자산 카탈로그의 그림은 **그림이 주인공**이라 여백이 그림을 작게 만든다.\n 바탕도 마찬가지다 — 배경이 없는 그림(투명 png · 3D 미리보기)은 놓일 면이 있어야 한다.\n */\n padding: var(--data-card-thumbnail-padding, var(--spacing-large));\n background-color: var(--data-card-thumbnail-background, transparent);\n box-sizing: border-box;\n /* 동심 안쪽 radius — Material 3 shape token (외곽 = corner-medium, 안쪽 =\n * corner-small). board-ui 카드와 동일 톤. */\n border-radius: var(--data-card-thumbnail-radius, var(--md-sys-shape-corner-small));\n overflow: hidden;\n }\n\n /*\n 그림이 없는 것도 **자리는 지킨다.**\n\n 전에는 값이 없으면 썸네일 요소를 아예 그리지 않았다. 그러면 한 목록 안에서 카드 높이가\n 들쭉날쭉해져 눈이 줄을 못 잡고, 「아직 그림이 없다」는 사실을 말할 자리도 사라진다.\n 빈 자리는 조용한 바탕으로 두고, 무엇이라고 부를지는 쓰는 쪽이 정한다.\n */\n [thumbnail][empty] {\n display: flex;\n align-items: center;\n justify-content: center;\n background-color: var(--data-card-thumbnail-empty-background, var(--md-sys-color-surface-container));\n color: var(--data-card-thumbnail-empty-color, var(--md-sys-color-outline));\n font: var(--data-card-item-etc-font);\n }\n\n [thumbnail][empty] md-icon {\n --md-icon-size: 28px;\n }\n\n [content] {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-tiny);\n margin: var(--data-card-item-margin);\n }\n\n [brief] {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-tiny);\n overflow: hidden;\n }\n\n /*\n 곁들이는 칸들 — 이 자리에 규칙이 아예 없었다.\n\n details 는 「이름 : 값」이 여러 줄 서는 자리인데 그 줄들을 세우는 규칙이 없어서, 카드가\n 글자 크기에 따라 줄이 붙어 버리고 제목 묶음(brief)과도 구분되지 않았다. 세로로 세우고\n 간격을 준다 — 카드에서 「무엇인가」와 「어떤가」는 다른 묶음이다.\n\n 제목과는 옅은 선으로 가른다 — 카드에서 「무엇인가」와 「어떤가」는 다른 묶음이다. 값이\n **전부** 빌 수 있는 목록이라면 선 아래에 아무것도 없는 카드가 생기므로, 그런 화면은\n --data-card-detail-border-top: none 으로 끈다.\n\n 「이름 : 값」이 아니라 짧은 표식 몇 개(상태 · 점수)를 다는 목록에서는 세로로 세우는 것이\n 자리만 먹는다. 그때는 화면이 한 줄로 뉘일 수 있다(--data-card-detail-direction: row).\n */\n [detail] {\n display: flex;\n flex-direction: var(--data-card-detail-direction, column);\n flex-wrap: wrap;\n align-items: baseline;\n gap: var(--spacing-tiny) var(--spacing-medium);\n margin-top: var(--spacing-small);\n padding-top: var(--spacing-small);\n border-top: var(--data-card-detail-border-top, 1px solid var(--md-sys-color-outline-variant));\n overflow: hidden;\n }\n\n [gutters] {\n display: flex;\n flex-direction: row;\n gap: var(--spacing-small);\n margin: var(--spacing-small) var(--spacing-medium);\n justify-content: end;\n align-items: stretch;\n }\n\n ox-card-field {\n font: var(--data-card-item-etc-font);\n color: var(--data-card-item-etc-color, var(--md-sys-color-on-surface));\n }\n\n ox-card-field[name] {\n font: var(--data-card-item-name-font);\n color: var(--data-card-item-name-color, var(--md-sys-color-secondary));\n }\n\n ox-card-field[desc] {\n font: var(--data-card-item-disc-font);\n color: var(--data-card-item-disc-color, var(--md-sys-color-tertiary));\n }\n `\n ]\n\n @property({ type: Object }) config: GristConfig = ZERO_CONFIG\n @property({ type: Object }) data: GristData = ZERO_DATA\n @property({ type: Object }) record: GristRecord = ZERO_RECORD\n @property({ type: Number }) rowIndex: number = -1\n /*\n * row-selector를 사용자가 변경할 때, record-card의 update를 유도하기 위해 selected-row attribute를 property에 추가함.\n * (이를 해주지 않으면, 리스트 refresh 경우에 selected-row checkbox가 클리어되지 않는 현상이 발생함.)\n */\n @property({ type: Boolean, attribute: 'selected-row' }) selectedRow: boolean = false\n @property({ attribute: false }) emphasized: any = false\n\n private _recordView: any\n private clickHandler = recordCardClickHandler.bind(this) as EventListener\n private dblclickHandler = recordCardDblClickHandler.bind(this) as EventListener\n\n firstUpdated() {\n /*\n long-press\n TODO. performance를 확인한 후에 활성화하자.\n */\n // longpressable(this.renderRoot.querySelector('[content]'))\n // this.renderRoot.addEventListener('long-press', recordPartialLongPressHandler.bind(this))\n this.renderRoot.addEventListener('click', this.clickHandler)\n this.renderRoot.addEventListener('dblclick', this.dblclickHandler)\n\n this.addEventListener('show-record-view', () => this.popupRecordView())\n }\n\n updated(changes: PropertyValues<this>) {\n if (changes.has('record') && this._recordView) {\n this._recordView.record = this.record\n }\n\n const 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 render() {\n const record = this.record\n const rowIndex = this.rowIndex\n const { columns, list } = this.config\n const { thumbnail, fields, details, emptyThumbnail } = list || {}\n\n const briefFields = fields.map(field => columns.find(column => column.name == field)).filter(column => column) || []\n const detailFields =\n details.map(field => columns.find(column => column.name == field)).filter(column => column) || []\n\n const thumbnailColumn = thumbnail ? columns.find(column => column.name == thumbnail) : undefined\n /*\n * 값이 없으면 썸네일 자리를 만들지 않는다.\n *\n * 전에는 설정에 썸네일 컬럼 이름이 있으면 **레코드에 값이 있는지 보지 않고** 그렸다. 그래서 이미지를\n * 갖지 않는 목록(결재함 · 업무함)에서 카드마다 깨진 이미지 표시가 세로의 절반을 차지하고, 정작\n * 제목은 잘렸다. 없는 것의 자리를 비워 두는 것이다.\n */\n const thumbnailValue = thumbnailColumn ? record?.[thumbnailColumn.name] : undefined\n const hasThumbnail =\n /*\n * 자기가 그리겠다고 한 renderer 에게는 값이 비어도 자리를 준다. 그림이 없다는 말도, 그림\n * 위에 얹는 것들(상태 표시 · 단추)도 renderer 안에 있고, 그 사정을 아는 것은 renderer 뿐이다.\n */\n thumbnailColumn?.record?.renderEmpty ||\n (Array.isArray(thumbnailValue)\n ? thumbnailValue.length > 0\n : thumbnailValue !== null && thumbnailValue !== undefined && thumbnailValue !== '')\n\n const gutters = (columns || []).filter(column => column.type == 'gutter' && column.forList)\n\n if (this.hasAttribute('dirty')) {\n var dirtyIcon\n\n switch (this.record['__dirty__']) {\n case 'M':\n dirtyIcon = 'done'\n break\n case '+':\n dirtyIcon = 'add'\n break\n case '-':\n dirtyIcon = 'remove'\n break\n }\n }\n\n return html`\n ${dirtyIcon ? html` <md-icon dirty>${dirtyIcon}</md-icon> ` : html``}\n ${thumbnailColumn\n ? hasThumbnail\n ? html` <ox-card-field\n thumbnail\n .rowIndex=${rowIndex}\n .column=${thumbnailColumn}\n .record=${record}\n .value=${record[thumbnailColumn?.name || '']}\n ></ox-card-field>`\n : html`<div thumbnail empty>\n ${emptyThumbnail?.icon ? html`<md-icon>${emptyThumbnail.icon}</md-icon>` : ''}\n ${emptyThumbnail?.label || ''}\n </div>`\n : html``}\n\n <div content>\n <div brief>\n ${briefFields.map(\n (column, idx) => html`\n <ox-card-field\n .rowIndex=${rowIndex}\n .column=${column}\n .record=${record}\n .value=${record[column?.name || '']}\n ?name=${idx == 0}\n ?desc=${idx == 1}\n ></ox-card-field>\n `\n )}\n </div>\n ${detailFields.length > 0\n ? html`\n <div detail>\n ${detailFields.map(\n (column, idx) => html`\n <ox-card-field\n .rowIndex=${rowIndex}\n .column=${column}\n .record=${record}\n .value=${record[column?.name || '']}\n ></ox-card-field>\n `\n )}\n </div>\n `\n : html``}\n </div>\n\n <div gutters>\n ${gutters.map(gutter => {\n return html`\n <ox-card-gutter\n .rowIndex=${rowIndex}\n .column=${gutter}\n .record=${record}\n .value=${record[gutter.name]}\n ></ox-card-gutter>\n `\n })}\n </div>\n `\n }\n\n popupRecordView() {\n var titleField = this.config.list.fields[0] || 'name'\n var title = this.record[titleField]\n\n /* field가 오브젝트형인 경우에는 렌더러를 타이틀로 사용한다. */\n if (typeof title == 'object') {\n var column = this.config.columns.find(column => column.name == titleField)\n title = column?.record.renderer(title, column, this.record, this.rowIndex, this /* cautious */)\n }\n\n this._recordView = RecordViewHandler(\n this.config.columns,\n this.record,\n this.rowIndex,\n this,\n {\n title\n },\n () => {\n delete this._recordView\n }\n )\n }\n}\n"]}
|
package/dist/src/data-grist.d.ts
CHANGED
|
@@ -114,10 +114,7 @@ export declare class DataGrist extends LitElement implements DataConsumer {
|
|
|
114
114
|
private dataProvider?;
|
|
115
115
|
private pulltorefreshHandle?;
|
|
116
116
|
private headroom?;
|
|
117
|
-
private orginPaddingTop?;
|
|
118
|
-
private originMarginTop?;
|
|
119
117
|
private headResizeObserver?;
|
|
120
|
-
private headroomAtTop;
|
|
121
118
|
private lastLocation;
|
|
122
119
|
private pendingFetch?;
|
|
123
120
|
private popstateEventHandler;
|
package/dist/src/data-grist.js
CHANGED
|
@@ -61,7 +61,6 @@ let DataGrist = class DataGrist extends LitElement {
|
|
|
61
61
|
* 둘을 같게 두면 확인하지 않은 사실을 화면이 단정하게 된다.
|
|
62
62
|
*/
|
|
63
63
|
this._fetched = false;
|
|
64
|
-
this.headroomAtTop = true;
|
|
65
64
|
this.lastLocation = {};
|
|
66
65
|
this.popstateEventHandler = ((e) => {
|
|
67
66
|
const { origin, pathname, search } = window.location;
|
|
@@ -281,36 +280,35 @@ let DataGrist = class DataGrist extends LitElement {
|
|
|
281
280
|
/* 머리가 덮고 있는 위쪽 padding. 머리의 기준 상자가 host 의 padding 박스이므로 이 값이다. */
|
|
282
281
|
const overlaid = parseFloat(getComputedStyle(this).paddingTop || '0') || 0;
|
|
283
282
|
const offset = Math.max(0, this.head.clientHeight - overlaid) + 'px';
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
283
|
+
/*
|
|
284
|
+
── 자리는 목록 **안쪽**에 만든다(padding), 목록을 밀어내지 않는다(margin) ──────────
|
|
285
|
+
전에는 맨 위에서 margin-top 으로 목록 상자 자체를 내려놓았다. 그러면 목록이 차지하는 상자가
|
|
286
|
+
머리 높이만큼 작아지고, **머리가 스크롤에 말려 올라가면 그 띠가 빈 채로 남는다.** 머리는
|
|
287
|
+
`headroom--unpinned` 에서 translateY(-100%) 로 사라지는데(HeadroomStyles), 목록은 여전히
|
|
288
|
+
62px 아래에서 시작해 흰 띠가 그대로 보였다.
|
|
289
|
+
|
|
290
|
+
padding 으로 두면 목록 상자는 늘 꽉 차 있고, 위쪽 62px 은 스크롤된 내용이 지나가는 자리가
|
|
291
|
+
된다. 맨 위에서는 그 자리를 머리가 덮으므로(불투명) 보이는 것은 전과 같다. 값이 늘 같아서
|
|
292
|
+
머리가 접히고 펴질 때 내용이 튀지 않는다 — 상태를 나눠 서로 다른 값을 쓰던 것이 원인이었다.
|
|
293
|
+
*/
|
|
294
|
+
this.grist.style.paddingTop = offset;
|
|
295
|
+
this.grist.style.removeProperty('margin-top');
|
|
290
296
|
}
|
|
291
297
|
setHeadroom() {
|
|
292
298
|
var _a, _b;
|
|
293
299
|
(_a = this.headroom) === null || _a === void 0 ? void 0 : _a.destroy();
|
|
294
300
|
if (this.grist && this.head && this.head.style.display !== 'none') {
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
this.applyHeadOffset();
|
|
305
|
-
},
|
|
306
|
-
onNotTop: () => {
|
|
307
|
-
this.grist.style.marginTop = this.originMarginTop;
|
|
308
|
-
this.orginPaddingTop = this.grist.style.paddingTop;
|
|
309
|
-
this.headroomAtTop = false;
|
|
310
|
-
this.applyHeadOffset();
|
|
311
|
-
}
|
|
312
|
-
});
|
|
301
|
+
/*
|
|
302
|
+
**맨 위인지 아닌지로 값을 갈라 두지 않는다.**
|
|
303
|
+
|
|
304
|
+
전에는 onTop·onNotTop 이 서로의 값을 저장했다 되돌리며 margin 과 padding 을 번갈아 썼다.
|
|
305
|
+
그 저장 변수들은 서로가 밀어 놓은 값을 다시 담아 믿을 수 없었고(2026-09-02 에 남은 주석이
|
|
306
|
+
그 사연이다), 상태에 따라 값이 달라지니 머리가 접힐 때 내용이 튀었다. 자리는 한 가지
|
|
307
|
+
방법으로만 만든다 — `applyHeadOffset` 하나가 소유한다.
|
|
308
|
+
*/
|
|
309
|
+
this.headroom = new Headroom(this.head, { scroller: this.grist });
|
|
313
310
|
this.headroom.init();
|
|
311
|
+
this.applyHeadOffset();
|
|
314
312
|
/* 머리가 커지거나 줄면 그때마다 다시 민다 — 한 번 잰 값으로 두면 내용이 겹친다. */
|
|
315
313
|
(_b = this.headResizeObserver) === null || _b === void 0 ? void 0 : _b.disconnect();
|
|
316
314
|
this.headResizeObserver = new ResizeObserver(() => this.applyHeadOffset());
|
|
@@ -1319,7 +1317,15 @@ DataGrist.styles = [
|
|
|
1319
1317
|
|
|
1320
1318
|
width: 100%;
|
|
1321
1319
|
box-sizing: border-box;
|
|
1322
|
-
|
|
1320
|
+
/*
|
|
1321
|
+
**머리 뒤는 막혀 있어야 한다.**
|
|
1322
|
+
|
|
1323
|
+
머리는 목록 위에 떠 있고 목록은 그 아래로 지나간다. --grist-background-color 는
|
|
1324
|
+
테마에서 **transparent 로 선언되어 있어**(그리스트가 페이지 바탕을 물려받게 하려고) 그것을
|
|
1325
|
+
그대로 쓰면 머리 글자와 목록 내용이 겹쳐 보인다. 값이 선언돼 있으니 var() 의 기본값도
|
|
1326
|
+
소용없다 — 다른 토큰으로 막고, 화면의 바탕이 surface 가 아니면 그 토큰으로 맞춘다.
|
|
1327
|
+
*/
|
|
1328
|
+
background-color: var(--grist-headroom-background-color, var(--md-sys-color-surface));
|
|
1323
1329
|
|
|
1324
1330
|
z-index: 8;
|
|
1325
1331
|
}
|