mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-09-23 17:30:44 +02:00
fix styling
This commit is contained in:
@@ -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;
|
||||||
|
@@ -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
|
||||||
|
: {}),
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
@@ -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
|
||||||
|
: {}),
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
@@ -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
|
||||||
|
: {}),
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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}
|
||||||
>
|
>
|
||||||
|
@@ -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;
|
||||||
|
@@ -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>
|
||||||
|
@@ -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>
|
||||||
|
@@ -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>
|
||||||
|
@@ -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"
|
||||||
|
@@ -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>
|
||||||
);
|
);
|
||||||
|
@@ -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 {
|
||||||
|
@@ -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}
|
||||||
|
@@ -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}
|
||||||
|
@@ -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;
|
||||||
}
|
}
|
||||||
|
@@ -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;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user