@seedgrid/ai-toolkit 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -9
- package/dist/adapters/shared.js +12 -7
- package/dist/cli.js +8 -2
- package/dist/commands/review.js +2 -2
- package/dist/commands/selfinstall.js +63 -0
- package/dist/core/plan.js +5 -0
- package/docs/arquitetura.md +2 -2
- package/docs/compatibilidade.md +1 -1
- package/docs/formato.md +4 -4
- package/docs/instalacao.md +1 -1
- package/package.json +1 -1
- package/seedgridKB/SeedGrid/Extensions/Catalogo-de-propriedades.md +971 -971
- package/seedgridKB/SeedGrid/Extensions/audit/Configuracao-Rust.md +93 -93
- package/seedgridKB/SeedGrid/Extensions/resilience/Configuracao.md +186 -186
- package/seedgridKB/SeedGrid/Extensions/resilience/Visao-geral.md +112 -112
- package/seedgridKB/SeedGrid/SeedGrid-components/Indice.md +323 -323
- package/seedgridKB/SeedGrid/SeedGrid-components/editor/API.md +103 -103
- package/seedgridKB/SeedGrid/SeedGrid-components/editor/Visao-geral.md +86 -86
- package/seedgridKB/SeedGrid/SeedGrid-components/gadgets/Exemplos.md +629 -629
- package/seedgridKB/SeedGrid/SeedGrid-components/inputs-texto/Visao-geral.md +123 -123
- package/seedgridKB/SeedGrid/SeedGrid-components/navegacao-e-overlay/Exemplos.md +59 -59
- package/seedgridKB/SeedGrid/Toolkit/agents/arquiteto.md +1 -1
- package/seedgridKB/SeedGrid/Toolkit/agents/backend-java.md +2 -2
- package/seedgridKB/SeedGrid/Toolkit/agents/backend-rust.md +1 -1
- package/seedgridKB/SeedGrid/Toolkit/agents/frontend-react.md +1 -1
- package/seedgridKB/SeedGrid/Toolkit/agents/qa.md +2 -2
- package/seedgridKB/SeedGrid/Toolkit/agents/revisor.md +2 -2
- package/seedgridKB/SeedGrid/Toolkit/references/fluxo-de-trabalho.md +3 -3
- package/seedgridKB/SeedGrid/Toolkit/rules/backend-java-seedgrid.md +2 -2
- package/seedgridKB/SeedGrid/Toolkit/rules/coordenacao.md +1 -1
- package/seedgridKB/SeedGrid/Toolkit/rules/documentos.md +26 -0
- package/seedgridKB/SeedGrid/Toolkit/rules/frontend-seedgrid.md +2 -2
- package/seedgridKB/SeedGrid/Toolkit/skills/{seedgrid-bump → sg-bump}/SKILL.md +1 -1
- package/seedgridKB/SeedGrid/Toolkit/skills/{revisar-codigo → sg-code-review}/SKILL.md +1 -1
- package/seedgridKB/SeedGrid/Toolkit/skills/{seedgrid-backend-crud → sg-crud-back}/SKILL.md +3 -3
- package/seedgridKB/SeedGrid/Toolkit/skills/{seedgrid-crud → sg-crud-front}/SKILL.md +2 -2
- package/seedgridKB/SeedGrid/Toolkit/skills/{seedgrid-crud → sg-crud-front}/agents/openai.yaml +1 -1
- package/seedgridKB/SeedGrid/Toolkit/skills/{seedgrid-crud → sg-crud-front}/references/routing.md +1 -1
- package/seedgridKB/SeedGrid/Toolkit/skills/{seedgrid-new-screen → sg-new-screen}/SKILL.md +1 -1
- package/seedgridKB/SeedGrid/Toolkit/skills/sg-new-screen/agents/openai.yaml +4 -0
- package/seedgridKB/SeedGrid/Toolkit/skills/sg-new-skill/SKILL.md +35 -0
- package/seedgridKB/SeedGrid/Toolkit/skills/{planejar-mudanca → sg-plan-change}/SKILL.md +1 -1
- package/seedgridKB/SeedGrid/Toolkit/skills/{seedgrid-review → sg-review}/SKILL.md +1 -1
- package/seedgridKB/SeedGrid/Toolkit/skills/{validar-mudanca → sg-validate-change}/SKILL.md +1 -1
- package/seedgridKB/manifest.json +26 -17
- package/seedgridKB/SeedGrid/Toolkit/skills/seedgrid-new-screen/agents/openai.yaml +0 -4
- /package/seedgridKB/SeedGrid/Toolkit/skills/{revisar-codigo → sg-code-review}/references/checklist.md +0 -0
- /package/seedgridKB/SeedGrid/Toolkit/skills/{seedgrid-crud → sg-crud-front}/references/checklist.md +0 -0
- /package/seedgridKB/SeedGrid/Toolkit/skills/{seedgrid-crud → sg-crud-front}/references/patterns.md +0 -0
- /package/seedgridKB/SeedGrid/Toolkit/skills/{seedgrid-new-screen → sg-new-screen}/references/checklist.md +0 -0
- /package/seedgridKB/SeedGrid/Toolkit/skills/{seedgrid-new-screen → sg-new-screen}/references/steps.md +0 -0
|
@@ -1,629 +1,629 @@
|
|
|
1
|
-
---
|
|
2
|
-
tags: [seedgrid, front, component, gadgets]
|
|
3
|
-
fonte: front/seedgrid-fe-components/apps/showcase/src/app/components/gadgets
|
|
4
|
-
branch: main
|
|
5
|
-
commit: ad30fc11de
|
|
6
|
-
analisado-em: 2026-10-14
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
# Exemplos — gadgets
|
|
10
|
-
|
|
11
|
-
Exemplos copiados 1:1 dos `samples/*.tsx.sample` do showcase (rotas `/components/gadgets/*`, `/components/providers/*`, `/components/hooks/*`), salvo os marcados como **exemplo próprio**. Todos verificados com type-check TypeScript (`strict`) contra `packages/seedgrid-fe-components/src/index.ts` no commit `ad30fc11de` — sem erros. Classes utilitárias são Tailwind do app de exemplo.
|
|
12
|
-
|
|
13
|
-
API: [[SeedGrid/SeedGrid-components/gadgets/API|API dos gadgets]] · Visão geral: [[SeedGrid/SeedGrid-components/gadgets/Visao-geral|Gadgets — visão geral]]
|
|
14
|
-
|
|
15
|
-
## SgCalendar
|
|
16
|
-
|
|
17
|
-
### Básico (não controlado)
|
|
18
|
-
|
|
19
|
-
Fonte: `front/seedgrid-fe-components/apps/showcase/src/app/components/gadgets/sg-calendar/samples/basico-mes-atual.tsx.sample`
|
|
20
|
-
|
|
21
|
-
```tsx
|
|
22
|
-
"use client";
|
|
23
|
-
|
|
24
|
-
import { SgCalendar } from "@seedgrid/fe-components";
|
|
25
|
-
|
|
26
|
-
export default function Example() {
|
|
27
|
-
return (
|
|
28
|
-
<SgCalendar
|
|
29
|
-
locale="pt-BR"
|
|
30
|
-
defaultValue="2026-03-10"
|
|
31
|
-
defaultViewDate="2026-03-01"
|
|
32
|
-
cardTitle="Calendario base"
|
|
33
|
-
cardProps={{ id: "showcase-calendar-basic", dragPersistKey: "showcase-calendar-basic" }}
|
|
34
|
-
/>
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
### Controlado, com limites e fins de semana desabilitados
|
|
40
|
-
|
|
41
|
-
Fonte: `front/seedgrid-fe-components/apps/showcase/src/app/components/gadgets/sg-calendar/samples/selecionado-controlado-limite-de-datas.tsx.sample`
|
|
42
|
-
|
|
43
|
-
```tsx
|
|
44
|
-
"use client";
|
|
45
|
-
|
|
46
|
-
import React from "react";
|
|
47
|
-
import { SgCalendar } from "@seedgrid/fe-components";
|
|
48
|
-
|
|
49
|
-
export default function Example() {
|
|
50
|
-
const [selected, setSelected] = React.useState(new Date(2026, 2, 10));
|
|
51
|
-
const minDate = React.useMemo(() => new Date(2026, 2, 5), []);
|
|
52
|
-
const maxDate = React.useMemo(() => new Date(2026, 2, 25), []);
|
|
53
|
-
|
|
54
|
-
return (
|
|
55
|
-
<div className="space-y-2">
|
|
56
|
-
<SgCalendar
|
|
57
|
-
value={selected}
|
|
58
|
-
onValueChange={setSelected}
|
|
59
|
-
locale="pt-BR"
|
|
60
|
-
minDate={minDate}
|
|
61
|
-
maxDate={maxDate}
|
|
62
|
-
isDateDisabled={(date) => date.getDay() === 0 || date.getDay() === 6}
|
|
63
|
-
cardTitle="Agenda de visitas"
|
|
64
|
-
cardProps={{ id: "showcase-calendar-range", dragPersistKey: "showcase-calendar-range" }}
|
|
65
|
-
/>
|
|
66
|
-
<p className="text-sm text-muted-foreground">
|
|
67
|
-
Selecionado: {selected.toLocaleDateString("pt-BR", { day: "2-digit", month: "2-digit", year: "numeric" })}
|
|
68
|
-
</p>
|
|
69
|
-
</div>
|
|
70
|
-
);
|
|
71
|
-
}
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
## SgClock
|
|
75
|
-
|
|
76
|
-
### Digital em três tamanhos, alternando 12h/24h
|
|
77
|
-
|
|
78
|
-
Fonte: `front/seedgrid-fe-components/apps/showcase/src/app/components/gadgets/sg-clock/samples/digital.tsx.sample`
|
|
79
|
-
|
|
80
|
-
```tsx
|
|
81
|
-
import * as React from "react";
|
|
82
|
-
import { SgButton, SgClock } from "@seedgrid/fe-components";
|
|
83
|
-
|
|
84
|
-
export default function Example(props: { initialServerTime?: string }) {
|
|
85
|
-
const [format, setFormat] = React.useState<"12h" | "24h">("24h");
|
|
86
|
-
|
|
87
|
-
return (
|
|
88
|
-
<>
|
|
89
|
-
<div className="mb-3 flex flex-wrap items-center gap-2">
|
|
90
|
-
<SgButton onClick={() => setFormat((value) => (value === "24h" ? "12h" : "24h"))}>
|
|
91
|
-
{format}
|
|
92
|
-
</SgButton>
|
|
93
|
-
</div>
|
|
94
|
-
|
|
95
|
-
<div className="grid gap-4 md:grid-cols-3">
|
|
96
|
-
<SgClock
|
|
97
|
-
clockStyle="digital"
|
|
98
|
-
size="sm"
|
|
99
|
-
initialServerTime={props.initialServerTime ?? new Date().toISOString()}
|
|
100
|
-
timezone="America/Sao_Paulo"
|
|
101
|
-
format={format}
|
|
102
|
-
digitalStyle="default"
|
|
103
|
-
/>
|
|
104
|
-
<SgClock
|
|
105
|
-
clockStyle="digital"
|
|
106
|
-
size="md"
|
|
107
|
-
initialServerTime={props.initialServerTime ?? new Date().toISOString()}
|
|
108
|
-
timezone="America/Sao_Paulo"
|
|
109
|
-
format={format}
|
|
110
|
-
digitalStyle="default"
|
|
111
|
-
/>
|
|
112
|
-
<SgClock
|
|
113
|
-
clockStyle="digital"
|
|
114
|
-
size="lg"
|
|
115
|
-
initialServerTime={props.initialServerTime ?? new Date().toISOString()}
|
|
116
|
-
timezone="America/Sao_Paulo"
|
|
117
|
-
format={format}
|
|
118
|
-
digitalStyle="default"
|
|
119
|
-
/>
|
|
120
|
-
</div>
|
|
121
|
-
</>
|
|
122
|
-
);
|
|
123
|
-
}
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
### Analógico com tema inline (sem provider nem registro)
|
|
127
|
-
|
|
128
|
-
Fonte: `front/seedgrid-fe-components/apps/showcase/src/app/components/gadgets/sg-clock/samples/tema-inline-sem-provider.tsx.sample`
|
|
129
|
-
|
|
130
|
-
```tsx
|
|
131
|
-
import React from "react";
|
|
132
|
-
import { SgClock } from "@seedgrid/fe-components";
|
|
133
|
-
|
|
134
|
-
export default function Example(props: { initialServerTime?: string }) {
|
|
135
|
-
return (
|
|
136
|
-
<div className="flex items-center justify-center rounded-xl border border-border bg-background p-6">
|
|
137
|
-
<SgClock
|
|
138
|
-
clockStyle="analog"
|
|
139
|
-
size={260}
|
|
140
|
-
initialServerTime={props.initialServerTime ?? new Date().toISOString()}
|
|
141
|
-
showSeconds
|
|
142
|
-
secondHandMode="smooth"
|
|
143
|
-
theme={{
|
|
144
|
-
id: "inline-theme",
|
|
145
|
-
label: "Inline Theme",
|
|
146
|
-
render: () => (
|
|
147
|
-
<>
|
|
148
|
-
<circle cx="50" cy="50" r="48" className="fill-white dark:fill-neutral-950" />
|
|
149
|
-
<circle cx="50" cy="50" r="47" className="fill-none stroke-neutral-300 dark:stroke-neutral-700" />
|
|
150
|
-
<circle cx="50" cy="50" r="38" className="fill-none stroke-neutral-200 dark:stroke-neutral-800" />
|
|
151
|
-
<circle cx="50" cy="50" r="6" className="fill-neutral-100 dark:fill-neutral-900" />
|
|
152
|
-
<text x="50" y="56" textAnchor="middle" className="fill-neutral-400 text-[8px] font-semibold">
|
|
153
|
-
SeedGrid
|
|
154
|
-
</text>
|
|
155
|
-
</>
|
|
156
|
-
),
|
|
157
|
-
}}
|
|
158
|
-
/>
|
|
159
|
-
</div>
|
|
160
|
-
);
|
|
161
|
-
}
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
### Fusos horários
|
|
165
|
-
|
|
166
|
-
Fonte: `front/seedgrid-fe-components/apps/showcase/src/app/components/gadgets/sg-clock/samples/timezone.tsx.sample`
|
|
167
|
-
|
|
168
|
-
```tsx
|
|
169
|
-
import * as React from "react";
|
|
170
|
-
import { SgButton, SgClock } from "@seedgrid/fe-components";
|
|
171
|
-
|
|
172
|
-
export default function Example(props: { initialServerTime?: string }) {
|
|
173
|
-
const [timezone, setTimezone] = React.useState("America/Sao_Paulo");
|
|
174
|
-
const [format, setFormat] = React.useState<"12h" | "24h">("24h");
|
|
175
|
-
|
|
176
|
-
return (
|
|
177
|
-
<>
|
|
178
|
-
<div className="flex flex-wrap gap-2">
|
|
179
|
-
<SgButton onClick={() => setTimezone("America/Sao_Paulo")}>Sao Paulo</SgButton>
|
|
180
|
-
<SgButton onClick={() => setTimezone("Europe/Lisbon")}>Lisboa</SgButton>
|
|
181
|
-
<SgButton onClick={() => setTimezone("America/New_York")}>New York</SgButton>
|
|
182
|
-
<SgButton onClick={() => setTimezone("Asia/Tokyo")}>Tokyo</SgButton>
|
|
183
|
-
<SgButton onClick={() => setFormat((value) => (value === "24h" ? "12h" : "24h"))}>
|
|
184
|
-
{format}
|
|
185
|
-
</SgButton>
|
|
186
|
-
</div>
|
|
187
|
-
|
|
188
|
-
<div className="mt-4 grid gap-3 md:grid-cols-2">
|
|
189
|
-
<SgClock
|
|
190
|
-
clockStyle="digital"
|
|
191
|
-
size="lg"
|
|
192
|
-
initialServerTime={props.initialServerTime ?? new Date().toISOString()}
|
|
193
|
-
timezone={timezone}
|
|
194
|
-
format={format}
|
|
195
|
-
showSeconds
|
|
196
|
-
/>
|
|
197
|
-
<SgClock
|
|
198
|
-
clockStyle="analog"
|
|
199
|
-
size={220}
|
|
200
|
-
initialServerTime={props.initialServerTime ?? new Date().toISOString()}
|
|
201
|
-
themeId="seedgrid"
|
|
202
|
-
timezone={timezone}
|
|
203
|
-
showSeconds={false}
|
|
204
|
-
/>
|
|
205
|
-
</div>
|
|
206
|
-
</>
|
|
207
|
-
);
|
|
208
|
-
}
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
Observação: o tema `seedgrid` usado acima só resolve se os temas embutidos tiverem sido registrados (o showcase registra em `sg-clock-showcase-client.tsx`). Ver o exemplo seguinte.
|
|
212
|
-
|
|
213
|
-
### Temas embutidos + provider + picker (exemplo próprio)
|
|
214
|
-
|
|
215
|
-
Os temas de `sgClockThemesBuiltIn` precisam ser registrados; o `SgClockThemePicker` lista os temas do `SgClockThemeProvider`.
|
|
216
|
-
|
|
217
|
-
```tsx
|
|
218
|
-
"use client";
|
|
219
|
-
|
|
220
|
-
import React from "react";
|
|
221
|
-
import {
|
|
222
|
-
SgClock,
|
|
223
|
-
SgClockThemePicker,
|
|
224
|
-
SgClockThemeProvider,
|
|
225
|
-
registerThemes,
|
|
226
|
-
sgClockThemesBuiltIn
|
|
227
|
-
} from "@seedgrid/fe-components";
|
|
228
|
-
|
|
229
|
-
// Os temas embutidos NÃO são registrados automaticamente: registre uma vez no boot do app.
|
|
230
|
-
registerThemes(sgClockThemesBuiltIn);
|
|
231
|
-
|
|
232
|
-
export default function Example() {
|
|
233
|
-
const [themeId, setThemeId] = React.useState("classic");
|
|
234
|
-
|
|
235
|
-
return (
|
|
236
|
-
<SgClockThemeProvider value={{ mode: "fallback", fallbackThemeId: "classic" }}>
|
|
237
|
-
<SgClockThemePicker value={themeId} onChange={setThemeId} label="Tema" />
|
|
238
|
-
<SgClock clockStyle="analog" size={240} themeId={themeId} />
|
|
239
|
-
</SgClockThemeProvider>
|
|
240
|
-
);
|
|
241
|
-
}
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
## SgTimeProvider / useSgTime
|
|
245
|
-
|
|
246
|
-
### Uso básico
|
|
247
|
-
|
|
248
|
-
Fonte: `front/seedgrid-fe-components/apps/showcase/src/app/components/providers/sg-time-provider/samples/uso-basico.tsx.sample`
|
|
249
|
-
|
|
250
|
-
```tsx
|
|
251
|
-
import { SgTimeProvider, useSgTime } from "@seedgrid/fe-components";
|
|
252
|
-
|
|
253
|
-
function LiveClock() {
|
|
254
|
-
const time = useSgTime();
|
|
255
|
-
const date = new Date(time.nowMs());
|
|
256
|
-
const pad = (n: number) => String(n).padStart(2, "0");
|
|
257
|
-
|
|
258
|
-
return (
|
|
259
|
-
<span>
|
|
260
|
-
{pad(date.getHours())}:{pad(date.getMinutes())}:{pad(date.getSeconds())}
|
|
261
|
-
</span>
|
|
262
|
-
);
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
export default function App() {
|
|
266
|
-
return (
|
|
267
|
-
<SgTimeProvider initialServerTime={new Date().toISOString()}>
|
|
268
|
-
<LiveClock />
|
|
269
|
-
</SgTimeProvider>
|
|
270
|
-
);
|
|
271
|
-
}
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
## SgClockThemeProvider / useSgClockThemeResolver
|
|
275
|
-
|
|
276
|
-
### Resolvedor via hook (modo `fallback`)
|
|
277
|
-
|
|
278
|
-
Só há temas a listar/resolver se algum tiver sido registrado (`registerThemes`) ou passado em `value.themes`; caso contrário `resolve("classic")` retorna `null`.
|
|
279
|
-
|
|
280
|
-
Fonte: `front/seedgrid-fe-components/apps/showcase/src/app/components/providers/sg-clock-theme-provider/samples/resolver-hook.tsx.sample`
|
|
281
|
-
|
|
282
|
-
```tsx
|
|
283
|
-
"use client";
|
|
284
|
-
|
|
285
|
-
import { SgClockThemeProvider, useSgClockThemeResolver } from "@seedgrid/fe-components";
|
|
286
|
-
|
|
287
|
-
function TemasDisponiveis() {
|
|
288
|
-
// Fora de um SgClockThemeProvider o hook devolve null: sem provider nao ha
|
|
289
|
-
// resolvedor, e nao existe fallback implicito.
|
|
290
|
-
const resolver = useSgClockThemeResolver();
|
|
291
|
-
if (!resolver) return <div>Sem SgClockThemeProvider na arvore.</div>;
|
|
292
|
-
|
|
293
|
-
const classic = resolver.resolve("classic");
|
|
294
|
-
const inexistente = resolver.resolve("nao-existe");
|
|
295
|
-
|
|
296
|
-
return (
|
|
297
|
-
<div className="space-y-1 text-sm">
|
|
298
|
-
<div>temas visíveis: {resolver.list().map((theme) => theme.id).join(", ")}</div>
|
|
299
|
-
<div>resolve("classic"): {classic?.id ?? "null"}</div>
|
|
300
|
-
{/* modo "fallback" (padrão) cai no fallbackThemeId; em "strict" isto seria null */}
|
|
301
|
-
<div>resolve("nao-existe"): {inexistente?.id ?? "null"}</div>
|
|
302
|
-
</div>
|
|
303
|
-
);
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
export default function Example() {
|
|
307
|
-
return (
|
|
308
|
-
<SgClockThemeProvider value={{ mode: "fallback", fallbackThemeId: "classic" }}>
|
|
309
|
-
<TemasDisponiveis />
|
|
310
|
-
</SgClockThemeProvider>
|
|
311
|
-
);
|
|
312
|
-
}
|
|
313
|
-
```
|
|
314
|
-
|
|
315
|
-
## SgClockThemePreview
|
|
316
|
-
|
|
317
|
-
### Galeria dos temas embutidos (exemplo próprio)
|
|
318
|
-
|
|
319
|
-
```tsx
|
|
320
|
-
import { SgClockThemePreview, sgClockThemesBuiltIn } from "@seedgrid/fe-components";
|
|
321
|
-
|
|
322
|
-
export default function Example() {
|
|
323
|
-
return (
|
|
324
|
-
<div style={{ display: "flex", gap: 12 }}>
|
|
325
|
-
{sgClockThemesBuiltIn.map((theme) => (
|
|
326
|
-
<SgClockThemePreview key={theme.id} theme={theme} size={72} />
|
|
327
|
-
))}
|
|
328
|
-
</div>
|
|
329
|
-
);
|
|
330
|
-
}
|
|
331
|
-
```
|
|
332
|
-
|
|
333
|
-
## SgLinearGauge
|
|
334
|
-
|
|
335
|
-
### Horizontal com faixas e ponteiro arrastável
|
|
336
|
-
|
|
337
|
-
Fonte: `front/seedgrid-fe-components/apps/showcase/src/app/components/gadgets/sg-linear-gauge/samples/default-horizontal.tsx.sample`
|
|
338
|
-
|
|
339
|
-
```tsx
|
|
340
|
-
"use client";
|
|
341
|
-
|
|
342
|
-
import React from "react";
|
|
343
|
-
import { SgLinearGauge } from "@seedgrid/fe-components";
|
|
344
|
-
|
|
345
|
-
export default function Example() {
|
|
346
|
-
const [value, setValue] = React.useState(64);
|
|
347
|
-
|
|
348
|
-
return (
|
|
349
|
-
<SgLinearGauge
|
|
350
|
-
min={0}
|
|
351
|
-
max={100}
|
|
352
|
-
value={value}
|
|
353
|
-
onValueChange={setValue}
|
|
354
|
-
ranges={[
|
|
355
|
-
{ start: 0, end: 30, color: "#22c55e" },
|
|
356
|
-
{ start: 30, end: 70, color: "#f59e0b" },
|
|
357
|
-
{ start: 70, end: 100, color: "#ef4444" }
|
|
358
|
-
]}
|
|
359
|
-
showTicks
|
|
360
|
-
showLabels
|
|
361
|
-
majorTickCount={5}
|
|
362
|
-
barPointer
|
|
363
|
-
primaryPointerDraggable
|
|
364
|
-
/>
|
|
365
|
-
);
|
|
366
|
-
}
|
|
367
|
-
```
|
|
368
|
-
|
|
369
|
-
### Vertical com múltiplos ponteiros
|
|
370
|
-
|
|
371
|
-
Fonte: `front/seedgrid-fe-components/apps/showcase/src/app/components/gadgets/sg-linear-gauge/samples/vertical-multi-pointers.tsx.sample`
|
|
372
|
-
|
|
373
|
-
```tsx
|
|
374
|
-
"use client";
|
|
375
|
-
|
|
376
|
-
import React from "react";
|
|
377
|
-
import { SgLinearGauge, type SgLinearGaugePointer } from "@seedgrid/fe-components";
|
|
378
|
-
|
|
379
|
-
export default function Example() {
|
|
380
|
-
const [temperature, setTemperature] = React.useState(28);
|
|
381
|
-
const [target, setTarget] = React.useState(72);
|
|
382
|
-
const pointers = React.useMemo<SgLinearGaugePointer[]>(
|
|
383
|
-
() => [
|
|
384
|
-
{ id: "target", value: target, color: "hsl(var(--secondary))", shape: "diamond", size: 11, draggable: true },
|
|
385
|
-
{ id: "temp", value: temperature, color: "hsl(var(--destructive))", shape: "circle", size: 10, draggable: true }
|
|
386
|
-
],
|
|
387
|
-
[target, temperature]
|
|
388
|
-
);
|
|
389
|
-
|
|
390
|
-
return (
|
|
391
|
-
<SgLinearGauge
|
|
392
|
-
min={-10}
|
|
393
|
-
max={50}
|
|
394
|
-
value={temperature}
|
|
395
|
-
onValueChange={setTemperature}
|
|
396
|
-
orientation="vertical"
|
|
397
|
-
width={140}
|
|
398
|
-
height={360}
|
|
399
|
-
pointers={pointers}
|
|
400
|
-
onPointerValueChange={(pointerId, next) => {
|
|
401
|
-
if (pointerId === "target") setTarget(next);
|
|
402
|
-
if (pointerId === "temp") setTemperature(next);
|
|
403
|
-
}}
|
|
404
|
-
ranges={[
|
|
405
|
-
{ start: -10, end: 15, color: "#38bdf8" },
|
|
406
|
-
{ start: 15, end: 30, color: "#22c55e" },
|
|
407
|
-
{ start: 30, end: 50, color: "#ef4444" }
|
|
408
|
-
]}
|
|
409
|
-
majorTickCount={6}
|
|
410
|
-
minorTicksPerInterval={1}
|
|
411
|
-
labelFormatter={(value) => `${Math.round(value)}°`}
|
|
412
|
-
/>
|
|
413
|
-
);
|
|
414
|
-
}
|
|
415
|
-
```
|
|
416
|
-
|
|
417
|
-
## SgRadialGauge
|
|
418
|
-
|
|
419
|
-
### Estilo velocímetro com conteúdo central
|
|
420
|
-
|
|
421
|
-
Fonte: `front/seedgrid-fe-components/apps/showcase/src/app/components/gadgets/sg-radial-gauge/samples/speedometer-style.tsx.sample`
|
|
422
|
-
|
|
423
|
-
```tsx
|
|
424
|
-
"use client";
|
|
425
|
-
|
|
426
|
-
import React from "react";
|
|
427
|
-
import { SgRadialGauge } from "@seedgrid/fe-components";
|
|
428
|
-
|
|
429
|
-
export default function Example() {
|
|
430
|
-
const [rpm, setRpm] = React.useState(62);
|
|
431
|
-
|
|
432
|
-
return (
|
|
433
|
-
<SgRadialGauge
|
|
434
|
-
min={0}
|
|
435
|
-
max={100}
|
|
436
|
-
value={rpm}
|
|
437
|
-
onValueChange={setRpm}
|
|
438
|
-
startAngle={140}
|
|
439
|
-
endAngle={40}
|
|
440
|
-
ranges={[
|
|
441
|
-
{ start: 0, end: 40, color: "#22c55e", width: 14 },
|
|
442
|
-
{ start: 40, end: 75, color: "#f59e0b", width: 14 },
|
|
443
|
-
{ start: 75, end: 100, color: "#ef4444", width: 14 }
|
|
444
|
-
]}
|
|
445
|
-
showTicks
|
|
446
|
-
showLabels
|
|
447
|
-
majorTickCount={5}
|
|
448
|
-
minorTicksPerInterval={2}
|
|
449
|
-
primaryPointerDraggable
|
|
450
|
-
primaryPointerType="needle"
|
|
451
|
-
primaryPointerColor="#2563eb"
|
|
452
|
-
centerContent={<div className="text-center"><div className="text-xs uppercase text-muted-foreground">RPM</div><div className="text-xl font-semibold">{Math.round(rpm)}</div></div>}
|
|
453
|
-
/>
|
|
454
|
-
);
|
|
455
|
-
}
|
|
456
|
-
```
|
|
457
|
-
|
|
458
|
-
### Ponteiros extras (marker/range) e anotação
|
|
459
|
-
|
|
460
|
-
Fonte: `front/seedgrid-fe-components/apps/showcase/src/app/components/gadgets/sg-radial-gauge/samples/multi-pointers-annotation.tsx.sample`
|
|
461
|
-
|
|
462
|
-
```tsx
|
|
463
|
-
"use client";
|
|
464
|
-
|
|
465
|
-
import React from "react";
|
|
466
|
-
import { SgRadialGauge, type SgRadialGaugePointer } from "@seedgrid/fe-components";
|
|
467
|
-
|
|
468
|
-
export default function Example() {
|
|
469
|
-
const [rpm, setRpm] = React.useState(62);
|
|
470
|
-
const [target, setTarget] = React.useState(70);
|
|
471
|
-
const [consumption, setConsumption] = React.useState(44);
|
|
472
|
-
const pointers = React.useMemo<SgRadialGaugePointer[]>(
|
|
473
|
-
() => [
|
|
474
|
-
{ id: "target", type: "marker", value: target, color: "#8b5cf6", size: 13, shape: "diamond", draggable: true, label: String(Math.round(target)) },
|
|
475
|
-
{ id: "consumption", type: "range", value: consumption, color: "#14b8a6", width: 10, draggable: true }
|
|
476
|
-
],
|
|
477
|
-
[consumption, target]
|
|
478
|
-
);
|
|
479
|
-
|
|
480
|
-
return (
|
|
481
|
-
<SgRadialGauge
|
|
482
|
-
min={0}
|
|
483
|
-
max={120}
|
|
484
|
-
value={rpm}
|
|
485
|
-
onValueChange={setRpm}
|
|
486
|
-
startAngle={135}
|
|
487
|
-
endAngle={45}
|
|
488
|
-
radiusFactor={0.95}
|
|
489
|
-
ranges={[
|
|
490
|
-
{ start: 0, end: 60, color: "#60a5fa", width: 12, opacity: 0.45 },
|
|
491
|
-
{ start: 60, end: 90, color: "#f59e0b", width: 12, opacity: 0.45 },
|
|
492
|
-
{ start: 90, end: 120, color: "#ef4444", width: 12, opacity: 0.45 }
|
|
493
|
-
]}
|
|
494
|
-
pointers={pointers}
|
|
495
|
-
onPointerValueChange={(id, next) => {
|
|
496
|
-
if (id === "target") setTarget(next);
|
|
497
|
-
if (id === "consumption") setConsumption(next);
|
|
498
|
-
}}
|
|
499
|
-
annotations={[{ id: "target-label", value: target, radiusFactor: 0.8, content: <span className="rounded bg-violet-600 px-2 py-1 text-[10px] font-semibold text-white">target</span> }]}
|
|
500
|
-
primaryPointerDraggable
|
|
501
|
-
/>
|
|
502
|
-
);
|
|
503
|
-
}
|
|
504
|
-
```
|
|
505
|
-
|
|
506
|
-
## SgQRCode
|
|
507
|
-
|
|
508
|
-
### Interativo com logo e fallback
|
|
509
|
-
|
|
510
|
-
Fonte: `front/seedgrid-fe-components/apps/showcase/src/app/components/gadgets/sg-qr-code/samples/interactive-example.tsx.sample`
|
|
511
|
-
|
|
512
|
-
```tsx
|
|
513
|
-
"use client";
|
|
514
|
-
|
|
515
|
-
import React from "react";
|
|
516
|
-
import { SgButton, SgQRCode } from "@seedgrid/fe-components";
|
|
517
|
-
|
|
518
|
-
export default function Example() {
|
|
519
|
-
const [value, setValue] = React.useState("https://seedgrid.com.br");
|
|
520
|
-
const [logoSrc, setLogoSrc] = React.useState("/logo-seedgrid.svg");
|
|
521
|
-
const [size, setSize] = React.useState(220);
|
|
522
|
-
|
|
523
|
-
return (
|
|
524
|
-
<div className="grid gap-6 md:grid-cols-[1fr_auto]">
|
|
525
|
-
<div className="space-y-4">
|
|
526
|
-
<label className="block space-y-1">
|
|
527
|
-
<span className="text-sm font-medium">Valor do QR Code</span>
|
|
528
|
-
<textarea
|
|
529
|
-
value={value}
|
|
530
|
-
onChange={(event) => setValue(event.target.value)}
|
|
531
|
-
rows={3}
|
|
532
|
-
className="w-full rounded-md border border-border bg-background px-3 py-2 text-sm outline-none ring-offset-background focus-visible:ring-2 focus-visible:ring-primary"
|
|
533
|
-
placeholder="Digite texto, URL ou payload PIX"
|
|
534
|
-
/>
|
|
535
|
-
</label>
|
|
536
|
-
|
|
537
|
-
<label className="block space-y-1">
|
|
538
|
-
<span className="text-sm font-medium">Logo central (URL)</span>
|
|
539
|
-
<input
|
|
540
|
-
type="text"
|
|
541
|
-
value={logoSrc}
|
|
542
|
-
onChange={(event) => setLogoSrc(event.target.value)}
|
|
543
|
-
className="w-full rounded-md border border-border bg-background px-3 py-2 text-sm outline-none ring-offset-background focus-visible:ring-2 focus-visible:ring-primary"
|
|
544
|
-
placeholder="https://exemplo.com/logo.png"
|
|
545
|
-
/>
|
|
546
|
-
</label>
|
|
547
|
-
|
|
548
|
-
<label className="block space-y-1">
|
|
549
|
-
<span className="text-sm font-medium">Tamanho: {size}px</span>
|
|
550
|
-
<input type="range" min={140} max={360} step={4} value={size} onChange={(event) => setSize(Number(event.target.value))} className="w-full" />
|
|
551
|
-
</label>
|
|
552
|
-
|
|
553
|
-
<div className="flex flex-wrap gap-2">
|
|
554
|
-
<SgButton onClick={() => setValue("https://seedgrid.com.br")}>URL</SgButton>
|
|
555
|
-
<SgButton severity="secondary" onClick={() => setValue("00020126580014BR.GOV.BCB.PIX0136contato@seedgrid.com.br5204000053039865802BR5922SEEDGRID TECNOLOGIA6009SAO PAULO62140510SEEDGRID1236304ABCD")}>
|
|
556
|
-
Exemplo PIX
|
|
557
|
-
</SgButton>
|
|
558
|
-
<SgButton severity="warning" onClick={() => setLogoSrc("")}>Sem logo</SgButton>
|
|
559
|
-
</div>
|
|
560
|
-
</div>
|
|
561
|
-
|
|
562
|
-
<div className="inline-flex h-fit rounded-xl border border-border bg-background p-4 shadow-sm">
|
|
563
|
-
<SgQRCode
|
|
564
|
-
value={value}
|
|
565
|
-
size={size}
|
|
566
|
-
logoSrc={logoSrc || undefined}
|
|
567
|
-
logoAlt="Logo SeedGrid"
|
|
568
|
-
emptyFallback={<span className="text-sm text-muted-foreground">Informe um valor</span>}
|
|
569
|
-
/>
|
|
570
|
-
</div>
|
|
571
|
-
</div>
|
|
572
|
-
);
|
|
573
|
-
}
|
|
574
|
-
```
|
|
575
|
-
|
|
576
|
-
## SgStringAnimator
|
|
577
|
-
|
|
578
|
-
### `autoStart` ao trocar o alvo
|
|
579
|
-
|
|
580
|
-
Fonte: `front/seedgrid-fe-components/apps/showcase/src/app/components/gadgets/sg-string-animator/samples/autostart-animacao-automatica.tsx.sample`
|
|
581
|
-
|
|
582
|
-
```tsx
|
|
583
|
-
"use client";
|
|
584
|
-
|
|
585
|
-
import React from "react";
|
|
586
|
-
import { SgStringAnimator } from "@seedgrid/fe-components";
|
|
587
|
-
|
|
588
|
-
const NAMES = ["LUCIANO", "MARTA", "PEDRO", "ANA", "SEEDGRID"];
|
|
589
|
-
|
|
590
|
-
export default function Example() {
|
|
591
|
-
const [index, setIndex] = React.useState(0);
|
|
592
|
-
const [target, setTarget] = React.useState(NAMES[0] ?? "");
|
|
593
|
-
|
|
594
|
-
React.useEffect(() => {
|
|
595
|
-
const id = window.setInterval(() => {
|
|
596
|
-
setIndex((prev) => {
|
|
597
|
-
const next = (prev + 1) % NAMES.length;
|
|
598
|
-
setTarget(NAMES[next] ?? "");
|
|
599
|
-
return next;
|
|
600
|
-
});
|
|
601
|
-
}, 1800);
|
|
602
|
-
return () => window.clearInterval(id);
|
|
603
|
-
}, []);
|
|
604
|
-
|
|
605
|
-
return <SgStringAnimator sourceString={NAMES[index] ?? ""} targetString={target} stringAnimatorStyle="roller3d" alignTo="left" fontSize={28} autoStart />;
|
|
606
|
-
}
|
|
607
|
-
```
|
|
608
|
-
|
|
609
|
-
### Disparo imperativo via `ref` (números alinhados à direita)
|
|
610
|
-
|
|
611
|
-
Fonte: `front/seedgrid-fe-components/apps/showcase/src/app/components/gadgets/sg-string-animator/samples/roller-3d-numeros-right-aligned.tsx.sample`
|
|
612
|
-
|
|
613
|
-
```tsx
|
|
614
|
-
"use client";
|
|
615
|
-
|
|
616
|
-
import React from "react";
|
|
617
|
-
import { SgStringAnimator, type SgStringAnimatorRef, SgButton } from "@seedgrid/fe-components";
|
|
618
|
-
|
|
619
|
-
export default function Example() {
|
|
620
|
-
const animRef = React.useRef<SgStringAnimatorRef>(null);
|
|
621
|
-
|
|
622
|
-
return (
|
|
623
|
-
<div className="flex flex-col gap-4">
|
|
624
|
-
<SgStringAnimator ref={animRef} sourceString="42" targetString="1337" stringAnimatorStyle="roller3d" alignTo="right" fontSize={28} />
|
|
625
|
-
<SgButton size="sm" onClick={() => animRef.current?.startAnimation()}>Animar</SgButton>
|
|
626
|
-
</div>
|
|
627
|
-
);
|
|
628
|
-
}
|
|
629
|
-
```
|
|
1
|
+
---
|
|
2
|
+
tags: [seedgrid, front, component, gadgets]
|
|
3
|
+
fonte: front/seedgrid-fe-components/apps/showcase/src/app/components/gadgets
|
|
4
|
+
branch: main
|
|
5
|
+
commit: ad30fc11de
|
|
6
|
+
analisado-em: 2026-10-14
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Exemplos — gadgets
|
|
10
|
+
|
|
11
|
+
Exemplos copiados 1:1 dos `samples/*.tsx.sample` do showcase (rotas `/components/gadgets/*`, `/components/providers/*`, `/components/hooks/*`), salvo os marcados como **exemplo próprio**. Todos verificados com type-check TypeScript (`strict`) contra `packages/seedgrid-fe-components/src/index.ts` no commit `ad30fc11de` — sem erros. Classes utilitárias são Tailwind do app de exemplo.
|
|
12
|
+
|
|
13
|
+
API: [[SeedGrid/SeedGrid-components/gadgets/API|API dos gadgets]] · Visão geral: [[SeedGrid/SeedGrid-components/gadgets/Visao-geral|Gadgets — visão geral]]
|
|
14
|
+
|
|
15
|
+
## SgCalendar
|
|
16
|
+
|
|
17
|
+
### Básico (não controlado)
|
|
18
|
+
|
|
19
|
+
Fonte: `front/seedgrid-fe-components/apps/showcase/src/app/components/gadgets/sg-calendar/samples/basico-mes-atual.tsx.sample`
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
"use client";
|
|
23
|
+
|
|
24
|
+
import { SgCalendar } from "@seedgrid/fe-components";
|
|
25
|
+
|
|
26
|
+
export default function Example() {
|
|
27
|
+
return (
|
|
28
|
+
<SgCalendar
|
|
29
|
+
locale="pt-BR"
|
|
30
|
+
defaultValue="2026-03-10"
|
|
31
|
+
defaultViewDate="2026-03-01"
|
|
32
|
+
cardTitle="Calendario base"
|
|
33
|
+
cardProps={{ id: "showcase-calendar-basic", dragPersistKey: "showcase-calendar-basic" }}
|
|
34
|
+
/>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Controlado, com limites e fins de semana desabilitados
|
|
40
|
+
|
|
41
|
+
Fonte: `front/seedgrid-fe-components/apps/showcase/src/app/components/gadgets/sg-calendar/samples/selecionado-controlado-limite-de-datas.tsx.sample`
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
44
|
+
"use client";
|
|
45
|
+
|
|
46
|
+
import React from "react";
|
|
47
|
+
import { SgCalendar } from "@seedgrid/fe-components";
|
|
48
|
+
|
|
49
|
+
export default function Example() {
|
|
50
|
+
const [selected, setSelected] = React.useState(new Date(2026, 2, 10));
|
|
51
|
+
const minDate = React.useMemo(() => new Date(2026, 2, 5), []);
|
|
52
|
+
const maxDate = React.useMemo(() => new Date(2026, 2, 25), []);
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<div className="space-y-2">
|
|
56
|
+
<SgCalendar
|
|
57
|
+
value={selected}
|
|
58
|
+
onValueChange={setSelected}
|
|
59
|
+
locale="pt-BR"
|
|
60
|
+
minDate={minDate}
|
|
61
|
+
maxDate={maxDate}
|
|
62
|
+
isDateDisabled={(date) => date.getDay() === 0 || date.getDay() === 6}
|
|
63
|
+
cardTitle="Agenda de visitas"
|
|
64
|
+
cardProps={{ id: "showcase-calendar-range", dragPersistKey: "showcase-calendar-range" }}
|
|
65
|
+
/>
|
|
66
|
+
<p className="text-sm text-muted-foreground">
|
|
67
|
+
Selecionado: {selected.toLocaleDateString("pt-BR", { day: "2-digit", month: "2-digit", year: "numeric" })}
|
|
68
|
+
</p>
|
|
69
|
+
</div>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## SgClock
|
|
75
|
+
|
|
76
|
+
### Digital em três tamanhos, alternando 12h/24h
|
|
77
|
+
|
|
78
|
+
Fonte: `front/seedgrid-fe-components/apps/showcase/src/app/components/gadgets/sg-clock/samples/digital.tsx.sample`
|
|
79
|
+
|
|
80
|
+
```tsx
|
|
81
|
+
import * as React from "react";
|
|
82
|
+
import { SgButton, SgClock } from "@seedgrid/fe-components";
|
|
83
|
+
|
|
84
|
+
export default function Example(props: { initialServerTime?: string }) {
|
|
85
|
+
const [format, setFormat] = React.useState<"12h" | "24h">("24h");
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<>
|
|
89
|
+
<div className="mb-3 flex flex-wrap items-center gap-2">
|
|
90
|
+
<SgButton onClick={() => setFormat((value) => (value === "24h" ? "12h" : "24h"))}>
|
|
91
|
+
{format}
|
|
92
|
+
</SgButton>
|
|
93
|
+
</div>
|
|
94
|
+
|
|
95
|
+
<div className="grid gap-4 md:grid-cols-3">
|
|
96
|
+
<SgClock
|
|
97
|
+
clockStyle="digital"
|
|
98
|
+
size="sm"
|
|
99
|
+
initialServerTime={props.initialServerTime ?? new Date().toISOString()}
|
|
100
|
+
timezone="America/Sao_Paulo"
|
|
101
|
+
format={format}
|
|
102
|
+
digitalStyle="default"
|
|
103
|
+
/>
|
|
104
|
+
<SgClock
|
|
105
|
+
clockStyle="digital"
|
|
106
|
+
size="md"
|
|
107
|
+
initialServerTime={props.initialServerTime ?? new Date().toISOString()}
|
|
108
|
+
timezone="America/Sao_Paulo"
|
|
109
|
+
format={format}
|
|
110
|
+
digitalStyle="default"
|
|
111
|
+
/>
|
|
112
|
+
<SgClock
|
|
113
|
+
clockStyle="digital"
|
|
114
|
+
size="lg"
|
|
115
|
+
initialServerTime={props.initialServerTime ?? new Date().toISOString()}
|
|
116
|
+
timezone="America/Sao_Paulo"
|
|
117
|
+
format={format}
|
|
118
|
+
digitalStyle="default"
|
|
119
|
+
/>
|
|
120
|
+
</div>
|
|
121
|
+
</>
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Analógico com tema inline (sem provider nem registro)
|
|
127
|
+
|
|
128
|
+
Fonte: `front/seedgrid-fe-components/apps/showcase/src/app/components/gadgets/sg-clock/samples/tema-inline-sem-provider.tsx.sample`
|
|
129
|
+
|
|
130
|
+
```tsx
|
|
131
|
+
import React from "react";
|
|
132
|
+
import { SgClock } from "@seedgrid/fe-components";
|
|
133
|
+
|
|
134
|
+
export default function Example(props: { initialServerTime?: string }) {
|
|
135
|
+
return (
|
|
136
|
+
<div className="flex items-center justify-center rounded-xl border border-border bg-background p-6">
|
|
137
|
+
<SgClock
|
|
138
|
+
clockStyle="analog"
|
|
139
|
+
size={260}
|
|
140
|
+
initialServerTime={props.initialServerTime ?? new Date().toISOString()}
|
|
141
|
+
showSeconds
|
|
142
|
+
secondHandMode="smooth"
|
|
143
|
+
theme={{
|
|
144
|
+
id: "inline-theme",
|
|
145
|
+
label: "Inline Theme",
|
|
146
|
+
render: () => (
|
|
147
|
+
<>
|
|
148
|
+
<circle cx="50" cy="50" r="48" className="fill-white dark:fill-neutral-950" />
|
|
149
|
+
<circle cx="50" cy="50" r="47" className="fill-none stroke-neutral-300 dark:stroke-neutral-700" />
|
|
150
|
+
<circle cx="50" cy="50" r="38" className="fill-none stroke-neutral-200 dark:stroke-neutral-800" />
|
|
151
|
+
<circle cx="50" cy="50" r="6" className="fill-neutral-100 dark:fill-neutral-900" />
|
|
152
|
+
<text x="50" y="56" textAnchor="middle" className="fill-neutral-400 text-[8px] font-semibold">
|
|
153
|
+
SeedGrid
|
|
154
|
+
</text>
|
|
155
|
+
</>
|
|
156
|
+
),
|
|
157
|
+
}}
|
|
158
|
+
/>
|
|
159
|
+
</div>
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Fusos horários
|
|
165
|
+
|
|
166
|
+
Fonte: `front/seedgrid-fe-components/apps/showcase/src/app/components/gadgets/sg-clock/samples/timezone.tsx.sample`
|
|
167
|
+
|
|
168
|
+
```tsx
|
|
169
|
+
import * as React from "react";
|
|
170
|
+
import { SgButton, SgClock } from "@seedgrid/fe-components";
|
|
171
|
+
|
|
172
|
+
export default function Example(props: { initialServerTime?: string }) {
|
|
173
|
+
const [timezone, setTimezone] = React.useState("America/Sao_Paulo");
|
|
174
|
+
const [format, setFormat] = React.useState<"12h" | "24h">("24h");
|
|
175
|
+
|
|
176
|
+
return (
|
|
177
|
+
<>
|
|
178
|
+
<div className="flex flex-wrap gap-2">
|
|
179
|
+
<SgButton onClick={() => setTimezone("America/Sao_Paulo")}>Sao Paulo</SgButton>
|
|
180
|
+
<SgButton onClick={() => setTimezone("Europe/Lisbon")}>Lisboa</SgButton>
|
|
181
|
+
<SgButton onClick={() => setTimezone("America/New_York")}>New York</SgButton>
|
|
182
|
+
<SgButton onClick={() => setTimezone("Asia/Tokyo")}>Tokyo</SgButton>
|
|
183
|
+
<SgButton onClick={() => setFormat((value) => (value === "24h" ? "12h" : "24h"))}>
|
|
184
|
+
{format}
|
|
185
|
+
</SgButton>
|
|
186
|
+
</div>
|
|
187
|
+
|
|
188
|
+
<div className="mt-4 grid gap-3 md:grid-cols-2">
|
|
189
|
+
<SgClock
|
|
190
|
+
clockStyle="digital"
|
|
191
|
+
size="lg"
|
|
192
|
+
initialServerTime={props.initialServerTime ?? new Date().toISOString()}
|
|
193
|
+
timezone={timezone}
|
|
194
|
+
format={format}
|
|
195
|
+
showSeconds
|
|
196
|
+
/>
|
|
197
|
+
<SgClock
|
|
198
|
+
clockStyle="analog"
|
|
199
|
+
size={220}
|
|
200
|
+
initialServerTime={props.initialServerTime ?? new Date().toISOString()}
|
|
201
|
+
themeId="seedgrid"
|
|
202
|
+
timezone={timezone}
|
|
203
|
+
showSeconds={false}
|
|
204
|
+
/>
|
|
205
|
+
</div>
|
|
206
|
+
</>
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Observação: o tema `seedgrid` usado acima só resolve se os temas embutidos tiverem sido registrados (o showcase registra em `sg-clock-showcase-client.tsx`). Ver o exemplo seguinte.
|
|
212
|
+
|
|
213
|
+
### Temas embutidos + provider + picker (exemplo próprio)
|
|
214
|
+
|
|
215
|
+
Os temas de `sgClockThemesBuiltIn` precisam ser registrados; o `SgClockThemePicker` lista os temas do `SgClockThemeProvider`.
|
|
216
|
+
|
|
217
|
+
```tsx
|
|
218
|
+
"use client";
|
|
219
|
+
|
|
220
|
+
import React from "react";
|
|
221
|
+
import {
|
|
222
|
+
SgClock,
|
|
223
|
+
SgClockThemePicker,
|
|
224
|
+
SgClockThemeProvider,
|
|
225
|
+
registerThemes,
|
|
226
|
+
sgClockThemesBuiltIn
|
|
227
|
+
} from "@seedgrid/fe-components";
|
|
228
|
+
|
|
229
|
+
// Os temas embutidos NÃO são registrados automaticamente: registre uma vez no boot do app.
|
|
230
|
+
registerThemes(sgClockThemesBuiltIn);
|
|
231
|
+
|
|
232
|
+
export default function Example() {
|
|
233
|
+
const [themeId, setThemeId] = React.useState("classic");
|
|
234
|
+
|
|
235
|
+
return (
|
|
236
|
+
<SgClockThemeProvider value={{ mode: "fallback", fallbackThemeId: "classic" }}>
|
|
237
|
+
<SgClockThemePicker value={themeId} onChange={setThemeId} label="Tema" />
|
|
238
|
+
<SgClock clockStyle="analog" size={240} themeId={themeId} />
|
|
239
|
+
</SgClockThemeProvider>
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## SgTimeProvider / useSgTime
|
|
245
|
+
|
|
246
|
+
### Uso básico
|
|
247
|
+
|
|
248
|
+
Fonte: `front/seedgrid-fe-components/apps/showcase/src/app/components/providers/sg-time-provider/samples/uso-basico.tsx.sample`
|
|
249
|
+
|
|
250
|
+
```tsx
|
|
251
|
+
import { SgTimeProvider, useSgTime } from "@seedgrid/fe-components";
|
|
252
|
+
|
|
253
|
+
function LiveClock() {
|
|
254
|
+
const time = useSgTime();
|
|
255
|
+
const date = new Date(time.nowMs());
|
|
256
|
+
const pad = (n: number) => String(n).padStart(2, "0");
|
|
257
|
+
|
|
258
|
+
return (
|
|
259
|
+
<span>
|
|
260
|
+
{pad(date.getHours())}:{pad(date.getMinutes())}:{pad(date.getSeconds())}
|
|
261
|
+
</span>
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export default function App() {
|
|
266
|
+
return (
|
|
267
|
+
<SgTimeProvider initialServerTime={new Date().toISOString()}>
|
|
268
|
+
<LiveClock />
|
|
269
|
+
</SgTimeProvider>
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
## SgClockThemeProvider / useSgClockThemeResolver
|
|
275
|
+
|
|
276
|
+
### Resolvedor via hook (modo `fallback`)
|
|
277
|
+
|
|
278
|
+
Só há temas a listar/resolver se algum tiver sido registrado (`registerThemes`) ou passado em `value.themes`; caso contrário `resolve("classic")` retorna `null`.
|
|
279
|
+
|
|
280
|
+
Fonte: `front/seedgrid-fe-components/apps/showcase/src/app/components/providers/sg-clock-theme-provider/samples/resolver-hook.tsx.sample`
|
|
281
|
+
|
|
282
|
+
```tsx
|
|
283
|
+
"use client";
|
|
284
|
+
|
|
285
|
+
import { SgClockThemeProvider, useSgClockThemeResolver } from "@seedgrid/fe-components";
|
|
286
|
+
|
|
287
|
+
function TemasDisponiveis() {
|
|
288
|
+
// Fora de um SgClockThemeProvider o hook devolve null: sem provider nao ha
|
|
289
|
+
// resolvedor, e nao existe fallback implicito.
|
|
290
|
+
const resolver = useSgClockThemeResolver();
|
|
291
|
+
if (!resolver) return <div>Sem SgClockThemeProvider na arvore.</div>;
|
|
292
|
+
|
|
293
|
+
const classic = resolver.resolve("classic");
|
|
294
|
+
const inexistente = resolver.resolve("nao-existe");
|
|
295
|
+
|
|
296
|
+
return (
|
|
297
|
+
<div className="space-y-1 text-sm">
|
|
298
|
+
<div>temas visíveis: {resolver.list().map((theme) => theme.id).join(", ")}</div>
|
|
299
|
+
<div>resolve("classic"): {classic?.id ?? "null"}</div>
|
|
300
|
+
{/* modo "fallback" (padrão) cai no fallbackThemeId; em "strict" isto seria null */}
|
|
301
|
+
<div>resolve("nao-existe"): {inexistente?.id ?? "null"}</div>
|
|
302
|
+
</div>
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export default function Example() {
|
|
307
|
+
return (
|
|
308
|
+
<SgClockThemeProvider value={{ mode: "fallback", fallbackThemeId: "classic" }}>
|
|
309
|
+
<TemasDisponiveis />
|
|
310
|
+
</SgClockThemeProvider>
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
## SgClockThemePreview
|
|
316
|
+
|
|
317
|
+
### Galeria dos temas embutidos (exemplo próprio)
|
|
318
|
+
|
|
319
|
+
```tsx
|
|
320
|
+
import { SgClockThemePreview, sgClockThemesBuiltIn } from "@seedgrid/fe-components";
|
|
321
|
+
|
|
322
|
+
export default function Example() {
|
|
323
|
+
return (
|
|
324
|
+
<div style={{ display: "flex", gap: 12 }}>
|
|
325
|
+
{sgClockThemesBuiltIn.map((theme) => (
|
|
326
|
+
<SgClockThemePreview key={theme.id} theme={theme} size={72} />
|
|
327
|
+
))}
|
|
328
|
+
</div>
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
## SgLinearGauge
|
|
334
|
+
|
|
335
|
+
### Horizontal com faixas e ponteiro arrastável
|
|
336
|
+
|
|
337
|
+
Fonte: `front/seedgrid-fe-components/apps/showcase/src/app/components/gadgets/sg-linear-gauge/samples/default-horizontal.tsx.sample`
|
|
338
|
+
|
|
339
|
+
```tsx
|
|
340
|
+
"use client";
|
|
341
|
+
|
|
342
|
+
import React from "react";
|
|
343
|
+
import { SgLinearGauge } from "@seedgrid/fe-components";
|
|
344
|
+
|
|
345
|
+
export default function Example() {
|
|
346
|
+
const [value, setValue] = React.useState(64);
|
|
347
|
+
|
|
348
|
+
return (
|
|
349
|
+
<SgLinearGauge
|
|
350
|
+
min={0}
|
|
351
|
+
max={100}
|
|
352
|
+
value={value}
|
|
353
|
+
onValueChange={setValue}
|
|
354
|
+
ranges={[
|
|
355
|
+
{ start: 0, end: 30, color: "#22c55e" },
|
|
356
|
+
{ start: 30, end: 70, color: "#f59e0b" },
|
|
357
|
+
{ start: 70, end: 100, color: "#ef4444" }
|
|
358
|
+
]}
|
|
359
|
+
showTicks
|
|
360
|
+
showLabels
|
|
361
|
+
majorTickCount={5}
|
|
362
|
+
barPointer
|
|
363
|
+
primaryPointerDraggable
|
|
364
|
+
/>
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### Vertical com múltiplos ponteiros
|
|
370
|
+
|
|
371
|
+
Fonte: `front/seedgrid-fe-components/apps/showcase/src/app/components/gadgets/sg-linear-gauge/samples/vertical-multi-pointers.tsx.sample`
|
|
372
|
+
|
|
373
|
+
```tsx
|
|
374
|
+
"use client";
|
|
375
|
+
|
|
376
|
+
import React from "react";
|
|
377
|
+
import { SgLinearGauge, type SgLinearGaugePointer } from "@seedgrid/fe-components";
|
|
378
|
+
|
|
379
|
+
export default function Example() {
|
|
380
|
+
const [temperature, setTemperature] = React.useState(28);
|
|
381
|
+
const [target, setTarget] = React.useState(72);
|
|
382
|
+
const pointers = React.useMemo<SgLinearGaugePointer[]>(
|
|
383
|
+
() => [
|
|
384
|
+
{ id: "target", value: target, color: "hsl(var(--secondary))", shape: "diamond", size: 11, draggable: true },
|
|
385
|
+
{ id: "temp", value: temperature, color: "hsl(var(--destructive))", shape: "circle", size: 10, draggable: true }
|
|
386
|
+
],
|
|
387
|
+
[target, temperature]
|
|
388
|
+
);
|
|
389
|
+
|
|
390
|
+
return (
|
|
391
|
+
<SgLinearGauge
|
|
392
|
+
min={-10}
|
|
393
|
+
max={50}
|
|
394
|
+
value={temperature}
|
|
395
|
+
onValueChange={setTemperature}
|
|
396
|
+
orientation="vertical"
|
|
397
|
+
width={140}
|
|
398
|
+
height={360}
|
|
399
|
+
pointers={pointers}
|
|
400
|
+
onPointerValueChange={(pointerId, next) => {
|
|
401
|
+
if (pointerId === "target") setTarget(next);
|
|
402
|
+
if (pointerId === "temp") setTemperature(next);
|
|
403
|
+
}}
|
|
404
|
+
ranges={[
|
|
405
|
+
{ start: -10, end: 15, color: "#38bdf8" },
|
|
406
|
+
{ start: 15, end: 30, color: "#22c55e" },
|
|
407
|
+
{ start: 30, end: 50, color: "#ef4444" }
|
|
408
|
+
]}
|
|
409
|
+
majorTickCount={6}
|
|
410
|
+
minorTicksPerInterval={1}
|
|
411
|
+
labelFormatter={(value) => `${Math.round(value)}°`}
|
|
412
|
+
/>
|
|
413
|
+
);
|
|
414
|
+
}
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
## SgRadialGauge
|
|
418
|
+
|
|
419
|
+
### Estilo velocímetro com conteúdo central
|
|
420
|
+
|
|
421
|
+
Fonte: `front/seedgrid-fe-components/apps/showcase/src/app/components/gadgets/sg-radial-gauge/samples/speedometer-style.tsx.sample`
|
|
422
|
+
|
|
423
|
+
```tsx
|
|
424
|
+
"use client";
|
|
425
|
+
|
|
426
|
+
import React from "react";
|
|
427
|
+
import { SgRadialGauge } from "@seedgrid/fe-components";
|
|
428
|
+
|
|
429
|
+
export default function Example() {
|
|
430
|
+
const [rpm, setRpm] = React.useState(62);
|
|
431
|
+
|
|
432
|
+
return (
|
|
433
|
+
<SgRadialGauge
|
|
434
|
+
min={0}
|
|
435
|
+
max={100}
|
|
436
|
+
value={rpm}
|
|
437
|
+
onValueChange={setRpm}
|
|
438
|
+
startAngle={140}
|
|
439
|
+
endAngle={40}
|
|
440
|
+
ranges={[
|
|
441
|
+
{ start: 0, end: 40, color: "#22c55e", width: 14 },
|
|
442
|
+
{ start: 40, end: 75, color: "#f59e0b", width: 14 },
|
|
443
|
+
{ start: 75, end: 100, color: "#ef4444", width: 14 }
|
|
444
|
+
]}
|
|
445
|
+
showTicks
|
|
446
|
+
showLabels
|
|
447
|
+
majorTickCount={5}
|
|
448
|
+
minorTicksPerInterval={2}
|
|
449
|
+
primaryPointerDraggable
|
|
450
|
+
primaryPointerType="needle"
|
|
451
|
+
primaryPointerColor="#2563eb"
|
|
452
|
+
centerContent={<div className="text-center"><div className="text-xs uppercase text-muted-foreground">RPM</div><div className="text-xl font-semibold">{Math.round(rpm)}</div></div>}
|
|
453
|
+
/>
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
### Ponteiros extras (marker/range) e anotação
|
|
459
|
+
|
|
460
|
+
Fonte: `front/seedgrid-fe-components/apps/showcase/src/app/components/gadgets/sg-radial-gauge/samples/multi-pointers-annotation.tsx.sample`
|
|
461
|
+
|
|
462
|
+
```tsx
|
|
463
|
+
"use client";
|
|
464
|
+
|
|
465
|
+
import React from "react";
|
|
466
|
+
import { SgRadialGauge, type SgRadialGaugePointer } from "@seedgrid/fe-components";
|
|
467
|
+
|
|
468
|
+
export default function Example() {
|
|
469
|
+
const [rpm, setRpm] = React.useState(62);
|
|
470
|
+
const [target, setTarget] = React.useState(70);
|
|
471
|
+
const [consumption, setConsumption] = React.useState(44);
|
|
472
|
+
const pointers = React.useMemo<SgRadialGaugePointer[]>(
|
|
473
|
+
() => [
|
|
474
|
+
{ id: "target", type: "marker", value: target, color: "#8b5cf6", size: 13, shape: "diamond", draggable: true, label: String(Math.round(target)) },
|
|
475
|
+
{ id: "consumption", type: "range", value: consumption, color: "#14b8a6", width: 10, draggable: true }
|
|
476
|
+
],
|
|
477
|
+
[consumption, target]
|
|
478
|
+
);
|
|
479
|
+
|
|
480
|
+
return (
|
|
481
|
+
<SgRadialGauge
|
|
482
|
+
min={0}
|
|
483
|
+
max={120}
|
|
484
|
+
value={rpm}
|
|
485
|
+
onValueChange={setRpm}
|
|
486
|
+
startAngle={135}
|
|
487
|
+
endAngle={45}
|
|
488
|
+
radiusFactor={0.95}
|
|
489
|
+
ranges={[
|
|
490
|
+
{ start: 0, end: 60, color: "#60a5fa", width: 12, opacity: 0.45 },
|
|
491
|
+
{ start: 60, end: 90, color: "#f59e0b", width: 12, opacity: 0.45 },
|
|
492
|
+
{ start: 90, end: 120, color: "#ef4444", width: 12, opacity: 0.45 }
|
|
493
|
+
]}
|
|
494
|
+
pointers={pointers}
|
|
495
|
+
onPointerValueChange={(id, next) => {
|
|
496
|
+
if (id === "target") setTarget(next);
|
|
497
|
+
if (id === "consumption") setConsumption(next);
|
|
498
|
+
}}
|
|
499
|
+
annotations={[{ id: "target-label", value: target, radiusFactor: 0.8, content: <span className="rounded bg-violet-600 px-2 py-1 text-[10px] font-semibold text-white">target</span> }]}
|
|
500
|
+
primaryPointerDraggable
|
|
501
|
+
/>
|
|
502
|
+
);
|
|
503
|
+
}
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
## SgQRCode
|
|
507
|
+
|
|
508
|
+
### Interativo com logo e fallback
|
|
509
|
+
|
|
510
|
+
Fonte: `front/seedgrid-fe-components/apps/showcase/src/app/components/gadgets/sg-qr-code/samples/interactive-example.tsx.sample`
|
|
511
|
+
|
|
512
|
+
```tsx
|
|
513
|
+
"use client";
|
|
514
|
+
|
|
515
|
+
import React from "react";
|
|
516
|
+
import { SgButton, SgQRCode } from "@seedgrid/fe-components";
|
|
517
|
+
|
|
518
|
+
export default function Example() {
|
|
519
|
+
const [value, setValue] = React.useState("https://seedgrid.com.br");
|
|
520
|
+
const [logoSrc, setLogoSrc] = React.useState("/logo-seedgrid.svg");
|
|
521
|
+
const [size, setSize] = React.useState(220);
|
|
522
|
+
|
|
523
|
+
return (
|
|
524
|
+
<div className="grid gap-6 md:grid-cols-[1fr_auto]">
|
|
525
|
+
<div className="space-y-4">
|
|
526
|
+
<label className="block space-y-1">
|
|
527
|
+
<span className="text-sm font-medium">Valor do QR Code</span>
|
|
528
|
+
<textarea
|
|
529
|
+
value={value}
|
|
530
|
+
onChange={(event) => setValue(event.target.value)}
|
|
531
|
+
rows={3}
|
|
532
|
+
className="w-full rounded-md border border-border bg-background px-3 py-2 text-sm outline-none ring-offset-background focus-visible:ring-2 focus-visible:ring-primary"
|
|
533
|
+
placeholder="Digite texto, URL ou payload PIX"
|
|
534
|
+
/>
|
|
535
|
+
</label>
|
|
536
|
+
|
|
537
|
+
<label className="block space-y-1">
|
|
538
|
+
<span className="text-sm font-medium">Logo central (URL)</span>
|
|
539
|
+
<input
|
|
540
|
+
type="text"
|
|
541
|
+
value={logoSrc}
|
|
542
|
+
onChange={(event) => setLogoSrc(event.target.value)}
|
|
543
|
+
className="w-full rounded-md border border-border bg-background px-3 py-2 text-sm outline-none ring-offset-background focus-visible:ring-2 focus-visible:ring-primary"
|
|
544
|
+
placeholder="https://exemplo.com/logo.png"
|
|
545
|
+
/>
|
|
546
|
+
</label>
|
|
547
|
+
|
|
548
|
+
<label className="block space-y-1">
|
|
549
|
+
<span className="text-sm font-medium">Tamanho: {size}px</span>
|
|
550
|
+
<input type="range" min={140} max={360} step={4} value={size} onChange={(event) => setSize(Number(event.target.value))} className="w-full" />
|
|
551
|
+
</label>
|
|
552
|
+
|
|
553
|
+
<div className="flex flex-wrap gap-2">
|
|
554
|
+
<SgButton onClick={() => setValue("https://seedgrid.com.br")}>URL</SgButton>
|
|
555
|
+
<SgButton severity="secondary" onClick={() => setValue("00020126580014BR.GOV.BCB.PIX0136contato@seedgrid.com.br5204000053039865802BR5922SEEDGRID TECNOLOGIA6009SAO PAULO62140510SEEDGRID1236304ABCD")}>
|
|
556
|
+
Exemplo PIX
|
|
557
|
+
</SgButton>
|
|
558
|
+
<SgButton severity="warning" onClick={() => setLogoSrc("")}>Sem logo</SgButton>
|
|
559
|
+
</div>
|
|
560
|
+
</div>
|
|
561
|
+
|
|
562
|
+
<div className="inline-flex h-fit rounded-xl border border-border bg-background p-4 shadow-sm">
|
|
563
|
+
<SgQRCode
|
|
564
|
+
value={value}
|
|
565
|
+
size={size}
|
|
566
|
+
logoSrc={logoSrc || undefined}
|
|
567
|
+
logoAlt="Logo SeedGrid"
|
|
568
|
+
emptyFallback={<span className="text-sm text-muted-foreground">Informe um valor</span>}
|
|
569
|
+
/>
|
|
570
|
+
</div>
|
|
571
|
+
</div>
|
|
572
|
+
);
|
|
573
|
+
}
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
## SgStringAnimator
|
|
577
|
+
|
|
578
|
+
### `autoStart` ao trocar o alvo
|
|
579
|
+
|
|
580
|
+
Fonte: `front/seedgrid-fe-components/apps/showcase/src/app/components/gadgets/sg-string-animator/samples/autostart-animacao-automatica.tsx.sample`
|
|
581
|
+
|
|
582
|
+
```tsx
|
|
583
|
+
"use client";
|
|
584
|
+
|
|
585
|
+
import React from "react";
|
|
586
|
+
import { SgStringAnimator } from "@seedgrid/fe-components";
|
|
587
|
+
|
|
588
|
+
const NAMES = ["LUCIANO", "MARTA", "PEDRO", "ANA", "SEEDGRID"];
|
|
589
|
+
|
|
590
|
+
export default function Example() {
|
|
591
|
+
const [index, setIndex] = React.useState(0);
|
|
592
|
+
const [target, setTarget] = React.useState(NAMES[0] ?? "");
|
|
593
|
+
|
|
594
|
+
React.useEffect(() => {
|
|
595
|
+
const id = window.setInterval(() => {
|
|
596
|
+
setIndex((prev) => {
|
|
597
|
+
const next = (prev + 1) % NAMES.length;
|
|
598
|
+
setTarget(NAMES[next] ?? "");
|
|
599
|
+
return next;
|
|
600
|
+
});
|
|
601
|
+
}, 1800);
|
|
602
|
+
return () => window.clearInterval(id);
|
|
603
|
+
}, []);
|
|
604
|
+
|
|
605
|
+
return <SgStringAnimator sourceString={NAMES[index] ?? ""} targetString={target} stringAnimatorStyle="roller3d" alignTo="left" fontSize={28} autoStart />;
|
|
606
|
+
}
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
### Disparo imperativo via `ref` (números alinhados à direita)
|
|
610
|
+
|
|
611
|
+
Fonte: `front/seedgrid-fe-components/apps/showcase/src/app/components/gadgets/sg-string-animator/samples/roller-3d-numeros-right-aligned.tsx.sample`
|
|
612
|
+
|
|
613
|
+
```tsx
|
|
614
|
+
"use client";
|
|
615
|
+
|
|
616
|
+
import React from "react";
|
|
617
|
+
import { SgStringAnimator, type SgStringAnimatorRef, SgButton } from "@seedgrid/fe-components";
|
|
618
|
+
|
|
619
|
+
export default function Example() {
|
|
620
|
+
const animRef = React.useRef<SgStringAnimatorRef>(null);
|
|
621
|
+
|
|
622
|
+
return (
|
|
623
|
+
<div className="flex flex-col gap-4">
|
|
624
|
+
<SgStringAnimator ref={animRef} sourceString="42" targetString="1337" stringAnimatorStyle="roller3d" alignTo="right" fontSize={28} />
|
|
625
|
+
<SgButton size="sm" onClick={() => animRef.current?.startAnimation()}>Animar</SgButton>
|
|
626
|
+
</div>
|
|
627
|
+
);
|
|
628
|
+
}
|
|
629
|
+
```
|