Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions src/ts/components/core/SegmentedControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import {
MantineSize,
SegmentedControlItem,
} from "@mantine/core";
import { renderDashComponent } from "dash-extensions-js";

import { BoxProps } from "props/box";
import { DashBaseProps, PersistenceProps } from "props/dash";
import { StylesApiProps } from "props/styles";
import React from "react";
import { setPersistence, getLoadingState } from "../../utils/dash3";
import { setPersistence, getLoadingState, newRenderDashComponent, getContextPath } from "../../utils/dash3";

interface Props
extends BoxProps,
Expand Down Expand Up @@ -59,14 +59,15 @@ const SegmentedControl = (props: Props) => {
...others
} = props;

const componentPath = getContextPath()
const renderedData = [];
data.forEach((item, index) => {
if (typeof item === "string") {
renderedData.push(item);
} else {
const rItem = {
value: item["value"],
label: renderDashComponent(item["label"]),
label: newRenderDashComponent(item["label"], index, componentPath ? [...componentPath, index, 'label'] : []),
disabled: item["disabled"],
};
renderedData.push(rItem);
Expand Down
7 changes: 3 additions & 4 deletions src/ts/components/core/stepper/Stepper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,12 @@ import {
Stepper as MantineStepper,
} from "@mantine/core";
import { useDidUpdate } from "@mantine/hooks";
import { renderDashComponents } from "dash-extensions-js";
import { BoxProps } from "props/box";
import { DashBaseProps } from "props/dash";
import { StylesApiProps } from "props/styles";
import { omit } from "ramda";
import React, { useState } from "react";
import { getChildLayout, getLoadingState } from "../../../utils/dash3";
import { getChildLayout, getLoadingState, newRenderDashComponents } from "../../../utils/dash3";

interface Props extends BoxProps, DashBaseProps, StylesApiProps {
/** Index of the active step */
Expand Down Expand Up @@ -73,15 +72,15 @@ const Stepper = ({ setProps, loading_state, active, children, ...others }: Props
</MantineStepper.Completed>
);
} else {
const renderedProps = renderDashComponents(
const renderedProps = newRenderDashComponents(
omit(["children"], childProps),
[
"label",
"description",
"icon",
"progressIcon",
"completedIcon",
]
], childProps.componentPath
);

return (
Expand Down
9 changes: 5 additions & 4 deletions src/ts/components/core/timeline/Timeline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import {
MantineRadius,
Timeline as MantineTimeline,
} from "@mantine/core";
import { renderDashComponents } from "dash-extensions-js";

import { BoxProps } from "props/box";
import { DashBaseProps } from "props/dash";
import { StylesApiProps } from "props/styles";
import { omit } from "ramda";
import React from "react";
import { getLoadingState, getChildProps } from "../../../utils/dash3";
import { getLoadingState, getChildProps, newRenderDashComponents } from "../../../utils/dash3";

interface Props extends BoxProps, StylesApiProps, DashBaseProps {
/** `Timeline.Item` components */
Expand Down Expand Up @@ -43,9 +43,10 @@ const Timeline = (props: Props) => {
>
{React.Children.map(children, (child: any, index) => {
const childProps = getChildProps(child)
const renderedProps = renderDashComponents(
const renderedProps = newRenderDashComponents(
omit(["children"], childProps),
["title", "bullet"]
["title", "bullet"],
childProps.componentPath
);
return (
<MantineTimeline.Item {...renderedProps} key={index}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,23 @@ import {
CodeHighlightTabs as MantineCodeHighlightTabs,
} from "@mantine/code-highlight";
import "@mantine/code-highlight/styles.css";
import { renderDashComponents } from "dash-extensions-js";

import React from "react";
import { getLoadingState } from "../../../../utils/dash3";
import { getLoadingState, newRenderDashComponents, getContextPath } from "../../../../utils/dash3";
import { Props } from "../CodeHighlightTabs"


/** CodeHighlightTabs */
const CodeHighlightTabs = (props: Props) => {
const { setProps, loading_state, code, ...others } = props;
const componentPath = getContextPath()
const renderedCode = [];
if (Array.isArray(code)) {
code.forEach((item, index) => {
renderedCode.push(renderDashComponents(item, ["icon"]));
renderedCode.push(newRenderDashComponents(item, ["icon"], componentPath ? [...componentPath, index] : []));
});
} else {
renderedCode.push(renderDashComponents(code, ["icon"]));
renderedCode.push(newRenderDashComponents(code, ["icon"], componentPath ? [...componentPath, 0] : []));
}

return (
Expand Down
2 changes: 2 additions & 0 deletions src/ts/props/dash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ export interface DashBaseProps {
/** Holds the name of the component that is loading */
component_name: string;
};
/** Holds the path of the component */
componentPath?: any[];
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to do this without adding a new prop to every component?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it was just a placeholder, its sometimes in there but sometimes not... It's not necessary as far as I can tell.

Comment thread
BSd3v marked this conversation as resolved.
Outdated
}

export interface PersistenceProps {
Expand Down
63 changes: 62 additions & 1 deletion src/ts/utils/dash3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,66 @@
* For more details, refer to the Dash documentation:
* Dash 3 for Component Developers - https://dash.plotly.com/dash-3-for-component-developers
*/
import React from "react";
import React, { useState, createElement } from "react";
import { DashBaseProps } from "props/dash";
import {dissoc, has, includes, isEmpty, isNil, mergeRight, type} from "ramda";

const SIMPLE_COMPONENT_TYPES = ['String', 'Number', 'Null', 'Boolean'];
const isSimpleComponent = component => includes(type(component), SIMPLE_COMPONENT_TYPES);

/** check for dash version */
export const isDash3 = (): boolean => {
return !!(window as any).dash_component_api;
};

export const newRenderDashComponent = (component: any, index?: number | null, basePath?: any[]) => {
if (!isDash3() || isEmpty(basePath)) {
const dash_extensions = require('dash-extensions-js');
const {renderDashComponent} = dash_extensions;
return renderDashComponent(component, index)
}

// Nothing to render.
if (isNil(component) || isEmpty(component)) {
return null;
}

// Simple stuff such as strings.
if (isSimpleComponent(component)) {
return component;
}

// Array of stuff.
if (Array.isArray(component)) {
return component.map((item, i) => newRenderDashComponent(item, i, [...(basePath || []), i]));
}

// Merge props.
const allProps = {
component,
componentPath: [...(basePath || [])],
key: index !== null ? index : Math.random().toString(36).substr(2, 9)
};

// Render the component.
return createElement((window as any).dash_component_api.ExternalWrapper, allProps);
};

export const newRenderDashComponents = (props: any, propsToRender: string[], basePath: any[]=[]) => {
if (!isDash3() || isEmpty(basePath)) {
const dash_extensions = require('dash-extensions-js');
const {renderDashComponents} = dash_extensions;
return renderDashComponents(props, propsToRender)
}
for (let i = 0; i < propsToRender.length; i++) {
const key = propsToRender[i];
if (props.hasOwnProperty(key)) {
props[key] = newRenderDashComponent(props[key], null, [...basePath, key]);
}
}
return props;
};

/** Apply persistence settings based on React version */
export const setPersistence = (Component: any, props: string[] = ["value"]): void => {
const persistence = { persisted_props: props, persistence_type: "local" };
Expand Down Expand Up @@ -76,3 +128,12 @@ export const applyDashProps = (component: any, props: Record<string, any>) => {

return component;
};

export const getContextPath = () => {
let componentPath = [];
if (isDash3()) {
const ctx = (window as any).dash_component_api.useDashContext()
componentPath = ctx.componentPath
}
return componentPath
}