@operato/scene-grist 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +16 -0
- package/LICENSE +21 -0
- package/README.md +0 -0
- package/dist/editors/index.d.ts +2 -0
- package/dist/editors/index.js +2 -0
- package/dist/editors/index.js.map +1 -0
- package/dist/grist-action.d.ts +82 -0
- package/dist/grist-action.js +322 -0
- package/dist/grist-action.js.map +1 -0
- package/dist/grist.d.ts +56 -0
- package/dist/grist.js +258 -0
- package/dist/grist.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/templates/grist-action.d.ts +17 -0
- package/dist/templates/grist-action.js +19 -0
- package/dist/templates/grist-action.js.map +1 -0
- package/dist/templates/grist.d.ts +16 -0
- package/dist/templates/grist.js +98 -0
- package/dist/templates/grist.js.map +1 -0
- package/dist/templates/index.d.ts +30 -0
- package/dist/templates/index.js +4 -0
- package/dist/templates/index.js.map +1 -0
- package/global/index.d.ts +1 -0
- package/helps/scene/component/grist-action.ko.md +45 -0
- package/helps/scene/component/grist-action.md +43 -0
- package/helps/scene/component/grist-action.zh.md +44 -0
- package/helps/scene/component/grist.ko.md +26 -0
- package/helps/scene/component/grist.md +26 -0
- package/helps/scene/component/grist.zh.md +26 -0
- package/package.json +64 -0
- package/src/editors/index.ts +1 -0
- package/src/grist-action.ts +362 -0
- package/src/grist.ts +302 -0
- package/src/index.ts +3 -0
- package/src/templates/grist-action.png +0 -0
- package/src/templates/grist-action.ts +19 -0
- package/src/templates/grist.png +0 -0
- package/src/templates/grist.ts +98 -0
- package/src/templates/index.ts +3 -0
- package/src/uuid.d.ts +1 -0
- package/things-scene.config.js +7 -0
- package/translations/en.json +13 -0
- package/translations/ko.json +13 -0
- package/translations/ms.json +13 -0
- package/translations/zh.json +13 -0
- package/tsconfig.json +23 -0
- 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)
|