Merge pull request #120 from tmshv/upd/maintenance-002

Maintenance 002
This commit is contained in:
tmshv authored and GitHub committed 2022-09-06 21:20:23 +03:00
commit 6b348debdd
42 files changed
+913 -579

No files matched your search

+2 -1
View File
@@ -14,11 +14,12 @@
"warn",
"double"
],
"no-tabs": "off",
"no-tabs": "error",
"indent": [
"warn",
4
],
"no-var": "error",
"eol-last": ["error", "always"],
"react/jsx-uses-react": "error",
"react/jsx-uses-vars": "error"
+591 -191
View File
File diff suppressed because it is too large. Load diff
+6 -4
View File
@@ -18,6 +18,8 @@
"author": "Roman Timashev",
"license": "ISC",
"dependencies": {
"@radix-ui/react-icons": "^1.1.1",
"@radix-ui/react-navigation-menu": "^1.0.0",
"classnames": "^2.2.6",
"date-fns": "^2.11.1",
"lodash": "^4.17.21",
@@ -25,10 +27,10 @@
"next": "^12.2.5",
"next-seo": "^5.5.0",
"rc-tooltip": "^5.1.0",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-icons": "^4.2.0",
"react-use": "^17.2.1",
"react-use": "^17.4.0",
"sharp": "^0.31.0",
"sitemap": "^1.5.0",
"typograf": "^6.11.1",
@@ -39,7 +41,7 @@
"@types/lodash": "^4.14.149",
"@types/markdown-it": "^12.2.3",
"@types/node": "^12.12.34",
"@types/react": "^16.9.32",
"@types/react": "^18.0.18",
"@types/sharp": "^0.29.2",
"eslint": "^8.23.0",
"eslint-config-next": "12.2.5",
+9 -9
View File
@@ -1,25 +1,25 @@
import { NextPage } from "next"
import { Wrapper } from "src/components/Wrapper"
import { NotFound } from "src/components/NotFound"
import { Copyright } from "src/components/Copyright"
import { Block } from "src/components/Block"
import { Spacer } from "src/components/Spacer"
import { Wrapper } from "@/components/Wrapper"
import { NotFound } from "@/components/NotFound"
import { Copyright } from "@/components/Copyright"
import { Spacer } from "@/components/Spacer"
import { Box } from "@/ui/Box"
const Index: NextPage = () => (
<Wrapper
header={null}
footer={(
<Block as={"footer"} direction={"horizontal"}>
<Box as={"footer"}>
<Spacer />
<Copyright />
<Spacer />
</Block>
</Box>
)}
>
<Block direction={"vertical"}>
<Box vertical>
<NotFound />
<Spacer />
</Block>
</Box>
</Wrapper>
)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 137 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 500 B

+10 -14
View File
@@ -1,29 +1,25 @@
import dynamic from "next/dynamic"
import { Wrapper } from "../Wrapper"
import { ConfigContext } from "src/context/ConfigContext"
import config from "src/config"
import { Footer } from "../Footer"
import { Content } from "../Content"
import { Block } from "../Block"
import { Breadcrumbs } from "../Breadcrumbs"
import { IBreadcumbsPart, IMenu } from "src/types"
import { useRouter } from "next/router"
import { useMobile } from "src/hooks/useMobile"
import { Gosuslugi } from "../Gosuslugi"
import { INavigationProps } from "@/components/Navigation"
import { Navigation } from "@/components/Navigation"
import { Box } from "@/ui/Box"
const Navigation = dynamic<INavigationProps>(() => import("../Navigation").then(mod => mod.Navigation), {
ssr: false,
})
export interface IAppProps {
export type AppProps = {
showAuthor?: boolean
contentStyle?: React.CSSProperties
breadcrumbs?: IBreadcumbsPart[]
menu: IMenu[]
children?: React.ReactNode
}
export const App: React.FC<IAppProps> = ({ showAuthor = false, menu, ...props }) => {
export const App: React.FC<AppProps> = ({ showAuthor = false, menu, ...props }) => {
const hideBreadcrumbs = useMobile()
const router = useRouter()
const blockStyle = {
@@ -39,17 +35,17 @@ export const App: React.FC<IAppProps> = ({ showAuthor = false, menu, ...props })
header={(
<header>
<Navigation style={{
margin: "0 var(--size-m) var(--size-m)",
// margin: "0 var(--size-m) var(--size-m)",
}} />
{hideBreadcrumbs || !props.breadcrumbs ? null : (
<Block direction={"horizontal"} style={blockStyle}>
<Box style={blockStyle}>
<Content>
<Breadcrumbs
items={props.breadcrumbs}
path={router.asPath}
/>
</Content>
</Block>
</Box>
)}
</header>
)}
@@ -68,11 +64,11 @@ export const App: React.FC<IAppProps> = ({ showAuthor = false, menu, ...props })
</Footer>
)}
>
<Block direction={"horizontal"} style={blockStyle}>
<Box style={blockStyle}>
<Content style={props.contentStyle}>
{props.children}
</Content>
</Block>
</Box>
</Wrapper>
</ConfigContext.Provider>
)
-22
View File
@@ -1,22 +0,0 @@
import s from "./styles.module.css"
import cx from "classnames"
import { createElement } from "react"
export type BlockProps = {
as?: keyof HTMLElementTagNameMap
style?: React.CSSProperties
direction: "horizontal" | "vertical"
}
export const Block: React.FC<BlockProps> = ({ as = "div", style, direction, ...props }) => {
const directionClass = direction === "horizontal"
? s.horizontal
: s.vertical
const newProps = {
className: cx(s.block, directionClass),
style,
}
return createElement(as, newProps, props.children)
}
-14
View File
@@ -1,14 +0,0 @@
.block {
display: flex;
flex-wrap: wrap;
align-items: baseline;
justify-content: flex-start;
}
.horizontal {
flex-direction: row;
}
.vertical {
flex-direction: column;
}
+1 -1
View File
@@ -1,7 +1,7 @@
import s from "./styles.module.css"
import { IBreadcumbsPart } from "src/types"
import { Button } from "../Button"
import { Button } from "@/ui/Button"
import { insertBetween } from "src/lib/array"
export type BreadcrumbsProps = {
+1
View File
@@ -10,6 +10,7 @@ const layoutClass = {
export type CardLayout = "simple" | "featured"
export type CardProps = {
children?: React.ReactNode
style?: React.CSSProperties
href: string
cover: React.ReactNode
+3 -1
View File
@@ -23,6 +23,8 @@
display: block;
height: 100%;
position: relative;
color: var(--color-text);
text-decoration: none;
}
.image {
@@ -40,7 +42,7 @@
.simple .body {
position: relative;
padding-top: 0;
padding-top: 0;
}
.simple .body:before {
+1
View File
@@ -1,6 +1,7 @@
import s from "./styles.module.css"
export type CardGridProps = {
children?: React.ReactNode
style?: React.CSSProperties
}
+1
View File
@@ -3,6 +3,7 @@ import { useReducedMotion } from "src/hooks/useReducedMotion"
export type ContentProps = {
style?: React.CSSProperties
children?: React.ReactNode
}
export const Content: React.FC<ContentProps> = props => {
+1 -1
View File
@@ -8,7 +8,7 @@ export type FileProps = FileDefinition & {
style?: React.CSSProperties
}
export const File: React.SFC<FileProps> = props => (
export const File: React.FC<FileProps> = props => (
<div className={s.file} style={props.style}>
{!props.cover?.src ? null : (
<div className={s.image}>
+10 -9
View File
@@ -3,34 +3,35 @@ import s from "./footer.module.css"
import cx from "classnames"
import { Copyright } from "../Copyright"
import { Author } from "./Author"
import { Block } from "../Block"
import { Spacer } from "../Spacer"
import { Contacts } from "../Contacts"
import { Box } from "@/ui/Box"
export interface IFooterProps {
export type FooterProps = {
showAuthor: boolean
children?: React.ReactNode
}
export const Footer: React.FC<IFooterProps> = props => (
export const Footer: React.FC<FooterProps> = ({ showAuthor, children }) => (
<footer className={cx(s.footer, "opposite")}>
<Block direction={"vertical"} style={{
<Box vertical align={false} style={{
marginBottom: "var(--size-l)",
}}>
<Contacts />
</Block>
</Box>
{props.children}
{children}
<Block direction={"horizontal"}>
<Box>
<Copyright />
{!props.showAuthor ? null : (
{!showAuthor ? null : (
<>
<Spacer as={"span"} />
<Author />
</>
)}
</Block>
</Box>
</footer >
)
+2 -1
View File
@@ -1,8 +1,9 @@
import Link from "next/link"
export interface ActiveLinkProps {
export type ActiveLinkProps = {
href?: string
activeStyle?: React.CSSProperties
children?: React.ReactNode
}
export const ActiveLink: React.FC<ActiveLinkProps> = props => props.href
-34
View File
@@ -1,34 +0,0 @@
import s from "./styles.module.css"
import cx from "classnames"
import { useRouter } from "next/router"
import { ActiveLink } from "./ActiveLink"
import { isPartOfPath } from "src/lib/url"
export type MenuItemProps = {
href: string
}
export const MenuItem: React.FC<MenuItemProps> = props => {
const ignore = ["/"]
const router = useRouter()
const current = router.asPath === props.href
const selected = current || (!ignore.includes(props.href) && isPartOfPath(props.href, router.asPath))
const href = current ? undefined : props.href
return (
<li
className={cx({
[s.selected]: selected,
})}
>
<ActiveLink
href={href}
activeStyle={{
fontWeight: "bold",
}}
>
{props.children}
</ActiveLink>
</li>
)
}
+35 -19
View File
@@ -1,27 +1,43 @@
import s from "./styles.module.css"
import cx from "classnames"
import { MenuItem } from "./MenuItem"
import { IMenu } from "src/types"
import { IMenu } from "@/types"
import * as NavigationMenu from "@radix-ui/react-navigation-menu"
import classnames from "classnames/bind"
import { useRouter } from "next/router"
import { ActiveLink } from "./ActiveLink"
const layoutClass = {
desktop: s.desktop,
mobile: s.mobile,
}
let cx = classnames.bind(s)
export type MenuProps = {
layout: "desktop" | "mobile"
vertical?: boolean
items: IMenu[]
}
export const Menu: React.FC<MenuProps> = props => (
<menu className={cx(s.menu, layoutClass[props.layout])}>
{props.items.map((item, index) => (
<MenuItem key={index}
href={item.href}
>
{item.name}
</MenuItem>
))}
</menu>
)
export const Menu: React.FC<MenuProps> = ({ vertical = false, items }) => {
const router = useRouter()
return (
<NavigationMenu.Root>
<NavigationMenu.List className={cx(s.menu, { vertical })}>
{
items.map(item => {
const current = router.asPath === item.href
const href = current ? undefined : item.href
if (!href) {
return (
<NavigationMenu.Item key={item.href} className={`${s.menuItem} ${s.current}`}>
<ActiveLink>{item.name}</ActiveLink>
</NavigationMenu.Item>
)
}
return (
<NavigationMenu.Item key={item.href} className={`${s.menuItem} ${s.active}`}>
<ActiveLink href={item.href}>{item.name}</ActiveLink>
</NavigationMenu.Item>
)
})
}
</NavigationMenu.List>
</NavigationMenu.Root>
)
}
+27 -26
View File
@@ -3,50 +3,51 @@
align-items: flex-start;
flex-wrap: wrap;
gap: var(--size-m);
font-size: var(--font-size-second);
list-style: none;
}
/* MENU LAYOUT */
.desktop {
/* NOT VERTICAL */
flex-direction: row;
}
.mobile {
.vertical {
flex-direction: column;
}
/* MENU ITEM */
.menu li {
position: relative;
border-top: var(--menu-mark-thickness) solid rgba(0, 0, 0, 0);
.menuItem {
user-select: none;
background-color: var(--color-back-main);
border-radius: var(--size-s);
transition: box-shadow 200ms ease-in, background-color 200ms ease-in;
padding: var(--size-s) var(--size-s);
}
/* MENU ITEM MOBILE */
.mobile li {
font-size: var(--font-size-default);
padding: var(--size-xs) var(--size-s);
.menuItem.active {
cursor: pointer;
}
.mobile li.selected {
.menuItem.active:hover {
box-shadow: var(--shadow);
}
.menuItem.current {
color: var(--color-text-opposite);
background-color: var(--color-back-opposite);
color: var(--color-text-opposite)
}
.mobile li.selected a {
color: var(--color-text-opposite)
}
/* MENU ITEM DESKTOP */
.desktop li {
padding: var(--size-xs) var(--size-s);
.menuItem a {
text-decoration: none;
color: var(--color-text);
}
.desktop li.selected {
border-top-color: var(--color-back-opposite);
/* DARK THEME */
@media (prefers-color-scheme: dark) {
.menuItem.active:hover {
box-shadow: none;
background-color: var(--color-back);
}
}
+11 -16
View File
@@ -1,27 +1,22 @@
import s from "./styles.module.css"
import { Button, ButtonProps } from "@/ui/Button"
import { HamburgerMenuIcon, Cross1Icon } from "@radix-ui/react-icons"
import cx from "classnames"
import { Button } from "../Button"
export interface IMenuToggleProps {
style?: React.CSSProperties
export type MenuToggleProps = Omit<ButtonProps, "theme"> & {
open: boolean
onClick: () => void
}
export const MenuToggle: React.FC<IMenuToggleProps> = props => {
export const MenuToggle: React.FC<MenuToggleProps> = ({ open, ...props }) => {
const icon = open ? (
<Cross1Icon width={24} height={24} />
) : (
<HamburgerMenuIcon width={24} height={24} />
)
return (
<Button
onClick={props.onClick}
style={props.style}
{...props}
theme={"icon"}
>
<div
className={cx(s.toggle, {
[s.open]: props.open,
[s.close]: !props.open,
})}
/>
{icon}
</Button>
)
}
@@ -1,14 +0,0 @@
.toggle {
--s: 32px;
width: var(--s);
height: var(--s);
background-size: var(--s);
}
.toggle.open {
background-image: url(/static/graphics/hmenu-close-b.png);
}
.toggle.close {
background-image: url(/static/graphics/hmenu-b.png);
}
+37 -48
View File
@@ -1,76 +1,65 @@
import { MenuToggle } from "../MenuToggle"
import { useMobile } from "src/hooks/useMobile"
import { Menu } from "../Menu"
import { Overlay } from "../Overlay"
import { useState, useCallback, useContext } from "react"
import { Overlay } from "@/ui/Overlay"
import { useContext } from "react"
import { ConfigContext } from "src/context/ConfigContext"
import { Block } from "../Block"
import { Box } from "@/ui/Box"
import { Spacer } from "../Spacer"
import { useToggle } from "react-use"
export interface INavigationProps {
export type NavigationProps = {
style?: React.CSSProperties
}
export const Navigation: React.FC<INavigationProps> = props => {
export const Navigation: React.FC<NavigationProps> = props => {
const collapseMenu = useMobile()
const [open, setOpen] = useState(false)
const onClick = useCallback(
() => {
setOpen(!open)
},
[open]
)
const [open, toggleOpen] = useToggle(false)
const { menu } = useContext(ConfigContext)
return (
<>
<Overlay
show={open}
onClickOverlay={onClick}
onClickOverlay={toggleOpen}
duration={250}
>
<Block direction={"horizontal"}>
<Box style={{
padding: "var(--size-s)",
}}>
<Spacer />
<MenuToggle
open={open}
onClick={onClick}
style={{
margin: "var(--size-xs)",
}}
onClick={toggleOpen}
/>
</Block>
<Block direction={"horizontal"} style={{
</Box>
<Box style={{
justifyContent: "center",
}}>
<nav style={{
width: "75%",
}}>
<Menu
layout={"mobile"}
items={menu}
/>
</nav>
</Block>
</Overlay>
{collapseMenu ? (
<Block direction={"horizontal"}>
<Spacer />
<MenuToggle
open={open}
onClick={onClick}
style={{
margin: "var(--size-xs)",
}}
/>
</Block>
) : (
<nav style={props.style}>
<Menu
layout={"desktop"}
<Menu vertical
items={menu}
/>
</nav>
)}
</Box>
</Overlay>
<Box wrap style={{
padding: "var(--size-s)",
}}>
{collapseMenu ? null : (
<Menu
items={menu}
/>
)}
<Spacer />
{!collapseMenu ? null : (
<MenuToggle
open={open}
onClick={toggleOpen}
/>
)}
</Box>
</>
)
}
-44
View File
@@ -1,44 +0,0 @@
import s from "./styles.module.css"
import { Transition } from "./Transition"
import { useReducedMotion } from "../../hooks/useReducedMotion"
import { useCallback, useEffect } from "react"
export interface IOverlayProps {
show: boolean
onClickOverlay: () => void
style?: React.CSSProperties
}
export const Overlay: React.FC<IOverlayProps> = props => {
const motionDisabled = useReducedMotion()
const onClick = useCallback((event: React.MouseEvent<HTMLDivElement>) => {
if (event.target === event.currentTarget) {
props.onClickOverlay()
}
}, [props.onClickOverlay])
const duration = motionDisabled ? 0 : 250
useEffect(() => {
if (props.show) {
document.body.classList.add("noscroll")
} else {
document.body.classList.remove("noscroll")
}
}, [props.show])
return (
<Transition
duration={duration}
show={props.show}
>
<div
onClick={onClick}
className={s.overlay}
style={props.style}
>
{props.children}
</div>
</Transition>
)
}
+1
View File
@@ -4,6 +4,7 @@ import { PageMeta } from "../PageMeta"
import { ITag } from "src/types"
export type PageProps = {
children?: React.ReactNode
style?: React.CSSProperties
date?: Date
tags: ITag[]
+3 -5
View File
@@ -3,8 +3,8 @@ import { PageCardDto } from "@/types"
import { CardGrid } from "../CardGrid"
import { Card, CardLayout } from "../Card"
import { Date } from "./Date"
import { Block } from "../Block"
import { Spacer } from "../Spacer"
import { Box } from "@/ui/Box"
export type PageGridProps = {
items: PageCardDto[]
@@ -23,9 +23,7 @@ export const PageGrid: React.FC<PageGridProps> = props => {
const content = !item.featured
? (
<Block
direction={"vertical"}
>
<Box vertical>
{item.title}
<Spacer />
{!item.date ? null : (
@@ -33,7 +31,7 @@ export const PageGrid: React.FC<PageGridProps> = props => {
marginTop: "var(--size-m)",
}}>{item.date}</Date>
)}
</Block>
</Box>
) : (
item.title
)
-20
View File
@@ -1,20 +0,0 @@
import { Block, BlockProps } from "../Block"
import { Button } from "../Button"
import { Spacer } from "../Spacer"
export type PagerProps = Omit<BlockProps, "direction"> & {
nextHref: string | null
prevHref: string | null
}
export const Pager: React.FC<PagerProps> = ({ prevHref, nextHref, ...props }) => (
<Block {...props} direction={"horizontal"}>
{!prevHref ? null : (
<Button href={prevHref}>Предыдущая страница</Button>
)}
<Spacer />
{!nextHref ? null : (
<Button href={nextHref}>Следущая страница</Button>
)}
</Block>
)
+1 -1
View File
@@ -43,7 +43,7 @@ export const Picture: React.FC<PictureProps> = memo(({ wide = false, ...props })
height={props.height}
layout={"responsive"}
blurDataURL={props.blur}
placeholder={"blur"}
placeholder={props.blur ? "blur": "empty"}
/>
{!props.caption ? null : (
<figcaption>{props.caption}</figcaption>
+8 -6
View File
@@ -1,17 +1,19 @@
import s from './styles.module.css'
import s from "./styles.module.css"
import cx from 'classnames'
import { ITag } from 'src/types'
import { Button } from '../Button'
import cx from "classnames"
import { ITag } from "src/types"
import { Button } from "@/ui/Button"
import Image from "next/image"
const directionClass = {
horizontal: '',
horizontal: "",
vertical: s.vertical,
}
export type Direction = 'horizontal' | 'vertical'
export type Direction = "horizontal" | "vertical"
export type TagProps = {
children?: React.ReactNode
href: string
direction: Direction
}
+1
View File
@@ -7,6 +7,7 @@ export type WrapperProps = {
footer: React.ReactNode
style?: React.CSSProperties
mainStyle?: React.CSSProperties
children?: React.ReactNode
}
export const Wrapper: React.FC<WrapperProps> = props => (
+1 -1
View File
@@ -1,7 +1,6 @@
.container {
display: flex;
flex-direction: column;
min-height: 100vh;
background-color: var(--color-back-main);
font-family: var(--text-font);
@@ -9,5 +8,6 @@
}
.main {
min-height: 100vh;
flex: 1;
}
-28
View File
@@ -40,34 +40,6 @@ const config = {
name: "Школа",
href: "/",
},
{
name: "Поступление",
href: "/join",
},
{
name: "Предметы",
href: "/courses",
},
{
name: "Коллектив",
href: "/collective",
},
{
name: "«Мы соседи»",
href: "/neighbors",
},
{
name: "Документы",
href: "/documents",
},
{
name: "Сведения об образовательной организации",
href: "/info",
},
{
name: "Дистанционное образование",
href: "/info/online-education",
},
],
}
-4
View File
@@ -1,7 +1,3 @@
export function isPartOfPath(part: string, path: string): boolean {
return path.startsWith(part)
}
export function queryList(key: string, values: string[]): string {
if (values.length === 0) {
return ""
+15
View File
@@ -2,6 +2,21 @@
hyphens: auto;
}
.article a {
color: var(--color-text);
text-decoration: none;
border-bottom: 1px solid var(--color-link);
}
.article a:hover {
border-bottom: 2px solid var(--color-text);
}
.article a:active {
border-width: 2px;
}
.article h1 {
margin-bottom: var(--size-l);
}
+7 -23
View File
@@ -44,14 +44,16 @@
/* EFFECTS */
--radius: 16px;
--shadow: 0 0 8px var(--color-shadow);
--shadow:
0 2px 4px rgba(27, 51, 88, 0.2),
0 4px 8px -8px rgba(0, 0, 0, 0.2),
0 8px 16px -8px rgba(0, 0, 0, 0.3);
--shadow-active:
0 12px 24px -8px rgba(27, 51, 88, 0.25),
0 8px 16px -8px rgba(0, 0, 0, 0.3);
/* SPECIAL */
--article-line-height: 1.5em;
--menu-mark-thickness: 4px;
--embed-video-ratio: 56.25%;
/* CONTENT */
@@ -108,24 +110,9 @@ h3 {
hyphens: none;
}
a {
color: var(--color-text);
text-decoration: none;
border-bottom: 1px solid var(--color-link);
}
a:hover {
border-bottom: 2px solid var(--color-text);
}
a:active {
border-width: 2px;
}
a.invisible {
/* a.invisible {
border-bottom: none;
}
} */
.opposite a {
color: var(--color-text-opposite);
@@ -136,10 +123,6 @@ a.invisible {
border-bottom-color: var(--color-text-opposite);
}
.noscroll {
overflow: hidden;
}
/* RC TOOLTIP OVERRIDE */
.hudozka.rc-tooltip {
@@ -199,6 +182,7 @@ a.invisible {
--color-back-main: hsl(240, 10%, 10%);
--color-back: hsl(240, 10%, 15%);
--color-back-active: hsl(240, 10%, 20%);
--color-back-opposite: hsl(240, 10%, 80%);
--color-link: hsl(240, 10%, 40%);
--color-link-opposite: hsl(240, 10%, 60%);
+25
View File
@@ -0,0 +1,25 @@
.box {
display: flex;
align-items: baseline;
justify-content: flex-start;
flex-wrap: nowrap;
flex-direction: row;
}
.box.align {
align-items: center;
}
.box.wrap {
flex-wrap: wrap;
}
.box.center {
align-items: center;
justify-content: center;
}
.box.vertical {
flex-direction: column;
}
+33
View File
@@ -0,0 +1,33 @@
import s from "./box.module.css"
import classnames from "classnames/bind"
import { createElement } from "react"
const cx = classnames.bind(s)
export type BoxProps = {
as?: keyof HTMLElementTagNameMap
children?: React.ReactNode
style?: React.CSSProperties
wrap?: boolean
align?: boolean
vertical?: boolean
center?: boolean
}
export const Box: React.FC<BoxProps> = ({
as = "div",
wrap = false,
vertical = false,
center = false,
align = true,
style,
...props
}) => {
const newProps = {
className: cx(s.box, { vertical, align, center, wrap }),
style,
}
return createElement(as, newProps, props.children)
}
@@ -1,28 +1,26 @@
.button {
display: block;
display: inline-flex;
align-items: center;
font-size: var(--font-size-default);
text-align: center;
border-radius: 100px;
border-radius: var(--size-s);
border: 1px solid var(--color-shadow);
color: var(--color-text);
background-color: var(--color-back);
transition: box-shadow 150ms;
box-shadow: none;
transition: box-shadow 200ms ease-in, background-color 200ms ease-in;
outline: none;
cursor: pointer;
}
.button:hover {
margin: 1px;
/* margin-left: 1px;
margin-right: 1px; */
border: none;
box-shadow: var(--shadow-active);
border-color: rgba(0, 0, 0, 0);
box-shadow: var(--shadow);
}
/* MODIFICATORS */
@@ -50,5 +48,13 @@
}
.themeIcon {
border-color: rgba(0, 0, 0, 0);
padding: var(--size-xs) var(--size-xs);
}
/* DARK THEME */
@media (prefers-color-scheme: dark) {
.button:hover {
background-color: var(--color-back-active);
}
}
@@ -1,8 +1,9 @@
import s from "./styles.module.css"
import s from "./button.module.css"
import Link from "next/link"
import cx from "classnames"
import { useCallback } from "react"
import type { MouseEvent, MouseEventHandler } from "react"
const sizeClass = {
default: s.sizeDefault,
@@ -16,6 +17,7 @@ const themeClass = {
}
export type ButtonProps = {
children?: React.ReactNode
value?: any
style?: React.CSSProperties
href?: string
@@ -29,11 +31,11 @@ export const Button: React.FC<ButtonProps> = ({ size = "default", theme = "defau
const className = cx(s.button, sizeClass[size], themeClass[theme], {
[s.disabled]: disabled,
})
const onClick = useCallback(event => {
if (props.onClick) {
const onClick = useCallback<MouseEventHandler<HTMLButtonElement>>(event => {
if (typeof props.onClick === "function") {
props.onClick(props.value, event)
}
}, [props.value, props.onClick])
}, [props])
if (props.href) {
return (
@@ -1,25 +1,26 @@
import * as React from "react"
import { useCallback, useEffect, useMemo, useState } from "react"
export interface ITransitionProps {
export type TransitionProps = {
children?: React.ReactNode
duration: number
extraStyle?: React.CSSProperties
show: boolean
}
export const Transition: React.FC<ITransitionProps> = props => {
const [mounted, setMounted] = React.useState(props.show)
const [opacity, setOpacity] = React.useState(0)
const onTransitionEnd = React.useCallback(() => {
export const Transition: React.FC<TransitionProps> = props => {
const [mounted, setMounted] = useState(props.show)
const [opacity, setOpacity] = useState(0)
const onTransitionEnd = useCallback(() => {
if (opacity === 0) {
setMounted(false)
}
}, [opacity])
const transition = React.useMemo(
const transition = useMemo(
() => `all ${props.duration}ms`,
[props.duration]
)
React.useEffect(() => {
useEffect(() => {
if (!props.show) {
setOpacity(0)
return
+39
View File
@@ -0,0 +1,39 @@
import s from "./styles.module.css"
import { Transition } from "./Transition"
import { useReducedMotion } from "../../hooks/useReducedMotion"
import { useLockBodyScroll } from "react-use"
export type OverlayProps = {
children?: React.ReactNode
show: boolean
onClickOverlay: () => void
style?: React.CSSProperties
duration: number
}
export const Overlay: React.FC<OverlayProps> = props => {
const motionDisabled = useReducedMotion()
const duration = motionDisabled ? 0 : props.duration
useLockBodyScroll(props.show)
return (
<Transition
duration={duration}
show={props.show}
>
<div
onClick={(event) => {
if (event.target === event.currentTarget) {
props.onClickOverlay()
}
}}
className={s.overlay}
style={props.style}
>
{props.children}
</div>
</Transition>
)
}
File renamed without changes.