diff --git a/Composer/packages/client/src/ShellApi.ts b/Composer/packages/client/src/ShellApi.ts index ab5bb8945b..96fe32302b 100644 --- a/Composer/packages/client/src/ShellApi.ts +++ b/Composer/packages/client/src/ShellApi.ts @@ -136,7 +136,7 @@ export const ShellApi: React.FC = () => { const editorWindow = window.frames[VISUAL_EDITOR]; apiClient.apiCall('reset', getState(VISUAL_EDITOR), editorWindow); } - }, [dialogs, lgFiles, luFiles, focusPath, selected, focused]); + }, [dialogs, lgFiles, luFiles, focusPath, selected, focused, promptTab]); useEffect(() => { if (window.frames[FORM_EDITOR]) { diff --git a/Composer/packages/extensions/visual-designer/__tests__/index.test.tsx b/Composer/packages/extensions/visual-designer/__tests__/index.test.tsx index fbed6dcf37..6b3c7dedef 100644 --- a/Composer/packages/extensions/visual-designer/__tests__/index.test.tsx +++ b/Composer/packages/extensions/visual-designer/__tests__/index.test.tsx @@ -25,6 +25,7 @@ describe('', () => { dialogId="SomeDialog" focusedEvent="events[0]" focusedSteps={['events[0].steps[0]']} + focusedTab="" shellApi={{ saveData: () => {}, }} diff --git a/Composer/packages/extensions/visual-designer/demo/src/stories/VisualEditorDemo.js b/Composer/packages/extensions/visual-designer/demo/src/stories/VisualEditorDemo.js index 40ecca98f7..94f84af60f 100644 --- a/Composer/packages/extensions/visual-designer/demo/src/stories/VisualEditorDemo.js +++ b/Composer/packages/extensions/visual-designer/demo/src/stories/VisualEditorDemo.js @@ -20,6 +20,7 @@ export class VisualEditorDemo extends Component { obiJson: ObiExamples[defaultFile], focusedEvent: 'events[0]', focusedSteps: [], + focusedTab: '', }; constructor(props) { @@ -32,6 +33,7 @@ export class VisualEditorDemo extends Component { obiJson: copyJson(ObiExamples[file]), focusedEvent: '', focusedSteps: [], + focusedTab: '', }); } @@ -41,7 +43,7 @@ export class VisualEditorDemo extends Component { } render() { - const { selectedFile, obiJson, focusedEvent, focusedSteps } = this.state; + const { selectedFile, obiJson, focusedEvent, focusedSteps, focusedTab } = this.state; return (
@@ -81,6 +83,7 @@ export class VisualEditorDemo extends Component { dialogId={selectedFile} focusedEvent={focusedEvent} focusedSteps={focusedSteps} + focusedTab={focusedTab} shellApi={{ navTo: e => { console.log('navTo', e); @@ -92,10 +95,11 @@ export class VisualEditorDemo extends Component { focusedSteps: [], }); }, - onFocusSteps: stepIds => { - console.log('onFocusSteps', stepIds); + onFocusSteps: (stepIds, tabName) => { + console.log('onFocusSteps', stepIds, tabName); this.setState({ focusedSteps: stepIds, + focusedTab: tabName, }); }, saveData: json => { diff --git a/Composer/packages/extensions/visual-designer/src/components/lib/KeyboardZone.tsx b/Composer/packages/extensions/visual-designer/src/components/lib/KeyboardZone.tsx index 0d650b83a2..a567728cb6 100644 --- a/Composer/packages/extensions/visual-designer/src/components/lib/KeyboardZone.tsx +++ b/Composer/packages/extensions/visual-designer/src/components/lib/KeyboardZone.tsx @@ -1,6 +1,7 @@ /** @jsx jsx */ import { jsx } from '@emotion/core'; import { FC } from 'react'; + import { mapShortcutToKeyboardCommand } from '../../constants/KeyboardCommandTypes'; const KeyNameByModifierAttr = { diff --git a/Composer/packages/extensions/visual-designer/src/components/menus/EdgeMenu.tsx b/Composer/packages/extensions/visual-designer/src/components/menus/EdgeMenu.tsx index 2579dd078b..5769acaa41 100644 --- a/Composer/packages/extensions/visual-designer/src/components/menus/EdgeMenu.tsx +++ b/Composer/packages/extensions/visual-designer/src/components/menus/EdgeMenu.tsx @@ -73,7 +73,20 @@ export const EdgeMenu: React.FC = ({ id, onClick, ...rest }) => { > = ({ id, data, onEvent, onResize }): JSX.E return (
- + - + - + {edges ? edges.map(x => ) : null}
diff --git a/Composer/packages/extensions/visual-designer/src/components/nodes/nodeProps.ts b/Composer/packages/extensions/visual-designer/src/components/nodes/nodeProps.ts index 9fc788c357..629e14bca9 100644 --- a/Composer/packages/extensions/visual-designer/src/components/nodes/nodeProps.ts +++ b/Composer/packages/extensions/visual-designer/src/components/nodes/nodeProps.ts @@ -2,6 +2,7 @@ import { Boundary } from '../../models/Boundary'; export interface NodeProps { id: string; + tab?: string; data: any; focused?: boolean; onEvent: (action, id, ...rest) => object | void; diff --git a/Composer/packages/extensions/visual-designer/src/components/nodes/templates/FormCard.tsx b/Composer/packages/extensions/visual-designer/src/components/nodes/templates/FormCard.tsx index 62e517bb43..df4ab136a3 100644 --- a/Composer/packages/extensions/visual-designer/src/components/nodes/templates/FormCard.tsx +++ b/Composer/packages/extensions/visual-designer/src/components/nodes/templates/FormCard.tsx @@ -63,7 +63,6 @@ export const FormCard: FunctionComponent = ({ fontSize: '14px', lineHeight: '19px', color: 'black', - position: 'relative', }} >
{header}
diff --git a/Composer/packages/extensions/visual-designer/src/components/renderers/ElementRenderer.tsx b/Composer/packages/extensions/visual-designer/src/components/renderers/ElementRenderer.tsx index a2083f5ded..79aa1f265c 100644 --- a/Composer/packages/extensions/visual-designer/src/components/renderers/ElementRenderer.tsx +++ b/Composer/packages/extensions/visual-designer/src/components/renderers/ElementRenderer.tsx @@ -47,11 +47,19 @@ const nodeBorderSelectedStyle = css` box-shadow: 0px 0px 0px 2px #0078d4; `; -export const ElementRenderer: FC = ({ id, data, onEvent, onResize }): JSX.Element => { +// BotAsks, UserAnswers and InvalidPromptBrick nodes selected style +const nodeBorderDoubleSelectedStyle = css` + outline: 2px solid #0078d4; + box-shadow: 0px 0px 0px 6px rgba(0, 120, 212, 0.3); +`; + +export const ElementRenderer: FC = ({ id, data, onEvent, onResize, tab }): JSX.Element => { const ChosenRenderer = chooseRendererByType(data.$type); - const { focusedId, focusedEvent } = useContext(NodeRendererContext); - const { getNodeIndex, selectedIds } = useContext(SelectionContext); + const selectableId = tab ? `${id}${tab}` : id; + const { focusedId, focusedEvent, focusedTab } = useContext(NodeRendererContext); + const { selectedIds, getNodeIndex } = useContext(SelectionContext); const nodeFocused = focusedId === id || focusedEvent === id; + const nodeDoubleSelected = tab && nodeFocused && tab === focusedTab; const nodeSelected = selectedIds.includes(id); const declareElementAttributes = (selectedId: string, id: string) => { @@ -61,28 +69,26 @@ export const ElementRenderer: FC = ({ id, data, onEvent, onResize }): [AttrNames.FocusedId]: id, [AttrNames.SelectableElement]: true, [AttrNames.SelectedId]: selectedId, - [AttrNames.SelectionIndex]: getNodeIndex(selectedId), + [AttrNames.SelectionIndex]: getNodeIndex(id), + [AttrNames.Tab]: tab, }; }; return (
= ({ useEffect((): void => { resetSelectionData(); - setSelectedElements(querySelectedElements()); - }, [data]); + setSelectableElements(querySelectableElements()); + }, [data, focusedEvent]); const selection = new Selection({ onSelectionChanged: (): void => { @@ -201,11 +201,10 @@ export const ObiEditor: FC = ({ }, }); - const querySelectedElements = () => { - const items: NodeListOf = document.querySelectorAll(`[${AttrNames.SelectableElement}]`); - return items; + const querySelectableElements = (): NodeListOf => { + return document.querySelectorAll(`[${AttrNames.SelectableElement}]`); }; - const [selectedElements, setSelectedElements] = useState>(querySelectedElements()); + const [selectableElements, setSelectableElements] = useState>(querySelectableElements()); const getClipboardTargetsFromContext = (): string[] => { const selectedActionIds = normalizeSelection(selectionContext.selectedIds); @@ -251,12 +250,12 @@ export const ObiEditor: FC = ({ break; case KeyboardPrimaryTypes.Cursor: { const currentSelectedId = selectionContext.selectedIds[0] || focusedId; - const { selected, focused } = moveCursor(selectedElements, currentSelectedId, command); + const { selected, focused, tab } = moveCursor(selectableElements, currentSelectedId, command); setSelectionContext({ getNodeIndex: selectionContext.getNodeIndex, selectedIds: [selected as string], }); - focused && onFocusSteps([focused]); + focused && onFocusSteps([focused], tab); break; } case KeyboardPrimaryTypes.Operation: { diff --git a/Composer/packages/extensions/visual-designer/src/index.tsx b/Composer/packages/extensions/visual-designer/src/index.tsx index 7bd0a29ddf..a78154826f 100644 --- a/Composer/packages/extensions/visual-designer/src/index.tsx +++ b/Composer/packages/extensions/visual-designer/src/index.tsx @@ -15,6 +15,7 @@ const VisualDesigner: React.FC = ({ dialogId, focusedEvent, focusedSteps, + focusedTab, data: inputData, shellApi, }): JSX.Element => { @@ -48,6 +49,7 @@ const VisualDesigner: React.FC = ({ const [context, setContext] = useState({ focusedId, focusedEvent, + focusedTab, getLgTemplates: getLgTemplates, removeLgTemplate: removeLgTemplate, }); @@ -57,8 +59,9 @@ const VisualDesigner: React.FC = ({ ...context, focusedId, focusedEvent, + focusedTab, }); - }, [focusedEvent, focusedSteps]); + }, [focusedEvent, focusedSteps, focusedTab]); return ( @@ -87,6 +90,7 @@ interface VisualDesignerProps { dialogId: string; focusedEvent: string; focusedSteps: string[]; + focusedTab: string; shellApi: any; currentDialog: { id: string; displayName: string; isRoot: boolean }; } diff --git a/Composer/packages/extensions/visual-designer/src/store/NodeRendererContext.ts b/Composer/packages/extensions/visual-designer/src/store/NodeRendererContext.ts index 7a05e99252..eed3c88213 100644 --- a/Composer/packages/extensions/visual-designer/src/store/NodeRendererContext.ts +++ b/Composer/packages/extensions/visual-designer/src/store/NodeRendererContext.ts @@ -8,6 +8,7 @@ interface LgTemplate { export const NodeRendererContext = React.createContext({ focusedId: '', focusedEvent: '', + focusedTab: '', getLgTemplates: (_id: string, _templateName: string) => Promise.resolve([] as LgTemplate[]), removeLgTemplate: (_id: string, _templateName: string) => Promise.resolve(), }); diff --git a/Composer/packages/extensions/visual-designer/src/utils/cursorTracker.ts b/Composer/packages/extensions/visual-designer/src/utils/cursorTracker.ts index 11013e5128..77056c0fdb 100644 --- a/Composer/packages/extensions/visual-designer/src/utils/cursorTracker.ts +++ b/Composer/packages/extensions/visual-designer/src/utils/cursorTracker.ts @@ -60,7 +60,7 @@ function localeNearestElement( assistDistance = Math.abs( currentElementBounds.top + currentElementBounds.height / 2 - (bounds.top + bounds.height / 2) ); - if (distance > 0 && distance <= minDistance && assistMinDistance > assistDistance) { + if (distance > 0 && distance <= minDistance && assistMinDistance >= assistDistance) { neareastElement = element; minDistance = distance; assistMinDistance = assistDistance; @@ -184,7 +184,8 @@ export function moveCursor( element.scrollIntoView(true); return { - selected: element.dataset.selectedId || id, - focused: element.dataset.focusedId, + selected: element.getAttribute(AttrNames.SelectedId) || id, + focused: element.getAttribute(AttrNames.FocusedId) || undefined, + tab: element.getAttribute(AttrNames.Tab) || '', }; }