kui-vue
Version:
A lightweight desktop UI component library suitable for Vue.js 2.
366 lines (357 loc) • 10.5 kB
JSX
import transfer from "../directives/transfer";
import resize from "../directives/resize";
import { setPlacement } from "../utils/placement";
const modes = ["rgb", "hex", "hsl"];
import Mode from "./mode";
import Hue from "./hue";
import Alpha from "./alpha";
import Paint from "./paint";
import Color from "color";
import Presets from "./presets";
import { cloneNodes } from "../utils/vnode";
import { withInstall } from "../utils/vue";
import {
defineComponent,
ref,
watch,
onMounted,
onBeforeUnmount,
nextTick,
// Transition,
} from "vue";
const ColorPicker = defineComponent({
name: "ColorPicker",
directives: {
transfer,
resize,
},
props: {
value: String,
transfer: { type: Boolean, default: true },
disabled: Boolean,
disabledAlpha: Boolean,
showText: Boolean,
arrow: Boolean,
placement: {
validator(value) {
return [
"top",
"top-left",
"top-right",
"bottom",
"bottom-left",
"bottom-right",
].includes(value);
},
default: "bottom-left",
},
trigger: {
type: String,
default: "click",
validator(value) {
return ["hover", "click"].indexOf(value) >= 0;
},
},
size: {
default: "default",
validator(value) {
return ["small", "large", "default"].indexOf(value) >= 0;
},
},
mode: {
type: String,
default: "hex",
validator: function (value) {
return modes.indexOf(value) !== -1;
},
},
show: Boolean,
presets: {
type: Array,
},
},
setup(ps, { emit, slots }) {
const currentMode = ref(ps.mode);
const currentColor = ref(ps.value || "#000000ff");
const visible = ref(ps.show);
const refPopper = ref();
const refSelection = ref();
const left = ref(0);
const top = ref(0);
const currentPlacement = ref(ps.placement);
const transOrigin = ref("bottom");
const rendered = ref(false);
const currentAlpha = ref(1);
const currentHue = ref(0);
const hideTimer = ref();
watch(
() => ps.value,
(v) => {
currentColor.value = v;
// currentAlpha.value = Color(v).alpha();
// currentHue.value = Color(v).hue();
}
);
onMounted(() => {
if (ps.value) {
currentAlpha.value = Color(ps.value).alpha();
currentHue.value = Color(ps.value).hue();
}
});
onBeforeUnmount(() => {
document.removeEventListener("click", outsideClick);
});
const updatePopPosition = () => {
nextTick(() => {
setPlacement({
refSelection,
refPopper,
currentPlacement,
transOrigin,
top,
left,
});
});
};
const outsideClick = (e) => {
const ctx = refSelection.value?.$el || refSelection.value;
if (
refPopper.value &&
!refPopper.value.contains(e.target) &&
ctx &&
!ctx.contains(e.target)
) {
clearTimeout(hideTimer.value);
hideTimer.value = setTimeout(() => (visible.value = false), 200);
}
};
const toggle = (open) => {
if (ps.disabled) {
return false;
}
if (open) {
if (!rendered.value) {
rendered.value = true;
document.addEventListener("click", outsideClick);
nextTick(() => {
visible.value = true;
updatePopPosition();
});
} else {
visible.value = true;
updatePopPosition();
}
} else {
visible.value = false;
}
// currentColor = value || "#000"; ??
};
const getColor = () => {
let text = "";
const color = Color(currentColor.value);
if (currentMode.value == "hex") {
text = color.alpha() < 1 ? color.hexa() : color.hex();
} else if (currentMode.value == "rgb") {
text = color.rgb().string(0);
} else if (currentMode.value == "hsl") {
text = color.hsl().string(0);
}
return text;
};
const renderTriggerText = () => {
let text = getColor();
return ps.showText ? (
<div class="k-color-picker-trigger-text">{text}</div>
) : null;
};
const onUpdate = (color) => {
currentColor.value = color;
const value = getColor();
// emit("update:value", value);
emit("input", value);
emit("change", value);
};
const onUpdateRGB = ({ r, g, b }) => {
const color = Color({ r, g, b, alpha: currentAlpha.value });
onUpdate(color.rgb());
};
const onUpdateHue = (hue) => {
// console.log("hue", hue);
currentHue.value = hue;
// currentColor.value = color;
// const value = getColor();
const value = Color(currentColor.value).hue(hue).rgb();
onUpdate(value);
};
const onUpdateAlpha = (a) => {
currentAlpha.value = a;
const value = Color(currentColor.value).alpha(a).rgb();
onUpdate(value);
};
const onUpdateMode = (mode) => {
currentMode.value = mode;
onUpdate(currentColor.value);
emit("update:mode", mode);
setTimeout(() => {
clearTimeout(hideTimer.value);
}, 0);
};
const updateColorValue = (color) => {
// console.log(color.string(), currentAlpha.value);
currentAlpha.value = color.alpha();
currentColor.value = color;
currentHue.value = color.hue();
onUpdate(color);
};
const updateColor = (color) => {
currentAlpha.value = color.alpha();
currentHue.value = color.hue();
updateColorValue(color.rgb());
};
const renderDrop = () => {
if (!rendered.value) return null;
const props = {
ref: refPopper,
// "k-placement": currentPlacement.value,
attrs: { "k-placement": currentPlacement.value },
class: [
"k-color-picker-dropdown",
{
"k-color-picker-disabled-alpha": ps.disabledAlpha,
},
],
style: {
left: `${left.value}px`,
top: `${top.value}px`,
transformOrigin: transOrigin.value,
},
// onMouseenter: () => {
// clearTimeout(hideTimer.value);
// },
on: {
mouseenter: () => {
clearTimeout(hideTimer.value);
},
},
};
// let [r, g, b] = hslToRgb(color.H, color.S, color.L);
return (
<transition name="k-color-picker">
<div v-transfer={true} v-show={visible.value} {...props}>
<div class="k-color-picker-body">
<Paint
hue={currentHue.value}
value={currentColor.value}
onUpdateRGB={onUpdateRGB}
/>
<div class="k-color-picker-bar">
<div class="k-color-picker-avatar">
<div
class="k-color-picker-avatar-inner"
style={`background-color:${currentColor.value}`}
></div>
</div>
<div class="k-color-picker-bar-box">
<Hue hue={currentHue.value} onUpdateHue={onUpdateHue} />
{!ps.disabledAlpha ? (
<Alpha
value={currentColor.value}
onUpdateAlpha={onUpdateAlpha}
/>
) : null}
</div>
</div>
<Mode
mode={currentMode.value}
value={currentColor.value}
disabledAlpha={ps.disabledAlpha}
onUpdateMode={onUpdateMode}
onUpdateColorValue={updateColorValue}
/>
<Presets
onUpdateColor={updateColor}
value={ps.presets}
color={currentColor.value}
/>
</div>
<div class={`k-color-picker-arrow`}>
<svg style={{ fill: "currentcolor" }} viewBox="0 0 24 8">
<path
id="ot"
d="m24,0.97087l0,1c-4,0 -5.5,1 -7.5,3c-2,2 -2.5,3 -4.5,3c-2,0 -2.5,-1 -4.5,-3c-2,-2 -3.5,-3 -7.5,-3l0,-1l24,0z"
/>
<path
stroke="currentcolor"
id="in"
d="m24,0l0,1c-4,0 -5.5,1 -7.5,3c-2,2 -2.5,3 -4.5,3c-2,0 -2.5,-1 -4.5,-3c-2,-2 -3.5,-3 -7.5,-3l0,-1l24,0z"
/>
</svg>
</div>
</div>
</transition>
);
};
const onMouseleave = (e) => {
if (ps.disabled) {
return;
}
if (ps.trigger == "hover") {
hideTimer.value = setTimeout(() => {
toggle(false);
}, 300);
}
};
return () => {
let drop = renderDrop();
let style = [
"k-color-picker",
{
"k-color-picker-opened": visible.value,
"k-color-picker-disabled": ps.disabled,
"k-color-picker-sm": ps.size == "small",
"k-color-picker-lg": ps.size == "large",
},
];
const triggerClick = ps.trigger == "click";
return slots.default ? (
<span>
{cloneNodes(
slots.default(),
{
ref: refSelection,
on: {
click: () => triggerClick && toggle(!visible.value),
mouseenter: () => !triggerClick && toggle(true),
mouseleave: onMouseleave,
},
// onClick: () => triggerClick && toggle(!visible.value), //for 3
// onMouseenter: () => !triggerClick && toggle(true),
// onMouseleave: onMouseleave,
},
true
)}
{drop}
</span>
) : (
<div class={style} ref={refSelection} v-resize={updatePopPosition}>
<div
class="k-color-picker-selection"
onMouseenter={() => !triggerClick && toggle(true)}
onMouseleave={onMouseleave}
onClick={() => triggerClick && toggle(!visible.value)}
>
<div class="k-color-picker-color">
<div
class="k-color-picker-color-inner"
style={`background-color:${currentColor.value}`}
></div>
</div>
{renderTriggerText()}
</div>
{drop}
</div>
);
};
},
});
export default withInstall(ColorPicker);