mirror of
https://github.com/maputnik/editor.git
synced 2026-06-08 08:17:27 +00:00
Migration of jsx files to tsx 2 (#850)
This is to continue the work of migrating all the jsx files into tsx files. The MO is basically described here: #848. About 7 files to go...
This commit is contained in:
@@ -0,0 +1,264 @@
|
||||
import React from 'react'
|
||||
import {mdiFunctionVariant, mdiTableRowPlusAfter} from '@mdi/js';
|
||||
import {latest} from '@maplibre/maplibre-gl-style-spec'
|
||||
|
||||
import InputButton from './InputButton'
|
||||
import InputSpec from './InputSpec'
|
||||
import InputNumber from './InputNumber'
|
||||
import InputSelect from './InputSelect'
|
||||
import Block from './Block'
|
||||
|
||||
import DeleteStopButton from './_DeleteStopButton'
|
||||
import labelFromFieldName from './_labelFromFieldName'
|
||||
|
||||
import docUid from '../libs/document-uid'
|
||||
import sortNumerically from '../libs/sort-numerically'
|
||||
|
||||
|
||||
/**
|
||||
* We cache a reference for each stop by its index.
|
||||
*
|
||||
* 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: ZoomPropertyProps, state: ZoomPropertyState) {
|
||||
// This is initialsed below only if required to improved performance.
|
||||
let newRefs: {[key: number]: string} = {};
|
||||
|
||||
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};
|
||||
}
|
||||
newRefs[idx] = docUid("stop-");
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return newRefs;
|
||||
}
|
||||
|
||||
type ZoomWithStops = {
|
||||
stops: [number | undefined, number][]
|
||||
base?: number
|
||||
}
|
||||
|
||||
|
||||
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: {} as {[key: number]: string}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const newRefs = setStopRefs(this.props, this.state);
|
||||
|
||||
if(newRefs) {
|
||||
this.setState({
|
||||
refs: newRefs
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps(props: ZoomPropertyProps, state: ZoomPropertyState) {
|
||||
const newRefs = setStopRefs(props, state);
|
||||
if(newRefs) {
|
||||
return {
|
||||
refs: newRefs
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Order the stops altering the refs to reflect their new position.
|
||||
orderStopsByZoom(stops: ZoomWithStops["stops"]) {
|
||||
const mappedWithRef = stops
|
||||
.map((stop, idx) => {
|
||||
return {
|
||||
ref: this.state.refs[idx],
|
||||
data: stop
|
||||
}
|
||||
})
|
||||
// Sort by zoom
|
||||
.sort((a, b) => sortNumerically(a.data[0]!, b.data[0]!));
|
||||
|
||||
// Fetch the new position of the stops
|
||||
const newRefs: {[key:number]: string} = {};
|
||||
mappedWithRef
|
||||
.forEach((stop, idx) =>{
|
||||
newRefs[idx] = stop.ref;
|
||||
})
|
||||
|
||||
this.setState({
|
||||
refs: newRefs
|
||||
});
|
||||
|
||||
return mappedWithRef.map((item) => item.data);
|
||||
}
|
||||
|
||||
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 as ZoomWithStops,
|
||||
stops: orderedStops
|
||||
}
|
||||
this.props.onChange!(this.props.fieldName, changedValue)
|
||||
}
|
||||
|
||||
changeBase(newValue: number | undefined) {
|
||||
const changedValue = {
|
||||
...this.props.value,
|
||||
base: newValue
|
||||
}
|
||||
|
||||
if (changedValue.base === undefined) {
|
||||
delete changedValue["base"];
|
||||
}
|
||||
this.props.onChange!(this.props.fieldName, changedValue)
|
||||
}
|
||||
|
||||
changeDataType = (type: string) => {
|
||||
if (type !== "interpolate" && this.props.onChangeToDataFunction) {
|
||||
this.props.onChangeToDataFunction(type);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
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)} />
|
||||
|
||||
return <tr
|
||||
key={key}
|
||||
>
|
||||
<td>
|
||||
<InputNumber
|
||||
aria-label="Zoom"
|
||||
value={zoomLevel}
|
||||
onChange={changedStop => this.changeZoomStop(idx, changedStop, value)}
|
||||
min={0}
|
||||
max={22}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<InputSpec
|
||||
aria-label="Output value"
|
||||
fieldName={this.props.fieldName}
|
||||
fieldSpec={this.props.fieldSpec as any}
|
||||
value={value}
|
||||
onChange={(_, newValue) => this.changeZoomStop(idx, zoomLevel, newValue as number)}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
{deleteStopBtn}
|
||||
</td>
|
||||
</tr>
|
||||
});
|
||||
|
||||
// return <div className="maputnik-zoom-spec-property">
|
||||
return <div className="maputnik-data-spec-block">
|
||||
<fieldset className="maputnik-data-spec-property">
|
||||
<legend>{labelFromFieldName(this.props.fieldName)}</legend>
|
||||
<div className="maputnik-data-fieldset-inner">
|
||||
<Block
|
||||
label={"Function"}
|
||||
>
|
||||
<div className="maputnik-data-spec-property-input">
|
||||
<InputSelect
|
||||
value={"interpolate"}
|
||||
onChange={propVal => this.changeDataType(propVal)}
|
||||
title={"Select a type of data scale (default is 'categorical')."}
|
||||
options={this.getDataFunctionTypes(this.props.fieldSpec!)}
|
||||
/>
|
||||
</div>
|
||||
</Block>
|
||||
<Block
|
||||
label={"Base"}
|
||||
>
|
||||
<div className="maputnik-data-spec-property-input">
|
||||
<InputSpec
|
||||
fieldName={"base"}
|
||||
fieldSpec={latest.function.base}
|
||||
value={this.props.value?.base}
|
||||
onChange={(_, newValue) => this.changeBase(newValue as number | undefined)}
|
||||
/>
|
||||
</div>
|
||||
</Block>
|
||||
<div className="maputnik-function-stop">
|
||||
<table className="maputnik-function-stop-table maputnik-function-stop-table--zoom">
|
||||
<caption>Stops</caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Zoom</th>
|
||||
<th rowSpan={2}>Output value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{zoomFields}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div className="maputnik-toolbox">
|
||||
<InputButton
|
||||
className="maputnik-add-stop"
|
||||
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} />
|
||||
</svg> Add stop
|
||||
</InputButton>
|
||||
<InputButton
|
||||
className="maputnik-add-stop"
|
||||
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} />
|
||||
</svg> Convert to expression
|
||||
</InputButton>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
}
|
||||
|
||||
getDataFunctionTypes(fieldSpec: {
|
||||
"property-type"?: string
|
||||
"function-type"?: string
|
||||
}) {
|
||||
if (fieldSpec['property-type'] === 'data-driven') {
|
||||
return ["interpolate", "categorical", "interval", "exponential", "identity"];
|
||||
}
|
||||
else {
|
||||
return ["interpolate"];
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user