From d2952cddd2810f5b3a3151d660c17418000b40b9 Mon Sep 17 00:00:00 2001 From: Fraxle Date: Tue, 26 May 2026 17:09:17 +0100 Subject: [PATCH] Fix CustomSelect panel to left-align by default, right-align only when it would overflow the viewport Co-Authored-By: Claude Sonnet 4.6 --- assets/css/ui.css | 10 ++++++++-- assets/js/components.js | 15 +++++++++++++-- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/assets/css/ui.css b/assets/css/ui.css index 7c7daf8..d2e4e41 100644 --- a/assets/css/ui.css +++ b/assets/css/ui.css @@ -475,8 +475,8 @@ .cs-panel { position: absolute; top: calc(100% + 4px); - right: 0; /* right edge flushes with button's right edge */ - left: auto; + left: 0; /* left edge flushes with button's left edge by default */ + right: auto; z-index: 1000; background: #fffcf2; border: 1.5px solid #c9b08a; @@ -487,6 +487,12 @@ overflow: hidden; } +/* Flip panel to right-align when left-align would overflow the viewport */ +.cs-panel.cs-panel--right { + left: auto; + right: 0; +} + /* Each option row */ .cs-option { display: block; diff --git a/assets/js/components.js b/assets/js/components.js index d151fdd..950b8aa 100644 --- a/assets/js/components.js +++ b/assets/js/components.js @@ -41,7 +41,9 @@ const html = htm.bind(h); // ------------------------------------------------------------------- export function CustomSelect({ value, options, onChange, hideLabel, hidingLabel, buttonLabel, isOn, noHide, groupedLeft, isLastChild, grpClass }) { const [open, setOpen] = useState(false); - const wrapRef = useRef(null); + const [flipRight, setFlipRight] = useState(false); + const wrapRef = useRef(null); + const panelRef = useRef(null); // Close on outside click or Escape useEffect(() => { @@ -60,6 +62,15 @@ export function CustomSelect({ value, options, onChange, hideLabel, hidingLabel, }; }, [open]); + // After the panel renders, check if it overflows the right viewport edge. + // If so, flip it to right-align; otherwise keep it left-aligned. + useEffect(() => { + if (!open) { setFlipRight(false); return; } + if (!panelRef.current) return; + const rect = panelRef.current.getBoundingClientRect(); + setFlipRight(rect.right > window.innerWidth - 8); + }, [open]); + // Build the label shown on the button const activeOpt = options.find(o => o.value === value); const btnLabel = buttonLabel || (isOn @@ -85,7 +96,7 @@ export function CustomSelect({ value, options, onChange, hideLabel, hidingLabel, ${open && html` -
+
${isOn && !noHide && html`