@operato/data-grist 2.0.0-alpha.99 → 2.0.0-beta.1

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 (216) hide show
  1. package/CHANGELOG.md +441 -0
  2. package/demo/data-grist-test.html +1 -1
  3. package/demo/index.html +1 -1
  4. package/dist/src/data-card/data-card-field.js +2 -2
  5. package/dist/src/data-card/data-card-field.js.map +1 -1
  6. package/dist/src/data-card/data-card-gutter-menu.js +5 -5
  7. package/dist/src/data-card/data-card-gutter-menu.js.map +1 -1
  8. package/dist/src/data-card/data-card-gutter.js +6 -6
  9. package/dist/src/data-card/data-card-gutter.js.map +1 -1
  10. package/dist/src/data-card/data-card.js +7 -9
  11. package/dist/src/data-card/data-card.js.map +1 -1
  12. package/dist/src/data-card/record-card.js +9 -10
  13. package/dist/src/data-card/record-card.js.map +1 -1
  14. package/dist/src/data-grid/data-grid-accum-field.js +12 -5
  15. package/dist/src/data-grid/data-grid-accum-field.js.map +1 -1
  16. package/dist/src/data-grid/data-grid-body-style.js +12 -0
  17. package/dist/src/data-grid/data-grid-body-style.js.map +1 -1
  18. package/dist/src/data-grid/data-grid-body.d.ts +0 -1
  19. package/dist/src/data-grid/data-grid-body.js +14 -21
  20. package/dist/src/data-grid/data-grid-body.js.map +1 -1
  21. package/dist/src/data-grid/data-grid-field.js +8 -2
  22. package/dist/src/data-grid/data-grid-field.js.map +1 -1
  23. package/dist/src/data-grid/data-grid-footer.js +4 -2
  24. package/dist/src/data-grid/data-grid-footer.js.map +1 -1
  25. package/dist/src/data-grid/data-grid-header.js +9 -6
  26. package/dist/src/data-grid/data-grid-header.js.map +1 -1
  27. package/dist/src/data-grid/data-grid.js +23 -1
  28. package/dist/src/data-grid/data-grid.js.map +1 -1
  29. package/dist/src/data-grid/event-handlers/data-grid-body-click-handler.js +3 -0
  30. package/dist/src/data-grid/event-handlers/data-grid-body-click-handler.js.map +1 -1
  31. package/dist/src/data-grist.d.ts +10 -2
  32. package/dist/src/data-grist.js +71 -8
  33. package/dist/src/data-grist.js.map +1 -1
  34. package/dist/src/data-list/data-list-field.js +5 -5
  35. package/dist/src/data-list/data-list-field.js.map +1 -1
  36. package/dist/src/data-list/data-list-gutter.js +3 -3
  37. package/dist/src/data-list/data-list-gutter.js.map +1 -1
  38. package/dist/src/data-list/data-list.js +4 -4
  39. package/dist/src/data-list/data-list.js.map +1 -1
  40. package/dist/src/data-list/record-partial.js +9 -10
  41. package/dist/src/data-list/record-partial.js.map +1 -1
  42. package/dist/src/data-manipulator.d.ts +1 -1
  43. package/dist/src/data-manipulator.js +5 -5
  44. package/dist/src/data-manipulator.js.map +1 -1
  45. package/dist/src/data-report/data-report-field.js +2 -1
  46. package/dist/src/data-report/data-report-field.js.map +1 -1
  47. package/dist/src/data-report/data-report-header.js +2 -2
  48. package/dist/src/data-report/data-report-header.js.map +1 -1
  49. package/dist/src/editors/ox-grist-editor-select.js +37 -25
  50. package/dist/src/editors/ox-grist-editor-select.js.map +1 -1
  51. package/dist/src/editors/ox-input-tree.js +8 -8
  52. package/dist/src/editors/ox-input-tree.js.map +1 -1
  53. package/dist/src/filters/filter-input-barcode.js +1 -0
  54. package/dist/src/filters/filter-input-barcode.js.map +1 -1
  55. package/dist/src/filters/filter-select.js +30 -16
  56. package/dist/src/filters/filter-select.js.map +1 -1
  57. package/dist/src/filters/filter-styles.js +46 -31
  58. package/dist/src/filters/filter-styles.js.map +1 -1
  59. package/dist/src/filters/filters-form.d.ts +15 -4
  60. package/dist/src/filters/filters-form.js +205 -70
  61. package/dist/src/filters/filters-form.js.map +1 -1
  62. package/dist/src/gutters/gutter-dirty.js +2 -2
  63. package/dist/src/gutters/gutter-dirty.js.map +1 -1
  64. package/dist/src/index.d.ts +1 -0
  65. package/dist/src/index.js +1 -0
  66. package/dist/src/index.js.map +1 -1
  67. package/dist/src/personalizer/index.d.ts +1 -0
  68. package/dist/src/personalizer/index.js +2 -0
  69. package/dist/src/personalizer/index.js.map +1 -0
  70. package/dist/src/personalizer/ox-grist-filter-personalizer.d.ts +8 -0
  71. package/dist/src/personalizer/ox-grist-filter-personalizer.js +177 -0
  72. package/dist/src/personalizer/ox-grist-filter-personalizer.js.map +1 -0
  73. package/dist/src/personalizer/ox-grist-personalizer.d.ts +8 -0
  74. package/dist/src/personalizer/ox-grist-personalizer.js +178 -0
  75. package/dist/src/personalizer/ox-grist-personalizer.js.map +1 -0
  76. package/dist/src/record-view/record-creator.js +2 -2
  77. package/dist/src/record-view/record-creator.js.map +1 -1
  78. package/dist/src/renderers/ox-grist-renderer-select.js +34 -4
  79. package/dist/src/renderers/ox-grist-renderer-select.js.map +1 -1
  80. package/dist/src/renderers/ox-grist-renderer-tree.js +8 -8
  81. package/dist/src/renderers/ox-grist-renderer-tree.js.map +1 -1
  82. package/dist/src/sorters/sorters-control.js +3 -3
  83. package/dist/src/sorters/sorters-control.js.map +1 -1
  84. package/dist/src/types.d.ts +41 -2
  85. package/dist/src/types.js.map +1 -1
  86. package/dist/stories/{accumulator.stories.d.ts → accumulator-format.stories.d.ts} +9 -0
  87. package/dist/stories/{accumulator.stories.js → accumulator-format.stories.js} +24 -12
  88. package/dist/stories/accumulator-format.stories.js.map +1 -0
  89. package/dist/stories/barcode-input-filter.stories.d.ts +5 -0
  90. package/dist/stories/barcode-input-filter.stories.js +29 -5
  91. package/dist/stories/barcode-input-filter.stories.js.map +1 -1
  92. package/dist/stories/bounded-select-filters.stories.d.ts +30 -0
  93. package/dist/stories/bounded-select-filters.stories.js +288 -0
  94. package/dist/stories/bounded-select-filters.stories.js.map +1 -0
  95. package/dist/stories/bounded-select-record.stories.d.ts +30 -0
  96. package/dist/stories/bounded-select-record.stories.js +291 -0
  97. package/dist/stories/bounded-select-record.stories.js.map +1 -0
  98. package/dist/stories/click-event.stories.d.ts +41 -0
  99. package/dist/stories/click-event.stories.js +234 -0
  100. package/dist/stories/click-event.stories.js.map +1 -0
  101. package/dist/stories/creatable-only-column.stories.d.ts +5 -0
  102. package/dist/stories/creatable-only-column.stories.js +46 -21
  103. package/dist/stories/creatable-only-column.stories.js.map +1 -1
  104. package/dist/stories/default-filters.stories.d.ts +5 -0
  105. package/dist/stories/default-filters.stories.js +84 -17
  106. package/dist/stories/default-filters.stories.js.map +1 -1
  107. package/dist/stories/dynamic-editable.stories.d.ts +5 -0
  108. package/dist/stories/dynamic-editable.stories.js +44 -21
  109. package/dist/stories/dynamic-editable.stories.js.map +1 -1
  110. package/dist/stories/empty-sorters.stories.d.ts +7 -1
  111. package/dist/stories/empty-sorters.stories.js +41 -17
  112. package/dist/stories/empty-sorters.stories.js.map +1 -1
  113. package/dist/stories/explicit-fetch.stories.d.ts +5 -0
  114. package/dist/stories/explicit-fetch.stories.js +40 -17
  115. package/dist/stories/explicit-fetch.stories.js.map +1 -1
  116. package/dist/stories/fixed-column.stories.d.ts +5 -0
  117. package/dist/stories/fixed-column.stories.js +53 -30
  118. package/dist/stories/fixed-column.stories.js.map +1 -1
  119. package/dist/stories/grid-setting.stories.d.ts +20 -4
  120. package/dist/stories/grid-setting.stories.js +96 -51
  121. package/dist/stories/grid-setting.stories.js.map +1 -1
  122. package/dist/stories/grist-modes.stories.d.ts +8 -2
  123. package/dist/stories/grist-modes.stories.js +58 -35
  124. package/dist/stories/grist-modes.stories.js.map +1 -1
  125. package/dist/stories/group-header.stories.d.ts +5 -0
  126. package/dist/stories/group-header.stories.js +53 -30
  127. package/dist/stories/group-header.stories.js.map +1 -1
  128. package/dist/stories/textarea.stories.d.ts +8 -2
  129. package/dist/stories/textarea.stories.js +37 -13
  130. package/dist/stories/textarea.stories.js.map +1 -1
  131. package/dist/stories/tree-column-with-checkbox.stories.d.ts +5 -0
  132. package/dist/stories/tree-column-with-checkbox.stories.js +44 -21
  133. package/dist/stories/tree-column-with-checkbox.stories.js.map +1 -1
  134. package/dist/stories/tree-column.stories.d.ts +5 -0
  135. package/dist/stories/tree-column.stories.js +44 -21
  136. package/dist/stories/tree-column.stories.js.map +1 -1
  137. package/dist/tsconfig.tsbuildinfo +1 -1
  138. package/docs/default-value/value-generator/date-generator.md +29 -0
  139. package/docs/default-value/value-generator/hour-time-generator.md +33 -0
  140. package/docs/default-value/value-generator/minute-time-generator.md +33 -0
  141. package/docs/default-value/value-generator/month-date-generator.md +2 -0
  142. package/docs/default-value/value-generator/now-generator.md +29 -0
  143. package/docs/default-value/value-generator/time-generator.md +31 -0
  144. package/docs/default-value/value-generator/today-generator.md +29 -0
  145. package/docs/default-value/value-generator/week-date-generator.md +31 -0
  146. package/docs/default-value/value-generator/year-date-generator.md +31 -0
  147. package/package.json +15 -10
  148. package/src/data-card/data-card-field.ts +2 -2
  149. package/src/data-card/data-card-gutter-menu.ts +5 -5
  150. package/src/data-card/data-card-gutter.ts +6 -6
  151. package/src/data-card/data-card.ts +7 -9
  152. package/src/data-card/record-card.ts +9 -10
  153. package/src/data-grid/data-grid-accum-field.ts +11 -5
  154. package/src/data-grid/data-grid-body-style.ts +12 -0
  155. package/src/data-grid/data-grid-body.ts +16 -29
  156. package/src/data-grid/data-grid-field.ts +7 -2
  157. package/src/data-grid/data-grid-footer.ts +4 -2
  158. package/src/data-grid/data-grid-header.ts +8 -6
  159. package/src/data-grid/data-grid.ts +23 -1
  160. package/src/data-grid/event-handlers/data-grid-body-click-handler.ts +4 -0
  161. package/src/data-grist.ts +88 -8
  162. package/src/data-list/data-list-field.ts +5 -5
  163. package/src/data-list/data-list-gutter.ts +3 -3
  164. package/src/data-list/data-list.ts +4 -4
  165. package/src/data-list/record-partial.ts +9 -10
  166. package/src/data-manipulator.ts +5 -5
  167. package/src/data-report/data-report-field.ts +2 -1
  168. package/src/data-report/data-report-header.ts +2 -2
  169. package/src/editors/ox-grist-editor-select.ts +41 -28
  170. package/src/editors/ox-input-tree.ts +8 -8
  171. package/src/filters/filter-input-barcode.ts +1 -0
  172. package/src/filters/filter-select.ts +41 -28
  173. package/src/filters/filter-styles.ts +46 -31
  174. package/src/filters/filters-form.ts +273 -119
  175. package/src/gutters/gutter-dirty.ts +2 -2
  176. package/src/index.ts +1 -0
  177. package/src/personalizer/index.ts +1 -0
  178. package/src/personalizer/ox-grist-filter-personalizer.ts +191 -0
  179. package/src/personalizer/ox-grist-personalizer.ts +192 -0
  180. package/src/record-view/record-creator.ts +2 -2
  181. package/src/renderers/ox-grist-renderer-select.ts +41 -6
  182. package/src/renderers/ox-grist-renderer-tree.ts +8 -8
  183. package/src/sorters/sorters-control.ts +3 -3
  184. package/src/types.ts +53 -2
  185. package/stories/{accumulator.stories.ts → accumulator-format.stories.ts} +33 -12
  186. package/stories/barcode-input-filter.stories.ts +31 -6
  187. package/stories/bounded-select-filters.stories.ts +339 -0
  188. package/stories/bounded-select-record.stories.ts +342 -0
  189. package/stories/click-event.stories.ts +269 -0
  190. package/stories/creatable-only-column.stories.ts +54 -28
  191. package/stories/default-filters.stories.ts +92 -24
  192. package/stories/dynamic-editable.stories.ts +52 -28
  193. package/stories/empty-sorters.stories.ts +51 -25
  194. package/stories/explicit-fetch.stories.ts +48 -24
  195. package/stories/fixed-column.stories.ts +62 -39
  196. package/stories/grid-setting.stories.ts +120 -63
  197. package/stories/grist-modes.stories.ts +74 -46
  198. package/stories/group-header.stories.ts +61 -39
  199. package/stories/textarea.stories.ts +49 -17
  200. package/stories/tree-column-with-checkbox.stories.ts +52 -28
  201. package/stories/tree-column.stories.ts +52 -28
  202. package/themes/dark-hc.css +151 -0
  203. package/themes/dark-mc.css +151 -0
  204. package/themes/dark.css +151 -0
  205. package/themes/grist-theme.css +103 -100
  206. package/themes/light-hc.css +151 -0
  207. package/themes/light-mc.css +151 -0
  208. package/themes/light.css +151 -0
  209. package/themes/md-typescale-styles.css +100 -0
  210. package/themes/spacing.css +43 -0
  211. package/themes/state-color.css +6 -0
  212. package/dist/stories/accumulator.stories.js.map +0 -1
  213. package/themes/app-theme.css +0 -145
  214. package/themes/form-theme.css +0 -75
  215. package/themes/oops-theme.css +0 -26
  216. package/themes/report-theme.css +0 -47
@@ -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
+ }
@@ -0,0 +1,192 @@
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
+ import { OxCheckbox } from '@operato/input'
9
+
10
+ import { ColumnConfig, PersonalGristPreference } from '../types'
11
+ import { DataGrist } from '../data-grist'
12
+
13
+ @customElement('ox-grist-personalizer')
14
+ export class OxGristPersonalizer extends LitElement {
15
+ static styles = [
16
+ css`
17
+ md-icon {
18
+ --md-icon-size: 14px;
19
+ width: 16px;
20
+ height: 16px;
21
+ color: var(--ox-grist-p13n-color, var(--md-sys-color-on-primary));
22
+ background-color: var(--ox-grist-p13n-background-color, var(--md-sys-color-primary));
23
+ border-radius: 0px 0px 7px 7px;
24
+ cursor: pointer;
25
+
26
+ &:hover {
27
+ color: var(--ox-grist-p13n-hover-color, var(--md-sys-color-primary));
28
+ background-color: var(--ox-grist-p13n-hover-background-color, var(--md-sys-color-on-primary));
29
+ }
30
+ }
31
+ `
32
+ ]
33
+
34
+ @property({ type: Boolean, attribute: true }) debug: boolean = false
35
+
36
+ @state() private preference?: PersonalGristPreference
37
+
38
+ render() {
39
+ return html`
40
+ <md-icon
41
+ @click=${(e: MouseEvent) => {
42
+ const grist = this.closest('ox-grist') as DataGrist
43
+
44
+ const { config, compiledConfig, sorters, pagination, mode } = grist
45
+ const { columns: compiledColumns } = compiledConfig
46
+
47
+ const columns = compiledColumns
48
+ .filter(ccolumn => {
49
+ const column = config.columns.find((column: Partial<ColumnConfig>) => column.name == ccolumn.name)
50
+ return column && column.name && column.type !== 'gutter' && !column.hidden && !column.unusable
51
+ })
52
+ .map(column => compiledColumns.find(compiledColumn => compiledColumn.name == column.name)!)
53
+
54
+ this.preference = {
55
+ columns: columns.map(column => {
56
+ return {
57
+ name: column.name,
58
+ hidden: column.hidden,
59
+ width: column.width
60
+ }
61
+ }),
62
+ sorters,
63
+ pagination: {
64
+ ...pagination,
65
+ limit: grist.getCurrentLimit()
66
+ },
67
+ mode
68
+ }
69
+
70
+ const template = html`
71
+ <div class="personalizer-header" slot="header">
72
+ <md-icon
73
+ style="margin-left: auto;"
74
+ @click=${async (e: MouseEvent) => {
75
+ if (grist.personalConfigProvider) {
76
+ const { mode, columns, sorters, pagination } = this.preference || {}
77
+ grist.personalConfig = this.preference = await grist.personalConfigProvider.save({
78
+ mode,
79
+ columns,
80
+ sorters,
81
+ pagination
82
+ })
83
+ }
84
+ popup.close()
85
+ }}
86
+ title=${String(i18next.t('button.save'))}
87
+ >keep</md-icon
88
+ ><md-icon
89
+ @click=${async (e: MouseEvent) => {
90
+ if (grist.personalConfigProvider) {
91
+ grist.personalConfig = this.preference = {}
92
+ await grist.personalConfigProvider.reset()
93
+ }
94
+ popup.close()
95
+ }}
96
+ title=${String(i18next.t('button.delete'))}
97
+ >keep_off</md-icon
98
+ ><md-icon @click=${async (e: MouseEvent) => popup.close()} title=${String(i18next.t('button.close'))}
99
+ >close</md-icon
100
+ >
101
+ </div>
102
+
103
+ ${columns.map(
104
+ column => html`
105
+ <ox-checkbox label="checkbox" ?checked=${!column.hidden} value=${column.name} option
106
+ >${column.header.renderer(column)}<span style="position: absolute; right: 10px; cursor: move;" handle
107
+ >☰</span
108
+ ></ox-checkbox
109
+ >
110
+ `
111
+ )}
112
+ `
113
+
114
+ const popup = OxPopupList.open({
115
+ template,
116
+ multiple: true,
117
+ sortable: true,
118
+ debug: this.debug,
119
+ attrSelected: 'checked',
120
+ top: e.pageY,
121
+ left: e.pageX,
122
+ styles: css`
123
+ :host {
124
+ width: 240px;
125
+ max-height: 300px;
126
+ overflow: auto;
127
+ }
128
+
129
+ ::slotted(.personalizer-header) {
130
+ --md-icon-size: 1.4em;
131
+
132
+ display: flex;
133
+ flex-direction: row;
134
+ align-items: center;
135
+ text-transform: capitalize;
136
+ box-shadow: 0 3px 3px rgba(0, 0, 0, 0.3);
137
+ }
138
+
139
+ ::slotted([option]) {
140
+ position: relative;
141
+ user-select: none;
142
+ }
143
+ `
144
+ })
145
+
146
+ popup.onselect = (e: Event) => {
147
+ const selected = (e as CustomEvent).detail
148
+
149
+ const pconfig: PersonalGristPreference = grist.personalConfig || {}
150
+ const pcolumns = this.preference?.columns || columns
151
+
152
+ pconfig.columns = pcolumns.map(column => {
153
+ return {
154
+ name: column.name,
155
+ hidden: selected.indexOf(column.name) == -1,
156
+ width: column.width
157
+ }
158
+ })
159
+
160
+ this.preference = pconfig
161
+
162
+ grist.personalConfig = this.preference
163
+
164
+ grist.applyUpdatedConfiguration()
165
+ }
166
+
167
+ popup.addEventListener('sorted', (e: Event) => {
168
+ const sorted = (e as CustomEvent).detail as HTMLElement[]
169
+
170
+ const pconfig: PersonalGristPreference = grist.personalConfig || {}
171
+
172
+ pconfig.columns = sorted.map(element => {
173
+ const name = (element as OxCheckbox).value
174
+ return {
175
+ name,
176
+ hidden: !element.hasAttribute('checked'),
177
+ width: columns.find(column => column.name == name)?.width
178
+ }
179
+ })
180
+
181
+ this.preference = pconfig
182
+
183
+ grist.personalConfig = this.preference
184
+
185
+ grist.applyUpdatedConfiguration()
186
+ })
187
+ }}
188
+ >settings</md-icon
189
+ >
190
+ `
191
+ }
192
+ }
@@ -145,7 +145,7 @@ export class RecordCreator extends LitElement {
145
145
  }
146
146
  })
147
147
 
148
- recordView.addEventListener('field-change', (e: Event) => {
148
+ recordView.addEventListener('field-change', async (e: Event) => {
149
149
  const view = e.currentTarget as RecordView
150
150
 
151
151
  var { after, before, column, record, row } = (e as CustomEvent).detail as {
@@ -158,7 +158,7 @@ export class RecordCreator extends LitElement {
158
158
 
159
159
  var validation = column.validation
160
160
  if (validation && typeof validation == 'function') {
161
- if (!validation.call(this, after, before, record, column)) {
161
+ if (!(await validation.call(this, after, before, record, column))) {
162
162
  return
163
163
  }
164
164
  }
@@ -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
  }
@@ -38,7 +38,7 @@ export class OxGristRendererTree extends OxGristRenderer {
38
38
  transform: translate(-25%, -50%) rotate(-90deg);
39
39
  content: ' ';
40
40
  border: 5px solid transparent;
41
- border-top: 5px solid var(--primary-color, #1890ff);
41
+ border-top: 5px solid var(--md-sys-color-on-primary-container, #1890ff);
42
42
  }
43
43
 
44
44
  span[expander][expanded]::before {
@@ -48,7 +48,7 @@ export class OxGristRendererTree extends OxGristRenderer {
48
48
  transform: translate(-50%, -25%);
49
49
  content: ' ';
50
50
  border: 5px solid transparent;
51
- border-top: 5px solid var(--primary-color, #1890ff);
51
+ border-top: 5px solid var(--md-sys-color-on-primary-container, #1890ff);
52
52
  }
53
53
 
54
54
  span[checkbox] {
@@ -70,7 +70,7 @@ export class OxGristRendererTree extends OxGristRenderer {
70
70
  display: block;
71
71
  width: 10px;
72
72
  height: 10px;
73
- border: 1px solid var(--primary-color, #1890ff);
73
+ border: 1px solid var(--md-sys-color-on-primary-container, #1890ff);
74
74
  border-radius: 2px;
75
75
  }
76
76
 
@@ -79,8 +79,8 @@ export class OxGristRendererTree extends OxGristRenderer {
79
79
  }
80
80
 
81
81
  span[checkbox][checked='checked']::before {
82
- background-color: var(--primary-color, #1890ff);
83
- border-color: var(--primary-color, #1890ff);
82
+ background-color: var(--md-sys-color-on-primary-container, #1890ff);
83
+ border-color: var(--md-sys-color-on-primary-container, #1890ff);
84
84
  }
85
85
 
86
86
  span[checkbox][checked='checked']::after {
@@ -100,8 +100,8 @@ export class OxGristRendererTree extends OxGristRenderer {
100
100
  }
101
101
 
102
102
  span[checkbox][checked='half-checked']::before {
103
- background-color: var(--primary-color, #1890ff);
104
- border-color: var(--primary-color, #1890ff);
103
+ background-color: var(--md-sys-color-on-primary-container, #1890ff);
104
+ border-color: var(--md-sys-color-on-primary-container, #1890ff);
105
105
  }
106
106
 
107
107
  span[checkbox][checked='half-checked']::after {
@@ -113,7 +113,7 @@ export class OxGristRendererTree extends OxGristRenderer {
113
113
  transform: translate(-50%, -50%);
114
114
  width: 10px;
115
115
  height: 2px;
116
- background-color: #fff;
116
+ background-color: var(--md-sys-color-surface);
117
117
  }
118
118
  `
119
119
 
@@ -30,12 +30,12 @@ export class SortersControl extends LitElement {
30
30
  user-select: none;
31
31
  }
32
32
  [option] md-icon {
33
- margin-left: var(--margin-default);
33
+ margin-left: var(--spacing-medium);
34
34
  --md-icon-size: var(--fontsize-large);
35
- color: var(--primary-color);
35
+ color: var(--md-sys-color-on-primary-container);
36
36
  }
37
37
  [option] sub {
38
- color: var(--primary-color);
38
+ color: var(--md-sys-color-on-primary-container);
39
39
  }
40
40
  `
41
41
  ]
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
  *
@@ -90,6 +106,7 @@ export type FilterOperator =
90
106
  * @property {string} type - The type of the filter condition.
91
107
  * @property {FilterOperator} [operator] - The filter operator used to compare values (optional).
92
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).
93
110
  * @property {string|number|boolean|string[]|number[]|undefined} [value] - The value to compare with in the filter condition (optional).
94
111
  * @property {string} [label] - The label to display for the filter condition (optional).
95
112
  */
@@ -99,6 +116,9 @@ export type FilterConfigObject = {
99
116
  options?: { [key: string]: any }
100
117
  value?: string | number | boolean | string[] | number[] | undefined
101
118
  label?: string
119
+ boundTo?: string[]
120
+ hidden?: boolean
121
+ onchange?: FilterChangedCallback
102
122
  }
103
123
 
104
124
  /**
@@ -341,6 +361,8 @@ export type ColumnConfig = {
341
361
  imex?: ImexConfig | boolean
342
362
  multiple?: boolean
343
363
  rowCount?: boolean
364
+ /** 특정 도구에 의해서 사용되는 옵션으로, 일반적인 용도로 사용되지 않음을 표현할 수 있다. */
365
+ unusable?: boolean
344
366
  }
345
367
 
346
368
  /**
@@ -355,7 +377,12 @@ export type ColumnConfig = {
355
377
  * @param {ColumnConfig} column - The configuration of the column being edited.
356
378
  * @returns {boolean} - Returns `true` if the value is valid, `false` otherwise.
357
379
  */
358
- export type ValidationCallback = (after: any, before: any, record: GristRecord, column: ColumnConfig) => boolean
380
+ export type ValidationCallback = (
381
+ after: any,
382
+ before: any,
383
+ record: GristRecord,
384
+ column: ColumnConfig
385
+ ) => boolean | Promise<boolean>
359
386
 
360
387
  /**
361
388
  * Configuration options for column labels.
@@ -743,3 +770,27 @@ export type GristData = {
743
770
  * @returns {boolean} - `true` if the record should be selected, `false` otherwise.
744
771
  */
745
772
  export type GristSelectFunction = (record: GristRecord) => boolean
773
+
774
+ /**
775
+ * Defines a type for personalized grid settings. It includes individual column settings and additional configurations specific to a user's grid view.
776
+ *
777
+ * @typedef {Object} PersonalGristPreference
778
+ * @property {Partial<ColumnConfig>[]} [columns] - Partially defines the configuration for each column in the grid.
779
+ * Each element can include only some properties of the `ColumnConfig`, used for optionally overriding column settings.
780
+ * @property {any} [key] - Allows for storing additional user-defined properties with dynamic keys.
781
+ * This property can include various custom settings beyond the grid configuration, and the keys can be freely defined.
782
+ */
783
+ export type PersonalGristPreference = {
784
+ columns?: Partial<ColumnConfig>[]
785
+ filters?: FilterPreference[]
786
+ pagination?: PaginationConfig
787
+ sorters?: SortersConfig
788
+ mode?: 'GRID' | 'LIST' | 'CARD'
789
+ [key: string]: any
790
+ }
791
+
792
+ export type FilterPreference = {
793
+ name: string
794
+ hidden?: boolean
795
+ value?: any
796
+ }