mirror of
https://github.com/a-nyx/maputnik-with-pmtiles.git
synced 2024-12-27 08:55:25 +01:00
Merge pull request #607 from orangemug/fix/issue-567-better-solution-for-tooltips
Inline property documentation and SDK docs
This commit is contained in:
commit
bc4706de83
29 changed files with 375 additions and 81 deletions
|
@ -20,7 +20,8 @@ class Button extends React.Component {
|
|||
aria-label={this.props["aria-label"]}
|
||||
className={classnames("maputnik-button", this.props.className)}
|
||||
data-wd-key={this.props["data-wd-key"]}
|
||||
style={this.props.style}>
|
||||
style={this.props.style}
|
||||
>
|
||||
{this.props.children}
|
||||
</button>
|
||||
}
|
||||
|
|
|
@ -1,23 +1,56 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import {MdInfoOutline, MdHighlightOff} from 'react-icons/md'
|
||||
|
||||
export default class DocLabel extends React.Component {
|
||||
static propTypes = {
|
||||
label: PropTypes.oneOfType([
|
||||
PropTypes.object,
|
||||
PropTypes.string
|
||||
]).isRequired,
|
||||
doc: PropTypes.string.isRequired,
|
||||
fieldSpec: PropTypes.object.isRequired,
|
||||
onToggleDoc: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
constructor (props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
open: false,
|
||||
}
|
||||
}
|
||||
|
||||
onToggleDoc = (open) => {
|
||||
this.setState({
|
||||
open,
|
||||
}, () => {
|
||||
if (this.props.onToggleDoc) {
|
||||
this.props.onToggleDoc(this.state.open);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return <label className="maputnik-doc-wrapper">
|
||||
<div className="maputnik-doc-target">
|
||||
<span>{this.props.label}</span>
|
||||
<div className="maputnik-doc-popup">
|
||||
{this.props.doc}
|
||||
const {label, fieldSpec} = this.props;
|
||||
const {doc} = fieldSpec || {};
|
||||
|
||||
if (doc) {
|
||||
return <label className="maputnik-doc-wrapper">
|
||||
<div className="maputnik-doc-target">
|
||||
{label}
|
||||
{'\xa0'}
|
||||
<button
|
||||
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)}
|
||||
>
|
||||
{this.state.open ? <MdHighlightOff /> : <MdInfoOutline />}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</label>
|
||||
}
|
||||
else {
|
||||
return <div />
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -200,17 +200,17 @@ export default class DataProperty extends React.Component {
|
|||
return <div className="maputnik-data-spec-block">
|
||||
<div className="maputnik-data-spec-property">
|
||||
<InputBlock
|
||||
doc={this.props.fieldSpec.doc}
|
||||
fieldSpec={this.props.fieldSpec}
|
||||
label={labelFromFieldName(this.props.fieldName)}
|
||||
>
|
||||
<div className="maputnik-data-spec-property-group">
|
||||
<DocLabel
|
||||
label="Property"
|
||||
doc={"Input a data property to base styles off of."}
|
||||
/>
|
||||
<div className="maputnik-data-spec-property-input">
|
||||
<StringInput
|
||||
value={this.props.value.property}
|
||||
title={"Input a data property to base styles off of."}
|
||||
onChange={propVal => this.changeDataProperty("property", propVal)}
|
||||
/>
|
||||
</div>
|
||||
|
@ -218,12 +218,12 @@ export default class DataProperty extends React.Component {
|
|||
<div className="maputnik-data-spec-property-group">
|
||||
<DocLabel
|
||||
label="Type"
|
||||
doc={"Select a type of data scale (default is 'categorical')."}
|
||||
/>
|
||||
<div className="maputnik-data-spec-property-input">
|
||||
<SelectInput
|
||||
value={this.props.value.type}
|
||||
onChange={propVal => this.changeDataProperty("type", propVal)}
|
||||
title={"Select a type of data scale (default is 'categorical')."}
|
||||
options={this.getDataFunctionTypes(this.props.fieldSpec)}
|
||||
/>
|
||||
</div>
|
||||
|
@ -231,7 +231,6 @@ export default class DataProperty extends React.Component {
|
|||
<div className="maputnik-data-spec-property-group">
|
||||
<DocLabel
|
||||
label="Default"
|
||||
doc={"Input a default value for data if not covered by the scales."}
|
||||
/>
|
||||
<div className="maputnik-data-spec-property-input">
|
||||
<SpecField
|
||||
|
@ -242,15 +241,15 @@ export default class DataProperty extends React.Component {
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
{dataFields}
|
||||
<Button
|
||||
className="maputnik-add-stop"
|
||||
onClick={this.props.onAddStop.bind(this)}
|
||||
>
|
||||
Add stop
|
||||
</Button>
|
||||
</InputBlock>
|
||||
</div>
|
||||
{dataFields}
|
||||
<Button
|
||||
className="maputnik-add-stop"
|
||||
onClick={this.props.onAddStop.bind(this)}
|
||||
>
|
||||
Add stop
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import DocLabel from './DocLabel'
|
||||
import Button from '../Button'
|
||||
import {MdDelete} from 'react-icons/md'
|
||||
|
||||
|
@ -15,11 +14,9 @@ export default class DeleteStopButton extends React.Component {
|
|||
return <Button
|
||||
className="maputnik-delete-stop"
|
||||
onClick={this.props.onClick}
|
||||
title={"Remove zoom level stop."}
|
||||
>
|
||||
<DocLabel
|
||||
label={<MdDelete />}
|
||||
doc={"Remove zoom level stop."}
|
||||
/>
|
||||
<MdDelete />
|
||||
</Button>
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import DocLabel from './DocLabel'
|
||||
import Button from '../Button'
|
||||
import {MdFunctions, MdInsertChart} from 'react-icons/md'
|
||||
|
||||
|
@ -19,24 +18,18 @@ export default class FunctionButtons extends React.Component {
|
|||
makeZoomButton = <Button
|
||||
className="maputnik-make-zoom-function"
|
||||
onClick={this.props.onZoomClick}
|
||||
title={"Turn property into a zoom function to enable a map feature to change with map's zoom level."}
|
||||
>
|
||||
<DocLabel
|
||||
label={<MdFunctions />}
|
||||
cursorTargetStyle={{ cursor: 'pointer' }}
|
||||
doc={"Turn property into a zoom function to enable a map feature to change with map's zoom level."}
|
||||
/>
|
||||
<MdFunctions />
|
||||
</Button>
|
||||
|
||||
if (this.props.fieldSpec['property-type'] === 'data-driven') {
|
||||
makeDataButton = <Button
|
||||
className="maputnik-make-data-function"
|
||||
onClick={this.props.onDataClick}
|
||||
title={"Turn property into a data function to enable a map feature to change according to data properties and the map's zoom level."}
|
||||
>
|
||||
<DocLabel
|
||||
label={<MdInsertChart />}
|
||||
cursorTargetStyle={{ cursor: 'pointer' }}
|
||||
doc={"Turn property into a data function to enable a map feature to change according to data properties and the map's zoom level."}
|
||||
/>
|
||||
<MdInsertChart />
|
||||
</Button>
|
||||
}
|
||||
return <div>{makeDataButton}{makeZoomButton}</div>
|
||||
|
|
|
@ -25,6 +25,7 @@ export default class SpecProperty extends React.Component {
|
|||
|
||||
return <InputBlock
|
||||
doc={this.props.fieldSpec.doc}
|
||||
fieldSpec={this.props.fieldSpec}
|
||||
label={labelFromFieldName(this.props.fieldName)}
|
||||
action={functionBtn}
|
||||
>
|
||||
|
|
|
@ -125,7 +125,7 @@ export default class ZoomProperty extends React.Component {
|
|||
|
||||
return <InputBlock
|
||||
key={key}
|
||||
doc={this.props.fieldSpec.doc}
|
||||
fieldSpec={this.props.fieldSpec}
|
||||
label={labelFromFieldName(this.props.fieldName)}
|
||||
action={deleteStopBtn}
|
||||
>
|
||||
|
|
|
@ -8,6 +8,7 @@ import SelectInput from '../inputs/SelectInput'
|
|||
import SingleFilterEditor from './SingleFilterEditor'
|
||||
import FilterEditorBlock from './FilterEditorBlock'
|
||||
import Button from '../Button'
|
||||
import SpecDoc from '../inputs/SpecDoc'
|
||||
|
||||
function hasCombiningFilter(filter) {
|
||||
return combiningFilterOps.indexOf(filter[0]) >= 0
|
||||
|
@ -29,6 +30,13 @@ export default class CombiningFilterEditor extends React.Component {
|
|||
onChange: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
constructor () {
|
||||
super();
|
||||
this.state = {
|
||||
showDoc: false,
|
||||
};
|
||||
}
|
||||
|
||||
// Convert filter to combining filter
|
||||
combiningFilter() {
|
||||
let filter = this.props.filter || ['all']
|
||||
|
@ -63,11 +71,21 @@ export default class CombiningFilterEditor extends React.Component {
|
|||
this.props.onChange(newFilterItem)
|
||||
}
|
||||
|
||||
onToggleDoc = (val) => {
|
||||
this.setState({
|
||||
showDoc: val
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const filter = this.combiningFilter()
|
||||
let combiningOp = filter[0]
|
||||
let filters = filter.slice(1)
|
||||
|
||||
const fieldSpec={
|
||||
doc: latest.layer.filter.doc + " Combine multiple filters together by using a compound filter."
|
||||
};
|
||||
|
||||
const editorBlocks = filters.map((f, idx) => {
|
||||
return <FilterEditorBlock key={idx} onDelete={this.deleteFilterItem.bind(this, idx)}>
|
||||
<SingleFilterEditor
|
||||
|
@ -89,7 +107,8 @@ export default class CombiningFilterEditor extends React.Component {
|
|||
<div className="maputnik-filter-editor-compound-select" data-wd-key="layer-filter">
|
||||
<DocLabel
|
||||
label={"Compound Filter"}
|
||||
doc={latest.layer.filter.doc + " Combine multiple filters together by using a compound filter."}
|
||||
onToggleDoc={this.onToggleDoc}
|
||||
fieldSpec={fieldSpec}
|
||||
/>
|
||||
<SelectInput
|
||||
value={combiningOp}
|
||||
|
@ -106,6 +125,12 @@ export default class CombiningFilterEditor extends React.Component {
|
|||
Add filter
|
||||
</Button>
|
||||
</div>
|
||||
<div
|
||||
className="maputnik-doc-inline"
|
||||
style={{display: this.state.showDoc ? '' : 'none'}}
|
||||
>
|
||||
<SpecDoc fieldSpec={fieldSpec} />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ import React from 'react'
|
|||
import PropTypes from 'prop-types'
|
||||
import classnames from 'classnames'
|
||||
import DocLabel from '../fields/DocLabel'
|
||||
import SpecDoc from './SpecDoc'
|
||||
|
||||
|
||||
/** Wrap a component with a label */
|
||||
class InputBlock extends React.Component {
|
||||
|
@ -11,11 +13,18 @@ class InputBlock extends React.Component {
|
|||
PropTypes.string,
|
||||
PropTypes.element,
|
||||
]).isRequired,
|
||||
doc: PropTypes.string,
|
||||
action: PropTypes.element,
|
||||
children: PropTypes.node.isRequired,
|
||||
style: PropTypes.object,
|
||||
onChange: PropTypes.func,
|
||||
fieldSpec: PropTypes.object,
|
||||
}
|
||||
|
||||
constructor (props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
showDoc: false,
|
||||
}
|
||||
}
|
||||
|
||||
onChange(e) {
|
||||
|
@ -23,6 +32,12 @@ class InputBlock extends React.Component {
|
|||
return this.props.onChange(value === "" ? undefined : value)
|
||||
}
|
||||
|
||||
onToggleDoc = (val) => {
|
||||
this.setState({
|
||||
showDoc: val
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div style={this.props.style}
|
||||
data-wd-key={this.props["data-wd-key"]}
|
||||
|
@ -31,15 +46,16 @@ class InputBlock extends React.Component {
|
|||
"maputnik-action-block": this.props.action
|
||||
})}
|
||||
>
|
||||
{this.props.doc &&
|
||||
{this.props.fieldSpec &&
|
||||
<div className="maputnik-input-block-label">
|
||||
<DocLabel
|
||||
label={this.props.label}
|
||||
doc={this.props.doc}
|
||||
onToggleDoc={this.onToggleDoc}
|
||||
fieldSpec={this.props.fieldSpec}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
{!this.props.doc &&
|
||||
{!this.props.fieldSpec &&
|
||||
<label className="maputnik-input-block-label">
|
||||
{this.props.label}
|
||||
</label>
|
||||
|
@ -52,6 +68,14 @@ class InputBlock extends React.Component {
|
|||
<div className="maputnik-input-block-content">
|
||||
{this.props.children}
|
||||
</div>
|
||||
{this.props.fieldSpec &&
|
||||
<div
|
||||
className="maputnik-doc-inline"
|
||||
style={{display: this.state.showDoc ? '' : 'none'}}
|
||||
>
|
||||
<SpecDoc fieldSpec={this.props.fieldSpec} />
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ class SelectInput extends React.Component {
|
|||
options: PropTypes.array.isRequired,
|
||||
style: PropTypes.object,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
title: PropTypes.string,
|
||||
}
|
||||
|
||||
|
||||
|
@ -21,6 +22,7 @@ class SelectInput extends React.Component {
|
|||
className="maputnik-select"
|
||||
data-wd-key={this.props["data-wd-key"]}
|
||||
style={this.props.style}
|
||||
title={this.props.title}
|
||||
value={this.props.value}
|
||||
onChange={e => this.props.onChange(e.target.value)}
|
||||
>
|
||||
|
|
83
src/components/inputs/SpecDoc.js
Normal file
83
src/components/inputs/SpecDoc.js
Normal file
|
@ -0,0 +1,83 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
export default class SpecDoc extends React.Component {
|
||||
static propTypes = {
|
||||
fieldSpec: PropTypes.object.isRequired,
|
||||
}
|
||||
|
||||
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 stylespec, so this is required
|
||||
// See <https://github.com/maputnik/editor/blob/master/src/components/fields/PropertyGroup.jsx#L16>
|
||||
!Array.isArray(values)
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{doc &&
|
||||
<div className="SpecDoc">
|
||||
<div className="SpecDoc__doc">{doc}</div>
|
||||
{renderValues &&
|
||||
<ul className="SpecDoc__values">
|
||||
{Object.entries(values).map(([key, value]) => {
|
||||
return (
|
||||
<li key={key}>
|
||||
<code>{JSON.stringify(key)}</code>
|
||||
<div>{value.doc}</div>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
{sdkSupport &&
|
||||
<div className="SpecDoc__sdk-support">
|
||||
<table className="SpecDoc__sdk-support__table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
{Object.values(headers).map(header => {
|
||||
return <th key={header}>{header}</th>;
|
||||
})}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{Object.entries(sdkSupport).map(([key, supportObj]) => {
|
||||
return (
|
||||
<tr key={key}>
|
||||
<td>{key}</td>
|
||||
{Object.keys(headers).map(k => {
|
||||
const value = supportObj[k];
|
||||
if (supportObj.hasOwnProperty(k)) {
|
||||
return <td key={k}>{supportObj[k]}</td>;
|
||||
}
|
||||
else {
|
||||
return <td key={k}>no</td>;
|
||||
}
|
||||
})}
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -11,9 +11,13 @@ class MetadataBlock extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
const fieldSpec = {
|
||||
doc: "Comments for the current layer. This is non-standard and not in the spec."
|
||||
};
|
||||
|
||||
return <InputBlock
|
||||
label={"Comments"}
|
||||
doc={"Comments for the current layer. This is non-standard and not in the spec."}
|
||||
fieldSpec={fieldSpec}
|
||||
data-wd-key="layer-comment"
|
||||
>
|
||||
<StringInput
|
||||
|
|
|
@ -13,7 +13,7 @@ class LayerIdBlock extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
return <InputBlock label={"ID"} doc={latest.layer.id.doc}
|
||||
return <InputBlock label={"ID"} fieldSpec={latest.layer.id}
|
||||
data-wd-key={this.props.wdKey}
|
||||
>
|
||||
<StringInput
|
||||
|
|
|
@ -19,7 +19,7 @@ class LayerSourceBlock extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
return <InputBlock label={"Source"} doc={latest.layer.source.doc}
|
||||
return <InputBlock label={"Source"} fieldSpec={latest.layer.source}
|
||||
data-wd-key={this.props.wdKey}
|
||||
>
|
||||
<AutocompleteInput
|
||||
|
|
|
@ -20,7 +20,7 @@ class LayerSourceLayer extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
return <InputBlock label={"Source Layer"} doc={latest.layer['source-layer'].doc}
|
||||
return <InputBlock label={"Source Layer"} fieldSpec={latest.layer['source-layer']}
|
||||
data-wd-key="layer-source-layer"
|
||||
>
|
||||
<AutocompleteInput
|
||||
|
|
|
@ -13,7 +13,7 @@ class LayerTypeBlock extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
return <InputBlock label={"Type"} doc={latest.layer.type.doc}
|
||||
return <InputBlock label={"Type"} fieldSpec={latest.layer.type}
|
||||
data-wd-key={this.props.wdKey}
|
||||
>
|
||||
<SelectInput
|
||||
|
|
|
@ -12,7 +12,7 @@ class MaxZoomBlock extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
return <InputBlock label={"Max Zoom"} doc={latest.layer.maxzoom.doc}
|
||||
return <InputBlock label={"Max Zoom"} fieldSpec={latest.layer.maxzoom}
|
||||
data-wd-key="max-zoom"
|
||||
>
|
||||
<NumberInput
|
||||
|
|
|
@ -12,7 +12,7 @@ class MinZoomBlock extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
return <InputBlock label={"Min Zoom"} doc={latest.layer.minzoom.doc}
|
||||
return <InputBlock label={"Min Zoom"} fieldSpec={latest.layer.minzoom}
|
||||
data-wd-key="min-zoom"
|
||||
>
|
||||
<NumberInput
|
||||
|
|
|
@ -126,6 +126,7 @@ class AddModal extends React.Component {
|
|||
onOpenToggle={this.props.onOpenToggle}
|
||||
title={'Add Layer'}
|
||||
data-wd-key="modal:add-layer"
|
||||
className="maputnik-add-modal"
|
||||
>
|
||||
<div className="maputnik-add-layer">
|
||||
<LayerIdBlock
|
||||
|
|
|
@ -11,6 +11,7 @@ import Button from '../Button'
|
|||
import Modal from './Modal'
|
||||
import {MdFileDownload} from 'react-icons/md'
|
||||
import style from '../../libs/style'
|
||||
import fieldSpecAdditional from '../../libs/field-spec-additional'
|
||||
|
||||
|
||||
|
||||
|
@ -70,6 +71,7 @@ class ExportModal extends React.Component {
|
|||
isOpen={this.props.isOpen}
|
||||
onOpenToggle={this.props.onOpenToggle}
|
||||
title={'Export Style'}
|
||||
className="maputnik-export-modal"
|
||||
>
|
||||
|
||||
<div className="maputnik-modal-section">
|
||||
|
@ -79,19 +81,28 @@ class ExportModal extends React.Component {
|
|||
</p>
|
||||
|
||||
<p>
|
||||
<InputBlock label={"MapTiler Access Token: "}>
|
||||
<InputBlock
|
||||
label={fieldSpecAdditional.maputnik.mapbox_access_token.label}
|
||||
fieldSpec={fieldSpecAdditional.maputnik.mapbox_access_token}
|
||||
>
|
||||
<StringInput
|
||||
value={(this.props.mapStyle.metadata || {})['maputnik:openmaptiles_access_token']}
|
||||
onChange={this.changeMetadataProperty.bind(this, "maputnik:openmaptiles_access_token")}
|
||||
/>
|
||||
</InputBlock>
|
||||
<InputBlock label={"Mapbox Access Token: "}>
|
||||
<InputBlock
|
||||
label={fieldSpecAdditional.maputnik.maptiler_access_token.label}
|
||||
fieldSpec={fieldSpecAdditional.maputnik.maptiler_access_token}
|
||||
>
|
||||
<StringInput
|
||||
value={(this.props.mapStyle.metadata || {})['maputnik:mapbox_access_token']}
|
||||
onChange={this.changeMetadataProperty.bind(this, "maputnik:mapbox_access_token")}
|
||||
/>
|
||||
</InputBlock>
|
||||
<InputBlock label={"Thunderforest Access Token: "}>
|
||||
<InputBlock
|
||||
label={fieldSpecAdditional.maputnik.thunderforest_access_token.label}
|
||||
fieldSpec={fieldSpecAdditional.maputnik.thunderforest_access_token}
|
||||
>
|
||||
<StringInput
|
||||
value={(this.props.mapStyle.metadata || {})['maputnik:thunderforest_access_token']}
|
||||
onChange={this.changeMetadataProperty.bind(this, "maputnik:thunderforest_access_token")}
|
||||
|
|
|
@ -2,6 +2,7 @@ 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';
|
||||
|
||||
|
||||
class Modal extends React.Component {
|
||||
|
@ -13,6 +14,7 @@ class Modal extends React.Component {
|
|||
children: PropTypes.node,
|
||||
underlayClickExits: PropTypes.bool,
|
||||
underlayProps: PropTypes.object,
|
||||
className: PropTypes.string,
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
|
@ -45,7 +47,7 @@ class Modal extends React.Component {
|
|||
verticallyCenter={true}
|
||||
onExit={this.onClose}
|
||||
>
|
||||
<div className="maputnik-modal"
|
||||
<div className={classnames("maputnik-modal", this.props.className)}
|
||||
data-wd-key={this.props["data-wd-key"]}
|
||||
>
|
||||
<header className="maputnik-modal-header">
|
||||
|
|
|
@ -11,6 +11,7 @@ import SelectInput from '../inputs/SelectInput'
|
|||
import EnumInput from '../inputs/EnumInput'
|
||||
import ColorField from '../fields/ColorField'
|
||||
import Modal from './Modal'
|
||||
import fieldSpecAdditional from '../../libs/field-spec-additional'
|
||||
|
||||
class SettingsModal extends React.Component {
|
||||
static propTypes = {
|
||||
|
@ -86,21 +87,21 @@ class SettingsModal extends React.Component {
|
|||
title={'Style Settings'}
|
||||
>
|
||||
<div className="modal-settings">
|
||||
<InputBlock label={"Name"} doc={latest.$root.name.doc}>
|
||||
<InputBlock label={"Name"} fieldSpec={latest.$root.name}>
|
||||
<StringInput {...inputProps}
|
||||
data-wd-key="modal-settings.name"
|
||||
value={this.props.mapStyle.name}
|
||||
onChange={this.changeStyleProperty.bind(this, "name")}
|
||||
/>
|
||||
</InputBlock>
|
||||
<InputBlock label={"Owner"} doc={"Owner ID of the style. Used by Mapbox or future style APIs."}>
|
||||
<InputBlock label={"Owner"} fieldSpec={{doc: "Owner ID of the style. Used by Mapbox or future style APIs."}}>
|
||||
<StringInput {...inputProps}
|
||||
data-wd-key="modal-settings.owner"
|
||||
value={this.props.mapStyle.owner}
|
||||
onChange={this.changeStyleProperty.bind(this, "owner")}
|
||||
/>
|
||||
</InputBlock>
|
||||
<InputBlock label={"Sprite URL"} doc={latest.$root.sprite.doc}>
|
||||
<InputBlock label={"Sprite URL"} fieldSpec={latest.$root.sprite}>
|
||||
<UrlInput {...inputProps}
|
||||
data-wd-key="modal-settings.sprite"
|
||||
value={this.props.mapStyle.sprite}
|
||||
|
@ -108,7 +109,7 @@ class SettingsModal extends React.Component {
|
|||
/>
|
||||
</InputBlock>
|
||||
|
||||
<InputBlock label={"Glyphs URL"} doc={latest.$root.glyphs.doc}>
|
||||
<InputBlock label={"Glyphs URL"} fieldSpec={latest.$root.glyphs}>
|
||||
<UrlInput {...inputProps}
|
||||
data-wd-key="modal-settings.glyphs"
|
||||
value={this.props.mapStyle.glyphs}
|
||||
|
@ -116,7 +117,10 @@ class SettingsModal extends React.Component {
|
|||
/>
|
||||
</InputBlock>
|
||||
|
||||
<InputBlock label={"Mapbox Access Token"} doc={"Public access token for Mapbox services."}>
|
||||
<InputBlock
|
||||
label={fieldSpecAdditional.maputnik.mapbox_access_token.label}
|
||||
fieldSpec={fieldSpecAdditional.maputnik.mapbox_access_token}
|
||||
>
|
||||
<StringInput {...inputProps}
|
||||
data-wd-key="modal-settings.maputnik:mapbox_access_token"
|
||||
value={metadata['maputnik:mapbox_access_token']}
|
||||
|
@ -124,7 +128,10 @@ class SettingsModal extends React.Component {
|
|||
/>
|
||||
</InputBlock>
|
||||
|
||||
<InputBlock label={"MapTiler Access Token"} doc={"Public access token for MapTiler Cloud."}>
|
||||
<InputBlock
|
||||
label={fieldSpecAdditional.maputnik.maptiler_access_token.label}
|
||||
fieldSpec={fieldSpecAdditional.maputnik.maptiler_access_token}
|
||||
>
|
||||
<StringInput {...inputProps}
|
||||
data-wd-key="modal-settings.maputnik:openmaptiles_access_token"
|
||||
value={metadata['maputnik:openmaptiles_access_token']}
|
||||
|
@ -132,7 +139,10 @@ class SettingsModal extends React.Component {
|
|||
/>
|
||||
</InputBlock>
|
||||
|
||||
<InputBlock label={"Thunderforest Access Token"} doc={"Public access token for Thunderforest services."}>
|
||||
<InputBlock
|
||||
label={fieldSpecAdditional.maputnik.thunderforest_access_token.label}
|
||||
fieldSpec={fieldSpecAdditional.maputnik.thunderforest_access_token}
|
||||
>
|
||||
<StringInput {...inputProps}
|
||||
data-wd-key="modal-settings.maputnik:thunderforest_access_token"
|
||||
value={metadata['maputnik:thunderforest_access_token']}
|
||||
|
@ -140,7 +150,7 @@ class SettingsModal extends React.Component {
|
|||
/>
|
||||
</InputBlock>
|
||||
|
||||
<InputBlock label={"Center"} doc={latest.$root.center.doc}>
|
||||
<InputBlock label={"Center"} fieldSpec={latest.$root.center}>
|
||||
<ArrayInput
|
||||
length={2}
|
||||
type="number"
|
||||
|
@ -150,7 +160,7 @@ class SettingsModal extends React.Component {
|
|||
/>
|
||||
</InputBlock>
|
||||
|
||||
<InputBlock label={"Zoom"} doc={latest.$root.zoom.doc}>
|
||||
<InputBlock label={"Zoom"} fieldSpec={latest.$root.zoom}>
|
||||
<NumberInput
|
||||
{...inputProps}
|
||||
value={mapStyle.zoom}
|
||||
|
@ -159,7 +169,7 @@ class SettingsModal extends React.Component {
|
|||
/>
|
||||
</InputBlock>
|
||||
|
||||
<InputBlock label={"Bearing"} doc={latest.$root.bearing.doc}>
|
||||
<InputBlock label={"Bearing"} fieldSpec={latest.$root.bearing}>
|
||||
<NumberInput
|
||||
{...inputProps}
|
||||
value={mapStyle.bearing}
|
||||
|
@ -168,7 +178,7 @@ class SettingsModal extends React.Component {
|
|||
/>
|
||||
</InputBlock>
|
||||
|
||||
<InputBlock label={"Pitch"} doc={latest.$root.pitch.doc}>
|
||||
<InputBlock label={"Pitch"} fieldSpec={latest.$root.pitch}>
|
||||
<NumberInput
|
||||
{...inputProps}
|
||||
value={mapStyle.pitch}
|
||||
|
@ -177,7 +187,7 @@ class SettingsModal extends React.Component {
|
|||
/>
|
||||
</InputBlock>
|
||||
|
||||
<InputBlock label={"Light anchor"} doc={latest.light.anchor.doc}>
|
||||
<InputBlock label={"Light anchor"} fieldSpec={latest.light.anchor}>
|
||||
<EnumInput
|
||||
{...inputProps}
|
||||
value={light.anchor}
|
||||
|
@ -187,7 +197,7 @@ class SettingsModal extends React.Component {
|
|||
/>
|
||||
</InputBlock>
|
||||
|
||||
<InputBlock label={"Light color"} doc={latest.light.color.doc}>
|
||||
<InputBlock label={"Light color"} fieldSpec={latest.light.color}>
|
||||
<ColorField
|
||||
{...inputProps}
|
||||
value={light.color}
|
||||
|
@ -196,7 +206,7 @@ class SettingsModal extends React.Component {
|
|||
/>
|
||||
</InputBlock>
|
||||
|
||||
<InputBlock label={"Light intensity"} doc={latest.light.intensity.doc}>
|
||||
<InputBlock label={"Light intensity"} fieldSpec={latest.light.intensity}>
|
||||
<NumberInput
|
||||
{...inputProps}
|
||||
value={light.intensity}
|
||||
|
@ -205,7 +215,7 @@ class SettingsModal extends React.Component {
|
|||
/>
|
||||
</InputBlock>
|
||||
|
||||
<InputBlock label={"Light position"} doc={latest.light.position.doc}>
|
||||
<InputBlock label={"Light position"} fieldSpec={latest.light.position}>
|
||||
<ArrayInput
|
||||
{...inputProps}
|
||||
type="number"
|
||||
|
@ -216,7 +226,7 @@ class SettingsModal extends React.Component {
|
|||
/>
|
||||
</InputBlock>
|
||||
|
||||
<InputBlock label={"Transition delay"} doc={latest.transition.delay.doc}>
|
||||
<InputBlock label={"Transition delay"} fieldSpec={latest.transition.delay}>
|
||||
<NumberInput
|
||||
{...inputProps}
|
||||
value={transition.delay}
|
||||
|
@ -225,7 +235,7 @@ class SettingsModal extends React.Component {
|
|||
/>
|
||||
</InputBlock>
|
||||
|
||||
<InputBlock label={"Transition duration"} doc={latest.transition.duration.doc}>
|
||||
<InputBlock label={"Transition duration"} fieldSpec={latest.transition.duration}>
|
||||
<NumberInput
|
||||
{...inputProps}
|
||||
value={transition.duration}
|
||||
|
@ -234,7 +244,10 @@ class SettingsModal extends React.Component {
|
|||
/>
|
||||
</InputBlock>
|
||||
|
||||
<InputBlock label={"Style Renderer"} doc={"Choose the default Maputnik renderer for this style."}>
|
||||
<InputBlock
|
||||
label={fieldSpecAdditional.maputnik.style_renderer.label}
|
||||
fieldSpec={fieldSpecAdditional.maputnik.style_renderer}
|
||||
>
|
||||
<SelectInput {...inputProps}
|
||||
data-wd-key="modal-settings.maputnik:renderer"
|
||||
options={[
|
||||
|
|
|
@ -195,14 +195,25 @@ class AddSource extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
// Kind of a hack because the type changes, however maputnik has 1..n
|
||||
// options per type, for example
|
||||
//
|
||||
// - 'geojson' - 'GeoJSON (URL)' and 'GeoJSON (JSON)'
|
||||
// - 'raster' - 'Raster (TileJSON URL)' and 'Raster (XYZ URL)'
|
||||
//
|
||||
// So we just ignore the values entirely as they are self explanatory
|
||||
const sourceTypeFieldSpec = {
|
||||
doc: latest.source_vector.type.doc
|
||||
};
|
||||
|
||||
return <div className="maputnik-add-source">
|
||||
<InputBlock label={"Source ID"} doc={"Unique ID that identifies the source and is used in the layer to reference the source."}>
|
||||
<InputBlock label={"Source ID"} fieldSpec={{doc: "Unique ID that identifies the source and is used in the layer to reference the source."}}>
|
||||
<StringInput
|
||||
value={this.state.sourceId}
|
||||
onChange={v => this.setState({ sourceId: v})}
|
||||
/>
|
||||
</InputBlock>
|
||||
<InputBlock label={"Source Type"} doc={latest.source_vector.type.doc}>
|
||||
<InputBlock label={"Source Type"} fieldSpec={sourceTypeFieldSpec}>
|
||||
<SelectInput
|
||||
options={[
|
||||
['geojson_json', 'GeoJSON (JSON)'],
|
||||
|
|
|
@ -20,7 +20,7 @@ class TileJSONSourceEditor extends React.Component {
|
|||
|
||||
render() {
|
||||
return <div>
|
||||
<InputBlock label={"TileJSON URL"} doc={latest.source_vector.url.doc}>
|
||||
<InputBlock label={"TileJSON URL"} fieldSpec={latest.source_vector.url}>
|
||||
<UrlInput
|
||||
value={this.props.source.url}
|
||||
onChange={url => this.props.onChange({
|
||||
|
@ -54,7 +54,7 @@ class TileURLSourceEditor extends React.Component {
|
|||
const prefix = ['1st', '2nd', '3rd', '4th', '5th', '6th', '7th']
|
||||
const tiles = this.props.source.tiles || []
|
||||
return tiles.map((tileUrl, tileIndex) => {
|
||||
return <InputBlock key={tileIndex} label={prefix[tileIndex] + " Tile URL"} doc={latest.source_vector.tiles.doc}>
|
||||
return <InputBlock key={tileIndex} label={prefix[tileIndex] + " Tile URL"} fieldSpec={latest.source_vector.tiles}>
|
||||
<UrlInput
|
||||
value={tileUrl}
|
||||
onChange={this.changeTileUrl.bind(this, tileIndex)}
|
||||
|
@ -66,7 +66,7 @@ class TileURLSourceEditor extends React.Component {
|
|||
render() {
|
||||
return <div>
|
||||
{this.renderTileUrls()}
|
||||
<InputBlock label={"Min Zoom"} doc={latest.source_vector.minzoom.doc}>
|
||||
<InputBlock label={"Min Zoom"} fieldSpec={latest.source_vector.minzoom}>
|
||||
<NumberInput
|
||||
value={this.props.source.minzoom || 0}
|
||||
onChange={minzoom => this.props.onChange({
|
||||
|
@ -75,7 +75,7 @@ class TileURLSourceEditor extends React.Component {
|
|||
})}
|
||||
/>
|
||||
</InputBlock>
|
||||
<InputBlock label={"Max Zoom"} doc={latest.source_vector.maxzoom.doc}>
|
||||
<InputBlock label={"Max Zoom"} fieldSpec={latest.source_vector.maxzoom}>
|
||||
<NumberInput
|
||||
value={this.props.source.maxzoom || 22}
|
||||
onChange={maxzoom => this.props.onChange({
|
||||
|
@ -191,7 +191,7 @@ class GeoJSONSourceUrlEditor extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
return <InputBlock label={"GeoJSON URL"} doc={latest.source_geojson.data.doc}>
|
||||
return <InputBlock label={"GeoJSON URL"} fieldSpec={latest.source_geojson.data}>
|
||||
<UrlInput
|
||||
value={this.props.source.data}
|
||||
onChange={data => this.props.onChange({
|
||||
|
@ -210,7 +210,7 @@ class GeoJSONSourceJSONEditor extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
return <InputBlock label={"GeoJSON"} doc={latest.source_geojson.data.doc}>
|
||||
return <InputBlock label={"GeoJSON"} fieldSpec={latest.source_geojson.data}>
|
||||
<JSONEditor
|
||||
layer={this.props.source.data}
|
||||
maxHeight={200}
|
||||
|
@ -246,7 +246,7 @@ class SourceTypeEditor extends React.Component {
|
|||
case 'tilexyz_raster': return <TileURLSourceEditor {...commonProps} />
|
||||
case 'tilejson_raster-dem': return <TileJSONSourceEditor {...commonProps} />
|
||||
case 'tilexyz_raster-dem': return <TileURLSourceEditor {...commonProps}>
|
||||
<InputBlock label={"Encoding"} doc={latest.source_raster_dem.encoding.doc}>
|
||||
<InputBlock label={"Encoding"} fieldSpec={latest.source_raster_dem.encoding}>
|
||||
<SelectInput
|
||||
options={Object.keys(latest.source_raster_dem.encoding.values)}
|
||||
onChange={encoding => this.props.onChange({
|
||||
|
|
22
src/libs/field-spec-additional.js
Normal file
22
src/libs/field-spec-additional.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
const spec = {
|
||||
maputnik: {
|
||||
mapbox_access_token: {
|
||||
label: "Mapbox Access Token",
|
||||
doc: "Public access token for Mapbox services."
|
||||
},
|
||||
maptiler_access_token: {
|
||||
label: "MapTiler Access Token",
|
||||
doc: "Public access token for MapTiler Cloud."
|
||||
},
|
||||
thunderforest_access_token: {
|
||||
label: "Thunderforest Access Token",
|
||||
doc: "Public access token for Thunderforest services."
|
||||
},
|
||||
style_renderer: {
|
||||
label: "Style Renderer",
|
||||
doc: "Choose the default Maputnik renderer for this style.",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export default spec;
|
|
@ -28,6 +28,14 @@
|
|||
height: 100%;
|
||||
}
|
||||
|
||||
.maputnik-input-block:hover,
|
||||
.maputnik-filter-editor-compound-select:hover {
|
||||
.maputnik-doc-button {
|
||||
opacity: 1;
|
||||
pointer-events: all;
|
||||
}
|
||||
}
|
||||
|
||||
// DOC LABEL
|
||||
.maputnik-doc {
|
||||
&-target {
|
||||
|
@ -57,6 +65,33 @@
|
|||
z-index: 10;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&-button {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
background: $color-black;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0;
|
||||
|
||||
svg {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&--open {
|
||||
opacity: 1;
|
||||
pointer-events: all;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.maputnik-doc-inline {
|
||||
color: $color-lowgray;
|
||||
background-color: $color-gray;
|
||||
padding: $margin-2;
|
||||
font-size: 12px;
|
||||
margin-top: $margin-3;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.maputnik-doc-target:hover .maputnik-doc-popup {
|
||||
|
|
|
@ -193,3 +193,30 @@
|
|||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.SpecDoc__sdk-support {
|
||||
position: relative;
|
||||
max-width: 100%;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.SpecDoc__sdk-support__table {
|
||||
width: 100%;
|
||||
margin-top: $margin-3;
|
||||
|
||||
td, th {
|
||||
border: solid 1px $color-midgray;
|
||||
padding: 4px 6px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.SpecDoc__values li {
|
||||
margin-top: $margin-3;
|
||||
}
|
||||
|
||||
.SpecDoc__values code {
|
||||
background: $color-midgray;
|
||||
padding: 0.1em 0.3em;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
top: $toolbar-height + $toolbar-offset;
|
||||
left: 200px;
|
||||
z-index: 1;
|
||||
width: 350px;
|
||||
width: 370px;
|
||||
background-color: $color-black;
|
||||
}
|
||||
|
||||
|
|
|
@ -108,6 +108,16 @@
|
|||
background-color: $color-midgray;
|
||||
}
|
||||
|
||||
.maputnik-add-modal {
|
||||
width: 400px;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.maputnik-export-modal {
|
||||
width: 400px;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.maputnik-add-layer {
|
||||
@extend .clearfix;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue