import { useContext } from 'react'
import * as React from 'react'
import styled, { createGlobalStyle, css } from 'styled-components'
import {
MAIN_COLUMN_HORIZONTAL_MIN_MARGIN,
RESPONSIVE_LEFT_WIDTH,
RESPONSIVE_MAIN_MAX_WIDTH,
} from '../../foundation/constants'
import { useMediaScreen1 } from '../../foundation/hooks'
import { columnSystem, COLUMN_UNIT, GUTTER_UNIT } from '@charcoal-ui/foundation'
import { maxWidth } from '@charcoal-ui/utils'
interface Props {
menu?: React.ReactNode
isHeaderTopMenu?: boolean
children: React.ReactNode
header?: React.ReactNode
wide?: boolean
center?: boolean
}
const LayoutConfigContext = React.createContext({
wide: false,
center: false,
withLeft: false,
})
export default function Layout({
menu,
children,
header,
center = false,
wide,
isHeaderTopMenu = false,
}: Props) {
const config = {
center,
wide: center ? true : (wide ?? false),
withLeft: menu != null && !isHeaderTopMenu,
}
return (
{config.withLeft && {menu}}
{header != null && }
{isHeaderTopMenu && (
{menu}
)}
{children}
)
}
const HeaderTopMenuContainer = styled.div`
margin-bottom: 40px;
overflow-x: auto;
word-break: keep-all;
@media ${({ theme }) => maxWidth(theme.breakpoint.screen1)} {
margin-bottom: 0;
padding-left: 16px;
padding-bottom: 16px;
background-color: ${({ theme }) => theme.color.surface2};
}
`
const GlobalStyle = createGlobalStyle`
:root {
background-color: ${({ theme }) => theme.color.background2};
@media ${({ theme }) => maxWidth(theme.breakpoint.screen1)} {
background-color: ${({ theme }) => theme.color.background1};
}
}
`
const LayoutRoot = styled.div`
display: flex;
background-color: ${({ theme }) => theme.color.background2};
@media ${({ theme }) => maxWidth(theme.breakpoint.screen1)} {
background-color: ${({ theme }) => theme.color.background1};
}
`
const LeftArea = styled.div`
min-width: ${RESPONSIVE_LEFT_WIDTH}px;
padding: 40px 0 40px ${GUTTER_UNIT}px;
box-sizing: border-box;
@media ${({ theme }) => theme.breakpoint.screen3} {
display: none;
}
`
const MainArea = styled.div<{ center: boolean }>`
flex-grow: 1;
/* https://www.w3.org/TR/css-flexbox-1/#min-size-auto */
min-width: 0;
max-width: ${(p) =>
p.center
? columnSystem(6, COLUMN_UNIT, GUTTER_UNIT)
: RESPONSIVE_MAIN_MAX_WIDTH}px;
padding: 40px ${MAIN_COLUMN_HORIZONTAL_MIN_MARGIN}px;
margin: 0 auto;
display: flex;
flex-direction: column;
@media ${({ theme }) => maxWidth(theme.breakpoint.screen1)} {
padding: 0;
}
`
const Header = styled.div`
font-weight: bold;
margin-bottom: 12px;
font-size: 20px;
line-height: 28px;
color: ${({ theme }) => theme.color.text2};
@media ${({ theme }) => maxWidth(theme.breakpoint.screen1)} {
margin-bottom: 0;
padding: 12px;
font-size: 16px;
line-height: 24px;
display: flex;
justify-content: center;
background-color: ${({ theme }) => theme.color.surface2};
}
`
const Grid = styled.div`
display: grid;
gap: ${GUTTER_UNIT}px;
grid-template-columns: 1fr;
grid-auto-columns: 1fr;
grid-auto-rows: auto;
@media ${({ theme }) => maxWidth(theme.breakpoint.screen1)} {
gap: 0;
background-color: ${({ theme }) => theme.color.background1};
padding-bottom: 60px;
}
`
interface LayoutItemProps {
span: number
children?: React.ReactNode
}
export const LayoutItem = React.forwardRef(
function LayoutItem({ span, children }, ref) {
const { withLeft } = useContext(LayoutConfigContext)
return (
{children}
)
},
)
interface StyledLayoutItemProps {
span: number
withLeft: boolean
}
const StyledLayoutItem = styled.div`
grid-column-start: auto;
grid-column-end: span ${(p) => p.span};
border-radius: 24px;
color: ${({ theme }) => theme.color.text2};
background-color: ${({ theme }) => theme.color.background1};
/* https://www.w3.org/TR/css-grid-1/#min-size-auto */
min-width: 0;
@media ${(p) =>
p.withLeft ? p.theme.breakpoint.screen4 : p.theme.breakpoint.screen3} {
${(p) =>
p.span > 2 &&
css`
grid-column-end: span 2;
`}
}
@media ${({ theme }) => maxWidth(theme.breakpoint.screen1)} {
${(p) =>
p.span > 1 &&
css`
grid-column-end: span 1;
`}
border-radius: 0;
padding-bottom: 40px;
}
`
export function LayoutItemHeader({ children }: { children: React.ReactNode }) {
const { wide, center } = useContext(LayoutConfigContext)
return (
{children}
)
}
interface StyledLayoutItemHeaderProps {
wide: boolean
center: boolean
}
const StyledLayoutItemHeader = styled.div`
padding: 0 ${(p) => (p.wide ? 40 : 24)}px;
height: ${(p) => (p.wide ? 64 : 48)}px;
display: grid;
align-items: center;
font-size: 16px;
line-height: 24px;
font-weight: bold;
background-color: ${({ theme }) => theme.color.surface2};
color: ${({ theme }) => theme.color.text2};
border-radius: 24px 24px 0 0;
${(p) =>
p.center &&
css`
justify-content: center;
`}
@media ${({ theme }) => maxWidth(theme.breakpoint.screen1)} {
margin-top: 4px;
padding: 0 16px;
background: none;
overflow-x: auto;
border-radius: unset;
${(p) =>
p.wide &&
css`
height: 48px;
margin-top: 0;
`}
}
`
export const LAYOUT_ITEM_BODY_PADDING = {
wide: {
x: 40,
y: 40,
},
default: {
x: 24,
y: 24,
},
column1: {
x: 16,
y: 16,
},
narrow: {
x: 24,
yTop: 12,
yBottom: 20,
},
narrowColumn1: {
x: 16,
yTop: 4,
yBottom: 0,
},
}
export function LayoutItemBody({
children,
horizontal = false,
narrow = false,
}: {
children: React.ReactNode
horizontal?: boolean
narrow?: boolean
}) {
const { wide } = useContext(LayoutConfigContext)
return (
{children}
)
}
interface StyledLayoutItemBodyProps {
wide: boolean
horizontal: boolean
narrow: boolean
}
export const StyledLayoutItemBody = styled.div`
padding: ${(p) =>
p.narrow
? `${LAYOUT_ITEM_BODY_PADDING.narrow.yTop}px ${
p.horizontal ? 0 : LAYOUT_ITEM_BODY_PADDING.narrow.x
}px ${LAYOUT_ITEM_BODY_PADDING.narrow.yBottom}px`
: p.wide
? `${p.horizontal ? 0 : LAYOUT_ITEM_BODY_PADDING.wide.y}px ${
LAYOUT_ITEM_BODY_PADDING.wide.x
}px`
: `${p.horizontal ? 0 : LAYOUT_ITEM_BODY_PADDING.default.y}px ${
LAYOUT_ITEM_BODY_PADDING.default.x
}px`};
@media ${({ theme }) => maxWidth(theme.breakpoint.screen1)} {
padding: ${(p) =>
p.narrow
? `${LAYOUT_ITEM_BODY_PADDING.narrowColumn1.yTop}px ${
p.horizontal ? 0 : LAYOUT_ITEM_BODY_PADDING.narrowColumn1.x
}px ${LAYOUT_ITEM_BODY_PADDING.narrowColumn1.yBottom}px`
: `${LAYOUT_ITEM_BODY_PADDING.column1.y}px ${
LAYOUT_ITEM_BODY_PADDING.column1.x
}px ${0}`};
}
width: 100%;
box-sizing: border-box;
`
export function useLayoutItemBodyPadding() {
const { wide } = useContext(LayoutConfigContext)
return useMediaScreen1()
? LAYOUT_ITEM_BODY_PADDING.column1
: wide
? LAYOUT_ITEM_BODY_PADDING.wide
: LAYOUT_ITEM_BODY_PADDING.default
}
export function CancelLayoutItemBodyPadding({
children,
cancelTop,
}: {
children: React.ReactNode
cancelTop?: boolean
}) {
const { wide } = useContext(LayoutConfigContext)
return (
{children}
)
}
interface StyledCancelLayoutItemBodyPaddingProps {
wide: boolean
cancelTop?: boolean
}
export const StyledCancelLayoutItemBodyPadding = styled.div`
margin: 0 -${(p) =>
p.wide
? LAYOUT_ITEM_BODY_PADDING.wide.x
: LAYOUT_ITEM_BODY_PADDING.default.x}px;
margin-top: -${({ cancelTop = false, wide }) =>
!cancelTop
? 0
: wide
? LAYOUT_ITEM_BODY_PADDING.wide.y
: LAYOUT_ITEM_BODY_PADDING.default.y}px;
@media ${({ theme }) => maxWidth(theme.breakpoint.screen1)} {
margin: 0 -${LAYOUT_ITEM_BODY_PADDING.column1.x}px;
margin-top: -${({ cancelTop = false }) =>
!cancelTop ? 0 : LAYOUT_ITEM_BODY_PADDING.column1.x}px;
}
`