@operato/input 1.1.21 → 1.1.23

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/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@operato/input",
3
3
  "description": "Webcomponents for input following open-wc recommendations",
4
4
  "author": "heartyoh@hatiolab.com",
5
- "version": "1.1.21",
5
+ "version": "1.1.23",
6
6
  "main": "dist/src/index.js",
7
7
  "module": "dist/src/index.js",
8
8
  "publishConfig": {
@@ -46,7 +46,8 @@
46
46
  "./ox-input-table.js": "./dist/src/ox-input-table.js",
47
47
  "./ox-input-scene-component-id.js": "./dist/src/ox-input-scene-component-id.js",
48
48
  "./ox-input-work-shift.js": "./dist/src/ox-input-work-shift.js",
49
- "./ox-input-hashtags.js": "./dist/src/ox-input-hashtags.js"
49
+ "./ox-input-hashtags.js": "./dist/src/ox-input-hashtags.js",
50
+ "./ox-input-mass-fraction.js": "./dist/src/ox-input-mass-fraction.js"
50
51
  },
51
52
  "scripts": {
52
53
  "clean": "npx rimraf dist",
@@ -66,7 +67,7 @@
66
67
  "@material/mwc-icon": "^0.27.0",
67
68
  "@operato/color-picker": "^1.1.13",
68
69
  "@operato/i18n": "^1.1.13",
69
- "@operato/popup": "^1.1.13",
70
+ "@operato/popup": "^1.1.23",
70
71
  "@operato/styles": "^1.1.13",
71
72
  "@operato/utils": "^1.1.15",
72
73
  "@polymer/paper-dropdown-menu": "^3.2.0",
@@ -114,5 +115,5 @@
114
115
  "prettier --write"
115
116
  ]
116
117
  },
117
- "gitHead": "5d0a919204e43656222fe0bdf6d81874912b172b"
118
+ "gitHead": "073fdb17432e4c45d55ad8f6fd0ad90158d433f0"
118
119
  }
@@ -0,0 +1,482 @@
1
+ /**
2
+ * @license Copyright © HatioLab Inc. All rights reserved.
3
+ */
4
+
5
+ import '@material/mwc-icon'
6
+ import '@operato/popup/ox-popup-list.js'
7
+ import { ScrollbarStyles } from '@operato/styles'
8
+
9
+ import { css, html } from 'lit'
10
+ import { customElement, property, queryAll } from 'lit/decorators.js'
11
+ import { live } from 'lit/directives/live.js'
12
+
13
+ import { OxFormField } from './ox-form-field'
14
+ import './ox-select.js'
15
+
16
+ const FLUIDS = [
17
+ '1-Butene',
18
+ 'Acetone',
19
+ 'Air',
20
+ 'Ammonia',
21
+ 'Argon',
22
+ 'Benzene',
23
+ 'CO2',
24
+ 'CarbonMonoxide',
25
+ 'CarbonylSulfide',
26
+ 'CycloHexane',
27
+ 'CycloPropane',
28
+ 'Cyclopentane',
29
+ 'D4',
30
+ 'D5',
31
+ 'D6',
32
+ 'Deuterium',
33
+ 'Dichloroethane',
34
+ 'DiethylEther',
35
+ 'DimethylCarbonate',
36
+ 'DimethylEther',
37
+ 'Ethane',
38
+ 'Ethanol',
39
+ 'EthylBenzene',
40
+ 'Ethylene',
41
+ 'EthyleneOxide',
42
+ 'Fluorine',
43
+ 'HFE143m',
44
+ 'HeavyWater',
45
+ 'Helium',
46
+ 'Hydrogen',
47
+ 'HydrogenChloride',
48
+ 'HydrogenSulfide',
49
+ 'IsoButane',
50
+ 'IsoButene',
51
+ 'Isohexane',
52
+ 'Isopentane',
53
+ 'Krypton',
54
+ 'MD2M',
55
+ 'MD3M',
56
+ 'MD4M',
57
+ 'MDM',
58
+ 'MM',
59
+ 'Methane',
60
+ 'Methanol',
61
+ 'MethylLinoleate',
62
+ 'MethylLinolenate',
63
+ 'MethylOleate',
64
+ 'MethylPalmitate',
65
+ 'MethylStearate',
66
+ 'Neon',
67
+ 'Neopentane',
68
+ 'Nitrogen',
69
+ 'NitrousOxide',
70
+ 'Novec649',
71
+ 'OrthoDeuterium',
72
+ 'OrthoHydrogen',
73
+ 'Oxygen',
74
+ 'ParaDeuterium',
75
+ 'ParaHydrogen',
76
+ 'Propylene',
77
+ 'Propyne',
78
+ 'R11',
79
+ 'R113',
80
+ 'R114',
81
+ 'R115',
82
+ 'R116',
83
+ 'R12',
84
+ 'R123',
85
+ 'R1233zd(E)',
86
+ 'R1234yf',
87
+ 'R1234ze(E)',
88
+ 'R1234ze(Z)',
89
+ 'R124',
90
+ 'R1243zf',
91
+ 'R125',
92
+ 'R13',
93
+ 'R134a',
94
+ 'R13I1',
95
+ 'R14',
96
+ 'R141b',
97
+ 'R142b',
98
+ 'R143a',
99
+ 'R152A',
100
+ 'R161',
101
+ 'R21',
102
+ 'R218',
103
+ 'R22',
104
+ 'R227EA',
105
+ 'R23',
106
+ 'R236EA',
107
+ 'R236FA',
108
+ 'R245ca',
109
+ 'R245fa',
110
+ 'R32',
111
+ 'R365MFC',
112
+ 'R40',
113
+ 'R404A',
114
+ 'R407C',
115
+ 'R41',
116
+ 'R410A',
117
+ 'R507A',
118
+ 'RC318',
119
+ 'SES36',
120
+ 'SulfurDioxide',
121
+ 'SulfurHexafluorid',
122
+ 'Toluene',
123
+ 'Water',
124
+ 'Xenon',
125
+ 'cis-2-Butene',
126
+ 'm-Xylene',
127
+ 'n-Butane',
128
+ 'n-Decane',
129
+ 'n-Dodecane',
130
+ 'n-Heptane',
131
+ 'n-Hexane',
132
+ 'n-Nonane',
133
+ 'n-Octane',
134
+ 'n-Pentane',
135
+ 'n-Propane',
136
+ 'n-Undecane',
137
+ 'o-Xylene',
138
+ 'p-Xylene',
139
+ 'trans-2-Butene'
140
+ ]
141
+
142
+ type ArrayElement<ArrayType extends readonly unknown[]> = ArrayType extends readonly (infer ElementType)[]
143
+ ? ElementType
144
+ : never
145
+
146
+ type FLUID = ArrayElement<typeof FLUIDS>
147
+
148
+ type MassFraction = { [key: FLUID]: number }
149
+ type ArrayedMassFraction = { key: FLUID; value: number }[]
150
+
151
+ /**
152
+ input component for mass-fraction map
153
+
154
+ Example:
155
+
156
+ <ox-input-mass-fraction
157
+ value=${map}
158
+ </ox-input-mass-fraction>
159
+ */
160
+ @customElement('ox-input-mass-fraction')
161
+ export class OxInputMassFraction extends OxFormField {
162
+ static styles = [
163
+ ScrollbarStyles,
164
+ css`
165
+ :host {
166
+ display: flex;
167
+ flex-direction: column;
168
+ justify-content: space-between;
169
+ }
170
+
171
+ :host > div {
172
+ display: flex;
173
+ flex-flow: row nowrap;
174
+ gap: var(--mass-fraction-gap, 4px);
175
+ margin-bottom: var(--margin-narrow);
176
+ }
177
+
178
+ button {
179
+ border: var(--button-border);
180
+ border-radius: var(--border-radius);
181
+ background-color: var(--button-background-color);
182
+ padding: var(--mass-fraction-button-padding-vertical, 2px) var(--mass-fraction-button-padding-horizontal, 4px);
183
+ color: var(--button-color);
184
+ cursor: pointer;
185
+ }
186
+ button mwc-icon {
187
+ font-size: var(--fontsize-default);
188
+ }
189
+ button:focus,
190
+ button:hover,
191
+ button:active {
192
+ border: var(--button-activ-border);
193
+ background-color: var(--button-background-focus-color);
194
+ color: var(--theme-white-color);
195
+ }
196
+
197
+ input,
198
+ ox-select {
199
+ border: 0;
200
+ border-bottom: var(--border-dark-color);
201
+ padding: var(--input-padding);
202
+ font: var(--input-font);
203
+ color: var(--primary-text-color);
204
+ min-width: 50px;
205
+ }
206
+
207
+ [data-key] {
208
+ flex: 2;
209
+ }
210
+
211
+ [data-value] {
212
+ flex: 1;
213
+ }
214
+
215
+ input:focus {
216
+ outline: none;
217
+ border-bottom: 1px solid var(--primary-color);
218
+ }
219
+
220
+ button.hidden {
221
+ opacity: 0;
222
+ cursor: default;
223
+ }
224
+
225
+ ox-popup-list {
226
+ max-height: 300px;
227
+ overflow: auto;
228
+ left: 0;
229
+ }
230
+ `
231
+ ]
232
+
233
+ @property({ type: Object }) defaultValue: MassFraction = {}
234
+ @property({ type: Object }) value: MassFraction = {}
235
+
236
+ private options = FLUIDS.map(fluid => html`<div option value=${fluid}>${fluid}</div>`)
237
+ private changingNow: boolean = false
238
+
239
+ firstUpdated() {
240
+ this.renderRoot.addEventListener('change', this._onChange.bind(this))
241
+ }
242
+
243
+ render() {
244
+ const value = !this.value || typeof this.value !== 'object' ? {} : this.value
245
+ const arrayed = this._toArray(value)
246
+
247
+ return html`
248
+ ${arrayed.map(
249
+ (item, idx) => html`
250
+ <div data-record>
251
+ <input type="text" data-key .value=${item.key} disabled />
252
+ <input
253
+ type="number"
254
+ data-value
255
+ placeholder="fraction"
256
+ min="0"
257
+ max="1"
258
+ step="0.01"
259
+ .value=${String(item.value)}
260
+ list="value-template"
261
+ />
262
+ <button class="record-action" @click=${(e: MouseEvent) => this._delete(e)} tabindex="-1">
263
+ <mwc-icon>remove</mwc-icon>
264
+ </button>
265
+ <button class="record-action" @click=${(e: MouseEvent) => this._up(e)} tabindex="-1" ?disabled=${idx === 0}>
266
+ <mwc-icon>arrow_upward</mwc-icon>
267
+ </button>
268
+ <button
269
+ class="record-action"
270
+ @click=${(e: MouseEvent) => this._down(e)}
271
+ tabindex="-1"
272
+ ?disabled=${idx === arrayed.length - 1}
273
+ >
274
+ <mwc-icon>arrow_downward</mwc-icon>
275
+ </button>
276
+ </div>
277
+ `
278
+ )}
279
+
280
+ <div data-record-new>
281
+ <ox-select
282
+ data-key
283
+ placeholder="fluid"
284
+ .value=${live('')}
285
+ @change=${(e: Event) => {
286
+ e.stopPropagation()
287
+ }}
288
+ >
289
+ <ox-popup-list with-search> ${this.options} </ox-popup-list>
290
+ </ox-select>
291
+
292
+ <input
293
+ type="number"
294
+ data-value
295
+ placeholder="proportion"
296
+ min="0"
297
+ max="1"
298
+ step="0.01"
299
+ value=""
300
+ list="value-template"
301
+ />
302
+ <button class="record-action" @click=${(e: MouseEvent) => this._add()} tabindex="-1">
303
+ <mwc-icon>add</mwc-icon>
304
+ </button>
305
+ <button
306
+ title="fill with the values suggested"
307
+ @click=${() => {
308
+ this.value = { ...this.defaultValue }
309
+ this.dispatchChangeEvent()
310
+ }}
311
+ >
312
+ <mwc-icon>settings_suggest</mwc-icon>
313
+ </button>
314
+ <button
315
+ title="normalize fraction"
316
+ @click=${() => {
317
+ this._normalize()
318
+ }}
319
+ >
320
+ <mwc-icon>repartition</mwc-icon>
321
+ </button>
322
+ </div>
323
+ `
324
+ }
325
+
326
+ _onChange(e: Event) {
327
+ if (this.changingNow) {
328
+ return
329
+ }
330
+
331
+ this.changingNow = true
332
+
333
+ const input = e.target as HTMLInputElement
334
+
335
+ const record = (e.target as Element).closest('[data-record],[data-record-new]') as HTMLElement
336
+
337
+ if (record.hasAttribute('data-record')) {
338
+ this._build()
339
+ } else if (record.hasAttribute('data-record-new') && input.hasAttribute('data-value')) {
340
+ this._add()
341
+ }
342
+
343
+ this.changingNow = false
344
+ }
345
+
346
+ _normalize() {
347
+ const fraction: MassFraction = this.value || {}
348
+ const sum = Object.values(fraction).reduce((a, b) => a + b, 0)
349
+ this.value = Object.keys(fraction).reduce((newvalue, key) => {
350
+ newvalue[key] = fraction[key] / sum
351
+ return newvalue
352
+ }, {} as MassFraction)
353
+
354
+ this.dispatchChangeEvent()
355
+ }
356
+
357
+ _build(includeNewRecord?: boolean) {
358
+ if (includeNewRecord) {
359
+ var records = this.renderRoot.querySelectorAll('[data-record],[data-record-new]') as NodeListOf<HTMLElement>
360
+ } else {
361
+ var records = this.renderRoot.querySelectorAll('[data-record]') as NodeListOf<HTMLElement>
362
+ }
363
+
364
+ var newmap: MassFraction = {}
365
+
366
+ for (var i = 0; i < records.length; i++) {
367
+ var record = records[i]
368
+
369
+ const key = (record.querySelector('[data-key]') as HTMLInputElement).value
370
+ const inputs = record.querySelectorAll(
371
+ '[data-value]:not([style*="display: none"])'
372
+ ) as NodeListOf<HTMLInputElement>
373
+
374
+ if (!inputs || inputs.length == 0) {
375
+ continue
376
+ }
377
+
378
+ var input = inputs[inputs.length - 1]
379
+
380
+ var value = input.value
381
+
382
+ if (key) {
383
+ newmap[key] = Number(value) || 0
384
+ }
385
+ }
386
+
387
+ this.value = newmap
388
+
389
+ this.dispatchChangeEvent()
390
+ }
391
+
392
+ /* map아이템들을 template(dom-repeat)용 배열로 변환하는 함수 */
393
+ _toArray(map: MassFraction) {
394
+ var array: ArrayedMassFraction = []
395
+
396
+ for (var key in map) {
397
+ array.push({
398
+ key: key,
399
+ value: map[key]
400
+ })
401
+ }
402
+
403
+ return array
404
+ }
405
+
406
+ _add() {
407
+ this._build(true)
408
+
409
+ const inputs = this.renderRoot.querySelectorAll(
410
+ '[data-record-new] input:not([style*="display: none"])'
411
+ ) as NodeListOf<HTMLInputElement & { value: any }>
412
+
413
+ for (var i = 0; i < inputs.length; i++) {
414
+ let input = inputs[i]
415
+
416
+ if (input.type == 'checkbox') input.checked = false
417
+ else input.value = ''
418
+ }
419
+
420
+ inputs[0].focus()
421
+ }
422
+
423
+ _delete(e: MouseEvent) {
424
+ const record = (e.target as Element).closest('[data-record]') as HTMLElement
425
+
426
+ ;(record!.querySelector('[data-key]') as HTMLInputElement)!.value = ''
427
+
428
+ this._build()
429
+ }
430
+
431
+ @queryAll('[data-record]') records!: NodeListOf<HTMLElement>
432
+
433
+ _up(e: MouseEvent) {
434
+ const record = (e.target as Element).closest('[data-record]') as HTMLElement
435
+ const array = Array.from(this.records)
436
+ const index = array.indexOf(record) - 1
437
+
438
+ if (index < 0) {
439
+ return
440
+ }
441
+
442
+ const deleted = array.splice(index, 1)
443
+ array.splice(index + 1, 0, ...deleted)
444
+
445
+ this.value = array.reduce((sum, record) => {
446
+ const key = (record.querySelector('[data-key]') as HTMLInputElement).value
447
+ const value = (record.querySelector('[data-value]') as HTMLInputElement).value
448
+
449
+ sum[key] = Number(value) || 0
450
+ return sum
451
+ }, {} as MassFraction)
452
+
453
+ this.dispatchChangeEvent()
454
+ }
455
+
456
+ _down(e: MouseEvent) {
457
+ const record = (e.target as Element).closest('[data-record]') as HTMLElement
458
+ const array = Array.from(this.records)
459
+ const index = array.indexOf(record)
460
+
461
+ if (index > array.length) {
462
+ return
463
+ }
464
+
465
+ array.splice(index, 1)
466
+ array.splice(index + 1, 0, record)
467
+
468
+ this.value = array.reduce((sum, record) => {
469
+ const key = (record.querySelector('[data-key]') as HTMLInputElement).value
470
+ const value = (record.querySelector('[data-value]') as HTMLInputElement).value
471
+
472
+ sum[key] = Number(value) || 0
473
+ return sum
474
+ }, {} as MassFraction)
475
+
476
+ this.dispatchChangeEvent()
477
+ }
478
+
479
+ dispatchChangeEvent() {
480
+ this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true, detail: this.value }))
481
+ }
482
+ }
@@ -175,6 +175,7 @@ export class OxInputUnit extends OxFormField {
175
175
  padding: var(--input-padding);
176
176
  font: var(--input-font);
177
177
  color: var(--primary-text-color);
178
+ min-width: 120px;
178
179
  }
179
180
 
180
181
  input:focus {
@@ -198,6 +199,7 @@ export class OxInputUnit extends OxFormField {
198
199
  margin-left: var(--margin-narrow);
199
200
  font: var(--label-font);
200
201
  color: var(--label-color);
202
+ min-width: 24px;
201
203
  }
202
204
  `
203
205
  ]
package/src/ox-select.ts CHANGED
@@ -119,7 +119,7 @@ export class Select extends OxFormField {
119
119
  }
120
120
 
121
121
  async updated(changes: PropertyValues<this>) {
122
- const popupList = this.querySelector('ox-popup-list') as OxPopupList
122
+ // const popupList = this.querySelector('ox-popup-list') as OxPopupList
123
123
 
124
124
  if (changes.has('value')) {
125
125
  const popupList = this.querySelector('ox-popup-list') as OxPopupList
@@ -129,8 +129,6 @@ export class Select extends OxFormField {
129
129
 
130
130
  this.label = popupList.getSelectedLabels()
131
131
  }
132
-
133
- this.label = popupList.getSelectedLabels()
134
132
  }
135
133
 
136
134
  expand() {
@@ -0,0 +1,54 @@
1
+ import '../src/ox-input-mass-fraction.js'
2
+
3
+ import { html, TemplateResult } from 'lit'
4
+
5
+ export default {
6
+ title: 'ox-input-mass-fraction',
7
+ component: 'ox-input-mass-fraction',
8
+ argTypes: {
9
+ name: { control: 'text' },
10
+ value: { control: 'object' },
11
+ defaultValue: { control: 'object' }
12
+ }
13
+ }
14
+
15
+ interface Story<T> {
16
+ (args: T): TemplateResult
17
+ args?: Partial<T>
18
+ argTypes?: Record<string, unknown>
19
+ }
20
+
21
+ interface ArgTypes {
22
+ name?: string
23
+ value?: object
24
+ defaultValue?: object
25
+ }
26
+
27
+ const Template: Story<ArgTypes> = ({ name = 'mass-fraction', value = {}, defaultValue = {} }: ArgTypes) => html`
28
+ <link href="/themes/app-theme.css" rel="stylesheet" />
29
+ <link href="https://fonts.googleapis.com/css?family=Material+Icons&display=block" rel="stylesheet" />
30
+ <style>
31
+ body {
32
+ }
33
+ </style>
34
+
35
+ <ox-input-mass-fraction
36
+ @change=${(e: Event) => {
37
+ console.log((e.target as HTMLInputElement).value)
38
+ }}
39
+ name=${name}
40
+ .value=${value}
41
+ .defaultValue=${defaultValue}
42
+ >
43
+ </ox-input-mass-fraction>
44
+ `
45
+
46
+ export const Regular = Template.bind({})
47
+ Regular.args = {
48
+ name: 'mass-fraction',
49
+ defaultValue: {
50
+ R410A: 0.8,
51
+ Water: 0.1,
52
+ '1-Butene': 0.1
53
+ }
54
+ }