lib-pixelbuild 0.3.9 → 0.4.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.
@@ -3,6 +3,7 @@ import { Suspense, memo, useMemo } from 'react';
3
3
  import { Helmet } from 'react-helmet-async';
4
4
  import { BlockRenderer } from '../renderers/BlockRenderer.js';
5
5
  import { buildResponsiveCssForStyles } from '../utils/responsiveStyles.js';
6
+ import { collectCustomCss } from '../utils/customCss.js';
6
7
  import { buildFontFamily } from '../utils/fonts.js';
7
8
  function mergeStyles(websiteStyles, pageStyles) {
8
9
  const merged = Object.assign({}, (websiteStyles || {}));
@@ -60,6 +61,6 @@ const PageBuilder = memo(function PageBuilder({ website, page, editionMode }) {
60
61
  (_d = designSystem === null || designSystem === void 0 ? void 0 : designSystem.typography) === null || _d === void 0 ? void 0 : _d.fontSizeBase,
61
62
  mergedStyles
62
63
  ]);
63
- return (_jsxs(Suspense, { fallback: _jsx("div", { children: ((_e = website.properties) === null || _e === void 0 ? void 0 : _e.loadingMessage) || 'Carregando...' }), 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, "data-pb-page": true, children: [_jsx("style", { children: buildResponsiveCssForStyles('[data-pb-page]', page.styles) }), _jsx(BlockRenderer, { blocks: page.blocks, editionMode: editionMode, componentDefaults: website.componentDefaults }), (page.blocks || []).length === 0 && editionMode && (_jsx("div", { style: { padding: '40px', textAlign: 'center', color: '#94a3b8' }, children: "Nenhum conte\u00FAdo adicionado." }))] })] }));
64
+ return (_jsxs(Suspense, { fallback: _jsx("div", { children: ((_e = website.properties) === null || _e === void 0 ? void 0 : _e.loadingMessage) || 'Carregando...' }), 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, "data-pb-page": true, children: [_jsx("style", { children: buildResponsiveCssForStyles('[data-pb-page]', page.styles) }), _jsx("style", { children: collectCustomCss(website, page) }), _jsx(BlockRenderer, { blocks: page.blocks, editionMode: editionMode, componentDefaults: website.componentDefaults }), (page.blocks || []).length === 0 && editionMode && (_jsx("div", { style: { padding: '40px', textAlign: 'center', color: '#94a3b8' }, children: "Nenhum conte\u00FAdo adicionado." }))] })] }));
64
65
  });
65
66
  export { PageBuilder };
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { memo, useMemo } from 'react';
3
- import { PbContainer } from '../ui/PbContainer.js';
3
+ import { ComponentShell } from '../ui/PbComponentShell.js';
4
4
  import { PbButton } from '../ui/PbButton.js';
5
5
  import { orderBySort } from '../utils/orderBySort.js';
6
6
  import { getString, getNumber, getBoolean } from '../utils/propertyUtils.js';
@@ -27,7 +27,7 @@ const ButtonsComponent = memo(function ButtonsComponent({ component }) {
27
27
  containerStyle.alignItems = align;
28
28
  else
29
29
  containerStyle.justifyContent = align;
30
- return (_jsx(PbContainer, { "data-pb-component": component.id, style: component.styles, children: _jsx("div", { style: containerStyle, children: elements.map((element) => {
30
+ return (_jsx(ComponentShell, { component: component, children: _jsx("div", { style: containerStyle, children: elements.map((element) => {
31
31
  const props = element.properties || {};
32
32
  const href = getString(props, 'path') || getString(props, 'href');
33
33
  const text = getString(props, 'text') || getString(props, 'title');
@@ -1,38 +1,10 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { memo, useMemo } from 'react';
3
- import { PbContainer } from '../ui/PbContainer.js';
3
+ import { ComponentShell } from '../ui/PbComponentShell.js';
4
4
  import { PbRow } from '../ui/PbRow.js';
5
5
  import { ElementFactory } from '../factories/ElementFactory.js';
6
6
  import { orderBySort } from '../utils/orderBySort.js';
7
7
  const elementFactory = new ElementFactory();
8
- const INSET_STYLE_KEYS = new Set([
9
- 'borderColor',
10
- 'borderStyle',
11
- 'borderWidth',
12
- 'borderTopWidth',
13
- 'borderRightWidth',
14
- 'borderBottomWidth',
15
- 'borderLeftWidth',
16
- 'borderRadius',
17
- 'backgroundColor',
18
- 'backgroundImage',
19
- 'backgroundSize',
20
- 'backgroundPosition',
21
- 'boxShadow'
22
- ]);
23
- function splitCardStyles(styles, inset) {
24
- const container = {};
25
- const body = {};
26
- for (const [key, value] of Object.entries(styles !== null && styles !== void 0 ? styles : {})) {
27
- if (inset && INSET_STYLE_KEYS.has(key)) {
28
- body[key] = value;
29
- }
30
- else {
31
- container[key] = value;
32
- }
33
- }
34
- return { container: container, body: body };
35
- }
36
8
  function applyCardStyles(element, props) {
37
9
  const overrides = {};
38
10
  if (element.elementType === 'icon') {
@@ -74,10 +46,7 @@ const CardComponent = memo(function CardComponent({ component }) {
74
46
  cardTitleSize: properties === null || properties === void 0 ? void 0 : properties.cardTitleSize,
75
47
  cardTextColor: properties === null || properties === void 0 ? void 0 : properties.cardTextColor,
76
48
  cardTextSize: properties === null || properties === void 0 ? void 0 : properties.cardTextSize,
77
- cardElementGap: properties === null || properties === void 0 ? void 0 : properties.cardElementGap,
78
- cardContentPadding: properties === null || properties === void 0 ? void 0 : properties.cardContentPadding,
79
- cardContentAlign: properties === null || properties === void 0 ? void 0 : properties.cardContentAlign,
80
- cardInsetBorder: properties === null || properties === void 0 ? void 0 : properties.cardInsetBorder
49
+ cardElementGap: properties === null || properties === void 0 ? void 0 : properties.cardElementGap
81
50
  }), [
82
51
  properties === null || properties === void 0 ? void 0 : properties.cardElement1,
83
52
  properties === null || properties === void 0 ? void 0 : properties.cardElement2,
@@ -89,10 +58,7 @@ const CardComponent = memo(function CardComponent({ component }) {
89
58
  properties === null || properties === void 0 ? void 0 : properties.cardTitleSize,
90
59
  properties === null || properties === void 0 ? void 0 : properties.cardTextColor,
91
60
  properties === null || properties === void 0 ? void 0 : properties.cardTextSize,
92
- properties === null || properties === void 0 ? void 0 : properties.cardElementGap,
93
- properties === null || properties === void 0 ? void 0 : properties.cardContentPadding,
94
- properties === null || properties === void 0 ? void 0 : properties.cardContentAlign,
95
- properties === null || properties === void 0 ? void 0 : properties.cardInsetBorder
61
+ properties === null || properties === void 0 ? void 0 : properties.cardElementGap
96
62
  ]);
97
63
  const orderedElements = useMemo(() => {
98
64
  const cardOrder = [
@@ -118,19 +84,6 @@ const CardComponent = memo(function CardComponent({ component }) {
118
84
  const layout = String((properties === null || properties === void 0 ? void 0 : properties.cardLayout) || 'stacked');
119
85
  const rawGap = cardProps.cardElementGap;
120
86
  const elementGap = rawGap === '' || rawGap == null ? '0' : String(rawGap);
121
- const contentPadding = typeof cardProps.cardContentPadding === 'string' ? cardProps.cardContentPadding : '';
122
- const insetBorder = cardProps.cardInsetBorder === true || cardProps.cardInsetBorder === 'true';
123
- const contentAlign = typeof cardProps.cardContentAlign === 'string' && cardProps.cardContentAlign !== 'left'
124
- ? cardProps.cardContentAlign
125
- : undefined;
126
- const { containerStyle, bodyStyle } = useMemo(() => {
127
- const { container, body } = splitCardStyles(component.styles, insetBorder);
128
- if (contentPadding)
129
- body.padding = contentPadding;
130
- if (contentAlign)
131
- body.textAlign = contentAlign;
132
- return { containerStyle: container, bodyStyle: body };
133
- }, [component.styles, insetBorder, contentPadding, contentAlign]);
134
87
  const rowGapStyle = { gap: elementGap };
135
88
  let content;
136
89
  if (layout === 'icon-left' || layout === 'icon-right') {
@@ -148,6 +101,6 @@ const CardComponent = memo(function CardComponent({ component }) {
148
101
  else {
149
102
  content = (_jsx(PbRow, { id: "element-row", style: rowGapStyle, children: elements.map((element) => elementFactory.build(element)) }));
150
103
  }
151
- return (_jsx(PbContainer, { "data-pb-component": component.id, style: containerStyle, children: _jsx("div", { style: bodyStyle, children: content }) }));
104
+ return _jsx(ComponentShell, { component: component, children: content });
152
105
  });
153
106
  export { CardComponent };
@@ -0,0 +1,5 @@
1
+ import type { ComponentType } from '../types/ComponentType.js';
2
+ declare const DividerComponent: import("react").NamedExoticComponent<{
3
+ readonly component: ComponentType;
4
+ }>;
5
+ export { DividerComponent };
@@ -0,0 +1,33 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { memo } from 'react';
3
+ import { ComponentShell } from '../ui/PbComponentShell.js';
4
+ import { getString } from '../utils/propertyUtils.js';
5
+ const DividerComponent = memo(function DividerComponent({ component }) {
6
+ const properties = component.properties || {};
7
+ const orientation = getString(properties, 'orientation', 'horizontal');
8
+ const thickness = getString(properties, 'thickness', '1px');
9
+ const color = getString(properties, 'color', '#e5e7eb');
10
+ const lineStyle = getString(properties, 'lineStyle', 'solid');
11
+ const length = getString(properties, 'length', '100%');
12
+ const align = getString(properties, 'align', 'center');
13
+ const isVertical = orientation === 'vertical';
14
+ const marginLeft = align === 'left' ? '0' : align === 'right' ? 'auto' : 'auto';
15
+ const marginRight = align === 'right' ? '0' : align === 'left' ? 'auto' : 'auto';
16
+ const line = isVertical
17
+ ? {
18
+ width: 0,
19
+ height: length,
20
+ borderLeft: `${thickness} ${lineStyle} ${color}`,
21
+ marginLeft,
22
+ marginRight
23
+ }
24
+ : {
25
+ width: length,
26
+ height: 0,
27
+ borderTop: `${thickness} ${lineStyle} ${color}`,
28
+ marginLeft,
29
+ marginRight
30
+ };
31
+ return (_jsx(ComponentShell, { component: component, outer: "div", children: _jsx("div", { style: { display: 'flex', flexDirection: isVertical ? 'row' : 'column', minHeight: isVertical ? undefined : '1px' }, children: _jsx("div", { style: line, "aria-hidden": "true" }) }) }));
32
+ });
33
+ export { DividerComponent };
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { memo, useEffect, useMemo } from 'react';
3
- import { PbContainer } from '../ui/PbContainer.js';
3
+ import { ComponentShell } from '../ui/PbComponentShell.js';
4
4
  import { PbRow } from '../ui/PbRow.js';
5
5
  import { UseFormStore } from '../stores/UseFormStore.js';
6
6
  import { ElementFactory } from '../factories/ElementFactory.js';
@@ -19,6 +19,6 @@ const FormComponent = memo(function FormComponent({ component }) {
19
19
  const handleSubmit = (event) => {
20
20
  event.preventDefault();
21
21
  };
22
- return (_jsx(PbContainer, { "data-pb-component": component.id, style: component.styles, children: _jsx("form", { onSubmit: handleSubmit, noValidate: true, children: _jsx(PbRow, { style: { gap: 0 }, children: elements.map((element) => elementFactory.build(element, component.id)) }) }) }));
22
+ return (_jsx(ComponentShell, { component: component, children: _jsx("form", { onSubmit: handleSubmit, noValidate: true, children: _jsx(PbRow, { style: { gap: 0 }, children: elements.map((element) => elementFactory.build(element, component.id)) }) }) }));
23
23
  });
24
24
  export { FormComponent };
@@ -0,0 +1,5 @@
1
+ import type { ComponentType } from '../types/ComponentType.js';
2
+ declare const HeroComponent: import("react").NamedExoticComponent<{
3
+ readonly component: ComponentType;
4
+ }>;
5
+ export { HeroComponent };
@@ -0,0 +1,51 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { memo, useMemo } from 'react';
3
+ import { ComponentShell } from '../ui/PbComponentShell.js';
4
+ import { ElementFactory } from '../factories/ElementFactory.js';
5
+ import { orderBySort } from '../utils/orderBySort.js';
6
+ import { getString } from '../utils/propertyUtils.js';
7
+ const elementFactory = new ElementFactory();
8
+ const HeroComponent = memo(function HeroComponent({ component }) {
9
+ const properties = component.properties || {};
10
+ const mediaType = getString(properties, 'mediaType', 'none');
11
+ const mediaSrc = getString(properties, 'mediaSrc', '');
12
+ const mediaPoster = getString(properties, 'mediaPoster', '');
13
+ const minHeight = getString(properties, 'minHeight', '600px');
14
+ const overlayColor = getString(properties, 'overlayColor', '#000000');
15
+ const overlayOpacity = properties.overlayOpacity !== undefined && properties.overlayOpacity !== ''
16
+ ? Number(properties.overlayOpacity)
17
+ : 0.5;
18
+ const contentAlign = getString(properties, 'contentAlign', 'center');
19
+ const contentVerticalAlign = getString(properties, 'contentVerticalAlign', 'center');
20
+ const elements = useMemo(() => orderBySort(component.elements), [component.elements]);
21
+ const justifyContent = contentAlign === 'left' ? 'flex-start' : contentAlign === 'right' ? 'flex-end' : 'center';
22
+ const alignItems = contentVerticalAlign === 'top' ? 'flex-start' : contentVerticalAlign === 'bottom' ? 'flex-end' : 'center';
23
+ const contentStyle = {
24
+ position: 'relative',
25
+ zIndex: 2,
26
+ minHeight,
27
+ display: 'flex',
28
+ flexDirection: 'column',
29
+ justifyContent: alignItems,
30
+ alignItems: justifyContent,
31
+ textAlign: contentAlign === 'left' || contentAlign === 'right' ? contentAlign : 'center'
32
+ };
33
+ const media = useMemo(() => {
34
+ if (mediaType === 'video' && mediaSrc) {
35
+ return (_jsx("video", { autoPlay: true, muted: true, loop: true, playsInline: true, src: mediaSrc, poster: mediaPoster || undefined, style: { position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover' } }));
36
+ }
37
+ if (mediaType === 'image' && mediaSrc) {
38
+ return (_jsx("img", { src: mediaSrc, alt: "", style: { position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover' } }));
39
+ }
40
+ return null;
41
+ }, [mediaType, mediaSrc, mediaPoster]);
42
+ const overlay = (_jsx("div", { style: {
43
+ position: 'absolute',
44
+ inset: 0,
45
+ zIndex: 1,
46
+ backgroundColor: overlayColor,
47
+ opacity: overlayOpacity
48
+ } }));
49
+ return (_jsx(ComponentShell, { component: component, outer: "div", children: _jsxs("div", { style: { position: 'relative', overflow: 'hidden', minHeight }, children: [media, overlay, _jsx("div", { style: contentStyle, children: elements.map((element) => elementFactory.build(element)) })] }) }));
50
+ });
51
+ export { HeroComponent };
@@ -1,12 +1,12 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { memo, useMemo } from 'react';
3
- import { PbContainer } from '../ui/PbContainer.js';
3
+ import { ComponentShell } from '../ui/PbComponentShell.js';
4
4
  import { PbRow } from '../ui/PbRow.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 IconComponent = memo(function IconComponent({ component }) {
9
9
  const elements = useMemo(() => orderBySort(component.elements), [component.elements]);
10
- return (_jsx(PbContainer, { "data-pb-component": component.id, style: component.styles, children: _jsx(PbRow, { id: "element-row", style: { justifyContent: 'center', gap: 0 }, children: elements.map((element) => elementFactory.build(element)) }) }));
10
+ return (_jsx(ComponentShell, { component: component, children: _jsx(PbRow, { id: "element-row", style: { justifyContent: 'center', gap: 0 }, children: elements.map((element) => elementFactory.build(element)) }) }));
11
11
  });
12
12
  export { IconComponent };
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { memo, useMemo } from 'react';
3
- import { PbContainer } from '../ui/PbContainer.js';
3
+ import { ComponentShell } from '../ui/PbComponentShell.js';
4
4
  import { PbRow } from '../ui/PbRow.js';
5
5
  import { ElementFactory } from '../factories/ElementFactory.js';
6
6
  import { orderBySort } from '../utils/orderBySort.js';
@@ -11,8 +11,12 @@ const ImageComponent = memo(function ImageComponent({ component }) {
11
11
  const fullBleed = getBoolean(component.properties, 'fullBleed', false);
12
12
  const elements = useMemo(() => orderBySort(component.elements).map((element) => resolveImageElement(component, element)), [component.elements, component.properties]);
13
13
  if (fullBleed) {
14
- return (_jsx("div", { "data-pb-component": component.id, style: Object.assign(Object.assign({}, component.styles), { width: 'calc(100% + var(--pb-gutter, 1.5rem))', marginLeft: 'calc(var(--pb-gutter, 1.5rem) * -0.5)', marginRight: 'calc(var(--pb-gutter, 1.5rem) * -0.5)' }), children: elements.map((element) => elementFactory.build(Object.assign(Object.assign({}, element), { fullBleed: true }))) }));
14
+ return (_jsx(ComponentShell, { component: component, outer: "div", outerStyle: {
15
+ width: 'calc(100% + var(--pb-gutter, 1.5rem))',
16
+ marginLeft: 'calc(var(--pb-gutter, 1.5rem) * -0.5)',
17
+ marginRight: 'calc(var(--pb-gutter, 1.5rem) * -0.5)'
18
+ }, children: elements.map((element) => elementFactory.build(Object.assign(Object.assign({}, element), { fullBleed: true }))) }));
15
19
  }
16
- return (_jsx(PbContainer, { "data-pb-component": component.id, style: component.styles, children: _jsx(PbRow, { id: "element-row", style: { gap: 0 }, children: elements.map((element) => elementFactory.build(element)) }) }));
20
+ return (_jsx(ComponentShell, { component: component, children: _jsx(PbRow, { id: "element-row", style: { gap: 0 }, children: elements.map((element) => elementFactory.build(element)) }) }));
17
21
  });
18
22
  export { ImageComponent };
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { memo, useMemo } from 'react';
3
+ import { ComponentShell } from '../ui/PbComponentShell.js';
3
4
  import { ElementFactory } from '../factories/ElementFactory.js';
4
5
  import { ElementTypeEnum } from '../constants/ElementEnum.js';
5
6
  import { orderBySort } from '../utils/orderBySort.js';
@@ -27,6 +28,6 @@ const ListComponent = memo(function ListComponent({ component }) {
27
28
  }
28
29
  return result;
29
30
  }, [elements]);
30
- return (_jsx("div", { "data-pb-component": component.id, style: component.styles, children: _jsx("ul", { style: { listStyle: 'none', paddingLeft: 0 }, children: items.map(({ icon, text, id }) => (_jsxs("li", { style: { display: 'flex', alignItems: 'center', justifyContent: itemAlign, marginBottom: itemGap }, children: [_jsx("span", { children: elementFactory.build(icon) }), _jsx("span", { style: { fontSize: itemFontSize }, children: text ? elementFactory.build(text) : null })] }, id))) }) }));
31
+ return (_jsx(ComponentShell, { component: component, outer: "div", children: _jsx("ul", { style: { listStyle: 'none', paddingLeft: 0 }, children: items.map(({ icon, text, id }) => (_jsxs("li", { style: { display: 'flex', alignItems: 'center', justifyContent: itemAlign, marginBottom: itemGap }, children: [_jsx("span", { children: elementFactory.build(icon) }), _jsx("span", { style: { fontSize: itemFontSize }, children: text ? elementFactory.build(text) : null })] }, id))) }) }));
31
32
  });
32
33
  export { ListComponent };
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useMemo } from 'react';
3
+ import { ComponentShell } from '../ui/PbComponentShell.js';
3
4
  const COORDS_PATTERN = /^-?\d+(\.\d+)?$/;
4
5
  function isValidCoord(value) {
5
6
  if (typeof value !== 'string')
@@ -18,7 +19,7 @@ export function MapComponent({ component }) {
18
19
  }
19
20
  return `https://maps.google.com/maps?q=${encodeURIComponent(`${latStr},${lngStr}`)}&z=${zoom}&output=embed`;
20
21
  }, [lat, lng, zoom]);
21
- return (_jsx("div", { "data-pb-component": component.id, style: component.styles, 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: {
22
+ return (_jsx(ComponentShell, { component: component, outer: "div", 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: {
22
23
  width: '100%',
23
24
  height,
24
25
  backgroundColor: placeholderBackgroundColor,
@@ -3,6 +3,7 @@ import { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
3
3
  import { ElementFactory } from '../factories/ElementFactory.js';
4
4
  import { orderBySort } from '../utils/orderBySort.js';
5
5
  import { resolveResponsiveProperties, breakpointFromWidth } from '../utils/responsiveProperties.js';
6
+ import { getComponentShellStyles } from '../utils/componentShell.js';
6
7
  const elementFactory = new ElementFactory();
7
8
  function cleanComponentStyles(styles) {
8
9
  const exclude = new Set(['minHeight', 'display', 'alignItems', 'justifyContent', 'flexDirection']);
@@ -177,13 +178,27 @@ export function SliderComponent({ component, editionMode = false }) {
177
178
  const chrome = resolveChromeColors(resolved);
178
179
  const useSharedImages = resolved.imageCustomization !== 'separate';
179
180
  const sharedImageStyles = resolved.imageStyles || {};
180
- return (_jsxs("div", { ref: containerRef, "data-pb-component": component.id, style: Object.assign({ position: 'relative' }, cleanComponentStyles(component.styles)), children: [_jsx("div", { style: Object.assign({ overflow: 'hidden' }, (fixedHeight ? { height: fixedHeight } : {})), children: _jsx("div", { style: {
181
- display: 'flex',
182
- columnGap: spacing,
183
- transform: `translateX(${translateX}px)`,
184
- transition: animate && !dragging ? 'transform 0.3s ease' : 'none',
185
- cursor: dragging ? 'grabbing' : n > 1 ? 'grab' : 'default',
186
- touchAction: 'pan-y',
187
- userSelect: 'none'
188
- }, onPointerDown: onPointerDown, onPointerMove: onPointerMove, onPointerUp: endDrag, onPointerCancel: endDrag, onTransitionEnd: handleTransitionEnd, children: trackSlides.map((element, i) => (_jsx("div", { style: { width: slideWidth, flexShrink: 0, overflow: 'hidden' }, children: buildSlide(element, fixedHeight, sharedImageStyles, useSharedImages) }, `${element.id}-${i}`))) }) }), showArrows && (_jsxs(_Fragment, { children: [_jsx(ArrowButton, { side: "left", size: chrome.arrowSize, background: chrome.arrowBackground, color: chrome.arrowColor, onClick: () => goTo(index - 1) }), _jsx(ArrowButton, { side: "right", size: chrome.arrowSize, background: chrome.arrowBackground, color: chrome.arrowColor, onClick: () => goTo(index + 1) })] })), showDots && (_jsx(Dots, { count: n, current: currentIndex, size: chrome.dotSize, activeBackground: chrome.dotActiveBackground, background: chrome.dotBackground, activeColor: chrome.dotActiveColor, color: chrome.dotColor, onGoTo: goToDot }))] }));
181
+ const { containerStyle, bodyStyle } = useMemo(() => getComponentShellStyles(component), [component]);
182
+ if (resolved.mode === 'marquee') {
183
+ const animName = `pb-marquee-${String(component.id).replace(/[^a-zA-Z0-9_-]/g, '_')}`;
184
+ const marqueeDuration = Number(resolved.marqueeDuration) > 0 ? Number(resolved.marqueeDuration) : 20;
185
+ const marqueeSlides = [...slides, ...slides];
186
+ const keyframes = `@keyframes ${animName}{from{transform:translateX(0)}to{transform:translateX(calc(-50% - ${spacing / 2}px))}}`;
187
+ return (_jsxs(_Fragment, { children: [_jsx("style", { children: keyframes }), _jsx("div", { ref: containerRef, "data-pb-component": component.id, style: Object.assign({ position: 'relative' }, cleanComponentStyles(containerStyle)), children: _jsx("div", { style: bodyStyle, children: _jsx("div", { style: { overflow: 'hidden' }, children: _jsx("div", { style: {
188
+ display: 'flex',
189
+ columnGap: spacing,
190
+ width: 'max-content',
191
+ animation: `${animName} ${marqueeDuration}s linear infinite`,
192
+ userSelect: 'none'
193
+ }, children: marqueeSlides.map((element, i) => (_jsx("div", { style: { width: slideWidth, flexShrink: 0, overflow: 'hidden' }, children: buildSlide(element, fixedHeight, sharedImageStyles, useSharedImages) }, `${element.id}-${i}`))) }) }) }) })] }));
194
+ }
195
+ return (_jsx("div", { ref: containerRef, "data-pb-component": component.id, style: Object.assign({ position: 'relative' }, cleanComponentStyles(containerStyle)), children: _jsxs("div", { style: bodyStyle, children: [_jsx("div", { style: Object.assign({ overflow: 'hidden' }, (fixedHeight ? { height: fixedHeight } : {})), children: _jsx("div", { style: {
196
+ display: 'flex',
197
+ columnGap: spacing,
198
+ transform: `translateX(${translateX}px)`,
199
+ transition: animate && !dragging ? 'transform 0.3s ease' : 'none',
200
+ cursor: dragging ? 'grabbing' : n > 1 ? 'grab' : 'default',
201
+ touchAction: 'pan-y',
202
+ userSelect: 'none'
203
+ }, onPointerDown: onPointerDown, onPointerMove: onPointerMove, onPointerUp: endDrag, onPointerCancel: endDrag, onTransitionEnd: handleTransitionEnd, children: trackSlides.map((element, i) => (_jsx("div", { style: { width: slideWidth, flexShrink: 0, overflow: 'hidden' }, children: buildSlide(element, fixedHeight, sharedImageStyles, useSharedImages) }, `${element.id}-${i}`))) }) }), showArrows && (_jsxs(_Fragment, { children: [_jsx(ArrowButton, { side: "left", size: chrome.arrowSize, background: chrome.arrowBackground, color: chrome.arrowColor, onClick: () => goTo(index - 1) }), _jsx(ArrowButton, { side: "right", size: chrome.arrowSize, background: chrome.arrowBackground, color: chrome.arrowColor, onClick: () => goTo(index + 1) })] })), showDots && (_jsx(Dots, { count: n, current: currentIndex, size: chrome.dotSize, activeBackground: chrome.dotActiveBackground, background: chrome.dotBackground, activeColor: chrome.dotActiveColor, color: chrome.dotColor, onGoTo: goToDot }))] }) }));
189
204
  }
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { memo, useMemo } from 'react';
3
- import { PbContainer } from '../ui/PbContainer.js';
3
+ import { ComponentShell } from '../ui/PbComponentShell.js';
4
4
  import { PbRow } from '../ui/PbRow.js';
5
5
  import { ElementFactory } from '../factories/ElementFactory.js';
6
6
  import { orderBySort } from '../utils/orderBySort.js';
@@ -10,6 +10,6 @@ const elementFactory = new ElementFactory();
10
10
  const TextComponent = memo(function TextComponent({ component }) {
11
11
  const elements = useMemo(() => orderBySort(component.elements).map((element) => resolveImageElement(component, element)), [component.elements, component.properties]);
12
12
  const elementGap = getString(component.properties, 'elementGap', '');
13
- return (_jsx(PbContainer, { "data-pb-component": component.id, style: component.styles, children: _jsx(PbRow, { id: "element-row", style: { gap: elementGap }, children: elements.map((element) => elementFactory.build(element)) }) }));
13
+ return (_jsx(ComponentShell, { component: component, children: _jsx(PbRow, { id: "element-row", style: { gap: elementGap }, children: elements.map((element) => elementFactory.build(element)) }) }));
14
14
  });
15
15
  export { TextComponent };
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useCallback, useMemo, useRef, useState } from 'react';
3
+ import { ComponentShell } from '../ui/PbComponentShell.js';
3
4
  import { getBoolean, getString } from '../utils/propertyUtils.js';
4
5
  function toEmbed(src) {
5
6
  const value = src.trim();
@@ -124,7 +125,11 @@ export function VideoComponent({ component }) {
124
125
  color: placeholderTextColor
125
126
  }, children: "V\u00EDdeo n\u00E3o configurado" }));
126
127
  if (fullBleed) {
127
- return (_jsx("div", { "data-pb-component": component.id, style: Object.assign(Object.assign({}, component.styles), { width: 'calc(100% + var(--pb-gutter, 1.5rem))', marginLeft: 'calc(var(--pb-gutter, 1.5rem) * -0.5)', marginRight: 'calc(var(--pb-gutter, 1.5rem) * -0.5)' }), children: content }));
128
+ return (_jsx(ComponentShell, { component: component, outer: "div", outerStyle: {
129
+ width: 'calc(100% + var(--pb-gutter, 1.5rem))',
130
+ marginLeft: 'calc(var(--pb-gutter, 1.5rem) * -0.5)',
131
+ marginRight: 'calc(var(--pb-gutter, 1.5rem) * -0.5)'
132
+ }, children: content }));
128
133
  }
129
- return (_jsx("div", { "data-pb-component": component.id, style: component.styles, children: content }));
134
+ return (_jsx(ComponentShell, { component: component, outer: "div", children: content }));
130
135
  }
@@ -15,5 +15,6 @@ export declare const ComponentTypeEnum: {
15
15
  readonly List: "list";
16
16
  readonly Buttons: "buttons";
17
17
  readonly Icon: "icon";
18
+ readonly Divider: "divider";
18
19
  };
19
20
  export type ComponentTypeSlug = (typeof ComponentTypeEnum)[keyof typeof ComponentTypeEnum];
@@ -14,5 +14,6 @@ export const ComponentTypeEnum = {
14
14
  Map: 'map',
15
15
  List: 'list',
16
16
  Buttons: 'buttons',
17
- Icon: 'icon'
17
+ Icon: 'icon',
18
+ Divider: 'divider'
18
19
  };
@@ -5,4 +5,4 @@
5
5
  * valores de destaque usam `var(--pb-*)` com fallback, então o design system
6
6
  * (Etapa 1) sobrescreve os tokens sem tocar nestes estilos.
7
7
  */
8
- export declare const baseCss = "\n/* \u2500\u2500\u2500 Reset e tipografia base \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.pb-root, .pb-root *, .pb-root *::before, .pb-root *::after {\n box-sizing: border-box;\n}\n.pb-root {\n margin: 0;\n font-family: var(--pb-font-family, 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif);\n font-size: var(--pb-font-size-base, 16px);\n line-height: var(--pb-line-height, 1.6);\n color: var(--pb-color-text, #1f2937);\n background-color: var(--pb-color-background, #ffffff);\n -webkit-font-smoothing: antialiased;\n text-rendering: optimizeLegibility;\n}\n.pb-root h1, .pb-root h2, .pb-root h3, .pb-root h4, .pb-root h5, .pb-root h6 {\n margin: 0 0 0.5em;\n font-weight: var(--pb-font-weight-bold, 700);\n line-height: 1.2;\n color: var(--pb-color-heading, #111827);\n}\n.pb-root p {\n margin: 0;\n}\n.pb-root img {\n max-width: 100%;\n height: auto;\n}\n.pb-root a {\n color: var(--pb-color-primary, #f58220);\n text-decoration: none;\n}\n.pb-root a:hover {\n text-decoration: underline;\n}\n\n/* \u2500\u2500\u2500 Layout: container / grade \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.pb-container {\n width: 100%;\n margin-inline: auto;\n padding-inline: var(--pb-gutter, 1.5rem);\n}\n.pb-container-fluid {\n max-width: none !important;\n padding-inline: var(--pb-gutter, 1.5rem);\n}\n@media (min-width: 576px) {\n .pb-container { max-width: 540px; }\n}\n@media (min-width: 768px) {\n .pb-container { max-width: 720px; }\n}\n@media (min-width: 992px) {\n .pb-container { max-width: 960px; }\n}\n@media (min-width: 1200px) {\n .pb-container { max-width: 1140px; }\n}\n@media (min-width: 1400px) {\n .pb-container { max-width: 1320px; }\n}\n\n.pb-row {\n display: grid;\n grid-template-columns: repeat(12, minmax(0, 1fr));\n gap: var(--pb-gutter, 0);\n}\n.pb-col {\n min-width: 0;\n}\n\n/* \u2500\u2500\u2500 Bot\u00F5es \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.pb-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 0.5em;\n padding: 0.5em 1.25em;\n font-family: inherit;\n font-size: 1rem;\n font-weight: var(--pb-font-weight-bold, 700);\n line-height: 1.5;\n text-align: center;\n text-decoration: none;\n border: 1px solid transparent;\n border-radius: var(--pb-radius-md, 8px);\n cursor: pointer;\n transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease, box-shadow 0.2s ease;\n}\n.pb-button:hover { text-decoration: none; }\n.pb-button:disabled {\n opacity: 0.55;\n cursor: not-allowed;\n}\n\n.pb-button-primary {\n background-color: var(--pb-color-primary, #f58220);\n border-color: var(--pb-color-primary, #f58220);\n color: #ffffff;\n}\n.pb-button-primary:hover:not(:disabled) {\n background-color: color-mix(in srgb, var(--pb-color-primary, #f58220) 88%, #000);\n border-color: color-mix(in srgb, var(--pb-color-primary, #f58220) 88%, #000);\n}\n.pb-button-secondary {\n background-color: var(--pb-color-secondary, #64748b);\n border-color: var(--pb-color-secondary, #64748b);\n color: #ffffff;\n}\n.pb-button-success {\n background-color: var(--pb-color-success, #16a34a);\n border-color: var(--pb-color-success, #16a34a);\n color: #ffffff;\n}\n.pb-button-danger {\n background-color: var(--pb-color-danger, #dc2626);\n border-color: var(--pb-color-danger, #dc2626);\n color: #ffffff;\n}\n.pb-button-warning {\n background-color: var(--pb-color-warning, #f59e0b);\n border-color: var(--pb-color-warning, #f59e0b);\n color: #1f2937;\n}\n.pb-button-info {\n background-color: var(--pb-color-info, #0ea5e9);\n border-color: var(--pb-color-info, #0ea5e9);\n color: #ffffff;\n}\n.pb-button-light {\n background-color: var(--pb-color-light, #f8fafc);\n border-color: var(--pb-color-border, #e5e7eb);\n color: var(--pb-color-text, #1f2937);\n}\n.pb-button-dark {\n background-color: var(--pb-color-dark, #0f172a);\n border-color: var(--pb-color-dark, #0f172a);\n color: #ffffff;\n}\n.pb-button-outline-primary {\n background-color: transparent;\n border-color: var(--pb-color-primary, #f58220);\n color: var(--pb-color-primary, #f58220);\n}\n.pb-button-outline-primary:hover:not(:disabled) {\n background-color: var(--pb-color-primary, #f58220);\n color: #ffffff;\n}\n.pb-button-outline-secondary {\n background-color: transparent;\n border-color: var(--pb-color-secondary, #64748b);\n color: var(--pb-color-secondary, #64748b);\n}\n.pb-button-outline-secondary:hover:not(:disabled) {\n background-color: var(--pb-color-secondary, #64748b);\n color: #ffffff;\n}\n.pb-button-link {\n background-color: transparent;\n border-color: transparent;\n color: var(--pb-color-primary, #f58220);\n padding-inline: 0.5em;\n}\n.pb-button-sm {\n padding: 0.375em 0.875em;\n font-size: 0.875rem;\n border-radius: var(--pb-radius-sm, 4px);\n}\n.pb-button-lg {\n padding: 0.625em 1.5em;\n font-size: 1.125rem;\n border-radius: var(--pb-radius-lg, 12px);\n}\n\n/* \u2500\u2500\u2500 Spinner \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.pb-spinner {\n display: inline-block;\n width: 1.5rem;\n height: 1.5rem;\n border: 0.2em solid currentColor;\n border-right-color: transparent;\n border-radius: 50%;\n animation: pb-spin 0.75s linear infinite;\n vertical-align: -0.25em;\n}\n.pb-spinner-sm {\n width: 1rem;\n height: 1rem;\n border-width: 0.18em;\n}\n@keyframes pb-spin {\n to { transform: rotate(360deg); }\n}\n\n/* \u2500\u2500\u2500 Alertas \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.pb-alert {\n padding: var(--pb-space-md, 16px) var(--pb-space-lg, 24px);\n border: 1px solid;\n border-radius: var(--pb-radius-md, 8px);\n margin-bottom: var(--pb-space-md, 16px);\n}\n.pb-alert-title {\n font-weight: var(--pb-font-weight-bold, 700);\n margin-bottom: 0.25em;\n}\n.pb-alert-primary {\n background-color: color-mix(in srgb, var(--pb-color-primary, #f58220) 10%, #fff);\n border-color: color-mix(in srgb, var(--pb-color-primary, #f58220) 35%, #fff);\n color: var(--pb-color-text, #1f2937);\n}\n.pb-alert-info {\n background-color: color-mix(in srgb, var(--pb-color-info, #0ea5e9) 10%, #fff);\n border-color: color-mix(in srgb, var(--pb-color-info, #0ea5e9) 35%, #fff);\n color: var(--pb-color-text, #1f2937);\n}\n.pb-alert-success {\n background-color: color-mix(in srgb, var(--pb-color-success, #16a34a) 10%, #fff);\n border-color: color-mix(in srgb, var(--pb-color-success, #16a34a) 35%, #fff);\n color: var(--pb-color-text, #1f2937);\n}\n.pb-alert-warning {\n background-color: color-mix(in srgb, var(--pb-color-warning, #f59e0b) 12%, #fff);\n border-color: color-mix(in srgb, var(--pb-color-warning, #f59e0b) 35%, #fff);\n color: var(--pb-color-text, #1f2937);\n}\n.pb-alert-danger {\n background-color: color-mix(in srgb, var(--pb-color-danger, #dc2626) 10%, #fff);\n border-color: color-mix(in srgb, var(--pb-color-danger, #dc2626) 35%, #fff);\n color: var(--pb-color-text, #1f2937);\n}\n.pb-alert-secondary {\n background-color: var(--pb-color-light, #f8fafc);\n border-color: var(--pb-color-border, #e5e7eb);\n color: var(--pb-color-text, #1f2937);\n}\n\n/* \u2500\u2500\u2500 Formul\u00E1rios \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.pb-form-group {\n margin-bottom: var(--pb-space-md, 16px);\n}\n.pb-form-label {\n display: block;\n margin-bottom: 0.375rem;\n font-weight: var(--pb-font-weight-bold, 700);\n color: var(--pb-color-text, #1f2937);\n}\n.pb-form-control,\n.pb-form-select,\n.pb-form-textarea {\n display: block;\n width: 100%;\n padding: 0.5em 0.75em;\n font-family: inherit;\n font-size: 1rem;\n line-height: 1.5;\n color: var(--pb-color-text, #1f2937);\n background-color: var(--pb-color-surface, #ffffff);\n border: 1px solid var(--pb-color-border, #e5e7eb);\n border-radius: var(--pb-radius-sm, 4px);\n transition: border-color 0.15s ease, box-shadow 0.15s ease;\n}\n.pb-form-control:focus,\n.pb-form-select:focus,\n.pb-form-textarea:focus {\n outline: none;\n border-color: var(--pb-color-primary, #f58220);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--pb-color-primary, #f58220) 20%, transparent);\n}\n.pb-form-control-invalid,\n.pb-form-select-invalid,\n.pb-form-textarea-invalid {\n border-color: var(--pb-color-danger, #dc2626);\n}\n.pb-invalid-feedback {\n display: block;\n margin-top: 0.375rem;\n font-size: 0.875rem;\n color: var(--pb-color-danger, #dc2626);\n}\n.pb-form-check {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n margin-bottom: 0.5rem;\n}\n.pb-form-check-input {\n width: 1.1em;\n height: 1.1em;\n accent-color: var(--pb-color-primary, #f58220);\n flex-shrink: 0;\n}\n.pb-form-check-label {\n font-weight: normal;\n margin-bottom: 0;\n}\n\n/* \u2500\u2500\u2500 Header \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.pb-header {\n width: 100%;\n}\n.pb-header-inner {\n display: flex;\n align-items: center;\n justify-content: flex-start;\n gap: var(--pb-space-md, 16px);\n flex-wrap: wrap;\n padding-block: var(--pb-space-sm, 8px);\n}\n.pb-brand {\n display: inline-flex;\n align-items: center;\n text-decoration: none;\n flex-shrink: 0;\n}\n.pb-brand img {\n max-height: 56px;\n width: auto;\n}\n.pb-nav {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n flex-wrap: wrap;\n min-width: 0;\n}\n.pb-nav-link {\n display: inline-block;\n padding: 0.5rem 0.75rem;\n color: inherit;\n text-decoration: none;\n font-weight: 500;\n border-bottom: 2px solid transparent;\n transition: color 0.2s ease, border-color 0.2s ease;\n}\n.pb-nav-link:hover {\n text-decoration: none;\n opacity: 0.85;\n}\n.pb-nav-link-active {\n border-bottom-color: currentColor;\n}\n.pb-nav-toggle {\n display: none;\n padding: 0.375rem 0.625rem;\n font-size: 1.25rem;\n line-height: 1;\n background: transparent;\n border: 1px solid var(--pb-color-border, #e5e7eb);\n border-radius: var(--pb-radius-sm, 4px);\n cursor: pointer;\n color: inherit;\n}\n@media (max-width: 991.98px) {\n .pb-nav-toggle { display: inline-flex; align-items: center; }\n .pb-nav { display: none; }\n .pb-nav-open { display: flex; flex-direction: column; width: 100%; }\n}\n\n/* \u2500\u2500\u2500 Footer \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.pb-footer {\n width: 100%;\n padding-block: var(--pb-space-xl, 32px);\n color: var(--pb-color-text, #1f2937);\n}\n.pb-footer-divider {\n border: none;\n border-top: 1px solid var(--pb-color-border, #e5e7eb);\n margin: var(--pb-space-lg, 24px) 0;\n}\n.pb-social-icons {\n display: flex;\n justify-content: center;\n gap: var(--pb-space-sm, 8px);\n flex-wrap: wrap;\n}\n.pb-social-icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-size: 1.5rem;\n color: inherit;\n text-decoration: none;\n}\n.pb-social-icon:hover {\n text-decoration: none;\n opacity: 0.8;\n}\n\n/* \u2500\u2500\u2500 Loading \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.pb-loading {\n min-height: 100vh;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: var(--pb-space-sm, 8px);\n font-weight: 600;\n letter-spacing: 0.08em;\n}\n\n/* \u2500\u2500\u2500 Utilit\u00E1rios de alinhamento \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.pb-text-center { text-align: center; }\n.pb-mb-0 { margin-bottom: 0; }\n";
8
+ export declare const baseCss = "\n/* \u2500\u2500\u2500 Reset e tipografia base \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.pb-root, .pb-root *, .pb-root *::before, .pb-root *::after {\n box-sizing: border-box;\n}\n.pb-root {\n margin: 0;\n font-family: var(--pb-font-family, 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif);\n font-size: var(--pb-font-size-base, 16px);\n line-height: var(--pb-line-height, 1.6);\n color: var(--pb-color-text, #1f2937);\n background-color: var(--pb-color-background, #ffffff);\n -webkit-font-smoothing: antialiased;\n text-rendering: optimizeLegibility;\n}\n.pb-root h1, .pb-root h2, .pb-root h3, .pb-root h4, .pb-root h5, .pb-root h6 {\n margin: 0 0 0.5em;\n font-family: var(--pb-font-heading, var(--pb-font-family, 'Inter', system-ui, sans-serif));\n font-weight: var(--pb-font-weight-bold, 700);\n line-height: 1.2;\n color: var(--pb-color-heading, #111827);\n}\n.pb-root p {\n margin: 0;\n}\n.pb-root img {\n max-width: 100%;\n height: auto;\n}\n.pb-root a {\n color: var(--pb-color-primary, #f58220);\n text-decoration: none;\n}\n.pb-root a:hover {\n text-decoration: underline;\n}\n\n/* \u2500\u2500\u2500 Layout: container / grade \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.pb-container {\n width: 100%;\n margin-inline: auto;\n padding-inline: var(--pb-gutter, 1.5rem);\n}\n.pb-container-fluid {\n max-width: none !important;\n padding-inline: var(--pb-gutter, 1.5rem);\n}\n@media (min-width: 576px) {\n .pb-container { max-width: 540px; }\n}\n@media (min-width: 768px) {\n .pb-container { max-width: 720px; }\n}\n@media (min-width: 992px) {\n .pb-container { max-width: 960px; }\n}\n@media (min-width: 1200px) {\n .pb-container { max-width: 1140px; }\n}\n@media (min-width: 1400px) {\n .pb-container { max-width: 1320px; }\n}\n\n.pb-row {\n display: grid;\n grid-template-columns: repeat(12, minmax(0, 1fr));\n gap: var(--pb-gutter, 0);\n}\n.pb-col {\n min-width: 0;\n}\n\n/* \u2500\u2500\u2500 Bot\u00F5es \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.pb-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 0.5em;\n padding: 0.5em 1.25em;\n font-family: inherit;\n font-size: 1rem;\n font-weight: var(--pb-font-weight-bold, 700);\n line-height: 1.5;\n text-align: center;\n text-decoration: none;\n border: 1px solid transparent;\n border-radius: var(--pb-radius-md, 8px);\n cursor: pointer;\n transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease, box-shadow 0.2s ease;\n}\n.pb-button:hover { text-decoration: none; }\n.pb-button:disabled {\n opacity: 0.55;\n cursor: not-allowed;\n}\n\n.pb-button-primary {\n background-color: var(--pb-color-primary, #f58220);\n border-color: var(--pb-color-primary, #f58220);\n color: #ffffff;\n}\n.pb-button-primary:hover:not(:disabled) {\n background-color: color-mix(in srgb, var(--pb-color-primary, #f58220) 88%, #000);\n border-color: color-mix(in srgb, var(--pb-color-primary, #f58220) 88%, #000);\n}\n.pb-button-secondary {\n background-color: var(--pb-color-secondary, #64748b);\n border-color: var(--pb-color-secondary, #64748b);\n color: #ffffff;\n}\n.pb-button-success {\n background-color: var(--pb-color-success, #16a34a);\n border-color: var(--pb-color-success, #16a34a);\n color: #ffffff;\n}\n.pb-button-danger {\n background-color: var(--pb-color-danger, #dc2626);\n border-color: var(--pb-color-danger, #dc2626);\n color: #ffffff;\n}\n.pb-button-warning {\n background-color: var(--pb-color-warning, #f59e0b);\n border-color: var(--pb-color-warning, #f59e0b);\n color: #1f2937;\n}\n.pb-button-info {\n background-color: var(--pb-color-info, #0ea5e9);\n border-color: var(--pb-color-info, #0ea5e9);\n color: #ffffff;\n}\n.pb-button-light {\n background-color: var(--pb-color-light, #f8fafc);\n border-color: var(--pb-color-border, #e5e7eb);\n color: var(--pb-color-text, #1f2937);\n}\n.pb-button-dark {\n background-color: var(--pb-color-dark, #0f172a);\n border-color: var(--pb-color-dark, #0f172a);\n color: #ffffff;\n}\n.pb-button-outline-primary {\n background-color: transparent;\n border-color: var(--pb-color-primary, #f58220);\n color: var(--pb-color-primary, #f58220);\n}\n.pb-button-outline-primary:hover:not(:disabled) {\n background-color: var(--pb-color-primary, #f58220);\n color: #ffffff;\n}\n.pb-button-outline-secondary {\n background-color: transparent;\n border-color: var(--pb-color-secondary, #64748b);\n color: var(--pb-color-secondary, #64748b);\n}\n.pb-button-outline-secondary:hover:not(:disabled) {\n background-color: var(--pb-color-secondary, #64748b);\n color: #ffffff;\n}\n.pb-button-link {\n background-color: transparent;\n border-color: transparent;\n color: var(--pb-color-primary, #f58220);\n padding-inline: 0.5em;\n}\n.pb-button-sm {\n padding: 0.375em 0.875em;\n font-size: 0.875rem;\n border-radius: var(--pb-radius-sm, 4px);\n}\n.pb-button-lg {\n padding: 0.625em 1.5em;\n font-size: 1.125rem;\n border-radius: var(--pb-radius-lg, 12px);\n}\n\n/* \u2500\u2500\u2500 Spinner \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.pb-spinner {\n display: inline-block;\n width: 1.5rem;\n height: 1.5rem;\n border: 0.2em solid currentColor;\n border-right-color: transparent;\n border-radius: 50%;\n animation: pb-spin 0.75s linear infinite;\n vertical-align: -0.25em;\n}\n.pb-spinner-sm {\n width: 1rem;\n height: 1rem;\n border-width: 0.18em;\n}\n@keyframes pb-spin {\n to { transform: rotate(360deg); }\n}\n\n/* \u2500\u2500\u2500 Alertas \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.pb-alert {\n padding: var(--pb-space-md, 16px) var(--pb-space-lg, 24px);\n border: 1px solid;\n border-radius: var(--pb-radius-md, 8px);\n margin-bottom: var(--pb-space-md, 16px);\n}\n.pb-alert-title {\n font-weight: var(--pb-font-weight-bold, 700);\n margin-bottom: 0.25em;\n}\n.pb-alert-primary {\n background-color: color-mix(in srgb, var(--pb-color-primary, #f58220) 10%, #fff);\n border-color: color-mix(in srgb, var(--pb-color-primary, #f58220) 35%, #fff);\n color: var(--pb-color-text, #1f2937);\n}\n.pb-alert-info {\n background-color: color-mix(in srgb, var(--pb-color-info, #0ea5e9) 10%, #fff);\n border-color: color-mix(in srgb, var(--pb-color-info, #0ea5e9) 35%, #fff);\n color: var(--pb-color-text, #1f2937);\n}\n.pb-alert-success {\n background-color: color-mix(in srgb, var(--pb-color-success, #16a34a) 10%, #fff);\n border-color: color-mix(in srgb, var(--pb-color-success, #16a34a) 35%, #fff);\n color: var(--pb-color-text, #1f2937);\n}\n.pb-alert-warning {\n background-color: color-mix(in srgb, var(--pb-color-warning, #f59e0b) 12%, #fff);\n border-color: color-mix(in srgb, var(--pb-color-warning, #f59e0b) 35%, #fff);\n color: var(--pb-color-text, #1f2937);\n}\n.pb-alert-danger {\n background-color: color-mix(in srgb, var(--pb-color-danger, #dc2626) 10%, #fff);\n border-color: color-mix(in srgb, var(--pb-color-danger, #dc2626) 35%, #fff);\n color: var(--pb-color-text, #1f2937);\n}\n.pb-alert-secondary {\n background-color: var(--pb-color-light, #f8fafc);\n border-color: var(--pb-color-border, #e5e7eb);\n color: var(--pb-color-text, #1f2937);\n}\n\n/* \u2500\u2500\u2500 Formul\u00E1rios \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.pb-form-group {\n margin-bottom: var(--pb-space-md, 16px);\n}\n.pb-form-label {\n display: block;\n margin-bottom: 0.375rem;\n font-weight: var(--pb-font-weight-bold, 700);\n color: var(--pb-color-text, #1f2937);\n}\n.pb-form-control,\n.pb-form-select,\n.pb-form-textarea {\n display: block;\n width: 100%;\n padding: 0.5em 0.75em;\n font-family: inherit;\n font-size: 1rem;\n line-height: 1.5;\n color: var(--pb-color-text, #1f2937);\n background-color: var(--pb-color-surface, #ffffff);\n border: 1px solid var(--pb-color-border, #e5e7eb);\n border-radius: var(--pb-radius-sm, 4px);\n transition: border-color 0.15s ease, box-shadow 0.15s ease;\n}\n.pb-form-control:focus,\n.pb-form-select:focus,\n.pb-form-textarea:focus {\n outline: none;\n border-color: var(--pb-color-primary, #f58220);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--pb-color-primary, #f58220) 20%, transparent);\n}\n.pb-form-control-invalid,\n.pb-form-select-invalid,\n.pb-form-textarea-invalid {\n border-color: var(--pb-color-danger, #dc2626);\n}\n.pb-invalid-feedback {\n display: block;\n margin-top: 0.375rem;\n font-size: 0.875rem;\n color: var(--pb-color-danger, #dc2626);\n}\n.pb-form-check {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n margin-bottom: 0.5rem;\n}\n.pb-form-check-input {\n width: 1.1em;\n height: 1.1em;\n accent-color: var(--pb-color-primary, #f58220);\n flex-shrink: 0;\n}\n.pb-form-check-label {\n font-weight: normal;\n margin-bottom: 0;\n}\n\n/* \u2500\u2500\u2500 Header \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.pb-header {\n width: 100%;\n}\n.pb-header-inner {\n display: flex;\n align-items: center;\n justify-content: flex-start;\n gap: var(--pb-space-md, 16px);\n flex-wrap: wrap;\n padding-block: var(--pb-space-sm, 8px);\n}\n.pb-brand {\n display: inline-flex;\n align-items: center;\n text-decoration: none;\n flex-shrink: 0;\n}\n.pb-brand img {\n max-height: 56px;\n width: auto;\n}\n.pb-nav {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n flex-wrap: wrap;\n min-width: 0;\n}\n.pb-nav-link {\n display: inline-block;\n padding: 0.5rem 0.75rem;\n color: inherit;\n text-decoration: none;\n font-weight: 500;\n border-bottom: 2px solid transparent;\n transition: color 0.2s ease, border-color 0.2s ease;\n}\n.pb-nav-link:hover {\n text-decoration: none;\n opacity: 0.85;\n}\n.pb-nav-link-active {\n border-bottom-color: currentColor;\n}\n.pb-nav-toggle {\n display: none;\n padding: 0.375rem 0.625rem;\n font-size: 1.25rem;\n line-height: 1;\n background: transparent;\n border: 1px solid var(--pb-color-border, #e5e7eb);\n border-radius: var(--pb-radius-sm, 4px);\n cursor: pointer;\n color: inherit;\n}\n@media (max-width: 991.98px) {\n .pb-nav-toggle { display: inline-flex; align-items: center; }\n .pb-nav { display: none; }\n .pb-nav-open { display: flex; flex-direction: column; width: 100%; }\n}\n\n/* \u2500\u2500\u2500 Footer \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.pb-footer {\n width: 100%;\n padding-block: var(--pb-space-xl, 32px);\n color: var(--pb-color-text, #1f2937);\n}\n.pb-footer-divider {\n border: none;\n border-top: 1px solid var(--pb-color-border, #e5e7eb);\n margin: var(--pb-space-lg, 24px) 0;\n}\n.pb-social-icons {\n display: flex;\n justify-content: center;\n gap: var(--pb-space-sm, 8px);\n flex-wrap: wrap;\n}\n.pb-social-icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-size: 1.5rem;\n color: inherit;\n text-decoration: none;\n}\n.pb-social-icon:hover {\n text-decoration: none;\n opacity: 0.8;\n}\n\n/* \u2500\u2500\u2500 Loading \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.pb-loading {\n min-height: 100vh;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: var(--pb-space-sm, 8px);\n font-weight: 600;\n letter-spacing: 0.08em;\n}\n\n/* \u2500\u2500\u2500 Utilit\u00E1rios de alinhamento \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.pb-text-center { text-align: center; }\n.pb-mb-0 { margin-bottom: 0; }\n";
@@ -22,6 +22,7 @@ export const baseCss = `
22
22
  }
23
23
  .pb-root h1, .pb-root h2, .pb-root h3, .pb-root h4, .pb-root h5, .pb-root h6 {
24
24
  margin: 0 0 0.5em;
25
+ font-family: var(--pb-font-heading, var(--pb-font-family, 'Inter', system-ui, sans-serif));
25
26
  font-weight: var(--pb-font-weight-bold, 700);
26
27
  line-height: 1.2;
27
28
  color: var(--pb-color-heading, #111827);
@@ -24,6 +24,7 @@ export interface DesignColors {
24
24
  }
25
25
  export interface DesignTypography {
26
26
  fontFamily: string;
27
+ headingFontFamily: string;
27
28
  fontSizeBase: string;
28
29
  fontWeightNormal: string;
29
30
  fontWeightBold: string;
@@ -29,6 +29,7 @@ export const defaultTokens = {
29
29
  },
30
30
  typography: {
31
31
  fontFamily: "'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif",
32
+ headingFontFamily: "'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif",
32
33
  fontSizeBase: '16px',
33
34
  fontWeightNormal: '400',
34
35
  fontWeightBold: '700',
@@ -101,6 +102,7 @@ export function tokenToCssVars(tokens) {
101
102
  '--pb-color-muted': colors.muted,
102
103
  '--pb-color-border': colors.border,
103
104
  '--pb-font-family': typography.fontFamily,
105
+ '--pb-font-heading': typography.headingFontFamily,
104
106
  '--pb-font-size-base': typography.fontSizeBase,
105
107
  '--pb-font-weight-normal': typography.fontWeightNormal,
106
108
  '--pb-font-weight-bold': typography.fontWeightBold,
@@ -10,6 +10,8 @@ import { MapComponent } from '../components/MapComponent.js';
10
10
  import { ButtonsComponent } from '../components/ButtonsComponent.js';
11
11
  import { IconComponent } from '../components/IconComponent.js';
12
12
  import { VideoComponent } from '../components/VideoComponent.js';
13
+ import { HeroComponent } from '../components/HeroComponent.js';
14
+ import { DividerComponent } from '../components/DividerComponent.js';
13
15
  /**
14
16
  * Aplica os estilos padrão do design system (`componentDefaults`) antes dos
15
17
  * estilos da instância — o override da instância prevalece.
@@ -26,8 +28,11 @@ export class ComponentFactory {
26
28
  switch (resolved.componentType) {
27
29
  case ComponentTypeEnum.Text:
28
30
  case ComponentTypeEnum.TextWithImage:
29
- case ComponentTypeEnum.HeroBanner:
30
31
  return _jsx(TextComponent, { component: resolved }, resolved.id);
32
+ case ComponentTypeEnum.HeroBanner:
33
+ return _jsx(HeroComponent, { component: resolved }, resolved.id);
34
+ case ComponentTypeEnum.Divider:
35
+ return _jsx(DividerComponent, { component: resolved }, resolved.id);
31
36
  case ComponentTypeEnum.Image:
32
37
  return _jsx(ImageComponent, { component: resolved }, resolved.id);
33
38
  case ComponentTypeEnum.List:
@@ -0,0 +1,8 @@
1
+ import { type CSSProperties, type ReactNode } from 'react';
2
+ import type { ComponentType } from '../types/ComponentType.js';
3
+ export declare function ComponentShell({ component, outer, outerStyle, children }: {
4
+ readonly component: ComponentType;
5
+ readonly outer?: 'container' | 'div';
6
+ readonly outerStyle?: CSSProperties;
7
+ readonly children: ReactNode;
8
+ }): import("react").JSX.Element;
@@ -0,0 +1,12 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useMemo } from 'react';
3
+ import { PbContainer } from './PbContainer.js';
4
+ import { getComponentShellStyles } from '../utils/componentShell.js';
5
+ export function ComponentShell({ component, outer = 'container', outerStyle, children }) {
6
+ const { containerStyle, bodyStyle } = useMemo(() => getComponentShellStyles(component), [component]);
7
+ const content = _jsx("div", { style: bodyStyle, children: children });
8
+ if (outer === 'div') {
9
+ return (_jsx("div", { "data-pb-component": component.id, style: Object.assign(Object.assign({}, containerStyle), outerStyle), children: content }));
10
+ }
11
+ return (_jsx(PbContainer, { "data-pb-component": component.id, style: Object.assign(Object.assign({}, containerStyle), outerStyle), children: content }));
12
+ }
@@ -0,0 +1,10 @@
1
+ import type { CSSProperties } from 'react';
2
+ import type { ComponentType } from '../types/ComponentType.js';
3
+ export declare function splitComponentStyles(styles: Record<string, unknown> | undefined, inset: boolean): {
4
+ container: CSSProperties;
5
+ body: CSSProperties;
6
+ };
7
+ export declare function getComponentShellStyles(component: ComponentType): {
8
+ containerStyle: CSSProperties;
9
+ bodyStyle: CSSProperties;
10
+ };
@@ -0,0 +1,41 @@
1
+ import { getString, getBoolean } from './propertyUtils.js';
2
+ const INSET_STYLE_KEYS = new Set([
3
+ 'borderColor',
4
+ 'borderStyle',
5
+ 'borderWidth',
6
+ 'borderTopWidth',
7
+ 'borderRightWidth',
8
+ 'borderBottomWidth',
9
+ 'borderLeftWidth',
10
+ 'borderRadius',
11
+ 'backgroundColor',
12
+ 'backgroundImage',
13
+ 'backgroundSize',
14
+ 'backgroundPosition',
15
+ 'boxShadow'
16
+ ]);
17
+ export function splitComponentStyles(styles, inset) {
18
+ const container = {};
19
+ const body = {};
20
+ for (const [key, value] of Object.entries(styles !== null && styles !== void 0 ? styles : {})) {
21
+ if (inset && INSET_STYLE_KEYS.has(key)) {
22
+ body[key] = value;
23
+ }
24
+ else {
25
+ container[key] = value;
26
+ }
27
+ }
28
+ return { container: container, body: body };
29
+ }
30
+ export function getComponentShellStyles(component) {
31
+ const properties = component.properties || {};
32
+ const inset = getBoolean(properties, 'contentInsetBorder', false);
33
+ const { container, body } = splitComponentStyles(component.styles, inset);
34
+ const contentPadding = getString(properties, 'contentPadding', '');
35
+ const contentAlign = getString(properties, 'contentAlign', '');
36
+ if (contentPadding)
37
+ body.padding = contentPadding;
38
+ if (contentAlign && contentAlign !== 'left')
39
+ body.textAlign = contentAlign;
40
+ return { containerStyle: container, bodyStyle: body };
41
+ }
@@ -0,0 +1,8 @@
1
+ import type { PageType } from '../types/PageType.js';
2
+ import type { WebsiteType } from '../types/WebsiteType.js';
3
+ /**
4
+ * Coleta o `customCss` declarado em qualquer nível (site, página, bloco,
5
+ * componente e elemento) e devolve um único bloco de CSS. É renderizado como
6
+ * `<style>` global, permitindo hover, pseudo-elementos, animações etc.
7
+ */
8
+ export declare function collectCustomCss(website?: WebsiteType, page?: PageType): string;
@@ -0,0 +1,27 @@
1
+ function extract(styles) {
2
+ if (!styles || typeof styles !== 'object')
3
+ return '';
4
+ const css = styles.customCss;
5
+ return typeof css === 'string' && css.trim() ? css.trim() : '';
6
+ }
7
+ /**
8
+ * Coleta o `customCss` declarado em qualquer nível (site, página, bloco,
9
+ * componente e elemento) e devolve um único bloco de CSS. É renderizado como
10
+ * `<style>` global, permitindo hover, pseudo-elementos, animações etc.
11
+ */
12
+ export function collectCustomCss(website, page) {
13
+ var _a, _b, _c;
14
+ const parts = [];
15
+ parts.push(extract(website === null || website === void 0 ? void 0 : website.styles));
16
+ parts.push(extract(page === null || page === void 0 ? void 0 : page.styles));
17
+ for (const block of (_a = page === null || page === void 0 ? void 0 : page.blocks) !== null && _a !== void 0 ? _a : []) {
18
+ parts.push(extract(block.styles));
19
+ for (const component of (_b = block.components) !== null && _b !== void 0 ? _b : []) {
20
+ parts.push(extract(component.styles));
21
+ for (const element of (_c = component.elements) !== null && _c !== void 0 ? _c : []) {
22
+ parts.push(extract(element.styles));
23
+ }
24
+ }
25
+ }
26
+ return parts.filter(Boolean).join('\n');
27
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lib-pixelbuild",
3
- "version": "0.3.9",
3
+ "version": "0.4.1",
4
4
  "engines": {
5
5
  "node": "^24.0.0",
6
6
  "npm": ">=10"