@allxsmith/bestax-bulma 5.26.6 → 5.27.1
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/columns/Column.js +48 -0
- package/dist/esm/columns/Column.js.map +1 -0
- package/dist/esm/columns/Columns.js +53 -0
- package/dist/esm/columns/Columns.js.map +1 -0
- package/dist/esm/components/Avatar.js +299 -0
- package/dist/esm/components/Avatar.js.map +1 -0
- package/dist/esm/components/Avatars.js +81 -0
- package/dist/esm/components/Avatars.js.map +1 -0
- package/dist/esm/components/Badge.js +105 -0
- package/dist/esm/components/Badge.js.map +1 -0
- package/dist/esm/components/Breadcrumb.js +36 -0
- package/dist/esm/components/Breadcrumb.js.map +1 -0
- package/dist/esm/components/Card.js +252 -0
- package/dist/esm/components/Card.js.map +1 -0
- package/dist/esm/components/Carousel.js +351 -0
- package/dist/esm/components/Carousel.js.map +1 -0
- package/dist/esm/components/Collapse.js +193 -0
- package/dist/esm/components/Collapse.js.map +1 -0
- package/dist/esm/components/Collapses.js +99 -0
- package/dist/esm/components/Collapses.js.map +1 -0
- package/dist/esm/components/Dialog.js +230 -0
- package/dist/esm/components/Dialog.js.map +1 -0
- package/dist/esm/components/Dropdown.js +362 -0
- package/dist/esm/components/Dropdown.js.map +1 -0
- package/dist/esm/components/Loading.js +87 -0
- package/dist/esm/components/Loading.js.map +1 -0
- package/dist/esm/components/Menu.js +117 -0
- package/dist/esm/components/Menu.js.map +1 -0
- package/dist/esm/components/Message.js +56 -0
- package/dist/esm/components/Message.js.map +1 -0
- package/dist/esm/components/Modal.js +354 -0
- package/dist/esm/components/Modal.js.map +1 -0
- package/dist/esm/components/Navbar.js +287 -0
- package/dist/esm/components/Navbar.js.map +1 -0
- package/dist/esm/components/Pagination.js +191 -0
- package/dist/esm/components/Pagination.js.map +1 -0
- package/dist/esm/components/Panel.js +100 -0
- package/dist/esm/components/Panel.js.map +1 -0
- package/dist/esm/components/Popover.js +298 -0
- package/dist/esm/components/Popover.js.map +1 -0
- package/dist/esm/components/Reveal.js +157 -0
- package/dist/esm/components/Reveal.js.map +1 -0
- package/dist/esm/components/Sidebar.js +209 -0
- package/dist/esm/components/Sidebar.js.map +1 -0
- package/dist/esm/components/Steps.js +128 -0
- package/dist/esm/components/Steps.js.map +1 -0
- package/dist/esm/components/Tabs.js +335 -0
- package/dist/esm/components/Tabs.js.map +1 -0
- package/dist/esm/components/Toast.js +322 -0
- package/dist/esm/components/Toast.js.map +1 -0
- package/dist/esm/components/Tooltip.js +202 -0
- package/dist/esm/components/Tooltip.js.map +1 -0
- package/dist/esm/components/collapsesContext.js +12 -0
- package/dist/esm/components/collapsesContext.js.map +1 -0
- package/dist/esm/elements/Block.js +29 -0
- package/dist/esm/elements/Block.js.map +1 -0
- package/dist/esm/elements/Box.js +32 -0
- package/dist/esm/elements/Box.js.map +1 -0
- package/dist/esm/elements/Button.js +139 -0
- package/dist/esm/elements/Button.js.map +1 -0
- package/dist/esm/elements/Buttons.js +42 -0
- package/dist/esm/elements/Buttons.js.map +1 -0
- package/dist/esm/elements/Code.js +25 -0
- package/dist/esm/elements/Code.js.map +1 -0
- package/dist/esm/elements/Content.js +33 -0
- package/dist/esm/elements/Content.js.map +1 -0
- package/dist/esm/elements/Delete.js +35 -0
- package/dist/esm/elements/Delete.js.map +1 -0
- package/dist/esm/elements/Divider.js +27 -0
- package/dist/esm/elements/Divider.js.map +1 -0
- package/dist/esm/elements/Emphasis.js +25 -0
- package/dist/esm/elements/Emphasis.js.map +1 -0
- package/dist/esm/elements/Figure.js +58 -0
- package/dist/esm/elements/Figure.js.map +1 -0
- package/dist/esm/elements/Icon.js +256 -0
- package/dist/esm/elements/Icon.js.map +1 -0
- package/dist/esm/elements/IconText.js +36 -0
- package/dist/esm/elements/IconText.js.map +1 -0
- package/dist/esm/elements/Image.js +46 -0
- package/dist/esm/elements/Image.js.map +1 -0
- package/dist/esm/elements/Link.js +36 -0
- package/dist/esm/elements/Link.js.map +1 -0
- package/dist/esm/elements/LinkButton.js +37 -0
- package/dist/esm/elements/LinkButton.js.map +1 -0
- package/dist/esm/elements/ListItem.js +28 -0
- package/dist/esm/elements/ListItem.js.map +1 -0
- package/dist/esm/elements/Loader.js +37 -0
- package/dist/esm/elements/Loader.js.map +1 -0
- package/dist/esm/elements/Notification.js +316 -0
- package/dist/esm/elements/Notification.js.map +1 -0
- package/dist/esm/elements/OrderedList.js +31 -0
- package/dist/esm/elements/OrderedList.js.map +1 -0
- package/dist/esm/elements/Paragraph.js +28 -0
- package/dist/esm/elements/Paragraph.js.map +1 -0
- package/dist/esm/elements/Pre.js +25 -0
- package/dist/esm/elements/Pre.js.map +1 -0
- package/dist/esm/elements/Progress.js +33 -0
- package/dist/esm/elements/Progress.js.map +1 -0
- package/dist/esm/elements/Skeleton.js +30 -0
- package/dist/esm/elements/Skeleton.js.map +1 -0
- package/dist/esm/elements/Span.js +28 -0
- package/dist/esm/elements/Span.js.map +1 -0
- package/dist/esm/elements/Strong.js +25 -0
- package/dist/esm/elements/Strong.js.map +1 -0
- package/dist/esm/elements/SubTitle.js +51 -0
- package/dist/esm/elements/SubTitle.js.map +1 -0
- package/dist/esm/elements/Table.js +51 -0
- package/dist/esm/elements/Table.js.map +1 -0
- package/dist/esm/elements/Tag.js +49 -0
- package/dist/esm/elements/Tag.js.map +1 -0
- package/dist/esm/elements/Tags.js +34 -0
- package/dist/esm/elements/Tags.js.map +1 -0
- package/dist/esm/elements/Tbody.js +26 -0
- package/dist/esm/elements/Tbody.js.map +1 -0
- package/dist/esm/elements/Td.js +61 -0
- package/dist/esm/elements/Td.js.map +1 -0
- package/dist/esm/elements/Tfoot.js +26 -0
- package/dist/esm/elements/Tfoot.js.map +1 -0
- package/dist/esm/elements/Th.js +34 -0
- package/dist/esm/elements/Th.js.map +1 -0
- package/dist/esm/elements/Thead.js +26 -0
- package/dist/esm/elements/Thead.js.map +1 -0
- package/dist/esm/elements/Title.js +44 -0
- package/dist/esm/elements/Title.js.map +1 -0
- package/dist/esm/elements/Tr.js +31 -0
- package/dist/esm/elements/Tr.js.map +1 -0
- package/dist/esm/elements/UnorderedList.js +31 -0
- package/dist/esm/elements/UnorderedList.js.map +1 -0
- package/dist/esm/elements/iconProps.js +49 -0
- package/dist/esm/elements/iconProps.js.map +1 -0
- package/dist/esm/form/Autocomplete.js +279 -0
- package/dist/esm/form/Autocomplete.js.map +1 -0
- package/dist/esm/form/Checkbox.js +111 -0
- package/dist/esm/form/Checkbox.js.map +1 -0
- package/dist/esm/form/Checkboxes.js +89 -0
- package/dist/esm/form/Checkboxes.js.map +1 -0
- package/dist/esm/form/Control.js +79 -0
- package/dist/esm/form/Control.js.map +1 -0
- package/dist/esm/form/DateInput.js +87 -0
- package/dist/esm/form/DateInput.js.map +1 -0
- package/dist/esm/form/DateInputBase.js +263 -0
- package/dist/esm/form/DateInputBase.js.map +1 -0
- package/dist/esm/form/DateRangeInput.js +76 -0
- package/dist/esm/form/DateRangeInput.js.map +1 -0
- package/dist/esm/form/DateRangeInputBase.js +301 -0
- package/dist/esm/form/DateRangeInputBase.js.map +1 -0
- package/dist/esm/form/DateTimeInput.js +87 -0
- package/dist/esm/form/DateTimeInput.js.map +1 -0
- package/dist/esm/form/DateTimeInputBase.js +333 -0
- package/dist/esm/form/DateTimeInputBase.js.map +1 -0
- package/dist/esm/form/Field.js +153 -0
- package/dist/esm/form/Field.js.map +1 -0
- package/dist/esm/form/File.js +109 -0
- package/dist/esm/form/File.js.map +1 -0
- package/dist/esm/form/FormContext.js +170 -0
- package/dist/esm/form/FormContext.js.map +1 -0
- package/dist/esm/form/Input.js +88 -0
- package/dist/esm/form/Input.js.map +1 -0
- package/dist/esm/form/InputBase.js +40 -0
- package/dist/esm/form/InputBase.js.map +1 -0
- package/dist/esm/form/Numberinput.js +199 -0
- package/dist/esm/form/Numberinput.js.map +1 -0
- package/dist/esm/form/Radio.js +108 -0
- package/dist/esm/form/Radio.js.map +1 -0
- package/dist/esm/form/Radios.js +91 -0
- package/dist/esm/form/Radios.js.map +1 -0
- package/dist/esm/form/Rate.js +279 -0
- package/dist/esm/form/Rate.js.map +1 -0
- package/dist/esm/form/Select.js +81 -0
- package/dist/esm/form/Select.js.map +1 -0
- package/dist/esm/form/SelectBase.js +54 -0
- package/dist/esm/form/SelectBase.js.map +1 -0
- package/dist/esm/form/Slider.js +459 -0
- package/dist/esm/form/Slider.js.map +1 -0
- package/dist/esm/form/Switch.js +96 -0
- package/dist/esm/form/Switch.js.map +1 -0
- package/dist/esm/form/Taginput.js +352 -0
- package/dist/esm/form/Taginput.js.map +1 -0
- package/dist/esm/form/TextArea.js +77 -0
- package/dist/esm/form/TextArea.js.map +1 -0
- package/dist/esm/form/TextAreaBase.js +45 -0
- package/dist/esm/form/TextAreaBase.js.map +1 -0
- package/dist/esm/form/TimeInput.js +83 -0
- package/dist/esm/form/TimeInput.js.map +1 -0
- package/dist/esm/form/TimeInputBase.js +246 -0
- package/dist/esm/form/TimeInputBase.js.map +1 -0
- package/dist/esm/form/_pickerInternals/Calendar.js +760 -0
- package/dist/esm/form/_pickerInternals/Calendar.js.map +1 -0
- package/dist/esm/form/_pickerInternals/PickerPopover.js +95 -0
- package/dist/esm/form/_pickerInternals/PickerPopover.js.map +1 -0
- package/dist/esm/form/_pickerInternals/TimeWheels.js +721 -0
- package/dist/esm/form/_pickerInternals/TimeWheels.js.map +1 -0
- package/dist/esm/form/_pickerInternals/audioTick.js +109 -0
- package/dist/esm/form/_pickerInternals/audioTick.js.map +1 -0
- package/dist/esm/form/_pickerInternals/dateUtils.js +267 -0
- package/dist/esm/form/_pickerInternals/dateUtils.js.map +1 -0
- package/dist/esm/form/_pickerInternals/formatters.js +322 -0
- package/dist/esm/form/_pickerInternals/formatters.js.map +1 -0
- package/dist/esm/form/_pickerInternals/haptics.js +38 -0
- package/dist/esm/form/_pickerInternals/haptics.js.map +1 -0
- package/dist/esm/form/_pickerInternals/nativeInputSupport.js +25 -0
- package/dist/esm/form/_pickerInternals/nativeInputSupport.js.map +1 -0
- package/dist/esm/form/_pickerInternals/pickerTypes.js +50 -0
- package/dist/esm/form/_pickerInternals/pickerTypes.js.map +1 -0
- package/dist/esm/form/_pickerInternals/segmentMap.js +315 -0
- package/dist/esm/form/_pickerInternals/segmentMap.js.map +1 -0
- package/dist/esm/form/_pickerInternals/useNativeMobilePicker.js +49 -0
- package/dist/esm/form/_pickerInternals/useNativeMobilePicker.js.map +1 -0
- package/dist/esm/form/_pickerInternals/useSegmentedEntry.js +427 -0
- package/dist/esm/form/_pickerInternals/useSegmentedEntry.js.map +1 -0
- package/dist/esm/form/controlLoading.js +18 -0
- package/dist/esm/form/controlLoading.js.map +1 -0
- package/dist/esm/form/useAutoLabelId.js +54 -0
- package/dist/esm/form/useAutoLabelId.js.map +1 -0
- package/dist/esm/grid/Cell.js +35 -0
- package/dist/esm/grid/Cell.js.map +1 -0
- package/dist/esm/grid/Grid.js +51 -0
- package/dist/esm/grid/Grid.js.map +1 -0
- package/dist/esm/helpers/ClientOnly.js +33 -0
- package/dist/esm/helpers/ClientOnly.js.map +1 -0
- package/dist/esm/helpers/Config.js +60 -0
- package/dist/esm/helpers/Config.js.map +1 -0
- package/dist/esm/helpers/Theme.js +947 -0
- package/dist/esm/helpers/Theme.js.map +1 -0
- package/dist/esm/helpers/anchorAttrs.js +72 -0
- package/dist/esm/helpers/anchorAttrs.js.map +1 -0
- package/dist/esm/helpers/bulmaClassHelpers.js +415 -0
- package/dist/esm/helpers/bulmaClassHelpers.js.map +1 -0
- package/dist/esm/helpers/buttonType.js +24 -0
- package/dist/esm/helpers/buttonType.js.map +1 -0
- package/dist/esm/helpers/classNames.js +137 -0
- package/dist/esm/helpers/classNames.js.map +1 -0
- package/dist/esm/helpers/colorDeprecations.js +50 -0
- package/dist/esm/helpers/colorDeprecations.js.map +1 -0
- package/dist/esm/helpers/devWarnings.js +27 -0
- package/dist/esm/helpers/devWarnings.js.map +1 -0
- package/dist/esm/helpers/inertProps.js +27 -0
- package/dist/esm/helpers/inertProps.js.map +1 -0
- package/dist/esm/helpers/mergeBulmaStyles.js +20 -0
- package/dist/esm/helpers/mergeBulmaStyles.js.map +1 -0
- package/dist/esm/helpers/polymorphic.js +18 -0
- package/dist/esm/helpers/polymorphic.js.map +1 -0
- package/dist/esm/helpers/portal.js +76 -0
- package/dist/esm/helpers/portal.js.map +1 -0
- package/dist/esm/helpers/positionStacks.js +45 -0
- package/dist/esm/helpers/positionStacks.js.map +1 -0
- package/dist/esm/helpers/scrollLock.js +38 -0
- package/dist/esm/helpers/scrollLock.js.map +1 -0
- package/dist/esm/helpers/shadowDom.js +71 -0
- package/dist/esm/helpers/shadowDom.js.map +1 -0
- package/dist/esm/helpers/statusRegion.js +282 -0
- package/dist/esm/helpers/statusRegion.js.map +1 -0
- package/dist/esm/helpers/useAnchoredPosition.js +86 -0
- package/dist/esm/helpers/useAnchoredPosition.js.map +1 -0
- package/dist/esm/helpers/useBulmaClasses.js +158 -0
- package/dist/esm/helpers/useBulmaClasses.js.map +1 -0
- package/dist/esm/helpers/useClientLayoutEffect.js +13 -0
- package/dist/esm/helpers/useClientLayoutEffect.js.map +1 -0
- package/dist/esm/helpers/useColorClasses.js +84 -0
- package/dist/esm/helpers/useColorClasses.js.map +1 -0
- package/dist/esm/helpers/useFlexboxClasses.js +92 -0
- package/dist/esm/helpers/useFlexboxClasses.js.map +1 -0
- package/dist/esm/helpers/useFocusTrap.js +470 -0
- package/dist/esm/helpers/useFocusTrap.js.map +1 -0
- package/dist/esm/helpers/useIsHydrated.js +31 -0
- package/dist/esm/helpers/useIsHydrated.js.map +1 -0
- package/dist/esm/helpers/useOtherClasses.js +115 -0
- package/dist/esm/helpers/useOtherClasses.js.map +1 -0
- package/dist/esm/helpers/useSpacingClasses.js +82 -0
- package/dist/esm/helpers/useSpacingClasses.js.map +1 -0
- package/dist/esm/helpers/useTypographyClasses.js +93 -0
- package/dist/esm/helpers/useTypographyClasses.js.map +1 -0
- package/dist/esm/helpers/useVisibilityClasses.js +134 -0
- package/dist/esm/helpers/useVisibilityClasses.js.map +1 -0
- package/dist/esm/helpers/withSubComponents.js +24 -0
- package/dist/esm/helpers/withSubComponents.js.map +1 -0
- package/dist/esm/layout/Container.js +48 -0
- package/dist/esm/layout/Container.js.map +1 -0
- package/dist/esm/layout/Footer.js +33 -0
- package/dist/esm/layout/Footer.js.map +1 -0
- package/dist/esm/layout/Hero.js +90 -0
- package/dist/esm/layout/Hero.js.map +1 -0
- package/dist/esm/layout/Level.js +110 -0
- package/dist/esm/layout/Level.js.map +1 -0
- package/dist/esm/layout/Media.js +86 -0
- package/dist/esm/layout/Media.js.map +1 -0
- package/dist/esm/layout/Section.js +30 -0
- package/dist/esm/layout/Section.js.map +1 -0
- package/dist/extras.css +1 -1
- package/dist/extras.css.map +1 -1
- package/dist/index.cjs +821 -102
- package/dist/index.cjs.map +1 -1
- package/dist/index.esm.js +115 -16845
- package/dist/index.esm.js.map +1 -1
- package/dist/types/components/Tabs.d.ts +25 -3
- package/dist/types/elements/Icon.d.ts +10 -7
- package/dist/types/form/DateInputBase.d.ts +8 -6
- package/dist/types/form/DateRangeInput.d.ts +91 -0
- package/dist/types/form/DateRangeInputBase.d.ts +172 -0
- package/dist/types/form/DateTimeInputBase.d.ts +10 -6
- package/dist/types/form/File.d.ts +18 -5
- package/dist/types/form/FormContext.d.ts +11 -0
- package/dist/types/form/TimeInputBase.d.ts +1 -1
- package/dist/types/form/_pickerInternals/PickerPopover.d.ts +1 -0
- package/dist/types/form/_pickerInternals/dateUtils.d.ts +7 -0
- package/dist/types/form/_pickerInternals/formatters.d.ts +14 -0
- package/dist/types/form/_pickerInternals/pickerTypes.d.ts +13 -0
- package/dist/types/form/_pickerInternals/useSegmentedEntry.d.ts +12 -3
- package/dist/types/index.d.ts +4 -0
- package/dist/types-cjs/components/Tabs.d.ts +25 -3
- package/dist/types-cjs/elements/Icon.d.ts +10 -7
- package/dist/types-cjs/form/DateInputBase.d.ts +8 -6
- package/dist/types-cjs/form/DateRangeInput.d.ts +91 -0
- package/dist/types-cjs/form/DateRangeInputBase.d.ts +172 -0
- package/dist/types-cjs/form/DateTimeInputBase.d.ts +10 -6
- package/dist/types-cjs/form/File.d.ts +18 -5
- package/dist/types-cjs/form/FormContext.d.ts +11 -0
- package/dist/types-cjs/form/TimeInputBase.d.ts +1 -1
- package/dist/types-cjs/form/_pickerInternals/PickerPopover.d.ts +1 -0
- package/dist/types-cjs/form/_pickerInternals/dateUtils.d.ts +7 -0
- package/dist/types-cjs/form/_pickerInternals/formatters.d.ts +14 -0
- package/dist/types-cjs/form/_pickerInternals/pickerTypes.d.ts +13 -0
- package/dist/types-cjs/form/_pickerInternals/useSegmentedEntry.d.ts +12 -3
- package/dist/types-cjs/index.d.ts +4 -0
- package/dist/versions/bestax-no-dark-mode.css +1 -1
- package/dist/versions/bestax-no-dark-mode.css.map +1 -1
- package/dist/versions/bestax-no-helpers-prefixed.css +1 -1
- package/dist/versions/bestax-no-helpers-prefixed.css.map +1 -1
- package/dist/versions/bestax-no-helpers.css +1 -1
- package/dist/versions/bestax-no-helpers.css.map +1 -1
- package/dist/versions/bestax-prefixed.css +1 -1
- package/dist/versions/bestax-prefixed.css.map +1 -1
- package/package.json +1 -1
- package/src/scss/components/_tabs.scss +44 -0
- package/src/scss/form/_dateinput.scss +87 -8
- package/src/scss/form/_daterangeinput.scss +63 -0
- package/src/scss/form/_datetimeinput.scss +7 -2
- package/src/scss/form/_file.scss +37 -0
- package/src/scss/form/_index.scss +2 -0
- package/src/scss/form/_picker-popover.scss +1 -4
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/* @allxsmith/bestax-bulma — AI agents: see AGENTS.md in the package root, or https://bestax.io/llms.txt */
|
|
2
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
3
|
+
import { forwardRef, useState, useEffect } from 'react';
|
|
4
|
+
import { prefixedClassNames, usePrefixedClassNames, classNames } from '../helpers/classNames.js';
|
|
5
|
+
import { useBulmaClasses } from '../helpers/useBulmaClasses.js';
|
|
6
|
+
import { useConfig } from '../helpers/Config.js';
|
|
7
|
+
import { useInsideField, useInsideControl, rendersOwnField } from './FormContext.js';
|
|
8
|
+
import { Field } from './Field.js';
|
|
9
|
+
import { useAutoLabelId } from './useAutoLabelId.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The `File` component provides a Bulma-styled file input, supporting color, size, boxed/fullwidth/align styles, icons, "has name", and filename display.
|
|
13
|
+
*
|
|
14
|
+
* @function
|
|
15
|
+
* @param {FileProps} props - Props for the File component.
|
|
16
|
+
* @returns {JSX.Element} The rendered file upload field.
|
|
17
|
+
* @see {@link https://bulma.io/documentation/form/file/ | Bulma File documentation}
|
|
18
|
+
*/
|
|
19
|
+
const File = forwardRef(({
|
|
20
|
+
// Field props
|
|
21
|
+
label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName, color, size, isBoxed, isFullwidth, isFullWidth, isRight, isCentered, hasName, buttonLabel, iconLeft, iconRight, className, inputClassName, fileName, pickedFilesLabel, onChange, ...props }, ref) => {
|
|
22
|
+
// What the user last picked, and the form a reset of which clears it.
|
|
23
|
+
const [picked, setPicked] = useState();
|
|
24
|
+
const pickedForm = picked?.form;
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
if (!pickedForm)
|
|
27
|
+
return;
|
|
28
|
+
let timer;
|
|
29
|
+
// The reset event fires before the form resets, and a listener after this one can still
|
|
30
|
+
// cancel it, so the name is cleared a task later, and only if the reset went ahead.
|
|
31
|
+
const onReset = (event) => {
|
|
32
|
+
timer = setTimeout(() => {
|
|
33
|
+
if (!event.defaultPrevented)
|
|
34
|
+
setPicked(undefined);
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
pickedForm.addEventListener('reset', onReset);
|
|
38
|
+
return () => {
|
|
39
|
+
pickedForm.removeEventListener('reset', onReset);
|
|
40
|
+
clearTimeout(timer);
|
|
41
|
+
};
|
|
42
|
+
}, [pickedForm]);
|
|
43
|
+
const handleChange = (event) => {
|
|
44
|
+
const { files, form } = event.target;
|
|
45
|
+
setPicked(files?.length
|
|
46
|
+
? { count: files.length, firstName: files[0].name, form }
|
|
47
|
+
: undefined);
|
|
48
|
+
onChange?.(event);
|
|
49
|
+
};
|
|
50
|
+
const pickedText = picked &&
|
|
51
|
+
(picked.count === 1
|
|
52
|
+
? picked.firstName
|
|
53
|
+
: (pickedFilesLabel?.(picked.count) ?? `${picked.count} files`));
|
|
54
|
+
const shownName = fileName ?? pickedText;
|
|
55
|
+
const insideField = useInsideField();
|
|
56
|
+
const insideControl = useInsideControl();
|
|
57
|
+
const ownField = rendersOwnField('File', {
|
|
58
|
+
insideField,
|
|
59
|
+
insideControl,
|
|
60
|
+
label,
|
|
61
|
+
message,
|
|
62
|
+
horizontal,
|
|
63
|
+
fieldClassName,
|
|
64
|
+
});
|
|
65
|
+
const { controlId, fieldLabelProps } = useAutoLabelId({
|
|
66
|
+
label,
|
|
67
|
+
id: props.id,
|
|
68
|
+
labelProps,
|
|
69
|
+
rendersLabel: ownField,
|
|
70
|
+
});
|
|
71
|
+
const { classPrefix } = useConfig();
|
|
72
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
73
|
+
color,
|
|
74
|
+
...props,
|
|
75
|
+
});
|
|
76
|
+
// Mutually exclusive alignment
|
|
77
|
+
let alignmentClass;
|
|
78
|
+
if (isRight && isCentered) {
|
|
79
|
+
// If both are set, prefer isRight and warn in dev
|
|
80
|
+
alignmentClass = prefixedClassNames(classPrefix, 'is-right');
|
|
81
|
+
}
|
|
82
|
+
else if (isRight) {
|
|
83
|
+
alignmentClass = prefixedClassNames(classPrefix, 'is-right');
|
|
84
|
+
}
|
|
85
|
+
else if (isCentered) {
|
|
86
|
+
alignmentClass = prefixedClassNames(classPrefix, 'is-centered');
|
|
87
|
+
}
|
|
88
|
+
const mainClass = usePrefixedClassNames('file', {
|
|
89
|
+
[`is-${color}`]: !!color,
|
|
90
|
+
[`is-${size}`]: !!size,
|
|
91
|
+
'is-boxed': isBoxed,
|
|
92
|
+
'is-fullwidth': isFullwidth ?? isFullWidth,
|
|
93
|
+
'has-name': hasName,
|
|
94
|
+
});
|
|
95
|
+
const fileClass = classNames(mainClass, bulmaHelperClasses, alignmentClass, className);
|
|
96
|
+
const helpClass = usePrefixedClassNames('help', {
|
|
97
|
+
[`is-${messageColor}`]: !!messageColor,
|
|
98
|
+
});
|
|
99
|
+
const messageEl = message ? jsx("p", { className: helpClass, children: message }) : null;
|
|
100
|
+
const fileElement = (jsx("div", { className: fileClass, children: jsxs("label", { className: usePrefixedClassNames('file-label'), children: [jsx("input", { ref: ref, className: classNames(usePrefixedClassNames('file-input'), inputClassName), type: "file", id: controlId, onChange: handleChange, ...rest }), jsxs("span", { className: usePrefixedClassNames('file-cta'), children: [iconLeft && (jsx("span", { className: prefixedClassNames(classPrefix, 'file-icon'), children: iconLeft })), jsx("span", { className: usePrefixedClassNames('file-label'), children: buttonLabel || 'Choose a file\u2026' }), iconRight && (jsx("span", { className: prefixedClassNames(classPrefix, 'file-icon'), children: iconRight }))] }), hasName && shownName && (jsx("span", { className: prefixedClassNames(classPrefix, 'file-name'), children: shownName }))] }) }));
|
|
101
|
+
if (ownField) {
|
|
102
|
+
return (jsxs(Field, { label: label, labelSize: labelSize, labelProps: fieldLabelProps, horizontal: horizontal, className: fieldClassName, children: [fileElement, messageEl] }));
|
|
103
|
+
}
|
|
104
|
+
return (jsxs(Fragment, { children: [fileElement, messageEl] }));
|
|
105
|
+
});
|
|
106
|
+
File.displayName = 'File';
|
|
107
|
+
|
|
108
|
+
export { File };
|
|
109
|
+
//# sourceMappingURL=File.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"File.js","sources":["../../../src/form/File.tsx"],"sourcesContent":["import React, { forwardRef, useEffect, useState } from 'react';\nimport {\n classNames,\n usePrefixedClassNames,\n prefixedClassNames,\n} from '../helpers/classNames';\nimport { useBulmaClasses, BulmaClassesProps } from '../helpers/useBulmaClasses';\nimport { useConfig } from '../helpers/Config';\nimport {\n useInsideField,\n useInsideControl,\n rendersOwnField,\n} from './FormContext';\nimport { Field } from './Field';\nimport { FormFieldProps } from './fieldProps';\nimport { useAutoLabelId } from './useAutoLabelId';\n\n/**\n * Props for the File component.\n * Inside an outer `Field`, or a `Control` with no `Field` around it, it\n * renders no `Field` of its own. The exception is `label`, `message`,\n * `horizontal` or `fieldClassName` in that bare `Control`: it keeps a `Field`\n * for them, nested in the `.control`, and warns in development. Wrap the\n * `Control` in a `Field` instead, and set the `label`, `horizontal` and class\n * name on that `Field`.\n */\nexport interface FileProps\n extends\n Omit<\n React.InputHTMLAttributes<HTMLInputElement>,\n 'size' | 'color' | 'type'\n >,\n Omit<BulmaClassesProps, 'color'>,\n FormFieldProps {\n /** Field label. Automatically associated with the file input via `htmlFor` — uses your `id` when provided, otherwise a generated one. The input then has two labels (this one plus the wrapping `file-label`); assistive tech reads both. Dropped inside an outer `Field` (label that `Field` yourself). */\n label?: React.ReactNode;\n /** Props for the label element. An explicit `htmlFor` here overrides the automatic association (no id is generated then). */\n labelProps?: React.LabelHTMLAttributes<HTMLLabelElement> & {\n [key: string]: unknown;\n };\n /** Bulma color modifier for the file input. */\n color?:\n | 'primary'\n | 'link'\n | 'info'\n | 'success'\n | 'warning'\n | 'danger'\n | 'black'\n | 'dark'\n | 'light'\n | 'white';\n /** Size modifier for the file input. */\n size?: 'small' | 'medium' | 'large';\n /** Boxed file input. */\n isBoxed?: boolean;\n /** Whether the file input expands to full width. */\n isFullwidth?: boolean;\n /** Whether the file input expands to full width. @deprecated Use `isFullwidth` instead — `isFullwidth` wins if both are set. */\n isFullWidth?: boolean;\n /** Position the CTA on the right (with `hasName`). */\n isRight?: boolean;\n /** Center the file input within its container. */\n isCentered?: boolean;\n /**\n * Show a file name area. Without `fileName` it shows what the user picked: the file's name, or\n * a count when `multiple` lets them pick several (see `pickedFilesLabel`). Before a pick there is\n * no name area. A reset of the input's form clears the name, but clearing the input from code\n * fires no change event and leaves it showing, so pass `fileName` to control the text then.\n */\n hasName?: boolean;\n /** Text on the file CTA button (defaults to \"Choose a file…\"). */\n buttonLabel?: React.ReactNode;\n /**\n * Left icon element. It renders inside the `<label>` that names the file input, beside\n * `buttonLabel`, so leave an `Icon` here unnamed (`<Icon name=\"upload\" />`): it is then\n * `aria-hidden`, while an `ariaLabel` would join the input's accessible name.\n */\n iconLeft?: React.ReactNode;\n /**\n * Right icon element. Sits inside the same `<label>` as `iconLeft`, so the same applies:\n * an `Icon` here with an `ariaLabel` joins the input's accessible name.\n */\n iconRight?: React.ReactNode;\n /** Additional CSS classes to apply. */\n className?: string;\n /** Additional CSS classes for the `<input>`. */\n inputClassName?: string;\n /**\n * Text for the file name area (with `hasName`), for a file chosen earlier or your own wording.\n * Setting it takes over from the picked file's name; an empty string shows no name area.\n */\n fileName?: string;\n /**\n * Builds the text `hasName` shows when several files are picked, from their count, for\n * localization. Default: `` `${count} files` ``. A single pick shows its file's name.\n */\n pickedFilesLabel?: (count: number) => string;\n}\n\n/**\n * The `File` component provides a Bulma-styled file input, supporting color, size, boxed/fullwidth/align styles, icons, \"has name\", and filename display.\n *\n * @function\n * @param {FileProps} props - Props for the File component.\n * @returns {JSX.Element} The rendered file upload field.\n * @see {@link https://bulma.io/documentation/form/file/ | Bulma File documentation}\n */\nexport const File = forwardRef<HTMLInputElement, FileProps>(\n (\n {\n // Field props\n label,\n labelSize,\n labelProps,\n horizontal,\n message,\n messageColor,\n fieldClassName,\n color,\n size,\n isBoxed,\n isFullwidth,\n isFullWidth,\n isRight,\n isCentered,\n hasName,\n buttonLabel,\n iconLeft,\n iconRight,\n className,\n inputClassName,\n fileName,\n pickedFilesLabel,\n onChange,\n ...props\n },\n ref\n ) => {\n // What the user last picked, and the form a reset of which clears it.\n const [picked, setPicked] = useState<{\n count: number;\n firstName: string;\n form: HTMLFormElement | null;\n }>();\n const pickedForm = picked?.form;\n useEffect(() => {\n if (!pickedForm) return;\n let timer: ReturnType<typeof setTimeout> | undefined;\n // The reset event fires before the form resets, and a listener after this one can still\n // cancel it, so the name is cleared a task later, and only if the reset went ahead.\n const onReset = (event: Event) => {\n timer = setTimeout(() => {\n if (!event.defaultPrevented) setPicked(undefined);\n });\n };\n pickedForm.addEventListener('reset', onReset);\n return () => {\n pickedForm.removeEventListener('reset', onReset);\n clearTimeout(timer);\n };\n }, [pickedForm]);\n const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {\n const { files, form } = event.target;\n setPicked(\n files?.length\n ? { count: files.length, firstName: files[0].name, form }\n : undefined\n );\n onChange?.(event);\n };\n const pickedText =\n picked &&\n (picked.count === 1\n ? picked.firstName\n : (pickedFilesLabel?.(picked.count) ?? `${picked.count} files`));\n const shownName = fileName ?? pickedText;\n\n const insideField = useInsideField();\n const insideControl = useInsideControl();\n const ownField = rendersOwnField('File', {\n insideField,\n insideControl,\n label,\n message,\n horizontal,\n fieldClassName,\n });\n const { controlId, fieldLabelProps } = useAutoLabelId({\n label,\n id: props.id,\n labelProps,\n rendersLabel: ownField,\n });\n const { classPrefix } = useConfig();\n const { bulmaHelperClasses, rest } = useBulmaClasses({\n color,\n ...props,\n });\n\n // Mutually exclusive alignment\n let alignmentClass: string | undefined;\n if (isRight && isCentered) {\n // If both are set, prefer isRight and warn in dev\n alignmentClass = prefixedClassNames(classPrefix, 'is-right');\n } else if (isRight) {\n alignmentClass = prefixedClassNames(classPrefix, 'is-right');\n } else if (isCentered) {\n alignmentClass = prefixedClassNames(classPrefix, 'is-centered');\n }\n\n const mainClass = usePrefixedClassNames('file', {\n [`is-${color}`]: !!color,\n [`is-${size}`]: !!size,\n 'is-boxed': isBoxed,\n 'is-fullwidth': isFullwidth ?? isFullWidth,\n 'has-name': hasName,\n });\n const fileClass = classNames(\n mainClass,\n bulmaHelperClasses,\n alignmentClass,\n className\n );\n\n const helpClass = usePrefixedClassNames('help', {\n [`is-${messageColor}`]: !!messageColor,\n });\n const messageEl = message ? <p className={helpClass}>{message}</p> : null;\n\n const fileElement = (\n <div className={fileClass}>\n <label className={usePrefixedClassNames('file-label')}>\n <input\n ref={ref}\n className={classNames(\n usePrefixedClassNames('file-input'),\n inputClassName\n )}\n type=\"file\"\n id={controlId}\n onChange={handleChange}\n {...rest}\n />\n <span className={usePrefixedClassNames('file-cta')}>\n {iconLeft && (\n <span className={prefixedClassNames(classPrefix, 'file-icon')}>\n {iconLeft}\n </span>\n )}\n <span className={usePrefixedClassNames('file-label')}>\n {buttonLabel || 'Choose a file\\u2026'}\n </span>\n {iconRight && (\n <span className={prefixedClassNames(classPrefix, 'file-icon')}>\n {iconRight}\n </span>\n )}\n </span>\n {hasName && shownName && (\n <span className={prefixedClassNames(classPrefix, 'file-name')}>\n {shownName}\n </span>\n )}\n </label>\n </div>\n );\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 {fileElement}\n {messageEl}\n </Field>\n );\n }\n\n return (\n <>\n {fileElement}\n {messageEl}\n </>\n );\n }\n);\n\nFile.displayName = 'File';\n\nexport default File;\n"],"names":["_jsx","_jsxs","_Fragment"],"mappings":"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"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/* @allxsmith/bestax-bulma — AI agents: see AGENTS.md in the package root, or https://bestax.io/llms.txt */
|
|
2
|
+
import { createContext, useContext } from 'react';
|
|
3
|
+
import { warnOnce } from '../helpers/devWarnings.js';
|
|
4
|
+
|
|
5
|
+
const FieldContext = createContext(false);
|
|
6
|
+
const ControlContext = createContext(false);
|
|
7
|
+
/**
|
|
8
|
+
* Hook to detect if the component is inside a Field wrapper.
|
|
9
|
+
* Form components use this to skip rendering their own Field.
|
|
10
|
+
*/
|
|
11
|
+
const useInsideField = () => useContext(FieldContext);
|
|
12
|
+
/**
|
|
13
|
+
* Hook to detect if the component is inside a Control wrapper.
|
|
14
|
+
* Form components use this to skip rendering their own Control.
|
|
15
|
+
*/
|
|
16
|
+
const useInsideControl = () => useContext(ControlContext);
|
|
17
|
+
/** Provider for Field context — used internally by Field component. */
|
|
18
|
+
const FieldProvider = FieldContext.Provider;
|
|
19
|
+
/** Provider for Control context — used internally by Control component. */
|
|
20
|
+
const ControlProvider = ControlContext.Provider;
|
|
21
|
+
// Each Field-shaping prop, with what takes its place on a `Field` the caller
|
|
22
|
+
// wraps around the `Control`. `message` needs nothing there, because the
|
|
23
|
+
// wrapper still renders it inside `Field > Control`. The other Field-level
|
|
24
|
+
// props (`labelSize`, `labelProps`, `messageColor`) change nothing unless a
|
|
25
|
+
// `label` or `message` renders, so on their own they keep no `Field`.
|
|
26
|
+
// `bare-control.test.tsx` holds this list to what each wrapper hands its
|
|
27
|
+
// `Field` and to which props change its markup, so a new Field-level prop
|
|
28
|
+
// fails there until it is sorted into one group or the other.
|
|
29
|
+
const FIELD_SHAPING = [
|
|
30
|
+
['label', 'label'],
|
|
31
|
+
['message', undefined],
|
|
32
|
+
['horizontal', 'horizontal'],
|
|
33
|
+
['fieldClassName', 'className (for fieldClassName)'],
|
|
34
|
+
];
|
|
35
|
+
/** `a`, `a and b`, `a, b and c`. */
|
|
36
|
+
const listOf = (items) => items.length > 1
|
|
37
|
+
? `${items.slice(0, -1).join(', ')} and ${items[items.length - 1]}`
|
|
38
|
+
: items[0];
|
|
39
|
+
/**
|
|
40
|
+
* Whether a convenience form wrapper renders a `Field` of its own (#905).
|
|
41
|
+
* Never inside an outer `Field`. Inside a `Control` with no `Field` around
|
|
42
|
+
* it, a `Field` of its own would put a `.field` inside the `.control`, where
|
|
43
|
+
* Bulma's control styles break (its icon rules are sibling selectors on the
|
|
44
|
+
* input). So it renders one there only when a prop that shapes the `Field`'s
|
|
45
|
+
* markup is set (`label`, `message`, `horizontal`, `fieldClassName`), keeping
|
|
46
|
+
* the markup it always had, and warns in development that the fix is a
|
|
47
|
+
* `Field` around that `Control`. A prop counts when it is truthy, the same
|
|
48
|
+
* test that decides whether it changes the `Field`'s output.
|
|
49
|
+
* Internal; not part of the public API.
|
|
50
|
+
*/
|
|
51
|
+
const rendersOwnField = (component, options) => {
|
|
52
|
+
if (options.insideField)
|
|
53
|
+
return false;
|
|
54
|
+
if (!options.insideControl)
|
|
55
|
+
return true;
|
|
56
|
+
const held = FIELD_SHAPING.filter(([prop]) => options[prop]);
|
|
57
|
+
if (held.length === 0)
|
|
58
|
+
return false;
|
|
59
|
+
const names = held.map(([prop]) => prop);
|
|
60
|
+
const moved = held
|
|
61
|
+
.map(([, onField]) => onField)
|
|
62
|
+
.filter((onField) => onField !== undefined);
|
|
63
|
+
warnOnce(`${component}:Field-in-bare-Control:${names.join('+')}`, `[bestax-bulma] <${component} ${names.join(' ')}> inside a <Control> ` +
|
|
64
|
+
`with no <Field> around it renders its own <Field> for ` +
|
|
65
|
+
`${names.length > 1 ? 'those props' : 'that prop'}, which puts a ` +
|
|
66
|
+
`.field inside the .control, a nesting Bulma's styles are not ` +
|
|
67
|
+
`written for. Wrap the <Control> in a <Field> instead` +
|
|
68
|
+
(moved.length > 0 ? `, and set ${listOf(moved)} on that <Field>.` : '.'));
|
|
69
|
+
return true;
|
|
70
|
+
};
|
|
71
|
+
// Each Control-level prop, with what takes its place on the `Control` the
|
|
72
|
+
// caller wrapped around the wrapper. `bare-control.test.tsx` holds this to
|
|
73
|
+
// what each wrapper hands its own `Control`, so a new Control-level prop
|
|
74
|
+
// fails there until it is added here and passed to `rendersOwnControl`.
|
|
75
|
+
const ON_CONTROL = {
|
|
76
|
+
isLoading: 'isLoading',
|
|
77
|
+
iconLeft: 'iconLeft',
|
|
78
|
+
iconLeftName: 'iconLeftName',
|
|
79
|
+
iconLeftSize: 'iconLeftSize',
|
|
80
|
+
iconRight: 'iconRight',
|
|
81
|
+
iconRightName: 'iconRightName',
|
|
82
|
+
iconRightSize: 'iconRightSize',
|
|
83
|
+
hasIconsLeft: 'hasIconsLeft',
|
|
84
|
+
hasIconsRight: 'hasIconsRight',
|
|
85
|
+
isExpanded: 'isExpanded',
|
|
86
|
+
controlSize: 'size (for controlSize)',
|
|
87
|
+
controlClassName: 'className (for controlClassName)',
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* Whether a convenience form wrapper renders a `Control` of its own (#921):
|
|
91
|
+
* only outside one. Inside a `Control` it renders none, so the props it
|
|
92
|
+
* would have handed its own do nothing there, and it warns in development,
|
|
93
|
+
* naming them and saying to set them on that `Control`. An `inline` picker
|
|
94
|
+
* renders none in either place, so it drops them inside a `Control` or not,
|
|
95
|
+
* and its warning says so without pointing at a `Control`. A wrapper passes
|
|
96
|
+
* only the props it hands its own `Control`, as the caller gave them, so a
|
|
97
|
+
* default the wrapper fills in itself never warns. A prop counts when it is
|
|
98
|
+
* truthy, since a falsy one would change nothing on a `Control` either.
|
|
99
|
+
* `iconLeftSize` and `hasIconsLeft` show nothing on a `Control` without a
|
|
100
|
+
* glyph, so when they move and the caller chose no left icon, the advice
|
|
101
|
+
* names the wrapper's default glyph too, and following it draws what the
|
|
102
|
+
* wrapper's own `Control` did.
|
|
103
|
+
* Internal; not part of the public API.
|
|
104
|
+
*/
|
|
105
|
+
const rendersOwnControl = (component, options) => {
|
|
106
|
+
if (!options.inline && !options.insideControl)
|
|
107
|
+
return true;
|
|
108
|
+
const held = Object.keys(ON_CONTROL).filter(prop => options[prop]);
|
|
109
|
+
if (held.length === 0)
|
|
110
|
+
return false;
|
|
111
|
+
const names = held.join(' ');
|
|
112
|
+
const doNothing = held.length > 1 ? 'those props do' : 'that prop does';
|
|
113
|
+
let message;
|
|
114
|
+
if (options.inline) {
|
|
115
|
+
message =
|
|
116
|
+
`[bestax-bulma] <${component} inline ${names}> renders no <Control> ` +
|
|
117
|
+
`in inline mode, inside a <Control> or not, so ${doNothing} ` +
|
|
118
|
+
`nothing. Leave ${held.length > 1 ? 'them' : 'it'} out of an ` +
|
|
119
|
+
`inline picker.`;
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
const toSet = held.map(prop => ON_CONTROL[prop]);
|
|
123
|
+
const needsGlyph = (options.iconLeftSize || options.hasIconsLeft) &&
|
|
124
|
+
!options.iconLeft &&
|
|
125
|
+
options.iconLeftName === undefined &&
|
|
126
|
+
options.defaultIconLeftName;
|
|
127
|
+
if (needsGlyph) {
|
|
128
|
+
toSet.push(`iconLeftName="${options.defaultIconLeftName}" (its default icon)`);
|
|
129
|
+
}
|
|
130
|
+
message =
|
|
131
|
+
`[bestax-bulma] <${component} ${names}> inside a <Control> renders ` +
|
|
132
|
+
`no <Control> of its own, so ${doNothing} nothing there. Set ` +
|
|
133
|
+
`${listOf(toSet)} on that <Control> instead.`;
|
|
134
|
+
}
|
|
135
|
+
// Keyed by the message itself: the props named do not settle the advice
|
|
136
|
+
// (an empty `iconLeftName` and an unset one name the same props), so two
|
|
137
|
+
// calls that would advise differently must not share a key.
|
|
138
|
+
warnOnce(message, message);
|
|
139
|
+
return false;
|
|
140
|
+
};
|
|
141
|
+
const FieldLabelIdContext = createContext(undefined);
|
|
142
|
+
/**
|
|
143
|
+
* The id a labeled Field wants its single composed control to adopt (#495).
|
|
144
|
+
* `undefined` outside a Field, in unlabeled/grouped/addons Fields, or when the
|
|
145
|
+
* user took over the association with an explicit `labelProps.htmlFor`.
|
|
146
|
+
* Consumed only by the single-control bases (InputBase, SelectBase,
|
|
147
|
+
* TextAreaBase). Internal; not part of the public API.
|
|
148
|
+
*/
|
|
149
|
+
const useFieldLabelId = () => useContext(FieldLabelIdContext);
|
|
150
|
+
/** Provider for the Field label-target id — used internally by Field. */
|
|
151
|
+
const FieldLabelIdProvider = FieldLabelIdContext.Provider;
|
|
152
|
+
const RadiosContext = createContext(undefined);
|
|
153
|
+
const CheckboxesContext = createContext(undefined);
|
|
154
|
+
/**
|
|
155
|
+
* Hook to read the full surrounding `<Radios>` group context (name + selection).
|
|
156
|
+
* Returns `undefined` when not inside a `<Radios>` group.
|
|
157
|
+
*/
|
|
158
|
+
const useRadiosGroup = () => useContext(RadiosContext);
|
|
159
|
+
/**
|
|
160
|
+
* Hook to read the full surrounding `<Checkboxes>` group context.
|
|
161
|
+
* Returns `undefined` when not inside a `<Checkboxes>` group.
|
|
162
|
+
*/
|
|
163
|
+
const useCheckboxesGroup = () => useContext(CheckboxesContext);
|
|
164
|
+
/** Provider for the Radios group context — used internally by Radios. */
|
|
165
|
+
const RadiosProvider = RadiosContext.Provider;
|
|
166
|
+
/** Provider for the Checkboxes group context — used internally by Checkboxes. */
|
|
167
|
+
const CheckboxesProvider = CheckboxesContext.Provider;
|
|
168
|
+
|
|
169
|
+
export { CheckboxesProvider, ControlProvider, FieldLabelIdProvider, FieldProvider, RadiosProvider, rendersOwnControl, rendersOwnField, useCheckboxesGroup, useFieldLabelId, useInsideControl, useInsideField, useRadiosGroup };
|
|
170
|
+
//# sourceMappingURL=FormContext.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FormContext.js","sources":["../../../src/form/FormContext.tsx"],"sourcesContent":["import React, { createContext, useContext } from 'react';\nimport { warnOnce } from '../helpers/devWarnings';\nimport type { ControlBaseProps } from './Control';\n\nconst FieldContext = createContext(false);\nconst ControlContext = createContext(false);\n\n/**\n * Hook to detect if the component is inside a Field wrapper.\n * Form components use this to skip rendering their own Field.\n */\nexport const useInsideField = () => useContext(FieldContext);\n\n/**\n * Hook to detect if the component is inside a Control wrapper.\n * Form components use this to skip rendering their own Control.\n */\nexport const useInsideControl = () => useContext(ControlContext);\n\n/** Provider for Field context — used internally by Field component. */\nexport const FieldProvider = FieldContext.Provider;\n\n/** Provider for Control context — used internally by Control component. */\nexport const ControlProvider = ControlContext.Provider;\n\n/** The wrapper props that change what its own `Field` renders. */\ninterface FieldShapingProps {\n /** Rendered as the `Field`'s label. */\n label?: React.ReactNode;\n /** Rendered as help text after the widget. */\n message?: React.ReactNode;\n /** Lays the `Field` out horizontally. */\n horizontal?: boolean;\n /** Set on the `Field` as its class. */\n fieldClassName?: string;\n}\n\ninterface OwnFieldOptions extends FieldShapingProps {\n /** `useInsideField()` as the wrapper read it. */\n insideField: boolean;\n /** `useInsideControl()` as the wrapper read it. */\n insideControl: boolean;\n}\n\n// Each Field-shaping prop, with what takes its place on a `Field` the caller\n// wraps around the `Control`. `message` needs nothing there, because the\n// wrapper still renders it inside `Field > Control`. The other Field-level\n// props (`labelSize`, `labelProps`, `messageColor`) change nothing unless a\n// `label` or `message` renders, so on their own they keep no `Field`.\n// `bare-control.test.tsx` holds this list to what each wrapper hands its\n// `Field` and to which props change its markup, so a new Field-level prop\n// fails there until it is sorted into one group or the other.\nconst FIELD_SHAPING: ReadonlyArray<\n [keyof FieldShapingProps, string | undefined]\n> = [\n ['label', 'label'],\n ['message', undefined],\n ['horizontal', 'horizontal'],\n ['fieldClassName', 'className (for fieldClassName)'],\n];\n\n/** `a`, `a and b`, `a, b and c`. */\nconst listOf = (items: string[]): string =>\n items.length > 1\n ? `${items.slice(0, -1).join(', ')} and ${items[items.length - 1]}`\n : items[0];\n\n/**\n * Whether a convenience form wrapper renders a `Field` of its own (#905).\n * Never inside an outer `Field`. Inside a `Control` with no `Field` around\n * it, a `Field` of its own would put a `.field` inside the `.control`, where\n * Bulma's control styles break (its icon rules are sibling selectors on the\n * input). So it renders one there only when a prop that shapes the `Field`'s\n * markup is set (`label`, `message`, `horizontal`, `fieldClassName`), keeping\n * the markup it always had, and warns in development that the fix is a\n * `Field` around that `Control`. A prop counts when it is truthy, the same\n * test that decides whether it changes the `Field`'s output.\n * Internal; not part of the public API.\n */\nexport const rendersOwnField = (\n component: string,\n options: OwnFieldOptions\n): boolean => {\n if (options.insideField) return false;\n if (!options.insideControl) return true;\n const held = FIELD_SHAPING.filter(([prop]) => options[prop]);\n if (held.length === 0) return false;\n const names = held.map(([prop]) => prop);\n const moved = held\n .map(([, onField]) => onField)\n .filter((onField): onField is string => onField !== undefined);\n warnOnce(\n `${component}:Field-in-bare-Control:${names.join('+')}`,\n `[bestax-bulma] <${component} ${names.join(' ')}> inside a <Control> ` +\n `with no <Field> around it renders its own <Field> for ` +\n `${names.length > 1 ? 'those props' : 'that prop'}, which puts a ` +\n `.field inside the .control, a nesting Bulma's styles are not ` +\n `written for. Wrap the <Control> in a <Field> instead` +\n (moved.length > 0 ? `, and set ${listOf(moved)} on that <Field>.` : '.')\n );\n return true;\n};\n\n/** The props a convenience form wrapper hands its own `Control`. Internal. */\nexport interface ControlLevelProps {\n /** Shows the `Control`'s loading spinner. */\n isLoading?: boolean;\n /** The `Control`'s left icon. */\n iconLeft?: ControlBaseProps['iconLeft'];\n /** Shortcut for the left icon's name. */\n iconLeftName?: string;\n /** Shortcut for the left icon's size. */\n iconLeftSize?: ControlBaseProps['iconLeftSize'];\n /** The `Control`'s right icon. */\n iconRight?: ControlBaseProps['iconRight'];\n /** Shortcut for the right icon's name. */\n iconRightName?: string;\n /** Shortcut for the right icon's size. */\n iconRightSize?: ControlBaseProps['iconRightSize'];\n /** Reserves the `Control`'s left icon space. */\n hasIconsLeft?: boolean;\n /** Reserves the `Control`'s right icon space. */\n hasIconsRight?: boolean;\n /** Expands the `Control`. */\n isExpanded?: boolean;\n /** Set on the `Control` as its `size`. */\n controlSize?: ControlBaseProps['size'];\n /** Set on the `Control` as its class. */\n controlClassName?: string;\n}\n\ninterface OwnControlOptions extends ControlLevelProps {\n /** `useInsideControl()` as the wrapper read it. */\n insideControl: boolean;\n /** A picker's `inline`, which renders no `Control` inside one or out. */\n inline?: boolean;\n /**\n * The left glyph the wrapper's own `Control` shows when the caller sets no\n * `iconLeftName`; read only then. `bare-control.test.tsx` reads that glyph\n * from what each wrapper hands its own `Control`, so a wrapper that gains\n * one fails there until it passes it here.\n */\n defaultIconLeftName?: string;\n}\n\n// Each Control-level prop, with what takes its place on the `Control` the\n// caller wrapped around the wrapper. `bare-control.test.tsx` holds this to\n// what each wrapper hands its own `Control`, so a new Control-level prop\n// fails there until it is added here and passed to `rendersOwnControl`.\nconst ON_CONTROL: Record<keyof ControlLevelProps, string> = {\n isLoading: 'isLoading',\n iconLeft: 'iconLeft',\n iconLeftName: 'iconLeftName',\n iconLeftSize: 'iconLeftSize',\n iconRight: 'iconRight',\n iconRightName: 'iconRightName',\n iconRightSize: 'iconRightSize',\n hasIconsLeft: 'hasIconsLeft',\n hasIconsRight: 'hasIconsRight',\n isExpanded: 'isExpanded',\n controlSize: 'size (for controlSize)',\n controlClassName: 'className (for controlClassName)',\n};\n\n/**\n * Whether a convenience form wrapper renders a `Control` of its own (#921):\n * only outside one. Inside a `Control` it renders none, so the props it\n * would have handed its own do nothing there, and it warns in development,\n * naming them and saying to set them on that `Control`. An `inline` picker\n * renders none in either place, so it drops them inside a `Control` or not,\n * and its warning says so without pointing at a `Control`. A wrapper passes\n * only the props it hands its own `Control`, as the caller gave them, so a\n * default the wrapper fills in itself never warns. A prop counts when it is\n * truthy, since a falsy one would change nothing on a `Control` either.\n * `iconLeftSize` and `hasIconsLeft` show nothing on a `Control` without a\n * glyph, so when they move and the caller chose no left icon, the advice\n * names the wrapper's default glyph too, and following it draws what the\n * wrapper's own `Control` did.\n * Internal; not part of the public API.\n */\nexport const rendersOwnControl = (\n component: string,\n options: OwnControlOptions\n): boolean => {\n if (!options.inline && !options.insideControl) return true;\n const held = (\n Object.keys(ON_CONTROL) as Array<keyof ControlLevelProps>\n ).filter(prop => options[prop]);\n if (held.length === 0) return false;\n const names = held.join(' ');\n const doNothing = held.length > 1 ? 'those props do' : 'that prop does';\n let message: string;\n if (options.inline) {\n message =\n `[bestax-bulma] <${component} inline ${names}> renders no <Control> ` +\n `in inline mode, inside a <Control> or not, so ${doNothing} ` +\n `nothing. Leave ${held.length > 1 ? 'them' : 'it'} out of an ` +\n `inline picker.`;\n } else {\n const toSet = held.map(prop => ON_CONTROL[prop]);\n const needsGlyph =\n (options.iconLeftSize || options.hasIconsLeft) &&\n !options.iconLeft &&\n options.iconLeftName === undefined &&\n options.defaultIconLeftName;\n if (needsGlyph) {\n toSet.push(\n `iconLeftName=\"${options.defaultIconLeftName}\" (its default icon)`\n );\n }\n message =\n `[bestax-bulma] <${component} ${names}> inside a <Control> renders ` +\n `no <Control> of its own, so ${doNothing} nothing there. Set ` +\n `${listOf(toSet)} on that <Control> instead.`;\n }\n // Keyed by the message itself: the props named do not settle the advice\n // (an empty `iconLeftName` and an unset one name the same props), so two\n // calls that would advise differently must not share a key.\n warnOnce(message, message);\n return false;\n};\n\nconst FieldLabelIdContext = createContext<string | undefined>(undefined);\n\n/**\n * The id a labeled Field wants its single composed control to adopt (#495).\n * `undefined` outside a Field, in unlabeled/grouped/addons Fields, or when the\n * user took over the association with an explicit `labelProps.htmlFor`.\n * Consumed only by the single-control bases (InputBase, SelectBase,\n * TextAreaBase). Internal; not part of the public API.\n */\nexport const useFieldLabelId = () => useContext(FieldLabelIdContext);\n\n/** Provider for the Field label-target id — used internally by Field. */\nexport const FieldLabelIdProvider = FieldLabelIdContext.Provider;\n\n/**\n * Shape of the Radios group context. The group provides:\n * - `name`: shared form field name (Stage 1)\n * - `value`: currently-selected radio value (Stage 2 — group-managed selection)\n * - `onChange`: dispatched by child Radios when clicked (Stage 2)\n *\n * Group sets `value`/`onChange` only when actively managing selection\n * (i.e., the user passed `value`, `defaultValue`, or `onChange` to `<Radios>`).\n * When only `name` is provided, the group is in \"name-only\" mode and child\n * Radios manage their own checked state independently.\n */\nexport interface RadiosGroupContextValue {\n name?: string;\n value?: string;\n onChange?: (value: string) => void;\n}\n\n/**\n * Shape of the Checkboxes group context. Like Radios but with array semantics\n * for multi-select.\n */\nexport interface CheckboxesGroupContextValue {\n name?: string;\n value?: string[];\n onChange?: (values: string[]) => void;\n}\n\nconst RadiosContext = createContext<RadiosGroupContextValue | undefined>(\n undefined\n);\nconst CheckboxesContext = createContext<\n CheckboxesGroupContextValue | undefined\n>(undefined);\n\n/**\n * Hook to read the full surrounding `<Radios>` group context (name + selection).\n * Returns `undefined` when not inside a `<Radios>` group.\n */\nexport const useRadiosGroup = () => useContext(RadiosContext);\n\n/**\n * Hook to read the full surrounding `<Checkboxes>` group context.\n * Returns `undefined` when not inside a `<Checkboxes>` group.\n */\nexport const useCheckboxesGroup = () => useContext(CheckboxesContext);\n\n/**\n * Hook to read just the shared `name` from a surrounding `<Radios>` group.\n * Child `<Radio>` components fall back to this when no local `name` prop is set.\n * Returns `undefined` outside of a `<Radios>` group.\n */\nexport const useRadiosName = () => useRadiosGroup()?.name;\n\n/**\n * Hook to read just the shared `name` from a surrounding `<Checkboxes>` group.\n * Returns `undefined` outside of a `<Checkboxes>` group.\n */\nexport const useCheckboxesName = () => useCheckboxesGroup()?.name;\n\n/** Provider for the Radios group context — used internally by Radios. */\nexport const RadiosProvider = RadiosContext.Provider;\n\n/** Provider for the Checkboxes group context — used internally by Checkboxes. */\nexport const CheckboxesProvider = CheckboxesContext.Provider;\n\n// --- Backward-compat aliases (Stage 1 names) -----------------------------\n// Internal callers may still import these. Both still work because they refer\n// to the same providers/hooks under the new shape.\n\n/** @deprecated Use `RadiosProvider` (provides {name, value, onChange}). */\nexport const RadiosNameProvider = RadiosProvider;\n\n/** @deprecated Use `CheckboxesProvider` (provides {name, value, onChange}). */\nexport const CheckboxesNameProvider = CheckboxesProvider;\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/* @allxsmith/bestax-bulma — AI agents: see AGENTS.md in the package root, or https://bestax.io/llms.txt */
|
|
2
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
3
|
+
import { forwardRef } from 'react';
|
|
4
|
+
import { usePrefixedClassNames } from '../helpers/classNames.js';
|
|
5
|
+
import { Field } from './Field.js';
|
|
6
|
+
import { Control } from './Control.js';
|
|
7
|
+
import { InputBase } from './InputBase.js';
|
|
8
|
+
import { useInsideField, useInsideControl, rendersOwnField, rendersOwnControl } from './FormContext.js';
|
|
9
|
+
import { useAutoLabelId } from './useAutoLabelId.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The `Input` component provides a Bulma-styled text input, supporting colors, sizes, rounded corners, static state, hover/focus/loading states, and all Bulma helper props.
|
|
13
|
+
*
|
|
14
|
+
* @function
|
|
15
|
+
* @param {InputProps} props - Props for Input.
|
|
16
|
+
* @returns {JSX.Element} The composed field element.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* <Input label="Username" placeholder="Enter username" iconLeftName="user" />
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* <Input
|
|
23
|
+
* label="Email"
|
|
24
|
+
* type="email"
|
|
25
|
+
* message="Please enter a valid email"
|
|
26
|
+
* messageColor="danger"
|
|
27
|
+
* color="danger"
|
|
28
|
+
* />
|
|
29
|
+
*/
|
|
30
|
+
const Input = forwardRef(({
|
|
31
|
+
// Field props
|
|
32
|
+
label, labelSize, labelProps, horizontal,
|
|
33
|
+
// Control props
|
|
34
|
+
iconLeft, iconRight, iconLeftName, iconRightName, iconLeftSize, iconRightSize, hasIconsLeft, hasIconsRight, isLoading, isExpanded, controlSize,
|
|
35
|
+
// Message props
|
|
36
|
+
message, messageColor,
|
|
37
|
+
// Container class overrides
|
|
38
|
+
fieldClassName, controlClassName,
|
|
39
|
+
// Everything else goes to Input
|
|
40
|
+
...inputProps }, ref) => {
|
|
41
|
+
const insideField = useInsideField();
|
|
42
|
+
const insideControl = useInsideControl();
|
|
43
|
+
const ownField = rendersOwnField('Input', {
|
|
44
|
+
insideField,
|
|
45
|
+
insideControl,
|
|
46
|
+
label,
|
|
47
|
+
message,
|
|
48
|
+
horizontal,
|
|
49
|
+
fieldClassName,
|
|
50
|
+
});
|
|
51
|
+
const ownControl = rendersOwnControl('Input', {
|
|
52
|
+
insideControl,
|
|
53
|
+
isLoading,
|
|
54
|
+
iconLeft,
|
|
55
|
+
iconLeftName,
|
|
56
|
+
iconLeftSize,
|
|
57
|
+
iconRight,
|
|
58
|
+
iconRightName,
|
|
59
|
+
iconRightSize,
|
|
60
|
+
hasIconsLeft,
|
|
61
|
+
hasIconsRight,
|
|
62
|
+
isExpanded,
|
|
63
|
+
controlSize,
|
|
64
|
+
controlClassName,
|
|
65
|
+
});
|
|
66
|
+
const { controlId, fieldLabelProps } = useAutoLabelId({
|
|
67
|
+
label,
|
|
68
|
+
id: inputProps.id,
|
|
69
|
+
labelProps,
|
|
70
|
+
rendersLabel: ownField,
|
|
71
|
+
});
|
|
72
|
+
const helpClass = usePrefixedClassNames('help', {
|
|
73
|
+
[`is-${messageColor}`]: !!messageColor,
|
|
74
|
+
});
|
|
75
|
+
let content = jsx(InputBase, { ref: ref, id: controlId, ...inputProps });
|
|
76
|
+
if (ownControl) {
|
|
77
|
+
content = (jsx(Control, { iconLeft: iconLeft, iconRight: iconRight, iconLeftName: iconLeftName, iconRightName: iconRightName, iconLeftSize: iconLeftSize, iconRightSize: iconRightSize, hasIconsLeft: hasIconsLeft, hasIconsRight: hasIconsRight, isLoading: isLoading, isExpanded: isExpanded, size: controlSize, className: controlClassName, children: content }));
|
|
78
|
+
}
|
|
79
|
+
const messageEl = message ? jsx("p", { className: helpClass, children: message }) : null;
|
|
80
|
+
if (ownField) {
|
|
81
|
+
return (jsxs(Field, { label: label, labelSize: labelSize, labelProps: fieldLabelProps, horizontal: horizontal, className: fieldClassName, children: [content, messageEl] }));
|
|
82
|
+
}
|
|
83
|
+
return (jsxs(Fragment, { children: [content, messageEl] }));
|
|
84
|
+
});
|
|
85
|
+
Input.displayName = 'Input';
|
|
86
|
+
|
|
87
|
+
export { Input };
|
|
88
|
+
//# sourceMappingURL=Input.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Input.js","sources":["../../../src/form/Input.tsx"],"sourcesContent":["import React, { forwardRef } from 'react';\nimport { usePrefixedClassNames } from '../helpers/classNames';\nimport { Field, FieldProps } from './Field';\nimport { Control, ControlBaseProps } from './Control';\nimport { InputBase, InputBaseProps } from './InputBase';\nimport {\n useInsideField,\n useInsideControl,\n rendersOwnField,\n rendersOwnControl,\n} from './FormContext';\nimport { useAutoLabelId } from './useAutoLabelId';\n\n/**\n * Props for the Input component.\n *\n * Composes Field, Control, and Input into a single convenience component.\n * Supports all Input props, plus Field-level (label, horizontal) and\n * 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. Inside an outer `Field`, or a `Control` with no\n * `Field` around 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 */\nexport interface InputProps extends InputBaseProps {\n /** Field label. Automatically associated with the input via `htmlFor` — uses your `id` when provided, otherwise a generated one. Dropped inside an outer `Field`, whose own label associates instead when that `Field` generates a target id (not `grouped`/`hasAddons`, no explicit `labelProps.htmlFor`). */\n label?: React.ReactNode;\n /** Size for the label. */\n labelSize?: FieldProps['labelSize'];\n /** Props for the label element when the component renders its own `Field`; dropped inside an outer `Field` (use that `Field`'s `labelProps` instead). An explicit `htmlFor` key — even `undefined` — overrides the automatic association and no id is generated. */\n labelProps?: FieldProps['labelProps'];\n /** Horizontal field layout. */\n horizontal?: boolean;\n /**\n * Icon props for 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 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 left icon name. */\n iconLeftName?: string;\n /** Shortcut for 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 left icon container. */\n hasIconsLeft?: boolean;\n /** Force right icon container. */\n hasIconsRight?: boolean;\n /**\n * Shows a loading spinner on the `Control` it renders.\n * Under `prefers-reduced-motion: reduce` the spinner stops and stays\n * drawn (with bestax's CSS loaded).\n * The spinner sits at the right edge, where `iconRight` also sits, and the\n * two overlap; leave the right icon out while loading.\n */\n isLoading?: boolean;\n /** Expand the control. */\n isExpanded?: boolean;\n /** Control size. */\n controlSize?: ControlBaseProps['size'];\n /** Help/validation message below the input. */\n message?: React.ReactNode;\n /** Bulma color for the message. */\n messageColor?: 'primary' | 'link' | 'info' | 'success' | 'warning' | 'danger';\n /** Additional CSS classes for the Field. */\n fieldClassName?: string;\n /** Additional CSS classes for the Control. */\n controlClassName?: string;\n}\n\n/**\n * The `Input` component provides a Bulma-styled text input, supporting colors, sizes, rounded corners, static state, hover/focus/loading states, and all Bulma helper props.\n *\n * @function\n * @param {InputProps} props - Props for Input.\n * @returns {JSX.Element} The composed field element.\n *\n * @example\n * <Input label=\"Username\" placeholder=\"Enter username\" iconLeftName=\"user\" />\n *\n * @example\n * <Input\n * label=\"Email\"\n * type=\"email\"\n * message=\"Please enter a valid email\"\n * messageColor=\"danger\"\n * color=\"danger\"\n * />\n */\nexport const Input = forwardRef<HTMLInputElement, InputProps>(\n (\n {\n // Field props\n label,\n labelSize,\n labelProps,\n horizontal,\n // Control props\n iconLeft,\n iconRight,\n iconLeftName,\n iconRightName,\n iconLeftSize,\n iconRightSize,\n hasIconsLeft,\n hasIconsRight,\n isLoading,\n isExpanded,\n controlSize,\n // Message props\n message,\n messageColor,\n // Container class overrides\n fieldClassName,\n controlClassName,\n // Everything else goes to Input\n ...inputProps\n },\n ref\n ) => {\n const insideField = useInsideField();\n const insideControl = useInsideControl();\n const ownField = rendersOwnField('Input', {\n insideField,\n insideControl,\n label,\n message,\n horizontal,\n fieldClassName,\n });\n const ownControl = rendersOwnControl('Input', {\n insideControl,\n isLoading,\n iconLeft,\n 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: inputProps.id,\n labelProps,\n rendersLabel: ownField,\n });\n const helpClass = usePrefixedClassNames('help', {\n [`is-${messageColor}`]: !!messageColor,\n });\n\n let content = <InputBase ref={ref} id={controlId} {...inputProps} />;\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}\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\nInput.displayName = 'Input';\n\nexport default Input;\n"],"names":["_jsx","_jsxs","_Fragment"],"mappings":";;;;;;;;;;AAmFA;;;;;;;;;;;;;;;;;;AAkBG;AACI,MAAM,KAAK,GAAG,UAAU,CAC7B,CACE;AACE;AACA,KAAK,EACL,SAAS,EACT,UAAU,EACV,UAAU;AACV;AACA,QAAQ,EACR,SAAS,EACT,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,aAAa,EACb,SAAS,EACT,UAAU,EACV,WAAW;AACX;AACA,OAAO,EACP,YAAY;AACZ;AACA,cAAc,EACd,gBAAgB;AAChB;AACA,GAAG,UAAU,EACd,EACD,GAAG,KACD;AACF,IAAA,MAAM,WAAW,GAAG,cAAc,EAAE;AACpC,IAAA,MAAM,aAAa,GAAG,gBAAgB,EAAE;AACxC,IAAA,MAAM,QAAQ,GAAG,eAAe,CAAC,OAAO,EAAE;QACxC,WAAW;QACX,aAAa;QACb,KAAK;QACL,OAAO;QACP,UAAU;QACV,cAAc;AACf,KAAA,CAAC;AACF,IAAA,MAAM,UAAU,GAAG,iBAAiB,CAAC,OAAO,EAAE;QAC5C,aAAa;QACb,SAAS;QACT,QAAQ;QACR,YAAY;QACZ,YAAY;QACZ,SAAS;QACT,aAAa;QACb,aAAa;QACb,YAAY;QACZ,aAAa;QACb,UAAU;QACV,WAAW;QACX,gBAAgB;AACjB,KAAA,CAAC;AACF,IAAA,MAAM,EAAE,SAAS,EAAE,eAAe,EAAE,GAAG,cAAc,CAAC;QACpD,KAAK;QACL,EAAE,EAAE,UAAU,CAAC,EAAE;QACjB,UAAU;AACV,QAAA,YAAY,EAAE,QAAQ;AACvB,KAAA,CAAC;AACF,IAAA,MAAM,SAAS,GAAG,qBAAqB,CAAC,MAAM,EAAE;AAC9C,QAAA,CAAC,MAAM,YAAY,CAAA,CAAE,GAAG,CAAC,CAAC,YAAY;AACvC,KAAA,CAAC;AAEF,IAAA,IAAI,OAAO,GAAGA,GAAA,CAAC,SAAS,IAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,SAAS,EAAA,GAAM,UAAU,GAAI;IAEpE,IAAI,UAAU,EAAE;QACd,OAAO,IACLA,GAAA,CAAC,OAAO,EAAA,EACN,QAAQ,EAAE,QAAQ,EAClB,SAAS,EAAE,SAAS,EACpB,YAAY,EAAE,YAAY,EAC1B,aAAa,EAAE,aAAa,EAC5B,YAAY,EAAE,YAAY,EAC1B,aAAa,EAAE,aAAa,EAC5B,YAAY,EAAE,YAAY,EAC1B,aAAa,EAAE,aAAa,EAC5B,SAAS,EAAE,SAAS,EACpB,UAAU,EAAE,UAAU,EACtB,IAAI,EAAE,WAAW,EACjB,SAAS,EAAE,gBAAgB,EAAA,QAAA,EAE1B,OAAO,EAAA,CACA,CACX;IACH;AAEA,IAAA,MAAM,SAAS,GAAG,OAAO,GAAGA,GAAA,CAAA,GAAA,EAAA,EAAG,SAAS,EAAE,SAAS,YAAG,OAAO,EAAA,CAAK,GAAG,IAAI;IAEzE,IAAI,QAAQ,EAAE;AACZ,QAAA,QACEC,IAAA,CAAC,KAAK,EAAA,EACJ,KAAK,EAAE,KAAK,EACZ,SAAS,EAAE,SAAS,EACpB,UAAU,EAAE,eAAe,EAC3B,UAAU,EAAE,UAAU,EACtB,SAAS,EAAE,cAAc,EAAA,QAAA,EAAA,CAExB,OAAO,EACP,SAAS,CAAA,EAAA,CACJ;IAEZ;AAEA,IAAA,QACEA,IAAA,CAAAC,QAAA,EAAA,EAAA,QAAA,EAAA,CACG,OAAO,EACP,SAAS,CAAA,EAAA,CACT;AAEP,CAAC;AAGH,KAAK,CAAC,WAAW,GAAG,OAAO;;"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/* @allxsmith/bestax-bulma — AI agents: see AGENTS.md in the package root, or https://bestax.io/llms.txt */
|
|
2
|
+
import { jsx } from 'react/jsx-runtime';
|
|
3
|
+
import { forwardRef } from 'react';
|
|
4
|
+
import { usePrefixedClassNames, classNames } from '../helpers/classNames.js';
|
|
5
|
+
import { useBulmaClasses } from '../helpers/useBulmaClasses.js';
|
|
6
|
+
import { useFieldLabelId } from './FormContext.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Bulma Input component with full Bulma helper class support.
|
|
10
|
+
*
|
|
11
|
+
* @function
|
|
12
|
+
* @param {InputBaseProps} props - Props for the Input component.
|
|
13
|
+
* @returns {JSX.Element} The rendered input element.
|
|
14
|
+
* @see {@link https://bulma.io/documentation/form/input/ | Bulma Input documentation}
|
|
15
|
+
*/
|
|
16
|
+
const InputBase = forwardRef(({ color, size, isRounded, isStatic, isHovered, isFocused, isLoading, className, disabled, readOnly, ...props }, ref) => {
|
|
17
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
18
|
+
color,
|
|
19
|
+
...props,
|
|
20
|
+
});
|
|
21
|
+
const fieldLabelId = useFieldLabelId();
|
|
22
|
+
const mainClass = usePrefixedClassNames('input', {
|
|
23
|
+
[`is-${color}`]: !!color,
|
|
24
|
+
[`is-${size}`]: !!size,
|
|
25
|
+
'is-rounded': isRounded,
|
|
26
|
+
'is-static': isStatic,
|
|
27
|
+
'is-hovered': isHovered,
|
|
28
|
+
'is-focused': isFocused,
|
|
29
|
+
'is-loading': isLoading,
|
|
30
|
+
});
|
|
31
|
+
const inputClass = classNames(mainClass, bulmaHelperClasses, className);
|
|
32
|
+
return (jsx("input", { ref: ref, className: inputClass, disabled: disabled, readOnly: readOnly, ...rest,
|
|
33
|
+
// After the spread: a labeled Field's generated id is adopted only
|
|
34
|
+
// when no user id arrived (rest.id may be an undefined own key).
|
|
35
|
+
id: rest.id ?? fieldLabelId }));
|
|
36
|
+
});
|
|
37
|
+
InputBase.displayName = 'InputBase';
|
|
38
|
+
|
|
39
|
+
export { InputBase };
|
|
40
|
+
//# sourceMappingURL=InputBase.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InputBase.js","sources":["../../../src/form/InputBase.tsx"],"sourcesContent":["import React, { forwardRef } from 'react';\nimport { classNames, usePrefixedClassNames } from '../helpers/classNames';\nimport { useBulmaClasses, BulmaClassesProps } from '../helpers/useBulmaClasses';\nimport { useFieldLabelId } from './FormContext';\n\n/**\n * Props for the Input component.\n */\nexport interface InputBaseProps\n extends\n Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size'>,\n Omit<BulmaClassesProps, 'color'> {\n /** Bulma color modifier for the input. */\n color?:\n | 'primary'\n | 'link'\n | 'info'\n | 'success'\n | 'warning'\n | 'danger'\n | 'black'\n | 'dark'\n | 'light'\n | 'white';\n /** Size modifier for the input. */\n size?: 'small' | 'medium' | 'large';\n /** Rounded input corners. */\n isRounded?: boolean;\n /** Styles the input as static text. It stays editable; add `readOnly` to stop edits. */\n isStatic?: boolean;\n /** Applies hovered state. */\n isHovered?: boolean;\n /** Applies focused state. */\n isFocused?: boolean;\n /**\n * Adds the `is-loading` class to the `<input>`. Bulma draws no spinner for it there: the\n * spinner belongs to the wrapping `Control`, so set `isLoading` on that `Control`.\n */\n isLoading?: boolean;\n /** Additional CSS classes to apply. */\n className?: string;\n /** Disabled input. */\n disabled?: boolean;\n /** Read-only input. */\n readOnly?: boolean;\n}\n\n/**\n * Bulma Input component with full Bulma helper class support.\n *\n * @function\n * @param {InputBaseProps} props - Props for the Input component.\n * @returns {JSX.Element} The rendered input element.\n * @see {@link https://bulma.io/documentation/form/input/ | Bulma Input documentation}\n */\nexport const InputBase = forwardRef<HTMLInputElement, InputBaseProps>(\n (\n {\n color,\n size,\n isRounded,\n isStatic,\n isHovered,\n isFocused,\n isLoading,\n className,\n disabled,\n readOnly,\n ...props\n },\n ref\n ) => {\n const { bulmaHelperClasses, rest } = useBulmaClasses({\n color,\n ...props,\n });\n const fieldLabelId = useFieldLabelId();\n\n const mainClass = usePrefixedClassNames('input', {\n [`is-${color}`]: !!color,\n [`is-${size}`]: !!size,\n 'is-rounded': isRounded,\n 'is-static': isStatic,\n 'is-hovered': isHovered,\n 'is-focused': isFocused,\n 'is-loading': isLoading,\n });\n const inputClass = classNames(mainClass, bulmaHelperClasses, className);\n\n return (\n <input\n ref={ref}\n className={inputClass}\n disabled={disabled}\n readOnly={readOnly}\n {...rest}\n // After the spread: a labeled Field's generated id is adopted only\n // when no user id arrived (rest.id may be an undefined own key).\n id={rest.id ?? fieldLabelId}\n />\n );\n }\n);\nInputBase.displayName = 'InputBase';\n\nexport default InputBase;\n"],"names":["_jsx"],"mappings":";;;;;;;AA+CA;;;;;;;AAOG;AACI,MAAM,SAAS,GAAG,UAAU,CACjC,CACE,EACE,KAAK,EACL,IAAI,EACJ,SAAS,EACT,QAAQ,EACR,SAAS,EACT,SAAS,EACT,SAAS,EACT,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,GAAG,KAAK,EACT,EACD,GAAG,KACD;AACF,IAAA,MAAM,EAAE,kBAAkB,EAAE,IAAI,EAAE,GAAG,eAAe,CAAC;QACnD,KAAK;AACL,QAAA,GAAG,KAAK;AACT,KAAA,CAAC;AACF,IAAA,MAAM,YAAY,GAAG,eAAe,EAAE;AAEtC,IAAA,MAAM,SAAS,GAAG,qBAAqB,CAAC,OAAO,EAAE;AAC/C,QAAA,CAAC,MAAM,KAAK,CAAA,CAAE,GAAG,CAAC,CAAC,KAAK;AACxB,QAAA,CAAC,MAAM,IAAI,CAAA,CAAE,GAAG,CAAC,CAAC,IAAI;AACtB,QAAA,YAAY,EAAE,SAAS;AACvB,QAAA,WAAW,EAAE,QAAQ;AACrB,QAAA,YAAY,EAAE,SAAS;AACvB,QAAA,YAAY,EAAE,SAAS;AACvB,QAAA,YAAY,EAAE,SAAS;AACxB,KAAA,CAAC;IACF,MAAM,UAAU,GAAG,UAAU,CAAC,SAAS,EAAE,kBAAkB,EAAE,SAAS,CAAC;AAEvE,IAAA,QACEA,GAAA,CAAA,OAAA,EAAA,EACE,GAAG,EAAE,GAAG,EACR,SAAS,EAAE,UAAU,EACrB,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,QAAQ,KACd,IAAI;;;QAGR,EAAE,EAAE,IAAI,CAAC,EAAE,IAAI,YAAY,EAAA,CAC3B;AAEN,CAAC;AAEH,SAAS,CAAC,WAAW,GAAG,WAAW;;"}
|