@brycks/core-front 0.2.9 → 0.3.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/feedback/Drawer/Drawer.cjs +2 -2
- package/dist/components/feedback/Drawer/Drawer.cjs.map +1 -1
- package/dist/components/feedback/Drawer/Drawer.js +78 -74
- 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 +76 -70
- package/dist/components/feedback/Modal/Modal.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("react/jsx-runtime"),
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("react/jsx-runtime"),t=require("react"),X=require("react-dom"),Y=require("../../../utils/styles.cjs"),K={sm:"320px",md:"400px",lg:"560px",xl:"720px",full:"100%"};function W(){return r.jsx("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",children:r.jsx("path",{d:"M4 4l8 8M12 4l-8 8",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round"})})}const x=t.forwardRef(function({isOpen:s,onClose:n,placement:o="right",size:u="md",title:c,closeOnOverlayClick:f=!0,closeOnEscape:y=!0,showCloseButton:b=!0,showOverlay:p=!0,className:j,style:S,children:E,testId:D,...R},l){const i=t.useRef(null),m=t.useRef(null),k=t.useRef(n);t.useEffect(()=>{k.current=n},[n]),t.useEffect(()=>{if(!s)return;m.current=document.activeElement;const e=i.current;e&&e.focus()},[s]),t.useEffect(()=>{if(!s)return;const e=i.current,v=a=>{if(a.key==="Escape"&&y){k.current();return}if(a.key==="Tab"&&e){const d=e.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'),h=d[0],g=d[d.length-1];a.shiftKey&&document.activeElement===h?(a.preventDefault(),g?.focus()):!a.shiftKey&&document.activeElement===g&&(a.preventDefault(),h?.focus())}};return document.addEventListener("keydown",v),document.body.style.overflow="hidden",()=>{document.removeEventListener("keydown",v),document.body.style.overflow="",m.current?.focus()}},[s,y]);const C=t.useCallback(e=>{e.target===e.currentTarget&&f&&n()},[f,n]);if(!s)return null;const T=o==="left"||o==="right",w=K[u],z={position:"fixed",inset:0,zIndex:"var(--brycks-z-drawer)",display:"flex",backgroundColor:p?"var(--brycks-background-overlay)":"transparent",animation:"brycks-drawer-overlay-in 200ms ease-out"},M={...(()=>{const e={position:"absolute",backgroundColor:"var(--brycks-background-elevated)",boxShadow:"var(--brycks-shadow-2xl)",display:"flex",flexDirection:"column",overflow:"hidden"};return T?{...e,top:0,bottom:0,[o]:0,width:w,maxWidth:"100vw"}:{...e,left:0,right:0,[o]:0,height:w,maxHeight:"100vh"}})(),animation:`brycks-drawer-${o}-in 250ms cubic-bezier(0.32, 0.72, 0, 1)`,outline:"none",...S},N={display:"flex",alignItems:"center",justifyContent:"space-between",padding:"16px 20px",borderBottom:"1px solid var(--brycks-border-muted)",flexShrink:0},q={fontSize:18,fontWeight:600,color:"var(--brycks-foreground-default)",margin:0},I={display:"flex",alignItems:"center",justifyContent:"center",width:32,height:32,borderRadius:"var(--brycks-radius-md)",color:"var(--brycks-foreground-muted)",transition:"all 150ms ease-out"},L={flex:1,overflow:"auto",padding:20},P=r.jsxs(r.Fragment,{children:[r.jsx("style",{children:`
|
|
2
2
|
@keyframes brycks-drawer-overlay-in {
|
|
3
3
|
from { opacity: 0; }
|
|
4
4
|
to { opacity: 1; }
|
|
@@ -19,5 +19,5 @@
|
|
|
19
19
|
from { transform: translateY(100%); }
|
|
20
20
|
to { transform: translateY(0); }
|
|
21
21
|
}
|
|
22
|
-
`}),r.jsx("div",{className:"brycks-drawer-overlay",style:
|
|
22
|
+
`}),r.jsx("div",{className:"brycks-drawer-overlay",style:z,onClick:C,"aria-hidden":"true",children:r.jsxs("div",{ref:e=>{i.current=e,typeof l=="function"?l(e):l&&(l.current=e)},role:"dialog","aria-modal":"true","aria-labelledby":c?"brycks-drawer-title":void 0,className:Y.cx("brycks-drawer",`brycks-drawer--${o}`,`brycks-drawer--${u}`,j),style:M,tabIndex:-1,"data-testid":D,...R,children:[(c||b)&&r.jsxs("div",{className:"brycks-drawer-header",style:N,children:[c&&r.jsx("h2",{id:"brycks-drawer-title",style:q,children:c}),b&&r.jsx("button",{type:"button",className:"brycks-drawer-close",style:I,onClick:n,"aria-label":"Close drawer",onMouseEnter:e=>{e.currentTarget.style.backgroundColor="var(--brycks-background-muted)",e.currentTarget.style.color="var(--brycks-foreground-default)"},onMouseLeave:e=>{e.currentTarget.style.backgroundColor="transparent",e.currentTarget.style.color="var(--brycks-foreground-muted)"},children:r.jsx(W,{})})]}),r.jsx("div",{className:"brycks-drawer-content",style:L,children:E})]})})]});return X.createPortal(P,document.body)});x.displayName="Drawer";exports.Drawer=x;
|
|
23
23
|
//# sourceMappingURL=Drawer.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Drawer.cjs","sources":["../../../../src/components/feedback/Drawer/Drawer.tsx"],"sourcesContent":["/**\n * Drawer Component\n *\n * A slide-out panel that can appear from any edge of the screen.\n * Supports focus trapping and keyboard navigation.\n */\n\nimport {\n forwardRef,\n useEffect,\n useRef,\n useCallback,\n type CSSProperties,\n type ReactNode,\n type HTMLAttributes,\n} from 'react'\nimport { createPortal } from 'react-dom'\nimport { cx } from '../../../utils/styles'\n\nexport type DrawerPlacement = 'left' | 'right' | 'top' | 'bottom'\nexport type DrawerSize = 'sm' | 'md' | 'lg' | 'xl' | 'full'\n\nexport interface DrawerProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {\n /** Whether the drawer is open */\n isOpen: boolean\n /** Callback when drawer should close */\n onClose: () => void\n /** Drawer placement */\n placement?: DrawerPlacement\n /** Drawer size */\n size?: DrawerSize\n /** Drawer title */\n title?: ReactNode\n /** Whether to close on overlay click */\n closeOnOverlayClick?: boolean\n /** Whether to close on escape key */\n closeOnEscape?: boolean\n /** Whether to show close button */\n showCloseButton?: boolean\n /** Whether to show overlay */\n showOverlay?: boolean\n /** Custom class name */\n className?: string\n /** Test ID */\n testId?: string\n}\n\nconst sizeMap: Record<DrawerSize, string> = {\n sm: '320px',\n md: '400px',\n lg: '560px',\n xl: '720px',\n full: '100%',\n}\n\nfunction CloseIcon() {\n return (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\">\n <path d=\"M4 4l8 8M12 4l-8 8\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" />\n </svg>\n )\n}\n\nexport const Drawer = forwardRef<HTMLDivElement, DrawerProps>(function Drawer(\n {\n isOpen,\n onClose,\n placement = 'right',\n size = 'md',\n title,\n closeOnOverlayClick = true,\n closeOnEscape = true,\n showCloseButton = true,\n showOverlay = true,\n className,\n style,\n children,\n testId,\n ...props\n },\n ref\n) {\n const drawerRef = useRef<HTMLDivElement>(null)\n const previousActiveElement = useRef<HTMLElement | null>(null)\n\n // Focus trap and keyboard handling\n useEffect(() => {\n if (!isOpen) return\n\n previousActiveElement.current = document.activeElement as HTMLElement\n const drawer = drawerRef.current\n if (drawer) {\n drawer.focus()\n }\n\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape' && closeOnEscape) {\n onClose()\n return\n }\n\n if (e.key === 'Tab' && drawer) {\n const focusableElements = drawer.querySelectorAll<HTMLElement>(\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])'\n )\n const firstElement = focusableElements[0]\n const lastElement = focusableElements[focusableElements.length - 1]\n\n if (e.shiftKey && document.activeElement === firstElement) {\n e.preventDefault()\n lastElement?.focus()\n } else if (!e.shiftKey && document.activeElement === lastElement) {\n e.preventDefault()\n firstElement?.focus()\n }\n }\n }\n\n document.addEventListener('keydown', handleKeyDown)\n document.body.style.overflow = 'hidden'\n\n return () => {\n document.removeEventListener('keydown', handleKeyDown)\n document.body.style.overflow = ''\n previousActiveElement.current?.focus()\n }\n }, [isOpen, closeOnEscape, onClose])\n\n const handleOverlayClick = useCallback(\n (e: React.MouseEvent) => {\n if (e.target === e.currentTarget && closeOnOverlayClick) {\n onClose()\n }\n },\n [closeOnOverlayClick, onClose]\n )\n\n if (!isOpen) return null\n\n const isHorizontal = placement === 'left' || placement === 'right'\n const drawerSize = sizeMap[size]\n\n const overlayStyle: CSSProperties = {\n position: 'fixed',\n inset: 0,\n zIndex: 'var(--brycks-z-drawer)' as unknown as number,\n display: 'flex',\n backgroundColor: showOverlay ? 'var(--brycks-background-overlay)' : 'transparent',\n animation: 'brycks-drawer-overlay-in 200ms ease-out',\n }\n\n const getPositionStyles = (): CSSProperties => {\n const base: CSSProperties = {\n position: 'absolute',\n backgroundColor: 'var(--brycks-background-elevated)',\n boxShadow: 'var(--brycks-shadow-2xl)',\n display: 'flex',\n flexDirection: 'column',\n overflow: 'hidden',\n }\n\n if (isHorizontal) {\n return {\n ...base,\n top: 0,\n bottom: 0,\n [placement]: 0,\n width: drawerSize,\n maxWidth: '100vw',\n }\n }\n\n return {\n ...base,\n left: 0,\n right: 0,\n [placement]: 0,\n height: drawerSize,\n maxHeight: '100vh',\n }\n }\n\n const drawerStyle: CSSProperties = {\n ...getPositionStyles(),\n animation: `brycks-drawer-${placement}-in 250ms cubic-bezier(0.32, 0.72, 0, 1)`,\n outline: 'none',\n ...style,\n }\n\n const headerStyle: CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: '16px 20px',\n borderBottom: '1px solid var(--brycks-border-muted)',\n flexShrink: 0,\n }\n\n const titleStyle: CSSProperties = {\n fontSize: 18,\n fontWeight: 600,\n color: 'var(--brycks-foreground-default)',\n margin: 0,\n }\n\n const closeButtonStyle: CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: 32,\n height: 32,\n borderRadius: 'var(--brycks-radius-md)',\n color: 'var(--brycks-foreground-muted)',\n transition: 'all 150ms ease-out',\n }\n\n const contentStyle: CSSProperties = {\n flex: 1,\n overflow: 'auto',\n padding: 20,\n }\n\n const drawerContent = (\n <>\n <style>\n {`\n @keyframes brycks-drawer-overlay-in {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n @keyframes brycks-drawer-left-in {\n from { transform: translateX(-100%); }\n to { transform: translateX(0); }\n }\n @keyframes brycks-drawer-right-in {\n from { transform: translateX(100%); }\n to { transform: translateX(0); }\n }\n @keyframes brycks-drawer-top-in {\n from { transform: translateY(-100%); }\n to { transform: translateY(0); }\n }\n @keyframes brycks-drawer-bottom-in {\n from { transform: translateY(100%); }\n to { transform: translateY(0); }\n }\n `}\n </style>\n <div\n className=\"brycks-drawer-overlay\"\n style={overlayStyle}\n onClick={handleOverlayClick}\n aria-hidden=\"true\"\n >\n <div\n ref={(node) => {\n (drawerRef as React.MutableRefObject<HTMLDivElement | null>).current = node\n if (typeof ref === 'function') ref(node)\n else if (ref) ref.current = node\n }}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={title ? 'brycks-drawer-title' : undefined}\n className={cx('brycks-drawer', `brycks-drawer--${placement}`, `brycks-drawer--${size}`, className)}\n style={drawerStyle}\n tabIndex={-1}\n data-testid={testId}\n {...props}\n >\n {(title || showCloseButton) && (\n <div className=\"brycks-drawer-header\" style={headerStyle}>\n {title && <h2 id=\"brycks-drawer-title\" style={titleStyle}>{title}</h2>}\n {showCloseButton && (\n <button\n type=\"button\"\n className=\"brycks-drawer-close\"\n style={closeButtonStyle}\n onClick={onClose}\n aria-label=\"Close drawer\"\n onMouseEnter={(e) => {\n e.currentTarget.style.backgroundColor = 'var(--brycks-background-muted)'\n e.currentTarget.style.color = 'var(--brycks-foreground-default)'\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.backgroundColor = 'transparent'\n e.currentTarget.style.color = 'var(--brycks-foreground-muted)'\n }}\n >\n <CloseIcon />\n </button>\n )}\n </div>\n )}\n <div className=\"brycks-drawer-content\" style={contentStyle}>\n {children}\n </div>\n </div>\n </div>\n </>\n )\n\n return createPortal(drawerContent, document.body)\n})\n\nDrawer.displayName = 'Drawer'\n"],"names":["sizeMap","CloseIcon","jsx","Drawer","forwardRef","isOpen","onClose","placement","size","title","closeOnOverlayClick","closeOnEscape","showCloseButton","showOverlay","className","style","children","testId","props","ref","drawerRef","useRef","previousActiveElement","useEffect","drawer","handleKeyDown","e","focusableElements","firstElement","lastElement","handleOverlayClick","useCallback","isHorizontal","drawerSize","overlayStyle","drawerStyle","base","headerStyle","titleStyle","closeButtonStyle","contentStyle","drawerContent","jsxs","Fragment","node","cx","createPortal"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Drawer.cjs","sources":["../../../../src/components/feedback/Drawer/Drawer.tsx"],"sourcesContent":["/**\n * Drawer Component\n *\n * A slide-out panel that can appear from any edge of the screen.\n * Supports focus trapping and keyboard navigation.\n */\n\nimport {\n forwardRef,\n useEffect,\n useRef,\n useCallback,\n type CSSProperties,\n type ReactNode,\n type HTMLAttributes,\n} from 'react'\nimport { createPortal } from 'react-dom'\nimport { cx } from '../../../utils/styles'\n\nexport type DrawerPlacement = 'left' | 'right' | 'top' | 'bottom'\nexport type DrawerSize = 'sm' | 'md' | 'lg' | 'xl' | 'full'\n\nexport interface DrawerProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {\n /** Whether the drawer is open */\n isOpen: boolean\n /** Callback when drawer should close */\n onClose: () => void\n /** Drawer placement */\n placement?: DrawerPlacement\n /** Drawer size */\n size?: DrawerSize\n /** Drawer title */\n title?: ReactNode\n /** Whether to close on overlay click */\n closeOnOverlayClick?: boolean\n /** Whether to close on escape key */\n closeOnEscape?: boolean\n /** Whether to show close button */\n showCloseButton?: boolean\n /** Whether to show overlay */\n showOverlay?: boolean\n /** Custom class name */\n className?: string\n /** Test ID */\n testId?: string\n}\n\nconst sizeMap: Record<DrawerSize, string> = {\n sm: '320px',\n md: '400px',\n lg: '560px',\n xl: '720px',\n full: '100%',\n}\n\nfunction CloseIcon() {\n return (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\">\n <path d=\"M4 4l8 8M12 4l-8 8\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" />\n </svg>\n )\n}\n\nexport const Drawer = forwardRef<HTMLDivElement, DrawerProps>(function Drawer(\n {\n isOpen,\n onClose,\n placement = 'right',\n size = 'md',\n title,\n closeOnOverlayClick = true,\n closeOnEscape = true,\n showCloseButton = true,\n showOverlay = true,\n className,\n style,\n children,\n testId,\n ...props\n },\n ref\n) {\n const drawerRef = useRef<HTMLDivElement>(null)\n const previousActiveElement = useRef<HTMLElement | null>(null)\n\n // Store onClose in a ref to avoid re-running effects when it changes\n const onCloseRef = useRef(onClose)\n useEffect(() => {\n onCloseRef.current = onClose\n }, [onClose])\n\n // Focus drawer when it opens (only once)\n useEffect(() => {\n if (!isOpen) return\n\n previousActiveElement.current = document.activeElement as HTMLElement\n const drawer = drawerRef.current\n if (drawer) {\n drawer.focus()\n }\n }, [isOpen])\n\n // Keyboard handling and body scroll lock\n useEffect(() => {\n if (!isOpen) return\n\n const drawer = drawerRef.current\n\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape' && closeOnEscape) {\n onCloseRef.current()\n return\n }\n\n if (e.key === 'Tab' && drawer) {\n const focusableElements = drawer.querySelectorAll<HTMLElement>(\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])'\n )\n const firstElement = focusableElements[0]\n const lastElement = focusableElements[focusableElements.length - 1]\n\n if (e.shiftKey && document.activeElement === firstElement) {\n e.preventDefault()\n lastElement?.focus()\n } else if (!e.shiftKey && document.activeElement === lastElement) {\n e.preventDefault()\n firstElement?.focus()\n }\n }\n }\n\n document.addEventListener('keydown', handleKeyDown)\n document.body.style.overflow = 'hidden'\n\n return () => {\n document.removeEventListener('keydown', handleKeyDown)\n document.body.style.overflow = ''\n previousActiveElement.current?.focus()\n }\n }, [isOpen, closeOnEscape])\n\n const handleOverlayClick = useCallback(\n (e: React.MouseEvent) => {\n if (e.target === e.currentTarget && closeOnOverlayClick) {\n onClose()\n }\n },\n [closeOnOverlayClick, onClose]\n )\n\n if (!isOpen) return null\n\n const isHorizontal = placement === 'left' || placement === 'right'\n const drawerSize = sizeMap[size]\n\n const overlayStyle: CSSProperties = {\n position: 'fixed',\n inset: 0,\n zIndex: 'var(--brycks-z-drawer)' as unknown as number,\n display: 'flex',\n backgroundColor: showOverlay ? 'var(--brycks-background-overlay)' : 'transparent',\n animation: 'brycks-drawer-overlay-in 200ms ease-out',\n }\n\n const getPositionStyles = (): CSSProperties => {\n const base: CSSProperties = {\n position: 'absolute',\n backgroundColor: 'var(--brycks-background-elevated)',\n boxShadow: 'var(--brycks-shadow-2xl)',\n display: 'flex',\n flexDirection: 'column',\n overflow: 'hidden',\n }\n\n if (isHorizontal) {\n return {\n ...base,\n top: 0,\n bottom: 0,\n [placement]: 0,\n width: drawerSize,\n maxWidth: '100vw',\n }\n }\n\n return {\n ...base,\n left: 0,\n right: 0,\n [placement]: 0,\n height: drawerSize,\n maxHeight: '100vh',\n }\n }\n\n const drawerStyle: CSSProperties = {\n ...getPositionStyles(),\n animation: `brycks-drawer-${placement}-in 250ms cubic-bezier(0.32, 0.72, 0, 1)`,\n outline: 'none',\n ...style,\n }\n\n const headerStyle: CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: '16px 20px',\n borderBottom: '1px solid var(--brycks-border-muted)',\n flexShrink: 0,\n }\n\n const titleStyle: CSSProperties = {\n fontSize: 18,\n fontWeight: 600,\n color: 'var(--brycks-foreground-default)',\n margin: 0,\n }\n\n const closeButtonStyle: CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: 32,\n height: 32,\n borderRadius: 'var(--brycks-radius-md)',\n color: 'var(--brycks-foreground-muted)',\n transition: 'all 150ms ease-out',\n }\n\n const contentStyle: CSSProperties = {\n flex: 1,\n overflow: 'auto',\n padding: 20,\n }\n\n const drawerContent = (\n <>\n <style>\n {`\n @keyframes brycks-drawer-overlay-in {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n @keyframes brycks-drawer-left-in {\n from { transform: translateX(-100%); }\n to { transform: translateX(0); }\n }\n @keyframes brycks-drawer-right-in {\n from { transform: translateX(100%); }\n to { transform: translateX(0); }\n }\n @keyframes brycks-drawer-top-in {\n from { transform: translateY(-100%); }\n to { transform: translateY(0); }\n }\n @keyframes brycks-drawer-bottom-in {\n from { transform: translateY(100%); }\n to { transform: translateY(0); }\n }\n `}\n </style>\n <div\n className=\"brycks-drawer-overlay\"\n style={overlayStyle}\n onClick={handleOverlayClick}\n aria-hidden=\"true\"\n >\n <div\n ref={(node) => {\n (drawerRef as React.MutableRefObject<HTMLDivElement | null>).current = node\n if (typeof ref === 'function') ref(node)\n else if (ref) ref.current = node\n }}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={title ? 'brycks-drawer-title' : undefined}\n className={cx('brycks-drawer', `brycks-drawer--${placement}`, `brycks-drawer--${size}`, className)}\n style={drawerStyle}\n tabIndex={-1}\n data-testid={testId}\n {...props}\n >\n {(title || showCloseButton) && (\n <div className=\"brycks-drawer-header\" style={headerStyle}>\n {title && <h2 id=\"brycks-drawer-title\" style={titleStyle}>{title}</h2>}\n {showCloseButton && (\n <button\n type=\"button\"\n className=\"brycks-drawer-close\"\n style={closeButtonStyle}\n onClick={onClose}\n aria-label=\"Close drawer\"\n onMouseEnter={(e) => {\n e.currentTarget.style.backgroundColor = 'var(--brycks-background-muted)'\n e.currentTarget.style.color = 'var(--brycks-foreground-default)'\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.backgroundColor = 'transparent'\n e.currentTarget.style.color = 'var(--brycks-foreground-muted)'\n }}\n >\n <CloseIcon />\n </button>\n )}\n </div>\n )}\n <div className=\"brycks-drawer-content\" style={contentStyle}>\n {children}\n </div>\n </div>\n </div>\n </>\n )\n\n return createPortal(drawerContent, document.body)\n})\n\nDrawer.displayName = 'Drawer'\n"],"names":["sizeMap","CloseIcon","jsx","Drawer","forwardRef","isOpen","onClose","placement","size","title","closeOnOverlayClick","closeOnEscape","showCloseButton","showOverlay","className","style","children","testId","props","ref","drawerRef","useRef","previousActiveElement","onCloseRef","useEffect","drawer","handleKeyDown","e","focusableElements","firstElement","lastElement","handleOverlayClick","useCallback","isHorizontal","drawerSize","overlayStyle","drawerStyle","base","headerStyle","titleStyle","closeButtonStyle","contentStyle","drawerContent","jsxs","Fragment","node","cx","createPortal"],"mappings":"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"}
|
|
@@ -1,70 +1,74 @@
|
|
|
1
|
-
import { jsxs as
|
|
2
|
-
import { forwardRef as
|
|
3
|
-
import { createPortal as
|
|
4
|
-
import { cx as
|
|
5
|
-
const
|
|
1
|
+
import { jsxs as d, Fragment as P, jsx as e } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef as W, useRef as u, useEffect as f, useCallback as $ } from "react";
|
|
3
|
+
import { createPortal as A } from "react-dom";
|
|
4
|
+
import { cx as B } from "../../../utils/styles.js";
|
|
5
|
+
const H = {
|
|
6
6
|
sm: "320px",
|
|
7
7
|
md: "400px",
|
|
8
8
|
lg: "560px",
|
|
9
9
|
xl: "720px",
|
|
10
10
|
full: "100%"
|
|
11
11
|
};
|
|
12
|
-
function
|
|
12
|
+
function q() {
|
|
13
13
|
return /* @__PURE__ */ e("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ e("path", { d: "M4 4l8 8M12 4l-8 8", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) });
|
|
14
14
|
}
|
|
15
|
-
const
|
|
16
|
-
isOpen:
|
|
17
|
-
onClose:
|
|
18
|
-
placement:
|
|
19
|
-
size:
|
|
20
|
-
title:
|
|
21
|
-
closeOnOverlayClick:
|
|
22
|
-
closeOnEscape:
|
|
23
|
-
showCloseButton:
|
|
24
|
-
showOverlay:
|
|
25
|
-
className:
|
|
26
|
-
style:
|
|
27
|
-
children:
|
|
28
|
-
testId:
|
|
29
|
-
...
|
|
30
|
-
},
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
15
|
+
const F = W(function({
|
|
16
|
+
isOpen: a,
|
|
17
|
+
onClose: t,
|
|
18
|
+
placement: o = "right",
|
|
19
|
+
size: y = "md",
|
|
20
|
+
title: s,
|
|
21
|
+
closeOnOverlayClick: m = !0,
|
|
22
|
+
closeOnEscape: b = !0,
|
|
23
|
+
showCloseButton: k = !0,
|
|
24
|
+
showOverlay: S = !0,
|
|
25
|
+
className: E,
|
|
26
|
+
style: C,
|
|
27
|
+
children: D,
|
|
28
|
+
testId: z,
|
|
29
|
+
...N
|
|
30
|
+
}, l) {
|
|
31
|
+
const c = u(null), w = u(null), h = u(t);
|
|
32
|
+
f(() => {
|
|
33
|
+
h.current = t;
|
|
34
|
+
}, [t]), f(() => {
|
|
35
|
+
if (!a) return;
|
|
36
|
+
w.current = document.activeElement;
|
|
37
|
+
const r = c.current;
|
|
36
38
|
r && r.focus();
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
}, [a]), f(() => {
|
|
40
|
+
if (!a) return;
|
|
41
|
+
const r = c.current, g = (n) => {
|
|
42
|
+
if (n.key === "Escape" && b) {
|
|
43
|
+
h.current();
|
|
40
44
|
return;
|
|
41
45
|
}
|
|
42
|
-
if (
|
|
46
|
+
if (n.key === "Tab" && r) {
|
|
43
47
|
const i = r.querySelectorAll(
|
|
44
48
|
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
45
|
-
),
|
|
46
|
-
|
|
49
|
+
), x = i[0], p = i[i.length - 1];
|
|
50
|
+
n.shiftKey && document.activeElement === x ? (n.preventDefault(), p?.focus()) : !n.shiftKey && document.activeElement === p && (n.preventDefault(), x?.focus());
|
|
47
51
|
}
|
|
48
52
|
};
|
|
49
|
-
return document.addEventListener("keydown",
|
|
50
|
-
document.removeEventListener("keydown",
|
|
53
|
+
return document.addEventListener("keydown", g), document.body.style.overflow = "hidden", () => {
|
|
54
|
+
document.removeEventListener("keydown", g), document.body.style.overflow = "", w.current?.focus();
|
|
51
55
|
};
|
|
52
|
-
}, [
|
|
53
|
-
const
|
|
56
|
+
}, [a, b]);
|
|
57
|
+
const T = $(
|
|
54
58
|
(r) => {
|
|
55
|
-
r.target === r.currentTarget &&
|
|
59
|
+
r.target === r.currentTarget && m && t();
|
|
56
60
|
},
|
|
57
|
-
[
|
|
61
|
+
[m, t]
|
|
58
62
|
);
|
|
59
|
-
if (!
|
|
60
|
-
const
|
|
63
|
+
if (!a) return null;
|
|
64
|
+
const I = o === "left" || o === "right", v = H[y], M = {
|
|
61
65
|
position: "fixed",
|
|
62
66
|
inset: 0,
|
|
63
67
|
zIndex: "var(--brycks-z-drawer)",
|
|
64
68
|
display: "flex",
|
|
65
|
-
backgroundColor:
|
|
69
|
+
backgroundColor: S ? "var(--brycks-background-overlay)" : "transparent",
|
|
66
70
|
animation: "brycks-drawer-overlay-in 200ms ease-out"
|
|
67
|
-
},
|
|
71
|
+
}, R = {
|
|
68
72
|
...(() => {
|
|
69
73
|
const r = {
|
|
70
74
|
position: "absolute",
|
|
@@ -74,38 +78,38 @@ const q = K(function({
|
|
|
74
78
|
flexDirection: "column",
|
|
75
79
|
overflow: "hidden"
|
|
76
80
|
};
|
|
77
|
-
return
|
|
81
|
+
return I ? {
|
|
78
82
|
...r,
|
|
79
83
|
top: 0,
|
|
80
84
|
bottom: 0,
|
|
81
|
-
[
|
|
82
|
-
width:
|
|
85
|
+
[o]: 0,
|
|
86
|
+
width: v,
|
|
83
87
|
maxWidth: "100vw"
|
|
84
88
|
} : {
|
|
85
89
|
...r,
|
|
86
90
|
left: 0,
|
|
87
91
|
right: 0,
|
|
88
|
-
[
|
|
89
|
-
height:
|
|
92
|
+
[o]: 0,
|
|
93
|
+
height: v,
|
|
90
94
|
maxHeight: "100vh"
|
|
91
95
|
};
|
|
92
96
|
})(),
|
|
93
|
-
animation: `brycks-drawer-${
|
|
97
|
+
animation: `brycks-drawer-${o}-in 250ms cubic-bezier(0.32, 0.72, 0, 1)`,
|
|
94
98
|
outline: "none",
|
|
95
|
-
...
|
|
96
|
-
},
|
|
99
|
+
...C
|
|
100
|
+
}, j = {
|
|
97
101
|
display: "flex",
|
|
98
102
|
alignItems: "center",
|
|
99
103
|
justifyContent: "space-between",
|
|
100
104
|
padding: "16px 20px",
|
|
101
105
|
borderBottom: "1px solid var(--brycks-border-muted)",
|
|
102
106
|
flexShrink: 0
|
|
103
|
-
},
|
|
107
|
+
}, L = {
|
|
104
108
|
fontSize: 18,
|
|
105
109
|
fontWeight: 600,
|
|
106
110
|
color: "var(--brycks-foreground-default)",
|
|
107
111
|
margin: 0
|
|
108
|
-
},
|
|
112
|
+
}, X = {
|
|
109
113
|
display: "flex",
|
|
110
114
|
alignItems: "center",
|
|
111
115
|
justifyContent: "center",
|
|
@@ -114,11 +118,11 @@ const q = K(function({
|
|
|
114
118
|
borderRadius: "var(--brycks-radius-md)",
|
|
115
119
|
color: "var(--brycks-foreground-muted)",
|
|
116
120
|
transition: "all 150ms ease-out"
|
|
117
|
-
},
|
|
121
|
+
}, Y = {
|
|
118
122
|
flex: 1,
|
|
119
123
|
overflow: "auto",
|
|
120
124
|
padding: 20
|
|
121
|
-
},
|
|
125
|
+
}, K = /* @__PURE__ */ d(P, { children: [
|
|
122
126
|
/* @__PURE__ */ e("style", { children: `
|
|
123
127
|
@keyframes brycks-drawer-overlay-in {
|
|
124
128
|
from { opacity: 0; }
|
|
@@ -145,33 +149,33 @@ const q = K(function({
|
|
|
145
149
|
"div",
|
|
146
150
|
{
|
|
147
151
|
className: "brycks-drawer-overlay",
|
|
148
|
-
style:
|
|
149
|
-
onClick:
|
|
152
|
+
style: M,
|
|
153
|
+
onClick: T,
|
|
150
154
|
"aria-hidden": "true",
|
|
151
|
-
children: /* @__PURE__ */
|
|
155
|
+
children: /* @__PURE__ */ d(
|
|
152
156
|
"div",
|
|
153
157
|
{
|
|
154
158
|
ref: (r) => {
|
|
155
|
-
|
|
159
|
+
c.current = r, typeof l == "function" ? l(r) : l && (l.current = r);
|
|
156
160
|
},
|
|
157
161
|
role: "dialog",
|
|
158
162
|
"aria-modal": "true",
|
|
159
|
-
"aria-labelledby":
|
|
160
|
-
className:
|
|
161
|
-
style:
|
|
163
|
+
"aria-labelledby": s ? "brycks-drawer-title" : void 0,
|
|
164
|
+
className: B("brycks-drawer", `brycks-drawer--${o}`, `brycks-drawer--${y}`, E),
|
|
165
|
+
style: R,
|
|
162
166
|
tabIndex: -1,
|
|
163
|
-
"data-testid":
|
|
164
|
-
...
|
|
167
|
+
"data-testid": z,
|
|
168
|
+
...N,
|
|
165
169
|
children: [
|
|
166
|
-
(
|
|
167
|
-
|
|
168
|
-
|
|
170
|
+
(s || k) && /* @__PURE__ */ d("div", { className: "brycks-drawer-header", style: j, children: [
|
|
171
|
+
s && /* @__PURE__ */ e("h2", { id: "brycks-drawer-title", style: L, children: s }),
|
|
172
|
+
k && /* @__PURE__ */ e(
|
|
169
173
|
"button",
|
|
170
174
|
{
|
|
171
175
|
type: "button",
|
|
172
176
|
className: "brycks-drawer-close",
|
|
173
|
-
style:
|
|
174
|
-
onClick:
|
|
177
|
+
style: X,
|
|
178
|
+
onClick: t,
|
|
175
179
|
"aria-label": "Close drawer",
|
|
176
180
|
onMouseEnter: (r) => {
|
|
177
181
|
r.currentTarget.style.backgroundColor = "var(--brycks-background-muted)", r.currentTarget.style.color = "var(--brycks-foreground-default)";
|
|
@@ -179,21 +183,21 @@ const q = K(function({
|
|
|
179
183
|
onMouseLeave: (r) => {
|
|
180
184
|
r.currentTarget.style.backgroundColor = "transparent", r.currentTarget.style.color = "var(--brycks-foreground-muted)";
|
|
181
185
|
},
|
|
182
|
-
children: /* @__PURE__ */ e(
|
|
186
|
+
children: /* @__PURE__ */ e(q, {})
|
|
183
187
|
}
|
|
184
188
|
)
|
|
185
189
|
] }),
|
|
186
|
-
/* @__PURE__ */ e("div", { className: "brycks-drawer-content", style:
|
|
190
|
+
/* @__PURE__ */ e("div", { className: "brycks-drawer-content", style: Y, children: D })
|
|
187
191
|
]
|
|
188
192
|
}
|
|
189
193
|
)
|
|
190
194
|
}
|
|
191
195
|
)
|
|
192
196
|
] });
|
|
193
|
-
return
|
|
197
|
+
return A(K, document.body);
|
|
194
198
|
});
|
|
195
|
-
|
|
199
|
+
F.displayName = "Drawer";
|
|
196
200
|
export {
|
|
197
|
-
|
|
201
|
+
F as Drawer
|
|
198
202
|
};
|
|
199
203
|
//# sourceMappingURL=Drawer.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Drawer.js","sources":["../../../../src/components/feedback/Drawer/Drawer.tsx"],"sourcesContent":["/**\n * Drawer Component\n *\n * A slide-out panel that can appear from any edge of the screen.\n * Supports focus trapping and keyboard navigation.\n */\n\nimport {\n forwardRef,\n useEffect,\n useRef,\n useCallback,\n type CSSProperties,\n type ReactNode,\n type HTMLAttributes,\n} from 'react'\nimport { createPortal } from 'react-dom'\nimport { cx } from '../../../utils/styles'\n\nexport type DrawerPlacement = 'left' | 'right' | 'top' | 'bottom'\nexport type DrawerSize = 'sm' | 'md' | 'lg' | 'xl' | 'full'\n\nexport interface DrawerProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {\n /** Whether the drawer is open */\n isOpen: boolean\n /** Callback when drawer should close */\n onClose: () => void\n /** Drawer placement */\n placement?: DrawerPlacement\n /** Drawer size */\n size?: DrawerSize\n /** Drawer title */\n title?: ReactNode\n /** Whether to close on overlay click */\n closeOnOverlayClick?: boolean\n /** Whether to close on escape key */\n closeOnEscape?: boolean\n /** Whether to show close button */\n showCloseButton?: boolean\n /** Whether to show overlay */\n showOverlay?: boolean\n /** Custom class name */\n className?: string\n /** Test ID */\n testId?: string\n}\n\nconst sizeMap: Record<DrawerSize, string> = {\n sm: '320px',\n md: '400px',\n lg: '560px',\n xl: '720px',\n full: '100%',\n}\n\nfunction CloseIcon() {\n return (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\">\n <path d=\"M4 4l8 8M12 4l-8 8\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" />\n </svg>\n )\n}\n\nexport const Drawer = forwardRef<HTMLDivElement, DrawerProps>(function Drawer(\n {\n isOpen,\n onClose,\n placement = 'right',\n size = 'md',\n title,\n closeOnOverlayClick = true,\n closeOnEscape = true,\n showCloseButton = true,\n showOverlay = true,\n className,\n style,\n children,\n testId,\n ...props\n },\n ref\n) {\n const drawerRef = useRef<HTMLDivElement>(null)\n const previousActiveElement = useRef<HTMLElement | null>(null)\n\n // Focus trap and keyboard handling\n useEffect(() => {\n if (!isOpen) return\n\n previousActiveElement.current = document.activeElement as HTMLElement\n const drawer = drawerRef.current\n if (drawer) {\n drawer.focus()\n }\n\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape' && closeOnEscape) {\n onClose()\n return\n }\n\n if (e.key === 'Tab' && drawer) {\n const focusableElements = drawer.querySelectorAll<HTMLElement>(\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])'\n )\n const firstElement = focusableElements[0]\n const lastElement = focusableElements[focusableElements.length - 1]\n\n if (e.shiftKey && document.activeElement === firstElement) {\n e.preventDefault()\n lastElement?.focus()\n } else if (!e.shiftKey && document.activeElement === lastElement) {\n e.preventDefault()\n firstElement?.focus()\n }\n }\n }\n\n document.addEventListener('keydown', handleKeyDown)\n document.body.style.overflow = 'hidden'\n\n return () => {\n document.removeEventListener('keydown', handleKeyDown)\n document.body.style.overflow = ''\n previousActiveElement.current?.focus()\n }\n }, [isOpen, closeOnEscape, onClose])\n\n const handleOverlayClick = useCallback(\n (e: React.MouseEvent) => {\n if (e.target === e.currentTarget && closeOnOverlayClick) {\n onClose()\n }\n },\n [closeOnOverlayClick, onClose]\n )\n\n if (!isOpen) return null\n\n const isHorizontal = placement === 'left' || placement === 'right'\n const drawerSize = sizeMap[size]\n\n const overlayStyle: CSSProperties = {\n position: 'fixed',\n inset: 0,\n zIndex: 'var(--brycks-z-drawer)' as unknown as number,\n display: 'flex',\n backgroundColor: showOverlay ? 'var(--brycks-background-overlay)' : 'transparent',\n animation: 'brycks-drawer-overlay-in 200ms ease-out',\n }\n\n const getPositionStyles = (): CSSProperties => {\n const base: CSSProperties = {\n position: 'absolute',\n backgroundColor: 'var(--brycks-background-elevated)',\n boxShadow: 'var(--brycks-shadow-2xl)',\n display: 'flex',\n flexDirection: 'column',\n overflow: 'hidden',\n }\n\n if (isHorizontal) {\n return {\n ...base,\n top: 0,\n bottom: 0,\n [placement]: 0,\n width: drawerSize,\n maxWidth: '100vw',\n }\n }\n\n return {\n ...base,\n left: 0,\n right: 0,\n [placement]: 0,\n height: drawerSize,\n maxHeight: '100vh',\n }\n }\n\n const drawerStyle: CSSProperties = {\n ...getPositionStyles(),\n animation: `brycks-drawer-${placement}-in 250ms cubic-bezier(0.32, 0.72, 0, 1)`,\n outline: 'none',\n ...style,\n }\n\n const headerStyle: CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: '16px 20px',\n borderBottom: '1px solid var(--brycks-border-muted)',\n flexShrink: 0,\n }\n\n const titleStyle: CSSProperties = {\n fontSize: 18,\n fontWeight: 600,\n color: 'var(--brycks-foreground-default)',\n margin: 0,\n }\n\n const closeButtonStyle: CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: 32,\n height: 32,\n borderRadius: 'var(--brycks-radius-md)',\n color: 'var(--brycks-foreground-muted)',\n transition: 'all 150ms ease-out',\n }\n\n const contentStyle: CSSProperties = {\n flex: 1,\n overflow: 'auto',\n padding: 20,\n }\n\n const drawerContent = (\n <>\n <style>\n {`\n @keyframes brycks-drawer-overlay-in {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n @keyframes brycks-drawer-left-in {\n from { transform: translateX(-100%); }\n to { transform: translateX(0); }\n }\n @keyframes brycks-drawer-right-in {\n from { transform: translateX(100%); }\n to { transform: translateX(0); }\n }\n @keyframes brycks-drawer-top-in {\n from { transform: translateY(-100%); }\n to { transform: translateY(0); }\n }\n @keyframes brycks-drawer-bottom-in {\n from { transform: translateY(100%); }\n to { transform: translateY(0); }\n }\n `}\n </style>\n <div\n className=\"brycks-drawer-overlay\"\n style={overlayStyle}\n onClick={handleOverlayClick}\n aria-hidden=\"true\"\n >\n <div\n ref={(node) => {\n (drawerRef as React.MutableRefObject<HTMLDivElement | null>).current = node\n if (typeof ref === 'function') ref(node)\n else if (ref) ref.current = node\n }}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={title ? 'brycks-drawer-title' : undefined}\n className={cx('brycks-drawer', `brycks-drawer--${placement}`, `brycks-drawer--${size}`, className)}\n style={drawerStyle}\n tabIndex={-1}\n data-testid={testId}\n {...props}\n >\n {(title || showCloseButton) && (\n <div className=\"brycks-drawer-header\" style={headerStyle}>\n {title && <h2 id=\"brycks-drawer-title\" style={titleStyle}>{title}</h2>}\n {showCloseButton && (\n <button\n type=\"button\"\n className=\"brycks-drawer-close\"\n style={closeButtonStyle}\n onClick={onClose}\n aria-label=\"Close drawer\"\n onMouseEnter={(e) => {\n e.currentTarget.style.backgroundColor = 'var(--brycks-background-muted)'\n e.currentTarget.style.color = 'var(--brycks-foreground-default)'\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.backgroundColor = 'transparent'\n e.currentTarget.style.color = 'var(--brycks-foreground-muted)'\n }}\n >\n <CloseIcon />\n </button>\n )}\n </div>\n )}\n <div className=\"brycks-drawer-content\" style={contentStyle}>\n {children}\n </div>\n </div>\n </div>\n </>\n )\n\n return createPortal(drawerContent, document.body)\n})\n\nDrawer.displayName = 'Drawer'\n"],"names":["sizeMap","CloseIcon","jsx","Drawer","forwardRef","isOpen","onClose","placement","size","title","closeOnOverlayClick","closeOnEscape","showCloseButton","showOverlay","className","style","children","testId","props","ref","drawerRef","useRef","previousActiveElement","useEffect","drawer","handleKeyDown","e","focusableElements","firstElement","lastElement","handleOverlayClick","useCallback","isHorizontal","drawerSize","overlayStyle","drawerStyle","base","headerStyle","titleStyle","closeButtonStyle","contentStyle","drawerContent","jsxs","Fragment","node","cx","createPortal"],"mappings":";;;;AA+CA,MAAMA,IAAsC;AAAA,EAC1C,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,MAAM;AACR;AAEA,SAASC,IAAY;AACnB,SACE,gBAAAC,EAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QACnD,4BAAC,QAAA,EAAK,GAAE,sBAAqB,QAAO,gBAAe,aAAY,OAAM,eAAc,SAAQ,EAAA,CAC7F;AAEJ;AAEO,MAAMC,IAASC,EAAwC,SAC5D;AAAA,EACE,QAAAC;AAAA,EACA,SAAAC;AAAA,EACA,WAAAC,IAAY;AAAA,EACZ,MAAAC,IAAO;AAAA,EACP,OAAAC;AAAA,EACA,qBAAAC,IAAsB;AAAA,EACtB,eAAAC,IAAgB;AAAA,EAChB,iBAAAC,IAAkB;AAAA,EAClB,aAAAC,IAAc;AAAA,EACd,WAAAC;AAAA,EACA,OAAAC;AAAA,EACA,UAAAC;AAAA,EACA,QAAAC;AAAA,EACA,GAAGC;AACL,GACAC,GACA;AACA,QAAMC,IAAYC,EAAuB,IAAI,GACvCC,IAAwBD,EAA2B,IAAI;AAG7D,EAAAE,EAAU,MAAM;AACd,QAAI,CAAClB,EAAQ;AAEb,IAAAiB,EAAsB,UAAU,SAAS;AACzC,UAAME,IAASJ,EAAU;AACzB,IAAII,KACFA,EAAO,MAAA;AAGT,UAAMC,IAAgB,CAACC,MAAqB;AAC1C,UAAIA,EAAE,QAAQ,YAAYf,GAAe;AACvC,QAAAL,EAAA;AACA;AAAA,MACF;AAEA,UAAIoB,EAAE,QAAQ,SAASF,GAAQ;AAC7B,cAAMG,IAAoBH,EAAO;AAAA,UAC/B;AAAA,QAAA,GAEII,IAAeD,EAAkB,CAAC,GAClCE,IAAcF,EAAkBA,EAAkB,SAAS,CAAC;AAElE,QAAID,EAAE,YAAY,SAAS,kBAAkBE,KAC3CF,EAAE,eAAA,GACFG,GAAa,MAAA,KACJ,CAACH,EAAE,YAAY,SAAS,kBAAkBG,MACnDH,EAAE,eAAA,GACFE,GAAc,MAAA;AAAA,MAElB;AAAA,IACF;AAEA,oBAAS,iBAAiB,WAAWH,CAAa,GAClD,SAAS,KAAK,MAAM,WAAW,UAExB,MAAM;AACX,eAAS,oBAAoB,WAAWA,CAAa,GACrD,SAAS,KAAK,MAAM,WAAW,IAC/BH,EAAsB,SAAS,MAAA;AAAA,IACjC;AAAA,EACF,GAAG,CAACjB,GAAQM,GAAeL,CAAO,CAAC;AAEnC,QAAMwB,IAAqBC;AAAA,IACzB,CAACL,MAAwB;AACvB,MAAIA,EAAE,WAAWA,EAAE,iBAAiBhB,KAClCJ,EAAA;AAAA,IAEJ;AAAA,IACA,CAACI,GAAqBJ,CAAO;AAAA,EAAA;AAG/B,MAAI,CAACD,EAAQ,QAAO;AAEpB,QAAM2B,IAAezB,MAAc,UAAUA,MAAc,SACrD0B,IAAajC,EAAQQ,CAAI,GAEzB0B,IAA8B;AAAA,IAClC,UAAU;AAAA,IACV,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,iBAAiBrB,IAAc,qCAAqC;AAAA,IACpE,WAAW;AAAA,EAAA,GAkCPsB,IAA6B;AAAA,IACjC,IAhCwB,MAAqB;AAC7C,YAAMC,IAAsB;AAAA,QAC1B,UAAU;AAAA,QACV,iBAAiB;AAAA,QACjB,WAAW;AAAA,QACX,SAAS;AAAA,QACT,eAAe;AAAA,QACf,UAAU;AAAA,MAAA;AAGZ,aAAIJ,IACK;AAAA,QACL,GAAGI;AAAA,QACH,KAAK;AAAA,QACL,QAAQ;AAAA,QACR,CAAC7B,CAAS,GAAG;AAAA,QACb,OAAO0B;AAAA,QACP,UAAU;AAAA,MAAA,IAIP;AAAA,QACL,GAAGG;AAAA,QACH,MAAM;AAAA,QACN,OAAO;AAAA,QACP,CAAC7B,CAAS,GAAG;AAAA,QACb,QAAQ0B;AAAA,QACR,WAAW;AAAA,MAAA;AAAA,IAEf,GAGK;AAAA,IACH,WAAW,iBAAiB1B,CAAS;AAAA,IACrC,SAAS;AAAA,IACT,GAAGQ;AAAA,EAAA,GAGCsB,IAA6B;AAAA,IACjC,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,SAAS;AAAA,IACT,cAAc;AAAA,IACd,YAAY;AAAA,EAAA,GAGRC,IAA4B;AAAA,IAChC,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,OAAO;AAAA,IACP,QAAQ;AAAA,EAAA,GAGJC,IAAkC;AAAA,IACtC,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,cAAc;AAAA,IACd,OAAO;AAAA,IACP,YAAY;AAAA,EAAA,GAGRC,IAA8B;AAAA,IAClC,MAAM;AAAA,IACN,UAAU;AAAA,IACV,SAAS;AAAA,EAAA,GAGLC,IACJ,gBAAAC,EAAAC,GAAA,EACE,UAAA;AAAA,IAAA,gBAAAzC,EAAC,SAAA,EACE,UAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,WAsBH;AAAA,IACA,gBAAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,WAAU;AAAA,QACV,OAAOgC;AAAA,QACP,SAASJ;AAAA,QACT,eAAY;AAAA,QAEZ,UAAA,gBAAAY;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,KAAK,CAACE,MAAS;AACZ,cAAAxB,EAA4D,UAAUwB,GACnE,OAAOzB,KAAQ,aAAYA,EAAIyB,CAAI,IAC9BzB,QAAS,UAAUyB;AAAA,YAC9B;AAAA,YACA,MAAK;AAAA,YACL,cAAW;AAAA,YACX,mBAAiBnC,IAAQ,wBAAwB;AAAA,YACjD,WAAWoC,EAAG,iBAAiB,kBAAkBtC,CAAS,IAAI,kBAAkBC,CAAI,IAAIM,CAAS;AAAA,YACjG,OAAOqB;AAAA,YACP,UAAU;AAAA,YACV,eAAalB;AAAA,YACZ,GAAGC;AAAA,YAEF,UAAA;AAAA,eAAAT,KAASG,MACT,gBAAA8B,EAAC,OAAA,EAAI,WAAU,wBAAuB,OAAOL,GAC1C,UAAA;AAAA,gBAAA5B,uBAAU,MAAA,EAAG,IAAG,uBAAsB,OAAO6B,GAAa,UAAA7B,GAAM;AAAA,gBAChEG,KACC,gBAAAV;AAAA,kBAAC;AAAA,kBAAA;AAAA,oBACC,MAAK;AAAA,oBACL,WAAU;AAAA,oBACV,OAAOqC;AAAA,oBACP,SAASjC;AAAA,oBACT,cAAW;AAAA,oBACX,cAAc,CAACoB,MAAM;AACnB,sBAAAA,EAAE,cAAc,MAAM,kBAAkB,kCACxCA,EAAE,cAAc,MAAM,QAAQ;AAAA,oBAChC;AAAA,oBACA,cAAc,CAACA,MAAM;AACnB,sBAAAA,EAAE,cAAc,MAAM,kBAAkB,eACxCA,EAAE,cAAc,MAAM,QAAQ;AAAA,oBAChC;AAAA,oBAEA,4BAACzB,GAAA,CAAA,CAAU;AAAA,kBAAA;AAAA,gBAAA;AAAA,cACb,GAEJ;AAAA,gCAED,OAAA,EAAI,WAAU,yBAAwB,OAAOuC,GAC3C,UAAAxB,EAAA,CACH;AAAA,YAAA;AAAA,UAAA;AAAA,QAAA;AAAA,MACF;AAAA,IAAA;AAAA,EACF,GACF;AAGF,SAAO8B,EAAaL,GAAe,SAAS,IAAI;AAClD,CAAC;AAEDtC,EAAO,cAAc;"}
|
|
1
|
+
{"version":3,"file":"Drawer.js","sources":["../../../../src/components/feedback/Drawer/Drawer.tsx"],"sourcesContent":["/**\n * Drawer Component\n *\n * A slide-out panel that can appear from any edge of the screen.\n * Supports focus trapping and keyboard navigation.\n */\n\nimport {\n forwardRef,\n useEffect,\n useRef,\n useCallback,\n type CSSProperties,\n type ReactNode,\n type HTMLAttributes,\n} from 'react'\nimport { createPortal } from 'react-dom'\nimport { cx } from '../../../utils/styles'\n\nexport type DrawerPlacement = 'left' | 'right' | 'top' | 'bottom'\nexport type DrawerSize = 'sm' | 'md' | 'lg' | 'xl' | 'full'\n\nexport interface DrawerProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {\n /** Whether the drawer is open */\n isOpen: boolean\n /** Callback when drawer should close */\n onClose: () => void\n /** Drawer placement */\n placement?: DrawerPlacement\n /** Drawer size */\n size?: DrawerSize\n /** Drawer title */\n title?: ReactNode\n /** Whether to close on overlay click */\n closeOnOverlayClick?: boolean\n /** Whether to close on escape key */\n closeOnEscape?: boolean\n /** Whether to show close button */\n showCloseButton?: boolean\n /** Whether to show overlay */\n showOverlay?: boolean\n /** Custom class name */\n className?: string\n /** Test ID */\n testId?: string\n}\n\nconst sizeMap: Record<DrawerSize, string> = {\n sm: '320px',\n md: '400px',\n lg: '560px',\n xl: '720px',\n full: '100%',\n}\n\nfunction CloseIcon() {\n return (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\">\n <path d=\"M4 4l8 8M12 4l-8 8\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" />\n </svg>\n )\n}\n\nexport const Drawer = forwardRef<HTMLDivElement, DrawerProps>(function Drawer(\n {\n isOpen,\n onClose,\n placement = 'right',\n size = 'md',\n title,\n closeOnOverlayClick = true,\n closeOnEscape = true,\n showCloseButton = true,\n showOverlay = true,\n className,\n style,\n children,\n testId,\n ...props\n },\n ref\n) {\n const drawerRef = useRef<HTMLDivElement>(null)\n const previousActiveElement = useRef<HTMLElement | null>(null)\n\n // Store onClose in a ref to avoid re-running effects when it changes\n const onCloseRef = useRef(onClose)\n useEffect(() => {\n onCloseRef.current = onClose\n }, [onClose])\n\n // Focus drawer when it opens (only once)\n useEffect(() => {\n if (!isOpen) return\n\n previousActiveElement.current = document.activeElement as HTMLElement\n const drawer = drawerRef.current\n if (drawer) {\n drawer.focus()\n }\n }, [isOpen])\n\n // Keyboard handling and body scroll lock\n useEffect(() => {\n if (!isOpen) return\n\n const drawer = drawerRef.current\n\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape' && closeOnEscape) {\n onCloseRef.current()\n return\n }\n\n if (e.key === 'Tab' && drawer) {\n const focusableElements = drawer.querySelectorAll<HTMLElement>(\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])'\n )\n const firstElement = focusableElements[0]\n const lastElement = focusableElements[focusableElements.length - 1]\n\n if (e.shiftKey && document.activeElement === firstElement) {\n e.preventDefault()\n lastElement?.focus()\n } else if (!e.shiftKey && document.activeElement === lastElement) {\n e.preventDefault()\n firstElement?.focus()\n }\n }\n }\n\n document.addEventListener('keydown', handleKeyDown)\n document.body.style.overflow = 'hidden'\n\n return () => {\n document.removeEventListener('keydown', handleKeyDown)\n document.body.style.overflow = ''\n previousActiveElement.current?.focus()\n }\n }, [isOpen, closeOnEscape])\n\n const handleOverlayClick = useCallback(\n (e: React.MouseEvent) => {\n if (e.target === e.currentTarget && closeOnOverlayClick) {\n onClose()\n }\n },\n [closeOnOverlayClick, onClose]\n )\n\n if (!isOpen) return null\n\n const isHorizontal = placement === 'left' || placement === 'right'\n const drawerSize = sizeMap[size]\n\n const overlayStyle: CSSProperties = {\n position: 'fixed',\n inset: 0,\n zIndex: 'var(--brycks-z-drawer)' as unknown as number,\n display: 'flex',\n backgroundColor: showOverlay ? 'var(--brycks-background-overlay)' : 'transparent',\n animation: 'brycks-drawer-overlay-in 200ms ease-out',\n }\n\n const getPositionStyles = (): CSSProperties => {\n const base: CSSProperties = {\n position: 'absolute',\n backgroundColor: 'var(--brycks-background-elevated)',\n boxShadow: 'var(--brycks-shadow-2xl)',\n display: 'flex',\n flexDirection: 'column',\n overflow: 'hidden',\n }\n\n if (isHorizontal) {\n return {\n ...base,\n top: 0,\n bottom: 0,\n [placement]: 0,\n width: drawerSize,\n maxWidth: '100vw',\n }\n }\n\n return {\n ...base,\n left: 0,\n right: 0,\n [placement]: 0,\n height: drawerSize,\n maxHeight: '100vh',\n }\n }\n\n const drawerStyle: CSSProperties = {\n ...getPositionStyles(),\n animation: `brycks-drawer-${placement}-in 250ms cubic-bezier(0.32, 0.72, 0, 1)`,\n outline: 'none',\n ...style,\n }\n\n const headerStyle: CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: '16px 20px',\n borderBottom: '1px solid var(--brycks-border-muted)',\n flexShrink: 0,\n }\n\n const titleStyle: CSSProperties = {\n fontSize: 18,\n fontWeight: 600,\n color: 'var(--brycks-foreground-default)',\n margin: 0,\n }\n\n const closeButtonStyle: CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: 32,\n height: 32,\n borderRadius: 'var(--brycks-radius-md)',\n color: 'var(--brycks-foreground-muted)',\n transition: 'all 150ms ease-out',\n }\n\n const contentStyle: CSSProperties = {\n flex: 1,\n overflow: 'auto',\n padding: 20,\n }\n\n const drawerContent = (\n <>\n <style>\n {`\n @keyframes brycks-drawer-overlay-in {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n @keyframes brycks-drawer-left-in {\n from { transform: translateX(-100%); }\n to { transform: translateX(0); }\n }\n @keyframes brycks-drawer-right-in {\n from { transform: translateX(100%); }\n to { transform: translateX(0); }\n }\n @keyframes brycks-drawer-top-in {\n from { transform: translateY(-100%); }\n to { transform: translateY(0); }\n }\n @keyframes brycks-drawer-bottom-in {\n from { transform: translateY(100%); }\n to { transform: translateY(0); }\n }\n `}\n </style>\n <div\n className=\"brycks-drawer-overlay\"\n style={overlayStyle}\n onClick={handleOverlayClick}\n aria-hidden=\"true\"\n >\n <div\n ref={(node) => {\n (drawerRef as React.MutableRefObject<HTMLDivElement | null>).current = node\n if (typeof ref === 'function') ref(node)\n else if (ref) ref.current = node\n }}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={title ? 'brycks-drawer-title' : undefined}\n className={cx('brycks-drawer', `brycks-drawer--${placement}`, `brycks-drawer--${size}`, className)}\n style={drawerStyle}\n tabIndex={-1}\n data-testid={testId}\n {...props}\n >\n {(title || showCloseButton) && (\n <div className=\"brycks-drawer-header\" style={headerStyle}>\n {title && <h2 id=\"brycks-drawer-title\" style={titleStyle}>{title}</h2>}\n {showCloseButton && (\n <button\n type=\"button\"\n className=\"brycks-drawer-close\"\n style={closeButtonStyle}\n onClick={onClose}\n aria-label=\"Close drawer\"\n onMouseEnter={(e) => {\n e.currentTarget.style.backgroundColor = 'var(--brycks-background-muted)'\n e.currentTarget.style.color = 'var(--brycks-foreground-default)'\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.backgroundColor = 'transparent'\n e.currentTarget.style.color = 'var(--brycks-foreground-muted)'\n }}\n >\n <CloseIcon />\n </button>\n )}\n </div>\n )}\n <div className=\"brycks-drawer-content\" style={contentStyle}>\n {children}\n </div>\n </div>\n </div>\n </>\n )\n\n return createPortal(drawerContent, document.body)\n})\n\nDrawer.displayName = 'Drawer'\n"],"names":["sizeMap","CloseIcon","jsx","Drawer","forwardRef","isOpen","onClose","placement","size","title","closeOnOverlayClick","closeOnEscape","showCloseButton","showOverlay","className","style","children","testId","props","ref","drawerRef","useRef","previousActiveElement","onCloseRef","useEffect","drawer","handleKeyDown","e","focusableElements","firstElement","lastElement","handleOverlayClick","useCallback","isHorizontal","drawerSize","overlayStyle","drawerStyle","base","headerStyle","titleStyle","closeButtonStyle","contentStyle","drawerContent","jsxs","Fragment","node","cx","createPortal"],"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 r=require("react/jsx-runtime"),t=require("react"),z=require("react-dom"),A=require("../../../utils/styles.cjs"),B={sm:"400px",md:"500px",lg:"640px",xl:"800px",full:"calc(100vw - 48px)"},P=t.memo(function(){return r.jsx("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none","aria-hidden":"true",children:r.jsx("path",{d:"M4 4L12 12M12 4L4 12",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round"})})}),E=t.forwardRef(function({isOpen:o,onClose:n,size:l="md",title:c,description:i,closeOnOverlayClick:m=!0,closeOnEscape:b=!0,showCloseButton:v=!0,className:w,style:k,testId:S,children:M,...C},s){const u=t.useRef(null),y=t.useRef(null),d=t.useRef(!1);t.useEffect(()=>{o&&!d.current?(y.current=document.activeElement,d.current=!0):!o&&d.current&&(document.activeElement instanceof HTMLElement&&document.activeElement.blur(),y.current?.focus(),y.current=null,d.current=!1)},[o]);const x=t.useRef(n);t.useEffect(()=>{x.current=n},[n]),t.useEffect(()=>{if(!o)return;const e=u.current;e&&e.focus()},[o]),t.useEffect(()=>{if(!o)return;const e=u.current,g=a=>{if(a.key==="Escape"&&b){x.current();return}if(a.key==="Tab"&&e){const f=e.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'),h=f[0],p=f[f.length-1];a.shiftKey&&document.activeElement===h?(a.preventDefault(),p?.focus()):!a.shiftKey&&document.activeElement===p&&(a.preventDefault(),h?.focus())}};return document.addEventListener("keydown",g),document.body.style.overflow="hidden",()=>{document.removeEventListener("keydown",g),document.body.style.overflow=""}},[o,b]);const R=t.useCallback(e=>{e.target===e.currentTarget&&m&&n()},[m,n]),L=t.useMemo(()=>({position:"fixed",inset:0,zIndex:"var(--brycks-z-modal)",display:"flex",alignItems:"center",justifyContent:"center",padding:24,backgroundColor:"var(--brycks-background-overlay)",backdropFilter:"blur(4px)",animation:"brycks-fade-in var(--brycks-duration-normal) var(--brycks-ease-out)"}),[]),T=t.useMemo(()=>({position:"relative",width:"100%",maxWidth:B[l],maxHeight:l==="full"?"calc(100vh - 48px)":"85vh",backgroundColor:"var(--brycks-background-elevated)",borderRadius:"var(--brycks-radius-xl)",boxShadow:"var(--brycks-shadow-2xl)",display:"flex",flexDirection:"column",overflow:"hidden",animation:"brycks-scale-in var(--brycks-duration-normal) var(--brycks-ease-spring)",outline:"none",...k}),[l,k]);if(!o)return null;const I={display:"flex",alignItems:"flex-start",justifyContent:"space-between",padding:"20px 24px",borderBottom:"1px solid var(--brycks-border-muted)"},D={display:"flex",flexDirection:"column",gap:4},N={fontSize:18,fontWeight:600,color:"var(--brycks-foreground-default)",margin:0,lineHeight:1.3},q={fontSize:14,color:"var(--brycks-foreground-muted)",margin:0},W={display:"flex",alignItems:"center",justifyContent:"center",width:32,height:32,borderRadius:"var(--brycks-radius-md)",color:"var(--brycks-foreground-muted)",transition:"all var(--brycks-duration-fast) var(--brycks-ease-out)",marginLeft:12,flexShrink:0},H={flex:1,overflow:"auto",padding:24},K=r.jsx("div",{className:"brycks-modal-overlay",style:L,onClick:R,children:r.jsxs("div",{ref:e=>{u.current=e,typeof s=="function"?s(e):s&&(s.current=e)},role:"dialog","aria-modal":"true","aria-labelledby":c?"brycks-modal-title":void 0,"aria-describedby":i?"brycks-modal-description":void 0,className:A.cx("brycks-modal",`brycks-modal--${l}`,w),style:T,tabIndex:-1,"data-testid":S,...C,children:[(c||v)&&r.jsxs("div",{className:"brycks-modal-header",style:I,children:[r.jsxs("div",{style:D,children:[c&&r.jsx("h2",{id:"brycks-modal-title",style:N,children:c}),i&&r.jsx("p",{id:"brycks-modal-description",style:q,children:i})]}),v&&r.jsx("button",{type:"button",className:"brycks-modal-close",style:W,onClick:n,"aria-label":"Close modal",onMouseEnter:e=>{e.currentTarget.style.backgroundColor="var(--brycks-background-muted)",e.currentTarget.style.color="var(--brycks-foreground-default)"},onMouseLeave:e=>{e.currentTarget.style.backgroundColor="transparent",e.currentTarget.style.color="var(--brycks-foreground-muted)"},children:r.jsx(P,{})})]}),r.jsx("div",{className:"brycks-modal-content",style:H,children:M})]})});return z.createPortal(K,document.body)});E.displayName="Modal";exports.Modal=E;
|
|
2
2
|
//# sourceMappingURL=Modal.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Modal.cjs","sources":["../../../../src/components/feedback/Modal/Modal.tsx"],"sourcesContent":["/**\r\n * Modal Component\r\n *\r\n * Accessible modal dialog with smooth Apple-inspired animations.\r\n * Supports focus trapping and keyboard navigation.\r\n */\r\n\r\nimport {\r\n forwardRef,\r\n useEffect,\r\n useRef,\r\n useCallback,\r\n useMemo,\r\n memo,\r\n type CSSProperties,\r\n type ReactNode,\r\n type HTMLAttributes,\r\n} from 'react'\r\nimport { createPortal } from 'react-dom'\r\nimport { cx } from '../../../utils/styles'\r\n\r\nexport type ModalSize = 'sm' | 'md' | 'lg' | 'xl' | 'full'\r\n\r\nexport interface ModalProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {\r\n /** Whether the modal is open */\r\n isOpen: boolean\r\n /** Callback when modal should close */\r\n onClose: () => void\r\n /** Modal size */\r\n size?: ModalSize\r\n /** Modal title */\r\n title?: ReactNode\r\n /** Modal description */\r\n description?: ReactNode\r\n /** Whether to close on overlay click */\r\n closeOnOverlayClick?: boolean\r\n /** Whether to close on escape key */\r\n closeOnEscape?: boolean\r\n /** Whether to show close button */\r\n showCloseButton?: boolean\r\n /** Custom class name */\r\n className?: string\r\n /** Test ID */\r\n testId?: string\r\n /** Modal content */\r\n children?: ReactNode\r\n}\r\n\r\nconst sizeWidths: Record<ModalSize, string> = {\r\n sm: '400px',\r\n md: '500px',\r\n lg: '640px',\r\n xl: '800px',\r\n full: 'calc(100vw - 48px)',\r\n}\r\n\r\n/** Close icon - memoized to prevent re-renders */\r\nconst CloseIcon = memo(function CloseIcon() {\r\n return (\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden=\"true\">\r\n <path\r\n d=\"M4 4L12 12M12 4L4 12\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"1.5\"\r\n strokeLinecap=\"round\"\r\n />\r\n </svg>\r\n )\r\n})\r\n\r\nexport const Modal = forwardRef<HTMLDivElement, ModalProps>(function Modal(\r\n {\r\n isOpen,\r\n onClose,\r\n size = 'md',\r\n title,\r\n description,\r\n closeOnOverlayClick = true,\r\n closeOnEscape = true,\r\n showCloseButton = true,\r\n className,\r\n style,\r\n testId,\r\n children,\r\n ...props\r\n },\r\n ref\r\n) {\r\n const modalRef = useRef<HTMLDivElement>(null)\r\n const previousActiveElement = useRef<HTMLElement | null>(null)\r\n const wasOpen = useRef(false)\r\n\r\n // Track previous open state to handle focus restoration before unmount\r\n useEffect(() => {\r\n // Modal just opened\r\n if (isOpen && !wasOpen.current) {\r\n previousActiveElement.current = document.activeElement as HTMLElement\r\n wasOpen.current = true\r\n }\r\n // Modal is about to close - restore focus BEFORE unmount\r\n else if (!isOpen && wasOpen.current) {\r\n // Blur any focused element inside modal to prevent aria-hidden warning\r\n if (document.activeElement instanceof HTMLElement) {\r\n document.activeElement.blur()\r\n }\r\n // Restore focus to previous element\r\n previousActiveElement.current?.focus()\r\n previousActiveElement.current = null\r\n wasOpen.current = false\r\n }\r\n }, [isOpen])\r\n\r\n // Focus trap and keyboard handling\r\n useEffect(() => {\r\n if (!isOpen) return\r\n\r\n const modal = modalRef.current\r\n if (modal) {\r\n modal.focus()\r\n }\r\n\r\n const handleKeyDown = (e: KeyboardEvent) => {\r\n if (e.key === 'Escape' && closeOnEscape) {\r\n onClose()\r\n return\r\n }\r\n\r\n if (e.key === 'Tab' && modal) {\r\n const focusableElements = modal.querySelectorAll<HTMLElement>(\r\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])'\r\n )\r\n const firstElement = focusableElements[0]\r\n const lastElement = focusableElements[focusableElements.length - 1]\r\n\r\n if (e.shiftKey && document.activeElement === firstElement) {\r\n e.preventDefault()\r\n lastElement?.focus()\r\n } else if (!e.shiftKey && document.activeElement === lastElement) {\r\n e.preventDefault()\r\n firstElement?.focus()\r\n }\r\n }\r\n }\r\n\r\n document.addEventListener('keydown', handleKeyDown)\r\n document.body.style.overflow = 'hidden'\r\n\r\n return () => {\r\n document.removeEventListener('keydown', handleKeyDown)\r\n document.body.style.overflow = ''\r\n }\r\n }, [isOpen, closeOnEscape, onClose])\r\n\r\n const handleOverlayClick = useCallback(\r\n (e: React.MouseEvent) => {\r\n if (e.target === e.currentTarget && closeOnOverlayClick) {\r\n onClose()\r\n }\r\n },\r\n [closeOnOverlayClick, onClose]\r\n )\r\n\r\n // Memoize styles to prevent object recreation on every render\r\n const overlayStyle = useMemo<CSSProperties>(() => ({\r\n position: 'fixed',\r\n inset: 0,\r\n zIndex: 'var(--brycks-z-modal)',\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n padding: 24,\r\n backgroundColor: 'var(--brycks-background-overlay)',\r\n backdropFilter: 'blur(4px)',\r\n animation: 'brycks-fade-in var(--brycks-duration-normal) var(--brycks-ease-out)',\r\n }), [])\r\n\r\n const modalStyle = useMemo<CSSProperties>(() => ({\r\n position: 'relative',\r\n width: '100%',\r\n maxWidth: sizeWidths[size],\r\n maxHeight: size === 'full' ? 'calc(100vh - 48px)' : '85vh',\r\n backgroundColor: 'var(--brycks-background-elevated)',\r\n borderRadius: 'var(--brycks-radius-xl)',\r\n boxShadow: 'var(--brycks-shadow-2xl)',\r\n display: 'flex',\r\n flexDirection: 'column',\r\n overflow: 'hidden',\r\n animation: 'brycks-scale-in var(--brycks-duration-normal) var(--brycks-ease-spring)',\r\n outline: 'none',\r\n ...style,\r\n }), [size, style])\r\n\r\n if (!isOpen) return null\r\n\r\n const headerStyle: CSSProperties = {\r\n display: 'flex',\r\n alignItems: 'flex-start',\r\n justifyContent: 'space-between',\r\n padding: '20px 24px',\r\n borderBottom: '1px solid var(--brycks-border-muted)',\r\n }\r\n\r\n const titleContainerStyle: CSSProperties = {\r\n display: 'flex',\r\n flexDirection: 'column',\r\n gap: 4,\r\n }\r\n\r\n const titleStyle: CSSProperties = {\r\n fontSize: 18,\r\n fontWeight: 600,\r\n color: 'var(--brycks-foreground-default)',\r\n margin: 0,\r\n lineHeight: 1.3,\r\n }\r\n\r\n const descriptionStyle: CSSProperties = {\r\n fontSize: 14,\r\n color: 'var(--brycks-foreground-muted)',\r\n margin: 0,\r\n }\r\n\r\n const closeButtonStyle: CSSProperties = {\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n width: 32,\r\n height: 32,\r\n borderRadius: 'var(--brycks-radius-md)',\r\n color: 'var(--brycks-foreground-muted)',\r\n transition: 'all var(--brycks-duration-fast) var(--brycks-ease-out)',\r\n marginLeft: 12,\r\n flexShrink: 0,\r\n }\r\n\r\n const contentStyle: CSSProperties = {\r\n flex: 1,\r\n overflow: 'auto',\r\n padding: 24,\r\n }\r\n\r\n const modalContent = (\r\n <div\r\n className=\"brycks-modal-overlay\"\r\n style={overlayStyle}\r\n onClick={handleOverlayClick}\r\n >\r\n <div\r\n ref={(node) => {\r\n (modalRef as React.MutableRefObject<HTMLDivElement | null>).current = node\r\n if (typeof ref === 'function') ref(node)\r\n else if (ref) (ref as React.MutableRefObject<HTMLDivElement | null>).current = node\r\n }}\r\n role=\"dialog\"\r\n aria-modal=\"true\"\r\n aria-labelledby={title ? 'brycks-modal-title' : undefined}\r\n aria-describedby={description ? 'brycks-modal-description' : undefined}\r\n className={cx('brycks-modal', `brycks-modal--${size}`, className)}\r\n style={modalStyle}\r\n tabIndex={-1}\r\n data-testid={testId}\r\n {...props}\r\n >\r\n {(title || showCloseButton) && (\r\n <div className=\"brycks-modal-header\" style={headerStyle}>\r\n <div style={titleContainerStyle}>\r\n {title && (\r\n <h2 id=\"brycks-modal-title\" style={titleStyle}>\r\n {title}\r\n </h2>\r\n )}\r\n {description && (\r\n <p id=\"brycks-modal-description\" style={descriptionStyle}>\r\n {description}\r\n </p>\r\n )}\r\n </div>\r\n {showCloseButton && (\r\n <button\r\n type=\"button\"\r\n className=\"brycks-modal-close\"\r\n style={closeButtonStyle}\r\n onClick={onClose}\r\n aria-label=\"Close modal\"\r\n onMouseEnter={(e) => {\r\n e.currentTarget.style.backgroundColor = 'var(--brycks-background-muted)'\r\n e.currentTarget.style.color = 'var(--brycks-foreground-default)'\r\n }}\r\n onMouseLeave={(e) => {\r\n e.currentTarget.style.backgroundColor = 'transparent'\r\n e.currentTarget.style.color = 'var(--brycks-foreground-muted)'\r\n }}\r\n >\r\n <CloseIcon />\r\n </button>\r\n )}\r\n </div>\r\n )}\r\n\r\n <div className=\"brycks-modal-content\" style={contentStyle}>\r\n {children}\r\n </div>\r\n </div>\r\n </div>\r\n )\r\n\r\n return createPortal(modalContent, document.body)\r\n})\r\n\r\nModal.displayName = 'Modal'\r\n"],"names":["sizeWidths","CloseIcon","memo","jsx","Modal","forwardRef","isOpen","onClose","size","title","description","closeOnOverlayClick","closeOnEscape","showCloseButton","className","style","testId","children","props","ref","modalRef","useRef","previousActiveElement","wasOpen","useEffect","modal","handleKeyDown","e","focusableElements","firstElement","lastElement","handleOverlayClick","useCallback","overlayStyle","useMemo","modalStyle","headerStyle","titleContainerStyle","titleStyle","descriptionStyle","closeButtonStyle","contentStyle","modalContent","jsxs","node","cx","createPortal"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Modal.cjs","sources":["../../../../src/components/feedback/Modal/Modal.tsx"],"sourcesContent":["/**\r\n * Modal Component\r\n *\r\n * Accessible modal dialog with smooth Apple-inspired animations.\r\n * Supports focus trapping and keyboard navigation.\r\n */\r\n\r\nimport {\r\n forwardRef,\r\n useEffect,\r\n useRef,\r\n useCallback,\r\n useMemo,\r\n memo,\r\n type CSSProperties,\r\n type ReactNode,\r\n type HTMLAttributes,\r\n} from 'react'\r\nimport { createPortal } from 'react-dom'\r\nimport { cx } from '../../../utils/styles'\r\n\r\nexport type ModalSize = 'sm' | 'md' | 'lg' | 'xl' | 'full'\r\n\r\nexport interface ModalProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {\r\n /** Whether the modal is open */\r\n isOpen: boolean\r\n /** Callback when modal should close */\r\n onClose: () => void\r\n /** Modal size */\r\n size?: ModalSize\r\n /** Modal title */\r\n title?: ReactNode\r\n /** Modal description */\r\n description?: ReactNode\r\n /** Whether to close on overlay click */\r\n closeOnOverlayClick?: boolean\r\n /** Whether to close on escape key */\r\n closeOnEscape?: boolean\r\n /** Whether to show close button */\r\n showCloseButton?: boolean\r\n /** Custom class name */\r\n className?: string\r\n /** Test ID */\r\n testId?: string\r\n /** Modal content */\r\n children?: ReactNode\r\n}\r\n\r\nconst sizeWidths: Record<ModalSize, string> = {\r\n sm: '400px',\r\n md: '500px',\r\n lg: '640px',\r\n xl: '800px',\r\n full: 'calc(100vw - 48px)',\r\n}\r\n\r\n/** Close icon - memoized to prevent re-renders */\r\nconst CloseIcon = memo(function CloseIcon() {\r\n return (\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden=\"true\">\r\n <path\r\n d=\"M4 4L12 12M12 4L4 12\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"1.5\"\r\n strokeLinecap=\"round\"\r\n />\r\n </svg>\r\n )\r\n})\r\n\r\nexport const Modal = forwardRef<HTMLDivElement, ModalProps>(function Modal(\r\n {\r\n isOpen,\r\n onClose,\r\n size = 'md',\r\n title,\r\n description,\r\n closeOnOverlayClick = true,\r\n closeOnEscape = true,\r\n showCloseButton = true,\r\n className,\r\n style,\r\n testId,\r\n children,\r\n ...props\r\n },\r\n ref\r\n) {\r\n const modalRef = useRef<HTMLDivElement>(null)\r\n const previousActiveElement = useRef<HTMLElement | null>(null)\r\n const wasOpen = useRef(false)\r\n\r\n // Track previous open state to handle focus restoration before unmount\r\n useEffect(() => {\r\n // Modal just opened\r\n if (isOpen && !wasOpen.current) {\r\n previousActiveElement.current = document.activeElement as HTMLElement\r\n wasOpen.current = true\r\n }\r\n // Modal is about to close - restore focus BEFORE unmount\r\n else if (!isOpen && wasOpen.current) {\r\n // Blur any focused element inside modal to prevent aria-hidden warning\r\n if (document.activeElement instanceof HTMLElement) {\r\n document.activeElement.blur()\r\n }\r\n // Restore focus to previous element\r\n previousActiveElement.current?.focus()\r\n previousActiveElement.current = null\r\n wasOpen.current = false\r\n }\r\n }, [isOpen])\r\n\r\n // Store onClose in a ref to avoid re-running effects when it changes\r\n const onCloseRef = useRef(onClose)\r\n useEffect(() => {\r\n onCloseRef.current = onClose\r\n }, [onClose])\r\n\r\n // Focus modal when it opens (only once)\r\n useEffect(() => {\r\n if (!isOpen) return\r\n\r\n const modal = modalRef.current\r\n if (modal) {\r\n modal.focus()\r\n }\r\n }, [isOpen])\r\n\r\n // Keyboard handling and body scroll lock\r\n useEffect(() => {\r\n if (!isOpen) return\r\n\r\n const modal = modalRef.current\r\n\r\n const handleKeyDown = (e: KeyboardEvent) => {\r\n if (e.key === 'Escape' && closeOnEscape) {\r\n onCloseRef.current()\r\n return\r\n }\r\n\r\n if (e.key === 'Tab' && modal) {\r\n const focusableElements = modal.querySelectorAll<HTMLElement>(\r\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])'\r\n )\r\n const firstElement = focusableElements[0]\r\n const lastElement = focusableElements[focusableElements.length - 1]\r\n\r\n if (e.shiftKey && document.activeElement === firstElement) {\r\n e.preventDefault()\r\n lastElement?.focus()\r\n } else if (!e.shiftKey && document.activeElement === lastElement) {\r\n e.preventDefault()\r\n firstElement?.focus()\r\n }\r\n }\r\n }\r\n\r\n document.addEventListener('keydown', handleKeyDown)\r\n document.body.style.overflow = 'hidden'\r\n\r\n return () => {\r\n document.removeEventListener('keydown', handleKeyDown)\r\n document.body.style.overflow = ''\r\n }\r\n }, [isOpen, closeOnEscape])\r\n\r\n const handleOverlayClick = useCallback(\r\n (e: React.MouseEvent) => {\r\n if (e.target === e.currentTarget && closeOnOverlayClick) {\r\n onClose()\r\n }\r\n },\r\n [closeOnOverlayClick, onClose]\r\n )\r\n\r\n // Memoize styles to prevent object recreation on every render\r\n const overlayStyle = useMemo<CSSProperties>(() => ({\r\n position: 'fixed',\r\n inset: 0,\r\n zIndex: 'var(--brycks-z-modal)',\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n padding: 24,\r\n backgroundColor: 'var(--brycks-background-overlay)',\r\n backdropFilter: 'blur(4px)',\r\n animation: 'brycks-fade-in var(--brycks-duration-normal) var(--brycks-ease-out)',\r\n }), [])\r\n\r\n const modalStyle = useMemo<CSSProperties>(() => ({\r\n position: 'relative',\r\n width: '100%',\r\n maxWidth: sizeWidths[size],\r\n maxHeight: size === 'full' ? 'calc(100vh - 48px)' : '85vh',\r\n backgroundColor: 'var(--brycks-background-elevated)',\r\n borderRadius: 'var(--brycks-radius-xl)',\r\n boxShadow: 'var(--brycks-shadow-2xl)',\r\n display: 'flex',\r\n flexDirection: 'column',\r\n overflow: 'hidden',\r\n animation: 'brycks-scale-in var(--brycks-duration-normal) var(--brycks-ease-spring)',\r\n outline: 'none',\r\n ...style,\r\n }), [size, style])\r\n\r\n if (!isOpen) return null\r\n\r\n const headerStyle: CSSProperties = {\r\n display: 'flex',\r\n alignItems: 'flex-start',\r\n justifyContent: 'space-between',\r\n padding: '20px 24px',\r\n borderBottom: '1px solid var(--brycks-border-muted)',\r\n }\r\n\r\n const titleContainerStyle: CSSProperties = {\r\n display: 'flex',\r\n flexDirection: 'column',\r\n gap: 4,\r\n }\r\n\r\n const titleStyle: CSSProperties = {\r\n fontSize: 18,\r\n fontWeight: 600,\r\n color: 'var(--brycks-foreground-default)',\r\n margin: 0,\r\n lineHeight: 1.3,\r\n }\r\n\r\n const descriptionStyle: CSSProperties = {\r\n fontSize: 14,\r\n color: 'var(--brycks-foreground-muted)',\r\n margin: 0,\r\n }\r\n\r\n const closeButtonStyle: CSSProperties = {\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n width: 32,\r\n height: 32,\r\n borderRadius: 'var(--brycks-radius-md)',\r\n color: 'var(--brycks-foreground-muted)',\r\n transition: 'all var(--brycks-duration-fast) var(--brycks-ease-out)',\r\n marginLeft: 12,\r\n flexShrink: 0,\r\n }\r\n\r\n const contentStyle: CSSProperties = {\r\n flex: 1,\r\n overflow: 'auto',\r\n padding: 24,\r\n }\r\n\r\n const modalContent = (\r\n <div\r\n className=\"brycks-modal-overlay\"\r\n style={overlayStyle}\r\n onClick={handleOverlayClick}\r\n >\r\n <div\r\n ref={(node) => {\r\n (modalRef as React.MutableRefObject<HTMLDivElement | null>).current = node\r\n if (typeof ref === 'function') ref(node)\r\n else if (ref) (ref as React.MutableRefObject<HTMLDivElement | null>).current = node\r\n }}\r\n role=\"dialog\"\r\n aria-modal=\"true\"\r\n aria-labelledby={title ? 'brycks-modal-title' : undefined}\r\n aria-describedby={description ? 'brycks-modal-description' : undefined}\r\n className={cx('brycks-modal', `brycks-modal--${size}`, className)}\r\n style={modalStyle}\r\n tabIndex={-1}\r\n data-testid={testId}\r\n {...props}\r\n >\r\n {(title || showCloseButton) && (\r\n <div className=\"brycks-modal-header\" style={headerStyle}>\r\n <div style={titleContainerStyle}>\r\n {title && (\r\n <h2 id=\"brycks-modal-title\" style={titleStyle}>\r\n {title}\r\n </h2>\r\n )}\r\n {description && (\r\n <p id=\"brycks-modal-description\" style={descriptionStyle}>\r\n {description}\r\n </p>\r\n )}\r\n </div>\r\n {showCloseButton && (\r\n <button\r\n type=\"button\"\r\n className=\"brycks-modal-close\"\r\n style={closeButtonStyle}\r\n onClick={onClose}\r\n aria-label=\"Close modal\"\r\n onMouseEnter={(e) => {\r\n e.currentTarget.style.backgroundColor = 'var(--brycks-background-muted)'\r\n e.currentTarget.style.color = 'var(--brycks-foreground-default)'\r\n }}\r\n onMouseLeave={(e) => {\r\n e.currentTarget.style.backgroundColor = 'transparent'\r\n e.currentTarget.style.color = 'var(--brycks-foreground-muted)'\r\n }}\r\n >\r\n <CloseIcon />\r\n </button>\r\n )}\r\n </div>\r\n )}\r\n\r\n <div className=\"brycks-modal-content\" style={contentStyle}>\r\n {children}\r\n </div>\r\n </div>\r\n </div>\r\n )\r\n\r\n return createPortal(modalContent, document.body)\r\n})\r\n\r\nModal.displayName = 'Modal'\r\n"],"names":["sizeWidths","CloseIcon","memo","jsx","Modal","forwardRef","isOpen","onClose","size","title","description","closeOnOverlayClick","closeOnEscape","showCloseButton","className","style","testId","children","props","ref","modalRef","useRef","previousActiveElement","wasOpen","useEffect","onCloseRef","modal","handleKeyDown","e","focusableElements","firstElement","lastElement","handleOverlayClick","useCallback","overlayStyle","useMemo","modalStyle","headerStyle","titleContainerStyle","titleStyle","descriptionStyle","closeButtonStyle","contentStyle","modalContent","jsxs","node","cx","createPortal"],"mappings":"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"}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { jsx as
|
|
2
|
-
import { forwardRef as
|
|
3
|
-
import { createPortal as
|
|
4
|
-
import { cx as
|
|
5
|
-
const
|
|
1
|
+
import { jsx as r, jsxs as b } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef as q, useRef as s, useEffect as i, useCallback as F, useMemo as S, memo as P } from "react";
|
|
3
|
+
import { createPortal as $ } from "react-dom";
|
|
4
|
+
import { cx as G } from "../../../utils/styles.js";
|
|
5
|
+
const J = {
|
|
6
6
|
sm: "400px",
|
|
7
7
|
md: "500px",
|
|
8
8
|
lg: "640px",
|
|
9
9
|
xl: "800px",
|
|
10
10
|
full: "calc(100vw - 48px)"
|
|
11
|
-
},
|
|
12
|
-
return /* @__PURE__ */
|
|
11
|
+
}, Q = P(function() {
|
|
12
|
+
return /* @__PURE__ */ r("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ r(
|
|
13
13
|
"path",
|
|
14
14
|
{
|
|
15
15
|
d: "M4 4L12 12M12 4L4 12",
|
|
@@ -18,50 +18,56 @@ const G = {
|
|
|
18
18
|
strokeLinecap: "round"
|
|
19
19
|
}
|
|
20
20
|
) });
|
|
21
|
-
}),
|
|
22
|
-
isOpen:
|
|
23
|
-
onClose:
|
|
21
|
+
}), U = q(function({
|
|
22
|
+
isOpen: t,
|
|
23
|
+
onClose: o,
|
|
24
24
|
size: a = "md",
|
|
25
25
|
title: l,
|
|
26
|
-
description:
|
|
27
|
-
closeOnOverlayClick:
|
|
28
|
-
closeOnEscape:
|
|
29
|
-
showCloseButton:
|
|
30
|
-
className:
|
|
31
|
-
style:
|
|
32
|
-
testId:
|
|
33
|
-
children:
|
|
34
|
-
...
|
|
26
|
+
description: u,
|
|
27
|
+
closeOnOverlayClick: v = !0,
|
|
28
|
+
closeOnEscape: k = !0,
|
|
29
|
+
showCloseButton: g = !0,
|
|
30
|
+
className: M,
|
|
31
|
+
style: h,
|
|
32
|
+
testId: L,
|
|
33
|
+
children: I,
|
|
34
|
+
...T
|
|
35
35
|
}, c) {
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}, [
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
const m = s(null), y = s(null), d = s(!1);
|
|
37
|
+
i(() => {
|
|
38
|
+
t && !d.current ? (y.current = document.activeElement, d.current = !0) : !t && d.current && (document.activeElement instanceof HTMLElement && document.activeElement.blur(), y.current?.focus(), y.current = null, d.current = !1);
|
|
39
|
+
}, [t]);
|
|
40
|
+
const x = s(o);
|
|
41
|
+
i(() => {
|
|
42
|
+
x.current = o;
|
|
43
|
+
}, [o]), i(() => {
|
|
44
|
+
if (!t) return;
|
|
45
|
+
const e = m.current;
|
|
42
46
|
e && e.focus();
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
47
|
+
}, [t]), i(() => {
|
|
48
|
+
if (!t) return;
|
|
49
|
+
const e = m.current, p = (n) => {
|
|
50
|
+
if (n.key === "Escape" && k) {
|
|
51
|
+
x.current();
|
|
46
52
|
return;
|
|
47
53
|
}
|
|
48
|
-
if (
|
|
49
|
-
const
|
|
54
|
+
if (n.key === "Tab" && e) {
|
|
55
|
+
const f = e.querySelectorAll(
|
|
50
56
|
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
51
|
-
),
|
|
52
|
-
|
|
57
|
+
), w = f[0], E = f[f.length - 1];
|
|
58
|
+
n.shiftKey && document.activeElement === w ? (n.preventDefault(), E?.focus()) : !n.shiftKey && document.activeElement === E && (n.preventDefault(), w?.focus());
|
|
53
59
|
}
|
|
54
60
|
};
|
|
55
|
-
return document.addEventListener("keydown",
|
|
56
|
-
document.removeEventListener("keydown",
|
|
61
|
+
return document.addEventListener("keydown", p), document.body.style.overflow = "hidden", () => {
|
|
62
|
+
document.removeEventListener("keydown", p), document.body.style.overflow = "";
|
|
57
63
|
};
|
|
58
|
-
}, [
|
|
59
|
-
const
|
|
64
|
+
}, [t, k]);
|
|
65
|
+
const N = F(
|
|
60
66
|
(e) => {
|
|
61
|
-
e.target === e.currentTarget &&
|
|
67
|
+
e.target === e.currentTarget && v && o();
|
|
62
68
|
},
|
|
63
|
-
[
|
|
64
|
-
),
|
|
69
|
+
[v, o]
|
|
70
|
+
), R = S(() => ({
|
|
65
71
|
position: "fixed",
|
|
66
72
|
inset: 0,
|
|
67
73
|
zIndex: "var(--brycks-z-modal)",
|
|
@@ -72,10 +78,10 @@ const G = {
|
|
|
72
78
|
backgroundColor: "var(--brycks-background-overlay)",
|
|
73
79
|
backdropFilter: "blur(4px)",
|
|
74
80
|
animation: "brycks-fade-in var(--brycks-duration-normal) var(--brycks-ease-out)"
|
|
75
|
-
}), []), j =
|
|
81
|
+
}), []), j = S(() => ({
|
|
76
82
|
position: "relative",
|
|
77
83
|
width: "100%",
|
|
78
|
-
maxWidth:
|
|
84
|
+
maxWidth: J[a],
|
|
79
85
|
maxHeight: a === "full" ? "calc(100vh - 48px)" : "85vh",
|
|
80
86
|
backgroundColor: "var(--brycks-background-elevated)",
|
|
81
87
|
borderRadius: "var(--brycks-radius-xl)",
|
|
@@ -85,30 +91,30 @@ const G = {
|
|
|
85
91
|
overflow: "hidden",
|
|
86
92
|
animation: "brycks-scale-in var(--brycks-duration-normal) var(--brycks-ease-spring)",
|
|
87
93
|
outline: "none",
|
|
88
|
-
...
|
|
89
|
-
}), [a,
|
|
90
|
-
if (!
|
|
94
|
+
...h
|
|
95
|
+
}), [a, h]);
|
|
96
|
+
if (!t) return null;
|
|
91
97
|
const D = {
|
|
92
98
|
display: "flex",
|
|
93
99
|
alignItems: "flex-start",
|
|
94
100
|
justifyContent: "space-between",
|
|
95
101
|
padding: "20px 24px",
|
|
96
102
|
borderBottom: "1px solid var(--brycks-border-muted)"
|
|
97
|
-
},
|
|
103
|
+
}, W = {
|
|
98
104
|
display: "flex",
|
|
99
105
|
flexDirection: "column",
|
|
100
106
|
gap: 4
|
|
101
|
-
},
|
|
107
|
+
}, H = {
|
|
102
108
|
fontSize: 18,
|
|
103
109
|
fontWeight: 600,
|
|
104
110
|
color: "var(--brycks-foreground-default)",
|
|
105
111
|
margin: 0,
|
|
106
112
|
lineHeight: 1.3
|
|
107
|
-
},
|
|
113
|
+
}, K = {
|
|
108
114
|
fontSize: 14,
|
|
109
115
|
color: "var(--brycks-foreground-muted)",
|
|
110
116
|
margin: 0
|
|
111
|
-
},
|
|
117
|
+
}, z = {
|
|
112
118
|
display: "flex",
|
|
113
119
|
alignItems: "center",
|
|
114
120
|
justifyContent: "center",
|
|
@@ -119,44 +125,44 @@ const G = {
|
|
|
119
125
|
transition: "all var(--brycks-duration-fast) var(--brycks-ease-out)",
|
|
120
126
|
marginLeft: 12,
|
|
121
127
|
flexShrink: 0
|
|
122
|
-
},
|
|
128
|
+
}, A = {
|
|
123
129
|
flex: 1,
|
|
124
130
|
overflow: "auto",
|
|
125
131
|
padding: 24
|
|
126
|
-
},
|
|
132
|
+
}, B = /* @__PURE__ */ r(
|
|
127
133
|
"div",
|
|
128
134
|
{
|
|
129
135
|
className: "brycks-modal-overlay",
|
|
130
|
-
style:
|
|
131
|
-
onClick:
|
|
132
|
-
children: /* @__PURE__ */
|
|
136
|
+
style: R,
|
|
137
|
+
onClick: N,
|
|
138
|
+
children: /* @__PURE__ */ b(
|
|
133
139
|
"div",
|
|
134
140
|
{
|
|
135
141
|
ref: (e) => {
|
|
136
|
-
|
|
142
|
+
m.current = e, typeof c == "function" ? c(e) : c && (c.current = e);
|
|
137
143
|
},
|
|
138
144
|
role: "dialog",
|
|
139
145
|
"aria-modal": "true",
|
|
140
146
|
"aria-labelledby": l ? "brycks-modal-title" : void 0,
|
|
141
|
-
"aria-describedby":
|
|
142
|
-
className:
|
|
147
|
+
"aria-describedby": u ? "brycks-modal-description" : void 0,
|
|
148
|
+
className: G("brycks-modal", `brycks-modal--${a}`, M),
|
|
143
149
|
style: j,
|
|
144
150
|
tabIndex: -1,
|
|
145
|
-
"data-testid":
|
|
146
|
-
...
|
|
151
|
+
"data-testid": L,
|
|
152
|
+
...T,
|
|
147
153
|
children: [
|
|
148
|
-
(l ||
|
|
149
|
-
/* @__PURE__ */
|
|
150
|
-
l && /* @__PURE__ */
|
|
151
|
-
|
|
154
|
+
(l || g) && /* @__PURE__ */ b("div", { className: "brycks-modal-header", style: D, children: [
|
|
155
|
+
/* @__PURE__ */ b("div", { style: W, children: [
|
|
156
|
+
l && /* @__PURE__ */ r("h2", { id: "brycks-modal-title", style: H, children: l }),
|
|
157
|
+
u && /* @__PURE__ */ r("p", { id: "brycks-modal-description", style: K, children: u })
|
|
152
158
|
] }),
|
|
153
|
-
|
|
159
|
+
g && /* @__PURE__ */ r(
|
|
154
160
|
"button",
|
|
155
161
|
{
|
|
156
162
|
type: "button",
|
|
157
163
|
className: "brycks-modal-close",
|
|
158
|
-
style:
|
|
159
|
-
onClick:
|
|
164
|
+
style: z,
|
|
165
|
+
onClick: o,
|
|
160
166
|
"aria-label": "Close modal",
|
|
161
167
|
onMouseEnter: (e) => {
|
|
162
168
|
e.currentTarget.style.backgroundColor = "var(--brycks-background-muted)", e.currentTarget.style.color = "var(--brycks-foreground-default)";
|
|
@@ -164,20 +170,20 @@ const G = {
|
|
|
164
170
|
onMouseLeave: (e) => {
|
|
165
171
|
e.currentTarget.style.backgroundColor = "transparent", e.currentTarget.style.color = "var(--brycks-foreground-muted)";
|
|
166
172
|
},
|
|
167
|
-
children: /* @__PURE__ */
|
|
173
|
+
children: /* @__PURE__ */ r(Q, {})
|
|
168
174
|
}
|
|
169
175
|
)
|
|
170
176
|
] }),
|
|
171
|
-
/* @__PURE__ */
|
|
177
|
+
/* @__PURE__ */ r("div", { className: "brycks-modal-content", style: A, children: I })
|
|
172
178
|
]
|
|
173
179
|
}
|
|
174
180
|
)
|
|
175
181
|
}
|
|
176
182
|
);
|
|
177
|
-
return
|
|
183
|
+
return $(B, document.body);
|
|
178
184
|
});
|
|
179
|
-
|
|
185
|
+
U.displayName = "Modal";
|
|
180
186
|
export {
|
|
181
|
-
|
|
187
|
+
U as Modal
|
|
182
188
|
};
|
|
183
189
|
//# sourceMappingURL=Modal.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Modal.js","sources":["../../../../src/components/feedback/Modal/Modal.tsx"],"sourcesContent":["/**\r\n * Modal Component\r\n *\r\n * Accessible modal dialog with smooth Apple-inspired animations.\r\n * Supports focus trapping and keyboard navigation.\r\n */\r\n\r\nimport {\r\n forwardRef,\r\n useEffect,\r\n useRef,\r\n useCallback,\r\n useMemo,\r\n memo,\r\n type CSSProperties,\r\n type ReactNode,\r\n type HTMLAttributes,\r\n} from 'react'\r\nimport { createPortal } from 'react-dom'\r\nimport { cx } from '../../../utils/styles'\r\n\r\nexport type ModalSize = 'sm' | 'md' | 'lg' | 'xl' | 'full'\r\n\r\nexport interface ModalProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {\r\n /** Whether the modal is open */\r\n isOpen: boolean\r\n /** Callback when modal should close */\r\n onClose: () => void\r\n /** Modal size */\r\n size?: ModalSize\r\n /** Modal title */\r\n title?: ReactNode\r\n /** Modal description */\r\n description?: ReactNode\r\n /** Whether to close on overlay click */\r\n closeOnOverlayClick?: boolean\r\n /** Whether to close on escape key */\r\n closeOnEscape?: boolean\r\n /** Whether to show close button */\r\n showCloseButton?: boolean\r\n /** Custom class name */\r\n className?: string\r\n /** Test ID */\r\n testId?: string\r\n /** Modal content */\r\n children?: ReactNode\r\n}\r\n\r\nconst sizeWidths: Record<ModalSize, string> = {\r\n sm: '400px',\r\n md: '500px',\r\n lg: '640px',\r\n xl: '800px',\r\n full: 'calc(100vw - 48px)',\r\n}\r\n\r\n/** Close icon - memoized to prevent re-renders */\r\nconst CloseIcon = memo(function CloseIcon() {\r\n return (\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden=\"true\">\r\n <path\r\n d=\"M4 4L12 12M12 4L4 12\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"1.5\"\r\n strokeLinecap=\"round\"\r\n />\r\n </svg>\r\n )\r\n})\r\n\r\nexport const Modal = forwardRef<HTMLDivElement, ModalProps>(function Modal(\r\n {\r\n isOpen,\r\n onClose,\r\n size = 'md',\r\n title,\r\n description,\r\n closeOnOverlayClick = true,\r\n closeOnEscape = true,\r\n showCloseButton = true,\r\n className,\r\n style,\r\n testId,\r\n children,\r\n ...props\r\n },\r\n ref\r\n) {\r\n const modalRef = useRef<HTMLDivElement>(null)\r\n const previousActiveElement = useRef<HTMLElement | null>(null)\r\n const wasOpen = useRef(false)\r\n\r\n // Track previous open state to handle focus restoration before unmount\r\n useEffect(() => {\r\n // Modal just opened\r\n if (isOpen && !wasOpen.current) {\r\n previousActiveElement.current = document.activeElement as HTMLElement\r\n wasOpen.current = true\r\n }\r\n // Modal is about to close - restore focus BEFORE unmount\r\n else if (!isOpen && wasOpen.current) {\r\n // Blur any focused element inside modal to prevent aria-hidden warning\r\n if (document.activeElement instanceof HTMLElement) {\r\n document.activeElement.blur()\r\n }\r\n // Restore focus to previous element\r\n previousActiveElement.current?.focus()\r\n previousActiveElement.current = null\r\n wasOpen.current = false\r\n }\r\n }, [isOpen])\r\n\r\n // Focus trap and keyboard handling\r\n useEffect(() => {\r\n if (!isOpen) return\r\n\r\n const modal = modalRef.current\r\n if (modal) {\r\n modal.focus()\r\n }\r\n\r\n const handleKeyDown = (e: KeyboardEvent) => {\r\n if (e.key === 'Escape' && closeOnEscape) {\r\n onClose()\r\n return\r\n }\r\n\r\n if (e.key === 'Tab' && modal) {\r\n const focusableElements = modal.querySelectorAll<HTMLElement>(\r\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])'\r\n )\r\n const firstElement = focusableElements[0]\r\n const lastElement = focusableElements[focusableElements.length - 1]\r\n\r\n if (e.shiftKey && document.activeElement === firstElement) {\r\n e.preventDefault()\r\n lastElement?.focus()\r\n } else if (!e.shiftKey && document.activeElement === lastElement) {\r\n e.preventDefault()\r\n firstElement?.focus()\r\n }\r\n }\r\n }\r\n\r\n document.addEventListener('keydown', handleKeyDown)\r\n document.body.style.overflow = 'hidden'\r\n\r\n return () => {\r\n document.removeEventListener('keydown', handleKeyDown)\r\n document.body.style.overflow = ''\r\n }\r\n }, [isOpen, closeOnEscape, onClose])\r\n\r\n const handleOverlayClick = useCallback(\r\n (e: React.MouseEvent) => {\r\n if (e.target === e.currentTarget && closeOnOverlayClick) {\r\n onClose()\r\n }\r\n },\r\n [closeOnOverlayClick, onClose]\r\n )\r\n\r\n // Memoize styles to prevent object recreation on every render\r\n const overlayStyle = useMemo<CSSProperties>(() => ({\r\n position: 'fixed',\r\n inset: 0,\r\n zIndex: 'var(--brycks-z-modal)',\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n padding: 24,\r\n backgroundColor: 'var(--brycks-background-overlay)',\r\n backdropFilter: 'blur(4px)',\r\n animation: 'brycks-fade-in var(--brycks-duration-normal) var(--brycks-ease-out)',\r\n }), [])\r\n\r\n const modalStyle = useMemo<CSSProperties>(() => ({\r\n position: 'relative',\r\n width: '100%',\r\n maxWidth: sizeWidths[size],\r\n maxHeight: size === 'full' ? 'calc(100vh - 48px)' : '85vh',\r\n backgroundColor: 'var(--brycks-background-elevated)',\r\n borderRadius: 'var(--brycks-radius-xl)',\r\n boxShadow: 'var(--brycks-shadow-2xl)',\r\n display: 'flex',\r\n flexDirection: 'column',\r\n overflow: 'hidden',\r\n animation: 'brycks-scale-in var(--brycks-duration-normal) var(--brycks-ease-spring)',\r\n outline: 'none',\r\n ...style,\r\n }), [size, style])\r\n\r\n if (!isOpen) return null\r\n\r\n const headerStyle: CSSProperties = {\r\n display: 'flex',\r\n alignItems: 'flex-start',\r\n justifyContent: 'space-between',\r\n padding: '20px 24px',\r\n borderBottom: '1px solid var(--brycks-border-muted)',\r\n }\r\n\r\n const titleContainerStyle: CSSProperties = {\r\n display: 'flex',\r\n flexDirection: 'column',\r\n gap: 4,\r\n }\r\n\r\n const titleStyle: CSSProperties = {\r\n fontSize: 18,\r\n fontWeight: 600,\r\n color: 'var(--brycks-foreground-default)',\r\n margin: 0,\r\n lineHeight: 1.3,\r\n }\r\n\r\n const descriptionStyle: CSSProperties = {\r\n fontSize: 14,\r\n color: 'var(--brycks-foreground-muted)',\r\n margin: 0,\r\n }\r\n\r\n const closeButtonStyle: CSSProperties = {\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n width: 32,\r\n height: 32,\r\n borderRadius: 'var(--brycks-radius-md)',\r\n color: 'var(--brycks-foreground-muted)',\r\n transition: 'all var(--brycks-duration-fast) var(--brycks-ease-out)',\r\n marginLeft: 12,\r\n flexShrink: 0,\r\n }\r\n\r\n const contentStyle: CSSProperties = {\r\n flex: 1,\r\n overflow: 'auto',\r\n padding: 24,\r\n }\r\n\r\n const modalContent = (\r\n <div\r\n className=\"brycks-modal-overlay\"\r\n style={overlayStyle}\r\n onClick={handleOverlayClick}\r\n >\r\n <div\r\n ref={(node) => {\r\n (modalRef as React.MutableRefObject<HTMLDivElement | null>).current = node\r\n if (typeof ref === 'function') ref(node)\r\n else if (ref) (ref as React.MutableRefObject<HTMLDivElement | null>).current = node\r\n }}\r\n role=\"dialog\"\r\n aria-modal=\"true\"\r\n aria-labelledby={title ? 'brycks-modal-title' : undefined}\r\n aria-describedby={description ? 'brycks-modal-description' : undefined}\r\n className={cx('brycks-modal', `brycks-modal--${size}`, className)}\r\n style={modalStyle}\r\n tabIndex={-1}\r\n data-testid={testId}\r\n {...props}\r\n >\r\n {(title || showCloseButton) && (\r\n <div className=\"brycks-modal-header\" style={headerStyle}>\r\n <div style={titleContainerStyle}>\r\n {title && (\r\n <h2 id=\"brycks-modal-title\" style={titleStyle}>\r\n {title}\r\n </h2>\r\n )}\r\n {description && (\r\n <p id=\"brycks-modal-description\" style={descriptionStyle}>\r\n {description}\r\n </p>\r\n )}\r\n </div>\r\n {showCloseButton && (\r\n <button\r\n type=\"button\"\r\n className=\"brycks-modal-close\"\r\n style={closeButtonStyle}\r\n onClick={onClose}\r\n aria-label=\"Close modal\"\r\n onMouseEnter={(e) => {\r\n e.currentTarget.style.backgroundColor = 'var(--brycks-background-muted)'\r\n e.currentTarget.style.color = 'var(--brycks-foreground-default)'\r\n }}\r\n onMouseLeave={(e) => {\r\n e.currentTarget.style.backgroundColor = 'transparent'\r\n e.currentTarget.style.color = 'var(--brycks-foreground-muted)'\r\n }}\r\n >\r\n <CloseIcon />\r\n </button>\r\n )}\r\n </div>\r\n )}\r\n\r\n <div className=\"brycks-modal-content\" style={contentStyle}>\r\n {children}\r\n </div>\r\n </div>\r\n </div>\r\n )\r\n\r\n return createPortal(modalContent, document.body)\r\n})\r\n\r\nModal.displayName = 'Modal'\r\n"],"names":["sizeWidths","CloseIcon","memo","jsx","Modal","forwardRef","isOpen","onClose","size","title","description","closeOnOverlayClick","closeOnEscape","showCloseButton","className","style","testId","children","props","ref","modalRef","useRef","previousActiveElement","wasOpen","useEffect","modal","handleKeyDown","e","focusableElements","firstElement","lastElement","handleOverlayClick","useCallback","overlayStyle","useMemo","modalStyle","headerStyle","titleContainerStyle","titleStyle","descriptionStyle","closeButtonStyle","contentStyle","modalContent","jsxs","node","cx","createPortal"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Modal.js","sources":["../../../../src/components/feedback/Modal/Modal.tsx"],"sourcesContent":["/**\r\n * Modal Component\r\n *\r\n * Accessible modal dialog with smooth Apple-inspired animations.\r\n * Supports focus trapping and keyboard navigation.\r\n */\r\n\r\nimport {\r\n forwardRef,\r\n useEffect,\r\n useRef,\r\n useCallback,\r\n useMemo,\r\n memo,\r\n type CSSProperties,\r\n type ReactNode,\r\n type HTMLAttributes,\r\n} from 'react'\r\nimport { createPortal } from 'react-dom'\r\nimport { cx } from '../../../utils/styles'\r\n\r\nexport type ModalSize = 'sm' | 'md' | 'lg' | 'xl' | 'full'\r\n\r\nexport interface ModalProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {\r\n /** Whether the modal is open */\r\n isOpen: boolean\r\n /** Callback when modal should close */\r\n onClose: () => void\r\n /** Modal size */\r\n size?: ModalSize\r\n /** Modal title */\r\n title?: ReactNode\r\n /** Modal description */\r\n description?: ReactNode\r\n /** Whether to close on overlay click */\r\n closeOnOverlayClick?: boolean\r\n /** Whether to close on escape key */\r\n closeOnEscape?: boolean\r\n /** Whether to show close button */\r\n showCloseButton?: boolean\r\n /** Custom class name */\r\n className?: string\r\n /** Test ID */\r\n testId?: string\r\n /** Modal content */\r\n children?: ReactNode\r\n}\r\n\r\nconst sizeWidths: Record<ModalSize, string> = {\r\n sm: '400px',\r\n md: '500px',\r\n lg: '640px',\r\n xl: '800px',\r\n full: 'calc(100vw - 48px)',\r\n}\r\n\r\n/** Close icon - memoized to prevent re-renders */\r\nconst CloseIcon = memo(function CloseIcon() {\r\n return (\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden=\"true\">\r\n <path\r\n d=\"M4 4L12 12M12 4L4 12\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"1.5\"\r\n strokeLinecap=\"round\"\r\n />\r\n </svg>\r\n )\r\n})\r\n\r\nexport const Modal = forwardRef<HTMLDivElement, ModalProps>(function Modal(\r\n {\r\n isOpen,\r\n onClose,\r\n size = 'md',\r\n title,\r\n description,\r\n closeOnOverlayClick = true,\r\n closeOnEscape = true,\r\n showCloseButton = true,\r\n className,\r\n style,\r\n testId,\r\n children,\r\n ...props\r\n },\r\n ref\r\n) {\r\n const modalRef = useRef<HTMLDivElement>(null)\r\n const previousActiveElement = useRef<HTMLElement | null>(null)\r\n const wasOpen = useRef(false)\r\n\r\n // Track previous open state to handle focus restoration before unmount\r\n useEffect(() => {\r\n // Modal just opened\r\n if (isOpen && !wasOpen.current) {\r\n previousActiveElement.current = document.activeElement as HTMLElement\r\n wasOpen.current = true\r\n }\r\n // Modal is about to close - restore focus BEFORE unmount\r\n else if (!isOpen && wasOpen.current) {\r\n // Blur any focused element inside modal to prevent aria-hidden warning\r\n if (document.activeElement instanceof HTMLElement) {\r\n document.activeElement.blur()\r\n }\r\n // Restore focus to previous element\r\n previousActiveElement.current?.focus()\r\n previousActiveElement.current = null\r\n wasOpen.current = false\r\n }\r\n }, [isOpen])\r\n\r\n // Store onClose in a ref to avoid re-running effects when it changes\r\n const onCloseRef = useRef(onClose)\r\n useEffect(() => {\r\n onCloseRef.current = onClose\r\n }, [onClose])\r\n\r\n // Focus modal when it opens (only once)\r\n useEffect(() => {\r\n if (!isOpen) return\r\n\r\n const modal = modalRef.current\r\n if (modal) {\r\n modal.focus()\r\n }\r\n }, [isOpen])\r\n\r\n // Keyboard handling and body scroll lock\r\n useEffect(() => {\r\n if (!isOpen) return\r\n\r\n const modal = modalRef.current\r\n\r\n const handleKeyDown = (e: KeyboardEvent) => {\r\n if (e.key === 'Escape' && closeOnEscape) {\r\n onCloseRef.current()\r\n return\r\n }\r\n\r\n if (e.key === 'Tab' && modal) {\r\n const focusableElements = modal.querySelectorAll<HTMLElement>(\r\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])'\r\n )\r\n const firstElement = focusableElements[0]\r\n const lastElement = focusableElements[focusableElements.length - 1]\r\n\r\n if (e.shiftKey && document.activeElement === firstElement) {\r\n e.preventDefault()\r\n lastElement?.focus()\r\n } else if (!e.shiftKey && document.activeElement === lastElement) {\r\n e.preventDefault()\r\n firstElement?.focus()\r\n }\r\n }\r\n }\r\n\r\n document.addEventListener('keydown', handleKeyDown)\r\n document.body.style.overflow = 'hidden'\r\n\r\n return () => {\r\n document.removeEventListener('keydown', handleKeyDown)\r\n document.body.style.overflow = ''\r\n }\r\n }, [isOpen, closeOnEscape])\r\n\r\n const handleOverlayClick = useCallback(\r\n (e: React.MouseEvent) => {\r\n if (e.target === e.currentTarget && closeOnOverlayClick) {\r\n onClose()\r\n }\r\n },\r\n [closeOnOverlayClick, onClose]\r\n )\r\n\r\n // Memoize styles to prevent object recreation on every render\r\n const overlayStyle = useMemo<CSSProperties>(() => ({\r\n position: 'fixed',\r\n inset: 0,\r\n zIndex: 'var(--brycks-z-modal)',\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n padding: 24,\r\n backgroundColor: 'var(--brycks-background-overlay)',\r\n backdropFilter: 'blur(4px)',\r\n animation: 'brycks-fade-in var(--brycks-duration-normal) var(--brycks-ease-out)',\r\n }), [])\r\n\r\n const modalStyle = useMemo<CSSProperties>(() => ({\r\n position: 'relative',\r\n width: '100%',\r\n maxWidth: sizeWidths[size],\r\n maxHeight: size === 'full' ? 'calc(100vh - 48px)' : '85vh',\r\n backgroundColor: 'var(--brycks-background-elevated)',\r\n borderRadius: 'var(--brycks-radius-xl)',\r\n boxShadow: 'var(--brycks-shadow-2xl)',\r\n display: 'flex',\r\n flexDirection: 'column',\r\n overflow: 'hidden',\r\n animation: 'brycks-scale-in var(--brycks-duration-normal) var(--brycks-ease-spring)',\r\n outline: 'none',\r\n ...style,\r\n }), [size, style])\r\n\r\n if (!isOpen) return null\r\n\r\n const headerStyle: CSSProperties = {\r\n display: 'flex',\r\n alignItems: 'flex-start',\r\n justifyContent: 'space-between',\r\n padding: '20px 24px',\r\n borderBottom: '1px solid var(--brycks-border-muted)',\r\n }\r\n\r\n const titleContainerStyle: CSSProperties = {\r\n display: 'flex',\r\n flexDirection: 'column',\r\n gap: 4,\r\n }\r\n\r\n const titleStyle: CSSProperties = {\r\n fontSize: 18,\r\n fontWeight: 600,\r\n color: 'var(--brycks-foreground-default)',\r\n margin: 0,\r\n lineHeight: 1.3,\r\n }\r\n\r\n const descriptionStyle: CSSProperties = {\r\n fontSize: 14,\r\n color: 'var(--brycks-foreground-muted)',\r\n margin: 0,\r\n }\r\n\r\n const closeButtonStyle: CSSProperties = {\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n width: 32,\r\n height: 32,\r\n borderRadius: 'var(--brycks-radius-md)',\r\n color: 'var(--brycks-foreground-muted)',\r\n transition: 'all var(--brycks-duration-fast) var(--brycks-ease-out)',\r\n marginLeft: 12,\r\n flexShrink: 0,\r\n }\r\n\r\n const contentStyle: CSSProperties = {\r\n flex: 1,\r\n overflow: 'auto',\r\n padding: 24,\r\n }\r\n\r\n const modalContent = (\r\n <div\r\n className=\"brycks-modal-overlay\"\r\n style={overlayStyle}\r\n onClick={handleOverlayClick}\r\n >\r\n <div\r\n ref={(node) => {\r\n (modalRef as React.MutableRefObject<HTMLDivElement | null>).current = node\r\n if (typeof ref === 'function') ref(node)\r\n else if (ref) (ref as React.MutableRefObject<HTMLDivElement | null>).current = node\r\n }}\r\n role=\"dialog\"\r\n aria-modal=\"true\"\r\n aria-labelledby={title ? 'brycks-modal-title' : undefined}\r\n aria-describedby={description ? 'brycks-modal-description' : undefined}\r\n className={cx('brycks-modal', `brycks-modal--${size}`, className)}\r\n style={modalStyle}\r\n tabIndex={-1}\r\n data-testid={testId}\r\n {...props}\r\n >\r\n {(title || showCloseButton) && (\r\n <div className=\"brycks-modal-header\" style={headerStyle}>\r\n <div style={titleContainerStyle}>\r\n {title && (\r\n <h2 id=\"brycks-modal-title\" style={titleStyle}>\r\n {title}\r\n </h2>\r\n )}\r\n {description && (\r\n <p id=\"brycks-modal-description\" style={descriptionStyle}>\r\n {description}\r\n </p>\r\n )}\r\n </div>\r\n {showCloseButton && (\r\n <button\r\n type=\"button\"\r\n className=\"brycks-modal-close\"\r\n style={closeButtonStyle}\r\n onClick={onClose}\r\n aria-label=\"Close modal\"\r\n onMouseEnter={(e) => {\r\n e.currentTarget.style.backgroundColor = 'var(--brycks-background-muted)'\r\n e.currentTarget.style.color = 'var(--brycks-foreground-default)'\r\n }}\r\n onMouseLeave={(e) => {\r\n e.currentTarget.style.backgroundColor = 'transparent'\r\n e.currentTarget.style.color = 'var(--brycks-foreground-muted)'\r\n }}\r\n >\r\n <CloseIcon />\r\n </button>\r\n )}\r\n </div>\r\n )}\r\n\r\n <div className=\"brycks-modal-content\" style={contentStyle}>\r\n {children}\r\n </div>\r\n </div>\r\n </div>\r\n )\r\n\r\n return createPortal(modalContent, document.body)\r\n})\r\n\r\nModal.displayName = 'Modal'\r\n"],"names":["sizeWidths","CloseIcon","memo","jsx","Modal","forwardRef","isOpen","onClose","size","title","description","closeOnOverlayClick","closeOnEscape","showCloseButton","className","style","testId","children","props","ref","modalRef","useRef","previousActiveElement","wasOpen","useEffect","onCloseRef","modal","handleKeyDown","e","focusableElements","firstElement","lastElement","handleOverlayClick","useCallback","overlayStyle","useMemo","modalStyle","headerStyle","titleContainerStyle","titleStyle","descriptionStyle","closeButtonStyle","contentStyle","modalContent","jsxs","node","cx","createPortal"],"mappings":"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"}
|
package/dist/styles.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
*,*:before,*:after{box-sizing:border-box}html{-moz-text-size-adjust:none;-webkit-text-size-adjust:none;text-size-adjust:none}*{margin:0;padding:0}html,body{height:100%}body{min-height:100vh;line-height:1.5;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-rendering:optimizeLegibility}img,picture,video,canvas,svg{display:block;max-width:100%}input,button,textarea,select{font:inherit;color:inherit}p,h1,h2,h3,h4,h5,h6{overflow-wrap:break-word}ul[role=list],ol[role=list]{list-style:none}html:focus-within{scroll-behavior:smooth}a:not([class]){text-decoration-skip-ink:auto;color:currentColor}h1,h2,h3,h4,h5,h6{text-wrap:balance}p{text-wrap:pretty}textarea:not([rows]){min-height:10em}:target{scroll-margin-block:5ex}@media (prefers-reduced-motion: reduce){html:focus-within{scroll-behavior:auto}*,*:before,*:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important;scroll-behavior:auto!important}}:focus-visible{outline:2px solid var(--brycks-border-focus, #5578f4);outline-offset:2px}:focus:not(:focus-visible){outline:none}button{background:none;border:none;cursor:pointer}a{text-decoration:none}table{border-collapse:collapse;border-spacing:0}[hidden]{display:none!important}[disabled]{cursor:not-allowed}:root{--brycks-font-sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;--brycks-font-mono: "SF Mono", "Fira Code", "Fira Mono", "Roboto Mono", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--brycks-font-scale-body: 1;--brycks-font-scale-headings: 1;--brycks-font-scale-ui: 1;--brycks-fs-base-xs: 11px;--brycks-fs-base-sm: 12px;--brycks-fs-base-base: 14px;--brycks-fs-base-md: 16px;--brycks-fs-base-lg: 18px;--brycks-fs-base-xl: 20px;--brycks-fs-base-2xl: 24px;--brycks-fs-base-3xl: 28px;--brycks-fs-base-4xl: 32px;--brycks-fs-base-5xl: 40px;--brycks-fs-base-6xl: 48px;--brycks-fs-base-7xl: 60px;--brycks-fs-base-8xl: 72px;--brycks-fs-base-9xl: 96px;--brycks-fs-body-xs: calc(var(--brycks-fs-base-xs) * var(--brycks-font-scale-body));--brycks-fs-body-sm: calc(var(--brycks-fs-base-sm) * var(--brycks-font-scale-body));--brycks-fs-body-base: calc(var(--brycks-fs-base-base) * var(--brycks-font-scale-body));--brycks-fs-body-md: calc(var(--brycks-fs-base-md) * var(--brycks-font-scale-body));--brycks-fs-body-lg: calc(var(--brycks-fs-base-lg) * var(--brycks-font-scale-body));--brycks-fs-h6: calc(var(--brycks-fs-base-xl) * var(--brycks-font-scale-headings));--brycks-fs-h5: calc(var(--brycks-fs-base-2xl) * var(--brycks-font-scale-headings));--brycks-fs-h4: calc(var(--brycks-fs-base-3xl) * var(--brycks-font-scale-headings));--brycks-fs-h3: calc(var(--brycks-fs-base-4xl) * var(--brycks-font-scale-headings));--brycks-fs-h2: calc(var(--brycks-fs-base-5xl) * var(--brycks-font-scale-headings));--brycks-fs-h1: calc(var(--brycks-fs-base-6xl) * var(--brycks-font-scale-headings));--brycks-fs-display-sm: calc(var(--brycks-fs-base-7xl) * var(--brycks-font-scale-headings));--brycks-fs-display-md: calc(var(--brycks-fs-base-8xl) * var(--brycks-font-scale-headings));--brycks-fs-display-lg: calc(var(--brycks-fs-base-9xl) * var(--brycks-font-scale-headings));--brycks-fs-ui-xs: calc(var(--brycks-fs-base-xs) * var(--brycks-font-scale-ui));--brycks-fs-ui-sm: calc(var(--brycks-fs-base-sm) * var(--brycks-font-scale-ui));--brycks-fs-ui-md: calc(var(--brycks-fs-base-base) * var(--brycks-font-scale-ui));--brycks-fs-ui-lg: calc(15px * var(--brycks-font-scale-ui));--brycks-fs-ui-xl: calc(var(--brycks-fs-base-md) * var(--brycks-font-scale-ui));--brycks-font-size-xs: var(--brycks-fs-body-sm);--brycks-font-size-sm: var(--brycks-fs-body-base);--brycks-font-size-md: var(--brycks-fs-body-md);--brycks-font-size-lg: var(--brycks-fs-body-lg);--brycks-font-size-xl: var(--brycks-fs-h6);--brycks-font-size-2xl: var(--brycks-fs-h5);--brycks-font-size-3xl: var(--brycks-fs-h4);--brycks-font-size-4xl: var(--brycks-fs-h3);--brycks-space-0: 0;--brycks-space-px: 1px;--brycks-space-0-5: 2px;--brycks-space-1: 4px;--brycks-space-1-5: 6px;--brycks-space-2: 8px;--brycks-space-2-5: 10px;--brycks-space-3: 12px;--brycks-space-3-5: 14px;--brycks-space-4: 16px;--brycks-space-5: 20px;--brycks-space-6: 24px;--brycks-space-7: 28px;--brycks-space-8: 32px;--brycks-space-9: 36px;--brycks-space-10: 40px;--brycks-space-12: 48px;--brycks-space-14: 56px;--brycks-space-16: 64px;--brycks-space-20: 80px;--brycks-space-24: 96px;--brycks-spacing-1: var(--brycks-space-1);--brycks-spacing-2: var(--brycks-space-2);--brycks-spacing-2-5: var(--brycks-space-2-5);--brycks-spacing-3: var(--brycks-space-3);--brycks-spacing-4: var(--brycks-space-4);--brycks-spacing-5: var(--brycks-space-5);--brycks-spacing-6: var(--brycks-space-6);--brycks-spacing-8: var(--brycks-space-8);--brycks-radius-none: 0;--brycks-radius-xs: 2px;--brycks-radius-sm: 4px;--brycks-radius-md: 6px;--brycks-radius-default: 8px;--brycks-radius-lg: 10px;--brycks-radius-xl: 12px;--brycks-radius-2xl: 16px;--brycks-radius-3xl: 20px;--brycks-radius-4xl: 24px;--brycks-radius-full: 9999px;--brycks-shadow-xs: 0 1px 2px 0 rgba(0, 0, 0, .03), 0 1px 3px 0 rgba(0, 0, 0, .06);--brycks-shadow-sm: 0 1px 3px 0 rgba(0, 0, 0, .04), 0 2px 6px 0 rgba(0, 0, 0, .06), 0 0 1px 0 rgba(0, 0, 0, .04);--brycks-shadow-md: 0 2px 4px -1px rgba(0, 0, 0, .04), 0 4px 8px -1px rgba(0, 0, 0, .06), 0 8px 16px -2px rgba(0, 0, 0, .06);--brycks-shadow-lg: 0 4px 6px -2px rgba(0, 0, 0, .03), 0 8px 16px -4px rgba(0, 0, 0, .08), 0 16px 32px -8px rgba(0, 0, 0, .08);--brycks-shadow-xl: 0 8px 16px -4px rgba(0, 0, 0, .04), 0 16px 32px -8px rgba(0, 0, 0, .08), 0 32px 64px -16px rgba(0, 0, 0, .12);--brycks-shadow-2xl: 0 12px 24px -6px rgba(0, 0, 0, .05), 0 24px 48px -12px rgba(0, 0, 0, .1), 0 48px 96px -24px rgba(0, 0, 0, .15);--brycks-z-base: 0;--brycks-z-docked: 10;--brycks-z-dropdown: 1000;--brycks-z-sticky: 1100;--brycks-z-banner: 1200;--brycks-z-overlay: 1300;--brycks-z-modal: 1400;--brycks-z-popover: 1500;--brycks-z-toast: 1700;--brycks-z-tooltip: 1800;--brycks-duration-fast: .1s;--brycks-duration-normal: .2s;--brycks-duration-slow: .4s;--brycks-ease-out: cubic-bezier(0, 0, .2, 1);--brycks-ease-in-out: cubic-bezier(.4, 0, .2, 1);--brycks-ease-spring: cubic-bezier(.34, 1.56, .64, 1)}:root,[data-theme=light]{--brycks-background-app: #FFFFFF;--brycks-background-subtle: #FAFAFA;--brycks-background-muted: #F5F5F7;--brycks-background-elevated: #FFFFFF;--brycks-background-overlay: rgba(0, 0, 0, .4);--brycks-background-inverse: #1D1D1F;--brycks-background-default: #FAFAFA;--brycks-foreground-default: #1D1D1F;--brycks-foreground-muted: #636366;--brycks-foreground-subtle: #8E8E93;--brycks-foreground-disabled: #AEAEB2;--brycks-foreground-inverse: #FFFFFF;--brycks-foreground-link: #3B5CE9;--brycks-border-default: #E8E8ED;--brycks-border-muted: #F5F5F7;--brycks-border-strong: #D2D2D7;--brycks-border-focus: #5578F4;--brycks-primary-default: #3B5CE9;--brycks-primary-hover: #2D47D6;--brycks-primary-active: #2A3CAD;--brycks-primary-muted: #EEF4FF;--brycks-primary-soft-hover: #E0EBFF;--brycks-primary-foreground: #FFFFFF;--brycks-accent-default: #F86545;--brycks-accent-hover: #E54A28;--brycks-accent-active: #C13A1D;--brycks-accent-muted: #FFF5F3;--brycks-accent-soft-hover: #FFE8E3;--brycks-accent-foreground: #FFFFFF;--brycks-success-default: #059669;--brycks-success-hover: #047857;--brycks-success-muted: #ECFDF5;--brycks-success-soft-hover: #D1FAE5;--brycks-success-foreground: #FFFFFF;--brycks-success-on-muted: #047857;--brycks-warning-default: #F59E0B;--brycks-warning-hover: #D97706;--brycks-warning-muted: #FFFBEB;--brycks-warning-soft-hover: #FEF3C7;--brycks-warning-foreground: #1D1D1F;--brycks-warning-on-muted: #92400E;--brycks-error-default: #DC2626;--brycks-error-hover: #B91C1C;--brycks-error-muted: #FEF2F2;--brycks-error-soft-hover: #FEE2E2;--brycks-error-foreground: #FFFFFF;--brycks-error-on-muted: #B91C1C;--brycks-info-default: #0891B2;--brycks-info-hover: #0E7490;--brycks-info-muted: #ECFEFF;--brycks-info-soft-hover: #CFFAFE;--brycks-info-foreground: #FFFFFF;--brycks-info-on-muted: #0E7490}[data-theme=dark]{--brycks-background-app: #000000;--brycks-background-subtle: #1D1D1F;--brycks-background-muted: #2C2C2E;--brycks-background-elevated: #1D1D1F;--brycks-background-overlay: rgba(0, 0, 0, .6);--brycks-background-inverse: #FFFFFF;--brycks-background-default: #1D1D1F;--brycks-foreground-default: #FFFFFF;--brycks-foreground-muted: #AEAEB2;--brycks-foreground-subtle: #8E8E93;--brycks-foreground-disabled: #636366;--brycks-foreground-inverse: #1D1D1F;--brycks-foreground-link: #7A9FFF;--brycks-border-default: #3A3A3C;--brycks-border-muted: #2C2C2E;--brycks-border-strong: #48484A;--brycks-border-focus: #7A9FFF;--brycks-primary-default: #5578F4;--brycks-primary-hover: #7A9FFF;--brycks-primary-active: #A4C1FF;--brycks-primary-muted: #1A2352;--brycks-primary-soft-hover: #283889;--brycks-primary-foreground: #FFFFFF;--brycks-accent-default: #FF8A71;--brycks-accent-hover: #FFB5A6;--brycks-accent-active: #FFD5CC;--brycks-accent-muted: #48140A;--brycks-accent-soft-hover: #842F1D;--brycks-accent-foreground: #000000;--brycks-success-default: #34D399;--brycks-success-hover: #6EE7B7;--brycks-success-muted: #022C22;--brycks-success-soft-hover: #064E3B;--brycks-success-foreground: #000000;--brycks-success-on-muted: #6EE7B7;--brycks-warning-default: #FBBF24;--brycks-warning-hover: #FCD34D;--brycks-warning-muted: #451A03;--brycks-warning-soft-hover: #78350F;--brycks-warning-foreground: #000000;--brycks-warning-on-muted: #FCD34D;--brycks-error-default: #F87171;--brycks-error-hover: #FCA5A5;--brycks-error-muted: #450A0A;--brycks-error-soft-hover: #7F1D1D;--brycks-error-foreground: #000000;--brycks-error-on-muted: #FCA5A5;--brycks-info-default: #22D3EE;--brycks-info-hover: #67E8F9;--brycks-info-muted: #083344;--brycks-info-soft-hover: #164E63;--brycks-info-foreground: #000000;--brycks-info-on-muted: #67E8F9}html{font-family:var(--brycks-font-sans);color-scheme:light dark}body{font-family:var(--brycks-font-sans);font-size:16px;line-height:1.5;color:var(--brycks-foreground-default);background-color:var(--brycks-background-app);transition:background-color var(--brycks-duration-normal) var(--brycks-ease-out),color var(--brycks-duration-normal) var(--brycks-ease-out)}::selection{background-color:var(--brycks-primary-muted);color:var(--brycks-foreground-default)}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background-color:var(--brycks-border-default);border-radius:var(--brycks-radius-full);border:2px solid transparent;background-clip:content-box}::-webkit-scrollbar-thumb:hover{background-color:var(--brycks-border-strong)}*{scrollbar-width:thin;scrollbar-color:var(--brycks-border-default) transparent}code,kbd,pre,samp{font-family:var(--brycks-font-mono)}.brycks-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.brycks-app-layout{display:flex;height:100vh;overflow:hidden;background-color:var(--brycks-background-subtle)}.brycks-app-layout__main{flex:1;display:flex;flex-direction:column;overflow:hidden}.brycks-app-layout__content{flex:1;overflow:auto;padding:var(--brycks-spacing-6)}.brycks-sidebar{width:240px;height:100vh;background-color:var(--brycks-background-elevated);border-right:1px solid var(--brycks-border-muted);display:flex;flex-direction:column;flex-shrink:0;will-change:width;contain:layout style}.brycks-sidebar--collapsed{width:72px}.brycks-sidebar__logo{padding:var(--brycks-spacing-4) var(--brycks-spacing-5);display:flex;align-items:center;gap:var(--brycks-spacing-3)}.brycks-sidebar--collapsed .brycks-sidebar__logo{padding:var(--brycks-spacing-4) var(--brycks-spacing-3);justify-content:center}.brycks-sidebar__logo-icon{width:32px;height:32px;background-color:var(--brycks-primary-default);border-radius:var(--brycks-radius-md);display:flex;align-items:center;justify-content:center;color:var(--brycks-primary-foreground);font-weight:700;font-size:var(--brycks-font-size-sm);flex-shrink:0}.brycks-sidebar__logo-text{font-size:var(--brycks-font-size-lg);font-weight:600;color:var(--brycks-foreground-default)}.brycks-sidebar__nav{flex:1;padding:var(--brycks-spacing-2) var(--brycks-spacing-3);overflow-y:auto}.brycks-sidebar--collapsed .brycks-sidebar__nav{padding:var(--brycks-spacing-2)}.brycks-sidebar__footer{padding:var(--brycks-spacing-3);text-align:center}.brycks-sidebar--collapsed .brycks-sidebar__footer{padding:var(--brycks-spacing-3) var(--brycks-spacing-2)}.brycks-nav-item{display:flex;align-items:center;gap:var(--brycks-spacing-3);padding:var(--brycks-spacing-2-5) var(--brycks-spacing-3);border-radius:var(--brycks-radius-md);cursor:pointer;background-color:transparent;color:var(--brycks-foreground-default);transition:background-color var(--brycks-duration-fast) var(--brycks-ease-out),color var(--brycks-duration-fast) var(--brycks-ease-out);border:none;width:100%;text-align:left;font-size:var(--brycks-font-size-sm)}.brycks-nav-item:hover{background-color:var(--brycks-background-muted)}.brycks-nav-item--active{background-color:var(--brycks-primary-muted);color:var(--brycks-primary-default)}.brycks-nav-item--active:hover{background-color:var(--brycks-primary-muted)}.brycks-sidebar--collapsed .brycks-nav-item{padding:var(--brycks-spacing-3);justify-content:center}.brycks-nav-item__icon{width:20px;height:20px;flex-shrink:0;color:var(--brycks-foreground-muted)}.brycks-nav-item--active .brycks-nav-item__icon{color:var(--brycks-primary-default)}.brycks-nav-item__label{font-weight:400}.brycks-nav-item--active .brycks-nav-item__label{font-weight:500}.brycks-header{height:64px;background-color:var(--brycks-background-elevated);border-bottom:1px solid var(--brycks-border-muted);display:flex;align-items:center;justify-content:space-between;padding:0 var(--brycks-spacing-5);flex-shrink:0}.brycks-header__left,.brycks-header__right{display:flex;align-items:center}.brycks-header__left{gap:var(--brycks-spacing-4)}.brycks-header__right{gap:var(--brycks-spacing-2)}.brycks-icon-btn{display:flex;align-items:center;justify-content:center;width:36px;height:36px;border-radius:var(--brycks-radius-md);background-color:transparent;color:var(--brycks-foreground-muted);cursor:pointer;border:none;transition:background-color var(--brycks-duration-fast) var(--brycks-ease-out),color var(--brycks-duration-fast) var(--brycks-ease-out)}.brycks-icon-btn:hover{background-color:var(--brycks-background-muted);color:var(--brycks-foreground-default)}.brycks-icon-btn:focus-visible{outline:2px solid var(--brycks-primary-default);outline-offset:2px}.brycks-avatar{width:36px;height:36px;border-radius:var(--brycks-radius-full);background-color:var(--brycks-primary-default);color:var(--brycks-primary-foreground);display:flex;align-items:center;justify-content:center;font-weight:600;font-size:var(--brycks-font-size-sm);cursor:pointer;transition:transform var(--brycks-duration-fast) var(--brycks-ease-out)}.brycks-avatar:hover{transform:scale(1.05)}.brycks-avatar--xs{width:24px;height:24px;font-size:10px}.brycks-avatar--sm{width:32px;height:32px;font-size:12px}.brycks-avatar--md{width:36px;height:36px;font-size:14px}.brycks-avatar--lg{width:40px;height:40px;font-size:16px}.brycks-avatar--xl{width:48px;height:48px;font-size:18px}.brycks-avatar--2xl{width:64px;height:64px;font-size:24px}.brycks-page-header{margin-bottom:var(--brycks-spacing-6)}.brycks-stat-card__icon{width:48px;height:48px;border-radius:var(--brycks-radius-lg);display:flex;align-items:center;justify-content:center}.brycks-stat-card__icon--default{background-color:color-mix(in srgb,var(--brycks-foreground-muted) 10%,transparent);color:var(--brycks-foreground-muted)}.brycks-stat-card__icon--primary{background-color:var(--brycks-primary-muted);color:var(--brycks-primary-default)}.brycks-stat-card__icon--success{background-color:var(--brycks-success-muted);color:var(--brycks-success-default)}.brycks-stat-card__icon--warning{background-color:var(--brycks-warning-muted);color:var(--brycks-warning-default)}.brycks-stat-card__icon--error{background-color:var(--brycks-error-muted);color:var(--brycks-error-default)}.brycks-stat-card__trend{display:inline-flex;align-items:center;gap:var(--brycks-spacing-1);font-size:var(--brycks-font-size-xs);font-weight:500}.brycks-stat-card__trend--positive{color:var(--brycks-success-default)}.brycks-stat-card__trend--negative{color:var(--brycks-error-default)}.brycks-quick-action{padding:var(--brycks-spacing-4);border-radius:var(--brycks-radius-lg);background-color:var(--brycks-background-muted);cursor:pointer;transition:background-color var(--brycks-duration-fast) var(--brycks-ease-out);text-align:center;border:none;width:100%}.brycks-quick-action:hover{background-color:var(--brycks-primary-muted)}.brycks-quick-action:focus-visible{outline:2px solid var(--brycks-primary-default);outline-offset:2px}.brycks-quick-action__icon{color:var(--brycks-foreground-muted);margin-bottom:var(--brycks-spacing-2)}.brycks-quick-action:hover .brycks-quick-action__icon{color:var(--brycks-primary-default)}.brycks-search-input{position:relative;min-width:240px}.brycks-search-input__icon{position:absolute;left:var(--brycks-spacing-3);top:50%;transform:translateY(-50%);pointer-events:none;z-index:1;color:var(--brycks-foreground-muted);width:16px;height:16px}.brycks-search-input input{padding-left:36px}.brycks-login-container{min-height:100vh;display:flex;align-items:center;justify-content:center;padding:var(--brycks-spacing-6);background-color:var(--brycks-background-subtle)}.brycks-login-card{width:100%;max-width:400px}.brycks-login-logo{display:flex;align-items:center;justify-content:center;margin-bottom:var(--brycks-spacing-8)}.brycks-login-logo__icon{width:64px;height:64px;background-color:var(--brycks-primary-default);border-radius:var(--brycks-radius-xl);display:flex;align-items:center;justify-content:center;color:var(--brycks-primary-foreground);font-weight:700;font-size:28px}.brycks-login-footer{margin-top:var(--brycks-spacing-6);text-align:center}.brycks-user-info{padding:var(--brycks-spacing-2) var(--brycks-spacing-3)}.brycks-link{color:var(--brycks-primary-default);text-decoration:none;font-size:var(--brycks-font-size-sm);cursor:pointer;transition:color var(--brycks-duration-fast) var(--brycks-ease-out)}.brycks-link:hover{color:var(--brycks-primary-hover);text-decoration:underline}.brycks-login-form__options{display:flex;justify-content:space-between;align-items:center}.brycks-dropdown-item__content{display:flex;align-items:center;gap:var(--brycks-spacing-2)}.brycks-vehicle-card{cursor:pointer;transition:transform var(--brycks-duration-fast) var(--brycks-ease-out),box-shadow var(--brycks-duration-fast) var(--brycks-ease-out)}.brycks-vehicle-card:hover{transform:translateY(-2px);box-shadow:var(--brycks-shadow-lg)}.brycks-vehicle-card__image{width:100%;aspect-ratio:16/10;background-color:var(--brycks-background-muted);border-radius:var(--brycks-radius-md);display:flex;align-items:center;justify-content:center;color:var(--brycks-foreground-muted);margin-bottom:var(--brycks-spacing-3)}.brycks-vehicle-card__price{color:var(--brycks-primary-default)}.brycks-filter-select{min-width:160px}.brycks-page-card{cursor:pointer;transition:border-color var(--brycks-duration-fast) var(--brycks-ease-out)}.brycks-page-card:hover{border-color:var(--brycks-primary-default)}.brycks-page-card__content{cursor:pointer}.brycks-page-card__icon{width:48px;height:48px;border-radius:var(--brycks-radius-lg);background-color:var(--brycks-primary-muted);color:var(--brycks-primary-default);display:flex;align-items:center;justify-content:center;flex-shrink:0}.brycks-section-item{padding:var(--brycks-spacing-3);background-color:var(--brycks-background-elevated);border:1px solid var(--brycks-border-muted);border-radius:var(--brycks-radius-md);cursor:grab;transition:background-color var(--brycks-duration-fast) var(--brycks-ease-out),border-color var(--brycks-duration-fast) var(--brycks-ease-out),opacity var(--brycks-duration-fast) var(--brycks-ease-out)}.brycks-section-item:active{cursor:grabbing}.brycks-section-item--dragging{opacity:.5;background-color:var(--brycks-background-muted)}.brycks-section-item--drag-over{background-color:var(--brycks-primary-muted);border-color:var(--brycks-primary-default)}.brycks-section-item__grip{cursor:grab;color:var(--brycks-foreground-muted)}.brycks-section-item__icon{width:40px;height:40px;border-radius:var(--brycks-radius-md);background-color:var(--brycks-primary-muted);color:var(--brycks-primary-default);display:flex;align-items:center;justify-content:center;font-weight:700;font-size:var(--brycks-font-size-base);flex-shrink:0}.brycks-section-type-btn{padding:var(--brycks-spacing-4);background-color:var(--brycks-background-muted);border:1px solid var(--brycks-border-muted);border-radius:var(--brycks-radius-md);cursor:pointer;text-align:center;transition:background-color var(--brycks-duration-fast) var(--brycks-ease-out),border-color var(--brycks-duration-fast) var(--brycks-ease-out)}.brycks-section-type-btn:hover{background-color:var(--brycks-primary-muted);border-color:var(--brycks-primary-default)}.brycks-empty-zone{padding:var(--brycks-spacing-10);border:2px dashed var(--brycks-border-muted);border-radius:var(--brycks-radius-lg);display:flex;justify-content:center;align-items:center}.brycks-info-item{display:flex;flex-direction:column;gap:var(--brycks-spacing-1)}.brycks-image-container{width:100%;aspect-ratio:16/9;background-color:var(--brycks-background-muted);border-radius:var(--brycks-radius-lg);display:flex;align-items:center;justify-content:center;color:var(--brycks-foreground-muted)}.brycks-table-action-btn{padding:var(--brycks-spacing-1)!important}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.brycks-skip-link{position:absolute;top:-40px;left:0;background:var(--brycks-primary-default);color:var(--brycks-primary-foreground);padding:var(--brycks-spacing-2) var(--brycks-spacing-4);z-index:var(--brycks-z-tooltip);text-decoration:none;font-weight:500}.brycks-skip-link:focus{top:0}*:focus-visible{outline:2px solid var(--brycks-primary-default);outline-offset:2px}@media (prefers-reduced-motion: reduce){*,*:before,*:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important;scroll-behavior:auto!important}}@keyframes brycks-fade-in{0%{opacity:0}to{opacity:1}}@keyframes brycks-fade-out{0%{opacity:1}to{opacity:0}}@keyframes brycks-scale-in{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}@keyframes brycks-scale-out{0%{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(.95)}}@keyframes brycks-slide-up{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}@keyframes brycks-slide-down{0%{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:translateY(0)}}@keyframes brycks-slide-left{0%{opacity:0;transform:translate(8px)}to{opacity:1;transform:translate(0)}}@keyframes brycks-slide-right{0%{opacity:0;transform:translate(-8px)}to{opacity:1;transform:translate(0)}}@keyframes brycks-drawer-slide-left{0%{transform:translate(-100%)}to{transform:translate(0)}}@keyframes brycks-drawer-slide-right{0%{transform:translate(100%)}to{transform:translate(0)}}@keyframes brycks-drawer-slide-top{0%{transform:translateY(-100%)}to{transform:translateY(0)}}@keyframes brycks-drawer-slide-bottom{0%{transform:translateY(100%)}to{transform:translateY(0)}}@keyframes brycks-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes brycks-pulse{0%,to{opacity:1}50%{opacity:.5}}@keyframes brycks-shimmer{0%{background-position:-200% 0}to{background-position:200% 0}}@keyframes brycks-bounce{0%,to{transform:translateY(-25%);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:translateY(0);animation-timing-function:cubic-bezier(0,0,.2,1)}}@keyframes brycks-bounce-scale{0%,80%,to{transform:scale(0);opacity:.5}40%{transform:scale(1);opacity:1}}@keyframes brycks-pulse-scale{0%,to{transform:scale(.8);opacity:.5}50%{transform:scale(1);opacity:1}}@keyframes brycks-focus-ring-pulse{0%,to{box-shadow:0 0 0 3px #5578f459}50%{box-shadow:0 0 0 4px #5578f440}}@keyframes brycks-toast-slide-in-right{0%{opacity:0;transform:translate(100%)}to{opacity:1;transform:translate(0)}}@keyframes brycks-toast-slide-in-left{0%{opacity:0;transform:translate(-100%)}to{opacity:1;transform:translate(0)}}@keyframes brycks-toast-slide-in-top{0%{opacity:0;transform:translateY(-100%)}to{opacity:1;transform:translateY(0)}}@keyframes brycks-toast-slide-in-bottom{0%{opacity:0;transform:translateY(100%)}to{opacity:1;transform:translateY(0)}}@keyframes brycks-accordion-expand{0%{height:0;opacity:0}to{height:var(--brycks-accordion-content-height);opacity:1}}@keyframes brycks-accordion-collapse{0%{height:var(--brycks-accordion-content-height);opacity:1}to{height:0;opacity:0}}@keyframes brycks-progress-indeterminate{0%{transform:translate(-100%)}to{transform:translate(400%)}}.brycks-animate-fade-in{animation:brycks-fade-in var(--brycks-duration-normal) var(--brycks-ease-out)}.brycks-animate-scale-in{animation:brycks-scale-in var(--brycks-duration-normal) var(--brycks-ease-spring)}.brycks-animate-slide-up{animation:brycks-slide-up var(--brycks-duration-normal) var(--brycks-ease-out)}.brycks-animate-spin{animation:brycks-spin 1s linear infinite}.brycks-animate-pulse{animation:brycks-pulse 2s cubic-bezier(.4,0,.6,1) infinite}.brycks-animate-shimmer{animation:brycks-shimmer 1.5s ease-in-out infinite;background:linear-gradient(90deg,var(--brycks-background-muted) 0%,var(--brycks-background-subtle) 50%,var(--brycks-background-muted) 100%);background-size:200% 100%}@media (prefers-reduced-motion: reduce){*,*:before,*:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important;scroll-behavior:auto!important}.brycks-animate-spin{animation-duration:.5s!important}.brycks-animate-pulse,.brycks-animate-shimmer,.brycks-animate-bounce{animation:none!important}.brycks-skeleton{animation:none!important;background:var(--brycks-background-muted)!important}}:root{--brycks-anim-duration-instant: 50ms;--brycks-anim-duration-fast: .1s;--brycks-anim-duration-normal: .2s;--brycks-anim-duration-slow: .3s;--brycks-anim-duration-slower: .4s;--brycks-anim-ease-linear: linear;--brycks-anim-ease-out: cubic-bezier(0, 0, .2, 1);--brycks-anim-ease-in: cubic-bezier(.4, 0, 1, 1);--brycks-anim-ease-in-out: cubic-bezier(.4, 0, .2, 1);--brycks-anim-ease-spring: cubic-bezier(.34, 1.56, .64, 1);--brycks-anim-ease-bounce: cubic-bezier(.68, -.55, .265, 1.55)}
|
|
1
|
+
*,*:before,*:after{box-sizing:border-box}html{-moz-text-size-adjust:none;-webkit-text-size-adjust:none;text-size-adjust:none}*{margin:0;padding:0}html,body{height:100%}body{min-height:100vh;line-height:1.5;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-rendering:optimizeLegibility}img,picture,video,canvas,svg{display:block;max-width:100%}input,button,textarea,select{font:inherit;color:inherit}p,h1,h2,h3,h4,h5,h6{overflow-wrap:break-word}ul[role=list],ol[role=list]{list-style:none}html:focus-within{scroll-behavior:smooth}a:not([class]){text-decoration-skip-ink:auto;color:currentColor}h1,h2,h3,h4,h5,h6{text-wrap:balance}p{text-wrap:pretty}textarea:not([rows]){min-height:10em}:target{scroll-margin-block:5ex}@media (prefers-reduced-motion: reduce){html:focus-within{scroll-behavior:auto}*,*:before,*:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important;scroll-behavior:auto!important}}:focus-visible{outline:2px solid var(--brycks-border-focus, #5578f4);outline-offset:2px}:focus:not(:focus-visible){outline:none}button{background:none;border:none;cursor:pointer}a{text-decoration:none}table{border-collapse:collapse;border-spacing:0}[hidden]{display:none!important}[disabled]{cursor:not-allowed}:root{--brycks-font-sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;--brycks-font-mono: "SF Mono", "Fira Code", "Fira Mono", "Roboto Mono", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--brycks-font-scale-body: 1;--brycks-font-scale-headings: 1;--brycks-font-scale-ui: 1;--brycks-fs-base-xs: 11px;--brycks-fs-base-sm: 12px;--brycks-fs-base-base: 14px;--brycks-fs-base-md: 16px;--brycks-fs-base-lg: 18px;--brycks-fs-base-xl: 20px;--brycks-fs-base-2xl: 24px;--brycks-fs-base-3xl: 28px;--brycks-fs-base-4xl: 32px;--brycks-fs-base-5xl: 40px;--brycks-fs-base-6xl: 48px;--brycks-fs-base-7xl: 60px;--brycks-fs-base-8xl: 72px;--brycks-fs-base-9xl: 96px;--brycks-fs-body-xs: calc(var(--brycks-fs-base-xs) * var(--brycks-font-scale-body));--brycks-fs-body-sm: calc(var(--brycks-fs-base-sm) * var(--brycks-font-scale-body));--brycks-fs-body-base: calc(var(--brycks-fs-base-base) * var(--brycks-font-scale-body));--brycks-fs-body-md: calc(var(--brycks-fs-base-md) * var(--brycks-font-scale-body));--brycks-fs-body-lg: calc(var(--brycks-fs-base-lg) * var(--brycks-font-scale-body));--brycks-fs-h6: calc(var(--brycks-fs-base-xl) * var(--brycks-font-scale-headings));--brycks-fs-h5: calc(var(--brycks-fs-base-2xl) * var(--brycks-font-scale-headings));--brycks-fs-h4: calc(var(--brycks-fs-base-3xl) * var(--brycks-font-scale-headings));--brycks-fs-h3: calc(var(--brycks-fs-base-4xl) * var(--brycks-font-scale-headings));--brycks-fs-h2: calc(var(--brycks-fs-base-5xl) * var(--brycks-font-scale-headings));--brycks-fs-h1: calc(var(--brycks-fs-base-6xl) * var(--brycks-font-scale-headings));--brycks-fs-display-sm: calc(var(--brycks-fs-base-7xl) * var(--brycks-font-scale-headings));--brycks-fs-display-md: calc(var(--brycks-fs-base-8xl) * var(--brycks-font-scale-headings));--brycks-fs-display-lg: calc(var(--brycks-fs-base-9xl) * var(--brycks-font-scale-headings));--brycks-fs-ui-xs: calc(var(--brycks-fs-base-xs) * var(--brycks-font-scale-ui));--brycks-fs-ui-sm: calc(var(--brycks-fs-base-sm) * var(--brycks-font-scale-ui));--brycks-fs-ui-md: calc(var(--brycks-fs-base-base) * var(--brycks-font-scale-ui));--brycks-fs-ui-lg: calc(15px * var(--brycks-font-scale-ui));--brycks-fs-ui-xl: calc(var(--brycks-fs-base-md) * var(--brycks-font-scale-ui));--brycks-font-size-xs: var(--brycks-fs-body-sm);--brycks-font-size-sm: var(--brycks-fs-body-base);--brycks-font-size-md: var(--brycks-fs-body-md);--brycks-font-size-lg: var(--brycks-fs-body-lg);--brycks-font-size-xl: var(--brycks-fs-h6);--brycks-font-size-2xl: var(--brycks-fs-h5);--brycks-font-size-3xl: var(--brycks-fs-h4);--brycks-font-size-4xl: var(--brycks-fs-h3);--brycks-space-0: 0;--brycks-space-px: 1px;--brycks-space-0-5: 2px;--brycks-space-1: 4px;--brycks-space-1-5: 6px;--brycks-space-2: 8px;--brycks-space-2-5: 10px;--brycks-space-3: 12px;--brycks-space-3-5: 14px;--brycks-space-4: 16px;--brycks-space-5: 20px;--brycks-space-6: 24px;--brycks-space-7: 28px;--brycks-space-8: 32px;--brycks-space-9: 36px;--brycks-space-10: 40px;--brycks-space-12: 48px;--brycks-space-14: 56px;--brycks-space-16: 64px;--brycks-space-20: 80px;--brycks-space-24: 96px;--brycks-spacing-1: var(--brycks-space-1);--brycks-spacing-2: var(--brycks-space-2);--brycks-spacing-2-5: var(--brycks-space-2-5);--brycks-spacing-3: var(--brycks-space-3);--brycks-spacing-4: var(--brycks-space-4);--brycks-spacing-5: var(--brycks-space-5);--brycks-spacing-6: var(--brycks-space-6);--brycks-spacing-8: var(--brycks-space-8);--brycks-radius-none: 0;--brycks-radius-xs: 2px;--brycks-radius-sm: 4px;--brycks-radius-md: 6px;--brycks-radius-default: 8px;--brycks-radius-lg: 10px;--brycks-radius-xl: 12px;--brycks-radius-2xl: 16px;--brycks-radius-3xl: 20px;--brycks-radius-4xl: 24px;--brycks-radius-full: 9999px;--brycks-shadow-xs: 0 1px 2px 0 rgba(0, 0, 0, .03), 0 1px 3px 0 rgba(0, 0, 0, .06);--brycks-shadow-sm: 0 1px 3px 0 rgba(0, 0, 0, .04), 0 2px 6px 0 rgba(0, 0, 0, .06), 0 0 1px 0 rgba(0, 0, 0, .04);--brycks-shadow-md: 0 2px 4px -1px rgba(0, 0, 0, .04), 0 4px 8px -1px rgba(0, 0, 0, .06), 0 8px 16px -2px rgba(0, 0, 0, .06);--brycks-shadow-lg: 0 4px 6px -2px rgba(0, 0, 0, .03), 0 8px 16px -4px rgba(0, 0, 0, .08), 0 16px 32px -8px rgba(0, 0, 0, .08);--brycks-shadow-xl: 0 8px 16px -4px rgba(0, 0, 0, .04), 0 16px 32px -8px rgba(0, 0, 0, .08), 0 32px 64px -16px rgba(0, 0, 0, .12);--brycks-shadow-2xl: 0 12px 24px -6px rgba(0, 0, 0, .05), 0 24px 48px -12px rgba(0, 0, 0, .1), 0 48px 96px -24px rgba(0, 0, 0, .15);--brycks-z-base: 0;--brycks-z-docked: 10;--brycks-z-dropdown: 1000;--brycks-z-sticky: 1100;--brycks-z-banner: 1200;--brycks-z-overlay: 1300;--brycks-z-modal: 1400;--brycks-z-popover: 1500;--brycks-z-toast: 1700;--brycks-z-tooltip: 1800;--brycks-duration-fast: .1s;--brycks-duration-normal: .2s;--brycks-duration-slow: .4s;--brycks-ease-out: cubic-bezier(0, 0, .2, 1);--brycks-ease-in-out: cubic-bezier(.4, 0, .2, 1);--brycks-ease-spring: cubic-bezier(.34, 1.56, .64, 1)}:root,[data-theme=light]{--brycks-background-app: #FFFFFF;--brycks-background-subtle: #FAFAFA;--brycks-background-muted: #F5F5F7;--brycks-background-elevated: #FFFFFF;--brycks-background-overlay: rgba(0, 0, 0, .4);--brycks-background-inverse: #1D1D1F;--brycks-background-default: #FAFAFA;--brycks-foreground-default: #1D1D1F;--brycks-foreground-muted: #636366;--brycks-foreground-subtle: #8E8E93;--brycks-foreground-disabled: #AEAEB2;--brycks-foreground-inverse: #FFFFFF;--brycks-foreground-link: #3B5CE9;--brycks-border-default: #E8E8ED;--brycks-border-muted: #F5F5F7;--brycks-border-strong: #D2D2D7;--brycks-border-focus: #5578F4;--brycks-primary-default: #3B5CE9;--brycks-primary-hover: #2D47D6;--brycks-primary-active: #2A3CAD;--brycks-primary-muted: #EEF4FF;--brycks-primary-soft-hover: #E0EBFF;--brycks-primary-foreground: #FFFFFF;--brycks-accent-default: #F86545;--brycks-accent-hover: #E54A28;--brycks-accent-active: #C13A1D;--brycks-accent-muted: #FFF5F3;--brycks-accent-soft-hover: #FFE8E3;--brycks-accent-foreground: #FFFFFF;--brycks-success-default: #059669;--brycks-success-hover: #047857;--brycks-success-muted: #ECFDF5;--brycks-success-soft-hover: #D1FAE5;--brycks-success-foreground: #FFFFFF;--brycks-success-on-muted: #047857;--brycks-warning-default: #F59E0B;--brycks-warning-hover: #D97706;--brycks-warning-muted: #FFFBEB;--brycks-warning-soft-hover: #FEF3C7;--brycks-warning-foreground: #1D1D1F;--brycks-warning-on-muted: #92400E;--brycks-error-default: #DC2626;--brycks-error-hover: #B91C1C;--brycks-error-muted: #FEF2F2;--brycks-error-soft-hover: #FEE2E2;--brycks-error-foreground: #FFFFFF;--brycks-error-on-muted: #B91C1C;--brycks-info-default: #0891B2;--brycks-info-hover: #0E7490;--brycks-info-muted: #ECFEFF;--brycks-info-soft-hover: #CFFAFE;--brycks-info-foreground: #FFFFFF;--brycks-info-on-muted: #0E7490}[data-theme=dark]{--brycks-background-app: #000000;--brycks-background-subtle: #1D1D1F;--brycks-background-muted: #2C2C2E;--brycks-background-elevated: #1D1D1F;--brycks-background-overlay: rgba(0, 0, 0, .6);--brycks-background-inverse: #FFFFFF;--brycks-background-default: #1D1D1F;--brycks-foreground-default: #FFFFFF;--brycks-foreground-muted: #AEAEB2;--brycks-foreground-subtle: #8E8E93;--brycks-foreground-disabled: #636366;--brycks-foreground-inverse: #1D1D1F;--brycks-foreground-link: #7A9FFF;--brycks-border-default: #3A3A3C;--brycks-border-muted: #2C2C2E;--brycks-border-strong: #48484A;--brycks-border-focus: #7A9FFF;--brycks-primary-default: #5578F4;--brycks-primary-hover: #7A9FFF;--brycks-primary-active: #A4C1FF;--brycks-primary-muted: #1A2352;--brycks-primary-soft-hover: #283889;--brycks-primary-foreground: #FFFFFF;--brycks-accent-default: #FF8A71;--brycks-accent-hover: #FFB5A6;--brycks-accent-active: #FFD5CC;--brycks-accent-muted: #48140A;--brycks-accent-soft-hover: #842F1D;--brycks-accent-foreground: #000000;--brycks-success-default: #34D399;--brycks-success-hover: #6EE7B7;--brycks-success-muted: #022C22;--brycks-success-soft-hover: #064E3B;--brycks-success-foreground: #000000;--brycks-success-on-muted: #6EE7B7;--brycks-warning-default: #FBBF24;--brycks-warning-hover: #FCD34D;--brycks-warning-muted: #451A03;--brycks-warning-soft-hover: #78350F;--brycks-warning-foreground: #000000;--brycks-warning-on-muted: #FCD34D;--brycks-error-default: #F87171;--brycks-error-hover: #FCA5A5;--brycks-error-muted: #450A0A;--brycks-error-soft-hover: #7F1D1D;--brycks-error-foreground: #000000;--brycks-error-on-muted: #FCA5A5;--brycks-info-default: #22D3EE;--brycks-info-hover: #67E8F9;--brycks-info-muted: #083344;--brycks-info-soft-hover: #164E63;--brycks-info-foreground: #000000;--brycks-info-on-muted: #67E8F9}html{font-family:var(--brycks-font-sans);color-scheme:light dark}body{font-family:var(--brycks-font-sans);font-size:16px;line-height:1.5;color:var(--brycks-foreground-default);background-color:var(--brycks-background-app);transition:background-color var(--brycks-duration-normal) var(--brycks-ease-out),color var(--brycks-duration-normal) var(--brycks-ease-out)}::selection{background-color:var(--brycks-primary-muted);color:var(--brycks-foreground-default)}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background-color:var(--brycks-border-default);border-radius:var(--brycks-radius-full);border:2px solid transparent;background-clip:content-box}::-webkit-scrollbar-thumb:hover{background-color:var(--brycks-border-strong)}*{scrollbar-width:thin;scrollbar-color:var(--brycks-border-default) transparent}code,kbd,pre,samp{font-family:var(--brycks-font-mono)}.brycks-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.brycks-app-layout{display:flex;height:100vh;overflow:hidden;background-color:var(--brycks-background-subtle)}.brycks-app-layout__main{flex:1;display:flex;flex-direction:column;overflow:hidden}.brycks-app-layout__content{flex:1;overflow:auto;padding:var(--brycks-spacing-6)}.brycks-sidebar{width:240px;height:100vh;background-color:var(--brycks-background-elevated);border-right:1px solid var(--brycks-border-muted);display:flex;flex-direction:column;flex-shrink:0;will-change:width;contain:layout style}.brycks-sidebar--collapsed{width:72px}.brycks-sidebar__logo{padding:var(--brycks-spacing-4) var(--brycks-spacing-5);display:flex;align-items:center;gap:var(--brycks-spacing-3)}.brycks-sidebar--collapsed .brycks-sidebar__logo{padding:var(--brycks-spacing-4) var(--brycks-spacing-3);justify-content:center}.brycks-sidebar__logo-icon{width:32px;height:32px;background-color:var(--brycks-primary-default);border-radius:var(--brycks-radius-md);display:flex;align-items:center;justify-content:center;color:var(--brycks-primary-foreground);font-weight:700;font-size:var(--brycks-font-size-sm);flex-shrink:0}.brycks-sidebar__logo-text{font-size:var(--brycks-font-size-lg);font-weight:600;color:var(--brycks-foreground-default)}.brycks-sidebar__nav{flex:1;padding:var(--brycks-spacing-2) var(--brycks-spacing-3);overflow-y:auto}.brycks-sidebar--collapsed .brycks-sidebar__nav{padding:var(--brycks-spacing-2)}.brycks-sidebar__footer{padding:var(--brycks-spacing-3);text-align:center}.brycks-sidebar--collapsed .brycks-sidebar__footer{padding:var(--brycks-spacing-3) var(--brycks-spacing-2)}.brycks-nav-item{display:flex;align-items:center;gap:var(--brycks-spacing-3);padding:var(--brycks-spacing-2-5) var(--brycks-spacing-3);border-radius:var(--brycks-radius-md);cursor:pointer;background-color:transparent;color:var(--brycks-foreground-default);transition:background-color var(--brycks-duration-fast) var(--brycks-ease-out),color var(--brycks-duration-fast) var(--brycks-ease-out);border:none;width:100%;text-align:left;font-size:var(--brycks-font-size-sm)}.brycks-nav-item:hover{background-color:var(--brycks-background-muted)}.brycks-nav-item--active{background-color:var(--brycks-primary-muted);color:var(--brycks-primary-default)}.brycks-nav-item--active:hover{background-color:var(--brycks-primary-muted)}.brycks-sidebar--collapsed .brycks-nav-item{padding:var(--brycks-spacing-3);justify-content:center}.brycks-nav-item__icon{width:20px;height:20px;flex-shrink:0;color:var(--brycks-foreground-muted)}.brycks-nav-item--active .brycks-nav-item__icon{color:var(--brycks-primary-default)}.brycks-nav-item__label{font-weight:400}.brycks-nav-item--active .brycks-nav-item__label{font-weight:500}.brycks-header{height:64px;background-color:var(--brycks-background-elevated);border-bottom:1px solid var(--brycks-border-muted);display:flex;align-items:center;justify-content:space-between;padding:0 var(--brycks-spacing-5);flex-shrink:0}.brycks-header__left,.brycks-header__right{display:flex;align-items:center}.brycks-header__left{gap:var(--brycks-spacing-4)}.brycks-header__right{gap:var(--brycks-spacing-2)}.brycks-icon-btn{display:flex;align-items:center;justify-content:center;width:36px;height:36px;border-radius:var(--brycks-radius-md);background-color:transparent;color:var(--brycks-foreground-muted);cursor:pointer;border:none;transition:background-color var(--brycks-duration-fast) var(--brycks-ease-out),color var(--brycks-duration-fast) var(--brycks-ease-out)}.brycks-icon-btn:hover{background-color:var(--brycks-background-muted);color:var(--brycks-foreground-default)}.brycks-icon-btn:focus-visible{outline:2px solid var(--brycks-primary-default);outline-offset:2px}.brycks-avatar{width:36px;height:36px;border-radius:var(--brycks-radius-full);background-color:var(--brycks-primary-default);color:var(--brycks-primary-foreground);display:flex;align-items:center;justify-content:center;font-weight:600;font-size:var(--brycks-font-size-sm);cursor:pointer;transition:transform var(--brycks-duration-fast) var(--brycks-ease-out)}.brycks-avatar:hover{transform:scale(1.05)}.brycks-avatar--xs{width:24px;height:24px;font-size:10px}.brycks-avatar--sm{width:32px;height:32px;font-size:12px}.brycks-avatar--md{width:36px;height:36px;font-size:14px}.brycks-avatar--lg{width:40px;height:40px;font-size:16px}.brycks-avatar--xl{width:48px;height:48px;font-size:18px}.brycks-avatar--2xl{width:64px;height:64px;font-size:24px}.brycks-page-header{margin-bottom:var(--brycks-spacing-6)}.brycks-stat-card__icon{width:48px;height:48px;border-radius:var(--brycks-radius-lg);display:flex;align-items:center;justify-content:center}.brycks-stat-card__icon--default{background-color:color-mix(in srgb,var(--brycks-foreground-muted) 10%,transparent);color:var(--brycks-foreground-muted)}.brycks-stat-card__icon--primary{background-color:var(--brycks-primary-muted);color:var(--brycks-primary-default)}.brycks-stat-card__icon--success{background-color:var(--brycks-success-muted);color:var(--brycks-success-default)}.brycks-stat-card__icon--warning{background-color:var(--brycks-warning-muted);color:var(--brycks-warning-default)}.brycks-stat-card__icon--error{background-color:var(--brycks-error-muted);color:var(--brycks-error-default)}.brycks-stat-card__trend{display:inline-flex;align-items:center;gap:var(--brycks-spacing-1);font-size:var(--brycks-font-size-xs);font-weight:500}.brycks-stat-card__trend--positive{color:var(--brycks-success-default)}.brycks-stat-card__trend--negative{color:var(--brycks-error-default)}.brycks-quick-action{padding:var(--brycks-spacing-4);border-radius:var(--brycks-radius-lg);background-color:var(--brycks-background-muted);cursor:pointer;transition:background-color var(--brycks-duration-fast) var(--brycks-ease-out);text-align:center;border:none;width:100%}.brycks-quick-action:hover{background-color:var(--brycks-primary-muted)}.brycks-quick-action:focus-visible{outline:2px solid var(--brycks-primary-default);outline-offset:2px}.brycks-quick-action__icon{color:var(--brycks-foreground-muted);margin-bottom:var(--brycks-spacing-2)}.brycks-quick-action:hover .brycks-quick-action__icon{color:var(--brycks-primary-default)}.brycks-search-input{position:relative;min-width:240px}.brycks-search-input__icon{position:absolute;left:var(--brycks-spacing-3);top:50%;transform:translateY(-50%);pointer-events:none;z-index:1;color:var(--brycks-foreground-muted);width:16px;height:16px}.brycks-search-input input{padding-left:36px}.brycks-login-container{min-height:100vh;display:flex;align-items:center;justify-content:center;padding:var(--brycks-spacing-6);background-color:var(--brycks-background-subtle)}.brycks-login-card{width:100%;max-width:400px}.brycks-login-logo{display:flex;align-items:center;justify-content:center;margin-bottom:var(--brycks-spacing-8)}.brycks-login-logo__icon{width:64px;height:64px;background-color:var(--brycks-primary-default);border-radius:var(--brycks-radius-xl);display:flex;align-items:center;justify-content:center;color:var(--brycks-primary-foreground);font-weight:700;font-size:28px}.brycks-login-footer{margin-top:var(--brycks-spacing-6);text-align:center}.brycks-user-info{padding:var(--brycks-spacing-2) var(--brycks-spacing-3)}.brycks-link{color:var(--brycks-primary-default);text-decoration:none;font-size:var(--brycks-font-size-sm);cursor:pointer;transition:color var(--brycks-duration-fast) var(--brycks-ease-out)}.brycks-link:hover{color:var(--brycks-primary-hover);text-decoration:underline}.brycks-login-form__options{display:flex;justify-content:space-between;align-items:center}.brycks-dropdown-item__content{display:flex;align-items:center;gap:var(--brycks-spacing-2)}.brycks-vehicle-card{cursor:pointer;transition:transform var(--brycks-duration-fast) var(--brycks-ease-out),box-shadow var(--brycks-duration-fast) var(--brycks-ease-out)}.brycks-vehicle-card:hover{transform:translateY(-2px);box-shadow:var(--brycks-shadow-lg)}.brycks-vehicle-card__image{width:100%;aspect-ratio:16/10;background-color:var(--brycks-background-muted);border-radius:var(--brycks-radius-md);display:flex;align-items:center;justify-content:center;color:var(--brycks-foreground-muted);margin-bottom:var(--brycks-spacing-3)}.brycks-vehicle-card__price{color:var(--brycks-primary-default)}.brycks-filter-select{min-width:160px}.brycks-page-card{cursor:pointer;transition:border-color var(--brycks-duration-fast) var(--brycks-ease-out)}.brycks-page-card:hover{border-color:var(--brycks-primary-default)}.brycks-page-card__content{cursor:pointer}.brycks-page-card__icon{width:48px;height:48px;border-radius:var(--brycks-radius-lg);background-color:var(--brycks-primary-muted);color:var(--brycks-primary-default);display:flex;align-items:center;justify-content:center;flex-shrink:0}.brycks-section-item{padding:var(--brycks-spacing-3);background-color:var(--brycks-background-elevated);border:1px solid var(--brycks-border-muted);border-radius:var(--brycks-radius-md);cursor:grab;transition:background-color var(--brycks-duration-fast) var(--brycks-ease-out),border-color var(--brycks-duration-fast) var(--brycks-ease-out),opacity var(--brycks-duration-fast) var(--brycks-ease-out)}.brycks-section-item:active{cursor:grabbing}.brycks-section-item--dragging{opacity:.5;background-color:var(--brycks-background-muted)}.brycks-section-item--drag-over{background-color:var(--brycks-primary-muted);border-color:var(--brycks-primary-default)}.brycks-section-item__grip{cursor:grab;color:var(--brycks-foreground-muted)}.brycks-section-item__icon{width:40px;height:40px;border-radius:var(--brycks-radius-md);background-color:var(--brycks-primary-muted);color:var(--brycks-primary-default);display:flex;align-items:center;justify-content:center;font-weight:700;font-size:var(--brycks-font-size-base);flex-shrink:0}.brycks-section-type-btn{padding:var(--brycks-spacing-4);background-color:var(--brycks-background-muted);border:1px solid var(--brycks-border-muted);border-radius:var(--brycks-radius-md);cursor:pointer;text-align:center;transition:background-color var(--brycks-duration-fast) var(--brycks-ease-out),border-color var(--brycks-duration-fast) var(--brycks-ease-out)}.brycks-section-type-btn:hover{background-color:var(--brycks-primary-muted);border-color:var(--brycks-primary-default)}.brycks-empty-zone{padding:var(--brycks-spacing-10);border:2px dashed var(--brycks-border-muted);border-radius:var(--brycks-radius-lg);display:flex;justify-content:center;align-items:center}.brycks-info-item{display:flex;flex-direction:column;gap:var(--brycks-spacing-1)}.brycks-image-container{width:100%;aspect-ratio:16/9;background-color:var(--brycks-background-muted);border-radius:var(--brycks-radius-lg);display:flex;align-items:center;justify-content:center;color:var(--brycks-foreground-muted)}.brycks-table-action-btn{padding:var(--brycks-spacing-1)!important}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.brycks-skip-link{position:absolute;top:-40px;left:0;background:var(--brycks-primary-default);color:var(--brycks-primary-foreground);padding:var(--brycks-spacing-2) var(--brycks-spacing-4);z-index:var(--brycks-z-tooltip);text-decoration:none;font-weight:500}.brycks-skip-link:focus{top:0}*:focus-visible{outline:2px solid var(--brycks-primary-default);outline-offset:2px}@media (prefers-reduced-motion: reduce){*,*:before,*:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important;scroll-behavior:auto!important}}@keyframes brycks-fade-in{0%{opacity:0}to{opacity:1}}@keyframes brycks-fade-out{0%{opacity:1}to{opacity:0}}@keyframes brycks-scale-in{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}@keyframes brycks-scale-out{0%{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(.95)}}@keyframes brycks-slide-up{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}@keyframes brycks-slide-down{0%{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:translateY(0)}}@keyframes brycks-slide-left{0%{opacity:0;transform:translate(8px)}to{opacity:1;transform:translate(0)}}@keyframes brycks-slide-right{0%{opacity:0;transform:translate(-8px)}to{opacity:1;transform:translate(0)}}@keyframes brycks-drawer-slide-left{0%{transform:translate(-100%)}to{transform:translate(0)}}@keyframes brycks-drawer-slide-right{0%{transform:translate(100%)}to{transform:translate(0)}}@keyframes brycks-drawer-slide-top{0%{transform:translateY(-100%)}to{transform:translateY(0)}}@keyframes brycks-drawer-slide-bottom{0%{transform:translateY(100%)}to{transform:translateY(0)}}@keyframes brycks-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes brycks-pulse{0%,to{opacity:1}50%{opacity:.5}}@keyframes brycks-shimmer{0%{background-position:-200% 0}to{background-position:200% 0}}@keyframes brycks-bounce{0%,to{transform:translateY(-25%);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:translateY(0);animation-timing-function:cubic-bezier(0,0,.2,1)}}@keyframes brycks-bounce-scale{0%,80%,to{transform:scale(0);opacity:.5}40%{transform:scale(1);opacity:1}}@keyframes brycks-pulse-scale{0%,to{transform:scale(.8);opacity:.5}50%{transform:scale(1);opacity:1}}@keyframes brycks-focus-ring-pulse{0%,to{box-shadow:0 0 0 3px #5578f459}50%{box-shadow:0 0 0 4px #5578f440}}@keyframes brycks-toast-slide-in-right{0%{opacity:0;transform:translate(100%)}to{opacity:1;transform:translate(0)}}@keyframes brycks-toast-slide-in-left{0%{opacity:0;transform:translate(-100%)}to{opacity:1;transform:translate(0)}}@keyframes brycks-toast-slide-in-top{0%{opacity:0;transform:translateY(-100%)}to{opacity:1;transform:translateY(0)}}@keyframes brycks-toast-slide-in-bottom{0%{opacity:0;transform:translateY(100%)}to{opacity:1;transform:translateY(0)}}@keyframes brycks-accordion-expand{0%{height:0;opacity:0}to{height:var(--brycks-accordion-content-height);opacity:1}}@keyframes brycks-accordion-collapse{0%{height:var(--brycks-accordion-content-height);opacity:1}to{height:0;opacity:0}}@keyframes brycks-progress-indeterminate{0%{transform:translate(-100%)}to{transform:translate(400%)}}.brycks-animate-fade-in{animation:brycks-fade-in var(--brycks-duration-normal) var(--brycks-ease-out)}.brycks-animate-scale-in{animation:brycks-scale-in var(--brycks-duration-normal) var(--brycks-ease-spring)}.brycks-animate-slide-up{animation:brycks-slide-up var(--brycks-duration-normal) var(--brycks-ease-out)}.brycks-animate-spin{animation:brycks-spin 1s linear infinite}.brycks-animate-pulse{animation:brycks-pulse 2s cubic-bezier(.4,0,.6,1) infinite}.brycks-animate-shimmer{animation:brycks-shimmer 1.5s ease-in-out infinite;background:linear-gradient(90deg,var(--brycks-background-muted) 0%,var(--brycks-background-subtle) 50%,var(--brycks-background-muted) 100%);background-size:200% 100%}@media (prefers-reduced-motion: reduce){*,*:before,*:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important;scroll-behavior:auto!important}.brycks-animate-spin{animation-duration:.5s!important}.brycks-animate-pulse,.brycks-animate-shimmer,.brycks-animate-bounce{animation:none!important}.brycks-skeleton{animation:none!important;background:var(--brycks-background-muted)!important}}:root{--brycks-anim-duration-instant: 50ms;--brycks-anim-duration-fast: .1s;--brycks-anim-duration-normal: .2s;--brycks-anim-duration-slow: .3s;--brycks-anim-duration-slower: .4s;--brycks-anim-ease-linear: linear;--brycks-anim-ease-out: cubic-bezier(0, 0, .2, 1);--brycks-anim-ease-in: cubic-bezier(.4, 0, 1, 1);--brycks-anim-ease-in-out: cubic-bezier(.4, 0, .2, 1);--brycks-anim-ease-spring: cubic-bezier(.34, 1.56, .64, 1);--brycks-anim-ease-bounce: cubic-bezier(.68, -.55, .265, 1.55)}:root{--brycks-fs-responsive-xs: clamp(.625rem, 2.5vw, .75rem);--brycks-fs-responsive-sm: clamp(.6875rem, 2.8vw, .875rem);--brycks-fs-responsive-base: clamp(.75rem, 3vw, 1rem);--brycks-fs-responsive-lg: clamp(.875rem, 3.5vw, 1.125rem);--brycks-fs-responsive-xl: clamp(1rem, 4vw, 1.25rem);--brycks-fs-responsive-2xl: clamp(1.125rem, 4.5vw, 1.5rem);--brycks-spacing-responsive-xs: clamp(.25rem, 1vw, .5rem);--brycks-spacing-responsive-sm: clamp(.375rem, 1.5vw, .75rem);--brycks-spacing-responsive-md: clamp(.5rem, 2vw, 1rem);--brycks-spacing-responsive-lg: clamp(.75rem, 3vw, 1.5rem)}.brycks-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.brycks-truncate-2{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.brycks-truncate-3{display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}.brycks-break-all{word-break:break-all}.brycks-break-word{word-break:break-word;overflow-wrap:break-word}.brycks-min-w-0{min-width:0}@media (max-width: 479px){.brycks-hide-mobile{display:none!important}}@media (max-width: 319px){.brycks-hide-xs{display:none!important}}@media (min-width: 640px){.brycks-show-mobile-only{display:none!important}}@media (max-width: 479px){html{font-size:14px}}@media (max-width: 319px){html{font-size:12px}}@media (max-width: 639px){.brycks-app-layout__content{padding:var(--brycks-spacing-4)}}@media (max-width: 479px){.brycks-app-layout__content{padding:var(--brycks-spacing-3)}}@media (max-width: 319px){.brycks-app-layout__content{padding:var(--brycks-spacing-2)}}@media (max-width: 319px){.brycks-sidebar{width:100%;max-width:250px}.brycks-sidebar__logo{padding:var(--brycks-spacing-2) var(--brycks-spacing-3)}.brycks-sidebar__logo-icon{width:28px;height:28px;font-size:var(--brycks-fs-responsive-sm)}.brycks-sidebar__logo-text{font-size:var(--brycks-fs-responsive-base)}.brycks-sidebar__nav{padding:var(--brycks-spacing-1) var(--brycks-spacing-2)}.brycks-nav-item{padding:var(--brycks-spacing-2);font-size:var(--brycks-fs-responsive-sm);gap:var(--brycks-spacing-2)}.brycks-nav-item__icon{width:18px;height:18px}.brycks-nav-item__label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.brycks-sidebar__footer{padding:var(--brycks-spacing-2)}}@media (max-width: 639px){.brycks-header{height:56px;padding:0 var(--brycks-spacing-3)}.brycks-header__left{gap:var(--brycks-spacing-2)}.brycks-header__right{gap:var(--brycks-spacing-1)}}@media (max-width: 479px){.brycks-header{padding:0 var(--brycks-spacing-2)}.brycks-icon-btn{width:32px;height:32px}}@media (max-width: 319px){.brycks-header{height:48px;padding:0 var(--brycks-spacing-1)}.brycks-icon-btn{width:28px;height:28px}.brycks-icon-btn svg{width:16px;height:16px}}@media (max-width: 319px){.brycks-avatar--md{width:32px;height:32px;font-size:12px}.brycks-avatar--lg{width:36px;height:36px;font-size:14px}}@media (max-width: 639px){.brycks-page-header{margin-bottom:var(--brycks-spacing-4)}}@media (max-width: 319px){.brycks-page-header{margin-bottom:var(--brycks-spacing-3)}}@media (max-width: 639px){.brycks-stat-card__icon{width:40px;height:40px}.brycks-stat-card__icon svg{width:18px;height:18px}}@media (max-width: 479px){.brycks-stat-card__icon{width:36px;height:36px}.brycks-stat-card__icon svg{width:16px;height:16px}}@media (max-width: 319px){.brycks-stat-card__icon{width:28px;height:28px}.brycks-stat-card__icon svg{width:14px;height:14px}.brycks-stat-card__trend{font-size:.5625rem}}@media (max-width: 639px){.brycks-quick-action{padding:var(--brycks-spacing-3)}}@media (max-width: 319px){.brycks-quick-action{padding:var(--brycks-spacing-2)}.brycks-quick-action__icon{margin-bottom:var(--brycks-spacing-1)}.brycks-quick-action__icon svg{width:20px;height:20px}}@media (max-width: 639px){.brycks-search-input{min-width:100%}}@media (max-width: 319px){.brycks-search-input input{font-size:14px;padding-left:32px;padding-top:var(--brycks-spacing-1);padding-bottom:var(--brycks-spacing-1)}.brycks-search-input__icon{left:var(--brycks-spacing-2);width:14px;height:14px}}@media (max-width: 479px){.brycks-login-container{padding:var(--brycks-spacing-4)}.brycks-login-card{max-width:100%}.brycks-login-logo{margin-bottom:var(--brycks-spacing-6)}.brycks-login-logo__icon{width:56px;height:56px;font-size:24px}}@media (max-width: 319px){.brycks-login-container{padding:var(--brycks-spacing-2)}.brycks-login-logo{margin-bottom:var(--brycks-spacing-4)}.brycks-login-logo__icon{width:48px;height:48px;font-size:20px}.brycks-login-footer{margin-top:var(--brycks-spacing-4)}.brycks-login-form__options{flex-direction:column;gap:var(--brycks-spacing-2);align-items:flex-start}}@media (max-width: 479px){.brycks-user-info{padding:var(--brycks-spacing-2)}.brycks-user-info span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:200px;display:block}}@media (max-width: 639px){.brycks-vehicle-card__image{aspect-ratio:16/9}}@media (max-width: 319px){.brycks-vehicle-card__image{aspect-ratio:4/3;margin-bottom:var(--brycks-spacing-2)}}@media (max-width: 639px){.brycks-filter-select{min-width:100%}}@media (max-width: 479px){.brycks-page-card__icon{width:40px;height:40px}}@media (max-width: 319px){.brycks-page-card__icon{width:36px;height:36px}.brycks-page-card__icon svg{width:16px;height:16px}}@media (max-width: 479px){.brycks-section-item{padding:var(--brycks-spacing-2)}.brycks-section-item__icon{width:32px;height:32px;font-size:var(--brycks-fs-responsive-sm)}}@media (max-width: 319px){.brycks-section-item{padding:var(--brycks-spacing-1)}.brycks-section-item__icon{width:28px;height:28px}.brycks-section-item__grip{display:none}}@media (max-width: 479px){.brycks-section-type-btn{padding:var(--brycks-spacing-2)}}@media (max-width: 479px){.brycks-empty-zone{padding:var(--brycks-spacing-6)}}@media (max-width: 319px){.brycks-empty-zone{padding:var(--brycks-spacing-4)}}@media (max-width: 639px){.brycks-image-container{aspect-ratio:16/10}}@media (max-width: 319px){.brycks-image-container{aspect-ratio:4/3}}@media (max-width: 639px){input[type=text],input[type=email],input[type=password],input[type=tel],input[type=number],input[type=search],input[type=url],textarea,select{font-size:16px!important}}@media (max-width: 319px){input,textarea,select{padding:var(--brycks-spacing-1) var(--brycks-spacing-2)}}@media (max-width: 479px){.brycks-button--md{padding:var(--brycks-spacing-2) var(--brycks-spacing-3);font-size:var(--brycks-fs-responsive-sm)}.brycks-button--sm{padding:var(--brycks-spacing-1) var(--brycks-spacing-2);font-size:var(--brycks-fs-responsive-xs)}}@media (max-width: 319px){.brycks-button--md{padding:var(--brycks-spacing-1) var(--brycks-spacing-2)}.brycks-button--sm{padding:var(--brycks-spacing-1)}.brycks-button svg{width:14px;height:14px}}@media (max-width: 479px){.brycks-badge--md{font-size:var(--brycks-fs-responsive-xs);padding:2px 6px}.brycks-badge--sm{font-size:.5625rem;padding:1px 4px}}@media (max-width: 639px){.brycks-modal{width:calc(100vw - var(--brycks-spacing-4));max-height:calc(100vh - var(--brycks-spacing-8));margin:var(--brycks-spacing-2)}.brycks-modal-header,.brycks-modal-body,.brycks-modal-footer{padding:var(--brycks-spacing-3)}.brycks-modal-footer{flex-wrap:wrap;gap:var(--brycks-spacing-2)}}@media (max-width: 479px){.brycks-modal-header,.brycks-modal-body,.brycks-modal-footer{padding:var(--brycks-spacing-2)}.brycks-modal-footer button{flex:1;min-width:80px}}@media (max-width: 319px){.brycks-modal{width:calc(100vw - var(--brycks-spacing-2));margin:var(--brycks-spacing-1)}.brycks-modal-footer button{min-width:60px}}@media (max-width: 639px){.brycks-card{border-radius:var(--brycks-radius-md)}.brycks-card-header,.brycks-card-body{padding:var(--brycks-spacing-3)}}@media (max-width: 479px){.brycks-card-header,.brycks-card-body{padding:var(--brycks-spacing-2)}}@media (max-width: 479px){.brycks-dropdown-content{max-width:calc(100vw - var(--brycks-spacing-4))}.brycks-dropdown-item{padding:var(--brycks-spacing-2);font-size:var(--brycks-fs-responsive-sm)}}@media (max-width: 319px){.brycks-dropdown-item{padding:var(--brycks-spacing-1) var(--brycks-spacing-2);font-size:var(--brycks-fs-responsive-xs)}.brycks-dropdown-item__content{gap:var(--brycks-spacing-1)}}@media (max-width: 479px){.brycks-tooltip{max-width:calc(100vw - var(--brycks-spacing-4));font-size:var(--brycks-fs-responsive-xs);padding:var(--brycks-spacing-1) var(--brycks-spacing-2)}}@media (max-width: 639px){.brycks-tabs .brycks-tab-list{overflow-x:auto;-webkit-overflow-scrolling:touch;scrollbar-width:none;-ms-overflow-style:none}.brycks-tabs .brycks-tab-list::-webkit-scrollbar{display:none}.brycks-tabs .brycks-tab{flex:0 0 auto;white-space:nowrap}}@media (max-width: 319px){.brycks-tabs .brycks-tab{padding:var(--brycks-spacing-1) var(--brycks-spacing-2);font-size:var(--brycks-fs-responsive-xs)}.brycks-tabs .brycks-tab svg{width:14px;height:14px}}.brycks-table-container{width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}@media (max-width: 639px){.brycks-table th,.brycks-table td{padding:var(--brycks-spacing-2);font-size:var(--brycks-fs-responsive-sm)}}@media (max-width: 319px){.brycks-table th,.brycks-table td{padding:var(--brycks-spacing-1);font-size:var(--brycks-fs-responsive-xs)}}@media (max-width: 479px){.brycks-pagination{gap:var(--brycks-spacing-1)}.brycks-pagination button{min-width:32px;height:32px;padding:var(--brycks-spacing-1);font-size:var(--brycks-fs-responsive-sm)}}@media (max-width: 319px){.brycks-pagination button{min-width:28px;height:28px;font-size:var(--brycks-fs-responsive-xs)}.brycks-pagination button span{display:none}}@media (max-width: 479px){.brycks-alert{padding:var(--brycks-spacing-2) var(--brycks-spacing-3);font-size:var(--brycks-fs-responsive-sm)}}@media (max-width: 319px){.brycks-alert{padding:var(--brycks-spacing-2);font-size:var(--brycks-fs-responsive-xs)}.brycks-alert__icon{width:16px;height:16px}}@media (max-width: 479px){.brycks-toast-container{left:var(--brycks-spacing-2);right:var(--brycks-spacing-2);bottom:var(--brycks-spacing-2)}.brycks-toast{max-width:100%;font-size:var(--brycks-fs-responsive-sm)}}.brycks-flex>*,.brycks-stack>*{min-width:0}@media print{.brycks-sidebar,.brycks-header,.brycks-skip-link{display:none!important}.brycks-app-layout__content{padding:0!important}.brycks-button,.brycks-icon-btn{display:none!important}}
|