@allxsmith/bestax-bulma 5.33.1 → 5.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bestax.css +1 -1
- package/dist/bestax.css.map +1 -1
- package/dist/esm/components/EmptyState.js +139 -0
- package/dist/esm/components/EmptyState.js.map +1 -0
- package/dist/esm/elements/Buttons.js.map +1 -1
- package/dist/esm/elements/SubTitle.js.map +1 -1
- package/dist/esm/elements/Title.js.map +1 -1
- package/dist/extras.css +1 -1
- package/dist/extras.css.map +1 -1
- package/dist/index.cjs +1079 -948
- package/dist/index.cjs.map +1 -1
- package/dist/index.esm.js +1 -0
- package/dist/index.esm.js.map +1 -1
- package/dist/types/components/EmptyState.d.ts +149 -0
- package/dist/types/elements/Buttons.d.ts +1 -1
- package/dist/types/elements/SubTitle.d.ts +13 -2
- package/dist/types/elements/Title.d.ts +11 -2
- package/dist/types/index.d.ts +1 -0
- package/dist/types-cjs/components/EmptyState.d.ts +149 -0
- package/dist/types-cjs/elements/Buttons.d.ts +1 -1
- package/dist/types-cjs/elements/SubTitle.d.ts +13 -2
- package/dist/types-cjs/elements/Title.d.ts +11 -2
- package/dist/types-cjs/index.d.ts +1 -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/_emptystate.scss +243 -0
- package/src/scss/components/_index.scss +1 -0
package/dist/index.cjs
CHANGED
|
@@ -2841,237 +2841,921 @@ const Dropdown = withSubComponents(DropdownComponent, {
|
|
|
2841
2841
|
Divider: DropdownDivider,
|
|
2842
2842
|
}, 'Dropdown');
|
|
2843
2843
|
|
|
2844
|
-
|
|
2845
|
-
const MenuListLevelContext = React.createContext(0);
|
|
2844
|
+
const validButtonColors = [...validColors, 'text', 'ghost'];
|
|
2846
2845
|
/**
|
|
2847
|
-
* The `
|
|
2846
|
+
* The `Button` component provides a flexible and highly customizable button for your Bulma React UI.
|
|
2848
2847
|
*
|
|
2849
2848
|
* @function
|
|
2850
|
-
* @param {
|
|
2851
|
-
* @
|
|
2852
|
-
* @
|
|
2849
|
+
* @param {ButtonProps} props - Props for the Button component.
|
|
2850
|
+
* @param {React.Ref} ref - Forwarded ref to the element `as` renders.
|
|
2851
|
+
* @returns {JSX.Element} The rendered button, anchor, or custom element.
|
|
2852
|
+
* @see {@link https://bulma.io/documentation/elements/button/ | Bulma Button documentation}
|
|
2853
2853
|
*/
|
|
2854
|
-
const
|
|
2855
|
-
const {
|
|
2854
|
+
const Button = React.forwardRef(function Button(props, ref) {
|
|
2855
|
+
const { color, size, isLight, isRounded, isLoading, isStatic, isFullwidth, isFullWidth, isOutlined, isInverted, isFocused, isActive, isHovered, isDisabled, className, children, textColor, bgColor, as: Component = 'button', href, onClick, target, rel, ...bulmaProps } = props;
|
|
2856
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
2857
|
+
color: textColor,
|
|
2858
|
+
backgroundColor: bgColor,
|
|
2859
|
+
...bulmaProps,
|
|
2860
|
+
});
|
|
2856
2861
|
// Generate Bulma classes with prefix
|
|
2857
|
-
const bulmaClasses = usePrefixedClassNames('
|
|
2858
|
-
|
|
2859
|
-
|
|
2862
|
+
const bulmaClasses = usePrefixedClassNames('button', {
|
|
2863
|
+
[`is-${color}`]: color &&
|
|
2864
|
+
validButtonColors.includes(color),
|
|
2865
|
+
[`is-${size}`]: size,
|
|
2866
|
+
'is-outlined': isOutlined,
|
|
2867
|
+
'is-light': isLight,
|
|
2868
|
+
'is-loading': isLoading,
|
|
2869
|
+
'is-static': isStatic,
|
|
2870
|
+
'is-disabled': isDisabled,
|
|
2871
|
+
'is-rounded': isRounded,
|
|
2872
|
+
'is-hovered': isHovered,
|
|
2873
|
+
'is-focused': isFocused,
|
|
2874
|
+
'is-active': isActive,
|
|
2875
|
+
'is-inverted': isInverted,
|
|
2876
|
+
'is-fullwidth': isFullwidth ?? isFullWidth,
|
|
2877
|
+
});
|
|
2878
|
+
// Combine prefixed Bulma classes with unprefixed user className and prefixed helper classes
|
|
2879
|
+
const buttonClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
|
|
2880
|
+
if (Component !== 'button') {
|
|
2881
|
+
// Strip the form-control attributes a link-like element cannot carry, so a
|
|
2882
|
+
// JavaScript consumer or a spread object doesn't put them on the DOM. The
|
|
2883
|
+
// types no longer allow them through; this is the runtime backstop.
|
|
2884
|
+
//
|
|
2885
|
+
// Only for INTRINSIC tags. A custom component (a router Link, ...) owns its
|
|
2886
|
+
// prop contract, and `ComponentPropsWithoutRef<T>` promises the caller that
|
|
2887
|
+
// its props reach it. Stripping names by their spelling alone broke that
|
|
2888
|
+
// promise silently: `<Button as={Custom} name="x" />` type-checked while
|
|
2889
|
+
// `Custom` never received the `name` it requires.
|
|
2890
|
+
//
|
|
2891
|
+
// `autoFocus` and `type` are deliberately NOT stripped: both are valid on
|
|
2892
|
+
// targets this list was filtering them from. `autoFocus` is a global
|
|
2893
|
+
// attribute (React decides per element whether to honour it, which is not
|
|
2894
|
+
// ours to pre-empt), and `type` on an anchor is the MIME hint that
|
|
2895
|
+
// `ButtonProps<'a'>` now types it as.
|
|
2896
|
+
//
|
|
2897
|
+
// The types already answer this for a TypeScript caller: `ButtonProps<T>`
|
|
2898
|
+
// derives from `ComponentPropsWithoutRef<T>`, so only what the target
|
|
2899
|
+
// accepts can be passed. What is left is a runtime backstop for JavaScript
|
|
2900
|
+
// consumers and spread objects.
|
|
2901
|
+
//
|
|
2902
|
+
// Keep it to the two cases where a stray attribute does something. The
|
|
2903
|
+
// submit-overrides belong to a submit control and never make sense on the
|
|
2904
|
+
// anchor path. `disabled` is the one that is VISIBLE: Bulma styles
|
|
2905
|
+
// `.button[disabled]` (background, border, shadow, opacity), so letting it
|
|
2906
|
+
// through to a `<span>` greys the element out — main stripped it, and not
|
|
2907
|
+
// stripping it would be a silent visual change on a types-only release.
|
|
2908
|
+
//
|
|
2909
|
+
// Everything else (`name`, `value`, `form`) is inert where it does not
|
|
2910
|
+
// belong, and enumerating which tags own it is what kept going wrong:
|
|
2911
|
+
// first all nine names on every non-button tag (so `<Button as="input"
|
|
2912
|
+
// name="query" value="Search">` submitted nothing), then a form-control
|
|
2913
|
+
// allowlist that still lost `formAction` on an input and `value` on an
|
|
2914
|
+
// option. Those now ride on the types instead.
|
|
2915
|
+
//
|
|
2916
|
+
// A custom component is exempt entirely — it owns its prop contract, and
|
|
2917
|
+
// `ComponentPropsWithoutRef<T>` promises the caller its props arrive.
|
|
2918
|
+
//
|
|
2919
|
+
// The elements that own `disabled`, per the HTML spec. A closed set, unlike
|
|
2920
|
+
// the open-ended question of which tag owns which attribute.
|
|
2921
|
+
const SUBMIT_OVERRIDES = [
|
|
2922
|
+
'formAction',
|
|
2923
|
+
'formEncType',
|
|
2924
|
+
'formMethod',
|
|
2925
|
+
'formNoValidate',
|
|
2926
|
+
'formTarget',
|
|
2927
|
+
];
|
|
2928
|
+
const OWNS_DISABLED = [
|
|
2929
|
+
'button',
|
|
2930
|
+
'fieldset',
|
|
2931
|
+
'input',
|
|
2932
|
+
'optgroup',
|
|
2933
|
+
'option',
|
|
2934
|
+
'select',
|
|
2935
|
+
'textarea',
|
|
2936
|
+
];
|
|
2937
|
+
// Two independent filters. They are not exclusive — an `<a>` owns neither
|
|
2938
|
+
// the submit-overrides nor `disabled`, so it takes both — and deriving them
|
|
2939
|
+
// from one destructure, or from one if/else, is what broke each of them in
|
|
2940
|
+
// turn: first the overrides came off every intrinsic (an `as="input"` lost
|
|
2941
|
+
// `formAction`), then scoping them to the anchor let `disabled` back onto
|
|
2942
|
+
// it.
|
|
2943
|
+
const forwardedRest = { ...rest };
|
|
2944
|
+
if (typeof Component === 'string' && !isCustomElement(Component)) {
|
|
2945
|
+
// Meaningless anywhere but a submit control, and an anchor is not one.
|
|
2946
|
+
if (Component === 'a') {
|
|
2947
|
+
for (const key of SUBMIT_OVERRIDES)
|
|
2948
|
+
delete forwardedRest[key];
|
|
2949
|
+
}
|
|
2950
|
+
// Visible via Bulma's `.button[disabled]`, so withheld from any element
|
|
2951
|
+
// that does not own it.
|
|
2952
|
+
if (!OWNS_DISABLED.includes(Component))
|
|
2953
|
+
delete forwardedRest.disabled;
|
|
2954
|
+
}
|
|
2955
|
+
return (jsxRuntime.jsx(Component, { ref: ref, className: buttonClasses, href: href, target: target, rel: rel, "aria-disabled": isDisabled, tabIndex: isDisabled ? -1 : undefined, onClick: isDisabled
|
|
2956
|
+
? // Guarded, because this handler is installed into whatever `as`
|
|
2957
|
+
// renders and a custom component's `onClick` need not be a DOM
|
|
2958
|
+
// event handler at all. One declaring `onClick: (v: string) =>
|
|
2959
|
+
// void` calls this with a string, and an unguarded
|
|
2960
|
+
// `e.preventDefault()` throws `is not a function`. Blocking the
|
|
2961
|
+
// real event is the point; a foreign payload is simply not ours
|
|
2962
|
+
// to act on.
|
|
2963
|
+
(e) => {
|
|
2964
|
+
if (typeof e?.preventDefault === 'function')
|
|
2965
|
+
e.preventDefault();
|
|
2966
|
+
}
|
|
2967
|
+
: onClick, ...forwardedRest, children: children }));
|
|
2968
|
+
}
|
|
2969
|
+
return (jsxRuntime.jsx("button", { ref: ref, className: buttonClasses, disabled: isDisabled, onClick: onClick, ...rest, children: children }));
|
|
2970
|
+
});
|
|
2971
|
+
Button.displayName = 'Button';
|
|
2972
|
+
|
|
2860
2973
|
/**
|
|
2861
|
-
*
|
|
2974
|
+
* The `LinkButton` component renders a `<button>` that visually looks like text or a link.
|
|
2862
2975
|
*
|
|
2863
2976
|
* @function
|
|
2864
|
-
* @param {
|
|
2865
|
-
* @
|
|
2977
|
+
* @param {LinkButtonProps} props - Props for the LinkButton component.
|
|
2978
|
+
* @param {React.Ref} ref - Forwarded ref to the element `as` renders.
|
|
2979
|
+
* @returns {JSX.Element} The rendered link-styled button element.
|
|
2980
|
+
*
|
|
2981
|
+
* @example
|
|
2982
|
+
* // Text variant (default)
|
|
2983
|
+
* <LinkButton onClick={handleClick}>Click me</LinkButton>
|
|
2984
|
+
*
|
|
2985
|
+
* @example
|
|
2986
|
+
* // Underline variant with color
|
|
2987
|
+
* <LinkButton variant="underline" color="primary">Learn more</LinkButton>
|
|
2866
2988
|
*/
|
|
2867
|
-
const
|
|
2868
|
-
const
|
|
2869
|
-
|
|
2870
|
-
|
|
2989
|
+
const LinkButton = React.forwardRef(function LinkButton({ variant = 'text', color, className, ...props }, ref) {
|
|
2990
|
+
const buttonColor = variant === 'underline' ? 'text' : variant;
|
|
2991
|
+
const prefixedClasses = usePrefixedClassNames('link-button', color && `link-button-${color}`, variant === 'underline' && 'link-button-underline');
|
|
2992
|
+
return (jsxRuntime.jsx(Button
|
|
2993
|
+
// LinkButton is polymorphic through to Button, which resolves the element
|
|
2994
|
+
// from `as`. TS cannot infer that through the spread below.
|
|
2995
|
+
, {
|
|
2996
|
+
// LinkButton is polymorphic through to Button, which resolves the element
|
|
2997
|
+
// from `as`. TS cannot infer that through the spread below.
|
|
2998
|
+
ref: ref, color: buttonColor, className: classNames(prefixedClasses, className), ...props }));
|
|
2999
|
+
});
|
|
3000
|
+
LinkButton.displayName = 'LinkButton';
|
|
3001
|
+
|
|
2871
3002
|
/**
|
|
2872
|
-
*
|
|
3003
|
+
* The `ToggleButton` component renders a button that stays pressed until it's pressed again, and says whether it's on through `aria-pressed`.
|
|
3004
|
+
*
|
|
3005
|
+
* It's always a `<button type="button">`, so it never submits a form and
|
|
3006
|
+
* carries no form value: a setting saved with a form is a `Checkbox` or a
|
|
3007
|
+
* `Switch`. A set of toggles that each turn on and off on their own goes in
|
|
3008
|
+
* `Buttons hasAddons` with `role="group"` and an `aria-label`. A set where only
|
|
3009
|
+
* one can be on is a radio group rather than a row of these: use
|
|
3010
|
+
* `SegmentedControl`, or `Radios` for a choice that's sent with a form.
|
|
2873
3011
|
*
|
|
2874
3012
|
* @function
|
|
2875
|
-
* @param {
|
|
2876
|
-
* @
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
*
|
|
2889
|
-
* no-op, the way both siblings' sets are.
|
|
3013
|
+
* @param {ToggleButtonProps} props - Props for the ToggleButton component.
|
|
3014
|
+
* @param {React.Ref<HTMLButtonElement>} ref - Forwarded ref to the `<button>`.
|
|
3015
|
+
* @returns {JSX.Element} The rendered toggle button.
|
|
3016
|
+
* @see {@link https://www.w3.org/WAI/ARIA/apg/patterns/button/ | APG button pattern}
|
|
3017
|
+
*
|
|
3018
|
+
* @example
|
|
3019
|
+
* <ToggleButton onPressedChange={setMuted}>Mute</ToggleButton>
|
|
3020
|
+
*
|
|
3021
|
+
* @example
|
|
3022
|
+
* <Buttons hasAddons role="group" aria-label="Text style">
|
|
3023
|
+
* <ToggleButton aria-label="Bold" pressed={bold} onPressedChange={setBold}>
|
|
3024
|
+
* <Icon name="bold" />
|
|
3025
|
+
* </ToggleButton>
|
|
3026
|
+
* </Buttons>
|
|
2890
3027
|
*/
|
|
2891
|
-
const
|
|
3028
|
+
const ToggleButton = React.forwardRef(function ToggleButton({ pressed: controlledPressed, defaultPressed = false, onPressedChange, color = 'link', isLoading, 'aria-busy': ariaBusy, 'aria-disabled': ariaDisabled, className, onClick, ...props }, ref) {
|
|
3029
|
+
const isControlled = controlledPressed !== undefined;
|
|
3030
|
+
const [internalPressed, setInternalPressed] = React.useState(defaultPressed);
|
|
3031
|
+
const pressed = controlledPressed ?? internalPressed;
|
|
3032
|
+
// Prefixed like Button's own classes, which a hand-written
|
|
3033
|
+
// `className="is-selected"` could not be under ConfigProvider classPrefix.
|
|
3034
|
+
// Bulma lifts `is-selected` above its neighbours in `.buttons.has-addons`.
|
|
3035
|
+
const selectedClass = usePrefixedClassNames({ 'is-selected': pressed });
|
|
3036
|
+
// The caller's onClick runs first, so a `preventDefault()` there vetoes
|
|
3037
|
+
// the toggle, as it does in Radix, MUI and Zag. Enter and Space reach here
|
|
3038
|
+
// through the button's own click, and a disabled button gets no click.
|
|
3039
|
+
//
|
|
3040
|
+
// A loading one gets the click but drops it. Bulma's spinner only stops a
|
|
3041
|
+
// pointer, so without this a key flipped a toggle the mouse couldn't.
|
|
3042
|
+
// `disabled` would stop both, but it takes focus away mid-save.
|
|
3043
|
+
const handleClick = (event) => {
|
|
3044
|
+
if (isLoading)
|
|
3045
|
+
return;
|
|
3046
|
+
onClick?.(event);
|
|
3047
|
+
if (event.defaultPrevented)
|
|
3048
|
+
return;
|
|
3049
|
+
const next = !pressed;
|
|
3050
|
+
onPressedChange?.(next);
|
|
3051
|
+
if (!isControlled)
|
|
3052
|
+
setInternalPressed(next);
|
|
3053
|
+
};
|
|
3054
|
+
// The props are spread first, so what a toggle depends on wins over
|
|
3055
|
+
// anything a JavaScript caller or a spread object passes along: it stays
|
|
3056
|
+
// a button, never submits, reports the state it's in, and says it's busy
|
|
3057
|
+
// and won't respond while it's loading. A caller's own `aria-busy` or
|
|
3058
|
+
// `aria-disabled` stays when it isn't loading.
|
|
3059
|
+
return (jsxRuntime.jsx(Button, { ...props, ref: ref, as: "button", type: "button", isLoading: isLoading, "aria-busy": isLoading || ariaBusy, "aria-disabled": isLoading || ariaDisabled, "aria-pressed": pressed, color: pressed ? color : undefined, className: classNames(selectedClass, className), onClick: handleClick }));
|
|
3060
|
+
});
|
|
3061
|
+
ToggleButton.displayName = 'ToggleButton';
|
|
3062
|
+
|
|
3063
|
+
const validButtonsSizes = ['small', 'medium', 'large'];
|
|
2892
3064
|
/**
|
|
2893
|
-
*
|
|
3065
|
+
* The `Buttons` component lets you group multiple `Button` elements together with Bulma's spacing, alignment, and add-on features.
|
|
2894
3066
|
*
|
|
2895
3067
|
* @function
|
|
2896
|
-
* @param {
|
|
2897
|
-
* @
|
|
2898
|
-
* @
|
|
3068
|
+
* @param {ButtonsProps} props - Props for the Buttons component.
|
|
3069
|
+
* @returns {JSX.Element} The rendered group of buttons.
|
|
3070
|
+
* @see {@link https://bulma.io/documentation/elements/button/#group | Bulma Button Group documentation}
|
|
2899
3071
|
*/
|
|
2900
|
-
const
|
|
2901
|
-
const
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
// intrinsic-only strip. Before #641 `href` was an own prop re-applied only
|
|
2907
|
-
// for `as="a"`; deriving it from `as` must not quietly widen that.
|
|
2908
|
-
const isLinkLike = Component === 'a' ||
|
|
2909
|
-
typeof Component !== 'string' ||
|
|
2910
|
-
isCustomElement(Component);
|
|
2911
|
-
// `href` ALONE. `Dropdown.Item` strips every anchor-only attribute except
|
|
2912
|
-
// `type`; `Level.Item` strips all of them, plus `rel`.
|
|
2913
|
-
//
|
|
2914
|
-
// Not because nothing else can arrive: `as` here is an open `React.ElementType`,
|
|
2915
|
-
// and a non-fresh spread skips excess-property checking, so a typed caller
|
|
2916
|
-
// CAN deliver `target` or `download` and they are forwarded. The set stays at
|
|
2917
|
-
// `href` because widening it would be wrong for an open `as` — `referrerPolicy`
|
|
2918
|
-
// on `as="img"`, `target` on `as="form"` and `media` on `as="source"` are all
|
|
2919
|
-
// legal, and a flat strip would delete them. Getting that right needs a
|
|
2920
|
-
// per-attribute-per-element rule, which `bestax-migrate` already models in
|
|
2921
|
-
// `LINK_ATTR_ELEMENTS`.
|
|
2922
|
-
const linkProps = isLinkLike
|
|
2923
|
-
? forwarded
|
|
2924
|
-
: omitAttrs(forwarded, STRIP_FROM_NON_LINK);
|
|
2925
|
-
const itemClass = classNames({ [usePrefixedClassNames('is-active')]: active }, bulmaHelperClasses);
|
|
2926
|
-
// Split children into label and nested MenuList(s)
|
|
2927
|
-
const labelChildren = [];
|
|
2928
|
-
const nestedMenuLists = [];
|
|
2929
|
-
React.Children.forEach(children, child => {
|
|
2930
|
-
if (React.isValidElement(child) && child.type === MenuList) {
|
|
2931
|
-
nestedMenuLists.push(child);
|
|
2932
|
-
}
|
|
2933
|
-
else {
|
|
2934
|
-
labelChildren.push(child);
|
|
2935
|
-
}
|
|
3072
|
+
const ButtonsComponent = ({ className, textColor, color, bgColor, isCentered, isRight, hasAddons, size, children, ...props }) => {
|
|
3073
|
+
const buttonsClasses = usePrefixedClassNames('buttons', {
|
|
3074
|
+
'is-centered': isCentered,
|
|
3075
|
+
'is-right': isRight,
|
|
3076
|
+
'has-addons': hasAddons,
|
|
3077
|
+
[`are-${size}`]: size && validButtonsSizes.includes(size),
|
|
2936
3078
|
});
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
});
|
|
2941
|
-
MenuItem.displayName = 'MenuItem';
|
|
2942
|
-
// Attach static subcomponents
|
|
2943
|
-
const Menu = withSubComponents(MenuComponent, {
|
|
2944
|
-
Label: MenuLabel,
|
|
2945
|
-
List: MenuList,
|
|
2946
|
-
Item: MenuItem,
|
|
2947
|
-
}, 'Menu');
|
|
2948
|
-
|
|
2949
|
-
const validMessageSizes = ['small', 'medium', 'large'];
|
|
2950
|
-
/**
|
|
2951
|
-
* The `Message` component provides Bulma's flexible notice/message box for your Bulma React UI.
|
|
2952
|
-
*
|
|
2953
|
-
* @function
|
|
2954
|
-
* @param {MessageProps} props - Props for the Message component.
|
|
2955
|
-
* @returns {JSX.Element} The rendered message.
|
|
2956
|
-
* @see {@link https://bulma.io/documentation/components/message/ | Bulma Message documentation}
|
|
2957
|
-
*/
|
|
2958
|
-
const MessageComponent = ({ className, title, textColor, color, bgColor, size, onClose, children, ...props }) => {
|
|
2959
|
-
const { classPrefix } = useConfig();
|
|
3079
|
+
/**
|
|
3080
|
+
* Generates Bulma helper classes and separates out remaining props.
|
|
3081
|
+
*/
|
|
2960
3082
|
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
2961
|
-
color: textColor,
|
|
3083
|
+
color: textColor ?? color,
|
|
2962
3084
|
backgroundColor: bgColor,
|
|
2963
3085
|
...props,
|
|
2964
3086
|
});
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
[`is-${color}`]: color,
|
|
2968
|
-
[`is-${size}`]: size && validMessageSizes.includes(size),
|
|
2969
|
-
});
|
|
2970
|
-
const deleteClass = usePrefixedClassNames('delete');
|
|
2971
|
-
const messageClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
|
|
2972
|
-
return (jsxRuntime.jsxs("article", { className: messageClasses, ...rest, "data-testid": "message", children: [(title || onClose) && (jsxRuntime.jsxs("div", { className: prefixedClassNames(classPrefix, 'message-header'), children: [title && jsxRuntime.jsx("span", { children: title }), onClose && (jsxRuntime.jsx("button", { className: deleteClass, "aria-label": "delete", onClick: onClose, type: "button", "data-testid": "message-close" }))] })), children && (jsxRuntime.jsx("div", { className: prefixedClassNames(classPrefix, 'message-body'), "data-testid": "message-body", children: children }))] }));
|
|
3087
|
+
const combinedClasses = classNames(buttonsClasses, className, bulmaHelperClasses);
|
|
3088
|
+
return (jsxRuntime.jsx("div", { className: combinedClasses, ...rest, children: children }));
|
|
2973
3089
|
};
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
* @returns {JSX.Element} The rendered message header.
|
|
2980
|
-
*/
|
|
2981
|
-
const MessageHeader = ({ className, children, ...props }) => (jsxRuntime.jsx("div", { className: classNames(usePrefixedClassNames('message-header'), className), ...props, children: children }));
|
|
2982
|
-
/**
|
|
2983
|
-
* Message body compound component. Renders a `.message-body` element.
|
|
2984
|
-
*
|
|
2985
|
-
* @function
|
|
2986
|
-
* @param {MessageBodyProps} props - Props for the MessageBody component.
|
|
2987
|
-
* @returns {JSX.Element} The rendered message body.
|
|
2988
|
-
*/
|
|
2989
|
-
const MessageBody = ({ className, children, ...props }) => (jsxRuntime.jsx("div", { className: classNames(usePrefixedClassNames('message-body'), className), ...props, children: children }));
|
|
2990
|
-
// Attach compound components
|
|
2991
|
-
const Message = withSubComponents(MessageComponent, {
|
|
2992
|
-
Header: MessageHeader,
|
|
2993
|
-
Body: MessageBody,
|
|
2994
|
-
}, 'Message');
|
|
3090
|
+
const Buttons = withSubComponents(ButtonsComponent, {
|
|
3091
|
+
Button,
|
|
3092
|
+
LinkButton,
|
|
3093
|
+
ToggleButton,
|
|
3094
|
+
}, 'Buttons');
|
|
2995
3095
|
|
|
2996
|
-
// Ref-counted across every caller (e.g. a Dialog rendering its own Modal)
|
|
2997
|
-
// so overlapping/nested locks don't fight over which one restores `overflow`.
|
|
2998
|
-
// Every overlay that locks body scroll — Modal, Dialog, Sidebar, Loading —
|
|
2999
|
-
// goes through here; a component setting `document.body.style.overflow`
|
|
3000
|
-
// itself would clear a lock another overlay still needs.
|
|
3001
|
-
let lockCount = 0;
|
|
3002
|
-
let originalOverflow = '';
|
|
3003
3096
|
/**
|
|
3004
|
-
*
|
|
3005
|
-
*
|
|
3006
|
-
*
|
|
3097
|
+
* Strips a redundant leading library prefix from an icon name (e.g. `fa-check` -> `check`
|
|
3098
|
+
* for the `fa` library, `mdi-account` -> `account` for `mdi`), so `name` behaves the same
|
|
3099
|
+
* whether or not the caller includes the prefix. Font Awesome and Material Design Icons docs
|
|
3100
|
+
* conventionally refer to icons with their class prefix (`fa-check`), which otherwise gets
|
|
3101
|
+
* doubled by `getIconClasses` (producing `fa-fa-check`, matching no glyph).
|
|
3007
3102
|
*
|
|
3008
|
-
* @
|
|
3009
|
-
* @param
|
|
3103
|
+
* @param {string} name - The icon name, possibly including a redundant library prefix.
|
|
3104
|
+
* @param {IconLibrary} library - The icon library the name will be rendered with.
|
|
3105
|
+
* @returns {string} The icon name with any redundant leading library prefix removed.
|
|
3010
3106
|
*/
|
|
3011
|
-
function
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
if (lockCount === 0) {
|
|
3023
|
-
document.body.style.overflow = originalOverflow;
|
|
3024
|
-
}
|
|
3025
|
-
};
|
|
3026
|
-
}, [active]);
|
|
3107
|
+
function stripRedundantLibraryPrefix(name, library) {
|
|
3108
|
+
if (!name) {
|
|
3109
|
+
return name;
|
|
3110
|
+
}
|
|
3111
|
+
if (library === 'fa' && name.startsWith('fa-')) {
|
|
3112
|
+
return name.substring(3);
|
|
3113
|
+
}
|
|
3114
|
+
if (library === 'mdi' && name.startsWith('mdi-')) {
|
|
3115
|
+
return name.substring(4);
|
|
3116
|
+
}
|
|
3117
|
+
return name;
|
|
3027
3118
|
}
|
|
3028
|
-
|
|
3029
|
-
// "Are we past hydration?" as a store that never changes: React reads the
|
|
3030
|
-
// server snapshot both while server-rendering and while hydrating, and the
|
|
3031
|
-
// client snapshot from the first post-hydration render onward.
|
|
3032
|
-
const subscribeToNothing = () => () => { };
|
|
3033
|
-
const getClientSnapshot = () => true;
|
|
3034
|
-
const getServerSnapshot = () => false;
|
|
3035
3119
|
/**
|
|
3036
|
-
*
|
|
3037
|
-
*
|
|
3038
|
-
* Returns `false` during server rendering and during the hydrating client
|
|
3039
|
-
* render, then `true` from the commit that follows. Use it to defer
|
|
3040
|
-
* DOM-only behaviour (such as portalling) past hydration so the first client
|
|
3041
|
-
* render matches the server markup instead of tripping hydration recovery.
|
|
3042
|
-
*
|
|
3043
|
-
* Only a component that hydrates ever sees `false` on the client. One that
|
|
3044
|
-
* mounts later (after a route change, or in an app with no server rendering)
|
|
3045
|
-
* gets `true` on its first render, so it pays for no extra render. It
|
|
3046
|
-
* subscribes to nothing, so no later render flips it back.
|
|
3120
|
+
* Gets the correct classes for the icon element based on the library and features.
|
|
3047
3121
|
*
|
|
3048
|
-
* @
|
|
3049
|
-
* @
|
|
3122
|
+
* @param {IconLibrary} library - The icon library.
|
|
3123
|
+
* @param {string} name - The icon name.
|
|
3124
|
+
* @param {string} [variant] - Icon style variant (e.g., 'solid', 'outlined', 'rounded').
|
|
3125
|
+
* @param {string | string[]} [features] - Additional library-specific modifiers.
|
|
3126
|
+
* @returns {string} The combined class string for the icon.
|
|
3050
3127
|
*/
|
|
3051
|
-
function
|
|
3052
|
-
|
|
3128
|
+
function getIconClasses(library, name, variant, features) {
|
|
3129
|
+
let baseClass;
|
|
3130
|
+
let iconClass;
|
|
3131
|
+
const featureList = Array.isArray(features)
|
|
3132
|
+
? features
|
|
3133
|
+
: features
|
|
3134
|
+
? [features]
|
|
3135
|
+
: [];
|
|
3136
|
+
switch (library) {
|
|
3137
|
+
case 'fa': {
|
|
3138
|
+
// Font Awesome: use variant as style ('solid' -> 'fas', 'regular' -> 'far', etc.)
|
|
3139
|
+
const styleMap = {
|
|
3140
|
+
solid: 'fas',
|
|
3141
|
+
regular: 'far',
|
|
3142
|
+
brands: 'fab',
|
|
3143
|
+
light: 'fal',
|
|
3144
|
+
duotone: 'fad',
|
|
3145
|
+
thin: 'fat',
|
|
3146
|
+
};
|
|
3147
|
+
const faStyle = variant ? styleMap[variant] || variant : 'fas';
|
|
3148
|
+
baseClass = faStyle;
|
|
3149
|
+
iconClass = `fa-${name}`;
|
|
3150
|
+
return [baseClass, iconClass, ...featureList].join(' ');
|
|
3151
|
+
}
|
|
3152
|
+
case 'mdi':
|
|
3153
|
+
// Material Design Icons: no variants, just features
|
|
3154
|
+
baseClass = 'mdi';
|
|
3155
|
+
iconClass = `mdi-${name}`;
|
|
3156
|
+
return [baseClass, iconClass, ...featureList].join(' ');
|
|
3157
|
+
case 'material-icons': {
|
|
3158
|
+
// Google Material Icons: map variants to full class names
|
|
3159
|
+
const styleVariants = {
|
|
3160
|
+
filled: 'material-icons',
|
|
3161
|
+
outlined: 'material-icons-outlined',
|
|
3162
|
+
round: 'material-icons-round',
|
|
3163
|
+
sharp: 'material-icons-sharp',
|
|
3164
|
+
};
|
|
3165
|
+
baseClass = variant
|
|
3166
|
+
? styleVariants[variant] || `material-icons-${variant}`
|
|
3167
|
+
: 'material-icons';
|
|
3168
|
+
return [baseClass, ...featureList].join(' ');
|
|
3169
|
+
}
|
|
3170
|
+
case 'material-symbols': {
|
|
3171
|
+
// Google Material Symbols: map variants to full class names
|
|
3172
|
+
const styleVariants = {
|
|
3173
|
+
outlined: 'material-symbols-outlined',
|
|
3174
|
+
rounded: 'material-symbols-rounded',
|
|
3175
|
+
sharp: 'material-symbols-sharp',
|
|
3176
|
+
};
|
|
3177
|
+
baseClass = variant
|
|
3178
|
+
? styleVariants[variant] || `material-symbols-${variant}`
|
|
3179
|
+
: 'material-symbols-outlined';
|
|
3180
|
+
return [baseClass, ...featureList].join(' ');
|
|
3181
|
+
}
|
|
3182
|
+
default:
|
|
3183
|
+
// fallback: just icon name and features
|
|
3184
|
+
return [name, ...featureList].join(' ');
|
|
3185
|
+
}
|
|
3053
3186
|
}
|
|
3054
|
-
|
|
3055
3187
|
/**
|
|
3056
|
-
*
|
|
3057
|
-
* `string` is treated as a `document.querySelector` selector (falling back to
|
|
3058
|
-
* `document.body` when it matches nothing), and any falsy value — `undefined`
|
|
3059
|
-
* or the empty string — resolves to `document.body`.
|
|
3060
|
-
*
|
|
3061
|
-
* The empty string is deliberately treated as "no target" rather than passed
|
|
3062
|
-
* through: `document.querySelector('')` throws a `SyntaxError`, and callers
|
|
3063
|
-
* building a selector from state can easily hand us `''`.
|
|
3188
|
+
* The `Icon` component is a Bulma-styled wrapper for displaying icons from various libraries (Font Awesome, Material Design Icons, Ionicons, Google Material Icons, Material Symbols, etc.).
|
|
3064
3189
|
*
|
|
3065
|
-
* @function
|
|
3066
|
-
* @param
|
|
3067
|
-
* @returns The
|
|
3190
|
+
* @function
|
|
3191
|
+
* @param {IconProps} props - Props for the Icon component.
|
|
3192
|
+
* @returns {JSX.Element} The rendered icon element.
|
|
3193
|
+
* @see {@link https://bulma.io/documentation/elements/icon/ | Bulma Icon documentation}
|
|
3068
3194
|
*/
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3195
|
+
const Icon = ({ className, textColor, bgColor, name, library, variant, features, libraryFeatures, // Deprecated but maintained for backward compatibility
|
|
3196
|
+
size, ariaLabel, style, icon, // Capture and exclude the deprecated 'icon' prop from DOM
|
|
3197
|
+
color: _color, // Exclude 'color' prop if passed directly
|
|
3198
|
+
containerClassName, children, ...restProps }) => {
|
|
3199
|
+
// Get the default icon library from context, fallback to 'fa' if not set
|
|
3200
|
+
const defaultLibrary = useIconLibrary();
|
|
3201
|
+
/**
|
|
3202
|
+
* Generates Bulma helper classes and separates out remaining props.
|
|
3203
|
+
* Note: variant, features, and libraryFeatures are excluded from props spread
|
|
3204
|
+
*/
|
|
3205
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
3206
|
+
color: textColor,
|
|
3207
|
+
backgroundColor: bgColor,
|
|
3208
|
+
...restProps,
|
|
3209
|
+
});
|
|
3210
|
+
// Hoisted unconditionally to respect rules-of-hooks; the branches below
|
|
3211
|
+
// pick which result to consume.
|
|
3212
|
+
const defaultIconClasses = usePrefixedClassNames('icon', {
|
|
3213
|
+
[`is-${size}`]: size,
|
|
3214
|
+
});
|
|
3215
|
+
const sizeModifierClass = usePrefixedClassNames(size ? `is-${size}` : undefined);
|
|
3216
|
+
const bulmaClasses = containerClassName
|
|
3217
|
+
? containerClassName
|
|
3218
|
+
: defaultIconClasses;
|
|
3219
|
+
const iconContainerClasses = classNames(bulmaClasses, containerClassName && size ? sizeModifierClass : undefined, bulmaHelperClasses, className);
|
|
3220
|
+
// A named icon is an image, whose name is its label alone, so a ligature inside
|
|
3221
|
+
// never leaks into it. An unnamed one is decorative and hidden outright. Spread
|
|
3222
|
+
// before `rest`, so a caller's own `role` or `aria-hidden` still wins.
|
|
3223
|
+
const named = !!ariaLabel || !!restProps['aria-label'] || !!restProps['aria-labelledby'];
|
|
3224
|
+
// A caller's own `role` or `tabIndex` on an unnamed icon means it isn't decoration (an
|
|
3225
|
+
// icon made a button, say), so it isn't hidden either: `aria-hidden` on something
|
|
3226
|
+
// focusable is a failure of its own.
|
|
3227
|
+
const a11yProps = named
|
|
3228
|
+
? { role: 'img', 'aria-label': ariaLabel || undefined }
|
|
3229
|
+
: restProps.role !== undefined || restProps.tabIndex !== undefined
|
|
3230
|
+
? {}
|
|
3231
|
+
: { 'aria-hidden': true };
|
|
3232
|
+
if (children !== undefined) {
|
|
3233
|
+
// `IconChildrenProps`: render the caller's node (an inline SVG, a `react-icons`
|
|
3234
|
+
// component, …) in place of a class-based glyph. Library/variant/features don't apply.
|
|
3235
|
+
return (jsxRuntime.jsx("span", { className: iconContainerClasses, ...a11yProps, style: style, ...rest, children: children }));
|
|
3236
|
+
}
|
|
3237
|
+
// `name` is guaranteed once `children` is absent UNLESS the caller is on the deprecated
|
|
3238
|
+
// `icon` path, which `IconDeprecatedProps` declares with `name?: undefined`.
|
|
3239
|
+
// (`IconChildrenProps['children']` excludes `undefined`, so `children={undefined}` can't
|
|
3240
|
+
// slip past into this branch.) The cast covers that third member, and the `!name && icon`
|
|
3241
|
+
// fallback below is what fills it in.
|
|
3242
|
+
let finalName = name;
|
|
3243
|
+
if (!name && icon) {
|
|
3244
|
+
// If icon prop is provided instead of name, try to parse it
|
|
3245
|
+
// e.g., "mdi mdi-rocket-launch" -> "rocket-launch"
|
|
3246
|
+
if (typeof icon === 'string') {
|
|
3247
|
+
const parts = icon.split(' ');
|
|
3248
|
+
const lastPart = parts[parts.length - 1];
|
|
3249
|
+
if (lastPart.startsWith('mdi-')) {
|
|
3250
|
+
finalName = lastPart.substring(4); // Remove "mdi-" prefix
|
|
3251
|
+
}
|
|
3252
|
+
else if (lastPart.startsWith('fa-')) {
|
|
3253
|
+
finalName = lastPart.substring(3); // Remove "fa-" prefix
|
|
3254
|
+
}
|
|
3255
|
+
else {
|
|
3256
|
+
finalName = lastPart;
|
|
3257
|
+
}
|
|
3258
|
+
}
|
|
3259
|
+
}
|
|
3260
|
+
const finalLibrary = library || defaultLibrary || 'fa';
|
|
3261
|
+
// Normalize a redundant leading library prefix (e.g. "fa-check" -> "check" when the
|
|
3262
|
+
// library is 'fa'), so `name` behaves identically with or without the prefix.
|
|
3263
|
+
finalName = stripRedundantLibraryPrefix(finalName, finalLibrary);
|
|
3264
|
+
if (!finalName) {
|
|
3265
|
+
// No glyph to name. Reachable through the public type only as `icon=""`, and otherwise
|
|
3266
|
+
// by a plain-JS caller (or one that casts). Building a class off an absent name produced
|
|
3267
|
+
// a bogus `fa-undefined` glyph; render the bare container instead, matching the
|
|
3268
|
+
// `children` branch.
|
|
3269
|
+
return (jsxRuntime.jsx("span", { className: iconContainerClasses, ...a11yProps, style: style, ...rest }));
|
|
3270
|
+
}
|
|
3271
|
+
// Backward compatibility: if libraryFeatures is provided, parse it for variant and features
|
|
3272
|
+
let finalVariant = variant;
|
|
3273
|
+
let finalFeatures = features;
|
|
3274
|
+
if (libraryFeatures && !variant && !features) {
|
|
3275
|
+
const legacyFeatures = Array.isArray(libraryFeatures)
|
|
3276
|
+
? libraryFeatures
|
|
3277
|
+
: [libraryFeatures];
|
|
3278
|
+
// For Font Awesome, extract style from features
|
|
3279
|
+
if (finalLibrary === 'fa') {
|
|
3280
|
+
const faStyle = legacyFeatures.find(f => [
|
|
3281
|
+
'fas',
|
|
3282
|
+
'far',
|
|
3283
|
+
'fab',
|
|
3284
|
+
'fal',
|
|
3285
|
+
'fad',
|
|
3286
|
+
'fat',
|
|
3287
|
+
'solid',
|
|
3288
|
+
'regular',
|
|
3289
|
+
'brands',
|
|
3290
|
+
'light',
|
|
3291
|
+
'duotone',
|
|
3292
|
+
'thin',
|
|
3293
|
+
].includes(f));
|
|
3294
|
+
if (faStyle) {
|
|
3295
|
+
finalVariant = faStyle;
|
|
3296
|
+
finalFeatures = legacyFeatures.filter(f => f !== faStyle);
|
|
3297
|
+
}
|
|
3298
|
+
else {
|
|
3299
|
+
finalFeatures = legacyFeatures;
|
|
3300
|
+
}
|
|
3301
|
+
}
|
|
3302
|
+
// For Material Icons/Symbols, extract style variant
|
|
3303
|
+
else if (finalLibrary === 'material-icons' ||
|
|
3304
|
+
finalLibrary === 'material-symbols') {
|
|
3305
|
+
const styleVariants = finalLibrary === 'material-icons'
|
|
3306
|
+
? ['filled', 'outlined', 'round', 'sharp']
|
|
3307
|
+
: ['outlined', 'rounded', 'sharp'];
|
|
3308
|
+
const styleVariant = legacyFeatures.find(f => styleVariants.includes(f));
|
|
3309
|
+
if (styleVariant) {
|
|
3310
|
+
finalVariant = styleVariant;
|
|
3311
|
+
finalFeatures = legacyFeatures.filter(f => f !== styleVariant);
|
|
3312
|
+
}
|
|
3313
|
+
else {
|
|
3314
|
+
finalFeatures = legacyFeatures;
|
|
3315
|
+
}
|
|
3316
|
+
}
|
|
3317
|
+
// For others, all features go to finalFeatures
|
|
3318
|
+
else {
|
|
3319
|
+
finalFeatures = legacyFeatures;
|
|
3320
|
+
}
|
|
3321
|
+
}
|
|
3322
|
+
// Handle web components vs CSS-based icons
|
|
3323
|
+
if (finalLibrary === 'ion') {
|
|
3324
|
+
// For Ionicons, handle variant in the name
|
|
3325
|
+
let ionName = finalName;
|
|
3326
|
+
if (finalVariant === 'outline') {
|
|
3327
|
+
ionName = `${finalName}-outline`;
|
|
3328
|
+
}
|
|
3329
|
+
else if (finalVariant === 'sharp') {
|
|
3330
|
+
ionName = `${finalName}-sharp`;
|
|
3331
|
+
}
|
|
3332
|
+
return (jsxRuntime.jsx("span", { className: iconContainerClasses, ...a11yProps, style: style, ...rest, children: jsxRuntime.jsx("ion-icon", { name: ionName }) }));
|
|
3333
|
+
}
|
|
3334
|
+
// Legacy CSS-based icons
|
|
3335
|
+
const iClasses = getIconClasses(finalLibrary, finalName, finalVariant, finalFeatures);
|
|
3336
|
+
// Material Icons and Material Symbols use text content, not CSS classes for the icon name
|
|
3337
|
+
if (finalLibrary === 'material-icons' ||
|
|
3338
|
+
finalLibrary === 'material-symbols') {
|
|
3339
|
+
return (jsxRuntime.jsx("span", { className: iconContainerClasses, ...a11yProps, style: style, ...rest, children: jsxRuntime.jsx("i", { className: iClasses, children: finalName }) }));
|
|
3340
|
+
}
|
|
3341
|
+
return (jsxRuntime.jsx("span", { className: iconContainerClasses, ...a11yProps, style: style, ...rest, children: jsxRuntime.jsx("i", { className: iClasses }) }));
|
|
3342
|
+
};
|
|
3343
|
+
|
|
3344
|
+
/**
|
|
3345
|
+
* The `Paragraph` component renders a styled `<p>` element with Bulma helper class integration.
|
|
3346
|
+
*
|
|
3347
|
+
* @function
|
|
3348
|
+
* @param {ParagraphProps} props - Props for the Paragraph component.
|
|
3349
|
+
* @returns {JSX.Element} The rendered paragraph element.
|
|
3350
|
+
*/
|
|
3351
|
+
const Paragraph = ({ className, textColor, bgColor, children, ...props }) => {
|
|
3352
|
+
/**
|
|
3353
|
+
* Generates Bulma helper classes and separates out remaining props.
|
|
3354
|
+
*/
|
|
3355
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
3356
|
+
color: textColor,
|
|
3357
|
+
backgroundColor: bgColor,
|
|
3358
|
+
...props,
|
|
3359
|
+
});
|
|
3360
|
+
const bulmaClasses = usePrefixedClassNames();
|
|
3361
|
+
const paragraphClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
|
|
3362
|
+
return (jsxRuntime.jsx("p", { className: paragraphClasses || undefined, ...rest, children: children }));
|
|
3363
|
+
};
|
|
3364
|
+
|
|
3365
|
+
const validTitleSizes = ['1', '2', '3', '4', '5', '6'];
|
|
3366
|
+
const validTitleElements = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p'];
|
|
3367
|
+
/**
|
|
3368
|
+
* The `Title` component renders a Bulma-styled title (heading), supporting sizes `1`-`6`, spacing, and rendering as any heading or paragraph element (`h1`-`h6`, `p`).
|
|
3369
|
+
*
|
|
3370
|
+
* @function
|
|
3371
|
+
* @param {TitleProps} props - Props for the Title component.
|
|
3372
|
+
* @returns {JSX.Element} The rendered title element.
|
|
3373
|
+
* @see {@link https://bulma.io/documentation/elements/title/ | Bulma Title documentation}
|
|
3374
|
+
*/
|
|
3375
|
+
const Title = ({ className, size, isSpaced, as = 'h1', hasSkeleton, textColor, bgColor, children, ...props }) => {
|
|
3376
|
+
/**
|
|
3377
|
+
* Generates Bulma helper classes and separates out remaining props.
|
|
3378
|
+
*/
|
|
3379
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
3380
|
+
color: textColor,
|
|
3381
|
+
backgroundColor: bgColor,
|
|
3382
|
+
...props,
|
|
3383
|
+
});
|
|
3384
|
+
// Validate 'as' prop at runtime
|
|
3385
|
+
const element = validTitleElements.includes(as) ? as : 'h1';
|
|
3386
|
+
// Validate 'size' prop at runtime (numeric sizes normalize to strings)
|
|
3387
|
+
const normalized = size == null ? undefined : String(size);
|
|
3388
|
+
const validSize = normalized && validTitleSizes.includes(normalized)
|
|
3389
|
+
? normalized
|
|
3390
|
+
: undefined;
|
|
3391
|
+
const bulmaClasses = usePrefixedClassNames('title', {
|
|
3392
|
+
[`is-${validSize}`]: validSize,
|
|
3393
|
+
'is-spaced': isSpaced,
|
|
3394
|
+
'has-skeleton': hasSkeleton,
|
|
3395
|
+
});
|
|
3396
|
+
const titleClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
|
|
3397
|
+
// Determine the tag based on 'element' and 'validSize'
|
|
3398
|
+
const Tag = element === 'p' ? 'p' : validSize ? `h${validSize}` : element;
|
|
3399
|
+
return (jsxRuntime.jsx(Tag, { className: titleClasses, ...rest, children: children }));
|
|
3400
|
+
};
|
|
3401
|
+
|
|
3402
|
+
const validEmptyStateColors = [
|
|
3403
|
+
'primary',
|
|
3404
|
+
'link',
|
|
3405
|
+
'info',
|
|
3406
|
+
'success',
|
|
3407
|
+
'warning',
|
|
3408
|
+
'danger',
|
|
3409
|
+
];
|
|
3410
|
+
const validEmptyStateHeadings = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
|
|
3411
|
+
const validEmptyStateSizes = ['small', 'medium', 'large'];
|
|
3412
|
+
/**
|
|
3413
|
+
* The circle the picture sits in, hidden from assistive tech whatever is in it.
|
|
3414
|
+
*
|
|
3415
|
+
* @function
|
|
3416
|
+
* @param {EmptyStateIndicatorProps} props - Props for EmptyState.Indicator.
|
|
3417
|
+
* @returns {JSX.Element} The rendered indicator.
|
|
3418
|
+
*/
|
|
3419
|
+
const EmptyStateIndicator = ({ className, textColor, bgColor, children, ...props }) => {
|
|
3420
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
3421
|
+
color: textColor,
|
|
3422
|
+
backgroundColor: bgColor,
|
|
3423
|
+
...props,
|
|
3424
|
+
});
|
|
3425
|
+
const indicatorClass = usePrefixedClassNames('empty-state-indicator');
|
|
3426
|
+
// Hidden after the rest of the props, so nothing passed in can expose the
|
|
3427
|
+
// picture: the title says what it means.
|
|
3428
|
+
return (jsxRuntime.jsx("div", { className: classNames(indicatorClass, bulmaHelperClasses, className), ...rest, "aria-hidden": "true", children: children }));
|
|
3429
|
+
};
|
|
3430
|
+
EmptyStateIndicator.displayName = 'EmptyState.Indicator';
|
|
3431
|
+
/**
|
|
3432
|
+
* The heading, a Bulma `Title` at the level `as` sets, `h2` by default, and at the EmptyState's size.
|
|
3433
|
+
*
|
|
3434
|
+
* @function
|
|
3435
|
+
* @param {EmptyStateTitleProps} props - Props for EmptyState.Title.
|
|
3436
|
+
* @returns {JSX.Element} The rendered heading.
|
|
3437
|
+
*/
|
|
3438
|
+
const EmptyStateTitle = ({ as = 'h2', className, ...props }) => {
|
|
3439
|
+
// `size` isn't in the type, but on `Title` it picks the tag, so it's kept
|
|
3440
|
+
// off even when it's passed anyway. The level is `as` alone.
|
|
3441
|
+
const { size: _size, ...rest } = props;
|
|
3442
|
+
const titleClass = usePrefixedClassNames('empty-state-title');
|
|
3443
|
+
const level = validEmptyStateHeadings.includes(as) ? as : 'h2';
|
|
3444
|
+
return (jsxRuntime.jsx(Title, { as: level, className: classNames(titleClass, className), ...rest }));
|
|
3445
|
+
};
|
|
3446
|
+
EmptyStateTitle.displayName = 'EmptyState.Title';
|
|
3447
|
+
/**
|
|
3448
|
+
* A sentence or two under the title, in the color of the text around it.
|
|
3449
|
+
*
|
|
3450
|
+
* @function
|
|
3451
|
+
* @param {EmptyStateDescriptionProps} props - Props for EmptyState.Description.
|
|
3452
|
+
* @returns {JSX.Element} The rendered description.
|
|
3453
|
+
*/
|
|
3454
|
+
const EmptyStateDescription = ({ className, ...props }) => {
|
|
3455
|
+
const descriptionClass = usePrefixedClassNames('empty-state-description');
|
|
3456
|
+
return (jsxRuntime.jsx(Paragraph, { className: classNames(descriptionClass, className), ...props }));
|
|
3457
|
+
};
|
|
3458
|
+
EmptyStateDescription.displayName = 'EmptyState.Description';
|
|
3459
|
+
/**
|
|
3460
|
+
* A row of buttons, centered under the text or at the start of the line beside the indicator.
|
|
3461
|
+
*
|
|
3462
|
+
* @function
|
|
3463
|
+
* @param {EmptyStateActionsProps} props - Props for EmptyState.Actions.
|
|
3464
|
+
* @returns {JSX.Element} The rendered row of buttons.
|
|
3465
|
+
*/
|
|
3466
|
+
const EmptyStateActions = ({ className, ...props }) => {
|
|
3467
|
+
const actionsClass = usePrefixedClassNames('empty-state-actions');
|
|
3468
|
+
return jsxRuntime.jsx(Buttons, { className: classNames(actionsClass, className), ...props });
|
|
3469
|
+
};
|
|
3470
|
+
EmptyStateActions.displayName = 'EmptyState.Actions';
|
|
3471
|
+
/** Whether a shorthand prop was given something to render. */
|
|
3472
|
+
const isGiven = (node) => node !== undefined &&
|
|
3473
|
+
node !== null &&
|
|
3474
|
+
typeof node !== 'boolean' &&
|
|
3475
|
+
node !== '';
|
|
3476
|
+
/**
|
|
3477
|
+
* The `EmptyState` component renders the block an app shows where content would be: an empty list, a search with no results, or a first-run screen.
|
|
3478
|
+
*
|
|
3479
|
+
* It's a column of a picture in a circle, a heading, a line of text and the
|
|
3480
|
+
* actions, with one layout whether it's written with the `icon`, `title` and
|
|
3481
|
+
* `description` props or with the parts. The children always render as
|
|
3482
|
+
* written, after the parts the props render.
|
|
3483
|
+
*
|
|
3484
|
+
* It has no role and no live region. It arrives already filled and goes away
|
|
3485
|
+
* when results come back, so it can't announce a search that matched
|
|
3486
|
+
* nothing: keep a `role="status"` region beside the search field for that,
|
|
3487
|
+
* in the page the whole time. While the data loads, show a `Skeleton` or a
|
|
3488
|
+
* `Loader` instead, so "No projects yet" doesn't flash first, and show a
|
|
3489
|
+
* load that failed in a `Message` or a `Notification`.
|
|
3490
|
+
*
|
|
3491
|
+
* @function
|
|
3492
|
+
* @param {EmptyStateProps} props - Props for the EmptyState component.
|
|
3493
|
+
* @param {React.Ref<HTMLDivElement>} ref - Forwarded ref to the root `<div>`.
|
|
3494
|
+
* @returns {JSX.Element} The rendered empty state.
|
|
3495
|
+
*
|
|
3496
|
+
* @example
|
|
3497
|
+
* <EmptyState
|
|
3498
|
+
* icon="folder-open"
|
|
3499
|
+
* title="No projects yet"
|
|
3500
|
+
* description="Projects you create or join show up here."
|
|
3501
|
+
* >
|
|
3502
|
+
* <Button color="primary">New project</Button>
|
|
3503
|
+
* </EmptyState>
|
|
3504
|
+
*/
|
|
3505
|
+
const EmptyStateRoot = React.forwardRef(({ className, icon, title, titleAs = 'h2', description, children, color, variant = 'light', size = 'normal', align = 'center', textColor, bgColor, ...props }, ref) => {
|
|
3506
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
3507
|
+
color: textColor,
|
|
3508
|
+
backgroundColor: bgColor,
|
|
3509
|
+
...props,
|
|
3510
|
+
});
|
|
3511
|
+
const rootClass = usePrefixedClassNames('empty-state', {
|
|
3512
|
+
[`is-${size}`]: validEmptyStateSizes.includes(size),
|
|
3513
|
+
'is-left': align === 'left',
|
|
3514
|
+
[`is-${color}`]: !!color && validEmptyStateColors.includes(color),
|
|
3515
|
+
'is-filled': variant === 'filled',
|
|
3516
|
+
});
|
|
3517
|
+
// The props' parts first, each only when it's given, then the children
|
|
3518
|
+
// exactly as they were written: nothing here looks at them.
|
|
3519
|
+
return (jsxRuntime.jsxs("div", { ref: ref, className: classNames(rootClass, bulmaHelperClasses, className), ...rest, children: [isGiven(icon) && (jsxRuntime.jsx(EmptyStateIndicator, { children: typeof icon === 'string' ? jsxRuntime.jsx(Icon, { name: icon }) : icon })), isGiven(title) && (jsxRuntime.jsx(EmptyStateTitle, { as: titleAs, children: title })), isGiven(description) && (jsxRuntime.jsx(EmptyStateDescription, { children: description })), children] }));
|
|
3520
|
+
});
|
|
3521
|
+
const EmptyState = withSubComponents(EmptyStateRoot, {
|
|
3522
|
+
Indicator: EmptyStateIndicator,
|
|
3523
|
+
Title: EmptyStateTitle,
|
|
3524
|
+
Description: EmptyStateDescription,
|
|
3525
|
+
Actions: EmptyStateActions,
|
|
3526
|
+
}, 'EmptyState');
|
|
3527
|
+
|
|
3528
|
+
// Context to track MenuList nesting level
|
|
3529
|
+
const MenuListLevelContext = React.createContext(0);
|
|
3530
|
+
/**
|
|
3531
|
+
* The `Menu` component provides Bulma's vertical navigation menu: a simple, accessible sidebar or section menu for your Bulma React UI.
|
|
3532
|
+
*
|
|
3533
|
+
* @function
|
|
3534
|
+
* @param {MenuProps} props - Props for the Menu component.
|
|
3535
|
+
* @returns {JSX.Element} The rendered menu.
|
|
3536
|
+
* @see {@link https://bulma.io/documentation/components/menu/ | Bulma Menu documentation}
|
|
3537
|
+
*/
|
|
3538
|
+
const MenuComponent = ({ className, children, ...props }) => {
|
|
3539
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses(props);
|
|
3540
|
+
// Generate Bulma classes with prefix
|
|
3541
|
+
const bulmaClasses = usePrefixedClassNames('menu');
|
|
3542
|
+
return (jsxRuntime.jsx("aside", { className: classNames(bulmaClasses, bulmaHelperClasses, className), ...rest, children: children }));
|
|
3543
|
+
};
|
|
3544
|
+
/**
|
|
3545
|
+
* Bulma Menu label component.
|
|
3546
|
+
*
|
|
3547
|
+
* @function
|
|
3548
|
+
* @param {MenuLabelProps} props - Props for the MenuLabel component.
|
|
3549
|
+
* @returns {JSX.Element} The rendered menu label.
|
|
3550
|
+
*/
|
|
3551
|
+
const MenuLabel = ({ className, children, ...props }) => {
|
|
3552
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses(props);
|
|
3553
|
+
return (jsxRuntime.jsx("p", { className: classNames(usePrefixedClassNames('menu-label'), className, bulmaHelperClasses), ...rest, children: children }));
|
|
3554
|
+
};
|
|
3555
|
+
/**
|
|
3556
|
+
* MenuList applies `menu-list` class only at the top level (not for nested lists).
|
|
3557
|
+
*
|
|
3558
|
+
* @function
|
|
3559
|
+
* @param {MenuListProps} props - Props for the MenuList component.
|
|
3560
|
+
* @returns {JSX.Element} The rendered menu list.
|
|
3561
|
+
*/
|
|
3562
|
+
const MenuList = ({ className, children, ...props }) => {
|
|
3563
|
+
const level = React.useContext(MenuListLevelContext);
|
|
3564
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses(props);
|
|
3565
|
+
const ulClass = classNames(className, bulmaHelperClasses, {
|
|
3566
|
+
[usePrefixedClassNames('menu-list')]: level === 0,
|
|
3567
|
+
});
|
|
3568
|
+
// Increment level for nested MenuLists
|
|
3569
|
+
return (jsxRuntime.jsx(MenuListLevelContext.Provider, { value: level + 1, children: jsxRuntime.jsx("ul", { className: ulClass || undefined, ...rest, children: children }) }));
|
|
3570
|
+
};
|
|
3571
|
+
/**
|
|
3572
|
+
* `href` alone — annotated so a typo is a compile error rather than a silent
|
|
3573
|
+
* no-op, the way both siblings' sets are.
|
|
3574
|
+
*/
|
|
3575
|
+
const STRIP_FROM_NON_LINK = { href: true };
|
|
3576
|
+
/**
|
|
3577
|
+
* MenuItem supports `as` prop for custom link components, e.g., react-router-dom Link.
|
|
3578
|
+
*
|
|
3579
|
+
* @function
|
|
3580
|
+
* @param {MenuItemProps} props - Props for the MenuItem component.
|
|
3581
|
+
* @param {React.Ref} ref - Forwarded ref to the inner element `as` renders.
|
|
3582
|
+
* @returns {JSX.Element} The rendered menu item.
|
|
3583
|
+
*/
|
|
3584
|
+
const MenuItem = React.forwardRef(function MenuItem(itemProps, ref) {
|
|
3585
|
+
const { className, children, active, as: Component = 'a', 'data-testid': testId, style, id, title, role, tabIndex, ...rest } = itemProps;
|
|
3586
|
+
const { bulmaHelperClasses, rest: forwarded } = useBulmaClasses(rest);
|
|
3587
|
+
// `href` reaches an anchor or a custom component (which owns its own prop
|
|
3588
|
+
// contract), but not another intrinsic tag — `<span href>` is invalid HTML.
|
|
3589
|
+
// The same rule Avatar applies through `isLinkLike` and Button through its
|
|
3590
|
+
// intrinsic-only strip. Before #641 `href` was an own prop re-applied only
|
|
3591
|
+
// for `as="a"`; deriving it from `as` must not quietly widen that.
|
|
3592
|
+
const isLinkLike = Component === 'a' ||
|
|
3593
|
+
typeof Component !== 'string' ||
|
|
3594
|
+
isCustomElement(Component);
|
|
3595
|
+
// `href` ALONE. `Dropdown.Item` strips every anchor-only attribute except
|
|
3596
|
+
// `type`; `Level.Item` strips all of them, plus `rel`.
|
|
3597
|
+
//
|
|
3598
|
+
// Not because nothing else can arrive: `as` here is an open `React.ElementType`,
|
|
3599
|
+
// and a non-fresh spread skips excess-property checking, so a typed caller
|
|
3600
|
+
// CAN deliver `target` or `download` and they are forwarded. The set stays at
|
|
3601
|
+
// `href` because widening it would be wrong for an open `as` — `referrerPolicy`
|
|
3602
|
+
// on `as="img"`, `target` on `as="form"` and `media` on `as="source"` are all
|
|
3603
|
+
// legal, and a flat strip would delete them. Getting that right needs a
|
|
3604
|
+
// per-attribute-per-element rule, which `bestax-migrate` already models in
|
|
3605
|
+
// `LINK_ATTR_ELEMENTS`.
|
|
3606
|
+
const linkProps = isLinkLike
|
|
3607
|
+
? forwarded
|
|
3608
|
+
: omitAttrs(forwarded, STRIP_FROM_NON_LINK);
|
|
3609
|
+
const itemClass = classNames({ [usePrefixedClassNames('is-active')]: active }, bulmaHelperClasses);
|
|
3610
|
+
// Split children into label and nested MenuList(s)
|
|
3611
|
+
const labelChildren = [];
|
|
3612
|
+
const nestedMenuLists = [];
|
|
3613
|
+
React.Children.forEach(children, child => {
|
|
3614
|
+
if (React.isValidElement(child) && child.type === MenuList) {
|
|
3615
|
+
nestedMenuLists.push(child);
|
|
3616
|
+
}
|
|
3617
|
+
else {
|
|
3618
|
+
labelChildren.push(child);
|
|
3619
|
+
}
|
|
3620
|
+
});
|
|
3621
|
+
return (jsxRuntime.jsxs("li", { className: className, "data-testid": testId, style: style, id: id, title: title, role: role, tabIndex: tabIndex, children: [jsxRuntime.jsx(Component, { ref: ref, className: itemClass || undefined, ...linkProps, ...(Component === 'button'
|
|
3622
|
+
? { type: buttonType(linkProps.type) }
|
|
3623
|
+
: {}), children: labelChildren }), nestedMenuLists] }));
|
|
3624
|
+
});
|
|
3625
|
+
MenuItem.displayName = 'MenuItem';
|
|
3626
|
+
// Attach static subcomponents
|
|
3627
|
+
const Menu = withSubComponents(MenuComponent, {
|
|
3628
|
+
Label: MenuLabel,
|
|
3629
|
+
List: MenuList,
|
|
3630
|
+
Item: MenuItem,
|
|
3631
|
+
}, 'Menu');
|
|
3632
|
+
|
|
3633
|
+
const validMessageSizes = ['small', 'medium', 'large'];
|
|
3634
|
+
/**
|
|
3635
|
+
* The `Message` component provides Bulma's flexible notice/message box for your Bulma React UI.
|
|
3636
|
+
*
|
|
3637
|
+
* @function
|
|
3638
|
+
* @param {MessageProps} props - Props for the Message component.
|
|
3639
|
+
* @returns {JSX.Element} The rendered message.
|
|
3640
|
+
* @see {@link https://bulma.io/documentation/components/message/ | Bulma Message documentation}
|
|
3641
|
+
*/
|
|
3642
|
+
const MessageComponent = ({ className, title, textColor, color, bgColor, size, onClose, children, ...props }) => {
|
|
3643
|
+
const { classPrefix } = useConfig();
|
|
3644
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
3645
|
+
color: textColor,
|
|
3646
|
+
backgroundColor: bgColor,
|
|
3647
|
+
...props,
|
|
3648
|
+
});
|
|
3649
|
+
// Generate Bulma classes with prefix
|
|
3650
|
+
const bulmaClasses = usePrefixedClassNames('message', {
|
|
3651
|
+
[`is-${color}`]: color,
|
|
3652
|
+
[`is-${size}`]: size && validMessageSizes.includes(size),
|
|
3653
|
+
});
|
|
3654
|
+
const deleteClass = usePrefixedClassNames('delete');
|
|
3655
|
+
const messageClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
|
|
3656
|
+
return (jsxRuntime.jsxs("article", { className: messageClasses, ...rest, "data-testid": "message", children: [(title || onClose) && (jsxRuntime.jsxs("div", { className: prefixedClassNames(classPrefix, 'message-header'), children: [title && jsxRuntime.jsx("span", { children: title }), onClose && (jsxRuntime.jsx("button", { className: deleteClass, "aria-label": "delete", onClick: onClose, type: "button", "data-testid": "message-close" }))] })), children && (jsxRuntime.jsx("div", { className: prefixedClassNames(classPrefix, 'message-body'), "data-testid": "message-body", children: children }))] }));
|
|
3657
|
+
};
|
|
3658
|
+
/**
|
|
3659
|
+
* Message header compound component. Renders a `.message-header` element.
|
|
3660
|
+
*
|
|
3661
|
+
* @function
|
|
3662
|
+
* @param {MessageHeaderProps} props - Props for the MessageHeader component.
|
|
3663
|
+
* @returns {JSX.Element} The rendered message header.
|
|
3664
|
+
*/
|
|
3665
|
+
const MessageHeader = ({ className, children, ...props }) => (jsxRuntime.jsx("div", { className: classNames(usePrefixedClassNames('message-header'), className), ...props, children: children }));
|
|
3666
|
+
/**
|
|
3667
|
+
* Message body compound component. Renders a `.message-body` element.
|
|
3668
|
+
*
|
|
3669
|
+
* @function
|
|
3670
|
+
* @param {MessageBodyProps} props - Props for the MessageBody component.
|
|
3671
|
+
* @returns {JSX.Element} The rendered message body.
|
|
3672
|
+
*/
|
|
3673
|
+
const MessageBody = ({ className, children, ...props }) => (jsxRuntime.jsx("div", { className: classNames(usePrefixedClassNames('message-body'), className), ...props, children: children }));
|
|
3674
|
+
// Attach compound components
|
|
3675
|
+
const Message = withSubComponents(MessageComponent, {
|
|
3676
|
+
Header: MessageHeader,
|
|
3677
|
+
Body: MessageBody,
|
|
3678
|
+
}, 'Message');
|
|
3679
|
+
|
|
3680
|
+
// Ref-counted across every caller (e.g. a Dialog rendering its own Modal)
|
|
3681
|
+
// so overlapping/nested locks don't fight over which one restores `overflow`.
|
|
3682
|
+
// Every overlay that locks body scroll — Modal, Dialog, Sidebar, Loading —
|
|
3683
|
+
// goes through here; a component setting `document.body.style.overflow`
|
|
3684
|
+
// itself would clear a lock another overlay still needs.
|
|
3685
|
+
let lockCount = 0;
|
|
3686
|
+
let originalOverflow = '';
|
|
3687
|
+
/**
|
|
3688
|
+
* Locks (and ref-counted-ly unlocks) `document.body` scrolling while `active`
|
|
3689
|
+
* is true. Safe to call from multiple components at once — the body scroll
|
|
3690
|
+
* is only restored once every active caller has released its lock.
|
|
3691
|
+
*
|
|
3692
|
+
* @function useScrollLock
|
|
3693
|
+
* @param active - Whether this caller wants the body scroll locked.
|
|
3694
|
+
*/
|
|
3695
|
+
function useScrollLock(active) {
|
|
3696
|
+
React.useEffect(() => {
|
|
3697
|
+
if (!active)
|
|
3698
|
+
return undefined;
|
|
3699
|
+
lockCount++;
|
|
3700
|
+
if (lockCount === 1) {
|
|
3701
|
+
originalOverflow = document.body.style.overflow;
|
|
3702
|
+
document.body.style.overflow = 'hidden';
|
|
3703
|
+
}
|
|
3704
|
+
return () => {
|
|
3705
|
+
lockCount--;
|
|
3706
|
+
if (lockCount === 0) {
|
|
3707
|
+
document.body.style.overflow = originalOverflow;
|
|
3708
|
+
}
|
|
3709
|
+
};
|
|
3710
|
+
}, [active]);
|
|
3711
|
+
}
|
|
3712
|
+
|
|
3713
|
+
// "Are we past hydration?" as a store that never changes: React reads the
|
|
3714
|
+
// server snapshot both while server-rendering and while hydrating, and the
|
|
3715
|
+
// client snapshot from the first post-hydration render onward.
|
|
3716
|
+
const subscribeToNothing = () => () => { };
|
|
3717
|
+
const getClientSnapshot = () => true;
|
|
3718
|
+
const getServerSnapshot = () => false;
|
|
3719
|
+
/**
|
|
3720
|
+
* Reports whether the component has passed hydration.
|
|
3721
|
+
*
|
|
3722
|
+
* Returns `false` during server rendering and during the hydrating client
|
|
3723
|
+
* render, then `true` from the commit that follows. Use it to defer
|
|
3724
|
+
* DOM-only behaviour (such as portalling) past hydration so the first client
|
|
3725
|
+
* render matches the server markup instead of tripping hydration recovery.
|
|
3726
|
+
*
|
|
3727
|
+
* Only a component that hydrates ever sees `false` on the client. One that
|
|
3728
|
+
* mounts later (after a route change, or in an app with no server rendering)
|
|
3729
|
+
* gets `true` on its first render, so it pays for no extra render. It
|
|
3730
|
+
* subscribes to nothing, so no later render flips it back.
|
|
3731
|
+
*
|
|
3732
|
+
* @function useIsHydrated
|
|
3733
|
+
* @returns `true` once the hydrating render has completed, otherwise `false`.
|
|
3734
|
+
*/
|
|
3735
|
+
function useIsHydrated() {
|
|
3736
|
+
return React.useSyncExternalStore(subscribeToNothing, getClientSnapshot, getServerSnapshot);
|
|
3737
|
+
}
|
|
3738
|
+
|
|
3739
|
+
/**
|
|
3740
|
+
* Resolves a portal target: an `HTMLElement` is used directly, a non-empty
|
|
3741
|
+
* `string` is treated as a `document.querySelector` selector (falling back to
|
|
3742
|
+
* `document.body` when it matches nothing), and any falsy value — `undefined`
|
|
3743
|
+
* or the empty string — resolves to `document.body`.
|
|
3744
|
+
*
|
|
3745
|
+
* The empty string is deliberately treated as "no target" rather than passed
|
|
3746
|
+
* through: `document.querySelector('')` throws a `SyntaxError`, and callers
|
|
3747
|
+
* building a selector from state can easily hand us `''`.
|
|
3748
|
+
*
|
|
3749
|
+
* @function resolvePortalContainer
|
|
3750
|
+
* @param container - The requested portal target, if any.
|
|
3751
|
+
* @returns The resolved DOM node to portal into.
|
|
3752
|
+
*/
|
|
3753
|
+
function resolvePortalContainer(container) {
|
|
3754
|
+
if (!container) {
|
|
3755
|
+
return document.body;
|
|
3756
|
+
}
|
|
3757
|
+
if (typeof container === 'string') {
|
|
3758
|
+
return (document.querySelector(container) ?? document.body);
|
|
3075
3759
|
}
|
|
3076
3760
|
return container;
|
|
3077
3761
|
}
|
|
@@ -3838,409 +4522,161 @@ function pageItems(total, current, siblings, boundaries) {
|
|
|
3838
4522
|
const end = start + windowSize - 1;
|
|
3839
4523
|
const gap = (from, to, name) => to === from ? [from] : [name];
|
|
3840
4524
|
return [
|
|
3841
|
-
...range(1, boundaries),
|
|
3842
|
-
...gap(boundaries + 1, start - 1, 'start-gap'),
|
|
3843
|
-
...range(start, end),
|
|
3844
|
-
...gap(end + 1, total - boundaries, 'end-gap'),
|
|
3845
|
-
...range(total - boundaries + 1, total),
|
|
3846
|
-
];
|
|
3847
|
-
}
|
|
3848
|
-
/**
|
|
3849
|
-
* "Previous" navigation button.
|
|
3850
|
-
*
|
|
3851
|
-
* @function
|
|
3852
|
-
* @param {PaginationPreviousNextProps} props - Props for the PaginationPrevious component.
|
|
3853
|
-
* @returns {JSX.Element} The rendered previous button.
|
|
3854
|
-
*/
|
|
3855
|
-
const PaginationPrevious = ({ className, disabled, children, ...props }) => (jsxRuntime.jsx("a", { className: classNames(usePrefixedClassNames('pagination-previous', {
|
|
3856
|
-
'is-disabled': disabled,
|
|
3857
|
-
}), className), "aria-disabled": disabled, tabIndex: disabled ? -1 : 0, ...props, onClick: disabled
|
|
3858
|
-
? e => {
|
|
3859
|
-
e.preventDefault();
|
|
3860
|
-
e.stopPropagation();
|
|
3861
|
-
}
|
|
3862
|
-
: props.onClick, children: children }));
|
|
3863
|
-
/**
|
|
3864
|
-
* "Next" navigation button.
|
|
3865
|
-
*
|
|
3866
|
-
* @function
|
|
3867
|
-
* @param {PaginationPreviousNextProps} props - Props for the PaginationNext component.
|
|
3868
|
-
* @returns {JSX.Element} The rendered next button.
|
|
3869
|
-
*/
|
|
3870
|
-
const PaginationNext = ({ className, disabled, children, ...props }) => (jsxRuntime.jsx("a", { className: classNames(usePrefixedClassNames('pagination-next', { 'is-disabled': disabled }), className), "aria-disabled": disabled, tabIndex: disabled ? -1 : 0, ...props, onClick: disabled
|
|
3871
|
-
? e => {
|
|
3872
|
-
e.preventDefault();
|
|
3873
|
-
e.stopPropagation();
|
|
3874
|
-
}
|
|
3875
|
-
: props.onClick, children: children }));
|
|
3876
|
-
/**
|
|
3877
|
-
* The `Pagination` component provides a flexible, composable Bulma pagination navigation for your Bulma React UI.
|
|
3878
|
-
*
|
|
3879
|
-
* @function
|
|
3880
|
-
* @param {PaginationProps} props - Props for the Pagination component.
|
|
3881
|
-
* @returns {JSX.Element} The rendered pagination.
|
|
3882
|
-
* @see {@link https://bulma.io/documentation/components/pagination/ | Bulma Pagination documentation}
|
|
3883
|
-
*/
|
|
3884
|
-
const PaginationComponent = ({ color, textColor, bgColor, size, align, rounded, total, current, onPageChange, siblingCount = 1, boundaryCount = 1, previousLabel = 'Previous', nextLabel = 'Next', disabled, getPageHref, getPageLabel, className, children, ...props }) => {
|
|
3885
|
-
const [kept, setKept] = React.useState(1);
|
|
3886
|
-
warnDeprecatedColorProp('Pagination', color, 'Use the textColor / bgColor helper props instead.');
|
|
3887
|
-
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
3888
|
-
color: textColor,
|
|
3889
|
-
backgroundColor: bgColor,
|
|
3890
|
-
...props,
|
|
3891
|
-
});
|
|
3892
|
-
// Generate Bulma classes with prefix
|
|
3893
|
-
const bulmaClasses = usePrefixedClassNames('pagination', {
|
|
3894
|
-
[`is-${color}`]: color,
|
|
3895
|
-
[`is-${size}`]: size,
|
|
3896
|
-
[`is-${align}`]: align,
|
|
3897
|
-
'is-rounded': rounded,
|
|
3898
|
-
});
|
|
3899
|
-
const paginationClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
|
|
3900
|
-
// A child that renders nothing (`{show && …}` when false) is no children.
|
|
3901
|
-
const fromTotal = total !== undefined && React.Children.toArray(children).length === 0;
|
|
3902
|
-
let content = children;
|
|
3903
|
-
if (fromTotal) {
|
|
3904
|
-
const pages = Number.isFinite(total) ? Math.max(Math.floor(total), 0) : 0;
|
|
3905
|
-
// A current page that's no number reads as the first.
|
|
3906
|
-
const page = Math.min(Math.max(whole(current ?? kept, 1), 1), Math.max(pages, 1));
|
|
3907
|
-
const choose = (next) => {
|
|
3908
|
-
if (next === page)
|
|
3909
|
-
return;
|
|
3910
|
-
if (current === undefined)
|
|
3911
|
-
setKept(next);
|
|
3912
|
-
onPageChange?.(next);
|
|
3913
|
-
};
|
|
3914
|
-
/**
|
|
3915
|
-
* What a link to `target` is given, beside what it renders. With no
|
|
3916
|
-
* `getPageHref` it's a button; a link that's `off` goes nowhere.
|
|
3917
|
-
*/
|
|
3918
|
-
const linkTo = (target, off) => ({
|
|
3919
|
-
href: off || !getPageHref ? undefined : getPageHref(target),
|
|
3920
|
-
role: getPageHref ? undefined : 'button',
|
|
3921
|
-
// A link that's off is disabled too, and the parts drop its clicks.
|
|
3922
|
-
onClick: () => choose(target),
|
|
3923
|
-
onKeyDown: getPageHref
|
|
3924
|
-
? undefined
|
|
3925
|
-
: (e) => {
|
|
3926
|
-
if (off || (e.key !== 'Enter' && e.key !== ' '))
|
|
3927
|
-
return;
|
|
3928
|
-
e.preventDefault();
|
|
3929
|
-
choose(target);
|
|
3930
|
-
},
|
|
3931
|
-
});
|
|
3932
|
-
const atStart = disabled || page <= 1;
|
|
3933
|
-
const atEnd = disabled || page >= pages;
|
|
3934
|
-
content = (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(PaginationPrevious, { disabled: atStart, ...linkTo(page - 1, atStart), children: previousLabel }), jsxRuntime.jsx(PaginationNext, { disabled: atEnd, ...linkTo(page + 1, atEnd), children: nextLabel }), jsxRuntime.jsx(PaginationList, { children: pageItems(pages, page, whole(siblingCount, 1), whole(boundaryCount, 1)).map(item => typeof item === 'number' ? (jsxRuntime.jsx(PaginationLink, { "aria-label": getPageLabel ? getPageLabel(item) : `Page ${item}`, active: item === page, disabled: disabled, ...linkTo(item, Boolean(disabled)), children: item }, item)) : (jsxRuntime.jsx(PaginationEllipsis, {}, item))) })] }));
|
|
3935
|
-
}
|
|
3936
|
-
return (jsxRuntime.jsx("nav", { className: paginationClasses, role: "navigation", "aria-label": "pagination", ...rest, children: content }));
|
|
3937
|
-
};
|
|
3938
|
-
/**
|
|
3939
|
-
* Container for page links and ellipsis.
|
|
3940
|
-
*
|
|
3941
|
-
* @function
|
|
3942
|
-
* @param {PaginationListProps} props - Props for the PaginationList component.
|
|
3943
|
-
* @returns {JSX.Element} The rendered pagination list.
|
|
3944
|
-
*/
|
|
3945
|
-
const PaginationList = ({ className, textColor, bgColor, children, ...props }) => {
|
|
3946
|
-
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
3947
|
-
color: textColor,
|
|
3948
|
-
backgroundColor: bgColor,
|
|
3949
|
-
...props,
|
|
3950
|
-
});
|
|
3951
|
-
return (jsxRuntime.jsx("ul", { className: classNames(usePrefixedClassNames('pagination-list'), bulmaHelperClasses, className), ...rest, children: children }));
|
|
3952
|
-
};
|
|
3953
|
-
/**
|
|
3954
|
-
* Page number or navigation link.
|
|
3955
|
-
*
|
|
3956
|
-
* @function
|
|
3957
|
-
* @param {PaginationLinkProps} props - Props for the PaginationLink component.
|
|
3958
|
-
* @returns {JSX.Element} The rendered pagination link.
|
|
3959
|
-
*/
|
|
3960
|
-
const PaginationLink = ({ className, textColor, bgColor, active, disabled, onClick, children, ...props }) => {
|
|
3961
|
-
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
3962
|
-
color: textColor,
|
|
3963
|
-
backgroundColor: bgColor,
|
|
3964
|
-
...props,
|
|
3965
|
-
});
|
|
3966
|
-
const handleClick = (e) => {
|
|
3967
|
-
if (disabled) {
|
|
3968
|
-
e.preventDefault();
|
|
3969
|
-
e.stopPropagation();
|
|
3970
|
-
return;
|
|
3971
|
-
}
|
|
3972
|
-
if (onClick) {
|
|
3973
|
-
onClick(e);
|
|
3974
|
-
}
|
|
3975
|
-
};
|
|
3976
|
-
return (jsxRuntime.jsx("li", { children: jsxRuntime.jsx("a", { className: classNames(usePrefixedClassNames('pagination-link', {
|
|
3977
|
-
'is-current': active,
|
|
3978
|
-
'is-disabled': disabled,
|
|
3979
|
-
}), bulmaHelperClasses, className), "aria-current": active ? 'page' : undefined, "aria-disabled": disabled, tabIndex: disabled ? -1 : 0, onClick: handleClick, ...rest, children: children }) }));
|
|
3980
|
-
};
|
|
3981
|
-
/**
|
|
3982
|
-
* Ellipsis separator.
|
|
3983
|
-
*
|
|
3984
|
-
* @function
|
|
3985
|
-
* @param {React.LiHTMLAttributes<HTMLLIElement>} props - Standard li props.
|
|
3986
|
-
* @returns {JSX.Element} The rendered ellipsis.
|
|
3987
|
-
*/
|
|
3988
|
-
const PaginationEllipsis = props => (jsxRuntime.jsx("li", { children: jsxRuntime.jsx("span", { className: usePrefixedClassNames('pagination-ellipsis'), ...props, children: "\u2026" }) }));
|
|
3989
|
-
const Pagination = withSubComponents(PaginationComponent, {
|
|
3990
|
-
Link: PaginationLink,
|
|
3991
|
-
List: PaginationList,
|
|
3992
|
-
Ellipsis: PaginationEllipsis,
|
|
3993
|
-
Previous: PaginationPrevious,
|
|
3994
|
-
Next: PaginationNext,
|
|
3995
|
-
}, 'Pagination');
|
|
3996
|
-
|
|
3997
|
-
/**
|
|
3998
|
-
* Strips a redundant leading library prefix from an icon name (e.g. `fa-check` -> `check`
|
|
3999
|
-
* for the `fa` library, `mdi-account` -> `account` for `mdi`), so `name` behaves the same
|
|
4000
|
-
* whether or not the caller includes the prefix. Font Awesome and Material Design Icons docs
|
|
4001
|
-
* conventionally refer to icons with their class prefix (`fa-check`), which otherwise gets
|
|
4002
|
-
* doubled by `getIconClasses` (producing `fa-fa-check`, matching no glyph).
|
|
4003
|
-
*
|
|
4004
|
-
* @param {string} name - The icon name, possibly including a redundant library prefix.
|
|
4005
|
-
* @param {IconLibrary} library - The icon library the name will be rendered with.
|
|
4006
|
-
* @returns {string} The icon name with any redundant leading library prefix removed.
|
|
4007
|
-
*/
|
|
4008
|
-
function stripRedundantLibraryPrefix(name, library) {
|
|
4009
|
-
if (!name) {
|
|
4010
|
-
return name;
|
|
4011
|
-
}
|
|
4012
|
-
if (library === 'fa' && name.startsWith('fa-')) {
|
|
4013
|
-
return name.substring(3);
|
|
4014
|
-
}
|
|
4015
|
-
if (library === 'mdi' && name.startsWith('mdi-')) {
|
|
4016
|
-
return name.substring(4);
|
|
4017
|
-
}
|
|
4018
|
-
return name;
|
|
4525
|
+
...range(1, boundaries),
|
|
4526
|
+
...gap(boundaries + 1, start - 1, 'start-gap'),
|
|
4527
|
+
...range(start, end),
|
|
4528
|
+
...gap(end + 1, total - boundaries, 'end-gap'),
|
|
4529
|
+
...range(total - boundaries + 1, total),
|
|
4530
|
+
];
|
|
4019
4531
|
}
|
|
4020
4532
|
/**
|
|
4021
|
-
*
|
|
4533
|
+
* "Previous" navigation button.
|
|
4022
4534
|
*
|
|
4023
|
-
* @
|
|
4024
|
-
* @param {
|
|
4025
|
-
* @
|
|
4026
|
-
* @param {string | string[]} [features] - Additional library-specific modifiers.
|
|
4027
|
-
* @returns {string} The combined class string for the icon.
|
|
4535
|
+
* @function
|
|
4536
|
+
* @param {PaginationPreviousNextProps} props - Props for the PaginationPrevious component.
|
|
4537
|
+
* @returns {JSX.Element} The rendered previous button.
|
|
4028
4538
|
*/
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
? [features]
|
|
4036
|
-
: [];
|
|
4037
|
-
switch (library) {
|
|
4038
|
-
case 'fa': {
|
|
4039
|
-
// Font Awesome: use variant as style ('solid' -> 'fas', 'regular' -> 'far', etc.)
|
|
4040
|
-
const styleMap = {
|
|
4041
|
-
solid: 'fas',
|
|
4042
|
-
regular: 'far',
|
|
4043
|
-
brands: 'fab',
|
|
4044
|
-
light: 'fal',
|
|
4045
|
-
duotone: 'fad',
|
|
4046
|
-
thin: 'fat',
|
|
4047
|
-
};
|
|
4048
|
-
const faStyle = variant ? styleMap[variant] || variant : 'fas';
|
|
4049
|
-
baseClass = faStyle;
|
|
4050
|
-
iconClass = `fa-${name}`;
|
|
4051
|
-
return [baseClass, iconClass, ...featureList].join(' ');
|
|
4052
|
-
}
|
|
4053
|
-
case 'mdi':
|
|
4054
|
-
// Material Design Icons: no variants, just features
|
|
4055
|
-
baseClass = 'mdi';
|
|
4056
|
-
iconClass = `mdi-${name}`;
|
|
4057
|
-
return [baseClass, iconClass, ...featureList].join(' ');
|
|
4058
|
-
case 'material-icons': {
|
|
4059
|
-
// Google Material Icons: map variants to full class names
|
|
4060
|
-
const styleVariants = {
|
|
4061
|
-
filled: 'material-icons',
|
|
4062
|
-
outlined: 'material-icons-outlined',
|
|
4063
|
-
round: 'material-icons-round',
|
|
4064
|
-
sharp: 'material-icons-sharp',
|
|
4065
|
-
};
|
|
4066
|
-
baseClass = variant
|
|
4067
|
-
? styleVariants[variant] || `material-icons-${variant}`
|
|
4068
|
-
: 'material-icons';
|
|
4069
|
-
return [baseClass, ...featureList].join(' ');
|
|
4539
|
+
const PaginationPrevious = ({ className, disabled, children, ...props }) => (jsxRuntime.jsx("a", { className: classNames(usePrefixedClassNames('pagination-previous', {
|
|
4540
|
+
'is-disabled': disabled,
|
|
4541
|
+
}), className), "aria-disabled": disabled, tabIndex: disabled ? -1 : 0, ...props, onClick: disabled
|
|
4542
|
+
? e => {
|
|
4543
|
+
e.preventDefault();
|
|
4544
|
+
e.stopPropagation();
|
|
4070
4545
|
}
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4546
|
+
: props.onClick, children: children }));
|
|
4547
|
+
/**
|
|
4548
|
+
* "Next" navigation button.
|
|
4549
|
+
*
|
|
4550
|
+
* @function
|
|
4551
|
+
* @param {PaginationPreviousNextProps} props - Props for the PaginationNext component.
|
|
4552
|
+
* @returns {JSX.Element} The rendered next button.
|
|
4553
|
+
*/
|
|
4554
|
+
const PaginationNext = ({ className, disabled, children, ...props }) => (jsxRuntime.jsx("a", { className: classNames(usePrefixedClassNames('pagination-next', { 'is-disabled': disabled }), className), "aria-disabled": disabled, tabIndex: disabled ? -1 : 0, ...props, onClick: disabled
|
|
4555
|
+
? e => {
|
|
4556
|
+
e.preventDefault();
|
|
4557
|
+
e.stopPropagation();
|
|
4082
4558
|
}
|
|
4083
|
-
|
|
4084
|
-
// fallback: just icon name and features
|
|
4085
|
-
return [name, ...featureList].join(' ');
|
|
4086
|
-
}
|
|
4087
|
-
}
|
|
4559
|
+
: props.onClick, children: children }));
|
|
4088
4560
|
/**
|
|
4089
|
-
* The `
|
|
4561
|
+
* The `Pagination` component provides a flexible, composable Bulma pagination navigation for your Bulma React UI.
|
|
4090
4562
|
*
|
|
4091
4563
|
* @function
|
|
4092
|
-
* @param {
|
|
4093
|
-
* @returns {JSX.Element} The rendered
|
|
4094
|
-
* @see {@link https://bulma.io/documentation/
|
|
4564
|
+
* @param {PaginationProps} props - Props for the Pagination component.
|
|
4565
|
+
* @returns {JSX.Element} The rendered pagination.
|
|
4566
|
+
* @see {@link https://bulma.io/documentation/components/pagination/ | Bulma Pagination documentation}
|
|
4095
4567
|
*/
|
|
4096
|
-
const
|
|
4097
|
-
|
|
4098
|
-
color
|
|
4099
|
-
containerClassName, children, ...restProps }) => {
|
|
4100
|
-
// Get the default icon library from context, fallback to 'fa' if not set
|
|
4101
|
-
const defaultLibrary = useIconLibrary();
|
|
4102
|
-
/**
|
|
4103
|
-
* Generates Bulma helper classes and separates out remaining props.
|
|
4104
|
-
* Note: variant, features, and libraryFeatures are excluded from props spread
|
|
4105
|
-
*/
|
|
4568
|
+
const PaginationComponent = ({ color, textColor, bgColor, size, align, rounded, total, current, onPageChange, siblingCount = 1, boundaryCount = 1, previousLabel = 'Previous', nextLabel = 'Next', disabled, getPageHref, getPageLabel, className, children, ...props }) => {
|
|
4569
|
+
const [kept, setKept] = React.useState(1);
|
|
4570
|
+
warnDeprecatedColorProp('Pagination', color, 'Use the textColor / bgColor helper props instead.');
|
|
4106
4571
|
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
4107
4572
|
color: textColor,
|
|
4108
4573
|
backgroundColor: bgColor,
|
|
4109
|
-
...
|
|
4574
|
+
...props,
|
|
4110
4575
|
});
|
|
4111
|
-
//
|
|
4112
|
-
|
|
4113
|
-
|
|
4576
|
+
// Generate Bulma classes with prefix
|
|
4577
|
+
const bulmaClasses = usePrefixedClassNames('pagination', {
|
|
4578
|
+
[`is-${color}`]: color,
|
|
4114
4579
|
[`is-${size}`]: size,
|
|
4580
|
+
[`is-${align}`]: align,
|
|
4581
|
+
'is-rounded': rounded,
|
|
4115
4582
|
});
|
|
4116
|
-
const
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
}
|
|
4153
|
-
else if (lastPart.startsWith('fa-')) {
|
|
4154
|
-
finalName = lastPart.substring(3); // Remove "fa-" prefix
|
|
4155
|
-
}
|
|
4156
|
-
else {
|
|
4157
|
-
finalName = lastPart;
|
|
4158
|
-
}
|
|
4159
|
-
}
|
|
4160
|
-
}
|
|
4161
|
-
const finalLibrary = library || defaultLibrary || 'fa';
|
|
4162
|
-
// Normalize a redundant leading library prefix (e.g. "fa-check" -> "check" when the
|
|
4163
|
-
// library is 'fa'), so `name` behaves identically with or without the prefix.
|
|
4164
|
-
finalName = stripRedundantLibraryPrefix(finalName, finalLibrary);
|
|
4165
|
-
if (!finalName) {
|
|
4166
|
-
// No glyph to name. Reachable through the public type only as `icon=""`, and otherwise
|
|
4167
|
-
// by a plain-JS caller (or one that casts). Building a class off an absent name produced
|
|
4168
|
-
// a bogus `fa-undefined` glyph; render the bare container instead, matching the
|
|
4169
|
-
// `children` branch.
|
|
4170
|
-
return (jsxRuntime.jsx("span", { className: iconContainerClasses, ...a11yProps, style: style, ...rest }));
|
|
4171
|
-
}
|
|
4172
|
-
// Backward compatibility: if libraryFeatures is provided, parse it for variant and features
|
|
4173
|
-
let finalVariant = variant;
|
|
4174
|
-
let finalFeatures = features;
|
|
4175
|
-
if (libraryFeatures && !variant && !features) {
|
|
4176
|
-
const legacyFeatures = Array.isArray(libraryFeatures)
|
|
4177
|
-
? libraryFeatures
|
|
4178
|
-
: [libraryFeatures];
|
|
4179
|
-
// For Font Awesome, extract style from features
|
|
4180
|
-
if (finalLibrary === 'fa') {
|
|
4181
|
-
const faStyle = legacyFeatures.find(f => [
|
|
4182
|
-
'fas',
|
|
4183
|
-
'far',
|
|
4184
|
-
'fab',
|
|
4185
|
-
'fal',
|
|
4186
|
-
'fad',
|
|
4187
|
-
'fat',
|
|
4188
|
-
'solid',
|
|
4189
|
-
'regular',
|
|
4190
|
-
'brands',
|
|
4191
|
-
'light',
|
|
4192
|
-
'duotone',
|
|
4193
|
-
'thin',
|
|
4194
|
-
].includes(f));
|
|
4195
|
-
if (faStyle) {
|
|
4196
|
-
finalVariant = faStyle;
|
|
4197
|
-
finalFeatures = legacyFeatures.filter(f => f !== faStyle);
|
|
4198
|
-
}
|
|
4199
|
-
else {
|
|
4200
|
-
finalFeatures = legacyFeatures;
|
|
4201
|
-
}
|
|
4202
|
-
}
|
|
4203
|
-
// For Material Icons/Symbols, extract style variant
|
|
4204
|
-
else if (finalLibrary === 'material-icons' ||
|
|
4205
|
-
finalLibrary === 'material-symbols') {
|
|
4206
|
-
const styleVariants = finalLibrary === 'material-icons'
|
|
4207
|
-
? ['filled', 'outlined', 'round', 'sharp']
|
|
4208
|
-
: ['outlined', 'rounded', 'sharp'];
|
|
4209
|
-
const styleVariant = legacyFeatures.find(f => styleVariants.includes(f));
|
|
4210
|
-
if (styleVariant) {
|
|
4211
|
-
finalVariant = styleVariant;
|
|
4212
|
-
finalFeatures = legacyFeatures.filter(f => f !== styleVariant);
|
|
4213
|
-
}
|
|
4214
|
-
else {
|
|
4215
|
-
finalFeatures = legacyFeatures;
|
|
4216
|
-
}
|
|
4217
|
-
}
|
|
4218
|
-
// For others, all features go to finalFeatures
|
|
4219
|
-
else {
|
|
4220
|
-
finalFeatures = legacyFeatures;
|
|
4221
|
-
}
|
|
4583
|
+
const paginationClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
|
|
4584
|
+
// A child that renders nothing (`{show && …}` when false) is no children.
|
|
4585
|
+
const fromTotal = total !== undefined && React.Children.toArray(children).length === 0;
|
|
4586
|
+
let content = children;
|
|
4587
|
+
if (fromTotal) {
|
|
4588
|
+
const pages = Number.isFinite(total) ? Math.max(Math.floor(total), 0) : 0;
|
|
4589
|
+
// A current page that's no number reads as the first.
|
|
4590
|
+
const page = Math.min(Math.max(whole(current ?? kept, 1), 1), Math.max(pages, 1));
|
|
4591
|
+
const choose = (next) => {
|
|
4592
|
+
if (next === page)
|
|
4593
|
+
return;
|
|
4594
|
+
if (current === undefined)
|
|
4595
|
+
setKept(next);
|
|
4596
|
+
onPageChange?.(next);
|
|
4597
|
+
};
|
|
4598
|
+
/**
|
|
4599
|
+
* What a link to `target` is given, beside what it renders. With no
|
|
4600
|
+
* `getPageHref` it's a button; a link that's `off` goes nowhere.
|
|
4601
|
+
*/
|
|
4602
|
+
const linkTo = (target, off) => ({
|
|
4603
|
+
href: off || !getPageHref ? undefined : getPageHref(target),
|
|
4604
|
+
role: getPageHref ? undefined : 'button',
|
|
4605
|
+
// A link that's off is disabled too, and the parts drop its clicks.
|
|
4606
|
+
onClick: () => choose(target),
|
|
4607
|
+
onKeyDown: getPageHref
|
|
4608
|
+
? undefined
|
|
4609
|
+
: (e) => {
|
|
4610
|
+
if (off || (e.key !== 'Enter' && e.key !== ' '))
|
|
4611
|
+
return;
|
|
4612
|
+
e.preventDefault();
|
|
4613
|
+
choose(target);
|
|
4614
|
+
},
|
|
4615
|
+
});
|
|
4616
|
+
const atStart = disabled || page <= 1;
|
|
4617
|
+
const atEnd = disabled || page >= pages;
|
|
4618
|
+
content = (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(PaginationPrevious, { disabled: atStart, ...linkTo(page - 1, atStart), children: previousLabel }), jsxRuntime.jsx(PaginationNext, { disabled: atEnd, ...linkTo(page + 1, atEnd), children: nextLabel }), jsxRuntime.jsx(PaginationList, { children: pageItems(pages, page, whole(siblingCount, 1), whole(boundaryCount, 1)).map(item => typeof item === 'number' ? (jsxRuntime.jsx(PaginationLink, { "aria-label": getPageLabel ? getPageLabel(item) : `Page ${item}`, active: item === page, disabled: disabled, ...linkTo(item, Boolean(disabled)), children: item }, item)) : (jsxRuntime.jsx(PaginationEllipsis, {}, item))) })] }));
|
|
4222
4619
|
}
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
|
|
4228
|
-
|
|
4620
|
+
return (jsxRuntime.jsx("nav", { className: paginationClasses, role: "navigation", "aria-label": "pagination", ...rest, children: content }));
|
|
4621
|
+
};
|
|
4622
|
+
/**
|
|
4623
|
+
* Container for page links and ellipsis.
|
|
4624
|
+
*
|
|
4625
|
+
* @function
|
|
4626
|
+
* @param {PaginationListProps} props - Props for the PaginationList component.
|
|
4627
|
+
* @returns {JSX.Element} The rendered pagination list.
|
|
4628
|
+
*/
|
|
4629
|
+
const PaginationList = ({ className, textColor, bgColor, children, ...props }) => {
|
|
4630
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
4631
|
+
color: textColor,
|
|
4632
|
+
backgroundColor: bgColor,
|
|
4633
|
+
...props,
|
|
4634
|
+
});
|
|
4635
|
+
return (jsxRuntime.jsx("ul", { className: classNames(usePrefixedClassNames('pagination-list'), bulmaHelperClasses, className), ...rest, children: children }));
|
|
4636
|
+
};
|
|
4637
|
+
/**
|
|
4638
|
+
* Page number or navigation link.
|
|
4639
|
+
*
|
|
4640
|
+
* @function
|
|
4641
|
+
* @param {PaginationLinkProps} props - Props for the PaginationLink component.
|
|
4642
|
+
* @returns {JSX.Element} The rendered pagination link.
|
|
4643
|
+
*/
|
|
4644
|
+
const PaginationLink = ({ className, textColor, bgColor, active, disabled, onClick, children, ...props }) => {
|
|
4645
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
4646
|
+
color: textColor,
|
|
4647
|
+
backgroundColor: bgColor,
|
|
4648
|
+
...props,
|
|
4649
|
+
});
|
|
4650
|
+
const handleClick = (e) => {
|
|
4651
|
+
if (disabled) {
|
|
4652
|
+
e.preventDefault();
|
|
4653
|
+
e.stopPropagation();
|
|
4654
|
+
return;
|
|
4229
4655
|
}
|
|
4230
|
-
|
|
4231
|
-
|
|
4656
|
+
if (onClick) {
|
|
4657
|
+
onClick(e);
|
|
4232
4658
|
}
|
|
4233
|
-
|
|
4234
|
-
|
|
4235
|
-
|
|
4236
|
-
|
|
4237
|
-
|
|
4238
|
-
if (finalLibrary === 'material-icons' ||
|
|
4239
|
-
finalLibrary === 'material-symbols') {
|
|
4240
|
-
return (jsxRuntime.jsx("span", { className: iconContainerClasses, ...a11yProps, style: style, ...rest, children: jsxRuntime.jsx("i", { className: iClasses, children: finalName }) }));
|
|
4241
|
-
}
|
|
4242
|
-
return (jsxRuntime.jsx("span", { className: iconContainerClasses, ...a11yProps, style: style, ...rest, children: jsxRuntime.jsx("i", { className: iClasses }) }));
|
|
4659
|
+
};
|
|
4660
|
+
return (jsxRuntime.jsx("li", { children: jsxRuntime.jsx("a", { className: classNames(usePrefixedClassNames('pagination-link', {
|
|
4661
|
+
'is-current': active,
|
|
4662
|
+
'is-disabled': disabled,
|
|
4663
|
+
}), bulmaHelperClasses, className), "aria-current": active ? 'page' : undefined, "aria-disabled": disabled, tabIndex: disabled ? -1 : 0, onClick: handleClick, ...rest, children: children }) }));
|
|
4243
4664
|
};
|
|
4665
|
+
/**
|
|
4666
|
+
* Ellipsis separator.
|
|
4667
|
+
*
|
|
4668
|
+
* @function
|
|
4669
|
+
* @param {React.LiHTMLAttributes<HTMLLIElement>} props - Standard li props.
|
|
4670
|
+
* @returns {JSX.Element} The rendered ellipsis.
|
|
4671
|
+
*/
|
|
4672
|
+
const PaginationEllipsis = props => (jsxRuntime.jsx("li", { children: jsxRuntime.jsx("span", { className: usePrefixedClassNames('pagination-ellipsis'), ...props, children: "\u2026" }) }));
|
|
4673
|
+
const Pagination = withSubComponents(PaginationComponent, {
|
|
4674
|
+
Link: PaginationLink,
|
|
4675
|
+
List: PaginationList,
|
|
4676
|
+
Ellipsis: PaginationEllipsis,
|
|
4677
|
+
Previous: PaginationPrevious,
|
|
4678
|
+
Next: PaginationNext,
|
|
4679
|
+
}, 'Pagination');
|
|
4244
4680
|
|
|
4245
4681
|
/**
|
|
4246
4682
|
* The `Panel` component implements Bulma's versatile panel block for React.
|
|
@@ -4595,196 +5031,67 @@ const Tab = ({ index, disabled, icon, iconLibrary, iconVariant, iconSize = 'smal
|
|
|
4595
5031
|
};
|
|
4596
5032
|
const handleBlur = (e) => {
|
|
4597
5033
|
onBlur?.(e);
|
|
4598
|
-
if (!tabListOf(e.currentTarget).contains(e.relatedTarget)) {
|
|
4599
|
-
ctx?.setFocusedTab(null);
|
|
4600
|
-
}
|
|
4601
|
-
};
|
|
4602
|
-
return (jsxRuntime.jsx("li", { className: classNames(activeClass, className) || undefined, role: "tab", id: tabId, "aria-selected": isActive, "aria-controls": ctx?.panelId(index), "aria-disabled": disabled || undefined, tabIndex: tabIndex, onKeyDown: handleKeyDown, onFocus: handleFocus, onBlur: handleBlur, ...props, children: jsxRuntime.jsxs("a", { ref: anchorRef, onClick: handleClick, "aria-disabled": disabled || undefined, children: [icon && (jsxRuntime.jsx(Icon, { name: icon, library: iconLibrary, variant: iconVariant, size: iconSize, features: iconFeatures })), children && jsxRuntime.jsx("span", { children: children })] }) }));
|
|
4603
|
-
};
|
|
4604
|
-
/**
|
|
4605
|
-
* Each tab; accepts `active`, `onClick`, etc.
|
|
4606
|
-
*
|
|
4607
|
-
* @function
|
|
4608
|
-
* @param {TabItemProps} props - Props for the TabItem component.
|
|
4609
|
-
* @returns {JSX.Element} The rendered tab item.
|
|
4610
|
-
* @deprecated Use `Tabs.Tab` with an `index` prop instead.
|
|
4611
|
-
*/
|
|
4612
|
-
const TabItem = ({ active, className, children, onClick, ...props }) => (jsxRuntime.jsx("li", { className: classNames({ [usePrefixedClassNames('is-active')]: active }, className) ||
|
|
4613
|
-
undefined, onClick: onClick, ...props, children: children }));
|
|
4614
|
-
/**
|
|
4615
|
-
* Container for tab content panels. No custom props beyond `children` and standard `<div>` HTML attributes. Applies the `.tabs-content` class.
|
|
4616
|
-
*
|
|
4617
|
-
* @function
|
|
4618
|
-
* @param {TabsContentProps} props - Props for the TabsContent component.
|
|
4619
|
-
* @returns {JSX.Element} The rendered tabs content wrapper.
|
|
4620
|
-
*/
|
|
4621
|
-
const TabsContentComponent = ({ className, children, ...props }) => {
|
|
4622
|
-
const contentClass = usePrefixedClassNames('tabs-content');
|
|
4623
|
-
return (jsxRuntime.jsx("div", { className: classNames(contentClass, className), ...props, children: children }));
|
|
4624
|
-
};
|
|
4625
|
-
/**
|
|
4626
|
-
* Individual content panel. Shows/hides based on active tab from context.
|
|
4627
|
-
*
|
|
4628
|
-
* @function
|
|
4629
|
-
* @param {TabContentItemProps} props - Props for the TabContentItem component.
|
|
4630
|
-
* @returns {JSX.Element} The rendered tab content panel.
|
|
4631
|
-
*/
|
|
4632
|
-
const TabContentItem = ({ index, className, children, id, ...props }) => {
|
|
4633
|
-
const ctx = useTabsContext();
|
|
4634
|
-
const isActive = ctx ? ctx.activeTab === index : false;
|
|
4635
|
-
const itemClass = usePrefixedClassNames('tabs-content-item', {
|
|
4636
|
-
'is-active': isActive,
|
|
4637
|
-
});
|
|
4638
|
-
const panelId = id ?? (ctx ? `${ctx.baseId}-panel-${index}` : undefined);
|
|
4639
|
-
const registerPanel = ctx?.registerPanel;
|
|
4640
|
-
React.useEffect(() => {
|
|
4641
|
-
if (!registerPanel || panelId === undefined)
|
|
4642
|
-
return undefined;
|
|
4643
|
-
return registerPanel({ index, id: panelId });
|
|
4644
|
-
}, [registerPanel, index, panelId]);
|
|
4645
|
-
return (jsxRuntime.jsx("div", { className: classNames(itemClass, className), role: "tabpanel", id: panelId, "aria-labelledby": ctx?.tabId(index), "aria-hidden": !isActive, ...props, children: children }));
|
|
4646
|
-
};
|
|
4647
|
-
// ---------------------------------------------------------------------------
|
|
4648
|
-
// Static property attachment
|
|
4649
|
-
// ---------------------------------------------------------------------------
|
|
4650
|
-
const TabsContent = withSubComponents(TabsContentComponent, {
|
|
4651
|
-
Item: TabContentItem,
|
|
4652
|
-
});
|
|
4653
|
-
const Tabs = withSubComponents(TabsComponent, {
|
|
4654
|
-
List: TabList,
|
|
4655
|
-
Tab,
|
|
4656
|
-
Item: TabItem,
|
|
4657
|
-
Content: TabsContent,
|
|
4658
|
-
}, 'Tabs');
|
|
4659
|
-
|
|
4660
|
-
const validButtonColors = [...validColors, 'text', 'ghost'];
|
|
4661
|
-
/**
|
|
4662
|
-
* The `Button` component provides a flexible and highly customizable button for your Bulma React UI.
|
|
4663
|
-
*
|
|
4664
|
-
* @function
|
|
4665
|
-
* @param {ButtonProps} props - Props for the Button component.
|
|
4666
|
-
* @param {React.Ref} ref - Forwarded ref to the element `as` renders.
|
|
4667
|
-
* @returns {JSX.Element} The rendered button, anchor, or custom element.
|
|
4668
|
-
* @see {@link https://bulma.io/documentation/elements/button/ | Bulma Button documentation}
|
|
4669
|
-
*/
|
|
4670
|
-
const Button = React.forwardRef(function Button(props, ref) {
|
|
4671
|
-
const { color, size, isLight, isRounded, isLoading, isStatic, isFullwidth, isFullWidth, isOutlined, isInverted, isFocused, isActive, isHovered, isDisabled, className, children, textColor, bgColor, as: Component = 'button', href, onClick, target, rel, ...bulmaProps } = props;
|
|
4672
|
-
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
4673
|
-
color: textColor,
|
|
4674
|
-
backgroundColor: bgColor,
|
|
4675
|
-
...bulmaProps,
|
|
4676
|
-
});
|
|
4677
|
-
// Generate Bulma classes with prefix
|
|
4678
|
-
const bulmaClasses = usePrefixedClassNames('button', {
|
|
4679
|
-
[`is-${color}`]: color &&
|
|
4680
|
-
validButtonColors.includes(color),
|
|
4681
|
-
[`is-${size}`]: size,
|
|
4682
|
-
'is-outlined': isOutlined,
|
|
4683
|
-
'is-light': isLight,
|
|
4684
|
-
'is-loading': isLoading,
|
|
4685
|
-
'is-static': isStatic,
|
|
4686
|
-
'is-disabled': isDisabled,
|
|
4687
|
-
'is-rounded': isRounded,
|
|
4688
|
-
'is-hovered': isHovered,
|
|
4689
|
-
'is-focused': isFocused,
|
|
4690
|
-
'is-active': isActive,
|
|
4691
|
-
'is-inverted': isInverted,
|
|
4692
|
-
'is-fullwidth': isFullwidth ?? isFullWidth,
|
|
4693
|
-
});
|
|
4694
|
-
// Combine prefixed Bulma classes with unprefixed user className and prefixed helper classes
|
|
4695
|
-
const buttonClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
|
|
4696
|
-
if (Component !== 'button') {
|
|
4697
|
-
// Strip the form-control attributes a link-like element cannot carry, so a
|
|
4698
|
-
// JavaScript consumer or a spread object doesn't put them on the DOM. The
|
|
4699
|
-
// types no longer allow them through; this is the runtime backstop.
|
|
4700
|
-
//
|
|
4701
|
-
// Only for INTRINSIC tags. A custom component (a router Link, ...) owns its
|
|
4702
|
-
// prop contract, and `ComponentPropsWithoutRef<T>` promises the caller that
|
|
4703
|
-
// its props reach it. Stripping names by their spelling alone broke that
|
|
4704
|
-
// promise silently: `<Button as={Custom} name="x" />` type-checked while
|
|
4705
|
-
// `Custom` never received the `name` it requires.
|
|
4706
|
-
//
|
|
4707
|
-
// `autoFocus` and `type` are deliberately NOT stripped: both are valid on
|
|
4708
|
-
// targets this list was filtering them from. `autoFocus` is a global
|
|
4709
|
-
// attribute (React decides per element whether to honour it, which is not
|
|
4710
|
-
// ours to pre-empt), and `type` on an anchor is the MIME hint that
|
|
4711
|
-
// `ButtonProps<'a'>` now types it as.
|
|
4712
|
-
//
|
|
4713
|
-
// The types already answer this for a TypeScript caller: `ButtonProps<T>`
|
|
4714
|
-
// derives from `ComponentPropsWithoutRef<T>`, so only what the target
|
|
4715
|
-
// accepts can be passed. What is left is a runtime backstop for JavaScript
|
|
4716
|
-
// consumers and spread objects.
|
|
4717
|
-
//
|
|
4718
|
-
// Keep it to the two cases where a stray attribute does something. The
|
|
4719
|
-
// submit-overrides belong to a submit control and never make sense on the
|
|
4720
|
-
// anchor path. `disabled` is the one that is VISIBLE: Bulma styles
|
|
4721
|
-
// `.button[disabled]` (background, border, shadow, opacity), so letting it
|
|
4722
|
-
// through to a `<span>` greys the element out — main stripped it, and not
|
|
4723
|
-
// stripping it would be a silent visual change on a types-only release.
|
|
4724
|
-
//
|
|
4725
|
-
// Everything else (`name`, `value`, `form`) is inert where it does not
|
|
4726
|
-
// belong, and enumerating which tags own it is what kept going wrong:
|
|
4727
|
-
// first all nine names on every non-button tag (so `<Button as="input"
|
|
4728
|
-
// name="query" value="Search">` submitted nothing), then a form-control
|
|
4729
|
-
// allowlist that still lost `formAction` on an input and `value` on an
|
|
4730
|
-
// option. Those now ride on the types instead.
|
|
4731
|
-
//
|
|
4732
|
-
// A custom component is exempt entirely — it owns its prop contract, and
|
|
4733
|
-
// `ComponentPropsWithoutRef<T>` promises the caller its props arrive.
|
|
4734
|
-
//
|
|
4735
|
-
// The elements that own `disabled`, per the HTML spec. A closed set, unlike
|
|
4736
|
-
// the open-ended question of which tag owns which attribute.
|
|
4737
|
-
const SUBMIT_OVERRIDES = [
|
|
4738
|
-
'formAction',
|
|
4739
|
-
'formEncType',
|
|
4740
|
-
'formMethod',
|
|
4741
|
-
'formNoValidate',
|
|
4742
|
-
'formTarget',
|
|
4743
|
-
];
|
|
4744
|
-
const OWNS_DISABLED = [
|
|
4745
|
-
'button',
|
|
4746
|
-
'fieldset',
|
|
4747
|
-
'input',
|
|
4748
|
-
'optgroup',
|
|
4749
|
-
'option',
|
|
4750
|
-
'select',
|
|
4751
|
-
'textarea',
|
|
4752
|
-
];
|
|
4753
|
-
// Two independent filters. They are not exclusive — an `<a>` owns neither
|
|
4754
|
-
// the submit-overrides nor `disabled`, so it takes both — and deriving them
|
|
4755
|
-
// from one destructure, or from one if/else, is what broke each of them in
|
|
4756
|
-
// turn: first the overrides came off every intrinsic (an `as="input"` lost
|
|
4757
|
-
// `formAction`), then scoping them to the anchor let `disabled` back onto
|
|
4758
|
-
// it.
|
|
4759
|
-
const forwardedRest = { ...rest };
|
|
4760
|
-
if (typeof Component === 'string' && !isCustomElement(Component)) {
|
|
4761
|
-
// Meaningless anywhere but a submit control, and an anchor is not one.
|
|
4762
|
-
if (Component === 'a') {
|
|
4763
|
-
for (const key of SUBMIT_OVERRIDES)
|
|
4764
|
-
delete forwardedRest[key];
|
|
4765
|
-
}
|
|
4766
|
-
// Visible via Bulma's `.button[disabled]`, so withheld from any element
|
|
4767
|
-
// that does not own it.
|
|
4768
|
-
if (!OWNS_DISABLED.includes(Component))
|
|
4769
|
-
delete forwardedRest.disabled;
|
|
5034
|
+
if (!tabListOf(e.currentTarget).contains(e.relatedTarget)) {
|
|
5035
|
+
ctx?.setFocusedTab(null);
|
|
4770
5036
|
}
|
|
4771
|
-
|
|
4772
|
-
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
|
|
4776
|
-
|
|
4777
|
-
|
|
4778
|
-
|
|
4779
|
-
|
|
4780
|
-
|
|
4781
|
-
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
|
|
4785
|
-
|
|
5037
|
+
};
|
|
5038
|
+
return (jsxRuntime.jsx("li", { className: classNames(activeClass, className) || undefined, role: "tab", id: tabId, "aria-selected": isActive, "aria-controls": ctx?.panelId(index), "aria-disabled": disabled || undefined, tabIndex: tabIndex, onKeyDown: handleKeyDown, onFocus: handleFocus, onBlur: handleBlur, ...props, children: jsxRuntime.jsxs("a", { ref: anchorRef, onClick: handleClick, "aria-disabled": disabled || undefined, children: [icon && (jsxRuntime.jsx(Icon, { name: icon, library: iconLibrary, variant: iconVariant, size: iconSize, features: iconFeatures })), children && jsxRuntime.jsx("span", { children: children })] }) }));
|
|
5039
|
+
};
|
|
5040
|
+
/**
|
|
5041
|
+
* Each tab; accepts `active`, `onClick`, etc.
|
|
5042
|
+
*
|
|
5043
|
+
* @function
|
|
5044
|
+
* @param {TabItemProps} props - Props for the TabItem component.
|
|
5045
|
+
* @returns {JSX.Element} The rendered tab item.
|
|
5046
|
+
* @deprecated Use `Tabs.Tab` with an `index` prop instead.
|
|
5047
|
+
*/
|
|
5048
|
+
const TabItem = ({ active, className, children, onClick, ...props }) => (jsxRuntime.jsx("li", { className: classNames({ [usePrefixedClassNames('is-active')]: active }, className) ||
|
|
5049
|
+
undefined, onClick: onClick, ...props, children: children }));
|
|
5050
|
+
/**
|
|
5051
|
+
* Container for tab content panels. No custom props beyond `children` and standard `<div>` HTML attributes. Applies the `.tabs-content` class.
|
|
5052
|
+
*
|
|
5053
|
+
* @function
|
|
5054
|
+
* @param {TabsContentProps} props - Props for the TabsContent component.
|
|
5055
|
+
* @returns {JSX.Element} The rendered tabs content wrapper.
|
|
5056
|
+
*/
|
|
5057
|
+
const TabsContentComponent = ({ className, children, ...props }) => {
|
|
5058
|
+
const contentClass = usePrefixedClassNames('tabs-content');
|
|
5059
|
+
return (jsxRuntime.jsx("div", { className: classNames(contentClass, className), ...props, children: children }));
|
|
5060
|
+
};
|
|
5061
|
+
/**
|
|
5062
|
+
* Individual content panel. Shows/hides based on active tab from context.
|
|
5063
|
+
*
|
|
5064
|
+
* @function
|
|
5065
|
+
* @param {TabContentItemProps} props - Props for the TabContentItem component.
|
|
5066
|
+
* @returns {JSX.Element} The rendered tab content panel.
|
|
5067
|
+
*/
|
|
5068
|
+
const TabContentItem = ({ index, className, children, id, ...props }) => {
|
|
5069
|
+
const ctx = useTabsContext();
|
|
5070
|
+
const isActive = ctx ? ctx.activeTab === index : false;
|
|
5071
|
+
const itemClass = usePrefixedClassNames('tabs-content-item', {
|
|
5072
|
+
'is-active': isActive,
|
|
5073
|
+
});
|
|
5074
|
+
const panelId = id ?? (ctx ? `${ctx.baseId}-panel-${index}` : undefined);
|
|
5075
|
+
const registerPanel = ctx?.registerPanel;
|
|
5076
|
+
React.useEffect(() => {
|
|
5077
|
+
if (!registerPanel || panelId === undefined)
|
|
5078
|
+
return undefined;
|
|
5079
|
+
return registerPanel({ index, id: panelId });
|
|
5080
|
+
}, [registerPanel, index, panelId]);
|
|
5081
|
+
return (jsxRuntime.jsx("div", { className: classNames(itemClass, className), role: "tabpanel", id: panelId, "aria-labelledby": ctx?.tabId(index), "aria-hidden": !isActive, ...props, children: children }));
|
|
5082
|
+
};
|
|
5083
|
+
// ---------------------------------------------------------------------------
|
|
5084
|
+
// Static property attachment
|
|
5085
|
+
// ---------------------------------------------------------------------------
|
|
5086
|
+
const TabsContent = withSubComponents(TabsContentComponent, {
|
|
5087
|
+
Item: TabContentItem,
|
|
4786
5088
|
});
|
|
4787
|
-
|
|
5089
|
+
const Tabs = withSubComponents(TabsComponent, {
|
|
5090
|
+
List: TabList,
|
|
5091
|
+
Tab,
|
|
5092
|
+
Item: TabItem,
|
|
5093
|
+
Content: TabsContent,
|
|
5094
|
+
}, 'Tabs');
|
|
4788
5095
|
|
|
4789
5096
|
// INTERNAL: deliberately not exported from src/index.ts.
|
|
4790
5097
|
/**
|
|
@@ -8753,129 +9060,6 @@ const Box = ({ className, textColor, color, bgColor, hasShadow = true, children,
|
|
|
8753
9060
|
return (jsxRuntime.jsx("div", { className: boxClasses, style: mergeBulmaStyles(bulmaHelperStyles, style), ...rest, children: children }));
|
|
8754
9061
|
};
|
|
8755
9062
|
|
|
8756
|
-
/**
|
|
8757
|
-
* The `LinkButton` component renders a `<button>` that visually looks like text or a link.
|
|
8758
|
-
*
|
|
8759
|
-
* @function
|
|
8760
|
-
* @param {LinkButtonProps} props - Props for the LinkButton component.
|
|
8761
|
-
* @param {React.Ref} ref - Forwarded ref to the element `as` renders.
|
|
8762
|
-
* @returns {JSX.Element} The rendered link-styled button element.
|
|
8763
|
-
*
|
|
8764
|
-
* @example
|
|
8765
|
-
* // Text variant (default)
|
|
8766
|
-
* <LinkButton onClick={handleClick}>Click me</LinkButton>
|
|
8767
|
-
*
|
|
8768
|
-
* @example
|
|
8769
|
-
* // Underline variant with color
|
|
8770
|
-
* <LinkButton variant="underline" color="primary">Learn more</LinkButton>
|
|
8771
|
-
*/
|
|
8772
|
-
const LinkButton = React.forwardRef(function LinkButton({ variant = 'text', color, className, ...props }, ref) {
|
|
8773
|
-
const buttonColor = variant === 'underline' ? 'text' : variant;
|
|
8774
|
-
const prefixedClasses = usePrefixedClassNames('link-button', color && `link-button-${color}`, variant === 'underline' && 'link-button-underline');
|
|
8775
|
-
return (jsxRuntime.jsx(Button
|
|
8776
|
-
// LinkButton is polymorphic through to Button, which resolves the element
|
|
8777
|
-
// from `as`. TS cannot infer that through the spread below.
|
|
8778
|
-
, {
|
|
8779
|
-
// LinkButton is polymorphic through to Button, which resolves the element
|
|
8780
|
-
// from `as`. TS cannot infer that through the spread below.
|
|
8781
|
-
ref: ref, color: buttonColor, className: classNames(prefixedClasses, className), ...props }));
|
|
8782
|
-
});
|
|
8783
|
-
LinkButton.displayName = 'LinkButton';
|
|
8784
|
-
|
|
8785
|
-
/**
|
|
8786
|
-
* The `ToggleButton` component renders a button that stays pressed until it's pressed again, and says whether it's on through `aria-pressed`.
|
|
8787
|
-
*
|
|
8788
|
-
* It's always a `<button type="button">`, so it never submits a form and
|
|
8789
|
-
* carries no form value: a setting saved with a form is a `Checkbox` or a
|
|
8790
|
-
* `Switch`. A set of toggles that each turn on and off on their own goes in
|
|
8791
|
-
* `Buttons hasAddons` with `role="group"` and an `aria-label`. A set where only
|
|
8792
|
-
* one can be on is a radio group rather than a row of these: use
|
|
8793
|
-
* `SegmentedControl`, or `Radios` for a choice that's sent with a form.
|
|
8794
|
-
*
|
|
8795
|
-
* @function
|
|
8796
|
-
* @param {ToggleButtonProps} props - Props for the ToggleButton component.
|
|
8797
|
-
* @param {React.Ref<HTMLButtonElement>} ref - Forwarded ref to the `<button>`.
|
|
8798
|
-
* @returns {JSX.Element} The rendered toggle button.
|
|
8799
|
-
* @see {@link https://www.w3.org/WAI/ARIA/apg/patterns/button/ | APG button pattern}
|
|
8800
|
-
*
|
|
8801
|
-
* @example
|
|
8802
|
-
* <ToggleButton onPressedChange={setMuted}>Mute</ToggleButton>
|
|
8803
|
-
*
|
|
8804
|
-
* @example
|
|
8805
|
-
* <Buttons hasAddons role="group" aria-label="Text style">
|
|
8806
|
-
* <ToggleButton aria-label="Bold" pressed={bold} onPressedChange={setBold}>
|
|
8807
|
-
* <Icon name="bold" />
|
|
8808
|
-
* </ToggleButton>
|
|
8809
|
-
* </Buttons>
|
|
8810
|
-
*/
|
|
8811
|
-
const ToggleButton = React.forwardRef(function ToggleButton({ pressed: controlledPressed, defaultPressed = false, onPressedChange, color = 'link', isLoading, 'aria-busy': ariaBusy, 'aria-disabled': ariaDisabled, className, onClick, ...props }, ref) {
|
|
8812
|
-
const isControlled = controlledPressed !== undefined;
|
|
8813
|
-
const [internalPressed, setInternalPressed] = React.useState(defaultPressed);
|
|
8814
|
-
const pressed = controlledPressed ?? internalPressed;
|
|
8815
|
-
// Prefixed like Button's own classes, which a hand-written
|
|
8816
|
-
// `className="is-selected"` could not be under ConfigProvider classPrefix.
|
|
8817
|
-
// Bulma lifts `is-selected` above its neighbours in `.buttons.has-addons`.
|
|
8818
|
-
const selectedClass = usePrefixedClassNames({ 'is-selected': pressed });
|
|
8819
|
-
// The caller's onClick runs first, so a `preventDefault()` there vetoes
|
|
8820
|
-
// the toggle, as it does in Radix, MUI and Zag. Enter and Space reach here
|
|
8821
|
-
// through the button's own click, and a disabled button gets no click.
|
|
8822
|
-
//
|
|
8823
|
-
// A loading one gets the click but drops it. Bulma's spinner only stops a
|
|
8824
|
-
// pointer, so without this a key flipped a toggle the mouse couldn't.
|
|
8825
|
-
// `disabled` would stop both, but it takes focus away mid-save.
|
|
8826
|
-
const handleClick = (event) => {
|
|
8827
|
-
if (isLoading)
|
|
8828
|
-
return;
|
|
8829
|
-
onClick?.(event);
|
|
8830
|
-
if (event.defaultPrevented)
|
|
8831
|
-
return;
|
|
8832
|
-
const next = !pressed;
|
|
8833
|
-
onPressedChange?.(next);
|
|
8834
|
-
if (!isControlled)
|
|
8835
|
-
setInternalPressed(next);
|
|
8836
|
-
};
|
|
8837
|
-
// The props are spread first, so what a toggle depends on wins over
|
|
8838
|
-
// anything a JavaScript caller or a spread object passes along: it stays
|
|
8839
|
-
// a button, never submits, reports the state it's in, and says it's busy
|
|
8840
|
-
// and won't respond while it's loading. A caller's own `aria-busy` or
|
|
8841
|
-
// `aria-disabled` stays when it isn't loading.
|
|
8842
|
-
return (jsxRuntime.jsx(Button, { ...props, ref: ref, as: "button", type: "button", isLoading: isLoading, "aria-busy": isLoading || ariaBusy, "aria-disabled": isLoading || ariaDisabled, "aria-pressed": pressed, color: pressed ? color : undefined, className: classNames(selectedClass, className), onClick: handleClick }));
|
|
8843
|
-
});
|
|
8844
|
-
ToggleButton.displayName = 'ToggleButton';
|
|
8845
|
-
|
|
8846
|
-
const validButtonsSizes = ['small', 'medium', 'large'];
|
|
8847
|
-
/**
|
|
8848
|
-
* The `Buttons` component lets you group multiple `Button` elements together with Bulma's spacing, alignment, and add-on features.
|
|
8849
|
-
*
|
|
8850
|
-
* @function
|
|
8851
|
-
* @param {ButtonsProps} props - Props for the Buttons component.
|
|
8852
|
-
* @returns {JSX.Element} The rendered group of buttons.
|
|
8853
|
-
* @see {@link https://bulma.io/documentation/elements/button/#group | Bulma Button Group documentation}
|
|
8854
|
-
*/
|
|
8855
|
-
const ButtonsComponent = ({ className, textColor, color, bgColor, isCentered, isRight, hasAddons, size, children, ...props }) => {
|
|
8856
|
-
const buttonsClasses = usePrefixedClassNames('buttons', {
|
|
8857
|
-
'is-centered': isCentered,
|
|
8858
|
-
'is-right': isRight,
|
|
8859
|
-
'has-addons': hasAddons,
|
|
8860
|
-
[`are-${size}`]: size && validButtonsSizes.includes(size),
|
|
8861
|
-
});
|
|
8862
|
-
/**
|
|
8863
|
-
* Generates Bulma helper classes and separates out remaining props.
|
|
8864
|
-
*/
|
|
8865
|
-
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
8866
|
-
color: textColor ?? color,
|
|
8867
|
-
backgroundColor: bgColor,
|
|
8868
|
-
...props,
|
|
8869
|
-
});
|
|
8870
|
-
const combinedClasses = classNames(buttonsClasses, className, bulmaHelperClasses);
|
|
8871
|
-
return (jsxRuntime.jsx("div", { className: combinedClasses, ...rest, children: children }));
|
|
8872
|
-
};
|
|
8873
|
-
const Buttons = withSubComponents(ButtonsComponent, {
|
|
8874
|
-
Button,
|
|
8875
|
-
LinkButton,
|
|
8876
|
-
ToggleButton,
|
|
8877
|
-
}, 'Buttons');
|
|
8878
|
-
|
|
8879
9063
|
/**
|
|
8880
9064
|
* The `Code` component renders a styled `<code>` element with Bulma helper class integration.
|
|
8881
9065
|
*
|
|
@@ -9904,27 +10088,6 @@ const OrderedListComponent = ({ className, textColor, bgColor, children, ...prop
|
|
|
9904
10088
|
};
|
|
9905
10089
|
const OrderedList = withSubComponents(OrderedListComponent, { Item: ListItem }, 'OrderedList');
|
|
9906
10090
|
|
|
9907
|
-
/**
|
|
9908
|
-
* The `Paragraph` component renders a styled `<p>` element with Bulma helper class integration.
|
|
9909
|
-
*
|
|
9910
|
-
* @function
|
|
9911
|
-
* @param {ParagraphProps} props - Props for the Paragraph component.
|
|
9912
|
-
* @returns {JSX.Element} The rendered paragraph element.
|
|
9913
|
-
*/
|
|
9914
|
-
const Paragraph = ({ className, textColor, bgColor, children, ...props }) => {
|
|
9915
|
-
/**
|
|
9916
|
-
* Generates Bulma helper classes and separates out remaining props.
|
|
9917
|
-
*/
|
|
9918
|
-
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
9919
|
-
color: textColor,
|
|
9920
|
-
backgroundColor: bgColor,
|
|
9921
|
-
...props,
|
|
9922
|
-
});
|
|
9923
|
-
const bulmaClasses = usePrefixedClassNames();
|
|
9924
|
-
const paragraphClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
|
|
9925
|
-
return (jsxRuntime.jsx("p", { className: paragraphClasses || undefined, ...rest, children: children }));
|
|
9926
|
-
};
|
|
9927
|
-
|
|
9928
10091
|
/**
|
|
9929
10092
|
* The `Pre` component renders a styled `<pre>` element with Bulma helper class integration.
|
|
9930
10093
|
*
|
|
@@ -10337,43 +10500,6 @@ isMultiline, size, children, ...props }) => {
|
|
|
10337
10500
|
};
|
|
10338
10501
|
const Tags = withSubComponents(TagsComponent, { Tag }, 'Tags');
|
|
10339
10502
|
|
|
10340
|
-
const validTitleSizes = ['1', '2', '3', '4', '5', '6'];
|
|
10341
|
-
const validTitleElements = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p'];
|
|
10342
|
-
/**
|
|
10343
|
-
* The `Title` component renders a Bulma-styled title (heading), supporting sizes `1`-`6`, spacing, and rendering as any heading or paragraph element (`h1`-`h6`, `p`).
|
|
10344
|
-
*
|
|
10345
|
-
* @function
|
|
10346
|
-
* @param {TitleProps} props - Props for the Title component.
|
|
10347
|
-
* @returns {JSX.Element} The rendered title element.
|
|
10348
|
-
* @see {@link https://bulma.io/documentation/elements/title/ | Bulma Title documentation}
|
|
10349
|
-
*/
|
|
10350
|
-
const Title = ({ className, size, isSpaced, as = 'h1', hasSkeleton, textColor, bgColor, children, ...props }) => {
|
|
10351
|
-
/**
|
|
10352
|
-
* Generates Bulma helper classes and separates out remaining props.
|
|
10353
|
-
*/
|
|
10354
|
-
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
10355
|
-
color: textColor,
|
|
10356
|
-
backgroundColor: bgColor,
|
|
10357
|
-
...props,
|
|
10358
|
-
});
|
|
10359
|
-
// Validate 'as' prop at runtime
|
|
10360
|
-
const element = validTitleElements.includes(as) ? as : 'h1';
|
|
10361
|
-
// Validate 'size' prop at runtime (numeric sizes normalize to strings)
|
|
10362
|
-
const normalized = size == null ? undefined : String(size);
|
|
10363
|
-
const validSize = normalized && validTitleSizes.includes(normalized)
|
|
10364
|
-
? normalized
|
|
10365
|
-
: undefined;
|
|
10366
|
-
const bulmaClasses = usePrefixedClassNames('title', {
|
|
10367
|
-
[`is-${validSize}`]: validSize,
|
|
10368
|
-
'is-spaced': isSpaced,
|
|
10369
|
-
'has-skeleton': hasSkeleton,
|
|
10370
|
-
});
|
|
10371
|
-
const titleClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
|
|
10372
|
-
// Determine the tag based on 'element' and 'validSize'
|
|
10373
|
-
const Tag = element === 'p' ? 'p' : validSize ? `h${validSize}` : element;
|
|
10374
|
-
return (jsxRuntime.jsx(Tag, { className: titleClasses, ...rest, children: children }));
|
|
10375
|
-
};
|
|
10376
|
-
|
|
10377
10503
|
/**
|
|
10378
10504
|
* The `UnorderedList` component renders a styled unordered list (`<ul>`) element with Bulma helper class integration.
|
|
10379
10505
|
*
|
|
@@ -19463,6 +19589,11 @@ exports.Dropdown = Dropdown;
|
|
|
19463
19589
|
exports.DropdownDivider = DropdownDivider;
|
|
19464
19590
|
exports.DropdownItem = DropdownItem;
|
|
19465
19591
|
exports.Emphasis = Emphasis;
|
|
19592
|
+
exports.EmptyState = EmptyState;
|
|
19593
|
+
exports.EmptyStateActions = EmptyStateActions;
|
|
19594
|
+
exports.EmptyStateDescription = EmptyStateDescription;
|
|
19595
|
+
exports.EmptyStateIndicator = EmptyStateIndicator;
|
|
19596
|
+
exports.EmptyStateTitle = EmptyStateTitle;
|
|
19466
19597
|
exports.Field = Field;
|
|
19467
19598
|
exports.FieldBody = FieldBody;
|
|
19468
19599
|
exports.FieldLabel = FieldLabel;
|