fix styling

This commit is contained in:
Ryan Di
2025-09-22 18:44:57 +10:00
parent 9b68439712
commit 620c84383d
17 changed files with 182 additions and 107 deletions

View File

@@ -536,3 +536,10 @@ export enum UserIdleState {
export const LINE_POLYGON_POINT_MERGE_DISTANCE = 20; export const LINE_POLYGON_POINT_MERGE_DISTANCE = 20;
export const DOUBLE_TAP_POSITION_THRESHOLD = 35; export const DOUBLE_TAP_POSITION_THRESHOLD = 35;
// glass background for mobile action buttons
export const MOBILE_ACTION_BUTTON_BG = {
background: "rgba(255, 255, 255, 0.1)",
backdropFilter: "blur(20px)",
WebkitBackdropFilter: "blur(20px)",
} as const;

View File

@@ -1,4 +1,8 @@
import { KEYS, updateActiveTool } from "@excalidraw/common"; import {
KEYS,
MOBILE_ACTION_BUTTON_BG,
updateActiveTool,
} from "@excalidraw/common";
import { getNonDeletedElements } from "@excalidraw/element"; import { getNonDeletedElements } from "@excalidraw/element";
import { fixBindingsAfterDeletion } from "@excalidraw/element"; import { fixBindingsAfterDeletion } from "@excalidraw/element";
@@ -326,6 +330,11 @@ export const actionDeleteSelected = register({
disabled={ disabled={
!isSomeElementSelected(getNonDeletedElements(elements), appState) !isSomeElementSelected(getNonDeletedElements(elements), appState)
} }
style={{
...(appState.stylesPanelMode === "mobile"
? MOBILE_ACTION_BUTTON_BG
: {}),
}}
/> />
), ),
}); });

View File

@@ -1,6 +1,7 @@
import { import {
DEFAULT_GRID_SIZE, DEFAULT_GRID_SIZE,
KEYS, KEYS,
MOBILE_ACTION_BUTTON_BG,
arrayToMap, arrayToMap,
getShortcutKey, getShortcutKey,
} from "@excalidraw/common"; } from "@excalidraw/common";
@@ -118,6 +119,11 @@ export const actionDuplicateSelection = register({
disabled={ disabled={
!isSomeElementSelected(getNonDeletedElements(elements), appState) !isSomeElementSelected(getNonDeletedElements(elements), appState)
} }
style={{
...(appState.stylesPanelMode === "mobile"
? MOBILE_ACTION_BUTTON_BG
: {}),
}}
/> />
), ),
}); });

View File

@@ -1,4 +1,10 @@
import { isWindows, KEYS, matchKey, arrayToMap } from "@excalidraw/common"; import {
isWindows,
KEYS,
matchKey,
arrayToMap,
MOBILE_ACTION_BUTTON_BG,
} from "@excalidraw/common";
import { CaptureUpdateAction } from "@excalidraw/element"; import { CaptureUpdateAction } from "@excalidraw/element";
@@ -67,7 +73,7 @@ export const createUndoAction: ActionCreator = (history) => ({
), ),
keyTest: (event) => keyTest: (event) =>
event[KEYS.CTRL_OR_CMD] && matchKey(event, KEYS.Z) && !event.shiftKey, event[KEYS.CTRL_OR_CMD] && matchKey(event, KEYS.Z) && !event.shiftKey,
PanelComponent: ({ updateData, data }) => { PanelComponent: ({ appState, updateData, data }) => {
const { isUndoStackEmpty } = useEmitter<HistoryChangedEvent>( const { isUndoStackEmpty } = useEmitter<HistoryChangedEvent>(
history.onHistoryChangedEmitter, history.onHistoryChangedEmitter,
new HistoryChangedEvent( new HistoryChangedEvent(
@@ -85,6 +91,11 @@ export const createUndoAction: ActionCreator = (history) => ({
size={data?.size || "medium"} size={data?.size || "medium"}
disabled={isUndoStackEmpty} disabled={isUndoStackEmpty}
data-testid="button-undo" data-testid="button-undo"
style={{
...(appState.stylesPanelMode === "mobile"
? MOBILE_ACTION_BUTTON_BG
: {}),
}}
/> />
); );
}, },
@@ -103,7 +114,7 @@ export const createRedoAction: ActionCreator = (history) => ({
keyTest: (event) => keyTest: (event) =>
(event[KEYS.CTRL_OR_CMD] && event.shiftKey && matchKey(event, KEYS.Z)) || (event[KEYS.CTRL_OR_CMD] && event.shiftKey && matchKey(event, KEYS.Z)) ||
(isWindows && event.ctrlKey && !event.shiftKey && matchKey(event, KEYS.Y)), (isWindows && event.ctrlKey && !event.shiftKey && matchKey(event, KEYS.Y)),
PanelComponent: ({ updateData, data }) => { PanelComponent: ({ appState, updateData, data }) => {
const { isRedoStackEmpty } = useEmitter( const { isRedoStackEmpty } = useEmitter(
history.onHistoryChangedEmitter, history.onHistoryChangedEmitter,
new HistoryChangedEvent( new HistoryChangedEvent(
@@ -121,6 +132,11 @@ export const createRedoAction: ActionCreator = (history) => ({
size={data?.size || "medium"} size={data?.size || "medium"}
disabled={isRedoStackEmpty} disabled={isRedoStackEmpty}
data-testid="button-redo" data-testid="button-redo"
style={{
...(appState.stylesPanelMode === "mobile"
? MOBILE_ACTION_BUTTON_BG
: {}),
}}
/> />
); );
}, },

View File

@@ -106,6 +106,7 @@
justify-content: center; justify-content: center;
align-items: center; align-items: center;
min-height: 2.5rem; min-height: 2.5rem;
pointer-events: auto;
--default-button-size: 2rem; --default-button-size: 2rem;
@@ -114,7 +115,6 @@
height: 1.625rem; height: 1.625rem;
border: none; border: none;
border-radius: var(--border-radius-lg); border-radius: var(--border-radius-lg);
background: transparent;
color: var(--color-on-surface); color: var(--color-on-surface);
cursor: pointer; cursor: pointer;
display: flex; display: flex;
@@ -122,21 +122,17 @@
justify-content: center; justify-content: center;
transition: all 0.2s ease; transition: all 0.2s ease;
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
svg { svg {
width: 1rem; width: 1rem;
height: 1rem; height: 1rem;
flex: 0 0 auto; flex: 0 0 auto;
} }
&:hover { &.active {
background: var(--button-hover-bg, var(--island-bg-color));
border-color: var(
--button-hover-border,
var(--button-border, var(--default-border-color))
);
}
&:active {
background: var(--button-active-bg, var(--island-bg-color)); background: var(--button-active-bg, var(--island-bg-color));
border-color: var(--button-active-border, var(--color-primary-darkest)); border-color: var(--button-active-border, var(--color-primary-darkest));
} }
@@ -168,9 +164,15 @@
} }
} }
.ToolIcon__icon { .ToolIcon {
width: 1.625rem; .ToolIcon__icon {
height: 1.625rem; width: 1.625rem;
height: 1.625rem;
&:hover {
background-color: transparent;
}
}
} }
} }

View File

@@ -328,7 +328,7 @@ const CombinedShapeProperties = ({
hasBackground(element.type) && !isTransparent(element.backgroundColor), hasBackground(element.type) && !isTransparent(element.backgroundColor),
); );
const showShowCombinedProperties = const shouldShowCombinedProperties =
showFillIcons || showFillIcons ||
hasStrokeWidth(appState.activeTool.type) || hasStrokeWidth(appState.activeTool.type) ||
targetElements.some((element) => hasStrokeWidth(element.type)) || targetElements.some((element) => hasStrokeWidth(element.type)) ||
@@ -337,14 +337,16 @@ const CombinedShapeProperties = ({
canChangeRoundness(appState.activeTool.type) || canChangeRoundness(appState.activeTool.type) ||
targetElements.some((element) => canChangeRoundness(element.type)); targetElements.some((element) => canChangeRoundness(element.type));
if (!showShowCombinedProperties) { const isOpen = appState.openPopup === "compactStrokeStyles";
if (!shouldShowCombinedProperties) {
return null; return null;
} }
return ( return (
<div className="compact-action-item"> <div className="compact-action-item">
<Popover.Root <Popover.Root
open={appState.openPopup === "compactStrokeStyles"} open={isOpen}
onOpenChange={(open) => { onOpenChange={(open) => {
if (open) { if (open) {
setAppState({ openPopup: "compactStrokeStyles" }); setAppState({ openPopup: "compactStrokeStyles" });
@@ -356,24 +358,23 @@ const CombinedShapeProperties = ({
<Popover.Trigger asChild> <Popover.Trigger asChild>
<button <button
type="button" type="button"
className="compact-action-button properties-trigger" className={clsx("compact-action-button properties-trigger", {
active: isOpen,
})}
title={t("labels.stroke")} title={t("labels.stroke")}
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
setAppState({ setAppState({
openPopup: openPopup: isOpen ? null : "compactStrokeStyles",
appState.openPopup === "compactStrokeStyles"
? null
: "compactStrokeStyles",
}); });
}} }}
> >
{adjustmentsIcon} {adjustmentsIcon}
</button> </button>
</Popover.Trigger> </Popover.Trigger>
{appState.openPopup === "compactStrokeStyles" && ( {isOpen && (
<PropertiesPopover <PropertiesPopover
className={PROPERTIES_CLASSES} className={PROPERTIES_CLASSES}
container={container} container={container}
@@ -428,6 +429,7 @@ const CombinedArrowProperties = ({
const showShowArrowProperties = const showShowArrowProperties =
toolIsArrow(appState.activeTool.type) || toolIsArrow(appState.activeTool.type) ||
targetElements.some((element) => toolIsArrow(element.type)); targetElements.some((element) => toolIsArrow(element.type));
const isOpen = appState.openPopup === "compactArrowProperties";
if (!showShowArrowProperties) { if (!showShowArrowProperties) {
return null; return null;
@@ -436,7 +438,7 @@ const CombinedArrowProperties = ({
return ( return (
<div className="compact-action-item"> <div className="compact-action-item">
<Popover.Root <Popover.Root
open={appState.openPopup === "compactArrowProperties"} open={isOpen}
onOpenChange={(open) => { onOpenChange={(open) => {
if (open) { if (open) {
setAppState({ openPopup: "compactArrowProperties" }); setAppState({ openPopup: "compactArrowProperties" });
@@ -448,17 +450,16 @@ const CombinedArrowProperties = ({
<Popover.Trigger asChild> <Popover.Trigger asChild>
<button <button
type="button" type="button"
className="compact-action-button properties-trigger" className={clsx("compact-action-button properties-trigger", {
active: isOpen,
})}
title={t("labels.arrowtypes")} title={t("labels.arrowtypes")}
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
setAppState({ setAppState({
openPopup: openPopup: isOpen ? null : "compactArrowProperties",
appState.openPopup === "compactArrowProperties"
? null
: "compactArrowProperties",
}); });
}} }}
> >
@@ -492,7 +493,7 @@ const CombinedArrowProperties = ({
})()} })()}
</button> </button>
</Popover.Trigger> </Popover.Trigger>
{appState.openPopup === "compactArrowProperties" && ( {isOpen && (
<PropertiesPopover <PropertiesPopover
container={container} container={container}
className="properties-content" className="properties-content"
@@ -523,11 +524,12 @@ const CombinedTextProperties = ({
elementsMap: NonDeletedElementsMap | NonDeletedSceneElementsMap; elementsMap: NonDeletedElementsMap | NonDeletedSceneElementsMap;
}) => { }) => {
const { saveCaretPosition, restoreCaretPosition } = useTextEditorFocus(); const { saveCaretPosition, restoreCaretPosition } = useTextEditorFocus();
const isOpen = appState.openPopup === "compactTextProperties";
return ( return (
<div className="compact-action-item"> <div className="compact-action-item">
<Popover.Root <Popover.Root
open={appState.openPopup === "compactTextProperties"} open={isOpen}
onOpenChange={(open) => { onOpenChange={(open) => {
if (open) { if (open) {
if (appState.editingTextElement) { if (appState.editingTextElement) {
@@ -545,13 +547,15 @@ const CombinedTextProperties = ({
<Popover.Trigger asChild> <Popover.Trigger asChild>
<button <button
type="button" type="button"
className="compact-action-button properties-trigger" className={clsx("compact-action-button properties-trigger", {
active: isOpen,
})}
title={t("labels.textAlign")} title={t("labels.textAlign")}
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
if (appState.openPopup === "compactTextProperties") { if (isOpen) {
setAppState({ openPopup: null }); setAppState({ openPopup: null });
} else { } else {
if (appState.editingTextElement) { if (appState.editingTextElement) {
@@ -629,6 +633,7 @@ const CombinedExtraActions = ({
} }
const isRTL = document.documentElement.getAttribute("dir") === "rtl"; const isRTL = document.documentElement.getAttribute("dir") === "rtl";
const isOpen = appState.openPopup === "compactOtherProperties";
if (isEditingTextOrNewElement || targetElements.length === 0) { if (isEditingTextOrNewElement || targetElements.length === 0) {
return null; return null;
@@ -637,7 +642,7 @@ const CombinedExtraActions = ({
return ( return (
<div className="compact-action-item"> <div className="compact-action-item">
<Popover.Root <Popover.Root
open={appState.openPopup === "compactOtherProperties"} open={isOpen}
onOpenChange={(open) => { onOpenChange={(open) => {
if (open) { if (open) {
setAppState({ openPopup: "compactOtherProperties" }); setAppState({ openPopup: "compactOtherProperties" });
@@ -649,23 +654,22 @@ const CombinedExtraActions = ({
<Popover.Trigger asChild> <Popover.Trigger asChild>
<button <button
type="button" type="button"
className="compact-action-button properties-trigger" className={clsx("compact-action-button properties-trigger", {
active: isOpen,
})}
title={t("labels.actions")} title={t("labels.actions")}
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
setAppState({ setAppState({
openPopup: openPopup: isOpen ? null : "compactOtherProperties",
appState.openPopup === "compactOtherProperties"
? null
: "compactOtherProperties",
}); });
}} }}
> >
{DotsHorizontalIcon} {DotsHorizontalIcon}
</button> </button>
</Popover.Trigger> </Popover.Trigger>
{appState.openPopup === "compactOtherProperties" && ( {isOpen && (
<PropertiesPopover <PropertiesPopover
className={PROPERTIES_CLASSES} className={PROPERTIES_CLASSES}
container={container} container={container}
@@ -798,12 +802,7 @@ export const CompactShapeActions = ({
<div className="compact-shape-actions"> <div className="compact-shape-actions">
{/* Stroke Color */} {/* Stroke Color */}
{canChangeStrokeColor(appState, targetElements) && ( {canChangeStrokeColor(appState, targetElements) && (
<div <div className={clsx("compact-action-item")}>
className={clsx("compact-action-item")}
style={{
marginRight: 4,
}}
>
{renderAction("changeStrokeColor")} {renderAction("changeStrokeColor")}
</div> </div>
)} )}
@@ -925,6 +924,7 @@ export const MobileShapeActions = ({
height: WIDTH * 1.75, height: WIDTH * 1.75,
alignItems: "center", alignItems: "center",
gap: GAP, gap: GAP,
pointerEvents: "none",
}} }}
ref={mobileActionsRef} ref={mobileActionsRef}
> >

View File

@@ -7,6 +7,12 @@
} }
} }
.color-picker__title {
padding: 0 0.5rem;
font-size: 0.875rem;
text-align: left;
}
.color-picker__heading { .color-picker__heading {
padding: 0 0.5rem; padding: 0 0.5rem;
font-size: 0.75rem; font-size: 0.75rem;

View File

@@ -18,7 +18,7 @@ import { useExcalidrawContainer } from "../App";
import { ButtonSeparator } from "../ButtonSeparator"; import { ButtonSeparator } from "../ButtonSeparator";
import { activeEyeDropperAtom } from "../EyeDropper"; import { activeEyeDropperAtom } from "../EyeDropper";
import { PropertiesPopover } from "../PropertiesPopover"; import { PropertiesPopover } from "../PropertiesPopover";
import { backgroundIcon, slashIcon, strokeIcon } from "../icons"; import { slashIcon, strokeIcon } from "../icons";
import { import {
saveCaretPosition, saveCaretPosition,
restoreCaretPosition, restoreCaretPosition,
@@ -213,6 +213,10 @@ const ColorPickerPopupContent = ({
type={type} type={type}
elements={elements} elements={elements}
updateData={updateData} updateData={updateData}
showTitle={
appState.stylesPanelMode === "compact" ||
appState.stylesPanelMode === "mobile"
}
> >
{colorInputJSX} {colorInputJSX}
</Picker> </Picker>
@@ -272,31 +276,18 @@ const ColorPickerTrigger = ({
onClick={handleClick} onClick={handleClick}
> >
<div className="color-picker__button-outline">{!color && slashIcon}</div> <div className="color-picker__button-outline">{!color && slashIcon}</div>
{compactMode && color && ( {compactMode && color && mode === "stroke" && (
<div className="color-picker__button-background"> <div className="color-picker__button-background">
{mode === "background" ? ( <span
<span style={{
style={{ color:
color: color && isColorDark(color, COLOR_OUTLINE_CONTRAST_THRESHOLD)
color && isColorDark(color, COLOR_OUTLINE_CONTRAST_THRESHOLD) ? "#fff"
? "#fff" : "#111",
: "#111", }}
}} >
> {strokeIcon}
{backgroundIcon} </span>
</span>
) : (
<span
style={{
color:
color && isColorDark(color, COLOR_OUTLINE_CONTRAST_THRESHOLD)
? "#fff"
: "#111",
}}
>
{strokeIcon}
</span>
)}
</div> </div>
)} )}
</Popover.Trigger> </Popover.Trigger>

View File

@@ -37,6 +37,7 @@ interface PickerProps {
palette: ColorPaletteCustom; palette: ColorPaletteCustom;
updateData: (formData?: any) => void; updateData: (formData?: any) => void;
children?: React.ReactNode; children?: React.ReactNode;
showTitle?: boolean;
onEyeDropperToggle: (force?: boolean) => void; onEyeDropperToggle: (force?: boolean) => void;
onEscape: (event: React.KeyboardEvent | KeyboardEvent) => void; onEscape: (event: React.KeyboardEvent | KeyboardEvent) => void;
} }
@@ -51,11 +52,20 @@ export const Picker = React.forwardRef(
palette, palette,
updateData, updateData,
children, children,
showTitle,
onEyeDropperToggle, onEyeDropperToggle,
onEscape, onEscape,
}: PickerProps, }: PickerProps,
ref, ref,
) => { ) => {
const title = showTitle
? type === "elementStroke"
? t("labels.stroke")
: type === "elementBackground"
? t("labels.background")
: null
: null;
const [customColors] = React.useState(() => { const [customColors] = React.useState(() => {
if (type === "canvasBackground") { if (type === "canvasBackground") {
return []; return [];
@@ -154,6 +164,8 @@ export const Picker = React.forwardRef(
// to allow focusing by clicking but not by tabbing // to allow focusing by clicking but not by tabbing
tabIndex={-1} tabIndex={-1}
> >
{title && <div className="color-picker__title">{title}</div>}
{!!customColors.length && ( {!!customColors.length && (
<div> <div>
<PickerHeading> <PickerHeading>

View File

@@ -21,6 +21,16 @@ export const FontPickerTrigger = ({
}: FontPickerTriggerProps) => { }: FontPickerTriggerProps) => {
const setAppState = useExcalidrawSetAppState(); const setAppState = useExcalidrawSetAppState();
const compactStyle = compactMode
? {
background: "rgba(255, 255, 255, 0.1)",
backdropFilter: "blur(20px)",
WebkitBackdropFilter: "blur(20px)",
width: "1.625rem",
height: "1.625rem",
}
: {};
return ( return (
<Popover.Trigger asChild> <Popover.Trigger asChild>
<div data-openpopup="fontFamily" className="properties-trigger"> <div data-openpopup="fontFamily" className="properties-trigger">
@@ -39,8 +49,7 @@ export const FontPickerTrigger = ({
}} }}
style={{ style={{
border: "none", border: "none",
width: compactMode ? "1.625rem" : undefined, ...compactStyle,
height: compactMode ? "1.625rem" : undefined,
}} }}
/> />
</div> </div>

View File

@@ -18,7 +18,7 @@ type LockIconProps = {
export const HandButton = (props: LockIconProps) => { export const HandButton = (props: LockIconProps) => {
return ( return (
<ToolButton <ToolButton
className={clsx("Shape", { fillable: false })} className={clsx("Shape", { fillable: false, active: props.checked })}
type="radio" type="radio"
icon={handIcon} icon={handIcon}
name="editor-current-shape" name="editor-current-shape"

View File

@@ -93,16 +93,7 @@ export const MobileMenu = ({
justifyContent: "space-between", justifyContent: "space-between",
}} }}
> >
<div <MainMenuTunnel.Out />
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
gap: 16,
}}
>
<MainMenuTunnel.Out />
</div>
{topRightUI ? topRightUI : <DefaultSidebarTriggerTunnel.Out />} {topRightUI ? topRightUI : <DefaultSidebarTriggerTunnel.Out />}
</div> </div>
); );

View File

@@ -39,6 +39,15 @@
.ToolIcon__icon { .ToolIcon__icon {
width: 2rem; width: 2rem;
height: 2rem; height: 2rem;
&:hover {
background-color: transparent;
}
}
&.active {
background: var(--button-active-bg, var(--island-bg-color));
border-color: var(--button-active-border, var(--color-primary-darkest));
} }
svg { svg {

View File

@@ -190,7 +190,6 @@ export const MobileToolBar = ({
options={SELECTION_TOOLS} options={SELECTION_TOOLS}
activeTool={activeTool} activeTool={activeTool}
defaultOption={app.defaultSelectionTool} defaultOption={app.defaultSelectionTool}
className="Selection"
namePrefix="selectionType" namePrefix="selectionType"
title={capitalizeString(t("toolBar.selection"))} title={capitalizeString(t("toolBar.selection"))}
data-testid="toolbar-selection" data-testid="toolbar-selection"
@@ -210,7 +209,9 @@ export const MobileToolBar = ({
{/* Free Draw */} {/* Free Draw */}
<ToolButton <ToolButton
className={clsx("Shape", { fillable: false })} className={clsx({
active: activeTool.type === "freedraw",
})}
type="radio" type="radio"
icon={FreedrawIcon} icon={FreedrawIcon}
checked={activeTool.type === "freedraw"} checked={activeTool.type === "freedraw"}
@@ -223,7 +224,9 @@ export const MobileToolBar = ({
{/* Eraser */} {/* Eraser */}
<ToolButton <ToolButton
className={clsx("Shape", { fillable: false })} className={clsx({
active: activeTool.type === "eraser",
})}
type="radio" type="radio"
icon={EraserIcon} icon={EraserIcon}
checked={activeTool.type === "eraser"} checked={activeTool.type === "eraser"}
@@ -296,7 +299,9 @@ export const MobileToolBar = ({
{/* Image */} {/* Image */}
<ToolButton <ToolButton
className={clsx("Shape", { fillable: false })} className={clsx({
active: activeTool.type === "image",
})}
type="radio" type="radio"
icon={ImageIcon} icon={ImageIcon}
checked={activeTool.type === "image"} checked={activeTool.type === "image"}
@@ -310,7 +315,9 @@ export const MobileToolBar = ({
{/* Text Tool */} {/* Text Tool */}
{showTextToolOutside && ( {showTextToolOutside && (
<ToolButton <ToolButton
className={clsx("Shape", { fillable: false })} className={clsx({
active: activeTool.type === "text",
})}
type="radio" type="radio"
icon={TextIcon} icon={TextIcon}
checked={activeTool.type === "text"} checked={activeTool.type === "text"}
@@ -325,7 +332,7 @@ export const MobileToolBar = ({
{/* Frame Tool */} {/* Frame Tool */}
{showFrameToolOutside && ( {showFrameToolOutside && (
<ToolButton <ToolButton
className={clsx("Shape", { fillable: false })} className={clsx({ active: frameToolSelected })}
type="radio" type="radio"
icon={frameToolIcon} icon={frameToolIcon}
checked={frameToolSelected} checked={frameToolSelected}

View File

@@ -48,11 +48,12 @@ export const ToolTypePopup = ({
const updatePosition = () => { const updatePosition = () => {
const triggerRect = triggerElement.getBoundingClientRect(); const triggerRect = triggerElement.getBoundingClientRect();
const panelRect = panelRef.current?.getBoundingClientRect(); const panelRect = panelRef.current?.getBoundingClientRect();
const panelWidth = panelRect?.width ?? 0; const panelWidth = panelRect?.width ?? 0;
const panelHeight = panelRect?.height ?? 0; const panelHeight = panelRect?.height ?? 0;
setPanelPosition({ setPanelPosition({
x: triggerRect.x - panelWidth / 2, x: triggerRect.left - panelWidth / 2,
y: panelHeight + 8, y: panelHeight + 8,
}); });
}; };
@@ -92,9 +93,9 @@ export const ToolTypePopup = ({
tabIndex={-1} tabIndex={-1}
style={{ style={{
position: "fixed", position: "fixed",
bottom: `${panelPosition.y}px`, top: `${-10}px`,
left: `${panelPosition.x}px`, left: `${panelPosition.x}px`,
zIndex: 2, zIndex: 9999,
}} }}
className={CLASSES.CONVERT_ELEMENT_TYPE_POPUP} className={CLASSES.CONVERT_ELEMENT_TYPE_POPUP}
> >
@@ -161,7 +162,13 @@ export const ToolWithPopup = ({
<div style={{ position: "relative" }}> <div style={{ position: "relative" }}>
<div ref={setTriggerRef}> <div ref={setTriggerRef}>
<ToolButton <ToolButton
className={clsx(className, { fillable })} className={clsx(className, {
fillable,
active:
isActive ||
isPopupOpen ||
options.some((o) => o.type === activeTool.type),
})}
type="radio" type="radio"
icon={displayedOption.icon} icon={displayedOption.icon}
checked={isActive} checked={isActive}

View File

@@ -3,7 +3,7 @@
.excalidraw { .excalidraw {
.dropdown-menu { .dropdown-menu {
position: absolute; position: absolute;
top: 100%; top: 2.5rem;
margin-top: 0.5rem; margin-top: 0.5rem;
&--placement-top { &--placement-top {
@@ -14,32 +14,35 @@
} }
&--mobile { &--mobile {
left: 0;
width: 100%; width: 100%;
row-gap: 0.75rem; row-gap: 0.75rem;
// When main menu is in the top toolbar, position relative to trigger // When main menu is in the top toolbar, position relative to trigger
&.main-menu-dropdown { &.main-menu-dropdown {
position: fixed; min-width: 232px;
left: 1rem; max-width: calc(100vw - var(--editor-container-padding) * 2);
top: 3.5rem;
min-width: 280px;
max-width: calc(100vw - 2rem);
margin-top: 0; margin-top: 0;
margin-bottom: 0; margin-bottom: 0;
z-index: var(--zIndex-layerUI); z-index: var(--zIndex-layerUI);
@media screen and (orientation: landscape) {
max-width: 232px;
}
} }
.dropdown-menu-container { .dropdown-menu-container {
padding: 8px 8px; padding: 8px 8px;
box-sizing: border-box; box-sizing: border-box;
// background-color: var(--island-bg-color); max-height: calc(
100svh - var(--editor-container-padding) * 2 - 2.25rem
);
box-shadow: var(--shadow-island); box-shadow: var(--shadow-island);
border-radius: var(--border-radius-lg); border-radius: var(--border-radius-lg);
position: relative; position: relative;
transition: box-shadow 0.5s ease-in-out; transition: box-shadow 0.5s ease-in-out;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
overflow-y: auto;
&.zen-mode { &.zen-mode {
box-shadow: none; box-shadow: none;
@@ -49,7 +52,7 @@
.dropdown-menu-container { .dropdown-menu-container {
background-color: var(--island-bg-color); background-color: var(--island-bg-color);
max-height: calc(100vh - 150px);
overflow-y: auto; overflow-y: auto;
--gap: 2; --gap: 2;
} }

View File

@@ -246,7 +246,7 @@ body.excalidraw-cursor-resize * {
left: 50%; left: 50%;
transform: translateX(-50%); transform: translateX(-50%);
--bar-padding: calc(4 * var(--space-factor)); --bar-padding: calc(4 * var(--space-factor));
z-index: 4; z-index: 3;
display: flex; display: flex;
flex-direction: column; flex-direction: column;