@operato/data-grist 2.0.0-alpha.102 → 2.0.0-alpha.104

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 (40) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/src/editors/ox-grist-editor-select.js +37 -25
  3. package/dist/src/editors/ox-grist-editor-select.js.map +1 -1
  4. package/dist/src/filters/filter-select.js +30 -16
  5. package/dist/src/filters/filter-select.js.map +1 -1
  6. package/dist/src/filters/filters-form.d.ts +6 -2
  7. package/dist/src/filters/filters-form.js +74 -21
  8. package/dist/src/filters/filters-form.js.map +1 -1
  9. package/dist/src/renderers/ox-grist-renderer-select.js +34 -4
  10. package/dist/src/renderers/ox-grist-renderer-select.js.map +1 -1
  11. package/dist/src/types.d.ts +14 -1
  12. package/dist/src/types.js.map +1 -1
  13. package/dist/stories/bounded-select-filters.stories.d.ts +25 -0
  14. package/dist/stories/bounded-select-filters.stories.js +264 -0
  15. package/dist/stories/bounded-select-filters.stories.js.map +1 -0
  16. package/dist/stories/bounded-select-record.stories.d.ts +25 -0
  17. package/dist/stories/bounded-select-record.stories.js +267 -0
  18. package/dist/stories/bounded-select-record.stories.js.map +1 -0
  19. package/dist/stories/default-filters.stories.js +43 -0
  20. package/dist/stories/default-filters.stories.js.map +1 -1
  21. package/dist/tsconfig.tsbuildinfo +1 -1
  22. package/docs/default-value/value-generator/date-generator.md +29 -0
  23. package/docs/default-value/value-generator/hour-time-generator.md +33 -0
  24. package/docs/default-value/value-generator/minute-time-generator.md +33 -0
  25. package/docs/default-value/value-generator/month-date-generator.md +2 -0
  26. package/docs/default-value/value-generator/now-generator.md +29 -0
  27. package/docs/default-value/value-generator/time-generator.md +31 -0
  28. package/docs/default-value/value-generator/today-generator.md +29 -0
  29. package/docs/default-value/value-generator/week-date-generator.md +31 -0
  30. package/docs/default-value/value-generator/year-date-generator.md +31 -0
  31. package/package.json +3 -3
  32. package/src/editors/ox-grist-editor-select.ts +41 -28
  33. package/src/filters/filter-select.ts +41 -28
  34. package/src/filters/filters-form.ts +74 -10
  35. package/src/renderers/ox-grist-renderer-select.ts +41 -6
  36. package/src/types.ts +19 -1
  37. package/stories/bounded-select-filters.stories.ts +313 -0
  38. package/stories/bounded-select-record.stories.ts +316 -0
  39. package/stories/default-filters.stories.ts +43 -0
  40. package/yarn-error.log +0 -16971
@@ -1,11 +1,41 @@
1
1
  import { html } from 'lit'
2
+ import { until } from 'lit/directives/until.js'
2
3
 
3
- import { FieldRenderer } from '../types'
4
+ import { FieldRenderer, SelectOption, SelectOptionObject } from '../types'
5
+
6
+ function buildOptions(options: SelectOption[], value: any) {
7
+ const selectOptionObjects = options.map(option => {
8
+ switch (typeof option) {
9
+ case 'string':
10
+ return {
11
+ display: option,
12
+ value: option
13
+ }
14
+ case 'object':
15
+ return {
16
+ display: option.display || option.name,
17
+ value: option.value
18
+ }
19
+ default:
20
+ return option
21
+ }
22
+ }) as SelectOptionObject[]
23
+
24
+ var res = selectOptionObjects
25
+ ? selectOptionObjects.filter((option: any) => option.value == String(value == null ? '' : value))
26
+ : []
27
+
28
+ if (res.length) {
29
+ return html`<span>${res[0].display || res[0].name || ''}</span>`
30
+ }
31
+ return html`<span>${value}</span>`
32
+ }
4
33
 
5
34
  export const OxGristRendererSelect: FieldRenderer = (value, column, record, rowIndex, field) => {
6
35
  if (value == null) {
7
36
  return ''
8
37
  }
38
+
9
39
  var rowOptionField = column.record.rowOptionField && record[column.record.rowOptionField]
10
40
  var options = rowOptionField?.options ? rowOptionField.options : column.record.options
11
41
 
@@ -13,11 +43,16 @@ export const OxGristRendererSelect: FieldRenderer = (value, column, record, rowI
13
43
  console.error(`options value for select '${column.name}' column is mandatory.`)
14
44
  } else if (typeof options == 'function') {
15
45
  options = options.call(null, value, column, record, rowIndex, field)
16
- }
17
46
 
18
- var res = options ? options.filter((option: any) => option.value == String(value == null ? '' : value)) : []
19
- if (res.length) {
20
- return html`<span>${res[0].display}</span>`
47
+ if (options instanceof Promise) {
48
+ return html`${until(
49
+ options.then(options => buildOptions(options, value)),
50
+ value
51
+ )}`
52
+ } else {
53
+ return buildOptions((options || []) as SelectOption[], value)
54
+ }
55
+ } else {
56
+ return buildOptions((options || []) as SelectOption[], value)
21
57
  }
22
- return html`<span>${value}</span>`
23
58
  }
package/src/types.ts CHANGED
@@ -9,7 +9,7 @@ import { DataListGutter } from './data-list/data-list-gutter'
9
9
  import { RecordPartial } from './data-list/record-partial'
10
10
  import { DataReportField } from './data-report/data-report-field'
11
11
  import { OxGristEditor } from './editors'
12
- import { QueryFilter } from './filters'
12
+ import { QueryFilter, OxFiltersForm } from './filters'
13
13
  import { OxGristRenderer } from './renderers/ox-grist-renderer'
14
14
 
15
15
  /**
@@ -83,6 +83,22 @@ export type FilterOperator =
83
83
  | 'i_nlike'
84
84
  | 'nlike'
85
85
 
86
+ export type SelectOptionObject =
87
+ | {
88
+ name: string
89
+ display?: never
90
+ value: string
91
+ }
92
+ | {
93
+ display: string
94
+ name?: never
95
+ value: string
96
+ }
97
+
98
+ export type SelectOption = SelectOptionObject | string
99
+
100
+ export type FilterChangedCallback = (value: any, form: OxFiltersForm) => boolean
101
+
86
102
  /**
87
103
  * Configuration object for specifying filter conditions.
88
104
  *
@@ -99,6 +115,8 @@ export type FilterConfigObject = {
99
115
  options?: { [key: string]: any }
100
116
  value?: string | number | boolean | string[] | number[] | undefined
101
117
  label?: string
118
+ boundTo?: string[]
119
+ onchange?: FilterChangedCallback
102
120
  }
103
121
 
104
122
  /**
@@ -0,0 +1,313 @@
1
+ import '../src/index.js'
2
+ import '../src/filters/filters-form.js'
3
+ import '../src/sorters/sorters-control.js'
4
+ import '@operato/popup/ox-popup-list.js'
5
+ import '@material/web/icon/icon.js'
6
+
7
+ import { html, TemplateResult } from 'lit'
8
+ import { sleep } from '@operato/utils'
9
+
10
+ import { ColumnConfig, FetchHandler, GristRecord, SelectOption } from '../src/types.js'
11
+
12
+ import { CommonHeaderStyles, CommonGristStyles } from '@operato/styles'
13
+ import { DataGridField } from '../src/data-grid/data-grid-field.js'
14
+
15
+ const fetchHandler: FetchHandler = async ({ sorters = [], filters, page, limit }) => {
16
+ var total = 120993
17
+ var start = (page! - 1) * limit!
18
+
19
+ await new Promise(resolve => setTimeout(resolve, 500))
20
+
21
+ return {
22
+ total,
23
+ records: Array(limit! * page! > total ? total % limit! : limit)
24
+ .fill('')
25
+ .map((item, idx) => {
26
+ const warehouse = idx % 2 ? '01' : '02'
27
+ const zone = idx % 2 ? 'Z001' : 'Z002'
28
+
29
+ return {
30
+ id: String(idx),
31
+ name: idx % 2 ? `shnam-${start + idx + 1}` : `heartyoh-${start + idx + 1}`,
32
+ description: idx % 2 ? `hatiolabmanager${start + idx + 1}1234567890` : `hatiosea manager-${start + idx + 1}`,
33
+ warehouse,
34
+ zone,
35
+ location: `L${warehouse}-${zone}-0${(idx % 5) + 1}`,
36
+ createdAt: Date.now(),
37
+ updatedAt: Date.now()
38
+ }
39
+ })
40
+ }
41
+ }
42
+
43
+ function buildConfig({ headerFilter }: { headerFilter: boolean }) {
44
+ return {
45
+ list: {
46
+ fields: ['name', 'description'],
47
+ details: ['updatedAt', 'createdAt']
48
+ },
49
+ columns: [
50
+ {
51
+ type: 'gutter',
52
+ gutterName: 'sequence'
53
+ },
54
+ {
55
+ type: 'string',
56
+ name: 'id',
57
+ hidden: true
58
+ },
59
+ {
60
+ type: 'string',
61
+ name: 'name',
62
+ label: true,
63
+ header: 'name',
64
+ filter: {
65
+ operator: 'eq',
66
+ value: 'shnam'
67
+ },
68
+ sortable: true,
69
+ width: 120
70
+ },
71
+ {
72
+ type: 'string',
73
+ name: 'description',
74
+ header: 'description',
75
+ record: {
76
+ align: 'left'
77
+ },
78
+ width: 200
79
+ },
80
+ {
81
+ type: 'select',
82
+ name: 'warehouse',
83
+ header: 'warehouse',
84
+ filter: {
85
+ operator: 'eq'
86
+ },
87
+ record: {
88
+ align: 'left',
89
+ options: async (
90
+ value: any,
91
+ column: ColumnConfig,
92
+ record: GristRecord,
93
+ rowIndex: number,
94
+ field: DataGridField
95
+ ): Promise<SelectOption[]> => {
96
+ await sleep(1000) // 테스트를 위해서 강제로 1초 쉬기
97
+ return [
98
+ {
99
+ display: '',
100
+ value: ''
101
+ }
102
+ ].concat(
103
+ WAREHOUSE.map(w => {
104
+ return {
105
+ display: w.name,
106
+ value: w.id
107
+ }
108
+ })
109
+ )
110
+ }
111
+ },
112
+ width: 200
113
+ },
114
+ {
115
+ type: 'select',
116
+ name: 'zone',
117
+ header: 'zone',
118
+ filter: {
119
+ operator: 'eq',
120
+ boundTo: ['warehouse'],
121
+ onchange: () => {
122
+ console.log('warehousde filter value changed.')
123
+ }
124
+ },
125
+ record: {
126
+ align: 'left',
127
+ options: async (
128
+ value: any,
129
+ column: ColumnConfig,
130
+ record: GristRecord,
131
+ rowIndex: number,
132
+ field: DataGridField
133
+ ): Promise<SelectOption[]> => {
134
+ console.log('arguments', arguments)
135
+ await sleep(1000) // 테스트를 위해서 강제로 1초 쉬기
136
+ const warehouse = record.warehouse
137
+ console.log('warehouse', warehouse)
138
+
139
+ const targetWH = warehouse && WAREHOUSE.find(w => w.id == warehouse)
140
+ const zones = targetWH
141
+ ? targetWH.zones
142
+ : WAREHOUSE.reduce((sum, warehouse) => {
143
+ sum = sum.concat(warehouse.zones)
144
+ return sum
145
+ }, [] as any[])
146
+
147
+ return [
148
+ {
149
+ display: '',
150
+ value: ''
151
+ }
152
+ ].concat(
153
+ zones.map((z: any) => {
154
+ return {
155
+ display: z.name,
156
+ value: z.id
157
+ }
158
+ })
159
+ )
160
+ }
161
+ },
162
+ width: 200
163
+ },
164
+ {
165
+ type: 'select',
166
+ name: 'location',
167
+ header: 'location',
168
+ filter: {
169
+ operator: 'eq',
170
+ boundTo: ['warehouse', 'zone']
171
+ },
172
+ record: {
173
+ align: 'left',
174
+ options: async (
175
+ value: any,
176
+ column: ColumnConfig,
177
+ record: GristRecord,
178
+ rowIndex: number,
179
+ field: DataGridField
180
+ ): Promise<SelectOption[]> => {
181
+ await sleep(1000) // 테스트를 위해서 강제로 1초 쉬기
182
+ const warehouse = record.warehouse
183
+ const zone = record.zone
184
+ console.log('warehouse, zone', warehouse, zone)
185
+
186
+ const targetWH = (warehouse && WAREHOUSE.find(w => w.id == warehouse)) || null
187
+ const targetZone = zone && targetWH?.zones.find((z: any) => z.id == zone)
188
+ return targetZone ? ['', ...targetZone.locations] : []
189
+ }
190
+ },
191
+ width: 200
192
+ },
193
+ {
194
+ type: 'datetime',
195
+ name: 'updatedAt',
196
+ header: 'updated at',
197
+ width: 180
198
+ },
199
+ {
200
+ type: 'datetime',
201
+ name: 'createdAt',
202
+ header: 'created at',
203
+ width: 180
204
+ }
205
+ ],
206
+ rows: {},
207
+ sorters: [
208
+ {
209
+ name: 'name',
210
+ desc: true
211
+ }
212
+ ],
213
+ filters: {
214
+ header: headerFilter
215
+ },
216
+ pagination: {
217
+ pages: [30, 50, 100, 200]
218
+ }
219
+ }
220
+ }
221
+
222
+ const WAREHOUSE = [
223
+ {
224
+ id: '01',
225
+ name: '제 1 창고',
226
+ zones: [
227
+ {
228
+ id: 'Z001',
229
+ name: 'Zone 01-001',
230
+ locations: ['L01-001-01', 'L01-001-02', 'L01-001-03', 'L01-001-04', 'L01-001-05']
231
+ },
232
+ {
233
+ id: 'Z002',
234
+ name: 'Zone 01-002',
235
+ locations: ['L01-002-01', 'L01-002-02', 'L01-002-03', 'L01-002-04', 'L01-002-05']
236
+ }
237
+ ]
238
+ },
239
+ {
240
+ id: '02',
241
+ name: '제 2 창고',
242
+ zones: [
243
+ {
244
+ id: 'Z001',
245
+ name: 'Zone 02-001',
246
+ locations: ['L02-001-01', 'L02-001-02', 'L02-001-03', 'L02-001-04', 'L02-001-05']
247
+ },
248
+ {
249
+ id: 'Z002',
250
+ name: 'Zone 02-002',
251
+ locations: ['L02-002-01', 'L02-002-02', 'L02-002-03', 'L02-002-04', 'L02-002-05']
252
+ }
253
+ ]
254
+ }
255
+ ]
256
+
257
+ export default {
258
+ title: 'bounded select filters for ox-grist',
259
+ component: 'ox-grist',
260
+ argTypes: {
261
+ headerFilter: { control: 'boolean' }
262
+ }
263
+ }
264
+
265
+ interface Story<T> {
266
+ (args: T): TemplateResult
267
+ args?: Partial<T>
268
+ argTypes?: Record<string, unknown>
269
+ }
270
+
271
+ interface ArgTypes {
272
+ headerFilter: boolean
273
+ }
274
+
275
+ const Template: Story<ArgTypes> = ({ headerFilter }: ArgTypes) =>
276
+ html` <link
277
+ href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL@20..48,100..700,0..1"
278
+ rel="stylesheet"
279
+ />
280
+ <link
281
+ href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL@20..48,100..700,0..1"
282
+ rel="stylesheet"
283
+ />
284
+ <link
285
+ href="https://fonts.googleapis.com/css2?family=Material+Symbols+Sharp:opsz,wght,FILL@20..48,100..700,0..1"
286
+ rel="stylesheet"
287
+ />
288
+ <link href="/themes/app-theme.css" rel="stylesheet" />
289
+ <link href="/themes/oops-theme.css" rel="stylesheet" />
290
+ <link href="/themes/grist-theme.css" rel="stylesheet" />
291
+
292
+ <style>
293
+ ${CommonGristStyles.cssText}
294
+ ${CommonHeaderStyles.cssText}
295
+ </style>
296
+
297
+ <ox-grist
298
+ .config=${buildConfig({ headerFilter })}
299
+ mode="GRID"
300
+ .fetchHandler=${fetchHandler}
301
+ @filters-change=${(e: Event) => console.log('filters', (e.target as any).filters)}
302
+ >
303
+ <div slot="headroom">
304
+ <div id="filters">
305
+ <ox-filters-form></ox-filters-form>
306
+ </div>
307
+ </div>
308
+ </ox-grist>`
309
+
310
+ export const Regular = Template.bind({})
311
+ Regular.args = {
312
+ headerFilter: true
313
+ }