mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-28 09:33:46 +00:00
refactor: unify ColorPicker — remove native input, fix scroll closing
- Remove native <input type="color">, eyedropper button, colorInputRef, isTelegram check and isInTelegramWebApp import from ColorPicker - Remove scroll event listener so picker only closes on click/tap outside - Replace native color input in AdminWheel InlinePrizeForm with ColorPicker
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
import { useState, useRef, useEffect, useMemo, useCallback } from 'react';
|
||||
import { useState, useRef, useEffect, useCallback } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { isInTelegramWebApp } from '@/hooks/useTelegramSDK';
|
||||
|
||||
interface ColorPickerProps {
|
||||
value: string;
|
||||
@@ -111,9 +110,6 @@ export function ColorPicker({ value, onChange, label, description, disabled }: C
|
||||
|
||||
const buttonRef = useRef<HTMLButtonElement>(null);
|
||||
const pickerRef = useRef<HTMLDivElement>(null);
|
||||
const colorInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const isTelegram = useMemo(() => isInTelegramWebApp(), []);
|
||||
|
||||
// Sync with external value
|
||||
useEffect(() => {
|
||||
@@ -177,18 +173,15 @@ export function ColorPicker({ value, onChange, label, description, disabled }: C
|
||||
}
|
||||
};
|
||||
|
||||
const handleScroll = () => handleClose();
|
||||
const handleResize = () => updatePosition();
|
||||
|
||||
document.addEventListener('mousedown', handleClickOutside);
|
||||
document.addEventListener('touchstart', handleClickOutside as EventListener);
|
||||
window.addEventListener('scroll', handleScroll, true);
|
||||
window.addEventListener('resize', handleResize);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('mousedown', handleClickOutside);
|
||||
document.removeEventListener('touchstart', handleClickOutside as EventListener);
|
||||
window.removeEventListener('scroll', handleScroll, true);
|
||||
window.removeEventListener('resize', handleResize);
|
||||
};
|
||||
}, [isOpen, handleClose, updatePosition]);
|
||||
@@ -229,18 +222,6 @@ export function ColorPicker({ value, onChange, label, description, disabled }: C
|
||||
[hsl, updateColorFromHsl],
|
||||
);
|
||||
|
||||
// Handle native color input
|
||||
const handleColorInputChange = useCallback(
|
||||
(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const newColor = e.target.value;
|
||||
setLocalValue(newColor);
|
||||
const rgb = hexToRgb(newColor);
|
||||
setHsl(rgbToHsl(rgb.r, rgb.g, rgb.b));
|
||||
onChange(newColor);
|
||||
},
|
||||
[onChange],
|
||||
);
|
||||
|
||||
// Handle hex input
|
||||
const handleHexInputChange = useCallback(
|
||||
(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
@@ -409,41 +390,6 @@ export function ColorPicker({ value, onChange, label, description, disabled }: C
|
||||
placeholder="#000000"
|
||||
maxLength={7}
|
||||
/>
|
||||
|
||||
{/* Native color picker button (hidden in Telegram) */}
|
||||
{!isTelegram && (
|
||||
<>
|
||||
<input
|
||||
ref={colorInputRef}
|
||||
type="color"
|
||||
value={localValue || '#000000'}
|
||||
onChange={handleColorInputChange}
|
||||
disabled={disabled}
|
||||
className="sr-only"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => colorInputRef.current?.click()}
|
||||
disabled={disabled}
|
||||
className="flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-xl border border-dark-700 bg-dark-800 text-dark-400 transition-colors hover:bg-dark-700 hover:text-dark-200 disabled:opacity-50"
|
||||
title="System color picker"
|
||||
>
|
||||
<svg
|
||||
className="h-5 w-5"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
strokeWidth={1.5}
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M4.098 19.902a3.75 3.75 0 005.304 0l6.401-6.402M6.75 21A3.75 3.75 0 013 17.25V4.125C3 3.504 3.504 3 4.125 3h5.25c.621 0 1.125.504 1.125 1.125v4.072M6.75 21a3.75 3.75 0 003.75-3.75V8.197M6.75 21h13.125c.621 0 1.125-.504 1.125-1.125v-5.25c0-.621-.504-1.125-1.125-1.125h-4.072M10.5 8.197l2.88-2.88c.438-.439 1.15-.439 1.59 0l3.712 3.713c.44.44.44 1.152 0 1.59l-2.879 2.88M6.75 17.25h.008v.008H6.75v-.008z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Render picker in portal */}
|
||||
|
||||
@@ -23,6 +23,7 @@ import { CSS } from '@dnd-kit/utilities';
|
||||
import { adminWheelApi, type WheelPrizeAdmin, type CreateWheelPrizeData } from '../api/wheel';
|
||||
import { useDestructiveConfirm } from '@/platform';
|
||||
import FortuneWheel from '../components/wheel/FortuneWheel';
|
||||
import { ColorPicker } from '@/components/ColorPicker';
|
||||
import { useBackButton } from '../platform/hooks/useBackButton';
|
||||
import { usePlatform } from '../platform/hooks/usePlatform';
|
||||
|
||||
@@ -1146,26 +1147,11 @@ function InlinePrizeForm({
|
||||
</div>
|
||||
|
||||
{/* Color */}
|
||||
<div>
|
||||
<label className="mb-1 block text-sm font-medium text-dark-300">
|
||||
{t('admin.wheel.prizes.fields.color')}
|
||||
</label>
|
||||
<div className="flex gap-2">
|
||||
<input
|
||||
type="color"
|
||||
value={formData.color}
|
||||
onChange={(e) => setFormData({ ...formData, color: e.target.value })}
|
||||
className="h-10 w-12 cursor-pointer rounded"
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
value={formData.color}
|
||||
onChange={(e) => setFormData({ ...formData, color: e.target.value })}
|
||||
className="input flex-1"
|
||||
pattern="^#[0-9A-Fa-f]{6}$"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<ColorPicker
|
||||
label={t('admin.wheel.prizes.fields.color')}
|
||||
value={formData.color}
|
||||
onChange={(color) => setFormData({ ...formData, color })}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Active toggle */}
|
||||
|
||||
Reference in New Issue
Block a user