diff options
Diffstat (limited to 'extension/src')
-rw-r--r-- | extension/src/assets/create_folder.svg | 1 | ||||
-rw-r--r-- | extension/src/assets/move.svg | 1 | ||||
-rw-r--r-- | extension/src/components/Body.tsx | 19 | ||||
-rw-r--r-- | extension/src/components/Bookmark-backup.txt | 161 | ||||
-rw-r--r-- | extension/src/components/Bookmark.tsx | 129 | ||||
-rw-r--r-- | extension/src/index.css | 24 |
6 files changed, 160 insertions, 175 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/assets/move.svg b/extension/src/assets/move.svg new file mode 100644 index 0000000..80dd312 --- /dev/null +++ b/extension/src/assets/move.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="currentColor"><path d="M480-80 310-250l57-57 73 73v-206H235l73 72-58 58L80-480l169-169 57 57-72 72h206v-206l-73 73-57-57 170-170 170 170-57 57-73-73v206h205l-73-72 58-58 170 170-170 170-57-57 73-73H520v205l72-73 58 58L480-80Z"/></svg>
\ No newline at end of file diff --git a/extension/src/components/Body.tsx b/extension/src/components/Body.tsx index e575d89..70f8fce 100644 --- a/extension/src/components/Body.tsx +++ b/extension/src/components/Body.tsx @@ -1,7 +1,7 @@ import React, {useEffect, useState} from "react"; import SettingsEditor from "./SettingsEditor.tsx"; import SettingsIcon from "../assets/settings.svg?react"; -import EditIcon from "../assets/edit.svg?react"; +import EditIcon from "../assets/move.svg?react"; import BookmarkTreeNode = browser.bookmarks.BookmarkTreeNode; import FolderBody from "./FolderBody.tsx"; import {defaultSettings, ISettings, loadSettings, writeSettings} from "../Settings.ts"; @@ -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<[BookmarkTreeNode | null, (arg0: BookmarkTreeNode | null) => void]>([ + null, + () => {} + ]) /** * 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<BookmarkTreeNode | null>(null); 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>) @@ -55,7 +63,7 @@ function Body() { <div id={"action-area"}> {settings.editMode && <span>Move mode: Drag bookmarks to change order</span>} <button onClick={_ => setSettings({...settings, editMode: !settings.editMode})}> - <EditIcon fill={settings.editMode? "lime" : "currentColor"}/> + <EditIcon className={settings.editMode? "enabled" : ""}/> </button> <button onClick={_ => setSettingsOpen(!settingsOpen)}> <SettingsIcon/> @@ -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..5a406eb 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,12 @@ 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 [bgColorPriority, setBgColorPriority] = React.useState(0); + let [activeDrag, setActiveDrag] = React.useContext(ActiveDrag); + let [dropRight, setDropRight] = React.useState(false); + let [dropLeft, setDropLeft] = React.useState(false); + let [dropCenter, setDropCenter] = React.useState(false); + let [thisDragged, setThisDragged] = React.useState(false); useEffect(() => { faviconURL(props.data).then(r => { @@ -25,23 +32,49 @@ function Bookmark(props: {data: BookmarkTreeNode}) { }) }, []); + useEffect(() => { + setDropLeft(false); + setDropRight(false); + setDropCenter(false); + }, [activeDrag]); + function handleImageLoad(e: SyntheticEvent<HTMLImageElement, Event>) { if (e.currentTarget.naturalWidth >= 75 || favicon!.startsWith("data:image/svg+xml")) { setIconMode("large") - } else if(!bgColor) { + } else if(bgColorPriority < 2) { setBgColor(new ColorThief().getColor(e.currentTarget)) + setBgColorPriority(2); } } + function handleDragStart(e: React.DragEvent<HTMLAnchorElement>) { + // e.dataTransfer.setData("text/bm-id", props.data.id); + // setActiveDrag(true); + console.log("data", e.dataTransfer.getData("text/bm-id").toString()) + } + + function handleDrag(e: React.DragEvent<HTMLAnchorElement>) { + // e.dataTransfer.setData("text/bm-id", props.data.id); + setActiveDrag(props.data); + setThisDragged(true); + // e.dataTransfer.dropEffect = "move"; + } + + function handleDragEnd() { + setActiveDrag(null); + setThisDragged(false); + } + return( <div className={"bookmark"}> - <a draggable={settings.editMode} href={props.data.url}> + <a draggable={settings.editMode} href={props.data.url} onDragStart={handleDragStart} onDrag={handleDrag} onDragEnd={handleDragEnd}> <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": { let url = new URL(props.data.url!); - if (!bgColor) { + if (bgColorPriority < 1) { setBgColor(hashStringToColor(url.hostname)); + setBgColorPriority(1); } return (<span className={"letter"}>{url.hostname.charAt(0)}</span>) } @@ -55,6 +88,92 @@ function Bookmark(props: {data: BookmarkTreeNode}) { </div> <span>{props.data.title}</span> </a> + {activeDrag && !thisDragged && ( + <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(activeDrag.id, { + parentId: props.data.parentId, + index: props.data.index + }) + location.reload() + }} + 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(activeDrag.id, { + parentId: props.data.parentId, + index: (props.data.index! + 1) + }) + location.reload() + e.preventDefault() + }} + 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(); + chrome.bookmarks.create({ + // type: "folder", + parentId: props.data.parentId, + index: props.data.index, + title: "New Folder" + }).then(r => { + getBrowser().bookmarks.move(props.data.id, {parentId: r.id}); + getBrowser().bookmarks.move(activeDrag?.id, {parentId: r.id}); + location.reload() + }) + }} + style={dropCenter ? undefined : {opacity: 0}} + // hidden={!dropCenter} + > + <CreateFolderIcon/> + </div> + </div> + )} </div> ); } @@ -68,7 +187,7 @@ function Bookmark(props: {data: BookmarkTreeNode}) { async function faviconURL(data: BookmarkTreeNode) { let i = (await getBrowser().storage.local.get("icon-cache-"+data.id))["icon-cache-"+data.id]; if (i) return i - if (i == null) return i; + // if (i == null) return i; const url = new URL('https://www.google.com/s2/favicons'); url.searchParams.set("sz", "256"); diff --git a/extension/src/index.css b/extension/src/index.css index a85aeaf..f240f82 100644 --- a/extension/src/index.css +++ b/extension/src/index.css @@ -120,6 +120,7 @@ body > .folderBody { .icon-box > img { width: 100%; border-radius: 10px; + pointer-events: none; } .icon-box > svg { @@ -191,7 +192,7 @@ a { /* Drop targets */ .drop-targets { - pointer-events: none; + /*pointer-events: none;*/ position: absolute; top: 0; /* bottom: 0; */ @@ -215,7 +216,7 @@ a { align-items: center; > div { - background-color: aqua; + background-color: white; height: 50%; width: 4px; } @@ -231,7 +232,7 @@ a { align-items: center; > div { - background-color: aqua; + background-color: white; width: 4px; height: 50%; } @@ -248,13 +249,28 @@ 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: #FFF; + width: 50px; + height: 50px; + border-radius: 10px; + padding: 10px; + margin-bottom: 14px; + fill: black; + } +} + +.enabled { + background-color: white; + fill: black; } |