@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,49 @@
|
|
|
1
|
+
/* @allxsmith/bestax-bulma — AI agents: see AGENTS.md in the package root, or https://bestax.io/llms.txt */
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Distinguishes an `IconProps` object from a plain custom node (an inline SVG, a
|
|
6
|
+
* `react-icons` component, …) in a slot that accepts either — `Control`'s
|
|
7
|
+
* `iconLeft`/`iconRight`, `IconText`'s `iconProps`.
|
|
8
|
+
*
|
|
9
|
+
* One copy, imported by both consumers: two hand-maintained copies, one per
|
|
10
|
+
* consumer, is how the deprecated `icon` path came to crash both (#663).
|
|
11
|
+
*
|
|
12
|
+
* Its own module rather than `Icon.tsx` because `src/index.ts` wildcard-exports
|
|
13
|
+
* that file, and everything published there is public forever. `polymorphic.ts`
|
|
14
|
+
* makes the same call for `isCustomElement`: an internal discriminator is not
|
|
15
|
+
* something to support for the life of the package.
|
|
16
|
+
*
|
|
17
|
+
* It tests each member's VALUE, not merely its key, and only after everything
|
|
18
|
+
* React owns is out of the way. `'icon' in value` was enough
|
|
19
|
+
* to claim a Font Awesome `IconDefinition` — `{ prefix, iconName, icon: [w, h, …,
|
|
20
|
+
* path] }` — whose `icon` is a path array rather than a class string. That object
|
|
21
|
+
* is not a renderable node either, so the honest outcome is the one this slot has
|
|
22
|
+
* always had for it: the node branch, and React's own "Objects are not valid as a
|
|
23
|
+
* React child". Claiming it here instead rendered a blank container that still
|
|
24
|
+
* reserved layout.
|
|
25
|
+
*
|
|
26
|
+
* A member added to `IconProps` needs a line here, and a missed one is a crash
|
|
27
|
+
* rather than a fallthrough. That is the cost of a slot taking either a props
|
|
28
|
+
* object or a node; keeping the guard in one place is what keeps the cost to a
|
|
29
|
+
* single line.
|
|
30
|
+
*/
|
|
31
|
+
function isIconProps(value) {
|
|
32
|
+
if (typeof value !== 'object' || value === null)
|
|
33
|
+
return false;
|
|
34
|
+
// Anything React owns. `isValidElement` covers elements only; a PORTAL is a
|
|
35
|
+
// valid `ReactNode` that fails it and carries a `children` key, so the member
|
|
36
|
+
// test below claimed it and `Control` spread it into `<Icon>` as props —
|
|
37
|
+
// rendering the portal's child locally instead of in its container. Every
|
|
38
|
+
// React internal carries `$$typeof`, so testing for it covers lazy, context
|
|
39
|
+
// and whatever React adds next, rather than portals alone.
|
|
40
|
+
if (React.isValidElement(value) || '$$typeof' in value)
|
|
41
|
+
return false;
|
|
42
|
+
const candidate = value;
|
|
43
|
+
return (typeof candidate.name === 'string' ||
|
|
44
|
+
typeof candidate.icon === 'string' ||
|
|
45
|
+
'children' in candidate);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export { isIconProps };
|
|
49
|
+
//# sourceMappingURL=iconProps.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"iconProps.js","sources":["../../../src/elements/iconProps.ts"],"sourcesContent":["import React from 'react';\nimport type { IconProps } from './Icon';\n\n/**\n * Distinguishes an `IconProps` object from a plain custom node (an inline SVG, a\n * `react-icons` component, …) in a slot that accepts either — `Control`'s\n * `iconLeft`/`iconRight`, `IconText`'s `iconProps`.\n *\n * One copy, imported by both consumers: two hand-maintained copies, one per\n * consumer, is how the deprecated `icon` path came to crash both (#663).\n *\n * Its own module rather than `Icon.tsx` because `src/index.ts` wildcard-exports\n * that file, and everything published there is public forever. `polymorphic.ts`\n * makes the same call for `isCustomElement`: an internal discriminator is not\n * something to support for the life of the package.\n *\n * It tests each member's VALUE, not merely its key, and only after everything\n * React owns is out of the way. `'icon' in value` was enough\n * to claim a Font Awesome `IconDefinition` — `{ prefix, iconName, icon: [w, h, …,\n * path] }` — whose `icon` is a path array rather than a class string. That object\n * is not a renderable node either, so the honest outcome is the one this slot has\n * always had for it: the node branch, and React's own \"Objects are not valid as a\n * React child\". Claiming it here instead rendered a blank container that still\n * reserved layout.\n *\n * A member added to `IconProps` needs a line here, and a missed one is a crash\n * rather than a fallthrough. That is the cost of a slot taking either a props\n * object or a node; keeping the guard in one place is what keeps the cost to a\n * single line.\n */\nexport function isIconProps(\n value: IconProps | React.ReactNode\n): value is IconProps {\n if (typeof value !== 'object' || value === null) return false;\n // Anything React owns. `isValidElement` covers elements only; a PORTAL is a\n // valid `ReactNode` that fails it and carries a `children` key, so the member\n // test below claimed it and `Control` spread it into `<Icon>` as props —\n // rendering the portal's child locally instead of in its container. Every\n // React internal carries `$$typeof`, so testing for it covers lazy, context\n // and whatever React adds next, rather than portals alone.\n if (React.isValidElement(value) || '$$typeof' in value) return false;\n const candidate = value as {\n name?: unknown;\n icon?: unknown;\n children?: unknown;\n };\n return (\n typeof candidate.name === 'string' ||\n typeof candidate.icon === 'string' ||\n 'children' in candidate\n );\n}\n"],"names":[],"mappings":";;;AAGA;;;;;;;;;;;;;;;;;;;;;;;;;;AA0BG;AACG,SAAU,WAAW,CACzB,KAAkC,EAAA;AAElC,IAAA,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;AAAE,QAAA,OAAO,KAAK;;;;;;;IAO7D,IAAI,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,IAAI,UAAU,IAAI,KAAK;AAAE,QAAA,OAAO,KAAK;IACpE,MAAM,SAAS,GAAG,KAIjB;AACD,IAAA,QACE,OAAO,SAAS,CAAC,IAAI,KAAK,QAAQ;AAClC,QAAA,OAAO,SAAS,CAAC,IAAI,KAAK,QAAQ;QAClC,UAAU,IAAI,SAAS;AAE3B;;"}
|
|
@@ -0,0 +1,279 @@
|
|
|
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, useRef, useState, useId, useCallback, useEffect } from 'react';
|
|
4
|
+
import { usePrefixedClassNames, classNames, prefixedClassNames } from '../helpers/classNames.js';
|
|
5
|
+
import { useConfig } from '../helpers/Config.js';
|
|
6
|
+
import { useBulmaClasses } from '../helpers/useBulmaClasses.js';
|
|
7
|
+
import { useInsideField } from './FormContext.js';
|
|
8
|
+
import { Field } from './Field.js';
|
|
9
|
+
import { useAutoLabelId } from './useAutoLabelId.js';
|
|
10
|
+
import { useControlLoading } from './controlLoading.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The `Autocomplete` component provides an input field with dropdown suggestions that filter based on user input.
|
|
14
|
+
*
|
|
15
|
+
* @function
|
|
16
|
+
* @param {AutocompleteProps} props - Props for the Autocomplete component.
|
|
17
|
+
* @param {React.Ref<HTMLInputElement>} ref - Forwarded ref to the input element.
|
|
18
|
+
* @returns {JSX.Element} The rendered autocomplete component.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* // Basic usage with string array
|
|
22
|
+
* <Autocomplete
|
|
23
|
+
* data={['Apple', 'Banana', 'Cherry']}
|
|
24
|
+
* placeholder="Search fruit..."
|
|
25
|
+
* onSelect={(item) => console.log(item)}
|
|
26
|
+
* />
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* // With object data
|
|
30
|
+
* <Autocomplete
|
|
31
|
+
* data={[{ value: '1', label: 'Option 1' }, { value: '2', label: 'Option 2' }]}
|
|
32
|
+
* field="label"
|
|
33
|
+
* onSelect={(item) => console.log(item)}
|
|
34
|
+
* />
|
|
35
|
+
*/
|
|
36
|
+
const Autocomplete = forwardRef(({ data = [], value: controlledValue, placeholder, field = 'label', clearable = false, openOnFocus = false, keepFirst = false, keepOpen = false, selectOnClickOutside = false, maxHeight = 200, loading = false, disabled = false, checkInfiniteScroll = false, infiniteScrollDistance = 50, color, size, onInput, onSelect, onActiveChange, onInfiniteScroll, itemTemplate, header, footer, empty, name, form, required, id,
|
|
37
|
+
// Field props
|
|
38
|
+
label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName, className, ...props }, ref) => {
|
|
39
|
+
const insideField = useInsideField();
|
|
40
|
+
const { controlId, fieldLabelProps } = useAutoLabelId({
|
|
41
|
+
label,
|
|
42
|
+
id,
|
|
43
|
+
labelProps,
|
|
44
|
+
rendersLabel: !insideField,
|
|
45
|
+
});
|
|
46
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses(props);
|
|
47
|
+
const { classPrefix } = useConfig();
|
|
48
|
+
const containerRef = useRef(null);
|
|
49
|
+
const inputRef = useRef(null);
|
|
50
|
+
const dropdownRef = useRef(null);
|
|
51
|
+
const [internalValue, setInternalValue] = useState('');
|
|
52
|
+
const [isActive, setIsActive] = useState(false);
|
|
53
|
+
const [highlightedIndex, setHighlightedIndex] = useState(-1);
|
|
54
|
+
const listboxId = `${useId()}-listbox`;
|
|
55
|
+
// Use controlled or internal value
|
|
56
|
+
const inputValue = controlledValue !== undefined ? controlledValue : internalValue;
|
|
57
|
+
// Get display value from item
|
|
58
|
+
const getDisplayValue = (item) => {
|
|
59
|
+
if (typeof item === 'string')
|
|
60
|
+
return item;
|
|
61
|
+
const fieldValue = item[field];
|
|
62
|
+
if (typeof fieldValue === 'string')
|
|
63
|
+
return fieldValue;
|
|
64
|
+
return item.value || '';
|
|
65
|
+
};
|
|
66
|
+
// Filter data based on input
|
|
67
|
+
const filteredData = data.filter(item => {
|
|
68
|
+
const displayValue = getDisplayValue(item);
|
|
69
|
+
return displayValue.toLowerCase().includes(inputValue.toLowerCase());
|
|
70
|
+
});
|
|
71
|
+
// Handle input change
|
|
72
|
+
const handleInputChange = useCallback((e) => {
|
|
73
|
+
const newValue = e.target.value;
|
|
74
|
+
if (controlledValue === undefined) {
|
|
75
|
+
setInternalValue(newValue);
|
|
76
|
+
}
|
|
77
|
+
onInput?.(newValue);
|
|
78
|
+
if (!isActive && newValue) {
|
|
79
|
+
setIsActive(true);
|
|
80
|
+
}
|
|
81
|
+
}, [controlledValue, isActive, onInput]);
|
|
82
|
+
// Handle item selection
|
|
83
|
+
const handleSelect = useCallback((item) => {
|
|
84
|
+
if (typeof item !== 'string' && item.disabled)
|
|
85
|
+
return;
|
|
86
|
+
const displayValue = getDisplayValue(item);
|
|
87
|
+
if (controlledValue === undefined) {
|
|
88
|
+
setInternalValue(displayValue);
|
|
89
|
+
}
|
|
90
|
+
onInput?.(displayValue);
|
|
91
|
+
onSelect?.(item);
|
|
92
|
+
if (!keepOpen) {
|
|
93
|
+
setIsActive(false);
|
|
94
|
+
}
|
|
95
|
+
setHighlightedIndex(-1);
|
|
96
|
+
}, [controlledValue, field, keepOpen, onInput, onSelect]);
|
|
97
|
+
// Handle clear
|
|
98
|
+
const handleClear = useCallback(() => {
|
|
99
|
+
if (controlledValue === undefined) {
|
|
100
|
+
setInternalValue('');
|
|
101
|
+
}
|
|
102
|
+
onInput?.('');
|
|
103
|
+
onSelect?.(null);
|
|
104
|
+
inputRef.current?.focus();
|
|
105
|
+
}, [controlledValue, onInput, onSelect]);
|
|
106
|
+
// Handle focus
|
|
107
|
+
const handleFocus = useCallback(() => {
|
|
108
|
+
if (openOnFocus && !disabled) {
|
|
109
|
+
setIsActive(true);
|
|
110
|
+
}
|
|
111
|
+
}, [openOnFocus, disabled]);
|
|
112
|
+
// Handle blur (click outside)
|
|
113
|
+
useEffect(() => {
|
|
114
|
+
if (!isActive)
|
|
115
|
+
return undefined;
|
|
116
|
+
const handleClickOutside = (e) => {
|
|
117
|
+
if (!containerRef.current)
|
|
118
|
+
return;
|
|
119
|
+
// Use composedPath() to correctly detect clicks inside Shadow DOM
|
|
120
|
+
const path = e.composedPath();
|
|
121
|
+
const isInside = path.includes(containerRef.current);
|
|
122
|
+
if (!isInside) {
|
|
123
|
+
if (selectOnClickOutside && highlightedIndex >= 0) {
|
|
124
|
+
handleSelect(filteredData[highlightedIndex]);
|
|
125
|
+
}
|
|
126
|
+
setIsActive(false);
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
document.addEventListener('mousedown', handleClickOutside);
|
|
130
|
+
return () => document.removeEventListener('mousedown', handleClickOutside);
|
|
131
|
+
}, [
|
|
132
|
+
isActive,
|
|
133
|
+
selectOnClickOutside,
|
|
134
|
+
highlightedIndex,
|
|
135
|
+
filteredData,
|
|
136
|
+
handleSelect,
|
|
137
|
+
]);
|
|
138
|
+
// Notify active state change
|
|
139
|
+
useEffect(() => {
|
|
140
|
+
onActiveChange?.(isActive);
|
|
141
|
+
}, [isActive, onActiveChange]);
|
|
142
|
+
// Keep first highlighted
|
|
143
|
+
useEffect(() => {
|
|
144
|
+
if (keepFirst && isActive && filteredData.length > 0) {
|
|
145
|
+
setHighlightedIndex(0);
|
|
146
|
+
}
|
|
147
|
+
}, [keepFirst, isActive, filteredData.length, inputValue]);
|
|
148
|
+
// Handle keyboard navigation
|
|
149
|
+
const handleKeyDown = useCallback((e) => {
|
|
150
|
+
if (disabled)
|
|
151
|
+
return;
|
|
152
|
+
switch (e.key) {
|
|
153
|
+
case 'ArrowDown':
|
|
154
|
+
e.preventDefault();
|
|
155
|
+
if (!isActive) {
|
|
156
|
+
setIsActive(true);
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
setHighlightedIndex(prev => prev < filteredData.length - 1 ? prev + 1 : prev);
|
|
160
|
+
}
|
|
161
|
+
break;
|
|
162
|
+
case 'ArrowUp':
|
|
163
|
+
e.preventDefault();
|
|
164
|
+
setHighlightedIndex(prev => (prev > 0 ? prev - 1 : prev));
|
|
165
|
+
break;
|
|
166
|
+
case 'Enter':
|
|
167
|
+
e.preventDefault();
|
|
168
|
+
if (highlightedIndex >= 0 && filteredData[highlightedIndex]) {
|
|
169
|
+
handleSelect(filteredData[highlightedIndex]);
|
|
170
|
+
}
|
|
171
|
+
break;
|
|
172
|
+
case 'Escape':
|
|
173
|
+
e.preventDefault();
|
|
174
|
+
setIsActive(false);
|
|
175
|
+
setHighlightedIndex(-1);
|
|
176
|
+
break;
|
|
177
|
+
case 'Tab':
|
|
178
|
+
if (highlightedIndex >= 0 && filteredData[highlightedIndex]) {
|
|
179
|
+
handleSelect(filteredData[highlightedIndex]);
|
|
180
|
+
}
|
|
181
|
+
setIsActive(false);
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
}, [disabled, isActive, highlightedIndex, filteredData, handleSelect]);
|
|
185
|
+
// Handle dropdown scroll for infinite scroll
|
|
186
|
+
const handleDropdownScroll = useCallback(() => {
|
|
187
|
+
if (!checkInfiniteScroll || !dropdownRef.current)
|
|
188
|
+
return;
|
|
189
|
+
const { scrollTop, scrollHeight, clientHeight } = dropdownRef.current;
|
|
190
|
+
if (scrollHeight - scrollTop - clientHeight <= infiniteScrollDistance) {
|
|
191
|
+
onInfiniteScroll?.();
|
|
192
|
+
}
|
|
193
|
+
}, [checkInfiniteScroll, infiniteScrollDistance, onInfiniteScroll]);
|
|
194
|
+
// Scroll highlighted item into view
|
|
195
|
+
useEffect(() => {
|
|
196
|
+
if (highlightedIndex >= 0 && dropdownRef.current) {
|
|
197
|
+
const highlightedEl = dropdownRef.current.querySelector(`[data-index="${highlightedIndex}"]`);
|
|
198
|
+
if (highlightedEl &&
|
|
199
|
+
typeof highlightedEl.scrollIntoView === 'function') {
|
|
200
|
+
highlightedEl.scrollIntoView({ block: 'nearest' });
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}, [highlightedIndex]);
|
|
204
|
+
// Combined ref
|
|
205
|
+
const combinedRef = useCallback((node) => {
|
|
206
|
+
inputRef.current =
|
|
207
|
+
node;
|
|
208
|
+
if (typeof ref === 'function') {
|
|
209
|
+
ref(node);
|
|
210
|
+
}
|
|
211
|
+
else if (ref) {
|
|
212
|
+
ref.current =
|
|
213
|
+
node;
|
|
214
|
+
}
|
|
215
|
+
}, [ref]);
|
|
216
|
+
// Generate classes
|
|
217
|
+
const autocompleteClasses = usePrefixedClassNames('autocomplete', {
|
|
218
|
+
'is-active': isActive,
|
|
219
|
+
[`is-${size}`]: !!size,
|
|
220
|
+
});
|
|
221
|
+
const inputClasses = usePrefixedClassNames('input', {
|
|
222
|
+
[`is-${color}`]: !!color,
|
|
223
|
+
[`is-${size}`]: !!size,
|
|
224
|
+
'is-loading': loading,
|
|
225
|
+
});
|
|
226
|
+
const controlClasses = usePrefixedClassNames('control', 'is-expanded', 'has-icons-right');
|
|
227
|
+
const dropdownMenuClasses = usePrefixedClassNames('dropdown-menu', {
|
|
228
|
+
'is-active': isActive && (filteredData.length > 0 || !!empty),
|
|
229
|
+
});
|
|
230
|
+
const dropdownContentClass = usePrefixedClassNames('dropdown-content');
|
|
231
|
+
const dropdownHeaderClass = usePrefixedClassNames('dropdown-header');
|
|
232
|
+
const dropdownFooterClass = usePrefixedClassNames('dropdown-footer');
|
|
233
|
+
const emptyItemClasses = usePrefixedClassNames('dropdown-item', 'has-text-grey');
|
|
234
|
+
const iconRightClickableClass = usePrefixedClassNames('icon', 'is-right', 'is-clickable');
|
|
235
|
+
const iconRightClass = usePrefixedClassNames('icon', 'is-right');
|
|
236
|
+
const loaderClass = usePrefixedClassNames('loader', 'is-loading');
|
|
237
|
+
const combinedClasses = classNames(autocompleteClasses, bulmaHelperClasses, className);
|
|
238
|
+
const helpClass = usePrefixedClassNames('help', {
|
|
239
|
+
[`is-${messageColor}`]: !!messageColor,
|
|
240
|
+
});
|
|
241
|
+
const messageEl = message ? jsx("p", { className: helpClass, children: message }) : null;
|
|
242
|
+
// The clear button gives way to a spinner at its spot: the `loading` one,
|
|
243
|
+
// or that of a loading Control this sits in.
|
|
244
|
+
const controlLoading = useControlLoading();
|
|
245
|
+
const showClear = clearable && !!inputValue && !disabled && !loading && !controlLoading;
|
|
246
|
+
// The combobox controls its listbox and points at the highlighted option.
|
|
247
|
+
// The listbox takes its name from the label this renders, or a fallback
|
|
248
|
+
// when it renders none.
|
|
249
|
+
const listOpen = isActive && (filteredData.length > 0 || !!empty);
|
|
250
|
+
const listLabelId = !insideField && label
|
|
251
|
+
? (fieldLabelProps?.id ?? `${listboxId}-label`)
|
|
252
|
+
: undefined;
|
|
253
|
+
const activeOptionId = listOpen &&
|
|
254
|
+
highlightedIndex >= 0 &&
|
|
255
|
+
highlightedIndex < filteredData.length
|
|
256
|
+
? `${listboxId}-${highlightedIndex}`
|
|
257
|
+
: undefined;
|
|
258
|
+
const autocompleteElement = (jsxs("div", { ref: containerRef, className: combinedClasses, ...rest, children: [jsxs("div", { className: controlClasses, children: [jsx("input", { ref: combinedRef, type: "text", className: inputClasses, id: controlId, value: inputValue, placeholder: placeholder, disabled: disabled, name: name, form: form, required: required, onChange: handleInputChange, onFocus: handleFocus, onKeyDown: handleKeyDown, role: "combobox", "aria-expanded": listOpen, "aria-controls": listOpen ? listboxId : undefined, "aria-activedescendant": activeOptionId, "aria-haspopup": "listbox", "aria-autocomplete": "list", autoComplete: "off" }), showClear && (jsx("button", { type: "button", className: iconRightClickableClass, onClick: handleClear, "aria-label": "Clear", children: jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", width: "16", height: "16", "aria-hidden": "true", children: [jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }), jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })] }) })), loading && (jsx("span", { className: iconRightClass, children: jsx("span", { className: loaderClass }) }))] }), listOpen && (jsx("div", { className: dropdownMenuClasses, children: jsxs("div", { ref: dropdownRef, className: dropdownContentClass, style: { maxHeight: `${maxHeight}px`, overflowY: 'auto' }, id: listboxId, role: "listbox", "aria-labelledby": listLabelId, "aria-label": listLabelId ? undefined : 'Suggestions', onScroll: handleDropdownScroll, children: [header && jsx("div", { className: dropdownHeaderClass, children: header }), filteredData.length > 0 ? (filteredData.map((item, index) => {
|
|
259
|
+
const isDisabled = typeof item !== 'string' && item.disabled;
|
|
260
|
+
const isHighlighted = index === highlightedIndex;
|
|
261
|
+
const itemClasses = prefixedClassNames(classPrefix, 'dropdown-item', {
|
|
262
|
+
'is-active': isHighlighted,
|
|
263
|
+
'is-disabled': isDisabled,
|
|
264
|
+
});
|
|
265
|
+
return (jsx("a", { id: `${listboxId}-${index}`, "data-index": index, className: itemClasses, onClick: () => !isDisabled && handleSelect(item), onMouseEnter: () => !isDisabled && setHighlightedIndex(index), role: "option", "aria-selected": isHighlighted, "aria-disabled": isDisabled, children: itemTemplate
|
|
266
|
+
? itemTemplate(item)
|
|
267
|
+
: getDisplayValue(item) }, index));
|
|
268
|
+
})) : (jsx("div", { className: emptyItemClasses, children: empty })), footer && jsx("div", { className: dropdownFooterClass, children: footer })] }) }))] }));
|
|
269
|
+
if (!insideField) {
|
|
270
|
+
return (jsxs(Field, { label: label, labelSize: labelSize, labelProps: listLabelId
|
|
271
|
+
? { ...fieldLabelProps, id: listLabelId }
|
|
272
|
+
: fieldLabelProps, horizontal: horizontal, className: fieldClassName, children: [autocompleteElement, messageEl] }));
|
|
273
|
+
}
|
|
274
|
+
return (jsxs(Fragment, { children: [autocompleteElement, messageEl] }));
|
|
275
|
+
});
|
|
276
|
+
Autocomplete.displayName = 'Autocomplete';
|
|
277
|
+
|
|
278
|
+
export { Autocomplete };
|
|
279
|
+
//# sourceMappingURL=Autocomplete.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Autocomplete.js","sources":["../../../src/form/Autocomplete.tsx"],"sourcesContent":["import React, {\n forwardRef,\n useState,\n useRef,\n useEffect,\n useCallback,\n useId,\n} from 'react';\nimport {\n classNames,\n usePrefixedClassNames,\n prefixedClassNames,\n} from '../helpers/classNames';\nimport { useConfig } from '../helpers/Config';\nimport { useBulmaClasses, BulmaClassesProps } from '../helpers/useBulmaClasses';\nimport { useInsideField } from './FormContext';\nimport { Field } from './Field';\nimport { FormFieldProps } from './fieldProps';\nimport { useAutoLabelId } from './useAutoLabelId';\nimport { useControlLoading } from './controlLoading';\n\n/**\n * An item in the Autocomplete dropdown list.\n */\nexport interface AutocompleteItem {\n /** The value used for filtering and selection. */\n value: string;\n /** Display label (optional). */\n label?: string;\n /** Whether the item is disabled and unselectable. */\n disabled?: boolean;\n [key: string]: unknown;\n}\n\n/**\n * Props for the Autocomplete component.\n * @extraProp {string} [className] - Additional CSS classes.\n * @extraProp {React.Ref<HTMLInputElement>} [ref] - Ref forwarded to the input element.\n */\nexport interface AutocompleteProps\n extends\n Omit<React.HTMLAttributes<HTMLDivElement>, 'onSelect' | 'onInput'>,\n Omit<BulmaClassesProps, 'color'>,\n FormFieldProps {\n /** Field label. Automatically associated with the text input via `htmlFor` — uses your `id` when provided, otherwise a generated one. 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 /** Applied to the inner text input (the labellable control), not the wrapper div. */\n id?: string;\n /** The options data to display (required). */\n data: AutocompleteItem[] | string[];\n /** The current input value (controlled). */\n value?: string;\n /** The selected item (controlled). */\n selected?: AutocompleteItem | string | null;\n /** Placeholder text for the input. */\n placeholder?: string;\n /** Object property to use as the display field. */\n field?: string;\n /**\n * Whether to show a clear button while the input holds a value.\n * It hides while the input is disabled, and gives way to a loading\n * spinner at the same right edge: the one `loading` draws, or that of a\n * `Control` with `isLoading` around the Autocomplete.\n */\n clearable?: boolean;\n /** Open dropdown when input is focused. */\n openOnFocus?: boolean;\n /** Keep first option highlighted. */\n keepFirst?: boolean;\n /** Keep dropdown open after selection. */\n keepOpen?: boolean;\n /** Select highlighted item on click outside. */\n selectOnClickOutside?: boolean;\n /** Maximum dropdown height in pixels. */\n maxHeight?: number;\n /** Render as dropdown style. */\n dropdown?: boolean;\n /**\n * Show a loading spinner in the input.\n * Under `prefers-reduced-motion: reduce` the spinner stops and stays\n * drawn (with bestax's CSS loaded).\n */\n loading?: boolean;\n /** Whether the input is disabled. */\n disabled?: boolean;\n /** Enables infinite scroll detection in the dropdown. */\n checkInfiniteScroll?: boolean;\n /** Distance in pixels from the bottom to trigger `onInfiniteScroll`. */\n infiniteScrollDistance?: number;\n /** Input color variant. */\n color?: 'primary' | 'link' | 'info' | 'success' | 'warning' | 'danger';\n /** Size variant. */\n size?: 'small' | 'medium' | 'large';\n /** Form field name. Forwarded to the inner `<input>`. */\n name?: string;\n /** Optional id of the form the input belongs to. */\n form?: string;\n /** Marks the field as required for native HTML form validation. */\n required?: boolean;\n /** Callback when input value changes. */\n onInput?: (value: string) => void;\n /** Callback when item is selected. */\n onSelect?: (item: AutocompleteItem | string | null) => void;\n /** Callback when dropdown active state changes. */\n onActiveChange?: (active: boolean) => void;\n /** Callback when scrolled to bottom (infinite scroll). */\n onInfiniteScroll?: () => void;\n /** Custom render for items. */\n itemTemplate?: (item: AutocompleteItem | string) => React.ReactNode;\n /** Custom header in dropdown. */\n header?: React.ReactNode;\n /** Custom footer in dropdown. */\n footer?: React.ReactNode;\n /** Content to show when no results. */\n empty?: React.ReactNode;\n}\n\n/**\n * The `Autocomplete` component provides an input field with dropdown suggestions that filter based on user input.\n *\n * @function\n * @param {AutocompleteProps} props - Props for the Autocomplete component.\n * @param {React.Ref<HTMLInputElement>} ref - Forwarded ref to the input element.\n * @returns {JSX.Element} The rendered autocomplete component.\n *\n * @example\n * // Basic usage with string array\n * <Autocomplete\n * data={['Apple', 'Banana', 'Cherry']}\n * placeholder=\"Search fruit...\"\n * onSelect={(item) => console.log(item)}\n * />\n *\n * @example\n * // With object data\n * <Autocomplete\n * data={[{ value: '1', label: 'Option 1' }, { value: '2', label: 'Option 2' }]}\n * field=\"label\"\n * onSelect={(item) => console.log(item)}\n * />\n */\nexport const Autocomplete = forwardRef<HTMLInputElement, AutocompleteProps>(\n (\n {\n data = [],\n value: controlledValue,\n placeholder,\n field = 'label',\n clearable = false,\n openOnFocus = false,\n keepFirst = false,\n keepOpen = false,\n selectOnClickOutside = false,\n maxHeight = 200,\n loading = false,\n disabled = false,\n checkInfiniteScroll = false,\n infiniteScrollDistance = 50,\n color,\n size,\n onInput,\n onSelect,\n onActiveChange,\n onInfiniteScroll,\n itemTemplate,\n header,\n footer,\n empty,\n name,\n form,\n required,\n id,\n // Field props\n label,\n labelSize,\n labelProps,\n horizontal,\n message,\n messageColor,\n fieldClassName,\n className,\n ...props\n },\n ref\n ) => {\n const insideField = useInsideField();\n const { controlId, fieldLabelProps } = useAutoLabelId({\n label,\n id,\n labelProps,\n rendersLabel: !insideField,\n });\n const { bulmaHelperClasses, rest } = useBulmaClasses(props);\n const { classPrefix } = useConfig();\n const containerRef = useRef<HTMLDivElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n const dropdownRef = useRef<HTMLDivElement>(null);\n\n const [internalValue, setInternalValue] = useState('');\n const [isActive, setIsActive] = useState(false);\n const [highlightedIndex, setHighlightedIndex] = useState(-1);\n const listboxId = `${useId()}-listbox`;\n\n // Use controlled or internal value\n const inputValue =\n controlledValue !== undefined ? controlledValue : internalValue;\n\n // Get display value from item\n const getDisplayValue = (item: AutocompleteItem | string): string => {\n if (typeof item === 'string') return item;\n const fieldValue = item[field];\n if (typeof fieldValue === 'string') return fieldValue;\n return item.value || '';\n };\n\n // Filter data based on input\n const filteredData = data.filter(item => {\n const displayValue = getDisplayValue(item);\n return displayValue.toLowerCase().includes(inputValue.toLowerCase());\n });\n\n // Handle input change\n const handleInputChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n const newValue = e.target.value;\n if (controlledValue === undefined) {\n setInternalValue(newValue);\n }\n onInput?.(newValue);\n if (!isActive && newValue) {\n setIsActive(true);\n }\n },\n [controlledValue, isActive, onInput]\n );\n\n // Handle item selection\n const handleSelect = useCallback(\n (item: AutocompleteItem | string) => {\n if (typeof item !== 'string' && item.disabled) return;\n\n const displayValue = getDisplayValue(item);\n if (controlledValue === undefined) {\n setInternalValue(displayValue);\n }\n onInput?.(displayValue);\n onSelect?.(item);\n\n if (!keepOpen) {\n setIsActive(false);\n }\n setHighlightedIndex(-1);\n },\n [controlledValue, field, keepOpen, onInput, onSelect]\n );\n\n // Handle clear\n const handleClear = useCallback(() => {\n if (controlledValue === undefined) {\n setInternalValue('');\n }\n onInput?.('');\n onSelect?.(null);\n inputRef.current?.focus();\n }, [controlledValue, onInput, onSelect]);\n\n // Handle focus\n const handleFocus = useCallback(() => {\n if (openOnFocus && !disabled) {\n setIsActive(true);\n }\n }, [openOnFocus, disabled]);\n\n // Handle blur (click outside)\n useEffect(() => {\n if (!isActive) return undefined;\n\n const handleClickOutside = (e: MouseEvent) => {\n if (!containerRef.current) return;\n\n // Use composedPath() to correctly detect clicks inside Shadow DOM\n const path = e.composedPath();\n const isInside = path.includes(containerRef.current);\n\n if (!isInside) {\n if (selectOnClickOutside && highlightedIndex >= 0) {\n handleSelect(filteredData[highlightedIndex]);\n }\n setIsActive(false);\n }\n };\n\n document.addEventListener('mousedown', handleClickOutside);\n return () =>\n document.removeEventListener('mousedown', handleClickOutside);\n }, [\n isActive,\n selectOnClickOutside,\n highlightedIndex,\n filteredData,\n handleSelect,\n ]);\n\n // Notify active state change\n useEffect(() => {\n onActiveChange?.(isActive);\n }, [isActive, onActiveChange]);\n\n // Keep first highlighted\n useEffect(() => {\n if (keepFirst && isActive && filteredData.length > 0) {\n setHighlightedIndex(0);\n }\n }, [keepFirst, isActive, filteredData.length, inputValue]);\n\n // Handle keyboard navigation\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent) => {\n if (disabled) return;\n\n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault();\n if (!isActive) {\n setIsActive(true);\n } else {\n setHighlightedIndex(prev =>\n prev < filteredData.length - 1 ? prev + 1 : prev\n );\n }\n break;\n case 'ArrowUp':\n e.preventDefault();\n setHighlightedIndex(prev => (prev > 0 ? prev - 1 : prev));\n break;\n case 'Enter':\n e.preventDefault();\n if (highlightedIndex >= 0 && filteredData[highlightedIndex]) {\n handleSelect(filteredData[highlightedIndex]);\n }\n break;\n case 'Escape':\n e.preventDefault();\n setIsActive(false);\n setHighlightedIndex(-1);\n break;\n case 'Tab':\n if (highlightedIndex >= 0 && filteredData[highlightedIndex]) {\n handleSelect(filteredData[highlightedIndex]);\n }\n setIsActive(false);\n break;\n }\n },\n [disabled, isActive, highlightedIndex, filteredData, handleSelect]\n );\n\n // Handle dropdown scroll for infinite scroll\n const handleDropdownScroll = useCallback(() => {\n if (!checkInfiniteScroll || !dropdownRef.current) return;\n\n const { scrollTop, scrollHeight, clientHeight } = dropdownRef.current;\n if (scrollHeight - scrollTop - clientHeight <= infiniteScrollDistance) {\n onInfiniteScroll?.();\n }\n }, [checkInfiniteScroll, infiniteScrollDistance, onInfiniteScroll]);\n\n // Scroll highlighted item into view\n useEffect(() => {\n if (highlightedIndex >= 0 && dropdownRef.current) {\n const highlightedEl = dropdownRef.current.querySelector(\n `[data-index=\"${highlightedIndex}\"]`\n );\n if (\n highlightedEl &&\n typeof highlightedEl.scrollIntoView === 'function'\n ) {\n highlightedEl.scrollIntoView({ block: 'nearest' });\n }\n }\n }, [highlightedIndex]);\n\n // Combined ref\n const combinedRef = useCallback(\n (node: HTMLInputElement | null) => {\n (inputRef as React.MutableRefObject<HTMLInputElement | null>).current =\n node;\n if (typeof ref === 'function') {\n ref(node);\n } else if (ref) {\n (ref as React.MutableRefObject<HTMLInputElement | null>).current =\n node;\n }\n },\n [ref]\n );\n\n // Generate classes\n const autocompleteClasses = usePrefixedClassNames('autocomplete', {\n 'is-active': isActive,\n [`is-${size}`]: !!size,\n });\n\n const inputClasses = usePrefixedClassNames('input', {\n [`is-${color}`]: !!color,\n [`is-${size}`]: !!size,\n 'is-loading': loading,\n });\n\n const controlClasses = usePrefixedClassNames(\n 'control',\n 'is-expanded',\n 'has-icons-right'\n );\n\n const dropdownMenuClasses = usePrefixedClassNames('dropdown-menu', {\n 'is-active': isActive && (filteredData.length > 0 || !!empty),\n });\n\n const dropdownContentClass = usePrefixedClassNames('dropdown-content');\n const dropdownHeaderClass = usePrefixedClassNames('dropdown-header');\n const dropdownFooterClass = usePrefixedClassNames('dropdown-footer');\n const emptyItemClasses = usePrefixedClassNames(\n 'dropdown-item',\n 'has-text-grey'\n );\n\n const iconRightClickableClass = usePrefixedClassNames(\n 'icon',\n 'is-right',\n 'is-clickable'\n );\n const iconRightClass = usePrefixedClassNames('icon', 'is-right');\n const loaderClass = usePrefixedClassNames('loader', 'is-loading');\n\n const combinedClasses = classNames(\n autocompleteClasses,\n bulmaHelperClasses,\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 // The clear button gives way to a spinner at its spot: the `loading` one,\n // or that of a loading Control this sits in.\n const controlLoading = useControlLoading();\n const showClear =\n clearable && !!inputValue && !disabled && !loading && !controlLoading;\n\n // The combobox controls its listbox and points at the highlighted option.\n // The listbox takes its name from the label this renders, or a fallback\n // when it renders none.\n const listOpen = isActive && (filteredData.length > 0 || !!empty);\n const listLabelId =\n !insideField && label\n ? ((fieldLabelProps?.id as string | undefined) ?? `${listboxId}-label`)\n : undefined;\n const activeOptionId =\n listOpen &&\n highlightedIndex >= 0 &&\n highlightedIndex < filteredData.length\n ? `${listboxId}-${highlightedIndex}`\n : undefined;\n\n const autocompleteElement = (\n <div ref={containerRef} className={combinedClasses} {...rest}>\n <div className={controlClasses}>\n <input\n ref={combinedRef}\n type=\"text\"\n className={inputClasses}\n id={controlId}\n value={inputValue}\n placeholder={placeholder}\n disabled={disabled}\n name={name}\n form={form}\n required={required}\n onChange={handleInputChange}\n onFocus={handleFocus}\n onKeyDown={handleKeyDown}\n role=\"combobox\"\n aria-expanded={listOpen}\n aria-controls={listOpen ? listboxId : undefined}\n aria-activedescendant={activeOptionId}\n aria-haspopup=\"listbox\"\n aria-autocomplete=\"list\"\n autoComplete=\"off\"\n />\n {showClear && (\n <button\n type=\"button\"\n className={iconRightClickableClass}\n onClick={handleClear}\n aria-label=\"Clear\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n width=\"16\"\n height=\"16\"\n aria-hidden=\"true\"\n >\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n </button>\n )}\n {loading && (\n <span className={iconRightClass}>\n <span className={loaderClass} />\n </span>\n )}\n </div>\n\n {listOpen && (\n <div className={dropdownMenuClasses}>\n <div\n ref={dropdownRef}\n className={dropdownContentClass}\n style={{ maxHeight: `${maxHeight}px`, overflowY: 'auto' }}\n id={listboxId}\n role=\"listbox\"\n aria-labelledby={listLabelId}\n aria-label={listLabelId ? undefined : 'Suggestions'}\n onScroll={handleDropdownScroll}\n >\n {header && <div className={dropdownHeaderClass}>{header}</div>}\n\n {filteredData.length > 0 ? (\n filteredData.map((item, index) => {\n const isDisabled = typeof item !== 'string' && item.disabled;\n const isHighlighted = index === highlightedIndex;\n\n const itemClasses = prefixedClassNames(\n classPrefix,\n 'dropdown-item',\n {\n 'is-active': isHighlighted,\n 'is-disabled': isDisabled,\n }\n );\n\n return (\n <a\n key={index}\n id={`${listboxId}-${index}`}\n data-index={index}\n className={itemClasses}\n onClick={() => !isDisabled && handleSelect(item)}\n onMouseEnter={() =>\n !isDisabled && setHighlightedIndex(index)\n }\n role=\"option\"\n aria-selected={isHighlighted}\n aria-disabled={isDisabled}\n >\n {itemTemplate\n ? itemTemplate(item)\n : getDisplayValue(item)}\n </a>\n );\n })\n ) : (\n <div className={emptyItemClasses}>{empty}</div>\n )}\n\n {footer && <div className={dropdownFooterClass}>{footer}</div>}\n </div>\n </div>\n )}\n </div>\n );\n\n if (!insideField) {\n return (\n <Field\n label={label}\n labelSize={labelSize}\n labelProps={\n listLabelId\n ? { ...fieldLabelProps, id: listLabelId }\n : fieldLabelProps\n }\n horizontal={horizontal}\n className={fieldClassName}\n >\n {autocompleteElement}\n {messageEl}\n </Field>\n );\n }\n\n return (\n <>\n {autocompleteElement}\n {messageEl}\n </>\n );\n }\n);\n\nAutocomplete.displayName = 'Autocomplete';\n\nexport default Autocomplete;\n"],"names":["_jsx","_jsxs","_Fragment"],"mappings":"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"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/* @allxsmith/bestax-bulma — AI agents: see AGENTS.md in the package root, or https://bestax.io/llms.txt */
|
|
2
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
|
+
import { forwardRef, useCallback } from 'react';
|
|
4
|
+
import { usePrefixedClassNames, classNames } from '../helpers/classNames.js';
|
|
5
|
+
import { useBulmaClasses } from '../helpers/useBulmaClasses.js';
|
|
6
|
+
import { useCheckboxesGroup } from './FormContext.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The values the Checkbox `color` prop accepts, as a readonly tuple.
|
|
10
|
+
*
|
|
11
|
+
* `CheckboxProps['color']` is typed from it, so the two list the same values.
|
|
12
|
+
* Map over it to build a color picker, or check a value that arrives at
|
|
13
|
+
* runtime before passing it in: the component adds no color class for a value
|
|
14
|
+
* outside the tuple.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* import { Checkbox, checkboxColors } from '@allxsmith/bestax-bulma';
|
|
18
|
+
*
|
|
19
|
+
* checkboxColors.map(color => (
|
|
20
|
+
* <Checkbox key={color} color={color}>
|
|
21
|
+
* {color}
|
|
22
|
+
* </Checkbox>
|
|
23
|
+
* ));
|
|
24
|
+
*/
|
|
25
|
+
const checkboxColors = [
|
|
26
|
+
'primary',
|
|
27
|
+
'link',
|
|
28
|
+
'info',
|
|
29
|
+
'success',
|
|
30
|
+
'warning',
|
|
31
|
+
'danger',
|
|
32
|
+
];
|
|
33
|
+
/**
|
|
34
|
+
* The values the Checkbox `size` prop accepts, as a readonly tuple.
|
|
35
|
+
*
|
|
36
|
+
* `CheckboxProps['size']` is typed from it, so the two list the same values.
|
|
37
|
+
* Use it to offer a size choice or to check a value that arrives at runtime:
|
|
38
|
+
* the component adds no size class for a value outside the tuple. These are
|
|
39
|
+
* element sizes, not the spacing scale in `validSizes`.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* import { checkboxSizes } from '@allxsmith/bestax-bulma';
|
|
43
|
+
*
|
|
44
|
+
* type CheckboxSize = (typeof checkboxSizes)[number];
|
|
45
|
+
*/
|
|
46
|
+
const checkboxSizes = ['small', 'normal', 'medium', 'large'];
|
|
47
|
+
/**
|
|
48
|
+
* The `Checkbox` component provides a Bulma-styled checkbox input.
|
|
49
|
+
*
|
|
50
|
+
* @function
|
|
51
|
+
* @param {CheckboxProps} props - Props for the Checkbox component.
|
|
52
|
+
* @returns {JSX.Element} The rendered checkbox element.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* // Basic checkbox
|
|
56
|
+
* <Checkbox>Accept terms</Checkbox>
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* // Inside a group
|
|
60
|
+
* <Checkboxes name="tags" defaultValue={['react']}>
|
|
61
|
+
* <Checkbox value="react">React</Checkbox>
|
|
62
|
+
* <Checkbox value="vue">Vue</Checkbox>
|
|
63
|
+
* </Checkboxes>
|
|
64
|
+
*/
|
|
65
|
+
const Checkbox = forwardRef(({ color, size, className, children, textColor, disabled, name, value, checked, defaultChecked, onChange, ...props }, ref) => {
|
|
66
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
67
|
+
color: textColor,
|
|
68
|
+
...props,
|
|
69
|
+
});
|
|
70
|
+
// Inherit name + selection state from a surrounding <Checkboxes> group.
|
|
71
|
+
// Local props always win over the group (explicit > implicit).
|
|
72
|
+
const group = useCheckboxesGroup();
|
|
73
|
+
const effectiveName = name ?? group?.name;
|
|
74
|
+
// Group-managed checked state — only when the group is in
|
|
75
|
+
// controlled/uncontrolled mode (group.value is defined) AND this Checkbox
|
|
76
|
+
// has a value to compare against. Local `checked` always wins.
|
|
77
|
+
const groupManaged = group?.value !== undefined && value !== undefined;
|
|
78
|
+
const groupHas = groupManaged && group.value.includes(String(value));
|
|
79
|
+
const effectiveChecked = checked !== undefined ? checked : groupManaged ? groupHas : undefined;
|
|
80
|
+
// When the group manages checked, suppress local defaultChecked to avoid
|
|
81
|
+
// the controlled/uncontrolled React warning.
|
|
82
|
+
const effectiveDefaultChecked = groupManaged ? undefined : defaultChecked;
|
|
83
|
+
const handleChange = useCallback((e) => {
|
|
84
|
+
// Local onChange always fires.
|
|
85
|
+
onChange?.(e);
|
|
86
|
+
// Dispatch to the group additionally — toggle this value's
|
|
87
|
+
// membership in the group's array.
|
|
88
|
+
if (group?.onChange && value !== undefined) {
|
|
89
|
+
const valStr = String(value);
|
|
90
|
+
const currentArr = group.value ?? [];
|
|
91
|
+
const next = e.target.checked
|
|
92
|
+
? currentArr.includes(valStr)
|
|
93
|
+
? currentArr
|
|
94
|
+
: [...currentArr, valStr]
|
|
95
|
+
: currentArr.filter(v => v !== valStr);
|
|
96
|
+
group.onChange(next);
|
|
97
|
+
}
|
|
98
|
+
}, [onChange, group, value]);
|
|
99
|
+
const mainClass = usePrefixedClassNames('styled-checkbox', 'checkbox', {
|
|
100
|
+
[`is-${color}`]: color && checkboxColors.includes(color),
|
|
101
|
+
[`is-${size}`]: size && checkboxSizes.includes(size),
|
|
102
|
+
});
|
|
103
|
+
const checkboxClass = classNames(mainClass, bulmaHelperClasses, className);
|
|
104
|
+
const checkClass = usePrefixedClassNames('check');
|
|
105
|
+
const controlLabelClass = usePrefixedClassNames('control-label');
|
|
106
|
+
return (jsxs("label", { className: checkboxClass, children: [jsx("input", { ref: ref, type: "checkbox", disabled: disabled, name: effectiveName, value: value, checked: effectiveChecked, defaultChecked: effectiveDefaultChecked, onChange: handleChange, ...rest }), jsx("span", { className: checkClass }), children && jsx("span", { className: controlLabelClass, children: children })] }));
|
|
107
|
+
});
|
|
108
|
+
Checkbox.displayName = 'Checkbox';
|
|
109
|
+
|
|
110
|
+
export { Checkbox, checkboxColors, checkboxSizes };
|
|
111
|
+
//# sourceMappingURL=Checkbox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Checkbox.js","sources":["../../../src/form/Checkbox.tsx"],"sourcesContent":["import React, { forwardRef, useCallback } from 'react';\nimport { classNames, usePrefixedClassNames } from '../helpers/classNames';\nimport {\n useBulmaClasses,\n BulmaClassesProps,\n validColors,\n} from '../helpers/useBulmaClasses';\nimport { useCheckboxesGroup } from './FormContext';\n\n/**\n * The values the Checkbox `color` prop accepts, as a readonly tuple.\n *\n * `CheckboxProps['color']` is typed from it, so the two list the same values.\n * Map over it to build a color picker, or check a value that arrives at\n * runtime before passing it in: the component adds no color class for a value\n * outside the tuple.\n *\n * @example\n * import { Checkbox, checkboxColors } from '@allxsmith/bestax-bulma';\n *\n * checkboxColors.map(color => (\n * <Checkbox key={color} color={color}>\n * {color}\n * </Checkbox>\n * ));\n */\nexport const checkboxColors = [\n 'primary',\n 'link',\n 'info',\n 'success',\n 'warning',\n 'danger',\n] as const;\n\n/**\n * The values the Checkbox `size` prop accepts, as a readonly tuple.\n *\n * `CheckboxProps['size']` is typed from it, so the two list the same values.\n * Use it to offer a size choice or to check a value that arrives at runtime:\n * the component adds no size class for a value outside the tuple. These are\n * element sizes, not the spacing scale in `validSizes`.\n *\n * @example\n * import { checkboxSizes } from '@allxsmith/bestax-bulma';\n *\n * type CheckboxSize = (typeof checkboxSizes)[number];\n */\nexport const checkboxSizes = ['small', 'normal', 'medium', 'large'] as const;\n\n/**\n * Props for the Checkbox component.\n */\nexport interface CheckboxProps\n extends\n Omit<\n React.InputHTMLAttributes<HTMLInputElement>,\n 'size' | 'type' | 'color'\n >,\n Omit<BulmaClassesProps, 'color' | 'backgroundColor' | 'size'> {\n /** Color of the checkbox. */\n color?: (typeof checkboxColors)[number];\n /** Size of the checkbox. */\n size?: (typeof checkboxSizes)[number];\n /** Text color helper. */\n textColor?: (typeof validColors)[number] | 'inherit' | 'current';\n /** Whether the checkbox is disabled. */\n disabled?: boolean;\n /** Additional CSS classes to apply. */\n className?: string;\n /** The label/content for the checkbox. */\n children?: React.ReactNode;\n}\n\n/**\n * The `Checkbox` component provides a Bulma-styled checkbox input.\n *\n * @function\n * @param {CheckboxProps} props - Props for the Checkbox component.\n * @returns {JSX.Element} The rendered checkbox element.\n *\n * @example\n * // Basic checkbox\n * <Checkbox>Accept terms</Checkbox>\n *\n * @example\n * // Inside a group\n * <Checkboxes name=\"tags\" defaultValue={['react']}>\n * <Checkbox value=\"react\">React</Checkbox>\n * <Checkbox value=\"vue\">Vue</Checkbox>\n * </Checkboxes>\n */\nexport const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(\n (\n {\n color,\n size,\n className,\n children,\n textColor,\n disabled,\n name,\n value,\n checked,\n defaultChecked,\n onChange,\n ...props\n },\n ref\n ) => {\n const { bulmaHelperClasses, rest } = useBulmaClasses({\n color: textColor,\n ...props,\n });\n\n // Inherit name + selection state from a surrounding <Checkboxes> group.\n // Local props always win over the group (explicit > implicit).\n const group = useCheckboxesGroup();\n const effectiveName = name ?? group?.name;\n\n // Group-managed checked state — only when the group is in\n // controlled/uncontrolled mode (group.value is defined) AND this Checkbox\n // has a value to compare against. Local `checked` always wins.\n const groupManaged = group?.value !== undefined && value !== undefined;\n const groupHas = groupManaged && group!.value!.includes(String(value));\n const effectiveChecked =\n checked !== undefined ? checked : groupManaged ? groupHas : undefined;\n // When the group manages checked, suppress local defaultChecked to avoid\n // the controlled/uncontrolled React warning.\n const effectiveDefaultChecked = groupManaged ? undefined : defaultChecked;\n\n const handleChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n // Local onChange always fires.\n onChange?.(e);\n // Dispatch to the group additionally — toggle this value's\n // membership in the group's array.\n if (group?.onChange && value !== undefined) {\n const valStr = String(value);\n const currentArr = group.value ?? [];\n const next = e.target.checked\n ? currentArr.includes(valStr)\n ? currentArr\n : [...currentArr, valStr]\n : currentArr.filter(v => v !== valStr);\n group.onChange(next);\n }\n },\n [onChange, group, value]\n );\n\n const mainClass = usePrefixedClassNames('styled-checkbox', 'checkbox', {\n [`is-${color}`]: color && checkboxColors.includes(color),\n [`is-${size}`]: size && checkboxSizes.includes(size),\n });\n const checkboxClass = classNames(mainClass, bulmaHelperClasses, className);\n const checkClass = usePrefixedClassNames('check');\n const controlLabelClass = usePrefixedClassNames('control-label');\n\n return (\n <label className={checkboxClass}>\n <input\n ref={ref}\n type=\"checkbox\"\n disabled={disabled}\n name={effectiveName}\n value={value}\n checked={effectiveChecked}\n defaultChecked={effectiveDefaultChecked}\n onChange={handleChange}\n {...rest}\n />\n <span className={checkClass} />\n {children && <span className={controlLabelClass}>{children}</span>}\n </label>\n );\n }\n);\n\nCheckbox.displayName = 'Checkbox';\n\nexport default Checkbox;\n"],"names":["_jsxs","_jsx"],"mappings":"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"}
|
|
@@ -0,0 +1,89 @@
|
|
|
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 { useState, useCallback, useMemo } from 'react';
|
|
4
|
+
import { usePrefixedClassNames, classNames } from '../helpers/classNames.js';
|
|
5
|
+
import { withSubComponents } from '../helpers/withSubComponents.js';
|
|
6
|
+
import { useBulmaClasses } from '../helpers/useBulmaClasses.js';
|
|
7
|
+
import { useInsideField, useInsideControl, rendersOwnField, CheckboxesProvider, rendersOwnControl } from './FormContext.js';
|
|
8
|
+
import { Field } from './Field.js';
|
|
9
|
+
import { Control } from './Control.js';
|
|
10
|
+
import { useAutoLabelledBy } from './useAutoLabelId.js';
|
|
11
|
+
import { Checkbox } from './Checkbox.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The `Checkboxes` component wraps multiple `Checkbox` components in a Bulma-styled group.
|
|
15
|
+
*
|
|
16
|
+
* @function
|
|
17
|
+
* @param {CheckboxesProps} props - Props for the Checkboxes component.
|
|
18
|
+
* @returns {JSX.Element} The rendered checkboxes group.
|
|
19
|
+
* @see {@link https://bulma.io/documentation/form/checkbox/#grouped-checkboxes | Bulma Checkboxes documentation}
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* // Controlled
|
|
23
|
+
* const [tags, setTags] = useState(['react']);
|
|
24
|
+
* <Checkboxes name="tags" value={tags} onChange={setTags}>
|
|
25
|
+
* <Checkbox value="react">React</Checkbox>
|
|
26
|
+
* <Checkbox value="vue">Vue</Checkbox>
|
|
27
|
+
* <Checkbox value="angular">Angular</Checkbox>
|
|
28
|
+
* </Checkboxes>
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* // Uncontrolled
|
|
32
|
+
* <Checkboxes name="tags" defaultValue={['react', 'vue']}>
|
|
33
|
+
* <Checkbox value="react">React</Checkbox>
|
|
34
|
+
* <Checkbox value="vue">Vue</Checkbox>
|
|
35
|
+
* <Checkbox value="angular">Angular</Checkbox>
|
|
36
|
+
* </Checkboxes>
|
|
37
|
+
*/
|
|
38
|
+
const CheckboxesComponent = ({ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName, name, value, defaultValue, onChange, children, className, ...props }) => {
|
|
39
|
+
const insideField = useInsideField();
|
|
40
|
+
const insideControl = useInsideControl();
|
|
41
|
+
const ownField = rendersOwnField('Checkboxes', {
|
|
42
|
+
insideField,
|
|
43
|
+
insideControl,
|
|
44
|
+
label,
|
|
45
|
+
message,
|
|
46
|
+
horizontal,
|
|
47
|
+
fieldClassName,
|
|
48
|
+
});
|
|
49
|
+
const { ariaLabelledBy, fieldLabelProps } = useAutoLabelledBy({
|
|
50
|
+
label,
|
|
51
|
+
labelProps,
|
|
52
|
+
rendersLabel: ownField,
|
|
53
|
+
});
|
|
54
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
55
|
+
...props,
|
|
56
|
+
});
|
|
57
|
+
const mainClass = usePrefixedClassNames('checkboxes');
|
|
58
|
+
const wrapperClass = classNames(mainClass, bulmaHelperClasses, className);
|
|
59
|
+
const helpClass = usePrefixedClassNames('help', {
|
|
60
|
+
[`is-${messageColor}`]: !!messageColor,
|
|
61
|
+
});
|
|
62
|
+
const messageEl = message ? jsx("p", { className: helpClass, children: message }) : null;
|
|
63
|
+
const groupActive = value !== undefined || defaultValue !== undefined || onChange !== undefined;
|
|
64
|
+
const [internalValue, setInternalValue] = useState(defaultValue);
|
|
65
|
+
const isControlled = value !== undefined;
|
|
66
|
+
const currentValue = isControlled ? value : internalValue;
|
|
67
|
+
const handleChange = useCallback((newValues) => {
|
|
68
|
+
if (!isControlled)
|
|
69
|
+
setInternalValue(newValues);
|
|
70
|
+
onChange?.(newValues);
|
|
71
|
+
}, [isControlled, onChange]);
|
|
72
|
+
const ctx = useMemo(() => ({
|
|
73
|
+
name,
|
|
74
|
+
...(groupActive ? { value: currentValue, onChange: handleChange } : {}),
|
|
75
|
+
}), [name, groupActive, currentValue, handleChange]);
|
|
76
|
+
const checkboxesElement = (jsx("div", { className: wrapperClass, role: "group", "aria-labelledby": ariaLabelledBy, ...rest, children: jsx(CheckboxesProvider, { value: ctx, children: children }) }));
|
|
77
|
+
let content = checkboxesElement;
|
|
78
|
+
if (rendersOwnControl('Checkboxes', { insideControl })) {
|
|
79
|
+
content = jsx(Control, { children: content });
|
|
80
|
+
}
|
|
81
|
+
if (ownField) {
|
|
82
|
+
return (jsxs(Field, { label: label, labelSize: labelSize, labelProps: fieldLabelProps, horizontal: horizontal, className: fieldClassName, children: [content, messageEl] }));
|
|
83
|
+
}
|
|
84
|
+
return (jsxs(Fragment, { children: [content, messageEl] }));
|
|
85
|
+
};
|
|
86
|
+
const Checkboxes = withSubComponents(CheckboxesComponent, { Checkbox }, 'Checkboxes');
|
|
87
|
+
|
|
88
|
+
export { Checkboxes };
|
|
89
|
+
//# sourceMappingURL=Checkboxes.js.map
|