@operato/dataset 1.0.0-alpha.5 → 1.0.0-alpha.52

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 +421 -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 +13 -15
  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 +14 -36
  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.5",
6
+ "version": "1.0.0-alpha.52",
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.5",
39
- "@operato/i18n": "^1.0.0-alpha.5",
40
- "@operato/input": "^1.0.0-alpha.5",
41
- "@operato/layout": "^1.0.0-alpha.5",
42
- "@operato/property-editor": "^1.0.0-alpha.5",
43
- "@operato/shell": "^1.0.0-alpha.5",
44
- "@operato/styles": "^1.0.0-alpha.5",
45
- "@operato/utils": "^1.0.0-alpha.5",
46
- "lit": "^2.2.0"
45
+ "@operato/data-grist": "^1.0.0-alpha.52",
46
+ "@operato/graphql": "^1.0.0-alpha.52",
47
+ "@operato/i18n": "^1.0.0-alpha.52",
48
+ "@operato/input": "^1.0.0-alpha.52",
49
+ "@operato/layout": "^1.0.0-alpha.52",
50
+ "@operato/property-editor": "^1.0.0-alpha.52",
51
+ "@operato/shell": "^1.0.0-alpha.52",
52
+ "@operato/styles": "^1.0.0-alpha.52",
53
+ "@operato/utils": "^1.0.0-alpha.52",
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": "be39304e12d181ad7546b14b3773e9a82a9f3d29"
88
+ "gitHead": "7852d487822b1dd0c578a73b4a1b5f9dec35e3fa"
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'
@@ -3,28 +3,7 @@ import '@operato/input/ox-input-file.js'
3
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 {
@@ -35,6 +14,8 @@ export class OxDataEntryForm extends LitElement {
35
14
  }
36
15
 
37
16
  form {
17
+ flex: 1;
18
+
38
19
  display: flex;
39
20
  flex-direction: column;
40
21
  }
@@ -49,6 +30,7 @@ export class OxDataEntryForm extends LitElement {
49
30
  background-color: var(--main-section-background-color);
50
31
  padding: var(--padding-default) 0;
51
32
  }
33
+
52
34
  div[name] {
53
35
  grid-column: span 2 / auto;
54
36
  font: var(--label-font);
@@ -73,6 +55,7 @@ export class OxDataEntryForm extends LitElement {
73
55
  padding: var(--input-field-padding);
74
56
  font: var(--input-field-font);
75
57
  }
58
+
76
59
  @media only screen and (max-width: 460px) {
77
60
  div[name] {
78
61
  grid-column: span 3 / auto;
@@ -91,28 +74,28 @@ export class OxDataEntryForm extends LitElement {
91
74
  }
92
75
 
93
76
  private onChange(e: Event) {
94
- const value = this.buildValue()
77
+ this.value = this.buildValue()
95
78
 
96
79
  this.dispatchEvent(
97
80
  new CustomEvent('change', {
98
81
  bubbles: true,
99
82
  composed: true,
100
- detail: value
83
+ detail: this.value
101
84
  })
102
85
  )
103
86
  }
104
87
 
105
88
  private buildInputs() {
106
- const dataItems = this.dataSet!.dataItems
89
+ const dataItems = this.dataSet!.dataItems.filter(item => item.active)
107
90
 
108
91
  return (dataItems || []).map(dataItem => {
109
- const { name, description, tag, type, quota = 1, options = {} } = dataItem
92
+ const { name, description, tag, type, quota = 1, options = {}, unit } = dataItem
110
93
 
111
94
  const samples = new Array(quota).fill(0)
112
- const value = this.value?.[tag]
95
+ const value = this.value && this.value[tag]
113
96
 
114
97
  const elements = samples.map((_, idx) => {
115
- const v = quota <= 1 ? value : value instanceof Array && value[idx]
98
+ const v = value instanceof Array ? value[idx] : idx === 0 ? value : undefined
116
99
 
117
100
  switch (type) {
118
101
  case 'select':
@@ -148,7 +131,7 @@ export class OxDataEntryForm extends LitElement {
148
131
  })
149
132
 
150
133
  return html` <label .title=${description}>
151
- <div name>${name}</div>
134
+ <div name>${name}${unit ? `(${unit})` : ''}</div>
152
135
  <div elements>${elements}</div>
153
136
  </label>`
154
137
  })
@@ -158,19 +141,14 @@ export class OxDataEntryForm extends LitElement {
158
141
  const dataItems = this.dataSet!.dataItems
159
142
 
160
143
  return (dataItems || []).reduce((sum, dataItem) => {
161
- const { tag, quota, type } = dataItem
144
+ const { tag, type } = dataItem
162
145
 
163
146
  const editors = Array.prototype.slice.call(
164
147
  this.renderRoot.querySelectorAll(`[name=${tag}]`) as NodeListOf<HTMLInputElement>
165
148
  ) as HTMLInputElement[]
166
149
 
167
150
  if (editors.length > 0) {
168
- sum[tag] =
169
- editors.length > 1
170
- ? editors.map(editor => (type === 'boolean' ? editor.checked : editor.value))
171
- : type === 'boolean'
172
- ? editors[0].checked
173
- : editors[0].value
151
+ sum[tag] = editors.map(editor => (type === 'boolean' ? editor.checked : editor.value))
174
152
  }
175
153
 
176
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
+ }