selection
This commit is contained in:
@@ -40,6 +40,9 @@ const QuickAddMenu = ({
|
||||
menuMinWidth = 220,
|
||||
triggerClassName = 'icon-button quick-add__trigger',
|
||||
triggerContent = null,
|
||||
disabled = false,
|
||||
align = 'start',
|
||||
positionStrategy = 'fixed',
|
||||
}) => {
|
||||
const anchorRef = useRef(null);
|
||||
const inputRef = useRef(null);
|
||||
@@ -57,8 +60,16 @@ const QuickAddMenu = ({
|
||||
anchorRef,
|
||||
minWidth: menuMinWidth,
|
||||
matchAnchorWidth: false,
|
||||
align,
|
||||
positionStrategy,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (disabled && isOpen) {
|
||||
close();
|
||||
}
|
||||
}, [disabled, isOpen, close]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen) {
|
||||
return undefined;
|
||||
@@ -145,6 +156,7 @@ const QuickAddMenu = ({
|
||||
onClick={toggle}
|
||||
aria-label={triggerAriaLabel}
|
||||
title={triggerTitle}
|
||||
disabled={disabled}
|
||||
>
|
||||
{triggerContent ?? <PlusIcon />}
|
||||
</button>
|
||||
|
||||
@@ -30,6 +30,9 @@ import {
|
||||
IconLayoutSidebarLeftExpand,
|
||||
IconLayoutSidebarRightCollapse,
|
||||
IconInfoCircle,
|
||||
IconCircleDashedCheck,
|
||||
IconFile,
|
||||
IconFolder,
|
||||
} from '@tabler/icons-react';
|
||||
import FolderSvg from '../assets/folder.svg';
|
||||
|
||||
@@ -330,6 +333,33 @@ export const CheckIcon = ({ className, size = '1em', stroke = 1.6, ...rest }) =>
|
||||
/>
|
||||
);
|
||||
|
||||
export const CircleDashedCheckIcon = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
|
||||
<IconCircleDashedCheck
|
||||
className={composeClassName('icon', className)}
|
||||
size={size}
|
||||
stroke={stroke}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
|
||||
export const FileIcon = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
|
||||
<IconFile
|
||||
className={composeClassName('icon', className)}
|
||||
size={size}
|
||||
stroke={stroke}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
|
||||
export const FolderOutlineIcon = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
|
||||
<IconFolder
|
||||
className={composeClassName('icon', className)}
|
||||
size={size}
|
||||
stroke={stroke}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
|
||||
export const AnalyzeIcon = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
|
||||
<IconAnalyze
|
||||
className={composeClassName('icon', className)}
|
||||
|
||||
@@ -26,7 +26,7 @@ const formatStyle = (metrics) => {
|
||||
return null;
|
||||
}
|
||||
const style = {
|
||||
position: 'fixed',
|
||||
position: metrics.strategy === 'absolute' ? 'absolute' : 'fixed',
|
||||
top: metrics.top,
|
||||
left: metrics.left,
|
||||
minWidth: metrics.minWidth,
|
||||
@@ -45,6 +45,7 @@ const useFloatingMenu = ({
|
||||
align = 'start',
|
||||
viewportMargin = DEFAULT_VIEWPORT_MARGIN,
|
||||
onOpenChange,
|
||||
positionStrategy = 'fixed',
|
||||
} = {}) => {
|
||||
const menuRef = useRef(null);
|
||||
const [menuMetrics, setMenuMetrics] = useState(null);
|
||||
@@ -63,22 +64,64 @@ const useFloatingMenu = ({
|
||||
|
||||
const rect = anchor.getBoundingClientRect();
|
||||
const desiredWidth = computeWidth(rect.width, minWidth, matchAnchorWidth);
|
||||
const menu = menuRef.current;
|
||||
const measuredWidth = menu?.offsetWidth ?? desiredWidth;
|
||||
const widthForAlignment = matchAnchorWidth ? desiredWidth : Math.max(desiredWidth, measuredWidth);
|
||||
|
||||
if (positionStrategy === 'absolute') {
|
||||
const anchor = anchorRef?.current;
|
||||
if (!anchor) {
|
||||
return false;
|
||||
}
|
||||
const offsetParent = (menu && menu.offsetParent) || anchor.offsetParent || anchor.parentElement;
|
||||
if (!offsetParent) {
|
||||
// Fall back to fixed positioning if we cannot resolve a relative parent.
|
||||
setMenuMetrics({
|
||||
strategy: 'fixed',
|
||||
top: rect.bottom + offset,
|
||||
left: rect.left,
|
||||
minWidth: desiredWidth,
|
||||
width: matchAnchorWidth ? desiredWidth : undefined,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
let left;
|
||||
if (align === 'end') {
|
||||
left = anchor.offsetLeft + anchor.offsetWidth - widthForAlignment;
|
||||
} else if (align === 'center') {
|
||||
left = anchor.offsetLeft + anchor.offsetWidth / 2 - widthForAlignment / 2;
|
||||
} else {
|
||||
left = anchor.offsetLeft;
|
||||
}
|
||||
|
||||
const top = anchor.offsetTop + anchor.offsetHeight + offset;
|
||||
|
||||
setMenuMetrics({
|
||||
strategy: 'absolute',
|
||||
top,
|
||||
left,
|
||||
minWidth: desiredWidth,
|
||||
width: matchAnchorWidth ? desiredWidth : undefined,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
const viewportWidth = resolveViewportWidth();
|
||||
const viewportHeight = typeof window !== 'undefined' ? window.innerHeight : 0;
|
||||
const safeMargin = viewportMargin ?? DEFAULT_VIEWPORT_MARGIN;
|
||||
const menu = menuRef.current;
|
||||
const menuHeight = menu?.offsetHeight ?? 0;
|
||||
|
||||
let left;
|
||||
if (align === 'end') {
|
||||
left = rect.right - desiredWidth;
|
||||
left = rect.right - widthForAlignment;
|
||||
} else if (align === 'center') {
|
||||
left = rect.left + rect.width / 2 - desiredWidth / 2;
|
||||
left = rect.left + rect.width / 2 - widthForAlignment / 2;
|
||||
} else {
|
||||
left = rect.left;
|
||||
}
|
||||
|
||||
const maxLeft = viewportWidth > 0 ? viewportWidth - desiredWidth - safeMargin : left;
|
||||
const maxLeft = viewportWidth > 0 ? viewportWidth - widthForAlignment - safeMargin : left;
|
||||
const clampedLeft = viewportWidth > 0 ? clamp(left, safeMargin, Math.max(maxLeft, safeMargin)) : left;
|
||||
|
||||
let top = rect.bottom + offset;
|
||||
@@ -91,6 +134,7 @@ const useFloatingMenu = ({
|
||||
}
|
||||
|
||||
setMenuMetrics({
|
||||
strategy: 'fixed',
|
||||
top,
|
||||
left: clampedLeft,
|
||||
minWidth: desiredWidth,
|
||||
@@ -98,7 +142,15 @@ const useFloatingMenu = ({
|
||||
});
|
||||
|
||||
return true;
|
||||
}, [anchorRef, align, matchAnchorWidth, minWidth, offset, viewportMargin]);
|
||||
}, [
|
||||
anchorRef,
|
||||
align,
|
||||
matchAnchorWidth,
|
||||
minWidth,
|
||||
offset,
|
||||
positionStrategy,
|
||||
viewportMargin,
|
||||
]);
|
||||
|
||||
const close = useCallback(() => {
|
||||
setIsOpen((prev) => {
|
||||
|
||||
Reference in New Issue
Block a user