diff --git a/sass/components/buttons.scss b/sass/components/buttons.scss index 58af676a..b98c9300 100644 --- a/sass/components/buttons.scss +++ b/sass/components/buttons.scss @@ -514,7 +514,7 @@ .editable-text-view { $font-size: 14px; $padding: 0; - $height: 24px; + $height: 32px; display: flex; flex-direction: row; @@ -540,7 +540,7 @@ transition: border-color $transition-default; - border-bottom: 2px solid transparent; + border-bottom: 2px solid $gray-210; &:hover { border-bottom: 2px solid $gray-210; diff --git a/sass/components/canvas/attribute_editor.scss b/sass/components/canvas/attribute_editor.scss index 2d3730cc..ca35405f 100644 --- a/sass/components/canvas/attribute_editor.scss +++ b/sass/components/canvas/attribute_editor.scss @@ -18,12 +18,12 @@ font-size: 14px; line-height: 24px; padding-bottom: 4px; + margin-top: 1px; margin-bottom: 0px; - border-bottom: 1px solid $gray-180; + border-bottom: 0px solid $gray-180; color: $black; .svg-image-icon { - padding: 2px; width: 20px; height: 20px; vertical-align: top; @@ -1104,7 +1104,7 @@ padding-top: 4px; background: lighten($color-active, 30%); height: 26px; - + &-attrubutes { @extend .el-drag-over; width: auto; diff --git a/sass/theme.scss b/sass/theme.scss index 03ed9e23..e367a95a 100644 --- a/sass/theme.scss +++ b/sass/theme.scss @@ -7,7 +7,7 @@ $theme: ""; $theme-primary-background: $gray-100; $theme-primary-foreground: ""; -$theme-secondary-background: $gray-225; +$theme-secondary-background: $gray-230; $theme-secondary-foreground: #333; $theme-hover-background: $color-header-hover; diff --git a/src/app/components/editable_text_view.tsx b/src/app/components/editable_text_view.tsx index 385beef6..d6620112 100644 --- a/src/app/components/editable_text_view.tsx +++ b/src/app/components/editable_text_view.tsx @@ -1,8 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. import * as React from "react"; -import { ButtonFlat } from "./buttons"; -import * as R from "../resources"; +import { + FluentTextField, + labelRender, +} from "../views/panels/widgets/controls/fluentui_customized_components"; +import { TextField } from "@fluentui/react"; export interface EditableTextViewProps { text: string; @@ -57,32 +60,22 @@ export class EditableTextView extends React.Component< }); } - public componentDidMount() { - if (this.props.autofocus) { - this.refs.input.select(); - } - } - - public componentDidUpdate( - prevProps: EditableTextViewProps, - prevState: EditableTextViewState - ) { - if (prevState.editing == false && this.state.editing == true) { - this.refs.input.select(); - } - } - public render() { - if (this.state.editing) { - return ( -
- + + - this.setState({ currentText: this.refs.input.value }) - } + onRenderLabel={labelRender} + type="text" + onChange={(event, newValue) => { + this.setState({ currentText: newValue }); + }} + onBlur={() => { + if (this.state.currentText == this.props.text) { + this.cancelEdit(); + } + }} onKeyDown={(e) => { if (e.key == "Enter") { this.confirmEdit(); @@ -91,26 +84,15 @@ export class EditableTextView extends React.Component< this.cancelEdit(); } }} - autoFocus={true} - onBlur={() => { - if (this.state.currentText == this.props.text) { - this.cancelEdit(); - } + autoFocus={false} + styles={{ + fieldGroup: { + border: !this.state.editing && "none", + }, }} /> - -
- ); - } else { - return ( -
- {this.props.text} -
- ); - } + + + ); } } diff --git a/src/app/views/panels/attribute_panel.tsx b/src/app/views/panels/attribute_panel.tsx index c9136379..70c2a1ed 100644 --- a/src/app/views/panels/attribute_panel.tsx +++ b/src/app/views/panels/attribute_panel.tsx @@ -174,7 +174,11 @@ export class AttributePanel extends React.Component<
- + { diff --git a/src/app/views/panels/widgets/controls/collapsiblePanel.tsx b/src/app/views/panels/widgets/controls/collapsiblePanel.tsx new file mode 100644 index 00000000..d3175411 --- /dev/null +++ b/src/app/views/panels/widgets/controls/collapsiblePanel.tsx @@ -0,0 +1,77 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +import { + GroupedList, + GroupHeader, + IGroupHeaderProps, + SelectionMode, +} from "@fluentui/react"; +import * as React from "react"; +import { + FluentGroupedList, + groupHeaderStyles, + groupStyles, +} from "./fluentui_customized_components"; + +export const CollapsiblePanel: React.FunctionComponent<{ + header: string; + widgets: JSX.Element[]; + isCollapsed?: boolean; +}> = ({ header, widgets, isCollapsed }) => { + const [groupState, setIsCollapsed] = React.useState( + isCollapsed === undefined ? true : isCollapsed + ); + return ( + + { + return ( + null} + {...props} + styles={groupHeaderStyles} + onToggleCollapse={(group) => { + setIsCollapsed(!group.isCollapsed); + }} + /> + ); + }, + }} + selectionMode={SelectionMode.none} + items={widgets + .filter((w) => w !== null) + .map((w, i) => ({ + key: i, + item: w, + }))} + onRenderCell={( + nestingDepth?: number, + item?: { + item: JSX.Element; + key: number; + }, + itemIndex?: number + ) => { + return item && typeof itemIndex === "number" && itemIndex > -1 ? ( +
+ {item.item} +
+ ) : null; + }} + groups={[ + { + count: widgets.length, + key: "group", + level: 0, + name: header, + startIndex: 0, + isCollapsed: groupState, + }, + ]} + compact={true} + styles={groupStyles} + /> +
+ ); +}; diff --git a/src/app/views/panels/widgets/controls/fluentui_customized_components.tsx b/src/app/views/panels/widgets/controls/fluentui_customized_components.tsx index 6c3fa5c1..3bacf3ab 100644 --- a/src/app/views/panels/widgets/controls/fluentui_customized_components.tsx +++ b/src/app/views/panels/widgets/controls/fluentui_customized_components.tsx @@ -4,8 +4,13 @@ import * as React from "react"; import { IDropdownProps, + IGroupedListStyleProps, + IGroupedListStyles, + IGroupHeaderStyleProps, + IGroupHeaderStyles, ILabelStyles, IRenderFunction, + IStyleFunctionOrObject, ITextFieldProps, Label, } from "@fluentui/react"; @@ -68,12 +73,6 @@ export const FluentLayoutItem = styled.div<{ flex: number }>` export const defaultFontWeight = 400; -export const FluentLabelFontWeight = styled.div` - label { - font-weight: ${defaultFontWeight}; - } -`; - export const defaultLabelStyle: ILabelStyles = { root: { fontWeight: defaultFontWeight, @@ -88,6 +87,39 @@ export const NestedChartButtonsWrapper = styled.div` margin-top: 5px; `; +export const FluentGroupedList = styled.div` + .charticulator__widget-collapsible-panel-item { + margin-left: 15px; + margin-right: 15px; + min-width: 270px; + } + + .ms-List-surface .ms-List-cell .ms-List-cell:last-child { + margin-bottom: 10px; + } +`; + +export const groupHeaderStyles: IStyleFunctionOrObject< + IGroupHeaderStyleProps, + IGroupHeaderStyles +> = { + title: { + fontWeight: 600, + }, + headerCount: { + display: "none", + }, +}; + +export const groupStyles: IStyleFunctionOrObject< + IGroupedListStyleProps, + IGroupedListStyles +> = { + group: { + borderTop: "1px #C8C6C4 solid", + }, +}; + export const PlaceholderStyle = styled.div<{ color?: string }>` input { ::-webkit-input-placeholder { diff --git a/src/app/views/panels/widgets/controls/fluentui_input_number.tsx b/src/app/views/panels/widgets/controls/fluentui_input_number.tsx index 2cfef0bc..cf3ad333 100644 --- a/src/app/views/panels/widgets/controls/fluentui_input_number.tsx +++ b/src/app/views/panels/widgets/controls/fluentui_input_number.tsx @@ -11,8 +11,8 @@ import { import * as React from "react"; import { prettyNumber } from "../../../../../core"; import { + defaultFontWeight, defaultLabelStyle, - FluentLabelFontWeight, FluentLayoutItem, FluentRowLayout, labelRender, @@ -126,38 +126,42 @@ export const FluentInputNumber: React.FC = (props) => { const tick = props.updownTick || 0.1; return ( <> - - { + if (reportValue(parseNumber(value) + tick)) { + setValue(parseNumber(value) + tick); } - step={tick} - onIncrement={(value) => { - if (reportValue(parseNumber(value) + tick)) { - setValue(parseNumber(value) + tick); - } - }} - onDecrement={(value) => { - if (reportValue(parseNumber(value) - tick)) { - setValue(parseNumber(value) - tick); - } - }} - onValidate={(value) => { - const num = parseNumber(value); - if (reportValue(num)) { - setValue(num); - return formatNumber(parseNumber(value)); - } - }} - /> - + }} + onDecrement={(value) => { + if (reportValue(parseNumber(value) - tick)) { + setValue(parseNumber(value) - tick); + } + }} + onValidate={(value) => { + const num = parseNumber(value); + if (reportValue(num)) { + setValue(num); + return formatNumber(parseNumber(value)); + } + }} + styles={{ + label: { + fontWeight: defaultFontWeight, + height: 25, + }, + }} + /> ); }; diff --git a/src/app/views/panels/widgets/fluentui_manager.tsx b/src/app/views/panels/widgets/fluentui_manager.tsx index 65a2a80f..9cdcc743 100644 --- a/src/app/views/panels/widgets/fluentui_manager.tsx +++ b/src/app/views/panels/widgets/fluentui_manager.tsx @@ -100,6 +100,8 @@ import { InputImage, InputImageProperty } from "./controls/fluentui_image"; import { Director, IDefaultValue, MenuItemBuilder } from "../../dataset/data_field_binding_builder"; import { FluentInputFormat } from "./controls/fluentui_input_format"; +import { CollapsiblePanel } from "./controls/collapsiblePanel"; + export type OnEditMappingHandler = ( attribute: string, mapping: Specification.Mapping @@ -264,7 +266,7 @@ export class FluentUIWidgetManager } catch (ex) { return { pass: false, - error: "Invalid format", + error: strings.objects.invalidFormat, }; } } @@ -1205,62 +1207,72 @@ export class FluentUIWidgetManager options: Prototypes.Controls.GroupByEditorOptions ): JSX.Element { let button: HTMLElement; - let text = "Group by..."; - switch (options.mode) { - case "button": - if (options.value) { - if (options.value.expression) { - text = "Group by " + options.value.expression; + let text = strings.objects.plotSegment.groupBy; + const getControl = () => { + switch (options.mode) { + case "button": + if (options.value) { + if (options.value.expression) { + text = + strings.objects.plotSegment.groupByCategory + + options.value.expression; + } } - } - return ( - - (button = e)} - iconProps={{ - iconName: "RowsGroup", - }} - onClick={() => { - globals.popupController.popupAt( - (context) => { - return ( - - - - ); - }, - { anchor: ReactDOM.findDOMNode(button) as Element } - ); - }} + return ( + + (button = e)} + iconProps={{ + iconName: "RowsGroup", + }} + onClick={() => { + globals.popupController.popupAt( + (context) => { + return ( + + + + ); + }, + { anchor: button as Element } + ); + }} + /> + + ); + case "panel": + return ( + - - ); - case "panel": - return ( - - ); - } + ); + } + }; + + return ( +
(button = e)}> + {getControl()} +
+ ); } public nestedChartEditor( @@ -1371,6 +1383,19 @@ export class FluentUIWidgetManager ); } + public verticalGroup( + options: Prototypes.Controls.VerticalGroupOptions, + widgets: JSX.Element[] + ) { + return ( + + ); + } + public table( rows: JSX.Element[][], // eslint-disable-next-line @typescript-eslint/no-unused-vars diff --git a/src/app/views/panels/widgets/manager.tsx b/src/app/views/panels/widgets/manager.tsx index 5a1a7ca4..d5d0e2eb 100644 --- a/src/app/views/panels/widgets/manager.tsx +++ b/src/app/views/panels/widgets/manager.tsx @@ -120,7 +120,9 @@ export class WidgetManager ) {} public tooltip( + // eslint-disable-next-line @typescript-eslint/no-unused-vars widget: JSX.Element, + // eslint-disable-next-line @typescript-eslint/no-unused-vars tooltipContent: JSX.Element ): JSX.Element { throw new Error("Method not implemented."); @@ -710,7 +712,7 @@ export class WidgetManager const items = this.getPropertyValue(property) as string[]; return ( - { this.emitSetProperty(property, items); @@ -1213,6 +1215,21 @@ export class WidgetManager ); } + public verticalGroup( + options: Prototypes.Controls.VerticalGroupOptions, + widgets: JSX.Element[] + ) { + return ( +
+ {widgets.map((x, id) => ( + + {x} + + ))} +
+ ); + } + public table( rows: JSX.Element[][], // eslint-disable-next-line diff --git a/src/core/prototypes/controls.ts b/src/core/prototypes/controls.ts index 787273f4..e0baa715 100644 --- a/src/core/prototypes/controls.ts +++ b/src/core/prototypes/controls.ts @@ -132,6 +132,11 @@ export interface InputColorOptions { // eslint-disable-next-line export interface TableOptions {} +export interface VerticalGroupOptions { + isCollapsed?: boolean; + header: string; +} + export interface FilterEditorOptions { table: string; target: { @@ -184,6 +189,7 @@ export interface InputFormatOptions { export interface InputFormatOptions { blank?: string; + label?: string; } export interface InputFormatOptions { @@ -264,6 +270,7 @@ export interface WidgetManager { // Basic layout elements horizontal(cols: number[], ...widgets: Widget[]): Widget; + verticalGroup(options: VerticalGroupOptions, ...widgets: Widget[]): Widget; vertical(...widgets: Widget[]): Widget; table(rows: Widget[][], options?: TableOptions): Widget; scrollList(widgets: Widget[], options?: ScrollListOptions): Widget; diff --git a/src/core/prototypes/guides/guide_coordinator.ts b/src/core/prototypes/guides/guide_coordinator.ts index f022632b..bfaad340 100644 --- a/src/core/prototypes/guides/guide_coordinator.ts +++ b/src/core/prototypes/guides/guide_coordinator.ts @@ -239,19 +239,16 @@ export class GuideCoordinatorClass extends ChartElementClass< manager: Controls.WidgetManager ): Controls.Widget[] { return [ - manager.sectionHeader(strings.objects.guides.guideCoordinator), - manager.row( - strings.objects.guides.count, - manager.inputNumber( - { property: "count" }, - { - showUpdown: true, - updownTick: 1, - updownRange: [1, 100], - minimum: 1, - maximum: 100, - } - ) + manager.inputNumber( + { property: "count" }, + { + showUpdown: true, + updownTick: 1, + updownRange: [1, 100], + minimum: 1, + maximum: 100, + label: strings.objects.guides.count, + } ), ]; } diff --git a/src/core/prototypes/legends/categorical_legend.ts b/src/core/prototypes/legends/categorical_legend.ts index 30ad9a0a..c59d5e40 100644 --- a/src/core/prototypes/legends/categorical_legend.ts +++ b/src/core/prototypes/legends/categorical_legend.ts @@ -272,19 +272,26 @@ export class CategoricalLegendClass extends LegendClass { return [ ...widgets, - manager.inputSelect( - { property: "orientation" }, + manager.verticalGroup( { - type: "radio", - showLabel: false, - icons: ["AlignHorizontalCenter", "AlignVerticalCenter"], - labels: [ - strings.objects.legend.vertical, - strings.objects.legend.horizontal, - ], - options: ["vertical", "horizontal"], - label: strings.objects.legend.orientation, - } + header: strings.objects.legend.categoricalLegend, + }, + [ + manager.inputSelect( + { property: "orientation" }, + { + type: "radio", + showLabel: false, + icons: ["AlignHorizontalCenter", "AlignVerticalCenter"], + labels: [ + strings.objects.legend.vertical, + strings.objects.legend.horizontal, + ], + options: ["vertical", "horizontal"], + label: strings.objects.legend.orientation, + } + ), + ] ), ]; } diff --git a/src/core/prototypes/legends/legend.ts b/src/core/prototypes/legends/legend.ts index c1dd99b3..ae0b8836 100644 --- a/src/core/prototypes/legends/legend.ts +++ b/src/core/prototypes/legends/legend.ts @@ -189,106 +189,118 @@ export abstract class LegendClass extends ChartElementClass { manager: Prototypes.Controls.WidgetManager & CharticulatorPropertyAccessors ): Controls.Widget[] { const widget = [ - manager.sectionHeader(strings.objects.legend.labels), - manager.inputFontFamily( - { property: "fontFamily" }, - { label: strings.objects.font } - ), - manager.inputNumber( - { property: "fontSize" }, + manager.verticalGroup( { - showUpdown: true, - updownStyle: "font", - updownTick: 2, - label: strings.objects.size, - } - ), - manager.inputColor( - { property: "textColor" }, - { label: strings.objects.color } - ), - manager.inputSelect( - { property: "markerShape" }, - { - type: "dropdown", - showLabel: true, - icons: ["RectangleShape", "TriangleShape", "Ellipse"], - labels: [ - strings.toolbar.rectangle, - strings.toolbar.triangle, - strings.toolbar.ellipse, - ], - options: ["rectangle", "triangle", "circle"], - label: strings.objects.legend.markerShape, - } - ), - manager.label("Ordering"), - manager.reorderWidget( - { - property: "order", + header: strings.objects.legend.labels, }, - { - items: this.getOrderingObjects(), - onConfirm: (items: string[]) => { - const scale = this.getScale()[0]; - const newMap: { [name: string]: ValueType } = {}; - items.forEach((item) => { - newMap[item] = (( - scale.properties.mapping - ))[item]; - }); - Prototypes.setProperty(scale, "mapping", newMap); - manager.emitSetProperty( - { - property: "mapping", - field: null, + [ + manager.inputFontFamily( + { property: "fontFamily" }, + { label: strings.objects.font } + ), + manager.inputNumber( + { property: "fontSize" }, + { + showUpdown: true, + updownStyle: "font", + updownTick: 2, + label: strings.objects.size, + } + ), + manager.inputColor( + { property: "textColor" }, + { label: strings.objects.color } + ), + manager.inputSelect( + { property: "markerShape" }, + { + type: "dropdown", + showLabel: true, + icons: ["RectangleShape", "TriangleShape", "Ellipse"], + labels: [ + strings.toolbar.rectangle, + strings.toolbar.triangle, + strings.toolbar.ellipse, + ], + options: ["rectangle", "triangle", "circle"], + label: strings.objects.legend.markerShape, + } + ), + manager.label("Ordering"), + manager.reorderWidget( + { + property: "order", + }, + { + items: this.getOrderingObjects(), + onConfirm: (items: string[]) => { + const scale = this.getScale()[0]; + const newMap: { [name: string]: ValueType } = {}; + items.forEach((item) => { + newMap[item] = (( + scale.properties.mapping + ))[item]; + }); + Prototypes.setProperty(scale, "mapping", newMap); + manager.emitSetProperty( + { + property: "mapping", + field: null, + }, + newMap + ); }, - newMap - ); - }, - } + } + ), + ] ), - manager.sectionHeader(strings.objects.legend.layout), - manager.vertical( - manager.label(strings.alignment.alignment), - manager.horizontal( - [0, 0], - manager.inputSelect( - { property: "alignX" }, - { - type: "radio", - icons: [ - "AlignHorizontalLeft", - "AlignHorizontalCenter", - "AlignHorizontalRight", - ], - labels: [ - strings.alignment.left, - strings.alignment.middle, - strings.alignment.right, - ], - options: ["start", "middle", "end"], - } + manager.verticalGroup( + { + header: strings.objects.legend.layout, + }, + [ + manager.vertical( + manager.label(strings.alignment.alignment), + manager.horizontal( + [0, 0], + manager.inputSelect( + { property: "alignX" }, + { + type: "radio", + icons: [ + "AlignHorizontalLeft", + "AlignHorizontalCenter", + "AlignHorizontalRight", + ], + labels: [ + strings.alignment.left, + strings.alignment.middle, + strings.alignment.right, + ], + options: ["start", "middle", "end"], + } + ), + manager.inputSelect( + { property: "alignY" }, + { + type: "radio", + options: ["start", "middle", "end"], + icons: [ + "AlignVerticalBottom", + "AlignVerticalCenter", + "AlignVerticalTop", + ], + labels: [ + strings.alignment.bottom, + strings.alignment.middle, + strings.alignment.top, + ], + } + ), + null + ) ), - manager.inputSelect( - { property: "alignY" }, - { - type: "radio", - options: ["start", "middle", "end"], - icons: [ - "AlignVerticalBottom", - "AlignVerticalCenter", - "AlignVerticalTop", - ], - labels: [ - strings.alignment.bottom, - strings.alignment.middle, - strings.alignment.top, - ], - } - ), - null - ) + ] ), ]; diff --git a/src/core/prototypes/marks/data_axis.ts b/src/core/prototypes/marks/data_axis.ts index aa00ec29..423e30c5 100644 --- a/src/core/prototypes/marks/data_axis.ts +++ b/src/core/prototypes/marks/data_axis.ts @@ -334,49 +334,62 @@ export class DataAxisClass extends MarkClass< manager, strings.toolbar.dataAxis ); - const r = [...axisWidgets]; - r.push( - manager.inputSelect( - { property: "visibleOn" }, + const r = [ + manager.verticalGroup( { - labels: [ - strings.objects.visibleOn.all, - strings.objects.visibleOn.first, - strings.objects.visibleOn.last, - ], - showLabel: true, - options: ["all", "first", "last"], - type: "dropdown", - label: strings.objects.visibleOn.label, - } - ) - ); + header: strings.objects.general, + }, + [ + manager.inputSelect( + { property: "visibleOn" }, + { + labels: [ + strings.objects.visibleOn.all, + strings.objects.visibleOn.first, + strings.objects.visibleOn.last, + ], + showLabel: true, + options: ["all", "first", "last"], + type: "dropdown", + label: strings.objects.visibleOn.label, + } + ), + ] + ), + ...axisWidgets, + ]; if (props.dataExpressions.length > 0) { - r.push(manager.sectionHeader("Data Expressions")); r.push( - manager.arrayWidget( - { property: "dataExpressions" }, - (item, index) => { - const expressionInput = manager.inputExpression( - { - property: "dataExpressions", - field: - item.field instanceof Array - ? [...item.field, "expression"] - : [item.field, "expression"], - }, - { table: this.getGlyphClass().object.table } - ); - return React.createElement( - "fragment", - { key: index }, - expressionInput - ); - }, + manager.verticalGroup( { - allowDelete: true, - allowReorder: true, - } + header: strings.objects.axes.dataExpressions, + }, + [ + manager.arrayWidget( + { property: "dataExpressions" }, + (item, index) => { + const expressionInput = manager.inputExpression( + { + property: "dataExpressions", + field: + item.field instanceof Array + ? [...item.field, "expression"] + : [item.field, "expression"], + }, + { table: this.getGlyphClass().object.table } + ); + return React.createElement( + "fragment", + { key: index }, + expressionInput + ); + }, + { + allowDelete: true, + allowReorder: true, + } + ), + ] ) ); } diff --git a/src/core/prototypes/marks/icon.ts b/src/core/prototypes/marks/icon.ts index aa0e3557..8481f887 100644 --- a/src/core/prototypes/marks/icon.ts +++ b/src/core/prototypes/marks/icon.ts @@ -1,3 +1,4 @@ +/* eslint-disable max-lines-per-function */ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. @@ -285,92 +286,112 @@ export class IconElementClass extends EmphasizableMarkClass< const parentWidgets = super.getAttributePanelWidgets(manager); const props = this.object.properties; let widgets = [ - manager.sectionHeader(strings.toolbar.icon), - manager.mappingEditor(strings.objects.icon.image, "image", {}), - manager.mappingEditor(strings.objects.size, "size", { - acceptKinds: [Specification.DataKind.Numerical], - hints: { rangeNumber: [0, 100] }, - defaultValue: 400, - numberOptions: { - showSlider: true, - minimum: 0, - sliderRange: [0, 3600], - sliderFunction: "sqrt", + manager.verticalGroup( + { + header: strings.toolbar.icon, }, - }), + [ + manager.mappingEditor(strings.objects.icon.image, "image", {}), + manager.mappingEditor(strings.objects.size, "size", { + acceptKinds: [Specification.DataKind.Numerical], + hints: { rangeNumber: [0, 100] }, + defaultValue: 400, + numberOptions: { + showSlider: true, + minimum: 0, + sliderRange: [0, 3600], + sliderFunction: "sqrt", + }, + }), + manager.mappingEditor( + strings.objects.visibleOn.visibility, + "visible", + { + defaultValue: true, + } + ), + ] + ), ]; widgets = widgets.concat([ - manager.sectionHeader(strings.objects.icon.anchorAndRotation), - manager.row( - strings.objects.icon.anchorX, - manager.horizontal( - [0, 1], - manager.inputSelect( - { property: "alignment", field: "x" }, - { - type: "radio", - icons: [ - "AlignHorizontalLeft", - "AlignHorizontalCenter", - "AlignHorizontalRight", - ], - labels: [ - strings.alignment.left, - strings.alignment.middle, - strings.alignment.right, - ], - options: ["left", "middle", "right"], - } + manager.verticalGroup( + { + header: strings.objects.anchorAndRotation, + }, + [ + manager.horizontal( + [0, 1], + manager.inputSelect( + { property: "alignment", field: "x" }, + { + type: "radio", + icons: [ + "AlignHorizontalLeft", + "AlignHorizontalCenter", + "AlignHorizontalRight", + ], + labels: [ + strings.alignment.left, + strings.alignment.middle, + strings.alignment.right, + ], + options: ["left", "middle", "right"], + label: strings.objects.anchorX, + } + ), + props.alignment.x != "middle" + ? manager.inputNumber( + { property: "alignment", field: "xMargin" }, + { + label: strings.margins.margin, + } + ) + : null ), - props.alignment.x != "middle" - ? manager.horizontal( - [0, 1], - manager.label(strings.margins.margin), - manager.inputNumber({ property: "alignment", field: "xMargin" }) - ) - : null - ) - ), - manager.row( - strings.objects.icon.anchorY, - manager.horizontal( - [0, 1], - manager.inputSelect( - { property: "alignment", field: "y" }, - { - type: "radio", - icons: [ - "AlignVerticalTop", - "AlignVerticalCenter", - "AlignVerticalBottom", - ], - labels: [ - strings.alignment.top, - strings.alignment.middle, - strings.alignment.bottom, - ], - options: ["top", "middle", "bottom"], - } + manager.horizontal( + [0, 1], + manager.inputSelect( + { property: "alignment", field: "y" }, + { + type: "radio", + icons: [ + "AlignVerticalTop", + "AlignVerticalCenter", + "AlignVerticalBottom", + ], + labels: [ + strings.alignment.top, + strings.alignment.middle, + strings.alignment.bottom, + ], + options: ["top", "middle", "bottom"], + label: strings.objects.anchorY, + } + ), + props.alignment.y != "middle" + ? manager.inputNumber( + { property: "alignment", field: "yMargin" }, + { + label: strings.margins.margin, + } + ) + : null ), - props.alignment.y != "middle" - ? manager.horizontal( - [0, 1], - manager.label("Margin:"), - manager.inputNumber({ property: "alignment", field: "yMargin" }) - ) - : null - ) + ] + ), + manager.verticalGroup( + { + header: strings.objects.style, + }, + [ + manager.mappingEditor(strings.objects.opacity, "opacity", { + hints: { rangeNumber: [0, 1] }, + defaultValue: 1, + numberOptions: { showSlider: true, minimum: 0, maximum: 1 }, + }), + ] ), - manager.sectionHeader(strings.objects.style), - manager.mappingEditor(strings.objects.opacity, "opacity", { - hints: { rangeNumber: [0, 1] }, - defaultValue: 1, - numberOptions: { showSlider: true, minimum: 0, maximum: 1, step: 0.1 }, - }), - manager.mappingEditor(strings.objects.visibleOn.visibility, "visible", { - defaultValue: true, - }), ]); return widgets.concat(parentWidgets); } diff --git a/src/core/prototypes/marks/image.ts b/src/core/prototypes/marks/image.ts index af11c2e2..1b03fbb5 100644 --- a/src/core/prototypes/marks/image.ts +++ b/src/core/prototypes/marks/image.ts @@ -96,115 +96,148 @@ export class ImageElementClass extends EmphasizableMarkClass< manager: Controls.WidgetManager ): Controls.Widget[] { const parentWidgets = super.getAttributePanelWidgets(manager); - let widgets: Controls.Widget[] = [ - manager.sectionHeader(strings.objects.size), - manager.mappingEditor(strings.objects.width, "width", { - hints: { autoRange: true, startWithZero: "always" }, - acceptKinds: [Specification.DataKind.Numerical], - defaultAuto: true, - }), - manager.mappingEditor(strings.objects.height, "height", { - hints: { autoRange: true, startWithZero: "always" }, - acceptKinds: [Specification.DataKind.Numerical], - defaultAuto: true, - }), - manager.sectionHeader(strings.toolbar.image), - manager.mappingEditor(strings.objects.icon.image, "image", {}), - manager.inputSelect( - { property: "imageMode" }, + const widgets: Controls.Widget[] = [ + manager.verticalGroup( { - type: "dropdown", - showLabel: true, - labels: [ - strings.objects.image.letterbox, - strings.objects.image.stretch, - ], - options: ["letterbox", "stretch"], - label: strings.objects.image.imageMode, - } + header: strings.objects.general, + }, + [ + manager.mappingEditor(strings.objects.width, "width", { + hints: { autoRange: true, startWithZero: "always" }, + acceptKinds: [Specification.DataKind.Numerical], + defaultAuto: true, + }), + manager.mappingEditor(strings.objects.height, "height", { + hints: { autoRange: true, startWithZero: "always" }, + acceptKinds: [Specification.DataKind.Numerical], + defaultAuto: true, + }), + manager.mappingEditor( + strings.objects.visibleOn.visibility, + "visible", + { + defaultValue: true, + } + ), + ] ), - ...(this.object.properties.imageMode == "letterbox" - ? [ - manager.label(strings.alignment.align), - manager.horizontal( - [0, 1], - manager.inputSelect( - { property: "alignX" }, + // manager.sectionHeader(strings.objects.size), + manager.verticalGroup( + { + header: strings.toolbar.image, + }, + [ + manager.mappingEditor(strings.objects.icon.image, "image", {}), + manager.inputSelect( + { property: "imageMode" }, + { + type: "dropdown", + showLabel: true, + labels: [ + strings.objects.image.letterbox, + strings.objects.image.stretch, + ], + options: ["letterbox", "stretch"], + label: strings.objects.image.imageMode, + } + ), + ...(this.object.properties.imageMode == "letterbox" + ? [ + manager.label(strings.alignment.align), + manager.horizontal( + [0, 1], + manager.inputSelect( + { property: "alignX" }, + { + type: "radio", + options: ["start", "middle", "end"], + icons: [ + "AlignHorizontalLeft", + "AlignHorizontalCenter", + "AlignHorizontalRight", + ], + labels: [ + strings.alignment.left, + strings.alignment.middle, + strings.alignment.right, + ], + } + ), + manager.inputSelect( + { property: "alignY" }, + { + type: "radio", + options: ["start", "middle", "end"], + icons: [ + "AlignVerticalBottom", + "AlignVerticalCenter", + "AlignVerticalTop", + ], + labels: [ + strings.alignment.bottom, + strings.alignment.middle, + strings.alignment.top, + ], + } + ) + ), + ] + : []), + ] + ), + manager.verticalGroup( + { + header: strings.alignment.padding, + }, + [ + manager.label(strings.coordinateSystem.x), + manager.inputNumber( + { property: "paddingX" }, + { + updownTick: 1, + showUpdown: true, + } + ), + manager.label(strings.coordinateSystem.y), + manager.inputNumber( + { property: "paddingY" }, + { + updownTick: 1, + showUpdown: true, + } + ), + ] + ), + manager.verticalGroup( + { + header: strings.objects.style, + }, + [ + manager.mappingEditor(strings.objects.fill, "fill", {}), + manager.mappingEditor(strings.objects.stroke, "stroke", {}), + this.object.mappings.stroke != null + ? manager.mappingEditor( + strings.objects.strokeWidth, + "strokeWidth", { - type: "radio", - options: ["start", "middle", "end"], - icons: [ - "AlignHorizontalLeft", - "AlignHorizontalCenter", - "AlignHorizontalRight", - ], - labels: [ - strings.alignment.left, - strings.alignment.middle, - strings.alignment.right, - ], - } - ), - manager.inputSelect( - { property: "alignY" }, - { - type: "radio", - options: ["start", "middle", "end"], - icons: [ - "AlignVerticalBottom", - "AlignVerticalCenter", - "AlignVerticalTop", - ], - labels: [ - strings.alignment.bottom, - strings.alignment.middle, - strings.alignment.top, - ], + hints: { rangeNumber: [0, 5] }, + defaultValue: 1, + numberOptions: { + showSlider: true, + sliderRange: [0, 5], + minimum: 0, + }, } ) - ), - ] - : []), - manager.sectionHeader(strings.alignment.padding), - manager.label(strings.coordinateSystem.x), - manager.inputNumber( - { property: "paddingX" }, - { - updownTick: 1, - showUpdown: true, - } + : null, + manager.mappingEditor(strings.objects.opacity, "opacity", { + hints: { rangeNumber: [0, 1] }, + defaultValue: 1, + numberOptions: { showSlider: true, minimum: 0, maximum: 1 }, + }), + ] ), - manager.label(strings.coordinateSystem.y), - manager.inputNumber( - { property: "paddingY" }, - { - updownTick: 1, - showUpdown: true, - } - ), - manager.sectionHeader(strings.objects.style), - manager.mappingEditor(strings.objects.fill, "fill", {}), - manager.mappingEditor(strings.objects.stroke, "stroke", {}), ]; - if (this.object.mappings.stroke != null) { - widgets.push( - manager.mappingEditor(strings.objects.strokeWidth, "strokeWidth", { - hints: { rangeNumber: [0, 5] }, - defaultValue: 1, - numberOptions: { showSlider: true, sliderRange: [0, 5], minimum: 0 }, - }) - ); - } - widgets = widgets.concat([ - manager.mappingEditor(strings.objects.opacity, "opacity", { - hints: { rangeNumber: [0, 1] }, - defaultValue: 1, - numberOptions: { showSlider: true, minimum: 0, maximum: 1, step: 0.1 }, - }), - manager.mappingEditor(strings.objects.visibleOn.visibility, "visible", { - defaultValue: true, - }), - ]); return widgets.concat(parentWidgets); } diff --git a/src/core/prototypes/marks/line.ts b/src/core/prototypes/marks/line.ts index 05a7909e..f0ac14a8 100644 --- a/src/core/prototypes/marks/line.ts +++ b/src/core/prototypes/marks/line.ts @@ -26,6 +26,7 @@ import * as Graphics from "../../graphics"; import { EmphasizableMarkClass } from "./emphasis"; import { ChartStateManager } from "../state"; import { MappingType } from "../../specification"; +import { strings } from "../../../strings"; export { LineElementAttributes, LineElementProperties }; @@ -305,45 +306,59 @@ export class LineElementClass extends EmphasizableMarkClass< ): Controls.Widget[] { const parentWidgets = super.getAttributePanelWidgets(manager); return [ - manager.sectionHeader("Line"), - manager.mappingEditor("X Span", "dx", { - hints: { autoRange: true, startWithZero: "always" }, - acceptKinds: [Specification.DataKind.Numerical], - defaultAuto: true, - }), - manager.mappingEditor("Y Span", "dy", { - hints: { autoRange: true, startWithZero: "always" }, - acceptKinds: [Specification.DataKind.Numerical], - defaultAuto: true, - }), - manager.sectionHeader("Style"), - manager.mappingEditor("Stroke", "stroke", {}), - manager.mappingEditor("Line Width", "strokeWidth", { - hints: { rangeNumber: [0, 5] }, - defaultValue: 1, - numberOptions: { showSlider: true, sliderRange: [0, 5], minimum: 0 }, - }), - manager.row( - "Line Style", - manager.inputSelect( - { property: "strokeStyle" }, - { - type: "dropdown", - showLabel: true, - icons: ["stroke/solid", "stroke/dashed", "stroke/dotted"], - labels: ["Solid", "Dashed", "Dotted"], - options: ["solid", "dashed", "dotted"], - } - ) + manager.verticalGroup( + { + header: strings.toolbar.line, + }, + [ + manager.mappingEditor("X Span", "dx", { + hints: { autoRange: true, startWithZero: "always" }, + acceptKinds: [Specification.DataKind.Numerical], + defaultAuto: true, + }), + manager.mappingEditor("Y Span", "dy", { + hints: { autoRange: true, startWithZero: "always" }, + acceptKinds: [Specification.DataKind.Numerical], + defaultAuto: true, + }), + manager.mappingEditor("Visibility", "visible", { + defaultValue: true, + }), + ] + ), + manager.verticalGroup( + { + header: strings.objects.style, + }, + [ + manager.mappingEditor(strings.objects.stroke, "stroke", {}), + manager.mappingEditor(strings.objects.strokeWidth, "strokeWidth", { + hints: { rangeNumber: [0, 5] }, + defaultValue: 1, + numberOptions: { + showSlider: true, + sliderRange: [0, 5], + minimum: 0, + }, + }), + manager.inputSelect( + { property: "strokeStyle" }, + { + type: "dropdown", + showLabel: true, + icons: ["stroke/solid", "stroke/dashed", "stroke/dotted"], + labels: ["Solid", "Dashed", "Dotted"], + options: ["solid", "dashed", "dotted"], + label: strings.objects.line.lineStyle, + } + ), + manager.mappingEditor(strings.objects.opacity, "opacity", { + hints: { rangeNumber: [0, 1] }, + defaultValue: 1, + numberOptions: { showSlider: true, minimum: 0, maximum: 1 }, + }), + ] ), - manager.mappingEditor("Opacity", "opacity", { - hints: { rangeNumber: [0, 1] }, - defaultValue: 1, - numberOptions: { showSlider: true, minimum: 0, maximum: 1, step: 0.1 }, - }), - manager.mappingEditor("Visibility", "visible", { - defaultValue: true, - }), ].concat(parentWidgets); } diff --git a/src/core/prototypes/marks/rect.ts b/src/core/prototypes/marks/rect.ts index bc7012fc..f6d059bf 100644 --- a/src/core/prototypes/marks/rect.ts +++ b/src/core/prototypes/marks/rect.ts @@ -1,3 +1,4 @@ +/* eslint-disable max-lines-per-function */ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. @@ -25,6 +26,7 @@ import { RectElementAttributes, RectElementProperties, } from "./rect.attrs"; +import { strings } from "../../../strings"; export { RectElementAttributes, RectElementProperties }; @@ -172,70 +174,99 @@ export class RectElementClass extends EmphasizableMarkClass< const parentWidgets = super.getAttributePanelWidgets(manager); let widgets: Controls.Widget[] = [ - manager.sectionHeader("Size & Shape"), - manager.mappingEditor("Width", "width", { - hints: { autoRange: true, startWithZero: "always" }, - acceptKinds: [DataKind.Numerical], - defaultAuto: true, - }), - manager.mappingEditor("Height", "height", { - hints: { autoRange: true, startWithZero: "always" }, - acceptKinds: [DataKind.Numerical], - defaultAuto: true, - }), - manager.inputSelect( - { property: "shape" }, + manager.verticalGroup( { - type: "dropdown", - showLabel: true, - label: "Shape", - icons: ["RectangleShape", "TriangleShape", "Ellipse"], - labels: ["Rectangle", "Triangle", "Ellipse"], - options: ["rectangle", "triangle", "ellipse"], - } + header: strings.objects.general, + }, + [ + manager.mappingEditor(strings.objects.width, "width", { + hints: { autoRange: true, startWithZero: "always" }, + acceptKinds: [DataKind.Numerical], + defaultAuto: true, + }), + manager.mappingEditor(strings.objects.height, "height", { + hints: { autoRange: true, startWithZero: "always" }, + acceptKinds: [DataKind.Numerical], + defaultAuto: true, + }), + manager.inputSelect( + { property: "shape" }, + { + type: "dropdown", + showLabel: true, + label: strings.objects.rect.shape, + icons: ["RectangleShape", "TriangleShape", "Ellipse"], + labels: [ + strings.objects.rect.shapes.rectangle, + strings.objects.rect.shapes.triangle, + strings.objects.rect.shapes.ellipse, + ], + options: ["rectangle", "triangle", "ellipse"], + } + ), + manager.inputBoolean( + { property: "allowFlipping" }, + { + type: "checkbox", + label: strings.objects.rect.flipping, + } + ), + manager.mappingEditor( + strings.objects.visibleOn.visibility, + "visible", + { + defaultValue: true, + } + ), + ] ), - manager.inputBoolean( - { property: "allowFlipping" }, + manager.verticalGroup( { - type: "checkbox", - label: "Flipping", - } + header: strings.objects.style, + }, + [ + manager.mappingEditor(strings.objects.fill, "fill", {}), + manager.mappingEditor(strings.objects.stroke, "stroke", {}), + this.object.mappings.stroke != null + ? manager.mappingEditor( + strings.objects.strokeWidth, + "strokeWidth", + { + hints: { rangeNumber: [0, 5] }, + defaultValue: 1, + numberOptions: { + showSlider: true, + sliderRange: [0, 5], + minimum: 0, + }, + } + ) + : null, + this.object.mappings.stroke != null + ? manager.inputSelect( + { property: "strokeStyle" }, + { + type: "dropdown", + showLabel: true, + label: "Line Style", + icons: ["stroke/solid", "stroke/dashed", "stroke/dotted"], + labels: [ + strings.objects.links.solid, + strings.objects.links.dashed, + strings.objects.links.dotted, + ], + options: ["solid", "dashed", "dotted"], + } + ) + : null, + manager.mappingEditor(strings.objects.opacity, "opacity", { + hints: { rangeNumber: [0, 1] }, + defaultValue: 1, + numberOptions: { showSlider: true, minimum: 0, maximum: 1 }, + }), + ] ), - manager.sectionHeader("Style"), - manager.mappingEditor("Fill", "fill", {}), - manager.mappingEditor("Stroke", "stroke", {}), ]; - if (this.object.mappings.stroke != null) { - widgets.push( - manager.mappingEditor("Line Width", "strokeWidth", { - hints: { rangeNumber: [0, 5] }, - defaultValue: 1, - numberOptions: { showSlider: true, sliderRange: [0, 5], minimum: 0 }, - }) - ); - manager.inputSelect( - { property: "strokeStyle" }, - { - type: "dropdown", - showLabel: true, - label: "Line Style", - icons: ["stroke/solid", "stroke/dashed", "stroke/dotted"], - labels: ["Solid", "Dashed", "Dotted"], - options: ["solid", "dashed", "dotted"], - } - ); - } - - widgets = widgets.concat([ - manager.mappingEditor("Opacity", "opacity", { - hints: { rangeNumber: [0, 1] }, - defaultValue: 1, - numberOptions: { showSlider: true, minimum: 0, maximum: 1, step: 0.1 }, - }), - manager.mappingEditor("Visibility", "visible", { - defaultValue: true, - }), - ]); widgets = widgets.concat(parentWidgets); return widgets; diff --git a/src/core/prototypes/marks/symbol.ts b/src/core/prototypes/marks/symbol.ts index b6a273a7..430c53b0 100644 --- a/src/core/prototypes/marks/symbol.ts +++ b/src/core/prototypes/marks/symbol.ts @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. +import { strings } from "../../../strings"; import { Point, Color, rgbToHex } from "../../common"; import * as Graphics from "../../graphics"; import * as Specification from "../../specification"; @@ -297,46 +298,68 @@ export class SymbolElementClass extends EmphasizableMarkClass< ): Controls.Widget[] { const parentWidgets = super.getAttributePanelWidgets(manager); let widgets = [ - manager.sectionHeader("Symbol"), - manager.mappingEditor("Shape", "symbol", { - acceptKinds: [Specification.DataKind.Categorical], - hints: { rangeEnum: symbolTypes }, - defaultValue: "circle", - }), - manager.mappingEditor("Size", "size", { - acceptKinds: [Specification.DataKind.Numerical], - hints: { rangeNumber: [0, 200 * Math.PI] }, - defaultValue: 60, - numberOptions: { - showSlider: true, - minimum: 0, - sliderRange: [0, 3600], - sliderFunction: "sqrt", + manager.verticalGroup( + { + header: strings.objects.general, }, - }), - manager.sectionHeader("Style"), - manager.mappingEditor("Fill", "fill", {}), - manager.mappingEditor("Stroke", "stroke", {}), + [ + manager.mappingEditor(strings.objects.rect.shape, "symbol", { + acceptKinds: [Specification.DataKind.Categorical], + hints: { rangeEnum: symbolTypes }, + defaultValue: "circle", + }), + manager.mappingEditor(strings.objects.size, "size", { + acceptKinds: [Specification.DataKind.Numerical], + hints: { rangeNumber: [0, 200 * Math.PI] }, + defaultValue: 60, + numberOptions: { + showSlider: true, + minimum: 0, + sliderRange: [0, 3600], + sliderFunction: "sqrt", + }, + }), + manager.mappingEditor( + strings.objects.visibleOn.visibility, + "visible", + { + defaultValue: true, + } + ), + ] + ), + manager.verticalGroup( + { + header: strings.objects.style, + }, + [ + manager.mappingEditor(strings.objects.fill, "fill", {}), + manager.mappingEditor(strings.objects.stroke, "stroke", {}), + this.object.mappings.stroke != null + ? manager.mappingEditor( + strings.objects.strokeWidth, + "strokeWidth", + { + hints: { rangeNumber: [0, 5] }, + defaultValue: 1, + numberOptions: { + showSlider: true, + sliderRange: [0, 5], + minimum: 0, + }, + } + ) + : null, + manager.mappingEditor(strings.objects.opacity, "opacity", { + hints: { rangeNumber: [0, 1] }, + defaultValue: 1, + numberOptions: { showSlider: true, minimum: 0, maximum: 1 }, + }), + ] + ), ]; - if (this.object.mappings.stroke != null) { - widgets.push( - manager.mappingEditor("Line Width", "strokeWidth", { - hints: { rangeNumber: [0, 5] }, - defaultValue: 1, - numberOptions: { showSlider: true, sliderRange: [0, 5], minimum: 0 }, - }) - ); - } - widgets = widgets.concat([ - manager.mappingEditor("Opacity", "opacity", { - hints: { rangeNumber: [0, 1] }, - defaultValue: 1, - numberOptions: { showSlider: true, minimum: 0, maximum: 1, step: 0.1 }, - }), - manager.mappingEditor("Visibility", "visible", { - defaultValue: true, - }), - ]); + widgets = widgets.concat([]); + return widgets.concat(parentWidgets); } diff --git a/src/core/prototypes/marks/text.ts b/src/core/prototypes/marks/text.ts index 53392f39..68ef55bb 100644 --- a/src/core/prototypes/marks/text.ts +++ b/src/core/prototypes/marks/text.ts @@ -1,7 +1,9 @@ +/* eslint-disable max-lines-per-function */ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. import { defaultFont, defaultFontSize } from "../../../app/stores/defaults"; +import { strings } from "../../../strings"; import { Point, replaceNewLineBySymbol, @@ -291,80 +293,111 @@ export class TextElementClass extends EmphasizableMarkClass< const parentWidgets = super.getAttributePanelWidgets(manager); const props = this.object.properties; return [ - manager.mappingEditor("Text", "text", {}), - manager.mappingEditor("Font", "fontFamily", { - defaultValue: defaultFont, - }), - manager.mappingEditor("Size", "fontSize", { - hints: { rangeNumber: [0, 36] }, - defaultValue: defaultFontSize, - numberOptions: { - showUpdown: true, - updownStyle: "font", - minimum: 0, - updownTick: 2, + manager.verticalGroup( + { + header: strings.objects.general, }, - }), - manager.sectionHeader("Anchor & Rotation"), - manager.inputSelect( - { property: "alignment", field: "x" }, - { - type: "radio", - icons: [ - "AlignHorizontalLeft", - "AlignHorizontalCenter", - "AlignHorizontalRight", - ], - labels: ["Left", "Middle", "Right"], - options: ["left", "middle", "right"], - label: "Anchor X", - } - ), - props.alignment.x != "middle" - ? manager.inputNumber( - { property: "alignment", field: "xMargin" }, - { - updownTick: 1, + [ + manager.mappingEditor(strings.toolbar.text, "text", {}), + manager.mappingEditor(strings.objects.font, "fontFamily", { + defaultValue: defaultFont, + }), + manager.mappingEditor(strings.objects.size, "fontSize", { + hints: { rangeNumber: [0, 36] }, + defaultValue: defaultFontSize, + numberOptions: { showUpdown: true, - label: "Margin", - } - ) - : null, - manager.inputSelect( - { property: "alignment", field: "y" }, - { - type: "radio", - icons: [ - "AlignVerticalTop", - "AlignVerticalCenter", - "AlignVerticalBottom", - ], - labels: ["Top", "Middle", "Bottom"], - options: ["top", "middle", "bottom"], - label: "Anchor Y", - } - ), - props.alignment.y != "middle" - ? manager.inputNumber( - { property: "alignment", field: "yMargin" }, + updownStyle: "font", + minimum: 0, + updownTick: 2, + }, + }), + manager.mappingEditor( + strings.objects.visibleOn.visibility, + "visible", { - updownTick: 1, - showUpdown: true, - label: "Margin", + defaultValue: true, } - ) - : null, - // manager.row("Rotation", manager.inputNumber({ property: "rotation" })), - manager.sectionHeader("Style"), - manager.mappingEditor("Color", "color", {}), - manager.mappingEditor("Outline", "outline", {}), - manager.mappingEditor("Opacity", "opacity", { - hints: { rangeNumber: [0, 1] }, - numberOptions: { showSlider: true, minimum: 0, maximum: 1, step: 0.1 }, - }), - manager.mappingEditor("Visibility", "visible", { - defaultValue: true, - }), + ), + ] + ), + manager.verticalGroup( + { + header: strings.objects.anchorAndRotation, + }, + [ + manager.inputSelect( + { property: "alignment", field: "x" }, + { + type: "radio", + icons: [ + "AlignHorizontalLeft", + "AlignHorizontalCenter", + "AlignHorizontalRight", + ], + labels: ["Left", "Middle", "Right"], + options: ["left", "middle", "right"], + label: strings.objects.anchorX, + } + ), + props.alignment.x != "middle" + ? manager.inputNumber( + { property: "alignment", field: "xMargin" }, + { + updownTick: 1, + showUpdown: true, + label: "Margin", + } + ) + : null, + manager.inputSelect( + { property: "alignment", field: "y" }, + { + type: "radio", + icons: [ + "AlignVerticalTop", + "AlignVerticalCenter", + "AlignVerticalBottom", + ], + labels: ["Top", "Middle", "Bottom"], + options: ["top", "middle", "bottom"], + label: strings.objects.anchorY, + } + ), + props.alignment.y != "middle" + ? manager.inputNumber( + { property: "alignment", field: "yMargin" }, + { + updownTick: 1, + showUpdown: true, + label: strings.objects.text.margin, + } + ) + : null, + manager.inputNumber( + { property: "rotation" }, + { + label: strings.objects.rotation, + showUpdown: true, + updownTick: 1, + } + ), + ] + ), + manager.verticalGroup( + { + header: "Style", + }, + [ + manager.mappingEditor(strings.objects.color, "color", {}), + manager.mappingEditor(strings.objects.outline, "outline", {}), + manager.mappingEditor(strings.objects.opacity, "opacity", { + hints: { rangeNumber: [0, 1] }, + defaultValue: 1, + numberOptions: { showSlider: true, minimum: 0, maximum: 1 }, + }), + ] + ), ].concat(parentWidgets); } diff --git a/src/core/prototypes/marks/textbox.ts b/src/core/prototypes/marks/textbox.ts index 88570251..30deeb95 100644 --- a/src/core/prototypes/marks/textbox.ts +++ b/src/core/prototypes/marks/textbox.ts @@ -2,6 +2,7 @@ // Licensed under the MIT license. import { defaultFont, defaultFontSize } from "../../../app/stores/defaults"; +import { strings } from "../../../strings"; import { Point, replaceNewLineBySymbol, @@ -109,36 +110,52 @@ export class TextboxElementClass extends EmphasizableMarkClass< const props = this.object.properties; const parentWidgets = super.getAttributePanelWidgets(manager); const widgets: Controls.Widget[] = [ - manager.sectionHeader("Size"), - manager.mappingEditor("Width", "width", { - hints: { autoRange: true, startWithZero: "always" }, - acceptKinds: [Specification.DataKind.Numerical], - defaultAuto: true, - }), - manager.mappingEditor("Height", "height", { - hints: { autoRange: true, startWithZero: "always" }, - acceptKinds: [Specification.DataKind.Numerical], - defaultAuto: true, - }), - manager.sectionHeader("Text"), - manager.mappingEditor("Text", "text", {}), - manager.mappingEditor("Font", "fontFamily", { - defaultValue: defaultFont, - }), - manager.mappingEditor("Size", "fontSize", { - hints: { rangeNumber: [0, 36] }, - defaultValue: defaultFontSize, - numberOptions: { - showUpdown: true, - updownStyle: "font", - minimum: 0, - updownTick: 2, + manager.verticalGroup( + { + header: strings.objects.general, }, - }), - manager.row( - "Align X", - manager.horizontal( - [0, 1], + [ + manager.mappingEditor("Width", "width", { + hints: { autoRange: true, startWithZero: "always" }, + acceptKinds: [Specification.DataKind.Numerical], + defaultAuto: true, + }), + manager.mappingEditor("Height", "height", { + hints: { autoRange: true, startWithZero: "always" }, + acceptKinds: [Specification.DataKind.Numerical], + defaultAuto: true, + }), + manager.mappingEditor("Visibility", "visible", { + defaultValue: true, + }), + ] + ), + manager.verticalGroup( + { + header: strings.toolbar.text, + }, + [ + manager.mappingEditor("Text", "text", {}), + manager.mappingEditor("Font", "fontFamily", { + defaultValue: defaultFont, + }), + manager.mappingEditor("Size", "fontSize", { + hints: { rangeNumber: [0, 36] }, + defaultValue: defaultFontSize, + numberOptions: { + showUpdown: true, + updownStyle: "font", + minimum: 0, + updownTick: 2, + }, + }), + ] + ), + manager.verticalGroup( + { + header: strings.objects.layout, + }, + [ manager.inputSelect( { property: "alignX" }, { @@ -150,6 +167,7 @@ export class TextboxElementClass extends EmphasizableMarkClass< "AlignHorizontalRight", ], labels: ["Left", "Middle", "Right"], + label: strings.objects.alignX, } ), props.alignX != "middle" @@ -161,16 +179,11 @@ export class TextboxElementClass extends EmphasizableMarkClass< { updownTick: 1, showUpdown: true, + label: strings.objects.text.margin, } ) ) - : null - ) - ), - manager.row( - "Align Y", - manager.horizontal( - [0, 1], + : null, manager.inputSelect( { property: "alignY" }, { @@ -182,6 +195,7 @@ export class TextboxElementClass extends EmphasizableMarkClass< "AlignVerticalTop", ], labels: ["Bottom", "Middle", "Top"], + label: strings.objects.alignX, } ), props.alignY != "middle" @@ -193,65 +207,68 @@ export class TextboxElementClass extends EmphasizableMarkClass< { updownTick: 1, showUpdown: true, + label: strings.objects.text.margin, } ) ) - : null - ) - ), - manager.sectionHeader("Layout"), - manager.row( - "Wrap text", - manager.inputBoolean( - { property: "wordWrap" }, - { - type: "checkbox", - } - ) - ), - props.wordWrap - ? manager.row( - "Alignment", - manager.horizontal( - [0, 1], - manager.inputSelect( - { property: "alignText" }, - { - type: "radio", - options: ["end", "middle", "start"], - icons: [ - "AlignVerticalBottom", - "AlignVerticalCenter", - "AlignVerticalTop", - ], - labels: ["Bottom", "Middle", "Top"], - } - ) - ) - ) - : null, - props.wordWrap - ? manager.row( - "Overflow", + : null, + manager.row( + "Wrap text", manager.inputBoolean( - { property: "overFlow" }, + { property: "wordWrap" }, { type: "checkbox", } ) - ) - : null, - manager.sectionHeader("Style"), - manager.mappingEditor("Color", "color", {}), - manager.mappingEditor("Outline", "outline", {}), - manager.mappingEditor("Opacity", "opacity", { - hints: { rangeNumber: [0, 1] }, - defaultValue: 1, - numberOptions: { showSlider: true, minimum: 0, maximum: 1, step: 0.1 }, - }), - manager.mappingEditor("Visibility", "visible", { - defaultValue: true, - }), + ), + props.wordWrap + ? manager.row( + "Alignment", + manager.horizontal( + [0, 1], + manager.inputSelect( + { property: "alignText" }, + { + type: "radio", + options: ["end", "middle", "start"], + icons: [ + "AlignVerticalBottom", + "AlignVerticalCenter", + "AlignVerticalTop", + ], + labels: ["Bottom", "Middle", "Top"], + } + ) + ) + ) + : null, + props.wordWrap + ? manager.row( + "Overflow", + manager.inputBoolean( + { property: "overFlow" }, + { + type: "checkbox", + } + ) + ) + : null, + ] + ), + manager.verticalGroup( + { + header: strings.objects.style, + }, + [ + manager.mappingEditor("Color", "color", {}), + manager.mappingEditor("Outline", "outline", {}), + manager.mappingEditor("Opacity", "opacity", { + hints: { rangeNumber: [0, 1] }, + defaultValue: 1, + numberOptions: { showSlider: true, minimum: 0, maximum: 1 }, + }), + ] + ), ]; return widgets.concat(parentWidgets); } diff --git a/src/core/prototypes/object.ts b/src/core/prototypes/object.ts index ec1e7fa9..c3f4f264 100644 --- a/src/core/prototypes/object.ts +++ b/src/core/prototypes/object.ts @@ -92,27 +92,33 @@ export abstract class ObjectClass< manager: Controls.WidgetManager ): Controls.Widget[] { return [ - manager.sectionHeader("Interactivity"), - manager.inputBoolean( - { property: "enableTooltips" }, + manager.verticalGroup( { - type: "checkbox", - label: "Tooltips", - } - ), - manager.inputBoolean( - { property: "enableContextMenu" }, - { - type: "checkbox", - label: "Context menu", - } - ), - manager.inputBoolean( - { property: "enableSelection" }, - { - type: "checkbox", - label: "Selection", - } + header: "Interactivity", + }, + [ + manager.inputBoolean( + { property: "enableTooltips" }, + { + type: "checkbox", + label: "Tooltips", + } + ), + manager.inputBoolean( + { property: "enableContextMenu" }, + { + type: "checkbox", + label: "Context menu", + } + ), + manager.inputBoolean( + { property: "enableSelection" }, + { + type: "checkbox", + label: "Selection", + } + ), + ] ), ]; } diff --git a/src/core/prototypes/plot_segments/axis.ts b/src/core/prototypes/plot_segments/axis.ts index f0deccb1..fab3a6ed 100644 --- a/src/core/prototypes/plot_segments/axis.ts +++ b/src/core/prototypes/plot_segments/axis.ts @@ -1,3 +1,4 @@ +/* eslint-disable max-lines-per-function */ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. @@ -960,85 +961,105 @@ export function getNumericalInterpolate( export function buildAxisAppearanceWidgets( isVisible: boolean, axisProperty: string, - m: Controls.WidgetManager + manager: Controls.WidgetManager ) { if (isVisible) { return [ - m.vertical( - m.inputBoolean( - { property: axisProperty, field: "visible" }, - { type: "checkbox", label: "Visible", headerLabel: "Appearance" } - ), - m.inputSelect( - { property: axisProperty, field: "side" }, + manager.vertical( + manager.verticalGroup( { - type: "dropdown", - showLabel: true, - label: "Position:", - options: ["default", "opposite"], - labels: ["Default", "Opposite"], - } - ), - m.sectionHeader("Axis Style"), - m.inputColor( - { - property: axisProperty, - field: ["style", "lineColor"], + header: strings.objects.appearance, }, - { - label: "Line Color", - } + [ + manager.inputBoolean( + { property: axisProperty, field: "visible" }, + { type: "checkbox", label: "Visible", headerLabel: "Appearance" } + ), + manager.inputSelect( + { property: axisProperty, field: "side" }, + { + type: "dropdown", + showLabel: true, + label: "Position:", + options: ["default", "opposite"], + labels: ["Default", "Opposite"], + } + ), + ] ), - m.inputColor( + manager.verticalGroup( { - property: axisProperty, - field: ["style", "tickColor"], + header: strings.objects.style, }, - { - label: "Tick Color", - } - ), - m.inputNumber( - { - property: axisProperty, - field: ["style", "tickSize"], - }, - { - label: "Tick Size", - } - ), - m.inputFontFamily( - { - property: axisProperty, - field: ["style", "fontFamily"], - }, - { - label: "Font Family", - } - ), - m.inputNumber( - { property: axisProperty, field: ["style", "fontSize"] }, - { - showUpdown: true, - updownStyle: "font", - updownTick: 2, - label: "Font Size", - } - ), - m.inputBoolean( - { property: axisProperty, field: ["style", "wordWrap"] }, - { - type: "checkbox", - headerLabel: "Text displaying", - label: "Wrap text", - } + [ + manager.inputColor( + { + property: axisProperty, + field: ["style", "lineColor"], + }, + { + label: "Line Color", + } + ), + manager.inputColor( + { + property: axisProperty, + field: ["style", "tickColor"], + }, + { + label: "Tick Color", + } + ), + manager.inputNumber( + { + property: axisProperty, + field: ["style", "tickSize"], + }, + { + label: "Tick Size", + } + ), + manager.inputFontFamily( + { + property: axisProperty, + field: ["style", "fontFamily"], + }, + { + label: "Font Family", + } + ), + manager.inputNumber( + { property: axisProperty, field: ["style", "fontSize"] }, + { + showUpdown: true, + updownStyle: "font", + updownTick: 2, + label: "Font Size", + } + ), + manager.inputBoolean( + { property: axisProperty, field: ["style", "wordWrap"] }, + { + type: "checkbox", + headerLabel: "Text displaying", + label: "Wrap text", + } + ), + ] ) ), ]; } else { - return m.inputBoolean( - { property: axisProperty, field: "visible" }, - { type: "checkbox", label: "Visible", headerLabel: "Appearance" } + return manager.verticalGroup( + { + header: strings.objects.appearance, + }, + [ + manager.inputBoolean( + { property: axisProperty, field: "visible" }, + { type: "checkbox", label: "Visible", headerLabel: "Appearance" } + ), + ] ); } } @@ -1047,7 +1068,7 @@ export function buildAxisAppearanceWidgets( export function buildAxisWidgets( data: Specification.Types.AxisDataBinding, axisProperty: string, - m: Controls.WidgetManager, + manager: Controls.WidgetManager, axisName: string ): Controls.Widget[] { const widgets = []; @@ -1059,99 +1080,93 @@ export function buildAxisWidgets( }, }; const makeAppearance = () => { - return buildAxisAppearanceWidgets(data.visible, axisProperty, m); + return buildAxisAppearanceWidgets(data.visible, axisProperty, manager); }; if (data != null) { switch (data.type) { case "numerical": { widgets.push( - m.sectionHeader( - axisName + ": Numerical", - m.clearButton({ property: axisProperty }, null, true), - dropzoneOptions - ) - ); - if (axisName != "Data Axis") { - widgets.push( - m.inputExpression( - { - property: axisProperty, - field: "expression", - }, - { - label: "Data", - } - ) - ); - } - if (data.valueType === "date") { - widgets.push(m.label("Range")); - widgets.push( - m.inputDate( - { property: axisProperty, field: "domainMin" }, - { label: "Start" } - ) - ); - widgets.push( - m.inputDate( - { property: axisProperty, field: "domainMax" }, - { label: "End" } - ) - ); - } else { - widgets.push( - m.vertical( - m.label("Range"), - m.horizontal( - [1, 0, 1], - m.inputNumber({ property: axisProperty, field: "domainMin" }), - m.label(" - ", { - addMargins: true, - }), - m.inputNumber({ property: axisProperty, field: "domainMax" }) - ) - ) - ); - } - if (data.numericalMode != "temporal") { - widgets.push( - m.inputSelect( - { property: axisProperty, field: "numericalMode" }, - { - options: ["linear", "logarithmic"], - labels: ["Linear", "Logarithmic"], - showLabel: true, - type: "dropdown", - label: "Mode", - } - ) - ); - } - widgets.push( - m.inputExpression( + manager.verticalGroup( { - property: axisProperty, - field: "tickDataExpression", + header: axisName + strings.objects.axes.numericalSuffix, }, - { - label: "Tick Data", - } - ) - ); - widgets.push( - m.inputFormat( - { - property: axisProperty, - field: "tickFormat", - }, - { - blank: strings.core.auto, - isDateField: - data.numericalMode === NumericalMode.Temporal || - data.valueType === DataType.Date, - label: "Tick Format", - } + [ + manager.sectionHeader( + axisName + strings.objects.axes.numericalSuffix, + manager.clearButton({ property: axisProperty }, null, true), + dropzoneOptions + ), + manager.inputExpression( + { + property: axisProperty, + field: "expression", + }, + { + label: "Data", + } + ), + data.valueType === "date" ? manager.label("Range") : null, + data.valueType === "date" + ? manager.inputDate( + { property: axisProperty, field: "domainMin" }, + { label: "Start" } + ) + : null, + data.valueType === "date" + ? manager.inputDate( + { property: axisProperty, field: "domainMax" }, + { label: "End" } + ) + : null, + data.valueType !== "date" ? manager.label("Range") : null, + data.valueType !== "date" + ? manager.inputNumber( + { property: axisProperty, field: "domainMin" }, + { label: strings.objects.axes.from } + ) + : null, + data.valueType !== "date" + ? manager.inputNumber( + { property: axisProperty, field: "domainMax" }, + { label: strings.objects.axes.to } + ) + : null, + data.numericalMode != "temporal" + ? manager.inputSelect( + { property: axisProperty, field: "numericalMode" }, + { + options: ["linear", "logarithmic"], + labels: ["Linear", "Logarithmic"], + showLabel: true, + type: "dropdown", + label: "Mode", + } + ) + : null, + manager.inputExpression( + { + property: axisProperty, + field: "tickDataExpression", + }, + { + label: "Tick Data", + } + ), + manager.inputFormat( + { + property: axisProperty, + field: "tickFormat", + }, + { + blank: strings.core.auto, + isDateField: + data.numericalMode === NumericalMode.Temporal || + data.valueType === DataType.Date, + label: strings.objects.axes.tickFormat, + } + ), + ] ) ); widgets.push(makeAppearance()); @@ -1160,66 +1175,67 @@ export function buildAxisWidgets( case "categorical": { widgets.push( - m.sectionHeader( - axisName + ": Categorical", - m.clearButton({ property: axisProperty }, null, true), - dropzoneOptions - ) - ); - widgets.push( - m.vertical( - m.label("Data"), - m.horizontal( - [1, 0], - m.inputExpression({ - property: axisProperty, - field: "expression", - }), - m.reorderWidget( - { property: axisProperty, field: "categories" }, - { allowReset: true } - ) - ) - ) - ); - if (data.valueType === "date") { - widgets.push( - m.inputExpression( - { - property: axisProperty, - field: "tickDataExpression", - }, - { - label: "Tick Data", - } - ) - ); - widgets.push( - m.inputFormat( - { - property: axisProperty, - field: "tickFormat", - }, - { - blank: strings.core.auto, - isDateField: - data.numericalMode === NumericalMode.Temporal || - data.valueType === DataType.Date, - label: "Tick Format", - } - ) - ); - } - widgets.push( - m.inputNumber( - { property: axisProperty, field: "gapRatio" }, + manager.verticalGroup( { - minimum: 0, - maximum: 1, - percentage: true, - showSlider: true, - label: "Gap", - } + header: axisName + ": Categorical", + }, + [ + manager.sectionHeader( + "Data", + manager.clearButton({ property: axisProperty }, null, true), + dropzoneOptions + ), + manager.vertical( + manager.label("Data"), + manager.horizontal( + [1, 0], + manager.inputExpression({ + property: axisProperty, + field: "expression", + }), + manager.reorderWidget( + { property: axisProperty, field: "categories" }, + { allowReset: true } + ) + ), + manager.inputNumber( + { property: axisProperty, field: "gapRatio" }, + { + minimum: 0, + maximum: 1, + percentage: true, + showSlider: true, + label: "Gap", + } + ), + data.valueType === "date" + ? (manager.inputExpression( + { + property: axisProperty, + field: "tickDataExpression", + }, + { + label: "Tick Data", + } + ), + manager.row( + "Tick Format", + manager.inputFormat( + { + property: axisProperty, + field: "tickFormat", + }, + { + blank: strings.core.auto, + isDateField: + data.numericalMode === NumericalMode.Temporal || + data.valueType === DataType.Date, + } + ) + )) + : null + ), + ] ) ); widgets.push(makeAppearance()); @@ -1228,57 +1244,64 @@ export function buildAxisWidgets( case "default": { widgets.push( - m.sectionHeader( - axisName + ": Stacking", - m.clearButton({ property: axisProperty }, null, true), - dropzoneOptions - ) - ); - widgets.push( - m.inputNumber( - { property: axisProperty, field: "gapRatio" }, + manager.verticalGroup( { - minimum: 0, - maximum: 1, - percentage: true, - showSlider: true, - label: "Gap", - } + header: axisName + ": Stacking", + }, + [ + manager.sectionHeader( + axisName + ": Stacking", + manager.clearButton({ property: axisProperty }, null, true), + dropzoneOptions + ), + manager.inputNumber( + { property: axisProperty, field: "gapRatio" }, + { + minimum: 0, + maximum: 1, + percentage: true, + showSlider: true, + label: "Gap", + } + ), + ] ) ); } break; } widgets.push( - m.sectionHeader(axisName + strings.objects.dataAxis.exportProperties) - ); - widgets.push( - m.inputBoolean( + manager.verticalGroup( { - property: axisProperty, - field: "autoDomainMin", + header: axisName + strings.objects.dataAxis.exportProperties, }, - { - type: "checkbox", - label: strings.objects.dataAxis.autoMin, - } - ) - ); - widgets.push( - m.inputBoolean( - { - property: axisProperty, - field: "autoDomainMax", - }, - { - type: "checkbox", - label: strings.objects.dataAxis.autoMax, - } + [ + manager.inputBoolean( + { + property: axisProperty, + field: "autoDomainMin", + }, + { + type: "checkbox", + label: strings.objects.dataAxis.autoMin, + } + ), + manager.inputBoolean( + { + property: axisProperty, + field: "autoDomainMax", + }, + { + type: "checkbox", + label: strings.objects.dataAxis.autoMax, + } + ), + ] ) ); } else { widgets.push( - m.sectionHeader( + manager.sectionHeader( axisName + ": " + strings.core.none, null, dropzoneOptions diff --git a/src/core/prototypes/plot_segments/plot_segment.ts b/src/core/prototypes/plot_segments/plot_segment.ts index 6692193d..df00084d 100644 --- a/src/core/prototypes/plot_segments/plot_segment.ts +++ b/src/core/prototypes/plot_segments/plot_segment.ts @@ -15,6 +15,7 @@ import { import { AxisRenderer } from "./axis"; import { utcFormat } from "d3-time-format"; import { NumericalMode } from "../../specification/types"; +import { strings } from "../../../strings"; export abstract class PlotSegmentClass< PropertiesType extends Specification.AttributeMap = Specification.AttributeMap, @@ -98,49 +99,49 @@ export abstract class PlotSegmentClass< return []; } return [ - manager.sectionHeader("Gridline"), - manager.horizontal( - [1, 1], - manager.inputSelect( - { property: axisProperty, field: ["style", "gridlineStyle"] }, - { - type: "dropdown", - showLabel: true, - icons: [ - "ChromeClose", - "stroke/solid", - "stroke/dashed", - "stroke/dotted", - ], - options: ["none", "solid", "dashed", "dotted"], - labels: ["None", "Solid", "Dashed", "Dotted"], - label: "Style", - } - ) - ), - manager.horizontal( - [1, 1], - manager.inputColor( - { - property: axisProperty, - field: ["style", "gridlineColor"], - }, - { - label: "Color", - } - ) - ), - manager.inputNumber( + manager.verticalGroup( { - property: axisProperty, - field: ["style", "gridlineWidth"], + header: strings.objects.plotSegment.gridline, }, - { - minimum: 0, - maximum: 100, - showUpdown: true, - label: "Width", - } + [ + manager.inputSelect( + { property: axisProperty, field: ["style", "gridlineStyle"] }, + { + type: "dropdown", + showLabel: true, + icons: [ + "ChromeClose", + "stroke/solid", + "stroke/dashed", + "stroke/dotted", + ], + options: ["none", "solid", "dashed", "dotted"], + labels: ["None", "Solid", "Dashed", "Dotted"], + label: strings.objects.style, + } + ), + manager.inputColor( + { + property: axisProperty, + field: ["style", "gridlineColor"], + }, + { + label: strings.objects.color, + } + ), + manager.inputNumber( + { + property: axisProperty, + field: ["style", "gridlineWidth"], + }, + { + minimum: 0, + maximum: 100, + showUpdown: true, + label: strings.objects.width, + } + ), + ] ), ]; } diff --git a/src/core/prototypes/plot_segments/region_2d/base.ts b/src/core/prototypes/plot_segments/region_2d/base.ts index c789a141..e0b993ad 100644 --- a/src/core/prototypes/plot_segments/region_2d/base.ts +++ b/src/core/prototypes/plot_segments/region_2d/base.ts @@ -2605,18 +2605,24 @@ export class Region2DConstraintBuilder { } const options = this.applicableSublayoutOptions(); return [ - m.sectionHeader("Sub-layout"), - m.inputSelect( - { property: "sublayout", field: "type" }, + m.verticalGroup( { - type: "radio", - options: options.map((x) => x.value), - icons: options.map((x) => x.icon), - labels: options.map((x) => x.label), - label: "Type", - } + header: strings.objects.plotSegment.subLayout, + }, + [ + m.inputSelect( + { property: "sublayout", field: "type" }, + { + type: "radio", + options: options.map((x) => x.value), + icons: options.map((x) => x.icon), + labels: options.map((x) => x.label), + label: strings.objects.plotSegment.type, + } + ), + ...extra, + ] ), - ...extra, ]; } diff --git a/src/core/prototypes/plot_segments/region_2d/curve.ts b/src/core/prototypes/plot_segments/region_2d/curve.ts index c6e74b33..b63b1e2e 100644 --- a/src/core/prototypes/plot_segments/region_2d/curve.ts +++ b/src/core/prototypes/plot_segments/region_2d/curve.ts @@ -582,22 +582,22 @@ export class CurvePlotSegment extends PlotSegmentClass< const builder = this.createBuilder(); return [ ...super.getAttributePanelWidgets(manager), - manager.sectionHeader("Curve Coordinates"), - manager.vertical( - manager.label("Normal"), - manager.horizontal( - [1, 0, 1], - manager.inputNumber({ property: "normalStart" }), - manager.label("-"), - manager.inputNumber({ property: "normalEnd" }) - ) + manager.verticalGroup( + { + header: strings.objects.plotSegment.curveCoordinates, + }, + [ + manager.vertical( + manager.label(strings.objects.plotSegment.normal), + manager.horizontal( + [1, 0, 1], + manager.inputNumber({ property: "normalStart" }), + manager.label("-"), + manager.inputNumber({ property: "normalEnd" }) + ) + ), + ] ), - // manager.row("Radius", manager.horizontal([0, 1, 0, 1], - // manager.label("Inner:"), - // manager.inputNumber({ property: "innerRatio" }), - // manager.label("Outer:"), - // manager.inputNumber({ property: "outerRatio" }) - // )), ...builder.buildPanelWidgets(manager), ]; } diff --git a/src/core/prototypes/plot_segments/region_2d/polar.ts b/src/core/prototypes/plot_segments/region_2d/polar.ts index 9120d896..7373fd24 100644 --- a/src/core/prototypes/plot_segments/region_2d/polar.ts +++ b/src/core/prototypes/plot_segments/region_2d/polar.ts @@ -773,33 +773,39 @@ export class PolarPlotSegment extends PlotSegmentClass< const builder = this.createBuilder(); return [ ...super.getAttributePanelWidgets(manager), - manager.sectionHeader("Polar Coordinates"), - manager.vertical( - manager.label("Angle"), - manager.horizontal( - [1, 0, 1], - manager.inputNumber({ property: "startAngle" }), - manager.label("-"), - manager.inputNumber({ property: "endAngle" }) - ) - ), - manager.vertical( - manager.label("Radius"), - manager.horizontal( - [0, 1, 0, 1], - manager.label("Inner:"), - manager.inputNumber({ property: "innerRatio" }), - manager.label("Outer:"), - manager.inputNumber({ property: "outerRatio" }) - ) - ), - manager.inputBoolean( - { property: "equalizeArea" }, + manager.verticalGroup( { - type: "checkbox", - label: "Height to Area", - headerLabel: "Equalize area", - } + header: strings.objects.plotSegment.polarCoordinates, + }, + [ + manager.vertical( + manager.label(strings.objects.plotSegment.angle), + manager.horizontal( + [1, 0, 1], + manager.inputNumber({ property: "startAngle" }), + manager.label("-"), + manager.inputNumber({ property: "endAngle" }) + ) + ), + manager.vertical( + manager.label(strings.objects.plotSegment.radius), + manager.horizontal( + [0, 1, 0, 1], + manager.label(strings.objects.plotSegment.inner), + manager.inputNumber({ property: "innerRatio" }), + manager.label(strings.objects.plotSegment.outer), + manager.inputNumber({ property: "outerRatio" }) + ) + ), + manager.inputBoolean( + { property: "equalizeArea" }, + { + type: "checkbox", + label: strings.objects.plotSegment.heightToArea, + headerLabel: strings.objects.plotSegment.equalizeArea, + } + ), + ] ), ...builder.buildPanelWidgets(manager), ]; diff --git a/src/fabric-icons/src/fabric-icons.tsx b/src/fabric-icons/src/fabric-icons.tsx index 6df853ba..79749b43 100644 --- a/src/fabric-icons/src/fabric-icons.tsx +++ b/src/fabric-icons/src/fabric-icons.tsx @@ -2462,7 +2462,7 @@ export function initializeIcons( "sublayout/jitter": ( ), + "sublayout/polar-grid": ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ), }, }; diff --git a/src/strings.ts b/src/strings.ts index 7ff56918..e2ec760e 100644 --- a/src/strings.ts +++ b/src/strings.ts @@ -328,10 +328,12 @@ export const strings = { bottom: "Bottom", }, objects: { + general: "General", contextMenu: "Context menu", interactivity: "Interactivity", colors: "Colors", color: "Color", + outline: "Outline", dimensions: "Dimensions", scale: "Scale", width: "Width", @@ -342,9 +344,45 @@ export const strings = { size: "Size", axis: "Axis", style: "Style", + rotation: "Rotation", + anchorAndRotation: "Anchor & Rotation", fill: "Fill", strokeWidth: "Line Width", stroke: "Stroke", + anchorX: "Anchor X", + anchorY: "Anchor Y", + alignX: "Align X", + alignY: "Align Y", + layout: "Layout", + appearance: "Appearance", + invalidFormat: "Invalid format", + axes: { + numericalSuffix: ": Numerical", + categoricalSuffix: ": Categorical", + tickFormat: "Tick Format", + from: "from", + to: "to", + gap: "Gap", + direction: "Direction", + count: "Count", + dataExpressions: "Data Expressions", + }, + plotSegment: { + subLayout: "Sub-layout", + type: "Type", + gridline: "Gridline", + polarCoordinates: "Polar Coordinates", + heightToArea: "Height to Area", + equalizeArea: "Equalize area", + inner: "Inner:", + outer: "Outer:", + radius: "Radius", + angle: "Angle", + curveCoordinates: "Curve Coordinates", + normal: "Normal", + groupBy: "Group by...", + groupByCategory: "Group by ", + }, visibleOn: { visibility: "Visibility", label: "Visible On", @@ -371,6 +409,7 @@ export const strings = { markerShape: "Shape", labels: "Labels", layout: "Layout", + categoricalLegend: "Categorical legend", }, links: { lineType: "Line Type", @@ -384,6 +423,9 @@ export const strings = { linkMarkType: "Line mark type", curveness: "Curveness", }, + line: { + lineStyle: "Line Style", + }, anchor: { label: "(drag the anchor in the glyph editor)", }, @@ -402,16 +444,13 @@ export const strings = { icon: { label: "Icon", image: "Image", - anchorAndRotation: "Anchor & Rotation", - anchorX: "Anchor X", - anchorY: "Anchor Y", }, image: { imageMode: "Resize Mode", letterbox: "Letterbox", stretch: "Stretch", dropImage: "Drop Image Here", - defaultPlaceholder: "Drop/Paste Image" + defaultPlaceholder: "Drop/Paste Image", }, scales: { mode: "Mode", @@ -420,5 +459,17 @@ export const strings = { interval: "Interval", inclusive: "Inclusive", }, + text: { + margin: "Margin", + }, + rect: { + shape: "Shape", + flipping: "Flipping", + shapes: { + rectangle: "Rectangle", + triangle: "Triangle", + ellipse: "Ellipse", + }, + }, }, };