mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-09-19 23:40:45 +02:00
fix popover not closable and font search auto focus
This commit is contained in:
@@ -1168,25 +1168,23 @@ export const actionChangeFontFamily = register({
|
||||
}
|
||||
}
|
||||
|
||||
setBatchedData({
|
||||
openPopup: "fontFamily",
|
||||
});
|
||||
updateData({ ...batchedData, openPopup: "fontFamily" });
|
||||
setBatchedData({});
|
||||
} else {
|
||||
// close immediately to avoid racing with other popovers opening
|
||||
// close: clear openPopup if we're still the active popup
|
||||
const data = {
|
||||
openPopup:
|
||||
appState.openPopup === "fontFamily"
|
||||
? null
|
||||
: appState.openPopup,
|
||||
currentHoveredFontFamily: null,
|
||||
cachedElements: new Map(cachedElementsRef.current),
|
||||
resetAll: true,
|
||||
} as ChangeFontFamilyData;
|
||||
|
||||
if (isUnmounted.current) {
|
||||
updateData({ ...batchedData, ...data });
|
||||
} else {
|
||||
// apply immediately instead of batching
|
||||
// apply immediately to avoid racing with other popovers opening
|
||||
updateData({ ...batchedData, ...data });
|
||||
setBatchedData({});
|
||||
}
|
||||
|
||||
cachedElementsRef.current.clear();
|
||||
}
|
||||
}}
|
||||
|
@@ -413,6 +413,10 @@ export const CompactShapeActions = ({
|
||||
return next;
|
||||
});
|
||||
}}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
{resizeIcon}
|
||||
</button>
|
||||
@@ -460,8 +464,8 @@ export const CompactShapeActions = ({
|
||||
<Popover.Root
|
||||
open={appState.openPopup === "arrowProperties"}
|
||||
onOpenChange={(open) => {
|
||||
setAppState({ openPopup: open ? "arrowProperties" : null });
|
||||
if (open) {
|
||||
setAppState({ openPopup: "arrowProperties" });
|
||||
setStrokePopoverOpen(false);
|
||||
setOtherActionsPopoverOpen(false);
|
||||
}
|
||||
@@ -482,6 +486,10 @@ export const CompactShapeActions = ({
|
||||
setAppState({ openPopup: "arrowProperties" });
|
||||
}
|
||||
}}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
{(() => {
|
||||
// Show an icon based on the current arrow type
|
||||
@@ -518,9 +526,11 @@ export const CompactShapeActions = ({
|
||||
container={container}
|
||||
style={{ maxWidth: "13rem" }}
|
||||
onClose={() => {
|
||||
if (appState.openPopup === "arrowProperties") {
|
||||
setAppState({ openPopup: null });
|
||||
}
|
||||
setAppState((prev: AppState) =>
|
||||
prev.openPopup === "arrowProperties"
|
||||
? { openPopup: null }
|
||||
: null,
|
||||
);
|
||||
}}
|
||||
>
|
||||
{renderAction("changeArrowProperties")}
|
||||
@@ -551,8 +561,8 @@ export const CompactShapeActions = ({
|
||||
<Popover.Root
|
||||
open={appState.openPopup === "textAlign"}
|
||||
onOpenChange={(open) => {
|
||||
setAppState({ openPopup: open ? "textAlign" : null });
|
||||
if (open) {
|
||||
setAppState({ openPopup: "textAlign" });
|
||||
setStrokePopoverOpen(false);
|
||||
setOtherActionsPopoverOpen(false);
|
||||
}
|
||||
@@ -573,6 +583,10 @@ export const CompactShapeActions = ({
|
||||
setAppState({ openPopup: "textAlign" });
|
||||
}
|
||||
}}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
{TextSizeIcon}
|
||||
</button>
|
||||
@@ -583,9 +597,11 @@ export const CompactShapeActions = ({
|
||||
container={container}
|
||||
style={{ maxWidth: "13rem" }}
|
||||
onClose={() => {
|
||||
if (appState.openPopup === "textAlign") {
|
||||
setAppState({ openPopup: null });
|
||||
}
|
||||
setAppState((prev: AppState) =>
|
||||
prev.openPopup === "textAlign"
|
||||
? { openPopup: null }
|
||||
: null,
|
||||
);
|
||||
}}
|
||||
>
|
||||
<div className="selected-shape-actions">
|
||||
@@ -651,6 +667,10 @@ export const CompactShapeActions = ({
|
||||
return next;
|
||||
});
|
||||
}}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
{settingsPlusIcon}
|
||||
</button>
|
||||
|
@@ -226,8 +226,14 @@ const ColorPickerTrigger = ({
|
||||
onPointerDown={(e) => {
|
||||
// use pointerdown so we run before outside-close logic
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
onToggle();
|
||||
}}
|
||||
onClick={(e) => {
|
||||
// suppress Radix default toggle to avoid double-toggle flicker
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<div className="color-picker__button-outline">{!color && slashIcon}</div>
|
||||
{compactMode && color && (
|
||||
@@ -313,7 +319,7 @@ export const ColorPicker = ({
|
||||
onToggle={() => {
|
||||
// atomic switch: if another popup is open, close it first, then open this one next tick
|
||||
if (appState.openPopup === type) {
|
||||
// toggle off
|
||||
// toggle off on same trigger
|
||||
updateData({ openPopup: null });
|
||||
} else if (appState.openPopup) {
|
||||
// switching
|
||||
|
@@ -102,16 +102,19 @@ export const FontPicker = React.memo(
|
||||
</div>
|
||||
)}
|
||||
{!compactMode && <ButtonSeparator />}
|
||||
<Popover.Root open={isOpened} onOpenChange={onPopupChange}>
|
||||
<Popover.Root open={isOpened} onOpenChange={() => {}}>
|
||||
<FontPickerTrigger
|
||||
selectedFontFamily={selectedFontFamily}
|
||||
onTrigger={() => {
|
||||
onTrigger={(e) => {
|
||||
// suppress default to avoid double toggle
|
||||
if (e && e.preventDefault) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
if (isOpened) {
|
||||
onPopupChange(false);
|
||||
} else {
|
||||
// switch from any open popup: close then open next tick
|
||||
onPopupChange(false);
|
||||
setTimeout(() => onPopupChange(true), 0);
|
||||
onPopupChange(true);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
@@ -285,6 +285,7 @@ export const FontPickerList = React.memo(
|
||||
onClose={onClose}
|
||||
onPointerLeave={onLeave}
|
||||
onKeyDown={onKeyDown}
|
||||
preventAutoFocusOnTouch={true}
|
||||
>
|
||||
<QuickSearch
|
||||
ref={inputRef}
|
||||
|
@@ -26,7 +26,17 @@ export const FontPickerTrigger = ({
|
||||
return (
|
||||
<Popover.Trigger asChild>
|
||||
{/* Empty div as trigger so it's stretched 100% due to different button sizes */}
|
||||
<div data-openpopup="fontFamily">
|
||||
<div
|
||||
data-openpopup="fontFamily"
|
||||
onPointerDown={(e) => {
|
||||
e.preventDefault();
|
||||
onTrigger?.(e);
|
||||
}}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<ButtonIcon
|
||||
standalone
|
||||
icon={TextIcon}
|
||||
@@ -35,10 +45,8 @@ export const FontPickerTrigger = ({
|
||||
testId={"font-family-show-fonts"}
|
||||
active={isTriggerActive}
|
||||
onClick={(e) => {
|
||||
if (onTrigger) {
|
||||
e.preventDefault();
|
||||
onTrigger(e);
|
||||
}
|
||||
e.stopPropagation();
|
||||
}}
|
||||
style={{
|
||||
border: "none",
|
||||
|
@@ -17,6 +17,7 @@ interface PropertiesPopoverProps {
|
||||
onPointerLeave?: React.PointerEventHandler<HTMLDivElement>;
|
||||
onFocusOutside?: Popover.PopoverContentProps["onFocusOutside"];
|
||||
onPointerDownOutside?: Popover.PopoverContentProps["onPointerDownOutside"];
|
||||
preventAutoFocusOnTouch?: boolean;
|
||||
}
|
||||
|
||||
export const PropertiesPopover = React.forwardRef<
|
||||
@@ -34,6 +35,7 @@ export const PropertiesPopover = React.forwardRef<
|
||||
onFocusOutside,
|
||||
onPointerLeave,
|
||||
onPointerDownOutside,
|
||||
preventAutoFocusOnTouch = false,
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
@@ -64,6 +66,12 @@ export const PropertiesPopover = React.forwardRef<
|
||||
onKeyDown={onKeyDown}
|
||||
onFocusOutside={onFocusOutside}
|
||||
onPointerDownOutside={onPointerDownOutside}
|
||||
onOpenAutoFocus={(e) => {
|
||||
// prevent auto-focus on touch devices to avoid keyboard popup
|
||||
if (preventAutoFocusOnTouch && device.isTouchScreen) {
|
||||
e.preventDefault();
|
||||
}
|
||||
}}
|
||||
onCloseAutoFocus={(e) => {
|
||||
e.stopPropagation();
|
||||
// prevents focusing the trigger
|
||||
|
Reference in New Issue
Block a user