Skip to content
This repository was archived by the owner on Jul 9, 2025. It is now read-only.
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const DemoMaps = {

class Demo extends Component {
state = {
selectedItem: DemoMaps.VisualEditorDemo.key,
selectedItem: DemoMaps.VisualSDKDemo.key,
};

renderNav() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { Component } from 'react';
import { seedNewDialog, SDKTypes } from '@bfc/shared';

import { renderSDKType } from '../../../src/schema/uischemaRenderer';
import { UISchemaRenderer } from '../../../src/schema/uischemaRenderer';
import { EdgeMenu } from '../../../src/components/menus/EdgeMenu';
import { JsonBlock } from '../components/json-block';

Expand All @@ -14,13 +14,17 @@ export class VisualSDKDemo extends Component {

seedInitialActions() {
const initialTypes = [
SDKTypes.SendActivity,
SDKTypes.EditArray,
SDKTypes.InitProperty,
SDKTypes.SetProperties,
SDKTypes.SetProperty,
SDKTypes.DeleteProperties,
SDKTypes.DeleteProperty,
SDKTypes.BeginDialog,
SDKTypes.EndDialog,
SDKTypes.RepeatDialog,
SDKTypes.ReplaceDialog,
SDKTypes.CancelAllDialogs,
SDKTypes.EmitEvent,
];
Expand Down Expand Up @@ -58,7 +62,9 @@ export class VisualSDKDemo extends Component {
}}
/>
</div>
<div className="action-preview--visual">{renderSDKType(action)}</div>
<div className="action-preview--visual">
<UISchemaRenderer id={`actions[${index}]`} data={action} onEvent={() => null} />
</div>
</div>
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

export * from './steps/ActivityRenderer';
export * from './steps/BeginDialog';
export * from './steps/DefaultRenderer';
export * from './steps/ReplaceDialog';
export * from './steps/ChoiceInput';
export * from './steps/TextInput';
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -10,34 +10,26 @@ import { ObiTypes } from '../../constants/ObiTypes';
import { AttrNames } from '../../constants/ElementAttributes';
import { NodeRendererContext } from '../../store/NodeRendererContext';
import { SelectionContext } from '../../store/SelectionContext';
import {
DefaultRenderer,
BeginDialog,
ReplaceDialog,
ActivityRenderer,
ChoiceInput,
BotAsks,
UserInput,
InvalidPromptBrick,
} from '../nodes/index';
import { BeginDialog, ReplaceDialog, ChoiceInput, BotAsks, UserInput, InvalidPromptBrick } from '../nodes/index';
import { ConditionNode } from '../nodes/steps/ConditionNode';
import { ForeachDetail } from '../nodes/steps/ForeachDetail';
import { ForeachPageDetail } from '../nodes/steps/ForeachPageDetail';
import { NodeProps, defaultNodeProps } from '../nodes/nodeProps';
import { UISchemaRenderer } from '../../schema/uischemaRenderer';

const rendererByObiType = {
[ObiTypes.BeginDialog]: BeginDialog,
[ObiTypes.ConditionNode]: ConditionNode,
[ObiTypes.ForeachDetail]: ForeachDetail,
[ObiTypes.ForeachPageDetail]: ForeachPageDetail,
[ObiTypes.ReplaceDialog]: ReplaceDialog,
[ObiTypes.SendActivity]: ActivityRenderer,
[ObiTypes.SendActivity]: UISchemaRenderer,
[ObiTypes.ChoiceInputDetail]: ChoiceInput,
[ObiTypes.BotAsks]: BotAsks,
[ObiTypes.UserAnswers]: UserInput,
[ObiTypes.InvalidPromptBrick]: InvalidPromptBrick,
};
const DEFAULT_RENDERER = DefaultRenderer;
const DEFAULT_RENDERER = UISchemaRenderer;

function chooseRendererByType($type): FC<NodeProps> | ComponentClass<NodeProps> {
const renderer = rendererByObiType[$type] || DEFAULT_RENDERER;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,38 @@
// Licensed under the MIT License.

import { SDKTypes } from '@bfc/shared';
import React from 'react';

import { ActionCard } from '../widgets/ActionCard';
import { ActivityRenderer } from '../widgets/ActivityRenderer';
import { DialogRefCard } from '../widgets/DialogRefCard';
import { ElementIcon } from '../utils/obiPropertyResolver';
import { ObiColors } from '../constants/ElementColors';

import { UISchema } from './uischema.types';

export const uiSchema: UISchema = {
default: {
'ui:widget': ActionCard,
},
[SDKTypes.SendActivity]: {
'ui:widget': ActivityRenderer,
field: 'activity',
icon: ElementIcon.MessageBot,
colors: {
theme: ObiColors.BlueMagenta20,
icon: ObiColors.BlueMagenta30,
},
},
[SDKTypes.BeginDialog]: {
'ui:widget': DialogRefCard,
dialog: data => data.dialog,
},
[SDKTypes.ReplaceDialog]: {
'ui:widget': DialogRefCard,
dialog: data => data.dialog,
getRefContent: data => dialogRef => <>Switch to {dialogRef}</>,
},
[SDKTypes.EditArray]: {
'ui:widget': ActionCard,
content: data => `${data.changeType} {${data.itemsProperty || '?'}}`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import { FC, ComponentClass } from 'react';
import { BaseSchema, SDKTypes } from '@bfc/shared';

import { NodeEventTypes } from '../constants/NodeEventTypes';

export enum UISchemaBuiltinKeys {
default = 'default',
}
Expand All @@ -23,8 +25,12 @@ export interface UIWidget {

export type WidgetComponent<T extends WidgetContainerProps> = FC<T> | ComponentClass<T, any>;

export type WidgetEventHandler = (eventName: NodeEventTypes, eventData?: any) => void;

export interface WidgetContainerProps {
id: string;
data: BaseSchema;
onEvent: WidgetEventHandler;
[propKey: string]: any;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import React from 'react';
import React, { FC } from 'react';
import { BaseSchema } from '@bfc/shared';
import get from 'lodash/get';

import { uiSchema } from './uischema';
import { UIWidget, UI_WIDGET_KEY, UIWidgetProp } from './uischema.types';
import { UIWidget, UI_WIDGET_KEY, UIWidgetProp, WidgetEventHandler } from './uischema.types';

const buildWidgetProp = (data: BaseSchema, rawPropValue: UIWidgetProp) => {
if (typeof rawPropValue === 'function') {
Expand Down Expand Up @@ -35,14 +35,20 @@ const parseWidgetSchema = (data: BaseSchema, widgetSchema: UIWidget) => {
};
};

const renderWidget = (inputData, schema: UIWidget, contextProps = {}): JSX.Element => {
const { Widget, props } = parseWidgetSchema(inputData, schema);
return <Widget data={inputData} {...contextProps} {...props} />;
};
export interface UISchemaRendererProps {
/** The uniq id of current schema data. Usually a json path. */
id: string;

/** Declarative json with a $type field. */
data: BaseSchema;

export const renderSDKType = (data: BaseSchema, context?: { menu: JSX.Element; onClick }): JSX.Element => {
/** Handle UI events */
onEvent: WidgetEventHandler;
}

export const UISchemaRenderer: FC<UISchemaRendererProps> = ({ id, data, onEvent, ...contextProps }): JSX.Element => {
const $type = get(data, '$type');
const schema = get(uiSchema, $type, uiSchema.default);

return renderWidget(data, schema, context);
const { Widget, props } = parseWidgetSchema(data, schema);
return <Widget id={id} data={data} onEvent={onEvent} {...contextProps} {...props} />;
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ import { generateSDKTitle } from '@bfc/shared';
import { FormCard } from '../components/nodes/templates/FormCard';
import { WidgetContainerProps, WidgetComponent } from '../schema/uischema.types';
import { ObiColors } from '../constants/ElementColors';
import { NodeEventTypes } from '../constants/NodeEventTypes';
import { NodeMenu } from '../components/menus/NodeMenu';

export interface ActionCardProps extends WidgetContainerProps {
title: string;
icon: string;
content: string | number | JSX.Element;
menu: JSX.Element;
colors?: {
theme: string;
icon: string;
};
onClick: () => any;
}

const DefaultCardColor = {
Expand All @@ -26,17 +26,24 @@ const DefaultCardColor = {
};

export const ActionCard: WidgetComponent<ActionCardProps> = ({
id,
data,
onEvent,
title,
icon,
content,
menu,
onClick,
colors = DefaultCardColor,
}) => {
const header = generateSDKTitle(data, title);
const nodeColors = { themeColor: colors.theme, iconColor: colors.icon };
return (
<FormCard header={header} corner={menu} icon={icon} label={content} nodeColors={nodeColors} onClick={onClick} />
<FormCard
header={header}
corner={<NodeMenu id={id} onEvent={onEvent} />}
icon={icon}
label={content}
nodeColors={nodeColors}
onClick={() => onEvent(NodeEventTypes.Focus, { id })}
/>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import React from 'react';
import { generateSDKTitle } from '@bfc/shared';
import get from 'lodash/get';

import { NodeEventTypes } from '../constants/NodeEventTypes';
import { ElementIcon } from '../utils/obiPropertyResolver';
import { NodeMenu } from '../components/menus/NodeMenu';
import { FormCard } from '../components/nodes/templates/FormCard';
import { useLgTemplate } from '../utils/hooks';
import { WidgetContainerProps } from '../schema/uischema.types';
import { ObiColors } from '../constants/ElementColors';

export interface ActivityRenderer extends WidgetContainerProps {
/** indicates which field contains lg activity. ('activity', 'prompt', 'invalidPropmt'...) */
field: string;
icon: ElementIcon;
colors?: {
theme: string;
icon: string;
};
}

const DefaultThemeColor = {
theme: ObiColors.BlueMagenta20,
icon: ObiColors.BlueMagenta30,
};

export const ActivityRenderer: React.FC<ActivityRenderer> = ({
id,
data,
onEvent,
field,
icon = ElementIcon.MessageBot,
colors = DefaultThemeColor,
}) => {
const designerId = get(data, '$designer.id');
const activityTemplate = get(data, field, '');

const templateText = useLgTemplate(activityTemplate, designerId);
const nodeColors = { themeColor: colors.theme, iconColor: colors.icon };

return (
<FormCard
header={generateSDKTitle(data)}
label={templateText}
icon={icon}
corner={<NodeMenu id={id} onEvent={onEvent} />}
nodeColors={nodeColors}
onClick={() => {
onEvent(NodeEventTypes.Focus, { id });
}}
/>
);
};
Loading