From dde2c24cbb06199e16970078c8fa0638e6754b71 Mon Sep 17 00:00:00 2001 From: Steven Date: Fri, 1 Mar 2019 14:41:27 -0500 Subject: [PATCH 1/3] Add query string for widths & heights --- src/card.ts | 6 ++++++ src/parser.ts | 14 ++++++++++---- src/template.ts | 27 ++++++++++++++++----------- src/types.d.ts | 2 ++ 4 files changed, 34 insertions(+), 15 deletions(-) diff --git a/src/card.ts b/src/card.ts index 2d85092..9594c2e 100644 --- a/src/card.ts +++ b/src/card.ts @@ -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); diff --git a/src/parser.ts b/src/parser.ts index 4ee2a76..d53c6c4 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -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; } diff --git a/src/template.ts b/src/template.ts index 1e0413d..7e48831 100644 --- a/src/template.ts +++ b/src/template.ts @@ -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 ` @@ -118,11 +115,10 @@ export function getHtml(parsedReq: ParsedRequest) {
-
- - ${otherImages.map(img => - `
+
` - )} +
+ ${images.map((img, i) => + getPlusSign(i) + getImage(img, widths[i], heights[i]) + ).join('')}
${emojify( @@ -133,3 +129,12 @@ export function getHtml(parsedReq: ParsedRequest) { `; } + +function getImage(url: string, width ='auto', height = '225') { + const src = sanitizeHtml(url); + return `` +} + +function getPlusSign(i: number) { + return i === 0 ? '' : '
+
'; +} diff --git a/src/types.d.ts b/src/types.d.ts index b4bc404..85a8bb1 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -8,4 +8,6 @@ interface ParsedRequest { md: boolean; fontSize: string; images: string[]; + widths: string[]; + heights: string[]; } From 5192bc20aa59ddb57e514089f1ba71ac18e8b703 Mon Sep 17 00:00:00 2001 From: Steven Date: Fri, 1 Mar 2019 15:35:14 -0500 Subject: [PATCH 2/3] Sanitize width/height --- src/template.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/template.ts b/src/template.ts index 7e48831..523c34b 100644 --- a/src/template.ts +++ b/src/template.ts @@ -130,9 +130,13 @@ export function getHtml(parsedReq: ParsedRequest) { `; } -function getImage(url: string, width ='auto', height = '225') { - const src = sanitizeHtml(url); - return `` +function getImage(src: string, width ='auto', height = '225') { + return `` } function getPlusSign(i: number) { From 74686546a729ddf2def8f9c632b12da608022eac Mon Sep 17 00:00:00 2001 From: Steven Date: Fri, 1 Mar 2019 17:54:33 -0500 Subject: [PATCH 3/3] Add width & height dropdowns --- public/style.css | 24 ++++++++- src/browser.ts | 127 +++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 128 insertions(+), 23 deletions(-) diff --git a/public/style.css b/public/style.css index bc8f7c2..06f7dac 100644 --- a/public/style.css +++ b/public/style.css @@ -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; diff --git a/src/browser.ts b/src/browser.ts index a04b722..940b81b 100644 --- a/src/browser.ts +++ b/src/browser.ts @@ -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}`,