mirror of
https://github.com/maputnik/editor.git
synced 2026-03-01 07:50:00 +00:00
Added previews to image autocomplete imputs.
This commit is contained in:
@@ -293,8 +293,20 @@ export default class App extends React.Component {
|
||||
}
|
||||
|
||||
updateIcons(baseUrl) {
|
||||
downloadSpriteMetadata(baseUrl, icons => {
|
||||
this.setState({ spec: updateRootSpec(this.state.spec, 'sprite', icons)})
|
||||
downloadSpriteMetadata(baseUrl, metadata => {
|
||||
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 InputAutocomplete from './InputAutocomplete'
|
||||
import InputEnum from './InputEnum'
|
||||
import SpriteIcon from './SpriteIcon'
|
||||
import capitalize from 'lodash.capitalize'
|
||||
|
||||
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 'string':
|
||||
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
|
||||
{...commonProps}
|
||||
options={options.map(f => [f, f])}
|
||||
options={options.map(([k,v]) => {
|
||||
return [
|
||||
k,
|
||||
<SpriteIcon
|
||||
metadata={metadata}
|
||||
maxWidth={120}
|
||||
id={k}
|
||||
data={v}
|
||||
/>
|
||||
];
|
||||
})}
|
||||
/>
|
||||
} else {
|
||||
return <InputString
|
||||
|
||||
@@ -13,7 +13,7 @@ function getFieldSpec(spec, layerType, fieldName) {
|
||||
if(iconProperties.indexOf(fieldName) >= 0) {
|
||||
return {
|
||||
...fieldSpec,
|
||||
values: spec.$root.sprite.values
|
||||
metadata: spec.$root.sprite.metadata
|
||||
}
|
||||
}
|
||||
if(fieldName === 'text-font') {
|
||||
|
||||
57
src/components/SpriteIcon.jsx
Normal file
57
src/components/SpriteIcon.jsx
Normal file
@@ -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>
|
||||
}
|
||||
|
||||
@@ -1,22 +1,28 @@
|
||||
import npmurl from 'url'
|
||||
|
||||
function loadJSON(url, defaultValue, cb) {
|
||||
fetch(url, {
|
||||
return fetch(url, {
|
||||
mode: 'cors',
|
||||
credentials: "same-origin"
|
||||
})
|
||||
.then(function(response) {
|
||||
return response.json();
|
||||
})
|
||||
.then(function(body) {
|
||||
cb(body)
|
||||
})
|
||||
.catch(function() {
|
||||
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) {
|
||||
if(!urlTemplate) return cb([])
|
||||
|
||||
@@ -31,11 +37,30 @@ export function downloadGlyphsMetadata(urlTemplate, cb) {
|
||||
}
|
||||
let url = npmurl.format(urlObj);
|
||||
|
||||
loadJSON(url, [], cb)
|
||||
loadJSON(url, []).then(cb);
|
||||
}
|
||||
|
||||
export function downloadSpriteMetadata(baseUrl, cb) {
|
||||
if(!baseUrl) return cb([])
|
||||
const url = baseUrl + '.json'
|
||||
loadJSON(url, {}, glyphs => cb(Object.keys(glyphs)))
|
||||
const jsonUrl = baseUrl + '.json'
|
||||
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