From e3ace9f2fe8e927f7bc452b96087dd8f4f135c01 Mon Sep 17 00:00:00 2001 From: IRHM <37304121+IRHM@users.noreply.github.com> Date: Sun, 22 Sep 2024 13:43:09 +0100 Subject: [PATCH 1/4] Create stayInView action Currently will only shift elements back into view if out of bounds on the left. Not necessary at the moment to support oob on other sides, or support resizing to fit. Maybe in the future when it's used more. --- src/lib/actions/stayInView.ts | 65 +++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 src/lib/actions/stayInView.ts diff --git a/src/lib/actions/stayInView.ts b/src/lib/actions/stayInView.ts new file mode 100644 index 00000000..bab4aebc --- /dev/null +++ b/src/lib/actions/stayInView.ts @@ -0,0 +1,65 @@ +export interface StayInViewOptions { + /** + * If the `node` contains an element (like an arrow for menus), + * that should be shifted over to account for any shifting + * of the `node` itself, then pass a selector for it here. + */ + elToShiftSelector?: string; +} + +export default function stayInView(node: HTMLElement, opts: StayInViewOptions) { + console.log("stayInView: Initial opts:", opts); + let { elToShiftSelector } = opts; + let viewDeb: ReturnType; + + /** + * Move element to in view, if it isn't. + * + * Currently only supports moving `node` back into view if oob + * on the left. May need to support other sides and/or resizing + * when `node` still wont fit after shifting it in the future. + */ + const getInView = () => { + const nrect = node.getBoundingClientRect(); + const brect = document.body.getBoundingClientRect(); + console.log("stayInView->getInView: Called.", nrect, brect); + if (nrect.x <= brect.x) { + const diff = nrect.x - brect.x + 10; + console.log( + "stayInView->getInView: Node is out of bounds on the left, shifting forwards to:", + diff + ); + node.style.left = `${diff}px`; + if (elToShiftSelector) { + const elToShift = node.querySelector(elToShiftSelector) as HTMLElement; + if (elToShift) { + console.log("stayInView->getInView: Shifting elToShift."); + const nrectNew = node.getBoundingClientRect(); + const arrowDiff = nrectNew.left - nrect.left; + elToShift.style.left = `${elToShift.offsetLeft - arrowDiff}px`; + } else { + console.warn("elToShift not found.", elToShiftSelector); + } + } + } + }; + + const getInViewDeb = () => { + clearTimeout(viewDeb); + viewDeb = setTimeout(getInView, 200); + }; + + window.addEventListener("resize", getInViewDeb); + getInView(); + + return { + update(opts: StayInViewOptions) { + console.log("updated", opts); + elToShiftSelector = opts.elToShiftSelector; + getInView(); + }, + destroy() { + window.removeEventListener("resize", getInViewDeb); + } + }; +} From f033a506324bc5a88a4a653438c1847d0459b64c Mon Sep 17 00:00:00 2001 From: IRHM <37304121+IRHM@users.noreply.github.com> Date: Sun, 22 Sep 2024 13:44:30 +0100 Subject: [PATCH 2/4] stayInView: Make logs debug scope, fix log in update func --- src/lib/actions/stayInView.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lib/actions/stayInView.ts b/src/lib/actions/stayInView.ts index bab4aebc..36b8d85a 100644 --- a/src/lib/actions/stayInView.ts +++ b/src/lib/actions/stayInView.ts @@ -8,7 +8,7 @@ export interface StayInViewOptions { } export default function stayInView(node: HTMLElement, opts: StayInViewOptions) { - console.log("stayInView: Initial opts:", opts); + console.debug("stayInView: Initial opts:", opts); let { elToShiftSelector } = opts; let viewDeb: ReturnType; @@ -22,10 +22,10 @@ export default function stayInView(node: HTMLElement, opts: StayInViewOptions) { const getInView = () => { const nrect = node.getBoundingClientRect(); const brect = document.body.getBoundingClientRect(); - console.log("stayInView->getInView: Called.", nrect, brect); + console.debug("stayInView->getInView: Called.", nrect, brect); if (nrect.x <= brect.x) { const diff = nrect.x - brect.x + 10; - console.log( + console.debug( "stayInView->getInView: Node is out of bounds on the left, shifting forwards to:", diff ); @@ -33,7 +33,7 @@ export default function stayInView(node: HTMLElement, opts: StayInViewOptions) { if (elToShiftSelector) { const elToShift = node.querySelector(elToShiftSelector) as HTMLElement; if (elToShift) { - console.log("stayInView->getInView: Shifting elToShift."); + console.debug("stayInView->getInView: Shifting elToShift."); const nrectNew = node.getBoundingClientRect(); const arrowDiff = nrectNew.left - nrect.left; elToShift.style.left = `${elToShift.offsetLeft - arrowDiff}px`; @@ -54,7 +54,7 @@ export default function stayInView(node: HTMLElement, opts: StayInViewOptions) { return { update(opts: StayInViewOptions) { - console.log("updated", opts); + console.debug("stayInView: Opts updated", opts); elToShiftSelector = opts.elToShiftSelector; getInView(); }, From 7a3e5f4354998744bebb3ecf54c1e0ab9c78fef4 Mon Sep 17 00:00:00 2001 From: IRHM <37304121+IRHM@users.noreply.github.com> Date: Sun, 22 Sep 2024 13:46:50 +0100 Subject: [PATCH 3/4] norm.scss: `.menu` support using an element with .arrow class instead of ::before pseudoelement when more control is needed more control = need to be able to move it in js. Still supports using ::before, so not all menus need updating and can stay simple, just the menus needing more control can start using an actual element for the arrow. --- src/norm.scss | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/norm.scss b/src/norm.scss index 25eb26a1..83646766 100644 --- a/src/norm.scss +++ b/src/norm.scss @@ -283,7 +283,11 @@ z-index: 50; // The lil arrow, adjust its pos under each specific menu. - &:before { + &:not(:has(> .arrow)):before, + :global(.arrow) { + // Works by showing arrow in ::before pseudoelement, + // or if more control is needed, applies to any .arrow + // element. content: ""; position: absolute; bottom: 100%; From b2f817b639d6baf39f524833436437f635dac55a Mon Sep 17 00:00:00 2001 From: IRHM <37304121+IRHM@users.noreply.github.com> Date: Sun, 22 Sep 2024 13:48:52 +0100 Subject: [PATCH 4/4] TagMenu: Use stayInView action, use an element for arrow TagMenu uses an element for the arrow now so the stayInView action can control its position. --- src/lib/tag/TagMenu.svelte | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/lib/tag/TagMenu.svelte b/src/lib/tag/TagMenu.svelte index 4f7c9ada..abf3b67c 100644 --- a/src/lib/tag/TagMenu.svelte +++ b/src/lib/tag/TagMenu.svelte @@ -5,6 +5,7 @@ import type { Tag as TagT } from "@/types"; import Tag from "./Tag.svelte"; import DeleteTagModal from "./DeleteTagModal.svelte"; + import stayInView from "../actions/stayInView"; export let titleText: string | undefined = undefined; export let classes: string | undefined = undefined; @@ -29,7 +30,8 @@ } -
+
.arrow" }}> +

{titleText ? titleText : "my tags"}

@@ -86,7 +88,7 @@ width: 200px; right: 47px; - &:before { + .arrow { left: 78px; } @@ -94,7 +96,7 @@ top: 50px; right: -78px; - &:before { + .arrow { left: 87px; /* The place where this button will be is always dark, so white works for both themes */ border-bottom-color: white;