Skip to content

Commit

Permalink
feat(article): implemented editable mode for action toolbar
Browse files Browse the repository at this point in the history
  • Loading branch information
JaleelB committed Sep 19, 2024
1 parent 5b841ea commit 54296a8
Show file tree
Hide file tree
Showing 4 changed files with 180 additions and 21 deletions.
12 changes: 10 additions & 2 deletions app/article/article-viewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import { useZoom } from "@/stores/article-store";
import { FloatingBubbleMenu } from "@/components/bubble-menu";
import TextAlign from "@tiptap/extension-text-align";
import Color from "@tiptap/extension-color";
import { cn } from "@/lib/utils"; // Make sure you have this utility function

function literalTemplate(
strings: TemplateStringsArray,
Expand Down Expand Up @@ -118,7 +119,10 @@ export function ArticleViewer({
],
editorProps: {
attributes: {
class: "prose dark:prose-dark",
class: cn(
"prose dark:prose-dark",
"focus:outline-none", // Remove focus outline
),
},
handleKeyDown: (view, event) => {
// Prevent deletion of content
Expand Down Expand Up @@ -167,7 +171,10 @@ export function ArticleViewer({

return (
<div
className="prose dark:prose-dark"
className={cn(
"prose dark:prose-dark relative",
isEditable && "rounded-lg bg-muted/20 p-2",
)}
style={{
appearance: "none",
}}
Expand All @@ -186,6 +193,7 @@ export function ArticleViewer({
)}
<EditorContent
editor={editor}
className={cn(isEditable && "bg-muted/50")}
style={{
width: `${100 / zoom}%`,
height: `${100 / zoom}%`,
Expand Down
183 changes: 167 additions & 16 deletions components/article-toolbar-options.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";

import { useState, useEffect, useTransition } from "react";
import { useState, useEffect, useTransition, useMemo } from "react";
import {
Command,
CommandEmpty,
Expand All @@ -9,10 +9,45 @@ import {
CommandItem,
CommandList,
} from "@/components/ui/command";
import { Check } from "lucide-react";
import { Check, Save } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Loader2 } from "lucide-react";
import { Minus, Plus, RotateCcw } from "lucide-react";
import { useSummary, useZoom, useZoomActions } from "@/stores/article-store";

import {
useIsEditable,
useToggleEditable,
useIsReadingMode,
useToggleReadingMode,
useEditor,
} from "@/stores/article-store";
import { Switch } from "@/components/ui/switch";
import {
Popover,
PopoverTrigger,
PopoverContent,
} from "@/components/ui/popover";
import {
Select,
SelectTrigger,
SelectValue,
SelectContent,
SelectItem,
} from "@/components/ui/select";
import {
Bold,
Italic,
Underline,
Strikethrough,
AlignLeft,
AlignCenter,
AlignRight,
Subscript,
Superscript,
Eraser,
} from "lucide-react";
import { toast } from "sonner";

const languages = [
{ value: "en", label: "English" },
Expand Down Expand Up @@ -96,15 +131,14 @@ export function LanguageSelector({
interface SummaryOptionProps {
onSummarize: () => Promise<void>;
isSummarizing: boolean;
summary: string | null;
}

export function SummaryOption({
onSummarize,
isSummarizing,
summary,
}: SummaryOptionProps) {
const [isPending, startTransition] = useTransition();
const summary = useSummary();

const handleSummarizeClick = () => {
if (!isSummarizing && !isPending && !summary) {
Expand Down Expand Up @@ -154,19 +188,15 @@ export function SummaryOption({
);
}

interface ZoomOptionProps {
zoom: number;
zoomIn: () => void;
zoomOut: () => void;
resetZoom: () => void;
}
export function ZoomOption() {
const zoom = useZoom();
const zoomActions = useZoomActions();

const { zoomIn, zoomOut, resetZoom } = useMemo(
() => zoomActions,
[zoomActions],
);

export function ZoomOption({
zoom,
zoomIn,
zoomOut,
resetZoom,
}: ZoomOptionProps) {
return (
<div className="flex flex-col gap-2 px-2 pb-2">
<div className="py-2 text-center text-5xl">
Expand All @@ -193,3 +223,124 @@ export function ZoomOption({
</div>
);
}

const fontFamilies = [
{ value: "Arial", label: "Arial" },
{ value: "Helvetica", label: "Helvetica" },
{ value: "Times New Roman", label: "Times New Roman" },
{ value: "Courier", label: "Courier" },
{ value: "Verdana", label: "Verdana" },
{ value: "Georgia", label: "Georgia" },
];

export function EditOption() {
const isEditable = useIsEditable();
const toggleEditable = useToggleEditable();
const editor = useEditor();

useEffect(() => {
if (isEditable) {
toast.info("Editing mode enabled");
}
}, [isEditable]);

const handleToggleEditable = () => {
const wasEditable = isEditable;
toggleEditable();
if (wasEditable) {
toast.info("Editing mode disabled.");
}
};

const setFontFamily = (font: string) => {
if (editor) {
editor.commands.selectAll();
editor.chain().focus().setFontFamily(font).run();
editor.commands.setTextSelection({ from: 0, to: 0 });
}
};

const clearFormatting = () => {
if (editor) {
editor
.chain()
.focus()
.unsetAllMarks()
.unsetFontFamily()
.unsetTextAlign()
.setTextSelection(editor.state.doc.content.size)
.run();

// Manually clear formatting for specific node types
editor.state.doc.descendants((node, pos) => {
if (node.type.name === "paragraph" || node.type.name === "heading") {
editor
.chain()
.focus()
.setNodeSelection(pos)
.unsetAllMarks()
.unsetFontFamily()
.unsetTextAlign()
.run();
}
});
}
};

const saveChanges = () => {
if (editor) {
const content = editor.getHTML();
console.log("Saving changes:", content);
toast.success("Changes saved successfully");
}
};

return (
<div className="flex flex-col gap-2 px-2 pb-2">
<div className="flex items-center justify-between">
<span className="text-sm font-medium">Enable Editing</span>
<Switch checked={isEditable} onCheckedChange={handleToggleEditable} />
</div>
{isEditable && (
<div className="flex max-h-[250px] flex-col justify-between gap-4 overflow-y-auto border-t border-accent pt-2">
<div className="flex items-center justify-between">
<span className="text-sm font-medium">Font Family</span>
<Select onValueChange={setFontFamily}>
<SelectTrigger
className="w-[150px]"
defaultValue={fontFamilies[0]?.value || "Arial"}
>
<SelectValue placeholder="Font Family" />
</SelectTrigger>
<SelectContent
align="end"
className="dark z-[1000] max-h-[100px] overflow-y-auto"
>
{fontFamilies.map((font) => (
<SelectItem key={font.value} value={font.value}>
{font.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="flex gap-2">
<Button
size="sm"
className="w-full"
variant="destructive"
onClick={clearFormatting}
>
<Eraser className="mr-2 h-4 w-4" />
Clear Formatting
</Button>
<Button size="sm" className="w-full" onClick={saveChanges}>
<Save className="mr-2 h-4 w-4" />
Save Changes
</Button>
</div>
</div>
)}
</div>
);
}
2 changes: 1 addition & 1 deletion components/article-toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ export function DynamicToolbar({
},
},
final: {
width: "320px",
width: "350px",
height: "270px",
borderRadius: "16px",
transition: {
Expand Down
4 changes: 2 additions & 2 deletions components/ui/dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const DialogOverlay = React.forwardRef<
<DialogPrimitive.Overlay
ref={ref}
className={cn(
"fixed inset-0 z-[2000] bg-white/40 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 dark:bg-black/40",
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className,
)}
{...props}
Expand All @@ -38,7 +38,7 @@ const DialogContent = React.forwardRef<
<DialogPrimitive.Content
ref={ref}
className={cn(
"fixed left-[50%] top-[50%] z-[2000] grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
className,
)}
{...props}
Expand Down

0 comments on commit 54296a8

Please sign in to comment.