@allxsmith/bestax-bulma 5.65.0 → 5.66.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/Card.js +6 -6
- package/dist/esm/components/Card.js.map +1 -1
- package/dist/esm/components/Carousel.js +19 -7
- package/dist/esm/components/Carousel.js.map +1 -1
- package/dist/esm/components/Message.js +1 -1
- package/dist/esm/components/Message.js.map +1 -1
- package/dist/esm/components/Modal.js +1 -1
- package/dist/esm/components/Modal.js.map +1 -1
- package/dist/esm/components/Navbar.js.map +1 -1
- package/dist/esm/components/Popover.js +4 -1
- package/dist/esm/components/Popover.js.map +1 -1
- package/dist/esm/components/SegmentedControl.js +4 -4
- package/dist/esm/components/SegmentedControl.js.map +1 -1
- package/dist/esm/components/Sidebar.js +1 -1
- package/dist/esm/components/Sidebar.js.map +1 -1
- package/dist/esm/elements/Figure.js +1 -1
- package/dist/esm/elements/Figure.js.map +1 -1
- package/dist/esm/form/ColorInputBase.js +1 -1
- package/dist/esm/form/ColorInputBase.js.map +1 -1
- package/dist/esm/form/DateInput.js +4 -1
- package/dist/esm/form/DateInput.js.map +1 -1
- package/dist/esm/form/DateInputBase.js +1 -1
- package/dist/esm/form/DateInputBase.js.map +1 -1
- package/dist/esm/form/DateRangeInput.js +4 -1
- package/dist/esm/form/DateRangeInput.js.map +1 -1
- package/dist/esm/form/DateRangeInputBase.js +1 -1
- package/dist/esm/form/DateRangeInputBase.js.map +1 -1
- package/dist/esm/form/DateTimeInput.js +4 -1
- package/dist/esm/form/DateTimeInput.js.map +1 -1
- package/dist/esm/form/DateTimeInputBase.js +1 -1
- package/dist/esm/form/DateTimeInputBase.js.map +1 -1
- package/dist/esm/form/Slider.js +19 -9
- package/dist/esm/form/Slider.js.map +1 -1
- package/dist/esm/form/TimeInput.js +4 -1
- package/dist/esm/form/TimeInput.js.map +1 -1
- package/dist/esm/form/TimeInputBase.js +1 -1
- package/dist/esm/form/TimeInputBase.js.map +1 -1
- package/dist/esm/form/TreeSelect.js.map +1 -1
- package/dist/esm/form/_pickerInternals/PickerPopover.js +4 -1
- package/dist/esm/form/_pickerInternals/PickerPopover.js.map +1 -1
- package/dist/esm/form/_pickerInternals/TimeWheels.js +1 -1
- package/dist/esm/form/_pickerInternals/TimeWheels.js.map +1 -1
- package/dist/esm/form/_pickerInternals/useLocaleDefaults.js +15 -1
- package/dist/esm/form/_pickerInternals/useLocaleDefaults.js.map +1 -1
- package/dist/esm/form/_selectInternals/DialogSelect.js +1 -1
- package/dist/esm/form/_selectInternals/DialogSelect.js.map +1 -1
- package/dist/esm/helpers/useAnchoredPosition.js +11 -3
- package/dist/esm/helpers/useAnchoredPosition.js.map +1 -1
- package/dist/extras.css +1 -1
- package/dist/extras.css.map +1 -1
- package/dist/index.cjs +122 -40
- package/dist/index.cjs.map +1 -1
- package/dist/index.esm.js +6 -6
- package/dist/types/components/Card.d.ts +52 -0
- package/dist/types/components/Carousel.d.ts +12 -3
- package/dist/types/components/Message.d.ts +16 -0
- package/dist/types/components/Modal.d.ts +71 -0
- package/dist/types/components/Navbar.d.ts +10 -0
- package/dist/types/components/Popover.d.ts +5 -1
- package/dist/types/components/SegmentedControl.d.ts +9 -0
- package/dist/types/components/Sidebar.d.ts +45 -5
- package/dist/types/elements/Figure.d.ts +11 -0
- package/dist/types/form/ColorInputBase.d.ts +7 -1
- package/dist/types/form/DateInputBase.d.ts +7 -1
- package/dist/types/form/DateRangeInputBase.d.ts +7 -1
- package/dist/types/form/DateTimeInputBase.d.ts +7 -1
- package/dist/types/form/TimeInputBase.d.ts +7 -1
- package/dist/types/form/TreeSelect.d.ts +4 -1
- package/dist/types/form/_pickerInternals/PickerPopover.d.ts +5 -0
- package/dist/types/form/_pickerInternals/useLocaleDefaults.d.ts +12 -0
- package/dist/types/form/_selectInternals/DialogSelect.d.ts +1 -0
- package/dist/types/helpers/useAnchoredPosition.d.ts +9 -2
- package/dist/types-cjs/components/Card.d.ts +52 -0
- package/dist/types-cjs/components/Carousel.d.ts +12 -3
- package/dist/types-cjs/components/Message.d.ts +16 -0
- package/dist/types-cjs/components/Modal.d.ts +71 -0
- package/dist/types-cjs/components/Navbar.d.ts +10 -0
- package/dist/types-cjs/components/Popover.d.ts +5 -1
- package/dist/types-cjs/components/SegmentedControl.d.ts +9 -0
- package/dist/types-cjs/components/Sidebar.d.ts +45 -5
- package/dist/types-cjs/elements/Figure.d.ts +11 -0
- package/dist/types-cjs/form/ColorInputBase.d.ts +7 -1
- package/dist/types-cjs/form/DateInputBase.d.ts +7 -1
- package/dist/types-cjs/form/DateRangeInputBase.d.ts +7 -1
- package/dist/types-cjs/form/DateTimeInputBase.d.ts +7 -1
- package/dist/types-cjs/form/TimeInputBase.d.ts +7 -1
- package/dist/types-cjs/form/TreeSelect.d.ts +4 -1
- package/dist/types-cjs/form/_pickerInternals/PickerPopover.d.ts +5 -0
- package/dist/types-cjs/form/_pickerInternals/useLocaleDefaults.d.ts +12 -0
- package/dist/types-cjs/form/_selectInternals/DialogSelect.d.ts +1 -0
- package/dist/types-cjs/helpers/useAnchoredPosition.d.ts +9 -2
- 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 +6 -6
- package/src/scss/CLAUDE.md +1 -1
- package/src/scss/_input-trigger.scss +24 -5
- package/src/scss/components/_carousel.scss +22 -4
- package/src/scss/components/_popover.scss +11 -0
- package/src/scss/elements/_button.scss +49 -0
- package/src/scss/elements/_index.scss +1 -0
- package/src/scss/elements/_loader.scss +10 -5
- package/src/scss/form/_dateinput.scss +6 -0
- package/src/scss/form/_passwordinput.scss +6 -5
- package/src/scss/form/_picker-popover.scss +45 -5
- package/src/scss/form/_slider.scss +39 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SegmentedControl.js","sources":["../../../src/components/SegmentedControl.tsx"],"sourcesContent":["import React, {\n createContext,\n forwardRef,\n useCallback,\n useContext,\n useEffect,\n useRef,\n useState,\n} from 'react';\nimport { Button, type ButtonOwnProps } from '../elements/Button';\nimport { classNames, usePrefixedClassNames } from '../helpers/classNames';\nimport { useBulmaClasses, BulmaClassesProps } from '../helpers/useBulmaClasses';\nimport { withSubComponents } from '../helpers/withSubComponents';\nimport { warnOnce } from '../helpers/devWarnings';\nimport { useClientLayoutEffect } from '../helpers/useClientLayoutEffect';\n\n// Spelled out so the API page can list the values. The tests hold it to\n// ToggleButton's, since the checked segment fills the way a pressed toggle\n// does and for the same reasons.\n/**\n * The fills the checked segment takes: `Button`'s colors, minus `text` and\n * `ghost`, which don't fill, and `white`, `light`, `dark` and `black`, which in\n * one theme or the other fill about as light or dark as an unchecked segment\n * already is. The same set `ToggleButton` takes.\n */\nexport type SegmentedControlColor =\n 'primary' | 'link' | 'info' | 'success' | 'warning' | 'danger';\n\n// ---------------------------------------------------------------------------\n// Context\n// ---------------------------------------------------------------------------\n\n/** A mounted segment, as the group needs to know it for the tab stop. */\ninterface SegmentRecord {\n value: string;\n disabled: boolean;\n node: HTMLButtonElement;\n}\n\ninterface SegmentedControlContextValue {\n /** The checked segment's value, or `undefined` while none is. */\n checked: string | undefined;\n /** The segment that holds `tabIndex={0}`, or `undefined` for none. */\n tabStop: string | undefined;\n /** The fill the checked segment takes. */\n color: SegmentedControlColor;\n /** Checks a segment, unless it's checked already. */\n select: (value: string) => void;\n /** Records which segment has focus, so the tab stop can follow it. */\n setFocused: (value: string | undefined) => void;\n register: (record: SegmentRecord) => () => void;\n}\n\n// Not exported, so it stays out of the public API that `index.ts` re-exports.\nconst SegmentedControlContext =\n createContext<SegmentedControlContextValue | null>(null);\n\n/** Sorts mounted segments into the order they sit in the page. */\nconst inDocumentOrder = (a: SegmentRecord, b: SegmentRecord) =>\n a.node.compareDocumentPosition(b.node) & Node.DOCUMENT_POSITION_FOLLOWING\n ? -1\n : 1;\n\n/** How far each arrow key moves along the row. */\nconst STEPS: Record<string, number> = {\n ArrowRight: 1,\n ArrowDown: 1,\n ArrowLeft: -1,\n ArrowUp: -1,\n};\n\n/**\n * The segment a key moves to, found among the segments of the same group in\n * DOM order, skipping disabled ones and wrapping at either end. `undefined`\n * for a key the group leaves alone, when no enabled segment is left, or when\n * the segment isn't inside its row in the page, as a portaled one isn't.\n */\nfunction segmentFor(\n current: HTMLButtonElement,\n key: string\n): HTMLButtonElement | undefined {\n const group = current.closest('[role=\"radiogroup\"]');\n if (!group) return undefined;\n const segments = Array.from(\n group.querySelectorAll<HTMLButtonElement>('[role=\"radio\"]')\n );\n const enabled = segments.filter(segment => !segment.disabled);\n\n if (key === 'Home') return enabled[0];\n if (key === 'End') return enabled[enabled.length - 1];\n\n const step = STEPS[key];\n if (!step) return undefined;\n const at = segments.indexOf(current);\n for (let i = 1; i <= segments.length; i += 1) {\n const candidate =\n segments[(at + step * i + segments.length) % segments.length];\n if (!candidate.disabled) return candidate;\n }\n return undefined;\n}\n\n// ---------------------------------------------------------------------------\n// SegmentedControl (root)\n// ---------------------------------------------------------------------------\n\n/**\n * Props for the SegmentedControl component. The helper props, the other `<div>`\n * attributes and the `ref` all land on the row.\n *\n * @extraProp {string} [aria-label] - Names the group for a screen reader, such as \"Text alignment\". A radio group needs a name, so pass this or `aria-labelledby`, and a development build warns when it has neither.\n * @extraProp {string} [aria-labelledby] - The id of a visible heading or label that names the group, in place of `aria-label`.\n * @extraProp {React.Ref<HTMLDivElement>} [ref] - Ref forwarded to the row's `<div>`.\n */\nexport interface SegmentedControlProps\n extends\n Omit<\n React.HTMLAttributes<HTMLDivElement>,\n 'onChange' | 'defaultValue' | 'color' | 'role'\n >,\n Omit<\n BulmaClassesProps,\n 'color' | 'colorShade' | 'backgroundColor' | 'backgroundColorShade'\n > {\n /**\n * The checked segment's `value`, when you hold the state. Setting it makes\n * the group controlled: picking a segment only calls `onChange`, and nothing\n * changes until you pass the new value. `null` checks nothing, with the\n * tab stop `defaultValue` describes for that. Pick this or `defaultValue`\n * for the group's lifetime.\n */\n value?: string | null;\n /**\n * The segment checked to begin with, when the group holds its own state.\n * Leave it out and nothing is checked until someone picks one, and Tab\n * lands on the first enabled segment meanwhile. That fallback takes hold\n * once the row has rendered in the browser: server markup with nothing\n * checked has no tab stop until then, while a checked segment is the stop\n * from the server markup on.\n */\n defaultValue?: string;\n /**\n * Called with the newly checked segment's `value`, whether or not `value`\n * is set. The arrow keys, Home and End check the segment they move to, so\n * each of those calls it too: the check follows focus, as in a native radio\n * group. Pressing the checked segment again doesn't call it, since a\n * segmented control can't be cleared: one stays checked once one is.\n */\n onChange?: (value: string) => void;\n /**\n * The checked segment's fill. The others are plain buttons, so the fill is\n * the difference people see. Takes the same colors as `ToggleButton`, for\n * the same reason: `white`, `light`, `dark` and `black` fill about as light\n * or dark as a plain button already is in one theme or the other.\n */\n color?: SegmentedControlColor;\n /** Size of every segment, through Bulma's `are-<size>` on the row. */\n size?: 'small' | 'medium' | 'large';\n /**\n * Joins the segments into one row with no gap between them, Bulma's\n * `has-addons`. Set it to `false` for separate buttons.\n */\n hasAddons?: boolean;\n /** Centers the row. */\n isCentered?: boolean;\n /** Aligns the row to the right. */\n isRight?: boolean;\n /** Additional CSS classes to apply. */\n className?: string;\n /** The `SegmentedControl.Segment`s, each with a `value` of its own. */\n children?: React.ReactNode;\n}\n\n/**\n * The `SegmentedControl` component renders a row of buttons where only one can be on, and tells a screen reader it's a radio group.\n *\n * It's Bulma's `.buttons.has-addons` row, with the checked segment filled and\n * marked `is-selected`, the way Bulma's button docs show it. To a screen\n * reader it's `role=\"radiogroup\"`, with `role=\"radio\"` and `aria-checked` on\n * each segment, which is how the APG toolbar example marks its alignment\n * buttons. The keys follow the APG radio group: the row is one tab stop, the\n * arrow keys move to the next or previous enabled segment and check it,\n * wrapping at either end, Home and End go to the first and last, and Space\n * checks the focused segment. Once a segment is checked, one always is.\n *\n * It carries no form value. A choice that's sent with a form is `Radios`.\n *\n * @function\n * @param {SegmentedControlProps} props - Props for the SegmentedControl component.\n * @param {React.Ref<HTMLDivElement>} ref - Forwarded ref to the row's `<div>`.\n * @returns {JSX.Element} The rendered segmented control.\n * @see {@link https://www.w3.org/WAI/ARIA/apg/patterns/radio/ | APG radio group pattern}\n * @see {@link https://bulma.io/documentation/elements/button/#list-of-buttons | Bulma list of buttons}\n *\n * @example\n * <SegmentedControl aria-label=\"Text alignment\" defaultValue=\"left\">\n * <SegmentedControl.Segment value=\"left\">Left</SegmentedControl.Segment>\n * <SegmentedControl.Segment value=\"center\">Center</SegmentedControl.Segment>\n * <SegmentedControl.Segment value=\"right\">Right</SegmentedControl.Segment>\n * </SegmentedControl>\n */\nconst SegmentedControlRoot = forwardRef<HTMLDivElement, SegmentedControlProps>(\n function SegmentedControl(\n {\n value,\n defaultValue,\n onChange,\n color = 'link',\n size,\n hasAddons = true,\n isCentered,\n isRight,\n className,\n children,\n ...props\n },\n ref\n ) {\n const { bulmaHelperClasses, rest } = useBulmaClasses({ ...props });\n\n // Controlled vs uncontrolled, as in Tabs and Collapses.\n const isControlled = value !== undefined;\n const [internalValue, setInternalValue] = useState(defaultValue);\n const checked = isControlled ? (value ?? undefined) : internalValue;\n\n const select = (next: string) => {\n if (next === checked) return;\n if (!isControlled) setInternalValue(next);\n onChange?.(next);\n };\n\n // The segments mount and register from an effect, so the group knows\n // which can hold the tab stop wherever in the tree they sit.\n const [segments, setSegments] = useState<SegmentRecord[]>([]);\n const register = useCallback((record: SegmentRecord) => {\n setSegments(prev => [...prev, record]);\n return () => setSegments(prev => prev.filter(r => r !== record));\n }, []);\n const [focused, setFocused] = useState<string | undefined>();\n\n // The first enabled segment in the page, sorted when it's read rather\n // than when a segment registers, so reordered segments are seen too. It's\n // read after the commit: a render runs before React moves the nodes, so\n // sorting there would find the old order.\n const [firstEnabled, setFirstEnabled] = useState<string | undefined>();\n useClientLayoutEffect(() => {\n const inPage = segments.filter(s => !s.disabled).sort(inDocumentOrder);\n setFirstEnabled(inPage[0]?.value);\n }, [segments, children]);\n\n // One tab stop for the row (roving tabindex). While focus is in the row\n // the stop follows it, so Tab leaves from wherever focus is even when a\n // controlled group hasn't taken the new value. Otherwise it's the checked\n // segment, or the first enabled one when none is checked or the checked\n // one is disabled. Before the segments register (the server render and\n // the first client render) the checked one holds it, so a row with\n // nothing checked has no stop until the client has rendered.\n const canHoldStop = (candidate: string | undefined) =>\n segments.some(s => !s.disabled && s.value === candidate);\n let tabStop: string | undefined;\n if (canHoldStop(focused)) tabStop = focused;\n else if (segments.length === 0 || canHoldStop(checked)) tabStop = checked;\n else tabStop = firstEnabled;\n\n const named = Boolean(rest['aria-label'] || rest['aria-labelledby']);\n useEffect(() => {\n if (!named) {\n warnOnce(\n 'SegmentedControl:unnamed',\n '[bestax-bulma] <SegmentedControl> has no accessible name. Pass ' +\n 'aria-label or aria-labelledby, so a screen reader can say what ' +\n 'the choice is.'\n );\n }\n }, [named]);\n\n const rowClasses = usePrefixedClassNames('buttons', {\n 'has-addons': hasAddons,\n 'is-centered': isCentered,\n 'is-right': isRight,\n [`are-${size}`]: size,\n });\n\n // The props are spread first, so the role a screen reader relies on wins\n // over anything a JavaScript caller or a spread object passes along.\n return (\n <SegmentedControlContext.Provider\n value={{ checked, tabStop, color, select, setFocused, register }}\n >\n <div\n {...rest}\n ref={ref}\n role=\"radiogroup\"\n className={classNames(rowClasses, bulmaHelperClasses, className)}\n >\n {children}\n </div>\n </SegmentedControlContext.Provider>\n );\n }\n);\n\n// ---------------------------------------------------------------------------\n// SegmentedControl.Segment\n// ---------------------------------------------------------------------------\n\n/**\n * Props for SegmentedControl.Segment. The helper props, the other button\n * attributes and the `ref` all land on the `<button>`.\n *\n * Some of `Button`'s props are left out on purpose. `as`, `type`, `role` and\n * `tabIndex`, since a segment is always a `<button type=\"button\" role=\"radio\">`\n * and the group decides which segment Tab reaches. `color`, which the group\n * sets for the checked segment, and the props that change the fill\n * (`isActive`, `isLight`, `isOutlined`, `isInverted`, `textColor`, `bgColor`\n * and their shades), since each would color it whether checked or not. `size`\n * and the width and shape props, which the row sets for every segment. And\n * `isStatic` and `isLoading`, a segment you couldn't pick.\n *\n * @extraProp {React.Ref<HTMLButtonElement>} [ref] - Ref forwarded to the `<button>`.\n */\nexport interface SegmentedControlSegmentProps\n extends\n Omit<\n ButtonOwnProps,\n | 'color'\n | 'size'\n | 'isActive'\n | 'isStatic'\n | 'isLoading'\n | 'isLight'\n | 'isOutlined'\n | 'isInverted'\n | 'isRounded'\n | 'isFullwidth'\n | 'isFullWidth'\n | 'textColor'\n | 'bgColor'\n | 'colorShade'\n | 'backgroundColorShade'\n >,\n Omit<\n React.ButtonHTMLAttributes<HTMLButtonElement>,\n | keyof ButtonOwnProps\n | 'type'\n | 'value'\n | 'role'\n | 'tabIndex'\n | 'aria-checked'\n | 'aria-pressed'\n > {\n /**\n * **Required.** What `onChange` reports when this segment is checked, and\n * what the group's `value` and `defaultValue` name it by. Unique within the\n * group.\n */\n value: string;\n /**\n * The label. An icon on its own needs an `aria-label`, so a screen reader\n * has something to say.\n */\n children?: React.ReactNode;\n /**\n * Disables the segment, the same as `disabled`: it can't be clicked, the\n * arrow keys, Home and End skip it, and it never holds the row's tab stop.\n * A checked one keeps its fill under Bulma's disabled look.\n */\n isDisabled?: boolean;\n /**\n * Not accepted: `aria-checked` follows the group's value. Declared because\n * JSX lets a hyphenated attribute through when the props don't name it.\n * @internal\n */\n 'aria-checked'?: never;\n /**\n * Not accepted: a segment is a radio, which says checked, not pressed.\n * @internal\n */\n 'aria-pressed'?: never;\n}\n\n/**\n * One option in the row: a `<button role=\"radio\">` that's checked while the group's value is its `value`, filled with the group's `color` and marked `is-selected`.\n *\n * @function\n * @param {SegmentedControlSegmentProps} props - Props for the segment.\n * @param {React.Ref<HTMLButtonElement>} ref - Forwarded ref to the `<button>`.\n * @returns {JSX.Element} The rendered segment.\n */\nconst Segment = forwardRef<HTMLButtonElement, SegmentedControlSegmentProps>(\n function Segment(\n {\n value,\n isDisabled,\n disabled,\n className,\n onClick,\n onKeyDown,\n onFocus,\n onBlur,\n ...props\n },\n ref\n ) {\n const ctx = useContext(SegmentedControlContext);\n const checked = ctx !== null && ctx.checked === value;\n const off = Boolean(isDisabled || disabled);\n\n // Prefixed like Button's own classes, which a hand-written\n // `className=\"is-selected\"` could not be under ConfigProvider classPrefix.\n // Bulma lifts `is-selected` above its neighbours in `.buttons.has-addons`.\n const selectedClass = usePrefixedClassNames({ 'is-selected': checked });\n\n const nodeRef = useRef<HTMLButtonElement | null>(null);\n const setRefs = useCallback(\n (node: HTMLButtonElement | null) => {\n nodeRef.current = node;\n if (typeof ref === 'function') ref(node);\n else if (ref) ref.current = node;\n },\n [ref]\n );\n\n const register = ctx?.register;\n useEffect(() => {\n if (!register) {\n warnOnce(\n 'SegmentedControl.Segment:outside',\n '[bestax-bulma] <SegmentedControl.Segment> is outside a ' +\n '<SegmentedControl>, so nothing can check it. Put it inside one.'\n );\n return undefined;\n }\n return register({\n value,\n disabled: off,\n node: nodeRef.current as HTMLButtonElement,\n });\n }, [register, value, off]);\n\n // The caller's onClick runs first, so a `preventDefault()` there keeps\n // the segment from being checked. A disabled button gets no click.\n const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {\n onClick?.(event);\n if (event.defaultPrevented) return;\n ctx?.select(value);\n };\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLButtonElement>) => {\n onKeyDown?.(event);\n if (\n !ctx ||\n event.defaultPrevented ||\n event.altKey ||\n event.ctrlKey ||\n event.metaKey\n ) {\n return;\n }\n const target = segmentFor(event.currentTarget, event.key);\n if (!target) return;\n event.preventDefault();\n // The row has taken this key, so nothing above it acts on it too: a\n // Carousel moves its slide on the arrows from a document listener that\n // doesn't read `defaultPrevented`. Keys the row leaves alone still bubble.\n event.stopPropagation();\n target.focus();\n // The check follows focus, APG's rule for a radio group outside a\n // toolbar. It goes through a click on the segment, as a native radio's\n // does, so its onClick sees it. Inside a toolbar the arrows would move\n // focus alone; that is the line a toolbar would change.\n target.click();\n };\n\n const handleFocus = (event: React.FocusEvent<HTMLButtonElement>) => {\n onFocus?.(event);\n ctx?.setFocused(value);\n };\n\n // The segment focus moves to, if any, records itself straight after.\n const handleBlur = (event: React.FocusEvent<HTMLButtonElement>) => {\n onBlur?.(event);\n ctx?.setFocused(undefined);\n };\n\n // The props are spread first, so what a radio depends on wins over\n // anything a JavaScript caller or a spread object passes along: it stays\n // a button, never submits, reports whether it's checked, and the group\n // decides whether Tab reaches it.\n return (\n <Button\n {...props}\n ref={setRefs}\n as=\"button\"\n type=\"button\"\n role=\"radio\"\n aria-checked={checked}\n tabIndex={ctx ? (ctx.tabStop === value ? 0 : -1) : undefined}\n isDisabled={off}\n color={checked ? ctx.color : undefined}\n className={classNames(selectedClass, className)}\n onClick={handleClick}\n onKeyDown={handleKeyDown}\n onFocus={handleFocus}\n onBlur={handleBlur}\n />\n );\n }\n);\n\nSegment.displayName = 'SegmentedControl.Segment';\n\nexport const SegmentedControl = withSubComponents(\n SegmentedControlRoot,\n { Segment },\n 'SegmentedControl'\n);\n\nexport default SegmentedControl;\n"],"names":["_jsx"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SegmentedControl.js","sources":["../../../src/components/SegmentedControl.tsx"],"sourcesContent":["import React, {\n createContext,\n forwardRef,\n useCallback,\n useContext,\n useEffect,\n useRef,\n useState,\n} from 'react';\nimport { Button, type ButtonOwnProps } from '../elements/Button';\nimport { classNames, usePrefixedClassNames } from '../helpers/classNames';\nimport { useBulmaClasses, BulmaClassesProps } from '../helpers/useBulmaClasses';\nimport { withSubComponents } from '../helpers/withSubComponents';\nimport { warnOnce } from '../helpers/devWarnings';\nimport { useClientLayoutEffect } from '../helpers/useClientLayoutEffect';\n\n// Spelled out so the API page can list the values. The tests hold it to\n// ToggleButton's, since the checked segment fills the way a pressed toggle\n// does and for the same reasons.\n/**\n * The fills the checked segment takes: `Button`'s colors, minus `text` and\n * `ghost`, which don't fill, and `white`, `light`, `dark` and `black`, which in\n * one theme or the other fill about as light or dark as an unchecked segment\n * already is. The same set `ToggleButton` takes.\n */\nexport type SegmentedControlColor =\n 'primary' | 'link' | 'info' | 'success' | 'warning' | 'danger';\n\n// ---------------------------------------------------------------------------\n// Context\n// ---------------------------------------------------------------------------\n\n/** A mounted segment, as the group needs to know it for the tab stop. */\ninterface SegmentRecord {\n value: string;\n disabled: boolean;\n node: HTMLButtonElement;\n}\n\ninterface SegmentedControlContextValue {\n /** The checked segment's value, or `undefined` while none is. */\n checked: string | undefined;\n /** The segment that holds `tabIndex={0}`, or `undefined` for none. */\n tabStop: string | undefined;\n /** The fill the checked segment takes. */\n color: SegmentedControlColor;\n /** Checks a segment, unless it's checked already. */\n select: (value: string) => void;\n /** Records which segment has focus, so the tab stop can follow it. */\n setFocused: (value: string | undefined) => void;\n register: (record: SegmentRecord) => () => void;\n}\n\n// Not exported, so it stays out of the public API that `index.ts` re-exports.\nconst SegmentedControlContext =\n createContext<SegmentedControlContextValue | null>(null);\n\n/** Sorts mounted segments into the order they sit in the page. */\nconst inDocumentOrder = (a: SegmentRecord, b: SegmentRecord) =>\n a.node.compareDocumentPosition(b.node) & Node.DOCUMENT_POSITION_FOLLOWING\n ? -1\n : 1;\n\n/** How far each arrow key moves along the row. */\nconst STEPS: Record<string, number> = {\n ArrowRight: 1,\n ArrowDown: 1,\n ArrowLeft: -1,\n ArrowUp: -1,\n};\n\n/**\n * The segment a key moves to, found among the segments of the same group in\n * DOM order, skipping disabled ones and wrapping at either end. `undefined`\n * for a key the group leaves alone, when no enabled segment is left, or when\n * the segment isn't inside its row in the page, as a portaled one isn't.\n */\nfunction segmentFor(\n current: HTMLButtonElement,\n key: string\n): HTMLButtonElement | undefined {\n const group = current.closest('[role=\"radiogroup\"]');\n if (!group) return undefined;\n const segments = Array.from(\n group.querySelectorAll<HTMLButtonElement>('[role=\"radio\"]')\n );\n const enabled = segments.filter(segment => !segment.disabled);\n\n if (key === 'Home') return enabled[0];\n if (key === 'End') return enabled[enabled.length - 1];\n\n const step = STEPS[key];\n if (!step) return undefined;\n const at = segments.indexOf(current);\n for (let i = 1; i <= segments.length; i += 1) {\n const candidate =\n segments[(at + step * i + segments.length) % segments.length];\n if (!candidate.disabled) return candidate;\n }\n return undefined;\n}\n\n// ---------------------------------------------------------------------------\n// SegmentedControl (root)\n// ---------------------------------------------------------------------------\n\n/**\n * Props for the SegmentedControl component. The helper props, the other `<div>`\n * attributes and the `ref` all land on the row.\n *\n * @extraProp {string} [aria-label] - Names the group for a screen reader, such as \"Text alignment\". A radio group needs a name, so pass this or `aria-labelledby`, and a development build warns when it has neither.\n * @extraProp {string} [aria-labelledby] - The id of a visible heading or label that names the group, in place of `aria-label`.\n * @extraProp {React.Ref<HTMLDivElement>} [ref] - Ref forwarded to the row's `<div>`.\n */\nexport interface SegmentedControlProps\n extends\n Omit<\n React.HTMLAttributes<HTMLDivElement>,\n 'onChange' | 'defaultValue' | 'color' | 'role'\n >,\n Omit<\n BulmaClassesProps,\n 'color' | 'colorShade' | 'backgroundColor' | 'backgroundColorShade'\n > {\n /**\n * The checked segment's `value`, when you hold the state. Setting it makes\n * the group controlled: picking a segment only calls `onChange`, and nothing\n * changes until you pass the new value. `null` checks nothing, with the\n * tab stop `defaultValue` describes for that. Pick this or `defaultValue`\n * for the group's lifetime.\n */\n value?: string | null;\n /**\n * The segment checked to begin with, when the group holds its own state.\n * Leave it out and nothing is checked until someone picks one, and Tab\n * lands on the first enabled segment meanwhile. That fallback takes hold\n * once the row has rendered in the browser: server markup with nothing\n * checked has no tab stop until then, while a checked segment is the stop\n * from the server markup on.\n */\n defaultValue?: string;\n /**\n * Called with the newly checked segment's `value`, whether or not `value`\n * is set. The arrow keys, Home and End check the segment they move to, so\n * each of those calls it too: the check follows focus, as in a native radio\n * group. Pressing the checked segment again doesn't call it, since a\n * segmented control can't be cleared: one stays checked once one is.\n */\n onChange?: (value: string) => void;\n /**\n * The checked segment's fill. The others are plain buttons, so the fill is\n * the difference people see. Takes the same colors as `ToggleButton`, for\n * the same reason: `white`, `light`, `dark` and `black` fill about as light\n * or dark as a plain button already is in one theme or the other.\n */\n color?: SegmentedControlColor;\n /** Size of every segment, through Bulma's `are-<size>` on the row. */\n size?: 'small' | 'medium' | 'large';\n /**\n * Joins the segments into one row with no gap between them, Bulma's\n * `has-addons`. Set it to `false` for separate buttons.\n */\n hasAddons?: boolean;\n /** Centers the row. */\n isCentered?: boolean;\n /** Aligns the row to the right. */\n isRight?: boolean;\n /** Additional CSS classes to apply. */\n className?: string;\n /** The `SegmentedControl.Segment`s, each with a `value` of its own. */\n children?: React.ReactNode;\n}\n\n/**\n * The `SegmentedControl` component renders a row of buttons where only one can be on, and tells a screen reader it's a radio group.\n *\n * It's Bulma's `.buttons.has-addons` row, with the checked segment filled and\n * marked `is-selected`, the way Bulma's button docs show it. To a screen\n * reader it's `role=\"radiogroup\"`, with `role=\"radio\"` and `aria-checked` on\n * each segment, which is how the APG toolbar example marks its alignment\n * buttons. The keys follow the APG radio group: the row is one tab stop, the\n * arrow keys move to the next or previous enabled segment and check it,\n * wrapping at either end, Home and End go to the first and last, and Space\n * checks the focused segment. Once a segment is checked, one always is.\n *\n * It carries no form value. A choice that's sent with a form is `Radios`.\n *\n * @function\n * @param {SegmentedControlProps} props - Props for the SegmentedControl component.\n * @param {React.Ref<HTMLDivElement>} ref - Forwarded ref to the row's `<div>`.\n * @returns {JSX.Element} The rendered segmented control.\n * @see {@link https://www.w3.org/WAI/ARIA/apg/patterns/radio/ | APG radio group pattern}\n * @see {@link https://bulma.io/documentation/elements/button/#list-of-buttons | Bulma list of buttons}\n *\n * @example\n * <SegmentedControl aria-label=\"Text alignment\" defaultValue=\"left\">\n * <SegmentedControl.Segment value=\"left\">Left</SegmentedControl.Segment>\n * <SegmentedControl.Segment value=\"center\">Center</SegmentedControl.Segment>\n * <SegmentedControl.Segment value=\"right\">Right</SegmentedControl.Segment>\n * </SegmentedControl>\n */\nconst SegmentedControlRoot = forwardRef<HTMLDivElement, SegmentedControlProps>(\n function SegmentedControl(\n {\n value,\n defaultValue,\n onChange,\n color = 'link',\n size,\n hasAddons = true,\n isCentered,\n isRight,\n className,\n children,\n ...props\n },\n ref\n ) {\n const { bulmaHelperClasses, rest } = useBulmaClasses({ ...props });\n\n // Controlled vs uncontrolled, as in Tabs and Collapses.\n const isControlled = value !== undefined;\n const [internalValue, setInternalValue] = useState(defaultValue);\n const checked = isControlled ? (value ?? undefined) : internalValue;\n\n const select = (next: string) => {\n if (next === checked) return;\n if (!isControlled) setInternalValue(next);\n onChange?.(next);\n };\n\n // The segments mount and register from an effect, so the group knows\n // which can hold the tab stop wherever in the tree they sit.\n const [segments, setSegments] = useState<SegmentRecord[]>([]);\n const register = useCallback((record: SegmentRecord) => {\n setSegments(prev => [...prev, record]);\n return () => setSegments(prev => prev.filter(r => r !== record));\n }, []);\n const [focused, setFocused] = useState<string | undefined>();\n\n // The first enabled segment in the page, sorted when it's read rather\n // than when a segment registers, so reordered segments are seen too. It's\n // read after the commit: a render runs before React moves the nodes, so\n // sorting there would find the old order.\n const [firstEnabled, setFirstEnabled] = useState<string | undefined>();\n useClientLayoutEffect(() => {\n const inPage = segments.filter(s => !s.disabled).sort(inDocumentOrder);\n setFirstEnabled(inPage[0]?.value);\n }, [segments, children]);\n\n // One tab stop for the row (roving tabindex). While focus is in the row\n // the stop follows it, so Tab leaves from wherever focus is even when a\n // controlled group hasn't taken the new value. Otherwise it's the checked\n // segment, or the first enabled one when none is checked or the checked\n // one is disabled. Before the segments register (the server render and\n // the first client render) the checked one holds it, so a row with\n // nothing checked has no stop until the client has rendered.\n const canHoldStop = (candidate: string | undefined) =>\n segments.some(s => !s.disabled && s.value === candidate);\n let tabStop: string | undefined;\n if (canHoldStop(focused)) tabStop = focused;\n else if (segments.length === 0 || canHoldStop(checked)) tabStop = checked;\n else tabStop = firstEnabled;\n\n const named = Boolean(rest['aria-label'] || rest['aria-labelledby']);\n useEffect(() => {\n if (!named) {\n warnOnce(\n 'SegmentedControl:unnamed',\n '[bestax-bulma] <SegmentedControl> has no accessible name. Pass ' +\n 'aria-label or aria-labelledby, so a screen reader can say what ' +\n 'the choice is.'\n );\n }\n }, [named]);\n\n const rowClasses = usePrefixedClassNames('buttons', {\n 'has-addons': hasAddons,\n 'is-centered': isCentered,\n 'is-right': isRight,\n [`are-${size}`]: size,\n });\n\n // The props are spread first, so the role a screen reader relies on wins\n // over anything a JavaScript caller or a spread object passes along.\n return (\n <SegmentedControlContext.Provider\n value={{ checked, tabStop, color, select, setFocused, register }}\n >\n <div\n {...rest}\n ref={ref}\n role=\"radiogroup\"\n className={classNames(rowClasses, bulmaHelperClasses, className)}\n >\n {children}\n </div>\n </SegmentedControlContext.Provider>\n );\n }\n);\n\n// ---------------------------------------------------------------------------\n// SegmentedControl.Segment\n// ---------------------------------------------------------------------------\n\n/**\n * Props for SegmentedControl.Segment. The helper props, the other button\n * attributes and the `ref` all land on the `<button>`.\n *\n * Some of `Button`'s props are left out on purpose. `as`, `type`, `role` and\n * `tabIndex`, since a segment is always a `<button type=\"button\" role=\"radio\">`\n * and the group decides which segment Tab reaches. `color`, which the group\n * sets for the checked segment, and the props that change the fill\n * (`isActive`, `isLight`, `isOutlined`, `isInverted`, `textColor`, `bgColor`\n * and their shades), since each would color it whether checked or not. `size`\n * and the width and shape props, which the row sets for every segment. And\n * `isStatic` and `isLoading`, a segment you couldn't pick.\n *\n * @extraProp {React.Ref<HTMLButtonElement>} [ref] - Ref forwarded to the `<button>`.\n */\nexport interface SegmentedControlSegmentProps\n extends\n Omit<\n ButtonOwnProps,\n | 'color'\n | 'size'\n | 'isActive'\n | 'isStatic'\n | 'isLoading'\n | 'isLight'\n | 'isOutlined'\n | 'isInverted'\n | 'isRounded'\n | 'isFullwidth'\n | 'isFullWidth'\n | 'textColor'\n | 'bgColor'\n | 'colorShade'\n | 'backgroundColorShade'\n >,\n Omit<\n React.ButtonHTMLAttributes<HTMLButtonElement>,\n | keyof ButtonOwnProps\n | 'type'\n | 'value'\n | 'role'\n | 'tabIndex'\n | 'aria-checked'\n | 'aria-pressed'\n > {\n /**\n * **Required.** What `onChange` reports when this segment is checked, and\n * what the group's `value` and `defaultValue` name it by. Unique within the\n * group.\n */\n value: string;\n /**\n * The label. An icon on its own needs an `aria-label`, so a screen reader\n * has something to say.\n */\n children?: React.ReactNode;\n /**\n * Disables the segment, the same as `disabled`: it can't be clicked, the\n * arrow keys, Home and End skip it, and it never holds the row's tab stop.\n * A checked one keeps its fill under Bulma's disabled look.\n */\n isDisabled?: boolean;\n /**\n * Not accepted: `aria-checked` follows the group's value. Declared because\n * JSX lets a hyphenated attribute through when the props don't name it.\n * @internal\n */\n 'aria-checked'?: never;\n /**\n * Not accepted: a segment is a radio, which says checked, not pressed.\n * @internal\n */\n 'aria-pressed'?: never;\n}\n\n/**\n * One option in the row: a `<button role=\"radio\">` that's checked while the group's value is its `value`, filled with the group's `color` and marked `is-selected`.\n *\n * @function\n * @param {SegmentedControlSegmentProps} props - Props for the segment.\n * @param {React.Ref<HTMLButtonElement>} ref - Forwarded ref to the `<button>`.\n * @returns {JSX.Element} The rendered segment.\n */\nexport const SegmentedControlSegment = forwardRef<\n HTMLButtonElement,\n SegmentedControlSegmentProps\n>(function SegmentedControlSegment(\n {\n value,\n isDisabled,\n disabled,\n className,\n onClick,\n onKeyDown,\n onFocus,\n onBlur,\n ...props\n },\n ref\n) {\n const ctx = useContext(SegmentedControlContext);\n const checked = ctx !== null && ctx.checked === value;\n const off = Boolean(isDisabled || disabled);\n\n // Prefixed like Button's own classes, which a hand-written\n // `className=\"is-selected\"` could not be under ConfigProvider classPrefix.\n // Bulma lifts `is-selected` above its neighbours in `.buttons.has-addons`.\n const selectedClass = usePrefixedClassNames({ 'is-selected': checked });\n\n const nodeRef = useRef<HTMLButtonElement | null>(null);\n const setRefs = useCallback(\n (node: HTMLButtonElement | null) => {\n nodeRef.current = node;\n if (typeof ref === 'function') ref(node);\n else if (ref) ref.current = node;\n },\n [ref]\n );\n\n const register = ctx?.register;\n useEffect(() => {\n if (!register) {\n warnOnce(\n 'SegmentedControl.Segment:outside',\n '[bestax-bulma] <SegmentedControl.Segment> is outside a ' +\n '<SegmentedControl>, so nothing can check it. Put it inside one.'\n );\n return undefined;\n }\n return register({\n value,\n disabled: off,\n node: nodeRef.current as HTMLButtonElement,\n });\n }, [register, value, off]);\n\n // The caller's onClick runs first, so a `preventDefault()` there keeps\n // the segment from being checked. A disabled button gets no click.\n const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {\n onClick?.(event);\n if (event.defaultPrevented) return;\n ctx?.select(value);\n };\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLButtonElement>) => {\n onKeyDown?.(event);\n if (\n !ctx ||\n event.defaultPrevented ||\n event.altKey ||\n event.ctrlKey ||\n event.metaKey\n ) {\n return;\n }\n const target = segmentFor(event.currentTarget, event.key);\n if (!target) return;\n event.preventDefault();\n // The row has taken this key, so nothing above it acts on it too: a\n // Carousel moves its slide on the arrows from a document listener that\n // doesn't read `defaultPrevented`. Keys the row leaves alone still bubble.\n event.stopPropagation();\n target.focus();\n // The check follows focus, APG's rule for a radio group outside a\n // toolbar. It goes through a click on the segment, as a native radio's\n // does, so its onClick sees it. Inside a toolbar the arrows would move\n // focus alone; that is the line a toolbar would change.\n target.click();\n };\n\n const handleFocus = (event: React.FocusEvent<HTMLButtonElement>) => {\n onFocus?.(event);\n ctx?.setFocused(value);\n };\n\n // The segment focus moves to, if any, records itself straight after.\n const handleBlur = (event: React.FocusEvent<HTMLButtonElement>) => {\n onBlur?.(event);\n ctx?.setFocused(undefined);\n };\n\n // The props are spread first, so what a radio depends on wins over\n // anything a JavaScript caller or a spread object passes along: it stays\n // a button, never submits, reports whether it's checked, and the group\n // decides whether Tab reaches it.\n return (\n <Button\n {...props}\n ref={setRefs}\n as=\"button\"\n type=\"button\"\n role=\"radio\"\n aria-checked={checked}\n tabIndex={ctx ? (ctx.tabStop === value ? 0 : -1) : undefined}\n isDisabled={off}\n color={checked ? ctx.color : undefined}\n className={classNames(selectedClass, className)}\n onClick={handleClick}\n onKeyDown={handleKeyDown}\n onFocus={handleFocus}\n onBlur={handleBlur}\n />\n );\n});\n\nSegmentedControlSegment.displayName = 'SegmentedControl.Segment';\n\nexport const SegmentedControl = withSubComponents(\n SegmentedControlRoot,\n { Segment: SegmentedControlSegment },\n 'SegmentedControl'\n);\n\nexport default SegmentedControl;\n"],"names":["_jsx"],"mappings":"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"}
|
|
@@ -230,5 +230,5 @@ const Sidebar = withSubComponents(SidebarComponent, {
|
|
|
230
230
|
Footer: SidebarFooter,
|
|
231
231
|
}, 'Sidebar');
|
|
232
232
|
|
|
233
|
-
export { Sidebar };
|
|
233
|
+
export { Sidebar, SidebarBody, SidebarClose, SidebarFooter, SidebarHeader, SidebarTitle };
|
|
234
234
|
//# sourceMappingURL=Sidebar.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Sidebar.js","sources":["../../../src/components/Sidebar.tsx"],"sourcesContent":["import React, { forwardRef, useEffect, useCallback, useRef } from 'react';\nimport { createPortal } from 'react-dom';\nimport { classNames, usePrefixedClassNames } from '../helpers/classNames';\nimport { useBulmaClasses, BulmaClassesProps } from '../helpers/useBulmaClasses';\nimport { withSubComponents } from '../helpers/withSubComponents';\nimport { useScrollLock } from '../helpers/scrollLock';\nimport { usePortalContainer } from '../helpers/portal';\nimport { LocalePortalScope } from '../helpers/LocaleProvider';\nimport { useConfig } from '../helpers/Config';\nimport { buttonType } from '../helpers/buttonType';\nimport { useOverlayEscape, useOverlayStack } from '../helpers/overlayStack';\nimport { trapTab } from '../helpers/useFocusTrap';\nimport { useOverlayFocus } from '../helpers/useOverlayFocus';\nimport { inertProps } from '../helpers/inertProps';\n\n/** Position of the sidebar relative to the viewport. */\nexport type SidebarPosition = 'left' | 'right';\n\n/**\n * Props for the Sidebar component.\n * @extraProp {string} [className] - Additional CSS classes.\n * @extraProp {React.Ref<HTMLElement>} [ref] - Ref forwarded to the sidebar element.\n */\nexport interface SidebarProps\n extends\n Omit<React.HTMLAttributes<HTMLElement>, 'children' | 'color'>,\n BulmaClassesProps {\n /**\n * Whether the sidebar is open (required). Opening moves focus into the\n * sidebar, to its first tab stop or the sidebar itself, except for a sidebar\n * without an overlay opened while a modal overlay, such as a Modal, is open:\n * that one leaves focus where it is. That's decided as it opens, and\n * changing `overlay` while it's open doesn't move focus. Closing hands\n * focus back to whatever had it when the sidebar opened, unless focus has\n * already left the sidebar, as it does for an overlay opened over it. A\n * closed sidebar stays in the page, so it can slide out, and is `inert` and\n * `aria-hidden` there: Tab passes over its content and assistive\n * technology doesn't read it.\n */\n isOpen: boolean;\n /** Callback when sidebar should close. */\n onClose?: () => void;\n /** Which side the sidebar appears from. Default: 'left'. */\n position?: SidebarPosition;\n /** Custom width of the sidebar. */\n width?: string;\n /** Sidebar takes full width (mobile-style). */\n isFullwidth?: boolean;\n /** Sidebar takes full width (mobile-style). @deprecated Use `isFullwidth` instead — `isFullwidth` wins if both are set. */\n fullWidth?: boolean;\n /**\n * Show an overlay behind the sidebar, which makes it modal: it gets\n * `aria-modal=\"true\"`, locks body scroll, and keeps Tab and Shift+Tab\n * inside it while it is the topmost modal overlay. A Modal or Dialog opened\n * over it, even one rendered inside it, holds Tab until it closes. Without\n * an overlay the sidebar isn't modal and holds no Tab: on the page, Tab\n * moves in and out of it freely, and opened over a Modal, it leaves the\n * Modal holding Tab, and focus, while still taking Escape first. Whether\n * the sidebar takes focus is decided as it opens: changing this while it's\n * open moves the Tab trap, not focus. Default: true.\n */\n overlay?: boolean;\n /** Close sidebar when overlay is clicked. Default: true. */\n overlayClose?: boolean;\n /**\n * Close sidebar on Escape key. Only the topmost open overlay responds,\n * whichever Modal, Dialog, Sidebar or full-page Loading opened last, so\n * Escape closes one layer at a time. The sidebar marks the Escape it closes\n * on as used with `preventDefault()`, and a Toast waits until no overlay is\n * open. Turned off, a sidebar on top still keeps Escape from the layers\n * under it. A popup inside it, such as a Dropdown's menu or a date picker's\n * calendar, takes its own Escape first, and so does content that calls\n * `preventDefault()` on it. Default: true.\n */\n escapeClose?: boolean;\n /** Allow closing the sidebar. Default: true. */\n canCancel?: boolean;\n /** Content to display in the sidebar. */\n children?: React.ReactNode;\n /** Renders inline instead of using a portal. */\n inline?: boolean;\n}\n\n/**\n * The `Sidebar` component provides a slide-out navigation panel that appears from the left or right side of the screen.\n *\n * @function\n * @param {SidebarProps} props - Props for the Sidebar component.\n * @param {React.Ref<HTMLElement>} ref - Forwarded ref to the sidebar element.\n * @returns {JSX.Element | null} The rendered sidebar component.\n *\n * @example\n * // Basic sidebar\n * const [isOpen, setIsOpen] = useState(false);\n * <Sidebar isOpen={isOpen} onClose={() => setIsOpen(false)}>\n * <Menu>...</Menu>\n * </Sidebar>\n *\n * @example\n * // Right-side sidebar with custom width\n * <Sidebar\n * isOpen={isOpen}\n * onClose={() => setIsOpen(false)}\n * position=\"right\"\n * width=\"320px\"\n * >\n * <div>Panel content</div>\n * </Sidebar>\n */\nconst SidebarComponent = forwardRef<HTMLElement, SidebarProps>(\n (\n {\n isOpen,\n onClose,\n position = 'left',\n width = '260px',\n isFullwidth,\n fullWidth,\n overlay = true,\n overlayClose = true,\n escapeClose = true,\n canCancel = true,\n children,\n inline = false,\n className,\n style,\n ...props\n },\n ref\n ) => {\n const { bulmaHelperClasses, rest } = useBulmaClasses(props);\n const resolveContainer = usePortalContainer();\n const { portalContainer } = useConfig();\n // Where the configured default puts the sidebar. An `inline` one doesn't\n // portal, so a change to the default moves nothing.\n const defaultTarget = inline ? undefined : portalContainer;\n const sidebarRef = useRef<HTMLElement>(null);\n // Cleanup returned by a consumer's callback ref on attach, held until detach.\n const consumerCleanupRef = useRef<(() => void) | null>(null);\n const resolvedFullwidth = isFullwidth ?? fullWidth ?? false;\n\n // Close handler\n const handleClose = useCallback(() => {\n if (canCancel && onClose) {\n onClose();\n }\n }, [canCancel, onClose]);\n\n // Handle overlay click\n const handleOverlayClick = useCallback(() => {\n if (overlayClose) {\n handleClose();\n }\n }, [overlayClose, handleClose]);\n\n // Joins the stack of open overlays, so the sidebar answers Escape only\n // while it's the topmost overlay, and keeps it from those under it. It's\n // modal with an overlay: without one it leaves Tab to a modal overlay\n // under it.\n const isTopmost = useOverlayStack(isOpen, overlay);\n const closesOnEscape = escapeClose && canCancel && Boolean(onClose);\n\n // Escape closes it, on `window`, as in `Modal`, so everything inside the\n // sidebar has had the key first, and an Escape it used is left to it.\n useOverlayEscape(isOpen && closesOnEscape, isTopmost, handleClose);\n\n // Keep Tab inside while there's an overlay, as `Modal` does:\n // `aria-modal` hides the rest of the page from assistive technology, so\n // the keyboard order has to agree. Only while it's the topmost modal\n // overlay, so a Modal or Dialog opened over the sidebar, even one\n // rendered inside it, holds Tab instead until it closes, and a sidebar\n // without an overlay opened over it doesn't. On `window` for the same\n // reason as Escape: a trap inside, such as a portaled popup's, has had\n // the Tab first, and one it used is left where it put focus. It's the trap\n // `useFocusTrap` sets, so a Tab the browser still takes out, as Safari\n // does past links, is sent back in. It reads the aside as each key\n // arrives, so one remounted while the sidebar is open, by a new portal\n // target or by switching `inline`, stays trapped.\n useEffect(() => {\n if (!isOpen || !overlay) return undefined;\n return trapTab(\n () => sidebarRef.current,\n window,\n () => isTopmost('modal')\n );\n }, [isOpen, overlay, isTopmost]);\n\n // Prevent body scroll when sidebar is open. Ref-counted through the shared\n // helper so an overlapping Modal/Dialog/Loading doesn't unlock underneath.\n useScrollLock(isOpen && overlay);\n\n // Move focus into the sidebar on open, to the first stop Tab wraps to or\n // the sidebar itself, and hand it back on close, as `Modal` does. A\n // different `defaultTarget` remounts the portaled sidebar: focus is handed\n // back from the old nodes and moved into the new ones. Whether it takes\n // focus is decided as it opens, so `overlay` changing while it's open\n // leaves focus where the user has it. Without an overlay the sidebar holds\n // no Tab, so with a modal overlay open under it, it leaves focus alone:\n // the first Tab would take focus back there and leave the sidebar's other\n // controls out of reach.\n useOverlayFocus(sidebarRef, isOpen, {\n modal: overlay,\n remountKey: defaultTarget,\n });\n\n // Use combined ref\n const combinedRef = useCallback(\n (node: HTMLElement | null) => {\n (sidebarRef as React.MutableRefObject<HTMLElement | null>).current =\n node;\n if (typeof ref === 'function') {\n // React 19 lets a callback ref return a cleanup function and detaches by\n // running it instead of calling the ref with `null`; React 18 discards the\n // return value entirely. Returning it from here would be a React-19-only\n // contract, so we hold the cleanup and run it ourselves on detach — the\n // same shape as `Dropdown`, `Modal`, `Dialog`, `Toast` and `Carousel`.\n // The form controls that merge a forwarded ref still discard the cleanup\n // and detach with `ref(null)`, so this is not yet library-wide — grep\n // `typeof ref === 'function'` for the current split.\n if (node === null) {\n const consumerCleanup = consumerCleanupRef.current;\n consumerCleanupRef.current = null;\n if (consumerCleanup) {\n consumerCleanup();\n } else {\n ref(null);\n }\n return;\n }\n const cleanup: unknown = ref(node);\n consumerCleanupRef.current =\n typeof cleanup === 'function' ? (cleanup as () => void) : null;\n } else if (ref) {\n (ref as React.MutableRefObject<HTMLElement | null>).current = node;\n }\n },\n [ref]\n );\n\n // Generate classes\n const sidebarClasses = usePrefixedClassNames('sidebar', {\n 'is-active': isOpen,\n [`is-${position}`]: position,\n 'is-fullwidth': resolvedFullwidth,\n });\n const backgroundClass = usePrefixedClassNames('sidebar-background', {\n 'is-active': isOpen,\n });\n const contentClass = usePrefixedClassNames('sidebar-content');\n\n const combinedClasses = classNames(\n sidebarClasses,\n bulmaHelperClasses,\n className\n );\n\n // Custom style for width\n const sidebarStyle: React.CSSProperties = {\n ...style,\n '--bulma-sidebar-width': resolvedFullwidth ? '100%' : width,\n } as React.CSSProperties;\n\n const sidebarContent = (\n <>\n {overlay && (\n <div\n className={backgroundClass}\n onClick={handleOverlayClick}\n aria-hidden=\"true\"\n />\n )}\n <aside\n ref={combinedRef}\n className={combinedClasses}\n style={sidebarStyle}\n role=\"dialog\"\n aria-modal={overlay ? 'true' : undefined}\n aria-hidden={!isOpen}\n // React commits this before the effect that moves focus in runs, so\n // an opening sidebar is no longer inert by then.\n {...inertProps(!isOpen)}\n tabIndex={-1}\n {...rest}\n >\n <div className={contentClass}>{children}</div>\n </aside>\n </>\n );\n\n if (inline) {\n return sidebarContent;\n }\n\n // Render to portal for proper stacking\n if (typeof document !== 'undefined') {\n const target = resolveContainer();\n return createPortal(\n <LocalePortalScope target={target}>{sidebarContent}</LocalePortalScope>,\n target\n );\n }\n\n return null;\n }\n);\n\n// Sub-components\n\n/**\n * Props for the SidebarHeader component.\n */\ninterface SidebarHeaderProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Header content. */\n children?: React.ReactNode;\n}\n\n/**\n * Container for the sidebar header.\n *\n * @function\n * @param {SidebarHeaderProps} props - Props for the SidebarHeader component.\n * @returns {JSX.Element} The rendered sidebar header.\n */\nconst SidebarHeader: React.FC<SidebarHeaderProps> = ({\n className,\n children,\n ...props\n}) => {\n const headerClass = usePrefixedClassNames('sidebar-header');\n return (\n <div className={classNames(headerClass, className)} {...props}>\n {children}\n </div>\n );\n};\n\n/**\n * Props for the SidebarTitle component.\n */\ninterface SidebarTitleProps extends React.HTMLAttributes<HTMLParagraphElement> {\n /** Title content. */\n children?: React.ReactNode;\n}\n\n/**\n * Title text inside the header.\n *\n * @function\n * @param {SidebarTitleProps} props - Props for the SidebarTitle component.\n * @returns {JSX.Element} The rendered sidebar title.\n */\nconst SidebarTitle: React.FC<SidebarTitleProps> = ({\n className,\n children,\n ...props\n}) => {\n const titleClass = usePrefixedClassNames('sidebar-title');\n return (\n <p className={classNames(titleClass, className)} {...props}>\n {children}\n </p>\n );\n};\n\n/**\n * Props for the SidebarClose component.\n * Extends standard button attributes.\n * @extraProp {'button' | 'submit' | 'reset'} [type='button'] - Button type. Defaults to `'button'`, so a close button inside a form does not submit it. Pass `'submit'` or `'reset'` and yours is used; any other value, or a spread carrying `type: undefined`, renders `'button'`.\n * @extraProp {string} [aria-label='Close'] - Accessible name. Pass your own to replace it. An empty one, or a spread carrying `'aria-label': undefined`, keeps the default rather than leaving the button unnamed.\n */\ntype SidebarCloseProps = React.ButtonHTMLAttributes<HTMLButtonElement>;\n\n/**\n * Close button for the sidebar.\n *\n * @function\n * @param {SidebarCloseProps} props - Props for the SidebarClose component.\n * @returns {JSX.Element} The rendered close button.\n */\nconst SidebarClose: React.FC<SidebarCloseProps> = ({\n className,\n children,\n ...props\n}) => {\n const closeClass = usePrefixedClassNames('sidebar-close');\n return (\n <button\n className={classNames(closeClass, className)}\n {...props}\n // After the spread, reading through it: a key the spread carries with no\n // value would otherwise erase the default, leaving a nameless button\n // that submits the form around it. An empty label falls back too: the\n // button is drawn as an icon, so `''` would leave it unnamed. A caller's\n // own value still wins; `buttonType` says more.\n aria-label={props['aria-label'] || 'Close'}\n type={buttonType(props.type)}\n >\n {children}\n </button>\n );\n};\n\n/**\n * Props for the SidebarBody component.\n */\ninterface SidebarBodyProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Body content. */\n children?: React.ReactNode;\n}\n\n/**\n * Main content area of the sidebar.\n *\n * @function\n * @param {SidebarBodyProps} props - Props for the SidebarBody component.\n * @returns {JSX.Element} The rendered sidebar body.\n */\nconst SidebarBody: React.FC<SidebarBodyProps> = ({\n className,\n children,\n ...props\n}) => {\n const bodyClass = usePrefixedClassNames('sidebar-body');\n return (\n <div className={classNames(bodyClass, className)} {...props}>\n {children}\n </div>\n );\n};\n\n/**\n * Props for the SidebarFooter component.\n */\ninterface SidebarFooterProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Footer content. */\n children?: React.ReactNode;\n}\n\n/**\n * Footer area of the sidebar.\n *\n * @function\n * @param {SidebarFooterProps} props - Props for the SidebarFooter component.\n * @returns {JSX.Element} The rendered sidebar footer.\n */\nconst SidebarFooter: React.FC<SidebarFooterProps> = ({\n className,\n children,\n ...props\n}) => {\n const footerClass = usePrefixedClassNames('sidebar-footer');\n return (\n <div className={classNames(footerClass, className)} {...props}>\n {children}\n </div>\n );\n};\n\n// Attach static subcomponents\nexport const Sidebar = withSubComponents(\n SidebarComponent,\n {\n Header: SidebarHeader,\n Title: SidebarTitle,\n Close: SidebarClose,\n Body: SidebarBody,\n Footer: SidebarFooter,\n },\n 'Sidebar'\n);\n\nexport default Sidebar;\n"],"names":["_jsxs","_Fragment","_jsx"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Sidebar.js","sources":["../../../src/components/Sidebar.tsx"],"sourcesContent":["import React, { forwardRef, useEffect, useCallback, useRef } from 'react';\nimport { createPortal } from 'react-dom';\nimport { classNames, usePrefixedClassNames } from '../helpers/classNames';\nimport { useBulmaClasses, BulmaClassesProps } from '../helpers/useBulmaClasses';\nimport { withSubComponents } from '../helpers/withSubComponents';\nimport { useScrollLock } from '../helpers/scrollLock';\nimport { usePortalContainer } from '../helpers/portal';\nimport { LocalePortalScope } from '../helpers/LocaleProvider';\nimport { useConfig } from '../helpers/Config';\nimport { buttonType } from '../helpers/buttonType';\nimport { useOverlayEscape, useOverlayStack } from '../helpers/overlayStack';\nimport { trapTab } from '../helpers/useFocusTrap';\nimport { useOverlayFocus } from '../helpers/useOverlayFocus';\nimport { inertProps } from '../helpers/inertProps';\n\n/** Position of the sidebar relative to the viewport. */\nexport type SidebarPosition = 'left' | 'right';\n\n/**\n * Props for the Sidebar component.\n * @extraProp {string} [className] - Additional CSS classes.\n * @extraProp {React.Ref<HTMLElement>} [ref] - Ref forwarded to the sidebar element.\n */\nexport interface SidebarProps\n extends\n Omit<React.HTMLAttributes<HTMLElement>, 'children' | 'color'>,\n BulmaClassesProps {\n /**\n * Whether the sidebar is open (required). Opening moves focus into the\n * sidebar, to its first tab stop or the sidebar itself, except for a sidebar\n * without an overlay opened while a modal overlay, such as a Modal, is open:\n * that one leaves focus where it is. That's decided as it opens, and\n * changing `overlay` while it's open doesn't move focus. Closing hands\n * focus back to whatever had it when the sidebar opened, unless focus has\n * already left the sidebar, as it does for an overlay opened over it. A\n * closed sidebar stays in the page, so it can slide out, and is `inert` and\n * `aria-hidden` there: Tab passes over its content and assistive\n * technology doesn't read it.\n */\n isOpen: boolean;\n /** Callback when sidebar should close. */\n onClose?: () => void;\n /** Which side the sidebar appears from. Default: 'left'. */\n position?: SidebarPosition;\n /** Custom width of the sidebar. */\n width?: string;\n /** Sidebar takes full width (mobile-style). */\n isFullwidth?: boolean;\n /** Sidebar takes full width (mobile-style). @deprecated Use `isFullwidth` instead — `isFullwidth` wins if both are set. */\n fullWidth?: boolean;\n /**\n * Show an overlay behind the sidebar, which makes it modal: it gets\n * `aria-modal=\"true\"`, locks body scroll, and keeps Tab and Shift+Tab\n * inside it while it is the topmost modal overlay. A Modal or Dialog opened\n * over it, even one rendered inside it, holds Tab until it closes. Without\n * an overlay the sidebar isn't modal and holds no Tab: on the page, Tab\n * moves in and out of it freely, and opened over a Modal, it leaves the\n * Modal holding Tab, and focus, while still taking Escape first. Whether\n * the sidebar takes focus is decided as it opens: changing this while it's\n * open moves the Tab trap, not focus. Default: true.\n */\n overlay?: boolean;\n /** Close sidebar when overlay is clicked. Default: true. */\n overlayClose?: boolean;\n /**\n * Close sidebar on Escape key. Only the topmost open overlay responds,\n * whichever Modal, Dialog, Sidebar or full-page Loading opened last, so\n * Escape closes one layer at a time. The sidebar marks the Escape it closes\n * on as used with `preventDefault()`, and a Toast waits until no overlay is\n * open. Turned off, a sidebar on top still keeps Escape from the layers\n * under it. A popup inside it, such as a Dropdown's menu or a date picker's\n * calendar, takes its own Escape first, and so does content that calls\n * `preventDefault()` on it. Default: true.\n */\n escapeClose?: boolean;\n /** Allow closing the sidebar. Default: true. */\n canCancel?: boolean;\n /** Content to display in the sidebar. */\n children?: React.ReactNode;\n /** Renders inline instead of using a portal. */\n inline?: boolean;\n}\n\n/**\n * The `Sidebar` component provides a slide-out navigation panel that appears from the left or right side of the screen.\n *\n * @function\n * @param {SidebarProps} props - Props for the Sidebar component.\n * @param {React.Ref<HTMLElement>} ref - Forwarded ref to the sidebar element.\n * @returns {JSX.Element | null} The rendered sidebar component.\n *\n * @example\n * // Basic sidebar\n * const [isOpen, setIsOpen] = useState(false);\n * <Sidebar isOpen={isOpen} onClose={() => setIsOpen(false)}>\n * <Menu>...</Menu>\n * </Sidebar>\n *\n * @example\n * // Right-side sidebar with custom width\n * <Sidebar\n * isOpen={isOpen}\n * onClose={() => setIsOpen(false)}\n * position=\"right\"\n * width=\"320px\"\n * >\n * <div>Panel content</div>\n * </Sidebar>\n */\nconst SidebarComponent = forwardRef<HTMLElement, SidebarProps>(\n (\n {\n isOpen,\n onClose,\n position = 'left',\n width = '260px',\n isFullwidth,\n fullWidth,\n overlay = true,\n overlayClose = true,\n escapeClose = true,\n canCancel = true,\n children,\n inline = false,\n className,\n style,\n ...props\n },\n ref\n ) => {\n const { bulmaHelperClasses, rest } = useBulmaClasses(props);\n const resolveContainer = usePortalContainer();\n const { portalContainer } = useConfig();\n // Where the configured default puts the sidebar. An `inline` one doesn't\n // portal, so a change to the default moves nothing.\n const defaultTarget = inline ? undefined : portalContainer;\n const sidebarRef = useRef<HTMLElement>(null);\n // Cleanup returned by a consumer's callback ref on attach, held until detach.\n const consumerCleanupRef = useRef<(() => void) | null>(null);\n const resolvedFullwidth = isFullwidth ?? fullWidth ?? false;\n\n // Close handler\n const handleClose = useCallback(() => {\n if (canCancel && onClose) {\n onClose();\n }\n }, [canCancel, onClose]);\n\n // Handle overlay click\n const handleOverlayClick = useCallback(() => {\n if (overlayClose) {\n handleClose();\n }\n }, [overlayClose, handleClose]);\n\n // Joins the stack of open overlays, so the sidebar answers Escape only\n // while it's the topmost overlay, and keeps it from those under it. It's\n // modal with an overlay: without one it leaves Tab to a modal overlay\n // under it.\n const isTopmost = useOverlayStack(isOpen, overlay);\n const closesOnEscape = escapeClose && canCancel && Boolean(onClose);\n\n // Escape closes it, on `window`, as in `Modal`, so everything inside the\n // sidebar has had the key first, and an Escape it used is left to it.\n useOverlayEscape(isOpen && closesOnEscape, isTopmost, handleClose);\n\n // Keep Tab inside while there's an overlay, as `Modal` does:\n // `aria-modal` hides the rest of the page from assistive technology, so\n // the keyboard order has to agree. Only while it's the topmost modal\n // overlay, so a Modal or Dialog opened over the sidebar, even one\n // rendered inside it, holds Tab instead until it closes, and a sidebar\n // without an overlay opened over it doesn't. On `window` for the same\n // reason as Escape: a trap inside, such as a portaled popup's, has had\n // the Tab first, and one it used is left where it put focus. It's the trap\n // `useFocusTrap` sets, so a Tab the browser still takes out, as Safari\n // does past links, is sent back in. It reads the aside as each key\n // arrives, so one remounted while the sidebar is open, by a new portal\n // target or by switching `inline`, stays trapped.\n useEffect(() => {\n if (!isOpen || !overlay) return undefined;\n return trapTab(\n () => sidebarRef.current,\n window,\n () => isTopmost('modal')\n );\n }, [isOpen, overlay, isTopmost]);\n\n // Prevent body scroll when sidebar is open. Ref-counted through the shared\n // helper so an overlapping Modal/Dialog/Loading doesn't unlock underneath.\n useScrollLock(isOpen && overlay);\n\n // Move focus into the sidebar on open, to the first stop Tab wraps to or\n // the sidebar itself, and hand it back on close, as `Modal` does. A\n // different `defaultTarget` remounts the portaled sidebar: focus is handed\n // back from the old nodes and moved into the new ones. Whether it takes\n // focus is decided as it opens, so `overlay` changing while it's open\n // leaves focus where the user has it. Without an overlay the sidebar holds\n // no Tab, so with a modal overlay open under it, it leaves focus alone:\n // the first Tab would take focus back there and leave the sidebar's other\n // controls out of reach.\n useOverlayFocus(sidebarRef, isOpen, {\n modal: overlay,\n remountKey: defaultTarget,\n });\n\n // Use combined ref\n const combinedRef = useCallback(\n (node: HTMLElement | null) => {\n (sidebarRef as React.MutableRefObject<HTMLElement | null>).current =\n node;\n if (typeof ref === 'function') {\n // React 19 lets a callback ref return a cleanup function and detaches by\n // running it instead of calling the ref with `null`; React 18 discards the\n // return value entirely. Returning it from here would be a React-19-only\n // contract, so we hold the cleanup and run it ourselves on detach — the\n // same shape as `Dropdown`, `Modal`, `Dialog`, `Toast` and `Carousel`.\n // The form controls that merge a forwarded ref still discard the cleanup\n // and detach with `ref(null)`, so this is not yet library-wide — grep\n // `typeof ref === 'function'` for the current split.\n if (node === null) {\n const consumerCleanup = consumerCleanupRef.current;\n consumerCleanupRef.current = null;\n if (consumerCleanup) {\n consumerCleanup();\n } else {\n ref(null);\n }\n return;\n }\n const cleanup: unknown = ref(node);\n consumerCleanupRef.current =\n typeof cleanup === 'function' ? (cleanup as () => void) : null;\n } else if (ref) {\n (ref as React.MutableRefObject<HTMLElement | null>).current = node;\n }\n },\n [ref]\n );\n\n // Generate classes\n const sidebarClasses = usePrefixedClassNames('sidebar', {\n 'is-active': isOpen,\n [`is-${position}`]: position,\n 'is-fullwidth': resolvedFullwidth,\n });\n const backgroundClass = usePrefixedClassNames('sidebar-background', {\n 'is-active': isOpen,\n });\n const contentClass = usePrefixedClassNames('sidebar-content');\n\n const combinedClasses = classNames(\n sidebarClasses,\n bulmaHelperClasses,\n className\n );\n\n // Custom style for width\n const sidebarStyle: React.CSSProperties = {\n ...style,\n '--bulma-sidebar-width': resolvedFullwidth ? '100%' : width,\n } as React.CSSProperties;\n\n const sidebarContent = (\n <>\n {overlay && (\n <div\n className={backgroundClass}\n onClick={handleOverlayClick}\n aria-hidden=\"true\"\n />\n )}\n <aside\n ref={combinedRef}\n className={combinedClasses}\n style={sidebarStyle}\n role=\"dialog\"\n aria-modal={overlay ? 'true' : undefined}\n aria-hidden={!isOpen}\n // React commits this before the effect that moves focus in runs, so\n // an opening sidebar is no longer inert by then.\n {...inertProps(!isOpen)}\n tabIndex={-1}\n {...rest}\n >\n <div className={contentClass}>{children}</div>\n </aside>\n </>\n );\n\n if (inline) {\n return sidebarContent;\n }\n\n // Render to portal for proper stacking\n if (typeof document !== 'undefined') {\n const target = resolveContainer();\n return createPortal(\n <LocalePortalScope target={target}>{sidebarContent}</LocalePortalScope>,\n target\n );\n }\n\n return null;\n }\n);\n\n// Sub-components\n\n/**\n * Props for the SidebarHeader component.\n */\nexport interface SidebarHeaderProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Header content. */\n children?: React.ReactNode;\n}\n\n/**\n * Container for the sidebar header.\n *\n * @function\n * @param {SidebarHeaderProps} props - Props for the SidebarHeader component.\n * @returns {JSX.Element} The rendered sidebar header.\n */\nexport const SidebarHeader: React.FC<SidebarHeaderProps> = ({\n className,\n children,\n ...props\n}) => {\n const headerClass = usePrefixedClassNames('sidebar-header');\n return (\n <div className={classNames(headerClass, className)} {...props}>\n {children}\n </div>\n );\n};\n\n/**\n * Props for the SidebarTitle component.\n */\nexport interface SidebarTitleProps extends React.HTMLAttributes<HTMLParagraphElement> {\n /** Title content. */\n children?: React.ReactNode;\n}\n\n/**\n * Title text inside the header.\n *\n * @function\n * @param {SidebarTitleProps} props - Props for the SidebarTitle component.\n * @returns {JSX.Element} The rendered sidebar title.\n */\nexport const SidebarTitle: React.FC<SidebarTitleProps> = ({\n className,\n children,\n ...props\n}) => {\n const titleClass = usePrefixedClassNames('sidebar-title');\n return (\n <p className={classNames(titleClass, className)} {...props}>\n {children}\n </p>\n );\n};\n\n/**\n * Props for the SidebarClose component.\n * Extends standard button attributes.\n * @extraProp {'button' | 'submit' | 'reset'} [type='button'] - Button type. Defaults to `'button'`, so a close button inside a form does not submit it. Pass `'submit'` or `'reset'` and yours is used; any other value, or a spread carrying `type: undefined`, renders `'button'`.\n * @extraProp {string} [aria-label='Close'] - Accessible name. Pass your own to replace it. An empty one, or a spread carrying `'aria-label': undefined`, keeps the default rather than leaving the button unnamed.\n */\nexport type SidebarCloseProps = React.ButtonHTMLAttributes<HTMLButtonElement>;\n\n/**\n * Close button for the sidebar.\n *\n * @function\n * @param {SidebarCloseProps} props - Props for the SidebarClose component.\n * @returns {JSX.Element} The rendered close button.\n */\nexport const SidebarClose: React.FC<SidebarCloseProps> = ({\n className,\n children,\n ...props\n}) => {\n const closeClass = usePrefixedClassNames('sidebar-close');\n return (\n <button\n className={classNames(closeClass, className)}\n {...props}\n // After the spread, reading through it: a key the spread carries with no\n // value would otherwise erase the default, leaving a nameless button\n // that submits the form around it. An empty label falls back too: the\n // button is drawn as an icon, so `''` would leave it unnamed. A caller's\n // own value still wins; `buttonType` says more.\n aria-label={props['aria-label'] || 'Close'}\n type={buttonType(props.type)}\n >\n {children}\n </button>\n );\n};\n\n/**\n * Props for the SidebarBody component.\n */\nexport interface SidebarBodyProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Body content. */\n children?: React.ReactNode;\n}\n\n/**\n * Main content area of the sidebar.\n *\n * @function\n * @param {SidebarBodyProps} props - Props for the SidebarBody component.\n * @returns {JSX.Element} The rendered sidebar body.\n */\nexport const SidebarBody: React.FC<SidebarBodyProps> = ({\n className,\n children,\n ...props\n}) => {\n const bodyClass = usePrefixedClassNames('sidebar-body');\n return (\n <div className={classNames(bodyClass, className)} {...props}>\n {children}\n </div>\n );\n};\n\n/**\n * Props for the SidebarFooter component.\n */\nexport interface SidebarFooterProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Footer content. */\n children?: React.ReactNode;\n}\n\n/**\n * Footer area of the sidebar.\n *\n * @function\n * @param {SidebarFooterProps} props - Props for the SidebarFooter component.\n * @returns {JSX.Element} The rendered sidebar footer.\n */\nexport const SidebarFooter: React.FC<SidebarFooterProps> = ({\n className,\n children,\n ...props\n}) => {\n const footerClass = usePrefixedClassNames('sidebar-footer');\n return (\n <div className={classNames(footerClass, className)} {...props}>\n {children}\n </div>\n );\n};\n\n// Attach static subcomponents\nexport const Sidebar = withSubComponents(\n SidebarComponent,\n {\n Header: SidebarHeader,\n Title: SidebarTitle,\n Close: SidebarClose,\n Body: SidebarBody,\n Footer: SidebarFooter,\n },\n 'Sidebar'\n);\n\nexport default Sidebar;\n"],"names":["_jsxs","_Fragment","_jsx"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Figure.js","sources":["../../../src/elements/Figure.tsx"],"sourcesContent":["import React from 'react';\nimport { classNames, usePrefixedClassNames } from '../helpers/classNames';\nimport { withSubComponents } from '../helpers/withSubComponents';\nimport {\n useBulmaClasses,\n BulmaClassesProps,\n validColors,\n} from '../helpers/useBulmaClasses';\n\n/**\n * Props for the FigureCaption component.\n */\nexport interface FigureCaptionProps\n extends\n React.HTMLAttributes<HTMLElement>,\n Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {\n /** Additional CSS classes to apply. */\n className?: string;\n /** Text color (Bulma color, 'inherit', or 'current'). */\n textColor?: (typeof validColors)[number] | 'inherit' | 'current';\n /** Background color (Bulma color, 'inherit', or 'current'). */\n bgColor?: (typeof validColors)[number] | 'inherit' | 'current';\n /** Content to be rendered inside the figcaption. */\n children?: React.ReactNode;\n}\n\n/**\n * FigureCaption component for rendering a styled figcaption element.\n *\n * A FigureCaption wraps the HTML `<figcaption>` element with Bulma helper class integration.\n * Use it inside Figure components to provide captions for images or other media.\n *\n * @function\n * @param {FigureCaptionProps} props - Props for the FigureCaption component.\n * @returns {JSX.Element} The rendered figcaption element.\n */\
|
|
1
|
+
{"version":3,"file":"Figure.js","sources":["../../../src/elements/Figure.tsx"],"sourcesContent":["import React from 'react';\nimport { classNames, usePrefixedClassNames } from '../helpers/classNames';\nimport { withSubComponents } from '../helpers/withSubComponents';\nimport {\n useBulmaClasses,\n BulmaClassesProps,\n validColors,\n} from '../helpers/useBulmaClasses';\n\n/**\n * Props for the FigureCaption component.\n */\nexport interface FigureCaptionProps\n extends\n React.HTMLAttributes<HTMLElement>,\n Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {\n /** Additional CSS classes to apply. */\n className?: string;\n /** Text color (Bulma color, 'inherit', or 'current'). */\n textColor?: (typeof validColors)[number] | 'inherit' | 'current';\n /** Background color (Bulma color, 'inherit', or 'current'). */\n bgColor?: (typeof validColors)[number] | 'inherit' | 'current';\n /** Content to be rendered inside the figcaption. */\n children?: React.ReactNode;\n}\n\n/**\n * FigureCaption component for rendering a styled figcaption element.\n *\n * A FigureCaption wraps the HTML `<figcaption>` element with Bulma helper class integration.\n * Use it inside Figure components to provide captions for images or other media.\n *\n * @function\n * @param {FigureCaptionProps} props - Props for the FigureCaption component.\n * @returns {JSX.Element} The rendered figcaption element.\n */\nexport const FigureCaption: React.FC<FigureCaptionProps> = ({\n className,\n textColor,\n bgColor,\n children,\n ...props\n}) => {\n const { bulmaHelperClasses, rest } = useBulmaClasses({\n color: textColor,\n backgroundColor: bgColor,\n ...props,\n } as BulmaClassesProps & typeof props);\n\n const bulmaClasses = usePrefixedClassNames();\n const captionClasses = classNames(\n bulmaClasses,\n bulmaHelperClasses,\n className\n );\n\n return (\n <figcaption className={captionClasses || undefined} {...rest}>\n {children}\n </figcaption>\n );\n};\n\n/**\n * Props for the Figure component.\n */\nexport interface FigureProps\n extends\n React.HTMLAttributes<HTMLElement>,\n Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {\n /** Additional CSS classes to apply. */\n className?: string;\n /** Text color helper. */\n textColor?: (typeof validColors)[number] | 'inherit' | 'current';\n /** Background color helper. */\n bgColor?: (typeof validColors)[number] | 'inherit' | 'current';\n /** Content to render inside the figure. */\n children?: React.ReactNode;\n}\n\n/**\n * The `Figure` component renders a styled `<figure>` element with Bulma helper class integration.\n *\n * @function\n * @param {FigureProps} props - Props for the Figure component.\n * @returns {JSX.Element} The rendered figure element.\n */\nconst FigureComponent: React.FC<FigureProps> = ({\n className,\n textColor,\n bgColor,\n children,\n ...props\n}) => {\n const { bulmaHelperClasses, rest } = useBulmaClasses({\n color: textColor,\n backgroundColor: bgColor,\n ...props,\n } as BulmaClassesProps & typeof props);\n\n const bulmaClasses = usePrefixedClassNames();\n const figureClasses = classNames(bulmaClasses, bulmaHelperClasses, className);\n\n return (\n <figure className={figureClasses || undefined} {...rest}>\n {children}\n </figure>\n );\n};\n\n/**\n * Figure component with Caption subcomponent.\n *\n * @example\n * <Figure>\n * <img src=\"image.jpg\" alt=\"Description\" />\n * <Figure.Caption>Image caption text</Figure.Caption>\n * </Figure>\n */\nexport const Figure = withSubComponents(\n FigureComponent,\n {\n Caption: FigureCaption,\n },\n 'Figure'\n);\n"],"names":["_jsx"],"mappings":";;;;;;;AA0BA;;;;;;;;;AASG;AACI,MAAM,aAAa,GAAiC,CAAC,EAC1D,SAAS,EACT,SAAS,EACT,OAAO,EACP,QAAQ,EACR,GAAG,KAAK,EACT,KAAI;AACH,IAAA,MAAM,EAAE,kBAAkB,EAAE,IAAI,EAAE,GAAG,eAAe,CAAC;AACnD,QAAA,KAAK,EAAE,SAAS;AAChB,QAAA,eAAe,EAAE,OAAO;AACxB,QAAA,GAAG,KAAK;AAC2B,KAAA,CAAC;AAEtC,IAAA,MAAM,YAAY,GAAG,qBAAqB,EAAE;IAC5C,MAAM,cAAc,GAAG,UAAU,CAC/B,YAAY,EACZ,kBAAkB,EAClB,SAAS,CACV;AAED,IAAA,QACEA,GAAA,CAAA,YAAA,EAAA,EAAY,SAAS,EAAE,cAAc,IAAI,SAAS,EAAA,GAAM,IAAI,EAAA,QAAA,EACzD,QAAQ,EAAA,CACE;AAEjB;AAmBA;;;;;;AAMG;AACH,MAAM,eAAe,GAA0B,CAAC,EAC9C,SAAS,EACT,SAAS,EACT,OAAO,EACP,QAAQ,EACR,GAAG,KAAK,EACT,KAAI;AACH,IAAA,MAAM,EAAE,kBAAkB,EAAE,IAAI,EAAE,GAAG,eAAe,CAAC;AACnD,QAAA,KAAK,EAAE,SAAS;AAChB,QAAA,eAAe,EAAE,OAAO;AACxB,QAAA,GAAG,KAAK;AAC2B,KAAA,CAAC;AAEtC,IAAA,MAAM,YAAY,GAAG,qBAAqB,EAAE;IAC5C,MAAM,aAAa,GAAG,UAAU,CAAC,YAAY,EAAE,kBAAkB,EAAE,SAAS,CAAC;AAE7E,IAAA,QACEA,GAAA,CAAA,QAAA,EAAA,EAAQ,SAAS,EAAE,aAAa,IAAI,SAAS,EAAA,GAAM,IAAI,EAAA,QAAA,EACpD,QAAQ,EAAA,CACF;AAEb,CAAC;AAED;;;;;;;;AAQG;AACI,MAAM,MAAM,GAAG,iBAAiB,CACrC,eAAe,EACf;AACE,IAAA,OAAO,EAAE,aAAa;CACvB,EACD,QAAQ;;"}
|
|
@@ -27,7 +27,7 @@ import { useFieldsetDisabled, useFieldLabelTarget } from './FormContext.js';
|
|
|
27
27
|
* @returns {JSX.Element} The rendered field.
|
|
28
28
|
*/
|
|
29
29
|
const ColorInputBase = forwardRef((props, ref) => {
|
|
30
|
-
const { value: controlledValue, defaultValue, onChange, onChangeEnd, format = 'hsl', alpha = false, swatches, eyeDropper = false, onOpen, onClose, openOnFocus = false, closeOnSwatchClick = false, position
|
|
30
|
+
const { value: controlledValue, defaultValue, onChange, onChangeEnd, format = 'hsl', alpha = false, swatches, eyeDropper = false, onOpen, onClose, openOnFocus = false, closeOnSwatchClick = false, position, appendToBody = false, color, size, isRounded, disabled: disabledProp, readOnly, placeholder, triggerIcon: triggerIconProp, triggerIconName = 'chevron-down', labels, className, id: idProp, onFocus, onBlur, onKeyDown, ...rest } = props;
|
|
31
31
|
const disabled = useFieldsetDisabled(disabledProp);
|
|
32
32
|
const id = useFieldLabelTarget(idProp, true);
|
|
33
33
|
const localeScope = usePickerLocaleScope(undefined, props.dir);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ColorInputBase.js","sources":["../../../src/form/ColorInputBase.tsx"],"sourcesContent":["import React, {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useRef,\n useState,\n} from 'react';\nimport { classNames, usePrefixedClassNames } from '../helpers/classNames';\nimport { useBulmaClasses, BulmaClassesProps } from '../helpers/useBulmaClasses';\nimport { Icon } from '../elements/Icon';\nimport { ColorSwatch } from '../elements/ColorSwatch';\nimport {\n ColorPickerAlphaSlider,\n ColorPickerArea,\n ColorPickerHueSlider,\n ColorPickerSwatches,\n type ColorFormat,\n type ColorPickerLabels,\n type ColorPickerSwatch,\n} from './ColorPicker';\nimport { formatColor, parseColor } from './_colorInternals/colorMath';\nimport {\n BLACK,\n ColorPickerContext,\n keepingChannels,\n mergeColorLabels,\n useColorPicker,\n} from './_colorInternals/pickerState';\nimport { EyeDropperButton } from './_colorInternals/EyeDropperButton';\nimport { PickerPopover } from './_pickerInternals/PickerPopover';\nimport {\n isPopoverToggleKey,\n type PickerPosition,\n} from './_pickerInternals/pickerTypes';\nimport { usePickerLocaleScope } from './_pickerInternals/useLocaleDefaults';\nimport { useControlLoading } from './controlLoading';\nimport { useFieldLabelTarget, useFieldsetDisabled } from './FormContext';\n\n/**\n * Props for the raw ColorInput base. Use the higher-level `ColorInput` for\n * Field/Control composition; `ColorInputBase` is the field and its popover\n * only.\n *\n * @helperProps Helper props go on the `.colorinput` `<div>`, with\n * `className`. The other attributes go on the text `<input>`.\n * @extraProp {string} [name] - Form field name. The field submits the text it shows, the value in `format`, or nothing when it's empty.\n * @extraProp {boolean} [required=false] - Marks the field required, so an empty one fails the form's constraint validation.\n * @extraProp {React.Ref<HTMLInputElement>} [ref] - Forwarded to the text `<input>`.\n */\nexport interface ColorInputBaseProps\n extends\n Omit<\n React.InputHTMLAttributes<HTMLInputElement>,\n | 'value'\n | 'defaultValue'\n | 'onChange'\n | 'size'\n | 'color'\n | 'type'\n | 'popover'\n >,\n Omit<BulmaClassesProps, 'color'> {\n /**\n * The color, controlled, as a CSS color the picker reads (hex, `rgb()`,\n * `hsl()` or a color name), or `''` for none.\n */\n value?: string;\n /** The color to start with, uncontrolled; empty without one. */\n defaultValue?: string;\n /**\n * Called with the color, in `format`, as it changes: on every step of a\n * drag in the panel, on a key or a swatch, and when typed text is\n * committed. Clearing the field calls it with `''`.\n */\n onChange?: (value: string) => void;\n /**\n * Called with the color, in `format`, once a change is done: when a drag\n * ends, and with `onChange` for everything else. The place to save it.\n */\n onChangeEnd?: (value: string) => void;\n /**\n * How the value is written: `'hsl'` (`hsl(171, 100%, 41%)`), `'hex'` or\n * `'rgb'`, with alpha as `hsla()`, `#rrggbbaa` and `rgba()`. Typed text in\n * any of them is written back in this one.\n * @defaultValue 'hsl'\n */\n format?: ColorFormat;\n /** Adds an alpha slider to the panel and writes the alpha into the value. */\n alpha?: boolean;\n /**\n * Preset colors in the panel: a list of CSS colors, each a string or\n * `{ color, label }`, or `\"bulma\"` for the page's theme colors.\n */\n swatches?: ColorPickerSwatch[] | 'bulma';\n /**\n * Adds a button to the panel that picks a color from anywhere on the\n * screen, where the browser has the EyeDropper API.\n */\n eyeDropper?: boolean;\n /** Fired when the panel opens. */\n onOpen?: () => void;\n /** Fired when the panel closes. */\n onClose?: () => void;\n /**\n * Opens the panel when the field takes focus, and moves focus into it.\n * Off by default, since typing a value is the quick way in and the\n * launcher, a click on the swatch or Alt+ArrowDown open it.\n */\n openOnFocus?: boolean;\n /** Closes the panel once a swatch is chosen. */\n closeOnSwatchClick?: boolean;\n /** The panel's place relative to the field. */\n position?: PickerPosition;\n /**\n * Renders the panel through a portal, into the `portalContainer` a\n * `ConfigProvider` sets, `document.body` by default.\n */\n appendToBody?: boolean;\n /** Bulma color modifier for the field. */\n color?: 'primary' | 'link' | 'info' | 'success' | 'warning' | 'danger';\n /** The field's size. */\n size?: 'small' | 'medium' | 'large';\n /** Renders the field with rounded ends. */\n isRounded?: boolean;\n /** Disables the field and its panel. A disabled `Fieldset` does too. */\n disabled?: boolean;\n /** Makes the field read-only, with no panel. */\n readOnly?: boolean;\n /** Text shown while the field is empty. */\n placeholder?: string;\n /**\n * Shows the launcher on the field's end that toggles the panel. Hidden by\n * default while a `Control` it sits in shows its `isLoading` spinner,\n * which shares that edge.\n * @defaultValue true\n */\n triggerIcon?: boolean;\n /** The launcher's glyph. */\n triggerIconName?: string;\n /** The strings it says, each with an English default. */\n labels?: ColorPickerLabels;\n}\n\n/**\n * The raw ColorInput: a text field with a swatch and a launcher that opens a color picker in a popover, without Field/Control wrapping.\n *\n * Use `ColorInput` for the convenience wrapper.\n *\n * @function\n * @param {ColorInputBaseProps} props - Props for ColorInputBase.\n * @param {React.Ref<HTMLInputElement>} ref - Forwarded ref to the text `<input>`.\n * @returns {JSX.Element} The rendered field.\n */\nexport const ColorInputBase = forwardRef<HTMLInputElement, ColorInputBaseProps>(\n (props, ref) => {\n const {\n value: controlledValue,\n defaultValue,\n onChange,\n onChangeEnd,\n format = 'hsl',\n alpha = false,\n swatches,\n eyeDropper = false,\n onOpen,\n onClose,\n openOnFocus = false,\n closeOnSwatchClick = false,\n position = 'bottom-left',\n appendToBody = false,\n color,\n size,\n isRounded,\n disabled: disabledProp,\n readOnly,\n placeholder,\n triggerIcon: triggerIconProp,\n triggerIconName = 'chevron-down',\n labels,\n className,\n id: idProp,\n onFocus,\n onBlur,\n onKeyDown,\n ...rest\n } = props;\n const disabled = useFieldsetDisabled(disabledProp);\n const id = useFieldLabelTarget(idProp, true);\n const localeScope = usePickerLocaleScope(undefined, props.dir);\n const controlLoading = useControlLoading();\n const triggerIcon = triggerIconProp ?? !controlLoading;\n const t = mergeColorLabels(labels);\n const { bulmaHelperClasses, rest: cleanRest } = useBulmaClasses(rest);\n\n const isControlled = controlledValue !== undefined;\n const [internalValue, setInternalValue] = useState(defaultValue ?? '');\n const value = isControlled ? controlledValue : internalValue;\n // What's been typed and not yet committed, or null while the field\n // shows the value.\n const [draft, setDraft] = useState<string | null>(null);\n // The last color it held, which the panel shows while the field is\n // empty, so clearing the field doesn't send the picker back to black.\n const [remembered, setRemembered] = useState(value);\n if (value && value !== remembered) setRemembered(value);\n\n const [open, setOpenState] = useState(false);\n const canOpen = !disabled && !readOnly;\n const inputRef = useRef<HTMLInputElement>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n // Focus a closing panel hands back to the field opens nothing.\n const returningRef = useRef(false);\n const reactId = useId();\n const popoverId = id ? `${id}-popover` : `colorinput-${reactId}`;\n\n const setOpen = useCallback(\n (next: boolean) => {\n if (next === open) return;\n if (!next) returningRef.current = true;\n setOpenState(next);\n if (next) onOpen?.();\n else onClose?.();\n },\n [open, onOpen, onClose]\n );\n\n // Cleared once the close has handed focus back, or hasn't.\n useEffect(() => {\n if (!open) returningRef.current = false;\n }, [open]);\n\n const setValue = (next: string, end = true) => {\n if (!isControlled) setInternalValue(next);\n if (next !== value) onChange?.(next);\n if (end) onChangeEnd?.(next);\n };\n\n // The picker in the panel, controlled by this field's value.\n const picker = useColorPicker({\n value: value || remembered || formatColor(BLACK, format, alpha),\n defaultValue: undefined,\n onChange: next => setValue(next, false),\n onChangeEnd,\n format,\n alpha,\n disabled,\n swatches,\n eyeDropper,\n labels,\n // Empty, the panel shows the last color the field held, or black, and\n // choosing that same color still fills the field.\n empty: value === '',\n });\n\n const commit = () => {\n if (draft === null) return;\n const typed = draft.trim();\n setDraft(null);\n if (typed === '') {\n if (value !== '') setValue('');\n return;\n }\n const parsed = parseColor(typed);\n if (!parsed) return;\n // A gray typed as hex, rgb() or a name keeps the panel's hue, as the\n // picker's own field does; one typed as hsl() names its own.\n const kept = keepingChannels(parsed, picker.color, typed);\n setValue(formatColor(alpha ? kept : { ...kept, a: 1 }, format, alpha));\n };\n const invalid = draft !== null && draft.trim() !== '' && !parseColor(draft);\n // The value as the field writes it: in `format`, however it was given.\n const parsedValue = parseColor(value);\n const valueText = parsedValue\n ? formatColor(\n alpha ? parsedValue : { ...parsedValue, a: 1 },\n format,\n alpha\n )\n : value;\n // What the swatch shows: a readable draft as it's typed, else the value.\n const shown = draft !== null && !invalid ? draft.trim() : value;\n\n const inputClass = usePrefixedClassNames('input', {\n [`is-${color}`]: !!color,\n [`is-${size}`]: !!size,\n 'is-rounded': !!isRounded,\n 'is-danger': invalid && !color,\n });\n const containerClass = usePrefixedClassNames('colorinput', {\n 'has-trigger': triggerIcon,\n });\n const swatchClass = usePrefixedClassNames('colorinput-swatch');\n const triggerClass = usePrefixedClassNames('colorinput-trigger');\n const panelClass = usePrefixedClassNames('color-picker');\n const eyeDropperClass = usePrefixedClassNames('colorinput-eyedropper');\n\n const combinedRef = useCallback(\n (node: HTMLInputElement | null) => {\n inputRef.current = node;\n if (typeof ref === 'function') ref(node);\n else if (ref) ref.current = node;\n },\n [ref]\n );\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {\n onKeyDown?.(event);\n if (event.defaultPrevented) return;\n if (isPopoverToggleKey(event) && event.key === 'ArrowDown') {\n event.preventDefault();\n if (canOpen) setOpen(true);\n } else if (draft !== null && event.key === 'Enter') {\n // Commits rather than submitting a form; an unreadable value stays\n // to be fixed.\n event.preventDefault();\n if (!invalid) commit();\n } else if (draft !== null && event.key === 'Escape') {\n // Puts the value back, and keeps a Modal around it open.\n event.preventDefault();\n event.stopPropagation();\n setDraft(null);\n }\n };\n\n const handleFocus = (event: React.FocusEvent<HTMLInputElement>) => {\n onFocus?.(event);\n const returning = returningRef.current;\n returningRef.current = false;\n if (openOnFocus && canOpen && !returning) setOpen(true);\n };\n\n const handleBlur = (event: React.FocusEvent<HTMLInputElement>) => {\n onBlur?.(event);\n commit();\n };\n\n const popupAttrs = {\n 'aria-haspopup': 'dialog',\n 'aria-controls': popoverId,\n 'aria-expanded': open,\n } as const;\n\n return (\n <div\n ref={containerRef}\n className={classNames(containerClass, bulmaHelperClasses, className)}\n {...localeScope.rootAttrs}\n >\n <input\n {...cleanRest}\n ref={combinedRef}\n type=\"text\"\n role=\"combobox\"\n {...popupAttrs}\n autoComplete=\"off\"\n spellCheck={false}\n autoCapitalize=\"off\"\n className={inputClass}\n value={draft ?? valueText}\n placeholder={placeholder}\n disabled={disabled}\n readOnly={readOnly}\n aria-invalid={invalid || cleanRest['aria-invalid'] || undefined}\n id={id}\n onChange={event => setDraft(event.target.value)}\n onKeyDown={handleKeyDown}\n onFocus={handleFocus}\n onBlur={handleBlur}\n />\n {/* For the pointer: the launcher does the same from the keyboard. */}\n <button\n type=\"button\"\n className={swatchClass}\n tabIndex={-1}\n aria-hidden=\"true\"\n disabled={!canOpen}\n onMouseDown={event => event.preventDefault()}\n onClick={() => setOpen(true)}\n >\n <ColorSwatch color={shown || 'transparent'} aria-hidden=\"true\" />\n </button>\n {triggerIcon && (\n <button\n type=\"button\"\n className={triggerClass}\n {...popupAttrs}\n aria-label={t.chooseColor}\n disabled={!canOpen}\n onClick={() => setOpen(!open)}\n >\n <Icon name={triggerIconName} size={size} />\n </button>\n )}\n <PickerPopover\n localeScope={localeScope.panel}\n isOpen={open}\n onClose={() => setOpen(false)}\n anchorRef={containerRef}\n position={position}\n appendToBody={appendToBody}\n ariaLabel={t.chooseColor}\n id={popoverId}\n restoreFocusRef={inputRef}\n >\n <ColorPickerContext.Provider value={picker}>\n <div className={panelClass}>\n <ColorPickerArea />\n <ColorPickerHueSlider />\n {alpha && <ColorPickerAlphaSlider />}\n {swatches && (\n <ColorPickerSwatches\n // A click on a swatch, or Enter or Space on one, closes\n // the panel when asked to; the arrows only choose.\n onClick={event => {\n const target = event.target as Element;\n if (\n closeOnSwatchClick &&\n target.closest('[role=\"radio\"]')\n ) {\n setOpen(false);\n }\n }}\n />\n )}\n <EyeDropperButton className={eyeDropperClass} />\n </div>\n </ColorPickerContext.Provider>\n </PickerPopover>\n </div>\n );\n }\n);\n\nColorInputBase.displayName = 'ColorInputBase';\n\nexport default ColorInputBase;\n"],"names":["_jsxs","_jsx"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ColorInputBase.js","sources":["../../../src/form/ColorInputBase.tsx"],"sourcesContent":["import React, {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useRef,\n useState,\n} from 'react';\nimport { classNames, usePrefixedClassNames } from '../helpers/classNames';\nimport { useBulmaClasses, BulmaClassesProps } from '../helpers/useBulmaClasses';\nimport { Icon } from '../elements/Icon';\nimport { ColorSwatch } from '../elements/ColorSwatch';\nimport {\n ColorPickerAlphaSlider,\n ColorPickerArea,\n ColorPickerHueSlider,\n ColorPickerSwatches,\n type ColorFormat,\n type ColorPickerLabels,\n type ColorPickerSwatch,\n} from './ColorPicker';\nimport { formatColor, parseColor } from './_colorInternals/colorMath';\nimport {\n BLACK,\n ColorPickerContext,\n keepingChannels,\n mergeColorLabels,\n useColorPicker,\n} from './_colorInternals/pickerState';\nimport { EyeDropperButton } from './_colorInternals/EyeDropperButton';\nimport { PickerPopover } from './_pickerInternals/PickerPopover';\nimport {\n isPopoverToggleKey,\n type PickerPosition,\n} from './_pickerInternals/pickerTypes';\nimport { usePickerLocaleScope } from './_pickerInternals/useLocaleDefaults';\nimport { useControlLoading } from './controlLoading';\nimport { useFieldLabelTarget, useFieldsetDisabled } from './FormContext';\n\n/**\n * Props for the raw ColorInput base. Use the higher-level `ColorInput` for\n * Field/Control composition; `ColorInputBase` is the field and its popover\n * only.\n *\n * @helperProps Helper props go on the `.colorinput` `<div>`, with\n * `className`. The other attributes go on the text `<input>`.\n * @extraProp {string} [name] - Form field name. The field submits the text it shows, the value in `format`, or nothing when it's empty.\n * @extraProp {boolean} [required=false] - Marks the field required, so an empty one fails the form's constraint validation.\n * @extraProp {React.Ref<HTMLInputElement>} [ref] - Forwarded to the text `<input>`.\n */\nexport interface ColorInputBaseProps\n extends\n Omit<\n React.InputHTMLAttributes<HTMLInputElement>,\n | 'value'\n | 'defaultValue'\n | 'onChange'\n | 'size'\n | 'color'\n | 'type'\n | 'popover'\n >,\n Omit<BulmaClassesProps, 'color'> {\n /**\n * The color, controlled, as a CSS color the picker reads (hex, `rgb()`,\n * `hsl()` or a color name), or `''` for none.\n */\n value?: string;\n /** The color to start with, uncontrolled; empty without one. */\n defaultValue?: string;\n /**\n * Called with the color, in `format`, as it changes: on every step of a\n * drag in the panel, on a key or a swatch, and when typed text is\n * committed. Clearing the field calls it with `''`.\n */\n onChange?: (value: string) => void;\n /**\n * Called with the color, in `format`, once a change is done: when a drag\n * ends, and with `onChange` for everything else. The place to save it.\n */\n onChangeEnd?: (value: string) => void;\n /**\n * How the value is written: `'hsl'` (`hsl(171, 100%, 41%)`), `'hex'` or\n * `'rgb'`, with alpha as `hsla()`, `#rrggbbaa` and `rgba()`. Typed text in\n * any of them is written back in this one.\n * @defaultValue 'hsl'\n */\n format?: ColorFormat;\n /** Adds an alpha slider to the panel and writes the alpha into the value. */\n alpha?: boolean;\n /**\n * Preset colors in the panel: a list of CSS colors, each a string or\n * `{ color, label }`, or `\"bulma\"` for the page's theme colors.\n */\n swatches?: ColorPickerSwatch[] | 'bulma';\n /**\n * Adds a button to the panel that picks a color from anywhere on the\n * screen, where the browser has the EyeDropper API.\n */\n eyeDropper?: boolean;\n /** Fired when the panel opens. */\n onOpen?: () => void;\n /** Fired when the panel closes. */\n onClose?: () => void;\n /**\n * Opens the panel when the field takes focus, and moves focus into it.\n * Off by default, since typing a value is the quick way in and the\n * launcher, a click on the swatch or Alt+ArrowDown open it.\n */\n openOnFocus?: boolean;\n /** Closes the panel once a swatch is chosen. */\n closeOnSwatchClick?: boolean;\n /**\n * Where the panel opens against the field. Left out, it opens below and\n * lines up with the field's start edge, which is its right edge when the\n * picker reads right to left. A corner you pass stays physical, so\n * `bottom-left` lines up the left edges either way.\n * @defaultValue 'bottom-left'\n */\n position?: PickerPosition;\n /**\n * Renders the panel through a portal, into the `portalContainer` a\n * `ConfigProvider` sets, `document.body` by default.\n */\n appendToBody?: boolean;\n /** Bulma color modifier for the field. */\n color?: 'primary' | 'link' | 'info' | 'success' | 'warning' | 'danger';\n /** The field's size. */\n size?: 'small' | 'medium' | 'large';\n /** Renders the field with rounded ends. */\n isRounded?: boolean;\n /** Disables the field and its panel. A disabled `Fieldset` does too. */\n disabled?: boolean;\n /** Makes the field read-only, with no panel. */\n readOnly?: boolean;\n /** Text shown while the field is empty. */\n placeholder?: string;\n /**\n * Shows the launcher on the field's end that toggles the panel. Hidden by\n * default while a `Control` it sits in shows its `isLoading` spinner,\n * which shares that edge.\n * @defaultValue true\n */\n triggerIcon?: boolean;\n /** The launcher's glyph. */\n triggerIconName?: string;\n /** The strings it says, each with an English default. */\n labels?: ColorPickerLabels;\n}\n\n/**\n * The raw ColorInput: a text field with a swatch and a launcher that opens a color picker in a popover, without Field/Control wrapping.\n *\n * Use `ColorInput` for the convenience wrapper.\n *\n * @function\n * @param {ColorInputBaseProps} props - Props for ColorInputBase.\n * @param {React.Ref<HTMLInputElement>} ref - Forwarded ref to the text `<input>`.\n * @returns {JSX.Element} The rendered field.\n */\nexport const ColorInputBase = forwardRef<HTMLInputElement, ColorInputBaseProps>(\n (props, ref) => {\n const {\n value: controlledValue,\n defaultValue,\n onChange,\n onChangeEnd,\n format = 'hsl',\n alpha = false,\n swatches,\n eyeDropper = false,\n onOpen,\n onClose,\n openOnFocus = false,\n closeOnSwatchClick = false,\n position,\n appendToBody = false,\n color,\n size,\n isRounded,\n disabled: disabledProp,\n readOnly,\n placeholder,\n triggerIcon: triggerIconProp,\n triggerIconName = 'chevron-down',\n labels,\n className,\n id: idProp,\n onFocus,\n onBlur,\n onKeyDown,\n ...rest\n } = props;\n const disabled = useFieldsetDisabled(disabledProp);\n const id = useFieldLabelTarget(idProp, true);\n const localeScope = usePickerLocaleScope(undefined, props.dir);\n const controlLoading = useControlLoading();\n const triggerIcon = triggerIconProp ?? !controlLoading;\n const t = mergeColorLabels(labels);\n const { bulmaHelperClasses, rest: cleanRest } = useBulmaClasses(rest);\n\n const isControlled = controlledValue !== undefined;\n const [internalValue, setInternalValue] = useState(defaultValue ?? '');\n const value = isControlled ? controlledValue : internalValue;\n // What's been typed and not yet committed, or null while the field\n // shows the value.\n const [draft, setDraft] = useState<string | null>(null);\n // The last color it held, which the panel shows while the field is\n // empty, so clearing the field doesn't send the picker back to black.\n const [remembered, setRemembered] = useState(value);\n if (value && value !== remembered) setRemembered(value);\n\n const [open, setOpenState] = useState(false);\n const canOpen = !disabled && !readOnly;\n const inputRef = useRef<HTMLInputElement>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n // Focus a closing panel hands back to the field opens nothing.\n const returningRef = useRef(false);\n const reactId = useId();\n const popoverId = id ? `${id}-popover` : `colorinput-${reactId}`;\n\n const setOpen = useCallback(\n (next: boolean) => {\n if (next === open) return;\n if (!next) returningRef.current = true;\n setOpenState(next);\n if (next) onOpen?.();\n else onClose?.();\n },\n [open, onOpen, onClose]\n );\n\n // Cleared once the close has handed focus back, or hasn't.\n useEffect(() => {\n if (!open) returningRef.current = false;\n }, [open]);\n\n const setValue = (next: string, end = true) => {\n if (!isControlled) setInternalValue(next);\n if (next !== value) onChange?.(next);\n if (end) onChangeEnd?.(next);\n };\n\n // The picker in the panel, controlled by this field's value.\n const picker = useColorPicker({\n value: value || remembered || formatColor(BLACK, format, alpha),\n defaultValue: undefined,\n onChange: next => setValue(next, false),\n onChangeEnd,\n format,\n alpha,\n disabled,\n swatches,\n eyeDropper,\n labels,\n // Empty, the panel shows the last color the field held, or black, and\n // choosing that same color still fills the field.\n empty: value === '',\n });\n\n const commit = () => {\n if (draft === null) return;\n const typed = draft.trim();\n setDraft(null);\n if (typed === '') {\n if (value !== '') setValue('');\n return;\n }\n const parsed = parseColor(typed);\n if (!parsed) return;\n // A gray typed as hex, rgb() or a name keeps the panel's hue, as the\n // picker's own field does; one typed as hsl() names its own.\n const kept = keepingChannels(parsed, picker.color, typed);\n setValue(formatColor(alpha ? kept : { ...kept, a: 1 }, format, alpha));\n };\n const invalid = draft !== null && draft.trim() !== '' && !parseColor(draft);\n // The value as the field writes it: in `format`, however it was given.\n const parsedValue = parseColor(value);\n const valueText = parsedValue\n ? formatColor(\n alpha ? parsedValue : { ...parsedValue, a: 1 },\n format,\n alpha\n )\n : value;\n // What the swatch shows: a readable draft as it's typed, else the value.\n const shown = draft !== null && !invalid ? draft.trim() : value;\n\n const inputClass = usePrefixedClassNames('input', {\n [`is-${color}`]: !!color,\n [`is-${size}`]: !!size,\n 'is-rounded': !!isRounded,\n 'is-danger': invalid && !color,\n });\n const containerClass = usePrefixedClassNames('colorinput', {\n 'has-trigger': triggerIcon,\n });\n const swatchClass = usePrefixedClassNames('colorinput-swatch');\n const triggerClass = usePrefixedClassNames('colorinput-trigger');\n const panelClass = usePrefixedClassNames('color-picker');\n const eyeDropperClass = usePrefixedClassNames('colorinput-eyedropper');\n\n const combinedRef = useCallback(\n (node: HTMLInputElement | null) => {\n inputRef.current = node;\n if (typeof ref === 'function') ref(node);\n else if (ref) ref.current = node;\n },\n [ref]\n );\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {\n onKeyDown?.(event);\n if (event.defaultPrevented) return;\n if (isPopoverToggleKey(event) && event.key === 'ArrowDown') {\n event.preventDefault();\n if (canOpen) setOpen(true);\n } else if (draft !== null && event.key === 'Enter') {\n // Commits rather than submitting a form; an unreadable value stays\n // to be fixed.\n event.preventDefault();\n if (!invalid) commit();\n } else if (draft !== null && event.key === 'Escape') {\n // Puts the value back, and keeps a Modal around it open.\n event.preventDefault();\n event.stopPropagation();\n setDraft(null);\n }\n };\n\n const handleFocus = (event: React.FocusEvent<HTMLInputElement>) => {\n onFocus?.(event);\n const returning = returningRef.current;\n returningRef.current = false;\n if (openOnFocus && canOpen && !returning) setOpen(true);\n };\n\n const handleBlur = (event: React.FocusEvent<HTMLInputElement>) => {\n onBlur?.(event);\n commit();\n };\n\n const popupAttrs = {\n 'aria-haspopup': 'dialog',\n 'aria-controls': popoverId,\n 'aria-expanded': open,\n } as const;\n\n return (\n <div\n ref={containerRef}\n className={classNames(containerClass, bulmaHelperClasses, className)}\n {...localeScope.rootAttrs}\n >\n <input\n {...cleanRest}\n ref={combinedRef}\n type=\"text\"\n role=\"combobox\"\n {...popupAttrs}\n autoComplete=\"off\"\n spellCheck={false}\n autoCapitalize=\"off\"\n className={inputClass}\n value={draft ?? valueText}\n placeholder={placeholder}\n disabled={disabled}\n readOnly={readOnly}\n aria-invalid={invalid || cleanRest['aria-invalid'] || undefined}\n id={id}\n onChange={event => setDraft(event.target.value)}\n onKeyDown={handleKeyDown}\n onFocus={handleFocus}\n onBlur={handleBlur}\n />\n {/* For the pointer: the launcher does the same from the keyboard. */}\n <button\n type=\"button\"\n className={swatchClass}\n tabIndex={-1}\n aria-hidden=\"true\"\n disabled={!canOpen}\n onMouseDown={event => event.preventDefault()}\n onClick={() => setOpen(true)}\n >\n <ColorSwatch color={shown || 'transparent'} aria-hidden=\"true\" />\n </button>\n {triggerIcon && (\n <button\n type=\"button\"\n className={triggerClass}\n {...popupAttrs}\n aria-label={t.chooseColor}\n disabled={!canOpen}\n onClick={() => setOpen(!open)}\n >\n <Icon name={triggerIconName} size={size} />\n </button>\n )}\n <PickerPopover\n localeScope={localeScope.panel}\n isOpen={open}\n onClose={() => setOpen(false)}\n anchorRef={containerRef}\n position={position}\n appendToBody={appendToBody}\n ariaLabel={t.chooseColor}\n id={popoverId}\n restoreFocusRef={inputRef}\n >\n <ColorPickerContext.Provider value={picker}>\n <div className={panelClass}>\n <ColorPickerArea />\n <ColorPickerHueSlider />\n {alpha && <ColorPickerAlphaSlider />}\n {swatches && (\n <ColorPickerSwatches\n // A click on a swatch, or Enter or Space on one, closes\n // the panel when asked to; the arrows only choose.\n onClick={event => {\n const target = event.target as Element;\n if (\n closeOnSwatchClick &&\n target.closest('[role=\"radio\"]')\n ) {\n setOpen(false);\n }\n }}\n />\n )}\n <EyeDropperButton className={eyeDropperClass} />\n </div>\n </ColorPickerContext.Provider>\n </PickerPopover>\n </div>\n );\n }\n);\n\nColorInputBase.displayName = 'ColorInputBase';\n\nexport default ColorInputBase;\n"],"names":["_jsxs","_jsx"],"mappings":"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"}
|
|
@@ -8,6 +8,7 @@ import { Control } from './Control.js';
|
|
|
8
8
|
import { DateInputBase } from './DateInputBase.js';
|
|
9
9
|
import { useInsideField, useInsideControl, rendersOwnField, rendersOwnControl } from './FormContext.js';
|
|
10
10
|
import { useAutoLabelId } from './useAutoLabelId.js';
|
|
11
|
+
import { usePickerDirAttrs } from './_pickerInternals/useLocaleDefaults.js';
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* The `DateInput` component is a form input that opens a popover calendar for date selection.
|
|
@@ -28,6 +29,8 @@ import { useAutoLabelId } from './useAutoLabelId.js';
|
|
|
28
29
|
*/
|
|
29
30
|
const DateInput = forwardRef((props, ref) => {
|
|
30
31
|
const { label, labelSize, labelProps, horizontal, iconLeft, iconRight, iconLeftName = 'calendar', iconRightName, iconLeftSize, iconRightSize, hasIconsLeft, hasIconsRight, isLoading, isExpanded, controlSize, message, messageColor, fieldClassName, controlClassName, ...baseProps } = props;
|
|
32
|
+
// The picker's own direction, which the Control below takes too.
|
|
33
|
+
const pickerDirAttrs = usePickerDirAttrs(baseProps.locale, baseProps.dir);
|
|
31
34
|
const insideField = useInsideField();
|
|
32
35
|
const insideControl = useInsideControl();
|
|
33
36
|
const ownField = rendersOwnField('DateInput', {
|
|
@@ -75,7 +78,7 @@ const DateInput = forwardRef((props, ref) => {
|
|
|
75
78
|
// whether that is the one rendered below or an enclosing one.
|
|
76
79
|
let content = (jsx(DateInputBase, { ref: ref, ...baseProps, id: controlId }));
|
|
77
80
|
if (ownControl) {
|
|
78
|
-
content = (jsx(Control, { iconLeft: iconLeft, iconRight: iconRight, iconLeftName: iconLeftName, iconRightName: iconRightName, iconLeftSize: iconLeftSize, iconRightSize: iconRightSize, hasIconsLeft: hasIconsLeft || !!iconLeftName, hasIconsRight: hasIconsRight, isLoading: isLoading, isExpanded: isExpanded, size: controlSize, className: controlClassName, children: content }));
|
|
81
|
+
content = (jsx(Control, { ...pickerDirAttrs, iconLeft: iconLeft, iconRight: iconRight, iconLeftName: iconLeftName, iconRightName: iconRightName, iconLeftSize: iconLeftSize, iconRightSize: iconRightSize, hasIconsLeft: hasIconsLeft || !!iconLeftName, hasIconsRight: hasIconsRight, isLoading: isLoading, isExpanded: isExpanded, size: controlSize, className: controlClassName, children: content }));
|
|
79
82
|
}
|
|
80
83
|
const messageEl = message ? jsx("p", { className: helpClass, children: message }) : null;
|
|
81
84
|
if (ownField) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DateInput.js","sources":["../../../src/form/DateInput.tsx"],"sourcesContent":["import React, { forwardRef } from 'react';\nimport { usePrefixedClassNames } from '../helpers/classNames';\nimport { Field, FieldProps } from './Field';\nimport { Control, ControlBaseProps } from './Control';\nimport { DateInputBase, DateInputBaseProps } from './DateInputBase';\nimport {\n useInsideField,\n useInsideControl,\n rendersOwnField,\n rendersOwnControl,\n} from './FormContext';\nimport { useAutoLabelId } from './useAutoLabelId';\n\n/**\n * Props for the DateInput convenience wrapper. Extends `DateInputBaseProps`\n * with Field-level (label, horizontal) and Control-level (icons, loading) props.\n * Inside an existing `Control` it renders no `Control` of its own, so its\n * Control-level props do nothing there and warn in development; set them on\n * that `Control` instead. In `inline` mode it renders no `Control` anywhere,\n * so they do nothing inside a `Control` or out, and warn in development\n * there too. Inside an outer `Field`, or a `Control` with no `Field` around\n * it, it renders no `Field` of its own either.\n * The exception is `label`, `message`, `horizontal` or `fieldClassName` in that\n * bare `Control`: it keeps a `Field` for them, nested in the `.control`, and\n * warns in development. Wrap the `Control` in a `Field` instead, and set the\n * `label`, `horizontal` and class name on that `Field`.\n * @extraProp {string} [name] - Form field name. The text field submits the text it displays. The native input on touch devices submits the ISO value (`YYYY-MM-DD`, or `YYYY-MM` at month granularity), and an `inline` calendar, which has no visible input, submits it from a hidden input (`YYYY-MM-DD`, `YYYY-MM` or `YYYY`).\n * @extraProp {string} [form] - Form id the input belongs to.\n * @extraProp {boolean} [required=false] - Marks the input as required.\n * @extraProp {string} [className] - Additional CSS classes for the input.\n * @extraProp {React.Ref<HTMLInputElement>} [ref] - Forwarded to the underlying `<input>`.\n */\nexport interface DateInputProps extends DateInputBaseProps {\n /** Field label (component auto-wraps in a `Field` if not already inside). Automatically associated with the input via `htmlFor` — uses your `id` when provided, otherwise a generated one. Not wired in `inline` mode (no visible input to label). Dropped inside an outer `Field`, whose own label associates instead when that `Field` generates a target id (not `grouped`/`hasAddons`, no explicit `labelProps.htmlFor`) and you set no `id` here. */\n label?: React.ReactNode;\n /** Size for the label. */\n labelSize?: FieldProps['labelSize'];\n /** Props for the label element. An explicit `htmlFor` here overrides the automatic association (no id is generated then). */\n labelProps?: FieldProps['labelProps'];\n /** Render the field with horizontal layout. */\n horizontal?: boolean;\n /**\n * Icon props for the left icon.\n * Bulma gives control icons `pointer-events: none`, so a clickable node here\n * never receives a click. Put a button beside the input in its own addon `Control` instead.\n */\n iconLeft?: ControlBaseProps['iconLeft'];\n /**\n * Icon props for the right icon.\n * Bulma gives control icons `pointer-events: none`, so a clickable node here\n * never receives a click. Put a button beside the input in its own addon `Control` instead.\n */\n iconRight?: ControlBaseProps['iconRight'];\n /** Shortcut for the right icon name. */\n iconRightName?: string;\n /** Shortcut for left icon size. */\n iconLeftSize?: ControlBaseProps['iconLeftSize'];\n /** Shortcut for right icon size. */\n iconRightSize?: ControlBaseProps['iconRightSize'];\n /** Force the left icon container. */\n hasIconsLeft?: boolean;\n /** Force the right icon container. */\n hasIconsRight?: boolean;\n /**\n * Shows a loading spinner on the `Control` it renders, and hides the\n * launcher (`triggerIcon`) while it does. Inside your own `Control` it\n * renders none, so this draws nothing and warns in development; set\n * `isLoading` on that `Control`.\n * Under `prefers-reduced-motion: reduce` the spinner stops and stays\n * drawn (with bestax's CSS loaded).\n */\n isLoading?: boolean;\n /**\n * Show a clickable launcher button on the right that toggles the popover.\n * Hidden by default while a spinner shows at the same right edge: this\n * component's `isLoading` when it renders its own `Control`, or the\n * enclosing `Control`'s `isLoading` inside one.\n * @defaultValue true\n */\n triggerIcon?: boolean;\n /** Expand the control to fill its container. */\n isExpanded?: boolean;\n /** Size of the wrapping Control. */\n controlSize?: ControlBaseProps['size'];\n /** Help/validation text below the input. */\n message?: React.ReactNode;\n /** Color modifier for the help message. */\n messageColor?: 'primary' | 'link' | 'info' | 'success' | 'warning' | 'danger';\n /** Additional CSS classes for the Field wrapper. */\n fieldClassName?: string;\n /** Additional CSS classes for the Control wrapper. */\n controlClassName?: string;\n}\n\n/**\n * The `DateInput` component is a form input that opens a popover calendar for date selection.\n *\n * @function\n * @param {DateInputProps} props - Props for the DateInput.\n * @returns {JSX.Element}\n *\n * @example\n * <DateInput label=\"Date of birth\" defaultValue={new Date(1990, 0, 1)} />\n *\n * @example\n * <DateInput\n * label=\"Booking\"\n * min={new Date()}\n * shouldDisableDate={d => d.getDay() === 0 || d.getDay() === 6}\n * />\n */\nexport const DateInput = forwardRef<HTMLInputElement, DateInputProps>(\n (props, ref) => {\n const {\n label,\n labelSize,\n labelProps,\n horizontal,\n iconLeft,\n iconRight,\n iconLeftName = 'calendar',\n iconRightName,\n iconLeftSize,\n iconRightSize,\n hasIconsLeft,\n hasIconsRight,\n isLoading,\n isExpanded,\n controlSize,\n message,\n messageColor,\n fieldClassName,\n controlClassName,\n ...baseProps\n } = props;\n const insideField = useInsideField();\n const insideControl = useInsideControl();\n const ownField = rendersOwnField('DateInput', {\n insideField,\n insideControl,\n label,\n message,\n horizontal,\n fieldClassName,\n });\n // Inline mode renders a bare calendar with no input, so the Control's\n // icon-left container has nothing to anchor to, and it renders no\n // Control inside one or out. The left icon is checked as the caller\n // passed it: the default glyph is this component's own choice, not a\n // prop the caller set, so it goes in only for the advice.\n const ownControl = rendersOwnControl('DateInput', {\n insideControl,\n inline: baseProps.inline,\n isLoading,\n iconLeft,\n iconLeftName: props.iconLeftName,\n defaultIconLeftName: iconLeftName,\n iconLeftSize,\n iconRight,\n iconRightName,\n iconRightSize,\n hasIconsLeft,\n hasIconsRight,\n isExpanded,\n controlSize,\n controlClassName,\n });\n const { controlId, fieldLabelProps } = useAutoLabelId({\n label,\n id: baseProps.id,\n labelProps,\n // Inline mode renders a bare calendar with no input to label.\n rendersLabel: ownField && !baseProps.inline,\n hasInput: !baseProps.inline,\n });\n const helpClass = usePrefixedClassNames('help', {\n [`is-${messageColor}`]: !!messageColor,\n });\n\n // The base hides its launcher while the Control it sits in is loading,\n // whether that is the one rendered below or an enclosing one.\n let content: React.ReactNode = (\n <DateInputBase ref={ref} {...baseProps} id={controlId} />\n );\n\n if (ownControl) {\n content = (\n <Control\n iconLeft={iconLeft}\n iconRight={iconRight}\n iconLeftName={iconLeftName}\n iconRightName={iconRightName}\n iconLeftSize={iconLeftSize}\n iconRightSize={iconRightSize}\n hasIconsLeft={hasIconsLeft || !!iconLeftName}\n hasIconsRight={hasIconsRight}\n isLoading={isLoading}\n isExpanded={isExpanded}\n size={controlSize}\n className={controlClassName}\n >\n {content}\n </Control>\n );\n }\n\n const messageEl = message ? <p className={helpClass}>{message}</p> : null;\n\n if (ownField) {\n return (\n <Field\n label={label}\n labelSize={labelSize}\n labelProps={fieldLabelProps}\n horizontal={horizontal}\n className={fieldClassName}\n >\n {content}\n {messageEl}\n </Field>\n );\n }\n\n return (\n <>\n {content}\n {messageEl}\n </>\n );\n }\n);\n\nDateInput.displayName = 'DateInput';\n\nexport default DateInput;\n"],"names":["_jsx","_jsxs","_Fragment"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DateInput.js","sources":["../../../src/form/DateInput.tsx"],"sourcesContent":["import React, { forwardRef } from 'react';\nimport { usePrefixedClassNames } from '../helpers/classNames';\nimport { Field, FieldProps } from './Field';\nimport { Control, ControlBaseProps } from './Control';\nimport { DateInputBase, DateInputBaseProps } from './DateInputBase';\nimport {\n useInsideField,\n useInsideControl,\n rendersOwnField,\n rendersOwnControl,\n} from './FormContext';\nimport { useAutoLabelId } from './useAutoLabelId';\nimport { usePickerDirAttrs } from './_pickerInternals/useLocaleDefaults';\n\n/**\n * Props for the DateInput convenience wrapper. Extends `DateInputBaseProps`\n * with Field-level (label, horizontal) and Control-level (icons, loading) props.\n * Inside an existing `Control` it renders no `Control` of its own, so its\n * Control-level props do nothing there and warn in development; set them on\n * that `Control` instead. In `inline` mode it renders no `Control` anywhere,\n * so they do nothing inside a `Control` or out, and warn in development\n * there too. Inside an outer `Field`, or a `Control` with no `Field` around\n * it, it renders no `Field` of its own either.\n * The exception is `label`, `message`, `horizontal` or `fieldClassName` in that\n * bare `Control`: it keeps a `Field` for them, nested in the `.control`, and\n * warns in development. Wrap the `Control` in a `Field` instead, and set the\n * `label`, `horizontal` and class name on that `Field`.\n * @extraProp {string} [name] - Form field name. The text field submits the text it displays. The native input on touch devices submits the ISO value (`YYYY-MM-DD`, or `YYYY-MM` at month granularity), and an `inline` calendar, which has no visible input, submits it from a hidden input (`YYYY-MM-DD`, `YYYY-MM` or `YYYY`).\n * @extraProp {string} [form] - Form id the input belongs to.\n * @extraProp {boolean} [required=false] - Marks the input as required.\n * @extraProp {string} [className] - Additional CSS classes for the input.\n * @extraProp {React.Ref<HTMLInputElement>} [ref] - Forwarded to the underlying `<input>`.\n */\nexport interface DateInputProps extends DateInputBaseProps {\n /** Field label (component auto-wraps in a `Field` if not already inside). Automatically associated with the input via `htmlFor` — uses your `id` when provided, otherwise a generated one. Not wired in `inline` mode (no visible input to label). Dropped inside an outer `Field`, whose own label associates instead when that `Field` generates a target id (not `grouped`/`hasAddons`, no explicit `labelProps.htmlFor`) and you set no `id` here. */\n label?: React.ReactNode;\n /** Size for the label. */\n labelSize?: FieldProps['labelSize'];\n /** Props for the label element. An explicit `htmlFor` here overrides the automatic association (no id is generated then). */\n labelProps?: FieldProps['labelProps'];\n /** Render the field with horizontal layout. */\n horizontal?: boolean;\n /**\n * Icon props for the left icon.\n * Bulma gives control icons `pointer-events: none`, so a clickable node here\n * never receives a click. Put a button beside the input in its own addon `Control` instead.\n */\n iconLeft?: ControlBaseProps['iconLeft'];\n /**\n * Icon props for the right icon.\n * Bulma gives control icons `pointer-events: none`, so a clickable node here\n * never receives a click. Put a button beside the input in its own addon `Control` instead.\n */\n iconRight?: ControlBaseProps['iconRight'];\n /** Shortcut for the right icon name. */\n iconRightName?: string;\n /** Shortcut for left icon size. */\n iconLeftSize?: ControlBaseProps['iconLeftSize'];\n /** Shortcut for right icon size. */\n iconRightSize?: ControlBaseProps['iconRightSize'];\n /** Force the left icon container. */\n hasIconsLeft?: boolean;\n /** Force the right icon container. */\n hasIconsRight?: boolean;\n /**\n * Shows a loading spinner on the `Control` it renders, and hides the\n * launcher (`triggerIcon`) while it does. Inside your own `Control` it\n * renders none, so this draws nothing and warns in development; set\n * `isLoading` on that `Control`.\n * Under `prefers-reduced-motion: reduce` the spinner stops and stays\n * drawn (with bestax's CSS loaded).\n */\n isLoading?: boolean;\n /**\n * Show a clickable launcher button on the right that toggles the popover.\n * Hidden by default while a spinner shows at the same right edge: this\n * component's `isLoading` when it renders its own `Control`, or the\n * enclosing `Control`'s `isLoading` inside one.\n * @defaultValue true\n */\n triggerIcon?: boolean;\n /** Expand the control to fill its container. */\n isExpanded?: boolean;\n /** Size of the wrapping Control. */\n controlSize?: ControlBaseProps['size'];\n /** Help/validation text below the input. */\n message?: React.ReactNode;\n /** Color modifier for the help message. */\n messageColor?: 'primary' | 'link' | 'info' | 'success' | 'warning' | 'danger';\n /** Additional CSS classes for the Field wrapper. */\n fieldClassName?: string;\n /** Additional CSS classes for the Control wrapper. */\n controlClassName?: string;\n}\n\n/**\n * The `DateInput` component is a form input that opens a popover calendar for date selection.\n *\n * @function\n * @param {DateInputProps} props - Props for the DateInput.\n * @returns {JSX.Element}\n *\n * @example\n * <DateInput label=\"Date of birth\" defaultValue={new Date(1990, 0, 1)} />\n *\n * @example\n * <DateInput\n * label=\"Booking\"\n * min={new Date()}\n * shouldDisableDate={d => d.getDay() === 0 || d.getDay() === 6}\n * />\n */\nexport const DateInput = forwardRef<HTMLInputElement, DateInputProps>(\n (props, ref) => {\n const {\n label,\n labelSize,\n labelProps,\n horizontal,\n iconLeft,\n iconRight,\n iconLeftName = 'calendar',\n iconRightName,\n iconLeftSize,\n iconRightSize,\n hasIconsLeft,\n hasIconsRight,\n isLoading,\n isExpanded,\n controlSize,\n message,\n messageColor,\n fieldClassName,\n controlClassName,\n ...baseProps\n } = props;\n // The picker's own direction, which the Control below takes too.\n const pickerDirAttrs = usePickerDirAttrs(baseProps.locale, baseProps.dir);\n const insideField = useInsideField();\n const insideControl = useInsideControl();\n const ownField = rendersOwnField('DateInput', {\n insideField,\n insideControl,\n label,\n message,\n horizontal,\n fieldClassName,\n });\n // Inline mode renders a bare calendar with no input, so the Control's\n // icon-left container has nothing to anchor to, and it renders no\n // Control inside one or out. The left icon is checked as the caller\n // passed it: the default glyph is this component's own choice, not a\n // prop the caller set, so it goes in only for the advice.\n const ownControl = rendersOwnControl('DateInput', {\n insideControl,\n inline: baseProps.inline,\n isLoading,\n iconLeft,\n iconLeftName: props.iconLeftName,\n defaultIconLeftName: iconLeftName,\n iconLeftSize,\n iconRight,\n iconRightName,\n iconRightSize,\n hasIconsLeft,\n hasIconsRight,\n isExpanded,\n controlSize,\n controlClassName,\n });\n const { controlId, fieldLabelProps } = useAutoLabelId({\n label,\n id: baseProps.id,\n labelProps,\n // Inline mode renders a bare calendar with no input to label.\n rendersLabel: ownField && !baseProps.inline,\n hasInput: !baseProps.inline,\n });\n const helpClass = usePrefixedClassNames('help', {\n [`is-${messageColor}`]: !!messageColor,\n });\n\n // The base hides its launcher while the Control it sits in is loading,\n // whether that is the one rendered below or an enclosing one.\n let content: React.ReactNode = (\n <DateInputBase ref={ref} {...baseProps} id={controlId} />\n );\n\n if (ownControl) {\n content = (\n <Control\n {...pickerDirAttrs}\n iconLeft={iconLeft}\n iconRight={iconRight}\n iconLeftName={iconLeftName}\n iconRightName={iconRightName}\n iconLeftSize={iconLeftSize}\n iconRightSize={iconRightSize}\n hasIconsLeft={hasIconsLeft || !!iconLeftName}\n hasIconsRight={hasIconsRight}\n isLoading={isLoading}\n isExpanded={isExpanded}\n size={controlSize}\n className={controlClassName}\n >\n {content}\n </Control>\n );\n }\n\n const messageEl = message ? <p className={helpClass}>{message}</p> : null;\n\n if (ownField) {\n return (\n <Field\n label={label}\n labelSize={labelSize}\n labelProps={fieldLabelProps}\n horizontal={horizontal}\n className={fieldClassName}\n >\n {content}\n {messageEl}\n </Field>\n );\n }\n\n return (\n <>\n {content}\n {messageEl}\n </>\n );\n }\n);\n\nDateInput.displayName = 'DateInput';\n\nexport default DateInput;\n"],"names":["_jsx","_jsxs","_Fragment"],"mappings":"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"}
|
|
@@ -38,7 +38,7 @@ const SKIPPED_SEGMENTS = {
|
|
|
38
38
|
* @returns {JSX.Element}
|
|
39
39
|
*/
|
|
40
40
|
const DateInputBase = forwardRef((props, ref) => {
|
|
41
|
-
const { value: controlledValue, defaultValue, onChange, onOpen, onClose, min, max, disabled: disabledProp, readOnly, placeholder, granularity = 'day', format, parse, locale: localeProp, inline = false, mobileNative = 'auto', editable = true, popover = true, openOnFocus = true, closeOnSelect = true, position
|
|
41
|
+
const { value: controlledValue, defaultValue, onChange, onOpen, onClose, min, max, disabled: disabledProp, readOnly, placeholder, granularity = 'day', format, parse, locale: localeProp, inline = false, mobileNative = 'auto', editable = true, popover = true, openOnFocus = true, closeOnSelect = true, position, appendToBody = false, color, size, isRounded, shouldDisableDate, unselectableDates, firstDayOfWeek: firstDayOfWeekProp, dayNames, monthNames, nearbyMonthDays = true, className, name, form, required, id: idProp, onFocus, onClick, onKeyDown, onBlur, iconLeftName: _iconLeftName, triggerIcon: triggerIconProp, triggerIconName = 'chevron-down', labels, ...rest } = props;
|
|
42
42
|
// A LocaleProvider around the picker sets its default locale and, through
|
|
43
43
|
// that locale, the day its week starts on.
|
|
44
44
|
const locale = usePickerLocale(localeProp);
|