mirror of
https://github.com/pawelmalak/snippet-box.git
synced 2025-12-21 21:33:10 +01:00
Finished snippet view. Added support for markdown docs
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useContext } from 'react';
|
||||
import { Snippet } from '../../typescript/interfaces';
|
||||
import { dateParser } from '../../utils';
|
||||
import { Card } from '../UI';
|
||||
import { Badge, Button, Card } from '../UI';
|
||||
import { SnippetsContext } from '../../store';
|
||||
|
||||
interface Props {
|
||||
snippet: Snippet;
|
||||
@@ -8,6 +11,7 @@ interface Props {
|
||||
|
||||
export const SnippetCard = (props: Props): JSX.Element => {
|
||||
const { title, description, language, code, id, updatedAt } = props.snippet;
|
||||
const { setSnippet } = useContext(SnippetsContext);
|
||||
|
||||
const copyHandler = () => {
|
||||
navigator.clipboard.writeText(code);
|
||||
@@ -18,7 +22,31 @@ export const SnippetCard = (props: Props): JSX.Element => {
|
||||
<h6 className='card-subtitle mb-2 text-muted'>
|
||||
{dateParser(updatedAt).relative}
|
||||
</h6>
|
||||
<p onClick={copyHandler}>{language}</p>
|
||||
<p className='text-truncate'>
|
||||
{description ? description : 'No description'}
|
||||
</p>
|
||||
<Badge text={language} color='success' />
|
||||
<hr />
|
||||
<div className='d-flex justify-content-end'>
|
||||
<Link
|
||||
to={{
|
||||
pathname: `/snippet/${id}`,
|
||||
state: { from: window.location.pathname }
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
text='View'
|
||||
color='dark'
|
||||
small
|
||||
outline
|
||||
classes='me-2'
|
||||
handler={() => {
|
||||
setSnippet(id);
|
||||
}}
|
||||
/>
|
||||
</Link>
|
||||
<Button text='Copy code' color='dark' small handler={copyHandler} />
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
17
client/src/components/Snippets/SnippetCode.tsx
Normal file
17
client/src/components/Snippets/SnippetCode.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
interface Props {
|
||||
code: string;
|
||||
}
|
||||
|
||||
export const SnippetCode = (props: Props): JSX.Element => {
|
||||
return (
|
||||
<div className='mb-3'>
|
||||
<textarea
|
||||
className='form-control'
|
||||
id='code'
|
||||
rows={10}
|
||||
value={props.code}
|
||||
disabled
|
||||
></textarea>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -57,7 +57,7 @@ export const SnippetDetails = (props: Props): JSX.Element => {
|
||||
<Button text='Edit' color='dark' small outline classes='me-3' />
|
||||
</Link>
|
||||
<Button
|
||||
text='Pin to homescreen'
|
||||
text='Pin snippet'
|
||||
color='dark'
|
||||
small
|
||||
outline
|
||||
|
||||
13
client/src/components/Snippets/SnippetDocs.tsx
Normal file
13
client/src/components/Snippets/SnippetDocs.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
|
||||
interface Props {
|
||||
markdown: string;
|
||||
}
|
||||
|
||||
export const SnippetDocs = (props: Props): JSX.Element => {
|
||||
return (
|
||||
<div>
|
||||
<ReactMarkdown>{props.markdown}</ReactMarkdown>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -11,7 +11,7 @@ export const SnippetGrid = (props: Props): JSX.Element => {
|
||||
return (
|
||||
<div className='row'>
|
||||
{snippets.map(snippet => (
|
||||
<div className='col-12 col-md-6 col-lg-3' key={snippet.id}>
|
||||
<div className='col-12 col-md-6 col-lg-4' key={snippet.id}>
|
||||
<SnippetCard snippet={snippet} />
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -3,7 +3,6 @@ import { useLocation, useParams } from 'react-router-dom';
|
||||
import { SnippetForm } from '../components/Snippets/SnippetForm';
|
||||
import { Layout, PageHeader } from '../components/UI';
|
||||
import { SnippetsContext } from '../store';
|
||||
import { Snippet } from '../typescript/interfaces';
|
||||
|
||||
interface Params {
|
||||
id?: string;
|
||||
@@ -28,7 +27,7 @@ export const Editor = (): JSX.Element => {
|
||||
setCurrentSnippet(+id);
|
||||
setInEdit(true);
|
||||
}
|
||||
}, []);
|
||||
}, [id, setCurrentSnippet]);
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
|
||||
54
client/src/containers/Snippet.tsx
Normal file
54
client/src/containers/Snippet.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
import { Fragment, useContext, useEffect } from 'react';
|
||||
import { useParams, useLocation } from 'react-router-dom';
|
||||
import { SnippetCode } from '../components/Snippets/SnippetCode';
|
||||
import { Layout, PageHeader, Spinner, Card } from '../components/UI';
|
||||
import { SnippetsContext } from '../store';
|
||||
import { SnippetDetails } from '../components/Snippets/SnippetDetails';
|
||||
import { SnippetDocs } from '../components/Snippets/SnippetDocs';
|
||||
|
||||
interface Params {
|
||||
id: string;
|
||||
}
|
||||
|
||||
export const Snippet = (): JSX.Element => {
|
||||
const { currentSnippet, getSnippetById } = useContext(SnippetsContext);
|
||||
const { id } = useParams<Params>();
|
||||
|
||||
// Get previous location
|
||||
const location = useLocation<{ from: string }>();
|
||||
const { from } = location.state || '/snippets';
|
||||
|
||||
useEffect(() => {
|
||||
getSnippetById(+id);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
{!currentSnippet ? (
|
||||
<div className='col-12'>
|
||||
<Spinner />
|
||||
</div>
|
||||
) : (
|
||||
<Fragment>
|
||||
<PageHeader title='' prevDest={from} />
|
||||
<div className='row mt-3'>
|
||||
<div className='col-12 col-md-7 col-lg-8'>
|
||||
<SnippetCode code={currentSnippet.code} />
|
||||
</div>
|
||||
<div className='col-12 col-md-5 col-lg-4'>
|
||||
<SnippetDetails snippet={currentSnippet} />
|
||||
</div>
|
||||
{currentSnippet.docs && (
|
||||
<div className='col-12'>
|
||||
<Card title='Snippet documentation'>
|
||||
<hr />
|
||||
<SnippetDocs markdown={currentSnippet.docs} />
|
||||
</Card>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Fragment>
|
||||
)}
|
||||
</Layout>
|
||||
);
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useEffect, useContext, useState, Fragment } from 'react';
|
||||
import { SnippetsContext } from '../store';
|
||||
import { SnippetGrid } from '../components/Snippets/SnippetGrid';
|
||||
import { Badge, Button, Card, Layout, List, Spinner } from '../components/UI';
|
||||
import { Badge, Button, Card, Layout, Spinner } from '../components/UI';
|
||||
import { Snippet } from '../typescript/interfaces';
|
||||
|
||||
export const Snippets = (): JSX.Element => {
|
||||
@@ -9,13 +9,17 @@ export const Snippets = (): JSX.Element => {
|
||||
useContext(SnippetsContext);
|
||||
|
||||
const [filter, setFilter] = useState<string | null>(null);
|
||||
const [localSnippets, setLocalSnippets] = useState<Snippet[]>([...snippets]);
|
||||
const [localSnippets, setLocalSnippets] = useState<Snippet[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
getSnippets();
|
||||
countSnippets();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setLocalSnippets([...snippets]);
|
||||
}, [snippets]);
|
||||
|
||||
const filterHandler = (language: string) => {
|
||||
setFilter(language);
|
||||
const filteredSnippets = snippets.filter(s => s.language === language);
|
||||
@@ -29,7 +33,7 @@ export const Snippets = (): JSX.Element => {
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<div className='col-12 col-md-4 col-lg-2'>
|
||||
<div className='col-12 col-md-4 col-lg-3'>
|
||||
<Card title='Filter by language'>
|
||||
<Fragment>
|
||||
{languageCount.map((el, idx) => {
|
||||
@@ -38,7 +42,7 @@ export const Snippets = (): JSX.Element => {
|
||||
return (
|
||||
<div
|
||||
className={`d-flex justify-content-between cursor-pointer ${
|
||||
isActiveFilter && 'text-primary fw-bold'
|
||||
isActiveFilter && 'text-dark fw-bold'
|
||||
}`}
|
||||
key={idx}
|
||||
onClick={() => filterHandler(el.language)}
|
||||
@@ -52,7 +56,7 @@ export const Snippets = (): JSX.Element => {
|
||||
<div className='d-grid mt-3'>
|
||||
<Button
|
||||
text='Clear filters'
|
||||
color='primary'
|
||||
color='dark'
|
||||
small
|
||||
outline
|
||||
handler={clearFilterHandler}
|
||||
@@ -60,7 +64,7 @@ export const Snippets = (): JSX.Element => {
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
<div className='col-12 col-md-8 col-lg-10'>
|
||||
<div className='col-12 col-md-8 col-lg-9'>
|
||||
{snippets.length > 0 ? (
|
||||
<SnippetGrid snippets={localSnippets} />
|
||||
) : (
|
||||
|
||||
Reference in New Issue
Block a user