@xwadex/fesd-next 0.3.4-7.21 → 0.3.4-7.23
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/components/content-builders/article4/article4cora/article4cora-roots.d.ts +4 -0
- package/dist/components/content-builders/article4/article4cora/article4cora-roots.js +79 -0
- package/dist/components/content-builders/article4/article4cora/media-base/index.d.ts +2 -0
- package/dist/components/content-builders/article4/article4cora/media-base/index.js +2 -0
- package/dist/components/content-builders/article4/article4cora/media-base/media-base-root.d.ts +15 -0
- package/dist/components/content-builders/article4/article4cora/media-base/media-base-root.js +52 -0
- package/dist/components/content-builders/article4/article4cora/media-cover/index.d.ts +2 -0
- package/dist/components/content-builders/article4/article4cora/media-cover/index.js +2 -0
- package/dist/components/content-builders/article4/article4cora/media-cover/media-cover-root.d.ts +15 -0
- package/dist/components/content-builders/article4/article4cora/media-cover/media-cover-root.js +23 -0
- package/dist/components/content-builders/article4/article4cora/media-embla/index.d.ts +2 -0
- package/dist/components/content-builders/article4/article4cora/media-embla/index.js +2 -0
- package/dist/components/content-builders/article4/article4cora/media-embla/media-embla-root.d.ts +25 -0
- package/dist/components/content-builders/article4/article4cora/media-embla/media-embla-root.js +107 -0
- package/dist/components/content-builders/article4/baseBlock.d.ts +17 -0
- package/dist/components/content-builders/article4/baseBlock.js +115 -0
- package/dist/components/content-builders/article4/commonComponents.d.ts +53 -0
- package/dist/components/content-builders/article4/commonComponents.js +81 -0
- package/dist/components/content-builders/article4/config.d.ts +4 -0
- package/dist/components/content-builders/article4/config.js +6 -0
- package/dist/components/content-builders/article4/index.d.ts +12 -0
- package/dist/components/content-builders/article4/index.js +12 -0
- package/dist/components/content-builders/article4/type10Block.d.ts +14 -0
- package/dist/components/content-builders/article4/type10Block.js +24 -0
- package/dist/components/content-builders/article4/type1Block.d.ts +9 -0
- package/dist/components/content-builders/article4/type1Block.js +12 -0
- package/dist/components/content-builders/article4/type2Block.d.ts +9 -0
- package/dist/components/content-builders/article4/type2Block.js +17 -0
- package/dist/components/content-builders/article4/type3Block.d.ts +8 -0
- package/dist/components/content-builders/article4/type3Block.js +12 -0
- package/dist/components/content-builders/article4/type4Block.d.ts +27 -0
- package/dist/components/content-builders/article4/type4Block.js +53 -0
- package/dist/components/content-builders/article4/type5Block.d.ts +24 -0
- package/dist/components/content-builders/article4/type5Block.js +43 -0
- package/dist/components/content-builders/article4/type6Block.d.ts +24 -0
- package/dist/components/content-builders/article4/type6Block.js +43 -0
- package/dist/components/content-builders/article4/type7Block.d.ts +12 -0
- package/dist/components/content-builders/article4/type7Block.js +19 -0
- package/dist/components/content-builders/article4/type8Block.d.ts +11 -0
- package/dist/components/content-builders/article4/type8Block.js +14 -0
- package/dist/components/content-builders/article4/type9Block.d.ts +11 -0
- package/dist/components/content-builders/article4/type9Block.js +19 -0
- package/dist/components/content-builders/blocks/base-block.d.ts +12 -0
- package/dist/components/content-builders/blocks/base-block.js +91 -0
- package/dist/components/content-builders/blocks/headers-block.d.ts +20 -0
- package/dist/components/content-builders/blocks/headers-block.js +49 -0
- package/dist/components/content-builders/blocks/index.d.ts +4 -0
- package/dist/components/content-builders/blocks/index.js +4 -0
- package/dist/components/content-builders/blocks/media-content.d.ts +7 -0
- package/dist/components/content-builders/blocks/media-content.js +23 -0
- package/dist/components/content-builders/blocks/root-header.d.ts +11 -0
- package/dist/components/content-builders/blocks/root-header.js +14 -0
- package/dist/components/content-builders/blocks/roots.d.ts +8 -0
- package/dist/components/content-builders/blocks/roots.js +23 -0
- package/dist/components/content-builders/blocks/text-content.d.ts +20 -0
- package/dist/components/content-builders/blocks/text-content.js +190 -0
- package/dist/components/content-builders/content-builder-actionbar.d.ts +1 -0
- package/dist/components/content-builders/content-builder-actionbar.js +64 -0
- package/dist/components/content-builders/content-builder-blocks-dialogs.d.ts +4 -0
- package/dist/components/content-builders/content-builder-blocks-dialogs.js +45 -0
- package/dist/components/content-builders/content-builder-blocks.d.ts +15 -0
- package/dist/components/content-builders/content-builder-blocks.js +40 -0
- package/dist/components/content-builders/content-builder-builder.d.ts +13 -0
- package/dist/components/content-builders/content-builder-builder.js +17 -0
- package/dist/components/content-builders/content-builder-configs.d.ts +203 -0
- package/dist/components/content-builders/content-builder-configs.js +125 -0
- package/dist/components/content-builders/content-builder-context.d.ts +24 -0
- package/dist/components/content-builders/content-builder-context.js +47 -0
- package/dist/components/content-builders/content-builder-fields.d.ts +1 -0
- package/dist/components/content-builders/content-builder-fields.js +5 -0
- package/dist/components/content-builders/content-builder-functionbar.d.ts +1 -0
- package/dist/components/content-builders/content-builder-functionbar.js +37 -0
- package/dist/components/content-builders/content-builder-helpers.d.ts +9 -0
- package/dist/components/content-builders/content-builder-helpers.js +43 -0
- package/dist/components/content-builders/content-builder-hooks.d.ts +39 -0
- package/dist/components/content-builders/content-builder-hooks.js +129 -0
- package/dist/components/content-builders/content-builder-iframe.d.ts +5 -0
- package/dist/components/content-builders/content-builder-iframe.js +15 -0
- package/dist/components/content-builders/content-builder-overlay.d.ts +1 -0
- package/dist/components/content-builders/content-builder-overlay.js +22 -0
- package/dist/components/content-builders/content-builder-panels.d.ts +10 -0
- package/dist/components/content-builders/content-builder-panels.js +165 -0
- package/dist/components/content-builders/content-builder-preview.d.ts +4 -0
- package/dist/components/content-builders/content-builder-preview.js +22 -0
- package/dist/components/content-builders/content-builder-pucks-context.d.ts +13 -0
- package/dist/components/content-builders/content-builder-pucks-context.js +17 -0
- package/dist/components/content-builders/content-builder-pucks-hooks.d.ts +17 -0
- package/dist/components/content-builders/content-builder-pucks-hooks.js +119 -0
- package/dist/components/content-builders/content-builder-render.d.ts +6 -0
- package/dist/components/content-builders/content-builder-render.js +6 -0
- package/dist/components/content-builders/content-builder.d.ts +7 -0
- package/dist/components/content-builders/content-builder.js +24 -0
- package/dist/components/content-builders/fields/fields-tabs.d.ts +5 -0
- package/dist/components/content-builders/fields/fields-tabs.js +9 -0
- package/dist/components/content-builders/index.d.ts +5 -0
- package/dist/components/content-builders/index.js +5 -0
- package/dist/components/content-builders/initial-datas.d.ts +56 -0
- package/dist/components/content-builders/initial-datas.js +818 -0
- package/dist/components/emblas/embla-container.d.ts +8 -0
- package/dist/components/emblas/embla-container.js +13 -0
- package/dist/components/emblas/embla-context.d.ts +21 -0
- package/dist/components/emblas/embla-context.js +13 -0
- package/dist/components/emblas/embla.d.ts +20 -0
- package/dist/components/emblas/embla.js +125 -0
- package/dist/components/emblas/emblas-pagination.d.ts +11 -0
- package/dist/components/emblas/emblas-pagination.js +90 -0
- package/dist/components/emblas/index.d.ts +4 -0
- package/dist/components/emblas/index.js +4 -0
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.js +2 -0
- package/package.json +1 -1
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
3
|
+
export function useBuilder({ builderProps }) {
|
|
4
|
+
const { config, defaultViewport = "1920" } = builderProps;
|
|
5
|
+
// const navigate = useNavigate()
|
|
6
|
+
const builderRef = useRef(null);
|
|
7
|
+
const resizeObserverRef = useRef(null);
|
|
8
|
+
const [viewport, setViewport] = useState({
|
|
9
|
+
view: defaultViewport, zoom: 1
|
|
10
|
+
});
|
|
11
|
+
const [blocksViews, setBlocksViews] = useState({ active: false });
|
|
12
|
+
const [panels, setPanels] = useState({
|
|
13
|
+
pageEditor: { x: 0, y: 0, z: 1, active: false },
|
|
14
|
+
contentEditor: { x: 0, y: 0, z: 1, active: false },
|
|
15
|
+
});
|
|
16
|
+
const setBuilderDragging = useCallback((state) => {
|
|
17
|
+
if (!builderRef?.current)
|
|
18
|
+
return;
|
|
19
|
+
builderRef.current.setAttribute("data-drag", JSON.stringify(state));
|
|
20
|
+
}, []);
|
|
21
|
+
const selectBuilderBlocks = useCallback(async () => {
|
|
22
|
+
setBuilderDragging(true);
|
|
23
|
+
return new Promise((resolve) => {
|
|
24
|
+
setBlocksViews({ active: true, callback: resolve });
|
|
25
|
+
});
|
|
26
|
+
}, []);
|
|
27
|
+
const setBuilderNewBlocks = useCallback((block) => {
|
|
28
|
+
setBuilderDragging(false);
|
|
29
|
+
blocksViews.callback?.(block);
|
|
30
|
+
requestAnimationFrame(() => {
|
|
31
|
+
setBlocksViews({ active: false });
|
|
32
|
+
});
|
|
33
|
+
}, [blocksViews]);
|
|
34
|
+
const setBuilderPanelsActive = useCallback((id, active) => {
|
|
35
|
+
if (!id)
|
|
36
|
+
return;
|
|
37
|
+
setPanels?.(prev => {
|
|
38
|
+
const next = ({ ...prev, [id]: { ...prev[id], active: active } });
|
|
39
|
+
return next;
|
|
40
|
+
});
|
|
41
|
+
}, []);
|
|
42
|
+
const setBuilderViewport = useCallback((viewport) => {
|
|
43
|
+
setViewport(prev => ({ ...prev, ...viewport }));
|
|
44
|
+
}, []);
|
|
45
|
+
const saveBuilderData = useCallback((data) => {
|
|
46
|
+
console.log("latest data", data);
|
|
47
|
+
// navigate({
|
|
48
|
+
// to: "/test/puck/preview",
|
|
49
|
+
// search: { datas: data },
|
|
50
|
+
// viewTransition: true
|
|
51
|
+
// })
|
|
52
|
+
}, []);
|
|
53
|
+
// const resizeObserver = useEffectEvent(([entry]: any) => {
|
|
54
|
+
// if (!entry) return
|
|
55
|
+
// console.log(entry);
|
|
56
|
+
// const scale = Math.min(1, entry.contentRect.width / 1920)
|
|
57
|
+
// if (entry.contentRect.width <= 1920) {
|
|
58
|
+
// console.log({ width: entry.contentRect.width, scale });
|
|
59
|
+
// }
|
|
60
|
+
// })
|
|
61
|
+
// const resizeScaleBuilder = useEffectEvent((active: boolean) => {
|
|
62
|
+
// if (!builderRef?.current) return
|
|
63
|
+
// if (active && !resizeObserverRef?.current) {
|
|
64
|
+
// resizeObserverRef.current = new ResizeObserver(resizeObserver)
|
|
65
|
+
// return resizeObserverRef.current.observe(builderRef?.current)
|
|
66
|
+
// }
|
|
67
|
+
// if (!active && resizeObserverRef?.current) {
|
|
68
|
+
// resizeObserverRef.current?.disconnect()
|
|
69
|
+
// resizeObserverRef.current = null
|
|
70
|
+
// }
|
|
71
|
+
// })
|
|
72
|
+
// useEffect(() => {
|
|
73
|
+
// resizeScaleBuilder(true)
|
|
74
|
+
// return () => resizeScaleBuilder(false)
|
|
75
|
+
// }, [builderRef?.current])
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
const onKeyDown = (e) => {
|
|
78
|
+
const key = e.key?.toLowerCase();
|
|
79
|
+
const isCmdOrCtrl = e.metaKey || e.ctrlKey;
|
|
80
|
+
// Puck 的 previewMode 切換熱鍵:cmd+i / ctrl+i
|
|
81
|
+
if (isCmdOrCtrl && key === "i") {
|
|
82
|
+
e.preventDefault();
|
|
83
|
+
e.stopPropagation();
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
window.addEventListener("keydown", onKeyDown, true); // <- capture
|
|
87
|
+
return () => window.removeEventListener("keydown", onKeyDown, true);
|
|
88
|
+
}, []);
|
|
89
|
+
return {
|
|
90
|
+
config,
|
|
91
|
+
builderRef,
|
|
92
|
+
viewport,
|
|
93
|
+
blocksViews,
|
|
94
|
+
panels,
|
|
95
|
+
selectBuilderBlocks,
|
|
96
|
+
setBuilderBlockView: setBlocksViews,
|
|
97
|
+
setBuilderPanels: setPanels,
|
|
98
|
+
setBuilderPanelsActive,
|
|
99
|
+
setBuilderViewport,
|
|
100
|
+
setBuilderDragging,
|
|
101
|
+
setBuilderNewBlocks,
|
|
102
|
+
saveBuilderData,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
export const useBuilderIframe = ({ iframeDocument, callback, isPreviewKeyDown, }) => {
|
|
106
|
+
const killPreviewKeyModeDown = (action) => {
|
|
107
|
+
if (!iframeDocument || !isPreviewKeyDown)
|
|
108
|
+
return;
|
|
109
|
+
if (action == "add")
|
|
110
|
+
iframeDocument.addEventListener("keydown", cancelPreviewModeKeyDown, true);
|
|
111
|
+
else
|
|
112
|
+
document.removeEventListener("keydown", cancelPreviewModeKeyDown, true);
|
|
113
|
+
};
|
|
114
|
+
useEffect(() => {
|
|
115
|
+
if (!iframeDocument)
|
|
116
|
+
return;
|
|
117
|
+
requestAnimationFrame(() => { callback?.(iframeDocument); });
|
|
118
|
+
killPreviewKeyModeDown("add");
|
|
119
|
+
return () => killPreviewKeyModeDown("remove");
|
|
120
|
+
}, [iframeDocument]);
|
|
121
|
+
};
|
|
122
|
+
function cancelPreviewModeKeyDown(e) {
|
|
123
|
+
const key = e.key?.toLowerCase();
|
|
124
|
+
const isCmdOrCtrl = e.metaKey || e.ctrlKey;
|
|
125
|
+
if (isCmdOrCtrl && key === "i") {
|
|
126
|
+
e.preventDefault();
|
|
127
|
+
e.stopPropagation();
|
|
128
|
+
}
|
|
129
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { memo } from "react";
|
|
4
|
+
import { useBuilderIframe } from "./content-builder-hooks";
|
|
5
|
+
export const BuilderIframe = memo(({ document, children }) => {
|
|
6
|
+
useBuilderIframe({
|
|
7
|
+
callback: (document) => {
|
|
8
|
+
if (document)
|
|
9
|
+
document.body.style.overflowY = "auto";
|
|
10
|
+
},
|
|
11
|
+
iframeDocument: document,
|
|
12
|
+
isPreviewKeyDown: true
|
|
13
|
+
});
|
|
14
|
+
return _jsx(_Fragment, { children: children });
|
|
15
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const BuilderComponentOverlay: import("react").MemoExoticComponent<({ children, componentType, isSelected }: any) => import("react/jsx-runtime").JSX.Element>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { memo, useCallback } from "react";
|
|
4
|
+
import { useBuilderContext } from "./content-builder-context";
|
|
5
|
+
import { Plus } from "lucide-react";
|
|
6
|
+
import { useBuilderPucksContext } from "./content-builder-pucks-context";
|
|
7
|
+
import { cn } from "../../shadcns/index.js";
|
|
8
|
+
export const BuilderComponentOverlay = memo(({ children, componentType, isSelected }) => {
|
|
9
|
+
const { setBuilderPanelsActive } = useBuilderContext();
|
|
10
|
+
const { insterBlock } = useBuilderPucksContext();
|
|
11
|
+
// TODO 要改為判斷哪些元件可以插入
|
|
12
|
+
const isinsterBlock = isSelected && (componentType == "BaseBlock" || componentType == "BaseBlockSue");
|
|
13
|
+
const addBeforeBlack = useCallback((e) => {
|
|
14
|
+
e?.stopPropagation();
|
|
15
|
+
insterBlock("before");
|
|
16
|
+
}, [insterBlock]);
|
|
17
|
+
const addAfterBlack = useCallback((e) => {
|
|
18
|
+
e?.stopPropagation();
|
|
19
|
+
insterBlock("after");
|
|
20
|
+
}, [insterBlock]);
|
|
21
|
+
return (_jsxs(_Fragment, { children: [isinsterBlock && _jsx("button", { onClick: addBeforeBlack, className: cn("border rounded-4xl bg-gray-900 text-white p-2 text-xs", "absolute -top-4 left-1/2 -translate-x-1/2", "cursor-pointer pointer-events-auto"), children: _jsx(Plus, { size: 15 }) }), _jsx("div", { className: "border-indigo-600 bg-indigo-600/5 border-2 h-full w-full", children: children }), isinsterBlock && _jsx("button", { onClick: addAfterBlack, className: cn("border rounded-4xl bg-gray-900 text-white p-2 text-xs", "absolute -bottom-4 left-1/2 -translate-x-1/2", "pointer-events-auto cursor-pointer"), children: _jsx(Plus, { size: 15 }) })] }));
|
|
22
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { DragEndEvent, DragStartEvent } from "@dnd-kit/core";
|
|
2
|
+
declare const BuilderEditorPannel: import("react").MemoExoticComponent<() => import("react/jsx-runtime").JSX.Element>;
|
|
3
|
+
export interface PropsTypes {
|
|
4
|
+
children?: React.ReactNode;
|
|
5
|
+
dragContainer?: React.RefObject<HTMLDivElement | null>;
|
|
6
|
+
onDragStart?: (event: DragStartEvent) => void;
|
|
7
|
+
onDragEnd?: (event: DragEndEvent) => void;
|
|
8
|
+
}
|
|
9
|
+
declare const BuilderPanelsContext: React.FC<PropsTypes>;
|
|
10
|
+
export { BuilderPanelsContext, BuilderEditorPannel, };
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { memo, useCallback, useEffect, useEffectEvent, useRef } from "react";
|
|
4
|
+
import { createPortal } from "react-dom";
|
|
5
|
+
import { sortableKeyboardCoordinates } from "@dnd-kit/sortable";
|
|
6
|
+
import { useSensors, useSensor, closestCenter, DndContext, KeyboardSensor, PointerSensor, useDndMonitor, useDraggable } from "@dnd-kit/core";
|
|
7
|
+
import { BuilderFields } from "./content-builder-fields";
|
|
8
|
+
import { useBuilderContext } from "./content-builder-context";
|
|
9
|
+
import { usePuckSelectedItem } from "./content-builder-pucks-hooks";
|
|
10
|
+
import { cn } from "../../shadcns/index.js";
|
|
11
|
+
const DraggablePanel = memo((props) => {
|
|
12
|
+
const { id, children, defaultPos = {
|
|
13
|
+
align: "left",
|
|
14
|
+
offsetX: 0,
|
|
15
|
+
offsetY: 0
|
|
16
|
+
} } = props;
|
|
17
|
+
const { attributes, listeners, setNodeRef, transform, setActivatorNodeRef } = useDraggable({ id });
|
|
18
|
+
const { setBuilderPanelsActive, setBuilderPanels, panels, builderRef } = useBuilderContext();
|
|
19
|
+
const { x = 0, y = 0, z = 0, active = false } = panels?.[id] ?? {};
|
|
20
|
+
const draggableRef = useRef(null);
|
|
21
|
+
const initPositions = useEffectEvent(() => {
|
|
22
|
+
if (!draggableRef.current || !active)
|
|
23
|
+
return;
|
|
24
|
+
const position = { x: 0, y: 0 };
|
|
25
|
+
const { width: panelW, height: panelH } = draggableRef.current.getBoundingClientRect();
|
|
26
|
+
const { align, offsetX = 0, offsetY = 0 } = defaultPos;
|
|
27
|
+
const viewportW = document.documentElement.clientWidth;
|
|
28
|
+
const viewportH = document.documentElement.clientHeight;
|
|
29
|
+
if (align === "right" || align === "rightTop" || align === "rightBottom") {
|
|
30
|
+
position.x = viewportW - panelW - offsetX;
|
|
31
|
+
}
|
|
32
|
+
else if (align === "left" || align === "leftTop" || align === "leftBottom") {
|
|
33
|
+
position.x = offsetX;
|
|
34
|
+
}
|
|
35
|
+
else if (align === "center") {
|
|
36
|
+
position.x = Math.round((viewportW - panelW) / 2) + offsetX;
|
|
37
|
+
}
|
|
38
|
+
if (align === "top" || align === "leftTop" || align === "rightTop") {
|
|
39
|
+
position.y = offsetY;
|
|
40
|
+
}
|
|
41
|
+
else if (align === "bottom" || align === "leftBottom" || align === "rightBottom") {
|
|
42
|
+
position.y = viewportH - panelH - offsetY;
|
|
43
|
+
}
|
|
44
|
+
else if (align === "center") {
|
|
45
|
+
position.y = Math.round((viewportH - panelH) / 2) + offsetY;
|
|
46
|
+
}
|
|
47
|
+
draggableRef.current.style.left = position.x + "px";
|
|
48
|
+
draggableRef.current.style.top = position.y + "px";
|
|
49
|
+
setBuilderPanels?.((prev) => {
|
|
50
|
+
const prevPosition = prev[id];
|
|
51
|
+
const panelPosition = { ...prevPosition, ...position };
|
|
52
|
+
return { ...prev, [id]: panelPosition };
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
const dragEndPositions = useCallback((delta) => {
|
|
56
|
+
setBuilderPanels?.((prev) => {
|
|
57
|
+
const prevPosition = prev[id];
|
|
58
|
+
const panelPosition = {
|
|
59
|
+
...prevPosition,
|
|
60
|
+
x: (delta?.x ?? 0) + prevPosition.x,
|
|
61
|
+
y: (delta?.y ?? 0) + prevPosition.y,
|
|
62
|
+
z: prevPosition.z,
|
|
63
|
+
};
|
|
64
|
+
return { ...prev, [id]: panelPosition };
|
|
65
|
+
});
|
|
66
|
+
}, [setBuilderPanels, id]);
|
|
67
|
+
const dragStartPositionsZ = useCallback(() => {
|
|
68
|
+
setBuilderPanels?.((prev) => {
|
|
69
|
+
const prevPosition = prev[id];
|
|
70
|
+
const panelsZ = Object.values(prev).map((pos) => pos.z);
|
|
71
|
+
const maxZ = Math.max(...panelsZ) + 1;
|
|
72
|
+
return { ...prev, [id]: { ...prevPosition, z: maxZ } };
|
|
73
|
+
});
|
|
74
|
+
}, [setBuilderPanels, id]);
|
|
75
|
+
const resizePositions = useCallback(() => {
|
|
76
|
+
setBuilderPanels?.((prev) => {
|
|
77
|
+
if (draggableRef?.current && builderRef?.current && prev[id]) {
|
|
78
|
+
const prevPosition = prev[id];
|
|
79
|
+
const panelRect = draggableRef.current.getBoundingClientRect();
|
|
80
|
+
const builderRect = builderRef.current.getBoundingClientRect();
|
|
81
|
+
const maxX = builderRect.width - panelRect.width;
|
|
82
|
+
const maxY = builderRect.height - panelRect.height;
|
|
83
|
+
if (prevPosition.x > maxX || prevPosition.y > maxY) {
|
|
84
|
+
const x = Math.min(Math.max(prevPosition.x, 0), Math.max(0, maxX));
|
|
85
|
+
const y = Math.min(Math.max(prevPosition.y, 0), Math.max(0, maxY));
|
|
86
|
+
return { ...prev, [id]: { ...prevPosition, x, y } };
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return prev;
|
|
90
|
+
});
|
|
91
|
+
}, [setBuilderPanels, id]);
|
|
92
|
+
useDndMonitor({
|
|
93
|
+
onDragStart: ({ active }) => {
|
|
94
|
+
if (active?.id !== id)
|
|
95
|
+
return;
|
|
96
|
+
dragStartPositionsZ();
|
|
97
|
+
},
|
|
98
|
+
onDragEnd: ({ delta, active }) => {
|
|
99
|
+
if (active?.id !== id)
|
|
100
|
+
return;
|
|
101
|
+
dragEndPositions(delta);
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
useEffect(() => {
|
|
105
|
+
if (!draggableRef?.current || !active)
|
|
106
|
+
return;
|
|
107
|
+
initPositions();
|
|
108
|
+
window.addEventListener("resize", resizePositions);
|
|
109
|
+
return () => window.removeEventListener("resize", resizePositions);
|
|
110
|
+
}, [resizePositions, active]);
|
|
111
|
+
// useEffect(() => {
|
|
112
|
+
// setBuilderPanels?.(prev => {
|
|
113
|
+
// const next = ({ ...prev, [id]: { ...prev[id], active: false } })
|
|
114
|
+
// return next
|
|
115
|
+
// })
|
|
116
|
+
// }, [selectedItem])
|
|
117
|
+
return (_jsx(_Fragment, { children: active && createPortal(_jsxs("div", { ref: (node) => {
|
|
118
|
+
setNodeRef(node);
|
|
119
|
+
draggableRef.current = node;
|
|
120
|
+
}, className: cn("fixed", "bg-white", "shadow-xl", "overflow-hidden", "rounded-md", "border"), style: {
|
|
121
|
+
zIndex: z,
|
|
122
|
+
left: x,
|
|
123
|
+
top: y,
|
|
124
|
+
transform: transform
|
|
125
|
+
? `translate3d(${transform.x}px, ${transform.y}px, 0)`
|
|
126
|
+
: undefined
|
|
127
|
+
}, children: [_jsxs("div", { className: "flex flex-row justify-between", children: [_jsx("div", { ref: setActivatorNodeRef, className: cn("w-5", "h-5", "bg-red-600", "cursor-grab", "active:cursor-grabbing"), ...attributes, ...listeners }), _jsx("button", { className: cn("px-2", "py-1", "cursor-pointer"), onClick: () => setBuilderPanelsActive(id, false), children: "close" })] }), children] }), document.body) }));
|
|
128
|
+
});
|
|
129
|
+
const BuilderEditorPannel = memo(() => {
|
|
130
|
+
const selectedItem = usePuckSelectedItem();
|
|
131
|
+
const { config: { components } } = useBuilderContext();
|
|
132
|
+
const { type } = selectedItem ?? {};
|
|
133
|
+
const label = type ? components[type].label : type;
|
|
134
|
+
return (_jsx(_Fragment, { children: selectedItem && _jsx(DraggablePanel, { id: "contentEditor", defaultPos: {
|
|
135
|
+
offsetX: 40,
|
|
136
|
+
offsetY: 80,
|
|
137
|
+
align: "rightTop"
|
|
138
|
+
}, children: _jsxs("div", { className: cn("w-87.5", "min-h-87.5", "h-full", "overflow-y-auto", "max-h-187.5"), children: [_jsx("div", { className: "px-4 font-semibold text-md", children: label }), _jsx("div", { children: _jsx(BuilderFields, {}) })] }) }) }));
|
|
139
|
+
});
|
|
140
|
+
const BuilderPanelsContext = memo(({ children }) => {
|
|
141
|
+
const { builderRef, setBuilderDragging, } = useBuilderContext();
|
|
142
|
+
const sensors = useSensors(useSensor(PointerSensor), useSensor(KeyboardSensor, {
|
|
143
|
+
coordinateGetter: sortableKeyboardCoordinates,
|
|
144
|
+
}));
|
|
145
|
+
const dragModifiers = useCallback(({ transform, activeNodeRect }) => {
|
|
146
|
+
if (!activeNodeRect || !builderRef?.current)
|
|
147
|
+
return transform;
|
|
148
|
+
const containerRect = builderRef.current.getBoundingClientRect();
|
|
149
|
+
const boxWidth = activeNodeRect.width;
|
|
150
|
+
const boxHeight = activeNodeRect.height;
|
|
151
|
+
const originalLeft = activeNodeRect.left;
|
|
152
|
+
const originalTop = activeNodeRect.top;
|
|
153
|
+
const minX = containerRect.left - originalLeft;
|
|
154
|
+
const maxX = containerRect.right - (originalLeft + boxWidth);
|
|
155
|
+
const minY = containerRect.top - originalTop;
|
|
156
|
+
const maxY = containerRect.bottom - (originalTop + boxHeight);
|
|
157
|
+
return {
|
|
158
|
+
...transform,
|
|
159
|
+
x: Math.min(Math.max(transform.x, minX), maxX),
|
|
160
|
+
y: Math.min(Math.max(transform.y, minY), maxY),
|
|
161
|
+
};
|
|
162
|
+
}, [builderRef]);
|
|
163
|
+
return (_jsx(DndContext, { sensors: sensors, collisionDetection: closestCenter, onDragStart: (e) => setBuilderDragging?.(true), onDragEnd: (e) => setBuilderDragging?.(false), modifiers: [dragModifiers], children: children }));
|
|
164
|
+
});
|
|
165
|
+
export { BuilderPanelsContext, BuilderEditorPannel, };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { memo } from "react";
|
|
4
|
+
import { Puck } from "@puckeditor/core";
|
|
5
|
+
import { useBuilderContext } from "./content-builder-context";
|
|
6
|
+
import { useBuilderPucksContext } from "./content-builder-pucks-context";
|
|
7
|
+
import { cn } from "../../shadcns/index.js";
|
|
8
|
+
export const BuilderViewport = memo((props) => {
|
|
9
|
+
const { viewport } = useBuilderContext();
|
|
10
|
+
const { selectBlock } = useBuilderPucksContext();
|
|
11
|
+
const isScreenView = viewport.view == "screen" || viewport.view == "fullscreen";
|
|
12
|
+
const screenTransform = `scale(${viewport.zoom})`;
|
|
13
|
+
const scerrnHeight = viewport?.zoom && viewport.zoom < 1 ? `${100 * (1 / viewport.zoom)}%` : undefined;
|
|
14
|
+
return (_jsx("div", { "data-component": "builder-viewport", className: cn("w-full h-full flex justify-center items-start bg-gray-50! overflow-y-auto", isScreenView ? "p-0 pt-10" : "p-10"), style: {
|
|
15
|
+
background: `url("data:image/svg+xml,%3Csvg width='24' height='24' xmlns='http://www.w3.org/2000/svg'%3E%3Ccircle cx='12' cy='12' r='1' fill='%239ca3af'/%3E%3C/svg%3E")`,
|
|
16
|
+
}, onClick: () => {
|
|
17
|
+
selectBlock(null);
|
|
18
|
+
}, children: _jsx("div", { className: cn("h-full duration-150 transition-[width]", viewport.view == "360" && "w-90", viewport.view == "768" && "w-3xl", viewport.view == "1400" && "w-350", viewport.view == "1920" && "w-480", isScreenView && "w-full"), children: _jsx("div", { className: cn("border w-full h-full shadow-2xl rounded bg-white verflow-hidden", "origin-top duration-150 transition-[transform_height] select-none"), style: { transform: screenTransform, minHeight: scerrnHeight }, ...props }) }) }));
|
|
19
|
+
});
|
|
20
|
+
export const BuilderPreview = memo((props) => {
|
|
21
|
+
return (_jsx(BuilderViewport, { children: _jsx(Puck.Preview, { ...props, "data-component": "builder-preview", id: "builders-preview" }) }));
|
|
22
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface BuilderPucksContextValue {
|
|
2
|
+
setPreviewMode: (state: "edit" | "interactive" | ((previewMode: "edit" | "interactive") => typeof previewMode)) => void;
|
|
3
|
+
insterBlock: (to?: "before" | "after") => void;
|
|
4
|
+
orderBlock: (move: "up" | "down") => void;
|
|
5
|
+
selectBlock: (id?: string | null) => void;
|
|
6
|
+
unSelectBlock: () => void;
|
|
7
|
+
scrollToBlock: (id: string) => void;
|
|
8
|
+
}
|
|
9
|
+
export interface BuilderPucksContextProps {
|
|
10
|
+
children: React.ReactNode;
|
|
11
|
+
}
|
|
12
|
+
export declare const BuilderPucksContextProviders: import("react").MemoExoticComponent<({ children }: BuilderPucksContextProps) => import("react/jsx-runtime").JSX.Element>;
|
|
13
|
+
export declare const useBuilderPucksContext: () => BuilderPucksContextValue;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { createContext, memo, use } from "react";
|
|
4
|
+
import { useBuilderPucks } from "./content-builder-pucks-hooks";
|
|
5
|
+
import { useBuilderContext } from "./content-builder-context";
|
|
6
|
+
const BuilderPucksContext = createContext(null);
|
|
7
|
+
export const BuilderPucksContextProviders = memo(({ children }) => {
|
|
8
|
+
const { builderProps } = useBuilderContext();
|
|
9
|
+
const BuilderRoots = useBuilderPucks();
|
|
10
|
+
return _jsx(BuilderPucksContext, { value: BuilderRoots, children: children });
|
|
11
|
+
});
|
|
12
|
+
export const useBuilderPucksContext = () => {
|
|
13
|
+
const context = use(BuilderPucksContext);
|
|
14
|
+
if (!context)
|
|
15
|
+
throw new Error("BuilderPucksContext must be used inside BuilderPucksContextProviders");
|
|
16
|
+
return context;
|
|
17
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { createUsePuck } from "@puckeditor/core";
|
|
2
|
+
import { scrollToBlock } from "./content-builder-helpers";
|
|
3
|
+
import type { Config } from "@puckeditor/core";
|
|
4
|
+
export declare const usePuck: ReturnType<typeof createUsePuck>;
|
|
5
|
+
export declare const usePuckSelectedItem: () => import("@puckeditor/core").ComponentData<any, string, import("@puckeditor/core").DefaultComponents> | null;
|
|
6
|
+
export declare const usePuckPreviewMode: () => "edit" | "interactive";
|
|
7
|
+
export interface UseBuilderRootsOptions {
|
|
8
|
+
config: Config;
|
|
9
|
+
}
|
|
10
|
+
export declare function useBuilderPucks(): {
|
|
11
|
+
setPreviewMode: (state: "edit" | "interactive" | ((previewMode: "edit" | "interactive") => typeof previewMode)) => void;
|
|
12
|
+
selectBlock: (id?: string | null) => void;
|
|
13
|
+
unSelectBlock: () => void;
|
|
14
|
+
insterBlock: (inster?: "before" | "after") => Promise<void>;
|
|
15
|
+
orderBlock: (move: "up" | "down") => void;
|
|
16
|
+
scrollToBlock: typeof scrollToBlock;
|
|
17
|
+
};
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useCallback, useMemo } from "react";
|
|
3
|
+
import { createUsePuck, useGetPuck } from "@puckeditor/core";
|
|
4
|
+
import { createComponentId, scrollToBlock } from "./content-builder-helpers";
|
|
5
|
+
import { useBuilderContext } from "./content-builder-context";
|
|
6
|
+
export const usePuck = createUsePuck();
|
|
7
|
+
export const usePuckSelectedItem = () => usePuck((api) => api.selectedItem);
|
|
8
|
+
export const usePuckPreviewMode = () => usePuck((api) => api.appState.ui.previewMode);
|
|
9
|
+
export function useBuilderPucks() {
|
|
10
|
+
const getPuck = useGetPuck();
|
|
11
|
+
const { selectBuilderBlocks, setBuilderPanels } = useBuilderContext();
|
|
12
|
+
const selectBlock = useCallback((id) => {
|
|
13
|
+
const { dispatch, getSelectorForId } = getPuck();
|
|
14
|
+
const selector = id ? getSelectorForId(id) : null;
|
|
15
|
+
dispatch({ type: "setUi", ui: { itemSelector: selector } });
|
|
16
|
+
}, []);
|
|
17
|
+
const unSelectBlock = useCallback(() => {
|
|
18
|
+
const { dispatch } = getPuck();
|
|
19
|
+
dispatch({ type: "setUi", ui: { itemSelector: undefined } });
|
|
20
|
+
}, []);
|
|
21
|
+
const onAfterCallback = useCallback((id) => {
|
|
22
|
+
selectBlock(id);
|
|
23
|
+
requestAnimationFrame(() => scrollToBlock(id));
|
|
24
|
+
}, []);
|
|
25
|
+
const insterBlock = useCallback(async (inster) => {
|
|
26
|
+
const { appState, dispatch, selectedItem, getParentById } = getPuck();
|
|
27
|
+
const blockType = await selectBuilderBlocks?.();
|
|
28
|
+
if (typeof blockType !== "string")
|
|
29
|
+
return;
|
|
30
|
+
const { id: itemId } = selectedItem?.props ?? {};
|
|
31
|
+
const itemParent = itemId
|
|
32
|
+
? getParentById(itemId)
|
|
33
|
+
: { type: "root" };
|
|
34
|
+
if (itemParent?.type !== "root")
|
|
35
|
+
return;
|
|
36
|
+
const nextContent = [...appState.data.content];
|
|
37
|
+
const selectedIndex = nextContent.findIndex((item) => item.props.id == itemId);
|
|
38
|
+
const safeIndex = selectedIndex < 0 ? nextContent.length : selectedIndex;
|
|
39
|
+
const insertIndex = inster === "before" ? safeIndex : safeIndex + 1;
|
|
40
|
+
const blockId = createComponentId(blockType);
|
|
41
|
+
dispatch({
|
|
42
|
+
type: "insert",
|
|
43
|
+
componentType: blockType,
|
|
44
|
+
destinationIndex: insertIndex,
|
|
45
|
+
destinationZone: "root:default-zone",
|
|
46
|
+
id: blockId,
|
|
47
|
+
});
|
|
48
|
+
requestAnimationFrame(() => onAfterCallback(blockId));
|
|
49
|
+
}, [selectBuilderBlocks]);
|
|
50
|
+
const orderBlock = (move) => {
|
|
51
|
+
const { appState, dispatch, selectedItem, getParentById } = getPuck();
|
|
52
|
+
const { id: itemId } = selectedItem?.props ?? {};
|
|
53
|
+
const itemParent = itemId
|
|
54
|
+
? getParentById(itemId)
|
|
55
|
+
: { type: "root" };
|
|
56
|
+
if (itemParent?.type !== "root")
|
|
57
|
+
return;
|
|
58
|
+
const nextContent = [...appState.data.content];
|
|
59
|
+
const selectedIndex = nextContent.findIndex((item) => item.props.id == itemId);
|
|
60
|
+
const maxIndex = nextContent.length - 1;
|
|
61
|
+
if (move === "up" && selectedIndex == 0)
|
|
62
|
+
return;
|
|
63
|
+
if (move === "down" && selectedIndex == maxIndex)
|
|
64
|
+
return;
|
|
65
|
+
const newIndex = selectedIndex + (move === "up" ? -1 : 1);
|
|
66
|
+
dispatch({
|
|
67
|
+
type: "reorder",
|
|
68
|
+
sourceIndex: selectedIndex,
|
|
69
|
+
destinationIndex: newIndex,
|
|
70
|
+
destinationZone: "root:default-zone",
|
|
71
|
+
});
|
|
72
|
+
requestAnimationFrame(() => onAfterCallback(itemId));
|
|
73
|
+
};
|
|
74
|
+
// const setComponentData = useCallback(({ propsKey, value }: any) => {
|
|
75
|
+
// const { appState, dispatch, selectedItem, getParentById } = getPuck()
|
|
76
|
+
// if (!selectedItem) return
|
|
77
|
+
// const { id: itemId } = selectedItem.props
|
|
78
|
+
// const { id: parentId } = getParentById(itemId)?.props ?? {}
|
|
79
|
+
// const nextData = walkTree(appState.data, config,
|
|
80
|
+
// (content, options) => parentId == options.parentId
|
|
81
|
+
// ? content.map((child) => child.props.id == itemId
|
|
82
|
+
// ? setDeep(child, propsKey, value)
|
|
83
|
+
// : child)
|
|
84
|
+
// : content
|
|
85
|
+
// )
|
|
86
|
+
// dispatch({ type: "setData", data: nextData })
|
|
87
|
+
// }, [config])
|
|
88
|
+
const setPreviewMode = (state) => {
|
|
89
|
+
const { appState, dispatch } = getPuck();
|
|
90
|
+
const ui = {
|
|
91
|
+
previewMode: typeof state == "function" ? state(appState.ui.previewMode) : state,
|
|
92
|
+
itemSelector: null
|
|
93
|
+
};
|
|
94
|
+
dispatch({ type: "setUi", ui });
|
|
95
|
+
requestAnimationFrame(() => {
|
|
96
|
+
setBuilderPanels((panels) => Object.keys(panels).reduce((init, key) => {
|
|
97
|
+
const nextValue = { ...panels[key], active: false };
|
|
98
|
+
return { ...init, [key]: nextValue };
|
|
99
|
+
}, {}));
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
const BuilderRoots = useMemo(() => ({
|
|
103
|
+
setPreviewMode,
|
|
104
|
+
selectBlock,
|
|
105
|
+
unSelectBlock,
|
|
106
|
+
insterBlock,
|
|
107
|
+
orderBlock,
|
|
108
|
+
scrollToBlock
|
|
109
|
+
}), [
|
|
110
|
+
getPuck,
|
|
111
|
+
setPreviewMode,
|
|
112
|
+
selectBlock,
|
|
113
|
+
unSelectBlock,
|
|
114
|
+
insterBlock,
|
|
115
|
+
orderBlock,
|
|
116
|
+
scrollToBlock
|
|
117
|
+
]);
|
|
118
|
+
return BuilderRoots;
|
|
119
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { PuckProps } from "./content-builder-builder";
|
|
2
|
+
import type { BuildersViewport } from "./content-builder-hooks";
|
|
3
|
+
export interface ContentBuilderProps extends PuckProps {
|
|
4
|
+
defaultViewport?: BuildersViewport["view"];
|
|
5
|
+
}
|
|
6
|
+
declare const ContentBuilder: React.FC<ContentBuilderProps>;
|
|
7
|
+
export { ContentBuilder };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { memo } from "react";
|
|
4
|
+
import { BuilderFunctionBar } from "./content-builder-functionbar";
|
|
5
|
+
import { BuilderContextProviders } from "./content-builder-context";
|
|
6
|
+
import { BuilderEditorPannel, BuilderPanelsContext } from "./content-builder-panels";
|
|
7
|
+
import { Builder } from "./content-builder-builder";
|
|
8
|
+
import { BuilderPreview } from "./content-builder-preview";
|
|
9
|
+
import { BuilderPucksContextProviders } from "./content-builder-pucks-context";
|
|
10
|
+
import { BuilderBlocksDialog } from "./content-builder-blocks-dialogs";
|
|
11
|
+
const ContentBuilder = memo((props) => {
|
|
12
|
+
if (!props?.data || !props?.config)
|
|
13
|
+
return null;
|
|
14
|
+
// const {
|
|
15
|
+
// data,
|
|
16
|
+
// builderConfigs,
|
|
17
|
+
// builderOverrides,
|
|
18
|
+
// blacksConfigs,
|
|
19
|
+
// blacksOverrides
|
|
20
|
+
// } = props
|
|
21
|
+
return (_jsx(BuilderContextProviders, { metadata: { build: true }, ...props, children: _jsx(Builder, { children: _jsxs(BuilderPucksContextProviders, { children: [_jsx(BuilderPanelsContext, { children: _jsx(BuilderEditorPannel, {}) }), _jsx(BuilderFunctionBar, {}), _jsx(BuilderPreview, {}), _jsx(BuilderBlocksDialog, {})] }) }) }));
|
|
22
|
+
});
|
|
23
|
+
ContentBuilder.displayName = "ContentBuilder";
|
|
24
|
+
export { ContentBuilder };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { AutoField, FieldLabel } from "@puckeditor/core";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
const FieldsTabs = ({ onChange, value }) => {
|
|
5
|
+
const [tab, setTab] = useState("A");
|
|
6
|
+
return (_jsxs(FieldLabel, { label: "Title2", readOnly: true, children: [_jsxs("div", { className: "flex gap-2", children: [_jsx("button", { onClick: () => setTab("A"), className: "flex-1 p-2 border", children: "A" }), _jsx("button", { onClick: () => setTab("B"), className: "flex-1 p-2 border", children: "B" })] }), tab == "A" && _jsx("div", { children: _jsx(AutoField, { field: { type: "text" }, onChange: onChange, value: value ?? "wade", readOnly: true }) }), tab == "B" && _jsx("div", { children: "B" })] }));
|
|
7
|
+
};
|
|
8
|
+
FieldsTabs.displayName = "FieldsTabs";
|
|
9
|
+
export default FieldsTabs;
|