@allxsmith/bestax-bulma 5.36.0 → 5.37.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 +2 -2
- package/dist/esm/components/Menu.js +9 -2
- package/dist/esm/components/Menu.js.map +1 -1
- package/dist/esm/components/Timeline.js +5 -5
- package/dist/esm/components/Timeline.js.map +1 -1
- package/dist/esm/components/TreeView.js +192 -0
- package/dist/esm/components/TreeView.js.map +1 -0
- package/dist/esm/helpers/typeahead.js +82 -0
- package/dist/esm/helpers/typeahead.js.map +1 -0
- package/dist/esm/helpers/useMenuKeys.js +8 -58
- package/dist/esm/helpers/useMenuKeys.js.map +1 -1
- package/dist/esm/helpers/useTreeView.js +380 -0
- package/dist/esm/helpers/useTreeView.js.map +1 -0
- package/dist/extras.css +1 -1
- package/dist/extras.css.map +1 -1
- package/dist/index.cjs +1954 -1370
- 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/Menu.d.ts +5 -0
- package/dist/types/components/TreeView.d.ts +115 -0
- package/dist/types/helpers/typeahead.d.ts +41 -0
- package/dist/types/helpers/useMenuKeys.d.ts +1 -2
- package/dist/types/helpers/useTreeView.d.ts +88 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types-cjs/components/Menu.d.ts +5 -0
- package/dist/types-cjs/components/TreeView.d.ts +115 -0
- package/dist/types-cjs/helpers/typeahead.d.ts +41 -0
- package/dist/types-cjs/helpers/useMenuKeys.d.ts +1 -2
- package/dist/types-cjs/helpers/useTreeView.d.ts +88 -0
- 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/_index.scss +1 -0
- package/src/scss/components/_treeview.scss +252 -0
package/dist/index.cjs
CHANGED
|
@@ -2365,41 +2365,13 @@ function getDeepestActiveElement(doc = document) {
|
|
|
2365
2365
|
}
|
|
2366
2366
|
|
|
2367
2367
|
// INTERNAL: deliberately not exported from src/index.ts.
|
|
2368
|
-
/**
|
|
2369
|
-
* Every menu-item role, so an item a caller gives `menuitemcheckbox` or
|
|
2370
|
-
* `menuitemradio` (a filter or sort menu) stays reachable.
|
|
2371
|
-
*/
|
|
2372
|
-
const MENU_ITEM_SELECTOR = '[role="menuitem"], [role="menuitemcheckbox"], [role="menuitemradio"]';
|
|
2373
|
-
/**
|
|
2374
|
-
* Whether a menu item can be reached and activated: it isn't disabled, as a
|
|
2375
|
-
* native control or through `aria-disabled="true"`.
|
|
2376
|
-
*/
|
|
2377
|
-
function isEnabledMenuItem(item) {
|
|
2378
|
-
return (!item.hasAttribute('disabled') &&
|
|
2379
|
-
item.getAttribute('aria-disabled') !== 'true');
|
|
2380
|
-
}
|
|
2381
|
-
/** Whether `node` is one of `menu`'s items, enabled or not. */
|
|
2382
|
-
const isItemOf = (menu, node) => Boolean(node && menu.contains(node) && node.matches(MENU_ITEM_SELECTOR));
|
|
2383
|
-
/** The items in `menu` the keys reach, in document order. */
|
|
2384
|
-
function enabledMenuItems(menu) {
|
|
2385
|
-
return Array.from(menu.querySelectorAll(MENU_ITEM_SELECTOR)).filter(isEnabledMenuItem);
|
|
2386
|
-
}
|
|
2387
2368
|
/** How long typed characters keep adding up to one search, in milliseconds. */
|
|
2388
2369
|
const TYPEAHEAD_TIMEOUT = 500;
|
|
2389
2370
|
/**
|
|
2390
|
-
*
|
|
2391
|
-
*
|
|
2392
|
-
*/
|
|
2393
|
-
function activatesItself(item, key) {
|
|
2394
|
-
if (item.tagName === 'BUTTON')
|
|
2395
|
-
return true;
|
|
2396
|
-
return key === 'Enter' && item.tagName === 'A' && item.hasAttribute('href');
|
|
2397
|
-
}
|
|
2398
|
-
/**
|
|
2399
|
-
* The text typing finds an item by: what it says, without what's hidden from
|
|
2400
|
-
* assistive technology, such as an icon whose glyph is a ligature's name.
|
|
2371
|
+
* The text typing finds an element by: what it says, without what's hidden
|
|
2372
|
+
* from assistive technology, such as an icon whose glyph is a ligature's name.
|
|
2401
2373
|
*/
|
|
2402
|
-
function
|
|
2374
|
+
function typeaheadText(element) {
|
|
2403
2375
|
let text = '';
|
|
2404
2376
|
const read = (node) => {
|
|
2405
2377
|
node.childNodes.forEach(child => {
|
|
@@ -2411,7 +2383,7 @@ function label(item) {
|
|
|
2411
2383
|
}
|
|
2412
2384
|
});
|
|
2413
2385
|
};
|
|
2414
|
-
read(
|
|
2386
|
+
read(element);
|
|
2415
2387
|
return text.trim().toLowerCase();
|
|
2416
2388
|
}
|
|
2417
2389
|
/**
|
|
@@ -2420,18 +2392,84 @@ function label(item) {
|
|
|
2420
2392
|
* through the items that start with it, and so does a run of one letter
|
|
2421
2393
|
* ("dd"). A longer search looks from the focused item, so typing more of
|
|
2422
2394
|
* its own text keeps it.
|
|
2395
|
+
*
|
|
2396
|
+
* @param items - The items typing can reach, in the order they're shown.
|
|
2397
|
+
* @param search - What's been typed, lowercased.
|
|
2398
|
+
* @param current - The focused item's index in `items`, or -1 for none.
|
|
2399
|
+
* @param textOf - The text an item is found by. By default it's the item's
|
|
2400
|
+
* own, read by `typeaheadText`.
|
|
2423
2401
|
*/
|
|
2424
|
-
function typeaheadMatch(items, search, current) {
|
|
2402
|
+
function typeaheadMatch(items, search, current, textOf = typeaheadText) {
|
|
2425
2403
|
const repeated = Array.from(search).every(char => char === search[0]);
|
|
2426
2404
|
const wanted = repeated ? search[0] : search;
|
|
2427
2405
|
const start = current < 0 ? 0 : current + (wanted.length === 1 ? 1 : 0);
|
|
2428
2406
|
for (let i = 0; i < items.length; i += 1) {
|
|
2429
2407
|
const item = items[(start + i) % items.length];
|
|
2430
|
-
if (
|
|
2408
|
+
if (textOf(item).startsWith(wanted))
|
|
2431
2409
|
return item;
|
|
2432
2410
|
}
|
|
2433
2411
|
return undefined;
|
|
2434
2412
|
}
|
|
2413
|
+
/**
|
|
2414
|
+
* Keeps what has been typed for a type-ahead search, and forgets it once
|
|
2415
|
+
* `TYPEAHEAD_TIMEOUT` passes with nothing typed. Pair it with
|
|
2416
|
+
* `typeaheadMatch` to find the item the search names.
|
|
2417
|
+
*
|
|
2418
|
+
* @function useTypeahead
|
|
2419
|
+
* @returns A way to add a character to the search, and a way to forget it.
|
|
2420
|
+
*/
|
|
2421
|
+
function useTypeahead() {
|
|
2422
|
+
const typedRef = React.useRef({ text: '' });
|
|
2423
|
+
React.useEffect(() => {
|
|
2424
|
+
const typed = typedRef.current;
|
|
2425
|
+
return () => clearTimeout(typed.timer);
|
|
2426
|
+
}, []);
|
|
2427
|
+
return React.useMemo(() => {
|
|
2428
|
+
const reset = () => {
|
|
2429
|
+
const typed = typedRef.current;
|
|
2430
|
+
clearTimeout(typed.timer);
|
|
2431
|
+
typed.text = '';
|
|
2432
|
+
};
|
|
2433
|
+
const type = (char) => {
|
|
2434
|
+
const typed = typedRef.current;
|
|
2435
|
+
typed.text += char.toLowerCase();
|
|
2436
|
+
clearTimeout(typed.timer);
|
|
2437
|
+
typed.timer = setTimeout(reset, TYPEAHEAD_TIMEOUT);
|
|
2438
|
+
return typed.text;
|
|
2439
|
+
};
|
|
2440
|
+
return { type, reset };
|
|
2441
|
+
}, []);
|
|
2442
|
+
}
|
|
2443
|
+
|
|
2444
|
+
// INTERNAL: deliberately not exported from src/index.ts.
|
|
2445
|
+
/**
|
|
2446
|
+
* Every menu-item role, so an item a caller gives `menuitemcheckbox` or
|
|
2447
|
+
* `menuitemradio` (a filter or sort menu) stays reachable.
|
|
2448
|
+
*/
|
|
2449
|
+
const MENU_ITEM_SELECTOR = '[role="menuitem"], [role="menuitemcheckbox"], [role="menuitemradio"]';
|
|
2450
|
+
/**
|
|
2451
|
+
* Whether a menu item can be reached and activated: it isn't disabled, as a
|
|
2452
|
+
* native control or through `aria-disabled="true"`.
|
|
2453
|
+
*/
|
|
2454
|
+
function isEnabledMenuItem(item) {
|
|
2455
|
+
return (!item.hasAttribute('disabled') &&
|
|
2456
|
+
item.getAttribute('aria-disabled') !== 'true');
|
|
2457
|
+
}
|
|
2458
|
+
/** Whether `node` is one of `menu`'s items, enabled or not. */
|
|
2459
|
+
const isItemOf = (menu, node) => Boolean(node && menu.contains(node) && node.matches(MENU_ITEM_SELECTOR));
|
|
2460
|
+
/** The items in `menu` the keys reach, in document order. */
|
|
2461
|
+
function enabledMenuItems(menu) {
|
|
2462
|
+
return Array.from(menu.querySelectorAll(MENU_ITEM_SELECTOR)).filter(isEnabledMenuItem);
|
|
2463
|
+
}
|
|
2464
|
+
/**
|
|
2465
|
+
* Whether the browser activates `item` on this key by itself: a `<button>`
|
|
2466
|
+
* answers Enter and Space, and a link with an `href` answers Enter.
|
|
2467
|
+
*/
|
|
2468
|
+
function activatesItself(item, key) {
|
|
2469
|
+
if (item.tagName === 'BUTTON')
|
|
2470
|
+
return true;
|
|
2471
|
+
return key === 'Enter' && item.tagName === 'A' && item.hasAttribute('href');
|
|
2472
|
+
}
|
|
2435
2473
|
/**
|
|
2436
2474
|
* Moves through and activates the items of a `role="menu"` element from the
|
|
2437
2475
|
* keyboard. Put the `onKeyDown` it returns on that element. It reads the
|
|
@@ -2463,17 +2501,8 @@ function typeaheadMatch(items, search, current) {
|
|
|
2463
2501
|
* what has been typed.
|
|
2464
2502
|
*/
|
|
2465
2503
|
function useMenuKeys() {
|
|
2466
|
-
const
|
|
2467
|
-
React.useEffect(() => {
|
|
2468
|
-
const typed = typedRef.current;
|
|
2469
|
-
return () => clearTimeout(typed.timer);
|
|
2470
|
-
}, []);
|
|
2504
|
+
const typeahead = useTypeahead();
|
|
2471
2505
|
return React.useMemo(() => {
|
|
2472
|
-
const reset = () => {
|
|
2473
|
-
const typed = typedRef.current;
|
|
2474
|
-
clearTimeout(typed.timer);
|
|
2475
|
-
typed.text = '';
|
|
2476
|
-
};
|
|
2477
2506
|
const onKeyDown = (event) => {
|
|
2478
2507
|
const menu = event.currentTarget;
|
|
2479
2508
|
const focused = getActiveElementInTree(menu);
|
|
@@ -2530,14 +2559,10 @@ function useMenuKeys() {
|
|
|
2530
2559
|
return;
|
|
2531
2560
|
}
|
|
2532
2561
|
event.preventDefault();
|
|
2533
|
-
|
|
2534
|
-
typed.text += event.key.toLowerCase();
|
|
2535
|
-
clearTimeout(typed.timer);
|
|
2536
|
-
typed.timer = setTimeout(reset, TYPEAHEAD_TIMEOUT);
|
|
2537
|
-
typeaheadMatch(items, typed.text, current)?.focus();
|
|
2562
|
+
typeaheadMatch(items, typeahead.type(event.key), current)?.focus();
|
|
2538
2563
|
};
|
|
2539
|
-
return { onKeyDown, reset };
|
|
2540
|
-
}, []);
|
|
2564
|
+
return { onKeyDown, reset: typeahead.reset };
|
|
2565
|
+
}, [typeahead]);
|
|
2541
2566
|
}
|
|
2542
2567
|
|
|
2543
2568
|
/**
|
|
@@ -3473,7 +3498,7 @@ const EmptyStateActions = ({ className, ...props }) => {
|
|
|
3473
3498
|
};
|
|
3474
3499
|
EmptyStateActions.displayName = 'EmptyState.Actions';
|
|
3475
3500
|
/** Whether a shorthand prop was given something to render. */
|
|
3476
|
-
const isGiven$
|
|
3501
|
+
const isGiven$2 = (node) => node !== undefined &&
|
|
3477
3502
|
node !== null &&
|
|
3478
3503
|
typeof node !== 'boolean' &&
|
|
3479
3504
|
node !== '';
|
|
@@ -3520,7 +3545,7 @@ const EmptyStateRoot = React.forwardRef(({ className, icon, title, titleAs = 'h2
|
|
|
3520
3545
|
});
|
|
3521
3546
|
// The props' parts first, each only when it's given, then the children
|
|
3522
3547
|
// exactly as they were written: nothing here looks at them.
|
|
3523
|
-
return (jsxRuntime.jsxs("div", { ref: ref, className: classNames(rootClass, bulmaHelperClasses, className), ...rest, children: [isGiven$
|
|
3548
|
+
return (jsxRuntime.jsxs("div", { ref: ref, className: classNames(rootClass, bulmaHelperClasses, className), ...rest, children: [isGiven$2(icon) && (jsxRuntime.jsx(EmptyStateIndicator, { children: typeof icon === 'string' ? jsxRuntime.jsx(Icon, { name: icon }) : icon })), isGiven$2(title) && (jsxRuntime.jsx(EmptyStateTitle, { as: titleAs, children: title })), isGiven$2(description) && (jsxRuntime.jsx(EmptyStateDescription, { children: description })), children] }));
|
|
3524
3549
|
});
|
|
3525
3550
|
const EmptyState = withSubComponents(EmptyStateRoot, {
|
|
3526
3551
|
Indicator: EmptyStateIndicator,
|
|
@@ -3559,6 +3584,10 @@ const MenuLabel = ({ className, children, ...props }) => {
|
|
|
3559
3584
|
/**
|
|
3560
3585
|
* MenuList applies `menu-list` class only at the top level (not for nested lists).
|
|
3561
3586
|
*
|
|
3587
|
+
* Every level renders `role="list"`, which Safari needs: WebKit stops
|
|
3588
|
+
* exposing a `<ul>` as a list once its markers are hidden, and Bulma's reset
|
|
3589
|
+
* hides them.
|
|
3590
|
+
*
|
|
3562
3591
|
* @function
|
|
3563
3592
|
* @param {MenuListProps} props - Props for the MenuList component.
|
|
3564
3593
|
* @returns {JSX.Element} The rendered menu list.
|
|
@@ -3569,8 +3598,11 @@ const MenuList = ({ className, children, ...props }) => {
|
|
|
3569
3598
|
const ulClass = classNames(className, bulmaHelperClasses, {
|
|
3570
3599
|
[usePrefixedClassNames('menu-list')]: level === 0,
|
|
3571
3600
|
});
|
|
3572
|
-
// Increment level for nested MenuLists
|
|
3573
|
-
|
|
3601
|
+
// Increment level for nested MenuLists. role="list" restates the <ul>'s
|
|
3602
|
+
// own role, for Safari: WebKit stops exposing a list as one once its
|
|
3603
|
+
// markers are hidden, as Bulma's reset hides them, unless the role is set
|
|
3604
|
+
// explicitly. After the spread, so a caller's role can't take it away.
|
|
3605
|
+
return (jsxRuntime.jsx(MenuListLevelContext.Provider, { value: level + 1, children: jsxRuntime.jsx("ul", { className: ulClass || undefined, ...rest, role: "list", children: children }) }));
|
|
3574
3606
|
};
|
|
3575
3607
|
/**
|
|
3576
3608
|
* `href` alone — annotated so a typo is a compile error rather than a silent
|
|
@@ -8130,7 +8162,7 @@ const TimelineItemContext = React.createContext({
|
|
|
8130
8162
|
titleAs: 'h2',
|
|
8131
8163
|
});
|
|
8132
8164
|
/** Whether a prop was given something to render. */
|
|
8133
|
-
const isGiven = (node) => node !== undefined &&
|
|
8165
|
+
const isGiven$1 = (node) => node !== undefined &&
|
|
8134
8166
|
node !== null &&
|
|
8135
8167
|
typeof node !== 'boolean' &&
|
|
8136
8168
|
node !== '';
|
|
@@ -8163,7 +8195,7 @@ const TimelineItem = React.forwardRef(({ className, title, time, dateTime, icon,
|
|
|
8163
8195
|
[`is-${color}`]: isTimelineColor(color),
|
|
8164
8196
|
});
|
|
8165
8197
|
const markerClass = usePrefixedClassNames('timeline-marker', {
|
|
8166
|
-
'is-icon': isGiven(icon),
|
|
8198
|
+
'is-icon': isGiven$1(icon),
|
|
8167
8199
|
});
|
|
8168
8200
|
const contentClass = usePrefixedClassNames('timeline-content');
|
|
8169
8201
|
const titleClass = usePrefixedClassNames('timeline-title');
|
|
@@ -8175,8 +8207,8 @@ const TimelineItem = React.forwardRef(({ className, title, time, dateTime, icon,
|
|
|
8175
8207
|
const words = label ? (jsxRuntime.jsx("span", { style: visuallyHidden, children: ` ${label}` })) : null;
|
|
8176
8208
|
return (jsxRuntime.jsxs("li", { ref: ref, className: classNames(itemClass, bulmaHelperClasses, className), ...rest, ...(state && {
|
|
8177
8209
|
'aria-current': state === 'active' ? 'step' : undefined,
|
|
8178
|
-
}), children: [jsxRuntime.jsx("div", { className: markerClass, "aria-hidden": "true", children: isGiven(icon) &&
|
|
8179
|
-
(typeof icon === 'string' ? jsxRuntime.jsx(Icon, { name: icon }) : icon) }), jsxRuntime.jsxs("div", { className: contentClass, children: [isGiven(title) ? (jsxRuntime.jsxs(TitleTag, { className: titleClass, children: [title, words] })) : (words), isGiven(time) &&
|
|
8210
|
+
}), children: [jsxRuntime.jsx("div", { className: markerClass, "aria-hidden": "true", children: isGiven$1(icon) &&
|
|
8211
|
+
(typeof icon === 'string' ? jsxRuntime.jsx(Icon, { name: icon }) : icon) }), jsxRuntime.jsxs("div", { className: contentClass, children: [isGiven$1(title) ? (jsxRuntime.jsxs(TitleTag, { className: titleClass, children: [title, words] })) : (words), isGiven$1(time) &&
|
|
8180
8212
|
(dateTime ? (jsxRuntime.jsx("time", { className: timeClass, dateTime: dateTime, children: time })) : (jsxRuntime.jsx("span", { className: timeClass, children: time }))), children] })] }));
|
|
8181
8213
|
});
|
|
8182
8214
|
TimelineItem.displayName = 'Timeline.Item';
|
|
@@ -8250,7 +8282,7 @@ const TimelineRoot = React.forwardRef(({ className, children, active, pending, a
|
|
|
8250
8282
|
// Each child gets its state through context rather than a clone, so an
|
|
8251
8283
|
// app's own component around a Timeline.Item still gets it. Fragments
|
|
8252
8284
|
// and arrays are opened up first, so each item inside one counts.
|
|
8253
|
-
return (jsxRuntime.jsxs("ol", { ref: ref, className: classNames(rootClass, bulmaHelperClasses, className), ...rest, role: "list", children: [flattenChildren(children).map((child, index) => (jsxRuntime.jsx(TimelineItemContext.Provider, { value: contextFor(stateAt(index)), children: child }, React.isValidElement(child) ? child.key : index))), isGiven(pending) && (jsxRuntime.jsx(TimelineItemContext.Provider, { value: contextFor('pending'), children: jsxRuntime.jsx(TimelineItem, { title: pending }) }))] }));
|
|
8285
|
+
return (jsxRuntime.jsxs("ol", { ref: ref, className: classNames(rootClass, bulmaHelperClasses, className), ...rest, role: "list", children: [flattenChildren(children).map((child, index) => (jsxRuntime.jsx(TimelineItemContext.Provider, { value: contextFor(stateAt(index)), children: child }, React.isValidElement(child) ? child.key : index))), isGiven$1(pending) && (jsxRuntime.jsx(TimelineItemContext.Provider, { value: contextFor('pending'), children: jsxRuntime.jsx(TimelineItem, { title: pending }) }))] }));
|
|
8254
8286
|
});
|
|
8255
8287
|
const Timeline = withSubComponents(TimelineRoot, { Item: TimelineItem }, 'Timeline');
|
|
8256
8288
|
|
|
@@ -9545,922 +9577,1353 @@ function RevealImpl(revealProps) {
|
|
|
9545
9577
|
const Reveal = RevealImpl;
|
|
9546
9578
|
Reveal.displayName = 'Reveal';
|
|
9547
9579
|
|
|
9580
|
+
// INTERNAL: deliberately not exported from src/index.ts.
|
|
9548
9581
|
/**
|
|
9549
|
-
*
|
|
9550
|
-
*
|
|
9551
|
-
*
|
|
9552
|
-
* @param {BlockProps} props - Props for the Block component.
|
|
9553
|
-
* @returns {JSX.Element} The rendered block element.
|
|
9554
|
-
* @see {@link https://bulma.io/documentation/elements/block/ | Bulma Block documentation}
|
|
9582
|
+
* What Enter, Space and a click on the row do in each mode, from the tables
|
|
9583
|
+
* in #1096. `open` opens or closes a parent and does nothing on a leaf. A
|
|
9584
|
+
* click on the chevron only ever opens or closes.
|
|
9555
9585
|
*/
|
|
9556
|
-
const
|
|
9557
|
-
|
|
9558
|
-
|
|
9559
|
-
|
|
9560
|
-
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
9561
|
-
color: textColor ?? color,
|
|
9562
|
-
backgroundColor: bgColor,
|
|
9563
|
-
...props,
|
|
9564
|
-
});
|
|
9565
|
-
const bulmaClasses = usePrefixedClassNames('block');
|
|
9566
|
-
const blockClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
|
|
9567
|
-
return (jsxRuntime.jsx("div", { className: blockClasses, ...rest, children: children }));
|
|
9586
|
+
const MODES = {
|
|
9587
|
+
none: { enter: ['open'], space: ['open'], click: ['open'] },
|
|
9588
|
+
single: { enter: ['select'], space: ['select'], click: ['select', 'open'] },
|
|
9589
|
+
checkbox: { enter: ['open'], space: ['check'], click: ['check'] },
|
|
9568
9590
|
};
|
|
9569
|
-
|
|
9591
|
+
/** Left and Right, swapped for a right-to-left page. */
|
|
9592
|
+
const MIRRORED = {
|
|
9593
|
+
ArrowLeft: 'ArrowRight',
|
|
9594
|
+
ArrowRight: 'ArrowLeft',
|
|
9595
|
+
};
|
|
9596
|
+
/** An IME is composing, so the key belongs to it (`keyCode` 229 is Safari's word for it). */
|
|
9597
|
+
const isComposing$2 = (event) => event.nativeEvent.isComposing || event.nativeEvent.keyCode === 229;
|
|
9598
|
+
/** The registry's items on the page, in document order. */
|
|
9599
|
+
const shownIn = (tree, { values }) => Array.from(tree.querySelectorAll('[role="treeitem"]')).filter(element => values.has(element));
|
|
9600
|
+
/** The item an item on the page sits in: its group's `<li>`, or none at the top. */
|
|
9601
|
+
function parentItemOf(element) {
|
|
9602
|
+
const group = element.parentElement;
|
|
9603
|
+
return group.getAttribute('role') === 'group' ? group.parentElement : null;
|
|
9604
|
+
}
|
|
9605
|
+
/** The values of the items an item on the page is in, from the top. */
|
|
9606
|
+
function pathOf(element, { values }) {
|
|
9607
|
+
const path = [];
|
|
9608
|
+
for (let at = parentItemOf(element); at; at = parentItemOf(at)) {
|
|
9609
|
+
path.unshift(values.get(at));
|
|
9610
|
+
}
|
|
9611
|
+
return path;
|
|
9612
|
+
}
|
|
9570
9613
|
/**
|
|
9571
|
-
*
|
|
9572
|
-
*
|
|
9573
|
-
*
|
|
9574
|
-
*
|
|
9575
|
-
*
|
|
9576
|
-
*
|
|
9577
|
-
|
|
9578
|
-
|
|
9579
|
-
|
|
9580
|
-
|
|
9581
|
-
|
|
9582
|
-
const
|
|
9583
|
-
|
|
9584
|
-
|
|
9585
|
-
|
|
9614
|
+
* Every known item's check state, by the rules in #1096:
|
|
9615
|
+
*
|
|
9616
|
+
* - a disabled item is checked only when its own value is listed;
|
|
9617
|
+
* - an enabled item with no enabled items anywhere under it is checked when
|
|
9618
|
+
* it, or any item above it, is listed;
|
|
9619
|
+
* - any other enabled item follows the items of that second kind in its
|
|
9620
|
+
* branch: checked when they all are, unchecked when none is, mixed between.
|
|
9621
|
+
*
|
|
9622
|
+
* Also returns the items of the second kind, which are what a toggle sets.
|
|
9623
|
+
*/
|
|
9624
|
+
function readChecks(records, listed) {
|
|
9625
|
+
const kids = new Map();
|
|
9626
|
+
records.forEach(({ parent }, value) => {
|
|
9627
|
+
kids.set(parent, [...(kids.get(parent) ?? []), value]);
|
|
9628
|
+
});
|
|
9629
|
+
const states = new Map();
|
|
9630
|
+
const ends = [];
|
|
9631
|
+
// How many enabled ends the branch holds, and how many are checked.
|
|
9632
|
+
const visit = (value, above) => {
|
|
9633
|
+
const marked = above || listed.has(value);
|
|
9634
|
+
let total = 0;
|
|
9635
|
+
let on = 0;
|
|
9636
|
+
for (const kid of kids.get(value) ?? []) {
|
|
9637
|
+
const [t, o] = visit(kid, marked);
|
|
9638
|
+
total += t;
|
|
9639
|
+
on += o;
|
|
9640
|
+
}
|
|
9641
|
+
if (records.get(value).disabled) {
|
|
9642
|
+
states.set(value, listed.has(value));
|
|
9643
|
+
return [total, on];
|
|
9644
|
+
}
|
|
9645
|
+
if (total === 0) {
|
|
9646
|
+
states.set(value, marked);
|
|
9647
|
+
ends.push(value);
|
|
9648
|
+
return [1, marked ? 1 : 0];
|
|
9649
|
+
}
|
|
9650
|
+
states.set(value, on === total ? true : on > 0 && 'mixed');
|
|
9651
|
+
return [total, on];
|
|
9652
|
+
};
|
|
9653
|
+
for (const top of kids.get(null) ?? [])
|
|
9654
|
+
visit(top, false);
|
|
9655
|
+
return { states, ends };
|
|
9656
|
+
}
|
|
9657
|
+
/**
|
|
9658
|
+
* The list a toggle of `value` leaves: it and every enabled item under it
|
|
9659
|
+
* take the opposite of its state (a mixed one checks), and the rest is read
|
|
9660
|
+
* back by the rules. That's every item that shows checked, parents
|
|
9661
|
+
* included, plus any listed value no item has.
|
|
9662
|
+
*/
|
|
9663
|
+
function toggledList(records, list, value) {
|
|
9664
|
+
const { states, ends } = readChecks(records, new Set(list));
|
|
9665
|
+
const to = states.get(value) !== true;
|
|
9666
|
+
// An end was reached from the top, so everything above it is known.
|
|
9667
|
+
const under = (end) => {
|
|
9668
|
+
for (let at = end; at !== null;) {
|
|
9669
|
+
if (at === value)
|
|
9670
|
+
return true;
|
|
9671
|
+
at = records.get(at).parent;
|
|
9672
|
+
}
|
|
9673
|
+
return false;
|
|
9674
|
+
};
|
|
9675
|
+
const seed = new Set(ends.filter(end => (under(end) ? to : states.get(end) === true)));
|
|
9676
|
+
records.forEach(({ disabled }, item) => {
|
|
9677
|
+
if (disabled && states.get(item) === true)
|
|
9678
|
+
seed.add(item);
|
|
9586
9679
|
});
|
|
9587
|
-
const
|
|
9588
|
-
|
|
9680
|
+
const after = readChecks(records, seed).states;
|
|
9681
|
+
return [
|
|
9682
|
+
...Array.from(after)
|
|
9683
|
+
.filter(([, state]) => state === true)
|
|
9684
|
+
.map(([item]) => item),
|
|
9685
|
+
...list.filter(item => !records.has(item)),
|
|
9686
|
+
];
|
|
9687
|
+
}
|
|
9688
|
+
/** A value the caller holds, or one this hook holds when the caller doesn't. */
|
|
9689
|
+
function useControlled(controlled, initial) {
|
|
9690
|
+
const [own, setOwn] = React.useState(initial);
|
|
9691
|
+
const isControlled = controlled !== undefined;
|
|
9692
|
+
const set = (next) => {
|
|
9693
|
+
if (!isControlled)
|
|
9694
|
+
setOwn(next);
|
|
9695
|
+
};
|
|
9696
|
+
return [isControlled ? controlled : own, set];
|
|
9697
|
+
}
|
|
9698
|
+
/**
|
|
9699
|
+
* The open state, the selection, the keys, the tab stop and the registry of
|
|
9700
|
+
* a TreeView (#1096). Focus moves for real, with a roving tabindex, rather
|
|
9701
|
+
* than through `aria-activedescendant`.
|
|
9702
|
+
*
|
|
9703
|
+
* Every item registers its value, its parent and whether it's disabled from
|
|
9704
|
+
* a layout effect. In a checkbox tree a closed item's children still render
|
|
9705
|
+
* and register while drawing nothing, so the registry knows every item and a
|
|
9706
|
+
* check reaches all of them. The keys read the items on the page, in
|
|
9707
|
+
* document order, each time they need them.
|
|
9708
|
+
*
|
|
9709
|
+
* @function useTreeView
|
|
9710
|
+
* @param options - The TreeView's props.
|
|
9711
|
+
* @returns The state and handlers the root and its items wire up.
|
|
9712
|
+
*/
|
|
9713
|
+
function useTreeView(options) {
|
|
9714
|
+
// An own key only, so a name every object inherits, like `toString`, is a
|
|
9715
|
+
// mode the tree doesn't know too.
|
|
9716
|
+
const mode = Object.prototype.hasOwnProperty.call(MODES, options.mode)
|
|
9717
|
+
? options.mode
|
|
9718
|
+
: 'none';
|
|
9719
|
+
const actions = MODES[mode];
|
|
9720
|
+
const [expanded, setExpanded] = useControlled(options.expanded, options.defaultExpanded);
|
|
9721
|
+
const [selection, setSelection] = useControlled(options.selected, options.defaultSelected);
|
|
9722
|
+
const [checkedList, setChecked] = useControlled(options.checked, options.defaultChecked);
|
|
9723
|
+
// Each is read in its own mode only.
|
|
9724
|
+
const selected = mode === 'single' ? selection : null;
|
|
9725
|
+
const listed = new Set(mode === 'checkbox' ? checkedList : []);
|
|
9726
|
+
const treeRef = React.useRef(null);
|
|
9727
|
+
const registryRef = React.useRef({
|
|
9728
|
+
records: new Map(),
|
|
9729
|
+
elements: new Map(),
|
|
9730
|
+
values: new WeakMap(),
|
|
9731
|
+
});
|
|
9732
|
+
// Items register from their own effects, so they ask for a render to be read.
|
|
9733
|
+
const [, refresh] = React.useReducer((n) => n + 1, 0);
|
|
9734
|
+
const typeahead = useTypeahead();
|
|
9735
|
+
// The item with focus, and where the stop rests while focus is elsewhere:
|
|
9736
|
+
// `undefined` until the tree has mounted. With the values above it, for
|
|
9737
|
+
// when it goes away.
|
|
9738
|
+
const [focused, setFocused] = React.useState(null);
|
|
9739
|
+
const [resting, setResting] = React.useState();
|
|
9740
|
+
const restingPathRef = React.useRef([]);
|
|
9741
|
+
// The values above an item that left the page with focus in it.
|
|
9742
|
+
const lostRef = React.useRef(null);
|
|
9743
|
+
const { records } = registryRef.current;
|
|
9744
|
+
const checks = mode === 'checkbox'
|
|
9745
|
+
? // eslint-disable-next-line react-hooks/refs -- read in render on purpose: items write the registry from their layout effects and then ask for this render with `refresh`
|
|
9746
|
+
readChecks(records, listed).states
|
|
9747
|
+
: undefined;
|
|
9748
|
+
const tabStop = focused ?? (resting === undefined ? selected : resting);
|
|
9749
|
+
const isOpen = (value) => expanded.includes(value);
|
|
9750
|
+
const stateOf = (value, path, disabled) => {
|
|
9751
|
+
if (mode === 'single') {
|
|
9752
|
+
return disabled ? {} : { selected: value === selected };
|
|
9753
|
+
}
|
|
9754
|
+
if (checks) {
|
|
9755
|
+
// Until the items register, a value stands for its branch, and a
|
|
9756
|
+
// parent can't know it's mixed.
|
|
9757
|
+
const fromList = listed.has(value) || (!disabled && path.some(v => listed.has(v)));
|
|
9758
|
+
return { checked: checks.get(value) ?? fromList };
|
|
9759
|
+
}
|
|
9760
|
+
return {};
|
|
9761
|
+
};
|
|
9762
|
+
const changeOpen = (next) => {
|
|
9763
|
+
setExpanded(next);
|
|
9764
|
+
options.onExpandedChange?.(next);
|
|
9765
|
+
};
|
|
9766
|
+
const run = {
|
|
9767
|
+
open: ({ value, isParent, open }) => {
|
|
9768
|
+
if (isParent) {
|
|
9769
|
+
changeOpen(open ? expanded.filter(v => v !== value) : [...expanded, value]);
|
|
9770
|
+
}
|
|
9771
|
+
},
|
|
9772
|
+
select: ({ value }) => {
|
|
9773
|
+
if (value === selected)
|
|
9774
|
+
return;
|
|
9775
|
+
setSelection(value);
|
|
9776
|
+
options.onSelectedChange?.(value);
|
|
9777
|
+
},
|
|
9778
|
+
check: ({ value }) => {
|
|
9779
|
+
const next = toggledList(records, checkedList, value);
|
|
9780
|
+
setChecked(next);
|
|
9781
|
+
options.onCheckedChange?.(next);
|
|
9782
|
+
},
|
|
9783
|
+
};
|
|
9784
|
+
/** Disabled items are never selected, checked, opened or closed. */
|
|
9785
|
+
const act = (item, list) => {
|
|
9786
|
+
if (!item.disabled)
|
|
9787
|
+
list.forEach(action => run[action](item));
|
|
9788
|
+
};
|
|
9789
|
+
// After every render, put the stop where it belongs. While focus is in
|
|
9790
|
+
// the tree it's the focused item. Otherwise it's the selected item, or the
|
|
9791
|
+
// first checked one on the page, and failing that it stays where it was.
|
|
9792
|
+
// An item that goes away hands it to its nearest ancestor on the page, or
|
|
9793
|
+
// to the first item, and focus goes with it if it had focus.
|
|
9794
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps -- after every render on purpose: it reads the page, and sets state only when what it reads has changed
|
|
9795
|
+
useClientLayoutEffect(() => {
|
|
9796
|
+
const tree = treeRef.current;
|
|
9797
|
+
const registry = registryRef.current;
|
|
9798
|
+
// Each item's element is read off the page with its value, rather than
|
|
9799
|
+
// out of `elements`, which holds one element per value: two items
|
|
9800
|
+
// sharing a value, which they shouldn't, can leave it without one the
|
|
9801
|
+
// page still has.
|
|
9802
|
+
const shown = shownIn(tree, registry);
|
|
9803
|
+
const onPage = shown.map(element => registry.values.get(element));
|
|
9804
|
+
const elementOf = (value) => shown[onPage.indexOf(value)];
|
|
9805
|
+
const isShown = (value) => value != null && onPage.includes(value);
|
|
9806
|
+
const heir = (path) => [...path].reverse().find(isShown) ?? onPage[0] ?? null;
|
|
9807
|
+
const lost = lostRef.current;
|
|
9808
|
+
lostRef.current = null;
|
|
9809
|
+
if (lost && !tree.contains(getActiveElementInTree(tree))) {
|
|
9810
|
+
const to = heir(lost);
|
|
9811
|
+
if (to)
|
|
9812
|
+
elementOf(to).focus();
|
|
9813
|
+
return;
|
|
9814
|
+
}
|
|
9815
|
+
if (focused !== null && !isShown(focused))
|
|
9816
|
+
setFocused(null);
|
|
9817
|
+
const preferred = onPage.find(value => value === selected || checks?.get(value) === true);
|
|
9818
|
+
const next = preferred ?? (isShown(resting) ? resting : heir(restingPathRef.current));
|
|
9819
|
+
if (next !== resting) {
|
|
9820
|
+
setResting(next);
|
|
9821
|
+
restingPathRef.current = next ? pathOf(elementOf(next), registry) : [];
|
|
9822
|
+
}
|
|
9589
9823
|
});
|
|
9590
|
-
const
|
|
9591
|
-
|
|
9592
|
-
|
|
9824
|
+
const register = React.useCallback((value, record) => {
|
|
9825
|
+
const { records: known } = registryRef.current;
|
|
9826
|
+
known.set(value, record);
|
|
9827
|
+
refresh();
|
|
9828
|
+
return () => {
|
|
9829
|
+
if (known.get(value) !== record)
|
|
9830
|
+
return;
|
|
9831
|
+
known.delete(value);
|
|
9832
|
+
refresh();
|
|
9833
|
+
};
|
|
9834
|
+
}, []);
|
|
9835
|
+
const setElement = React.useCallback((value, element) => {
|
|
9836
|
+
const registry = registryRef.current;
|
|
9837
|
+
if (element) {
|
|
9838
|
+
registry.elements.set(value, element);
|
|
9839
|
+
registry.values.set(element, value);
|
|
9840
|
+
return;
|
|
9841
|
+
}
|
|
9842
|
+
const gone = registry.elements.get(value);
|
|
9843
|
+
if (!gone)
|
|
9844
|
+
return;
|
|
9845
|
+
// The ref lets go before the element leaves the page, so it can still
|
|
9846
|
+
// say whether focus was in it, and where it sat.
|
|
9847
|
+
if (!lostRef.current && gone.contains(getActiveElementInTree(gone))) {
|
|
9848
|
+
lostRef.current = [...pathOf(gone, registry), value];
|
|
9849
|
+
}
|
|
9850
|
+
registry.elements.delete(value);
|
|
9851
|
+
}, []);
|
|
9852
|
+
const onFocus = (value, element) => {
|
|
9853
|
+
setFocused(value);
|
|
9854
|
+
setResting(value);
|
|
9855
|
+
restingPathRef.current = pathOf(element, registryRef.current);
|
|
9856
|
+
};
|
|
9857
|
+
const onBlur = () => setFocused(null);
|
|
9858
|
+
const onKeyDown = (event, item) => {
|
|
9859
|
+
const element = event.currentTarget;
|
|
9860
|
+
// A key on an item inside this one bubbles here too, and is its own.
|
|
9861
|
+
if (event.target !== element ||
|
|
9862
|
+
event.defaultPrevented ||
|
|
9863
|
+
event.altKey ||
|
|
9864
|
+
event.ctrlKey ||
|
|
9865
|
+
event.metaKey ||
|
|
9866
|
+
isComposing$2(event)) {
|
|
9867
|
+
return;
|
|
9868
|
+
}
|
|
9869
|
+
const registry = registryRef.current;
|
|
9870
|
+
const items = shownIn(treeRef.current, registry);
|
|
9871
|
+
const at = items.indexOf(element);
|
|
9872
|
+
const rtl = getComputedStyle(element).direction === 'rtl';
|
|
9873
|
+
const key = (rtl && MIRRORED[event.key]) || event.key;
|
|
9874
|
+
switch (key) {
|
|
9875
|
+
case 'ArrowDown':
|
|
9876
|
+
items[at + 1]?.focus();
|
|
9877
|
+
break;
|
|
9878
|
+
case 'ArrowUp':
|
|
9879
|
+
items[at - 1]?.focus();
|
|
9880
|
+
break;
|
|
9881
|
+
case 'Home':
|
|
9882
|
+
items[0].focus();
|
|
9883
|
+
break;
|
|
9884
|
+
case 'End':
|
|
9885
|
+
items[items.length - 1].focus();
|
|
9886
|
+
break;
|
|
9887
|
+
case 'ArrowRight': {
|
|
9888
|
+
const next = items[at + 1];
|
|
9889
|
+
if (!item.open)
|
|
9890
|
+
act(item, ['open']);
|
|
9891
|
+
else if (next && parentItemOf(next) === element)
|
|
9892
|
+
next.focus();
|
|
9893
|
+
break;
|
|
9894
|
+
}
|
|
9895
|
+
case 'ArrowLeft':
|
|
9896
|
+
if (item.open && !item.disabled)
|
|
9897
|
+
act(item, ['open']);
|
|
9898
|
+
else
|
|
9899
|
+
parentItemOf(element)?.focus();
|
|
9900
|
+
break;
|
|
9901
|
+
// A held key acts once.
|
|
9902
|
+
case 'Enter':
|
|
9903
|
+
if (!event.repeat)
|
|
9904
|
+
act(item, actions.enter);
|
|
9905
|
+
break;
|
|
9906
|
+
case ' ':
|
|
9907
|
+
if (!event.repeat)
|
|
9908
|
+
act(item, actions.space);
|
|
9909
|
+
break;
|
|
9910
|
+
case '*': {
|
|
9911
|
+
const closed = [];
|
|
9912
|
+
registry.records.forEach((record, value) => {
|
|
9913
|
+
if (record.parent === item.parent &&
|
|
9914
|
+
record.isParent &&
|
|
9915
|
+
!record.disabled &&
|
|
9916
|
+
!expanded.includes(value)) {
|
|
9917
|
+
closed.push(value);
|
|
9918
|
+
}
|
|
9919
|
+
});
|
|
9920
|
+
if (closed.length)
|
|
9921
|
+
changeOpen([...expanded, ...closed]);
|
|
9922
|
+
break;
|
|
9923
|
+
}
|
|
9924
|
+
default:
|
|
9925
|
+
// Type-ahead takes a printable character. Anything else, Escape and
|
|
9926
|
+
// Tab included, is left to whatever is around the tree.
|
|
9927
|
+
if (event.key.length !== 1)
|
|
9928
|
+
return;
|
|
9929
|
+
typeaheadMatch(items, typeahead.type(event.key), at, el => typeaheadText(el.firstElementChild))?.focus();
|
|
9930
|
+
}
|
|
9931
|
+
event.preventDefault();
|
|
9932
|
+
};
|
|
9933
|
+
const onClick = (element, item, onToggle) => {
|
|
9934
|
+
element.focus();
|
|
9935
|
+
act(item, onToggle ? ['open'] : actions.click);
|
|
9936
|
+
};
|
|
9937
|
+
return {
|
|
9938
|
+
treeRef,
|
|
9939
|
+
rendersClosed: mode === 'checkbox',
|
|
9940
|
+
tabStop,
|
|
9941
|
+
isOpen,
|
|
9942
|
+
stateOf,
|
|
9943
|
+
register,
|
|
9944
|
+
setElement,
|
|
9945
|
+
onFocus,
|
|
9946
|
+
onBlur,
|
|
9947
|
+
onKeyDown,
|
|
9948
|
+
onClick,
|
|
9949
|
+
};
|
|
9950
|
+
}
|
|
9593
9951
|
|
|
9952
|
+
const FieldContext = React.createContext(false);
|
|
9953
|
+
const ControlContext = React.createContext(false);
|
|
9594
9954
|
/**
|
|
9595
|
-
*
|
|
9596
|
-
*
|
|
9597
|
-
* @function
|
|
9598
|
-
* @param {CodeProps} props - Props for the Code component.
|
|
9599
|
-
* @returns {JSX.Element} The rendered code element.
|
|
9955
|
+
* Hook to detect if the component is inside a Field wrapper.
|
|
9956
|
+
* Form components use this to skip rendering their own Field.
|
|
9600
9957
|
*/
|
|
9601
|
-
const
|
|
9602
|
-
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
9603
|
-
color: textColor,
|
|
9604
|
-
backgroundColor: bgColor,
|
|
9605
|
-
...props,
|
|
9606
|
-
});
|
|
9607
|
-
const bulmaClasses = usePrefixedClassNames();
|
|
9608
|
-
const codeClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
|
|
9609
|
-
return (jsxRuntime.jsx("code", { className: codeClasses || undefined, ...rest, children: children }));
|
|
9610
|
-
};
|
|
9611
|
-
|
|
9612
|
-
// Valid size modifiers for the content class
|
|
9613
|
-
const validSizes = ['small', 'medium', 'large'];
|
|
9958
|
+
const useInsideField = () => React.useContext(FieldContext);
|
|
9614
9959
|
/**
|
|
9615
|
-
*
|
|
9616
|
-
*
|
|
9617
|
-
* @function
|
|
9618
|
-
* @param {ContentProps} props - Props for the Content component.
|
|
9619
|
-
* @returns {JSX.Element} The rendered content block.
|
|
9620
|
-
* @see {@link https://bulma.io/documentation/elements/content/ | Bulma Content documentation}
|
|
9960
|
+
* Hook to detect if the component is inside a Control wrapper.
|
|
9961
|
+
* Form components use this to skip rendering their own Control.
|
|
9621
9962
|
*/
|
|
9622
|
-
const
|
|
9623
|
-
|
|
9624
|
-
|
|
9625
|
-
|
|
9626
|
-
|
|
9627
|
-
|
|
9628
|
-
|
|
9629
|
-
|
|
9630
|
-
|
|
9631
|
-
|
|
9632
|
-
|
|
9633
|
-
|
|
9634
|
-
|
|
9635
|
-
|
|
9636
|
-
|
|
9637
|
-
|
|
9963
|
+
const useInsideControl = () => React.useContext(ControlContext);
|
|
9964
|
+
/** Provider for Field context — used internally by Field component. */
|
|
9965
|
+
const FieldProvider = FieldContext.Provider;
|
|
9966
|
+
/** Provider for Control context — used internally by Control component. */
|
|
9967
|
+
const ControlProvider = ControlContext.Provider;
|
|
9968
|
+
// Each Field-shaping prop, with what takes its place on a `Field` the caller
|
|
9969
|
+
// wraps around the `Control`. `message` needs nothing there, because the
|
|
9970
|
+
// wrapper still renders it inside `Field > Control`. The other Field-level
|
|
9971
|
+
// props (`labelSize`, `labelProps`, `messageColor`) change nothing unless a
|
|
9972
|
+
// `label` or `message` renders, so on their own they keep no `Field`.
|
|
9973
|
+
// `bare-control.test.tsx` holds this list to what each wrapper hands its
|
|
9974
|
+
// `Field` and to which props change its markup, so a new Field-level prop
|
|
9975
|
+
// fails there until it is sorted into one group or the other.
|
|
9976
|
+
const FIELD_SHAPING = [
|
|
9977
|
+
['label', 'label'],
|
|
9978
|
+
['message', undefined],
|
|
9979
|
+
['horizontal', 'horizontal'],
|
|
9980
|
+
['fieldClassName', 'className (for fieldClassName)'],
|
|
9981
|
+
];
|
|
9982
|
+
/** `a`, `a and b`, `a, b and c`. */
|
|
9983
|
+
const listOf = (items) => items.length > 1
|
|
9984
|
+
? `${items.slice(0, -1).join(', ')} and ${items[items.length - 1]}`
|
|
9985
|
+
: items[0];
|
|
9638
9986
|
/**
|
|
9639
|
-
*
|
|
9640
|
-
*
|
|
9641
|
-
*
|
|
9642
|
-
*
|
|
9643
|
-
*
|
|
9644
|
-
*
|
|
9645
|
-
*
|
|
9646
|
-
*
|
|
9987
|
+
* Whether a convenience form wrapper renders a `Field` of its own (#905).
|
|
9988
|
+
* Never inside an outer `Field`. Inside a `Control` with no `Field` around
|
|
9989
|
+
* it, a `Field` of its own would put a `.field` inside the `.control`, where
|
|
9990
|
+
* Bulma's control styles break (its icon rules are sibling selectors on the
|
|
9991
|
+
* input). So it renders one there only when a prop that shapes the `Field`'s
|
|
9992
|
+
* markup is set (`label`, `message`, `horizontal`, `fieldClassName`), keeping
|
|
9993
|
+
* the markup it always had, and warns in development that the fix is a
|
|
9994
|
+
* `Field` around that `Control`. A prop counts when it is truthy, the same
|
|
9995
|
+
* test that decides whether it changes the `Field`'s output.
|
|
9996
|
+
* Internal; not part of the public API.
|
|
9647
9997
|
*/
|
|
9648
|
-
const
|
|
9649
|
-
|
|
9650
|
-
|
|
9651
|
-
|
|
9652
|
-
|
|
9653
|
-
|
|
9654
|
-
|
|
9655
|
-
|
|
9656
|
-
|
|
9657
|
-
const
|
|
9658
|
-
[
|
|
9659
|
-
|
|
9660
|
-
})
|
|
9661
|
-
|
|
9662
|
-
|
|
9998
|
+
const rendersOwnField = (component, options) => {
|
|
9999
|
+
if (options.insideField)
|
|
10000
|
+
return false;
|
|
10001
|
+
if (!options.insideControl)
|
|
10002
|
+
return true;
|
|
10003
|
+
const held = FIELD_SHAPING.filter(([prop]) => options[prop]);
|
|
10004
|
+
if (held.length === 0)
|
|
10005
|
+
return false;
|
|
10006
|
+
const names = held.map(([prop]) => prop);
|
|
10007
|
+
const moved = held
|
|
10008
|
+
.map(([, onField]) => onField)
|
|
10009
|
+
.filter((onField) => onField !== undefined);
|
|
10010
|
+
warnOnce(`${component}:Field-in-bare-Control:${names.join('+')}`, `[bestax-bulma] <${component} ${names.join(' ')}> inside a <Control> ` +
|
|
10011
|
+
`with no <Field> around it renders its own <Field> for ` +
|
|
10012
|
+
`${names.length > 1 ? 'those props' : 'that prop'}, which puts a ` +
|
|
10013
|
+
`.field inside the .control, a nesting Bulma's styles are not ` +
|
|
10014
|
+
`written for. Wrap the <Control> in a <Field> instead` +
|
|
10015
|
+
(moved.length > 0 ? `, and set ${listOf(moved)} on that <Field>.` : '.'));
|
|
10016
|
+
return true;
|
|
10017
|
+
};
|
|
10018
|
+
// Each Control-level prop, with what takes its place on the `Control` the
|
|
10019
|
+
// caller wrapped around the wrapper. `bare-control.test.tsx` holds this to
|
|
10020
|
+
// what each wrapper hands its own `Control`, so a new Control-level prop
|
|
10021
|
+
// fails there until it is added here and passed to `rendersOwnControl`.
|
|
10022
|
+
const ON_CONTROL = {
|
|
10023
|
+
isLoading: 'isLoading',
|
|
10024
|
+
iconLeft: 'iconLeft',
|
|
10025
|
+
iconLeftName: 'iconLeftName',
|
|
10026
|
+
iconLeftSize: 'iconLeftSize',
|
|
10027
|
+
iconRight: 'iconRight',
|
|
10028
|
+
iconRightName: 'iconRightName',
|
|
10029
|
+
iconRightSize: 'iconRightSize',
|
|
10030
|
+
hasIconsLeft: 'hasIconsLeft',
|
|
10031
|
+
hasIconsRight: 'hasIconsRight',
|
|
10032
|
+
isExpanded: 'isExpanded',
|
|
10033
|
+
controlSize: 'size (for controlSize)',
|
|
10034
|
+
controlClassName: 'className (for controlClassName)',
|
|
9663
10035
|
};
|
|
9664
|
-
|
|
9665
10036
|
/**
|
|
9666
|
-
*
|
|
9667
|
-
*
|
|
9668
|
-
*
|
|
9669
|
-
*
|
|
9670
|
-
*
|
|
10037
|
+
* Whether a convenience form wrapper renders a `Control` of its own (#921):
|
|
10038
|
+
* only outside one. Inside a `Control` it renders none, so the props it
|
|
10039
|
+
* would have handed its own do nothing there, and it warns in development,
|
|
10040
|
+
* naming them and saying to set them on that `Control`. An `inline` picker
|
|
10041
|
+
* renders none in either place, so it drops them inside a `Control` or not,
|
|
10042
|
+
* and its warning says so without pointing at a `Control`. A wrapper passes
|
|
10043
|
+
* only the props it hands its own `Control`, as the caller gave them, so a
|
|
10044
|
+
* default the wrapper fills in itself never warns. A prop counts when it is
|
|
10045
|
+
* truthy, since a falsy one would change nothing on a `Control` either.
|
|
10046
|
+
* `iconLeftSize` and `hasIconsLeft` show nothing on a `Control` without a
|
|
10047
|
+
* glyph, so when they move and the caller chose no left icon, the advice
|
|
10048
|
+
* names the wrapper's default glyph too, and following it draws what the
|
|
10049
|
+
* wrapper's own `Control` did.
|
|
10050
|
+
* Internal; not part of the public API.
|
|
9671
10051
|
*/
|
|
9672
|
-
const
|
|
9673
|
-
|
|
9674
|
-
|
|
9675
|
-
|
|
9676
|
-
|
|
9677
|
-
|
|
9678
|
-
|
|
9679
|
-
|
|
9680
|
-
|
|
9681
|
-
|
|
9682
|
-
|
|
10052
|
+
const rendersOwnControl = (component, options) => {
|
|
10053
|
+
if (!options.inline && !options.insideControl)
|
|
10054
|
+
return true;
|
|
10055
|
+
const held = Object.keys(ON_CONTROL).filter(prop => options[prop]);
|
|
10056
|
+
if (held.length === 0)
|
|
10057
|
+
return false;
|
|
10058
|
+
const names = held.join(' ');
|
|
10059
|
+
const doNothing = held.length > 1 ? 'those props do' : 'that prop does';
|
|
10060
|
+
let message;
|
|
10061
|
+
if (options.inline) {
|
|
10062
|
+
message =
|
|
10063
|
+
`[bestax-bulma] <${component} inline ${names}> renders no <Control> ` +
|
|
10064
|
+
`in inline mode, inside a <Control> or not, so ${doNothing} ` +
|
|
10065
|
+
`nothing. Leave ${held.length > 1 ? 'them' : 'it'} out of an ` +
|
|
10066
|
+
`inline picker.`;
|
|
10067
|
+
}
|
|
10068
|
+
else {
|
|
10069
|
+
const toSet = held.map(prop => ON_CONTROL[prop]);
|
|
10070
|
+
const needsGlyph = (options.iconLeftSize || options.hasIconsLeft) &&
|
|
10071
|
+
!options.iconLeft &&
|
|
10072
|
+
options.iconLeftName === undefined &&
|
|
10073
|
+
options.defaultIconLeftName;
|
|
10074
|
+
if (needsGlyph) {
|
|
10075
|
+
toSet.push(`iconLeftName="${options.defaultIconLeftName}" (its default icon)`);
|
|
10076
|
+
}
|
|
10077
|
+
message =
|
|
10078
|
+
`[bestax-bulma] <${component} ${names}> inside a <Control> renders ` +
|
|
10079
|
+
`no <Control> of its own, so ${doNothing} nothing there. Set ` +
|
|
10080
|
+
`${listOf(toSet)} on that <Control> instead.`;
|
|
10081
|
+
}
|
|
10082
|
+
// Keyed by the message itself: the props named do not settle the advice
|
|
10083
|
+
// (an empty `iconLeftName` and an unset one name the same props), so two
|
|
10084
|
+
// calls that would advise differently must not share a key.
|
|
10085
|
+
warnOnce(message, message);
|
|
10086
|
+
return false;
|
|
9683
10087
|
};
|
|
9684
|
-
|
|
10088
|
+
const FieldLabelIdContext = React.createContext(undefined);
|
|
9685
10089
|
/**
|
|
9686
|
-
* The
|
|
9687
|
-
*
|
|
9688
|
-
*
|
|
9689
|
-
*
|
|
9690
|
-
*
|
|
10090
|
+
* The id a labeled Field wants its single composed control to adopt (#495).
|
|
10091
|
+
* `undefined` outside a Field, in unlabeled/grouped/addons Fields, or when the
|
|
10092
|
+
* user took over the association with an explicit `labelProps.htmlFor`.
|
|
10093
|
+
* Consumed through `useFieldLabelTarget` by the single-control bases
|
|
10094
|
+
* (InputBase, SelectBase, TextAreaBase, and the date and time picker bases
|
|
10095
|
+
* unless inline, #968) and, through `useAutoLabelId`, by the convenience
|
|
10096
|
+
* inputs that render an input of their own (#939). Read it only through
|
|
10097
|
+
* those, which report what they take: a control that read it directly would
|
|
10098
|
+
* report nothing and lose its label (#1004), and
|
|
10099
|
+
* `__tests__/field-label-readers.test.ts` fails on one. Internal; not part of
|
|
10100
|
+
* the public API.
|
|
9691
10101
|
*/
|
|
9692
|
-
const
|
|
9693
|
-
|
|
9694
|
-
|
|
9695
|
-
|
|
9696
|
-
...props,
|
|
9697
|
-
});
|
|
9698
|
-
const bulmaClasses = usePrefixedClassNames();
|
|
9699
|
-
const emphasisClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
|
|
9700
|
-
return (jsxRuntime.jsx("em", { className: emphasisClasses || undefined, ...rest, children: children }));
|
|
9701
|
-
};
|
|
9702
|
-
|
|
10102
|
+
const useFieldLabelId = () => React.useContext(FieldLabelIdContext);
|
|
10103
|
+
/** Provider for the Field label-target id — used internally by Field. */
|
|
10104
|
+
const FieldLabelIdProvider = FieldLabelIdContext.Provider;
|
|
10105
|
+
const FieldLabelElementIdContext = React.createContext(undefined);
|
|
9703
10106
|
/**
|
|
9704
|
-
*
|
|
9705
|
-
*
|
|
9706
|
-
*
|
|
9707
|
-
*
|
|
9708
|
-
*
|
|
9709
|
-
*
|
|
9710
|
-
* @param {FigureCaptionProps} props - Props for the FigureCaption component.
|
|
9711
|
-
* @returns {JSX.Element} The rendered figcaption element.
|
|
10107
|
+
* The id of a labeled Field's own `<label>`, for a group control (Radios,
|
|
10108
|
+
* Checkboxes, Rate, DateRangeInput and DateRangeInputBase) to point
|
|
10109
|
+
* `aria-labelledby` at, since a group cannot take the label's `htmlFor`
|
|
10110
|
+
* (#939, #1005). Set under the same conditions as
|
|
10111
|
+
* {@link useFieldLabelId}. Consumed through `useAutoLabelledBy`.
|
|
10112
|
+
* Internal; not part of the public API.
|
|
9712
10113
|
*/
|
|
9713
|
-
const
|
|
9714
|
-
|
|
9715
|
-
|
|
9716
|
-
|
|
9717
|
-
|
|
9718
|
-
|
|
9719
|
-
|
|
9720
|
-
|
|
9721
|
-
|
|
10114
|
+
const useFieldLabelElementId = () => React.useContext(FieldLabelElementIdContext);
|
|
10115
|
+
/** Provider for the Field label's own id, used internally by Field. */
|
|
10116
|
+
const FieldLabelElementIdProvider = FieldLabelElementIdContext.Provider;
|
|
10117
|
+
const FieldLabelForContext = React.createContext({});
|
|
10118
|
+
/**
|
|
10119
|
+
* The nearest labeled Field's `<label>`: what it points `htmlFor` at, the id
|
|
10120
|
+
* it generated or the caller's own `labelProps.htmlFor`, and the id the label
|
|
10121
|
+
* renders with, if it has one. An unlabeled Field passes both through
|
|
10122
|
+
* together, so a control in the inner Field of a horizontal layout can tell
|
|
10123
|
+
* the outer label names it, and point at that label by its id, as a range
|
|
10124
|
+
* Slider's thumbs (#981) and Autocomplete's and Taginput's suggestion lists
|
|
10125
|
+
* (#998, #1082) do. The
|
|
10126
|
+
* `htmlFor` is only ever compared with a control's own id, never adopted, so
|
|
10127
|
+
* it hands out no id for a control. It keeps the generated id after the
|
|
10128
|
+
* label drops a `for` nothing took (#1004), which no control's id matches
|
|
10129
|
+
* then, so the value does not change under the content that reported it.
|
|
10130
|
+
* The `id` is set only while that label renders with it. Unlike
|
|
10131
|
+
* {@link useFieldLabelId}, it is set in a `grouped` or `hasAddons` Field
|
|
10132
|
+
* too, so a label wired by hand names the control its `for` picks in those
|
|
10133
|
+
* rows as well. Consumed through `useAutoLabelId`.
|
|
10134
|
+
* Internal; not part of the public API.
|
|
10135
|
+
*/
|
|
10136
|
+
const useFieldLabelFor = () => React.useContext(FieldLabelForContext);
|
|
10137
|
+
/** Provider for the Field label's `htmlFor` target and id, used internally by Field. */
|
|
10138
|
+
const FieldLabelForProvider = FieldLabelForContext.Provider;
|
|
10139
|
+
const FieldLabelForReportContext = React.createContext(undefined);
|
|
10140
|
+
/**
|
|
10141
|
+
* Tells the nearest labeled Field that this content took its generated id
|
|
10142
|
+
* while `takesFor` is true (#1004). Once the Field's content has mounted, its
|
|
10143
|
+
* label keeps the generated `for` only while something has said so, so the
|
|
10144
|
+
* label renders none over a group, a `Checkbox`, `Radio` or `Switch`, an
|
|
10145
|
+
* inner `Field`, an input with an `id` of its own, markup of your own, or
|
|
10146
|
+
* nothing at all. Anything that puts the id from {@link useFieldLabelId} on
|
|
10147
|
+
* an element has to say so, or the label drops the `for` that names it, and
|
|
10148
|
+
* content that takes no id has nothing to do. Set under the same conditions
|
|
10149
|
+
* as {@link useFieldLabelId}, so a label wired by hand and a `grouped` or
|
|
10150
|
+
* `hasAddons` Field hear nothing. It reports from a layout effect, which a
|
|
10151
|
+
* server never runs and a hydrating client runs only after its first render,
|
|
10152
|
+
* so that render writes the `for` the way the server did.
|
|
10153
|
+
* Internal; not part of the public API.
|
|
10154
|
+
*/
|
|
10155
|
+
const useReportFieldLabelFor = (takesFor) => {
|
|
10156
|
+
const report = React.useContext(FieldLabelForReportContext);
|
|
10157
|
+
useClientLayoutEffect(() => (takesFor ? report?.() : undefined), [report, takesFor]);
|
|
9722
10158
|
};
|
|
10159
|
+
/** Provider for the labeled Field's report function, used internally by Field. */
|
|
10160
|
+
const FieldLabelForReportProvider = FieldLabelForReportContext.Provider;
|
|
9723
10161
|
/**
|
|
9724
|
-
* The
|
|
9725
|
-
*
|
|
9726
|
-
*
|
|
9727
|
-
*
|
|
9728
|
-
* @
|
|
10162
|
+
* The id a control with one input of its own renders with: the caller's
|
|
10163
|
+
* `id` when one arrived, otherwise the one a labeled Field offers (#495),
|
|
10164
|
+
* unless it has no input to put it on (`hasInput: false`, as in an `inline`
|
|
10165
|
+
* picker). It also tells that Field when it took the Field's id, through
|
|
10166
|
+
* {@link useReportFieldLabelFor}, so the label keeps its `for` (#1004). Used
|
|
10167
|
+
* by the single-control bases. Internal; not part of the public API.
|
|
9729
10168
|
*/
|
|
9730
|
-
const
|
|
9731
|
-
const
|
|
9732
|
-
|
|
9733
|
-
|
|
9734
|
-
|
|
9735
|
-
});
|
|
9736
|
-
const bulmaClasses = usePrefixedClassNames();
|
|
9737
|
-
const figureClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
|
|
9738
|
-
return (jsxRuntime.jsx("figure", { className: figureClasses || undefined, ...rest, children: children }));
|
|
10169
|
+
const useFieldLabelTarget = (id, hasInput = true) => {
|
|
10170
|
+
const fieldLabelId = useFieldLabelId();
|
|
10171
|
+
const target = id ?? (hasInput ? fieldLabelId : undefined);
|
|
10172
|
+
useReportFieldLabelFor(fieldLabelId !== undefined && target === fieldLabelId);
|
|
10173
|
+
return target;
|
|
9739
10174
|
};
|
|
10175
|
+
const RadiosContext = React.createContext(undefined);
|
|
10176
|
+
const CheckboxesContext = React.createContext(undefined);
|
|
9740
10177
|
/**
|
|
9741
|
-
*
|
|
9742
|
-
*
|
|
9743
|
-
* @example
|
|
9744
|
-
* <Figure>
|
|
9745
|
-
* <img src="image.jpg" alt="Description" />
|
|
9746
|
-
* <Figure.Caption>Image caption text</Figure.Caption>
|
|
9747
|
-
* </Figure>
|
|
10178
|
+
* Hook to read the full surrounding `<Radios>` group context (name + selection).
|
|
10179
|
+
* Returns `undefined` when not inside a `<Radios>` group.
|
|
9748
10180
|
*/
|
|
9749
|
-
const
|
|
9750
|
-
Caption: FigureCaption,
|
|
9751
|
-
}, 'Figure');
|
|
9752
|
-
|
|
10181
|
+
const useRadiosGroup = () => React.useContext(RadiosContext);
|
|
9753
10182
|
/**
|
|
9754
|
-
*
|
|
9755
|
-
* `
|
|
9756
|
-
* `iconLeft`/`iconRight`, `IconText`'s `iconProps`.
|
|
9757
|
-
*
|
|
9758
|
-
* One copy, imported by both consumers: two hand-maintained copies, one per
|
|
9759
|
-
* consumer, is how the deprecated `icon` path came to crash both (#663).
|
|
9760
|
-
*
|
|
9761
|
-
* Its own module rather than `Icon.tsx` because `src/index.ts` wildcard-exports
|
|
9762
|
-
* that file, and everything published there is public forever. `polymorphic.ts`
|
|
9763
|
-
* makes the same call for `isCustomElement`: an internal discriminator is not
|
|
9764
|
-
* something to support for the life of the package.
|
|
9765
|
-
*
|
|
9766
|
-
* It tests each member's VALUE, not merely its key, and only after everything
|
|
9767
|
-
* React owns is out of the way. `'icon' in value` was enough
|
|
9768
|
-
* to claim a Font Awesome `IconDefinition` — `{ prefix, iconName, icon: [w, h, …,
|
|
9769
|
-
* path] }` — whose `icon` is a path array rather than a class string. That object
|
|
9770
|
-
* is not a renderable node either, so the honest outcome is the one this slot has
|
|
9771
|
-
* always had for it: the node branch, and React's own "Objects are not valid as a
|
|
9772
|
-
* React child". Claiming it here instead rendered a blank container that still
|
|
9773
|
-
* reserved layout.
|
|
9774
|
-
*
|
|
9775
|
-
* A member added to `IconProps` needs a line here, and a missed one is a crash
|
|
9776
|
-
* rather than a fallthrough. That is the cost of a slot taking either a props
|
|
9777
|
-
* object or a node; keeping the guard in one place is what keeps the cost to a
|
|
9778
|
-
* single line.
|
|
10183
|
+
* Hook to read the full surrounding `<Checkboxes>` group context.
|
|
10184
|
+
* Returns `undefined` when not inside a `<Checkboxes>` group.
|
|
9779
10185
|
*/
|
|
9780
|
-
|
|
9781
|
-
|
|
9782
|
-
|
|
9783
|
-
|
|
9784
|
-
|
|
9785
|
-
// test below claimed it and `Control` spread it into `<Icon>` as props —
|
|
9786
|
-
// rendering the portal's child locally instead of in its container. Every
|
|
9787
|
-
// React internal carries `$$typeof`, so testing for it covers lazy, context
|
|
9788
|
-
// and whatever React adds next, rather than portals alone.
|
|
9789
|
-
if (React.isValidElement(value) || '$$typeof' in value)
|
|
9790
|
-
return false;
|
|
9791
|
-
const candidate = value;
|
|
9792
|
-
return (typeof candidate.name === 'string' ||
|
|
9793
|
-
typeof candidate.icon === 'string' ||
|
|
9794
|
-
'children' in candidate);
|
|
9795
|
-
}
|
|
10186
|
+
const useCheckboxesGroup = () => React.useContext(CheckboxesContext);
|
|
10187
|
+
/** Provider for the Radios group context — used internally by Radios. */
|
|
10188
|
+
const RadiosProvider = RadiosContext.Provider;
|
|
10189
|
+
/** Provider for the Checkboxes group context — used internally by Checkboxes. */
|
|
10190
|
+
const CheckboxesProvider = CheckboxesContext.Provider;
|
|
9796
10191
|
|
|
9797
10192
|
/**
|
|
9798
|
-
* The
|
|
10193
|
+
* The values the Checkbox `color` prop accepts, as a readonly tuple.
|
|
9799
10194
|
*
|
|
9800
|
-
*
|
|
9801
|
-
*
|
|
9802
|
-
*
|
|
9803
|
-
*
|
|
9804
|
-
*/
|
|
9805
|
-
const IconTextComponent = ({ className, textColor, color, bgColor, iconProps, children, items, ...props }) => {
|
|
9806
|
-
/**
|
|
9807
|
-
* Generates Bulma helper classes and separates out remaining props.
|
|
9808
|
-
*/
|
|
9809
|
-
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
9810
|
-
color: textColor ?? color,
|
|
9811
|
-
backgroundColor: bgColor,
|
|
9812
|
-
...props,
|
|
9813
|
-
});
|
|
9814
|
-
const bulmaClasses = usePrefixedClassNames('icon-text');
|
|
9815
|
-
const iconTextClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
|
|
9816
|
-
return (jsxRuntime.jsx("span", { className: iconTextClasses, ...rest, children: items ? (items.map((item, index) => (jsxRuntime.jsxs(React.Fragment, { children: [item.iconProps &&
|
|
9817
|
-
(isIconProps(item.iconProps) ? (jsxRuntime.jsx(Icon, { ...item.iconProps })) : (jsxRuntime.jsx(Icon, { children: item.iconProps }))), item.text && jsxRuntime.jsx("span", { children: item.text })] }, index)))) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [iconProps &&
|
|
9818
|
-
(isIconProps(iconProps) ? (jsxRuntime.jsx(Icon, { ...iconProps })) : (jsxRuntime.jsx(Icon, { children: iconProps }))), children && jsxRuntime.jsx("span", { children: children })] })) }));
|
|
9819
|
-
};
|
|
9820
|
-
const IconText = withSubComponents(IconTextComponent, { Icon }, 'IconText');
|
|
9821
|
-
|
|
9822
|
-
/**
|
|
9823
|
-
* The `Image` component wraps images, iframes, or custom content in a Bulma-styled container, supporting fixed sizes, aspect ratios, rounded corners, retina images, and all Bulma helper props for color and spacing.
|
|
10195
|
+
* `CheckboxProps['color']` is typed from it, so the two list the same values.
|
|
10196
|
+
* Map over it to build a color picker, or check a value that arrives at
|
|
10197
|
+
* runtime before passing it in: the component adds no color class for a value
|
|
10198
|
+
* outside the tuple.
|
|
9824
10199
|
*
|
|
9825
|
-
* @
|
|
9826
|
-
*
|
|
9827
|
-
*
|
|
9828
|
-
*
|
|
10200
|
+
* @example
|
|
10201
|
+
* import { Checkbox, checkboxColors } from '@allxsmith/bestax-bulma';
|
|
10202
|
+
*
|
|
10203
|
+
* checkboxColors.map(color => (
|
|
10204
|
+
* <Checkbox key={color} color={color}>
|
|
10205
|
+
* {color}
|
|
10206
|
+
* </Checkbox>
|
|
10207
|
+
* ));
|
|
9829
10208
|
*/
|
|
9830
|
-
const
|
|
9831
|
-
|
|
9832
|
-
|
|
9833
|
-
|
|
9834
|
-
|
|
9835
|
-
|
|
9836
|
-
|
|
9837
|
-
|
|
9838
|
-
});
|
|
9839
|
-
const bulmaClasses = usePrefixedClassNames('image', {
|
|
9840
|
-
[`is-${size}`]: size,
|
|
9841
|
-
'has-ratio': size && typeof size === 'string' && size.includes('by'),
|
|
9842
|
-
});
|
|
9843
|
-
const imageClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
|
|
9844
|
-
// Default tag logic: if "as" is provided, use it.
|
|
9845
|
-
// If not, use <figure> for aspect ratios or children, <div> otherwise.
|
|
9846
|
-
let Tag;
|
|
9847
|
-
if (as) {
|
|
9848
|
-
Tag = as;
|
|
9849
|
-
}
|
|
9850
|
-
else if (size && typeof size === 'string' && size.includes('by')) {
|
|
9851
|
-
Tag = 'figure';
|
|
9852
|
-
}
|
|
9853
|
-
else {
|
|
9854
|
-
Tag = 'div';
|
|
9855
|
-
}
|
|
9856
|
-
const roundedClass = usePrefixedClassNames('is-rounded');
|
|
9857
|
-
const content = children ? (children) : (jsxRuntime.jsx("img", { className: classNames({ [roundedClass]: isRounded }) || undefined, src: src, alt: alt, ...(isRetina && src ? { srcSet: `${src} 2x` } : {}) }));
|
|
9858
|
-
return (jsxRuntime.jsx(Tag, { className: imageClasses, ...rest, children: content }));
|
|
9859
|
-
};
|
|
9860
|
-
|
|
10209
|
+
const checkboxColors = [
|
|
10210
|
+
'primary',
|
|
10211
|
+
'link',
|
|
10212
|
+
'info',
|
|
10213
|
+
'success',
|
|
10214
|
+
'warning',
|
|
10215
|
+
'danger',
|
|
10216
|
+
];
|
|
9861
10217
|
/**
|
|
9862
|
-
*
|
|
9863
|
-
*
|
|
9864
|
-
* It doesn't wrap in the middle and isn't drawn as a key itself. It sets no
|
|
9865
|
-
* font size, so the keys in it are the size of a key on its own.
|
|
10218
|
+
* The values the Checkbox `size` prop accepts, as a readonly tuple.
|
|
9866
10219
|
*
|
|
9867
|
-
*
|
|
9868
|
-
*
|
|
9869
|
-
*
|
|
9870
|
-
*
|
|
10220
|
+
* `CheckboxProps['size']` is typed from it, so the two list the same values.
|
|
10221
|
+
* Use it to offer a size choice or to check a value that arrives at runtime:
|
|
10222
|
+
* the component adds no size class for a value outside the tuple. These are
|
|
10223
|
+
* element sizes, not the spacing scale in `validSizes`.
|
|
9871
10224
|
*
|
|
9872
10225
|
* @example
|
|
9873
|
-
*
|
|
9874
|
-
* <Kbd>Ctrl</Kbd>
|
|
9875
|
-
* <Kbd>Shift</Kbd>
|
|
9876
|
-
* <Kbd>P</Kbd>
|
|
9877
|
-
* </Kbd.Group>
|
|
10226
|
+
* import { checkboxSizes } from '@allxsmith/bestax-bulma';
|
|
9878
10227
|
*
|
|
9879
|
-
*
|
|
9880
|
-
* <Kbd.Group separator="then">
|
|
9881
|
-
* <Kbd>G</Kbd>
|
|
9882
|
-
* <Kbd>I</Kbd>
|
|
9883
|
-
* </Kbd.Group>
|
|
10228
|
+
* type CheckboxSize = (typeof checkboxSizes)[number];
|
|
9884
10229
|
*/
|
|
9885
|
-
const
|
|
9886
|
-
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
9887
|
-
color: textColor,
|
|
9888
|
-
...props,
|
|
9889
|
-
});
|
|
9890
|
-
const groupClass = usePrefixedClassNames('kbd-group');
|
|
9891
|
-
const separatorClass = usePrefixedClassNames('kbd-separator');
|
|
9892
|
-
// Null, undefined and booleans drop out, and Fragments are opened up, so
|
|
9893
|
-
// an empty Fragment leaves no separator behind and the keys in one are
|
|
9894
|
-
// separated like the rest. The spaces are part of the text, so a screen
|
|
9895
|
-
// reader reads "Ctrl plus S" rather than one run-together word, whatever
|
|
9896
|
-
// the CSS does.
|
|
9897
|
-
const keys = flattenChildren(children);
|
|
9898
|
-
return (jsxRuntime.jsx("kbd", { ref: ref, className: classNames(groupClass, bulmaHelperClasses, className), ...rest, children: keys.map((key, index) => (jsxRuntime.jsxs(React.Fragment, { children: [index > 0 && (jsxRuntime.jsx("span", { className: separatorClass, children: ` ${separator} ` })), key] }, React.isValidElement(key) && key.key != null ? key.key : index))) }));
|
|
9899
|
-
});
|
|
9900
|
-
KbdGroup.displayName = 'Kbd.Group';
|
|
10230
|
+
const checkboxSizes = ['small', 'normal', 'medium', 'large'];
|
|
9901
10231
|
/**
|
|
9902
|
-
* The `
|
|
9903
|
-
*
|
|
9904
|
-
* It only shows the key: it doesn't listen for it. The key takes its colors
|
|
9905
|
-
* from the text it sits in, and its size in `em`, so it fits a paragraph, a
|
|
9906
|
-
* title or a filled button as it is, and `textSize` sizes it outright. For a
|
|
9907
|
-
* combination or a sequence, put the keys in a `Kbd.Group`. What tells
|
|
9908
|
-
* assistive tech a control has a shortcut is `aria-keyshortcuts` on that
|
|
9909
|
-
* control, with `Kbd` as the hint people see next to it.
|
|
10232
|
+
* The `Checkbox` component provides a Bulma-styled checkbox input.
|
|
9910
10233
|
*
|
|
9911
10234
|
* @function
|
|
9912
|
-
* @param {
|
|
9913
|
-
* @
|
|
9914
|
-
* @returns {JSX.Element} The rendered key.
|
|
10235
|
+
* @param {CheckboxProps} props - Props for the Checkbox component.
|
|
10236
|
+
* @returns {JSX.Element} The rendered checkbox element.
|
|
9915
10237
|
*
|
|
9916
10238
|
* @example
|
|
9917
|
-
*
|
|
10239
|
+
* // Basic checkbox
|
|
10240
|
+
* <Checkbox>Accept terms</Checkbox>
|
|
9918
10241
|
*
|
|
9919
10242
|
* @example
|
|
9920
|
-
*
|
|
10243
|
+
* // Inside a group
|
|
10244
|
+
* <Checkboxes name="tags" defaultValue={['react']}>
|
|
10245
|
+
* <Checkbox value="react">React</Checkbox>
|
|
10246
|
+
* <Checkbox value="vue">Vue</Checkbox>
|
|
10247
|
+
* </Checkboxes>
|
|
9921
10248
|
*/
|
|
9922
|
-
const
|
|
10249
|
+
const Checkbox = React.forwardRef(({ color, size, className, children, textColor, disabled, name, value, checked, defaultChecked, indeterminate, onChange, ...props }, ref) => {
|
|
9923
10250
|
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
9924
10251
|
color: textColor,
|
|
9925
|
-
backgroundColor: bgColor,
|
|
9926
10252
|
...props,
|
|
9927
10253
|
});
|
|
9928
|
-
|
|
9929
|
-
//
|
|
9930
|
-
//
|
|
9931
|
-
|
|
9932
|
-
|
|
9933
|
-
|
|
9934
|
-
|
|
9935
|
-
|
|
9936
|
-
|
|
9937
|
-
|
|
9938
|
-
|
|
9939
|
-
|
|
9940
|
-
|
|
9941
|
-
|
|
9942
|
-
|
|
9943
|
-
|
|
9944
|
-
|
|
9945
|
-
const
|
|
9946
|
-
|
|
9947
|
-
|
|
9948
|
-
|
|
9949
|
-
|
|
9950
|
-
|
|
9951
|
-
|
|
9952
|
-
|
|
9953
|
-
|
|
10254
|
+
// The input's own ref, merged with the forwarded one. A callback ref's
|
|
10255
|
+
// return value is handed back, so React 19 still runs a cleanup it
|
|
10256
|
+
// returns, as it did when the ref went straight to the input.
|
|
10257
|
+
const inputRef = React.useRef(null);
|
|
10258
|
+
const setRefs = React.useCallback((node) => {
|
|
10259
|
+
inputRef.current = node;
|
|
10260
|
+
if (typeof ref === 'function')
|
|
10261
|
+
return ref(node);
|
|
10262
|
+
if (ref)
|
|
10263
|
+
ref.current = node;
|
|
10264
|
+
}, [ref]);
|
|
10265
|
+
// `indeterminate` is a DOM property with no attribute, so it's set on the
|
|
10266
|
+
// input rather than rendered. A click clears it, so this runs after every
|
|
10267
|
+
// render, not only when the prop changes. Unset, it's left alone, so a
|
|
10268
|
+
// ref that sets the property itself still works. `ownsIndeterminateRef`
|
|
10269
|
+
// remembers that this component set it, so a prop that goes back to unset
|
|
10270
|
+
// clears the dash once instead of leaving it on.
|
|
10271
|
+
const ownsIndeterminateRef = React.useRef(false);
|
|
10272
|
+
useClientLayoutEffect(() => {
|
|
10273
|
+
if (indeterminate !== undefined) {
|
|
10274
|
+
ownsIndeterminateRef.current = true;
|
|
10275
|
+
inputRef.current.indeterminate = indeterminate;
|
|
10276
|
+
}
|
|
10277
|
+
else if (ownsIndeterminateRef.current) {
|
|
10278
|
+
ownsIndeterminateRef.current = false;
|
|
10279
|
+
inputRef.current.indeterminate = false;
|
|
10280
|
+
}
|
|
9954
10281
|
});
|
|
9955
|
-
|
|
9956
|
-
|
|
10282
|
+
// Inherit name + selection state from a surrounding <Checkboxes> group.
|
|
10283
|
+
// Local props always win over the group (explicit > implicit).
|
|
10284
|
+
const group = useCheckboxesGroup();
|
|
10285
|
+
const effectiveName = name ?? group?.name;
|
|
10286
|
+
// Group-managed checked state — only when the group is in
|
|
10287
|
+
// controlled/uncontrolled mode (group.value is defined) AND this Checkbox
|
|
10288
|
+
// has a value to compare against. Local `checked` always wins.
|
|
10289
|
+
const groupManaged = group?.value !== undefined && value !== undefined;
|
|
10290
|
+
const groupHas = groupManaged && group.value.includes(String(value));
|
|
10291
|
+
const effectiveChecked = checked !== undefined ? checked : groupManaged ? groupHas : undefined;
|
|
10292
|
+
// When the group manages checked, suppress local defaultChecked to avoid
|
|
10293
|
+
// the controlled/uncontrolled React warning.
|
|
10294
|
+
const effectiveDefaultChecked = groupManaged ? undefined : defaultChecked;
|
|
10295
|
+
const handleChange = React.useCallback((e) => {
|
|
10296
|
+
// The click that got here cleared the mixed state. Put it back now,
|
|
10297
|
+
// since a click that changes no state renders nothing to do it.
|
|
10298
|
+
if (indeterminate !== undefined)
|
|
10299
|
+
e.target.indeterminate = indeterminate;
|
|
10300
|
+
// Local onChange always fires.
|
|
10301
|
+
onChange?.(e);
|
|
10302
|
+
// Dispatch to the group additionally — toggle this value's
|
|
10303
|
+
// membership in the group's array.
|
|
10304
|
+
if (group?.onChange && value !== undefined) {
|
|
10305
|
+
const valStr = String(value);
|
|
10306
|
+
const currentArr = group.value ?? [];
|
|
10307
|
+
const next = e.target.checked
|
|
10308
|
+
? currentArr.includes(valStr)
|
|
10309
|
+
? currentArr
|
|
10310
|
+
: [...currentArr, valStr]
|
|
10311
|
+
: currentArr.filter(v => v !== valStr);
|
|
10312
|
+
group.onChange(next);
|
|
10313
|
+
}
|
|
10314
|
+
}, [onChange, group, value, indeterminate]);
|
|
10315
|
+
const mainClass = usePrefixedClassNames('styled-checkbox', 'checkbox', {
|
|
10316
|
+
[`is-${color}`]: color && checkboxColors.includes(color),
|
|
10317
|
+
[`is-${size}`]: size && checkboxSizes.includes(size),
|
|
9957
10318
|
});
|
|
9958
|
-
const
|
|
9959
|
-
|
|
10319
|
+
const checkboxClass = classNames(mainClass, bulmaHelperClasses, className);
|
|
10320
|
+
const checkClass = usePrefixedClassNames('check');
|
|
10321
|
+
const controlLabelClass = usePrefixedClassNames('control-label');
|
|
10322
|
+
return (jsxRuntime.jsxs("label", { className: checkboxClass, children: [jsxRuntime.jsx("input", { ref: setRefs, type: "checkbox", disabled: disabled, name: effectiveName, value: value, checked: effectiveChecked, defaultChecked: effectiveDefaultChecked, onChange: handleChange, ...rest }), jsxRuntime.jsx("span", { className: checkClass }), children && jsxRuntime.jsx("span", { className: controlLabelClass, children: children })] }));
|
|
9960
10323
|
});
|
|
9961
|
-
|
|
10324
|
+
Checkbox.displayName = 'Checkbox';
|
|
9962
10325
|
|
|
10326
|
+
const validTreeViewSizes = ['small', 'medium', 'large'];
|
|
10327
|
+
// Not exported, so they stay out of the public API that `index.ts` re-exports.
|
|
10328
|
+
const TreeViewContext = React.createContext(null);
|
|
10329
|
+
const TOP = { path: [], hidden: false };
|
|
10330
|
+
const BranchContext = React.createContext(TOP);
|
|
10331
|
+
/** The box's input lets the row's click toggle it, so a change on its own does nothing. */
|
|
10332
|
+
const ignoreChange = () => { };
|
|
10333
|
+
/** Whether an `icon` was given something to render. */
|
|
10334
|
+
const isGiven = (node) => node !== undefined &&
|
|
10335
|
+
node !== null &&
|
|
10336
|
+
typeof node !== 'boolean' &&
|
|
10337
|
+
node !== '';
|
|
9963
10338
|
/**
|
|
9964
|
-
* The `
|
|
10339
|
+
* The `TreeView` component renders a tree of items you browse and pick from, the way a file explorer works: one tab stop, the arrow keys to move, and Right and Left to open and close.
|
|
9965
10340
|
*
|
|
9966
|
-
*
|
|
9967
|
-
*
|
|
9968
|
-
*
|
|
9969
|
-
|
|
9970
|
-
const ListItem = ({ className, textColor, bgColor, children, ...props }) => {
|
|
9971
|
-
/**
|
|
9972
|
-
* Generates Bulma helper classes and separates out remaining props.
|
|
9973
|
-
*/
|
|
9974
|
-
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
9975
|
-
color: textColor,
|
|
9976
|
-
backgroundColor: bgColor,
|
|
9977
|
-
...props,
|
|
9978
|
-
});
|
|
9979
|
-
const bulmaClasses = usePrefixedClassNames();
|
|
9980
|
-
const itemClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
|
|
9981
|
-
return (jsxRuntime.jsx("li", { className: itemClasses || undefined, ...rest, children: children }));
|
|
9982
|
-
};
|
|
9983
|
-
|
|
9984
|
-
/**
|
|
9985
|
-
* The `Loader` component renders Bulma's `.loader`, a small spinning ring for inline loading states.
|
|
10341
|
+
* It's the APG Tree View pattern, drawn to look like Bulma's menu. It isn't
|
|
10342
|
+
* for site navigation: items aren't links, and a sidebar whose sections open
|
|
10343
|
+
* and close is `Menu`'s job. The root owns which items are open, through
|
|
10344
|
+
* `expanded`, `defaultExpanded` and `onExpandedChange`.
|
|
9986
10345
|
*
|
|
9987
|
-
*
|
|
9988
|
-
*
|
|
9989
|
-
*
|
|
9990
|
-
*
|
|
10346
|
+
* `selectionMode` picks one of three trees. With `none`, nothing is picked.
|
|
10347
|
+
* With `single`, one item is, and `selected` says which. With `checkbox`,
|
|
10348
|
+
* each item has a box, a check reaches every enabled item under it, closed
|
|
10349
|
+
* branches included, and a parent with some of its items checked is mixed.
|
|
10350
|
+
* `selected` is read in a `single` tree only, and `checked` in a `checkbox`
|
|
10351
|
+
* tree only. Selection never follows focus.
|
|
9991
10352
|
*
|
|
9992
|
-
*
|
|
9993
|
-
*
|
|
9994
|
-
*
|
|
9995
|
-
* @returns {JSX.Element} The rendered loader element.
|
|
10353
|
+
* Disabled items stay reachable from the keys, so a screen reader finds
|
|
10354
|
+
* them. The tree needs a name, from `aria-label` or `aria-labelledby`, and a
|
|
10355
|
+
* development build warns when it has neither.
|
|
9996
10356
|
*
|
|
9997
|
-
* @
|
|
9998
|
-
*
|
|
10357
|
+
* @function
|
|
10358
|
+
* @param {TreeViewProps} props - Props for the TreeView component.
|
|
10359
|
+
* @param {React.Ref<HTMLUListElement>} ref - Forwarded ref to the `<ul>`.
|
|
10360
|
+
* @returns {JSX.Element} The rendered tree.
|
|
10361
|
+
* @see {@link https://www.w3.org/WAI/ARIA/apg/patterns/treeview/ | APG Tree View Pattern}
|
|
9999
10362
|
*
|
|
10000
10363
|
* @example
|
|
10001
|
-
* <
|
|
10002
|
-
|
|
10003
|
-
|
|
10004
|
-
|
|
10005
|
-
|
|
10006
|
-
|
|
10007
|
-
|
|
10008
|
-
|
|
10009
|
-
|
|
10010
|
-
|
|
10364
|
+
* <TreeView aria-label="Files" selectionMode="single" defaultExpanded={['src']}>
|
|
10365
|
+
* <TreeView.Item value="src" label="src" icon="folder">
|
|
10366
|
+
* <TreeView.Item value="src/index.ts" label="index.ts" icon="file" />
|
|
10367
|
+
* </TreeView.Item>
|
|
10368
|
+
* <TreeView.Item value="package.json" label="package.json" icon="file" />
|
|
10369
|
+
* </TreeView>
|
|
10370
|
+
*/
|
|
10371
|
+
const TreeViewRoot = React.forwardRef(function TreeView({ children, selectionMode = 'none', selected, defaultSelected = null, onSelectedChange, checked, defaultChecked = [], onCheckedChange, expanded, defaultExpanded = [], onExpandedChange, size, className, ...props }, ref) {
|
|
10372
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses({ ...props });
|
|
10373
|
+
const engine = useTreeView({
|
|
10374
|
+
mode: selectionMode,
|
|
10375
|
+
selected,
|
|
10376
|
+
defaultSelected,
|
|
10377
|
+
onSelectedChange,
|
|
10378
|
+
checked,
|
|
10379
|
+
defaultChecked,
|
|
10380
|
+
onCheckedChange,
|
|
10381
|
+
expanded,
|
|
10382
|
+
defaultExpanded,
|
|
10383
|
+
onExpandedChange,
|
|
10384
|
+
});
|
|
10385
|
+
const { treeRef } = engine;
|
|
10386
|
+
const setRefs = React.useCallback((node) => {
|
|
10387
|
+
treeRef.current = node;
|
|
10388
|
+
if (typeof ref === 'function')
|
|
10389
|
+
ref(node);
|
|
10390
|
+
else if (ref)
|
|
10391
|
+
ref.current = node;
|
|
10392
|
+
}, [ref, treeRef]);
|
|
10393
|
+
const named = Boolean(rest['aria-label'] || rest['aria-labelledby']);
|
|
10394
|
+
React.useEffect(() => {
|
|
10395
|
+
if (!named) {
|
|
10396
|
+
warnOnce('TreeView:unnamed', '[bestax-bulma] <TreeView> has no accessible name. Pass ' +
|
|
10397
|
+
'aria-label or aria-labelledby, so a screen reader can say what ' +
|
|
10398
|
+
'the tree holds.');
|
|
10399
|
+
}
|
|
10400
|
+
}, [named]);
|
|
10401
|
+
const rootClass = usePrefixedClassNames('tree-view', {
|
|
10402
|
+
[`is-${size}`]: !!size && validTreeViewSizes.includes(size),
|
|
10403
|
+
});
|
|
10404
|
+
// The props are spread first, so the role and the attributes the tree
|
|
10405
|
+
// owns win over anything a caller passes along.
|
|
10406
|
+
return (jsxRuntime.jsx(TreeViewContext.Provider, { value: engine, children: jsxRuntime.jsx(BranchContext.Provider, { value: TOP, children: jsxRuntime.jsx("ul", { ...rest, ref: setRefs, className: classNames(rootClass, bulmaHelperClasses, className), role: "tree", "aria-multiselectable": engine.rendersClosed || undefined, children: children }) }) }));
|
|
10011
10407
|
});
|
|
10012
|
-
Loader.displayName = 'Loader';
|
|
10013
|
-
|
|
10014
10408
|
/**
|
|
10015
|
-
*
|
|
10409
|
+
* One item in the tree: a row with its chevron, its box, its icon and its label, and, while it's open, a group of the items under it.
|
|
10016
10410
|
*
|
|
10017
10411
|
* @function
|
|
10018
|
-
* @param {
|
|
10019
|
-
* @
|
|
10020
|
-
* @
|
|
10021
|
-
*/
|
|
10022
|
-
const
|
|
10023
|
-
|
|
10024
|
-
|
|
10025
|
-
|
|
10026
|
-
|
|
10027
|
-
const
|
|
10028
|
-
|
|
10029
|
-
|
|
10030
|
-
|
|
10031
|
-
const
|
|
10032
|
-
|
|
10033
|
-
|
|
10034
|
-
|
|
10412
|
+
* @param {TreeViewItemProps} props - Props for TreeView.Item.
|
|
10413
|
+
* @param {React.Ref<HTMLLIElement>} ref - Forwarded ref to the `<li>`.
|
|
10414
|
+
* @returns {JSX.Element | null} The rendered item, or nothing under a closed one.
|
|
10415
|
+
*/
|
|
10416
|
+
const TreeViewItem = React.forwardRef(function TreeViewItem({ value, label, icon, children, hasChildren = false, disabled = false, className, onClick, onKeyDown, onFocus, onBlur, ...props }, ref) {
|
|
10417
|
+
const tree = React.useContext(TreeViewContext);
|
|
10418
|
+
const branch = React.useContext(BranchContext);
|
|
10419
|
+
const labelId = React.useId();
|
|
10420
|
+
const hasItems = React.Children.toArray(children).length > 0;
|
|
10421
|
+
const isParent = hasItems || hasChildren;
|
|
10422
|
+
const open = isParent && !!tree?.isOpen(value);
|
|
10423
|
+
const parent = branch.path[branch.path.length - 1] ?? null;
|
|
10424
|
+
const itemClass = usePrefixedClassNames('tree-view-item');
|
|
10425
|
+
const groupClass = usePrefixedClassNames('tree-view-group');
|
|
10426
|
+
const toggleClass = usePrefixedClassNames('tree-view-toggle');
|
|
10427
|
+
const checkClass = usePrefixedClassNames('tree-view-check');
|
|
10428
|
+
const iconClass = usePrefixedClassNames('tree-view-icon');
|
|
10429
|
+
const labelClass = usePrefixedClassNames('tree-view-label');
|
|
10430
|
+
const register = tree?.register;
|
|
10431
|
+
useClientLayoutEffect(() => {
|
|
10432
|
+
if (!register) {
|
|
10433
|
+
warnOnce('TreeView.Item:outside', '[bestax-bulma] <TreeView.Item> is outside a <TreeView>, so it ' +
|
|
10434
|
+
'renders nothing. Put it inside one.');
|
|
10435
|
+
return undefined;
|
|
10436
|
+
}
|
|
10437
|
+
return register(value, { parent, disabled, isParent });
|
|
10438
|
+
}, [register, value, parent, disabled, isParent]);
|
|
10439
|
+
const setElement = tree?.setElement;
|
|
10440
|
+
const setRefs = React.useCallback((node) => {
|
|
10441
|
+
// Only an item inside a tree renders an element to hand over.
|
|
10442
|
+
setElement(value, node);
|
|
10443
|
+
if (typeof ref === 'function')
|
|
10444
|
+
ref(node);
|
|
10445
|
+
else if (ref)
|
|
10446
|
+
ref.current = node;
|
|
10447
|
+
}, [setElement, value, ref]);
|
|
10448
|
+
const rowRef = React.useRef(null);
|
|
10449
|
+
const toggleRef = React.useRef(null);
|
|
10450
|
+
const childBranch = React.useMemo(() => ({
|
|
10451
|
+
path: [...branch.path, value],
|
|
10452
|
+
hidden: branch.hidden || !open,
|
|
10453
|
+
}), [branch, value, open]);
|
|
10454
|
+
const { selected, checked } = tree?.stateOf(value, branch.path, disabled) ?? {};
|
|
10455
|
+
const rowClass = usePrefixedClassNames('tree-view-row', {
|
|
10456
|
+
'is-selected': selected,
|
|
10457
|
+
'is-disabled': disabled,
|
|
10035
10458
|
});
|
|
10036
|
-
|
|
10037
|
-
|
|
10038
|
-
|
|
10039
|
-
|
|
10040
|
-
|
|
10041
|
-
const
|
|
10042
|
-
|
|
10043
|
-
|
|
10044
|
-
|
|
10045
|
-
|
|
10046
|
-
|
|
10047
|
-
|
|
10048
|
-
|
|
10049
|
-
|
|
10050
|
-
|
|
10051
|
-
|
|
10052
|
-
|
|
10053
|
-
|
|
10054
|
-
|
|
10055
|
-
|
|
10056
|
-
|
|
10057
|
-
|
|
10058
|
-
|
|
10059
|
-
//
|
|
10060
|
-
|
|
10061
|
-
|
|
10062
|
-
|
|
10063
|
-
|
|
10064
|
-
|
|
10065
|
-
|
|
10066
|
-
|
|
10067
|
-
|
|
10068
|
-
|
|
10069
|
-
}
|
|
10070
|
-
|
|
10071
|
-
|
|
10072
|
-
|
|
10073
|
-
|
|
10074
|
-
|
|
10075
|
-
}
|
|
10459
|
+
if (!tree)
|
|
10460
|
+
return null;
|
|
10461
|
+
// While it's open the children render in its group. In a checkbox tree
|
|
10462
|
+
// they render while it's closed too, drawing nothing, so a check knows
|
|
10463
|
+
// what's under it.
|
|
10464
|
+
const items = (open || tree.rendersClosed) && (jsxRuntime.jsx(BranchContext.Provider, { value: childBranch, children: children }));
|
|
10465
|
+
if (branch.hidden)
|
|
10466
|
+
return jsxRuntime.jsx(jsxRuntime.Fragment, { children: items });
|
|
10467
|
+
const info = { value, parent, disabled, isParent, open };
|
|
10468
|
+
return (jsxRuntime.jsxs("li", { ...props, ref: setRefs, className: classNames(itemClass, className), role: "treeitem", "aria-labelledby": labelId, "aria-level": branch.path.length + 1, "aria-expanded": isParent ? open : undefined, "aria-disabled": disabled || undefined, "aria-selected": selected, "aria-checked": checked, tabIndex: tree.tabStop === value ? 0 : -1, onKeyDown: event => {
|
|
10469
|
+
onKeyDown?.(event);
|
|
10470
|
+
tree.onKeyDown(event, info);
|
|
10471
|
+
}, onFocus: event => {
|
|
10472
|
+
onFocus?.(event);
|
|
10473
|
+
if (event.target === event.currentTarget) {
|
|
10474
|
+
tree.onFocus(value, event.currentTarget);
|
|
10475
|
+
}
|
|
10476
|
+
}, onBlur: event => {
|
|
10477
|
+
onBlur?.(event);
|
|
10478
|
+
if (event.target === event.currentTarget)
|
|
10479
|
+
tree.onBlur();
|
|
10480
|
+
}, onClick: event => {
|
|
10481
|
+
onClick?.(event);
|
|
10482
|
+
// A click in its row is its own, and so is one on the item itself,
|
|
10483
|
+
// as a test's click on the treeitem is. One in its group belongs
|
|
10484
|
+
// to the item inside it, or to nothing. The chevron's box is on
|
|
10485
|
+
// every row, but it's a chevron only on a parent: on a leaf it's
|
|
10486
|
+
// more of the row.
|
|
10487
|
+
const target = event.target;
|
|
10488
|
+
if (target === event.currentTarget ||
|
|
10489
|
+
rowRef.current.contains(target)) {
|
|
10490
|
+
tree.onClick(event.currentTarget, info, isParent && toggleRef.current.contains(target));
|
|
10491
|
+
}
|
|
10492
|
+
}, children: [jsxRuntime.jsxs("div", { className: rowClass, ref: rowRef, children: [jsxRuntime.jsx("span", { className: toggleClass, "aria-hidden": "true", ref: toggleRef }), checked !== undefined && (jsxRuntime.jsx("span", { className: checkClass, "aria-hidden": "true", onClick: event => {
|
|
10493
|
+
// A click on the box's label is followed by one on its
|
|
10494
|
+
// input, and both reach the item. The label's is cancelled,
|
|
10495
|
+
// so the item toggles once.
|
|
10496
|
+
if (!(event.target instanceof HTMLInputElement)) {
|
|
10497
|
+
event.preventDefault();
|
|
10498
|
+
}
|
|
10499
|
+
}, children: jsxRuntime.jsx(Checkbox, { tabIndex: -1, checked: checked === true, indeterminate: checked === 'mixed', disabled: disabled, onChange: ignoreChange }) })), isGiven(icon) && (jsxRuntime.jsx("span", { className: iconClass, "aria-hidden": "true", children: typeof icon === 'string' ? jsxRuntime.jsx(Icon, { name: icon }) : icon })), jsxRuntime.jsx("span", { className: labelClass, id: labelId, children: label })] }), open && hasItems ? (jsxRuntime.jsx("ul", { className: groupClass, role: "group", children: items })) : (items)] }));
|
|
10500
|
+
});
|
|
10501
|
+
TreeViewItem.displayName = 'TreeView.Item';
|
|
10502
|
+
const TreeView = withSubComponents(TreeViewRoot, { Item: TreeViewItem }, 'TreeView');
|
|
10503
|
+
|
|
10076
10504
|
/**
|
|
10077
|
-
*
|
|
10505
|
+
* The `Block` component renders a simple container with Bulma's `.block` class, adding vertical margin between sections of content.
|
|
10078
10506
|
*
|
|
10079
|
-
* @
|
|
10080
|
-
*
|
|
10081
|
-
*
|
|
10082
|
-
|
|
10083
|
-
|
|
10084
|
-
|
|
10085
|
-
* Show a notification with the given options.
|
|
10086
|
-
* @param {NotificationOptions} options - Notification configuration.
|
|
10087
|
-
* @returns {string} The unique ID of the created notification.
|
|
10088
|
-
*/
|
|
10089
|
-
show: (options) => {
|
|
10090
|
-
const id = `notification-${++notificationId}`;
|
|
10091
|
-
const instance = { id, options };
|
|
10092
|
-
if (options.queue) {
|
|
10093
|
-
queuedNotifications.push(instance);
|
|
10094
|
-
processQueuedNotification();
|
|
10095
|
-
}
|
|
10096
|
-
else {
|
|
10097
|
-
notifications.push(instance);
|
|
10098
|
-
notifyNotificationListeners();
|
|
10099
|
-
}
|
|
10100
|
-
return id;
|
|
10101
|
-
},
|
|
10102
|
-
/**
|
|
10103
|
-
* Show a success notification.
|
|
10104
|
-
* @param {string|React.ReactNode} message - The message to display.
|
|
10105
|
-
* @param {Partial<NotificationOptions>} [options] - Additional options.
|
|
10106
|
-
* @returns {string} The notification ID.
|
|
10107
|
-
*/
|
|
10108
|
-
success: (message, options) => {
|
|
10109
|
-
return notification.show({ message, color: 'success', ...options });
|
|
10110
|
-
},
|
|
10111
|
-
/**
|
|
10112
|
-
* Show a danger notification.
|
|
10113
|
-
* @param {string|React.ReactNode} message - The message to display.
|
|
10114
|
-
* @param {Partial<NotificationOptions>} [options] - Additional options.
|
|
10115
|
-
* @returns {string} The notification ID.
|
|
10116
|
-
*/
|
|
10117
|
-
danger: (message, options) => {
|
|
10118
|
-
return notification.show({ message, color: 'danger', ...options });
|
|
10119
|
-
},
|
|
10120
|
-
/**
|
|
10121
|
-
* Show a warning notification.
|
|
10122
|
-
* @param {string|React.ReactNode} message - The message to display.
|
|
10123
|
-
* @param {Partial<NotificationOptions>} [options] - Additional options.
|
|
10124
|
-
* @returns {string} The notification ID.
|
|
10125
|
-
*/
|
|
10126
|
-
warning: (message, options) => {
|
|
10127
|
-
return notification.show({ message, color: 'warning', ...options });
|
|
10128
|
-
},
|
|
10129
|
-
/**
|
|
10130
|
-
* Show an info notification.
|
|
10131
|
-
* @param {string|React.ReactNode} message - The message to display.
|
|
10132
|
-
* @param {Partial<NotificationOptions>} [options] - Additional options.
|
|
10133
|
-
* @returns {string} The notification ID.
|
|
10134
|
-
*/
|
|
10135
|
-
info: (message, options) => {
|
|
10136
|
-
return notification.show({ message, color: 'info', ...options });
|
|
10137
|
-
},
|
|
10507
|
+
* @function
|
|
10508
|
+
* @param {BlockProps} props - Props for the Block component.
|
|
10509
|
+
* @returns {JSX.Element} The rendered block element.
|
|
10510
|
+
* @see {@link https://bulma.io/documentation/elements/block/ | Bulma Block documentation}
|
|
10511
|
+
*/
|
|
10512
|
+
const Block = ({ className, textColor, color, bgColor, children, ...props }) => {
|
|
10138
10513
|
/**
|
|
10139
|
-
*
|
|
10140
|
-
* @param {string} id - The notification ID to close.
|
|
10514
|
+
* Generates Bulma helper classes and separates out remaining props.
|
|
10141
10515
|
*/
|
|
10142
|
-
|
|
10143
|
-
|
|
10144
|
-
|
|
10145
|
-
|
|
10146
|
-
|
|
10147
|
-
|
|
10148
|
-
|
|
10149
|
-
|
|
10150
|
-
|
|
10151
|
-
|
|
10152
|
-
|
|
10153
|
-
|
|
10154
|
-
|
|
10155
|
-
|
|
10156
|
-
|
|
10157
|
-
|
|
10158
|
-
|
|
10159
|
-
|
|
10516
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
10517
|
+
color: textColor ?? color,
|
|
10518
|
+
backgroundColor: bgColor,
|
|
10519
|
+
...props,
|
|
10520
|
+
});
|
|
10521
|
+
const bulmaClasses = usePrefixedClassNames('block');
|
|
10522
|
+
const blockClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
|
|
10523
|
+
return (jsxRuntime.jsx("div", { className: blockClasses, ...rest, children: children }));
|
|
10524
|
+
};
|
|
10525
|
+
|
|
10526
|
+
/**
|
|
10527
|
+
* The `Box` component renders a bordered, padded container with an optional shadow using Bulma's `.box` class.
|
|
10528
|
+
*
|
|
10529
|
+
* @function
|
|
10530
|
+
* @param {BoxProps} props - Props for the Box component.
|
|
10531
|
+
* @returns {JSX.Element} The rendered box element.
|
|
10532
|
+
* @see {@link https://bulma.io/documentation/elements/box/ | Bulma Box documentation}
|
|
10533
|
+
*/
|
|
10534
|
+
const Box = ({ className, textColor, color, bgColor, hasShadow = true, children, style, ...props }) => {
|
|
10160
10535
|
/**
|
|
10161
|
-
*
|
|
10162
|
-
* @param {(items: NotificationInstance[]) => void} listener - Callback invoked on changes.
|
|
10163
|
-
* @returns {() => void} Unsubscribe function.
|
|
10536
|
+
* Generates Bulma helper classes and separates out remaining props.
|
|
10164
10537
|
*/
|
|
10165
|
-
|
|
10166
|
-
|
|
10167
|
-
|
|
10168
|
-
|
|
10538
|
+
const { bulmaHelperClasses, bulmaHelperStyles, rest } = useBulmaClasses({
|
|
10539
|
+
color: textColor ?? color,
|
|
10540
|
+
backgroundColor: bgColor,
|
|
10541
|
+
...props,
|
|
10542
|
+
});
|
|
10543
|
+
const bulmaClasses = usePrefixedClassNames('box', {
|
|
10544
|
+
'is-shadowless': !hasShadow,
|
|
10545
|
+
});
|
|
10546
|
+
const boxClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
|
|
10547
|
+
return (jsxRuntime.jsx("div", { className: boxClasses, style: mergeBulmaStyles(bulmaHelperStyles, style), ...rest, children: children }));
|
|
10169
10548
|
};
|
|
10549
|
+
|
|
10170
10550
|
/**
|
|
10171
|
-
*
|
|
10172
|
-
* than politely through NotificationContainer's status region.
|
|
10551
|
+
* The `Code` component renders a styled `<code>` element with Bulma helper class integration.
|
|
10173
10552
|
*
|
|
10174
|
-
* @function
|
|
10175
|
-
* @param {
|
|
10176
|
-
* @returns {
|
|
10553
|
+
* @function
|
|
10554
|
+
* @param {CodeProps} props - Props for the Code component.
|
|
10555
|
+
* @returns {JSX.Element} The rendered code element.
|
|
10177
10556
|
*/
|
|
10178
|
-
const
|
|
10557
|
+
const Code = ({ className, textColor, bgColor, children, ...props }) => {
|
|
10558
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
10559
|
+
color: textColor,
|
|
10560
|
+
backgroundColor: bgColor,
|
|
10561
|
+
...props,
|
|
10562
|
+
});
|
|
10563
|
+
const bulmaClasses = usePrefixedClassNames();
|
|
10564
|
+
const codeClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
|
|
10565
|
+
return (jsxRuntime.jsx("code", { className: codeClasses || undefined, ...rest, children: children }));
|
|
10566
|
+
};
|
|
10567
|
+
|
|
10568
|
+
// Valid size modifiers for the content class
|
|
10569
|
+
const validSizes = ['small', 'medium', 'large'];
|
|
10179
10570
|
/**
|
|
10180
|
-
*
|
|
10571
|
+
* The `Content` component applies Bulma’s typographic styles to its children, enhancing the appearance of HTML elements like paragraphs, headings, lists, and tables.
|
|
10181
10572
|
*
|
|
10182
10573
|
* @function
|
|
10183
|
-
* @param {
|
|
10184
|
-
* @returns {JSX.Element} The rendered
|
|
10574
|
+
* @param {ContentProps} props - Props for the Content component.
|
|
10575
|
+
* @returns {JSX.Element} The rendered content block.
|
|
10576
|
+
* @see {@link https://bulma.io/documentation/elements/content/ | Bulma Content documentation}
|
|
10185
10577
|
*/
|
|
10186
|
-
const
|
|
10187
|
-
|
|
10188
|
-
|
|
10189
|
-
|
|
10190
|
-
const
|
|
10191
|
-
|
|
10192
|
-
|
|
10193
|
-
|
|
10194
|
-
|
|
10195
|
-
|
|
10196
|
-
|
|
10197
|
-
|
|
10198
|
-
|
|
10199
|
-
|
|
10200
|
-
const timer = setTimeout(handleClose, duration);
|
|
10201
|
-
return () => clearTimeout(timer);
|
|
10202
|
-
}, [duration, indefinite, isPaused, handleClose]);
|
|
10203
|
-
const handleMouseEnter = React.useCallback(() => {
|
|
10204
|
-
if (pauseOnHover)
|
|
10205
|
-
setIsPaused(true);
|
|
10206
|
-
}, [pauseOnHover]);
|
|
10207
|
-
const handleMouseLeave = React.useCallback(() => {
|
|
10208
|
-
if (pauseOnHover)
|
|
10209
|
-
setIsPaused(false);
|
|
10210
|
-
}, [pauseOnHover]);
|
|
10211
|
-
return (jsxRuntime.jsx(Notification, { color: color, isLight: isLight, hasDelete: hasDelete, onDelete: handleClose, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, style: { pointerEvents: 'auto' }, children: urgent ? (jsxRuntime.jsx("span", { role: "alert", "aria-live": "assertive", children: message })) : (jsxRuntime.jsx("span", { ref: messageRef, children: message })) }));
|
|
10578
|
+
const Content = ({ className, textColor, color, bgColor, size, children, ...props }) => {
|
|
10579
|
+
/**
|
|
10580
|
+
* Generates Bulma helper classes and separates out remaining props.
|
|
10581
|
+
*/
|
|
10582
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
10583
|
+
color: textColor ?? color,
|
|
10584
|
+
backgroundColor: bgColor,
|
|
10585
|
+
...props,
|
|
10586
|
+
});
|
|
10587
|
+
const bulmaClasses = usePrefixedClassNames('content', {
|
|
10588
|
+
[`is-${size}`]: size && size !== 'normal' && validSizes.includes(size),
|
|
10589
|
+
});
|
|
10590
|
+
const contentClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
|
|
10591
|
+
return (jsxRuntime.jsx("div", { className: contentClasses, ...rest, children: children }));
|
|
10212
10592
|
};
|
|
10213
|
-
|
|
10214
|
-
// screen, then across the bottom.
|
|
10215
|
-
const notificationStackOrder = [
|
|
10216
|
-
'top-left',
|
|
10217
|
-
'top',
|
|
10218
|
-
'top-right',
|
|
10219
|
-
'bottom-left',
|
|
10220
|
-
'bottom',
|
|
10221
|
-
'bottom-right',
|
|
10222
|
-
];
|
|
10593
|
+
|
|
10223
10594
|
/**
|
|
10224
|
-
*
|
|
10595
|
+
* The `Delete` component provides a Bulma-styled close/delete button for dismissing modals, notifications, tags, messages, and more.
|
|
10596
|
+
*
|
|
10597
|
+
* It renders `type="button"`, so a delete button inside a form does not submit it.
|
|
10225
10598
|
*
|
|
10226
10599
|
* @function
|
|
10227
|
-
* @param {
|
|
10228
|
-
* @returns {
|
|
10600
|
+
* @param {DeleteProps} props - Props for the Delete component.
|
|
10601
|
+
* @returns {JSX.Element} The rendered delete button.
|
|
10602
|
+
* @see {@link https://bulma.io/documentation/elements/delete/ | Bulma Delete documentation}
|
|
10229
10603
|
*/
|
|
10230
|
-
const
|
|
10231
|
-
|
|
10232
|
-
|
|
10233
|
-
|
|
10234
|
-
|
|
10235
|
-
|
|
10236
|
-
|
|
10237
|
-
|
|
10238
|
-
|
|
10239
|
-
|
|
10240
|
-
|
|
10241
|
-
|
|
10242
|
-
|
|
10243
|
-
|
|
10244
|
-
|
|
10245
|
-
? { left: '50%', transform: 'translateX(-50%)', alignItems: 'center' }
|
|
10246
|
-
: isRight
|
|
10247
|
-
? { right: 0, alignItems: 'flex-end' }
|
|
10248
|
-
: { left: 0, alignItems: 'flex-start' }),
|
|
10249
|
-
};
|
|
10604
|
+
const Delete = ({ className, textColor, color, bgColor, onClick, size, ariaLabel, disabled = false, ...props }) => {
|
|
10605
|
+
/**
|
|
10606
|
+
* Generates Bulma helper classes and separates out remaining props.
|
|
10607
|
+
*/
|
|
10608
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
10609
|
+
color: textColor ?? color,
|
|
10610
|
+
backgroundColor: bgColor,
|
|
10611
|
+
...props,
|
|
10612
|
+
});
|
|
10613
|
+
const bulmaClasses = usePrefixedClassNames('delete', {
|
|
10614
|
+
[`is-${size}`]: size,
|
|
10615
|
+
'is-disabled': disabled,
|
|
10616
|
+
});
|
|
10617
|
+
const classes = classNames(bulmaClasses, bulmaHelperClasses, className);
|
|
10618
|
+
return (jsxRuntime.jsx("button", { className: classes, onClick: onClick, disabled: disabled, ...rest, "aria-label": rest['aria-label'] || ariaLabel || 'Close', type: buttonType(rest.type) }));
|
|
10250
10619
|
};
|
|
10620
|
+
|
|
10251
10621
|
/**
|
|
10252
|
-
*
|
|
10253
|
-
* Place once at your app root to enable the notification API. A notification
|
|
10254
|
-
* shown with a `position` appears there, and one shown without goes to the
|
|
10255
|
-
* container's `position`, so the container renders a stack for each position
|
|
10256
|
-
* in use.
|
|
10257
|
-
*
|
|
10258
|
-
* It keeps a visually hidden `role="status"` live region in the page from the
|
|
10259
|
-
* moment it mounts, even while nothing is showing, and announces every
|
|
10260
|
-
* notification other than `danger` and `warning` through it, a moment after
|
|
10261
|
-
* the notification appears. Those notifications carry no `role="status"` of
|
|
10262
|
-
* their own, so `getByRole('status')` finds the region, not the notification.
|
|
10263
|
-
* The text stays in the region briefly, even if the notification closes in
|
|
10264
|
-
* the meantime, and while both are up the page holds two copies of it. A
|
|
10265
|
-
* polite notification that closes before its announcement is written, a
|
|
10266
|
-
* moment after it appears, isn't announced at all. `danger` and `warning`
|
|
10267
|
-
* notifications announce themselves as assertive alerts. The region is hidden
|
|
10268
|
-
* with inline styles, so it needs no stylesheet.
|
|
10622
|
+
* The `Divider` component renders a styled horizontal rule (`<hr>`) element with Bulma helper class integration.
|
|
10269
10623
|
*
|
|
10270
10624
|
* @function
|
|
10271
|
-
* @param {
|
|
10272
|
-
* @returns {JSX.Element
|
|
10625
|
+
* @param {DividerProps} props - Props for the Divider component.
|
|
10626
|
+
* @returns {JSX.Element} The rendered hr element.
|
|
10273
10627
|
*/
|
|
10274
|
-
const
|
|
10275
|
-
|
|
10276
|
-
|
|
10277
|
-
|
|
10278
|
-
|
|
10279
|
-
|
|
10280
|
-
|
|
10281
|
-
|
|
10282
|
-
const
|
|
10283
|
-
|
|
10284
|
-
|
|
10285
|
-
const politeItems = React.useMemo(() => items.filter(item => !isUrgentColor(item.options.color)), [items]);
|
|
10286
|
-
// Each polite notification's message element, so the text the region
|
|
10287
|
-
// announces is the text on screen, whatever the message renders.
|
|
10288
|
-
const messageNodesRef = React.useRef(new Map());
|
|
10289
|
-
const registerMessage = React.useCallback((id, node) => {
|
|
10290
|
-
if (node) {
|
|
10291
|
-
messageNodesRef.current.set(id, node);
|
|
10292
|
-
}
|
|
10293
|
-
else {
|
|
10294
|
-
messageNodesRef.current.delete(id);
|
|
10295
|
-
}
|
|
10296
|
-
}, []);
|
|
10297
|
-
// StatusRegion only describes polite notifications that are on screen, and
|
|
10298
|
-
// each of those registered its message element when it mounted. The
|
|
10299
|
-
// fallback is for a notification announced without being rendered, should
|
|
10300
|
-
// the container ever render fewer than it announces: a plain-text message
|
|
10301
|
-
// still says itself, and anything else is skipped.
|
|
10302
|
-
const describe = React.useCallback((item) => {
|
|
10303
|
-
const node = messageNodesRef.current.get(item.id);
|
|
10304
|
-
/* istanbul ignore if: every polite notification on screen registers its message element when it mounts */
|
|
10305
|
-
if (!node) {
|
|
10306
|
-
const { message } = item.options;
|
|
10307
|
-
return typeof message === 'string' ? message : null;
|
|
10308
|
-
}
|
|
10309
|
-
return spokenText(node);
|
|
10310
|
-
}, []);
|
|
10311
|
-
if (typeof document === 'undefined' || !isHydrated) {
|
|
10312
|
-
return null;
|
|
10313
|
-
}
|
|
10314
|
-
const stacks = groupIntoPositionStacks(items, item => item.options.position, position, notificationStackOrder);
|
|
10315
|
-
return reactDom.createPortal(jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [stacks.map(stack => (jsxRuntime.jsx("div", { style: notificationStackStyle(stack.position), children: stack.items.map(item => (jsxRuntime.jsx(NotificationItem, { instance: item, onClose: notification.close, registerMessage: registerMessage }, item.id))) }, stack.key))), jsxRuntime.jsx(StatusRegion, { items: politeItems, describe: describe })] }), resolveContainer());
|
|
10628
|
+
const Divider = ({ className, bgColor, ...props }) => {
|
|
10629
|
+
/**
|
|
10630
|
+
* Generates Bulma helper classes and separates out remaining props.
|
|
10631
|
+
*/
|
|
10632
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
10633
|
+
backgroundColor: bgColor,
|
|
10634
|
+
...props,
|
|
10635
|
+
});
|
|
10636
|
+
const bulmaClasses = usePrefixedClassNames();
|
|
10637
|
+
const dividerClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
|
|
10638
|
+
return jsxRuntime.jsx("hr", { className: dividerClasses || undefined, ...rest });
|
|
10316
10639
|
};
|
|
10317
10640
|
|
|
10318
10641
|
/**
|
|
10319
|
-
* The `
|
|
10642
|
+
* The `Emphasis` component renders a styled `<em>` element with Bulma helper class integration.
|
|
10320
10643
|
*
|
|
10321
10644
|
* @function
|
|
10322
|
-
* @param {
|
|
10323
|
-
* @returns {JSX.Element} The rendered
|
|
10645
|
+
* @param {EmphasisProps} props - Props for the Emphasis component.
|
|
10646
|
+
* @returns {JSX.Element} The rendered em element.
|
|
10324
10647
|
*/
|
|
10325
|
-
const
|
|
10326
|
-
/**
|
|
10327
|
-
* Generates Bulma helper classes and separates out remaining props.
|
|
10328
|
-
*/
|
|
10648
|
+
const Emphasis = ({ className, textColor, bgColor, children, ...props }) => {
|
|
10329
10649
|
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
10330
10650
|
color: textColor,
|
|
10331
10651
|
backgroundColor: bgColor,
|
|
10332
10652
|
...props,
|
|
10333
10653
|
});
|
|
10334
10654
|
const bulmaClasses = usePrefixedClassNames();
|
|
10335
|
-
const
|
|
10336
|
-
return (jsxRuntime.jsx("
|
|
10655
|
+
const emphasisClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
|
|
10656
|
+
return (jsxRuntime.jsx("em", { className: emphasisClasses || undefined, ...rest, children: children }));
|
|
10337
10657
|
};
|
|
10338
|
-
const OrderedList = withSubComponents(OrderedListComponent, { Item: ListItem }, 'OrderedList');
|
|
10339
10658
|
|
|
10340
10659
|
/**
|
|
10341
|
-
*
|
|
10660
|
+
* FigureCaption component for rendering a styled figcaption element.
|
|
10661
|
+
*
|
|
10662
|
+
* A FigureCaption wraps the HTML `<figcaption>` element with Bulma helper class integration.
|
|
10663
|
+
* Use it inside Figure components to provide captions for images or other media.
|
|
10342
10664
|
*
|
|
10343
10665
|
* @function
|
|
10344
|
-
* @param {
|
|
10345
|
-
* @returns {JSX.Element} The rendered
|
|
10666
|
+
* @param {FigureCaptionProps} props - Props for the FigureCaption component.
|
|
10667
|
+
* @returns {JSX.Element} The rendered figcaption element.
|
|
10346
10668
|
*/
|
|
10347
|
-
const
|
|
10669
|
+
const FigureCaption = ({ className, textColor, bgColor, children, ...props }) => {
|
|
10348
10670
|
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
10349
10671
|
color: textColor,
|
|
10350
10672
|
backgroundColor: bgColor,
|
|
10351
10673
|
...props,
|
|
10352
10674
|
});
|
|
10353
10675
|
const bulmaClasses = usePrefixedClassNames();
|
|
10354
|
-
const
|
|
10355
|
-
return (jsxRuntime.jsx("
|
|
10676
|
+
const captionClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
|
|
10677
|
+
return (jsxRuntime.jsx("figcaption", { className: captionClasses || undefined, ...rest, children: children }));
|
|
10678
|
+
};
|
|
10679
|
+
/**
|
|
10680
|
+
* The `Figure` component renders a styled `<figure>` element with Bulma helper class integration.
|
|
10681
|
+
*
|
|
10682
|
+
* @function
|
|
10683
|
+
* @param {FigureProps} props - Props for the Figure component.
|
|
10684
|
+
* @returns {JSX.Element} The rendered figure element.
|
|
10685
|
+
*/
|
|
10686
|
+
const FigureComponent = ({ className, textColor, bgColor, children, ...props }) => {
|
|
10687
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
10688
|
+
color: textColor,
|
|
10689
|
+
backgroundColor: bgColor,
|
|
10690
|
+
...props,
|
|
10691
|
+
});
|
|
10692
|
+
const bulmaClasses = usePrefixedClassNames();
|
|
10693
|
+
const figureClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
|
|
10694
|
+
return (jsxRuntime.jsx("figure", { className: figureClasses || undefined, ...rest, children: children }));
|
|
10356
10695
|
};
|
|
10696
|
+
/**
|
|
10697
|
+
* Figure component with Caption subcomponent.
|
|
10698
|
+
*
|
|
10699
|
+
* @example
|
|
10700
|
+
* <Figure>
|
|
10701
|
+
* <img src="image.jpg" alt="Description" />
|
|
10702
|
+
* <Figure.Caption>Image caption text</Figure.Caption>
|
|
10703
|
+
* </Figure>
|
|
10704
|
+
*/
|
|
10705
|
+
const Figure = withSubComponents(FigureComponent, {
|
|
10706
|
+
Caption: FigureCaption,
|
|
10707
|
+
}, 'Figure');
|
|
10357
10708
|
|
|
10358
10709
|
/**
|
|
10359
|
-
*
|
|
10710
|
+
* Distinguishes an `IconProps` object from a plain custom node (an inline SVG, a
|
|
10711
|
+
* `react-icons` component, …) in a slot that accepts either — `Control`'s
|
|
10712
|
+
* `iconLeft`/`iconRight`, `IconText`'s `iconProps`.
|
|
10713
|
+
*
|
|
10714
|
+
* One copy, imported by both consumers: two hand-maintained copies, one per
|
|
10715
|
+
* consumer, is how the deprecated `icon` path came to crash both (#663).
|
|
10716
|
+
*
|
|
10717
|
+
* Its own module rather than `Icon.tsx` because `src/index.ts` wildcard-exports
|
|
10718
|
+
* that file, and everything published there is public forever. `polymorphic.ts`
|
|
10719
|
+
* makes the same call for `isCustomElement`: an internal discriminator is not
|
|
10720
|
+
* something to support for the life of the package.
|
|
10721
|
+
*
|
|
10722
|
+
* It tests each member's VALUE, not merely its key, and only after everything
|
|
10723
|
+
* React owns is out of the way. `'icon' in value` was enough
|
|
10724
|
+
* to claim a Font Awesome `IconDefinition` — `{ prefix, iconName, icon: [w, h, …,
|
|
10725
|
+
* path] }` — whose `icon` is a path array rather than a class string. That object
|
|
10726
|
+
* is not a renderable node either, so the honest outcome is the one this slot has
|
|
10727
|
+
* always had for it: the node branch, and React's own "Objects are not valid as a
|
|
10728
|
+
* React child". Claiming it here instead rendered a blank container that still
|
|
10729
|
+
* reserved layout.
|
|
10730
|
+
*
|
|
10731
|
+
* A member added to `IconProps` needs a line here, and a missed one is a crash
|
|
10732
|
+
* rather than a fallthrough. That is the cost of a slot taking either a props
|
|
10733
|
+
* object or a node; keeping the guard in one place is what keeps the cost to a
|
|
10734
|
+
* single line.
|
|
10735
|
+
*/
|
|
10736
|
+
function isIconProps(value) {
|
|
10737
|
+
if (typeof value !== 'object' || value === null)
|
|
10738
|
+
return false;
|
|
10739
|
+
// Anything React owns. `isValidElement` covers elements only; a PORTAL is a
|
|
10740
|
+
// valid `ReactNode` that fails it and carries a `children` key, so the member
|
|
10741
|
+
// test below claimed it and `Control` spread it into `<Icon>` as props —
|
|
10742
|
+
// rendering the portal's child locally instead of in its container. Every
|
|
10743
|
+
// React internal carries `$$typeof`, so testing for it covers lazy, context
|
|
10744
|
+
// and whatever React adds next, rather than portals alone.
|
|
10745
|
+
if (React.isValidElement(value) || '$$typeof' in value)
|
|
10746
|
+
return false;
|
|
10747
|
+
const candidate = value;
|
|
10748
|
+
return (typeof candidate.name === 'string' ||
|
|
10749
|
+
typeof candidate.icon === 'string' ||
|
|
10750
|
+
'children' in candidate);
|
|
10751
|
+
}
|
|
10752
|
+
|
|
10753
|
+
/**
|
|
10754
|
+
* The `IconText` component provides a Bulma-styled horizontal arrangement of one or more `Icon` components and optional text.
|
|
10360
10755
|
*
|
|
10361
10756
|
* @function
|
|
10362
|
-
* @param {
|
|
10363
|
-
* @returns {JSX.Element} The rendered
|
|
10364
|
-
* @see {@link https://bulma.io/documentation/elements/
|
|
10757
|
+
* @param {IconTextProps} props - Props for the IconText component.
|
|
10758
|
+
* @returns {JSX.Element} The rendered icon text element.
|
|
10759
|
+
* @see {@link https://bulma.io/documentation/elements/icon/#icon-text | Bulma IconText documentation}
|
|
10365
10760
|
*/
|
|
10366
|
-
const
|
|
10367
|
-
warnUnstyledColor('Progress', color);
|
|
10761
|
+
const IconTextComponent = ({ className, textColor, color, bgColor, iconProps, children, items, ...props }) => {
|
|
10368
10762
|
/**
|
|
10369
10763
|
* Generates Bulma helper classes and separates out remaining props.
|
|
10370
10764
|
*/
|
|
10371
10765
|
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
10766
|
+
color: textColor ?? color,
|
|
10767
|
+
backgroundColor: bgColor,
|
|
10372
10768
|
...props,
|
|
10373
10769
|
});
|
|
10374
|
-
const bulmaClasses = usePrefixedClassNames('
|
|
10375
|
-
|
|
10770
|
+
const bulmaClasses = usePrefixedClassNames('icon-text');
|
|
10771
|
+
const iconTextClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
|
|
10772
|
+
return (jsxRuntime.jsx("span", { className: iconTextClasses, ...rest, children: items ? (items.map((item, index) => (jsxRuntime.jsxs(React.Fragment, { children: [item.iconProps &&
|
|
10773
|
+
(isIconProps(item.iconProps) ? (jsxRuntime.jsx(Icon, { ...item.iconProps })) : (jsxRuntime.jsx(Icon, { children: item.iconProps }))), item.text && jsxRuntime.jsx("span", { children: item.text })] }, index)))) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [iconProps &&
|
|
10774
|
+
(isIconProps(iconProps) ? (jsxRuntime.jsx(Icon, { ...iconProps })) : (jsxRuntime.jsx(Icon, { children: iconProps }))), children && jsxRuntime.jsx("span", { children: children })] })) }));
|
|
10775
|
+
};
|
|
10776
|
+
const IconText = withSubComponents(IconTextComponent, { Icon }, 'IconText');
|
|
10777
|
+
|
|
10778
|
+
/**
|
|
10779
|
+
* The `Image` component wraps images, iframes, or custom content in a Bulma-styled container, supporting fixed sizes, aspect ratios, rounded corners, retina images, and all Bulma helper props for color and spacing.
|
|
10780
|
+
*
|
|
10781
|
+
* @function
|
|
10782
|
+
* @param {ImageProps} props - Props for the Image component.
|
|
10783
|
+
* @returns {JSX.Element} The rendered image element.
|
|
10784
|
+
* @see {@link https://bulma.io/documentation/elements/image/ | Bulma Image documentation}
|
|
10785
|
+
*/
|
|
10786
|
+
const Image = ({ as, className, textColor, color, bgColor, size, isRounded, isRetina, src, alt, children, ...props }) => {
|
|
10787
|
+
/**
|
|
10788
|
+
* Generates Bulma helper classes and separates out remaining props.
|
|
10789
|
+
*/
|
|
10790
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
10791
|
+
color: textColor ?? color,
|
|
10792
|
+
backgroundColor: bgColor,
|
|
10793
|
+
...props,
|
|
10794
|
+
});
|
|
10795
|
+
const bulmaClasses = usePrefixedClassNames('image', {
|
|
10376
10796
|
[`is-${size}`]: size,
|
|
10797
|
+
'has-ratio': size && typeof size === 'string' && size.includes('by'),
|
|
10377
10798
|
});
|
|
10378
|
-
const
|
|
10379
|
-
|
|
10799
|
+
const imageClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
|
|
10800
|
+
// Default tag logic: if "as" is provided, use it.
|
|
10801
|
+
// If not, use <figure> for aspect ratios or children, <div> otherwise.
|
|
10802
|
+
let Tag;
|
|
10803
|
+
if (as) {
|
|
10804
|
+
Tag = as;
|
|
10805
|
+
}
|
|
10806
|
+
else if (size && typeof size === 'string' && size.includes('by')) {
|
|
10807
|
+
Tag = 'figure';
|
|
10808
|
+
}
|
|
10809
|
+
else {
|
|
10810
|
+
Tag = 'div';
|
|
10811
|
+
}
|
|
10812
|
+
const roundedClass = usePrefixedClassNames('is-rounded');
|
|
10813
|
+
const content = children ? (children) : (jsxRuntime.jsx("img", { className: classNames({ [roundedClass]: isRounded }) || undefined, src: src, alt: alt, ...(isRetina && src ? { srcSet: `${src} 2x` } : {}) }));
|
|
10814
|
+
return (jsxRuntime.jsx(Tag, { className: imageClasses, ...rest, children: content }));
|
|
10380
10815
|
};
|
|
10381
10816
|
|
|
10382
10817
|
/**
|
|
10383
|
-
*
|
|
10818
|
+
* Keys pressed together or one after another, in the outer `<kbd>` HTML gives a combination, with the separator between each pair as text.
|
|
10819
|
+
*
|
|
10820
|
+
* It doesn't wrap in the middle and isn't drawn as a key itself. It sets no
|
|
10821
|
+
* font size, so the keys in it are the size of a key on its own.
|
|
10384
10822
|
*
|
|
10385
10823
|
* @function
|
|
10386
|
-
* @param {
|
|
10387
|
-
* @
|
|
10824
|
+
* @param {KbdGroupProps} props - Props for Kbd.Group.
|
|
10825
|
+
* @param {React.Ref<HTMLElement>} ref - Forwarded ref to the outer `<kbd>`.
|
|
10826
|
+
* @returns {JSX.Element} The rendered combination.
|
|
10388
10827
|
*
|
|
10389
10828
|
* @example
|
|
10390
|
-
*
|
|
10391
|
-
*
|
|
10829
|
+
* <Kbd.Group>
|
|
10830
|
+
* <Kbd>Ctrl</Kbd>
|
|
10831
|
+
* <Kbd>Shift</Kbd>
|
|
10832
|
+
* <Kbd>P</Kbd>
|
|
10833
|
+
* </Kbd.Group>
|
|
10392
10834
|
*
|
|
10393
10835
|
* @example
|
|
10394
|
-
*
|
|
10395
|
-
*
|
|
10836
|
+
* <Kbd.Group separator="then">
|
|
10837
|
+
* <Kbd>G</Kbd>
|
|
10838
|
+
* <Kbd>I</Kbd>
|
|
10839
|
+
* </Kbd.Group>
|
|
10396
10840
|
*/
|
|
10397
|
-
const
|
|
10398
|
-
const
|
|
10399
|
-
|
|
10400
|
-
|
|
10401
|
-
|
|
10402
|
-
|
|
10403
|
-
|
|
10404
|
-
|
|
10405
|
-
|
|
10841
|
+
const KbdGroup = React.forwardRef(({ className, textColor, separator = '+', children, ...props }, ref) => {
|
|
10842
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
10843
|
+
color: textColor,
|
|
10844
|
+
...props,
|
|
10845
|
+
});
|
|
10846
|
+
const groupClass = usePrefixedClassNames('kbd-group');
|
|
10847
|
+
const separatorClass = usePrefixedClassNames('kbd-separator');
|
|
10848
|
+
// Null, undefined and booleans drop out, and Fragments are opened up, so
|
|
10849
|
+
// an empty Fragment leaves no separator behind and the keys in one are
|
|
10850
|
+
// separated like the rest. The spaces are part of the text, so a screen
|
|
10851
|
+
// reader reads "Ctrl plus S" rather than one run-together word, whatever
|
|
10852
|
+
// the CSS does.
|
|
10853
|
+
const keys = flattenChildren(children);
|
|
10854
|
+
return (jsxRuntime.jsx("kbd", { ref: ref, className: classNames(groupClass, bulmaHelperClasses, className), ...rest, children: keys.map((key, index) => (jsxRuntime.jsxs(React.Fragment, { children: [index > 0 && (jsxRuntime.jsx("span", { className: separatorClass, children: ` ${separator} ` })), key] }, React.isValidElement(key) && key.key != null ? key.key : index))) }));
|
|
10855
|
+
});
|
|
10856
|
+
KbdGroup.displayName = 'Kbd.Group';
|
|
10406
10857
|
/**
|
|
10407
|
-
* The `
|
|
10858
|
+
* The `Kbd` component renders a `<kbd>` drawn as a keycap, for a key the reader presses.
|
|
10859
|
+
*
|
|
10860
|
+
* It only shows the key: it doesn't listen for it. The key takes its colors
|
|
10861
|
+
* from the text it sits in, and its size in `em`, so it fits a paragraph, a
|
|
10862
|
+
* title or a filled button as it is, and `textSize` sizes it outright. For a
|
|
10863
|
+
* combination or a sequence, put the keys in a `Kbd.Group`. What tells
|
|
10864
|
+
* assistive tech a control has a shortcut is `aria-keyshortcuts` on that
|
|
10865
|
+
* control, with `Kbd` as the hint people see next to it.
|
|
10408
10866
|
*
|
|
10409
10867
|
* @function
|
|
10410
|
-
* @param {
|
|
10411
|
-
* @
|
|
10868
|
+
* @param {KbdProps} props - Props for the Kbd component.
|
|
10869
|
+
* @param {React.Ref<HTMLElement>} ref - Forwarded ref to the `<kbd>`.
|
|
10870
|
+
* @returns {JSX.Element} The rendered key.
|
|
10871
|
+
*
|
|
10872
|
+
* @example
|
|
10873
|
+
* <Kbd>Esc</Kbd>
|
|
10874
|
+
*
|
|
10875
|
+
* @example
|
|
10876
|
+
* <Kbd label="Command">⌘</Kbd>
|
|
10412
10877
|
*/
|
|
10413
|
-
const
|
|
10414
|
-
/**
|
|
10415
|
-
* Generates Bulma helper classes and separates out remaining props.
|
|
10416
|
-
*/
|
|
10878
|
+
const KbdRoot = React.forwardRef(({ className, textColor, bgColor, label, children, ...props }, ref) => {
|
|
10417
10879
|
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
10418
10880
|
color: textColor,
|
|
10419
10881
|
backgroundColor: bgColor,
|
|
10420
10882
|
...props,
|
|
10421
10883
|
});
|
|
10422
|
-
const
|
|
10423
|
-
|
|
10424
|
-
|
|
10425
|
-
|
|
10884
|
+
const kbdClass = usePrefixedClassNames('kbd');
|
|
10885
|
+
// The name is text rather than an aria-label, which ARIA in HTML
|
|
10886
|
+
// prohibits on kbd. Its styles are inline, so it stays hidden in a
|
|
10887
|
+
// modular build that leaves out Bulma's helper classes.
|
|
10888
|
+
return (jsxRuntime.jsx("kbd", { ref: ref, className: classNames(kbdClass, bulmaHelperClasses, className), ...rest, children: label ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("span", { "aria-hidden": "true", children: children }), jsxRuntime.jsx("span", { style: visuallyHidden, children: label })] })) : (children) }));
|
|
10889
|
+
});
|
|
10890
|
+
const Kbd = withSubComponents(KbdRoot, { Group: KbdGroup }, 'Kbd');
|
|
10426
10891
|
|
|
10427
10892
|
/**
|
|
10428
|
-
* The `
|
|
10893
|
+
* The `Link` component renders a styled anchor (`<a>`) element with Bulma helper class integration.
|
|
10429
10894
|
*
|
|
10430
10895
|
* @function
|
|
10431
|
-
* @param {
|
|
10432
|
-
* @
|
|
10896
|
+
* @param {LinkProps} props - Props for the Link component.
|
|
10897
|
+
* @param {React.Ref} ref - Forwarded ref to the element `as` renders.
|
|
10898
|
+
* @returns {JSX.Element} The rendered anchor, or whatever `as` names.
|
|
10899
|
+
* @see {@link https://bulma.io/documentation/elements/content/ | Bulma Content documentation}
|
|
10433
10900
|
*/
|
|
10434
|
-
const
|
|
10901
|
+
const Link = React.forwardRef(function Link(linkProps, ref) {
|
|
10902
|
+
const { className, textColor, bgColor, isActive, as: Component = 'a', children, ...props } = linkProps;
|
|
10903
|
+
/**
|
|
10904
|
+
* Generates Bulma helper classes and separates out remaining props.
|
|
10905
|
+
*/
|
|
10435
10906
|
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
10436
10907
|
color: textColor,
|
|
10437
10908
|
backgroundColor: bgColor,
|
|
10438
10909
|
...props,
|
|
10439
10910
|
});
|
|
10440
|
-
const bulmaClasses = usePrefixedClassNames(
|
|
10441
|
-
|
|
10442
|
-
|
|
10443
|
-
|
|
10911
|
+
const bulmaClasses = usePrefixedClassNames({
|
|
10912
|
+
'is-active': isActive,
|
|
10913
|
+
});
|
|
10914
|
+
const linkClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
|
|
10915
|
+
return (jsxRuntime.jsx(Component, { ref: ref, className: linkClasses || undefined, ...rest, ...(Component === 'button' ? { type: buttonType(rest.type) } : {}), children: children }));
|
|
10916
|
+
});
|
|
10917
|
+
Link.displayName = 'Link';
|
|
10444
10918
|
|
|
10445
|
-
const validSubTitleSizes = ['1', '2', '3', '4', '5', '6'];
|
|
10446
|
-
const validSubTitleElements = [
|
|
10447
|
-
'h1',
|
|
10448
|
-
'h2',
|
|
10449
|
-
'h3',
|
|
10450
|
-
'h4',
|
|
10451
|
-
'h5',
|
|
10452
|
-
'h6',
|
|
10453
|
-
'p',
|
|
10454
|
-
];
|
|
10455
10919
|
/**
|
|
10456
|
-
* The `
|
|
10920
|
+
* The `ListItem` component renders a styled list item (`<li>`) element with Bulma helper class integration.
|
|
10457
10921
|
*
|
|
10458
10922
|
* @function
|
|
10459
|
-
* @param {
|
|
10460
|
-
* @returns {JSX.Element} The rendered
|
|
10461
|
-
* @see {@link https://bulma.io/documentation/elements/title/#subtitle | Bulma Subtitle documentation}
|
|
10923
|
+
* @param {ListItemProps} props - Props for the ListItem component.
|
|
10924
|
+
* @returns {JSX.Element} The rendered li element.
|
|
10462
10925
|
*/
|
|
10463
|
-
const
|
|
10926
|
+
const ListItem = ({ className, textColor, bgColor, children, ...props }) => {
|
|
10464
10927
|
/**
|
|
10465
10928
|
* Generates Bulma helper classes and separates out remaining props.
|
|
10466
10929
|
*/
|
|
@@ -10469,681 +10932,800 @@ const SubTitle = ({ className, size, as = 'h1', hasSkeleton, textColor, bgColor,
|
|
|
10469
10932
|
backgroundColor: bgColor,
|
|
10470
10933
|
...props,
|
|
10471
10934
|
});
|
|
10472
|
-
|
|
10473
|
-
const
|
|
10474
|
-
|
|
10475
|
-
const normalized = size == null ? undefined : String(size);
|
|
10476
|
-
const validSize = normalized && validSubTitleSizes.includes(normalized)
|
|
10477
|
-
? normalized
|
|
10478
|
-
: undefined;
|
|
10479
|
-
const bulmaClasses = usePrefixedClassNames('subtitle', {
|
|
10480
|
-
[`is-${validSize}`]: validSize,
|
|
10481
|
-
'has-skeleton': hasSkeleton,
|
|
10482
|
-
});
|
|
10483
|
-
const subTitleClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
|
|
10484
|
-
// Determine the tag based on 'element' and 'validSize'
|
|
10485
|
-
const Tag = element === 'p' ? 'p' : validSize ? `h${validSize}` : element;
|
|
10486
|
-
return (jsxRuntime.jsx(Tag, { className: subTitleClasses, ...rest, children: children }));
|
|
10935
|
+
const bulmaClasses = usePrefixedClassNames();
|
|
10936
|
+
const itemClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
|
|
10937
|
+
return (jsxRuntime.jsx("li", { className: itemClasses || undefined, ...rest, children: children }));
|
|
10487
10938
|
};
|
|
10488
10939
|
|
|
10489
10940
|
/**
|
|
10490
|
-
*
|
|
10941
|
+
* The `Loader` component renders Bulma's `.loader`, a small spinning ring for inline loading states.
|
|
10491
10942
|
*
|
|
10492
|
-
*
|
|
10943
|
+
* The ring is `1em` square, so `textSize` scales it. It is exposed as an
|
|
10944
|
+
* indeterminate `progressbar` named by `ariaLabel`, and it stops spinning
|
|
10945
|
+
* under `prefers-reduced-motion: reduce` while staying drawn. In forced
|
|
10946
|
+
* colors its two open sides take the page's color, so it stays open.
|
|
10493
10947
|
*
|
|
10494
10948
|
* @function
|
|
10495
|
-
* @param {
|
|
10496
|
-
* @
|
|
10497
|
-
* @
|
|
10949
|
+
* @param {LoaderProps} props - Props for the Loader component.
|
|
10950
|
+
* @param {React.Ref<HTMLSpanElement>} ref - Forwarded ref to the loader element.
|
|
10951
|
+
* @returns {JSX.Element} The rendered loader element.
|
|
10952
|
+
*
|
|
10953
|
+
* @example
|
|
10954
|
+
* <Loader />
|
|
10955
|
+
*
|
|
10956
|
+
* @example
|
|
10957
|
+
* <Loader textSize="3" ariaLabel="Saving" />
|
|
10498
10958
|
*/
|
|
10499
|
-
const
|
|
10500
|
-
|
|
10501
|
-
|
|
10502
|
-
|
|
10503
|
-
|
|
10504
|
-
|
|
10505
|
-
|
|
10506
|
-
};
|
|
10959
|
+
const Loader = React.forwardRef(({ className, ariaLabel = 'Loading', ...props }, ref) => {
|
|
10960
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses(props);
|
|
10961
|
+
const loaderClass = usePrefixedClassNames('loader');
|
|
10962
|
+
// role="progressbar", not role="status": a status region mounted together
|
|
10963
|
+
// with its text is not reliably announced, so the loader claims only what
|
|
10964
|
+
// it can deliver, a named indicator found by reading the page. No
|
|
10965
|
+
// aria-valuenow, because the progress is indeterminate.
|
|
10966
|
+
return (jsxRuntime.jsx("span", { ref: ref, className: classNames(loaderClass, bulmaHelperClasses, className), role: "progressbar", "aria-label": ariaLabel, ...rest }));
|
|
10967
|
+
});
|
|
10968
|
+
Loader.displayName = 'Loader';
|
|
10507
10969
|
|
|
10508
10970
|
/**
|
|
10509
|
-
*
|
|
10510
|
-
*
|
|
10511
|
-
* Supports Bulma helper classes for additional styling.
|
|
10971
|
+
* The `Notification` component is a Bulma-styled alert/message area for providing feedback, warnings, or information to users.
|
|
10512
10972
|
*
|
|
10513
10973
|
* @function
|
|
10514
|
-
* @param {
|
|
10515
|
-
* @returns {JSX.Element} The rendered
|
|
10516
|
-
* @see {@link https://bulma.io/documentation/elements/
|
|
10974
|
+
* @param {NotificationProps} props - Props for the Notification component.
|
|
10975
|
+
* @returns {JSX.Element} The rendered notification element.
|
|
10976
|
+
* @see {@link https://bulma.io/documentation/elements/notification/ | Bulma Notification documentation}
|
|
10517
10977
|
*/
|
|
10518
|
-
const
|
|
10978
|
+
const Notification = ({ className, color, textColor, isLight, hasDelete, onDelete, children, ...props }) => {
|
|
10979
|
+
warnUnstyledColor('Notification', color);
|
|
10519
10980
|
/**
|
|
10520
10981
|
* Generates Bulma helper classes and separates out remaining props.
|
|
10521
10982
|
*/
|
|
10522
|
-
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
10523
|
-
|
|
10524
|
-
|
|
10983
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
10984
|
+
color: textColor,
|
|
10985
|
+
...props,
|
|
10986
|
+
});
|
|
10987
|
+
const bulmaClasses = usePrefixedClassNames('notification', {
|
|
10988
|
+
[`is-${color}`]: color && validColors.includes(color),
|
|
10989
|
+
'is-light': isLight,
|
|
10990
|
+
'has-delete': hasDelete,
|
|
10991
|
+
});
|
|
10992
|
+
const deleteClasses = usePrefixedClassNames('delete');
|
|
10993
|
+
const notificationClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
|
|
10994
|
+
return (jsxRuntime.jsxs("div", { className: notificationClasses, ...rest, children: [hasDelete && (jsxRuntime.jsx("button", { type: "button", className: deleteClasses, onClick: onDelete, "aria-label": "Close notification" })), children] }));
|
|
10995
|
+
};
|
|
10996
|
+
let notificationId = 0;
|
|
10997
|
+
const notificationListeners = new Set();
|
|
10998
|
+
let notifications = [];
|
|
10999
|
+
// Queue support
|
|
11000
|
+
let queuedNotifications = [];
|
|
11001
|
+
let currentQueuedNotification = null;
|
|
11002
|
+
// The server has nowhere to portal a notification to, and its copy of this
|
|
11003
|
+
// module is shared by every request, so server rendering reads an empty list.
|
|
11004
|
+
// Hydration reads it too, which keeps the first client render matching the
|
|
11005
|
+
// server's.
|
|
11006
|
+
const noNotifications = [];
|
|
11007
|
+
const getServerNotifications = () => noNotifications;
|
|
11008
|
+
// What a container shows right now: the stacked notifications, then the queued
|
|
11009
|
+
// one on screen. NotificationContainer renders from this rather than from
|
|
11010
|
+
// updates alone, so notifications raised before it mounted still appear. It is
|
|
11011
|
+
// replaced rather than mutated, and only when listeners are notified, because
|
|
11012
|
+
// useSyncExternalStore needs the same array back between changes. An empty
|
|
11013
|
+
// list is `noNotifications` itself, so an empty store reads the same snapshot
|
|
11014
|
+
// on the client as on the server, and a notify that leaves nothing showing
|
|
11015
|
+
// when nothing was showing doesn't render the container again.
|
|
11016
|
+
let visibleNotifications = noNotifications;
|
|
11017
|
+
const getVisibleNotifications = () => visibleNotifications;
|
|
11018
|
+
const notifyNotificationListeners = () => {
|
|
11019
|
+
const allVisible = [...notifications];
|
|
11020
|
+
if (currentQueuedNotification) {
|
|
11021
|
+
allVisible.push(currentQueuedNotification);
|
|
11022
|
+
}
|
|
11023
|
+
visibleNotifications = allVisible.length > 0 ? allVisible : noNotifications;
|
|
11024
|
+
notificationListeners.forEach(listener => listener([...allVisible]));
|
|
11025
|
+
};
|
|
11026
|
+
const processQueuedNotification = () => {
|
|
11027
|
+
if (currentQueuedNotification || queuedNotifications.length === 0)
|
|
11028
|
+
return;
|
|
11029
|
+
currentQueuedNotification = queuedNotifications.shift();
|
|
11030
|
+
notifyNotificationListeners();
|
|
10525
11031
|
};
|
|
10526
|
-
|
|
10527
11032
|
/**
|
|
10528
|
-
*
|
|
10529
|
-
*
|
|
10530
|
-
* Supports Bulma helper classes for additional styling.
|
|
11033
|
+
* Programmatic notification API for showing, closing, and managing notifications.
|
|
10531
11034
|
*
|
|
10532
|
-
* @
|
|
10533
|
-
*
|
|
10534
|
-
*
|
|
10535
|
-
* @see {@link https://bulma.io/documentation/elements/table/#table-footer | Bulma Table documentation}
|
|
11035
|
+
* @example
|
|
11036
|
+
* notification.success('File saved successfully');
|
|
11037
|
+
* notification.danger('Something went wrong', { duration: 5000 });
|
|
10536
11038
|
*/
|
|
10537
|
-
const
|
|
11039
|
+
const notification = {
|
|
10538
11040
|
/**
|
|
10539
|
-
*
|
|
11041
|
+
* Show a notification with the given options.
|
|
11042
|
+
* @param {NotificationOptions} options - Notification configuration.
|
|
11043
|
+
* @returns {string} The unique ID of the created notification.
|
|
11044
|
+
*/
|
|
11045
|
+
show: (options) => {
|
|
11046
|
+
const id = `notification-${++notificationId}`;
|
|
11047
|
+
const instance = { id, options };
|
|
11048
|
+
if (options.queue) {
|
|
11049
|
+
queuedNotifications.push(instance);
|
|
11050
|
+
processQueuedNotification();
|
|
11051
|
+
}
|
|
11052
|
+
else {
|
|
11053
|
+
notifications.push(instance);
|
|
11054
|
+
notifyNotificationListeners();
|
|
11055
|
+
}
|
|
11056
|
+
return id;
|
|
11057
|
+
},
|
|
11058
|
+
/**
|
|
11059
|
+
* Show a success notification.
|
|
11060
|
+
* @param {string|React.ReactNode} message - The message to display.
|
|
11061
|
+
* @param {Partial<NotificationOptions>} [options] - Additional options.
|
|
11062
|
+
* @returns {string} The notification ID.
|
|
11063
|
+
*/
|
|
11064
|
+
success: (message, options) => {
|
|
11065
|
+
return notification.show({ message, color: 'success', ...options });
|
|
11066
|
+
},
|
|
11067
|
+
/**
|
|
11068
|
+
* Show a danger notification.
|
|
11069
|
+
* @param {string|React.ReactNode} message - The message to display.
|
|
11070
|
+
* @param {Partial<NotificationOptions>} [options] - Additional options.
|
|
11071
|
+
* @returns {string} The notification ID.
|
|
11072
|
+
*/
|
|
11073
|
+
danger: (message, options) => {
|
|
11074
|
+
return notification.show({ message, color: 'danger', ...options });
|
|
11075
|
+
},
|
|
11076
|
+
/**
|
|
11077
|
+
* Show a warning notification.
|
|
11078
|
+
* @param {string|React.ReactNode} message - The message to display.
|
|
11079
|
+
* @param {Partial<NotificationOptions>} [options] - Additional options.
|
|
11080
|
+
* @returns {string} The notification ID.
|
|
11081
|
+
*/
|
|
11082
|
+
warning: (message, options) => {
|
|
11083
|
+
return notification.show({ message, color: 'warning', ...options });
|
|
11084
|
+
},
|
|
11085
|
+
/**
|
|
11086
|
+
* Show an info notification.
|
|
11087
|
+
* @param {string|React.ReactNode} message - The message to display.
|
|
11088
|
+
* @param {Partial<NotificationOptions>} [options] - Additional options.
|
|
11089
|
+
* @returns {string} The notification ID.
|
|
11090
|
+
*/
|
|
11091
|
+
info: (message, options) => {
|
|
11092
|
+
return notification.show({ message, color: 'info', ...options });
|
|
11093
|
+
},
|
|
11094
|
+
/**
|
|
11095
|
+
* Close a specific notification by ID.
|
|
11096
|
+
* @param {string} id - The notification ID to close.
|
|
11097
|
+
*/
|
|
11098
|
+
close: (id) => {
|
|
11099
|
+
if (currentQueuedNotification && currentQueuedNotification.id === id) {
|
|
11100
|
+
currentQueuedNotification = null;
|
|
11101
|
+
processQueuedNotification();
|
|
11102
|
+
}
|
|
11103
|
+
else {
|
|
11104
|
+
queuedNotifications = queuedNotifications.filter(n => n.id !== id);
|
|
11105
|
+
notifications = notifications.filter(n => n.id !== id);
|
|
11106
|
+
}
|
|
11107
|
+
notifyNotificationListeners();
|
|
11108
|
+
},
|
|
11109
|
+
/** Close all notifications and clear the queue. */
|
|
11110
|
+
closeAll: () => {
|
|
11111
|
+
notifications = [];
|
|
11112
|
+
queuedNotifications = [];
|
|
11113
|
+
currentQueuedNotification = null;
|
|
11114
|
+
notifyNotificationListeners();
|
|
11115
|
+
},
|
|
11116
|
+
/**
|
|
11117
|
+
* Subscribe to notification state changes.
|
|
11118
|
+
* @param {(items: NotificationInstance[]) => void} listener - Callback invoked on changes.
|
|
11119
|
+
* @returns {() => void} Unsubscribe function.
|
|
10540
11120
|
*/
|
|
10541
|
-
|
|
10542
|
-
|
|
10543
|
-
|
|
11121
|
+
subscribe: (listener) => {
|
|
11122
|
+
notificationListeners.add(listener);
|
|
11123
|
+
return () => notificationListeners.delete(listener);
|
|
11124
|
+
},
|
|
10544
11125
|
};
|
|
10545
|
-
|
|
10546
11126
|
/**
|
|
10547
|
-
*
|
|
10548
|
-
*
|
|
10549
|
-
* `TableColor` is typed from it, and `Tr`, `Th` and `Td` all take a
|
|
10550
|
-
* `TableColor`, so the tuple and those props list the same values. Map over
|
|
10551
|
-
* it to build a color picker, or check a value that arrives at runtime before
|
|
10552
|
-
* passing it in: the components add no color class for a value outside the
|
|
10553
|
-
* tuple.
|
|
10554
|
-
*
|
|
10555
|
-
* @example
|
|
10556
|
-
* import { Tr, Td, validTableColors } from '@allxsmith/bestax-bulma';
|
|
11127
|
+
* Whether a notification of this color announces itself as an alert, rather
|
|
11128
|
+
* than politely through NotificationContainer's status region.
|
|
10557
11129
|
*
|
|
10558
|
-
*
|
|
10559
|
-
*
|
|
10560
|
-
*
|
|
10561
|
-
* {color}
|
|
10562
|
-
* </Td>
|
|
10563
|
-
* ))}
|
|
10564
|
-
* </Tr>;
|
|
11130
|
+
* @function isUrgentColor
|
|
11131
|
+
* @param {NotificationOptions['color']} color - The notification's color.
|
|
11132
|
+
* @returns {boolean} True for `danger` and `warning`.
|
|
10565
11133
|
*/
|
|
10566
|
-
const
|
|
10567
|
-
'primary',
|
|
10568
|
-
'link',
|
|
10569
|
-
'info',
|
|
10570
|
-
'success',
|
|
10571
|
-
'warning',
|
|
10572
|
-
'danger',
|
|
10573
|
-
'black',
|
|
10574
|
-
'dark',
|
|
10575
|
-
'light',
|
|
10576
|
-
'white',
|
|
10577
|
-
];
|
|
11134
|
+
const isUrgentColor = (color) => color === 'danger' || color === 'warning';
|
|
10578
11135
|
/**
|
|
10579
|
-
*
|
|
10580
|
-
*
|
|
10581
|
-
* Supports Bulma color modifiers and helper classes for additional styling.
|
|
11136
|
+
* Single auto-dismissing notification item used by NotificationContainer.
|
|
10582
11137
|
*
|
|
10583
11138
|
* @function
|
|
10584
|
-
* @param {
|
|
10585
|
-
* @returns {JSX.Element} The rendered
|
|
10586
|
-
* @see {@link https://bulma.io/documentation/elements/table/#table-body | Bulma Table documentation}
|
|
11139
|
+
* @param {{ instance: NotificationInstance; onClose: (id: string) => void; registerMessage: (id: string, node: HTMLElement | null) => void }} props - Component props.
|
|
11140
|
+
* @returns {JSX.Element} The rendered notification item.
|
|
10587
11141
|
*/
|
|
10588
|
-
const
|
|
10589
|
-
const
|
|
10590
|
-
|
|
10591
|
-
|
|
10592
|
-
|
|
10593
|
-
|
|
10594
|
-
|
|
10595
|
-
const
|
|
10596
|
-
|
|
10597
|
-
|
|
11142
|
+
const NotificationItem = ({ instance, onClose, registerMessage }) => {
|
|
11143
|
+
const { message, color, isLight, duration = 3000, hasDelete = true, indefinite = false, pauseOnHover = true, } = instance.options;
|
|
11144
|
+
const [isPaused, setIsPaused] = React.useState(false);
|
|
11145
|
+
const urgent = isUrgentColor(color);
|
|
11146
|
+
const handleClose = React.useCallback(() => {
|
|
11147
|
+
onClose(instance.id);
|
|
11148
|
+
}, [onClose, instance.id]);
|
|
11149
|
+
const messageRef = React.useCallback((node) => {
|
|
11150
|
+
registerMessage(instance.id, node);
|
|
11151
|
+
}, [registerMessage, instance.id]);
|
|
11152
|
+
// Auto-close timer
|
|
11153
|
+
React.useEffect(() => {
|
|
11154
|
+
if (indefinite || duration === 0 || isPaused)
|
|
11155
|
+
return undefined;
|
|
11156
|
+
const timer = setTimeout(handleClose, duration);
|
|
11157
|
+
return () => clearTimeout(timer);
|
|
11158
|
+
}, [duration, indefinite, isPaused, handleClose]);
|
|
11159
|
+
const handleMouseEnter = React.useCallback(() => {
|
|
11160
|
+
if (pauseOnHover)
|
|
11161
|
+
setIsPaused(true);
|
|
11162
|
+
}, [pauseOnHover]);
|
|
11163
|
+
const handleMouseLeave = React.useCallback(() => {
|
|
11164
|
+
if (pauseOnHover)
|
|
11165
|
+
setIsPaused(false);
|
|
11166
|
+
}, [pauseOnHover]);
|
|
11167
|
+
return (jsxRuntime.jsx(Notification, { color: color, isLight: isLight, hasDelete: hasDelete, onDelete: handleClose, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, style: { pointerEvents: 'auto' }, children: urgent ? (jsxRuntime.jsx("span", { role: "alert", "aria-live": "assertive", children: message })) : (jsxRuntime.jsx("span", { ref: messageRef, children: message })) }));
|
|
10598
11168
|
};
|
|
10599
|
-
|
|
11169
|
+
// The order NotificationContainer renders its stacks in: across the top of the
|
|
11170
|
+
// screen, then across the bottom.
|
|
11171
|
+
const notificationStackOrder = [
|
|
11172
|
+
'top-left',
|
|
11173
|
+
'top',
|
|
11174
|
+
'top-right',
|
|
11175
|
+
'bottom-left',
|
|
11176
|
+
'bottom',
|
|
11177
|
+
'bottom-right',
|
|
11178
|
+
];
|
|
10600
11179
|
/**
|
|
10601
|
-
*
|
|
10602
|
-
*
|
|
10603
|
-
* Supports the is-selected modifier and color modifiers.
|
|
11180
|
+
* Inline style that fixes a stack of notifications to its place on the screen.
|
|
10604
11181
|
*
|
|
10605
11182
|
* @function
|
|
10606
|
-
* @param {
|
|
10607
|
-
* @returns {
|
|
10608
|
-
* @see {@link https://bulma.io/documentation/elements/table/#table-row | Bulma Table documentation}
|
|
11183
|
+
* @param {NotificationPosition} position - Where the stack sits.
|
|
11184
|
+
* @returns {React.CSSProperties} The stack's style.
|
|
10609
11185
|
*/
|
|
10610
|
-
const
|
|
10611
|
-
const
|
|
10612
|
-
|
|
10613
|
-
|
|
10614
|
-
|
|
10615
|
-
|
|
10616
|
-
|
|
10617
|
-
|
|
10618
|
-
|
|
10619
|
-
|
|
10620
|
-
|
|
11186
|
+
const notificationStackStyle = (position) => {
|
|
11187
|
+
const isBottom = position.startsWith('bottom');
|
|
11188
|
+
const isCenter = position === 'top' || position === 'bottom';
|
|
11189
|
+
const isRight = position.endsWith('right');
|
|
11190
|
+
return {
|
|
11191
|
+
position: 'fixed',
|
|
11192
|
+
zIndex: 100,
|
|
11193
|
+
display: 'flex',
|
|
11194
|
+
flexDirection: isBottom ? 'column-reverse' : 'column',
|
|
11195
|
+
gap: '0.75rem',
|
|
11196
|
+
padding: '1rem',
|
|
11197
|
+
pointerEvents: 'none',
|
|
11198
|
+
maxWidth: '100%',
|
|
11199
|
+
...(isBottom ? { bottom: 0 } : { top: 0 }),
|
|
11200
|
+
...(isCenter
|
|
11201
|
+
? { left: '50%', transform: 'translateX(-50%)', alignItems: 'center' }
|
|
11202
|
+
: isRight
|
|
11203
|
+
? { right: 0, alignItems: 'flex-end' }
|
|
11204
|
+
: { left: 0, alignItems: 'flex-start' }),
|
|
11205
|
+
};
|
|
10621
11206
|
};
|
|
10622
|
-
|
|
10623
|
-
const validAlignments = ['left', 'right', 'centered'];
|
|
10624
11207
|
/**
|
|
10625
|
-
*
|
|
10626
|
-
*
|
|
10627
|
-
*
|
|
11208
|
+
* Container component for rendering programmatic notifications.
|
|
11209
|
+
* Place once at your app root to enable the notification API. A notification
|
|
11210
|
+
* shown with a `position` appears there, and one shown without goes to the
|
|
11211
|
+
* container's `position`, so the container renders a stack for each position
|
|
11212
|
+
* in use.
|
|
10628
11213
|
*
|
|
10629
|
-
*
|
|
10630
|
-
*
|
|
10631
|
-
*
|
|
10632
|
-
*
|
|
10633
|
-
|
|
10634
|
-
|
|
10635
|
-
|
|
10636
|
-
|
|
10637
|
-
|
|
10638
|
-
|
|
10639
|
-
|
|
10640
|
-
* Generates Bulma helper classes and separates out remaining props.
|
|
10641
|
-
*/
|
|
10642
|
-
const { bulmaHelperClasses, rest } = useBulmaClasses({ ...props });
|
|
10643
|
-
const thClasses = classNames(bulmaClasses, className, bulmaHelperClasses);
|
|
10644
|
-
return (jsxRuntime.jsx("th", { className: thClasses || undefined, style: width
|
|
10645
|
-
? { width: typeof width === 'number' ? `${width}px` : width }
|
|
10646
|
-
: undefined, ...rest, children: children }));
|
|
10647
|
-
};
|
|
10648
|
-
|
|
10649
|
-
/**
|
|
10650
|
-
* The `Table` component and its subcomponents provide a highly composable, Bulma-styled table system for React.
|
|
11214
|
+
* It keeps a visually hidden `role="status"` live region in the page from the
|
|
11215
|
+
* moment it mounts, even while nothing is showing, and announces every
|
|
11216
|
+
* notification other than `danger` and `warning` through it, a moment after
|
|
11217
|
+
* the notification appears. Those notifications carry no `role="status"` of
|
|
11218
|
+
* their own, so `getByRole('status')` finds the region, not the notification.
|
|
11219
|
+
* The text stays in the region briefly, even if the notification closes in
|
|
11220
|
+
* the meantime, and while both are up the page holds two copies of it. A
|
|
11221
|
+
* polite notification that closes before its announcement is written, a
|
|
11222
|
+
* moment after it appears, isn't announced at all. `danger` and `warning`
|
|
11223
|
+
* notifications announce themselves as assertive alerts. The region is hidden
|
|
11224
|
+
* with inline styles, so it needs no stylesheet.
|
|
10651
11225
|
*
|
|
10652
11226
|
* @function
|
|
10653
|
-
* @param {
|
|
10654
|
-
* @returns {JSX.Element} The rendered
|
|
10655
|
-
* @see {@link https://bulma.io/documentation/elements/table/ | Bulma Table documentation}
|
|
11227
|
+
* @param {{ position?: NotificationPosition }} props - Container props.
|
|
11228
|
+
* @returns {JSX.Element | null} The rendered notification container, or null on the server and while hydrating.
|
|
10656
11229
|
*/
|
|
10657
|
-
const
|
|
10658
|
-
|
|
10659
|
-
|
|
10660
|
-
|
|
10661
|
-
|
|
10662
|
-
|
|
10663
|
-
|
|
10664
|
-
|
|
10665
|
-
|
|
10666
|
-
|
|
10667
|
-
|
|
10668
|
-
|
|
10669
|
-
|
|
10670
|
-
|
|
10671
|
-
const
|
|
10672
|
-
|
|
10673
|
-
|
|
10674
|
-
|
|
10675
|
-
|
|
10676
|
-
|
|
10677
|
-
|
|
10678
|
-
|
|
10679
|
-
|
|
10680
|
-
|
|
10681
|
-
|
|
10682
|
-
|
|
10683
|
-
|
|
10684
|
-
|
|
11230
|
+
const NotificationContainer = ({ position = 'top-right' }) => {
|
|
11231
|
+
// Starts from the notifications already showing instead of an empty list,
|
|
11232
|
+
// then follows changes.
|
|
11233
|
+
const items = React.useSyncExternalStore(notification.subscribe, getVisibleNotifications, getServerNotifications);
|
|
11234
|
+
// A portal needs a document, and it has no server-rendered counterpart, so
|
|
11235
|
+
// the server render and the hydrating one render nothing, and the container
|
|
11236
|
+
// portals in from the render after hydration.
|
|
11237
|
+
const isHydrated = useIsHydrated();
|
|
11238
|
+
const resolveContainer = usePortalContainer();
|
|
11239
|
+
// The notifications the status region announces. Danger and warning
|
|
11240
|
+
// announce themselves.
|
|
11241
|
+
const politeItems = React.useMemo(() => items.filter(item => !isUrgentColor(item.options.color)), [items]);
|
|
11242
|
+
// Each polite notification's message element, so the text the region
|
|
11243
|
+
// announces is the text on screen, whatever the message renders.
|
|
11244
|
+
const messageNodesRef = React.useRef(new Map());
|
|
11245
|
+
const registerMessage = React.useCallback((id, node) => {
|
|
11246
|
+
if (node) {
|
|
11247
|
+
messageNodesRef.current.set(id, node);
|
|
11248
|
+
}
|
|
11249
|
+
else {
|
|
11250
|
+
messageNodesRef.current.delete(id);
|
|
11251
|
+
}
|
|
11252
|
+
}, []);
|
|
11253
|
+
// StatusRegion only describes polite notifications that are on screen, and
|
|
11254
|
+
// each of those registered its message element when it mounted. The
|
|
11255
|
+
// fallback is for a notification announced without being rendered, should
|
|
11256
|
+
// the container ever render fewer than it announces: a plain-text message
|
|
11257
|
+
// still says itself, and anything else is skipped.
|
|
11258
|
+
const describe = React.useCallback((item) => {
|
|
11259
|
+
const node = messageNodesRef.current.get(item.id);
|
|
11260
|
+
/* istanbul ignore if: every polite notification on screen registers its message element when it mounts */
|
|
11261
|
+
if (!node) {
|
|
11262
|
+
const { message } = item.options;
|
|
11263
|
+
return typeof message === 'string' ? message : null;
|
|
11264
|
+
}
|
|
11265
|
+
return spokenText(node);
|
|
11266
|
+
}, []);
|
|
11267
|
+
if (typeof document === 'undefined' || !isHydrated) {
|
|
11268
|
+
return null;
|
|
11269
|
+
}
|
|
11270
|
+
const stacks = groupIntoPositionStacks(items, item => item.options.position, position, notificationStackOrder);
|
|
11271
|
+
return reactDom.createPortal(jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [stacks.map(stack => (jsxRuntime.jsx("div", { style: notificationStackStyle(stack.position), children: stack.items.map(item => (jsxRuntime.jsx(NotificationItem, { instance: item, onClose: notification.close, registerMessage: registerMessage }, item.id))) }, stack.key))), jsxRuntime.jsx(StatusRegion, { items: politeItems, describe: describe })] }), resolveContainer());
|
|
11272
|
+
};
|
|
10685
11273
|
|
|
10686
|
-
const validTagColors = [
|
|
10687
|
-
'primary',
|
|
10688
|
-
'link',
|
|
10689
|
-
'info',
|
|
10690
|
-
'success',
|
|
10691
|
-
'warning',
|
|
10692
|
-
'danger',
|
|
10693
|
-
'black',
|
|
10694
|
-
'dark',
|
|
10695
|
-
'light',
|
|
10696
|
-
'white',
|
|
10697
|
-
];
|
|
10698
|
-
const validTagSizes = ['normal', 'medium', 'large'];
|
|
10699
11274
|
/**
|
|
10700
|
-
* The `
|
|
11275
|
+
* The `OrderedList` component renders a styled ordered list (`<ol>`) element with Bulma helper class integration.
|
|
10701
11276
|
*
|
|
10702
11277
|
* @function
|
|
10703
|
-
* @param {
|
|
10704
|
-
* @returns {JSX.Element} The rendered
|
|
10705
|
-
* @see {@link https://bulma.io/documentation/elements/tag/ | Bulma Tag documentation}
|
|
11278
|
+
* @param {OrderedListProps} props - Props for the OrderedList component.
|
|
11279
|
+
* @returns {JSX.Element} The rendered ol element.
|
|
10706
11280
|
*/
|
|
10707
|
-
const
|
|
11281
|
+
const OrderedListComponent = ({ className, textColor, bgColor, children, ...props }) => {
|
|
10708
11282
|
/**
|
|
10709
11283
|
* Generates Bulma helper classes and separates out remaining props.
|
|
10710
11284
|
*/
|
|
10711
|
-
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
10712
|
-
|
|
10713
|
-
|
|
10714
|
-
|
|
10715
|
-
'is-light': isLight,
|
|
10716
|
-
'is-rounded': isRounded,
|
|
10717
|
-
'is-delete': isDelete,
|
|
10718
|
-
'is-hoverable': isHoverable,
|
|
11285
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
11286
|
+
color: textColor,
|
|
11287
|
+
backgroundColor: bgColor,
|
|
11288
|
+
...props,
|
|
10719
11289
|
});
|
|
10720
|
-
const
|
|
10721
|
-
|
|
10722
|
-
|
|
10723
|
-
}
|
|
10724
|
-
return (jsxRuntime.jsx("span", { className: tagClasses, ...rest, children: children }));
|
|
11290
|
+
const bulmaClasses = usePrefixedClassNames();
|
|
11291
|
+
const listClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
|
|
11292
|
+
return (jsxRuntime.jsx("ol", { className: listClasses || undefined, ...rest, children: children }));
|
|
10725
11293
|
};
|
|
11294
|
+
const OrderedList = withSubComponents(OrderedListComponent, { Item: ListItem }, 'OrderedList');
|
|
10726
11295
|
|
|
10727
|
-
const validTagsSizes = ['medium', 'large'];
|
|
10728
11296
|
/**
|
|
10729
|
-
* The `
|
|
11297
|
+
* The `Pre` component renders a styled `<pre>` element with Bulma helper class integration.
|
|
10730
11298
|
*
|
|
10731
11299
|
* @function
|
|
10732
|
-
* @param {
|
|
10733
|
-
* @returns {JSX.Element} The rendered
|
|
10734
|
-
* @see {@link https://bulma.io/documentation/elements/tag/#list-of-tags | Bulma Tags documentation}
|
|
11300
|
+
* @param {PreProps} props - Props for the Pre component.
|
|
11301
|
+
* @returns {JSX.Element} The rendered pre element.
|
|
10735
11302
|
*/
|
|
10736
|
-
const
|
|
10737
|
-
|
|
10738
|
-
|
|
10739
|
-
|
|
10740
|
-
|
|
10741
|
-
*/
|
|
10742
|
-
const { bulmaHelperClasses, rest } = useBulmaClasses({ ...props });
|
|
10743
|
-
const bulmaClasses = usePrefixedClassNames('tags', {
|
|
10744
|
-
'has-addons': hasAddons,
|
|
10745
|
-
[`are-${size}`]: size && validTagsSizes.includes(size),
|
|
11303
|
+
const Pre = ({ className, textColor, bgColor, children, ...props }) => {
|
|
11304
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
11305
|
+
color: textColor,
|
|
11306
|
+
backgroundColor: bgColor,
|
|
11307
|
+
...props,
|
|
10746
11308
|
});
|
|
10747
|
-
const
|
|
10748
|
-
|
|
11309
|
+
const bulmaClasses = usePrefixedClassNames();
|
|
11310
|
+
const preClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
|
|
11311
|
+
return (jsxRuntime.jsx("pre", { className: preClasses || undefined, ...rest, children: children }));
|
|
10749
11312
|
};
|
|
10750
|
-
const Tags = withSubComponents(TagsComponent, { Tag }, 'Tags');
|
|
10751
11313
|
|
|
10752
11314
|
/**
|
|
10753
|
-
* The `
|
|
11315
|
+
* The `Progress` component displays a Bulma-styled progress bar.
|
|
10754
11316
|
*
|
|
10755
11317
|
* @function
|
|
10756
|
-
* @param {
|
|
10757
|
-
* @returns {JSX.Element} The rendered
|
|
11318
|
+
* @param {ProgressProps} props - Props for the Progress component.
|
|
11319
|
+
* @returns {JSX.Element} The rendered progress bar element.
|
|
11320
|
+
* @see {@link https://bulma.io/documentation/elements/progress/ | Bulma Progress documentation}
|
|
10758
11321
|
*/
|
|
10759
|
-
const
|
|
11322
|
+
const Progress = ({ className, color, size, value, max, children, ...props }) => {
|
|
11323
|
+
warnUnstyledColor('Progress', color);
|
|
10760
11324
|
/**
|
|
10761
11325
|
* Generates Bulma helper classes and separates out remaining props.
|
|
10762
11326
|
*/
|
|
10763
11327
|
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
10764
|
-
color: textColor,
|
|
10765
|
-
backgroundColor: bgColor,
|
|
10766
11328
|
...props,
|
|
10767
11329
|
});
|
|
10768
|
-
const bulmaClasses = usePrefixedClassNames(
|
|
10769
|
-
|
|
10770
|
-
|
|
11330
|
+
const bulmaClasses = usePrefixedClassNames('progress', {
|
|
11331
|
+
[`is-${color}`]: color && validColors.includes(color),
|
|
11332
|
+
[`is-${size}`]: size,
|
|
11333
|
+
});
|
|
11334
|
+
const progressClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
|
|
11335
|
+
return (jsxRuntime.jsx("progress", { className: progressClasses, value: value, max: max, ...rest, children: children }));
|
|
10771
11336
|
};
|
|
10772
|
-
const UnorderedList = withSubComponents(UnorderedListComponent, { Item: ListItem }, 'UnorderedList');
|
|
10773
11337
|
|
|
10774
|
-
const FieldContext = React.createContext(false);
|
|
10775
|
-
const ControlContext = React.createContext(false);
|
|
10776
|
-
/**
|
|
10777
|
-
* Hook to detect if the component is inside a Field wrapper.
|
|
10778
|
-
* Form components use this to skip rendering their own Field.
|
|
10779
|
-
*/
|
|
10780
|
-
const useInsideField = () => React.useContext(FieldContext);
|
|
10781
|
-
/**
|
|
10782
|
-
* Hook to detect if the component is inside a Control wrapper.
|
|
10783
|
-
* Form components use this to skip rendering their own Control.
|
|
10784
|
-
*/
|
|
10785
|
-
const useInsideControl = () => React.useContext(ControlContext);
|
|
10786
|
-
/** Provider for Field context — used internally by Field component. */
|
|
10787
|
-
const FieldProvider = FieldContext.Provider;
|
|
10788
|
-
/** Provider for Control context — used internally by Control component. */
|
|
10789
|
-
const ControlProvider = ControlContext.Provider;
|
|
10790
|
-
// Each Field-shaping prop, with what takes its place on a `Field` the caller
|
|
10791
|
-
// wraps around the `Control`. `message` needs nothing there, because the
|
|
10792
|
-
// wrapper still renders it inside `Field > Control`. The other Field-level
|
|
10793
|
-
// props (`labelSize`, `labelProps`, `messageColor`) change nothing unless a
|
|
10794
|
-
// `label` or `message` renders, so on their own they keep no `Field`.
|
|
10795
|
-
// `bare-control.test.tsx` holds this list to what each wrapper hands its
|
|
10796
|
-
// `Field` and to which props change its markup, so a new Field-level prop
|
|
10797
|
-
// fails there until it is sorted into one group or the other.
|
|
10798
|
-
const FIELD_SHAPING = [
|
|
10799
|
-
['label', 'label'],
|
|
10800
|
-
['message', undefined],
|
|
10801
|
-
['horizontal', 'horizontal'],
|
|
10802
|
-
['fieldClassName', 'className (for fieldClassName)'],
|
|
10803
|
-
];
|
|
10804
|
-
/** `a`, `a and b`, `a, b and c`. */
|
|
10805
|
-
const listOf = (items) => items.length > 1
|
|
10806
|
-
? `${items.slice(0, -1).join(', ')} and ${items[items.length - 1]}`
|
|
10807
|
-
: items[0];
|
|
10808
11338
|
/**
|
|
10809
|
-
*
|
|
10810
|
-
*
|
|
10811
|
-
*
|
|
10812
|
-
*
|
|
10813
|
-
*
|
|
10814
|
-
*
|
|
10815
|
-
*
|
|
10816
|
-
*
|
|
10817
|
-
*
|
|
10818
|
-
*
|
|
11339
|
+
* The `Skeleton` component provides a Bulma-styled skeleton loader for React applications, useful for indicating that content is loading.
|
|
11340
|
+
*
|
|
11341
|
+
* @function
|
|
11342
|
+
* @param {SkeletonProps} props - Props for the Skeleton component.
|
|
11343
|
+
* @returns {JSX.Element} The rendered skeleton element.
|
|
11344
|
+
*
|
|
11345
|
+
* @example
|
|
11346
|
+
* // Block skeleton
|
|
11347
|
+
* <Skeleton />
|
|
11348
|
+
*
|
|
11349
|
+
* @example
|
|
11350
|
+
* // Multi-line skeleton
|
|
11351
|
+
* <Skeleton variant="lines" lines={5} />
|
|
10819
11352
|
*/
|
|
10820
|
-
const
|
|
10821
|
-
|
|
10822
|
-
|
|
10823
|
-
if (
|
|
10824
|
-
return
|
|
10825
|
-
|
|
10826
|
-
|
|
10827
|
-
return false;
|
|
10828
|
-
const names = held.map(([prop]) => prop);
|
|
10829
|
-
const moved = held
|
|
10830
|
-
.map(([, onField]) => onField)
|
|
10831
|
-
.filter((onField) => onField !== undefined);
|
|
10832
|
-
warnOnce(`${component}:Field-in-bare-Control:${names.join('+')}`, `[bestax-bulma] <${component} ${names.join(' ')}> inside a <Control> ` +
|
|
10833
|
-
`with no <Field> around it renders its own <Field> for ` +
|
|
10834
|
-
`${names.length > 1 ? 'those props' : 'that prop'}, which puts a ` +
|
|
10835
|
-
`.field inside the .control, a nesting Bulma's styles are not ` +
|
|
10836
|
-
`written for. Wrap the <Control> in a <Field> instead` +
|
|
10837
|
-
(moved.length > 0 ? `, and set ${listOf(moved)} on that <Field>.` : '.'));
|
|
10838
|
-
return true;
|
|
10839
|
-
};
|
|
10840
|
-
// Each Control-level prop, with what takes its place on the `Control` the
|
|
10841
|
-
// caller wrapped around the wrapper. `bare-control.test.tsx` holds this to
|
|
10842
|
-
// what each wrapper hands its own `Control`, so a new Control-level prop
|
|
10843
|
-
// fails there until it is added here and passed to `rendersOwnControl`.
|
|
10844
|
-
const ON_CONTROL = {
|
|
10845
|
-
isLoading: 'isLoading',
|
|
10846
|
-
iconLeft: 'iconLeft',
|
|
10847
|
-
iconLeftName: 'iconLeftName',
|
|
10848
|
-
iconLeftSize: 'iconLeftSize',
|
|
10849
|
-
iconRight: 'iconRight',
|
|
10850
|
-
iconRightName: 'iconRightName',
|
|
10851
|
-
iconRightSize: 'iconRightSize',
|
|
10852
|
-
hasIconsLeft: 'hasIconsLeft',
|
|
10853
|
-
hasIconsRight: 'hasIconsRight',
|
|
10854
|
-
isExpanded: 'isExpanded',
|
|
10855
|
-
controlSize: 'size (for controlSize)',
|
|
10856
|
-
controlClassName: 'className (for controlClassName)',
|
|
11353
|
+
const Skeleton = ({ className, variant = 'block', lines = 3, children, ...props }) => {
|
|
11354
|
+
const linesClass = usePrefixedClassNames('skeleton-lines');
|
|
11355
|
+
const blockClass = usePrefixedClassNames('skeleton-block');
|
|
11356
|
+
if (variant === 'lines') {
|
|
11357
|
+
return (jsxRuntime.jsx("div", { className: classNames(linesClass, className), ...props, children: Array.from({ length: lines }).map((_, i) => (jsxRuntime.jsx("div", {}, i))) }));
|
|
11358
|
+
}
|
|
11359
|
+
return (jsxRuntime.jsx("div", { className: classNames(blockClass, className), ...props, children: children }));
|
|
10857
11360
|
};
|
|
11361
|
+
|
|
10858
11362
|
/**
|
|
10859
|
-
*
|
|
10860
|
-
*
|
|
10861
|
-
*
|
|
10862
|
-
*
|
|
10863
|
-
*
|
|
10864
|
-
* and its warning says so without pointing at a `Control`. A wrapper passes
|
|
10865
|
-
* only the props it hands its own `Control`, as the caller gave them, so a
|
|
10866
|
-
* default the wrapper fills in itself never warns. A prop counts when it is
|
|
10867
|
-
* truthy, since a falsy one would change nothing on a `Control` either.
|
|
10868
|
-
* `iconLeftSize` and `hasIconsLeft` show nothing on a `Control` without a
|
|
10869
|
-
* glyph, so when they move and the caller chose no left icon, the advice
|
|
10870
|
-
* names the wrapper's default glyph too, and following it draws what the
|
|
10871
|
-
* wrapper's own `Control` did.
|
|
10872
|
-
* Internal; not part of the public API.
|
|
11363
|
+
* The `Span` component renders a styled inline `<span>` element with Bulma helper class integration.
|
|
11364
|
+
*
|
|
11365
|
+
* @function
|
|
11366
|
+
* @param {SpanProps} props - Props for the Span component.
|
|
11367
|
+
* @returns {JSX.Element} The rendered span element.
|
|
10873
11368
|
*/
|
|
10874
|
-
const
|
|
10875
|
-
|
|
10876
|
-
|
|
10877
|
-
|
|
10878
|
-
|
|
10879
|
-
|
|
10880
|
-
|
|
10881
|
-
|
|
10882
|
-
|
|
10883
|
-
|
|
10884
|
-
|
|
10885
|
-
|
|
10886
|
-
`in inline mode, inside a <Control> or not, so ${doNothing} ` +
|
|
10887
|
-
`nothing. Leave ${held.length > 1 ? 'them' : 'it'} out of an ` +
|
|
10888
|
-
`inline picker.`;
|
|
10889
|
-
}
|
|
10890
|
-
else {
|
|
10891
|
-
const toSet = held.map(prop => ON_CONTROL[prop]);
|
|
10892
|
-
const needsGlyph = (options.iconLeftSize || options.hasIconsLeft) &&
|
|
10893
|
-
!options.iconLeft &&
|
|
10894
|
-
options.iconLeftName === undefined &&
|
|
10895
|
-
options.defaultIconLeftName;
|
|
10896
|
-
if (needsGlyph) {
|
|
10897
|
-
toSet.push(`iconLeftName="${options.defaultIconLeftName}" (its default icon)`);
|
|
10898
|
-
}
|
|
10899
|
-
message =
|
|
10900
|
-
`[bestax-bulma] <${component} ${names}> inside a <Control> renders ` +
|
|
10901
|
-
`no <Control> of its own, so ${doNothing} nothing there. Set ` +
|
|
10902
|
-
`${listOf(toSet)} on that <Control> instead.`;
|
|
10903
|
-
}
|
|
10904
|
-
// Keyed by the message itself: the props named do not settle the advice
|
|
10905
|
-
// (an empty `iconLeftName` and an unset one name the same props), so two
|
|
10906
|
-
// calls that would advise differently must not share a key.
|
|
10907
|
-
warnOnce(message, message);
|
|
10908
|
-
return false;
|
|
11369
|
+
const Span = ({ className, textColor, bgColor, children, ...props }) => {
|
|
11370
|
+
/**
|
|
11371
|
+
* Generates Bulma helper classes and separates out remaining props.
|
|
11372
|
+
*/
|
|
11373
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
11374
|
+
color: textColor,
|
|
11375
|
+
backgroundColor: bgColor,
|
|
11376
|
+
...props,
|
|
11377
|
+
});
|
|
11378
|
+
const bulmaClasses = usePrefixedClassNames();
|
|
11379
|
+
const spanClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
|
|
11380
|
+
return (jsxRuntime.jsx("span", { className: spanClasses || undefined, ...rest, children: children }));
|
|
10909
11381
|
};
|
|
10910
|
-
|
|
11382
|
+
|
|
10911
11383
|
/**
|
|
10912
|
-
* The
|
|
10913
|
-
*
|
|
10914
|
-
*
|
|
10915
|
-
*
|
|
10916
|
-
*
|
|
10917
|
-
* unless inline, #968) and, through `useAutoLabelId`, by the convenience
|
|
10918
|
-
* inputs that render an input of their own (#939). Read it only through
|
|
10919
|
-
* those, which report what they take: a control that read it directly would
|
|
10920
|
-
* report nothing and lose its label (#1004), and
|
|
10921
|
-
* `__tests__/field-label-readers.test.ts` fails on one. Internal; not part of
|
|
10922
|
-
* the public API.
|
|
11384
|
+
* The `Strong` component renders a styled `<strong>` element with Bulma helper class integration.
|
|
11385
|
+
*
|
|
11386
|
+
* @function
|
|
11387
|
+
* @param {StrongProps} props - Props for the Strong component.
|
|
11388
|
+
* @returns {JSX.Element} The rendered strong element.
|
|
10923
11389
|
*/
|
|
10924
|
-
const
|
|
10925
|
-
|
|
10926
|
-
|
|
10927
|
-
|
|
11390
|
+
const Strong = ({ className, textColor, bgColor, children, ...props }) => {
|
|
11391
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
11392
|
+
color: textColor,
|
|
11393
|
+
backgroundColor: bgColor,
|
|
11394
|
+
...props,
|
|
11395
|
+
});
|
|
11396
|
+
const bulmaClasses = usePrefixedClassNames();
|
|
11397
|
+
const strongClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
|
|
11398
|
+
return (jsxRuntime.jsx("strong", { className: strongClasses || undefined, ...rest, children: children }));
|
|
11399
|
+
};
|
|
11400
|
+
|
|
11401
|
+
const validSubTitleSizes = ['1', '2', '3', '4', '5', '6'];
|
|
11402
|
+
const validSubTitleElements = [
|
|
11403
|
+
'h1',
|
|
11404
|
+
'h2',
|
|
11405
|
+
'h3',
|
|
11406
|
+
'h4',
|
|
11407
|
+
'h5',
|
|
11408
|
+
'h6',
|
|
11409
|
+
'p',
|
|
11410
|
+
];
|
|
10928
11411
|
/**
|
|
10929
|
-
* The
|
|
10930
|
-
*
|
|
10931
|
-
*
|
|
10932
|
-
*
|
|
10933
|
-
*
|
|
10934
|
-
*
|
|
11412
|
+
* The `SubTitle` component renders a Bulma-styled subtitle (secondary heading), supporting sizes `1-6` and rendering as any heading or paragraph element (`h1-h6`, `p`).
|
|
11413
|
+
*
|
|
11414
|
+
* @function
|
|
11415
|
+
* @param {SubTitleProps} props - Props for the SubTitle component.
|
|
11416
|
+
* @returns {JSX.Element} The rendered subtitle element.
|
|
11417
|
+
* @see {@link https://bulma.io/documentation/elements/title/#subtitle | Bulma Subtitle documentation}
|
|
10935
11418
|
*/
|
|
10936
|
-
const
|
|
10937
|
-
/**
|
|
10938
|
-
|
|
10939
|
-
|
|
11419
|
+
const SubTitle = ({ className, size, as = 'h1', hasSkeleton, textColor, bgColor, children, ...props }) => {
|
|
11420
|
+
/**
|
|
11421
|
+
* Generates Bulma helper classes and separates out remaining props.
|
|
11422
|
+
*/
|
|
11423
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
11424
|
+
color: textColor,
|
|
11425
|
+
backgroundColor: bgColor,
|
|
11426
|
+
...props,
|
|
11427
|
+
});
|
|
11428
|
+
// Validate 'as' prop at runtime
|
|
11429
|
+
const element = validSubTitleElements.includes(as) ? as : 'h1';
|
|
11430
|
+
// Validate 'size' prop at runtime (numeric sizes normalize to strings)
|
|
11431
|
+
const normalized = size == null ? undefined : String(size);
|
|
11432
|
+
const validSize = normalized && validSubTitleSizes.includes(normalized)
|
|
11433
|
+
? normalized
|
|
11434
|
+
: undefined;
|
|
11435
|
+
const bulmaClasses = usePrefixedClassNames('subtitle', {
|
|
11436
|
+
[`is-${validSize}`]: validSize,
|
|
11437
|
+
'has-skeleton': hasSkeleton,
|
|
11438
|
+
});
|
|
11439
|
+
const subTitleClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
|
|
11440
|
+
// Determine the tag based on 'element' and 'validSize'
|
|
11441
|
+
const Tag = element === 'p' ? 'p' : validSize ? `h${validSize}` : element;
|
|
11442
|
+
return (jsxRuntime.jsx(Tag, { className: subTitleClasses, ...rest, children: children }));
|
|
11443
|
+
};
|
|
11444
|
+
|
|
10940
11445
|
/**
|
|
10941
|
-
*
|
|
10942
|
-
*
|
|
10943
|
-
*
|
|
10944
|
-
*
|
|
10945
|
-
*
|
|
10946
|
-
*
|
|
10947
|
-
*
|
|
10948
|
-
*
|
|
10949
|
-
* it hands out no id for a control. It keeps the generated id after the
|
|
10950
|
-
* label drops a `for` nothing took (#1004), which no control's id matches
|
|
10951
|
-
* then, so the value does not change under the content that reported it.
|
|
10952
|
-
* The `id` is set only while that label renders with it. Unlike
|
|
10953
|
-
* {@link useFieldLabelId}, it is set in a `grouped` or `hasAddons` Field
|
|
10954
|
-
* too, so a label wired by hand names the control its `for` picks in those
|
|
10955
|
-
* rows as well. Consumed through `useAutoLabelId`.
|
|
10956
|
-
* Internal; not part of the public API.
|
|
11446
|
+
* Thead component for rendering a styled Bulma table header.
|
|
11447
|
+
*
|
|
11448
|
+
* Supports Bulma helper classes for additional styling.
|
|
11449
|
+
*
|
|
11450
|
+
* @function
|
|
11451
|
+
* @param {TheadProps} props - Props for the Thead component.
|
|
11452
|
+
* @returns {JSX.Element} The rendered table header element.
|
|
11453
|
+
* @see {@link https://bulma.io/documentation/elements/table/#table-head | Bulma Table documentation}
|
|
10957
11454
|
*/
|
|
10958
|
-
const
|
|
10959
|
-
/**
|
|
10960
|
-
|
|
10961
|
-
|
|
11455
|
+
const Thead = ({ className, children, ...props }) => {
|
|
11456
|
+
/**
|
|
11457
|
+
* Generates Bulma helper classes and separates out remaining props.
|
|
11458
|
+
*/
|
|
11459
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses({ ...props });
|
|
11460
|
+
const theadClasses = classNames(className, bulmaHelperClasses);
|
|
11461
|
+
return (jsxRuntime.jsx("thead", { className: theadClasses || undefined, ...rest, children: children }));
|
|
11462
|
+
};
|
|
11463
|
+
|
|
10962
11464
|
/**
|
|
10963
|
-
*
|
|
10964
|
-
*
|
|
10965
|
-
*
|
|
10966
|
-
*
|
|
10967
|
-
*
|
|
10968
|
-
*
|
|
10969
|
-
*
|
|
10970
|
-
*
|
|
10971
|
-
* as {@link useFieldLabelId}, so a label wired by hand and a `grouped` or
|
|
10972
|
-
* `hasAddons` Field hear nothing. It reports from a layout effect, which a
|
|
10973
|
-
* server never runs and a hydrating client runs only after its first render,
|
|
10974
|
-
* so that render writes the `for` the way the server did.
|
|
10975
|
-
* Internal; not part of the public API.
|
|
11465
|
+
* Tbody component for rendering a styled Bulma table body.
|
|
11466
|
+
*
|
|
11467
|
+
* Supports Bulma helper classes for additional styling.
|
|
11468
|
+
*
|
|
11469
|
+
* @function
|
|
11470
|
+
* @param {TbodyProps} props - Props for the Tbody component.
|
|
11471
|
+
* @returns {JSX.Element} The rendered table body element.
|
|
11472
|
+
* @see {@link https://bulma.io/documentation/elements/table/#table-body | Bulma Table documentation}
|
|
10976
11473
|
*/
|
|
10977
|
-
const
|
|
10978
|
-
|
|
10979
|
-
|
|
11474
|
+
const Tbody = ({ className, children, ...props }) => {
|
|
11475
|
+
/**
|
|
11476
|
+
* Generates Bulma helper classes and separates out remaining props.
|
|
11477
|
+
*/
|
|
11478
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses({ ...props });
|
|
11479
|
+
const tbodyClasses = classNames(className, bulmaHelperClasses);
|
|
11480
|
+
return (jsxRuntime.jsx("tbody", { className: tbodyClasses || undefined, ...rest, children: children }));
|
|
10980
11481
|
};
|
|
10981
|
-
|
|
10982
|
-
const FieldLabelForReportProvider = FieldLabelForReportContext.Provider;
|
|
11482
|
+
|
|
10983
11483
|
/**
|
|
10984
|
-
*
|
|
10985
|
-
*
|
|
10986
|
-
*
|
|
10987
|
-
*
|
|
10988
|
-
*
|
|
10989
|
-
*
|
|
11484
|
+
* Tfoot component for rendering a styled Bulma table footer.
|
|
11485
|
+
*
|
|
11486
|
+
* Supports Bulma helper classes for additional styling.
|
|
11487
|
+
*
|
|
11488
|
+
* @function
|
|
11489
|
+
* @param {TfootProps} props - Props for the Tfoot component.
|
|
11490
|
+
* @returns {JSX.Element} The rendered table footer element.
|
|
11491
|
+
* @see {@link https://bulma.io/documentation/elements/table/#table-footer | Bulma Table documentation}
|
|
10990
11492
|
*/
|
|
10991
|
-
const
|
|
10992
|
-
|
|
10993
|
-
|
|
10994
|
-
|
|
10995
|
-
|
|
11493
|
+
const Tfoot = ({ className, children, ...props }) => {
|
|
11494
|
+
/**
|
|
11495
|
+
* Generates Bulma helper classes and separates out remaining props.
|
|
11496
|
+
*/
|
|
11497
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses({ ...props });
|
|
11498
|
+
const tfootClasses = classNames(className, bulmaHelperClasses);
|
|
11499
|
+
return (jsxRuntime.jsx("tfoot", { className: tfootClasses || undefined, ...rest, children: children }));
|
|
10996
11500
|
};
|
|
10997
|
-
|
|
10998
|
-
const CheckboxesContext = React.createContext(undefined);
|
|
11501
|
+
|
|
10999
11502
|
/**
|
|
11000
|
-
*
|
|
11001
|
-
*
|
|
11503
|
+
* The values the table `color` prop accepts, as a readonly tuple.
|
|
11504
|
+
*
|
|
11505
|
+
* `TableColor` is typed from it, and `Tr`, `Th` and `Td` all take a
|
|
11506
|
+
* `TableColor`, so the tuple and those props list the same values. Map over
|
|
11507
|
+
* it to build a color picker, or check a value that arrives at runtime before
|
|
11508
|
+
* passing it in: the components add no color class for a value outside the
|
|
11509
|
+
* tuple.
|
|
11510
|
+
*
|
|
11511
|
+
* @example
|
|
11512
|
+
* import { Tr, Td, validTableColors } from '@allxsmith/bestax-bulma';
|
|
11513
|
+
*
|
|
11514
|
+
* <Tr>
|
|
11515
|
+
* {validTableColors.map(color => (
|
|
11516
|
+
* <Td key={color} color={color}>
|
|
11517
|
+
* {color}
|
|
11518
|
+
* </Td>
|
|
11519
|
+
* ))}
|
|
11520
|
+
* </Tr>;
|
|
11002
11521
|
*/
|
|
11003
|
-
const
|
|
11522
|
+
const validTableColors = [
|
|
11523
|
+
'primary',
|
|
11524
|
+
'link',
|
|
11525
|
+
'info',
|
|
11526
|
+
'success',
|
|
11527
|
+
'warning',
|
|
11528
|
+
'danger',
|
|
11529
|
+
'black',
|
|
11530
|
+
'dark',
|
|
11531
|
+
'light',
|
|
11532
|
+
'white',
|
|
11533
|
+
];
|
|
11004
11534
|
/**
|
|
11005
|
-
*
|
|
11006
|
-
*
|
|
11535
|
+
* Td component for rendering a styled Bulma table cell.
|
|
11536
|
+
*
|
|
11537
|
+
* Supports Bulma color modifiers and helper classes for additional styling.
|
|
11538
|
+
*
|
|
11539
|
+
* @function
|
|
11540
|
+
* @param {TdProps} props - Props for the Td component.
|
|
11541
|
+
* @returns {JSX.Element} The rendered table cell element.
|
|
11542
|
+
* @see {@link https://bulma.io/documentation/elements/table/#table-body | Bulma Table documentation}
|
|
11543
|
+
*/
|
|
11544
|
+
const Td = ({ className, color, children, ...props }) => {
|
|
11545
|
+
const colorClass = usePrefixedClassNames('', {
|
|
11546
|
+
[`is-${color}`]: color && validTableColors.includes(color),
|
|
11547
|
+
});
|
|
11548
|
+
/**
|
|
11549
|
+
* Generates Bulma helper classes and separates out remaining props.
|
|
11550
|
+
*/
|
|
11551
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses({ ...props });
|
|
11552
|
+
const tdClasses = classNames(colorClass, className, bulmaHelperClasses);
|
|
11553
|
+
return (jsxRuntime.jsx("td", { className: tdClasses || undefined, ...rest, children: children }));
|
|
11554
|
+
};
|
|
11555
|
+
|
|
11556
|
+
/**
|
|
11557
|
+
* Tr component for rendering a styled Bulma table row.
|
|
11558
|
+
*
|
|
11559
|
+
* Supports the is-selected modifier and color modifiers.
|
|
11560
|
+
*
|
|
11561
|
+
* @function
|
|
11562
|
+
* @param {TrProps} props - Props for the Tr component.
|
|
11563
|
+
* @returns {JSX.Element} The rendered table row element.
|
|
11564
|
+
* @see {@link https://bulma.io/documentation/elements/table/#table-row | Bulma Table documentation}
|
|
11007
11565
|
*/
|
|
11008
|
-
const
|
|
11009
|
-
|
|
11010
|
-
|
|
11011
|
-
|
|
11012
|
-
|
|
11566
|
+
const Tr = ({ className, isSelected, color, children, ...props }) => {
|
|
11567
|
+
const bulmaClasses = usePrefixedClassNames('', {
|
|
11568
|
+
'is-selected': isSelected,
|
|
11569
|
+
[`is-${color}`]: color && validTableColors.includes(color),
|
|
11570
|
+
});
|
|
11571
|
+
/**
|
|
11572
|
+
* Generates Bulma helper classes and separates out remaining props.
|
|
11573
|
+
*/
|
|
11574
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses({ ...props });
|
|
11575
|
+
const trClasses = classNames(bulmaClasses, className, bulmaHelperClasses);
|
|
11576
|
+
return (jsxRuntime.jsx("tr", { className: trClasses || undefined, ...rest, children: children }));
|
|
11577
|
+
};
|
|
11013
11578
|
|
|
11579
|
+
const validAlignments = ['left', 'right', 'centered'];
|
|
11014
11580
|
/**
|
|
11015
|
-
*
|
|
11581
|
+
* Th component for rendering a styled Bulma table header cell.
|
|
11016
11582
|
*
|
|
11017
|
-
*
|
|
11018
|
-
* Map over it to build a color picker, or check a value that arrives at
|
|
11019
|
-
* runtime before passing it in: the component adds no color class for a value
|
|
11020
|
-
* outside the tuple.
|
|
11583
|
+
* Supports alignment, width, and color modifiers.
|
|
11021
11584
|
*
|
|
11022
|
-
* @
|
|
11023
|
-
*
|
|
11585
|
+
* @function
|
|
11586
|
+
* @param {ThProps} props - Props for the Th component.
|
|
11587
|
+
* @returns {JSX.Element} The rendered table header cell element.
|
|
11588
|
+
* @see {@link https://bulma.io/documentation/elements/table/#table-head | Bulma Table documentation}
|
|
11589
|
+
*/
|
|
11590
|
+
const Th = ({ className, isAligned, width, color, children, ...props }) => {
|
|
11591
|
+
const bulmaClasses = usePrefixedClassNames('', {
|
|
11592
|
+
[`has-text-${isAligned}`]: isAligned && validAlignments.includes(isAligned),
|
|
11593
|
+
[`is-${color}`]: color && validTableColors.includes(color),
|
|
11594
|
+
});
|
|
11595
|
+
/**
|
|
11596
|
+
* Generates Bulma helper classes and separates out remaining props.
|
|
11597
|
+
*/
|
|
11598
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses({ ...props });
|
|
11599
|
+
const thClasses = classNames(bulmaClasses, className, bulmaHelperClasses);
|
|
11600
|
+
return (jsxRuntime.jsx("th", { className: thClasses || undefined, style: width
|
|
11601
|
+
? { width: typeof width === 'number' ? `${width}px` : width }
|
|
11602
|
+
: undefined, ...rest, children: children }));
|
|
11603
|
+
};
|
|
11604
|
+
|
|
11605
|
+
/**
|
|
11606
|
+
* The `Table` component and its subcomponents provide a highly composable, Bulma-styled table system for React.
|
|
11024
11607
|
*
|
|
11025
|
-
*
|
|
11026
|
-
*
|
|
11027
|
-
*
|
|
11028
|
-
*
|
|
11029
|
-
* ));
|
|
11608
|
+
* @function
|
|
11609
|
+
* @param {TableProps} props - Props for the Table component.
|
|
11610
|
+
* @returns {JSX.Element} The rendered table element.
|
|
11611
|
+
* @see {@link https://bulma.io/documentation/elements/table/ | Bulma Table documentation}
|
|
11030
11612
|
*/
|
|
11031
|
-
const
|
|
11613
|
+
const TableComponent = ({ className, isBordered, isStriped, isNarrow, isHoverable, isFullwidth, isFullWidth, isResponsive, children, ...props }) => {
|
|
11614
|
+
/**
|
|
11615
|
+
* Generates Bulma helper classes and separates out remaining props.
|
|
11616
|
+
*/
|
|
11617
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses({ ...props });
|
|
11618
|
+
const bulmaClasses = usePrefixedClassNames('table', {
|
|
11619
|
+
'is-bordered': isBordered,
|
|
11620
|
+
'is-striped': isStriped,
|
|
11621
|
+
'is-narrow': isNarrow,
|
|
11622
|
+
'is-hoverable': isHoverable,
|
|
11623
|
+
'is-fullwidth': isFullwidth ?? isFullWidth,
|
|
11624
|
+
});
|
|
11625
|
+
const containerClass = usePrefixedClassNames('table-container');
|
|
11626
|
+
const tableClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
|
|
11627
|
+
const tableElement = (jsxRuntime.jsx("table", { className: tableClasses, ...rest, children: children }));
|
|
11628
|
+
if (isResponsive) {
|
|
11629
|
+
return jsxRuntime.jsx("div", { className: containerClass, children: tableElement });
|
|
11630
|
+
}
|
|
11631
|
+
return tableElement;
|
|
11632
|
+
};
|
|
11633
|
+
const Table = withSubComponents(TableComponent, {
|
|
11634
|
+
Thead,
|
|
11635
|
+
Tbody,
|
|
11636
|
+
Tfoot,
|
|
11637
|
+
Tr,
|
|
11638
|
+
Th,
|
|
11639
|
+
Td,
|
|
11640
|
+
}, 'Table');
|
|
11641
|
+
|
|
11642
|
+
const validTagColors = [
|
|
11032
11643
|
'primary',
|
|
11033
11644
|
'link',
|
|
11034
11645
|
'info',
|
|
11035
11646
|
'success',
|
|
11036
11647
|
'warning',
|
|
11037
11648
|
'danger',
|
|
11649
|
+
'black',
|
|
11650
|
+
'dark',
|
|
11651
|
+
'light',
|
|
11652
|
+
'white',
|
|
11038
11653
|
];
|
|
11654
|
+
const validTagSizes = ['normal', 'medium', 'large'];
|
|
11039
11655
|
/**
|
|
11040
|
-
* The
|
|
11041
|
-
*
|
|
11042
|
-
* `CheckboxProps['size']` is typed from it, so the two list the same values.
|
|
11043
|
-
* Use it to offer a size choice or to check a value that arrives at runtime:
|
|
11044
|
-
* the component adds no size class for a value outside the tuple. These are
|
|
11045
|
-
* element sizes, not the spacing scale in `validSizes`.
|
|
11046
|
-
*
|
|
11047
|
-
* @example
|
|
11048
|
-
* import { checkboxSizes } from '@allxsmith/bestax-bulma';
|
|
11656
|
+
* The `Tag` component renders a Bulma-styled label or badge.
|
|
11049
11657
|
*
|
|
11050
|
-
*
|
|
11658
|
+
* @function
|
|
11659
|
+
* @param {TagProps} props - Props for the Tag component.
|
|
11660
|
+
* @returns {JSX.Element} The rendered tag element.
|
|
11661
|
+
* @see {@link https://bulma.io/documentation/elements/tag/ | Bulma Tag documentation}
|
|
11051
11662
|
*/
|
|
11052
|
-
const
|
|
11663
|
+
const Tag = ({ className, color, size, isLight, isRounded, isDelete, isHoverable, onDelete, children, ...props }) => {
|
|
11664
|
+
/**
|
|
11665
|
+
* Generates Bulma helper classes and separates out remaining props.
|
|
11666
|
+
*/
|
|
11667
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses({ ...props });
|
|
11668
|
+
const bulmaClasses = usePrefixedClassNames('tag', {
|
|
11669
|
+
[`is-${color}`]: color && validTagColors.includes(color),
|
|
11670
|
+
[`is-${size}`]: size && size !== 'normal' && validTagSizes.includes(size),
|
|
11671
|
+
'is-light': isLight,
|
|
11672
|
+
'is-rounded': isRounded,
|
|
11673
|
+
'is-delete': isDelete,
|
|
11674
|
+
'is-hoverable': isHoverable,
|
|
11675
|
+
});
|
|
11676
|
+
const tagClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
|
|
11677
|
+
if (isDelete) {
|
|
11678
|
+
return (jsxRuntime.jsx("button", { className: tagClasses, onClick: onDelete, ...rest, "aria-label": rest['aria-label'] || 'Delete tag', type: buttonType(rest.type) }));
|
|
11679
|
+
}
|
|
11680
|
+
return (jsxRuntime.jsx("span", { className: tagClasses, ...rest, children: children }));
|
|
11681
|
+
};
|
|
11682
|
+
|
|
11683
|
+
const validTagsSizes = ['medium', 'large'];
|
|
11053
11684
|
/**
|
|
11054
|
-
* The `
|
|
11685
|
+
* The `Tags` component groups multiple `Tag` components together in a Bulma-styled container that wraps onto multiple lines by default.
|
|
11055
11686
|
*
|
|
11056
11687
|
* @function
|
|
11057
|
-
* @param {
|
|
11058
|
-
* @returns {JSX.Element} The rendered
|
|
11059
|
-
*
|
|
11060
|
-
|
|
11061
|
-
|
|
11062
|
-
|
|
11688
|
+
* @param {TagsProps} props - Props for the Tags component.
|
|
11689
|
+
* @returns {JSX.Element} The rendered tags container.
|
|
11690
|
+
* @see {@link https://bulma.io/documentation/elements/tag/#list-of-tags | Bulma Tags documentation}
|
|
11691
|
+
*/
|
|
11692
|
+
const TagsComponent = ({ className, hasAddons,
|
|
11693
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- accepted for backwards compatibility, deprecated no-op (no `are-multiline` CSS ships)
|
|
11694
|
+
isMultiline, size, children, ...props }) => {
|
|
11695
|
+
/**
|
|
11696
|
+
* Generates Bulma helper classes and separates out remaining props.
|
|
11697
|
+
*/
|
|
11698
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses({ ...props });
|
|
11699
|
+
const bulmaClasses = usePrefixedClassNames('tags', {
|
|
11700
|
+
'has-addons': hasAddons,
|
|
11701
|
+
[`are-${size}`]: size && validTagsSizes.includes(size),
|
|
11702
|
+
});
|
|
11703
|
+
const tagsClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
|
|
11704
|
+
return (jsxRuntime.jsx("div", { className: tagsClasses, ...rest, children: children }));
|
|
11705
|
+
};
|
|
11706
|
+
const Tags = withSubComponents(TagsComponent, { Tag }, 'Tags');
|
|
11707
|
+
|
|
11708
|
+
/**
|
|
11709
|
+
* The `UnorderedList` component renders a styled unordered list (`<ul>`) element with Bulma helper class integration.
|
|
11063
11710
|
*
|
|
11064
|
-
* @
|
|
11065
|
-
*
|
|
11066
|
-
*
|
|
11067
|
-
* <Checkbox value="react">React</Checkbox>
|
|
11068
|
-
* <Checkbox value="vue">Vue</Checkbox>
|
|
11069
|
-
* </Checkboxes>
|
|
11711
|
+
* @function
|
|
11712
|
+
* @param {UnorderedListProps} props - Props for the UnorderedList component.
|
|
11713
|
+
* @returns {JSX.Element} The rendered ul element.
|
|
11070
11714
|
*/
|
|
11071
|
-
const
|
|
11715
|
+
const UnorderedListComponent = ({ className, textColor, bgColor, children, ...props }) => {
|
|
11716
|
+
/**
|
|
11717
|
+
* Generates Bulma helper classes and separates out remaining props.
|
|
11718
|
+
*/
|
|
11072
11719
|
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
11073
11720
|
color: textColor,
|
|
11721
|
+
backgroundColor: bgColor,
|
|
11074
11722
|
...props,
|
|
11075
11723
|
});
|
|
11076
|
-
|
|
11077
|
-
|
|
11078
|
-
|
|
11079
|
-
|
|
11080
|
-
|
|
11081
|
-
inputRef.current = node;
|
|
11082
|
-
if (typeof ref === 'function')
|
|
11083
|
-
return ref(node);
|
|
11084
|
-
if (ref)
|
|
11085
|
-
ref.current = node;
|
|
11086
|
-
}, [ref]);
|
|
11087
|
-
// `indeterminate` is a DOM property with no attribute, so it's set on the
|
|
11088
|
-
// input rather than rendered. A click clears it, so this runs after every
|
|
11089
|
-
// render, not only when the prop changes. Unset, it's left alone, so a
|
|
11090
|
-
// ref that sets the property itself still works. `ownsIndeterminateRef`
|
|
11091
|
-
// remembers that this component set it, so a prop that goes back to unset
|
|
11092
|
-
// clears the dash once instead of leaving it on.
|
|
11093
|
-
const ownsIndeterminateRef = React.useRef(false);
|
|
11094
|
-
useClientLayoutEffect(() => {
|
|
11095
|
-
if (indeterminate !== undefined) {
|
|
11096
|
-
ownsIndeterminateRef.current = true;
|
|
11097
|
-
inputRef.current.indeterminate = indeterminate;
|
|
11098
|
-
}
|
|
11099
|
-
else if (ownsIndeterminateRef.current) {
|
|
11100
|
-
ownsIndeterminateRef.current = false;
|
|
11101
|
-
inputRef.current.indeterminate = false;
|
|
11102
|
-
}
|
|
11103
|
-
});
|
|
11104
|
-
// Inherit name + selection state from a surrounding <Checkboxes> group.
|
|
11105
|
-
// Local props always win over the group (explicit > implicit).
|
|
11106
|
-
const group = useCheckboxesGroup();
|
|
11107
|
-
const effectiveName = name ?? group?.name;
|
|
11108
|
-
// Group-managed checked state — only when the group is in
|
|
11109
|
-
// controlled/uncontrolled mode (group.value is defined) AND this Checkbox
|
|
11110
|
-
// has a value to compare against. Local `checked` always wins.
|
|
11111
|
-
const groupManaged = group?.value !== undefined && value !== undefined;
|
|
11112
|
-
const groupHas = groupManaged && group.value.includes(String(value));
|
|
11113
|
-
const effectiveChecked = checked !== undefined ? checked : groupManaged ? groupHas : undefined;
|
|
11114
|
-
// When the group manages checked, suppress local defaultChecked to avoid
|
|
11115
|
-
// the controlled/uncontrolled React warning.
|
|
11116
|
-
const effectiveDefaultChecked = groupManaged ? undefined : defaultChecked;
|
|
11117
|
-
const handleChange = React.useCallback((e) => {
|
|
11118
|
-
// The click that got here cleared the mixed state. Put it back now,
|
|
11119
|
-
// since a click that changes no state renders nothing to do it.
|
|
11120
|
-
if (indeterminate !== undefined)
|
|
11121
|
-
e.target.indeterminate = indeterminate;
|
|
11122
|
-
// Local onChange always fires.
|
|
11123
|
-
onChange?.(e);
|
|
11124
|
-
// Dispatch to the group additionally — toggle this value's
|
|
11125
|
-
// membership in the group's array.
|
|
11126
|
-
if (group?.onChange && value !== undefined) {
|
|
11127
|
-
const valStr = String(value);
|
|
11128
|
-
const currentArr = group.value ?? [];
|
|
11129
|
-
const next = e.target.checked
|
|
11130
|
-
? currentArr.includes(valStr)
|
|
11131
|
-
? currentArr
|
|
11132
|
-
: [...currentArr, valStr]
|
|
11133
|
-
: currentArr.filter(v => v !== valStr);
|
|
11134
|
-
group.onChange(next);
|
|
11135
|
-
}
|
|
11136
|
-
}, [onChange, group, value, indeterminate]);
|
|
11137
|
-
const mainClass = usePrefixedClassNames('styled-checkbox', 'checkbox', {
|
|
11138
|
-
[`is-${color}`]: color && checkboxColors.includes(color),
|
|
11139
|
-
[`is-${size}`]: size && checkboxSizes.includes(size),
|
|
11140
|
-
});
|
|
11141
|
-
const checkboxClass = classNames(mainClass, bulmaHelperClasses, className);
|
|
11142
|
-
const checkClass = usePrefixedClassNames('check');
|
|
11143
|
-
const controlLabelClass = usePrefixedClassNames('control-label');
|
|
11144
|
-
return (jsxRuntime.jsxs("label", { className: checkboxClass, children: [jsxRuntime.jsx("input", { ref: setRefs, type: "checkbox", disabled: disabled, name: effectiveName, value: value, checked: effectiveChecked, defaultChecked: effectiveDefaultChecked, onChange: handleChange, ...rest }), jsxRuntime.jsx("span", { className: checkClass }), children && jsxRuntime.jsx("span", { className: controlLabelClass, children: children })] }));
|
|
11145
|
-
});
|
|
11146
|
-
Checkbox.displayName = 'Checkbox';
|
|
11724
|
+
const bulmaClasses = usePrefixedClassNames();
|
|
11725
|
+
const listClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
|
|
11726
|
+
return (jsxRuntime.jsx("ul", { className: listClasses || undefined, ...rest, children: children }));
|
|
11727
|
+
};
|
|
11728
|
+
const UnorderedList = withSubComponents(UnorderedListComponent, { Item: ListItem }, 'UnorderedList');
|
|
11147
11729
|
|
|
11148
11730
|
// Whether the nearest enclosing `Control` shows its `isLoading` spinner. The
|
|
11149
11731
|
// picker bases read it so their right-side launcher gives way to that
|
|
@@ -20119,6 +20701,8 @@ exports.ToastContainer = ToastContainer;
|
|
|
20119
20701
|
exports.ToggleButton = ToggleButton;
|
|
20120
20702
|
exports.Tooltip = Tooltip;
|
|
20121
20703
|
exports.Tr = Tr;
|
|
20704
|
+
exports.TreeView = TreeView;
|
|
20705
|
+
exports.TreeViewItem = TreeViewItem;
|
|
20122
20706
|
exports.UnorderedList = UnorderedList;
|
|
20123
20707
|
exports.checkboxColors = checkboxColors;
|
|
20124
20708
|
exports.checkboxSizes = checkboxSizes;
|