@thinkpixellab-public/px-vue 4.0.11 → 4.0.13
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/assets/icons/ellipsis.svg +1 -0
- package/assets/icons/eyedropper.svg +1 -0
- package/bases/PxBaseColor.vue +186 -0
- package/bases/PxBaseItems.vue +2 -2
- package/bases/PxBaseSvg.vue +36 -40
- package/bases/PxBaseUniqueId.vue +5 -8
- package/components/PxBalancedText.vue +13 -5
- package/components/PxCodeSample.vue +90 -0
- package/components/PxColorPalette.vue +118 -0
- package/components/PxColorPaletteButton.vue +140 -0
- package/components/PxColorPanel.vue +267 -0
- package/components/PxColorPicker.vue +144 -0
- package/components/PxFlex.vue +1 -1
- package/components/PxIcon.vue +19 -5
- package/components/PxIconButton.vue +4 -9
- package/components/PxLabeledInput.vue +54 -0
- package/components/PxSvgRender.vue +39 -0
- package/components/PxTable.vue +125 -51
- package/package.json +3 -1
- package/stories/PxCodeSample.stories.js +53 -0
- package/stories/PxColorPalette.stories.js +59 -0
- package/stories/PxColorPanel.stories.js +45 -0
- package/stories/PxColorPicker.stories.js +44 -0
- package/stories/PxLabeledInput.stories.js +44 -0
- package/stories/PxSvgRender.stories.js +40 -0
- package/test/PxBaseColor.test.js +54 -0
- package/utils/color.js +50 -0
- package/utils/drag.js +17 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg viewBox="0 0 24 24"><g transform="translate(4 11)" fill="#000" fill-rule="evenodd"><circle cx="1.5" cy="1.5" r="1.5"/><circle cx="8" cy="1.5" r="1.5"/><circle cx="14.5" cy="1.5" r="1.5"/></g></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg viewBox="0 0 24 24"><g fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round" stroke="#000" stroke-width="2"><path d="M16.716 2.498a3.067 3.067 0 0 1 2.962-.793c1.059.283 2.654 1.878 2.937 2.937a3.067 3.067 0 0 1-.793 2.962l-4.152 4.152-.538-.537-9.72 9.72L1.6 22.72l1.78-5.811 9.721-9.721-.537-.538 4.152-4.152ZM11.315 5.402l7.526 7.526"/></g></svg>
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import tinycolor from 'tinycolor2';
|
|
3
|
+
import {
|
|
4
|
+
hsvaEnsureObject,
|
|
5
|
+
hsvaIsEqual,
|
|
6
|
+
hsvaToHsla,
|
|
7
|
+
hslaToStr,
|
|
8
|
+
hsvaContrastColor,
|
|
9
|
+
} from '../utils/color.js';
|
|
10
|
+
|
|
11
|
+
const TINY_FORMATS = ['rgb', 'prgb', 'hex6', 'hex3', 'hex8', 'name', 'hsl', 'hsv'];
|
|
12
|
+
|
|
13
|
+
export default {
|
|
14
|
+
name: 'px-color-picker',
|
|
15
|
+
|
|
16
|
+
props: {
|
|
17
|
+
/**
|
|
18
|
+
* The color value.
|
|
19
|
+
*/
|
|
20
|
+
color: { default: '#FF0000' },
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The format of the color value (used for emitting events, v-model, and the default format
|
|
24
|
+
* for getColor). Allowed values are the same as those available from the tinycolor library
|
|
25
|
+
* (rgb, prgb, hex6, hex3, hex8, name, hsl, hsv) as well as 'auto' (which will attempt to
|
|
26
|
+
* use the same format as the last value set with the color prop) and 'tiny' (which will
|
|
27
|
+
* return a tinycolor object) and 'hsva' which will return hsva values as an object. The
|
|
28
|
+
* format has the advantage of not requiring a tinycolor object to be created when the color
|
|
29
|
+
* changes.
|
|
30
|
+
*
|
|
31
|
+
* @values rgb, prgb, hex6, hex3, hex8, name, hsl, hsv, auto, tiny
|
|
32
|
+
*/
|
|
33
|
+
format: { type: String, default: 'hsva' },
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Whether alpha colors are enabled.
|
|
37
|
+
*/
|
|
38
|
+
alphaEnabled: { type: Boolean, default: false },
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
data() {
|
|
42
|
+
return { autoFormat: this.format, h: 0, s: 0, v: 0, a: 1 };
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
computed: {
|
|
46
|
+
hsva() {
|
|
47
|
+
return { h: this.h, s: this.s, v: this.v, a: this.alphaEnabled ? this.a : 1 };
|
|
48
|
+
},
|
|
49
|
+
hsla() {
|
|
50
|
+
return hsvaToHsla(this.hsva);
|
|
51
|
+
},
|
|
52
|
+
hslStr() {
|
|
53
|
+
return hslaToStr(this.hsla, false);
|
|
54
|
+
},
|
|
55
|
+
hslaStr() {
|
|
56
|
+
return hslaToStr(this.hsla, true);
|
|
57
|
+
},
|
|
58
|
+
hslaTransparentStr() {
|
|
59
|
+
return hslaToStr({ ...this.hsla, a: 0 }, true);
|
|
60
|
+
},
|
|
61
|
+
hueStr() {
|
|
62
|
+
return `hsl(${this.h}, 100%, 50%)`;
|
|
63
|
+
},
|
|
64
|
+
contrastStr() {
|
|
65
|
+
return hsvaContrastColor(this.hsva);
|
|
66
|
+
},
|
|
67
|
+
eyedropperSupported() {
|
|
68
|
+
return window?.EyeDropper;
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
watch: {
|
|
73
|
+
color(nv) {
|
|
74
|
+
// update colorValue when color changes
|
|
75
|
+
this.setColor(nv);
|
|
76
|
+
},
|
|
77
|
+
hsla() {
|
|
78
|
+
// emit change when this changes as a proxy for changes to hsva as individual props
|
|
79
|
+
this.emitChange();
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
mounted() {
|
|
84
|
+
if (this.color) {
|
|
85
|
+
this.setColor(this.color);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (this.format) {
|
|
89
|
+
if (!TINY_FORMATS.includes(this.format)) {
|
|
90
|
+
console.warn(
|
|
91
|
+
`[PxBaseColor] Invalid format: "${this.format}". See comments in PxBaseColor.vue`,
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
methods: {
|
|
97
|
+
setColor(strOrHsva) {
|
|
98
|
+
if (typeof strOrHsva === 'string') {
|
|
99
|
+
this.setColorStr(strOrHsva);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (typeof strOrHsva === 'object') {
|
|
103
|
+
this.setColorHsva(strOrHsva);
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
setColorStr(str) {
|
|
108
|
+
// this is for efficiency but also to prevent color creep as we convert between formats
|
|
109
|
+
if (this.lastEmit == str) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const tiny = tinycolor(str);
|
|
114
|
+
this.autoFormat = tiny.getFormat();
|
|
115
|
+
|
|
116
|
+
const hsva = tiny.toHsv();
|
|
117
|
+
this.setColorHsva(hsva);
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
setColorHsva(hsva) {
|
|
121
|
+
if (this.isCurrentHsva(hsva)) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
this.h = hsva.h;
|
|
126
|
+
this.s = hsva.s;
|
|
127
|
+
this.v = hsva.v;
|
|
128
|
+
this.a = hsva.a;
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
emitChange() {
|
|
132
|
+
const clr = this.getColor(this.format);
|
|
133
|
+
|
|
134
|
+
this.lastEmit = clr;
|
|
135
|
+
|
|
136
|
+
this.$emit('update:color', clr);
|
|
137
|
+
this.$emit('change', clr);
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
getColor(format = null) {
|
|
141
|
+
format = format || this.format;
|
|
142
|
+
|
|
143
|
+
if (format == 'auto') {
|
|
144
|
+
format = this.autoFormat;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
let clr = { h: this.h, s: this.s, v: this.v, a: this.a };
|
|
148
|
+
|
|
149
|
+
if (format == 'hsva') {
|
|
150
|
+
return clr;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (format && [...TINY_FORMATS, 'tiny'].includes(format)) {
|
|
154
|
+
const tiny = tinycolor(clr);
|
|
155
|
+
|
|
156
|
+
if (format == 'tiny') {
|
|
157
|
+
clr = tiny;
|
|
158
|
+
} else {
|
|
159
|
+
clr = tiny.toString(format);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return clr;
|
|
164
|
+
},
|
|
165
|
+
|
|
166
|
+
colorStrToHsva(str) {
|
|
167
|
+
const tiny = tinycolor(str);
|
|
168
|
+
return tiny.toHsv();
|
|
169
|
+
},
|
|
170
|
+
|
|
171
|
+
isCurrentHsva(hOrObj, s, v, a) {
|
|
172
|
+
const hsvaCompare = hsvaEnsureObject(hOrObj, s, v, a);
|
|
173
|
+
return hsvaIsEqual(this.hsva, hsvaCompare);
|
|
174
|
+
},
|
|
175
|
+
|
|
176
|
+
setColorFromEyedropper() {
|
|
177
|
+
if (this.eyedropperSupported) {
|
|
178
|
+
const eyeDropper = new EyeDropper();
|
|
179
|
+
eyeDropper.open().then(result => {
|
|
180
|
+
this.setColorStr(result.sRGBHex);
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
</script>
|
package/bases/PxBaseItems.vue
CHANGED
|
@@ -94,11 +94,11 @@ export default {
|
|
|
94
94
|
},
|
|
95
95
|
|
|
96
96
|
isValidItem(item) {
|
|
97
|
-
return !!this.items
|
|
97
|
+
return !!this.items?.includes(item);
|
|
98
98
|
},
|
|
99
99
|
|
|
100
100
|
isValidItemKey(key) {
|
|
101
|
-
return !!this.itemKeys
|
|
101
|
+
return !!this.itemKeys?.includes(key);
|
|
102
102
|
},
|
|
103
103
|
},
|
|
104
104
|
};
|
package/bases/PxBaseSvg.vue
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script>
|
|
2
|
-
import
|
|
2
|
+
import isServer from '../utils/isServer';
|
|
3
3
|
import { cssEmFallback } from '../utils/cssStr.js';
|
|
4
4
|
|
|
5
5
|
export default {
|
|
@@ -133,24 +133,22 @@ export default {
|
|
|
133
133
|
calcStyle() {
|
|
134
134
|
return null;
|
|
135
135
|
},
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
if (isSvgStr) {
|
|
144
|
-
const svgComponent = {
|
|
145
|
-
template: this.transformSvgString(nv),
|
|
146
|
-
};
|
|
147
|
-
this.svgComponent = markRaw(svgComponent);
|
|
148
|
-
}
|
|
149
|
-
},
|
|
136
|
+
isSvgString() {
|
|
137
|
+
return this.src && this.src.includes('<svg');
|
|
138
|
+
},
|
|
139
|
+
svgString() {
|
|
140
|
+
if (this.isSvgString) {
|
|
141
|
+
return this.transformSvgString(this.src);
|
|
142
|
+
}
|
|
150
143
|
},
|
|
151
144
|
},
|
|
145
|
+
|
|
152
146
|
methods: {
|
|
153
147
|
transformSvgString(svg) {
|
|
148
|
+
if (isServer() || typeof DOMParser === undefined) {
|
|
149
|
+
return svg;
|
|
150
|
+
}
|
|
151
|
+
|
|
154
152
|
const parser = new DOMParser();
|
|
155
153
|
const svgElement = parser.parseFromString(svg, 'image/svg+xml').querySelector('svg');
|
|
156
154
|
const transformed = this.transform(svgElement);
|
|
@@ -163,33 +161,31 @@ export default {
|
|
|
163
161
|
return;
|
|
164
162
|
}
|
|
165
163
|
|
|
166
|
-
if (
|
|
167
|
-
|
|
164
|
+
if (svg.viewBox) {
|
|
165
|
+
// only a valid viewbox if it has both width/height
|
|
166
|
+
let viewBox =
|
|
167
|
+
svg.viewBox.baseVal.width && svg.viewBox.baseVal.height
|
|
168
|
+
? svg.viewBox.baseVal
|
|
169
|
+
: null;
|
|
170
|
+
|
|
171
|
+
// we only support width/height set in px (unitType = 1 or 5)
|
|
172
|
+
let width =
|
|
173
|
+
svg.width.baseVal.unitType == 1 || svg.width.baseVal.unitType == 5
|
|
174
|
+
? parseFloat(svg.width.baseVal.value)
|
|
175
|
+
: viewBox.width;
|
|
176
|
+
let height =
|
|
177
|
+
svg.height.baseVal.unitType == 1 || svg.height.baseVal.unitType == 5
|
|
178
|
+
? parseFloat(svg.height.baseVal.value)
|
|
179
|
+
: viewBox.height;
|
|
180
|
+
|
|
181
|
+
// save as data values
|
|
182
|
+
this.svgViewBox = viewBox
|
|
183
|
+
? `${viewBox.x} ${viewBox.y} ${viewBox.width} ${viewBox.height}`
|
|
184
|
+
: `0 0 ${width} ${height}`;
|
|
185
|
+
this.svgWidth = width;
|
|
186
|
+
this.svgHeight = height;
|
|
168
187
|
}
|
|
169
188
|
|
|
170
|
-
// only a valid viewbox if it has both width/height
|
|
171
|
-
let viewBox =
|
|
172
|
-
svg.viewBox.baseVal.width && svg.viewBox.baseVal.height
|
|
173
|
-
? svg.viewBox.baseVal
|
|
174
|
-
: null;
|
|
175
|
-
|
|
176
|
-
// we only support width/height set in px (unitType = 1 or 5)
|
|
177
|
-
let width =
|
|
178
|
-
svg.width.baseVal.unitType == 1 || svg.width.baseVal.unitType == 5
|
|
179
|
-
? parseFloat(svg.width.baseVal.value)
|
|
180
|
-
: viewBox.width;
|
|
181
|
-
let height =
|
|
182
|
-
svg.height.baseVal.unitType == 1 || svg.height.baseVal.unitType == 5
|
|
183
|
-
? parseFloat(svg.height.baseVal.value)
|
|
184
|
-
: viewBox.height;
|
|
185
|
-
|
|
186
|
-
// save as data values
|
|
187
|
-
this.svgViewBox = viewBox
|
|
188
|
-
? `${viewBox.x} ${viewBox.y} ${viewBox.width} ${viewBox.height}`
|
|
189
|
-
: `0 0 ${width} ${height}`;
|
|
190
|
-
this.svgWidth = width;
|
|
191
|
-
this.svgHeight = height;
|
|
192
|
-
|
|
193
189
|
// string replace
|
|
194
190
|
if (this.replacements) {
|
|
195
191
|
let inner = svg.innerHTML;
|
package/bases/PxBaseUniqueId.vue
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
#VUE3 Todo
|
|
3
|
-
-->
|
|
4
|
-
|
|
5
1
|
<script>
|
|
6
|
-
// TODO: this is currently not compatible with SSR
|
|
7
|
-
|
|
8
2
|
export default {
|
|
3
|
+
beforeCreate() {
|
|
4
|
+
const id = typeof useId === 'function' ? useId() : Math.floor(Math.random() * 0x10000);
|
|
5
|
+
this.$__uniqueId = id;
|
|
6
|
+
},
|
|
9
7
|
methods: {
|
|
10
8
|
/**
|
|
11
9
|
* Get a unique id derived from vue's unique component identifier (this.$.uid) for use when
|
|
@@ -20,8 +18,7 @@ export default {
|
|
|
20
18
|
|
|
21
19
|
uniqueId(prefix = null) {
|
|
22
20
|
prefix = typeof prefix === 'string' ? prefix.trim() + '-' : '';
|
|
23
|
-
|
|
24
|
-
return prefix + ('000000' + Number(this.$.uid).toString(16).toLowerCase()).slice(-4);
|
|
21
|
+
return prefix + this.$__uniqueId;
|
|
25
22
|
},
|
|
26
23
|
},
|
|
27
24
|
};
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
-->
|
|
4
4
|
<template>
|
|
5
5
|
<component
|
|
6
|
-
:is="
|
|
6
|
+
:is="calcTag"
|
|
7
7
|
class="px-balanced-text"
|
|
8
8
|
:style="{ opacity: elementOpacity, color: debugColor, textAlign }"
|
|
9
9
|
>
|
|
@@ -11,13 +11,13 @@
|
|
|
11
11
|
<component
|
|
12
12
|
v-if="hasHtml"
|
|
13
13
|
:class="[bem('inner', { html: true }), innerClass]"
|
|
14
|
-
:is="
|
|
14
|
+
:is="calcInnerTag"
|
|
15
15
|
ref="text"
|
|
16
16
|
v-html="html"
|
|
17
17
|
/>
|
|
18
18
|
|
|
19
19
|
<!-- slot -->
|
|
20
|
-
<component v-else :class="[bem('inner'), innerClass]" :is="
|
|
20
|
+
<component v-else :class="[bem('inner'), innerClass]" :is="calcInnerTag" ref="text">
|
|
21
21
|
<slot>
|
|
22
22
|
{{ text }}
|
|
23
23
|
</slot>
|
|
@@ -37,12 +37,12 @@ export default {
|
|
|
37
37
|
/**
|
|
38
38
|
* The outer tag element
|
|
39
39
|
*/
|
|
40
|
-
tag: { type: String, default:
|
|
40
|
+
tag: { type: String, default: null },
|
|
41
41
|
|
|
42
42
|
/**
|
|
43
43
|
* The inner tag element
|
|
44
44
|
*/
|
|
45
|
-
innerTag: { type: String, default:
|
|
45
|
+
innerTag: { type: String, default: null },
|
|
46
46
|
|
|
47
47
|
// the inner tag element
|
|
48
48
|
innerClass: { type: String, default: null },
|
|
@@ -83,6 +83,14 @@ export default {
|
|
|
83
83
|
};
|
|
84
84
|
},
|
|
85
85
|
computed: {
|
|
86
|
+
calcTag() {
|
|
87
|
+
const defaultTag = this.hasHtml ? 'div' : 'p';
|
|
88
|
+
return this.tag || defaultTag;
|
|
89
|
+
},
|
|
90
|
+
calcInnerTag() {
|
|
91
|
+
const defaultTag = this.hasHtml ? 'div' : 'span';
|
|
92
|
+
return this.innerTag || defaultTag;
|
|
93
|
+
},
|
|
86
94
|
hasHtml() {
|
|
87
95
|
return !!this.htmlValue && !this.text;
|
|
88
96
|
},
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div :class="bem()">
|
|
3
|
+
<pre
|
|
4
|
+
v-if="loaded"
|
|
5
|
+
class="px-code-sample"
|
|
6
|
+
:class="lang"
|
|
7
|
+
><code v-html="highlightedCode"></code></pre>
|
|
8
|
+
<PxCopyText
|
|
9
|
+
v-if="showCopyButton"
|
|
10
|
+
:class="bem('copy-button')"
|
|
11
|
+
:text="code"
|
|
12
|
+
:showText="false"
|
|
13
|
+
></PxCopyText>
|
|
14
|
+
</div>
|
|
15
|
+
</template>
|
|
16
|
+
|
|
17
|
+
<script>
|
|
18
|
+
import hljs from 'highlight.js/lib/core';
|
|
19
|
+
import javascript from 'highlight.js/lib/languages/javascript';
|
|
20
|
+
import xml from 'highlight.js/lib/languages/xml';
|
|
21
|
+
import css from 'highlight.js/lib/languages/css';
|
|
22
|
+
import PxCopyText from './PxCopyText.vue';
|
|
23
|
+
|
|
24
|
+
export default {
|
|
25
|
+
name: 'px-code-sample',
|
|
26
|
+
components: { PxCopyText },
|
|
27
|
+
// mixins: [ Mixin ],
|
|
28
|
+
props: {
|
|
29
|
+
code: { type: String, default: '' },
|
|
30
|
+
lang: { type: String, default: null },
|
|
31
|
+
showCopyButton: { type: Boolean, default: true },
|
|
32
|
+
},
|
|
33
|
+
data() {
|
|
34
|
+
return { loaded: false };
|
|
35
|
+
},
|
|
36
|
+
computed: {
|
|
37
|
+
highlightedCode() {
|
|
38
|
+
if (this.loaded) {
|
|
39
|
+
// remove trailing newline and spaces
|
|
40
|
+
|
|
41
|
+
const code = this.code.replace(/^\s+$/, '').replace(/^[\s\uFEFF\xA0]+/, '');
|
|
42
|
+
|
|
43
|
+
if (code) {
|
|
44
|
+
if (this.lang) {
|
|
45
|
+
return hljs.highlight(code, { language: this.lang }).value;
|
|
46
|
+
} else {
|
|
47
|
+
return hljs.highlightAuto(code).value;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return '';
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
// watch: {},
|
|
55
|
+
mounted() {
|
|
56
|
+
hljs.registerLanguage('javascript', javascript);
|
|
57
|
+
hljs.registerLanguage('xml', xml);
|
|
58
|
+
hljs.registerLanguage('css', css);
|
|
59
|
+
|
|
60
|
+
setTimeout(() => {
|
|
61
|
+
this.loaded = true;
|
|
62
|
+
});
|
|
63
|
+
},
|
|
64
|
+
// methods: {},
|
|
65
|
+
};
|
|
66
|
+
</script>
|
|
67
|
+
|
|
68
|
+
<style lang="scss">
|
|
69
|
+
@use '../styles/px.scss' as *;
|
|
70
|
+
@use '@thinkpixellab-public/px-styles/src/modules/highlightjs.scss' as *;
|
|
71
|
+
.px-code-sample {
|
|
72
|
+
position: relative;
|
|
73
|
+
padding: 1em;
|
|
74
|
+
border-radius: 0.5em;
|
|
75
|
+
|
|
76
|
+
@include highlightjs();
|
|
77
|
+
pre,
|
|
78
|
+
code {
|
|
79
|
+
padding: 0;
|
|
80
|
+
margin: 0;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
&__copy-button {
|
|
84
|
+
position: absolute;
|
|
85
|
+
top: 1em;
|
|
86
|
+
right: 1em;
|
|
87
|
+
color: white;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
</style>
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Describe this component...
|
|
3
|
+
-->
|
|
4
|
+
<template>
|
|
5
|
+
<div :class="bem()">
|
|
6
|
+
<PxColorPaletteButton
|
|
7
|
+
v-for="(color, index) in calcPalette"
|
|
8
|
+
:key="index"
|
|
9
|
+
:hsva="color.hsva"
|
|
10
|
+
:selected="!(pickerActive && isCurrentHsva(pickerHsva)) && isCurrentHsva(color.hsva)"
|
|
11
|
+
@click="onPaletteClick(color)"
|
|
12
|
+
></PxColorPaletteButton>
|
|
13
|
+
<PxColorPicker
|
|
14
|
+
v-if="showPicker"
|
|
15
|
+
v-bind="calcPickerAttrs"
|
|
16
|
+
:color="pickerHsva"
|
|
17
|
+
:selected="pickerActive && isCurrentHsva(pickerHsva)"
|
|
18
|
+
:pending="pickerPending"
|
|
19
|
+
@click="onPickerClick"
|
|
20
|
+
@change="onPickerChange"
|
|
21
|
+
/>
|
|
22
|
+
</div>
|
|
23
|
+
</template>
|
|
24
|
+
|
|
25
|
+
<script>
|
|
26
|
+
import PxBaseColor from '../bases/PxBaseColor.vue';
|
|
27
|
+
import tinycolor from 'tinycolor2';
|
|
28
|
+
import PxColorPaletteButton from './PxColorPaletteButton.vue';
|
|
29
|
+
import { PxColorPicker, PxColorPickerPropsFilter } from './PxColorPicker.vue';
|
|
30
|
+
|
|
31
|
+
export default {
|
|
32
|
+
name: 'px-color-palette',
|
|
33
|
+
components: { PxColorPaletteButton, PxColorPicker },
|
|
34
|
+
mixins: [PxBaseColor],
|
|
35
|
+
props: {
|
|
36
|
+
/**
|
|
37
|
+
* Array of colors to display in the palette
|
|
38
|
+
*/
|
|
39
|
+
palette: { type: Array, default: null },
|
|
40
|
+
|
|
41
|
+
// none | picker | eyedropper | eyedropper-only
|
|
42
|
+
pickerMode: { type: String, default: 'none' },
|
|
43
|
+
pickerAttrs: { type: Object, default: () => {} },
|
|
44
|
+
pickerInitColor: { type: String, default: 'blue' },
|
|
45
|
+
pickerInitSelected: { type: Boolean, default: false },
|
|
46
|
+
},
|
|
47
|
+
data() {
|
|
48
|
+
return {
|
|
49
|
+
pickerActive: this.pickerInitSelected,
|
|
50
|
+
pickerPending: true,
|
|
51
|
+
pickerHsva: this.colorStrToHsva(this.pickerInitColor),
|
|
52
|
+
};
|
|
53
|
+
},
|
|
54
|
+
computed: {
|
|
55
|
+
calcPalette() {
|
|
56
|
+
return this.palette.map(color => {
|
|
57
|
+
const tiny = tinycolor(color);
|
|
58
|
+
return {
|
|
59
|
+
hsva: tiny.toHsv(),
|
|
60
|
+
css: tiny.toString(this.alphaEnabled ? 'hex6' : 'rgb'),
|
|
61
|
+
};
|
|
62
|
+
});
|
|
63
|
+
},
|
|
64
|
+
calcPickerAttrs() {
|
|
65
|
+
const eyedropperMode = {
|
|
66
|
+
none: 'none',
|
|
67
|
+
picker: 'none',
|
|
68
|
+
eyedropper: 'popup',
|
|
69
|
+
'eyedropper-only': 'only',
|
|
70
|
+
}[this.pickerMode];
|
|
71
|
+
|
|
72
|
+
const iconFallback = {
|
|
73
|
+
none: null,
|
|
74
|
+
picker: 'ellipsis',
|
|
75
|
+
eyedropper: 'ellipsis',
|
|
76
|
+
'eyedropper-only': 'eyedropper',
|
|
77
|
+
}[this.pickerMode];
|
|
78
|
+
|
|
79
|
+
return PxColorPickerPropsFilter({
|
|
80
|
+
iconFallback,
|
|
81
|
+
eyedropperMode,
|
|
82
|
+
alphaEnabled: this.alphaEnabled,
|
|
83
|
+
...this.pickerAttrs,
|
|
84
|
+
});
|
|
85
|
+
},
|
|
86
|
+
showPicker() {
|
|
87
|
+
return this.pickerMode != 'none';
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
// watch: {},
|
|
91
|
+
mounted() {},
|
|
92
|
+
methods: {
|
|
93
|
+
onPaletteClick(color) {
|
|
94
|
+
this.pickerActive = false;
|
|
95
|
+
this.setColorHsva(color.hsva);
|
|
96
|
+
},
|
|
97
|
+
onPickerClick() {
|
|
98
|
+
this.pickerActive = true;
|
|
99
|
+
},
|
|
100
|
+
onPickerChange(e) {
|
|
101
|
+
if (this.pickerActive) {
|
|
102
|
+
this.pickerPending = false;
|
|
103
|
+
this.pickerHsva = e;
|
|
104
|
+
this.setColorHsva(e);
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
</script>
|
|
110
|
+
|
|
111
|
+
<style lang="scss">
|
|
112
|
+
@use '../styles/px.scss' as *;
|
|
113
|
+
|
|
114
|
+
.px-color-palette {
|
|
115
|
+
display: flex;
|
|
116
|
+
gap: 0.5em;
|
|
117
|
+
}
|
|
118
|
+
</style>
|