IGRPForm com validação Zod e os campos do Design System — texto, seleção, datas, ficheiros e listas repetitivas — vistos pelo código e pelas props.
Um formulário do iGRP é sempre um IGRPForm com um esquema Zod, e cada campo é um componente IGRP* ligado ao formulário pelo name. É isto que o Studio gera quando arrastas o widget Form e os seus campos no Page Builder; esta página mostra o mesmo do lado do código.
Secção em construção
Esta página cobre os componentes principais; o resto do catálogo será completado. Para configurar estes componentes no Studio (sem código), vê Widgets do Page Builder.
Como funciona um formulário
O IGRPForm cria o formulário (react-hook-form + zodResolver) e partilha-o com os campos que tem dentro. Cada campo funciona num de dois modos:
| Modo | Quando | Como |
|---|---|---|
| Ligado ao formulário | Dentro de um IGRPForm | Lê e escreve o valor pelo name, que tem de existir no esquema; o erro de validação aparece no campo |
| Controlado | Fora de um IGRPForm | O valor é teu, por value / onChange (ou onValueChange, onDateChange…) |
'use client';
import { useRef } from 'react';
import { z } from 'zod';
import {
IGRPButton,
IGRPForm,
type IGRPFormHandle,
IGRPInputText,
IGRPSelect,
IGRPDatePickerSingle,
} from '@igrp/igrp-framework-react-design-system';
const schema = z.object({
nome: z.string().min(1, 'Obrigatório'),
email: z.string().email('Email inválido'),
categoria: z.string().min(1, 'Escolhe uma categoria'),
dataInicio: z.date(),
});
export function RegistoForm() {
const formRef = useRef<IGRPFormHandle<typeof schema>>(null);
return (
<IGRPForm
schema={schema}
formRef={formRef}
defaultValues={{ nome: '', email: '', categoria: '' }}
onSubmit={async (values) => {
await guardar(values); // values já validados e tipados pelo esquema
}}
showToastOnError
>
<IGRPInputText name="nome" label="Nome" required />
<IGRPInputText name="email" type="email" label="Email" />
<IGRPSelect
name="categoria"
label="Categoria"
options={[
{ label: 'Geral', value: 'geral' },
{ label: 'Urgente', value: 'urgente' },
]}
/>
<IGRPDatePickerSingle name="dataInicio" label="Data de início" />
<IGRPButton type="submit">Guardar</IGRPButton>
</IGRPForm>
);
}O formRef dá acesso ao formulário a partir de fora — por exemplo, um botão no cabeçalho da página que submete: formRef.current?.submit().
Componentes principais
IGRPForm
O contentor do formulário: valida com o esquema Zod, chama onSubmit só com valores válidos e mostra o erro global se a submissão falhar.
| Prop | Tipo | Por defeito | Descrição |
|---|---|---|---|
schema | z.ZodType | — | Esquema Zod de validação (obrigatório) |
onSubmit | (values) => void | Promise<void> | — | Chamado com os valores validados (obrigatório) |
formRef | RefObject<IGRPFormHandle | null> | — | Acesso a submit(), reset(), setGlobalError(), isSubmitting e à API do react-hook-form (obrigatório) |
defaultValues | z.input<schema> | — | Valores iniciais |
validationMode | 'onSubmit' | 'onChange' | 'onBlur' | 'onTouched' | 'all' | 'onSubmit' | Quando validar |
resetAfterSubmit | boolean | false | Repõe os defaultValues depois de submeter com sucesso |
showToastOnError | boolean | false | Mostra um toast quando o onSubmit lança erro |
resetKey | React.Key | — | Muda-o (por exemplo, o id do registo) para recarregar o formulário com novos defaultValues |
Se o onSubmit lançar uma exceção, a mensagem aparece no topo do formulário; também a podes definir tu com formRef.current?.setGlobalError('…'). Para a configuração no Page Builder, ver Widget Formulário.
IGRPInputText
Campo de texto com label, texto de ajuda, ícone opcional e mensagem de erro.
Gerado pelo Studio| Prop | Tipo | Por defeito | Descrição |
|---|---|---|---|
name | string | — | Nome do campo no esquema |
label | string | — | Label do campo |
type | 'text' | 'email' | 'number' | 'text' | Tipo do input |
helperText | string | — | Texto de ajuda por baixo do campo |
required | boolean | — | Marca o campo como obrigatório |
showIcon / iconName | boolean / string | — | Ícone Lucide dentro do campo |
iconPlacement | 'start' | 'end' | 'center' | — | Posição do ícone |
error | string | — | Mensagem de erro que sobrepõe a do formulário |
<IGRPInputText
name="email"
type="email"
label="Email"
helperText="Usado para notificações"
showIcon
iconName="Mail"
/>IGRPSelect
Lista de escolha única a partir de um array de opções, com pesquisa e agrupamento opcionais.
Gerado pelo Studio| Prop | Tipo | Por defeito | Descrição |
|---|---|---|---|
name | string | — | Nome do campo no esquema |
options | IGRPOptionsProps[] | — | Opções (label, value e opcionalmente group, icon, status, description, disabled) |
label | string | — | Label do campo |
placeholder | string | — | Texto quando não há valor |
showSearch | boolean | — | Campo de pesquisa dentro da lista |
showGroup | boolean | — | Agrupa as opções pelo seu group |
value / onValueChange | string / (value: string) => void | — | Modo controlado, fora de um IGRPForm |
<IGRPSelect
name="distrito"
label="Distrito"
placeholder="Escolhe…"
showSearch
options={[
{ label: 'Norte', value: 'norte', group: 'Continente' },
{ label: 'Sul', value: 'sul', group: 'Continente' },
]}
showGroup
/>O mesmo formato de opções (IGRPOptionsProps) serve IGRPCombobox, IGRPMultiSelect, IGRPRadioGroup e o seletor do IGRPInputAddOn.
IGRPDatePickerSingle
Seleção de uma data num calendário em popover, com botão para limpar.
Gerado pelo Studio| Prop | Tipo | Por defeito | Descrição |
|---|---|---|---|
name | string | — | Nome do campo no esquema (o valor é um Date) |
label | string | — | Label do campo |
dateFormat | string | 'dd-MM-yyyy' | Formato de apresentação (date-fns) |
placeholder | string | — | Texto quando não há data |
disableBefore / disableAfter | Date | — | Limita as datas que se podem escolher |
disableDayOfWeek | number | number[] | — | Dias da semana bloqueados (0 = domingo) |
date / onDateChange | Date / (date?: Date) => void | — | Modo controlado, fora de um IGRPForm |
<IGRPDatePickerSingle
name="dataInicio"
label="Data de início"
disableBefore={new Date()}
disableDayOfWeek={[0, 6]}
/>Quando a data é limpa, o valor do campo fica null — o esquema deve aceitá-lo se o campo for opcional (z.date().nullable()).
IGRPFormList
Lista repetitiva dentro de um formulário: cada item é um cartão colapsável com os seus campos, com botões para adicionar e remover.
Gerado pelo Studio| Prop | Tipo | Por defeito | Descrição |
|---|---|---|---|
id | string | — | Nome do array no esquema (obrigatório) |
renderItem | (item, index) => ReactNode | — | Os campos de cada item (obrigatório) |
defaultItem | TItem | — | Valores de um item novo |
label / description | string | — | Título e descrição da lista |
computeLabel | (item, index) => string | — | Título de cada cartão |
addButtonLabel | string | — | Texto do botão de adicionar |
allowEmpty | boolean | false | Permite ficar sem itens (por defeito mantém pelo menos um) |
allowMultipleOpen | boolean | false | Vários itens abertos ao mesmo tempo |
const schema = z.object({
contactos: z.array(z.object({ nome: z.string().min(1), telefone: z.string() })),
});
<IGRPFormList
id="contactos"
label="Contactos"
addButtonLabel="Adicionar contacto"
defaultItem={{ nome: '', telefone: '' }}
computeLabel={(item, i) => item.nome || `Contacto ${i + 1}`}
renderItem={(_item, index) => (
<>
<IGRPInputText name={`contactos.${index}.nome`} label="Nome" />
<IGRPInputPhone name={`contactos.${index}.telefone`} label="Telefone" />
</>
)}
/>Dentro de um IGRPForm, cada campo do item escreve no formulário pelo seu próprio name (contactos.0.nome…). Fora de um formulário, usa value / onChange e o terceiro argumento de renderItem.
Outros componentes de formulário
Todos seguem o mesmo padrão: name, label, helperText, required e ligação automática ao IGRPForm.
| Componente | Para quê | Widget no Studio |
|---|---|---|
IGRPInputNumber | Número formatado (Intl.NumberFormatOptions), com min / max | Number |
IGRPInputPassword | Palavra-passe com botão para mostrar (showPasswordToggle) | Password |
IGRPInputUrl | URL com seletor de protocolo | URL |
IGRPInputSearch | Pesquisa com debounce e botão de submeter | Input Search |
IGRPInputPhone | Telefone com país e formato internacional | Phone |
IGRPInputColor | Seletor de cor (oklch, hex…) | Color |
IGRPInputAddOn | Campo de texto com um seletor acoplado | Add On |
IGRPInputHidden | Valor escondido que segue no formulário | Input Hidden |
IGRPTextarea | Texto em várias linhas | Textarea |
IGRPCombobox | Escolha com pesquisa sobre muitas opções | Combobox |
IGRPMultiSelect | Escolha múltipla | — |
IGRPCheckbox | Caixa de verificação | Checkbox |
IGRPSwitch | Interruptor ligado/desligado | Switch |
IGRPRadioGroup | Escolha única visível; variant="card" mostra cartões de opção | Radio |
IGRPInputTime | Hora (HH:mm) | Time |
IGRPDateTimeInput | Data e hora num só campo | — |
IGRPDatePickerRange / IGRPDatePickerMultiple | Intervalo de datas / várias datas | Date Range / Date Multiple |
IGRPDatePickerInputSingle | Data escrita à mão, com calendário de apoio | Date Picker Input Single |
IGRPCalendarSingle / Range / Multiple (+ Time) | Calendário sempre visível, com ou sem hora | Calendar Single / Range / Multiple… |
IGRPInputFile | Upload de ficheiros com zona de arrastar | Upload File |
IGRPRepetitiveComponent | Repete um bloco por cada item de um array (só apresentação) | Repetitive Component |
IGRPLabel | Label isolada | Label |
IGRPRichTextEditor / IGRPRichTextView | Editor e visualizador de texto rico, importados de …/rich-text | — |
Componentes descontinuados
IGRPCalendar e IGRPDatePicker ainda existem, mas usa as variantes Single, Range ou Multiple — ver Instalação e tema.
Última atualização:
