Skip to content

Commit

Permalink
feat: renderer folder
Browse files Browse the repository at this point in the history
  • Loading branch information
betterRunner committed Sep 6, 2021
1 parent 89a0440 commit 2e5dd50
Show file tree
Hide file tree
Showing 11 changed files with 1,250 additions and 15 deletions.
55 changes: 55 additions & 0 deletions src/content-scripts/renderer/dom/logo-icon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
const DOM_ICON_ID = "context-note-logo-icon";

let onClickEvt: EventListener;
/**
* generate a logo icon image dom on top left of selected text
* @param x mouse x
* @param y mouse y
* @param cb callback when clicked
* @returns
*/
export function genLogoIconAndRegisterClickCb(
x: number,
y: number,
cb: () => void
) {
const iconSize = 35;

const ele = document
.querySelector("body")
?.appendChild(document.createElement("img")) as HTMLImageElement;
if (!ele) return;

ele.id = DOM_ICON_ID;
ele.style.position = "absolute";
ele.style.left = `${x}px`;
ele.style.top = `${y - iconSize}px`;
ele.style.width = `${iconSize}px`;
ele.style.height = `${iconSize}px`;
ele.style.cursor = "pointer";
ele.style.zIndex = '100';

// get assets of exteison by `chrome.runtime.getURL`
// https://stackoverflow.com/questions/11804332/insert-an-image-in-chrome-extension
ele.src = chrome.runtime.getURL("assets/icon16.png");

onClickEvt = (e) => {
e.preventDefault();
e.stopPropagation();
cb?.();
clearLogoIcon();
};
ele.addEventListener("mouseup", onClickEvt);
}

export function clearLogoIcon() {
// delete all logo icon doms
const eles = document.querySelectorAll(`#${DOM_ICON_ID}`);
eles.forEach((ele) => {
onClickEvt && ele.removeEventListener("mouseup", onClickEvt);
const parent = ele.parentElement;
if (parent) {
parent.removeChild(ele);
}
});
}
133 changes: 133 additions & 0 deletions src/content-scripts/renderer/dom/rect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import { v4 as uuid } from "uuid";
import { Rect } from "@/types/common";
import {
DOMATTR_RECT_GROUP,
PREFIX_RECT,
PREFIX_RECT_GROUP,
} from "@/utils/constant";

function setHighlightStyle(ele: HTMLElement, rect: Rect) {
const PADDING = 6;
ele.style.position = "absolute";
ele.style.left = rect.x - PADDING / 2 + "px";
ele.style.top = rect.y - PADDING / 2 + "px";
ele.style.width = `${rect.width + PADDING}px`;
ele.style.height = `${rect.height + PADDING}px`;
ele.style.background = "yellow";
ele.style.opacity = "0.3";
ele.style.content = " ";
}

function findRelatedIds(
id: string,
idMap: { [key: string]: string[] }
): [string, string[]] {
for (const key in idMap) {
const ids = idMap[key] || [];
if (ids.includes(id)) {
return [key, ids];
}
}
return ["", []];
}

function setRectStyleWithIds(ids: string[], key: string, value: string) {
if (ids.length === 0) return;

const rects = (document.querySelectorAll(
(ids.map((id) => `#${id}`) as unknown) as string
) as unknown) as HTMLElement[];
rects.forEach((rect) => {
((rect.style as { [key: string]: any }) || {})[key] = value;
});
}

const groupRectIdsMap: { [key: string]: string[] } = {};
/**
* Generate the highlight rect doms and register their click event.
*/
export function genHighlightRects(
noteId: string,
rects: Rect[] = [],
clickCb?: (groupId: string) => void
) {
const groupId = noteId || `${PREFIX_RECT_GROUP}-${uuid()}`;
groupRectIdsMap[groupId] = [];
for (const rect of rects) {
const ele = document.createElement("div");
const id = `${PREFIX_RECT}-${uuid()}`;
ele.setAttribute("id", id);
ele.setAttribute(DOMATTR_RECT_GROUP, groupId);
// highlight the rect
setHighlightStyle(ele, rect);
document.querySelector("body")?.appendChild(ele);
groupRectIdsMap[groupId].push(ele.id);

// click event
document.addEventListener("mouseup", (event) => {
const withinBoundaries = event.composedPath().includes(ele);

if (withinBoundaries) {
const groupId = boldHighlightGroupRects(ele?.id, "");
clickCb?.(groupId);
} else {
unboldHighlightGroupRects(ele?.id);
}
});
}
return groupId;
}

/**
* Delete all rects with `noteId`, if `noteId` is not provided, delete all rects.
*/
export function delHighlightRects(noteId?: string | undefined) {
const query = !noteId ? `[${DOMATTR_RECT_GROUP}]` : `[${DOMATTR_RECT_GROUP}=${noteId}]`
const rectDoms = document.querySelectorAll(query);
rectDoms.forEach(dom => {
dom.parentElement?.removeChild(dom);
})
}

/**
* Bold the group highlight rects.
* @param id one id of the group rects
* @param groupId the group id
* @param scrollIntoView if need scrollIntoView when bolding
* @returns groupId
*/
export function boldHighlightGroupRects(
id?: string,
groupId?: string,
scrollIntoView?: boolean
): string {
// set all related rects border to dotted
let relatedIds: string[];
if (groupId) {
relatedIds = groupRectIdsMap[groupId] || [];
} else {
[groupId, relatedIds] = findRelatedIds(id || "", groupRectIdsMap);
}
setTimeout(() => {
setRectStyleWithIds(relatedIds, "border", "2px dotted #000");
});
if (groupId) {
if (scrollIntoView) {
// scroll to first rect
const firstRectId = relatedIds?.[0];
const firstRect = document.querySelector(`#${firstRectId}`);
firstRect && firstRect.scrollIntoView({ block: "center" });
}
}
return groupId;
}

/**
* Unbold the group highlight rects.
* @param id one id of the group rects.
*/
export function unboldHighlightGroupRects(id: string) {
// set all related rects border to none
const [_, relatedIds] = findRelatedIds(id, groupRectIdsMap);
setRectStyleWithIds(relatedIds, "border", "none");
}
15 changes: 0 additions & 15 deletions src/content-scripts/renderer/highlight.ts

This file was deleted.

47 changes: 47 additions & 0 deletions src/content-scripts/renderer/popup/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<template>
<div v-show="visible">
<NoteBook v-clickoutside="handleClickOutside"></NoteBook>
</div>
</template>

<script lang="ts">
import { defineComponent, provide, ref, reactive } from "vue";
import NoteBook from "./note-book/index.vue";
import { Note } from "@/types/note";
import { Tag } from "@/types/tag";
import { Storage } from "@/types/storage";
import { get } from "@/utils/storage";
import { StorageKeys } from "@/utils/constant";
export default defineComponent({
components: {
NoteBook,
},
setup() {
const visible = ref(false);
const handleClickOutside = () => {
visible.value = false;
};
// global reading `notes` and `tags` from storage and provide to sub components.
const storage = reactive<Storage>({
[StorageKeys.notes]: [],
[StorageKeys.tags]: [],
});
get(StorageKeys.notes).then((res) => {
storage.notes = (res as Note[]) || [];
});
get(StorageKeys.tags).then((res) => {
storage.tags = (res as Tag[]) || [];
});
provide("storage", storage);
return {
visible,
handleClickOutside,
};
},
});
</script>

<style scoped></style>
64 changes: 64 additions & 0 deletions src/content-scripts/renderer/popup/note-book/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<template>
<div class="note-book-wrapper">
<NoteList></NoteList>
<TagBook
v-show="!!curNoteId"
:noteId="curNoteId"
:coor="tagBookCoor"
@close="handleCloseTagBook"
></TagBook>
</div>
</template>

<script lang="ts">
import { ref } from "vue";
import mitt from "@/utils/mitt";
import { Coor } from "@/types/common";
import TagBook from "../tag-book/index.vue";
import NoteList from "./note-list.vue";
export default {
components: {
NoteList,
TagBook,
},
setup(props) {
const tagBookCoor = ref<Coor>({ x: 0, y: 0 });
const curNoteId = ref("");
mitt.on("open-tag-book", ({ noteId, coor }: any) => {
if (coor) {
// `setTimeout` to let `handleCloseTagBook` trigger first.
setTimeout(() => {
tagBookCoor.value = coor as Coor;
curNoteId.value = noteId;
});
}
});
const handleCloseTagBook = () => {
curNoteId.value = "";
};
return {
tagBookCoor,
curNoteId,
handleCloseTagBook,
};
},
};
</script>

<style scoped>
.note-book-wrapper {
position: fixed;
right: 0px;
top: 0px;
width: 500px;
min-width: 500px;
height: 100vh;
overflow-y: scroll;
background-color: #646cff80;
opacity: 1;
border-radius: 10px;
z-index: 9999;
}
</style>
Loading

0 comments on commit 2e5dd50

Please sign in to comment.