lib-pixelbuild 0.1.3 → 0.2.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 +102 -127
- package/dist/builders/PageBuilder.d.ts +3 -2
- package/dist/builders/PageBuilder.js +41 -33
- package/dist/components/CardComponent.d.ts +3 -3
- package/dist/components/CardComponent.js +65 -6
- package/dist/components/FormComponent.d.ts +3 -3
- package/dist/components/FormComponent.js +8 -14
- package/dist/components/ListComponent.d.ts +3 -2
- package/dist/components/ListComponent.js +20 -15
- package/dist/components/LoadingComponent.js +1 -1
- package/dist/components/MapComponent.js +18 -4
- package/dist/components/SliderComponent.js +69 -33
- package/dist/components/SocialIconsComponent.js +1 -1
- package/dist/components/TextComponent.d.ts +3 -3
- package/dist/components/TextComponent.js +6 -7
- package/dist/elements/AlertElement.js +4 -3
- package/dist/elements/ButtonElement.js +19 -17
- package/dist/elements/IconElement.d.ts +1 -2
- package/dist/elements/IconElement.js +10 -9
- package/dist/elements/IconMap.d.ts +2 -0
- package/dist/elements/IconMap.js +30 -0
- package/dist/elements/InputElement.js +16 -16
- package/dist/factories/ButtonActionFactory.d.ts +6 -1
- package/dist/factories/ButtonActionFactory.js +14 -4
- package/dist/index.d.ts +2 -6
- package/dist/index.js +1 -4
- package/dist/layouts/Header.d.ts +2 -1
- package/dist/layouts/Header.js +33 -5
- package/dist/layouts/SiteLayout.d.ts +2 -1
- package/dist/layouts/SiteLayout.js +2 -2
- package/dist/renderers/SectionRenderer.d.ts +3 -3
- package/dist/renderers/SectionRenderer.js +45 -29
- package/dist/services/EmailService.d.ts +10 -2
- package/dist/services/EmailService.js +25 -25
- package/dist/types/RawWebsiteType.d.ts +8 -184
- package/dist/utils/InputValidators.js +11 -11
- package/dist/utils/propertyUtils.d.ts +3 -0
- package/dist/utils/propertyUtils.js +28 -0
- package/package.json +32 -19
- package/dist/elements/FileElement.d.ts +0 -4
- package/dist/elements/FileElement.js +0 -6
- package/dist/services/ApiService.d.ts +0 -5
- package/dist/services/ApiService.js +0 -17
- package/dist/services/CacheService.d.ts +0 -5
- package/dist/services/CacheService.js +0 -16
- package/dist/services/ConstructorService.d.ts +0 -12
- package/dist/services/ConstructorService.js +0 -33
- package/dist/types/AuthenticateResponseType.d.ts +0 -5
- package/dist/types/AuthenticateResponseType.js +0 -1
- package/dist/types/ButtonPropertiesType.d.ts +0 -11
- package/dist/types/ButtonPropertiesType.js +0 -1
- package/dist/types/FilePropertiesType.d.ts +0 -4
- package/dist/types/FilePropertiesType.js +0 -1
- package/dist/types/ImagePropertiesType.d.ts +0 -4
- package/dist/types/ImagePropertiesType.js +0 -1
- package/dist/types/InputPropertiesType.d.ts +0 -7
- package/dist/types/InputPropertiesType.js +0 -1
- package/dist/types/LinkPropertiesType.d.ts +0 -4
- package/dist/types/LinkPropertiesType.js +0 -1
- package/dist/types/PlanWebsiteType.d.ts +0 -9
- package/dist/types/PlanWebsiteType.js +0 -1
- package/dist/types/UserType.d.ts +0 -10
- package/dist/types/UserType.js +0 -1
package/README.md
CHANGED
|
@@ -1,176 +1,151 @@
|
|
|
1
1
|
# lib-pixelbuild
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
React render engine for schema-driven PixelBuild websites.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Features
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
- Schema-driven rendering — pages, components, elements from JSON
|
|
8
|
+
- Dynamic component/element factories with lazy loading
|
|
9
|
+
- SEO via `react-helmet-async`
|
|
10
|
+
- Analytics via `react-ga4`
|
|
11
|
+
- Dynamic forms with validation (CPF, phone, email, etc.)
|
|
12
|
+
- Accessible image sliders (`keen-slider`)
|
|
13
|
+
- Email service with configurable credentials
|
|
14
|
+
- Zustand-powered dynamic form state management
|
|
8
15
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
## ✨ Visão Geral
|
|
12
|
-
|
|
13
|
-
- **lib-pixelbuild** permite criar sites completos a partir de um modelo de dados padronizado, compondo páginas, componentes e elementos de forma dinâmica.
|
|
14
|
-
- Foco em **reutilização**, **manutenção centralizada** e **distribuição fácil** de novas funcionalidades para múltiplos projetos.
|
|
15
|
-
- Utiliza React, TypeScript e padrões modernos de desenvolvimento.
|
|
16
|
-
|
|
17
|
-
---
|
|
18
|
-
|
|
19
|
-
## 🚀 Instalação
|
|
16
|
+
## Installation
|
|
20
17
|
|
|
21
18
|
```bash
|
|
22
19
|
npm install lib-pixelbuild
|
|
23
20
|
```
|
|
24
21
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
---
|
|
28
|
-
|
|
29
|
-
## 🏗️ Estrutura do Projeto
|
|
22
|
+
**Peer dependencies** — seu projeto precisa ter estas também:
|
|
30
23
|
|
|
31
24
|
```
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
elements/ # Elementos (Text, Image, Input, Button, etc)
|
|
35
|
-
constants/ # Enums e constantes de tipos
|
|
36
|
-
factories/ # Fábricas para instanciar componentes/elementos dinamicamente
|
|
37
|
-
services/ # Serviços utilitários (API, Cache, Email, Analytics, etc)
|
|
38
|
-
stores/ # Gerenciamento de estado
|
|
39
|
-
types/ # Tipos para todo o modelo de dados
|
|
40
|
-
utils/ # Funções utilitárias e validadores
|
|
41
|
-
index.ts # Exporta tudo que é público na lib
|
|
25
|
+
react react-dom react-bootstrap bootstrap zustand react-router-dom
|
|
26
|
+
react-icons react-ga4 react-helmet-async react-imask dompurify keen-slider
|
|
42
27
|
```
|
|
43
28
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
## 🧩 Como Funciona
|
|
47
|
-
|
|
48
|
-
- **Estrutura de Dados**: O site é definido por um objeto JSON (ver `RawWebsiteType`), contendo páginas, componentes e elementos.
|
|
49
|
-
- **Factories**: Fábricas constroem dinamicamente componentes e elementos a partir do JSON.
|
|
50
|
-
- **Componentes**: Renderizam partes do site (textos, listas, formulários, carrosséis, etc).
|
|
51
|
-
- **Elementos**: Unidades atômicas (input, botão, imagem, texto, etc).
|
|
52
|
-
- **Serviços**: Abstraem chamadas de API, cache local, envio de e-mail, validação, etc.
|
|
53
|
-
- **Gerenciamento de Estado**: Utiliza Zustand para formulários dinâmicos.
|
|
54
|
-
|
|
55
|
-
---
|
|
56
|
-
|
|
57
|
-
## 🛠️ Uso Básico
|
|
58
|
-
|
|
59
|
-
1. **Importe o que precisa:**
|
|
60
|
-
```ts
|
|
61
|
-
import { MainLayout, PageRenderer, ConstructorService } from 'lib-pixelbuild'
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
2. **Busque a estrutura do site:**
|
|
65
|
-
```ts
|
|
66
|
-
const constructorService = new ConstructorService()
|
|
67
|
-
const websiteData = await constructorService.fetchWebsiteFromApi(websiteId, apiUrl)
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
3. **Renderize:**
|
|
71
|
-
```tsx
|
|
72
|
-
<MainLayout website={websiteData}>
|
|
73
|
-
<PageRenderer ga4="GA_TRACKING_ID" title="Título" components={websiteData.pages[0].components} />
|
|
74
|
-
</MainLayout>
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
---
|
|
78
|
-
|
|
79
|
-
## 🧪 Teste Local
|
|
29
|
+
## Usage
|
|
80
30
|
|
|
81
|
-
|
|
31
|
+
```tsx
|
|
32
|
+
import { SiteLayout, PageBuilder } from 'lib-pixelbuild'
|
|
33
|
+
import type { WebsiteType, PageType } from 'lib-pixelbuild'
|
|
82
34
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
35
|
+
function App({ website }: { website: WebsiteType }) {
|
|
36
|
+
const page = website.pages[0]
|
|
37
|
+
return (
|
|
38
|
+
<SiteLayout website={website} imageBaseUrl="/images">
|
|
39
|
+
<PageBuilder website={website} page={page} editionMode={false} />
|
|
40
|
+
</SiteLayout>
|
|
41
|
+
)
|
|
42
|
+
}
|
|
91
43
|
```
|
|
92
44
|
|
|
93
|
-
|
|
94
|
-
- `npm run link:consumer-peers` força a lib linkada a usar as **mesmas cópias das peer dependencies** do projeto consumidor (ex.: `react`, `react-dom`, `react-router-dom`), evitando conflitos de contexto e `Invalid hook call`.
|
|
95
|
-
- `npm run build:watch` recompila automaticamente sempre que você alterar algo em `src/`.
|
|
96
|
-
- Antes de rodar o link, garanta que o consumidor já executou `npm install`.
|
|
45
|
+
## Development — hot-reload
|
|
97
46
|
|
|
98
|
-
|
|
47
|
+
Os consumidores (`site-avalloneadvogados`, `app-pixelbuild`) têm `vite.config.js` configurado com alias para o **código-fonte TypeScript** da lib em modo dev:
|
|
99
48
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
```bash
|
|
103
|
-
npm install
|
|
104
|
-
npm link lib-pixelbuild
|
|
105
|
-
npm start
|
|
49
|
+
```js
|
|
50
|
+
'lib-pixelbuild': path.resolve(__dirname, '../lib-pixelbuild/src/index.ts')
|
|
106
51
|
```
|
|
107
52
|
|
|
108
|
-
|
|
53
|
+
Isso significa que:
|
|
109
54
|
|
|
110
|
-
|
|
55
|
+
1. **A lib NÃO precisa ser buildada** durante o desenvolvimento
|
|
56
|
+
2. Alterações no `src/` da lib refletem **instantaneamente** nos consumidores (hot-reload do Vite)
|
|
57
|
+
3. As peer dependencies (`react`, `react-bootstrap`, etc.) são resolvidas do `node_modules` do consumidor via aliases
|
|
111
58
|
|
|
112
|
-
|
|
59
|
+
**Pré-requisito:** o consumidor precisa ter todas as peer dependencies instaladas.
|
|
113
60
|
|
|
114
|
-
###
|
|
115
|
-
|
|
116
|
-
Na lib:
|
|
61
|
+
### Fluxo de desenvolvimento
|
|
117
62
|
|
|
118
63
|
```bash
|
|
119
|
-
|
|
120
|
-
|
|
64
|
+
# Terminal 1: lib (não precisa rodar nada, só editar os arquivos)
|
|
65
|
+
cd lib-pixelbuild
|
|
121
66
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
npm unlink lib-pixelbuild
|
|
126
|
-
npm install
|
|
67
|
+
# Terminal 2: consumidor
|
|
68
|
+
cd site-avalloneadvogados
|
|
69
|
+
npm run dev # hot-reload reflete alterações na lib automaticamente
|
|
127
70
|
```
|
|
128
71
|
|
|
129
|
-
|
|
72
|
+
### Build para produção
|
|
130
73
|
|
|
131
|
-
|
|
74
|
+
```bash
|
|
75
|
+
cd lib-pixelbuild
|
|
76
|
+
npm run build # compila dist/
|
|
132
77
|
|
|
133
|
-
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
- `npm run lint:fix` — Corrige problemas de lint automaticamente.
|
|
137
|
-
- `npm run format` — Formata o código com Prettier.
|
|
138
|
-
- `npm run link:global` — Atalho para `npm link`.
|
|
139
|
-
- `npm run unlink:global` — Remove o link global local da lib.
|
|
140
|
-
- `npm run link:consumer-peers -- /caminho/consumidor` — Linka as `peerDependencies` do consumidor na lib (incluindo `react`, `react-dom` e `react-router-dom`).
|
|
78
|
+
cd site-avalloneadvogados
|
|
79
|
+
npm run build # usa o dist/ da lib (produção)
|
|
80
|
+
```
|
|
141
81
|
|
|
142
|
-
|
|
82
|
+
Em produção, os `vite.config.js` não usam o alias de source — resolvem pelo `package.json` da lib (`file:` → `dist/index.js`).
|
|
143
83
|
|
|
144
|
-
##
|
|
84
|
+
## Publicação
|
|
145
85
|
|
|
146
86
|
```bash
|
|
147
|
-
|
|
87
|
+
# 1. Build + testes
|
|
88
|
+
npm run build && npm test && npm run lint
|
|
148
89
|
|
|
90
|
+
# 2. Publicar
|
|
149
91
|
npm publish --access public
|
|
150
92
|
```
|
|
151
93
|
|
|
152
|
-
|
|
94
|
+
O comando `prepublishOnly` já roda build, testes e lint automaticamente antes de publicar.
|
|
95
|
+
|
|
96
|
+
## Scripts
|
|
97
|
+
|
|
98
|
+
| Script | Description |
|
|
99
|
+
|--------|-------------|
|
|
100
|
+
| `npm run build` | Compile to `dist/` + ESM fix |
|
|
101
|
+
| `npm run build:watch` | Watch mode |
|
|
102
|
+
| `npm run test` | Run vitest |
|
|
103
|
+
| `npm run typecheck` | `tsc --noEmit` |
|
|
104
|
+
| `npm run lint` | ESLint |
|
|
105
|
+
| `npm run format` | Prettier |
|
|
106
|
+
| `npm run prepare` | Husky + lint-staged (auto-run on install) |
|
|
107
|
+
| `npm run lint:fix` | ESLint auto-fix |
|
|
108
|
+
| `npm run link:global` | `npm link` para consumo local |
|
|
109
|
+
| `npm run unlink:global` | Remove o link global |
|
|
153
110
|
|
|
154
|
-
##
|
|
111
|
+
## Project Structure
|
|
155
112
|
|
|
156
|
-
|
|
113
|
+
```
|
|
114
|
+
src/
|
|
115
|
+
builders/ PageBuilder — assembles pages from schema
|
|
116
|
+
components/ Card, Form, List, Map, Slider, Text, etc.
|
|
117
|
+
constants/ Type enums
|
|
118
|
+
elements/ Atomic elements — Button, Icon, Image, Input, Link, Text
|
|
119
|
+
factories/ ComponentFactory, ElementFactory, ButtonActionFactory
|
|
120
|
+
layouts/ SiteLayout, Header, Footer, Menu
|
|
121
|
+
services/ EmailService (configurable)
|
|
122
|
+
stores/ Zustand store for forms
|
|
123
|
+
types/ RawWebsiteType and related types
|
|
124
|
+
utils/ propertyUtils (type-safe accessors), InputValidators
|
|
125
|
+
```
|
|
157
126
|
|
|
158
|
-
|
|
127
|
+
## Public API
|
|
159
128
|
|
|
160
|
-
|
|
129
|
+
```ts
|
|
130
|
+
// Components
|
|
131
|
+
Header Footer SiteLayout
|
|
132
|
+
PageBuilder SectionRenderer LoadingComponent
|
|
161
133
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
- dompurify
|
|
166
|
-
- react-icons
|
|
167
|
-
- react-input-mask
|
|
168
|
-
- react-helmet-async
|
|
134
|
+
// Factories
|
|
135
|
+
ComponentFactory ElementFactory
|
|
136
|
+
buttonActionFactory → configureEmail(config)
|
|
169
137
|
|
|
170
|
-
|
|
138
|
+
// Constants
|
|
139
|
+
ComponentTypeEnum ElementTypeEnum
|
|
140
|
+
analyticsService
|
|
171
141
|
|
|
172
|
-
|
|
142
|
+
// Types
|
|
143
|
+
RawWebsiteType WebsiteType PageType SectionType
|
|
144
|
+
SectionColumnType ColumnWidthType ComponentType
|
|
145
|
+
ElementType PropertiesType StylesType
|
|
146
|
+
EmailServiceConfig
|
|
147
|
+
```
|
|
173
148
|
|
|
174
|
-
|
|
149
|
+
## Note
|
|
175
150
|
|
|
176
|
-
|
|
151
|
+
This library is under active development. Breaking changes expected until v1.0.
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { WebsiteType } from '../types/WebsiteType.js';
|
|
2
2
|
import type { PageType } from '../types/PageType.js';
|
|
3
|
-
|
|
3
|
+
declare const PageBuilder: import("react").NamedExoticComponent<{
|
|
4
4
|
readonly website: WebsiteType;
|
|
5
5
|
readonly page: PageType;
|
|
6
6
|
readonly editionMode: boolean;
|
|
7
|
-
}
|
|
7
|
+
}>;
|
|
8
|
+
export { PageBuilder };
|
|
@@ -1,40 +1,48 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Suspense } from 'react';
|
|
2
|
+
import { Suspense, memo, useMemo } from 'react';
|
|
3
3
|
import { Helmet } from 'react-helmet-async';
|
|
4
|
-
import { Menu } from '../layouts/Menu.js';
|
|
5
4
|
import { SectionRenderer } from '../renderers/SectionRenderer.js';
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
for (const key of Object.keys(pStyles)) {
|
|
11
|
-
const val = pStyles[key];
|
|
5
|
+
function mergeStyles(websiteStyles, pageStyles) {
|
|
6
|
+
const merged = Object.assign({}, websiteStyles);
|
|
7
|
+
for (const key of Object.keys(pageStyles)) {
|
|
8
|
+
const val = pageStyles[key];
|
|
12
9
|
if (val !== '' && val !== null && val !== undefined) {
|
|
13
|
-
|
|
10
|
+
merged[key] = val;
|
|
14
11
|
}
|
|
15
12
|
}
|
|
16
|
-
return
|
|
17
|
-
backgroundColor: mergedStyles.backgroundColor,
|
|
18
|
-
color: mergedStyles.color,
|
|
19
|
-
backgroundImage: mergedStyles.backgroundGradientColorStart
|
|
20
|
-
? `linear-gradient(${mergedStyles.backgroundGradientColorStart}, ${mergedStyles.backgroundGradientColorEnd}), url(${mergedStyles.backgroundImage})`
|
|
21
|
-
: mergedStyles.backgroundImage
|
|
22
|
-
? `url(${mergedStyles.backgroundImage})`
|
|
23
|
-
: undefined,
|
|
24
|
-
backgroundSize: mergedStyles.backgroundSize,
|
|
25
|
-
backgroundPosition: mergedStyles.backgroundPosition,
|
|
26
|
-
width: mergedStyles.width,
|
|
27
|
-
height: mergedStyles.height,
|
|
28
|
-
fontSize: mergedStyles.fontSize,
|
|
29
|
-
fontWeight: mergedStyles.fontWeight,
|
|
30
|
-
textAlign: mergedStyles.textAlign,
|
|
31
|
-
paddingTop: mergedStyles.paddingTop,
|
|
32
|
-
paddingBottom: mergedStyles.paddingBottom,
|
|
33
|
-
paddingLeft: mergedStyles.paddingLeft,
|
|
34
|
-
paddingRight: mergedStyles.paddingRight,
|
|
35
|
-
marginTop: mergedStyles.marginTop,
|
|
36
|
-
marginBottom: mergedStyles.marginBottom,
|
|
37
|
-
borderRadius: mergedStyles.borderRadius,
|
|
38
|
-
minHeight: page.sections.length === 0 ? '200px' : undefined,
|
|
39
|
-
}, children: [_jsx(SectionRenderer, { sections: page.sections, editionMode: editionMode }), page.sections.length === 0 && editionMode && (_jsx("div", { style: { padding: '40px', textAlign: 'center', color: '#94a3b8' }, children: "Nenhuma se\u00E7\u00E3o configurada. Use o editor de layout para adicionar conte\u00FAdo." }))] })] }));
|
|
13
|
+
return merged;
|
|
40
14
|
}
|
|
15
|
+
function toCssValue(val) {
|
|
16
|
+
if (typeof val === 'string' && val.length > 0)
|
|
17
|
+
return val;
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
const PageBuilder = memo(function PageBuilder({ website, page, editionMode }) {
|
|
21
|
+
const mergedStyles = useMemo(() => mergeStyles(website.styles, page.styles), [website.styles, page.styles]);
|
|
22
|
+
const pageStyle = useMemo(() => ({
|
|
23
|
+
backgroundColor: toCssValue(mergedStyles.backgroundColor),
|
|
24
|
+
color: toCssValue(mergedStyles.color),
|
|
25
|
+
backgroundImage: mergedStyles.backgroundGradientColorStart
|
|
26
|
+
? `linear-gradient(${mergedStyles.backgroundGradientColorStart}, ${mergedStyles.backgroundGradientColorEnd}), url(${mergedStyles.backgroundImage})`
|
|
27
|
+
: mergedStyles.backgroundImage
|
|
28
|
+
? `url(${mergedStyles.backgroundImage})`
|
|
29
|
+
: undefined,
|
|
30
|
+
backgroundSize: toCssValue(mergedStyles.backgroundSize),
|
|
31
|
+
backgroundPosition: toCssValue(mergedStyles.backgroundPosition),
|
|
32
|
+
width: toCssValue(mergedStyles.width),
|
|
33
|
+
height: toCssValue(mergedStyles.height),
|
|
34
|
+
fontSize: toCssValue(mergedStyles.fontSize),
|
|
35
|
+
fontWeight: toCssValue(mergedStyles.fontWeight),
|
|
36
|
+
textAlign: mergedStyles.textAlign,
|
|
37
|
+
paddingTop: toCssValue(mergedStyles.paddingTop),
|
|
38
|
+
paddingBottom: toCssValue(mergedStyles.paddingBottom),
|
|
39
|
+
paddingLeft: toCssValue(mergedStyles.paddingLeft),
|
|
40
|
+
paddingRight: toCssValue(mergedStyles.paddingRight),
|
|
41
|
+
marginTop: toCssValue(mergedStyles.marginTop),
|
|
42
|
+
marginBottom: toCssValue(mergedStyles.marginBottom),
|
|
43
|
+
borderRadius: toCssValue(mergedStyles.borderRadius),
|
|
44
|
+
minHeight: page.sections.length === 0 ? '200px' : undefined,
|
|
45
|
+
}), [mergedStyles, page.sections.length]);
|
|
46
|
+
return (_jsxs(Suspense, { fallback: _jsx("div", { children: website.properties.loadingMessage }), children: [!editionMode && (_jsxs(Helmet, { children: [_jsx("title", { children: page.title }), _jsx("meta", { name: "description", content: page.title }), _jsx("meta", { property: "og:title", content: page.title }), _jsx("meta", { property: "og:description", content: page.title }), _jsx("meta", { property: "og:image", content: page.title })] })), _jsxs("div", { style: pageStyle, children: [_jsx(SectionRenderer, { sections: page.sections, editionMode: editionMode }), page.sections.length === 0 && editionMode && (_jsx("div", { style: { padding: '40px', textAlign: 'center', color: '#94a3b8' }, children: "No sections configured. Use the layout editor to add content." }))] })] }));
|
|
47
|
+
});
|
|
48
|
+
export { PageBuilder };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
1
|
import type { ComponentType } from '../types/ComponentType.js';
|
|
3
|
-
|
|
2
|
+
declare const CardComponent: import("react").NamedExoticComponent<{
|
|
4
3
|
readonly component: ComponentType;
|
|
5
|
-
}
|
|
4
|
+
}>;
|
|
5
|
+
export { CardComponent };
|
|
@@ -1,12 +1,71 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import
|
|
2
|
+
import { memo, useMemo } from 'react';
|
|
3
3
|
import { Row, Container } from 'react-bootstrap';
|
|
4
4
|
import { ElementFactory } from '../factories/ElementFactory.js';
|
|
5
5
|
import { orderBySort } from '../utils/orderBySort.js';
|
|
6
6
|
const elementFactory = new ElementFactory();
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
function applyCardStyles(element, props) {
|
|
8
|
+
const overrides = {};
|
|
9
|
+
if (element.elementType === 'icon') {
|
|
10
|
+
if (props.cardIconColor)
|
|
11
|
+
overrides.color = props.cardIconColor;
|
|
12
|
+
if (props.cardIconSize)
|
|
13
|
+
overrides.fontSize = props.cardIconSize;
|
|
14
|
+
}
|
|
15
|
+
else if (element.elementType === 'title') {
|
|
16
|
+
if (props.cardTitleColor)
|
|
17
|
+
overrides.color = props.cardTitleColor;
|
|
18
|
+
if (props.cardTitleSize)
|
|
19
|
+
overrides.fontSize = props.cardTitleSize;
|
|
20
|
+
}
|
|
21
|
+
else if (element.elementType === 'text') {
|
|
22
|
+
if (props.cardTextColor)
|
|
23
|
+
overrides.color = props.cardTextColor;
|
|
24
|
+
if (props.cardTextSize)
|
|
25
|
+
overrides.fontSize = props.cardTextSize;
|
|
26
|
+
}
|
|
27
|
+
return Object.assign(Object.assign({}, element), { styles: Object.assign(Object.assign({}, element.styles), overrides) });
|
|
12
28
|
}
|
|
29
|
+
const CardComponent = memo(function CardComponent({ component }) {
|
|
30
|
+
const { properties } = component;
|
|
31
|
+
const cardProps = useMemo(() => ({
|
|
32
|
+
cardElement1: properties === null || properties === void 0 ? void 0 : properties.cardElement1,
|
|
33
|
+
cardElement2: properties === null || properties === void 0 ? void 0 : properties.cardElement2,
|
|
34
|
+
cardElement3: properties === null || properties === void 0 ? void 0 : properties.cardElement3,
|
|
35
|
+
cardElement4: properties === null || properties === void 0 ? void 0 : properties.cardElement4,
|
|
36
|
+
cardIconColor: properties === null || properties === void 0 ? void 0 : properties.cardIconColor,
|
|
37
|
+
cardIconSize: properties === null || properties === void 0 ? void 0 : properties.cardIconSize,
|
|
38
|
+
cardTitleColor: properties === null || properties === void 0 ? void 0 : properties.cardTitleColor,
|
|
39
|
+
cardTitleSize: properties === null || properties === void 0 ? void 0 : properties.cardTitleSize,
|
|
40
|
+
cardTextColor: properties === null || properties === void 0 ? void 0 : properties.cardTextColor,
|
|
41
|
+
cardTextSize: properties === null || properties === void 0 ? void 0 : properties.cardTextSize,
|
|
42
|
+
}), [
|
|
43
|
+
properties === null || properties === void 0 ? void 0 : properties.cardElement1,
|
|
44
|
+
properties === null || properties === void 0 ? void 0 : properties.cardElement2,
|
|
45
|
+
properties === null || properties === void 0 ? void 0 : properties.cardElement3,
|
|
46
|
+
properties === null || properties === void 0 ? void 0 : properties.cardElement4,
|
|
47
|
+
properties === null || properties === void 0 ? void 0 : properties.cardIconColor,
|
|
48
|
+
properties === null || properties === void 0 ? void 0 : properties.cardIconSize,
|
|
49
|
+
properties === null || properties === void 0 ? void 0 : properties.cardTitleColor,
|
|
50
|
+
properties === null || properties === void 0 ? void 0 : properties.cardTitleSize,
|
|
51
|
+
properties === null || properties === void 0 ? void 0 : properties.cardTextColor,
|
|
52
|
+
properties === null || properties === void 0 ? void 0 : properties.cardTextSize,
|
|
53
|
+
]);
|
|
54
|
+
const orderedElements = useMemo(() => {
|
|
55
|
+
const cardOrder = [
|
|
56
|
+
cardProps.cardElement1,
|
|
57
|
+
cardProps.cardElement2,
|
|
58
|
+
cardProps.cardElement3,
|
|
59
|
+
cardProps.cardElement4,
|
|
60
|
+
].filter((v) => typeof v === 'string' && v.length > 0);
|
|
61
|
+
if (cardOrder.length > 0) {
|
|
62
|
+
return cardOrder
|
|
63
|
+
.map((type) => component.elements.find((el) => el.elementType === type))
|
|
64
|
+
.filter((el) => el !== undefined);
|
|
65
|
+
}
|
|
66
|
+
return orderBySort(component.elements);
|
|
67
|
+
}, [cardProps.cardElement1, cardProps.cardElement2, cardProps.cardElement3, cardProps.cardElement4, component.elements]);
|
|
68
|
+
const elements = useMemo(() => orderedElements.map((el) => applyCardStyles(el, cardProps)), [orderedElements, cardProps]);
|
|
69
|
+
return (_jsx(Container, { style: component.styles, children: _jsx(Row, { id: "element-row", children: elements.map((element) => elementFactory.build(element)) }) }));
|
|
70
|
+
});
|
|
71
|
+
export { CardComponent };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
1
|
import type { ComponentType } from '../types/ComponentType.js';
|
|
3
|
-
|
|
2
|
+
declare const FormComponent: import("react").NamedExoticComponent<{
|
|
4
3
|
readonly component: ComponentType;
|
|
5
|
-
}
|
|
4
|
+
}>;
|
|
5
|
+
export { FormComponent };
|
|
@@ -1,25 +1,19 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import
|
|
2
|
+
import { memo, useEffect, useMemo } from 'react';
|
|
3
3
|
import { Form, Row } from 'react-bootstrap';
|
|
4
4
|
import { UseFormStore } from '../stores/UseFormStore.js';
|
|
5
5
|
import { ElementFactory } from '../factories/ElementFactory.js';
|
|
6
6
|
import { orderBySort } from '../utils/orderBySort.js';
|
|
7
7
|
const elementFactory = new ElementFactory();
|
|
8
|
-
|
|
8
|
+
const FormComponent = memo(function FormComponent({ component }) {
|
|
9
9
|
const registerForm = UseFormStore((state) => state.registerForm);
|
|
10
10
|
useEffect(() => {
|
|
11
11
|
registerForm(component.id);
|
|
12
12
|
}, [registerForm, component.id]);
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
catch (error) {
|
|
18
|
-
console.error('Erro ao enviar o formulário:', error);
|
|
19
|
-
}
|
|
13
|
+
const elements = useMemo(() => orderBySort(component.elements), [component.elements]);
|
|
14
|
+
const handleSubmit = (event) => {
|
|
15
|
+
event.preventDefault();
|
|
20
16
|
};
|
|
21
|
-
return (_jsx(Form, { onSubmit: handleSubmit, children: _jsx(Row, { children:
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}) }) }));
|
|
25
|
-
}
|
|
17
|
+
return (_jsx(Form, { onSubmit: handleSubmit, children: _jsx(Row, { children: elements.map((element) => elementFactory.build(element, component.id)) }) }));
|
|
18
|
+
});
|
|
19
|
+
export { FormComponent };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ComponentType } from '../types/ComponentType.js';
|
|
2
|
-
|
|
2
|
+
declare const ListComponent: import("react").NamedExoticComponent<{
|
|
3
3
|
readonly component: ComponentType;
|
|
4
|
-
}
|
|
4
|
+
}>;
|
|
5
|
+
export { ListComponent };
|
|
@@ -1,24 +1,29 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { memo, useMemo } from 'react';
|
|
2
3
|
import { Col } from 'react-bootstrap';
|
|
3
4
|
import { ElementFactory } from '../factories/ElementFactory.js';
|
|
4
5
|
import { ElementTypeEnum } from '../constants/ElementEnum.js';
|
|
5
6
|
import { orderBySort } from '../utils/orderBySort.js';
|
|
6
7
|
const elementFactory = new ElementFactory();
|
|
7
|
-
|
|
8
|
-
const elements = orderBySort(component.elements);
|
|
9
|
-
const items =
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
8
|
+
const ListComponent = memo(function ListComponent({ component }) {
|
|
9
|
+
const elements = useMemo(() => orderBySort(component.elements), [component.elements]);
|
|
10
|
+
const items = useMemo(() => {
|
|
11
|
+
const result = [];
|
|
12
|
+
for (let i = 0; i < elements.length; i++) {
|
|
13
|
+
const current = elements[i];
|
|
14
|
+
if (current.elementType === ElementTypeEnum.Icon) {
|
|
15
|
+
const next = elements[i + 1];
|
|
16
|
+
if ((next === null || next === void 0 ? void 0 : next.elementType) === ElementTypeEnum.Text || (next === null || next === void 0 ? void 0 : next.elementType) === ElementTypeEnum.Title) {
|
|
17
|
+
result.push({ icon: current, text: next, id: current.id });
|
|
18
|
+
i++;
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
result.push({ icon: current, text: null, id: current.id });
|
|
22
|
+
}
|
|
20
23
|
}
|
|
21
24
|
}
|
|
22
|
-
|
|
25
|
+
return result;
|
|
26
|
+
}, [elements]);
|
|
23
27
|
return (_jsx(Col, { style: component.styles, children: _jsx("ul", { style: { listStyle: 'none' }, children: items.map(({ icon, text, id }) => (_jsxs("li", { style: { display: 'flex', alignItems: 'center', justifyContent: 'center', marginBottom: '8px' }, children: [_jsx("span", { children: elementFactory.build(icon) }), _jsx("span", { style: { fontSize: '22px' }, children: text ? elementFactory.build(text) : null })] }, id))) }) }));
|
|
24
|
-
}
|
|
28
|
+
});
|
|
29
|
+
export { ListComponent };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Container, Spinner } from 'react-bootstrap';
|
|
3
3
|
export function LoadingComponent() {
|
|
4
|
-
return (_jsxs(Container, { className: "d-flex flex-column align-items-center justify-content-center", style: { minHeight: '100vh' }, children: [_jsx("output", { "aria-busy": "true", children: _jsx(Spinner, { animation: "border" }) }), _jsx("p", { className: "mt-3", children: "
|
|
4
|
+
return (_jsxs(Container, { className: "d-flex flex-column align-items-center justify-content-center", style: { minHeight: '100vh' }, children: [_jsx("output", { "aria-busy": "true", children: _jsx(Spinner, { animation: "border" }) }), _jsx("p", { className: "mt-3", children: "LOADING" })] }));
|
|
5
5
|
}
|
|
@@ -1,10 +1,24 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo } from 'react';
|
|
2
3
|
import { Row, Col } from 'react-bootstrap';
|
|
4
|
+
const COORDS_PATTERN = /^-?\d+(\.\d+)?$/;
|
|
5
|
+
function isValidCoord(value) {
|
|
6
|
+
if (typeof value !== 'string')
|
|
7
|
+
return false;
|
|
8
|
+
return COORDS_PATTERN.test(value.trim()) && Math.abs(Number(value)) <= 180;
|
|
9
|
+
}
|
|
3
10
|
export function MapComponent({ component }) {
|
|
4
11
|
const { lat, lng, zoom = 15, height = '400px' } = component.properties;
|
|
5
|
-
const src =
|
|
6
|
-
|
|
7
|
-
|
|
12
|
+
const src = useMemo(() => {
|
|
13
|
+
if (!lat || !lng)
|
|
14
|
+
return null;
|
|
15
|
+
const latStr = String(lat).trim();
|
|
16
|
+
const lngStr = String(lng).trim();
|
|
17
|
+
if (!isValidCoord(latStr) || !isValidCoord(lngStr)) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
return `https://maps.google.com/maps?q=${encodeURIComponent(`${latStr},${lngStr}`)}&z=${zoom}&output=embed`;
|
|
21
|
+
}, [lat, lng, zoom]);
|
|
8
22
|
return (_jsx(Col, { style: component.styles, children: _jsx(Row, { children: _jsx(Col, { children: src ? (_jsx("iframe", { title: "Localiza\u00E7\u00E3o", src: src, width: "100%", height: height, style: { border: 0, borderRadius: '4px' }, allowFullScreen: true, loading: "lazy", referrerPolicy: "no-referrer-when-downgrade" })) : (_jsx("div", { style: {
|
|
9
23
|
width: '100%',
|
|
10
24
|
height,
|
|
@@ -14,5 +28,5 @@ export function MapComponent({ component }) {
|
|
|
14
28
|
justifyContent: 'center',
|
|
15
29
|
borderRadius: '4px',
|
|
16
30
|
color: '#666',
|
|
17
|
-
}, children: "
|
|
31
|
+
}, children: "Map not configured" })) }) }) }));
|
|
18
32
|
}
|