/
githubmirror
/
zulip
Обзор
Документация
Войти
/
githubmirror
/
zulip
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
web/src/flatpickr.ts
198 строк
7 KB
Anders Kaseorg
eslint: Fix unicorn/prefer-else-if.
29 июл 2026, 02:35
29 июл 2026, 02:35
1418047
Код
Авторство
О чём код?
import {formatISO} from "date-fns"; import flatpickr from "flatpickr"; import confirmDatePlugin from "flatpickr/dist/plugins/confirmDate/confirmDate"; import {$} from "jquery"; import assert from "minimalistic-assert"; import {$t} from "./i18n.ts"; import {user_settings} from "./user_settings.ts"; import * as util from "./util.ts"; export let flatpickr_instance: flatpickr.Instance | undefined; export function is_open(): boolean { return Boolean(flatpickr_instance?.isOpen); } function is_numeric_key(key: string): boolean { return ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"].includes(key); } const ENABLE_TIME_DEFAULT = true; export function show_flatpickr( element: HTMLElement, callback: (time: string) => void, default_timestamp: flatpickr.Options.DateOption, options: flatpickr.Options.Options = {}, hide_confirm_button = false, ): flatpickr.Instance { const $flatpickr_input = $<HTMLInputElement>("<input>").attr("id", "#timestamp_flatpickr"); options.enableTime ??= ENABLE_TIME_DEFAULT; if (hide_confirm_button && options.enableTime) { throw new Error( "`hide_confirm_button` is only supported for date-only flatpickr (set `enableTime` to false).", ); } const plugins: flatpickr.Options.Plugin[] = [...(options.plugins ?? [])]; if (!hide_confirm_button) { plugins.unshift( confirmDatePlugin({ showAlways: true, confirmText: $t({defaultMessage: "Confirm"}), confirmIcon: "", }), ); } function submit(): void { const time = $flatpickr_input.val(); assert(typeof time === "string"); callback(time); } // Called when hide_confirm_button is true and we want to directly // submit on date select. const on_change_hook: flatpickr.Options.Hook = (selectedDates) => { if (selectedDates.length === 0) { return; } submit(); flatpickr_instance?.close(); // Defer destroy until after flatpickr's keydown handler finishes closing. setTimeout(() => { flatpickr_instance?.destroy(); }, 0); }; if (hide_confirm_button) { if (options.onChange === undefined) { options.onChange = on_change_hook; } else if (Array.isArray(options.onChange)) { options.onChange = [...options.onChange, on_change_hook]; } else { options.onChange = [options.onChange, on_change_hook]; } } flatpickr_instance = flatpickr(util.the($flatpickr_input), { mode: "single", clickOpens: false, defaultDate: default_timestamp, positionElement: element, dateFormat: "Z", formatDate: (date) => formatISO(date), disableMobile: true, time_24hr: user_settings.twenty_four_hour_time, minuteIncrement: 1, onKeyDown(_selectedDates, _dateStr, instance, event: KeyboardEvent) { // See also the keydown handler below. // // TODO: Add a clear explanation of exactly how key // interactions are dispatched; it seems that keyboard // logic from this function, the built-in flatpickr // onKeyDown function, and the below keydown handler are // used, but it's not at all clear in what order they are // called, or what the overall control flow is. if (event.key === "Tab") { // Ensure that tab/shift_tab navigation work to // navigate between the elements in flatpickr itself // and the confirmation button at the bottom of the // popover. const enabled_time_options = [ instance.hourElement, instance.minuteElement, ...(user_settings.twenty_four_hour_time ? [] : [instance.amPM]), ]; const elems = [ instance.selectedDateElem, ...(options.enableTime ? enabled_time_options : []), ...(hide_confirm_button ? [] : [$(".flatpickr-confirm")[0]]), ]; assert(event.target instanceof HTMLElement); const i = elems.indexOf(event.target); const n = elems.length; const remain = (i + (event.shiftKey ? -1 : 1)) % n; const target = elems[Math.floor(remain >= 0 ? remain : remain + n)]; event.preventDefault(); event.stopPropagation(); assert(target !== undefined); target.focus(); } else { // Prevent keypresses from propagating to our general hotkey.ts // logic. Without this, `Up` will navigate both in the // flatpickr instance and in the message feed behind // it. event.stopPropagation(); } }, ...options, plugins, }); const $container = $(flatpickr_instance.calendarContainer); $container.on("keydown", (e) => { // Main keyboard UI implementation. if (is_numeric_key(e.key)) { // Let users type numeric values return true; } switch (e.key) { case "Backspace": case "Delete": // Let backspace or delete be handled normally return true; case "Enter": if (e.target.classList[0] === "flatpickr-day") { // use flatpickr's built-in behavior to choose the selected day. return true; } if (!hide_confirm_button) { $container.find(".flatpickr-confirm").trigger("click"); } break; case "Escape": flatpickr_instance?.close(); flatpickr_instance?.destroy(); break; case "Tab": // Use flatpickr's built-in navigation between elements. return true; case "ArrowLeft": case "ArrowUp": case "ArrowRight": case "ArrowDown": // use flatpickr's built-in navigation of the date grid. e.stopPropagation(); return true; } e.stopPropagation(); e.preventDefault(); return true; }); if (!hide_confirm_button) { $container.on("click", ".flatpickr-confirm", () => { submit(); flatpickr_instance?.close(); flatpickr_instance?.destroy(); }); } flatpickr_instance.open(); assert(flatpickr_instance.selectedDateElem !== undefined); flatpickr_instance.selectedDateElem.focus(); return flatpickr_instance; } export function close_all(): void { $(".flatpickr-calendar").removeClass("open"); }