ranui
Version:
A framework-agnostic Web Components UI library built on native custom elements, with TypeScript types, light/dark theming, SSR and PWA support.
240 lines (239 loc) • 17.7 kB
JavaScript
import { B as x, F as d, T as b, _ as h, h as P, i as s } from "./factory-WDxu11V0.js";
import { t as m } from "./events-Cqz3ramM.js";
import "./builder-EH76YCFS.js";
import { a as k, i as y, l as p, n as w, o as S, t as A } from "./utils-U-9uH7EX.js";
import { a as $, n as _ } from "./ssr-BzykYefm.js";
import { i as C, n as D } from "./a11y-ZSlIUH9B.js";
import "./input-DEoPNM0j.js";
import "./select-E396XACJ.js";
import "./popover-Bw11RB79.js";
import { HEX_COLOR_REGEX as I, RGBA_REGEX as E, RGB_REGEX as R, hexToHsv as H, hsvToRgb as g, range as c, rgbToHex as f, rgbToHsv as v } from "ranuts/utils";
var V = ":host{box-sizing:border-box;min-width:var(--ran-colorpicker-min-width, var(--ran-size-7, 32px));height:var(--ran-colorpicker-height, var(--ran-size-7, 32px));border-radius:var(--ran-colorpicker-border-radius, var(--ran-radius-md, 8px));border:var(--ran-colorpicker-border, 1px solid var(--ran-color-border, #eaeaea));cursor:pointer;display:var(--ran-colorpicker-host-display, inline-flex);align-items:center;justify-content:center;transition:var(--ran-colorpicker-transition, none);background:var(--ran-colorpicker-background, var(--ran-color-bg-elevated, #ffffff));padding:var(--ran-colorpicker-padding, var(--ran-space-1, 4px))}:host(:hover){border-color:var(--ran-colorpicker-hover-border-color, var(--ran-color-border-hover, var(--ran-gray-500)))}.ran-colorpicker-block{position:relative;border-radius:var(--ran-colorpicker-block-border-radius, var(--ran-radius-sm, 4px));width:var(--ran-colorpicker-block-width, var(--ran-size-4, 24px));height:var(--ran-colorpicker-block-height, var(--ran-size-4, 24px));box-shadow:inset 0 0 0 1px var(--ran-gray-alpha-200, rgba(0, 0, 0, .09));background-image:conic-gradient(var(--ran-gray-alpha-100, rgba(0, 0, 0, .06)) 0 25%,transparent 0 50%,var(--ran-gray-alpha-100, rgba(0, 0, 0, .06)) 0 75%,transparent 0);background-size:50% 50%}.ran-colorpicker-block[disabled]{cursor:var(--ran-colorpicker-disabled-cursor, not-allowed);opacity:var(--ran-colorpicker-disabled-opacity, .5)}.ran-colorpicker-inner{width:100%;height:100%;border-radius:inherit}", F = ".ran-color-picker-inner{box-sizing:border-box}.ran-color-picker-inner-content{box-sizing:border-box;display:flex;flex-direction:column;gap:12px;width:224px;padding:12px;font-family:var(--ran-font-family, inherit)}.ran-color-picker-inner-content *,.ran-color-picker-inner-content *:before,.ran-color-picker-inner-content *:after{box-sizing:border-box}.ran-color-picker-palette{position:relative;width:100%;height:160px;border-radius:var(--ran-radius-sm, 6px);overflow:hidden;cursor:crosshair;box-shadow:inset 0 0 0 1px var(--ran-gray-alpha-200, rgba(0, 0, 0, .09));touch-action:none}.ran-color-picker-saturation{position:absolute;inset:0;border-radius:inherit;background-image:linear-gradient(to top,#000,#0000),linear-gradient(to right,#fff,#fff0)}.ran-color-picker-palette-dot{position:absolute;top:0;left:0;z-index:2;width:14px;height:14px;transform:translate(-50%,-50%);border-radius:50%;border:2px solid #fff;box-shadow:0 0 0 1px #0003,0 1px 3px #00000059;pointer-events:none}.ran-color-picker-controls{display:flex;align-items:center;gap:12px}.ran-color-picker-preview{position:relative;flex:none;width:28px;height:28px;border-radius:50%;overflow:hidden;box-shadow:inset 0 0 0 1px var(--ran-gray-alpha-200, rgba(0, 0, 0, .09));background-image:conic-gradient(var(--ran-gray-alpha-100, rgba(0, 0, 0, .06)) 0 25%,transparent 0 50%,var(--ran-gray-alpha-100, rgba(0, 0, 0, .06)) 0 75%,transparent 0);background-size:12px 12px}.ran-color-picker-preview-inner{position:absolute;inset:0;border-radius:inherit}.ran-color-picker-sliders{flex:1;min-width:0;display:flex;flex-direction:column;gap:12px}.ran-color-picker-slider{position:relative;height:10px;border-radius:999px;cursor:pointer;box-shadow:inset 0 0 0 1px var(--ran-gray-alpha-200, rgba(0, 0, 0, .09));touch-action:none}.ran-color-picker-slider-hue{background:linear-gradient(to right,red,#ff0,#0f0,#0ff,#00f,#f0f,red)}.ran-color-picker-slider-alpha{background-image:conic-gradient(var(--ran-gray-alpha-100, rgba(0, 0, 0, .06)) 0 25%,transparent 0 50%,var(--ran-gray-alpha-100, rgba(0, 0, 0, .06)) 0 75%,transparent 0);background-size:8px 8px}.ran-color-picker-slider-alpha-track{position:absolute;inset:0;border-radius:inherit}.ran-color-picker-slider-thumb{position:absolute;top:50%;left:0;z-index:1;width:14px;height:14px;transform:translate(-50%,-50%);border-radius:50%;border:2px solid #fff;box-shadow:0 0 0 1px #0003,0 1px 3px #00000059;pointer-events:none}.ran-color-picker-input-container{display:flex;align-items:center;gap:8px}.ran-color-picker-format{flex:none;width:68px}.ran-color-picker-value{flex:1;min-width:0}", u = 360, T = class extends _ {
colorpicker;
colorpickerInner;
context;
_shadowDom;
_events = new m();
_effectDisposers = [];
popoverBlock;
popoverContent;
colorPickerInner;
colorPickerPalette;
colorPickerSaturation;
colorPickerPaletteDot;
colorPickerHueSlider;
colorPickerHueThumb;
colorPickerAlphaSlider;
colorPickerAlphaTrack;
colorPickerAlphaThumb;
colorPickerPreviewInner;
colorPickerValueInput;
colorPickerFormatSelect;
colorPickerPaletteSelect;
_activeSlider = null;
_format = "HEX";
_editingInput = !1;
static get observedAttributes() {
return [
"disabled",
"value",
"sheet"
];
}
constructor() {
super(), this._shadowDom = A(this, V);
const e = b(), t = b(), r = b(), i = s().class("ran-colorpicker-inner").ref(r).part("swatch").build(), o = s().class("ran-colorpicker-block").ref(t).part("block").children(i).build(), a = h("r-content").ref(e).class("ran-content").build(), n = h("r-popover").class("ran-popover").attr("trigger", "click").cssVar("ran-popover-content-padding", "0").children(o, a).build();
this._shadowDom.appendChild(n), this.popoverBlock = n, this.popoverContent = k(e, "r content"), this.colorpicker = k(t, "block"), this.colorpickerInner = k(r, "inner"), this.colorpicker.setAttribute("role", "button"), this.colorpicker.setAttribute("tabindex", "0"), this.colorpicker.setAttribute("aria-haspopup", "dialog"), this.colorpicker.setAttribute("aria-label", "Choose color"), this.colorPickerPaletteSelect = !1, this.createContext();
}
get value() {
return this.context?.value.getter() || "";
}
set value(e) {
this.setAttribute("value", e);
}
get sheet() {
return w(this, "sheet");
}
set sheet(e) {
y(this, "sheet", e);
}
get disabled() {
return p(this);
}
set disabled(e) {
e === !1 || e === void 0 || e === null ? this.removeAttribute("disabled") : this.setAttribute("disabled", "");
}
handlerExternalCss = () => {
S(this, this._shadowDom, "sheet", null, this.sheet);
};
syncDisabledState = () => {
const e = p(this);
this.context?.disabled.setter(e), e ? (this.setAttribute("aria-disabled", "true"), this.colorpicker?.setAttribute("disabled", "")) : (this.removeAttribute("aria-disabled"), this.colorpicker?.removeAttribute("disabled")), this.colorpicker?.setAttribute("aria-disabled", e ? "true" : "false"), this.colorpicker?.setAttribute("tabindex", e ? "-1" : "0");
};
blockWhenDisabled = (e) => {
p(this) && (e.stopImmediatePropagation(), e.preventDefault());
};
createContext = () => {
const e = (t) => {
const [r, i] = x(t);
return {
getter: r,
setter: i
};
};
this.context = {
value: e(""),
disabled: e(!1),
hue: e(0),
saturation: e(0),
lightness: e(100),
transparency: e(100)
};
};
currentRgba = () => {
const { hue: e, saturation: t, lightness: r, transparency: i } = this.context, [o, a, n] = g(e.getter(), t.getter(), r.getter());
return {
r: o,
g: a,
b: n,
a: i.getter() / 100
};
};
currentValue = () => {
const { r: e, g: t, b: r, a: i } = this.currentRgba();
return i < 1 ? `rgba(${e}, ${t}, ${r}, ${Number(i.toFixed(2))})` : f(e, t, r);
};
currentDisplay = () => {
const { r: e, g: t, b: r, a: i } = this.currentRgba();
return this._format === "RGB" ? i < 1 ? `rgba(${e}, ${t}, ${r}, ${Number(i.toFixed(2))})` : `rgb(${e}, ${t}, ${r})` : f(e, t, r);
};
paintSwatch = () => {
const { r: e, g: t, b: r, a: i } = this.currentRgba();
this.colorpickerInner?.style.setProperty("background", `rgba(${e}, ${t}, ${r}, ${Number(i.toFixed(3))})`);
};
updateColorValue = (e) => {
if (e === this.context?.value.getter()) return;
const t = e.replace(/\s+/g, ""), r = I.exec(e), i = E.exec(t), o = R.exec(t);
if (r) {
const [a, n, l] = H(r[0]);
this.context.hue.setter(a), this.context.saturation.setter(n), this.context.lightness.setter(l), this.context.transparency.setter(100);
} else if (i) {
const [a, n, l] = v(Number(i[1]), Number(i[2]), Number(i[3]));
this.context.hue.setter(a), this.context.saturation.setter(n), this.context.lightness.setter(l), this.context.transparency.setter(Number(i[4]) * 100);
} else if (o) {
const [a, n, l] = v(Number(o[1]), Number(o[2]), Number(o[3]));
this.context.hue.setter(a), this.context.saturation.setter(n), this.context.lightness.setter(l), this.context.transparency.setter(100);
} else return;
this.setAttribute("value", e), this.context?.value.setter(e), this.paintSwatch();
};
emitChange = () => {
const e = this.currentValue(), { r: t, g: r, b: i, a: o } = this.currentRgba();
this.context?.value.setter(e), this.setAttribute("value", e), this.dispatchEvent(new CustomEvent("change", {
detail: {
value: e,
hex: f(t, r, i),
rgb: `rgb(${t}, ${r}, ${i})`,
rgba: `rgba(${t}, ${r}, ${i}, ${Number(o.toFixed(2))})`,
alpha: Number(o.toFixed(2))
},
bubbles: !0,
composed: !0
}));
};
palettePointerDown = (e) => {
e.preventDefault(), this.colorPickerPaletteSelect = !0, this.updatePaletteFromEvent(e);
};
updatePaletteFromEvent = (e) => {
if (!this.colorPickerPalette) return;
const { left: t, top: r, width: i, height: o } = this.colorPickerPalette.getBoundingClientRect();
if (!i || !o) return;
const a = c(e.clientX - t, 0, i), n = c(e.clientY - r, 0, o);
this.context.saturation.setter(a / i * 100), this.context.lightness.setter((1 - n / o) * 100), this.emitChange();
};
sliderPointerDown = (e) => (t) => {
t.preventDefault(), this._activeSlider = e, this.updateSliderFromEvent(e, t);
};
updateSliderFromEvent = (e, t) => {
const r = e === "hue" ? this.colorPickerHueSlider : this.colorPickerAlphaSlider;
if (!r) return;
const { left: i, width: o } = r.getBoundingClientRect();
if (!o) return;
const a = c(t.clientX - i, 0, o) / o;
e === "hue" ? this.context.hue.setter(a * u) : this.context.transparency.setter(a * 100), this.emitChange();
};
onPointerMove = (e) => {
const t = e;
this.colorPickerPaletteSelect ? this.updatePaletteFromEvent(t) : this._activeSlider && this.updateSliderFromEvent(this._activeSlider, t);
};
initSliderA11y = (e, t, r) => {
e.setAttribute("role", "slider"), e.setAttribute("tabindex", "0"), e.setAttribute("aria-label", t), e.setAttribute("aria-orientation", "horizontal"), e.setAttribute("aria-valuemin", "0"), e.setAttribute("aria-valuemax", String(r));
};
sliderKeydown = (e) => (t) => {
const r = e === "hue" ? u : 100, i = e === "hue" ? this.context.hue : this.context.transparency, o = C(t, {
current: i.getter(),
min: 0,
max: r,
step: 1
});
o !== void 0 && (t.preventDefault(), i.setter(c(o, 0, r)), this.emitChange());
};
onPointerUp = () => {
this.colorPickerPaletteSelect = !1, this._activeSlider = null;
};
onSwatchKeydown = (e) => {
p(this) || D(e) && (e.preventDefault(), this.colorpicker?.click());
};
onValueInput = (e) => {
const t = e.target, r = e.detail?.value ?? t?.value;
typeof r == "string" && (this._editingInput = !0, this.updateColorValue(r.trim()), this._editingInput = !1, this.emitChange());
};
onFormatChange = (e) => {
const t = e.detail?.value;
this._format = t === "RGB" ? "RGB" : "HEX", this.syncValueInput();
};
syncValueInput = () => {
this._editingInput || !this.colorPickerValueInput || (this.colorPickerValueInput.value = this.currentDisplay());
};
setupEffects = () => {
this._effectDisposers.push(d(() => {
const [e, t, r] = g(this.context.hue.getter(), 100, 100);
this.colorPickerSaturation?.style.setProperty("background-color", `rgb(${e}, ${t}, ${r})`);
}), d(() => {
const e = c(this.context.saturation.getter(), 0, 100), t = c(this.context.lightness.getter(), 0, 100);
this.colorPickerPaletteDot?.style.setProperty("left", `${e}%`), this.colorPickerPaletteDot?.style.setProperty("top", `${100 - t}%`);
}), d(() => {
const e = this.context.hue.getter(), [t, r, i] = g(e, 100, 100);
this.colorPickerHueThumb?.style.setProperty("left", `${e / u * 100}%`), this.colorPickerHueThumb?.style.setProperty("background", `rgb(${t}, ${r}, ${i})`), this.colorPickerHueSlider?.setAttribute("aria-valuenow", String(Math.round(e)));
}), d(() => {
const { r: e, g: t, b: r, a: i } = this.currentRgba(), o = `rgb(${e}, ${t}, ${r})`, a = `rgba(${e}, ${t}, ${r}, ${Number(i.toFixed(3))})`;
this.colorPickerAlphaTrack?.style.setProperty("background", `linear-gradient(to right, rgba(${e}, ${t}, ${r}, 0), ${o})`), this.colorPickerAlphaThumb?.style.setProperty("left", `${i * 100}%`), this.colorPickerAlphaThumb?.style.setProperty("background", a), this.colorPickerPreviewInner?.style.setProperty("background", a), this.colorpickerInner?.style.setProperty("background", a), this.colorPickerAlphaSlider?.setAttribute("aria-valuenow", String(Math.round(i * 100))), this.colorPickerAlphaSlider?.setAttribute("aria-valuetext", `${Math.round(i * 100)}%`), this.syncValueInput();
}));
};
disposeEffects = () => {
for (const e of this._effectDisposers) e();
this._effectDisposers = [];
};
openColorPicker = () => {
if (p(this) || this.colorPickerInner) return;
this.colorPickerSaturation = s().class("ran-color-picker-saturation").build(), this.colorPickerPaletteDot = s().class("ran-color-picker-palette-dot").build(), this.colorPickerPalette = s().class("ran-color-picker-palette").on("pointerdown", this.palettePointerDown).children(this.colorPickerSaturation, this.colorPickerPaletteDot).build(), this.colorPickerPreviewInner = s().class("ran-color-picker-preview-inner").build();
const e = s().class("ran-color-picker-preview").children(this.colorPickerPreviewInner).build();
this.colorPickerHueThumb = s().class("ran-color-picker-slider-thumb").build(), this.colorPickerHueSlider = s().class("ran-color-picker-slider ran-color-picker-slider-hue").on("pointerdown", this.sliderPointerDown("hue")).on("keydown", this.sliderKeydown("hue")).children(this.colorPickerHueThumb).build(), this.initSliderA11y(this.colorPickerHueSlider, "Hue", u), this.colorPickerAlphaTrack = s().class("ran-color-picker-slider-alpha-track").build(), this.colorPickerAlphaThumb = s().class("ran-color-picker-slider-thumb").build(), this.colorPickerAlphaSlider = s().class("ran-color-picker-slider ran-color-picker-slider-alpha").on("pointerdown", this.sliderPointerDown("alpha")).on("keydown", this.sliderKeydown("alpha")).children(this.colorPickerAlphaTrack, this.colorPickerAlphaThumb).build(), this.initSliderA11y(this.colorPickerAlphaSlider, "Alpha opacity", 100);
const t = s().class("ran-color-picker-sliders").children(this.colorPickerHueSlider, this.colorPickerAlphaSlider).build(), r = s().class("ran-color-picker-controls").children(e, t).build();
this.colorPickerFormatSelect = h("r-select").class("ran-color-picker-format").attr("value", this._format).attr("defaultValue", this._format).attr("trigger", "click").children(...["HEX", "RGB"].map((a) => h("r-option").attr("value", a).text(a).build())).on("change", this.onFormatChange).build(), this.colorPickerValueInput = h("r-input").class("ran-color-picker-value").on("change", this.onValueInput).build();
const i = s().class("ran-color-picker-input-container").children(this.colorPickerFormatSelect, this.colorPickerValueInput).build(), o = s().class("ran-color-picker-inner-content").children(this.colorPickerPalette, r, i).build();
this.colorPickerInner = s().class("ran-color-picker-inner").children(P().text(F).build(), o).build(), this.popoverContent.appendChild(this.colorPickerInner), this.setupEffects(), this.syncValueInput();
};
connectedCallback() {
this.handlerExternalCss(), this.setAttribute("class", "ran-colorpicker"), this._events.on(this, "click", this.blockWhenDisabled, { capture: !0 }), this._events.on(this, "keydown", this.blockWhenDisabled, { capture: !0 }), this._events.on(this.popoverBlock, "click", this.openColorPicker), this._events.on(this.colorpicker, "keydown", this.onSwatchKeydown), this._events.on(document, "pointermove", this.onPointerMove), this._events.on(document, "pointerup", this.onPointerUp), this._events.on(document, "pointercancel", this.onPointerUp), this.syncDisabledState(), this.value && this.updateColorValue(this.value), this.paintSwatch(), this.colorPickerInner && this._effectDisposers.length === 0 && this.setupEffects();
}
disconnectedCallback() {
this._events.abort(), this.disposeEffects();
}
attributeChangedCallback(e, t, r) {
t !== r && (e === "value" && this.updateColorValue(r), e === "disabled" && this.syncDisabledState(), e === "sheet" && this.handlerExternalCss());
}
};
$("r-colorpicker", T);
export {
T as t
};