From 6294f5fa3384f82f51631e43685bba0e49abab4f Mon Sep 17 00:00:00 2001 From: codetorso Date: Sat, 29 Jun 2024 20:34:48 +0530 Subject: fix typescript errors --- apps/web/components/canvas/canvas.tsx | 96 ++++++++++ apps/web/components/canvas/draggableComponent.tsx | 67 +++++++ apps/web/components/canvas/dropComponent.tsx | 206 ++++++++++++++++++++++ apps/web/components/canvas/enabledComp copy.tsx | 22 +++ apps/web/components/canvas/enabledComp.tsx | 22 +++ apps/web/components/canvas/resizableLayout.tsx | 170 ++++++++++++++++++ apps/web/components/canvas/savesnap.tsx | 33 ++++ apps/web/components/canvas/textCard.tsx | 45 +++++ apps/web/components/canvas/twitterCard.tsx | 84 +++++++++ 9 files changed, 745 insertions(+) create mode 100644 apps/web/components/canvas/canvas.tsx create mode 100644 apps/web/components/canvas/draggableComponent.tsx create mode 100644 apps/web/components/canvas/dropComponent.tsx create mode 100644 apps/web/components/canvas/enabledComp copy.tsx create mode 100644 apps/web/components/canvas/enabledComp.tsx create mode 100644 apps/web/components/canvas/resizableLayout.tsx create mode 100644 apps/web/components/canvas/savesnap.tsx create mode 100644 apps/web/components/canvas/textCard.tsx create mode 100644 apps/web/components/canvas/twitterCard.tsx (limited to 'apps/web/components') diff --git a/apps/web/components/canvas/canvas.tsx b/apps/web/components/canvas/canvas.tsx new file mode 100644 index 00000000..1fbff4b8 --- /dev/null +++ b/apps/web/components/canvas/canvas.tsx @@ -0,0 +1,96 @@ +import { useCallback, useEffect, useRef, useState } from "react"; +import { Editor, Tldraw, setUserPreferences, TLStoreWithStatus } from "tldraw"; +import { createAssetFromUrl } from "../../lib/createAssetUrl"; +import "tldraw/tldraw.css"; +import { components } from "./enabledComp"; +import { twitterCardUtil } from "./twitterCard"; +import { textCardUtil } from "./textCard"; +import createEmbedsFromUrl from "../../lib/createEmbeds"; +import { loadRemoteSnapshot } from "../../lib/loadSnap"; +import { SaveStatus } from "./savesnap"; +import { getAssetUrls } from "@tldraw/assets/selfHosted"; +import { memo } from "react"; +import DragContext from "../../lib/context"; +import DropZone from "./dropComponent"; +import { useRect } from "./resizableLayout"; +// import "./canvas.css"; + +export const Canvas = memo(() => { + const [isDraggingOver, setIsDraggingOver] = useState(false); + const Dragref = useRef(null); + + const handleDragOver = (event: any) => { + event.preventDefault(); + setIsDraggingOver(true); + console.log("entere"); + }; + + useEffect(() => { + const divElement = Dragref.current; + if (divElement) { + divElement.addEventListener("dragover", handleDragOver); + } + return () => { + if (divElement) { + divElement.removeEventListener("dragover", handleDragOver); + } + }; + }, []); + + return ( + +
+ +
+
+ ); +}); + +const TldrawComponent = memo(() => { + const { id } = useRect(); + const [storeWithStatus, setStoreWithStatus] = useState({ + status: "loading", + }); + useEffect(() => { + const fetchStore = async () => { + const store = await loadRemoteSnapshot(id); + + setStoreWithStatus({ + store: store, + status: "not-synced", + }); + }; + + fetchStore(); + }, []); + + const handleMount = useCallback((editor: Editor) => { + (window as any).app = editor; + (window as any).editor = editor; + editor.registerExternalAssetHandler("url", createAssetFromUrl); + editor.registerExternalContentHandler("url", ({ url, point, sources }) => { + createEmbedsFromUrl({ url, point, sources, editor }); + }); + }, []); + + setUserPreferences({ id: "supermemory", colorScheme: "dark" }); + + const assetUrls = getAssetUrls(); + return ( +
+ +
+ +
+ +
+
+ ); +}); diff --git a/apps/web/components/canvas/draggableComponent.tsx b/apps/web/components/canvas/draggableComponent.tsx new file mode 100644 index 00000000..8c39c732 --- /dev/null +++ b/apps/web/components/canvas/draggableComponent.tsx @@ -0,0 +1,67 @@ +import Image from "next/image"; +import { useRef, useState } from "react"; + +interface DraggableComponentsProps { + content: string; + extraInfo?: string; + iconAlt: string; +} + +export default function DraggableComponentsContainer({ + content, +}: { + content: DraggableComponentsProps[]; +}) { + return ( +
+ {content.map((i) => { + return ( + + ); + })} +
+ ); +} + +function DraggableComponents({ + content, + extraInfo, + iconAlt, +}: DraggableComponentsProps) { + const [isDragging, setIsDragging] = useState(false); + const containerRef = useRef(null); + + const handleDragStart = (event: React.DragEvent) => { + setIsDragging(true); + if (containerRef.current) { + // Serialize the children as a string for dataTransfer + const childrenHtml = containerRef.current.innerHTML; + event.dataTransfer.setData("text/html", childrenHtml); + } + }; + + const handleDragEnd = () => { + setIsDragging(false); + }; + + return ( +
+
+
+

{content}

+
+

{extraInfo}

+
+
+ ); +} diff --git a/apps/web/components/canvas/dropComponent.tsx b/apps/web/components/canvas/dropComponent.tsx new file mode 100644 index 00000000..5ea383a1 --- /dev/null +++ b/apps/web/components/canvas/dropComponent.tsx @@ -0,0 +1,206 @@ +import React, { useRef, useCallback, useEffect, useContext } from "react"; +import { useEditor } from "tldraw"; +import DragContext, { + DragContextType, + useDragContext, +} from "../../lib/context"; +import { handleExternalDroppedContent } from "../../lib/createEmbeds"; + +const stripHtmlTags = (html: string): string => { + const div = document.createElement("div"); + div.innerHTML = html; + return div.textContent || div.innerText || ""; +}; + +function formatTextToRatio(text: string) { + const totalWidth = text.length; + const maxLineWidth = Math.floor(totalWidth / 4); + + const words = text.split(" "); + let lines = []; + let currentLine = ""; + + words.forEach((word) => { + // Check if adding the next word exceeds the maximum line width + if ((currentLine + word).length <= maxLineWidth) { + currentLine += (currentLine ? " " : "") + word; + } else { + // If the current line is full, push it to new line + lines.push(currentLine); + currentLine = word; + } + }); + if (currentLine) { + lines.push(currentLine); + } + return lines.join("\n"); +} + +function DropZone() { + const dropRef = useRef(null); + const { isDraggingOver, setIsDraggingOver } = useDragContext(); + + const editor = useEditor(); + + const handleDragLeave = () => { + setIsDraggingOver(false); + console.log("leaver"); + }; + + useEffect(() => { + setInterval(() => { + editor.selectAll(); + const shapes = editor.getSelectedShapes(); + const text = shapes.filter((s) => s.type === "text"); + console.log("hrhh", text); + }, 5000); + }, []); + + const handleDrop = useCallback((event: DragEvent) => { + event.preventDefault(); + setIsDraggingOver(false); + const dt = event.dataTransfer; + if (!dt) { + return; + } + const items = dt.items; + + for (let i = 0; i < items.length; i++) { + if (items[i]!.kind === "file" && items[i]!.type.startsWith("image/")) { + const file = items[i]!.getAsFile(); + if (file) { + const reader = new FileReader(); + reader.onload = (e) => { + if (e.target) { + // setDroppedImage(e.target.result as string); + } + }; + reader.readAsDataURL(file); + } + } else if (items[i]!.kind === "string") { + items[i]!.getAsString((data) => { + const cleanText = stripHtmlTags(data); + const onethree = formatTextToRatio(cleanText); + handleExternalDroppedContent({ editor, text: onethree }); + }); + } + } + }, []); + + useEffect(() => { + const divElement = dropRef.current; + if (divElement) { + divElement.addEventListener("drop", handleDrop); + divElement.addEventListener("dragleave", handleDragLeave); + } + return () => { + if (divElement) { + divElement.removeEventListener("drop", handleDrop); + divElement.addEventListener("dragleave", handleDragLeave); + } + }; + }, []); + + return ( +
+ {isDraggingOver && ( + <> +
+ +
+
+ +
+
+ +
+
+ +
+

Drop here to add Content on Canvas

+ + )} +
+ ); +} + +function TopRight() { + return ( + + + + ); +} + +function TopLeft() { + return ( + + + + ); +} + +function BottomLeft() { + return ( + + + + ); +} + +function BottomRight() { + return ( + + + + ); +} + +export default DropZone; diff --git a/apps/web/components/canvas/enabledComp copy.tsx b/apps/web/components/canvas/enabledComp copy.tsx new file mode 100644 index 00000000..85811b82 --- /dev/null +++ b/apps/web/components/canvas/enabledComp copy.tsx @@ -0,0 +1,22 @@ +import { TLUiComponents } from "tldraw"; + +export const components: Partial = { + ActionsMenu: null, + MainMenu: null, + QuickActions: null, + TopPanel: null, + DebugPanel: null, + DebugMenu: null, + PageMenu: null, + // Minimap: null, + // ContextMenu: null, + // HelpMenu: null, + // ZoomMenu: null, + // StylePanel: null, + // NavigationPanel: null, + // Toolbar: null, + // KeyboardShortcutsDialog: null, + // HelperButtons: null, + // SharePanel: null, + // MenuPanel: null, +}; \ No newline at end of file diff --git a/apps/web/components/canvas/enabledComp.tsx b/apps/web/components/canvas/enabledComp.tsx new file mode 100644 index 00000000..85811b82 --- /dev/null +++ b/apps/web/components/canvas/enabledComp.tsx @@ -0,0 +1,22 @@ +import { TLUiComponents } from "tldraw"; + +export const components: Partial = { + ActionsMenu: null, + MainMenu: null, + QuickActions: null, + TopPanel: null, + DebugPanel: null, + DebugMenu: null, + PageMenu: null, + // Minimap: null, + // ContextMenu: null, + // HelpMenu: null, + // ZoomMenu: null, + // StylePanel: null, + // NavigationPanel: null, + // Toolbar: null, + // KeyboardShortcutsDialog: null, + // HelperButtons: null, + // SharePanel: null, + // MenuPanel: null, +}; \ No newline at end of file diff --git a/apps/web/components/canvas/resizableLayout.tsx b/apps/web/components/canvas/resizableLayout.tsx new file mode 100644 index 00000000..5ba6780b --- /dev/null +++ b/apps/web/components/canvas/resizableLayout.tsx @@ -0,0 +1,170 @@ +"use client"; + +import { Canvas } from "./canvas"; +import React, { createContext, useContext, useState } from "react"; +import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels"; +import { SettingsIcon, DragIcon } from "@repo/ui/icons"; +import DraggableComponentsContainer from "./draggableComponent"; +import Image from "next/image"; +import { Label } from "@repo/ui/shadcn/label"; + +interface RectContextType { + fullScreen: boolean; + setFullScreen: React.Dispatch>; + visible: boolean; + setVisible: React.Dispatch>; + id: string +} + +const RectContext = createContext(undefined); + +export const RectProvider = ({ id, children }: {id: string, children: React.ReactNode}) => { + const [fullScreen, setFullScreen] = useState(false); + const [visible, setVisible] = useState(true); + + const value = { + id, + fullScreen, + setFullScreen, + visible, + setVisible, + }; + + return {children}; +}; + +export const useRect = () => { + const context = useContext(RectContext); + if (context === undefined) { + throw new Error('useRect must be used within a RectProvider'); + } + return context; +}; + + +export function ResizaleLayout() { + const { setVisible, fullScreen, setFullScreen } = useRect(); + + return ( +
+ { + l[0]! < 20 ? setVisible(false) : setVisible(true); + }} + className={` ${fullScreen ? "w-[calc(100vw-2rem)]" : "w-screen"} transition-all`} + direction="horizontal" + > + { + setTimeout(() => setFullScreen(false), 50); + }} + onCollapse={() => { + setTimeout(() => setFullScreen(true), 50); + }} + defaultSize={30} + collapsible={true} + > + + + + + + + + + +
+ ); +} + +function DragIconContainer() { + const { fullScreen} = useRect(); + return ( +
+ drag-icon +
+ ); +} + +function CanvasContainer() { + const { fullScreen} = useRect(); + return ( +
+ +
+ ); +} + +function SidePanelContainer() { + const { fullScreen, visible} = useRect(); + return ( +
+
+ Change Filters + setting-icon +
+ {visible ? ( + + ) : ( +

Need more space to show!

+ )} +
+ ); +} + +function SidePanel() { + const [value, setValue] = useState(""); + // const [dragAsText, setDragAsText] = useState(false); + return ( + <> +
+ { + setValue(e.target.value); + }} + value={value} + // rows={1} + className="w-full resize-none rounded-xl bg-[#151515] px-3 py-4 text-xl text-[#989EA4] outline-none focus:outline-none sm:max-h-52" + /> +
+
+ {/* setDragAsText(e)} + id="drag-text-mode" + /> */} + +
+ + + ); +} + + +const content = [ + { + content: + "Regional growth patterns diverge, with strong performance in the United States and several emerging markets, contrasted by weaker prospects in many advanced economies, particularly in Europe (World Economic Forum) (OECD). The rapid adoption of artificial intelligence (AI) is expected to drive productivity growth, especially in advanced economies, potentially mitigating labor shortages and boosting income levels in emerging markets (World Economic Forum) (OECD). However, ongoing geopolitical tensions and economic fragmentation are likely to maintain a level of uncertainty and volatility in the global economy (World Economic Forum.", + iconAlt: "Autocomplete", + extraInfo: + "Page Url: https://chatgpt.com/c/762cd44e-1752-495b-967a-aa3c23c6024a", + }, + { + content: + "As of mid-2024, the global economy is experiencing modest growth with significant regional disparities. Global GDP growth is projected to be around 3.1% in 2024, rising slightly to 3.2% in 2025. This performance, although below the pre-pandemic average, reflects resilience despite various economic pressures, including tight monetary conditions and geopolitical tensions (IMF)(OECD) Inflation is moderating faster than expected, with global headline inflation projected to fall to 5.8% in 2024 and 4.4% in 2025, contributing to improving real incomes and positive trade growth (IMF) (OECD)", + iconAlt: "Autocomplete", + extraInfo: + "Page Url: https://www.cnbc.com/2024/05/23/nvidia-keeps-hitting-records-can-investors-still-buy-the-stock.html?&qsearchterm=nvidia", + }, +]; diff --git a/apps/web/components/canvas/savesnap.tsx b/apps/web/components/canvas/savesnap.tsx new file mode 100644 index 00000000..45fc7e9d --- /dev/null +++ b/apps/web/components/canvas/savesnap.tsx @@ -0,0 +1,33 @@ +import { useCallback, useEffect, useState } from "react"; +import { debounce, getSnapshot, useEditor } from "tldraw"; +import { SaveCanvas } from "@/app/actions/doers"; + +export function SaveStatus({id}: {id:string}) { + const [save, setSave] = useState("saved!"); + const editor = useEditor(); + + + const debouncedSave = useCallback( + debounce(async () => { + const snapshot = getSnapshot(editor.store) + SaveCanvas({id, data: JSON.stringify(snapshot)}) + + setSave("saved!"); + }, 3000), + [editor], // Dependency array ensures the function is not recreated on every render + ); + + useEffect(() => { + const unsubscribe = editor.store.listen( + () => { + setSave("saving..."); + debouncedSave(); + }, + { scope: "document", source: "user" }, + ); + + return () => unsubscribe(); // Cleanup on unmount + }, [editor, debouncedSave]); + + return ; +} \ No newline at end of file diff --git a/apps/web/components/canvas/textCard.tsx b/apps/web/components/canvas/textCard.tsx new file mode 100644 index 00000000..b24dae52 --- /dev/null +++ b/apps/web/components/canvas/textCard.tsx @@ -0,0 +1,45 @@ +import { BaseBoxShapeUtil, HTMLContainer, TLBaseShape } from "tldraw"; + +type ITextCardShape = TLBaseShape< + "Textcard", + { w: number; h: number; content: string; extrainfo: string } +>; + +export class textCardUtil extends BaseBoxShapeUtil { + static override type = "Textcard" as const; + + getDefaultProps(): ITextCardShape["props"] { + return { + w: 100, + h: 50, + content: "", + extrainfo: "", + }; + } + + component(s: ITextCardShape) { + return ( + +
+

{s.props.content}

+

+ {s.props.extrainfo} +

+
+
+ ); + } + + indicator(shape: ITextCardShape) { + return ; + } +} diff --git a/apps/web/components/canvas/twitterCard.tsx b/apps/web/components/canvas/twitterCard.tsx new file mode 100644 index 00000000..c5582a98 --- /dev/null +++ b/apps/web/components/canvas/twitterCard.tsx @@ -0,0 +1,84 @@ +import { BaseBoxShapeUtil, HTMLContainer, TLBaseShape, toDomPrecision } from "tldraw"; + +type ITwitterCardShape = TLBaseShape< + "Twittercard", + { w: number; h: number; url: string } +>; + +export class twitterCardUtil extends BaseBoxShapeUtil { + static override type = "Twittercard" as const; + + getDefaultProps(): ITwitterCardShape["props"] { + return { + w: 500, + h: 550, + url: "", + }; + } + + component(s: ITwitterCardShape) { + return ( + + + + ); + } + + indicator(shape: ITwitterCardShape) { + return ; + } +} + +function TwitterPost({ + isInteractive, + width, + height, + url, +}: { + isInteractive: boolean; + width: number; + height: number; + url: string; +}) { + const link = (() => { + try { + const urlObj = new URL(url); + const path = urlObj.pathname; + return path; + } catch (error) { + console.error("Invalid URL", error); + return null; + } + })(); + + return ( +