@operato/data-grist 0.3.9 → 0.3.16

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 (90) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/assets/images/no-image.png +0 -0
  3. package/custom-elements.json +747 -180
  4. package/demo/index.html +38 -62
  5. package/dist/src/configure/list-option-builder.js +0 -2
  6. package/dist/src/configure/list-option-builder.js.map +1 -1
  7. package/dist/src/configure/zero-config.d.ts +0 -1
  8. package/dist/src/configure/zero-config.js +0 -2
  9. package/dist/src/configure/zero-config.js.map +1 -1
  10. package/dist/src/data-card/data-card.d.ts +3 -6
  11. package/dist/src/data-card/data-card.js +3 -130
  12. package/dist/src/data-card/data-card.js.map +1 -1
  13. package/dist/src/data-card/record-card.d.ts +0 -1
  14. package/dist/src/data-card/record-card.js +16 -13
  15. package/dist/src/data-card/record-card.js.map +1 -1
  16. package/dist/src/data-grid/data-grid-body.d.ts +1 -1
  17. package/dist/src/data-grid/data-grid-body.js +5 -5
  18. package/dist/src/data-grid/data-grid-body.js.map +1 -1
  19. package/dist/src/data-grid/data-grid-field.d.ts +1 -1
  20. package/dist/src/data-grid/data-grid-field.js +4 -2
  21. package/dist/src/data-grid/data-grid-field.js.map +1 -1
  22. package/dist/src/data-grid/data-grid-header.d.ts +1 -0
  23. package/dist/src/data-grid/data-grid-header.js +9 -1
  24. package/dist/src/data-grid/data-grid-header.js.map +1 -1
  25. package/dist/src/data-grid/data-grid.d.ts +8 -5
  26. package/dist/src/data-grid/data-grid.js +7 -133
  27. package/dist/src/data-grid/data-grid.js.map +1 -1
  28. package/dist/src/data-grist.d.ts +1 -0
  29. package/dist/src/data-grist.js +4 -4
  30. package/dist/src/data-grist.js.map +1 -1
  31. package/dist/src/data-list/data-list.d.ts +3 -6
  32. package/dist/src/data-list/data-list.js +3 -130
  33. package/dist/src/data-list/data-list.js.map +1 -1
  34. package/dist/src/data-manipulator.d.ts +20 -0
  35. package/dist/src/data-manipulator.js +148 -0
  36. package/dist/src/data-manipulator.js.map +1 -0
  37. package/dist/src/editors/registry.d.ts +1 -1
  38. package/dist/src/editors/registry.js.map +1 -1
  39. package/dist/src/gutters/gutter-button.js +1 -0
  40. package/dist/src/gutters/gutter-button.js.map +1 -1
  41. package/dist/src/gutters/gutter-row-selector.js +1 -0
  42. package/dist/src/gutters/gutter-row-selector.js.map +1 -1
  43. package/dist/src/record-view/event-handlers/record-view-body-click-handler.d.ts +7 -0
  44. package/dist/src/record-view/event-handlers/record-view-body-click-handler.js +22 -0
  45. package/dist/src/record-view/event-handlers/record-view-body-click-handler.js.map +1 -0
  46. package/dist/src/record-view/event-handlers/record-view-body-keydown-handler.d.ts +7 -0
  47. package/dist/src/record-view/event-handlers/record-view-body-keydown-handler.js +96 -0
  48. package/dist/src/record-view/event-handlers/record-view-body-keydown-handler.js.map +1 -0
  49. package/dist/src/record-view/record-creator.d.ts +13 -0
  50. package/dist/src/record-view/record-creator.js +90 -0
  51. package/dist/src/record-view/record-creator.js.map +1 -0
  52. package/dist/src/record-view/record-view-body.d.ts +4 -5
  53. package/dist/src/record-view/record-view-body.js +19 -44
  54. package/dist/src/record-view/record-view-body.js.map +1 -1
  55. package/dist/src/record-view/record-view.d.ts +7 -2
  56. package/dist/src/record-view/record-view.js +114 -41
  57. package/dist/src/record-view/record-view.js.map +1 -1
  58. package/dist/src/renderers/image-renderer.js +12 -4
  59. package/dist/src/renderers/image-renderer.js.map +1 -1
  60. package/dist/src/sorters/sorters-control.d.ts +12 -0
  61. package/dist/src/sorters/sorters-control.js +106 -0
  62. package/dist/src/sorters/sorters-control.js.map +1 -0
  63. package/dist/src/types.d.ts +2 -3
  64. package/dist/src/types.js.map +1 -1
  65. package/dist/tsconfig.tsbuildinfo +1 -1
  66. package/package.json +10 -8
  67. package/src/configure/list-option-builder.ts +0 -2
  68. package/src/configure/zero-config.ts +0 -2
  69. package/src/data-card/data-card.ts +4 -156
  70. package/src/data-card/record-card.ts +17 -14
  71. package/src/data-grid/data-grid-body.ts +6 -6
  72. package/src/data-grid/data-grid-field.ts +5 -3
  73. package/src/data-grid/data-grid-header.ts +11 -1
  74. package/src/data-grid/data-grid.ts +19 -149
  75. package/src/data-grist.ts +5 -4
  76. package/src/data-list/data-list.ts +4 -156
  77. package/src/data-manipulator.ts +201 -0
  78. package/src/editors/registry.ts +2 -3
  79. package/src/gutters/gutter-button.ts +3 -2
  80. package/src/gutters/gutter-row-selector.ts +3 -2
  81. package/src/record-view/event-handlers/record-view-body-click-handler.ts +28 -0
  82. package/src/record-view/event-handlers/record-view-body-keydown-handler.ts +115 -0
  83. package/src/record-view/record-creator.ts +111 -0
  84. package/src/record-view/record-view-body.ts +21 -58
  85. package/src/record-view/record-view.ts +123 -48
  86. package/src/renderers/image-renderer.ts +14 -5
  87. package/src/sorters/sorters-control.ts +111 -0
  88. package/src/types.ts +10 -3
  89. package/themes/grist-theme.css +2 -22
  90. package/yarn-error.log +16718 -0
@@ -6,6 +6,7 @@ import { customElement, property, state } from 'lit/decorators.js'
6
6
  import throttle from 'lodash-es/throttle'
7
7
 
8
8
  import { OxPopup } from '@operato/popup'
9
+ import { closestElement } from '@operato/utils'
9
10
 
10
11
  import { ZERO_COLUMNS, ZERO_CONFIG, ZERO_DATA } from '../configure/zero-config'
11
12
  import { FilterStyles } from '../filters/filter-styles'
@@ -24,7 +25,7 @@ export class DataGridHeader extends LitElement {
24
25
  overflow: hidden;
25
26
  }
26
27
 
27
- :scope > div {
28
+ div[column] {
28
29
  display: flex;
29
30
 
30
31
  white-space: nowrap;
@@ -119,6 +120,15 @@ export class DataGridHeader extends LitElement {
119
120
  private _lastAccVal?: number
120
121
  private _throttledNotifier?: any
121
122
 
123
+ connectedCallback() {
124
+ super.connectedCallback()
125
+
126
+ const grid = closestElement('ox-grist', this)
127
+ grid?.addEventListener('sorters-change', (e: Event) => {
128
+ this._sorters = (e as CustomEvent).detail
129
+ })
130
+ }
131
+
122
132
  render() {
123
133
  var columns = this.columns || []
124
134
 
@@ -2,20 +2,21 @@ import './data-grid-header'
2
2
  import './data-grid-body'
3
3
  import './data-grid-footer'
4
4
 
5
- import { ColumnConfig, GristConfig, GristData, GristRecord, PaginationConfig } from '../types'
6
- import { LitElement, PropertyValues, css, html } from 'lit'
7
- import { ZERO_CONFIG, ZERO_DATA } from '../configure/zero-config'
5
+ import { css, html, LitElement, PropertyValues } from 'lit'
8
6
  import { customElement, property, query } from 'lit/decorators.js'
9
7
 
10
- import { DataGridHeader } from './data-grid-header'
11
8
  import { ScrollbarStyles } from '@operato/styles'
9
+
10
+ import { DataManipulator } from '../data-manipulator'
11
+ import { ColumnConfig, GristRecord, PaginationConfig } from '../types'
12
12
  import { supportsPassive } from '../utils'
13
+ import { DataGridHeader } from './data-grid-header'
13
14
 
14
15
  /**
15
16
  * DataGrid
16
17
  */
17
18
  @customElement('ox-grid')
18
- export class DataGrid extends LitElement {
19
+ export class DataGrid extends DataManipulator {
19
20
  static styles = [
20
21
  ScrollbarStyles,
21
22
  css`
@@ -40,8 +41,6 @@ export class DataGrid extends LitElement {
40
41
  `
41
42
  ]
42
43
 
43
- @property({ type: Object }) config: GristConfig = ZERO_CONFIG
44
- @property({ type: Object }) data: GristData = ZERO_DATA
45
44
  @property({ type: Object }) focused?: { row: number; column: number }
46
45
 
47
46
  @query('ox-grid-body', true) body!: LitElement
@@ -82,70 +81,11 @@ export class DataGrid extends LitElement {
82
81
  this.body.requestUpdate()
83
82
  })
84
83
 
85
- this.addEventListener('select-record-change', e => {
86
- var {
87
- records: selectedRecords,
88
- added = [],
89
- removed = []
90
- } = (e as CustomEvent).detail as {
91
- records: GristRecord[]
92
- added: GristRecord[]
93
- removed: GristRecord[]
94
- }
95
- var { records } = this.data
96
- var { selectable } = this.config.rows
97
-
98
- if (!records) {
99
- return
100
- }
101
-
102
- if (selectable && !selectable.multiple) {
103
- records.forEach(record => (record['__selected__'] = false))
104
- }
105
-
106
- if (selectedRecords) {
107
- records.forEach(record => (record['__selected__'] = false))
108
- selectedRecords.forEach(record => (record['__selected__'] = true))
109
- } else {
110
- removed.forEach(record => (record['__selected__'] = false))
111
- added.forEach(record => (record['__selected__'] = true))
112
- }
113
-
114
- this.header.requestUpdate()
115
- this.body.requestUpdate()
116
- })
117
-
118
- /* field change processing */
119
- this.addEventListener('field-change', e => {
120
- var { after, before, column, record, row } = (e as CustomEvent).detail
121
-
122
- /* compare changes */
123
- if (after === before) {
124
- return
125
- }
126
-
127
- var validation = column.validation
128
- if (validation && typeof (validation == 'function')) {
129
- if (!validation.call(this, after, before, record, column)) {
130
- return
131
- }
132
- }
133
-
134
- this.onRecordChanged({ [column.name]: after }, row, column)
135
- })
136
-
137
84
  this.addEventListener('focus-change', e => {
138
85
  this.focused = (e as CustomEvent).detail
139
86
  this.focus()
140
87
  this.requestUpdate()
141
88
  })
142
-
143
- /* record reset processing */
144
- this.addEventListener('record-reset', e => {
145
- var { record, row } = (e as CustomEvent).detail
146
-
147
- this.onRecordChanged(record['__origin__'], row, null)
148
- })
149
89
  }
150
90
 
151
91
  onWheelEvent(e: WheelEvent) {
@@ -155,89 +95,19 @@ export class DataGrid extends LitElement {
155
95
  supportsPassive || e.preventDefault()
156
96
  }
157
97
 
158
- onRecordChanged(
159
- recordData: GristRecord,
160
- row: number,
161
- column: ColumnConfig | null /* TODO column should be removed */
162
- ) {
163
- // TODO 오브젝트나 배열 타입인 경우 deepCompare 후에 변경 적용 여부를 결정한다.
164
-
165
- /* 빈 그리드로 시작한 경우, data 설정이 되어있지 않을 수 있다. */
166
- var records = this.data.records
167
-
168
- var beforeRecord: GristRecord = records[row]
169
- var afterRecord: GristRecord
170
- var wantToDelete = false
171
- var wantToAppend = false
172
-
173
- if (!recordData) {
174
- if (!beforeRecord) {
175
- /* recordData가 없고, beforeRecord도 없다면, 레코드 생성 중에 리셋된 경우이므로 아무것도 하지 않는다. */
176
- this.requestUpdate()
177
- return
178
- } else {
179
- /*
180
- * beforeRecord가 있는데, 빈데이타로 업데이트하고자 한다면,
181
- * 삭제하고자 하는 의도로 이해된다. (주의 필요)
182
- */
183
- if (beforeRecord['__dirty__'] == '+') {
184
- wantToDelete = true
185
- } else {
186
- afterRecord = {
187
- ...beforeRecord,
188
- __dirty__: '-'
189
- }
190
- }
191
- }
192
- } else {
193
- if (!beforeRecord) {
194
- /* 기존 레코드가 없는 경우에는 새로운 레코드가 생성된다 */
195
- afterRecord = {
196
- ...recordData,
197
- __dirty__: '+'
198
- }
199
-
200
- wantToAppend = true
201
- } else {
202
- let beforeDirty = beforeRecord.__dirty__
203
- if (beforeDirty == '+') {
204
- /* 기존에 새로 생성된 레코드가 있었으며 계속 수정중이다. */
205
- afterRecord = {
206
- ...beforeRecord,
207
- ...recordData,
208
- __dirty__: '+'
209
- }
210
- } else {
211
- /* 기존에 레코드가 있었으며 계속 수정중이다. */
212
- afterRecord = {
213
- ...beforeRecord,
214
- ...recordData,
215
- __dirty__: 'M'
216
- }
217
- }
218
- }
219
- }
220
-
221
- if (wantToAppend) {
222
- records.push(afterRecord!)
223
- } else if (wantToDelete) {
224
- records.splice(row, 1)
225
- } else {
226
- records.splice(row, 1, afterRecord!)
227
- }
228
-
229
- this.dispatchEvent(
230
- new CustomEvent('record-change', {
231
- bubbles: true,
232
- composed: true,
233
- detail: {
234
- before: beforeRecord,
235
- after: afterRecord!,
236
- column,
237
- row
238
- }
239
- })
240
- )
98
+ onSelectRecordChanged({
99
+ selectedRecords,
100
+ added,
101
+ removed
102
+ }: {
103
+ selectedRecords: GristRecord[]
104
+ added: GristRecord[]
105
+ removed: GristRecord[]
106
+ }): void {
107
+ super.onSelectRecordChanged({ selectedRecords, added, removed })
108
+
109
+ this.header.requestUpdate()
110
+ this.body.requestUpdate()
241
111
  }
242
112
 
243
113
  updated(changes: PropertyValues<this>) {
package/src/data-grist.ts CHANGED
@@ -275,6 +275,10 @@ export class DataGrist extends LitElement implements DataConsumer {
275
275
  }
276
276
  }
277
277
 
278
+ get compiledConfig(): GristConfig {
279
+ return this._config
280
+ }
281
+
278
282
  get sorters(): SortersConfig | undefined {
279
283
  return this._config?.sorters
280
284
  }
@@ -392,15 +396,12 @@ export class DataGrist extends LitElement implements DataConsumer {
392
396
  * @method
393
397
  */
394
398
  reset() {
395
- if (!ZERO_PAGINATION) {
396
- console.error('ZERO_PAGINATION', ZERO_PAGINATION)
397
- }
398
399
  var {
399
400
  limit = ZERO_PAGINATION.limit,
400
401
  page = ZERO_PAGINATION.page,
401
402
  total = ZERO_PAGINATION.total,
402
403
  records = []
403
- } = this.data
404
+ } = this.data || ZERO_PAGINATION
404
405
 
405
406
  /* 원본 데이타를 남기고, 복사본(_data)을 사용한다. */
406
407
  records = records.map((record, idx) => {
@@ -1,14 +1,14 @@
1
1
  import '@material/mwc-icon'
2
2
  import './record-partial'
3
3
 
4
- import { css, html, LitElement, PropertyValues } from 'lit'
4
+ import { css, html, PropertyValues } from 'lit'
5
5
  import { customElement, property } from 'lit/decorators.js'
6
6
 
7
- import { ZERO_CONFIG, ZERO_DATA } from '../configure/zero-config'
8
- import { ColumnConfig, GristConfig, GristData, GristRecord } from '../types'
7
+ import { DataManipulator } from '../data-manipulator'
8
+ import { GristRecord } from '../types'
9
9
 
10
10
  @customElement('ox-list')
11
- export class DataList extends LitElement {
11
+ export class DataList extends DataManipulator {
12
12
  static styles = [
13
13
  css`
14
14
  :host {
@@ -42,8 +42,6 @@ export class DataList extends LitElement {
42
42
  `
43
43
  ]
44
44
 
45
- @property({ type: Object }) config: GristConfig = ZERO_CONFIG
46
- @property({ type: Object }) data: GristData = ZERO_DATA
47
45
  @property({ type: Boolean }) isTop: boolean = false
48
46
  @property({ type: Array }) private _records: GristRecord[] = []
49
47
 
@@ -71,156 +69,6 @@ export class DataList extends LitElement {
71
69
 
72
70
  this.isTop = this.scrollTop == 0
73
71
  })
74
-
75
- this.addEventListener('select-record-change', e => {
76
- var {
77
- records: selectedRecords,
78
- added = [],
79
- removed = []
80
- } = (e as CustomEvent).detail as {
81
- records: GristRecord[]
82
- added: GristRecord[]
83
- removed: GristRecord[]
84
- }
85
- var { records } = this.data || {}
86
- var { selectable = false } = this.config.rows || {}
87
-
88
- if (!records || !selectable) {
89
- return
90
- } else if (selectable && !selectable.multiple) {
91
- records.forEach(record => (record['__selected__'] = false))
92
- }
93
-
94
- if (selectedRecords) {
95
- records.forEach(record => (record['__selected__'] = false))
96
- selectedRecords.forEach(record => (record['__selected__'] = true))
97
- } else {
98
- removed.forEach(record => (record['__selected__'] = false))
99
- added.forEach(record => (record['__selected__'] = true))
100
- }
101
-
102
- this.requestUpdate()
103
- })
104
-
105
- /* field change processing */
106
- this.addEventListener('field-change', e => {
107
- var { after, before, column, record, row } = (e as CustomEvent).detail as {
108
- after: any
109
- before: any
110
- column: ColumnConfig
111
- record: GristRecord
112
- row: number
113
- }
114
-
115
- /* compare changes */
116
- if (after === before) {
117
- return
118
- }
119
-
120
- var validation = column.validation
121
- if (validation && typeof validation == 'function') {
122
- if (!validation.call(this, after, before, record, column)) {
123
- return
124
- }
125
- }
126
-
127
- this.onRecordChanged({ [column.name]: after }, row, column)
128
- })
129
-
130
- /* record reset processing */
131
- this.addEventListener('record-reset', e => {
132
- var { record, row } = (e as CustomEvent).detail as {
133
- record: GristRecord
134
- row: number
135
- }
136
-
137
- this.onRecordChanged(record['__origin__'], row, null)
138
- })
139
- }
140
-
141
- onRecordChanged(
142
- recordData: GristRecord,
143
- row: number,
144
- column: ColumnConfig | null /* TODO column should be removed */
145
- ) {
146
- // TODO 오브젝트나 배열 타입인 경우 deepCompare 후에 변경 적용 여부를 결정한다.
147
-
148
- /* 빈 그리드로 시작한 경우, data 설정이 되어있지 않을 수 있다. */
149
- var records = this.data.records
150
-
151
- var beforeRecord = records[row]
152
- var afterRecord: GristRecord
153
- var wantToDelete = false
154
- var wantToAppend = false
155
-
156
- if (!recordData) {
157
- if (!beforeRecord) {
158
- /* recordData가 없고, beforeRecord도 없다면, 레코드 생성 중에 리셋된 경우이므로 아무것도 하지 않는다. */
159
- this.requestUpdate()
160
- return
161
- } else {
162
- /*
163
- * beforeRecord가 있는데, 빈데이타로 업데이트하고자 한다면,
164
- * 삭제하고자 하는 의도로 이해된다. (주의 필요)
165
- */
166
- if (beforeRecord['__dirty__'] == '+') {
167
- wantToDelete = true
168
- } else {
169
- afterRecord = {
170
- ...beforeRecord,
171
- __dirty__: '-'
172
- }
173
- }
174
- }
175
- } else {
176
- if (!beforeRecord) {
177
- /* 기존 레코드가 없는 경우에는 새로운 레코드가 생성된다 */
178
- afterRecord = {
179
- ...recordData,
180
- __dirty__: '+'
181
- }
182
-
183
- wantToAppend = true
184
- } else {
185
- let beforeDirty = beforeRecord['__dirty__']
186
- if (beforeDirty == '+') {
187
- /* 기존에 새로 생성된 레코드가 있었으며 계속 수정중이다. */
188
- afterRecord = {
189
- ...beforeRecord,
190
- ...recordData,
191
- __dirty__: '+'
192
- }
193
- } else {
194
- /* 기존에 레코드가 있었으며 계속 수정중이다. */
195
- afterRecord = {
196
- ...beforeRecord,
197
- ...recordData,
198
- __dirty__: 'M'
199
- }
200
- }
201
- }
202
- }
203
-
204
- if (wantToAppend) {
205
- records.push(afterRecord!)
206
- } else if (wantToDelete) {
207
- records.splice(row, 1)
208
- } else {
209
- records.splice(row, 1, afterRecord!)
210
- }
211
-
212
- this.dispatchEvent(
213
- new CustomEvent('record-change', {
214
- bubbles: true,
215
- composed: true,
216
- detail: {
217
- before: beforeRecord,
218
- after: afterRecord!,
219
- column,
220
- row
221
- }
222
- })
223
- )
224
72
  }
225
73
 
226
74
  updated(changes: PropertyValues<this>) {
@@ -0,0 +1,201 @@
1
+ import { LitElement } from 'lit'
2
+ import { property } from 'lit/decorators.js'
3
+
4
+ import { ZERO_CONFIG, ZERO_DATA } from './configure/zero-config'
5
+ import { ColumnConfig, GristConfig, GristData, GristRecord } from './types'
6
+
7
+ export class DataManipulator extends LitElement {
8
+ @property({ type: Object }) config: GristConfig = ZERO_CONFIG
9
+ @property({ type: Object }) data: GristData = ZERO_DATA
10
+
11
+ constructor() {
12
+ super()
13
+
14
+ this.addEventListener('select-record-change', e => {
15
+ var {
16
+ records: selectedRecords,
17
+ added = [],
18
+ removed = []
19
+ } = (e as CustomEvent).detail as {
20
+ records: GristRecord[]
21
+ added: GristRecord[]
22
+ removed: GristRecord[]
23
+ }
24
+
25
+ this.onSelectRecordChanged({
26
+ selectedRecords,
27
+ added,
28
+ removed
29
+ })
30
+ })
31
+
32
+ /* field change processing */
33
+ this.addEventListener('field-change', e => {
34
+ var { after, before, column, record, row } = (e as CustomEvent).detail as {
35
+ after: any
36
+ before: any
37
+ column: ColumnConfig
38
+ record: GristRecord
39
+ row: number
40
+ }
41
+
42
+ this.onFieldChange({ after, before, column, record, row })
43
+ })
44
+
45
+ /* record reset processing */
46
+ this.addEventListener('record-reset', e => {
47
+ var { record, row } = (e as CustomEvent).detail as {
48
+ record: GristRecord
49
+ row: number
50
+ }
51
+
52
+ this.onRecordChanged(record['__origin__'], row, null)
53
+ })
54
+ }
55
+
56
+ onFieldChange({
57
+ after,
58
+ before,
59
+ column,
60
+ record,
61
+ row
62
+ }: {
63
+ after: any
64
+ before: any
65
+ column: ColumnConfig
66
+ record: GristRecord
67
+ row: number
68
+ }) {
69
+ /* compare changes */
70
+ if (after === before) {
71
+ return
72
+ }
73
+
74
+ var validation = column.validation
75
+ if (validation && typeof validation == 'function') {
76
+ if (!validation.call(this, after, before, record, column)) {
77
+ return
78
+ }
79
+ }
80
+
81
+ this.onRecordChanged({ [column.name]: after }, row, column)
82
+ }
83
+
84
+ onSelectRecordChanged({
85
+ selectedRecords,
86
+ added = [],
87
+ removed = []
88
+ }: {
89
+ selectedRecords: GristRecord[]
90
+ added: GristRecord[]
91
+ removed: GristRecord[]
92
+ }) {
93
+ var { records } = this.data || {}
94
+ var { selectable = false } = this.config.rows || {}
95
+
96
+ if (!records || !selectable) {
97
+ return
98
+ }
99
+
100
+ if (selectable && !selectable.multiple) {
101
+ records.forEach(record => (record['__selected__'] = false))
102
+ }
103
+
104
+ if (selectedRecords) {
105
+ records.forEach(record => (record['__selected__'] = false))
106
+ selectedRecords.forEach(record => (record['__selected__'] = true))
107
+ } else {
108
+ removed.forEach(record => (record['__selected__'] = false))
109
+ added.forEach(record => (record['__selected__'] = true))
110
+ }
111
+
112
+ this.requestUpdate()
113
+ }
114
+
115
+ onRecordChanged(
116
+ recordData: GristRecord,
117
+ row: number,
118
+ column: ColumnConfig | null /* TODO column should be removed */
119
+ ) {
120
+ // TODO 오브젝트나 배열 타입인 경우 deepCompare 후에 변경 적용 여부를 결정한다.
121
+
122
+ /* 빈 그리드로 시작한 경우, data 설정이 되어있지 않을 수 있다. */
123
+ var records = this.data.records
124
+
125
+ var beforeRecord = records[row]
126
+ var afterRecord: GristRecord
127
+ var wantToDelete = false
128
+ var wantToAppend = false
129
+
130
+ if (!recordData) {
131
+ if (!beforeRecord) {
132
+ /* recordData가 없고, beforeRecord도 없다면, 레코드 생성 중에 리셋된 경우이므로 아무것도 하지 않는다. */
133
+ this.requestUpdate()
134
+ return
135
+ } else {
136
+ /*
137
+ * beforeRecord가 있는데, 빈데이타로 업데이트하고자 한다면,
138
+ * 삭제하고자 하는 의도로 이해된다. (주의 필요)
139
+ */
140
+ if (beforeRecord['__dirty__'] == '+') {
141
+ wantToDelete = true
142
+ } else {
143
+ afterRecord = {
144
+ ...beforeRecord,
145
+ __dirty__: '-'
146
+ }
147
+ }
148
+ }
149
+ } else {
150
+ if (!beforeRecord) {
151
+ /* 기존 레코드가 없는 경우에는 새로운 레코드가 생성된다 */
152
+ afterRecord = {
153
+ ...recordData,
154
+ __dirty__: '+'
155
+ }
156
+
157
+ wantToAppend = true
158
+ } else {
159
+ let beforeDirty = beforeRecord['__dirty__']
160
+ if (beforeDirty == '+') {
161
+ /* 기존에 새로 생성된 레코드가 있었으며 계속 수정중이다. */
162
+ afterRecord = {
163
+ ...beforeRecord,
164
+ ...recordData,
165
+ __dirty__: '+'
166
+ }
167
+ } else {
168
+ /* 기존에 레코드가 있었으며 계속 수정중이다. */
169
+ afterRecord = {
170
+ ...beforeRecord,
171
+ ...recordData,
172
+ __dirty__: 'M'
173
+ }
174
+ }
175
+ }
176
+ }
177
+
178
+ if (wantToAppend) {
179
+ records.push(afterRecord!)
180
+ } else if (wantToDelete) {
181
+ records.splice(row, 1)
182
+ } else {
183
+ records.splice(row, 1, afterRecord!)
184
+ }
185
+
186
+ this.dispatchEvent(
187
+ new CustomEvent('record-change', {
188
+ bubbles: true,
189
+ composed: true,
190
+ detail: {
191
+ before: beforeRecord,
192
+ after: afterRecord!,
193
+ column,
194
+ row
195
+ }
196
+ })
197
+ )
198
+
199
+ this.requestUpdate()
200
+ }
201
+ }
@@ -1,3 +1,5 @@
1
+ import { DataGridField } from '../data-grid/data-grid-field'
2
+ import { ColumnConfig, FieldEditor, GristRecord } from '../types'
1
3
  import {
2
4
  CheckboxInput,
3
5
  ColorInput,
@@ -15,9 +17,6 @@ import {
15
17
  TimeInput,
16
18
  WeekInput
17
19
  } from './input-editors'
18
- import { ColumnConfig, FieldEditor, GristRecord } from '../types'
19
-
20
- import { DataGridField } from '../data-grid/data-grid-field'
21
20
 
22
21
  var EDITORS: { [name: string]: { new (): InputEditor } } = {
23
22
  string: TextInput,
@@ -1,9 +1,9 @@
1
1
  import '@material/mwc-icon'
2
2
 
3
- import { FieldRenderer, HeaderRenderer } from '../types'
4
-
5
3
  import { html } from 'lit'
6
4
 
5
+ import { FieldRenderer, HeaderRenderer } from '../types'
6
+
7
7
  export class GutterButton {
8
8
  static instance(config: any = {}) {
9
9
  var { icon = 'edit' } = config
@@ -27,6 +27,7 @@ export class GutterButton {
27
27
  } as HeaderRenderer
28
28
  },
29
29
  record: {
30
+ align: 'center',
30
31
  renderer: function (value, column, record, rowIndex, field) {
31
32
  return html` <mwc-icon style=${inlineRecordStyle}>${iconFn(record)}</mwc-icon> `
32
33
  } as FieldRenderer