@webdevarif/dashui 0.3.3 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -3165,9 +3165,9 @@ function PostSidebarSection({
3165
3165
  }
3166
3166
 
3167
3167
  // src/components/ui/hsl-color-input.tsx
3168
- import { useState as useState7, useEffect as useEffect5, useCallback } from "react";
3168
+ import { useState as useState7, useRef as useRef4, useEffect as useEffect5, useCallback } from "react";
3169
+ import { createPortal } from "react-dom";
3169
3170
  import { HslColorPicker } from "react-colorful";
3170
- import * as Popover2 from "@radix-ui/react-popover";
3171
3171
  import { jsx as jsx49, jsxs as jsxs33 } from "react/jsx-runtime";
3172
3172
  function parseHsl(value) {
3173
3173
  if (!value) return { h: 0, s: 0, l: 0 };
@@ -3178,83 +3178,117 @@ function parseHsl(value) {
3178
3178
  l: parseFloat(parts[2]) || 0
3179
3179
  };
3180
3180
  }
3181
- function formatHsl(color) {
3182
- return `${Math.round(color.h)} ${Math.round(color.s)}% ${Math.round(color.l)}%`;
3181
+ function formatHsl(c) {
3182
+ return `${Math.round(c.h)} ${Math.round(c.s)}% ${Math.round(c.l)}%`;
3183
3183
  }
3184
3184
  function HslColorInput({ value, onChange, className, inputClassName, disabled }) {
3185
+ const [open, setOpen] = useState7(false);
3186
+ const [pos, setPos] = useState7({ top: 0, left: 0 });
3185
3187
  const [inputVal, setInputVal] = useState7(value);
3188
+ const [mounted, setMounted] = useState7(false);
3189
+ const triggerRef = useRef4(null);
3190
+ const pickerRef = useRef4(null);
3191
+ useEffect5(() => {
3192
+ setMounted(true);
3193
+ }, []);
3186
3194
  useEffect5(() => {
3187
3195
  setInputVal(value);
3188
3196
  }, [value]);
3189
- const hslColor = parseHsl(value);
3197
+ const openPicker = useCallback(() => {
3198
+ const rect = triggerRef.current?.getBoundingClientRect();
3199
+ if (!rect) return;
3200
+ const pickerW = 228;
3201
+ let left = rect.left;
3202
+ if (left + pickerW > window.innerWidth - 8) left = window.innerWidth - pickerW - 8;
3203
+ setPos({ top: rect.bottom + 6, left: Math.max(8, left) });
3204
+ setOpen(true);
3205
+ }, []);
3206
+ useEffect5(() => {
3207
+ if (!open) return;
3208
+ const handler = (e) => {
3209
+ const target = e.target;
3210
+ if (pickerRef.current?.contains(target)) return;
3211
+ if (triggerRef.current?.contains(target)) return;
3212
+ setOpen(false);
3213
+ };
3214
+ document.addEventListener("pointerdown", handler, true);
3215
+ return () => document.removeEventListener("pointerdown", handler, true);
3216
+ }, [open]);
3190
3217
  const cssColor = value ? `hsl(${value})` : "transparent";
3191
- const handlePickerChange = useCallback((color) => {
3192
- const formatted = formatHsl(color);
3193
- setInputVal(formatted);
3194
- onChange(formatted);
3195
- }, [onChange]);
3196
- const handleInputChange = (e) => {
3197
- const v = e.target.value;
3198
- setInputVal(v);
3199
- if (/^\d+(\.\d+)?\s+\d+(\.\d+)?%?\s+\d+(\.\d+)?%?$/.test(v.trim())) {
3200
- onChange(v);
3218
+ const picker = /* @__PURE__ */ jsxs33(
3219
+ "div",
3220
+ {
3221
+ ref: pickerRef,
3222
+ style: { position: "fixed", top: pos.top, left: pos.left, zIndex: 9999 },
3223
+ className: "bg-[#1a1c2e] border border-white/10 rounded-xl shadow-2xl p-3 flex flex-col gap-3",
3224
+ onPointerDown: (e) => e.stopPropagation(),
3225
+ children: [
3226
+ /* @__PURE__ */ jsx49(
3227
+ HslColorPicker,
3228
+ {
3229
+ color: parseHsl(value),
3230
+ onChange: (c) => {
3231
+ const formatted = formatHsl(c);
3232
+ setInputVal(formatted);
3233
+ onChange(formatted);
3234
+ }
3235
+ }
3236
+ ),
3237
+ /* @__PURE__ */ jsxs33("div", { className: "flex items-center gap-2 px-1", children: [
3238
+ /* @__PURE__ */ jsx49("div", { className: "w-5 h-5 rounded border border-white/10 shrink-0", style: { background: cssColor } }),
3239
+ /* @__PURE__ */ jsx49("span", { className: "text-xs font-mono text-white/40 flex-1 truncate", children: value || "none" }),
3240
+ /* @__PURE__ */ jsx49(
3241
+ "button",
3242
+ {
3243
+ onClick: () => setOpen(false),
3244
+ className: "text-white/30 hover:text-white/70 text-xs px-1.5 py-0.5 rounded hover:bg-white/5 transition-colors",
3245
+ children: "Done"
3246
+ }
3247
+ )
3248
+ ] })
3249
+ ]
3201
3250
  }
3202
- };
3251
+ );
3203
3252
  return /* @__PURE__ */ jsxs33("div", { className: cn("flex items-center gap-1.5", className), children: [
3204
- /* @__PURE__ */ jsxs33(Popover2.Root, { children: [
3205
- /* @__PURE__ */ jsx49(Popover2.Trigger, { asChild: true, disabled, children: /* @__PURE__ */ jsx49(
3206
- "button",
3207
- {
3208
- type: "button",
3209
- className: cn(
3210
- "w-5 h-5 rounded border border-white/10 shrink-0 transition-all",
3211
- "hover:scale-110 hover:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20",
3212
- disabled && "opacity-50 cursor-not-allowed"
3213
- ),
3214
- style: { background: cssColor },
3215
- "aria-label": "Open color picker"
3216
- }
3217
- ) }),
3218
- /* @__PURE__ */ jsx49(Popover2.Portal, { children: /* @__PURE__ */ jsxs33(
3219
- Popover2.Content,
3220
- {
3221
- sideOffset: 8,
3222
- align: "start",
3223
- className: cn(
3224
- "z-[9999] rounded-xl shadow-2xl p-3",
3225
- "bg-[#1a1c2e] border border-white/10",
3226
- "flex flex-col gap-3",
3227
- "animate-in fade-in-0 zoom-in-95"
3228
- ),
3229
- children: [
3230
- /* @__PURE__ */ jsx49(HslColorPicker, { color: hslColor, onChange: handlePickerChange }),
3231
- /* @__PURE__ */ jsxs33("div", { className: "flex items-center gap-2", children: [
3232
- /* @__PURE__ */ jsx49("div", { className: "w-6 h-6 rounded border border-white/10 shrink-0", style: { background: cssColor } }),
3233
- /* @__PURE__ */ jsx49("span", { className: "text-xs font-mono text-white/50 truncate", children: value || "none" })
3234
- ] }),
3235
- /* @__PURE__ */ jsx49(Popover2.Arrow, { className: "fill-white/10" })
3236
- ]
3237
- }
3238
- ) })
3239
- ] }),
3253
+ /* @__PURE__ */ jsx49(
3254
+ "button",
3255
+ {
3256
+ ref: triggerRef,
3257
+ type: "button",
3258
+ disabled,
3259
+ onClick: () => open ? setOpen(false) : openPicker(),
3260
+ className: cn(
3261
+ "w-5 h-5 rounded border border-white/10 shrink-0 transition-all",
3262
+ "hover:scale-110 hover:border-white/30 focus:outline-none",
3263
+ disabled && "opacity-50 cursor-not-allowed"
3264
+ ),
3265
+ style: { background: cssColor },
3266
+ "aria-label": "Pick color"
3267
+ }
3268
+ ),
3240
3269
  /* @__PURE__ */ jsx49(
3241
3270
  "input",
3242
3271
  {
3243
3272
  type: "text",
3244
3273
  value: inputVal,
3245
- onChange: handleInputChange,
3274
+ onChange: (e) => {
3275
+ setInputVal(e.target.value);
3276
+ if (/^\d+(\.\d+)?\s+\d+(\.\d+)?%\s+\d+(\.\d+)?%$/.test(e.target.value.trim())) {
3277
+ onChange(e.target.value.trim());
3278
+ }
3279
+ },
3246
3280
  onBlur: () => setInputVal(value),
3247
3281
  disabled,
3248
3282
  placeholder: "H S% L%",
3249
3283
  className: cn(
3250
3284
  "w-28 bg-white/5 border border-white/10 rounded px-1.5 py-0.5",
3251
- "text-xs text-white/70 font-mono outline-none",
3252
- "focus:border-white/30 placeholder:text-white/20",
3253
- "disabled:opacity-50 disabled:cursor-not-allowed",
3285
+ "text-xs text-white/70 font-mono outline-none focus:border-white/30",
3286
+ "placeholder:text-white/20 disabled:opacity-50",
3254
3287
  inputClassName
3255
3288
  )
3256
3289
  }
3257
- )
3290
+ ),
3291
+ mounted && open && createPortal(picker, document.body)
3258
3292
  ] });
3259
3293
  }
3260
3294