@brycks/core-front 0.5.0 → 0.7.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/components/data/List/List.cjs +1 -1
- package/dist/components/data/List/List.cjs.map +1 -1
- package/dist/components/data/List/List.js +68 -148
- package/dist/components/data/List/List.js.map +1 -1
- package/dist/components/data/Table/Table.cjs +1 -1
- package/dist/components/data/Table/Table.cjs.map +1 -1
- package/dist/components/data/Table/Table.js +142 -224
- package/dist/components/data/Table/Table.js.map +1 -1
- package/dist/components/data/TreeView/TreeView.cjs +1 -1
- package/dist/components/data/TreeView/TreeView.cjs.map +1 -1
- package/dist/components/data/TreeView/TreeView.js +141 -182
- package/dist/components/data/TreeView/TreeView.js.map +1 -1
- package/dist/components/feedback/Drawer/Drawer.cjs +1 -22
- package/dist/components/feedback/Drawer/Drawer.cjs.map +1 -1
- package/dist/components/feedback/Drawer/Drawer.js +84 -184
- package/dist/components/feedback/Drawer/Drawer.js.map +1 -1
- package/dist/components/feedback/Modal/Modal.cjs +1 -1
- package/dist/components/feedback/Modal/Modal.cjs.map +1 -1
- package/dist/components/feedback/Modal/Modal.js +65 -150
- package/dist/components/feedback/Modal/Modal.js.map +1 -1
- package/dist/components/feedback/Toast/Toast.cjs +1 -10
- package/dist/components/feedback/Toast/Toast.cjs.map +1 -1
- package/dist/components/feedback/Toast/Toast.js +94 -187
- package/dist/components/feedback/Toast/Toast.js.map +1 -1
- package/dist/components/form/Checkbox/Checkbox.cjs +1 -1
- package/dist/components/form/Checkbox/Checkbox.js +11 -11
- package/dist/components/form/Combobox/Combobox.cjs +1 -6
- package/dist/components/form/Combobox/Combobox.cjs.map +1 -1
- package/dist/components/form/Combobox/Combobox.js +187 -283
- package/dist/components/form/Combobox/Combobox.js.map +1 -1
- package/dist/components/form/Input/Input.cjs +1 -1
- package/dist/components/form/Input/Input.cjs.map +1 -1
- package/dist/components/form/Input/Input.js +38 -133
- package/dist/components/form/Input/Input.js.map +1 -1
- package/dist/components/form/Select/Select.cjs +1 -1
- package/dist/components/form/Select/Select.cjs.map +1 -1
- package/dist/components/form/Select/Select.js +36 -82
- package/dist/components/form/Select/Select.js.map +1 -1
- package/dist/components/form/TagInput/TagInput.cjs +1 -1
- package/dist/components/form/TagInput/TagInput.js +8 -8
- package/dist/components/form/Textarea/Textarea.cjs +1 -1
- package/dist/components/form/Textarea/Textarea.cjs.map +1 -1
- package/dist/components/form/Textarea/Textarea.js +66 -95
- package/dist/components/form/Textarea/Textarea.js.map +1 -1
- package/dist/components/navigation/ContextMenu/ContextMenu.cjs +1 -1
- package/dist/components/navigation/ContextMenu/ContextMenu.js +9 -9
- package/dist/components/navigation/Menu/Menu.cjs +1 -1
- package/dist/components/navigation/Menu/Menu.cjs.map +1 -1
- package/dist/components/navigation/Menu/Menu.js +70 -135
- package/dist/components/navigation/Menu/Menu.js.map +1 -1
- package/dist/components/navigation/Pagination/Pagination.cjs +1 -1
- package/dist/components/navigation/Pagination/Pagination.cjs.map +1 -1
- package/dist/components/navigation/Pagination/Pagination.js +87 -157
- package/dist/components/navigation/Pagination/Pagination.js.map +1 -1
- package/dist/components/primitives/Button/Button.cjs +1 -1
- package/dist/components/primitives/Button/Button.cjs.map +1 -1
- package/dist/components/primitives/Button/Button.js +47 -43
- package/dist/components/primitives/Button/Button.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("react/jsx-runtime"),r=require("react"),z=require("react-dom"),L=require("../../../utils/styles.cjs"),
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("react/jsx-runtime"),r=require("react"),z=require("react-dom"),L=require("../../../utils/styles.cjs"),v=require("../../../design-system/primitives/sizing.cjs"),M=require("../../../design-system/tokens/spacing.cjs"),S=require("../../../design-system/tokens/motion.cjs"),j=require("../../../design-system/tokens/typography.cjs"),I=r.createContext(null);function R(){return o.jsx("svg",{width:"12",height:"12",viewBox:"0 0 12 12",fill:"none","aria-hidden":"true",children:o.jsx("path",{d:"M4.5 3l3 3-3 3",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round"})})}function P({items:i,position:h,onClose:u,level:p=0}){const[l,x]=r.useState(-1),[b,c]=r.useState(null),y=r.useRef(null),a=i.filter(e=>!e.separator&&!e.disabled),g=r.useCallback(e=>{switch(e.key){case"ArrowDown":e.preventDefault(),e.stopPropagation(),x(n=>{const s=n+1;return s>=a.length?0:s});break;case"ArrowUp":e.preventDefault(),e.stopPropagation(),x(n=>{const s=n-1;return s<0?a.length-1:s});break;case"ArrowRight":{e.preventDefault(),e.stopPropagation();const n=a[l];if(n?.children){const s=i.indexOf(n);c(s)}break}case"ArrowLeft":p>0&&(e.preventDefault(),e.stopPropagation(),u());break;case"Enter":case" ":if(e.preventDefault(),e.stopPropagation(),l>=0){const n=a[l];if(n?.children){const s=i.indexOf(n);c(s)}else n?.onClick&&(n.onClick(),u())}break;case"Escape":e.preventDefault(),e.stopPropagation(),u();break}},[l,a,i,p,u]);r.useEffect(()=>{y.current?.focus()},[]);const w={position:"fixed",top:h.y,left:h.x,minWidth:180,maxWidth:280,backgroundColor:"var(--brycks-background-elevated)",border:"1px solid var(--brycks-border-default)",borderRadius:"var(--brycks-radius-lg)",boxShadow:"var(--brycks-shadow-lg)",padding:M.spacing[1],zIndex:"var(--brycks-z-dropdown)",outline:"none"},C=(e,n)=>({display:"flex",alignItems:"center",gap:M.spacing[2],padding:`${v.componentPaddingY.sm}px ${v.componentPaddingX.sm}px`,fontSize:j.fontSizes.sm,fontWeight:400,color:n?"var(--brycks-foreground-disabled)":"var(--brycks-foreground-default)",backgroundColor:e?"var(--brycks-background-muted)":"transparent",borderRadius:"var(--brycks-radius-md)",cursor:n?"not-allowed":"pointer",transition:`background-color ${S.durations.fast}ms ${S.easings.easeOut}`}),k={display:"flex",alignItems:"center",justifyContent:"center",width:v.iconSizes.sm,height:v.iconSizes.sm,color:"inherit"},d={marginLeft:"auto",fontSize:j.fontSizes.xs,color:"var(--brycks-foreground-muted)"},m={height:1,margin:`${M.spacing[1]}px 0`,backgroundColor:"var(--brycks-border-default)"};let t=-1;return o.jsx("ul",{ref:y,role:"menu",tabIndex:0,style:w,onKeyDown:g,children:i.map((e,n)=>{if(e.separator)return o.jsx("li",{role:"separator",style:m},e.id);e.disabled||t++;const s=t,E=!e.disabled&&s===l,f=e.children&&e.children.length>0;return o.jsxs("li",{role:"menuitem","aria-disabled":e.disabled,"aria-haspopup":f?"menu":void 0,"aria-expanded":f&&b===n?"true":void 0,style:C(E,!!e.disabled),onMouseEnter:()=>{e.disabled||(x(s),c(f?n:null))},onMouseLeave:()=>{f||c(null)},onClick:()=>{e.disabled||(f?c(n):e.onClick&&(e.onClick(),u()))},children:[e.icon&&o.jsx("span",{style:k,children:e.icon}),o.jsx("span",{style:{flex:1},children:e.label}),e.shortcut&&o.jsx("span",{style:d,children:e.shortcut}),f&&o.jsx("span",{style:k,children:o.jsx(R,{})}),f&&b===n&&e.children&&o.jsx(P,{items:e.children,position:{x:176,y:0},onClose:()=>c(null),level:p+1})]},e.id)})})}const D=r.forwardRef(function({items:h,children:u,onOpen:p,onClose:l,disabled:x=!1,className:b,testId:c},y){const[a,g]=r.useState(!1),[w,C]=r.useState({x:0,y:0}),k=r.useCallback(t=>{if(x)return;t.preventDefault(),t.stopPropagation();const e=Math.min(t.clientX,window.innerWidth-200),n=Math.min(t.clientY,window.innerHeight-300);C({x:e,y:n}),g(!0),p?.()},[x,p]),d=r.useCallback(()=>{g(!1),l?.()},[l]);r.useEffect(()=>{if(!a)return;const t=()=>{d()};return document.addEventListener("mousedown",t),()=>document.removeEventListener("mousedown",t)},[a,d]),r.useEffect(()=>{if(!a)return;const t=e=>{e.key==="Escape"&&d()};return document.addEventListener("keydown",t),()=>document.removeEventListener("keydown",t)},[a,d]);const m={display:"contents"};return o.jsx(I.Provider,{value:{closeMenu:d},children:o.jsxs("div",{ref:y,className:L.cx("brycks-contextmenu",b),style:m,"data-testid":c,onContextMenu:k,children:[u,a&&z.createPortal(o.jsx("div",{style:{position:"fixed",inset:0,zIndex:"var(--brycks-z-dropdown)"},onMouseDown:t=>t.stopPropagation(),children:o.jsx(P,{items:h,position:w,onClose:d})}),document.body)]})})});D.displayName="ContextMenu";function q(){const i=r.useContext(I);if(!i)throw new Error("useContextMenu must be used within a ContextMenu");return i}exports.ContextMenu=D;exports.useContextMenu=q;
|
|
2
2
|
//# sourceMappingURL=ContextMenu.cjs.map
|
|
@@ -2,10 +2,10 @@ import { jsx as o, jsxs as D } from "react/jsx-runtime";
|
|
|
2
2
|
import { forwardRef as R, useState as k, useCallback as M, useEffect as I, createContext as A, useContext as W, useRef as $ } from "react";
|
|
3
3
|
import { createPortal as j } from "react-dom";
|
|
4
4
|
import { cx as O } from "../../../utils/styles.js";
|
|
5
|
+
import { componentPaddingY as K, componentPaddingX as N, iconSizes as S } from "../../../design-system/primitives/sizing.js";
|
|
5
6
|
import { spacing as C } from "../../../design-system/tokens/spacing.js";
|
|
6
|
-
import { durations as
|
|
7
|
-
import { fontSizes as
|
|
8
|
-
import { componentPaddingY as X, componentPaddingX as Y, iconSizes as P } from "../../../design-system/primitives/sizing.js";
|
|
7
|
+
import { durations as X, easings as Y } from "../../../design-system/tokens/motion.js";
|
|
8
|
+
import { fontSizes as P } from "../../../design-system/tokens/typography.js";
|
|
9
9
|
const E = A(null);
|
|
10
10
|
function B() {
|
|
11
11
|
return /* @__PURE__ */ o("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ o("path", { d: "M4.5 3l3 3-3 3", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) });
|
|
@@ -75,24 +75,24 @@ function L({ items: a, position: h, onClose: l, level: f = 0 }) {
|
|
|
75
75
|
display: "flex",
|
|
76
76
|
alignItems: "center",
|
|
77
77
|
gap: C[2],
|
|
78
|
-
padding: `${
|
|
79
|
-
fontSize:
|
|
78
|
+
padding: `${K.sm}px ${N.sm}px`,
|
|
79
|
+
fontSize: P.sm,
|
|
80
80
|
fontWeight: 400,
|
|
81
81
|
color: n ? "var(--brycks-foreground-disabled)" : "var(--brycks-foreground-default)",
|
|
82
82
|
backgroundColor: e ? "var(--brycks-background-muted)" : "transparent",
|
|
83
83
|
borderRadius: "var(--brycks-radius-md)",
|
|
84
84
|
cursor: n ? "not-allowed" : "pointer",
|
|
85
|
-
transition: `background-color ${
|
|
85
|
+
transition: `background-color ${X.fast}ms ${Y.easeOut}`
|
|
86
86
|
}), g = {
|
|
87
87
|
display: "flex",
|
|
88
88
|
alignItems: "center",
|
|
89
89
|
justifyContent: "center",
|
|
90
|
-
width:
|
|
91
|
-
height:
|
|
90
|
+
width: S.sm,
|
|
91
|
+
height: S.sm,
|
|
92
92
|
color: "inherit"
|
|
93
93
|
}, d = {
|
|
94
94
|
marginLeft: "auto",
|
|
95
|
-
fontSize:
|
|
95
|
+
fontSize: P.xs,
|
|
96
96
|
color: "var(--brycks-foreground-muted)"
|
|
97
97
|
}, w = {
|
|
98
98
|
height: 1,
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u=require("react/jsx-runtime"),
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u=require("react/jsx-runtime"),s=require("react"),h=require("../../../utils/styles.cjs"),R=s.createContext(null);function C(){const l=s.useContext(R);if(!l)throw new Error("Menu components must be used within a Menu");return l}const _=s.forwardRef(function({activeItem:n,onChange:o,size:r="md",collapsed:c=!1,className:d,style:f,children:x,testId:v,...g},m){const[I,w]=s.useState(null),[i,a]=s.useState(-1),p=s.useRef([]),M=s.useRef(null),k=n!==void 0?n:I,b=e=>{n===void 0&&w(e),o?.(e)},N=s.useCallback(e=>{const t=p.current.indexOf(e);return t===-1?(p.current.push(e),p.current.length-1):t},[]),j=s.useCallback(e=>{const t=M.current?.querySelectorAll('[role="menuitem"]:not(:disabled)');if(!t?.length)return;const D=i>=0?i:0;switch(e.key){case"ArrowDown":{e.preventDefault();const y=(D+1)%t.length;a(y),t[y]?.focus();break}case"ArrowUp":{e.preventDefault();const y=(D-1+t.length)%t.length;a(y),t[y]?.focus();break}case"Home":e.preventDefault(),a(0),t[0]?.focus();break;case"End":e.preventDefault(),a(t.length-1),t[t.length-1]?.focus();break}},[i]);return u.jsx(R.Provider,{value:{size:r,activeItem:k,setActiveItem:b,collapsed:c,focusedIndex:i,setFocusedIndex:a,registerItem:N},children:u.jsx("nav",{ref:e=>{M.current=e,typeof m=="function"?m(e):m&&(m.current=e)},role:"menu",className:h.cx("brycks-menu",`brycks-menu--${r}`,c&&"brycks-menu--collapsed",d),style:f,onKeyDown:j,"data-testid":v,...g,children:x})})});_.displayName="Menu";const S=s.forwardRef(function({value:n,icon:o,badge:r,disabled:c=!1,onClick:d,className:f,style:x,children:v,...g},m){const{activeItem:I,setActiveItem:w,collapsed:i,focusedIndex:a,setFocusedIndex:p,registerItem:M}=C(),k=I===n,b=M(n),N=()=>{c||(w(n),d?.(n))},j=()=>{p(b)},e=a>=0?a===b:I?k:b===0;return u.jsxs("button",{ref:m,type:"button",role:"menuitem",disabled:c,tabIndex:e?0:-1,className:h.cx("brycks-menu-item",k&&"brycks-menu-item--active",f),style:x,onClick:N,onFocus:j,title:i?String(v):void 0,...g,children:[o&&u.jsx("span",{className:"brycks-menu-item__icon",children:o}),!i&&u.jsx("span",{className:"brycks-menu-item__label",children:v}),!i&&r&&u.jsx("span",{className:"brycks-menu-item__badge",children:r})]})});S.displayName="MenuItem";const A=s.forwardRef(function({label:n,className:o,style:r,children:c,...d},f){const{collapsed:x}=C();return u.jsxs("div",{ref:f,className:h.cx("brycks-menu-group",o),style:r,...d,children:[n&&!x&&u.jsx("div",{className:"brycks-menu-group__label",children:n}),c]})});A.displayName="MenuGroup";const q=s.forwardRef(function({className:n,style:o,...r},c){return u.jsx("div",{ref:c,role:"separator",className:h.cx("brycks-menu-divider",n),style:o,...r})});q.displayName="MenuDivider";exports.Menu=_;exports.MenuDivider=q;exports.MenuGroup=A;exports.MenuItem=S;
|
|
2
2
|
//# sourceMappingURL=Menu.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Menu.cjs","sources":["../../../../src/components/navigation/Menu/Menu.tsx"],"sourcesContent":["/**\n * Menu Component\n *\n * A vertical navigation menu with support for nested items.\n * Suitable for sidebars and navigation panels.\n */\n\nimport {\n forwardRef,\n useState,\n useRef,\n useCallback,\n createContext,\n useContext,\n type CSSProperties,\n type ReactNode,\n type HTMLAttributes,\n type KeyboardEvent,\n} from 'react'\nimport { cx } from '../../../utils/styles'\nimport { spacing, fontSizes, durations, easings } from '../../../design-system'\nimport { componentHeights, componentGap, componentPaddingX, componentPaddingY, iconSizes } from '../../../design-system/primitives'\n\nexport type MenuSize = 'sm' | 'md' | 'lg'\n\ninterface MenuContextValue {\n size: MenuSize\n activeItem: string | null\n setActiveItem: (value: string | null) => void\n collapsed: boolean\n focusedIndex: number\n setFocusedIndex: (index: number) => void\n registerItem: (value: string) => number\n}\n\nconst MenuContext = createContext<MenuContextValue | null>(null)\n\nfunction useMenuContext() {\n const context = useContext(MenuContext)\n if (!context) {\n throw new Error('Menu components must be used within a Menu')\n }\n return context\n}\n\nexport interface MenuProps extends Omit<HTMLAttributes<HTMLElement>, 'onChange'> {\n /** Currently active item */\n activeItem?: string | null\n /** Callback when active item changes */\n onChange?: (value: string | null) => void\n /** Menu size */\n size?: MenuSize\n /** Whether the menu is collapsed (icons only) */\n collapsed?: boolean\n /** Custom class name */\n className?: string\n /** Test ID */\n testId?: string\n}\n\nexport const Menu = forwardRef<HTMLElement, MenuProps>(function Menu(\n {\n activeItem: controlledActiveItem,\n onChange,\n size = 'md',\n collapsed = false,\n className,\n style,\n children,\n testId,\n ...props\n },\n ref\n) {\n const [internalActiveItem, setInternalActiveItem] = useState<string | null>(null)\n const [focusedIndex, setFocusedIndex] = useState(-1)\n const itemsRef = useRef<string[]>([])\n const menuRef = useRef<HTMLElement>(null)\n const activeItem = controlledActiveItem !== undefined ? controlledActiveItem : internalActiveItem\n\n const setActiveItem = (value: string | null) => {\n if (controlledActiveItem === undefined) {\n setInternalActiveItem(value)\n }\n onChange?.(value)\n }\n\n // Register items for keyboard navigation\n const registerItem = useCallback((value: string) => {\n const index = itemsRef.current.indexOf(value)\n if (index === -1) {\n itemsRef.current.push(value)\n return itemsRef.current.length - 1\n }\n return index\n }, [])\n\n // Keyboard navigation\n const handleKeyDown = useCallback((e: KeyboardEvent<HTMLElement>) => {\n const items = menuRef.current?.querySelectorAll<HTMLButtonElement>('[role=\"menuitem\"]:not(:disabled)')\n if (!items?.length) return\n\n const currentIndex = focusedIndex >= 0 ? focusedIndex : 0\n\n switch (e.key) {\n case 'ArrowDown': {\n e.preventDefault()\n const nextIndex = (currentIndex + 1) % items.length\n setFocusedIndex(nextIndex)\n items[nextIndex]?.focus()\n break\n }\n case 'ArrowUp': {\n e.preventDefault()\n const prevIndex = (currentIndex - 1 + items.length) % items.length\n setFocusedIndex(prevIndex)\n items[prevIndex]?.focus()\n break\n }\n case 'Home':\n e.preventDefault()\n setFocusedIndex(0)\n items[0]?.focus()\n break\n case 'End':\n e.preventDefault()\n setFocusedIndex(items.length - 1)\n items[items.length - 1]?.focus()\n break\n }\n }, [focusedIndex])\n\n const menuStyle: CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n gap: spacing[0.5],\n padding: spacing[2],\n ...style,\n }\n\n return (\n <MenuContext.Provider value={{ size, activeItem, setActiveItem, collapsed, focusedIndex, setFocusedIndex, registerItem }}>\n <nav\n ref={(node) => {\n (menuRef as React.MutableRefObject<HTMLElement | null>).current = node\n if (typeof ref === 'function') ref(node)\n else if (ref) (ref as React.MutableRefObject<HTMLElement | null>).current = node\n }}\n role=\"menu\"\n className={cx('brycks-menu', `brycks-menu--${size}`, collapsed && 'brycks-menu--collapsed', className)}\n style={menuStyle}\n onKeyDown={handleKeyDown}\n data-testid={testId}\n {...props}\n >\n {children}\n </nav>\n </MenuContext.Provider>\n )\n})\n\nMenu.displayName = 'Menu'\n\n// MenuItem\nexport interface MenuItemProps extends Omit<HTMLAttributes<HTMLButtonElement>, 'onClick'> {\n /** Item value (unique identifier) */\n value: string\n /** Icon before the label */\n icon?: ReactNode\n /** Badge/count after the label */\n badge?: ReactNode\n /** Whether the item is disabled */\n disabled?: boolean\n /** Callback when clicked */\n onClick?: (value: string) => void\n /** Custom class name */\n className?: string\n}\n\nconst sizeConfig: Record<MenuSize, { height: number; fontSize: number; iconSize: number; padding: number }> = {\n sm: { height: componentHeights.sm, fontSize: fontSizes.base - 1, iconSize: iconSizes.sm, padding: componentGap.lg },\n md: { height: componentHeights.md, fontSize: fontSizes.base, iconSize: iconSizes.md - 2, padding: componentPaddingX.sm },\n lg: { height: componentHeights.lg, fontSize: fontSizes.md, iconSize: iconSizes.md, padding: spacing[3.5] },\n}\n\nexport const MenuItem = forwardRef<HTMLButtonElement, MenuItemProps>(function MenuItem(\n { value, icon, badge, disabled = false, onClick, className, style, children, ...props },\n ref\n) {\n const { size, activeItem, setActiveItem, collapsed, focusedIndex, setFocusedIndex, registerItem } = useMenuContext()\n const isActive = activeItem === value\n const config = sizeConfig[size]\n const itemIndex = registerItem(value)\n\n const handleClick = () => {\n if (disabled) return\n setActiveItem(value)\n onClick?.(value)\n }\n\n const handleFocus = () => {\n setFocusedIndex(itemIndex)\n }\n\n const itemStyle: CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n gap: componentGap.lg,\n width: '100%',\n minHeight: config.height,\n padding: collapsed ? config.padding : `0 ${config.padding}px`,\n fontSize: config.fontSize,\n fontWeight: isActive ? 600 : 500,\n color: disabled\n ? 'var(--brycks-foreground-disabled)'\n : isActive\n ? 'var(--brycks-primary-default)'\n : 'var(--brycks-foreground-default)',\n backgroundColor: isActive ? 'var(--brycks-primary-muted)' : 'transparent',\n border: 'none',\n borderRadius: 'var(--brycks-radius-md)',\n cursor: disabled ? 'not-allowed' : 'pointer',\n transition: `all ${durations.quick}ms ${easings.easeOut}`,\n textAlign: 'left',\n justifyContent: collapsed ? 'center' : 'flex-start',\n ...style,\n }\n\n const iconStyle: CSSProperties = {\n width: config.iconSize,\n height: config.iconSize,\n flexShrink: 0,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n }\n\n const badgeStyle: CSSProperties = {\n marginLeft: 'auto',\n }\n\n // Roving tabindex: before any item is focused (focusedIndex -1), Tab must\n // still be able to enter the menu through the active item or the first one.\n const isTabStop =\n focusedIndex >= 0 ? focusedIndex === itemIndex : activeItem ? isActive : itemIndex === 0\n\n return (\n <button\n ref={ref}\n type=\"button\"\n role=\"menuitem\"\n disabled={disabled}\n tabIndex={isTabStop ? 0 : -1}\n className={cx('brycks-menu-item', isActive && 'brycks-menu-item--active', className)}\n style={itemStyle}\n onClick={handleClick}\n onFocus={handleFocus}\n onMouseEnter={(e) => {\n if (!disabled && !isActive) {\n e.currentTarget.style.backgroundColor = 'var(--brycks-background-muted)'\n }\n }}\n onMouseLeave={(e) => {\n if (!isActive) {\n e.currentTarget.style.backgroundColor = 'transparent'\n }\n }}\n title={collapsed ? String(children) : undefined}\n {...props}\n >\n {icon && <span style={iconStyle}>{icon}</span>}\n {!collapsed && <span style={{ flex: 1 }}>{children}</span>}\n {!collapsed && badge && <span style={badgeStyle}>{badge}</span>}\n </button>\n )\n})\n\nMenuItem.displayName = 'MenuItem'\n\n// MenuGroup\nexport interface MenuGroupProps extends HTMLAttributes<HTMLDivElement> {\n /** Group label */\n label?: string\n /** Custom class name */\n className?: string\n}\n\nexport const MenuGroup = forwardRef<HTMLDivElement, MenuGroupProps>(function MenuGroup(\n { label, className, style, children, ...props },\n ref\n) {\n const { collapsed } = useMenuContext()\n\n const groupStyle: CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n gap: spacing[0.5],\n ...style,\n }\n\n const labelStyle: CSSProperties = {\n padding: `${spacing[3]}px ${spacing[3]}px ${componentPaddingY.sm}px ${spacing[3]}px`,\n fontSize: fontSizes.xs,\n fontWeight: 600,\n color: 'var(--brycks-foreground-muted)',\n textTransform: 'uppercase',\n letterSpacing: '0.05em',\n }\n\n return (\n <div ref={ref} className={cx('brycks-menu-group', className)} style={groupStyle} {...props}>\n {label && !collapsed && <div style={labelStyle}>{label}</div>}\n {children}\n </div>\n )\n})\n\nMenuGroup.displayName = 'MenuGroup'\n\n// MenuDivider\nexport interface MenuDividerProps extends HTMLAttributes<HTMLDivElement> {\n /** Custom class name */\n className?: string\n}\n\nexport const MenuDivider = forwardRef<HTMLDivElement, MenuDividerProps>(function MenuDivider(\n { className, style, ...props },\n ref\n) {\n const dividerStyle: CSSProperties = {\n height: 1,\n backgroundColor: 'var(--brycks-border-muted)',\n margin: `${spacing[2]}px 0`,\n ...style,\n }\n\n return (\n <div\n ref={ref}\n role=\"separator\"\n className={cx('brycks-menu-divider', className)}\n style={dividerStyle}\n {...props}\n />\n )\n})\n\nMenuDivider.displayName = 'MenuDivider'\n"],"names":["MenuContext","createContext","useMenuContext","context","useContext","Menu","forwardRef","controlledActiveItem","onChange","size","collapsed","className","style","children","testId","props","ref","internalActiveItem","setInternalActiveItem","useState","focusedIndex","setFocusedIndex","itemsRef","useRef","menuRef","activeItem","setActiveItem","value","registerItem","useCallback","index","handleKeyDown","items","currentIndex","nextIndex","prevIndex","menuStyle","spacing","jsx","node","cx","sizeConfig","componentHeights","fontSizes","iconSizes","componentGap","componentPaddingX","MenuItem","icon","badge","disabled","onClick","isActive","config","itemIndex","handleClick","handleFocus","itemStyle","durations","easings","iconStyle","badgeStyle","isTabStop","jsxs","e","MenuGroup","label","groupStyle","labelStyle","componentPaddingY","MenuDivider","dividerStyle"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Menu.cjs","sources":["../../../../src/components/navigation/Menu/Menu.tsx"],"sourcesContent":["/**\n * Menu Component\n *\n * A vertical navigation menu with support for nested items.\n * Suitable for sidebars and navigation panels.\n */\n\nimport {\n forwardRef,\n useState,\n useRef,\n useCallback,\n createContext,\n useContext,\n type ReactNode,\n type HTMLAttributes,\n type KeyboardEvent,\n} from 'react'\nimport { cx } from '../../../utils/styles'\n\nexport type MenuSize = 'sm' | 'md' | 'lg'\n\ninterface MenuContextValue {\n size: MenuSize\n activeItem: string | null\n setActiveItem: (value: string | null) => void\n collapsed: boolean\n focusedIndex: number\n setFocusedIndex: (index: number) => void\n registerItem: (value: string) => number\n}\n\nconst MenuContext = createContext<MenuContextValue | null>(null)\n\nfunction useMenuContext() {\n const context = useContext(MenuContext)\n if (!context) {\n throw new Error('Menu components must be used within a Menu')\n }\n return context\n}\n\nexport interface MenuProps extends Omit<HTMLAttributes<HTMLElement>, 'onChange'> {\n /** Currently active item */\n activeItem?: string | null\n /** Callback when active item changes */\n onChange?: (value: string | null) => void\n /** Menu size */\n size?: MenuSize\n /** Whether the menu is collapsed (icons only) */\n collapsed?: boolean\n /** Custom class name */\n className?: string\n /** Test ID */\n testId?: string\n}\n\nexport const Menu = forwardRef<HTMLElement, MenuProps>(function Menu(\n {\n activeItem: controlledActiveItem,\n onChange,\n size = 'md',\n collapsed = false,\n className,\n style,\n children,\n testId,\n ...props\n },\n ref\n) {\n const [internalActiveItem, setInternalActiveItem] = useState<string | null>(null)\n const [focusedIndex, setFocusedIndex] = useState(-1)\n const itemsRef = useRef<string[]>([])\n const menuRef = useRef<HTMLElement>(null)\n const activeItem = controlledActiveItem !== undefined ? controlledActiveItem : internalActiveItem\n\n const setActiveItem = (value: string | null) => {\n if (controlledActiveItem === undefined) {\n setInternalActiveItem(value)\n }\n onChange?.(value)\n }\n\n // Register items for keyboard navigation\n const registerItem = useCallback((value: string) => {\n const index = itemsRef.current.indexOf(value)\n if (index === -1) {\n itemsRef.current.push(value)\n return itemsRef.current.length - 1\n }\n return index\n }, [])\n\n // Keyboard navigation\n const handleKeyDown = useCallback((e: KeyboardEvent<HTMLElement>) => {\n const items = menuRef.current?.querySelectorAll<HTMLButtonElement>('[role=\"menuitem\"]:not(:disabled)')\n if (!items?.length) return\n\n const currentIndex = focusedIndex >= 0 ? focusedIndex : 0\n\n switch (e.key) {\n case 'ArrowDown': {\n e.preventDefault()\n const nextIndex = (currentIndex + 1) % items.length\n setFocusedIndex(nextIndex)\n items[nextIndex]?.focus()\n break\n }\n case 'ArrowUp': {\n e.preventDefault()\n const prevIndex = (currentIndex - 1 + items.length) % items.length\n setFocusedIndex(prevIndex)\n items[prevIndex]?.focus()\n break\n }\n case 'Home':\n e.preventDefault()\n setFocusedIndex(0)\n items[0]?.focus()\n break\n case 'End':\n e.preventDefault()\n setFocusedIndex(items.length - 1)\n items[items.length - 1]?.focus()\n break\n }\n }, [focusedIndex])\n\n return (\n <MenuContext.Provider value={{ size, activeItem, setActiveItem, collapsed, focusedIndex, setFocusedIndex, registerItem }}>\n <nav\n ref={(node) => {\n (menuRef as React.MutableRefObject<HTMLElement | null>).current = node\n if (typeof ref === 'function') ref(node)\n else if (ref) (ref as React.MutableRefObject<HTMLElement | null>).current = node\n }}\n role=\"menu\"\n className={cx('brycks-menu', `brycks-menu--${size}`, collapsed && 'brycks-menu--collapsed', className)}\n style={style}\n onKeyDown={handleKeyDown}\n data-testid={testId}\n {...props}\n >\n {children}\n </nav>\n </MenuContext.Provider>\n )\n})\n\nMenu.displayName = 'Menu'\n\n// MenuItem\nexport interface MenuItemProps extends Omit<HTMLAttributes<HTMLButtonElement>, 'onClick'> {\n /** Item value (unique identifier) */\n value: string\n /** Icon before the label */\n icon?: ReactNode\n /** Badge/count after the label */\n badge?: ReactNode\n /** Whether the item is disabled */\n disabled?: boolean\n /** Callback when clicked */\n onClick?: (value: string) => void\n /** Custom class name */\n className?: string\n}\n\nexport const MenuItem = forwardRef<HTMLButtonElement, MenuItemProps>(function MenuItem(\n { value, icon, badge, disabled = false, onClick, className, style, children, ...props },\n ref\n) {\n const { activeItem, setActiveItem, collapsed, focusedIndex, setFocusedIndex, registerItem } = useMenuContext()\n const isActive = activeItem === value\n const itemIndex = registerItem(value)\n\n const handleClick = () => {\n if (disabled) return\n setActiveItem(value)\n onClick?.(value)\n }\n\n const handleFocus = () => {\n setFocusedIndex(itemIndex)\n }\n\n // Roving tabindex: before any item is focused (focusedIndex -1), Tab must\n // still be able to enter the menu through the active item or the first one.\n const isTabStop =\n focusedIndex >= 0 ? focusedIndex === itemIndex : activeItem ? isActive : itemIndex === 0\n\n return (\n <button\n ref={ref}\n type=\"button\"\n role=\"menuitem\"\n disabled={disabled}\n tabIndex={isTabStop ? 0 : -1}\n className={cx('brycks-menu-item', isActive && 'brycks-menu-item--active', className)}\n style={style}\n onClick={handleClick}\n onFocus={handleFocus}\n title={collapsed ? String(children) : undefined}\n {...props}\n >\n {icon && <span className=\"brycks-menu-item__icon\">{icon}</span>}\n {!collapsed && <span className=\"brycks-menu-item__label\">{children}</span>}\n {!collapsed && badge && <span className=\"brycks-menu-item__badge\">{badge}</span>}\n </button>\n )\n})\n\nMenuItem.displayName = 'MenuItem'\n\n// MenuGroup\nexport interface MenuGroupProps extends HTMLAttributes<HTMLDivElement> {\n /** Group label */\n label?: string\n /** Custom class name */\n className?: string\n}\n\nexport const MenuGroup = forwardRef<HTMLDivElement, MenuGroupProps>(function MenuGroup(\n { label, className, style, children, ...props },\n ref\n) {\n const { collapsed } = useMenuContext()\n\n return (\n <div ref={ref} className={cx('brycks-menu-group', className)} style={style} {...props}>\n {label && !collapsed && <div className=\"brycks-menu-group__label\">{label}</div>}\n {children}\n </div>\n )\n})\n\nMenuGroup.displayName = 'MenuGroup'\n\n// MenuDivider\nexport interface MenuDividerProps extends HTMLAttributes<HTMLDivElement> {\n /** Custom class name */\n className?: string\n}\n\nexport const MenuDivider = forwardRef<HTMLDivElement, MenuDividerProps>(function MenuDivider(\n { className, style, ...props },\n ref\n) {\n return (\n <div\n ref={ref}\n role=\"separator\"\n className={cx('brycks-menu-divider', className)}\n style={style}\n {...props}\n />\n )\n})\n\nMenuDivider.displayName = 'MenuDivider'\n"],"names":["MenuContext","createContext","useMenuContext","context","useContext","Menu","forwardRef","controlledActiveItem","onChange","size","collapsed","className","style","children","testId","props","ref","internalActiveItem","setInternalActiveItem","useState","focusedIndex","setFocusedIndex","itemsRef","useRef","menuRef","activeItem","setActiveItem","value","registerItem","useCallback","index","handleKeyDown","items","currentIndex","nextIndex","prevIndex","jsx","node","cx","MenuItem","icon","badge","disabled","onClick","isActive","itemIndex","handleClick","handleFocus","isTabStop","jsxs","MenuGroup","label","MenuDivider"],"mappings":"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"}
|
|
@@ -1,190 +1,125 @@
|
|
|
1
|
-
import { jsx as
|
|
2
|
-
import { forwardRef as
|
|
3
|
-
import { cx as
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const P = L(null);
|
|
9
|
-
function q() {
|
|
10
|
-
const f = O(P);
|
|
11
|
-
if (!f)
|
|
1
|
+
import { jsx as i, jsxs as R } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef as k, useState as C, useRef as A, useCallback as F, createContext as j, useContext as E } from "react";
|
|
3
|
+
import { cx as M } from "../../../utils/styles.js";
|
|
4
|
+
const S = j(null);
|
|
5
|
+
function G() {
|
|
6
|
+
const a = E(S);
|
|
7
|
+
if (!a)
|
|
12
8
|
throw new Error("Menu components must be used within a Menu");
|
|
13
|
-
return
|
|
9
|
+
return a;
|
|
14
10
|
}
|
|
15
|
-
const
|
|
11
|
+
const K = k(function({
|
|
16
12
|
activeItem: n,
|
|
17
13
|
onChange: c,
|
|
18
14
|
size: s = "md",
|
|
19
15
|
collapsed: r = !1,
|
|
20
16
|
className: l,
|
|
21
|
-
style:
|
|
22
|
-
children:
|
|
23
|
-
testId:
|
|
24
|
-
...
|
|
25
|
-
},
|
|
26
|
-
const [
|
|
27
|
-
n === void 0 &&
|
|
28
|
-
},
|
|
29
|
-
const t =
|
|
30
|
-
return t === -1 ? (
|
|
31
|
-
}, []),
|
|
17
|
+
style: d,
|
|
18
|
+
children: f,
|
|
19
|
+
testId: y,
|
|
20
|
+
...g
|
|
21
|
+
}, m) {
|
|
22
|
+
const [v, N] = C(null), [o, u] = C(-1), p = A([]), I = A(null), h = n !== void 0 ? n : v, b = (e) => {
|
|
23
|
+
n === void 0 && N(e), c?.(e);
|
|
24
|
+
}, w = F((e) => {
|
|
25
|
+
const t = p.current.indexOf(e);
|
|
26
|
+
return t === -1 ? (p.current.push(e), p.current.length - 1) : t;
|
|
27
|
+
}, []), D = F((e) => {
|
|
32
28
|
const t = I.current?.querySelectorAll('[role="menuitem"]:not(:disabled)');
|
|
33
29
|
if (!t?.length) return;
|
|
34
|
-
const
|
|
30
|
+
const _ = o >= 0 ? o : 0;
|
|
35
31
|
switch (e.key) {
|
|
36
32
|
case "ArrowDown": {
|
|
37
33
|
e.preventDefault();
|
|
38
|
-
const
|
|
39
|
-
|
|
34
|
+
const x = (_ + 1) % t.length;
|
|
35
|
+
u(x), t[x]?.focus();
|
|
40
36
|
break;
|
|
41
37
|
}
|
|
42
38
|
case "ArrowUp": {
|
|
43
39
|
e.preventDefault();
|
|
44
|
-
const
|
|
45
|
-
|
|
40
|
+
const x = (_ - 1 + t.length) % t.length;
|
|
41
|
+
u(x), t[x]?.focus();
|
|
46
42
|
break;
|
|
47
43
|
}
|
|
48
44
|
case "Home":
|
|
49
|
-
e.preventDefault(),
|
|
45
|
+
e.preventDefault(), u(0), t[0]?.focus();
|
|
50
46
|
break;
|
|
51
47
|
case "End":
|
|
52
|
-
e.preventDefault(),
|
|
48
|
+
e.preventDefault(), u(t.length - 1), t[t.length - 1]?.focus();
|
|
53
49
|
break;
|
|
54
50
|
}
|
|
55
|
-
}, [
|
|
56
|
-
|
|
57
|
-
flexDirection: "column",
|
|
58
|
-
gap: u[0.5],
|
|
59
|
-
padding: u[2],
|
|
60
|
-
...h
|
|
61
|
-
};
|
|
62
|
-
return /* @__PURE__ */ m(P.Provider, { value: { size: s, activeItem: $, setActiveItem: i, collapsed: r, focusedIndex: y, setFocusedIndex: o, registerItem: a }, children: /* @__PURE__ */ m(
|
|
51
|
+
}, [o]);
|
|
52
|
+
return /* @__PURE__ */ i(S.Provider, { value: { size: s, activeItem: h, setActiveItem: b, collapsed: r, focusedIndex: o, setFocusedIndex: u, registerItem: w }, children: /* @__PURE__ */ i(
|
|
63
53
|
"nav",
|
|
64
54
|
{
|
|
65
55
|
ref: (e) => {
|
|
66
|
-
I.current = e, typeof
|
|
56
|
+
I.current = e, typeof m == "function" ? m(e) : m && (m.current = e);
|
|
67
57
|
},
|
|
68
58
|
role: "menu",
|
|
69
|
-
className:
|
|
70
|
-
style:
|
|
71
|
-
onKeyDown:
|
|
72
|
-
"data-testid":
|
|
73
|
-
...
|
|
74
|
-
children:
|
|
59
|
+
className: M("brycks-menu", `brycks-menu--${s}`, r && "brycks-menu--collapsed", l),
|
|
60
|
+
style: d,
|
|
61
|
+
onKeyDown: D,
|
|
62
|
+
"data-testid": y,
|
|
63
|
+
...g,
|
|
64
|
+
children: f
|
|
75
65
|
}
|
|
76
66
|
) });
|
|
77
67
|
});
|
|
78
|
-
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}, e = () => {
|
|
87
|
-
I(v);
|
|
88
|
-
}, t = {
|
|
89
|
-
display: "flex",
|
|
90
|
-
alignItems: "center",
|
|
91
|
-
gap: H.lg,
|
|
92
|
-
width: "100%",
|
|
93
|
-
minHeight: a.height,
|
|
94
|
-
padding: o ? a.padding : `0 ${a.padding}px`,
|
|
95
|
-
fontSize: a.fontSize,
|
|
96
|
-
fontWeight: i ? 600 : 500,
|
|
97
|
-
color: r ? "var(--brycks-foreground-disabled)" : i ? "var(--brycks-primary-default)" : "var(--brycks-foreground-default)",
|
|
98
|
-
backgroundColor: i ? "var(--brycks-primary-muted)" : "transparent",
|
|
99
|
-
border: "none",
|
|
100
|
-
borderRadius: "var(--brycks-radius-md)",
|
|
101
|
-
cursor: r ? "not-allowed" : "pointer",
|
|
102
|
-
transition: `all ${X.quick}ms ${Y.easeOut}`,
|
|
103
|
-
textAlign: "left",
|
|
104
|
-
justifyContent: o ? "center" : "flex-start",
|
|
105
|
-
...b
|
|
106
|
-
}, M = {
|
|
107
|
-
width: a.iconSize,
|
|
108
|
-
height: a.iconSize,
|
|
109
|
-
flexShrink: 0,
|
|
110
|
-
display: "flex",
|
|
111
|
-
alignItems: "center",
|
|
112
|
-
justifyContent: "center"
|
|
113
|
-
}, d = {
|
|
114
|
-
marginLeft: "auto"
|
|
115
|
-
}, K = x >= 0 ? x === v : S ? i : v === 0;
|
|
116
|
-
return /* @__PURE__ */ E(
|
|
68
|
+
K.displayName = "Menu";
|
|
69
|
+
const q = k(function({ value: n, icon: c, badge: s, disabled: r = !1, onClick: l, className: d, style: f, children: y, ...g }, m) {
|
|
70
|
+
const { activeItem: v, setActiveItem: N, collapsed: o, focusedIndex: u, setFocusedIndex: p, registerItem: I } = G(), h = v === n, b = I(n), w = () => {
|
|
71
|
+
r || (N(n), l?.(n));
|
|
72
|
+
}, D = () => {
|
|
73
|
+
p(b);
|
|
74
|
+
}, e = u >= 0 ? u === b : v ? h : b === 0;
|
|
75
|
+
return /* @__PURE__ */ R(
|
|
117
76
|
"button",
|
|
118
77
|
{
|
|
119
|
-
ref:
|
|
78
|
+
ref: m,
|
|
120
79
|
type: "button",
|
|
121
80
|
role: "menuitem",
|
|
122
81
|
disabled: r,
|
|
123
|
-
tabIndex:
|
|
124
|
-
className:
|
|
125
|
-
style:
|
|
126
|
-
onClick:
|
|
127
|
-
onFocus:
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
},
|
|
131
|
-
onMouseLeave: (A) => {
|
|
132
|
-
i || (A.currentTarget.style.backgroundColor = "transparent");
|
|
133
|
-
},
|
|
134
|
-
title: o ? String(p) : void 0,
|
|
135
|
-
...k,
|
|
82
|
+
tabIndex: e ? 0 : -1,
|
|
83
|
+
className: M("brycks-menu-item", h && "brycks-menu-item--active", d),
|
|
84
|
+
style: f,
|
|
85
|
+
onClick: w,
|
|
86
|
+
onFocus: D,
|
|
87
|
+
title: o ? String(y) : void 0,
|
|
88
|
+
...g,
|
|
136
89
|
children: [
|
|
137
|
-
c && /* @__PURE__ */
|
|
138
|
-
!o && /* @__PURE__ */
|
|
139
|
-
!o && s && /* @__PURE__ */
|
|
90
|
+
c && /* @__PURE__ */ i("span", { className: "brycks-menu-item__icon", children: c }),
|
|
91
|
+
!o && /* @__PURE__ */ i("span", { className: "brycks-menu-item__label", children: y }),
|
|
92
|
+
!o && s && /* @__PURE__ */ i("span", { className: "brycks-menu-item__badge", children: s })
|
|
140
93
|
]
|
|
141
94
|
}
|
|
142
95
|
);
|
|
143
96
|
});
|
|
144
|
-
|
|
145
|
-
const
|
|
146
|
-
const { collapsed:
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
gap: u[0.5],
|
|
150
|
-
...s
|
|
151
|
-
}, k = {
|
|
152
|
-
padding: `${u[3]}px ${u[3]}px ${U.sm}px ${u[3]}px`,
|
|
153
|
-
fontSize: z.xs,
|
|
154
|
-
fontWeight: 600,
|
|
155
|
-
color: "var(--brycks-foreground-muted)",
|
|
156
|
-
textTransform: "uppercase",
|
|
157
|
-
letterSpacing: "0.05em"
|
|
158
|
-
};
|
|
159
|
-
return /* @__PURE__ */ E("div", { ref: h, className: w("brycks-menu-group", c), style: p, ...l, children: [
|
|
160
|
-
n && !b && /* @__PURE__ */ m("div", { style: k, children: n }),
|
|
97
|
+
q.displayName = "MenuItem";
|
|
98
|
+
const H = k(function({ label: n, className: c, style: s, children: r, ...l }, d) {
|
|
99
|
+
const { collapsed: f } = G();
|
|
100
|
+
return /* @__PURE__ */ R("div", { ref: d, className: M("brycks-menu-group", c), style: s, ...l, children: [
|
|
101
|
+
n && !f && /* @__PURE__ */ i("div", { className: "brycks-menu-group__label", children: n }),
|
|
161
102
|
r
|
|
162
103
|
] });
|
|
163
104
|
});
|
|
164
|
-
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
height: 1,
|
|
168
|
-
backgroundColor: "var(--brycks-border-muted)",
|
|
169
|
-
margin: `${u[2]}px 0`,
|
|
170
|
-
...c
|
|
171
|
-
};
|
|
172
|
-
return /* @__PURE__ */ m(
|
|
105
|
+
H.displayName = "MenuGroup";
|
|
106
|
+
const O = k(function({ className: n, style: c, ...s }, r) {
|
|
107
|
+
return /* @__PURE__ */ i(
|
|
173
108
|
"div",
|
|
174
109
|
{
|
|
175
110
|
ref: r,
|
|
176
111
|
role: "separator",
|
|
177
|
-
className:
|
|
178
|
-
style:
|
|
112
|
+
className: M("brycks-menu-divider", n),
|
|
113
|
+
style: c,
|
|
179
114
|
...s
|
|
180
115
|
}
|
|
181
116
|
);
|
|
182
117
|
});
|
|
183
|
-
|
|
118
|
+
O.displayName = "MenuDivider";
|
|
184
119
|
export {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
120
|
+
K as Menu,
|
|
121
|
+
O as MenuDivider,
|
|
122
|
+
H as MenuGroup,
|
|
123
|
+
q as MenuItem
|
|
189
124
|
};
|
|
190
125
|
//# sourceMappingURL=Menu.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Menu.js","sources":["../../../../src/components/navigation/Menu/Menu.tsx"],"sourcesContent":["/**\n * Menu Component\n *\n * A vertical navigation menu with support for nested items.\n * Suitable for sidebars and navigation panels.\n */\n\nimport {\n forwardRef,\n useState,\n useRef,\n useCallback,\n createContext,\n useContext,\n type CSSProperties,\n type ReactNode,\n type HTMLAttributes,\n type KeyboardEvent,\n} from 'react'\nimport { cx } from '../../../utils/styles'\nimport { spacing, fontSizes, durations, easings } from '../../../design-system'\nimport { componentHeights, componentGap, componentPaddingX, componentPaddingY, iconSizes } from '../../../design-system/primitives'\n\nexport type MenuSize = 'sm' | 'md' | 'lg'\n\ninterface MenuContextValue {\n size: MenuSize\n activeItem: string | null\n setActiveItem: (value: string | null) => void\n collapsed: boolean\n focusedIndex: number\n setFocusedIndex: (index: number) => void\n registerItem: (value: string) => number\n}\n\nconst MenuContext = createContext<MenuContextValue | null>(null)\n\nfunction useMenuContext() {\n const context = useContext(MenuContext)\n if (!context) {\n throw new Error('Menu components must be used within a Menu')\n }\n return context\n}\n\nexport interface MenuProps extends Omit<HTMLAttributes<HTMLElement>, 'onChange'> {\n /** Currently active item */\n activeItem?: string | null\n /** Callback when active item changes */\n onChange?: (value: string | null) => void\n /** Menu size */\n size?: MenuSize\n /** Whether the menu is collapsed (icons only) */\n collapsed?: boolean\n /** Custom class name */\n className?: string\n /** Test ID */\n testId?: string\n}\n\nexport const Menu = forwardRef<HTMLElement, MenuProps>(function Menu(\n {\n activeItem: controlledActiveItem,\n onChange,\n size = 'md',\n collapsed = false,\n className,\n style,\n children,\n testId,\n ...props\n },\n ref\n) {\n const [internalActiveItem, setInternalActiveItem] = useState<string | null>(null)\n const [focusedIndex, setFocusedIndex] = useState(-1)\n const itemsRef = useRef<string[]>([])\n const menuRef = useRef<HTMLElement>(null)\n const activeItem = controlledActiveItem !== undefined ? controlledActiveItem : internalActiveItem\n\n const setActiveItem = (value: string | null) => {\n if (controlledActiveItem === undefined) {\n setInternalActiveItem(value)\n }\n onChange?.(value)\n }\n\n // Register items for keyboard navigation\n const registerItem = useCallback((value: string) => {\n const index = itemsRef.current.indexOf(value)\n if (index === -1) {\n itemsRef.current.push(value)\n return itemsRef.current.length - 1\n }\n return index\n }, [])\n\n // Keyboard navigation\n const handleKeyDown = useCallback((e: KeyboardEvent<HTMLElement>) => {\n const items = menuRef.current?.querySelectorAll<HTMLButtonElement>('[role=\"menuitem\"]:not(:disabled)')\n if (!items?.length) return\n\n const currentIndex = focusedIndex >= 0 ? focusedIndex : 0\n\n switch (e.key) {\n case 'ArrowDown': {\n e.preventDefault()\n const nextIndex = (currentIndex + 1) % items.length\n setFocusedIndex(nextIndex)\n items[nextIndex]?.focus()\n break\n }\n case 'ArrowUp': {\n e.preventDefault()\n const prevIndex = (currentIndex - 1 + items.length) % items.length\n setFocusedIndex(prevIndex)\n items[prevIndex]?.focus()\n break\n }\n case 'Home':\n e.preventDefault()\n setFocusedIndex(0)\n items[0]?.focus()\n break\n case 'End':\n e.preventDefault()\n setFocusedIndex(items.length - 1)\n items[items.length - 1]?.focus()\n break\n }\n }, [focusedIndex])\n\n const menuStyle: CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n gap: spacing[0.5],\n padding: spacing[2],\n ...style,\n }\n\n return (\n <MenuContext.Provider value={{ size, activeItem, setActiveItem, collapsed, focusedIndex, setFocusedIndex, registerItem }}>\n <nav\n ref={(node) => {\n (menuRef as React.MutableRefObject<HTMLElement | null>).current = node\n if (typeof ref === 'function') ref(node)\n else if (ref) (ref as React.MutableRefObject<HTMLElement | null>).current = node\n }}\n role=\"menu\"\n className={cx('brycks-menu', `brycks-menu--${size}`, collapsed && 'brycks-menu--collapsed', className)}\n style={menuStyle}\n onKeyDown={handleKeyDown}\n data-testid={testId}\n {...props}\n >\n {children}\n </nav>\n </MenuContext.Provider>\n )\n})\n\nMenu.displayName = 'Menu'\n\n// MenuItem\nexport interface MenuItemProps extends Omit<HTMLAttributes<HTMLButtonElement>, 'onClick'> {\n /** Item value (unique identifier) */\n value: string\n /** Icon before the label */\n icon?: ReactNode\n /** Badge/count after the label */\n badge?: ReactNode\n /** Whether the item is disabled */\n disabled?: boolean\n /** Callback when clicked */\n onClick?: (value: string) => void\n /** Custom class name */\n className?: string\n}\n\nconst sizeConfig: Record<MenuSize, { height: number; fontSize: number; iconSize: number; padding: number }> = {\n sm: { height: componentHeights.sm, fontSize: fontSizes.base - 1, iconSize: iconSizes.sm, padding: componentGap.lg },\n md: { height: componentHeights.md, fontSize: fontSizes.base, iconSize: iconSizes.md - 2, padding: componentPaddingX.sm },\n lg: { height: componentHeights.lg, fontSize: fontSizes.md, iconSize: iconSizes.md, padding: spacing[3.5] },\n}\n\nexport const MenuItem = forwardRef<HTMLButtonElement, MenuItemProps>(function MenuItem(\n { value, icon, badge, disabled = false, onClick, className, style, children, ...props },\n ref\n) {\n const { size, activeItem, setActiveItem, collapsed, focusedIndex, setFocusedIndex, registerItem } = useMenuContext()\n const isActive = activeItem === value\n const config = sizeConfig[size]\n const itemIndex = registerItem(value)\n\n const handleClick = () => {\n if (disabled) return\n setActiveItem(value)\n onClick?.(value)\n }\n\n const handleFocus = () => {\n setFocusedIndex(itemIndex)\n }\n\n const itemStyle: CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n gap: componentGap.lg,\n width: '100%',\n minHeight: config.height,\n padding: collapsed ? config.padding : `0 ${config.padding}px`,\n fontSize: config.fontSize,\n fontWeight: isActive ? 600 : 500,\n color: disabled\n ? 'var(--brycks-foreground-disabled)'\n : isActive\n ? 'var(--brycks-primary-default)'\n : 'var(--brycks-foreground-default)',\n backgroundColor: isActive ? 'var(--brycks-primary-muted)' : 'transparent',\n border: 'none',\n borderRadius: 'var(--brycks-radius-md)',\n cursor: disabled ? 'not-allowed' : 'pointer',\n transition: `all ${durations.quick}ms ${easings.easeOut}`,\n textAlign: 'left',\n justifyContent: collapsed ? 'center' : 'flex-start',\n ...style,\n }\n\n const iconStyle: CSSProperties = {\n width: config.iconSize,\n height: config.iconSize,\n flexShrink: 0,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n }\n\n const badgeStyle: CSSProperties = {\n marginLeft: 'auto',\n }\n\n // Roving tabindex: before any item is focused (focusedIndex -1), Tab must\n // still be able to enter the menu through the active item or the first one.\n const isTabStop =\n focusedIndex >= 0 ? focusedIndex === itemIndex : activeItem ? isActive : itemIndex === 0\n\n return (\n <button\n ref={ref}\n type=\"button\"\n role=\"menuitem\"\n disabled={disabled}\n tabIndex={isTabStop ? 0 : -1}\n className={cx('brycks-menu-item', isActive && 'brycks-menu-item--active', className)}\n style={itemStyle}\n onClick={handleClick}\n onFocus={handleFocus}\n onMouseEnter={(e) => {\n if (!disabled && !isActive) {\n e.currentTarget.style.backgroundColor = 'var(--brycks-background-muted)'\n }\n }}\n onMouseLeave={(e) => {\n if (!isActive) {\n e.currentTarget.style.backgroundColor = 'transparent'\n }\n }}\n title={collapsed ? String(children) : undefined}\n {...props}\n >\n {icon && <span style={iconStyle}>{icon}</span>}\n {!collapsed && <span style={{ flex: 1 }}>{children}</span>}\n {!collapsed && badge && <span style={badgeStyle}>{badge}</span>}\n </button>\n )\n})\n\nMenuItem.displayName = 'MenuItem'\n\n// MenuGroup\nexport interface MenuGroupProps extends HTMLAttributes<HTMLDivElement> {\n /** Group label */\n label?: string\n /** Custom class name */\n className?: string\n}\n\nexport const MenuGroup = forwardRef<HTMLDivElement, MenuGroupProps>(function MenuGroup(\n { label, className, style, children, ...props },\n ref\n) {\n const { collapsed } = useMenuContext()\n\n const groupStyle: CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n gap: spacing[0.5],\n ...style,\n }\n\n const labelStyle: CSSProperties = {\n padding: `${spacing[3]}px ${spacing[3]}px ${componentPaddingY.sm}px ${spacing[3]}px`,\n fontSize: fontSizes.xs,\n fontWeight: 600,\n color: 'var(--brycks-foreground-muted)',\n textTransform: 'uppercase',\n letterSpacing: '0.05em',\n }\n\n return (\n <div ref={ref} className={cx('brycks-menu-group', className)} style={groupStyle} {...props}>\n {label && !collapsed && <div style={labelStyle}>{label}</div>}\n {children}\n </div>\n )\n})\n\nMenuGroup.displayName = 'MenuGroup'\n\n// MenuDivider\nexport interface MenuDividerProps extends HTMLAttributes<HTMLDivElement> {\n /** Custom class name */\n className?: string\n}\n\nexport const MenuDivider = forwardRef<HTMLDivElement, MenuDividerProps>(function MenuDivider(\n { className, style, ...props },\n ref\n) {\n const dividerStyle: CSSProperties = {\n height: 1,\n backgroundColor: 'var(--brycks-border-muted)',\n margin: `${spacing[2]}px 0`,\n ...style,\n }\n\n return (\n <div\n ref={ref}\n role=\"separator\"\n className={cx('brycks-menu-divider', className)}\n style={dividerStyle}\n {...props}\n />\n )\n})\n\nMenuDivider.displayName = 'MenuDivider'\n"],"names":["MenuContext","createContext","useMenuContext","context","useContext","Menu","forwardRef","controlledActiveItem","onChange","size","collapsed","className","style","children","testId","props","ref","internalActiveItem","setInternalActiveItem","useState","focusedIndex","setFocusedIndex","itemsRef","useRef","menuRef","activeItem","setActiveItem","value","registerItem","useCallback","index","handleKeyDown","items","currentIndex","nextIndex","prevIndex","menuStyle","spacing","jsx","node","cx","sizeConfig","componentHeights","fontSizes","iconSizes","componentGap","componentPaddingX","MenuItem","icon","badge","disabled","onClick","isActive","config","itemIndex","handleClick","handleFocus","itemStyle","durations","easings","iconStyle","badgeStyle","isTabStop","jsxs","e","MenuGroup","label","groupStyle","labelStyle","componentPaddingY","MenuDivider","dividerStyle"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Menu.js","sources":["../../../../src/components/navigation/Menu/Menu.tsx"],"sourcesContent":["/**\n * Menu Component\n *\n * A vertical navigation menu with support for nested items.\n * Suitable for sidebars and navigation panels.\n */\n\nimport {\n forwardRef,\n useState,\n useRef,\n useCallback,\n createContext,\n useContext,\n type ReactNode,\n type HTMLAttributes,\n type KeyboardEvent,\n} from 'react'\nimport { cx } from '../../../utils/styles'\n\nexport type MenuSize = 'sm' | 'md' | 'lg'\n\ninterface MenuContextValue {\n size: MenuSize\n activeItem: string | null\n setActiveItem: (value: string | null) => void\n collapsed: boolean\n focusedIndex: number\n setFocusedIndex: (index: number) => void\n registerItem: (value: string) => number\n}\n\nconst MenuContext = createContext<MenuContextValue | null>(null)\n\nfunction useMenuContext() {\n const context = useContext(MenuContext)\n if (!context) {\n throw new Error('Menu components must be used within a Menu')\n }\n return context\n}\n\nexport interface MenuProps extends Omit<HTMLAttributes<HTMLElement>, 'onChange'> {\n /** Currently active item */\n activeItem?: string | null\n /** Callback when active item changes */\n onChange?: (value: string | null) => void\n /** Menu size */\n size?: MenuSize\n /** Whether the menu is collapsed (icons only) */\n collapsed?: boolean\n /** Custom class name */\n className?: string\n /** Test ID */\n testId?: string\n}\n\nexport const Menu = forwardRef<HTMLElement, MenuProps>(function Menu(\n {\n activeItem: controlledActiveItem,\n onChange,\n size = 'md',\n collapsed = false,\n className,\n style,\n children,\n testId,\n ...props\n },\n ref\n) {\n const [internalActiveItem, setInternalActiveItem] = useState<string | null>(null)\n const [focusedIndex, setFocusedIndex] = useState(-1)\n const itemsRef = useRef<string[]>([])\n const menuRef = useRef<HTMLElement>(null)\n const activeItem = controlledActiveItem !== undefined ? controlledActiveItem : internalActiveItem\n\n const setActiveItem = (value: string | null) => {\n if (controlledActiveItem === undefined) {\n setInternalActiveItem(value)\n }\n onChange?.(value)\n }\n\n // Register items for keyboard navigation\n const registerItem = useCallback((value: string) => {\n const index = itemsRef.current.indexOf(value)\n if (index === -1) {\n itemsRef.current.push(value)\n return itemsRef.current.length - 1\n }\n return index\n }, [])\n\n // Keyboard navigation\n const handleKeyDown = useCallback((e: KeyboardEvent<HTMLElement>) => {\n const items = menuRef.current?.querySelectorAll<HTMLButtonElement>('[role=\"menuitem\"]:not(:disabled)')\n if (!items?.length) return\n\n const currentIndex = focusedIndex >= 0 ? focusedIndex : 0\n\n switch (e.key) {\n case 'ArrowDown': {\n e.preventDefault()\n const nextIndex = (currentIndex + 1) % items.length\n setFocusedIndex(nextIndex)\n items[nextIndex]?.focus()\n break\n }\n case 'ArrowUp': {\n e.preventDefault()\n const prevIndex = (currentIndex - 1 + items.length) % items.length\n setFocusedIndex(prevIndex)\n items[prevIndex]?.focus()\n break\n }\n case 'Home':\n e.preventDefault()\n setFocusedIndex(0)\n items[0]?.focus()\n break\n case 'End':\n e.preventDefault()\n setFocusedIndex(items.length - 1)\n items[items.length - 1]?.focus()\n break\n }\n }, [focusedIndex])\n\n return (\n <MenuContext.Provider value={{ size, activeItem, setActiveItem, collapsed, focusedIndex, setFocusedIndex, registerItem }}>\n <nav\n ref={(node) => {\n (menuRef as React.MutableRefObject<HTMLElement | null>).current = node\n if (typeof ref === 'function') ref(node)\n else if (ref) (ref as React.MutableRefObject<HTMLElement | null>).current = node\n }}\n role=\"menu\"\n className={cx('brycks-menu', `brycks-menu--${size}`, collapsed && 'brycks-menu--collapsed', className)}\n style={style}\n onKeyDown={handleKeyDown}\n data-testid={testId}\n {...props}\n >\n {children}\n </nav>\n </MenuContext.Provider>\n )\n})\n\nMenu.displayName = 'Menu'\n\n// MenuItem\nexport interface MenuItemProps extends Omit<HTMLAttributes<HTMLButtonElement>, 'onClick'> {\n /** Item value (unique identifier) */\n value: string\n /** Icon before the label */\n icon?: ReactNode\n /** Badge/count after the label */\n badge?: ReactNode\n /** Whether the item is disabled */\n disabled?: boolean\n /** Callback when clicked */\n onClick?: (value: string) => void\n /** Custom class name */\n className?: string\n}\n\nexport const MenuItem = forwardRef<HTMLButtonElement, MenuItemProps>(function MenuItem(\n { value, icon, badge, disabled = false, onClick, className, style, children, ...props },\n ref\n) {\n const { activeItem, setActiveItem, collapsed, focusedIndex, setFocusedIndex, registerItem } = useMenuContext()\n const isActive = activeItem === value\n const itemIndex = registerItem(value)\n\n const handleClick = () => {\n if (disabled) return\n setActiveItem(value)\n onClick?.(value)\n }\n\n const handleFocus = () => {\n setFocusedIndex(itemIndex)\n }\n\n // Roving tabindex: before any item is focused (focusedIndex -1), Tab must\n // still be able to enter the menu through the active item or the first one.\n const isTabStop =\n focusedIndex >= 0 ? focusedIndex === itemIndex : activeItem ? isActive : itemIndex === 0\n\n return (\n <button\n ref={ref}\n type=\"button\"\n role=\"menuitem\"\n disabled={disabled}\n tabIndex={isTabStop ? 0 : -1}\n className={cx('brycks-menu-item', isActive && 'brycks-menu-item--active', className)}\n style={style}\n onClick={handleClick}\n onFocus={handleFocus}\n title={collapsed ? String(children) : undefined}\n {...props}\n >\n {icon && <span className=\"brycks-menu-item__icon\">{icon}</span>}\n {!collapsed && <span className=\"brycks-menu-item__label\">{children}</span>}\n {!collapsed && badge && <span className=\"brycks-menu-item__badge\">{badge}</span>}\n </button>\n )\n})\n\nMenuItem.displayName = 'MenuItem'\n\n// MenuGroup\nexport interface MenuGroupProps extends HTMLAttributes<HTMLDivElement> {\n /** Group label */\n label?: string\n /** Custom class name */\n className?: string\n}\n\nexport const MenuGroup = forwardRef<HTMLDivElement, MenuGroupProps>(function MenuGroup(\n { label, className, style, children, ...props },\n ref\n) {\n const { collapsed } = useMenuContext()\n\n return (\n <div ref={ref} className={cx('brycks-menu-group', className)} style={style} {...props}>\n {label && !collapsed && <div className=\"brycks-menu-group__label\">{label}</div>}\n {children}\n </div>\n )\n})\n\nMenuGroup.displayName = 'MenuGroup'\n\n// MenuDivider\nexport interface MenuDividerProps extends HTMLAttributes<HTMLDivElement> {\n /** Custom class name */\n className?: string\n}\n\nexport const MenuDivider = forwardRef<HTMLDivElement, MenuDividerProps>(function MenuDivider(\n { className, style, ...props },\n ref\n) {\n return (\n <div\n ref={ref}\n role=\"separator\"\n className={cx('brycks-menu-divider', className)}\n style={style}\n {...props}\n />\n )\n})\n\nMenuDivider.displayName = 'MenuDivider'\n"],"names":["MenuContext","createContext","useMenuContext","context","useContext","Menu","forwardRef","controlledActiveItem","onChange","size","collapsed","className","style","children","testId","props","ref","internalActiveItem","setInternalActiveItem","useState","focusedIndex","setFocusedIndex","itemsRef","useRef","menuRef","activeItem","setActiveItem","value","registerItem","useCallback","index","handleKeyDown","items","currentIndex","nextIndex","prevIndex","jsx","node","cx","MenuItem","icon","badge","disabled","onClick","isActive","itemIndex","handleClick","handleFocus","isTabStop","jsxs","MenuGroup","label","MenuDivider"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const n=require("react/jsx-runtime"),k=require("react"),x=require("../../../utils/styles.cjs"),w={navigation:"Pagination",first:"First page",previous:"Previous page",next:"Next page",last:"Last page",page:u=>`Page ${u}`,of:(u,t)=>`Page ${u} of ${t}`};function R(){return n.jsx("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",children:n.jsx("path",{d:"M10 12L6 8l4-4",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round"})})}function B(){return n.jsx("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",children:n.jsx("path",{d:"M6 4l4 4-4 4",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round"})})}function I(){return n.jsx("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",children:n.jsx("path",{d:"M11 12l-4-4 4-4M7 12l-4-4 4-4",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round"})})}function S(){return n.jsx("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",children:n.jsx("path",{d:"M5 4l4 4-4 4M9 4l4 4-4 4",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round"})})}const m=k.forwardRef(function({page:t,totalPages:e,onChange:j,size:y="md",variant:o="default",siblingCount:c=1,showFirstLast:p=!0,showPrevNext:d=!0,disabled:l=!1,labels:_,className:g,style:v,testId:L,...M},C){const r={...w,..._},N=k.useMemo(()=>{if(o==="minimal"||o==="simple")return[];const i=[],h=Math.max(t-c,1),b=Math.min(t+c,e),$=h>2,f=b<e-1;if($)if(f){i.push(1),i.push("ellipsis");for(let s=h;s<=b;s++)i.push(s);i.push("ellipsis"),i.push(e)}else{i.push(1),i.push("ellipsis");for(let s=Math.max(e-2-c,1);s<=e;s++)i.push(s)}else{for(let s=1;s<=Math.min(3+c,e);s++)i.push(s);f&&(i.push("ellipsis"),i.push(e))}return i},[t,e,c,o]),a=i=>{i>=1&&i<=e&&i!==t&&!l&&j(i)};return n.jsxs("nav",{ref:C,role:"navigation","aria-label":r.navigation,className:x.cx("brycks-pagination",`brycks-pagination--${y}`,`brycks-pagination--${o}`,g),style:v,"data-testid":L,...M,children:[p&&o!=="minimal"&&n.jsx("button",{type:"button","aria-label":r.first,disabled:t===1||l,className:"brycks-pagination__button",onClick:()=>a(1),children:n.jsx(I,{})}),d&&n.jsx("button",{type:"button","aria-label":r.previous,disabled:t===1||l,className:"brycks-pagination__button",onClick:()=>a(t-1),children:n.jsx(R,{})}),o==="simple"||o==="minimal"?n.jsx("span",{className:"brycks-pagination__info",children:o==="minimal"?`${t} / ${e}`:r.of(t,e)}):N.map((i,h)=>i==="ellipsis"?n.jsx("span",{className:"brycks-pagination__ellipsis",children:"..."},`ellipsis-${h}`):n.jsx("button",{type:"button","aria-label":r.page(i),"aria-current":i===t?"page":void 0,disabled:l,className:x.cx("brycks-pagination__button",i===t&&"brycks-pagination__button--active"),onClick:()=>a(i),children:i},i)),d&&n.jsx("button",{type:"button","aria-label":r.next,disabled:t===e||l,className:"brycks-pagination__button",onClick:()=>a(t+1),children:n.jsx(B,{})}),p&&o!=="minimal"&&n.jsx("button",{type:"button","aria-label":r.last,disabled:t===e||l,className:"brycks-pagination__button",onClick:()=>a(e),children:n.jsx(S,{})})]})});m.displayName="Pagination";exports.Pagination=m;
|
|
2
2
|
//# sourceMappingURL=Pagination.cjs.map
|