@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.
@@ -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
  }
@@ -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: [/* @__PURE__ */ jsxs(p$1, {
10473
- align: "center",
10474
- gap: "2",
10475
- mb: "3",
10476
- children: [/* @__PURE__ */ jsx(i, {
10477
- checked: n.autoGrow || !1,
10478
- onCheckedChange: (E) => _(n.id, { autoGrow: E })
10479
- }), /* @__PURE__ */ jsx(p$2, {
10480
- size: "2",
10481
- children: "Expandir altura automaticamente (Multilinha)"
10482
- })]
10483
- }), /* @__PURE__ */ jsx(o$2, {
10484
- size: "4",
10485
- mb: "3"
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
- }), G = useRef({}), Z = useRef(!1), Lj = useRef(!1), Rj = useRef(0), zj = useRef(0), Hj = useRef({
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
- }), Uj = useRef(!1), Wj = j.canvasHeight || 150, Gj = j.isList ? j.mockData.length > 0 ? j.mockData[0] : null : j.singleMockData, Jj = n.content, Yj = {};
11875
- if (Gj) {
11876
- if (n.type === "text") Jj = Jj.replace(/\{\{(.*?)\}\}/g, (_, E) => {
11877
- let O = Gj[E.trim()];
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 _ = Gj[n.dataBinding];
11882
- _ != null && (Jj = String(_));
11883
- } else Jj = Jj.replace(/\{\{(.*?)\}\}/g, (n, _) => {
11884
- let E = Gj[_.trim()];
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 = Gj[_];
11889
- E != null && (Yj = {
11890
- ...Yj,
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 _ = Gj[n.property];
11895
- checkCondition(_, n.operator, n.value) && (Yj = {
11896
- ...Yj,
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 Xj = Yj.display === "none";
11902
- if (Xj && _) {
11903
- let { display: n, ..._ } = Yj;
11904
- Yj = _;
11933
+ let $j = Qj.display === "none";
11934
+ if ($j && _) {
11935
+ let { display: n, ..._ } = Qj;
11936
+ Qj = _;
11905
11937
  }
11906
- let Zj = (_) => {
11907
- _.stopPropagation(), !Z.current && !Lj.current && !P && (_.shiftKey ? E(n.id, !0) : E(n.id, !1));
11908
- }, Qj = (O) => {
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 (Z.current = !1, Lj.current = !1, n.groupId && !_) {
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), Lj.current = !0), z(!0), U.current = {
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
- }), G.current = N;
11929
- }, $j = (_) => {
11930
- _.stopPropagation(), _.preventDefault(), H(!0);
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
- Hj.current = {
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 - Hj.current.x, j = _.clientY - Hj.current.y;
11939
- Rj.current = Math.atan2(j, A) * (180 / Math.PI), zj.current = n.rotation || 0;
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 (P) {
11945
- let E = j.zoom || 1, O = (_.clientX - U.current.x) / E, M = (_.clientY - U.current.y) / 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
- P && (z(!1), A([], !0), N && N([])), B && (H(!1), O(n.id, { rotation: n.rotation }, !0));
12032
+ U && (G(!1), O(n.id, { rotation: n.rotation }, !0));
11998
12033
  };
11999
- return (P || B) && (window.addEventListener("mousemove", _), window.addEventListener("mouseup", E)), () => {
12034
+ return U && (window.addEventListener("mousemove", _), window.addEventListener("mouseup", E)), () => {
12000
12035
  window.removeEventListener("mousemove", _), window.removeEventListener("mouseup", E);
12001
12036
  };
12002
12037
  }, [
12003
- P,
12004
- B,
12038
+ U,
12005
12039
  n.id,
12006
12040
  O,
12007
- A,
12008
- j.isList,
12009
- j.selectedElementIds
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 eM = {
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
- ...Yj
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, Wj - n.y) : void 0,
12085
+ maxHeight: j.isList ? Math.max(10, Yj - n.y) : void 0,
12037
12086
  onResizeStart: (n) => {
12038
- Uj.current = !!n.shiftKey;
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: Xj && !_ ? "none" : void 0,
12052
- opacity: Xj && _ ? .4 : 1
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: Uj.current === !0,
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
- style: eM,
12076
- onMouseDown: Qj,
12077
- onClick: Zj,
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: Jj
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" && (Jj ? /* @__PURE__ */ jsx("img", {
12094
- src: Jj,
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: $j,
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
- if (n.type === "text" && n.autoGrow) {
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 M = parseInt(String(n.style?.fontSize || 16)), N = String(n.style?.fontFamily || "Arial"), P = measureTextHeight(j, n.width, N, M), z = n.height, B = P - z;
17374
- if (B > 0) {
17375
- n.height = P, n.content = j;
17376
- let _ = [], M = O.get(n.id);
17377
- M && E.forEach((E) => {
17378
- if (E.id === n.id) return;
17379
- let A = O.get(E.id);
17380
- A && isInside(M, A) && _.push(E);
17381
- });
17382
- let N = new Set([n.id]);
17383
- _.forEach((n) => {
17384
- n.height += B, N.add(n.id);
17385
- }), A.push({
17386
- triggerY: n.y + z,
17387
- delta: B,
17388
- ignoreIds: N
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 };