diff --git a/src/components/fields/FunctionSpecField.jsx b/src/components/fields/FunctionSpecField.jsx
index d074c892..9772a33d 100644
--- a/src/components/fields/FunctionSpecField.jsx
+++ b/src/components/fields/FunctionSpecField.jsx
@@ -1,24 +1,10 @@
import React from 'react'
-import Color from 'color'
-
-import Button from '../Button'
-import SpecField from './SpecField'
-import NumberInput from '../inputs/NumberInput'
-import StringInput from '../inputs/StringInput'
-import SelectInput from '../inputs/SelectInput'
-import DocLabel from './DocLabel'
-import InputBlock from '../inputs/InputBlock'
-
-import AddIcon from 'react-icons/lib/md/add-circle-outline'
-import DeleteIcon from 'react-icons/lib/md/delete'
-import FunctionIcon from 'react-icons/lib/md/functions'
-import MdInsertChart from 'react-icons/lib/md/insert-chart'
-
import PropTypes from 'prop-types'
-import capitalize from 'lodash.capitalize'
-import docUid from '../../libs/document-uid'
-import sortNumerically from '../../libs/sort-numerically'
+import SpecProperty from './_SpecProperty'
+import DataProperty from './_DataProperty'
+import ZoomProperty from './_ZoomProperty'
+
function isZoomField(value) {
return typeof value === 'object' && value.stops && typeof value.property === 'undefined'
@@ -46,70 +32,6 @@ export default class FunctionSpecProperty extends React.Component {
]),
}
- constructor() {
- super()
- this.state = {
- refs: {}
- }
- }
-
- /**
- * 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.
- */
- setStopRefs(props) {
- // This is initialsed below only if required to improved performance.
- let newRefs;
-
- if(props.value && props.value.stops) {
- props.value.stops.forEach((val, idx) => {
- if(!this.state.refs.hasOwnProperty(idx)) {
- if(!newRefs) {
- newRefs = {...this.state.refs};
- }
- newRefs[idx] = docUid("stop-");
- }
- })
- }
-
- if(newRefs) {
- this.setState({
- refs: newRefs
- })
- }
- }
-
- componentWillReceiveProps(nextProps) {
- this.setStopRefs(nextProps);
- }
-
- // Order the stops altering the refs to reflect their new position.
- orderStopsByZoom(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 = {};
- mappedWithRef
- .forEach((stop, idx) =>{
- newRefs[idx] = stop.ref;
- })
-
- this.setState({
- refs: newRefs
- });
-
- return mappedWithRef.map((item) => item.data);
- }
-
addStop() {
const stops = this.props.value.stops.slice(0)
const lastStop = stops[stops.length - 1]
@@ -157,25 +79,6 @@ export default class FunctionSpecProperty extends React.Component {
this.props.onChange(this.props.fieldName, zoomFunc)
}
- getFieldFunctionType(fieldSpec) {
- if (fieldSpec.function === "interpolated") {
- return "exponential"
- }
- if (fieldSpec.type === "number") {
- return "interval"
- }
- return "categorical"
- }
-
- getDataFunctionTypes(functionType) {
- if (functionType === "interpolated") {
- return ["categorical", "interval", "exponential"]
- }
- else {
- return ["categorical", "interval"]
- }
- }
-
makeDataFunction() {
const dataFunc = {
property: "",
@@ -188,264 +91,50 @@ export default class FunctionSpecProperty extends React.Component {
this.props.onChange(this.props.fieldName, dataFunc)
}
- changeStop(changeIdx, stopData, value) {
- const stops = this.props.value.stops.slice(0);
- stops[changeIdx] = [stopData, value];
-
- const orderedStops = this.orderStopsByZoom(stops);
-
- const changedValue = {
- ...this.props.value,
- stops: orderedStops
- }
- this.props.onChange(this.props.fieldName, changedValue)
- }
-
- changeDataProperty(propName, propVal) {
- if (propVal) {
- this.props.value[propName] = propVal
- }
- else {
- delete this.props.value[propName]
- }
- this.props.onChange(this.props.fieldName, this.props.value)
- }
-
- renderDataProperty() {
- if (typeof this.props.value.type === "undefined") {
- this.props.value.type = this.getFieldFunctionType(this.props.fieldSpec)
- }
- const dataFields = this.props.value.stops.map((stop, idx) => {
- const zoomLevel = stop[0].zoom
- const dataLevel = stop[0].value
- const value = stop[1]
- const deleteStopBtn =
-
- const dataProps = {
- label: "Data value",
- value: dataLevel,
- onChange: newData => this.changeStop(idx, { zoom: zoomLevel, value: newData }, value)
- }
- const dataInput = this.props.value.type === "categorical" ? :
-
- return
-
- this.changeStop(idx, {zoom: newZoom, value: dataLevel}, value)}
- min={0}
- max={22}
- />
-
-
- {dataInput}
-
-
- this.changeStop(idx, {zoom: zoomLevel, value: dataLevel}, newValue)}
- />
-
-
- })
-
- return
-
-
-
-
-
- this.changeDataProperty("property", propVal)}
- />
-
-
-
-
-
- this.changeDataProperty("type", propVal)}
- options={this.getDataFunctionTypes(this.props.fieldSpec.function)}
- />
-
-
-
-
-
- this.changeDataProperty("default", propVal)}
- />
-
-
-
-
- {dataFields}
-
-
- }
-
- renderZoomProperty() {
- 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=
-
- return
-
-
- this.changeStop(idx, changedStop, value)}
- min={0}
- max={22}
- />
-
-
- this.changeStop(idx, zoomLevel, newValue)}
- />
-
-
-
- })
-
- return
- {zoomFields}
-
-
- }
-
- renderProperty() {
- const functionBtn =
- return
-
-
- }
-
render() {
const propClass = this.props.fieldSpec.default === this.props.value ? "maputnik-default-property" : "maputnik-modified-property"
- let specField
+ let specField;
+
if (isZoomField(this.props.value)) {
- specField = this.renderZoomProperty()
+ specField = (
+
+ )
}
else if (isDataField(this.props.value)) {
- specField = this.renderDataProperty()
+ specField = (
+
+ )
}
else {
- specField = this.renderProperty()
+ specField = (
+
+ )
}
+
return
{specField}
}
}
-class MakeFunctionButtons extends React.Component {
- static propTypes = {
- fieldSpec: PropTypes.object,
- onZoomClick: PropTypes.func,
- onDataClick: PropTypes.func,
- }
-
- render() {
- let makeZoomButton, makeDataButton
- if (this.props.fieldSpec['zoom-function']) {
- makeZoomButton =
-
- if (this.props.fieldSpec['property-function'] && ['piecewise-constant', 'interpolated'].indexOf(this.props.fieldSpec['function']) !== -1) {
- makeDataButton =
- }
- return {makeDataButton}{makeZoomButton}
- }
- else {
- return null
- }
- }
-}
-
-class DeleteStopButton extends React.Component {
- static propTypes = {
- onClick: PropTypes.func,
- }
-
- render() {
- return
- }
-}
-
-function labelFromFieldName(fieldName) {
- let label = fieldName.split('-').slice(1).join(' ')
- return capitalize(label)
-}
diff --git a/src/components/fields/_DataProperty.jsx b/src/components/fields/_DataProperty.jsx
new file mode 100644
index 00000000..c13d8285
--- /dev/null
+++ b/src/components/fields/_DataProperty.jsx
@@ -0,0 +1,171 @@
+import React from 'react'
+import PropTypes from 'prop-types'
+
+import Button from '../Button'
+import SpecField from './SpecField'
+import NumberInput from '../inputs/NumberInput'
+import StringInput from '../inputs/StringInput'
+import SelectInput from '../inputs/SelectInput'
+import DocLabel from './DocLabel'
+import InputBlock from '../inputs/InputBlock'
+
+import labelFromFieldName from './_labelFromFieldName'
+import DeleteStopButton from './_DeleteStopButton'
+
+
+export default class DataProperty extends React.Component {
+ static propTypes = {
+ value: PropTypes.oneOfType([
+ PropTypes.object,
+ PropTypes.string,
+ PropTypes.number,
+ PropTypes.bool,
+ PropTypes.array
+ ]),
+ }
+
+ getFieldFunctionType(fieldSpec) {
+ if (fieldSpec.function === "interpolated") {
+ return "exponential"
+ }
+ if (fieldSpec.type === "number") {
+ return "interval"
+ }
+ return "categorical"
+ }
+
+ getDataFunctionTypes(functionType) {
+ if (functionType === "interpolated") {
+ return ["categorical", "interval", "exponential"]
+ }
+ else {
+ return ["categorical", "interval"]
+ }
+ }
+
+
+ changeStop(changeIdx, stopData, value) {
+ const stops = this.props.value.stops.slice(0)
+ stops[changeIdx] = [stopData, value]
+ const changedValue = {
+ ...this.props.value,
+ stops: stops,
+ }
+ this.props.onChange(this.props.fieldName, changedValue)
+ }
+
+ changeDataProperty(propName, propVal) {
+ if (propVal) {
+ this.props.value[propName] = propVal
+ }
+ else {
+ delete this.props.value[propName]
+ }
+ this.props.onChange(this.props.fieldName, this.props.value)
+ }
+
+ render() {
+ if (typeof this.props.value.type === "undefined") {
+ this.props.value.type = this.getFieldFunctionType(this.props.fieldSpec)
+ }
+
+ const dataFields = this.props.value.stops.map((stop, idx) => {
+ const zoomLevel = stop[0].zoom
+ const dataLevel = stop[0].value
+ const value = stop[1]
+ const deleteStopBtn =
+
+ const dataProps = {
+ label: "Data value",
+ value: dataLevel,
+ onChange: newData => this.changeStop(idx, { zoom: zoomLevel, value: newData }, value)
+ }
+
+ let dataInput;
+ if(this.props.value.type === "categorical") {
+ dataInput =
+ }
+ else {
+ dataInput =
+ }
+
+ return
+
+ this.changeStop(idx, {zoom: newZoom, value: dataLevel}, value)}
+ min={0}
+ max={22}
+ />
+
+
+ {dataInput}
+
+
+ this.changeStop(idx, {zoom: zoomLevel, value: dataLevel}, newValue)}
+ />
+
+
+ })
+
+ return
+
+
+
+
+
+ this.changeDataProperty("property", propVal)}
+ />
+
+
+
+
+
+ this.changeDataProperty("type", propVal)}
+ options={this.getDataFunctionTypes(this.props.fieldSpec.function)}
+ />
+
+
+
+
+
+ this.changeDataProperty("default", propVal)}
+ />
+
+
+
+
+ {dataFields}
+
+
+ }
+}
diff --git a/src/components/fields/_DeleteStopButton.jsx b/src/components/fields/_DeleteStopButton.jsx
new file mode 100644
index 00000000..ef64f434
--- /dev/null
+++ b/src/components/fields/_DeleteStopButton.jsx
@@ -0,0 +1,25 @@
+import React from 'react'
+import PropTypes from 'prop-types'
+
+import DocLabel from './DocLabel'
+import Button from '../Button'
+import DeleteIcon from 'react-icons/lib/md/delete'
+
+
+export default class DeleteStopButton extends React.Component {
+ static propTypes = {
+ onClick: PropTypes.func,
+ }
+
+ render() {
+ return
+ }
+}
diff --git a/src/components/fields/_FunctionButtons.jsx b/src/components/fields/_FunctionButtons.jsx
new file mode 100644
index 00000000..9e663985
--- /dev/null
+++ b/src/components/fields/_FunctionButtons.jsx
@@ -0,0 +1,49 @@
+import React from 'react'
+import PropTypes from 'prop-types'
+
+import DocLabel from './DocLabel'
+import Button from '../Button'
+import FunctionIcon from 'react-icons/lib/md/functions'
+import MdInsertChart from 'react-icons/lib/md/insert-chart'
+
+
+export default class FunctionButtons extends React.Component {
+ static propTypes = {
+ fieldSpec: PropTypes.object,
+ onZoomClick: PropTypes.func,
+ onDataClick: PropTypes.func,
+ }
+
+ render() {
+ let makeZoomButton, makeDataButton
+ if (this.props.fieldSpec['zoom-function']) {
+ makeZoomButton =
+
+ if (this.props.fieldSpec['property-function'] && ['piecewise-constant', 'interpolated'].indexOf(this.props.fieldSpec['function']) !== -1) {
+ makeDataButton =
+ }
+ return {makeDataButton}{makeZoomButton}
+ }
+ else {
+ return null
+ }
+ }
+}
diff --git a/src/components/fields/_SpecProperty.jsx b/src/components/fields/_SpecProperty.jsx
new file mode 100644
index 00000000..583bffa7
--- /dev/null
+++ b/src/components/fields/_SpecProperty.jsx
@@ -0,0 +1,32 @@
+import React from 'react'
+import PropTypes from 'prop-types'
+
+import SpecField from './SpecField'
+import FunctionButtons from './_FunctionButtons'
+import InputBlock from '../inputs/InputBlock'
+
+import labelFromFieldName from './_labelFromFieldName'
+
+
+export default class SpecProperty extends React.Component {
+ static propTypes = {
+ onZoomClick: PropTypes.func.isRequired,
+ onDataClick: PropTypes.func.isRequired
+ }
+
+ render() {
+ const functionBtn =
+
+ return
+
+
+ }
+}
diff --git a/src/components/fields/_ZoomProperty.jsx b/src/components/fields/_ZoomProperty.jsx
new file mode 100644
index 00000000..e670af38
--- /dev/null
+++ b/src/components/fields/_ZoomProperty.jsx
@@ -0,0 +1,149 @@
+import React from 'react'
+import PropTypes from 'prop-types'
+
+import Button from '../Button'
+import SpecField from './SpecField'
+import NumberInput from '../inputs/NumberInput'
+import InputBlock from '../inputs/InputBlock'
+
+import DeleteStopButton from './_DeleteStopButton'
+import labelFromFieldName from './_labelFromFieldName'
+
+import docUid from '../../libs/document-uid'
+import sortNumerically from '../../libs/sort-numerically'
+
+
+export default class ZoomProperty extends React.Component {
+ static propTypes = {
+ }
+
+
+ constructor() {
+ super()
+ this.state = {
+ refs: {}
+ }
+ }
+
+ componentWillMount() {
+ this.setState({
+ refs: this.setStopRefs(this.props)
+ })
+ }
+
+ /**
+ * 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.
+ */
+ setStopRefs(props) {
+ // This is initialsed below only if required to improved performance.
+ let newRefs;
+
+ if(props.value && props.value.stops) {
+ props.value.stops.forEach((val, idx) => {
+ if(!this.state.refs.hasOwnProperty(idx)) {
+ if(!newRefs) {
+ newRefs = {...this.state.refs};
+ }
+ newRefs[idx] = docUid("stop-");
+ }
+ })
+ }
+
+ return newRefs;
+ }
+
+ componentWillReceiveProps(nextProps) {
+ const newRefs = this.setStopRefs(nextProps);
+ if(newRefs) {
+ this.setState({
+ refs: newRefs
+ })
+ }
+ }
+
+ // Order the stops altering the refs to reflect their new position.
+ orderStopsByZoom(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 = {};
+ mappedWithRef
+ .forEach((stop, idx) =>{
+ newRefs[idx] = stop.ref;
+ })
+
+ this.setState({
+ refs: newRefs
+ });
+
+ return mappedWithRef.map((item) => item.data);
+ }
+
+ changeZoomStop(changeIdx, stopData, value) {
+ const stops = this.props.value.stops.slice(0);
+ stops[changeIdx] = [stopData, value];
+
+ const orderedStops = this.orderStopsByZoom(stops);
+
+ const changedValue = {
+ ...this.props.value,
+ stops: orderedStops
+ }
+ this.props.onChange(this.props.fieldName, changedValue)
+ }
+
+ 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=
+
+ return
+
+
+ this.changeZoomStop(idx, changedStop, value)}
+ min={0}
+ max={22}
+ />
+
+
+ this.changeZoomStop(idx, zoomLevel, newValue)}
+ />
+
+
+
+ });
+
+ return
+ {zoomFields}
+
+
+ }
+}
diff --git a/src/components/fields/_labelFromFieldName.js b/src/components/fields/_labelFromFieldName.js
new file mode 100644
index 00000000..fea405f6
--- /dev/null
+++ b/src/components/fields/_labelFromFieldName.js
@@ -0,0 +1,6 @@
+import capitalize from 'lodash.capitalize'
+
+export default function labelFromFieldName(fieldName) {
+ let label = fieldName.split('-').slice(1).join(' ')
+ return capitalize(label)
+}
diff --git a/src/components/inputs/InputBlock.jsx b/src/components/inputs/InputBlock.jsx
index 37c1d0ab..8fff7670 100644
--- a/src/components/inputs/InputBlock.jsx
+++ b/src/components/inputs/InputBlock.jsx
@@ -12,7 +12,7 @@ class InputBlock extends React.Component {
]).isRequired,
doc: PropTypes.string,
action: PropTypes.element,
- children: PropTypes.element.isRequired,
+ children: PropTypes.node.isRequired,
style: PropTypes.object,
onChange: PropTypes.func,
}