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",
+ },
+ },
},
};