@operato/input 1.1.21 → 1.1.24

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.
@@ -0,0 +1,517 @@
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
+ [records] {
172
+ flex: 1;
173
+
174
+ overflow: overlay;
175
+ }
176
+
177
+ [records] > div {
178
+ display: flex;
179
+ flex-flow: row nowrap;
180
+ gap: var(--mass-fraction-gap, 4px);
181
+ margin-bottom: var(--margin-narrow);
182
+ }
183
+
184
+ [data-record-new] {
185
+ display: flex;
186
+ flex-flow: row nowrap;
187
+ gap: var(--mass-fraction-gap, 3px);
188
+ margin: var(--margin-narrow) 0;
189
+ }
190
+
191
+ button {
192
+ align-self: center;
193
+ border: var(--button-border);
194
+ border-radius: var(--border-radius);
195
+ background-color: var(--button-background-color);
196
+ padding: var(--mass-fraction-button-padding-vertical, 3px) var(--mass-fraction-button-padding-horizontal, 3px);
197
+ color: var(--button-color);
198
+ cursor: pointer;
199
+ }
200
+ button mwc-icon {
201
+ font-size: var(--fontsize-default);
202
+ }
203
+ button:focus,
204
+ button:hover,
205
+ button:active {
206
+ border: var(--button-active-border);
207
+ background-color: var(--button-background-focus-color);
208
+ color: var(--theme-white-color);
209
+ }
210
+
211
+ input,
212
+ ox-select {
213
+ border: 0;
214
+ border-bottom: var(--border-dark-color);
215
+ font: var(--input-font);
216
+ color: var(--primary-text-color);
217
+ min-width: 50px;
218
+ }
219
+
220
+ input {
221
+ padding: var(--input-padding);
222
+ }
223
+
224
+ ox-select {
225
+ padding: 0;
226
+ }
227
+
228
+ [data-key] {
229
+ flex: 2;
230
+ }
231
+
232
+ [data-value] {
233
+ flex: 1;
234
+ }
235
+
236
+ input:focus {
237
+ outline: none;
238
+ border-bottom: 1px solid var(--primary-color);
239
+ }
240
+
241
+ button.hidden {
242
+ opacity: 0;
243
+ cursor: default;
244
+ }
245
+
246
+ ox-popup-list {
247
+ max-height: 300px;
248
+ overflow: auto;
249
+ left: 0;
250
+ }
251
+
252
+ [records] > [nofraction] {
253
+ display: block;
254
+ text-align: center;
255
+ }
256
+ `
257
+ ]
258
+
259
+ @property({ type: Object }) defaultValue: MassFraction = {}
260
+ @property({ type: Object }) value: MassFraction = {}
261
+
262
+ @queryAll('[data-record]') records!: NodeListOf<HTMLElement>
263
+
264
+ private options = FLUIDS.map(fluid => html`<div option value=${fluid}>${fluid}</div>`)
265
+ private changingNow: boolean = false
266
+
267
+ firstUpdated() {
268
+ this.renderRoot.addEventListener('change', this._onChange.bind(this))
269
+ }
270
+
271
+ render() {
272
+ const value = !this.value || typeof this.value !== 'object' ? {} : this.value
273
+ const arrayed = this._toArray(value)
274
+
275
+ return html`
276
+ <div records>
277
+ ${arrayed.length
278
+ ? arrayed.map(
279
+ (item, idx) => html`
280
+ <div data-record>
281
+ <input type="text" data-key .value=${item.key} disabled />
282
+ <input
283
+ type="number"
284
+ data-value
285
+ placeholder="fraction"
286
+ min="0"
287
+ max="1"
288
+ step="0.01"
289
+ .value=${String(item.value)}
290
+ list="value-template"
291
+ />
292
+ <button class="record-action" @click=${(e: MouseEvent) => this._delete(e)} tabindex="-1">
293
+ <mwc-icon>remove</mwc-icon>
294
+ </button>
295
+ <button
296
+ class="record-action"
297
+ @click=${(e: MouseEvent) => this._up(e)}
298
+ tabindex="-1"
299
+ ?disabled=${idx === 0}
300
+ >
301
+ <mwc-icon>arrow_upward</mwc-icon>
302
+ </button>
303
+ <button
304
+ class="record-action"
305
+ @click=${(e: MouseEvent) => this._down(e)}
306
+ tabindex="-1"
307
+ ?disabled=${idx === arrayed.length - 1}
308
+ >
309
+ <mwc-icon>arrow_downward</mwc-icon>
310
+ </button>
311
+ </div>
312
+ `
313
+ )
314
+ : html`<div nofraction>No Fractions</div>`}
315
+ </div>
316
+
317
+ <div data-record-new>
318
+ <ox-select
319
+ data-key
320
+ placeholder="Fluid"
321
+ .value=${live('')}
322
+ @change=${(e: Event) => {
323
+ e.stopPropagation()
324
+ }}
325
+ >
326
+ <ox-popup-list with-search> ${this.options} </ox-popup-list>
327
+ </ox-select>
328
+
329
+ <input
330
+ type="number"
331
+ data-value
332
+ placeholder="proportion"
333
+ min="0"
334
+ max="1"
335
+ step="0.01"
336
+ value=""
337
+ list="value-template"
338
+ />
339
+ <button class="record-action" @click=${(e: MouseEvent) => this._add()} tabindex="-1">
340
+ <mwc-icon>add</mwc-icon>
341
+ </button>
342
+ <button
343
+ title="fill with the values suggested"
344
+ @click=${() => {
345
+ this.value = { ...this.defaultValue }
346
+ this.dispatchChangeEvent()
347
+ }}
348
+ >
349
+ <mwc-icon>settings_suggest</mwc-icon>
350
+ </button>
351
+ <button
352
+ title="normalize fraction"
353
+ @click=${() => {
354
+ this._normalize()
355
+ }}
356
+ >
357
+ <mwc-icon>repartition</mwc-icon>
358
+ </button>
359
+ </div>
360
+ `
361
+ }
362
+
363
+ _onChange(e: Event) {
364
+ if (this.changingNow) {
365
+ return
366
+ }
367
+
368
+ this.changingNow = true
369
+
370
+ const input = e.target as HTMLInputElement
371
+
372
+ const record = (e.target as Element).closest('[data-record],[data-record-new]') as HTMLElement
373
+
374
+ if (record.hasAttribute('data-record')) {
375
+ this._build()
376
+ } else if (record.hasAttribute('data-record-new') && input.hasAttribute('data-value')) {
377
+ this._add()
378
+ }
379
+
380
+ this.changingNow = false
381
+ }
382
+
383
+ _normalize() {
384
+ const fraction: MassFraction = this.value || {}
385
+ const sum = Object.values(fraction).reduce((a, b) => a + b, 0)
386
+ this.value = Object.keys(fraction).reduce((newvalue, key) => {
387
+ newvalue[key] = fraction[key] / sum
388
+ return newvalue
389
+ }, {} as MassFraction)
390
+
391
+ this.dispatchChangeEvent()
392
+ }
393
+
394
+ _build(includeNewRecord?: boolean) {
395
+ if (includeNewRecord) {
396
+ var records = this.renderRoot.querySelectorAll('[data-record],[data-record-new]') as NodeListOf<HTMLElement>
397
+ } else {
398
+ var records = this.renderRoot.querySelectorAll('[data-record]') as NodeListOf<HTMLElement>
399
+ }
400
+
401
+ var newmap: MassFraction = {}
402
+
403
+ for (var i = 0; i < records.length; i++) {
404
+ var record = records[i]
405
+
406
+ const key = (record.querySelector('[data-key]') as HTMLInputElement).value
407
+ const inputs = record.querySelectorAll(
408
+ '[data-value]:not([style*="display: none"])'
409
+ ) as NodeListOf<HTMLInputElement>
410
+
411
+ if (!inputs || inputs.length == 0) {
412
+ continue
413
+ }
414
+
415
+ var input = inputs[inputs.length - 1]
416
+
417
+ var value = input.value
418
+
419
+ if (key) {
420
+ newmap[key] = Number(value) || 0
421
+ }
422
+ }
423
+
424
+ this.value = newmap
425
+
426
+ this.dispatchChangeEvent()
427
+ }
428
+
429
+ /* map아이템들을 template(dom-repeat)용 배열로 변환하는 함수 */
430
+ _toArray(map: MassFraction) {
431
+ var array: ArrayedMassFraction = []
432
+
433
+ for (var key in map) {
434
+ array.push({
435
+ key: key,
436
+ value: map[key]
437
+ })
438
+ }
439
+
440
+ return array
441
+ }
442
+
443
+ _add() {
444
+ this._build(true)
445
+
446
+ const inputs = this.renderRoot.querySelectorAll(
447
+ '[data-record-new] input:not([style*="display: none"])'
448
+ ) as NodeListOf<HTMLInputElement & { value: any }>
449
+
450
+ for (var i = 0; i < inputs.length; i++) {
451
+ let input = inputs[i]
452
+
453
+ if (input.type == 'checkbox') input.checked = false
454
+ else input.value = ''
455
+ }
456
+
457
+ inputs[0].focus()
458
+ }
459
+
460
+ _delete(e: MouseEvent) {
461
+ const record = (e.target as Element).closest('[data-record]') as HTMLElement
462
+
463
+ ;(record!.querySelector('[data-key]') as HTMLInputElement)!.value = ''
464
+
465
+ this._build()
466
+ }
467
+
468
+ _up(e: MouseEvent) {
469
+ const record = (e.target as Element).closest('[data-record]') as HTMLElement
470
+ const array = Array.from(this.records)
471
+ const index = array.indexOf(record) - 1
472
+
473
+ if (index < 0) {
474
+ return
475
+ }
476
+
477
+ const deleted = array.splice(index, 1)
478
+ array.splice(index + 1, 0, ...deleted)
479
+
480
+ this.value = array.reduce((sum, record) => {
481
+ const key = (record.querySelector('[data-key]') as HTMLInputElement).value
482
+ const value = (record.querySelector('[data-value]') as HTMLInputElement).value
483
+
484
+ sum[key] = Number(value) || 0
485
+ return sum
486
+ }, {} as MassFraction)
487
+
488
+ this.dispatchChangeEvent()
489
+ }
490
+
491
+ _down(e: MouseEvent) {
492
+ const record = (e.target as Element).closest('[data-record]') as HTMLElement
493
+ const array = Array.from(this.records)
494
+ const index = array.indexOf(record)
495
+
496
+ if (index > array.length) {
497
+ return
498
+ }
499
+
500
+ array.splice(index, 1)
501
+ array.splice(index + 1, 0, record)
502
+
503
+ this.value = array.reduce((sum, record) => {
504
+ const key = (record.querySelector('[data-key]') as HTMLInputElement).value
505
+ const value = (record.querySelector('[data-value]') as HTMLInputElement).value
506
+
507
+ sum[key] = Number(value) || 0
508
+ return sum
509
+ }, {} as MassFraction)
510
+
511
+ this.dispatchChangeEvent()
512
+ }
513
+
514
+ dispatchChangeEvent() {
515
+ this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true, detail: this.value }))
516
+ }
517
+ }
@@ -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
+ }