Load icon and font metadata from endpoint

This commit is contained in:
Lukas Martinelli 2017-01-10 11:13:53 +01:00
parent 09b6b2dffe
commit 0fb59ca544
5 changed files with 80 additions and 17 deletions

View file

@ -10,6 +10,7 @@ import Toolbar from './Toolbar'
import AppLayout from './AppLayout'
import MessagePanel from './MessagePanel'
import { downloadGlyphsMetadata, downloadSpriteMetadata } from '../libs/metadata'
import GlSpec from 'mapbox-gl-style-spec/reference/latest.js'
import validateStyleMin from 'mapbox-gl-style-spec/lib/validate_style.min'
import formatStyle from 'mapbox-gl-style-spec/lib/format'
@ -21,6 +22,18 @@ import { ApiStyleStore } from '../libs/apistore'
import { RevisionStore } from '../libs/revisions'
import LayerWatcher from '../libs/layerwatcher'
function updateRootSpec(spec, fieldName, newValues) {
return {
...spec,
$root: {
...spec.$root,
[fieldName]: {
...spec.$root[fieldName],
values: newValues
}
}
}
}
export default class App extends React.Component {
constructor(props) {
@ -52,6 +65,7 @@ export default class App extends React.Component {
sources: {},
vectorLayers: {},
inspectModeEnabled: false,
spec: GlSpec,
}
this.layerWatcher = new LayerWatcher({
@ -85,7 +99,26 @@ export default class App extends React.Component {
this.styleStore.save(snapshotStyle)
}
updateFonts(urlTemplate) {
downloadGlyphsMetadata(urlTemplate, fonts => {
this.setState({ spec: updateRootSpec(this.state.spec, 'glyphs', fonts)})
})
}
updateIcons(baseUrl) {
downloadSpriteMetadata(baseUrl, icons => {
this.setState({ spec: updateRootSpec(this.state.spec, 'sprite', icons)})
})
}
onStyleChanged(newStyle, save=true) {
if(newStyle.glyphs !== this.state.mapStyle.glyphs) {
this.updateFonts(newStyle.glyphs)
}
if(newStyle.sprite !== this.state.mapStyle.sprite) {
this.updateIcons(newStyle.sprite)
}
const errors = validateStyleMin(newStyle, GlSpec)
if(errors.length === 0) {
this.revisionStore.addRevision(newStyle)
@ -212,6 +245,7 @@ export default class App extends React.Component {
layer={selectedLayer}
sources={this.state.sources}
vectorLayers={this.state.vectorLayers}
spec={this.state.spec}
onLayerChanged={this.onLayerChanged.bind(this)}
onLayerIdChange={this.onLayerIdChange.bind(this)}
/> : null

View file

@ -1,20 +1,34 @@
import React from 'react'
import GlSpec from 'mapbox-gl-style-spec/reference/latest.js'
import ZoomSpecField from './ZoomSpecField'
import colors from '../../config/colors'
import { margins } from '../../config/scales'
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(layerType, fieldName) {
const groupName = getGroupName(layerType, fieldName)
const group = GlSpec[groupName + '_' + layerType]
return group[fieldName]
function getFieldSpec(spec, layerType, fieldName) {
const groupName = getGroupName(spec, layerType, fieldName)
const group = spec[groupName + '_' + layerType]
const fieldSpec = group[fieldName]
if(iconProperties.indexOf(fieldName) >= 0) {
return {
...fieldSpec,
values: spec.$root.sprite.values
}
}
if(fieldName === 'text-font') {
return {
...fieldSpec,
values: spec.$root.glyphs.values
}
}
return fieldSpec
}
function getGroupName(layerType, fieldName) {
const paint = GlSpec['paint_' + layerType] || {}
function getGroupName(spec, layerType, fieldName) {
const paint = spec['paint_' + layerType] || {}
if (fieldName in paint) {
return 'paint'
} else {
@ -27,16 +41,17 @@ export default class PropertyGroup extends React.Component {
layer: React.PropTypes.object.isRequired,
groupFields: React.PropTypes.array.isRequired,
onChange: React.PropTypes.func.isRequired,
spec: React.PropTypes.object.isRequired,
}
onPropertyChange(property, newValue) {
const group = getGroupName(this.props.layer.type, property)
const group = getGroupName(this.props.spec, this.props.layer.type, property)
this.props.onChange(group , property, newValue)
}
render() {
const fields = this.props.groupFields.map(fieldName => {
const fieldSpec = getFieldSpec(this.props.layer.type, fieldName)
const fieldSpec = getFieldSpec(this.props.spec, this.props.layer.type, fieldName)
const paint = this.props.layer.paint || {}
const layout = this.props.layer.layout || {}

View file

@ -1,7 +1,6 @@
import React from 'react'
import color from 'color'
import GlSpec from 'mapbox-gl-style-spec/reference/latest.min.js'
import ColorField from './ColorField'
import NumberInput from '../inputs/NumberInput'
import CheckboxInput from '../inputs/CheckboxInput'
@ -10,8 +9,10 @@ import SelectInput from '../inputs/SelectInput'
import MultiButtonInput from '../inputs/MultiButtonInput'
import ArrayInput from '../inputs/ArrayInput'
import FontInput from '../inputs/FontInput'
import IconInput from '../inputs/IconInput'
import capitalize from 'lodash.capitalize'
const iconProperties = ['background-pattern', 'fill-pattern', 'line-pattern', 'fill-extrusion-pattern', 'icon-image']
import input from '../../config/input.js'
@ -78,11 +79,17 @@ export default class SpecField extends React.Component {
options={options}
/>
}
case 'string': return (
<StringInput
{...commonProps}
/>
)
case 'string':
if(iconProperties.indexOf(this.props.fieldName) >= 0) {
return <IconInput
{...commonProps}
icons={this.props.fieldSpec.values}
/>
} else {
return <StringInput
{...commonProps}
/>
}
case 'color': return (
<ColorField
{...commonProps}
@ -97,6 +104,7 @@ export default class SpecField extends React.Component {
if(this.props.fieldName === 'text-font') {
return <FontInput
{...commonProps}
fonts={this.props.fieldSpec.values}
/>
} else {
return <ArrayInput

View file

@ -9,10 +9,15 @@ import fontStacks from '../../config/fontstacks.json'
class FontInput extends React.Component {
static propTypes = {
value: React.PropTypes.array.isRequired,
fonts: React.PropTypes.array,
style: React.PropTypes.object,
onChange: React.PropTypes.func.isRequired,
}
static defaultProps = {
fonts: []
}
get values() {
return this.props.value || this.props.default.slice(1) || []
}
@ -28,7 +33,7 @@ class FontInput extends React.Component {
return <AutocompleteInput
key={i}
value={value}
options={fontStacks.map(f => [f, f])}
options={this.props.fonts.map(f => [f, f])}
onChange={this.changeFont.bind(this, i)}
wrapperStyle={{
display: 'block',

View file

@ -1,6 +1,5 @@
import React from 'react'
import GlSpec from 'mapbox-gl-style-spec/reference/latest.js'
import JSONEditor from './JSONEditor'
import FilterEditor from '../filter/FilterEditor'
import PropertyGroup from '../fields/PropertyGroup'
@ -47,6 +46,7 @@ export default class LayerEditor extends React.Component {
layer: React.PropTypes.object.isRequired,
sources: React.PropTypes.object,
vectorLayers: React.PropTypes.object,
spec: React.PropTypes.object.isRequired,
onLayerChanged: React.PropTypes.func,
onLayerIdChange: React.PropTypes.func,
}
@ -146,6 +146,7 @@ export default class LayerEditor extends React.Component {
case 'properties': return <PropertyGroup
layer={this.props.layer}
groupFields={fields}
spec={this.props.spec}
onChange={this.changeProperty.bind(this)}
/>
case 'jsoneditor': return <JSONEditor