️ feat: add onReset prop

This commit is contained in:
Wahyu Kurniawan 2024-02-29 10:44:25 +07:00
parent 630af612a2
commit e0c5895e9c
No known key found for this signature in database
GPG Key ID: 040A1549143A8E33
6 changed files with 59 additions and 29 deletions

View File

@ -19,6 +19,7 @@ import {
import './Calendar.css';
import { format } from 'date-fns';
import { cn } from 'utils/classnames';
type ValuePiece = Date | null;
export type Value = ValuePiece | [ValuePiece, ValuePiece];
@ -63,6 +64,11 @@ export interface CalendarProps extends CustomReactCalendarProps, CalendarTheme {
* @returns None
*/
onCancel?: () => void;
/**
* Optional callback function that is called when a reset action is triggered.
* @returns None
*/
onReset?: () => void;
}
/**
@ -80,6 +86,7 @@ export const Calendar = ({
actions,
onSelect,
onCancel,
onReset,
onChange: onChangeProp,
...props
}: CalendarProps): JSX.Element => {
@ -217,6 +224,11 @@ export const Calendar = ({
[setValue, setActiveDate, changeNavigationLabel, selectRange],
);
const handleReset = useCallback(() => {
setValue(null);
onReset?.();
}, [setValue, onReset]);
return (
<div
{...wrapperProps}
@ -276,12 +288,20 @@ export const Calendar = ({
{/* Footer or CTA */}
<div
{...footerProps}
className={footer({ className: footerProps?.className })}
className={cn(footer({ className: footerProps?.className }), {
'justify-between': value,
})}
>
{actions ? (
actions
) : (
<>
{value && (
<Button variant="danger" onClick={handleReset}>
Reset
</Button>
)}
<div className="space-x-3">
<Button variant="tertiary" onClick={onCancel}>
Cancel
</Button>
@ -291,6 +311,7 @@ export const Calendar = ({
>
Select
</Button>
</div>
</>
)}
</div>

View File

@ -2,7 +2,7 @@ import { VariantProps, tv } from 'tailwind-variants';
export const datePickerTheme = tv({
slots: {
input: [],
input: ['w-full'],
},
});

View File

@ -1,4 +1,4 @@
import React, { useCallback, useState } from 'react';
import React, { useCallback, useMemo, useState } from 'react';
import { Input, InputProps } from 'components/shared/Input';
import * as Popover from '@radix-ui/react-popover';
import { datePickerTheme } from './DatePicker.theme';
@ -27,6 +27,10 @@ export interface DatePickerProps
* Whether to allow the selection of a date range.
*/
selectRange?: boolean;
/**
* Optional callback function that is called when the date picker is reset.
*/
onReset?: () => void;
}
/**
@ -39,6 +43,7 @@ export const DatePicker = ({
calendarProps,
value,
onChange,
onReset,
selectRange = false,
...props
}: DatePickerProps) => {
@ -50,16 +55,16 @@ export const DatePicker = ({
* Renders the value of the date based on the current state of `props.value`.
* @returns {string | undefined} - The formatted date value or `undefined` if `props.value` is falsy.
*/
const renderValue = useCallback(() => {
if (!value) return undefined;
const renderValue = useMemo(() => {
if (!value) return '';
if (Array.isArray(value)) {
return value
.map((date) => format(date as Date, 'dd/MM/yyyy'))
.join(' - ');
}
return format(value, 'dd/MM/yyyy');
}, [value]);
}, [value, onReset]);
console.log(renderValue);
/**
* Handles the selection of a date from the calendar.
*/
@ -71,15 +76,20 @@ export const DatePicker = ({
[setOpen, onChange],
);
const handleReset = useCallback(() => {
setOpen(false);
onReset?.();
}, [setOpen, onReset]);
return (
<Popover.Root open={open}>
<Popover.Trigger>
<Popover.Trigger className="w-full">
<Input
{...props}
rightIcon={<CalendarIcon onClick={() => setOpen(true)} />}
readOnly
placeholder="Select a date..."
value={renderValue()}
value={renderValue}
className={input({ className })}
onClick={() => setOpen(true)}
/>
@ -93,6 +103,7 @@ export const DatePicker = ({
{...calendarProps}
selectRange={selectRange}
value={value}
onReset={handleReset}
onCancel={() => setOpen(false)}
onSelect={handleSelect}
/>

View File

@ -2,7 +2,7 @@ import { VariantProps, tv } from 'tailwind-variants';
export const selectTheme = tv({
slots: {
container: ['flex', 'flex-col', 'relative', 'gap-2'],
container: ['flex', 'flex-col', 'relative', 'gap-2', 'w-full'],
label: ['text-sm', 'text-elements-high-em'],
description: ['text-xs', 'text-elements-low-em'],
inputWrapper: [

View File

@ -3,7 +3,6 @@ import React, {
useState,
ComponentPropsWithoutRef,
useMemo,
useCallback,
MouseEvent,
useRef,
useEffect,
@ -12,7 +11,7 @@ import { useMultipleSelection, useCombobox } from 'downshift';
import { SelectTheme, selectTheme } from './Select.theme';
import {
ChevronDownIcon,
CrossIcon,
CrossCircleIcon,
WarningIcon,
} from 'components/shared/CustomIcon';
import { cloneIcon } from 'utils/cloneIcon';
@ -270,11 +269,8 @@ export const Select = ({
itemToString: (item) => (item && !multiple ? item.label : ''),
});
const isSelected = useCallback(
(item: SelectOption) =>
multiple ? selectedItems.includes(item) : selectedItem === item,
[selectedItems, selectedItem, multiple],
);
const isSelected = (item: SelectOption) =>
multiple ? selectedItems.includes(item) : selectedItem === item;
const handleClear = (e: MouseEvent<SVGSVGElement, globalThis.MouseEvent>) => {
e.stopPropagation();
@ -306,7 +302,7 @@ export const Select = ({
return (
<div className={theme.iconContainer({ class: 'pr-4 right-0' })}>
{clearable && (selectedItems.length > 0 || selectedItem) && (
<CrossIcon
<CrossCircleIcon
className={theme.icon({ class: 'h-4 w-4' })}
onClick={handleClear}
/>
@ -318,7 +314,7 @@ export const Select = ({
)}
</div>
);
}, [cloneIcon, theme, rightIcon]);
}, [cloneIcon, theme, rightIcon, selectedItem, selectedItems, clearable]);
const renderHelperText = useMemo(
() => (

View File

@ -62,7 +62,9 @@ const SelectItem = forwardRef<HTMLLIElement, SelectItemProps>(
<p className={theme.label()} data-disabled={disabled}>
{label}
</p>
{orientation === 'horizontal' && <span className={theme.dot()} />}
{orientation === 'horizontal' && description && (
<span className={theme.dot()} />
)}
{description && (
<p className={theme.description()} data-disabled={disabled}>
{description}