refactoring, horizonalAnchor prop added to Popup

This commit is contained in:
Igor Lobanov 2022-10-28 11:10:14 +02:00
parent 0eda49ed5d
commit 54a74fa2ee
8 changed files with 197 additions and 183 deletions

View File

@ -338,7 +338,6 @@
.control {
cursor: pointer;
margin-left: 1.6rem;
border: 0;
.icon {
@ -356,7 +355,128 @@
}
}
.control + .control {
margin-left: 1.6rem;
}
img {
vertical-align: middle;
}
}
.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);
}
.userpic {
margin-right: 0;
img {
height: 100%;
width: 100%;
}
}
}
.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;
}
.button,
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;
}
}
.userControlItemInbox,
.userControlItemSearch {
@include media-breakpoint-down(sm) {
display: none;
}
}
.userControlItemWritePost {
width: auto;
@include media-breakpoint-up(lg) {
.icon {
display: none;
}
.textLabel {
display: inline;
padding: 0 1.2rem;
position: relative;
z-index: 1;
}
}
&,
a::before {
border-radius: 1.2em;
}
}

View File

@ -1,5 +1,4 @@
import { For, Show, createSignal, createMemo, createEffect, onMount, onCleanup } from 'solid-js'
import Private from './Private'
import Notifications from './Notifications'
import { Icon } from './Icon'
import { Modal } from './Modal'
@ -9,11 +8,13 @@ import { useModalStore, showModal, useWarningsStore } from '../../stores/ui'
import { useAuthStore } from '../../stores/auth'
import { handleClientRouteLinkClick, router, Routes, useRouter } from '../../stores/router'
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'
import { SharePopup } from '../Article/SharePopup'
import { ProfilePopup } from './ProfilePopup'
import Userpic from '../Author/Userpic'
import type { Author } from '../../graphql/types.gen'
const log = getLogger('header')
@ -35,6 +36,8 @@ export const Header = (props: Props) => {
const [fixed, setFixed] = createSignal(false)
const [visibleWarnings, setVisibleWarnings] = createSignal(false)
const [isSharePopupVisible, setIsSharePopupVisible] = createSignal(false)
const [isProfilePopupVisible, setIsProfilePopupVisible] = createSignal(false)
// stores
const { warnings } = useWarningsStore()
const { session } = useAuthStore()
@ -86,7 +89,8 @@ export const Header = (props: Props) => {
classList={{
[styles.headerFixed]: props.isHeaderFixed,
[styles.headerScrolledTop]: !getIsScrollingBottom() && getIsScrolled(),
[styles.headerScrolledBottom]: (getIsScrollingBottom() && getIsScrolled()) || isSharePopupVisible(),
[styles.headerScrolledBottom]:
(getIsScrollingBottom() && getIsScrolled() && !isProfilePopupVisible()) || isSharePopupVisible(),
[styles.headerWithTitle]: Boolean(props.title)
}}
>
@ -128,8 +132,15 @@ export const Header = (props: Props) => {
</ul>
</div>
<div class={styles.usernav}>
<div class={clsx(privateStyles.userControl, styles.userControl, 'col')}>
<div class={privateStyles.userControlItem}>
<div class={clsx(styles.userControl, styles.userControl, 'col')}>
<div class={clsx(styles.userControlItem, styles.userControlItemWritePost)}>
<a href="/create">
<span class={styles.textLabel}>{t('Create post')}</span>
<Icon name="pencil" class={styles.icon} />
</a>
</div>
<div class={styles.userControlItem}>
<a href="#" onClick={handleBellIconClick}>
<div>
<Icon name="bell-white" counter={authorized() ? warnings().length : 1} />
@ -138,7 +149,7 @@ export const Header = (props: Props) => {
</div>
<Show when={visibleWarnings()}>
<div class={clsx(privateStyles.userControlItem, 'notifications')}>
<div class={clsx(styles.userControlItem, 'notifications')}>
<Notifications />
</div>
</Show>
@ -146,21 +157,42 @@ export const Header = (props: Props) => {
<Show
when={authorized()}
fallback={
<div class={clsx(privateStyles.userControlItem, 'loginbtn')}>
<div class={clsx(styles.userControlItem, 'loginbtn')}>
<a href="?modal=auth&mode=login" onClick={handleClientRouteLinkClick}>
<Icon name="user-anonymous" />
</a>
</div>
}
>
<Private />
<div class={clsx(styles.userControlItem, styles.userControlItemInbox)}>
<a href="/inbox">
{/*FIXME: replace with route*/}
<div classList={{ entered: page().path === '/inbox' }}>
<Icon name="inbox-white" counter={session()?.news?.unread || 0} />
</div>
</a>
</div>
<ProfilePopup
onVisibilityChange={(isVisible) => {
setIsProfilePopupVisible(isVisible)
}}
containerCssClass={styles.control}
trigger={
<div class={styles.userControlItem}>
<button class={styles.button}>
<div classList={{ entered: page().path === `/${session().user?.slug}` }}>
<Userpic user={session().user as Author} class={styles.userpic} />
</div>
</button>
</div>
}
/>
</Show>
</div>
<Show when={props.title}>
<div class={styles.articleControls}>
<SharePopup
onVisibilityChange={(isVisible) => {
console.log({ isVisible })
setIsSharePopupVisible(isVisible)
}}
containerCssClass={styles.control}

View File

@ -6,9 +6,17 @@
background: #fff;
border: 2px solid #000;
top: calc(100% + 8px);
transform: translateX(-50%);
opacity: 1;
&.horizontalAnchorCenter {
left: 50%;
transform: translateX(-50%);
}
&.horizontalAnchorRight {
right: 0;
}
@include font-size(1.6rem);
padding: 2.4rem;

View File

@ -2,15 +2,19 @@ import { createEffect, createSignal, JSX, onCleanup, onMount, Show } from 'solid
import styles from './Popup.module.scss'
import { clsx } from 'clsx'
type HorizontalAnchor = 'center' | 'right'
export type PopupProps = {
containerCssClass?: string
trigger: JSX.Element
children: JSX.Element
onVisibilityChange?: (isVisible) => void
horizontalAnchor?: HorizontalAnchor
}
export const Popup = (props: PopupProps) => {
const [isVisible, setIsVisible] = createSignal(false)
const horizontalAnchor: HorizontalAnchor = props.horizontalAnchor || 'center'
createEffect(() => {
if (props.onVisibilityChange) {
@ -38,12 +42,19 @@ export const Popup = (props: PopupProps) => {
})
const toggle = () => setIsVisible((oldVisible) => !oldVisible)
// class={clsx(styles.popupShare, stylesPopup.popupShare)}
return (
<span class={clsx(styles.container, props.containerCssClass)} ref={container}>
<span onClick={toggle}>{props.trigger}</span>
<Show when={isVisible()}>
<div class={clsx(styles.popup)}>{props.children}</div>
<div
class={clsx(styles.popup, {
[styles.horizontalAnchorCenter]: horizontalAnchor === 'center',
[styles.horizontalAnchorRight]: horizontalAnchor === 'right'
})}
>
{props.children}
</div>
</Show>
</span>
)

View File

@ -1,116 +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);
}
.userpic {
margin-right: 0;
img {
height: 100%;
width: 100%;
}
}
}
.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;
}
.button,
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 {
width: auto;
@include media-breakpoint-up(lg) {
.icon {
display: none;
}
.textLabel {
display: inline;
padding: 0 1.2rem;
position: relative;
z-index: 1;
}
}
&,
a::before {
border-radius: 1.2em;
}
}
.userControlItemInbox,
.userControlItemSearch {
@include media-breakpoint-down(sm) {
display: none;
}
}

View File

@ -1,49 +0,0 @@
import type { Author } from '../../graphql/types.gen'
import Userpic from '../Author/Userpic'
import { ProfilePopup } from './ProfilePopup'
import { Icon } from './Icon'
import styles from './Private.module.scss'
import { useAuthStore } from '../../stores/auth'
import { useRouter } from '../../stores/router'
import { clsx } from 'clsx'
import { createSignal } from 'solid-js'
export default () => {
const { session } = useAuthStore()
const { page } = useRouter()
const [isProfilePopupVisible, setIsProfilePopupVisible] = createSignal(false)
return (
<div class={clsx(styles.userControl, 'col')}>
<div class={clsx(styles.userControlItem, styles.userControlItemWritePost)}>
<a href="/create">
<span class={styles.textLabel}>опубликовать материал</span>
<Icon name="pencil" class={styles.icon} />
</a>
</div>
<div class={clsx(styles.userControlItem, styles.userControlItemInbox)}>
<a href="/inbox">
{/*FIXME: replace with route*/}
<div classList={{ entered: page().path === '/inbox' }}>
<Icon name="inbox-white" counter={session()?.news?.unread || 0} />
</div>
</a>
</div>
<ProfilePopup
onVisibilityChange={(isVisible) => {
setIsProfilePopupVisible(isVisible)
}}
containerCssClass={styles.control}
trigger={
<div class={styles.userControlItem}>
<button class={styles.button}>
<div classList={{ entered: page().path === `/${session().user?.slug}` }}>
<Userpic user={session().user as Author} class={styles.userpic} />
</div>
</button>
</div>
}
/>
</div>
)
}

View File

@ -1,6 +1,5 @@
import styles from './Popup.module.scss'
import { Popup, PopupProps } from './Popup'
import { useAuthStore } from '../../stores/auth'
import { signOut, useAuthStore } from '../../stores/auth'
type ProfilePopupProps = Omit<PopupProps, 'children'>
@ -8,7 +7,7 @@ export const ProfilePopup = (props: ProfilePopupProps) => {
const { session } = useAuthStore()
return (
<Popup {...props}>
<Popup {...props} horizontalAnchor="right">
<ul class="nodash">
<li>
<a href={`/${session().user?.slug}`}>Профиль</a>
@ -29,7 +28,15 @@ export const ProfilePopup = (props: ProfilePopupProps) => {
<a href="#">Настройки</a>
</li>
<li>
<a href="#">Выйти из&nbsp;аккаунта</a>
<a
href="#"
onClick={(event) => {
event.preventDefault()
signOut()
}}
>
Выйти из&nbsp;аккаунта
</a>
</li>
</ul>
</Popup>

View File

@ -164,5 +164,6 @@
"Almost done! Check your email.": "Почти готово! Осталось подтвердить вашу почту.",
"We've sent you a message with a link to enter our website.": "Мы выслали вам письмо с ссылкой на почту. Перейдите по ссылке в письме, чтобы войти на сайт.",
"Send link again": "Прислать ссылку ещё раз",
"Link sent, check your email": "Ссылка отправлена, проверьте почту"
"Link sent, check your email": "Ссылка отправлена, проверьте почту",
"Create post": "Создать публикацию"
}