Skip to content

Commit

Permalink
feat: repaint ui for setProperties in visual editor (#2017)
Browse files Browse the repository at this point in the history
* elements interface

* styled component

* use styledComponent in visual editor

* refactor

* add styled package

* module name

* revert uischema & refactor itemOverview component

* ui adjust

* rename & delete needless code

*  truncate text

* copy formcard to listoverviewcard for choiceinput and setProperties

* refactor code

* delete listOverviewWidget

* css

* fix some bug

Co-authored-by: Andy Brown <[email protected]>
  • Loading branch information
alanlong9278 and a-b-r-o-w-n authored Mar 3, 2020
1 parent 998219a commit aed7c65
Show file tree
Hide file tree
Showing 11 changed files with 314 additions and 184 deletions.

This file was deleted.

3 changes: 2 additions & 1 deletion Composer/packages/extensions/visual-designer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
},
"dependencies": {
"@bfc/shared": "*",
"@emotion/core": "^10.0.7",
"@emotion/core": "^10.0.27",
"@emotion/styled": "^10.0.27",
"@types/react": "16.9.0",
"classnames": "^2.2.6",
"create-react-class": "^15.6.3",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import React, { FC } from 'react';

import { SingleLineDiv } from '../elements/styledComponents';

export interface ListOverviewProps {
items: string[];
renderItem: (item: object | string) => JSX.Element;
maxCount: number;
}
export const ListOverview: FC<ListOverviewProps> = ({ items, renderItem, maxCount }) => {
if (!Array.isArray(items)) {
return null;
}
return (
<div style={{ padding: '0 0 8px 8px' }}>
{items.slice(0, maxCount).map((item, index) => {
return (
<div style={{ marginTop: '8px' }} key={index}>
{renderItem(item)}
</div>
);
})}
{items.length > maxCount ? (
<SingleLineDiv
data-testid="hasMore"
style={{
marginTop: '8px',
textAlign: 'center',
}}
>
{`${items.length - maxCount} more`}
</SingleLineDiv>
) : null}
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import { css } from '@emotion/core';
import styled from '@emotion/styled';
import { Link } from 'office-ui-fabric-react/lib/Link';

import { ObiColors } from '../../constants/ElementColors';

import { MultiLineDivProps, DivProps } from './styledComponents.types';

const dynamicStyle = props =>
css`
color: ${props.color || ObiColors.Black};
`;

export const LinkBtn = styled(Link)(props => ({
color: props.color || ObiColors.AzureBlue,
}));

export const Span = styled.span`
${dynamicStyle}
`;

export const BorderedDiv = styled.div<DivProps>(props => ({
color: props.color || ObiColors.Black,
width: props.width,
height: props.height,
padding: '2px 0 0 8px',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
overflow: 'hidden',
fontFamily: 'Segoe UI',
fontSize: '12px',
lineHeight: '14px',
border: '1px solid #C4C4C4',
boxSizing: 'border-box',
}));

export const MultiLineDiv = styled.div<MultiLineDivProps>(props => ({
color: props.color || ObiColors.Black,
fontSize: '12px',
height: `${(props.lineNum || 1) * 19}px`,
lineHeight: '19px',
fontFamily: 'Segoe UI',
overflow: 'hidden',
textOverflow: 'ellipsis',
wordBreak: 'break-word',
display: '-webkit-box',
'-webkit-line-clamp': `${props.lineNum || 1}`,
'-webkit-box-orient': 'vertical',
}));

export const SingleLineDiv = styled.div<DivProps>(props => ({
width: props.width || 150,
height: props.height || '19px',
color: props.color || ObiColors.Black,
fontSize: '12px',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
overflow: 'hidden',
lineHeight: '19px',
fontFamily: 'Segoe UI',
}));
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import { DetailedHTMLProps, HTMLAttributes, ComponentClass, FC } from 'react';

export type ElementProps = DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>;
export interface DivProps extends ElementProps {
width?: number;
height?: number;
}

export interface MultiLineDivProps extends DivProps {
lineNum?: number;
}
export type ElementComponent<T extends ElementProps> = FC<T> | ComponentClass<T, any>;
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ export const ChoiceInputSize = {
export const ChoiceInputMarginTop = 8;
export const ChoiceInputMarginBottom = 10;

export const PropertyAssignmentSize = {
width: 155,
height: 16,
};

export const AssignmentMarginTop = 8;
export const AssignmentMarginBottom = 8;

export const EventNodeSize = {
width: 240,
height: 125,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ import {
ChoiceInputMarginTop,
ChoiceInputMarginBottom,
IconBrickSize,
AssignmentMarginTop,
PropertyAssignmentSize,
AssignmentMarginBottom,
} from '../constants/ElementSizes';
import { transformIfCondtion } from '../transformers/transformIfCondition';
import { transformSwitchCondition } from '../transformers/transformSwitchCondition';
Expand Down Expand Up @@ -75,6 +78,20 @@ export function measureChoiceInputDetailBoundary(data): Boundary {
return new Boundary(width, height);
}

export function measurePropertyAssignmentBoundary(data): Boundary {
const width = InitNodeSize.width;
const height = Math.max(
InitNodeSize.height / 2 +
(data.assignments && Array.isArray(data.assignments)
? (data.assignments.length < 4
? data.assignments.length * (PropertyAssignmentSize.height + AssignmentMarginTop)
: 4 * (PropertyAssignmentSize.height + AssignmentMarginTop)) + AssignmentMarginBottom
: 0),
InitNodeSize.height
);
return new Boundary(width, height);
}

function measureBaseInputBoundary(data): Boundary {
const { botAsks, userAnswers } = transformBaseInput(data, '');
return calculateBaseInputBoundary(measureJsonBoundary(botAsks.json), measureJsonBoundary(userAnswers.json));
Expand Down Expand Up @@ -126,6 +143,9 @@ export function measureJsonBoundary(json): Boundary {
case ObiTypes.InvalidPromptBrick:
boundary = new Boundary(IconBrickSize.width, IconBrickSize.height);
break;
case ObiTypes.SetProperties:
boundary = measurePropertyAssignmentBoundary(json);
break;
case ObiTypes.EndDialog:
case ObiTypes.EndTurn:
case ObiTypes.RepeatDialog:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ import { PromptWidget } from '../widgets/PromptWidget';
import { IfConditionWidget } from '../widgets/IfConditionWidget';
import { SwitchConditionWidget } from '../widgets/SwitchConditionWidget';
import { ForeachWidget } from '../widgets/ForeachWidget';
import { ChoiceInputChoices } from '../widgets/ChoiceInput';
import { ActionHeader } from '../widgets/ActionHeader';
import { ElementIcon } from '../utils/obiPropertyResolver';
import { ObiColors } from '../constants/ElementColors';
import { SingleLineDiv, BorderedDiv } from '../components/elements/styledComponents';
import { PropertyAssignmentSize, ChoiceInputSize } from '../constants/ElementSizes';
import { ListOverview } from '../components/common/ListOverview';

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

Expand All @@ -40,14 +42,38 @@ const BaseInputSchema: UIWidget = {
icon: ElementIcon.User,
menu: 'none',
content: data => data.property || '<property>',
children: data => (data.$type === SDKTypes.ChoiceInput ? <ChoiceInputChoices choices={data.choices} /> : null),
colors: {
theme: ObiColors.LightBlue,
icon: ObiColors.AzureBlue,
},
},
};

const ChoiceInputSchema: UIWidget = Object.assign({}, BaseInputSchema, {
userInput: {
'ui:widget': ActionCard,
title: data => `User Input (${getInputType(data.$type)})`,
disableSDKTitle: true,
icon: ElementIcon.User,
menu: 'none',
content: data => <SingleLineDiv>{data.property || '<property>'}</SingleLineDiv>,
children: data => {
const renderItem = item => {
const content = item.value;
return (
<BorderedDiv width={ChoiceInputSize.width} height={ChoiceInputSize.height} title={content}>
{content}
</BorderedDiv>
);
};
return <ListOverview items={data.choices} renderItem={renderItem} maxCount={3} />;
},
colors: {
theme: ObiColors.LightBlue,
icon: ObiColors.AzureBlue,
},
},
});
export const uiSchema: UISchema = {
default: {
'ui:widget': ActionCard,
Expand Down Expand Up @@ -102,7 +128,7 @@ export const uiSchema: UISchema = {
[SDKTypes.DateTimeInput]: BaseInputSchema,
[SDKTypes.NumberInput]: BaseInputSchema,
[SDKTypes.TextInput]: BaseInputSchema,
[SDKTypes.ChoiceInput]: BaseInputSchema,
[SDKTypes.ChoiceInput]: ChoiceInputSchema,
[SDKTypes.BeginDialog]: {
'ui:widget': DialogRefCard,
dialog: data => data.dialog,
Expand All @@ -123,6 +149,22 @@ export const uiSchema: UISchema = {
[SDKTypes.SetProperties]: {
'ui:widget': ActionCard,
content: data => `Set ${Array.isArray(data.assignments) ? data.assignments.length : 0} property values`,
children: data => {
const renderItem = item => {
const content = `${item.property} = ${item.value}`;
return (
<SingleLineDiv
width={PropertyAssignmentSize.width}
height={PropertyAssignmentSize.height}
title={content}
style={{ paddingLeft: '3px' }}
>
{content}
</SingleLineDiv>
);
};
return <ListOverview items={data.assignments} renderItem={renderItem} maxCount={3} />;
},
},
[SDKTypes.DeleteProperty]: {
'ui:widget': ActionCard,
Expand Down
Loading

0 comments on commit aed7c65

Please sign in to comment.