@operato/dataset 1.0.0-alpha.9 → 1.0.0-beta.2

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 (56) hide show
  1. package/CHANGELOG.md +420 -0
  2. package/demo/index.html +8 -90
  3. package/demo/ox-data-entry-form.html +118 -0
  4. package/demo/ox-data-item-spec.html +152 -0
  5. package/demo/ox-data-ooc-view.html +185 -0
  6. package/demo/ox-data-sample-view.html +150 -0
  7. package/demo/ox-grist-editor-data-item-spec.html +476 -0
  8. package/dist/src/grist-editor/index.d.ts +1 -0
  9. package/dist/src/grist-editor/index.js +7 -0
  10. package/dist/src/grist-editor/index.js.map +1 -0
  11. package/dist/src/grist-editor/ox-grist-editor-data-item-spec.d.ts +11 -0
  12. package/dist/src/grist-editor/ox-grist-editor-data-item-spec.js +77 -0
  13. package/dist/src/grist-editor/ox-grist-editor-data-item-spec.js.map +1 -0
  14. package/dist/src/grist-editor/ox-popup-data-item-spec.d.ts +13 -0
  15. package/dist/src/grist-editor/ox-popup-data-item-spec.js +90 -0
  16. package/dist/src/grist-editor/ox-popup-data-item-spec.js.map +1 -0
  17. package/dist/src/index.d.ts +6 -1
  18. package/dist/src/index.js +6 -1
  19. package/dist/src/index.js.map +1 -1
  20. package/dist/src/ox-data-entry-form.d.ts +1 -24
  21. package/dist/src/ox-data-entry-form.js +12 -16
  22. package/dist/src/ox-data-entry-form.js.map +1 -1
  23. package/dist/src/ox-data-item-spec.d.ts +18 -0
  24. package/dist/src/ox-data-item-spec.js +77 -0
  25. package/dist/src/ox-data-item-spec.js.map +1 -0
  26. package/dist/src/ox-data-ooc-view.d.ts +10 -0
  27. package/dist/src/ox-data-ooc-view.js +69 -0
  28. package/dist/src/ox-data-ooc-view.js.map +1 -0
  29. package/dist/src/ox-data-sample-view.d.ts +13 -0
  30. package/dist/src/ox-data-sample-view.js +168 -0
  31. package/dist/src/ox-data-sample-view.js.map +1 -0
  32. package/dist/src/ox-data-use-case.d.ts +16 -0
  33. package/dist/src/ox-data-use-case.js +111 -0
  34. package/dist/src/ox-data-use-case.js.map +1 -0
  35. package/dist/src/types.d.ts +78 -0
  36. package/dist/src/types.js +2 -0
  37. package/dist/src/types.js.map +1 -0
  38. package/dist/tsconfig.tsbuildinfo +1 -1
  39. package/package.json +20 -12
  40. package/src/grist-editor/index.ts +10 -0
  41. package/src/grist-editor/ox-grist-editor-data-item-spec.ts +92 -0
  42. package/src/grist-editor/ox-popup-data-item-spec.ts +90 -0
  43. package/src/index.ts +6 -1
  44. package/src/ox-data-entry-form.ts +13 -37
  45. package/src/ox-data-item-spec.ts +74 -0
  46. package/src/ox-data-ooc-view.ts +67 -0
  47. package/src/ox-data-sample-view.ts +175 -0
  48. package/src/ox-data-use-case.ts +147 -0
  49. package/src/types.ts +72 -0
  50. package/themes/grist-theme.css +194 -0
  51. package/themes/oops-theme.css +26 -0
  52. package/themes/report-theme.css +47 -0
  53. package/translations/en.json +1 -0
  54. package/translations/ko.json +1 -0
  55. package/translations/ms.json +1 -0
  56. package/translations/zh.json +1 -0
package/package.json CHANGED
@@ -3,12 +3,19 @@
3
3
  "description": "WebApplication dataset supporting components following open-wc recommendations",
4
4
  "license": "MIT",
5
5
  "author": "heartyoh",
6
- "version": "1.0.0-alpha.9",
6
+ "version": "1.0.0-beta.2",
7
7
  "main": "dist/src/index.js",
8
8
  "module": "dist/src/index.js",
9
9
  "exports": {
10
10
  ".": "./dist/src/index.js",
11
- "./ox-data-entry-form.js": "./dist/src/ox-data-entry-form.js"
11
+ "./grist-editor": "./dist/src/grist-editor/index.js",
12
+ "./grist-editor/ox-grist-editor-data-item-spec.js": "./dist/src/grist-editor/ox-grist-editor-data-item-spec.js",
13
+ "./ox-data-entry-form.js": "./dist/src/ox-data-entry-form.js",
14
+ "./ox-data-item-spec.js": "./dist/src/ox-data-item-spec.js",
15
+ "./ox-data-ooc-view.js": "./dist/src/ox-data-ooc-view.js",
16
+ "./ox-data-sample-view.js": "./dist/src/ox-data-sample-view.js",
17
+ "./ox-data-use-case.js": "./dist/src/ox-data-use-case.js",
18
+ "./types.js": "./dist/src/types.js"
12
19
  },
13
20
  "publishConfig": {
14
21
  "access": "public",
@@ -35,15 +42,16 @@
35
42
  "@material/mwc-button": "^0.25.3",
36
43
  "@material/mwc-icon": "^0.25.3",
37
44
  "@material/mwc-icon-button": "^0.25.3",
38
- "@operato/graphql": "^1.0.0-alpha.9",
39
- "@operato/i18n": "^1.0.0-alpha.9",
40
- "@operato/input": "^1.0.0-alpha.9",
41
- "@operato/layout": "^1.0.0-alpha.9",
42
- "@operato/property-editor": "^1.0.0-alpha.9",
43
- "@operato/shell": "^1.0.0-alpha.9",
44
- "@operato/styles": "^1.0.0-alpha.9",
45
- "@operato/utils": "^1.0.0-alpha.9",
46
- "lit": "^2.2.0"
45
+ "@operato/data-grist": "^1.0.0-beta.2",
46
+ "@operato/graphql": "^1.0.0-beta.2",
47
+ "@operato/i18n": "^1.0.0-beta.2",
48
+ "@operato/input": "^1.0.0-beta.2",
49
+ "@operato/layout": "^1.0.0-beta.2",
50
+ "@operato/property-editor": "^1.0.0-beta.2",
51
+ "@operato/shell": "^1.0.0-beta.2",
52
+ "@operato/styles": "^1.0.0-beta.2",
53
+ "@operato/utils": "^1.0.0-beta.2",
54
+ "lit": "^2.2.1"
47
55
  },
48
56
  "devDependencies": {
49
57
  "@custom-elements-manifest/analyzer": "^0.4.17",
@@ -77,5 +85,5 @@
77
85
  "prettier --write"
78
86
  ]
79
87
  },
80
- "gitHead": "fb82c87ab8b869139076c4e74c46309460b67d35"
88
+ "gitHead": "514a836d3f5e959695701ba41913aee18a1efe66"
81
89
  }
@@ -0,0 +1,10 @@
1
+ import { OxGristRendererJson5, registerEditor, registerRenderer } from '@operato/data-grist'
2
+
3
+ import { OxGristEditorDataItemSpec } from './ox-grist-editor-data-item-spec'
4
+
5
+ /* register grist renderer/editor for id */
6
+ registerEditor('data-item-spec', OxGristEditorDataItemSpec)
7
+
8
+ registerRenderer('data-item-spec', OxGristRendererJson5)
9
+
10
+ export * from './ox-grist-editor-data-item-spec'
@@ -0,0 +1,92 @@
1
+ /**
2
+ * @license Copyright © HatioLab Inc. All rights reserved.
3
+ */
4
+
5
+ import './ox-popup-data-item-spec.js'
6
+
7
+ import { html } from 'lit'
8
+ import { customElement } from 'lit/decorators.js'
9
+ import { cloneDeep } from 'lodash-es'
10
+
11
+ import { OxGristEditor } from '@operato/data-grist'
12
+ import { i18next } from '@operato/i18n'
13
+ import { openPopup, PopupHandle } from '@operato/layout'
14
+
15
+ @customElement('ox-grist-editor-data-item-spec')
16
+ export class OxGristEditorDataItemSpec extends OxGristEditor {
17
+ private popup?: PopupHandle
18
+
19
+ get editorTemplate() {
20
+ const value = typeof this.value === 'object' ? JSON.stringify(this.value) : this.value
21
+ return html` ${value || ''} `
22
+ }
23
+
24
+ async firstUpdated() {
25
+ await this.updateComplete
26
+
27
+ this.renderRoot.addEventListener('click', (e: Event) => {
28
+ e.stopPropagation()
29
+
30
+ this.openSelector()
31
+ })
32
+
33
+ this.openSelector()
34
+ }
35
+
36
+ async openSelector() {
37
+ if (this.popup) {
38
+ delete this.popup
39
+ }
40
+
41
+ var { options } = this.column.record
42
+
43
+ if (typeof options === 'function') {
44
+ options = await options.call(this, this.value, this.column, this.record, this.row, this.field)
45
+ }
46
+
47
+ const { name, help, objectified = false } = options
48
+
49
+ const confirmCallback = (newval: any) => {
50
+ this.dispatchEvent(
51
+ new CustomEvent('field-change', {
52
+ bubbles: true,
53
+ composed: true,
54
+ detail: {
55
+ before: this.value,
56
+ after: !objectified ? JSON.stringify(newval) : newval,
57
+ record: this.record,
58
+ column: this.column,
59
+ row: this.row
60
+ }
61
+ })
62
+ )
63
+ }
64
+
65
+ try {
66
+ var value: any =
67
+ !objectified && typeof this.value === 'string' ? JSON.parse(this.value) : cloneDeep(this.value || {})
68
+ } catch (e) {
69
+ var value: any = {}
70
+ }
71
+
72
+ /*
73
+ 주의 : 이 팝업 템플릿은 layout 모듈에 의해서 render 되므로,
74
+ layout의 구성에 변화가 발생하면, 다시 render된다.
75
+ 이 팝업이 떠 있는 상태에서, 또 다른 팝업이 뜨는 경우도 layout 구성의 변화를 야기한다. (overlay의 갯수의 증가)
76
+ 이 경우 value, options, confirmCallback 등 클로져를 사용한 것들이 초기 바인딩된 값으로 다시 바인딩되게 되는데,
77
+ 만약, 템플릿 내부에서 이들 속성의 레퍼런스가 변화했다면, 원래 상태로 되돌아가는 현상이 발생하게 된다.
78
+ 따라서, 가급적 이들 속성의 레퍼런스를 변화시키지 않는 것이 좋다.
79
+ */
80
+ var template = html`
81
+ <ox-popup-data-item-spec .value=${value} .dataItem=${this.record} .confirmCallback=${confirmCallback}>
82
+ </ox-popup-data-item-spec>
83
+ `
84
+
85
+ this.popup = openPopup(template, {
86
+ backdrop: true,
87
+ size: 'large',
88
+ title: `${name?.toUpperCase() || ''} ${i18next.t('field.spec')}`,
89
+ help
90
+ })
91
+ }
92
+ }
@@ -0,0 +1,90 @@
1
+ import '../ox-data-item-spec.js'
2
+
3
+ import { LitElement, css, html } from 'lit'
4
+ import { customElement, property } from 'lit/decorators.js'
5
+
6
+ import { DataItem } from '../types.js'
7
+ import { ScrollbarStyles } from '@operato/styles'
8
+ import { i18next } from '@operato/i18n'
9
+
10
+ @customElement('ox-popup-data-item-spec')
11
+ export class OxPopupDataItemSpec extends LitElement {
12
+ static styles = [
13
+ ScrollbarStyles,
14
+ css`
15
+ :host {
16
+ display: flex;
17
+ flex-direction: column;
18
+
19
+ background-color: #fff;
20
+
21
+ width: var(--overlay-center-normal-width, 50%);
22
+ height: var(--overlay-center-normal-height, 50%);
23
+ }
24
+
25
+ ox-data-item-spec {
26
+ flex: 1;
27
+ overflow-y: auto;
28
+ }
29
+
30
+ span {
31
+ flex: 1;
32
+
33
+ display: flex;
34
+ align-items: center;
35
+ justify-content: center;
36
+
37
+ color: var(--primary-color);
38
+ }
39
+
40
+ .button-container {
41
+ display: flex;
42
+ margin-left: auto;
43
+ }
44
+ `
45
+ ]
46
+
47
+ @property({ type: Object }) value: any
48
+ @property({ type: Object }) dataItem?: DataItem
49
+ @property({ type: Object }) confirmCallback!: (newval: any) => void
50
+
51
+ render() {
52
+ var dataItem = this.dataItem || {}
53
+
54
+ return html`
55
+ <ox-data-item-spec .value=${this.value} .dataItem=${dataItem} @change=${this.onChange.bind(this)}>
56
+ </ox-data-item-spec>
57
+
58
+ <div class="button-container">
59
+ <mwc-button @click=${this.onCancel.bind(this)}>${i18next.t('button.cancel')}</mwc-button>
60
+ <mwc-button @click=${this.onConfirm.bind(this)}>${i18next.t('button.confirm')}</mwc-button>
61
+ </div>
62
+ `
63
+ }
64
+
65
+ private onChange(e: CustomEvent) {
66
+ e.stopPropagation()
67
+
68
+ /*
69
+ 주의 : 이 팝업 템플릿은 layout 모듈에 의해서 render 되므로,
70
+ layout의 구성에 변화가 발생하면, 다시 render된다.
71
+ 이 팝업이 떠 있는 상태에서, 또 다른 팝업이 뜨는 경우도 layout 구성의 변화를 야기한다. (overlay의 갯수의 증가)
72
+ 이 경우 value, options, confirmCallback 등 클로져를 사용한 것들이 초기 바인딩된 값으로 다시 바인딩되게 되는데,
73
+ 만약, 템플릿 내부에서 이들 속성의 레퍼런스가 변화했다면, 원래 상태로 되돌아가는 현상이 발생하게 된다.
74
+ 따라서, 가급적 이들 속성의 레퍼런스를 변화시키지 않는 것이 좋다.
75
+ (이 팝업 클래스를 템플릿으로 사용한 곳의 코드를 참조하세요.)
76
+ =>
77
+ 이런 이유로, Object.assign(...)을 사용하였다.
78
+ */
79
+ this.value = e.detail
80
+ }
81
+
82
+ private onCancel(e: Event) {
83
+ history.back()
84
+ }
85
+
86
+ private onConfirm(e: Event) {
87
+ this.confirmCallback && this.confirmCallback(this.value)
88
+ history.back()
89
+ }
90
+ }
package/src/index.ts CHANGED
@@ -1 +1,6 @@
1
- export * from './ox-data-entry-form'
1
+ export * from './ox-data-entry-form.js'
2
+ export * from './ox-data-sample-view.js'
3
+ export * from './ox-data-ooc-view.js'
4
+ export * from './ox-data-item-spec.js'
5
+ export * from './ox-data-use-case'
6
+ export * from './types.js'
@@ -1,30 +1,9 @@
1
1
  import '@operato/input/ox-input-file.js'
2
2
 
3
- import { LitElement, css, html } from 'lit'
3
+ import { css, html, LitElement } from 'lit'
4
4
  import { customElement, property } from 'lit/decorators.js'
5
5
 
6
- type SelectOption = { text: string; value: string }
7
- type SelectOptions = SelectOption[]
8
- type TypeOptions = {
9
- options?: SelectOptions
10
- [prop: string]: any
11
- }
12
-
13
- export type DataItem = {
14
- name: string
15
- description: string
16
- sequence: number
17
- tag: string
18
- type: string
19
- options: TypeOptions
20
- quota: number
21
- }
22
-
23
- export type DataSet = {
24
- name: string
25
- description: string
26
- dataItems: DataItem[]
27
- }
6
+ import { DataSet } from './types.js'
28
7
 
29
8
  @customElement('ox-data-entry-form')
30
9
  export class OxDataEntryForm extends LitElement {
@@ -51,6 +30,7 @@ export class OxDataEntryForm extends LitElement {
51
30
  background-color: var(--main-section-background-color);
52
31
  padding: var(--padding-default) 0;
53
32
  }
33
+
54
34
  div[name] {
55
35
  grid-column: span 2 / auto;
56
36
  font: var(--label-font);
@@ -75,6 +55,7 @@ export class OxDataEntryForm extends LitElement {
75
55
  padding: var(--input-field-padding);
76
56
  font: var(--input-field-font);
77
57
  }
58
+
78
59
  @media only screen and (max-width: 460px) {
79
60
  div[name] {
80
61
  grid-column: span 3 / auto;
@@ -93,28 +74,28 @@ export class OxDataEntryForm extends LitElement {
93
74
  }
94
75
 
95
76
  private onChange(e: Event) {
96
- const value = this.buildValue()
77
+ this.value = this.buildValue()
97
78
 
98
79
  this.dispatchEvent(
99
80
  new CustomEvent('change', {
100
81
  bubbles: true,
101
82
  composed: true,
102
- detail: value
83
+ detail: this.value
103
84
  })
104
85
  )
105
86
  }
106
87
 
107
88
  private buildInputs() {
108
- const dataItems = this.dataSet!.dataItems
89
+ const dataItems = this.dataSet!.dataItems.filter(item => item.active)
109
90
 
110
91
  return (dataItems || []).map(dataItem => {
111
- const { name, description, tag, type, quota = 1, options = {} } = dataItem
92
+ const { name, description, tag, type, quota = 1, options = {}, unit } = dataItem
112
93
 
113
94
  const samples = new Array(quota).fill(0)
114
- const value = this.value?.[tag]
95
+ const value = this.value && this.value[tag]
115
96
 
116
97
  const elements = samples.map((_, idx) => {
117
- const v = quota <= 1 ? value : value instanceof Array && value[idx]
98
+ const v = value instanceof Array ? value[idx] : idx === 0 ? value : undefined
118
99
 
119
100
  switch (type) {
120
101
  case 'select':
@@ -150,7 +131,7 @@ export class OxDataEntryForm extends LitElement {
150
131
  })
151
132
 
152
133
  return html` <label .title=${description}>
153
- <div name>${name}</div>
134
+ <div name>${name}${unit ? `(${unit})` : ''}</div>
154
135
  <div elements>${elements}</div>
155
136
  </label>`
156
137
  })
@@ -160,19 +141,14 @@ export class OxDataEntryForm extends LitElement {
160
141
  const dataItems = this.dataSet!.dataItems
161
142
 
162
143
  return (dataItems || []).reduce((sum, dataItem) => {
163
- const { tag, quota, type } = dataItem
144
+ const { tag, type } = dataItem
164
145
 
165
146
  const editors = Array.prototype.slice.call(
166
147
  this.renderRoot.querySelectorAll(`[name=${tag}]`) as NodeListOf<HTMLInputElement>
167
148
  ) as HTMLInputElement[]
168
149
 
169
150
  if (editors.length > 0) {
170
- sum[tag] =
171
- editors.length > 1
172
- ? editors.map(editor => (type === 'boolean' ? editor.checked : editor.value))
173
- : type === 'boolean'
174
- ? editors[0].checked
175
- : editors[0].value
151
+ sum[tag] = editors.map(editor => (type === 'boolean' ? editor.checked : editor.value))
176
152
  }
177
153
 
178
154
  return sum
@@ -0,0 +1,74 @@
1
+ import '@operato/property-editor/ox-properties-dynamic-view.js'
2
+
3
+ import { css, html, LitElement, PropertyValues } from 'lit'
4
+ import { customElement, property, queryAll, state } from 'lit/decorators.js'
5
+
6
+ import { OxDataUseCase } from './ox-data-use-case.js'
7
+ import { DataItem, DataItemSpecSet } from './types.js'
8
+
9
+ @customElement('ox-data-item-spec')
10
+ export class OxDataItemSpec extends LitElement {
11
+ static styles = css`
12
+ :host {
13
+ display: flex;
14
+ flex-direction: row;
15
+ border-bottom: var(--border-dark-color);
16
+ padding: var(--margin-default) 0;
17
+ }
18
+ [specName] {
19
+ font: var(--legend-font);
20
+ color: var(--legend-text-color);
21
+ }
22
+ [description] {
23
+ font: var(--form-sublabel-font);
24
+ opacity: 0.8;
25
+ }
26
+
27
+ form {
28
+ flex: 1;
29
+ }
30
+ `
31
+
32
+ @property({ type: Object }) value?: { [specSetName: string]: any }
33
+ @property({ type: Object }) dataItem?: DataItem
34
+
35
+ @state() dataItemSpecSets: DataItemSpecSet[] = []
36
+
37
+ @queryAll('ox-properties-dynamic-view') specSetViews!: NodeListOf<HTMLElement & { value: any }>
38
+
39
+ render() {
40
+ return html`<form @property-change=${(e: Event) => this.onChange(e)}>
41
+ ${this.dataItemSpecSets.map(
42
+ ({ name, description, specs }) => html` <div specName>${name}</div>
43
+ <div description>${description}</div>
44
+ <ox-properties-dynamic-view data-name=${name} .props=${specs} .value=${this.value?.[name]}>
45
+ </ox-properties-dynamic-view>`
46
+ )}
47
+ </form>`
48
+ }
49
+
50
+ updated(changes: PropertyValues<this>) {
51
+ if (changes.has('dataItem')) {
52
+ this.dataItemSpecSets = OxDataUseCase.getUseCases().map(useCase => useCase.getSpecification(this.dataItem!))
53
+ }
54
+ }
55
+
56
+ private onChange(e: Event) {
57
+ this.value = this.buildValue()
58
+
59
+ this.dispatchEvent(
60
+ new CustomEvent('change', {
61
+ bubbles: true,
62
+ composed: true,
63
+ detail: { ...this.value }
64
+ })
65
+ )
66
+ }
67
+
68
+ private buildValue() {
69
+ var value = {} as any
70
+ this.specSetViews.forEach(view => (value[view.getAttribute('data-name')!] = view.value))
71
+
72
+ return value
73
+ }
74
+ }
@@ -0,0 +1,67 @@
1
+ import '@operato/input/ox-input-file.js'
2
+ import './ox-data-sample-view'
3
+
4
+ import { DataOoc, DataSet } from './types.js'
5
+ import { LitElement, css, html } from 'lit'
6
+ import { customElement, property } from 'lit/decorators.js'
7
+
8
+ @customElement('ox-data-ooc-view')
9
+ export class OxDataOocView extends LitElement {
10
+ static styles = css`
11
+ :host {
12
+ display: flex;
13
+ flex-direction: column;
14
+ background-color: var(--main-section-background-color);
15
+ padding: var(--padding-wide);
16
+
17
+ position: relative;
18
+ }
19
+
20
+ h3 {
21
+ margin: var(--title-margin);
22
+ padding-top: 12px;
23
+ font: var(--title-font);
24
+ color: var(--title-text-color);
25
+ }
26
+
27
+ h3[state] {
28
+ position: absolute;
29
+
30
+ right: 20px;
31
+ top: 25px;
32
+ }
33
+
34
+ [page-description] {
35
+ margin: var(--page-description-margin);
36
+ font: var(--page-description-font);
37
+ color: var(--page-description-color);
38
+ }
39
+ `
40
+
41
+ @property({ type: Object }) dataSet?: DataSet
42
+ @property({ type: Object }) dataOoc?: DataOoc
43
+
44
+ render() {
45
+ const history = this.dataOoc?.history || []
46
+ const formatter = new Intl.DateTimeFormat(navigator.language, { dateStyle: 'full', timeStyle: 'short' })
47
+
48
+ return html`
49
+ <ox-data-sample-view .dataSample=${this.dataOoc} .dataSet=${this.dataSet}></ox-data-sample-view>
50
+
51
+ <h3 state>${this.dataOoc?.state || ''}</h3>
52
+
53
+ <h3>history</h3>
54
+ ${history.map(
55
+ ({ user, state, comment, timestamp }) => html`
56
+ <p page-description>
57
+ ${formatter.format(new Date(timestamp))}
58
+ <br />
59
+ ${state} by ${user.name}
60
+ <br />
61
+ ${comment}
62
+ </p>
63
+ `
64
+ )}
65
+ `
66
+ }
67
+ }
@@ -0,0 +1,175 @@
1
+ import '@operato/input/ox-input-file.js'
2
+
3
+ import { css, html, LitElement } from 'lit'
4
+ import { customElement, property } from 'lit/decorators.js'
5
+
6
+ import { OxDataUseCase } from './ox-data-use-case.js'
7
+ import { DataItem, DataSample, DataSet } from './types.js'
8
+
9
+ @customElement('ox-data-sample-view')
10
+ export class OxDataSampleView extends LitElement {
11
+ static styles = css`
12
+ :host {
13
+ display: flex;
14
+ flex-direction: column;
15
+ background-color: var(--main-section-background-color);
16
+ }
17
+
18
+ form {
19
+ flex: 1;
20
+
21
+ display: flex;
22
+ flex-direction: column;
23
+ }
24
+
25
+ h2 {
26
+ margin: var(--title-margin);
27
+ padding-top: 25px;
28
+ font: var(--title-font);
29
+ color: var(--title-text-color);
30
+ }
31
+ [page-description] {
32
+ margin: var(--page-description-margin);
33
+ font: var(--page-description-font);
34
+ color: var(--page-description-color);
35
+ }
36
+
37
+ table {
38
+ border-collapse: collapse;
39
+ margin-bottom: var(--margin-default);
40
+ }
41
+ th {
42
+ padding: var(--th-padding);
43
+ border-top: var(--th-border-top);
44
+ border-bottom: var(--td-border-bottom);
45
+ text-transform: var(--th-text-transform);
46
+ font: var(--th-font);
47
+ color: var(--th-color);
48
+ text-align: left;
49
+ }
50
+ th[item] {
51
+ min-width: 100px;
52
+ }
53
+ th[value] {
54
+ min-width: 100px;
55
+ }
56
+ tr {
57
+ background-color: var(--tr-background-color);
58
+ }
59
+ tr:nth-child(odd) {
60
+ background-color: var(--tr-background-odd-color);
61
+ }
62
+ tr:hover {
63
+ background-color: var(--tr-background-hover-color);
64
+ }
65
+ tr[ooc],
66
+ tr[oos] {
67
+ background-color: #fefbdf;
68
+ }
69
+ td {
70
+ border-bottom: var(--td-border-bottom);
71
+ padding: var(--td-padding);
72
+ font: var(--td-font);
73
+ color: var(--td-color);
74
+ }
75
+ td[name] {
76
+ font-weight: bold;
77
+ }
78
+ td mwc-icon {
79
+ color: var(--status-danger-color);
80
+ }
81
+
82
+ pre {
83
+ tab-size: 2;
84
+ }
85
+ `
86
+
87
+ @property({ type: Object }) dataSet?: DataSet
88
+ @property({ type: Object }) dataSample?: DataSample
89
+
90
+ render() {
91
+ const dataItems = this.dataSet?.dataItems.filter(item => item.active) || []
92
+ const data = this.dataSample?.data || {}
93
+ const formatter = new Intl.DateTimeFormat(navigator.language, { dateStyle: 'full', timeStyle: 'short' })
94
+
95
+ const { name, description, collectedAt, workDate, workShift } = this.dataSample || {}
96
+
97
+ return html` <h2>${name}</h2>
98
+ <p page-description>
99
+ ${description}<br /><br />
100
+ work date: ${workDate} ${workShift}<br />
101
+ collected at: ${formatter.format(new Date(collectedAt!))}
102
+ </p>
103
+
104
+ <form @change=${(e: Event) => this.onChange(e)}>
105
+ <table>
106
+ <tr>
107
+ <th item>item</th>
108
+ <th>description</th>
109
+ <th>unit</th>
110
+ <th value>value</th>
111
+ <th>specification</th>
112
+ <th>OOC</th>
113
+ <th>OOS</th>
114
+ </tr>
115
+ ${dataItems.map(dataItem => {
116
+ const { ooc, oos } = this.evaluateOOC(dataItem, data?.[dataItem.tag]) || {}
117
+ const { name, description, unit } = dataItem
118
+ return html`
119
+ <tr ?ooc=${ooc} ?oos=${oos}>
120
+ <td name>${name}</td>
121
+ <td>${description}</td>
122
+ <td>${unit}</td>
123
+ <td>${this.buildValue(dataItem)}</td>
124
+ <td><pre>${this.buildSpec(dataItem)}</pre></td>
125
+ <td>${ooc ? html`<mwc-icon>done</mwc-icon>` : ''}</td>
126
+ <td>${oos ? html`<mwc-icon>done</mwc-icon>` : ''}</td>
127
+ </tr>
128
+ `
129
+ })}
130
+ </table>
131
+ </form>`
132
+ }
133
+
134
+ private onChange(e: Event) {}
135
+
136
+ private buildSpec(dataItem: DataItem) {
137
+ return OxDataUseCase.elaborateDataItemSpec(this.dataSet!, dataItem)
138
+ }
139
+
140
+ private buildValue(dataItem: DataItem) {
141
+ const { tag, type } = dataItem
142
+
143
+ if (!this.dataSample) {
144
+ return ''
145
+ }
146
+
147
+ const value = this.dataSample?.data[dataItem.tag]
148
+ if (value === undefined) {
149
+ return ''
150
+ }
151
+ const values = value instanceof Array ? value : [value]
152
+
153
+ const elements = values.map((_: any, idx) => {
154
+ const v = value[idx]
155
+
156
+ switch (type) {
157
+ case 'boolean':
158
+ return html` <input type="checkbox" name=${tag} .checked=${v} disabled />`
159
+ break
160
+
161
+ case 'select':
162
+ case 'number':
163
+ case 'string':
164
+ default:
165
+ return String(v === undefined ? '' : v)
166
+ }
167
+ })
168
+
169
+ return type === 'boolean' ? elements : elements.join(', ')
170
+ }
171
+
172
+ private evaluateOOC(dataItem: DataItem, value: any) {
173
+ return OxDataUseCase.evaluateItem(this.dataSet!, dataItem, value)
174
+ }
175
+ }