Skip to content

Commit f932d10

Browse files
committed
update to v2, implement teleport, update class names, update dom structure
1 parent 81a6939 commit f932d10

File tree

9 files changed

+492
-389
lines changed

9 files changed

+492
-389
lines changed

.github/README.md

Lines changed: 200 additions & 150 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,26 +35,11 @@ npm i vue-color-input
3535
### Import
3636
```javascript
3737
import ColorInput from 'vue-color-input'
38-
39-
40-
// install it with use()
41-
42-
app.use(ColorInput)
43-
44-
// OR register component globally
45-
46-
app.component('ColorInput', ColorInput)
47-
48-
// OR locally
49-
50-
export.default {
51-
components: { ColorInput }
52-
}
5338
```
5439

5540
### Use
5641
```xml
57-
<color-input v-model="color" />
42+
<ColorInput v-model="color" />
5843
```
5944

6045

dev/demo-styles.css

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,21 @@
1-
.color-input.user .box {
1+
div.color-input__box {
22
width: 100px;
33
height: 100px;
4-
border-radius: 50px;
54
}
6-
.color-input.user .box.active {
5+
div.color-input__box--active {
76
background: #0f0f0f;
87
}
9-
.color-input.user .box.active .inner {
10-
transform: scale(.9) rotate(90deg);
8+
div.color-input__box-inner--active {
9+
transform: scale(.88);
1110
}
11+
div.color-input__box--disabled {}
1212

13-
.picker-popup-enter-from,
14-
.picker-popup-leave-to {}
15-
.picker-popup-enter-active,
16-
.picker-popup-leave-active {}
13+
div.color-input__popup {}
14+
div.color-input__popup .slider {}
15+
div.color-input__popup .slider-pointer {}
16+
div.color-input__popup .saturation-pointer {}
1717

18-
.picker-popup.user {}
19-
.picker-popup.user .box.disabled {}
20-
.picker-popup.user .slider {}
21-
.picker-popup.user .slider-pointer {}
22-
.picker-popup.user .saturation-pointer {}
18+
div.color-input__popup--enter-from,
19+
div.color-input__popup--leave-to {}
20+
div.color-input__popup--enter-active,
21+
div.color-input__popup--leave-active {}

dev/serve-setup.vue

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<script setup lang="ts">
2+
import ColorInput from '../src/color-input.vue'
3+
import { ref } from 'vue'
4+
5+
const color = ref('#ccc')
6+
7+
const colorInput = ref<InstanceType<typeof ColorInput>>()
8+
9+
setTimeout(() => {
10+
colorInput.value.pickStart()
11+
}, 2000)
12+
13+
setTimeout(() => {
14+
console.log(colorInput.value.active)
15+
console.log(colorInput.value.color.toRgb())
16+
}, 4000)
17+
18+
19+
</script>
20+
21+
<template>
22+
<ColorInput v-model="color" ref="colorInput" class="my-color-input" />
23+
</template>
24+
25+
<style>
26+
</style>
27+

dev/serve.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { createApp } from 'vue';
22
import Dev from './serve.vue';
3+
import DevSetup from './serve-setup.vue';
34

4-
const app = createApp(Dev);
5+
const app = createApp(DevSetup);
56
app.config.unwrapInjectedRef = true;
67
app.mount('#app');
78

8-
document.title = 'vue-color-input';
9+
document.title = 'vue-color-input';

dev/serve.vue

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
<template>
22
<div id="app">
3-
<div class="box-parent" v-if="appendToActive">
4-
HERE WILL BE THE BOX (ALWAYS <i>POSITION:RELATIVE</i>)
5-
</div>
63
<h1>vue-color-input demo</h1>
74
<h3>
8-
<a class="docsLink" href="https://github.com/gVguy/vue-color-input#vue-color-input"
5+
<a class="docs-link" href="https://github.com/gVguy/vue-color-input#vue-color-input"
96
:style="{ color: linkColor }">Docs</a>
107
</h3>
118
<div class="setup">
@@ -44,10 +41,6 @@
4441
<h3>disable-text-inputs</h3>
4542
<input type="checkbox" class="chx" v-model="disableTextInputs" @pointerdown.stop>
4643
</div>
47-
<div class="setup-block">
48-
<h3>appendTo external div</h3>
49-
<input type="checkbox" class="chx" v-model="appendToActive" @pointerdown.stop>
50-
</div>
5144
</div>
5245
<color-input
5346
:appendTo="appendToActive ? appendTo : null"
@@ -298,10 +291,10 @@
298291
@import url('https://fonts.googleapis.com/css2?family=Source+Code+Pro:wght@300&display=swap');
299292
body {
300293
background: #fbfbfb;
301-
}
302-
#app {
303294
font-family: 'Montserrat', sans-serif;
304295
font-size: 16px;
296+
}
297+
#app {
305298
display: flex;
306299
flex-direction: column;
307300
align-items: center;
@@ -311,7 +304,7 @@
311304
h1 {
312305
margin-bottom: 0;
313306
}
314-
.docsLink {
307+
.docs-link {
315308
margin-bottom: 20px;
316309
text-decoration: underline;
317310
}

src/bem.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
const ROOT_CLASS = 'color-input'
2+
export function bem(className, state = {}) {
3+
const baseClass = `${ROOT_CLASS}__${className}`
4+
const result = {}
5+
result[baseClass] = true
6+
for (const [key, value] of Object.entries(state)) {
7+
result[`${baseClass}--${key}`] = value
8+
}
9+
return result
10+
}

src/color-input.vue

Lines changed: 40 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
11
<template>
2-
<div class="color-input user" ref="root" :style="cssVars">
32
<div
4-
:class="['box', { active, disabled }]"
3+
:class="[$attrs.class, bem('box', { disabled, active })]"
54
@click.stop="pickStart"
6-
ref="box"
5+
ref="root"
76
>
8-
<div class="inner transparent">
9-
<div class="color" :style="boxColorStyles"></div>
7+
<div
8+
:class="bem('box-inner', { active })"
9+
:style="transparentPatternBg"
10+
>
11+
<div :class="bem('box-color')" :style="boxColorStyles"></div>
1012
</div>
1113
</div>
1214

13-
<Teleport :to="parent" v-if="parent">
15+
<Teleport to="body">
1416
<transition :name="transition">
1517
<color-picker
16-
class="picker-popup user"
17-
:color="this.color"
18+
:class="$attrs.class"
19+
:color="color"
1820
:position="processedPosition"
1921
:disable-alpha="processedDisableAlpha"
2022
:boxRect="boxRect"
@@ -37,14 +39,14 @@
3739
/>
3840
</transition>
3941
</Teleport>
40-
</div>
4142
</template>
4243

4344
<script>
4445
import { defineComponent } from "vue";
4546
import ColorPicker from "./components/color-picker.vue";
4647
4748
import tinycolor from "tinycolor2";
49+
import { bem } from './bem'
4850
4951
import transparentPattern from "./assets/transparent-pattern.svg";
5052
@@ -56,8 +58,9 @@ const isSameNodeRecursive = (elA, elB) => {
5658
return false;
5759
};
5860
59-
export default /*#__PURE__*/ defineComponent({
61+
export default defineComponent({
6062
name: "ColorInput",
63+
expose: ['pickStart', 'pickEnd', 'color', 'active'],
6164
props: {
6265
modelValue: [String, Object],
6366
position: {
@@ -66,7 +69,7 @@ export default /*#__PURE__*/ defineComponent({
6669
},
6770
transition: {
6871
type: String,
69-
default: "picker-popup",
72+
default: 'color-input__popup-',
7073
},
7174
disableAlpha: {
7275
type: Boolean,
@@ -81,7 +84,6 @@ export default /*#__PURE__*/ defineComponent({
8184
default: false,
8285
},
8386
format: String,
84-
appendTo: [String, HTMLElement],
8587
},
8688
emits: [
8789
"mounted",
@@ -108,7 +110,6 @@ export default /*#__PURE__*/ defineComponent({
108110
active: false,
109111
ready: false,
110112
hidePicker: false,
111-
parent: null,
112113
boxRect: {},
113114
innerBoxRect: {},
114115
textInputsFormat: "rgb",
@@ -117,6 +118,9 @@ export default /*#__PURE__*/ defineComponent({
117118
};
118119
},
119120
computed: {
121+
transparentPatternBg() {
122+
return { backgroundImage: `url(${transparentPattern})` }
123+
},
120124
boxColorStyles() {
121125
return {
122126
background: this.color.toRgbString(),
@@ -152,6 +156,13 @@ export default /*#__PURE__*/ defineComponent({
152156
position = position.split(" ");
153157
position[1] = position[1] || "center";
154158
159+
// reorder [Y, X]
160+
if (
161+
['top','bottom'].includes(position[0]) ||
162+
['right', 'left'].includes(position[1])
163+
)
164+
return position; // already correct order
165+
position.reverse();
155166
return position;
156167
},
157168
processedFormat() {
@@ -212,6 +223,7 @@ export default /*#__PURE__*/ defineComponent({
212223
},
213224
},
214225
methods: {
226+
bem,
215227
pickStart(e) {
216228
if (this.active || this.disabled) return;
217229
this.getBoxRect();
@@ -222,7 +234,8 @@ export default /*#__PURE__*/ defineComponent({
222234
this.hidePicker = true;
223235
this.$refs.picker.init();
224236
225-
document.body.addEventListener("pointerdown", this.pickEnd);
237+
document.addEventListener("pointerdown", this.pickEnd);
238+
window.addEventListener('resize', this.getBoxRect);
226239
this.$emit("pickStart");
227240
},
228241
pickEnd(e) {
@@ -231,7 +244,8 @@ export default /*#__PURE__*/ defineComponent({
231244
(e && isSameNodeRecursive(e.target, this.$refs.picker.$refs.pickerRoot))
232245
)
233246
return;
234-
document.body.removeEventListener("pointerdown", this.pickEnd);
247+
document.removeEventListener("pointerdown", this.pickEnd);
248+
window.removeEventListener('resize', this.getBoxRect)
235249
this.active = false;
236250
this.$emit("pickEnd");
237251
},
@@ -280,30 +294,14 @@ export default /*#__PURE__*/ defineComponent({
280294
}
281295
this.$emit("update:modelValue", this.output);
282296
},
283-
getParent() {
284-
let parent;
285-
if (this.appendTo) {
286-
if (typeof this.appendTo === "string") {
287-
parent = document.querySelector(this.appendTo);
288-
} else {
289-
parent = this.appendTo;
290-
}
291-
}
292-
293-
this.parent = parent || this.$refs.root;
294-
},
295297
getBoxRect() {
296-
this.boxRect = this.parent.getBoundingClientRect();
298+
this.boxRect = this.$refs.root.getBoundingClientRect();
297299
},
298300
},
299301
created() {
300302
this.init();
301-
this.cssVars = {
302-
"--transparent-pattern": "url(" + transparentPattern + ")",
303-
};
304303
},
305304
mounted() {
306-
this.getParent();
307305
this.$emit("mounted");
308306
},
309307
beforeUnmount() {
@@ -363,58 +361,35 @@ export default /*#__PURE__*/ defineComponent({
363361
position: relative;
364362
display: inline-block;
365363
366-
.box {
364+
&__box {
367365
width: 40px;
368366
height: 40px;
369367
cursor: pointer;
370368
border-radius: 20%;
371369
overflow: hidden;
372-
transition: all 0.2s, background-color 0.05s 0.15s;
373-
.inner {
370+
transition: background-color 0.05s 0.15s;
371+
&-inner {
374372
border-radius: inherit;
375373
overflow: hidden;
376-
transition: inherit;
377-
}
378-
.transparent {
374+
transition: transform .2s;
379375
@extend %fill-100;
380-
background-image: var(--transparent-pattern);
381376
background-color: #fff;
382377
background-size: 100%;
378+
&--active {
379+
transform: scale(0.9);
380+
}
383381
}
384-
.color {
382+
&-color {
385383
@extend %fill-100;
386384
}
387-
&.active {
385+
&--active {
388386
background: #fbfbfb;
389387
transition: all 0.2s, background-color 0.05s;
390-
.inner {
391-
transform: scale(0.9);
392-
}
393388
}
394-
&.disabled {
389+
&--disabled {
395390
cursor: not-allowed;
396391
}
397392
}
398393
}
399-
.picker-popup {
400-
position: absolute;
401-
z-index: 9999;
402-
width: auto;
403-
min-width: 280px;
404-
background-color: #fbfbfb;
405-
box-shadow: 0px 5px 10px rgba(15, 15, 15, 0.4);
406-
margin: 10px;
407-
user-select: none;
408-
color: #0f0f0f;
409-
}
410394
411-
.picker-popup-enter-from,
412-
.picker-popup-leave-to {
413-
transform: translateY(-10px);
414-
opacity: 0;
415-
}
416-
.picker-popup-enter-active,
417-
.picker-popup-leave-active {
418-
transition: transform 0.3s, opacity 0.3s;
419-
}
420395
</style>

0 commit comments

Comments
 (0)