import { BlurView } from "expo-blur"; import React, { useEffect, useMemo, useState } from "react"; import { FlatList, Platform, SectionList, Text, View } from "react-native"; import useLayoutType from "../../hooks/useLayoutType"; import CreateLyricsHeader from "../../screens/Writing/components/CreateLyricsHeader"; import { Palette } from "../../styles"; import { FONT_FAMILY } from "../../styles/Fonts"; import { LinearGradient } from "../LinearGradient/LinearGradient"; const BLUE_SELECTION_GRADIENT = ["#4673F9", "#7023F7"]; const buildItemKey = (value, category) => category ? `${category}::${value}` : `${value}`; const ListSelection = ({ options = [], variant = "simple", selected: selectedProp, setSelected: setSelectedProp, multiple = false, maxSelection, getItemValue, selectedValueExtractor, formatSelectedValue, // styles contentContainerStyle, itemContainerStyle, itemOuterStyle, itemTextStyle, highlightColor = "#F94697", disableHover = false, }) => { // état interne si non contrôlé const [internalSelected, setInternalSelected] = useState( variant === "sectioned" ? {} : multiple ? [] : null ); const selected = selectedProp !== undefined ? selectedProp : internalSelected; const setSelected = setSelectedProp !== undefined ? setSelectedProp : setInternalSelected; const { isWeb } = useLayoutType(); const [hoveredKey, setHoveredKey] = useState(null); useEffect(() => { if (disableHover && hoveredKey !== null) setHoveredKey(null); }, [disableHover, hoveredKey]); // helpers const valueOf = useMemo(() => { if (typeof getItemValue === "function") return getItemValue; if (variant === "simple") return (item) => item; return (item) => item?.title ?? item; }, [getItemValue, variant]); const formatValue = (item) => typeof formatSelectedValue === "function" ? formatSelectedValue(item) : valueOf(item); const extractSelectedComparable = (s) => { if (typeof selectedValueExtractor === "function") return selectedValueExtractor(s); if (s && typeof s === "object" && "title" in s) return s.title; return s; }; const isSelected = (val, category) => { if (variant === "sectioned") return selected?.[category] === val; if (multiple) { const list = Array.isArray(selected) ? selected : []; return list.some((v) => v === val); } return extractSelectedComparable(selected) === val; }; const toggleSelect = (item, category) => { const val = valueOf(item); if (variant === "sectioned") { const current = selected && typeof selected === "object" ? selected : {}; const next = { ...current }; if (current?.[category] === val) next[category] = null; else next[category] = formatValue(item); setSelected(next); return; } if (multiple) { const list = Array.isArray(selected) ? selected : []; const exists = list.some((v) => v === val); if (exists) setSelected(list.filter((v) => v !== val)); else if (!maxSelection || list.length < maxSelection) setSelected([...list, formatValue(item)]); } else { if (extractSelectedComparable(selected) === val) setSelected(null); else setSelected(formatValue(item)); } }; // contenus élémentaires : toujours encapsuler le texte dans const renderSimpleContent = (label) => ( {typeof label === "string" ? label : String(label)} ); const renderTitleDescriptionContent = (item) => ( {item?.title} {` : ${item?.description ?? ""}`} ); // applique le gradient bleu UNIQUEMENT sur web et quand sélectionné const withWebBlueGradientIfSelected = (content, sel) => { if (!isWeb || !sel) return content; // wrap dans un View pour éviter texte brut direct sous le gradient (compat RN Web) return ( {content} ); }; // ============ SECTIONED ============ if (variant === "sectioned") { return ( { const cat = section?.title; const val = valueOf(item); const sel = isSelected(val, cat); const itemKey = buildItemKey(String(val ?? ""), cat); const isHovered = isWeb && !disableHover && hoveredKey === itemKey; const showHoverOutline = isHovered && !sel; const baseContent = renderSimpleContent(item); const headerContainerStyle = { borderWidth: 0, borderColor: "transparent", borderRadius: 14, ...itemContainerStyle, }; if (sel && isWeb) headerContainerStyle.backgroundColor = "transparent"; return ( setHoveredKey(itemKey) : undefined } onMouseLeave={ isWeb && !disableHover ? () => setHoveredKey(null) : undefined } > toggleSelect(item, cat)} tint={sel ? "default" : "dark"} colors={[Palette.tran, Palette.tran]} showBorder={!sel} disableBlur={sel && isWeb} blurViewStyle={ sel && isWeb ? { paddingHorizontal: 0, paddingVertical: 0 } : undefined } containerStyle={headerContainerStyle} > {withWebBlueGradientIfSelected(baseContent, sel)} {showHoverOutline && ( )} ); }} renderSectionHeader={({ section: { title } }) => ( {title} )} keyExtractor={(item, idx) => `${valueOf(item)}-${idx}`} /> ); } // ============ FLAT (simple/titleDescription/emotion) ============ return ( { const val = valueOf(item); const sel = isSelected(val); const useEmotionColors = variant === "emotion"; // pour "emotion", on masque le bord gauche si sélectionné const borderColors = useEmotionColors ? sel ? [Palette.tran, Palette.tran] : item?.color : [Palette.tran, Palette.tran]; const tint = useEmotionColors ? "dark" : sel ? "default" : "dark"; const itemKey = buildItemKey(String(val ?? "")); const isHovered = isWeb && !disableHover && hoveredKey === itemKey; const showHoverOutline = isHovered && !sel; const showSelectionOutline = !isWeb && (variant === "simple" || variant === "emotion" || variant === "titleDescription") && sel; const baseContent = variant === "simple" ? renderSimpleContent(item) : renderTitleDescriptionContent(item); const headerContainerStyle = { borderWidth: 0, borderColor: "transparent", borderRadius: 14, ...itemContainerStyle, }; if (sel && isWeb) headerContainerStyle.backgroundColor = "transparent"; const shouldShowBorder = !sel; return ( setHoveredKey(itemKey) : undefined } onMouseLeave={ isWeb && !disableHover ? () => setHoveredKey(null) : undefined } > toggleSelect(item)} gradientProps={ useEmotionColors && !sel ? { locations: [0.24, 1] } : undefined } showBorder={shouldShowBorder} disableBlur={sel && isWeb} // web: pas de blur si gradient blurViewStyle={ sel && isWeb ? { paddingHorizontal: 0, paddingVertical: 0 } : undefined } containerStyle={headerContainerStyle} > {withWebBlueGradientIfSelected(baseContent, sel)} {(showHoverOutline || showSelectionOutline) && ( )} ); }} keyExtractor={(item, idx) => `${valueOf(item)}-${idx}`} /> ); }; export default ListSelection;