Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added github comment icon workflow #946

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
a0f3af2
feat: added github comment icon workflow
jguddas Feb 13, 2023
a6626bd
feat: improved segment node styling
jguddas Feb 16, 2023
7549da7
feat: improved segment node styling
jguddas Feb 16, 2023
966cc3c
fix: fixed grid alignment issue
jguddas Feb 16, 2023
7b1f630
chore: cleanup
jguddas Feb 16, 2023
bf474a9
fix: added ref forwarding to SvgPreview component
jguddas Feb 16, 2023
72f6c90
chore: removed svg preview from icon detail overlay
jguddas Mar 4, 2023
0fd655a
chore: updated tj-actions/changed-files
jguddas Mar 4, 2023
384fdf5
chore: switched to pull_request_target
jguddas Mar 4, 2023
99a4b82
chore: simplified path segment highlighting logic
jguddas Mar 5, 2023
9620bd4
Fixes incorrect relative links in documentation pages (#973)
karsa-mistmere Mar 5, 2023
0bf2f80
Add forklift icon (#943)
ericfennis Mar 5, 2023
09917f9
adds utility-pole icon (#971)
karsa-mistmere Mar 5, 2023
360adf9
:package: Bump lucide package versions to 0.123.0
Mar 5, 2023
5f17b43
Adds `nfc` icons (#960)
karsa-mistmere Mar 6, 2023
36766b9
:package: Bump lucide package versions to 0.124.0
Mar 6, 2023
0053fed
fix: updated pnpm-lock.yaml
jguddas Mar 10, 2023
2fd405f
fix: added missing api endpoint file
jguddas Mar 10, 2023
60fcc08
chore: fixed nextjs path name parsing in production
jguddas Mar 11, 2023
cd3c35a
Merge branch 'main' into feat/added-github-comment-icon-workflow
jguddas Mar 11, 2023
6f07f1d
chore: only run workflow when path includes icons/*.svg
jguddas Mar 11, 2023
308c2b8
chore: added Cache-Control header to gh-icon api route response
jguddas Mar 18, 2023
193df07
feat: added dark mode support to gh-icon
jguddas Mar 18, 2023
5979a61
feat: switched to using picture tag for gh-icon
jguddas Mar 18, 2023
f68edcd
feat: added space between gh-icons in pr comment
jguddas Mar 18, 2023
3ea3b3c
fix: changed icon size base back to 24x24
jguddas Mar 18, 2023
1c1a017
feat: added title to gh-icon comment image
jguddas Mar 18, 2023
ef98619
fix: changed gh-icon url
jguddas Mar 19, 2023
3ae1296
chore: added groups with class names
jguddas Mar 21, 2023
d3a75e7
feat: improved shadow masking
jguddas Mar 26, 2023
1b23dfd
Removes need for building duplicate icons by supporting CSS based dar…
Mar 27, 2023
2a3dd7a
chore: resolved type issues
Mar 27, 2023
d6a9f90
feat: changed image width from 48% to 400px
jguddas Apr 1, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions .github/workflows/pull-request.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: Pull request comment

on:
pull_request_target:
paths:
- 'icons/*.svg'

permissions:
pull-requests: write
contents: write

jobs:
Explore-GitHub-Actions:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
ref: refs/pull/${{ github.event.pull_request.number }}/merge
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v35
with:
files: icons/*.svg
- name: Generate comment
id: generate-comment
run: |
delimiter="$(openssl rand -hex 8)"
echo "body<<$delimiter" >> $GITHUB_OUTPUT
for file in ${{ steps.changed-files.outputs.all_changed_files }}; do
cat "$file" | # get file content
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\"/> |"
done | tr '\n' ' ' >> $GITHUB_OUTPUT
echo >> $GITHUB_OUTPUT
echo "$delimiter" >> $GITHUB_OUTPUT
- name: Find Comment
uses: peter-evans/find-comment@v2
id: fc
with:
issue-number: ${{ github.event.pull_request.number }}
comment-author: 'github-actions[bot]'
body-includes: Added or changed icons
- name: Create or update comment
uses: peter-evans/create-or-update-comment@v2
with:
comment-id: ${{ steps.fc.outputs.comment-id }}
issue-number: ${{ github.event.pull_request.number }}
body: |
Added or changed icons

${{ steps.generate-comment.outputs.body }}
edit-mode: replace
13 changes: 13 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion site/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"@next/mdx": "^11.0.0",
"@svgr/webpack": "^6.3.1",
"downloadjs": "^1.4.7",
"element-to-path": "^1.2.1",
"framer-motion": "^4",
"fuse.js": "^6.5.3",
"gray-matter": "^4.0.3",
Expand All @@ -38,7 +39,8 @@
"react-color": "^2.19.3",
"react-dom": "17.0.2",
"react-svg-loader": "^3.0.3",
"svgson": "^5.2.1"
"svgson": "^5.2.1",
"svg-pathdata": "^6.0.3"
},
"devDependencies": {
"@next/eslint-plugin-next": "^12.2.5",
Expand Down
2 changes: 1 addition & 1 deletion site/src/components/IconWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ interface IconWrapperProps extends SVGProps<SVGSVGElement> {
}

export const IconWrapper = forwardRef<SVGSVGElement, IconWrapperProps>((props, ref) => {
const defaultAttrs : SVGProps<SVGSVGElement>= {
const defaultAttrs: SVGProps<SVGSVGElement> = {
xmlns: 'http://www.w3.org/2000/svg',
width: '24px',
height: '24px',
Expand Down
225 changes: 225 additions & 0 deletions site/src/components/SvgPreview/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
import React from 'react';
import { PathProps, Path } from './types';
import { getPaths, assert } from './utils';

const Grid = ({
radius,
fill,
...props
}: {
strokeWidth: number;
radius: number;
} & PathProps<'stroke', 'strokeWidth'>) => (
<g className="svg-preview-grid-group" strokeLinecap="butt" {...props}>
<rect
width={24 - props.strokeWidth}
height={24 - props.strokeWidth}
x={props.strokeWidth / 2}
y={props.strokeWidth / 2}
rx={radius}
fill={fill}
/>
<path
d={
props.d ||
new Array(Math.floor(24 - 1))
.fill(null)
.flatMap((_, i) => [
`M${props.strokeWidth} ${i + 1}h${24 - props.strokeWidth * 2}`,
`M${i + 1} ${props.strokeWidth}v${24 - props.strokeWidth * 2}`,
])
.join('')
}
/>
</g>
);

const Shadow = ({
radius,
paths,
...props
}: {
radius: number;
paths: Path[];
} & PathProps<'stroke' | 'strokeWidth' | 'strokeOpacity', 'd'>) => {
const groupedPaths = Object.entries(
paths.reduce((groups, val) => {
const key = val.c.id;
groups[key] = [...(groups[key] || []), val];
return groups;
}, {} as Record<number, Path[]>)
);
return (
<>
<g className="svg-preview-shadow-mask-group" {...props}>
{groupedPaths.map(([id, paths]) => (
<mask
id={`svg-preview-shadow-mask-${id}`}
maskUnits="userSpaceOnUse"
strokeOpacity="1"
strokeWidth={props.strokeWidth}
stroke="#000"
>
<rect x={0} y={0} width={24} height={24} fill="#fff" stroke="none" rx={radius} />
<path
d={paths
.flatMap(({ prev, next }) => [
`M${prev.x} ${prev.y}h.01`,
`M${next.x} ${next.y}h.01`,
])
.filter((val, idx, arr) => arr.indexOf(val) === idx)
.join('')}
/>
</mask>
))}
</g>
<g className="svg-preview-shadow-group" {...props}>
{paths.map(({ d, c: { id } }, i) => (
<path key={i} mask={`url(#svg-preview-shadow-mask-${id})`} d={d} />
))}
<path
d={paths
.flatMap(({ prev, next }) => [`M${prev.x} ${prev.y}h.01`, `M${next.x} ${next.y}h.01`])
.filter((val, idx, arr) => arr.indexOf(val) === idx)
.join('')}
/>
</g>
</>
);
};

const ColoredPath = ({
colors,
paths,
...props
}: { paths: Path[]; colors: string[] } & PathProps<never, 'd' | 'stroke'>) => (
<g className="svg-preview-colored-path-group" {...props}>
{paths.map(({ d, c }, i) => (
<path key={i} d={d} stroke={colors[(c.name === 'path' ? i : c.id) % colors.length]} />
))}
</g>
);

const ControlPath = ({
paths,
radius,
pointSize,
...props
}: { pointSize: number; paths: Path[]; radius: number } & PathProps<
'stroke' | 'strokeWidth',
'd'
>) => {
const controlPaths = paths.map((path, i) => {
const element = paths.filter((p) => p.c.id === path.c.id);
const lastElement = element.at(-1)?.next;
assert(lastElement);
const isClosed = element[0].prev.x === lastElement.x && element[0].prev.y === lastElement.y;
const showMarker = !['rect', 'circle', 'ellipse'].includes(path.c.name);
return {
...path,
showMarker,
startMarker: showMarker && path.isStart && !isClosed,
endMarker: showMarker && paths[i + 1]?.isStart !== false && !isClosed,
};
});
return (
<>
<g
className="svg-preview-control-path-marker-mask-group"
strokeWidth={pointSize}
stroke="#000"
>
{controlPaths.map(({ prev, next, showMarker }, i) => {
return (
showMarker && (
<mask
id={`svg-preview-control-path-marker-mask-${i}`}
key={i}
maskUnits="userSpaceOnUse"
>
<rect x="0" y="0" width="24" height="24" fill="#fff" stroke="none" rx={radius} />
<path d={`M${prev.x} ${prev.y}h.01`} />
<path d={`M${next.x} ${next.y}h.01`} />
</mask>
)
);
})}
</g>
<g className="svg-preview-control-path-group" {...props}>
{controlPaths.map(({ d, showMarker }, i) => (
<path
key={i}
mask={showMarker ? `url(#svg-preview-control-path-marker-mask-${i})` : undefined}
d={d}
/>
))}
</g>
<g className="svg-preview-control-path-marker-group" {...props}>
<path
d={controlPaths
.flatMap(({ prev, next, showMarker }) =>
showMarker ? [`M${prev.x} ${prev.y}h.01`, `M${next.x} ${next.y}h.01`] : []
)
.join('')}
/>
{controlPaths.map(({ d, prev, next, startMarker, endMarker }, i) => (
<React.Fragment key={i}>
{startMarker && <circle cx={prev.x} cy={prev.y} r={pointSize / 2} />}
{endMarker && <circle cx={next.x} cy={next.y} r={pointSize / 2} />}
</React.Fragment>
))}
</g>
</>
);
};

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) {
.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>
);
}
);

export default SvgPreview;
21 changes: 21 additions & 0 deletions site/src/components/SvgPreview/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { SVGProps } from 'react';
import { getCommands } from './utils';

export type Point = { x: number; y: number };

export type Path = {
d: string;
prev: Point;
next: Point;
isStart: boolean;
c: ReturnType<typeof getCommands>[number];
};

export type PathProps<
RequiredProps extends keyof SVGProps<SVGPathElement | SVGRectElement | SVGCircleElement>,
NeverProps extends keyof SVGProps<SVGPathElement | SVGRectElement | SVGCircleElement>
> = Required<Pick<React.SVGProps<SVGElement & SVGRectElement & SVGCircleElement>, RequiredProps>> &
Omit<
React.SVGProps<SVGPathElement & SVGRectElement & SVGCircleElement>,
RequiredProps & NeverProps
>;
Loading