4 |
changeGradientControl({ type: 'linear' })"
7 | />
8 |
changeGradientControl({ type: 'radial' })"
11 | />
12 |
13 |
14 |
23 |
24 |
25 | {{degree}}°
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/src/lib/components/ColorPicker/Gradient/GradientControls/script.js:
--------------------------------------------------------------------------------
1 | import { useMouseEvents } from "@/lib/hooks";
2 | import { calculateDegree } from "@/lib/helpers";
3 |
4 | export default {
5 | name: "GradientControls",
6 |
7 | props: {
8 | type: String,
9 | degree: Number,
10 | changeGradientControl: {
11 | type: Function,
12 | default: () => {}
13 | }
14 | },
15 | data() {
16 | return {
17 | disableClick: false,
18 | mouseEvents: () => {},
19 | }
20 | },
21 |
22 | mounted() {
23 | this.mouseEvents = useMouseEvents(this.mouseDownHandler, this.mouseMoveHandler, this.mouseUpHandler);
24 | },
25 |
26 | computed: {
27 | degreesStyle() {
28 | return { transform: `rotate(${this.degree}deg)` };
29 | }
30 | },
31 |
32 | methods: {
33 | mouseDownHandler(event) {
34 | const pointer = event.target;
35 | const pointerBox = pointer.getBoundingClientRect();
36 | const centerY = pointerBox.top + parseInt(8 - window.pageYOffset, 10);
37 | const centerX = pointerBox.left + parseInt(8 - window.pageXOffset, 10);
38 |
39 | return {
40 | centerY,
41 | centerX,
42 |
43 | };
44 | },
45 |
46 | mouseMoveHandler(event, { centerX, centerY }) {
47 | this.disableClick = true;
48 |
49 | const newDegree = calculateDegree(event.clientX, event.clientY, centerX, centerY);
50 |
51 | this.changeGradientControl({ degree: parseInt(newDegree, 10) });
52 | },
53 |
54 | mouseUpHandler(event) {
55 | const targetClasses = event.target.classList;
56 |
57 | if (targetClasses.contains('gradient-degrees') || targetClasses.contains('icon-rotate')) {
58 | return;
59 | }
60 |
61 | this.disableClick = false;
62 | },
63 |
64 | onClickGradientDegree() {
65 | if (this.disableClick) {
66 | this.disableClick = false;
67 | return;
68 | }
69 |
70 | let gradientDegree = this.degree + 45;
71 |
72 | if (gradientDegree >= 360) {
73 | gradientDegree = 0;
74 | }
75 |
76 | this.changeGradientControl({ degree: parseInt(gradientDegree, 10) });
77 | }
78 | }
79 | };
80 |
--------------------------------------------------------------------------------
/src/lib/components/ColorPicker/Gradient/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
29 |
30 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/src/lib/components/ColorPicker/Gradient/script.js:
--------------------------------------------------------------------------------
1 | import GradientControls from './GradientControls';
2 | import Preview from '../Preview';
3 | import Area from '../Area';
4 |
5 | import { getRightValue, rgbToHsv, generateGradientStyle } from "@/lib/helpers";
6 |
7 | export default {
8 | name: "Gradient",
9 |
10 | props: {
11 | type: {
12 | type: String,
13 | default: 'linear'
14 | },
15 | degree: {
16 | type: Number,
17 | default: 0
18 | },
19 | points: {
20 | type: Array,
21 | default: () => {
22 | return [
23 | {
24 | left: 0,
25 | red: 0,
26 | green: 0,
27 | blue: 0,
28 | alpha: 1,
29 | },
30 | {
31 | left: 100,
32 | red: 255,
33 | green: 0,
34 | blue: 0,
35 | alpha: 1,
36 | },
37 | ];
38 | }
39 | },
40 | onStartChange: Function,
41 | onChange: Function,
42 | onEndChange: Function,
43 | },
44 |
45 | components: {
46 | GradientControls,
47 | Area,
48 | Preview
49 | },
50 |
51 | data() {
52 | return {
53 | activePointIndex: 0,
54 | gradientPoints: this.points,
55 | activePoint: this.points[0],
56 | colorRed: this.points[0].red,
57 | colorGreen: this.points[0].green,
58 | colorBlue: this.points[0].blue,
59 | colorAlpha: this.points[0].alpha,
60 | colorHue: 0,
61 | colorSaturation: 100,
62 | colorValue: 100,
63 | gradientType: this.type,
64 | gradientDegree: this.degree,
65 | actions: {
66 | onStartChange: this.onStartChange,
67 | onChange: this.onChange,
68 | onEndChange: this.onEndChange,
69 | }
70 | }
71 | },
72 |
73 | mounted() {
74 | const { hue, saturation, value } = rgbToHsv({ red: this.colorRed, green: this.colorGreen, blue: this.colorBlue });
75 |
76 | this.colorHue = hue;
77 | this.colorSaturation = saturation;
78 | this.colorValue = value;
79 |
80 | document.addEventListener('keyup', this.keyUpHandler);
81 | },
82 |
83 | beforeDestroy() {
84 | document.removeEventListener('keyup', this.keyUpHandler);
85 | },
86 |
87 | methods: {
88 | removePoint(index = this.activePointIndex) {
89 | if (this.gradientPoints.length <= 2) {
90 | return;
91 | }
92 |
93 | this.gradientPoints.splice(index, 1);
94 |
95 |
96 | if (index > 0) {
97 | this.activePointIndex = index - 1;
98 | }
99 |
100 | this.onChange && this.onChange({
101 | points: this.gradientPoints,
102 | type: this.gradientType,
103 | degree: this.gradientDegree,
104 | style: generateGradientStyle(this.gradientPoints, this.gradientType, this.gradientDegree),
105 | });
106 | },
107 |
108 | keyUpHandler(event) {
109 | if ((event.keyCode === 46 || event.keyCode === 8)) {
110 | this.removePoint(this.activePointIndex);
111 | }
112 | },
113 |
114 | changeActivePointIndex(index) {
115 | this.activePointIndex = index;
116 |
117 | this.activePoint = this.gradientPoints[index];
118 |
119 | const { red, green, blue, alpha } = this.activePoint;
120 |
121 | this.colorRed = red;
122 | this.colorGreen = green;
123 | this.colorBlue = blue;
124 | this.colorAlpha = alpha;
125 |
126 | const { hue, saturation, value } = rgbToHsv({ red, green, blue });
127 |
128 | this.colorHue = hue;
129 | this.colorSaturation = saturation;
130 | this.colorValue = value;
131 | },
132 |
133 | changeGradientControl({ type, degree }) {
134 | type = getRightValue(type, this.gradientType);
135 | degree = getRightValue(degree, this.gradientDegree);
136 |
137 | this.gradientType = type;
138 | this.gradientDegree = degree;
139 |
140 | this.onChange({
141 | points: this.gradientPoints,
142 | type: this.gradientType,
143 | degree: this.gradientDegree,
144 | style: generateGradientStyle(this.gradientPoints, this.gradientType, this.gradientDegree),
145 | })
146 | },
147 |
148 | updateColor({ red, green, blue, alpha, hue, saturation, value }, actionName = 'onChange') {
149 | red = getRightValue(red, this.colorRed);
150 | green = getRightValue(green, this.colorGreen);
151 | blue = getRightValue(blue, this.colorBlue);
152 | alpha = getRightValue(alpha, this.colorAlpha);
153 | hue = getRightValue(hue, this.colorHue);
154 | saturation = getRightValue(saturation, this.colorSaturation);
155 | value = getRightValue(value, this.colorValue);
156 |
157 | const localGradientPoints = this.gradientPoints.slice();
158 |
159 | localGradientPoints[this.activePointIndex] = {
160 | ...localGradientPoints[this.activePointIndex],
161 | red,
162 | green,
163 | blue,
164 | alpha,
165 | };
166 |
167 | this.colorRed = red;
168 | this.colorGreen = green;
169 | this.colorBlue = blue;
170 | this.colorAlpha = alpha;
171 | this.colorHue = hue;
172 | this.colorSaturation = saturation;
173 | this.colorValue = value;
174 | this.gradientPoints = localGradientPoints;
175 |
176 | const action = this.actions[actionName];
177 |
178 | action && action({
179 | points: localGradientPoints,
180 | type: this.gradientType,
181 | degree: this.gradientDegree,
182 | style: generateGradientStyle(localGradientPoints, this.gradientType, this.gradientDegree),
183 | });
184 | },
185 |
186 | updateGradientLeft(left, index, actionName = 'onChange') {
187 | this.gradientPoints[index].left = left;
188 |
189 | const action = this.actions[actionName];
190 |
191 | action && action({
192 | points: this.gradientPoints,
193 | type: this.gradientType,
194 | degree: this.gradientDegree,
195 | style: generateGradientStyle(this.gradientPoints, this.gradientType, this.gradientDegree),
196 | });
197 | },
198 |
199 | addPoint(left) {
200 | this.gradientPoints.push({
201 | ...this.gradientPoints[this.activePointIndex],
202 | left,
203 | });
204 |
205 | this.activePointIndex = this.gradientPoints.length - 1;
206 |
207 | this.onChange && this.onChange({
208 | points: this.gradientPoints,
209 | type: this.gradientType,
210 | degree: this.gradientDegree,
211 | style: generateGradientStyle(this.gradientPoints, this.gradientType, this.gradientDegree),
212 | });
213 | },
214 |
215 | }
216 | };
217 |
--------------------------------------------------------------------------------
/src/lib/components/ColorPicker/Preview/Hex/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/lib/components/ColorPicker/Preview/Hex/script.js:
--------------------------------------------------------------------------------
1 | import { Input } from "@/lib/components/UI";
2 |
3 | import { rgbToHex, hexToRgb } from "@/lib/helpers";
4 |
5 | export default {
6 | name: "Preview",
7 |
8 | props: {
9 | red: Number,
10 | green: Number,
11 | blue: Number,
12 | alpha: Number,
13 | updateColor: Function,
14 | },
15 |
16 | components: {
17 | Input
18 | },
19 |
20 | data() {
21 | return {
22 | inProgress: false,
23 | hexValue: rgbToHex(this.red, this.green, this.blue),
24 | }
25 | },
26 |
27 | computed: {
28 | hex() {
29 | return rgbToHex(this.red, this.green, this.blue)
30 | }
31 | },
32 |
33 | watch: {
34 | inProgress: "setHex",
35 | red: "setHex",
36 | green: "setHex",
37 | blue: "setHex",
38 | },
39 |
40 | methods: {
41 | setHex() {
42 | if (this.inProgress) {
43 | return;
44 | }
45 |
46 | this.hexValue = this.hex;
47 | },
48 |
49 | changeHex(event) {
50 | const color = hexToRgb(event.target.value);
51 |
52 | if (color) {
53 | this.updateColor(color);
54 | }
55 | }
56 | }
57 | };
58 |
--------------------------------------------------------------------------------
/src/lib/components/ColorPicker/Preview/RGB/RGBItem/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
13 |
14 |
15 |
16 |
17 |
18 |
21 |
--------------------------------------------------------------------------------
/src/lib/components/ColorPicker/Preview/RGB/RGBItem/script.js:
--------------------------------------------------------------------------------
1 | import { Input } from "@/lib/components/UI";
2 |
3 | export default {
4 | name: "RGBItem",
5 |
6 | props: {
7 | value: String | Number,
8 | type: String,
9 | label: String,
10 | onChange: Function,
11 | },
12 |
13 | components: {
14 | Input
15 | },
16 |
17 | data() {
18 | return {
19 | inputValue: this.value,
20 | inProgress: false
21 | }
22 | },
23 |
24 | watch: {
25 | value: "setValue"
26 | },
27 |
28 | methods: {
29 | onChangeHandler(event) {
30 | const value = +event.target.value;
31 |
32 | if (Number.isNaN(value) || value.length > 3 || value < 0 || value > 255) {
33 | this.inputValue = this.value;
34 |
35 | this.$forceUpdate();
36 |
37 | return;
38 | }
39 |
40 | this.inputValue = event.target.value;
41 |
42 | this.onChange(value);
43 | },
44 |
45 | onBlur() {
46 | if (!this.inputValue && !this.inputValue !== 0) {
47 | this.inputValue = this.value;
48 | }
49 |
50 | this.inProgress = false;
51 | },
52 |
53 | setValue() {
54 | if (this.value !== +this.inputValue && this.inputValue !== '') {
55 | this.inputValue = this.value;
56 | }
57 | }
58 | }
59 | };
60 |
--------------------------------------------------------------------------------
/src/lib/components/ColorPicker/Preview/RGB/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
15 |
21 |
27 |
28 |
29 |
30 |
31 |
32 |
35 |
--------------------------------------------------------------------------------
/src/lib/components/ColorPicker/Preview/RGB/script.js:
--------------------------------------------------------------------------------
1 | import RGBItem from './RGBItem';
2 | import { rgbToHsv } from "@/lib/helpers";
3 |
4 | export default {
5 | name: "RGB",
6 |
7 | props: {
8 | red: Number,
9 | green: Number,
10 | blue: Number,
11 | alpha: Number,
12 | updateColor: Function,
13 | },
14 |
15 | components: {
16 | RGBItem
17 | },
18 |
19 | methods: {
20 | changeValue(field, value) {
21 | if (field === 'alpha') {
22 | this.updateColor({ alpha: value / 100 });
23 |
24 | return;
25 | }
26 |
27 | const color = rgbToHsv({
28 | red: this.red, green: this.green, blue: this.blue, [field]: value,
29 | });
30 |
31 | this.updateColor({ ...color, [field]: value });
32 | },
33 | }
34 | };
35 |
--------------------------------------------------------------------------------
/src/lib/components/ColorPicker/Preview/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/lib/components/ColorPicker/Preview/script.js:
--------------------------------------------------------------------------------
1 | import Hex from './Hex';
2 | import RGB from './RGB';
3 |
4 | export default {
5 | name: "Preview",
6 |
7 | props: {
8 | red: Number,
9 | green: Number,
10 | blue: Number,
11 | alpha: Number,
12 | updateColor: Function,
13 | },
14 |
15 | components: {
16 | Hex,
17 | RGB
18 | }
19 | };
20 |
--------------------------------------------------------------------------------
/src/lib/components/ColorPicker/Solid/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
14 |
15 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/lib/components/ColorPicker/Solid/script.js:
--------------------------------------------------------------------------------
1 | import Area from '../Area';
2 | import Preview from '../Preview';
3 |
4 | import { rgbToHsv, getRightValue, generateSolidStyle } from '@/lib/helpers';
5 |
6 | export default {
7 | name: "Solid",
8 |
9 | props: {
10 | red: {
11 | type: Number,
12 | default: 255
13 | },
14 | green: {
15 | type: Number,
16 | default: 0
17 | },
18 | blue: {
19 | type: Number,
20 | default: 0
21 | },
22 | alpha: {
23 | type: Number,
24 | default: 1
25 | },
26 | hue: Number,
27 | saturation: Number,
28 | value: Number,
29 | onStartChange: Function,
30 | onChange: Function,
31 | onEndChange: Function,
32 | },
33 |
34 | components: {
35 | Area,
36 | Preview,
37 | },
38 |
39 | data() {
40 | return {
41 | colorRed: this.red,
42 | colorGreen: this.green,
43 | colorBlue: this.blue,
44 | colorAlpha: this.alpha,
45 | colorHue: 0,
46 | colorSaturation: 100,
47 | colorValue: 100,
48 | actions: {
49 | onStartChange: this.onStartChange,
50 | onChange: this.onChange,
51 | onEndChange: this.onEndChange,
52 | }
53 |
54 | }
55 | },
56 |
57 | mounted() {
58 | const { hue, saturation, value } = rgbToHsv({ red: this.colorRed, green: this.colorGreen, blue: this.colorBlue });
59 |
60 | this.colorHue = hue;
61 | this.colorSaturation = saturation;
62 | this.colorValue = value;
63 | },
64 |
65 | computed: {
66 | hsv() {
67 | if (this.hue === undefined || this.saturation === undefined || this.value=== undefined) {
68 | return rgbToHsv({ red: this.red, green: this.green, blue: this.blue });
69 | }
70 |
71 | return {
72 | hue: this.hue,
73 | saturation: this.saturation,
74 | value: this.value,
75 | }
76 | },
77 |
78 | color() {
79 | return {
80 | red: this.red,
81 | green: this.green,
82 | blue: this.blue,
83 | alpha: this.alpha,
84 |
85 | }
86 | }
87 | },
88 |
89 | watch: {
90 | hsv: function ({ hue, saturation, value }) {
91 | this.colorHue = hue;
92 | this.colorSaturation = saturation;
93 | this.colorValue = value;
94 | },
95 |
96 | color: function ({ red, green, blue, alpha }) {
97 | this.colorRed = red;
98 | this.colorGreen = green;
99 | this.colorBlue = blue;
100 | this.colorAlpha = alpha;
101 | },
102 | },
103 |
104 | methods: {
105 | updateColor({ red, green, blue, alpha, hue, saturation, value }, actionName = 'onChange') {
106 | red = getRightValue(red, this.colorRed);
107 | green = getRightValue(green, this.colorGreen);
108 | blue = getRightValue(blue, this.colorBlue);
109 | alpha = getRightValue(alpha, this.colorAlpha);
110 | hue = getRightValue(hue, this.colorHue);
111 | saturation = getRightValue(saturation, this.colorSaturation);
112 | value = getRightValue(value, this.colorValue);
113 |
114 | this.colorRed = red;
115 | this.colorGreen = green;
116 | this.colorBlue = blue;
117 | this.colorAlpha = alpha;
118 | this.colorHue = hue;
119 | this.colorSaturation = saturation;
120 | this.colorValue = value;
121 |
122 | const action = this.actions[actionName];
123 |
124 | action && action({
125 | red,
126 | green,
127 | blue,
128 | alpha,
129 | hue,
130 | saturation,
131 | value,
132 | style: generateSolidStyle(red, green, blue, alpha),
133 | });
134 | },
135 | }
136 | };
137 |
--------------------------------------------------------------------------------
/src/lib/components/ColorPicker/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
13 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/src/lib/components/ColorPicker/script.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import { Plugin } from "vue-fragment";
3 |
4 | import Solid from './Solid';
5 | import Gradient from './Gradient';
6 |
7 | Vue.use(Plugin);
8 |
9 | export default {
10 | name: "ColorPicker",
11 |
12 | props: {
13 | isGradient: {
14 | type: Boolean,
15 | default: false,
16 | },
17 | color: {
18 | type: Object,
19 | default: () => ({
20 | red: 255,
21 | green: 0,
22 | blue: 0,
23 | alpha: 1,
24 | hue: 0,
25 | saturation: 100,
26 | value: 100,
27 | })
28 | },
29 |
30 | gradient: {
31 | type: Object,
32 | default: () => ({
33 | type: 'linear',
34 | degree: 0,
35 | points: [
36 | {
37 | left: 0,
38 | red: 0,
39 | green: 0,
40 | blue: 0,
41 | alpha: 1,
42 | },
43 | {
44 | left: 100,
45 | red: 255,
46 | green: 0,
47 | blue: 0,
48 | alpha: 1,
49 | },
50 | ],
51 | })
52 | },
53 |
54 | onStartChange: {
55 | type: Function,
56 | default: () => {}
57 | },
58 | onChange: {
59 | type: Function,
60 | default: () => {}
61 | },
62 | onEndChange: {
63 | type: Function,
64 | default: () => {}
65 | },
66 | },
67 |
68 | components: {
69 | Solid,
70 | Gradient
71 | }
72 | };
73 |
--------------------------------------------------------------------------------
/src/lib/components/UI/Input/index.scss:
--------------------------------------------------------------------------------
1 | .input-field {
2 | display: flex;
3 | flex-shrink: 0;
4 | align-items: center;
5 | flex-direction: column;
6 |
7 | .label {
8 | font-size: 12px;
9 | line-height: 15px;
10 | font-weight: 600;
11 | margin-top: 6px;
12 | margin-bottom: 0;
13 | color: #1F2667;
14 | }
15 |
16 | .input-container {
17 | display: flex;
18 | align-items: center;
19 | position: relative;
20 | width: 100%;
21 | border-radius: 6px;
22 | color: #28314d;
23 |
24 | .input {
25 | width: 100%;
26 | outline: 0;
27 | color: #1F2667;
28 | border-radius: inherit;
29 | border: 1px solid #bbbfc5;
30 | height: 24px;
31 | font-size: 12px;
32 | font-weight: 600;
33 | padding: 0 6px;
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/lib/components/UI/Input/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
13 | {{label}}
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/lib/components/UI/Input/script.js:
--------------------------------------------------------------------------------
1 | export default {
2 | name: "Input",
3 |
4 | props: {
5 | value: {
6 | type: String | Number,
7 | default: '',
8 | },
9 | label: {
10 | type: String,
11 | default: '',
12 | },
13 | type: {
14 | type: String,
15 | default: 'text'
16 | },
17 | classes: {
18 | type: String,
19 | default: ''
20 | },
21 | onFocus: {
22 | type: Function,
23 | default: () => {
24 | }
25 | },
26 | onBlur: {
27 | type: Function,
28 | default: () => {
29 | }
30 | },
31 | },
32 |
33 | model: {
34 | prop: "value",
35 | event: "input"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/lib/components/UI/index.js:
--------------------------------------------------------------------------------
1 | export { default as Input } from './Input';
2 |
--------------------------------------------------------------------------------
/src/lib/helpers/calculateDegree.js:
--------------------------------------------------------------------------------
1 | export default function calculateDegree(x, y, centerX, centerY) {
2 | const radians = Math.atan2(x - centerX, y - centerY);
3 | return (radians * (180 / Math.PI) * -1) + 180;
4 | }
5 |
--------------------------------------------------------------------------------
/src/lib/helpers/changePicker.js:
--------------------------------------------------------------------------------
1 | import hsvToRgb from './hsvToRgb';
2 |
3 | export default function changePicker(x, y, height, width, hue) {
4 | if (x > width) x = width;
5 | if (y > height) y = height;
6 | if (x < 0) x = 0;
7 | if (y < 0) y = 0;
8 | const value = 100 - (y * 100 / height) | 0;
9 | const saturation = x * 100 / width | 0;
10 | return {
11 | ...hsvToRgb(hue, saturation, value),
12 | saturation,
13 | value,
14 | };
15 | }
16 |
--------------------------------------------------------------------------------
/src/lib/helpers/generateStyles.js:
--------------------------------------------------------------------------------
1 | export function generateSolidStyle(red, green, blue, alpha) {
2 | return `rgba(${red}, ${green}, ${blue}, ${alpha})`;
3 | }
4 |
5 | export function generateGradientStyle(points, type, degree) {
6 | let style = '';
7 | const sortedPoints = points.slice();
8 |
9 | sortedPoints.sort((a, b) => a.left - b.left);
10 |
11 | if (type === 'linear') {
12 | style = `linear-gradient(${degree}deg,`;
13 | } else {
14 | style = 'radial-gradient(';
15 | }
16 |
17 | sortedPoints.forEach((point, index) => {
18 | style += `rgba(${point.red}, ${point.green}, ${point.blue}, ${point.alpha}) ${point.left}%`;
19 |
20 | if (index !== sortedPoints.length - 1) {
21 | style += ',';
22 | }
23 | });
24 |
25 | style += ')';
26 |
27 | return style;
28 | }
29 |
--------------------------------------------------------------------------------
/src/lib/helpers/getAlpha.js:
--------------------------------------------------------------------------------
1 | export default function getAlpha(value, width) {
2 | value = Number((value / width).toFixed(2));
3 |
4 | return value > 1 ? 1 : value < 0 ? 0 : value;
5 | }
6 |
--------------------------------------------------------------------------------
/src/lib/helpers/getHue.js:
--------------------------------------------------------------------------------
1 | import { hsvToRgb } from './index';
2 |
3 | export default function getHue(offsetX, width, saturation, value) {
4 | let hue = ((360 * offsetX) / width) | 0;
5 |
6 | hue = hue < 0 ? 0 : hue > 360 ? 360 : hue;
7 |
8 | return {
9 | ...hsvToRgb(hue, saturation, value),
10 | hue,
11 | };
12 | }
13 |
--------------------------------------------------------------------------------
/src/lib/helpers/getRgbByHue.js:
--------------------------------------------------------------------------------
1 | export default function getRgbByHue(hue) {
2 | let C = 1;
3 | const H = hue / 60;
4 | let X = C * (1 - Math.abs(H % 2 - 1));
5 | const m = 0;
6 | const precision = 255;
7 | let r = 0;
8 | let g = 0;
9 | let b = 0;
10 |
11 | C = (C + m) * precision | 0;
12 | X = (X + m) * precision | 0;
13 |
14 | if (H >= 0 && H < 1) {
15 | r = C | 0;
16 | g = X | 0;
17 | b = m | 0;
18 | }
19 | if (H >= 1 && H < 2) {
20 | r = X | 0;
21 | g = C | 0;
22 | b = m | 0;
23 | }
24 | if (H >= 2 && H < 3) {
25 | r = m | 0;
26 | g = C | 0;
27 | b = X | 0;
28 | }
29 | if (H >= 3 && H < 4) {
30 | r = m | 0;
31 | g = X | 0;
32 | b = C | 0;
33 | }
34 | if (H >= 4 && H < 5) {
35 | r = X | 0;
36 | g = m | 0;
37 | b = C | 0;
38 | }
39 | if (H >= 5 && H <= 6) {
40 | r = C | 0;
41 | g = m | 0;
42 | b = X | 0;
43 | }
44 |
45 | return {
46 | red: r,
47 | green: g,
48 | blue: b,
49 | };
50 | }
51 |
--------------------------------------------------------------------------------
/src/lib/helpers/getRightValue.js:
--------------------------------------------------------------------------------
1 | export default function getRightValue(newValue, oldValue) {
2 | return (!newValue && newValue !== 0) ? oldValue : newValue;
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/helpers/hexToRgb.js:
--------------------------------------------------------------------------------
1 | import { rgbToHsv, setRgba } from './index';
2 |
3 | const hexRegexp = /(^#{0,1}[0-9A-F]{6}$)|(^#{0,1}[0-9A-F]{3}$)|(^#{0,1}[0-9A-F]{8}$)/i;
4 |
5 | const regexp = /([0-9A-F])([0-9A-F])([0-9A-F])/i;
6 |
7 | export default function hexToRgb(value) {
8 | const valid = hexRegexp.test(value);
9 |
10 | if (valid) {
11 | if (value[0] === '#') value = value.slice(1, value.length);
12 |
13 | if (value.length === 3) value = value.replace(regexp, '$1$1$2$2$3$3');
14 |
15 | const red = parseInt(value.substr(0, 2), 16);
16 | const green = parseInt(value.substr(2, 2), 16);
17 | const blue = parseInt(value.substr(4, 2), 16);
18 | const alpha = parseInt(value.substr(6, 2), 16) / 255;
19 |
20 | const color = setRgba(red, green, blue, alpha);
21 | const hsv = rgbToHsv({ ...color });
22 |
23 | return {
24 | ...color,
25 | ...hsv,
26 | };
27 | }
28 |
29 | return false;
30 | }
31 |
--------------------------------------------------------------------------------
/src/lib/helpers/hsvToRgb.js:
--------------------------------------------------------------------------------
1 | import setRGBA from './setRgba';
2 |
3 | export default function hsvToRgb(hue, saturation, value) {
4 | value /= 100;
5 | const sat = saturation / 100;
6 | let C = sat * value;
7 | const H = hue / 60;
8 | let X = C * (1 - Math.abs(H % 2 - 1));
9 | let m = value - C;
10 | const precision = 255;
11 |
12 | C = (C + m) * precision | 0;
13 | X = (X + m) * precision | 0;
14 | m = m * precision | 0;
15 |
16 | if (H >= 1 && H < 2) {
17 | return setRGBA(X, C, m);
18 | }
19 | if (H >= 2 && H < 3) {
20 | return setRGBA(m, C, X);
21 | }
22 | if (H >= 3 && H < 4) {
23 | return setRGBA(m, X, C);
24 | }
25 | if (H >= 4 && H < 5) {
26 | return setRGBA(X, m, C);
27 | }
28 | if (H >= 5 && H <= 6) {
29 | return setRGBA(C, m, X);
30 | }
31 |
32 | return setRGBA(C, X, m);
33 | }
34 |
--------------------------------------------------------------------------------
/src/lib/helpers/index.js:
--------------------------------------------------------------------------------
1 | export { default as rgbToHsv } from './rgbToHsv';
2 | export { default as getRgbByHue } from './getRgbByHue';
3 | export { default as changePicker } from './changePicker';
4 | export { default as hsvToRgb } from './hsvToRgb';
5 | export { default as getHue } from './getHue';
6 | export { default as getAlpha } from './getAlpha';
7 | export { default as rgbToHex } from './rgbToHex';
8 | export { default as hexToRgb } from './hexToRgb';
9 | export { default as setRgba } from './setRgba';
10 | export { default as updateGradientActivePercent } from './updateGradientActivePercent';
11 | export { default as calculateDegree } from './calculateDegree';
12 | export { default as getRightValue } from './getRightValue';
13 | export * from './generateStyles';
14 |
--------------------------------------------------------------------------------
/src/lib/helpers/rgbToHex.js:
--------------------------------------------------------------------------------
1 | export default function rgbToHex(red, green, blue) {
2 | let r16 = red.toString(16);
3 | let g16 = green.toString(16);
4 | let b16 = blue.toString(16);
5 |
6 | if (red < 16) r16 = `0${r16}`;
7 | if (green < 16) g16 = `0${g16}`;
8 | if (blue < 16) b16 = `0${b16}`;
9 |
10 | return r16 + g16 + b16;
11 | }
12 |
--------------------------------------------------------------------------------
/src/lib/helpers/rgbToHsv.js:
--------------------------------------------------------------------------------
1 | export default function rgbToHSv({ red, green, blue }) {
2 | let rr;
3 | let gg;
4 | let bb;
5 | let h;
6 | let s;
7 |
8 | const rabs = red / 255;
9 | const gabs = green / 255;
10 | const babs = blue / 255;
11 | const v = Math.max(rabs, gabs, babs);
12 | const diff = v - Math.min(rabs, gabs, babs);
13 | const diffc = c => (v - c) / 6 / diff + 1 / 2;
14 | if (diff === 0) {
15 | h = 0;
16 | s = 0;
17 | } else {
18 | s = diff / v;
19 | rr = diffc(rabs);
20 | gg = diffc(gabs);
21 | bb = diffc(babs);
22 |
23 | if (rabs === v) {
24 | h = bb - gg;
25 | } else if (gabs === v) {
26 | h = (1 / 3) + rr - bb;
27 | } else if (babs === v) {
28 | h = (2 / 3) + gg - rr;
29 | }
30 | if (h < 0) {
31 | h += 1;
32 | } else if (h > 1) {
33 | h -= 1;
34 | }
35 | }
36 |
37 | return {
38 | hue: Math.round(h * 360),
39 | saturation: Math.round(s * 100),
40 | value: Math.round(v * 100),
41 | };
42 | }
43 |
--------------------------------------------------------------------------------
/src/lib/helpers/setRgba.js:
--------------------------------------------------------------------------------
1 | function isValidRGBValue(value) {
2 | return (typeof (value) === 'number' && Number.isNaN(value) === false && value >= 0 && value <= 255);
3 | }
4 |
5 | export default function setRGBA(red, green, blue, alpha) {
6 | if (isValidRGBValue(red) && isValidRGBValue(green) && isValidRGBValue(blue)) {
7 | const color = {
8 | red: red | 0,
9 | green: green | 0,
10 | blue: blue | 0,
11 | };
12 |
13 | if (isValidRGBValue(alpha) === true) {
14 | color.alpha = alpha | 0;
15 | }
16 |
17 | // RGBToHSL(color.r, color.g, color.b);
18 |
19 | return color;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/lib/helpers/updateGradientActivePercent.js:
--------------------------------------------------------------------------------
1 | export default function updateGradientActivePercent(offsetX, width) {
2 | const leftPercent = (offsetX * 100) / width;
3 | return leftPercent < 0 ? 0 : leftPercent > 100 ? 100 : leftPercent;
4 | }
5 |
--------------------------------------------------------------------------------
/src/lib/hooks/index.js:
--------------------------------------------------------------------------------
1 | export { default as useMouseEvents } from './mouseEvents';
2 |
--------------------------------------------------------------------------------
/src/lib/hooks/mouseEvents.js:
--------------------------------------------------------------------------------
1 | export default function useMouseEvents(mouseDownHandler, mouseMoveHandler, mouseUpHandler) {
2 | return function mouseEventsHandler(event) {
3 | let positions = mouseDownHandler(event);
4 |
5 | function onMouseMove(event) {
6 | positions = mouseMoveHandler(event, positions) || positions;
7 | }
8 |
9 | window.addEventListener('mousemove', onMouseMove);
10 |
11 | window.addEventListener('mouseup', event => {
12 | window.removeEventListener('mousemove', onMouseMove);
13 |
14 | mouseUpHandler && mouseUpHandler(event, positions);
15 | }, { once: true });
16 | };
17 | }
18 |
--------------------------------------------------------------------------------
/src/lib/index.js:
--------------------------------------------------------------------------------
1 | export { default as ColorPicker } from './components/ColorPicker/index.vue';
2 |
3 | import './index.scss';
4 |
--------------------------------------------------------------------------------
/src/lib/index.scss:
--------------------------------------------------------------------------------
1 | * {
2 | box-sizing: border-box;
3 | }
4 |
5 | .ui-color-picker {
6 | margin: 8px;
7 | background-color: #FFFFFF;
8 | display: flex;
9 | flex-direction: column;
10 | width: 280px;
11 | user-select: none;
12 |
13 | .gradient-controls {
14 | display: flex;
15 | flex-direction: row;
16 | align-items: center;
17 | width: 100%;
18 | margin-bottom: 8px;
19 | height: 24px;
20 | padding: 0 16px;
21 |
22 | .gradient-type {
23 | flex: 1;
24 | display: flex;
25 |
26 | .gradient-type-item {
27 | height: 28px;
28 | width: 28px;
29 | border-radius: 50%;
30 | position: relative;
31 | cursor: pointer;
32 |
33 | &.active {
34 | &::after {
35 | content: '';
36 | display: block;
37 | position: absolute;
38 | top: -3px;
39 | bottom: -3px;
40 | left: -3px;
41 | right: -3px;
42 | border: 2px solid #1F2667;
43 | border-radius: 50%;
44 | }
45 | }
46 |
47 | &.liner-gradient {
48 | background: linear-gradient(270deg, #FFFFFF 0%, #1F2667 100%);
49 | }
50 |
51 | &.radial-gradient {
52 | margin-left: 8px;
53 | background: radial-gradient(circle, #FFFFFF 0%, #1F2667 100%);
54 | }
55 | }
56 |
57 | }
58 |
59 | .gradient-degrees-options {
60 | position: relative;
61 |
62 | .gradient-degrees {
63 | display: -ms-flexbox;
64 | display: flex;
65 | -webkit-box-pack: center;
66 | -ms-flex-pack: center;
67 | justify-content: center;
68 | -webkit-box-align: center;
69 | -ms-flex-align: center;
70 | align-items: center;
71 | position: relative;
72 | width: 28px;
73 | height: 28px;
74 | border: 3px solid #1F2667;
75 | border-radius: 18px;
76 | margin-right: 54px;
77 |
78 | .gradient-degree-center {
79 | position: relative;
80 | width: 6px;
81 | height: 22px;
82 | pointer-events: none;
83 |
84 | .gradient-degree-pointer {
85 | position: absolute;
86 | width: 6px;
87 | height: 6px;
88 | top: 2px;
89 | border-radius: 3px;
90 | background: #1F2667;
91 | }
92 | }
93 | }
94 |
95 | .gradient-degree-value {
96 | position: absolute;
97 | top: 0;
98 | right: 0;
99 | width: 48px;
100 | height: 28px;
101 | display: flex;
102 | align-items: center;
103 | justify-content: center;
104 | border: 1px solid #bbbfc5;
105 | border-radius: 6px;
106 |
107 | p {
108 | text-align: center;
109 | padding: 0 6px;
110 | }
111 | }
112 | }
113 | }
114 |
115 | .picker-area {
116 | display: flex;
117 | flex-direction: column;
118 | padding: 0 16px;
119 |
120 | &.gradient-tab {
121 | .picking-area {
122 | margin-bottom: 10px;
123 | }
124 | }
125 |
126 | .picking-area {
127 | width: 100%;
128 | height: 160px;
129 | margin-bottom: 16px;
130 | position: relative;
131 | border-radius: 8px;
132 |
133 | &:hover {
134 | cursor: default;
135 | }
136 |
137 | .picking-area-overlay1 {
138 | height: 100%;
139 | width: 100%;
140 | background: linear-gradient(to right, white 0%, rgba(255, 255, 255, 0) 100%);
141 | border-radius: 3px;
142 |
143 | .picking-area-overlay2 {
144 | height: 100%;
145 | width: 100%;
146 | background: linear-gradient(to bottom, rgba(0, 0, 0, 0) 0%, black 100%);
147 | border-radius: 8px;
148 | }
149 | }
150 | }
151 |
152 | .preview {
153 | display: flex;
154 | flex-direction: row;
155 | margin-bottom: 16px;
156 |
157 | .preview-box {
158 | box-sizing: border-box;
159 | height: 36px;
160 | width: 36px;
161 | border-radius: 8px;
162 | border: 1px solid #EBEDF5;
163 | }
164 |
165 | .color-hue-alpha {
166 | display: flex;
167 | flex-direction: column;
168 | flex: 1;
169 | margin-left: 6px;
170 |
171 | .hue {
172 | width: 100%;
173 | position: relative;
174 | border-radius: 10px;
175 | margin-bottom: 8px;
176 | padding: 0 7px;
177 | background-color: red;
178 | cursor: pointer;
179 |
180 | .hue-area {
181 | position: relative;
182 | height: 14px;
183 | background: -webkit-linear-gradient(left, #ff0000, #ff0080, #ff00ff, #8000ff, #0000ff, #0080ff, #00ffff, #00ff80, #00ff00, #80ff00, #ffff00, #ff8000, #ff0000);
184 | background: -o-linear-gradient(left, #ff0000, #ff8000, #ffff00, #80ff00, #00ff00, #00ff80, #00ffff, #0080ff, #0000ff, #8000ff, #ff00ff, #ff0080, #ff0000);
185 | background: -ms-linear-gradient(left, #ff0000, #ff8000, #ffff00, #80ff00, #00ff00, #00ff80, #00ffff, #0080ff, #0000ff, #8000ff, #ff00ff, #ff0080, #ff0000);
186 | background: -moz-linear-gradient(left, #ff0000, #ff8000, #ffff00, #80ff00, #00ff00, #00ff80, #00ffff, #0080ff, #0000ff, #8000ff, #ff00ff, #ff0080, #ff0000);
187 | background: linear-gradient(to right, #ff0000, #ff8000, #ffff00, #80ff00, #00ff00, #00ff80, #00ffff, #0080ff, #0000ff, #8000ff, #ff00ff, #ff0080, #ff0000);
188 | }
189 | }
190 |
191 | .alpha {
192 | position: relative;
193 | width: 100%;
194 | overflow: hidden;
195 | border-radius: 10px;
196 | height: 14px;
197 | cursor: pointer;
198 |
199 | .gradient {
200 | position: absolute;
201 | top: 0;
202 | left: 0;
203 | right: 0;
204 | bottom: 0;
205 | }
206 |
207 | .alpha-area {
208 | width: 100%;
209 | height: 100%;
210 | background: url("assets/images/alpha-background.svg");
211 | background-size: auto;
212 | background-position: 50% 50%;
213 | border-radius: 10px;
214 | padding: 0 7px;
215 |
216 | .alpha-mask {
217 | width: 100%;
218 | height: 100%;
219 | position: relative;
220 | }
221 | }
222 | }
223 | }
224 | }
225 |
226 | .gradient {
227 | width: 100%;
228 | height: 14px;
229 | position: relative;
230 | cursor: pointer;
231 | border-radius: 10px;
232 | margin-bottom: 8px;
233 | padding: 0 7px;
234 |
235 | .gradient-slider-container {
236 | height: 100%;
237 | width: 100%;
238 | position: relative;
239 | }
240 | }
241 |
242 | .picker-pointer {
243 | position: absolute;
244 | top: 1px;
245 | height: 12px;
246 | width: 12px;
247 | border: 1px solid #FFFFFF;
248 | background: transparent;
249 | border-radius: 50%;
250 | box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.3);
251 |
252 | .child-point {
253 | position: absolute;
254 | top: 50%;
255 | left: 50%;
256 | transform: translate(-50%, -50%);
257 | height: 3px;
258 | width: 3px;
259 | background: #FFFFFF;
260 | border-radius: 50%;
261 | opacity: 0;
262 | transition: opacity 0.33s;
263 |
264 | &.active {
265 | opacity: 1;
266 | }
267 | }
268 | }
269 | }
270 |
271 | .color-preview-area {
272 | margin-bottom: 8px;
273 | padding: 0 16px;
274 |
275 | .input-group {
276 | width: 100%;
277 | display: flex;
278 | flex-direction: row;
279 | justify-content: space-between;
280 |
281 | .uc-field-group:not(:last-child) {
282 | margin-right: 7px;
283 | }
284 | }
285 |
286 | .hex {
287 | width: 65px;
288 | }
289 |
290 | .rgb {
291 | width: 40px;
292 | }
293 | }
294 |
295 | .colors {
296 | padding: 3px 16px 0;
297 |
298 | .colors-label {
299 | display: flex;
300 | align-items: center;
301 | margin-bottom: 4px;
302 | cursor: pointer;
303 |
304 | .uc-icon {
305 | margin-right: 8px;
306 | transition: transform 0.3s;
307 | }
308 |
309 | .tp-text {
310 | text-transform: uppercase;
311 | }
312 |
313 | &.show {
314 | & + .colors-item-container {
315 | max-height: 80px;
316 | padding-bottom: 16px;
317 | }
318 |
319 | .uc-icon {
320 | transform: rotate(-90deg);
321 | }
322 | }
323 | }
324 |
325 | .template {
326 | display: flex;
327 | flex-direction: column;
328 | }
329 |
330 | .global {
331 | display: flex;
332 | flex-direction: column;
333 | align-items: flex-start;
334 | }
335 |
336 | .colors-item-container {
337 | display: flex;
338 | flex-wrap: wrap;
339 | width: 100%;
340 | transition: max-height 0.3s, padding-bottom 0.3s;
341 | max-height: 0;
342 | overflow: hidden;
343 |
344 | .colors-item {
345 | height: 24px;
346 | width: 24px;
347 | border-radius: 50%;
348 | background-color: #EBEDF5;
349 | cursor: pointer;
350 | position: relative;
351 | margin-top: 4px;
352 | flex-shrink: 0;
353 |
354 | &:not(.plus) {
355 | border: 1px solid #EBEDF5;
356 | }
357 |
358 | &.empty {
359 | display: flex;
360 | align-items: center;
361 | justify-content: center;
362 |
363 | .line {
364 | width: 2px;
365 | height: 16px;
366 | background-color: #8892B3;
367 | border-radius: 1px;
368 | transform: rotate(45deg);
369 | }
370 | }
371 |
372 | &.plus {
373 | margin-bottom: 4px;
374 |
375 | .uc-icon {
376 | position: absolute;
377 | top: 50%;
378 | left: 50%;
379 | transform: translate(-50%, -50%);
380 | opacity: 1;
381 | }
382 | }
383 |
384 | &:not(:first-child):not(:nth-child(9)) {
385 | margin-left: 8px;
386 | }
387 |
388 | .uc-icon {
389 | position: absolute;
390 | right: -8px;
391 | top: -3px;
392 | opacity: 0;
393 | transition: opacity 0.3s;
394 | }
395 |
396 | &:hover {
397 | .uc-icon {
398 | opacity: 1;
399 | }
400 | }
401 |
402 | &.active {
403 | &::after {
404 | content: '';
405 | display: block;
406 | position: absolute;
407 | top: -3px;
408 | bottom: -3px;
409 | left: -3px;
410 | right: -3px;
411 | border: 2px solid #8892B3;
412 | border-radius: 50%;
413 | }
414 | }
415 | }
416 | }
417 | }
418 | }
419 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import App from './App.vue'
3 |
4 | Vue.config.productionTip = false
5 |
6 | new Vue({
7 | render: h => h(App),
8 | }).$mount('#app')
9 |
--------------------------------------------------------------------------------
/vue.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | publicPath: process.env.NODE_ENV === 'production'
3 | ? './' // prod
4 | : '/', // dev
5 | }
6 |
--------------------------------------------------------------------------------