@seedgrid/fe-components 2026.9.5 → 2026.10.1

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.
Files changed (44) hide show
  1. package/dist/ai/seedgrid-components.manifest.json +361 -240
  2. package/dist/digits/matrix-digit/SgMatrixDigit.meta.d.ts.map +1 -1
  3. package/dist/digits/matrix-digit/SgMatrixDigit.meta.js +0 -1
  4. package/dist/digits/roller3d-digit/SgRoller3DDigit.meta.d.ts.map +1 -1
  5. package/dist/digits/roller3d-digit/SgRoller3DDigit.meta.js +0 -2
  6. package/dist/digits/segment-digit/SgSegmentDigit.meta.d.ts.map +1 -1
  7. package/dist/digits/segment-digit/SgSegmentDigit.meta.js +0 -2
  8. package/dist/digits/seven-segment-digit/SgSevenSegmentDigit.meta.d.ts.map +1 -1
  9. package/dist/digits/seven-segment-digit/SgSevenSegmentDigit.meta.js +0 -1
  10. package/dist/index.d.ts +2 -0
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +1 -0
  13. package/dist/inputs/SgDatatable.meta.d.ts.map +1 -1
  14. package/dist/inputs/SgDatatable.meta.js +0 -1
  15. package/dist/inputs/SgInputCurrency.meta.d.ts.map +1 -1
  16. package/dist/inputs/SgInputCurrency.meta.js +0 -21
  17. package/dist/inputs/SgInputNumber.meta.d.ts.map +1 -1
  18. package/dist/inputs/SgInputNumber.meta.js +0 -14
  19. package/dist/inputs/SgInputTextarea.meta.d.ts.map +1 -1
  20. package/dist/inputs/SgInputTextarea.meta.js +0 -14
  21. package/dist/inputs/SgPickList.meta.d.ts.map +1 -1
  22. package/dist/inputs/SgPickList.meta.js +0 -3
  23. package/dist/inputs/SgRating.meta.d.ts.map +1 -1
  24. package/dist/inputs/SgRating.meta.js +0 -1
  25. package/dist/inputs/SgTextEditor.meta.d.ts.map +1 -1
  26. package/dist/inputs/SgTextEditor.meta.js +0 -4
  27. package/dist/inputs/SgToggleSwitch.meta.d.ts.map +1 -1
  28. package/dist/inputs/SgToggleSwitch.meta.js +0 -3
  29. package/dist/layout/SgTreeView.meta.d.ts.map +1 -1
  30. package/dist/layout/SgTreeView.meta.js +0 -30
  31. package/dist/realtime/SgRealtimeProvider.d.ts +71 -0
  32. package/dist/realtime/SgRealtimeProvider.d.ts.map +1 -0
  33. package/dist/realtime/SgRealtimeProvider.js +170 -0
  34. package/dist/realtime/SgRealtimeProvider.meta.d.ts +4 -0
  35. package/dist/realtime/SgRealtimeProvider.meta.d.ts.map +1 -0
  36. package/dist/realtime/SgRealtimeProvider.meta.js +50 -0
  37. package/dist/realtime/useSgRealtimeEvent.meta.d.ts +4 -0
  38. package/dist/realtime/useSgRealtimeEvent.meta.d.ts.map +1 -0
  39. package/dist/realtime/useSgRealtimeEvent.meta.js +33 -0
  40. package/dist/realtime/useSgRealtimeStatus.meta.d.ts +4 -0
  41. package/dist/realtime/useSgRealtimeStatus.meta.d.ts.map +1 -0
  42. package/dist/realtime/useSgRealtimeStatus.meta.js +32 -0
  43. package/dist/sandbox.cjs +21 -21
  44. package/package.json +5 -4
@@ -0,0 +1,170 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import React from "react";
4
+ import { PulseSubscriberClient } from "@seedgrid/pulse-subscriber-react/core";
5
+ const SgRealtimeContext = React.createContext(null);
6
+ const DEFAULT_TOKEN_ENDPOINT = "/api/realtime/connect";
7
+ function mapState(state) {
8
+ switch (state) {
9
+ case "connected":
10
+ case "subscribed":
11
+ return "connected";
12
+ case "connecting":
13
+ return "connecting";
14
+ case "reconnecting":
15
+ return "reconnecting";
16
+ case "auth-error":
17
+ return "auth-error";
18
+ case "idle":
19
+ return "idle";
20
+ default:
21
+ return "unavailable";
22
+ }
23
+ }
24
+ async function requestToken(endpoint, init) {
25
+ const response = await fetch(endpoint, { method: "POST", credentials: "same-origin", ...init });
26
+ if (!response.ok) {
27
+ throw new Error(`token de realtime indisponivel (HTTP ${response.status})`);
28
+ }
29
+ return (await response.json());
30
+ }
31
+ /**
32
+ * Avisos em tempo real para o usuário logado, pelo SeedGrid Pulse.
33
+ *
34
+ * Monte uma vez (no layout) e ouça com {@link useSgRealtimeEvent}. Pede o token de conexão ao backend
35
+ * (que o emite para o usuário DA SESSÃO), conecta direto no Pulse, reconecta sozinho e para se a
36
+ * credencial for revogada. O token fica só em memória. Quando o realtime não está conectado,
37
+ * {@link useSgRealtimeStatus} diz isso e a tela volta ao polling.
38
+ */
39
+ export function SgRealtimeProvider({ children, channels, tokenEndpoint = DEFAULT_TOKEN_ENDPOINT, tokenRequestInit, fetchToken, pulseUrl, enabled = true, connectOptions }) {
40
+ const [status, setStatus] = React.useState("idle");
41
+ const listenersRef = React.useRef(new Set());
42
+ // Lidos sempre o mais recente, sem reconectar a cada render.
43
+ const optionsRef = React.useRef({ tokenEndpoint, tokenRequestInit, fetchToken, connectOptions });
44
+ optionsRef.current = { tokenEndpoint, tokenRequestInit, fetchToken, connectOptions };
45
+ const channelsKey = channels.join(",");
46
+ React.useEffect(() => {
47
+ if (typeof window === "undefined" || !enabled || channelsKey.length === 0) {
48
+ setStatus(enabled ? "idle" : "disabled");
49
+ return undefined;
50
+ }
51
+ let cancelled = false;
52
+ const client = new PulseSubscriberClient();
53
+ const getConnectResponse = () => {
54
+ const { tokenEndpoint: endpoint, tokenRequestInit: init, fetchToken: custom } = optionsRef.current;
55
+ return custom ? custom() : requestToken(endpoint, init);
56
+ };
57
+ const offState = client.onStateChange((state) => {
58
+ if (!cancelled)
59
+ setStatus(mapState(state));
60
+ });
61
+ const offMessage = client.onMessage((message) => {
62
+ const event = {
63
+ channel: message.channel,
64
+ event: message.event,
65
+ data: message.data,
66
+ messageId: message.messageId,
67
+ publishedAt: message.publishedAt
68
+ };
69
+ listenersRef.current.forEach((listener) => {
70
+ try {
71
+ listener(event);
72
+ }
73
+ catch {
74
+ // Um handler com defeito não derruba os outros.
75
+ }
76
+ });
77
+ });
78
+ void (async () => {
79
+ setStatus("connecting");
80
+ let first;
81
+ try {
82
+ first = await getConnectResponse();
83
+ }
84
+ catch {
85
+ if (!cancelled)
86
+ setStatus("unavailable");
87
+ return;
88
+ }
89
+ if (cancelled)
90
+ return;
91
+ if (!first.enabled || !first.token) {
92
+ setStatus("disabled");
93
+ return;
94
+ }
95
+ // O primeiro token já está em mãos; as reconexões pedem um novo (ele vale só para entrar).
96
+ let pending = first;
97
+ const getToken = async () => {
98
+ const response = pending ?? (await getConnectResponse());
99
+ pending = null;
100
+ if (!response.enabled || !response.token) {
101
+ throw new Error("realtime desligado");
102
+ }
103
+ return { token: response.token, expiresAt: response.expiresAt, connectUrl: response.connectUrl };
104
+ };
105
+ try {
106
+ await client.connect({
107
+ ...optionsRef.current.connectOptions,
108
+ getToken,
109
+ ...(pulseUrl ? { baseUrl: pulseUrl } : {})
110
+ });
111
+ if (cancelled)
112
+ return;
113
+ await client.subscribe({ channels: channelsKey.split(",") });
114
+ }
115
+ catch {
116
+ // O estado já reflete a falha (onStateChange); a tela fica no polling.
117
+ }
118
+ })();
119
+ return () => {
120
+ cancelled = true;
121
+ offState();
122
+ offMessage();
123
+ void client.disconnect().catch(() => undefined);
124
+ };
125
+ }, [enabled, channelsKey, pulseUrl]);
126
+ const addListener = React.useCallback((listener) => {
127
+ listenersRef.current.add(listener);
128
+ return () => {
129
+ listenersRef.current.delete(listener);
130
+ };
131
+ }, []);
132
+ const value = React.useMemo(() => ({ status, addListener }), [status, addListener]);
133
+ return _jsx(SgRealtimeContext.Provider, { value: value, children: children });
134
+ }
135
+ /** `backup.finished.*` casa `backup.finished.success` e `backup.finished.error`. */
136
+ export function matchesSgRealtimeEvent(pattern, event) {
137
+ if (pattern === "*" || pattern === event)
138
+ return true;
139
+ if (pattern.endsWith(".*")) {
140
+ const prefix = pattern.slice(0, -1);
141
+ return event.startsWith(prefix);
142
+ }
143
+ return false;
144
+ }
145
+ /**
146
+ * Chama `handler` a cada aviso do `channel` cujo nome casa `events` (aceita `prefixo.*`). Fora de um
147
+ * {@link SgRealtimeProvider}, não faz nada.
148
+ */
149
+ export function useSgRealtimeEvent(channel, events, handler) {
150
+ const context = React.useContext(SgRealtimeContext);
151
+ const handlerRef = React.useRef(handler);
152
+ handlerRef.current = handler;
153
+ const patterns = Array.isArray(events) ? events.join("|") : events;
154
+ React.useEffect(() => {
155
+ if (!context)
156
+ return undefined;
157
+ const list = patterns.split("|");
158
+ return context.addListener((event) => {
159
+ if (event.channel === channel && list.some((pattern) => matchesSgRealtimeEvent(pattern, event.event))) {
160
+ handlerRef.current(event);
161
+ }
162
+ });
163
+ }, [context, channel, patterns]);
164
+ }
165
+ /** Estado do realtime. `connected` falso ⇒ a tela usa o polling de reserva. */
166
+ export function useSgRealtimeStatus() {
167
+ const context = React.useContext(SgRealtimeContext);
168
+ const status = context?.status ?? "disabled";
169
+ return { status, connected: status === "connected" };
170
+ }
@@ -0,0 +1,4 @@
1
+ import type { SgAiHintsV0, SgMetaV0 } from "../ai-meta/types";
2
+ export declare const sgMeta: SgMetaV0;
3
+ export declare const aiHints: SgAiHintsV0;
4
+ //# sourceMappingURL=SgRealtimeProvider.meta.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SgRealtimeProvider.meta.d.ts","sourceRoot":"","sources":["../../src/realtime/SgRealtimeProvider.meta.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE9D,eAAO,MAAM,MAAM,EAAE,QAgCpB,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,WAiBrB,CAAC"}
@@ -0,0 +1,50 @@
1
+ export const sgMeta = {
2
+ version: "0.1",
3
+ componentId: "provider.realtime",
4
+ package: "@seedgrid/fe-components",
5
+ exportName: "SgRealtimeProvider",
6
+ slug: "sg-realtime-provider",
7
+ displayName: "SgRealtimeProvider",
8
+ category: "provider",
9
+ subcategory: "realtime",
10
+ description: "Avisos em tempo real para o usuario logado pelo SeedGrid Pulse: pede o token de conexao ao backend (que o emite para o usuario da sessao), conecta direto no Pulse, reconecta sozinho, para quando a credencial e revogada e expoe o status para a tela decidir entre aviso e polling de reserva.",
11
+ tags: ["provider", "realtime", "pulse", "websocket", "notification", "polling"],
12
+ capabilities: ["token-from-backend", "auto-reconnect", "channel-subscription", "fallback-status"],
13
+ fieldSemantics: ["realtimeChannels", "tokenEndpoint", "connectionStatus"],
14
+ props: [
15
+ { name: "channels", type: "string[]", required: true, description: "Channels que a aplicacao quer ouvir (ex.: [\"backup\"]).", semanticRole: "data", bindable: true },
16
+ { name: "tokenEndpoint", type: "string", default: "/api/realtime/connect", description: "Rota que entrega o token de conexao do usuario logado (a BFF, na mesma origem).", semanticRole: "behavior", bindable: true },
17
+ { name: "enabled", type: "boolean", default: true, description: "Liga/desliga (ex.: false enquanto nao ha usuario logado).", semanticRole: "behavior", bindable: true },
18
+ { name: "children", type: "ReactNode", required: true, description: "Arvore que ouve os avisos com useSgRealtimeEvent.", semanticRole: "data", bindable: false }
19
+ ],
20
+ states: ["idle", "disabled", "connecting", "connected", "reconnecting", "unavailable", "auth-error"],
21
+ examples: [
22
+ { id: "simulador", title: "Simulador", file: "apps/showcase/src/app/components/providers/sg-realtime-provider/samples/simulador.tsx.sample", kind: "sample" },
23
+ { id: "layout-com-bff", title: "No layout, com a BFF", file: "apps/showcase/src/app/components/providers/sg-realtime-provider/samples/layout-com-bff.tsx.sample", kind: "sample" },
24
+ { id: "polling-de-reserva", title: "Polling de reserva", file: "apps/showcase/src/app/components/providers/sg-realtime-provider/samples/polling-de-reserva.tsx.sample", kind: "sample" }
25
+ ],
26
+ showcase: { route: "/components/providers/sg-realtime-provider", hasPlayground: true, hasPropsTable: true },
27
+ sdui: {
28
+ rendererType: "provider.realtime",
29
+ acceptsDataBinding: true,
30
+ defaultProps: { channels: [], tokenEndpoint: "/api/realtime/connect", enabled: true }
31
+ }
32
+ };
33
+ export const aiHints = {
34
+ version: "0.1",
35
+ preferredUseCases: [
36
+ "Avisar o usuario quando um processo demorado termina (backup, relatorio, importacao) sem polling.",
37
+ "Atualizar uma lista quando o backend publica um evento para o usuario logado."
38
+ ],
39
+ avoidUseCases: [
40
+ "Transportar dados de negocio: o aviso so leva identificadores; busque o registro pela API.",
41
+ "Mensagens entre usuarios ou chat."
42
+ ],
43
+ synonyms: ["realtime provider", "pulse provider", "websocket provider", "push notifications"],
44
+ relatedEntityFields: ["channel", "event", "userPublicId"],
45
+ compositionHints: [
46
+ "Montar uma vez no layout autenticado e ouvir com useSgRealtimeEvent nas telas.",
47
+ "Usar useSgRealtimeStatus para ligar o polling de reserva quando nao estiver conectado."
48
+ ],
49
+ rankingSignals: { freeText: 0, structuredChoice: 0, date: 0, number: 0, denseLayout: 0 }
50
+ };
@@ -0,0 +1,4 @@
1
+ import type { SgAiHintsV0, SgMetaV0 } from "../ai-meta/types";
2
+ export declare const sgMeta: SgMetaV0;
3
+ export declare const aiHints: SgAiHintsV0;
4
+ //# sourceMappingURL=useSgRealtimeEvent.meta.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSgRealtimeEvent.meta.d.ts","sourceRoot":"","sources":["../../src/realtime/useSgRealtimeEvent.meta.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE9D,eAAO,MAAM,MAAM,EAAE,QAwBpB,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,WAQrB,CAAC"}
@@ -0,0 +1,33 @@
1
+ export const sgMeta = {
2
+ version: "0.1",
3
+ componentId: "hook.realtime-event",
4
+ package: "@seedgrid/fe-components",
5
+ exportName: "useSgRealtimeEvent",
6
+ slug: "use-sg-realtime-event",
7
+ displayName: "useSgRealtimeEvent",
8
+ category: "hook",
9
+ subcategory: "realtime",
10
+ description: "Hook que chama o handler a cada aviso do SgRealtimeProvider no channel informado cujo evento casa o padrao (nome, lista, prefixo.* ou *).",
11
+ tags: ["hook", "realtime", "pulse", "event"],
12
+ capabilities: ["event-subscription", "wildcard-match"],
13
+ fieldSemantics: ["realtimeChannel", "eventPattern", "eventHandler"],
14
+ props: [
15
+ { name: "channel", type: "string", required: true, description: "Channel do aviso (ex.: \"backup\").", semanticRole: "data", bindable: false },
16
+ { name: "events", type: "string | string[]", required: true, description: "Nome do evento, lista, \"prefixo.*\" ou \"*\".", semanticRole: "data", bindable: false },
17
+ { name: "handler", type: "(event: SgRealtimeEvent) => void", required: true, description: "Chamado a cada aviso que casa.", semanticRole: "behavior", bindable: false }
18
+ ],
19
+ states: ["listening"],
20
+ examples: [
21
+ { id: "uso-basico", title: "Uso basico", file: "apps/showcase/src/app/components/hooks/use-sg-realtime-event/samples/uso-basico.tsx.sample", kind: "sample" }
22
+ ],
23
+ showcase: { route: "/components/hooks/use-sg-realtime-event", hasPlayground: true, hasPropsTable: true }
24
+ };
25
+ export const aiHints = {
26
+ version: "0.1",
27
+ preferredUseCases: ["Recarregar uma tela quando o backend avisa que um processo terminou."],
28
+ avoidUseCases: ["Ler dados de negocio do aviso: ele so traz identificadores."],
29
+ synonyms: ["realtime event", "pulse event", "on notice"],
30
+ relatedEntityFields: ["channel", "event"],
31
+ compositionHints: ["Usar dentro de um SgRealtimeProvider que assina o channel."],
32
+ rankingSignals: { freeText: 0, structuredChoice: 0, date: 0, number: 0, denseLayout: 0 }
33
+ };
@@ -0,0 +1,4 @@
1
+ import type { SgAiHintsV0, SgMetaV0 } from "../ai-meta/types";
2
+ export declare const sgMeta: SgMetaV0;
3
+ export declare const aiHints: SgAiHintsV0;
4
+ //# sourceMappingURL=useSgRealtimeStatus.meta.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSgRealtimeStatus.meta.d.ts","sourceRoot":"","sources":["../../src/realtime/useSgRealtimeStatus.meta.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE9D,eAAO,MAAM,MAAM,EAAE,QAuBpB,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,WAQrB,CAAC"}
@@ -0,0 +1,32 @@
1
+ export const sgMeta = {
2
+ version: "0.1",
3
+ componentId: "hook.realtime-status",
4
+ package: "@seedgrid/fe-components",
5
+ exportName: "useSgRealtimeStatus",
6
+ slug: "use-sg-realtime-status",
7
+ displayName: "useSgRealtimeStatus",
8
+ category: "hook",
9
+ subcategory: "realtime",
10
+ description: "Hook com o estado da conexao do SgRealtimeProvider com o Pulse. connected falso indica que a tela deve usar o polling de reserva.",
11
+ tags: ["hook", "realtime", "pulse", "status", "polling"],
12
+ capabilities: ["connection-status", "fallback-signal"],
13
+ fieldSemantics: ["connectionStatus", "connected"],
14
+ props: [
15
+ { name: "returns.status", type: "SgRealtimeStatus", description: "idle, disabled, connecting, connected, reconnecting, unavailable ou auth-error.", semanticRole: "data", bindable: false },
16
+ { name: "returns.connected", type: "boolean", description: "true so com a conexao aberta e assinada.", semanticRole: "data", bindable: false }
17
+ ],
18
+ states: ["disabled", "connected", "unavailable"],
19
+ examples: [
20
+ { id: "uso-basico", title: "Uso basico", file: "apps/showcase/src/app/components/hooks/use-sg-realtime-status/samples/uso-basico.tsx.sample", kind: "sample" }
21
+ ],
22
+ showcase: { route: "/components/hooks/use-sg-realtime-status", hasPlayground: true, hasPropsTable: true }
23
+ };
24
+ export const aiHints = {
25
+ version: "0.1",
26
+ preferredUseCases: ["Decidir entre aviso em tempo real e polling de reserva numa tela."],
27
+ avoidUseCases: ["Decidir permissao ou autenticacao: o status so diz da conexao com o Pulse."],
28
+ synonyms: ["realtime status", "pulse status", "connection state"],
29
+ relatedEntityFields: ["status"],
30
+ compositionHints: ["Usar dentro de um SgRealtimeProvider; fora dele o status e disabled."],
31
+ rankingSignals: { freeText: 0, structuredChoice: 0, date: 0, number: 0, denseLayout: 0 }
32
+ };