// JanelaApp: a "gravação": um app de lista nítido (janela 1400 × 940 com barra, lateral e lista) que // MUDA DE ESTADO por quadros, sempre causado pelo clique do cursor: o botão afunda, o modal abre, // o texto é digitado com cursor de texto, o Salvar afunda, o modal fecha e a linha nova entra no topo // com fundo de destaque. Pontos úteis pra câmera e cursor: veja ALVOS (coordenadas da janela). import React from 'react'; import {useCurrentFrame} from 'remotion'; import {COR, FONTE, JANELA, MONO, e5, lerp, mola, seg} from '../base'; export const ALVOS = {botao: [1230, 124], campo: [850, 440], salvar: [1060, 568], novaLinha: [850, 242]} as const; export type JanelaAppProps = { endereco?: string; lateral?: string[]; titulo: string; /** "{n} tarefas": {n} vira o número de itens */ subtitulo?: string; botao: string; itens: {texto: string; tag?: string}[]; modal: {titulo: string; placeholder: string; texto: string; botao: string; tag?: string}; /** quadros (relativos à gravação): clique no botão, digitação [de, até], clique em salvar */ tempos: {abre: number; digita: [number, number]; salva: number}; }; const Linha: React.FC<{texto: string; tag?: string; y: number; h?: number; destaque?: number; check?: number}> = ({texto, tag, y, h = 84, destaque = 0, check = 0}) =>
0 ? `rgba(255,91,20,${0.14 * destaque})` : undefined, boxShadow: destaque > 0 ? `inset 0 0 0 2px rgba(255,91,20,${0.5 * destaque})` : undefined}}>
0 ? COR.ok : '#C7CCD4'}`, boxSizing: 'border-box', background: check > 0.5 ? COR.ok : undefined, transform: `scale(${check > 0 ? mola(check) : 1})`}} />
{texto}
{tag &&
{tag}
}
; export const JanelaApp: React.FC = ({endereco = 'pauta.app', lateral = ['Hoje', 'Próximos', 'Projetos', 'Arquivo'], titulo, subtitulo = '{n} tarefas', botao, itens, modal, tempos}) => { const f = useCurrentFrame(); const apertaB = f >= tempos.abre && f < tempos.abre + 4 ? 0.94 : 1; const abre = e5(seg(f, tempos.abre + 1, tempos.abre + 9)), fecha = seg(f, tempos.salva + 2, tempos.salva + 8); const mod = abre * (1 - fecha); const [d0, d1] = tempos.digita; const n = Math.round(lerp(0, modal.texto.length, seg(f, d0, d1))); const caret = f < d0 + 1 || n < modal.texto.length || Math.floor(f / 8) % 2 === 0; const apertaS = f >= tempos.salva && f < tempos.salva + 4 ? 0.92 : 1; const entra = e5(seg(f, tempos.salva + 6, tempos.salva + 16)); const brilho = 1 - seg(f, tempos.salva + 30, tempos.salva + 60); const total = itens.length + (entra > 0 ? 1 : 0); const Y0 = 210; return
{/* barra */}
{['#FF5F57', '#FEBC2E', '#28C840'].map((c, i) =>
)}
{endereco}
{/* lateral */}
{lateral.map((l, i) =>
{l}
)}
{/* cabeçalho */}
{titulo}
{subtitulo.replace('{n}', String(total))}
{botao}
{/* lista */} {entra > 0 && } {itens.map((it, i) => )} {/* modal */} {mod > 0.01 && <>
{modal.titulo}
= d0 - 4 ? COR.acento : COR.linha}`, boxSizing: 'border-box', boxShadow: f >= d0 - 4 ? `0 0 0 6px ${COR.acentoClaro}` : undefined, fontFamily: FONTE, fontWeight: 600, fontSize: 30, lineHeight: '74px', paddingLeft: 22, color: n ? COR.tinta : '#A3A9B3', whiteSpace: 'nowrap'}}> {n ? modal.texto.slice(0, n) : f >= d0 - 4 ? '' : modal.placeholder} {f >= d0 - 4 && caret && }
{modal.botao}
}
; };