@operato/data-grist 2.0.0-alpha.133 → 2.0.0-alpha.134

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 (30) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/src/data-grid/data-grid-header.js +1 -1
  3. package/dist/src/data-grid/data-grid-header.js.map +1 -1
  4. package/dist/src/data-grist.js +34 -36
  5. package/dist/src/data-grist.js.map +1 -1
  6. package/dist/src/filters/filter-styles.js +21 -11
  7. package/dist/src/filters/filter-styles.js.map +1 -1
  8. package/dist/src/filters/filters-form.d.ts +8 -2
  9. package/dist/src/filters/filters-form.js +128 -62
  10. package/dist/src/filters/filters-form.js.map +1 -1
  11. package/dist/src/personalizer/ox-grist-filter-personalizer.d.ts +8 -0
  12. package/dist/src/personalizer/ox-grist-filter-personalizer.js +177 -0
  13. package/dist/src/personalizer/ox-grist-filter-personalizer.js.map +1 -0
  14. package/dist/src/personalizer/ox-grist-personalizer.js +7 -1
  15. package/dist/src/personalizer/ox-grist-personalizer.js.map +1 -1
  16. package/dist/src/types.d.ts +11 -0
  17. package/dist/src/types.js.map +1 -1
  18. package/dist/stories/grid-setting.stories.d.ts +1 -0
  19. package/dist/stories/grid-setting.stories.js +21 -29
  20. package/dist/stories/grid-setting.stories.js.map +1 -1
  21. package/dist/tsconfig.tsbuildinfo +1 -1
  22. package/package.json +6 -6
  23. package/src/data-grid/data-grid-header.ts +1 -1
  24. package/src/data-grist.ts +36 -38
  25. package/src/filters/filter-styles.ts +21 -11
  26. package/src/filters/filters-form.ts +196 -125
  27. package/src/personalizer/ox-grist-filter-personalizer.ts +191 -0
  28. package/src/personalizer/ox-grist-personalizer.ts +7 -1
  29. package/src/types.ts +12 -0
  30. package/stories/grid-setting.stories.ts +22 -29
package/src/data-grist.ts CHANGED
@@ -176,7 +176,7 @@ export class DataGrist extends LitElement implements DataConsumer {
176
176
  load: () => {
177
177
  return {}
178
178
  },
179
- save: preference => preference,
179
+ save: (preference: any) => preference,
180
180
  reset: () => {}
181
181
  }
182
182
 
@@ -326,51 +326,49 @@ export class DataGrist extends LitElement implements DataConsumer {
326
326
  const config = { ...this.config }
327
327
  const columns: Partial<ColumnConfig>[] = config.columns
328
328
 
329
- if (this.personalConfig) {
330
- const { columns: personalColumns, sorters, pagination, mode } = this.personalConfig
329
+ const { columns: personalColumns, sorters, pagination, mode } = this.personalConfig
331
330
 
332
- if (personalColumns) {
333
- const xcolumns = columns.map((column: Partial<ColumnConfig>) => {
334
- const personalColumn = personalColumns.find(pcolumn => pcolumn.name == column.name)
335
- return personalColumn ? { ...column, ...personalColumn } : column
336
- })
337
-
338
- function reorderList(a: { name: string }[], b: { name: string }[]): { name: string }[] {
339
- // 결과 배열 초기화, a 배열 길이만큼 undefined로 채움
340
- const result = new Array(a.length)
331
+ if (personalColumns) {
332
+ const xcolumns = columns.map((column: Partial<ColumnConfig>) => {
333
+ const personalColumn = personalColumns.find(pcolumn => pcolumn.name == column.name)
334
+ return personalColumn ? { ...column, ...personalColumn } : column
335
+ })
341
336
 
342
- // b 배열에 없는 아이템은 원래 위치로 채움
343
- a.forEach((item, index) => {
344
- if (!item.name || !b.find(bi => bi.name == item.name)) {
345
- result[index] = item
346
- }
347
- })
337
+ function reorderList(a: { name: string }[], b: { name: string }[]): { name: string }[] {
338
+ // 결과 배열 초기화, a 배열 길이만큼 undefined로 채움
339
+ const result = new Array(a.length)
348
340
 
349
- b.forEach(item => {
350
- const ai = a.find(ai => ai.name == item.name)
351
- if (ai) {
352
- result[result.findIndex(slot => slot === undefined)] = ai
353
- }
354
- })
341
+ // b 배열에 없는 아이템은 원래 위치로 채움
342
+ a.forEach((item, index) => {
343
+ if (!item.name || !b.find(bi => bi.name == item.name)) {
344
+ result[index] = item
345
+ }
346
+ })
355
347
 
356
- return result
357
- }
348
+ b.forEach(item => {
349
+ const ai = a.find(ai => ai.name == item.name)
350
+ if (ai) {
351
+ result[result.findIndex(slot => slot === undefined)] = ai
352
+ }
353
+ })
358
354
 
359
- // 배열 재정렬 실행
360
- config.columns = reorderList(xcolumns as any, personalColumns as any)
355
+ return result
361
356
  }
362
357
 
363
- if (pagination) {
364
- config.pagination = pagination
365
- }
358
+ // 배열 재정렬 실행
359
+ config.columns = reorderList(xcolumns as any, personalColumns as any)
360
+ }
366
361
 
367
- if (sorters) {
368
- config.sorters = sorters
369
- }
362
+ if (pagination) {
363
+ config.pagination = pagination
364
+ }
370
365
 
371
- if (mode) {
372
- this.mode = mode
373
- }
366
+ if (sorters) {
367
+ config.sorters = sorters
368
+ }
369
+
370
+ if (mode) {
371
+ this.mode = mode
374
372
  }
375
373
 
376
374
  this._config = buildConfig({
@@ -611,7 +609,7 @@ export class DataGrist extends LitElement implements DataConsumer {
611
609
 
612
610
  if (changes.has('personalConfigProvider')) {
613
611
  this.personalConfig = await this.personalConfigProvider.load()
614
- } else if (changes.has('personalConfig')) {
612
+ } else if (changes.has('config') || changes.has('personalConfig')) {
615
613
  this.applyUpdatedConfiguration()
616
614
  }
617
615
 
@@ -47,10 +47,6 @@ export const FilterStyles = css`
47
47
  background-color: var(--ox-filters-input-background-color, transparent);
48
48
  }
49
49
 
50
- label > span + * {
51
- min-width: 120px;
52
- }
53
-
54
50
  ox-select:focus,
55
51
  input:focus {
56
52
  outline: none;
@@ -88,15 +84,29 @@ export const FilterStyles = css`
88
84
  --ox-filters-input-font: normal 16px var(--theme-font);
89
85
  }
90
86
 
91
- input[from],
92
- input[to] {
93
- max-width: 44%;
87
+ ox-input-barcode {
88
+ max-width: unset;
89
+ flex: 1;
90
+ }
91
+
92
+ ox-input-search {
93
+ max-width: unset;
94
+ }
95
+
96
+ ox-select {
97
+ max-width: unset;
98
+ }
99
+
100
+ ox-checkbox {
101
+ max-width: unset;
102
+ }
103
+
104
+ input[type='number'] {
105
+ max-width: unset;
94
106
  }
95
107
 
96
- input[type*='datetime'][from],
97
- input[type*='datetime'][to] {
98
- padding-right: var(--padding-narrow);
99
- min-width: 91%;
108
+ input {
109
+ flex: 1;
100
110
  }
101
111
  }
102
112
  `
@@ -6,11 +6,12 @@ import '@operato/input/ox-input-search.js'
6
6
  import { css, html, LitElement, PropertyValues, TemplateResult, nothing } from 'lit'
7
7
  import { customElement, property, queryAsync, state } from 'lit/decorators.js'
8
8
 
9
+ import { PagePreferenceProvider } from '@operato/p13n'
9
10
  import { getDefaultValue } from '@operato/time-calculator'
10
11
 
11
- import { FilterConfigObject } from '..'
12
+ import { FilterConfigObject, FilterPreference } from '../types.js'
12
13
  import { DataGrist } from '../data-grist'
13
- import { ColumnConfig, FilterOperator, FilterValue, GristConfig } from '../types'
14
+ import { ColumnConfig, FilterOperator, FilterValue, GristConfig, PersonalGristPreference } from '../types'
14
15
  import { FilterStyles } from './filter-styles'
15
16
  import { getFilterRenderer } from './registry'
16
17
 
@@ -50,8 +51,9 @@ export class OxFiltersForm extends LitElement {
50
51
  }
51
52
 
52
53
  @media only screen and (max-width: 460px) {
53
- label[between] {
54
- display: block;
54
+ form {
55
+ flex-direction: column;
56
+ flex-flow: column;
55
57
  }
56
58
  }
57
59
  `
@@ -62,6 +64,10 @@ export class OxFiltersForm extends LitElement {
62
64
  @property({ type: Boolean, attribute: 'autofocus' }) autofocus: boolean = true
63
65
  @property({ type: Boolean, attribute: 'empty', reflect: true }) empty: boolean = true
64
66
 
67
+ @state() personalConfigProvider?: PagePreferenceProvider
68
+ @state() personalConfig?: PersonalGristPreference
69
+ @state() personalFilters?: FilterPreference[]
70
+
65
71
  @state() config!: GristConfig
66
72
  @state() filterColumns: ColumnConfig[] = []
67
73
  @state() searchColumns: ColumnConfig[] = []
@@ -78,6 +84,7 @@ export class OxFiltersForm extends LitElement {
78
84
 
79
85
  if (grist) {
80
86
  this.config = grist.compiledConfig
87
+ this.personalConfigProvider = grist.personalConfigProvider
81
88
 
82
89
  grist.addEventListener('config-change', (e: Event) => {
83
90
  this.config = (e as CustomEvent).detail
@@ -139,44 +146,11 @@ export class OxFiltersForm extends LitElement {
139
146
  }
140
147
  }
141
148
 
142
- updated(changes: PropertyValues<this>) {
143
- if (changes.has('config')) {
144
- const filters = this.config.columns.filter(columnConfig => !!columnConfig.filter)
145
- this.filterColumns = filters.filter((columnConfig: ColumnConfig) => {
146
- const filter = columnConfig.filter as FilterConfigObject
147
- return filter!.operator !== 'search'
148
- })
149
- this.searchColumns = filters.filter(columnConfig => {
150
- const filter = columnConfig.filter as FilterConfigObject
151
- return filter!.operator === 'search'
152
- })
153
-
154
- const grist = this.closest('ox-grist') as DataGrist
155
-
156
- this.value = (grist?.filters || []).map(filter => {
157
- return {
158
- ...filter,
159
- value: this.buildDefaultValue(filter!.operator, filter!.value)
160
- }
161
- })
162
-
163
- this.empty = (this.searchColumns.length === 0 || this.withoutSearch) && this.filterColumns.length === 0
164
-
165
- this.autoUpdateTargetsOnChange = {}
166
- this.filterColumns
167
- ?.filter(({ filter }) => {
168
- return typeof filter == 'object' && filter.boundTo && filter.boundTo.length > 0
169
- })
170
- .map(({ name, filter }) => {
171
- const boundTo = (filter as FilterConfigObject).boundTo
172
-
173
- boundTo!.forEach(to => {
174
- const origin = this.autoUpdateTargetsOnChange[to] || []
175
- if (name && !origin.includes(name)) {
176
- this.autoUpdateTargetsOnChange[to] = [...origin, name]
177
- }
178
- })
179
- })
149
+ async updated(changes: PropertyValues<this>) {
150
+ if (changes.has('personalConfigProvider') && this.personalConfigProvider) {
151
+ this.personalConfig = await this.personalConfigProvider.load()
152
+ } else if (changes.has('config') || changes.has('personalConfig')) {
153
+ this.applyUpdatedConfiguration()
180
154
  }
181
155
  }
182
156
 
@@ -197,93 +171,189 @@ export class OxFiltersForm extends LitElement {
197
171
  grist && grist.fetch()
198
172
  }}
199
173
  >
200
- ${this.searchColumns.length === 0 || this.withoutSearch
201
- ? html``
202
- : html`
203
- <ox-input-search name="search" .value=${searchValue} ?autofocus=${this.autofocus}></ox-input-search>
204
- `}
205
- ${this.filterColumns.map((column: ColumnConfig) => {
206
- const { name, header, label, filter } = column
207
-
208
- const type = (filter as FilterConfigObject).type
209
- const operator = (filter as FilterConfigObject).operator
210
- const filterLabel = (filter as FilterConfigObject).label
211
-
212
- const labelText =
213
- filterLabel !== undefined
214
- ? filterLabel
215
- : typeof label === 'object' && label.renderer
216
- ? label.renderer(column)
217
- : header.renderer(column) || name
218
-
219
- const idx = operator === 'between' ? 1 : 0
220
- const renderer = getFilterRenderer(
221
- operator === 'like' || operator === 'i_like' || operator === 'i_nlike' || operator === 'nlike'
222
- ? 'text'
223
- : type
224
- )[idx]
225
- const value =
226
- this.value?.find(filter => filter.name == name)?.value ??
227
- this.buildDefaultValue(operator!, (filter as FilterConfigObject)?.value)
228
-
229
- if (!renderer) {
230
- return html``
231
- }
232
-
233
- return type === 'boolean' || type === 'checkbox'
234
- ? renderer(column, value, this)
235
- : type !== 'select' && labelText
236
- ? html`<label filter-title ?between=${operator === 'between'}
237
- ><span>${labelText}</span> ${renderer(column, value, this)}
238
- </label> `
239
- : type !== 'select' && !labelText
240
- ? renderer(column, value, this)
241
- : operator === 'in'
242
- ? html`
243
- <ox-select
244
- name=${name}
245
- placeholder=${labelText}
246
- .value=${value}
247
- @change=${(e: CustomEvent) =>
248
- e.target?.dispatchEvent(
249
- new CustomEvent('filter-change', {
250
- detail: {
251
- name,
252
- operator,
253
- value: e.detail
254
- }
255
- })
256
- )}
257
- >
258
- <ox-popup-list multiple attr-selected="checked" with-search>
259
- ${renderer(column, value, this)}
260
- </ox-popup-list>
261
- </ox-select>
262
- `
263
- : html`
264
- <ox-select
265
- name=${name}
266
- placeholder=${labelText}
267
- .value=${value}
268
- @change=${(e: CustomEvent) =>
269
- e.target?.dispatchEvent(
270
- new CustomEvent('filter-change', {
271
- detail: {
272
- name,
273
- operator,
274
- value: e.detail
275
- }
276
- })
277
- )}
278
- >
279
- <ox-popup-list with-search> ${renderer(column, value, this)} </ox-popup-list>
280
- </ox-select>
281
- `
282
- })}
174
+ ${this.filterColumns
175
+ .filter(column => !(column.filter as FilterConfigObject).hidden)
176
+ .map((column: ColumnConfig) => {
177
+ const { name, header, label, filter } = column
178
+
179
+ const type = (filter as FilterConfigObject).type
180
+
181
+ if (type == 'search') {
182
+ return html`
183
+ <ox-input-search name="search" .value=${searchValue} ?autofocus=${this.autofocus}></ox-input-search>
184
+ `
185
+ }
186
+
187
+ const operator = (filter as FilterConfigObject).operator
188
+ const filterLabel = (filter as FilterConfigObject).label
189
+
190
+ const labelText =
191
+ filterLabel !== undefined
192
+ ? filterLabel
193
+ : typeof label === 'object' && label.renderer
194
+ ? label.renderer(column)
195
+ : header.renderer(column) || name
196
+
197
+ const idx = operator === 'between' ? 1 : 0
198
+ const renderer = getFilterRenderer(
199
+ operator === 'like' || operator === 'i_like' || operator === 'i_nlike' || operator === 'nlike'
200
+ ? 'text'
201
+ : type
202
+ )[idx]
203
+ const value =
204
+ this.value?.find(filter => filter.name == name)?.value ??
205
+ this.buildDefaultValue(operator!, (filter as FilterConfigObject)?.value)
206
+
207
+ if (!renderer) {
208
+ return html``
209
+ }
210
+
211
+ return type === 'boolean' || type === 'checkbox'
212
+ ? renderer(column, value, this)
213
+ : type !== 'select' && labelText
214
+ ? html`<label filter-title ?between=${operator === 'between'}
215
+ ><span>${labelText}</span> ${renderer(column, value, this)}
216
+ </label> `
217
+ : type !== 'select' && !labelText
218
+ ? renderer(column, value, this)
219
+ : operator === 'in'
220
+ ? html`
221
+ <ox-select
222
+ name=${name}
223
+ placeholder=${labelText}
224
+ .value=${value}
225
+ @change=${(e: CustomEvent) =>
226
+ e.target?.dispatchEvent(
227
+ new CustomEvent('filter-change', {
228
+ detail: {
229
+ name,
230
+ operator,
231
+ value: e.detail
232
+ }
233
+ })
234
+ )}
235
+ >
236
+ <ox-popup-list multiple attr-selected="checked" with-search>
237
+ ${renderer(column, value, this)}
238
+ </ox-popup-list>
239
+ </ox-select>
240
+ `
241
+ : html`
242
+ <ox-select
243
+ name=${name}
244
+ placeholder=${labelText}
245
+ .value=${value}
246
+ @change=${(e: CustomEvent) =>
247
+ e.target?.dispatchEvent(
248
+ new CustomEvent('filter-change', {
249
+ detail: {
250
+ name,
251
+ operator,
252
+ value: e.detail
253
+ }
254
+ })
255
+ )}
256
+ >
257
+ <ox-popup-list with-search> ${renderer(column, value, this)} </ox-popup-list>
258
+ </ox-select>
259
+ `
260
+ })}
283
261
  </form>
262
+ <slot name="setting"></slot>
284
263
  `
285
264
  }
286
265
 
266
+ applyUpdatedConfiguration() {
267
+ const filters = this.config.columns.filter(columnConfig => !!columnConfig.filter)
268
+ this.filterColumns = filters.filter((columnConfig: ColumnConfig) => {
269
+ const filter = columnConfig.filter as FilterConfigObject
270
+ return filter!.operator !== 'search'
271
+ })
272
+ this.searchColumns = filters.filter(columnConfig => {
273
+ const filter = columnConfig.filter as FilterConfigObject
274
+ return filter!.operator === 'search'
275
+ })
276
+
277
+ if (this.searchColumns.length > 0) {
278
+ this.filterColumns.unshift({ name: 'search', filter: { type: 'search' } } as any)
279
+ }
280
+
281
+ if (!this.personalConfig) {
282
+ this.personalFilters = this.filterColumns.map(column => {
283
+ return { name: column.name }
284
+ })
285
+ } else {
286
+ const { filters: personalFilters = [] } = this.personalConfig
287
+
288
+ if (personalFilters) {
289
+ const xfilters = this.filterColumns.map(column => {
290
+ return personalFilters.find(pFilter => pFilter.name == column.name) || { name: column.name }
291
+ })
292
+
293
+ function reorderList(a: FilterPreference[], b: FilterPreference[]): FilterPreference[] {
294
+ // 결과 배열 초기화, a 배열 길이만큼 undefined로 채움
295
+ const result = new Array(a.length)
296
+
297
+ // b 배열에 없는 아이템은 원래 위치로 채움
298
+ a.forEach((item, index) => {
299
+ if (!item.name || !b.find(bi => bi.name == item.name)) {
300
+ result[index] = item
301
+ }
302
+ })
303
+
304
+ b.forEach(item => {
305
+ const ai = a.find(ai => ai.name == item.name)
306
+ if (ai) {
307
+ result[result.findIndex(slot => slot === undefined)] = ai
308
+ }
309
+ })
310
+
311
+ return result
312
+ }
313
+
314
+ // 배열 재정렬 실행
315
+ this.personalFilters = reorderList(xfilters as any, personalFilters as any) as FilterPreference[]
316
+
317
+ this.filterColumns = this.personalFilters
318
+ .map(filter => {
319
+ const column = this.filterColumns.find(column => column.name == filter.name)
320
+ if (column?.filter) {
321
+ ;(column.filter as FilterConfigObject)!.hidden = filter.hidden
322
+ }
323
+ return column
324
+ })
325
+ .filter(Boolean) as ColumnConfig[]
326
+ }
327
+ }
328
+
329
+ const grist = this.closest('ox-grist') as DataGrist
330
+
331
+ this.value = (grist?.filters || []).map(filter => {
332
+ return {
333
+ ...filter,
334
+ value: this.buildDefaultValue(filter!.operator, filter!.value)
335
+ }
336
+ })
337
+
338
+ this.empty = (this.searchColumns.length === 0 || this.withoutSearch) && this.filterColumns.length === 0
339
+
340
+ this.autoUpdateTargetsOnChange = {}
341
+ this.filterColumns
342
+ ?.filter(({ filter }) => {
343
+ return typeof filter == 'object' && filter.boundTo && filter.boundTo.length > 0
344
+ })
345
+ .map(({ name, filter }) => {
346
+ const boundTo = (filter as FilterConfigObject).boundTo
347
+
348
+ boundTo!.forEach(to => {
349
+ const origin = this.autoUpdateTargetsOnChange[to] || []
350
+ if (name && !origin.includes(name)) {
351
+ this.autoUpdateTargetsOnChange[to] = [...origin, name]
352
+ }
353
+ })
354
+ })
355
+ }
356
+
287
357
  async getQueryFilters(): Promise<QueryFilter[]> {
288
358
  const form = await this.form
289
359
  if (!form) return []
@@ -292,6 +362,7 @@ export class OxFiltersForm extends LitElement {
292
362
  const search: string | undefined = formData.get('search')?.toString()
293
363
 
294
364
  var filters = this.filterColumns
365
+ .filter(column => column.name !== 'search' && !(column.filter as FilterConfigObject)!.hidden)
295
366
  .map((column: ColumnConfig) => {
296
367
  const { name, type, filter } = column
297
368
  const operator = (filter as FilterConfigObject).operator