"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.colorPickerThemeInjectionKey = exports.colorPickerPanelProps = void 0;
const vue_1 = require("vue");
const seemly_1 = require("seemly");
const vooks_1 = require("vooks");
const vueuc_1 = require("vueuc");
const vdirs_1 = require("vdirs");
const styles_1 = require("../styles");
const _mixins_1 = require("../../_mixins");
const _utils_1 = require("../../_utils");
const HueSlider_1 = require("./HueSlider");
const AlphaSlider_1 = require("./AlphaSlider");
const Pallete_1 = require("./Pallete");
const ColorInput_1 = require("./ColorInput");
const ColorPickerTrigger_1 = require("./ColorPickerTrigger");
const utils_1 = require("./utils");
const index_cssr_1 = require("./styles/index.cssr");
const button_1 = require("../../button");
const ColorPickerSwatches_1 = require("./ColorPickerSwatches");
exports.colorPickerPanelProps = Object.assign(Object.assign({}, _mixins_1.useTheme.props), { value: String, show: {
        type: Boolean,
        default: undefined
    }, defaultShow: {
        type: Boolean,
        default: false
    }, defaultValue: String, modes: {
        type: Array,
        // no hsva by default since browser doesn't support it
        default: ['rgb', 'hex', 'hsl']
    }, to: _utils_1.useAdjustedTo.propTo, showAlpha: {
        type: Boolean,
        default: true
    }, swatches: Array, actions: {
        type: Array,
        default: null
    }, internalActions: Array, size: String, onComplete: Function, 'onUpdate:show': [Function, Array], onUpdateShow: [Function, Array], 'onUpdate:value': [Function, Array], onUpdateValue: [Function, Array] });
exports.colorPickerThemeInjectionKey = Symbol('colorPickerThemeInjection');
exports.default = (0, vue_1.defineComponent)({
    name: 'ColorPicker',
    props: exports.colorPickerPanelProps,
    setup(props, { slots }) {
        const selfRef = (0, vue_1.ref)(null);
        let upcomingValue = null;
        const formItem = (0, _mixins_1.useFormItem)(props);
        const { mergedSizeRef } = formItem;
        const { localeRef } = (0, _mixins_1.useLocale)('global');
        const { mergedClsPrefixRef, namespaceRef } = (0, _mixins_1.useConfig)(props);
        const themeRef = (0, _mixins_1.useTheme)('ColorPicker', 'ColorPicker', index_cssr_1.default, styles_1.colorPickerLight, props, mergedClsPrefixRef);
        (0, vue_1.provide)(exports.colorPickerThemeInjectionKey, themeRef);
        const uncontrolledShowRef = (0, vue_1.ref)(props.defaultShow);
        const mergedShowRef = (0, vooks_1.useMergedState)((0, vue_1.toRef)(props, 'show'), uncontrolledShowRef);
        function doUpdateShow(value) {
            const { onUpdateShow, 'onUpdate:show': _onUpdateShow } = props;
            if (onUpdateShow)
                (0, _utils_1.call)(onUpdateShow, value);
            if (_onUpdateShow)
                (0, _utils_1.call)(_onUpdateShow, value);
            uncontrolledShowRef.value = value;
        }
        const { defaultValue } = props;
        const uncontrolledValueRef = (0, vue_1.ref)(defaultValue === undefined
            ? (0, utils_1.deriveDefaultValue)(props.modes, props.showAlpha)
            : defaultValue);
        const mergedValueRef = (0, vooks_1.useMergedState)((0, vue_1.toRef)(props, 'value'), uncontrolledValueRef);
        const undoStackRef = (0, vue_1.ref)([mergedValueRef.value]);
        const valueIndexRef = (0, vue_1.ref)(0);
        const valueModeRef = (0, vue_1.computed)(() => (0, utils_1.getModeFromValue)(mergedValueRef.value));
        const displayedModeRef = (0, vue_1.ref)((0, utils_1.getModeFromValue)(mergedValueRef.value) || 'rgb');
        function handleUpdateDisplayedMode() {
            const { modes } = props;
            const { value: displayedMode } = displayedModeRef;
            const currentModeIndex = modes.findIndex((mode) => mode === displayedMode);
            if (~currentModeIndex) {
                displayedModeRef.value = modes[(currentModeIndex + 1) % modes.length];
            }
            else {
                displayedModeRef.value = 'rgb';
            }
        }
        let _h, // avoid conflict with render function's h
        s, l, v, r, g, b, a;
        const hsvaRef = (0, vue_1.computed)(() => {
            const { value: mergedValue } = mergedValueRef;
            if (!mergedValue)
                return null;
            // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
            switch (valueModeRef.value) {
                case 'hsv':
                    return (0, seemly_1.hsva)(mergedValue);
                case 'hsl':
                    ;
                    [_h, s, l, a] = (0, seemly_1.hsla)(mergedValue);
                    return [...(0, seemly_1.hsl2hsv)(_h, s, l), a];
                case 'rgb':
                case 'hex':
                    ;
                    [r, g, b, a] = (0, seemly_1.rgba)(mergedValue);
                    return [...(0, seemly_1.rgb2hsv)(r, g, b), a];
            }
        });
        const rgbaRef = (0, vue_1.computed)(() => {
            const { value: mergedValue } = mergedValueRef;
            if (!mergedValue)
                return null;
            // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
            switch (valueModeRef.value) {
                case 'rgb':
                case 'hex':
                    return (0, seemly_1.rgba)(mergedValue);
                case 'hsv':
                    ;
                    [_h, s, v, a] = (0, seemly_1.hsva)(mergedValue);
                    return [...(0, seemly_1.hsv2rgb)(_h, s, v), a];
                case 'hsl':
                    ;
                    [_h, s, l, a] = (0, seemly_1.hsla)(mergedValue);
                    return [...(0, seemly_1.hsl2rgb)(_h, s, l), a];
            }
        });
        const hslaRef = (0, vue_1.computed)(() => {
            const { value: mergedValue } = mergedValueRef;
            if (!mergedValue)
                return null;
            // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
            switch (valueModeRef.value) {
                case 'hsl':
                    return (0, seemly_1.hsla)(mergedValue);
                case 'hsv':
                    ;
                    [_h, s, v, a] = (0, seemly_1.hsva)(mergedValue);
                    return [...(0, seemly_1.hsv2hsl)(_h, s, v), a];
                case 'rgb':
                case 'hex':
                    ;
                    [r, g, b, a] = (0, seemly_1.rgba)(mergedValue);
                    return [...(0, seemly_1.rgb2hsl)(r, g, b), a];
            }
        });
        const mergedValueArrRef = (0, vue_1.computed)(() => {
            switch (displayedModeRef.value) {
                case 'rgb':
                case 'hex':
                    return rgbaRef.value;
                case 'hsv':
                    return hsvaRef.value;
                case 'hsl':
                    return hslaRef.value;
            }
        });
        const displayedHueRef = (0, vue_1.ref)(0);
        const displayedAlphaRef = (0, vue_1.ref)(1);
        const displayedSvRef = (0, vue_1.ref)([0, 0]);
        function handleUpdateSv(s, v) {
            const { value: hsvaArr } = hsvaRef;
            const hue = displayedHueRef.value;
            const alpha = hsvaArr ? hsvaArr[3] : 1;
            displayedSvRef.value = [s, v];
            const { showAlpha } = props;
            switch (displayedModeRef.value) {
                case 'hsv':
                    doUpdateValue((showAlpha ? seemly_1.toHsvaString : seemly_1.toHsvString)([hue, s, v, alpha]), 'cursor');
                    break;
                case 'hsl':
                    doUpdateValue((showAlpha ? seemly_1.toHslaString : seemly_1.toHslString)([
                        ...(0, seemly_1.hsv2hsl)(hue, s, v),
                        alpha
                    ]), 'cursor');
                    break;
                case 'rgb':
                    doUpdateValue((showAlpha ? seemly_1.toRgbaString : seemly_1.toRgbString)([
                        ...(0, seemly_1.hsv2rgb)(hue, s, v),
                        alpha
                    ]), 'cursor');
                    break;
                case 'hex':
                    doUpdateValue((showAlpha ? seemly_1.toHexaString : seemly_1.toHexString)([
                        ...(0, seemly_1.hsv2rgb)(hue, s, v),
                        alpha
                    ]), 'cursor');
                    break;
            }
        }
        function handleUpdateHue(hue) {
            displayedHueRef.value = hue;
            const { value: hsvaArr } = hsvaRef;
            if (!hsvaArr) {
                return;
            }
            const [, s, v, a] = hsvaArr;
            const { showAlpha } = props;
            switch (displayedModeRef.value) {
                case 'hsv':
                    doUpdateValue((showAlpha ? seemly_1.toHsvaString : seemly_1.toHsvString)([hue, s, v, a]), 'cursor');
                    break;
                case 'rgb':
                    doUpdateValue((showAlpha ? seemly_1.toRgbaString : seemly_1.toRgbString)([
                        ...(0, seemly_1.hsv2rgb)(hue, s, v),
                        a
                    ]), 'cursor');
                    break;
                case 'hex':
                    doUpdateValue((showAlpha ? seemly_1.toHexaString : seemly_1.toHexString)([
                        ...(0, seemly_1.hsv2rgb)(hue, s, v),
                        a
                    ]), 'cursor');
                    break;
                case 'hsl':
                    doUpdateValue((showAlpha ? seemly_1.toHslaString : seemly_1.toHslString)([
                        ...(0, seemly_1.hsv2hsl)(hue, s, v),
                        a
                    ]), 'cursor');
                    break;
            }
        }
        function handleUpdateAlpha(alpha) {
            switch (displayedModeRef.value) {
                case 'hsv':
                    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
                    ;
                    [_h, s, v] = hsvaRef.value;
                    doUpdateValue((0, seemly_1.toHsvaString)([_h, s, v, alpha]), 'cursor');
                    break;
                case 'rgb':
                    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
                    ;
                    [r, g, b] = rgbaRef.value;
                    doUpdateValue((0, seemly_1.toRgbaString)([r, g, b, alpha]), 'cursor');
                    break;
                case 'hex':
                    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
                    ;
                    [r, g, b] = rgbaRef.value;
                    doUpdateValue((0, seemly_1.toHexaString)([r, g, b, alpha]), 'cursor');
                    break;
                case 'hsl':
                    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
                    ;
                    [_h, s, l] = hslaRef.value;
                    doUpdateValue((0, seemly_1.toHslaString)([_h, s, l, alpha]), 'cursor');
                    break;
            }
            displayedAlphaRef.value = alpha;
        }
        function doUpdateValue(value, updateSource) {
            if (updateSource === 'cursor') {
                upcomingValue = value;
            }
            else {
                upcomingValue = null;
            }
            const { nTriggerFormChange, nTriggerFormInput } = formItem;
            const { onUpdateValue, 'onUpdate:value': _onUpdateValue } = props;
            if (onUpdateValue)
                (0, _utils_1.call)(onUpdateValue, value);
            if (_onUpdateValue)
                (0, _utils_1.call)(_onUpdateValue, value);
            nTriggerFormChange();
            nTriggerFormInput();
            uncontrolledValueRef.value = value;
        }
        function handleInputUpdateValue(value) {
            doUpdateValue(value, 'input');
            void (0, vue_1.nextTick)(handleComplete);
        }
        function handleComplete(pushStack = true) {
            const { value } = mergedValueRef;
            // no value & only hue changes will complete with no value
            if (value) {
                const { nTriggerFormChange, nTriggerFormInput } = formItem;
                const { onComplete } = props;
                if (onComplete) {
                    ;
                    onComplete(value);
                }
                const { value: undoStack } = undoStackRef;
                const { value: valueIndex } = valueIndexRef;
                if (pushStack) {
                    undoStack.splice(valueIndex + 1, undoStack.length, value);
                    valueIndexRef.value = valueIndex + 1;
                }
                nTriggerFormChange();
                nTriggerFormInput();
            }
        }
        function undo() {
            const { value: valueIndex } = valueIndexRef;
            if (valueIndex - 1 < 0)
                return;
            doUpdateValue(undoStackRef.value[valueIndex - 1], 'input');
            handleComplete(false);
            valueIndexRef.value = valueIndex - 1;
        }
        function redo() {
            const { value: valueIndex } = valueIndexRef;
            if (valueIndex < 0 || valueIndex + 1 >= undoStackRef.value.length)
                return;
            doUpdateValue(undoStackRef.value[valueIndex + 1], 'input');
            handleComplete(false);
            valueIndexRef.value = valueIndex + 1;
        }
        function handleConfirm() {
            doUpdateShow(false);
        }
        const undoableRef = (0, vue_1.computed)(() => valueIndexRef.value >= 1);
        const redoableRef = (0, vue_1.computed)(() => {
            const { value: undoStack } = undoStackRef;
            return undoStack.length > 1 && valueIndexRef.value < undoStack.length - 1;
        });
        (0, vue_1.watch)(mergedShowRef, (value) => {
            if (!value) {
                undoStackRef.value = [mergedValueRef.value];
                valueIndexRef.value = 0;
            }
        });
        (0, vue_1.watchEffect)(() => {
            if (upcomingValue && upcomingValue === mergedValueRef.value) {
                // let it works in uncontrolled mode
            }
            else {
                const { value } = hsvaRef;
                if (value) {
                    displayedHueRef.value = value[0];
                    displayedAlphaRef.value = value[3];
                    displayedSvRef.value = [value[1], value[2]];
                }
            }
            upcomingValue = null;
        });
        const cssVarsRef = (0, vue_1.computed)(() => {
            const { value: mergedSize } = mergedSizeRef;
            const { common: { cubicBezierEaseInOut }, self: { textColor, color, panelFontSize, boxShadow, border, borderRadius, dividerColor, [(0, _utils_1.createKey)('height', mergedSize)]: height, [(0, _utils_1.createKey)('fontSize', mergedSize)]: fontSize } } = themeRef.value;
            return {
                '--bezier': cubicBezierEaseInOut,
                '--text-color': textColor,
                '--color': color,
                '--panel-font-size': panelFontSize,
                '--font-size': fontSize,
                '--box-shadow': boxShadow,
                '--border': border,
                '--border-radius': borderRadius,
                '--height': height,
                '--divider-color': dividerColor
            };
        });
        function renderPanel() {
            var _a;
            const { value: rgba } = rgbaRef;
            const { value: displayedHue } = displayedHueRef;
            const { internalActions, modes, actions } = props;
            const { value: mergedTheme } = themeRef;
            const { value: mergedClsPrefix } = mergedClsPrefixRef;
            return ((0, vue_1.h)("div", { class: `${mergedClsPrefix}-color-picker-panel`, onDragstart: (e) => {
                    e.preventDefault();
                }, style: cssVarsRef.value },
                (0, vue_1.h)("div", { class: `${mergedClsPrefix}-color-picker-control` },
                    (0, vue_1.h)(Pallete_1.default, { clsPrefix: mergedClsPrefix, rgba: rgba, displayedHue: displayedHue, displayedSv: displayedSvRef.value, onUpdateSV: handleUpdateSv, onComplete: handleComplete }),
                    (0, vue_1.h)(HueSlider_1.default, { clsPrefix: mergedClsPrefix, hue: displayedHue, onUpdateHue: handleUpdateHue, onComplete: handleComplete }),
                    props.showAlpha ? ((0, vue_1.h)(AlphaSlider_1.default, { clsPrefix: mergedClsPrefix, rgba: rgba, alpha: displayedAlphaRef.value, onUpdateAlpha: handleUpdateAlpha, onComplete: handleComplete })) : null,
                    (0, vue_1.h)(ColorInput_1.default, { clsPrefix: mergedClsPrefix, showAlpha: props.showAlpha, mode: displayedModeRef.value, modes: modes, onUpdateMode: handleUpdateDisplayedMode, value: mergedValueRef.value, valueArr: mergedValueArrRef.value, onUpdateValue: handleInputUpdateValue }),
                    ((_a = props.swatches) === null || _a === void 0 ? void 0 : _a.length) && ((0, vue_1.h)(ColorPickerSwatches_1.default, { clsPrefix: mergedClsPrefix, mode: displayedModeRef.value, swatches: props.swatches, onUpdateColor: (color) => doUpdateValue(color, 'input') }))),
                (actions === null || actions === void 0 ? void 0 : actions.length) ? ((0, vue_1.h)("div", { class: `${mergedClsPrefix}-color-picker-action` }, actions.includes('confirm') && ((0, vue_1.h)(button_1.NButton, { size: "small", onClick: handleConfirm, theme: mergedTheme.peers.Button, themeOverrides: mergedTheme.peerOverrides.Button }, { default: () => localeRef.value.confirm })))) : null,
                slots.action ? ((0, vue_1.h)("div", { class: `${mergedClsPrefix}-color-picker-action` }, { default: slots.action })) : internalActions ? ((0, vue_1.h)("div", { class: `${mergedClsPrefix}-color-picker-action` },
                    internalActions.includes('undo') && ((0, vue_1.h)(button_1.NButton, { size: "small", onClick: undo, disabled: !undoableRef.value, theme: mergedTheme.peers.Button, themeOverrides: mergedTheme.peerOverrides.Button }, { default: () => localeRef.value.undo })),
                    internalActions.includes('redo') && ((0, vue_1.h)(button_1.NButton, { size: "small", onClick: redo, disabled: !redoableRef.value, theme: mergedTheme.peers.Button, themeOverrides: mergedTheme.peerOverrides.Button }, { default: () => localeRef.value.redo })))) : null));
        }
        return {
            mergedClsPrefix: mergedClsPrefixRef,
            namespace: namespaceRef,
            selfRef,
            hsla: hslaRef,
            rgba: rgbaRef,
            mergedShow: mergedShowRef,
            isMounted: (0, vooks_1.useIsMounted)(),
            adjustedTo: (0, _utils_1.useAdjustedTo)(props),
            mergedValue: mergedValueRef,
            handleTriggerClick() {
                doUpdateShow(true);
            },
            handleClickOutside(e) {
                var _a;
                if ((_a = selfRef.value) === null || _a === void 0 ? void 0 : _a.contains(e.target))
                    return;
                doUpdateShow(false);
            },
            renderPanel,
            cssVars: cssVarsRef
        };
    },
    render() {
        const { mergedClsPrefix } = this;
        return ((0, vue_1.h)("div", { class: `${mergedClsPrefix}-color-picker`, ref: "selfRef", style: this.cssVars },
            (0, vue_1.h)(vueuc_1.VBinder, null, {
                default: () => [
                    (0, vue_1.h)(vueuc_1.VTarget, null, {
                        default: () => ((0, vue_1.h)(ColorPickerTrigger_1.default, { clsPrefix: mergedClsPrefix, value: this.mergedValue, hsla: this.hsla, onClick: this.handleTriggerClick }))
                    }),
                    (0, vue_1.h)(vueuc_1.VFollower, { placement: "bottom-start", show: this.mergedShow, containerClass: this.namespace, teleportDisabled: this.adjustedTo === _utils_1.useAdjustedTo.tdkey, to: this.adjustedTo }, {
                        default: () => ((0, vue_1.h)(vue_1.Transition, { name: "fade-in-scale-up-transition", appear: this.isMounted }, {
                            default: () => this.mergedShow
                                ? (0, vue_1.withDirectives)(this.renderPanel(), [
                                    [vdirs_1.clickoutside, this.handleClickOutside]
                                ])
                                : null
                        }))
                    })
                ]
            })));
    }
});
