Migration of jsx files to tsx 1 (#848)

In this PR I have changed some of the jsx files to tsx file.
I'm starting off with the "leafs" so that migration of the rest will be
easier, hopefully.

What I'm basically doing is taking a jsx file, copy paste it into:
https://mskelton.dev/ratchet

And after that I'm fixing the types as needed.
It's not a very long process.

Hopefully more PRs will follow and this will be over soon.
I don't plan to migrate the storybook as I generally don't understand
why is it useful, I'll open an issue to see if anyone thinks
differently.
This commit is contained in:
Harel M
2023-12-21 23:46:56 +02:00
committed by GitHub
parent 3bf0e510e6
commit fa182e66fa
64 changed files with 1009 additions and 859 deletions

View File

@@ -1,40 +1,43 @@
import React from 'react'
import PropTypes from 'prop-types'
import React, {SyntheticEvent} from 'react'
import classnames from 'classnames'
import FieldDocLabel from './FieldDocLabel'
import Doc from './Doc'
/** Wrap a component with a label */
export default class Block extends React.Component {
static propTypes = {
"data-wd-key": PropTypes.string,
label: PropTypes.oneOfType([
PropTypes.string,
PropTypes.element,
]),
action: PropTypes.element,
children: PropTypes.node.isRequired,
style: PropTypes.object,
onChange: PropTypes.func,
fieldSpec: PropTypes.object,
wideMode: PropTypes.bool,
error: PropTypes.array,
}
type BlockProps = {
"data-wd-key"?: string
label?: string
action?: React.ReactElement
style?: object
onChange?(...args: unknown[]): unknown
fieldSpec?: object
wideMode?: boolean
error?: unknown[]
};
constructor (props) {
type BlockState = {
showDoc: boolean
};
/** Wrap a component with a label */
export default class Block extends React.Component<BlockProps, BlockState> {
_blockEl: HTMLDivElement | null = null;
constructor (props: BlockProps) {
super(props);
this.state = {
showDoc: false,
}
}
onChange(e) {
onChange(e: React.BaseSyntheticEvent<Event, HTMLInputElement, HTMLInputElement>) {
const value = e.target.value
return this.props.onChange(value === "" ? undefined : value)
if (this.props.onChange) {
return this.props.onChange(value === "" ? undefined : value)
}
}
onToggleDoc = (val) => {
onToggleDoc = (val: boolean) => {
this.setState({
showDoc: val
});
@@ -46,20 +49,17 @@ export default class Block extends React.Component {
* causing the picker to reopen. This causes a scenario where the picker can
* never be closed once open.
*/
onLabelClick = (event) => {
onLabelClick = (event: SyntheticEvent<any, any>) => {
const el = event.nativeEvent.target;
const nativeEvent = event.nativeEvent;
const contains = this._blockEl.contains(el);
const contains = this._blockEl?.contains(el);
if (event.nativeEvent.target.nodeName !== "INPUT" && !contains) {
event.stopPropagation();
}
event.preventDefault();
event.preventDefault();
}
render() {
const errors = [].concat(this.props.error || []);
return <label style={this.props.style}
data-wd-key={this.props["data-wd-key"]}
className={classnames({

View File

@@ -1,24 +1,33 @@
import React from 'react'
import PropTypes from 'prop-types'
export default class Doc extends React.Component {
static propTypes = {
fieldSpec: PropTypes.object.isRequired,
const headers = {
js: "JS",
android: "Android",
ios: "iOS",
macos: "macOS",
};
type DocProps = {
fieldSpec: {
doc?: string
values?: {
[key: string]: {
doc?: string
}
}
'sdk-support'?: {
[key: string]: typeof headers
}
}
};
export default class Doc extends React.Component<DocProps> {
render () {
const {fieldSpec} = this.props;
const {doc, values} = fieldSpec;
const sdkSupport = fieldSpec['sdk-support'];
const headers = {
js: "JS",
android: "Android",
ios: "iOS",
macos: "macOS",
};
const renderValues = (
!!values &&
// HACK: Currently we merge additional values into the style spec, so this is required
@@ -30,7 +39,7 @@ export default class Doc extends React.Component {
<>
{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]) => {
@@ -61,10 +70,9 @@ export default class Doc extends React.Component {
return (
<tr key={key}>
<td>{key}</td>
{Object.keys(headers).map(k => {
const value = supportObj[k];
{Object.keys(headers).map((k) => {
if (supportObj.hasOwnProperty(k)) {
return <td key={k}>{supportObj[k]}</td>;
return <td key={k}>{supportObj[k as keyof typeof headers]}</td>;
}
else {
return <td key={k}>no</td>;
@@ -80,4 +88,4 @@ export default class Doc extends React.Component {
</>
);
}
}
}

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>
}
}

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>
}
}

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>
}
}

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>
}
}

View File

@@ -1,26 +1,27 @@
import React from 'react'
import PropTypes from 'prop-types'
import {MdInfoOutline, MdHighlightOff} from 'react-icons/md'
export default class FieldDocLabel extends React.Component {
static propTypes = {
label: PropTypes.oneOfType([
PropTypes.object,
PropTypes.string
]).isRequired,
fieldSpec: PropTypes.object,
onToggleDoc: PropTypes.func,
type FieldDocLabelProps = {
label: object | string | undefined
fieldSpec?: {
doc?: string
}
onToggleDoc?(...args: unknown[]): unknown
};
constructor (props) {
type FieldDocLabelState = {
open: boolean
};
export default class FieldDocLabel extends React.Component<FieldDocLabelProps, FieldDocLabelState> {
constructor (props: FieldDocLabelProps) {
super(props);
this.state = {
open: false,
}
}
onToggleDoc = (open) => {
onToggleDoc = (open: boolean) => {
this.setState({
open,
}, () => {
@@ -43,6 +44,7 @@ export default class FieldDocLabel extends React.Component {
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>

View File

@@ -1,20 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
import InputEnum from './InputEnum'
import Block from './Block';
import Fieldset from './Fieldset';
export default class FieldEnum extends React.Component {
static propTypes = {
...InputEnum.propTypes,
}
render() {
const {props} = this;
return <Fieldset label={props.label}>
<InputEnum {...props} />
</Fieldset>
}
}

View File

@@ -0,0 +1,20 @@
import React from 'react'
import InputEnum, {InputEnumProps} from './InputEnum'
import Fieldset from './Fieldset';
type FieldEnumProps = InputEnumProps & {
label?: string;
fieldSpec?: {
doc: string
}
};
export default class FieldEnum extends React.Component<FieldEnumProps> {
render() {
return <Fieldset label={this.props.label} fieldSpec={this.props.fieldSpec}>
<InputEnum {...this.props} />
</Fieldset>
}
}

View File

@@ -1,18 +1,17 @@
import React from 'react'
import PropTypes from 'prop-types'
import {latest} from '@maplibre/maplibre-gl-style-spec'
import Block from './Block'
import InputString from './InputString'
export default class FieldId extends React.Component {
static propTypes = {
value: PropTypes.string.isRequired,
wdKey: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
error: PropTypes.object,
}
type FieldIdProps = {
value: string
wdKey: string
onChange(...args: unknown[]): unknown
error?: unknown[]
};
export default class FieldId extends React.Component<FieldIdProps> {
render() {
return <Block label={"ID"} fieldSpec={latest.layer.id}
data-wd-key={this.props.wdKey}

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} />
}
}

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} />
}
}

View File

@@ -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}

View File

@@ -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}

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>
}
}

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>
}
}

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>
}
}

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>
}
}

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>
}
}

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>
}
}

View File

@@ -1,20 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
import Block from './Block'
import InputString from './InputString'
export default class FieldString extends React.Component {
static propTypes = {
...InputString.propTypes,
name: PropTypes.string,
}
render() {
const {props} = this;
return <Block label={props.label}>
<InputString {...props} />
</Block>
}
}

View File

@@ -0,0 +1,19 @@
import React from 'react'
import Block from './Block'
import InputString, {InputStringProps} from './InputString'
type FieldStringProps = InputStringProps & {
name?: string
label?: string
fieldSpec?: {
doc: string
}
};
export default class FieldString extends React.Component<FieldStringProps> {
render() {
return <Block label={this.props.label} fieldSpec={this.props.fieldSpec}>
<InputString {...this.props} />
</Block>
}
}

View File

@@ -1,20 +1,19 @@
import React from 'react'
import PropTypes from 'prop-types'
import {latest} from '@maplibre/maplibre-gl-style-spec'
import Block from './Block'
import InputSelect from './InputSelect'
import InputString from './InputString'
export default class FieldType extends React.Component {
static propTypes = {
value: PropTypes.string.isRequired,
wdKey: PropTypes.string,
onChange: PropTypes.func.isRequired,
error: PropTypes.object,
disabled: PropTypes.bool,
}
type FieldTypeProps = {
value: string
wdKey?: string
onChange(...args: unknown[]): unknown
error?: unknown[] | undefined
disabled?: boolean
};
export default class FieldType extends React.Component<FieldTypeProps> {
static defaultProps = {
disabled: false,
}

View File

@@ -1,22 +0,0 @@
import React, {Fragment} from 'react'
import PropTypes from 'prop-types'
import InputUrl from './InputUrl'
import Block from './Block'
export default class FieldUrl extends React.Component {
static propTypes = {
...InputUrl.propTypes,
}
render () {
const {props} = this;
return (
<Block label={this.props.label}>
<InputUrl {...props} />
</Block>
);
}
}

View File

@@ -0,0 +1,23 @@
import React from 'react'
import InputUrl, {FieldUrlProps as InputUrlProps} from './InputUrl'
import Block from './Block'
type FieldUrlProps = InputUrlProps & {
label: string;
fieldSpec?: {
doc: string
}
};
export default class FieldUrl extends React.Component<FieldUrlProps> {
render () {
return (
<Block label={this.props.label} fieldSpec={this.props.fieldSpec}>
<InputUrl {...this.props} />
</Block>
);
}
}

View File

@@ -1,13 +1,23 @@
import React from 'react'
import PropTypes from 'prop-types'
import React, { ReactElement } from 'react'
import FieldDocLabel from './FieldDocLabel'
import Doc from './Doc'
type FieldsetProps = {
label?: string,
fieldSpec?: { doc?: string },
action?: ReactElement,
};
type FieldsetState = {
showDoc: boolean
};
let IDX = 0;
export default class Fieldset extends React.Component {
constructor (props) {
export default class Fieldset extends React.Component<FieldsetProps, FieldsetState> {
_labelId: string;
constructor (props: FieldsetProps) {
super(props);
this._labelId = `fieldset_label_${(IDX++)}`;
this.state = {
@@ -15,15 +25,13 @@ export default class Fieldset extends React.Component {
}
}
onToggleDoc = (val) => {
onToggleDoc = (val: boolean) => {
this.setState({
showDoc: val
});
}
render () {
const {props} = this;
return <div className="maputnik-input-block" role="group" aria-labelledby={this._labelId}>
{this.props.fieldSpec &&
<div className="maputnik-input-block-label">
@@ -36,14 +44,14 @@ export default class Fieldset extends React.Component {
}
{!this.props.fieldSpec &&
<div className="maputnik-input-block-label">
{props.label}
{this.props.label}
</div>
}
<div className="maputnik-input-block-action">
{this.props.action}
</div>
<div className="maputnik-input-block-content">
{props.children}
{this.props.children}
</div>
{this.props.fieldSpec &&
<div

View File

@@ -1,14 +1,12 @@
import React from 'react'
import PropTypes from 'prop-types'
import InputButton from './InputButton'
import {MdDelete} from 'react-icons/md'
export default class FilterEditorBlock extends React.Component {
static propTypes = {
onDelete: PropTypes.func.isRequired,
children: PropTypes.element.isRequired,
}
type FilterEditorBlockProps = {
onDelete(...args: unknown[]): unknown
};
export default class FilterEditorBlock extends React.Component<FilterEditorBlockProps> {
render() {
return <div className="maputnik-filter-editor-block">
<div className="maputnik-filter-editor-block-action">

View File

@@ -1,19 +1,18 @@
import React from 'react'
import PropTypes from 'prop-types'
import IconLine from './IconLine.jsx'
import IconFill from './IconFill.jsx'
import IconSymbol from './IconSymbol.jsx'
import IconBackground from './IconBackground.jsx'
import IconCircle from './IconCircle.jsx'
import IconMissing from './IconMissing.jsx'
import IconLine from './IconLine'
import IconFill from './IconFill'
import IconSymbol from './IconSymbol'
import IconBackground from './IconBackground'
import IconCircle from './IconCircle'
import IconMissing from './IconMissing'
export default class IconLayer extends React.Component {
static propTypes = {
type: PropTypes.string.isRequired,
style: PropTypes.object,
}
type IconLayerProps = {
type: string
style?: object
};
export default class IconLayer extends React.Component<IconLayerProps> {
render() {
const iconProps = { style: this.props.style }
switch(this.props.type) {

View File

@@ -1,24 +1,29 @@
import React from 'react'
import PropTypes from 'prop-types'
import InputString from './InputString'
import InputNumber from './InputNumber'
export default class FieldArray extends React.Component {
static propTypes = {
value: PropTypes.array,
type: PropTypes.string,
length: PropTypes.number,
default: PropTypes.array,
onChange: PropTypes.func,
'aria-label': PropTypes.string,
}
export type FieldArrayProps = {
value: string[] | number[]
type?: string
length?: number
default?: string[] | number[]
onChange?(...args: unknown[]): unknown
'aria-label'?: string
label?: string
};
type FieldArrayState = {
value: string[] | number[]
initialPropsValue: unknown[]
}
export default class FieldArray extends React.Component<FieldArrayProps, FieldArrayState> {
static defaultProps = {
value: [],
default: [],
}
constructor (props) {
constructor (props: FieldArrayProps) {
super(props);
this.state = {
value: this.props.value.slice(0),
@@ -27,8 +32,8 @@ export default class FieldArray extends React.Component {
};
}
static getDerivedStateFromProps(props, state) {
const value = [];
static getDerivedStateFromProps(props: FieldArrayProps, state: FieldArrayState) {
const value: any[] = [];
const initialPropsValue = state.initialPropsValue.slice(0);
Array(props.length).fill(null).map((_, i) => {
@@ -47,24 +52,24 @@ export default class FieldArray extends React.Component {
};
}
isComplete (value) {
isComplete(value: unknown[]) {
return Array(this.props.length).fill(null).every((_, i) => {
const val = value[i]
return !(val === undefined || val === "");
});
}
changeValue(idx, newValue) {
changeValue(idx: number, newValue: string) {
const value = this.state.value.slice(0);
value[idx] = newValue;
this.setState({
value,
}, () => {
if (this.isComplete(value)) {
if (this.isComplete(value) && this.props.onChange) {
this.props.onChange(value);
}
else {
else if (this.props.onChange){
// Unset until complete
this.props.onChange(undefined);
}
@@ -85,8 +90,8 @@ export default class FieldArray extends React.Component {
if(this.props.type === 'number') {
return <InputNumber
key={i}
default={containsValues ? undefined : this.props.default[i]}
value={value[i]}
default={containsValues || !this.props.default ? undefined : this.props.default[i] as number}
value={value[i] as number}
required={containsValues ? true : false}
onChange={this.changeValue.bind(this, i)}
aria-label={this.props['aria-label'] || this.props.label}
@@ -94,8 +99,8 @@ export default class FieldArray extends React.Component {
} else {
return <InputString
key={i}
default={containsValues ? undefined : this.props.default[i]}
value={value[i]}
default={containsValues || !this.props.default ? undefined : this.props.default[i] as string}
value={value[i] as string}
required={containsValues ? true : false}
onChange={this.changeValue.bind(this, i)}
aria-label={this.props['aria-label'] || this.props.label}

View File

@@ -1,24 +1,25 @@
import React from 'react'
import PropTypes from 'prop-types'
import classnames from 'classnames'
import Autocomplete from 'react-autocomplete'
const MAX_HEIGHT = 140;
export default class InputAutocomplete extends React.Component {
static propTypes = {
value: PropTypes.string,
options: PropTypes.array,
onChange: PropTypes.func,
keepMenuWithinWindowBounds: PropTypes.bool,
'aria-label': PropTypes.string,
}
export type InputAutocompleteProps = {
value?: string
options: any[]
onChange(...args: unknown[]): unknown
keepMenuWithinWindowBounds?: boolean
'aria-label'?: string
};
export default class InputAutocomplete extends React.Component<InputAutocompleteProps> {
state = {
maxHeight: MAX_HEIGHT
}
autocompleteMenuEl: HTMLDivElement | null = null;
static defaultProps = {
onChange: () => {},
options: [],
@@ -26,7 +27,7 @@ export default class InputAutocomplete extends React.Component {
calcMaxHeight() {
if(this.props.keepMenuWithinWindowBounds) {
const maxHeight = window.innerHeight - this.autocompleteMenuEl.getBoundingClientRect().top;
const maxHeight = window.innerHeight - this.autocompleteMenuEl!.getBoundingClientRect().top;
const limitedMaxHeight = Math.min(maxHeight, MAX_HEIGHT);
if(limitedMaxHeight != this.state.maxHeight) {
@@ -45,7 +46,7 @@ export default class InputAutocomplete extends React.Component {
this.calcMaxHeight();
}
onChange (v) {
onChange(v: string) {
this.props.onChange(v === "" ? undefined : v);
}
@@ -64,7 +65,7 @@ export default class InputAutocomplete extends React.Component {
}}
wrapperProps={{
className: "maputnik-autocomplete",
style: null
style: {}
}}
inputProps={{
'aria-label': this.props['aria-label'],
@@ -75,11 +76,12 @@ export default class InputAutocomplete extends React.Component {
items={this.props.options}
getItemValue={(item) => item[0]}
onSelect={v => this.onChange(v)}
onChange={(e, v) => this.onChange(v)}
onChange={(_e, v) => this.onChange(v)}
shouldItemRender={(item, value="") => {
if (typeof(value) === "string") {
return item[0].toLowerCase().indexOf(value.toLowerCase()) > -1
}
return false
}}
renderItem={(item, isHighlighted) => (
<div

View File

@@ -1,21 +1,20 @@
import React from 'react'
import PropTypes from 'prop-types'
import classnames from 'classnames'
export default class InputButton extends React.Component {
static propTypes = {
"data-wd-key": PropTypes.string,
"aria-label": PropTypes.string,
onClick: PropTypes.func,
style: PropTypes.object,
className: PropTypes.string,
children: PropTypes.node,
disabled: PropTypes.bool,
type: PropTypes.string,
id: PropTypes.string,
title: PropTypes.string,
}
type InputButtonProps = {
"data-wd-key"?: string
"aria-label"?: string
onClick?(...args: unknown[]): unknown
style?: object
className?: string
children?: React.ReactNode
disabled?: boolean
type?: typeof HTMLButtonElement.prototype.type
id?: string
title?: string
};
export default class InputButton extends React.Component<InputButtonProps> {
render() {
return <button
id={this.props.id}
@@ -31,5 +30,4 @@ export default class InputButton extends React.Component {
{this.props.children}
</button>
}
}
}

View File

@@ -1,13 +1,12 @@
import React from 'react'
import PropTypes from 'prop-types'
export default class InputCheckbox extends React.Component {
static propTypes = {
value: PropTypes.bool,
style: PropTypes.object,
onChange: PropTypes.func,
}
export type InputCheckboxProps = {
value?: boolean
style?: object
onChange(...args: unknown[]): unknown
};
export default class InputCheckbox extends React.Component<InputCheckboxProps> {
static defaultProps = {
value: false,
}
@@ -35,5 +34,4 @@ export default class InputCheckbox extends React.Component {
</div>
</div>
}
}
}

View File

@@ -1,36 +1,37 @@
import React from 'react'
import Color from 'color'
import ChromePicker from 'react-color/lib/components/chrome/Chrome'
import PropTypes from 'prop-types'
import {ColorResult} from 'react-color';
import lodash from 'lodash';
function formatColor(color) {
function formatColor(color: ColorResult): string {
const rgb = color.rgb
return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${rgb.a})`
}
/*** Number fields with support for min, max and units and documentation*/
export default class InputColor extends React.Component {
static propTypes = {
onChange: PropTypes.func.isRequired,
name: PropTypes.string,
value: PropTypes.string,
doc: PropTypes.string,
style: PropTypes.object,
default: PropTypes.string,
'aria-label': PropTypes.string,
}
export type InputColorProps = {
onChange(...args: unknown[]): unknown
name?: string
value?: string
doc?: string
style?: object
default?: string
'aria-label'?: string
};
/*** Number fields with support for min, max and units and documentation*/
export default class InputColor extends React.Component<InputColorProps> {
state = {
pickerOpened: false
}
colorInput: HTMLInputElement | null = null;
constructor () {
super();
constructor (props: InputColorProps) {
super(props);
this.onChangeNoCheck = lodash.throttle(this.onChangeNoCheck, 1000/30);
}
onChangeNoCheck (v) {
onChangeNoCheck(v: string) {
this.props.onChange(v);
}
@@ -68,31 +69,31 @@ export default class InputColor extends React.Component {
}
}
onChange (v) {
onChange (v: string) {
this.props.onChange(v === "" ? undefined : v);
}
render() {
const offset = this.calcPickerOffset()
var currentColor = this.color.object()
currentColor = {
const currentColor = this.color.object();
const currentChromeColor = {
r: currentColor.r,
g: currentColor.g,
b: currentColor.b,
// Rename alpha -> a for ChromePicker
a: currentColor.alpha
a: currentColor.alpha!
}
const picker = <div
className="maputnik-color-picker-offset"
style={{
position: 'fixed',
zIndex: 1,
position: 'fixed',
zIndex: 1,
left: offset.left,
top: offset.top,
}}>
<ChromePicker
color={currentColor}
color={currentChromeColor}
onChange={c => this.onChangeNoCheck(formatColor(c))}
/>
<div

View File

@@ -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>
}

View File

@@ -1,10 +1,9 @@
import React from 'react'
import PropTypes from 'prop-types'
import InputSelect from './InputSelect'
import InputMultiInput from './InputMultiInput'
function optionsLabelLength(options) {
function optionsLabelLength(options: any[]) {
let sum = 0;
options.forEach(([_, label]) => {
sum += label.length
@@ -13,18 +12,20 @@ function optionsLabelLength(options) {
}
export default class InputEnum extends React.Component {
static propTypes = {
"data-wd-key": PropTypes.string,
value: PropTypes.string,
style: PropTypes.object,
default: PropTypes.string,
name: PropTypes.string,
onChange: PropTypes.func,
options: PropTypes.array,
'aria-label': PropTypes.string,
}
export type InputEnumProps = {
"data-wd-key"?: string
value?: string
style?: object
default?: string
name?: string
onChange(...args: unknown[]): unknown
options: any[]
'aria-label'?: string
label?: string
};
export default class InputEnum extends React.Component<InputEnumProps> {
render() {
const {options, value, onChange, name, label} = this.props;
@@ -32,14 +33,14 @@ export default class InputEnum extends React.Component {
return <InputMultiInput
name={name}
options={options}
value={value || this.props.default}
value={(value || this.props.default)!}
onChange={onChange}
aria-label={this.props['aria-label'] || label}
/>
} else {
return <InputSelect
options={options}
value={value || this.props.default}
value={(value || this.props.default)!}
onChange={onChange}
aria-label={this.props['aria-label'] || label}
/>

View File

@@ -1,17 +1,17 @@
import React from 'react'
import PropTypes from 'prop-types'
import InputAutocomplete from './InputAutocomplete'
export default class FieldFont extends React.Component {
static propTypes = {
value: PropTypes.array,
default: PropTypes.array,
fonts: PropTypes.array,
style: PropTypes.object,
onChange: PropTypes.func.isRequired,
'aria-label': PropTypes.string,
}
export type FieldFontProps = {
name: string
value?: string[]
default?: string[]
fonts?: unknown[]
style?: object
onChange(...args: unknown[]): unknown
'aria-label'?: string
};
export default class FieldFont extends React.Component<FieldFontProps> {
static defaultProps = {
fonts: []
}
@@ -28,7 +28,7 @@ export default class FieldFont extends React.Component {
}
}
changeFont(idx, newValue) {
changeFont(idx: number, newValue: string) {
const changedValues = this.values.slice(0)
changedValues[idx] = newValue
const filteredValues = changedValues
@@ -46,7 +46,7 @@ export default class FieldFont extends React.Component {
<InputAutocomplete
aria-label={this.props['aria-label'] || this.props.name}
value={value}
options={this.props.fonts.map(f => [f, f])}
options={this.props.fonts?.map(f => [f, f])}
onChange={this.changeFont.bind(this, i)}
/>
</li>

View File

@@ -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;
}

View File

@@ -1,16 +1,15 @@
import React from 'react'
import PropTypes from 'prop-types'
import classnames from 'classnames'
import InputButton from './InputButton'
export default class InputMultiInput extends React.Component {
static propTypes = {
name: PropTypes.string.isRequired,
value: PropTypes.string.isRequired,
options: PropTypes.array.isRequired,
onChange: PropTypes.func.isRequired,
}
export type InputMultiInputProps = {
name?: string
value: string
options: any[]
onChange(...args: unknown[]): unknown
'aria-label'?: string
};
export default class InputMultiInput extends React.Component<InputMultiInputProps> {
render() {
let options = this.props.options
if(options.length > 0 && !Array.isArray(options[0])) {
@@ -25,7 +24,7 @@ export default class InputMultiInput extends React.Component {
>
<input type="radio"
name={this.props.name}
onChange={e => this.props.onChange(val)}
onChange={_e => this.props.onChange(val)}
value={val}
checked={val === selectedValue}
/>

View File

@@ -1,27 +1,35 @@
import React from 'react'
import PropTypes from 'prop-types'
import React, { BaseSyntheticEvent } from 'react'
let IDX = 0;
export default class InputNumber extends React.Component {
static propTypes = {
value: PropTypes.number,
default: PropTypes.number,
min: PropTypes.number,
max: PropTypes.number,
onChange: PropTypes.func,
allowRange: PropTypes.bool,
rangeStep: PropTypes.number,
wdKey: PropTypes.string,
required: PropTypes.bool,
"aria-label": PropTypes.string,
}
export type InputNumberProps = {
value?: number
default?: number
min?: number
max?: number
onChange?(...args: unknown[]): unknown
allowRange?: boolean
rangeStep?: number
wdKey?: string
required?: boolean
"aria-label"?: string
};
type InputNumberState = {
uuid: number
editing: boolean
editingRange?: boolean
value?: number
dirtyValue?: number
}
export default class InputNumber extends React.Component<InputNumberProps, InputNumberState> {
static defaultProps = {
rangeStep: 1
}
_keyboardEvent: boolean = false;
constructor(props) {
constructor(props: InputNumberProps) {
super(props)
this.state = {
uuid: IDX++,
@@ -31,7 +39,7 @@ export default class InputNumber extends React.Component {
}
}
static getDerivedStateFromProps(props, state) {
static getDerivedStateFromProps(props: InputNumberProps, state: InputNumberState) {
if (!state.editing && props.value !== state.value) {
return {
value: props.value,
@@ -41,16 +49,15 @@ export default class InputNumber extends React.Component {
return null;
}
changeValue(newValue) {
changeValue(newValue: number | string | undefined) {
const value = (newValue === "" || newValue === undefined) ?
undefined :
parseFloat(newValue);
undefined : +newValue;
const hasChanged = this.props.value !== value;
if(this.isValid(value) && hasChanged) {
this.props.onChange(value)
if (this.props.onChange) this.props.onChange(value)
this.setState({
value: newValue,
value: value,
});
}
else if (!this.isValid(value) && hasChanged) {
@@ -60,25 +67,25 @@ export default class InputNumber extends React.Component {
}
this.setState({
dirtyValue: newValue === "" ? undefined : newValue,
dirtyValue: newValue === "" ? undefined : value,
})
}
isValid(v) {
isValid(v: number | string | undefined) {
if (v === undefined) {
return true;
}
const value = parseFloat(v)
const value = +v;
if(isNaN(value)) {
return false
}
if(!isNaN(this.props.min) && value < this.props.min) {
if(!isNaN(this.props.min!) && value < this.props.min!) {
return false
}
if(!isNaN(this.props.max) && value > this.props.max) {
if(!isNaN(this.props.max!) && value > this.props.max!) {
return false
}
@@ -88,7 +95,7 @@ export default class InputNumber extends React.Component {
resetValue = () => {
this.setState({editing: false});
// Reset explicitly to default value if value has been cleared
if(this.state.value === "") {
if(!this.state.value) {
return;
}
@@ -104,8 +111,8 @@ export default class InputNumber extends React.Component {
}
}
onChangeRange = (e) => {
let value = parseFloat(e.target.value, 10);
onChangeRange = (e: BaseSyntheticEvent<Event, HTMLInputElement, HTMLInputElement>) => {
let value = parseFloat(e.target.value);
const step = this.props.rangeStep;
let dirtyValue = value;
@@ -119,11 +126,11 @@ export default class InputNumber extends React.Component {
// for example we might go from 13 to 13.23, however because we know
// that came from a keyboard event we always want to increase by a
// single step value.
if (value < this.state.dirtyValue) {
value = this.state.value - step;
if (value < this.state.dirtyValue!) {
value = this.state.value! - step;
}
else {
value = this.state.value + step
value = this.state.value! + step
}
dirtyValue = value;
}
@@ -140,10 +147,10 @@ export default class InputNumber extends React.Component {
this._keyboardEvent = false;
// Clamp between min/max
value = Math.max(this.props.min, Math.min(this.props.max, value));
value = Math.max(this.props.min!, Math.min(this.props.max!, value));
this.setState({value, dirtyValue});
this.props.onChange(value);
if (this.props.onChange) this.props.onChange(value);
}
render() {
@@ -196,15 +203,15 @@ export default class InputNumber extends React.Component {
type="text"
spellCheck="false"
className="maputnik-number"
placeholder={this.props.default}
placeholder={this.props.default?.toString()}
value={inputValue === undefined ? "" : inputValue}
onFocus={e => {
onFocus={_e => {
this.setState({editing: true});
}}
onChange={e => {
this.changeValue(e.target.value);
}}
onBlur={e => {
onBlur={_e => {
this.setState({editing: false});
this.resetValue()
}}
@@ -218,7 +225,7 @@ export default class InputNumber extends React.Component {
aria-label={this.props['aria-label']}
spellCheck="false"
className="maputnik-number"
placeholder={this.props.default}
placeholder={this.props.default?.toString()}
value={value === undefined ? "" : value}
onChange={e => this.changeValue(e.target.value)}
onFocus={() => {

View File

@@ -1,22 +1,20 @@
import React from 'react'
import PropTypes from 'prop-types'
export default class InputSelect extends React.Component {
static propTypes = {
value: PropTypes.string.isRequired,
"data-wd-key": PropTypes.string,
options: PropTypes.array.isRequired,
style: PropTypes.object,
onChange: PropTypes.func.isRequired,
title: PropTypes.string,
'aria-label': PropTypes.string,
}
export type InputSelectProps = {
value: string
"data-wd-key"?: string
options: [string, any][] | string[]
style?: object
onChange(...args: unknown[]): unknown
title?: string
'aria-label'?: string
};
export default class InputSelect extends React.Component<InputSelectProps> {
render() {
let options = this.props.options
let options = this.props.options;
if(options.length > 0 && !Array.isArray(options[0])) {
options = options.map(v => [v, v])
options = options.map((v) => [v, v]) as [string, any][];
}
return <select

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>
);
}
}

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>
);
}
}

View File

@@ -1,26 +1,30 @@
import React from 'react'
import PropTypes from 'prop-types'
export default class InputString extends React.Component {
static propTypes = {
"data-wd-key": PropTypes.string,
value: PropTypes.string,
style: PropTypes.object,
default: PropTypes.string,
onChange: PropTypes.func,
onInput: PropTypes.func,
multi: PropTypes.bool,
required: PropTypes.bool,
disabled: PropTypes.bool,
spellCheck: PropTypes.bool,
'aria-label': PropTypes.string,
}
export type InputStringProps = {
"data-wd-key"?: string
value?: string
style?: object
default?: string
onChange?(...args: unknown[]): unknown
onInput?(...args: unknown[]): unknown
multi?: boolean
required?: boolean
disabled?: boolean
spellCheck?: boolean
'aria-label'?: string
};
type InputStringState = {
editing: boolean
value?: string
}
export default class InputString extends React.Component<InputStringProps, InputStringState> {
static defaultProps = {
onInput: () => {},
}
constructor(props) {
constructor(props: InputStringProps) {
super(props)
this.state = {
editing: false,
@@ -28,7 +32,7 @@ export default class InputString extends React.Component {
}
}
static getDerivedStateFromProps(props, state) {
static getDerivedStateFromProps(props: InputStringProps, state: InputStringState) {
if (!state.editing) {
return {
value: props.value
@@ -68,22 +72,22 @@ export default class InputString extends React.Component {
style: this.props.style,
value: this.state.value === undefined ? "" : this.state.value,
placeholder: this.props.default,
onChange: e => {
onChange: (e: React.BaseSyntheticEvent<Event, HTMLInputElement, HTMLInputElement>) => {
this.setState({
editing: true,
value: e.target.value
}, () => {
this.props.onInput(this.state.value);
if (this.props.onInput) this.props.onInput(this.state.value);
});
},
onBlur: () => {
if(this.state.value!==this.props.value) {
this.setState({editing: false});
this.props.onChange(this.state.value);
if (this.props.onChange) this.props.onChange(this.state.value);
}
},
onKeyDown: (e) => {
if (e.keyCode === 13) {
if (e.keyCode === 13 && this.props.onChange) {
this.props.onChange(this.state.value);
}
},

View File

@@ -1,16 +1,15 @@
import React, {Fragment} from 'react'
import PropTypes from 'prop-types'
import React from 'react'
import InputString from './InputString'
import SmallError from './SmallError'
function validate (url) {
function validate(url: string) {
if (url === "") {
return;
}
let error;
const getProtocol = (url) => {
const getProtocol = (url: string) => {
try {
const urlObj = new URL(url);
return urlObj.protocol;
@@ -48,38 +47,44 @@ function validate (url) {
return error;
}
export default class FieldUrl extends React.Component {
static propTypes = {
"data-wd-key": PropTypes.string,
value: PropTypes.string,
style: PropTypes.object,
default: PropTypes.string,
onChange: PropTypes.func,
onInput: PropTypes.func,
multi: PropTypes.bool,
required: PropTypes.bool,
'aria-label': PropTypes.string,
}
export type FieldUrlProps = {
"data-wd-key"?: string
value: string
style?: object
default?: string
onChange(...args: unknown[]): unknown
onInput?(...args: unknown[]): unknown
multi?: boolean
required?: boolean
'aria-label'?: string
type?: string
className?: string
};
type FieldUrlState = {
error?: React.ReactNode
}
export default class FieldUrl extends React.Component<FieldUrlProps, FieldUrlState> {
static defaultProps = {
onInput: () => {},
}
constructor (props) {
constructor (props: FieldUrlProps) {
super(props);
this.state = {
error: validate(props.value)
};
}
onInput = (url) => {
onInput = (url: string) => {
this.setState({
error: validate(url)
});
this.props.onInput(url);
if (this.props.onInput) this.props.onInput(url);
}
onChange = (url) => {
onChange = (url: string) => {
this.setState({
error: validate(url)
});

View File

@@ -1,22 +1,21 @@
import React from 'react'
import PropTypes from 'prop-types'
import {MdClose} from 'react-icons/md'
import AriaModal from 'react-aria-modal'
import classnames from 'classnames';
export default class Modal extends React.Component {
static propTypes = {
"data-wd-key": PropTypes.string,
isOpen: PropTypes.bool.isRequired,
title: PropTypes.string.isRequired,
onOpenToggle: PropTypes.func.isRequired,
children: PropTypes.node,
underlayClickExits: PropTypes.bool,
underlayProps: PropTypes.object,
className: PropTypes.string,
}
type ModalProps = {
"data-wd-key"?: string
isOpen: boolean
title: string
onOpenToggle(...args: unknown[]): unknown
underlayClickExits?: boolean
underlayProps?: any
className?: string
};
export default class Modal extends React.Component<ModalProps> {
static defaultProps = {
underlayClickExits: true
}
@@ -24,7 +23,7 @@ export default class Modal extends React.Component {
// See <https://github.com/maputnik/editor/issues/416>
onClose = () => {
if (document.activeElement) {
document.activeElement.blur();
(document.activeElement as HTMLElement).blur();
}
setTimeout(() => {
@@ -37,6 +36,7 @@ export default class Modal extends React.Component {
return <AriaModal
titleText={this.props.title}
underlayClickExits={this.props.underlayClickExits}
// @ts-ignore
underlayProps={this.props.underlayProps}
data-wd-key={this.props["data-wd-key"]}
verticallyCenter={true}

View File

@@ -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()}
>

View File

@@ -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>

View File

@@ -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")}
/>

View File

@@ -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 = [
{

View File

@@ -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');

View File

@@ -1,15 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
export default class ScrollContainer extends React.Component {
static propTypes = {
children: PropTypes.node
}
render() {
return <div className="maputnik-scroll-container">
{this.props.children}
</div>
}
}

View File

@@ -0,0 +1,14 @@
import React from 'react'
type ScrollContainerProps = {
children?: React.ReactNode
};
export default class ScrollContainer extends React.Component<ScrollContainerProps> {
render() {
return <div className="maputnik-scroll-container">
{this.props.children}
</div>
}
}

View File

@@ -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>
}
}

View File

@@ -1,13 +1,13 @@
import React from 'react'
import PropTypes from 'prop-types'
import './SmallError.scss';
export default class SmallError extends React.Component {
static propTypes = {
children: PropTypes.node,
}
type SmallErrorProps = {
children?: React.ReactNode
};
export default class SmallError extends React.Component<SmallErrorProps> {
render () {
return (
<div className="SmallError">
@@ -15,5 +15,4 @@ export default class SmallError extends React.Component {
</div>
);
}
}
}

View File

@@ -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;