add post preview card
This commit is contained in:
parent
58c880bf18
commit
a6a7eaf20c
@ -1,6 +1,6 @@
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import PersonalCard from './PersonalCard';
|
||||
|
||||
storiesOf('Components/Personal Card', module).add('with emoji', () => {
|
||||
storiesOf('Components/Personal Card', module).add('static', () => {
|
||||
return <PersonalCard />;
|
||||
});
|
||||
|
@ -2,7 +2,6 @@ import * as React from 'react';
|
||||
import '@fortawesome/fontawesome-svg-core';
|
||||
import { faEnvelope } from '@fortawesome/free-solid-svg-icons';
|
||||
import {
|
||||
faTelegram,
|
||||
faTwitter,
|
||||
faGithub,
|
||||
faLinkedin,
|
||||
@ -32,7 +31,7 @@ const ContactComp = ({ id, type }: Contact) => {
|
||||
return (
|
||||
<div className={'p-3'}>
|
||||
<a
|
||||
className={'text-5xl text-gray-600' + ' ' + iconDescr[1]}
|
||||
className={'text-5xl text-gray-500' + ' ' + iconDescr[1]}
|
||||
href={contactToLink({ id, type })}
|
||||
target="_blank">
|
||||
<FontAwesomeIcon key={type} icon={iconDescr[0]} />
|
||||
|
56
components/post-feed/PostFeed.stories.tsx
Normal file
56
components/post-feed/PostFeed.stories.tsx
Normal file
@ -0,0 +1,56 @@
|
||||
import { storiesOf } from '@storybook/react';
|
||||
// import { DateTime } from 'luxon';
|
||||
// import PostOverviewCard, { PostOverviewCardProps } from './PostOverviewCard';
|
||||
|
||||
// storiesOf('Components/Post Overview Card', module)
|
||||
// .add('static', () => {
|
||||
// const defaultPreview: PostOverviewCardProps = {
|
||||
// createdAt: DateTime.now(),
|
||||
// createdBy: {
|
||||
// name: 'fido_node',
|
||||
// avatar: 'avatar.jpg'
|
||||
// },
|
||||
// path: '/blog/asdasdasdasd',
|
||||
// title: 'Awesome title',
|
||||
// preview: {},
|
||||
// tags: [{ text: 'asdf' }, { text: 'ghjkl' }, { text: 'qwertyuioio' }]
|
||||
// };
|
||||
|
||||
// return <PostOverviewCard {...defaultPreview} />;
|
||||
// })
|
||||
// .add('with preview', () => {
|
||||
// const defaultPreview: PostOverviewCardProps = {
|
||||
// createdAt: DateTime.now(),
|
||||
// createdBy: {
|
||||
// name: 'fido_node',
|
||||
// avatar: 'avatar.jpg'
|
||||
// },
|
||||
// path: '/blog/asdasdasdasd',
|
||||
// title: 'Awesome title',
|
||||
// preview: {
|
||||
// headerMedia: {
|
||||
// key: 'Image',
|
||||
// url: './pattern.png',
|
||||
// alt: 'awesome pattern'
|
||||
// }
|
||||
// },
|
||||
// tags: [{ text: 'asdf' }, { text: 'ghjkl' }, { text: 'qwertyuioio' }]
|
||||
// };
|
||||
|
||||
// return <PostOverviewCard {...defaultPreview} />;
|
||||
// })
|
||||
// .add('without tags', () => {
|
||||
// const defaultPreview: PostOverviewCardProps = {
|
||||
// createdAt: DateTime.now(),
|
||||
// createdBy: {
|
||||
// name: 'fido_node',
|
||||
// avatar: 'avatar.jpg'
|
||||
// },
|
||||
// path: '/blog/asdasdasdasd',
|
||||
// title: 'Awesome title',
|
||||
// preview: {},
|
||||
// tags: []
|
||||
// };
|
||||
|
||||
// return <PostOverviewCard {...defaultPreview} />;
|
||||
// });
|
8
components/post-feed/PostFeed.tsx
Normal file
8
components/post-feed/PostFeed.tsx
Normal file
@ -0,0 +1,8 @@
|
||||
import * as React from 'react';
|
||||
import { PostOverviewCardProps } from '../post-overview/PostOverviewCard';
|
||||
|
||||
export type PostFeedProps = {
|
||||
posts: PostOverviewCardProps[];
|
||||
};
|
||||
|
||||
export default ({ posts }: PostFeedProps) => <div></div>;
|
61
components/post-overview/PostOverviewCard.stories.tsx
Normal file
61
components/post-overview/PostOverviewCard.stories.tsx
Normal file
@ -0,0 +1,61 @@
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import { DateTime } from 'luxon';
|
||||
import PostOverviewCard, { PostOverviewCardProps } from './PostOverviewCard';
|
||||
|
||||
storiesOf('Components/Post Overview Card', module)
|
||||
.add('static', () => {
|
||||
const defaultPreview: PostOverviewCardProps = {
|
||||
createdAt: DateTime.now(),
|
||||
createdBy: {
|
||||
name: 'fido_node',
|
||||
avatar: 'avatar.jpg'
|
||||
},
|
||||
path: '/blog/asdasdasdasd',
|
||||
title: ' The Ultimate Web Developer Resources List 🔥Awesome 200+ Resources',
|
||||
preview: {},
|
||||
tags: [{ text: 'asdf' }, { text: 'ghjkl' }, { text: 'qwertyuioio' }]
|
||||
};
|
||||
|
||||
return <PostOverviewCard {...defaultPreview} />;
|
||||
})
|
||||
.add('with preview', () => {
|
||||
const defaultPreview: PostOverviewCardProps = {
|
||||
createdAt: DateTime.now(),
|
||||
createdBy: {
|
||||
name: 'fido_node',
|
||||
avatar: 'avatar.jpg'
|
||||
},
|
||||
path: '/blog/asdasdasdasd',
|
||||
title: 'How to create your personal static blog and do not lost your mind. 🔥',
|
||||
preview: {
|
||||
headerMedia: {
|
||||
key: 'Image',
|
||||
url: './pattern.png',
|
||||
alt: 'awesome pattern'
|
||||
}
|
||||
},
|
||||
tags: [
|
||||
{ text: 'next.js' },
|
||||
{ text: 'typescript' },
|
||||
{ text: 'tailwind' },
|
||||
{ text: 'storybook' }
|
||||
]
|
||||
};
|
||||
|
||||
return <PostOverviewCard {...defaultPreview} />;
|
||||
})
|
||||
.add('without tags', () => {
|
||||
const defaultPreview: PostOverviewCardProps = {
|
||||
createdAt: DateTime.now(),
|
||||
createdBy: {
|
||||
name: 'fido_node',
|
||||
avatar: 'avatar.jpg'
|
||||
},
|
||||
path: '/blog/asdasdasdasd',
|
||||
title: ' The Ultimate Web Developer Resources List 🔥Awesome 200+ Resources',
|
||||
preview: {},
|
||||
tags: []
|
||||
};
|
||||
|
||||
return <PostOverviewCard {...defaultPreview} />;
|
||||
});
|
50
components/post-overview/PostOverviewCard.tsx
Normal file
50
components/post-overview/PostOverviewCard.tsx
Normal file
@ -0,0 +1,50 @@
|
||||
import { DateTime } from 'luxon';
|
||||
import * as React from 'react';
|
||||
import { PostBody, Preview, Tag } from '../../src/posts/post';
|
||||
import { User } from '../../src/user/user';
|
||||
import { isDefined } from '../../src/util/types';
|
||||
import Avatar from './elements/Avatar';
|
||||
import NameAndData from './elements/NameAndData';
|
||||
import Share from './elements/Share';
|
||||
import Tags from './elements/Tags';
|
||||
|
||||
export type PostOverviewCardProps = {
|
||||
createdAt: DateTime;
|
||||
createdBy: User;
|
||||
path: string;
|
||||
title: string;
|
||||
preview: Preview;
|
||||
tags: Tag[];
|
||||
};
|
||||
|
||||
export default ({ createdBy, createdAt, title, tags, path, preview }: PostOverviewCardProps) => (
|
||||
<div className={'flex flex-col border rounded-2xl border-gray-300 overflow-hidden max-w-4xl'}>
|
||||
{isDefined(preview.headerMedia) ? (
|
||||
<img
|
||||
src={preview.headerMedia.url}
|
||||
alt={preview.headerMedia.alt}
|
||||
className={'object-cover h-52 w-full'}></img>
|
||||
) : null}
|
||||
|
||||
<div className={'flex flex-row pt-2 pl-2'}>
|
||||
<Avatar imgUrl={createdBy.avatar} />
|
||||
<NameAndData name={createdBy.name} date={createdAt} />
|
||||
</div>
|
||||
<div className={'flex flex-row px-16'}>
|
||||
<a className={'text-4xl font-bold hover:text-purple-500 '} href={path}>
|
||||
{title}
|
||||
</a>
|
||||
</div>
|
||||
{tags.length > 0 ? (
|
||||
<div className={'flex flex-row pl-16 pt-2'}>
|
||||
<Tags tags={tags} />
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<div className={'flex flex-row px-16 self-end'}>
|
||||
<Share link={path} text={title} />
|
||||
</div>
|
||||
|
||||
<div className={'flex flex-row pl-16 pb-6'}></div>
|
||||
</div>
|
||||
);
|
9
components/post-overview/elements/Avatar.tsx
Normal file
9
components/post-overview/elements/Avatar.tsx
Normal file
@ -0,0 +1,9 @@
|
||||
import * as React from 'react';
|
||||
|
||||
type Props = {
|
||||
imgUrl: string;
|
||||
};
|
||||
|
||||
export default ({ imgUrl }: Props) => (
|
||||
<img src={imgUrl} className={'border rounded-full w-10 h-10 m-2'}></img>
|
||||
);
|
16
components/post-overview/elements/NameAndData.tsx
Normal file
16
components/post-overview/elements/NameAndData.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
import { DateTime } from 'luxon';
|
||||
import * as React from 'react';
|
||||
|
||||
type Props = {
|
||||
name: string;
|
||||
date: DateTime;
|
||||
};
|
||||
|
||||
export default ({ name, date }: Props) => (
|
||||
<div className="flex-row pt-1 text-gray-500">
|
||||
<div className={'hover:text-black'}>{name}</div>
|
||||
<div className={'text-sm hover:text-black'}>
|
||||
{date.toLocaleString({ month: 'long', day: 'numeric' })}
|
||||
</div>
|
||||
</div>
|
||||
);
|
31
components/post-overview/elements/Share.tsx
Normal file
31
components/post-overview/elements/Share.tsx
Normal file
@ -0,0 +1,31 @@
|
||||
import { DateTime } from 'luxon';
|
||||
import * as React from 'react';
|
||||
|
||||
import { faTwitter } from '@fortawesome/free-brands-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
|
||||
const twitterShareLink = 'https://twitter.com/intent/tweet';
|
||||
|
||||
type Props = {
|
||||
link: string;
|
||||
text: string;
|
||||
};
|
||||
|
||||
function buildTwitterShareLink(link: string, text: string): string {
|
||||
return `${twitterShareLink}?text=${escape(text.slice(0, 279))}&url=${encodeURI(link)}`;
|
||||
}
|
||||
|
||||
export default ({ link, text }: Props) => {
|
||||
const twitterUrl = buildTwitterShareLink(link, text);
|
||||
return (
|
||||
<div className={'text-gray-500 flex flex-row space-x-3 items-center'}>
|
||||
<div>Share:</div>
|
||||
<a
|
||||
className={'text-blue-500 hover:text-blue-700 text-3xl '}
|
||||
href={twitterUrl}
|
||||
target="_blank">
|
||||
<FontAwesomeIcon icon={faTwitter} />
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
};
|
16
components/post-overview/elements/Tags.tsx
Normal file
16
components/post-overview/elements/Tags.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
import * as React from 'react';
|
||||
import { Tag } from '../../../src/posts/post';
|
||||
|
||||
type Props = {
|
||||
tags: Tag[];
|
||||
};
|
||||
|
||||
export default ({ tags }: Props) => (
|
||||
<div className={'flex flex-row py-2 space-x-2'}>
|
||||
{tags.map((t) => (
|
||||
<div className={'flex rounded-md text-sm text-gray-500 hover:text-black underline'}>
|
||||
<a>{'#' + t.text}</a>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
@ -7,7 +7,7 @@
|
||||
"export": "next build && next export",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"storybook": "start-storybook -s ./public -p 6006 ",
|
||||
"storybook": "start-storybook -s ./story-static -p 6006 ",
|
||||
"build-storybook": "build-storybook"
|
||||
},
|
||||
"dependencies": {
|
||||
@ -15,6 +15,9 @@
|
||||
"@fortawesome/free-brands-svg-icons": "^5.15.3",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.15.3",
|
||||
"@fortawesome/react-fontawesome": "^0.1.14",
|
||||
"@types/luxon": "^1.26.5",
|
||||
"install": "^0.13.0",
|
||||
"luxon": "^1.26.0",
|
||||
"next": "^10.1.0",
|
||||
"react": "17.0.1",
|
||||
"react-dom": "17.0.1"
|
||||
|
4
src/posts/paging.ts
Normal file
4
src/posts/paging.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export type Paging = {
|
||||
totalPages: number;
|
||||
currentPage: number;
|
||||
};
|
39
src/posts/post.ts
Normal file
39
src/posts/post.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import { DateTime } from 'luxon';
|
||||
import { User } from '../user/user';
|
||||
|
||||
export type Post = {
|
||||
createdAt: DateTime;
|
||||
createdBy: User;
|
||||
title: string;
|
||||
preview: Preview;
|
||||
tags: Tag[];
|
||||
postBody: PostBody;
|
||||
};
|
||||
|
||||
export type Preview = {
|
||||
readTime?: number;
|
||||
headerMedia?: MediaElement;
|
||||
};
|
||||
|
||||
export type Tag = {
|
||||
text: string;
|
||||
};
|
||||
|
||||
export type PostBody = {
|
||||
elements: Element[];
|
||||
};
|
||||
|
||||
type Paragraph = {
|
||||
key: 'Paragraph';
|
||||
text: string;
|
||||
};
|
||||
|
||||
type Image = {
|
||||
key: 'Image';
|
||||
url: string;
|
||||
alt: string;
|
||||
};
|
||||
|
||||
type MediaElement = Image;
|
||||
|
||||
type Element = Paragraph | MediaElement;
|
4
src/user/user.ts
Normal file
4
src/user/user.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export type User = {
|
||||
name: string;
|
||||
avatar: string;
|
||||
};
|
3
src/util/types.ts
Normal file
3
src/util/types.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export function isDefined<T>(t: T | undefined | null): t is T {
|
||||
return t != null && typeof t !== undefined;
|
||||
}
|
BIN
story-static/avatar.jpg
Normal file
BIN
story-static/avatar.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 39 KiB |
BIN
story-static/favicon.ico
Normal file
BIN
story-static/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.4 KiB |
BIN
story-static/pattern.png
Normal file
BIN
story-static/pattern.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 69 KiB |
15
yarn.lock
15
yarn.lock
@ -2334,6 +2334,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad"
|
||||
integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==
|
||||
|
||||
"@types/luxon@^1.26.5":
|
||||
version "1.26.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-1.26.5.tgz#843fb705e16e4d2a90847a351b799ea9d879859e"
|
||||
integrity sha512-XeQxxRMyJi1znfzHw4CGDLyup/raj84SnjjkI2fDootZPGlB0yqtvlvEIAmzHDa5wiEI5JJevZOWxpcofsaV+A==
|
||||
|
||||
"@types/markdown-to-jsx@^6.11.3":
|
||||
version "6.11.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/markdown-to-jsx/-/markdown-to-jsx-6.11.3.tgz#cdd1619308fecbc8be7e6a26f3751260249b020e"
|
||||
@ -6237,6 +6242,11 @@ inline-style-parser@0.1.1:
|
||||
resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.1.1.tgz#ec8a3b429274e9c0a1f1c4ffa9453a7fef72cea1"
|
||||
integrity sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==
|
||||
|
||||
install@^0.13.0:
|
||||
version "0.13.0"
|
||||
resolved "https://registry.yarnpkg.com/install/-/install-0.13.0.tgz#6af6e9da9dd0987de2ab420f78e60d9c17260776"
|
||||
integrity sha512-zDml/jzr2PKU9I8J/xyZBQn8rPCAY//UOYNmR01XwNwyfhEWObo2SWfSl1+0tm1u6PhxLwDnfsT/6jB7OUxqFA==
|
||||
|
||||
internal-slot@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c"
|
||||
@ -7174,6 +7184,11 @@ lru-cache@^6.0.0:
|
||||
dependencies:
|
||||
yallist "^4.0.0"
|
||||
|
||||
luxon@^1.26.0:
|
||||
version "1.26.0"
|
||||
resolved "https://registry.yarnpkg.com/luxon/-/luxon-1.26.0.tgz#d3692361fda51473948252061d0f8561df02b578"
|
||||
integrity sha512-+V5QIQ5f6CDXQpWNICELwjwuHdqeJM1UenlZWx5ujcRMc9venvluCjFb4t5NYLhb6IhkbMVOxzVuOqkgMxee2A==
|
||||
|
||||
make-dir@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c"
|
||||
|
Loading…
Reference in New Issue
Block a user