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:
c0mrade
2026-02-04 15:40:30 +03:00
parent bb32cd8757
commit 3a042473f0
2 changed files with 7 additions and 75 deletions

View File

@@ -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 */}

View File

@@ -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 */}