feat: added backdrop to gh-icon (#1097)

* feat: added backdrop to gh-icon

* feat: added backdrop to gh-icon

* feat: changed fill pattern

* chore: pattern cleanup

* feat: improved backdrop color
This commit is contained in:
Jakob Guddas
2023-04-18 22:40:18 +02:00
committed by GitHub
parent bfab755958
commit 4679ff791d
4 changed files with 160 additions and 62 deletions
+1 -1
View File
@@ -32,7 +32,7 @@ jobs:
tr '\n' ' ' | # remove line breaks
sed -e 's/<svg[^>]*>/<svg>/g' | # remove attributes from svg element
base64 -w 0 | # encode svg
sed "s|.*|<img width=\"400\" title=\"$file\" alt=\"$file\" src=\"https://lucide.dev/api/gh-icon/&.svg\"/> |"
sed "s|.*|<img width=\"400\" title=\"$file\" alt=\"$file\" src=\"https://lucide.dev/api/gh-icon/$(basename ${file//\.svg/})/&.svg\"/> |"
done | tr '\n' ' ' >> $GITHUB_OUTPUT
echo >> $GITHUB_OUTPUT
echo "$delimiter" >> $GITHUB_OUTPUT
+49 -42
View File
@@ -173,53 +173,60 @@ const ControlPath = ({
);
};
const SvgPreview = React.forwardRef<SVGSVGElement, { src: string; showGrid?: boolean }>(
({ src, showGrid = false }, ref) => {
const paths = getPaths(src);
const darkModeCss = `@media screen and (prefers-color-scheme: dark) {
const SvgPreview = React.forwardRef<
SVGSVGElement,
{
src: string | ReturnType<typeof getPaths>;
showGrid?: boolean;
} & React.SVGProps<SVGSVGElement>
>(({ src, children, showGrid = false, ...props }, ref) => {
const paths = typeof src === 'string' ? getPaths(src) : src;
const darkModeCss = `@media screen and (prefers-color-scheme: dark) {
.svg-preview-grid-group,
.svg-preview-shadow-mask-group,
.svg-preview-shadow-group {
stroke: #fff;
}
}`;
return (
<svg
ref={ref}
xmlns="http://www.w3.org/2000/svg"
width={24}
height={24}
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
>
<style>{darkModeCss}</style>
{showGrid && <Grid strokeWidth={0.1} stroke="#777" strokeOpacity={0.3} radius={1} />}
<Shadow paths={paths} strokeWidth={4} stroke="#777" radius={1} strokeOpacity={0.15} />
<ColoredPath
paths={paths}
colors={[
'#1982c4',
'#4267AC',
'#6a4c93',
'#B55379',
'#FF595E',
'#FF7655',
'#ff924c',
'#FFAE43',
'#ffca3a',
'#C5CA30',
'#8ac926',
'#52A675',
]}
/>
<ControlPath radius={1} paths={paths} pointSize={1} stroke="#fff" strokeWidth={0.125} />
</svg>
);
}
);
return (
<svg
ref={ref}
xmlns="http://www.w3.org/2000/svg"
width={24}
height={24}
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
{...props}
>
<style>{darkModeCss}</style>
{showGrid && <Grid strokeWidth={0.1} stroke="#777" strokeOpacity={0.3} radius={1} />}
<Shadow paths={paths} strokeWidth={4} stroke="#777" radius={1} strokeOpacity={0.15} />
<ColoredPath
paths={paths}
colors={[
'#1982c4',
'#4267AC',
'#6a4c93',
'#B55379',
'#FF595E',
'#FF7655',
'#ff924c',
'#FFAE43',
'#ffca3a',
'#C5CA30',
'#8ac926',
'#52A675',
]}
/>
<ControlPath radius={1} paths={paths} pointSize={1} stroke="#fff" strokeWidth={0.125} />
{children}
</svg>
);
});
export default SvgPreview;
+110
View File
@@ -0,0 +1,110 @@
import { getData } from 'src/lib/icons';
import SvgPreview from '../../../components/SvgPreview';
import { stringify } from 'svgson';
import { ReactNode } from 'react';
export default async function handler(req, res) {
// ReactDOMServer needs to be imported dynamically
// https://github.com/vercel/next.js/issues/43810
const ReactDOMServer = (await import('react-dom/server')).default;
const url = req.url.split('/');
const data = url.at(-1).slice(0, -4);
const src = Buffer.from(data, 'base64')
.toString('utf8')
.replace(/<svg[^>]*>|<\/svg>/g, '');
let backdrop: ReactNode;
try {
if (url.at(-2) !== 'gh-icon') {
const backdropString = stringify({
type: 'element',
name: 'svg',
value: undefined,
attributes: {},
children: (await getData(url.at(-2))).iconNode.map(([name, attributes]) => ({
type: 'element',
name,
attributes,
children: undefined,
value: undefined,
})),
}).replace(/<svg[^>]*>|<\/svg>/g, '');
backdrop = (
<>
<defs xmlns="http://www.w3.org/2000/svg">
<pattern
id="pattern"
width=".1"
height=".1"
patternUnits="userSpaceOnUse"
patternTransform="rotate(45 50 50)"
>
<line stroke="red" strokeWidth={0.1} y2={1} />
<line stroke="red" strokeWidth={0.1} y2={1} />
</pattern>
</defs>
<mask id="svg-preview-backdrop-mask-outline" maskUnits="userSpaceOnUse">
<g stroke="#fff" dangerouslySetInnerHTML={{ __html: backdropString }} />
<g dangerouslySetInnerHTML={{ __html: src }} />
</mask>
<mask id="svg-preview-backdrop-mask-fill" maskUnits="userSpaceOnUse">
<g stroke="#fff" dangerouslySetInnerHTML={{ __html: backdropString }} />
<g dangerouslySetInnerHTML={{ __html: src }} />
<g strokeWidth={1.75} dangerouslySetInnerHTML={{ __html: backdropString }} />
</mask>
<g
strokeWidth={2.25}
stroke="url(#pattern)"
mask={'url(#svg-preview-backdrop-mask-outline)'}
>
<rect
x="0"
y="0"
width="24"
height="24"
fill="url(#pattern)"
opacity={0.5}
stroke="none"
/>
<g dangerouslySetInnerHTML={{ __html: src }} />
</g>
<rect
x="0"
y="0"
width="24"
height="24"
fill="url(#pattern)"
stroke="none"
mask={'url(#svg-preview-backdrop-mask-fill)'}
/>
<rect
x="0"
y="0"
width="24"
height="24"
fill="red"
opacity={0.5}
stroke="none"
mask={'url(#svg-preview-backdrop-mask-fill)'}
/>
</>
);
}
} catch (e) {
backdrop = undefined;
}
const svg = Buffer.from(
ReactDOMServer.renderToString(
<SvgPreview src={src} showGrid>
{backdrop}
</SvgPreview>
)
);
res.setHeader('Cache-Control', 'public,max-age=31536000');
res.setHeader('Content-Type', 'image/svg+xml');
res.status(200).end(svg);
}
-19
View File
@@ -1,19 +0,0 @@
import SvgPreview from '../../../components/SvgPreview';
export default async function handler(req, res) {
// ReactDOMServer needs to be imported dynamically
// https://github.com/vercel/next.js/issues/43810
const ReactDOMServer = (await import('react-dom/server')).default;
const url = req.url.split('/').at(-1);
const data = url.slice(0, -4);
const src = Buffer.from(data, 'base64').toString('utf8');
const svg = Buffer.from(
ReactDOMServer.renderToString(<SvgPreview src={src} showGrid />)
);
res.setHeader('Cache-Control', 'public,max-age=31536000');
res.setHeader('Content-Type', 'image/svg+xml');
res.status(200).end(svg);
}