feat: add support for setting generic font family at both theme and component-level

This commit is contained in:
Hossein Mehrabi 2023-04-14 01:34:15 +03:30
parent 5bd80f56ea
commit 45076179ce
43 changed files with 578 additions and 304 deletions

View File

@ -1,12 +1,12 @@
import { DecoratorFunction, Parameters } from '@storybook/addons'
import {
storybookDefaultTheme,
storybookDefaultThemeKey,
themes,
} from './themes'
import { ArgTypes } from '@storybook/react'
import { THEME_TYPOGRAPHY_FONT_CATEGORIES } from '../src/components/Theme/constants'
import { storybookThemes } from './themes'
import { GlobalTypes } from './types'
import { withTheme } from './withTheme.decorator'
export const parameters: Parameters = {
...storybookThemes.parameters,
actions: { argTypesRegex: '^on[A-Z].*' },
controls: {
matchers: {
@ -14,17 +14,10 @@ export const parameters: Parameters = {
date: /Date$/,
},
},
backgrounds: {
default: 'light',
values: Object.entries(themes).map(([name, theme]) => ({
name,
value: `rgb(${theme.palette.secondary})`,
})),
},
viewport: {
viewports: {
...Object.fromEntries(
Object.entries(storybookDefaultTheme.breakpoints).map(
Object.entries(storybookThemes.defaultTheme.breakpoints).map(
([key, { width }]) => [
key,
{
@ -44,18 +37,16 @@ export const parameters: Parameters = {
export const decorators: DecoratorFunction[] = [withTheme]
export const globalTypes = {
theme: {
name: 'Theme',
description: 'Theme',
defaultValue: storybookDefaultThemeKey,
toolbar: {
icon: 'circlehollow',
items: Object.entries(themes).map(([name, theme]) => ({
value: name,
icon: name.startsWith('Light') ? 'circlehollow' : 'circle',
title: name,
})),
export const argTypes: ArgTypes = {
genericFontFamily: {
type: {
name: 'enum',
value: THEME_TYPOGRAPHY_FONT_CATEGORIES,
},
defaultValue: 'inherit',
},
}
export const globalTypes: GlobalTypes = {
...storybookThemes.globalTypes,
}

View File

@ -1,44 +1,106 @@
import { Parameters } from '@storybook/addons'
import { StoryContext } from '@storybook/react'
import { createTheme, CreateThemeProps, defaultThemes } from '../src'
import { THEME_TYPOGRAPHY_FONT_CATEGORIES } from '../src/components/Theme/constants'
import { GlobalTypes } from './types'
const themeProps: CreateThemeProps = {
typography: {
body3: {
fontFamily: 'sans-serif',
},
},
typography: {},
breakpoints: {},
palette: {},
typographyGlobal: {},
}
const typefaceTypes: Record<string, CreateThemeProps['typographyGlobal']> = {
'sans-serif': {
fontFamily: 'Helvetica, sans-serif',
},
serif: {
fontFamily: 'Georgia, serif',
},
monospace: {
fontFamily: 'Courier, monospace',
},
}
const createThemes = () => {
const fonts = THEME_TYPOGRAPHY_FONT_CATEGORIES.slice(1)
export const themesArray = Object.keys(defaultThemes).flatMap((key) =>
Object.entries(typefaceTypes).map(([typeFace, typographyGlobal]) =>
const themes = fonts.map((font) => [
createTheme(
{
name: `${defaultThemes[key].name} (${typeFace})`,
name: `${defaultThemes.light.name} (${font})`,
...themeProps,
typographyGlobal,
typographyGlobal: {
genericFontFamily: font,
},
},
defaultThemes[key],
defaultThemes.light,
),
),
)
createTheme(
{
name: `${defaultThemes.dark.name} (${font})`,
...themeProps,
typographyGlobal: {
genericFontFamily: font,
},
},
defaultThemes.dark,
),
])
export const themes = Object.fromEntries(
themesArray.map((theme) => [theme.name, theme]),
)
const getTheme = (context: StoryContext) => {
const themeColor = context.globals?.themeColor ?? 'Light'
const themeFont = context.globals?.themeFont ?? fonts[0]
export const storybookDefaultThemeKey = themesArray[0].name
export const storybookDefaultTheme = themesArray[0]
return themes[fonts.findIndex((font) => font === themeFont)][
themeColor === 'Light' ? 0 : 1
]
}
return {
getTheme,
defaultTheme: themes[0][0],
parameters: {
backgrounds: {
default: 'Light',
values: [
{
name: 'Light',
value: `rgb(${defaultThemes.light.palette.secondary})`,
},
{
name: 'Dark',
value: `rgb(${defaultThemes.dark.palette.secondary})`,
},
],
},
} as Parameters,
globalTypes: {
themeColor: {
name: 'Theme Color',
description: 'Theme Color',
defaultValue: 'Light',
toolbar: {
title: ' Theme Color',
icon: 'circlehollow',
items: [
{
title: 'Light',
value: 'Light',
icon: 'circlehollow',
},
{
title: 'Dark',
value: 'Dark',
icon: 'circle',
},
],
},
},
themeFont: {
name: 'Theme Font',
description: 'Theme Font',
defaultValue: THEME_TYPOGRAPHY_FONT_CATEGORIES[1],
toolbar: {
title: ' Theme Font',
items: fonts.map((font) => ({
title: font,
value: font,
})),
},
},
} as GlobalTypes,
}
}
export const storybookThemes = createThemes()

View File

@ -0,0 +1,4 @@
import { ToolbarArgType } from '@storybook/addon-toolbars/dist/ts3.9/types'
import { ArgTypes } from '@storybook/react'
export type GlobalTypes = ArgTypes<Partial<ToolbarArgType>>

View File

@ -1,19 +1,18 @@
import { DecoratorFunction, useGlobals } from '@storybook/addons'
import React, { useEffect } from 'react'
import { Theme, ThemeProvider } from '../src'
import { storybookDefaultThemeKey, themes } from './themes'
import { ThemeProvider } from '../src'
import { storybookThemes } from './themes'
export const withTheme: DecoratorFunction = (Story, context) => {
const StoryComponent = Story as any as React.ComponentType
const themeName = context.globals?.theme ?? storybookDefaultThemeKey
const theme = themes[themeName] as Theme
const theme = storybookThemes.getTheme(context)
const [globals, setGlobals] = useGlobals()
useEffect(() => {
const background = (context.parameters.backgrounds?.values ?? []).find(
(value) => value.name === themeName,
(value) => theme.name.startsWith(value.name),
)?.value
globals.backgrounds?.value !== background &&

View File

@ -7,11 +7,10 @@ import { DropdownMenu } from '../DropdownMenu'
import { Portal } from '../PortalProvider/Portal'
import { Typography } from '../Typography'
import { autocompleteClasses } from './Autocomplete.classes'
import { CommonProps, useCommonProps } from '../../utils/useCommonProps'
export type AutocompleteProps = Omit<
React.HTMLAttributes<HTMLDivElement>,
'onChange' | 'value'
> &
export type AutocompleteProps = CommonProps &
Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange' | 'value'> &
Pick<React.InputHTMLAttributes<HTMLInputElement>, 'onChange'> & {
label?: React.ReactNode
size?: 'large' | 'medium' | 'small'
@ -44,6 +43,7 @@ export const Autocomplete: React.FC<AutocompleteProps> & {
variant = 'outlined',
...props
}) => {
const commonProps = useCommonProps(props)
const ref = useRef<HTMLInputElement>(null)
const containerRef = useRef<HTMLDivElement>(null)
const input = useInput({ defaultValue, value, onChange, ref })
@ -89,6 +89,7 @@ export const Autocomplete: React.FC<AutocompleteProps> & {
ref={containerRef}
className={clsx(
props.className,
commonProps.className,
autocompleteClasses.root,
autocompleteClasses[size],
disabled && autocompleteClasses.disabled,
@ -141,6 +142,7 @@ export const Autocomplete: React.FC<AutocompleteProps> & {
open={isOpen}
onClose={() => setOpen(false)}
size={size}
genericFontFamily={props.genericFontFamily}
>
{suggestions.map((opt, idx: number) => (
<DropdownItem

View File

@ -1,15 +1,17 @@
import clsx from 'clsx'
import React from 'react'
import { CommonProps, useCommonProps } from '../../utils/useCommonProps'
import { Typography } from '../Typography'
import { badgeClasses } from './Badge.classes'
export type BadgeProps = React.HTMLAttributes<HTMLDivElement> & {
variant?: 'outlined' | 'filled'
icon?: React.ReactNode
iconDirection?: 'left' | 'right'
size?: 'large' | 'small'
disabled?: boolean
}
export type BadgeProps = CommonProps &
React.HTMLAttributes<HTMLDivElement> & {
variant?: 'outlined' | 'filled'
icon?: React.ReactNode
iconDirection?: 'left' | 'right'
size?: 'large' | 'small'
disabled?: boolean
}
export const Badge: React.FC<BadgeProps> & {
classes: typeof badgeClasses
@ -22,12 +24,15 @@ export const Badge: React.FC<BadgeProps> & {
children,
...props
}) => {
const commonProps = useCommonProps(props)
return (
<div
aria-label={children as string}
{...props}
className={clsx(
props.className,
commonProps.className,
badgeClasses.root,
badgeClasses[variant],
disabled && badgeClasses.disabled,

View File

@ -1,5 +1,6 @@
import clsx from 'clsx'
import React, { useEffect, useRef, useState } from 'react'
import { CommonProps, useCommonProps } from '../../utils/useCommonProps'
import { BreadcrumbItem } from '../BreadcrumbItem'
import { breadcrumbItemClasses } from '../BreadcrumbItem/BreadcrumbItem.classes'
import { DropdownMenu } from '../DropdownMenu'
@ -15,18 +16,19 @@ export type BreadcrumbOption = {
>
}
export type BreadcrumbProps = Omit<
React.HTMLAttributes<HTMLDivElement>,
'label' | 'disabled' | 'value' | 'onChange'
> & {
disabled?: boolean
ellipsis?: boolean
maxItems?: number
options?: BreadcrumbOption[]
value?: string | string[]
onChange?: (value: string | string[]) => void
size?: 'small' | 'large'
}
export type BreadcrumbProps = CommonProps &
Omit<
React.HTMLAttributes<HTMLDivElement>,
'label' | 'disabled' | 'value' | 'onChange'
> & {
disabled?: boolean
ellipsis?: boolean
maxItems?: number
options?: BreadcrumbOption[]
value?: string | string[]
onChange?: (value: string | string[]) => void
size?: 'small' | 'large'
}
export const Breadcrumb: React.FC<BreadcrumbProps> & {
classes: typeof breadcrumbClasses
@ -40,6 +42,8 @@ export const Breadcrumb: React.FC<BreadcrumbProps> & {
options = [],
...props
}) => {
const commonProps = useCommonProps(props)
const ellipsisRef = useRef<HTMLLIElement>(null)
const [open, setOpen] = useState<boolean>(false)
@ -78,6 +82,7 @@ export const Breadcrumb: React.FC<BreadcrumbProps> & {
{...props}
className={clsx(
props.className,
commonProps.className,
breadcrumbClasses.root,
disabled && breadcrumbClasses.disabled,
open && breadcrumbClasses.open,
@ -103,6 +108,7 @@ export const Breadcrumb: React.FC<BreadcrumbProps> & {
onClose={() => setOpen(false)}
className={clsx(breadcrumbClasses.listBox)}
size={size}
genericFontFamily={props.genericFontFamily}
>
{collapsed.map((opt, idx) => (
<BreadcrumbItem

View File

@ -1,20 +1,23 @@
import clsx from 'clsx'
import React from 'react'
import { CommonProps, useCommonProps } from '../../utils/useCommonProps'
import { Typography } from '../Typography'
import { breadcrumbItemClasses } from './BreadcrumbItem.classes'
export type BreadcrumbItemProps = React.LiHTMLAttributes<HTMLLIElement> & {
label: string
link?: string
linkComponent?: React.ComponentType<
React.AnchorHTMLAttributes<HTMLAnchorElement>
>
outlined?: boolean
disabled?: boolean
selected?: boolean
ellipsisRef?: React.RefObject<HTMLLIElement>
onClick?: () => void
size?: 'small' | 'large'
}
export type BreadcrumbItemProps = CommonProps &
React.LiHTMLAttributes<HTMLLIElement> & {
label: string
link?: string
linkComponent?: React.ComponentType<
React.AnchorHTMLAttributes<HTMLAnchorElement>
>
outlined?: boolean
disabled?: boolean
selected?: boolean
ellipsisRef?: React.RefObject<HTMLLIElement>
onClick?: () => void
size?: 'small' | 'large'
}
export const BreadcrumbItem: React.FC<BreadcrumbItemProps> & {
classes: typeof breadcrumbItemClasses
@ -30,10 +33,13 @@ export const BreadcrumbItem: React.FC<BreadcrumbItemProps> & {
className,
...props
}) => {
const commonProps = useCommonProps(props)
return (
<li
{...props}
className={clsx(
commonProps.className,
breadcrumbItemClasses.root,
breadcrumbItemClasses[size],
className,

View File

@ -1,20 +1,25 @@
import clsx from 'clsx'
import React from 'react'
import { CommonProps, useCommonProps } from '../../utils/useCommonProps'
import { buttonClasses } from './Button.classes'
export type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {
size?: 'large' | 'medium' | 'small'
icon?: React.ReactNode
}
export type ButtonProps = CommonProps &
React.ButtonHTMLAttributes<HTMLButtonElement> & {
size?: 'large' | 'medium' | 'small'
icon?: React.ReactNode
}
export const Button: React.FC<ButtonProps> & {
classes: typeof buttonClasses
} = ({ size = 'medium', icon, children, ...props }) => {
const commonProps = useCommonProps(props)
return (
<>
<button
{...props}
className={clsx(
commonProps.className,
props.className,
buttonClasses.root,
buttonClasses[size],

View File

@ -1,18 +1,29 @@
import clsx from 'clsx'
import React from 'react'
import { CommonProps, useCommonProps } from '../../utils/useCommonProps'
import { cardClasses } from './Card.classes'
import { CardContext } from './Card.context'
export type CardProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'label'> & {
size?: 'small' | 'medium' | 'large'
}
export type CardProps = CommonProps &
Omit<React.HTMLAttributes<HTMLDivElement>, 'label'> & {
size?: 'small' | 'medium' | 'large'
}
export const Card: React.FC<CardProps> & {
classes: typeof cardClasses
} = ({ size = 'large', children, ...props }) => {
const commonProps = useCommonProps(props)
return (
<CardContext.Provider value={{ size }}>
<div {...props} className={clsx(cardClasses.root, cardClasses[size])}>
<div
{...props}
className={clsx(
commonProps.className,
cardClasses.root,
cardClasses[size],
)}
>
{children}
</div>
</CardContext.Provider>

View File

@ -1,17 +1,25 @@
import clsx from 'clsx'
import React from 'react'
import { CommonProps, useCommonProps } from '../../utils/useCommonProps'
import { cardBodyClasses } from './CardBody.classes'
export type CardBodyProps = Omit<
React.HTMLAttributes<HTMLDivElement>,
'label'
> & {}
export type CardBodyProps = CommonProps &
Omit<React.HTMLAttributes<HTMLDivElement>, 'label'> & {}
export const CardBody: React.FC<CardBodyProps> & {
classes: typeof cardBodyClasses
} = ({ children, ...props }) => {
const commonProps = useCommonProps(props)
return (
<div {...props} className={clsx(props.className, cardBodyClasses.root)}>
<div
{...props}
className={clsx(
commonProps.className,
props.className,
cardBodyClasses.root,
)}
>
{children}
</div>
)

View File

@ -1,19 +1,19 @@
import clsx from 'clsx'
import React from 'react'
import { CommonProps, useCommonProps } from '../../utils/useCommonProps'
import { useCardContext } from '../Card/Card.context'
import { Typography } from '../Typography'
import { cardHeaderClasses } from './CardHeader.classes'
export type CardHeaderProps = Omit<
React.HTMLAttributes<HTMLDivElement>,
'label'
> & {
size?: 'small' | 'medium' | 'large'
}
export type CardHeaderProps = CommonProps &
Omit<React.HTMLAttributes<HTMLDivElement>, 'label'> & {
size?: 'small' | 'medium' | 'large'
}
export const CardHeader: React.FC<CardHeaderProps> & {
classes: typeof cardHeaderClasses
} = ({ size: _size = 'large', children, ...props }) => {
const commonProps = useCommonProps(props)
const sizeContext = useCardContext()
const size = sizeContext?.size ?? _size
@ -21,6 +21,7 @@ export const CardHeader: React.FC<CardHeaderProps> & {
<div
{...props}
className={clsx(
commonProps.className,
props.className,
cardHeaderClasses.root,
cardHeaderClasses[size],

View File

@ -1,5 +1,6 @@
import clsx from 'clsx'
import React, { useEffect, useRef, useState } from 'react'
import { CommonProps, useCommonProps } from '../../utils/useCommonProps'
import { useInput } from '../../utils/useInput'
import { useCheckboxGroupContext } from '../CheckboxGroup/CheckboxGroup.context'
import { CheckboxFilledIcon, CheckboxIcon } from '../Icons'
@ -7,10 +8,11 @@ import { CheckboxIndeterminateIcon } from '../Icons/CheckboxIndeterminate'
import { Typography } from '../Typography'
import { checkboxClasses } from './Checkbox.classes'
export type CheckboxProps = Omit<
React.LabelHTMLAttributes<HTMLLabelElement>,
'onChange' | 'value' | 'color'
> &
export type CheckboxProps = CommonProps &
Omit<
React.LabelHTMLAttributes<HTMLLabelElement>,
'onChange' | 'value' | 'color'
> &
Pick<
React.InputHTMLAttributes<HTMLInputElement>,
'name' | 'onChange' | 'checked' | 'defaultChecked'
@ -35,6 +37,7 @@ export const Checkbox: React.FC<CheckboxProps> & {
children,
...props
}) => {
const commonProps = useCommonProps(props)
const ref = useRef<HTMLInputElement>(null)
const [focused, setFocused] = useState(false)
const input = useInput({
@ -70,6 +73,7 @@ export const Checkbox: React.FC<CheckboxProps> & {
aria-disabled={disabled ? 'true' : 'false'}
{...props}
className={clsx(
commonProps.className,
props.className,
checkboxClasses.root,
checkboxClasses[size],

View File

@ -3,22 +3,30 @@ import React from 'react'
import { CheckboxGroupContext } from './CheckboxGroup.context'
import { checkboxGroupClasses } from './CheckboxGroup.classes'
import { Typography } from '../Typography'
import { CommonProps, useCommonProps } from '../../utils/useCommonProps'
export type ActiveCheckboxType = string | number | readonly string[]
export type CheckboxGroupProps = React.HTMLAttributes<HTMLDivElement> & {
size?: 'small' | 'medium' | 'large'
label?: string
}
export type CheckboxGroupProps = CommonProps &
React.HTMLAttributes<HTMLDivElement> & {
size?: 'small' | 'medium' | 'large'
label?: string
}
export const CheckboxGroup: React.FC<CheckboxGroupProps> & {
classes: typeof checkboxGroupClasses
} = ({ size = 'large', label, children, ...props }) => {
const commonProps = useCommonProps(props)
return (
<CheckboxGroupContext.Provider value={{ size }}>
<div
{...props}
className={clsx(props.className, checkboxGroupClasses.root)}
className={clsx(
commonProps.className,
props.className,
checkboxGroupClasses.root,
)}
>
<Typography
component="span"

View File

@ -1,22 +1,22 @@
import clsx from 'clsx'
import React, { useEffect, useRef, useState } from 'react'
import { CommonProps, useCommonProps } from '../../utils/useCommonProps'
import { CollapseHeader } from '../CollapseHeader'
import { collapseClasses } from './Collapse.classes'
export type CollapseProps = Omit<
React.HTMLAttributes<HTMLDivElement>,
'label'
> & {
label: string
disabled?: boolean
size?: 'small' | 'medium' | 'large'
open?: boolean
onChange?: (open: boolean) => void
}
export type CollapseProps = CommonProps &
Omit<React.HTMLAttributes<HTMLDivElement>, 'label'> & {
label: string
disabled?: boolean
size?: 'small' | 'medium' | 'large'
open?: boolean
onChange?: (open: boolean) => void
}
export const Collapse: React.FC<CollapseProps> & {
classes: typeof collapseClasses
} = ({ label, disabled = false, size = 'large', children, ...props }) => {
const globalProps = useCommonProps(props)
const ref = useRef<HTMLDivElement>(null)
const [open, setOpen] = useState(props.open ?? false)
@ -36,6 +36,7 @@ export const Collapse: React.FC<CollapseProps> & {
{...props}
ref={ref}
className={clsx(
globalProps.className,
props.className,
collapseClasses.root,
disabled && collapseClasses.disabled,

View File

@ -1,20 +1,19 @@
import clsx from 'clsx'
import React from 'react'
import { CommonProps, useCommonProps } from '../../utils/useCommonProps'
import { ArrowDownIcon, ArrowUpIcon } from '../Icons'
import { Typography } from '../Typography'
import { collapseHeaderClasses } from './CollapseHeader.classes'
export type CollapseHeaderProps = Omit<
React.HTMLAttributes<HTMLDivElement>,
'label' | 'disabled'
> & {
label: string
open: boolean
setOpen: React.Dispatch<React.SetStateAction<boolean>>
disabled?: boolean
onTrigger: () => void
size?: 'small' | 'medium' | 'large'
}
export type CollapseHeaderProps = CommonProps &
Omit<React.HTMLAttributes<HTMLDivElement>, 'label' | 'disabled'> & {
label: string
open: boolean
setOpen: React.Dispatch<React.SetStateAction<boolean>>
disabled?: boolean
onTrigger: () => void
size?: 'small' | 'medium' | 'large'
}
export const CollapseHeader: React.FC<CollapseHeaderProps> & {
classes: typeof collapseHeaderClasses
@ -27,10 +26,13 @@ export const CollapseHeader: React.FC<CollapseHeaderProps> & {
onTrigger,
...props
}) => {
const commonProps = useCommonProps(props)
return (
<div
{...props}
className={clsx(
commonProps.className,
props.className,
collapseHeaderClasses.root,
collapseHeaderClasses[size],

View File

@ -1,32 +1,34 @@
import clsx from 'clsx'
import React, { useEffect, useRef, useState } from 'react'
import { CommonProps, useCommonProps } from '../../utils/useCommonProps'
import { SelectOption, useSelect } from '../../utils/useSelect'
import { DropdownItem } from '../DropdownItem'
import { ArrowDownIcon, ArrowUpIcon, ErrorIcon } from '../Icons'
import { DropdownMenu } from '../DropdownMenu'
import { ArrowDownIcon, ArrowUpIcon, ErrorIcon } from '../Icons'
import { Portal } from '../PortalProvider/Portal'
import { Typography } from '../Typography'
import { dropdownClasses } from './Dropdown.classes'
export type DropdownOption = SelectOption
export type DropdownProps = Omit<
React.HTMLAttributes<HTMLDivElement>,
'label' | 'disabled' | 'value' | 'onChange'
> & {
label?: React.ReactNode
error?: boolean
disabled?: boolean
supportingText?: string
size?: 'small' | 'medium' | 'large'
triggerLabel: string
export type DropdownProps = CommonProps &
Omit<
React.HTMLAttributes<HTMLDivElement>,
'label' | 'disabled' | 'value' | 'onChange'
> & {
label?: React.ReactNode
error?: boolean
disabled?: boolean
supportingText?: string
size?: 'small' | 'medium' | 'large'
triggerLabel?: string
multi?: boolean
options?: DropdownOption[]
value?: string | string[]
onChange?: (value: string | string[]) => void
variant?: 'outlined' | 'outlined-bottom'
}
multi?: boolean
options?: DropdownOption[]
value?: string | string[]
onChange?: (value: string | string[]) => void
variant?: 'outlined' | 'outlined-bottom'
}
export const Dropdown: React.FC<DropdownProps> & {
classes: typeof dropdownClasses
@ -45,6 +47,7 @@ export const Dropdown: React.FC<DropdownProps> & {
variant = 'outlined',
...props
}) => {
const commonProps = useCommonProps(props)
const containerRef = useRef<HTMLDivElement>(null)
const [open, setOpen] = useState(false)
@ -71,6 +74,7 @@ export const Dropdown: React.FC<DropdownProps> & {
ref={containerRef}
{...props}
className={clsx(
commonProps.className,
props.className,
dropdownClasses.root,
dropdownClasses[size],
@ -143,6 +147,7 @@ export const Dropdown: React.FC<DropdownProps> & {
open={open}
onClose={() => setOpen(false)}
size={size}
genericFontFamily={props.genericFontFamily}
>
{options.map((opt) => (
<DropdownItem

View File

@ -1,19 +1,18 @@
import clsx from 'clsx'
import React from 'react'
import { CommonProps, useCommonProps } from '../../utils/useCommonProps'
import { CheckboxFilledIcon, CheckboxIcon, LsdIconProps } from '../Icons'
import { Typography } from '../Typography'
import { dropdownItemClasses } from './DropdownItem.classes'
export type DropdownItemProps = Omit<
React.HTMLAttributes<HTMLDivElement>,
'label'
> & {
label: React.ReactNode
selected?: boolean
withIcon?: boolean
disabled?: boolean
size: 'small' | 'medium' | 'large'
}
export type DropdownItemProps = CommonProps &
Omit<React.HTMLAttributes<HTMLDivElement>, 'label'> & {
label: React.ReactNode
selected?: boolean
withIcon?: boolean
disabled?: boolean
size: 'small' | 'medium' | 'large'
}
export const DropdownItem: React.FC<DropdownItemProps> & {
classes: typeof dropdownItemClasses
@ -26,6 +25,8 @@ export const DropdownItem: React.FC<DropdownItemProps> & {
className,
...props
}) => {
const commonProps = useCommonProps(props)
const iconProps: LsdIconProps = {
color: 'primary',
className: dropdownItemClasses.icon,
@ -37,6 +38,7 @@ export const DropdownItem: React.FC<DropdownItemProps> & {
aria-selected={selected ? 'true' : 'false'}
{...props}
className={clsx(
commonProps.className,
className,
dropdownItemClasses.root,
dropdownItemClasses[size],

View File

@ -1,18 +1,17 @@
import clsx from 'clsx'
import React, { useEffect, useRef, useState } from 'react'
import { useClickAway } from 'react-use'
import { CommonProps, useCommonProps } from '../../utils/useCommonProps'
import { dropdownMenuClasses } from './DropdownMenu.classes'
export type DropdownMenuProps = Omit<
React.HTMLAttributes<HTMLUListElement>,
'label'
> & {
open?: boolean
label?: string
size?: 'small' | 'medium' | 'large'
onClose?: () => void
handleRef: React.RefObject<HTMLElement>
}
export type DropdownMenuProps = CommonProps &
Omit<React.HTMLAttributes<HTMLUListElement>, 'label'> & {
open?: boolean
label?: string
size?: 'small' | 'medium' | 'large'
onClose?: () => void
handleRef: React.RefObject<HTMLElement>
}
export const DropdownMenu: React.FC<DropdownMenuProps> & {
classes: typeof dropdownMenuClasses
@ -25,6 +24,7 @@ export const DropdownMenu: React.FC<DropdownMenuProps> & {
children,
...props
}) => {
const commonProps = useCommonProps(props)
const ref = useRef<HTMLUListElement>(null)
const [style, setStyle] = useState<React.CSSProperties>({})
@ -57,6 +57,7 @@ export const DropdownMenu: React.FC<DropdownMenuProps> & {
aria-label={label}
style={{ ...style, ...(props.style ?? {}) }}
className={clsx(
commonProps.className,
props.className,
dropdownMenuClasses.root,
dropdownMenuClasses[size],

View File

@ -1,12 +1,14 @@
import clsx from 'clsx'
import React from 'react'
import { CommonProps, useCommonProps } from '../../utils/useCommonProps'
import { useIconButtonGroupContext } from '../IconButtonGroup/IconButtonGroup.context'
import { iconButtonClasses } from './IconButton.classes'
export type IconButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {
variant?: 'outlined' | 'filled'
size?: 'small' | 'medium' | 'large'
}
export type IconButtonProps = CommonProps &
React.ButtonHTMLAttributes<HTMLButtonElement> & {
variant?: 'outlined' | 'filled'
size?: 'small' | 'medium' | 'large'
}
export const IconButton: React.FC<IconButtonProps> & {
classes: typeof iconButtonClasses
@ -17,6 +19,7 @@ export const IconButton: React.FC<IconButtonProps> & {
children,
...props
}) => {
const commonProps = useCommonProps(props)
const context = useIconButtonGroupContext()
const size = sizeProp ?? context?.size ?? 'large'
@ -27,6 +30,7 @@ export const IconButton: React.FC<IconButtonProps> & {
<button
{...props}
className={clsx(
commonProps.className,
props.className,
iconButtonClasses.root,
iconButtonClasses[size],

View File

@ -1,13 +1,15 @@
import clsx from 'clsx'
import React from 'react'
import { CommonProps, useCommonProps } from '../../utils/useCommonProps'
import { iconButtonGroupClasses } from './IconButtonGroup.classes'
import { IconButtonGroupContext } from './IconButtonGroup.context'
export type IconButtonGroupProps = React.HTMLAttributes<HTMLDivElement> & {
variant?: 'outlined' | 'filled'
size?: 'small' | 'medium' | 'large'
disabled?: boolean
}
export type IconButtonGroupProps = CommonProps &
React.HTMLAttributes<HTMLDivElement> & {
variant?: 'outlined' | 'filled'
size?: 'small' | 'medium' | 'large'
disabled?: boolean
}
export const IconButtonGroup: React.FC<IconButtonGroupProps> & {
classes: typeof iconButtonGroupClasses
@ -18,10 +20,13 @@ export const IconButtonGroup: React.FC<IconButtonGroupProps> & {
children,
...props
}) => {
const commonProps = useCommonProps(props)
return (
<div
{...props}
className={clsx(
commonProps.className,
props.className,
iconButtonGroupClasses.root,
iconButtonGroupClasses[size],

View File

@ -1,11 +1,13 @@
import clsx from 'clsx'
import React from 'react'
import { CommonProps, useCommonProps } from '../../../utils/useCommonProps'
import { lsdIconClasses } from './LsdIcon.classes'
export type LsdIconProps = React.SVGAttributes<SVGElement> & {
size?: 'small'
color?: 'primary' | 'secondary'
}
export type LsdIconProps = CommonProps &
React.SVGAttributes<SVGElement> & {
size?: 'small'
color?: 'primary' | 'secondary'
}
type LsdIconComponent = React.FC<LsdIconProps> & {
classes: typeof lsdIconClasses
@ -24,9 +26,12 @@ export const LsdIcon = (
className,
...props
}) => {
const commonProps = useCommonProps(props)
return (
<Component
className={clsx(
commonProps.className,
className,
lsdIconClasses.root,
lsdIconClasses[size],

View File

@ -1,20 +1,25 @@
import clsx from 'clsx'
import React from 'react'
import { CommonProps, useCommonProps } from '../../utils/useCommonProps'
import { Typography } from '../Typography'
import { quoteClasses } from './Quote.classes'
export type QuoteProps = React.HTMLAttributes<HTMLDivElement> & {
mode?: 'indented-line' | 'parentheses'
}
export type QuoteProps = CommonProps &
React.HTMLAttributes<HTMLDivElement> & {
mode?: 'indented-line' | 'parentheses'
}
export const Quote: React.FC<QuoteProps> & {
classes: typeof quoteClasses
} = ({ mode = 'indented-line', children, ...props }) => {
const commonProps = useCommonProps(props)
return (
<>
<div
{...props}
className={clsx(
commonProps.className,
props.className,
quoteClasses.root,
mode && mode === 'parentheses'

View File

@ -1,15 +1,17 @@
import clsx from 'clsx'
import React, { useRef } from 'react'
import { CommonProps, useCommonProps } from '../../utils/useCommonProps'
import { useInput } from '../../utils/useInput'
import { RadioButtonFilledIcon, RadioButtonIcon } from '../Icons'
import { useRadioButtonGroupContext } from '../RadioButtonGroup/RadioButtonGroup.context'
import { Typography } from '../Typography'
import { radioButtonClasses } from './RadioButton.classes'
export type RadioButtonProps = Omit<
React.LabelHTMLAttributes<HTMLLabelElement>,
'onChange' | 'value' | 'color'
> &
export type RadioButtonProps = CommonProps &
Omit<
React.LabelHTMLAttributes<HTMLLabelElement>,
'onChange' | 'value' | 'color'
> &
Pick<
React.InputHTMLAttributes<HTMLInputElement>,
'onChange' | 'checked' | 'defaultChecked'
@ -35,6 +37,7 @@ export const RadioButton: React.FC<RadioButtonProps> & {
children,
...props
}) => {
const commonProps = useCommonProps(props)
const ref = useRef<HTMLInputElement>(null)
const radioButtonGroup = useRadioButtonGroupContext()
@ -65,6 +68,7 @@ export const RadioButton: React.FC<RadioButtonProps> & {
aria-disabled={disabled ? 'true' : 'false'}
{...props}
className={clsx(
commonProps.className,
props.className,
radioButtonClasses.root,
radioButtonClasses[size],

View File

@ -1,22 +1,25 @@
import clsx from 'clsx'
import React, { useEffect, useRef, useState } from 'react'
import { RadioButtonGroupContext } from './RadioButtonGroup.context'
import { radioButtonGroupClasses } from './RadioButtonGroup.classes'
import { CommonProps, useCommonProps } from '../../utils/useCommonProps'
import { Typography } from '../Typography'
import { radioButtonGroupClasses } from './RadioButtonGroup.classes'
import { RadioButtonGroupContext } from './RadioButtonGroup.context'
export type ActiveRadioButtonType = string | readonly string[]
export type RadioButtonGroupProps = React.HTMLAttributes<HTMLDivElement> & {
value: ActiveRadioButtonType | null
name?: string | null
onChange?: (value: ActiveRadioButtonType) => void
size?: 'small' | 'medium' | 'large'
label?: string
}
export type RadioButtonGroupProps = CommonProps &
React.HTMLAttributes<HTMLDivElement> & {
value: ActiveRadioButtonType | null
name?: string | null
onChange?: (value: ActiveRadioButtonType) => void
size?: 'small' | 'medium' | 'large'
label?: string
}
export const RadioButtonGroup: React.FC<RadioButtonGroupProps> & {
classes: typeof radioButtonGroupClasses
} = ({ size = 'large', label, value, name, onChange, children, ...props }) => {
const commonProps = useCommonProps(props)
const ref = useRef<HTMLDivElement>(null)
const [activeValue, setActiveValue] = useState(value)
@ -34,7 +37,11 @@ export const RadioButtonGroup: React.FC<RadioButtonGroupProps> & {
<div
ref={ref}
{...props}
className={clsx(props.className, radioButtonGroupClasses.root)}
className={clsx(
commonProps.className,
props.className,
radioButtonGroupClasses.root,
)}
>
<Typography
component="span"

View File

@ -1,16 +1,18 @@
import clsx from 'clsx'
import React from 'react'
import { CommonProps, useCommonProps } from '../../utils/useCommonProps'
import { useTabsContext } from '../Tabs/Tab.context'
import { Typography } from '../Typography'
import { tabItemClasses } from './TabItem.classes'
export type TabItemProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {
name: string
selected?: boolean
inactive?: boolean
icon?: React.ReactNode
size?: 'small' | 'medium' | 'large'
}
export type TabItemProps = CommonProps &
React.ButtonHTMLAttributes<HTMLButtonElement> & {
name: string
selected?: boolean
inactive?: boolean
icon?: React.ReactNode
size?: 'small' | 'medium' | 'large'
}
export const TabItem: React.FC<TabItemProps> & {
classes: typeof tabItemClasses
@ -23,6 +25,7 @@ export const TabItem: React.FC<TabItemProps> & {
children,
...props
}) => {
const commonProps = useCommonProps(props)
const tabs = useTabsContext()
const size = tabs?.size ?? _size
const selected = tabs ? tabs.activeTab === name : _selected
@ -38,6 +41,7 @@ export const TabItem: React.FC<TabItemProps> & {
<button
{...props}
className={clsx(
commonProps.className,
props.className,
tabItemClasses.root,
tabItemClasses[size],

View File

@ -1,18 +1,20 @@
import clsx from 'clsx'
import React, { useState } from 'react'
import React from 'react'
import { CommonProps, useCommonProps } from '../../utils/useCommonProps'
import { DropdownOption } from '../Dropdown'
import { TableBody } from '../TableBody'
import { TableHeader } from '../TableHeader'
import { tableClasses } from './Table.classes'
import { TableContext } from './Table.context'
export type TableProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'label'> & {
size?: 'small' | 'medium' | 'large'
type?: 'default' | 'checkbox' | 'radio'
headerOptions?: DropdownOption[]
header?: React.ReactNode
toolbar?: React.ReactNode
}
export type TableProps = CommonProps &
Omit<React.HTMLAttributes<HTMLDivElement>, 'label'> & {
size?: 'small' | 'medium' | 'large'
type?: 'default' | 'checkbox' | 'radio'
headerOptions?: DropdownOption[]
header?: React.ReactNode
toolbar?: React.ReactNode
}
export const Table: React.FC<TableProps> & {
classes: typeof tableClasses
@ -25,9 +27,18 @@ export const Table: React.FC<TableProps> & {
children,
...props
}) => {
const commonProps = useCommonProps(props)
return (
<TableContext.Provider value={{ size, type, headerOptions }}>
<div {...props} className={clsx(tableClasses.root, tableClasses[size])}>
<div
{...props}
className={clsx(
commonProps.className,
tableClasses.root,
tableClasses[size],
)}
>
<TableHeader>{header}</TableHeader>
<TableBody toolbar={toolbar} options={headerOptions}>
{children}

View File

@ -1,17 +1,16 @@
import clsx from 'clsx'
import React from 'react'
import { CommonProps, useCommonProps } from '../../utils/useCommonProps'
import { DropdownOption } from '../Dropdown'
import { tableBodyClasses } from './TableBody.classes'
export type TableBodyProps = Omit<
React.HTMLAttributes<HTMLDivElement>,
'buttonLabel'
> & {
options?: DropdownOption[]
buttonLabel?: 'Button'
size?: 'small' | 'medium' | 'large'
toolbar?: React.ReactNode
}
export type TableBodyProps = CommonProps &
Omit<React.HTMLAttributes<HTMLDivElement>, 'buttonLabel'> & {
options?: DropdownOption[]
buttonLabel?: 'Button'
size?: 'small' | 'medium' | 'large'
toolbar?: React.ReactNode
}
export const TableBody: React.FC<TableBodyProps> & {
classes: typeof tableBodyClasses
@ -23,8 +22,17 @@ export const TableBody: React.FC<TableBodyProps> & {
children,
...props
}) => {
const commonProps = useCommonProps(props)
return (
<div {...props} className={clsx(props.className, tableBodyClasses.root)}>
<div
{...props}
className={clsx(
commonProps.className,
props.className,
tableBodyClasses.root,
)}
>
{toolbar && (
<div className={clsx(tableBodyClasses.toolbar)}>{toolbar}</div>
)}

View File

@ -1,19 +1,27 @@
import clsx from 'clsx'
import React from 'react'
import { CommonProps, useCommonProps } from '../../utils/useCommonProps'
import { tableHeaderClasses } from './TableHeader.classes'
export type TableHeaderProps = Omit<
React.HTMLAttributes<HTMLDivElement>,
'label'
> & {
size?: 'small' | 'medium' | 'large'
}
export type TableHeaderProps = CommonProps &
Omit<React.HTMLAttributes<HTMLDivElement>, 'label'> & {
size?: 'small' | 'medium' | 'large'
}
export const TableHeader: React.FC<TableHeaderProps> & {
classes: typeof tableHeaderClasses
} = ({ size: _size = 'large', children, ...props }) => {
const commonProps = useCommonProps(props)
return (
<div {...props} className={clsx(props.className, tableHeaderClasses.root)}>
<div
{...props}
className={clsx(
commonProps.className,
props.className,
tableHeaderClasses.root,
)}
>
{children}
</div>
)

View File

@ -1,21 +1,26 @@
import clsx from 'clsx'
import React from 'react'
import { CommonProps, useCommonProps } from '../../utils/useCommonProps'
import { useTableContext } from '../Table/Table.context'
import { tableItemClasses } from './TableItem.classes'
export type TableItemProps = React.HTMLAttributes<HTMLDivElement> & {
size?: 'large' | 'medium' | 'small'
}
export type TableItemProps = CommonProps &
React.HTMLAttributes<HTMLDivElement> & {
size?: 'large' | 'medium' | 'small'
}
export const TableItem: React.FC<TableItemProps> & {
classes: typeof tableItemClasses
} = ({ size: _size = 'large', children, ...props }) => {
const commonProps = useCommonProps(props)
const table = useTableContext()
const size = table?.size ?? _size
return (
<td
{...props}
className={clsx(
commonProps.className,
props.className,
tableItemClasses.root,
tableItemClasses[size],

View File

@ -1,15 +1,17 @@
import clsx from 'clsx'
import React from 'react'
import { CommonProps, useCommonProps } from '../../utils/useCommonProps'
import { Checkbox } from '../Checkbox'
import { RadioButton } from '../RadioButton'
import { useTableContext } from '../Table/Table.context'
import { tableItemClasses } from '../TableItem/TableItem.classes'
import { tableRowClasses } from './TableRow.classes'
export type TableRowProps = React.HTMLAttributes<HTMLDivElement> & {
size?: 'large' | 'medium' | 'small'
type?: 'default' | 'checkbox' | 'radio'
}
export type TableRowProps = CommonProps &
React.HTMLAttributes<HTMLDivElement> & {
size?: 'large' | 'medium' | 'small'
type?: 'default' | 'checkbox' | 'radio'
}
export const TableRow: React.FC<TableRowProps> & {
classes: typeof tableRowClasses
@ -19,11 +21,19 @@ export const TableRow: React.FC<TableRowProps> & {
children,
...props
}) => {
const commonProps = useCommonProps(props)
const table = useTableContext()
const type = table?.type ?? _type
return (
<tr {...props} className={clsx(props.className, tableRowClasses.root)}>
<tr
{...props}
className={clsx(
commonProps.className,
props.className,
tableRowClasses.root,
)}
>
{type === 'checkbox' && (
<td className={tableItemClasses.root}>
<Checkbox />

View File

@ -1,21 +1,20 @@
import clsx from 'clsx'
import React, { useEffect, useRef, useState } from 'react'
import { CommonProps, useCommonProps } from '../../utils/useCommonProps'
import { useHorizontalScroll } from '../../utils/useHorizontalScroll'
import { NavigateBeforeIcon, NavigateNextIcon } from '../Icons'
import { TabItem } from '../TabItem'
import { TabsContext } from './Tab.context'
import { tabsClasses } from './Tabs.classes'
export type TabsProps = Omit<
React.HTMLAttributes<HTMLDivElement>,
'onChange'
> & {
activeTab?: string | null
fullWidth?: boolean
onChange?: (activeTab: string) => void
size?: 'small' | 'medium' | 'large'
scrollControls?: boolean
}
export type TabsProps = CommonProps &
Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange'> & {
activeTab?: string | null
fullWidth?: boolean
onChange?: (activeTab: string) => void
size?: 'small' | 'medium' | 'large'
scrollControls?: boolean
}
export const Tabs: React.FC<TabsProps> & {
classes: typeof tabsClasses
@ -28,6 +27,7 @@ export const Tabs: React.FC<TabsProps> & {
children,
...props
}) => {
const commonProps = useCommonProps(props)
const ref = useRef<HTMLDivElement>(null)
const [value, setValue] = useState(activeTab)
@ -50,6 +50,7 @@ export const Tabs: React.FC<TabsProps> & {
ref={ref}
{...props}
className={clsx(
commonProps.className,
props.className,
tabsClasses.root,
fullWidth && tabsClasses.fullWidth,

View File

@ -1,15 +1,17 @@
import clsx from 'clsx'
import React from 'react'
import { CommonProps, useCommonProps } from '../../utils/useCommonProps'
import { Typography } from '../Typography'
import { tagClasses } from './Tag.classes'
export type TagProps = React.HTMLAttributes<HTMLDivElement> & {
variant?: 'outlined' | 'filled'
icon?: React.ReactNode
iconDirection?: 'left' | 'right'
disabled?: boolean
size?: 'large' | 'small'
}
export type TagProps = CommonProps &
React.HTMLAttributes<HTMLDivElement> & {
variant?: 'outlined' | 'filled'
icon?: React.ReactNode
iconDirection?: 'left' | 'right'
disabled?: boolean
size?: 'large' | 'small'
}
export const Tag: React.FC<TagProps> & {
classes: typeof tagClasses
@ -22,11 +24,14 @@ export const Tag: React.FC<TagProps> & {
size = 'large',
...props
}) => {
const commonProps = useCommonProps(props)
return (
<div
aria-label={children as string}
{...props}
className={clsx(
commonProps.className,
props.className,
tagClasses.root,
tagClasses[variant],

View File

@ -1,15 +1,14 @@
import clsx from 'clsx'
import React, { useRef } from 'react'
import { CommonProps, useCommonProps } from '../../utils/useCommonProps'
import { useInput } from '../../utils/useInput'
import { IconButton } from '../IconButton'
import { CloseIcon, ErrorIcon } from '../Icons'
import { Typography } from '../Typography'
import { textFieldClasses } from './TextField.classes'
export type TextFieldProps = Omit<
React.HTMLAttributes<HTMLDivElement>,
'onChange' | 'value'
> &
export type TextFieldProps = CommonProps &
Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange' | 'value'> &
Pick<React.InputHTMLAttributes<HTMLInputElement>, 'onChange'> & {
size?: 'large' | 'medium'
icon?: React.ReactNode
@ -44,6 +43,7 @@ export const TextField: React.FC<TextFieldProps> & {
variant = 'outlined-bottom',
...props
}) => {
const commonProps = useCommonProps(props)
const ref = useRef<HTMLInputElement>(null)
const input = useInput({ defaultValue, value, onChange, ref })
@ -54,6 +54,7 @@ export const TextField: React.FC<TextFieldProps> & {
aria-disabled={disabled ? 'true' : 'false'}
{...props}
className={clsx(
commonProps.className,
props.className,
textFieldClasses.root,
textFieldClasses[size],

View File

@ -123,7 +123,9 @@ export const baseTheme: Theme = {
label1: { fontSize: '0.875rem', lineHeight: '1.25rem' },
label2: { fontSize: '0.75rem', lineHeight: '1rem' },
},
typographyGlobal: {},
typographyGlobal: {
genericFontFamily: 'sans-serif',
},
palette: {
primary: '0, 0, 0',
secondary: '255, 255, 255',

View File

@ -1,5 +1,6 @@
import {
Breakpoints,
TypographyGenericFontFamily,
TypographyProperties,
TypographyVariants,
VariantThemeProperties,
@ -42,10 +43,16 @@ export const THEME_TYPOGRAPHY_ELEMENTS: Partial<
export const THEME_TYPOGRAPHY_PROPERTIES = [
'fontSize',
'fontFamily',
'lineHeight',
] as TypographyProperties[]
export const THEME_VARIANT_PROPERTIES = [
'typography',
] as VariantThemeProperties[]
export const THEME_TYPOGRAPHY_FONT_CATEGORIES = [
'inherit',
'monospace',
'sans-serif',
'serif',
] as TypographyGenericFontFamily[]

View File

@ -15,12 +15,15 @@ import type {
const createTypographyStyles = (theme: CreateThemeProps, defaultTheme: Theme) =>
pairs<TypographyVariants>(THEME_TYPOGRAPHY_VARIANTS, (variant) => ({
...defaultTheme.typographyGlobal,
...theme.typographyGlobal,
...defaultTheme.typography[variant],
...(theme.typography[variant] ?? {}),
}))
const createTypographyGlobalStyles = (
theme: CreateThemeProps,
defaultTheme: Theme,
) => ({ ...defaultTheme.typographyGlobal, ...theme.typographyGlobal })
const createBreakpointStyle = (
key: VariantThemeProperties,
all: BreakpointStyles[],
@ -109,7 +112,7 @@ export const createTheme = (
const theme: Theme = {
name: props.name ?? from.name,
typography: createTypographyStyles(props, from),
typographyGlobal: { ...from.typographyGlobal, ...props.typographyGlobal },
typographyGlobal: createTypographyGlobalStyles(props, from),
breakpoints: createBreakpointStyles(props, from),
palette: createPaletteStyles(props, from),
globalStyles: css``,

View File

@ -38,6 +38,13 @@ const generateThemeGlobalStyles = withTheme((theme) => {
})
})
vars.push(
cssUtils.define(
cssUtils.vars.lsd('typography', 'generic-font-family'),
theme.typographyGlobal.genericFontFamily,
),
)
THEME_BREAKPOINTS.forEach((breakpoint, breakpointIndex) => {
THEME_TYPOGRAPHY_VARIANTS.forEach((variant) => {
THEME_TYPOGRAPHY_PROPERTIES.forEach((property) => {

View File

@ -3,7 +3,7 @@ import { CSSProperties } from 'react'
import { DeepPartial } from 'utility-types'
export type Breakpoints = 'xs' | 'sm' | 'md' | 'lg' | 'xl'
export type TypographyTypefaces = 'sans-serif' | 'serif' | 'mono'
export type TypographyGenericFontFamily = 'sans-serif' | 'serif' | 'monospace'
export type TypographyVariants =
| 'display1'
| 'display2'
@ -25,10 +25,10 @@ export type VariantThemeProperties = keyof Pick<Theme, 'typography'>
export type TypographyStyles = Pick<
CSSProperties,
'fontSize' | 'fontFamily' | 'lineHeight'
'fontSize' | 'lineHeight'
> & { fontFamily?: string }
export type GlobalTypographyStyles = {
fontFamily?: string
genericFontFamily: TypographyGenericFontFamily
}
export type TypographyProperties = keyof TypographyStyles
export type ThemeTypography<T extends string = TypographyVariants> = {
@ -94,7 +94,7 @@ export type CreateThemeProps = {
name?: string
breakpoints: ThemeOptionBreakpoints
typography: ThemeOptionTypography
typographyGlobal: GlobalTypographyStyles
typographyGlobal: Partial<GlobalTypographyStyles>
palette: ThemeOptionPalette
}

View File

@ -6,6 +6,9 @@ export const typographyClasses: {
root: string
primary: string
secondary: string
serif: string
sansSerif: string
monospace: string
} & Record<TypographyVariants, string> = {
...pairs(
THEME_TYPOGRAPHY_VARIANTS,
@ -14,4 +17,7 @@ export const typographyClasses: {
root: `lsd-typography`,
primary: `lsd-typography--primary`,
secondary: `lsd-typography--secondary`,
serif: 'lsd-typography--serif',
sansSerif: 'lsd-typography--sans-serif',
monospace: 'lsd-typography--monospace',
}

View File

@ -15,6 +15,10 @@ const selectors = (variant: TypographyVariants) =>
export const TypographyStyles = withTheme(
(theme) => css`
body * {
font-family: var(--lsd-typography-generic-font-family);
}
.${typographyClasses.root} {
}
@ -26,13 +30,33 @@ export const TypographyStyles = withTheme(
color: rgb(var(--lsd-text-secondary));
}
.${typographyClasses.sansSerif} {
&,
* {
font-family: sans-serif;
}
}
.${typographyClasses.serif} {
&,
* {
font-family: serif;
}
}
.${typographyClasses.monospace} {
&,
* {
font-family: monospace;
}
}
${THEME_TYPOGRAPHY_VARIANTS.map(
(variant) => css`
${selectors(variant)} {
color: rgb(var(--lsd-text-primary));
font-weight: normal;
font-size: var(--lsd-${variant}-fontSize);
font-family: var(--lsd-${variant}-fontFamily);
line-height: var(--lsd-${variant}-lineHeight);
}
`,
@ -40,9 +64,8 @@ export const TypographyStyles = withTheme(
input {
color: rgb(var(--lsd-text-primary));
font-weight: normal;
font-size: var(--lsd-body1-fontSize);
font-family: var(--lsd-body1-fontFamily);
font-weight: normal;
}
h1,

View File

@ -1,31 +1,32 @@
import clsx from 'clsx'
import React from 'react'
import { CommonProps, useCommonProps } from '../../utils/useCommonProps'
import { TypographyVariants } from '../Theme'
import { typographyClasses } from './Typography.classes'
export type TypographyProps = {
export type TypographyProps = CommonProps & {
variant?: TypographyVariants
color?: 'primary' | 'secondary'
} & (
| ({
component?: 'p'
} & React.HTMLAttributes<HTMLParagraphElement>)
| ({
component?: 'span'
} & React.HTMLAttributes<HTMLSpanElement>)
| ({
component?: 'label'
} & React.LabelHTMLAttributes<HTMLLabelElement>)
| ({
component?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'
} & React.HTMLAttributes<HTMLHeadingElement>)
| ({
component?: 'div'
} & React.HtmlHTMLAttributes<HTMLDivElement>)
| ({
component?: 'a'
} & React.AnchorHTMLAttributes<HTMLAnchorElement>)
)
| ({
component?: 'p'
} & React.HTMLAttributes<HTMLParagraphElement>)
| ({
component?: 'span'
} & React.HTMLAttributes<HTMLSpanElement>)
| ({
component?: 'label'
} & React.LabelHTMLAttributes<HTMLLabelElement>)
| ({
component?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'
} & React.HTMLAttributes<HTMLHeadingElement>)
| ({
component?: 'div'
} & React.HtmlHTMLAttributes<HTMLDivElement>)
| ({
component?: 'a'
} & React.AnchorHTMLAttributes<HTMLAnchorElement>)
)
export const Typography: React.FC<TypographyProps> & {
classes: typeof typographyClasses
@ -37,6 +38,7 @@ export const Typography: React.FC<TypographyProps> & {
children,
...props
}) => {
const commonProps = useCommonProps(props)
const componentName =
component ??
(
@ -58,6 +60,7 @@ export const Typography: React.FC<TypographyProps> & {
return (
<Component
className={clsx(
commonProps.className,
typographyClasses.root,
typographyClasses[variant],
color && typographyClasses[color],

View File

@ -0,0 +1,17 @@
import clsx from 'clsx'
import { GlobalTypographyStyles } from '../components/Theme'
import { typographyClasses } from '../components/Typography/Typography.classes'
export type CommonProps = {
genericFontFamily?: 'inherit' | GlobalTypographyStyles['genericFontFamily']
}
export const useCommonProps = ({ genericFontFamily }: CommonProps) => {
return {
className: clsx(
genericFontFamily === 'serif' && typographyClasses.serif,
genericFontFamily === 'monospace' && typographyClasses.monospace,
genericFontFamily === 'sans-serif' && typographyClasses.sansSerif,
),
}
}