@webdevarif/dashui 0.3.2 → 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
@@ -3166,74 +3166,104 @@ function PostSidebarSection({
3166
3166
 
3167
3167
  // src/components/ui/hsl-color-input.tsx
3168
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
3171
  import { jsx as jsx49, jsxs as jsxs33 } from "react/jsx-runtime";
3171
3172
  function parseHsl(value) {
3172
3173
  if (!value) return { h: 0, s: 0, l: 0 };
3173
3174
  const parts = value.trim().split(/\s+/);
3174
- const h = parseFloat(parts[0]) || 0;
3175
- const s = parseFloat(parts[1]) || 0;
3176
- const l = parseFloat(parts[2]) || 0;
3177
- return { h, s, l };
3175
+ return {
3176
+ h: parseFloat(parts[0]) || 0,
3177
+ s: parseFloat(parts[1]) || 0,
3178
+ l: parseFloat(parts[2]) || 0
3179
+ };
3178
3180
  }
3179
- function formatHsl(color) {
3180
- 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)}%`;
3181
3183
  }
3182
- function HslColorInput({
3183
- value,
3184
- onChange,
3185
- className,
3186
- inputClassName,
3187
- disabled
3188
- }) {
3184
+ function HslColorInput({ value, onChange, className, inputClassName, disabled }) {
3189
3185
  const [open, setOpen] = useState7(false);
3186
+ const [pos, setPos] = useState7({ top: 0, left: 0 });
3190
3187
  const [inputVal, setInputVal] = useState7(value);
3191
- const containerRef = useRef4(null);
3188
+ const [mounted, setMounted] = useState7(false);
3189
+ const triggerRef = useRef4(null);
3190
+ const pickerRef = useRef4(null);
3191
+ useEffect5(() => {
3192
+ setMounted(true);
3193
+ }, []);
3192
3194
  useEffect5(() => {
3193
3195
  setInputVal(value);
3194
3196
  }, [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
+ }, []);
3195
3206
  useEffect5(() => {
3196
3207
  if (!open) return;
3197
3208
  const handler = (e) => {
3198
- if (containerRef.current && !containerRef.current.contains(e.target)) {
3199
- setOpen(false);
3200
- }
3209
+ const target = e.target;
3210
+ if (pickerRef.current?.contains(target)) return;
3211
+ if (triggerRef.current?.contains(target)) return;
3212
+ setOpen(false);
3201
3213
  };
3202
- document.addEventListener("mousedown", handler);
3203
- return () => document.removeEventListener("mousedown", handler);
3214
+ document.addEventListener("pointerdown", handler, true);
3215
+ return () => document.removeEventListener("pointerdown", handler, true);
3204
3216
  }, [open]);
3205
- const hslColor = parseHsl(value);
3206
3217
  const cssColor = value ? `hsl(${value})` : "transparent";
3207
- const handlePickerChange = useCallback((color) => {
3208
- const formatted = formatHsl(color);
3209
- setInputVal(formatted);
3210
- onChange(formatted);
3211
- }, [onChange]);
3212
- const handleInputChange = (e) => {
3213
- const v = e.target.value;
3214
- setInputVal(v);
3215
- if (/^\d+\s+\d+%?\s+\d+%?$/.test(v.trim()) || /^\d+(\.\d+)?\s+\d+(\.\d+)?%\s+\d+(\.\d+)?%$/.test(v.trim())) {
3216
- 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
+ ]
3217
3250
  }
3218
- };
3219
- const handleInputBlur = () => {
3220
- setInputVal(value);
3221
- };
3222
- return /* @__PURE__ */ jsxs33("div", { ref: containerRef, className: cn("relative flex items-center gap-1.5", className), children: [
3251
+ );
3252
+ return /* @__PURE__ */ jsxs33("div", { className: cn("flex items-center gap-1.5", className), children: [
3223
3253
  /* @__PURE__ */ jsx49(
3224
3254
  "button",
3225
3255
  {
3256
+ ref: triggerRef,
3226
3257
  type: "button",
3227
3258
  disabled,
3228
- onClick: () => setOpen((o) => !o),
3259
+ onClick: () => open ? setOpen(false) : openPicker(),
3229
3260
  className: cn(
3230
3261
  "w-5 h-5 rounded border border-white/10 shrink-0 transition-all",
3231
- "hover:scale-110 hover:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20",
3262
+ "hover:scale-110 hover:border-white/30 focus:outline-none",
3232
3263
  disabled && "opacity-50 cursor-not-allowed"
3233
3264
  ),
3234
3265
  style: { background: cssColor },
3235
- title: value,
3236
- "aria-label": "Open color picker"
3266
+ "aria-label": "Pick color"
3237
3267
  }
3238
3268
  ),
3239
3269
  /* @__PURE__ */ jsx49(
@@ -3241,42 +3271,24 @@ function HslColorInput({
3241
3271
  {
3242
3272
  type: "text",
3243
3273
  value: inputVal,
3244
- onChange: handleInputChange,
3245
- onBlur: handleInputBlur,
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
+ },
3280
+ onBlur: () => setInputVal(value),
3246
3281
  disabled,
3247
- placeholder: "0 0% 0%",
3282
+ placeholder: "H S% L%",
3248
3283
  className: cn(
3249
3284
  "w-28 bg-white/5 border border-white/10 rounded px-1.5 py-0.5",
3250
- "text-xs text-white/70 font-mono outline-none",
3251
- "focus:border-white/30 placeholder:text-white/20",
3252
- "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",
3253
3287
  inputClassName
3254
3288
  )
3255
3289
  }
3256
3290
  ),
3257
- open && /* @__PURE__ */ jsxs33("div", { className: cn(
3258
- "absolute z-50 top-full left-0 mt-2",
3259
- "bg-[#1a1c2e] border border-white/10 rounded-xl shadow-2xl p-3",
3260
- "flex flex-col gap-3"
3261
- ), children: [
3262
- /* @__PURE__ */ jsx49(
3263
- HslColorPicker,
3264
- {
3265
- color: hslColor,
3266
- onChange: handlePickerChange
3267
- }
3268
- ),
3269
- /* @__PURE__ */ jsxs33("div", { className: "flex items-center gap-2", children: [
3270
- /* @__PURE__ */ jsx49(
3271
- "div",
3272
- {
3273
- className: "w-6 h-6 rounded border border-white/10 shrink-0",
3274
- style: { background: cssColor }
3275
- }
3276
- ),
3277
- /* @__PURE__ */ jsx49("span", { className: "text-xs font-mono text-white/50 truncate", children: value || "none" })
3278
- ] })
3279
- ] })
3291
+ mounted && open && createPortal(picker, document.body)
3280
3292
  ] });
3281
3293
  }
3282
3294