/** * La griglia delle icone del catalogo, divisa nei gruppi del catalogo * (piatti, secondi, verdure, frutta, ingredienti, dolci, bevande, occasioni). * Senza titoli, per scelta di Paolo: solo icone, e fra un gruppo e l'altro * spazio e una riga sottile. Niente ricerca, niente caricamento, niente rete. * * Il disegno lo fa Icona.tsx, a partire dalla chiave che sta nel database. * * Ogni cella si annuncia col nome tradotto dell'icona, `icone.`: la * chiave del catalogo è italiana e non si traduce, e messa lì com'è farebbe * sentire sessanta parole italiane a chi usa l'app in inglese. Il nome è l'unica * cosa che ha chi la griglia non la vede: il disegno non si descrive da sé. * * I colori non si scrivono qui: vengono da tema.ts, come in tutte le schermate. */ import { useMemo, useRef } from 'react'; import { Pressable, StyleSheet, View } from 'react-native'; import Icona from './Icona.tsx'; import { GRUPPI_ICONE, ICONE_DA_SCEGLIERE } from '../../domain/icone.ts'; import type { ChiaveIcona } from '../../domain/icone.ts'; import { useApp } from '../contesto.ts'; import type { Colori } from '../tema.ts'; import { RAGGIO, SPAZIO, useColori } from '../tema.ts'; export type PropsSceltaIcona = { scelta: ChiaveIcona; onScegli: (chiave: ChiaveIcona) => void; /** * Dove comincia il gruppo dell'icona scelta, detto una volta sola appena la * griglia si dispone: chi la tiene in una ScrollView ci scorre, così chi * riapre una categoria vede subito la sua icona invece dei primi gruppi. */ onGruppoScelto?: (y: number) => void; }; export function SceltaIcona({ scelta, onScegli, onGruppoScelto }: PropsSceltaIcona) { const { testo } = useApp(); const colori = useColori(); const stili = useMemo(() => creaStili(colori), [colori]); // Solo la prima volta: scegliendo un'icona di un altro gruppo la griglia non // deve saltare sotto il dito. const detto = useRef(false); const gruppoScelto = ICONE_DA_SCEGLIERE.find((v) => v.chiave === scelta)?.gruppo; return ( {GRUPPI_ICONE.map((gruppo, i) => ( 0 ? stili.separato : null} onLayout={(e) => { if (gruppo !== gruppoScelto || detto.current) return; detto.current = true; onGruppoScelto?.(e.nativeEvent.layout.y); }} > {ICONE_DA_SCEGLIERE.filter((voce) => voce.gruppo === gruppo).map((voce) => { const attiva = voce.chiave === scelta; return ( onScegli(voce.chiave)} style={[stili.cella, attiva ? stili.cellaScelta : null]} accessibilityRole="button" accessibilityState={{ selected: attiva }} accessibilityLabel={testo(`icone.${voce.chiave}`)} > ); })} ))} ); } const creaStili = (colori: Colori) => StyleSheet.create({ gruppi: { gap: SPAZIO.m }, separato: { paddingTop: SPAZIO.m, borderTopWidth: StyleSheet.hairlineWidth, borderTopColor: colori.bordo }, griglia: { flexDirection: 'row', flexWrap: 'wrap', gap: SPAZIO.s }, cella: { width: 44, height: 44, borderRadius: RAGGIO.campo, alignItems: 'center', justifyContent: 'center', backgroundColor: colori.superficie, borderWidth: StyleSheet.hairlineWidth, borderColor: colori.bordo, }, cellaScelta: { backgroundColor: colori.accento, borderColor: colori.accento }, });