6eDesign / svelte-calendar

A lightweight datepicker with neat animations and a unique UX.
https://6edesign.github.io/svelte-calendar/
MIT License
543 stars 90 forks source link

How to style certain days on the calendar #137

Closed rotimi-best closed 3 years ago

rotimi-best commented 3 years ago

Hi everyone, is anyone aware of a way of for example adding dots to certain dates like in this screenshot.

Use case

I am building an app that needs to show various events for certain days, I want my users to know what specific days they have an event.

Thanks 👍🏾 🙏

telegram-cloud-photo-size-2-5190901288332801410-x

rotimi-best commented 3 years ago

My hacky solution

type DataByDayIndexType = {
  [dayIndex: number]: [];
};

export type DataByMonthIndexType = {
  [monthIndex: number]: DataByDayIndexType;
};

// Here is a mapping of my data
const dataByMonth: DataByMonthIndexType = {
  // January
  [0]: {
    // 1st of January
    [1]: [
      // Some data I have for this day
    ]
  },
  // December
  [11]: {
    // 25th of December
    [25]: []
  }
}

/**
 * 
 * Adds dots to the dates on the calendar
 * 
 * @param currentDate {string | number | Date} The date or month currently selected
 * @param currentMonthIndexInRenderedMonth {number} This value can be either 1 or 2. 
 * According to the implementation of the calendar it renders the previous month, 
 * current month and next month. But only shows the current month. 
 * Every time you move backward in the calendar the current month render 
 * is always index 1 (out of 0, 1 and 2) while forward it's 2.
 * @returns void
 */
const addDotsToCalendar = (
  currentDate: string | number | Date,
  currentMonthIndexInRenderedMonth: number
): void => {
  const calendar = document.getElementById('calendar');

  // Target the container with the months. For some wierd reason there is no specific class placed on the current month displayed to the user, which is why these query returns an array of 3 [0, 1, 2]
  const renderedMonths =
    calendar?.querySelectorAll('.contents .container .stage .grid .grid') ||
    [];
  const currentMonthIndex = new Date(currentDate).getMonth();
  const currentMonth = renderedMonths[currentMonthIndexInRenderedMonth];

  // All elements for each day in the viewable month rendered - without the dates of the previous or next month
  const currentMonthElements =
    currentMonth?.querySelectorAll<HTMLElement>('a:not(.outsider)') || [];

  const currentMonthData = dataByMonth[currentMonthIndex];

  // For most months I don't have anything to display on the calendar, so I avoid looping through each date
  if (!currentMonthData) return;

  for (let i in currentMonthElements) {
    if (!currentMonthElements.hasOwnProperty(i)) {
      continue;
    }

    const dayElement = currentMonthElements[i];
    dayElement.style.position = 'relative';

    // The date is inside the element like <a>2</a>
    const dateIndex = Number(dayElement.innerHTML);
    if (!!currentMonthData[dateIndex]) {
      // I had style targetting .active-day in my style sheet. I didn't want to use inline styles cause I wanted to also target mobile
      dayElement.insertAdjacentHTML(
        'beforeend',
        '<span class="active-day">•</span>'
      );
    }
  }
}
AnnieTaylorCHEN commented 1 year ago

Hi, if I have an array with dates like :

const datesArray = ['2023-01-01', '2023-02-02', '2023-03-03']

and I would want the calendar to match those dates and mark the background color as green, what's the best approach to do so?I'm not sure I got the idea how you did that dot thing.