Update remaining assets and code, add webber
This commit is contained in:
78
src/docs/components/Meta.js
Normal file
78
src/docs/components/Meta.js
Normal file
@ -0,0 +1,78 @@
|
||||
import React from 'react';
|
||||
import Helmet from 'react-helmet';
|
||||
import { useStaticQuery, graphql } from 'gatsby';
|
||||
import '../styles/index.scss';
|
||||
|
||||
// ===================================================
|
||||
// Page metadata (using Helmet)
|
||||
// ===================================================
|
||||
const Meta = ({ description = '', lang = 'en', meta = [], title }) => {
|
||||
const { site, file } = useStaticQuery(
|
||||
graphql`
|
||||
query {
|
||||
site {
|
||||
siteMetadata {
|
||||
title
|
||||
description
|
||||
author
|
||||
}
|
||||
}
|
||||
file(relativePath: { eq: "logo.png" }) {
|
||||
id
|
||||
childImageSharp {
|
||||
fluid(maxHeight: 400) {
|
||||
src
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
const metaDescription = description || site.siteMetadata.description;
|
||||
|
||||
return (
|
||||
<Helmet
|
||||
htmlAttributes={{
|
||||
lang,
|
||||
}}
|
||||
title={title ? title : site.siteMetadata.title}
|
||||
titleTemplate={title ? `%s - ${site.siteMetadata.title}` : '%s'}
|
||||
meta={[
|
||||
{
|
||||
name: `description`,
|
||||
content: metaDescription,
|
||||
},
|
||||
{
|
||||
name: `author`,
|
||||
content: site.siteMetadata.author,
|
||||
},
|
||||
{
|
||||
name: `viewport`,
|
||||
content: `width=device-width, initial-scale=1`,
|
||||
},
|
||||
{
|
||||
name: `og:title`,
|
||||
content: site.siteMetadata.title,
|
||||
},
|
||||
{
|
||||
name: `og:description`,
|
||||
content: metaDescription,
|
||||
},
|
||||
{
|
||||
name: `og:type`,
|
||||
content: `website`,
|
||||
},
|
||||
{
|
||||
name: `og:image`,
|
||||
content: file.childImageSharp.fluid.src,
|
||||
},
|
||||
].concat(meta)}
|
||||
bodyAttributes={{
|
||||
class: 'card-page',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default Meta;
|
||||
22
src/docs/components/SVGs/BackArrowIcon.js
Normal file
22
src/docs/components/SVGs/BackArrowIcon.js
Normal file
@ -0,0 +1,22 @@
|
||||
import React from 'react';
|
||||
|
||||
const BackArrowIcon = ({ className, onClick }) => (
|
||||
<svg
|
||||
onClick={onClick}
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
width='18'
|
||||
height='18'
|
||||
viewBox='0 0 24 24'
|
||||
fill='none'
|
||||
stroke='currentColor'
|
||||
strokeWidth='2'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
className={`feather feather-arrow-left ${className}`}
|
||||
>
|
||||
<line x1='19' y1='12' x2='5' y2='12'></line>
|
||||
<polyline points='12 19 5 12 12 5'></polyline>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default BackArrowIcon;
|
||||
22
src/docs/components/SVGs/ClipboardIcon.js
Normal file
22
src/docs/components/SVGs/ClipboardIcon.js
Normal file
@ -0,0 +1,22 @@
|
||||
import React from 'react';
|
||||
|
||||
const ClipboardIcon = ({ className, onClick }) => (
|
||||
<svg
|
||||
onClick={onClick}
|
||||
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'
|
||||
className={`feather feather-clipboard ${className}`}
|
||||
>
|
||||
<path d='M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2'></path>
|
||||
<rect x='8' y='2' width='8' height='4' rx='1' ry='1'></rect>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default ClipboardIcon;
|
||||
23
src/docs/components/SVGs/CollapseClosedIcon.js
Normal file
23
src/docs/components/SVGs/CollapseClosedIcon.js
Normal file
@ -0,0 +1,23 @@
|
||||
import React from 'react';
|
||||
|
||||
const CollapseClosedIcon = ({ className, onClick }) => (
|
||||
<svg
|
||||
onClick={onClick}
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
width='16'
|
||||
height='16'
|
||||
viewBox='0 0 24 24'
|
||||
fill='none'
|
||||
stroke='currentColor'
|
||||
strokeWidth='1'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
className={`feather feather-plus-square ${className}`}
|
||||
>
|
||||
<rect x='3' y='3' width='18' height='18' rx='2' ry='2'></rect>
|
||||
<line x1='12' y1='8' x2='12' y2='16'></line>
|
||||
<line x1='8' y1='12' x2='16' y2='12'></line>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default CollapseClosedIcon;
|
||||
22
src/docs/components/SVGs/CollapseOpenIcon.js
Normal file
22
src/docs/components/SVGs/CollapseOpenIcon.js
Normal file
@ -0,0 +1,22 @@
|
||||
import React from 'react';
|
||||
|
||||
const CollapseOpenIcon = ({ className, onClick }) => (
|
||||
<svg
|
||||
onClick={onClick}
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
width='16'
|
||||
height='16'
|
||||
viewBox='0 0 24 24'
|
||||
fill='none'
|
||||
stroke='currentColor'
|
||||
strokeWidth='1'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
className={`feather feather-minus-square ${className}`}
|
||||
>
|
||||
<rect x='3' y='3' width='18' height='18' rx='2' ry='2'></rect>
|
||||
<line x1='8' y1='12' x2='16' y2='12'></line>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default CollapseOpenIcon;
|
||||
21
src/docs/components/SVGs/DarkModeIcon.js
Normal file
21
src/docs/components/SVGs/DarkModeIcon.js
Normal file
@ -0,0 +1,21 @@
|
||||
import React from 'react';
|
||||
|
||||
const DarkModeIcon = ({ className, onClick }) => (
|
||||
<svg
|
||||
onClick={onClick}
|
||||
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'
|
||||
className={`feather feather-moon ${className}`}
|
||||
>
|
||||
<path d='M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z'></path>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default DarkModeIcon;
|
||||
21
src/docs/components/SVGs/GithubIcon.js
Normal file
21
src/docs/components/SVGs/GithubIcon.js
Normal file
@ -0,0 +1,21 @@
|
||||
import React from 'react';
|
||||
|
||||
const GithubIcon = ({ className, onClick }) => (
|
||||
<svg
|
||||
onClick={onClick}
|
||||
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'
|
||||
className={`feather feather-github ${className}`}
|
||||
>
|
||||
<path d='M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22'></path>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default GithubIcon;
|
||||
29
src/docs/components/SVGs/LightModeIcon.js
Normal file
29
src/docs/components/SVGs/LightModeIcon.js
Normal file
@ -0,0 +1,29 @@
|
||||
import React from 'react';
|
||||
|
||||
const LightModeIcon = ({ className, onClick }) => (
|
||||
<svg
|
||||
onClick={onClick}
|
||||
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'
|
||||
className={`feather feather-sun ${className}`}
|
||||
>
|
||||
<circle cx='12' cy='12' r='5'></circle>
|
||||
<line x1='12' y1='1' x2='12' y2='3'></line>
|
||||
<line x1='12' y1='21' x2='12' y2='23'></line>
|
||||
<line x1='4.22' y1='4.22' x2='5.64' y2='5.64'></line>
|
||||
<line x1='18.36' y1='18.36' x2='19.78' y2='19.78'></line>
|
||||
<line x1='1' y1='12' x2='3' y2='12'></line>
|
||||
<line x1='21' y1='12' x2='23' y2='12'></line>
|
||||
<line x1='4.22' y1='19.78' x2='5.64' y2='18.36'></line>
|
||||
<line x1='18.36' y1='5.64' x2='19.78' y2='4.22'></line>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default LightModeIcon;
|
||||
26
src/docs/components/SVGs/ListIcon.js
Normal file
26
src/docs/components/SVGs/ListIcon.js
Normal file
@ -0,0 +1,26 @@
|
||||
import React from 'react';
|
||||
|
||||
const ListIcon = ({ className, onClick }) => (
|
||||
<svg
|
||||
onClick={onClick}
|
||||
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'
|
||||
className={`feather feather-list ${className}`}
|
||||
>
|
||||
<line x1='8' y1='6' x2='21' y2='6'></line>
|
||||
<line x1='8' y1='12' x2='21' y2='12'></line>
|
||||
<line x1='8' y1='18' x2='21' y2='18'></line>
|
||||
<line x1='3' y1='6' x2='3' y2='6'></line>
|
||||
<line x1='3' y1='12' x2='3' y2='12'></line>
|
||||
<line x1='3' y1='18' x2='3' y2='18'></line>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default ListIcon;
|
||||
24
src/docs/components/SVGs/SearchIcon.js
Normal file
24
src/docs/components/SVGs/SearchIcon.js
Normal file
@ -0,0 +1,24 @@
|
||||
import React from 'react';
|
||||
|
||||
const SearchIcon = ({ className, onClick }) => {
|
||||
return (
|
||||
<svg
|
||||
onClick={onClick}
|
||||
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'
|
||||
className={`feather feather-search ${className}`}
|
||||
>
|
||||
<circle cx='11' cy='11' r='8'></circle>
|
||||
<line x1='21' y1='21' x2='16.65' y2='16.65'></line>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default SearchIcon;
|
||||
25
src/docs/components/SVGs/ShareIcon.js
Normal file
25
src/docs/components/SVGs/ShareIcon.js
Normal file
@ -0,0 +1,25 @@
|
||||
import React from 'react';
|
||||
|
||||
const ShareIcon = ({ className, onClick }) => (
|
||||
<svg
|
||||
onClick={onClick}
|
||||
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'
|
||||
className={`feather feather-share-2 ${className}`}
|
||||
>
|
||||
<circle cx='18' cy='5' r='3'></circle>
|
||||
<circle cx='6' cy='12' r='3'></circle>
|
||||
<circle cx='18' cy='19' r='3'></circle>
|
||||
<line x1='8.59' y1='13.51' x2='15.42' y2='17.49'></line>
|
||||
<line x1='15.41' y1='6.51' x2='8.59' y2='10.49'></line>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default ShareIcon;
|
||||
28
src/docs/components/Search.js
Normal file
28
src/docs/components/Search.js
Normal file
@ -0,0 +1,28 @@
|
||||
import React from 'react';
|
||||
|
||||
// ===================================================
|
||||
// Simple, stateful search component
|
||||
// ===================================================
|
||||
const Search = ({ defaultValue = '', setSearchQuery, className = '' }) => {
|
||||
const [value, setValue] = React.useState(defaultValue);
|
||||
|
||||
React.useEffect(() => {
|
||||
setSearchQuery(value);
|
||||
}, [value]);
|
||||
|
||||
return (
|
||||
<input
|
||||
defaultValue={defaultValue}
|
||||
className='search-box'
|
||||
type='search'
|
||||
id='searchInput'
|
||||
placeholder='Search...'
|
||||
aria-label='Snippet search'
|
||||
onKeyUp={e => {
|
||||
setValue(e.target.value);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default Search;
|
||||
148
src/docs/components/Shell.js
Normal file
148
src/docs/components/Shell.js
Normal file
@ -0,0 +1,148 @@
|
||||
import React from 'react';
|
||||
import { graphql, useStaticQuery } from 'gatsby';
|
||||
import { connect } from 'react-redux';
|
||||
import AniLink from 'gatsby-plugin-transition-link/AniLink';
|
||||
import ReactCSSTransitionReplace from 'react-css-transition-replace';
|
||||
import config from '../../../config';
|
||||
|
||||
import { toggleDarkMode } from '../state/app';
|
||||
|
||||
import SearchIcon from './SVGs/SearchIcon';
|
||||
import GithubIcon from './SVGs/GithubIcon';
|
||||
import DarkModeIcon from './SVGs/DarkModeIcon';
|
||||
import LightModeIcon from './SVGs/LightModeIcon';
|
||||
import ListIcon from './SVGs/ListIcon';
|
||||
|
||||
// ===================================================
|
||||
// Application-level UI component
|
||||
// ===================================================
|
||||
const Shell = ({
|
||||
isDarkMode,
|
||||
isSearch,
|
||||
isList,
|
||||
dispatch,
|
||||
withIcon = true,
|
||||
withTitle = true,
|
||||
children,
|
||||
}) => {
|
||||
const data = useStaticQuery(graphql`
|
||||
query SiteTitleQuery {
|
||||
site {
|
||||
siteMetadata {
|
||||
title
|
||||
description
|
||||
}
|
||||
}
|
||||
file(relativePath: { eq: "30s-icon.png" }) {
|
||||
id
|
||||
childImageSharp {
|
||||
original {
|
||||
src
|
||||
}
|
||||
}
|
||||
}
|
||||
snippetDataJson(meta: { type: { eq: "snippetListingArray" } }) {
|
||||
data {
|
||||
title
|
||||
id
|
||||
attributes {
|
||||
tags
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`);
|
||||
let viewportWidth = typeof window !== 'undefined' && window.innerWidth;
|
||||
|
||||
return (
|
||||
<div className={isDarkMode ? 'page-container dark' : 'page-container'}>
|
||||
{/* Menu */}
|
||||
<header className='menu'>
|
||||
<AniLink
|
||||
cover
|
||||
direction={viewportWidth < 600 ? 'up' : 'right'}
|
||||
bg={isDarkMode ? '#434E76' : '#FFFFFF'}
|
||||
to='/search'
|
||||
aria-label='Search'
|
||||
className={isSearch ? 'menu-button active' : 'menu-button'}
|
||||
>
|
||||
<SearchIcon />
|
||||
</AniLink>
|
||||
<AniLink
|
||||
cover
|
||||
direction={viewportWidth < 600 ? 'up' : 'right'}
|
||||
bg={isDarkMode ? '#434E76' : '#FFFFFF'}
|
||||
to='/list'
|
||||
aria-label='Snippet list'
|
||||
className={isList ? 'menu-button active' : 'menu-button'}
|
||||
>
|
||||
<ListIcon />
|
||||
</AniLink>
|
||||
{/* eslint-disable-next-line */}
|
||||
<a target='_blank'
|
||||
rel='noopener'
|
||||
href={config.repositoryUrl}
|
||||
aria-label='View on GitHub'
|
||||
className='menu-button'
|
||||
>
|
||||
<GithubIcon />
|
||||
</a>
|
||||
{/*
|
||||
The use of React.Fragment here will cause a lot of errors to show up in webber:dev.
|
||||
Truth is, this is the only decent way I found to deal with this module's odd behavior.
|
||||
Keep as is, unless you can provide a better solution, in which case please send a PR.
|
||||
*/}
|
||||
<ReactCSSTransitionReplace
|
||||
transitionName='cross-fade'
|
||||
transitionEnterTimeout={300}
|
||||
transitionLeaveTimeout={300}
|
||||
component='button'
|
||||
className='menu-button'
|
||||
childComponent={React.Fragment}
|
||||
>
|
||||
{isDarkMode ? (
|
||||
<LightModeIcon
|
||||
key='lmit'
|
||||
onClick={() => dispatch(toggleDarkMode(!isDarkMode))}
|
||||
/>
|
||||
) : (
|
||||
<DarkModeIcon
|
||||
key='dmit'
|
||||
onClick={() => dispatch(toggleDarkMode(!isDarkMode))}
|
||||
/>
|
||||
)}
|
||||
</ReactCSSTransitionReplace>
|
||||
</header>
|
||||
{/* Content */}
|
||||
<div className='content'>
|
||||
{withTitle ? (
|
||||
<h1 className='website-title'>
|
||||
{data.site.siteMetadata.title}
|
||||
{withIcon ? (
|
||||
<img
|
||||
src={data.file.childImageSharp.original.src}
|
||||
alt='Logo'
|
||||
className='website-logo'
|
||||
/>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
</h1>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default connect(
|
||||
state => ({
|
||||
isDarkMode: state.app.isDarkMode,
|
||||
lastPageTitle: state.app.lastPageTitle,
|
||||
lastPageUrl: state.app.lastPageUrl,
|
||||
searchQuery: state.app.searchQuery,
|
||||
}),
|
||||
null,
|
||||
)(Shell);
|
||||
21
src/docs/components/SimpleCard.js
Normal file
21
src/docs/components/SimpleCard.js
Normal file
@ -0,0 +1,21 @@
|
||||
import React from 'react';
|
||||
|
||||
// ===================================================
|
||||
// Generic card (displays textual content)
|
||||
// ===================================================
|
||||
const SimpleCard = ({
|
||||
title,
|
||||
children,
|
||||
isDarkMode
|
||||
}) => (
|
||||
<div className='card short'>
|
||||
<h4 className='card-title'>
|
||||
{title}
|
||||
</h4>
|
||||
<div className='card-description'>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default SimpleCard;
|
||||
175
src/docs/components/SnippetCard.js
Normal file
175
src/docs/components/SnippetCard.js
Normal file
@ -0,0 +1,175 @@
|
||||
import React from 'react';
|
||||
import { CopyToClipboard } from 'react-copy-to-clipboard';
|
||||
import config from '../../../config';
|
||||
|
||||
import { getTextualContent, getCodeBlocks, optimizeAllNodes } from '../util';
|
||||
import ClipboardIcon from './SVGs/ClipboardIcon';
|
||||
// import ShareIcon from './SVGs/ShareIcon';
|
||||
import AniLink from 'gatsby-plugin-transition-link/AniLink';
|
||||
import CollapseOpenIcon from './SVGs/CollapseOpenIcon';
|
||||
import CollapseClosedIcon from './SVGs/CollapseClosedIcon';
|
||||
import ReactCSSTransitionReplace from 'react-css-transition-replace';
|
||||
|
||||
// ===================================================
|
||||
// Snippet Card HOC - check components below for more
|
||||
// ===================================================
|
||||
const SnippetCard = ({ short, snippetData, ...rest }) => {
|
||||
let difficulty = snippetData.tags.includes('advanced')
|
||||
? 'advanced'
|
||||
: snippetData.tags.includes('beginner')
|
||||
? 'beginner'
|
||||
: 'intermediate';
|
||||
return short ? (
|
||||
<ShortCard snippetData={snippetData} difficulty={difficulty} {...rest} />
|
||||
) : (
|
||||
<FullCard snippetData={snippetData} difficulty={difficulty} {...rest} />
|
||||
);
|
||||
};
|
||||
|
||||
// ===================================================
|
||||
// Simple card corner for difficulty display
|
||||
// ===================================================
|
||||
const CardCorner = ({ difficulty = 'intermediate' }) => (
|
||||
<div
|
||||
className={`card-corner ${difficulty}`}
|
||||
aria-label={difficulty}
|
||||
title={difficulty}
|
||||
/>
|
||||
);
|
||||
|
||||
// ===================================================
|
||||
// Full snippet view (tags, code, title, description)
|
||||
// ===================================================
|
||||
const FullCard = ({ snippetData, difficulty, isDarkMode }) => {
|
||||
const [examplesOpen, setExamplesOpen] = React.useState(false);
|
||||
const tags = snippetData.tags;
|
||||
let cardCodeHtml = `${optimizeAllNodes(
|
||||
getCodeBlocks(snippetData.html).code,
|
||||
)}`;
|
||||
let cardExamplesHtml = `${optimizeAllNodes(
|
||||
getCodeBlocks(snippetData.html).example,
|
||||
)}`;
|
||||
return (
|
||||
<div className='card'>
|
||||
<CardCorner difficulty={difficulty} />
|
||||
<h4 className='card-title'>{snippetData.title}</h4>
|
||||
{tags.map(tag => (
|
||||
<span className='tag' key={`tag_${tag}`}>{tag}</span>
|
||||
))}
|
||||
<div
|
||||
className='card-description'
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `${getTextualContent(snippetData.html)}`,
|
||||
}}
|
||||
/>
|
||||
<div className='card-bottom'>
|
||||
<CopyToClipboard
|
||||
text={snippetData.code}
|
||||
onCopy={() => {
|
||||
let tst = document.createElement('div');
|
||||
tst.classList = 'toast';
|
||||
tst.innerHTML = 'Snippet copied to clipboard!';
|
||||
document.body.appendChild(tst);
|
||||
setTimeout(function() {
|
||||
tst.style.opacity = 0;
|
||||
setTimeout(function() {
|
||||
document.body.removeChild(tst);
|
||||
}, 300);
|
||||
}, 1700);
|
||||
}}
|
||||
>
|
||||
<button
|
||||
className='button button-a button-copy'
|
||||
aria-label='Copy to clipboard'
|
||||
>
|
||||
<ClipboardIcon />
|
||||
</button>
|
||||
</CopyToClipboard>
|
||||
{/* <button className="button button-b button-social-sh" aria-label="Share">
|
||||
<ShareIcon />
|
||||
</button> */}
|
||||
<pre
|
||||
className={`card-code language-${config.language}`}
|
||||
dangerouslySetInnerHTML={{ __html: cardCodeHtml }}
|
||||
/>
|
||||
<button
|
||||
className='button button-example-toggler'
|
||||
onClick={() => setExamplesOpen(!examplesOpen)}
|
||||
>
|
||||
{examplesOpen ? <CollapseOpenIcon /> : <CollapseClosedIcon />}Examples
|
||||
</button>
|
||||
<ReactCSSTransitionReplace
|
||||
transitionName='roll-up'
|
||||
transitionEnterTimeout={300}
|
||||
transitionLeaveTimeout={300}
|
||||
>
|
||||
{examplesOpen && (
|
||||
<pre
|
||||
className='section card-examples language-js'
|
||||
dangerouslySetInnerHTML={{ __html: cardExamplesHtml }}
|
||||
/>
|
||||
)}
|
||||
</ReactCSSTransitionReplace>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// ===================================================
|
||||
// Short snippet view (title, description, code*)
|
||||
// ===================================================
|
||||
const ShortCard = ({ snippetData, difficulty, isDarkMode }) => {
|
||||
let cardCodeHtml = `${optimizeAllNodes(
|
||||
getCodeBlocks(snippetData.html).code,
|
||||
)}`;
|
||||
return (
|
||||
<div className='card short'>
|
||||
<CardCorner difficulty={difficulty} />
|
||||
<h4 className='card-title'>
|
||||
<AniLink
|
||||
paintDrip
|
||||
to={`/${snippetData.id}`}
|
||||
hex={isDarkMode ? '#434E76' : '#FFFFFF'}
|
||||
>
|
||||
{snippetData.title}
|
||||
</AniLink>
|
||||
</h4>
|
||||
<div
|
||||
className='card-description'
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `${getTextualContent(snippetData.html)}`,
|
||||
}}
|
||||
/>
|
||||
<div className='card-bottom'>
|
||||
<CopyToClipboard
|
||||
text={snippetData.code}
|
||||
onCopy={() => {
|
||||
let tst = document.createElement('div');
|
||||
tst.classList = 'toast';
|
||||
tst.innerHTML = 'Snippet copied to clipboard!';
|
||||
document.body.appendChild(tst);
|
||||
setTimeout(function() {
|
||||
tst.style.opacity = 0;
|
||||
setTimeout(function() {
|
||||
document.body.removeChild(tst);
|
||||
}, 300);
|
||||
}, 1700);
|
||||
}}
|
||||
>
|
||||
<button
|
||||
className='button button-a button-copy'
|
||||
aria-label='Copy to clipboard'
|
||||
>
|
||||
<ClipboardIcon />
|
||||
</button>
|
||||
</CopyToClipboard>
|
||||
<pre
|
||||
className={`card-code language-${config.language}`}
|
||||
dangerouslySetInnerHTML={{ __html: cardCodeHtml }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SnippetCard;
|
||||
Reference in New Issue
Block a user