@operato/dataset 1.5.56 → 1.7.0
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.
- package/CHANGELOG.md +18 -0
- package/dist/src/ox-data-entry-form.d.ts +8 -2
- package/dist/src/ox-data-entry-form.js +67 -3
- package/dist/src/ox-data-entry-form.js.map +1 -1
- package/dist/src/ox-data-entry-subgroup-form.d.ts +33 -0
- package/dist/src/ox-data-entry-subgroup-form.js +140 -0
- package/dist/src/ox-data-entry-subgroup-form.js.map +1 -0
- package/dist/src/ox-data-sample-subgroup-view.js +2 -0
- package/dist/src/ox-data-sample-subgroup-view.js.map +1 -1
- package/dist/stories/ox-data-entry-form.stories.js +7 -0
- package/dist/stories/ox-data-entry-form.stories.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/ox-data-entry-form.ts +77 -5
- package/src/ox-data-entry-subgroup-form.ts +146 -0
- package/src/ox-data-sample-subgroup-view.ts +2 -0
- package/stories/ox-data-entry-form.stories.ts +7 -0
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@operato/dataset",
|
|
3
3
|
"description": "WebApplication dataset supporting components following open-wc recommendations",
|
|
4
4
|
"author": "heartyoh",
|
|
5
|
-
"version": "1.
|
|
5
|
+
"version": "1.7.0",
|
|
6
6
|
"main": "dist/src/index.js",
|
|
7
7
|
"module": "dist/src/index.js",
|
|
8
8
|
"exports": {
|
|
@@ -137,5 +137,5 @@
|
|
|
137
137
|
"prettier --write"
|
|
138
138
|
]
|
|
139
139
|
},
|
|
140
|
-
"gitHead": "
|
|
140
|
+
"gitHead": "c81df18d34366841dec7a36dbfa1a42252b8aa4c"
|
|
141
141
|
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import '@operato/input/ox-input-file.js'
|
|
2
|
+
import './ox-data-entry-subgroup-form.js'
|
|
2
3
|
|
|
3
|
-
import { css, html, LitElement } from 'lit'
|
|
4
|
-
import { customElement, property } from 'lit/decorators.js'
|
|
4
|
+
import { css, html, LitElement, TemplateResult } from 'lit'
|
|
5
|
+
import { customElement, property, queryAll } from 'lit/decorators.js'
|
|
5
6
|
|
|
6
|
-
import { DataSet } from './types.js'
|
|
7
|
+
import { DataSet, DataItem, DataSample } from './types.js'
|
|
8
|
+
import { OxDataEntrySubgroupForm } from './ox-data-entry-subgroup-form.js'
|
|
7
9
|
|
|
8
10
|
@customElement('ox-data-entry-form')
|
|
9
11
|
export class OxDataEntryForm extends LitElement {
|
|
@@ -22,6 +24,7 @@ export class OxDataEntryForm extends LitElement {
|
|
|
22
24
|
text-transform: capitalize;
|
|
23
25
|
text-align: center;
|
|
24
26
|
}
|
|
27
|
+
|
|
25
28
|
h3 {
|
|
26
29
|
margin: var(--page-description-margin);
|
|
27
30
|
font: var(--page-description-font);
|
|
@@ -36,6 +39,7 @@ export class OxDataEntryForm extends LitElement {
|
|
|
36
39
|
display: flex;
|
|
37
40
|
flex-direction: column;
|
|
38
41
|
}
|
|
42
|
+
|
|
39
43
|
label {
|
|
40
44
|
display: grid;
|
|
41
45
|
|
|
@@ -47,6 +51,7 @@ export class OxDataEntryForm extends LitElement {
|
|
|
47
51
|
align-items: center;
|
|
48
52
|
margin-bottom: var(--margin-default);
|
|
49
53
|
}
|
|
54
|
+
|
|
50
55
|
label:nth-child(odd) {
|
|
51
56
|
background-color: var(--main-section-background-color);
|
|
52
57
|
padding: var(--padding-default) 0;
|
|
@@ -65,13 +70,16 @@ export class OxDataEntryForm extends LitElement {
|
|
|
65
70
|
color: var(--item-description-color);
|
|
66
71
|
text-align: left;
|
|
67
72
|
}
|
|
73
|
+
|
|
68
74
|
div[description] * {
|
|
69
75
|
vertical-align: middle;
|
|
70
76
|
}
|
|
77
|
+
|
|
71
78
|
div[description] mwc-icon {
|
|
72
79
|
margin-top: -3px;
|
|
73
80
|
font-size: 0.9rem;
|
|
74
81
|
}
|
|
82
|
+
|
|
75
83
|
div[elements] {
|
|
76
84
|
grid-area: inputs;
|
|
77
85
|
display: flex;
|
|
@@ -80,9 +88,11 @@ export class OxDataEntryForm extends LitElement {
|
|
|
80
88
|
gap: 10px;
|
|
81
89
|
padding-right: var(--padding-default);
|
|
82
90
|
}
|
|
91
|
+
|
|
83
92
|
div[elements] * {
|
|
84
93
|
flex: 1;
|
|
85
94
|
}
|
|
95
|
+
|
|
86
96
|
div[elements] input,
|
|
87
97
|
div[elements] select {
|
|
88
98
|
border: var(--input-field-border);
|
|
@@ -91,6 +101,11 @@ export class OxDataEntryForm extends LitElement {
|
|
|
91
101
|
font: var(--input-field-font);
|
|
92
102
|
}
|
|
93
103
|
|
|
104
|
+
div[subgroup] {
|
|
105
|
+
grid-column: 1 / 3;
|
|
106
|
+
grid-row: 2;
|
|
107
|
+
}
|
|
108
|
+
|
|
94
109
|
@media only screen and (max-width: 460px) {
|
|
95
110
|
label {
|
|
96
111
|
display: grid;
|
|
@@ -113,6 +128,8 @@ export class OxDataEntryForm extends LitElement {
|
|
|
113
128
|
@property({ type: Object }) dataSet?: DataSet
|
|
114
129
|
@property({ type: Object }) value?: { [tag: string]: any }
|
|
115
130
|
|
|
131
|
+
@queryAll('ox-data-entry-subgroup-form') subgroups!: NodeListOf<OxDataEntrySubgroupForm>
|
|
132
|
+
|
|
116
133
|
render() {
|
|
117
134
|
return html` <form @change=${(e: Event) => this.onChange(e)}>
|
|
118
135
|
<h2>${this.dataSet?.name || ''}</h2>
|
|
@@ -134,8 +151,37 @@ export class OxDataEntryForm extends LitElement {
|
|
|
134
151
|
}
|
|
135
152
|
|
|
136
153
|
private buildInputs() {
|
|
137
|
-
const dataItems = this.dataSet?.dataItems.filter(item => item.active)
|
|
154
|
+
const dataItems = this.dataSet?.dataItems.filter(item => item.active) || []
|
|
155
|
+
const nonGroupDataItems = dataItems.filter(dataItem => !dataItem.group)
|
|
156
|
+
const dataItemSubgroups = Object.entries(this.groupDataItemsByGroup(dataItems)).map(([subgroup, dataItems]) => {
|
|
157
|
+
const tags = dataItems.map(dataItem => dataItem.tag)
|
|
158
|
+
const value = tags.reduce((partial, key) => {
|
|
159
|
+
partial[key] = this.value?.[key]
|
|
160
|
+
return partial
|
|
161
|
+
}, {} as any)
|
|
162
|
+
|
|
163
|
+
return this.buildInputs4Subgroup(subgroup, dataItems, value)
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
return [...this.buildInputs4NonGrouped(nonGroupDataItems), ...dataItemSubgroups]
|
|
167
|
+
}
|
|
138
168
|
|
|
169
|
+
private buildInputs4Subgroup(subgroup: string, dataItems: DataItem[], value: { [tag: string]: any }): TemplateResult {
|
|
170
|
+
return html`
|
|
171
|
+
<label>
|
|
172
|
+
<div name>${subgroup}</div>
|
|
173
|
+
<div subgroup>
|
|
174
|
+
<ox-data-entry-subgroup-form
|
|
175
|
+
.subgroup=${subgroup}
|
|
176
|
+
.dataItems=${dataItems}
|
|
177
|
+
.value=${value}
|
|
178
|
+
></ox-data-entry-subgroup-form>
|
|
179
|
+
</div>
|
|
180
|
+
</label>
|
|
181
|
+
`
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
private buildInputs4NonGrouped(dataItems: DataItem[]): TemplateResult[] {
|
|
139
185
|
return (dataItems || []).map(dataItem => {
|
|
140
186
|
const { name, description, tag, type, quota = 1, options = {}, unit } = dataItem
|
|
141
187
|
|
|
@@ -190,8 +236,9 @@ export class OxDataEntryForm extends LitElement {
|
|
|
190
236
|
|
|
191
237
|
private buildValue() {
|
|
192
238
|
const dataItems = this.dataSet!.dataItems
|
|
239
|
+
const nonGroupDataItems = dataItems.filter(dataItem => !dataItem.group)
|
|
193
240
|
|
|
194
|
-
|
|
241
|
+
const nonGroupValue = (nonGroupDataItems || []).reduce((sum, dataItem) => {
|
|
195
242
|
const { tag, type } = dataItem
|
|
196
243
|
|
|
197
244
|
const editors = Array.prototype.slice.call(
|
|
@@ -204,5 +251,30 @@ export class OxDataEntryForm extends LitElement {
|
|
|
204
251
|
|
|
205
252
|
return sum
|
|
206
253
|
}, {} as { [tag: string]: any })
|
|
254
|
+
|
|
255
|
+
return Array.from(this.subgroups).reduce((value, subgroup) => {
|
|
256
|
+
return {
|
|
257
|
+
...value,
|
|
258
|
+
...subgroup.buildValue()
|
|
259
|
+
}
|
|
260
|
+
}, nonGroupValue || {})
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
private groupDataItemsByGroup(dataItems: DataItem[]): { [group: string]: DataItem[] } {
|
|
264
|
+
const groupedDataItems: { [group: string]: DataItem[] } = {}
|
|
265
|
+
|
|
266
|
+
for (const dataItem of dataItems) {
|
|
267
|
+
const { group } = dataItem
|
|
268
|
+
|
|
269
|
+
if (group) {
|
|
270
|
+
if (!groupedDataItems[group]) {
|
|
271
|
+
groupedDataItems[group] = []
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
groupedDataItems[group].push(dataItem)
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return groupedDataItems
|
|
207
279
|
}
|
|
208
280
|
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import '@operato/input/ox-input-file.js'
|
|
2
|
+
import '@operato/data-grist/ox-grist.js'
|
|
3
|
+
|
|
4
|
+
import { css, html, LitElement } from 'lit'
|
|
5
|
+
import { customElement, property, state, query } from 'lit/decorators.js'
|
|
6
|
+
|
|
7
|
+
import { ScrollbarStyles } from '@operato/styles'
|
|
8
|
+
import { isMobileDevice } from '@operato/utils'
|
|
9
|
+
|
|
10
|
+
import { DataItem } from './types.js'
|
|
11
|
+
import { DataGrist, FetchOption } from '@operato/data-grist'
|
|
12
|
+
|
|
13
|
+
@customElement('ox-data-entry-subgroup-form')
|
|
14
|
+
export class OxDataEntrySubgroupForm extends LitElement {
|
|
15
|
+
static styles = [
|
|
16
|
+
ScrollbarStyles,
|
|
17
|
+
css`
|
|
18
|
+
:host {
|
|
19
|
+
display: flex;
|
|
20
|
+
flex-direction: column;
|
|
21
|
+
|
|
22
|
+
width: 100%;
|
|
23
|
+
min-height: 100px;
|
|
24
|
+
}
|
|
25
|
+
`
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
@property({ type: String }) subgroup?: string
|
|
29
|
+
@property({ type: Array }) dataItems?: DataItem[]
|
|
30
|
+
@property({ type: Object }) value?: { [tag: string]: any }
|
|
31
|
+
|
|
32
|
+
@state() gristConfig = this.buildGristConfiguration()
|
|
33
|
+
@query('ox-grist') grist!: DataGrist
|
|
34
|
+
|
|
35
|
+
render() {
|
|
36
|
+
return html`
|
|
37
|
+
<ox-grist
|
|
38
|
+
.mode=${isMobileDevice() ? 'LIST' : 'GRID'}
|
|
39
|
+
.config=${this.buildGristConfiguration()}
|
|
40
|
+
.fetchHandler=${this.fetchHandler.bind(this)}
|
|
41
|
+
>
|
|
42
|
+
</ox-grist>
|
|
43
|
+
`
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
private buildGristConfiguration() {
|
|
47
|
+
const columns = (this.dataItems || []).map(dataItem => {
|
|
48
|
+
const { name, description, tag, type, options = {}, unit } = dataItem
|
|
49
|
+
const columnConfig = {
|
|
50
|
+
type,
|
|
51
|
+
name: tag,
|
|
52
|
+
header: `${name}${unit ? ` (${unit})` : ''}`,
|
|
53
|
+
record: {
|
|
54
|
+
editable: true
|
|
55
|
+
},
|
|
56
|
+
width: 200
|
|
57
|
+
} as any
|
|
58
|
+
|
|
59
|
+
switch (type) {
|
|
60
|
+
case 'select':
|
|
61
|
+
columnConfig.record.options = [
|
|
62
|
+
'',
|
|
63
|
+
...(options.options || []).map((option: any) => {
|
|
64
|
+
if (typeof option == 'string') {
|
|
65
|
+
return option
|
|
66
|
+
}
|
|
67
|
+
const { display, text, value } = option || {}
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
display: display || text,
|
|
71
|
+
value
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
]
|
|
75
|
+
return columnConfig
|
|
76
|
+
|
|
77
|
+
case 'boolean':
|
|
78
|
+
columnConfig.record.align = 'center'
|
|
79
|
+
return columnConfig
|
|
80
|
+
|
|
81
|
+
case 'number':
|
|
82
|
+
columnConfig.record.align = 'right'
|
|
83
|
+
return columnConfig
|
|
84
|
+
|
|
85
|
+
case 'date':
|
|
86
|
+
return columnConfig
|
|
87
|
+
|
|
88
|
+
case 'datetime':
|
|
89
|
+
return columnConfig
|
|
90
|
+
|
|
91
|
+
case 'file':
|
|
92
|
+
return columnConfig
|
|
93
|
+
|
|
94
|
+
case 'string':
|
|
95
|
+
return columnConfig
|
|
96
|
+
|
|
97
|
+
default:
|
|
98
|
+
return columnConfig
|
|
99
|
+
}
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
list: { fields: ['name', 'data'] },
|
|
104
|
+
columns: [{ type: 'gutter', gutterName: 'sequence' }, ...columns],
|
|
105
|
+
rows: {
|
|
106
|
+
appendable: true
|
|
107
|
+
},
|
|
108
|
+
pagination: { infinite: true }
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async fetchHandler({ page = 1, limit = 100, sortings = [], filters = [] }: FetchOption) {
|
|
113
|
+
const length = Object.entries(this.value || {}).reduce((max, [tag, value]) => {
|
|
114
|
+
return Math.max(max, Array.isArray(value) ? value.length : 1)
|
|
115
|
+
}, 0)
|
|
116
|
+
|
|
117
|
+
const tags = (this.dataItems || []).map(dataItem => dataItem.tag)
|
|
118
|
+
|
|
119
|
+
const records = Array.from({ length }, (_, index) => index).map(index => {
|
|
120
|
+
return tags.reduce((partial, tag) => {
|
|
121
|
+
const v = this.value?.[tag]
|
|
122
|
+
partial[tag] = Array.isArray(v) ? v[index] : index == 0 ? v : undefined
|
|
123
|
+
return partial
|
|
124
|
+
}, {} as any)
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
total: records.length || 0,
|
|
129
|
+
records: records
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
public buildValue() {
|
|
134
|
+
const records = this.grist._data.records || []
|
|
135
|
+
const tags = (this.dataItems || []).map(dataItem => dataItem.tag)
|
|
136
|
+
this.grist.commit()
|
|
137
|
+
|
|
138
|
+
return tags.reduce((partial, tag) => {
|
|
139
|
+
partial[tag] = Array.from({ length: records.length }, (_, index) => index).map(index => {
|
|
140
|
+
const record = records[index]
|
|
141
|
+
return record?.[tag]
|
|
142
|
+
})
|
|
143
|
+
return partial
|
|
144
|
+
}, {} as any)
|
|
145
|
+
}
|
|
146
|
+
}
|
|
@@ -42,6 +42,7 @@ export class OxDataSampleSubgroupView extends LitElement {
|
|
|
42
42
|
|
|
43
43
|
th.label {
|
|
44
44
|
background-color: #aaa;
|
|
45
|
+
width: 120px;
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
tr {
|
|
@@ -63,6 +64,7 @@ export class OxDataSampleSubgroupView extends LitElement {
|
|
|
63
64
|
td.label {
|
|
64
65
|
background-color: #aaa;
|
|
65
66
|
text-transform: var(--th-text-transform);
|
|
67
|
+
width: 120px;
|
|
66
68
|
}
|
|
67
69
|
|
|
68
70
|
td mwc-icon {
|
|
@@ -29,6 +29,7 @@ const dataSet = {
|
|
|
29
29
|
description: '창고 온도는 섭씨 0도 이하로 유지되어야 합니다.',
|
|
30
30
|
sequence: 1,
|
|
31
31
|
tag: 'temp',
|
|
32
|
+
group: '측정데이타',
|
|
32
33
|
type: 'number',
|
|
33
34
|
quota: 1,
|
|
34
35
|
active: true,
|
|
@@ -51,6 +52,7 @@ const dataSet = {
|
|
|
51
52
|
description: '창고 습도는 30% 이하로 유지되어야 합니다.',
|
|
52
53
|
sequence: 2,
|
|
53
54
|
tag: 'humid',
|
|
55
|
+
group: '측정데이타',
|
|
54
56
|
type: 'number',
|
|
55
57
|
quota: 5,
|
|
56
58
|
active: true,
|
|
@@ -73,6 +75,7 @@ const dataSet = {
|
|
|
73
75
|
description: '육안 검사는 포장전 30분 내로 실행되어야 합니다.',
|
|
74
76
|
sequence: 3,
|
|
75
77
|
tag: 'inspection',
|
|
78
|
+
group: '측정데이타',
|
|
76
79
|
type: 'boolean',
|
|
77
80
|
quota: 3,
|
|
78
81
|
active: true,
|
|
@@ -92,6 +95,7 @@ const dataSet = {
|
|
|
92
95
|
description: '품평은 최우수/우수/보통/미달을 포함하여 간단히 평가.',
|
|
93
96
|
sequence: 4,
|
|
94
97
|
tag: 'evaluation',
|
|
98
|
+
group: '측정데이타',
|
|
95
99
|
type: 'select',
|
|
96
100
|
options: {
|
|
97
101
|
options: [
|
|
@@ -150,6 +154,9 @@ const Template: Story<ArgTypes> = ({}: ArgTypes) =>
|
|
|
150
154
|
body {
|
|
151
155
|
}
|
|
152
156
|
</style>
|
|
157
|
+
<link href="/themes/app-theme.css" rel="stylesheet" />
|
|
158
|
+
<link href="/themes/oops-theme.css" rel="stylesheet" />
|
|
159
|
+
<link href="/themes/grist-theme.css" rel="stylesheet" />
|
|
153
160
|
|
|
154
161
|
<ox-data-entry-form
|
|
155
162
|
.dataSet=${dataSet}
|