mirror of
https://github.com/maputnik/editor.git
synced 2026-08-27 15:37:27 +00:00
Added previews to image autocomplete imputs.
This commit is contained in:
+14
-2
@@ -293,8 +293,20 @@ export default class App extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateIcons(baseUrl) {
|
updateIcons(baseUrl) {
|
||||||
downloadSpriteMetadata(baseUrl, icons => {
|
downloadSpriteMetadata(baseUrl, metadata => {
|
||||||
this.setState({ spec: updateRootSpec(this.state.spec, 'sprite', icons)})
|
const {spec} = this.state;
|
||||||
|
this.setState({
|
||||||
|
spec: {
|
||||||
|
...spec,
|
||||||
|
$root: {
|
||||||
|
...spec.$root,
|
||||||
|
sprite: {
|
||||||
|
...spec.$root["sprite"],
|
||||||
|
metadata: metadata
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import InputDynamicArray from './InputDynamicArray'
|
|||||||
import InputFont from './InputFont'
|
import InputFont from './InputFont'
|
||||||
import InputAutocomplete from './InputAutocomplete'
|
import InputAutocomplete from './InputAutocomplete'
|
||||||
import InputEnum from './InputEnum'
|
import InputEnum from './InputEnum'
|
||||||
|
import SpriteIcon from './SpriteIcon'
|
||||||
import capitalize from 'lodash.capitalize'
|
import capitalize from 'lodash.capitalize'
|
||||||
|
|
||||||
const iconProperties = ['background-pattern', 'fill-pattern', 'line-pattern', 'fill-extrusion-pattern', 'icon-image']
|
const iconProperties = ['background-pattern', 'fill-pattern', 'line-pattern', 'fill-extrusion-pattern', 'icon-image']
|
||||||
@@ -85,10 +86,21 @@ export default class SpecField extends React.Component {
|
|||||||
case 'formatted':
|
case 'formatted':
|
||||||
case 'string':
|
case 'string':
|
||||||
if (iconProperties.indexOf(this.props.fieldName) >= 0) {
|
if (iconProperties.indexOf(this.props.fieldName) >= 0) {
|
||||||
const options = this.props.fieldSpec.values || [];
|
const {metadata} = this.props.fieldSpec;
|
||||||
|
const options = Object.entries(metadata && metadata.data ? metadata.data : {});
|
||||||
return <InputAutocomplete
|
return <InputAutocomplete
|
||||||
{...commonProps}
|
{...commonProps}
|
||||||
options={options.map(f => [f, f])}
|
options={options.map(([k,v]) => {
|
||||||
|
return [
|
||||||
|
k,
|
||||||
|
<SpriteIcon
|
||||||
|
metadata={metadata}
|
||||||
|
maxWidth={120}
|
||||||
|
id={k}
|
||||||
|
data={v}
|
||||||
|
/>
|
||||||
|
];
|
||||||
|
})}
|
||||||
/>
|
/>
|
||||||
} else {
|
} else {
|
||||||
return <InputString
|
return <InputString
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ function getFieldSpec(spec, layerType, fieldName) {
|
|||||||
if(iconProperties.indexOf(fieldName) >= 0) {
|
if(iconProperties.indexOf(fieldName) >= 0) {
|
||||||
return {
|
return {
|
||||||
...fieldSpec,
|
...fieldSpec,
|
||||||
values: spec.$root.sprite.values
|
metadata: spec.$root.sprite.metadata
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(fieldName === 'text-font') {
|
if(fieldName === 'text-font') {
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
export default function SpriteIcon (props) {
|
||||||
|
const {metadata, id, data, maxWidth} = props;
|
||||||
|
const {image} = metadata;
|
||||||
|
const pixelRatio = Math.max(window.devicePixelRatio, 2);
|
||||||
|
|
||||||
|
function getUrls (pixelRatio, url) {
|
||||||
|
const out = [];
|
||||||
|
for (var i=pixelRatio; i>=1; i--) {
|
||||||
|
if (i > 1) {
|
||||||
|
out.push(
|
||||||
|
url.replace(/.png/, `@${i}x.png`)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
out.push(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out.map(url => {
|
||||||
|
return `url(${url})`;
|
||||||
|
}).join(", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
let scale = 1;
|
||||||
|
if (data.width > maxWidth) {
|
||||||
|
scale = maxWidth/data.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
const spriteWidth = image.width * scale;
|
||||||
|
const spriteHeight = image.height * scale;
|
||||||
|
const width = data.width * scale;
|
||||||
|
const height = data.height * scale;
|
||||||
|
const x = data.x * scale;
|
||||||
|
const y = data.y * scale;
|
||||||
|
|
||||||
|
return <div className="maputnik-sprite-icon">
|
||||||
|
<div className="maputnik-sprite-icon__text">
|
||||||
|
{id}
|
||||||
|
</div>
|
||||||
|
{image &&
|
||||||
|
<div className="maputnik-sprite-icon__icon">
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
backgroundImage: getUrls(pixelRatio, metadata.imageUrl),
|
||||||
|
backgroundSize: `${spriteWidth}px ${spriteHeight}px`,
|
||||||
|
backgroundPosition: `${-x}px ${-y}px`
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
+33
-8
@@ -1,22 +1,28 @@
|
|||||||
import npmurl from 'url'
|
import npmurl from 'url'
|
||||||
|
|
||||||
function loadJSON(url, defaultValue, cb) {
|
function loadJSON(url, defaultValue, cb) {
|
||||||
fetch(url, {
|
return fetch(url, {
|
||||||
mode: 'cors',
|
mode: 'cors',
|
||||||
credentials: "same-origin"
|
credentials: "same-origin"
|
||||||
})
|
})
|
||||||
.then(function(response) {
|
.then(function(response) {
|
||||||
return response.json();
|
return response.json();
|
||||||
})
|
})
|
||||||
.then(function(body) {
|
|
||||||
cb(body)
|
|
||||||
})
|
|
||||||
.catch(function() {
|
.catch(function() {
|
||||||
console.warn('Can not metadata for ' + url)
|
console.warn('Can not metadata for ' + url)
|
||||||
cb(defaultValue)
|
return defaultValue;
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function loadImage(url) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const img = new Image();
|
||||||
|
img.onload = () => resolve(img);
|
||||||
|
img.onerror = (err) => reject(err);
|
||||||
|
img.src = url;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function downloadGlyphsMetadata(urlTemplate, cb) {
|
export function downloadGlyphsMetadata(urlTemplate, cb) {
|
||||||
if(!urlTemplate) return cb([])
|
if(!urlTemplate) return cb([])
|
||||||
|
|
||||||
@@ -31,11 +37,30 @@ export function downloadGlyphsMetadata(urlTemplate, cb) {
|
|||||||
}
|
}
|
||||||
let url = npmurl.format(urlObj);
|
let url = npmurl.format(urlObj);
|
||||||
|
|
||||||
loadJSON(url, [], cb)
|
loadJSON(url, []).then(cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function downloadSpriteMetadata(baseUrl, cb) {
|
export function downloadSpriteMetadata(baseUrl, cb) {
|
||||||
if(!baseUrl) return cb([])
|
if(!baseUrl) return cb([])
|
||||||
const url = baseUrl + '.json'
|
const jsonUrl = baseUrl + '.json'
|
||||||
loadJSON(url, {}, glyphs => cb(Object.keys(glyphs)))
|
const imageUrl = baseUrl + '.png'
|
||||||
|
|
||||||
|
Promise.all([
|
||||||
|
loadImage(imageUrl).catch(() => undefined),
|
||||||
|
loadJSON(jsonUrl, {}),
|
||||||
|
])
|
||||||
|
.then(([image, data]) => {
|
||||||
|
const out = {
|
||||||
|
jsonUrl,
|
||||||
|
imageUrl,
|
||||||
|
data
|
||||||
|
};
|
||||||
|
if (image) {
|
||||||
|
out.image = {
|
||||||
|
width: image.naturalWidth,
|
||||||
|
height: image.naturalHeight,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cb(out);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -167,3 +167,18 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.maputnik-sprite-icon {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.maputnik-sprite-icon__text {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.maputnik-sprite-icon__icon {
|
||||||
|
margin-left: 0.6em;
|
||||||
|
padding: 2px;
|
||||||
|
background-color: white;
|
||||||
|
background-size: 8px 8px;
|
||||||
|
background-position: 0 0, 4px 4px;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user