Skip to content

Commit 9f0b4f8

Browse files
committed
Add draft state with visual preview
- Track rotation from document and draft changes separately - Show rotated/removed/new pages in draft state - Apply operations on save - Swap dimensions for 90/270 degree rotations
1 parent 0500b05 commit 9f0b4f8

File tree

1 file changed

+120
-4
lines changed

1 file changed

+120
-4
lines changed

web/ui-customization-doc-editor-sidebar/src/DocumentEditor.tsx

Lines changed: 120 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,13 @@ interface PageData {
3030
alt: string;
3131
pageIndex: number;
3232
src: string;
33+
rotation: number;
34+
}
35+
36+
interface DraftPageData extends PageData {
37+
draftRotation?: number; // Additional rotation applied in draft state
38+
isNew?: boolean;
39+
isRemoved?: boolean;
3340
}
3441

3542
type DocumentOperation =
@@ -40,7 +47,7 @@ type DocumentOperation =
4047
const DocumentEditor = (props: Props) => {
4148
const { instance } = props;
4249
const [pages, setPages] = useState<PageData[]>([]);
43-
// TODO: handle for 'all' selection
50+
const [draftPages, setDraftPages] = useState<DraftPageData[]>([]);
4451
const [selectedKeys, setSelectedKeys] = useState<Set<string | number>>(
4552
new Set(),
4653
);
@@ -66,10 +73,12 @@ const DocumentEditor = (props: Props) => {
6673
alt: pageInfo.label,
6774
pageIndex: pageInfo.index,
6875
src,
76+
rotation: pageInfo.rotation || 0,
6977
});
7078
}
7179

7280
setPages(pagesData);
81+
setDraftPages(pagesData);
7382
}, [instance]);
7483

7584
useEffect(() => {
@@ -91,26 +100,64 @@ const DocumentEditor = (props: Props) => {
91100
pageIndexes: [...selectedKeys].map((key) => parseKey(key) - 1),
92101
rotateBy: 90,
93102
};
103+
// Update draft state
104+
setDraftPages((current) =>
105+
current.map((page) =>
106+
selectedKeys.has(page.id)
107+
? { ...page, draftRotation: (page.draftRotation || 0) + 90 }
108+
: page,
109+
),
110+
);
94111
} else if (operation === "rotate-counterclockwise") {
95112
operationData = {
96113
type: "rotatePages",
97114
pageIndexes: [...selectedKeys].map((key) => parseKey(key) - 1),
98115
rotateBy: 270,
99116
};
117+
// Update draft state
118+
setDraftPages((current) =>
119+
current.map((page) =>
120+
selectedKeys.has(page.id)
121+
? { ...page, draftRotation: (page.draftRotation || 0) + 270 }
122+
: page,
123+
),
124+
);
100125
} else if (operation === "remove-pages") {
101126
operationData = {
102127
type: "removePages",
103128
pageIndexes: [...selectedKeys].map((key) => parseKey(key) - 1),
104129
};
130+
// Update draft state
131+
setDraftPages((current) =>
132+
current.map((page) =>
133+
selectedKeys.has(page.id) ? { ...page, isRemoved: true } : page,
134+
),
135+
);
105136
} else if (operation === "add-page") {
137+
const afterIndex = parseKey([...selectedKeys][0]) - 1;
106138
operationData = {
107139
type: "addPage",
108-
afterPageIndex: parseKey([...selectedKeys][0]) - 1,
140+
afterPageIndex: afterIndex,
109141
backgroundColor: new NutrientViewer.Color({ r: 255, g: 255, b: 255 }),
110142
pageHeight: 400,
111143
pageWidth: 300,
112144
rotateBy: 0,
113145
};
146+
// Update draft state
147+
setDraftPages((current) => {
148+
const newPage: DraftPageData = {
149+
id: `temp-${Date.now()}`,
150+
label: "New Page",
151+
alt: "New blank page",
152+
pageIndex: afterIndex + 1,
153+
src: "",
154+
rotation: 0,
155+
isNew: true,
156+
};
157+
const result = [...current];
158+
result.splice(afterIndex + 1, 0, newPage);
159+
return result;
160+
});
114161
}
115162

116163
if (operationData) {
@@ -130,6 +177,48 @@ const DocumentEditor = (props: Props) => {
130177
setSelectedKeys(new Set());
131178
};
132179

180+
const displayPages = draftPages.filter((page) => !page.isRemoved);
181+
182+
const renderImage = (item: {
183+
id: string;
184+
data?: { alt?: string; src?: string };
185+
}) => {
186+
// Find the corresponding draft page
187+
const draftPage = displayPages.find((page) => page.id === item.id);
188+
189+
if (!draftPage) {
190+
return <div>Error: Page not found</div>;
191+
}
192+
193+
if (draftPage.isNew) {
194+
// Render a white blank page
195+
return (
196+
<div
197+
style={{
198+
backgroundColor: "white",
199+
width: "100%",
200+
height: "100%",
201+
display: "flex",
202+
alignItems: "center",
203+
justifyContent: "center",
204+
border: "1px solid #e5e7eb",
205+
}}
206+
>
207+
<Text>New Page</Text>
208+
</div>
209+
);
210+
}
211+
212+
// Apply only draft rotation (the fetched image already has the document rotation applied)
213+
const style = draftPage.draftRotation
214+
? { transform: `rotate(${draftPage.draftRotation}deg)` }
215+
: undefined;
216+
217+
return (
218+
<img src={draftPage.src} alt={draftPage.alt} style={style} width="100%" />
219+
);
220+
};
221+
133222
const selectionText = selectedKeys.size ? (
134223
<Text>{selectedKeys.size} page(s) selected</Text>
135224
) : (
@@ -185,14 +274,41 @@ const DocumentEditor = (props: Props) => {
185274
{operations}
186275
<ImageGallery
187276
aria-label="Document editor sidebar"
188-
items={pages}
277+
items={displayPages}
189278
imageWidth="md"
190279
selectionMode="multiple"
280+
selectedKeys={selectedKeys}
191281
onSelectionChange={(keys) =>
192282
setSelectedKeys(
193-
keys === "all" ? new Set(pages.map((page) => page.id)) : keys,
283+
keys === "all"
284+
? new Set(displayPages.map((page) => page.id))
285+
: keys,
194286
)
195287
}
288+
renderImage={renderImage}
289+
imageDimensions={(item) => {
290+
const draftPage = displayPages.find(
291+
(page) => page.id === item.id,
292+
);
293+
if (!draftPage) {
294+
return { width: 180, height: 250 };
295+
}
296+
297+
// Calculate total rotation (document rotation + draft rotation)
298+
const totalRotation =
299+
draftPage.rotation + (draftPage.draftRotation || 0);
300+
301+
// For 90 or 270 degree rotations, swap dimensions
302+
const normalizedRotation = totalRotation % 360;
303+
const isRotated90or270 =
304+
normalizedRotation === 90 || normalizedRotation === 270;
305+
306+
if (isRotated90or270) {
307+
return { width: 250, height: 180 };
308+
}
309+
310+
return { width: 180, height: 250 };
311+
}}
196312
/>
197313
<Box gap="md" display="flex">
198314
<ActionButton

0 commit comments

Comments
 (0)