Compare commits

..

5 Commits

Author SHA1 Message Date
HarelM c7c1139e87 More typescript migration 2023-12-21 22:23:31 +02:00
HarelM ab102dccd7 Add a test to make sure this functionality doesn't get broken 2023-12-21 22:00:16 +02:00
HarelM 901be16ae4 Migrate more components, fix spec documentation missing information 2023-12-21 20:44:39 +02:00
HarelM df65cc3eff Migrate more components 2023-12-21 15:16:41 +02:00
HarelM c633368a9a Migrate more components 2023-12-21 14:53:21 +02:00
45 changed files with 530 additions and 481 deletions
+7
View File
@@ -77,11 +77,18 @@ describe("modals", () => {
});
it("name", () => {
when.click("field-doc-button-Name");
should.containText("spec-field-doc", "name for the style");
});
it("show name specifications", () => {
when.setValue(get.dataAttribute("modal:settings.name"), "foobar");
when.click("modal:settings.owner");
should.equalStyleStore((obj) => obj.name, "foobar");
});
it("owner", () => {
when.setValue(get.dataAttribute("modal:settings.owner"), "foobar");
when.click("modal:settings.name");
+39
View File
@@ -67,8 +67,10 @@
"@storybook/react": "^7.6.5",
"@storybook/react-vite": "^7.6.5",
"@storybook/theming": "^7.6.5",
"@types/codemirror": "^5.60.15",
"@types/color": "^3.0.6",
"@types/cors": "^2.8.17",
"@types/lodash.capitalize": "^4.2.9",
"@types/lodash.isequal": "^4.5.8",
"@types/lodash.throttle": "^4.1.9",
"@types/react": "^16.14.52",
@@ -76,6 +78,7 @@
"@types/react-autocomplete": "^1.8.9",
"@types/react-color": "^3.0.10",
"@types/react-dom": "^16.9.24",
"@types/react-file-reader-input": "^2.0.4",
"@types/react-icon-base": "^2.1.6",
"@types/uuid": "^9.0.7",
"@vitejs/plugin-react": "^4.2.0",
@@ -4588,6 +4591,15 @@
"@types/chai": "*"
}
},
"node_modules/@types/codemirror": {
"version": "5.60.15",
"resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.15.tgz",
"integrity": "sha512-dTOvwEQ+ouKJ/rE9LT1Ue2hmP6H1mZv5+CCnNWu2qtiOe2LQa9lCprEY20HxiDmV/Bxh+dXjywmy5aKvoGjULA==",
"dev": true,
"dependencies": {
"@types/tern": "*"
}
},
"node_modules/@types/color": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/@types/color/-/color-3.0.6.tgz",
@@ -4732,6 +4744,15 @@
"integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==",
"dev": true
},
"node_modules/@types/lodash.capitalize": {
"version": "4.2.9",
"resolved": "https://registry.npmjs.org/@types/lodash.capitalize/-/lodash.capitalize-4.2.9.tgz",
"integrity": "sha512-SV1dav/WbuI816SVig4trFz8ID/m5maVzC8I/E/DejPvmlXvIhw7Y0GWxJ03UhU6qaZOj6qQnR1xxC0mP7ZXoQ==",
"dev": true,
"dependencies": {
"@types/lodash": "*"
}
},
"node_modules/@types/lodash.isequal": {
"version": "4.5.8",
"resolved": "https://registry.npmjs.org/@types/lodash.isequal/-/lodash.isequal-4.5.8.tgz",
@@ -4897,6 +4918,15 @@
"@types/react": "^16"
}
},
"node_modules/@types/react-file-reader-input": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@types/react-file-reader-input/-/react-file-reader-input-2.0.4.tgz",
"integrity": "sha512-Jqrfn+w42j8t8Q3npMXXKPdk+reIM0UHLKVc3ykrA7q7bN3Z62SGhsClZX0+Edlqm66lcKwmDQl+WMm+Xor7Xg==",
"dev": true,
"dependencies": {
"@types/react": "*"
}
},
"node_modules/@types/react-icon-base": {
"version": "2.1.6",
"resolved": "https://registry.npmjs.org/@types/react-icon-base/-/react-icon-base-2.1.6.tgz",
@@ -4966,6 +4996,15 @@
"integrity": "sha512-0vWLNK2D5MT9dg0iOo8GlKguPAU02QjmZitPEsXRuJXU/OGIOt9vT9Fc26wtYuavLxtO45v9PGleoL9Z0k1LHg==",
"dev": true
},
"node_modules/@types/tern": {
"version": "0.23.9",
"resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.9.tgz",
"integrity": "sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==",
"dev": true,
"dependencies": {
"@types/estree": "*"
}
},
"node_modules/@types/uuid": {
"version": "9.0.7",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.7.tgz",
+3
View File
@@ -96,8 +96,10 @@
"@storybook/react": "^7.6.5",
"@storybook/react-vite": "^7.6.5",
"@storybook/theming": "^7.6.5",
"@types/codemirror": "^5.60.15",
"@types/color": "^3.0.6",
"@types/cors": "^2.8.17",
"@types/lodash.capitalize": "^4.2.9",
"@types/lodash.isequal": "^4.5.8",
"@types/lodash.throttle": "^4.1.9",
"@types/react": "^16.14.52",
@@ -105,6 +107,7 @@
"@types/react-autocomplete": "^1.8.9",
"@types/react-color": "^3.0.10",
"@types/react-dom": "^16.9.24",
"@types/react-file-reader-input": "^2.0.4",
"@types/react-icon-base": "^2.1.6",
"@types/uuid": "^9.0.7",
"@vitejs/plugin-react": "^4.2.0",
+1 -1
View File
@@ -39,7 +39,7 @@ export default class Doc extends React.Component<DocProps> {
<>
{doc &&
<div className="SpecDoc">
<div className="SpecDoc__doc">{doc}</div>
<div className="SpecDoc__doc" data-wd-key='spec-field-doc'>{doc}</div>
{renderValues &&
<ul className="SpecDoc__values">
{Object.entries(values).map(([key, value]) => {
-21
View File
@@ -1,21 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
import Block from './Block'
import InputArray from './InputArray'
import Fieldset from './Fieldset'
export default class FieldArray extends React.Component {
static propTypes = {
...InputArray.propTypes,
name: PropTypes.string,
}
render() {
const {props} = this;
return <Fieldset label={props.label}>
<InputArray {...props} />
</Fieldset>
}
}
+19
View File
@@ -0,0 +1,19 @@
import React from 'react'
import InputArray, { FieldArrayProps as InputArrayProps } from './InputArray'
import Fieldset from './Fieldset'
type FieldArrayProps = InputArrayProps & {
name?: string
fieldSpec?: {
doc: string
}
};
export default class FieldArray extends React.Component<FieldArrayProps> {
render() {
return <Fieldset label={this.props.label} fieldSpec={this.props.fieldSpec}>
<InputArray {...this.props} />
</Fieldset>
}
}
-20
View File
@@ -1,20 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
import Block from './Block'
import InputColor from './InputColor'
export default class FieldColor extends React.Component {
static propTypes = {
...InputColor.propTypes,
}
render() {
const {props} = this;
return <Block label={props.label}>
<InputColor {...props} />
</Block>
}
}
+21
View File
@@ -0,0 +1,21 @@
import React from 'react'
import Block from './Block'
import InputColor, {InputColorProps} from './InputColor'
type FieldColorProps = InputColorProps & {
label?: string
fieldSpec?: {
doc: string
}
};
export default class FieldColor extends React.Component<FieldColorProps> {
render() {
return <Block label={this.props.label} fieldSpec={this.props.fieldSpec}>
<InputColor {...this.props} />
</Block>
}
}
+1
View File
@@ -44,6 +44,7 @@ export default class FieldDocLabel extends React.Component<FieldDocLabelProps, F
aria-label={this.state.open ? "close property documentation" : "open property documentation"}
className={`maputnik-doc-button maputnik-doc-button--${this.state.open ? 'open' : 'closed'}`}
onClick={() => this.onToggleDoc(!this.state.open)}
data-wd-key={'field-doc-button-'+label}
>
{this.state.open ? <MdHighlightOff /> : <MdInfoOutline />}
</button>
+8 -5
View File
@@ -3,15 +3,18 @@ import InputEnum, {InputEnumProps} from './InputEnum'
import Fieldset from './Fieldset';
type FieldEnumProps = InputEnumProps & { label : string };
type FieldEnumProps = InputEnumProps & {
label?: string;
fieldSpec?: {
doc: string
}
};
export default class FieldEnum extends React.Component<FieldEnumProps> {
render() {
const {props} = this;
return <Fieldset label={props.label}>
<InputEnum {...props} />
return <Fieldset label={this.props.label} fieldSpec={this.props.fieldSpec}>
<InputEnum {...this.props} />
</Fieldset>
}
}
-16
View File
@@ -1,16 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
import InputJson from './InputJson'
export default class FieldJson extends React.Component {
static propTypes = {
...InputJson.propTypes,
}
render() {
const {props} = this;
return <InputJson {...props} />
}
}
+13
View File
@@ -0,0 +1,13 @@
import React from 'react'
import InputJson, {InputJsonProps} from './InputJson'
type FieldJsonProps = InputJsonProps & {};
export default class FieldJson extends React.Component<FieldJsonProps> {
render() {
return <InputJson {...this.props} />
}
}
@@ -1,17 +1,16 @@
import React from 'react'
import PropTypes from 'prop-types'
import {latest} from '@maplibre/maplibre-gl-style-spec'
import Block from './Block'
import InputNumber from './InputNumber'
export default class FieldMaxZoom extends React.Component {
static propTypes = {
value: PropTypes.number,
onChange: PropTypes.func.isRequired,
error: PropTypes.object,
}
type FieldMaxZoomProps = {
value?: number
onChange(...args: unknown[]): unknown
error?: unknown[]
};
export default class FieldMaxZoom extends React.Component<FieldMaxZoomProps> {
render() {
return <Block label={"Max Zoom"} fieldSpec={latest.layer.maxzoom}
error={this.props.error}
@@ -1,17 +1,16 @@
import React from 'react'
import PropTypes from 'prop-types'
import {latest} from '@maplibre/maplibre-gl-style-spec'
import Block from './Block'
import InputNumber from './InputNumber'
export default class FieldMinZoom extends React.Component {
static propTypes = {
value: PropTypes.number,
onChange: PropTypes.func.isRequired,
error: PropTypes.object,
}
type FieldMinZoomProps = {
value?: number
onChange(...args: unknown[]): unknown
error?: unknown[]
};
export default class FieldMinZoom extends React.Component<FieldMinZoomProps> {
render() {
return <Block label={"Min Zoom"} fieldSpec={latest.layer.minzoom}
error={this.props.error}
-21
View File
@@ -1,21 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
import Block from './Block'
import InputMultiInput from './InputMultiInput'
import Fieldset from './Fieldset'
export default class FieldMultiInput extends React.Component {
static propTypes = {
...InputMultiInput.propTypes,
}
render() {
const {props} = this;
return <Fieldset label={props.label}>
<InputMultiInput {...props} />
</Fieldset>
}
}
+20
View File
@@ -0,0 +1,20 @@
import React from 'react'
import InputMultiInput, {InputMultiInputProps} from './InputMultiInput'
import Fieldset from './Fieldset'
type FieldMultiInputProps = InputMultiInputProps & {
label?: string
};
export default class FieldMultiInput extends React.Component<FieldMultiInputProps> {
render() {
const {props} = this;
return <Fieldset label={props.label}>
<InputMultiInput {...props} />
</Fieldset>
}
}
-19
View File
@@ -1,19 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
import InputNumber from './InputNumber'
import Block from './Block'
export default class FieldNumber extends React.Component {
static propTypes = {
...InputNumber.propTypes,
}
render() {
const {props} = this;
return <Block label={props.label}>
<InputNumber {...props} />
</Block>
}
}
+20
View File
@@ -0,0 +1,20 @@
import React from 'react'
import InputNumber, {InputNumberProps} from './InputNumber'
import Block from './Block'
type FieldNumberProps = InputNumberProps & {
label?: string
fieldSpec?: {
doc: string
}
};
export default class FieldNumber extends React.Component<FieldNumberProps> {
render() {
return <Block label={this.props.label} fieldSpec={this.props.fieldSpec}>
<InputNumber {...this.props} />
</Block>
}
}
-20
View File
@@ -1,20 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
import Block from './Block'
import InputSelect from './InputSelect'
export default class FieldSelect extends React.Component {
static propTypes = {
...InputSelect.propTypes,
}
render() {
const {props} = this;
return <Block label={props.label}>
<InputSelect {...props}/>
</Block>
}
}
+22
View File
@@ -0,0 +1,22 @@
import React from 'react'
import Block from './Block'
import InputSelect, {InputSelectProps} from './InputSelect'
type FieldSelectProps = InputSelectProps & {
label?: string
fieldSpec?: {
doc: string
}
};
export default class FieldSelect extends React.Component<FieldSelectProps> {
render() {
return <Block label={this.props.label} fieldSpec={this.props.fieldSpec}>
<InputSelect {...this.props}/>
</Block>
}
}
+5 -4
View File
@@ -5,14 +5,15 @@ import InputString, {InputStringProps} from './InputString'
type FieldStringProps = InputStringProps & {
name?: string
label?: string
fieldSpec?: {
doc: string
}
};
export default class FieldString extends React.Component<FieldStringProps> {
render() {
const {props} = this;
return <Block label={props.label}>
<InputString {...props} />
return <Block label={this.props.label} fieldSpec={this.props.fieldSpec}>
<InputString {...this.props} />
</Block>
}
}
+5 -4
View File
@@ -5,16 +5,17 @@ import Block from './Block'
type FieldUrlProps = InputUrlProps & {
label: string;
fieldSpec?: {
doc: string
}
};
export default class FieldUrl extends React.Component<FieldUrlProps> {
render () {
const {props} = this;
return (
<Block label={this.props.label}>
<InputUrl {...props} />
<Block label={this.props.label} fieldSpec={this.props.fieldSpec}>
<InputUrl {...this.props} />
</Block>
);
}
+3 -3
View File
@@ -1,11 +1,11 @@
import React from 'react'
import React, { ReactElement } from 'react'
import FieldDocLabel from './FieldDocLabel'
import Doc from './Doc'
type FieldsetProps = {
label: string,
label?: string,
fieldSpec?: { doc?: string },
action?: string,
action?: ReactElement,
};
type FieldsetState = {
+2 -2
View File
@@ -2,8 +2,8 @@ import React from 'react'
import InputString from './InputString'
import InputNumber from './InputNumber'
type FieldArrayProps = {
value: string[]
export type FieldArrayProps = {
value: string[] | number[]
type?: string
length?: number
default?: string[] | number[]
+1 -1
View File
@@ -5,7 +5,7 @@ import Autocomplete from 'react-autocomplete'
const MAX_HEIGHT = 140;
type InputAutocompleteProps = {
export type InputAutocompleteProps = {
value?: string
options: any[]
onChange(...args: unknown[]): unknown
+1 -1
View File
@@ -1,6 +1,6 @@
import React from 'react'
type InputCheckboxProps = {
export type InputCheckboxProps = {
value?: boolean
style?: object
onChange(...args: unknown[]): unknown
+1 -1
View File
@@ -9,7 +9,7 @@ function formatColor(color: ColorResult): string {
return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${rgb.a})`
}
type InputColorProps = {
export type InputColorProps = {
onChange(...args: unknown[]): unknown
name?: string
value?: string
@@ -1,30 +1,34 @@
import React from 'react'
import PropTypes from 'prop-types'
import capitalize from 'lodash.capitalize'
import {MdDelete} from 'react-icons/md'
import InputString from './InputString'
import InputNumber from './InputNumber'
import InputButton from './InputButton'
import {MdDelete} from 'react-icons/md'
import FieldDocLabel from './FieldDocLabel'
import InputEnum from './InputEnum'
import capitalize from 'lodash.capitalize'
import InputUrl from './InputUrl'
export default class FieldDynamicArray extends React.Component {
static propTypes = {
value: PropTypes.array,
type: PropTypes.string,
default: PropTypes.array,
onChange: PropTypes.func,
style: PropTypes.object,
fieldSpec: PropTypes.object,
'aria-label': PropTypes.string,
export type FieldDynamicArrayProps = {
value?: (string | number)[]
type?: 'url' | 'number' | 'enum'
default?: (string | number)[]
onChange?(...args: unknown[]): unknown
style?: object
fieldSpec?: {
values?: any
}
'aria-label'?: string
label: string
};
changeValue(idx, newValue) {
export default class FieldDynamicArray extends React.Component<FieldDynamicArrayProps> {
changeValue(idx: number, newValue: string | number) {
const values = this.values.slice(0)
values[idx] = newValue
this.props.onChange(values)
if (this.props.onChange) this.props.onChange(values)
}
get values() {
@@ -41,20 +45,20 @@ export default class FieldDynamicArray extends React.Component {
}
else if (this.props.type === 'enum') {
const {fieldSpec} = this.props;
const defaultValue = Object.keys(fieldSpec.values)[0];
const defaultValue = Object.keys(fieldSpec!.values)[0];
values.push(defaultValue);
} else {
values.push("")
}
this.props.onChange(values)
if (this.props.onChange) this.props.onChange(values)
}
deleteValue(valueIdx) {
deleteValue(valueIdx: number) {
const values = this.values.slice(0)
values.splice(valueIdx, 1)
this.props.onChange(values.length > 0 ? values : undefined);
if (this.props.onChange) this.props.onChange(values.length > 0 ? values : undefined);
}
render() {
@@ -63,30 +67,30 @@ export default class FieldDynamicArray extends React.Component {
let input;
if(this.props.type === 'url') {
input = <InputUrl
value={v}
value={v as string}
onChange={this.changeValue.bind(this, i)}
aria-label={this.props['aria-label'] || this.props.label}
/>
}
else if (this.props.type === 'number') {
input = <InputNumber
value={v}
value={v as number}
onChange={this.changeValue.bind(this, i)}
aria-label={this.props['aria-label'] || this.props.label}
/>
}
else if (this.props.type === 'enum') {
const options = Object.keys(this.props.fieldSpec.values).map(v => [v, capitalize(v)]);
const options = Object.keys(this.props.fieldSpec?.values).map(v => [v, capitalize(v)]);
input = <InputEnum
options={options}
value={v}
value={v as string}
onChange={this.changeValue.bind(this, i)}
aria-label={this.props['aria-label'] || this.props.label}
/>
}
else {
input = <InputString
value={v}
value={v as string}
onChange={this.changeValue.bind(this, i)}
aria-label={this.props['aria-label'] || this.props.label}
/>
@@ -120,11 +124,11 @@ export default class FieldDynamicArray extends React.Component {
}
}
class DeleteValueInputButton extends React.Component {
static propTypes = {
onClick: PropTypes.func,
}
type DeleteValueInputButtonProps = {
onClick?(...args: unknown[]): unknown
};
class DeleteValueInputButton extends React.Component<DeleteValueInputButtonProps> {
render() {
return <InputButton
className="maputnik-delete-stop"
@@ -133,7 +137,7 @@ class DeleteValueInputButton extends React.Component {
>
<FieldDocLabel
label={<MdDelete />}
doc={"Remove array item."}
fieldSpec={{doc:" Remove array item."}}
/>
</InputButton>
}
+1 -1
View File
@@ -17,7 +17,7 @@ export type InputEnumProps = {
value?: string
style?: object
default?: string
name: string
name?: string
onChange(...args: unknown[]): unknown
options: any[]
'aria-label'?: string
+1 -1
View File
@@ -1,7 +1,7 @@
import React from 'react'
import InputAutocomplete from './InputAutocomplete'
type FieldFontProps = {
export type FieldFontProps = {
name: string
value?: string[]
default?: string[]
@@ -1,47 +1,45 @@
import React from 'react'
import PropTypes from 'prop-types'
import classnames from 'classnames';
import Block from './Block'
import FieldString from './FieldString'
import CodeMirror from 'codemirror';
import CodeMirror, { ModeSpec } from 'codemirror';
import 'codemirror/mode/javascript/javascript'
import 'codemirror/addon/lint/lint'
import 'codemirror/addon/edit/matchbrackets'
import 'codemirror/lib/codemirror.css'
import 'codemirror/addon/lint/lint.css'
import jsonlint from 'jsonlint'
import stringifyPretty from 'json-stringify-pretty-compact'
import '../util/codemirror-mgl';
export default class InputJson extends React.Component {
static propTypes = {
layer: PropTypes.any.isRequired,
maxHeight: PropTypes.number,
onChange: PropTypes.func,
lineNumbers: PropTypes.bool,
lineWrapping: PropTypes.bool,
getValue: PropTypes.func,
gutters: PropTypes.array,
className: PropTypes.string,
onFocus: PropTypes.func,
onBlur: PropTypes.func,
onJSONValid: PropTypes.func,
onJSONInvalid: PropTypes.func,
mode: PropTypes.object,
lint: PropTypes.oneOfType([
PropTypes.bool,
PropTypes.object,
]),
}
export type InputJsonProps = {
layer: any
maxHeight?: number
onChange?(...args: unknown[]): unknown
lineNumbers?: boolean
lineWrapping?: boolean
getValue(data: any): string
gutters?: string[]
className?: string
onFocus?(...args: unknown[]): unknown
onBlur?(...args: unknown[]): unknown
onJSONValid?(...args: unknown[]): unknown
onJSONInvalid?(...args: unknown[]): unknown
mode?: ModeSpec<any>
lint?: boolean | object
};
type InputJsonState = {
isEditing: boolean
showMessage: boolean
prevValue: string
};
export default class InputJson extends React.Component<InputJsonProps, InputJsonState> {
static defaultProps = {
lineNumbers: true,
lineWrapping: false,
gutters: ["CodeMirror-lint-markers"],
getValue: (data) => {
getValue: (data: any) => {
return stringifyPretty(data, {indent: 2, maxLength: 40});
},
onFocus: () => {},
@@ -49,8 +47,12 @@ export default class InputJson extends React.Component {
onJSONInvalid: () => {},
onJSONValid: () => {},
}
_keyEvent: string;
_doc: CodeMirror.Editor | undefined;
_el: HTMLDivElement | null = null;
_cancelNextChange: boolean = false;
constructor(props) {
constructor(props: InputJsonProps) {
super(props);
this._keyEvent = "keyboard";
this.state = {
@@ -61,7 +63,7 @@ export default class InputJson extends React.Component {
}
componentDidMount () {
this._doc = CodeMirror(this._el, {
this._doc = CodeMirror(this._el!, {
value: this.props.getValue(this.props.layer),
mode: this.props.mode || {
name: "mgl",
@@ -84,12 +86,12 @@ export default class InputJson extends React.Component {
this._doc.on('blur', this.onBlur);
}
onPointerDown = (cm, e) => {
onPointerDown = () => {
this._keyEvent = "pointer";
}
onFocus = (cm, e) => {
this.props.onFocus();
onFocus = () => {
if (this.props.onFocus) this.props.onFocus();
this.setState({
isEditing: true,
showMessage: (this._keyEvent === "keyboard"),
@@ -98,7 +100,7 @@ export default class InputJson extends React.Component {
onBlur = () => {
this._keyEvent = "keyboard";
this.props.onBlur();
if (this.props.onBlur) this.props.onBlur();
this.setState({
isEditing: false,
showMessage: false,
@@ -106,29 +108,29 @@ export default class InputJson extends React.Component {
}
componentWillUnMount () {
this._doc.off('change', this.onChange);
this._doc.off('focus', this.onFocus);
this._doc.off('blur', this.onBlur);
this._doc!.off('change', this.onChange);
this._doc!.off('focus', this.onFocus);
this._doc!.off('blur', this.onBlur);
}
componentDidUpdate(prevProps) {
componentDidUpdate(prevProps: InputJsonProps) {
if (!this.state.isEditing && prevProps.layer !== this.props.layer) {
this._cancelNextChange = true;
this._doc.setValue(
this._doc!.setValue(
this.props.getValue(this.props.layer),
)
}
}
onChange = (e) => {
onChange = (_e: unknown) => {
if (this._cancelNextChange) {
this._cancelNextChange = false;
this.setState({
prevValue: this._doc.getValue(),
prevValue: this._doc!.getValue(),
})
return;
}
const newCode = this._doc.getValue();
const newCode = this._doc!.getValue();
if (this.state.prevValue !== newCode) {
let parsedLayer, err;
@@ -139,12 +141,12 @@ export default class InputJson extends React.Component {
console.warn(_err)
}
if (err) {
if (err && this.props.onJSONInvalid) {
this.props.onJSONInvalid();
}
else {
this.props.onChange(parsedLayer)
this.props.onJSONValid();
if (this.props.onChange) this.props.onChange(parsedLayer)
if (this.props.onJSONValid) this.props.onJSONValid();
}
}
@@ -155,7 +157,7 @@ export default class InputJson extends React.Component {
render() {
const {showMessage} = this.state;
const style = {};
const style = {} as {maxHeight?: number};
if (this.props.maxHeight) {
style.maxHeight = this.props.maxHeight;
}
+2 -2
View File
@@ -1,8 +1,8 @@
import React from 'react'
import classnames from 'classnames'
type InputMultiInputProps = {
name: string
export type InputMultiInputProps = {
name?: string
value: string
options: any[]
onChange(...args: unknown[]): unknown
+1 -1
View File
@@ -2,7 +2,7 @@ import React, { BaseSyntheticEvent } from 'react'
let IDX = 0;
type InputNumberProps = {
export type InputNumberProps = {
value?: number
default?: number
min?: number
+1 -1
View File
@@ -1,6 +1,6 @@
import React from 'react'
type InputSelectProps = {
export type InputSelectProps = {
value: string
"data-wd-key"?: string
options: [string, any][] | string[]
-139
View File
@@ -1,139 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
import InputColor from './InputColor'
import InputNumber from './InputNumber'
import InputCheckbox from './InputCheckbox'
import InputString from './InputString'
import InputSelect from './InputSelect'
import InputMultiInput from './InputMultiInput'
import InputArray from './InputArray'
import InputDynamicArray from './InputDynamicArray'
import InputFont from './InputFont'
import InputAutocomplete from './InputAutocomplete'
import InputEnum from './InputEnum'
import capitalize from 'lodash.capitalize'
const iconProperties = ['background-pattern', 'fill-pattern', 'line-pattern', 'fill-extrusion-pattern', 'icon-image']
function labelFromFieldName(fieldName) {
let label = fieldName.split('-').slice(1).join(' ')
if(label.length > 0) {
label = label.charAt(0).toUpperCase() + label.slice(1);
}
return label
}
function optionsLabelLength(options) {
let sum = 0;
options.forEach(([_, label]) => {
sum += label.length
})
return sum
}
/** Display any field from the Maplibre GL style spec and
* choose the correct field component based on the @{fieldSpec}
* to display @{value}. */
export default class SpecField extends React.Component {
static propTypes = {
onChange: PropTypes.func.isRequired,
fieldName: PropTypes.string.isRequired,
fieldSpec: PropTypes.object.isRequired,
value: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.array,
PropTypes.bool
]),
/** Override the style of the field */
style: PropTypes.object,
'aria-label': PropTypes.string,
}
render() {
const commonProps = {
error: this.props.error,
fieldSpec: this.props.fieldSpec,
label: this.props.label,
action: this.props.action,
style: this.props.style,
value: this.props.value,
default: this.props.fieldSpec.default,
name: this.props.fieldName,
onChange: newValue => this.props.onChange(this.props.fieldName, newValue),
'aria-label': this.props['aria-label'],
}
function childNodes() {
switch(this.props.fieldSpec.type) {
case 'number': return (
<InputNumber
{...commonProps}
min={this.props.fieldSpec.minimum}
max={this.props.fieldSpec.maximum}
/>
)
case 'enum':
const options = Object.keys(this.props.fieldSpec.values).map(v => [v, capitalize(v)])
return <InputEnum
{...commonProps}
options={options}
/>
case 'resolvedImage':
case 'formatted':
case 'string':
if (iconProperties.indexOf(this.props.fieldName) >= 0) {
const options = this.props.fieldSpec.values || [];
return <InputAutocomplete
{...commonProps}
options={options.map(f => [f, f])}
/>
} else {
return <InputString
{...commonProps}
/>
}
case 'color': return (
<InputColor
{...commonProps}
/>
)
case 'boolean': return (
<InputCheckbox
{...commonProps}
/>
)
case 'array':
if(this.props.fieldName === 'text-font') {
return <InputFont
{...commonProps}
fonts={this.props.fieldSpec.values}
/>
} else {
if (this.props.fieldSpec.length) {
return <InputArray
{...commonProps}
type={this.props.fieldSpec.value}
length={this.props.fieldSpec.length}
/>
} else {
return <InputDynamicArray
{...commonProps}
fieldSpec={this.props.fieldSpec}
type={this.props.fieldSpec.value}
/>
}
}
default: return null
}
}
return (
<div data-wd-key={"spec-field:"+this.props.fieldName}>
{childNodes.call(this)}
</div>
);
}
}
+126
View File
@@ -0,0 +1,126 @@
import React, { ReactElement } from 'react'
import InputColor, { InputColorProps } from './InputColor'
import InputNumber, { InputNumberProps } from './InputNumber'
import InputCheckbox, { InputCheckboxProps } from './InputCheckbox'
import InputString, { InputStringProps } from './InputString'
import InputArray, { FieldArrayProps } from './InputArray'
import InputDynamicArray, { FieldDynamicArrayProps } from './InputDynamicArray'
import InputFont, { FieldFontProps } from './InputFont'
import InputAutocomplete, { InputAutocompleteProps } from './InputAutocomplete'
import InputEnum, { InputEnumProps } from './InputEnum'
import capitalize from 'lodash.capitalize'
const iconProperties = ['background-pattern', 'fill-pattern', 'line-pattern', 'fill-extrusion-pattern', 'icon-image']
export type SpecFieldProps = {
onChange(...args: unknown[]): unknown
fieldName: string
fieldSpec: {
default?: unknown
type: 'number' | 'enum' | 'resolvedImage' | 'formatted' | 'string' | 'color' | 'boolean' | 'array'
minimum?: number
maximum?: number
values?: unknown[]
length?: number
value?: string
}
value?: string | number | unknown[] | boolean
/** Override the style of the field */
style?: object
'aria-label'?: string
error: unknown[]
label: string
action: ReactElement
};
/** Display any field from the Maplibre GL style spec and
* choose the correct field component based on the @{fieldSpec}
* to display @{value}. */
export default class SpecField extends React.Component<SpecFieldProps> {
childNodes() {
const commonProps = {
error: this.props.error,
fieldSpec: this.props.fieldSpec,
label: this.props.label,
action: this.props.action,
style: this.props.style,
value: this.props.value,
default: this.props.fieldSpec.default,
name: this.props.fieldName,
onChange: (newValue: string) => this.props.onChange(this.props.fieldName, newValue),
'aria-label': this.props['aria-label'],
}
switch(this.props.fieldSpec.type) {
case 'number': return (
<InputNumber
{...commonProps as InputNumberProps}
min={this.props.fieldSpec.minimum}
max={this.props.fieldSpec.maximum}
/>
)
case 'enum':
const options = Object.keys(this.props.fieldSpec.values || []).map(v => [v, capitalize(v)])
return <InputEnum
{...commonProps as Omit<InputEnumProps, "options">}
options={options}
/>
case 'resolvedImage':
case 'formatted':
case 'string':
if (iconProperties.indexOf(this.props.fieldName) >= 0) {
const options = this.props.fieldSpec.values || [];
return <InputAutocomplete
{...commonProps as Omit<InputAutocompleteProps, "options">}
options={options.map(f => [f, f])}
/>
} else {
return <InputString
{...commonProps as InputStringProps}
/>
}
case 'color': return (
<InputColor
{...commonProps as InputColorProps}
/>
)
case 'boolean': return (
<InputCheckbox
{...commonProps as InputCheckboxProps}
/>
)
case 'array':
if(this.props.fieldName === 'text-font') {
return <InputFont
{...commonProps as FieldFontProps}
fonts={this.props.fieldSpec.values}
/>
} else {
if (this.props.fieldSpec.length) {
return <InputArray
{...commonProps as FieldArrayProps}
type={this.props.fieldSpec.value}
length={this.props.fieldSpec.length}
/>
} else {
return <InputDynamicArray
{...commonProps as FieldDynamicArrayProps}
fieldSpec={this.props.fieldSpec}
type={this.props.fieldSpec.value as FieldDynamicArrayProps['type']}
/>
}
}
default: return null
}
}
render() {
return (
<div data-wd-key={"spec-field:"+this.props.fieldName}>
{this.childNodes()}
</div>
);
}
}
+2 -2
View File
@@ -6,7 +6,7 @@ export type InputStringProps = {
style?: object
default?: string
onChange?(...args: unknown[]): unknown
onInput(...args: unknown[]): unknown
onInput?(...args: unknown[]): unknown
multi?: boolean
required?: boolean
disabled?: boolean
@@ -77,7 +77,7 @@ export default class InputString extends React.Component<InputStringProps, Input
editing: true,
value: e.target.value
}, () => {
this.props.onInput(this.state.value);
if (this.props.onInput) this.props.onInput(this.state.value);
});
},
onBlur: () => {
+4 -2
View File
@@ -53,10 +53,12 @@ export type FieldUrlProps = {
style?: object
default?: string
onChange(...args: unknown[]): unknown
onInput(...args: unknown[]): unknown
onInput?(...args: unknown[]): unknown
multi?: boolean
required?: boolean
'aria-label'?: string
type?: string
className?: string
};
type FieldUrlState = {
@@ -79,7 +81,7 @@ export default class FieldUrl extends React.Component<FieldUrlProps, FieldUrlSta
this.setState({
error: validate(url)
});
this.props.onInput(url);
if (this.props.onInput) this.props.onInput(url);
}
onChange = (url: string) => {
@@ -1,19 +1,19 @@
import React from 'react'
import PropTypes from 'prop-types'
import InputButton from './InputButton'
import Modal from './Modal'
export default class ModalLoading extends React.Component {
static propTypes = {
isOpen: PropTypes.bool.isRequired,
onCancel: PropTypes.func.isRequired,
title: PropTypes.string.isRequired,
message: PropTypes.node.isRequired,
}
type ModalLoadingProps = {
isOpen: boolean
onCancel(...args: unknown[]): unknown
title: string
message: React.ReactNode
};
underlayOnClick(e) {
export default class ModalLoading extends React.Component<ModalLoadingProps> {
underlayOnClick(e: Event) {
// This stops click events falling through to underlying modals.
e.stopPropagation();
}
@@ -24,9 +24,9 @@ export default class ModalLoading extends React.Component {
isOpen={this.props.isOpen}
underlayClickExits={false}
underlayProps={{
onClick: (e) => underlayProps(e)
// @ts-ignore
onClick: (e: Event) => underlayProps(e)
}}
closeable={false}
title={this.props.title}
onOpenToggle={() => this.props.onCancel()}
>
@@ -1,25 +1,24 @@
import React from 'react'
import PropTypes from 'prop-types'
import React, { FormEvent } from 'react'
import {MdFileUpload} from 'react-icons/md'
import {MdAddCircleOutline} from 'react-icons/md'
import FileReaderInput, { Result } from 'react-file-reader-input'
import ModalLoading from './ModalLoading'
import Modal from './Modal'
import InputButton from './InputButton'
import FileReaderInput from 'react-file-reader-input'
import InputUrl from './InputUrl'
import {MdFileUpload} from 'react-icons/md'
import {MdAddCircleOutline} from 'react-icons/md'
import style from '../libs/style'
import publicStyles from '../config/styles.json'
class PublicStyle extends React.Component {
static propTypes = {
url: PropTypes.string.isRequired,
thumbnailUrl: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
onSelect: PropTypes.func.isRequired,
}
type PublicStyleProps = {
url: string
thumbnailUrl: string
title: string
onSelect(...args: unknown[]): unknown
};
class PublicStyle extends React.Component<PublicStyleProps> {
render() {
return <div className="maputnik-public-style">
<InputButton
@@ -43,14 +42,21 @@ class PublicStyle extends React.Component {
}
}
export default class ModalOpen extends React.Component {
static propTypes = {
isOpen: PropTypes.bool.isRequired,
onOpenToggle: PropTypes.func.isRequired,
onStyleOpen: PropTypes.func.isRequired,
}
type ModalOpenProps = {
isOpen: boolean
onOpenToggle(...args: unknown[]): unknown
onStyleOpen(...args: unknown[]): unknown
};
constructor(props) {
type ModalOpenState = {
styleUrl: string
error?: string | null
activeRequest?: any
activeRequestUrl?: string | null
};
export default class ModalOpen extends React.Component<ModalOpenProps, ModalOpenState> {
constructor(props: ModalOpenProps) {
super(props);
this.state = {
styleUrl: ""
@@ -63,7 +69,7 @@ export default class ModalOpen extends React.Component {
})
}
onCancelActiveRequest(e) {
onCancelActiveRequest(e: Event) {
// Else the click propagates to the underlying modal
if(e) e.stopPropagation();
@@ -76,12 +82,12 @@ export default class ModalOpen extends React.Component {
}
}
onStyleSelect = (styleUrl) => {
onStyleSelect = (styleUrl: string) => {
this.clearError();
let canceled;
let canceled: boolean = false;
const activeRequest = fetch(styleUrl, {
fetch(styleUrl, {
mode: 'cors',
credentials: "same-origin"
})
@@ -123,13 +129,13 @@ export default class ModalOpen extends React.Component {
})
}
onSubmitUrl = (e) => {
onSubmitUrl = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
this.onStyleSelect(this.state.styleUrl);
}
onUpload = (_, files) => {
const [e, file] = files[0];
onUpload = (_: any, files: Result[]) => {
const [_e, file] = files[0];
const reader = new FileReader();
this.clearError();
@@ -138,11 +144,11 @@ export default class ModalOpen extends React.Component {
reader.onload = e => {
let mapStyle;
try {
mapStyle = JSON.parse(e.target.result)
mapStyle = JSON.parse(e.target?.result as string)
}
catch(err) {
this.setState({
error: err.toString()
error: (err as Error).toString()
});
return;
}
@@ -161,7 +167,7 @@ export default class ModalOpen extends React.Component {
this.props.onOpenToggle();
}
onChangeUrl = (url) => {
onChangeUrl = (url: string) => {
this.setState({
styleUrl: url,
});
@@ -200,7 +206,7 @@ export default class ModalOpen extends React.Component {
<section className="maputnik-modal-section">
<h1>Upload Style</h1>
<p>Upload a JSON style from your computer.</p>
<FileReaderInput onChange={this.onUpload} tabIndex="-1" aria-label="Style file">
<FileReaderInput onChange={this.onUpload} tabIndex={-1} aria-label="Style file">
<InputButton className="maputnik-upload-button"><MdFileUpload /> Upload</InputButton>
</FileReaderInput>
</section>
@@ -246,7 +252,7 @@ export default class ModalOpen extends React.Component {
<ModalLoading
isOpen={!!this.state.activeRequest}
title={'Loading style'}
onCancel={(e) => this.onCancelActiveRequest(e)}
onCancel={(e: Event) => this.onCancelActiveRequest(e)}
message={"Loading: "+this.state.activeRequestUrl}
/>
</div>
@@ -1,8 +1,6 @@
import React from 'react'
import PropTypes from 'prop-types'
import {LightSpecification, StyleSpecification, TransitionSpecification, latest} from '@maplibre/maplibre-gl-style-spec'
import {latest} from '@maplibre/maplibre-gl-style-spec'
import Block from './Block'
import FieldArray from './FieldArray'
import FieldNumber from './FieldNumber'
import FieldString from './FieldString'
@@ -13,16 +11,16 @@ import FieldColor from './FieldColor'
import Modal from './Modal'
import fieldSpecAdditional from '../libs/field-spec-additional'
export default class ModalSettings extends React.Component {
static propTypes = {
mapStyle: PropTypes.object.isRequired,
onStyleChanged: PropTypes.func.isRequired,
onChangeMetadataProperty: PropTypes.func.isRequired,
isOpen: PropTypes.bool.isRequired,
onOpenToggle: PropTypes.func.isRequired,
}
type ModalSettingsProps = {
mapStyle: StyleSpecification
onStyleChanged(...args: unknown[]): unknown
onChangeMetadataProperty(...args: unknown[]): unknown
isOpen: boolean
onOpenToggle(...args: unknown[]): unknown
};
changeTransitionProperty(property, value) {
export default class ModalSettings extends React.Component<ModalSettingsProps> {
changeTransitionProperty(property: keyof TransitionSpecification, value: number | undefined) {
const transition = {
...this.props.mapStyle.transition,
}
@@ -40,7 +38,7 @@ export default class ModalSettings extends React.Component {
});
}
changeLightProperty(property, value) {
changeLightProperty(property: keyof LightSpecification, value: any) {
const light = {
...this.props.mapStyle.light,
}
@@ -49,6 +47,7 @@ export default class ModalSettings extends React.Component {
delete light[property];
}
else {
// @ts-ignore
light[property] = value;
}
@@ -58,22 +57,24 @@ export default class ModalSettings extends React.Component {
});
}
changeStyleProperty(property, value) {
changeStyleProperty(property: keyof StyleSpecification | "owner", value: any) {
const changedStyle = {
...this.props.mapStyle,
};
if (value === undefined) {
// @ts-ignore
delete changedStyle[property];
}
else {
// @ts-ignore
changedStyle[property] = value;
}
this.props.onStyleChanged(changedStyle);
}
render() {
const metadata = this.props.mapStyle.metadata || {}
const metadata = this.props.mapStyle.metadata || {} as any;
const {onChangeMetadataProperty, mapStyle} = this.props;
const inputProps = { }
@@ -98,14 +99,14 @@ export default class ModalSettings extends React.Component {
label={"Owner"}
fieldSpec={{doc: "Owner ID of the style. Used by Mapbox or future style APIs."}}
data-wd-key="modal:settings.owner"
value={this.props.mapStyle.owner}
value={(this.props.mapStyle as any).owner}
onChange={this.changeStyleProperty.bind(this, "owner")}
/>
<FieldUrl {...inputProps}
fieldSpec={latest.$root.sprite}
label="Sprite URL"
data-wd-key="modal:settings.sprite"
value={this.props.mapStyle.sprite}
value={this.props.mapStyle.sprite as string}
onChange={this.changeStyleProperty.bind(this, "sprite")}
/>
@@ -113,7 +114,7 @@ export default class ModalSettings extends React.Component {
label="Glyphs URL"
fieldSpec={latest.$root.glyphs}
data-wd-key="modal:settings.glyphs"
value={this.props.mapStyle.glyphs}
value={this.props.mapStyle.glyphs as string}
onChange={this.changeStyleProperty.bind(this, "glyphs")}
/>
@@ -138,7 +139,7 @@ export default class ModalSettings extends React.Component {
fieldSpec={latest.$root.center}
length={2}
type="number"
value={mapStyle.center}
value={mapStyle.center || []}
default={latest.$root.center.default || [0, 0]}
onChange={this.changeStyleProperty.bind(this, "center")}
/>
@@ -175,7 +176,7 @@ export default class ModalSettings extends React.Component {
label={"Light anchor"}
fieldSpec={latest.light.anchor}
name="light-anchor"
value={light.anchor}
value={light.anchor as string}
options={Object.keys(latest.light.anchor.values)}
default={latest.light.anchor.default}
onChange={this.changeLightProperty.bind(this, "anchor")}
@@ -185,7 +186,7 @@ export default class ModalSettings extends React.Component {
{...inputProps}
label={"Light color"}
fieldSpec={latest.light.color}
value={light.color}
value={light.color as string}
default={latest.light.color.default}
onChange={this.changeLightProperty.bind(this, "color")}
/>
@@ -194,7 +195,7 @@ export default class ModalSettings extends React.Component {
{...inputProps}
label={"Light intensity"}
fieldSpec={latest.light.intensity}
value={light.intensity}
value={light.intensity as number}
default={latest.light.intensity.default}
onChange={this.changeLightProperty.bind(this, "intensity")}
/>
@@ -205,7 +206,7 @@ export default class ModalSettings extends React.Component {
fieldSpec={latest.light.position}
type="number"
length={latest.light.position.length}
value={light.position}
value={light.position as number[]}
default={latest.light.position.default}
onChange={this.changeLightProperty.bind(this, "position")}
/>
@@ -1,15 +1,15 @@
import React from 'react'
import PropTypes from 'prop-types'
import Modal from './Modal'
export default class ModalShortcuts extends React.Component {
static propTypes = {
isOpen: PropTypes.bool.isRequired,
onOpenToggle: PropTypes.func.isRequired,
}
type ModalShortcutsProps = {
isOpen: boolean
onOpenToggle(...args: unknown[]): unknown
};
export default class ModalShortcuts extends React.Component<ModalShortcutsProps> {
render() {
const help = [
{
@@ -1,17 +1,17 @@
import React from 'react'
import PropTypes from 'prop-types'
import InputButton from './InputButton'
import Modal from './Modal'
// @ts-ignore
import logoImage from 'maputnik-design/logos/logo-color.svg'
export default class ModalSurvey extends React.Component {
static propTypes = {
isOpen: PropTypes.bool.isRequired,
onOpenToggle: PropTypes.func.isRequired,
}
type ModalSurveyProps = {
isOpen: boolean
onOpenToggle(...args: unknown[]): unknown
};
export default class ModalSurvey extends React.Component<ModalSurveyProps> {
onClick = () => {
window.open('https://gregorywolanski.typeform.com/to/cPgaSY', '_blank');
@@ -1,18 +1,17 @@
import React from 'react'
import PropTypes from 'prop-types'
import {otherFilterOps} from '../libs/filterops'
import InputString from './InputString'
import InputAutocomplete from './InputAutocomplete'
import InputSelect from './InputSelect'
function tryParseInt(v) {
function tryParseInt(v: string | number) {
if (v === '') return v
if (isNaN(v)) return v
return parseFloat(v)
if (isNaN(v as number)) return v
return parseFloat(v as string)
}
function tryParseBool(v) {
function tryParseBool(v: string | boolean) {
const isString = (typeof(v) === "string");
if(!isString) {
return v;
@@ -29,24 +28,24 @@ function tryParseBool(v) {
}
}
function parseFilter(v) {
v = tryParseInt(v);
v = tryParseBool(v);
function parseFilter(v: string | boolean | number) {
v = tryParseInt(v as any);
v = tryParseBool(v as any);
return v;
}
export default class SingleFilterEditor extends React.Component {
static propTypes = {
filter: PropTypes.array.isRequired,
onChange: PropTypes.func.isRequired,
properties: PropTypes.object,
}
type SingleFilterEditorProps = {
filter: any[]
onChange(...args: unknown[]): unknown
properties?: {[key: string]: string}
};
export default class SingleFilterEditor extends React.Component<SingleFilterEditorProps> {
static defaultProps = {
properties: {},
}
onFilterPartChanged(filterOp, propertyName, filterArgs) {
onFilterPartChanged(filterOp: string, propertyName: string, filterArgs: string[]) {
let newFilter = [filterOp, propertyName, ...filterArgs.map(parseFilter)]
if(filterOp === 'has' || filterOp === '!has') {
newFilter = [filterOp, propertyName]
@@ -67,15 +66,15 @@ export default class SingleFilterEditor extends React.Component {
<InputAutocomplete
aria-label="key"
value={propertyName}
options={Object.keys(this.props.properties).map(propName => [propName, propName])}
onChange={newPropertyName => this.onFilterPartChanged(filterOp, newPropertyName, filterArgs)}
options={Object.keys(this.props.properties!).map(propName => [propName, propName])}
onChange={(newPropertyName: string) => this.onFilterPartChanged(filterOp, newPropertyName, filterArgs)}
/>
</div>
<div className="maputnik-filter-editor-operator">
<InputSelect
aria-label="function"
value={filterOp}
onChange={newFilterOp => this.onFilterPartChanged(newFilterOp, propertyName, filterArgs)}
onChange={(newFilterOp: string) => this.onFilterPartChanged(newFilterOp, propertyName, filterArgs)}
options={otherFilterOps}
/>
</div>
@@ -84,12 +83,11 @@ export default class SingleFilterEditor extends React.Component {
<InputString
aria-label="value"
value={filterArgs.join(',')}
onChange={ v=> this.onFilterPartChanged(filterOp, propertyName, v.split(','))}
onChange={(v: string) => this.onFilterPartChanged(filterOp, propertyName, v.split(','))}
/>
</div>
}
</div>
}
}
@@ -1,13 +1,12 @@
import React from 'react'
import PropTypes from 'prop-types'
import Block from './Block'
import InputSpec from './InputSpec'
import InputSpec, { SpecFieldProps as InputFieldSpecProps } from './InputSpec'
import Fieldset from './Fieldset'
const typeMap = {
color: () => Block,
enum: ({fieldSpec}) => (Object.keys(fieldSpec.values).length <= 3 ? Fieldset : Block),
enum: ({fieldSpec}: any) => (Object.keys(fieldSpec.values).length <= 3 ? Fieldset : Block),
boolean: () => Block,
array: () => Fieldset,
resolvedImage: () => Block,
@@ -16,12 +15,11 @@ const typeMap = {
formatted: () => Block,
};
export default class SpecField extends React.Component {
static propTypes = {
...InputSpec.propTypes,
name: PropTypes.string,
}
type SpecFieldProps = InputFieldSpecProps & {
name?: string
};
export default class SpecField extends React.Component<SpecFieldProps> {
render() {
const {props} = this;