diff options
Diffstat (limited to 'extension/src')
-rw-r--r-- | extension/src/assets/create_folder.svg | 1 | ||||
-rw-r--r-- | extension/src/components/Body.tsx | 15 | ||||
-rw-r--r-- | extension/src/components/Bookmark-backup.txt | 161 | ||||
-rw-r--r-- | extension/src/components/Bookmark.tsx | 92 | ||||
-rw-r--r-- | extension/src/index.css | 17 |
5 files changed, 116 insertions, 170 deletions
diff --git a/extension/src/assets/create_folder.svg b/extension/src/assets/create_folder.svg new file mode 100644 index 0000000..3e94c9c --- /dev/null +++ b/extension/src/assets/create_folder.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="M560-320h80v-80h80v-80h-80v-80h-80v80h-80v80h80v80ZM160-160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800h240l80 80h320q33 0 56.5 23.5T880-640v400q0 33-23.5 56.5T800-160H160Z"/></svg>
\ No newline at end of file diff --git a/extension/src/components/Body.tsx b/extension/src/components/Body.tsx index e575d89..07b0259 100644 --- a/extension/src/components/Body.tsx +++ b/extension/src/components/Body.tsx @@ -9,9 +9,15 @@ import {getBrowser} from "../main.tsx"; export const Settings = React.createContext<[ISettings, (arg0: ISettings) => void]>([ - defaultSettings, - () => {} -]); + defaultSettings, + () => {} + ]); + +export const ActiveDrag = + React.createContext<[boolean, (arg0: boolean) => void]>([ + false, + () => {} + ]) /** * A component for the full body of the application @@ -22,6 +28,7 @@ function Body() { const [settings, setSettings] = useState<ISettings>(defaultSettings); const [selectedBookmarkTree, setSelectedBookmarkTree] = useState<BookmarkTreeNode[]>([]) const [fullBookmarkTree, setFullBookmarkTree] = useState<BookmarkTreeNode[] | null>([]) + const [activeDrag, setActiveDrag] = useState(false); useEffect(() => { loadSettings().then(r => { @@ -47,6 +54,7 @@ function Body() { return ( <Settings.Provider value={[settings!, setSettings]}> + <ActiveDrag.Provider value={[activeDrag, setActiveDrag]}> {(() => {switch (settings.backgroundMode) { case "color": return (<style>{"body {background-color: " + settings.backgroundColor + "; }"}</style>) case "image": return (<style>{"body {background-image: url(\"" + settings.backgroundImage + "\"); }"}</style>) @@ -63,6 +71,7 @@ function Body() { </div> <SettingsEditor tree={fullBookmarkTree!} isOpen={[settingsOpen, setSettingsOpen]}/> {selectedBookmarkTree[0] && (<FolderBody data={selectedBookmarkTree[0]}/>)} + </ActiveDrag.Provider> </Settings.Provider> ) } diff --git a/extension/src/components/Bookmark-backup.txt b/extension/src/components/Bookmark-backup.txt deleted file mode 100644 index 4d1eb60..0000000 --- a/extension/src/components/Bookmark-backup.txt +++ /dev/null @@ -1,161 +0,0 @@ -import BookmarkTreeNode = browser.bookmarks.BookmarkTreeNode; -import React, {SyntheticEvent, useEffect} from "react"; -import {getBrowser} from "../main.tsx"; -import {Settings} from "./Body.tsx"; -import react from "@vitejs/plugin-react"; - -/** - * A component for a single bookmark - * - * @param props.data The BookmarkTreeNode with the data for the bookmark - */ -function Bookmark(props: {data: BookmarkTreeNode}) { - let [favicon, setFavicon] = React.useState<string | null>(null); - let [iconMode, setIconMode] = React.useState<"large" | "small" | "letter">("letter"); - let [settings, setSettings] = React.useContext(Settings); - let [bgColor, setBgColor] = React.useState<[number, number, number] | null>(null) - // let [dropRight, setDropRight] = React.useState(false); - // let [dropLeft, setDropLeft] = React.useState(false); - // let [dropCenter, setDropCenter] = React.useState(false); - - useEffect(() => { - setBgColor([255, 0, 0]) - // faviconURL(props.data.url).then(o => o && setFavicon(o)) - faviconURL(props.data).then(r => { - if (r) { - setFavicon(r) - setIconMode("small"); - } - }) - }, []); - - // useEffect(() => { - // console.log("signal received", dropCenter) - // }, [dropCenter]); - - function handleImageLoad(e: SyntheticEvent<HTMLImageElement, Event>) { - if (e.currentTarget.naturalWidth >= 75 || favicon!.startsWith("data:image/svg+xml")) { - setIconMode("large") - } - } - - return( - <div className={"bookmark"}> - <a draggable={settings.editMode} href={props.data.url} - // onDragStart={e => { - // console.log("dragStart") - // e.dataTransfer.dropEffect = "move"; - // e.dataTransfer.setData("bm-id", props.data.id); - // }} - > - <div className={"icon-box " + (iconMode)} - style={bgColor ? {"--icon-bg": `rgba(${bgColor[0]}, ${bgColor[1]}, ${bgColor[2]}, 0.2)`} as React.CSSProperties : undefined}> - {favicon ? ( - <img alt="Bookmark icon" - draggable={false} - src={favicon} - onLoad={handleImageLoad} - /> - ) : ( - <span className={"letter"}>{new URL(props.data.url!).hostname.charAt(0)}</span> - )} - - </div> - <span>{props.data.title}</span> - </a> - {/*<div className={"drop-targets"}>*/} - {/* <div*/} - {/* className={"left"}*/} - {/* onDragEnter={_ => setDropLeft(true)}*/} - {/* onDragLeave={_ => setDropRight(false)}*/} - {/* onDrop={e => getBrowser().bookmarks.move(e.dataTransfer.getData("bm-id"), {parentId: props.data.parentId, index: props.data.index})}*/} - {/* style={dropLeft ? undefined : {visibility: "hidden"}}*/} - {/* // hidden={!dropLeft}*/} - {/* >*/} - {/* <div/>*/} - {/* </div>*/} - {/* <div*/} - {/* className={"right"}*/} - {/* onDragEnter={_ => setDropLeft(true)}*/} - {/* onDragLeave={_ => setDropRight(false)}*/} - {/* onDrop={e => getBrowser().bookmarks.move(e.dataTransfer.getData("bm-id"), {parentId: props.data.parentId, index: (props.data.index! + 1)})}*/} - {/* style={dropRight ? undefined : {visibility: "hidden"}}*/} - {/* // hidden={!dropRight}*/} - {/* >*/} - {/* <div/>*/} - {/* </div>*/} - {/* <div*/} - {/* className={"center"}*/} - {/* onDragOver={e => {*/} - {/* e.preventDefault()*/} - {/* // console.log("dropped")*/} - {/* }}*/} - {/* onDragEnter={e => {*/} - {/* e.preventDefault()*/} - {/* setDropCenter(true)*/} - {/* console.log("enter")*/} - {/* }}*/} - {/* onDragLeave={_ => {*/} - {/* setDropCenter(false)*/} - {/* console.log("exit")*/} - {/* }}*/} - {/* onDrop={e => {*/} - {/* e.preventDefault();*/} - {/* console.log("dropped")*/} - {/* }}*/} - {/* style={dropCenter ? undefined : {visibility: "hidden"}}*/} - {/* // hidden={!dropCenter}*/} - {/* >*/} - {/* <span>+</span>*/} - {/* </div>*/} - {/*</div>*/} - </div> - ); -} - -/** - * Gets the icon for a bookmark - * - * @param data The URL of the link - * @return The URL of the icon - */ -async function faviconURL(data: BookmarkTreeNode) { - let i = (await getBrowser().storage.local.get("icon-cache-"+data.id))["icon-cache-"+data.id]; - if (i) return i - - const url = new URL('https://www.google.com/s2/favicons'); - url.searchParams.set("sz", "256"); - url.searchParams.set("domain_url", data.url!); - let resp = await fetch(url) - let imgData = resp.ok ? await toDataURL(url.toString()) : null; - getBrowser().storage.local.set({["icon-cache-"+data.id]: imgData}); - return imgData; -} - -function toDataURL(url:string):string { - // @ts-ignore - return fetch(url) - .then(response => response.blob()) - .then(blob => new Promise((resolve, reject) => { - const reader = new FileReader() - reader.onloadend = () => resolve(reader.result) - reader.onerror = reject - reader.readAsDataURL(blob) - })) -} - -// function hashColor(url:string) { -// hashCode(String) -// } -// -// function hashCode(str: string) { -// let hash = 0; -// for (let i = 0, len = str.length; i < len; i++) { -// let chr = str.charCodeAt(i); -// hash = (hash << 5) - hash + chr; -// hash |= 0; // Convert to 32bit integer -// } -// return hash; -// } - -export default Bookmark;
\ No newline at end of file diff --git a/extension/src/components/Bookmark.tsx b/extension/src/components/Bookmark.tsx index d1c1756..fc73387 100644 --- a/extension/src/components/Bookmark.tsx +++ b/extension/src/components/Bookmark.tsx @@ -1,8 +1,9 @@ import BookmarkTreeNode = browser.bookmarks.BookmarkTreeNode; import React, {SyntheticEvent, useEffect} from "react"; import {getBrowser} from "../main.tsx"; -import {Settings} from "./Body.tsx"; +import {ActiveDrag, Settings} from "./Body.tsx"; import ColorThief from 'colorthief' +import CreateFolderIcon from "../assets/create_folder.svg?react" import react from "@vitejs/plugin-react"; /** @@ -15,6 +16,10 @@ function Bookmark(props: {data: BookmarkTreeNode}) { let [iconMode, setIconMode] = React.useState<"large" | "small" | "letter">("letter"); let [settings, setSettings] = React.useContext(Settings); let [bgColor, setBgColor] = React.useState<[number, number, number] | null>(null) + let [activeDrag, setActiveDrag] = React.useContext(ActiveDrag); + let [dropRight, setDropRight] = React.useState(false); + let [dropLeft, setDropLeft] = React.useState(false); + let [dropCenter, setDropCenter] = React.useState(false); useEffect(() => { faviconURL(props.data).then(r => { @@ -33,9 +38,15 @@ function Bookmark(props: {data: BookmarkTreeNode}) { } } + function handleDrag(e: React.DragEvent<HTMLAnchorElement>) { + e.dataTransfer.dropEffect = "move"; + e.dataTransfer.setData("bm-id", props.data.id); + setActiveDrag(true); + } + return( <div className={"bookmark"}> - <a draggable={settings.editMode} href={props.data.url}> + <a draggable={settings.editMode} href={props.data.url} onDrag={handleDrag} onDragEnd={_ => setActiveDrag(false)}> <div className={"icon-box " + (iconMode)} style={bgColor ? {"--icon-bg": `rgba(${bgColor[0]}, ${bgColor[1]}, ${bgColor[2]}, 0.2)`} as React.CSSProperties : undefined}> {(() => { switch (iconMode) { case "letter": { @@ -55,6 +66,83 @@ function Bookmark(props: {data: BookmarkTreeNode}) { </div> <span>{props.data.title}</span> </a> + {activeDrag && ( + <div className={"drop-targets"}> + <div + className={"left"} + onDragOver={e => { + e.preventDefault() + setDropLeft(true) + }} + onDragEnter={e =>{ + e.preventDefault() + }} + onDragLeave={_ => { + setDropLeft(false) + }} + onDrop={e => { + getBrowser().bookmarks.move(e.dataTransfer.getData("bm-id"), { + parentId: props.data.parentId, + index: props.data.index + }) + console.log("dropped") + }} + style={dropLeft ? undefined : {opacity: 0}} + // hidden={!dropLeft} + > + <div/> + </div> + <div + className={"right"} + onDragOver={e => { + e.preventDefault(); + setDropRight(true); + }} + onDragEnter={e => { + e.preventDefault(); + }} + onDragLeave={_ => { + setDropRight(false) + }} + onDrop={e => { + getBrowser().bookmarks.move(e.dataTransfer.getData("bm-id"), { + parentId: props.data.parentId, + index: (props.data.index! + 1) + }) + e.preventDefault() + console.log("dropped right", e.dataTransfer.getData("bm-id")) + }} + style={dropRight ? undefined : {opacity: 0}} + // hidden={!dropRight} + > + <div/> + </div> + <div + className={"center"} + onDragOver={e => { + e.preventDefault() + setDropCenter(true) + // console.log("dropped") + }} + onDragEnter={e => { + e.preventDefault() + // console.log("enter") + }} + onDragLeave={_ => { + setDropCenter(false) + // console.log("exit") + }} + onDrop={e => { + e.preventDefault(); + console.log("dropped") + }} + style={dropCenter ? undefined : {opacity: 0}} + // hidden={!dropCenter} + > + <CreateFolderIcon/> + </div> + </div> + )} </div> ); } diff --git a/extension/src/index.css b/extension/src/index.css index a85aeaf..a14e0d6 100644 --- a/extension/src/index.css +++ b/extension/src/index.css @@ -191,7 +191,7 @@ a { /* Drop targets */ .drop-targets { - pointer-events: none; + /*pointer-events: none;*/ position: absolute; top: 0; /* bottom: 0; */ @@ -215,7 +215,7 @@ a { align-items: center; > div { - background-color: aqua; + background-color: white; height: 50%; width: 4px; } @@ -231,7 +231,7 @@ a { align-items: center; > div { - background-color: aqua; + background-color: white; width: 4px; height: 50%; } @@ -248,13 +248,22 @@ a { align-items: center; > span { - color: aqua; + /*color: aqua;*/ font-size: 100px; /* background-color: aqua; */ /* aspect-ratio: 1 / 1; */ /* height: 100px; */ font-family: monospace; } + + > svg { + background-color: #000000ba; + width: 65px; + height: 65px; + border-radius: 10px; + padding: 10px; + margin-bottom: 14px; + } } |