/
kan64
/
spreadsheet-lab
Обзор
Документация
Войти
/
kan64
/
spreadsheet-lab
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
develop
client/src/components/BorderPicker.tsx
186 строк
6 KB
Anton Kravchenkov
first_commit
18 фев 2026, 23:41
18 фев 2026, 23:41
42314b2
Код
Авторство
О чём код?
import React, { useState, useRef, useEffect } from 'react'; export type BorderPreset = | 'all' | 'outer' | 'top' | 'bottom' | 'left' | 'right' | 'inner-horizontal' | 'inner-vertical' | 'none'; interface BorderPickerProps { borderColor: string; onBorderColorChange: (color: string) => void; onApplyBorder: (preset: BorderPreset) => void; disabled?: boolean; } const presets: { id: BorderPreset; label: string; icon: React.ReactNode }[] = [ { id: 'all', label: 'Все границы', icon: ( <svg width="18" height="18" viewBox="0 0 18 18" fill="none" stroke="currentColor" strokeWidth="1.5"> <rect x="1" y="1" width="16" height="16" /> <line x1="9" y1="1" x2="9" y2="17" /> <line x1="1" y1="9" x2="17" y2="9" /> </svg> ), }, { id: 'outer', label: 'Внешние границы', icon: ( <svg width="18" height="18" viewBox="0 0 18 18" fill="none" stroke="currentColor" strokeWidth="1.5"> <rect x="1" y="1" width="16" height="16" /> </svg> ), }, { id: 'top', label: 'Верхняя граница', icon: ( <svg width="18" height="18" viewBox="0 0 18 18" fill="none" stroke="currentColor" strokeWidth="1.5"> <line x1="1" y1="1" x2="17" y2="1" /> <rect x="1" y="1" width="16" height="16" stroke="none" fill="none" /> <line x1="1" y1="1" x2="1" y2="17" strokeOpacity="0.2" /> <line x1="17" y1="1" x2="17" y2="17" strokeOpacity="0.2" /> <line x1="1" y1="17" x2="17" y2="17" strokeOpacity="0.2" /> </svg> ), }, { id: 'bottom', label: 'Нижняя граница', icon: ( <svg width="18" height="18" viewBox="0 0 18 18" fill="none" stroke="currentColor" strokeWidth="1.5"> <line x1="1" y1="17" x2="17" y2="17" /> <line x1="1" y1="1" x2="17" y2="1" strokeOpacity="0.2" /> <line x1="1" y1="1" x2="1" y2="17" strokeOpacity="0.2" /> <line x1="17" y1="1" x2="17" y2="17" strokeOpacity="0.2" /> </svg> ), }, { id: 'left', label: 'Левая граница', icon: ( <svg width="18" height="18" viewBox="0 0 18 18" fill="none" stroke="currentColor" strokeWidth="1.5"> <line x1="1" y1="1" x2="1" y2="17" /> <line x1="1" y1="1" x2="17" y2="1" strokeOpacity="0.2" /> <line x1="17" y1="1" x2="17" y2="17" strokeOpacity="0.2" /> <line x1="1" y1="17" x2="17" y2="17" strokeOpacity="0.2" /> </svg> ), }, { id: 'right', label: 'Правая граница', icon: ( <svg width="18" height="18" viewBox="0 0 18 18" fill="none" stroke="currentColor" strokeWidth="1.5"> <line x1="17" y1="1" x2="17" y2="17" /> <line x1="1" y1="1" x2="17" y2="1" strokeOpacity="0.2" /> <line x1="1" y1="1" x2="1" y2="17" strokeOpacity="0.2" /> <line x1="1" y1="17" x2="17" y2="17" strokeOpacity="0.2" /> </svg> ), }, { id: 'none', label: 'Убрать границы', icon: ( <svg width="18" height="18" viewBox="0 0 18 18" fill="none" stroke="currentColor" strokeWidth="1.5" strokeOpacity="0.25"> <rect x="1" y="1" width="16" height="16" /> <line x1="3" y1="3" x2="15" y2="15" stroke="currentColor" strokeOpacity="0.6" /> </svg> ), }, ]; export const BorderPicker: React.FC<BorderPickerProps> = ({ borderColor, onBorderColorChange, onApplyBorder, disabled, }) => { const [open, setOpen] = useState(false); const wrapperRef = useRef<HTMLDivElement>(null); useEffect(() => { const handleClickOutside = (e: MouseEvent) => { if (wrapperRef.current && !wrapperRef.current.contains(e.target as Node)) { setOpen(false); } }; if (open) { document.addEventListener('mousedown', handleClickOutside); } return () => document.removeEventListener('mousedown', handleClickOutside); }, [open]); return ( <div className="border-picker-wrapper" ref={wrapperRef}> <button className="toolbar-btn" onClick={() => setOpen(!open)} disabled={disabled} title="Границы" > <svg width="16" height="16" viewBox="0 0 18 18" fill="none" stroke="currentColor" strokeWidth="1.5"> <rect x="1" y="1" width="16" height="16" /> <line x1="9" y1="1" x2="9" y2="17" /> <line x1="1" y1="9" x2="17" y2="9" /> </svg> <svg width="8" height="8" viewBox="0 0 8 8" fill="currentColor" style={{ position: 'absolute', bottom: 2, right: 2 }} > <path d="M1 2.5 L4 5.5 L7 2.5" /> </svg> <div className="color-indicator" style={{ backgroundColor: borderColor, position: 'absolute', bottom: 0, left: 4, right: 4, height: 3, borderRadius: 1 }} /> </button> {open && ( <div className="border-picker-dropdown"> <div className="border-picker-grid"> {presets.map((preset) => ( <button key={preset.id} className="border-picker-option" title={preset.label} onClick={() => { onApplyBorder(preset.id); setOpen(false); }} > {preset.icon} </button> ))} </div> <div className="border-picker-separator" /> <div className="border-picker-color-row"> <span className="border-picker-color-label">Цвет границы:</span> <label className="border-picker-color-swatch"> <div className="border-picker-color-preview" style={{ backgroundColor: borderColor }} /> <input type="color" value={borderColor} onChange={(e) => onBorderColorChange(e.target.value)} className="color-input" /> </label> </div> </div> )} </div> ); };