Merge pull request #66 from styfle/image-size

Add support for modifying image width & height
This commit is contained in:
Steven 2019-03-04 10:08:04 -05:00 committed by GitHub
commit 9762ae2fdc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 166 additions and 38 deletions

View File

@ -152,6 +152,12 @@ button:hover {
border-color: #ddd;
}
.select-wrapper.small {
height: 24px;
min-width: 100px;
width: 100px;
}
.select-arrow {
border-left: 1px solid #eaeaea;
background: #fff;
@ -165,6 +171,10 @@ button:hover {
justify-content: center;
}
.select-arrow.small {
width: 22px;
}
select {
height: 100%;
border: none;
@ -180,8 +190,13 @@ select {
text-transform: none;
}
.field {
margin: 20px 80px;
.field-flex {
display: flex;
margin-top: 10px;
}
.field-value {
margin: 10px 80px;
}
.field-label {
@ -196,6 +211,11 @@ select {
display: inline-block;
}
label {
display: flex;
align-items: center;
}
.toast-area {
position: fixed;
bottom: 10px;

View File

@ -31,11 +31,14 @@ interface DropdownProps {
options: DropdownOption[];
value: string;
onchange: (val: string) => void;
small: boolean;
}
const Dropdown = ({ options, value, onchange }: DropdownProps) => {
const Dropdown = ({ options, value, onchange, small }: DropdownProps) => {
const wrapper = small ? 'select-wrapper small' : 'select-wrapper';
const arrow = small ? 'select-arrow small' : 'select-arrow';
return H('div',
{ className: 'select-wrapper'},
{ className: wrapper },
H('select',
{ onchange: (e: any) => onchange(e.target.value) },
options.map(o =>
@ -46,7 +49,7 @@ const Dropdown = ({ options, value, onchange }: DropdownProps) => {
)
),
H('div',
{ className: 'select-arrow' },
{ className: arrow },
'▼'
)
);
@ -150,11 +153,35 @@ const imageDarkOptions: DropdownOption[] = [
{ text: 'Hyper', value: 'https://assets.zeit.co/image/upload/front/assets/design/hyper-bw-logo.svg' },
];
const widthOptions = [
{ text: 'width', value: 'auto' },
{ text: '50', value: '50' },
{ text: '100', value: '100' },
{ text: '150', value: '150' },
{ text: '200', value: '200' },
{ text: '250', value: '250' },
{ text: '300', value: '300' },
{ text: '350', value: '350' },
];
const heightOptions = [
{ text: 'height', value: 'auto' },
{ text: '50', value: '50' },
{ text: '100', value: '100' },
{ text: '150', value: '150' },
{ text: '200', value: '200' },
{ text: '250', value: '250' },
{ text: '300', value: '300' },
{ text: '350', value: '350' },
];
interface AppState extends ParsedRequest {
loading: boolean;
showToast: boolean;
messageToast: string;
selectedImageIndex: number;
widths: string[];
heights: string[];
overrideUrl: URL | null;
}
@ -179,6 +206,8 @@ const App = (_: any, state: AppState, setState: SetState) => {
md = true,
text = '**Hello** World',
images=[imageLightOptions[0].value],
widths=[],
heights=[],
showToast = false,
messageToast = '',
loading = true,
@ -195,6 +224,12 @@ const App = (_: any, state: AppState, setState: SetState) => {
for (let image of images) {
url.searchParams.append('images', image);
}
for (let width of widths) {
url.searchParams.append('widths', width);
}
for (let height of heights) {
url.searchParams.append('heights', height);
}
return H('div',
{ className: 'split' },
@ -250,27 +285,77 @@ const App = (_: any, state: AppState, setState: SetState) => {
}),
H(Field, {
label: 'Image 1',
input: H(Dropdown, {
options: imageOptions,
value: imageOptions[selectedImageIndex].value,
onchange: (val: string) => {
let clone = [...images];
clone[0] = val;
const selected = imageOptions.map(o => o.value).indexOf(val);
setLoadingState({ images: clone, selectedImageIndex: selected });
}
})
input: H('div',
H(Dropdown, {
options: imageOptions,
value: imageOptions[selectedImageIndex].value,
onchange: (val: string) => {
let clone = [...images];
clone[0] = val;
const selected = imageOptions.map(o => o.value).indexOf(val);
setLoadingState({ images: clone, selectedImageIndex: selected });
}
}),
H('div',
{ className: 'field-flex' },
H(Dropdown, {
options: widthOptions,
value: widths[0],
small: true,
onchange: (val: string) => {
let clone = [...widths];
clone[0] = val;
setLoadingState({ widths: clone });
}
}),
H(Dropdown, {
options: heightOptions,
value: heights[0],
small: true,
onchange: (val: string) => {
let clone = [...heights];
clone[0] = val;
setLoadingState({ heights: clone });
}
})
)
),
}),
...images.slice(1).map((image, i) => H(Field, {
label: `Image ${i + 2}`,
input: H(TextInput, {
value: image,
oninput: (val: string) => {
let clone = [...images];
clone[i + 1] = val;
setLoadingState({ images: clone, overrideUrl: url });
}
})
input: H('div',
H(TextInput, {
value: image,
oninput: (val: string) => {
let clone = [...images];
clone[i + 1] = val;
setLoadingState({ images: clone, overrideUrl: url });
}
}),
H('div',
{ className: 'field-flex' },
H(Dropdown, {
options: widthOptions,
value: widths[i + 1],
small: true,
onchange: (val: string) => {
let clone = [...widths];
clone[i + 1] = val;
setLoadingState({ widths: clone });
}
}),
H(Dropdown, {
options: heightOptions,
value: heights[i + 1],
small: true,
onchange: (val: string) => {
let clone = [...heights];
clone[i + 1] = val;
setLoadingState({ heights: clone });
}
})
)
)
})),
H(Field, {
label: `Image ${images.length + 1}`,

View File

@ -5,11 +5,17 @@ import { getHtml } from './template';
import { writeTempFile, pathToFileURL } from './file';
const isDev = !process.env.NOW_REGION;
const isHtmlDebug = process.env.OG_HTML_DEBUG === '1';
export default async function handler(req: IncomingMessage, res: ServerResponse) {
try {
const parsedReq = parseRequest(req);
const html = getHtml(parsedReq);
if (isHtmlDebug) {
res.setHeader('Content-Type', 'text/html');
res.end(html);
return;
}
const { text, fileType } = parsedReq;
const filePath = await writeTempFile(text, html);
const fileUrl = pathToFileURL(filePath);

View File

@ -4,14 +4,14 @@ import { parse } from 'url';
export function parseRequest(req: IncomingMessage) {
console.log('HTTP ' + req.url);
const { pathname = '/', query = {} } = parse(req.url || '', true);
const { fontSize, images, theme, md } = query;
const { fontSize, images, widths, heights, theme, md } = query;
if (Array.isArray(fontSize)) {
throw new Error('Expected a single fontSize');
}
if (Array.isArray(theme)) {
throw new Error('Expected a single theme');
}
const arr = pathname.slice(1).split('.');
let extension = '';
@ -31,13 +31,19 @@ export function parseRequest(req: IncomingMessage) {
theme: theme === 'dark' ? 'dark' : 'light',
md: md === '1' || md === 'true',
fontSize: fontSize || '96px',
images: Array.isArray(images) ? images : [images],
images: getArray(images),
widths: getArray(widths),
heights: getArray(heights),
};
parsedRequest.images = getDefaultImages(parsedRequest.images, parsedRequest.theme);
return parsedRequest;
}
function getDefaultImages(images: string[], theme: Theme) {
function getArray(stringOrArray: string[] | string): string[] {
return Array.isArray(stringOrArray) ? stringOrArray : [stringOrArray];
}
function getDefaultImages(images: string[], theme: Theme): string[] {
if (images.length > 0 && images[0] && images[0].startsWith('https://assets.zeit.co/image/upload/front/assets/design/')) {
return images;
}

View File

@ -64,7 +64,7 @@ function getCss(theme: string, fontSize: string) {
content: '\`';
}
.img-wrapper {
.logo-wrapper {
display: flex;
align-items: center;
align-content: center;
@ -73,8 +73,6 @@ function getCss(theme: string, fontSize: string) {
}
.logo {
width: auto;
height: 225px;
margin: 0 75px;
}
@ -88,7 +86,7 @@ function getCss(theme: string, fontSize: string) {
margin: 150px;
}
img.emoji {
.emoji {
height: 1em;
width: 1em;
margin: 0 .05em 0 .1em;
@ -105,8 +103,7 @@ function getCss(theme: string, fontSize: string) {
}
export function getHtml(parsedReq: ParsedRequest) {
const { text, theme, md, fontSize, images } = parsedReq;
const [ firstImage, ...otherImages ] = images;
const { text, theme, md, fontSize, images, widths, heights } = parsedReq;
return `<!DOCTYPE html>
<html>
<meta charset="utf-8">
@ -118,11 +115,10 @@ export function getHtml(parsedReq: ParsedRequest) {
<body>
<div>
<div class="spacer">
<div class="img-wrapper">
<img class="logo" src="${sanitizeHtml(firstImage)}" />
${otherImages.map(img =>
`<div class="plus">+</div><img class="logo" src="${sanitizeHtml(img)}" />`
)}
<div class="logo-wrapper">
${images.map((img, i) =>
getPlusSign(i) + getImage(img, widths[i], heights[i])
).join('')}
</div>
<div class="spacer">
<div class="heading">${emojify(
@ -133,3 +129,16 @@ export function getHtml(parsedReq: ParsedRequest) {
</body>
</html>`;
}
function getImage(src: string, width ='auto', height = '225') {
return `<img
class="logo"
src="${sanitizeHtml(src)}"
width="${sanitizeHtml(width)}"
height="${sanitizeHtml(height)}"
/>`
}
function getPlusSign(i: number) {
return i === 0 ? '' : '<div class="plus">+</div>';
}

2
src/types.d.ts vendored
View File

@ -8,4 +8,6 @@ interface ParsedRequest {
md: boolean;
fontSize: string;
images: string[];
widths: string[];
heights: string[];
}