Skip to content

Commit

Permalink
Toolbar additional parameters (#7168)
Browse files Browse the repository at this point in the history
* ToolbarPanelConfig additionalParams

(cherry picked from commit 893c34c)

* allow additionalParams in RemoteComponent

* add RemoteModuleDialog

* deps

* fixed missing params

* fixed missing params

* label translation

* allow RemoteModuleDialog buttons customization

* revert unnecessary changes

* test update

* test update

* fixed theme & styles

* review

* formatting

* Update NussknackerRemoteDebug.run.xml

* Update NussknackerRemoteDebug.run.xml

* Update NussknackerRemoteDebug.run.xml

* formatting
  • Loading branch information
JulianWielga authored Nov 22, 2024
1 parent ec8c461 commit fcebd61
Show file tree
Hide file tree
Showing 22 changed files with 293 additions and 110 deletions.
15 changes: 7 additions & 8 deletions designer/client/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion designer/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"@mui/icons-material": "5.15.7",
"@mui/lab": "5.0.0-alpha.165",
"@mui/material": "5.15.7",
"@touk/federated-component": "1.0.0",
"@touk/federated-component": "1.1.0",
"@touk/window-manager": "1.9.0",
"ace-builds": "1.34.2",
"axios": "1.7.5",
Expand Down
38 changes: 34 additions & 4 deletions designer/client/src/components/RemoteComponent.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import React from "react";
import React, { useMemo } from "react";
import LoaderSpinner from "./spinner/Spinner";
import { FederatedComponent, FederatedComponentProps, getFederatedComponentLoader } from "@touk/federated-component";
import { NuThemeProvider } from "../containers/theme/nuThemeProvider";
import SystemUtils from "../common/SystemUtils";
import { useWindows, WindowKind } from "../windowManager";

export const loadExternalReactModule = getFederatedComponentLoader({ Wrapper: NuThemeProvider });
export const loadExternalReactModuleWithAuth = getFederatedComponentLoader({
Expand All @@ -13,6 +14,35 @@ export const loadExternalReactModuleWithAuth = getFederatedComponentLoader({
window["loadExternalReactModule"] = loadExternalReactModule;
window["loadExternalReactModuleWithAuth"] = loadExternalReactModuleWithAuth;

export const RemoteComponent = <P extends NonNullable<unknown>>(props: FederatedComponentProps<P>) => (
<FederatedComponent<P> {...props} fallback={<LoaderSpinner show={true} />} buildHash={__BUILD_HASH__} />
);
export type RemoteToolbarContentProps = {
openRemoteModuleWindow: <P extends NonNullable<unknown>>(props: P & { url?: string; title?: string }) => void;
};

function RemoteComponentRender<P extends NonNullable<unknown>, T = unknown>(props: FederatedComponentProps<P>, ref: React.ForwardedRef<T>) {
const { open } = useWindows();
const sharedContext = useMemo<RemoteToolbarContentProps>(
() => ({
openRemoteModuleWindow: ({ title, ...props }) =>
open({
kind: WindowKind.remote,
title,
meta: props,
}),
}),
[open],
);

return (
<FederatedComponent<P, T>
ref={ref}
{...sharedContext}
{...props}
fallback={<LoaderSpinner show={true} />}
buildHash={__BUILD_HASH__}
/>
);
}

export const RemoteComponent = React.forwardRef(RemoteComponentRender) as <P extends NonNullable<unknown>, T = unknown>(
props: FederatedComponentProps<P> & React.RefAttributes<T>,
) => React.ReactElement;
61 changes: 61 additions & 0 deletions designer/client/src/components/RemoteModuleDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { ModuleUrl } from "@touk/federated-component";
import { WindowContentProps } from "@touk/window-manager";
import type { FooterButtonProps } from "@touk/window-manager/cjs/components/window/footer";
import React, { useCallback, useMemo, useRef } from "react";
import { useTranslation } from "react-i18next";
import { WindowContent, WindowKind } from "../windowManager";
import { LoadingButtonTypes } from "../windowManager/LoadingButton";
import { RemoteComponent } from "./RemoteComponent";

export type RemoteModuleDialogProps = NonNullable<unknown>;
export type RemoteModuleDialogRef = NonNullable<{
closeAction?: () => Promise<void>;
adjustButtons: (buttons: { closeButton: FooterButtonProps; confirmButton: FooterButtonProps }) => FooterButtonProps[];
}>;

export function RemoteModuleDialog<P extends NonNullable<unknown>>({
close,
...props
}: WindowContentProps<WindowKind, { url: ModuleUrl } & P>): JSX.Element {
const {
data: { meta: passProps },
} = props;

const ref = useRef<RemoteModuleDialogRef>();

const closeAction = useCallback(async () => {
await Promise.all([ref.current?.closeAction?.()]);
close();
}, [close]);

const { t } = useTranslation();

const closeButton = useMemo<FooterButtonProps>(
() => ({
title: t("dialog.button.cancel", "cancel"),
action: closeAction,
className: LoadingButtonTypes.secondaryButton,
}),
[closeAction, t],
);

const confirmButton = useMemo<FooterButtonProps>(
() => ({
title: t("dialog.button.ok", "OK"),
action: closeAction,
}),
[closeAction, t],
);

return (
<WindowContent
{...props}
close={close}
buttons={ref.current?.adjustButtons({ closeButton, confirmButton }) || [closeButton, confirmButton]}
>
<RemoteComponent<RemoteModuleDialogProps, RemoteModuleDialogRef> ref={ref} {...passProps} />
</WindowContent>
);
}

export default RemoteModuleDialog;
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ export const SettingLabelStyled = styled(FormLabel)(({ theme }) => ({
color: theme.palette.text.secondary,
fontSize: "12px",
fontWeight: "400",
flexBasis: "30%",
".MuiFormControl-root &": {
flexBasis: "30%",
},
}));

export const ListItemContainer = styled("div")`
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import React, { PropsWithChildren, ReactElement, useMemo } from "react";
import React, { PropsWithChildren, ReactElement } from "react";
import { useTranslation } from "react-i18next";
import { splitUrl } from "@touk/federated-component";
import { ToolbarButtons } from "./toolbarButtons";
import { RemoteComponent } from "../RemoteComponent";
import { ToolbarConfig } from "../toolbarSettings/types";
import { ToolbarButtons } from "./toolbarButtons";
import { ToolbarWrapper } from "./toolbarWrapper/ToolbarWrapper";
import { RemoteComponent } from "../RemoteComponent";

export type ToolbarPanelProps = PropsWithChildren<Omit<ToolbarConfig, "buttons">>;

Expand All @@ -22,8 +21,6 @@ export function DefaultToolbarPanel(props: ToolbarPanelProps): ReactElement {
}

function RemoteToolbarContent(props: ToolbarPanelProps): ReactElement {
const { componentUrl, ...passProps } = props;
const [url, scope] = useMemo(() => splitUrl(componentUrl), [componentUrl]);

return <RemoteComponent url={url} scope={scope} {...passProps} />;
const { componentUrl, additionalParams, ...passProps } = props;
return <RemoteComponent url={componentUrl} {...additionalParams} {...passProps} />;
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export const TOOLBAR_WRAPPER_CLASSNAME = "toolbar-wrapper";

export function ToolbarWrapper(props: ToolbarWrapperProps): React.JSX.Element | null {
const theme = useTheme();
const { title, children, id, onClose, onExpand, onCollapse, color = theme.palette.background.paper, disableCollapse } = props;
const { title, children, id, onClose, onExpand, onCollapse, color, disableCollapse } = props;
const handlerProps = useDragHandler();

const dispatch = useDispatch();
Expand Down Expand Up @@ -59,15 +59,15 @@ export function ToolbarWrapper(props: ToolbarWrapperProps): React.JSX.Element |
borderRadius: theme.spacing(0.5),
}}
expanded={!isCollapsedLocal}
color={color}
color={color || theme.palette.background.paper}
width={SIDEBAR_WIDTH}
data-testid={id}
{...(isCollapsible ? {} : handlerProps)}
>
{(isCollapsible || onClose) && (
<PanelHeader
{...(isCollapsible ? handlerProps : {})}
color={color}
color={color || theme.palette.background.paper}
onClick={toggleCollapsed}
onKeyDown={(e) => {
if (e.key === "Enter") {
Expand All @@ -82,6 +82,7 @@ export function ToolbarWrapper(props: ToolbarWrapperProps): React.JSX.Element |
textTransform={"uppercase"}
variant={"overline"}
sx={{
color: color ? "inherit" : undefined,
"::after": {
// force line height for empty
content: "' '",
Expand Down
1 change: 1 addition & 0 deletions designer/client/src/components/toolbarSettings/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export interface ToolbarConfig {
color?: string;
buttonsVariant?: ButtonsVariant;
disableCollapse?: boolean;
additionalParams?: Record<string, string>;
}

export type ToolbarsConfig = Partial<Record<ToolbarsSide, ToolbarConfig[]>>;
50 changes: 46 additions & 4 deletions designer/client/src/components/toolbars/creator/CreatorPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,35 @@
import { ModuleUrl } from "@touk/federated-component";
import { isEmpty } from "lodash";
import React, { useCallback, useState } from "react";
import { ErrorBoundary } from "react-error-boundary";
import { useTranslation } from "react-i18next";
import { EventTrackingSelector, getEventTrackingProps } from "../../../containers/event-tracking";
import { RemoteComponent } from "../../RemoteComponent";
import { SearchIcon } from "../../table/SearchFilter";
import { SearchInputWithIcon } from "../../themed/SearchInput";
import { ToolbarPanelProps } from "../../toolbarComponents/DefaultToolbarPanel";
import { ToolbarWrapper } from "../../toolbarComponents/toolbarWrapper/ToolbarWrapper";
import ToolBox from "./ToolBox";
import { SearchInputWithIcon } from "../../themed/SearchInput";
import { EventTrackingSelector, getEventTrackingProps } from "../../../containers/event-tracking";

export function CreatorPanel(props: ToolbarPanelProps): JSX.Element {
type CreatorPanelProps = ToolbarPanelProps & {
additionalParams?: {
addGroupElement?: ModuleUrl;
};
};

const AddGroupElement = <P extends NonNullable<{ url: ModuleUrl; componentGroup: string }>>(props: P) => {
const { t } = useTranslation();
return props.url ? (
<ErrorBoundary fallback={null}>
<RemoteComponent
{...props}
label={t("panels.creator.addMore", "Add more {{componentGroup}}...", { componentGroup: props.componentGroup })}
/>
</ErrorBoundary>
) : null;
};

export function CreatorPanel({ additionalParams, ...props }: CreatorPanelProps): JSX.Element {
const { t } = useTranslation();
const [filter, setFilter] = useState("");
const clearFilter = useCallback(() => setFilter(""), []);
Expand All @@ -24,7 +45,28 @@ export function CreatorPanel(props: ToolbarPanelProps): JSX.Element {
>
<SearchIcon isEmpty={isEmpty(filter)} />
</SearchInputWithIcon>
<ToolBox filter={filter} />
<ToolBox
filter={filter}
addGroupLabelElement={({ name }) => (
<AddGroupElement
url={additionalParams?.addGroupElement}
variant="small"
componentGroup={name}
{...additionalParams}
{...props}
/>
)}
addTreeElement={({ name }) => (
<AddGroupElement
url={additionalParams?.addGroupElement}
variant="big"
className="tool"
componentGroup={name}
{...additionalParams}
{...props}
/>
)}
/>
</ToolbarWrapper>
);
}
24 changes: 16 additions & 8 deletions designer/client/src/components/toolbars/creator/ToolBox.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import { lighten, styled } from "@mui/material";
import { getLuminance } from "@mui/system/colorManipulator";
import React, { useMemo } from "react";
import { useTranslation } from "react-i18next";
import { useSelector } from "react-redux";
import "react-treeview/react-treeview.css";
import { filterComponentsByLabel } from "../../../common/ProcessDefinitionUtils";
import { blendDarken, blendLighten } from "../../../containers/theme/helpers";
import { getProcessDefinitionData } from "../../../reducers/selectors/settings";
import { ComponentGroup } from "../../../types";
import { ToolboxComponentGroup } from "./ToolboxComponentGroup";
import Tool from "./Tool";
import { useTranslation } from "react-i18next";
import { lighten, styled } from "@mui/material";

import { blendDarken, blendLighten } from "../../../containers/theme/helpers";
import { getLuminance } from "@mui/system/colorManipulator";
import { ToolboxComponentGroup } from "./ToolboxComponentGroup";

const StyledToolbox = styled("div")(({ theme }) => ({
fontSize: "14px",
Expand Down Expand Up @@ -78,6 +77,7 @@ const StyledToolbox = styled("div")(({ theme }) => ({
padding: theme.spacing(0.75, 0.5, 0.75, 4),
border: "none",
borderRight: 0,
userSelect: "none",
"&.disabled": {
opacity: 0.4,
cursor: "not-allowed !important",
Expand All @@ -88,7 +88,7 @@ const StyledToolbox = styled("div")(({ theme }) => ({
cursor: "grabbing",
},

"&:hover": {
"&:hover, &:focus-within": {
backgroundColor: theme.palette.action.hover,
color: lighten(theme.palette.text.primary, 0.2),
},
Expand All @@ -104,7 +104,13 @@ const StyledToolbox = styled("div")(({ theme }) => ({
},
}));

export default function ToolBox(props: { filter: string }): JSX.Element {
type ToolBoxProps = {
filter: string;
addTreeElement?: (group: ComponentGroup) => React.ReactElement | null;
addGroupLabelElement?: (group: ComponentGroup) => React.ReactElement | null;
};

export default function ToolBox(props: ToolBoxProps): JSX.Element {
const processDefinitionData = useSelector(getProcessDefinitionData);
const { t } = useTranslation();

Expand All @@ -126,6 +132,8 @@ export default function ToolBox(props: { filter: string }): JSX.Element {
componentGroup={componentGroup}
highlights={filters}
flatten={groups.length === 1}
addTreeElement={props.addTreeElement?.(componentGroup)}
addGroupLabelElement={props.addGroupLabelElement?.(componentGroup)}
/>
))
) : (
Expand Down
Loading

0 comments on commit fcebd61

Please sign in to comment.