@operato/scene-grist 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/LICENSE +21 -0
  3. package/README.md +0 -0
  4. package/dist/editors/index.d.ts +2 -0
  5. package/dist/editors/index.js +2 -0
  6. package/dist/editors/index.js.map +1 -0
  7. package/dist/grist-action.d.ts +82 -0
  8. package/dist/grist-action.js +322 -0
  9. package/dist/grist-action.js.map +1 -0
  10. package/dist/grist.d.ts +56 -0
  11. package/dist/grist.js +258 -0
  12. package/dist/grist.js.map +1 -0
  13. package/dist/index.d.ts +4 -0
  14. package/dist/index.js +4 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/templates/grist-action.d.ts +17 -0
  17. package/dist/templates/grist-action.js +19 -0
  18. package/dist/templates/grist-action.js.map +1 -0
  19. package/dist/templates/grist.d.ts +16 -0
  20. package/dist/templates/grist.js +98 -0
  21. package/dist/templates/grist.js.map +1 -0
  22. package/dist/templates/index.d.ts +30 -0
  23. package/dist/templates/index.js +4 -0
  24. package/dist/templates/index.js.map +1 -0
  25. package/global/index.d.ts +1 -0
  26. package/helps/scene/component/grist-action.ko.md +45 -0
  27. package/helps/scene/component/grist-action.md +43 -0
  28. package/helps/scene/component/grist-action.zh.md +44 -0
  29. package/helps/scene/component/grist.ko.md +26 -0
  30. package/helps/scene/component/grist.md +26 -0
  31. package/helps/scene/component/grist.zh.md +26 -0
  32. package/package.json +64 -0
  33. package/src/editors/index.ts +1 -0
  34. package/src/grist-action.ts +362 -0
  35. package/src/grist.ts +302 -0
  36. package/src/index.ts +3 -0
  37. package/src/templates/grist-action.png +0 -0
  38. package/src/templates/grist-action.ts +19 -0
  39. package/src/templates/grist.png +0 -0
  40. package/src/templates/grist.ts +98 -0
  41. package/src/templates/index.ts +3 -0
  42. package/src/uuid.d.ts +1 -0
  43. package/things-scene.config.js +7 -0
  44. package/translations/en.json +13 -0
  45. package/translations/ko.json +13 -0
  46. package/translations/ms.json +13 -0
  47. package/translations/zh.json +13 -0
  48. package/tsconfig.json +23 -0
  49. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,26 @@
1
+ # grist
2
+
3
+ It is a component that expresses multiple record data in the form of a data grid or data list.
4
+ Data grid is suitable for web application UI, and data list is suitable for mobile application.
5
+ This can be set with the grist mode attribute.
6
+
7
+ ## Properties
8
+
9
+ - grist mode
10
+ - Grid: Data grid format that organizes multi-columns in a table format
11
+ - list: Data list format that composes multi-column information in the form of an item block
12
+ - Depends on device : Depending on the current display, it is automatically selected in the form of a data grid or a data list.
13
+ - config
14
+ - Configuration for grist
15
+ - It consists of column, header, record and pagination information.
16
+ - appendable
17
+ - Set whether to provide UI function so that new record can be added
18
+ - paginatable
19
+ - Set whether to provide the footer area UI function that provides pagination function in the footer area
20
+ - scale
21
+ - Set scale of grist content
22
+ - The minimum value is 0.1, and it can be increased by 0.1.
23
+ - Default value is 1
24
+ - bound data
25
+ - focused row : The current record selected or moved by the user with the mouse or keyboard is sent as the data attribute. Since only one record is focused, the data is in the form of a single object.
26
+ - selected rows : Records selected by the user through the selection checkbox are sent as data attributes. Since multiple records can be selected, the data becomes an array of records.
@@ -0,0 +1,26 @@
1
+ # grist
2
+
3
+ 它是一个以数据网格或数据列表的形式表示多个记录数据的组件。
4
+ 数据网格适用于 Web 应用程序 UI,数据列表适用于移动应用程序。
5
+ 可以使用 grist 模式属性进行移动端应用设置。
6
+
7
+ ## Properties
8
+
9
+ - 模式
10
+ - Grid: 数据网格格式,以表格格式组织多列
11
+ - list: 以项目块的形式组成多列信息的数据列表格式
12
+ - Depends on device : 根据当前设备自动选择数据网格或数据列表
13
+ - 配置
14
+ - 用于 grist 的配置
15
+ - 包含表列、表头、数据、分页信息
16
+ - 可增加新行
17
+ - 设置是否提供 UI 功能,以便可以添加新记录
18
+ - 可分页
19
+ - 设置是否提供在页脚区域中提供分页功能的页脚区域 UI 功能
20
+ - scale
21
+ - Set scale of grist content
22
+ - The minimum value is 0.1, and it can be increased by 0.1.
23
+ - Default value is 1
24
+ - 绑定数据
25
+ - focused row : 用户选择的或移动数据的属性将会传递出去,只传递一行数据
26
+ - selected rows : 用户选择的所有数据会将会传递出去,数据将会作为 Array 发送。
package/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "@operato/scene-grist",
3
+ "description": "Grist UI component for things-scene",
4
+ "license": "MIT",
5
+ "author": "heartyoh",
6
+ "version": "0.0.9",
7
+ "main": "dist/index.js",
8
+ "module": "dist/index.js",
9
+ "things-scene": true,
10
+ "publishConfig": {
11
+ "access": "public",
12
+ "@oprato:registry": "https://registry.npmjs.org"
13
+ },
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/things-scene/operato-scene.git",
17
+ "directory": "packages/grist"
18
+ },
19
+ "scripts": {
20
+ "serve": "tsc && things-factory-dev",
21
+ "start": "tsc && concurrently -k -r \"tsc --watch --preserveWatchOutput\" \"wds\"",
22
+ "build": "tsc",
23
+ "prepublish": "tsc",
24
+ "lint": "eslint --ext .ts,.html . --ignore-path .gitignore && prettier \"**/*.ts\" --check --ignore-path .gitignore",
25
+ "format": "eslint --ext .ts,.html . --fix --ignore-path .gitignore && prettier \"**/*.ts\" --write --ignore-path .gitignore",
26
+ "migration": "things-factory-migration"
27
+ },
28
+ "dependencies": {
29
+ "@operato/data-grist": "^0.2.35",
30
+ "uuid": "^3.4.0"
31
+ },
32
+ "devDependencies": {
33
+ "@hatiolab/prettier-config": "^1.0.0",
34
+ "@hatiolab/things-scene": "^2.7.20",
35
+ "@operato/board": "^0.2.35",
36
+ "@things-factory/builder": "^4.0.6",
37
+ "@things-factory/operato-board": "^4.0.6",
38
+ "@types/chance": "^1.1.3",
39
+ "@typescript-eslint/eslint-plugin": "^4.33.0",
40
+ "@typescript-eslint/parser": "^4.33.0",
41
+ "@web/dev-server": "^0.1.28",
42
+ "concurrently": "^5.3.0",
43
+ "eslint": "^7.32.0",
44
+ "eslint-config-prettier": "^8.3.0",
45
+ "husky": "^4.3.8",
46
+ "lint-staged": "^10.5.4",
47
+ "prettier": "^2.4.1",
48
+ "tslib": "^2.3.1",
49
+ "typescript": "^4.5.2"
50
+ },
51
+ "prettier": "@hatiolab/prettier-config",
52
+ "husky": {
53
+ "hooks": {
54
+ "pre-commit": "lint-staged"
55
+ }
56
+ },
57
+ "lint-staged": {
58
+ "*.ts": [
59
+ "eslint --fix",
60
+ "prettier --write"
61
+ ]
62
+ },
63
+ "gitHead": "594dd5222e6d477a4cac888d564814c3340e6523"
64
+ }
@@ -0,0 +1 @@
1
+ export default []
@@ -0,0 +1,362 @@
1
+ /*
2
+ * Copyright © HatioLab Inc. All rights reserved.
3
+ *
4
+ * grist 컴포넌트를 보조하여 grist의 각종 동작을 수행하는 컴포넌트.
5
+ */
6
+
7
+ import { Component, Properties, RectPath, ValueHolder } from '@hatiolab/things-scene'
8
+ import { GristData, GristRecord, SorterConfig } from '@operato/data-grist/src/types.js'
9
+
10
+ import { DataGrist } from '@operato/data-grist'
11
+ import SceneGrist from './grist'
12
+ import uuid from 'uuid'
13
+
14
+ export enum ACTIONS {
15
+ GET_ALL_ROWS = 'getAllRows',
16
+ COMMIT = 'commit',
17
+ GET_SELECTED = 'getSelectedRows',
18
+ GET_DIRTY = 'getDirtyRows',
19
+ ADD_ROW = 'addRow',
20
+ DELETE_SELECTED_ROWS = 'deleteSelectedRowsSoftly',
21
+ GET_PAGE_INFO = 'getPageInfo'
22
+ }
23
+
24
+ const NATURE = {
25
+ mutable: false,
26
+ resizable: true,
27
+ rotatable: true,
28
+ properties: [
29
+ {
30
+ // 대상 Grist
31
+ type: 'id-input',
32
+ label: 'target-grist',
33
+ name: 'target',
34
+ property: {
35
+ component: 'grist'
36
+ }
37
+ },
38
+ {
39
+ // 동작
40
+ type: 'select',
41
+ label: 'action',
42
+ name: 'action',
43
+ property: {
44
+ options: [
45
+ {
46
+ // 페이지네이션 정보 가져오기
47
+ display: 'Get page information',
48
+ value: ACTIONS.GET_PAGE_INFO
49
+ },
50
+ {
51
+ // 모든 레코드 데이터 가져오기
52
+ display: 'Get all rows',
53
+ value: ACTIONS.GET_ALL_ROWS
54
+ },
55
+ {
56
+ // 체크된 레코드 데이터 가져오기
57
+ display: 'Get selected rows',
58
+ value: ACTIONS.GET_SELECTED
59
+ },
60
+ {
61
+ // 변경 사항이 있는 데이터 가져오기
62
+ display: 'Get dirty rows',
63
+ value: ACTIONS.GET_DIRTY
64
+ },
65
+ {
66
+ // 행 추가
67
+ display: 'Add a row',
68
+ value: ACTIONS.ADD_ROW
69
+ },
70
+ {
71
+ // 선택 행 삭제
72
+ display: 'Delete selected rows',
73
+ value: ACTIONS.DELETE_SELECTED_ROWS
74
+ },
75
+ {
76
+ // 변경 사항을 데이터에 적용
77
+ display: 'Commit',
78
+ value: ACTIONS.COMMIT
79
+ }
80
+ ]
81
+ }
82
+ },
83
+ {
84
+ // 뷰어 시작 시 자동 실행 여부
85
+ type: 'checkbox',
86
+ label: 'run-at-startup',
87
+ name: 'runAtStartup'
88
+ },
89
+ {
90
+ // 행 추가 시의 포맷
91
+ type: 'textarea',
92
+ label: 'record-adder-format',
93
+ name: 'recordFormat'
94
+ }
95
+ ],
96
+ help: 'scene/component/grist-action'
97
+ }
98
+
99
+ export default class GristAction extends ValueHolder(RectPath(Component)) {
100
+ static get nature() {
101
+ return NATURE
102
+ }
103
+
104
+ // grist의 fetchHandler를 사용할 때 이 컴포넌트를 판별할 ID
105
+ private uuid = uuid.v4()
106
+ private _data: any
107
+
108
+ ready() {
109
+ // 뷰어 시작시에도 action 값이 getPageInfo로 되어 있을 경우 fetchHandler를 등록하기 위해 onchange를 호출함
110
+ this.onchange({ action: this.state.action })
111
+ if (this.state.runAtStartup) this.doAction()
112
+ }
113
+
114
+ dispose() {
115
+ super.dispose()
116
+ }
117
+
118
+ onclick() {
119
+ // 컴포넌트 클릭 시 동작
120
+ this.doAction()
121
+ }
122
+
123
+ onchange(after: Properties) {
124
+ // value 값이 바뀌면 동작
125
+ if ('value' in after) {
126
+ this.doAction()
127
+ }
128
+
129
+ // action 값이 바뀌면 getPageInfo인지 확인하고 grist에 fetchHandler를 등록하거나 폐기함
130
+ if ('action' in after) {
131
+ const gristComponent = this.targetGristComponent
132
+
133
+ if (gristComponent) {
134
+ if (after.action == ACTIONS.GET_PAGE_INFO)
135
+ //@ts-ignore
136
+ gristComponent.beforeFetchFuncs[this.uuid] = fetchedData => {
137
+ //@ts-ignore
138
+ this.data = this.getPageInfoFrom(null, fetchedData)
139
+ this.doDataMap()
140
+ }
141
+ //@ts-ignore
142
+ else delete gristComponent.beforeFetchFuncs[this.uuid]
143
+ }
144
+ }
145
+ }
146
+
147
+ // 데이터 매핑을 수동으로 실행하기 위해 기존의 메소드를 무효화
148
+ executeMappings() {
149
+ console.debug(
150
+ "[@operato/data-grist] The method 'executeMappings' is overriden in the component 'grist-action', because of to prevent executing when initialize."
151
+ )
152
+ }
153
+
154
+ // 데이터 매핑을 수동으로 조작하기 위한 새 메소드
155
+ doDataMap() {
156
+ super.executeMappings()
157
+ }
158
+
159
+ // action 값에 따라 동작
160
+ doAction(action?: ACTIONS) {
161
+ if (!this.app.isViewMode) {
162
+ return
163
+ }
164
+
165
+ // 파라미터가 명시되어있지 않으면 컴포넌트 속성에서 action 값을 가져옴
166
+ var { action: storedAction } = this.state
167
+ action = action || storedAction
168
+
169
+ // 대상 Grist 컴포넌트
170
+ var grist = this.targetGristElement
171
+ if (!grist) return
172
+
173
+ var data
174
+ switch (action) {
175
+ case ACTIONS.GET_ALL_ROWS:
176
+ data = grist.grist.data
177
+ break
178
+ case ACTIONS.COMMIT:
179
+ grist.commit()
180
+ break
181
+ case ACTIONS.GET_SELECTED:
182
+ data = {
183
+ patches: this.buildPatches(grist.selected),
184
+ original: grist.selected
185
+ }
186
+ break
187
+ case ACTIONS.GET_DIRTY:
188
+ data = this.assortDirties(grist)
189
+ break
190
+ case ACTIONS.ADD_ROW:
191
+ {
192
+ var records = grist.dirtyData.records || []
193
+
194
+ let recordFormat
195
+ try {
196
+ recordFormat = eval(`(${this.state.recordFormat})`)
197
+ } catch (e) {
198
+ console.log('Invalid JSON format. It will be assumed as empty object.\n', e)
199
+ recordFormat = {}
200
+ }
201
+ records.push({ ...recordFormat, __dirty__: '+' })
202
+ this.refreshGrist(grist)
203
+ }
204
+ break
205
+ case ACTIONS.DELETE_SELECTED_ROWS:
206
+ {
207
+ var records = grist.dirtyData.records || []
208
+
209
+ //@ts-ignore
210
+ records.forEach((record, idx) => {
211
+ if (record['__selected__']) {
212
+ if (record['__dirty__'] == '+') delete records[idx]
213
+ else record['__dirty__'] = '-'
214
+ }
215
+ })
216
+ grist.dirtyData.records = records.flat()
217
+ this.refreshGrist(grist)
218
+ }
219
+ break
220
+ case ACTIONS.GET_PAGE_INFO:
221
+ data = this.getPageInfoFrom(grist)
222
+ break
223
+ }
224
+
225
+ // 이 컴포넌트의 data 값이 바뀌는 동작은 데이터 매핑까지 실행함
226
+ if (data) {
227
+ this.data = data
228
+ this.doDataMap()
229
+ }
230
+ }
231
+
232
+ // 대상 grist 컴포넌트의 레코드를 새로고침
233
+ refreshGrist(grist: DataGrist) {
234
+ grist = grist || this.targetGristElement
235
+ if (!grist) return
236
+
237
+ //@ts-ignore
238
+ grist.dataProvider.onRecordChange()
239
+ grist.grist.data = { ...grist.dirtyData }
240
+ }
241
+
242
+ // 변경 사항이 있는 레코드의 경우, CUD를 분류해서 반환함
243
+ assortDirties(grist: DataGrist) {
244
+ const dirties = grist.dirtyRecords
245
+ var patches = this.buildPatches(dirties)
246
+ var records = {
247
+ original: dirties,
248
+ patches,
249
+ created: [] as GristRecord[],
250
+ updated: [] as GristRecord[],
251
+ deleted: [] as GristRecord[]
252
+ }
253
+
254
+ patches.forEach((record: GristRecord) => {
255
+ switch (record['cuFlag']) {
256
+ case 'M':
257
+ records.updated.push(record)
258
+ break
259
+ case '+':
260
+ records.created.push(record)
261
+ break
262
+ case '-':
263
+ records.deleted.push(record)
264
+ break
265
+ }
266
+ })
267
+ return records
268
+ }
269
+
270
+ // 페이지네이션 정보를 가져옴
271
+ getPageInfoFrom(grist: DataGrist, fetchedData?: GristData) {
272
+ //@ts-ignore
273
+ var { page = 1, limit = 20, sorters = [] } = fetchedData || (grist && grist.dataProvider) || pagination(grist)
274
+
275
+ sorters = sorters.map((sorter: SorterConfig) => {
276
+ sorter.desc = sorter.desc ? true : false
277
+ return sorter
278
+ })
279
+ return { page, limit, sorters }
280
+
281
+ function pagination(grist: DataGrist) {
282
+ var config = grist && grist.config && grist.config.pagination
283
+ if (config)
284
+ return {
285
+ page: config.page,
286
+ limit: config.limit || (config.pages && config.pages[0])
287
+ }
288
+ else return {}
289
+ }
290
+ }
291
+
292
+ // 레코드들을 서버 공통 resolver에 맞는 포맷으로 만듦
293
+ buildPatches(patches: GristRecord[]): GristRecord[] {
294
+ return patches.map(patch => {
295
+ let patchField: { [key: string]: any } = patch.id ? { id: patch.id } : {}
296
+ const dirtyFields = patch.__dirtyfields__
297
+
298
+ for (let key in dirtyFields) {
299
+ patchField[key] = dirtyFields[key].after
300
+ }
301
+ patchField.cuFlag = patch.__dirty__
302
+
303
+ return patchField
304
+ })
305
+ }
306
+
307
+ render(context: CanvasRenderingContext2D) {
308
+ var { top, left, height, width, fillStyle = 'transparent' } = this.model
309
+
310
+ // background의 색상
311
+ context.beginPath()
312
+ context.rect(left, top, width, height)
313
+
314
+ context.fillStyle = fillStyle
315
+ context.fill()
316
+
317
+ // value의 색상
318
+ context.beginPath()
319
+
320
+ var drawValue = width - (width * Math.max(Math.min(this.animValue, 100), 0)) / 100
321
+ drawValue = Math.max(Math.min(drawValue, width), 0)
322
+
323
+ context.rect(left + drawValue, top, width - drawValue, height)
324
+
325
+ this.drawFill(context)
326
+
327
+ context.closePath()
328
+
329
+ context.beginPath()
330
+
331
+ context.rect(left, top, width, height)
332
+ }
333
+
334
+ postrender(context: CanvasRenderingContext2D) {
335
+ this.drawStroke(context)
336
+ this.drawText(context)
337
+ }
338
+
339
+ get controls() {
340
+ return []
341
+ }
342
+
343
+ get targetGristComponent(): SceneGrist {
344
+ var { target } = this.state
345
+ return this.root.findById(target) as SceneGrist
346
+ }
347
+
348
+ get targetGristElement(): DataGrist | undefined {
349
+ return this.targetGristComponent?.grist
350
+ }
351
+
352
+ // @ts-ignore
353
+ get data() {
354
+ return this._data
355
+ }
356
+
357
+ set data(data) {
358
+ this._data = data
359
+ }
360
+ }
361
+
362
+ Component.register('grist-action', GristAction)