react-modern-calendar-datepicker icon indicating copy to clipboard operation
react-modern-calendar-datepicker copied to clipboard

Themability: Color & Arrow

Open bruskowski opened this issue 4 years ago • 16 comments

Is your feature request related to a problem? Please describe. I'd like to be able to easily change the color scheme of the component and swap the arrow icon.

Describe the solution you'd like Set key colors through props. Use these as CSS variables throughout the component.

Describe alternatives you've considered I can override most colors with classes and CSS selectors, but not for every element (e.g. arrow). Some colors are defined with CSS variables, which makes theming easy, others are 'hardcoded'. Sometimes I need to do some 'digging'.

Additional context I'll open a pull-request, where I've made some smaller tweaks to allow color theming (heavier use of CSS variables, additional props). This is not super complete, though. Also I am not a dev, so please be critical :)

Thanks for this beautiful component, I've wrapped it for Framer X so people can use it for prototyping: https://packages.framer.com/package/richy/modern-calendar Intend

bruskowski avatar Apr 01 '20 09:04 bruskowski

Hi Richard, thank you so much for publishing this for Framer X. It's recommended to discuss the features here and then on a pull request. The changes you want to apply like changing the arrow are really vast. I think instead of implementing dozens of props for customizing one little thing, it's better to have a nice API for theming with some default themes like light or dark.

Kiarash-Z avatar Apr 01 '20 19:04 Kiarash-Z

You're welcome, thank you for building this!

Regarding the API: I see, that makes sense. Maybe instead of adding a prop for each color, …

  • having the option to override the CSS color variables directly through a custom class might be a better way, or …
  • one theme prop, that can either be a preset like dark and light or an object with all color definitions.

Anyways, my prototyping use case is probably quite atypical, so don't worry too much about it.

bruskowski avatar Apr 01 '20 22:04 bruskowski

I think the second option is way better. I need others opinion about this API 🤔 I gotta pin this issue.

Kiarash-Z avatar Apr 05 '20 19:04 Kiarash-Z

Hi there! I really need to change the arrow as well, has been any development on this? Or maybe there's a workaround in the meantime? :thinking: Like @richardbruskowski said, one can get by overriding classes, but I had no luck with the arrows :confused:

alessandrojcm avatar May 29 '20 21:05 alessandrojcm

Hi @alessandrojcm, I need more feedbacks to make a decision about the full customizability API 🤔 Arrows are placed into the calendar using background-image. Therefore, for now, you can override the default arrows by overriding the background-image for .Calendar__monthArrow CSS class:

image

Kiarash-Z avatar May 30 '20 11:05 Kiarash-Z

Hi @Kiarash-Z, yes that was I was trying to do; but in our project we're using font awesome for the icons. Those are font icons, so I'm not sure how could I override the calendar's arrow with those. I've tried by setting the content property in the ::after pseudo-class and applying the font awesome classes with @extend, but no luck :confused:

Regarding the API, I think that having an object which defines the theme would work; something among the lines of ThemeUI or TailwindCSS, where you defined all the variables in an object, then that could be applied via CSS custom properties. Now, with the arrow, I think render props could work: the calendar would pass a callback to the render prop that triggers next/previous date and then the user could just use whatever component they like; also, if you go with the theme object, the render prop could pass also the color variables. What do you think?

alessandrojcm avatar May 30 '20 17:05 alessandrojcm

@alessandrojcm Yeah, I think this type of config might be good:

const getConfig = (defaultValues) => {
  textColor: defaultValues.defaultColor,
  // many other properties here...
  renderArrows: (position) => <i className={position === 'left' ? 'left' : 'right'} />
}

<Calendar configure={getConfig} />

Or I might just define a separate prop for rendering arrows 🤔

Kiarash-Z avatar May 31 '20 06:05 Kiarash-Z

@Kiarash-Z Yeah I agree, arrows should be a different prop. With the render props approach, I mentioned could look like this:

<Calendar>
    {({ prevMonth, prevStyles }, { nextMonth, nextStyles }) => (
        <CustomLeftArrow callback={prevMonth} className={prevStyles} />
        <CustomRightArrow callback={nextMonth} className={nextStyles} />
    )}
</Calendar>

Pros are that the user has full control of what to render, cons are that you couldn't get to fancy with the animations, you could pass the animations via the styles, but it would be up to the user to have the animations play nicely with their components. Also, there is the problem of the month/year selector: it should be rendered between the arrows render props make that difficult, you either pass the component as a prop, so the user is responsible for placing it; or you could place it yourself between the custom arrows (maybe using the classes for that, but then the user should pass the classes to the arrows.

Another approach I've seen in other libraries is, like you said, exposing props for the arrows:

<Calendar rightArrow={CustomArrowComponent} leftArrow={CustomArrowComponent} />

Those custom components should receive a callback as prop and the calendar would render that and pass the callback. What do you think? 🤔

alessandrojcm avatar May 31 '20 19:05 alessandrojcm

@alessandrojcm This is a good approach but for rendering arrows. For the styles, I guess it's the developer's duty to provide appropriate styles when customizing arrows. For rendering arrows, I think it's kind of verbose to expose two props; one for each arrow. Therefore, I would rather pass an isLeft: boolean to renderArrows prop. It could look like this:

const renderArrows = ({ isLeftArrow, goToPreviousMonth, goToNextMonth }) => {
  if (isLeftArrow) return <CustomLeftArrow className="MyCustomLeftArrow" onClick={goToPreviousMonth} />
  return <CustomRightArrow className="className="MyCustomRightArrow" onClick={goToNextMonth} />
}

<Calendar renderArrows={renderArrows} />

I would be glad to hear your ideas 😊

Kiarash-Z avatar Jun 01 '20 09:06 Kiarash-Z

@Kiarash-Z 5 Yeah that would work but, for me at least, passing a component to render both the arrows is kinda strange. How will the calendar render the month/year selector internally?

As for the styles, yes you're right that the dev should be responsible for the styling. But passing at least the colors would be nice for the sake of consistency.

alessandrojcm avatar Jun 01 '20 15:06 alessandrojcm

@alessandrojcm It's quite easy internally:

{renderArrows({ isLeftArrow: true })}
<MonthYearSelector />
{renderArrows({ isLeftArrow: false })}

As for the colors, I think because of their default color(black), it wouldn't be needed to expose them to renderArrows function.

Kiarash-Z avatar Jun 02 '20 09:06 Kiarash-Z

@Kiarash-Z oh, I see. Well, regarding the colors, I meant if the user passes a theme (in which the arrow color could change) I think it would be nice to pass that down to the arrows, just to be consistent 🤔

alessandrojcm avatar Jun 02 '20 13:06 alessandrojcm

Any updates on the renderArrows? I think it would be a great addition to the API, along with renderDay and renderHeader.

fandy avatar Jul 14 '20 01:07 fandy

Hi Is there any possibility of changing the background color of the calendar right now?

setayeshk avatar Apr 11 '21 07:04 setayeshk

@setayeshk you can set it through this css:

.custom-calendar { box-shadow: 0 1em 3em rgba(156, 136, 255,0.2); background-color: #040320; color: white; }

cking3190 avatar May 05 '21 14:05 cking3190

A temporary solution for now:

cal
<Calendar
  value={selectedDayRange}
  onChange={setSelectedDayRange}
  calendarClassName="custom-calendar"
  calendarSelectedDayClassName="custom-selected"
  calendarRangeStartClassName="custom-start"
  calendarRangeBetweenClassName="custom-between"
  calendarRangeEndClassName="custom-end"/>
.custom-calendar {
  box-shadow: unset !important;
  background-color: unset !important;
  .Calendar__day, .Calendar__monthText, .Calendar__yearText {
    color: white !important;
  }
  .custom-start, .custom-end, .custom-between {
    color: white !important;
    background-color: rgba(0, 128, 255, 0.7) !important;
  }
  .Calendar__monthArrow {
    background-image: url("data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%2724%27 height=%2724%27 viewBox=%270 0 24 24%27%3E%3Cg class=%27nc-icon-wrapper%27 fill=%27%23ffffff%27%3E%3Cdefs stroke=%27none%27%3E%3C/defs%3E%3Cpath class=%27cls-1%27 d=%27M12 23.25V.75%27 fill=%27none%27 stroke=%27%23ffffff%27 stroke-linecap=%27round%27 stroke-linejoin=%27round%27 stroke-width=%271.5px%27%3E%3C/path%3E%3Cpath class=%27cls-2%27 d=%27M22.5 11.25L12 .75 1.5 11.25%27 fill=%27none%27 stroke=%27%23ffffff%27 stroke-linecap=%27round%27 stroke-linejoin=%27round%27 stroke-width=%271.5px%27 fill-rule=%27evenodd%27%3E%3C/path%3E%3C/g%3E%3C/svg%3E") !important;
  }
  .Calendar__monthYear.-shown>*:hover, .Calendar:not(.-noFocusOutline) .Calendar__monthYear.-shown>*:focus, .Calendar__monthYear>*.-activeBackground {
    background: #f5f5f577 !important;
  }
  .Calendar__day:not(.-blank):not(.-selectedStart):not(.-selectedEnd):not(.-selectedBetween):not(.-selected):hover {
    background: #eaeaea77 !important;
  }
  .Calendar__day.-today:not(.-selectedStart):not(.-selectedEnd):not(.-selectedBetween)::after {
    background: white !important;
  }
  .Calendar__yearSelectorWrapper::after, .Calendar__yearSelectorWrapper::before {
    visibility: hidden !important;
  }
  .Calendar__yearSelectorItem.-active *, .Calendar__monthSelectorItem.-active * {
    background-color: rgba(0, 128, 255, 0.7) !important;
  }
}

mohsenasm avatar Sep 20 '23 11:09 mohsenasm