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 +1 -1
- package/src/components/ui/ColorPicker/Alpha.vue +3 -4
- package/src/components/ui/ColorPicker/Colors.vue +2 -3
- package/src/components/ui/ColorPicker/Preview.vue +2 -3
- package/src/components/ui/ColorPicker/Saturation.vue +3 -4
- package/src/components/ui/ColorPicker/utils.js +170 -0
- package/src/components/ui/ColorPicker.vue +78 -44
- package/src/components/ui/InputColorPicker.vue +86 -16
- package/src/components/ui/utils/FormComponent.js +11 -0
- package/src/components/ui/ColorPicker/mixin.js +0 -105
package/package.json
CHANGED
|
@@ -21,9 +21,8 @@
|
|
|
21
21
|
}
|
|
22
22
|
</style>
|
|
23
23
|
<script>
|
|
24
|
-
import
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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
|
-
|
|
66
|
-
|
|
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
|
-
@
|
|
44
|
-
v-if="modelMode
|
|
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
|
-
@
|
|
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
|
|
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
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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(
|
|
208
|
-
|
|
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
|
|
270
|
+
this.modelMode = this.modelMode === HEX ? RGBA : HEX;
|
|
249
271
|
},
|
|
250
272
|
selectSaturation(color) {
|
|
251
|
-
const { r, g, b, h, s, v } =
|
|
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 } =
|
|
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 } =
|
|
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 } =
|
|
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 } =
|
|
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 } =
|
|
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 } =
|
|
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
|
-
@
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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
|
-
};
|