mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-04-25 16:56:22 +02:00
feat(editor): add reserveToolbarSpace option to enhance toolbar visibility management
This commit is contained in:
parent
0381632bc2
commit
a1d3356bf5
5 changed files with 36 additions and 4 deletions
|
|
@ -572,6 +572,7 @@ export function EditorPanelContent({
|
||||||
placeholder="Start writing..."
|
placeholder="Start writing..."
|
||||||
editorVariant="default"
|
editorVariant="default"
|
||||||
allowModeToggle={false}
|
allowModeToggle={false}
|
||||||
|
reserveToolbarSpace
|
||||||
defaultEditing={isEditing}
|
defaultEditing={isEditing}
|
||||||
className="[&_[role=toolbar]]:!bg-sidebar"
|
className="[&_[role=toolbar]]:!bg-sidebar"
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,15 @@ interface EditorSaveContextValue {
|
||||||
isSaving: boolean;
|
isSaving: boolean;
|
||||||
/** Whether the user can toggle between editing and viewing modes */
|
/** Whether the user can toggle between editing and viewing modes */
|
||||||
canToggleMode: boolean;
|
canToggleMode: boolean;
|
||||||
|
/** Whether fixed-toolbar space should be reserved even when controls are hidden */
|
||||||
|
reserveToolbarSpace: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const EditorSaveContext = createContext<EditorSaveContextValue>({
|
export const EditorSaveContext = createContext<EditorSaveContextValue>({
|
||||||
hasUnsavedChanges: false,
|
hasUnsavedChanges: false,
|
||||||
isSaving: false,
|
isSaving: false,
|
||||||
canToggleMode: false,
|
canToggleMode: false,
|
||||||
|
reserveToolbarSpace: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
export function useEditorSave() {
|
export function useEditorSave() {
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,8 @@ export interface PlateEditorProps {
|
||||||
isSaving?: boolean;
|
isSaving?: boolean;
|
||||||
/** Whether edit/view mode toggle UI should be available in toolbars. */
|
/** Whether edit/view mode toggle UI should be available in toolbars. */
|
||||||
allowModeToggle?: boolean;
|
allowModeToggle?: boolean;
|
||||||
|
/** Reserve fixed-toolbar vertical space even when controls are hidden. */
|
||||||
|
reserveToolbarSpace?: boolean;
|
||||||
/** Start the editor in editing mode instead of viewing mode. Ignored when readOnly is true. */
|
/** Start the editor in editing mode instead of viewing mode. Ignored when readOnly is true. */
|
||||||
defaultEditing?: boolean;
|
defaultEditing?: boolean;
|
||||||
/**
|
/**
|
||||||
|
|
@ -94,6 +96,7 @@ export function PlateEditor({
|
||||||
hasUnsavedChanges = false,
|
hasUnsavedChanges = false,
|
||||||
isSaving = false,
|
isSaving = false,
|
||||||
allowModeToggle = true,
|
allowModeToggle = true,
|
||||||
|
reserveToolbarSpace = false,
|
||||||
defaultEditing = false,
|
defaultEditing = false,
|
||||||
preset = "full",
|
preset = "full",
|
||||||
extraPlugins = [],
|
extraPlugins = [],
|
||||||
|
|
@ -185,8 +188,9 @@ export function PlateEditor({
|
||||||
hasUnsavedChanges,
|
hasUnsavedChanges,
|
||||||
isSaving,
|
isSaving,
|
||||||
canToggleMode,
|
canToggleMode,
|
||||||
|
reserveToolbarSpace,
|
||||||
}),
|
}),
|
||||||
[onSave, hasUnsavedChanges, isSaving, canToggleMode]
|
[onSave, hasUnsavedChanges, isSaving, canToggleMode, reserveToolbarSpace]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,19 @@ import { FixedToolbarButtons } from "@/components/ui/fixed-toolbar-buttons";
|
||||||
|
|
||||||
function ConditionalFixedToolbar() {
|
function ConditionalFixedToolbar() {
|
||||||
const readOnly = useEditorReadOnly();
|
const readOnly = useEditorReadOnly();
|
||||||
const { onSave, hasUnsavedChanges, canToggleMode } = useEditorSave();
|
const { onSave, hasUnsavedChanges, canToggleMode, reserveToolbarSpace } = useEditorSave();
|
||||||
|
|
||||||
const hasVisibleControls =
|
const hasVisibleControls =
|
||||||
!readOnly || canToggleMode || (!!onSave && hasUnsavedChanges && !readOnly);
|
!readOnly || canToggleMode || (!!onSave && hasUnsavedChanges && !readOnly);
|
||||||
|
|
||||||
if (!hasVisibleControls) return null;
|
if (!hasVisibleControls) {
|
||||||
|
if (!reserveToolbarSpace) return null;
|
||||||
|
return (
|
||||||
|
<FixedToolbar className="pointer-events-none opacity-0">
|
||||||
|
<div className="h-8 w-full" />
|
||||||
|
</FixedToolbar>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FixedToolbar>
|
<FixedToolbar>
|
||||||
|
|
|
||||||
|
|
@ -116,6 +116,7 @@ export function ReportPanelContent({
|
||||||
const [exporting, setExporting] = useState<string | null>(null);
|
const [exporting, setExporting] = useState<string | null>(null);
|
||||||
const [saving, setSaving] = useState(false);
|
const [saving, setSaving] = useState(false);
|
||||||
const copyTimerRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
|
const copyTimerRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
|
||||||
|
const changeCountRef = useRef(0);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return () => {
|
return () => {
|
||||||
|
|
@ -190,8 +191,21 @@ export function ReportPanelContent({
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setEditedMarkdown(null);
|
setEditedMarkdown(null);
|
||||||
setIsEditing(false);
|
setIsEditing(false);
|
||||||
|
changeCountRef.current = 0;
|
||||||
}, [activeReportId]);
|
}, [activeReportId]);
|
||||||
|
|
||||||
|
const handleReportMarkdownChange = useCallback(
|
||||||
|
(nextMarkdown: string) => {
|
||||||
|
if (!isEditing) return;
|
||||||
|
changeCountRef.current += 1;
|
||||||
|
// Plate may emit an initial normalize/serialize change on mount.
|
||||||
|
if (changeCountRef.current <= 1) return;
|
||||||
|
const savedMarkdown = reportContent?.content ?? "";
|
||||||
|
setEditedMarkdown(nextMarkdown === savedMarkdown ? null : nextMarkdown);
|
||||||
|
},
|
||||||
|
[isEditing, reportContent?.content]
|
||||||
|
);
|
||||||
|
|
||||||
// Copy markdown content (uses latest editor content)
|
// Copy markdown content (uses latest editor content)
|
||||||
const handleCopy = useCallback(async () => {
|
const handleCopy = useCallback(async () => {
|
||||||
if (!currentMarkdown) return;
|
if (!currentMarkdown) return;
|
||||||
|
|
@ -299,6 +313,7 @@ export function ReportPanelContent({
|
||||||
|
|
||||||
const handleCancelEditing = useCallback(() => {
|
const handleCancelEditing = useCallback(() => {
|
||||||
setEditedMarkdown(null);
|
setEditedMarkdown(null);
|
||||||
|
changeCountRef.current = 0;
|
||||||
setIsEditing(false);
|
setIsEditing(false);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|
@ -436,6 +451,7 @@ export function ReportPanelContent({
|
||||||
className="size-6"
|
className="size-6"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setEditedMarkdown(null);
|
setEditedMarkdown(null);
|
||||||
|
changeCountRef.current = 0;
|
||||||
setIsEditing(true);
|
setIsEditing(true);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
@ -473,11 +489,12 @@ export function ReportPanelContent({
|
||||||
key={`report-${activeReportId}-${isEditing ? "editing" : "viewing"}`}
|
key={`report-${activeReportId}-${isEditing ? "editing" : "viewing"}`}
|
||||||
preset="full"
|
preset="full"
|
||||||
markdown={reportContent.content}
|
markdown={reportContent.content}
|
||||||
onMarkdownChange={setEditedMarkdown}
|
onMarkdownChange={handleReportMarkdownChange}
|
||||||
readOnly={!isEditing}
|
readOnly={!isEditing}
|
||||||
placeholder="Report content..."
|
placeholder="Report content..."
|
||||||
editorVariant="default"
|
editorVariant="default"
|
||||||
allowModeToggle={false}
|
allowModeToggle={false}
|
||||||
|
reserveToolbarSpace
|
||||||
defaultEditing={isEditing}
|
defaultEditing={isEditing}
|
||||||
className="[&_[role=toolbar]]:!bg-sidebar"
|
className="[&_[role=toolbar]]:!bg-sidebar"
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue