/** * La griglia delle icone del catalogo, divisa nei gruppi del catalogo * (piatti, dolci, ingredienti, bevande, occasioni): sono più di sessanta, e * col titolo del gruppo si trova quella che si cerca senza scorrerle tutte. * 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, Text, View } from 'react-native'; import Icona from './Icona.tsx'; import { GRUPPI_ICONE, ICONE } 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, TESTO, 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.find((v) => v.chiave === scelta)?.gruppo; return ( {GRUPPI_ICONE.map((gruppo) => ( { if (gruppo !== gruppoScelto || detto.current) return; detto.current = true; onGruppoScelto?.(e.nativeEvent.layout.y); }} > {testo(`icone.gruppo.${gruppo}`)} {ICONE.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 }, gruppo: { gap: SPAZIO.xs }, titolo: { ...TESTO.piccolo, fontWeight: '600', color: colori.testoTenue }, 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 }, });