mirror of
https://github.com/excalidraw/excalidraw.git
synced 2024-11-02 03:25:53 +01:00
feat: merge search sidebar back to default sidebar (#8497)
This commit is contained in:
parent
fd39712ba6
commit
813f9b702e
@ -20,7 +20,10 @@ import {
|
|||||||
get,
|
get,
|
||||||
} from "idb-keyval";
|
} from "idb-keyval";
|
||||||
import { clearAppStateForLocalStorage } from "../../packages/excalidraw/appState";
|
import { clearAppStateForLocalStorage } from "../../packages/excalidraw/appState";
|
||||||
import { SEARCH_SIDEBAR } from "../../packages/excalidraw/constants";
|
import {
|
||||||
|
CANVAS_SEARCH_TAB,
|
||||||
|
DEFAULT_SIDEBAR,
|
||||||
|
} from "../../packages/excalidraw/constants";
|
||||||
import type { LibraryPersistedData } from "../../packages/excalidraw/data/library";
|
import type { LibraryPersistedData } from "../../packages/excalidraw/data/library";
|
||||||
import type { ImportedDataState } from "../../packages/excalidraw/data/types";
|
import type { ImportedDataState } from "../../packages/excalidraw/data/types";
|
||||||
import { clearElementsForLocalStorage } from "../../packages/excalidraw/element";
|
import { clearElementsForLocalStorage } from "../../packages/excalidraw/element";
|
||||||
@ -69,7 +72,10 @@ const saveDataStateToLocalStorage = (
|
|||||||
try {
|
try {
|
||||||
const _appState = clearAppStateForLocalStorage(appState);
|
const _appState = clearAppStateForLocalStorage(appState);
|
||||||
|
|
||||||
if (_appState.openSidebar?.name === SEARCH_SIDEBAR.name) {
|
if (
|
||||||
|
_appState.openSidebar?.name === DEFAULT_SIDEBAR.name &&
|
||||||
|
_appState.openSidebar.tab === CANVAS_SEARCH_TAB
|
||||||
|
) {
|
||||||
_appState.openSidebar = null;
|
_appState.openSidebar = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ import { register } from "./register";
|
|||||||
import type { AppState } from "../types";
|
import type { AppState } from "../types";
|
||||||
import { searchIcon } from "../components/icons";
|
import { searchIcon } from "../components/icons";
|
||||||
import { StoreAction } from "../store";
|
import { StoreAction } from "../store";
|
||||||
import { CLASSES, SEARCH_SIDEBAR } from "../constants";
|
import { CANVAS_SEARCH_TAB, CLASSES, DEFAULT_SIDEBAR } from "../constants";
|
||||||
|
|
||||||
export const actionToggleSearchMenu = register({
|
export const actionToggleSearchMenu = register({
|
||||||
name: "searchMenu",
|
name: "searchMenu",
|
||||||
@ -17,7 +17,10 @@ export const actionToggleSearchMenu = register({
|
|||||||
predicate: (appState) => appState.gridModeEnabled,
|
predicate: (appState) => appState.gridModeEnabled,
|
||||||
},
|
},
|
||||||
perform(elements, appState, _, app) {
|
perform(elements, appState, _, app) {
|
||||||
if (appState.openSidebar?.name === SEARCH_SIDEBAR.name) {
|
if (
|
||||||
|
appState.openSidebar?.name === DEFAULT_SIDEBAR.name &&
|
||||||
|
appState.openSidebar.tab === CANVAS_SEARCH_TAB
|
||||||
|
) {
|
||||||
const searchInput =
|
const searchInput =
|
||||||
app.excalidrawContainerValue.container?.querySelector<HTMLInputElement>(
|
app.excalidrawContainerValue.container?.querySelector<HTMLInputElement>(
|
||||||
`.${CLASSES.SEARCH_MENU_INPUT_WRAPPER} input`,
|
`.${CLASSES.SEARCH_MENU_INPUT_WRAPPER} input`,
|
||||||
@ -31,13 +34,14 @@ export const actionToggleSearchMenu = register({
|
|||||||
}
|
}
|
||||||
|
|
||||||
searchInput?.focus();
|
searchInput?.focus();
|
||||||
|
searchInput?.select();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
appState: {
|
appState: {
|
||||||
...appState,
|
...appState,
|
||||||
openSidebar: { name: SEARCH_SIDEBAR.name },
|
openSidebar: { name: DEFAULT_SIDEBAR.name, tab: CANVAS_SEARCH_TAB },
|
||||||
openDialog: null,
|
openDialog: null,
|
||||||
},
|
},
|
||||||
storeAction: StoreAction.NONE,
|
storeAction: StoreAction.NONE,
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import { DEFAULT_SIDEBAR, LIBRARY_SIDEBAR_TAB } from "../constants";
|
import {
|
||||||
|
CANVAS_SEARCH_TAB,
|
||||||
|
DEFAULT_SIDEBAR,
|
||||||
|
LIBRARY_SIDEBAR_TAB,
|
||||||
|
} from "../constants";
|
||||||
import { useTunnels } from "../context/tunnels";
|
import { useTunnels } from "../context/tunnels";
|
||||||
import { useUIAppState } from "../context/ui-appState";
|
import { useUIAppState } from "../context/ui-appState";
|
||||||
import type { MarkOptional, Merge } from "../utility-types";
|
import type { MarkOptional, Merge } from "../utility-types";
|
||||||
@ -10,7 +14,8 @@ import { LibraryMenu } from "./LibraryMenu";
|
|||||||
import type { SidebarProps, SidebarTriggerProps } from "./Sidebar/common";
|
import type { SidebarProps, SidebarTriggerProps } from "./Sidebar/common";
|
||||||
import { Sidebar } from "./Sidebar/Sidebar";
|
import { Sidebar } from "./Sidebar/Sidebar";
|
||||||
import "../components/dropdownMenu/DropdownMenu.scss";
|
import "../components/dropdownMenu/DropdownMenu.scss";
|
||||||
import { t } from "../i18n";
|
import { SearchMenu } from "./SearchMenu";
|
||||||
|
import { LibraryIcon, searchIcon } from "./icons";
|
||||||
|
|
||||||
const DefaultSidebarTrigger = withInternalFallback(
|
const DefaultSidebarTrigger = withInternalFallback(
|
||||||
"DefaultSidebarTrigger",
|
"DefaultSidebarTrigger",
|
||||||
@ -66,16 +71,20 @@ export const DefaultSidebar = Object.assign(
|
|||||||
|
|
||||||
const { DefaultSidebarTabTriggersTunnel } = useTunnels();
|
const { DefaultSidebarTabTriggersTunnel } = useTunnels();
|
||||||
|
|
||||||
|
const isForceDocked = appState.openSidebar?.tab === CANVAS_SEARCH_TAB;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Sidebar
|
<Sidebar
|
||||||
{...rest}
|
{...rest}
|
||||||
name={"default"}
|
name={"default"}
|
||||||
className={clsx("default-sidebar", className)}
|
className={clsx("default-sidebar", className)}
|
||||||
docked={docked ?? appState.defaultSidebarDockedPreference}
|
docked={
|
||||||
|
isForceDocked || (docked ?? appState.defaultSidebarDockedPreference)
|
||||||
|
}
|
||||||
onDock={
|
onDock={
|
||||||
// `onDock=false` disables docking.
|
// `onDock=false` disables docking.
|
||||||
// if `docked` passed, but no onDock passed, disable manual docking.
|
// if `docked` passed, but no onDock passed, disable manual docking.
|
||||||
onDock === false || (!onDock && docked != null)
|
isForceDocked || onDock === false || (!onDock && docked != null)
|
||||||
? undefined
|
? undefined
|
||||||
: // compose to allow the host app to listen on default behavior
|
: // compose to allow the host app to listen on default behavior
|
||||||
composeEventHandlers(onDock, (docked) => {
|
composeEventHandlers(onDock, (docked) => {
|
||||||
@ -85,26 +94,22 @@ export const DefaultSidebar = Object.assign(
|
|||||||
>
|
>
|
||||||
<Sidebar.Tabs>
|
<Sidebar.Tabs>
|
||||||
<Sidebar.Header>
|
<Sidebar.Header>
|
||||||
{rest.__fallback && (
|
<DefaultSidebar.TabTriggers>
|
||||||
<div
|
<Sidebar.TabTrigger tab={CANVAS_SEARCH_TAB}>
|
||||||
style={{
|
{searchIcon}
|
||||||
color: "var(--color-primary)",
|
</Sidebar.TabTrigger>
|
||||||
fontSize: "1.2em",
|
<Sidebar.TabTrigger tab={LIBRARY_SIDEBAR_TAB}>
|
||||||
fontWeight: "bold",
|
{LibraryIcon}
|
||||||
textOverflow: "ellipsis",
|
</Sidebar.TabTrigger>
|
||||||
overflow: "hidden",
|
</DefaultSidebar.TabTriggers>
|
||||||
whiteSpace: "nowrap",
|
{rest.__fallback && <DefaultSidebarTabTriggersTunnel.Out />}
|
||||||
paddingRight: "1em",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{t("toolBar.library")}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<DefaultSidebarTabTriggersTunnel.Out />
|
|
||||||
</Sidebar.Header>
|
</Sidebar.Header>
|
||||||
<Sidebar.Tab tab={LIBRARY_SIDEBAR_TAB}>
|
<Sidebar.Tab tab={LIBRARY_SIDEBAR_TAB}>
|
||||||
<LibraryMenu />
|
<LibraryMenu />
|
||||||
</Sidebar.Tab>
|
</Sidebar.Tab>
|
||||||
|
<Sidebar.Tab tab={CANVAS_SEARCH_TAB}>
|
||||||
|
<SearchMenu />
|
||||||
|
</Sidebar.Tab>
|
||||||
{children}
|
{children}
|
||||||
</Sidebar.Tabs>
|
</Sidebar.Tabs>
|
||||||
</Sidebar>
|
</Sidebar>
|
||||||
|
@ -13,7 +13,7 @@ import { isEraserActive } from "../appState";
|
|||||||
import "./HintViewer.scss";
|
import "./HintViewer.scss";
|
||||||
import { isNodeInFlowchart } from "../element/flowchart";
|
import { isNodeInFlowchart } from "../element/flowchart";
|
||||||
import { isGridModeEnabled } from "../snapping";
|
import { isGridModeEnabled } from "../snapping";
|
||||||
import { SEARCH_SIDEBAR } from "../constants";
|
import { CANVAS_SEARCH_TAB, DEFAULT_SIDEBAR } from "../constants";
|
||||||
|
|
||||||
interface HintViewerProps {
|
interface HintViewerProps {
|
||||||
appState: UIAppState;
|
appState: UIAppState;
|
||||||
@ -32,7 +32,8 @@ const getHints = ({
|
|||||||
const multiMode = appState.multiElement !== null;
|
const multiMode = appState.multiElement !== null;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
appState.openSidebar?.name === SEARCH_SIDEBAR.name &&
|
appState.openSidebar?.name === DEFAULT_SIDEBAR.name &&
|
||||||
|
appState.openSidebar.tab === CANVAS_SEARCH_TAB &&
|
||||||
appState.searchMatches?.length
|
appState.searchMatches?.length
|
||||||
) {
|
) {
|
||||||
return t("hints.dismissSearch");
|
return t("hints.dismissSearch");
|
||||||
|
@ -5,7 +5,6 @@ import {
|
|||||||
CLASSES,
|
CLASSES,
|
||||||
DEFAULT_SIDEBAR,
|
DEFAULT_SIDEBAR,
|
||||||
LIBRARY_SIDEBAR_WIDTH,
|
LIBRARY_SIDEBAR_WIDTH,
|
||||||
SEARCH_SIDEBAR,
|
|
||||||
TOOL_TYPE,
|
TOOL_TYPE,
|
||||||
} from "../constants";
|
} from "../constants";
|
||||||
import { showSelectedShapeActions } from "../element";
|
import { showSelectedShapeActions } from "../element";
|
||||||
@ -54,9 +53,6 @@ import { LibraryIcon } from "./icons";
|
|||||||
import { UIAppStateContext } from "../context/ui-appState";
|
import { UIAppStateContext } from "../context/ui-appState";
|
||||||
import { DefaultSidebar } from "./DefaultSidebar";
|
import { DefaultSidebar } from "./DefaultSidebar";
|
||||||
import { EyeDropper, activeEyeDropperAtom } from "./EyeDropper";
|
import { EyeDropper, activeEyeDropperAtom } from "./EyeDropper";
|
||||||
|
|
||||||
import "./LayerUI.scss";
|
|
||||||
import "./Toolbar.scss";
|
|
||||||
import { mutateElement } from "../element/mutateElement";
|
import { mutateElement } from "../element/mutateElement";
|
||||||
import { ShapeCache } from "../scene/ShapeCache";
|
import { ShapeCache } from "../scene/ShapeCache";
|
||||||
import Scene from "../scene/Scene";
|
import Scene from "../scene/Scene";
|
||||||
@ -64,7 +60,9 @@ import { LaserPointerButton } from "./LaserPointerButton";
|
|||||||
import { TTDDialog } from "./TTDDialog/TTDDialog";
|
import { TTDDialog } from "./TTDDialog/TTDDialog";
|
||||||
import { Stats } from "./Stats";
|
import { Stats } from "./Stats";
|
||||||
import { actionToggleStats } from "../actions";
|
import { actionToggleStats } from "../actions";
|
||||||
import { SearchSidebar } from "./SearchSidebar";
|
|
||||||
|
import "./LayerUI.scss";
|
||||||
|
import "./Toolbar.scss";
|
||||||
|
|
||||||
interface LayerUIProps {
|
interface LayerUIProps {
|
||||||
actionManager: ActionManager;
|
actionManager: ActionManager;
|
||||||
@ -365,21 +363,16 @@ const LayerUI = ({
|
|||||||
|
|
||||||
const renderSidebars = () => {
|
const renderSidebars = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<DefaultSidebar
|
||||||
{appState.openSidebar?.name === SEARCH_SIDEBAR.name && (
|
__fallback
|
||||||
<SearchSidebar />
|
onDock={(docked) => {
|
||||||
)}
|
trackEvent(
|
||||||
<DefaultSidebar
|
"sidebar",
|
||||||
__fallback
|
`toggleDock (${docked ? "dock" : "undock"})`,
|
||||||
onDock={(docked) => {
|
`(${device.editor.isMobile ? "mobile" : "desktop"})`,
|
||||||
trackEvent(
|
);
|
||||||
"sidebar",
|
}}
|
||||||
`toggleDock (${docked ? "dock" : "undock"})`,
|
/>
|
||||||
`(${device.editor.isMobile ? "mobile" : "desktop"})`,
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,29 +0,0 @@
|
|||||||
import { SEARCH_SIDEBAR } from "../constants";
|
|
||||||
import { t } from "../i18n";
|
|
||||||
import { SearchMenu } from "./SearchMenu";
|
|
||||||
import { Sidebar } from "./Sidebar/Sidebar";
|
|
||||||
|
|
||||||
export const SearchSidebar = () => {
|
|
||||||
return (
|
|
||||||
<Sidebar name={SEARCH_SIDEBAR.name} docked>
|
|
||||||
<Sidebar.Tabs>
|
|
||||||
<Sidebar.Header>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
color: "var(--color-primary)",
|
|
||||||
fontSize: "1.2em",
|
|
||||||
fontWeight: "bold",
|
|
||||||
textOverflow: "ellipsis",
|
|
||||||
overflow: "hidden",
|
|
||||||
whiteSpace: "nowrap",
|
|
||||||
paddingRight: "1em",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{t("search.title")}
|
|
||||||
</div>
|
|
||||||
</Sidebar.Header>
|
|
||||||
<SearchMenu />
|
|
||||||
</Sidebar.Tabs>
|
|
||||||
</Sidebar>
|
|
||||||
);
|
|
||||||
};
|
|
@ -377,16 +377,13 @@ export const DEFAULT_ELEMENT_PROPS: {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const LIBRARY_SIDEBAR_TAB = "library";
|
export const LIBRARY_SIDEBAR_TAB = "library";
|
||||||
|
export const CANVAS_SEARCH_TAB = "search";
|
||||||
|
|
||||||
export const DEFAULT_SIDEBAR = {
|
export const DEFAULT_SIDEBAR = {
|
||||||
name: "default",
|
name: "default",
|
||||||
defaultTab: LIBRARY_SIDEBAR_TAB,
|
defaultTab: LIBRARY_SIDEBAR_TAB,
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const SEARCH_SIDEBAR = {
|
|
||||||
name: "search",
|
|
||||||
};
|
|
||||||
|
|
||||||
export const LIBRARY_DISABLED_TYPES = new Set([
|
export const LIBRARY_DISABLED_TYPES = new Set([
|
||||||
"iframe",
|
"iframe",
|
||||||
"embeddable",
|
"embeddable",
|
||||||
|
@ -167,7 +167,7 @@
|
|||||||
"noMatch": "No matches found...",
|
"noMatch": "No matches found...",
|
||||||
"singleResult": "result",
|
"singleResult": "result",
|
||||||
"multipleResults": "results",
|
"multipleResults": "results",
|
||||||
"placeholder": "Find text..."
|
"placeholder": "Find text on canvas..."
|
||||||
},
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"clearReset": "Reset the canvas",
|
"clearReset": "Reset the canvas",
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { act, render, waitFor } from "./test-utils";
|
import { act, render, waitFor } from "./test-utils";
|
||||||
import { Excalidraw } from "../index";
|
import { Excalidraw } from "../index";
|
||||||
import { CLASSES, SEARCH_SIDEBAR } from "../constants";
|
import { CANVAS_SEARCH_TAB, CLASSES, DEFAULT_SIDEBAR } from "../constants";
|
||||||
import { Keyboard } from "./helpers/ui";
|
import { Keyboard } from "./helpers/ui";
|
||||||
import { KEYS } from "../keys";
|
import { KEYS } from "../keys";
|
||||||
import { updateTextEditor } from "./queries/dom";
|
import { updateTextEditor } from "./queries/dom";
|
||||||
@ -34,7 +34,8 @@ describe("search", () => {
|
|||||||
Keyboard.keyPress(KEYS.F);
|
Keyboard.keyPress(KEYS.F);
|
||||||
});
|
});
|
||||||
expect(h.app.state.openSidebar).not.toBeNull();
|
expect(h.app.state.openSidebar).not.toBeNull();
|
||||||
expect(h.app.state.openSidebar?.name).toBe(SEARCH_SIDEBAR.name);
|
expect(h.app.state.openSidebar?.name).toBe(DEFAULT_SIDEBAR.name);
|
||||||
|
expect(h.app.state.openSidebar?.tab).toBe(CANVAS_SEARCH_TAB);
|
||||||
|
|
||||||
const searchInput = await querySearchInput();
|
const searchInput = await querySearchInput();
|
||||||
expect(searchInput.matches(":focus")).toBe(true);
|
expect(searchInput.matches(":focus")).toBe(true);
|
||||||
@ -78,7 +79,8 @@ describe("search", () => {
|
|||||||
Keyboard.keyPress(KEYS.F);
|
Keyboard.keyPress(KEYS.F);
|
||||||
});
|
});
|
||||||
expect(h.app.state.openSidebar).not.toBeNull();
|
expect(h.app.state.openSidebar).not.toBeNull();
|
||||||
expect(h.app.state.openSidebar?.name).toBe(SEARCH_SIDEBAR.name);
|
expect(h.app.state.openSidebar?.name).toBe(DEFAULT_SIDEBAR.name);
|
||||||
|
expect(h.app.state.openSidebar?.tab).toBe(CANVAS_SEARCH_TAB);
|
||||||
|
|
||||||
const searchInput = await querySearchInput();
|
const searchInput = await querySearchInput();
|
||||||
|
|
||||||
@ -122,7 +124,8 @@ describe("search", () => {
|
|||||||
Keyboard.keyPress(KEYS.F);
|
Keyboard.keyPress(KEYS.F);
|
||||||
});
|
});
|
||||||
expect(h.app.state.openSidebar).not.toBeNull();
|
expect(h.app.state.openSidebar).not.toBeNull();
|
||||||
expect(h.app.state.openSidebar?.name).toBe(SEARCH_SIDEBAR.name);
|
expect(h.app.state.openSidebar?.name).toBe(DEFAULT_SIDEBAR.name);
|
||||||
|
expect(h.app.state.openSidebar?.tab).toBe(CANVAS_SEARCH_TAB);
|
||||||
|
|
||||||
const searchInput = await querySearchInput();
|
const searchInput = await querySearchInput();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user