|
|
@@ -0,0 +1,239 @@
|
|
|
+<template>
|
|
|
+ <div class="color-picker" @contextmenu.prevent>
|
|
|
+ <div class="picker-saturation-wrap">
|
|
|
+ <Saturation v-model="color" />
|
|
|
+ </div>
|
|
|
+ <div class="picker-controls">
|
|
|
+ <div class="picker-color-wrap">
|
|
|
+ <div class="picker-current-color" :style="{ background: currentColor }"></div>
|
|
|
+ <Checkboard />
|
|
|
+ </div>
|
|
|
+ <div class="picker-sliders">
|
|
|
+ <div class="picker-hue-wrap"><Hue v-model="color" /></div>
|
|
|
+ <div class="picker-alpha-wrap"><Alpha v-model="color" /></div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="picker-field"><EditableInput v-model="color" /></div>
|
|
|
+
|
|
|
+ <div class="picker-presets">
|
|
|
+ <div
|
|
|
+ class="picker-presets-color"
|
|
|
+ v-for="c in themeColors"
|
|
|
+ :key="c"
|
|
|
+ :style="{background: c}"
|
|
|
+ @click="selectPresetColor(c)"
|
|
|
+ ></div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="picker-gradient-presets">
|
|
|
+ <div
|
|
|
+ class="picker-gradient-col"
|
|
|
+ v-for="(col, index) in presetColors"
|
|
|
+ :key="index"
|
|
|
+ >
|
|
|
+ <div class="picker-gradient-color"
|
|
|
+ v-for="c in col"
|
|
|
+ :key="c"
|
|
|
+ :style="{background: c}"
|
|
|
+ @click="selectPresetColor(c)"
|
|
|
+ ></div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="picker-presets">
|
|
|
+ <div
|
|
|
+ v-for="c in standardColors"
|
|
|
+ :key="c"
|
|
|
+ class="picker-presets-color"
|
|
|
+ :style="{ background: c }"
|
|
|
+ @click="selectPresetColor(c)"
|
|
|
+ ></div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script lang="ts">
|
|
|
+import { computed, defineComponent } from 'vue'
|
|
|
+import tinycolor, { ColorFormats } from 'tinycolor2'
|
|
|
+
|
|
|
+import Alpha from './Alpha.vue'
|
|
|
+import Checkboard from './Checkboard.vue'
|
|
|
+import Hue from './Hue.vue'
|
|
|
+import Saturation from './Saturation.vue'
|
|
|
+import EditableInput from './EditableInput.vue'
|
|
|
+
|
|
|
+const presetColorConfig = [
|
|
|
+ ['#7f7f7f', '#f2f2f2'],
|
|
|
+ ['#0d0d0d', '#808080'],
|
|
|
+ ['#1c1a10', '#ddd8c3'],
|
|
|
+ ['#0e243d', '#c6d9f0'],
|
|
|
+ ['#233f5e', '#dae5f0'],
|
|
|
+ ['#632623', '#f2dbdb'],
|
|
|
+ ['#4d602c', '#eaf1de'],
|
|
|
+ ['#3f3150', '#e6e0ec'],
|
|
|
+ ['#1e5867', '#d9eef3'],
|
|
|
+ ['#99490f', '#fee9da'],
|
|
|
+]
|
|
|
+
|
|
|
+const gradient = (startColor: string, endColor: string, step: number) => {
|
|
|
+ const _startColor = tinycolor(startColor).toRgb()
|
|
|
+ const _endColor = tinycolor(endColor).toRgb()
|
|
|
+
|
|
|
+ const rStep = (_endColor.r - _startColor.r) / step
|
|
|
+ const gStep = (_endColor.g - _startColor.g) / step
|
|
|
+ const bStep = (_endColor.b - _startColor.b) / step
|
|
|
+ const gradientColorArr = []
|
|
|
+
|
|
|
+ for(let i = 0; i < step; i++) {
|
|
|
+ const gradientColor = tinycolor({
|
|
|
+ r: _startColor.r + rStep * i,
|
|
|
+ g: _startColor.g + gStep * i,
|
|
|
+ b: _startColor.b + bStep * i,
|
|
|
+ }).toRgbString()
|
|
|
+ gradientColorArr.push(gradientColor)
|
|
|
+ }
|
|
|
+ return gradientColorArr
|
|
|
+}
|
|
|
+
|
|
|
+const getPresetColors = () => {
|
|
|
+ const presetColors = []
|
|
|
+ for(const color of presetColorConfig) {
|
|
|
+ presetColors.push(gradient(color[1], color[0], 5))
|
|
|
+ }
|
|
|
+ return presetColors
|
|
|
+}
|
|
|
+
|
|
|
+export default defineComponent({
|
|
|
+ name: 'color-picker',
|
|
|
+ components: {
|
|
|
+ Alpha,
|
|
|
+ Checkboard,
|
|
|
+ Hue,
|
|
|
+ Saturation,
|
|
|
+ EditableInput,
|
|
|
+ },
|
|
|
+ props: {
|
|
|
+ modelValue: {
|
|
|
+ type: String,
|
|
|
+ default: '#e86b99',
|
|
|
+ },
|
|
|
+ },
|
|
|
+ setup(props, { emit }) {
|
|
|
+ const color = computed({
|
|
|
+ get() {
|
|
|
+ return tinycolor(props.modelValue).toRgb()
|
|
|
+ },
|
|
|
+ set(rgba: ColorFormats.RGBA) {
|
|
|
+ const rgbaString = `rgba(${[rgba.r, rgba.g, rgba.b, rgba.a].join(',')})`
|
|
|
+ emit('update:modelValue', rgbaString)
|
|
|
+ },
|
|
|
+ })
|
|
|
+
|
|
|
+ const themeColors = ['#000000', '#ffffff', '#eeece1', '#1e497b', '#4e81bb', '#e2534d', '#9aba60', '#8165a0', '#47acc5', '#f9974c']
|
|
|
+ const standardColors = ['#c21401', '#ff1e02', '#ffc12a', '#ffff3a', '#90cf5b', '#00af57', '#00afee', '#0071be', '#00215f', '#72349d']
|
|
|
+ const presetColors = getPresetColors()
|
|
|
+
|
|
|
+ const currentColor = computed(() => {
|
|
|
+ return `rgba(${[color.value.r, color.value.g, color.value.b, color.value.a].join(',')})`
|
|
|
+ })
|
|
|
+
|
|
|
+ const selectPresetColor = (colorString: string) => {
|
|
|
+ emit('update:modelValue', colorString)
|
|
|
+ }
|
|
|
+
|
|
|
+ return {
|
|
|
+ themeColors,
|
|
|
+ standardColors,
|
|
|
+ presetColors,
|
|
|
+ color,
|
|
|
+ currentColor,
|
|
|
+ selectPresetColor,
|
|
|
+ }
|
|
|
+ },
|
|
|
+})
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+.color-picker {
|
|
|
+ position: relative;
|
|
|
+ width: 240px;
|
|
|
+ background: #fff;
|
|
|
+ user-select: none;
|
|
|
+}
|
|
|
+.picker-saturation-wrap {
|
|
|
+ width: 100%;
|
|
|
+ padding-bottom: 50%;
|
|
|
+ position: relative;
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+.picker-controls {
|
|
|
+ display: flex;
|
|
|
+}
|
|
|
+.picker-sliders {
|
|
|
+ padding: 4px 0;
|
|
|
+ flex: 1;
|
|
|
+}
|
|
|
+.picker-hue-wrap {
|
|
|
+ position: relative;
|
|
|
+ height: 10px;
|
|
|
+}
|
|
|
+.picker-alpha-wrap {
|
|
|
+ position: relative;
|
|
|
+ height: 10px;
|
|
|
+ margin-top: 4px;
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+.picker-color-wrap {
|
|
|
+ width: 24px;
|
|
|
+ height: 24px;
|
|
|
+ position: relative;
|
|
|
+ margin-top: 4px;
|
|
|
+ margin-right: 4px;
|
|
|
+
|
|
|
+ .checkerboard {
|
|
|
+ background-size: auto;
|
|
|
+ }
|
|
|
+}
|
|
|
+.picker-current-color {
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ right: 0;
|
|
|
+ bottom: 0;
|
|
|
+ z-index: 2;
|
|
|
+}
|
|
|
+
|
|
|
+.picker-field {
|
|
|
+ margin-bottom: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+.picker-presets {
|
|
|
+ @include grid-layout-wrapper();
|
|
|
+}
|
|
|
+.picker-presets-color {
|
|
|
+ @include grid-layout-item(10, 7%);
|
|
|
+
|
|
|
+ height: 0;
|
|
|
+ padding-bottom: 7%;
|
|
|
+ flex-shrink: 0;
|
|
|
+ position: relative;
|
|
|
+ cursor: pointer;
|
|
|
+}
|
|
|
+.picker-gradient-presets {
|
|
|
+ @include grid-layout-wrapper();
|
|
|
+}
|
|
|
+.picker-gradient-col {
|
|
|
+ @include grid-layout-item(10, 7%);
|
|
|
+
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+}
|
|
|
+.picker-gradient-color {
|
|
|
+ width: 100%;
|
|
|
+ height: 0;
|
|
|
+ padding-bottom: 100%;
|
|
|
+ position: relative;
|
|
|
+ cursor: pointer;
|
|
|
+}
|
|
|
+</style>
|