Merge pull request #20 from Discours/header-module-css

Header module css
This commit is contained in:
Igor Lobanov 2022-10-04 12:50:36 +02:00 committed by GitHub
commit ba8b3a9938
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 307 additions and 165 deletions

View File

@ -16,7 +16,7 @@ const getDevCssClassPrefix = (filename: string): string => {
return filename
.slice(filename.indexOf(PATH_PREFIX) + PATH_PREFIX.length)
.replace('.module.scss', '')
.replace(/[/?\\]/, '-')
.replace(/[/?\\]/g, '-')
.replace('?', '-')
}

View File

@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.0287 1.11303e-08H11.4833C10.2582 -7.574e-05 9.06438 0.386513 8.07182 1.10475C7.07926 1.82299 6.33897 2.83615 5.95618 4.00001H5.818C4.27493 4.00001 2.79513 4.61294 1.70405 5.70406C0.612932 6.79518 0 8.27506 0 9.81801V10.9089C0.000530297 11.7805 0.196883 12.6408 0.574533 13.4262C0.952183 14.2117 1.50141 14.9023 2.18177 15.447V19.2724C2.1817 19.4752 2.26624 19.6688 2.4151 19.8065C2.56396 19.9442 2.76358 20.0136 2.9657 19.9978C3.16789 19.982 3.35432 19.8824 3.47994 19.7233L5.81802 16.727H8.36342C9.58787 16.7263 10.781 16.3393 11.7726 15.6211C12.7642 14.903 13.5042 13.8902 13.8869 12.727H14.0142L16.3631 15.7233C16.4882 15.8846 16.6752 15.9858 16.8785 16.0024C17.0819 16.019 17.2829 15.9493 17.4324 15.8105C17.5819 15.6717 17.6663 15.4765 17.6649 15.2725V11.4471C18.3453 10.9024 18.8945 10.2118 19.2721 9.42628C19.6498 8.64084 19.8461 7.78056 19.8467 6.909V5.81811C19.8467 4.79685 19.5778 3.79362 19.0672 2.90908C18.5565 2.02464 17.8221 1.29017 16.9376 0.77948C16.0532 0.268887 15.0499 3.49299e-05 14.0286 3.49299e-05L14.0287 1.11303e-08ZM8.3635 15.2724H5.54174C5.30273 15.2571 5.07152 15.3605 4.92357 15.5488L3.63632 17.1705V15.0834C3.63595 14.8455 3.51913 14.6227 3.3236 14.4871C2.74696 14.0852 2.27583 13.5501 1.95045 12.9272C1.625 12.3041 1.45492 11.6118 1.45455 10.9089V9.81799C1.45455 8.66074 1.91432 7.55089 2.73263 6.73248C3.55094 5.91417 4.66085 5.45441 5.81814 5.45441H8.36354C9.52079 5.45441 10.6306 5.91417 11.449 6.73248C12.2674 7.55079 12.7271 8.6607 12.7271 9.81799V10.9089C12.7271 12.0661 12.2674 13.176 11.449 13.9944C10.6307 14.8127 9.52083 15.2725 8.36354 15.2725L8.3635 15.2724ZM18.3923 6.90894C18.3915 7.61211 18.2208 8.30468 17.8948 8.92762C17.5687 9.55056 17.0969 10.0856 16.5196 10.487C16.3254 10.6234 16.2101 10.846 16.2105 11.0833V13.1705L14.9087 11.5341C14.7608 11.3459 14.5296 11.2425 14.2905 11.2578H14.1815V10.8941V9.8178C14.1815 8.79654 13.9126 7.79331 13.402 6.90877C12.8913 6.02433 12.1569 5.28986 11.2724 4.77917C10.388 4.26857 9.38464 3.99972 8.36338 3.99972H7.51612C7.86429 3.24011 8.42321 2.5965 9.12646 2.14529C9.82971 1.69401 10.6477 1.45424 11.4834 1.45432H14.0288C15.186 1.45432 16.2959 1.91408 17.1143 2.73239C17.9326 3.5507 18.3923 4.66061 18.3923 5.8179L18.3923 6.90894Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M17.9975 4.89363L13.1038 0L0 13.1064V18H4.89363L17.9975 4.89363ZM13.105 2.55674L15.4421 4.89383L14.4612 5.87591L12.1241 3.53882L13.105 2.55674ZM12.1241 3.53882L14.4612 5.87591L4.14434 16.1915L1.80725 13.8544L12.1241 3.53882ZM1.80725 16.1915V13.8544L4.14434 16.1915H1.80725Z" fill="#141414"/>
</svg>

After

Width:  |  Height:  |  Size: 404 B

View File

@ -0,0 +1,3 @@
<svg width="20" height="18" viewBox="0 0 20 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M18.9051 5.84476L13.2802 0.219864C13.0659 0.00516444 12.7433 -0.0590774 12.4631 0.057267C12.1827 0.173408 12 0.446907 12 0.750148V2.98109C10.4431 3.00453 6.93799 3.36927 4.10315 5.93941C1.49973 8.29983 0.122008 11.8884 0.00774261 16.6101C0.00376666 16.8204 0 17.0314 0 17.25C0 17.6198 0.269523 17.9345 0.635078 17.9912C0.673581 17.9971 0.712294 18 0.750586 18C1.07243 18 1.36456 17.7924 1.46479 17.4775C1.48885 17.4024 3.87798 10.1908 12 9.76976L11.9997 12.0003C11.9997 12.3035 12.1824 12.577 12.4628 12.6932C12.742 12.8093 13.0655 12.7453 13.28 12.5306L18.9049 6.90566C19.1979 6.61269 19.1979 6.1381 18.9049 5.84512L18.9051 5.84476ZM13.4999 10.1894V9.00001C13.4999 8.58588 13.164 8.25002 12.7499 8.25002C6.93427 8.25002 3.55504 11.3027 1.76204 13.8064C2.23748 10.9045 3.35892 8.6391 5.11105 7.05058C7.66947 4.73114 10.8634 4.47797 12.1347 4.47797C12.3772 4.47797 12.5501 4.48717 12.635 4.49303C12.7083 4.5014 12.7497 4.49994 12.7499 4.49994C13.1529 4.48487 13.4999 4.15319 13.4999 3.74995V2.56053L17.314 6.3749L13.4999 10.1894Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -13,10 +13,14 @@ type MainLayoutProps = {
}
export const MainLayout = (props: MainLayoutProps) => {
const isHeaderFixed = props.isHeaderFixed !== undefined ? props.isHeaderFixed : true
return (
<>
<Header title={props.headerTitle} isHeaderFixed={props.isHeaderFixed === true} />
<main class="main-content">{props.children}</main>
<Header title={props.headerTitle} isHeaderFixed={isHeaderFixed} />
<main class="main-content" classList={{ 'main-content--no-padding': !isHeaderFixed }}>
{props.children}
</main>
<Show when={props.hideFooter !== true}>
<Footer />
</Show>

View File

@ -1,6 +1,7 @@
header {
.mainHeader {
background: #fff;
margin-bottom: 2.2rem;
position: absolute;
width: 100%;
z-index: 10;
@ -11,13 +12,17 @@ header {
padding: 0 divide($container-padding-x, 2);
}
}
a:hover {
.icon {
filter: invert(1);
}
}
}
.header--fixed {
position: fixed;
top: 0;
.main-logo {
.headerFixed.headerScrolledBottom,
.headerFixed.headerScrolledTop {
.mainLogo {
height: 56px;
img {
@ -26,7 +31,13 @@ header {
}
}
.header__inner {
.headerFixed {
position: fixed;
top: 0;
}
.headerInner {
align-items: center;
background: #fff;
border-bottom: 4px solid #000;
flex-wrap: nowrap;
@ -48,7 +59,7 @@ header {
}
}
.main-logo {
.mainLogo {
align-items: center;
display: inline-flex;
height: 70px;
@ -85,13 +96,10 @@ header {
}
}
nav {
align-items: center;
}
.usernav {
display: inline-flex;
padding-right: 0;
position: relative;
width: auto;
@include media-breakpoint-down(md) {
@ -101,52 +109,47 @@ nav {
}
}
.main-navigation {
.mainNavigationWrapper {
padding-left: 0;
position: relative;
@include font-size(1.7rem);
}
ul {
display: inline-flex;
list-style: none;
margin: 0;
opacity: 1;
padding: 0;
transition: opacity 0.3s;
.mainNavigation {
display: inline-flex;
list-style: none;
margin: 0;
opacity: 1;
padding: 0;
transition: opacity 0.3s;
@include media-breakpoint-down(md) {
background: #fff;
bottom: 0;
display: none;
font-size: 2.6rem;
font-weight: bold;
left: 0;
padding: $container-padding-x;
position: fixed;
top: 74px;
width: 100%;
z-index: 1;
@include media-breakpoint-down(md) {
background: #fff;
bottom: 0;
display: none;
font-size: 2.6rem;
font-weight: bold;
left: 0;
padding: $container-padding-x;
position: fixed;
top: 74px;
width: 100%;
z-index: 1;
li {
margin-bottom: 2.4rem;
}
}
@include media-breakpoint-down(sm) {
padding: divide($container-padding-x, 2);
}
.header--scrolled-bottom & {
opacity: 0;
li {
margin-bottom: 2.4rem;
}
}
&.fixed {
ul {
display: inline-flex;
@include media-breakpoint-down(sm) {
padding: divide($container-padding-x, 2);
}
@include media-breakpoint-down(lg) {
display: block !important;
}
&.fixed {
display: inline-flex;
@include media-breakpoint-down(lg) {
display: block !important;
}
}
@ -166,7 +169,14 @@ nav {
}
}
.burger-container {
.headerWithTitle.headerScrolledBottom {
.mainNavigation,
.userControl {
opacity: 0;
}
}
.burgerContainer {
box-sizing: content-box;
display: inline-flex;
float: right;
@ -243,35 +253,84 @@ nav {
}
}
.article-header {
@include font-size(1.4rem);
left: 0;
margin: 0.2em;
.articleHeader,
.articleControls {
opacity: 0;
overflow: hidden;
position: absolute;
text-overflow: ellipsis;
transition: opacity 0.3s, z-index 0s 0.3s;
white-space: nowrap;
width: 100%;
z-index: -1;
.header--scrolled-bottom & {
.headerScrolledBottom & {
transition: opacity 0.3s;
opacity: 1;
z-index: 1;
}
}
.header__search {
.articleHeader {
@include font-size(1.4rem);
left: 0;
margin: 0.2em;
overflow: hidden;
position: absolute;
text-overflow: ellipsis;
white-space: nowrap;
width: 100%;
}
.headerSearch {
text-transform: lowercase;
.icon {
display: inline-block;
height: 1em;
margin-right: 0.3em;
transition: filter 0.3s;
vertical-align: middle;
width: 1em;
}
}
.userControl {
opacity: 1;
transition: opacity 0.3s;
z-index: 1;
.headerWithTitle.headerScrolledBottom & {
transition: opacity 0.3s, z-index 0s 0.3s;
opacity: 0;
z-index: -1;
}
}
.articleControls {
display: flex;
left: 0;
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 100%;
.icon {
margin-left: 1.6rem;
opacity: 0.6;
transition: opacity 0.3s;
}
img {
vertical-align: middle;
}
a {
border: none;
&:hover {
background: none;
.icon {
filter: none;
opacity: 1;
}
}
}
}

View File

@ -8,9 +8,11 @@ import { t } from '../../utils/intl'
import { useModalStore, showModal, useWarningsStore } from '../../stores/ui'
import { useAuthStore } from '../../stores/auth'
import { handleClientRouteLinkClick, router, Routes, useRouter } from '../../stores/router'
import './Header.scss'
import styles from './Header.module.scss'
import privateStyles from './Private.module.scss'
import { getPagePath } from '@nanostores/router'
import { getLogger } from '../../utils/logger'
import { clsx } from 'clsx'
const log = getLogger('header')
@ -69,6 +71,8 @@ export const Header = (props: Props) => {
onMount(() => {
let scrollTop = window.scrollY
window.console.log(props.title)
const handleScroll = () => {
setIsScrollingBottom(window.scrollY > scrollTop)
setIsScrolled(window.scrollY > 0)
@ -83,27 +87,33 @@ export const Header = (props: Props) => {
return (
<header
class="main-header"
class={styles.mainHeader}
classList={{
['header--fixed']: props.isHeaderFixed,
['header--scrolled-top']: !getIsScrollingBottom() && getIsScrolled(),
['header--scrolled-bottom']: getIsScrollingBottom() && getIsScrolled()
[styles.headerFixed]: props.isHeaderFixed,
[styles.headerScrolledTop]: !getIsScrollingBottom() && getIsScrolled(),
[styles.headerScrolledBottom]: getIsScrollingBottom() && getIsScrolled(),
[styles.headerWithTitle]: Boolean(props.title)
}}
>
<Modal name="auth">
<AuthModal />
</Modal>
<div class="wide-container">
<nav class="row header__inner" classList={{ fixed: fixed() }}>
<div class="main-logo col-auto">
<nav class={clsx(styles.headerInner, 'row')} classList={{ fixed: fixed() }}>
<div class={clsx(styles.mainLogo, 'col-auto')}>
<a href={getPagePath(router, 'home')} onClick={handleClientRouteLinkClick}>
<img src="/logo.svg" alt={t('Discours')} />
</a>
</div>
<div class="col main-navigation">
<div class="article-header">{props.title}</div>
<div class={clsx(styles.mainNavigationWrapper, 'col')}>
<Show when={props.title}>
<div class={styles.articleHeader}>{props.title}</div>
</Show>
<ul class="col main-navigation text-xl inline-flex" classList={{ fixed: fixed() }}>
<ul
class={clsx(styles.mainNavigation, 'col text-xl inline-flex')}
classList={{ fixed: fixed() }}
>
<For each={resources}>
{(r) => (
<li classList={{ selected: r.route === getPage().route }}>
@ -113,17 +123,17 @@ export const Header = (props: Props) => {
</li>
)}
</For>
<li class="header__search">
<li class={styles.headerSearch}>
<a href="#">
<Icon name="search" />
<Icon name="search" class={styles.icon} iconClassName={styles.searchIcon} />
{t('Search')}
</a>
</li>
</ul>
</div>
<div class="usernav">
<div class="usercontrol col">
<div class="usercontrol__item">
<div class={styles.usernav}>
<div class={clsx(privateStyles.userControl, styles.userControl, 'col')}>
<div class={privateStyles.userControlItem}>
<a href="#auth" onClick={handleBellIconClick}>
<div>
<Icon name="bell-white" counter={authorized() ? getWarnings().length : 1} />
@ -132,7 +142,7 @@ export const Header = (props: Props) => {
</div>
<Show when={visibleWarnings()}>
<div class="usercontrol__item notifications">
<div class={clsx(privateStyles.userControlItem, 'notifications')}>
<Notifications />
</div>
</Show>
@ -140,9 +150,9 @@ export const Header = (props: Props) => {
<Show
when={authorized()}
fallback={
<div class="usercontrol__item loginbtn">
<div class={clsx(privateStyles.userControlItem, 'loginbtn')}>
<a href="#auth" onClick={handleEnterClick}>
{t('enter')}
<Icon name="user-anonymous" />
</a>
</div>
}
@ -150,9 +160,19 @@ export const Header = (props: Props) => {
<Private />
</Show>
</div>
<Show when={props.title}>
<div class={styles.articleControls}>
<Icon name="share-outline" class={styles.icon} />
<a href="#comments">
<Icon name="comments-outline" class={styles.icon} />
</a>
<Icon name="pencil-outline" class={styles.icon} />
<Icon name="bookmark" class={styles.icon} />
</div>
</Show>
</div>
<div class="burger-container">
<div class="burger" classList={{ fixed: fixed() }} onClick={toggleFixed}>
<div class={styles.burgerContainer}>
<div class={styles.burger} classList={{ fixed: fixed() }} onClick={toggleFixed}>
<div />
</div>
</div>

View File

@ -1,14 +1,25 @@
import { mergeProps, Show } from 'solid-js'
import type { JSX } from 'solid-js'
import { clsx } from 'clsx'
import './Icon.css'
export const Icon = (_props: any) => {
const props = mergeProps({ title: '', counter: 0 }, _props)
type IconProps = {
class?: string
iconClassName?: string
style?: string | JSX.CSSProperties
title?: string
name?: string
counter?: number
}
export const Icon = (passedProps: IconProps) => {
const props = mergeProps({ title: '', counter: 0 }, passedProps)
return (
<div class="icon" style={props.style}>
<img src={`/icons/${props.name}.svg`} alt={props.title ?? props.name} />
<div class={clsx('icon', props.class)} style={props.style}>
<img src={`/icons/${props.name}.svg`} alt={props.title ?? props.name} class={props.iconClassName} />
<Show when={props.counter}>
<div class="notifications-counter">{props.counter.toString()}</div>
<div class="notifications-counter">{props.counter}</div>
</Show>
</div>
)

View File

@ -0,0 +1,100 @@
.userControl {
align-items: baseline;
display: flex;
@include font-size(1.7rem);
justify-content: flex-end;
@include media-breakpoint-down(md) {
padding: divide($container-padding-x, 2);
}
.circlewrap {
margin-right: 0;
}
}
.userControlItem {
align-items: center;
border: 2px solid #f6f6f6;
border-radius: 100%;
display: flex;
height: 2.4em;
justify-content: center;
margin-left: divide($container-padding-x, 2);
position: relative;
width: 2.4em;
@include media-breakpoint-up(sm) {
margin-left: 1.2rem;
}
.circlewrap {
height: 23px;
min-width: 23px;
width: 23px;
}
a {
border: none;
&:hover {
background: none;
&::before {
background-color: #000;
}
img {
filter: invert(1);
}
}
img {
filter: invert(0);
transition: filter 0.3s;
}
&::before {
background-color: #fff;
border-radius: 100%;
content: '';
height: 100%;
left: 0;
position: absolute;
top: 0;
transition: background-color 0.3s;
width: 100%;
}
}
img {
height: 20px;
vertical-align: middle;
width: auto;
}
.textLabel {
display: none;
}
}
.userControlItemWritePost {
@include media-breakpoint-up(lg) {
.icon {
display: none;
}
.textLabel {
display: inline;
}
}
}
.userControlItemInbox,
.userControlItemSearch {
@include media-breakpoint-down(sm) {
display: none;
}
}

View File

@ -1,58 +0,0 @@
.usercontrol {
align-items: baseline;
display: flex;
@include font-size(1.7rem);
justify-content: flex-end;
@include media-breakpoint-down(md) {
padding: divide($container-padding-x, 2);
}
.circlewrap {
margin-right: 0;
}
}
.usercontrol__item {
margin-left: divide($container-padding-x, 2);
@include media-breakpoint-up(sm) {
margin-left: 3.2rem;
}
.circlewrap {
height: 23px;
min-width: 23px;
width: 23px;
}
img {
height: 20px;
vertical-align: middle;
width: auto;
}
.text-label {
display: none;
}
}
.usercontrol__item--write-post {
@include media-breakpoint-up(lg) {
.icon {
display: none;
}
.text-label {
display: inline;
}
}
}
.usercontrol__item--inbox,
.usercontrol__item--search {
@include media-breakpoint-down(sm) {
display: none;
}
}

View File

@ -1,28 +1,29 @@
import type { Author } from '../../graphql/types.gen'
import Userpic from '../Author/Userpic'
import { Icon } from './Icon'
import './Private.scss'
import styles from './Private.module.scss'
import { useAuthStore } from '../../stores/auth'
import { useRouter } from '../../stores/router'
import clsx from 'clsx'
export default () => {
const { session } = useAuthStore()
const { getPage } = useRouter()
return (
<div class="usercontrol col">
<div class="usercontrol__item usercontrol__item--write-post">
<div class={clsx(styles.userControl, 'col')}>
<div class={clsx(styles.userControlItem, styles.userControlItemWritePost)}>
<a href="/create">
<span class="text-label">опубликовать материал</span>
<span class={styles.textLabel}>опубликовать материал</span>
<Icon name="pencil" />
</a>
</div>
<div class="usercontrol__item usercontrol__item--search">
<div class={clsx(styles.userControlItem, styles.userControlItemSearch)}>
<a href="/search">
<Icon name="search" />
</a>
</div>
<div class="usercontrol__item usercontrol__item--inbox">
<div class={clsx(styles.userControlItem, styles.userControlItemInbox)}>
<a href="/inbox">
{/*FIXME: replace with route*/}
<div classList={{ entered: getPage().path === '/inbox' }}>
@ -30,7 +31,7 @@ export default () => {
</div>
</a>
</div>
<div class="usercontrol__item">
<div class={styles.userControlItem}>
<a href={`/${session().user?.slug}`}>
{/*FIXME: replace with route*/}
<div classList={{ entered: getPage().path === `/${session().user?.slug}` }}>

View File

@ -1,12 +1,3 @@
.main-logo {
height: 80px !important;
}
main {
padding-bottom: 0;
padding-top: 0 !important;
}
.errorPageWrapper {
height: 100vh;
padding-top: 100px;
@ -15,7 +6,7 @@ main {
.errorPage {
position: relative;
top: 35%;
transform: translateY(-43%);
transform: translateY(-45%);
.image-link:hover {
background: none;

View File

@ -536,6 +536,11 @@ figcaption {
transition: all 1s ease;
}
.main-content--no-padding {
padding-bottom: 0;
padding-top: 0;
}
.container {
max-width: 1400px;