Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Day.render / CalendarDays.render #47

Merged
merged 4 commits into from
Jun 8, 2022

Conversation

astahmer
Copy link
Contributor

Hey, I needed to customize the content of each day's cell, this should cover it

example story:
Screenshot 2022-06-01 at 01 00 39

with:

<CalendarDays
            render={args =>
              new Date(args.day).getDate() < 8 ? (
                <Box d="flex" flexDirection="column" alignItems="center">
                  <div>{format(args.day, 'd')}</div>
                  <Circle size="4px" bgColor="pink.300" />
                </Box>
              ) : (
                format(args.day, 'd')
              )
            }
          />

@netlify
Copy link

netlify bot commented May 31, 2022

Deploy Preview for uselessdev-datepicker ready!

Name Link
🔨 Latest commit 556da33
🔍 Latest deploy log https://app.netlify.com/sites/uselessdev-datepicker/deploys/629b2684061017000896cba1
😎 Deploy Preview https://deploy-preview-47--uselessdev-datepicker.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site settings.

@hiwllc
Copy link
Owner

hiwllc commented Jun 2, 2022

Hi, @astahmer first of all thank you for opening this pull request.

So, about the implementation, I really want to avoid the use of render props (yes I know I'm using render props in controls but I'm planning to change this) and use children instead. I suggest something like this:

<CalendarDays>
  {({ day }) => {
    if (new Date(day).getDate() < 8) {
      return (
        <Box d="flex" flexDirection="column" alignItems="center">
          <div>{day /* should use date-fns format as well */}</div>
          <Circle size="4px" bgColor="pink.300" />
        </Box>
      )
    }

    return day
  }}
</CalendarDays>

But, I still don't know if this is 'ok' maybe we can find another solution something like exposing a hook such as useCalendarDays and:

const { days } = useCalendarDays()

<CalendarDays>
  {days.map(day => <Box>{day}</Box>)}
</CalendarDays>

@astahmer
Copy link
Contributor Author

astahmer commented Jun 2, 2022

Ok I see, no problem i'll change it ! 👍 Out of curiosity, is there a problem with render props ?

I think that with a hook such as const { days } = useCalendarDays(), you'd be missing the built-in logic (isSelected/isInRange/isDisabled), so I would not really consider that option.

IMO you could make it a hook at the Day level:

`const { day, isSelected, isInRange, isDisabled, variant } = useCalendarDay()`

or make it another compound component at the Day level:

<CalendarDays>
    <CalendarDay>
        {({ day }) => (day ? `(${format(day, 'd')})` : <span />)}
    </CalendarDay>
<CalendarDays>

or even better we can just make both and useCalendarDay inside CalendarDay so that the user has the same level of control as the built-in components

feat: DayContext /CalendarDay / useCalendarDay
docs: add stories
@hiwllc
Copy link
Owner

hiwllc commented Jun 3, 2022

Ok I see, no problem i'll change it ! 👍 Out of curiosity, is there a problem with render props ?

absolutely not

or make it another compound component at the Day level:

I was thinking of something like this

</Day>
)
}
export const CustomDayComponent: ComponentStory<typeof Calendar> = () => {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's keep this simple by only adding the WithCustomDay story.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the story I was thinking in something like this:

function CustomDay({ day, ...props }: Day) {
  if (new Date(day).getDate() > 8) {
    return <CalendarDay day={day} {...props} />
  }

  return (
    <CalendarDay
      day={day}
      {...props}
      flexDir="column"
      rounded="md"
      _hover={{ bgColor: 'pink.100' }}
    >
      {format(day, 'd')}
      <Circle size="4px" bgColor="pink.300" />
    </CalendarDay>
  )
}

export const WithCustomDay: ComponentStory<typeof Calendar> = () => {
  const [dates, setDates] = React.useState<CalendarValues>({})

  const handleSelectDate = (dates: CalendarValues) => setDates(dates)

  return (
    <Calendar value={dates} onSelectDate={handleSelectDate}>
      <CalendarControls>
        <CalendarPrevButton />
        <CalendarNextButton />
      </CalendarControls>

      <CalendarMonths>
        <CalendarMonth>
          <CalendarMonthName />
          <CalendarWeek />
          <CalendarDays>{props => <CustomDay {...props} />}</CalendarDays>
        </CalendarMonth>
      </CalendarMonths>
    </Calendar>
  )
}

this is similar to what you already did

src/day.tsx Outdated

export function Day({ day, variant, disabled, onSelectDate,render }: Day) {
export function Day({ day, variant, disabled, onSelectDate, children }: Day) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now this component will be 'public' we should rename it to CalendarDay

@@ -15,8 +15,56 @@ import { CalendarContext } from './context'
import { Day } from './day'
import { MonthContext } from './month'

export function CalendarDays({render}: Pick<Day, "render">) {
export function CalendarDays({ children }: CalendarDayProps) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this file has too much change, for this PR I want to suggest only add props for the children:

type Props = {
  children?: (props: Day) => React.ReactElement<Day>
}

export function CalendarDays({ children }: Props) {

and just before the return verify if children exists and return:

if (children) {
  return children({
    day,
    onSelectDate: onSelectDates,
    disabled: isDisabled,
    variant,
  })
}

) as JSX.Element
}

export const useCalendarDay = () => {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really like the hook, but I think we should add this in another pull request.

@astahmer
Copy link
Contributor Author

astahmer commented Jun 4, 2022

we can keep this as a type and we can use ButtonProps to have style props in the CalendarDay
if we add the ButtonProps in Day type we will need to spread the props after the children something like this:
and we can now merge with styles in the sx props on Button component

done in the last commit 👍

Now this component will be 'public' we should rename it to CalendarDay

that makes sense but at the same time, then how would you name the built-in component containing the isSelected / isDisabled / etc logic (either directly in the render fn or in the future using the useCalendarDay hook) ?
or does that mean there would not be one provided ?
like this one

this file has too much change, for this PR I want to suggest only add props for the children:
let's keep this simple by only adding the WithCustomDay story.
I really like the hook, but I think we should add this in another pull request.

if you don't mind, could you do it ? this might be more straightforward than me doing it since i'm not really sure how you want to split the PR

@hiwllc hiwllc merged commit 0f3a502 into hiwllc:main Jun 8, 2022
@hiwllc
Copy link
Owner

hiwllc commented Jun 9, 2022

@all-contributors please add @astahmer for code

@allcontributors
Copy link
Contributor

@uselessdev

I've put up a pull request to add @astahmer! 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants