@1urso/generic-editor 0.1.0

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 ADDED
@@ -0,0 +1,216 @@
1
+ # Generic Editor
2
+
3
+ Uma biblioteca React poderosa e agnóstica de framework para criação de layouts dinâmicos, geração de templates e edição visual. Projetada para ser integrada em qualquer aplicação React (Web, Electron, Tauri, Next.js, etc.).
4
+
5
+ ## Características
6
+
7
+ - **Editor Visual Drag & Drop**: Posicionamento livre, redimensionamento e rotação de elementos.
8
+ - **Data Binding**: Suporte a variáveis dinâmicas (ex: `{{produto.nome}}`) para geração de templates.
9
+ - **Framework Agnostic**: Funciona em qualquer ambiente React.
10
+ - **JSON Based**: Entrada e saída puramente em JSON, facilitando persistência e integração com backends.
11
+ - **Socket Ready**: Projetado para suportar atualizações em tempo real via WebSockets.
12
+ - **Preview em Tempo Real**: Visualização instantânea de como o layout ficará renderizado.
13
+
14
+ ## Instalação
15
+
16
+ ```bash
17
+ npm install generic-editor
18
+ # ou
19
+ yarn add generic-editor
20
+ ```
21
+
22
+ ### Peer Dependencies
23
+
24
+ Certifique-se de ter as seguintes dependências instaladas em seu projeto, pois o editor depende delas para UI e funcionalidades:
25
+
26
+ ```bash
27
+ npm install @radix-ui/themes @radix-ui/react-icons react-resizable-panels re-resizable framer-motion @dnd-kit/core
28
+ ```
29
+
30
+ E não esqueça de importar os estilos do Radix UI no topo da sua aplicação:
31
+
32
+ ```tsx
33
+ import '@radix-ui/themes/styles.css';
34
+ ```
35
+
36
+ ## Como Usar
37
+
38
+ ### Exemplo Básico
39
+
40
+ ```tsx
41
+ import React, { useState } from 'react';
42
+ import { EditorContent } from 'generic-editor';
43
+
44
+ const MyPage = () => {
45
+ // Configuração das variáveis disponíveis para o usuário arrastar/usar
46
+ const layoutConfig = {
47
+ props: [
48
+ { name: 'Nome do Produto', dataName: 'productName' },
49
+ { name: 'Preço', dataName: 'price' },
50
+ { name: 'Imagem URL', dataName: 'imageUrl' }
51
+ ]
52
+ };
53
+
54
+ const handleSave = (jsonState: string) => {
55
+ console.log("Layout salvo:", jsonState);
56
+ // Envie para sua API, salve em arquivo, etc.
57
+ };
58
+
59
+ return (
60
+ <div style={{ height: '100vh' }}>
61
+ <EditorContent
62
+ layout={layoutConfig}
63
+ onSave={handleSave}
64
+ />
65
+ </div>
66
+ );
67
+ };
68
+ ```
69
+
70
+ ## API Reference
71
+
72
+ ### `<EditorContent />`
73
+
74
+ Componente principal do editor.
75
+
76
+ | Prop | Tipo | Obrigatório | Descrição |
77
+ |------|------|-------------|-----------|
78
+ | `layout` | `ILayout` | Sim | Configuração das variáveis disponíveis para data-binding. |
79
+ | `initialState` | `any` (JSON string ou Objeto) | Não | Estado inicial do editor. Útil para carregar layouts salvos ou atualizações via socket. |
80
+ | `onSave` | `(json: string) => void` | Não | Callback disparado quando o usuário clica em "Salvar". Retorna o estado completo em JSON. |
81
+
82
+ ### Interfaces
83
+
84
+ #### `ILayout`
85
+
86
+ Define as propriedades disponíveis para o usuário utilizar no layout (Data Binding).
87
+
88
+ ```typescript
89
+ interface ILayout {
90
+ props: IProp[];
91
+ }
92
+
93
+ interface IProp {
94
+ name: string; // Nome visível para o usuário (ex: "Nome do Cliente")
95
+ dataName: string; // Chave da variável no JSON de dados (ex: "customerName") -> gera {{customerName}}
96
+ }
97
+ ```
98
+
99
+ ## Estrutura de Dados (JSON)
100
+
101
+ O editor exporta e importa um JSON com a seguinte estrutura:
102
+
103
+ ```json
104
+ {
105
+ "elements": [
106
+ {
107
+ "id": "uuid-1234",
108
+ "type": "text", // ou 'image', 'box'
109
+ "content": "Olá {{name}}",
110
+ "x": 50,
111
+ "y": 100,
112
+ "width": 200,
113
+ "height": 50,
114
+ "rotation": 0,
115
+ "style": { "color": "#000000" }
116
+ }
117
+ ],
118
+ "listSettings": { ... },
119
+ "mockData": [ ... ],
120
+ "isList": false
121
+ }
122
+ ```
123
+
124
+ ## Guias de Integração
125
+
126
+ ### 1. Integração com WebSockets (Real-time)
127
+
128
+ O editor reage a mudanças na prop `initialState`. Isso permite que você conecte o editor a um WebSocket e atualize o layout em tempo real quando outro usuário fizer alterações (colaboração básica) ou carregar dados remotamente.
129
+
130
+ ```tsx
131
+ import React, { useEffect, useState } from 'react';
132
+ import { EditorContent } from 'generic-editor';
133
+ import { io } from 'socket.io-client';
134
+
135
+ const socket = io('http://localhost:3000');
136
+
137
+ const SocketEditor = () => {
138
+ const [remoteState, setRemoteState] = useState(null);
139
+
140
+ useEffect(() => {
141
+ // Escuta atualizações do servidor
142
+ socket.on('layout-update', (data) => {
143
+ setRemoteState(data);
144
+ });
145
+
146
+ return () => socket.off('layout-update');
147
+ }, []);
148
+
149
+ const handleSave = (jsonState) => {
150
+ // Envia alterações para o servidor
151
+ socket.emit('save-layout', jsonState);
152
+ };
153
+
154
+ return (
155
+ <EditorContent
156
+ layout={{ props: [] }}
157
+ initialState={remoteState} // O editor atualizará quando remoteState mudar
158
+ onSave={handleSave}
159
+ />
160
+ );
161
+ };
162
+ ```
163
+
164
+ > **Nota**: O editor faz um "merge" inteligente ao receber `initialState`, mas para colaboração em tempo real (estilo Google Docs), recomenda-se gerenciar conflitos no backend ou usar bibliotecas como Yjs, passando apenas o estado final consolidado para o `initialState`.
165
+
166
+ ### 2. Integração com Electron / Tauri (File System)
167
+
168
+ Para aplicativos desktop, você pode usar o `onSave` para escrever diretamente no disco.
169
+
170
+ ```tsx
171
+ // Exemplo Tauri / Electron
172
+ import { writeFile, readTextFile } from '@tauri-apps/api/fs'; // ou 'fs' do Node no Electron
173
+
174
+ const DesktopEditor = () => {
175
+ const [fileContent, setFileContent] = useState(null);
176
+
177
+ useEffect(() => {
178
+ // Carregar arquivo ao abrir
179
+ readTextFile('path/to/layout.json').then(content => {
180
+ setFileContent(content);
181
+ });
182
+ }, []);
183
+
184
+ const handleSave = async (jsonState) => {
185
+ await writeFile({
186
+ path: 'path/to/layout.json',
187
+ contents: jsonState
188
+ });
189
+ alert('Salvo com sucesso no disco!');
190
+ };
191
+
192
+ return (
193
+ <EditorContent
194
+ layout={{ props: [] }}
195
+ initialState={fileContent}
196
+ onSave={handleSave}
197
+ />
198
+ );
199
+ };
200
+ ```
201
+
202
+ ### 3. Integração com Backend (REST API)
203
+
204
+ ```tsx
205
+ const CloudEditor = () => {
206
+ const handleSave = async (jsonState) => {
207
+ await fetch('/api/layouts/123', {
208
+ method: 'PUT',
209
+ headers: { 'Content-Type': 'application/json' },
210
+ body: jsonState // Envia o JSON completo
211
+ });
212
+ };
213
+
214
+ return <EditorContent layout={...} onSave={handleSave} />;
215
+ };
216
+ ```
package/dist/App.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ declare function App(): import("react/jsx-runtime").JSX.Element;
2
+ export default App;
@@ -0,0 +1,5 @@
1
+ import { default as React } from 'react';
2
+ interface CanvasProps {
3
+ }
4
+ export declare const Canvas: React.FC<CanvasProps>;
5
+ export {};
@@ -0,0 +1,2 @@
1
+ import { default as React } from 'react';
2
+ export declare const EditorSettings: React.FC;
@@ -0,0 +1,6 @@
1
+ import { default as React } from 'react';
2
+ import { IElement } from '../context';
3
+ export declare const ElementContextMenu: React.FC<{
4
+ children: React.ReactNode;
5
+ element: IElement;
6
+ }>;
@@ -0,0 +1,4 @@
1
+ import { default as React } from 'react';
2
+ export declare const ExportDialog: React.FC<{
3
+ onSaveExternal?: (json: string) => void;
4
+ }>;
@@ -0,0 +1,2 @@
1
+ import { default as React } from 'react';
2
+ export declare const Preview: React.FC;
@@ -0,0 +1,49 @@
1
+ import { default as React, ReactNode } from 'react';
2
+ export interface IElement {
3
+ id: string;
4
+ type: 'text' | 'image' | 'box';
5
+ content: string;
6
+ x: number;
7
+ y: number;
8
+ width: number;
9
+ height: number;
10
+ rotation?: number;
11
+ style?: React.CSSProperties;
12
+ dataBinding?: string;
13
+ }
14
+ export interface IListSettings {
15
+ sortProp?: string;
16
+ sortOrder: 'asc' | 'desc';
17
+ }
18
+ export interface IProp {
19
+ name: string;
20
+ dataName: string;
21
+ }
22
+ interface IEditorState {
23
+ elements: IElement[];
24
+ selectedElementId: string | null;
25
+ isList: boolean;
26
+ mockData: any[];
27
+ singleMockData: Record<string, any>;
28
+ listSettings: IListSettings;
29
+ availableProps: IProp[];
30
+ availableFonts: string[];
31
+ }
32
+ interface IEditorContext {
33
+ state: IEditorState;
34
+ addElement: (element: Omit<IElement, 'id' | 'x' | 'y' | 'width' | 'height'> & Partial<Pick<IElement, 'x' | 'y' | 'width' | 'height'>>) => void;
35
+ removeElement: (id: string) => void;
36
+ selectElement: (id: string | null) => void;
37
+ moveElement: (dragIndex: number, hoverIndex: number) => void;
38
+ updateElement: (id: string, updates: Partial<IElement>) => void;
39
+ setMockData: (data: any[], singleData: Record<string, any>) => void;
40
+ updateListSettings: (settings: Partial<IListSettings>) => void;
41
+ loadState: (savedState: Partial<IEditorState>) => void;
42
+ }
43
+ export declare const EditorProvider: React.FC<{
44
+ children: ReactNode;
45
+ isList?: boolean;
46
+ availableProps?: IProp[];
47
+ }>;
48
+ export declare const useEditor: () => IEditorContext;
49
+ export {};
@@ -0,0 +1,9 @@
1
+ import { default as React } from 'react';
2
+ import { ILayout } from './types';
3
+ interface EditorProps {
4
+ layout: ILayout;
5
+ initialState?: any;
6
+ onSave?: (json: string) => void;
7
+ }
8
+ export declare const GenericEditor: React.FC<EditorProps>;
9
+ export {};
@@ -0,0 +1,10 @@
1
+ interface IProp {
2
+ name: string;
3
+ dataName: string;
4
+ }
5
+ export interface ILayout {
6
+ name: string;
7
+ props: IProp[];
8
+ isList?: boolean;
9
+ }
10
+ export {};
@@ -0,0 +1,8 @@
1
+ import { IElement, IListSettings } from '../context';
2
+ interface RenderOptions {
3
+ isList?: boolean;
4
+ listSettings?: IListSettings;
5
+ }
6
+ export declare const generateHTML: (elements: IElement[], data: any, options?: RenderOptions) => string;
7
+ export declare const getRendererCode: () => string;
8
+ export {};