From 7e04153eb3042079b6ce5f19c4403301c29f3873 Mon Sep 17 00:00:00 2001 From: "Ramil Minyukov (Akvelon INC)" Date: Wed, 25 Aug 2021 14:26:50 +0300 Subject: [PATCH 1/2] Updated Editor panels --- src/app/editor_panels.tsx | 102 ++++++++++++++++++++++++++++++++++++++ src/app/main_view.tsx | 59 +++------------------- 2 files changed, 109 insertions(+), 52 deletions(-) create mode 100644 src/app/editor_panels.tsx diff --git a/src/app/editor_panels.tsx b/src/app/editor_panels.tsx new file mode 100644 index 000000000..fcfd389bc --- /dev/null +++ b/src/app/editor_panels.tsx @@ -0,0 +1,102 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import { + ErrorBoundary, + MinimizablePane, + MinimizablePanelView, + TelemetryRecorder, +} from "./components"; +import { strings } from "../strings"; +import { AttributePanel, MarkEditorView } from "./views"; +import { ObjectListEditor } from "./views/panels/object_list_editor"; +import * as React from "react"; +import { MainViewState } from "./main_view"; +import { AppStore } from "./stores"; + +interface EditorPanelsProps { + state: MainViewState; + setState: (newState: MainViewState) => void; + telemetry?: TelemetryRecorder; + store: AppStore; +} + +export class EditorPanels extends React.Component< + EditorPanelsProps, + Record +> { + constructor(props: EditorPanelsProps) { + super(props); + this.props.store.addListener(AppStore.EVENT_GRAPHICS, () => + this.forceUpdate() + ); + } + + render() { + return ( +
+ + {this.props.state.glyphViewMaximized ? null : ( + + this.props.setState({ + ...this.props.state, + glyphViewMaximized: true, + }) + } + > + + + + + )} + {this.props.state.layersViewMaximized ? null : ( + + this.props.setState({ + ...this.props.state, + layersViewMaximized: true, + }) + } + > + + + + + )} + {this.props.state.attributeViewMaximized ? null : ( + + this.props.setState({ + ...this.props.state, + attributeViewMaximized: true, + }) + } + > + + + + + )} + +
+ ); + } +} diff --git a/src/app/main_view.tsx b/src/app/main_view.tsx index 57fd9b742..160b33d70 100644 --- a/src/app/main_view.tsx +++ b/src/app/main_view.tsx @@ -26,6 +26,7 @@ import { ScalesPanel } from "./views/panels/scales_panel"; import { strings } from "../strings"; import { FluentUIToolbar } from "./views/fluentui_tool_bar"; import { MainReactContext } from "./context_component"; +import { EditorPanels } from "./editor_panels"; export enum UndoRedoLocation { MenuBar = "menubar", @@ -102,8 +103,6 @@ export class MainView extends React.Component { attributeViewMaximized: false, scaleViewMaximized: false, }; - - props.store.addListener(AppStore.EVENT_GRAPHICS, () => this.forceUpdate()); } public static childContextTypes = { @@ -166,56 +165,12 @@ export class MainView extends React.Component { const editorPanels = () => { return ( -
- - {this.state.glyphViewMaximized ? null : ( - this.setState({ glyphViewMaximized: true })} - > - - - - - )} - {this.state.layersViewMaximized ? null : ( - this.setState({ layersViewMaximized: true })} - > - - - - - )} - {this.state.attributeViewMaximized ? null : ( - - this.setState({ attributeViewMaximized: true }) - } - > - - - - - )} - -
+ ); }; From b9be32b95a2cdebadedb3e4d62216f617c43063f Mon Sep 17 00:00:00 2001 From: "Ramil Minyukov (Akvelon INC)" Date: Wed, 25 Aug 2021 16:49:40 +0300 Subject: [PATCH 2/2] Added tool bar menu items --- src/app/components/draggable.tsx | 4 +- src/app/main_view.tsx | 26 +- src/app/views/fluentui_tool_bar_v2.tsx | 1468 +++++++++++++++++ src/app/views/toolbar/divider.tsx | 22 + .../views/toolbar/fluentui_tools_buttons.tsx | 111 ++ src/app/views/toolbar/history_buttons.tsx | 94 ++ 6 files changed, 1716 insertions(+), 9 deletions(-) create mode 100644 src/app/views/fluentui_tool_bar_v2.tsx create mode 100644 src/app/views/toolbar/divider.tsx create mode 100644 src/app/views/toolbar/fluentui_tools_buttons.tsx create mode 100644 src/app/views/toolbar/history_buttons.tsx diff --git a/src/app/components/draggable.tsx b/src/app/components/draggable.tsx index e9065e33c..47ac98385 100644 --- a/src/app/components/draggable.tsx +++ b/src/app/components/draggable.tsx @@ -12,6 +12,7 @@ import { classNames } from "../utils"; import { Point } from "../../core"; import * as Hammer from "hammerjs"; +import { CSSProperties } from "react"; export interface DraggableElementProps { className?: string; @@ -20,6 +21,7 @@ export interface DraggableElementProps { onDragEnd?: () => void; dragData: () => any; renderDragElement?: () => [JSX.Element, Point]; + styles?: CSSProperties; } export interface DraggableElementState { @@ -82,7 +84,7 @@ export class DraggableElement extends React.Component< "dragging", this.state.dragging, ])} - style={{ display: "inline-block", cursor: "pointer" }} + style={{ display: "inline-block", cursor: "pointer", ...this.props.styles }} > {this.props.children} diff --git a/src/app/main_view.tsx b/src/app/main_view.tsx index 160b33d70..a1a613918 100644 --- a/src/app/main_view.tsx +++ b/src/app/main_view.tsx @@ -27,6 +27,7 @@ import { strings } from "../strings"; import { FluentUIToolbar } from "./views/fluentui_tool_bar"; import { MainReactContext } from "./context_component"; import { EditorPanels } from "./editor_panels"; +import { FluentUIToolbarV2 } from "./views/fluentui_tool_bar_v2"; export enum UndoRedoLocation { MenuBar = "menubar", @@ -123,14 +124,23 @@ export class MainView extends React.Component { toolbarLabels: boolean; }) => { return ( -
- {/* */} - -
+ <> +
+ {/* */} + +
+
+ +
+ ); }; diff --git a/src/app/views/fluentui_tool_bar_v2.tsx b/src/app/views/fluentui_tool_bar_v2.tsx new file mode 100644 index 000000000..4eb9426a6 --- /dev/null +++ b/src/app/views/fluentui_tool_bar_v2.tsx @@ -0,0 +1,1468 @@ +/* eslint-disable max-lines-per-function */ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import * as React from "react"; +import { useContext, useEffect, useState } from "react"; +import * as R from "../resources"; +import { getSVGIcon } from "../resources"; + +import { EventSubscription } from "../../core"; +import { Actions, DragData } from "../actions"; +import { + DraggableElement, + FluentToolButton, + SVGImageIcon, +} from "../components"; +import { ContextedComponent, MainReactContext } from "../context_component"; + +import { LinkCreationPanel } from "./panels/link_creator"; +import { LegendCreationPanel } from "./panels/legend_creator"; +import { AppStore } from "../stores"; +import { strings } from "../../strings"; +import { LayoutDirection, UndoRedoLocation } from "../main_view"; +import { + Callout, + CommandBar, + ContextualMenuItemType, + DefaultButton, + DirectionalHint, + IconButton, + VerticalDivider, +} from "@fluentui/react"; +import { EditorType } from "../stores/app_store"; +import { ObjectTextButton } from "./toolbar/fluentui_tools_buttons"; +import { getCommandBarHistoryButtons, HistoryButtons, HistoryButtonsType } from "./toolbar/history_buttons"; +import { getCommandBarDivider } from "./toolbar/divider"; + +const minWidthToColapseButtons = Object.freeze({ + guides: 1090, + plotSegments: 1120, + scaffolds: 1211, +}); + +export const FluentUIToolbarV2: React.FC<{ + layout: LayoutDirection; + undoRedoLocation: UndoRedoLocation; + toolbarLabels: boolean; +}> = (props) => { + const {store} = useContext(MainReactContext); + const [innerWidth, setInnerWidth] = useState(window.innerWidth); + + const resizeListener = () => { + setInnerWidth(window.innerWidth); + }; + + // useEffect(() => { + // setInnerWidth(window.innerWidth); + // window.addEventListener("resize", resizeListener); + // return () => { + // window.removeEventListener("resize", resizeListener); + // }; + // }, [setInnerWidth]); + + const getGlyphToolItems = (labels: boolean = true) => { + return [ + <> + <> + + {labels && ( + + {strings.toolbar.marks} + + )} + + + + + + + {store.editorType === EditorType.Embedded ? ( + + ) : null} + {props.undoRedoLocation === UndoRedoLocation.ToolBar ? ( + <> + + new Actions.Undo().dispatch(store.dispatcher)} + /> + new Actions.Redo().dispatch(store.dispatcher)} + /> + + ) : null} + + , + ]; + }; + + // eslint-disable-next-line max-lines-per-function + const getChartToolItems = (labels: boolean = true) => { + return [ + <> + + + + {labels && ( + + {strings.toolbar.guides} + + )} + + + {labels && ( + <> + + {props.layout === LayoutDirection.Vertical + ? strings.toolbar.plot + : strings.toolbar.plotSegments} + + + )} + + <> + + {labels && ( + + {strings.toolbar.scaffolds} + + )} + null, + onDrag: () => new DragData.ScaffoldType("cartesian-x"), + }, + { + classID: "scaffold/cartesian-y", + title: strings.toolbar.lineV, + icon: "scaffold/cartesian-y", + onClick: () => null, + onDrag: () => new DragData.ScaffoldType("cartesian-y"), + }, + { + classID: "scaffold/circle", + title: strings.toolbar.polar, + icon: "scaffold/circle", + onClick: () => null, + onDrag: () => new DragData.ScaffoldType("polar"), + }, + { + classID: "scaffold/curve", + title: strings.toolbar.curve, + icon: "scaffold/curve", + onClick: () => null, + onDrag: () => new DragData.ScaffoldType("curve"), + }, + ]} + /> + + , + ]; + }; + + const renderScaffoldButton = () => { + return ( + null, + onDrag: () => new DragData.ScaffoldType("cartesian-x"), + }, + { + classID: "scaffold/cartesian-y", + title: strings.toolbar.lineV, + icon: "scaffold/cartesian-y", + onClick: () => null, + onDrag: () => new DragData.ScaffoldType("cartesian-y"), + }, + { + classID: "scaffold/circle", + title: strings.toolbar.polar, + icon: "scaffold/circle", + onClick: () => null, + onDrag: () => new DragData.ScaffoldType("polar"), + }, + { + classID: "scaffold/curve", + title: strings.toolbar.curve, + icon: "scaffold/curve", + onClick: () => null, + onDrag: () => new DragData.ScaffoldType("curve"), + }, + ]} + /> + ); + }; + + const renderGuidesButton = () => { + return ( + + ); + }; + + const getRedoUndoButtons = + props.undoRedoLocation === UndoRedoLocation.ToolBar ? ( + <> + new Actions.Undo().dispatch(store.dispatcher)} + /> + new Actions.Redo().dispatch(store.dispatcher)} + /> + + + ) : null; + + // eslint-disable-next-line max-lines-per-function + const getToolItems = ( + labels: boolean = true, + innerWidth: number = window.innerWidth + ) => { + return ( + <> + {props.undoRedoLocation === UndoRedoLocation.ToolBar ? ( + <> + new Actions.Undo().dispatch(store.dispatcher)} + /> + new Actions.Redo().dispatch(store.dispatcher)} + /> + + + ) : null} + {labels && ( + + {strings.toolbar.marks} + + )} + + + + + + + + + + + + + {/*{labels && (*/} + {/* */} + {/* {strings.toolbar.guides}*/} + {/* */} + {/*)}*/} + {renderGuidesButton()} + {/*{innerWidth > minWidthToColapseButtons.guides ? (*/} + {/* <>*/} + {/* {renderGuidesButton()}*/} + {/* */} + {/* */} + {/* */} + {/* */} + {/* */} + {/* */} + {/*) : (*/} + {/* renderGuidesButton()*/} + {/*)}*/} + + {labels && ( + <> + + {props.layout === LayoutDirection.Vertical + ? strings.toolbar.plot + : strings.toolbar.plotSegments} + + + )} + + + + {/*{labels && (*/} + {/* */} + {/* {strings.toolbar.scaffolds}*/} + {/* */} + {/*)}*/} + {/*{innerWidth > minWidthToColapseButtons.scaffolds ? (*/} + {/* <>*/} + {/* */} + {/* */} + {/* */} + {/* */} + {/* */} + {/*) : (*/} + {/* renderScaffoldButton()*/} + {/*)}*/} + + {renderScaffoldButton()} + + ); + }; + + let tooltipsItems = []; + //todo: nested embedded and remove nested + if (store.editorType === "embedded") { + const chartToolItems = getChartToolItems(props.toolbarLabels); + const glyphToolItems = getGlyphToolItems(props.toolbarLabels); + tooltipsItems = [...chartToolItems, ...glyphToolItems]; + } else { + tooltipsItems = [getToolItems(props.toolbarLabels, innerWidth)]; + } + return ( + <> +
+ { + return ( + + {strings.toolbar.marks} + + ); + }, + }, + { + key: "marks", + iconProps: { + iconName: "RectangleShape", + }, + commandBarButtonAs: () => { + return ( + + ); + }, + text: strings.toolbar.rectangle, + subMenuProps: { + items: [ + { + key: "marks.rect", + iconProps: { + iconName: "RectangleShape", + }, + text: strings.toolbar.rectangle, + }, + { + key: "marks.rect", + iconProps: { + iconName: "Ellipse", + }, + text: strings.toolbar.ellipse, + }, + { + key: "marks.rect", + iconProps: { + iconName: "TriangleShape", + }, + text: strings.toolbar.triangle, + }, + ], + }, + }, + { + key: "mark.symbol", + text: strings.toolbar.symbol, + iconProps: { + iconName: "Shapes", + }, + commandBarButtonAs: () => { + return ( + + ); + }, + }, + { + key: "mark.line", + text: strings.toolbar.line, + iconProps: { + iconName: "Line", + }, + commandBarButtonAs: () => { + return ( + + ); + }, + }, + { + key: "Text", + text: strings.toolbar.text, + iconProps: { + iconName: "FontColorA", + }, + subMenuProps: { + items: [ + { + key: "mark.text", + text: strings.toolbar.text, + iconProps: { + iconName: "FontColorA", + }, + }, + { + key: "mark.textbox", + text: strings.toolbar.textbox, + iconProps: { + iconName: "TextField", + }, + }, + ], + }, + }, + { + key: "Icon", + text: strings.toolbar.icon, + iconProps: { + iconName: "ImagePixel", + }, + subMenuProps: { + items: [ + { + key: "mark.icon", + text: strings.toolbar.icon, + iconProps: { + iconName: "ImagePixel", + }, + }, + { + key: "mark.image", + text: strings.toolbar.image, + iconProps: { + iconName: "FileImage", + }, + }, + ], + }, + }, + { + key: "mark.data-axis", + text: strings.toolbar.dataAxis, + iconProps: { + iconName: "mark/data-axis", + }, + commandBarButtonAs: () => { + return ( + + ); + }, + }, + //todo: {store.editorType === EditorType.Embedded ? ( + // + // ) : null} + { + key: "mark.nestedChart", + text: strings.toolbar.nestedChart, + iconProps: { + iconName: "BarChartVerticalFilter", + }, + commandBarButtonAs: () => { + return ( + + ); + }, + }, + + { + key: "divider2", + itemType: ContextualMenuItemType.Divider, + onRender: () => ( + + + + ), + }, + + { + key: "mark.legend", + text: strings.toolbar.legend, + iconProps: { + iconName: "CharticulatorLegend", + }, + commandBarButtonAs: () => { + return ; + }, + }, + { + key: "mark.link", + text: strings.toolbar.link, + iconProps: { + iconName: "CharticulatorLine", + }, + commandBarButtonAs: () => { + return ; + }, + }, + { + key: "divider4", + itemType: ContextualMenuItemType.Divider, + onRender: () => ( + + + + ), + }, + + { + key: "guides_label", + itemType: ContextualMenuItemType.Header, + text: strings.toolbar.guides, + commandBarButtonAs: () => { + return ( + + {strings.toolbar.guides} + + ); + }, + }, + { + key: "guides", + iconProps: { + iconName: "guide/x", + }, + text: strings.toolbar.guideX, + subMenuProps: { + items: [ + { + key: "guide-x", + iconProps: { + iconName: "guide/x", + }, + text: strings.toolbar.guideX, + }, + { + key: "guide-y", + iconProps: { + iconName: "guide/y", + }, + text: strings.toolbar.guideY, + }, + { + key: "guide-coordinator-x", + iconProps: { + iconName: "CharticulatorGuideX", + }, + text: strings.toolbar.guideX, + }, + { + key: "guide-coordinator-y", + iconProps: { + iconName: "CharticulatorGuideY", + }, + text: strings.toolbar.guideY, + }, + { + key: "guide-coordinator-polar", + iconProps: { + iconName: "CharticulatorGuideCoordinator", + }, + text: strings.toolbar.guidePolar, + }, + ], + }, + }, + { + key: "divider5", + itemType: ContextualMenuItemType.Divider, + onRender: () => ( + + + + ), + }, + + { + key: "plot_label", + itemType: ContextualMenuItemType.Header, + text: strings.toolbar.plotSegments, + commandBarButtonAs: () => { + return ( + + {strings.toolbar.plotSegments} + + ); + }, + }, + { + key: "plot-segment.cartesian", + iconProps: { + iconName: "BorderDot", + }, + text: strings.toolbar.region2D, + subMenuProps: { + items: [ + { + key: "plot-segment", + iconProps: { + iconName: "BorderDot", + }, + text: strings.toolbar.region2D, + }, + { + key: "plot-segment.line", + iconProps: { + iconName: "Line", + }, + text: strings.toolbar.line, + }, + ], + }, + }, + { + key: "divider6", + itemType: ContextualMenuItemType.Divider, + onRender: () => ( + + + + ), + }, + { + key: "scaffold_label", + text: strings.toolbar.scaffolds, + itemType: ContextualMenuItemType.Header, + commandBarButtonAs: () => { + return ( + + {strings.toolbar.scaffolds} + + ); + }, + }, + { + key: "scaffold.guide-x", + iconProps: { + iconName: "scaffold/cartesian-x", + }, + text: strings.toolbar.lineH, + commandBarButtonAs: () => { + return ( + null, + onDrag: () => new DragData.ScaffoldType("cartesian-x"), + }, + { + classID: "scaffold/cartesian-y", + title: strings.toolbar.lineV, + icon: "scaffold/cartesian-y", + onClick: () => null, + onDrag: () => new DragData.ScaffoldType("cartesian-y"), + }, + { + classID: "scaffold/circle", + title: strings.toolbar.polar, + icon: "scaffold/circle", + onClick: () => null, + onDrag: () => new DragData.ScaffoldType("polar"), + }, + { + classID: "scaffold/curve", + title: strings.toolbar.curve, + icon: "scaffold/curve", + onClick: () => null, + onDrag: () => new DragData.ScaffoldType("curve"), + }, + ]} + /> + ); + }, + subMenuProps: { + items: [ + { + key: "guide-x", + iconProps: { + iconName: "scaffold/cartesian-x", + }, + text: strings.toolbar.lineH, + onRender: (item) => { + return ( + + new DragData.ScaffoldType("cartesian-x") + } + /> + ); + }, + }, + { + key: "guide-y", + iconProps: { + iconName: "scaffold/cartesian-y", + }, + text: strings.toolbar.lineV, + onRender: (item) => { + return ( + + new DragData.ScaffoldType("cartesian-y") + } + /> + ); + }, + }, + { + key: "scaffold/circle", + iconProps: { + iconName: "scaffold/circle", + }, + text: strings.toolbar.guideX, + }, + { + key: "scaffold/curve", + iconProps: { + iconName: "scaffold/curve", + }, + text: strings.toolbar.curve, + }, + ], + }, + }, + ]} + styles={{ + root: { + height: 32, + padding: 0, + width: "100%", + }, + }} + /> +
+ + ); +}; + +export interface ObjectButtonProps { + title: string; + text?: string; + classID: string; + icon: string; + options?: string; + noDragging?: boolean; + onClick?: () => void; + onDrag?: () => any; + compact?: boolean; +} + +export class ObjectButton extends ContextedComponent> { + public token: EventSubscription; + + public getIsActive() { + return ( + this.store.currentTool == this.props.classID && + this.store.currentToolOptions == this.props.options + ); + } + + public componentDidMount() { + this.token = this.context.store.addListener( + AppStore.EVENT_CURRENT_TOOL, + () => { + //this.forceUpdate(); + } + ); + } + + public componentWillUnmount() { + this.token.remove(); + } + + public render() { + return ( + <> + { + return new DragData.ObjectType( + this.props.classID, + this.props.options + ); + } + } + onDragStart={() => this.setState({dragging: true})} + onDragEnd={() => this.setState({dragging: false})} + renderDragElement={() => { + return [ + , + {x: -16, y: -16}, + ]; + }} + > + { + this.dispatch( + new Actions.SetCurrentTool( + this.props.classID, + this.props.options + ) + ); + if (this.props.onClick) { + this.props.onClick(); + } + }} + /> + + + ); + } +} + +interface MultiObjectButtonProps { + compact?: boolean; + tools: ObjectButtonProps[]; +} + +interface MultiObjectButtonState { + currentSelection: { + classID: string; + options: string; + }; + dragging: boolean; +} + +export class MultiObjectButton extends ContextedComponent { + public state: MultiObjectButtonState = null; + + public token: EventSubscription; + + public isActive() { + const store = this.store; + for (const item of this.props.tools) { + if ( + item.classID == store.currentTool && + item.options == store.currentToolOptions + ) { + return true; + } + } + return false; + } + + public getSelectedTool() { + for (const item of this.props.tools) { + if ( + item.classID == this.state?.currentSelection?.classID && + item.options == this.state?.currentSelection?.options + ) { + return item; + } + } + return this.props.tools[0]; + } + + public componentDidMount() { + //set menu item state + this.setState({ + currentSelection: { + classID: this.props.tools[0].classID, + options: this.props.tools[0].options, + }, + dragging: false, + }); + + this.token = this.store.addListener(AppStore.EVENT_CURRENT_TOOL, () => { + for (const item of this.props.tools) { + // If the tool is within the tools defined here, we update the current selection + if ( + this.store.currentTool == item.classID && + this.store.currentToolOptions == item.options + ) { + this.setState({ + currentSelection: { + classID: item.classID, + options: item.options, + }, + }); + break; + } + } + //this.forceUpdate(); + }); + } + + public componentWillUnmount() { + this.token.remove(); + } + + public render() { + const currentTool = this.getSelectedTool(); + + return ( + { + if (currentTool?.onDrag) { + return currentTool?.onDrag(); + } + return new DragData.ObjectType( + currentTool?.classID, + currentTool?.options + ); + }} + onDragStart={() => this.setState({dragging: true})} + onDragEnd={() => this.setState({dragging: false})} + renderDragElement={() => { + return [ + , + {x: 16, y: 16}, + ]; + }} + > + { + return { + key: tool.classID + index, + data: { + classID: tool.classID, + options: tool.options, + }, + text: tool.title, + iconProps: {iconName: tool.icon}, + }; + }), + onItemClick: (ev: any, item: any) => { + if (item.data) { + this.dispatch( + new Actions.SetCurrentTool( + item.data.classID, + item.data.options + ) + ); + } + }, + }} + iconProps={{ + iconName: currentTool?.icon, + }} + onMenuClick={() => { + if (currentTool) { + this.dispatch( + new Actions.SetCurrentTool( + currentTool.classID, + currentTool.options + ) + ); + } + }} + /> + + ); + } +} + +export const ScaffoldButton: React.FC<{ + currentTool: string; + title: string; + type: string; + icon: string; +}> = (props) => { + return ( + { + // this.dispatch(new Actions.SetCurrentTool(this.props.type)); + }} + dragData={() => { + return new DragData.ScaffoldType(props.type); + }} + /> + ); +}; + +export const LinkButton: React.FC<{ + label: boolean; +}> = (props) => { + const {store} = React.useContext(MainReactContext); + const [isOpen, openDialog] = React.useState(false); + + return ( + + { + openDialog(true); + }} + /> + {isOpen ? ( + + ) : null} + + ); +}; + + +//todo: update Callout id +export const LegendButton: React.FC = () => { + const {store} = React.useContext(MainReactContext); + const [isOpen, setOpen] = React.useState(false); + + React.useEffect(() => { + return () => { + setOpen(false); + }; + }, [setOpen]); + + return ( + + { + setOpen(!isOpen); + }} + /> + {isOpen ? ( + setOpen(false)} + target="#createLegend" + directionalHint={DirectionalHint.bottomLeftEdge} + > + setOpen(false)}/> + + ) : null} + + ); +}; + diff --git a/src/app/views/toolbar/divider.tsx b/src/app/views/toolbar/divider.tsx new file mode 100644 index 000000000..ec4f4eb18 --- /dev/null +++ b/src/app/views/toolbar/divider.tsx @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import { + ContextualMenuItemType, + ICommandBarItemProps, + VerticalDivider, +} from "@fluentui/react"; +import * as React from "react"; + +export function getCommandBarDivider(key: string): ICommandBarItemProps { + return { + key: key, + itemType: ContextualMenuItemType.Divider, + onRender: () => ( + + + + ), + }; +} + diff --git a/src/app/views/toolbar/fluentui_tools_buttons.tsx b/src/app/views/toolbar/fluentui_tools_buttons.tsx new file mode 100644 index 000000000..4e79b2997 --- /dev/null +++ b/src/app/views/toolbar/fluentui_tools_buttons.tsx @@ -0,0 +1,111 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +import { ContextedComponent } from "../../context_component"; +import { EventSubscription } from "../../../core"; +import { AppStore } from "../../stores"; +import { DraggableElement, SVGImageIcon } from "../../components"; +import { Actions, DragData } from "../../actions"; +import { getSVGIcon } from "../../resources"; +import { + DefaultButton, + IconButton, +} from "@fluentui/react"; +import * as React from "react"; +import { ObjectButtonProps } from "../fluentui_tool_bar"; + + +export class ObjectTextButton extends ContextedComponent> { + public token: EventSubscription; + + public getIsActive() { + return ( + this.store.currentTool == this.props.classID && + this.store.currentToolOptions == this.props.options + ); + } + + public componentDidMount() { + this.token = this.context.store.addListener( + AppStore.EVENT_CURRENT_TOOL, + () => { + this.forceUpdate(); + } + ); + } + + public componentWillUnmount() { + this.token.remove(); + } + + public render() { + return ( + <> + { + return new DragData.ObjectType( + this.props.classID, + this.props.options + ); + } + } + onDragStart={() => this.setState({dragging: true})} + onDragEnd={() => this.setState({dragging: false})} + renderDragElement={() => { + return [ + , + {x: -16, y: -16}, + ]; + }} + > + { + this.dispatch( + new Actions.SetCurrentTool( + this.props.classID, + this.props.options + ) + ); + if (this.props.onClick) { + this.props.onClick(); + } + }} + styles={{ + root: { + border: "none", + paddingLeft: 3, + paddingRight: 3, + width: "100%", + }, + label: { + fontWeight: "400", + textAlign: "left", + }, + icon: { + color: "rgb(0, 120, 212)", + }, + }} + /> + + + ); + } +} diff --git a/src/app/views/toolbar/history_buttons.tsx b/src/app/views/toolbar/history_buttons.tsx new file mode 100644 index 000000000..3ca52f868 --- /dev/null +++ b/src/app/views/toolbar/history_buttons.tsx @@ -0,0 +1,94 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import { AppStore } from "../../stores"; +import * as React from "react"; +import { Actions } from "../../actions"; +import { ICommandBarItemProps, IconButton } from "@fluentui/react"; +import { strings } from "../../../strings"; + +export enum HistoryButtonsType { + UNDO, + REDO, +} + +interface HistoryButtonsProps { + type: HistoryButtonsType; + iconName: string; + store: AppStore; +} + +export class HistoryButtons extends React.Component { + constructor(props: HistoryButtonsProps) { + super(props); + this.props.store.addListener(AppStore.EVENT_GRAPHICS, () => + this.forceUpdate() + ); + } + + render() { + let disabled: boolean; + let onClick: () => void; + if (this.props.type === HistoryButtonsType.UNDO) { + disabled = this.props.store.historyManager.statesBefore.length === 0; + onClick = () => new Actions.Undo().dispatch(this.props.store.dispatcher); + } else { + disabled = this.props.store.historyManager.statesAfter.length === 0; + onClick = () => new Actions.Redo().dispatch(this.props.store.dispatcher); + } + + return ( + + ); + } +} + +export function getCommandBarHistoryButtons( + store: AppStore +): ICommandBarItemProps[] { + return [ + { + key: "undo", + iconProps: { + iconName: "Undo", + }, + title: strings.menuBar.undo, + onRender: () => { + return ( + + ); + }, + }, + { + key: "redo", + iconProps: { + iconName: "Redo", + }, + title: strings.menuBar.redo, + onRender: () => { + return ( + + ); + }, + }, + ]; +}