goodteditor-ui 1.0.70 → 1.0.71

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "goodteditor-ui",
3
- "version": "1.0.70",
3
+ "version": "1.0.71",
4
4
  "main": "index.js",
5
5
  "homepage": "https://goodt-ui.netlify.app/",
6
6
  "scripts": {
@@ -21,9 +21,8 @@
21
21
  }
22
22
  </style>
23
23
  <script>
24
- import mixin from './mixin';
24
+ import { createAlphaSquare, createLinearGradient } from './utils';
25
25
  export default {
26
- mixins: [mixin],
27
26
  props: {
28
27
  color: {
29
28
  type: String,
@@ -66,7 +65,7 @@ export default {
66
65
  const width = this.width;
67
66
  const height = this.height;
68
67
  const size = this.alphaSize;
69
- const canvasSquare = this.createAlphaSquare(size);
68
+ const canvasSquare = createAlphaSquare(size);
70
69
 
71
70
  const ctx = canvas.getContext('2d');
72
71
  canvas.width = width;
@@ -75,7 +74,7 @@ export default {
75
74
  ctx.fillStyle = ctx.createPattern(canvasSquare, 'repeat');
76
75
  ctx.fillRect(0, 0, width, height);
77
76
 
78
- this.createLinearGradient('p', ctx, width, height, 'rgba(255,255,255,0)', this.color);
77
+ createLinearGradient('p', ctx, width, height, 'rgba(255,255,255,0)', this.color);
79
78
  },
80
79
  renderSlide() {
81
80
  this.slideAlphaStyle = {
@@ -62,9 +62,8 @@
62
62
  }
63
63
  </style>
64
64
  <script>
65
- import mixin from './mixin';
65
+ import { createAlphaSquare } from './utils';
66
66
  export default {
67
- mixins: [mixin],
68
67
  props: {
69
68
  color: {
70
69
  type: String,
@@ -87,7 +86,7 @@ export default {
87
86
  };
88
87
  },
89
88
  created() {
90
- this.imgAlphaBase64 = this.createAlphaSquare(4).toDataURL();
89
+ this.imgAlphaBase64 = createAlphaSquare(4).toDataURL();
91
90
  },
92
91
  destroyed() {
93
92
  this.setColorsHistory(this.color);
@@ -2,9 +2,8 @@
2
2
  <canvas />
3
3
  </template>
4
4
  <script>
5
- import mixin from './mixin';
5
+ import { createAlphaSquare } from './utils';
6
6
  export default {
7
- mixins: [mixin],
8
7
  props: {
9
8
  color: {
10
9
  type: String,
@@ -38,7 +37,7 @@ export default {
38
37
  const width = this.width;
39
38
  const height = this.height;
40
39
  const size = this.alphaSize;
41
- const canvasSquare = this.createAlphaSquare(size);
40
+ const canvasSquare = createAlphaSquare(size);
42
41
 
43
42
  const ctx = canvas.getContext('2d');
44
43
  canvas.width = width;
@@ -22,9 +22,8 @@
22
22
  }
23
23
  </style>
24
24
  <script>
25
- import mixin from './mixin';
25
+ import { createLinearGradient } from './utils';
26
26
  export default {
27
- mixins: [mixin],
28
27
  props: {
29
28
  color: {
30
29
  type: String,
@@ -62,8 +61,8 @@ export default {
62
61
  ctx.fillStyle = this.color;
63
62
  ctx.fillRect(0, 0, size, size);
64
63
 
65
- this.createLinearGradient('l', ctx, size, size, '#FFFFFF', 'rgba(255,255,255,0)');
66
- this.createLinearGradient('p', ctx, size, size, 'rgba(0,0,0,0)', '#000000');
64
+ createLinearGradient('l', ctx, size, size, '#FFFFFF', 'rgba(255,255,255,0)');
65
+ createLinearGradient('p', ctx, size, size, 'rgba(0,0,0,0)', '#000000');
67
66
  },
68
67
  renderSlide() {
69
68
  this.slideSaturationStyle = {
@@ -0,0 +1,170 @@
1
+ export const MODEL_MODE = {
2
+ HEX: 'hex',
3
+ RGBA: 'rgba'
4
+ };
5
+
6
+ export const isValidColorString = (value) => {
7
+ return CSS.supports('color', value);
8
+ }
9
+
10
+ const hex2Dec = (val) => {
11
+ const value = parseInt(val, 16);
12
+ if (Number.isNaN(value)) {
13
+ throw new Error(`${val} is not a valid HEX number`);
14
+ }
15
+ return value;
16
+ };
17
+
18
+ const checkRgbaColorChannelRange = (c) => {
19
+ if (c < 0 || c > 255) {
20
+ throw new Error(`${c} is not a valid RGBa color channel value range`);
21
+ }
22
+ };
23
+
24
+ const checkRgbaAlphaChannelRange = (c) => {
25
+ if (c < 0 || c > 1) {
26
+ throw new Error(`${c} is not a valid RGBa alpha channel value range`);
27
+ }
28
+ };
29
+
30
+ const checkRgbaChannelsRange = (rgba) => {
31
+ ['r', 'g', 'b'].forEach((c) => checkRgbaColorChannelRange(rgba[c]));
32
+ checkRgbaAlphaChannelRange(rgba['a']);
33
+ };
34
+
35
+ export const rgb2hex = ({ r, g, b, a }, toUpper) => {
36
+ const change = val => ('0' + Number(val).toString(16)).slice(-2);
37
+ const color = `#${change(r)}${change(g)}${change(b)}${a < 1 ? change(Math.round(a * 255)) : ''}`;
38
+ return toUpper ? color.toUpperCase() : color;
39
+ };
40
+
41
+ export const hex2rgb = (hex) => {
42
+ hex = hex.slice(1);
43
+ if (hex.length === 3) {
44
+ hex = hex.split('').map((v) => v.padEnd(2, v)).join('');
45
+ } else if (hex.length < 6) {
46
+ hex = hex.padEnd(6, '0');
47
+ }
48
+ hex = hex.padEnd(8, 'ff');
49
+ const [r, g, b, a] = [0, 2, 4, 6].map((i) => hex2Dec(hex.slice(i, i + 2)));
50
+ return {
51
+ r,
52
+ g,
53
+ b,
54
+ a: (a / 255).toFixed(2)
55
+ };
56
+ };
57
+
58
+ export const rgb2rgba = (rgba) => {
59
+ if (typeof rgba === 'string') {
60
+ const error = new Error(`${rgba} is not a valid RGBa`);
61
+ rgba = /rgba?\((.*?)\)/.exec(rgba);
62
+ if (rgba === null) {
63
+ throw error;
64
+ }
65
+ rgba = rgba[1].split(',').map(Number);
66
+ if (rgba.length < 3 || rgba.some((v) => Number.isNaN(v))) {
67
+ throw error;
68
+ }
69
+ return {
70
+ r: rgba[0],
71
+ g: rgba[1],
72
+ b: rgba[2],
73
+ a: rgba[3] ?? 1
74
+ };
75
+ } else {
76
+ return rgba;
77
+ }
78
+ };
79
+
80
+ export const rgb2hsv = ({ r, g, b }) => {
81
+ r = r / 255;
82
+ g = g / 255;
83
+ b = b / 255;
84
+ const max = Math.max(r, g, b);
85
+ const min = Math.min(r, g, b);
86
+ const delta = max - min;
87
+ let h = 0;
88
+ if (max === min) {
89
+ h = 0;
90
+ } else if (max === r) {
91
+ if (g >= b) {
92
+ h = (60 * (g - b)) / delta;
93
+ } else {
94
+ h = (60 * (g - b)) / delta + 360;
95
+ }
96
+ } else if (max === g) {
97
+ h = (60 * (b - r)) / delta + 120;
98
+ } else if (max === b) {
99
+ h = (60 * (r - g)) / delta + 240;
100
+ }
101
+ h = Math.floor(h);
102
+ let s = parseFloat((max === 0 ? 0 : 1 - min / max).toFixed(2));
103
+ let v = parseFloat(max.toFixed(2));
104
+ return { h, s, v };
105
+ };
106
+
107
+ const normalizeRgbaChannels = (rgba) => {
108
+ rgba = { ...rgba };
109
+ ['r', 'g', 'b'].forEach((c) => { rgba[c] = Math.min(Math.max(rgba[c], 0), 255) });
110
+ rgba['a'] = Math.min(Math.max(rgba['a'], 0), 1);
111
+ return rgba;
112
+ };
113
+
114
+ export const setColorValue = (color) => {
115
+ let rgba;
116
+ let modelMode = MODEL_MODE.RGBA;
117
+ if (/#/.test(color)) {
118
+ rgba = hex2rgb(color);
119
+ modelMode = MODEL_MODE.HEX;
120
+ } else if (/rgb/.test(color)) {
121
+ rgba = rgb2rgba(color);
122
+ } else if (typeof color === 'string') {
123
+ rgba = rgb2rgba(`rgba(${color})`);
124
+ } else if (color instanceof Object) {
125
+ rgba = color;
126
+ } else {
127
+ throw new Error('Invalid color value');
128
+ }
129
+
130
+ rgba = normalizeRgbaChannels(rgba);
131
+ const { r, g, b, a } = rgba;
132
+ const { h, s, v } = rgb2hsv(rgba);
133
+ return {
134
+ r,
135
+ g,
136
+ b,
137
+ a: a === undefined ? 1 : a,
138
+ h,
139
+ s,
140
+ v,
141
+ modelMode
142
+ };
143
+ };
144
+
145
+ export const createAlphaSquare = (size) => {
146
+ const canvas = document.createElement('canvas');
147
+ const ctx = canvas.getContext('2d');
148
+ const doubleSize = size * 2;
149
+ canvas.width = doubleSize;
150
+ canvas.height = doubleSize;
151
+
152
+ ctx.fillStyle = '#ffffff';
153
+ ctx.fillRect(0, 0, doubleSize, doubleSize);
154
+ ctx.fillStyle = '#ccd5db';
155
+ ctx.fillRect(0, 0, size, size);
156
+ ctx.fillRect(size, size, size, size);
157
+
158
+ return canvas;
159
+ };
160
+
161
+ export const createLinearGradient = (direction, ctx, width, height, color1, color2) => {
162
+ const isL = direction === 'l';
163
+ const gradient = ctx.createLinearGradient(0, 0, isL ? width : 0, isL ? 0 : height);
164
+ gradient.addColorStop(0.01, color1);
165
+ gradient.addColorStop(0.99, color2);
166
+ ctx.fillStyle = gradient;
167
+ ctx.fillRect(0, 0, width, height);
168
+ };
169
+
170
+ export default {};
@@ -40,14 +40,14 @@
40
40
  class="input input-small text-xsmall flex-grow"
41
41
  type="text"
42
42
  :value="modelRgba"
43
- @change="e => inputRgba(e.target.value)"
44
- v-if="modelMode == modelModes.RGBA"
43
+ @input="({ target }) => inputRgba(target.value)"
44
+ v-if="modelMode === modelModes.RGBA"
45
45
  />
46
46
  <input
47
47
  class="input input-small text-xsmall flex-grow"
48
48
  type="text"
49
49
  :value="modelHex"
50
- @change="e => inputHex(e.target.value)"
50
+ @input="({ target }) => inputHex(target.value)"
51
51
  v-else
52
52
  />
53
53
  <div class="btn btn-outline btn-small mar-left-2" @click="toggleModelMode">
@@ -78,69 +78,78 @@
78
78
  canvas {
79
79
  display: block;
80
80
  }
81
+
81
82
  input {
82
83
  min-width: 0;
83
84
  }
85
+
84
86
  .btn {
85
87
  min-height: 0;
86
88
  width: 2.25em;
87
89
  padding: 0;
88
90
  }
91
+
89
92
  .text-upper {
90
93
  text-transform: uppercase;
91
94
  }
92
95
  }
93
96
  </style>
94
97
  <script>
95
- import mixin from './ColorPicker/mixin';
98
+ import { MODEL_MODE, setColorValue, rgb2hex, isValidColorString } from './ColorPicker/utils';
99
+ import { Key } from './utils/Helpers';
96
100
  import CpSaturation from './ColorPicker/Saturation.vue';
97
101
  import CpHue from './ColorPicker/Hue.vue';
98
102
  import CpAlpha from './ColorPicker/Alpha.vue';
99
103
  import CpPreview from './ColorPicker/Preview.vue';
100
104
  import CpColors from './ColorPicker/Colors.vue';
101
105
 
102
- let MODEL_MODE = {
103
- HEX: 'h',
104
- RGBA: 'r',
106
+ export const DefaultChannelValues = {
107
+ r: 0,
108
+ g: 0,
109
+ b: 0,
110
+ a: 1,
111
+ h: 0,
112
+ s: 0,
113
+ v: 0
105
114
  };
115
+
106
116
  export default {
107
117
  components: {
108
118
  CpSaturation,
109
119
  CpHue,
110
120
  CpAlpha,
111
121
  CpPreview,
112
- CpColors,
122
+ CpColors
113
123
  },
114
- mixins: [mixin],
115
124
  props: {
116
125
  /**
117
126
  * @model
118
127
  */
119
128
  value: {
120
129
  type: String,
121
- default: '#000000',
130
+ default: '#000000'
122
131
  },
123
132
  /**
124
133
  * Default colors palette
125
134
  */
126
135
  colorsDefault: {
127
136
  type: Array,
128
- default: () => [],
137
+ default: () => []
129
138
  },
130
139
  /**
131
140
  * LocalStorage key for saving used colors
132
141
  */
133
142
  colorsHistoryKey: {
134
143
  type: String,
135
- default: '',
144
+ default: ''
136
145
  },
137
146
  /**
138
147
  * Show submit button
139
148
  */
140
149
  showSubmit: {
141
150
  type: Boolean,
142
- default: false,
143
- },
151
+ default: false
152
+ }
144
153
  },
145
154
  data() {
146
155
  return {
@@ -151,19 +160,13 @@ export default {
151
160
  modelHex: '',
152
161
  modelMode: MODEL_MODE.RGBA,
153
162
  modelModes: MODEL_MODE,
154
- r: 0,
155
- g: 0,
156
- b: 0,
157
- a: 1,
158
- h: 0,
159
- s: 0,
160
- v: 0,
163
+ ...DefaultChannelValues
161
164
  };
162
165
  },
163
166
  computed: {
164
167
  nextModelMode() {
165
168
  let { HEX, RGBA } = MODEL_MODE;
166
- return this.modelMode == HEX ? RGBA : HEX;
169
+ return (this.modelMode === HEX ? RGBA : HEX)[0];
167
170
  },
168
171
  totalWidth() {
169
172
  return this.hueHeight + (this.hueWidth + 4) * 2;
@@ -176,14 +179,14 @@ export default {
176
179
  r: this.r,
177
180
  g: this.g,
178
181
  b: this.b,
179
- a: this.a,
182
+ a: this.a
180
183
  };
181
184
  },
182
185
  hsv() {
183
186
  return {
184
187
  h: this.h,
185
188
  s: this.s,
186
- v: this.v,
189
+ v: this.v
187
190
  };
188
191
  },
189
192
  rgbString() {
@@ -196,19 +199,35 @@ export default {
196
199
  return `rgba(${this.rgbaStringShort})`;
197
200
  },
198
201
  hexString() {
199
- return this.rgb2hex(this.rgba, true);
202
+ return rgb2hex(this.rgba, true);
200
203
  },
201
204
  previewRgbaString() {
202
205
  return this.rgbaString;
203
- },
206
+ }
204
207
  },
205
208
  watch: {
206
209
  value: {
207
- handler(v) {
208
- Object.assign(this, this.setColorValue(this.value));
210
+ handler(input, prevInput) {
211
+ // fallback color value
212
+ let channels;
213
+ try {
214
+ if (isValidColorString(input) === false) {
215
+ throw new Error('Invalid color value');
216
+ };
217
+ const { modelMode, ...channelsOnly } = setColorValue(input);
218
+ if (prevInput == null) {
219
+ this.modelMode = modelMode;
220
+ }
221
+ channels = channelsOnly;
222
+ } catch(e) {
223
+ console.error(e);
224
+ channels = { ...DefaultChannelValues }
225
+ }
226
+
227
+ Object.assign(this, channels);
209
228
  this.setText();
210
229
  },
211
- immediate: true,
230
+ immediate: true
212
231
  },
213
232
  rgba() {
214
233
  /**
@@ -218,19 +237,21 @@ export default {
218
237
  this.$emit('changeColor', {
219
238
  rgba: this.rgba,
220
239
  hsv: this.hsv,
221
- hex: this.modelHex,
240
+ hex: this.modelHex
222
241
  });
223
242
  /**
224
243
  * Triggers when the color changes
225
244
  * @property {Number} rgbaString color rgba string
226
245
  */
227
246
  this.$emit('input', this.rgbaString);
228
- },
247
+ }
248
+ },
249
+ created() {
250
+ document.addEventListener('keydown', this.onDocKeydown);
251
+ },
252
+ beforeDestroy() {
253
+ document.removeEventListener('keydown', this.onDocKeydown);
229
254
  },
230
- /*created() {
231
- Object.assign(this, this.setColorValue(this.value));
232
- this.setText();
233
- },*/
234
255
  methods: {
235
256
  submit() {
236
257
  /**
@@ -241,27 +262,28 @@ export default {
241
262
  rgba: this.rgba,
242
263
  hsv: this.hsv,
243
264
  hex: this.modelHex,
265
+ mode: this.modelMode
244
266
  });
245
267
  },
246
268
  toggleModelMode() {
247
269
  let { HEX, RGBA } = MODEL_MODE;
248
- this.modelMode = this.modelMode == HEX ? RGBA : HEX;
270
+ this.modelMode = this.modelMode === HEX ? RGBA : HEX;
249
271
  },
250
272
  selectSaturation(color) {
251
- const { r, g, b, h, s, v } = this.setColorValue(color);
273
+ const { r, g, b, h, s, v } = setColorValue(color);
252
274
  Object.assign(this, { r, g, b, h, s, v });
253
275
  this.setText();
254
276
  },
255
277
  changeSaturation(color) {
256
- //const { r, g, b } = this.setColorValue(color);
278
+ //const { r, g, b } = setColorValue(color);
257
279
  //const { a } = this;
258
280
  //this.previewRgbaString = `rgba(${r},${g},${b},${a})`;
259
- const { r, g, b, h, s, v } = this.setColorValue(color);
281
+ const { r, g, b, h, s, v } = setColorValue(color);
260
282
  Object.assign(this, { r, g, b, h, s, v });
261
283
  this.setText();
262
284
  },
263
285
  selectHue(color) {
264
- const { r, g, b, h, s, v } = this.setColorValue(color);
286
+ const { r, g, b, h, s, v } = setColorValue(color);
265
287
  Object.assign(this, { r, g, b, h, s, v });
266
288
  this.setText();
267
289
  this.$nextTick(() => {
@@ -274,7 +296,7 @@ export default {
274
296
  this.setText();
275
297
  },
276
298
  inputHex(color) {
277
- const { r, g, b, a, h, s, v } = this.setColorValue(color);
299
+ const { r, g, b, a, h, s, v } = setColorValue(color);
278
300
  Object.assign(this, { r, g, b, a, h, s, v });
279
301
  this.modelHex = color;
280
302
  this.modelRgba = this.rgbaStringShort;
@@ -285,7 +307,7 @@ export default {
285
307
  });
286
308
  },
287
309
  inputRgba(color) {
288
- const { r, g, b, a, h, s, v } = this.setColorValue(color);
310
+ const { r, g, b, a, h, s, v } = setColorValue(color);
289
311
  Object.assign(this, { r, g, b, a, h, s, v });
290
312
  this.modelHex = this.hexString;
291
313
  this.modelRgba = color;
@@ -300,7 +322,7 @@ export default {
300
322
  this.modelRgba = this.rgbaStringShort;
301
323
  },
302
324
  selectColor(color) {
303
- const { r, g, b, a, h, s, v } = this.setColorValue(color);
325
+ const { r, g, b, a, h, s, v } = setColorValue(color);
304
326
  Object.assign(this, { r, g, b, a, h, s, v });
305
327
  this.setText();
306
328
  this.$nextTick(() => {
@@ -309,6 +331,18 @@ export default {
309
331
  this.$refs.hue.renderSlide();
310
332
  });
311
333
  },
312
- },
334
+ onDocKeydown(e) {
335
+ if (e.key === Key.ESC) {
336
+ e.stopImmediatePropagation();
337
+ this.$emit('close');
338
+ return;
339
+ }
340
+ if (e.key === Key.ENTER) {
341
+ e.preventDefault();
342
+ e.stopImmediatePropagation();
343
+ this.submit();
344
+ }
345
+ }
346
+ }
313
347
  };
314
348
  </script>
@@ -8,7 +8,7 @@
8
8
  >
9
9
  <div class="ui-input-color-picker-wrapper d-flex flex-v-center w-100">
10
10
  <div style="flex: 1 0 0">
11
- <!--
11
+ <!--
12
12
  @slot Custom input slot
13
13
  @binding {String} id input id attribute value
14
14
  @binding {String} value input value
@@ -26,7 +26,7 @@
26
26
  />
27
27
  </slot>
28
28
  </div>
29
- <!--
29
+ <!--
30
30
  @slot Icon slot
31
31
  @binding {String} value current value
32
32
  @binding {Function} toggle function that toggles the color picker
@@ -34,12 +34,12 @@
34
34
  <slot name="icon" v-bind="{ value, toggle: togglePopover }">
35
35
  <div
36
36
  class="ui-input-color-picker-icon-preview mar-left-2 cursor-pointer"
37
- :style="{ background: value }"
37
+ :style="{ ...(isValid && { background: value }) }"
38
38
  @click="togglePopover"
39
39
  ></div>
40
40
  </slot>
41
41
  </div>
42
- <ui-popover :show.sync="popoverShow" v-bind="popoverOptions">
42
+ <ui-popover v-if="popoverShow" :show.sync="popoverShow" v-bind="popoverOptions">
43
43
  <color-picker
44
44
  class="ui-input-color-picker-cp pull-left"
45
45
  v-bind="{
@@ -47,7 +47,9 @@
47
47
  colorsDefault,
48
48
  showSubmit: true,
49
49
  }"
50
- @submit="onSubmit"
50
+ @close="onColorPickerClose"
51
+ @submit="onColorPickerSubmit"
52
+ @valid="emitValid"
51
53
  ></color-picker>
52
54
  </ui-popover>
53
55
  </div>
@@ -55,15 +57,20 @@
55
57
  <style lang="less" scoped>
56
58
  .ui-input-color-picker {
57
59
  display: inline-flex;
60
+ /* unset default FormComponent mixin */
61
+ pointer-events: unset !important;
62
+
58
63
  &-wrapper {
59
64
  display: flex;
60
65
  }
66
+
61
67
  &-icon-preview {
62
68
  width: 1em;
63
69
  height: 1em;
64
70
  border: 1px solid var(--color-border);
65
71
  background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKAQMAAAC3/F3+AAAAA3NCSVQICAjb4U/gAAAABlBMVEXM1dv///9zs4+jAAAACXBIWXMAAAsSAAALEgHS3X78AAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJljYD/AgIx+MCAjAHyDCLzvUxo8AAAAAElFTkSuQmCC');
66
72
  }
73
+
67
74
  &-input {
68
75
  border: none;
69
76
  margin: 0;
@@ -72,12 +79,18 @@
72
79
  color: inherit;
73
80
  background: transparent;
74
81
  outline: none;
82
+
83
+ &::placeholder {
84
+ color: inherit;
85
+ }
75
86
  }
76
87
  }
77
88
  </style>
78
89
  <script>
90
+ import { MODEL_MODE, isValidColorString } from './ColorPicker/utils';
79
91
  import ColorPicker from './ColorPicker.vue';
80
92
  import UiPopover from './Popover.vue';
93
+ import { Key } from './utils/Helpers';
81
94
  import FormComponent from './utils/FormComponent';
82
95
  import WithPopover from './utils/WithPopover';
83
96
 
@@ -90,7 +103,7 @@ export default {
90
103
  */
91
104
  editable: {
92
105
  type: Boolean,
93
- default: true,
106
+ default: true
94
107
  },
95
108
  /**
96
109
  * Default colors palette
@@ -99,53 +112,110 @@ export default {
99
112
  type: Array,
100
113
  default() {
101
114
  return [];
102
- },
103
- },
115
+ }
116
+ }
104
117
  },
105
118
  data() {
106
119
  return {
120
+ isValid: false,
107
121
  inputBinds: {
108
122
  placeholder: this.placeholder,
109
123
  readonly: this.readonly || !this.editable,
124
+ disabled: this.disabled
110
125
  },
111
126
  inputEvents: {
127
+ input: this.onInputInput,
112
128
  change: this.onInputChange,
113
129
  focus: this.onInputFocus,
114
130
  blur: this.onInputBlur,
115
- },
131
+ keydown: this.onInputKeydownLocal
132
+ }
116
133
  };
117
134
  },
135
+ created() {
136
+ this.validateInput(this.value);
137
+ },
118
138
  methods: {
119
- changeColor(val) {
139
+ inputColor(val) {
120
140
  /**
121
141
  * Input event
122
142
  * @property {String} color
123
143
  */
124
144
  this.$emit('input', val);
145
+ },
146
+ changeColor(val) {
125
147
  /**
126
148
  * Change event
127
149
  * @property {String} color
128
150
  */
129
151
  this.$emit('change', val);
130
152
  },
153
+ emitValid(isValid) {
154
+ this.$emit('valid', isValid);
155
+ },
156
+ validateInput(value) {
157
+ const isValid = value === '' ? true : isValidColorString(value);
158
+ this.isValid = isValid;
159
+ this.emitValid(isValid);
160
+ },
161
+ // EVENT HANDLERS
131
162
  onRootFocus(e) {
163
+ if (this.disabled) {
164
+ return;
165
+ }
132
166
  let input = this.getInputRef();
133
167
  input && input.focus();
134
168
  },
135
169
  onInputChange(e) {
136
- this.changeColor(e.target.value);
170
+ const { value } = e.target;
171
+ this.validateInput(value);
172
+ this.changeColor(value);
173
+ },
174
+ onInputInput(e) {
175
+ const { value } = e.target;
176
+ this.validateInput(value);
177
+ this.inputColor(e.target.value);
137
178
  },
138
179
  onInputFocus(e) {
180
+ if (this.disabled) {
181
+ return;
182
+ }
139
183
  this.rootHasFocus = true;
140
184
  },
141
185
  onInputBlur(e) {
142
186
  this.rootHasFocus = false;
143
187
  },
144
- onSubmit({ rgba }) {
145
- let { r, g, b, a } = rgba;
146
- this.changeColor(`rgba(${r}, ${g}, ${b}, ${a})`);
147
- this.popoverShow = false;
188
+ onColorPickerSubmit({ mode, ...profiles }) {
189
+ if (this.readonly || this.disabled) {
190
+ return;
191
+ }
192
+ const value = profiles[mode];
193
+ let colorString;
194
+ if (mode === MODEL_MODE.RGBA) {
195
+ let { r, g, b, a } = value;
196
+ colorString = `rgba(${r}, ${g}, ${b}, ${a})`;
197
+ }
198
+ if (mode === MODEL_MODE.HEX) {
199
+ colorString = value;
200
+ }
201
+ this.changeColor(colorString);
202
+ this.inputColor(colorString);
203
+ this.onColorPickerClose();
148
204
  },
149
- },
205
+ onInputKeydownLocal(e) {
206
+ if (e.key === Key.ESC) {
207
+ if (this.popoverShow) {
208
+ e.stopImmediatePropagation();
209
+ this.onColorPickerClose();
210
+ return;
211
+ }
212
+ this.onInputKeydown(e);
213
+ }
214
+ },
215
+ onColorPickerClose() {
216
+ this.popoverShow = false;
217
+ this.getInputRef().focus();
218
+ }
219
+ }
150
220
  };
151
221
  </script>
@@ -1,3 +1,5 @@
1
+ import { Key } from '../utils/Helpers';
2
+
1
3
  const r = () =>
2
4
  Math.random()
3
5
  .toString(36)
@@ -103,5 +105,14 @@ export default {
103
105
  }
104
106
  this.rootHasFocus = false;
105
107
  },
108
+ onInputKeydown(e) {
109
+ if (e.key === Key.ESC && this.rootHasFocus) {
110
+ e.stopPropagation();
111
+ const el = this.getInputRef();
112
+ if (el != null) {
113
+ el.blur()
114
+ }
115
+ }
116
+ }
106
117
  },
107
118
  };
@@ -1,105 +0,0 @@
1
- export default {
2
- methods: {
3
- setColorValue(color) {
4
- let rgba = { r: 0, g: 0, b: 0, a: 1 };
5
- if (/#/.test(color)) {
6
- rgba = this.hex2rgb(color);
7
- } else if (/rgb/.test(color)) {
8
- rgba = this.rgb2rgba(color);
9
- } else if (typeof color === 'string') {
10
- rgba = this.rgb2rgba(`rgba(${color})`);
11
- } else if (Object.prototype.toString.call(color) === '[object Object]') {
12
- rgba = color;
13
- }
14
- let fix = c => (c < 0 ? 0 : c > 255 ? 255 : c);
15
- ['r', 'g', 'b'].forEach(c => (rgba[c] = fix(rgba[c])));
16
- const { r, g, b, a } = rgba;
17
- const { h, s, v } = this.rgb2hsv(rgba);
18
- return {
19
- r,
20
- g,
21
- b,
22
- a: a === undefined ? 1 : a,
23
- h,
24
- s,
25
- v,
26
- };
27
- },
28
- createAlphaSquare(size) {
29
- const canvas = document.createElement('canvas');
30
- const ctx = canvas.getContext('2d');
31
- const doubleSize = size * 2;
32
- canvas.width = doubleSize;
33
- canvas.height = doubleSize;
34
-
35
- ctx.fillStyle = '#ffffff';
36
- ctx.fillRect(0, 0, doubleSize, doubleSize);
37
- ctx.fillStyle = '#ccd5db';
38
- ctx.fillRect(0, 0, size, size);
39
- ctx.fillRect(size, size, size, size);
40
-
41
- return canvas;
42
- },
43
- createLinearGradient(direction, ctx, width, height, color1, color2) {
44
- const isL = direction === 'l';
45
- const gradient = ctx.createLinearGradient(0, 0, isL ? width : 0, isL ? 0 : height);
46
- gradient.addColorStop(0.01, color1);
47
- gradient.addColorStop(0.99, color2);
48
- ctx.fillStyle = gradient;
49
- ctx.fillRect(0, 0, width, height);
50
- },
51
- rgb2hex({ r, g, b }, toUpper) {
52
- const change = val => ('0' + Number(val).toString(16)).slice(-2);
53
- const color = `#${change(r)}${change(g)}${change(b)}`;
54
- return toUpper ? color.toUpperCase() : color;
55
- },
56
- hex2rgb(hex) {
57
- hex = hex.slice(1);
58
- const change = val => parseInt(val, 16) || 0;
59
- return {
60
- r: change(hex.slice(0, 2)),
61
- g: change(hex.slice(2, 4)),
62
- b: change(hex.slice(4, 6)),
63
- };
64
- },
65
- rgb2rgba(rgba) {
66
- if (typeof rgba === 'string') {
67
- rgba = (/rgba?\((.*?)\)/.exec(rgba) || ['', '0,0,0,1'])[1].split(',');
68
- return {
69
- r: Number(rgba[0]) || 0,
70
- g: Number(rgba[1]) || 0,
71
- b: Number(rgba[2]) || 0,
72
- a: Number(rgba[3] ? rgba[3] : 1),
73
- };
74
- } else {
75
- return rgba;
76
- }
77
- },
78
- rgb2hsv({ r, g, b }) {
79
- r = r / 255;
80
- g = g / 255;
81
- b = b / 255;
82
- const max = Math.max(r, g, b);
83
- const min = Math.min(r, g, b);
84
- const delta = max - min;
85
- let h = 0;
86
- if (max === min) {
87
- h = 0;
88
- } else if (max === r) {
89
- if (g >= b) {
90
- h = (60 * (g - b)) / delta;
91
- } else {
92
- h = (60 * (g - b)) / delta + 360;
93
- }
94
- } else if (max === g) {
95
- h = (60 * (b - r)) / delta + 120;
96
- } else if (max === b) {
97
- h = (60 * (r - g)) / delta + 240;
98
- }
99
- h = Math.floor(h);
100
- let s = parseFloat((max === 0 ? 0 : 1 - min / max).toFixed(2));
101
- let v = parseFloat(max.toFixed(2));
102
- return { h, s, v };
103
- },
104
- },
105
- };