Skip to content

Commit bfaba42

Browse files
committed
feat: enhance ZenEditor with header and breadcrumb navigation for improved user experience
1 parent a8051bb commit bfaba42

File tree

12 files changed

+226
-150
lines changed

12 files changed

+226
-150
lines changed

src/Components/Card/Card.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ const Card: FC<ICard> = memo(
105105
/>
106106

107107
{actions && actions.length > 0 && (
108-
<div className='yl:absolute yl:top-3 yl:right-3 yl:z-20 yl:flex yl:gap-2 yl:opacity-0 yl:transition-opacity yl:duration-200 group-yl:hover:opacity-100 yl:md:opacity-100 md:group-yl:hover:opacity-100'>
108+
<div className='yl:absolute yl:top-3 yl:right-3 yl:z-20 yl:flex yl:gap-2 opacity-0 transition-opacity duration-200 group-hover:opacity-100'>
109109
{actions
110110
.filter(a => a.show !== false)
111111
.map((action, idx) => (
@@ -116,13 +116,13 @@ const Card: FC<ICard> = memo(
116116
action.onClick(e);
117117
}}
118118
className={classNames(
119-
"yl:bg-background/5 yl:flex yl:min-w-[75px] yl:cursor-pointer yl:items-center yl:justify-center yl:gap-1 yl:rounded-sm yl:border-2 yl:py-1 yl:text-xs yl:font-medium yl:transition-all yl:duration-200 yl:hover:shadow-sm yl:hover:brightness-95",
119+
"yl:flex yl:min-w-[75px] yl:cursor-pointer yl:items-center yl:justify-center yl:gap-1 yl:rounded-sm yl:py-1 yl:px-2 yl:text-xs yl:font-medium yl:transition-all yl:duration-200 yl:ease-out yl:shadow-lg yl:backdrop-blur-sm",
120120
{
121-
"yl:border-primary yl:text-primary yl:hover:bg-primary yl:hover:text-background":
121+
"yl:bg-primary yl:text-background yl:hover:bg-primary/80 yl:hover:shadow-xl":
122122
action.variant === "primary" || !action.variant,
123-
"yl:hover:text-background yl:border-red-500 yl:text-red-600 yl:hover:bg-red-700":
123+
"yl:bg-red-500 yl:text-background yl:hover:bg-red-600 yl:hover:shadow-xl":
124124
action.variant === "danger",
125-
"yl:border-border/20 yl:text-muted yl:hover:bg-background/80":
125+
"yl:bg-background/80 yl:text-text yl:border yl:border-border/20 yl:hover:bg-background yl:hover:shadow-xl yl:hover:border-border/40":
126126
action.variant === "outlined"
127127
}
128128
)}

src/Components/ZenEditor/ZenEditor.stories.tsx

Lines changed: 42 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { useCallback, useState } from "react";
44
import { Input } from "../Inputs";
55
import { TIPTAP_TOOLBAR_ITEMS } from "../Inputs/Tiptap/Tiptap.constants";
66
import { ZenEditor } from "./ZenEditor";
7-
import { ZenSaveStatus } from "./ZenEditor.types";
87

98
const meta: Meta<typeof ZenEditor> = {
109
title: "Components/ZenEditor",
@@ -45,24 +44,18 @@ const SamplePropertiesForm = () => {
4544
const ZenEditorWithState = () => {
4645
const [content, setContent] = useState("");
4746
const [isPropertiesOpen, setIsPropertiesOpen] = useState(false);
48-
const [saveStatus, setSaveStatus] = useState<ZenSaveStatus>("saved");
4947

5048
const handleUpdate = useCallback(({ editor }: { editor: unknown }) => {
5149
const typedEditor = editor as { getJSON: () => unknown };
5250
setContent(JSON.stringify(typedEditor.getJSON()));
53-
setSaveStatus("unsaved");
5451
}, []);
5552

5653
const handleToggleProperties = useCallback(() => {
5754
setIsPropertiesOpen(prev => !prev);
5855
}, []);
5956

6057
const handleSave = useCallback(() => {
61-
setSaveStatus("saving");
6258
// Simulate save
63-
setTimeout(() => {
64-
setSaveStatus("saved");
65-
}, 1000);
6659
}, []);
6760

6861
const handleCancel = useCallback(() => {
@@ -94,16 +87,16 @@ const ZenEditorWithState = () => {
9487
children: <SamplePropertiesForm />
9588
}}
9689
statusBar={{
97-
saveStatus,
9890
primaryAction: {
9991
label: "Publish",
100-
onClick: handleSave,
101-
isLoading: saveStatus === "saving"
92+
onClick: handleSave
10293
},
10394
secondaryAction: {
10495
label: "Cancel",
10596
onClick: handleCancel
106-
}
97+
},
98+
propertiesButtonText: "Article Details",
99+
propertiesButtonIcon: "Edit1Outline"
107100
}}
108101
/>
109102
);
@@ -113,126 +106,65 @@ export const Default: Story = {
113106
render: () => <ZenEditorWithState />
114107
};
115108

116-
// Story showing unsaved state
117-
const ZenEditorUnsaved = () => {
118-
const [isPropertiesOpen, setIsPropertiesOpen] = useState(false);
109+
// Story with drawer open
110+
const ZenEditorWithDrawerOpen = () => {
111+
const [isPropertiesOpen, setIsPropertiesOpen] = useState(true);
119112

120113
const handleToggleProperties = useCallback(() => {
121114
setIsPropertiesOpen(prev => !prev);
122115
}, []);
123116

124-
const handleSave = useCallback(() => {
125-
// No-op for story
126-
}, []);
127-
128117
return (
129118
<ZenEditor
130119
value=''
131120
onUpdate={() => {}}
132121
placeholder='Start writing...'
133122
toolbarItems={[
123+
TIPTAP_TOOLBAR_ITEMS.HEADING_2,
134124
TIPTAP_TOOLBAR_ITEMS.BOLD,
135125
TIPTAP_TOOLBAR_ITEMS.ITALIC,
136-
TIPTAP_TOOLBAR_ITEMS.LINK
126+
TIPTAP_TOOLBAR_ITEMS.UNORDERED_LIST
137127
]}
138128
propertiesDrawer={{
139129
isOpen: isPropertiesOpen,
140130
onToggle: handleToggleProperties,
141-
title: "Properties",
131+
title: "Article Properties",
142132
children: <SamplePropertiesForm />
143133
}}
144134
statusBar={{
145-
saveStatus: "unsaved",
146135
primaryAction: {
147-
label: "Save",
148-
onClick: handleSave
149-
}
136+
label: "Publish",
137+
onClick: () => {}
138+
},
139+
propertiesButtonText: "Article Details",
140+
propertiesButtonIcon: "Edit1Outline"
150141
}}
151142
/>
152143
);
153144
};
154145

155-
export const UnsavedChanges: Story = {
156-
render: () => <ZenEditorUnsaved />
146+
export const WithDrawerOpen: Story = {
147+
render: () => <ZenEditorWithDrawerOpen />
157148
};
158149

159-
// Story showing saving state
160-
const ZenEditorSaving = () => {
150+
// Story with breadcrumb header
151+
const ZenEditorWithBreadcrumbs = () => {
161152
const [isPropertiesOpen, setIsPropertiesOpen] = useState(false);
162153

163154
const handleToggleProperties = useCallback(() => {
164155
setIsPropertiesOpen(prev => !prev);
165156
}, []);
166157

167-
return (
168-
<ZenEditor
169-
value=''
170-
onUpdate={() => {}}
171-
placeholder='Start writing...'
172-
toolbarItems={[TIPTAP_TOOLBAR_ITEMS.BOLD, TIPTAP_TOOLBAR_ITEMS.ITALIC]}
173-
propertiesDrawer={{
174-
isOpen: isPropertiesOpen,
175-
onToggle: handleToggleProperties,
176-
title: "Properties",
177-
children: <SamplePropertiesForm />
178-
}}
179-
statusBar={{
180-
saveStatus: "saving",
181-
primaryAction: {
182-
label: "Save",
183-
onClick: () => {},
184-
isLoading: true
185-
}
186-
}}
187-
/>
188-
);
189-
};
190-
191-
export const Saving: Story = {
192-
render: () => <ZenEditorSaving />
193-
};
194-
195-
// Story showing error state
196-
const ZenEditorError = () => {
197-
const [isPropertiesOpen, setIsPropertiesOpen] = useState(false);
198-
199-
const handleToggleProperties = useCallback(() => {
200-
setIsPropertiesOpen(prev => !prev);
158+
const handleBack = useCallback(() => {
159+
console.log("Navigate back");
201160
}, []);
202161

203-
return (
204-
<ZenEditor
205-
value=''
206-
onUpdate={() => {}}
207-
placeholder='Start writing...'
208-
toolbarItems={[TIPTAP_TOOLBAR_ITEMS.BOLD, TIPTAP_TOOLBAR_ITEMS.ITALIC]}
209-
propertiesDrawer={{
210-
isOpen: isPropertiesOpen,
211-
onToggle: handleToggleProperties,
212-
title: "Properties",
213-
children: <SamplePropertiesForm />
214-
}}
215-
statusBar={{
216-
saveStatus: "error",
217-
primaryAction: {
218-
label: "Retry",
219-
onClick: () => {}
220-
}
221-
}}
222-
/>
223-
);
224-
};
225-
226-
export const SaveError: Story = {
227-
render: () => <ZenEditorError />
228-
};
229-
230-
// Story with drawer open
231-
const ZenEditorWithDrawerOpen = () => {
232-
const [isPropertiesOpen, setIsPropertiesOpen] = useState(true);
162+
const handleCourseClick = useCallback(() => {
163+
console.log("Navigate to course");
164+
}, []);
233165

234-
const handleToggleProperties = useCallback(() => {
235-
setIsPropertiesOpen(prev => !prev);
166+
const handleSectionClick = useCallback(() => {
167+
console.log("Navigate to section");
236168
}, []);
237169

238170
return (
@@ -243,26 +175,34 @@ const ZenEditorWithDrawerOpen = () => {
243175
toolbarItems={[
244176
TIPTAP_TOOLBAR_ITEMS.HEADING_2,
245177
TIPTAP_TOOLBAR_ITEMS.BOLD,
246-
TIPTAP_TOOLBAR_ITEMS.ITALIC,
247-
TIPTAP_TOOLBAR_ITEMS.UNORDERED_LIST
178+
TIPTAP_TOOLBAR_ITEMS.ITALIC
248179
]}
180+
header={{
181+
onBack: handleBack,
182+
breadcrumbs: [
183+
{ label: "Advanced React Patterns", onClick: handleCourseClick },
184+
{ label: "State Management", onClick: handleSectionClick },
185+
{ label: "Edit Lecture" }
186+
]
187+
}}
249188
propertiesDrawer={{
250189
isOpen: isPropertiesOpen,
251190
onToggle: handleToggleProperties,
252-
title: "Article Properties",
191+
title: "Lecture Properties",
253192
children: <SamplePropertiesForm />
254193
}}
255194
statusBar={{
256-
saveStatus: "saved",
257195
primaryAction: {
258-
label: "Publish",
196+
label: "Save Lecture",
259197
onClick: () => {}
260-
}
198+
},
199+
propertiesButtonText: "Lecture Details",
200+
propertiesButtonIcon: "InfoOutline"
261201
}}
262202
/>
263203
);
264204
};
265205

266-
export const WithDrawerOpen: Story = {
267-
render: () => <ZenEditorWithDrawerOpen />
206+
export const WithBreadcrumbs: Story = {
207+
render: () => <ZenEditorWithBreadcrumbs />
268208
};

src/Components/ZenEditor/ZenEditor.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ const ZenEditorComponent: ForwardRefRenderFunction<
2727
onImageUploadError,
2828
propertiesDrawer,
2929
statusBar,
30+
header,
3031
className,
3132
contentClassName,
3233
autoFocus = true
@@ -71,9 +72,12 @@ const ZenEditorComponent: ForwardRefRenderFunction<
7172
return (
7273
<div className={containerClasses}>
7374
<ZenStatusBar
74-
saveStatus={statusBar.saveStatus}
7575
onPropertiesToggle={propertiesDrawer.onToggle}
7676
isPropertiesOpen={propertiesDrawer.isOpen}
77+
propertiesButtonText={statusBar.propertiesButtonText}
78+
propertiesButtonIcon={statusBar.propertiesButtonIcon}
79+
onBack={header?.onBack}
80+
breadcrumbs={header?.breadcrumbs}
7781
/>
7882

7983
<div className={contentClasses}>

src/Components/ZenEditor/ZenEditor.types.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ import {
66
TiptapRef,
77
TiptapSuggestionOptions
88
} from "../Inputs/Tiptap/Tiptap.types";
9+
import { IZenBreadcrumbItem } from "./ZenHeader";
910
import { IZenAction } from "./ZenPropertiesDrawer";
10-
import { ZenSaveStatus } from "./ZenStatusBar";
1111

1212
// Re-export for convenience
13+
export type { IZenBreadcrumbItem } from "./ZenHeader";
1314
export type { IZenAction } from "./ZenPropertiesDrawer";
14-
export type { ZenSaveStatus } from "./ZenStatusBar";
1515

1616
export interface IZenEditorPropertiesDrawer {
1717
isOpen: boolean;
@@ -21,9 +21,15 @@ export interface IZenEditorPropertiesDrawer {
2121
}
2222

2323
export interface IZenEditorStatusBar {
24-
saveStatus: ZenSaveStatus;
2524
primaryAction: IZenAction;
2625
secondaryAction?: IZenAction;
26+
propertiesButtonText: string;
27+
propertiesButtonIcon: string;
28+
}
29+
30+
export interface IZenEditorHeader {
31+
onBack?: () => void;
32+
breadcrumbs: IZenBreadcrumbItem[];
2733
}
2834

2935
export interface IZenEditorProps {
@@ -37,6 +43,7 @@ export interface IZenEditorProps {
3743
onImageUploadError?: (error: string) => void;
3844
propertiesDrawer: IZenEditorPropertiesDrawer;
3945
statusBar: IZenEditorStatusBar;
46+
header?: IZenEditorHeader;
4047
className?: string;
4148
contentClassName?: string;
4249
autoFocus?: boolean;

0 commit comments

Comments
 (0)