mirror of
https://github.com/NohamR/Pocket-Bird.git
synced 2026-05-25 19:59:38 +00:00
179 lines
5.0 KiB
JavaScript
179 lines
5.0 KiB
JavaScript
import {
|
|
makeElement,
|
|
makeDraggable,
|
|
makeClosable
|
|
} from './shared.js';
|
|
|
|
/**
|
|
* @typedef {Object} SavedStickyNote
|
|
* @property {string} id
|
|
* @property {string} site
|
|
* @property {string} content
|
|
* @property {number} top
|
|
* @property {number} left
|
|
*/
|
|
|
|
export class StickyNote {
|
|
/**
|
|
* @param {string} id
|
|
* @param {string} [site]
|
|
* @param {string} [content]
|
|
* @param {number} [top]
|
|
* @param {number} [left]
|
|
*/
|
|
constructor(id, site = "", content = "", top = 0, left = 0) {
|
|
this.id = id;
|
|
this.site = site;
|
|
this.content = content;
|
|
this.top = top;
|
|
this.left = left;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Parse URL parameters into a key-value map
|
|
* @param {string} url
|
|
* @returns {Record<string, string>}
|
|
*/
|
|
export function parseUrlParams(url) {
|
|
const queryString = url.split("?")[1];
|
|
if (!queryString) return {};
|
|
|
|
return queryString.split("&").reduce((params, param) => {
|
|
const [key, value] = param.split("=");
|
|
return { ...params, [key]: value };
|
|
}, {});
|
|
}
|
|
|
|
/**
|
|
* @param {StickyNote} stickyNote
|
|
* @returns {boolean} Whether the given sticky note is applicable to the current site/page
|
|
*/
|
|
export function isStickyNoteApplicable(stickyNote) {
|
|
const stickyNoteUrl = stickyNote.site;
|
|
const currentUrl = window.location.href;
|
|
const stickyNoteWebsite = stickyNoteUrl.split("?")[0];
|
|
const currentWebsite = currentUrl.split("?")[0];
|
|
|
|
if (stickyNoteWebsite !== currentWebsite) {
|
|
return false;
|
|
}
|
|
|
|
const stickyNoteParams = parseUrlParams(stickyNoteUrl);
|
|
const currentParams = parseUrlParams(currentUrl);
|
|
|
|
if (window.location.hostname === "www.youtube.com") {
|
|
if (currentParams.v !== undefined && currentParams.v !== stickyNoteParams.v) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @param {StickyNote} stickyNote
|
|
* @param {() => void} onSave
|
|
* @param {() => void} onDelete
|
|
* @returns {HTMLElement}
|
|
*/
|
|
export function renderStickyNote(stickyNote, onSave, onDelete) {
|
|
const noteElement = makeElement("birb-window");
|
|
noteElement.classList.add("birb-sticky-note");
|
|
|
|
// Create header
|
|
const header = makeElement("birb-window-header");
|
|
const titleDiv = makeElement("birb-window-title", "Sticky Note");
|
|
const closeButton = makeElement("birb-window-close", "x");
|
|
header.appendChild(titleDiv);
|
|
header.appendChild(closeButton);
|
|
|
|
// Create content
|
|
const content = makeElement("birb-window-content");
|
|
const textarea = document.createElement("textarea");
|
|
textarea.className = "birb-sticky-note-input";
|
|
textarea.style.width = "150px";
|
|
textarea.placeholder = "Write your notes here and they'll stick to the page!";
|
|
textarea.value = stickyNote.content;
|
|
content.appendChild(textarea);
|
|
|
|
noteElement.appendChild(header);
|
|
noteElement.appendChild(content);
|
|
|
|
noteElement.style.top = `${stickyNote.top}px`;
|
|
noteElement.style.left = `${stickyNote.left}px`;
|
|
document.body.appendChild(noteElement);
|
|
|
|
makeDraggable(header, true, (top, left) => {
|
|
stickyNote.top = top;
|
|
stickyNote.left = left;
|
|
onSave();
|
|
});
|
|
|
|
if (closeButton) {
|
|
makeClosable(() => {
|
|
if (stickyNote.content.trim() === "" || confirm("Are you sure you want to delete this sticky note?")) {
|
|
onDelete();
|
|
noteElement.remove();
|
|
}
|
|
}, closeButton);
|
|
}
|
|
|
|
if (textarea && textarea instanceof HTMLTextAreaElement) {
|
|
let saveTimeout;
|
|
// Save after debounce
|
|
textarea.addEventListener("input", () => {
|
|
stickyNote.content = textarea.value;
|
|
if (saveTimeout) {
|
|
clearTimeout(saveTimeout);
|
|
}
|
|
saveTimeout = setTimeout(() => {
|
|
onSave();
|
|
}, 250);
|
|
});
|
|
}
|
|
|
|
// On window resize
|
|
window.addEventListener("resize", () => {
|
|
const modTop = `${stickyNote.top - Math.min(window.innerHeight - noteElement.offsetHeight, stickyNote.top)}px`;
|
|
const modLeft = `${stickyNote.left - Math.min(window.innerWidth - noteElement.offsetWidth, stickyNote.left)}px`;
|
|
noteElement.style.transform = `scale(var(--birb-ui-scale)) translate(-${modLeft}, -${modTop})`;
|
|
});
|
|
|
|
return noteElement;
|
|
}
|
|
|
|
/**
|
|
* @param {StickyNote[]} stickyNotes
|
|
* @param {() => void} onSave
|
|
* @param {(note: StickyNote) => void} onDelete
|
|
*/
|
|
export function drawStickyNotes(stickyNotes, onSave, onDelete) {
|
|
// Remove all existing sticky notes
|
|
const existingNotes = document.querySelectorAll(".birb-sticky-note");
|
|
existingNotes.forEach(note => note.remove());
|
|
// Render all sticky notes
|
|
for (let stickyNote of stickyNotes) {
|
|
if (isStickyNoteApplicable(stickyNote)) {
|
|
renderStickyNote(stickyNote, onSave, () => onDelete(stickyNote));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {StickyNote[]} stickyNotes
|
|
* @param {() => void} onSave
|
|
* @param {(note: StickyNote) => void} onDelete
|
|
*/
|
|
export function createNewStickyNote(stickyNotes, onSave, onDelete) {
|
|
const id = Date.now().toString();
|
|
const site = window.location.href;
|
|
const stickyNote = new StickyNote(id, site, "");
|
|
const element = renderStickyNote(stickyNote, onSave, () => onDelete(stickyNote));
|
|
element.style.left = `${window.innerWidth / 2 - element.offsetWidth / 2}px`;
|
|
element.style.top = `${window.scrollY + window.innerHeight / 2 - element.offsetHeight / 2}px`;
|
|
stickyNote.top = parseInt(element.style.top, 10);
|
|
stickyNote.left = parseInt(element.style.left, 10);
|
|
stickyNotes.push(stickyNote);
|
|
onSave();
|
|
}
|