@operato/input 0.4.2 → 1.0.0-alpha.10

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 (73) hide show
  1. package/CHANGELOG.md +93 -0
  2. package/README.md +9 -6
  3. package/assets/images/icon-editor-gradient-direction.png +0 -0
  4. package/assets/images/icon-properties-label.png +0 -0
  5. package/demo/index-color-gradient.html +35 -0
  6. package/demo/index-color-stops.html +62 -0
  7. package/demo/index-color.html +35 -0
  8. package/demo/index-file.html +41 -0
  9. package/demo/index-image.html +1 -1
  10. package/demo/index-multiple-colors.html +37 -0
  11. package/demo/index-options.html +43 -0
  12. package/demo/index-range.html +40 -0
  13. package/demo/index.html +16 -56
  14. package/dist/src/index.d.ts +15 -11
  15. package/dist/src/index.js +15 -11
  16. package/dist/src/index.js.map +1 -1
  17. package/dist/src/ox-checkbox.js +1 -1
  18. package/dist/src/ox-checkbox.js.map +1 -1
  19. package/dist/src/ox-input-code.d.ts +4 -4
  20. package/dist/src/ox-input-code.js +8 -8
  21. package/dist/src/ox-input-code.js.map +1 -1
  22. package/dist/src/ox-input-color-stops.d.ts +71 -0
  23. package/dist/src/ox-input-color-stops.js +445 -0
  24. package/dist/src/ox-input-color-stops.js.map +1 -0
  25. package/dist/src/ox-input-color.d.ts +176 -0
  26. package/dist/src/ox-input-color.js +298 -0
  27. package/dist/src/ox-input-color.js.map +1 -0
  28. package/dist/src/ox-input-data.d.ts +2 -2
  29. package/dist/src/ox-input-data.js +2 -2
  30. package/dist/src/ox-input-data.js.map +1 -1
  31. package/dist/src/ox-input-file.d.ts +2 -0
  32. package/dist/src/ox-input-file.js +6 -1
  33. package/dist/src/ox-input-file.js.map +1 -1
  34. package/dist/src/ox-input-multiple-colors.d.ts +28 -0
  35. package/dist/src/ox-input-multiple-colors.js +113 -0
  36. package/dist/src/ox-input-multiple-colors.js.map +1 -0
  37. package/dist/src/ox-input-options.d.ts +22 -0
  38. package/dist/src/ox-input-options.js +137 -0
  39. package/dist/src/ox-input-options.js.map +1 -0
  40. package/dist/src/ox-input-range.d.ts +4 -0
  41. package/dist/src/ox-input-range.js +161 -0
  42. package/dist/src/ox-input-range.js.map +1 -0
  43. package/dist/src/{ox-input-id.d.ts → ox-input-scene-component-id.d.ts} +0 -0
  44. package/dist/src/{ox-input-id.js → ox-input-scene-component-id.js} +2 -2
  45. package/dist/src/ox-input-scene-component-id.js.map +1 -0
  46. package/dist/src/ox-input-stack.d.ts +1 -1
  47. package/dist/src/ox-input-stack.js +1 -1
  48. package/dist/src/ox-input-stack.js.map +1 -1
  49. package/dist/tsconfig.tsbuildinfo +1 -1
  50. package/package.json +16 -7
  51. package/src/index.ts +15 -11
  52. package/src/ox-checkbox.ts +1 -1
  53. package/src/ox-input-background-pattern.ts.xxx +163 -0
  54. package/src/ox-input-code.ts +9 -10
  55. package/src/ox-input-color-gradient.ts.xxx +343 -0
  56. package/src/ox-input-color-stops.ts +499 -0
  57. package/src/ox-input-color.ts +323 -0
  58. package/src/ox-input-data.ts +5 -5
  59. package/src/ox-input-file.ts +8 -3
  60. package/src/ox-input-fill-style.ts.xxx +361 -0
  61. package/src/ox-input-keyvalues.ts.ing +10 -10
  62. package/src/ox-input-multiple-colors.ts +113 -0
  63. package/src/ox-input-options.ts +165 -0
  64. package/src/ox-input-range.ts +147 -0
  65. package/src/ox-input-ranges.ts.ing +12 -12
  66. package/src/{ox-input-id.ts → ox-input-scene-component-id.ts} +1 -1
  67. package/src/ox-input-stack.ts +1 -1
  68. package/test/property-angle.test.ts.backup +1 -1
  69. package/translations/en.json +1 -0
  70. package/translations/ko.json +1 -0
  71. package/translations/ms.json +1 -0
  72. package/translations/zh.json +1 -0
  73. package/dist/src/ox-input-id.js.map +0 -1
@@ -0,0 +1,361 @@
1
+ /**
2
+ * @license Copyright © HatioLab Inc. All rights reserved.
3
+ */
4
+
5
+ import '@operato/i18n/ox-i18n.js'
6
+ import './ox-input-color'
7
+ import './ox-input-color-gradient'
8
+ import './ox-input-background-pattern'
9
+
10
+ import { BackgroundPatternOption, OxInputBackgroundPattern } from './ox-input-background-pattern'
11
+ import { GradientOption, OxInputColorGradient } from './ox-input-color-gradient'
12
+ import { PropertyValues, css, html } from 'lit'
13
+ import { customElement, property, query } from 'lit/decorators.js'
14
+
15
+ import { ColorStop } from './ox-input-color-stops'
16
+ import { OxFormField } from './ox-form-field'
17
+
18
+ export type FillStyle =
19
+ | {
20
+ type?: 'no' | 'solid' | 'gradient' | 'pattern'
21
+ gradientType?: 'linear' | 'radial'
22
+ colorStops?: ColorStop[]
23
+ rotation?: number
24
+ center?: 'center' | 'left-top' | 'right-top' | 'right-bottom' | 'left-bottom'
25
+ image?: HTMLImageElement | string
26
+ offsetX?: number
27
+ offsetY?: number
28
+ width?: number
29
+ height?: number
30
+ align?: 'left-top' | 'top' | 'right-top' | 'left' | 'center' | 'right' | 'left-bottom' | 'bottom' | 'right-bottom'
31
+ fitPattern?: boolean
32
+ }
33
+ | 'no'
34
+ | string
35
+
36
+ @customElement('ox-input-color-style')
37
+ export class OxInputColorStyle extends OxFormField {
38
+ static styles = css`
39
+ :host {
40
+ display: flex;
41
+ flex-direction: column;
42
+ }
43
+
44
+ [fill-type] {
45
+ display: flex;
46
+ margin: 0 0 14px 0;
47
+ }
48
+
49
+ [fill-type] * {
50
+ flex: auto;
51
+ margin: 0;
52
+ text-align: left;
53
+ align-self: center;
54
+ }
55
+
56
+ .grid-10 {
57
+ display: grid;
58
+
59
+ grid-template-columns: repeat(10, 1fr);
60
+ grid-gap: 5px;
61
+ grid-auto-rows: minmax(24px, auto);
62
+ }
63
+
64
+ .grid-10 > ox-input-color {
65
+ grid-column: span 4;
66
+ }
67
+
68
+ .grid-10 > .icon-only-label {
69
+ grid-column: span 1;
70
+
71
+ background: url(/assets/images/icon-properties-label.png) no-repeat;
72
+ float: left;
73
+ margin: 0;
74
+ }
75
+
76
+ .icon-only-label.color {
77
+ background-position: 70% -498px;
78
+ }
79
+
80
+ [editors] > :not([active]) {
81
+ display: none;
82
+ }
83
+ `
84
+
85
+ @property({ type: Object }) value?: FillStyle
86
+ @property({ type: String }) fillType?: string
87
+ @property({ type: String }) solid?: string
88
+ @property({ type: Object }) gradient?: GradientOption
89
+ @property({ type: Object }) pattern?: BackgroundPatternOption
90
+
91
+ private _block_reset: boolean = false
92
+
93
+ updated(changes: PropertyValues<this>) {
94
+ changes.has('value') && this._onChangedValue(this.value || {})
95
+ }
96
+
97
+ render() {
98
+ return html`
99
+ <div @change=${(e: Event) => this._onChangedFillType(e)} fill-type>
100
+ <input
101
+ type="radio"
102
+ id="fill-type-no"
103
+ name="fill-type"
104
+ value="no"
105
+ .checked=${!this.fillType || this.fillType == 'no'}
106
+ />
107
+ <label for="fill-type-no"><ox-i18n msgid="label.no-fill">no fill</ox-i18n></label>
108
+ <input type="radio" id="fill-type-solid" name="fill-type" value="solid" .checked=${this.fillType == 'solid'} />
109
+ <label for="fill-type-solid"><ox-i18n msgid="label.solid">solid</ox-i18n></label>
110
+ <input
111
+ type="radio"
112
+ id="fill-type-gradient"
113
+ name="fill-type"
114
+ value="gradient"
115
+ .checked=${this.fillType == 'gradient'}
116
+ />
117
+ <label for="fill-type-gradient"><ox-i18n msgid="label.gradient">gradient</ox-i18n></label>
118
+ <input
119
+ type="radio"
120
+ id="fill-type-pattern"
121
+ name="fill-type"
122
+ value="pattern"
123
+ .checked=${this.fillType == 'pattern'}
124
+ />
125
+ <label for="fill-type-pattern"><ox-i18n msgid="label.pattern">pattern</ox-i18n></label>
126
+ </div>
127
+
128
+ <div editors>
129
+ <div ?active=${this.fillType == 'no'}></div>
130
+
131
+ <div class="grid-10" ?active=${this.fillType == 'solid'}>
132
+ <label class="icon-only-label color"></label>
133
+ <ox-input-color @change=${(e: Event) => this._onChangedSolid(e)} .value=${this.solid}> </ox-input-color>
134
+ </div>
135
+
136
+ <div ?active=${this.fillType == 'gradient'}>
137
+ <ox-input-color-gradient @change=${(e: Event) => this._onChandedGradient(e)} .value=${this.gradient}>
138
+ </ox-input-color-gradient>
139
+ </div>
140
+
141
+ <div ?active=${this.fillType == 'pattern'}>
142
+ <ox-input-background-pattern @change=${(e: Event) => this._onChangedPattern(e)} .value=${this.pattern}>
143
+ </ox-input-background-pattern>
144
+ </div>
145
+ </div>
146
+ `
147
+ }
148
+
149
+ async _onChangedValue(value: FillStyle) {
150
+ /*
151
+ * this._block_reset의 역할은 내부 사용자 인터렉션에 의한 value의 변경시에는 각 type별 이전값을 유지하기 위함이다.
152
+ */
153
+ await this.requestUpdate()
154
+
155
+ /* 설정 값에 따라서, 멤버 속성을 설정한다. */
156
+ if (!value) {
157
+ this.fillType = 'no'
158
+
159
+ if (!this._block_reset) {
160
+ this.solid = undefined
161
+ this.gradient = undefined
162
+ this.pattern = undefined
163
+ }
164
+
165
+ this._block_reset = false
166
+ return
167
+ }
168
+
169
+ switch (typeof value) {
170
+ case 'string':
171
+ this.fillType = 'solid'
172
+ this.solid = value
173
+
174
+ if (!this._block_reset) {
175
+ this.gradient = undefined
176
+ this.pattern = undefined
177
+ }
178
+ break
179
+ case 'object':
180
+ this.fillType = value.type
181
+
182
+ if (value.type === 'gradient') {
183
+ this.gradient = {
184
+ type: value.gradientType || 'linear',
185
+ colorStops: value.colorStops || [
186
+ {
187
+ position: 0,
188
+ color: this.solid || '#000000'
189
+ },
190
+ {
191
+ position: 1,
192
+ color: this.solid || '#FFFFFF'
193
+ }
194
+ ],
195
+ rotation: Number(value.rotation) || 0,
196
+ center: value.center
197
+ }
198
+
199
+ if (!this._block_reset) {
200
+ this.pattern = undefined
201
+ this.solid = undefined
202
+ }
203
+ } else if (value.type === 'pattern') {
204
+ this.pattern = {
205
+ image: value.image,
206
+ offsetX: Number(value.offsetX) || 0,
207
+ offsetY: Number(value.offsetY) || 0,
208
+ width: Number(value.width),
209
+ height: Number(value.height),
210
+ align: value.align,
211
+ fitPattern: value.fitPattern
212
+ }
213
+
214
+ if (!this._block_reset) {
215
+ this.gradient = undefined
216
+ this.solid = undefined
217
+ }
218
+ }
219
+
220
+ break
221
+ default:
222
+ }
223
+
224
+ this._block_reset = false
225
+ }
226
+
227
+ _onChangedFillType(e: Event) {
228
+ const element = e.target as HTMLInputElement
229
+ this.fillType = element.value
230
+
231
+ switch (this.fillType) {
232
+ case 'gradient':
233
+ if (!this.gradient) {
234
+ this.gradient = {
235
+ type: 'linear',
236
+ colorStops: [
237
+ {
238
+ position: 0,
239
+ color: this.solid || '#000000'
240
+ },
241
+ {
242
+ position: 1,
243
+ color: this.solid || '#FFFFFF'
244
+ }
245
+ ],
246
+ rotation: 0,
247
+ center: 'center'
248
+ }
249
+ }
250
+
251
+ this.value = {
252
+ type: 'gradient',
253
+ gradientType: this.gradient.type || 'linear',
254
+ colorStops: this.gradient.colorStops || [
255
+ {
256
+ position: 0,
257
+ color: this.solid || '#000000'
258
+ },
259
+ {
260
+ position: 1,
261
+ color: this.solid || '#FFFFFF'
262
+ }
263
+ ],
264
+ rotation: Number(this.gradient.rotation) || 0,
265
+ center: this.gradient.center
266
+ }
267
+ break
268
+
269
+ case 'pattern':
270
+ if (!this.pattern) this.pattern = {}
271
+
272
+ this.value = {
273
+ type: 'pattern',
274
+ image: this.pattern.image,
275
+ offsetX: Number(this.pattern.offsetX) || 0,
276
+ offsetY: Number(this.pattern.offsetY) || 0,
277
+ width: Number(this.pattern.width),
278
+ height: Number(this.pattern.height),
279
+ align: this.pattern.align,
280
+ fitPattern: this.pattern.fitPattern
281
+ }
282
+ break
283
+
284
+ case 'solid':
285
+ if (!this.solid) this.solid = '#fff'
286
+ this.value = this.solid
287
+ break
288
+
289
+ case 'no':
290
+ this.value = ''
291
+ break
292
+ }
293
+
294
+ this._block_reset = true
295
+ this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true }))
296
+ }
297
+
298
+ _onChangedSolid(e: Event) {
299
+ if (this.fillType !== 'solid') return
300
+
301
+ this.solid = (e.target as HTMLInputElement).value
302
+
303
+ this.value = this.solid
304
+
305
+ this._block_reset = true
306
+ this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true }))
307
+ }
308
+
309
+ _onChandedGradient(e: Event) {
310
+ /*
311
+ * TODO Gradient의 rotation은 symmetry 기능 등으로 외부에서 변경될 수도 있다.
312
+ * 이 점을 감안해서, 외부 변경에 대한 대응을 해야 한다.
313
+ */
314
+
315
+ if (this.fillType !== 'gradient') {
316
+ return
317
+ }
318
+
319
+ this.gradient = (e.target as OxInputColorGradient).value
320
+
321
+ this.value = {
322
+ type: 'gradient',
323
+ gradientType: this.gradient.type || 'linear',
324
+ colorStops: this.gradient.colorStops || [
325
+ {
326
+ position: 0,
327
+ color: this.solid || '#000000'
328
+ },
329
+ {
330
+ position: 1,
331
+ color: this.solid || '#FFFFFF'
332
+ }
333
+ ],
334
+ rotation: Number(this.gradient.rotation) || 0,
335
+ center: this.gradient.center
336
+ }
337
+
338
+ this._block_reset = true
339
+ this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true }))
340
+ }
341
+
342
+ _onChangedPattern(e: Event) {
343
+ if (this.fillType !== 'pattern') return
344
+
345
+ this.pattern = (e.target as OxInputBackgroundPattern).value
346
+
347
+ this.value = {
348
+ type: 'pattern',
349
+ image: this.pattern?.image,
350
+ offsetX: Number(this.pattern?.offsetX) || 0,
351
+ offsetY: Number(this.pattern?.offsetY) || 0,
352
+ width: Number(this.pattern?.width),
353
+ height: Number(this.pattern?.height),
354
+ align: this.pattern?.align,
355
+ fitPattern: this.pattern?.fitPattern
356
+ }
357
+
358
+ this._block_reset = true
359
+ this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true }))
360
+ }
361
+ }
@@ -2,7 +2,7 @@
2
2
  * @license Copyright © HatioLab Inc. All rights reserved.
3
3
  */
4
4
 
5
- import './things-editor-color'
5
+ import './ox-input-color'
6
6
 
7
7
  import { LitElement, css, html } from 'lit'
8
8
  import { customElement, property } from 'lit/decorators.js'
@@ -19,7 +19,7 @@ Example:
19
19
  </ox-input-value-map>
20
20
  */
21
21
  @customElement('ox-input-keyvalues')
22
- export default class OxInputValueMap extends LitElement {
22
+ export class OxInputValueMap extends LitElement {
23
23
  static styles = [
24
24
  css`
25
25
  :host {
@@ -56,15 +56,15 @@ export default class OxInputValueMap extends LitElement {
56
56
  }
57
57
 
58
58
  input,
59
- things-editor-color {
59
+ ox-input-color {
60
60
  flex: 1;
61
61
  }
62
62
 
63
- things-editor-color {
64
- --things-editor-color-input-color: {
63
+ ox-input-color {
64
+ --ox-input-color-input-color: {
65
65
  margin: 1px;
66
66
  }
67
- --things-editor-color-input-span: {
67
+ --ox-input-color-input-span: {
68
68
  width: 10px;
69
69
  height: 10px;
70
70
  }
@@ -92,7 +92,7 @@ export default class OxInputValueMap extends LitElement {
92
92
  <input type="text" data-key placeholder="key" .value=${item.key} /> ${this.valuetype == 'boolean'
93
93
  ? html` <input type="checkbox" data-value .checked=${item.value} data-value-type=${this.valuetype} /> `
94
94
  : this.valuetype == 'color'
95
- ? html` <things-editor-color data-value .value=${item.value} tabindex="-1"> </things-editor-color> `
95
+ ? html` <ox-input-color data-value .value=${item.value} tabindex="-1"> </ox-input-color> `
96
96
  : html`
97
97
  <input
98
98
  type="text"
@@ -110,7 +110,7 @@ export default class OxInputValueMap extends LitElement {
110
110
  <input type="text" data-key placeholder="key" value="" /> ${this.valuetype == 'boolean'
111
111
  ? html` <input type="checkbox" data-value data-value-type=${this.valuetype} /> `
112
112
  : this.valuetype == 'color'
113
- ? html` <things-editor-color data-value value="" tabindex="-1" placeholder="value"> </things-editor-color> `
113
+ ? html` <ox-input-color data-value value="" tabindex="-1" placeholder="value"> </ox-input-color> `
114
114
  : html` <input type="text" data-value placeholder="value" value="" data-value-type=${this.valuetype} /> `}
115
115
  <button class="record-action" @click=${(e: MouseEvent) => this._add(e)} tabindex="-1">+</button>
116
116
  </div>
@@ -127,13 +127,13 @@ export default class OxInputValueMap extends LitElement {
127
127
  `
128
128
  : this.valuetype == 'color'
129
129
  ? html`
130
- <things-editor-color
130
+ <ox-input-color
131
131
  data-value
132
132
  .value=${(this.value && this.value.default) || ''}
133
133
  placeholder="value"
134
134
  tabindex="-1"
135
135
  >
136
- </things-editor-color>
136
+ </ox-input-color>
137
137
  `
138
138
  : html`
139
139
  <input
@@ -0,0 +1,113 @@
1
+ /**
2
+ * @license Copyright © HatioLab Inc. All rights reserved.
3
+ */
4
+
5
+ import './ox-input-color.js'
6
+
7
+ import { css, html } from 'lit'
8
+ import { customElement, property, query, queryAll } from 'lit/decorators.js'
9
+
10
+ import { OxFormField } from './ox-form-field.js'
11
+ import { OxInputColor } from './ox-input-color.js'
12
+
13
+ /**
14
+ 색상 배열을 편집하는 컴포넌트이다.
15
+
16
+ 새로운 색상을 추가하고자 할 때는 `+` 버튼을 클릭한다.<br />
17
+ 색상을 제거하고자 할 때는 `-` 버튼을 클릭한다.<br />
18
+
19
+ Example:
20
+
21
+ <ox-input-multiple-color values=${values}>
22
+ </ox-input-multiple-color>
23
+ */
24
+
25
+ @customElement('ox-input-multiple-colors')
26
+ export class OxInputMultipleColors extends OxFormField {
27
+ static styles = css`
28
+ :host {
29
+ display: inline-block;
30
+ }
31
+
32
+ #colors-container > div {
33
+ display: grid;
34
+ grid-template-columns: 22px 1fr 22px;
35
+ grid-gap: 5px;
36
+ align-items: center;
37
+ justify-content: left;
38
+ }
39
+
40
+ ox-input-color {
41
+ height: 25px;
42
+ width: 100%;
43
+ }
44
+
45
+ input[type='button'] {
46
+ width: 22px;
47
+ height: 25px;
48
+ border: 1px solid rgba(0, 0, 0, 0.15);
49
+ padding-top: 0px;
50
+ padding-bottom: 2px;
51
+ background-color: #f1f2f4;
52
+ color: #8f9192;
53
+ font-size: 16px;
54
+ }
55
+ `
56
+
57
+ @property({ type: Array }) value: string[] = []
58
+
59
+ @query('#colors-container') colorsContainer!: HTMLDivElement
60
+ @queryAll('ox-input-color') colors!: NodeListOf<OxInputColor>
61
+
62
+ firstUpdated() {
63
+ this.renderRoot.addEventListener('change', this._onValueChanged.bind(this))
64
+ }
65
+
66
+ render() {
67
+ return html`
68
+ <div id="colors-container">
69
+ ${(this.value || []).map(
70
+ (item, index) => html`
71
+ <div>
72
+ <input type="button" value="+" @click=${() => this._appendEditorColor()} data-index=${index} />
73
+
74
+ <ox-input-color .value=${item}> </ox-input-color>
75
+
76
+ ${(this.value || []).length > 1
77
+ ? html`
78
+ <input
79
+ type="button"
80
+ value="-"
81
+ @click=${(e: Event) => this._removeEditorColor(e)}
82
+ data-index=${index}
83
+ />
84
+ `
85
+ : html``}
86
+ </div>
87
+ `
88
+ )}
89
+ </div>
90
+ `
91
+ }
92
+
93
+ _onValueChanged() {
94
+ this.value = Array.from(this.colors).map(color => color.value!)
95
+ }
96
+
97
+ _appendEditorColor() {
98
+ this.value = [...this.value, 'black']
99
+
100
+ this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true }))
101
+ }
102
+
103
+ _removeEditorColor(e: Event) {
104
+ var values = []
105
+ for (var i = 0; i < this.value.length; i++) {
106
+ if (i == Number((e.target as HTMLElement).dataset.index)) continue
107
+ else values.push(this.value[i])
108
+ }
109
+
110
+ this.value = values
111
+ this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true }))
112
+ }
113
+ }
@@ -0,0 +1,165 @@
1
+ /**
2
+ * @license Copyright © HatioLab Inc. All rights reserved.
3
+ */
4
+
5
+ import { LitElement, css, html } from 'lit'
6
+ import { customElement, property } from 'lit/decorators.js'
7
+
8
+ import { OxFormField } from './ox-form-field'
9
+
10
+ type Option = { text: string; value: string }
11
+
12
+ @customElement('ox-input-options')
13
+ export class OxInputOptions extends OxFormField {
14
+ static styles = css`
15
+ div {
16
+ display: grid;
17
+ grid-template-columns: repeat(10, 1fr);
18
+ grid-gap: 5px;
19
+ grid-auto-rows: minmax(24px, auto);
20
+ }
21
+
22
+ input[data-text] {
23
+ grid-column: span 5;
24
+ }
25
+
26
+ input[data-value] {
27
+ grid-column: span 4;
28
+ }
29
+
30
+ button {
31
+ grid-column: span 1;
32
+ }
33
+ `
34
+
35
+ @property({ type: Array }) value: Option[] = []
36
+
37
+ firstUpdated() {
38
+ this.renderRoot.addEventListener('change', this._onChange.bind(this))
39
+ }
40
+
41
+ render() {
42
+ const options = this.value instanceof Array ? this.value : []
43
+
44
+ return html`
45
+ ${(options || []).map(
46
+ item => html`
47
+ <div data-record="">
48
+ <input type="text" data-text="" placeholder="text" .value=${item.text} />
49
+ <input type="text" data-value="" placeholder="value" .value=${item.value} />
50
+ <button class="record-action" @click=${(e: MouseEvent) => this._delete(e)} tabindex="-1">-</button>
51
+ </div>
52
+ `
53
+ )}
54
+
55
+ <div data-record-new="">
56
+ <input type="text" data-text="" placeholder="text" value="" />
57
+ <input type="text" data-value="" placeholder="value" value="" @change=${(e: Event) => this._add()} />
58
+ <button class="record-action" @click=${(e: MouseEvent) => this._add()} tabindex="-1">+</button>
59
+ </div>
60
+ `
61
+ }
62
+
63
+ private _changingNow: boolean = false
64
+
65
+ _onChange(e: Event) {
66
+ if (this._changingNow) return
67
+
68
+ this._changingNow = true
69
+
70
+ var input = e.target as HTMLInputElement
71
+ var value = input.value
72
+
73
+ var div = input.parentElement
74
+
75
+ if (div && div.hasAttribute('data-record')) {
76
+ var dataList = div.querySelectorAll('[data-value]:not([hidden])') as NodeListOf<HTMLInputElement>
77
+
78
+ for (var i = 0; i < dataList.length; i++) {
79
+ if (dataList[i] !== input) {
80
+ dataList[i].value = value || ''
81
+ }
82
+ }
83
+ }
84
+
85
+ if (div && div.hasAttribute('data-record')) this._build(true)
86
+ else if (div && div.hasAttribute('data-record-new') && input.hasAttribute('data-value')) this._add()
87
+
88
+ e.stopPropagation()
89
+
90
+ this._changingNow = false
91
+ }
92
+
93
+ _build(includeNewRecord?: any) {
94
+ if (includeNewRecord) {
95
+ var records = this.renderRoot.querySelectorAll('[data-record],[data-record-new]') as NodeListOf<HTMLElement>
96
+ } else {
97
+ var records = this.renderRoot.querySelectorAll('[data-record]') as NodeListOf<HTMLElement>
98
+ }
99
+
100
+ var newoptions: Option[] = []
101
+
102
+ for (var i = 0; i < records.length; i++) {
103
+ var record = records[i]
104
+
105
+ var text = (record.querySelector('[data-text]') as HTMLInputElement).value
106
+ var inputs = record.querySelectorAll('[data-value]:not([style*="display: none"])') as NodeListOf<HTMLInputElement>
107
+ if (!inputs || inputs.length == 0) continue
108
+
109
+ var input = inputs[inputs.length - 1]
110
+ var value = input.value
111
+
112
+ if (text) newoptions.push({ text: text, value: value || text })
113
+ }
114
+
115
+ this.value = newoptions
116
+ this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true, detail: this.value }))
117
+ }
118
+
119
+ sort() {
120
+ var sorter = function (a: Option, b: Option) {
121
+ return b.text < a.text ? 1 : -1
122
+ }
123
+
124
+ this.value = [...this.value.sort(sorter)]
125
+
126
+ this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true, detail: this.value }))
127
+ }
128
+
129
+ _add() {
130
+ this._build(true)
131
+
132
+ var inputs = this.renderRoot.querySelectorAll(
133
+ '[data-record-new] input:not([style*="display: none"])'
134
+ ) as NodeListOf<HTMLInputElement>
135
+
136
+ for (var i = 0; i < inputs.length; i++) {
137
+ let input = inputs[i]
138
+ input.value = ''
139
+ }
140
+
141
+ inputs[0].focus()
142
+ }
143
+
144
+ _delete(e: Event) {
145
+ const record = (e.target as HTMLElement).parentElement
146
+ const input = record && (record.querySelector('[data-text]') as HTMLInputElement)
147
+
148
+ if (input) {
149
+ input.value = ''
150
+ }
151
+
152
+ this._build()
153
+ }
154
+
155
+ protected appendFormData({ formData }: FormDataEvent): void {
156
+ if (!this.name) return
157
+
158
+ const value = this.value
159
+
160
+ formData.append(
161
+ this.name!,
162
+ typeof value === 'string' ? value : value === undefined || value === null ? '' : JSON.stringify(value)
163
+ )
164
+ }
165
+ }