@1urso/generic-editor 0.1.40 → 0.1.41
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/dist/editor/context.d.ts +2 -1
- package/dist/generic-editor.js +226 -140
- package/dist/generic-editor.umd.cjs +64 -39
- package/package.json +1 -1
package/dist/editor/context.d.ts
CHANGED
|
@@ -25,7 +25,7 @@ export interface IElementAnimation {
|
|
|
25
25
|
}
|
|
26
26
|
export interface IElement {
|
|
27
27
|
id: string;
|
|
28
|
-
type: 'text' | 'image' | 'box' | 'group';
|
|
28
|
+
type: 'text' | 'image' | 'box' | 'group' | 'text-container';
|
|
29
29
|
name?: string;
|
|
30
30
|
groupId?: string;
|
|
31
31
|
content: string;
|
|
@@ -39,6 +39,7 @@ export interface IElement {
|
|
|
39
39
|
formatting?: IElementFormatting;
|
|
40
40
|
conditions?: IElementCondition[];
|
|
41
41
|
autoGrow?: boolean;
|
|
42
|
+
containerExpansion?: 'vertical' | 'horizontal';
|
|
42
43
|
animation?: IElementAnimation;
|
|
43
44
|
styleBindings?: Record<string, string>;
|
|
44
45
|
}
|
package/dist/generic-editor.js
CHANGED
|
@@ -8771,7 +8771,7 @@ const EditorProvider = ({ children: n, isList: E = !1, availableProps: O = [], t
|
|
|
8771
8771
|
});
|
|
8772
8772
|
}, []), Rj = React.useCallback((n) => {
|
|
8773
8773
|
let _ = {};
|
|
8774
|
-
n.type === "box" && (_.backgroundColor = "var(--gray-4)");
|
|
8774
|
+
n.type === "box" && (_.backgroundColor = "var(--gray-4)"), n.type === "text-container" && (_.backgroundColor = "var(--gray-4)", _.border = "1px solid var(--gray-8)", _.padding = "8px", _.display = "flex", _.alignItems = "flex-start", _.justifyContent = "flex-start");
|
|
8775
8775
|
let E = {
|
|
8776
8776
|
id: crypto.randomUUID(),
|
|
8777
8777
|
x: 50,
|
|
@@ -10469,21 +10469,53 @@ var AnimationSettings = ({ element: n, updateElement: _ }) => {
|
|
|
10469
10469
|
color: "gray",
|
|
10470
10470
|
children: "Configure como os dados serão exibidos quando substituídos."
|
|
10471
10471
|
}),
|
|
10472
|
-
n.type === "text" && /* @__PURE__ */ jsxs(p, { children: [
|
|
10473
|
-
|
|
10474
|
-
|
|
10475
|
-
|
|
10476
|
-
|
|
10477
|
-
|
|
10478
|
-
|
|
10479
|
-
|
|
10480
|
-
|
|
10481
|
-
|
|
10482
|
-
|
|
10483
|
-
|
|
10484
|
-
|
|
10485
|
-
|
|
10486
|
-
|
|
10472
|
+
(n.type === "text" || n.type === "text-container") && /* @__PURE__ */ jsxs(p, { children: [
|
|
10473
|
+
/* @__PURE__ */ jsxs(p$1, {
|
|
10474
|
+
align: "center",
|
|
10475
|
+
gap: "2",
|
|
10476
|
+
mb: "3",
|
|
10477
|
+
children: [/* @__PURE__ */ jsx(i, {
|
|
10478
|
+
checked: n.autoGrow || !1,
|
|
10479
|
+
onCheckedChange: (E) => _(n.id, { autoGrow: E })
|
|
10480
|
+
}), /* @__PURE__ */ jsx(p$2, {
|
|
10481
|
+
size: "2",
|
|
10482
|
+
children: n.type === "text-container" ? "Expandir container automaticamente" : "Expandir altura automaticamente (Multilinha)"
|
|
10483
|
+
})]
|
|
10484
|
+
}),
|
|
10485
|
+
n.type === "text-container" && n.autoGrow && /* @__PURE__ */ jsxs(p, {
|
|
10486
|
+
mb: "3",
|
|
10487
|
+
ml: "4",
|
|
10488
|
+
children: [/* @__PURE__ */ jsx(p$2, {
|
|
10489
|
+
size: "1",
|
|
10490
|
+
mb: "1",
|
|
10491
|
+
as: "div",
|
|
10492
|
+
children: "Direção de Expansão"
|
|
10493
|
+
}), /* @__PURE__ */ jsxs("select", {
|
|
10494
|
+
value: n.containerExpansion || "vertical",
|
|
10495
|
+
onChange: (E) => _(n.id, { containerExpansion: E.target.value }),
|
|
10496
|
+
style: {
|
|
10497
|
+
width: "100%",
|
|
10498
|
+
padding: "6px",
|
|
10499
|
+
borderRadius: "4px",
|
|
10500
|
+
border: "1px solid var(--gray-6)",
|
|
10501
|
+
backgroundColor: "var(--color-panel-solid)",
|
|
10502
|
+
color: "var(--gray-12)",
|
|
10503
|
+
fontSize: "14px"
|
|
10504
|
+
},
|
|
10505
|
+
children: [/* @__PURE__ */ jsx("option", {
|
|
10506
|
+
value: "vertical",
|
|
10507
|
+
children: "Vertical (Altura Cresce)"
|
|
10508
|
+
}), /* @__PURE__ */ jsx("option", {
|
|
10509
|
+
value: "horizontal",
|
|
10510
|
+
children: "Horizontal (Largura Cresce)"
|
|
10511
|
+
})]
|
|
10512
|
+
})]
|
|
10513
|
+
}),
|
|
10514
|
+
/* @__PURE__ */ jsx(o$2, {
|
|
10515
|
+
size: "4",
|
|
10516
|
+
mb: "3"
|
|
10517
|
+
})
|
|
10518
|
+
] }),
|
|
10487
10519
|
/* @__PURE__ */ jsxs(p, { children: [/* @__PURE__ */ jsx(p$2, {
|
|
10488
10520
|
size: "1",
|
|
10489
10521
|
mb: "1",
|
|
@@ -11865,58 +11897,58 @@ const ElementContextMenu = ({ children: n, element: _ }) => {
|
|
|
11865
11897
|
}) })] })
|
|
11866
11898
|
] });
|
|
11867
11899
|
}, DraggableElement = React.memo(({ element: n, isSelected: _ }) => {
|
|
11868
|
-
let { selectElement: E, updateElement: O, updateElements: A, state: j, resizeGroup: M, setSnapLines: N } = useEditor(), [P, z] = useState(!1), [B, H] = useState(!1), U = useRef({
|
|
11900
|
+
let { selectElement: E, updateElement: O, updateElements: A, state: j, resizeGroup: M, setSnapLines: N } = useEditor(), [P, z] = useState(!1), [B, H] = useState(!1), [U, G] = useState(!1), Z = useRef(null), Lj = useRef({
|
|
11869
11901
|
x: 0,
|
|
11870
11902
|
y: 0
|
|
11871
|
-
}),
|
|
11903
|
+
}), Rj = useRef({}), zj = useRef(!1), Hj = useRef(!1), Uj = useRef(0), Wj = useRef(0), Gj = useRef({
|
|
11872
11904
|
x: 0,
|
|
11873
11905
|
y: 0
|
|
11874
|
-
}),
|
|
11875
|
-
if (
|
|
11876
|
-
if (n.type === "text")
|
|
11877
|
-
let O =
|
|
11906
|
+
}), Jj = useRef(!1), Yj = j.canvasHeight || 150, Xj = j.isList ? j.mockData.length > 0 ? j.mockData[0] : null : j.singleMockData, Zj = n.content, Qj = {};
|
|
11907
|
+
if (Xj) {
|
|
11908
|
+
if (n.type === "text") Zj = Zj.replace(/\{\{(.*?)\}\}/g, (_, E) => {
|
|
11909
|
+
let O = Xj[E.trim()];
|
|
11878
11910
|
return O == null ? _ : n.formatting ? formatValue(O, n.formatting) : String(O);
|
|
11879
11911
|
});
|
|
11880
11912
|
else if (n.type === "image") if (n.dataBinding) {
|
|
11881
|
-
let _ =
|
|
11882
|
-
_ != null && (
|
|
11883
|
-
} else
|
|
11884
|
-
let E =
|
|
11913
|
+
let _ = Xj[n.dataBinding];
|
|
11914
|
+
_ != null && (Zj = String(_));
|
|
11915
|
+
} else Zj = Zj.replace(/\{\{(.*?)\}\}/g, (n, _) => {
|
|
11916
|
+
let E = Xj[_.trim()];
|
|
11885
11917
|
return E == null ? n : String(E);
|
|
11886
11918
|
});
|
|
11887
11919
|
n.styleBindings && Object.entries(n.styleBindings).forEach(([n, _]) => {
|
|
11888
|
-
let E =
|
|
11889
|
-
E != null && (
|
|
11890
|
-
...
|
|
11920
|
+
let E = Xj[_];
|
|
11921
|
+
E != null && (Qj = {
|
|
11922
|
+
...Qj,
|
|
11891
11923
|
[n]: String(E)
|
|
11892
11924
|
});
|
|
11893
11925
|
}), n.conditions && n.conditions.forEach((n) => {
|
|
11894
|
-
let _ =
|
|
11895
|
-
checkCondition(_, n.operator, n.value) && (
|
|
11896
|
-
...
|
|
11926
|
+
let _ = Xj[n.property];
|
|
11927
|
+
checkCondition(_, n.operator, n.value) && (Qj = {
|
|
11928
|
+
...Qj,
|
|
11897
11929
|
...n.style
|
|
11898
11930
|
});
|
|
11899
11931
|
});
|
|
11900
11932
|
}
|
|
11901
|
-
let
|
|
11902
|
-
if (
|
|
11903
|
-
let { display: n, ..._ } =
|
|
11904
|
-
|
|
11933
|
+
let $j = Qj.display === "none";
|
|
11934
|
+
if ($j && _) {
|
|
11935
|
+
let { display: n, ..._ } = Qj;
|
|
11936
|
+
Qj = _;
|
|
11905
11937
|
}
|
|
11906
|
-
let
|
|
11907
|
-
_.stopPropagation(), !
|
|
11908
|
-
},
|
|
11938
|
+
let eM = (_) => {
|
|
11939
|
+
_.stopPropagation(), !zj.current && !Hj.current && !P && (_.shiftKey ? E(n.id, !0) : E(n.id, !1));
|
|
11940
|
+
}, tM = (O) => {
|
|
11909
11941
|
if (O.button !== 0) return;
|
|
11910
11942
|
O.stopPropagation();
|
|
11911
11943
|
let A = O.shiftKey;
|
|
11912
|
-
if (
|
|
11944
|
+
if (zj.current = !1, Hj.current = !1, n.groupId && !_) {
|
|
11913
11945
|
E(n.groupId, A);
|
|
11914
11946
|
return;
|
|
11915
11947
|
}
|
|
11916
|
-
_ || (E(n.id, A),
|
|
11948
|
+
_ || (E(n.id, A), Hj.current = !0), z(!0), Lj.current = {
|
|
11917
11949
|
x: O.clientX,
|
|
11918
11950
|
y: O.clientY
|
|
11919
|
-
};
|
|
11951
|
+
}, O.currentTarget.setPointerCapture(O.pointerId);
|
|
11920
11952
|
let M = new Set(j.selectedElementIds);
|
|
11921
11953
|
_ || (A || M.clear(), M.add(n.id));
|
|
11922
11954
|
let N = {};
|
|
@@ -11925,90 +11957,107 @@ const ElementContextMenu = ({ children: n, element: _ }) => {
|
|
|
11925
11957
|
x: n.x,
|
|
11926
11958
|
y: n.y
|
|
11927
11959
|
});
|
|
11928
|
-
}),
|
|
11929
|
-
},
|
|
11930
|
-
|
|
11960
|
+
}), Rj.current = N;
|
|
11961
|
+
}, nM = (_) => {
|
|
11962
|
+
if (P) {
|
|
11963
|
+
let E = j.zoom || 1, O = (_.clientX - Lj.current.x) / E, M = (_.clientY - Lj.current.y) / E;
|
|
11964
|
+
(Math.abs(O) > 2 || Math.abs(M) > 2) && (zj.current = !0);
|
|
11965
|
+
let P = 5 / E, z = [], B = Object.entries(Rj.current).map(([_, E]) => {
|
|
11966
|
+
let A = E.x + O, N = E.y + M;
|
|
11967
|
+
if (j.gridSize > 0) A = Math.round(A / j.gridSize) * j.gridSize, N = Math.round(N / j.gridSize) * j.gridSize;
|
|
11968
|
+
else {
|
|
11969
|
+
let E = !1, O = !1, M = n.width ?? 100, B = n.height ?? 100;
|
|
11970
|
+
Object.keys(Rj.current).length === 1 && j.elements.forEach((n) => {
|
|
11971
|
+
if (n.id === _) return;
|
|
11972
|
+
let j = n.width ?? 100, H = n.height ?? 100;
|
|
11973
|
+
E || (Math.abs(A - n.x) < P ? (A = n.x, E = !0, z.push({
|
|
11974
|
+
orientation: "vertical",
|
|
11975
|
+
position: n.x
|
|
11976
|
+
})) : Math.abs(A - (n.x + j)) < P ? (A = n.x + j, E = !0, z.push({
|
|
11977
|
+
orientation: "vertical",
|
|
11978
|
+
position: n.x + j
|
|
11979
|
+
})) : Math.abs(A + M - n.x) < P ? (A = n.x - M, E = !0, z.push({
|
|
11980
|
+
orientation: "vertical",
|
|
11981
|
+
position: n.x
|
|
11982
|
+
})) : Math.abs(A + M - (n.x + j)) < P && (A = n.x + j - M, E = !0, z.push({
|
|
11983
|
+
orientation: "vertical",
|
|
11984
|
+
position: n.x + j
|
|
11985
|
+
}))), O || (Math.abs(N - n.y) < P ? (N = n.y, O = !0, z.push({
|
|
11986
|
+
orientation: "horizontal",
|
|
11987
|
+
position: n.y
|
|
11988
|
+
})) : Math.abs(N - (n.y + H)) < P ? (N = n.y + H, O = !0, z.push({
|
|
11989
|
+
orientation: "horizontal",
|
|
11990
|
+
position: n.y + H
|
|
11991
|
+
})) : Math.abs(N + B - n.y) < P ? (N = n.y - B, O = !0, z.push({
|
|
11992
|
+
orientation: "horizontal",
|
|
11993
|
+
position: n.y
|
|
11994
|
+
})) : Math.abs(N + B - (n.y + H)) < P && (N = n.y + H - B, O = !0, z.push({
|
|
11995
|
+
orientation: "horizontal",
|
|
11996
|
+
position: n.y + H
|
|
11997
|
+
})));
|
|
11998
|
+
});
|
|
11999
|
+
}
|
|
12000
|
+
return j.isList && (N = Math.max(0, N)), {
|
|
12001
|
+
id: _,
|
|
12002
|
+
changes: {
|
|
12003
|
+
x: A,
|
|
12004
|
+
y: N
|
|
12005
|
+
}
|
|
12006
|
+
};
|
|
12007
|
+
});
|
|
12008
|
+
N && N(z), A(B, !1);
|
|
12009
|
+
}
|
|
12010
|
+
}, rM = (n) => {
|
|
12011
|
+
P && (z(!1), n.currentTarget.releasePointerCapture(n.pointerId), A([], !0), N && N([]));
|
|
12012
|
+
}, iM = (_) => {
|
|
12013
|
+
_.stopPropagation(), _.preventDefault(), G(!0);
|
|
11931
12014
|
let E = _.target.closest(".resizable-element");
|
|
11932
12015
|
if (E) {
|
|
11933
12016
|
let O = E.getBoundingClientRect();
|
|
11934
|
-
|
|
12017
|
+
Gj.current = {
|
|
11935
12018
|
x: O.left + O.width / 2,
|
|
11936
12019
|
y: O.top + O.height / 2
|
|
11937
12020
|
};
|
|
11938
|
-
let A = _.clientX -
|
|
11939
|
-
|
|
12021
|
+
let A = _.clientX - Gj.current.x, j = _.clientY - Gj.current.y;
|
|
12022
|
+
Uj.current = Math.atan2(j, A) * (180 / Math.PI), Wj.current = n.rotation || 0;
|
|
11940
12023
|
}
|
|
11941
12024
|
};
|
|
11942
12025
|
useEffect(() => {
|
|
11943
12026
|
let _ = (_) => {
|
|
11944
|
-
if (
|
|
11945
|
-
let E =
|
|
11946
|
-
(Math.abs(O) > 2 || Math.abs(M) > 2) && (Z.current = !0);
|
|
11947
|
-
let P = 5 / E, z = [], B = Object.entries(G.current).map(([_, E]) => {
|
|
11948
|
-
let A = E.x + O, N = E.y + M;
|
|
11949
|
-
if (j.gridSize > 0) A = Math.round(A / j.gridSize) * j.gridSize, N = Math.round(N / j.gridSize) * j.gridSize;
|
|
11950
|
-
else {
|
|
11951
|
-
let E = !1, O = !1, M = n.width ?? 100, B = n.height ?? 100;
|
|
11952
|
-
Object.keys(G.current).length === 1 && j.elements.forEach((n) => {
|
|
11953
|
-
if (n.id === _) return;
|
|
11954
|
-
let j = n.width ?? 100, H = n.height ?? 100;
|
|
11955
|
-
E || (Math.abs(A - n.x) < P ? (A = n.x, E = !0, z.push({
|
|
11956
|
-
orientation: "vertical",
|
|
11957
|
-
position: n.x
|
|
11958
|
-
})) : Math.abs(A - (n.x + j)) < P ? (A = n.x + j, E = !0, z.push({
|
|
11959
|
-
orientation: "vertical",
|
|
11960
|
-
position: n.x + j
|
|
11961
|
-
})) : Math.abs(A + M - n.x) < P ? (A = n.x - M, E = !0, z.push({
|
|
11962
|
-
orientation: "vertical",
|
|
11963
|
-
position: n.x
|
|
11964
|
-
})) : Math.abs(A + M - (n.x + j)) < P && (A = n.x + j - M, E = !0, z.push({
|
|
11965
|
-
orientation: "vertical",
|
|
11966
|
-
position: n.x + j
|
|
11967
|
-
}))), O || (Math.abs(N - n.y) < P ? (N = n.y, O = !0, z.push({
|
|
11968
|
-
orientation: "horizontal",
|
|
11969
|
-
position: n.y
|
|
11970
|
-
})) : Math.abs(N - (n.y + H)) < P ? (N = n.y + H, O = !0, z.push({
|
|
11971
|
-
orientation: "horizontal",
|
|
11972
|
-
position: n.y + H
|
|
11973
|
-
})) : Math.abs(N + B - n.y) < P ? (N = n.y - B, O = !0, z.push({
|
|
11974
|
-
orientation: "horizontal",
|
|
11975
|
-
position: n.y
|
|
11976
|
-
})) : Math.abs(N + B - (n.y + H)) < P && (N = n.y + H - B, O = !0, z.push({
|
|
11977
|
-
orientation: "horizontal",
|
|
11978
|
-
position: n.y + H
|
|
11979
|
-
})));
|
|
11980
|
-
});
|
|
11981
|
-
}
|
|
11982
|
-
return j.isList && (N = Math.max(0, N)), {
|
|
11983
|
-
id: _,
|
|
11984
|
-
changes: {
|
|
11985
|
-
x: A,
|
|
11986
|
-
y: N
|
|
11987
|
-
}
|
|
11988
|
-
};
|
|
11989
|
-
});
|
|
11990
|
-
N && N(z), A(B, !1);
|
|
11991
|
-
}
|
|
11992
|
-
if (B) {
|
|
11993
|
-
let E = _.clientX - Hj.current.x, A = _.clientY - Hj.current.y, j = Math.atan2(A, E) * (180 / Math.PI) - Rj.current, M = (zj.current + j) % 360;
|
|
12027
|
+
if (U) {
|
|
12028
|
+
let E = _.clientX - Gj.current.x, A = _.clientY - Gj.current.y, j = Math.atan2(A, E) * (180 / Math.PI) - Uj.current, M = (Wj.current + j) % 360;
|
|
11994
12029
|
O(n.id, { rotation: M }, !1);
|
|
11995
12030
|
}
|
|
11996
12031
|
}, E = () => {
|
|
11997
|
-
|
|
12032
|
+
U && (G(!1), O(n.id, { rotation: n.rotation }, !0));
|
|
11998
12033
|
};
|
|
11999
|
-
return
|
|
12034
|
+
return U && (window.addEventListener("mousemove", _), window.addEventListener("mouseup", E)), () => {
|
|
12000
12035
|
window.removeEventListener("mousemove", _), window.removeEventListener("mouseup", E);
|
|
12001
12036
|
};
|
|
12002
12037
|
}, [
|
|
12003
|
-
|
|
12004
|
-
B,
|
|
12038
|
+
U,
|
|
12005
12039
|
n.id,
|
|
12006
12040
|
O,
|
|
12007
|
-
|
|
12008
|
-
|
|
12009
|
-
|
|
12041
|
+
n.rotation
|
|
12042
|
+
]), useEffect(() => {
|
|
12043
|
+
n.type === "text-container" && n.autoGrow && n.containerExpansion === "horizontal" && requestAnimationFrame(() => {
|
|
12044
|
+
if (Z.current) {
|
|
12045
|
+
let _ = Z.current.scrollWidth;
|
|
12046
|
+
Math.abs(_ - (n.width ?? 0)) > 2 && O(n.id, { width: _ }, !1);
|
|
12047
|
+
}
|
|
12048
|
+
});
|
|
12049
|
+
}, [
|
|
12050
|
+
Zj,
|
|
12051
|
+
n.autoGrow,
|
|
12052
|
+
n.containerExpansion,
|
|
12053
|
+
n.style,
|
|
12054
|
+
n.width,
|
|
12055
|
+
n.formatting,
|
|
12056
|
+
O,
|
|
12057
|
+
n.id,
|
|
12058
|
+
n.type
|
|
12010
12059
|
]);
|
|
12011
|
-
let
|
|
12060
|
+
let aM = {
|
|
12012
12061
|
position: "absolute",
|
|
12013
12062
|
left: 0,
|
|
12014
12063
|
top: 0,
|
|
@@ -12016,8 +12065,8 @@ const ElementContextMenu = ({ children: n, element: _ }) => {
|
|
|
12016
12065
|
height: n.autoGrow ? "auto" : "100%",
|
|
12017
12066
|
minHeight: n.autoGrow ? "100%" : void 0,
|
|
12018
12067
|
padding: n.type === "image" || n.type === "text" ? 0 : "8px",
|
|
12019
|
-
border: _ ? "2px solid var(--accent-9)" : "1px dashed transparent",
|
|
12020
|
-
outline: _ ? "none" : "1px solid transparent",
|
|
12068
|
+
border: _ || B ? "2px solid var(--accent-9)" : "1px dashed transparent",
|
|
12069
|
+
outline: _ || B ? "none" : "1px solid transparent",
|
|
12021
12070
|
cursor: P ? "grabbing" : "grab",
|
|
12022
12071
|
borderRadius: "var(--radius-2)",
|
|
12023
12072
|
overflow: n.autoGrow ? "visible" : "hidden",
|
|
@@ -12025,7 +12074,7 @@ const ElementContextMenu = ({ children: n, element: _ }) => {
|
|
|
12025
12074
|
wordBreak: n.autoGrow ? "break-word" : void 0,
|
|
12026
12075
|
userSelect: "none",
|
|
12027
12076
|
...n.style,
|
|
12028
|
-
...
|
|
12077
|
+
...Qj
|
|
12029
12078
|
};
|
|
12030
12079
|
return /* @__PURE__ */ jsx(Resizable, {
|
|
12031
12080
|
className: "resizable-element",
|
|
@@ -12033,11 +12082,12 @@ const ElementContextMenu = ({ children: n, element: _ }) => {
|
|
|
12033
12082
|
width: n.width ?? 100,
|
|
12034
12083
|
height: n.autoGrow ? "auto" : n.height ?? 100
|
|
12035
12084
|
},
|
|
12036
|
-
maxHeight: j.isList ? Math.max(10,
|
|
12085
|
+
maxHeight: j.isList ? Math.max(10, Yj - n.y) : void 0,
|
|
12037
12086
|
onResizeStart: (n) => {
|
|
12038
|
-
|
|
12087
|
+
H(!0), Jj.current = !!n.shiftKey;
|
|
12039
12088
|
},
|
|
12040
12089
|
onResizeStop: (_, E, A, j) => {
|
|
12090
|
+
H(!1);
|
|
12041
12091
|
let N = (n.width ?? 100) + j.width, P = (n.height ?? 100) + j.height;
|
|
12042
12092
|
n.type === "group" ? M(n.id, N, P) : O(n.id, {
|
|
12043
12093
|
width: N,
|
|
@@ -12048,8 +12098,10 @@ const ElementContextMenu = ({ children: n, element: _ }) => {
|
|
|
12048
12098
|
position: "absolute",
|
|
12049
12099
|
transform: `translate(${n.x ?? 0}px, ${n.y ?? 0}px) rotate(${n.rotation || 0}deg)`,
|
|
12050
12100
|
height: n.autoGrow ? "auto" : void 0,
|
|
12051
|
-
display:
|
|
12052
|
-
opacity:
|
|
12101
|
+
display: $j && !_ ? "none" : void 0,
|
|
12102
|
+
opacity: $j && _ ? .4 : 1,
|
|
12103
|
+
zIndex: _ ? 1e3 : void 0,
|
|
12104
|
+
outline: _ ? "1px dashed var(--accent-9)" : void 0
|
|
12053
12105
|
},
|
|
12054
12106
|
enable: _ && !n.autoGrow ? void 0 : {
|
|
12055
12107
|
top: !1,
|
|
@@ -12061,7 +12113,7 @@ const ElementContextMenu = ({ children: n, element: _ }) => {
|
|
|
12061
12113
|
bottomLeft: !1,
|
|
12062
12114
|
topLeft: !1
|
|
12063
12115
|
},
|
|
12064
|
-
lockAspectRatio:
|
|
12116
|
+
lockAspectRatio: Jj.current === !0,
|
|
12065
12117
|
grid: j.gridSize > 0 ? [j.gridSize, j.gridSize] : void 0,
|
|
12066
12118
|
children: /* @__PURE__ */ jsx(ElementContextMenu, {
|
|
12067
12119
|
element: n,
|
|
@@ -12072,9 +12124,20 @@ const ElementContextMenu = ({ children: n, element: _ }) => {
|
|
|
12072
12124
|
position: "relative"
|
|
12073
12125
|
},
|
|
12074
12126
|
children: [/* @__PURE__ */ jsxs(p, {
|
|
12075
|
-
|
|
12076
|
-
|
|
12077
|
-
|
|
12127
|
+
ref: Z,
|
|
12128
|
+
style: {
|
|
12129
|
+
...aM,
|
|
12130
|
+
whiteSpace: n.type === "text-container" && n.autoGrow && n.containerExpansion === "horizontal" ? "nowrap" : aM.whiteSpace,
|
|
12131
|
+
width: n.type === "text-container" && n.autoGrow && n.containerExpansion === "horizontal" ? "max-content" : "100%",
|
|
12132
|
+
height: n.type === "text-container" && n.autoGrow && n.containerExpansion === "vertical" ? "auto" : "100%"
|
|
12133
|
+
},
|
|
12134
|
+
onPointerDown: tM,
|
|
12135
|
+
onPointerMove: nM,
|
|
12136
|
+
onPointerUp: rM,
|
|
12137
|
+
onMouseDown: (n) => {
|
|
12138
|
+
n.button === 0 && n.stopPropagation();
|
|
12139
|
+
},
|
|
12140
|
+
onClick: eM,
|
|
12078
12141
|
title: n.name,
|
|
12079
12142
|
onMouseEnter: (n) => {
|
|
12080
12143
|
_ || (n.currentTarget.style.borderColor = "var(--gray-6)");
|
|
@@ -12088,10 +12151,18 @@ const ElementContextMenu = ({ children: n, element: _ }) => {
|
|
|
12088
12151
|
width: "100%",
|
|
12089
12152
|
height: "100%"
|
|
12090
12153
|
},
|
|
12091
|
-
children:
|
|
12154
|
+
children: Zj
|
|
12155
|
+
}),
|
|
12156
|
+
n.type === "text-container" && /* @__PURE__ */ jsx(p$2, {
|
|
12157
|
+
style: {
|
|
12158
|
+
width: "100%",
|
|
12159
|
+
height: "100%",
|
|
12160
|
+
display: "block"
|
|
12161
|
+
},
|
|
12162
|
+
children: Zj
|
|
12092
12163
|
}),
|
|
12093
|
-
n.type === "image" && (
|
|
12094
|
-
src:
|
|
12164
|
+
n.type === "image" && (Zj ? /* @__PURE__ */ jsx("img", {
|
|
12165
|
+
src: Zj,
|
|
12095
12166
|
alt: "Element",
|
|
12096
12167
|
style: {
|
|
12097
12168
|
width: "100%",
|
|
@@ -12156,7 +12227,7 @@ const ElementContextMenu = ({ children: n, element: _ }) => {
|
|
|
12156
12227
|
zIndex: 50,
|
|
12157
12228
|
boxShadow: "0 0 0 2px white"
|
|
12158
12229
|
},
|
|
12159
|
-
onMouseDown:
|
|
12230
|
+
onMouseDown: iM,
|
|
12160
12231
|
children: /* @__PURE__ */ jsx(p, { style: {
|
|
12161
12232
|
position: "absolute",
|
|
12162
12233
|
top: 12,
|
|
@@ -17364,29 +17435,40 @@ var gestureAnimations = {
|
|
|
17364
17435
|
const processLayout = (n, _) => {
|
|
17365
17436
|
let E = n.map((n) => ({ ...n })), O = new Map(n.map((n) => [n.id, n])), A = [];
|
|
17366
17437
|
E.forEach((n) => {
|
|
17367
|
-
|
|
17438
|
+
let j = n.type === "text", M = n.type === "text-container";
|
|
17439
|
+
if ((j || M) && n.autoGrow) {
|
|
17368
17440
|
let j = n.content;
|
|
17369
17441
|
_ && (j = j.replace(/\{\{(.*?)\}\}/g, (E, O) => {
|
|
17370
17442
|
let A = _[O.trim()];
|
|
17371
17443
|
return A == null ? E : n.formatting ? formatValue(A, n.formatting) : String(A);
|
|
17372
17444
|
}));
|
|
17373
|
-
let
|
|
17374
|
-
if (
|
|
17375
|
-
|
|
17376
|
-
|
|
17377
|
-
|
|
17378
|
-
|
|
17379
|
-
|
|
17380
|
-
|
|
17381
|
-
|
|
17382
|
-
let
|
|
17383
|
-
|
|
17384
|
-
n.height
|
|
17385
|
-
|
|
17386
|
-
|
|
17387
|
-
|
|
17388
|
-
|
|
17389
|
-
|
|
17445
|
+
let N = parseInt(String(n.style?.fontSize || 16)), P = String(n.style?.fontFamily || "Arial");
|
|
17446
|
+
if (M && n.containerExpansion === "horizontal") {
|
|
17447
|
+
let _ = document.createElement("canvas").getContext("2d");
|
|
17448
|
+
if (_) {
|
|
17449
|
+
_.font = `${N}px ${P}`;
|
|
17450
|
+
let E = _.measureText(j), O = Math.ceil(E.width + parseInt(String(n.style?.padding || 0)) * 2);
|
|
17451
|
+
O > n.width && (n.width = O, n.content = j);
|
|
17452
|
+
}
|
|
17453
|
+
} else {
|
|
17454
|
+
let _ = measureTextHeight(j, n.width, P, N), M = n.height, z = _ - M;
|
|
17455
|
+
if (z > 0) {
|
|
17456
|
+
n.height = _, n.content = j;
|
|
17457
|
+
let N = [], P = O.get(n.id);
|
|
17458
|
+
P && E.forEach((_) => {
|
|
17459
|
+
if (_.id === n.id) return;
|
|
17460
|
+
let E = O.get(_.id);
|
|
17461
|
+
E && isInside(P, E) && N.push(_);
|
|
17462
|
+
});
|
|
17463
|
+
let B = new Set([n.id]);
|
|
17464
|
+
N.forEach((n) => {
|
|
17465
|
+
n.height += z, B.add(n.id);
|
|
17466
|
+
}), A.push({
|
|
17467
|
+
triggerY: n.y + M,
|
|
17468
|
+
delta: z,
|
|
17469
|
+
ignoreIds: B
|
|
17470
|
+
});
|
|
17471
|
+
}
|
|
17390
17472
|
}
|
|
17391
17473
|
}
|
|
17392
17474
|
}), E.forEach((n) => {
|
|
@@ -18056,6 +18138,10 @@ var LayersPanel = ({ onOpenSettings: n }) => {
|
|
|
18056
18138
|
/* @__PURE__ */ jsx(v$3, {
|
|
18057
18139
|
onSelect: () => Xj("box"),
|
|
18058
18140
|
children: "Caixa (Container)"
|
|
18141
|
+
}),
|
|
18142
|
+
/* @__PURE__ */ jsx(v$3, {
|
|
18143
|
+
onSelect: () => Xj("text-container"),
|
|
18144
|
+
children: "Container com Texto"
|
|
18059
18145
|
})
|
|
18060
18146
|
]
|
|
18061
18147
|
})] }),
|
|
@@ -18298,5 +18384,5 @@ const GenericEditor = (n) => /* @__PURE__ */ jsx(EditorProvider, {
|
|
|
18298
18384
|
availableProps: n.layout.props,
|
|
18299
18385
|
theme: n.theme,
|
|
18300
18386
|
children: /* @__PURE__ */ jsx(EditorContent, { ...n })
|
|
18301
|
-
}), generateHTML = (n, _, E = {}) => Function("elements", "data", "options", getRendererCode() + "\nreturn renderTemplate(elements, data, options);")(n, _, E), getRendererCode = () => "\n/**\n * Render Template\n * @param {Array} elements - The JSON configuration of elements\n * @param {Object|Array} data - The data object to inject (Object for single, Array for list)\n * @param {Object} options - { isList: boolean, listSettings: { sortProp: string, sortOrder: 'asc'|'desc', newestPosition: 'top'|'bottom', scrollDirection: 'up'|'down', containerHeight: number }, canvasHeight: number }\n * @returns {string} - The generated HTML string\n */\nfunction renderTemplate(elements, data, options = {}) {\n const { isList, listSettings, canvasHeight } = options;\n\n const measureTextHeight = (text, width, fontFamily, fontSize, lineHeightMultiplier = 1.2) => {\n if (!text) return 0;\n try {\n const canvas = document.createElement('canvas');\n const context = canvas.getContext('2d');\n if (!context) return 0;\n context.font = `${fontSize}px ${fontFamily}`;\n const words = String(text).split(' ');\n let line = '';\n let lineCount = 1;\n for (let i = 0; i < words.length; i++) {\n const testLine = line + words[i] + ' ';\n const metrics = context.measureText(testLine);\n const testWidth = metrics.width;\n if (testWidth > width && i > 0) {\n line = words[i] + ' ';\n lineCount++;\n } else {\n line = testLine;\n }\n }\n const explicitLines = String(text).split('\\n').length - 1;\n lineCount += explicitLines;\n return Math.ceil(lineCount * fontSize * lineHeightMultiplier);\n } catch (_) {\n return 0;\n }\n };\n\n const computeLayout = (elements, itemData) => {\n const layoutElements = JSON.parse(JSON.stringify(elements));\n \n const isInside = (inner, outer) => {\n const eps = 0.1;\n return (\n inner.x >= outer.x - eps &&\n inner.x + inner.width <= outer.x + outer.width + eps &&\n inner.y >= outer.y - eps &&\n inner.y + inner.height <= outer.y + outer.height + eps\n );\n };\n\n const autoGrowTexts = layoutElements\n .filter(el => el.type === 'text' && el.autoGrow)\n .sort((a, b) => a.y - b.y);\n\n autoGrowTexts.forEach(textEl => {\n let content = textEl.content;\n content = content.replace(/\\{\\{(.*?)\\}\\}/g, (match, key) => {\n const val = itemData[key.trim()];\n return val !== undefined && val !== null ? String(val) : match;\n });\n \n const fontSize = parseInt(String((textEl.style && textEl.style.fontSize) || 16));\n const fontFamily = String((textEl.style && textEl.style.fontFamily) || 'Arial');\n const measuredHeight = measureTextHeight(content, textEl.width, fontFamily, fontSize);\n \n const designHeight = textEl.height;\n const delta = measuredHeight - designHeight;\n \n if (delta > 0) {\n const originalBottom = textEl.y + designHeight;\n const originalTextRect = {\n x: textEl.x,\n y: textEl.y,\n width: textEl.width,\n height: designHeight\n };\n \n textEl.height = measuredHeight;\n \n layoutElements.forEach(other => {\n if (other.id === textEl.id) return;\n \n if (isInside(originalTextRect, other)) {\n other.height += delta;\n }\n \n if (other.y >= originalBottom) {\n other.y += delta;\n }\n });\n }\n });\n \n let maxY = 0;\n layoutElements.forEach(el => {\n const bottom = el.y + el.height;\n if (bottom > maxY) maxY = bottom;\n });\n \n return { layoutElements, maxY };\n };\n\n const computeItemHeight = (elements, itemData, fallbackHeight) => {\n const { maxY } = computeLayout(elements, itemData);\n return fallbackHeight ? Math.max(maxY, fallbackHeight) : maxY;\n };\n\n const formatValue = (value, formatting) => {\n if (!formatting || formatting.type === 'text') return value !== undefined && value !== null ? String(value) : '';\n if (value === undefined || value === null) return '';\n\n if (formatting.type === 'boolean') {\n const isTrue = String(value) === 'true' || value === true || (typeof value === 'number' && value > 0);\n return isTrue ? (formatting.trueLabel || 'Sim') : (formatting.falseLabel || 'Não');\n }\n\n if (formatting.type === 'date') {\n try {\n const date = new Date(value);\n if (isNaN(date.getTime())) return String(value);\n \n if (formatting.dateFormat) {\n const d = date.getDate().toString().padStart(2, '0');\n const m = (date.getMonth() + 1).toString().padStart(2, '0');\n const y = date.getFullYear();\n const H = date.getHours().toString().padStart(2, '0');\n const M = date.getMinutes().toString().padStart(2, '0');\n const S = date.getSeconds().toString().padStart(2, '0');\n \n return formatting.dateFormat\n .replace('DD', d)\n .replace('MM', m)\n .replace('YYYY', String(y))\n .replace('HH', H)\n .replace('mm', M)\n .replace('ss', S);\n }\n return date.toLocaleDateString();\n } catch (e) { return String(value); }\n }\n\n if (formatting.type === 'number') {\n const num = parseFloat(value);\n if (isNaN(num)) return String(value);\n \n if (formatting.numberFormat === 'currency') {\n return (formatting.currencySymbol || 'R$') + ' ' + num.toFixed(formatting.decimalPlaces || 2);\n }\n if (formatting.numberFormat === 'percent') {\n return num.toFixed(formatting.decimalPlaces || 0) + '%';\n }\n if (formatting.decimalPlaces !== undefined) {\n return num.toFixed(formatting.decimalPlaces);\n }\n return num.toFixed(formatting.decimalPlaces || 0);\n }\n \n return String(value);\n };\n\n const checkCondition = (propValue, operator, ruleValue) => {\n const val = String(propValue).toLowerCase();\n const target = String(ruleValue).toLowerCase();\n \n switch (operator) {\n case 'equals': return val === target;\n case 'notEquals': return val !== target;\n case 'contains': return val.includes(target);\n case 'greaterThan': return parseFloat(val) > parseFloat(target);\n case 'lessThan': return parseFloat(val) < parseFloat(target);\n case 'truthy': return !!propValue;\n case 'falsy': return !propValue;\n default: return false;\n }\n };\n\n const camelToKebab = (string) => {\n return string.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, '$1-$2').toLowerCase();\n };\n\n const hex8ToRgba = (hex) => {\n const m = /^#([0-9a-fA-F]{8})$/.exec(hex);\n if (!m) return hex;\n const h = m[1];\n const r = parseInt(h.slice(0, 2), 16);\n const g = parseInt(h.slice(2, 4), 16);\n const b = parseInt(h.slice(4, 6), 16);\n const a = parseInt(h.slice(6, 8), 16) / 255;\n return `rgba(${r}, ${g}, ${b}, ${a})`;\n };\n\n const styleObjectToString = (style) => {\n if (!style) return '';\n const pxProps = [\n 'width', 'height', 'top', 'left', 'right', 'bottom', \n 'fontSize', 'borderRadius', 'padding', 'margin', 'borderWidth',\n 'borderTopLeftRadius', 'borderTopRightRadius', 'borderBottomRightRadius', 'borderBottomLeftRadius'\n ];\n \n return Object.entries(style)\n .map(([key, value]) => {\n if (value === undefined || value === null) return '';\n const cssKey = camelToKebab(key);\n let cssValue = (typeof value === 'number' && pxProps.includes(key)) ? value + 'px' : value;\n if (typeof cssValue === 'string') {\n if (/^#([0-9a-fA-F]{8})$/.test(cssValue)) {\n cssValue = hex8ToRgba(cssValue);\n }\n }\n return `${cssKey}: ${cssValue}`;\n })\n .filter(Boolean)\n .join('; ');\n };\n\n const getAnimationStyles = (anim) => {\n if (!anim || anim.type === 'none') return {};\n return {\n animationName: anim.type,\n animationDuration: (anim.duration || 1) + 's',\n animationDelay: (anim.delay || 0) + 's',\n animationIterationCount: anim.iterationCount || 1,\n animationTimingFunction: anim.timingFunction || 'ease',\n animationFillMode: 'both'\n };\n };\n\n const keyframesCss = `\n @keyframes slideIn {\n from { opacity: 0; transform: translateY(20px); }\n to { opacity: 1; transform: translateY(0); }\n }\n @keyframes fadeIn { \n from { opacity: 0; } \n to { opacity: 1; } \n }\n @keyframes slideInLeft { \n from { opacity: 0; transform: translateX(-50px); } \n to { opacity: 1; transform: translateX(0); } \n }\n @keyframes slideInRight { \n from { opacity: 0; transform: translateX(50px); } \n to { opacity: 1; transform: translateX(0); } \n }\n @keyframes slideInUp { \n from { opacity: 0; transform: translateY(50px); } \n to { opacity: 1; transform: translateY(0); } \n }\n @keyframes slideInDown { \n from { opacity: 0; transform: translateY(-50px); } \n to { opacity: 1; transform: translateY(0); } \n }\n @keyframes zoomIn { \n from { opacity: 0; transform: scale(0.5); } \n to { opacity: 1; transform: scale(1); } \n }\n @keyframes bounceIn {\n 0% { opacity: 0; transform: scale(0.3); }\n 50% { opacity: 1; transform: scale(1.05); }\n 70% { transform: scale(0.9); }\n 100% { transform: scale(1); }\n }\n @keyframes pulse {\n 0% { transform: scale(1); }\n 50% { transform: scale(1.05); }\n 100% { transform: scale(1); }\n }\n @keyframes shake {\n 0%, 100% { transform: translateX(0); }\n 10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); }\n 20%, 40%, 60%, 80% { transform: translateX(5px); }\n }\n @keyframes spin { \n from { transform: rotate(0deg); } \n to { transform: rotate(360deg); } \n }\n \n /* Improved / Smoother Animations */\n @keyframes smoothSlideUp {\n 0% { opacity: 0; transform: translateY(30px); }\n 100% { opacity: 1; transform: translateY(0); }\n }\n @keyframes popIn {\n 0% { opacity: 0; transform: scale(0.8) translateY(10px); }\n 100% { opacity: 1; transform: scale(1) translateY(0); }\n }\n @keyframes blurIn {\n 0% { opacity: 0; filter: blur(10px); }\n 100% { opacity: 1; filter: blur(0); }\n }\n `;\n\n const renderItem = (itemData, index = 0, offsetY = 0) => {\n const { layoutElements } = computeLayout(elements, itemData);\n return layoutElements.map(element => {\n let content = element.content;\n let imgSrc = '';\n\n // Resolve Content & Formatting\n if (element.type === 'text') {\n content = content.replace(/\\{\\{(.*?)\\}\\}/g, (match, key) => {\n const val = itemData[key.trim()];\n if (val === undefined || val === null) return match;\n if (element.formatting) {\n return formatValue(val, element.formatting);\n }\n return String(val);\n });\n } else if (element.type === 'image') {\n if (element.dataBinding) {\n const val = itemData[element.dataBinding];\n if (val !== undefined && val !== null) {\n imgSrc = String(val);\n } else {\n imgSrc = content;\n }\n } else {\n imgSrc = content.replace(/\\{\\{(.*?)\\}\\}/g, (match, key) => {\n const val = itemData[key.trim()];\n return val !== undefined && val !== null ? String(val) : match;\n });\n }\n }\n\n // Resolve Conditional Styles\n let conditionalStyles = {};\n if (element.conditions) {\n element.conditions.forEach(rule => {\n const propVal = itemData[rule.property];\n if (checkCondition(propVal, rule.operator, rule.value)) {\n conditionalStyles = { ...conditionalStyles, ...rule.style };\n }\n });\n }\n\n // Resolve Style Bindings\n let bindingStyles = {};\n if (element.styleBindings) {\n Object.entries(element.styleBindings).forEach(([styleProp, variableName]) => {\n const val = itemData[variableName];\n if (val !== undefined && val !== null) {\n bindingStyles[styleProp] = String(val);\n }\n });\n }\n\n const baseStyle = {\n position: 'absolute',\n left: element.x,\n top: element.y + offsetY,\n width: element.width,\n height: element.autoGrow ? 'auto' : element.height,\n transform: element.rotation ? `rotate(${element.rotation}deg)` : undefined,\n overflow: element.autoGrow ? 'visible' : 'hidden',\n whiteSpace: element.autoGrow ? 'pre-wrap' : undefined,\n wordBreak: element.autoGrow ? 'break-word' : undefined,\n ...element.style,\n ...conditionalStyles,\n ...bindingStyles\n };\n \n // Fix: remove padding if it's not explicitly set, or handle it for text\n if (element.type === 'text' && !baseStyle.padding) {\n // baseStyle.padding = '8px'; // Removed default padding to respect resize box\n }\n \n const styleString = styleObjectToString(baseStyle);\n\n if (element.type === 'text') {\n return `<div style=\"${styleString}\">${content}</div>`;\n } else if (element.type === 'image') {\n const imgStyle = styleObjectToString({\n width: '100%',\n height: '100%',\n objectFit: element.style?.objectFit || 'cover',\n display: 'block'\n });\n return `<div style=\"${styleString}\"><img src=\"${imgSrc}\" alt=\"Element\" style=\"${imgStyle}\" /></div>`;\n } else if (element.type === 'box') {\n return `<div style=\"${styleString}\"></div>`;\n }\n return '';\n }).join('\\n');\n };\n\n if (isList && Array.isArray(data)) {\n // Calculate per-item height respecting autoGrow\n // Sort data\n let listData = [...data];\n if (listSettings && listSettings.sortProp) {\n const prop = listSettings.sortProp;\n const order = listSettings.sortOrder === 'asc' ? 1 : -1;\n listData.sort((a, b) => {\n const valA = a[prop];\n const valB = b[prop];\n if (valA < valB) return -1 * order;\n if (valA > valB) return 1 * order;\n return 0;\n });\n }\n \n // Handle newest position\n if (listSettings && listSettings.newestPosition === 'top') {\n listData.reverse();\n }\n\n // Generate HTML for all items\n const itemsHtml = listData.map((item, index) => {\n const itemHtml = renderItem(item, index, 0); \n const itemHeight = computeItemHeight(elements, item, canvasHeight);\n const itemContainerStyle = styleObjectToString({\n position: 'relative',\n height: itemHeight,\n width: '100%'\n });\n \n return `<div class=\"list-item\" style=\"${itemContainerStyle}\">${itemHtml}</div>`;\n }).join('\\n');\n\n // Animation Styles based on settings\n const scrollDirection = (listSettings && listSettings.scrollDirection) || 'down';\n const containerHeight = (listSettings && listSettings.containerHeight) ? listSettings.containerHeight + 'px' : '100%';\n \n const justify = (listSettings && listSettings.newestPosition === 'top') ? 'flex-start' : 'flex-end';\n\n // Entry Animation from settings\n const entryAnim = listSettings && listSettings.entryAnimation ? listSettings.entryAnimation : { type: 'slideIn', duration: 0.3, timingFunction: 'ease-out' };\n const entryAnimName = entryAnim.type === 'none' ? 'none' : entryAnim.type;\n const entryAnimDuration = entryAnim.duration + 's';\n const entryAnimTiming = entryAnim.timingFunction || 'ease-out';\n\n const animationCss = `\n ${keyframesCss}\n\n .list-wrapper {\n display: flex;\n flex-direction: column;\n justify-content: ${justify};\n height: ${containerHeight};\n width: 100%;\n overflow-y: auto;\n overflow-x: hidden;\n box-sizing: border-box;\n padding: 10px;\n }\n .list-item {\n flex-shrink: 0;\n animation: ${entryAnimName} ${entryAnimDuration} ${entryAnimTiming};\n margin-bottom: 10px;\n width: 100%;\n position: relative;\n }\n `;\n \n const scrollScript = scrollDirection === 'up' \n ? `<script>\n document.addEventListener('DOMContentLoaded', () => {\n const wrapper = document.querySelector('.list-wrapper');\n if(wrapper) wrapper.scrollTop = wrapper.scrollHeight;\n });\n <\/script>`\n : '';\n\n // Inject Smart Script for Dynamic Updates\n const injectionScript = `\n <script>\n (function() {\n try {\n const elements = ${JSON.stringify(elements)};\n const formatValue = ${formatValue.toString()};\n const checkCondition = ${checkCondition.toString()};\n const camelToKebab = ${camelToKebab.toString()};\n const hex8ToRgba = ${hex8ToRgba.toString()};\n const styleObjectToString = ${styleObjectToString.toString()};\n const getAnimationStyles = ${getAnimationStyles.toString()};\n const renderItem = ${renderItem.toString()};\n\n const measureTextHeight = ${measureTextHeight.toString()};\n const computeLayout = ${computeLayout.toString()};\n const computeItemHeight = ${computeItemHeight.toString()};\n const itemHeightFallback = ${canvasHeight || 0};\n const newestPosition = \"${(listSettings && listSettings.newestPosition) || 'bottom'}\";\n const scrollDirection = \"${(listSettings && listSettings.scrollDirection) || 'down'}\";\n\n window.addItem = function(data) {\n const wrapper = document.querySelector('.list-wrapper');\n if (!wrapper) return;\n\n const itemHtml = renderItem(data, 0, 0);\n const itemHeight = computeItemHeight(elements, data, itemHeightFallback);\n const itemContainerStyle = styleObjectToString({\n position: 'relative',\n height: itemHeight,\n width: '100%'\n });\n\n const div = document.createElement('div');\n div.className = 'list-item';\n div.setAttribute('style', itemContainerStyle);\n div.innerHTML = itemHtml;\n\n if (newestPosition === 'top') {\n wrapper.insertBefore(div, wrapper.firstChild);\n } else {\n wrapper.appendChild(div);\n }\n \n if (scrollDirection === 'up') {\n wrapper.scrollTop = wrapper.scrollHeight;\n }\n };\n } catch(e) { console.error(\"Smart List Init Error\", e); }\n })();\n <\/script>\n `;\n\n return `\n <style>${animationCss}</style>\n <div class=\"list-wrapper\">\n ${itemsHtml}\n </div>\n ${scrollScript}\n ${injectionScript}\n `;\n }\n\n // Single Item\n const contentHtml = renderItem(data);\n return `<div style=\"position: relative; width: 100%; height: 100%; overflow: hidden;\">${contentHtml}</div>`;\n}\n";
|
|
18387
|
+
}), generateHTML = (n, _, E = {}) => Function("elements", "data", "options", getRendererCode() + "\nreturn renderTemplate(elements, data, options);")(n, _, E), getRendererCode = () => "\n/**\n * Render Template\n * @param {Array} elements - The JSON configuration of elements\n * @param {Object|Array} data - The data object to inject (Object for single, Array for list)\n * @param {Object} options - { isList: boolean, listSettings: { sortProp: string, sortOrder: 'asc'|'desc', newestPosition: 'top'|'bottom', scrollDirection: 'up'|'down', containerHeight: number }, canvasHeight: number }\n * @returns {string} - The generated HTML string\n */\nfunction renderTemplate(elements, data, options = {}) {\n const { isList, listSettings, canvasHeight } = options;\n\n const measureTextHeight = (text, width, fontFamily, fontSize, lineHeightMultiplier = 1.2) => {\n if (!text) return 0;\n try {\n const canvas = document.createElement('canvas');\n const context = canvas.getContext('2d');\n if (!context) return 0;\n context.font = `${fontSize}px ${fontFamily}`;\n const words = String(text).split(' ');\n let line = '';\n let lineCount = 1;\n for (let i = 0; i < words.length; i++) {\n const testLine = line + words[i] + ' ';\n const metrics = context.measureText(testLine);\n const testWidth = metrics.width;\n if (testWidth > width && i > 0) {\n line = words[i] + ' ';\n lineCount++;\n } else {\n line = testLine;\n }\n }\n const explicitLines = String(text).split('\\n').length - 1;\n lineCount += explicitLines;\n return Math.ceil(lineCount * fontSize * lineHeightMultiplier);\n } catch (_) {\n return 0;\n }\n };\n\n const computeLayout = (elements, itemData) => {\n const layoutElements = JSON.parse(JSON.stringify(elements));\n \n const isInside = (inner, outer) => {\n const eps = 0.1;\n return (\n inner.x >= outer.x - eps &&\n inner.x + inner.width <= outer.x + outer.width + eps &&\n inner.y >= outer.y - eps &&\n inner.y + inner.height <= outer.y + outer.height + eps\n );\n };\n\n const autoGrowElements = layoutElements\n .filter(el => (el.type === 'text' || el.type === 'text-container') && el.autoGrow)\n .sort((a, b) => a.y - b.y);\n\n autoGrowElements.forEach(textEl => {\n let content = textEl.content;\n content = content.replace(/\\{\\{(.*?)\\}\\}/g, (match, key) => {\n const val = itemData[key.trim()];\n return val !== undefined && val !== null ? String(val) : match;\n });\n \n const fontSize = parseInt(String((textEl.style && textEl.style.fontSize) || 16));\n const fontFamily = String((textEl.style && textEl.style.fontFamily) || 'Arial');\n \n const isHorizontal = textEl.type === 'text-container' && textEl.containerExpansion === 'horizontal';\n \n if (isHorizontal) {\n // Horizontal expansion: Update width only\n // Requires canvas context which is available in measureTextHeight scope or we create new one\n // For simplicity, we can't easily access the measure logic here if it's not exposed, \n // but measureTextHeight is available in this scope.\n // However measureTextHeight calculates HEIGHT. We need WIDTH.\n \n try {\n const canvas = document.createElement('canvas');\n const context = canvas.getContext('2d');\n if (context) {\n context.font = `${fontSize}px ${fontFamily}`;\n const metrics = context.measureText(content);\n const padding = parseInt(String((textEl.style && textEl.style.padding) || 0)) * 2;\n const newWidth = Math.ceil(metrics.width + padding);\n if (newWidth > textEl.width) {\n textEl.width = newWidth;\n }\n }\n } catch(e) {}\n } else {\n // Vertical Expansion\n const measuredHeight = measureTextHeight(content, textEl.width, fontFamily, fontSize);\n const designHeight = textEl.height;\n const delta = measuredHeight - designHeight;\n \n if (delta > 0) {\n const originalBottom = textEl.y + designHeight;\n const originalTextRect = {\n x: textEl.x,\n y: textEl.y,\n width: textEl.width,\n height: designHeight\n };\n \n textEl.height = measuredHeight;\n \n layoutElements.forEach(other => {\n if (other.id === textEl.id) return;\n \n if (isInside(originalTextRect, other)) {\n other.height += delta;\n }\n \n if (other.y >= originalBottom) {\n other.y += delta;\n }\n });\n }\n }\n });\n \n let maxY = 0;\n layoutElements.forEach(el => {\n const bottom = el.y + el.height;\n if (bottom > maxY) maxY = bottom;\n });\n \n return { layoutElements, maxY };\n };\n\n const computeItemHeight = (elements, itemData, fallbackHeight) => {\n const { maxY } = computeLayout(elements, itemData);\n return fallbackHeight ? Math.max(maxY, fallbackHeight) : maxY;\n };\n\n const formatValue = (value, formatting) => {\n if (!formatting || formatting.type === 'text') return value !== undefined && value !== null ? String(value) : '';\n if (value === undefined || value === null) return '';\n\n if (formatting.type === 'boolean') {\n const isTrue = String(value) === 'true' || value === true || (typeof value === 'number' && value > 0);\n return isTrue ? (formatting.trueLabel || 'Sim') : (formatting.falseLabel || 'Não');\n }\n\n if (formatting.type === 'date') {\n try {\n const date = new Date(value);\n if (isNaN(date.getTime())) return String(value);\n \n if (formatting.dateFormat) {\n const d = date.getDate().toString().padStart(2, '0');\n const m = (date.getMonth() + 1).toString().padStart(2, '0');\n const y = date.getFullYear();\n const H = date.getHours().toString().padStart(2, '0');\n const M = date.getMinutes().toString().padStart(2, '0');\n const S = date.getSeconds().toString().padStart(2, '0');\n \n return formatting.dateFormat\n .replace('DD', d)\n .replace('MM', m)\n .replace('YYYY', String(y))\n .replace('HH', H)\n .replace('mm', M)\n .replace('ss', S);\n }\n return date.toLocaleDateString();\n } catch (e) { return String(value); }\n }\n\n if (formatting.type === 'number') {\n const num = parseFloat(value);\n if (isNaN(num)) return String(value);\n \n if (formatting.numberFormat === 'currency') {\n return (formatting.currencySymbol || 'R$') + ' ' + num.toFixed(formatting.decimalPlaces || 2);\n }\n if (formatting.numberFormat === 'percent') {\n return num.toFixed(formatting.decimalPlaces || 0) + '%';\n }\n if (formatting.decimalPlaces !== undefined) {\n return num.toFixed(formatting.decimalPlaces);\n }\n return num.toFixed(formatting.decimalPlaces || 0);\n }\n \n return String(value);\n };\n\n const checkCondition = (propValue, operator, ruleValue) => {\n const val = String(propValue).toLowerCase();\n const target = String(ruleValue).toLowerCase();\n \n switch (operator) {\n case 'equals': return val === target;\n case 'notEquals': return val !== target;\n case 'contains': return val.includes(target);\n case 'greaterThan': return parseFloat(val) > parseFloat(target);\n case 'lessThan': return parseFloat(val) < parseFloat(target);\n case 'truthy': return !!propValue;\n case 'falsy': return !propValue;\n default: return false;\n }\n };\n\n const camelToKebab = (string) => {\n return string.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, '$1-$2').toLowerCase();\n };\n\n const hex8ToRgba = (hex) => {\n const m = /^#([0-9a-fA-F]{8})$/.exec(hex);\n if (!m) return hex;\n const h = m[1];\n const r = parseInt(h.slice(0, 2), 16);\n const g = parseInt(h.slice(2, 4), 16);\n const b = parseInt(h.slice(4, 6), 16);\n const a = parseInt(h.slice(6, 8), 16) / 255;\n return `rgba(${r}, ${g}, ${b}, ${a})`;\n };\n\n const styleObjectToString = (style) => {\n if (!style) return '';\n const pxProps = [\n 'width', 'height', 'top', 'left', 'right', 'bottom', \n 'fontSize', 'borderRadius', 'padding', 'margin', 'borderWidth',\n 'borderTopLeftRadius', 'borderTopRightRadius', 'borderBottomRightRadius', 'borderBottomLeftRadius'\n ];\n \n return Object.entries(style)\n .map(([key, value]) => {\n if (value === undefined || value === null) return '';\n const cssKey = camelToKebab(key);\n let cssValue = (typeof value === 'number' && pxProps.includes(key)) ? value + 'px' : value;\n if (typeof cssValue === 'string') {\n if (/^#([0-9a-fA-F]{8})$/.test(cssValue)) {\n cssValue = hex8ToRgba(cssValue);\n }\n }\n return `${cssKey}: ${cssValue}`;\n })\n .filter(Boolean)\n .join('; ');\n };\n\n const getAnimationStyles = (anim) => {\n if (!anim || anim.type === 'none') return {};\n return {\n animationName: anim.type,\n animationDuration: (anim.duration || 1) + 's',\n animationDelay: (anim.delay || 0) + 's',\n animationIterationCount: anim.iterationCount || 1,\n animationTimingFunction: anim.timingFunction || 'ease',\n animationFillMode: 'both'\n };\n };\n\n const keyframesCss = `\n @keyframes slideIn {\n from { opacity: 0; transform: translateY(20px); }\n to { opacity: 1; transform: translateY(0); }\n }\n @keyframes fadeIn { \n from { opacity: 0; } \n to { opacity: 1; } \n }\n @keyframes slideInLeft { \n from { opacity: 0; transform: translateX(-50px); } \n to { opacity: 1; transform: translateX(0); } \n }\n @keyframes slideInRight { \n from { opacity: 0; transform: translateX(50px); } \n to { opacity: 1; transform: translateX(0); } \n }\n @keyframes slideInUp { \n from { opacity: 0; transform: translateY(50px); } \n to { opacity: 1; transform: translateY(0); } \n }\n @keyframes slideInDown { \n from { opacity: 0; transform: translateY(-50px); } \n to { opacity: 1; transform: translateY(0); } \n }\n @keyframes zoomIn { \n from { opacity: 0; transform: scale(0.5); } \n to { opacity: 1; transform: scale(1); } \n }\n @keyframes bounceIn {\n 0% { opacity: 0; transform: scale(0.3); }\n 50% { opacity: 1; transform: scale(1.05); }\n 70% { transform: scale(0.9); }\n 100% { transform: scale(1); }\n }\n @keyframes pulse {\n 0% { transform: scale(1); }\n 50% { transform: scale(1.05); }\n 100% { transform: scale(1); }\n }\n @keyframes shake {\n 0%, 100% { transform: translateX(0); }\n 10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); }\n 20%, 40%, 60%, 80% { transform: translateX(5px); }\n }\n @keyframes spin { \n from { transform: rotate(0deg); } \n to { transform: rotate(360deg); } \n }\n \n /* Improved / Smoother Animations */\n @keyframes smoothSlideUp {\n 0% { opacity: 0; transform: translateY(30px); }\n 100% { opacity: 1; transform: translateY(0); }\n }\n @keyframes popIn {\n 0% { opacity: 0; transform: scale(0.8) translateY(10px); }\n 100% { opacity: 1; transform: scale(1) translateY(0); }\n }\n @keyframes blurIn {\n 0% { opacity: 0; filter: blur(10px); }\n 100% { opacity: 1; filter: blur(0); }\n }\n `;\n\n const renderItem = (itemData, index = 0, offsetY = 0) => {\n const { layoutElements } = computeLayout(elements, itemData);\n return layoutElements.map(element => {\n let content = element.content;\n let imgSrc = '';\n\n // Resolve Content & Formatting\n if (element.type === 'text') {\n content = content.replace(/\\{\\{(.*?)\\}\\}/g, (match, key) => {\n const val = itemData[key.trim()];\n if (val === undefined || val === null) return match;\n if (element.formatting) {\n return formatValue(val, element.formatting);\n }\n return String(val);\n });\n } else if (element.type === 'image') {\n if (element.dataBinding) {\n const val = itemData[element.dataBinding];\n if (val !== undefined && val !== null) {\n imgSrc = String(val);\n } else {\n imgSrc = content;\n }\n } else {\n imgSrc = content.replace(/\\{\\{(.*?)\\}\\}/g, (match, key) => {\n const val = itemData[key.trim()];\n return val !== undefined && val !== null ? String(val) : match;\n });\n }\n }\n\n // Resolve Conditional Styles\n let conditionalStyles = {};\n if (element.conditions) {\n element.conditions.forEach(rule => {\n const propVal = itemData[rule.property];\n if (checkCondition(propVal, rule.operator, rule.value)) {\n conditionalStyles = { ...conditionalStyles, ...rule.style };\n }\n });\n }\n\n // Resolve Style Bindings\n let bindingStyles = {};\n if (element.styleBindings) {\n Object.entries(element.styleBindings).forEach(([styleProp, variableName]) => {\n const val = itemData[variableName];\n if (val !== undefined && val !== null) {\n bindingStyles[styleProp] = String(val);\n }\n });\n }\n\n const baseStyle = {\n position: 'absolute',\n left: element.x,\n top: element.y + offsetY,\n width: element.width,\n height: element.autoGrow ? 'auto' : element.height,\n transform: element.rotation ? `rotate(${element.rotation}deg)` : undefined,\n overflow: element.autoGrow ? 'visible' : 'hidden',\n whiteSpace: (element.type === 'text-container' && element.autoGrow && element.containerExpansion === 'horizontal') ? 'nowrap' : (element.autoGrow ? 'pre-wrap' : undefined),\n wordBreak: element.autoGrow ? 'break-word' : undefined,\n ...element.style,\n ...conditionalStyles,\n ...bindingStyles\n };\n \n // Fix: remove padding if it's not explicitly set, or handle it for text\n if (element.type === 'text' && !baseStyle.padding) {\n // baseStyle.padding = '8px'; // Removed default padding to respect resize box\n }\n \n const styleString = styleObjectToString(baseStyle);\n\n if (element.type === 'text') {\n return `<div style=\"${styleString}\">${content}</div>`;\n } else if (element.type === 'image') {\n const imgStyle = styleObjectToString({\n width: '100%',\n height: '100%',\n objectFit: element.style?.objectFit || 'cover',\n display: 'block'\n });\n return `<div style=\"${styleString}\"><img src=\"${imgSrc}\" alt=\"Element\" style=\"${imgStyle}\" /></div>`;\n } else if (element.type === 'box') {\n return `<div style=\"${styleString}\"></div>`;\n }\n return '';\n }).join('\\n');\n };\n\n if (isList && Array.isArray(data)) {\n // Calculate per-item height respecting autoGrow\n // Sort data\n let listData = [...data];\n if (listSettings && listSettings.sortProp) {\n const prop = listSettings.sortProp;\n const order = listSettings.sortOrder === 'asc' ? 1 : -1;\n listData.sort((a, b) => {\n const valA = a[prop];\n const valB = b[prop];\n if (valA < valB) return -1 * order;\n if (valA > valB) return 1 * order;\n return 0;\n });\n }\n \n // Handle newest position\n if (listSettings && listSettings.newestPosition === 'top') {\n listData.reverse();\n }\n\n // Generate HTML for all items\n const itemsHtml = listData.map((item, index) => {\n const itemHtml = renderItem(item, index, 0); \n const itemHeight = computeItemHeight(elements, item, canvasHeight);\n const itemContainerStyle = styleObjectToString({\n position: 'relative',\n height: itemHeight,\n width: '100%'\n });\n \n return `<div class=\"list-item\" style=\"${itemContainerStyle}\">${itemHtml}</div>`;\n }).join('\\n');\n\n // Animation Styles based on settings\n const scrollDirection = (listSettings && listSettings.scrollDirection) || 'down';\n const containerHeight = (listSettings && listSettings.containerHeight) ? listSettings.containerHeight + 'px' : '100%';\n \n const justify = (listSettings && listSettings.newestPosition === 'top') ? 'flex-start' : 'flex-end';\n\n // Entry Animation from settings\n const entryAnim = listSettings && listSettings.entryAnimation ? listSettings.entryAnimation : { type: 'slideIn', duration: 0.3, timingFunction: 'ease-out' };\n const entryAnimName = entryAnim.type === 'none' ? 'none' : entryAnim.type;\n const entryAnimDuration = entryAnim.duration + 's';\n const entryAnimTiming = entryAnim.timingFunction || 'ease-out';\n\n const animationCss = `\n ${keyframesCss}\n\n .list-wrapper {\n display: flex;\n flex-direction: column;\n justify-content: ${justify};\n height: ${containerHeight};\n width: 100%;\n overflow-y: auto;\n overflow-x: hidden;\n box-sizing: border-box;\n padding: 10px;\n }\n .list-item {\n flex-shrink: 0;\n animation: ${entryAnimName} ${entryAnimDuration} ${entryAnimTiming};\n margin-bottom: 10px;\n width: 100%;\n position: relative;\n }\n `;\n \n const scrollScript = scrollDirection === 'up' \n ? `<script>\n document.addEventListener('DOMContentLoaded', () => {\n const wrapper = document.querySelector('.list-wrapper');\n if(wrapper) wrapper.scrollTop = wrapper.scrollHeight;\n });\n <\/script>`\n : '';\n\n // Inject Smart Script for Dynamic Updates\n const injectionScript = `\n <script>\n (function() {\n try {\n const elements = ${JSON.stringify(elements)};\n const formatValue = ${formatValue.toString()};\n const checkCondition = ${checkCondition.toString()};\n const camelToKebab = ${camelToKebab.toString()};\n const hex8ToRgba = ${hex8ToRgba.toString()};\n const styleObjectToString = ${styleObjectToString.toString()};\n const getAnimationStyles = ${getAnimationStyles.toString()};\n const renderItem = ${renderItem.toString()};\n\n const measureTextHeight = ${measureTextHeight.toString()};\n const computeLayout = ${computeLayout.toString()};\n const computeItemHeight = ${computeItemHeight.toString()};\n const itemHeightFallback = ${canvasHeight || 0};\n const newestPosition = \"${(listSettings && listSettings.newestPosition) || 'bottom'}\";\n const scrollDirection = \"${(listSettings && listSettings.scrollDirection) || 'down'}\";\n\n window.addItem = function(data) {\n const wrapper = document.querySelector('.list-wrapper');\n if (!wrapper) return;\n\n const itemHtml = renderItem(data, 0, 0);\n const itemHeight = computeItemHeight(elements, data, itemHeightFallback);\n const itemContainerStyle = styleObjectToString({\n position: 'relative',\n height: itemHeight,\n width: '100%'\n });\n\n const div = document.createElement('div');\n div.className = 'list-item';\n div.setAttribute('style', itemContainerStyle);\n div.innerHTML = itemHtml;\n\n if (newestPosition === 'top') {\n wrapper.insertBefore(div, wrapper.firstChild);\n } else {\n wrapper.appendChild(div);\n }\n \n if (scrollDirection === 'up') {\n wrapper.scrollTop = wrapper.scrollHeight;\n }\n };\n } catch(e) { console.error(\"Smart List Init Error\", e); }\n })();\n <\/script>\n `;\n\n return `\n <style>${animationCss}</style>\n <div class=\"list-wrapper\">\n ${itemsHtml}\n </div>\n ${scrollScript}\n ${injectionScript}\n `;\n }\n\n // Single Item\n const contentHtml = renderItem(data);\n return `<div style=\"position: relative; width: 100%; height: 100%; overflow: hidden;\">${contentHtml}</div>`;\n}\n";
|
|
18302
18388
|
export { GenericEditor as EditorContent, generateHTML };
|