mirror of https://github.com/acid-info/lsd.git
feat: add label element to DateField and adjust styles
This commit is contained in:
parent
5f623c8700
commit
2537acf89d
|
@ -2,7 +2,7 @@ import React from 'react'
|
|||
|
||||
export type CalendarContextType = {
|
||||
focusedDate: Date | null
|
||||
size?: 'large' | 'medium'
|
||||
size?: 'large' | 'medium' | 'small'
|
||||
isDateFocused: (date: Date) => boolean
|
||||
isDateSelected: (date: Date) => boolean
|
||||
isDateHovered: (date: Date) => boolean
|
||||
|
|
|
@ -9,7 +9,7 @@ export default {
|
|||
size: {
|
||||
type: {
|
||||
name: 'enum',
|
||||
value: ['medium', 'large'],
|
||||
value: ['small', 'medium', 'large'],
|
||||
},
|
||||
defaultValue: 'large',
|
||||
},
|
||||
|
|
|
@ -20,7 +20,7 @@ export type CalendarProps = Omit<
|
|||
value?: string
|
||||
onChange: (data: Date) => void
|
||||
handleRef: React.RefObject<HTMLElement>
|
||||
size?: 'large' | 'medium'
|
||||
size?: 'large' | 'medium' | 'small'
|
||||
onClose?: () => void
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
export const dateFieldClasses = {
|
||||
root: `lsd-date-field`,
|
||||
label: 'lsd-date-field__label',
|
||||
|
||||
inputContainer: `lsd-date-field-input-container`,
|
||||
input: `lsd-date-field-input-container__input`,
|
||||
icon: `lsd-date-field-input-container__icon`,
|
||||
iconButton: `lsd-date-field-input-container__icon-button`,
|
||||
inputContainer: `lsd-date-field__input-container`,
|
||||
input: `lsd-date-field__input-container__input`,
|
||||
icon: `lsd-date-field__input-container__icon`,
|
||||
iconButton: `lsd-date-field__input-container__icon-button`,
|
||||
|
||||
supportingText: 'lsd-date-field__supporting-text',
|
||||
|
||||
|
@ -13,4 +14,8 @@ export const dateFieldClasses = {
|
|||
|
||||
large: `lsd-date-field--large`,
|
||||
medium: `lsd-date-field--medium`,
|
||||
small: `lsd-date-field--small`,
|
||||
|
||||
outlined: `lsd-date-field--outlined`,
|
||||
outlinedBottom: `lsd-date-field--outlined-bottom`,
|
||||
}
|
||||
|
|
|
@ -8,7 +8,14 @@ export default {
|
|||
size: {
|
||||
type: {
|
||||
name: 'enum',
|
||||
value: ['medium', 'large'],
|
||||
value: ['small', 'medium', 'large'],
|
||||
},
|
||||
defaultValue: 'large',
|
||||
},
|
||||
variant: {
|
||||
type: {
|
||||
name: 'enum',
|
||||
value: ['outlined', 'outlined-bottom'],
|
||||
},
|
||||
defaultValue: 'large',
|
||||
},
|
||||
|
@ -24,6 +31,7 @@ export const Controlled: Story<DateFieldProps> = ({ ...args }) => {
|
|||
}
|
||||
|
||||
Uncontrolled.args = {
|
||||
id: 'label',
|
||||
size: 'large',
|
||||
supportingText: 'Supporting text',
|
||||
disabled: false,
|
||||
|
@ -32,9 +40,12 @@ Uncontrolled.args = {
|
|||
error: false,
|
||||
errorIcon: false,
|
||||
clearButton: true,
|
||||
variant: 'outlined-bottom',
|
||||
label: 'Label',
|
||||
}
|
||||
|
||||
Controlled.args = {
|
||||
id: 'label',
|
||||
size: 'large',
|
||||
supportingText: 'Supporting text',
|
||||
disabled: false,
|
||||
|
@ -43,4 +54,6 @@ Controlled.args = {
|
|||
error: false,
|
||||
errorIcon: false,
|
||||
clearButton: true,
|
||||
variant: 'outlined-bottom',
|
||||
label: 'Label',
|
||||
}
|
||||
|
|
|
@ -4,10 +4,27 @@ import { dateFieldClasses } from './DateField.classes'
|
|||
export const DateFieldStyles = css`
|
||||
.${dateFieldClasses.root} {
|
||||
width: auto;
|
||||
border-bottom: 1px solid rgb(var(--lsd-border-primary));
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.${dateFieldClasses.label} {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.${dateFieldClasses.icon} {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.${dateFieldClasses.outlined} {
|
||||
border: 1px solid rgb(var(--lsd-border-primary));
|
||||
}
|
||||
|
||||
.${dateFieldClasses.outlinedBottom} {
|
||||
border-bottom: 1px solid rgb(var(--lsd-border-primary));
|
||||
}
|
||||
|
||||
.${dateFieldClasses.inputContainer} {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
@ -48,32 +65,64 @@ export const DateFieldStyles = css`
|
|||
}
|
||||
|
||||
.${dateFieldClasses.supportingText} {
|
||||
width: fit-content;
|
||||
margin-top: 20px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.${dateFieldClasses.large} {
|
||||
width: 208px;
|
||||
|
||||
.${dateFieldClasses.label} {
|
||||
margin: 0 0 6px 18px;
|
||||
}
|
||||
.${dateFieldClasses.inputContainer} {
|
||||
height: 40px;
|
||||
padding: 10px 14px;
|
||||
}
|
||||
.${dateFieldClasses.input} {
|
||||
padding: 9px 13px 9px 17px;
|
||||
}
|
||||
.${dateFieldClasses.icon} {
|
||||
padding: 12px 13px;
|
||||
}
|
||||
.${dateFieldClasses.supportingText} {
|
||||
margin: 6px 18px 0 18px;
|
||||
}
|
||||
}
|
||||
|
||||
.${dateFieldClasses.medium} {
|
||||
width: 188px;
|
||||
.${dateFieldClasses.label} {
|
||||
margin: 0 0 6px 14px;
|
||||
}
|
||||
.${dateFieldClasses.inputContainer} {
|
||||
height: 32px;
|
||||
padding: 6px 12px;
|
||||
}
|
||||
.${dateFieldClasses.input} {
|
||||
padding: 5px 11px 5px 13px;
|
||||
}
|
||||
.${dateFieldClasses.icon} {
|
||||
padding: 8px 11px;
|
||||
}
|
||||
.${dateFieldClasses.supportingText} {
|
||||
margin: 6px 14px 0 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.${dateFieldClasses.iconButton} {
|
||||
padding: 0;
|
||||
width: auto;
|
||||
height: auto;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
|
||||
svg {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
.${dateFieldClasses.small} {
|
||||
width: 164px;
|
||||
.${dateFieldClasses.label} {
|
||||
margin: 0 0 6px 12px;
|
||||
}
|
||||
.${dateFieldClasses.inputContainer} {
|
||||
height: 28px;
|
||||
}
|
||||
.${dateFieldClasses.input} {
|
||||
padding: 5px 9px 5px 11px;
|
||||
}
|
||||
.${dateFieldClasses.icon} {
|
||||
padding: 6px 9px;
|
||||
}
|
||||
.${dateFieldClasses.supportingText} {
|
||||
margin: 6px 12px 0 12px;
|
||||
}
|
||||
}
|
||||
`
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import clsx from 'clsx'
|
||||
import React, { useRef } from 'react'
|
||||
import { useInput } from '../../utils/useInput'
|
||||
import { IconButton } from '../IconButton'
|
||||
import { CloseIcon, ErrorIcon } from '../Icons'
|
||||
import { Typography } from '../Typography'
|
||||
import { dateFieldClasses } from './DateField.classes'
|
||||
|
@ -11,7 +10,8 @@ export type DateFieldProps = Omit<
|
|||
'onChange' | 'value'
|
||||
> &
|
||||
Pick<React.InputHTMLAttributes<HTMLInputElement>, 'onChange'> & {
|
||||
size?: 'large' | 'medium'
|
||||
label?: React.ReactNode
|
||||
size?: 'large' | 'medium' | 'small'
|
||||
error?: boolean
|
||||
errorIcon?: boolean
|
||||
clearButton?: boolean
|
||||
|
@ -23,11 +23,13 @@ export type DateFieldProps = Omit<
|
|||
icon?: React.ReactNode
|
||||
onIconClick?: () => void
|
||||
inputProps?: React.InputHTMLAttributes<HTMLInputElement>
|
||||
variant?: 'outlined' | 'outlined-bottom'
|
||||
}
|
||||
|
||||
export const DateField: React.FC<DateFieldProps> & {
|
||||
classes: typeof dateFieldClasses
|
||||
} = ({
|
||||
label,
|
||||
size = 'large',
|
||||
error = false,
|
||||
errorIcon = false,
|
||||
|
@ -42,6 +44,7 @@ export const DateField: React.FC<DateFieldProps> & {
|
|||
icon,
|
||||
onIconClick,
|
||||
inputProps = {},
|
||||
variant = 'outlined-bottom',
|
||||
...props
|
||||
}) => {
|
||||
const ref = useRef<HTMLInputElement>(null)
|
||||
|
@ -49,6 +52,8 @@ export const DateField: React.FC<DateFieldProps> & {
|
|||
|
||||
const onCancel = () => input.setValue('')
|
||||
|
||||
const inputId = inputProps?.id ?? (props.id || 'date-field') + '-input'
|
||||
|
||||
return (
|
||||
<div
|
||||
aria-disabled={disabled ? 'true' : 'false'}
|
||||
|
@ -61,8 +66,26 @@ export const DateField: React.FC<DateFieldProps> & {
|
|||
error && dateFieldClasses.error,
|
||||
)}
|
||||
>
|
||||
<div className={dateFieldClasses.inputContainer}>
|
||||
{label && (
|
||||
<Typography
|
||||
htmlFor={inputId}
|
||||
className={dateFieldClasses.label}
|
||||
variant="label2"
|
||||
component="label"
|
||||
>
|
||||
{label}
|
||||
</Typography>
|
||||
)}
|
||||
<div
|
||||
className={clsx(
|
||||
dateFieldClasses.inputContainer,
|
||||
variant === 'outlined'
|
||||
? dateFieldClasses.outlined
|
||||
: dateFieldClasses.outlinedBottom,
|
||||
)}
|
||||
>
|
||||
<input
|
||||
id={inputId}
|
||||
type="date"
|
||||
placeholder={placeholder}
|
||||
{...inputProps}
|
||||
|
@ -72,32 +95,28 @@ export const DateField: React.FC<DateFieldProps> & {
|
|||
className={clsx(inputProps.className, dateFieldClasses.input)}
|
||||
/>
|
||||
{icon ? (
|
||||
<IconButton
|
||||
disabled={disabled}
|
||||
className={dateFieldClasses.iconButton}
|
||||
<span
|
||||
className={dateFieldClasses.icon}
|
||||
onClick={() => !disabled && onIconClick && onIconClick()}
|
||||
>
|
||||
{icon}
|
||||
</IconButton>
|
||||
</span>
|
||||
) : error && errorIcon ? (
|
||||
<ErrorIcon color="primary" className={dateFieldClasses.icon} />
|
||||
<span className={dateFieldClasses.icon}>
|
||||
<ErrorIcon color="primary" />
|
||||
</span>
|
||||
) : clearButton && input.filled ? (
|
||||
<IconButton
|
||||
disabled={disabled}
|
||||
<span
|
||||
onClick={() => !disabled && onCancel()}
|
||||
aria-label="clear"
|
||||
className={dateFieldClasses.iconButton}
|
||||
className={dateFieldClasses.icon}
|
||||
>
|
||||
<CloseIcon color="primary" className={dateFieldClasses.icon} />
|
||||
</IconButton>
|
||||
<CloseIcon color="primary" />
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
{supportingText && (
|
||||
<div className={clsx(dateFieldClasses.supportingText)}>
|
||||
<Typography
|
||||
variant={size === 'large' ? 'label1' : 'label2'}
|
||||
component="p"
|
||||
>
|
||||
<Typography variant={'label2'} component="p">
|
||||
{supportingText}
|
||||
</Typography>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
export const datePickerClasses = {
|
||||
root: `lsd-date-picker`,
|
||||
|
||||
calendar: `lsd-date-picker-calendar`,
|
||||
calendar: `lsd-date-picker__calendar`,
|
||||
|
||||
large: `lsd-date-picker--large`,
|
||||
medium: `lsd-date-picker--medium`,
|
||||
small: `lsd-date-picker--small`,
|
||||
}
|
||||
|
|
|
@ -8,7 +8,14 @@ export default {
|
|||
size: {
|
||||
type: {
|
||||
name: 'enum',
|
||||
value: ['medium', 'large'],
|
||||
value: ['small', 'medium', 'large'],
|
||||
},
|
||||
defaultValue: 'large',
|
||||
},
|
||||
variant: {
|
||||
type: {
|
||||
name: 'enum',
|
||||
value: ['outlined', 'outlined-bottom'],
|
||||
},
|
||||
defaultValue: 'large',
|
||||
},
|
||||
|
@ -24,6 +31,7 @@ export const Controlled: Story<DatePickerProps> = ({ ...args }) => {
|
|||
}
|
||||
|
||||
Uncontrolled.args = {
|
||||
id: 'label',
|
||||
supportingText: 'Supporting text',
|
||||
disabled: false,
|
||||
error: false,
|
||||
|
@ -33,9 +41,12 @@ Uncontrolled.args = {
|
|||
clearButton: true,
|
||||
withCalendar: true,
|
||||
size: 'large',
|
||||
variant: 'outlined-bottom',
|
||||
label: 'Label',
|
||||
}
|
||||
|
||||
Controlled.args = {
|
||||
id: 'label',
|
||||
supportingText: 'Supporting text',
|
||||
disabled: false,
|
||||
error: false,
|
||||
|
@ -45,4 +56,6 @@ Controlled.args = {
|
|||
clearButton: true,
|
||||
withCalendar: true,
|
||||
size: 'large',
|
||||
variant: 'outlined-bottom',
|
||||
label: 'Label',
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { css } from '@emotion/react'
|
||||
import { datePickerClasses } from './DatePicker.classes'
|
||||
import { dateFieldClasses } from '../DateField/DateField.classes'
|
||||
|
||||
export const DatePickerStyles = css`
|
||||
.${datePickerClasses.root} {
|
||||
|
@ -9,4 +10,22 @@ export const DatePickerStyles = css`
|
|||
.${datePickerClasses.calendar} {
|
||||
border-top: none !important;
|
||||
}
|
||||
|
||||
.${datePickerClasses.large} {
|
||||
.${dateFieldClasses.large} {
|
||||
width: 318px;
|
||||
}
|
||||
}
|
||||
|
||||
.${datePickerClasses.medium} {
|
||||
.${dateFieldClasses.medium} {
|
||||
width: 290px;
|
||||
}
|
||||
}
|
||||
|
||||
.${datePickerClasses.small} {
|
||||
.${dateFieldClasses.small} {
|
||||
width: 262px;
|
||||
}
|
||||
}
|
||||
`
|
||||
|
|
|
@ -16,6 +16,7 @@ export type DatePickerProps = Omit<
|
|||
'onChange' | 'value'
|
||||
> &
|
||||
Pick<React.InputHTMLAttributes<HTMLInputElement>, 'onChange'> & {
|
||||
label?: React.ReactNode
|
||||
error?: boolean
|
||||
errorIcon?: boolean
|
||||
clearButton?: boolean
|
||||
|
@ -25,13 +26,22 @@ export type DatePickerProps = Omit<
|
|||
value?: string
|
||||
defaultValue?: string
|
||||
placeholder?: string
|
||||
size?: 'large' | 'medium'
|
||||
size?: 'large' | 'medium' | 'small'
|
||||
variant?: 'outlined' | 'outlined-bottom'
|
||||
inputProps?: React.InputHTMLAttributes<HTMLInputElement>
|
||||
}
|
||||
|
||||
export const DatePicker: React.FC<DatePickerProps> & {
|
||||
classes: typeof datePickerClasses
|
||||
} = ({ value: valueProp, onChange, withCalendar = true, ...props }) => {
|
||||
} = ({
|
||||
label,
|
||||
size = 'large',
|
||||
value: valueProp,
|
||||
onChange,
|
||||
withCalendar = true,
|
||||
variant = 'outlined-bottom',
|
||||
...props
|
||||
}) => {
|
||||
const ref = useRef<HTMLDivElement>(null)
|
||||
const [openCalendar, setOpenCalendar] = useState(false)
|
||||
|
||||
|
@ -48,18 +58,27 @@ export const DatePicker: React.FC<DatePickerProps> & {
|
|||
const handleDateChange = (date: Date) =>
|
||||
input.setValue(dateToISODateString(removeDateTimezoneOffset(date)))
|
||||
|
||||
const inputId = (props.id || 'date-picker') + '-input'
|
||||
|
||||
return (
|
||||
<div
|
||||
id={inputId}
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={clsx(props.className, datePickerClasses.root)}
|
||||
className={clsx(
|
||||
props.className,
|
||||
datePickerClasses.root,
|
||||
datePickerClasses[size],
|
||||
)}
|
||||
>
|
||||
<DateField
|
||||
label={label}
|
||||
size={size}
|
||||
variant={variant}
|
||||
icon={withCalendar && <CalendarIcon color="primary" />}
|
||||
onIconClick={() => setOpenCalendar((prev) => !prev)}
|
||||
value={input.value}
|
||||
onChange={input.onChange}
|
||||
style={{ width: '310px' }}
|
||||
{...props}
|
||||
>
|
||||
<Portal id="calendar">
|
||||
|
|
Loading…
Reference in New Issue