aboutsummaryrefslogtreecommitdiff
path: root/packages/components/src
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2025-04-07 15:20:36 -0400
committerbobzel <zzzman@gmail.com>2025-04-07 15:20:36 -0400
commit40b8e5c30e7251db6258cac5b4ed5fb4a6a2418d (patch)
tree89ece841da05670fdb4fc429ed0c46b51b539954 /packages/components/src
parenta3d6fae1482c61ca725d0a103f13b621aa32b3e3 (diff)
added multiToggle option to allow popup to stay up until explicitly untoggled. fixed multitoggle to honor toggleStatus. fixed popup to follow target. fixed setting text highlight background fro popup menu. fixed flashcardui buttons to have background sized properly.
Diffstat (limited to 'packages/components/src')
-rw-r--r--packages/components/src/components/MultiToggle/MultiToggle.tsx16
-rw-r--r--packages/components/src/components/Popup/Popup.tsx43
2 files changed, 44 insertions, 15 deletions
diff --git a/packages/components/src/components/MultiToggle/MultiToggle.tsx b/packages/components/src/components/MultiToggle/MultiToggle.tsx
index 0a6fb81c9..c1d610c34 100644
--- a/packages/components/src/components/MultiToggle/MultiToggle.tsx
+++ b/packages/components/src/components/MultiToggle/MultiToggle.tsx
@@ -17,6 +17,7 @@ export interface IMultiToggleProps extends IGlobalProps {
selectedItems?: (string | number) | (string | number)[];
onSelectionChange?: (val: (string | number) | (string | number)[], added: boolean) => unknown;
toggleStatus?: boolean;
+ showUntilToggle?: boolean; // whether popup stays open when background is clicked. muyst click toggle button tp close it.
}
function promoteToArrayOrUndefined(d: (string | number)[] | (string | number) | undefined) {
@@ -32,12 +33,23 @@ export const MultiToggle = (props: IMultiToggleProps) => {
init = false;
const [selectedItemsLocal, setSelectedItemsLocal] = useState(initVal as (string | number) | (string | number)[]);
- const { items, selectedItems = selectedItemsLocal, tooltip, tooltipPlacement = 'top', onSelectionChange, color, background } = props;
+ const {
+ items, //
+ selectedItems = selectedItemsLocal,
+ tooltip,
+ toggleStatus,
+ tooltipPlacement = 'top',
+ onSelectionChange,
+ color,
+ background,
+ } = props;
const itemsMap = new Map();
items.forEach(item => itemsMap.set(item.val, item));
return (
<div className="multiToggle-container">
<Popup
+ isOpen={toggleStatus}
+ multitoggle={true} // this is used to indicate that this is a multi toggle, so it can be styled differently in the popup
toggle={
<div style={{ position: 'relative' }}>
<IconButton
@@ -53,7 +65,7 @@ export const MultiToggle = (props: IMultiToggleProps) => {
{promoteToArray(selectedItems).length < 2 ? null : <div style={{ position: 'absolute', top: '0', left: '0', color: color ?? Colors.MEDIUM_BLUE }}>+</div>}
</div>
}
- isToggle={true}
+ showUntilToggle={props.showUntilToggle ?? true}
toggleFunc={() => {
const selItem = items.find(item => promoteToArray(selectedItems).includes(item.val));
selItem && setSelectedItemsLocal([selItem.val]);
diff --git a/packages/components/src/components/Popup/Popup.tsx b/packages/components/src/components/Popup/Popup.tsx
index 9e72ece87..9e91a75cf 100644
--- a/packages/components/src/components/Popup/Popup.tsx
+++ b/packages/components/src/components/Popup/Popup.tsx
@@ -3,6 +3,7 @@ import { IGlobalProps, Placement, Size } from '../../global';
import { Toggle, ToggleType } from '../Toggle';
import './Popup.scss';
import { Popper } from '@mui/material';
+import PositionObserver from '@thednp/position-observer';
export enum PopupTrigger {
CLICK = 'click',
@@ -23,9 +24,10 @@ export interface IPopupProps extends IGlobalProps {
isOpen?: boolean;
setOpen?: (b: boolean) => void;
background?: string;
- isToggle?: boolean; // whether popup stays open when background is clicked. muyst click toggle button tp close it.
+ showUntilToggle?: boolean; // whether popup stays open when background is clicked. muyst click toggle button tp close it.
toggleFunc?: () => void;
popupContainsPt?: (x: number, y: number) => boolean;
+ multitoggle?: boolean;
}
/**
@@ -57,20 +59,21 @@ export const Popup = (props: IPopupProps) => {
fillWidth,
iconPlacement = 'left',
background,
+ multitoggle,
popupContainsPt,
} = props;
const triggerRef = useRef(null);
const popperRef = useRef<HTMLDivElement | null>(null);
- const toggleRef = useRef<HTMLDivElement | null>(null);
+ const [toggleRef, setToggleRef] = useState<HTMLDivElement | null>(null);
let timeout = setTimeout(() => {});
const handlePointerAwayDown = (e: PointerEvent) => {
const rect = popperRef.current?.getBoundingClientRect();
- const rect2 = toggleRef.current?.getBoundingClientRect();
+ const rect2 = toggleRef?.getBoundingClientRect();
if (
- !props.isToggle &&
+ !props.showUntilToggle &&
(!rect2 || !(rect2.left < e.clientX && rect2.top < e.clientY && rect2.right > e.clientX && rect2.bottom > e.clientY)) &&
rect &&
!(rect.left < e.clientX && rect.top < e.clientY && rect.right > e.clientX && rect.bottom > e.clientY) &&
@@ -82,22 +85,36 @@ export const Popup = (props: IPopupProps) => {
}
};
+ let observer: PositionObserver | undefined = undefined;
+ const [previousPosition, setPreviousPosition] = useState<DOMRect | undefined>(toggleRef?.getBoundingClientRect());
+
useEffect(() => {
if (isOpen) {
window.removeEventListener('pointerdown', handlePointerAwayDown, { capture: true });
window.addEventListener('pointerdown', handlePointerAwayDown, { capture: true });
- return () => window.removeEventListener('pointerdown', handlePointerAwayDown, { capture: true });
- }
- }, [isOpen, popupContainsPt]);
-
+ if (toggleRef && multitoggle) {
+ (observer = new PositionObserver(entries => {
+ entries.forEach(entry => {
+ const currentPosition = entry.boundingClientRect;
+ if (Math.floor(currentPosition.top) !== Math.floor(previousPosition?.top ?? 0) || Math.floor(currentPosition.left) !== Math.floor(previousPosition?.left ?? 0)) {
+ // Perform actions when position changes
+ setPreviousPosition(currentPosition); // Update previous position
+ }
+ });
+ })).observe(toggleRef);
+ }
+ return () => {
+ window.removeEventListener('pointerdown', handlePointerAwayDown, { capture: true });
+ observer?.disconnect();
+ };
+ } else observer?.disconnect();
+ }, [isOpen, toggleRef, popupContainsPt]);
return (
<div className={`popup-wrapper ${fillWidth && 'fillWidth'}`}>
<div
- className={`trigger-container ${fillWidth && 'fillWidth'}`}
ref={triggerRef}
- onClick={() => {
- if (trigger === PopupTrigger.CLICK) setOpen(!isOpen);
- }}
+ className={`trigger-container ${fillWidth && 'fillWidth'}`}
+ onClick={() => trigger === PopupTrigger.CLICK && setOpen(!isOpen)}
onPointerEnter={() => {
if (trigger === PopupTrigger.HOVER || trigger === PopupTrigger.HOVER_DELAY) {
clearTimeout(timeout);
@@ -109,7 +126,7 @@ export const Popup = (props: IPopupProps) => {
timeout = setTimeout(() => setOpen(false), 1000);
}
}}>
- <div ref={toggleRef}>
+ <div className="special" ref={R => setToggleRef(R)}>
{toggle ?? (
<Toggle
tooltip={tooltip}