@operato/data-grist 7.1.31 → 7.1.32

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 (173) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/tsconfig.tsbuildinfo +1 -1
  3. package/package.json +10 -10
  4. package/.storybook/main.js +0 -3
  5. package/.storybook/preview.js +0 -52
  6. package/.storybook/server.mjs +0 -8
  7. package/demo/data-grist-test.html +0 -468
  8. package/demo/favicon.ico +0 -0
  9. package/demo/index.html +0 -541
  10. package/demo/report-test.html +0 -249
  11. package/dist/src/record-view/record-creator.d.ts +0 -17
  12. package/dist/src/record-view/record-creator.js +0 -148
  13. package/dist/src/record-view/record-creator.js.map +0 -1
  14. package/src/accumulator/accumulator.ts +0 -63
  15. package/src/configure/column-builder.ts +0 -114
  16. package/src/configure/config-builder.ts +0 -40
  17. package/src/configure/filters-option-builder.ts +0 -8
  18. package/src/configure/imex-option-builder.ts +0 -5
  19. package/src/configure/list-option-builder.ts +0 -9
  20. package/src/configure/rows-option-builder.ts +0 -38
  21. package/src/configure/tree-option-builder.ts +0 -22
  22. package/src/configure/zero-config.ts +0 -83
  23. package/src/const.ts +0 -1
  24. package/src/data-card/data-card-field.ts +0 -94
  25. package/src/data-card/data-card-gutter-menu.ts +0 -94
  26. package/src/data-card/data-card-gutter.ts +0 -103
  27. package/src/data-card/data-card.ts +0 -154
  28. package/src/data-card/event-handlers/record-card-click-handler.ts +0 -34
  29. package/src/data-card/event-handlers/record-card-dblclick-handler.ts +0 -34
  30. package/src/data-card/record-card.ts +0 -289
  31. package/src/data-consumer.ts +0 -11
  32. package/src/data-grid/data-grid-accum-field.ts +0 -109
  33. package/src/data-grid/data-grid-body-style.ts +0 -99
  34. package/src/data-grid/data-grid-body.ts +0 -753
  35. package/src/data-grid/data-grid-field.ts +0 -236
  36. package/src/data-grid/data-grid-footer.ts +0 -117
  37. package/src/data-grid/data-grid-header.ts +0 -574
  38. package/src/data-grid/data-grid.ts +0 -293
  39. package/src/data-grid/event-handlers/data-grid-body-click-handler.ts +0 -69
  40. package/src/data-grid/event-handlers/data-grid-body-contextmenu-handler.ts +0 -32
  41. package/src/data-grid/event-handlers/data-grid-body-dblclick-handler.ts +0 -42
  42. package/src/data-grid/event-handlers/data-grid-body-focus-change-handler.ts +0 -24
  43. package/src/data-grid/event-handlers/data-grid-body-keydown-handler.ts +0 -234
  44. package/src/data-grist.ts +0 -1233
  45. package/src/data-list/data-list-field.ts +0 -82
  46. package/src/data-list/data-list-gutter.ts +0 -108
  47. package/src/data-list/data-list.ts +0 -145
  48. package/src/data-list/event-handlers/record-partial-click-handler.ts +0 -34
  49. package/src/data-list/event-handlers/record-partial-dblclick-handler.ts +0 -33
  50. package/src/data-list/event-handlers/record-partial-long-press-handler.ts +0 -33
  51. package/src/data-list/record-partial.ts +0 -255
  52. package/src/data-manipulator.ts +0 -426
  53. package/src/data-provider.ts +0 -271
  54. package/src/data-report/data-report-body-style.ts +0 -58
  55. package/src/data-report/data-report-body.ts +0 -189
  56. package/src/data-report/data-report-component.ts +0 -138
  57. package/src/data-report/data-report-field.ts +0 -83
  58. package/src/data-report/data-report-header.ts +0 -242
  59. package/src/data-report/event-handlers/data-report-body-click-handler.ts +0 -38
  60. package/src/data-report/event-handlers/data-report-body-dblclick-handler.ts +0 -25
  61. package/src/data-report/event-handlers/data-report-body-keydown-handler.ts +0 -68
  62. package/src/data-report.ts +0 -424
  63. package/src/editors/index.ts +0 -4
  64. package/src/editors/ox-grist-editor-checkbox.ts +0 -28
  65. package/src/editors/ox-grist-editor-color.ts +0 -10
  66. package/src/editors/ox-grist-editor-date.ts +0 -10
  67. package/src/editors/ox-grist-editor-datetime.ts +0 -27
  68. package/src/editors/ox-grist-editor-email.ts +0 -10
  69. package/src/editors/ox-grist-editor-file.ts +0 -28
  70. package/src/editors/ox-grist-editor-image.ts +0 -31
  71. package/src/editors/ox-grist-editor-month.ts +0 -10
  72. package/src/editors/ox-grist-editor-multiple-select.ts +0 -57
  73. package/src/editors/ox-grist-editor-number.ts +0 -27
  74. package/src/editors/ox-grist-editor-password.ts +0 -10
  75. package/src/editors/ox-grist-editor-select.ts +0 -55
  76. package/src/editors/ox-grist-editor-tel.ts +0 -10
  77. package/src/editors/ox-grist-editor-text.ts +0 -14
  78. package/src/editors/ox-grist-editor-textarea.ts +0 -16
  79. package/src/editors/ox-grist-editor-time.ts +0 -10
  80. package/src/editors/ox-grist-editor-tree.ts +0 -27
  81. package/src/editors/ox-grist-editor-varname.ts +0 -36
  82. package/src/editors/ox-grist-editor-week.ts +0 -10
  83. package/src/editors/ox-grist-editor.ts +0 -207
  84. package/src/editors/ox-input-tree.ts +0 -226
  85. package/src/editors/registry.ts +0 -82
  86. package/src/empty-note.ts +0 -46
  87. package/src/filters/filter-checkbox.ts +0 -49
  88. package/src/filters/filter-input-barcode.ts +0 -34
  89. package/src/filters/filter-input.ts +0 -30
  90. package/src/filters/filter-range-date.ts +0 -81
  91. package/src/filters/filter-range-number.ts +0 -64
  92. package/src/filters/filter-select-buttons.ts +0 -60
  93. package/src/filters/filter-select.ts +0 -68
  94. package/src/filters/filter-styles.ts +0 -119
  95. package/src/filters/filters-form.ts +0 -476
  96. package/src/filters/index.ts +0 -10
  97. package/src/filters/registry.ts +0 -56
  98. package/src/formatters/date-formatter.ts +0 -3
  99. package/src/formatters/index.ts +0 -1
  100. package/src/formatters/number-formatter.ts +0 -3
  101. package/src/formatters/registry.ts +0 -30
  102. package/src/formatters/text-formatter.ts +0 -3
  103. package/src/gutters/gutter-button.ts +0 -51
  104. package/src/gutters/gutter-dirty.ts +0 -96
  105. package/src/gutters/gutter-row-selector.ts +0 -89
  106. package/src/gutters/gutter-sequence.ts +0 -54
  107. package/src/gutters/index.ts +0 -1
  108. package/src/gutters/registry.ts +0 -32
  109. package/src/handlers/contextmenu-tree-mutation.ts +0 -80
  110. package/src/handlers/index.ts +0 -1
  111. package/src/handlers/move-down.ts +0 -44
  112. package/src/handlers/move-up.ts +0 -44
  113. package/src/handlers/record-copy.ts +0 -38
  114. package/src/handlers/record-delete.ts +0 -30
  115. package/src/handlers/record-view-handler.ts +0 -27
  116. package/src/handlers/registry.ts +0 -42
  117. package/src/handlers/select-row-toggle.ts +0 -30
  118. package/src/handlers/select-row.ts +0 -27
  119. package/src/index.ts +0 -17
  120. package/src/personalizer/index.ts +0 -1
  121. package/src/personalizer/ox-grist-filter-personalizer.ts +0 -192
  122. package/src/personalizer/ox-grist-personalizer.ts +0 -226
  123. package/src/record-view/event-handlers/record-view-body-click-handler.ts +0 -33
  124. package/src/record-view/event-handlers/record-view-body-keydown-handler.ts +0 -26
  125. package/src/record-view/index.ts +0 -2
  126. package/src/record-view/ox-record-creator.ts +0 -289
  127. package/src/record-view/record-view-body.ts +0 -250
  128. package/src/record-view/record-view-handler.ts +0 -86
  129. package/src/record-view/record-view.ts +0 -122
  130. package/src/renderers/index.ts +0 -14
  131. package/src/renderers/ox-grist-renderer-boolean.ts +0 -43
  132. package/src/renderers/ox-grist-renderer-color.ts +0 -15
  133. package/src/renderers/ox-grist-renderer-date.ts +0 -62
  134. package/src/renderers/ox-grist-renderer-file.ts +0 -31
  135. package/src/renderers/ox-grist-renderer-image.ts +0 -27
  136. package/src/renderers/ox-grist-renderer-json5.ts +0 -36
  137. package/src/renderers/ox-grist-renderer-link.ts +0 -17
  138. package/src/renderers/ox-grist-renderer-password.ts +0 -7
  139. package/src/renderers/ox-grist-renderer-progress.ts +0 -45
  140. package/src/renderers/ox-grist-renderer-select.ts +0 -58
  141. package/src/renderers/ox-grist-renderer-text.ts +0 -16
  142. package/src/renderers/ox-grist-renderer-textarea.ts +0 -7
  143. package/src/renderers/ox-grist-renderer-tree.ts +0 -189
  144. package/src/renderers/ox-grist-renderer.ts +0 -35
  145. package/src/renderers/registry.ts +0 -111
  146. package/src/sorters/sorters-control.ts +0 -143
  147. package/src/types.ts +0 -813
  148. package/src/utils/index.ts +0 -2
  149. package/src/utils/list-param.ts +0 -72
  150. package/src/utils/supports-passive.ts +0 -13
  151. package/stories/accumulator-format.stories.ts +0 -276
  152. package/stories/barcode-input-filter.stories.ts +0 -216
  153. package/stories/bounded-select-filters.stories.ts +0 -333
  154. package/stories/bounded-select-record.stories.ts +0 -336
  155. package/stories/click-event-custom.stories.ts +0 -288
  156. package/stories/click-event.stories.ts +0 -283
  157. package/stories/creatable-only-column.stories.ts +0 -253
  158. package/stories/default-filters.stories.ts +0 -241
  159. package/stories/dynamic-editable.stories.ts +0 -313
  160. package/stories/empty-sorters.stories.ts +0 -180
  161. package/stories/explicit-fetch.stories.ts +0 -186
  162. package/stories/fixed-column.stories.ts +0 -416
  163. package/stories/grid-setting.stories.ts +0 -501
  164. package/stories/grist-modes.stories.ts +0 -451
  165. package/stories/group-header.stories.ts +0 -442
  166. package/stories/record-view.stories.ts +0 -143
  167. package/stories/textarea.stories.ts +0 -261
  168. package/stories/tree-column-with-checkbox.stories.ts +0 -297
  169. package/stories/tree-column.stories.ts +0 -296
  170. package/tsconfig.json +0 -26
  171. package/web-dev-server.config.mjs +0 -27
  172. package/web-test-runner.config.mjs +0 -45
  173. package/yarn-error.log +0 -16971
package/src/data-grist.ts DELETED
@@ -1,1233 +0,0 @@
1
- import './data-grid/data-grid'
2
- import './data-list/data-list'
3
- import './data-card/data-card'
4
-
5
- import { css, html, LitElement, PropertyValues } from 'lit'
6
- import { customElement, property, query, queryAsync, state } from 'lit/decorators.js'
7
- import isEmpty from 'lodash-es/isEmpty'
8
- import isEqual from 'lodash-es/isEqual'
9
-
10
- import Headroom from '@operato/headroom'
11
- import { pulltorefresh } from '@operato/pull-to-refresh'
12
- import { HeadroomStyles, ScrollbarStyles, SpinnerStyles } from '@operato/styles'
13
- import { PagePreferenceProvider } from '@operato/p13n'
14
- import { SnapshotTaker, TimeCapsule } from '@operato/utils'
15
-
16
- import { buildConfig } from './configure/config-builder'
17
- import { ZERO_CONFIG, ZERO_DATA, ZERO_PAGES, ZERO_PAGINATION } from './configure/zero-config'
18
- import { DataCard } from './data-card/data-card'
19
- import { DataConsumer } from './data-consumer'
20
- import { DataGrid } from './data-grid/data-grid'
21
- import { DataList } from './data-list/data-list'
22
- import { DataProvider } from './data-provider'
23
- import {
24
- ColumnConfig,
25
- FetchHandler,
26
- FilterConfigObject,
27
- FilterValue,
28
- GristConfig,
29
- GristData,
30
- GristRecord,
31
- GristSelectFunction,
32
- PaginationConfig,
33
- PersonalGristPreference,
34
- SortersConfig,
35
- ValidationReason
36
- } from './types'
37
- import { convertListParamToSearchString, convertSearchStringToListParam } from './utils'
38
-
39
- /**
40
- * A custom element for rendering data in a grid, list, or card format.
41
- *
42
- * @element ox-grist
43
- */
44
- @customElement('ox-grist')
45
- export class DataGrist extends LitElement implements DataConsumer {
46
- static styles = [
47
- ScrollbarStyles,
48
- HeadroomStyles,
49
- SpinnerStyles,
50
- css`
51
- :host {
52
- display: flex;
53
- flex-direction: column;
54
- box-sizing: border-box;
55
- background-color: var(--grist-background-color);
56
- min-height: 120px;
57
-
58
- overflow: hidden;
59
-
60
- /* for pulltorefresh controller */
61
- position: relative;
62
-
63
- padding: var(--ox-grist-padding);
64
-
65
- --md-icon-size: var(--grid-record-wide-fontsize);
66
- }
67
-
68
- #wrap {
69
- flex: 1;
70
- display: flex;
71
- flex-direction: column;
72
- overflow: auto;
73
- }
74
-
75
- ox-grid {
76
- flex: 1;
77
- border: var(--grid-wrap-container-border, 0px solid transparent);
78
- border-width: var(--grid-wrap-container-border-width, 0px);
79
- }
80
-
81
- slot[name='headroom'] {
82
- display: block;
83
- position: absolute;
84
- top: 0;
85
- left: 0;
86
-
87
- width: 100%;
88
- box-sizing: border-box;
89
- background-color: var(--grist-background-color);
90
-
91
- z-index: 8;
92
- }
93
- `
94
- ]
95
-
96
- /**
97
- * The rendering mode of the component, which can be 'GRID', 'LIST', or 'CARD'.
98
- * Default is 'GRID'.
99
- *
100
- * @property {string}
101
- */
102
- @property() mode: 'GRID' | 'LIST' | 'CARD' = 'GRID'
103
-
104
- /**
105
- * The configuration object for the data grist.
106
- *
107
- * @property {Object}
108
- */
109
- @property() config: any
110
-
111
- /**
112
- * The data to be displayed in the data grist.
113
- *
114
- * @property {GristData}
115
- */
116
- @property({ type: Object }) data: GristData = ZERO_DATA
117
-
118
- /**
119
- * An array of selected records in the data grist.
120
- *
121
- * @property {GristRecord[]}
122
- */
123
- @property({ type: Array }) selectedRecords?: GristRecord[]
124
-
125
- /**
126
- * Indicates whether explicit fetching of data is enabled. If true, data will be fetched
127
- * only when `fetch` method is called. Default is false.
128
- *
129
- * @property {boolean}
130
- */
131
- @property({ type: Boolean, attribute: 'explicit-fetch' }) explicitFetch: boolean = false
132
-
133
- /**
134
- * The fetch handler function used to retrieve data from a remote source.
135
- *
136
- * @property {FetchHandler}
137
- */
138
- @property() fetchHandler?: FetchHandler
139
-
140
- /**
141
- * Additional fetch options to be passed to the fetch handler.
142
- *
143
- * @property {Object}
144
- */
145
- @property() fetchOptions: any
146
-
147
- /**
148
- * An array of filter values to be applied to the data grist.
149
- *
150
- * @property {FilterValue[]}
151
- */
152
- @property({ type: Array }) filters?: FilterValue[]
153
-
154
- /**
155
- * An array of sorters to determine the order of records in the data grist.
156
- *
157
- * @property {SortersConfig}
158
- */
159
- @property({ type: Array }) sorters?: SortersConfig
160
-
161
- /**
162
- * The pagination configuration for the data grist.
163
- *
164
- * @property {PaginationConfig}
165
- */
166
- @property({ type: Object }) pagination?: PaginationConfig
167
-
168
- /**
169
- * Indicates whether URL parameters are sensitive to changes. If true, changes in URL
170
- * parameters will trigger data fetching. Default is undefined.
171
- *
172
- * @property {boolean}
173
- */
174
- @property({ type: Boolean, attribute: 'url-params-sensitive' }) urlParamsSensitive?: boolean
175
-
176
- @property({ type: Object }) personalConfigProvider: PagePreferenceProvider = {
177
- load: () => {
178
- return {}
179
- },
180
- save: (preference: any) => preference,
181
- reset: () => {}
182
- }
183
-
184
- @state() personalConfig?: {
185
- columns?: Partial<ColumnConfig>[]
186
- [key: string]: any
187
- }
188
-
189
- @state() _data: GristData = ZERO_DATA // copy data, dirty data
190
- @state() _config: GristConfig = ZERO_CONFIG // compiled configuration
191
- @state() private _showSpinner: boolean = false
192
-
193
- @query('slot[name=headroom]') head!: HTMLElement
194
- @query('#grist') grist!: DataGrid | DataList | DataCard
195
- @queryAsync('#wrap') private wrap!: Promise<HTMLElement>
196
-
197
- private timeCapsule?: TimeCapsule
198
- private snapshotTaker?: SnapshotTaker
199
-
200
- private dataProvider?: DataProvider
201
- private pulltorefreshHandle?: any
202
- private headroom?: Headroom
203
- private orginPaddingTop?: string
204
- private originMarginTop?: string
205
- private lastLocation: { origin?: string; pathname?: string; search?: string } = {}
206
-
207
- /* 그리드가 준비되기 전에 fetch 요청이 있었음을 나타내는 플래그임 */
208
- private pendingFetch?: boolean
209
-
210
- private popstateEventHandler: EventListener = ((e: Event) => {
211
- const { origin, pathname, search } = window.location
212
- if (
213
- this.lastLocation.origin &&
214
- (this.lastLocation.origin !== origin ||
215
- this.lastLocation.pathname !== pathname ||
216
- this.lastLocation.search === search)
217
- ) {
218
- return
219
- }
220
-
221
- this.lastLocation = {
222
- origin,
223
- pathname,
224
- search
225
- }
226
-
227
- var { filters = [], sorters = [] } = convertSearchStringToListParam(search)
228
-
229
- try {
230
- if (!isEqual(filters, this.filters) || !isEqual(sorters, this.sorters)) {
231
- this.dispatchEvent(
232
- new CustomEvent('fetch-params-change', {
233
- bubbles: true,
234
- composed: true,
235
- detail: {
236
- filters,
237
- sorters,
238
- from: 'url-parameter'
239
- }
240
- })
241
- )
242
- }
243
- } catch (e) {
244
- console.error(`invalid fetch params on URL query string : ${e}`)
245
- }
246
- }).bind(this)
247
-
248
- private fetchParamsChangeEventHandler: EventListener = ((e: Event) => {
249
- const { sorters, filters, from } = (e as CustomEvent).detail
250
-
251
- sorters && (this.sorters = sorters)
252
- filters && (this.filters = filters)
253
-
254
- if (!this.urlParamsSensitive || from === 'url-parameter') {
255
- return
256
- }
257
-
258
- const queryString = convertListParamToSearchString({ filters, sorters, base: window.location.search })
259
- this.lastLocation.search = queryString ? `?${queryString}` : ''
260
- const url = `${window.location.pathname}${this.lastLocation.search}`
261
-
262
- history.pushState({}, document.title, url)
263
- }).bind(this)
264
-
265
- async firstUpdated() {
266
- // Mutation Observer를 사용하여 슬롯의 크기 변경을 감지하고 다시 그린다.
267
- const observer = new ResizeObserver(mutationsList => {
268
- this.setHeadroom()
269
- })
270
-
271
- observer.observe(this.head)
272
- }
273
-
274
- async connectedCallback() {
275
- super.connectedCallback()
276
-
277
- this.dataProvider = new DataProvider(this)
278
-
279
- this.timeCapsule = new TimeCapsule(10)
280
- this.snapshotTaker = new SnapshotTaker(this, this.timeCapsule!)
281
-
282
- this.addEventListener('fetch-params-change', this.fetchParamsChangeEventHandler)
283
-
284
- await this.updateComplete
285
- }
286
-
287
- disconnectedCallback() {
288
- super.disconnectedCallback()
289
-
290
- this.removeEventListener('fetch-params-change', this.fetchParamsChangeEventHandler)
291
-
292
- this.timeCapsule && this.timeCapsule.dispose()
293
- this.snapshotTaker && this.snapshotTaker.dispose()
294
-
295
- delete this.timeCapsule
296
- delete this.snapshotTaker
297
-
298
- this.dataProvider?.dispose()
299
- this.resetPullToRefresh()
300
- }
301
-
302
- private resetPullToRefresh() {
303
- if (this.pulltorefreshHandle) {
304
- this.pulltorefreshHandle()
305
- delete this.pulltorefreshHandle
306
- }
307
- }
308
-
309
- private async setPullToRefresh() {
310
- this.resetPullToRefresh()
311
- if (this.mode !== 'GRID' && this.fetchHandler) {
312
- this.pulltorefreshHandle = pulltorefresh({
313
- container: await this.wrap,
314
- scrollable: this.grist.pullToRefreshTarget,
315
- refresh: () => {
316
- return this.fetch(true)
317
- }
318
- })
319
- }
320
- }
321
-
322
- public applyUpdatedConfiguration() {
323
- if (!this.personalConfig || !this.config) {
324
- return
325
- }
326
-
327
- const config = { ...this.config }
328
- const columns: Partial<ColumnConfig>[] = config.columns
329
-
330
- const { columns: personalColumns, sorters, pagination, mode } = this.personalConfig
331
-
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)
341
-
342
- // b 배열에 없는 아이템은 원래 위치로 채움
343
- a.forEach((item, index) => {
344
- if (!item.name || !b.find(bi => bi.name == item.name)) {
345
- result[index] = item
346
- }
347
- })
348
-
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
- })
355
-
356
- return result
357
- }
358
-
359
- // 배열 재정렬 실행
360
- config.columns = reorderList(xcolumns as any, personalColumns as any)
361
- }
362
-
363
- if (pagination) {
364
- config.pagination = pagination
365
- }
366
-
367
- if (sorters) {
368
- config.sorters = sorters
369
- }
370
-
371
- if (mode) {
372
- this.mode = mode
373
- }
374
-
375
- this._config = buildConfig({
376
- ...config
377
- })
378
-
379
- this.dispatchEvent(
380
- new CustomEvent('config-change', {
381
- bubbles: true,
382
- composed: true,
383
- detail: this.compiledConfig
384
- })
385
- )
386
-
387
- this.pagination = this.compiledConfig.pagination || {}
388
-
389
- if (!this.urlParamsSensitive) {
390
- const sorters = this.compiledConfig?.sorters
391
- const filters = this.compiledConfig?.columns
392
- .filter(
393
- column =>
394
- column.filter &&
395
- 'value' in (column.filter as FilterConfigObject) &&
396
- (typeof (column.filter as FilterConfigObject).value === 'boolean' ||
397
- (column.filter as FilterConfigObject).value)
398
- )
399
- .map(column => {
400
- const filter = column.filter as FilterConfigObject
401
- return { name: column.name, operator: filter.operator, value: filter.value } as FilterValue
402
- })
403
-
404
- this.dispatchEvent(
405
- new CustomEvent('fetch-params-change', {
406
- bubbles: true,
407
- composed: true,
408
- detail: {
409
- filters,
410
- sorters,
411
- from: 'config'
412
- }
413
- })
414
- )
415
- }
416
-
417
- if (this.dataProvider) {
418
- const { limit, pages = ZERO_PAGES } = this._config.pagination || ZERO_PAGINATION
419
- this.dataProvider.page = 1
420
- this.dataProvider.limit = limit || pages[0] || ZERO_PAGES[0]
421
- }
422
-
423
- if (this.pendingFetch || (!this.urlParamsSensitive && !this.explicitFetch)) {
424
- this.pendingFetch = false
425
- this.fetch()
426
- }
427
- }
428
-
429
- private setHeadroom() {
430
- this.headroom?.destroy()
431
-
432
- if (this.grist && this.head && this.head.style.display !== 'none') {
433
- const style = getComputedStyle(this.grist)
434
-
435
- this.orginPaddingTop = style.paddingTop || '0'
436
- this.originMarginTop = style.marginTop || '0'
437
-
438
- this.headroom = new Headroom(this.head, {
439
- scroller: this.grist,
440
- onTop: () => {
441
- this.grist.style.paddingTop = this.orginPaddingTop!
442
- this.originMarginTop = this.grist.style.marginTop
443
-
444
- this.grist.style.marginTop = this.head.clientHeight + 'px'
445
- },
446
- onNotTop: () => {
447
- this.grist.style.marginTop = this.originMarginTop!
448
- this.orginPaddingTop = this.grist.style.paddingTop
449
-
450
- this.grist.style.paddingTop = this.head.clientHeight + 'px'
451
- }
452
- })
453
-
454
- this.headroom.init()
455
- } else if (this.orginPaddingTop) {
456
- this.grist.style.paddingTop = this.orginPaddingTop
457
- this.grist.style.marginTop = this.originMarginTop!
458
- }
459
- }
460
-
461
- render() {
462
- const empty = !this._showSpinner && this._data.records.length == 0
463
-
464
- return html`
465
- <slot name="headroom"> </slot>
466
- <div id="wrap" @keydown=${(e: KeyboardEvent) => this.onKeydown(e)}>
467
- ${this.mode == 'GRID'
468
- ? html`
469
- <ox-grid
470
- id="grist"
471
- .config=${this.compiledConfig}
472
- .data=${this._data}
473
- .sorters=${this.sorters || []}
474
- .filters=${this.filters || []}
475
- .pagination=${this.pagination || {}}
476
- ?empty=${empty}
477
- >
478
- <slot name="setting" slot="setting"> </slot>
479
- </ox-grid>
480
- `
481
- : this.mode == 'CARD'
482
- ? html`
483
- <ox-card
484
- id="grist"
485
- .config=${this.compiledConfig}
486
- .data=${this._data}
487
- .sorters=${this.sorters || []}
488
- .filters=${this.filters || []}
489
- ?empty=${empty}
490
- >
491
- </ox-card>
492
- `
493
- : html`
494
- <ox-list
495
- id="grist"
496
- .config=${this.compiledConfig}
497
- .data=${this._data}
498
- .sorters=${this.sorters || []}
499
- .filters=${this.filters || []}
500
- ?empty=${empty}
501
- >
502
- </ox-list>
503
- `}
504
- </div>
505
-
506
- <div id="spinner" ?show=${this._showSpinner}></div>
507
- `
508
- }
509
-
510
- /* for timecapsule feature */
511
- private onKeydown(e: KeyboardEvent) {
512
- if (e.key === 'z' && (e.metaKey || e.ctrlKey)) {
513
- if (e.shiftKey) {
514
- this.redo()
515
- } else {
516
- this.undo()
517
- }
518
- }
519
- }
520
-
521
- /**
522
- * Gets the current state of the component. The state includes information about the
523
- * dirty records and their changes.
524
- *
525
- * @getter
526
- * @public
527
- * @type {string}
528
- */
529
- get state() {
530
- return JSON.stringify(this.dirtyData)
531
- }
532
-
533
- /**
534
- * Undoes the previous change in the component's data by restoring it to the previous state.
535
- * This method is part of the TimeCapsule feature, allowing users to revert changes.
536
- */
537
- undo() {
538
- if (!this.timeCapsule?.backwardable) {
539
- return
540
- }
541
-
542
- this._data = JSON.parse(this.timeCapsule?.backward())
543
- }
544
-
545
- /**
546
- * Redoes the previously undone change in the component's data by restoring it to the next state.
547
- * This method is part of the TimeCapsule feature, allowing users to reapply changes.
548
- */
549
- redo() {
550
- if (!this.timeCapsule?.forwardable) {
551
- return
552
- }
553
-
554
- this._data = JSON.parse(this.timeCapsule?.forward())
555
- }
556
-
557
- /**
558
- * Fetches data from a data source and updates the component's data. This method is used to retrieve
559
- * new data or refresh the existing data in the component.
560
- *
561
- * @method
562
- * @param {boolean} reset - If true, the method resets the scroll position to the top.
563
- */
564
- async fetch(reset = true) {
565
- if (this.compiledConfig === ZERO_CONFIG) {
566
- /* avoid to be here */
567
- console.warn('grist is not configured yet.')
568
- this.pendingFetch = true
569
- return
570
- }
571
-
572
- if (reset && this.grist) {
573
- /*
574
- * scroll 의 현재위치에 의해서 scroll 이벤트가 발생할 수 있으므로, 이를 방지하기 위해서 스크롤의 위치를 TOP으로 옮긴다.
575
- * (scroll 이 첫페이지 크기 이상으로 내려가 있는 경우, 첫페이지부터 다시 표시하는 경우에, scroll 이벤트가 발생한다.)
576
- */
577
- this.grist.scrollTop = 0
578
- }
579
-
580
- if (this.dataProvider) {
581
- let { limit: initLimit, page: initPage, infinite } = this.compiledConfig.pagination || {}
582
- let { limit = initLimit || ZERO_PAGINATION.limit, page = initPage || ZERO_PAGINATION.page } = this.dataProvider
583
-
584
- if (infinite || this.mode !== 'GRID') {
585
- await this.dataProvider.attach(reset)
586
- } else {
587
- await this.dataProvider.fetch({
588
- limit,
589
- page,
590
- sorters: this.sorters || this.compiledConfig?.sorters,
591
- sortings: this.sorters || this.compiledConfig?.sorters,
592
- filters: this.filters
593
- })
594
-
595
- this.pagination && (this.pagination!.limit = limit)
596
- }
597
- }
598
- }
599
-
600
- async updated(changes: PropertyValues<this>) {
601
- var needToSetPullToRefresh = false
602
-
603
- if (changes.has('filters')) {
604
- await this.requestUpdate()
605
- }
606
-
607
- if (changes.has('sorters')) {
608
- await this.requestUpdate()
609
- }
610
-
611
- if (changes.has('personalConfigProvider')) {
612
- this.personalConfig = await this.personalConfigProvider.load()
613
- } else if (changes.has('config') || changes.has('personalConfig')) {
614
- this.applyUpdatedConfiguration()
615
- }
616
-
617
- if (changes.has('fetchHandler')) {
618
- this.dataProvider && (this.dataProvider.fetchHandler = this.fetchHandler)
619
- needToSetPullToRefresh = true
620
- }
621
-
622
- if (changes.has('fetchOptions')) {
623
- this.dataProvider && (this.dataProvider.fetchOptions = this.fetchOptions)
624
- }
625
-
626
- if (changes.has('data')) {
627
- this.reset()
628
- }
629
-
630
- if (changes.has('mode')) {
631
- if (this.mode === 'GRID') {
632
- this.style.removeProperty('--ox-grist-padding')
633
- } else {
634
- this.style.setProperty('--ox-grist-padding', '0')
635
- }
636
-
637
- needToSetPullToRefresh = true
638
- this.setHeadroom()
639
- }
640
-
641
- if (changes.has('selectedRecords')) {
642
- var { records } = this.data || []
643
- var selectedRecords = this.selectedRecords || []
644
-
645
- var _records = this.dirtyData.records
646
-
647
- /* 원본데이타에서 index를 찾아서, 복사본 데이타의 selected를 설정한다. */
648
- selectedRecords.forEach(selected => {
649
- var index = records.indexOf(selected)
650
- var record = _records[index]
651
- if (record) {
652
- record['__selected__'] = true
653
- }
654
- })
655
-
656
- /* update _data property intentionally */
657
- this.refresh()
658
- }
659
-
660
- if (changes.has('urlParamsSensitive')) {
661
- if (this.urlParamsSensitive) {
662
- //@ts-ignore
663
- this.popstateEventHandler() // call for the first time
664
-
665
- window.addEventListener('popstate', this.popstateEventHandler)
666
- } else {
667
- window.removeEventListener('popstate', this.popstateEventHandler)
668
- }
669
- }
670
-
671
- if (needToSetPullToRefresh) {
672
- await this.setPullToRefresh()
673
- }
674
- }
675
-
676
- /**
677
- * Represents the compiled configuration of the component, which includes various settings and
678
- * column configurations. You can access this property to get information about how the component
679
- * is configured.
680
- *
681
- * @getter
682
- * @public
683
- * @type {GristConfig}
684
- */
685
- get compiledConfig(): GristConfig {
686
- return this._config
687
- }
688
-
689
- /**
690
- * Returns the dirty data in the component, which includes the records that have been added,
691
- * modified, or deleted but have not been committed to the main data yet.
692
- *
693
- * @getter
694
- * @public
695
- * @type {GristData} - An object representing the dirty data.
696
- */
697
- get dirtyData(): GristData {
698
- return (this.grist as any)?.data || {}
699
- }
700
-
701
- /**
702
- * Returns an array of GristRecord objects representing the records in the dirty state. These are
703
- * the records that have been added, modified, or deleted but have not been committed to the main
704
- * data yet.
705
- *
706
- * @getter
707
- * @public
708
- * @type {GristRecord[]} - An array of GristRecord objects representing the dirty records.
709
- */
710
- get dirtyRecords() {
711
- var { records = [] } = this.dirtyData
712
- return records.filter(record => record['__dirty__'])
713
- }
714
-
715
- /**
716
- * Exports a list of patches representing the changes in the dirty state of records. Each patch
717
- * contains information about whether a record was added, modified, or deleted, along with the
718
- * record's unique identifier and the changed field values.
719
- *
720
- * @param {Object} options - Export options that control the format of the patch list.
721
- * @param {string} options.flagName - The name of the flag field in the patch indicating the change type (default: 'patchFlag').
722
- * @param {string} options.addedFlag - The flag value for added records (default: '+').
723
- * @param {string} options.deletedFlag - The flag value for deleted records (default: '-').
724
- * @param {string} options.modifiedFlag - The flag value for modified records (default: 'M').
725
- * @param {string} options.idField - The name of the unique identifier field (default: 'id').
726
- * @returns {Object[]} - An array of objects representing the patches.
727
- */
728
- exportPatchList({ flagName = 'patchFlag', addedFlag = '+', deletedFlag = '-', modifiedFlag = 'M', idField = 'id' }) {
729
- let dirtyRecords = this.dirtyRecords
730
- if (!dirtyRecords || dirtyRecords.length == 0) {
731
- return []
732
- }
733
-
734
- return dirtyRecords.map(record => {
735
- let flag = record.__dirty__
736
-
737
- let patch = {
738
- [flagName]: flag == 'M' ? modifiedFlag : flag == '+' ? addedFlag : deletedFlag
739
- }
740
-
741
- if (idField in record && record[idField]) {
742
- patch[idField] = record[idField]
743
- }
744
-
745
- for (let key in record.__dirtyfields__) {
746
- patch[key] = record[key]
747
- }
748
- return patch
749
- })
750
- }
751
-
752
- /**
753
- * Exports the selected records or all records in the component, depending on the specified options.
754
- * You can use this method to export data from the component in various formats or for different purposes.
755
- *
756
- * @param {Object} options - Export options that control the behavior of the export.
757
- * @param {boolean} options.ifSelectedOnly - If true, exports only the selected records. If false, exports all records.
758
- * @param {boolean} options.includeHiddenField - If true, includes hidden fields in the exported data.
759
- * @returns {Object[]} - An array of objects representing the exported records.
760
- */
761
- exportRecords({ ifSelectedOnly = true, includeHiddenField = true } = {}) {
762
- let records = ifSelectedOnly ? this.selected : this.data.records
763
-
764
- if (ifSelectedOnly && (!records || records.length == 0)) {
765
- records = this.data.records
766
- }
767
-
768
- let columns = this.compiledConfig.columns.filter(column => column.type !== 'gutter')
769
- if (!includeHiddenField) {
770
- columns = columns.filter(column => !column.hidden)
771
- }
772
- let columnNames = columns.map(column => column.name)
773
-
774
- return records.map(item => {
775
- return columnNames.reduce((record, name) => {
776
- record[name] = item[name]
777
- return record
778
- }, {} as any)
779
- })
780
- }
781
-
782
- /**
783
- * Gets the currently selected records in the component. It returns an array of GristRecord objects
784
- * that are currently selected. You can access this getter to retrieve the selected records.
785
- *
786
- * @getter
787
- * @public
788
- * @type {GristRecord[]}
789
- */
790
- get selected() {
791
- var { records = [] } = this.grist?.data
792
- return records.filter(record => record['__selected__'])
793
- }
794
-
795
- /**
796
- * Sets the currently selected records in the component. You can use this setter to programmatically
797
- * select specific records by providing an array of GristRecord objects to be selected.
798
- *
799
- * @setter
800
- * @public
801
- * @type {GristRecord[]}
802
- */
803
- set selected(selected: GristRecord[]) {
804
- if (!this.grist) {
805
- console.warn('grist not ready')
806
- return
807
- }
808
-
809
- selected.forEach(record => (record.__selected__ = true))
810
- this.refresh()
811
- }
812
-
813
- /**
814
- * Selects records in the component based on the provided selector function. You can use this method
815
- * to programmatically select specific records in the component.
816
- *
817
- * @method
818
- * @param {GristSelectFunction} selector - A function that determines which records to select.
819
- * @param {boolean} reset - If true, clears the previous selection before applying the new one.
820
- * If false, adds to the existing selection.
821
- */
822
- select(selector: GristSelectFunction, reset: boolean = false) {
823
- var { records = [] } = this.grist?.data
824
-
825
- if (reset) {
826
- this.selected.forEach(record => (record.__selected__ = false))
827
- }
828
-
829
- records.filter(record => selector(record)).forEach(record => (record.__selected__ = true))
830
- this.refresh()
831
- }
832
-
833
- /**
834
- * Shows the loading spinner in the component's UI to indicate ongoing data loading or processing.
835
- * You can call this method to display the spinner when necessary.
836
- */
837
- showSpinner() {
838
- this._showSpinner = true
839
- }
840
-
841
- /**
842
- * Hides the loading spinner in the component's UI to indicate that data loading or processing has completed.
843
- * You can call this method to hide the spinner when loading or processing is finished.
844
- */
845
- hideSpinner() {
846
- this._showSpinner = false
847
- }
848
-
849
- /**
850
- * Focuses on the component, making it the active element in the document. This method is useful
851
- * when you want to programmatically set focus to the component.
852
- */
853
- focus() {
854
- super.focus()
855
-
856
- this.grist.focus()
857
- }
858
-
859
- /**
860
- * Commits the changes made in the dirty state to the component's data. This method updates the
861
- * component's data with the changes made in the dirty state and clears the dirty state.
862
- */
863
- commit() {
864
- var { page, total, limit, records } = this.grist.data
865
-
866
- this.data = {
867
- page,
868
- total,
869
- limit,
870
- records: records.map(record => {
871
- var copied = {
872
- ...record
873
- }
874
-
875
- delete copied.__seq__
876
- delete copied.__dirty__
877
- delete copied.__selected__
878
- delete copied.__changes__
879
- delete copied.__dirtyfields__
880
- delete copied.__origin__
881
- delete copied.__depth__
882
- delete copied.__expanded__
883
- delete copied.__check_in_tree__
884
- delete copied.__children__
885
- delete copied.__typename
886
-
887
- return copied
888
- })
889
- }
890
- }
891
-
892
- /**
893
- * Shows the headroom element in the component. The headroom element is typically used for
894
- * displaying additional information or controls at the top of the component.
895
- */
896
- showHeadroom() {
897
- if (this.head) {
898
- this.head.style.display = 'block'
899
- this.setHeadroom()
900
- }
901
- }
902
-
903
- /**
904
- * Hides the headroom element in the component. This method hides the additional information
905
- * or controls displayed at the top of the component.
906
- */
907
- hideHeadroom() {
908
- if (this.head) {
909
- this.head.style.display = 'none'
910
- this.setHeadroom()
911
- }
912
- }
913
-
914
- /**
915
- * Toggles the visibility of the headroom element in the component. If the headroom element is
916
- * currently visible, this method hides it. If it's hidden, this method shows it.
917
- */
918
- toggleHeadroom() {
919
- if (this.head) {
920
- const display = this.head.style.display
921
- this.head.style.display = display !== 'none' ? 'none' : 'block'
922
-
923
- this.setHeadroom()
924
- }
925
- }
926
-
927
- /**
928
- * Forced internal data to be reflected on the screen
929
- * Data changing through a normal method is automatically reflected on the screen, so it is a method that does not need to be used in general.
930
- * Therefore, it will be deprecated.
931
- * @method
932
- */
933
- refresh() {
934
- this.grist.refresh()
935
- }
936
-
937
- /**
938
- * Resets the component's data to its original state before any changes were made.
939
- * This method discards all unsaved changes and restores the data to its initial state.
940
- *
941
- * @method
942
- * @public
943
- */
944
- reset() {
945
- // TODO tree 형태의 데이타로 _data를 만들 때, children, collapsed 등을 감안한다.
946
- var {
947
- limit = ZERO_PAGINATION.limit,
948
- page = ZERO_PAGINATION.page,
949
- total = ZERO_PAGINATION.total,
950
- records = []
951
- } = this.data || ZERO_PAGINATION
952
-
953
- const { childrenProperty, expanded } = this.compiledConfig.tree
954
-
955
- /* 원본 데이타를 남기고, 복사본(_data)을 사용한다. */
956
- records = ([] as GristRecord[]).concat(
957
- ...records.map((record, idx) =>
958
- this.traverseReset(
959
- record,
960
- this.mode == 'GRID' ? (page - 1) * limit + idx + 1 : idx + 1,
961
- 0,
962
- childrenProperty,
963
- expanded as () => boolean
964
- )
965
- )
966
- )
967
-
968
- if (childrenProperty) {
969
- records = ([] as GristRecord[]).concat(...records.map(record => this.traverseExpanded(record)))
970
- }
971
-
972
- this._data = {
973
- limit,
974
- page,
975
- total,
976
- records
977
- }
978
-
979
- this.timeCapsule?.reset()
980
- this.snapshotTaker?.take(true)
981
- }
982
-
983
- private traverseReset(
984
- record: GristRecord,
985
- seq: number,
986
- __depth__: number,
987
- childrenProperty: string | undefined,
988
- expanded: () => boolean
989
- ): GristRecord {
990
- const copied = {
991
- ...record,
992
- __seq__: seq,
993
- __origin__: record
994
- }
995
-
996
- if (childrenProperty) {
997
- const children: GristRecord[] = record[childrenProperty!]
998
-
999
- const __expanded__ = (expanded as Function)(record)
1000
- const __children__ = (children || []).map(child =>
1001
- this.traverseReset(child, seq, __depth__ + 1, childrenProperty, expanded)
1002
- )
1003
-
1004
- Object.assign(copied, { __depth__, __children__, __expanded__ })
1005
- }
1006
-
1007
- return copied
1008
- }
1009
-
1010
- private traverseExpanded(record: GristRecord): GristRecord[] {
1011
- const { __expanded__, __children__ = [] } = record
1012
-
1013
- if (__expanded__ && __children__.length > 0) {
1014
- return [record].concat(...__children__.map(child => this.traverseExpanded(child)))
1015
- } else {
1016
- return [record]
1017
- }
1018
- }
1019
-
1020
- /**
1021
- * Checks for dirty records in the component's data and marks them as dirty.
1022
- * Dirty records are those that have unsaved changes.
1023
- */
1024
- checkDirties() {
1025
- const records = this.dirtyRecords
1026
- const { columns = [] } = this.compiledConfig || {}
1027
-
1028
- for (var record of records || []) {
1029
- var origin = record['__origin__'] || {}
1030
-
1031
- var dirtyFields = (record['__dirtyfields__'] = columns
1032
- .filter(column => column.type !== 'gutter' && !isEqual(origin[column.name], record[column.name]))
1033
- .reduce((sum, column) => {
1034
- var name = column.name
1035
-
1036
- sum[name] = {
1037
- before: origin[name],
1038
- after: record[name]
1039
- }
1040
-
1041
- return sum
1042
- }, {} as any))
1043
-
1044
- if (record['__dirty__'] == 'M' && isEmpty(dirtyFields)) {
1045
- delete record['__dirty__']
1046
- }
1047
- }
1048
-
1049
- this._data = { ...this.dirtyData }
1050
-
1051
- this.snapshotTaker?.touch()
1052
- }
1053
-
1054
- /**
1055
- * Clones the selected records in the component's data. It creates a copy of the selected records
1056
- * and marks them as new (added) records.
1057
- */
1058
- cloneSelectedRecords() {
1059
- const records = this.selected || ([] as GristRecord[])
1060
-
1061
- records.forEach(record => {
1062
- var cloned = {
1063
- __dirty__: '+'
1064
- } as GristRecord
1065
-
1066
- this.compiledConfig.columns
1067
- .filter(column => column.record.editable)
1068
- .forEach(column => {
1069
- cloned[column.name] = record[column.name]
1070
- })
1071
- const rowIndex = this.dirtyData.records.findIndex(rec => rec === record)
1072
-
1073
- this.dirtyData.records.splice(rowIndex + 1, 0, cloned)
1074
- })
1075
-
1076
- this.checkDirties()
1077
- }
1078
-
1079
- /**
1080
- * Adds child nodes to selected records in the component's tree data. It allows users to add child nodes
1081
- * to the selected parent records.
1082
- */
1083
- addChildNodes() {
1084
- const records = this.selected || ([] as GristRecord[])
1085
-
1086
- records.forEach(record => {
1087
- this.grist.addChildNode(record)
1088
- })
1089
- }
1090
-
1091
- /**
1092
- * Adds sibling nodes to selected records in the component's tree data. It allows users to add sibling nodes
1093
- * to the selected records.
1094
- */
1095
- addSiblingNodes() {
1096
- const records = this.selected || ([] as GristRecord[])
1097
-
1098
- records.forEach(record => {
1099
- this.grist.addSiblingNode(record)
1100
- })
1101
- }
1102
-
1103
- /**
1104
- * Deletes the selected records in the component's data. It removes the selected records from the data,
1105
- * optionally marking them as deleted.
1106
- *
1107
- * @method
1108
- * @param {boolean} dirty - If true, the method marks the records as deleted.
1109
- */
1110
- deleteSelectedRecords(dirty = true) {
1111
- const records = this.selected || ([] as GristRecord[])
1112
-
1113
- records.forEach(record => {
1114
- if (dirty) {
1115
- record.__dirty__ = '-'
1116
- }
1117
-
1118
- const rowIndex = this.dirtyData.records.findIndex(rec => rec === record)
1119
- this.dirtyData.records.splice(rowIndex, 1)
1120
- })
1121
-
1122
- this.checkDirties()
1123
- }
1124
-
1125
- /**
1126
- * Adds a new record to the data grid. The added record is marked as newly created
1127
- * by setting the `__dirty__` flag to '+'. This flag indicates that the record
1128
- * is in a "new" state and hasn't been committed to the main data yet.
1129
- *
1130
- * @param {GristRecord} [record] - An optional record to add. If no record is provided,
1131
- * an empty record with the `__dirty__` flag set to '+' will be added.
1132
- */
1133
- addRecord(record?: GristRecord) {
1134
- this._data = {
1135
- ...this._data,
1136
- records: [
1137
- ...this.dirtyData.records,
1138
- {
1139
- ...record,
1140
- __dirty__: '+'
1141
- }
1142
- ]
1143
- }
1144
- }
1145
-
1146
- /**
1147
- * Retrieves the search text used for filtering records.
1148
- *
1149
- * @property {string}
1150
- */
1151
- get searchText() {
1152
- return (this.filters?.find(filter => filter.operator === 'search')?.value as string)?.match(/^\%(.*)\%$/)?.[1] || ''
1153
- }
1154
-
1155
- /**
1156
- * Sets the search text for filtering records.
1157
- *
1158
- * @property {string}
1159
- */
1160
- set searchText(searchText: string) {
1161
- var filters = (this.filters || []).filter((filter: FilterValue) => filter.operator !== 'search')
1162
-
1163
- if (searchText) {
1164
- const filtersConfig = this.compiledConfig.columns.filter(columnConfig => !!columnConfig.filter)
1165
- const searchColumns = filtersConfig.filter(columnConfig => {
1166
- const filter = columnConfig.filter as FilterConfigObject
1167
- return filter!.operator === 'search'
1168
- })
1169
-
1170
- filters = [
1171
- ...searchColumns.map((column: ColumnConfig) => {
1172
- const { name } = column
1173
-
1174
- return {
1175
- name,
1176
- operator: 'search',
1177
- value: `%${searchText}%`
1178
- } as FilterValue
1179
- }),
1180
- ...filters
1181
- ]
1182
- }
1183
-
1184
- this.grist.dispatchEvent(
1185
- new CustomEvent('fetch-params-change', {
1186
- bubbles: true,
1187
- composed: true,
1188
- detail: {
1189
- filters
1190
- }
1191
- })
1192
- )
1193
- }
1194
- /**
1195
- * Returns the current pagination limit.
1196
- * @returns {number} The current pagination limit value
1197
- */
1198
- getCurrentLimit() {
1199
- return this.dataProvider?.limit || ZERO_PAGINATION.limit
1200
- }
1201
-
1202
- /**
1203
- * Checks the validity of dirty records.
1204
- * @returns {Array<{record: GristRecord, invalidFields: Array<{field: string, reason: ValidationReason}>}>} List of invalid records and their corresponding invalid fields
1205
- */
1206
- checkDirtyRecordsValidity(): { record: GristRecord; invalidFields: { field: string; reason: ValidationReason }[] }[] {
1207
- const records = this.dirtyRecords
1208
- const validationResults = []
1209
-
1210
- for (const record of records) {
1211
- const invalidFields = []
1212
-
1213
- for (const column of this.compiledConfig.columns) {
1214
- if (column.record?.mandatory && (record[column.name] === undefined || record[column.name] === null)) {
1215
- invalidFields.push({
1216
- field: column.name,
1217
- reason: ValidationReason.MANDATORY
1218
- })
1219
- }
1220
- // Additional validation rules can be implemented here.
1221
- }
1222
-
1223
- if (invalidFields.length > 0) {
1224
- validationResults.push({
1225
- record,
1226
- invalidFields
1227
- })
1228
- }
1229
- }
1230
-
1231
- return validationResults
1232
- }
1233
- }