@operato/dataset 7.1.31 → 7.1.33

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 (50) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/tsconfig.tsbuildinfo +1 -1
  3. package/package.json +12 -12
  4. package/.editorconfig +0 -29
  5. package/.storybook/main.js +0 -3
  6. package/.storybook/preview.js +0 -52
  7. package/.storybook/server.mjs +0 -8
  8. package/demo/favicon.ico +0 -0
  9. package/demo/index.html +0 -349
  10. package/demo/ox-data-ooc-brief-view-test.html +0 -349
  11. package/src/grist-editor/index.ts +0 -10
  12. package/src/grist-editor/ox-grist-editor-data-item-spec.ts +0 -93
  13. package/src/grist-editor/ox-popup-data-item-spec.ts +0 -93
  14. package/src/index.ts +0 -2
  15. package/src/ox-data-entry-form.ts +0 -303
  16. package/src/ox-data-entry-subgroup-form.ts +0 -157
  17. package/src/ox-data-entry-subgroup-view.ts +0 -221
  18. package/src/ox-data-entry-view.ts +0 -241
  19. package/src/ox-data-item-spec.ts +0 -131
  20. package/src/ox-data-ooc-badge.ts +0 -73
  21. package/src/ox-data-ooc-brief-view.ts +0 -43
  22. package/src/ox-data-ooc-correction-part.ts +0 -107
  23. package/src/ox-data-ooc-history.ts +0 -74
  24. package/src/ox-data-ooc-view.ts +0 -51
  25. package/src/ox-data-sample-subgroup-view.ts +0 -210
  26. package/src/ox-data-sample-view.ts +0 -271
  27. package/src/ox-data-summary-view.ts +0 -198
  28. package/src/types.ts +0 -178
  29. package/src/usecase/ccp/index.ts +0 -10
  30. package/src/usecase/ccp/ox-data-use-case-ccp.ts +0 -147
  31. package/src/usecase/ccp/ox-input-ccp-limits.ts +0 -184
  32. package/src/usecase/ccp/ox-property-editor-ccp-limits.ts +0 -23
  33. package/src/usecase/ox-data-use-case.ts +0 -178
  34. package/src/usecase/qc/index.ts +0 -10
  35. package/src/usecase/qc/ox-data-use-case-qc.ts +0 -76
  36. package/src/usecase/qc/ox-input-qc-limits.ts +0 -183
  37. package/src/usecase/qc/ox-property-editor-qc-limits.ts +0 -23
  38. package/src/usecase/spc/index.ts +0 -10
  39. package/src/usecase/spc/ox-data-use-case-spc.ts +0 -147
  40. package/src/usecase/spc/ox-input-spc-limits.ts +0 -184
  41. package/src/usecase/spc/ox-property-editor-spc-limits.ts +0 -23
  42. package/stories/ox-data-entry-form.stories.ts +0 -253
  43. package/stories/ox-data-item-spec.stories.ts +0 -257
  44. package/stories/ox-data-ooc-brief-view.stories.ts +0 -333
  45. package/stories/ox-data-ooc-view.stories.ts +0 -327
  46. package/stories/ox-data-sample-view.stories.ts +0 -313
  47. package/stories/ox-grist-editor-data-item-spec.stories.ts +0 -412
  48. package/tsconfig.json +0 -24
  49. package/web-dev-server.config.mjs +0 -27
  50. package/web-test-runner.config.mjs +0 -41
@@ -1,303 +0,0 @@
1
- import '@operato/input/ox-input-file.js'
2
- import '@operato/input/ox-input-signature.js'
3
- import './ox-data-entry-subgroup-form.js'
4
-
5
- import { css, html, LitElement, TemplateResult } from 'lit'
6
- import { customElement, property, queryAll } from 'lit/decorators.js'
7
-
8
- import { DataSet, DataItem, DataSample } from './types.js'
9
- import { OxDataEntrySubgroupForm } from './ox-data-entry-subgroup-form.js'
10
-
11
- @customElement('ox-data-entry-form')
12
- export class OxDataEntryForm extends LitElement {
13
- static styles = css`
14
- :host {
15
- --item-description-font: normal 0.8rem/1rem var(--theme-font);
16
- --item-description-color: var(--page-description-color);
17
- }
18
-
19
- h2 {
20
- margin: var(--title-margin);
21
- font: var(--title-font);
22
- color: var(--title-text-color);
23
- text-transform: capitalize;
24
- text-align: center;
25
- }
26
-
27
- h3 {
28
- margin: var(--page-description-margin);
29
- font: var(--page-description-font);
30
- color: var(--page-description-color);
31
- text-transform: capitalize;
32
- text-align: center;
33
- }
34
-
35
- form {
36
- display: flex;
37
- flex-direction: column;
38
- overflow: hidden;
39
- }
40
-
41
- form > div[label] {
42
- display: grid;
43
-
44
- grid-template-rows: auto 1fr;
45
- grid-template-columns: 1fr 5fr;
46
- grid-template-areas: 'name description' 'empty inputs';
47
-
48
- grid-gap: 9px;
49
- align-items: center;
50
- margin-bottom: var(--spacing-medium);
51
-
52
- &:nth-child(odd) {
53
- background-color: var(--md-sys-color-background);
54
- padding: var(--padding-default) 0;
55
- }
56
- }
57
-
58
- div[name] {
59
- grid-area: name;
60
- font: var(--label-font);
61
- color: var(--label-color, var(--md-sys-color-on-surface));
62
- text-align: right;
63
- }
64
- div[description] {
65
- grid-area: description;
66
- opacity: 0.7;
67
- font: var(--item-description-font);
68
- color: var(--item-description-color);
69
- text-align: left;
70
-
71
- * {
72
- vertical-align: middle;
73
- }
74
-
75
- md-icon {
76
- font-size: 0.9rem;
77
- }
78
- }
79
-
80
- div[elements] {
81
- grid-area: inputs;
82
- display: flex;
83
- flex-direction: row;
84
- flex-wrap: wrap;
85
- gap: 10px;
86
- padding-right: var(--padding-default);
87
-
88
- * {
89
- flex: 1;
90
- }
91
-
92
- input,
93
- select {
94
- border: var(--input-field-border);
95
- border-radius: var(--input-field-border-radius);
96
- padding: var(--input-field-padding);
97
- font: var(--input-field-font);
98
- }
99
- }
100
-
101
- div[subgroup] {
102
- grid-column: 1 / 3;
103
- grid-row: 2;
104
- }
105
-
106
- @media only screen and (max-width: 460px) {
107
- form > div[label] {
108
- display: grid;
109
-
110
- grid-template-rows: auto auto 1fr;
111
- grid-template-columns: 1fr;
112
- grid-template-areas: 'name' 'description' 'inputs';
113
-
114
- grid-gap: 9px;
115
- align-items: center;
116
- margin-bottom: var(--spacing-medium);
117
- }
118
-
119
- div[name] {
120
- text-align: left;
121
- }
122
- }
123
- `
124
-
125
- @property({ type: Object }) dataSet?: DataSet
126
- @property({ type: Object }) value?: { [tag: string]: any }
127
-
128
- @queryAll('ox-data-entry-subgroup-form') subgroups!: NodeListOf<OxDataEntrySubgroupForm>
129
-
130
- render() {
131
- return html` <form @change=${(e: Event) => this.onChange(e)}>
132
- <h2>${this.dataSet?.name || ''}</h2>
133
- <h3>${this.dataSet?.description || ''}</h3>
134
- ${this.buildInputs()}
135
- </form>`
136
- }
137
-
138
- private onChange(e: Event) {
139
- this.value = this.buildValue()
140
-
141
- this.dispatchEvent(
142
- new CustomEvent('change', {
143
- bubbles: true,
144
- composed: true,
145
- detail: this.value
146
- })
147
- )
148
- }
149
-
150
- private buildInputs() {
151
- const dataItems = this.dataSet?.dataItems.filter(item => item.active) || []
152
- const nonGroupDataItems = dataItems.filter(dataItem => !dataItem.group)
153
- const dataItemSubgroups = Object.entries(this.groupDataItemsByGroup(dataItems)).map(([subgroup, dataItems]) => {
154
- const tags = dataItems.map(dataItem => dataItem.tag)
155
- const value = tags.reduce((partial, key) => {
156
- partial[key] = this.value?.[key]
157
- return partial
158
- }, {} as any)
159
-
160
- return this.buildInputs4Subgroup(subgroup, dataItems, value)
161
- })
162
-
163
- return [...this.buildInputs4NonGrouped(nonGroupDataItems), ...dataItemSubgroups]
164
- }
165
-
166
- private buildInputs4Subgroup(subgroup: string, dataItems: DataItem[], value: { [tag: string]: any }): TemplateResult {
167
- return html`
168
- <div label>
169
- <div name>${subgroup}</div>
170
- <div subgroup>
171
- <ox-data-entry-subgroup-form
172
- .subgroup=${subgroup}
173
- .dataItems=${dataItems}
174
- .value=${value}
175
- @change=${(e: Event) => this.onChange(e)}
176
- ></ox-data-entry-subgroup-form>
177
- </div>
178
- </div>
179
- `
180
- }
181
-
182
- private buildInputs4NonGrouped(dataItems: DataItem[]): TemplateResult[] {
183
- return (dataItems || []).map(dataItem => {
184
- const { name, description, tag, type, quota = 1, options = {}, unit } = dataItem
185
-
186
- const samples = new Array(quota).fill(0)
187
- const value = this.value && this.value[tag]
188
-
189
- const elements = samples.map((_, idx) => {
190
- const v = value instanceof Array ? value[idx] : idx === 0 ? value : undefined
191
-
192
- switch (type) {
193
- case 'select':
194
- return html` <select .name=${tag}>
195
- <option value=""></option>
196
- ${(options.options || []).map(
197
- option => html`<option value=${option.value} ?selected=${option.value === v}>${option.text}</option>`
198
- )}
199
- </select>`
200
-
201
- case 'radio':
202
- return html`<div
203
- @change=${(e: Event) => {
204
- const div = e.currentTarget as HTMLElement
205
- const inputHidden = div.querySelector(`input[name="${tag}"]`) as HTMLInputElement
206
- inputHidden.value = (e.target as HTMLInputElement).value
207
- }}
208
- >
209
- <input type="hidden" name=${tag} />
210
-
211
- ${(options.options || []).map(
212
- option =>
213
- html`<label>
214
- <input
215
- type="radio"
216
- name=${'$' + tag + '-' + idx}
217
- .value=${option.value}
218
- ?checked=${option.value === v}
219
- />
220
- ${option.text}
221
- </label>`
222
- )}
223
- </div>`
224
-
225
- case 'boolean':
226
- return html` <input type="checkbox" name=${tag} .checked=${v} />`
227
-
228
- case 'number':
229
- return html` <input type="number" name=${tag} value=${v} />`
230
-
231
- case 'date':
232
- return html` <input type="date" name=${tag} value=${v} />`
233
-
234
- case 'datetime':
235
- return html` <input type="datetime-local" name=${tag} value=${v} />`
236
-
237
- case 'file':
238
- return html`<ox-input-file name=${tag} multiple .value=${v}></ox-input-file>`
239
-
240
- case 'signature':
241
- return html`<ox-input-signature name=${tag} value=${v}></ox-input-signature>`
242
-
243
- case 'string':
244
- default:
245
- return html` <input type="text" name=${tag} value=${v} />`
246
- }
247
- })
248
-
249
- return html` <div label>
250
- <div name>${name}${unit ? `(${unit})` : ''}</div>
251
- <div description><md-icon>info</md-icon> ${description}</div>
252
- <div elements>${elements}</div>
253
- </div>`
254
- })
255
- }
256
-
257
- private buildValue() {
258
- const dataItems = this.dataSet!.dataItems
259
- const nonGroupDataItems = dataItems.filter(dataItem => !dataItem.group)
260
-
261
- const nonGroupValue = (nonGroupDataItems || []).reduce(
262
- (sum, dataItem) => {
263
- const { tag, type } = dataItem
264
-
265
- const editors = Array.prototype.slice.call(
266
- this.renderRoot.querySelectorAll(`[name=${tag}]`) as NodeListOf<HTMLInputElement>
267
- ) as HTMLInputElement[]
268
-
269
- if (editors.length > 0) {
270
- sum[tag] = editors.map(editor => (type === 'boolean' ? editor.checked : editor.value))
271
- }
272
-
273
- return sum
274
- },
275
- {} as { [tag: string]: any }
276
- )
277
-
278
- return Array.from(this.subgroups).reduce((value, subgroup) => {
279
- return {
280
- ...value,
281
- ...subgroup.buildValue()
282
- }
283
- }, nonGroupValue || {})
284
- }
285
-
286
- private groupDataItemsByGroup(dataItems: DataItem[]): { [group: string]: DataItem[] } {
287
- const groupedDataItems: { [group: string]: DataItem[] } = {}
288
-
289
- for (const dataItem of dataItems) {
290
- const { group } = dataItem
291
-
292
- if (group) {
293
- if (!groupedDataItems[group]) {
294
- groupedDataItems[group] = []
295
- }
296
-
297
- groupedDataItems[group].push(dataItem)
298
- }
299
- }
300
-
301
- return groupedDataItems
302
- }
303
- }
@@ -1,157 +0,0 @@
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
- @field-change=${this.onFieldChange.bind(this)}
42
- >
43
- </ox-grist>
44
- `
45
- }
46
-
47
- private buildGristConfiguration() {
48
- const columns = (this.dataItems || []).map(dataItem => {
49
- const { name, description, tag, type, options = {}, unit } = dataItem
50
- const columnConfig = {
51
- type,
52
- name: tag,
53
- header: `${name}${unit ? ` (${unit})` : ''}`,
54
- record: {
55
- editable: true
56
- },
57
- width: 200
58
- } as any
59
-
60
- switch (type) {
61
- case 'select':
62
- case 'radio': // 그리드안에 radio가 들어갈수 없어서 select로 변환
63
- columnConfig.type = 'select'
64
- columnConfig.record.options = [
65
- '',
66
- ...(options.options || []).map((option: any) => {
67
- if (typeof option == 'string') {
68
- return option
69
- }
70
- const { display, text, value } = option || {}
71
-
72
- return {
73
- display: display || text,
74
- value
75
- }
76
- })
77
- ]
78
- return columnConfig
79
-
80
- case 'boolean':
81
- columnConfig.record.align = 'center'
82
- return columnConfig
83
-
84
- case 'number':
85
- columnConfig.record.align = 'right'
86
- return columnConfig
87
-
88
- case 'date':
89
- return columnConfig
90
-
91
- case 'datetime':
92
- return columnConfig
93
-
94
- case 'file':
95
- columnConfig.record.multiple = true
96
- return columnConfig
97
-
98
- case 'signature':
99
- return columnConfig
100
-
101
- case 'string':
102
- return columnConfig
103
-
104
- default:
105
- return columnConfig
106
- }
107
- })
108
-
109
- return {
110
- list: { fields: ['name', 'data'] },
111
- columns: [{ type: 'gutter', gutterName: 'sequence' }, ...columns],
112
- rows: {
113
- appendable: true
114
- },
115
- pagination: { infinite: true }
116
- }
117
- }
118
-
119
- async fetchHandler({ page = 1, limit = 100, sortings = [], filters = [] }: FetchOption) {
120
- const length = Object.entries(this.value || {}).reduce((max, [tag, value]) => {
121
- return Math.max(max, Array.isArray(value) ? value.length : 1)
122
- }, 0)
123
-
124
- const tags = (this.dataItems || []).map(dataItem => dataItem.tag)
125
-
126
- const records = Array.from({ length }, (_, index) => index).map(index => {
127
- return tags.reduce((partial, tag) => {
128
- const v = this.value?.[tag]
129
- partial[tag] = Array.isArray(v) ? v[index] : index == 0 ? v : undefined
130
- return partial
131
- }, {} as any)
132
- })
133
-
134
- return {
135
- total: records.length || 0,
136
- records: records
137
- }
138
- }
139
-
140
- public buildValue() {
141
- const records = this.grist._data.records || []
142
- const tags = (this.dataItems || []).map(dataItem => dataItem.tag)
143
- this.grist.commit()
144
-
145
- return tags.reduce((partial, tag) => {
146
- partial[tag] = Array.from({ length: records.length }, (_, index) => index).map(index => {
147
- const record = records[index]
148
- return record?.[tag]
149
- })
150
- return partial
151
- }, {} as any)
152
- }
153
-
154
- onFieldChange(e: CustomEvent) {
155
- this.dispatchEvent(new CustomEvent('change', { detail: this.grist.dirtyData }))
156
- }
157
- }
@@ -1,221 +0,0 @@
1
- import '@material/web/icon/icon.js'
2
- import '@operato/input/ox-input-file.js'
3
- import '@operato/input/ox-input-signature.js'
4
-
5
- import { css, html, LitElement, nothing } from 'lit'
6
- import { customElement, property } from 'lit/decorators.js'
7
-
8
- import { i18next } from '@operato/i18n'
9
-
10
- import { DataSample, DataSet, DataSpecLimitSet } from './types.js'
11
- import { OxDataUseCase } from './usecase/ox-data-use-case.js'
12
-
13
- @customElement('ox-data-entry-subgroup-view')
14
- export class OxDataEntrySubgroupView extends LitElement {
15
- static styles = css`
16
- :host {
17
- display: flex;
18
- flex-direction: column;
19
- }
20
-
21
- h3 {
22
- margin: var(--title-margin);
23
- font: var(--title-font);
24
- font-size: 20px;
25
- color: var(--title-text-color);
26
- text-transform: capitalize;
27
- }
28
-
29
- table {
30
- border-collapse: collapse;
31
- margin-bottom: var(--spacing-medium);
32
- }
33
-
34
- th {
35
- padding: var(--th-padding);
36
- border-top: var(--th-border-top);
37
- border-left: var(--td-border-line, 1px solid rgba(0, 0, 0, 0.05));
38
- border-bottom: var(--td-border-bottom);
39
- text-transform: var(--th-text-transform);
40
- font: var(--th-font);
41
- font-weight: bold;
42
- color: var(--th-color);
43
- text-align: center;
44
- white-space: nowrap;
45
- }
46
-
47
- th.label,
48
- td.label {
49
- background-color: var(--label-cell-background-color, #f6f6f6);
50
- width: 120px;
51
- text-transform: var(--th-text-transform);
52
- }
53
-
54
- tr {
55
- background-color: var(--tr-background-color);
56
- }
57
-
58
- tr:hover {
59
- background-color: var(--tr-background-hover-color);
60
- }
61
-
62
- td {
63
- border-left: var(--td-border-line, 1px solid rgba(0, 0, 0, 0.05));
64
- border-bottom: var(--td-border-bottom);
65
- padding: var(--td-padding);
66
- font: var(--td-font);
67
- color: var(--td-color);
68
- text-align: center;
69
- }
70
- tr th:first-child,
71
- tr td:first-child {
72
- border-left: none;
73
- }
74
-
75
- td md-icon {
76
- color: var(--md-sys-color-error);
77
- }
78
-
79
- pre {
80
- tab-size: 2;
81
- text-align: left;
82
- }
83
-
84
- a[file] {
85
- display: flex;
86
- align-items: center;
87
- gap: var(--spacing-small);
88
- }
89
-
90
- a[file] md-icon {
91
- --md-icon-size: 16px;
92
- color: var(--md-sys-color-primary);
93
- }
94
- `
95
-
96
- @property({ type: Object }) dataSet?: DataSet
97
- @property({ type: Object }) data?: any
98
- @property({ type: String }) subgroup?: string
99
-
100
- render() {
101
- if (!this.data || !this.dataSet) {
102
- return html``
103
- }
104
-
105
- const { useCase, dataItems = [] } = this.dataSet
106
-
107
- const { data } = this
108
- const useCaseNames = useCase?.split(',').filter(useCase => useCase.trim()) || []
109
- const subgroupDataItems = dataItems.filter(dataItem => dataItem.group == this.subgroup && !dataItem.hidden)
110
- const records = subgroupDataItems.reduce((max, dataItem) => {
111
- const value = data[dataItem.tag]
112
- return Math.max(max, Array.isArray(value) ? value.length : 1)
113
- }, 0)
114
- const judgment = subgroupDataItems.reduce(
115
- (judgment, dataItem) => {
116
- const tag = dataItem.tag
117
- judgment[tag] = OxDataUseCase.evaluateTag(this.dataSet!, dataItems, data, tag) || {}
118
- return judgment
119
- },
120
- {} as { [tag: string]: { ooc: boolean; oos: boolean } }
121
- )
122
-
123
- return html` <h3>${this.subgroup}</h3>
124
- <table>
125
- <tr>
126
- <th class="label">${i18next.t('field.name')}</th>
127
- ${subgroupDataItems.map(
128
- dataItem => html` <th>${dataItem.name} ${dataItem.unit ? `(${dataItem.unit})` : ''}</th> `
129
- )}
130
- </tr>
131
- <tr>
132
- <td class="label">${i18next.t('field.description')}</td>
133
- ${subgroupDataItems.map(dataItem => html` <td>${dataItem.description}</td> `)}
134
- </tr>
135
- ${Array.from({ length: records }, (_, index) => index).map(
136
- index => html`
137
- <tr>
138
- <td class="label">${records > 1 ? index + 1 : i18next.t('field.value')}</td>
139
- ${subgroupDataItems.map(dataItem => {
140
- const { tag = '', type } = dataItem
141
- const valueArray = data[tag]
142
- const value = Array.isArray(valueArray) ? valueArray[index] : index == 0 ? valueArray : undefined
143
-
144
- return html` <td>${this.buildValue(type, value)}</td> `
145
- })}
146
- </tr>
147
- `
148
- )}
149
- <tr>
150
- <td class="label">${i18next.t('field.spec')}</td>
151
- ${subgroupDataItems.map(
152
- dataItem => html` <td><pre>${this.buildSpec(useCaseNames, dataItem.spec)}</pre></td> `
153
- )}
154
- </tr>
155
- <tr>
156
- <td class="label">${i18next.t('field.ooc')}</td>
157
- ${subgroupDataItems.map(
158
- dataItem => html` <td>${judgment?.[dataItem.tag]?.ooc ? html`<md-icon>done</md-icon>` : nothing}</td> `
159
- )}
160
- </tr>
161
- <tr>
162
- <td class="label">${i18next.t('field.oos')}</td>
163
- ${subgroupDataItems.map(
164
- dataItem => html` <td>${judgment?.[dataItem.tag]?.oos ? html`<md-icon>done</md-icon>` : nothing}</td> `
165
- )}
166
- </tr>
167
- </table>`
168
- }
169
-
170
- private buildSpec(useCaseNames: string[], spec: DataSpecLimitSet): string {
171
- return OxDataUseCase.elaborateDataItemSpec(useCaseNames, spec)
172
- }
173
-
174
- private download(file: { mimetype: string; name: string; fullpath: string }) {
175
- const element = document.createElement('a')
176
- element.setAttribute('href', file.fullpath)
177
- element.setAttribute('download', file.name!)
178
- document.body.appendChild(element)
179
- element.click()
180
- }
181
-
182
- private buildValue(type: string, value: any | any[]) {
183
- if (value === undefined) {
184
- return ''
185
- }
186
- const values = value instanceof Array ? value : [value]
187
-
188
- if (type == 'file') {
189
- const files = values.flat() as { mimetype: string; name: string; fullpath: string }[]
190
-
191
- return files.filter(Boolean).map(
192
- (file, idx) => html`
193
- <a @click=${() => this.download(file)} file><md-icon>description</md-icon>${file.name}</a>
194
- ${files.length - 1 == idx ? html`</br>` : nothing}
195
- `
196
- )
197
- }
198
-
199
- if (type == 'signature') {
200
- return html` <ox-input-signature .value=${value} disabled></ox-input-signature>`
201
- }
202
-
203
- const elements = values.map((v: any, idx) => {
204
- switch (typeof v) {
205
- case 'boolean':
206
- return html` <input type="checkbox" .checked=${v} disabled />`
207
- break
208
-
209
- default:
210
- if (type == 'date') {
211
- return v ? new Date(v).toLocaleDateString() : ''
212
- } else if (type == 'datetime') {
213
- return v ? new Date(v).toLocaleString() : ''
214
- }
215
- return v ?? ''
216
- }
217
- })
218
-
219
- return typeof values[0] === 'boolean' ? elements : elements.join(', ')
220
- }
221
- }