@operato/data-grist 8.0.0-alpha.8 → 8.0.0-beta.1

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 (218) hide show
  1. package/CHANGELOG.md +186 -0
  2. package/dist/src/data-grid/data-grid-body.js +21 -7
  3. package/dist/src/data-grid/data-grid-body.js.map +1 -1
  4. package/dist/src/data-grid/data-grid-footer.js +2 -0
  5. package/dist/src/data-grid/data-grid-footer.js.map +1 -1
  6. package/dist/src/data-grid/data-grid-header.d.ts +1 -1
  7. package/dist/src/data-grid/data-grid-header.js +13 -9
  8. package/dist/src/data-grid/data-grid-header.js.map +1 -1
  9. package/dist/src/editors/ox-grist-editor-varname.d.ts +6 -0
  10. package/dist/src/editors/ox-grist-editor-varname.js +36 -0
  11. package/dist/src/editors/ox-grist-editor-varname.js.map +1 -0
  12. package/dist/src/editors/ox-grist-editor.js +3 -3
  13. package/dist/src/editors/ox-grist-editor.js.map +1 -1
  14. package/dist/src/editors/registry.js +3 -1
  15. package/dist/src/editors/registry.js.map +1 -1
  16. package/dist/src/gutters/gutter-sequence.d.ts +1 -1
  17. package/dist/src/record-view/index.d.ts +1 -1
  18. package/dist/src/record-view/index.js +1 -1
  19. package/dist/src/record-view/index.js.map +1 -1
  20. package/dist/src/record-view/{record-creator.d.ts → ox-record-creator.d.ts} +8 -4
  21. package/dist/src/record-view/{record-creator.js → ox-record-creator.js} +90 -34
  22. package/dist/src/record-view/ox-record-creator.js.map +1 -0
  23. package/dist/src/renderers/ox-grist-renderer-boolean.js +1 -1
  24. package/dist/src/renderers/ox-grist-renderer-boolean.js.map +1 -1
  25. package/dist/stories/accumulator-format.stories.d.ts +1 -1
  26. package/dist/stories/accumulator-format.stories.js +1 -1
  27. package/dist/stories/accumulator-format.stories.js.map +1 -1
  28. package/dist/stories/click-event-custom.stories.d.ts +45 -0
  29. package/dist/stories/click-event-custom.stories.js +247 -0
  30. package/dist/stories/click-event-custom.stories.js.map +1 -0
  31. package/dist/stories/click-event.stories.d.ts +1 -1
  32. package/dist/stories/click-event.stories.js +1 -1
  33. package/dist/stories/click-event.stories.js.map +1 -1
  34. package/dist/stories/fixed-column.stories.d.ts +1 -1
  35. package/dist/stories/fixed-column.stories.js +1 -1
  36. package/dist/stories/fixed-column.stories.js.map +1 -1
  37. package/dist/stories/grid-setting.stories.d.ts +1 -1
  38. package/dist/stories/grid-setting.stories.js +1 -1
  39. package/dist/stories/grid-setting.stories.js.map +1 -1
  40. package/dist/stories/grist-modes.stories.d.ts +1 -1
  41. package/dist/stories/grist-modes.stories.js +1 -1
  42. package/dist/stories/grist-modes.stories.js.map +1 -1
  43. package/dist/stories/group-header.stories.d.ts +1 -1
  44. package/dist/stories/group-header.stories.js +1 -1
  45. package/dist/stories/group-header.stories.js.map +1 -1
  46. package/dist/stories/textarea.stories.d.ts +1 -1
  47. package/dist/stories/textarea.stories.js +1 -1
  48. package/dist/stories/textarea.stories.js.map +1 -1
  49. package/dist/stories/tree-column-with-checkbox.stories.d.ts +1 -1
  50. package/dist/stories/tree-column-with-checkbox.stories.js +1 -1
  51. package/dist/stories/tree-column-with-checkbox.stories.js.map +1 -1
  52. package/dist/stories/tree-column.stories.d.ts +1 -1
  53. package/dist/stories/tree-column.stories.js +1 -1
  54. package/dist/stories/tree-column.stories.js.map +1 -1
  55. package/dist/tsconfig.tsbuildinfo +1 -1
  56. package/package.json +20 -20
  57. package/themes/calendar-theme.css +3 -1
  58. package/.storybook/main.js +0 -3
  59. package/.storybook/preview.js +0 -52
  60. package/.storybook/server.mjs +0 -8
  61. package/dist/src/record-view/record-creator.js.map +0 -1
  62. package/src/accumulator/accumulator.ts +0 -63
  63. package/src/configure/column-builder.ts +0 -114
  64. package/src/configure/config-builder.ts +0 -40
  65. package/src/configure/filters-option-builder.ts +0 -8
  66. package/src/configure/imex-option-builder.ts +0 -5
  67. package/src/configure/list-option-builder.ts +0 -9
  68. package/src/configure/rows-option-builder.ts +0 -38
  69. package/src/configure/tree-option-builder.ts +0 -22
  70. package/src/configure/zero-config.ts +0 -83
  71. package/src/const.ts +0 -1
  72. package/src/data-card/data-card-field.ts +0 -94
  73. package/src/data-card/data-card-gutter-menu.ts +0 -94
  74. package/src/data-card/data-card-gutter.ts +0 -103
  75. package/src/data-card/data-card.ts +0 -154
  76. package/src/data-card/event-handlers/record-card-click-handler.ts +0 -34
  77. package/src/data-card/event-handlers/record-card-dblclick-handler.ts +0 -34
  78. package/src/data-card/record-card.ts +0 -298
  79. package/src/data-consumer.ts +0 -11
  80. package/src/data-grid/data-grid-accum-field.ts +0 -109
  81. package/src/data-grid/data-grid-body-style.ts +0 -85
  82. package/src/data-grid/data-grid-body.ts +0 -749
  83. package/src/data-grid/data-grid-field.ts +0 -227
  84. package/src/data-grid/data-grid-footer.ts +0 -117
  85. package/src/data-grid/data-grid-header.ts +0 -574
  86. package/src/data-grid/data-grid.ts +0 -293
  87. package/src/data-grid/event-handlers/data-grid-body-click-handler.ts +0 -69
  88. package/src/data-grid/event-handlers/data-grid-body-contextmenu-handler.ts +0 -32
  89. package/src/data-grid/event-handlers/data-grid-body-dblclick-handler.ts +0 -42
  90. package/src/data-grid/event-handlers/data-grid-body-focus-change-handler.ts +0 -24
  91. package/src/data-grid/event-handlers/data-grid-body-keydown-handler.ts +0 -234
  92. package/src/data-grist.ts +0 -1233
  93. package/src/data-list/data-list-field.ts +0 -82
  94. package/src/data-list/data-list-gutter.ts +0 -108
  95. package/src/data-list/data-list.ts +0 -145
  96. package/src/data-list/event-handlers/record-partial-click-handler.ts +0 -34
  97. package/src/data-list/event-handlers/record-partial-dblclick-handler.ts +0 -33
  98. package/src/data-list/event-handlers/record-partial-long-press-handler.ts +0 -33
  99. package/src/data-list/record-partial.ts +0 -264
  100. package/src/data-manipulator.ts +0 -426
  101. package/src/data-provider.ts +0 -271
  102. package/src/data-report/data-report-body-style.ts +0 -58
  103. package/src/data-report/data-report-body.ts +0 -189
  104. package/src/data-report/data-report-component.ts +0 -138
  105. package/src/data-report/data-report-field.ts +0 -83
  106. package/src/data-report/data-report-header.ts +0 -242
  107. package/src/data-report/event-handlers/data-report-body-click-handler.ts +0 -38
  108. package/src/data-report/event-handlers/data-report-body-dblclick-handler.ts +0 -25
  109. package/src/data-report/event-handlers/data-report-body-keydown-handler.ts +0 -68
  110. package/src/data-report.ts +0 -424
  111. package/src/editors/index.ts +0 -4
  112. package/src/editors/ox-grist-editor-checkbox.ts +0 -28
  113. package/src/editors/ox-grist-editor-color.ts +0 -10
  114. package/src/editors/ox-grist-editor-date.ts +0 -10
  115. package/src/editors/ox-grist-editor-datetime.ts +0 -27
  116. package/src/editors/ox-grist-editor-email.ts +0 -10
  117. package/src/editors/ox-grist-editor-file.ts +0 -28
  118. package/src/editors/ox-grist-editor-image.ts +0 -31
  119. package/src/editors/ox-grist-editor-month.ts +0 -10
  120. package/src/editors/ox-grist-editor-multiple-select.ts +0 -57
  121. package/src/editors/ox-grist-editor-number.ts +0 -27
  122. package/src/editors/ox-grist-editor-password.ts +0 -10
  123. package/src/editors/ox-grist-editor-select.ts +0 -55
  124. package/src/editors/ox-grist-editor-tel.ts +0 -10
  125. package/src/editors/ox-grist-editor-text.ts +0 -14
  126. package/src/editors/ox-grist-editor-textarea.ts +0 -16
  127. package/src/editors/ox-grist-editor-time.ts +0 -10
  128. package/src/editors/ox-grist-editor-tree.ts +0 -27
  129. package/src/editors/ox-grist-editor-week.ts +0 -10
  130. package/src/editors/ox-grist-editor.ts +0 -207
  131. package/src/editors/ox-input-tree.ts +0 -226
  132. package/src/editors/registry.ts +0 -80
  133. package/src/empty-note.ts +0 -46
  134. package/src/filters/filter-checkbox.ts +0 -49
  135. package/src/filters/filter-input-barcode.ts +0 -34
  136. package/src/filters/filter-input.ts +0 -30
  137. package/src/filters/filter-range-date.ts +0 -81
  138. package/src/filters/filter-range-number.ts +0 -64
  139. package/src/filters/filter-select-buttons.ts +0 -60
  140. package/src/filters/filter-select.ts +0 -68
  141. package/src/filters/filter-styles.ts +0 -119
  142. package/src/filters/filters-form.ts +0 -476
  143. package/src/filters/index.ts +0 -10
  144. package/src/filters/registry.ts +0 -56
  145. package/src/formatters/date-formatter.ts +0 -3
  146. package/src/formatters/index.ts +0 -1
  147. package/src/formatters/number-formatter.ts +0 -3
  148. package/src/formatters/registry.ts +0 -30
  149. package/src/formatters/text-formatter.ts +0 -3
  150. package/src/gutters/gutter-button.ts +0 -51
  151. package/src/gutters/gutter-dirty.ts +0 -96
  152. package/src/gutters/gutter-row-selector.ts +0 -89
  153. package/src/gutters/gutter-sequence.ts +0 -54
  154. package/src/gutters/index.ts +0 -1
  155. package/src/gutters/registry.ts +0 -32
  156. package/src/handlers/contextmenu-tree-mutation.ts +0 -80
  157. package/src/handlers/index.ts +0 -1
  158. package/src/handlers/move-down.ts +0 -44
  159. package/src/handlers/move-up.ts +0 -44
  160. package/src/handlers/record-copy.ts +0 -38
  161. package/src/handlers/record-delete.ts +0 -30
  162. package/src/handlers/record-view-handler.ts +0 -27
  163. package/src/handlers/registry.ts +0 -42
  164. package/src/handlers/select-row-toggle.ts +0 -30
  165. package/src/handlers/select-row.ts +0 -27
  166. package/src/index.ts +0 -17
  167. package/src/personalizer/index.ts +0 -1
  168. package/src/personalizer/ox-grist-filter-personalizer.ts +0 -192
  169. package/src/personalizer/ox-grist-personalizer.ts +0 -226
  170. package/src/record-view/event-handlers/record-view-body-click-handler.ts +0 -33
  171. package/src/record-view/event-handlers/record-view-body-keydown-handler.ts +0 -26
  172. package/src/record-view/index.ts +0 -2
  173. package/src/record-view/record-creator.ts +0 -226
  174. package/src/record-view/record-view-body.ts +0 -257
  175. package/src/record-view/record-view-handler.ts +0 -86
  176. package/src/record-view/record-view.ts +0 -122
  177. package/src/renderers/index.ts +0 -14
  178. package/src/renderers/ox-grist-renderer-boolean.ts +0 -43
  179. package/src/renderers/ox-grist-renderer-color.ts +0 -15
  180. package/src/renderers/ox-grist-renderer-date.ts +0 -62
  181. package/src/renderers/ox-grist-renderer-file.ts +0 -31
  182. package/src/renderers/ox-grist-renderer-image.ts +0 -27
  183. package/src/renderers/ox-grist-renderer-json5.ts +0 -36
  184. package/src/renderers/ox-grist-renderer-link.ts +0 -17
  185. package/src/renderers/ox-grist-renderer-password.ts +0 -7
  186. package/src/renderers/ox-grist-renderer-progress.ts +0 -45
  187. package/src/renderers/ox-grist-renderer-select.ts +0 -58
  188. package/src/renderers/ox-grist-renderer-text.ts +0 -16
  189. package/src/renderers/ox-grist-renderer-textarea.ts +0 -7
  190. package/src/renderers/ox-grist-renderer-tree.ts +0 -189
  191. package/src/renderers/ox-grist-renderer.ts +0 -35
  192. package/src/renderers/registry.ts +0 -111
  193. package/src/sorters/sorters-control.ts +0 -143
  194. package/src/types.ts +0 -813
  195. package/src/utils/index.ts +0 -2
  196. package/src/utils/list-param.ts +0 -72
  197. package/src/utils/supports-passive.ts +0 -13
  198. package/stories/accumulator-format.stories.ts +0 -276
  199. package/stories/barcode-input-filter.stories.ts +0 -216
  200. package/stories/bounded-select-filters.stories.ts +0 -333
  201. package/stories/bounded-select-record.stories.ts +0 -336
  202. package/stories/click-event.stories.ts +0 -283
  203. package/stories/creatable-only-column.stories.ts +0 -253
  204. package/stories/default-filters.stories.ts +0 -241
  205. package/stories/dynamic-editable.stories.ts +0 -313
  206. package/stories/empty-sorters.stories.ts +0 -180
  207. package/stories/explicit-fetch.stories.ts +0 -186
  208. package/stories/fixed-column.stories.ts +0 -416
  209. package/stories/grid-setting.stories.ts +0 -501
  210. package/stories/grist-modes.stories.ts +0 -451
  211. package/stories/group-header.stories.ts +0 -442
  212. package/stories/record-view.stories.ts +0 -143
  213. package/stories/textarea.stories.ts +0 -261
  214. package/stories/tree-column-with-checkbox.stories.ts +0 -297
  215. package/stories/tree-column.stories.ts +0 -296
  216. package/tsconfig.json +0 -26
  217. package/web-dev-server.config.mjs +0 -27
  218. package/web-test-runner.config.mjs +0 -45
@@ -1,749 +0,0 @@
1
- import './data-grid-field'
2
- import './data-grid-accum-field'
3
-
4
- import { css, html, LitElement, nothing, PropertyValues } from 'lit'
5
- import { customElement, property, query, state } from 'lit/decorators.js'
6
- import { ifDefined } from 'lit/directives/if-defined.js'
7
- import debounce from 'lodash-es/debounce'
8
-
9
- import { sleep, parseToNumberOrNull } from '@operato/utils'
10
-
11
- import { ZERO_CONFIG, ZERO_DATA } from '../configure/zero-config'
12
- import { RecordViewHandler } from '../record-view/record-view-handler'
13
- import { ColumnConfig, GristConfig, GristData, GristRecord } from '../types'
14
- import { supportsPassive } from '../utils'
15
- import { dataGridBodyStyle } from './data-grid-body-style'
16
- import { DataGridField } from './data-grid-field'
17
- import { dataGridBodyClickHandler } from './event-handlers/data-grid-body-click-handler'
18
- import { dataGridBodyDblclickHandler } from './event-handlers/data-grid-body-dblclick-handler'
19
- import { dataGridBodyFocusChangeHandler } from './event-handlers/data-grid-body-focus-change-handler'
20
- import { dataGridBodyContextMenuHandler } from './event-handlers/data-grid-body-contextmenu-handler'
21
- import { dataGridBodyKeydownHandler } from './event-handlers/data-grid-body-keydown-handler'
22
- import { accumulate } from '../accumulator/accumulator'
23
-
24
- const THRESHOLD = 300
25
- const DATA_PADDING = 3
26
- const ROW_HEIGHT = 40
27
- const GAP_SIZE = 1
28
-
29
- function calcScrollPos(parent: DataGridBody, child: Element) {
30
- /* getBoundingClientRect는 safari에서 스크롤 상태에서 다른 브라우저와는 다른 값을 리턴함 - 사파리는 약간 이상 작동함. */
31
- var { top: ct, left: cl, right: cr, bottom: cb } = child.getBoundingClientRect()
32
- var { top: pt, left: pl, right: pr, bottom: pb } = parent.getBoundingClientRect()
33
- var { scrollLeft, scrollTop } = parent
34
- var scrollbarWidth = parent.clientWidth - parent.offsetWidth
35
- var scrollbarHeight = parent.clientHeight - parent.offsetHeight
36
-
37
- return {
38
- left: cl < pl ? scrollLeft - (pl - cl) : cr > pr ? scrollLeft - (pr - cr) - scrollbarWidth : undefined,
39
- top: ct < pt ? scrollTop - (pt - ct) : cb > pb ? scrollTop - (pb - cb) - scrollbarHeight : undefined
40
- }
41
- }
42
-
43
- const ZERO_FOCUS = {
44
- row: 0,
45
- column: 0
46
- }
47
-
48
- @customElement('ox-grid-body')
49
- export class DataGridBody extends LitElement {
50
- debounce = debounce((scrollTop: number, clientHeight: number) => {
51
- const maxVisibleRows = Math.ceil(clientHeight / (ROW_HEIGHT + GAP_SIZE))
52
- const from = Math.max(0, Math.floor(scrollTop / (ROW_HEIGHT + GAP_SIZE)) - maxVisibleRows * DATA_PADDING)
53
- const to = Math.min(this.data.records?.length || 0, from + maxVisibleRows * (DATA_PADDING * 2 + 1))
54
-
55
- this.from = from
56
- this.to = to
57
- }, THRESHOLD)
58
-
59
- static styles = [
60
- dataGridBodyStyle,
61
- css`
62
- :host {
63
- font-variation-settings: 'FILL' 1;
64
- }
65
-
66
- [select-block] {
67
- position: absolute;
68
- left: var(--select-box-left);
69
- top: var(--select-box-top);
70
- width: var(--select-box-width);
71
- height: var(--select-box-height);
72
- border: var(--grid-record-focused-cell-border);
73
- background-image: var(--focused-background-image);
74
- pointer-events: none;
75
- z-index: 5;
76
- }
77
-
78
- [fixed] {
79
- position: sticky;
80
- background-color: var(--grid-record-background-color);
81
- z-index: 2; /* 고정된 열을 다른 열 위에 표시. */
82
- }
83
-
84
- :host([raised]) [fixed] {
85
- /* 고정 컬럼이 살짝 올라와 있는 느낌을 표현 */
86
- box-shadow: 3px 0 3px rgba(0, 0, 0, 0.1);
87
- }
88
-
89
- ox-grid-accum-field {
90
- position: sticky;
91
- bottom: 0;
92
- z-index: 1;
93
- }
94
-
95
- ox-grid-accum-field[fixed] {
96
- background-color: var(--md-sys-color-primary-container);
97
- }
98
- `
99
- ]
100
-
101
- @property({ type: Object }) config: GristConfig = ZERO_CONFIG
102
- @property({ type: Array }) columns: ColumnConfig[] = []
103
- @property({ type: Object }) data: GristData = ZERO_DATA
104
- @property({ type: Object }) focused: { row: number; column: number } = ZERO_FOCUS
105
- @property({ type: Object }) editTarget: { row: number; column: number; valueWith: string | null } | null = null
106
- @property({ type: Number }) from = -1
107
- @property({ type: Number }) to = -1
108
- @property({ type: Array }) fixedLefts: number[] = []
109
-
110
- @state() _selectBlock?: {
111
- start: DataGridField
112
- end?: DataGridField
113
- }
114
-
115
- @query('[select-block]') selectBlock?: HTMLDivElement
116
- @query('ox-grid-field[focused]') focusedField?: DataGridField
117
-
118
- private _focusedListener?: (e: KeyboardEvent) => void
119
- private _recordView?: any
120
- private _recordViewRow?: number
121
- private _draggable?: boolean
122
-
123
- resetEdit() {
124
- this.editTarget = null
125
- }
126
-
127
- handleOnScroll(e: WheelEvent) {
128
- const { scrollTop, clientHeight } = e.target as HTMLElement
129
- this.debounce(scrollTop, clientHeight)
130
- }
131
-
132
- // issue #13
133
- // renderOptimisticRow() {
134
- // return
135
- // }
136
-
137
- render() {
138
- // block이 선택되어 있으면, focused row/column은 표현하지 않는다.
139
- var { row: focusedRow, column: focusedColumn } = (!this._selectBlock && this.focused) || {}
140
- var { row: editingRow, column: editingColumn, valueWith = null } = this.editTarget || {}
141
-
142
- var columns = this.columns.filter(column => !column.hidden)
143
- var data = this.data
144
- var { records } = data
145
- var { appendable, classifier, accumulator } = this.config.rows
146
- const { start, end } = this._selectBlock || {}
147
-
148
- /*
149
- * 레코드를 추가할 수 있는 경우에는 항상 추가 레코드를 보여준다.
150
- * 만약, 이전 방식처럼, 커서를 옮겨야만 새로운 레코드가 보이게 하고 싶다면, 조건부를 다음의 코드로 대체한다.
151
- * -- if (focusedRow == records.length)
152
- */
153
- if (appendable) {
154
- records = [...records, { __dirty__: '+' }]
155
- }
156
-
157
- if (accumulator) {
158
- var accumRecord = this.buildAccumulatorRecord()
159
- }
160
-
161
- return html`
162
- ${records.map((record, idxRow) => {
163
- var attrFocusedRow = idxRow === focusedRow
164
- var attrSelected = record['__selected__']
165
- var attrOdd = idxRow % 2
166
- var dirtyFields = record['__dirtyfields__'] || {}
167
- var { emphasized } = classifier.call(null, record, idxRow) || {}
168
-
169
- return html`
170
- ${columns.map(
171
- (column, idxColumn) => html`
172
- <ox-grid-field
173
- .data=${data}
174
- .type=${column.type}
175
- .rowIndex=${idxRow}
176
- .columnIndex=${idxColumn}
177
- .column=${column}
178
- .record=${record}
179
- .checked=${record.__selected__ ? 'checked' : record.__check_in_tree__}
180
- .emphasized=${emphasized}
181
- ?gutter=${column.type == 'gutter'}
182
- ?odd=${attrOdd}
183
- ?focused-row=${attrFocusedRow}
184
- ?selected-row=${attrSelected}
185
- ?focused=${idxRow === focusedRow && idxColumn === focusedColumn}
186
- ?editing=${idxRow === editingRow && idxColumn === editingColumn}
187
- .valueWith=${valueWith}
188
- .value=${record[column.name]}
189
- ?dirty=${!!dirtyFields[column.name]}
190
- fixed=${ifDefined(this.fixedLefts[idxColumn])}
191
- ></ox-grid-field>
192
- `
193
- )}
194
- <ox-grid-field
195
- .data=${data}
196
- .rowIndex=${idxRow}
197
- .columnIndex=${-1}
198
- .record=${record}
199
- .emphasized=${emphasized}
200
- ?odd=${attrOdd}
201
- ?focused-row=${attrFocusedRow}
202
- ?selected-row=${attrSelected}
203
- ></ox-grid-field>
204
- `
205
- })}
206
- ${accumulator
207
- ? html`
208
- ${columns.map(
209
- (column, idxColumn) => html`
210
- <ox-grid-accum-field
211
- .data=${data}
212
- .columnIndex=${idxColumn}
213
- .rowIndex=${records.length}
214
- .column=${column}
215
- .record=${accumRecord!}
216
- .value=${accumRecord[column.name]}
217
- fixed=${ifDefined(this.fixedLefts[idxColumn])}
218
- ></ox-grid-accum-field>
219
- `
220
- )}
221
- <ox-grid-accum-field
222
- .data=${data}
223
- .columnIndex=${-1}
224
- .rowIndex=${records.length}
225
- .record=${accumRecord!}
226
- ></ox-grid-accum-field>
227
- `
228
- : nothing}
229
- ${start && end && start !== end ? html` <div select-block></div> ` : html``}
230
- <slot></slot>
231
- `
232
- }
233
-
234
- firstUpdated() {
235
- // TODO issue #13
236
- // this.addEventListener('scroll', this.handleOnScroll.bind(this))
237
-
238
- /* focus() 를 받을 수 있도록 함. */
239
- this.setAttribute('tabindex', '-1')
240
-
241
- /*
242
- * focusout 으로 property를 변경시키는 경우, focusout에 의해 update가 발생하는 경우에는,
243
- * 그리드 내부의 컴포넌트가 갱신되는 현상을 초래하게 된다.
244
- * 따라서, focusout 핸들러에서 update를 유발하는 코드는 강력하게 금지시킨다.
245
- */
246
- this.addEventListener('focusout', e => {
247
- if (this._focusedListener) {
248
- this.removeEventListener('keydown', this._focusedListener)
249
- delete this._focusedListener
250
- }
251
- })
252
-
253
- this.addEventListener('focusin', e => {
254
- if (!this._focusedListener) {
255
- this._focusedListener = dataGridBodyKeydownHandler.bind(this)
256
- this.addEventListener('keydown', this._focusedListener)
257
- }
258
- })
259
-
260
- this.addEventListener('set-select-block', async e => {
261
- e.stopPropagation()
262
-
263
- const { startRow = -1, startColumn = -1, endRow = -1, endColumn = -1 } = ((e as CustomEvent).detail as any) || {}
264
-
265
- const start = this.getFieldByIndex(startRow, startColumn) as DataGridField
266
- const end = this.getFieldByIndex(endRow, endColumn) as DataGridField
267
-
268
- this.setSelectBlock(start, end)
269
- })
270
-
271
- this.renderRoot.addEventListener('contextmenu', (event: Event) => {
272
- const e = event as MouseEvent
273
- this.setSelectBlock()
274
-
275
- this._draggable = false
276
-
277
- var target = (e.target as Element).closest('ox-grid-field') as DataGridField
278
- var { rowIndex, columnIndex } = target || {}
279
-
280
- this.dispatchEvent(
281
- new CustomEvent('focus-change', {
282
- bubbles: true,
283
- composed: true,
284
- detail: {
285
- row: rowIndex,
286
- column: columnIndex
287
- }
288
- })
289
- )
290
- })
291
-
292
- this.renderRoot.addEventListener('mousedown', (event: Event) => {
293
- const e = event as MouseEvent
294
- this.setSelectBlock()
295
-
296
- if (e.buttons !== 1) {
297
- return
298
- }
299
-
300
- this._draggable = true
301
-
302
- var target = (e.target as Element).closest('ox-grid-field') as DataGridField
303
- var { rowIndex, columnIndex } = target || {}
304
-
305
- this.dispatchEvent(
306
- new CustomEvent('focus-change', {
307
- bubbles: true,
308
- composed: true,
309
- detail: {
310
- row: rowIndex,
311
- column: columnIndex
312
- }
313
- })
314
- )
315
-
316
- if (columnIndex >= 0 && target.editableOnClick && !isNaN(rowIndex) && !isNaN(columnIndex)) {
317
- this.startEditTarget(rowIndex, columnIndex)
318
- }
319
- })
320
-
321
- this.renderRoot.addEventListener('mousemove', (event: Event) => {
322
- const e = event as MouseEvent
323
- if (e.buttons !== 1 || !this._draggable) {
324
- return
325
- }
326
-
327
- const field = e.target as DataGridField
328
- if (!this._selectBlock) {
329
- this.setSelectBlock(this.focusedField || field, this.focusedField || field)
330
-
331
- return
332
- }
333
-
334
- var { start, end } = this._selectBlock || {}
335
-
336
- if (start && end !== field) {
337
- end = field
338
-
339
- this.setSelectBlock(start, end)
340
- }
341
- })
342
-
343
- this.renderRoot.addEventListener('mouseup', (event: Event) => {
344
- this._draggable = false
345
- })
346
-
347
- this.renderRoot.addEventListener('click', dataGridBodyClickHandler.bind(this))
348
- this.renderRoot.addEventListener('dblclick', dataGridBodyDblclickHandler.bind(this))
349
- this.renderRoot.addEventListener('contextmenu', dataGridBodyContextMenuHandler.bind(this))
350
-
351
- this.addEventListener('focus-change', dataGridBodyFocusChangeHandler.bind(this))
352
-
353
- requestAnimationFrame(() => {
354
- const primaryColor = getComputedStyle(this).getPropertyValue('--md-sys-color-primary')
355
-
356
- this.style.setProperty(
357
- '--focused-background-image',
358
- `url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10'><rect fill='${primaryColor}' fill-opacity='0.2' x='0' y='0' width='100%' height='100%'/></svg>")`
359
- )
360
- })
361
-
362
- this.addEventListener('show-record-view', (e: Event) =>
363
- this.popupRecordView((e as CustomEvent).detail as { row: number; record: GristRecord })
364
- )
365
-
366
- this.addEventListener('scroll', function () {
367
- this.scrollLeft == 0 ? this.removeAttribute('raised') : this.setAttribute('raised', '')
368
- })
369
- }
370
-
371
- getFieldByIndex(rowIndex: number, columnIndex: number, residue: boolean = true) {
372
- if (rowIndex < 0) {
373
- return
374
- }
375
-
376
- var columns = this.columns.filter(column => !column.hidden).length
377
- if (!residue && columnIndex >= columns) {
378
- return
379
- }
380
-
381
- return this.renderRoot.children.item(
382
- rowIndex * (columns + 1) /* 1 means last dummy column */ + ((columnIndex + columns) % columns)
383
- ) as DataGridField
384
- }
385
-
386
- startEditTarget(row: number, column: number, valueWith: string | null = null) {
387
- var { editable } = this.columns.filter(column => !column.hidden)[column].record
388
- if (typeof editable === 'function') {
389
- const curRow = this.data.records[row] || {}
390
- const curCol = this.columns[column + 1]
391
- editable = editable.call(this, curRow[curCol.name], curCol, curRow, row, this)
392
- }
393
-
394
- if (!editable) {
395
- return
396
- }
397
-
398
- if (this.editTarget && this.editTarget.row == row && this.editTarget.column == column) {
399
- return
400
- }
401
-
402
- this.editTarget = {
403
- row,
404
- column,
405
- valueWith
406
- }
407
- }
408
-
409
- shouldUpdate(changes: any) {
410
- if (!changes.has('editTarget')) {
411
- /*
412
- * 큰 변화에 대해서는 실제 update가 발생되기 전에 editTarget을 초기화한다.
413
- */
414
- this.editTarget = null
415
- }
416
-
417
- return super.shouldUpdate(changes)
418
- }
419
-
420
- updated(changes: PropertyValues<this>) {
421
- if (changes.has('focused')) {
422
- let element = this.renderRoot?.querySelector('[focused]')
423
- if (!element) {
424
- return
425
- }
426
-
427
- let { top, left } = calcScrollPos(this, element)
428
- // TODO this.scroll()을 사용하면, 효과가 좋으나 left 계산에 문제가 있는 것 같음.
429
- // this.scroll({
430
- // top,
431
- // left,
432
- // behavior: 'smooth'
433
- // })
434
- if (top !== undefined) {
435
- this.scrollTop = top
436
- }
437
- if (left !== undefined) {
438
- this.scrollLeft = left
439
- }
440
- }
441
-
442
- if (this._recordView) {
443
- this._recordView.record = this.data.records[this._recordViewRow!]
444
- }
445
- }
446
-
447
- focus() {
448
- super.focus()
449
-
450
- if (this.focused === ZERO_FOCUS) {
451
- let { records } = this.data
452
- let row = records.findIndex(record => record['__selected__'])
453
-
454
- this.focused = { row: row == -1 ? 0 : row, column: 0 }
455
- }
456
- }
457
-
458
- popupRecordView({ record, row }: { row: number; record: GristRecord }) {
459
- var titleField = this.config.list.fields[0] || 'name'
460
- var title = record[titleField]
461
-
462
- /* field가 오브젝트형인 경우에는 렌더러를 타이틀로 사용한다. */
463
- if (typeof title == 'object') {
464
- var column = this.config.columns.find(column => column.name == titleField)
465
- title = column?.record.renderer(title, column, record, row, this /* cautious */)
466
- }
467
-
468
- this._recordViewRow = row
469
- this._recordView = RecordViewHandler(
470
- this.config.columns,
471
- record,
472
- row,
473
- this,
474
- {
475
- title
476
- },
477
- () => {
478
- delete this._recordView
479
- delete this._recordViewRow
480
- }
481
- )
482
- }
483
-
484
- getSelectedBlockValues(): Array<Array<any>> | any | undefined {
485
- var { start, end } = this._selectBlock || {}
486
-
487
- if (!(start && end)) {
488
- start = this.focusedField
489
-
490
- end = start
491
- }
492
-
493
- if (start && end) {
494
- const startRowIndex = start.rowIndex < end.rowIndex ? start.rowIndex : end.rowIndex
495
- const endRowIndex = start.rowIndex < end.rowIndex ? end.rowIndex : start.rowIndex
496
- const startColumnIndex = start.columnIndex < end.columnIndex ? start.columnIndex : end.columnIndex
497
- const endColumnIndex = start.columnIndex < end.columnIndex ? end.columnIndex : start.columnIndex
498
-
499
- const columnArray = new Array(endColumnIndex - startColumnIndex + 1).fill(startColumnIndex)
500
- const columns = this.columns.filter(column => !column.hidden)
501
-
502
- return (
503
- '<table>' +
504
- new Array(endRowIndex - startRowIndex + 1)
505
- .fill(startRowIndex)
506
- .map((start, index) => {
507
- const rowIndex = start + index
508
- const record = this.data.records[rowIndex]
509
-
510
- const tds = columnArray
511
- .map((start, index) => {
512
- const columnIndex = start + index
513
- const column = columns[columnIndex]
514
- const value = record?.[column.name]
515
- const type = typeof value
516
- const text =
517
- value === undefined || value === null ? '' : type == 'object' ? JSON.stringify(value) : value
518
-
519
- return `<td type=${type}>${text}</td>`
520
- })
521
- .join('')
522
- return `<tr>${tds}</tr>`
523
- })
524
- .join('') +
525
- '</table>'
526
- )
527
- }
528
- }
529
-
530
- async copy() {
531
- const copied = this.getSelectedBlockValues()
532
-
533
- await navigator.clipboard.write([
534
- new ClipboardItem({
535
- 'text/html': new Blob([copied], { type: 'text/html' }),
536
- 'text/plain': new Blob([copied], { type: 'text/plain' })
537
- })
538
- ])
539
-
540
- const selectBlock = this.selectBlock || this.focusedField
541
- if (selectBlock) {
542
- const backgroundColor = selectBlock.style.backgroundColor
543
- const opacity = selectBlock.style.opacity
544
-
545
- selectBlock.setAttribute('data-tooltip', 'copied to clipboard!')
546
- const rect = selectBlock.getBoundingClientRect()
547
- selectBlock.style.setProperty('--tooltip-top', `${rect.top}px`)
548
- selectBlock.style.setProperty('--tooltip-left', `${rect.left}px`)
549
-
550
- selectBlock.style.backgroundColor = 'red'
551
- selectBlock.style.opacity = '0.5'
552
- await sleep(500)
553
- selectBlock.removeAttribute('data-tooltip')
554
- selectBlock.style.backgroundColor = backgroundColor
555
- selectBlock.style.opacity = opacity
556
- }
557
- }
558
-
559
- async paste() {
560
- try {
561
- const selection = window.getSelection()
562
-
563
- const clipboardItems = await navigator.clipboard.read()
564
- if (!clipboardItems) {
565
- return
566
- }
567
-
568
- var type: string | undefined
569
- var content: string | undefined
570
-
571
- for (const clipboardItem of clipboardItems) {
572
- try {
573
- var blob = await clipboardItem.getType('text/html')
574
- content = blob && (await blob.text())
575
- type = 'text/html'
576
- } catch (e) {
577
- try {
578
- blob = await clipboardItem.getType('text/plain')
579
- content = blob && (await blob.text())
580
- type = 'text/plain'
581
- } catch (e) {}
582
- }
583
-
584
- break
585
- }
586
-
587
- if (!content) {
588
- return
589
- }
590
-
591
- const { row, column } = this.focused
592
- const { records } = this.data
593
- const columns = this.columns.filter(column => !column.hidden)
594
-
595
- if (type === 'text/html') {
596
- const div = document.createElement('div')
597
- div.innerHTML = content!.trim()
598
- const table = div.querySelector('table') as HTMLTableElement
599
- if (!table) {
600
- return
601
- }
602
-
603
- if (selection) {
604
- this.resetEdit()
605
- selection.removeAllRanges()
606
-
607
- // 포커스가 빠지기 전에 isWorking으로 focusout 이벤트에 대한 값 변경을 막음
608
- this.focusedField!.isWorking = true
609
- await this.updateComplete
610
- this.focusedField!.isWorking = false
611
- }
612
- const rows = table.querySelectorAll('tr')
613
-
614
- rows.forEach((record, rowIndex) => {
615
- if (!(record instanceof HTMLTableRowElement)) {
616
- return
617
- }
618
-
619
- var targetRecord = records[row + rowIndex] || { __dirty__: '+' }
620
- if (row + rowIndex >= records.length) {
621
- records.push(targetRecord)
622
- }
623
-
624
- const cells = record.querySelectorAll('td')
625
- cells.forEach((item, columnIndex) => {
626
- const targetColumn = columns[column + columnIndex]
627
- var value = item.textContent?.trim() as any
628
- let type = targetColumn.type || item.getAttribute('type') || 'string'
629
- type = type.includes('object') ? 'object' : type // 오브젝트 타입 예외처리
630
- let { editable } = targetColumn.record
631
- if (typeof editable === 'function') {
632
- editable = editable.call(this, value, targetColumn, targetRecord, row, this)
633
- }
634
-
635
- switch (type) {
636
- case 'object':
637
- case 'parameters':
638
- try {
639
- value = JSON.parse(value || 'null')
640
- } catch (err) {}
641
- break
642
- case 'boolean':
643
- case 'checkbox':
644
- value = !!value && !!String(value).match(/true/i)
645
- break
646
- case 'number':
647
- case 'float':
648
- case 'integer':
649
- case 'progress':
650
- value = parseToNumberOrNull(value)
651
- break
652
- default:
653
- value = value
654
- }
655
-
656
- if (targetColumn && !targetColumn.gutterName && editable) {
657
- this.dispatchEvent(
658
- new CustomEvent('field-change', {
659
- bubbles: true,
660
- composed: true,
661
- detail: {
662
- before: targetRecord[targetColumn.name],
663
- after: value,
664
- column: targetColumn,
665
- record: targetRecord,
666
- row: row + rowIndex
667
- }
668
- })
669
- )
670
- }
671
- })
672
- })
673
-
674
- return
675
- } else if (!selection && type === 'text/plain') {
676
- const targetRecord = records[row] || { __dirty__: '+' }
677
- const targetColumn = columns[column]
678
- let { editable } = targetColumn.record
679
- if (typeof editable === 'function') {
680
- editable = editable.call(this, content, targetColumn, targetRecord, row, this)
681
- }
682
-
683
- if (targetColumn && !targetColumn.gutterName && editable) {
684
- this.dispatchEvent(
685
- new CustomEvent('field-change', {
686
- bubbles: true,
687
- composed: true,
688
- detail: {
689
- before: targetRecord[targetColumn.name],
690
- after: content,
691
- column: targetColumn,
692
- record: targetRecord,
693
- row: row
694
- }
695
- })
696
- )
697
- }
698
- }
699
- } catch (e) {
700
- console.log('e : ', e)
701
- }
702
- }
703
-
704
- setSelectBlock(start?: DataGridField, end?: DataGridField) {
705
- if (start?.columnIndex == -1) {
706
- start = this.getFieldByIndex(start.rowIndex, this.columns.filter(column => !column.hidden).length - 1)
707
- }
708
-
709
- if (end?.columnIndex == -1) {
710
- end = this.getFieldByIndex(end.rowIndex, this.columns.filter(column => !column.hidden).length - 1)
711
- }
712
-
713
- this._selectBlock = start && { start, end }
714
-
715
- if (start && end) {
716
- window.getSelection()?.removeAllRanges()
717
-
718
- if (start !== end) {
719
- const left = start.columnIndex < end.columnIndex ? start : end
720
- const right = left === start ? end : start
721
- const top = start.rowIndex < end.rowIndex ? start : end
722
- const bottom = top === start ? end : start
723
-
724
- const { offsetLeft } = left
725
- const { offsetTop } = top
726
- const width = right.offsetLeft - offsetLeft + right.offsetWidth
727
- const height = bottom.offsetTop - offsetTop + bottom.offsetHeight
728
-
729
- this.style.setProperty('--select-box-left', offsetLeft - 1 + 'px')
730
- this.style.setProperty('--select-box-top', offsetTop - 1 + 'px')
731
- this.style.setProperty('--select-box-width', width + 'px')
732
- this.style.setProperty('--select-box-height', height + 'px')
733
- }
734
-
735
- this.focus()
736
- }
737
- }
738
-
739
- buildAccumulatorRecord(): GristRecord {
740
- var columns = this.columns.filter(column => !column.hidden)
741
-
742
- return columns.reduce((record, column) => {
743
- if (column.accumulator) {
744
- record[column.name] = accumulate(this.data, column, column.accumulator)
745
- }
746
- return record
747
- }, {} as GristRecord)
748
- }
749
- }