10 changed files with 197 additions and 167 deletions
@ -0,0 +1,57 @@
@@ -0,0 +1,57 @@
|
||||
.root { |
||||
position: relative; |
||||
display: grid; |
||||
} |
||||
|
||||
.buttonsLogoTitleSection { |
||||
// margin-left: 1.5vw; |
||||
// margin-right: 1.5vw; |
||||
} |
||||
|
||||
.row { |
||||
margin-bottom: 7px; |
||||
} |
||||
|
||||
.logoTitleSection { |
||||
display: flex; |
||||
@media (max-width: 768px) { |
||||
flex-direction: column; |
||||
.logo { |
||||
margin-left: auto; |
||||
margin-right: auto; |
||||
margin-bottom: 10px; |
||||
} |
||||
} |
||||
} |
||||
|
||||
.titleSection { |
||||
display: flex; |
||||
flex-direction: column; |
||||
|
||||
.title { |
||||
font-size: 32px; |
||||
font-weight: bold; |
||||
color: black; |
||||
text-transform: uppercase; |
||||
line-height: 30px; |
||||
} |
||||
|
||||
.subtitle { |
||||
font-size: 24px; |
||||
font-weight: 400; |
||||
line-height: 22px; |
||||
color: var(--theme-text-secondary); |
||||
} |
||||
} |
||||
|
||||
.tagList { |
||||
font-family: var(--theme-text-display-font-family); |
||||
color: var(--theme-text-primary); |
||||
|
||||
span { |
||||
display: inline-block; |
||||
margin-right: 8px; |
||||
font-size: 14px; |
||||
font-weight: 300; |
||||
} |
||||
} |
@ -0,0 +1,36 @@
@@ -0,0 +1,36 @@
|
||||
import cn from 'classnames'; |
||||
|
||||
import { ServerLogo } from '../../ui'; |
||||
import SocialLinks from '../../ui/SocialLinks/SocialLinks'; |
||||
import { SocialLink } from '../../../interfaces/social-link.model'; |
||||
import s from './ContentHeader.module.scss'; |
||||
|
||||
interface Props { |
||||
name: string; |
||||
title: string; |
||||
summary: string; |
||||
tags: string[]; |
||||
links: SocialLink[]; |
||||
logo: string; |
||||
} |
||||
export default function ContentHeader({ name, title, summary, logo, tags, links }: Props) { |
||||
return ( |
||||
<div className={s.root}> |
||||
<div className={s.logoTitleSection}> |
||||
<div className={s.logo}> |
||||
<ServerLogo src={logo} /> |
||||
</div> |
||||
<div className={s.titleSection}> |
||||
<div className={cn(s.title, s.row)}>{name}</div> |
||||
<div className={cn(s.subtitle, s.row)}>{title || summary}</div> |
||||
<div className={cn(s.tagList, s.row)}> |
||||
{tags.length > 0 && tags.map(tag => <span key={tag}>#{tag} </span>)} |
||||
</div> |
||||
<div className={cn(s.socialLinks, s.row)}> |
||||
<SocialLinks links={links} /> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
); |
||||
} |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
export { default } from './ContentHeader'; |
@ -1,89 +0,0 @@
@@ -1,89 +0,0 @@
|
||||
.root { |
||||
position: relative; |
||||
display: grid; |
||||
} |
||||
|
||||
.buttonsLogoTitleSection { |
||||
margin-left: 1.5vw; |
||||
margin-right: 1.5vw; |
||||
} |
||||
|
||||
.logoTitleSection { |
||||
display: flex; |
||||
align-items: center; |
||||
} |
||||
|
||||
.titleSection { |
||||
display: flex; |
||||
flex-direction: column; |
||||
|
||||
.title { |
||||
font-size: 1.5rem; |
||||
font-weight: bold; |
||||
color: var(--theme-text-primary); |
||||
} |
||||
|
||||
.subtitle { |
||||
margin-top: 0.35em; |
||||
font-size: 1.5em; |
||||
font-weight: 300; |
||||
color: var(--theme-text-secondary); |
||||
text-transform: uppercase; |
||||
} |
||||
} |
||||
|
||||
.mobile { |
||||
&.root { |
||||
position: relative; |
||||
display: flex; |
||||
padding: 0 0.3rem; |
||||
align-items: center; |
||||
justify-content: space-between; |
||||
.mobileInfo { |
||||
display: flex; |
||||
align-items: center; |
||||
.title { |
||||
font-size: 1.2rem; |
||||
font-weight: 600; |
||||
} |
||||
} |
||||
.mobileStatus { |
||||
display: flex; |
||||
font-weight: 600; |
||||
.viewerCount { |
||||
display: flex; |
||||
align-items: center; |
||||
gap: 4px; |
||||
} |
||||
.liveStatus { |
||||
display: flex; |
||||
align-items: center; |
||||
margin-left: 0.5rem; |
||||
font-size: 0.8rem; |
||||
gap: 4px; |
||||
.liveCircle { |
||||
border-radius: 50%; |
||||
background-color: red; |
||||
width: 0.5rem; |
||||
height: 0.5rem; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
.tagList { |
||||
font-family: var(--theme-text-display-font-family); |
||||
color: var(--theme-text-secondary); |
||||
|
||||
ul { |
||||
margin: 0; |
||||
padding: 0; |
||||
} |
||||
|
||||
li { |
||||
display: inline-block; |
||||
margin: 0 0.7em 0 0; |
||||
font-weight: 600; |
||||
} |
||||
} |
@ -1,65 +0,0 @@
@@ -1,65 +0,0 @@
|
||||
import { useRecoilValue } from 'recoil'; |
||||
import cn from 'classnames'; |
||||
import { EyeFilled } from '@ant-design/icons'; |
||||
import { useEffect } from 'react'; |
||||
import { ClientConfig } from '../../../interfaces/client-config.model'; |
||||
import { |
||||
clientConfigStateAtom, |
||||
isOnlineSelector, |
||||
serverStatusState, |
||||
} from '../../stores/ClientConfigStore'; |
||||
import { ServerLogo } from '../../ui'; |
||||
import SocialLinks from '../../ui/SocialLinks/SocialLinks'; |
||||
import s from './StreamInfo.module.scss'; |
||||
import { ServerStatus } from '../../../interfaces/server-status.model'; |
||||
|
||||
interface Props { |
||||
isMobile: boolean; |
||||
} |
||||
export default function StreamInfo({ isMobile }: Props) { |
||||
const { socialHandles, name, title, tags, summary } = |
||||
useRecoilValue<ClientConfig>(clientConfigStateAtom); |
||||
const { viewerCount } = useRecoilValue<ServerStatus>(serverStatusState); |
||||
const online = useRecoilValue<boolean>(isOnlineSelector); |
||||
|
||||
useEffect(() => { |
||||
console.log({ online }); |
||||
}, [online]); |
||||
|
||||
return isMobile ? ( |
||||
<div className={cn(s.root, s.mobile)}> |
||||
<div className={s.mobileInfo}> |
||||
<ServerLogo src="/logo" /> |
||||
<div className={s.title}>{name}</div> |
||||
</div> |
||||
<div className={s.mobileStatus}> |
||||
<div className={s.viewerCount}> |
||||
{online && ( |
||||
<> |
||||
<span>{viewerCount}</span> |
||||
<EyeFilled /> |
||||
</> |
||||
)} |
||||
</div> |
||||
<div className={s.liveStatus}> |
||||
{online && <div className={s.liveCircle} />} |
||||
<span>{online ? 'LIVE' : 'OFFLINE'}</span> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
) : ( |
||||
<div className={s.root}> |
||||
<div className={s.logoTitleSection}> |
||||
<ServerLogo src="/logo" /> |
||||
<div className={s.titleSection}> |
||||
<div className={s.title}>{name}</div> |
||||
<div className={s.subtitle}>{title || summary}</div> |
||||
<div className={s.tagList}> |
||||
{tags.length > 0 && tags.map(tag => <span key={tag}>#{tag} </span>)} |
||||
</div> |
||||
<SocialLinks links={socialHandles} /> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
); |
||||
} |
@ -1 +0,0 @@
@@ -1 +0,0 @@
|
||||
export { default } from './StreamInfo'; |
@ -0,0 +1,76 @@
@@ -0,0 +1,76 @@
|
||||
import React from 'react'; |
||||
import { ComponentStory, ComponentMeta } from '@storybook/react'; |
||||
import ContentHeader from '../components/common/ContentHeader/ContentHeader'; |
||||
|
||||
export default { |
||||
title: 'owncast/Components/Content Header', |
||||
component: ContentHeader, |
||||
parameters: {}, |
||||
} as ComponentMeta<typeof ContentHeader>; |
||||
|
||||
const Template: ComponentStory<typeof ContentHeader> = args => <ContentHeader {...args} />; |
||||
|
||||
export const Example = Template.bind({}); |
||||
Example.args = { |
||||
name: 'My Awesome Owncast Stream', |
||||
summary: 'A calvacade of glorious sights and sounds', |
||||
tags: ['word', 'tag with spaces', 'music'], |
||||
logo: 'https://watch.owncast.online/logo', |
||||
links: [ |
||||
{ |
||||
platform: 'github', |
||||
url: 'https://github.com/owncast/owncast', |
||||
icon: 'https://watch.owncast.online/img/platformlogos/github.svg', |
||||
}, |
||||
{ |
||||
platform: 'Documentation', |
||||
url: 'https://owncast.online', |
||||
icon: 'https://watch.owncast.online/img/platformlogos/link.svg', |
||||
}, |
||||
{ |
||||
platform: 'mastodon', |
||||
url: 'https://fosstodon.org/users/owncast', |
||||
icon: 'https://watch.owncast.online/img/platformlogos/mastodon.svg', |
||||
}, |
||||
], |
||||
}; |
||||
|
||||
export const LongContent = Template.bind({}); |
||||
LongContent.args = { |
||||
name: 'My Awesome Owncast Stream, streaming the best of streams and some lorem ipsum too', |
||||
summary: |
||||
'A calvacade of glorious sights and sounds. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', |
||||
tags: [ |
||||
'word', |
||||
'tag with spaces', |
||||
'music', |
||||
'more tags', |
||||
'a bunch', |
||||
'keep going', |
||||
'and more', |
||||
'just a few more', |
||||
'video games', |
||||
'things', |
||||
'stuff', |
||||
'ok some more', |
||||
'this should do it', |
||||
], |
||||
logo: 'https://watch.owncast.online/logo', |
||||
links: [ |
||||
{ |
||||
platform: 'github', |
||||
url: 'https://github.com/owncast/owncast', |
||||
icon: 'https://watch.owncast.online/img/platformlogos/github.svg', |
||||
}, |
||||
{ |
||||
platform: 'Documentation', |
||||
url: 'https://owncast.online', |
||||
icon: 'https://watch.owncast.online/img/platformlogos/link.svg', |
||||
}, |
||||
{ |
||||
platform: 'mastodon', |
||||
url: 'https://fosstodon.org/users/owncast', |
||||
icon: 'https://watch.owncast.online/img/platformlogos/mastodon.svg', |
||||
}, |
||||
], |
||||
}; |
Loading…
Reference in new issue