diff --git a/packages/common/src/constants.ts b/packages/common/src/constants.ts index b41fb1a37d..b7c31e5273 100644 --- a/packages/common/src/constants.ts +++ b/packages/common/src/constants.ts @@ -536,3 +536,10 @@ export enum UserIdleState { export const LINE_POLYGON_POINT_MERGE_DISTANCE = 20; 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; diff --git a/packages/excalidraw/actions/actionDeleteSelected.tsx b/packages/excalidraw/actions/actionDeleteSelected.tsx index 4fe8643eac..374236578f 100644 --- a/packages/excalidraw/actions/actionDeleteSelected.tsx +++ b/packages/excalidraw/actions/actionDeleteSelected.tsx @@ -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 { fixBindingsAfterDeletion } from "@excalidraw/element"; @@ -326,6 +330,11 @@ export const actionDeleteSelected = register({ disabled={ !isSomeElementSelected(getNonDeletedElements(elements), appState) } + style={{ + ...(appState.stylesPanelMode === "mobile" + ? MOBILE_ACTION_BUTTON_BG + : {}), + }} /> ), }); diff --git a/packages/excalidraw/actions/actionDuplicateSelection.tsx b/packages/excalidraw/actions/actionDuplicateSelection.tsx index 081980bd9d..4a84ccd76a 100644 --- a/packages/excalidraw/actions/actionDuplicateSelection.tsx +++ b/packages/excalidraw/actions/actionDuplicateSelection.tsx @@ -1,6 +1,7 @@ import { DEFAULT_GRID_SIZE, KEYS, + MOBILE_ACTION_BUTTON_BG, arrayToMap, getShortcutKey, } from "@excalidraw/common"; @@ -118,6 +119,11 @@ export const actionDuplicateSelection = register({ disabled={ !isSomeElementSelected(getNonDeletedElements(elements), appState) } + style={{ + ...(appState.stylesPanelMode === "mobile" + ? MOBILE_ACTION_BUTTON_BG + : {}), + }} /> ), }); diff --git a/packages/excalidraw/actions/actionHistory.tsx b/packages/excalidraw/actions/actionHistory.tsx index b948fe7d49..a1971f527c 100644 --- a/packages/excalidraw/actions/actionHistory.tsx +++ b/packages/excalidraw/actions/actionHistory.tsx @@ -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"; @@ -67,7 +73,7 @@ export const createUndoAction: ActionCreator = (history) => ({ ), keyTest: (event) => event[KEYS.CTRL_OR_CMD] && matchKey(event, KEYS.Z) && !event.shiftKey, - PanelComponent: ({ updateData, data }) => { + PanelComponent: ({ appState, updateData, data }) => { const { isUndoStackEmpty } = useEmitter( history.onHistoryChangedEmitter, new HistoryChangedEvent( @@ -85,6 +91,11 @@ export const createUndoAction: ActionCreator = (history) => ({ size={data?.size || "medium"} disabled={isUndoStackEmpty} data-testid="button-undo" + style={{ + ...(appState.stylesPanelMode === "mobile" + ? MOBILE_ACTION_BUTTON_BG + : {}), + }} /> ); }, @@ -103,7 +114,7 @@ export const createRedoAction: ActionCreator = (history) => ({ keyTest: (event) => (event[KEYS.CTRL_OR_CMD] && event.shiftKey && matchKey(event, KEYS.Z)) || (isWindows && event.ctrlKey && !event.shiftKey && matchKey(event, KEYS.Y)), - PanelComponent: ({ updateData, data }) => { + PanelComponent: ({ appState, updateData, data }) => { const { isRedoStackEmpty } = useEmitter( history.onHistoryChangedEmitter, new HistoryChangedEvent( @@ -121,6 +132,11 @@ export const createRedoAction: ActionCreator = (history) => ({ size={data?.size || "medium"} disabled={isRedoStackEmpty} data-testid="button-redo" + style={{ + ...(appState.stylesPanelMode === "mobile" + ? MOBILE_ACTION_BUTTON_BG + : {}), + }} /> ); }, diff --git a/packages/excalidraw/components/Actions.scss b/packages/excalidraw/components/Actions.scss index 90f5688117..c84413e228 100644 --- a/packages/excalidraw/components/Actions.scss +++ b/packages/excalidraw/components/Actions.scss @@ -106,6 +106,7 @@ justify-content: center; align-items: center; min-height: 2.5rem; + pointer-events: auto; --default-button-size: 2rem; @@ -114,7 +115,6 @@ height: 1.625rem; border: none; border-radius: var(--border-radius-lg); - background: transparent; color: var(--color-on-surface); cursor: pointer; display: flex; @@ -122,21 +122,17 @@ justify-content: center; transition: all 0.2s ease; + background: rgba(255, 255, 255, 0.1); + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); + svg { width: 1rem; height: 1rem; flex: 0 0 auto; } - &:hover { - background: var(--button-hover-bg, var(--island-bg-color)); - border-color: var( - --button-hover-border, - var(--button-border, var(--default-border-color)) - ); - } - - &:active { + &.active { background: var(--button-active-bg, var(--island-bg-color)); border-color: var(--button-active-border, var(--color-primary-darkest)); } @@ -168,9 +164,15 @@ } } - .ToolIcon__icon { - width: 1.625rem; - height: 1.625rem; + .ToolIcon { + .ToolIcon__icon { + width: 1.625rem; + height: 1.625rem; + + &:hover { + background-color: transparent; + } + } } } diff --git a/packages/excalidraw/components/Actions.tsx b/packages/excalidraw/components/Actions.tsx index 7d267d2773..25c76b537c 100644 --- a/packages/excalidraw/components/Actions.tsx +++ b/packages/excalidraw/components/Actions.tsx @@ -328,7 +328,7 @@ const CombinedShapeProperties = ({ hasBackground(element.type) && !isTransparent(element.backgroundColor), ); - const showShowCombinedProperties = + const shouldShowCombinedProperties = showFillIcons || hasStrokeWidth(appState.activeTool.type) || targetElements.some((element) => hasStrokeWidth(element.type)) || @@ -337,14 +337,16 @@ const CombinedShapeProperties = ({ canChangeRoundness(appState.activeTool.type) || targetElements.some((element) => canChangeRoundness(element.type)); - if (!showShowCombinedProperties) { + const isOpen = appState.openPopup === "compactStrokeStyles"; + + if (!shouldShowCombinedProperties) { return null; } return (
{ if (open) { setAppState({ openPopup: "compactStrokeStyles" }); @@ -356,24 +358,23 @@ const CombinedShapeProperties = ({ - {appState.openPopup === "compactStrokeStyles" && ( + {isOpen && ( toolIsArrow(element.type)); + const isOpen = appState.openPopup === "compactArrowProperties"; if (!showShowArrowProperties) { return null; @@ -436,7 +438,7 @@ const CombinedArrowProperties = ({ return (
{ if (open) { setAppState({ openPopup: "compactArrowProperties" }); @@ -448,17 +450,16 @@ const CombinedArrowProperties = ({ - {appState.openPopup === "compactArrowProperties" && ( + {isOpen && ( { const { saveCaretPosition, restoreCaretPosition } = useTextEditorFocus(); + const isOpen = appState.openPopup === "compactTextProperties"; return (
{ if (open) { if (appState.editingTextElement) { @@ -545,13 +547,15 @@ const CombinedTextProperties = ({ - {appState.openPopup === "compactOtherProperties" && ( + {isOpen && ( {/* Stroke Color */} {canChangeStrokeColor(appState, targetElements) && ( -
+
{renderAction("changeStrokeColor")}
)} @@ -925,6 +924,7 @@ export const MobileShapeActions = ({ height: WIDTH * 1.75, alignItems: "center", gap: GAP, + pointerEvents: "none", }} ref={mobileActionsRef} > diff --git a/packages/excalidraw/components/ColorPicker/ColorPicker.scss b/packages/excalidraw/components/ColorPicker/ColorPicker.scss index 0e3768dcc0..f6acb32b84 100644 --- a/packages/excalidraw/components/ColorPicker/ColorPicker.scss +++ b/packages/excalidraw/components/ColorPicker/ColorPicker.scss @@ -7,6 +7,12 @@ } } + .color-picker__title { + padding: 0 0.5rem; + font-size: 0.875rem; + text-align: left; + } + .color-picker__heading { padding: 0 0.5rem; font-size: 0.75rem; diff --git a/packages/excalidraw/components/ColorPicker/ColorPicker.tsx b/packages/excalidraw/components/ColorPicker/ColorPicker.tsx index 51c7bbd2c5..c97ae1a97d 100644 --- a/packages/excalidraw/components/ColorPicker/ColorPicker.tsx +++ b/packages/excalidraw/components/ColorPicker/ColorPicker.tsx @@ -18,7 +18,7 @@ import { useExcalidrawContainer } from "../App"; import { ButtonSeparator } from "../ButtonSeparator"; import { activeEyeDropperAtom } from "../EyeDropper"; import { PropertiesPopover } from "../PropertiesPopover"; -import { backgroundIcon, slashIcon, strokeIcon } from "../icons"; +import { slashIcon, strokeIcon } from "../icons"; import { saveCaretPosition, restoreCaretPosition, @@ -213,6 +213,10 @@ const ColorPickerPopupContent = ({ type={type} elements={elements} updateData={updateData} + showTitle={ + appState.stylesPanelMode === "compact" || + appState.stylesPanelMode === "mobile" + } > {colorInputJSX} @@ -272,31 +276,18 @@ const ColorPickerTrigger = ({ onClick={handleClick} >
{!color && slashIcon}
- {compactMode && color && ( + {compactMode && color && mode === "stroke" && (
- {mode === "background" ? ( - - {backgroundIcon} - - ) : ( - - {strokeIcon} - - )} + + {strokeIcon} +
)} diff --git a/packages/excalidraw/components/ColorPicker/Picker.tsx b/packages/excalidraw/components/ColorPicker/Picker.tsx index f784912f4c..e9943a858f 100644 --- a/packages/excalidraw/components/ColorPicker/Picker.tsx +++ b/packages/excalidraw/components/ColorPicker/Picker.tsx @@ -37,6 +37,7 @@ interface PickerProps { palette: ColorPaletteCustom; updateData: (formData?: any) => void; children?: React.ReactNode; + showTitle?: boolean; onEyeDropperToggle: (force?: boolean) => void; onEscape: (event: React.KeyboardEvent | KeyboardEvent) => void; } @@ -51,11 +52,20 @@ export const Picker = React.forwardRef( palette, updateData, children, + showTitle, onEyeDropperToggle, onEscape, }: PickerProps, ref, ) => { + const title = showTitle + ? type === "elementStroke" + ? t("labels.stroke") + : type === "elementBackground" + ? t("labels.background") + : null + : null; + const [customColors] = React.useState(() => { if (type === "canvasBackground") { return []; @@ -154,6 +164,8 @@ export const Picker = React.forwardRef( // to allow focusing by clicking but not by tabbing tabIndex={-1} > + {title &&
{title}
} + {!!customColors.length && (
diff --git a/packages/excalidraw/components/FontPicker/FontPickerTrigger.tsx b/packages/excalidraw/components/FontPicker/FontPickerTrigger.tsx index 5c122725c1..5d60a8c3b9 100644 --- a/packages/excalidraw/components/FontPicker/FontPickerTrigger.tsx +++ b/packages/excalidraw/components/FontPicker/FontPickerTrigger.tsx @@ -21,6 +21,16 @@ export const FontPickerTrigger = ({ }: FontPickerTriggerProps) => { 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 (
@@ -39,8 +49,7 @@ export const FontPickerTrigger = ({ }} style={{ border: "none", - width: compactMode ? "1.625rem" : undefined, - height: compactMode ? "1.625rem" : undefined, + ...compactStyle, }} />
diff --git a/packages/excalidraw/components/HandButton.tsx b/packages/excalidraw/components/HandButton.tsx index 5ebfdf9d3f..db653a8103 100644 --- a/packages/excalidraw/components/HandButton.tsx +++ b/packages/excalidraw/components/HandButton.tsx @@ -18,7 +18,7 @@ type LockIconProps = { export const HandButton = (props: LockIconProps) => { return ( -
- -
+ {topRightUI ? topRightUI : }
); diff --git a/packages/excalidraw/components/MobileToolBar.scss b/packages/excalidraw/components/MobileToolBar.scss index 0fefa43abc..34eefae7ef 100644 --- a/packages/excalidraw/components/MobileToolBar.scss +++ b/packages/excalidraw/components/MobileToolBar.scss @@ -39,6 +39,15 @@ .ToolIcon__icon { width: 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 { diff --git a/packages/excalidraw/components/MobileToolBar.tsx b/packages/excalidraw/components/MobileToolBar.tsx index 8d6aa80e31..3973575936 100644 --- a/packages/excalidraw/components/MobileToolBar.tsx +++ b/packages/excalidraw/components/MobileToolBar.tsx @@ -190,7 +190,6 @@ export const MobileToolBar = ({ options={SELECTION_TOOLS} activeTool={activeTool} defaultOption={app.defaultSelectionTool} - className="Selection" namePrefix="selectionType" title={capitalizeString(t("toolBar.selection"))} data-testid="toolbar-selection" @@ -210,7 +209,9 @@ export const MobileToolBar = ({ {/* Free Draw */} { const triggerRect = triggerElement.getBoundingClientRect(); + const panelRect = panelRef.current?.getBoundingClientRect(); const panelWidth = panelRect?.width ?? 0; const panelHeight = panelRect?.height ?? 0; setPanelPosition({ - x: triggerRect.x - panelWidth / 2, + x: triggerRect.left - panelWidth / 2, y: panelHeight + 8, }); }; @@ -92,9 +93,9 @@ export const ToolTypePopup = ({ tabIndex={-1} style={{ position: "fixed", - bottom: `${panelPosition.y}px`, + top: `${-10}px`, left: `${panelPosition.x}px`, - zIndex: 2, + zIndex: 9999, }} className={CLASSES.CONVERT_ELEMENT_TYPE_POPUP} > @@ -161,7 +162,13 @@ export const ToolWithPopup = ({
o.type === activeTool.type), + })} type="radio" icon={displayedOption.icon} checked={isActive} diff --git a/packages/excalidraw/components/dropdownMenu/DropdownMenu.scss b/packages/excalidraw/components/dropdownMenu/DropdownMenu.scss index eb6b5dff86..a0a230941d 100644 --- a/packages/excalidraw/components/dropdownMenu/DropdownMenu.scss +++ b/packages/excalidraw/components/dropdownMenu/DropdownMenu.scss @@ -3,7 +3,7 @@ .excalidraw { .dropdown-menu { position: absolute; - top: 100%; + top: 2.5rem; margin-top: 0.5rem; &--placement-top { @@ -14,32 +14,35 @@ } &--mobile { - left: 0; width: 100%; row-gap: 0.75rem; // When main menu is in the top toolbar, position relative to trigger &.main-menu-dropdown { - position: fixed; - left: 1rem; - top: 3.5rem; - min-width: 280px; - max-width: calc(100vw - 2rem); + min-width: 232px; + max-width: calc(100vw - var(--editor-container-padding) * 2); margin-top: 0; margin-bottom: 0; z-index: var(--zIndex-layerUI); + + @media screen and (orientation: landscape) { + max-width: 232px; + } } .dropdown-menu-container { padding: 8px 8px; 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); border-radius: var(--border-radius-lg); position: relative; transition: box-shadow 0.5s ease-in-out; display: flex; flex-direction: column; + overflow-y: auto; &.zen-mode { box-shadow: none; @@ -49,7 +52,7 @@ .dropdown-menu-container { background-color: var(--island-bg-color); - max-height: calc(100vh - 150px); + overflow-y: auto; --gap: 2; } diff --git a/packages/excalidraw/css/styles.scss b/packages/excalidraw/css/styles.scss index b11daf0a66..9b41a7372f 100644 --- a/packages/excalidraw/css/styles.scss +++ b/packages/excalidraw/css/styles.scss @@ -246,7 +246,7 @@ body.excalidraw-cursor-resize * { left: 50%; transform: translateX(-50%); --bar-padding: calc(4 * var(--space-factor)); - z-index: 4; + z-index: 3; display: flex; flex-direction: column;