Keen-UI icon indicating copy to clipboard operation
Keen-UI copied to clipboard

UiColorPicker 0.1

Open EmilMoe opened this issue 6 years ago • 0 comments

I have worked a bit on a fix that immitates Keen-UI with vue-color that's an wrapper for tinycolor2.

It relies on:

  • Bootstrap 3
  • Vue-Color https://github.com/xiaokaike/vue-color
  • TinyColor https://github.com/bgrins/TinyColor

Here's the full source of my component, it's far from perfect yet, but it has some features. I wasn't able to use UiTextbox as the popup would fail. Feel free to make a more proper implementation and hopefully add it to the Keen-UI library.

<template>
    <div>
        <div
                class="input-group color-picker"
                ref="colorpicker">
            <span
                    class="input-group-addon color-picker-container">
                <span
                        class="color-icon"
                        @click="togglePicker">
                    <i
                            class="material-icons"
                            v-if="icon">
                        {{ icon }}
                    </i>
                </span>
                <chrome-picker
                        :value="colors"
                        @input="updateFromPicker"
                        v-if="displayPicker">
                </chrome-picker>
            </span>
            <div>
                <label
                        v-if="label">
                    {{ label }}
                </label>
                <input
                        type="text"
                        :class="{
                            'form-control': true,
                            'focus': displayPicker
                        }"
                        v-model="colorValue"
                        @focus="showPicker"
                        @input="updateFromInput">
                <div
                        class="help"
                        v-if="help">
                    {{ help }}
                </div>
            </div>
        </div>
    </div>
</template>

<script>
    Vue.component('ChromePicker', require('vue-color/src/components/Chrome.vue'))

    export default {
        props: {
            /**
             * Bind color to external (preset) value.
             */
            color: {
                type: String,
                default: ''
            },
            /**
             *
             */
            label: {
                type: String,
                default: 'Color'
            },
            /**
             *
             */
            icon: {
                type: String,
                default: 'invert_colors'
            },
            /**
             *
             */
            help: {
                type: String,
                default: null
            }
        },
        data() {
            return {
                colors: {
                    hex: '#000000',
                },
                colorValue: this.color,
                displayPicker: false,
            }
        },
        /**
         *
         */
        mounted() {
            this.setColor(this.color || '#000000');
        },
        methods: {
            /**
             *
             * @param color
             */
            setColor(color) {
                this.updateColors(color);
                this.colorValue = color;
            },
            /**
             *
             * @param color
             */
            updateColors(color) {
                let rgba = color.replace(/^rgba?\(|\s+|\)$/g,'').split(','),
                    hex = '#' + ((1 << 24) + (parseInt(rgba[0]) << 16) + (parseInt(rgba[1]) << 8) + parseInt(rgba[2])).toString(16).slice(1);
                this.colors = {
                    hex: hex,
                    a: rgba[3],
                }
            },
            /**
             *
             */
            showPicker() {
                document.addEventListener('click', this.documentClick)

                this.displayPicker = true
            },
            /**
             *
             */
            hidePicker() {
                document.removeEventListener('click', this.documentClick)
                this.displayPicker = false
            },
            /**
             *
             */
            togglePicker() {
                this.displayPicker ? this.hidePicker() : this.showPicker()
            },
            /**
             *
             */
            updateFromInput() {
                this.updateColors(this.colorValue)
            },
            /**
             *
             * @param color
             */
            updateFromPicker(color) {
                this.colors = color
                this.colorValue = 'rgba(' + color.rgba.r + ', ' + color.rgba.g + ', ' + color.rgba.b + ', ' + color.rgba.a + ')'
            },
            /**
             *
             * @param e
             */
            documentClick(e) {
                console.log('document click')

                let el = this.$refs.colorpicker,
                    target = e.target;

                if (el !== target && !el.contains(target))
                    this.hidePicker()
            }
        },
        watch: {
            /**
             *
             * @param val
             */
            colorValue(val) {
                if (val) {
                    this.updateColors(val)
                    this.$emit('input', val)
                }
            }
        }
    }
</script>

<style lang="scss" scoped>
    .input-group {
        margin-bottom: 1rem;
    }

    label {
        font-weight: bold;
        color: rgba(0, 0, 0, 0.54);
        font-size: 0.9375rem;
        line-height: normal;
        margin-left: 0.75rem;
    }

    .input-group-addon {
        border-radius: 0;
        border: none;
        background-color: transparent;
        padding: 0;
        margin-bottom: 1rem;

        i {
            font-size: 1.5rem;
            color: rgba(0, 0, 0, 0.54);
            padding-top: 1.5rem;
        }
    }

    .form-control {
        border: none;
        border-bottom: 1px solid #ececec;
        border-radius: 0;
        box-shadow: none;
        padding: 0;
        font-family: Roboto, -apple-system, BlinkMacSystemFont, "Segoe UI", Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
        margin-left: 0.75rem;
        font-size: 1rem;
        color: rgba(0, 0, 0, 0.87);
        height: 2rem;

        &:focus,
        &.focus {
            outline: none;
            border-bottom: 2px solid #2196f3;
        }
    }

    .vc-chrome {
        position: absolute;
        top: 45px;
        left: 0;
        z-index: 9;
        margin-left: 2.3rem;
    }

    .help {
        padding-top: 0.25rem;
        margin-left: 0.75rem;
        color: rgba(0, 0, 0, 0.54);
        font-size: 0.875rem;
        line-height: 1.2;
    }
</style>

EmilMoe avatar Apr 10 '18 14:04 EmilMoe