@allxsmith/bestax-bulma 5.29.0 → 5.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bestax.css +1 -1
- package/dist/bestax.css.map +1 -1
- package/dist/esm/components/ContextMenu.js +244 -0
- package/dist/esm/components/ContextMenu.js.map +1 -0
- package/dist/esm/components/HoverCard.js +3 -10
- package/dist/esm/components/HoverCard.js.map +1 -1
- package/dist/esm/components/Tooltip.js +36 -61
- package/dist/esm/components/Tooltip.js.map +1 -1
- package/dist/esm/elements/Button.js.map +1 -1
- package/dist/esm/elements/ToggleButton.js +60 -0
- package/dist/esm/elements/ToggleButton.js.map +1 -0
- package/dist/esm/form/DateTimeInputBase.js.map +1 -1
- package/dist/esm/form/TimeInputBase.js.map +1 -1
- package/dist/esm/form/_pickerInternals/pickerTypes.js +14 -6
- package/dist/esm/form/_pickerInternals/pickerTypes.js.map +1 -1
- package/dist/esm/helpers/useContextMenuTrigger.js +166 -0
- package/dist/esm/helpers/useContextMenuTrigger.js.map +1 -0
- package/dist/esm/helpers/useHoverOpen.js +40 -6
- package/dist/esm/helpers/useHoverOpen.js.map +1 -1
- package/dist/esm/helpers/useMenuKeys.js +182 -0
- package/dist/esm/helpers/useMenuKeys.js.map +1 -0
- package/dist/extras.css +1 -1
- package/dist/extras.css.map +1 -1
- package/dist/index.cjs +2078 -1451
- package/dist/index.cjs.map +1 -1
- package/dist/index.esm.js +2 -0
- package/dist/index.esm.js.map +1 -1
- package/dist/types/components/ContextMenu.d.ts +66 -0
- package/dist/types/components/Tooltip.d.ts +45 -4
- package/dist/types/elements/Button.d.ts +1 -1
- package/dist/types/elements/ToggleButton.d.ts +90 -0
- package/dist/types/form/DateTimeInputBase.d.ts +4 -1
- package/dist/types/form/TimeInputBase.d.ts +5 -2
- package/dist/types/form/_pickerInternals/pickerTypes.d.ts +16 -1
- package/dist/types/helpers/useContextMenuTrigger.d.ts +67 -0
- package/dist/types/helpers/useHoverOpen.d.ts +19 -3
- package/dist/types/helpers/useMenuKeys.d.ts +57 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types-cjs/components/ContextMenu.d.ts +66 -0
- package/dist/types-cjs/components/Tooltip.d.ts +45 -4
- package/dist/types-cjs/elements/Button.d.ts +1 -1
- package/dist/types-cjs/elements/ToggleButton.d.ts +90 -0
- package/dist/types-cjs/form/DateTimeInputBase.d.ts +4 -1
- package/dist/types-cjs/form/TimeInputBase.d.ts +5 -2
- package/dist/types-cjs/form/_pickerInternals/pickerTypes.d.ts +16 -1
- package/dist/types-cjs/helpers/useContextMenuTrigger.d.ts +67 -0
- package/dist/types-cjs/helpers/useHoverOpen.d.ts +19 -3
- package/dist/types-cjs/helpers/useMenuKeys.d.ts +57 -0
- package/dist/types-cjs/index.d.ts +2 -0
- package/dist/versions/bestax-no-dark-mode.css +1 -1
- package/dist/versions/bestax-no-dark-mode.css.map +1 -1
- package/dist/versions/bestax-no-helpers-prefixed.css +1 -1
- package/dist/versions/bestax-no-helpers-prefixed.css.map +1 -1
- package/dist/versions/bestax-no-helpers.css +1 -1
- package/dist/versions/bestax-no-helpers.css.map +1 -1
- package/dist/versions/bestax-prefixed.css +1 -1
- package/dist/versions/bestax-prefixed.css.map +1 -1
- package/package.json +1 -1
- package/src/scss/components/_contextmenu.scss +50 -0
- package/src/scss/components/_index.scss +1 -0
- package/src/scss/components/_tooltip.scss +76 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TimeInputBase.js","sources":["../../../src/form/TimeInputBase.tsx"],"sourcesContent":["import React, {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { classNames, usePrefixedClassNames } from '../helpers/classNames';\nimport { useBulmaClasses, BulmaClassesProps } from '../helpers/useBulmaClasses';\nimport {\n PickerPosition,\n HourFormat,\n PickerLabels,\n mergeLabels,\n} from './_pickerInternals/pickerTypes';\nimport {\n formatTime,\n hourCycleFromFormat,\n parseTime,\n DateFormatOption,\n DEFAULT_TIME_FORMAT_24,\n DEFAULT_TIME_FORMAT_12,\n} from './_pickerInternals/formatters';\nimport {\n setTimeOfDay,\n floorMin,\n isWithin,\n snapTimeToIncrement,\n} from './_pickerInternals/dateUtils';\nimport { TimeWheels } from './_pickerInternals/TimeWheels';\nimport { PickerPopover } from './_pickerInternals/PickerPopover';\nimport { useNativeMobilePicker } from './_pickerInternals/useNativeMobilePicker';\nimport { useSegmentedEntry } from './_pickerInternals/useSegmentedEntry';\nimport { useControlLoading } from './controlLoading';\nimport { useFieldLabelTarget } from './FormContext';\nimport { Icon } from '../elements/Icon';\nimport { Buttons } from '../elements/Buttons';\n\nconst toIsoTime = (d: Date, withSeconds: boolean): string => {\n const hh = String(d.getHours()).padStart(2, '0');\n const mm = String(d.getMinutes()).padStart(2, '0');\n if (!withSeconds) return `${hh}:${mm}`;\n const ss = String(d.getSeconds()).padStart(2, '0');\n return `${hh}:${mm}:${ss}`;\n};\n\nconst fromIsoTime = (s: string): Date | null => {\n // The HTML time value may carry fractional seconds (the spec allows them\n // and some engines normalize to `:ss.sss`); accept and drop.\n const m = /^(\\d{2}):(\\d{2})(?::(\\d{2})(?:\\.\\d{1,3})?)?$/.exec(s);\n if (!m) return null;\n const today = new Date();\n return new Date(\n today.getFullYear(),\n today.getMonth(),\n today.getDate(),\n Number(m[1]),\n Number(m[2]),\n m[3] ? Number(m[3]) : 0,\n 0\n );\n};\n\n/**\n * Props for the raw TimeInput base. Use the higher-level `TimeInput` for\n * Field/Control composition; `TimeInputBase` is the input + popover only.\n */\nexport interface TimeInputBaseProps\n extends\n Omit<\n React.InputHTMLAttributes<HTMLInputElement>,\n | 'value'\n | 'defaultValue'\n | 'onChange'\n | 'size'\n | 'color'\n | 'min'\n | 'max'\n | 'type'\n | 'popover'\n >,\n Omit<BulmaClassesProps, 'color'> {\n /** Controlled selected time (date portion is preserved). */\n value?: Date | null;\n /** Initial value for uncontrolled usage. */\n defaultValue?: Date | null;\n /** Fired when the value changes. */\n onChange?: (d: Date | null) => void;\n /** Fired when the popover opens. */\n onOpen?: () => void;\n /** Fired when the popover closes. */\n onClose?: () => void;\n /**\n * Earliest selectable time. A `min` before year 1 is raised to midnight on\n * 1 January of year 1, and a `value` dated before then is out of range, so\n * the wheels and typing change nothing on it, as in the date pickers.\n */\n min?: Date;\n /** Latest selectable time. */\n max?: Date;\n /** Disable the input. */\n disabled?: boolean;\n /** Make the input read-only. */\n readOnly?: boolean;\n /** Placeholder text for the input. */\n placeholder?: string;\n /**\n * Token format string or `Intl.DateTimeFormat` options.\n * @defaultValue (see below)\n */\n format?: DateFormatOption;\n /**\n * Custom parser. Enter and leaving the field call it only if the user\n * changed the text, so focus passing through commits nothing and the value\n * keeps what the format leaves out, such as seconds and the date.\n */\n parse?: (s: string) => Date | null;\n /** BCP-47 locale tag for Intl formatting. */\n locale?: string;\n /** Render the spinner inline (no popover). */\n inline?: boolean;\n /** Use `<input type=\"time\">` on coarse-pointer + small-viewport devices. */\n mobileNative?: boolean | 'auto';\n /** Allow segmented keyboard typing in the input (type the time directly, auto-advancing across segments). `false` makes the field picker-only. */\n editable?: boolean;\n /** Whether the spinner popover exists. `false` makes the field input-only (segmented typing, no popover). */\n popover?: boolean;\n /**\n * Open the popover when the input is focused. Focus that a closing popover\n * hands back to the input leaves it closed. Dismissing it commits nothing:\n * an empty field stays empty, and leaving afterwards commits only what was\n * typed since. With it off, the launcher or Alt+ArrowDown opens it.\n */\n openOnFocus?: boolean;\n /** Close the popover after a time is selected (off by default). */\n closeOnSelect?: boolean;\n /** Popover anchor position relative to the input. */\n position?: PickerPosition;\n /**\n * Render the popover through a portal, into the `portalContainer` a\n * `ConfigProvider` sets, `document.body` by default. On a narrow screen\n * an in-place popover spans the bottom of the viewport, while a portaled\n * one stays on its `position` corner.\n */\n appendToBody?: boolean;\n /**\n * Bulma color modifier for the input, also carried by the wheels, where it\n * colors the selection band. A focused wheel's keyboard focus ring is drawn\n * inside the band in the color's `-invert`, like the selected value, so it\n * shows on the fill. Unset, the wheels use\n * `--bulma-timeinput-wheel-selected-bg`, which defaults to `primary`.\n */\n color?: 'primary' | 'link' | 'info' | 'success' | 'warning' | 'danger';\n /** Size variant. */\n size?: 'small' | 'medium' | 'large';\n /** Render the input with rounded corners. */\n isRounded?: boolean;\n /** Hour format. `'12'` shows an AM/PM toggle. */\n hourFormat?: HourFormat;\n /** Show a seconds column. Note: iOS Safari's native time picker UI does not include a seconds wheel; pass `mobileNative={false}` if you need one on iOS. */\n enableSeconds?: boolean;\n /** Hour step for the spinner. */\n incrementHours?: number;\n /** Minute step. Combine with `min`/`max` for slot-style pickers. */\n incrementMinutes?: number;\n /** Second step. Default `1`. */\n incrementSeconds?: number;\n /** Predicate returning `true` for times that should be skipped. Blocked times are also rejected during manual typing. */\n unselectableTimes?: (d: Date) => boolean;\n /** Decorative left icon glyph for the wrapping `Control` (shown by default). Set `''` to hide. */\n iconLeftName?: string;\n /**\n * Show a clickable launcher button on the right that toggles the popover.\n * Hidden by default while a `Control` it sits in shows its `isLoading`\n * spinner, which shares that right edge.\n * @defaultValue true\n */\n triggerIcon?: boolean;\n /** Glyph for the right launcher button. */\n triggerIconName?: string;\n /** Optional translatable string overrides. */\n labels?: PickerLabels;\n /** Play a short audible click on each wheel-item crossing. Substitute for haptic feedback on iOS Safari (which has no web haptic API as of May 2026); on Android, `navigator.vibrate(5)` fires automatically regardless. */\n audioTick?: boolean;\n /** Auto-route platform-appropriate feedback: vibrate on Android (already happening), audio thunk on iOS (where vibrate is unavailable). One switch instead of platform-sniffing on the consumer side. `audioTick={true}` always wins. */\n haptics?: boolean;\n}\n\n/**\n * Raw TimeInput — input + popover spinner without Field/Control wrapping.\n * Use `TimeInput` for the convenience wrapper.\n *\n * @function\n * @param {TimeInputBaseProps} props\n * @returns {JSX.Element}\n */\nexport const TimeInputBase = forwardRef<HTMLInputElement, TimeInputBaseProps>(\n (props, ref) => {\n const {\n value: controlledValue,\n defaultValue,\n onChange,\n onOpen,\n onClose,\n min,\n max,\n disabled,\n readOnly,\n placeholder,\n format,\n parse,\n locale,\n inline = false,\n mobileNative = 'auto',\n editable = true,\n popover = true,\n openOnFocus = true,\n closeOnSelect = false,\n position = 'bottom-left',\n appendToBody = false,\n color,\n size,\n isRounded,\n hourFormat = '24',\n enableSeconds = false,\n incrementHours = 1,\n incrementMinutes = 1,\n incrementSeconds = 1,\n unselectableTimes,\n className,\n name,\n form,\n required,\n id: idProp,\n onFocus,\n onClick,\n onKeyDown,\n onBlur,\n iconLeftName: _iconLeftName,\n triggerIcon: triggerIconProp,\n triggerIconName = 'chevron-down',\n labels,\n audioTick = false,\n haptics = false,\n ...rest\n } = props;\n // Inside a labeled Field the input takes the id the label points at\n // when the caller set none, as InputBase does, and the popover's ids\n // follow it. Inline there is no input to name, so nothing takes the\n // Field's id or builds on it.\n const id = useFieldLabelTarget(idProp, !inline);\n // The launcher gives way to the loading spinner of a Control this sits in.\n const controlLoading = useControlLoading();\n const triggerIcon = triggerIconProp ?? !controlLoading;\n\n // Platform-appropriate feedback routing: when `haptics` is opted in and\n // the runtime has no Vibration API (notably iOS Safari), enable the\n // audio thunk so the user still gets *some* tactile cue. When vibrate\n // is available (Android Chrome etc.), the existing unconditional\n // `navigator.vibrate(5)` carries the haptic — no audio added unless\n // the consumer explicitly set `audioTick`. `audioTick` always wins.\n const hasVibrate =\n typeof navigator !== 'undefined' &&\n typeof navigator.vibrate === 'function';\n const effectiveAudioTick = audioTick || (haptics && !hasVibrate);\n const t = mergeLabels(labels);\n\n const isControlled = controlledValue !== undefined;\n const [internalValue, setInternalValue] = useState<Date | null>(\n defaultValue ?? null\n );\n const value = isControlled ? (controlledValue ?? null) : internalValue;\n\n const defaultFormat: DateFormatOption =\n format ??\n (hourFormat === '12'\n ? enableSeconds\n ? 'hh:mm:ss A'\n : DEFAULT_TIME_FORMAT_12\n : enableSeconds\n ? 'HH:mm:ss'\n : DEFAULT_TIME_FORMAT_24);\n\n // The displayed format is the source of truth for the hour cycle: an\n // explicit 12-hour `format` must drive a 12-hour wheel even when\n // `hourFormat` was left at its default. Fall back to the raw prop only when\n // the cycle can't be read from the format (an Intl-options object, or no\n // hour token).\n const effectiveHourFormat =\n hourCycleFromFormat(defaultFormat) ?? hourFormat;\n\n const [open, setOpenState] = useState(false);\n const [text, setText] = useState<string>(\n value ? formatTime(value, defaultFormat, locale) : ''\n );\n\n const inputRef = useRef<HTMLInputElement>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n // Snapshot of value at the moment the popover opens, used by Cancel.\n const valueAtOpenRef = useRef<Date | null>(null);\n const reactId = useId();\n const popoverId = id ? `${id}-popover` : `picker-${reactId}`;\n\n const { bulmaHelperClasses, rest: cleanRest } = useBulmaClasses(rest);\n\n const { shouldUseNative, isSmallViewport } = useNativeMobilePicker({\n force: mobileNative === 'auto' ? undefined : mobileNative,\n });\n const useNative = !inline && shouldUseNative;\n const wheelItemHeight = isSmallViewport ? 40 : 32;\n\n const inputClass = usePrefixedClassNames('input', {\n [`is-${color}`]: !!color,\n [`is-${size}`]: !!size,\n 'is-rounded': isRounded,\n });\n const containerClass = usePrefixedClassNames('timeinput-container');\n const triggerClass = usePrefixedClassNames('timeinput-trigger');\n const panelClass = usePrefixedClassNames('timeinput-panel');\n const footerClass = usePrefixedClassNames('timeinput-footer');\n const footerButton = usePrefixedClassNames('button', 'is-small');\n const footerOkClass = usePrefixedClassNames(\n 'button',\n 'is-small',\n 'timeinput-footer-ok'\n );\n const mobileFooterClass = usePrefixedClassNames('timeinput-footer-mobile');\n const mobileFooterResetClass = usePrefixedClassNames(\n 'timeinput-footer-reset'\n );\n const mobileFooterDoneClass = usePrefixedClassNames(\n 'timeinput-footer-done'\n );\n\n // What the latest request asked for, so a second request in the same\n // event sees it before React renders. The callbacks fire here rather than\n // in a state updater, which StrictMode calls twice, and after the state\n // is set, so a request a callback makes is the one that lands last.\n const requestedOpenRef = useRef(false);\n const setOpen = useCallback(\n (next: boolean) => {\n if (requestedOpenRef.current === next) return;\n requestedOpenRef.current = next;\n setOpenState(next);\n if (next) {\n valueAtOpenRef.current = value;\n onOpen?.();\n } else {\n onClose?.();\n }\n },\n [onOpen, onClose, value]\n );\n\n useEffect(() => {\n setText(value ? formatTime(value, defaultFormat, locale) : '');\n }, [value, defaultFormat, locale]);\n\n // A value's date before year 1 is out of range, as in the date pickers.\n const lowerBound = useMemo(() => floorMin(min), [min]);\n\n const commitValue = useCallback(\n (next: Date | null) => {\n if (!isControlled) setInternalValue(next);\n onChange?.(next);\n },\n [isControlled, onChange]\n );\n\n // An empty field starts from today, at the whole minute or second the\n // wheels show.\n const handleSpinnerChange = useCallback(\n (parts: { hours: number; minutes: number; seconds?: number }) => {\n const next = value\n ? setTimeOfDay(value, parts)\n : setTimeOfDay(new Date(), {\n ...parts,\n seconds: parts.seconds ?? 0,\n milliseconds: 0,\n });\n if (!isWithin(next, lowerBound, max)) return;\n commitValue(next);\n },\n [value, lowerBound, max, commitValue]\n );\n\n const tryParse = useCallback(\n (s: string): Date | null => {\n const trimmed = s.trim();\n if (!trimmed) return null;\n const fmt = typeof format === 'string' ? format : undefined;\n return parse\n ? parse(trimmed)\n : parseTime(\n trimmed,\n fmt ??\n (typeof defaultFormat === 'string'\n ? defaultFormat\n : DEFAULT_TIME_FORMAT_24)\n );\n },\n [parse, format, defaultFormat]\n );\n\n // The Date the user edits when starting without a current value. Noon so\n // the 12h / 24h hour calculations are unambiguous.\n const makeBaseDate = useCallback((): Date => {\n const d = new Date();\n d.setHours(12, 0, 0, 0);\n return d;\n }, []);\n\n const inputReadOnlyAttr = !!readOnly || !editable;\n const canOpen = !!popover && !disabled && !readOnly;\n\n const { inputHandlers } = useSegmentedEntry({\n format: defaultFormat,\n value,\n commitValue,\n formatFn: formatTime,\n tryParse,\n text,\n setText,\n makeBaseDate,\n locale,\n min: lowerBound,\n max,\n isBlocked: unselectableTimes,\n disabled,\n readOnly,\n editable,\n popover,\n openOnFocus,\n closeOnSelect,\n isOpen: open,\n setOpen,\n inputRef,\n containerRef,\n onFocus,\n onClick,\n onKeyDown,\n onBlur,\n });\n\n const combinedRef = useCallback(\n (node: HTMLInputElement | null) => {\n (inputRef as React.MutableRefObject<HTMLInputElement | null>).current =\n node;\n if (typeof ref === 'function') ref(node);\n else if (ref)\n (ref as React.MutableRefObject<HTMLInputElement | null>).current =\n node;\n },\n [ref]\n );\n\n if (useNative) {\n const nativeStep = enableSeconds\n ? incrementSeconds\n : incrementMinutes * 60;\n return (\n <input\n {...cleanRest}\n ref={combinedRef}\n type=\"time\"\n step={nativeStep}\n className={classNames(inputClass, bulmaHelperClasses, className)}\n value={value ? toIsoTime(value, enableSeconds) : ''}\n onChange={e => {\n const parsed = e.target.value ? fromIsoTime(e.target.value) : null;\n commitValue(parsed);\n }}\n min={min ? toIsoTime(min, enableSeconds) : undefined}\n max={max ? toIsoTime(max, enableSeconds) : undefined}\n disabled={disabled}\n readOnly={readOnly}\n placeholder={placeholder}\n name={name}\n form={form}\n required={required}\n id={id}\n />\n );\n }\n\n const spinnerValue = {\n hours: value?.getHours() ?? 0,\n minutes: value?.getMinutes() ?? 0,\n seconds: enableSeconds ? (value?.getSeconds() ?? 0) : undefined,\n };\n\n const panel = (\n <div className={panelClass}>\n <TimeWheels\n value={spinnerValue}\n onChange={handleSpinnerChange}\n hourFormat={effectiveHourFormat}\n enableSeconds={enableSeconds}\n incrementHours={incrementHours}\n incrementMinutes={incrementMinutes}\n incrementSeconds={incrementSeconds}\n unselectableTimes={unselectableTimes}\n color={color}\n size={size}\n disabled={disabled}\n // The popover panel takes `popoverId`, so the wheels inside it take\n // one of their own. Inline there is no panel, and the wheels keep\n // the id they released with.\n id={inline ? popoverId : `${popoverId}-time`}\n labels={labels}\n itemHeight={wheelItemHeight}\n audioTick={effectiveAudioTick}\n onCommit={() => setOpen(false)}\n />\n {/* Footer is popover-only. Desktop gets the OK / Now / Cancel triad;\n mobile drops to an iOS-style \"Reset / ✓\" pair to match the OS\n picker's footer affordances and free up vertical space on small\n screens. */}\n {!inline && !isSmallViewport && (\n <Buttons className={footerClass} hasAddons>\n <button\n type=\"button\"\n className={footerOkClass}\n onClick={() => setOpen(false)}\n >\n {t.ok}\n </button>\n <button\n type=\"button\"\n className={footerButton}\n onClick={() => {\n commitValue(\n snapTimeToIncrement(new Date(), {\n incrementHours,\n incrementMinutes,\n incrementSeconds,\n enableSeconds,\n })\n );\n setOpen(false);\n }}\n >\n {t.now}\n </button>\n <button\n type=\"button\"\n className={footerButton}\n onClick={() => {\n commitValue(valueAtOpenRef.current);\n setOpen(false);\n }}\n >\n {t.cancel}\n </button>\n </Buttons>\n )}\n {!inline && isSmallViewport && (\n <div className={mobileFooterClass}>\n <button\n type=\"button\"\n className={mobileFooterResetClass}\n onClick={() => {\n commitValue(valueAtOpenRef.current);\n setOpen(false);\n }}\n >\n {t.reset}\n </button>\n <button\n type=\"button\"\n className={mobileFooterDoneClass}\n aria-label={t.done}\n onClick={() => setOpen(false)}\n >\n <svg\n width=\"22\"\n height=\"22\"\n viewBox=\"0 0 22 22\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M5 11l4 4 8-8\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </button>\n </div>\n )}\n </div>\n );\n\n if (inline) {\n return (\n <div\n {...cleanRest}\n ref={containerRef}\n className={classNames(containerClass, bulmaHelperClasses, className)}\n >\n {panel}\n {name && (\n <input\n type=\"hidden\"\n name={name}\n form={form}\n value={value ? toIsoTime(value, enableSeconds) : ''}\n required={required}\n />\n )}\n </div>\n );\n }\n\n return (\n <div\n ref={containerRef}\n className={classNames(containerClass, bulmaHelperClasses, className)}\n {...cleanRest}\n >\n <input\n ref={combinedRef}\n type=\"text\"\n role=\"combobox\"\n aria-haspopup=\"dialog\"\n aria-expanded={open}\n aria-controls={popoverId}\n autoComplete=\"off\"\n className={inputClass}\n value={text}\n placeholder={placeholder}\n disabled={disabled}\n readOnly={inputReadOnlyAttr}\n name={name}\n form={form}\n required={required}\n id={id}\n {...inputHandlers}\n />\n {popover && triggerIcon && (\n <button\n type=\"button\"\n className={triggerClass}\n onClick={() => {\n if (canOpen) setOpen(!open);\n }}\n disabled={!canOpen}\n aria-label={t.chooseTime}\n aria-haspopup=\"dialog\"\n aria-controls={popoverId}\n aria-expanded={open}\n tabIndex={canOpen ? 0 : -1}\n >\n <Icon name={triggerIconName} size={size} />\n </button>\n )}\n {popover && (\n <PickerPopover\n isOpen={open}\n onClose={() => setOpen(false)}\n anchorRef={containerRef}\n position={position}\n appendToBody={appendToBody}\n ariaLabel={t.chooseTime}\n id={popoverId}\n restoreFocusRef={inputRef}\n >\n {panel}\n </PickerPopover>\n )}\n </div>\n );\n }\n);\n\nTimeInputBase.displayName = 'TimeInputBase';\n\nexport default TimeInputBase;\n"],"names":["_jsx","_jsxs"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TimeInputBase.js","sources":["../../../src/form/TimeInputBase.tsx"],"sourcesContent":["import React, {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { classNames, usePrefixedClassNames } from '../helpers/classNames';\nimport { useBulmaClasses, BulmaClassesProps } from '../helpers/useBulmaClasses';\nimport {\n PickerPosition,\n HourFormat,\n PickerLabels,\n mergeLabels,\n} from './_pickerInternals/pickerTypes';\nimport {\n formatTime,\n hourCycleFromFormat,\n parseTime,\n DateFormatOption,\n DEFAULT_TIME_FORMAT_24,\n DEFAULT_TIME_FORMAT_12,\n} from './_pickerInternals/formatters';\nimport {\n setTimeOfDay,\n floorMin,\n isWithin,\n snapTimeToIncrement,\n} from './_pickerInternals/dateUtils';\nimport { TimeWheels } from './_pickerInternals/TimeWheels';\nimport { PickerPopover } from './_pickerInternals/PickerPopover';\nimport { useNativeMobilePicker } from './_pickerInternals/useNativeMobilePicker';\nimport { useSegmentedEntry } from './_pickerInternals/useSegmentedEntry';\nimport { useControlLoading } from './controlLoading';\nimport { useFieldLabelTarget } from './FormContext';\nimport { Icon } from '../elements/Icon';\nimport { Buttons } from '../elements/Buttons';\n\nconst toIsoTime = (d: Date, withSeconds: boolean): string => {\n const hh = String(d.getHours()).padStart(2, '0');\n const mm = String(d.getMinutes()).padStart(2, '0');\n if (!withSeconds) return `${hh}:${mm}`;\n const ss = String(d.getSeconds()).padStart(2, '0');\n return `${hh}:${mm}:${ss}`;\n};\n\nconst fromIsoTime = (s: string): Date | null => {\n // The HTML time value may carry fractional seconds (the spec allows them\n // and some engines normalize to `:ss.sss`); accept and drop.\n const m = /^(\\d{2}):(\\d{2})(?::(\\d{2})(?:\\.\\d{1,3})?)?$/.exec(s);\n if (!m) return null;\n const today = new Date();\n return new Date(\n today.getFullYear(),\n today.getMonth(),\n today.getDate(),\n Number(m[1]),\n Number(m[2]),\n m[3] ? Number(m[3]) : 0,\n 0\n );\n};\n\n/**\n * Props for the raw TimeInput base. Use the higher-level `TimeInput` for\n * Field/Control composition; `TimeInputBase` is the input + popover only.\n */\nexport interface TimeInputBaseProps\n extends\n Omit<\n React.InputHTMLAttributes<HTMLInputElement>,\n | 'value'\n | 'defaultValue'\n | 'onChange'\n | 'size'\n | 'color'\n | 'min'\n | 'max'\n | 'type'\n | 'popover'\n >,\n Omit<BulmaClassesProps, 'color'> {\n /** Controlled selected time (date portion is preserved). */\n value?: Date | null;\n /** Initial value for uncontrolled usage. */\n defaultValue?: Date | null;\n /** Fired when the value changes. */\n onChange?: (d: Date | null) => void;\n /** Fired when the popover opens. */\n onOpen?: () => void;\n /** Fired when the popover closes. */\n onClose?: () => void;\n /**\n * Earliest selectable time. A `min` before year 1 is raised to midnight on\n * 1 January of year 1, and a `value` dated before then is out of range, so\n * the wheels and typing change nothing on it, as in the date pickers.\n */\n min?: Date;\n /** Latest selectable time. */\n max?: Date;\n /** Disable the input. */\n disabled?: boolean;\n /** Make the input read-only. */\n readOnly?: boolean;\n /** Placeholder text for the input. */\n placeholder?: string;\n /**\n * Token format string or `Intl.DateTimeFormat` options.\n * @defaultValue (see below)\n */\n format?: DateFormatOption;\n /**\n * Custom parser. Enter and leaving the field call it only if the user\n * changed the text, so focus passing through commits nothing and the value\n * keeps what the format leaves out, such as seconds and the date.\n */\n parse?: (s: string) => Date | null;\n /** BCP-47 locale tag for Intl formatting. */\n locale?: string;\n /** Render the spinner inline (no popover). */\n inline?: boolean;\n /** Use `<input type=\"time\">` on coarse-pointer + small-viewport devices. */\n mobileNative?: boolean | 'auto';\n /** Allow segmented keyboard typing in the input (type the time directly, auto-advancing across segments). `false` makes the field picker-only. */\n editable?: boolean;\n /** Whether the spinner popover exists. `false` makes the field input-only (segmented typing, no popover). */\n popover?: boolean;\n /**\n * Open the popover when the input is focused. Focus that a closing popover\n * hands back to the input leaves it closed. Dismissing it commits nothing:\n * an empty field stays empty, and leaving afterwards commits only what was\n * typed since. With it off, the launcher or Alt+ArrowDown opens it.\n */\n openOnFocus?: boolean;\n /** Close the popover after a time is selected (off by default). */\n closeOnSelect?: boolean;\n /** Popover anchor position relative to the input. */\n position?: PickerPosition;\n /**\n * Render the popover through a portal, into the `portalContainer` a\n * `ConfigProvider` sets, `document.body` by default. On a narrow screen\n * an in-place popover spans the bottom of the viewport, while a portaled\n * one stays on its `position` corner.\n */\n appendToBody?: boolean;\n /**\n * Bulma color modifier for the input, also carried by the wheels, where it\n * colors the selection band. A focused wheel's keyboard focus ring is drawn\n * inside the band in the color's `-invert`, like the selected value, so it\n * shows on the fill. Unset, the wheels use\n * `--bulma-timeinput-wheel-selected-bg`, which defaults to `primary`.\n */\n color?: 'primary' | 'link' | 'info' | 'success' | 'warning' | 'danger';\n /** Size variant. */\n size?: 'small' | 'medium' | 'large';\n /** Render the input with rounded corners. */\n isRounded?: boolean;\n /** Hour format. `'12'` adds an AM/PM column to the wheels. */\n hourFormat?: HourFormat;\n /** Show a seconds column. Note: iOS Safari's native time picker UI does not include a seconds wheel; pass `mobileNative={false}` if you need one on iOS. */\n enableSeconds?: boolean;\n /** Hour step for the spinner. */\n incrementHours?: number;\n /** Minute step. Combine with `min`/`max` for slot-style pickers. */\n incrementMinutes?: number;\n /** Second step. Default `1`. */\n incrementSeconds?: number;\n /** Predicate returning `true` for times that should be skipped. Blocked times are also rejected during manual typing. */\n unselectableTimes?: (d: Date) => boolean;\n /** Decorative left icon glyph for the wrapping `Control` (shown by default). Set `''` to hide. */\n iconLeftName?: string;\n /**\n * Show a clickable launcher button on the right that toggles the popover.\n * Hidden by default while a `Control` it sits in shows its `isLoading`\n * spinner, which shares that right edge.\n * @defaultValue true\n */\n triggerIcon?: boolean;\n /** Glyph for the right launcher button. */\n triggerIconName?: string;\n /**\n * Optional translatable string overrides. In a 12-hour picker, `ampm`\n * names the AM/PM column, `'AM/PM'` by default.\n */\n labels?: PickerLabels;\n /** Play a short audible click on each wheel-item crossing. Substitute for haptic feedback on iOS Safari (which has no web haptic API as of May 2026); on Android, `navigator.vibrate(5)` fires automatically regardless. */\n audioTick?: boolean;\n /** Auto-route platform-appropriate feedback: vibrate on Android (already happening), audio thunk on iOS (where vibrate is unavailable). One switch instead of platform-sniffing on the consumer side. `audioTick={true}` always wins. */\n haptics?: boolean;\n}\n\n/**\n * Raw TimeInput — input + popover spinner without Field/Control wrapping.\n * Use `TimeInput` for the convenience wrapper.\n *\n * @function\n * @param {TimeInputBaseProps} props\n * @returns {JSX.Element}\n */\nexport const TimeInputBase = forwardRef<HTMLInputElement, TimeInputBaseProps>(\n (props, ref) => {\n const {\n value: controlledValue,\n defaultValue,\n onChange,\n onOpen,\n onClose,\n min,\n max,\n disabled,\n readOnly,\n placeholder,\n format,\n parse,\n locale,\n inline = false,\n mobileNative = 'auto',\n editable = true,\n popover = true,\n openOnFocus = true,\n closeOnSelect = false,\n position = 'bottom-left',\n appendToBody = false,\n color,\n size,\n isRounded,\n hourFormat = '24',\n enableSeconds = false,\n incrementHours = 1,\n incrementMinutes = 1,\n incrementSeconds = 1,\n unselectableTimes,\n className,\n name,\n form,\n required,\n id: idProp,\n onFocus,\n onClick,\n onKeyDown,\n onBlur,\n iconLeftName: _iconLeftName,\n triggerIcon: triggerIconProp,\n triggerIconName = 'chevron-down',\n labels,\n audioTick = false,\n haptics = false,\n ...rest\n } = props;\n // Inside a labeled Field the input takes the id the label points at\n // when the caller set none, as InputBase does, and the popover's ids\n // follow it. Inline there is no input to name, so nothing takes the\n // Field's id or builds on it.\n const id = useFieldLabelTarget(idProp, !inline);\n // The launcher gives way to the loading spinner of a Control this sits in.\n const controlLoading = useControlLoading();\n const triggerIcon = triggerIconProp ?? !controlLoading;\n\n // Platform-appropriate feedback routing: when `haptics` is opted in and\n // the runtime has no Vibration API (notably iOS Safari), enable the\n // audio thunk so the user still gets *some* tactile cue. When vibrate\n // is available (Android Chrome etc.), the existing unconditional\n // `navigator.vibrate(5)` carries the haptic — no audio added unless\n // the consumer explicitly set `audioTick`. `audioTick` always wins.\n const hasVibrate =\n typeof navigator !== 'undefined' &&\n typeof navigator.vibrate === 'function';\n const effectiveAudioTick = audioTick || (haptics && !hasVibrate);\n const t = mergeLabels(labels);\n\n const isControlled = controlledValue !== undefined;\n const [internalValue, setInternalValue] = useState<Date | null>(\n defaultValue ?? null\n );\n const value = isControlled ? (controlledValue ?? null) : internalValue;\n\n const defaultFormat: DateFormatOption =\n format ??\n (hourFormat === '12'\n ? enableSeconds\n ? 'hh:mm:ss A'\n : DEFAULT_TIME_FORMAT_12\n : enableSeconds\n ? 'HH:mm:ss'\n : DEFAULT_TIME_FORMAT_24);\n\n // The displayed format is the source of truth for the hour cycle: an\n // explicit 12-hour `format` must drive a 12-hour wheel even when\n // `hourFormat` was left at its default. Fall back to the raw prop only when\n // the cycle can't be read from the format (an Intl-options object, or no\n // hour token).\n const effectiveHourFormat =\n hourCycleFromFormat(defaultFormat) ?? hourFormat;\n\n const [open, setOpenState] = useState(false);\n const [text, setText] = useState<string>(\n value ? formatTime(value, defaultFormat, locale) : ''\n );\n\n const inputRef = useRef<HTMLInputElement>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n // Snapshot of value at the moment the popover opens, used by Cancel.\n const valueAtOpenRef = useRef<Date | null>(null);\n const reactId = useId();\n const popoverId = id ? `${id}-popover` : `picker-${reactId}`;\n\n const { bulmaHelperClasses, rest: cleanRest } = useBulmaClasses(rest);\n\n const { shouldUseNative, isSmallViewport } = useNativeMobilePicker({\n force: mobileNative === 'auto' ? undefined : mobileNative,\n });\n const useNative = !inline && shouldUseNative;\n const wheelItemHeight = isSmallViewport ? 40 : 32;\n\n const inputClass = usePrefixedClassNames('input', {\n [`is-${color}`]: !!color,\n [`is-${size}`]: !!size,\n 'is-rounded': isRounded,\n });\n const containerClass = usePrefixedClassNames('timeinput-container');\n const triggerClass = usePrefixedClassNames('timeinput-trigger');\n const panelClass = usePrefixedClassNames('timeinput-panel');\n const footerClass = usePrefixedClassNames('timeinput-footer');\n const footerButton = usePrefixedClassNames('button', 'is-small');\n const footerOkClass = usePrefixedClassNames(\n 'button',\n 'is-small',\n 'timeinput-footer-ok'\n );\n const mobileFooterClass = usePrefixedClassNames('timeinput-footer-mobile');\n const mobileFooterResetClass = usePrefixedClassNames(\n 'timeinput-footer-reset'\n );\n const mobileFooterDoneClass = usePrefixedClassNames(\n 'timeinput-footer-done'\n );\n\n // What the latest request asked for, so a second request in the same\n // event sees it before React renders. The callbacks fire here rather than\n // in a state updater, which StrictMode calls twice, and after the state\n // is set, so a request a callback makes is the one that lands last.\n const requestedOpenRef = useRef(false);\n const setOpen = useCallback(\n (next: boolean) => {\n if (requestedOpenRef.current === next) return;\n requestedOpenRef.current = next;\n setOpenState(next);\n if (next) {\n valueAtOpenRef.current = value;\n onOpen?.();\n } else {\n onClose?.();\n }\n },\n [onOpen, onClose, value]\n );\n\n useEffect(() => {\n setText(value ? formatTime(value, defaultFormat, locale) : '');\n }, [value, defaultFormat, locale]);\n\n // A value's date before year 1 is out of range, as in the date pickers.\n const lowerBound = useMemo(() => floorMin(min), [min]);\n\n const commitValue = useCallback(\n (next: Date | null) => {\n if (!isControlled) setInternalValue(next);\n onChange?.(next);\n },\n [isControlled, onChange]\n );\n\n // An empty field starts from today, at the whole minute or second the\n // wheels show.\n const handleSpinnerChange = useCallback(\n (parts: { hours: number; minutes: number; seconds?: number }) => {\n const next = value\n ? setTimeOfDay(value, parts)\n : setTimeOfDay(new Date(), {\n ...parts,\n seconds: parts.seconds ?? 0,\n milliseconds: 0,\n });\n if (!isWithin(next, lowerBound, max)) return;\n commitValue(next);\n },\n [value, lowerBound, max, commitValue]\n );\n\n const tryParse = useCallback(\n (s: string): Date | null => {\n const trimmed = s.trim();\n if (!trimmed) return null;\n const fmt = typeof format === 'string' ? format : undefined;\n return parse\n ? parse(trimmed)\n : parseTime(\n trimmed,\n fmt ??\n (typeof defaultFormat === 'string'\n ? defaultFormat\n : DEFAULT_TIME_FORMAT_24)\n );\n },\n [parse, format, defaultFormat]\n );\n\n // The Date the user edits when starting without a current value. Noon so\n // the 12h / 24h hour calculations are unambiguous.\n const makeBaseDate = useCallback((): Date => {\n const d = new Date();\n d.setHours(12, 0, 0, 0);\n return d;\n }, []);\n\n const inputReadOnlyAttr = !!readOnly || !editable;\n const canOpen = !!popover && !disabled && !readOnly;\n\n const { inputHandlers } = useSegmentedEntry({\n format: defaultFormat,\n value,\n commitValue,\n formatFn: formatTime,\n tryParse,\n text,\n setText,\n makeBaseDate,\n locale,\n min: lowerBound,\n max,\n isBlocked: unselectableTimes,\n disabled,\n readOnly,\n editable,\n popover,\n openOnFocus,\n closeOnSelect,\n isOpen: open,\n setOpen,\n inputRef,\n containerRef,\n onFocus,\n onClick,\n onKeyDown,\n onBlur,\n });\n\n const combinedRef = useCallback(\n (node: HTMLInputElement | null) => {\n (inputRef as React.MutableRefObject<HTMLInputElement | null>).current =\n node;\n if (typeof ref === 'function') ref(node);\n else if (ref)\n (ref as React.MutableRefObject<HTMLInputElement | null>).current =\n node;\n },\n [ref]\n );\n\n if (useNative) {\n const nativeStep = enableSeconds\n ? incrementSeconds\n : incrementMinutes * 60;\n return (\n <input\n {...cleanRest}\n ref={combinedRef}\n type=\"time\"\n step={nativeStep}\n className={classNames(inputClass, bulmaHelperClasses, className)}\n value={value ? toIsoTime(value, enableSeconds) : ''}\n onChange={e => {\n const parsed = e.target.value ? fromIsoTime(e.target.value) : null;\n commitValue(parsed);\n }}\n min={min ? toIsoTime(min, enableSeconds) : undefined}\n max={max ? toIsoTime(max, enableSeconds) : undefined}\n disabled={disabled}\n readOnly={readOnly}\n placeholder={placeholder}\n name={name}\n form={form}\n required={required}\n id={id}\n />\n );\n }\n\n const spinnerValue = {\n hours: value?.getHours() ?? 0,\n minutes: value?.getMinutes() ?? 0,\n seconds: enableSeconds ? (value?.getSeconds() ?? 0) : undefined,\n };\n\n const panel = (\n <div className={panelClass}>\n <TimeWheels\n value={spinnerValue}\n onChange={handleSpinnerChange}\n hourFormat={effectiveHourFormat}\n enableSeconds={enableSeconds}\n incrementHours={incrementHours}\n incrementMinutes={incrementMinutes}\n incrementSeconds={incrementSeconds}\n unselectableTimes={unselectableTimes}\n color={color}\n size={size}\n disabled={disabled}\n // The popover panel takes `popoverId`, so the wheels inside it take\n // one of their own. Inline there is no panel, and the wheels keep\n // the id they released with.\n id={inline ? popoverId : `${popoverId}-time`}\n labels={labels}\n itemHeight={wheelItemHeight}\n audioTick={effectiveAudioTick}\n onCommit={() => setOpen(false)}\n />\n {/* Footer is popover-only. Desktop gets the OK / Now / Cancel triad;\n mobile drops to an iOS-style \"Reset / ✓\" pair to match the OS\n picker's footer affordances and free up vertical space on small\n screens. */}\n {!inline && !isSmallViewport && (\n <Buttons className={footerClass} hasAddons>\n <button\n type=\"button\"\n className={footerOkClass}\n onClick={() => setOpen(false)}\n >\n {t.ok}\n </button>\n <button\n type=\"button\"\n className={footerButton}\n onClick={() => {\n commitValue(\n snapTimeToIncrement(new Date(), {\n incrementHours,\n incrementMinutes,\n incrementSeconds,\n enableSeconds,\n })\n );\n setOpen(false);\n }}\n >\n {t.now}\n </button>\n <button\n type=\"button\"\n className={footerButton}\n onClick={() => {\n commitValue(valueAtOpenRef.current);\n setOpen(false);\n }}\n >\n {t.cancel}\n </button>\n </Buttons>\n )}\n {!inline && isSmallViewport && (\n <div className={mobileFooterClass}>\n <button\n type=\"button\"\n className={mobileFooterResetClass}\n onClick={() => {\n commitValue(valueAtOpenRef.current);\n setOpen(false);\n }}\n >\n {t.reset}\n </button>\n <button\n type=\"button\"\n className={mobileFooterDoneClass}\n aria-label={t.done}\n onClick={() => setOpen(false)}\n >\n <svg\n width=\"22\"\n height=\"22\"\n viewBox=\"0 0 22 22\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M5 11l4 4 8-8\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </button>\n </div>\n )}\n </div>\n );\n\n if (inline) {\n return (\n <div\n {...cleanRest}\n ref={containerRef}\n className={classNames(containerClass, bulmaHelperClasses, className)}\n >\n {panel}\n {name && (\n <input\n type=\"hidden\"\n name={name}\n form={form}\n value={value ? toIsoTime(value, enableSeconds) : ''}\n required={required}\n />\n )}\n </div>\n );\n }\n\n return (\n <div\n ref={containerRef}\n className={classNames(containerClass, bulmaHelperClasses, className)}\n {...cleanRest}\n >\n <input\n ref={combinedRef}\n type=\"text\"\n role=\"combobox\"\n aria-haspopup=\"dialog\"\n aria-expanded={open}\n aria-controls={popoverId}\n autoComplete=\"off\"\n className={inputClass}\n value={text}\n placeholder={placeholder}\n disabled={disabled}\n readOnly={inputReadOnlyAttr}\n name={name}\n form={form}\n required={required}\n id={id}\n {...inputHandlers}\n />\n {popover && triggerIcon && (\n <button\n type=\"button\"\n className={triggerClass}\n onClick={() => {\n if (canOpen) setOpen(!open);\n }}\n disabled={!canOpen}\n aria-label={t.chooseTime}\n aria-haspopup=\"dialog\"\n aria-controls={popoverId}\n aria-expanded={open}\n tabIndex={canOpen ? 0 : -1}\n >\n <Icon name={triggerIconName} size={size} />\n </button>\n )}\n {popover && (\n <PickerPopover\n isOpen={open}\n onClose={() => setOpen(false)}\n anchorRef={containerRef}\n position={position}\n appendToBody={appendToBody}\n ariaLabel={t.chooseTime}\n id={popoverId}\n restoreFocusRef={inputRef}\n >\n {panel}\n </PickerPopover>\n )}\n </div>\n );\n }\n);\n\nTimeInputBase.displayName = 'TimeInputBase';\n\nexport default TimeInputBase;\n"],"names":["_jsx","_jsxs"],"mappings":"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"}
|
|
@@ -21,7 +21,7 @@ const DEFAULT_PICKER_LABELS = {
|
|
|
21
21
|
hours: 'hours',
|
|
22
22
|
minutes: 'minutes',
|
|
23
23
|
seconds: 'seconds',
|
|
24
|
-
ampm: '
|
|
24
|
+
ampm: 'AM/PM',
|
|
25
25
|
increaseHours: 'Increase hours',
|
|
26
26
|
decreaseHours: 'Decrease hours',
|
|
27
27
|
increaseMinutes: 'Increase minutes',
|
|
@@ -40,11 +40,19 @@ const DEFAULT_PICKER_LABELS = {
|
|
|
40
40
|
done: 'Done',
|
|
41
41
|
time: 'Time',
|
|
42
42
|
};
|
|
43
|
-
/**
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
43
|
+
/**
|
|
44
|
+
* Merge user-supplied label overrides with the defaults. A key set to
|
|
45
|
+
* `undefined` keeps its default, so a locale table that is missing a string
|
|
46
|
+
* still names its control instead of leaving it with no accessible name.
|
|
47
|
+
*/
|
|
48
|
+
const mergeLabels = (overrides) => {
|
|
49
|
+
const merged = { ...DEFAULT_PICKER_LABELS };
|
|
50
|
+
for (const [key, value] of Object.entries(overrides ?? {})) {
|
|
51
|
+
if (value !== undefined)
|
|
52
|
+
merged[key] = value;
|
|
53
|
+
}
|
|
54
|
+
return merged;
|
|
55
|
+
};
|
|
48
56
|
|
|
49
57
|
export { DEFAULT_PICKER_LABELS, isPopoverToggleKey, mergeLabels };
|
|
50
58
|
//# sourceMappingURL=pickerTypes.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pickerTypes.js","sources":["../../../../src/form/_pickerInternals/pickerTypes.ts"],"sourcesContent":["export type PickerPosition =\n 'bottom-left' | 'bottom-right' | 'top-left' | 'top-right' | 'auto';\n\nexport type HourFormat = '12' | '24';\n\nexport type DayOfWeek = 0 | 1 | 2 | 3 | 4 | 5 | 6;\n\n/**\n * Whether a key is Alt+ArrowDown or Alt+ArrowUp, which open and close a\n * picker's popover. The calendar and the time wheels leave both to it rather\n * than reading them as plain arrows.\n */\nexport const isPopoverToggleKey = (e: {\n altKey: boolean;\n key: string;\n}): boolean => e.altKey && (e.key === 'ArrowDown' || e.key === 'ArrowUp');\n\n/** The unit a date picker selects: a day, a month or a year. */\nexport type DateGranularity = 'day' | 'month' | 'year';\n\n/**\n * A range of days, start first. Either end may be `null` while the range is\n * half filled, and `[null, null]` is empty.\n */\nexport type DateRangeValue = [Date | null, Date | null];\n\n/**\n * Translatable strings used across all four pickers. Pass via the `labels`\n * prop to override defaults; consumers manage their own locale-driven mapping.\n */\nexport interface PickerLabels {\n // Calendar\n prevMonth?: string;\n nextMonth?: string;\n /** Month grid header: steps back a year. */\n prevYear?: string;\n /** Month grid header: steps forward a year. */\n nextYear?: string;\n chooseDate?: string;\n /** Launcher and popover name when the picker selects a month. */\n chooseMonth?: string;\n /** Launcher, popover and year list name when the picker selects a year. */\n chooseYear?: string;\n /** Range picking: names the start of a range, in the calendar and the field. */\n rangeStart?: string;\n /** Range picking: names the end of a range, in the calendar and the field. */\n rangeEnd?: string;\n /**\n * Range picking: describes the day a pending range would end on, the one\n * the pointer or the keyboard is on, until it is picked.\n */\n rangePreviewEnd?: string;\n /** Range picking: shown between the start and end inputs. */\n rangeSeparator?: string;\n /** Range picking: names the launcher and the popover. */\n chooseDateRange?: string;\n // Time spinner\n hours?: string;\n minutes?: string;\n seconds?: string;\n ampm?: string;\n increaseHours?: string;\n decreaseHours?: string;\n increaseMinutes?: string;\n decreaseMinutes?: string;\n increaseSeconds?: string;\n decreaseSeconds?: string;\n toggleAmPm?: string;\n chooseTime?: string;\n chooseDateTime?: string;\n // Footer buttons\n now?: string;\n today?: string;\n clear?: string;\n cancel?: string;\n ok?: string;\n /** Mobile footer: text link that reverts to the value at open (like iOS). */\n reset?: string;\n /** Mobile footer: aria-label for the circular checkmark commit button. */\n done?: string;\n /** DateTimeInput footer: label preceding the selected-time display. */\n time?: string;\n}\n\nexport const DEFAULT_PICKER_LABELS: Required<PickerLabels> = {\n prevMonth: 'Previous month',\n nextMonth: 'Next month',\n prevYear: 'Previous year',\n nextYear: 'Next year',\n chooseDate: 'Choose date',\n chooseMonth: 'Choose month',\n chooseYear: 'Choose year',\n rangeStart: 'Start date',\n rangeEnd: 'End date',\n rangePreviewEnd: 'Choose as end date',\n rangeSeparator: '–',\n chooseDateRange: 'Choose date range',\n hours: 'hours',\n minutes: 'minutes',\n seconds: 'seconds',\n ampm: '
|
|
1
|
+
{"version":3,"file":"pickerTypes.js","sources":["../../../../src/form/_pickerInternals/pickerTypes.ts"],"sourcesContent":["export type PickerPosition =\n 'bottom-left' | 'bottom-right' | 'top-left' | 'top-right' | 'auto';\n\nexport type HourFormat = '12' | '24';\n\nexport type DayOfWeek = 0 | 1 | 2 | 3 | 4 | 5 | 6;\n\n/**\n * Whether a key is Alt+ArrowDown or Alt+ArrowUp, which open and close a\n * picker's popover. The calendar and the time wheels leave both to it rather\n * than reading them as plain arrows.\n */\nexport const isPopoverToggleKey = (e: {\n altKey: boolean;\n key: string;\n}): boolean => e.altKey && (e.key === 'ArrowDown' || e.key === 'ArrowUp');\n\n/** The unit a date picker selects: a day, a month or a year. */\nexport type DateGranularity = 'day' | 'month' | 'year';\n\n/**\n * A range of days, start first. Either end may be `null` while the range is\n * half filled, and `[null, null]` is empty.\n */\nexport type DateRangeValue = [Date | null, Date | null];\n\n/**\n * Translatable strings used across all four pickers. Pass via the `labels`\n * prop to override defaults; consumers manage their own locale-driven mapping.\n */\nexport interface PickerLabels {\n // Calendar\n prevMonth?: string;\n nextMonth?: string;\n /** Month grid header: steps back a year. */\n prevYear?: string;\n /** Month grid header: steps forward a year. */\n nextYear?: string;\n chooseDate?: string;\n /** Launcher and popover name when the picker selects a month. */\n chooseMonth?: string;\n /** Launcher, popover and year list name when the picker selects a year. */\n chooseYear?: string;\n /** Range picking: names the start of a range, in the calendar and the field. */\n rangeStart?: string;\n /** Range picking: names the end of a range, in the calendar and the field. */\n rangeEnd?: string;\n /**\n * Range picking: describes the day a pending range would end on, the one\n * the pointer or the keyboard is on, until it is picked.\n */\n rangePreviewEnd?: string;\n /** Range picking: shown between the start and end inputs. */\n rangeSeparator?: string;\n /** Range picking: names the launcher and the popover. */\n chooseDateRange?: string;\n // Time spinner\n hours?: string;\n minutes?: string;\n seconds?: string;\n /**\n * Time wheels: names the AM/PM column a 12-hour picker shows.\n * @defaultValue 'AM/PM'\n */\n ampm?: string;\n /** @deprecated Rendered nowhere: no picker reads it. It goes in the next major. */\n increaseHours?: string;\n /** @deprecated Rendered nowhere: no picker reads it. It goes in the next major. */\n decreaseHours?: string;\n /** @deprecated Rendered nowhere: no picker reads it. It goes in the next major. */\n increaseMinutes?: string;\n /** @deprecated Rendered nowhere: no picker reads it. It goes in the next major. */\n decreaseMinutes?: string;\n /** @deprecated Rendered nowhere: no picker reads it. It goes in the next major. */\n increaseSeconds?: string;\n /** @deprecated Rendered nowhere: no picker reads it. It goes in the next major. */\n decreaseSeconds?: string;\n /** @deprecated Rendered nowhere: no picker reads it. It goes in the next major. */\n toggleAmPm?: string;\n chooseTime?: string;\n chooseDateTime?: string;\n // Footer buttons\n now?: string;\n today?: string;\n clear?: string;\n cancel?: string;\n ok?: string;\n /** Mobile footer: text link that reverts to the value at open (like iOS). */\n reset?: string;\n /** Mobile footer: aria-label for the circular checkmark commit button. */\n done?: string;\n /** DateTimeInput footer: label preceding the selected-time display. */\n time?: string;\n}\n\nexport const DEFAULT_PICKER_LABELS: Required<PickerLabels> = {\n prevMonth: 'Previous month',\n nextMonth: 'Next month',\n prevYear: 'Previous year',\n nextYear: 'Next year',\n chooseDate: 'Choose date',\n chooseMonth: 'Choose month',\n chooseYear: 'Choose year',\n rangeStart: 'Start date',\n rangeEnd: 'End date',\n rangePreviewEnd: 'Choose as end date',\n rangeSeparator: '–',\n chooseDateRange: 'Choose date range',\n hours: 'hours',\n minutes: 'minutes',\n seconds: 'seconds',\n ampm: 'AM/PM',\n increaseHours: 'Increase hours',\n decreaseHours: 'Decrease hours',\n increaseMinutes: 'Increase minutes',\n decreaseMinutes: 'Decrease minutes',\n increaseSeconds: 'Increase seconds',\n decreaseSeconds: 'Decrease seconds',\n toggleAmPm: 'Toggle AM/PM',\n chooseTime: 'Choose time',\n chooseDateTime: 'Choose date and time',\n now: 'Now',\n today: 'Today',\n clear: 'Clear',\n cancel: 'Cancel',\n ok: 'OK',\n reset: 'Reset',\n done: 'Done',\n time: 'Time',\n};\n\n/**\n * Merge user-supplied label overrides with the defaults. A key set to\n * `undefined` keeps its default, so a locale table that is missing a string\n * still names its control instead of leaving it with no accessible name.\n */\nexport const mergeLabels = (\n overrides?: PickerLabels\n): Required<PickerLabels> => {\n const merged = { ...DEFAULT_PICKER_LABELS };\n for (const [key, value] of Object.entries(overrides ?? {})) {\n if (value !== undefined) merged[key as keyof PickerLabels] = value;\n }\n return merged;\n};\n"],"names":[],"mappings":";AAOA;;;;AAIG;AACI,MAAM,kBAAkB,GAAG,CAAC,CAGlC,KAAc,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,GAAG,KAAK,WAAW,IAAI,CAAC,CAAC,GAAG,KAAK,SAAS;AAgFjE,MAAM,qBAAqB,GAA2B;AAC3D,IAAA,SAAS,EAAE,gBAAgB;AAC3B,IAAA,SAAS,EAAE,YAAY;AACvB,IAAA,QAAQ,EAAE,eAAe;AACzB,IAAA,QAAQ,EAAE,WAAW;AACrB,IAAA,UAAU,EAAE,aAAa;AACzB,IAAA,WAAW,EAAE,cAAc;AAC3B,IAAA,UAAU,EAAE,aAAa;AACzB,IAAA,UAAU,EAAE,YAAY;AACxB,IAAA,QAAQ,EAAE,UAAU;AACpB,IAAA,eAAe,EAAE,oBAAoB;AACrC,IAAA,cAAc,EAAE,GAAG;AACnB,IAAA,eAAe,EAAE,mBAAmB;AACpC,IAAA,KAAK,EAAE,OAAO;AACd,IAAA,OAAO,EAAE,SAAS;AAClB,IAAA,OAAO,EAAE,SAAS;AAClB,IAAA,IAAI,EAAE,OAAO;AACb,IAAA,aAAa,EAAE,gBAAgB;AAC/B,IAAA,aAAa,EAAE,gBAAgB;AAC/B,IAAA,eAAe,EAAE,kBAAkB;AACnC,IAAA,eAAe,EAAE,kBAAkB;AACnC,IAAA,eAAe,EAAE,kBAAkB;AACnC,IAAA,eAAe,EAAE,kBAAkB;AACnC,IAAA,UAAU,EAAE,cAAc;AAC1B,IAAA,UAAU,EAAE,aAAa;AACzB,IAAA,cAAc,EAAE,sBAAsB;AACtC,IAAA,GAAG,EAAE,KAAK;AACV,IAAA,KAAK,EAAE,OAAO;AACd,IAAA,KAAK,EAAE,OAAO;AACd,IAAA,MAAM,EAAE,QAAQ;AAChB,IAAA,EAAE,EAAE,IAAI;AACR,IAAA,KAAK,EAAE,OAAO;AACd,IAAA,IAAI,EAAE,MAAM;AACZ,IAAA,IAAI,EAAE,MAAM;;AAGd;;;;AAIG;AACI,MAAM,WAAW,GAAG,CACzB,SAAwB,KACE;AAC1B,IAAA,MAAM,MAAM,GAAG,EAAE,GAAG,qBAAqB,EAAE;AAC3C,IAAA,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,IAAI,EAAE,CAAC,EAAE;QAC1D,IAAI,KAAK,KAAK,SAAS;AAAE,YAAA,MAAM,CAAC,GAAyB,CAAC,GAAG,KAAK;IACpE;AACA,IAAA,OAAO,MAAM;AACf;;"}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/* @allxsmith/bestax-bulma — AI agents: see AGENTS.md in the package root, or https://bestax.io/llms.txt */
|
|
2
|
+
import { useRef, useEffect } from 'react';
|
|
3
|
+
|
|
4
|
+
// INTERNAL: deliberately not exported from src/index.ts.
|
|
5
|
+
/** How long a touch or a pen has to stay down to open the menu, in milliseconds. */
|
|
6
|
+
const LONG_PRESS_DELAY = 500;
|
|
7
|
+
/** How far a long press can move before it counts as something else, in CSS pixels. */
|
|
8
|
+
const LONG_PRESS_TOLERANCE = 10;
|
|
9
|
+
/**
|
|
10
|
+
* The last press no trigger is to start a long press for: one a trigger
|
|
11
|
+
* took, or another pointer coming down while a press is held. A press
|
|
12
|
+
* reaches every trigger it bubbles through, the innermost first and all
|
|
13
|
+
* before the next press, so this is how a trigger around another one knows
|
|
14
|
+
* the inner one took it, and how a trigger a second finger lands on knows to
|
|
15
|
+
* leave it.
|
|
16
|
+
*/
|
|
17
|
+
let claimed = null;
|
|
18
|
+
/** Whether the event happened in the element its handler is on, rather than in content portaled out of it. */
|
|
19
|
+
const inTrigger = (event) => event.currentTarget.contains(event.target);
|
|
20
|
+
/**
|
|
21
|
+
* Where a `contextmenu` asks to open: at its point when that is on the
|
|
22
|
+
* element it was fired at, which it is for the pointer, and at that
|
|
23
|
+
* element's bottom-left corner otherwise, which is where the browser puts
|
|
24
|
+
* its own menu for a key.
|
|
25
|
+
*/
|
|
26
|
+
function contextMenuPoint(event) {
|
|
27
|
+
const { clientX: x, clientY: y } = event;
|
|
28
|
+
const rect = event.target.getBoundingClientRect();
|
|
29
|
+
return x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom
|
|
30
|
+
? { x, y }
|
|
31
|
+
: { x: rect.left, y: rect.bottom };
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Decides when a context menu asks to open, and where. It renders nothing,
|
|
35
|
+
* and knows nothing about ARIA or the menu.
|
|
36
|
+
*
|
|
37
|
+
* - **`contextmenu`:** a right-click, and Shift+F10 or the context menu key
|
|
38
|
+
* where the browser fires the event for them, asks to open at the point
|
|
39
|
+
* `contextMenuPoint` works out, and prevents the browser's own menu. Each
|
|
40
|
+
* one asks again, so a right-click elsewhere on the trigger moves it.
|
|
41
|
+
* - **Long press:** a touch or a pen held still on the trigger for
|
|
42
|
+
* `LONG_PRESS_DELAY` fires a `contextmenu` at what it landed on, so it
|
|
43
|
+
* takes the same path, handlers on the way included. Moving more than
|
|
44
|
+
* `LONG_PRESS_TOLERANCE`, lifting, another pointer coming down anywhere in
|
|
45
|
+
* the document, or `pointercancel`, which is the browser taking the
|
|
46
|
+
* gesture for a scroll or a zoom, cancels it, and a mouse never starts
|
|
47
|
+
* one. A pointer that comes down while another is held starts no long
|
|
48
|
+
* press of its own, on this trigger or another, so two fingers on two rows
|
|
49
|
+
* open nothing. While the finger is down, and only then, a listener on the
|
|
50
|
+
* document hears other pointers coming down. Where the
|
|
51
|
+
* browser fires `contextmenu` for a long press itself, the first of the two
|
|
52
|
+
* opens it, and the end of a touch that opened it is prevented, so lifting
|
|
53
|
+
* doesn't click what is under the finger.
|
|
54
|
+
* - **Who opens:** a handler that calls `preventDefault()` on the
|
|
55
|
+
* `contextmenu` before this one keeps it from asking, which is how a
|
|
56
|
+
* trigger's own handler opts out and how the innermost of nested triggers
|
|
57
|
+
* wins. A long press goes to the innermost trigger that takes it. Events
|
|
58
|
+
* from content portaled out of the trigger, which React brings here
|
|
59
|
+
* anyway, are ignored.
|
|
60
|
+
* - **`disabled`:** nothing asks, and the browser's menu shows.
|
|
61
|
+
*
|
|
62
|
+
* Nothing it scheduled runs after the component unmounts.
|
|
63
|
+
*
|
|
64
|
+
* @function useContextMenuTrigger
|
|
65
|
+
* @param options - How to ask to open, and whether to.
|
|
66
|
+
* @returns Handlers for the trigger element.
|
|
67
|
+
*/
|
|
68
|
+
function useContextMenuTrigger({ onOpen, disabled, }) {
|
|
69
|
+
const pressRef = useRef(null);
|
|
70
|
+
/** A long press opened the menu, and the touch that made it hasn't ended. */
|
|
71
|
+
const heldRef = useRef(false);
|
|
72
|
+
const cancel = () => {
|
|
73
|
+
clearTimeout(pressRef.current?.timer);
|
|
74
|
+
pressRef.current?.stop();
|
|
75
|
+
pressRef.current = null;
|
|
76
|
+
};
|
|
77
|
+
useEffect(() => () => {
|
|
78
|
+
clearTimeout(pressRef.current?.timer);
|
|
79
|
+
pressRef.current?.stop();
|
|
80
|
+
pressRef.current = null;
|
|
81
|
+
}, []);
|
|
82
|
+
const isPress = (event) => pressRef.current?.pointerId === event.pointerId;
|
|
83
|
+
return {
|
|
84
|
+
onContextMenu: event => {
|
|
85
|
+
if (disabled || event.defaultPrevented || !inTrigger(event))
|
|
86
|
+
return;
|
|
87
|
+
event.preventDefault();
|
|
88
|
+
const press = pressRef.current;
|
|
89
|
+
if (press) {
|
|
90
|
+
if (press.opened)
|
|
91
|
+
return;
|
|
92
|
+
clearTimeout(press.timer);
|
|
93
|
+
press.opened = true;
|
|
94
|
+
heldRef.current = true;
|
|
95
|
+
}
|
|
96
|
+
onOpen(contextMenuPoint(event), event.currentTarget);
|
|
97
|
+
},
|
|
98
|
+
onPointerDown: event => {
|
|
99
|
+
const native = event.nativeEvent;
|
|
100
|
+
if (disabled ||
|
|
101
|
+
(event.pointerType !== 'touch' && event.pointerType !== 'pen') ||
|
|
102
|
+
claimed === native ||
|
|
103
|
+
!inTrigger(event)) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
claimed = native;
|
|
107
|
+
heldRef.current = false;
|
|
108
|
+
const target = event.target;
|
|
109
|
+
const doc = target.ownerDocument;
|
|
110
|
+
// Another pointer coming down anywhere makes it a pinch or a second
|
|
111
|
+
// touch, not a long press. In the capture phase, so it's heard before
|
|
112
|
+
// any trigger it lands on, which then leaves it. The press's own
|
|
113
|
+
// pointerdown has passed the document already, so it isn't heard.
|
|
114
|
+
const another = (other) => {
|
|
115
|
+
claimed = other;
|
|
116
|
+
cancel();
|
|
117
|
+
};
|
|
118
|
+
doc.addEventListener('pointerdown', another, true);
|
|
119
|
+
const press = {
|
|
120
|
+
pointerId: event.pointerId,
|
|
121
|
+
target,
|
|
122
|
+
x: event.clientX,
|
|
123
|
+
y: event.clientY,
|
|
124
|
+
opened: false,
|
|
125
|
+
stop: () => doc.removeEventListener('pointerdown', another, true),
|
|
126
|
+
};
|
|
127
|
+
press.timer = setTimeout(() => {
|
|
128
|
+
const view = press.target.ownerDocument.defaultView;
|
|
129
|
+
press.target.dispatchEvent(new view.MouseEvent('contextmenu', {
|
|
130
|
+
bubbles: true,
|
|
131
|
+
cancelable: true,
|
|
132
|
+
composed: true,
|
|
133
|
+
clientX: press.x,
|
|
134
|
+
clientY: press.y,
|
|
135
|
+
button: 2,
|
|
136
|
+
}));
|
|
137
|
+
}, LONG_PRESS_DELAY);
|
|
138
|
+
pressRef.current = press;
|
|
139
|
+
},
|
|
140
|
+
onPointerMove: event => {
|
|
141
|
+
const press = pressRef.current;
|
|
142
|
+
if (!press || !isPress(event) || press.opened)
|
|
143
|
+
return;
|
|
144
|
+
const moved = Math.hypot(event.clientX - press.x, event.clientY - press.y);
|
|
145
|
+
if (moved > LONG_PRESS_TOLERANCE)
|
|
146
|
+
cancel();
|
|
147
|
+
},
|
|
148
|
+
onPointerUp: event => {
|
|
149
|
+
if (isPress(event))
|
|
150
|
+
cancel();
|
|
151
|
+
},
|
|
152
|
+
onPointerCancel: event => {
|
|
153
|
+
if (isPress(event))
|
|
154
|
+
cancel();
|
|
155
|
+
},
|
|
156
|
+
onTouchEnd: event => {
|
|
157
|
+
if (!heldRef.current)
|
|
158
|
+
return;
|
|
159
|
+
heldRef.current = false;
|
|
160
|
+
event.preventDefault();
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export { LONG_PRESS_DELAY, LONG_PRESS_TOLERANCE, useContextMenuTrigger };
|
|
166
|
+
//# sourceMappingURL=useContextMenuTrigger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useContextMenuTrigger.js","sources":["../../../src/helpers/useContextMenuTrigger.ts"],"sourcesContent":["// INTERNAL: deliberately not exported from src/index.ts.\nimport { useEffect, useRef } from 'react';\nimport type { MouseEvent, PointerEvent, TouchEvent } from 'react';\n\n/** A point in the viewport, in CSS pixels. */\nexport interface ContextMenuPoint {\n x: number;\n y: number;\n}\n\n/**\n * Options for `useContextMenuTrigger`.\n */\nexport interface UseContextMenuTriggerOptions {\n /**\n * Asks to open at `point`. `trigger` is the element the handlers are on,\n * for its document.\n */\n onOpen: (point: ContextMenuPoint, trigger: HTMLElement) => void;\n /** Leave the browser's own menu alone, and start no long press. */\n disabled: boolean;\n}\n\n/** Handlers for the trigger element. */\nexport interface ContextMenuTriggerHandlers {\n onContextMenu: (event: MouseEvent<HTMLElement>) => void;\n onPointerDown: (event: PointerEvent<HTMLElement>) => void;\n onPointerMove: (event: PointerEvent<HTMLElement>) => void;\n onPointerUp: (event: PointerEvent<HTMLElement>) => void;\n onPointerCancel: (event: PointerEvent<HTMLElement>) => void;\n onTouchEnd: (event: TouchEvent<HTMLElement>) => void;\n}\n\n/** How long a touch or a pen has to stay down to open the menu, in milliseconds. */\nexport const LONG_PRESS_DELAY = 500;\n\n/** How far a long press can move before it counts as something else, in CSS pixels. */\nexport const LONG_PRESS_TOLERANCE = 10;\n\n/** A touch or a pen held on the trigger. */\ninterface Press {\n pointerId: number;\n /** What it landed on, which its `contextmenu` goes to. */\n target: Element;\n x: number;\n y: number;\n timer?: ReturnType<typeof setTimeout>;\n /** A `contextmenu` arrived while it was down, so the menu opened. */\n opened: boolean;\n /** Stops listening for another pointer coming down in its document. */\n stop: () => void;\n}\n\n/**\n * The last press no trigger is to start a long press for: one a trigger\n * took, or another pointer coming down while a press is held. A press\n * reaches every trigger it bubbles through, the innermost first and all\n * before the next press, so this is how a trigger around another one knows\n * the inner one took it, and how a trigger a second finger lands on knows to\n * leave it.\n */\nlet claimed: Event | null = null;\n\n/** Whether the event happened in the element its handler is on, rather than in content portaled out of it. */\nconst inTrigger = (event: { currentTarget: Element; target: EventTarget }) =>\n event.currentTarget.contains(event.target as Node);\n\n/**\n * Where a `contextmenu` asks to open: at its point when that is on the\n * element it was fired at, which it is for the pointer, and at that\n * element's bottom-left corner otherwise, which is where the browser puts\n * its own menu for a key.\n */\nfunction contextMenuPoint(event: MouseEvent<HTMLElement>): ContextMenuPoint {\n const { clientX: x, clientY: y } = event;\n const rect = (event.target as Element).getBoundingClientRect();\n return x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom\n ? { x, y }\n : { x: rect.left, y: rect.bottom };\n}\n\n/**\n * Decides when a context menu asks to open, and where. It renders nothing,\n * and knows nothing about ARIA or the menu.\n *\n * - **`contextmenu`:** a right-click, and Shift+F10 or the context menu key\n * where the browser fires the event for them, asks to open at the point\n * `contextMenuPoint` works out, and prevents the browser's own menu. Each\n * one asks again, so a right-click elsewhere on the trigger moves it.\n * - **Long press:** a touch or a pen held still on the trigger for\n * `LONG_PRESS_DELAY` fires a `contextmenu` at what it landed on, so it\n * takes the same path, handlers on the way included. Moving more than\n * `LONG_PRESS_TOLERANCE`, lifting, another pointer coming down anywhere in\n * the document, or `pointercancel`, which is the browser taking the\n * gesture for a scroll or a zoom, cancels it, and a mouse never starts\n * one. A pointer that comes down while another is held starts no long\n * press of its own, on this trigger or another, so two fingers on two rows\n * open nothing. While the finger is down, and only then, a listener on the\n * document hears other pointers coming down. Where the\n * browser fires `contextmenu` for a long press itself, the first of the two\n * opens it, and the end of a touch that opened it is prevented, so lifting\n * doesn't click what is under the finger.\n * - **Who opens:** a handler that calls `preventDefault()` on the\n * `contextmenu` before this one keeps it from asking, which is how a\n * trigger's own handler opts out and how the innermost of nested triggers\n * wins. A long press goes to the innermost trigger that takes it. Events\n * from content portaled out of the trigger, which React brings here\n * anyway, are ignored.\n * - **`disabled`:** nothing asks, and the browser's menu shows.\n *\n * Nothing it scheduled runs after the component unmounts.\n *\n * @function useContextMenuTrigger\n * @param options - How to ask to open, and whether to.\n * @returns Handlers for the trigger element.\n */\nexport function useContextMenuTrigger({\n onOpen,\n disabled,\n}: UseContextMenuTriggerOptions): ContextMenuTriggerHandlers {\n const pressRef = useRef<Press | null>(null);\n /** A long press opened the menu, and the touch that made it hasn't ended. */\n const heldRef = useRef(false);\n\n const cancel = () => {\n clearTimeout(pressRef.current?.timer);\n pressRef.current?.stop();\n pressRef.current = null;\n };\n\n useEffect(\n () => () => {\n clearTimeout(pressRef.current?.timer);\n pressRef.current?.stop();\n pressRef.current = null;\n },\n []\n );\n const isPress = (event: PointerEvent<HTMLElement>) =>\n pressRef.current?.pointerId === event.pointerId;\n\n return {\n onContextMenu: event => {\n if (disabled || event.defaultPrevented || !inTrigger(event)) return;\n event.preventDefault();\n const press = pressRef.current;\n if (press) {\n if (press.opened) return;\n clearTimeout(press.timer);\n press.opened = true;\n heldRef.current = true;\n }\n onOpen(contextMenuPoint(event), event.currentTarget);\n },\n onPointerDown: event => {\n const native = event.nativeEvent;\n if (\n disabled ||\n (event.pointerType !== 'touch' && event.pointerType !== 'pen') ||\n claimed === native ||\n !inTrigger(event)\n ) {\n return;\n }\n claimed = native;\n heldRef.current = false;\n const target = event.target as Element;\n const doc = target.ownerDocument;\n // Another pointer coming down anywhere makes it a pinch or a second\n // touch, not a long press. In the capture phase, so it's heard before\n // any trigger it lands on, which then leaves it. The press's own\n // pointerdown has passed the document already, so it isn't heard.\n const another = (other: Event) => {\n claimed = other;\n cancel();\n };\n doc.addEventListener('pointerdown', another, true);\n const press: Press = {\n pointerId: event.pointerId,\n target,\n x: event.clientX,\n y: event.clientY,\n opened: false,\n stop: () => doc.removeEventListener('pointerdown', another, true),\n };\n press.timer = setTimeout(() => {\n const view = press.target.ownerDocument.defaultView as typeof window;\n press.target.dispatchEvent(\n new view.MouseEvent('contextmenu', {\n bubbles: true,\n cancelable: true,\n composed: true,\n clientX: press.x,\n clientY: press.y,\n button: 2,\n })\n );\n }, LONG_PRESS_DELAY);\n pressRef.current = press;\n },\n onPointerMove: event => {\n const press = pressRef.current;\n if (!press || !isPress(event) || press.opened) return;\n const moved = Math.hypot(\n event.clientX - press.x,\n event.clientY - press.y\n );\n if (moved > LONG_PRESS_TOLERANCE) cancel();\n },\n onPointerUp: event => {\n if (isPress(event)) cancel();\n },\n onPointerCancel: event => {\n if (isPress(event)) cancel();\n },\n onTouchEnd: event => {\n if (!heldRef.current) return;\n heldRef.current = false;\n event.preventDefault();\n },\n };\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -4,6 +4,16 @@ import { useClientLayoutEffect } from './useClientLayoutEffect.js';
|
|
|
4
4
|
import { isEventInside, getDeepestActiveElement } from './shadowDom.js';
|
|
5
5
|
|
|
6
6
|
// INTERNAL: deliberately not exported from src/index.ts.
|
|
7
|
+
/**
|
|
8
|
+
* Runs the caller's own handler on the element that holds the trigger, then
|
|
9
|
+
* the hook's, so neither replaces the other.
|
|
10
|
+
*/
|
|
11
|
+
function chain$1(own, ours) {
|
|
12
|
+
return event => {
|
|
13
|
+
own?.(event);
|
|
14
|
+
ours(event);
|
|
15
|
+
};
|
|
16
|
+
}
|
|
7
17
|
/**
|
|
8
18
|
* Whether focus that just arrived on `el` is keyboard focus, as the browser
|
|
9
19
|
* decides it for `:focus-visible`: focus from a key or from the browser's
|
|
@@ -45,7 +55,8 @@ let openings = 0;
|
|
|
45
55
|
*
|
|
46
56
|
* The keypress belongs to whatever has focus. When that is one of the open
|
|
47
57
|
* floating elements or their triggers, or nothing, the listener stops it in
|
|
48
|
-
* the capture phase, before anything around (a Popover, a Modal) sees it
|
|
58
|
+
* the capture phase, before anything around (a Popover, a Modal) sees it,
|
|
59
|
+
* unless the one it closes was set not to consume Escape.
|
|
49
60
|
* When focus is anywhere else, such as a combobox or a dialog of its own,
|
|
50
61
|
* the floating element still closes, since hover put it there without
|
|
51
62
|
* focus, and the key goes on to that owner.
|
|
@@ -79,7 +90,8 @@ function wantEscape(doc, entry) {
|
|
|
79
90
|
}
|
|
80
91
|
if (!top)
|
|
81
92
|
return;
|
|
82
|
-
if (
|
|
93
|
+
if (top.consumes() &&
|
|
94
|
+
(holding.length > 0 || !focused || focused === doc.body)) {
|
|
83
95
|
event.preventDefault();
|
|
84
96
|
event.stopPropagation();
|
|
85
97
|
}
|
|
@@ -111,7 +123,8 @@ function wantEscape(doc, entry) {
|
|
|
111
123
|
* - The pointer entering, or keyboard focus arriving on the trigger, asks to
|
|
112
124
|
* open after `openDelay`. A touch pointer doesn't count. Neither does focus
|
|
113
125
|
* that a press brings to the trigger: it counts when the browser shows it
|
|
114
|
-
* as `:focus-visible`.
|
|
126
|
+
* as `:focus-visible`. The pointer arriving from outside a shadow root the
|
|
127
|
+
* trigger renders into counts too, though React sends no enter for it.
|
|
115
128
|
* - Focus in the floating element holds it open however it got there,
|
|
116
129
|
* including focus in content it portals elsewhere, such as a nested card.
|
|
117
130
|
* Whether a click focuses what it lands on is up to the browser: Safari
|
|
@@ -122,8 +135,9 @@ function wantEscape(doc, entry) {
|
|
|
122
135
|
* else closes it on a timer.
|
|
123
136
|
* - Escape closes it at once, wherever focus is, and stops there when focus
|
|
124
137
|
* is in it, on its trigger or nowhere, so a `Popover` or `Modal` around it
|
|
125
|
-
* keeps its own. With focus somewhere else,
|
|
126
|
-
* it. Inside another one, the inner one
|
|
138
|
+
* keeps its own. With focus somewhere else, or with `consumeEscape` off,
|
|
139
|
+
* the key goes on after closing it. Inside another one, the inner one
|
|
140
|
+
* closes first. Escape before it
|
|
127
141
|
* opens takes the opening back and goes on its way. One key listener per
|
|
128
142
|
* document serves every hook on it, there only while one of them is open
|
|
129
143
|
* or on its way to open.
|
|
@@ -199,6 +213,7 @@ function useHoverOpen(options) {
|
|
|
199
213
|
clearTimeout(stateRef.current.timer);
|
|
200
214
|
latestRef.current.onOpenChange(false);
|
|
201
215
|
},
|
|
216
|
+
consumes: () => latestRef.current.consumeEscape !== false,
|
|
202
217
|
});
|
|
203
218
|
// Asks to open or to close. Escape can take an ask to open back until it is
|
|
204
219
|
// answered: by the commit that follows it, or, when the caller keeps it
|
|
@@ -330,6 +345,25 @@ function useHoverOpen(options) {
|
|
|
330
345
|
stateRef.current.pointerIn = true;
|
|
331
346
|
settle();
|
|
332
347
|
},
|
|
348
|
+
onPointerOver: event => {
|
|
349
|
+
// React makes the enter out of the pointerout on the element the
|
|
350
|
+
// pointer left, and skips this pointerover when that element is its
|
|
351
|
+
// own. Leaving page content for a trigger inside a shadow root, that
|
|
352
|
+
// pointerout names only the shadow host, so no enter reaches here.
|
|
353
|
+
// A pointerover from outside the holder and the floating element is
|
|
354
|
+
// the pointer arriving, then. One from inside them is the pointer
|
|
355
|
+
// moving about within, which doesn't bring back a closed one.
|
|
356
|
+
const s = stateRef.current;
|
|
357
|
+
if (s.pointerIn || event.pointerType === 'touch')
|
|
358
|
+
return;
|
|
359
|
+
const from = event.relatedTarget;
|
|
360
|
+
if (within(event.currentTarget, from) ||
|
|
361
|
+
within(latestRef.current.floatingRef.current, from)) {
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
s.pointerIn = true;
|
|
365
|
+
settle();
|
|
366
|
+
},
|
|
333
367
|
onPointerLeave: event => {
|
|
334
368
|
// A touch pointer never set pointerIn, so its leave has nothing to
|
|
335
369
|
// clear, and on a device with a mouse too it would close a card the
|
|
@@ -386,5 +420,5 @@ function useHoverOpen(options) {
|
|
|
386
420
|
}, [settle, ask]);
|
|
387
421
|
}
|
|
388
422
|
|
|
389
|
-
export { useHoverOpen };
|
|
423
|
+
export { chain$1 as chain, useHoverOpen };
|
|
390
424
|
//# sourceMappingURL=useHoverOpen.js.map
|