Mobile

ReminderPicker

Today, tomorrow, next week or custom, repeating, with a plain sentence of what was set.

#mobile#interactive#dates#tasks#form#tablet

Demo

Preview

Pay the rent

When
Repeat

Reminds you tomorrow, Monday 2 November, at 9:00.

When
Repeat
Every
2 weeks

Reminds you tomorrow, Saturday 28 February, at 1:30 PM, then every 2 weeks on Tuesday and Saturday, 4 times in all.

Next: Sat 28 Feb, Tue 10 Mar, Sat 14 Mar

Get it

It needs theme.qwe, as every component does; or take the whole kit, quillo-ui.qwe. Each component brings its own styles to the pages that show it.

$ curl -O https://quillolang.org/components/ui/theme.qwe$ curl -O https://quillolang.org/components/ui/reminder_picker.qwe
main.qwe
import ui/theme.qwe use theme_cssuse ui/reminder_picker.qweroute GET / = <html><head><style>{theme_css}</style></head><body class="ui-root">...</body></html>

Example

The code of the preview above.

demos/reminder_picker.qwe
component ReminderPickerDemoLive() {    state got = ""    state last = ""    state cleared = false    <div.demo-stack>        <ReminderPicker id="reminder-demo" today="2026-11-01" now="10:20" about="Pay the rent"                        on_set={(r) => { got = `${r.at} | ${r.sentence} | ${join(r.next, " ")}` }}                        on_change={(r) => { last = r != null ? r.sentence : "cannot be set" }}/>        <p.demo-note id="reminder-got">{got}</p>        <p.demo-note id="reminder-last">{last}</p>        // Twelve-hour, late in the day, and given a value; and bad data. Radio groups stay apart.        <div id="reminder-late"><ReminderPicker id="reminder-two" today="2026-02-27" now="23:10" hour12={true}            value={{ date: "2026-02-28", time: "13:30", rule: { unit: "week", every: 2, days: [2, 6], ends: "after", times: 4 } }} on_clear={() => { cleared = true }}/></div>        <p.demo-note id="reminder-cleared">{cleared ? "cleared" : ""}</p>        <div id="reminder-bad" hidden="true"><ReminderPicker id="reminder-three" today="nope" now="99:99" time="later" value={{ date: "2026-02-30", rule: "no" }}/></div>    </div>}view ReminderPickerDemo() = <div.demo-stack><ReminderPickerDemoLive/></div>

Source

ui/reminder_picker.qwe
// ReminderPicker: when to be reminded. Later today, tomorrow, next week or a day of one's own,// at a time, once or again and again, and it says in a plain sentence what will happen,// with the next few days, so nobody has to work out what "every 2 weeks on Monday" comes to.// A monthly reminder on the 31st falls on the last day of a shorter month, and says so.// It picks; the reminder itself is the program's to send, by quilloNative.notify or a// `schedule` job, from what on_set gives it. On a tablet the day and the repeat stand side by side.////   component Bill() {//       <ReminderPicker id="rent" today="2026-11-01" now="10:20" about="Pay the rent"//                       on_set={(r) => { save_reminder(r) }}/>//   }//// id: a prefix for its ids and radio group, one per picker on a page ("reminder"). today:// "YYYY-MM-DD", now: "HH:MM"; left out, the browser's when it starts. about: what it is for, shown// at the top. time: the time a day starts with ("09:00"). hour12: 9:00 AM rather than 9:00 (false).// value: a reminder to start from, as on_set gave it. button: what Set says ("Set reminder"). class.// on_set(reminder): inside one of your components, { date, time, at ("YYYY-MM-DDTHH:MM", local),// rule, sentence, next }. rule is null, or { unit: "day", "weekdays", "week", "month" or "year",// every, days (for a week: 1 for Monday to 7 for Sunday), ends: "never", "on" or "after", until, times }.// next: the first three dates. on_change(reminder): the same at each change, null while it// cannot be set. on_clear(): shown with a value, the Remove button was pressed.import ui/theme.qwe use ui_clslet reminder_picker_css = css`.ui-reminder { container-type: inline-size; color: var(--ui-fg); font: 15px/1.4 var(--ui-font); }.ui-reminder-inner { display: grid; gap: 14px; }.ui-reminder-about { margin: 0; font-size: 17px; font-weight: 600; overflow-wrap: anywhere; }.ui-reminder-part { display: grid; gap: 10px; min-width: 0; margin: 0; padding: 0; border: 0; }.ui-reminder-part legend { padding: 0; margin-bottom: 6px; color: var(--ui-muted); font-size: 13px; font-weight: 600; }.ui-reminder-choices { display: grid; overflow: hidden; border: 1px solid var(--ui-border); border-radius: var(--ui-radius); background: var(--ui-surface); }.ui-reminder-choice { position: relative; display: flex; align-items: center; gap: 10px; min-height: 56px; padding: 6px 14px; cursor: pointer; -webkit-tap-highlight-color: transparent; }.ui-reminder-choice + .ui-reminder-choice { border-top: 1px solid var(--ui-border); }.ui-reminder-choice input { flex: none; width: 20px; height: 20px; margin: 0; accent-color: var(--ui-accent); }.ui-reminder-choice:has(input:disabled) { color: var(--ui-muted); cursor: default; }.ui-reminder-choice:has(input:focus-visible) { box-shadow: inset var(--ui-focus); }.ui-reminder-name { flex: 1; min-width: 0; font-weight: 500; }.ui-reminder-hint { flex: none; color: var(--ui-muted); font-size: 14px; }.ui-reminder-fields { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 10px; }.ui-reminder-field { display: grid; gap: 4px; min-width: 0; color: var(--ui-muted); font-size: 13px; }.ui-reminder-input { box-sizing: border-box; width: 100%; min-width: 0; min-height: 46px; padding: 0 10px; border: 1px solid var(--ui-border); border-radius: var(--ui-radius-sm); background: var(--ui-surface); color: var(--ui-fg); font: 16px/1.2 var(--ui-font); appearance: none; -webkit-appearance: none; }select.ui-reminder-input { appearance: auto; -webkit-appearance: auto; }.ui-reminder-input:focus-visible { outline: none; border-color: var(--ui-accent); box-shadow: var(--ui-focus); }.ui-reminder-step { display: flex; align-items: center; gap: 4px; }.ui-reminder-step button { display: grid; flex: none; place-items: center; width: 46px; height: 46px; padding: 0; border: 1px solid var(--ui-border); border-radius: var(--ui-radius-sm); background: var(--ui-surface); color: var(--ui-fg); font: 500 20px/1 var(--ui-font); cursor: pointer; touch-action: manipulation; }.ui-reminder-step button:disabled { color: var(--ui-strong); cursor: default; }.ui-reminder-step span { flex: 1; min-width: 44px; color: var(--ui-fg); font-size: 16px; text-align: center; }.ui-reminder-step button:focus-visible, .ui-reminder-day:focus-visible, .ui-reminder-set:focus-visible, .ui-reminder-clear:focus-visible { outline: none; box-shadow: var(--ui-focus); }.ui-reminder-days { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 4px; }.ui-reminder-day { min-width: 0; min-height: 46px; padding: 0; border: 1px solid var(--ui-border); border-radius: var(--ui-radius-sm); background: var(--ui-surface); color: var(--ui-fg); font: 500 13px/1 var(--ui-font); cursor: pointer; touch-action: manipulation; }.ui-reminder-day[aria-pressed="true"] { border-color: var(--ui-accent); background: var(--ui-accent); color: var(--ui-accent-fg); }.ui-reminder-say { margin: 0; padding: 12px 14px; border-radius: var(--ui-radius); background: var(--ui-accent-soft); overflow-wrap: anywhere; }.ui-reminder-say[data-bad] { background: var(--ui-danger-soft); color: var(--ui-danger); }.ui-reminder-next { margin: 6px 0 0; color: var(--ui-muted); font-size: 13px; }.ui-reminder-next:empty { display: none; }.ui-reminder-end { display: flex; flex-wrap: wrap; gap: 8px; }.ui-reminder-set, .ui-reminder-clear { flex: 1; min-height: 46px; padding: 0 16px; border: 0; border-radius: var(--ui-radius-sm); background: var(--ui-accent); color: var(--ui-accent-fg); font: 600 15px/1 var(--ui-font); cursor: pointer; touch-action: manipulation; }.ui-reminder-set:disabled { background: var(--ui-subtle); color: var(--ui-muted); cursor: default; }.ui-reminder-clear { flex: 0 0 auto; background: var(--ui-danger-soft); color: var(--ui-danger); }/* A tablet has the room to show both questions at once. */@container (min-width: 600px) {  .ui-reminder-inner { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); column-gap: 24px; align-items: start; }  .ui-reminder-about, .ui-reminder-told, .ui-reminder-end { grid-column: 1 / -1; }  .ui-reminder-end { justify-content: flex-end; }  .ui-reminder-set { flex: 0 0 auto; min-width: 200px; }}@media (hover: hover) {  .ui-reminder-choice:hover { background: var(--ui-subtle); }}`let reminder_picker_weekdays = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]fn reminder_picker_pad(n: int) -> str = n < 10 ? `0${n}` : str(n)fn reminder_picker_time_ok(t) -> bool = type_of(t) == "str" && re.test(t, "^([01][0-9]|2[0-3]):[0-5][0-9]$")fn reminder_picker_minutes(t: str) -> int = int(slice(t, 0, 2)) * 60 + int(slice(t, 3, 5))// A full hour at least one hour off, or null when that runs past 23:00.fn reminder_picker_later(now: str) {    let m = reminder_picker_minutes(now) + 60    let up = m % 60 == 0 ? m : m + 60 - m % 60    up > 1380 ? null : `${reminder_picker_pad(int(up / 60))}:00`}// 1st, 2nd, 3rd, 4th, 11th, 21st.fn reminder_picker_ordinal(n: int) -> str {    let tail = n % 100 >= 11 && n % 100 <= 13 ? "th" : match n % 10 {        1 => "st"        2 => "nd"        3 => "rd"        _ => "th"    }    `${n}${tail}`}// "9:00", or "9:00 AM" and "12:30 PM" with hour12.fn reminder_picker_clock(t: str, hour12: bool) -> str {    let h = int(slice(t, 0, 2))    let m = slice(t, 3, 5)    if !hour12 { return `${h}:${m}` }    let shown = h % 12 == 0 ? 12 : h % 12    `${shown}:${m} ${h < 12 ? "AM" : "PM"}`}// "Monday", "Monday and Thursday", "Monday, Wednesday and Friday".fn reminder_picker_and(names: list) -> str {    let n = len(names)    if n == 0 { return "" }    if n == 1 { return names[0] }    `${join(slice(names, 0, n - 1), ", ")} and ${names[n - 1]}`}fn reminder_picker_day_words(d: str, today: str) -> str {    let n = date.days_between(today, d)    let year = slice(d, 0, 4) != slice(today, 0, 4) ? date.format(d, "dddd D MMMM YYYY") : date.format(d, "dddd D MMMM")    if n == 0 { return "today" }    if n == 1 { return `tomorrow, ${year},` }    `on ${year}`}fn reminder_picker_repeat_words(rule, start: str) -> str {    if rule == null { return "" }    let n = rule.every    let day = int(slice(start, 8, 10))    let body = match rule.unit {        "day" => n == 1 ? "every day" : `every ${n} days`        "weekdays" => "every weekday"        "week" => {            let names = rule.days | map((d) => reminder_picker_weekdays[d - 1])            `${n == 1 ? "every week" : `every ${n} weeks`} on ${reminder_picker_and(names)}`        }        "month" => {            let spot = `on the ${reminder_picker_ordinal(day)}`            let tail = day > 28 ? ", or the last day of a shorter month" : ""            `${n == 1 ? "every month" : `every ${n} months`} ${spot}${tail}`        }        _ => {            let leap = slice(start, 5, 10) == "02-29"            let spot = date.format(start, "D MMMM")            `${n == 1 ? "every year" : `every ${n} years`} on ${spot}${leap ? ", or 28 February in other years" : ""}`        }    }    body}fn reminder_picker_end_words(rule, today: str) -> str {    if rule == null || rule.ends == "never" { return "" }    if rule.ends == "on" {        let same = slice(rule.until, 0, 4) == slice(today, 0, 4)        return `, until ${date.format(rule.until, same ? "D MMMM" : "D MMMM YYYY")}`    }    rule.times == 1 ? ", once only" : `, ${rule.times} times in all`}// The sentence for a reminder.fn reminder_picker_sentence(d: str, t: str, rule, today: str, hour12: bool) -> str {    let first = `${reminder_picker_day_words(d, today)} at ${reminder_picker_clock(t, hour12)}`    let again = reminder_picker_repeat_words(rule, d)    let tail = again != "" ? `, then ${again}` : ""    `Reminds you ${first}${tail}${reminder_picker_end_words(rule, today)}.`}// The dates it goes off, the first `count`, past none of the rule's ends.fn reminder_picker_dates(start: str, rule, count: int) -> list {    if rule == null { return [start] }    let limit = rule.ends == "after" ? min([count, rule.times]) : count    let stop = rule.ends == "on" ? rule.until : "9999-12-31"    let out = []    let i = 0    // A rule can pass many days between dates, but never many hundred.    while len(out) < limit && i < 1500 {        let d = reminder_picker_nth(start, rule, i)        if d == null || d > stop { return out }        if d != "" { out = out + [d] }        i += 1    }    out}// The i-th candidate of a rule: a date, "" for one that does not count, null past the end of the calendar.fn reminder_picker_nth(start: str, rule, i: int) {    let y = int(slice(start, 0, 4))    if y + (rule.unit == "year" ? i * rule.every : 0) > 9000 { return null }    if rule.unit == "day" { return date.add_days(start, i * rule.every) }    if rule.unit == "weekdays" {        let d = date.add_days(start, i)        return date.weekday(d) <= 5 ? d : ""    }    if rule.unit == "month" { return date.add_months(start, i * rule.every) }    if rule.unit == "year" { return date.add_years(start, i * rule.every) }    // Weeks: i counts days from the Monday of the first week, and only every n-th week has any.    let monday = date.add_days(start, 1 - date.weekday(start))    let d = date.add_days(monday, i)    let week = int(i / 7)    if week % rule.every != 0 || d < start { return "" }    contains(rule.days, date.weekday(d)) ? d : ""}// The rule from the fields, or null for no repeat.fn reminder_picker_rule(unit: str, every: int, days: list, ends: str, until: str, times: str, start: str) {    if unit == "none" { return null }    let wd = date.weekday(start)    let week_days = sort(unique(len(days) > 0 ? days : [wd]))    let count = re.test(times, "^[0-9]{1,3}$") ? int(times) : 0    { unit, every: unit == "weekdays" ? 1 : every, days: unit == "week" ? week_days : [], ends, until: ends == "on" ? until : null, times: ends == "after" ? count : null }}// What stops it being set, in words, or "".fn reminder_picker_problem(d, t, rule, today: str, now: str) -> str {    if d == null || !date.valid(d, "date") { return "Pick a day." }    if !reminder_picker_time_ok(t) { return "Pick a time." }    if d < today || (d == today && t <= now) { return "That time has gone. Pick a later one." }    if d > "2999-12-31" { return "Pick a day before the year 3000." }    if rule == null { return "" }    if rule.ends == "on" && (rule.until == null || !date.valid(rule.until, "date")) { return "Pick the day it ends." }    if rule.ends == "on" && rule.until < d { return "It ends before the first reminder." }    if rule.ends == "after" && (rule.times < 1 || rule.times > 999) { return "Say how many times, from 1 to 999." }    ""}// Which answer a day means, for a value given back.fn reminder_picker_rule_of(value) {    let r = get(value, "rule")    if type_of(r) != "map" { return { unit: "none", every: 1, days: [], ends: "never", until: "", times: "5" } }    {        unit: contains(["day", "weekdays", "week", "month", "year"], get(r, "unit")) ? r.unit : "none",        every: type_of(get(r, "every")) == "int" ? r.every : 1,        days: type_of(get(r, "days")) == "list" ? r.days : [],        ends: contains(["on", "after"], get(r, "ends")) ? r.ends : "never",        until: type_of(get(r, "until")) == "str" ? r.until : "",        times: get(r, "times") != null ? str(r.times) : "5",    }}component ReminderPicker(id: str = "reminder", today: str = "", now: str = "", about: str? = null, time: str = "09:00", hour12: bool = false, value = null,                         button: str = "Set reminder", on_set = null, on_change = null, on_clear = null, class: str? = null) {    style global reminder_picker_css    let given = type_of(value) == "map" && type_of(get(value, "date")) == "str" && date.valid(value.date, "date") ? value : null    let saved = reminder_picker_rule_of(given != null ? given : {})    state day = date.valid(today, "date") ? today : date.today()    state clock = reminder_picker_time_ok(now) ? now : "00:00"    state choice = given != null ? "custom" : "tomorrow"    state picked = given != null ? given.date : ""    state hours = given != null && reminder_picker_time_ok(get(given, "time")) ? given.time : ""       // the time, once someone has chosen one    state unit = saved.unit    state every = saved.every    state days = saved.days    state ends = saved.ends    state until = saved.until    state times = saved.times    state told = ""    let base_time = reminder_picker_time_ok(time) ? time : "09:00"    let later = reminder_picker_later(clock)    let tomorrow = date.add_days(day, 1)    // On a Sunday the next Monday is tomorrow, already offered, so it is the one after.    let monday = date.add_days(day, date.weekday(day) == 7 ? 8 : 8 - date.weekday(day))    let when = match choice {        "later" => day        "tomorrow" => tomorrow        "week" => monday        _ => picked != "" ? picked : null    }    let at = hours != "" ? hours : choice == "later" && later != null ? later : base_time    let start_ok = when != null && date.valid(when, "date")    let rule = start_ok ? reminder_picker_rule(unit, every, days, ends, until, times, when) : null    let problem = reminder_picker_problem(when, at, rule, day, clock)    let first_days = problem == "" ? reminder_picker_dates(when, rule, 3) : []    let real_start = len(first_days) > 0 ? first_days[0] : when    let sentence = problem == "" ? reminder_picker_sentence(real_start, at, rule, day, hour12) : ""    let result = () => problem != "" ? null : { date: real_start, time: at, at: `${real_start}T${at}`, rule, sentence, next: first_days }    let told_up = () => {        told = ""        on_change && on_change(result())    }    let name = `${id}-when`    let hint_day = (d: str) => date.format(d, "ddd D MMM")    <form class={ui_cls(["ui-reminder", class])} id={id} autocomplete="off"          @mount={(_me) => {              // Asked first: nothing here may read the clock half set.              let t = js_new("Date")              let y = t.getFullYear()              let mo = t.getMonth() + 1              let dd = t.getDate()              let hh = t.getHours()              let mi = t.getMinutes()              if !date.valid(today, "date") { day = date.of(y, mo, dd) }              if !reminder_picker_time_ok(now) { clock = `${reminder_picker_pad(hh)}:${reminder_picker_pad(mi)}` }          }}          @submit={(e) => {              e.preventDefault()              let r = result()              if r != null {                  told = "Set."                  on_set && on_set(r)              }          }}>        <div.ui-reminder-inner>            {about != null && <p.ui-reminder-about>{about}</p>}            <fieldset.ui-reminder-part>                <legend>When</legend>                <div.ui-reminder-choices>                    <label.ui-reminder-choice>                        <input type="radio" name={name} value="later" checked={choice == "later"} disabled={later == null ? true : null}                               @change={() => { choice = "later"; told_up() }}/>                        <span.ui-reminder-name>Later today</span>                        <span.ui-reminder-hint>{later != null ? reminder_picker_clock(later, hour12) : "Too late today"}</span>                    </label>                    <label.ui-reminder-choice>                        <input type="radio" name={name} value="tomorrow" checked={choice == "tomorrow"} @change={() => { choice = "tomorrow"; told_up() }}/>                        <span.ui-reminder-name>Tomorrow</span>                        <span.ui-reminder-hint>{hint_day(tomorrow)}</span>                    </label>                    <label.ui-reminder-choice>                        <input type="radio" name={name} value="week" checked={choice == "week"} @change={() => { choice = "week"; told_up() }}/>                        <span.ui-reminder-name>Next week</span>                        <span.ui-reminder-hint>{hint_day(monday)}</span>                    </label>                    <label.ui-reminder-choice>                        <input type="radio" name={name} value="custom" checked={choice == "custom"} @change={() => { choice = "custom"; told_up() }}/>                        <span.ui-reminder-name>Pick a day</span>                        <span.ui-reminder-hint>{picked != "" && date.valid(picked, "date") ? hint_day(picked) : ""}</span>                    </label>                </div>                <div.ui-reminder-fields>                    {choice == "custom" && <label.ui-reminder-field for={`${id}-date`}>Day                        <input.ui-reminder-input id={`${id}-date`} type="date" min={day} value={picked}                               @input={(e) => { let v = e.value; picked = v; told_up() }}/>                    </label>}                    <label.ui-reminder-field for={`${id}-time`}>Time                        <input.ui-reminder-input id={`${id}-time`} type="time" value={at}                               @input={(e) => { let v = e.value; hours = v; told_up() }}/>                    </label>                </div>            </fieldset>            <fieldset.ui-reminder-part>                <legend>Repeat</legend>                <label.ui-reminder-field for={`${id}-unit`}>How often                    <select.ui-reminder-input id={`${id}-unit`} value={unit}                            @change={(e) => { let v = e.value; unit = v; every = 1; days = []; told_up() }}>                        <option value="none" selected={unit == "none"}>Once</option>                        <option value="day" selected={unit == "day"}>Daily</option>                        <option value="weekdays" selected={unit == "weekdays"}>Every weekday</option>                        <option value="week" selected={unit == "week"}>Weekly</option>                        <option value="month" selected={unit == "month"}>Monthly</option>                        <option value="year" selected={unit == "year"}>Yearly</option>                    </select>                </label>                {(unit == "day" || unit == "week" || unit == "month" || unit == "year") && <div.ui-reminder-field>Every                    <div.ui-reminder-step role="group" aria-label="How many between each">                        <button type="button" aria-label="Fewer" disabled={every <= 1 ? true : null} @click={() => { every = max([1, every - 1]); told_up() }}>−</button>                        <span aria-live="polite">{every} {unit}{every == 1 ? "" : "s"}</span>                        <button type="button" aria-label="More" disabled={every >= 99 ? true : null} @click={() => { every = min([99, every + 1]); told_up() }}>+</button>                    </div>                </div>}                {unit == "week" && <div.ui-reminder-days role="group" aria-label="On which days">                    {for [i, n] in enumerate(reminder_picker_weekdays) => <button.ui-reminder-day type="button" key={n}                            aria-label={n} aria-pressed={contains(rule != null ? rule.days : [], i + 1) ? "true" : "false"}                            @click={() => {                                let now_days = rule != null ? rule.days : []                                let d = i + 1                                days = contains(now_days, d) ? now_days | filter((x) => x != d) : now_days + [d]                                told_up()                            }}>{slice(n, 0, 3)}</button>}                </div>}                {unit != "none" && <div.ui-reminder-fields>                    <label.ui-reminder-field for={`${id}-ends`}>Ends                        <select.ui-reminder-input id={`${id}-ends`} value={ends} @change={(e) => { let v = e.value; ends = v; told_up() }}>                            <option value="never" selected={ends == "never"}>Never</option>                            <option value="on" selected={ends == "on"}>On a day</option>                            <option value="after" selected={ends == "after"}>After some times</option>                        </select>                    </label>                    {ends == "on" && <label.ui-reminder-field for={`${id}-until`}>Last day                        <input.ui-reminder-input id={`${id}-until`} type="date" min={day} value={until} @input={(e) => { let v = e.value; until = v; told_up() }}/>                    </label>}                    {ends == "after" && <label.ui-reminder-field for={`${id}-times`}>Times                        <input.ui-reminder-input id={`${id}-times`} type="text" inputmode="numeric" value={times} @input={(e) => { let v = e.value; times = v; told_up() }}/>                    </label>}                </div>}            </fieldset>            <div.ui-reminder-told>                <p.ui-reminder-say role="status" data-bad={problem != "" ? "" : null}>{problem != "" ? problem : sentence}</p>                <p.ui-reminder-next>{len(first_days) > 1 ? `Next: ${join(first_days | map((d) => hint_day(d)), ", ")}` : ""}{told != "" ? ` ${told}` : ""}</p>            </div>            <div.ui-reminder-end>                {given != null && <button.ui-reminder-clear type="button" @click={() => { on_clear && on_clear() }}>Remove</button>}                <button.ui-reminder-set type="submit" disabled={problem != "" ? true : null}>{button}</button>            </div>        </div>    </form>}
Thread
27Useful? Like it, or add to its thread below.

Thread 0 posts

On ReminderPicker: questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.