Docs

Reminder Scheduler

a simple card to set up recurring reminders along with frequency and repetition.

requires interactiontoggle switch

Installation

Update tailwind.config.js

Add the following to your tailwind.config.js file.

theme: {
    extend: {
        colors: {
        foreground: "hsl(var(--foreground))",
     },
       transitionTimingFunction: {
        slow: "cubic-bezier(.405, 0, .025, 1)",
        "minor-spring": "cubic-bezier(0.18,0.89,0.82,1.04)",
     }
    },
  },

Run the following command

It will create a new file reminder-scheduler.tsx inside the components/animata/card directory.

mkdir -p components/animata/card && touch components/animata/card/reminder-scheduler.tsx

Paste the code

Open the newly created file and paste the following code:

import React, { useEffect, useState } from "react";
 
import { cn } from "@/lib/utils";
 
interface ReminderSchedulerProps {
  isRepeating: boolean;
  toggleRepeating: () => void;
  repeatInterval: string;
  setRepeatInterval: (interval: string) => void;
  daysOfWeek: string[];
}
 
const ReminderScheduler: React.FC<ReminderSchedulerProps> = ({
  isRepeating,
  toggleRepeating,
  repeatInterval,
  setRepeatInterval,
  daysOfWeek,
}) => {
  const selectedDays = isRepeating ? new Set(["Th", "Fr", "Su"]) : new Set(["Mo", "We", "Sa"]);
  return (
    <div className="mx-auto max-w-sm rounded-3xl border border-gray-200 bg-white p-6 shadow-md">
      {/* Toggle Switch */}
      <div className="mb-4 flex items-center justify-between">
        <span className="text-xl text-black">Is repeating</span>
        <Switch toggle={toggleRepeating} value={isRepeating} />
      </div>
 
      {/* Repeat Interval Dropdown */}
      <div
        className={`mb-4 flex justify-between transition-opacity duration-500 ease-in-out ${!isRepeating ? "opacity-40" : ""}`}
      >
        <label className="mt-5 text-slate-800">Repeat</label>
        <select
          disabled={!isRepeating}
          value={repeatInterval}
          onChange={(e) => setRepeatInterval(e.target.value)}
          className="focus:ring-border-gray-100 mt-2 block w-[70%] rounded-xl border border-gray-100 bg-white px-3 py-3 font-bold text-black shadow-sm focus:border-gray-100 focus:outline-none"
        >
          <option value="Daily">Daily</option>
          <option value="Weekly">Weekly</option>
          <option value="Monthly">Monthly</option>
        </select>
      </div>
 
      {/* Day Selection */}
      <div className="flex items-center rounded-2xl bg-gray-100 p-4">
        <div className="grid grid-cols-7 justify-around gap-2">
          {daysOfWeek.map((day) => (
            <SwapText
              key={day}
              check={selectedDays.has(day)}
              initialText={day}
              finalText={day}
              supportsHover={false}
              initialTextClassName="w-[37px] h-[37px] item-center text-center opacity-50 text-sm text-black rounded-lg p-2"
              finalTextClassName="w-[37px] h-[37px] item-center text-center text-sm	 text-black bg-white rounded-lg p-2"
            />
          ))}
        </div>
      </div>
    </div>
  );
};
 
const Switch = ({ toggle, value }: { toggle: () => void; value: boolean }) => {
  return (
    <label className="inline-flex cursor-pointer items-center">
      <input checked={value} type="checkbox" className="peer sr-only" onChange={toggle} />
      <div className="rtl:peer-checked:after:-translate-x-[unset] peer relative h-8 w-[53px] rounded-full bg-gray-200 transition-colors duration-500 after:absolute after:start-[5px] after:top-[4px] after:h-6 after:w-6 after:rounded-full after:border after:border-white after:bg-white after:transition-all after:duration-300 after:content-[''] peer-checked:bg-[#95ef90] peer-checked:after:translate-x-[19px] peer-checked:after:border-white"></div>
    </label>
  );
};
// credit: author: harimanok_ , https://github.com/hari
interface SwapTextProps extends React.ComponentPropsWithoutRef<"div"> {
  initialText: string;
  finalText: string;
  supportsHover?: boolean;
  textClassName?: string;
  initialTextClassName?: string;
  finalTextClassName?: string;
  disableClick?: boolean;
  check?: boolean;
}
 
function SwapText({
  initialText,
  finalText,
  className,
  supportsHover = true,
  textClassName,
  initialTextClassName,
  finalTextClassName,
  disableClick,
  check,
  ...props
}: SwapTextProps) {
  const [active, setActive] = useState<boolean>(!check);
  useEffect(() => {
    let timeoutId: NodeJS.Timeout;
    if (check) {
      timeoutId = setTimeout(() => {
        setActive((current) => !current);
      }, 100);
    } else {
      timeoutId = setTimeout(() => {
        setActive((current) => !current);
      }, 100);
    }
    return () => {
      clearTimeout(timeoutId); // clear the timeout when component unmounts
    };
  }, [check]);
  const common = "block transition-all duration-1000 ease-slow";
  const longWord = finalText.length > initialText.length ? finalText : null;
  return (
    <div {...props} className={cn("relative overflow-hidden text-foreground", className)}>
      <div
        className={cn("group cursor-pointer select-none text-3xl font-bold", textClassName)}
        onClick={() => !disableClick && setActive((current) => !current)}
      >
        <span
          className={cn(common, initialTextClassName, {
            "flex flex-col": true,
            "-translate-y-full": active,
            "group-hover:-translate-y-full": supportsHover,
          })}
        >
          {initialText}
          {Boolean(longWord?.length) && <span className="invisible h-0">{longWord}</span>}
        </span>
        <span
          className={cn(`${common} absolute top-full`, finalTextClassName, {
            "-translate-y-full": active,
            "group-hover:-translate-y-full": supportsHover,
          })}
        >
          {finalText}
        </span>
      </div>
    </div>
  );
}
export default ReminderScheduler;

Credits

Built by Prince Yadav

hari