chore: add rollup config for wds (#39397)

With this PR, we want to publish WDS as a npm package. To do that, we
are now adding a rollup setup to compile and build the package.

One thing to note:
Rollup needed `7.24.0` version of browserlist and we had to update
browserlist version in resolutions in root package.json. Now since we
updated browserlist, certain code that was written with old format (
code was coming from blueprint's hotkeys component ) started failing and
we had to refactor. It included the code around hotkeys component. It
was deprecatdd by blueprint and we were still using. We have now
refactored those part of code to use the `useHotKeys` hook.

/ok-to-test tags="@tag.All"

<!-- This is an auto-generated comment: Cypress test results  -->
> [!TIP]
> 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉
> Workflow run:
<https://github.com/appsmithorg/appsmith/actions/runs/13538917574>
> Commit: 9b0b51791e4a95574c9729245ba09994ab371b71
> <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=13538917574&attempt=2"
target="_blank">Cypress dashboard</a>.
> Tags: `@tag.All`
> Spec:
> <hr>Wed, 26 Feb 2025 09:12:45 UTC
<!-- end of auto-generated comment: Cypress test results  -->
This commit is contained in:
Pawan Kumar 2025-02-27 16:05:52 +05:30 committed by GitHub
parent 3df028d5a4
commit d2dc01dc99
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 2143 additions and 1582 deletions

View File

@ -306,7 +306,7 @@
"babel-plugin-module-resolver": "^4.1.0",
"babel-plugin-named-asset-import": "^0.3.8",
"babel-preset-react-app": "^10.0.1",
"browserslist": "^4.18.1",
"browserslist": "^4.24.4",
"case-sensitive-paths-webpack-plugin": "^2.4.0",
"chalk": "^4.1.1",
"compression-webpack-plugin": "^10.0.0",
@ -399,7 +399,7 @@
"xlsx": "https://cdn.sheetjs.com/xlsx-0.19.3/xlsx-0.19.3.tgz"
},
"resolutions": {
"browserslist": "4.20.3",
"browserslist": "4.24.4",
"chokidar": "3.5.3",
"css-select": "4.1.3",
"ejs": "3.1.10",

View File

@ -1,6 +1,6 @@
const esModules = ["remark-gfm"].join("|");
module.exports = {
export default {
preset: "ts-jest",
roots: ["<rootDir>/src"],
setupFiles: ["<rootDir>../../../test/__mocks__/reactMarkdown.tsx"],

View File

@ -4,11 +4,16 @@
"main": "src/index.ts",
"author": "Valera Melnikov <valera@appsmith.com>, Pawan Kumar <pawan@appsmith.com>",
"license": "MIT",
"type": "module",
"files": [
"build"
],
"scripts": {
"lint": "yarn g:lint",
"prettier": "yarn g:prettier",
"test:unit": "yarn g:jest",
"build:icons": "npx tsx ./src/scripts/build-icons.ts"
"build:icons": "npx tsx ./src/scripts/build-icons.ts",
"build:package": "rm -rf build && rollup -c rollup.config.js"
},
"dependencies": {
"@appsmith/wds-headless": "workspace:^",
@ -34,12 +39,32 @@
"usehooks-ts": "*"
},
"devDependencies": {
"@babel/core": "^7.26.9",
"@babel/helper-compilation-targets": "^7.26.5",
"@babel/preset-env": "^7.26.9",
"@babel/preset-react": "^7.26.3",
"@babel/preset-typescript": "^7.26.0",
"@babel/runtime": "^7.26.9",
"@rollup/plugin-babel": "^6.0.4",
"@rollup/plugin-commonjs": "^28.0.2",
"@rollup/plugin-image": "^3.0.3",
"@rollup/plugin-node-resolve": "^16.0.0",
"@rollup/plugin-replace": "^6.0.2",
"@rollup/plugin-terser": "^0.4.4",
"@rollup/plugin-typescript": "^12.1.2",
"@rollup/plugin-url": "^8.0.2",
"@types/fs-extra": "^11.0.4",
"@types/react-syntax-highlighter": "^15.5.13",
"@types/react-transition-group": "^4.4.11",
"eslint-plugin-storybook": "^0.6.10"
"@types/react-transition-group": "^4.4.12",
"browserslist": "^4.24.4",
"eslint-plugin-storybook": "^0.11.3",
"postcss-import": "^16.1.0",
"rollup": "^4.34.8",
"rollup-plugin-copy": "^3.5.0",
"rollup-plugin-postcss": "^4.0.2"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0"
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0",
"react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0"
}
}

View File

@ -0,0 +1,93 @@
import path from "path";
import { dirname } from "path";
import { fileURLToPath } from "url";
import { defineConfig } from "rollup";
import postcssNesting from "postcss-nesting";
import postcssImport from "postcss-import";
import postcssAtRulesVariables from "postcss-at-rules-variables";
import postcssEach from "postcss-each";
import postcssModulesValues from "postcss-modules-values";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
import json from "@rollup/plugin-json";
import resolve from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
import typescript from "@rollup/plugin-typescript";
import postcss from "rollup-plugin-postcss";
import babel from "@rollup/plugin-babel";
import replace from "@rollup/plugin-replace";
const BUILD_DIR = path.resolve(__dirname, "build");
const EXTERNALS = ["react", "react-dom"];
export default defineConfig({
input: path.resolve(__dirname, "src/index.ts"),
output: {
file: path.resolve(BUILD_DIR, "bundle.js"),
format: "esm",
sourcemap: true,
inlineDynamicImports: true,
globals: {
react: "React",
"react-dom": "ReactDOM",
},
},
external: EXTERNALS,
plugins: [
json(),
replace({
preventAssignment: true,
values: {
"process.env.NODE_ENV": JSON.stringify("production"),
"process.env.REACT_APP_ENV": JSON.stringify("production"),
},
}),
commonjs({
include: /node_modules/,
transformMixedEsModules: true,
requireReturnsDefault: "preferred",
esmExternals: true,
}),
babel({
exclude: "node_modules/**",
babelHelpers: "bundled",
extensions: [".js", ".jsx", ".ts", ".tsx"],
presets: [
["@babel/preset-react", { runtime: "automatic" }],
["@babel/preset-typescript", { isTSX: true, allExtensions: true }],
],
skipPreflightCheck: true,
babelrc: false,
configFile: false,
}),
postcss({
modules: true,
minimize: true,
sourceMap: true,
plugins: [
postcssNesting(),
postcssImport(),
postcssAtRulesVariables(),
postcssEach(),
postcssModulesValues(),
],
}),
typescript({
tsconfig: "./tsconfig.json",
declaration: true,
declarationDir: path.resolve(BUILD_DIR),
rootDir: "src",
outDir: path.resolve(BUILD_DIR),
}),
resolve({
extensions: [".js", ".jsx", ".ts", ".tsx"],
browser: true,
preferBuiltins: false,
dedupe: EXTERNALS,
}),
],
});

View File

@ -1,5 +1,5 @@
import React from "react";
import { Hotkey, Hotkeys, HotkeysTarget } from "@blueprintjs/core";
import React, { useMemo } from "react";
import { useHotkeys, type HotkeyConfig } from "@blueprintjs/core";
import type { SearchItem, SelectEvent } from "./utils";
interface Props {
@ -15,70 +15,64 @@ interface Props {
children: React.ReactNode;
}
@HotkeysTarget
class GlobalSearchHotKeys extends React.Component<Props> {
get hotKeysConfig() {
return [
{
combo: "up",
onKeyDown: this.props.handleUpKey,
hideWhenModalClosed: true,
allowInInput: true,
group: "Omnibar",
label: "Move up the list",
},
{
combo: "down",
onKeyDown: this.props.handleDownKey,
hideWhenModalClosed: true,
allowInInput: true,
group: "Omnibar",
label: "Move down the list",
},
{
combo: "return",
onKeyDown: (event: KeyboardEvent) => {
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const activeElement = document.activeElement as any;
activeElement?.blur(); // scroll into view doesn't work with the search input focused
this.props.handleItemLinkClick(event, null, "ENTER_KEY");
const GlobalSearchHotKeys: React.FC<Props> = ({
children,
handleDownKey,
handleItemLinkClick,
handleUpKey,
modalOpen,
}) => {
const hotkeys: HotkeyConfig[] = useMemo(
() =>
[
{
combo: "up",
onKeyDown: handleUpKey,
hideWhenModalClosed: true,
allowInInput: true,
group: "Omnibar",
label: "Move up the list",
global: false,
},
hideWhenModalClosed: true,
allowInInput: true,
group: "Omnibar",
label: "Navigate",
},
].filter(
({ hideWhenModalClosed }) =>
!hideWhenModalClosed || (hideWhenModalClosed && this.props.modalOpen),
);
}
{
combo: "down",
onKeyDown: handleDownKey,
hideWhenModalClosed: true,
allowInInput: true,
group: "Omnibar",
label: "Move down the list",
global: false,
},
{
combo: "return",
onKeyDown: (event: KeyboardEvent) => {
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const activeElement = document.activeElement as any;
renderHotkeys() {
return (
<Hotkeys>
{this.hotKeysConfig.map(
({ allowInInput, combo, group, label, onKeyDown }, index) => (
<Hotkey
allowInInput={allowInInput}
combo={combo}
global={false}
group={group}
key={index}
label={label}
onKeyDown={onKeyDown}
/>
),
)}
</Hotkeys>
);
}
activeElement?.blur(); // scroll into view doesn't work with the search input focused
handleItemLinkClick(event, null, "ENTER_KEY");
},
hideWhenModalClosed: true,
allowInInput: true,
group: "Omnibar",
label: "Navigate",
global: false,
},
].filter(
({ hideWhenModalClosed }) =>
!hideWhenModalClosed || (hideWhenModalClosed && modalOpen),
),
[handleUpKey, handleDownKey, handleItemLinkClick, modalOpen],
);
render() {
return <div>{this.props.children}</div>;
}
}
const { handleKeyDown, handleKeyUp } = useHotkeys(hotkeys);
return (
<div onKeyDown={handleKeyDown} onKeyUp={handleKeyUp}>
{children}
</div>
);
};
export default GlobalSearchHotKeys;

View File

@ -1,5 +1,5 @@
import React from "react";
import { Hotkey, Hotkeys, HotkeysTarget } from "@blueprintjs/core";
import { useHotkeys } from "@blueprintjs/core";
interface Props {
handleUpKey: () => void;
@ -9,78 +9,66 @@ interface Props {
children: React.ReactNode;
}
@HotkeysTarget
class GlobalSearchHotKeys extends React.Component<Props> {
get hotKeysConfig() {
return [
function BranchListHotKeys({
children,
handleDownKey,
handleEscKey,
handleSubmitKey,
handleUpKey,
}: Props) {
const hotkeys = React.useMemo(
() => [
{
combo: "up",
onKeyDown: () => {
this.props.handleUpKey();
},
global: true,
label: "Move up the list",
onKeyDown: handleUpKey,
allowInInput: true,
group: "Branches",
label: "Move up the list",
},
{
combo: "down",
onKeyDown: this.props.handleDownKey,
global: true,
label: "Move down the list",
onKeyDown: handleDownKey,
allowInInput: true,
group: "Branches",
label: "Move down the list",
},
{
combo: "return",
onKeyDown: this.props.handleSubmitKey,
global: true,
label: "Submit",
onKeyDown: handleSubmitKey,
allowInInput: true,
group: "Branches",
label: "Submit",
},
{
combo: "ESC",
onKeyDown: this.props.handleEscKey,
combo: "esc",
global: true,
label: "ESC",
onKeyDown: handleEscKey,
allowInInput: true,
group: "Branches",
label: "ESC",
},
];
}
],
[handleUpKey, handleDownKey, handleSubmitKey, handleEscKey],
);
renderHotkeys() {
return (
<Hotkeys>
{this.hotKeysConfig.map(
({ allowInInput, combo, group, label, onKeyDown }, index) => (
<Hotkey
allowInInput={allowInInput}
combo={combo}
global={false}
group={group}
key={index}
label={label}
onKeyDown={onKeyDown}
/>
),
)}
</Hotkeys>
);
}
useHotkeys(hotkeys);
render() {
return (
<div
style={{
display: "flex",
flex: 1,
flexDirection: "column",
minHeight: 0,
overflow: "auto",
}}
>
{this.props.children}
</div>
);
}
return (
<div
style={{
display: "flex",
flex: 1,
flexDirection: "column",
minHeight: 0,
overflow: "auto",
}}
>
{children}
</div>
);
}
export default GlobalSearchHotKeys;
export default BranchListHotKeys;

View File

@ -1,25 +0,0 @@
import { Hotkey } from "@blueprintjs/core";
import { GitOpsTab } from "git/constants/enums";
import noop from "lodash/noop";
import React, { useCallback } from "react";
interface HotKeysViewProps {
toggleOpsModal: (show: boolean, tab?: GitOpsTab.Deploy) => void;
}
function HotKeysView({ toggleOpsModal = noop }: HotKeysViewProps) {
const handleCommitModal = useCallback(() => {
toggleOpsModal(true, GitOpsTab.Deploy);
}, [toggleOpsModal]);
return (
<Hotkey
combo="ctrl + shift + g"
global
label="Show git commit modal"
onKeyDown={handleCommitModal}
/>
);
}
export default HotKeysView;

View File

@ -1,11 +1,8 @@
import React from "react";
import HotKeysView from "./HotKeysView";
import { useHotKeysView } from "./useHotKeysView";
import useOps from "git/hooks/useOps";
function HotKeys() {
export function useHotKeys() {
const { toggleOpsModal } = useOps();
return <HotKeysView toggleOpsModal={toggleOpsModal} />;
return useHotKeysView({ toggleOpsModal });
}
export default HotKeys;

View File

@ -0,0 +1,27 @@
import { GitOpsTab } from "git/constants/enums";
import noop from "lodash/noop";
import { useCallback, useMemo } from "react";
interface HotKeysViewProps {
toggleOpsModal: (show: boolean, tab?: GitOpsTab.Deploy) => void;
}
export function useHotKeysView({ toggleOpsModal = noop }: HotKeysViewProps) {
const handleCommitModal = useCallback(() => {
toggleOpsModal(true, GitOpsTab.Deploy);
}, [toggleOpsModal]);
const hotKeys = useMemo(
() => [
{
combo: "ctrl + shift + g",
global: true,
label: "Show git commit modal",
onKeyDown: handleCommitModal,
},
],
[handleCommitModal],
);
return hotKeys;
}

View File

@ -2,6 +2,7 @@
export { GitArtifactType, GitOpsTab } from "./constants/enums";
// components
export { useHotKeys } from "./components/HotKeys";
export { default as GitContextProvider } from "./components/GitContextProvider";
export { default as GitModals } from "./ee/components/GitModals";
export { default as GitImportModal } from "./components/ImportModal";
@ -10,7 +11,6 @@ export { default as GitQuickActions } from "./components/QuickActions";
export { default as GitProtectedBranchCallout } from "./components/ProtectedBranchCallout";
export { default as GitGlobalProfile } from "./components/GlobalProfile";
export { default as GitDeployMenuItems } from "./components/DeployMenuItems";
export { default as GitHotKeys } from "./components/HotKeys";
export { default as GitCardBadge } from "./components/CardBadge";
export type { ConnectSuccessPayload as GitConnectSuccessPayload } from "./store/actions/connectActions";

View File

@ -1,20 +1,17 @@
import React, { useCallback } from "react";
import { connect, useDispatch, useSelector } from "react-redux";
import type { AppState } from "ee/reducers";
import { Hotkey, Hotkeys, HotkeysTarget } from "@blueprintjs/core";
import React, { useCallback, useContext, useMemo } from "react";
import { useDispatch, useSelector } from "react-redux";
import {
closePropertyPane,
closeTableFilterPane,
copyWidget,
cutWidget,
deleteSelectedWidget,
deleteSelectedWidget as _deleteSelectedWidget,
groupWidgets,
pasteWidget,
} from "actions/widgetActions";
import { selectWidgetInitAction } from "actions/widgetSelectionActions";
import { setGlobalSearchCategory } from "actions/globalSearchActions";
import { setGlobalSearchCategory as _setGlobalSearchCategory } from "actions/globalSearchActions";
import { getSelectedText, isMacOrIOS } from "utils/helpers";
import { getLastSelectedWidget, getSelectedWidgets } from "selectors/ui";
import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants";
import AnalyticsUtil from "ee/utils/AnalyticsUtil";
import { WIDGETS_SEARCH_ID } from "constants/Explorer";
@ -28,33 +25,105 @@ import {
} from "components/editorComponents/GlobalSearch/utils";
import { redoAction, undoAction } from "actions/pageActions";
import { getAppMode } from "ee/selectors/applicationSelectors";
import type { APP_MODE } from "entities/App";
import {
createMessage,
SAVE_HOTKEY_TOASTER_MESSAGE,
} from "ee/constants/messages";
import { previewModeSelector } from "selectors/editorSelectors";
import { matchBuilderPath } from "constants/routes";
import { toggleInstaller } from "actions/JSLibraryActions";
import { SelectionRequestType } from "sagas/WidgetSelectUtils";
import { toast } from "@appsmith/ads";
import { showDebuggerFlag } from "selectors/debuggerSelectors";
import { getIsFirstTimeUserOnboardingEnabled } from "selectors/onboardingSelectors";
import WalkthroughContext from "components/featureWalkthrough/walkthroughContext";
import { setPreviewModeInitAction } from "actions/editorActions";
import { setPreviewModeInitAction as _setPreviewModeInitAction } from "actions/editorActions";
import { setIsGitSyncModalOpen } from "actions/gitSyncActions";
import { GitSyncModalTab } from "entities/GitSync";
import {
selectGitApplicationProtectedMode,
selectGitModEnabled,
selectGitApplicationProtectedMode,
} from "selectors/gitModSelectors";
import { GitHotKeys as GitHotKeysNew } from "git";
import { useHotkeys } from "@blueprintjs/core";
import { useHotKeys as useGitHotKeys } from "git";
import { getSelectedWidgets, getLastSelectedWidget } from "selectors/ui";
import { previewModeSelector } from "selectors/editorSelectors";
interface Props {
children: React.ReactNode;
getMousePosition: () => { x: number; y: number };
toggleDebugger: () => void;
}
function GlobalHotKeys(props: Props) {
const { children, getMousePosition, toggleDebugger } = props;
function GitHotKeys() {
const isGitModEnabled = useSelector(selectGitModEnabled);
const dispatch = useDispatch();
const isGitModEnabled = useSelector(selectGitModEnabled);
const isWalkthroughOpened = useContext(WalkthroughContext)?.isOpened;
const gitHotKeys = useGitHotKeys();
const selectedWidget = useSelector(getLastSelectedWidget);
const selectedWidgets = useSelector(getSelectedWidgets);
const isPreviewMode = useSelector(previewModeSelector);
const isProtectedMode = useSelector(selectGitApplicationProtectedMode);
const copySelectedWidget = useCallback(
() => dispatch(copyWidget(true)),
[dispatch],
);
const pasteCopiedWidget = useCallback(
(mouseLocation: { x: number; y: number }) =>
dispatch(pasteWidget({ groupWidgets: false, mouseLocation })),
[dispatch],
);
const deleteSelectedWidget = useCallback(
() => dispatch(_deleteSelectedWidget(true)),
[dispatch],
);
const cutSelectedWidget = useCallback(
() => dispatch(cutWidget()),
[dispatch],
);
const groupSelectedWidget = useCallback(
() => dispatch(groupWidgets()),
[dispatch],
);
const setGlobalSearchCategory = useCallback(
(category: SearchCategory) => dispatch(_setGlobalSearchCategory(category)),
[dispatch],
);
const resetSnipingMode = useCallback(
() => dispatch(resetSnipingModeAction()),
[dispatch],
);
const closeProppane = useCallback(
() => dispatch(closePropertyPane()),
[dispatch],
);
const closeTableFilterProppane = useCallback(
() => dispatch(closeTableFilterPane()),
[dispatch],
);
const selectAllWidgetsInit = useCallback(
() => dispatch(selectWidgetInitAction(SelectionRequestType.All)),
[dispatch],
);
const deselectAllWidgets = useCallback(
() => dispatch(selectWidgetInitAction(SelectionRequestType.Empty)),
[dispatch],
);
const executeAction = useCallback(
() => dispatch(runActionViaShortcut()),
[dispatch],
);
const undo = useCallback(() => dispatch(undoAction()), [dispatch]);
const redo = useCallback(() => dispatch(redoAction()), [dispatch]);
const hideInstaller = useCallback(
() => dispatch(toggleInstaller(false)),
[dispatch],
);
const setPreviewModeInitAction = useCallback(
(shouldSet: boolean) => dispatch(_setPreviewModeInitAction(shouldSet)),
[dispatch],
);
const showCommitModal = useCallback(() => {
dispatch(
@ -65,104 +134,58 @@ function GitHotKeys() {
);
}, [dispatch]);
return isGitModEnabled ? (
<GitHotKeysNew />
) : (
<Hotkey
combo="ctrl + shift + g"
global
label="Show git commit modal"
onKeyDown={showCommitModal}
/>
const stopPropagationIfWidgetSelected = useCallback(
(e: KeyboardEvent): boolean => {
const multipleWidgetsSelected = selectedWidgets && selectedWidgets.length;
const singleWidgetSelected =
selectedWidget && selectedWidget != MAIN_CONTAINER_WIDGET_ID;
if (
(singleWidgetSelected || multipleWidgetsSelected) &&
!getSelectedText()
) {
e.preventDefault();
e.stopPropagation();
return true;
}
return false;
},
[selectedWidgets, selectedWidget],
);
}
interface Props {
copySelectedWidget: () => void;
pasteCopiedWidget: (mouseLocation: { x: number; y: number }) => void;
deleteSelectedWidget: () => void;
cutSelectedWidget: () => void;
groupSelectedWidget: () => void;
setGlobalSearchCategory: (category: SearchCategory) => void;
resetSnipingMode: () => void;
closeProppane: () => void;
closeTableFilterProppane: () => void;
executeAction: () => void;
selectAllWidgetsInit: () => void;
deselectAllWidgets: () => void;
selectedWidget?: string;
selectedWidgets: string[];
isDebuggerOpen: boolean;
children: React.ReactNode;
undo: () => void;
redo: () => void;
appMode?: APP_MODE;
isPreviewMode: boolean;
isProtectedMode: boolean;
setPreviewModeInitAction: (shouldSet: boolean) => void;
isSignpostingEnabled: boolean;
getMousePosition: () => { x: number; y: number };
hideInstaller: () => void;
toggleDebugger: () => void;
}
@HotkeysTarget
class GlobalHotKeys extends React.Component<Props> {
public stopPropagationIfWidgetSelected(e: KeyboardEvent): boolean {
const multipleWidgetsSelected =
this.props.selectedWidgets && this.props.selectedWidgets.length;
const singleWidgetSelected =
this.props.selectedWidget &&
this.props.selectedWidget != MAIN_CONTAINER_WIDGET_ID;
if (
(singleWidgetSelected || multipleWidgetsSelected) &&
!getSelectedText()
) {
const onOnmnibarHotKeyDown = useCallback(
(
e: KeyboardEvent,
categoryId: SEARCH_CATEGORY_ID = SEARCH_CATEGORY_ID.NAVIGATION,
) => {
e.preventDefault();
e.stopPropagation();
return true;
}
if (isPreviewMode) return;
return false;
}
const category = filterCategories[categoryId];
public onOnmnibarHotKeyDown(
e: KeyboardEvent,
categoryId: SEARCH_CATEGORY_ID = SEARCH_CATEGORY_ID.NAVIGATION,
) {
e.preventDefault();
setGlobalSearchCategory(category);
hideInstaller();
AnalyticsUtil.logEvent("OPEN_OMNIBAR", {
source: "HOTKEY_COMBO",
category: category.title,
});
},
[isPreviewMode, setGlobalSearchCategory, hideInstaller, AnalyticsUtil],
);
// don't open omnibar if preview mode is on
if (this.props.isPreviewMode) return;
const category = filterCategories[categoryId];
this.props.setGlobalSearchCategory(category);
this.props.hideInstaller();
AnalyticsUtil.logEvent("OPEN_OMNIBAR", {
source: "HOTKEY_COMBO",
category: category.title,
});
}
public renderHotkeys() {
const { isOpened: isWalkthroughOpened } = this.context ?? {};
const { isProtectedMode } = this.props;
// If walkthrough is open disable shortcuts
if (isWalkthroughOpened || isProtectedMode) return <Hotkeys />;
return (
<Hotkeys>
<Hotkey
combo="mod + f"
global
label="Search entities"
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
onKeyDown={(e: any) => {
const hotkeys = useMemo(() => {
if (isWalkthroughOpened || isProtectedMode) {
return [];
} else {
return [
{
combo: "mod + f",
global: true,
label: "Search entities",
onKeyDown: (e: KeyboardEvent) => {
const widgetSearchInput =
document.getElementById(WIDGETS_SEARCH_ID);
@ -171,273 +194,239 @@ class GlobalHotKeys extends React.Component<Props> {
e.preventDefault();
e.stopPropagation();
}
}}
/>
<Hotkey
allowInInput
combo="mod + p"
global
label="Navigate"
onKeyDown={(e) => this.onOnmnibarHotKeyDown(e)}
/>
<Hotkey
allowInInput
combo="mod + plus"
global
label="Create new"
onKeyDown={(e) =>
this.onOnmnibarHotKeyDown(e, SEARCH_CATEGORY_ID.ACTION_OPERATION)
}
/>
<Hotkey
allowInInput
combo="mod + k"
global
label="Show omnibar"
onKeyDown={(e) =>
this.onOnmnibarHotKeyDown(e, SEARCH_CATEGORY_ID.INIT)
}
/>
<Hotkey
allowInInput
combo="mod + d"
global
group="Canvas"
label="Open Debugger"
onKeyDown={this.props.toggleDebugger}
preventDefault
/>
<Hotkey
combo="mod + c"
global
group="Canvas"
label="Copy widget"
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
onKeyDown={(e: any) => {
if (this.stopPropagationIfWidgetSelected(e)) {
this.props.copySelectedWidget();
},
},
{
allowInInput: true,
combo: "mod + p",
global: true,
label: "Navigate",
onKeyDown: (e: KeyboardEvent) => onOnmnibarHotKeyDown(e),
},
{
allowInInput: true,
combo: "mod + plus",
global: true,
label: "Create new",
onKeyDown: (e: KeyboardEvent) =>
onOnmnibarHotKeyDown(e, SEARCH_CATEGORY_ID.ACTION_OPERATION),
},
{
allowInInput: true,
combo: "mod + k",
global: true,
label: "Show omnibar",
onKeyDown: (e: KeyboardEvent) =>
onOnmnibarHotKeyDown(e, SEARCH_CATEGORY_ID.INIT),
},
{
allowInInput: true,
combo: "mod + d",
global: true,
group: "Canvas",
label: "Open Debugger",
onKeyDown: toggleDebugger,
preventDefault: true,
},
{
combo: "mod + c",
global: true,
group: "Canvas",
label: "Copy widget",
onKeyDown: (e: KeyboardEvent) => {
if (stopPropagationIfWidgetSelected(e)) {
copySelectedWidget();
}
}}
/>
<Hotkey
combo="mod + v"
global
group="Canvas"
label="Paste Widget"
onKeyDown={() => {
},
},
{
combo: "mod + v",
global: true,
group: "Canvas",
label: "Paste Widget",
onKeyDown: () => {
if (matchBuilderPath(window.location.pathname)) {
this.props.pasteCopiedWidget(
this.props.getMousePosition() || { x: 0, y: 0 },
);
pasteCopiedWidget(getMousePosition() || { x: 0, y: 0 });
}
}}
/>
<Hotkey
combo="backspace"
global
group="Canvas"
label="Delete widget"
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
onKeyDown={(e: any) => {
if (this.stopPropagationIfWidgetSelected(e) && isMacOrIOS()) {
this.props.deleteSelectedWidget();
},
},
{
combo: "backspace",
global: true,
group: "Canvas",
label: "Delete widget",
onKeyDown: (e: KeyboardEvent) => {
if (stopPropagationIfWidgetSelected(e) && isMacOrIOS()) {
deleteSelectedWidget();
}
}}
/>
<Hotkey
combo="del"
global
group="Canvas"
label="Delete widget"
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
onKeyDown={(e: any) => {
if (this.stopPropagationIfWidgetSelected(e)) {
this.props.deleteSelectedWidget();
},
},
{
combo: "del",
global: true,
group: "Canvas",
label: "Delete widget",
onKeyDown: (e: KeyboardEvent) => {
if (stopPropagationIfWidgetSelected(e)) {
deleteSelectedWidget();
}
}}
/>
<Hotkey
combo="mod + x"
global
group="Canvas"
label="Cut Widget"
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
onKeyDown={(e: any) => {
if (this.stopPropagationIfWidgetSelected(e)) {
this.props.cutSelectedWidget();
},
},
{
combo: "mod + x",
global: true,
group: "Canvas",
label: "Cut Widget",
onKeyDown: (e: KeyboardEvent) => {
if (stopPropagationIfWidgetSelected(e)) {
cutSelectedWidget();
}
}}
/>
<Hotkey
combo="mod + a"
global
group="Canvas"
label="Select all Widget"
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
onKeyDown={(e: any) => {
},
},
{
combo: "mod + a",
global: true,
group: "Canvas",
label: "Select all Widget",
onKeyDown: (e: KeyboardEvent) => {
if (matchBuilderPath(window.location.pathname)) {
this.props.selectAllWidgetsInit();
selectAllWidgetsInit();
e.preventDefault();
}
}}
/>
<Hotkey
combo="esc"
global
group="Canvas"
label="Deselect all Widget"
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
onKeyDown={(e: any) => {
this.props.resetSnipingMode();
},
},
{
combo: "esc",
global: true,
group: "Canvas",
label: "Deselect all Widget",
onKeyDown: (e: KeyboardEvent) => {
resetSnipingMode();
if (matchBuilderPath(window.location.pathname)) {
this.props.deselectAllWidgets();
this.props.closeProppane();
this.props.closeTableFilterProppane();
deselectAllWidgets();
closeProppane();
closeTableFilterProppane();
}
e.preventDefault();
}}
/>
<Hotkey
combo="v"
global
label="Edit Mode"
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
onKeyDown={(e: any) => {
this.props.resetSnipingMode();
},
},
{
combo: "v",
global: true,
label: "Edit Mode",
onKeyDown: (e: KeyboardEvent) => {
resetSnipingMode();
e.preventDefault();
}}
/>
<Hotkey
allowInInput
combo="mod + enter"
global
label="Execute Action"
onKeyDown={this.props.executeAction}
preventDefault
stopPropagation
/>
<Hotkey
combo="mod + z"
global
label="Undo change in canvas"
onKeyDown={this.props.undo}
preventDefault
stopPropagation
/>
<Hotkey
combo="mod + shift + z"
global
label="Redo change in canvas"
onKeyDown={this.props.redo}
preventDefault
stopPropagation
/>
<Hotkey
combo="mod + y"
global
label="Redo change in canvas"
onKeyDown={this.props.redo}
preventDefault
stopPropagation
/>
<Hotkey
combo="mod + g"
global
group="Canvas"
label="Cut Widgets for grouping"
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
onKeyDown={(e: any) => {
if (this.stopPropagationIfWidgetSelected(e)) {
this.props.groupSelectedWidget();
},
},
{
allowInInput: true,
combo: "mod + enter",
global: true,
label: "Execute Action",
onKeyDown: executeAction,
preventDefault: true,
stopPropagation: true,
},
{
combo: "mod + z",
global: true,
label: "Undo change in canvas",
onKeyDown: undo,
preventDefault: true,
stopPropagation: true,
},
{
combo: "mod + shift + z",
global: true,
label: "Redo change in canvas",
onKeyDown: redo,
preventDefault: true,
stopPropagation: true,
},
{
combo: "mod + y",
global: true,
label: "Redo change in canvas",
onKeyDown: redo,
preventDefault: true,
stopPropagation: true,
},
{
combo: "mod + g",
global: true,
group: "Canvas",
label: "Cut Widgets for grouping",
onKeyDown: (e: KeyboardEvent) => {
if (stopPropagationIfWidgetSelected(e)) {
groupSelectedWidget();
}
}}
/>
<Hotkey
combo="mod + s"
global
label="Save progress"
onKeyDown={() => {
},
},
{
combo: "mod + s",
global: true,
label: "Save progress",
onKeyDown: () => {
toast.show(createMessage(SAVE_HOTKEY_TOASTER_MESSAGE), {
kind: "info",
});
}}
preventDefault
stopPropagation
/>
<Hotkey
combo="alt + p"
global
label="Preview Mode"
onKeyDown={() => {
this.props.setPreviewModeInitAction(!this.props.isPreviewMode);
}}
/>
<GitHotKeys />
</Hotkeys>
);
}
},
preventDefault: true,
stopPropagation: true,
},
{
combo: "alt + p",
global: true,
label: "Preview Mode",
onKeyDown: () => {
setPreviewModeInitAction(!isPreviewMode);
},
},
...(isGitModEnabled
? gitHotKeys
: [
{
combo: "ctrl + shift + g",
global: true,
label: "Show git commit modal",
onKeyDown: showCommitModal,
},
]),
];
}
}, [
isWalkthroughOpened,
isProtectedMode,
isGitModEnabled,
gitHotKeys,
showCommitModal,
isPreviewMode,
getMousePosition,
toggleDebugger,
copySelectedWidget,
pasteCopiedWidget,
deleteSelectedWidget,
cutSelectedWidget,
selectAllWidgetsInit,
deselectAllWidgets,
closeProppane,
closeTableFilterProppane,
resetSnipingMode,
executeAction,
undo,
redo,
groupSelectedWidget,
setPreviewModeInitAction,
onOnmnibarHotKeyDown,
stopPropagationIfWidgetSelected,
]);
render() {
return <div>{this.props.children}</div>;
}
useHotkeys(hotkeys, { showDialogKeyCombo: "?" });
return <div>{children}</div>;
}
const mapStateToProps = (state: AppState) => {
return {
selectedWidget: getLastSelectedWidget(state),
selectedWidgets: getSelectedWidgets(state),
isDebuggerOpen: showDebuggerFlag(state),
appMode: getAppMode(state),
isPreviewMode: previewModeSelector(state),
isProtectedMode: selectGitApplicationProtectedMode(state),
isSignpostingEnabled: getIsFirstTimeUserOnboardingEnabled(state),
};
};
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const mapDispatchToProps = (dispatch: any) => {
return {
copySelectedWidget: () => dispatch(copyWidget(true)),
pasteCopiedWidget: (mouseLocation: { x: number; y: number }) =>
dispatch(
pasteWidget({
groupWidgets: false,
mouseLocation,
}),
),
deleteSelectedWidget: () => dispatch(deleteSelectedWidget(true)),
cutSelectedWidget: () => dispatch(cutWidget()),
groupSelectedWidget: () => dispatch(groupWidgets()),
setGlobalSearchCategory: (category: SearchCategory) =>
dispatch(setGlobalSearchCategory(category)),
resetSnipingMode: () => dispatch(resetSnipingModeAction()),
closeProppane: () => dispatch(closePropertyPane()),
closeTableFilterProppane: () => dispatch(closeTableFilterPane()),
selectAllWidgetsInit: () =>
dispatch(selectWidgetInitAction(SelectionRequestType.All)),
deselectAllWidgets: () =>
dispatch(selectWidgetInitAction(SelectionRequestType.Empty)),
executeAction: () => dispatch(runActionViaShortcut()),
undo: () => dispatch(undoAction()),
redo: () => dispatch(redoAction()),
hideInstaller: () => dispatch(toggleInstaller(false)),
setPreviewModeInitAction: (shouldSet: boolean) =>
dispatch(setPreviewModeInitAction(shouldSet)),
};
};
GlobalHotKeys.contextType = WalkthroughContext;
export default connect(mapStateToProps, mapDispatchToProps)(GlobalHotKeys);
export default GlobalHotKeys;

View File

@ -2,6 +2,7 @@ import GlobalHotKeys from "./GlobalHotKeys";
import React from "react";
import { useMouseLocation } from "./useMouseLocation";
import useDebuggerTriggerClick from "components/editorComponents/Debugger/hooks/useDebuggerTriggerClick";
import { HotkeysProvider } from "@blueprintjs/core";
//HOC to track user's mouse location, separated out so that it doesn't render the component on every mouse move
// TODO: Fix this the next time the file is edited
@ -11,13 +12,15 @@ function HotKeysHOC(props: any) {
const toggleDebugger = useDebuggerTriggerClick();
return (
<GlobalHotKeys
{...props}
getMousePosition={getMousePosition}
toggleDebugger={toggleDebugger}
>
{props.children}
</GlobalHotKeys>
<HotkeysProvider>
<GlobalHotKeys
{...props}
getMousePosition={getMousePosition}
toggleDebugger={toggleDebugger}
>
{props.children}
</GlobalHotKeys>
</HotkeysProvider>
);
}

View File

@ -1,5 +1,5 @@
import React from "react";
import { Hotkey, Hotkeys, HotkeysTarget } from "@blueprintjs/core";
import React, { useMemo } from "react";
import { useHotkeys } from "@blueprintjs/core";
import { JS_OBJECT_HOTKEYS_CLASSNAME } from "./constants";
interface Props {
@ -7,31 +7,23 @@ interface Props {
children: React.ReactNode;
}
@HotkeysTarget
class JSObjectHotKeys extends React.Component<Props> {
public renderHotkeys() {
return (
<Hotkeys>
<Hotkey
allowInInput
combo="mod + enter"
global
label="Run Js Function"
onKeyDown={this.props.runActiveJSFunction}
/>
</Hotkeys>
);
}
function JSObjectHotKeys({ children, runActiveJSFunction }: Props) {
const hotkeys = useMemo(
() => [
{
combo: "mod + enter",
global: true,
label: "Run Js Function",
onKeyDown: runActiveJSFunction,
allowInInput: true,
},
],
[runActiveJSFunction],
);
render() {
/*
Blueprint's v3 decorated component must return a single DOM element in its render() method, not a custom React component.
This constraint allows HotkeysTarget to inject event handlers without creating an extra wrapper element.
*/
return (
<div className={JS_OBJECT_HOTKEYS_CLASSNAME}>{this.props.children}</div>
);
}
useHotkeys(hotkeys);
return <div className={JS_OBJECT_HOTKEYS_CLASSNAME}>{children}</div>;
}
export default JSObjectHotKeys;

View File

@ -1,5 +1,5 @@
import React from "react";
import { Hotkey, Hotkeys, HotkeysTarget } from "@blueprintjs/core";
import React, { useMemo } from "react";
import { useHotkeys } from "@blueprintjs/core";
interface Props {
handleUpKey: () => void;
@ -9,78 +9,66 @@ interface Props {
children: React.ReactNode;
}
@HotkeysTarget
class GlobalSearchHotKeys extends React.Component<Props> {
get hotKeysConfig() {
return [
function BranchListHotkeys({
children,
handleDownKey,
handleEscKey,
handleSubmitKey,
handleUpKey,
}: Props) {
const hotkeys = useMemo(
() => [
{
combo: "up",
onKeyDown: () => {
this.props.handleUpKey();
},
global: false,
label: "Move up the list",
onKeyDown: handleUpKey,
allowInInput: true,
group: "Branches",
label: "Move up the list",
},
{
combo: "down",
onKeyDown: this.props.handleDownKey,
global: false,
label: "Move down the list",
onKeyDown: handleDownKey,
allowInInput: true,
group: "Branches",
label: "Move down the list",
},
{
combo: "return",
onKeyDown: this.props.handleSubmitKey,
global: false,
label: "Submit",
onKeyDown: handleSubmitKey,
allowInInput: true,
group: "Branches",
label: "Submit",
},
{
combo: "ESC",
onKeyDown: this.props.handleEscKey,
global: false,
label: "ESC",
onKeyDown: handleEscKey,
allowInInput: true,
group: "Branches",
label: "ESC",
},
];
}
],
[handleUpKey, handleDownKey, handleSubmitKey, handleEscKey],
);
renderHotkeys() {
return (
<Hotkeys>
{this.hotKeysConfig.map(
({ allowInInput, combo, group, label, onKeyDown }, index) => (
<Hotkey
allowInInput={allowInInput}
combo={combo}
global={false}
group={group}
key={index}
label={label}
onKeyDown={onKeyDown}
/>
),
)}
</Hotkeys>
);
}
useHotkeys(hotkeys);
render() {
return (
<div
style={{
display: "flex",
flex: 1,
flexDirection: "column",
minHeight: 0,
overflow: "auto",
}}
>
{this.props.children}
</div>
);
}
return (
<div
style={{
display: "flex",
flex: 1,
flexDirection: "column",
minHeight: 0,
overflow: "auto",
}}
>
{children}
</div>
);
}
export default GlobalSearchHotKeys;
export default BranchListHotkeys;

File diff suppressed because it is too large Load Diff