mirror of
https://github.com/maputnik/editor.git
synced 2026-09-01 01:47:26 +00:00
Compare commits
6 Commits
1318dd279e
...
a192b04135
| Author | SHA1 | Date | |
|---|---|---|---|
| a192b04135 | |||
| a36f56fe93 | |||
| 1a7754c78d | |||
| de777102e1 | |||
| 6c9f0fefc9 | |||
| 6af3eeefb6 |
@@ -2,16 +2,16 @@ import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import ScrollContainer from './ScrollContainer'
|
||||
|
||||
class AppLayout extends React.Component {
|
||||
static propTypes = {
|
||||
toolbar: PropTypes.element.isRequired,
|
||||
layerList: PropTypes.element.isRequired,
|
||||
layerEditor: PropTypes.element,
|
||||
map: PropTypes.element.isRequired,
|
||||
bottom: PropTypes.element,
|
||||
modals: PropTypes.node,
|
||||
}
|
||||
type AppLayoutProps = {
|
||||
toolbar: React.ReactElement
|
||||
layerList: React.ReactElement
|
||||
layerEditor?: React.ReactElement
|
||||
map: React.ReactElement
|
||||
bottom?: React.ReactElement
|
||||
modals?: React.ReactNode
|
||||
};
|
||||
|
||||
class AppLayout extends React.Component<AppLayoutProps> {
|
||||
static childContextTypes = {
|
||||
reactIconBase: PropTypes.object
|
||||
}
|
||||
@@ -1,29 +1,28 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import {formatLayerId} from '../util/format';
|
||||
import { StyleSpecification } from '@maplibre/maplibre-gl-style-spec';
|
||||
|
||||
export default class AppMessagePanel extends React.Component {
|
||||
static propTypes = {
|
||||
errors: PropTypes.array,
|
||||
infos: PropTypes.array,
|
||||
mapStyle: PropTypes.object,
|
||||
onLayerSelect: PropTypes.func,
|
||||
currentLayer: PropTypes.object,
|
||||
selectedLayerIndex: PropTypes.number,
|
||||
}
|
||||
type AppMessagePanelProps = {
|
||||
errors?: unknown[]
|
||||
infos?: unknown[]
|
||||
mapStyle?: StyleSpecification
|
||||
onLayerSelect?(...args: unknown[]): unknown
|
||||
currentLayer?: object
|
||||
selectedLayerIndex?: number
|
||||
};
|
||||
|
||||
export default class AppMessagePanel extends React.Component<AppMessagePanelProps> {
|
||||
static defaultProps = {
|
||||
onLayerSelect: () => {},
|
||||
}
|
||||
|
||||
render() {
|
||||
const {selectedLayerIndex} = this.props;
|
||||
const errors = this.props.errors.map((error, idx) => {
|
||||
const errors = this.props.errors?.map((error: any, idx) => {
|
||||
let content;
|
||||
if (error.parsed && error.parsed.type === "layer") {
|
||||
const {parsed} = error;
|
||||
const {mapStyle, currentLayer} = this.props;
|
||||
const layerId = mapStyle.layers[parsed.data.index].id;
|
||||
const layerId = this.props.mapStyle?.layers[parsed.data.index].id;
|
||||
content = (
|
||||
<>
|
||||
Layer <span>{formatLayerId(layerId)}</span>: {parsed.data.message}
|
||||
@@ -32,7 +31,7 @@ export default class AppMessagePanel extends React.Component {
|
||||
—
|
||||
<button
|
||||
className="maputnik-message-panel__switch-button"
|
||||
onClick={() => this.props.onLayerSelect(parsed.data.index)}
|
||||
onClick={() => this.props.onLayerSelect!(parsed.data.index)}
|
||||
>
|
||||
switch to layer
|
||||
</button>
|
||||
@@ -49,7 +48,7 @@ export default class AppMessagePanel extends React.Component {
|
||||
</p>
|
||||
})
|
||||
|
||||
const infos = this.props.infos.map((m, i) => {
|
||||
const infos = this.props.infos?.map((m, i) => {
|
||||
return <p key={"info-"+i}>{m}</p>
|
||||
})
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import classnames from 'classnames'
|
||||
import {detect} from 'detect-browser';
|
||||
|
||||
@@ -9,27 +8,28 @@ import pkgJson from '../../package.json'
|
||||
|
||||
// This is required because of <https://stackoverflow.com/a/49846426>, there isn't another way to detect support that I'm aware of.
|
||||
const browser = detect();
|
||||
const colorAccessibilityFiltersEnabled = ['chrome', 'firefox'].indexOf(browser.name) > -1;
|
||||
const colorAccessibilityFiltersEnabled = ['chrome', 'firefox'].indexOf(browser!.name) > -1;
|
||||
|
||||
|
||||
class IconText extends React.Component {
|
||||
static propTypes = {
|
||||
children: PropTypes.node,
|
||||
}
|
||||
type IconTextProps = {
|
||||
children?: React.ReactNode
|
||||
};
|
||||
|
||||
|
||||
class IconText extends React.Component<IconTextProps> {
|
||||
render() {
|
||||
return <span className="maputnik-icon-text">{this.props.children}</span>
|
||||
}
|
||||
}
|
||||
|
||||
class ToolbarLink extends React.Component {
|
||||
static propTypes = {
|
||||
className: PropTypes.string,
|
||||
children: PropTypes.node,
|
||||
href: PropTypes.string,
|
||||
onToggleModal: PropTypes.func,
|
||||
}
|
||||
type ToolbarLinkProps = {
|
||||
className?: string
|
||||
children?: React.ReactNode
|
||||
href?: string
|
||||
onToggleModal?(...args: unknown[]): unknown
|
||||
};
|
||||
|
||||
class ToolbarLink extends React.Component<ToolbarLinkProps> {
|
||||
render() {
|
||||
return <a
|
||||
className={classnames('maputnik-toolbar-link', this.props.className)}
|
||||
@@ -42,14 +42,14 @@ class ToolbarLink extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
class ToolbarLinkHighlighted extends React.Component {
|
||||
static propTypes = {
|
||||
className: PropTypes.string,
|
||||
children: PropTypes.node,
|
||||
href: PropTypes.string,
|
||||
onToggleModal: PropTypes.func
|
||||
}
|
||||
type ToolbarLinkHighlightedProps = {
|
||||
className?: string
|
||||
children?: React.ReactNode
|
||||
href?: string
|
||||
onToggleModal?(...args: unknown[]): unknown
|
||||
};
|
||||
|
||||
class ToolbarLinkHighlighted extends React.Component<ToolbarLinkHighlightedProps> {
|
||||
render() {
|
||||
return <a
|
||||
className={classnames('maputnik-toolbar-link', "maputnik-toolbar-link--highlighted", this.props.className)}
|
||||
@@ -64,12 +64,12 @@ class ToolbarLinkHighlighted extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
class ToolbarSelect extends React.Component {
|
||||
static propTypes = {
|
||||
children: PropTypes.node,
|
||||
wdKey: PropTypes.string
|
||||
}
|
||||
type ToolbarSelectProps = {
|
||||
children?: React.ReactNode
|
||||
wdKey?: string
|
||||
};
|
||||
|
||||
class ToolbarSelect extends React.Component<ToolbarSelectProps> {
|
||||
render() {
|
||||
return <div
|
||||
className='maputnik-toolbar-select'
|
||||
@@ -80,13 +80,13 @@ class ToolbarSelect extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
class ToolbarAction extends React.Component {
|
||||
static propTypes = {
|
||||
children: PropTypes.node,
|
||||
onClick: PropTypes.func,
|
||||
wdKey: PropTypes.string
|
||||
}
|
||||
type ToolbarActionProps = {
|
||||
children?: React.ReactNode
|
||||
onClick?(...args: unknown[]): unknown
|
||||
wdKey?: string
|
||||
};
|
||||
|
||||
class ToolbarAction extends React.Component<ToolbarActionProps> {
|
||||
render() {
|
||||
return <button
|
||||
className='maputnik-toolbar-action'
|
||||
@@ -98,22 +98,22 @@ class ToolbarAction extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
export default class AppToolbar extends React.Component {
|
||||
static propTypes = {
|
||||
mapStyle: PropTypes.object.isRequired,
|
||||
inspectModeEnabled: PropTypes.bool.isRequired,
|
||||
onStyleChanged: PropTypes.func.isRequired,
|
||||
// A new style has been uploaded
|
||||
onStyleOpen: PropTypes.func.isRequired,
|
||||
// A dict of source id's and the available source layers
|
||||
sources: PropTypes.object.isRequired,
|
||||
children: PropTypes.node,
|
||||
onToggleModal: PropTypes.func,
|
||||
onSetMapState: PropTypes.func,
|
||||
mapState: PropTypes.string,
|
||||
renderer: PropTypes.string,
|
||||
}
|
||||
type AppToolbarProps = {
|
||||
mapStyle: object
|
||||
inspectModeEnabled: boolean
|
||||
onStyleChanged(...args: unknown[]): unknown
|
||||
// A new style has been uploaded
|
||||
onStyleOpen(...args: unknown[]): unknown
|
||||
// A dict of source id's and the available source layers
|
||||
sources: object
|
||||
children?: React.ReactNode
|
||||
onToggleModal(...args: unknown[]): unknown
|
||||
onSetMapState(...args: unknown[]): unknown
|
||||
mapState?: string
|
||||
renderer?: string
|
||||
};
|
||||
|
||||
export default class AppToolbar extends React.Component<AppToolbarProps> {
|
||||
state = {
|
||||
isOpen: {
|
||||
settings: false,
|
||||
@@ -124,16 +124,16 @@ export default class AppToolbar extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
handleSelection(val) {
|
||||
handleSelection(val: string | undefined) {
|
||||
this.props.onSetMapState(val);
|
||||
}
|
||||
|
||||
onSkip = (target) => {
|
||||
onSkip = (target: string) => {
|
||||
if (target === "map") {
|
||||
document.querySelector(".maplibregl-canvas").focus();
|
||||
(document.querySelector(".maplibregl-canvas") as HTMLCanvasElement).focus();
|
||||
}
|
||||
else {
|
||||
const el = document.querySelector("#skip-target-"+target);
|
||||
const el = document.querySelector("#skip-target-"+target) as HTMLButtonElement;
|
||||
el.focus();
|
||||
}
|
||||
}
|
||||
@@ -190,21 +190,21 @@ export default class AppToolbar extends React.Component {
|
||||
<button
|
||||
data-wd-key="root:skip:layer-list"
|
||||
className="maputnik-toolbar-skip"
|
||||
onClick={e => this.onSkip("layer-list")}
|
||||
onClick={_e => this.onSkip("layer-list")}
|
||||
>
|
||||
Layers list
|
||||
</button>
|
||||
<button
|
||||
data-wd-key="root:skip:layer-editor"
|
||||
className="maputnik-toolbar-skip"
|
||||
onClick={e => this.onSkip("layer-editor")}
|
||||
onClick={_e => this.onSkip("layer-editor")}
|
||||
>
|
||||
Layer editor
|
||||
</button>
|
||||
<button
|
||||
data-wd-key="root:skip:map-view"
|
||||
className="maputnik-toolbar-skip"
|
||||
onClick={e => this.onSkip("map")}
|
||||
onClick={_e => this.onSkip("map")}
|
||||
>
|
||||
Map view
|
||||
</button>
|
||||
@@ -246,7 +246,7 @@ export default class AppToolbar extends React.Component {
|
||||
className="maputnik-select"
|
||||
data-wd-key="maputnik-select"
|
||||
onChange={(e) => this.handleSelection(e.target.value)}
|
||||
value={currentView.id}
|
||||
value={currentView?.id}
|
||||
>
|
||||
{views.filter(v => v.group === "general").map((item) => {
|
||||
return (
|
||||
@@ -10,10 +10,8 @@ type FieldAutocompleteProps = InputAutocompleteProps & {
|
||||
|
||||
export default class FieldAutocomplete extends React.Component<FieldAutocompleteProps> {
|
||||
render() {
|
||||
const {props} = this;
|
||||
|
||||
return <Block label={props.label}>
|
||||
<InputAutocomplete {...props} />
|
||||
return <Block label={this.props.label}>
|
||||
<InputAutocomplete {...this.props} />
|
||||
</Block>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import SpecProperty from './_SpecProperty'
|
||||
import DataProperty from './_DataProperty'
|
||||
import DataProperty, { Stop } from './_DataProperty'
|
||||
import ZoomProperty from './_ZoomProperty'
|
||||
import ExpressionProperty from './_ExpressionProperty'
|
||||
import {function as styleFunction} from '@maplibre/maplibre-gl-style-spec';
|
||||
import {findDefaultFromSpec} from '../util/spec-helper';
|
||||
|
||||
|
||||
function isLiteralExpression (value) {
|
||||
function isLiteralExpression(value: any) {
|
||||
return (Array.isArray(value) && value.length === 2 && value[0] === "literal");
|
||||
}
|
||||
|
||||
function isGetExpression (value) {
|
||||
function isGetExpression(value: any) {
|
||||
return (
|
||||
Array.isArray(value) &&
|
||||
value.length === 2 &&
|
||||
@@ -21,14 +20,14 @@ function isGetExpression (value) {
|
||||
);
|
||||
}
|
||||
|
||||
function isZoomField(value) {
|
||||
function isZoomField(value: any) {
|
||||
return (
|
||||
typeof(value) === 'object' &&
|
||||
value.stops &&
|
||||
typeof(value.property) === 'undefined' &&
|
||||
Array.isArray(value.stops) &&
|
||||
value.stops.length > 1 &&
|
||||
value.stops.every(stop => {
|
||||
value.stops.every((stop: Stop) => {
|
||||
return (
|
||||
Array.isArray(stop) &&
|
||||
stop.length === 2
|
||||
@@ -37,7 +36,7 @@ function isZoomField(value) {
|
||||
);
|
||||
}
|
||||
|
||||
function isIdentityProperty (value) {
|
||||
function isIdentityProperty(value: any) {
|
||||
return (
|
||||
typeof(value) === 'object' &&
|
||||
value.type === "identity" &&
|
||||
@@ -45,14 +44,14 @@ function isIdentityProperty (value) {
|
||||
);
|
||||
}
|
||||
|
||||
function isDataStopProperty (value) {
|
||||
function isDataStopProperty(value: any) {
|
||||
return (
|
||||
typeof(value) === 'object' &&
|
||||
value.stops &&
|
||||
typeof(value.property) !== 'undefined' &&
|
||||
value.stops.length > 1 &&
|
||||
Array.isArray(value.stops) &&
|
||||
value.stops.every(stop => {
|
||||
value.stops.every((stop: Stop) => {
|
||||
return (
|
||||
Array.isArray(stop) &&
|
||||
stop.length === 2 &&
|
||||
@@ -62,26 +61,26 @@ function isDataStopProperty (value) {
|
||||
);
|
||||
}
|
||||
|
||||
function isDataField(value) {
|
||||
function isDataField(value: any) {
|
||||
return (
|
||||
isIdentityProperty(value) ||
|
||||
isDataStopProperty(value)
|
||||
);
|
||||
}
|
||||
|
||||
function isPrimative (value) {
|
||||
function isPrimative(value: any): value is string | boolean | number {
|
||||
const valid = ["string", "boolean", "number"];
|
||||
return valid.includes(typeof(value));
|
||||
}
|
||||
|
||||
function isArrayOfPrimatives (values) {
|
||||
function isArrayOfPrimatives(values: any): values is Array<string | boolean | number> {
|
||||
if (Array.isArray(values)) {
|
||||
return values.every(isPrimative);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function getDataType (value, fieldSpec={}) {
|
||||
function getDataType(value: any, fieldSpec={} as any) {
|
||||
if (value === undefined) {
|
||||
return "value";
|
||||
}
|
||||
@@ -103,35 +102,33 @@ function getDataType (value, fieldSpec={}) {
|
||||
}
|
||||
|
||||
|
||||
type FieldFunctionProps = {
|
||||
onChange(fieldName: string, value: any): unknown
|
||||
fieldName: string
|
||||
fieldType: string
|
||||
fieldSpec: any
|
||||
errors?: unknown[]
|
||||
value?: any
|
||||
};
|
||||
|
||||
type FieldFunctionState = {
|
||||
dataType: string
|
||||
isEditing: boolean
|
||||
}
|
||||
|
||||
/** Supports displaying spec field for zoom function objects
|
||||
* https://www.mapbox.com/mapbox-gl-style-spec/#types-function-zoom-property
|
||||
*/
|
||||
export default class FieldFunction extends React.Component {
|
||||
static propTypes = {
|
||||
onChange: PropTypes.func.isRequired,
|
||||
fieldName: PropTypes.string.isRequired,
|
||||
fieldType: PropTypes.string.isRequired,
|
||||
fieldSpec: PropTypes.object.isRequired,
|
||||
errors: PropTypes.object,
|
||||
|
||||
value: PropTypes.oneOfType([
|
||||
PropTypes.object,
|
||||
PropTypes.string,
|
||||
PropTypes.number,
|
||||
PropTypes.bool,
|
||||
PropTypes.array
|
||||
]),
|
||||
}
|
||||
|
||||
constructor (props) {
|
||||
super();
|
||||
export default class FieldFunction extends React.Component<FieldFunctionProps, FieldFunctionState> {
|
||||
constructor (props: FieldFunctionProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
dataType: getDataType(props.value, props.fieldSpec),
|
||||
isEditing: false,
|
||||
}
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps(props, state) {
|
||||
static getDerivedStateFromProps(props: FieldFunctionProps, state: FieldFunctionState) {
|
||||
// Because otherwise when editing values we end up accidentally changing field type.
|
||||
if (state.isEditing) {
|
||||
return {};
|
||||
@@ -144,7 +141,7 @@ export default class FieldFunction extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
getFieldFunctionType(fieldSpec) {
|
||||
getFieldFunctionType(fieldSpec: any) {
|
||||
if (fieldSpec.expression.interpolated) {
|
||||
return "exponential"
|
||||
}
|
||||
@@ -183,7 +180,7 @@ export default class FieldFunction extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
deleteStop = (stopIdx) => {
|
||||
deleteStop = (stopIdx: number) => {
|
||||
const stops = this.props.value.stops.slice(0)
|
||||
stops.splice(stopIdx, 1)
|
||||
|
||||
@@ -207,7 +204,7 @@ export default class FieldFunction extends React.Component {
|
||||
if (value.stops) {
|
||||
zoomFunc = {
|
||||
base: value.base,
|
||||
stops: value.stops.map(stop => {
|
||||
stops: value.stops.map((stop: Stop) => {
|
||||
return [stop[0].zoom, stop[1] || findDefaultFromSpec(this.props.fieldSpec)];
|
||||
})
|
||||
}
|
||||
@@ -292,7 +289,7 @@ export default class FieldFunction extends React.Component {
|
||||
property: "",
|
||||
type: functionType,
|
||||
base: value.base,
|
||||
stops: value.stops.map(stop => {
|
||||
stops: value.stops.map((stop: Stop) => {
|
||||
return [{zoom: stop[0], value: stopValue}, stop[1] || findDefaultFromSpec(this.props.fieldSpec)];
|
||||
})
|
||||
}
|
||||
@@ -10,10 +10,8 @@ type FieldMultiInputProps = InputMultiInputProps & {
|
||||
|
||||
export default class FieldMultiInput extends React.Component<FieldMultiInputProps> {
|
||||
render() {
|
||||
const {props} = this;
|
||||
|
||||
return <Fieldset label={props.label}>
|
||||
<InputMultiInput {...props} />
|
||||
return <Fieldset label={this.props.label}>
|
||||
<InputMultiInput {...this.props} />
|
||||
</Fieldset>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,17 +3,17 @@ import InputString from './InputString'
|
||||
import InputNumber from './InputNumber'
|
||||
|
||||
export type FieldArrayProps = {
|
||||
value: string[] | number[]
|
||||
value: (string | number | undefined)[]
|
||||
type?: string
|
||||
length?: number
|
||||
default?: string[] | number[]
|
||||
onChange?(...args: unknown[]): unknown
|
||||
default?: (string | number | undefined)[]
|
||||
onChange?(value: (string | number | undefined)[] | undefined): unknown
|
||||
'aria-label'?: string
|
||||
label?: string
|
||||
};
|
||||
|
||||
type FieldArrayState = {
|
||||
value: string[] | number[]
|
||||
value: (string | number | undefined)[]
|
||||
initialPropsValue: unknown[]
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ export default class FieldArray extends React.Component<FieldArrayProps, FieldAr
|
||||
});
|
||||
}
|
||||
|
||||
changeValue(idx: number, newValue: string) {
|
||||
changeValue(idx: number, newValue: string | number | undefined) {
|
||||
const value = this.state.value.slice(0);
|
||||
value[idx] = newValue;
|
||||
|
||||
@@ -93,7 +93,7 @@ export default class FieldArray extends React.Component<FieldArrayProps, FieldAr
|
||||
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)}
|
||||
onChange={(v) => this.changeValue(i, v)}
|
||||
aria-label={this.props['aria-label'] || this.props.label}
|
||||
/>
|
||||
} else {
|
||||
|
||||
@@ -11,10 +11,10 @@ import InputUrl from './InputUrl'
|
||||
|
||||
|
||||
export type FieldDynamicArrayProps = {
|
||||
value?: (string | number)[]
|
||||
value?: (string | number | undefined)[]
|
||||
type?: 'url' | 'number' | 'enum' | 'string'
|
||||
default?: (string | number)[]
|
||||
onChange?(...args: unknown[]): unknown
|
||||
default?: (string | number | undefined)[]
|
||||
onChange?(values: (string | number | undefined)[] | undefined): unknown
|
||||
style?: object
|
||||
fieldSpec?: {
|
||||
values?: any
|
||||
@@ -25,7 +25,7 @@ export type FieldDynamicArrayProps = {
|
||||
|
||||
|
||||
export default class FieldDynamicArray extends React.Component<FieldDynamicArrayProps> {
|
||||
changeValue(idx: number, newValue: string | number) {
|
||||
changeValue(idx: number, newValue: string | number | undefined) {
|
||||
const values = this.values.slice(0)
|
||||
values[idx] = newValue
|
||||
if (this.props.onChange) this.props.onChange(values)
|
||||
|
||||
@@ -7,7 +7,7 @@ export type InputNumberProps = {
|
||||
default?: number
|
||||
min?: number
|
||||
max?: number
|
||||
onChange?(...args: unknown[]): unknown
|
||||
onChange?(value: number | undefined): unknown
|
||||
allowRange?: boolean
|
||||
rangeStep?: number
|
||||
wdKey?: string
|
||||
|
||||
@@ -5,7 +5,7 @@ export type InputSelectProps = {
|
||||
"data-wd-key"?: string
|
||||
options: [string, any][] | string[]
|
||||
style?: object
|
||||
onChange(...args: unknown[]): unknown
|
||||
onChange(value: string): unknown
|
||||
title?: string
|
||||
'aria-label'?: string
|
||||
};
|
||||
|
||||
@@ -14,11 +14,11 @@ 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
|
||||
onChange?(fieldName: string | undefined, value: number | undefined | (string | number | undefined)[]): unknown
|
||||
fieldName?: string
|
||||
fieldSpec: {
|
||||
fieldSpec?: {
|
||||
default?: unknown
|
||||
type: 'number' | 'enum' | 'resolvedImage' | 'formatted' | 'string' | 'color' | 'boolean' | 'array'
|
||||
type?: 'number' | 'enum' | 'resolvedImage' | 'formatted' | 'string' | 'color' | 'boolean' | 'array'
|
||||
minimum?: number
|
||||
maximum?: number
|
||||
values?: unknown[]
|
||||
@@ -29,9 +29,9 @@ export type SpecFieldProps = {
|
||||
/** Override the style of the field */
|
||||
style?: object
|
||||
'aria-label'?: string
|
||||
error: unknown[]
|
||||
label: string
|
||||
action: ReactElement
|
||||
error?: unknown[]
|
||||
label?: string
|
||||
action?: ReactElement
|
||||
};
|
||||
|
||||
/** Display any field from the Maplibre GL style spec and
|
||||
@@ -47,12 +47,12 @@ export default class SpecField extends React.Component<SpecFieldProps> {
|
||||
action: this.props.action,
|
||||
style: this.props.style,
|
||||
value: this.props.value,
|
||||
default: this.props.fieldSpec.default,
|
||||
default: this.props.fieldSpec?.default,
|
||||
name: this.props.fieldName,
|
||||
onChange: (newValue: string) => this.props.onChange!(this.props.fieldName, newValue),
|
||||
onChange: (newValue: number | undefined | (string | number | undefined)[]) => this.props.onChange!(this.props.fieldName, newValue),
|
||||
'aria-label': this.props['aria-label'],
|
||||
}
|
||||
switch(this.props.fieldSpec.type) {
|
||||
switch(this.props.fieldSpec?.type) {
|
||||
case 'number': return (
|
||||
<InputNumber
|
||||
{...commonProps as InputNumberProps}
|
||||
|
||||
@@ -5,13 +5,14 @@ export type InputStringProps = {
|
||||
value?: string
|
||||
style?: object
|
||||
default?: string
|
||||
onChange?(...args: unknown[]): unknown
|
||||
onInput?(...args: unknown[]): unknown
|
||||
onChange?(value: string | undefined): unknown
|
||||
onInput?(value: string | undefined): unknown
|
||||
multi?: boolean
|
||||
required?: boolean
|
||||
disabled?: boolean
|
||||
spellCheck?: boolean
|
||||
'aria-label'?: string
|
||||
title?: string
|
||||
};
|
||||
|
||||
type InputStringState = {
|
||||
@@ -72,6 +73,7 @@ export default class InputString extends React.Component<InputStringProps, Input
|
||||
style: this.props.style,
|
||||
value: this.state.value === undefined ? "" : this.state.value,
|
||||
placeholder: this.props.default,
|
||||
title: this.props.title,
|
||||
onChange: (e: React.BaseSyntheticEvent<Event, HTMLInputElement, HTMLInputElement>) => {
|
||||
this.setState({
|
||||
editing: true,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import Icon from '@mdi/react'
|
||||
import {
|
||||
mdiMenuDown,
|
||||
@@ -13,21 +12,22 @@ import {
|
||||
} from 'react-accessible-accordion';
|
||||
|
||||
|
||||
export default class LayerEditorGroup extends React.Component {
|
||||
static propTypes = {
|
||||
"id": PropTypes.string,
|
||||
"data-wd-key": PropTypes.string,
|
||||
title: PropTypes.string.isRequired,
|
||||
isActive: PropTypes.bool.isRequired,
|
||||
children: PropTypes.element.isRequired,
|
||||
onActiveToggle: PropTypes.func.isRequired
|
||||
}
|
||||
type LayerEditorGroupProps = {
|
||||
"id"?: string
|
||||
"data-wd-key"?: string
|
||||
title: string
|
||||
isActive: boolean
|
||||
children: React.ReactElement
|
||||
onActiveToggle(...args: unknown[]): unknown
|
||||
};
|
||||
|
||||
|
||||
export default class LayerEditorGroup extends React.Component<LayerEditorGroupProps> {
|
||||
render() {
|
||||
return <AccordionItem uuid={this.props.id}>
|
||||
<AccordionItemHeading className="maputnik-layer-editor-group"
|
||||
data-wd-key={"layer-editor-group:"+this.props["data-wd-key"]}
|
||||
onClick={e => this.props.onActiveToggle(!this.props.isActive)}
|
||||
onClick={_e => this.props.onActiveToggle(!this.props.isActive)}
|
||||
>
|
||||
<AccordionItemButton className="maputnik-layer-editor-group__button">
|
||||
<span style={{flexGrow: 1}}>{this.props.title}</span>
|
||||
@@ -1,21 +1,20 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import Collapser from './Collapser'
|
||||
|
||||
export default class LayerListGroup extends React.Component {
|
||||
static propTypes = {
|
||||
title: PropTypes.string.isRequired,
|
||||
"data-wd-key": PropTypes.string,
|
||||
isActive: PropTypes.bool.isRequired,
|
||||
onActiveToggle: PropTypes.func.isRequired,
|
||||
'aria-controls': PropTypes.string,
|
||||
}
|
||||
type LayerListGroupProps = {
|
||||
title: string
|
||||
"data-wd-key"?: string
|
||||
isActive: boolean
|
||||
onActiveToggle(...args: unknown[]): unknown
|
||||
'aria-controls'?: string
|
||||
};
|
||||
|
||||
export default class LayerListGroup extends React.Component<LayerListGroupProps> {
|
||||
render() {
|
||||
return <li className="maputnik-layer-list-group">
|
||||
<div className="maputnik-layer-list-group-header"
|
||||
data-wd-key={"layer-list-group:"+this.props["data-wd-key"]}
|
||||
onClick={e => this.props.onActiveToggle(!this.props.isActive)}
|
||||
onClick={_e => this.props.onActiveToggle(!this.props.isActive)}
|
||||
>
|
||||
<button
|
||||
className="maputnik-layer-list-group-title"
|
||||
+12
-15
@@ -1,18 +1,16 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import IconLayer from './IconLayer'
|
||||
import {latest} from '@maplibre/maplibre-gl-style-spec'
|
||||
|
||||
function groupFeaturesBySourceLayer(features) {
|
||||
const sources = {}
|
||||
function groupFeaturesBySourceLayer(features: any[]) {
|
||||
const sources = {} as any
|
||||
|
||||
let returnedFeatures = {};
|
||||
let returnedFeatures = {} as any;
|
||||
|
||||
features.forEach(feature => {
|
||||
if(returnedFeatures.hasOwnProperty(feature.layer.id)) {
|
||||
returnedFeatures[feature.layer.id]++
|
||||
|
||||
const featureObject = sources[feature.layer['source-layer']].find(f => f.layer.id === feature.layer.id)
|
||||
const featureObject = sources[feature.layer['source-layer']].find((f: any) => f.layer.id === feature.layer.id)
|
||||
|
||||
featureObject.counter = returnedFeatures[feature.layer.id]
|
||||
} else {
|
||||
@@ -26,14 +24,14 @@ function groupFeaturesBySourceLayer(features) {
|
||||
return sources
|
||||
}
|
||||
|
||||
class FeatureLayerPopup extends React.Component {
|
||||
static propTypes = {
|
||||
onLayerSelect: PropTypes.func.isRequired,
|
||||
features: PropTypes.array,
|
||||
zoom: PropTypes.number,
|
||||
}
|
||||
type FeatureLayerPopupProps = {
|
||||
onLayerSelect(...args: unknown[]): unknown
|
||||
features: any[]
|
||||
zoom?: number
|
||||
};
|
||||
|
||||
_getFeatureColor(feature, zoom) {
|
||||
class FeatureLayerPopup extends React.Component<FeatureLayerPopupProps> {
|
||||
_getFeatureColor(feature: any, _zoom?: number) {
|
||||
// Guard because openlayers won't have this
|
||||
if (!feature.layer.paint) {
|
||||
return;
|
||||
@@ -57,7 +55,6 @@ class FeatureLayerPopup extends React.Component {
|
||||
}
|
||||
|
||||
if(propName) {
|
||||
const propertySpec = latest["paint_"+feature.layer.type][propName];
|
||||
let color = feature.layer.paint[propName];
|
||||
return String(color);
|
||||
}
|
||||
@@ -78,7 +75,7 @@ class FeatureLayerPopup extends React.Component {
|
||||
const sources = groupFeaturesBySourceLayer(this.props.features)
|
||||
|
||||
const items = Object.keys(sources).map(vectorLayerId => {
|
||||
const layers = sources[vectorLayerId].map((feature, idx) => {
|
||||
const layers = sources[vectorLayerId].map((feature: any, idx: number) => {
|
||||
const featureColor = this._getFeatureColor(feature, this.props.zoom);
|
||||
|
||||
return <div
|
||||
@@ -1,6 +1,5 @@
|
||||
import React from 'react'
|
||||
import {throttle} from 'lodash';
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import MapMaplibreGlLayerPopup from './MapMaplibreGlLayerPopup';
|
||||
|
||||
@@ -9,10 +8,10 @@ import {apply} from 'ol-mapbox-style';
|
||||
import {Map, View, Overlay} from 'ol';
|
||||
|
||||
import {toLonLat} from 'ol/proj';
|
||||
import {toStringHDMS} from 'ol/coordinate';
|
||||
import { StyleSpecification } from '@maplibre/maplibre-gl-style-spec';
|
||||
|
||||
|
||||
function renderCoords (coords) {
|
||||
function renderCoords (coords: string[]) {
|
||||
if (!coords || coords.length < 2) {
|
||||
return null;
|
||||
}
|
||||
@@ -23,36 +22,49 @@ function renderCoords (coords) {
|
||||
}
|
||||
}
|
||||
|
||||
export default class MapOpenLayers extends React.Component {
|
||||
static propTypes = {
|
||||
onDataChange: PropTypes.func,
|
||||
mapStyle: PropTypes.object.isRequired,
|
||||
accessToken: PropTypes.string,
|
||||
style: PropTypes.object,
|
||||
onLayerSelect: PropTypes.func.isRequired,
|
||||
debugToolbox: PropTypes.bool.isRequired,
|
||||
replaceAccessTokens: PropTypes.func.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
}
|
||||
type MapOpenLayersProps = {
|
||||
onDataChange?(...args: unknown[]): unknown
|
||||
mapStyle: object
|
||||
accessToken?: string
|
||||
style?: object
|
||||
onLayerSelect(...args: unknown[]): unknown
|
||||
debugToolbox: boolean
|
||||
replaceAccessTokens(...args: unknown[]): unknown
|
||||
onChange(...args: unknown[]): unknown
|
||||
};
|
||||
|
||||
type MapOpenLayersState = {
|
||||
zoom: string
|
||||
rotation: string
|
||||
cursor: string[]
|
||||
center: string[]
|
||||
selectedFeatures?: any[]
|
||||
};
|
||||
|
||||
export default class MapOpenLayers extends React.Component<MapOpenLayersProps, MapOpenLayersState> {
|
||||
static defaultProps = {
|
||||
onMapLoaded: () => {},
|
||||
onDataChange: () => {},
|
||||
onLayerSelect: () => {},
|
||||
}
|
||||
updateStyle: any;
|
||||
map: any;
|
||||
container: HTMLDivElement | null = null;
|
||||
overlay: Overlay | undefined;
|
||||
popupContainer: HTMLElement | null = null;
|
||||
|
||||
constructor(props) {
|
||||
constructor(props: MapOpenLayersProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
zoom: 0,
|
||||
rotation: 0,
|
||||
cursor: [],
|
||||
zoom: "0",
|
||||
rotation: "0",
|
||||
cursor: [] as string[],
|
||||
center: [],
|
||||
};
|
||||
this.updateStyle = throttle(this._updateStyle.bind(this), 200);
|
||||
}
|
||||
|
||||
_updateStyle(newMapStyle) {
|
||||
_updateStyle(newMapStyle: StyleSpecification) {
|
||||
if(!this.map) return;
|
||||
|
||||
// See <https://github.com/openlayers/ol-mapbox-style/issues/215#issuecomment-493198815>
|
||||
@@ -60,7 +72,7 @@ export default class MapOpenLayers extends React.Component {
|
||||
apply(this.map, newMapStyle);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
componentDidUpdate(prevProps: MapOpenLayersProps) {
|
||||
if (this.props.mapStyle !== prevProps.mapStyle) {
|
||||
this.updateStyle(
|
||||
this.props.replaceAccessTokens(this.props.mapStyle)
|
||||
@@ -70,7 +82,7 @@ export default class MapOpenLayers extends React.Component {
|
||||
|
||||
componentDidMount() {
|
||||
this.overlay = new Overlay({
|
||||
element: this.popupContainer,
|
||||
element: this.popupContainer!,
|
||||
autoPan: true,
|
||||
autoPanAnimation: {
|
||||
duration: 250
|
||||
@@ -78,7 +90,7 @@ export default class MapOpenLayers extends React.Component {
|
||||
});
|
||||
|
||||
const map = new Map({
|
||||
target: this.container,
|
||||
target: this.container!,
|
||||
overlays: [this.overlay],
|
||||
view: new View({
|
||||
zoom: 1,
|
||||
@@ -98,7 +110,7 @@ export default class MapOpenLayers extends React.Component {
|
||||
|
||||
const onMoveEnd = () => {
|
||||
const zoom = map.getView().getZoom();
|
||||
const center = toLonLat(map.getView().getCenter());
|
||||
const center = toLonLat(map.getView().getCenter()!);
|
||||
|
||||
this.props.onChange({
|
||||
zoom,
|
||||
@@ -112,15 +124,15 @@ export default class MapOpenLayers extends React.Component {
|
||||
onMoveEnd();
|
||||
map.on('moveend', onMoveEnd);
|
||||
|
||||
map.on('postrender', (evt) => {
|
||||
const center = toLonLat(map.getView().getCenter());
|
||||
map.on('postrender', (_e) => {
|
||||
const center = toLonLat(map.getView().getCenter()!);
|
||||
this.setState({
|
||||
center: [
|
||||
center[0].toFixed(2),
|
||||
center[1].toFixed(2),
|
||||
],
|
||||
rotation: map.getView().getRotation().toFixed(2),
|
||||
zoom: map.getView().getZoom().toFixed(2)
|
||||
zoom: map.getView().getZoom()!.toFixed(2)
|
||||
});
|
||||
});
|
||||
|
||||
@@ -132,9 +144,9 @@ export default class MapOpenLayers extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
closeOverlay = (e) => {
|
||||
closeOverlay = (e: any) => {
|
||||
e.target.blur();
|
||||
this.overlay.setPosition(undefined);
|
||||
this.overlay!.setPosition(undefined);
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -233,7 +233,7 @@ class AddSource extends React.Component<AddSourceProps, AddSourceState> {
|
||||
['image', 'Image'],
|
||||
['video', 'Video'],
|
||||
]}
|
||||
onChange={(mode: EditorMode) => this.setState({mode: mode, source: this.defaultSource(mode)})}
|
||||
onChange={mode => this.setState({mode: mode as EditorMode, source: this.defaultSource(mode as EditorMode)})}
|
||||
value={this.state.mode as string}
|
||||
/>
|
||||
<ModalSourcesTypeEditor
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import FieldFunction from './FieldFunction'
|
||||
import { LayerSpecification } from '@maplibre/maplibre-gl-style-spec'
|
||||
const iconProperties = ['background-pattern', 'fill-pattern', 'line-pattern', 'fill-extrusion-pattern', 'icon-image']
|
||||
|
||||
/** Extract field spec by {@fieldName} from the {@layerType} in the
|
||||
* style specification from either the paint or layout group */
|
||||
function getFieldSpec(spec, layerType, fieldName) {
|
||||
function getFieldSpec(spec: any, layerType: LayerSpecification["type"], fieldName: string) {
|
||||
const groupName = getGroupName(spec, layerType, fieldName)
|
||||
const group = spec[groupName + '_' + layerType]
|
||||
const fieldSpec = group[fieldName]
|
||||
@@ -25,7 +25,7 @@ function getFieldSpec(spec, layerType, fieldName) {
|
||||
return fieldSpec
|
||||
}
|
||||
|
||||
function getGroupName(spec, layerType, fieldName) {
|
||||
function getGroupName(spec: any, layerType: LayerSpecification["type"], fieldName: string) {
|
||||
const paint = spec['paint_' + layerType] || {}
|
||||
if (fieldName in paint) {
|
||||
return 'paint'
|
||||
@@ -34,18 +34,18 @@ function getGroupName(spec, layerType, fieldName) {
|
||||
}
|
||||
}
|
||||
|
||||
export default class PropertyGroup extends React.Component {
|
||||
static propTypes = {
|
||||
layer: PropTypes.object.isRequired,
|
||||
groupFields: PropTypes.array.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
spec: PropTypes.object.isRequired,
|
||||
errors: PropTypes.object,
|
||||
}
|
||||
type PropertyGroupProps = {
|
||||
layer: LayerSpecification
|
||||
groupFields: string[]
|
||||
onChange(...args: unknown[]): unknown
|
||||
spec: any
|
||||
errors?: unknown[]
|
||||
};
|
||||
|
||||
onPropertyChange = (property, newValue) => {
|
||||
export default class PropertyGroup extends React.Component<PropertyGroupProps> {
|
||||
onPropertyChange = (property: string, newValue: any) => {
|
||||
const group = getGroupName(this.props.spec, this.props.layer.type, property)
|
||||
this.props.onChange(group , property, newValue)
|
||||
this.props.onChange(group ,property, newValue)
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -55,7 +55,9 @@ export default class PropertyGroup extends React.Component {
|
||||
|
||||
const paint = this.props.layer.paint || {}
|
||||
const layout = this.props.layer.layout || {}
|
||||
const fieldValue = fieldName in paint ? paint[fieldName] : layout[fieldName]
|
||||
const fieldValue = fieldName in paint
|
||||
? paint[fieldName as keyof typeof paint]
|
||||
: layout[fieldName as keyof typeof layout]
|
||||
const fieldType = fieldName in paint ? 'paint' : 'layout';
|
||||
|
||||
return <FieldFunction
|
||||
@@ -15,15 +15,15 @@ const typeMap = {
|
||||
formatted: () => Block,
|
||||
};
|
||||
|
||||
type SpecFieldProps = InputFieldSpecProps & {
|
||||
export type SpecFieldProps = InputFieldSpecProps & {
|
||||
name?: string
|
||||
};
|
||||
|
||||
export default class SpecField extends React.Component<SpecFieldProps> {
|
||||
render() {
|
||||
const fieldType = this.props.fieldSpec.type;
|
||||
const fieldType = this.props.fieldSpec?.type;
|
||||
|
||||
const typeBlockFn = typeMap[fieldType];
|
||||
const typeBlockFn = typeMap[fieldType!];
|
||||
|
||||
let TypeBlock;
|
||||
if (typeBlockFn) {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import {mdiFunctionVariant, mdiTableRowPlusAfter} from '@mdi/js';
|
||||
import {latest} from '@maplibre/maplibre-gl-style-spec'
|
||||
|
||||
@@ -8,7 +7,6 @@ import InputSpec from './InputSpec'
|
||||
import InputNumber from './InputNumber'
|
||||
import InputString from './InputString'
|
||||
import InputSelect from './InputSelect'
|
||||
import FieldDocLabel from './FieldDocLabel'
|
||||
import Block from './Block'
|
||||
import docUid from '../libs/document-uid'
|
||||
import sortNumerically from '../libs/sort-numerically'
|
||||
@@ -19,12 +17,12 @@ import DeleteStopButton from './_DeleteStopButton'
|
||||
|
||||
|
||||
|
||||
function setStopRefs(props, state) {
|
||||
function setStopRefs(props: DataPropertyProps, state: DataPropertyState) {
|
||||
// This is initialsed below only if required to improved performance.
|
||||
let newRefs;
|
||||
let newRefs: {[key: number]: string} | undefined;
|
||||
|
||||
if(props.value && props.value.stops) {
|
||||
props.value.stops.forEach((val, idx) => {
|
||||
props.value.stops.forEach((_val, idx) => {
|
||||
if(!state.refs.hasOwnProperty(idx)) {
|
||||
if(!newRefs) {
|
||||
newRefs = {...state};
|
||||
@@ -37,27 +35,39 @@ function setStopRefs(props, state) {
|
||||
return newRefs;
|
||||
}
|
||||
|
||||
export default class DataProperty extends React.Component {
|
||||
static propTypes = {
|
||||
onChange: PropTypes.func,
|
||||
onDeleteStop: PropTypes.func,
|
||||
onAddStop: PropTypes.func,
|
||||
onExpressionClick: PropTypes.func,
|
||||
fieldName: PropTypes.string,
|
||||
fieldType: PropTypes.string,
|
||||
fieldSpec: PropTypes.object,
|
||||
value: PropTypes.oneOfType([
|
||||
PropTypes.object,
|
||||
PropTypes.string,
|
||||
PropTypes.number,
|
||||
PropTypes.bool,
|
||||
PropTypes.array
|
||||
]),
|
||||
errors: PropTypes.object,
|
||||
}
|
||||
type DataPropertyProps = {
|
||||
onChange?(fieldName: string, value: any): unknown
|
||||
onDeleteStop?(...args: unknown[]): unknown
|
||||
onAddStop?(...args: unknown[]): unknown
|
||||
onExpressionClick?(...args: unknown[]): unknown
|
||||
onChangeToZoomFunction?(...args: unknown[]): unknown
|
||||
fieldName: string
|
||||
fieldType?: string
|
||||
fieldSpec?: object
|
||||
value?: DataPropertyValue
|
||||
errors?: object
|
||||
};
|
||||
|
||||
type DataPropertyState = {
|
||||
refs: {[key: number]: string}
|
||||
};
|
||||
|
||||
type DataPropertyValue = {
|
||||
default?: any
|
||||
property?: string
|
||||
base?: number
|
||||
type?: string
|
||||
stops: Stop[]
|
||||
}
|
||||
|
||||
export type Stop = [{
|
||||
zoom: number
|
||||
value: number
|
||||
}, number]
|
||||
|
||||
export default class DataProperty extends React.Component<DataPropertyProps, DataPropertyState> {
|
||||
state = {
|
||||
refs: {}
|
||||
refs: {} as {[key: number]: string}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
@@ -70,7 +80,7 @@ export default class DataProperty extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps(props, state) {
|
||||
static getDerivedStateFromProps(props: DataPropertyProps, state: DataPropertyState) {
|
||||
const newRefs = setStopRefs(props, state);
|
||||
if(newRefs) {
|
||||
return {
|
||||
@@ -80,7 +90,7 @@ export default class DataProperty extends React.Component {
|
||||
return null;
|
||||
}
|
||||
|
||||
getFieldFunctionType(fieldSpec) {
|
||||
getFieldFunctionType(fieldSpec: any) {
|
||||
if (fieldSpec.expression.interpolated) {
|
||||
return "exponential"
|
||||
}
|
||||
@@ -90,7 +100,7 @@ export default class DataProperty extends React.Component {
|
||||
return "categorical"
|
||||
}
|
||||
|
||||
getDataFunctionTypes(fieldSpec) {
|
||||
getDataFunctionTypes(fieldSpec: any) {
|
||||
if (fieldSpec.expression.interpolated) {
|
||||
return ["interpolate", "categorical", "interval", "exponential", "identity"]
|
||||
}
|
||||
@@ -100,7 +110,7 @@ export default class DataProperty extends React.Component {
|
||||
}
|
||||
|
||||
// Order the stops altering the refs to reflect their new position.
|
||||
orderStopsByZoom(stops) {
|
||||
orderStopsByZoom(stops: Stop[]) {
|
||||
const mappedWithRef = stops
|
||||
.map((stop, idx) => {
|
||||
return {
|
||||
@@ -112,7 +122,7 @@ export default class DataProperty extends React.Component {
|
||||
.sort((a, b) => sortNumerically(a.data[0].zoom, b.data[0].zoom));
|
||||
|
||||
// Fetch the new position of the stops
|
||||
const newRefs = {};
|
||||
const newRefs = {} as {[key: number]: string};
|
||||
mappedWithRef
|
||||
.forEach((stop, idx) =>{
|
||||
newRefs[idx] = stop.ref;
|
||||
@@ -125,7 +135,7 @@ export default class DataProperty extends React.Component {
|
||||
return mappedWithRef.map((item) => item.data);
|
||||
}
|
||||
|
||||
onChange = (fieldName, value) => {
|
||||
onChange = (fieldName: string, value: any) => {
|
||||
if (value.type === "identity") {
|
||||
value = {
|
||||
type: value.type,
|
||||
@@ -139,21 +149,21 @@ export default class DataProperty extends React.Component {
|
||||
type: value.type,
|
||||
// Default props if they don't already exist.
|
||||
stops: [
|
||||
[{zoom: 6, value: stopValue}, findDefaultFromSpec(this.props.fieldSpec)],
|
||||
[{zoom: 10, value: stopValue}, findDefaultFromSpec(this.props.fieldSpec)]
|
||||
[{zoom: 6, value: stopValue}, findDefaultFromSpec(this.props.fieldSpec as any)],
|
||||
[{zoom: 10, value: stopValue}, findDefaultFromSpec(this.props.fieldSpec as any)]
|
||||
],
|
||||
...value,
|
||||
}
|
||||
}
|
||||
this.props.onChange(fieldName, value);
|
||||
this.props.onChange!(fieldName, value);
|
||||
}
|
||||
|
||||
changeStop(changeIdx, stopData, value) {
|
||||
const stops = this.props.value.stops.slice(0)
|
||||
changeStop(changeIdx: number, stopData: { zoom: number | undefined, value: number }, value: number) {
|
||||
const stops = this.props.value?.stops.slice(0) || []
|
||||
// const changedStop = stopData.zoom === undefined ? stopData.value : stopData
|
||||
stops[changeIdx] = [
|
||||
{
|
||||
...stopData,
|
||||
value: stopData.value,
|
||||
zoom: (stopData.zoom === undefined) ? 0 : stopData.zoom,
|
||||
},
|
||||
value
|
||||
@@ -168,7 +178,7 @@ export default class DataProperty extends React.Component {
|
||||
this.onChange(this.props.fieldName, changedValue)
|
||||
}
|
||||
|
||||
changeBase(newValue) {
|
||||
changeBase(newValue: number | undefined) {
|
||||
const changedValue = {
|
||||
...this.props.value,
|
||||
base: newValue
|
||||
@@ -177,11 +187,11 @@ export default class DataProperty extends React.Component {
|
||||
if (changedValue.base === undefined) {
|
||||
delete changedValue["base"];
|
||||
}
|
||||
this.props.onChange(this.props.fieldName, changedValue)
|
||||
this.props.onChange!(this.props.fieldName, changedValue)
|
||||
}
|
||||
|
||||
changeDataType(propVal) {
|
||||
if (propVal === "interpolate") {
|
||||
changeDataType(propVal: string) {
|
||||
if (propVal === "interpolate" && this.props.onChangeToZoomFunction) {
|
||||
this.props.onChangeToZoomFunction();
|
||||
}
|
||||
else {
|
||||
@@ -192,41 +202,39 @@ export default class DataProperty extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
changeDataProperty(propName, propVal) {
|
||||
changeDataProperty(propName: "property" | "default", propVal: any) {
|
||||
if (propVal) {
|
||||
this.props.value[propName] = propVal
|
||||
this.props.value![propName] = propVal
|
||||
}
|
||||
else {
|
||||
delete this.props.value[propName]
|
||||
delete this.props.value![propName]
|
||||
}
|
||||
this.onChange(this.props.fieldName, this.props.value)
|
||||
}
|
||||
|
||||
render() {
|
||||
const {fieldName, fieldType, errors} = this.props;
|
||||
|
||||
if (typeof this.props.value.type === "undefined") {
|
||||
this.props.value.type = this.getFieldFunctionType(this.props.fieldSpec)
|
||||
if (typeof this.props.value?.type === "undefined") {
|
||||
this.props.value!.type = this.getFieldFunctionType(this.props.fieldSpec)
|
||||
}
|
||||
|
||||
let dataFields;
|
||||
if (this.props.value.stops) {
|
||||
if (this.props.value?.stops) {
|
||||
dataFields = this.props.value.stops.map((stop, idx) => {
|
||||
const zoomLevel = typeof stop[0] === 'object' ? stop[0].zoom : undefined;
|
||||
const key = this.state.refs[idx];
|
||||
const dataLevel = typeof stop[0] === 'object' ? stop[0].value : stop[0];
|
||||
const value = stop[1]
|
||||
const deleteStopBtn = <DeleteStopButton onClick={this.props.onDeleteStop.bind(this, idx)} />
|
||||
const deleteStopBtn = <DeleteStopButton onClick={this.props.onDeleteStop?.bind(this, idx)} />
|
||||
|
||||
const dataProps = {
|
||||
'aria-label': "Input value",
|
||||
label: "Data value",
|
||||
value: dataLevel,
|
||||
onChange: newData => this.changeStop(idx, { zoom: zoomLevel, value: newData }, value)
|
||||
value: dataLevel as any,
|
||||
onChange: (newData: string | number | undefined) => this.changeStop(idx, { zoom: zoomLevel, value: newData as number }, value)
|
||||
}
|
||||
|
||||
let dataInput;
|
||||
if(this.props.value.type === "categorical") {
|
||||
if(this.props.value?.type === "categorical") {
|
||||
dataInput = <InputString {...dataProps} />
|
||||
}
|
||||
else {
|
||||
@@ -246,16 +254,6 @@ export default class DataProperty extends React.Component {
|
||||
</div>
|
||||
}
|
||||
|
||||
const errorKeyStart = `${fieldType}.${fieldName}.stops[${idx}]`;
|
||||
const foundErrors = Object.entries(errors).filter(([key, error]) => {
|
||||
return key.startsWith(errorKeyStart);
|
||||
});
|
||||
|
||||
const message = foundErrors.map(([key, error]) => {
|
||||
return error.message;
|
||||
}).join("");
|
||||
const error = message ? {message} : undefined;
|
||||
|
||||
return <tr key={key}>
|
||||
<td>
|
||||
{zoomInput}
|
||||
@@ -269,7 +267,7 @@ export default class DataProperty extends React.Component {
|
||||
fieldName={this.props.fieldName}
|
||||
fieldSpec={this.props.fieldSpec}
|
||||
value={value}
|
||||
onChange={(_, newValue) => this.changeStop(idx, {zoom: zoomLevel, value: dataLevel}, newValue)}
|
||||
onChange={(_, newValue) => this.changeStop(idx, {zoom: zoomLevel, value: dataLevel}, newValue as number)}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
@@ -289,14 +287,14 @@ export default class DataProperty extends React.Component {
|
||||
>
|
||||
<div className="maputnik-data-spec-property-input">
|
||||
<InputSelect
|
||||
value={this.props.value.type}
|
||||
value={this.props.value!.type}
|
||||
onChange={propVal => this.changeDataType(propVal)}
|
||||
title={"Select a type of data scale (default is 'categorical')."}
|
||||
options={this.getDataFunctionTypes(this.props.fieldSpec)}
|
||||
/>
|
||||
</div>
|
||||
</Block>
|
||||
{this.props.value.type !== "identity" &&
|
||||
{this.props.value?.type !== "identity" &&
|
||||
<Block
|
||||
label={"Base"}
|
||||
key="base"
|
||||
@@ -305,8 +303,8 @@ export default class DataProperty extends React.Component {
|
||||
<InputSpec
|
||||
fieldName={"base"}
|
||||
fieldSpec={latest.function.base}
|
||||
value={this.props.value.base}
|
||||
onChange={(_, newValue) => this.changeBase(newValue)}
|
||||
value={this.props.value?.base}
|
||||
onChange={(_, newValue) => this.changeBase(newValue as number)}
|
||||
/>
|
||||
</div>
|
||||
</Block>
|
||||
@@ -317,7 +315,7 @@ export default class DataProperty extends React.Component {
|
||||
>
|
||||
<div className="maputnik-data-spec-property-input">
|
||||
<InputString
|
||||
value={this.props.value.property}
|
||||
value={this.props.value?.property}
|
||||
title={"Input a data property to base styles off of."}
|
||||
onChange={propVal => this.changeDataProperty("property", propVal)}
|
||||
/>
|
||||
@@ -331,7 +329,7 @@ export default class DataProperty extends React.Component {
|
||||
<InputSpec
|
||||
fieldName={this.props.fieldName}
|
||||
fieldSpec={this.props.fieldSpec}
|
||||
value={this.props.value.default}
|
||||
value={this.props.value?.default}
|
||||
onChange={(_, propVal) => this.changeDataProperty("default", propVal)}
|
||||
/>
|
||||
</Block>
|
||||
@@ -344,7 +342,7 @@ export default class DataProperty extends React.Component {
|
||||
<tr>
|
||||
<th>Zoom</th>
|
||||
<th>Input value</th>
|
||||
<th rowSpan="2">Output value</th>
|
||||
<th rowSpan={2}>Output value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -357,7 +355,7 @@ export default class DataProperty extends React.Component {
|
||||
{dataFields &&
|
||||
<InputButton
|
||||
className="maputnik-add-stop"
|
||||
onClick={this.props.onAddStop.bind(this)}
|
||||
onClick={this.props.onAddStop?.bind(this)}
|
||||
>
|
||||
<svg style={{width:"14px", height:"14px", verticalAlign: "text-bottom"}} viewBox="0 0 24 24">
|
||||
<path fill="currentColor" d={mdiTableRowPlusAfter} />
|
||||
@@ -366,7 +364,7 @@ export default class DataProperty extends React.Component {
|
||||
}
|
||||
<InputButton
|
||||
className="maputnik-add-stop"
|
||||
onClick={this.props.onExpressionClick.bind(this)}
|
||||
onClick={this.props.onExpressionClick?.bind(this)}
|
||||
>
|
||||
<svg style={{width:"14px", height:"14px", verticalAlign: "text-bottom"}} viewBox="0 0 24 24">
|
||||
<path fill="currentColor" d={mdiFunctionVariant} />
|
||||
@@ -1,45 +1,46 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import {MdDelete, MdUndo} from 'react-icons/md'
|
||||
import stringifyPretty from 'json-stringify-pretty-compact'
|
||||
|
||||
import Block from './Block'
|
||||
import InputButton from './InputButton'
|
||||
import {MdDelete, MdUndo} from 'react-icons/md'
|
||||
import FieldString from './FieldString'
|
||||
|
||||
import labelFromFieldName from './_labelFromFieldName'
|
||||
import stringifyPretty from 'json-stringify-pretty-compact'
|
||||
import FieldJson from './FieldJson'
|
||||
|
||||
|
||||
export default class ExpressionProperty extends React.Component {
|
||||
static propTypes = {
|
||||
onDelete: PropTypes.func,
|
||||
fieldName: PropTypes.string,
|
||||
fieldType: PropTypes.string,
|
||||
fieldSpec: PropTypes.object,
|
||||
value: PropTypes.any,
|
||||
errors: PropTypes.object,
|
||||
onChange: PropTypes.func,
|
||||
onUndo: PropTypes.func,
|
||||
canUndo: PropTypes.func,
|
||||
onFocus: PropTypes.func,
|
||||
onBlur: PropTypes.func,
|
||||
}
|
||||
type ExpressionPropertyProps = {
|
||||
onDelete?(...args: unknown[]): unknown
|
||||
fieldName: string
|
||||
fieldType?: string
|
||||
fieldSpec?: object
|
||||
value?: any
|
||||
errors?: {[key: string]: any}
|
||||
onChange?(...args: unknown[]): unknown
|
||||
onUndo?(...args: unknown[]): unknown
|
||||
canUndo?(...args: unknown[]): unknown
|
||||
onFocus?(...args: unknown[]): unknown
|
||||
onBlur?(...args: unknown[]): unknown
|
||||
};
|
||||
|
||||
type ExpressionPropertyState = {
|
||||
jsonError: boolean
|
||||
};
|
||||
|
||||
export default class ExpressionProperty extends React.Component<ExpressionPropertyProps, ExpressionPropertyState> {
|
||||
static defaultProps = {
|
||||
errors: {},
|
||||
onFocus: () => {},
|
||||
onBlur: () => {},
|
||||
}
|
||||
|
||||
constructor (props) {
|
||||
super();
|
||||
constructor (props:ExpressionPropertyProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
jsonError: false,
|
||||
};
|
||||
}
|
||||
|
||||
onJSONInvalid = (err) => {
|
||||
onJSONInvalid = (_err: Error) => {
|
||||
this.setState({
|
||||
jsonError: true,
|
||||
})
|
||||
@@ -82,11 +83,11 @@ export default class ExpressionProperty extends React.Component {
|
||||
|
||||
const fieldKey = fieldType === undefined ? fieldName : `${fieldType}.${fieldName}`;
|
||||
|
||||
const fieldError = errors[fieldKey];
|
||||
const fieldError = errors![fieldKey];
|
||||
const errorKeyStart = `${fieldKey}[`;
|
||||
const foundErrors = [];
|
||||
|
||||
function getValue (data) {
|
||||
function getValue(data: any) {
|
||||
return stringifyPretty(data, {indent: 2, maxLength: 38})
|
||||
}
|
||||
|
||||
@@ -94,11 +95,11 @@ export default class ExpressionProperty extends React.Component {
|
||||
foundErrors.push({message: "Invalid JSON"});
|
||||
}
|
||||
else {
|
||||
Object.entries(errors)
|
||||
.filter(([key, error]) => {
|
||||
Object.entries(errors!)
|
||||
.filter(([key, _error]) => {
|
||||
return key.startsWith(errorKeyStart);
|
||||
})
|
||||
.forEach(([key, error]) => {
|
||||
.forEach(([_key, error]) => {
|
||||
return foundErrors.push(error);
|
||||
})
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import React from 'react'
|
||||
|
||||
import SpecField from './SpecField'
|
||||
import SpecField, {SpecFieldProps} from './SpecField'
|
||||
import FunctionButtons from './_FunctionButtons'
|
||||
|
||||
import labelFromFieldName from './_labelFromFieldName'
|
||||
|
||||
|
||||
type SpecPropertyProps = {
|
||||
type SpecPropertyProps = SpecFieldProps & {
|
||||
onZoomClick(...args: unknown[]): unknown
|
||||
onDataClick(...args: unknown[]): unknown
|
||||
fieldName?: string
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import {mdiFunctionVariant, mdiTableRowPlusAfter} from '@mdi/js';
|
||||
import {latest} from '@maplibre/maplibre-gl-style-spec'
|
||||
|
||||
@@ -7,7 +6,6 @@ import InputButton from './InputButton'
|
||||
import InputSpec from './InputSpec'
|
||||
import InputNumber from './InputNumber'
|
||||
import InputSelect from './InputSelect'
|
||||
import FieldDocLabel from './FieldDocLabel'
|
||||
import Block from './Block'
|
||||
|
||||
import DeleteStopButton from './_DeleteStopButton'
|
||||
@@ -22,12 +20,12 @@ import sortNumerically from '../libs/sort-numerically'
|
||||
*
|
||||
* When the stops are reordered the references are also updated (see this.orderStops) this allows React to use the same key for the element and keep keyboard focus.
|
||||
*/
|
||||
function setStopRefs(props, state) {
|
||||
function setStopRefs(props: ZoomPropertyProps, state: ZoomPropertyState) {
|
||||
// This is initialsed below only if required to improved performance.
|
||||
let newRefs;
|
||||
let newRefs: {[key: number]: string} = {};
|
||||
|
||||
if(props.value && props.value.stops) {
|
||||
props.value.stops.forEach((val, idx) => {
|
||||
if(props.value && (props.value as ZoomWithStops).stops) {
|
||||
(props.value as ZoomWithStops).stops.forEach((_val, idx: number) => {
|
||||
if(!state.refs.hasOwnProperty(idx)) {
|
||||
if(!newRefs) {
|
||||
newRefs = {...state};
|
||||
@@ -40,32 +38,39 @@ function setStopRefs(props, state) {
|
||||
return newRefs;
|
||||
}
|
||||
|
||||
type ZoomWithStops = {
|
||||
stops: [number | undefined, number][]
|
||||
base?: number
|
||||
}
|
||||
|
||||
export default class ZoomProperty extends React.Component {
|
||||
static propTypes = {
|
||||
onChange: PropTypes.func,
|
||||
onDeleteStop: PropTypes.func,
|
||||
onAddStop: PropTypes.func,
|
||||
onExpressionClick: PropTypes.func,
|
||||
fieldType: PropTypes.string,
|
||||
fieldName: PropTypes.string,
|
||||
fieldSpec: PropTypes.object,
|
||||
errors: PropTypes.object,
|
||||
value: PropTypes.oneOfType([
|
||||
PropTypes.object,
|
||||
PropTypes.string,
|
||||
PropTypes.number,
|
||||
PropTypes.bool,
|
||||
PropTypes.array
|
||||
]),
|
||||
|
||||
type ZoomPropertyProps = {
|
||||
onChange?(...args: unknown[]): unknown
|
||||
onChangeToDataFunction?(...args: unknown[]): unknown
|
||||
onDeleteStop?(...args: unknown[]): unknown
|
||||
onAddStop?(...args: unknown[]): unknown
|
||||
onExpressionClick?(...args: unknown[]): unknown
|
||||
fieldType?: string
|
||||
fieldName: string
|
||||
fieldSpec?: {
|
||||
"property-type"?: string
|
||||
"function-type"?: string
|
||||
}
|
||||
errors?: object
|
||||
value?: ZoomWithStops
|
||||
};
|
||||
|
||||
type ZoomPropertyState = {
|
||||
refs: {[key: number]: string}
|
||||
}
|
||||
|
||||
export default class ZoomProperty extends React.Component<ZoomPropertyProps, ZoomPropertyState> {
|
||||
static defaultProps = {
|
||||
errors: {},
|
||||
}
|
||||
|
||||
state = {
|
||||
refs: {}
|
||||
refs: {} as {[key: number]: string}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
@@ -78,7 +83,7 @@ export default class ZoomProperty extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps(props, state) {
|
||||
static getDerivedStateFromProps(props: ZoomPropertyProps, state: ZoomPropertyState) {
|
||||
const newRefs = setStopRefs(props, state);
|
||||
if(newRefs) {
|
||||
return {
|
||||
@@ -89,7 +94,7 @@ export default class ZoomProperty extends React.Component {
|
||||
}
|
||||
|
||||
// Order the stops altering the refs to reflect their new position.
|
||||
orderStopsByZoom(stops) {
|
||||
orderStopsByZoom(stops: ZoomWithStops["stops"]) {
|
||||
const mappedWithRef = stops
|
||||
.map((stop, idx) => {
|
||||
return {
|
||||
@@ -98,10 +103,10 @@ export default class ZoomProperty extends React.Component {
|
||||
}
|
||||
})
|
||||
// Sort by zoom
|
||||
.sort((a, b) => sortNumerically(a.data[0], b.data[0]));
|
||||
.sort((a, b) => sortNumerically(a.data[0]!, b.data[0]!));
|
||||
|
||||
// Fetch the new position of the stops
|
||||
const newRefs = {};
|
||||
const newRefs: {[key:number]: string} = {};
|
||||
mappedWithRef
|
||||
.forEach((stop, idx) =>{
|
||||
newRefs[idx] = stop.ref;
|
||||
@@ -114,20 +119,20 @@ export default class ZoomProperty extends React.Component {
|
||||
return mappedWithRef.map((item) => item.data);
|
||||
}
|
||||
|
||||
changeZoomStop(changeIdx, stopData, value) {
|
||||
const stops = this.props.value.stops.slice(0);
|
||||
changeZoomStop(changeIdx: number, stopData: number | undefined, value: number) {
|
||||
const stops = (this.props.value as ZoomWithStops).stops.slice(0);
|
||||
stops[changeIdx] = [stopData, value];
|
||||
|
||||
const orderedStops = this.orderStopsByZoom(stops);
|
||||
|
||||
const changedValue = {
|
||||
...this.props.value,
|
||||
...this.props.value as ZoomWithStops,
|
||||
stops: orderedStops
|
||||
}
|
||||
this.props.onChange(this.props.fieldName, changedValue)
|
||||
this.props.onChange!(this.props.fieldName, changedValue)
|
||||
}
|
||||
|
||||
changeBase(newValue) {
|
||||
changeBase(newValue: number | undefined) {
|
||||
const changedValue = {
|
||||
...this.props.value,
|
||||
base: newValue
|
||||
@@ -136,33 +141,21 @@ export default class ZoomProperty extends React.Component {
|
||||
if (changedValue.base === undefined) {
|
||||
delete changedValue["base"];
|
||||
}
|
||||
this.props.onChange(this.props.fieldName, changedValue)
|
||||
this.props.onChange!(this.props.fieldName, changedValue)
|
||||
}
|
||||
|
||||
changeDataType = (type) => {
|
||||
if (type !== "interpolate") {
|
||||
changeDataType = (type: string) => {
|
||||
if (type !== "interpolate" && this.props.onChangeToDataFunction) {
|
||||
this.props.onChangeToDataFunction(type);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {fieldName, fieldType, errors} = this.props;
|
||||
|
||||
const zoomFields = this.props.value.stops.map((stop, idx) => {
|
||||
const zoomFields = this.props.value?.stops.map((stop, idx) => {
|
||||
const zoomLevel = stop[0]
|
||||
const key = this.state.refs[idx];
|
||||
const value = stop[1]
|
||||
const deleteStopBtn= <DeleteStopButton onClick={this.props.onDeleteStop.bind(this, idx)} />
|
||||
|
||||
const errorKeyStart = `${fieldType}.${fieldName}.stops[${idx}]`;
|
||||
const foundErrors = Object.entries(errors).filter(([key, error]) => {
|
||||
return key.startsWith(errorKeyStart);
|
||||
});
|
||||
|
||||
const message = foundErrors.map(([key, error]) => {
|
||||
return error.message;
|
||||
}).join("");
|
||||
const error = message ? {message} : undefined;
|
||||
const deleteStopBtn= <DeleteStopButton onClick={this.props.onDeleteStop?.bind(this, idx)} />
|
||||
|
||||
return <tr
|
||||
key={key}
|
||||
@@ -180,9 +173,9 @@ export default class ZoomProperty extends React.Component {
|
||||
<InputSpec
|
||||
aria-label="Output value"
|
||||
fieldName={this.props.fieldName}
|
||||
fieldSpec={this.props.fieldSpec}
|
||||
fieldSpec={this.props.fieldSpec as any}
|
||||
value={value}
|
||||
onChange={(_, newValue) => this.changeZoomStop(idx, zoomLevel, newValue)}
|
||||
onChange={(_, newValue) => this.changeZoomStop(idx, zoomLevel, newValue as number)}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
@@ -204,7 +197,7 @@ export default class ZoomProperty extends React.Component {
|
||||
value={"interpolate"}
|
||||
onChange={propVal => this.changeDataType(propVal)}
|
||||
title={"Select a type of data scale (default is 'categorical')."}
|
||||
options={this.getDataFunctionTypes(this.props.fieldSpec)}
|
||||
options={this.getDataFunctionTypes(this.props.fieldSpec!)}
|
||||
/>
|
||||
</div>
|
||||
</Block>
|
||||
@@ -215,8 +208,8 @@ export default class ZoomProperty extends React.Component {
|
||||
<InputSpec
|
||||
fieldName={"base"}
|
||||
fieldSpec={latest.function.base}
|
||||
value={this.props.value.base}
|
||||
onChange={(_, newValue) => this.changeBase(newValue)}
|
||||
value={this.props.value?.base}
|
||||
onChange={(_, newValue) => this.changeBase(newValue as number | undefined)}
|
||||
/>
|
||||
</div>
|
||||
</Block>
|
||||
@@ -226,7 +219,7 @@ export default class ZoomProperty extends React.Component {
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Zoom</th>
|
||||
<th rowSpan="2">Output value</th>
|
||||
<th rowSpan={2}>Output value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -237,7 +230,7 @@ export default class ZoomProperty extends React.Component {
|
||||
<div className="maputnik-toolbox">
|
||||
<InputButton
|
||||
className="maputnik-add-stop"
|
||||
onClick={this.props.onAddStop.bind(this)}
|
||||
onClick={this.props.onAddStop?.bind(this)}
|
||||
>
|
||||
<svg style={{width:"14px", height:"14px", verticalAlign: "text-bottom"}} viewBox="0 0 24 24">
|
||||
<path fill="currentColor" d={mdiTableRowPlusAfter} />
|
||||
@@ -245,7 +238,7 @@ export default class ZoomProperty extends React.Component {
|
||||
</InputButton>
|
||||
<InputButton
|
||||
className="maputnik-add-stop"
|
||||
onClick={this.props.onExpressionClick.bind(this)}
|
||||
onClick={this.props.onExpressionClick?.bind(this)}
|
||||
>
|
||||
<svg style={{width:"14px", height:"14px", verticalAlign: "text-bottom"}} viewBox="0 0 24 24">
|
||||
<path fill="currentColor" d={mdiFunctionVariant} />
|
||||
@@ -257,7 +250,10 @@ export default class ZoomProperty extends React.Component {
|
||||
</div>
|
||||
}
|
||||
|
||||
getDataFunctionTypes(fieldSpec) {
|
||||
getDataFunctionTypes(fieldSpec: {
|
||||
"property-type"?: string
|
||||
"function-type"?: string
|
||||
}) {
|
||||
if (fieldSpec['property-type'] === 'data-driven') {
|
||||
return ["interpolate", "categorical", "interval", "exponential", "identity"];
|
||||
}
|
||||
@@ -265,5 +261,4 @@ export default class ZoomProperty extends React.Component {
|
||||
return ["interpolate"];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
export function formatLayerId (id) {
|
||||
return id === "" ? "[empty_string]" : `'${id}'`;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export function formatLayerId (id: string | undefined) {
|
||||
return id === "" ? "[empty_string]" : `'${id}'`;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* If we don't have a default value just make one up
|
||||
*/
|
||||
export function findDefaultFromSpec (spec) {
|
||||
export function findDefaultFromSpec(spec: { type: 'string' | 'color' | 'boolean' | 'array', default?: any }) {
|
||||
if (spec.hasOwnProperty('default')) {
|
||||
return spec.default;
|
||||
}
|
||||
Reference in New Issue
Block a user