Merge branch 'master' into fix/slider-offset-bug

This commit is contained in:
aoki
2020-06-28 17:42:52 +09:00
committed by GitHub
117 changed files with 2871 additions and 38296 deletions

3
.gitignore vendored
View File

@@ -27,3 +27,6 @@ yarn-error.log*
.now
dist
esm
examples/**/yarn.lock
examples/**/out
examples/**/.next

View File

@@ -4,6 +4,7 @@ exports[`AutoComplete should render correctly 1`] = `
<AutoComplete
className=""
clearable={false}
disableFreeSolo={false}
disableMatchWidth={false}
disabled={false}
initialValue=""

View File

@@ -2,6 +2,7 @@ import React from 'react'
import { mount } from 'enzyme'
import { AutoComplete } from 'components'
import { nativeEvent } from 'tests/utils'
import { act } from 'react-dom/test-utils'
describe('AutoComplete', () => {
it('should render correctly', () => {
@@ -32,11 +33,13 @@ describe('AutoComplete', () => {
expect((input as HTMLInputElement).value).toEqual('value2')
})
it('should render clear icon', () => {
it('should render clear icon', async () => {
const wrapper = mount(<AutoComplete initialValue="value" />)
expect(wrapper.find('svg').length).toBe(0)
wrapper.setProps({ clearable: true })
await act(async () => {
wrapper.setProps({ clearable: true })
})
expect(wrapper.find('svg').length).toBe(1)
wrapper.find('svg').simulate('click', nativeEvent)
@@ -44,11 +47,13 @@ describe('AutoComplete', () => {
expect((input as HTMLInputElement).value).toEqual('')
})
it('should reponse width change', () => {
it('should reponse width change', async () => {
const wrapper = mount(<AutoComplete initialValue="value" width="100px" />)
expect(wrapper.prop('width')).toEqual('100px')
await act(async () => {
wrapper.setProps({ width: '200px' })
})
wrapper.setProps({ width: '200px' })
expect(wrapper.prop('width')).toEqual('200px')
})
})

View File

@@ -1,7 +1,8 @@
import React from 'react'
import { mount, render } from 'enzyme'
import { AutoComplete } from 'components'
import { nativeEvent } from 'tests/utils'
import { nativeEvent, updateWrapper } from 'tests/utils'
import { act } from 'react-dom/test-utils'
const mockOptions = [{ label: 'London', value: 'london' }]
describe('AutoComplete Search', () => {
@@ -33,9 +34,11 @@ describe('AutoComplete Search', () => {
expect(value).not.toEqual('london')
})
it('should render searching component', () => {
it('should render searching component', async () => {
let wrapper = mount(<AutoComplete searching={false} options={mockOptions} />)
wrapper.setProps({ searching: true })
await act(async () => {
wrapper.setProps({ searching: true })
})
wrapper.find('input').at(0).simulate('focus')
let dropdown = wrapper.find('.auto-complete-dropdown')
expect(dropdown.text()).not.toContain('london')
@@ -136,4 +139,15 @@ describe('AutoComplete Search', () => {
const wrapper = mount(<AutoComplete options={[]} />)
expect(() => wrapper.unmount()).not.toThrow()
})
it('value should be reset when freeSolo disabled', async () => {
const wrapper = mount(<AutoComplete initialValue="value" disableFreeSolo />)
const input = wrapper.find('input').at(0)
input.simulate('focus')
input.simulate('change', { target: { value: 'test' } })
expect((input.getDOMNode() as HTMLInputElement).value).toEqual('test')
input.simulate('blur')
await updateWrapper(wrapper, 200)
expect((input.getDOMNode() as HTMLInputElement).value).toEqual('value')
})
})

View File

@@ -0,0 +1,60 @@
import React, { useEffect } from 'react'
import { mount } from 'enzyme'
import { AutoComplete, useInput } from 'components'
describe('UseInput', () => {
it('should follow change with use-input', () => {
let log = ''
const logSpy = jest.spyOn(console, 'log').mockImplementation(msg => (log = msg))
const MockInput: React.FC<{ value?: string }> = ({ value }) => {
const { state, setState, bindings } = useInput('')
useEffect(() => {
if (value) setState(value)
}, [value])
useEffect(() => {
if (state) console.log(state)
}, [state])
return <AutoComplete {...bindings} />
}
const wrapper = mount(<MockInput />)
wrapper.setProps({ value: 'test' })
const input = wrapper.find('input').at(0).getDOMNode() as HTMLInputElement
expect(input.value).toEqual('test')
expect(log).toContain('test')
log = ''
wrapper
.find('input')
.at(0)
.simulate('change', { target: { value: 'test-change' } })
expect(log).toContain('test-change')
logSpy.mockRestore()
})
it('should follow change with use-input', () => {
const MockInput: React.FC<{ value?: string; resetValue?: boolean }> = ({
value,
resetValue,
}) => {
const { reset, setState, bindings } = useInput('')
useEffect(() => {
if (value) setState(value)
}, [value])
useEffect(() => {
if (resetValue) reset()
}, [resetValue])
return <AutoComplete {...bindings} />
}
const wrapper = mount(<MockInput />)
wrapper.setProps({ value: 'test' })
let input = wrapper.find('input').at(0).getDOMNode() as HTMLInputElement
expect(input.value).toEqual('test')
wrapper.setProps({ resetValue: true })
input = wrapper.find('input').at(0).getDOMNode() as HTMLInputElement
expect(input.value).toEqual('')
})
})

View File

@@ -28,9 +28,10 @@ const AutoCompleteItem: React.FC<React.PropsWithChildren<AutoCompleteItemProps>>
children,
}) => {
const theme = useTheme()
const { value, updateValue, size } = useAutoCompleteContext()
const { value, updateValue, size, updateVisible } = useAutoCompleteContext()
const selectHandler = () => {
updateValue && updateValue(identValue)
updateVisible && updateVisible(false)
}
const isActive = useMemo(() => value === identValue, [identValue, value])
@@ -38,7 +39,7 @@ const AutoCompleteItem: React.FC<React.PropsWithChildren<AutoCompleteItemProps>>
return (
<div className={`item ${isActive ? 'active' : ''}`} onClick={selectHandler}>
<Ellipsis>{children}</Ellipsis>
<Ellipsis height={`calc(1.688 * ${theme.layout.gap})`}>{children}</Ellipsis>
<style jsx>{`
.item {
display: flex;
@@ -47,8 +48,8 @@ const AutoCompleteItem: React.FC<React.PropsWithChildren<AutoCompleteItemProps>>
font-weight: normal;
white-space: pre;
font-size: ${fontSize};
padding: ${theme.layout.gapHalf};
line-height: 1.2;
padding: 0 ${theme.layout.gapHalf};
height: calc(1.688 * ${theme.layout.gap});
background-color: ${theme.palette.background};
color: ${theme.palette.foreground};
user-select: none;

View File

@@ -8,6 +8,7 @@ import { AutoCompleteContext, AutoCompleteConfig } from './auto-complete-context
import { NormalSizes, NormalTypes } from '../utils/prop-types'
import Loading from '../loading'
import { pickChild } from '../utils/collections'
import useCurrentState from '../utils/use-current-state'
export type AutoCompleteOption = {
label: string
@@ -31,6 +32,7 @@ interface Props {
dropdownClassName?: string
dropdownStyle?: object
disableMatchWidth?: boolean
disableFreeSolo?: boolean
className?: string
}
@@ -41,6 +43,7 @@ const defaultProps = {
clearable: false,
size: 'medium' as NormalSizes,
disableMatchWidth: false,
disableFreeSolo: false,
className: '',
}
@@ -83,11 +86,16 @@ const AutoComplete: React.FC<React.PropsWithChildren<AutoCompleteProps>> = ({
dropdownClassName,
dropdownStyle,
disableMatchWidth,
disableFreeSolo,
...props
}) => {
const ref = useRef<HTMLDivElement>(null)
const [state, setState] = useState<string>(customInitialValue)
const inputRef = useRef<HTMLInputElement>(null)
const resetTimer = useRef<number>()
const [state, setState, stateRef] = useCurrentState<string>(customInitialValue)
const [selectVal, setSelectVal] = useState<string>(customInitialValue)
const [visible, setVisible] = useState<boolean>(false)
const [, searchChild] = pickChild(children, AutoCompleteSearching)
const [, emptyChild] = pickChild(children, AutoCompleteEmpty)
const autoCompleteItems = useMemo(() => {
@@ -110,14 +118,24 @@ const AutoComplete: React.FC<React.PropsWithChildren<AutoCompleteProps>> = ({
const updateValue = (val: string) => {
if (disabled) return
setSelectVal(val)
onSelect && onSelect(val)
setState(val)
inputRef.current && inputRef.current.focus()
}
const updateVisible = (next: boolean) => setVisible(next)
const onInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setVisible(true)
onSearch && onSearch(event.target.value)
setState(event.target.value)
}
const resetInputValue = () => {
if (!disableFreeSolo) return
if (!state || state === '') return
if (state !== selectVal) {
setState(selectVal)
}
}
useEffect(() => {
onChange && onChange(state)
@@ -140,9 +158,15 @@ const AutoComplete: React.FC<React.PropsWithChildren<AutoCompleteProps>> = ({
)
const toggleFocusHandler = (next: boolean) => {
clearTimeout(resetTimer.current)
setVisible(next)
if (next) {
onSearch && onSearch(state)
onSearch && onSearch(stateRef.current)
} else {
resetTimer.current = window.setTimeout(() => {
resetInputValue()
clearTimeout(resetTimer.current)
}, 100)
}
}
@@ -157,6 +181,7 @@ const AutoComplete: React.FC<React.PropsWithChildren<AutoCompleteProps>> = ({
<AutoCompleteContext.Provider value={initialValue}>
<div ref={ref} className="auto-complete">
<Input
ref={inputRef}
size={size}
status={status}
onChange={onInputChange}

View File

@@ -316,7 +316,7 @@ initialize {
font-variant: tabular-nums;
line-height: 1;
vertical-align: middle;
background-color: #ff0000;
background-color: #e00;
color: white;
font-size: .875rem;
border: 0;
@@ -358,7 +358,7 @@ initialize {
font-variant: tabular-nums;
line-height: 1;
vertical-align: middle;
background-color: #ff0000;
background-color: #e00;
color: white;
font-size: .875rem;
border: 0;
@@ -645,7 +645,7 @@ initialize {
font-variant: tabular-nums;
line-height: 1;
vertical-align: middle;
background-color: #ff0000;
background-color: #e00;
color: white;
font-size: .875rem;
border: 0;
@@ -687,7 +687,7 @@ initialize {
font-variant: tabular-nums;
line-height: 1;
vertical-align: middle;
background-color: #ff0000;
background-color: #e00;
color: white;
font-size: .875rem;
border: 0;
@@ -974,7 +974,7 @@ initialize {
font-variant: tabular-nums;
line-height: 1;
vertical-align: middle;
background-color: #ff0000;
background-color: #e00;
color: white;
font-size: .875rem;
border: 0;
@@ -1016,7 +1016,7 @@ initialize {
font-variant: tabular-nums;
line-height: 1;
vertical-align: middle;
background-color: #ff0000;
background-color: #e00;
color: white;
font-size: .875rem;
border: 0;
@@ -1303,7 +1303,7 @@ initialize {
font-variant: tabular-nums;
line-height: 1;
vertical-align: middle;
background-color: #ff0000;
background-color: #e00;
color: white;
font-size: .875rem;
border: 0;
@@ -1345,7 +1345,7 @@ initialize {
font-variant: tabular-nums;
line-height: 1;
vertical-align: middle;
background-color: #ff0000;
background-color: #e00;
color: white;
font-size: .875rem;
border: 0;

View File

@@ -0,0 +1,85 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Breadcrumbs should redefined all separators 1`] = `
"<nav class=\\"\\"><span class=\\"breadcrums-item \\">test-1</span><div class=\\"separator \\">*<style>
.separator {
display: inline-flex;
margin: 0 8px;
user-select: none;
pointer-events: none;
align-items: center;
}
</style></div><span class=\\"breadcrums-item \\">test-2</span><style>
nav {
margin: 0;
padding: 0;
line-height: inherit;
color: #888;
font-size: 1rem;
box-sizing: border-box;
display: flex;
align-items: center;
}
nav :global(.link:hover) {
color: rgba(0, 112, 243, 0.85);
}
nav > :global(span:last-of-type) {
color: #444;
}
nav > :global(.separator:last-child) {
display: none;
}
nav :global(svg) {
width: 1em;
height: 1em;
margin: 0 4px;
}
nav :global(.breadcrums-item) {
display: inline-flex;
align-items: center;
}
</style></nav>"
`;
exports[`Breadcrumbs should render correctly 1`] = `
"<nav class=\\"\\"><span class=\\"breadcrums-item \\">test-1</span><style>
nav {
margin: 0;
padding: 0;
line-height: inherit;
color: #888;
font-size: 1rem;
box-sizing: border-box;
display: flex;
align-items: center;
}
nav :global(.link:hover) {
color: rgba(0, 112, 243, 0.85);
}
nav > :global(span:last-of-type) {
color: #444;
}
nav > :global(.separator:last-child) {
display: none;
}
nav :global(svg) {
width: 1em;
height: 1em;
margin: 0 4px;
}
nav :global(.breadcrums-item) {
display: inline-flex;
align-items: center;
}
</style></nav>"
`;

View File

@@ -0,0 +1,75 @@
import React from 'react'
import { mount } from 'enzyme'
import { Breadcrumbs } from 'components'
describe('Breadcrumbs', () => {
it('should render correctly', () => {
const wrapper = mount(
<Breadcrumbs>
<Breadcrumbs.Item>test-1</Breadcrumbs.Item>
</Breadcrumbs>,
)
expect(wrapper.html()).toMatchSnapshot()
expect(() => wrapper.unmount()).not.toThrow()
})
it('should redefined all separators', () => {
const wrapper = mount(
<Breadcrumbs separator="*">
<Breadcrumbs.Item>test-1</Breadcrumbs.Item>
<Breadcrumbs.Item>test-2</Breadcrumbs.Item>
</Breadcrumbs>,
)
expect(wrapper.html()).toMatchSnapshot()
expect(wrapper.html()).toContain('*')
expect(() => wrapper.unmount()).not.toThrow()
})
it('the specified separator should be redefined', () => {
const wrapper = mount(
<Breadcrumbs separator="*">
<Breadcrumbs.Item>test-1</Breadcrumbs.Item>
<Breadcrumbs.Separator>%</Breadcrumbs.Separator>
<Breadcrumbs.Item>test-2</Breadcrumbs.Item>
</Breadcrumbs>,
)
expect(wrapper.html()).toContain('%')
})
it('should render string when href missing', () => {
let wrapper = mount(
<Breadcrumbs>
<Breadcrumbs.Item>test-1</Breadcrumbs.Item>
</Breadcrumbs>,
)
let dom = wrapper.find('.breadcrums-item').at(0).getDOMNode()
expect(dom.tagName).toEqual('SPAN')
wrapper = mount(
<Breadcrumbs>
<Breadcrumbs.Item href="">test-1</Breadcrumbs.Item>
</Breadcrumbs>,
)
dom = wrapper.find('.breadcrums-item').at(0).getDOMNode()
expect(dom.tagName).toEqual('A')
wrapper = mount(
<Breadcrumbs>
<Breadcrumbs.Item nextLink>test-1</Breadcrumbs.Item>
</Breadcrumbs>,
)
dom = wrapper.find('.breadcrums-item').at(0).getDOMNode()
expect(dom.tagName).toEqual('A')
})
it('should trigger click event', () => {
const handler = jest.fn()
const wrapper = mount(
<Breadcrumbs>
<Breadcrumbs.Item onClick={handler}>test-1</Breadcrumbs.Item>
</Breadcrumbs>,
)
wrapper.find('.breadcrums-item').at(0).simulate('click')
expect(handler).toHaveBeenCalled()
})
})

View File

@@ -0,0 +1,61 @@
import Link from '../link'
import { Props as LinkBasicProps } from '../link/link'
import React, { useMemo } from 'react'
import withDefaults from '../utils/with-defaults'
import { pickChild } from '../utils/collections'
import BreadcrumbsSeparator from './breadcrumbs-separator'
interface Props {
href?: string
nextLink?: boolean
onClick?: (event: React.MouseEvent) => void
className?: string
}
const defaultProps = {
nextLink: false,
className: '',
}
type NativeAttrs = Omit<React.AnchorHTMLAttributes<any>, keyof Props>
type NativeLinkAttrs = Omit<NativeAttrs, keyof LinkBasicProps>
export type BreadcrumbsProps = Props & typeof defaultProps & NativeLinkAttrs
const BreadcrumbsItem = React.forwardRef<
HTMLAnchorElement,
React.PropsWithChildren<BreadcrumbsProps>
>(
(
{ href, nextLink, onClick, children, className, ...props },
ref: React.Ref<HTMLAnchorElement>,
) => {
const isLink = useMemo(() => href !== undefined || nextLink, [href, nextLink])
const [withoutSepChildren] = pickChild(children, BreadcrumbsSeparator)
const clickHandler = (event: React.MouseEvent) => {
onClick && onClick(event)
}
if (!isLink) {
return (
<span className={`breadcrums-item ${className}`} onClick={clickHandler}>
{withoutSepChildren}
</span>
)
}
return (
<Link
className={`breadcrums-item ${className}`}
href={href}
onClick={clickHandler}
ref={ref}
{...props}>
{withoutSepChildren}
</Link>
)
},
)
const MemoBreadcrumbsItem = React.memo(BreadcrumbsItem)
export default withDefaults(MemoBreadcrumbsItem, defaultProps)

View File

@@ -0,0 +1,37 @@
import React from 'react'
import withDefaults from '../utils/with-defaults'
interface Props {
className?: string
}
const defaultProps = {
className: '',
}
type NativeAttrs = Omit<React.HTMLAttributes<any>, keyof Props>
export type BreadcrumbsProps = Props & typeof defaultProps & NativeAttrs
const BreadcrumbsSeparator: React.FC<React.PropsWithChildren<BreadcrumbsProps>> = ({
children,
className,
}) => {
return (
<div className={`separator ${className}`}>
{children}
<style jsx>{`
.separator {
display: inline-flex;
margin: 0 8px;
user-select: none;
pointer-events: none;
align-items: center;
}
`}</style>
</div>
)
}
const MemoBreadcrumbsSeparator = React.memo(BreadcrumbsSeparator)
export default withDefaults(MemoBreadcrumbsSeparator, defaultProps)

View File

@@ -0,0 +1,114 @@
import React, { ReactNode, useMemo } from 'react'
import useTheme from '../styles/use-theme'
import BreadcrumbsItem from './breadcrumbs-item'
import BreadcrumbsSeparator from './breadcrumbs-separator'
import { addColorAlpha } from '../utils/color'
import { NormalSizes } from '../utils/prop-types'
interface Props {
size: NormalSizes
separator?: string | ReactNode
className?: string
}
const defaultProps = {
size: 'medium' as NormalSizes,
separator: '/',
className: '',
}
type NativeAttrs = Omit<React.HTMLAttributes<any>, keyof Props>
export type BreadcrumbsProps = Props & typeof defaultProps & NativeAttrs
const getSize = (size: NormalSizes) => {
const sizes: { [key in NormalSizes]: string } = {
mini: '.75rem',
small: '.875rem',
medium: '1rem',
large: '1.125rem',
}
return sizes[size]
}
const Breadcrumbs: React.FC<React.PropsWithChildren<BreadcrumbsProps>> = ({
size,
separator,
children,
className,
}) => {
const theme = useTheme()
const fontSize = useMemo(() => getSize(size), [size])
const hoverColor = useMemo(() => {
return addColorAlpha(theme.palette.link, 0.85)
}, [theme.palette.link])
const childrenArray = React.Children.toArray(children)
const withSeparatorChildren = childrenArray.map((item, index) => {
if (!React.isValidElement(item)) return item
const last = childrenArray[index - 1]
const lastIsSeparator = React.isValidElement(last) && last.type === BreadcrumbsSeparator
const currentIsSeparator = item.type === BreadcrumbsSeparator
if (!lastIsSeparator && !currentIsSeparator && index > 0) {
return (
<React.Fragment key={index}>
<BreadcrumbsSeparator>{separator}</BreadcrumbsSeparator>
{item}
</React.Fragment>
)
}
return item
})
return (
<nav className={className}>
{withSeparatorChildren}
<style jsx>{`
nav {
margin: 0;
padding: 0;
line-height: inherit;
color: ${theme.palette.accents_4};
font-size: ${fontSize};
box-sizing: border-box;
display: flex;
align-items: center;
}
nav :global(.link:hover) {
color: ${hoverColor};
}
nav > :global(span:last-of-type) {
color: ${theme.palette.accents_6};
}
nav > :global(.separator:last-child) {
display: none;
}
nav :global(svg) {
width: 1em;
height: 1em;
margin: 0 4px;
}
nav :global(.breadcrums-item) {
display: inline-flex;
align-items: center;
}
`}</style>
</nav>
)
}
type MemoBreadcrumbsComponent<P = {}> = React.NamedExoticComponent<P> & {
Item: typeof BreadcrumbsItem
Separator: typeof BreadcrumbsSeparator
}
type ComponentProps = Partial<typeof defaultProps> &
Omit<Props, keyof typeof defaultProps> &
NativeAttrs
Breadcrumbs.defaultProps = defaultProps
export default React.memo(Breadcrumbs) as MemoBreadcrumbsComponent<ComponentProps>

View File

@@ -0,0 +1,8 @@
import Breadcrumbs from './breadcrumbs'
import BreadcrumbsItem from './breadcrumbs-item'
import BreadcrumbsSeparator from './breadcrumbs-separator'
Breadcrumbs.Item = BreadcrumbsItem
Breadcrumbs.Separator = BreadcrumbsSeparator
export default Breadcrumbs

View File

@@ -301,8 +301,8 @@ initialize {
}
button:hover {
border-color: #f49b0b;
background-color: #f49b0b;
border-color: #ab570a;
background-color: #ab570a;
}
",
"next": null,
@@ -353,8 +353,8 @@ initialize {
}
button:hover {
border-color: #f49b0b;
background-color: #f49b0b;
border-color: #ab570a;
background-color: #ab570a;
}
",
"next": null,
@@ -407,7 +407,7 @@ initialize {
box-sizing: border-box;
margin: 0;
border: none;
background-color: #ff0000;
background-color: #e00;
color: #fff;
width: 100%;
height: 2.5rem;
@@ -417,8 +417,8 @@ initialize {
}
button:hover {
border-color: #c00;
background-color: #c00;
border-color: #c50000;
background-color: #c50000;
}
",
"next": null,
@@ -459,7 +459,7 @@ initialize {
box-sizing: border-box;
margin: 0;
border: none;
background-color: #ff0000;
background-color: #e00;
color: #fff;
width: 100%;
height: 2.5rem;
@@ -469,8 +469,8 @@ initialize {
}
button:hover {
border-color: #c00;
background-color: #c00;
border-color: #c50000;
background-color: #c50000;
}
",
"next": null,
@@ -544,7 +544,7 @@ initialize {
box-sizing: border-box;
margin: 0;
border: none;
background-color: #ff0000;
background-color: #e00;
color: #fff;
width: 100%;
height: 2.5rem;
@@ -554,8 +554,8 @@ initialize {
}
button:hover {
border-color: #c00;
background-color: #c00;
border-color: #c50000;
background-color: #c50000;
}
",
"next": null,
@@ -596,7 +596,7 @@ initialize {
box-sizing: border-box;
margin: 0;
border: none;
background-color: #ff0000;
background-color: #e00;
color: #fff;
width: 100%;
height: 2.5rem;
@@ -606,8 +606,8 @@ initialize {
}
button:hover {
border-color: #c00;
background-color: #c00;
border-color: #c50000;
background-color: #c50000;
}
",
"next": null,
@@ -672,8 +672,8 @@ initialize {
}
button:hover {
border-color: #f49b0b;
background-color: #f49b0b;
border-color: #ab570a;
background-color: #ab570a;
}
",
"next": null,
@@ -724,8 +724,8 @@ initialize {
}
button:hover {
border-color: #f49b0b;
background-color: #f49b0b;
border-color: #ab570a;
background-color: #ab570a;
}
",
"next": null,
@@ -832,8 +832,8 @@ initialize {
}
button:hover {
border-color: #f49b0b;
background-color: #f49b0b;
border-color: #ab570a;
background-color: #ab570a;
}
",
"next": null,
@@ -884,8 +884,8 @@ initialize {
}
button:hover {
border-color: #f49b0b;
background-color: #f49b0b;
border-color: #ab570a;
background-color: #ab570a;
}
",
"next": null,
@@ -938,7 +938,7 @@ initialize {
box-sizing: border-box;
margin: 0;
border: none;
background-color: #ff0000;
background-color: #e00;
color: #fff;
width: 100%;
height: 2.5rem;
@@ -948,8 +948,8 @@ initialize {
}
button:hover {
border-color: #c00;
background-color: #c00;
border-color: #c50000;
background-color: #c50000;
}
",
"next": null,
@@ -990,7 +990,7 @@ initialize {
box-sizing: border-box;
margin: 0;
border: none;
background-color: #ff0000;
background-color: #e00;
color: #fff;
width: 100%;
height: 2.5rem;
@@ -1000,8 +1000,8 @@ initialize {
}
button:hover {
border-color: #c00;
background-color: #c00;
border-color: #c50000;
background-color: #c50000;
}
",
"next": null,
@@ -1075,7 +1075,7 @@ initialize {
box-sizing: border-box;
margin: 0;
border: none;
background-color: #ff0000;
background-color: #e00;
color: #fff;
width: 100%;
height: 2.5rem;
@@ -1085,8 +1085,8 @@ initialize {
}
button:hover {
border-color: #c00;
background-color: #c00;
border-color: #c50000;
background-color: #c50000;
}
",
"next": null,
@@ -1127,7 +1127,7 @@ initialize {
box-sizing: border-box;
margin: 0;
border: none;
background-color: #ff0000;
background-color: #e00;
color: #fff;
width: 100%;
height: 2.5rem;
@@ -1137,8 +1137,8 @@ initialize {
}
button:hover {
border-color: #c00;
background-color: #c00;
border-color: #c50000;
background-color: #c50000;
}
",
"next": null,
@@ -1203,8 +1203,8 @@ initialize {
}
button:hover {
border-color: #f49b0b;
background-color: #f49b0b;
border-color: #ab570a;
background-color: #ab570a;
}
",
"next": null,
@@ -1255,8 +1255,8 @@ initialize {
}
button:hover {
border-color: #f49b0b;
background-color: #f49b0b;
border-color: #ab570a;
background-color: #ab570a;
}
",
"next": null,
@@ -1733,8 +1733,8 @@ initialize {
}
button:hover {
border-color: #f49b0b;
background-color: #f49b0b;
border-color: #ab570a;
background-color: #ab570a;
}
",
"next": null,
@@ -1785,8 +1785,8 @@ initialize {
}
button:hover {
border-color: #f49b0b;
background-color: #f49b0b;
border-color: #ab570a;
background-color: #ab570a;
}
",
"next": null,
@@ -1839,7 +1839,7 @@ initialize {
box-sizing: border-box;
margin: 0;
border: none;
background-color: #ff0000;
background-color: #e00;
color: #fff;
width: 100%;
height: 2.5rem;
@@ -1849,8 +1849,8 @@ initialize {
}
button:hover {
border-color: #c00;
background-color: #c00;
border-color: #c50000;
background-color: #c50000;
}
",
"next": null,
@@ -1891,7 +1891,7 @@ initialize {
box-sizing: border-box;
margin: 0;
border: none;
background-color: #ff0000;
background-color: #e00;
color: #fff;
width: 100%;
height: 2.5rem;
@@ -1901,8 +1901,8 @@ initialize {
}
button:hover {
border-color: #c00;
background-color: #c00;
border-color: #c50000;
background-color: #c50000;
}
",
"next": null,
@@ -1976,7 +1976,7 @@ initialize {
box-sizing: border-box;
margin: 0;
border: none;
background-color: #ff0000;
background-color: #e00;
color: #fff;
width: 100%;
height: 2.5rem;
@@ -1986,8 +1986,8 @@ initialize {
}
button:hover {
border-color: #c00;
background-color: #c00;
border-color: #c50000;
background-color: #c50000;
}
",
"next": null,
@@ -2028,7 +2028,7 @@ initialize {
box-sizing: border-box;
margin: 0;
border: none;
background-color: #ff0000;
background-color: #e00;
color: #fff;
width: 100%;
height: 2.5rem;
@@ -2038,8 +2038,8 @@ initialize {
}
button:hover {
border-color: #c00;
background-color: #c00;
border-color: #c50000;
background-color: #c50000;
}
",
"next": null,
@@ -2104,8 +2104,8 @@ initialize {
}
button:hover {
border-color: #f49b0b;
background-color: #f49b0b;
border-color: #ab570a;
background-color: #ab570a;
}
",
"next": null,
@@ -2156,8 +2156,8 @@ initialize {
}
button:hover {
border-color: #f49b0b;
background-color: #f49b0b;
border-color: #ab570a;
background-color: #ab570a;
}
",
"next": null,
@@ -2264,8 +2264,8 @@ initialize {
}
button:hover {
border-color: #f49b0b;
background-color: #f49b0b;
border-color: #ab570a;
background-color: #ab570a;
}
",
"next": null,
@@ -2316,8 +2316,8 @@ initialize {
}
button:hover {
border-color: #f49b0b;
background-color: #f49b0b;
border-color: #ab570a;
background-color: #ab570a;
}
",
"next": null,
@@ -2370,7 +2370,7 @@ initialize {
box-sizing: border-box;
margin: 0;
border: none;
background-color: #ff0000;
background-color: #e00;
color: #fff;
width: 100%;
height: 2.5rem;
@@ -2380,8 +2380,8 @@ initialize {
}
button:hover {
border-color: #c00;
background-color: #c00;
border-color: #c50000;
background-color: #c50000;
}
",
"next": null,
@@ -2422,7 +2422,7 @@ initialize {
box-sizing: border-box;
margin: 0;
border: none;
background-color: #ff0000;
background-color: #e00;
color: #fff;
width: 100%;
height: 2.5rem;
@@ -2432,8 +2432,8 @@ initialize {
}
button:hover {
border-color: #c00;
background-color: #c00;
border-color: #c50000;
background-color: #c50000;
}
",
"next": null,
@@ -2507,7 +2507,7 @@ initialize {
box-sizing: border-box;
margin: 0;
border: none;
background-color: #ff0000;
background-color: #e00;
color: #fff;
width: 100%;
height: 2.5rem;
@@ -2517,8 +2517,8 @@ initialize {
}
button:hover {
border-color: #c00;
background-color: #c00;
border-color: #c50000;
background-color: #c50000;
}
",
"next": null,
@@ -2559,7 +2559,7 @@ initialize {
box-sizing: border-box;
margin: 0;
border: none;
background-color: #ff0000;
background-color: #e00;
color: #fff;
width: 100%;
height: 2.5rem;
@@ -2569,8 +2569,8 @@ initialize {
}
button:hover {
border-color: #c00;
background-color: #c00;
border-color: #c50000;
background-color: #c50000;
}
",
"next": null,
@@ -2635,8 +2635,8 @@ initialize {
}
button:hover {
border-color: #f49b0b;
background-color: #f49b0b;
border-color: #ab570a;
background-color: #ab570a;
}
",
"next": null,
@@ -2687,8 +2687,8 @@ initialize {
}
button:hover {
border-color: #f49b0b;
background-color: #f49b0b;
border-color: #ab570a;
background-color: #ab570a;
}
",
"next": null,
@@ -3356,8 +3356,8 @@ initialize {
}
button:hover {
border-color: #f49b0b;
background-color: #f49b0b;
border-color: #ab570a;
background-color: #ab570a;
}
",
"next": null,
@@ -3408,8 +3408,8 @@ initialize {
}
button:hover {
border-color: #f49b0b;
background-color: #f49b0b;
border-color: #ab570a;
background-color: #ab570a;
}
",
"next": null,
@@ -3462,7 +3462,7 @@ initialize {
box-sizing: border-box;
margin: 0;
border: none;
background-color: #ff0000;
background-color: #e00;
color: #fff;
width: 100%;
height: 2.5rem;
@@ -3472,8 +3472,8 @@ initialize {
}
button:hover {
border-color: #c00;
background-color: #c00;
border-color: #c50000;
background-color: #c50000;
}
",
"next": null,
@@ -3514,7 +3514,7 @@ initialize {
box-sizing: border-box;
margin: 0;
border: none;
background-color: #ff0000;
background-color: #e00;
color: #fff;
width: 100%;
height: 2.5rem;
@@ -3524,8 +3524,8 @@ initialize {
}
button:hover {
border-color: #c00;
background-color: #c00;
border-color: #c50000;
background-color: #c50000;
}
",
"next": null,
@@ -3599,7 +3599,7 @@ initialize {
box-sizing: border-box;
margin: 0;
border: none;
background-color: #ff0000;
background-color: #e00;
color: #fff;
width: 100%;
height: 2.5rem;
@@ -3609,8 +3609,8 @@ initialize {
}
button:hover {
border-color: #c00;
background-color: #c00;
border-color: #c50000;
background-color: #c50000;
}
",
"next": null,
@@ -3651,7 +3651,7 @@ initialize {
box-sizing: border-box;
margin: 0;
border: none;
background-color: #ff0000;
background-color: #e00;
color: #fff;
width: 100%;
height: 2.5rem;
@@ -3661,8 +3661,8 @@ initialize {
}
button:hover {
border-color: #c00;
background-color: #c00;
border-color: #c50000;
background-color: #c50000;
}
",
"next": null,
@@ -3727,8 +3727,8 @@ initialize {
}
button:hover {
border-color: #f49b0b;
background-color: #f49b0b;
border-color: #ab570a;
background-color: #ab570a;
}
",
"next": null,
@@ -3779,8 +3779,8 @@ initialize {
}
button:hover {
border-color: #f49b0b;
background-color: #f49b0b;
border-color: #ab570a;
background-color: #ab570a;
}
",
"next": null,
@@ -3887,8 +3887,8 @@ initialize {
}
button:hover {
border-color: #f49b0b;
background-color: #f49b0b;
border-color: #ab570a;
background-color: #ab570a;
}
",
"next": null,
@@ -3939,8 +3939,8 @@ initialize {
}
button:hover {
border-color: #f49b0b;
background-color: #f49b0b;
border-color: #ab570a;
background-color: #ab570a;
}
",
"next": null,
@@ -3993,7 +3993,7 @@ initialize {
box-sizing: border-box;
margin: 0;
border: none;
background-color: #ff0000;
background-color: #e00;
color: #fff;
width: 100%;
height: 2.5rem;
@@ -4003,8 +4003,8 @@ initialize {
}
button:hover {
border-color: #c00;
background-color: #c00;
border-color: #c50000;
background-color: #c50000;
}
",
"next": null,
@@ -4045,7 +4045,7 @@ initialize {
box-sizing: border-box;
margin: 0;
border: none;
background-color: #ff0000;
background-color: #e00;
color: #fff;
width: 100%;
height: 2.5rem;
@@ -4055,8 +4055,8 @@ initialize {
}
button:hover {
border-color: #c00;
background-color: #c00;
border-color: #c50000;
background-color: #c50000;
}
",
"next": null,
@@ -4130,7 +4130,7 @@ initialize {
box-sizing: border-box;
margin: 0;
border: none;
background-color: #ff0000;
background-color: #e00;
color: #fff;
width: 100%;
height: 2.5rem;
@@ -4140,8 +4140,8 @@ initialize {
}
button:hover {
border-color: #c00;
background-color: #c00;
border-color: #c50000;
background-color: #c50000;
}
",
"next": null,
@@ -4182,7 +4182,7 @@ initialize {
box-sizing: border-box;
margin: 0;
border: none;
background-color: #ff0000;
background-color: #e00;
color: #fff;
width: 100%;
height: 2.5rem;
@@ -4192,8 +4192,8 @@ initialize {
}
button:hover {
border-color: #c00;
background-color: #c00;
border-color: #c50000;
background-color: #c50000;
}
",
"next": null,
@@ -4258,8 +4258,8 @@ initialize {
}
button:hover {
border-color: #f49b0b;
background-color: #f49b0b;
border-color: #ab570a;
background-color: #ab570a;
}
",
"next": null,
@@ -4310,8 +4310,8 @@ initialize {
}
button:hover {
border-color: #f49b0b;
background-color: #f49b0b;
border-color: #ab570a;
background-color: #ab570a;
}
",
"next": null,

View File

@@ -176,7 +176,7 @@ initialize {
span {
width: 20%;
background-color: #79ffe1;
background-color: #50e3c2;
height: 100%;
margin: 0;
padding: 0;
@@ -219,7 +219,7 @@ initialize {
span {
width: 20%;
background-color: #79ffe1;
background-color: #50e3c2;
height: 100%;
margin: 0;
padding: 0;
@@ -386,7 +386,7 @@ initialize {
span {
width: 66.67%;
background-color: #c00;
background-color: #c50000;
height: 100%;
margin: 0;
padding: 0;
@@ -429,7 +429,7 @@ initialize {
span {
width: 66.67%;
background-color: #c00;
background-color: #c50000;
height: 100%;
margin: 0;
padding: 0;
@@ -633,7 +633,7 @@ initialize {
span {
width: 66.67%;
background-color: #c00;
background-color: #c50000;
height: 100%;
margin: 0;
padding: 0;
@@ -676,7 +676,7 @@ initialize {
span {
width: 66.67%;
background-color: #c00;
background-color: #c50000;
height: 100%;
margin: 0;
padding: 0;
@@ -752,7 +752,7 @@ initialize {
span {
width: 20%;
background-color: #79ffe1;
background-color: #50e3c2;
height: 100%;
margin: 0;
padding: 0;
@@ -795,7 +795,7 @@ initialize {
span {
width: 20%;
background-color: #79ffe1;
background-color: #50e3c2;
height: 100%;
margin: 0;
padding: 0;
@@ -880,7 +880,7 @@ initialize {
span {
width: 66.67%;
background-color: #c00;
background-color: #c50000;
height: 100%;
margin: 0;
padding: 0;
@@ -923,7 +923,7 @@ initialize {
span {
width: 66.67%;
background-color: #c00;
background-color: #c50000;
height: 100%;
margin: 0;
padding: 0;
@@ -1094,7 +1094,7 @@ initialize {
span {
width: 20%;
background-color: #79ffe1;
background-color: #50e3c2;
height: 100%;
margin: 0;
padding: 0;
@@ -1137,7 +1137,7 @@ initialize {
span {
width: 20%;
background-color: #79ffe1;
background-color: #50e3c2;
height: 100%;
margin: 0;
padding: 0;

View File

@@ -213,7 +213,7 @@ initialize {
pre code {
color: #000;
font-size: 0.75rem;
font-size: 0.8125rem;
line-height: 1.25rem;
white-space: pre;
}
@@ -547,7 +547,7 @@ initialize {
pre code {
color: #fff;
font-size: 0.75rem;
font-size: 0.8125rem;
line-height: 1.25rem;
white-space: pre;
}

View File

@@ -216,7 +216,7 @@ const CssBaseline: React.FC<React.PropsWithChildren<{}>> = ({ children }) => {
pre code {
color: ${theme.palette.foreground};
font-size: 0.75rem;
font-size: 0.8125rem;
line-height: 1.25rem;
white-space: pre;
}

View File

@@ -721,7 +721,7 @@ initialize {
width: 0.625rem;
height: 0.625rem;
border-radius: 50%;
background-color: #ff0000;
background-color: #e00;
user-select: none;
}
@@ -787,7 +787,7 @@ initialize {
width: 0.625rem;
height: 0.625rem;
border-radius: 50%;
background-color: #ff0000;
background-color: #e00;
user-select: none;
}
@@ -853,7 +853,7 @@ initialize {
width: 0.625rem;
height: 0.625rem;
border-radius: 50%;
background-color: #ff0000;
background-color: #e00;
user-select: none;
}
@@ -1443,7 +1443,7 @@ initialize {
width: 0.625rem;
height: 0.625rem;
border-radius: 50%;
background-color: #ff0000;
background-color: #e00;
user-select: none;
}
@@ -1509,7 +1509,7 @@ initialize {
width: 0.625rem;
height: 0.625rem;
border-radius: 50%;
background-color: #ff0000;
background-color: #e00;
user-select: none;
}
@@ -1575,7 +1575,7 @@ initialize {
width: 0.625rem;
height: 0.625rem;
border-radius: 50%;
background-color: #ff0000;
background-color: #e00;
user-select: none;
}
@@ -2165,7 +2165,7 @@ initialize {
width: 0.625rem;
height: 0.625rem;
border-radius: 50%;
background-color: #ff0000;
background-color: #e00;
user-select: none;
}
@@ -2231,7 +2231,7 @@ initialize {
width: 0.625rem;
height: 0.625rem;
border-radius: 50%;
background-color: #ff0000;
background-color: #e00;
user-select: none;
}
@@ -2297,7 +2297,7 @@ initialize {
width: 0.625rem;
height: 0.625rem;
border-radius: 50%;
background-color: #ff0000;
background-color: #e00;
user-select: none;
}
@@ -2887,7 +2887,7 @@ initialize {
width: 0.625rem;
height: 0.625rem;
border-radius: 50%;
background-color: #ff0000;
background-color: #e00;
user-select: none;
}
@@ -2953,7 +2953,7 @@ initialize {
width: 0.625rem;
height: 0.625rem;
border-radius: 50%;
background-color: #ff0000;
background-color: #e00;
user-select: none;
}
@@ -3019,7 +3019,7 @@ initialize {
width: 0.625rem;
height: 0.625rem;
border-radius: 50%;
background-color: #ff0000;
background-color: #e00;
user-select: none;
}

View File

@@ -25,6 +25,7 @@ exports[`Grid all breakpoint values should be supported 1`] = `
flex-grow: 0;
max-width: 4.166666666666667%;
flex-basis: 4.166666666666667%;
}
@media only screen and (max-width: 650px) {
@@ -32,6 +33,7 @@ exports[`Grid all breakpoint values should be supported 1`] = `
flex-grow: 0;
max-width: 4.166666666666667%;
flex-basis: 4.166666666666667%;
}
}
@@ -40,6 +42,7 @@ exports[`Grid all breakpoint values should be supported 1`] = `
flex-grow: 0;
max-width: 8.333333333333334%;
flex-basis: 8.333333333333334%;
}
}
@@ -48,6 +51,7 @@ exports[`Grid all breakpoint values should be supported 1`] = `
flex-grow: 0;
max-width: 12.5%;
flex-basis: 12.5%;
}
}
@@ -56,6 +60,7 @@ exports[`Grid all breakpoint values should be supported 1`] = `
flex-grow: 0;
max-width: 16.666666666666668%;
flex-basis: 16.666666666666668%;
}
}
@@ -64,6 +69,7 @@ exports[`Grid all breakpoint values should be supported 1`] = `
flex-grow: 0;
max-width: 20.833333333333336%;
flex-basis: 20.833333333333336%;
}
}
</style></div><style>
@@ -90,6 +96,7 @@ exports[`Grid all breakpoint values should be supported 1`] = `
flex-grow: 0;
max-width: 4.166666666666667%;
flex-basis: 4.166666666666667%;
}
@media only screen and (max-width: 650px) {
@@ -97,6 +104,7 @@ exports[`Grid all breakpoint values should be supported 1`] = `
flex-grow: 0;
max-width: 4.166666666666667%;
flex-basis: 4.166666666666667%;
}
}
@@ -105,6 +113,7 @@ exports[`Grid all breakpoint values should be supported 1`] = `
flex-grow: 0;
max-width: 8.333333333333334%;
flex-basis: 8.333333333333334%;
}
}
@@ -113,6 +122,7 @@ exports[`Grid all breakpoint values should be supported 1`] = `
flex-grow: 0;
max-width: 12.5%;
flex-basis: 12.5%;
}
}
@@ -121,6 +131,7 @@ exports[`Grid all breakpoint values should be supported 1`] = `
flex-grow: 0;
max-width: 16.666666666666668%;
flex-basis: 16.666666666666668%;
}
}
@@ -129,6 +140,7 @@ exports[`Grid all breakpoint values should be supported 1`] = `
flex-grow: 0;
max-width: 20.833333333333336%;
flex-basis: 20.833333333333336%;
}
}
</style></div>"
@@ -159,6 +171,7 @@ exports[`Grid css value should be passed through 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
@media only screen and (max-width: 650px) {
@@ -166,6 +179,7 @@ exports[`Grid css value should be passed through 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -174,6 +188,7 @@ exports[`Grid css value should be passed through 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -182,6 +197,7 @@ exports[`Grid css value should be passed through 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -190,6 +206,7 @@ exports[`Grid css value should be passed through 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -198,6 +215,7 @@ exports[`Grid css value should be passed through 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
</style></div><style>
@@ -224,6 +242,7 @@ exports[`Grid css value should be passed through 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
@media only screen and (max-width: 650px) {
@@ -231,6 +250,7 @@ exports[`Grid css value should be passed through 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -239,6 +259,7 @@ exports[`Grid css value should be passed through 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -247,6 +268,7 @@ exports[`Grid css value should be passed through 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -255,6 +277,7 @@ exports[`Grid css value should be passed through 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -263,6 +286,7 @@ exports[`Grid css value should be passed through 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
</style></div>"
@@ -293,6 +317,7 @@ exports[`Grid decimal spacing should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
@media only screen and (max-width: 650px) {
@@ -300,6 +325,7 @@ exports[`Grid decimal spacing should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -308,6 +334,7 @@ exports[`Grid decimal spacing should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -316,6 +343,7 @@ exports[`Grid decimal spacing should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -324,6 +352,7 @@ exports[`Grid decimal spacing should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -332,6 +361,7 @@ exports[`Grid decimal spacing should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
</style></div><style>
@@ -358,6 +388,7 @@ exports[`Grid decimal spacing should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
@media only screen and (max-width: 650px) {
@@ -365,6 +396,7 @@ exports[`Grid decimal spacing should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -373,6 +405,7 @@ exports[`Grid decimal spacing should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -381,6 +414,7 @@ exports[`Grid decimal spacing should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -389,6 +423,7 @@ exports[`Grid decimal spacing should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -397,6 +432,7 @@ exports[`Grid decimal spacing should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
</style></div>"
@@ -427,6 +463,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
@media only screen and (max-width: 650px) {
@@ -434,6 +471,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -442,6 +480,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -450,6 +489,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -458,6 +498,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -466,6 +507,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
</style></div><div class=\\"item mock \\"><div class=\\"item mock \\">test<style>
@@ -492,6 +534,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
@media only screen and (max-width: 650px) {
@@ -499,6 +542,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -507,6 +551,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -515,6 +560,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -523,6 +569,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -531,6 +578,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
</style></div><div class=\\"item mock \\"><div class=\\"item mock \\">test<style>
@@ -557,6 +605,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
@media only screen and (max-width: 650px) {
@@ -564,6 +613,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -572,6 +622,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -580,6 +631,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -588,6 +640,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -596,6 +649,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
</style></div><div class=\\"item mock \\"><div class=\\"item mock \\">test<style>
@@ -622,6 +676,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
@media only screen and (max-width: 650px) {
@@ -629,6 +684,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -637,6 +693,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -645,6 +702,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -653,6 +711,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -661,6 +720,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
</style></div><style>
@@ -687,6 +747,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
@media only screen and (max-width: 650px) {
@@ -694,6 +755,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -702,6 +764,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -710,6 +773,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -718,6 +782,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -726,6 +791,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
</style></div>,<style>
@@ -752,6 +818,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
@media only screen and (max-width: 650px) {
@@ -759,6 +826,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -767,6 +835,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -775,6 +844,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -783,6 +853,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -791,6 +862,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
</style></div>,<style>
@@ -817,6 +889,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
@media only screen and (max-width: 650px) {
@@ -824,6 +897,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -832,6 +906,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -840,6 +915,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -848,6 +924,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -856,6 +933,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
</style></div>,<style>
@@ -882,6 +960,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
@media only screen and (max-width: 650px) {
@@ -889,6 +968,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -897,6 +977,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -905,6 +986,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -913,6 +995,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -921,6 +1004,7 @@ exports[`Grid nested components should be supported 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
</style></div>"
@@ -951,6 +1035,7 @@ exports[`Grid should render correctly 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
@media only screen and (max-width: 650px) {
@@ -958,6 +1043,7 @@ exports[`Grid should render correctly 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -966,6 +1052,7 @@ exports[`Grid should render correctly 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -974,6 +1061,7 @@ exports[`Grid should render correctly 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -982,6 +1070,7 @@ exports[`Grid should render correctly 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -990,6 +1079,7 @@ exports[`Grid should render correctly 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
</style></div><div class=\\"item mock \\">test<style>
@@ -1016,6 +1106,7 @@ exports[`Grid should render correctly 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
@media only screen and (max-width: 650px) {
@@ -1023,6 +1114,7 @@ exports[`Grid should render correctly 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -1031,6 +1123,7 @@ exports[`Grid should render correctly 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -1039,6 +1132,7 @@ exports[`Grid should render correctly 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -1047,6 +1141,7 @@ exports[`Grid should render correctly 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -1055,6 +1150,7 @@ exports[`Grid should render correctly 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
</style></div><style>
@@ -1081,6 +1177,7 @@ exports[`Grid should render correctly 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
@media only screen and (max-width: 650px) {
@@ -1088,6 +1185,7 @@ exports[`Grid should render correctly 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -1096,6 +1194,7 @@ exports[`Grid should render correctly 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -1104,6 +1203,7 @@ exports[`Grid should render correctly 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -1112,6 +1212,7 @@ exports[`Grid should render correctly 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -1120,6 +1221,7 @@ exports[`Grid should render correctly 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
</style></div>"
@@ -1150,6 +1252,7 @@ exports[`Grid should work correctly when size exceeds 1`] = `
flex-grow: 0;
max-width: 100%;
flex-basis: 100%;
}
@media only screen and (max-width: 650px) {
@@ -1157,6 +1260,7 @@ exports[`Grid should work correctly when size exceeds 1`] = `
flex-grow: 0;
max-width: 100%;
flex-basis: 100%;
}
}
@@ -1165,6 +1269,7 @@ exports[`Grid should work correctly when size exceeds 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -1173,6 +1278,7 @@ exports[`Grid should work correctly when size exceeds 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -1181,6 +1287,7 @@ exports[`Grid should work correctly when size exceeds 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -1189,6 +1296,7 @@ exports[`Grid should work correctly when size exceeds 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
</style></div><div class=\\"item xs mock \\">test<style>
@@ -1215,6 +1323,7 @@ exports[`Grid should work correctly when size exceeds 1`] = `
flex-grow: 0;
max-width: 0;
flex-basis: 0;
}
@media only screen and (max-width: 650px) {
@@ -1222,6 +1331,7 @@ exports[`Grid should work correctly when size exceeds 1`] = `
flex-grow: 0;
max-width: 0;
flex-basis: 0;
}
}
@@ -1230,6 +1340,7 @@ exports[`Grid should work correctly when size exceeds 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -1238,6 +1349,7 @@ exports[`Grid should work correctly when size exceeds 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -1246,6 +1358,7 @@ exports[`Grid should work correctly when size exceeds 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -1254,6 +1367,7 @@ exports[`Grid should work correctly when size exceeds 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
</style></div><style>
@@ -1280,6 +1394,7 @@ exports[`Grid should work correctly when size exceeds 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
@media only screen and (max-width: 650px) {
@@ -1287,6 +1402,7 @@ exports[`Grid should work correctly when size exceeds 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -1295,6 +1411,7 @@ exports[`Grid should work correctly when size exceeds 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -1303,6 +1420,7 @@ exports[`Grid should work correctly when size exceeds 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -1311,6 +1429,7 @@ exports[`Grid should work correctly when size exceeds 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -1319,6 +1438,7 @@ exports[`Grid should work correctly when size exceeds 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
</style></div>"

View File

@@ -111,4 +111,10 @@ describe('Grid', () => {
expect(wrapper.html()).toMatchSnapshot()
expect(() => wrapper.unmount()).not.toThrow()
})
it('Grid should be hidden when value is 0', () => {
let wrapper = mount(<Grid.Container xs={0} />)
expect(wrapper.find('.item').hasClass('xs')).toBeTruthy()
expect(wrapper.find('.item').html()).toContain('display: none')
})
})

View File

@@ -32,19 +32,23 @@ type ItemLayoutValue = {
grow: number
width: string
basis: string
display: string
}
const getItemLayout = (val: BreakpointsValue): ItemLayoutValue => {
const display = val === 0 ? 'display: none;' : ''
if (typeof val === 'number') {
const width = (100 / 24) * val
const ratio = width > 100 ? '100%' : width < 0 ? '0' : `${width}%`
return {
grow: 0,
display,
width: ratio,
basis: ratio,
}
}
return {
grow: 1,
display,
width: '100%',
basis: '0',
}
@@ -62,6 +66,7 @@ const GridBasicItem: React.FC<React.PropsWithChildren<GridBasicItemProps>> = ({
alignContent,
children,
className,
...props
}) => {
const theme = useTheme()
const classes = useMemo(() => {
@@ -77,11 +82,11 @@ const GridBasicItem: React.FC<React.PropsWithChildren<GridBasicItemProps>> = ({
xl,
}
const classString = Object.keys(aligns).reduce((pre, name) => {
if (Boolean(aligns[name]) && aligns[name] !== 0) return `${pre} ${name}`
if (aligns[name] !== undefined && aligns[name] !== false) return `${pre} ${name}`
return pre
}, '')
return classString.trim()
}, [justify, direction, alignItems, alignContent])
}, [justify, direction, alignItems, alignContent, xs, sm, md, lg, xl])
const layout = useMemo<
{
@@ -99,7 +104,7 @@ const GridBasicItem: React.FC<React.PropsWithChildren<GridBasicItemProps>> = ({
)
return (
<div className={`item ${classes} ${className}`}>
<div className={`item ${classes} ${className}`} {...props}>
{children}
<style jsx>{`
.item {
@@ -125,6 +130,7 @@ const GridBasicItem: React.FC<React.PropsWithChildren<GridBasicItemProps>> = ({
flex-grow: ${layout.xs.grow};
max-width: ${layout.xs.width};
flex-basis: ${layout.xs.basis};
${layout.xs.display}
}
@media only screen and (max-width: ${theme.breakpoints.xs.max}) {
@@ -132,6 +138,7 @@ const GridBasicItem: React.FC<React.PropsWithChildren<GridBasicItemProps>> = ({
flex-grow: ${layout.xs.grow};
max-width: ${layout.xs.width};
flex-basis: ${layout.xs.basis};
${layout.xs.display}
}
}
@@ -140,6 +147,7 @@ const GridBasicItem: React.FC<React.PropsWithChildren<GridBasicItemProps>> = ({
flex-grow: ${layout.sm.grow};
max-width: ${layout.sm.width};
flex-basis: ${layout.sm.basis};
${layout.sm.display}
}
}
@@ -148,6 +156,7 @@ const GridBasicItem: React.FC<React.PropsWithChildren<GridBasicItemProps>> = ({
flex-grow: ${layout.md.grow};
max-width: ${layout.md.width};
flex-basis: ${layout.md.basis};
${layout.md.display}
}
}
@@ -156,6 +165,7 @@ const GridBasicItem: React.FC<React.PropsWithChildren<GridBasicItemProps>> = ({
flex-grow: ${layout.lg.grow};
max-width: ${layout.lg.width};
flex-basis: ${layout.lg.basis};
${layout.lg.display}
}
}
@@ -164,6 +174,7 @@ const GridBasicItem: React.FC<React.PropsWithChildren<GridBasicItemProps>> = ({
flex-grow: ${layout.xl.grow};
max-width: ${layout.xl.width};
flex-basis: ${layout.xl.basis};
${layout.xl.display}
}
}
`}</style>

View File

@@ -59,4 +59,15 @@ describe('Image Browser', () => {
const wrapper = mount(<Image.Browser />)
expect(() => wrapper.unmount()).not.toThrow()
})
it('anchor props should be passed through', () => {
const anchorRel = 'noreferrer'
const wrapper = mount(
<Image.Browser url={link} anchorProps={{ rel: anchorRel }}>
<Image src={url} />
</Image.Browser>,
)
const rel = wrapper.find('a').getDOMNode().getAttribute('rel')
expect(anchorRel).toEqual(anchorRel)
})
})

View File

@@ -1,21 +1,26 @@
import React, { useMemo } from 'react'
import Link from '../link'
import { Props as LinkProps } from '../link/link'
import useTheme from '../styles/use-theme'
import withDefaults from '../utils/with-defaults'
import ImageBrowserHttpsIcon from './image-browser-https-icon'
import { getBrowserColors, BrowserColors } from './styles'
type AnchorProps = Omit<React.AnchorHTMLAttributes<any>, keyof LinkProps>
interface Props {
title?: string
url?: string
showFullLink?: boolean
invert?: boolean
anchorProps?: AnchorProps
className?: string
}
const defaultProps = {
className: '',
showFullLink: false,
anchorProps: {} as AnchorProps,
invert: false,
}
@@ -42,12 +47,17 @@ const getTitle = (title: string, colors: BrowserColors) => (
</div>
)
const getAddressInput = (url: string, showFullLink: boolean, colors: BrowserColors) => (
const getAddressInput = (
url: string,
showFullLink: boolean,
colors: BrowserColors,
anchorProps: AnchorProps,
) => (
<div className="address-input">
<span className="https">
<ImageBrowserHttpsIcon />
</span>
<Link href={url} title={url} target="_blank">
<Link href={url} title={url} target="_blank" {...anchorProps}>
{showFullLink ? url : getHostFromUrl(url)}
</Link>
<style jsx>{`
@@ -94,16 +104,16 @@ const getAddressInput = (url: string, showFullLink: boolean, colors: BrowserColo
const ImageBrowser = React.forwardRef<HTMLDivElement, React.PropsWithChildren<ImageBrowserProps>>(
(
{ url, title, children, showFullLink, invert, className, ...props },
{ url, title, children, showFullLink, invert, anchorProps, className, ...props },
ref: React.Ref<HTMLDivElement>,
) => {
const theme = useTheme()
const colors = useMemo(() => getBrowserColors(invert, theme.palette), [invert, theme.palette])
const input = useMemo(() => {
if (url) return getAddressInput(url, showFullLink, colors)
if (url) return getAddressInput(url, showFullLink, colors, anchorProps)
if (title) return getTitle(title, colors)
return null
}, [url, showFullLink, title, colors])
}, [url, showFullLink, title, colors, anchorProps])
return (
<div className={`bowser ${className}`} ref={ref} {...props}>

View File

@@ -58,3 +58,5 @@ export { default as User } from './user'
export { default as Page } from './page'
export { default as Grid } from './grid'
export { default as ButtonGroup } from './button-group'
export { default as Breadcrumbs } from './breadcrumbs'
export { default as Pagination } from './pagination'

View File

@@ -1,6 +1,10 @@
import React, { Dispatch, MutableRefObject, SetStateAction } from 'react'
import useCurrentState from '../utils/use-current-state'
export type BindingsChangeTarget =
| React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
| string
const useInput = (
initialValue: string,
): {
@@ -10,7 +14,7 @@ const useInput = (
reset: () => void
bindings: {
value: string
onChange: (event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => void
onChange: (event: BindingsChangeTarget) => void
}
} => {
const [state, setState, currentRef] = useCurrentState<string>(initialValue)
@@ -22,8 +26,12 @@ const useInput = (
reset: () => setState(initialValue),
bindings: {
value: state,
onChange: (event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
setState(event.target.value)
onChange: (event: BindingsChangeTarget) => {
if (typeof event === 'object' && event.target) {
setState(event.target.value)
} else {
setState(event as string)
}
},
},
}

View File

@@ -4,7 +4,7 @@ import useTheme from '../styles/use-theme'
import useWarning from '../utils/use-warning'
import LinkIcon from './icon'
interface Props {
export interface Props {
href?: string
color?: boolean
pure?: boolean

View File

@@ -760,7 +760,7 @@ exports[`Loading should work with different types 1`] = `
width: 4px;
height: 4px;
border-radius: 50%;
background-color: #ff0000;
background-color: #e00;
margin: 0 1px;
display: inline-block;
animation: loading-blink 1.4s infinite both;

View File

@@ -9,6 +9,7 @@ exports[`Modal customization should be supported 1`] = `
text-align: center;
margin: 0;
display: inline-flex;
flex-shrink: 0;
justify-content: center;
align-items: center;
word-break: break-word;
@@ -18,6 +19,7 @@ exports[`Modal customization should be supported 1`] = `
</style><style>
.wrapper {
max-width: 85vw;
max-height: 75vh;
width: 100px;
overflow: hidden;
display: flex;
@@ -66,6 +68,7 @@ exports[`Modal should render correctly 1`] = `
text-align: center;
margin: 0;
display: inline-flex;
flex-shrink: 0;
justify-content: center;
align-items: center;
word-break: break-word;
@@ -88,8 +91,9 @@ exports[`Modal should render correctly 1`] = `
}
</style><div class=\\"content \\"><p>Some content contained within the modal.</p></div><style>
.content {
margin: 0;
padding: 16pt 0 8pt 0;
margin: 0 -16pt;
padding: 16pt 16pt 8pt;
overflow-y: auto;
}
.content > :global(*:first-child) {
@@ -166,10 +170,12 @@ exports[`Modal should render correctly 1`] = `
div {
height: 3.625rem;
flex-shrink: 0;
}
</style><style>
.wrapper {
max-width: 85vw;
max-height: 75vh;
width: 26rem;
overflow: hidden;
display: flex;
@@ -215,7 +221,7 @@ exports[`Modal should render correctly 1`] = `
align-content: center;
align-items: center;
flex-direction: column;
justify-content: center;
justify-content: space-around;
height: 100vh;
width: 100vw;
overflow: auto;
@@ -232,8 +238,9 @@ exports[`Modal should render correctly 1`] = `
}
.offset {
height: 25vh;
height: 0;
opacity: 0;
display: flex;
background-color: transparent;
}

View File

@@ -28,6 +28,7 @@ const ModalActions: React.FC<React.PropsWithChildren<{}>> = ({ children, ...prop
div {
height: 3.625rem;
flex-shrink: 0;
}
`}</style>
</>

View File

@@ -23,8 +23,9 @@ const ModalContent: React.FC<ModalContentProps> = ({ className, children, ...pro
</div>
<style jsx>{`
.content {
margin: 0;
padding: ${theme.layout.gap} 0 ${theme.layout.gapHalf} 0;
margin: 0 -${theme.layout.gap};
padding: ${theme.layout.gap} ${theme.layout.gap} ${theme.layout.gapHalf};
overflow-y: auto;
}
.content > :global(*:first-child) {

View File

@@ -29,6 +29,7 @@ const ModalTitle: React.FC<ModalTitleProps> = ({ className, children, ...props }
text-align: center;
margin: 0;
display: inline-flex;
flex-shrink: 0;
justify-content: center;
align-items: center;
word-break: break-word;

View File

@@ -32,6 +32,7 @@ const ModalWrapper: React.FC<React.PropsWithChildren<ModalWrapperProps>> = ({
<style jsx>{`
.wrapper {
max-width: 85vw;
max-height: 75vh;
width: ${width};
overflow: hidden;
display: flex;

View File

@@ -80,7 +80,7 @@ const Modal: React.FC<React.PropsWithChildren<ModalProps>> = ({
if (!portal) return null
return createPortal(
<ModalContext.Provider value={modalConfig}>
<Backdrop onClick={closeFromBackdrop} visible={visible} offsetY={25}>
<Backdrop onClick={closeFromBackdrop} visible={visible}>
<ModalWrapper visible={visible} className={wrapClassName} width={wrapperWidth}>
{withoutActionsChildren}
{hasActions && <ModalActions>{ActionsChildren}</ModalActions>}

View File

@@ -182,8 +182,8 @@ exports[`Note should work with different types 1`] = `
padding: 8pt 16pt;
font-size: 14px;
line-height: 1.8;
border: 1px solid #ff0000;
color: #ff0000;
border: 1px solid #e00;
color: #e00;
background-color: #fff;
border-radius: 5px;
}

View File

@@ -0,0 +1,189 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Pagination should render correctly 1`] = `
"<nav><li><button class=\\" disabled\\">prev</button><style>
li {
margin-right: 6px;
display: inline-block;
}
button {
border: none;
display: inline-flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
text-transform: capitalize;
user-select: none;
white-space: nowrap;
text-align: center;
vertical-align: middle;
box-shadow: none;
outline: none;
height: var(--pagination-size);
min-width: var(--pagination-size);
font-size: inherit;
cursor: pointer;
color: #0070f3;
border-radius: 5px;
background-color: #fff;
transition: all linear 200ms 0ms;
}
button:hover {
background-color: rgba(0, 112, 243, 0.1);
}
.active {
font-weight: bold;
background-color: #0070f3;
color: #fff;
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.12);
}
.active:hover {
background-color: rgba(0, 112, 243, 0.8);
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);
}
.disabled {
color: #888;
cursor: not-allowed;
}
.disabled:hover {
background-color: #eaeaea;
}
button :global(svg) {
width: 1.3em;
height: 1.3em;
}
</style></li><li><button class=\\"active \\">1</button><style>
li {
margin-right: 6px;
display: inline-block;
}
button {
border: none;
display: inline-flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
text-transform: capitalize;
user-select: none;
white-space: nowrap;
text-align: center;
vertical-align: middle;
box-shadow: none;
outline: none;
height: var(--pagination-size);
min-width: var(--pagination-size);
font-size: inherit;
cursor: pointer;
color: #0070f3;
border-radius: 5px;
background-color: #fff;
transition: all linear 200ms 0ms;
}
button:hover {
background-color: rgba(0, 112, 243, 0.1);
}
.active {
font-weight: bold;
background-color: #0070f3;
color: #fff;
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.12);
}
.active:hover {
background-color: rgba(0, 112, 243, 0.8);
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);
}
.disabled {
color: #888;
cursor: not-allowed;
}
.disabled:hover {
background-color: #eaeaea;
}
button :global(svg) {
width: 1.3em;
height: 1.3em;
}
</style></li><li><button class=\\" disabled\\">next</button><style>
li {
margin-right: 6px;
display: inline-block;
}
button {
border: none;
display: inline-flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
text-transform: capitalize;
user-select: none;
white-space: nowrap;
text-align: center;
vertical-align: middle;
box-shadow: none;
outline: none;
height: var(--pagination-size);
min-width: var(--pagination-size);
font-size: inherit;
cursor: pointer;
color: #0070f3;
border-radius: 5px;
background-color: #fff;
transition: all linear 200ms 0ms;
}
button:hover {
background-color: rgba(0, 112, 243, 0.1);
}
.active {
font-weight: bold;
background-color: #0070f3;
color: #fff;
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.12);
}
.active:hover {
background-color: rgba(0, 112, 243, 0.8);
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);
}
.disabled {
color: #888;
cursor: not-allowed;
}
.disabled:hover {
background-color: #eaeaea;
}
button :global(svg) {
width: 1.3em;
height: 1.3em;
}
</style></li></nav><style>
nav {
margin: 0;
padding: 0;
font-variant: tabular-nums;
font-feature-settings: 'tnum';
font-size: .875rem;
--pagination-size: 2rem;
}
nav :global(button:last-of-type) {
margin-right: 0;
}
</style>"
`;

View File

@@ -0,0 +1,148 @@
import React from 'react'
import { mount } from 'enzyme'
import { Pagination } from 'components'
import { act } from 'react-dom/test-utils'
import { updateWrapper } from 'tests/utils'
describe('Pagination', () => {
it('should render correctly', () => {
const wrapper = mount(<Pagination />)
expect(wrapper.html()).toMatchSnapshot()
expect(() => wrapper.unmount()).not.toThrow()
})
it('the specified page should be activated', async () => {
const wrapper = mount(<Pagination count={10} initialPage={2} />)
expect(wrapper.find('.active').text()).toEqual('2')
await act(async () => {
wrapper.setProps({ page: 10 })
})
await updateWrapper(wrapper, 200)
expect(wrapper.find('.active').text()).toEqual('10')
})
it('should trigger change event', async () => {
let current = 1
const handler = jest.fn().mockImplementation(val => (current = val))
const wrapper = mount(<Pagination count={10} initialPage={2} onChange={handler} />)
await act(async () => {
wrapper.setProps({ page: 10 })
})
await updateWrapper(wrapper, 200)
expect(handler).toHaveBeenCalled()
expect(current).toEqual(10)
const btns = wrapper.find('button')
btns.at(0).simulate('click')
await updateWrapper(wrapper, 200)
expect(current).toEqual(9)
btns.at(btns.length - 1).simulate('click')
btns.at(btns.length - 1).simulate('click')
btns.at(btns.length - 1).simulate('click')
btns.at(btns.length - 1).simulate('click')
await updateWrapper(wrapper, 200)
expect(current).toEqual(10)
handler.mockRestore()
})
it('the page should be rendered to follow the specified limit', async () => {
const wrapper = mount(<Pagination count={20} limit={20} />)
expect(wrapper.find('button').length).toBeGreaterThanOrEqual(20)
await act(async () => {
wrapper.setProps({ limit: 5 })
})
await updateWrapper(wrapper, 200)
expect(wrapper.find('button').length).toBeLessThanOrEqual(10)
})
it('should be render all pages when limit is greater than the total', async () => {
const handler = jest.fn()
const wrapper = mount(<Pagination count={15} limit={40} onChange={handler} />)
expect(wrapper.find('button').length).toBeGreaterThanOrEqual(15)
wrapper.find('button').at(10).simulate('click')
await updateWrapper(wrapper, 200)
expect(handler).toHaveBeenCalled()
handler.mockRestore()
})
it('omit pages by limit value', async () => {
const wrapper = mount(<Pagination count={20} limit={5} />)
const btn4 = wrapper.find('button').at(4)
expect(btn4.text()).toEqual('4')
btn4.simulate('click')
await updateWrapper(wrapper, 200)
let btns = wrapper.find('button').map(btn => btn.text())
expect(btns.includes('2')).not.toBeTruthy()
expect(btns.includes('1')).toBeTruthy()
expect(btns.includes('3')).toBeTruthy()
expect(btns.includes('4')).toBeTruthy()
expect(btns.includes('5')).toBeTruthy()
expect(btns.includes('6')).not.toBeTruthy()
expect(btns.includes('20')).toBeTruthy()
const btn5 = wrapper.find('button').at(5)
expect(btn5.text()).toEqual('5')
btn5.simulate('click')
await updateWrapper(wrapper, 200)
btns = wrapper.find('button').map(btn => btn.text())
expect(btns.includes('1')).toBeTruthy()
expect(btns.includes('2')).not.toBeTruthy()
expect(btns.includes('3')).not.toBeTruthy()
expect(btns.includes('4')).toBeTruthy()
expect(btns.includes('5')).toBeTruthy()
expect(btns.includes('6')).toBeTruthy()
expect(btns.includes('7')).not.toBeTruthy()
expect(btns.includes('8')).not.toBeTruthy()
expect(btns.includes('20')).toBeTruthy()
})
it('should trigger change event when ellipsis clicked', async () => {
let current = 20
const handler = jest.fn().mockImplementation(val => (current = val))
const wrapper = mount(<Pagination count={20} initialPage={20} onChange={handler} />)
const btn = wrapper.find('svg').at(0).parents('button')
btn.at(0).simulate('click')
await updateWrapper(wrapper, 200)
expect(handler).toHaveBeenCalled()
expect(current).toEqual(15)
await act(async () => {
wrapper.setProps({ page: 1 })
})
await updateWrapper(wrapper, 200)
const lastBtn = wrapper.find('svg').at(0).parents('button')
lastBtn.at(0).simulate('click')
await updateWrapper(wrapper, 200)
expect(current).toEqual(1 + 5)
})
it('another SVG should be displayed when the mouse is moved in', async () => {
const wrapper = mount(<Pagination count={20} initialPage={20} />)
const svg = wrapper.find('svg').at(0)
const btn = svg.parents('button')
const html = svg.html()
btn.simulate('mouseEnter')
await updateWrapper(wrapper)
expect(html).not.toEqual(wrapper.find('svg').at(0).html())
btn.simulate('mouseLeave')
await updateWrapper(wrapper)
expect(html).toEqual(wrapper.find('svg').at(0).html())
})
it('custom buttons should be display', () => {
const wrapper = mount(
<Pagination count={20}>
<Pagination.Previous>custom-prev</Pagination.Previous>
<Pagination.Next>custom-next</Pagination.Next>
</Pagination>,
)
const btns = wrapper.find('button')
expect(btns.at(0).text()).toEqual('custom-prev')
expect(btns.at(btns.length - 1).text()).toEqual('custom-next')
})
})

View File

@@ -0,0 +1,8 @@
import Pagination from './pagination'
import PaginationPrevious from './pagination-previous'
import PaginationNext from './pagination-next'
Pagination.Previous = PaginationPrevious
Pagination.Next = PaginationNext
export default Pagination

View File

@@ -0,0 +1,18 @@
import React from 'react'
import { tuple } from '../utils/prop-types'
const paginationUpdateTypes = tuple('prev', 'next', 'click')
export type PaginationUpdateType = typeof paginationUpdateTypes[number]
export interface PaginationConfig {
isFirst?: boolean
isLast?: boolean
update?: (type: PaginationUpdateType) => void
}
const defaultContext = {}
export const PaginationContext = React.createContext<PaginationConfig>(defaultContext)
export const usePaginationContext = (): PaginationConfig =>
React.useContext<PaginationConfig>(PaginationContext)

View File

@@ -0,0 +1,60 @@
import React, { useState } from 'react'
import PaginationItem from './pagination-item'
interface Props {
isBefore?: boolean
onClick?: (e: React.MouseEvent) => void
}
const PaginationEllipsis: React.FC<Props> = ({ isBefore, onClick }) => {
const [showMore, setShowMore] = useState(false)
return (
<PaginationItem
onClick={e => onClick && onClick(e)}
onMouseEnter={() => setShowMore(true)}
onMouseLeave={() => setShowMore(false)}>
{showMore ? (
<svg
className="more"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
fill="none"
shapeRendering="geometricPrecision">
<path d="M13 17l5-5-5-5" />
<path d="M6 17l5-5-5-5" />
</svg>
) : (
<svg
viewBox="0 0 24 24"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
fill="none"
shapeRendering="geometricPrecision">
<circle cx="12" cy="12" r="1" fill="currentColor" />
<circle cx="19" cy="12" r="1" fill="currentColor" />
<circle cx="5" cy="12" r="1" fill="currentColor" />
</svg>
)}
<style jsx>{`
svg {
color: currentColor;
stroke: currentColor;
width: 1rem;
height: 1rem;
}
.more {
transform: rotate(${isBefore ? '180deg' : '0deg'});
}
`}</style>
</PaginationItem>
)
}
export default PaginationEllipsis

View File

@@ -0,0 +1,101 @@
import React, { useMemo } from 'react'
import useTheme from '../styles/use-theme'
import { addColorAlpha } from '../utils/color'
interface Props {
active?: boolean
disabled?: boolean
onClick?: (e: React.MouseEvent) => void
}
type NativeAttrs = Omit<React.ButtonHTMLAttributes<any>, keyof Props>
export type PaginationItemProps = Props & NativeAttrs
const PaginationItem: React.FC<React.PropsWithChildren<PaginationItemProps>> = ({
active,
children,
disabled,
onClick,
...props
}) => {
const theme = useTheme()
const [hover, activeHover] = useMemo(
() => [addColorAlpha(theme.palette.success, 0.1), addColorAlpha(theme.palette.success, 0.8)],
[theme.palette.success],
)
const clickHandler = (event: React.MouseEvent) => {
if (disabled) return
onClick && onClick(event)
}
return (
<li>
<button
className={`${active ? 'active' : ''} ${disabled ? 'disabled' : ''}`}
onClick={clickHandler}
{...props}>
{children}
</button>
<style jsx>{`
li {
margin-right: 6px;
display: inline-block;
}
button {
border: none;
display: inline-flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
text-transform: capitalize;
user-select: none;
white-space: nowrap;
text-align: center;
vertical-align: middle;
box-shadow: none;
outline: none;
height: var(--pagination-size);
min-width: var(--pagination-size);
font-size: inherit;
cursor: pointer;
color: ${theme.palette.success};
border-radius: ${theme.layout.radius};
background-color: ${theme.palette.background};
transition: all linear 200ms 0ms;
}
button:hover {
background-color: ${hover};
}
.active {
font-weight: bold;
background-color: ${theme.palette.success};
color: ${theme.palette.background};
box-shadow: ${theme.expressiveness.shadowSmall};
}
.active:hover {
background-color: ${activeHover};
box-shadow: ${theme.expressiveness.shadowMedium};
}
.disabled {
color: ${theme.palette.accents_4};
cursor: not-allowed;
}
.disabled:hover {
background-color: ${theme.palette.accents_2};
}
button :global(svg) {
width: 1.3em;
height: 1.3em;
}
`}</style>
</li>
)
}
export default PaginationItem

View File

@@ -0,0 +1,19 @@
import React from 'react'
import PaginationItem from './pagination-item'
import { usePaginationContext } from './pagination-context'
export type PaginationNextProps = React.ButtonHTMLAttributes<any>
const PaginationNext: React.FC<React.PropsWithChildren<PaginationNextProps>> = ({
children,
...props
}) => {
const { update, isLast } = usePaginationContext()
return (
<PaginationItem onClick={() => update && update('next')} disabled={isLast} {...props}>
{children}
</PaginationItem>
)
}
export default PaginationNext

View File

@@ -0,0 +1,104 @@
import React, { Dispatch, SetStateAction, useCallback, useMemo } from 'react'
import PaginationItem from './pagination-item'
import PaginationEllipsis from './pagination-ellipsis'
interface Props {
limit: number
count: number
current: number
setPage: Dispatch<SetStateAction<number>>
}
const PaginationPages: React.FC<Props> = ({ limit, count, current, setPage }) => {
const showPages = useMemo(() => {
const oddLimit = limit % 2 === 0 ? limit - 1 : limit
return oddLimit - 2
}, [limit])
const middleNumber = (showPages + 1) / 2
const [showBeforeEllipsis, showAfterEllipsis] = useMemo(() => {
const showEllipsis = count > limit
return [
showEllipsis && current > middleNumber + 1,
showEllipsis && current < count - middleNumber,
]
}, [current, showPages, middleNumber, count, limit])
const pagesArray = useMemo(() => [...new Array(showPages)], [showPages])
const renderItem = useCallback(
(value: number, active: number) => (
<PaginationItem
key={`pagination-item-${value}`}
active={value === active}
onClick={() => setPage(value)}>
{value}
</PaginationItem>
),
[],
)
const startPages = pagesArray.map((_, index) => {
const value = index + 2
return renderItem(value, current)
})
const middlePages = pagesArray.map((_, index) => {
const middleIndexNumber = middleNumber - (index + 1)
const value = current - middleIndexNumber
return (
<PaginationItem
key={`pagination-middle-${index}`}
active={index + 1 === middleNumber}
onClick={() => setPage(value)}>
{value}
</PaginationItem>
)
})
const endPages = pagesArray.map((_, index) => {
const value = count - (showPages - index)
return renderItem(value, current)
})
if (count <= limit) {
/* eslint-disable react/jsx-no-useless-fragment */
return (
<>
{[...new Array(count)].map((_, index) => {
const value = index + 1
return (
<PaginationItem
key={`pagination-item-${value}`}
active={value === current}
onClick={() => setPage(value)}>
{value}
</PaginationItem>
)
})}
</>
)
/* eslint-enable */
}
return (
<>
{renderItem(1, current)}
{showBeforeEllipsis && (
<PaginationEllipsis
key="pagination-ellipsis-before"
isBefore
onClick={() => setPage(last => (last - 5 >= 1 ? last - 5 : 1))}
/>
)}
{showBeforeEllipsis && showAfterEllipsis
? middlePages
: showBeforeEllipsis
? endPages
: startPages}
{showAfterEllipsis && (
<PaginationEllipsis
key="pagination-ellipsis-after"
onClick={() => setPage(last => (last + 5 <= count ? last + 5 : count))}
/>
)}
{renderItem(count, current)}
</>
)
}
export default PaginationPages

View File

@@ -0,0 +1,19 @@
import React from 'react'
import PaginationItem from './pagination-item'
import { usePaginationContext } from './pagination-context'
export type PaginationNextProps = React.ButtonHTMLAttributes<any>
const PaginationPrevious: React.FC<React.PropsWithChildren<PaginationNextProps>> = ({
children,
...props
}) => {
const { update, isFirst } = usePaginationContext()
return (
<PaginationItem onClick={() => update && update('prev')} disabled={isFirst} {...props}>
{children}
</PaginationItem>
)
}
export default PaginationPrevious

View File

@@ -0,0 +1,142 @@
import React, { useEffect, useMemo } from 'react'
import PaginationPrevious from './pagination-previous'
import PaginationNext from './pagination-next'
import PaginationPages from './pagination-pages'
import { PaginationContext, PaginationConfig, PaginationUpdateType } from './pagination-context'
import useCurrentState from '../utils/use-current-state'
import { pickChild } from '../utils/collections'
import { NormalSizes } from '../utils/prop-types'
interface Props {
size?: NormalSizes
page?: number
initialPage?: number
count?: number
limit?: number
onChange?: (val: number) => void
}
const defaultProps = {
size: 'medium' as NormalSizes,
initialPage: 1,
count: 1,
limit: 7,
}
type NativeAttrs = Omit<React.HTMLAttributes<any>, keyof Props>
export type PaginationProps = Props & typeof defaultProps & NativeAttrs
type PaginationSize = {
font: string
width: string
}
const getPaginationSizes = (size: NormalSizes) => {
const sizes: { [key in NormalSizes]: PaginationSize } = {
mini: {
font: '.75rem',
width: '1.25rem',
},
small: {
font: '.75rem',
width: '1.65rem',
},
medium: {
font: '.875rem',
width: '2rem',
},
large: {
font: '1rem',
width: '2.4rem',
},
}
return sizes[size]
}
const Pagination: React.FC<React.PropsWithChildren<PaginationProps>> = ({
page: customPage,
initialPage,
count,
limit,
size,
children,
onChange,
}) => {
const [page, setPage, pageRef] = useCurrentState(initialPage)
const [, prevChildren] = pickChild(children, PaginationPrevious)
const [, nextChildren] = pickChild(children, PaginationNext)
const [prevItem, nextItem] = useMemo(() => {
const hasChildren = (c: any) => React.Children.count(c) > 0
const prevDefault = <PaginationPrevious>prev</PaginationPrevious>
const nextDefault = <PaginationNext>next</PaginationNext>
return [
hasChildren(prevChildren) ? prevChildren : prevDefault,
hasChildren(nextChildren) ? nextChildren : nextDefault,
]
}, [prevChildren, nextChildren])
const { font, width } = useMemo(() => getPaginationSizes(size), [size])
const update = (type: PaginationUpdateType) => {
if (type === 'prev' && pageRef.current > 1) {
setPage(last => last - 1)
}
if (type === 'next' && pageRef.current < count) {
setPage(last => last + 1)
}
}
const values = useMemo<PaginationConfig>(
() => ({
isFirst: page <= 1,
isLast: page >= count,
update,
}),
[page],
)
useEffect(() => {
onChange && onChange(page)
}, [page])
useEffect(() => {
if (customPage !== undefined) {
setPage(customPage)
}
}, [customPage])
return (
<PaginationContext.Provider value={values}>
<nav>
{prevItem}
<PaginationPages count={count} current={page} limit={limit} setPage={setPage} />
{nextItem}
</nav>
<style jsx>{`
nav {
margin: 0;
padding: 0;
font-variant: tabular-nums;
font-feature-settings: 'tnum';
font-size: ${font};
--pagination-size: ${width};
}
nav :global(button:last-of-type) {
margin-right: 0;
}
`}</style>
</PaginationContext.Provider>
)
}
type MemoPaginationComponent<P = {}> = React.NamedExoticComponent<P> & {
Previous: typeof PaginationPrevious
Next: typeof PaginationNext
}
type ComponentProps = Partial<typeof defaultProps> &
Omit<Props, keyof typeof defaultProps> &
NativeAttrs
Pagination.defaultProps = defaultProps
export default React.memo(Pagination) as MemoPaginationComponent<ComponentProps>

View File

@@ -414,7 +414,7 @@ exports[`Progress should work with different types 1`] = `
bottom: 0;
transition: all 100ms ease-in;
border-radius: 5px;
background-color: #ff0000;
background-color: #e00;
width: 1%;
}
</style></div></div>"

View File

@@ -61,6 +61,28 @@ describe('Radio Group', () => {
changeHandler.mockRestore()
})
it('the radio value should be support number', () => {
let value = ''
const changeHandler = jest.fn().mockImplementation(val => (value = val))
const wrapper = mount(
<Radio.Group onChange={changeHandler}>
<Radio value={5}>Option 1</Radio>
<Radio value={10}>Option 2</Radio>
</Radio.Group>,
)
wrapper
.find('input')
.at(0)
.simulate('change', {
...nativeEvent,
target: { checked: true },
})
expect(changeHandler).toHaveBeenCalled()
expect(value).toEqual(5)
changeHandler.mockRestore()
})
it('should ignore events when disabled', () => {
const changeHandler = jest.fn()
const wrapper = mount(

View File

@@ -1,9 +1,9 @@
import React from 'react'
export interface RadioConfig {
updateState?: (value: string) => void
updateState?: (value: string | number) => void
disabledAll: boolean
value?: string
value?: string | number
inGroup: boolean
}

View File

@@ -4,11 +4,11 @@ import { RadioContext } from './radio-context'
import { NormalSizes } from 'components/utils/prop-types'
interface Props {
value?: string
initialValue?: string
value?: string | number
initialValue?: string | number
disabled?: boolean
size?: NormalSizes
onChange?: (value: string) => void
onChange?: (value: string | number) => void
className?: string
useRow?: boolean
}
@@ -44,8 +44,8 @@ const RadioGroup: React.FC<React.PropsWithChildren<RadioGroupProps>> = ({
useRow,
...props
}) => {
const [selfVal, setSelfVal] = useState<string | undefined>(initialValue)
const updateState = (nextValue: string) => {
const [selfVal, setSelfVal] = useState<string | number | undefined>(initialValue)
const updateState = (nextValue: string | number) => {
setSelfVal(nextValue)
onChange && onChange(nextValue)
}

View File

@@ -20,7 +20,7 @@ export interface RadioEvent {
interface Props {
checked?: boolean
value?: string
value?: string | number
size?: NormalSizes
className?: string
disabled?: boolean
@@ -77,7 +77,7 @@ const Radio: React.FC<React.PropsWithChildren<RadioProps>> = ({
}
setSelfChecked(!selfChecked)
if (inGroup) {
updateState && updateState(radioValue as string)
updateState && updateState(radioValue as string | number)
}
onChange && onChange(selfEvent)
}

View File

@@ -6,6 +6,7 @@ exports[`Select should render correctly 1`] = `
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
line-height: calc(1.688 * 16pt);
min-width: 0;
}
</style></span></span><div class=\\"icon\\"><svg viewBox=\\"0 0 24 24\\" width=\\"1.25em\\" height=\\"1.25em\\" stroke-width=\\"1\\" stroke-linecap=\\"round\\" stroke-linejoin=\\"round\\" fill=\\"none\\" shape-rendering=\\"geometricPrecision\\"><path d=\\"M6 9l6 6 6-6\\"></path><style>
@@ -93,6 +94,7 @@ exports[`Select should render correctly 1`] = `
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
line-height: calc(1 * 16pt);
min-width: 0;
}
</style></span></span><div class=\\"icon\\"><svg viewBox=\\"0 0 24 24\\" width=\\"1.25em\\" height=\\"1.25em\\" stroke-width=\\"1\\" stroke-linecap=\\"round\\" stroke-linejoin=\\"round\\" fill=\\"none\\" shape-rendering=\\"geometricPrecision\\"><path d=\\"M6 9l6 6 6-6\\"></path><style>
@@ -180,6 +182,7 @@ exports[`Select should render correctly 1`] = `
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
line-height: calc(1.344 * 16pt);
min-width: 0;
}
</style></span></span><div class=\\"icon\\"><svg viewBox=\\"0 0 24 24\\" width=\\"1.25em\\" height=\\"1.25em\\" stroke-width=\\"1\\" stroke-linecap=\\"round\\" stroke-linejoin=\\"round\\" fill=\\"none\\" shape-rendering=\\"geometricPrecision\\"><path d=\\"M6 9l6 6 6-6\\"></path><style>
@@ -267,6 +270,7 @@ exports[`Select should render correctly 1`] = `
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
line-height: calc(2 * 16pt);
min-width: 0;
}
</style></span></span><div class=\\"icon\\"><svg viewBox=\\"0 0 24 24\\" width=\\"1.25em\\" height=\\"1.25em\\" stroke-width=\\"1\\" stroke-linecap=\\"round\\" stroke-linejoin=\\"round\\" fill=\\"none\\" shape-rendering=\\"geometricPrecision\\"><path d=\\"M6 9l6 6 6-6\\"></path><style>
@@ -358,6 +362,7 @@ exports[`Select should work correctly with labels 1`] = `
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
line-height: calc(1.688 * 16pt);
min-width: 0;
}
</style></span></span><div class=\\"icon\\"><svg viewBox=\\"0 0 24 24\\" width=\\"1.25em\\" height=\\"1.25em\\" stroke-width=\\"1\\" stroke-linecap=\\"round\\" stroke-linejoin=\\"round\\" fill=\\"none\\" shape-rendering=\\"geometricPrecision\\"><path d=\\"M6 9l6 6 6-6\\"></path><style>
@@ -467,6 +472,7 @@ initialize {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
line-height: calc(1.688 * 16pt);
min-width: 0;
}
",
@@ -708,6 +714,7 @@ initialize {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
line-height: calc(1.688 * 16pt);
min-width: 0;
}
",
@@ -817,6 +824,7 @@ initialize {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
line-height: calc(1.688 * 16pt);
min-width: 0;
}
",
@@ -1130,6 +1138,7 @@ initialize {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
line-height: calc(1.688 * 16pt);
min-width: 0;
}
",
@@ -1316,6 +1325,7 @@ initialize {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
line-height: calc(1.688 * 16pt);
min-width: 0;
}
",

View File

@@ -6,6 +6,7 @@ exports[`Select Multiple should render correctly 1`] = `
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
line-height: calc(1.688 * 16pt);
min-width: 0;
}
</style></span></span><div class=\\"item mock \\"><style>
@@ -32,6 +33,7 @@ exports[`Select Multiple should render correctly 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
@media only screen and (max-width: 650px) {
@@ -39,6 +41,7 @@ exports[`Select Multiple should render correctly 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -47,6 +50,7 @@ exports[`Select Multiple should render correctly 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -55,6 +59,7 @@ exports[`Select Multiple should render correctly 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -63,6 +68,7 @@ exports[`Select Multiple should render correctly 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
@@ -71,6 +77,7 @@ exports[`Select Multiple should render correctly 1`] = `
flex-grow: 1;
max-width: 100%;
flex-basis: 0;
}
}
</style></div><div class=\\"icon\\"><svg viewBox=\\"0 0 24 24\\" width=\\"1.25em\\" height=\\"1.25em\\" stroke-width=\\"1\\" stroke-linecap=\\"round\\" stroke-linejoin=\\"round\\" fill=\\"none\\" shape-rendering=\\"geometricPrecision\\"><path d=\\"M6 9l6 6 6-6\\"></path><style>

View File

@@ -81,7 +81,7 @@ const SelectOption: React.FC<React.PropsWithChildren<SelectOptionProps>> = ({
className={`option ${divider ? 'divider' : ''} ${label ? 'label' : ''} ${className}`}
onClick={clickHandler}
{...props}>
<Ellipsis>{children}</Ellipsis>
<Ellipsis height={`calc(1.688 * ${theme.layout.gap})`}>{children}</Ellipsis>
</div>
<style jsx>{`

View File

@@ -139,7 +139,7 @@ const Select: React.FC<React.PropsWithChildren<SelectProps>> = ({
{...props}>
{isEmpty && (
<span className="value placeholder">
<Ellipsis>{placeholder}</Ellipsis>
<Ellipsis height={sizes.height}>{placeholder}</Ellipsis>
</span>
)}
{value && !multiple && <span className="value">{selectedChild}</span>}

View File

@@ -1,103 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Backdrop content should be offset 1`] = `
"<div class=\\"backdrop transition-enter\\"><div class=\\"layer\\"></div><div class=\\"content\\"><span>test-value</span></div><div class=\\"offset\\"></div><style>
.backdrop {
position: fixed;
top: 0;
left: 0;
display: flex;
align-content: center;
align-items: center;
flex-direction: column;
justify-content: center;
height: 100vh;
width: 100vw;
overflow: auto;
z-index: 1000;
}
.content {
display: flex;
align-items: center;
justify-content: center;
position: relative;
z-index: 1001;
outline: none;
}
.offset {
height: 0vh;
opacity: 0;
background-color: transparent;
}
.layer {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
opacity: 0.25;
background-color: black;
transition: opacity 0.35s cubic-bezier(0.4, 0, 0.2, 1);
pointer-events: none;
z-index: 1000;
}
</style></div>"
`;
exports[`Backdrop content should be offset 2`] = `
"<div class=\\"backdrop transition-enter\\"><div class=\\"layer\\"></div><div class=\\"content\\"><span>test-value</span></div><div class=\\"offset\\"></div><style>
.backdrop {
position: fixed;
top: 0;
left: 0;
display: flex;
align-content: center;
align-items: center;
flex-direction: column;
justify-content: center;
height: 100vh;
width: 100vw;
overflow: auto;
z-index: 1000;
}
.content {
display: flex;
align-items: center;
justify-content: center;
position: relative;
z-index: 1001;
outline: none;
}
.offset {
height: 100vh;
opacity: 0;
background-color: transparent;
}
.layer {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
opacity: 0.25;
background-color: black;
transition: opacity 0.35s cubic-bezier(0.4, 0, 0.2, 1);
pointer-events: none;
z-index: 1000;
}
</style></div>"
`;
exports[`Backdrop should render correctly 1`] = `
"<div class=\\"backdrop transition-enter\\"><div class=\\"layer\\"></div><div class=\\"content\\"><span>test-value</span></div><div class=\\"offset\\"></div><style>
.backdrop {
@@ -108,7 +10,7 @@ exports[`Backdrop should render correctly 1`] = `
align-content: center;
align-items: center;
flex-direction: column;
justify-content: center;
justify-content: space-around;
height: 100vh;
width: 100vw;
overflow: auto;
@@ -125,8 +27,9 @@ exports[`Backdrop should render correctly 1`] = `
}
.offset {
height: 0vh;
height: 0;
opacity: 0;
display: flex;
background-color: transparent;
}

View File

@@ -6,6 +6,7 @@ exports[`Ellipsis should render correctly 1`] = `
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
line-height: 10px;
min-width: 0;
}
</style></span></div>"

View File

@@ -61,17 +61,28 @@ describe('Backdrop', () => {
handler.mockRestore()
})
it('content should be offset', () => {
it('backdrop handler should ignore click events from content', async () => {
const handler = jest.fn()
const wrapper = mount(
<Backdrop visible>
<Backdrop visible onClick={handler}>
<span>test-value</span>
</Backdrop>,
)
const notOffset = wrapper.html()
expect(wrapper.html()).toMatchSnapshot()
wrapper.setProps({ offsetY: '100' })
expect(wrapper.html()).toMatchSnapshot()
expect(notOffset).not.toEqual(wrapper.html())
/**
* In simulation,`mousedown` and `mouseup`not directly triiger `click` event,
* the click event below is just for simulation.
*/
wrapper.find('.content').simulate('mousedown')
wrapper.find('.backdrop').simulate('click', nativeEvent)
wrapper.find('.backdrop').simulate('mouseup')
await updateWrapper(wrapper)
expect(handler).not.toHaveBeenCalled()
wrapper.find('.backdrop').simulate('mousedown')
wrapper.find('.backdrop').simulate('click', nativeEvent)
wrapper.find('.backdrop').simulate('mouseup')
await updateWrapper(wrapper)
expect(handler).toHaveBeenCalled()
})
})

View File

@@ -6,7 +6,7 @@ describe('Ellipsis', () => {
it('should render correctly', () => {
const wrapper = mount(
<div style={{ width: '1px' }}>
<Ellipsis>text</Ellipsis>
<Ellipsis height="10px">text</Ellipsis>
</div>,
)
expect(wrapper.html()).toMatchSnapshot()

View File

@@ -2,11 +2,11 @@ import React, { MouseEvent, useCallback } from 'react'
import withDefaults from '../utils/with-defaults'
import useTheme from '../styles/use-theme'
import CSSTransition from './css-transition'
import useCurrentState from '../utils/use-current-state'
interface Props {
onClick?: (event: MouseEvent<HTMLElement>) => void
visible?: boolean
offsetY?: number
}
const defaultProps = {
@@ -18,20 +18,32 @@ const defaultProps = {
export type BackdropProps = Props & typeof defaultProps
const Backdrop: React.FC<React.PropsWithChildren<BackdropProps>> = React.memo(
({ children, onClick, visible, offsetY }) => {
({ children, onClick, visible }) => {
const theme = useTheme()
const clickHandler = useCallback((event: MouseEvent<HTMLElement>) => {
const [, setIsContentMouseDown, IsContentMouseDownRef] = useCurrentState(false)
const clickHandler = (event: MouseEvent<HTMLElement>) => {
if (IsContentMouseDownRef.current) return
onClick && onClick(event)
}, [])
}
const childrenClickHandler = useCallback((event: MouseEvent<HTMLElement>) => {
event.stopPropagation()
}, [])
const mouseUpHandler = () => {
if (!IsContentMouseDownRef.current) return
const timer = setTimeout(() => {
setIsContentMouseDown(false)
clearTimeout(timer)
}, 0)
}
return (
<CSSTransition visible={visible} clearTime={300}>
<div className="backdrop" onClick={clickHandler}>
<div className="backdrop" onClick={clickHandler} onMouseUp={mouseUpHandler}>
<div className="layer" />
<div onClick={childrenClickHandler} className="content">
<div
onClick={childrenClickHandler}
className="content"
onMouseDown={() => setIsContentMouseDown(true)}>
{children}
</div>
<div onClick={childrenClickHandler} className="offset" />
@@ -44,7 +56,7 @@ const Backdrop: React.FC<React.PropsWithChildren<BackdropProps>> = React.memo(
align-content: center;
align-items: center;
flex-direction: column;
justify-content: center;
justify-content: space-around;
height: 100vh;
width: 100vw;
overflow: auto;
@@ -61,8 +73,9 @@ const Backdrop: React.FC<React.PropsWithChildren<BackdropProps>> = React.memo(
}
.offset {
height: ${offsetY}vh;
height: 0;
opacity: 0;
display: flex;
background-color: transparent;
}

View File

@@ -1,6 +1,10 @@
import React from 'react'
const Ellipsis: React.FC<React.PropsWithChildren<{}>> = ({ children }) => {
export type EllipsisProps = {
height: string
}
const Ellipsis: React.FC<React.PropsWithChildren<EllipsisProps>> = ({ children, height }) => {
return (
<span>
{children}
@@ -9,6 +13,7 @@ const Ellipsis: React.FC<React.PropsWithChildren<{}>> = ({ children }) => {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
line-height: ${height};
min-width: 0;
}
`}</style>

View File

@@ -18,19 +18,29 @@ export const palette: ZeitUIThemesPalette = {
secondary: '#888',
code: '#79ffe1',
border: '#333',
error: '#ff0000',
error: '#e00',
errorLighter: '#f7d4d6',
errorLight: '#ff1a1a',
errorDark: '#c00',
errorDark: '#c50000',
success: '#0070f3',
successLighter: '#d3e5ff',
successLight: '#3291ff',
successDark: '#0366d6',
successDark: '#0761d1',
warning: '#f5a623',
warningLighter: '#ffefcf',
warningLight: '#f7b955',
warningDark: '#f49b0b',
cyan: '#79ffe1',
warningDark: '#ab570a',
cyan: '#50e3c2',
cyanLighter: '#aaffec',
cyanLight: '#79ffe1',
cyanDark: '#29bc9b',
violet: '#7928ca',
violetLighter: '#e3d7fc',
violetLight: '#8a63d2',
violetDark: '#4c2889',
purple: '#f81ce5',
alert: '#ff0080',
violet: '#7928ca',
magenta: '#eb367f',
link: '#3291ff',
}

View File

@@ -18,19 +18,29 @@ export const palette: ZeitUIThemesPalette = {
secondary: '#666',
code: '#f81ce5',
border: '#eaeaea',
error: '#ff0000',
error: '#e00',
errorLight: '#ff1a1a',
errorDark: '#c00',
errorLighter: '#f7d4d6',
errorDark: '#c50000',
success: '#0070f3',
successLight: '#3291ff',
successDark: '#0366d6',
successLighter: '#d3e5ff',
successDark: '#0761d1',
warning: '#f5a623',
warningLight: '#f7b955',
warningDark: '#f49b0b',
cyan: '#79ffe1',
warningLighter: '#ffefcf',
warningDark: '#ab570a',
cyan: '#50e3c2',
cyanLighter: '#aaffec',
cyanLight: '#79ffe1',
cyanDark: '#29bc9b',
violet: '#7928ca',
violetLighter: '#e3d7fc',
violetLight: '#8a63d2',
violetDark: '#4c2889',
purple: '#f81ce5',
alert: '#ff0080',
violet: '#7928ca',
magenta: '#eb367f',
link: '#0070f3',
}

View File

@@ -18,19 +18,29 @@ export interface ZeitUIThemesPalette {
code: string
border: string
success: string
successLighter: string
successLight: string
successDark: string
error: string
errorLighter: string
errorLight: string
errorDark: string
warning: string
warningLighter: string
warningLight: string
warningDark: string
cyan: string
purple: string
alert: string
cyanLighter: string
cyanLight: string
cyanDark: string
violet: string
violetLighter: string
violetLight: string
violetDark: string
link: string
purple: string
magenta: string
alert: string
}
export interface ZeitUIThemesExpressiveness {

View File

@@ -319,9 +319,9 @@ initialize {
font-size: 0.875rem;
height: 1.75rem;
border-radius: 5px;
border: 1px solid #ff0000;
border: 1px solid #e00;
background-color: #fff;
color: #ff0000;
color: #e00;
padding: 6px;
box-sizing: border-box;
}
@@ -356,9 +356,9 @@ initialize {
font-size: 0.875rem;
height: 1.75rem;
border-radius: 5px;
border: 1px solid #ff0000;
border: 1px solid #e00;
background-color: #fff;
color: #ff0000;
color: #e00;
padding: 6px;
box-sizing: border-box;
}
@@ -618,9 +618,9 @@ initialize {
font-size: 0.875rem;
height: 1.75rem;
border-radius: 5px;
border: 1px solid #ff0000;
border: 1px solid #e00;
background-color: #fff;
color: #ff0000;
color: #e00;
padding: 6px;
box-sizing: border-box;
}
@@ -655,9 +655,9 @@ initialize {
font-size: 0.875rem;
height: 1.75rem;
border-radius: 5px;
border: 1px solid #ff0000;
border: 1px solid #e00;
background-color: #fff;
color: #ff0000;
color: #e00;
padding: 6px;
box-sizing: border-box;
}
@@ -917,9 +917,9 @@ initialize {
font-size: 0.875rem;
height: 1.75rem;
border-radius: 5px;
border: 1px solid #ff0000;
border: 1px solid #e00;
background-color: #fff;
color: #ff0000;
color: #e00;
padding: 6px;
box-sizing: border-box;
}
@@ -954,9 +954,9 @@ initialize {
font-size: 0.875rem;
height: 1.75rem;
border-radius: 5px;
border: 1px solid #ff0000;
border: 1px solid #e00;
background-color: #fff;
color: #ff0000;
color: #e00;
padding: 6px;
box-sizing: border-box;
}
@@ -1390,9 +1390,9 @@ initialize {
font-size: 0.875rem;
height: 1.75rem;
border-radius: 5px;
border: 1px solid #ff0000;
border: 1px solid #e00;
background-color: #fff;
color: #ff0000;
color: #e00;
padding: 6px;
box-sizing: border-box;
}
@@ -1427,9 +1427,9 @@ initialize {
font-size: 0.875rem;
height: 1.75rem;
border-radius: 5px;
border: 1px solid #ff0000;
border: 1px solid #e00;
background-color: #fff;
color: #ff0000;
color: #e00;
padding: 6px;
box-sizing: border-box;
}

View File

@@ -75,7 +75,7 @@ exports[`Text should work with different styles 1`] = `
}
</style><p class=\\" \\">test-value</p><style>
p {
color: #ff0000;
color: #e00;
}
.custom-size {

View File

@@ -1,3 +1,16 @@
## Custom styles
This is an example of **how to override styles in `zeit-ui`**.
## Getting Started
First, run the development server:
```bash
npm i && npm run dev
# or
yarn && yarn dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

View File

@@ -8,7 +8,7 @@
"build": "react-scripts build"
},
"dependencies": {
"@zeit-ui/react": "^0.0.1-beta.25",
"@zeit-ui/react": "latest",
"react": "^16.13.1",
"react-dom": "^16.13.1"
},

View File

@@ -1,14 +1,14 @@
import React from 'react'
import ReactDom from 'react-dom'
import { ZEITUIProvider, CSSBaseline } from '@zeit-ui/react'
import { ZeitProvider, CssBaseline } from '@zeit-ui/react'
import Home from './home'
const App = () => {
return (
<ZEITUIProvider>
<CSSBaseline />
<ZeitProvider>
<CssBaseline />
<Home />
</ZEITUIProvider>
</ZeitProvider>
)
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,15 @@
## Custom themes example
## Custom themes
This is an example of **how to custom Themes in `zeit-ui`**.
## Getting Started
First, run the development server:
```bash
npm i && npm run dev
# or
yarn && yarn dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

View File

@@ -8,7 +8,7 @@
"build": "react-scripts build"
},
"dependencies": {
"@zeit-ui/react": "^0.0.1-beta.17",
"@zeit-ui/react": "latest",
"react": "^16.13.1",
"react-dom": "^16.13.1"
},

View File

@@ -5,7 +5,7 @@ const Home = () => {
const theme = useTheme()
return (
<Card shadow style={{ width: '500px', margin: '100px auto' }}>
<Card shadow>
<Text>Modern and minimalist React UI library.</Text>
<Text type={'success'}>
Modern and minimalist React UI library. <Tag>{theme.palette.success}</Tag>

View File

@@ -1,15 +1,17 @@
import React from 'react'
import ReactDom from 'react-dom'
import { ZEITUIProvider, CSSBaseline } from '@zeit-ui/react'
import { ZeitProvider, CssBaseline, Page } from '@zeit-ui/react'
import Home from './home'
import Theme from './theme'
const App = () => {
return (
<ZEITUIProvider theme={Theme}>
<CSSBaseline />
<Home />
</ZEITUIProvider>
<ZeitProvider theme={Theme}>
<CssBaseline />
<Page size="mini">
<Home />
</Page>
</ZeitProvider>
)
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,15 @@
## Extends Components
This is an example of **how to inherit components in `zeit-ui`**.
## Getting Started
First, run the development server:
```bash
npm i && npm run dev
# or
yarn && yarn dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

View File

@@ -0,0 +1,26 @@
{
"name": "zeit-ui-react-extends-components",
"version": "1.0.0",
"main": "index.js",
"dependencies": {
"@zeit-ui/react": "latest",
"react": "^16.12.0",
"react-dom": "^16.12.0"
},
"devDependencies": {
"@types/react": "16.9.19",
"@types/react-dom": "16.9.5",
"react-scripts": "^3.4.1",
"typescript": "3.7.5"
},
"scripts": {
"dev": "react-scripts start",
"build": "react-scripts build"
},
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"not op_mini all"
]
}

View File

@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Extends Components</title>
</head>
<body>
<div id="app"></div>
</body>
</html>

View File

@@ -0,0 +1,14 @@
import * as React from 'react'
import MyInput from './my-input'
import { ZeitProvider, CssBaseline, Page } from '@zeit-ui/react'
export default function App() {
return (
<ZeitProvider>
<CssBaseline />
<Page size="mini">
<MyInput error="this is required" placeholder="my input" />
</Page>
</ZeitProvider>
)
}

View File

@@ -0,0 +1,7 @@
import * as React from 'react'
import { render } from 'react-dom'
import App from './app'
const rootElement = document.getElementById('app')
render(<App />, rootElement)

View File

@@ -0,0 +1,29 @@
import React, { ReactElement } from 'react'
import { Input, Spacer, Text } from '@zeit-ui/react'
type InputErrorType = {
error?: string
}
const MyInput: React.FC<InputErrorType & React.ComponentProps<typeof Input>> = ({
error = null,
...inputProps
}): ReactElement => {
const hasError = Boolean(error)
const { status } = inputProps
return (
<>
<Input status={hasError ? 'error' : status} {...inputProps} />
{hasError && (
<>
<Spacer y={0.4} />
<Text small type="error" size="var(--size-xs2)">
{error}
</Text>
</>
)}
</>
)
}
export default MyInput

View File

@@ -0,0 +1 @@
/// <reference types="react-scripts" />

View File

@@ -0,0 +1,24 @@
{
"include": [
"./src/*"
],
"compilerOptions": {
"strict": true,
"esModuleInterop": true,
"lib": [
"dom",
"es2015"
],
"jsx": "react",
"target": "es5",
"allowJs": true,
"skipLibCheck": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true
}
}

View File

@@ -9,7 +9,7 @@
"analyze": "source-map-explorer 'build/static/js/*.js'"
},
"dependencies": {
"@zeit-ui/react": "^1.5.0-rc.0",
"@zeit-ui/react": "latest",
"react": "^16.13.1",
"react-dom": "^16.13.1"
},

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,14 @@
{
"presets": [
"next/babel"
],
"plugins": [
[
"import",
{
"libraryName": "@zeit-ui/react",
"libraryDirectory": "esm"
}
]
]
}

View File

@@ -0,0 +1,53 @@
# Tree shaking with Next.js
## About
This is example of `tree-shaking` for [next.js](https://nextjs.com/). Note that `@next/bundle-analyzer` is not required.
## Getting Started
First, run the development server:
```bash
npm run dev
# or
yarn dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
**Run the following command to see the size of the bundle:**
```bash
npm run analyze
# or
yarn analyze
```
## Preview
If we don't do anything, the volume of `zeit-ui/react` is about `58kb`, note that **the volume here includes lib `styled-jsx`**.
It means that even if you use lib `styled-jsx` again, the volume will not increase.
At present, it seems that this volume of component library is still acceptable:
![output-before](public/output-before.png)
<br/>
**When we use `tree shaking`:**
The volume of `zeit-ui/react` is about `13kb`.(It consists of two parts)
It should be noted that the specific package size depends on how many components you use.
![output-after](public/output-after.png)
## Other
If you don't use `tree shaking` in your porject, bundle `zeit-ui/react` as a `chunk`
every time, you may notice that the hash name of `chunk` is still changing,
this may cause you to not make full use of the cache.
- This issue from `next.js`, and they're improving that, you can track progress [here](https://github.com/vercel/next.js/issues/6303).
- If you want to customize config of webpack, to ensure that the `chunk` from `zeit-ui/react` is always the same,
you can refer to [this docuemnt](https://webpack.js.org/guides/code-splitting/).

View File

@@ -0,0 +1,6 @@
const withTM = require('next-transpile-modules')(['@zeit-ui/react'])
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true',
})
module.exports = withTM(withBundleAnalyzer({}))

View File

@@ -0,0 +1,22 @@
{
"name": "tree-shaking-nextjs",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"analyze": "ANALYZE=true yarn build"
},
"dependencies": {
"@zeit-ui/react": "latest",
"next": "9.4.2",
"react": "16.13.1",
"react-dom": "16.13.1"
},
"devDependencies": {
"@next/bundle-analyzer": "^9.4.4",
"babel-plugin-import": "^1.13.0",
"next-transpile-modules": "^3.3.0"
}
}

View File

@@ -0,0 +1,11 @@
import { ZeitProvider, CssBaseline } from '@zeit-ui/react'
function MyApp({ Component, pageProps }) {
return (
<ZeitProvider>
<CssBaseline />
<Component {...pageProps} />
</ZeitProvider>
)
}
export default MyApp

View File

@@ -0,0 +1,33 @@
import Document, { Html, Head, Main, NextScript } from 'next/document'
import { CssBaseline } from '@zeit-ui/react'
class MyDocument extends Document {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx)
const styles = CssBaseline.flush()
return {
...initialProps,
styles: (
<>
{initialProps.styles}
{styles}
</>
),
}
}
render() {
return (
<Html>
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
export default MyDocument

View File

@@ -0,0 +1,12 @@
import { Page, Text, Button } from '@zeit-ui/react'
export default function Home() {
return (
<Page>
<Text h1>
Welcome to <a href="https://nextjs.org">Next.js!</a>
</Text>
<Button>hello</Button>
</Page>
)
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

View File

@@ -21,7 +21,7 @@
"webpack-dev-server": "^3.11.0"
},
"dependencies": {
"@zeit-ui/react": "^1.5.0-rc.0",
"@zeit-ui/react": "latest",
"react": "^16.13.1",
"react-dom": "^16.13.1"
}

File diff suppressed because it is too large Load Diff

View File

@@ -56,14 +56,14 @@ const Controls: React.FC<{}> = React.memo(({}) => {
value={isDark ? 'dark' : 'light'}
title={isChinese ? '切换主题' : 'Switch Themes'}>
<Select.Option value="light">
<div className="select-content">
<span className="select-content">
<SunIcon size={14} /> {isChinese ? '明亮' : 'Light'}
</div>
</span>
</Select.Option>
<Select.Option value="dark">
<div className="select-content">
<span className="select-content">
<MoonIcon size={14} /> {isChinese ? '暗黑' : 'Dark'}
</div>
</span>
</Select.Option>
</Select>
</div>
@@ -82,7 +82,8 @@ const Controls: React.FC<{}> = React.memo(({}) => {
.select-content {
width: auto;
display: inline-flex;
height: 18px;
display: flex;
justify-content: center;
align-items: center;
}

View File

@@ -20,28 +20,44 @@ const normal: ColorEnum = {
/* eslint-enable camelcase */
const error: ColorEnum = {
errorLighter: 'Lighter',
errorLight: 'Light',
error: 'Default',
errorDark: 'Dark',
}
const success: ColorEnum = {
successLighter: 'Lighter',
successLight: 'Light',
success: 'Default',
successDark: 'Dark',
}
const warning: ColorEnum = {
warningLighter: 'Lighter',
warningLight: 'Light',
warning: 'Default',
warningDark: 'Dark',
}
const violet: ColorEnum = {
violetLighter: 'Lighter',
violetLight: 'Light',
violet: 'Default',
violetDark: 'Dark',
}
const cyan: ColorEnum = {
cyanLighter: 'Lighter',
cyanLight: 'Light',
cyan: 'Default',
cyanDark: 'Dark',
}
const highlight: ColorEnum = {
alert: 'Alert',
purple: 'Purple',
violet: 'Violet',
cyan: 'Cyan',
magenta: 'Violet',
}
const colorsData: { [key: string]: ColorEnum } = {
@@ -49,6 +65,8 @@ const colorsData: { [key: string]: ColorEnum } = {
success,
warning,
error,
violet,
cyan,
highlight,
}
@@ -72,5 +90,15 @@ export const getCurrentColor = (
return 'black'
}
if (type === 'warning' || type === 'cyan') {
if (index < 3) return 'black'
return 'white'
}
if (Object.keys(colorsData[type]).find(key => key.endsWith('Lighter'))) {
if (index === 0) return 'black'
return 'white'
}
return palette.background
}

View File

@@ -1,5 +1,5 @@
import React, { useMemo } from 'react'
import { useTheme, useToasts, Code, ZeitUIThemesPalette } from 'components'
import { useTheme, useToasts, Code, Grid, ZeitUIThemesPalette } from 'components'
import useClipboard from 'components/utils/use-clipboard'
import { getColorData, getCurrentColor } from './colors-data'
@@ -14,13 +14,21 @@ const getColorItem = (type: string, palette: ZeitUIThemesPalette, copy: Function
return (keys as Array<keyof ZeitUIThemesPalette>).map((key, index) => (
<div className="color" key={`color-item-${index}`}>
<h4>{data[key]}</h4>
<span className="usage" onClick={() => copy(`theme.palette.${key}`)}>
theme.palette.{key}
</span>
<span className="value" onClick={() => copy(palette[key])}>
{palette[key]}
</span>
<Grid.Container justify="space-between" style={{ height: '4.5rem' }}>
<Grid.Container alignItems="center" sm={8} xs={16}>
<h4>{data[key]}</h4>
</Grid.Container>
<Grid.Container alignItems="center" justify="center" sm={8} xs={0}>
<span className="usage" onClick={() => copy(`theme.palette.${key}`)}>
theme.palette.{key}
</span>
</Grid.Container>
<Grid.Container alignItems="center" justify="flex-end" sm={8} xs>
<span className="value" onClick={() => copy(palette[key])}>
{palette[key]}
</span>
</Grid.Container>
</Grid.Container>
<style jsx>{`
.color {
background-color: ${palette[key]};
@@ -61,13 +69,8 @@ const Colors: React.FC<Props> = ({ type }) => {
}
.colors :global(.color) {
display: flex;
align-items: center;
justify-content: space-between;
padding: ${theme.layout.gap};
height: 6rem;
position: relative;
cursor: pointer;
user-select: none;
}
@@ -86,18 +89,16 @@ const Colors: React.FC<Props> = ({ type }) => {
}
.colors :global(.usage) {
font-size: 0.875rem;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
font-size: 1rem;
padding: 1rem;
cursor: pointer;
}
.colors :global(.value) {
font-size: 0.875rem;
text-transform: uppercase;
padding: 1rem 0;
padding: 1rem;
cursor: pointer;
}
`}</style>
</div>

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More