@aurora-ds/components 1.2.0 → 1.4.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/cjs/index.js +340 -32
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/index.js +340 -34
- package/dist/esm/index.js.map +1 -1
- package/dist/index.d.ts +122 -3
- package/package.json +1 -1
package/dist/esm/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment as Fragment$1 } from 'react/jsx-runtime';
|
|
2
2
|
import { keyframes, createStyles, useTheme, cx, createVariants, createTheme } from '@aurora-ds/theme';
|
|
3
3
|
import * as React from 'react';
|
|
4
|
-
import { createElement, Fragment, useRef, useState, useCallback, useEffect, useId, isValidElement, cloneElement, useLayoutEffect, useMemo, createContext, useContext } from 'react';
|
|
4
|
+
import { createElement, Fragment, useRef, useState, useCallback, useEffect, useId, isValidElement, cloneElement, useLayoutEffect, useMemo, createContext, useContext, Children } from 'react';
|
|
5
5
|
import { createPortal } from 'react-dom';
|
|
6
6
|
|
|
7
7
|
function _extends$8() { return _extends$8 = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends$8.apply(null, arguments); }
|
|
@@ -641,36 +641,42 @@ const IconButton = ({ ref, icon: IconComponent, ariaLabel, variant = 'contained'
|
|
|
641
641
|
IconButton.displayName = 'IconButton';
|
|
642
642
|
|
|
643
643
|
const LINK_STYLES = createStyles((theme) => ({
|
|
644
|
-
root: ({ underline = 'hover' }) =>
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
color: theme.colors.
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
644
|
+
root: ({ underline = 'hover', color = 'default' }) => {
|
|
645
|
+
const mainColor = color === 'secondary' ? theme.colors.textSecondary : theme.colors.linkMain;
|
|
646
|
+
const hoverColor = color === 'secondary' ? theme.colors.textTertiary : theme.colors.linkHover;
|
|
647
|
+
const activeColor = color === 'secondary' ? theme.colors.textPrimary : theme.colors.linkActive;
|
|
648
|
+
const disabledColor = color === 'secondary' ? theme.colors.textDisabled : theme.colors.linkDisabled;
|
|
649
|
+
return {
|
|
650
|
+
display: 'inline-flex',
|
|
651
|
+
alignItems: 'center',
|
|
652
|
+
gap: '0.25em',
|
|
653
|
+
color: mainColor,
|
|
654
|
+
fontFamily: 'inherit',
|
|
655
|
+
fontSize: 'inherit',
|
|
656
|
+
lineHeight: 'inherit',
|
|
657
|
+
fontWeight: 'inherit',
|
|
658
|
+
textDecoration: underline === 'always' ? 'underline' : 'none',
|
|
659
|
+
cursor: 'pointer',
|
|
660
|
+
borderRadius: theme.radius.xs,
|
|
661
|
+
transition: `color ${theme.transition.fast}`,
|
|
662
|
+
':hover:not([aria-disabled="true"])': {
|
|
663
|
+
color: hoverColor,
|
|
664
|
+
textDecoration: underline !== 'none' ? 'underline' : 'none',
|
|
665
|
+
},
|
|
666
|
+
':active:not([aria-disabled="true"])': {
|
|
667
|
+
color: activeColor,
|
|
668
|
+
},
|
|
669
|
+
':focus-visible': {
|
|
670
|
+
outline: `2px solid ${mainColor}`,
|
|
671
|
+
outlineOffset: '2px',
|
|
672
|
+
},
|
|
673
|
+
'&[aria-disabled="true"]': {
|
|
674
|
+
color: disabledColor,
|
|
675
|
+
cursor: 'not-allowed',
|
|
676
|
+
textDecoration: 'none',
|
|
677
|
+
},
|
|
678
|
+
};
|
|
679
|
+
},
|
|
674
680
|
icon: {
|
|
675
681
|
display: 'inline-flex',
|
|
676
682
|
alignItems: 'center',
|
|
@@ -693,7 +699,7 @@ const LINK_STYLES = createStyles((theme) => ({
|
|
|
693
699
|
* @example <Link href='/terms' underline='none'>Terms</Link>
|
|
694
700
|
* @example <Link onClick={() => navigate('/about')}>About (SPA)</Link>
|
|
695
701
|
*/
|
|
696
|
-
const Link = ({ ref, underline = 'hover', external = false, disabled = false, startIcon: StartIcon, endIcon: EndIcon, children, className, href, onClick, onKeyDown, ...rest }) => {
|
|
702
|
+
const Link = ({ ref, underline = 'hover', color = 'default', external = false, disabled = false, startIcon: StartIcon, endIcon: EndIcon, children, className, href, onClick, onKeyDown, ...rest }) => {
|
|
697
703
|
// An <a> without href has no implicit ARIA role and is not focusable.
|
|
698
704
|
// When used for SPA navigation (onClick only), we restore both behaviours.
|
|
699
705
|
const hasHref = !!href;
|
|
@@ -714,7 +720,7 @@ const Link = ({ ref, underline = 'hover', external = false, disabled = false, st
|
|
|
714
720
|
}
|
|
715
721
|
onKeyDown?.(e);
|
|
716
722
|
};
|
|
717
|
-
return (jsxs("a", { ref: ref, href: href, className: cx(LINK_STYLES.root({ underline }), className), "aria-disabled": disabled || undefined,
|
|
723
|
+
return (jsxs("a", { ref: ref, href: href, className: cx(LINK_STYLES.root({ underline, color }), className), "aria-disabled": disabled || undefined,
|
|
718
724
|
// Without href: must be explicitly put in the tab order.
|
|
719
725
|
// With href: the browser handles focusability natively (no tabIndex needed).
|
|
720
726
|
tabIndex: disabled ? -1 : (!hasHref ? 0 : undefined),
|
|
@@ -2553,6 +2559,78 @@ const Grid = ({ display = 'grid', columns, rows, autoFlow, autoColumns, autoRows
|
|
|
2553
2559
|
};
|
|
2554
2560
|
Grid.displayName = 'Grid';
|
|
2555
2561
|
|
|
2562
|
+
const BREADCRUMB_STYLES = createStyles((_theme) => ({
|
|
2563
|
+
nav: {
|
|
2564
|
+
display: 'block',
|
|
2565
|
+
},
|
|
2566
|
+
list: {
|
|
2567
|
+
display: 'flex',
|
|
2568
|
+
flexWrap: 'wrap',
|
|
2569
|
+
alignItems: 'center',
|
|
2570
|
+
listStyle: 'none',
|
|
2571
|
+
margin: 0,
|
|
2572
|
+
padding: 0,
|
|
2573
|
+
},
|
|
2574
|
+
}), { id: 'breadcrumb' });
|
|
2575
|
+
|
|
2576
|
+
const BreadcrumbContext = createContext({
|
|
2577
|
+
separator: '/',
|
|
2578
|
+
});
|
|
2579
|
+
const useBreadcrumbContext = () => useContext(BreadcrumbContext);
|
|
2580
|
+
|
|
2581
|
+
const BREADCRUMB_ITEM_STYLES = createStyles((theme) => ({
|
|
2582
|
+
item: {
|
|
2583
|
+
display: 'inline-flex',
|
|
2584
|
+
alignItems: 'center',
|
|
2585
|
+
fontSize: theme.fontSize.sm,
|
|
2586
|
+
lineHeight: theme.lineHeight.normal,
|
|
2587
|
+
},
|
|
2588
|
+
separator: {
|
|
2589
|
+
display: 'inline-flex',
|
|
2590
|
+
alignItems: 'center',
|
|
2591
|
+
marginLeft: theme.spacing.sm,
|
|
2592
|
+
marginRight: theme.spacing.sm,
|
|
2593
|
+
color: theme.colors.textTertiary,
|
|
2594
|
+
userSelect: 'none',
|
|
2595
|
+
},
|
|
2596
|
+
}), { id: 'breadcrumb-item' });
|
|
2597
|
+
|
|
2598
|
+
const BreadcrumbItem = ({ label, href, onClick, current = false, isFirst = false, }) => {
|
|
2599
|
+
const { separator } = useBreadcrumbContext();
|
|
2600
|
+
return (jsxs("li", { className: BREADCRUMB_ITEM_STYLES.item, "aria-current": current ? 'page' : undefined, children: [!isFirst && (jsx("span", { "aria-hidden": true, className: BREADCRUMB_ITEM_STYLES.separator, children: separator })), current ? (jsx(Text, { variant: 'span', fontWeight: 'semibold', color: 'textPrimary', children: label })) : (jsx(Link, { href: href, onClick: onClick, color: 'secondary', underline: 'hover', children: label }))] }));
|
|
2601
|
+
};
|
|
2602
|
+
BreadcrumbItem.displayName = 'Breadcrumb.Item';
|
|
2603
|
+
|
|
2604
|
+
/**
|
|
2605
|
+
* WAI-ARIA compliant breadcrumb navigation using a compound component API.
|
|
2606
|
+
*
|
|
2607
|
+
* ```tsx
|
|
2608
|
+
* <Breadcrumb separator='/'>
|
|
2609
|
+
* <Breadcrumb.Item label='Home' href='/' />
|
|
2610
|
+
* <Breadcrumb.Item label='Products' href='/products' />
|
|
2611
|
+
* <Breadcrumb.Item label='Laptop Pro X' current />
|
|
2612
|
+
* </Breadcrumb>
|
|
2613
|
+
* ```
|
|
2614
|
+
*
|
|
2615
|
+
* The last item is typically marked as `current` — it renders as bold text (non-clickable)
|
|
2616
|
+
* and exposes `aria-current="page"` for assistive technologies.
|
|
2617
|
+
*/
|
|
2618
|
+
const BreadcrumbBase = ({ children, separator = '/', ariaLabel = 'Breadcrumb', }) => {
|
|
2619
|
+
const contextValue = useMemo(() => ({ separator }), [separator]);
|
|
2620
|
+
const items = Children.map(children, (child, index) => {
|
|
2621
|
+
if (!isValidElement(child)) {
|
|
2622
|
+
return child;
|
|
2623
|
+
}
|
|
2624
|
+
return cloneElement(child, {
|
|
2625
|
+
isFirst: index === 0,
|
|
2626
|
+
});
|
|
2627
|
+
});
|
|
2628
|
+
return (jsx(BreadcrumbContext.Provider, { value: contextValue, children: jsx("nav", { "aria-label": ariaLabel, className: BREADCRUMB_STYLES.nav, children: jsx("ol", { className: BREADCRUMB_STYLES.list, children: items }) }) }));
|
|
2629
|
+
};
|
|
2630
|
+
BreadcrumbBase.displayName = 'Breadcrumb';
|
|
2631
|
+
const Breadcrumb = BreadcrumbBase;
|
|
2632
|
+
Breadcrumb.Item = BreadcrumbItem;
|
|
2633
|
+
|
|
2556
2634
|
const DrawerContext = createContext({
|
|
2557
2635
|
isExpanded: true,
|
|
2558
2636
|
});
|
|
@@ -2908,6 +2986,234 @@ Drawer.Body = DrawerBody;
|
|
|
2908
2986
|
Drawer.Footer = DrawerFooter;
|
|
2909
2987
|
Drawer.Item = DrawerItem;
|
|
2910
2988
|
|
|
2989
|
+
const TABS_LIST_STYLES = createStyles(() => ({
|
|
2990
|
+
root: {
|
|
2991
|
+
display: 'flex',
|
|
2992
|
+
flexDirection: 'row',
|
|
2993
|
+
alignItems: 'center',
|
|
2994
|
+
overflowX: 'auto',
|
|
2995
|
+
scrollbarWidth: 'none',
|
|
2996
|
+
width: 'fit-content',
|
|
2997
|
+
'::-webkit-scrollbar': { display: 'none' },
|
|
2998
|
+
},
|
|
2999
|
+
}));
|
|
3000
|
+
|
|
3001
|
+
const TabsContext = createContext(null);
|
|
3002
|
+
|
|
3003
|
+
/**
|
|
3004
|
+
* Internal hook — consumes the Tabs context and throws if used outside a `<Tabs>` provider.
|
|
3005
|
+
* @internal
|
|
3006
|
+
*/
|
|
3007
|
+
const useTabsContext = () => {
|
|
3008
|
+
const ctx = useContext(TabsContext);
|
|
3009
|
+
if (!ctx) {
|
|
3010
|
+
throw new Error('This component must be used inside a <Tabs> provider.');
|
|
3011
|
+
}
|
|
3012
|
+
return ctx;
|
|
3013
|
+
};
|
|
3014
|
+
|
|
3015
|
+
const TabsList = ({ children, ariaLabel, ariaLabelledBy }) => {
|
|
3016
|
+
const { value, setValue, baseId, getTabValues } = useTabsContext();
|
|
3017
|
+
/**
|
|
3018
|
+
* Returns true if the tab at the given index is aria-disabled.
|
|
3019
|
+
* We check the DOM attribute because disabled state lives in TabItem,
|
|
3020
|
+
* not in the shared context, and we want to avoid adding it to the context.
|
|
3021
|
+
*/
|
|
3022
|
+
const isTabDisabled = (tabValue) => {
|
|
3023
|
+
const el = document.getElementById(`${baseId}-tab-${tabValue}`);
|
|
3024
|
+
return el?.getAttribute('aria-disabled') === 'true';
|
|
3025
|
+
};
|
|
3026
|
+
/**
|
|
3027
|
+
* Moves focus to the nearest non-disabled tab starting from `startIndex`
|
|
3028
|
+
* and stepping in the given `direction` (+1 = right, -1 = left).
|
|
3029
|
+
* Stops after a full loop if all tabs are disabled.
|
|
3030
|
+
*/
|
|
3031
|
+
const focusNextEnabled = (startIndex, direction) => {
|
|
3032
|
+
const values = getTabValues();
|
|
3033
|
+
for (let i = 1; i <= values.length; i++) {
|
|
3034
|
+
const index = (startIndex + direction * i + values.length) % values.length;
|
|
3035
|
+
const nextValue = values[index];
|
|
3036
|
+
if (!isTabDisabled(nextValue)) {
|
|
3037
|
+
document.getElementById(`${baseId}-tab-${nextValue}`)?.focus();
|
|
3038
|
+
setValue(nextValue);
|
|
3039
|
+
return;
|
|
3040
|
+
}
|
|
3041
|
+
}
|
|
3042
|
+
};
|
|
3043
|
+
const handleKeyDown = (event) => {
|
|
3044
|
+
const values = getTabValues();
|
|
3045
|
+
const currentIndex = values.indexOf(value);
|
|
3046
|
+
if (currentIndex === -1) {
|
|
3047
|
+
return;
|
|
3048
|
+
}
|
|
3049
|
+
switch (event.key) {
|
|
3050
|
+
case 'ArrowRight':
|
|
3051
|
+
event.preventDefault();
|
|
3052
|
+
focusNextEnabled(currentIndex, 1);
|
|
3053
|
+
break;
|
|
3054
|
+
case 'ArrowLeft':
|
|
3055
|
+
event.preventDefault();
|
|
3056
|
+
focusNextEnabled(currentIndex, -1);
|
|
3057
|
+
break;
|
|
3058
|
+
case 'Home': {
|
|
3059
|
+
event.preventDefault();
|
|
3060
|
+
// Focus the first non-disabled tab
|
|
3061
|
+
const firstIdx = values.findIndex((v) => !isTabDisabled(v));
|
|
3062
|
+
if (firstIdx !== -1) {
|
|
3063
|
+
document.getElementById(`${baseId}-tab-${values[firstIdx]}`)?.focus();
|
|
3064
|
+
setValue(values[firstIdx]);
|
|
3065
|
+
}
|
|
3066
|
+
break;
|
|
3067
|
+
}
|
|
3068
|
+
case 'End': {
|
|
3069
|
+
event.preventDefault();
|
|
3070
|
+
// Focus the last non-disabled tab
|
|
3071
|
+
const lastIdx = [...values].reverse().findIndex((v) => !isTabDisabled(v));
|
|
3072
|
+
if (lastIdx !== -1) {
|
|
3073
|
+
const realIndex = values.length - 1 - lastIdx;
|
|
3074
|
+
document.getElementById(`${baseId}-tab-${values[realIndex]}`)?.focus();
|
|
3075
|
+
setValue(values[realIndex]);
|
|
3076
|
+
}
|
|
3077
|
+
break;
|
|
3078
|
+
}
|
|
3079
|
+
}
|
|
3080
|
+
};
|
|
3081
|
+
return (jsx("div", { role: 'tablist', "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, className: TABS_LIST_STYLES.root, onKeyDown: handleKeyDown, children: children }));
|
|
3082
|
+
};
|
|
3083
|
+
TabsList.displayName = 'Tabs.List';
|
|
3084
|
+
|
|
3085
|
+
const TABS_PANEL_STYLES = createStyles(() => ({
|
|
3086
|
+
root: {
|
|
3087
|
+
width: '100%',
|
|
3088
|
+
},
|
|
3089
|
+
}));
|
|
3090
|
+
|
|
3091
|
+
const TabsPanel = ({ value, children, keepMounted = false }) => {
|
|
3092
|
+
const { value: activeValue, baseId } = useTabsContext();
|
|
3093
|
+
const isActive = activeValue === value;
|
|
3094
|
+
if (!isActive && !keepMounted) {
|
|
3095
|
+
return null;
|
|
3096
|
+
}
|
|
3097
|
+
return (jsx("div", { role: 'tabpanel', id: `${baseId}-panel-${value}`, "aria-labelledby": `${baseId}-tab-${value}`,
|
|
3098
|
+
// WAI-ARIA APG: tabIndex={0} allows keyboard scrolling when the panel has no focusable child.
|
|
3099
|
+
// eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
|
|
3100
|
+
tabIndex: 0, hidden: !isActive, className: TABS_PANEL_STYLES.root, children: children }));
|
|
3101
|
+
};
|
|
3102
|
+
TabsPanel.displayName = 'Tabs.Panel';
|
|
3103
|
+
|
|
3104
|
+
const TAB_ITEM_STYLES = createStyles((theme) => ({
|
|
3105
|
+
root: ({ isActive, disabled }) => ({
|
|
3106
|
+
display: 'inline-flex',
|
|
3107
|
+
alignItems: 'center',
|
|
3108
|
+
justifyContent: 'center',
|
|
3109
|
+
boxSizing: 'border-box',
|
|
3110
|
+
paddingTop: theme.spacing.xs,
|
|
3111
|
+
paddingBottom: theme.spacing.xs,
|
|
3112
|
+
paddingLeft: theme.spacing.md,
|
|
3113
|
+
paddingRight: theme.spacing.md,
|
|
3114
|
+
border: 'none',
|
|
3115
|
+
borderBottom: `2px solid ${isActive ? theme.colors.primaryMain : 'transparent'}`,
|
|
3116
|
+
background: 'transparent',
|
|
3117
|
+
cursor: disabled ? 'not-allowed' : 'pointer',
|
|
3118
|
+
fontFamily: 'inherit',
|
|
3119
|
+
whiteSpace: 'nowrap',
|
|
3120
|
+
transition: `border-color ${theme.transition.fast}`,
|
|
3121
|
+
...(disabled ? {} : {
|
|
3122
|
+
':hover': {
|
|
3123
|
+
borderBottomColor: isActive ? theme.colors.primaryMain : theme.colors.textTertiary,
|
|
3124
|
+
},
|
|
3125
|
+
}),
|
|
3126
|
+
':focus-visible': {
|
|
3127
|
+
outline: `2px solid ${theme.colors.primaryMain}`,
|
|
3128
|
+
outlineOffset: '2px',
|
|
3129
|
+
},
|
|
3130
|
+
}),
|
|
3131
|
+
}));
|
|
3132
|
+
|
|
3133
|
+
/**
|
|
3134
|
+
* Handles tab registration, active state derivation and interaction props
|
|
3135
|
+
* for a single `Tabs.Tab` (TabItem) button.
|
|
3136
|
+
*
|
|
3137
|
+
* Registers the tab value into the shared ordered registry on mount so that
|
|
3138
|
+
* `TabsList` can perform correct ArrowLeft/Right/Home/End keyboard navigation.
|
|
3139
|
+
*/
|
|
3140
|
+
const useTabItem = ({ value, disabled }) => {
|
|
3141
|
+
const { value: activeValue, setValue, baseId, registerTab } = useTabsContext();
|
|
3142
|
+
// Maintains the ordered tab registry used by TabsList for keyboard navigation.
|
|
3143
|
+
useEffect(() => registerTab(value), [registerTab, value]);
|
|
3144
|
+
const isActive = activeValue === value;
|
|
3145
|
+
return {
|
|
3146
|
+
isActive,
|
|
3147
|
+
buttonProps: {
|
|
3148
|
+
id: `${baseId}-tab-${value}`,
|
|
3149
|
+
'aria-selected': isActive,
|
|
3150
|
+
'aria-controls': `${baseId}-panel-${value}`,
|
|
3151
|
+
'aria-disabled': disabled || undefined,
|
|
3152
|
+
tabIndex: isActive ? 0 : -1,
|
|
3153
|
+
onClick: () => {
|
|
3154
|
+
if (!disabled) {
|
|
3155
|
+
setValue(value);
|
|
3156
|
+
}
|
|
3157
|
+
},
|
|
3158
|
+
},
|
|
3159
|
+
};
|
|
3160
|
+
};
|
|
3161
|
+
|
|
3162
|
+
const TabItem = ({ value, label, disabled = false }) => {
|
|
3163
|
+
const { isActive, buttonProps } = useTabItem({ value, disabled });
|
|
3164
|
+
return (jsx("button", { type: 'button', role: 'tab', className: TAB_ITEM_STYLES.root({ isActive, disabled }), ...buttonProps, children: jsx(Text, { variant: 'span', fontSize: 'sm', fontWeight: isActive ? 'semibold' : 'medium', color: disabled ? 'textDisabled' : isActive ? 'textPrimary' : 'textSecondary', children: label }) }));
|
|
3165
|
+
};
|
|
3166
|
+
TabItem.displayName = 'Tabs.Tab';
|
|
3167
|
+
|
|
3168
|
+
/**
|
|
3169
|
+
* WAI-ARIA compliant tab interface using a compound component API.
|
|
3170
|
+
*
|
|
3171
|
+
* ```tsx
|
|
3172
|
+
* <Tabs defaultValue={'active'} onChange={handleChange}>
|
|
3173
|
+
* <Tabs.List ariaLabel={'Subscriptions'}>
|
|
3174
|
+
* <Tabs.Tab value={'active'} label={'Active'} />
|
|
3175
|
+
* <Tabs.Tab value={'cancelled'} label={'Cancelled'} />
|
|
3176
|
+
* </Tabs.List>
|
|
3177
|
+
* <Tabs.Panel value={'active'}>...</Tabs.Panel>
|
|
3178
|
+
* <Tabs.Panel value={'cancelled'}>...</Tabs.Panel>
|
|
3179
|
+
* </Tabs>
|
|
3180
|
+
* ```
|
|
3181
|
+
*
|
|
3182
|
+
* Supports controlled (`value` + `onChange`) and uncontrolled (`defaultValue`) modes.
|
|
3183
|
+
* Keyboard navigation: `ArrowLeft/Right`, `Home`, `End`.
|
|
3184
|
+
* Only the active panel is mounted — use `keepMounted` on `Tabs.Panel` to preserve state across switches.
|
|
3185
|
+
*/
|
|
3186
|
+
const TabsBase = ({ children, value: controlledValue, defaultValue, onChange, id, }) => {
|
|
3187
|
+
const reactId = useId();
|
|
3188
|
+
const baseId = id ?? `tabs-${reactId}`;
|
|
3189
|
+
const [internalValue, setInternalValue] = useState(defaultValue ?? '');
|
|
3190
|
+
const isControlled = controlledValue !== undefined;
|
|
3191
|
+
const value = isControlled ? controlledValue : internalValue;
|
|
3192
|
+
const tabsRef = useRef([]);
|
|
3193
|
+
const registerTab = useCallback((tabValue) => {
|
|
3194
|
+
if (!tabsRef.current.includes(tabValue)) {
|
|
3195
|
+
tabsRef.current.push(tabValue);
|
|
3196
|
+
}
|
|
3197
|
+
return () => {
|
|
3198
|
+
tabsRef.current = tabsRef.current.filter((v) => v !== tabValue);
|
|
3199
|
+
};
|
|
3200
|
+
}, []);
|
|
3201
|
+
const getTabValues = useCallback(() => tabsRef.current, []);
|
|
3202
|
+
const setValue = useCallback((next) => {
|
|
3203
|
+
if (!isControlled) {
|
|
3204
|
+
setInternalValue(next);
|
|
3205
|
+
}
|
|
3206
|
+
onChange?.(next);
|
|
3207
|
+
}, [isControlled, onChange]);
|
|
3208
|
+
const contextValue = useMemo(() => ({ value, setValue, baseId, registerTab, getTabValues }), [value, setValue, baseId, registerTab, getTabValues]);
|
|
3209
|
+
return (jsx(TabsContext.Provider, { value: contextValue, children: children }));
|
|
3210
|
+
};
|
|
3211
|
+
TabsBase.displayName = 'Tabs';
|
|
3212
|
+
const Tabs = TabsBase;
|
|
3213
|
+
Tabs.List = TabsList;
|
|
3214
|
+
Tabs.Tab = TabItem;
|
|
3215
|
+
Tabs.Panel = TabsPanel;
|
|
3216
|
+
|
|
2911
3217
|
const AlertContext = createContext({
|
|
2912
3218
|
variant: 'default',
|
|
2913
3219
|
accentColor: 'defaultActive',
|
|
@@ -3576,5 +3882,5 @@ const darkTheme = createTheme({
|
|
|
3576
3882
|
breakpoints: themeBreakpoints,
|
|
3577
3883
|
});
|
|
3578
3884
|
|
|
3579
|
-
export { Alert, Backdrop, Badge, Box, Button, Card, Checkbox, Dialog, Drawer, Form, Grid, Icon, IconButton, InfoBubble, Link, Menu, Select, Skeleton, Stack, Switch, Text, TextField, Tooltip, darkTheme, lightTheme, useDrawerContext };
|
|
3885
|
+
export { Alert, Backdrop, Badge, Box, Breadcrumb, Button, Card, Checkbox, Dialog, Drawer, Form, Grid, Icon, IconButton, InfoBubble, Link, Menu, Select, Skeleton, Stack, Switch, Tabs, Text, TextField, Tooltip, darkTheme, lightTheme, useDrawerContext };
|
|
3580
3886
|
//# sourceMappingURL=index.js.map
|