@operato/data-grist 1.11.6 → 1.11.8

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 (39) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/demo/data-grist-test.html +2 -1
  3. package/dist/src/configure/column-builder.js +8 -0
  4. package/dist/src/configure/column-builder.js.map +1 -1
  5. package/dist/src/data-grid/data-grid-accum-field.js +2 -15
  6. package/dist/src/data-grid/data-grid-accum-field.js.map +1 -1
  7. package/dist/src/data-grid/data-grid-body.js +0 -1
  8. package/dist/src/data-grid/data-grid-body.js.map +1 -1
  9. package/dist/src/data-grid/data-grid-header.d.ts +8 -5
  10. package/dist/src/data-grid/data-grid-header.js +131 -47
  11. package/dist/src/data-grid/data-grid-header.js.map +1 -1
  12. package/dist/src/editors/ox-grist-editor.js +1 -1
  13. package/dist/src/editors/ox-grist-editor.js.map +1 -1
  14. package/dist/src/types.d.ts +2 -0
  15. package/dist/src/types.js.map +1 -1
  16. package/dist/stories/dynamic-editable.stories.js +22 -7
  17. package/dist/stories/dynamic-editable.stories.js.map +1 -1
  18. package/dist/stories/fixed-column.stories.js +2 -1
  19. package/dist/stories/fixed-column.stories.js.map +1 -1
  20. package/dist/stories/grist-modes.stories.js +2 -1
  21. package/dist/stories/grist-modes.stories.js.map +1 -1
  22. package/dist/stories/group-header.stories.d.ts +26 -0
  23. package/dist/stories/group-header.stories.js +473 -0
  24. package/dist/stories/group-header.stories.js.map +1 -0
  25. package/dist/tsconfig.tsbuildinfo +1 -1
  26. package/package.json +2 -2
  27. package/src/configure/column-builder.ts +8 -0
  28. package/src/data-grid/data-grid-accum-field.ts +2 -15
  29. package/src/data-grid/data-grid-body.ts +0 -2
  30. package/src/data-grid/data-grid-header.ts +148 -47
  31. package/src/editors/ox-grist-editor.ts +1 -1
  32. package/src/types.ts +2 -0
  33. package/stories/dynamic-editable.stories.ts +22 -7
  34. package/stories/fixed-column.stories.ts +2 -1
  35. package/stories/grist-modes.stories.ts +2 -1
  36. package/stories/group-header.stories.ts +505 -0
  37. package/dist/src/data-grid/data-grid-field-header.d.ts +0 -24
  38. package/dist/src/data-grid/data-grid-field-header.js +0 -381
  39. package/dist/src/data-grid/data-grid-field-header.js.map +0 -1
@@ -1,8 +1,9 @@
1
1
  import '@operato/popup/ox-popup.js'
2
2
  import '@material/mwc-icon'
3
3
 
4
- import { css, html, LitElement, PropertyValues, nothing } from 'lit'
4
+ import { css, html, LitElement, PropertyValues, nothing, TemplateResult } from 'lit'
5
5
  import { customElement, property, state } from 'lit/decorators.js'
6
+ import { ifDefined } from 'lit/directives/if-defined.js'
6
7
  import throttle from 'lodash-es/throttle'
7
8
 
8
9
  import { OxPopup } from '@operato/popup'
@@ -24,6 +25,7 @@ export class DataGridHeader extends LitElement {
24
25
  display: grid;
25
26
  grid-template-columns: var(--grid-template-columns);
26
27
 
28
+ border-top: var(--grid-header-top-border);
27
29
  overflow: hidden;
28
30
  }
29
31
 
@@ -33,7 +35,6 @@ export class DataGridHeader extends LitElement {
33
35
  white-space: nowrap;
34
36
  overflow: hidden;
35
37
  background-color: var(--grist-background-color);
36
- border-top: var(--grid-header-top-border);
37
38
  border-bottom: var(--grid-header-bottom-border);
38
39
  padding: var(--grid-header-padding);
39
40
 
@@ -58,6 +59,7 @@ export class DataGridHeader extends LitElement {
58
59
  text-overflow: ellipsis;
59
60
  line-height: 1.6;
60
61
  text-transform: capitalize;
62
+ align-self: center;
61
63
  }
62
64
 
63
65
  span[for-title] mwc-icon {
@@ -66,6 +68,9 @@ export class DataGridHeader extends LitElement {
66
68
 
67
69
  span[sorter],
68
70
  span[filter] {
71
+ display: flex;
72
+ align-self: center;
73
+
69
74
  padding: 0;
70
75
  border: 0;
71
76
  }
@@ -120,6 +125,15 @@ export class DataGridHeader extends LitElement {
120
125
  z-index: 1;
121
126
  }
122
127
 
128
+ .span-both {
129
+ grid-row: 1 / span 2; /* 1단과 2단에 걸쳐 표시 */
130
+ }
131
+
132
+ .group-header {
133
+ grid-row: 1; /* 1행에 배치 */
134
+ grid-column: var(--group-start) / span var(--group-size); /* 2열부터 시작하여 2열에 걸쳐 표시 */
135
+ }
136
+
123
137
  @media print {
124
138
  :host {
125
139
  grid-template-columns: var(--grid-template-print-columns);
@@ -134,6 +148,21 @@ export class DataGridHeader extends LitElement {
134
148
  @property({ type: Array }) filters: FilterValue[] = []
135
149
  @property({ type: Boolean, attribute: 'filtering-feature' }) filteringFeature: boolean = false
136
150
 
151
+ @state() private row1: {
152
+ index: number
153
+ column: ColumnConfig
154
+ clazz?: string
155
+ start?: number
156
+ size?: number
157
+ align?: string
158
+ group?: string
159
+ }[] = []
160
+
161
+ @state() private row2: {
162
+ index: number
163
+ column: ColumnConfig
164
+ }[] = []
165
+
137
166
  private _lastAccVal?: number
138
167
  private _throttledNotifier?: any
139
168
 
@@ -176,54 +205,71 @@ export class DataGridHeader extends LitElement {
176
205
  }
177
206
 
178
207
  render() {
179
- var columns = this.columns || []
208
+ const clazz = this.row2.length > 0 ? 'span-both' : undefined
180
209
 
181
210
  return html`
182
- ${columns.map((column, idx) =>
183
- !column.hidden
184
- ? html`
185
- <div ?gutter=${column.type == 'gutter'} ?fixed=${column.fixed} column>
186
- <span
187
- for-title
188
- style="text-align:${column.record.align || 'left'};${column.header?.style || ''}"
189
- @click=${(e: MouseEvent) => this._changeSort(column)}
190
- @mouseover=${(e: MouseEvent) => {
191
- const element = e.target as HTMLSpanElement
192
-
193
- if (detectOverflow(element)) {
194
- element.setAttribute('data-tooltip', element.textContent!.trim())
195
- }
196
- }}
197
- @mouseout=${(e: MouseEvent) => {
198
- const element = e.target as HTMLSpanElement
199
- element.removeAttribute('data-tooltip')
200
- }}
201
- >${this._renderHeader(column)}
202
- </span>
203
-
204
- ${column.sortable
205
- ? html`
206
- <span sorter @click=${(e: MouseEvent) => this._changeSort(column)}>
207
- ${this._renderSortHeader(column)}
208
- </span>
209
- `
210
- : nothing}
211
- ${this.filteringFeature && column.filter && (column.filter as FilterConfigObject).operator !== 'search'
212
- ? html` <span filter> ${this._renderFilterHeader(column)} </span> `
213
- : nothing}
214
- ${column.resizable !== false
215
- ? html`
216
- <span splitter draggable="false" @mousedown=${(e: MouseEvent) => this._mousedown(e, idx)}
217
- >&nbsp;</span
218
- >
219
- `
220
- : nothing}
221
- </div>
222
- `
223
- : nothing
211
+ ${this.row1.map(({ column, clazz, start, size, align, index, group }) =>
212
+ this.renderHeaderColumn({ column, clazz, start, size, align, index, group })
224
213
  )}
225
214
 
226
- <div column></div>
215
+ <div column class=${ifDefined(clazz)}></div>
216
+
217
+ ${this.row2.map(({ column, index }) => this.renderHeaderColumn({ column, index }))}
218
+ `
219
+ }
220
+
221
+ renderHeaderColumn({ column, clazz, start, size, align, index, group }: any): TemplateResult {
222
+ if (column.hidden) {
223
+ return html`${nothing}`
224
+ }
225
+
226
+ return html`
227
+ <div
228
+ ?gutter=${column.type == 'gutter'}
229
+ ?fixed=${column.fixed}
230
+ column
231
+ class=${ifDefined(clazz)}
232
+ style=${group
233
+ ? `--group-start:${start};--group-size:${size};${column.header?.style || ''}`
234
+ : `${column.header?.style || ''}`}
235
+ >
236
+ <span
237
+ for-title
238
+ style="text-align:${align || column.record.align || 'left'};"
239
+ @click=${(e: MouseEvent) => this._changeSort(column)}
240
+ @mouseover=${(e: MouseEvent) => {
241
+ const element = e.target as HTMLSpanElement
242
+
243
+ if (detectOverflow(element)) {
244
+ element.setAttribute('data-tooltip', element.textContent!.trim())
245
+ }
246
+ }}
247
+ @mouseout=${(e: MouseEvent) => {
248
+ const element = e.target as HTMLSpanElement
249
+ element.removeAttribute('data-tooltip')
250
+ }}
251
+ >${this._renderHeader(column)}
252
+ </span>
253
+
254
+ ${!group && column.sortable
255
+ ? html`
256
+ <span sorter @click=${(e: MouseEvent) => this._changeSort(column)}>
257
+ ${this._renderSortHeader(column)}
258
+ </span>
259
+ `
260
+ : nothing}
261
+ ${!group &&
262
+ this.filteringFeature &&
263
+ column.filter &&
264
+ (column.filter as FilterConfigObject).operator !== 'search'
265
+ ? html` <span filter> ${this._renderFilterHeader(column)} </span> `
266
+ : nothing}
267
+ ${column.resizable !== false
268
+ ? html`
269
+ <span splitter draggable="false" @mousedown=${(e: MouseEvent) => this._mousedown(e, index)}>&nbsp;</span>
270
+ `
271
+ : nothing}
272
+ </div>
227
273
  `
228
274
  }
229
275
 
@@ -246,6 +292,61 @@ export class DataGridHeader extends LitElement {
246
292
  })
247
293
  )
248
294
  }
295
+
296
+ if (changes.has('columns')) {
297
+ this.row2 = this.columns.reduce((row2, column, index) => {
298
+ if (column.hidden || !column.header?.group) {
299
+ return row2
300
+ }
301
+ return row2.concat({
302
+ index,
303
+ column
304
+ } as any)
305
+ }, [] as any[])
306
+
307
+ const clazz = this.row2.length > 0 ? 'span-both' : undefined
308
+
309
+ var columnNo = 0
310
+
311
+ this.row1 = this.columns.reduce((row1, column, index) => {
312
+ if (column.hidden) {
313
+ return row1
314
+ }
315
+
316
+ columnNo++
317
+ if (!column.header?.group) {
318
+ return row1.concat({
319
+ index,
320
+ column,
321
+ clazz
322
+ } as any)
323
+ }
324
+ const { group, groupStyle } = column.header
325
+ const last = row1[row1.length - 1] as any
326
+
327
+ if (!last || group !== last.group) {
328
+ return row1.concat({
329
+ index,
330
+ column: {
331
+ ...column,
332
+ header: {
333
+ renderer: () => group,
334
+ style: groupStyle
335
+ }
336
+ },
337
+ group,
338
+ align: 'center',
339
+ clazz: 'group-header',
340
+ start: columnNo,
341
+ size: 1
342
+ } as any)
343
+ }
344
+
345
+ last.size++
346
+
347
+ return row1
348
+ }, [] as any[])
349
+ }
249
350
  }
250
351
 
251
352
  _renderHeader(column: ColumnConfig) {
@@ -433,7 +534,7 @@ export class DataGridHeader extends LitElement {
433
534
  e.preventDefault()
434
535
  let column = this.columns[idx]
435
536
 
436
- let width = Math.max(0, Number(column.width) + this._accumalate(e.movementX))
537
+ let width = Math.max(0, Number(column.width || 100) + this._accumalate(e.movementX))
437
538
  if (width == 0) {
438
539
  /* CLARIFY-ME 왜 마지막 이벤트의 offsetX로 음수 값이 오는가 */
439
540
  return
@@ -123,7 +123,7 @@ export class OxGristEditor extends LitElement {
123
123
 
124
124
  // 입력을 위한 키를 누르면서 편집모드가 될때는 누른 키가 처음에 입력되도록, enter 같은 것을 눌러서 편집모드가 되면 현재 값으로 편집모드 전환
125
125
  const editorValue = this.field?.valueWithEdit ? this.field.valueWithEdit : this.formatForEditor(currentValue)
126
- this.value = this._dirtyValue = editorValue
126
+ this.value = this._dirtyValue = isFinite(editorValue) ? Number(editorValue) : editorValue
127
127
 
128
128
  requestAnimationFrame(() => {
129
129
  this.focus()
package/src/types.ts CHANGED
@@ -159,6 +159,8 @@ export type ColumnWidthCallback = (column: ColumnConfig) => string
159
159
  export type HeaderConfig = {
160
160
  renderer: HeaderRenderer
161
161
  style?: string
162
+ group?: string
163
+ groupStyle?: string
162
164
  }
163
165
  export type HeaderRenderer = (column: ColumnConfig) => any
164
166
 
@@ -24,7 +24,10 @@ const fetchHandler: FetchHandler = async ({ sorters = [], filters, page, limit }
24
24
  name: idx % 2 ? `shnam-${start + idx + 1}` : `heartyoh-${start + idx + 1}`,
25
25
  description: idx % 2 ? `hatiolabmanager${start + idx + 1}1234567890` : `hatiosea manager-${start + idx + 1}`,
26
26
  date: '2023-09-20',
27
- createdAt: Date.now(),
27
+ routing: {
28
+ id: '006dc64d-4fb9-4afc-a9ea-962bd1b9e110',
29
+ name: '조림>세척'
30
+ },
28
31
  updatedAt: Date.now()
29
32
  }
30
33
  })
@@ -104,19 +107,31 @@ function buildConfig({ headerFilter }: { headerFilter: boolean }) {
104
107
  width: 120
105
108
  },
106
109
  {
107
- type: 'datetime',
108
- name: 'updatedAt',
109
- header: 'updated at',
110
+ type: 'object',
111
+ name: 'routing',
112
+ header: 'routing',
113
+ record: { editable: true, options: { queryName: 'routings' }, mandatory: true },
110
114
  width: 180
111
115
  },
112
116
  {
113
117
  type: 'datetime',
114
- name: 'createdAt',
115
- header: 'created at',
118
+ name: 'updatedAt',
119
+ header: 'updated at',
116
120
  width: 180
117
121
  }
118
122
  ],
119
- rows: {},
123
+ rows: {
124
+ selectable: {
125
+ multiple: false
126
+ },
127
+ handlers: {
128
+ click: 'select-row',
129
+ dblclick: () => {
130
+ console.log('dblclick333')
131
+ }
132
+ },
133
+ appendable: false
134
+ },
120
135
  sorters: [
121
136
  {
122
137
  name: 'name',
@@ -259,7 +259,8 @@ const config = {
259
259
  header: 'thumbnail',
260
260
  record: {
261
261
  editable: true
262
- }
262
+ },
263
+ width: 120
263
264
  },
264
265
  {
265
266
  type: 'datetime',
@@ -252,7 +252,8 @@ const config = {
252
252
  header: 'thumbnail',
253
253
  record: {
254
254
  editable: true
255
- }
255
+ },
256
+ width: 120
256
257
  },
257
258
  {
258
259
  type: 'datetime',