@operato/data-grist 2.0.0-alpha.132 → 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 (32) hide show
  1. package/CHANGELOG.md +25 -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.d.ts +5 -6
  5. package/dist/src/data-grist.js +53 -32
  6. package/dist/src/data-grist.js.map +1 -1
  7. package/dist/src/filters/filter-styles.js +21 -11
  8. package/dist/src/filters/filter-styles.js.map +1 -1
  9. package/dist/src/filters/filters-form.d.ts +8 -2
  10. package/dist/src/filters/filters-form.js +128 -62
  11. package/dist/src/filters/filters-form.js.map +1 -1
  12. package/dist/src/personalizer/ox-grist-filter-personalizer.d.ts +8 -0
  13. package/dist/src/personalizer/ox-grist-filter-personalizer.js +177 -0
  14. package/dist/src/personalizer/ox-grist-filter-personalizer.js.map +1 -0
  15. package/dist/src/personalizer/ox-grist-personalizer.d.ts +0 -2
  16. package/dist/src/personalizer/ox-grist-personalizer.js +15 -9
  17. package/dist/src/personalizer/ox-grist-personalizer.js.map +1 -1
  18. package/dist/src/types.d.ts +11 -0
  19. package/dist/src/types.js.map +1 -1
  20. package/dist/stories/grid-setting.stories.d.ts +1 -0
  21. package/dist/stories/grid-setting.stories.js +21 -25
  22. package/dist/stories/grid-setting.stories.js.map +1 -1
  23. package/dist/tsconfig.tsbuildinfo +1 -1
  24. package/package.json +6 -5
  25. package/src/data-grid/data-grid-header.ts +1 -1
  26. package/src/data-grist.ts +58 -36
  27. package/src/filters/filter-styles.ts +21 -11
  28. package/src/filters/filters-form.ts +196 -125
  29. package/src/personalizer/ox-grist-filter-personalizer.ts +191 -0
  30. package/src/personalizer/ox-grist-personalizer.ts +15 -5
  31. package/src/types.ts +12 -0
  32. package/stories/grid-setting.stories.ts +22 -25
@@ -0,0 +1,191 @@
1
+ import '@material/web/button/outlined-button.js'
2
+
3
+ import { css, html, LitElement } from 'lit'
4
+ import { customElement, property, state } from 'lit/decorators.js'
5
+
6
+ import { i18next } from '@operato/i18n'
7
+ import { OxPopupList } from '@operato/popup'
8
+
9
+ import { FilterConfigObject, FilterPreference, PersonalGristPreference } from '../types.js'
10
+
11
+ import { OxFiltersForm } from '../filters/filters-form'
12
+
13
+ @customElement('ox-grist-filter-personalizer')
14
+ export class OxGristFilterPersonalizer extends LitElement {
15
+ static styles = [
16
+ css`
17
+ md-icon {
18
+ --md-icon-size: 16px;
19
+ width: 36px;
20
+ height: 36px;
21
+ color: var(--md-sys-color-secondary-container);
22
+ cursor: pointer;
23
+
24
+ display: flex;
25
+ place-content: center;
26
+ place-items: center;
27
+ position: relative;
28
+
29
+ &:hover {
30
+ color: var(--md-sys-color-primary-container);
31
+ }
32
+ }
33
+
34
+ md-ripple {
35
+ border-radius: 50%;
36
+ inset: unset;
37
+ height: 36px;
38
+ width: 36px;
39
+ }
40
+ `
41
+ ]
42
+
43
+ @property({ type: Boolean, attribute: true }) debug: boolean = false
44
+
45
+ @state() private preference?: PersonalGristPreference
46
+
47
+ render() {
48
+ return html`
49
+ <md-icon
50
+ @click=${async (e: MouseEvent) => {
51
+ const form = this.closest('ox-filters-form') as OxFiltersForm
52
+ const { filterColumns, personalFilters = [] } = form
53
+ const queryFilters = await form.getQueryFilters()
54
+
55
+ this.preference = {
56
+ filters: (
57
+ personalFilters.map((filter: FilterPreference) => {
58
+ const originFilterColumn =
59
+ filter.name == 'search'
60
+ ? { filter: { name: 'search' } }
61
+ : filterColumns.find(f => f.name == filter.name)
62
+
63
+ if (!originFilterColumn) {
64
+ /* 원래 filters 설정에 있는 것들 만을 유지한다. */
65
+ return
66
+ }
67
+
68
+ const { value } = originFilterColumn.filter! as FilterConfigObject
69
+
70
+ return {
71
+ name: filter.name,
72
+ hidden: filter.hidden,
73
+ /* 만약, filters에 기본값이 이미 설정되어 있다면, 그대로 유지한다. */
74
+ value: value ?? queryFilters.find(f => f.name == filter.name)?.value
75
+ }
76
+ }) || []
77
+ ).filter(Boolean) as FilterPreference[]
78
+ }
79
+
80
+ const template = html`
81
+ <div class="personalizer-header" slot="header">
82
+ <md-icon
83
+ style="margin-left: auto;"
84
+ @click=${async (e: MouseEvent) => {
85
+ if (form.personalConfigProvider) {
86
+ form.personalConfig = await form.personalConfigProvider.save(this.preference)
87
+ }
88
+ popup.close()
89
+ }}
90
+ title=${String(i18next.t('button.save'))}
91
+ >keep</md-icon
92
+ ><md-icon
93
+ @click=${async (e: MouseEvent) => {
94
+ if (form.personalConfigProvider) {
95
+ form.personalConfig = this.preference = {}
96
+ await form.personalConfigProvider.reset()
97
+ }
98
+ popup.close()
99
+ }}
100
+ title=${String(i18next.t('button.delete'))}
101
+ >keep_off</md-icon
102
+ ><md-icon @click=${async (e: MouseEvent) => popup.close()} title=${String(i18next.t('button.close'))}
103
+ >close</md-icon
104
+ >
105
+ </div>
106
+
107
+ ${this.preference?.filters!.map(
108
+ filter => html`
109
+ <ox-checkbox label="checkbox" ?checked=${!filter.hidden} value=${filter.name} option
110
+ >${filter.name}<span style="position: absolute; right: 10px; cursor: move;" handle
111
+ >☰</span
112
+ ></ox-checkbox
113
+ >
114
+ `
115
+ )}
116
+ `
117
+
118
+ const popup = OxPopupList.open({
119
+ template,
120
+ multiple: true,
121
+ sortable: true,
122
+ debug: this.debug,
123
+ attrSelected: 'checked',
124
+ top: e.pageY,
125
+ left: e.pageX,
126
+ styles: css`
127
+ :host {
128
+ width: 240px;
129
+ max-height: 300px;
130
+ overflow: auto;
131
+ }
132
+
133
+ ::slotted(.personalizer-header) {
134
+ --md-icon-size: 1.4em;
135
+
136
+ display: flex;
137
+ flex-direction: row;
138
+ align-items: center;
139
+ text-transform: capitalize;
140
+ box-shadow: 0 3px 3px rgba(0, 0, 0, 0.3);
141
+ }
142
+
143
+ ::slotted([option]) {
144
+ position: relative;
145
+ user-select: none;
146
+ }
147
+ `
148
+ })
149
+
150
+ popup.onselect = (e: Event) => {
151
+ const selected = (e as CustomEvent).detail
152
+
153
+ const pconfig: PersonalGristPreference = { ...form.personalConfig }
154
+ const pfilters = this.preference?.filters!
155
+
156
+ pconfig.filters = pfilters.map(filter => {
157
+ return {
158
+ name: filter.name,
159
+ hidden: selected.indexOf(filter.name) == -1,
160
+ value: filter.value
161
+ }
162
+ })
163
+
164
+ form.personalConfig = this.preference = pconfig
165
+
166
+ form.applyUpdatedConfiguration()
167
+ }
168
+
169
+ popup.addEventListener('sorted', (e: Event) => {
170
+ const sorted = (e as CustomEvent).detail as HTMLElement[]
171
+
172
+ const pconfig: PersonalGristPreference = { ...form.personalConfig }
173
+ const pfilters = this.preference?.filters!
174
+
175
+ pconfig.filters = sorted
176
+ .map(element => {
177
+ const name = (element as HTMLInputElement).value
178
+ return pfilters.find(filter => filter.name == name)!
179
+ })
180
+ .filter(Boolean)
181
+
182
+ form.personalConfig = this.preference = pconfig
183
+
184
+ form.applyUpdatedConfiguration()
185
+ })
186
+ }}
187
+ >settings<md-ripple></md-ripple
188
+ ></md-icon>
189
+ `
190
+ }
191
+ }
@@ -30,8 +30,6 @@ export class OxGristPersonalizer extends LitElement {
30
30
  `
31
31
  ]
32
32
 
33
- @property({ type: String }) page!: string
34
- @property({ type: String }) element!: string
35
33
  @property({ type: Boolean, attribute: true }) debug: boolean = false
36
34
 
37
35
  @state() private preference?: PersonalGristPreference
@@ -42,7 +40,7 @@ export class OxGristPersonalizer extends LitElement {
42
40
  @click=${(e: MouseEvent) => {
43
41
  const grist = this.closest('ox-grist') as DataGrist
44
42
 
45
- const { config, compiledConfig } = grist
43
+ const { config, compiledConfig, sorters, pagination, mode } = grist
46
44
  const { columns: compiledColumns } = compiledConfig
47
45
 
48
46
  const columns = compiledColumns
@@ -59,7 +57,13 @@ export class OxGristPersonalizer extends LitElement {
59
57
  hidden: column.hidden,
60
58
  width: column.width
61
59
  }
62
- })
60
+ }),
61
+ sorters,
62
+ pagination: {
63
+ ...pagination,
64
+ limit: grist.getCurrentLimit()
65
+ },
66
+ mode
63
67
  }
64
68
 
65
69
  const template = html`
@@ -68,7 +72,13 @@ export class OxGristPersonalizer extends LitElement {
68
72
  style="margin-left: auto;"
69
73
  @click=${async (e: MouseEvent) => {
70
74
  if (grist.personalConfigProvider) {
71
- grist.personalConfig = this.preference = await grist.personalConfigProvider.save(this.preference)
75
+ const { mode, columns, sorters, pagination } = this.preference || {}
76
+ grist.personalConfig = this.preference = await grist.personalConfigProvider.save({
77
+ mode,
78
+ columns,
79
+ sorters,
80
+ pagination
81
+ })
72
82
  }
73
83
  popup.close()
74
84
  }}
package/src/types.ts CHANGED
@@ -106,6 +106,7 @@ export type FilterChangedCallback = (value: any, form: OxFiltersForm) => boolean
106
106
  * @property {string} type - The type of the filter condition.
107
107
  * @property {FilterOperator} [operator] - The filter operator used to compare values (optional).
108
108
  * @property {Object} [options] - Additional options or parameters for the filter condition (optional).
109
+ * @property {boolean|undefined} [hidden] - The hidden flag for the filter condition (optional).
109
110
  * @property {string|number|boolean|string[]|number[]|undefined} [value] - The value to compare with in the filter condition (optional).
110
111
  * @property {string} [label] - The label to display for the filter condition (optional).
111
112
  */
@@ -116,6 +117,7 @@ export type FilterConfigObject = {
116
117
  value?: string | number | boolean | string[] | number[] | undefined
117
118
  label?: string
118
119
  boundTo?: string[]
120
+ hidden?: boolean
119
121
  onchange?: FilterChangedCallback
120
122
  }
121
123
 
@@ -775,5 +777,15 @@ export type GristSelectFunction = (record: GristRecord) => boolean
775
777
  */
776
778
  export type PersonalGristPreference = {
777
779
  columns?: Partial<ColumnConfig>[]
780
+ filters?: FilterPreference[]
781
+ pagination?: PaginationConfig
782
+ sorters?: SortersConfig
783
+ mode?: 'GRID' | 'LIST' | 'CARD'
778
784
  [key: string]: any
779
785
  }
786
+
787
+ export type FilterPreference = {
788
+ name: string
789
+ hidden?: boolean
790
+ value?: any
791
+ }
@@ -8,6 +8,7 @@ import '../src/filters/filters-form.js'
8
8
  import '../src/sorters/sorters-control.js'
9
9
  import '../src/record-view/record-creator.js'
10
10
  import '../src/personalizer/ox-grist-personalizer.js'
11
+ import '../src/personalizer/ox-grist-filter-personalizer.js'
11
12
 
12
13
  import { html, TemplateResult } from 'lit'
13
14
 
@@ -364,6 +365,17 @@ interface ArgTypes {
364
365
  debug: boolean
365
366
  }
366
367
 
368
+ var personalConfig: PersonalGristPreference = {
369
+ columns: [
370
+ { name: 'name', hidden: false, width: 200 },
371
+ { name: 'description', hidden: true }
372
+ ],
373
+ pagination: {
374
+ pages: [20, 30, 50, 100, 200],
375
+ limit: 30
376
+ }
377
+ }
378
+
367
379
  const Template: Story<ArgTypes> = ({
368
380
  config,
369
381
  mode = 'GRID',
@@ -399,19 +411,19 @@ const Template: Story<ArgTypes> = ({
399
411
  .personalConfigProvider=${{
400
412
  async load() {
401
413
  await sleep(1000)
402
- return {
403
- columns: [
404
- { name: 'name', hidden: false, width: 200 },
405
- { name: 'description', hidden: true }
406
- ]
407
- }
414
+ return personalConfig
408
415
  },
409
416
  async save(preference: PersonalGristPreference) {
410
417
  await sleep(1000)
411
- console.log('saving preference', preference)
418
+ personalConfig = {
419
+ ...personalConfig,
420
+ ...preference
421
+ }
422
+ console.log('saving preference', personalConfig)
412
423
  return preference
413
424
  },
414
425
  async reset() {
426
+ personalConfig = {}
415
427
  await sleep(1000)
416
428
  }
417
429
  }}
@@ -420,24 +432,9 @@ const Template: Story<ArgTypes> = ({
420
432
  >
421
433
  <div slot="headroom">
422
434
  <div id="filters">
423
- <ox-filters-form autofocus></ox-filters-form>
424
- </div>
425
-
426
- <div id="sorters">
427
- Sort
428
- <md-icon
429
- @click=${(e: Event) => {
430
- const target = e.currentTarget as HTMLElement
431
- ;(target.closest('#sorters')!.querySelector('#sorter-control') as any).open({
432
- right: 0,
433
- top: target.offsetTop + target.offsetHeight
434
- })
435
- }}
436
- >expand_more</md-icon
437
- >
438
- <ox-popup id="sorter-control">
439
- <ox-sorters-control> </ox-sorters-control>
440
- </ox-popup>
435
+ <ox-filters-form autofocus>
436
+ <ox-grist-filter-personalizer slot="setting"></ox-grist-filter-personalizer>
437
+ </ox-filters-form>
441
438
  </div>
442
439
 
443
440
  <div id="modes">