feat: Introduce ExcalidrawElements and ExcalidrawAppState provider (#5463)
* feat: Introduce ExcalidrawData provider so that app state and elements need not be passed to children * fix * fix zen mode * Separate providers for data and elements * pass appState and elements to layerUI * pass appState and elements to selectedShapeActions * pass appState and elements to MobileMenu * pass appState to librarymenu * rename * rename to ExcalidrawAppState
This commit is contained in:
parent
46a61ad4df
commit
ec350ba8b2
@ -31,12 +31,10 @@ export const SelectedShapeActions = ({
|
|||||||
appState,
|
appState,
|
||||||
elements,
|
elements,
|
||||||
renderAction,
|
renderAction,
|
||||||
activeTool,
|
|
||||||
}: {
|
}: {
|
||||||
appState: AppState;
|
appState: AppState;
|
||||||
elements: readonly ExcalidrawElement[];
|
elements: readonly ExcalidrawElement[];
|
||||||
renderAction: ActionManager["renderAction"];
|
renderAction: ActionManager["renderAction"];
|
||||||
activeTool: AppState["activeTool"]["type"];
|
|
||||||
}) => {
|
}) => {
|
||||||
const targetElements = getTargetElements(
|
const targetElements = getTargetElements(
|
||||||
getNonDeletedElements(elements),
|
getNonDeletedElements(elements),
|
||||||
@ -56,13 +54,13 @@ export const SelectedShapeActions = ({
|
|||||||
const isRTL = document.documentElement.getAttribute("dir") === "rtl";
|
const isRTL = document.documentElement.getAttribute("dir") === "rtl";
|
||||||
|
|
||||||
const showFillIcons =
|
const showFillIcons =
|
||||||
hasBackground(activeTool) ||
|
hasBackground(appState.activeTool.type) ||
|
||||||
targetElements.some(
|
targetElements.some(
|
||||||
(element) =>
|
(element) =>
|
||||||
hasBackground(element.type) && !isTransparent(element.backgroundColor),
|
hasBackground(element.type) && !isTransparent(element.backgroundColor),
|
||||||
);
|
);
|
||||||
const showChangeBackgroundIcons =
|
const showChangeBackgroundIcons =
|
||||||
hasBackground(activeTool) ||
|
hasBackground(appState.activeTool.type) ||
|
||||||
targetElements.some((element) => hasBackground(element.type));
|
targetElements.some((element) => hasBackground(element.type));
|
||||||
|
|
||||||
const showLinkIcon =
|
const showLinkIcon =
|
||||||
@ -79,23 +77,23 @@ export const SelectedShapeActions = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="panelColumn">
|
<div className="panelColumn">
|
||||||
{((hasStrokeColor(activeTool) &&
|
{((hasStrokeColor(appState.activeTool.type) &&
|
||||||
activeTool !== "image" &&
|
appState.activeTool.type !== "image" &&
|
||||||
commonSelectedType !== "image") ||
|
commonSelectedType !== "image") ||
|
||||||
targetElements.some((element) => hasStrokeColor(element.type))) &&
|
targetElements.some((element) => hasStrokeColor(element.type))) &&
|
||||||
renderAction("changeStrokeColor")}
|
renderAction("changeStrokeColor")}
|
||||||
{showChangeBackgroundIcons && renderAction("changeBackgroundColor")}
|
{showChangeBackgroundIcons && renderAction("changeBackgroundColor")}
|
||||||
{showFillIcons && renderAction("changeFillStyle")}
|
{showFillIcons && renderAction("changeFillStyle")}
|
||||||
|
|
||||||
{(hasStrokeWidth(activeTool) ||
|
{(hasStrokeWidth(appState.activeTool.type) ||
|
||||||
targetElements.some((element) => hasStrokeWidth(element.type))) &&
|
targetElements.some((element) => hasStrokeWidth(element.type))) &&
|
||||||
renderAction("changeStrokeWidth")}
|
renderAction("changeStrokeWidth")}
|
||||||
|
|
||||||
{(activeTool === "freedraw" ||
|
{(appState.activeTool.type === "freedraw" ||
|
||||||
targetElements.some((element) => element.type === "freedraw")) &&
|
targetElements.some((element) => element.type === "freedraw")) &&
|
||||||
renderAction("changeStrokeShape")}
|
renderAction("changeStrokeShape")}
|
||||||
|
|
||||||
{(hasStrokeStyle(activeTool) ||
|
{(hasStrokeStyle(appState.activeTool.type) ||
|
||||||
targetElements.some((element) => hasStrokeStyle(element.type))) && (
|
targetElements.some((element) => hasStrokeStyle(element.type))) && (
|
||||||
<>
|
<>
|
||||||
{renderAction("changeStrokeStyle")}
|
{renderAction("changeStrokeStyle")}
|
||||||
@ -103,12 +101,12 @@ export const SelectedShapeActions = ({
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{(canChangeSharpness(activeTool) ||
|
{(canChangeSharpness(appState.activeTool.type) ||
|
||||||
targetElements.some((element) => canChangeSharpness(element.type))) && (
|
targetElements.some((element) => canChangeSharpness(element.type))) && (
|
||||||
<>{renderAction("changeSharpness")}</>
|
<>{renderAction("changeSharpness")}</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{(hasText(activeTool) ||
|
{(hasText(appState.activeTool.type) ||
|
||||||
targetElements.some((element) => hasText(element.type))) && (
|
targetElements.some((element) => hasText(element.type))) && (
|
||||||
<>
|
<>
|
||||||
{renderAction("changeFontSize")}
|
{renderAction("changeFontSize")}
|
||||||
@ -123,7 +121,7 @@ export const SelectedShapeActions = ({
|
|||||||
(element) =>
|
(element) =>
|
||||||
hasBoundTextElement(element) || isBoundToContainer(element),
|
hasBoundTextElement(element) || isBoundToContainer(element),
|
||||||
) && renderAction("changeVerticalAlign")}
|
) && renderAction("changeVerticalAlign")}
|
||||||
{(canHaveArrowheads(activeTool) ||
|
{(canHaveArrowheads(appState.activeTool.type) ||
|
||||||
targetElements.some((element) => canHaveArrowheads(element.type))) && (
|
targetElements.some((element) => canHaveArrowheads(element.type))) && (
|
||||||
<>{renderAction("changeArrowhead")}</>
|
<>{renderAction("changeArrowhead")}</>
|
||||||
)}
|
)}
|
||||||
|
@ -272,6 +272,7 @@ const deviceContextInitialValue = {
|
|||||||
};
|
};
|
||||||
const DeviceContext = React.createContext<Device>(deviceContextInitialValue);
|
const DeviceContext = React.createContext<Device>(deviceContextInitialValue);
|
||||||
export const useDevice = () => useContext<Device>(DeviceContext);
|
export const useDevice = () => useContext<Device>(DeviceContext);
|
||||||
|
|
||||||
const ExcalidrawContainerContext = React.createContext<{
|
const ExcalidrawContainerContext = React.createContext<{
|
||||||
container: HTMLDivElement | null;
|
container: HTMLDivElement | null;
|
||||||
id: string | null;
|
id: string | null;
|
||||||
@ -279,6 +280,22 @@ const ExcalidrawContainerContext = React.createContext<{
|
|||||||
export const useExcalidrawContainer = () =>
|
export const useExcalidrawContainer = () =>
|
||||||
useContext(ExcalidrawContainerContext);
|
useContext(ExcalidrawContainerContext);
|
||||||
|
|
||||||
|
const ExcalidrawElementsContext = React.createContext<
|
||||||
|
readonly NonDeletedExcalidrawElement[]
|
||||||
|
>([]);
|
||||||
|
|
||||||
|
const ExcalidrawAppStateContext = React.createContext<AppState>({
|
||||||
|
...getDefaultAppState(),
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
offsetLeft: 0,
|
||||||
|
offsetTop: 0,
|
||||||
|
});
|
||||||
|
export const useExcalidrawElements = () =>
|
||||||
|
useContext(ExcalidrawElementsContext);
|
||||||
|
export const useExcalidrawAppState = () =>
|
||||||
|
useContext(ExcalidrawAppStateContext);
|
||||||
|
|
||||||
let didTapTwice: boolean = false;
|
let didTapTwice: boolean = false;
|
||||||
let tappedTwiceTimer = 0;
|
let tappedTwiceTimer = 0;
|
||||||
let cursorX = 0;
|
let cursorX = 0;
|
||||||
@ -505,6 +522,10 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
value={this.excalidrawContainerValue}
|
value={this.excalidrawContainerValue}
|
||||||
>
|
>
|
||||||
<DeviceContext.Provider value={this.device}>
|
<DeviceContext.Provider value={this.device}>
|
||||||
|
<ExcalidrawAppStateContext.Provider value={this.state}>
|
||||||
|
<ExcalidrawElementsContext.Provider
|
||||||
|
value={this.scene.getNonDeletedElements()}
|
||||||
|
>
|
||||||
<LayerUI
|
<LayerUI
|
||||||
canvas={this.canvas}
|
canvas={this.canvas}
|
||||||
appState={this.state}
|
appState={this.state}
|
||||||
@ -544,11 +565,11 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
/>
|
/>
|
||||||
<div className="excalidraw-textEditorContainer" />
|
<div className="excalidraw-textEditorContainer" />
|
||||||
<div className="excalidraw-contextMenuContainer" />
|
<div className="excalidraw-contextMenuContainer" />
|
||||||
{selectedElement.length === 1 && this.state.showHyperlinkPopup && (
|
{selectedElement.length === 1 &&
|
||||||
|
this.state.showHyperlinkPopup && (
|
||||||
<Hyperlink
|
<Hyperlink
|
||||||
key={selectedElement[0].id}
|
key={selectedElement[0].id}
|
||||||
element={selectedElement[0]}
|
element={selectedElement[0]}
|
||||||
appState={this.state}
|
|
||||||
setAppState={this.setAppState}
|
setAppState={this.setAppState}
|
||||||
onLinkOpen={this.props.onLinkOpen}
|
onLinkOpen={this.props.onLinkOpen}
|
||||||
/>
|
/>
|
||||||
@ -562,6 +583,8 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<main>{this.renderCanvas()}</main>
|
<main>{this.renderCanvas()}</main>
|
||||||
|
</ExcalidrawElementsContext.Provider>{" "}
|
||||||
|
</ExcalidrawAppStateContext.Provider>
|
||||||
</DeviceContext.Provider>
|
</DeviceContext.Provider>
|
||||||
</ExcalidrawContainerContext.Provider>
|
</ExcalidrawContainerContext.Provider>
|
||||||
</div>
|
</div>
|
||||||
|
@ -71,8 +71,8 @@ const LayerUI = ({
|
|||||||
appState,
|
appState,
|
||||||
files,
|
files,
|
||||||
setAppState,
|
setAppState,
|
||||||
canvas,
|
|
||||||
elements,
|
elements,
|
||||||
|
canvas,
|
||||||
onCollabButtonClick,
|
onCollabButtonClick,
|
||||||
onLockToggle,
|
onLockToggle,
|
||||||
onPenModeToggle,
|
onPenModeToggle,
|
||||||
@ -210,8 +210,8 @@ const LayerUI = ({
|
|||||||
)}
|
)}
|
||||||
</Stack.Row>
|
</Stack.Row>
|
||||||
<BackgroundPickerAndDarkModeToggle
|
<BackgroundPickerAndDarkModeToggle
|
||||||
actionManager={actionManager}
|
|
||||||
appState={appState}
|
appState={appState}
|
||||||
|
actionManager={actionManager}
|
||||||
setAppState={setAppState}
|
setAppState={setAppState}
|
||||||
showThemeBtn={showThemeBtn}
|
showThemeBtn={showThemeBtn}
|
||||||
/>
|
/>
|
||||||
@ -244,7 +244,6 @@ const LayerUI = ({
|
|||||||
appState={appState}
|
appState={appState}
|
||||||
elements={elements}
|
elements={elements}
|
||||||
renderAction={actionManager.renderAction}
|
renderAction={actionManager.renderAction}
|
||||||
activeTool={appState.activeTool.type}
|
|
||||||
/>
|
/>
|
||||||
</Island>
|
</Island>
|
||||||
</Section>
|
</Section>
|
||||||
@ -279,7 +278,6 @@ const LayerUI = ({
|
|||||||
libraryReturnUrl={libraryReturnUrl}
|
libraryReturnUrl={libraryReturnUrl}
|
||||||
focusContainer={focusContainer}
|
focusContainer={focusContainer}
|
||||||
library={library}
|
library={library}
|
||||||
theme={appState.theme}
|
|
||||||
files={files}
|
files={files}
|
||||||
id={id}
|
id={id}
|
||||||
appState={appState}
|
appState={appState}
|
||||||
|
@ -80,7 +80,6 @@ export const LibraryMenu = ({
|
|||||||
onInsertLibraryItems,
|
onInsertLibraryItems,
|
||||||
pendingElements,
|
pendingElements,
|
||||||
onAddToLibrary,
|
onAddToLibrary,
|
||||||
theme,
|
|
||||||
setAppState,
|
setAppState,
|
||||||
files,
|
files,
|
||||||
libraryReturnUrl,
|
libraryReturnUrl,
|
||||||
@ -93,7 +92,6 @@ export const LibraryMenu = ({
|
|||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onInsertLibraryItems: (libraryItems: LibraryItems) => void;
|
onInsertLibraryItems: (libraryItems: LibraryItems) => void;
|
||||||
onAddToLibrary: () => void;
|
onAddToLibrary: () => void;
|
||||||
theme: AppState["theme"];
|
|
||||||
files: BinaryFiles;
|
files: BinaryFiles;
|
||||||
setAppState: React.Component<any, AppState>["setState"];
|
setAppState: React.Component<any, AppState>["setState"];
|
||||||
libraryReturnUrl: ExcalidrawProps["libraryReturnUrl"];
|
libraryReturnUrl: ExcalidrawProps["libraryReturnUrl"];
|
||||||
@ -105,7 +103,6 @@ export const LibraryMenu = ({
|
|||||||
const ref = useRef<HTMLDivElement | null>(null);
|
const ref = useRef<HTMLDivElement | null>(null);
|
||||||
|
|
||||||
const device = useDevice();
|
const device = useDevice();
|
||||||
|
|
||||||
useOnClickOutside(
|
useOnClickOutside(
|
||||||
ref,
|
ref,
|
||||||
useCallback(
|
useCallback(
|
||||||
@ -290,7 +287,7 @@ export const LibraryMenu = ({
|
|||||||
appState={appState}
|
appState={appState}
|
||||||
libraryReturnUrl={libraryReturnUrl}
|
libraryReturnUrl={libraryReturnUrl}
|
||||||
library={library}
|
library={library}
|
||||||
theme={theme}
|
theme={appState.theme}
|
||||||
files={files}
|
files={files}
|
||||||
id={id}
|
id={id}
|
||||||
selectedItems={selectedItems}
|
selectedItems={selectedItems}
|
||||||
|
@ -221,7 +221,6 @@ export const MobileMenu = ({
|
|||||||
appState={appState}
|
appState={appState}
|
||||||
elements={elements}
|
elements={elements}
|
||||||
renderAction={actionManager.renderAction}
|
renderAction={actionManager.renderAction}
|
||||||
activeTool={appState.activeTool.type}
|
|
||||||
/>
|
/>
|
||||||
</Section>
|
</Section>
|
||||||
) : null}
|
) : null}
|
||||||
|
@ -32,6 +32,7 @@ import { getElementAbsoluteCoords } from "./";
|
|||||||
|
|
||||||
import "./Hyperlink.scss";
|
import "./Hyperlink.scss";
|
||||||
import { trackEvent } from "../analytics";
|
import { trackEvent } from "../analytics";
|
||||||
|
import { useExcalidrawAppState } from "../components/App";
|
||||||
|
|
||||||
const CONTAINER_WIDTH = 320;
|
const CONTAINER_WIDTH = 320;
|
||||||
const SPACE_BOTTOM = 85;
|
const SPACE_BOTTOM = 85;
|
||||||
@ -48,15 +49,15 @@ let IS_HYPERLINK_TOOLTIP_VISIBLE = false;
|
|||||||
|
|
||||||
export const Hyperlink = ({
|
export const Hyperlink = ({
|
||||||
element,
|
element,
|
||||||
appState,
|
|
||||||
setAppState,
|
setAppState,
|
||||||
onLinkOpen,
|
onLinkOpen,
|
||||||
}: {
|
}: {
|
||||||
element: NonDeletedExcalidrawElement;
|
element: NonDeletedExcalidrawElement;
|
||||||
appState: AppState;
|
|
||||||
setAppState: React.Component<any, AppState>["setState"];
|
setAppState: React.Component<any, AppState>["setState"];
|
||||||
onLinkOpen: ExcalidrawProps["onLinkOpen"];
|
onLinkOpen: ExcalidrawProps["onLinkOpen"];
|
||||||
}) => {
|
}) => {
|
||||||
|
const appState = useExcalidrawAppState();
|
||||||
|
|
||||||
const linkVal = element.link || "";
|
const linkVal = element.link || "";
|
||||||
|
|
||||||
const [inputVal, setInputVal] = useState(linkVal);
|
const [inputVal, setInputVal] = useState(linkVal);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user