feat(scaleable): add scaleable props to each component (#531)

* feat(scaleable): add scaleable props to each component

* chore(scaleable): update the exported type

* feat: apply scaleable to components

chore: remove with-default

test: improve testcase for scaleable

chore: resolve test warning

ci: upgrade nodejs to latest lts

docs: fix type error in document site

* docs: update documents to be compatible with scaleable

chore: fix build errors

* chore: remove all size-related attributes

docs: improve guide document

* docs: add scaleable documentation

test: update snapshots

chore: remove unused

* feat: add scaleable to grid components

* docs: improve docs

* test: update snapshots

* fix(grid): fix basic component props
This commit is contained in:
witt
2021-06-23 10:53:30 +08:00
committed by unix
parent c3708c1948
commit 7facec3849
382 changed files with 15046 additions and 27916 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -6,11 +6,13 @@ exports[`Select Multiple should render correctly 1`] = `
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
line-height: calc(1.688 * 16pt);
line-height: var(--select-height);
min-width: 0;
}
</style></span></span><div class=\\"item mock \\"><style>
.item {
font-size: inherit;
height: auto;
}
.justify {
@@ -80,11 +82,13 @@ exports[`Select Multiple should render correctly 1`] = `
display: inherit;
}
}
</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>
</style></div><div class=\\"icon\\"><svg viewBox=\\"0 0 24 24\\" stroke-width=\\"1\\" stroke-linecap=\\"round\\" stroke-linejoin=\\"round\\" fill=\\"none\\" shape-rendering=\\"geometricPrecision\\"><path d=\\"M6 9l6 6 6-6\\"></path><style>
svg {
color: inherit;
stroke: currentColor;
transition: all 200ms ease;
width: 1.214em;
height: 1.214em;
}
</style></svg></div><style>
.select {
@@ -94,24 +98,29 @@ exports[`Select Multiple should render correctly 1`] = `
white-space: nowrap;
position: relative;
cursor: pointer;
max-width: 80vw;
width: initial;
max-width: 90vw;
overflow: hidden;
transition: border 150ms ease-in 0s, color 200ms ease-out 0s,
box-shadow 200ms ease 0s;
border: 1px solid #eaeaea;
border-radius: 5px;
padding: 0 4pt 0 8pt;
height: calc(1.688 * 16pt);
min-width: 10rem;
background-color: #fff;
--select-font-size: calc(0.875 * 16px);
--select-height: calc(2.25 * 16px);
min-width: 11.5em;
width: initial;
height: var(--select-height);
padding: 0 calc(0.334 * 16px) 0
calc(0.667 * 16px);
margin: 0 0 0 0;
}
.multiple {
height: auto;
min-height: calc(1.688 * 16pt);
padding: 4pt calc(.875rem * 2)
4pt 8pt;
min-height: var(--select-height);
padding: calc(0.334 * 16px) calc(0.334 * 16px) calc(0.334 * 16px)
calc(0.667 * 16px);
}
.select:hover {
@@ -129,10 +138,10 @@ exports[`Select Multiple should render correctly 1`] = `
align-items: center;
line-height: 1;
padding: 0;
margin-right: 1.25rem;
font-size: .875rem;
margin-right: 1.25em;
font-size: var(--select-font-size);
color: #000;
width: calc(100% - 1.25rem);
width: calc(100% - 1.25em);
}
.value > :global(div),
@@ -151,7 +160,7 @@ exports[`Select Multiple should render correctly 1`] = `
.icon {
position: absolute;
right: 4pt;
font-size: .875rem;
font-size: var(--select-font-size);
top: 50%;
bottom: 0;
transform: translateY(-50%) rotate(0deg);

View File

@@ -8,9 +8,7 @@ describe('Select', () => {
const wrapper = mount(
<div>
<Select />
<Select size="mini" />
<Select size="small" />
<Select size="large" />
<Select scale={2} />
</div>,
)
expect(wrapper.html()).toMatchSnapshot()

View File

@@ -1,6 +1,9 @@
import Select from './select'
import SelectOption from './select-option'
Select.Option = SelectOption
export type SelectComponentType = typeof Select & {
Option: typeof SelectOption
}
;(Select as SelectComponentType).Option = SelectOption
export default Select
export default Select as SelectComponentType

View File

@@ -1,19 +1,16 @@
import React, { MutableRefObject } from 'react'
import { NormalSizes } from '../utils/prop-types'
export interface SelectConfig {
value?: string | string[]
updateValue?: (next: string | undefined) => unknown
visible?: boolean
updateVisible?: (next: boolean) => unknown
size?: NormalSizes
disableAll?: boolean
ref?: MutableRefObject<HTMLElement | null>
}
const defaultContext = {
visible: false,
size: 'medium' as NormalSizes,
disableAll: false,
}

View File

@@ -1,6 +1,5 @@
import React, { CSSProperties } from 'react'
import useTheme from '../use-theme'
import withDefaults from '../utils/with-defaults'
import { useSelectContext } from './select-context'
import Dropdown from '../shared/dropdown'
@@ -18,7 +17,7 @@ const defaultProps = {
}
type NativeAttrs = Omit<React.HTMLAttributes<any>, keyof Props>
export type SelectDropdownProps = Props & typeof defaultProps & NativeAttrs
export type SelectDropdownProps = Props & NativeAttrs
const SelectDropdown: React.FC<React.PropsWithChildren<SelectDropdownProps>> = ({
visible,
@@ -27,7 +26,7 @@ const SelectDropdown: React.FC<React.PropsWithChildren<SelectDropdownProps>> = (
dropdownStyle,
disableMatchWidth,
getPopupContainer,
}) => {
}: React.PropsWithChildren<SelectDropdownProps> & typeof defaultProps) => {
const theme = useTheme()
const { ref } = useSelectContext()
@@ -44,10 +43,10 @@ const SelectDropdown: React.FC<React.PropsWithChildren<SelectDropdownProps>> = (
border-radius: ${theme.layout.radius};
box-shadow: ${theme.expressiveness.shadowLarge};
background-color: ${theme.palette.background};
max-height: 15rem;
max-height: 17em;
overflow-y: auto;
overflow-anchor: none;
padding: ${theme.layout.gapQuarter} 0;
padding: 0.38em 0;
}
`}</style>
</div>
@@ -55,4 +54,6 @@ const SelectDropdown: React.FC<React.PropsWithChildren<SelectDropdownProps>> = (
)
}
export default withDefaults(SelectDropdown, defaultProps)
SelectDropdown.defaultProps = defaultProps
SelectDropdown.displayName = 'GeistSelectDropdown'
export default SelectDropdown

View File

@@ -1,16 +1,12 @@
import React, { useMemo } from 'react'
import React from 'react'
import useTheme from '../use-theme'
interface Props {
onClick?: (event: React.MouseEvent<HTMLDivElement>) => void
heightRatio?: string | undefined
}
const SelectIconClear: React.FC<Props> = ({ onClick, heightRatio }) => {
const SelectIconClear: React.FC<Props> = ({ onClick }) => {
const theme = useTheme()
const width = useMemo(() => {
return heightRatio ? `calc(10.66px * ${heightRatio})` : '18px'
}, [heightRatio])
const clickHandler = (event: React.MouseEvent<HTMLDivElement>) => {
event.preventDefault()
event.stopPropagation()
@@ -33,8 +29,7 @@ const SelectIconClear: React.FC<Props> = ({ onClick, heightRatio }) => {
<style jsx>{`
.clear-icon {
padding: 0;
padding-left: ${theme.layout.gapQuarter};
padding: 0 0 0 0.5em;
margin: 0;
display: inline-flex;
align-items: center;
@@ -53,8 +48,8 @@ const SelectIconClear: React.FC<Props> = ({ onClick, heightRatio }) => {
svg {
color: currentColor;
width: ${width};
height: ${width};
width: 1em;
height: 1em;
}
`}</style>
</div>

View File

@@ -1,22 +1,9 @@
import React from 'react'
import withDefaults from '../utils/with-defaults'
interface Props {
width?: string
}
const defaultProps = {
width: '1.25em',
}
export type SelectIconProps = Props & typeof defaultProps
const SelectIcon: React.FC<SelectIconProps> = ({ width }) => {
const SelectIconComponent: React.FC<unknown> = () => {
return (
<svg
viewBox="0 0 24 24"
width={width}
height={width}
strokeWidth="1"
strokeLinecap="round"
strokeLinejoin="round"
@@ -28,12 +15,14 @@ const SelectIcon: React.FC<SelectIconProps> = ({ width }) => {
color: inherit;
stroke: currentColor;
transition: all 200ms ease;
width: 1.214em;
height: 1.214em;
}
`}</style>
</svg>
)
}
const MemoSelectIcon = React.memo(SelectIcon)
export default withDefaults(MemoSelectIcon, defaultProps)
SelectIconComponent.displayName = 'GeistSelectIcon'
const SelectIcon = React.memo(SelectIconComponent)
export default SelectIcon

View File

@@ -6,12 +6,10 @@ import SelectClearIcon from './select-icon-clear'
interface Props {
disabled: boolean
onClear: (() => void) | null
size: string
}
const SelectMultipleValue: React.FC<React.PropsWithChildren<Props>> = ({
disabled,
size,
onClear,
children,
}) => {
@@ -21,17 +19,17 @@ const SelectMultipleValue: React.FC<React.PropsWithChildren<Props>> = ({
<Grid>
<div className="item">
{children}
{!!onClear && <SelectClearIcon heightRatio="1.5" onClick={onClear} />}
{!!onClear && <SelectClearIcon onClick={onClear} />}
</div>
<style jsx>{`
.item {
display: inline-flex;
height: calc(${size} * 2);
justify-items: center;
align-items: center;
line-height: 1;
padding: 0 0.5rem;
font-size: ${size};
padding: 0 0.5em;
font-size: var(--select-font-size);
height: calc(var(--select-font-size) * 2);
border-radius: ${theme.layout.radius};
background-color: ${theme.palette.accents_2};
color: ${disabled ? theme.palette.accents_4 : theme.palette.accents_6};
@@ -50,4 +48,5 @@ const SelectMultipleValue: React.FC<React.PropsWithChildren<Props>> = ({
)
}
SelectMultipleValue.displayName = 'GeistSelectMultipleValue'
export default SelectMultipleValue

View File

@@ -1,9 +1,9 @@
import React, { useMemo } from 'react'
import withDefaults from '../utils/with-defaults'
import useTheme from '../use-theme'
import { useSelectContext } from './select-context'
import useWarning from '../utils/use-warning'
import Ellipsis from '../shared/ellipsis'
import useScaleable, { withScaleable } from '../use-scaleable'
interface Props {
value?: string
@@ -23,9 +23,9 @@ const defaultProps = {
}
type NativeAttrs = Omit<React.HTMLAttributes<any>, keyof Props>
export type SelectOptionProps = Props & typeof defaultProps & NativeAttrs
export type SelectOptionProps = Props & NativeAttrs
const SelectOption: React.FC<React.PropsWithChildren<SelectOptionProps>> = ({
const SelectOptionComponent: React.FC<React.PropsWithChildren<SelectOptionProps>> = ({
value: identValue,
className,
children,
@@ -34,8 +34,9 @@ const SelectOption: React.FC<React.PropsWithChildren<SelectOptionProps>> = ({
label,
preventAllEvents,
...props
}) => {
}: React.PropsWithChildren<SelectOptionProps> & typeof defaultProps) => {
const theme = useTheme()
const { SCALES } = useScaleable()
const { updateValue, value, disableAll } = useSelectContext()
const isDisabled = useMemo(() => disabled || disableAll, [disabled, disableAll])
const isLabel = useMemo(() => label || divider, [label, divider])
@@ -93,15 +94,18 @@ const SelectOption: React.FC<React.PropsWithChildren<SelectOptionProps>> = ({
justify-content: flex-start;
align-items: center;
font-weight: normal;
font-size: 0.75rem;
height: calc(1.688 * ${theme.layout.gap});
padding: 0 ${theme.layout.gapHalf};
background-color: ${bgColor};
color: ${color};
user-select: none;
border: 0;
cursor: ${isDisabled ? 'not-allowed' : 'pointer'};
transition: background 0.2s ease 0s, border-color 0.2s ease 0s;
--select-font-size: ${SCALES.font(0.75)};
font-size: var(--select-font-size);
width: ${SCALES.width(1, '100%')};
height: ${SCALES.height(2.25)};
padding: ${SCALES.pt(0)} ${SCALES.pr(0.667)} ${SCALES.pb(0)} ${SCALES.pl(0.667)};
margin: ${SCALES.mt(0)} ${SCALES.mr(0)} ${SCALES.mb(0)} ${SCALES.ml(0)};
}
.option:hover {
@@ -111,24 +115,29 @@ const SelectOption: React.FC<React.PropsWithChildren<SelectOptionProps>> = ({
.divider {
line-height: 0;
height: 0;
padding: 0;
overflow: hidden;
border-top: 1px solid ${theme.palette.border};
margin: 0.5rem 0;
width: 100%;
width: ${SCALES.width(1, '100%')};
height: ${SCALES.height(1, 0)};
padding: ${SCALES.pt(0)} ${SCALES.pr(0)} ${SCALES.pb(0)} ${SCALES.pl(0)};
margin: ${SCALES.mt(0.5)} ${SCALES.mr(0)} ${SCALES.mb(0.5)} ${SCALES.ml(0)};
}
.label {
font-size: 0.875rem;
color: ${theme.palette.accents_7};
border-bottom: 1px solid ${theme.palette.border};
text-transform: capitalize;
cursor: default;
font-size: ${SCALES.font(0.875)};
width: ${SCALES.width(1, '100%')};
font-weight: 500;
}
`}</style>
</>
)
}
export default withDefaults(SelectOption, defaultProps)
SelectOptionComponent.defaultProps = defaultProps
SelectOptionComponent.displayName = 'GeistSelectOption'
const SelectOption = withScaleable(SelectOptionComponent)
export default SelectOption

View File

@@ -1,21 +1,20 @@
import React, { CSSProperties, useEffect, useMemo, useRef, useState } from 'react'
import { NormalSizes, NormalTypes } from '../utils/prop-types'
import { NormalTypes } from '../utils/prop-types'
import useTheme from '../use-theme'
import useClickAway from '../utils/use-click-away'
import useCurrentState from '../utils/use-current-state'
import { pickChildByProps } from '../utils/collections'
import SelectIcon from './select-icon'
import SelectOption from './select-option'
import SelectDropdown from './select-dropdown'
import SelectMultipleValue from './select-multiple-value'
import Grid from '../grid'
import { SelectContext, SelectConfig } from './select-context'
import { getColors, getSizes } from './styles'
import { getColors } from './styles'
import Ellipsis from '../shared/ellipsis'
import useScaleable, { withScaleable } from '../use-scaleable'
interface Props {
disabled?: boolean
size?: NormalSizes
type?: NormalTypes
value?: string | string[]
initialValue?: string | string[]
@@ -26,7 +25,6 @@ interface Props {
multiple?: boolean
clearable?: boolean
className?: string
width?: string
dropdownClassName?: string
dropdownStyle?: CSSProperties
disableMatchWidth?: boolean
@@ -35,23 +33,20 @@ interface Props {
const defaultProps = {
disabled: false,
size: 'medium' as NormalSizes,
type: 'default' as NormalTypes,
icon: SelectIcon as React.ComponentType,
pure: false,
multiple: false,
clearable: true,
width: 'initial',
className: '',
disableMatchWidth: false,
}
type NativeAttrs = Omit<React.HTMLAttributes<any>, keyof Props>
export type SelectProps = Props & typeof defaultProps & NativeAttrs
export type SelectProps = Props & NativeAttrs
const Select: React.FC<React.PropsWithChildren<SelectProps>> = ({
const SelectComponent: React.FC<React.PropsWithChildren<SelectProps>> = ({
children,
size,
type,
disabled,
initialValue: init,
@@ -62,15 +57,15 @@ const Select: React.FC<React.PropsWithChildren<SelectProps>> = ({
multiple,
clearable,
placeholder,
width,
className,
dropdownClassName,
dropdownStyle,
disableMatchWidth,
getPopupContainer,
...props
}) => {
}: React.PropsWithChildren<SelectProps> & typeof defaultProps) => {
const theme = useTheme()
const { SCALES } = useScaleable()
const ref = useRef<HTMLDivElement>(null)
const [visible, setVisible] = useState<boolean>(false)
const [value, setValue, valueRef] = useCurrentState<string | string[] | undefined>(
@@ -84,7 +79,6 @@ const Select: React.FC<React.PropsWithChildren<SelectProps>> = ({
if (!Array.isArray(value)) return !value
return value.length === 0
}, [value])
const sizes = useMemo(() => getSizes(theme, size), [theme, size])
const { border, borderHover, iconBorder, placeholderColor } = useMemo(
() => getColors(theme.palette, type),
@@ -110,11 +104,10 @@ const Select: React.FC<React.PropsWithChildren<SelectProps>> = ({
visible,
updateValue,
updateVisible,
size,
ref,
disableAll: disabled,
}),
[visible, size, disabled, ref, value, multiple],
[visible, disabled, ref, value, multiple],
)
const clickHandler = (event: React.MouseEvent<HTMLDivElement>) => {
@@ -139,7 +132,6 @@ const Select: React.FC<React.PropsWithChildren<SelectProps>> = ({
if (!multiple) return el
return (
<SelectMultipleValue
size={sizes.fontSize}
disabled={disabled}
onClear={clearable ? () => updateValue(child.props.value) : null}>
{el}
@@ -157,7 +149,7 @@ const Select: React.FC<React.PropsWithChildren<SelectProps>> = ({
{...props}>
{isEmpty && (
<span className="value placeholder">
<Ellipsis height={sizes.height}>{placeholder}</Ellipsis>
<Ellipsis height="var(--select-height)">{placeholder}</Ellipsis>
</span>
)}
{value && !multiple && <span className="value">{selectedChild}</span>}
@@ -183,26 +175,31 @@ const Select: React.FC<React.PropsWithChildren<SelectProps>> = ({
white-space: nowrap;
position: relative;
cursor: ${disabled ? 'not-allowed' : 'pointer'};
max-width: 80vw;
width: ${width};
max-width: 90vw;
overflow: hidden;
transition: border 150ms ease-in 0s, color 200ms ease-out 0s,
box-shadow 200ms ease 0s;
border: 1px solid ${border};
border-radius: ${theme.layout.radius};
padding: 0 ${theme.layout.gapQuarter} 0 ${theme.layout.gapHalf};
height: ${sizes.height};
min-width: ${sizes.minWidth};
background-color: ${disabled
? theme.palette.accents_1
: theme.palette.background};
--select-font-size: ${SCALES.font(0.875)};
--select-height: ${SCALES.height(2.25)};
min-width: 11.5em;
width: ${SCALES.width(1, 'initial')};
height: var(--select-height);
padding: ${SCALES.pt(0)} ${SCALES.pr(0.334)} ${SCALES.pb(0)}
${SCALES.pl(0.667)};
margin: ${SCALES.mt(0)} ${SCALES.mr(0)} ${SCALES.mb(0)} ${SCALES.ml(0)};
}
.multiple {
height: auto;
min-height: ${sizes.height};
padding: ${theme.layout.gapQuarter} calc(${sizes.fontSize} * 2)
${theme.layout.gapQuarter} ${theme.layout.gapHalf};
min-height: var(--select-height);
padding: ${SCALES.pt(0.334)} ${SCALES.pr(0.334)} ${SCALES.pb(0.334)}
${SCALES.pl(0.667)};
}
.select:hover {
@@ -220,10 +217,10 @@ const Select: React.FC<React.PropsWithChildren<SelectProps>> = ({
align-items: center;
line-height: 1;
padding: 0;
margin-right: 1.25rem;
font-size: ${sizes.fontSize};
margin-right: 1.25em;
font-size: var(--select-font-size);
color: ${disabled ? theme.palette.accents_4 : theme.palette.foreground};
width: calc(100% - 1.25rem);
width: calc(100% - 1.25em);
}
.value > :global(div),
@@ -242,7 +239,7 @@ const Select: React.FC<React.PropsWithChildren<SelectProps>> = ({
.icon {
position: absolute;
right: ${theme.layout.gapQuarter};
font-size: ${sizes.fontSize};
font-size: var(--select-font-size);
top: 50%;
bottom: 0;
transform: translateY(-50%) rotate(${visible ? '180' : '0'}deg);
@@ -258,13 +255,7 @@ const Select: React.FC<React.PropsWithChildren<SelectProps>> = ({
)
}
type SelectComponent<P = {}> = React.FC<P> & {
Option: typeof SelectOption
}
type ComponentProps = Partial<typeof defaultProps> &
Omit<Props, keyof typeof defaultProps> &
NativeAttrs
;(Select as SelectComponent<ComponentProps>).defaultProps = defaultProps
export default Select as SelectComponent<ComponentProps>
SelectComponent.defaultProps = defaultProps
SelectComponent.displayName = 'GeistSelect'
const Select = withScaleable(SelectComponent)
export default Select

View File

@@ -1,38 +1,5 @@
import { NormalSizes, NormalTypes } from '../utils/prop-types'
import { GeistUIThemes, GeistUIThemesPalette } from '../themes/presets'
export interface SelectSize {
height: string
fontSize: string
minWidth: string
}
export const getSizes = (theme: GeistUIThemes, size?: NormalSizes) => {
const sizes: { [key in NormalSizes]: SelectSize } = {
medium: {
height: `calc(1.688 * ${theme.layout.gap})`,
fontSize: '.875rem',
minWidth: '10rem',
},
small: {
height: `calc(1.344 * ${theme.layout.gap})`,
fontSize: '.75rem',
minWidth: '8rem',
},
mini: {
height: `calc(1 * ${theme.layout.gap})`,
fontSize: '.75rem',
minWidth: '6.5rem',
},
large: {
height: `calc(2 * ${theme.layout.gap})`,
fontSize: '1.225rem',
minWidth: '12.5rem',
},
}
return size ? sizes[size] : sizes.medium
}
import { NormalTypes } from '../utils/prop-types'
import { GeistUIThemesPalette } from '../themes/presets'
export type SelectColor = {
border: string