fermmap-shared 0.2.5 → 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/main.cjs +56 -1
- package/dist/main.cjs.map +1 -1
- package/dist/main.css +78 -58
- package/dist/main.css.map +1 -1
- package/dist/module.css +78 -58
- package/dist/module.css.map +1 -1
- package/dist/module.mjs +58 -3
- package/dist/module.mjs.map +1 -1
- package/package.json +1 -1
package/dist/module.css.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":"ACMsB;EAAA;;;;EAAA;;;;EAUF;;;;EAAA;;;;EAAA;;;;EAiBE;;;;EAAA;;;;EAcI;;;;ECjCL;;;;;;EAAA;;;;EAAA;;;;EAAA;;;;;EAAA;;;;;EAAA;;;;EAAA;;;;;EAAA;;;;EAAA;;;;EC2CI;;;;EAsDE;;;;;EAeJ;;;;EAAA;;;;ECxHC;;;;EAAA;;;;EAOK;;;;ECkCT;;;;EA6BF;;;;;EAkBC;;;;;EAAA;;;;EAiBQ;;;;EAQF;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAaG;;;;ECxHL;;;;EAiBL;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAuCC;;;;EAAA;;;;EClDC;;;;EAcY;;;;EAAA;;;;ECFL;;;;EAAA;;;;EAAA;;;;ECrBP;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;ECoCO;;;;EAAA;;;;ECxCK;;;;EAkBZ;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EA6BC;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAqBC;;;;EA6CI;;;;EAKN;;;;EC3Gc;;;;EAAA;;;;EAWF;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EA8BF;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;ECtCH;;;;EC0BL;;;;EClBE;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;ECRJ;;;;EAAA;;;;EAAA;;;;EAaA;;;;EAqBM;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAsBE;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAWR;;;;EAAA;;;;EC5DM;;;;EAAA;;;;EAiBN;;;;EAeM;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;ECpCC;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAgCM;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;;;EC7BZ;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;;EAAA;;;;EAAA;;;;EAAA;;;;EA6BC;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAaA;;;;EAAA;;;;EAKI;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EC/DP;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAcK;;;;EAMA;;;;EAAA;;;;EAAA;;;;EAKD;;;;EAAA;;;;EAAA;;;;EAAA;;;;EC5BO;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAiBJ;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAuBA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EC1CP;;;;EAAA;;;;EAAA;;;;EAMG;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAYJ;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAmCK;;;;EAAA;;;;EAAA;;;;EC5CA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;;AtBGH;EeGA;;;;EAAA;;;;EAAA;;;;EIUI;;;;EAAA;;;;ECvBM;;;;ECgBZ;;;;EAAA;;;;EAAA;;;;ECTK;;;;EAAA;;;;;AtBGH;EmBOI;;;;EAAA;;;;EAAA;;;;EAMA;;;;EAAA;;;;EAAA;;;;ECvBM;;;;ECgBZ;;;;;ArBNE;EAAA;IAAA;;;;;;AAAA;EAAA;IAAA;;;;;;AAiBE;EmBVE;;;;EAAA;;;;EAAA;;;;EAMA;;;;EAAA;;;;EAAA;;;;EAKD;;;;;AnBDD;EmBJE;;;;EAAA;;;;EAAA;;;;EAKD;;;;;AnBDD;EmBJE;;;;EAAA;;;;EAAA;;;;EAKD;;;;;AnBDD;EAAA;;;;;AAAA;EAAA;;;;;AAAA;EAAA;;;;;ACnBD;EAAA;IAAA;;;;IgBwCY;;;;IAAA;;;;IG/BP;;;;IAAA;;;;IAAA;;;;IAAA;;;;IAAA;;;;IAAA;;;;IAuBA;;;;;;AG5C1B;;;;;;;;;;;AAWA;;;;;;AAMA;;;;AClBA;;;;AAIA;;;;AAIA;;;;;;;;;;ACHA;;;;AAIA;;;;AAIA;;;;;;;;;;;;AAYA;;;;;;;;;;;;AT6FwF;;AAAA;EAAA;IAAA","sources":["27d1e9fc0b161a21","packages/shared/src/components/AlertDialog.tsx","packages/shared/src/components/Button.tsx","packages/shared/src/components/Autocomplete.tsx","packages/shared/src/components/AutocompleteTable.tsx","packages/shared/src/components/Table.tsx","packages/shared/src/components/Checkbox.tsx","packages/shared/src/components/ProgressBar.tsx","packages/shared/src/components/AvatarButton.tsx","packages/shared/src/components/Badge.tsx","packages/shared/src/components/CloseButton.tsx","packages/shared/src/components/ComboBox.tsx","packages/shared/src/components/DisclosureGroup.tsx","packages/shared/src/components/LabeledValue.tsx","packages/shared/src/components/Menu.tsx","packages/shared/src/components/PasswordStrength.tsx","packages/shared/src/components/RadioGroup.tsx","packages/shared/src/components/SearchField.tsx","packages/shared/src/components/SegmentedControl.tsx","packages/shared/src/components/Select.tsx","packages/shared/src/components/StatCard.tsx","packages/shared/src/components/Switch.tsx","packages/shared/src/components/Tabs.tsx","packages/shared/src/components/TextArea.tsx","packages/shared/src/components/Checkbox.css","packages/shared/src/components/ProgressBar.css","packages/shared/src/components/DisclosureGroup.css"],"sourcesContent":["@import \"9f47f68ba38c295a\";\n@import \"78c590a309b0e6a4\";\n@import \"290522a232eb340f\";\n@import \"9d168f2695c440b5\";\n@import \"cc4d3a5fac51363d\";\n@import \"8ee7d6e801b40a1c\";\n@import \"6ffa14505a299e4e\";\n@import \"cfef988437953ae9\";\n@import \"93c21ad3845ca137\";\n@import \"83429286ceb3be13\";\n@import \"03dd1cfb5962a09d\";\n@import \"620de91e79ad0a16\";\n@import \"d274ac5a14987bf9\";\n@import \"416838a95541a7c4\";\n@import \"53f941c4c340fa03\";\n@import \"82593e9a11ceb790\";\n@import \"4312b6d61177a486\";\n@import \"7f7804fa3c921faf\";\n@import \"29e82be3069c1c2c\";\n@import \"26b1a04880d0efb1\";\n@import \"f315d14c1596594f\";\n@import \"245a17a9caf6065d\";\n@import \"03b25ce5fcd826d5\";\n@import \"f64b1199c5ac10c2\";\n@import \"d00995e4bf421650\";\n@import \"418c33e40e8fa307\";\n@import \"77a381a4ad5e4deb\";\n@import \"79a47b3e11bde76b\";\n@import \"4c61b37e83b6cbaf\";\n@import \"7d6c90f6382f216d\";\n@import \"d5a82419c43c0258\";\n@import \"4c5c6d90d8125894\";\n@import \"d7830e0fdb5f5a4f\";\n@import \"3cb9bcfb6f0038f8\";\n@import \"8d4f769c29e4ee31\";\n@import \"e8338bb3ae3fb1c4\";\n@import \"541891615c093f21\";\n@import \"3415552572bd8d79\";\n@import \"30bf95091f7ade51\";\n@import \"f03216825356c24c\";\n@import \"a035a1ff61c82329\";\n@import \"f0b45c7a5a4ee6b4\";\n@import \"00d20b13f70db88f\";\n@import \"99348462226d32b0\";\n@import \"f133ffb8107f1499\";\n@import \"060090694d7abb5e\";\n@import \"02d1fda45f088142\";\n@import \"0be6be6f1b4df772\";\n@import \"17a5281dc3bf68c3\";\n@import \"5cbf8c2ecc386b8a\";\n@import \"d000b87b862d640a\";\n@import \"99cf12a0bc5155d4\";\n@import \"3dea62b5608bc4c7\";\n@import \"00dffe07cf30c480\";\n@import \"daa2dcfcc6aba2ec\";\n@import \"22d59f7061eee5c3\";\n@import \"c730c2c683575e83\";\n@import \"4c4abb4dd24ed78d\";\n@import \"55d42732ed05bd7c\";\n@import \"7e7d0afbf7be7aaf\";\n@import \"fe26295011b80084\";\n@import \"98f373d7d990b86e\";\n@import \"44556bb5ffa970fa\";\n@import \"ffb67edd6d59f4d0\";\n@import \"3f8a3d39101fb8de\";\n@import \"da5d3570a6be5108\";\n@import \"a81b95f6ba098d94\";\n@import \"6e45401afedcc213\";\n@import \"823e68b0820e33b0\";\n@import \"6098b6422a6b5107\";\n@import \"e704ae4b0ea5b6a4\";\n@import \"18ca83a63b402953\";\n@import \"9934fabeca5dc14b\";\n@import \"08e05744d70ceb2e\";\n@import \"51f566c4383bc873\";\n@import \"912ea4a8b470ca7f\";\n@import \"6240e539776dd470\";\n@import \"4728afee3b53ed4d\";\n@import \"0239ad58c0477121\";\n@import \"be422f30337666a7\";\n@import \"c390e1b28b674d46\";\n@import \"00f37031e59ae452\";\n@import \"b2e8234656a0acab\";\n@import \"1c127e7148e69ee6\";\n@import \"31b2213af713491b\";\n@import \"c313bd17239b6dfb\";\n@import \"cc3cacc59937fd97\";\n@import \"1b5034e53b6b6e9d\";\n@import \"f43b41886b283acc\";\n@import \"b30e558e03268eb5\";\n@import \"16fa3c2822ce8fa6\";\n@import \"6cc8a6d86b48a0f8\";\n@import \"2d5249b468c9ed81\";\n@import \"d69789ae2771f37d\";\n@import \"a750f611c47db198\";\n@import \"2243763201ee6943\";\n@import \"2c30d3e83234d14f\";\n@import \"e9c2f06a78f0fa1b\";\n@import \"1629b57b41de7f33\";\n@import \"d38717759287a5cb\";\n@import \"5046bf9ea1029bc4\";\n@import \"a33499d27b9fcd76\";\n@import \"a31990ea1642f273\";\n@import \"d1b031cf4bda7480\";\n@import \"e81d7eb3988d206b\";\n@import \"6f65c616b892c898\";\n@import \"adda47912e5baae7\";\n@import \"d9ef614c1a51f3a0\";\n@import \"36dbb656eeb11098\";\n@import \"d3b3b5097317263d\";\n@import \"c7bfffb20d48ffa6\";\n@import \"75d222754bbb0b8f\";\n@import \"b79287f289978582\";\n@import \"55cf679aaecacd02\";\n@import \"a4a1c07b3ebdf826\";\n@import \"5a19908ab4aad173\";\n","import { Button } from './Button';\nimport { Dialog, Heading, Modal, ModalOverlay } from 'react-aria-components';\nimport { style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { TextField } from './TextField';\nimport { useState } from 'react';\n\nconst overlayStyles = style({\n position: 'fixed',\n inset: 0,\n backgroundColor: 'transparent-black-500',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n isolation: 'isolate',\n});\n\nconst modalStyles = style({\n backgroundColor: 'layer-1',\n borderRadius: 'xl',\n boxShadow: 'elevated',\n padding: 24,\n width: {\n default: 'calc(100vw - 32px)',\n sm: 400,\n md: 500\n },\n maxWidth: 500,\n transform: {\n entering: 'scale(0.95)',\n default: 'scale(1)'\n },\n});\n\nconst headingStyles = style({\n font: 'heading-xl',\n color: 'heading',\n margin: 0,\n marginBottom: 12\n});\n\nconst messageStyles = style({\n font: 'body',\n color: 'neutral',\n margin: 0,\n marginBottom: 24\n});\n\nconst buttonGroupStyles = style({\n display: 'flex',\n gap: 12,\n justifyContent: 'end'\n});\n\nexport interface AlertDialogProps {\n isOpen: boolean;\n onClose: () => void;\n title: string;\n message: string;\n variant?: 'danger' | 'warning' | 'info' | 'success';\n confirmLabel?: string;\n cancelLabel?: string;\n onConfirm: () => void | Promise<void>;\n showCancel?: boolean;\n children?: React.ReactNode; // Optional custom content (e.g., Switch, Checkbox)\n}\n\nexport function AlertDialog({\n isOpen,\n onClose,\n title,\n message,\n variant = 'info',\n confirmLabel = 'OK',\n cancelLabel = 'Cancel',\n onConfirm,\n showCancel = true,\n children\n}: AlertDialogProps) {\n const [isProcessing, setIsProcessing] = useState(false);\n\n const handleConfirm = async () => {\n setIsProcessing(true);\n try {\n await onConfirm();\n onClose();\n } finally {\n setIsProcessing(false);\n }\n };\n\n // Map variant to button variant\n const buttonVariant = variant === 'danger' ? 'danger' :\n variant === 'success' ? 'primary' :\n 'primary';\n\n return (\n <ModalOverlay isOpen={isOpen} onOpenChange={(open) => !open && onClose()} className={overlayStyles}>\n <Modal className={modalStyles}>\n <Dialog>\n {({ close }) => (\n <>\n <Heading slot=\"title\" className={headingStyles}>\n {title}\n </Heading>\n <p className={messageStyles}>{message}</p>\n {children && (\n <div className={style({ marginBottom: 24 })}>\n {children}\n </div>\n )}\n <div className={buttonGroupStyles}>\n {showCancel && (\n <Button\n variant=\"secondary\"\n size=\"M\"\n onPress={close}\n isDisabled={isProcessing}\n >\n {cancelLabel}\n </Button>\n )}\n <Button\n variant={buttonVariant}\n size=\"M\"\n onPress={handleConfirm}\n isDisabled={isProcessing}\n >\n {isProcessing ? 'Processing...' : confirmLabel}\n </Button>\n </div>\n </>\n )}\n </Dialog>\n </Modal>\n </ModalOverlay>\n );\n}\n\nexport interface PromptDialogProps {\n isOpen: boolean;\n onClose: () => void;\n title: string;\n message: string;\n inputLabel: string;\n inputPlaceholder?: string;\n confirmLabel?: string;\n cancelLabel?: string;\n onConfirm: (value: string) => void | Promise<void>;\n validator?: (value: string) => string | null;\n}\n\nexport function PromptDialog({\n isOpen,\n onClose,\n title,\n message,\n inputLabel,\n inputPlaceholder = '',\n confirmLabel = 'OK',\n cancelLabel = 'Cancel',\n onConfirm,\n validator\n}: PromptDialogProps) {\n const [value, setValue] = useState('');\n const [error, setError] = useState<string | null>(null);\n const [isProcessing, setIsProcessing] = useState(false);\n\n const handleConfirm = async () => {\n // Validate\n if (validator) {\n const validationError = validator(value);\n if (validationError) {\n setError(validationError);\n return;\n }\n }\n\n setIsProcessing(true);\n try {\n await onConfirm(value);\n setValue('');\n setError(null);\n onClose();\n } finally {\n setIsProcessing(false);\n }\n };\n\n const handleClose = () => {\n setValue('');\n setError(null);\n onClose();\n };\n\n return (\n <ModalOverlay isOpen={isOpen} onOpenChange={(open) => !open && handleClose()} className={overlayStyles}>\n <Modal className={modalStyles}>\n <Dialog>\n {({ close }) => (\n <>\n <Heading slot=\"title\" className={headingStyles}>\n {title}\n </Heading>\n <p className={messageStyles}>{message}</p>\n <div className={style({ marginBottom: 24 })}>\n <TextField\n label={inputLabel}\n value={value}\n onChange={(newValue) => {\n setValue(newValue);\n setError(null);\n }}\n placeholder={inputPlaceholder}\n error={error || undefined}\n autoFocus\n />\n </div>\n <div className={buttonGroupStyles}>\n <Button\n variant=\"secondary\"\n size=\"M\"\n onPress={() => {\n setValue('');\n setError(null);\n close();\n }}\n isDisabled={isProcessing}\n >\n {cancelLabel}\n </Button>\n <Button\n variant=\"primary\"\n size=\"M\"\n onPress={handleConfirm}\n isDisabled={isProcessing}\n >\n {isProcessing ? 'Processing...' : confirmLabel}\n </Button>\n </div>\n </>\n )}\n </Dialog>\n </Modal>\n </ModalOverlay>\n );\n}\n","import { Button as AriaButton, ButtonProps as AriaButtonProps } from 'react-aria-components';\nimport { focusRing, style } from '@react-spectrum/s2/style' with { type: 'macro' };\n\n// For props: https://react-spectrum.adobe.com/react-aria/Button.html#props\n// For styling: https://react-spectrum.adobe.com/react-aria/Button.html#styling\n// For conditional styling: https://react-spectrum.adobe.com/beta/s2/styling.html#conditional-styles\n\nexport interface ButtonProps extends AriaButtonProps {\n children?: React.ReactNode;\n variant?: 'primary' | 'secondary' | 'danger' | 'navigation';\n size?: 'S' | 'M' | 'L';\n}\n\n// Primary variant styles\nconst buttonStyles = style({\n ...focusRing(),\n font: {\n default: 'body',\n size: {\n S: 'body-sm',\n L: 'body-lg',// or `body` too?\n },\n },\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: {\n default: 8,\n size: {\n S: 4,\n },\n },\n paddingY: {\n default: 12,\n size: {\n S: 8,\n L: 16,\n },\n variant: {\n navigation: {\n default: 4\n }\n }\n },\n paddingStart: {\n default: 16,\n size: {\n S: 12,\n L: 24,\n },\n variant: {\n navigation: {\n default: 8\n }\n }\n },\n paddingEnd: {\n default: 16,\n size: {\n S: 12,\n L: 24,\n },\n variant: {\n navigation: 12\n }\n },\n width: {\n variant: {\n navigation: 'fit'\n }\n },\n color: {\n variant: {\n secondary: {\n default: '--plum-700',\n },\n danger: {\n default: 'white',\n },\n navigation: {\n default: '--plum-700',\n }\n },\n forcedColors: 'ButtonText',\n isDisabled: {\n default: 'gray-400',\n forcedColors: 'GrayText',\n },\n },\n backgroundColor: {\n default: '--plum-300',\n variant: {\n primary: {\n default: '--plum-300',\n isHovered: '--plum-400',\n isFocusVisibile: '--plum-400',\n isPressed: '--plum-400',\n },\n secondary: {\n default: '--yogurt-200',\n isHovered: '--yogurt-300',\n isFocusVisibile: '--yogurt-300',\n isPressed: '--yogurt-400',\n },\n navigation: {\n default: 'transparent',\n isHovered: '--yogurt-200',\n isFocusVisibile: '--yogurt-200',\n isPressed: '--yogurt-300',\n },\n danger: {\n default: '--berry-500',\n isHovered: '--berry-600',\n isFocusVisibile: '--berry-600',\n isPressed: '--berry-700',\n },\n },\n forcedColors: 'ButtonFace',\n isDisabled: {\n default: 'gray-100',\n isDisabled: 'GrayText',\n },\n },\n borderRadius: 'lg',\n borderStyle: {\n default: 'none',\n forcedColors: 'solid'\n },\n borderColor: {\n forcedColors: 'ButtonBorder'\n },\n borderWidth: {\n forcedColors: 1\n },\n cursor: {\n default: 'pointer',\n isDisabled: 'not-allowed'\n }\n});\n\nexport const Button = ({\n children,\n variant = 'primary',\n size = 'M',\n className,\n ...props\n}: ButtonProps) => {\n return (\n <AriaButton\n {...props}\n className={renderProps => `${className || ''} ` + buttonStyles({ ...renderProps, size, variant })}\n >\n {children}\n </AriaButton>\n );\n};\n","import { useState, useMemo } from 'react';\nimport {\n SearchField as AriaSearchField,\n Autocomplete as AriaAutocomplete,\n Input,\n Label,\n ListBox,\n ListBoxItem,\n useFilter,\n Popover\n} from 'react-aria-components';\nimport { focusRing, style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport Search from '@react-spectrum/s2/icons/Search';\nimport CheckmarkCircle from '@react-spectrum/s2/icons/CheckmarkCircle';\nimport type { Selection } from 'react-aria-components';\n\nconst containerStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 4,\n width: 'full',\n});\n\nconst labelStyles = style({\n font: 'body',\n color: '--plum-700',\n});\n\nconst searchFieldStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 4,\n});\n\nconst inputContainerStyles = style({\n position: 'relative',\n display: 'flex',\n alignItems: 'center',\n});\n\nconst inputStyles = style({\n ...focusRing(),\n width: 'full',\n padding: 8,\n paddingStart: 40,\n font: 'body',\n borderRadius: 'lg',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--yogurt-400',\n backgroundColor: {\n default: '--yogurt-100',\n isHovered: '--yogurt-200',\n isFocusVisible: '--yogurt-200',\n },\n});\n\nconst searchIconStyles = style({\n position: 'absolute',\n left: 12,\n top: '50%',\n transform: 'translateY(-50%)',\n pointerEvents: 'none',\n color: '--yogurt-600',\n});\n\nconst popoverStyles = style({\n backgroundColor: '--yogurt-50',\n borderRadius: 'lg',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--yogurt-400',\n boxShadow: 'elevated',\n marginTop: 4,\n maxHeight: 300,\n overflow: 'auto',\n isolation: 'isolate',\n // width: 'full',\n // minWidth: 300,\n});\n\nconst listBoxStyles = style({\n padding: 4,\n outline: 'none',\n});\n\nconst listBoxItemStyles = style({\n ...focusRing(),\n font: 'body',\n paddingX: 8,\n paddingY: 8,\n borderRadius: 'sm',\n cursor: 'pointer',\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n backgroundColor: {\n default: 'transparent',\n isHovered: '--yogurt-200',\n isFocused: '--yogurt-200',\n isPressed: '--yogurt-300',\n },\n color: {\n default: '--plum-700',\n isHovered: '--plum-800',\n isFocused: '--plum-800',\n isPressed: '--plum-900',\n },\n outline: 'none',\n});\n\nconst selectedItemStyles = style({\n backgroundColor: {\n default: '--plum-100',\n isHovered: '--plum-200',\n isFocused: '--plum-200',\n },\n color: '--plum-900',\n});\n\nconst checkboxStyles = style({\n width: 16,\n height: 16,\n flexShrink: 0,\n});\n\nconst itemTextStyles = style({\n flex: 1,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n});\n\nconst errorStyles = style({\n font: 'body-sm',\n color: '--berry-600',\n});\n\nexport interface AutocompleteItem {\n id: string | number;\n name: string;\n [key: string]: any;\n}\n\nexport interface AutocompleteProps<T extends AutocompleteItem> {\n items: T[];\n selectedKeys?: Selection;\n onSelectionChange?: (keys: Selection) => void;\n selectionMode?: 'none' | 'single' | 'multiple';\n label: string;\n placeholder?: string;\n ariaLabel?: string;\n error?: string;\n getItemText?: (item: T) => string;\n}\n\n/**\n * Autocomplete component using React Aria SearchField + ListBox\n * \n * Features:\n * - SearchField for filtering items\n * - Optional checkboxes for multi-selection\n * - Shows both selected and unselected items when filtered\n * - Selected items highlighted with background color\n * - Keyboard navigation (arrow keys, enter, escape)\n * - Accessible (ARIA labels, focus management)\n * \n * @example\n * ```tsx\n * // Single select\n * <Autocomplete\n * items={sources}\n * selectedKeys={selectedKey}\n * onSelectionChange={setSelectedKey}\n * selectionMode=\"single\"\n * label=\"Select Source\"\n * placeholder=\"Search sources...\"\n * />\n * \n * // Multi select\n * <Autocomplete\n * items={sources}\n * selectedKeys={selectedKeys}\n * onSelectionChange={setSelectedKeys}\n * selectionMode=\"multiple\"\n * label=\"Select Sources\"\n * placeholder=\"Search sources...\"\n * />\n * \n * // No selection (just filtering)\n * <Autocomplete\n * items={sources}\n * selectionMode=\"none\"\n * label=\"Search Sources\"\n * placeholder=\"Search sources...\"\n * />\n * ```\n */\nexport function Autocomplete<T extends AutocompleteItem>({\n items,\n selectedKeys,\n onSelectionChange,\n selectionMode = 'none',\n label,\n placeholder,\n ariaLabel,\n error,\n getItemText = (item) => item.name,\n}: AutocompleteProps<T>) {\n const [inputValue, setInputValue] = useState('');\n const { contains } = useFilter({ sensitivity: 'base' });\n\n // Filter items based on input value\n const filteredItems = useMemo(() => {\n if (!inputValue) return items;\n const searchLower = inputValue.toLowerCase();\n return items.filter((item) =>\n getItemText(item).toLowerCase().includes(searchLower)\n );\n }, [items, inputValue, getItemText]);\n\n // Sort to show selected items first when in selection mode\n const sortedItems = useMemo(() => {\n if (selectionMode === 'none' || !selectedKeys) return filteredItems;\n\n return [...filteredItems].sort((a, b) => {\n const aSelected = selectedKeys === 'all' || selectedKeys.has(String(a.id));\n const bSelected = selectedKeys === 'all' || selectedKeys.has(String(b.id));\n if (aSelected && !bSelected) return -1;\n if (!aSelected && bSelected) return 1;\n return 0;\n });\n }, [filteredItems, selectedKeys, selectionMode]);\n\n const showCheckboxes = selectionMode !== 'none';\n\n return (\n <div className={containerStyles}>\n <AriaAutocomplete\n filter={contains}\n inputValue={inputValue}\n onInputChange={setInputValue}\n >\n <AriaSearchField\n className={searchFieldStyles}\n aria-label={ariaLabel || label}\n >\n <Label className={labelStyles}>{label}</Label>\n <div className={inputContainerStyles}>\n <div className={searchIconStyles}><Search /></div>\n <Input\n className={inputStyles}\n placeholder={placeholder}\n />\n </div>\n </AriaSearchField>\n\n <ListBox\n className={listBoxStyles}\n items={sortedItems}\n selectionMode={selectionMode}\n selectedKeys={selectedKeys}\n onSelectionChange={onSelectionChange}\n aria-label={ariaLabel || `${label} options`}\n >\n {(item) => {\n const isSelected = selectedKeys === 'all' || (selectedKeys && selectedKeys.has(String(item.id)));\n return (\n <ListBoxItem\n key={item.id}\n id={String(item.id)}\n textValue={getItemText(item)}\n className={(renderProps) =>\n `${listBoxItemStyles(renderProps)} ${isSelected ? selectedItemStyles(renderProps) : ''}`\n }\n >\n {showCheckboxes && (\n <div className={checkboxStyles}>\n {isSelected && <CheckmarkCircle />}\n </div>\n )}\n <span className={itemTextStyles}>{getItemText(item)}</span>\n </ListBoxItem>\n );\n }}\n </ListBox>\n\n {error && <div className={errorStyles}>{error}</div>}\n </AriaAutocomplete>\n </div>\n );\n}\n","import { useMemo } from 'react';\nimport type { Selection } from 'react-aria-components';\nimport { Autocomplete, type AutocompleteItem } from './Autocomplete';\nimport { Table, type ColumnDef } from './Table';\nimport { style } from '@react-spectrum/s2/style' with { type: 'macro' };\n\nconst containerStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 16,\n width: 'full',\n});\n\nconst tableContainerStyles = style({\n maxHeight: 384,\n overflow: 'auto',\n});\n\nexport interface AutocompleteTableProps<T extends AutocompleteItem> {\n // Autocomplete props\n allItems: T[];\n selectedKeys: Selection;\n onSelectionChange: (keys: Selection) => void;\n selectionMode?: 'single' | 'multiple';\n searchLabel: string;\n searchPlaceholder?: string;\n getItemText?: (item: T) => string;\n\n // Table props\n columns: ColumnDef<T>[];\n tableLabel: string;\n}\n\n/**\n * Composite component combining Autocomplete and Table\n * Useful for search-and-select workflows where selected items are displayed in a table\n * \n * Features:\n * - Autocomplete search field at top\n * - Table below showing selected items\n * - Filters all items in autocomplete\n * - Shows only selected items in table\n * - Scrollable table with max height of 384px\n * \n * @example\n * ```tsx\n * <AutocompleteTable\n * allItems={allSources}\n * selectedKeys={selectedSourceIds}\n * onSelectionChange={setSelectedSourceIds}\n * selectionMode=\"multiple\"\n * searchLabel=\"Search Sources\"\n * searchPlaceholder=\"Search by name or URL...\"\n * columns={[\n * { key: 'name', label: 'Name', isRowHeader: true },\n * { key: 'domain', label: 'Domain' },\n * { key: 'actions', label: 'Actions', align: 'end' },\n * ]}\n * tableLabel=\"Selected Sources\"\n * />\n * ```\n */\nexport function AutocompleteTable<T extends AutocompleteItem>({\n allItems,\n selectedKeys,\n onSelectionChange,\n selectionMode = 'multiple',\n searchLabel,\n searchPlaceholder,\n getItemText,\n columns,\n tableLabel,\n}: AutocompleteTableProps<T>) {\n // Filter to show only selected items in table\n const selectedItems = useMemo(() => {\n if (selectedKeys === 'all') return allItems;\n if (!selectedKeys || selectedKeys.size === 0) return [];\n\n return allItems.filter(item => selectedKeys.has(String(item.id)));\n }, [allItems, selectedKeys]);\n\n return (\n <div className={containerStyles}>\n <Autocomplete\n items={allItems}\n selectedKeys={selectedKeys}\n onSelectionChange={onSelectionChange}\n selectionMode={selectionMode}\n label={searchLabel}\n placeholder={searchPlaceholder}\n getItemText={getItemText}\n />\n\n <div className={tableContainerStyles}>\n <Table\n columns={columns}\n data={selectedItems}\n ariaLabel={tableLabel}\n />\n </div>\n </div>\n );\n}\n","import {\n Cell,\n Column,\n Row,\n Table as AriaTable,\n TableBody,\n TableHeader,\n type Key,\n type SortDescriptor\n} from 'react-aria-components';\nimport { focusRing, style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { Checkbox } from './Checkbox';\nimport { ProgressBar } from './ProgressBar';\nimport { ReactNode, useRef, useEffect } from 'react';\n\nexport interface ColumnDef<T> {\n key: string;\n label: string;\n renderCell: (item: T) => ReactNode;\n width?: string;\n isRowHeader?: boolean;\n align?: 'start' | 'center' | 'end';\n}\n\nexport interface TableProps<T> {\n data: T[];\n columns: ColumnDef<T>[];\n onRowAction?: (key: Key) => void;\n ariaLabel: string;\n sortDescriptor?: SortDescriptor;\n onSortChange?: (descriptor: SortDescriptor) => void;\n selectionMode?: 'none' | 'single' | 'multiple';\n selectedKeys?: Set<Key>;\n onSelectionChange?: (keys: Set<Key>) => void;\n onLoadMore?: () => void;\n isLoadingMore?: boolean;\n}\n\nconst tableContainerStyles = style({\n backgroundColor: 'layer-1',\n borderRadius: 'xl',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--plum-200',\n overflow: 'auto'\n});\n\nconst tableStyles = style({\n width: 'full',\n borderCollapse: 'collapse'\n});\n\nconst tableHeaderStyles = style({\n backgroundColor: '--yogurt-200',\n borderBottomWidth: 1,\n borderBottomStyle: 'solid',\n borderBottomColor: '--plum-200'\n});\n\nconst columnHeaderStyles = style({\n padding: 16,\n textAlign: {\n default: 'start',\n align: {\n start: 'start',\n center: 'center',\n end: 'end',\n },\n },\n font: 'title-lg',\n color: '--plum-700',\n cursor: {\n allowsSorting: 'pointer'\n }\n});\n\nconst rowStyles = style({\n ...focusRing(),\n backgroundColor: {\n default: 'transparent',\n isHovered: '--yogurt-100',\n isSelected: '--plum-100',\n isFocusVisible: '--yogurt-100'\n },\n cursor: {\n default: 'default',\n onRowAction: 'pointer',\n selectionMode: {\n single: 'pointer',\n multiple: 'pointer'\n }\n }\n});\n\nconst cellStyles = style({\n padding: 16,\n textAlign: {\n default: 'start',\n align: {\n start: 'start',\n center: 'center',\n end: 'end',\n },\n },\n font: 'body',\n color: '--plum-700',\n borderBottomWidth: 1,\n borderBottomStyle: 'solid',\n borderBottomColor: '--plum-100'\n});\n\nconst checkboxCellStyles = style({\n padding: 16,\n width: 48,\n borderBottomWidth: 1,\n borderBottomStyle: 'solid',\n borderBottomColor: '--plum-100'\n});\n\nconst emptyStateStyles = style({\n padding: 48,\n textAlign: 'center',\n font: 'body',\n color: '--plum-600',\n});\n\nconst loadingStyles = style({\n padding: 24,\n display: 'flex',\n justifyContent: 'center'\n});\n\nconst scrollTriggerStyles = style({\n height: 1,\n pointerEvents: 'none'\n});\n\n/**\n * Reusable Table component using React Aria Components\n * \n * Features:\n * - Collection-based API for better performance\n * - Built-in virtualization support (via RAC)\n * - Keyboard navigation\n * - Screen reader support\n * - Optional sorting\n * - Optional row selection\n * - Optional row actions (clickable rows)\n * - Checkbox column for multiple selection\n * - Hover and selected states\n * \n * @example\n * ```tsx\n * const columns: ColumnDef<Run>[] = [\n * { key: 'id', label: 'ID', renderCell: (run) => `#${run.id}` },\n * { key: 'status', label: 'Status', renderCell: (run) => <Badge variant={run.status}>{run.status}</Badge> }\n * ];\n * \n * <Table\n * data={runs}\n * columns={columns}\n * ariaLabel=\"Scraper runs\"\n * selectionMode=\"multiple\"\n * selectedKeys={selectedIds}\n * onSelectionChange={setSelectedIds}\n * />\n * ```\n */\nexport function Table<T extends { id: number | string }>({\n data,\n columns,\n onRowAction,\n ariaLabel,\n sortDescriptor,\n onSortChange,\n selectionMode = 'none',\n selectedKeys,\n onSelectionChange,\n onLoadMore,\n isLoadingMore\n}: TableProps<T>) {\n const observerTarget = useRef<HTMLDivElement>(null);\n\n // Infinite scroll observer\n useEffect(() => {\n if (!onLoadMore) return;\n\n const observer = new IntersectionObserver(\n (entries) => {\n if (entries[0].isIntersecting && !isLoadingMore) {\n onLoadMore();\n }\n },\n { threshold: 0.1, rootMargin: '100px' }\n );\n\n if (observerTarget.current) {\n observer.observe(observerTarget.current);\n }\n\n return () => observer.disconnect();\n }, [onLoadMore, isLoadingMore]);\n\n return (\n <>\n <div className={tableContainerStyles}>\n <AriaTable\n aria-label={ariaLabel}\n selectionMode={selectionMode}\n selectedKeys={selectedKeys}\n onSelectionChange={onSelectionChange as any}\n onRowAction={onRowAction}\n sortDescriptor={sortDescriptor}\n onSortChange={onSortChange}\n className={tableStyles}\n >\n <TableHeader className={tableHeaderStyles}>\n {selectionMode === 'multiple' && (\n <Column className={columnHeaderStyles({ align: 'center', alignItems: 'center' })}>\n <Checkbox slot=\"selection\" size=\"S\" />\n </Column>\n )}\n {columns.map((column) => (\n <Column\n key={column.key}\n id={column.key}\n isRowHeader={column.isRowHeader}\n allowsSorting={!!onSortChange}\n className={columnHeaderStyles({ align: column.align })}\n >\n {column.label}\n </Column>\n ))}\n </TableHeader>\n <TableBody\n items={data}\n renderEmptyState={() => (\n <div className={emptyStateStyles}>\n No data available\n </div>\n )}\n >\n {(item) => (\n <Row\n key={item.id}\n id={item.id}\n className={(renderProps) => rowStyles({\n ...renderProps,\n onRowAction: !!onRowAction,\n selectionMode\n })}\n >\n {selectionMode === 'multiple' && (\n <Cell className={checkboxCellStyles}>\n <Checkbox slot=\"selection\" size=\"S\" />\n </Cell>\n )}\n {columns.map((column) => (\n <Cell key={column.key} className={cellStyles({ align: column.align })}>\n {column.renderCell(item)}\n </Cell>\n ))}\n </Row>\n )}\n </TableBody>\n </AriaTable>\n </div>\n\n {/* Infinite scroll trigger */}\n {onLoadMore && <div ref={observerTarget} className={scrollTriggerStyles} />}\n\n {/* Loading indicator */}\n {isLoadingMore && (\n <div className={loadingStyles}>\n <ProgressBar isIndeterminate aria-label=\"Loading more items\" />\n </div>\n )}\n </>\n );\n}\n\n","import { Checkbox as AriaCheckbox, type CheckboxProps as AriaCheckboxProps } from 'react-aria-components';\nimport { focusRing, style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport './Checkbox.css';\n\nexport interface CheckboxProps extends Omit<AriaCheckboxProps, 'className' | 'style'> {\n /**\n * The size of the Checkbox.\n * @default 'M'\n */\n size?: 'S' | 'M' | 'L';\n}\n\nconst checkboxStyles = style({\n display: 'flex',\n position: 'relative',\n alignItems: 'center',\n gap: 8,\n font: 'body',\n color: {\n default: '--plum-700',\n isDisabled: 'disabled'\n },\n cursor: {\n default: 'pointer',\n isDisabled: 'default'\n },\n disableTapHighlight: true\n});\n\nconst boxStyles = style({\n ...focusRing(),\n size: {\n default: 20,\n size: {\n S: 16,\n M: 20,\n L: 24\n }\n },\n flexShrink: 0,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n borderWidth: 2,\n borderStyle: 'solid',\n borderRadius: 'sm',\n transition: 'default',\n backgroundColor: {\n default: 'layer-1',\n isSelected: {\n default: '--plum-600',\n isHovered: '--plum-700',\n isPressed: '--plum-800'\n },\n isDisabled: {\n default: 'gray-100',\n isSelected: 'gray-400'\n }\n },\n borderColor: {\n default: '--plum-400',\n isHovered: '--plum-500',\n isPressed: '--plum-600',\n isSelected: 'transparent',\n isDisabled: 'gray-300'\n }\n});\n\nconst dashStyles = style({\n width: '60%',\n height: 2,\n backgroundColor: 'white',\n borderRadius: 'sm'\n});\n\n/**\n * Checkbox component for selection within tables and forms.\n * Simplified version based on React Spectrum S2 Checkbox.\n * \n * @example\n * ```tsx\n * <Checkbox isSelected={selected} onChange={setSelected}>\n * Subscribe to newsletter\n * </Checkbox>\n * \n * // In table header/cell (no label)\n * <Checkbox slot=\"selection\" />\n * ```\n */\nexport function Checkbox({ size = 'M', children, ...props }: CheckboxProps) {\n return (\n <AriaCheckbox\n {...props}\n className={(renderProps) => checkboxStyles(renderProps)}\n >\n {(renderProps) => {\n const { isSelected, isIndeterminate } = renderProps;\n\n return (\n <>\n <div className={boxStyles({ ...renderProps, isSelected: isSelected || isIndeterminate, size })}>\n {isIndeterminate ? (\n <div className={dashStyles} />\n ) : isSelected ? (\n <svg viewBox=\"0 0 18 18\" aria-hidden=\"true\" className=\"checkbox-checkmark\">\n <polyline points=\"2 9 7 14 16 4\" className=\"checkbox-checkmark-path checkbox-checkmark-selected\" />\n </svg>\n ) : null}\n </div>\n {children}\n </>\n );\n }}\n </AriaCheckbox>\n );\n}\n","import { ProgressBar as AriaProgressBar } from 'react-aria-components';\nimport { style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport './ProgressBar.css';\n\nconst progressBarStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 4,\n width: 'full',\n});\n\nconst labelStyles = style({\n font: 'body',\n color: '--plum-700',\n display: 'flex',\n justifyContent: 'space-between',\n});\n\nconst trackStyles = style({\n height: 6,\n backgroundColor: '--yogurt-200',\n borderRadius: 'full',\n overflow: 'hidden',\n position: 'relative',\n});\n\nconst fillStyles = style({\n height: 'full',\n backgroundColor: '--plum-500',\n borderRadius: 'full',\n});\n\nconst indeterminateFillStyles = style({\n height: 'full',\n backgroundColor: '--plum-500',\n borderRadius: 'full',\n width: '40%',\n});\n\ninterface ProgressBarProps {\n label?: string;\n value?: number;\n maxValue?: number;\n isIndeterminate?: boolean;\n showValueLabel?: boolean;\n 'aria-label'?: string;\n}\n\n/**\n * ProgressBar Component\n * \n * Accessible progress indicator using React Aria Components.\n * Supports both determinate (with value) and indeterminate (loading) states.\n * \n * @example\n * // Determinate progress\n * <ProgressBar label=\"Loading...\" value={50} maxValue={100} />\n * \n * @example\n * // Indeterminate progress\n * <ProgressBar label=\"Loading...\" isIndeterminate />\n */\nexport function ProgressBar({\n label,\n value = 0,\n maxValue = 100,\n isIndeterminate = false,\n showValueLabel = true,\n 'aria-label': ariaLabel,\n}: ProgressBarProps) {\n return (\n <AriaProgressBar\n value={isIndeterminate ? undefined : value}\n maxValue={maxValue}\n isIndeterminate={isIndeterminate}\n aria-label={ariaLabel || label}\n className={progressBarStyles}\n >\n {({ percentage, valueText }) => (\n <>\n {label && (\n <div className={labelStyles}>\n <span>{label}</span>\n {!isIndeterminate && showValueLabel && <span>{valueText}</span>}\n </div>\n )}\n <div className={trackStyles}>\n <div\n className={`${isIndeterminate ? 'progress-fill-indeterminate' : 'progress-fill'} ${isIndeterminate ? indeterminateFillStyles : fillStyles}`}\n style={isIndeterminate ? undefined : { width: `${percentage}%` }}\n />\n </div>\n </>\n )}\n </AriaProgressBar>\n );\n}\n","import { Button as AriaButton, ButtonProps as AriaButtonProps } from 'react-aria-components';\nimport { focusRing, style } from '@react-spectrum/s2/style' with { type: 'macro' };\n\nexport interface AvatarButtonProps extends AriaButtonProps {\n initials: string;\n name: string;\n}\n\nconst avatarButtonStyles = style({\n ...focusRing(),\n display: 'flex',\n alignItems: 'center',\n gap: 12,\n padding: 8,\n paddingX: 16,\n backgroundColor: {\n default: '--plum-100',\n isHovered: '--plum-200',\n isPressed: '--plum-300',\n isFocusVisible: '--plum-200'\n },\n color: '--plum-800',\n borderRadius: 'full',\n borderWidth: 2,\n borderStyle: 'solid',\n borderColor: '--plum-300',\n font: 'title-lg',\n cursor: 'pointer'\n});\n\nconst avatarCircleStyles = style({\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: 32,\n height: 32,\n borderRadius: 'full',\n backgroundColor: '--plum-500',\n color: 'white',\n font: 'title-lg'\n});\n\nexport const AvatarButton = ({ initials, name, ...props }: AvatarButtonProps) => {\n return (\n <AriaButton\n {...props}\n className={renderProps => avatarButtonStyles(renderProps)}\n >\n <div className={avatarCircleStyles}>\n {initials}\n </div>\n <span>{name}</span>\n </AriaButton>\n );\n};\n","import { style } from '@react-spectrum/s2/style' with { type: 'macro' };\n\nexport type BadgeVariant = 'completed' | 'running' | 'failed' | 'cancelled' | 'pending' | 'plum' | 'cucumber' | 'berry' | 'ginger' | 'peach';\n\nexport interface BadgeProps {\n variant?: BadgeVariant;\n children: React.ReactNode;\n}\n\nconst badgeStyles = style({\n paddingY: 4,\n paddingX: 12,\n borderRadius: 'lg',\n font: 'ui-sm',\n display: 'inline-block',\n backgroundColor: {\n variant: {\n completed: '--cucumber-100',\n running: '--ginger-100',\n failed: '--berry-100',\n cancelled: '--plum-100',\n pending: '--plum-100',\n plum: '--plum-100',\n cucumber: '--cucumber-100',\n berry: '--berry-100',\n ginger: '--ginger-100',\n peach: '--peach-100',\n yogurt: '--yogurt-100',\n }\n },\n color: {\n variant: {\n completed: '--cucumber-700',\n running: '--ginger-700',\n failed: '--berry-700',\n cancelled: '--plum-700',\n pending: '--plum-700',\n plum: '--plum-700',\n cucumber: '--cucumber-700',\n berry: '--berry-700',\n ginger: '--ginger-700',\n peach: '--peach-700',\n yogurt: '--yogurt-700',\n }\n }\n});\n\n/**\n * Badge component for displaying status or category with conditional colors\n * \n * @example\n * <Badge variant=\"completed\">Completed</Badge>\n * <Badge variant=\"cucumber\">Vegetable</Badge>\n */\nexport function Badge({ variant, children }: BadgeProps) {\n return (\n <span className={badgeStyles({ variant })}>\n {children}\n </span>\n );\n}\n","import { Button as AriaButton, ButtonProps as AriaButtonProps } from 'react-aria-components';\nimport CloseCircle from '@react-spectrum/s2/icons/CloseCircle';\nimport { focusRing, iconStyle, style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { useIntl } from '../lib/i18n-context';\n\n// For props: https://react-spectrum.adobe.com/react-aria/Button.html#props\n// For styling: https://react-spectrum.adobe.com/react-aria/Button.html#styling\n\nexport interface CloseButtonProps extends Omit<AriaButtonProps, 'onPress'> {\n onClose: () => void;\n}\n\nconst closeButtonStyles = style({\n ...focusRing(),\n padding: 8,\n backgroundColor: {\n default: 'transparent',\n isHovered: '--yogurt-100',\n isPressed: '--yogurt-200'\n },\n borderStyle: 'none',\n borderRadius: 'lg',\n cursor: 'pointer',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n forcedColors: {\n backgroundColor: 'ButtonFace',\n color: 'ButtonText',\n borderColor: 'ButtonBorder',\n borderWidth: 1,\n borderStyle: 'solid'\n }\n});\n\nexport const CloseButton = ({ onClose, ...props }: CloseButtonProps) => {\n const intl = useIntl();\n\n return (\n <AriaButton\n {...props}\n onPress={onClose}\n className={renderProps => closeButtonStyles(renderProps)}\n aria-label={intl.formatMessage({ id: 'ferment.detail.close' })}\n >\n <CloseCircle styles={iconStyle({ size: 'XL' })} />\n </AriaButton>\n );\n};\n","import { Button, ComboBox as AriaComboBox, ComboBoxRenderProps, FieldError, Group, Input, Label, ListBox, ListBoxItem, Popover, Text } from 'react-aria-components';\nimport type { Key } from 'react-aria-components';\nimport { focusRing, style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport ChevronDown from '@react-spectrum/s2/icons/ChevronDown';\n\nconst comboBoxContainerStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 4,\n minWidth: 192,\n});\n\nconst labelStyles = style({\n font: 'body',\n color: '--plum-700',\n});\n\nconst inputWrapperStyles = style({\n position: 'relative',\n display: 'flex',\n alignItems: 'center',\n});\n\nconst inputStyles = style({\n ...focusRing(),\n width: 'calc(100% - 44px)', // Padding (12px+32px) and border (1px+1px)\n font: 'body',\n flexGrow: 1,\n flexShrink: 1,\n paddingStart: 12,\n paddingEnd: 32,\n paddingY: 8,\n backgroundColor: {\n default: '--yogurt-100',\n isFocusVisible: '--yogurt-200',\n isDisabled: 'gray-100',\n },\n borderRadius: 'lg',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: {\n default: '--yogurt-400',\n isFocusVisible: '--plum-600',\n isDisabled: 'gray-300',\n },\n color: {\n default: '--plum-700',\n isDisabled: 'gray-500',\n },\n outlineStyle: 'none',\n});\n\nconst buttonStyles = style({\n verticalAlign: 'middle',\n marginStart: -36, // 32px for button + padding and 4px this padding\n marginEnd: 8,\n padding: 4,\n borderRadius: 'sm',\n backgroundColor: {\n default: 'transparent',\n isHovered: 'gray-200',\n },\n borderStyle: 'none',\n color: {\n default: 'gray-600',\n isDisabled: 'gray-400',\n },\n cursor: {\n default: 'pointer',\n isDisabled: 'default',\n },\n});\n\nconst popoverStyles = style({\n backgroundColor: '--yogurt-50',\n borderRadius: 'lg',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--yogurt-400',\n boxShadow: 'elevated',\n marginTop: 4,\n maxHeight: 320,\n overflow: 'auto',\n isolation: 'isolate',\n});\n\nconst listBoxStyles = style({\n padding: 4,\n outline: 'none',\n});\n\nconst listBoxItemStyles = style({\n font: 'body',\n paddingX: 8,\n paddingY: 4,\n borderRadius: 'sm',\n cursor: {\n default: 'pointer',\n isDisabled: 'not-allowed',\n },\n backgroundColor: {\n default: 'transparent',\n isHovered: '--yogurt-200',\n isSelected: '--plum-200',\n isFocused: '--yogurt-200',\n isDisabled: '--yogurt-100',\n },\n color: {\n default: '--plum-700',\n isHovered: '--plum-800',\n isFocused: '--plum-800',\n isPressed: '--plum-900',\n isSelected: '--plum-700',\n isDisabled: 'gray-400',\n },\n outline: 'none',\n});\n\nconst descriptionStyles = style({\n font: 'detail',\n color: 'gray-600',\n});\n\nconst errorStyles = style({\n font: 'detail',\n color: '--berry-700',\n});\n\nexport interface ComboBoxItem {\n id: Key;\n label: string;\n textValue?: string;\n}\n\nexport interface ComboBoxProps {\n label?: string;\n description?: string;\n errorMessage?: string;\n isRequired?: boolean;\n isDisabled?: boolean;\n selectedKey?: Key | null;\n onSelectionChange?: (key: Key | null) => void;\n items: ComboBoxItem[];\n placeholder?: string;\n 'aria-label'?: string;\n inputValue?: string;\n onInputChange?: (value: string) => void;\n}\n\n/**\n * Generic ComboBox component for searchable single-selection.\n * \n * Uses React Aria Components ComboBox for accessible selection with filtering.\n * Reusable for any data type - countries, users, categories, etc.\n * \n * @example\n * ```tsx\n * const items = [\n * { id: 1, label: 'Apple' },\n * { id: 2, label: 'Banana' },\n * { id: 3, label: 'Orange' },\n * ];\n * \n * <ComboBox\n * label=\"Fruit\"\n * items={items}\n * selectedKey={selectedId}\n * onSelectionChange={setSelectedId}\n * placeholder=\"Select a fruit\"\n * />\n * ```\n */\nexport function ComboBox({\n label,\n description,\n errorMessage,\n isRequired = false,\n isDisabled = false,\n selectedKey,\n onSelectionChange,\n items,\n placeholder,\n 'aria-label': ariaLabel,\n inputValue: controlledInputValue,\n onInputChange: controlledOnInputChange,\n}: ComboBoxProps) {\n return (\n <AriaComboBox\n className={comboBoxContainerStyles}\n inputValue={controlledInputValue}\n onInputChange={controlledOnInputChange}\n selectedKey={selectedKey}\n onSelectionChange={onSelectionChange}\n isDisabled={isDisabled}\n isRequired={isRequired}\n aria-label={ariaLabel || label}\n >\n {label && <Label className={labelStyles}>{label}</Label>}\n <div className={inputWrapperStyles}>\n <Input className={inputStyles} placeholder={placeholder} />\n <Button className={buttonStyles}>\n <ChevronDown />\n </Button>\n </div>\n {description && !errorMessage && (\n <Text slot=\"description\" className={descriptionStyles}>\n {description}\n </Text>\n )}\n {errorMessage && <FieldError className={errorStyles}>{errorMessage}</FieldError>}\n <Popover className={popoverStyles}>\n <ListBox className={listBoxStyles} items={items}>\n {(item) => (\n <ListBoxItem\n key={item.id}\n id={item.id}\n textValue={item.textValue || item.label}\n className={listBoxItemStyles}\n >\n {item.label}\n </ListBoxItem>\n )}\n </ListBox>\n </Popover>\n </AriaComboBox>\n );\n}\n","import {\n Button as AriaButton,\n Disclosure,\n DisclosureGroup as AriaDisclosureGroup,\n DisclosurePanel,\n DisclosureStateContext,\n Heading\n} from 'react-aria-components';\nimport ChevronRight from '@react-spectrum/s2/icons/ChevronRight';\nimport type { DisclosureGroupProps } from 'react-aria-components';\nimport { style, iconStyle, focusRing } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { useContext } from 'react';\n\nimport './DisclosureGroup.css';\n\n// Disclosure container - very thin grey border, full width button appearance\nconst disclosureContainerStyles = style({\n borderStyle: 'none',\n borderWidth: 0,\n borderColor: 'transparent',\n borderRadius: 'lg',\n overflow: 'hidden',\n width: 'full',\n backgroundColor: '--yogurt-100'\n});\n\n// Disclosure trigger button - blends seamlessly with container\nconst disclosureTriggerStyles = style({\n ...focusRing(),\n font: 'title',\n color: {\n default: '--plum-700',\n isHovered: '--plum-800',\n isFocusVisible: '--plum-800',\n isPressed: '--plum-900'\n },\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n padding: 16,\n paddingX: 20,\n width: 'full',\n backgroundColor: {\n default: '--yogurt-100',\n isHovered: '--yogurt-200',\n isPressed: '--yogurt-300'\n },\n borderStyle: 'none',\n borderRadius: 'none',\n cursor: 'pointer'\n});\n\nconst disclosureHeaderStyles = style({\n margin: 0,\n});\n\n// Disclosure panel - white/light background, blends with button header\nconst disclosurePanelStyles = style({\n padding: {\n default: 0,\n isExpanded: 20,\n },\n backgroundColor: '--yogurt-50',\n borderTopWidth: 1,\n borderTopStyle: 'solid',\n borderColor: '--yogurt-300'\n});\n\nexport interface DisclosureItem {\n id: string;\n title: string;\n content: React.ReactNode;\n onPress?: () => void;\n}\n\ninterface CustomDisclosureGroupProps extends Omit<DisclosureGroupProps, 'children'> {\n items: DisclosureItem[];\n 'aria-label'?: string;\n}\n\n/**\n * Reusable DisclosureGroup component\n * Displays collapsible sections with consistent styling\n * \n * @example\n * <DisclosureGroup\n * items={[\n * {\n * id: 'section1',\n * title: 'Section 1',\n * content: <div>Content goes here</div>\n * }\n * ]}\n * />\n */\nexport function DisclosureGroup({ items, ...props }: CustomDisclosureGroupProps) {\n return (\n <AriaDisclosureGroup {...props} className={disclosureContainerStyles}>\n {items.map((item) => (\n <Disclosure key={item.id}>\n <DisclosureContents key={item.id} {...item} />\n </Disclosure>\n ))}\n </AriaDisclosureGroup>\n );\n}\n\nconst DisclosureContents = (item: DisclosureItem) => {\n let { isExpanded } = useContext(DisclosureStateContext)!;\n\n return (\n <>\n <Heading className={disclosureHeaderStyles}>\n <AriaButton\n slot=\"trigger\"\n className={(renderProps) =>\n `disclosure-chevron ${disclosureTriggerStyles(renderProps)}`\n }\n onPress={item.onPress}\n >\n <ChevronRight styles={iconStyle({ size: 'S' })} />\n {item.title}\n </AriaButton>\n </Heading>\n <DisclosurePanel className={disclosurePanelStyles({ isExpanded })}>\n {item.content}\n </DisclosurePanel>\n </>\n );\n}\n","import {\n TextField as AriaTextField,\n Label,\n Text,\n} from 'react-aria-components';\nimport { style, } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport {\n textFieldDescriptionStyles as labeledValueDescriptionStyles,\n textFieldErrorStyles as labeledValueErrorStyles,\n textFieldLabelStyles as labeledValueLabelStyles,\n} from './TextField';\n\nexport interface LabeledValueProps {\n label: string;\n error?: string;\n description?: string;\n children: React.ReactNode;\n}\n\nconst labeledValueStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 8,\n marginBottom: 20,\n});\n\nconst labeledValueValueStyles = style({\n font: 'body',\n color: '--plum-700',\n});\n\n\nexport function LabeledValue({ label, children, error, description, ...props }: LabeledValueProps) {\n return (\n <AriaTextField className={labeledValueStyles} {...props}>\n <Label className={labeledValueLabelStyles}>{label}</Label>\n <span className={labeledValueValueStyles}>\n {children}\n </span>\n {description && !error && (\n <Text slot=\"description\" className={labeledValueDescriptionStyles}>\n {description}\n </Text>\n )}\n {error && (\n <Text slot=\"errorMessage\" className={labeledValueErrorStyles}>\n {error}\n </Text>\n )}\n </AriaTextField>\n );\n}\n","import {\n MenuTrigger,\n Menu as AriaMenu,\n MenuItem as AriaMenuItem,\n Popover,\n Separator\n} from 'react-aria-components';\nimport { style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { ReactNode } from 'react';\n\nconst menuStyles = style({\n backgroundColor: '--yogurt-50',\n borderRadius: 'lg',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--yogurt-400',\n boxShadow: 'elevated',\n padding: 4,\n minWidth: 200,\n outline: 'none'\n});\n\nconst menuItemStyles = style({\n display: 'flex',\n alignItems: 'center',\n gap: 12,\n paddingX: 12,\n paddingY: 8,\n borderRadius: 'lg',\n font: 'body',\n color: {\n default: '--plum-700',\n isHovered: '--plum-800',\n isFocused: '--plum-800',\n isPressed: '--plum-900'\n },\n backgroundColor: {\n default: 'transparent',\n isHovered: '--yogurt-200',\n isFocused: '--yogurt-200'\n },\n cursor: 'pointer',\n outline: 'none'\n});\n\nconst dividerStyles = style({\n height: 1,\n backgroundColor: '--yogurt-400',\n marginY: 4\n});\n\nexport interface MenuItem {\n id: string;\n label: string;\n icon?: ReactNode;\n onAction: () => void;\n}\n\nexport interface MenuProps {\n trigger: ReactNode;\n items: MenuItem[];\n placement?: 'bottom' | 'bottom start' | 'bottom end' | 'top' | 'top start' | 'top end';\n 'aria-label'?: string;\n}\n\n/**\n * Menu component styled to match Select component\n * Uses yogurt colors for consistency with Select\n */\nexport function Menu({ trigger, items, placement = 'bottom end', 'aria-label': ariaLabel }: MenuProps) {\n return (\n <MenuTrigger>\n {trigger}\n <Popover placement={placement}>\n <AriaMenu className={menuStyles} aria-label={ariaLabel}>\n {items.map((item, index) =>\n item.id === 'divider' ? (\n <Separator key={`divider-${index}`} className={dividerStyles} />\n ) : (\n <AriaMenuItem\n key={item.id}\n id={item.id}\n onAction={item.onAction}\n className={menuItemStyles}\n >\n {item.icon}\n {item.label}\n </AriaMenuItem>\n )\n )}\n </AriaMenu>\n </Popover>\n </MenuTrigger>\n );\n}\n","import { style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { useIntl } from '../lib/i18n-context';\n\nexport interface PasswordValidation {\n minLength: boolean;\n hasLetter: boolean;\n hasNumber: boolean;\n hasSpecial: boolean;\n}\n\nexport interface PasswordStrengthProps {\n validation: PasswordValidation;\n}\n\nconst containerStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 8\n});\n\nconst requirementStyles = style({\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n font: 'body-sm'\n});\n\nconst indicatorStyles = style({\n width: 16,\n height: 16,\n borderRadius: 'full',\n flexShrink: 0,\n backgroundColor: {\n default: '--gray-300',\n isValid: '--cucumber-500'\n },\n color: {\n default: 'inherit',\n isValid: '--cucumber-700'\n }\n});\n\n/**\n * Visual password strength indicator showing validation requirements\n * Displays checkmarks (green) for met requirements, circles (gray) for unmet\n */\nexport function PasswordStrength({ validation }: PasswordStrengthProps) {\n const intl = useIntl();\n\n const requirements = [\n {\n key: 'minLength',\n met: validation.minLength,\n label: intl.formatMessage({ id: 'validation.password.minLength' })\n },\n {\n key: 'hasLetter',\n met: validation.hasLetter,\n label: intl.formatMessage({ id: 'validation.password.letter' })\n },\n {\n key: 'hasNumber',\n met: validation.hasNumber,\n label: intl.formatMessage({ id: 'validation.password.number' })\n },\n {\n key: 'hasSpecial',\n met: validation.hasSpecial,\n label: intl.formatMessage({ id: 'validation.password.special' })\n }\n ];\n\n console.log('PasswordStrength requirements:', requirements, intl, intl.formatMessage({ id: 'validation.password.special' }));\n\n return (\n <div className={containerStyles}>\n {requirements.map(req => (\n <div key={req.key} className={requirementStyles}>\n <div\n className={indicatorStyles({ isValid: req.met })}\n />\n <span>{req.label}</span>\n </div>\n ))}\n </div>\n );\n}\n","import { Radio as AriaRadio, RadioGroup as AriaRadioGroup, type RadioGroupProps as AriaRadioGroupProps, type RadioProps as AriaRadioProps } from 'react-aria-components';\nimport { style, focusRing } from '@react-spectrum/s2/style' with { type: 'macro' };\n\nexport interface RadioGroupProps extends Omit<AriaRadioGroupProps, 'children'> {\n label: string;\n options: Array<{ id: string; label: string; value: string }>;\n error?: string;\n}\n\nexport interface RadioProps extends AriaRadioProps {\n children: React.ReactNode;\n}\n\nconst radioGroupStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 8,\n});\n\nconst labelStyles = style({\n font: 'body',\n fontWeight: 'medium',\n color: '--plum-700',\n marginBottom: 8,\n});\n\nconst radioContainerStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 12,\n});\n\nconst radioStyles = style({\n ...focusRing(),\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n font: 'body',\n color: {\n default: '--plum-700',\n isSelected: '--plum-800',\n isHovered: '--plum-600',\n isDisabled: 'gray-400',\n },\n cursor: 'pointer',\n padding: 8,\n borderRadius: 'lg',\n backgroundColor: {\n default: 'transparent',\n isHovered: '--plum-50',\n },\n});\n\nconst radioButtonStyles = style({\n width: 20,\n height: 20,\n borderRadius: 'full',\n borderWidth: 2,\n borderStyle: 'solid',\n borderColor: {\n default: 'gray-400',\n isSelected: '--plum-500',\n isHovered: '--plum-300',\n isDisabled: 'gray-200',\n },\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n flexShrink: 0,\n backgroundColor: {\n default: 'layer-1',\n isDisabled: 'gray-100',\n },\n});\n\nconst radioInnerDotStyles = style({\n width: 10,\n height: 10,\n borderRadius: 'full',\n backgroundColor: '--plum-500',\n opacity: {\n default: 0,\n isSelected: 1,\n },\n});\n\nconst errorStyles = style({\n font: 'body-sm',\n color: '--berry-600',\n marginTop: 4,\n});\n\nexport function Radio({ children, ...props }: RadioProps) {\n return (\n <AriaRadio {...props} className={renderProps => radioStyles(renderProps)}>\n {renderProps => (\n <>\n <div className={radioButtonStyles(renderProps)}>\n <div className={radioInnerDotStyles(renderProps)} />\n </div>\n <span>{children}</span>\n </>\n )}\n </AriaRadio>\n );\n}\n\nexport function RadioGroup({ label, options, error, ...props }: RadioGroupProps) {\n return (\n <AriaRadioGroup {...props} className={radioGroupStyles}>\n <span className={labelStyles}>{label}</span>\n <div className={radioContainerStyles}>\n {options.map(option => (\n <Radio key={option.id} value={option.value}>\n {option.label}\n </Radio>\n ))}\n </div>\n {error && <span className={errorStyles}>{error}</span>}\n </AriaRadioGroup>\n );\n}\n","import { SearchField as AriaSearchField, Input, Label, Button } from 'react-aria-components';\nimport Close from '@react-spectrum/s2/icons/Close';\nimport { focusRing, style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { raw } from '../utils/styleMacro' with { type: 'macro' };\nimport Search from '@react-spectrum/s2/icons/Search';\n\n\nconst searchFieldContainerStyles = style({\n flex: {\n default: 'none',\n md: 1,\n },\n});\n\nconst searchFieldStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 4,\n});\n\nconst searchInputContainerStyles = style({\n position: 'relative',\n display: 'flex',\n alignItems: 'center',\n});\n\nconst searchInputStyles = style({\n ...focusRing(),\n width: 'full',\n padding: 8,\n paddingStart: 40,\n font: 'body',\n borderRadius: 'lg',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--yogurt-400',\n backgroundColor: {\n default: '--yogurt-100',\n isHovered: '--yogurt-200',\n isFocusVisible: '--yogurt-200',\n },\n});\n\nconst labelStyles = style({\n font: 'body',\n color: '--plum-700',\n marginBottom: 4,\n});\n\nconst searchIconStyles = {\n position: 'absolute' as const,\n left: 12,\n top: '50%',\n transform: 'translateY(-50%)',\n pointerEvents: 'none' as const,\n color: '--yogurt-600',\n};\n\nconst clearButtonStyles = style({\n ...focusRing(),\n position: 'absolute',\n right: 8,\n top: '50%',\n transform: 'translateY(-50%)',\n padding: 4,\n backgroundColor: 'transparent',\n borderStyle: 'none',\n borderRadius: 'default',\n cursor: 'pointer',\n color: {\n default: '--yogurt-600',\n isHovered: '--yogurt-700',\n isPressed: '--yogurt-800',\n },\n});\n\nexport interface SearchFieldProps {\n value: string;\n onChange: (value: string) => void;\n label: string;\n placeholder: string;\n ariaLabel: string;\n}\n\n/**\n * Reusable SearchField component using React Aria Components\n * Styled with S2 design tokens and includes search icon\n * \n * @example\n * <SearchField\n * value={searchQuery}\n * onChange={setSearchTerm}\n * label=\"Search\"\n * placeholder=\"Search ferments...\"\n * ariaLabel=\"Search ferments by name or description\"\n * />\n */\nexport function SearchField({\n value,\n onChange,\n label,\n placeholder,\n ariaLabel,\n}: SearchFieldProps) {\n return (\n <div className={searchFieldContainerStyles}>\n <AriaSearchField\n value={value}\n onChange={onChange}\n className={searchFieldStyles}\n aria-label={ariaLabel}\n >\n {({ isEmpty }) => (\n <>\n <Label className={labelStyles}>{label}</Label>\n <div className={searchInputContainerStyles}>\n <div style={searchIconStyles}>\n <Search />\n </div>\n <Input className={(renderProps) => searchInputStyles(renderProps) + ' ' + raw('&::-webkit-search-cancel-button { display: none }', 'custom_layer')} placeholder={placeholder} />\n {!isEmpty && <Button className={clearButtonStyles} aria-label=\"Clear search\">\n <Close />\n </Button>}\n </div>\n </>\n )}\n </AriaSearchField>\n </div>\n );\n}\n","import { useRouter } from 'next/navigation';\nimport {\n ToggleButton,\n ToggleButtonGroup,\n SelectionIndicator\n} from 'react-aria-components';\nimport { focusRing, style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { useIntl } from '../lib/i18n-context';\nimport GlobeGrid from '@react-spectrum/s2/icons/GlobeGrid';\nimport ListIcon from '@react-spectrum/s2/icons/ViewList';\n\nconst segmentedControlStyles = style({\n display: 'flex',\n gap: 0,\n padding: 4,\n backgroundColor: '--yogurt-200',\n borderRadius: 'lg',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--yogurt-400',\n});\n\nconst toggleButtonStyles = style({\n ...focusRing(),\n position: 'relative',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: 8,\n paddingX: 16,\n paddingY: 8,\n font: 'body',\n color: {\n default: '--plum-700',\n isHovered: '--plum-800',\n isFocusVisible: '--plum-800',\n isPressed: '--plum-900'\n },\n backgroundColor: 'transparent',\n borderStyle: 'none',\n borderRadius: 'default',\n cursor: 'pointer',\n zIndex: 1,\n '--iconPrimary': {\n type: 'color',\n value: {\n default: '--plum-700',\n isHovered: '--plum-800',\n isFocusVisible: '--plum-800',\n isPressed: '--plum-900'\n },\n }\n});\n\nconst selectionIndicatorStyles = style({\n position: 'absolute',\n inset: 0,\n borderRadius: 'default',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: 'gray-300',\n boxShadow: 'elevated',\n zIndex: -1,\n color: {\n isDisabled: 'gray-400',\n forcedColors: 'ButtonText'\n },\n backgroundColor: {\n default: '--plum-300',\n isHovered: '--plum-400',\n isFocusVisibile: '--plum-400',\n isPressed: '--plum-400',\n isDisabled: 'gray-100',\n forcedColors: 'ButtonFace'\n },\n});\n\ninterface SegmentedControlProps {\n value: 'map' | 'list';\n}\n\n/**\n * SegmentedControl for Map/List view toggle\n * Uses RAC ToggleButtonGroup for accessible segmented control\n */\nexport function SegmentedControl({ value }: SegmentedControlProps) {\n const intl = useIntl();\n const router = useRouter();\n\n const handleSelectionChange = (newValue: 'map' | 'list') => {\n if (newValue === 'map') {\n router.push('/');\n } else {\n router.push('/ferments');\n }\n };\n\n return (\n <ToggleButtonGroup\n selectionMode=\"single\"\n selectedKeys={new Set([value])}\n disallowEmptySelection\n onSelectionChange={(keys) => {\n const selected = Array.from(keys)[0] as 'map' | 'list';\n handleSelectionChange(selected);\n }}\n aria-label={intl.formatMessage({ id: 'nav.viewToggle.ariaLabel' })}\n className={segmentedControlStyles}\n >\n <ToggleButton\n id=\"map\"\n className={(renderProps) => toggleButtonStyles(renderProps)}\n aria-label={intl.formatMessage({ id: 'nav.map' })}\n >\n {(renderProps) => (\n <>\n <SelectionIndicator className={selectionIndicatorStyles(renderProps)} />\n <GlobeGrid aria-hidden=\"true\" />\n <span>{intl.formatMessage({ id: 'nav.map' })}</span>\n </>\n )}\n </ToggleButton>\n\n <ToggleButton\n id=\"list\"\n className={(renderProps) => toggleButtonStyles(renderProps)}\n aria-label={intl.formatMessage({ id: 'nav.list' })}\n >\n {(renderProps) => (\n <>\n <SelectionIndicator className={selectionIndicatorStyles(renderProps)} />\n <ListIcon aria-hidden=\"true\" />\n <span>{intl.formatMessage({ id: 'nav.list' })}</span>\n </>\n )}\n </ToggleButton>\n </ToggleButtonGroup>\n );\n}\n","import {\n Select as AriaSelect,\n SelectProps,\n Label,\n Button,\n SelectValue,\n Popover,\n ListBox,\n ListBoxItem\n} from 'react-aria-components';\nimport { focusRing, style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport ChevronDown from '@react-spectrum/s2/icons/ChevronDown';\n\nconst selectContainerStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 4,\n minWidth: 200\n});\n\nconst labelStyles = style({\n font: 'body',\n color: '--plum-700',\n});\n\nconst buttonStyles = style({\n ...focusRing(),\n paddingX: 12,\n paddingY: 8,\n backgroundColor: {\n default: '--yogurt-100',\n isHovered: '--yogurt-200',\n isFocusVisible: '--yogurt-200',\n isPressed: '--yogurt-300'\n },\n borderRadius: 'lg',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--yogurt-400',\n font: 'body',\n color: {\n default: '--plum-700',\n isHovered: '--plum-800',\n isFocused: '--plum-800',\n isPressed: '--plum-900',\n },\n cursor: 'pointer',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: 8,\n minWidth: 200\n});\n\nconst popoverStyles = style({\n backgroundColor: '--yogurt-50',\n borderRadius: 'lg',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--yogurt-400',\n boxShadow: 'elevated',\n marginTop: 4,\n maxHeight: 300,\n overflow: 'auto',\n isolation: 'isolate',\n});\n\nconst listBoxStyles = style({\n padding: 4,\n outline: 'none'\n});\n\nconst listBoxItemStyles = style({\n font: 'body',\n paddingX: 8,\n paddingY: 4,\n borderRadius: 'sm',\n cursor: {\n default: 'pointer',\n isDisabled: 'not-allowed'\n },\n backgroundColor: {\n default: 'transparent',\n isHovered: '--yogurt-200',\n isSelected: '--plum-200',\n isFocused: '--yogurt-200',\n isDisabled: '--yogurt-100'\n },\n color: {\n default: '--plum-700',\n isHovered: '--plum-800',\n isFocused: '--plum-800',\n isPressed: '--plum-900',\n isSelected: '--plum-700',\n isDisabled: 'gray-400'\n },\n outline: 'none'\n});\n\nexport interface SelectOption {\n id: string;\n label: string;\n value: string | null;\n isDisabled?: boolean;\n}\n\ninterface CustomSelectProps extends Omit<SelectProps<SelectOption>, 'children'> {\n label: string;\n options: SelectOption[];\n placeholder?: string;\n}\n\n/**\n * Reusable Select component following RAC pattern\n * Label is inside the Select wrapper for proper a11y\n */\nexport function Select({ label, options, placeholder, ...props }: CustomSelectProps) {\n return (\n <AriaSelect {...props} className={selectContainerStyles}>\n <Label className={labelStyles}>{label}</Label>\n <Button className={buttonStyles}>\n <SelectValue />\n <ChevronDown aria-hidden=\"true\" />\n </Button>\n <Popover className={popoverStyles}>\n <ListBox className={listBoxStyles} items={options}>\n {(item) => (\n <ListBoxItem\n key={item.id}\n id={item.id}\n textValue={item.label}\n isDisabled={item.isDisabled}\n className={listBoxItemStyles}\n >\n {item.label}\n </ListBoxItem>\n )}\n </ListBox>\n </Popover>\n </AriaSelect>\n );\n}\n","import { ReactNode } from 'react';\nimport { style } from '@react-spectrum/s2/style' with { type: 'macro' };\n\nexport interface StatCardProps {\n title: string;\n value: ReactNode;\n description?: string;\n}\n\nconst cardStyles = style({\n backgroundColor: 'layer-1',\n borderRadius: 'xl',\n padding: 24,\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--plum-200',\n display: 'flex',\n flexDirection: 'column',\n gap: 12,\n alignItems: 'center',\n textAlign: 'center'\n});\n\nconst cardTitleStyles = style({\n font: 'title-lg',\n color: '--plum-700',\n margin: 0\n});\n\nconst cardValueStyles = style({\n font: 'heading-2xl',\n color: '--plum-700',\n});\n\nconst cardDescStyles = style({\n font: 'body',\n color: '--plum-700',\n margin: 0\n});\n\n/**\n * StatCard component for displaying dashboard statistics\n * \n * @example\n * <StatCard \n * title=\"Total Sources\"\n * value={42}\n * description=\"Active scraping sources\"\n * />\n * \n * @example\n * // With icon as value\n * <StatCard \n * title=\"System Status\"\n * value={<CheckmarkCircle />}\n * description=\"All systems operational\"\n * />\n */\nexport function StatCard({ title, value, description }: StatCardProps) {\n return (\n <div className={cardStyles}>\n <h3 className={cardTitleStyles}>{title}</h3>\n <div className={cardValueStyles}>{value}</div>\n {description && <p className={cardDescStyles}>{description}</p>}\n </div>\n );\n}\n","import { Switch as AriaSwitch, SwitchProps as AriaSwitchProps } from 'react-aria-components';\nimport { focusRing, style } from '@react-spectrum/s2/style' with { type: 'macro' };\n\n// For props: https://react-spectrum.adobe.com/react-aria/Switch.html#props\n// For styling: https://react-spectrum.adobe.com/react-aria/Switch.html#styling\n\nconst switchContainerStyles = style({\n ...focusRing(),\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n paddingEnd: 2,\n cursor: 'pointer',\n font: 'ui-lg',\n color: {\n default: '--plum-700',\n isHovered: '--plum-800',\n isFocusVisible: '--plum-800',\n isPressed: '--plum-900'\n },\n borderRadius: 'full'\n});\n\nconst switchTrackStyles = style({\n width: 44,\n height: 24,\n borderRadius: 'full',\n padding: 2,\n display: 'flex',\n alignItems: 'center',\n backgroundColor: {\n default: '--yogurt-400',\n isSelected: '--plum-500',\n isDisabled: 'disabled'\n },\n borderStyle: {\n forcedColors: 'solid'\n },\n borderColor: {\n forcedColors: 'ButtonBorder'\n },\n borderWidth: {\n forcedColors: 1\n }\n});\n\nconst switchThumbStyles = style({\n width: 20,\n height: 20,\n borderRadius: 'full',\n backgroundColor: {\n default: 'white',\n forcedColors: 'ButtonText'\n },\n transform: {\n default: 'translateX(0)',\n isSelected: 'translateX(22px)'\n }\n});\n\nexport interface SwitchProps extends Omit<AriaSwitchProps, 'children'> {\n children?: React.ReactNode;\n}\n\nexport const Switch = ({ children, ...props }: SwitchProps) => {\n return (\n <AriaSwitch\n {...props}\n className={renderProps => switchContainerStyles(renderProps)}\n >\n {renderProps => (\n <>\n <div className={switchTrackStyles(renderProps)}>\n <div className={switchThumbStyles(renderProps)} />\n </div>\n {children}\n </>\n )}\n </AriaSwitch>\n );\n};","import { focusRing, style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { Tabs as AriaTabs, TabList as AriaTabList, Tab as AriaTab, TabPanel as AriaTabPanel } from 'react-aria-components';\nimport type { TabListProps, TabPanelProps, TabProps, TabsProps } from 'react-aria-components';\n\nconst tabsStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 0\n});\n\nconst tabListStyles = style({\n display: 'flex',\n gap: 4,\n borderBottomWidth: 2,\n borderBottomStyle: 'solid',\n borderBottomColor: '--plum-200',\n marginBottom: 24,\n overflowX: 'auto',\n overflowY: 'hidden',\n WebkitOverflowScrolling: 'touch'\n});\n\nconst tabStyles = style({\n ...focusRing(),\n paddingX: 20,\n paddingY: 12,\n font: 'body',\n fontWeight: {\n isSelected: 'bold'\n },\n color: {\n default: '--plum-700',\n isSelected: '--plum-800',\n isHovered: '--plum-800',\n isDisabled: '--plum-300'\n },\n backgroundColor: {\n default: 'transparent',\n isHovered: '--plum-50',\n isSelected: '--plum-100'\n },\n borderWidth: 0,\n borderBottomWidth: 2,\n borderStyle: 'solid',\n borderColor: {\n default: 'transparent',\n isSelected: '--plum-700',\n isHovered: '--plum-300'\n },\n cursor: {\n default: 'pointer',\n isDisabled: 'not-allowed'\n },\n transitionProperty: 'all',\n transitionDuration: '200ms'\n});\n\nconst tabPanelStyles = style({\n paddingY: 0,\n outlineStyle: 'none'\n});\n\nexport function Tabs({ children, ...props }: TabsProps) {\n return (\n <AriaTabs className={tabsStyles} {...props}>\n {children}\n </AriaTabs>\n );\n}\n\nexport function TabList<T extends object>({ children, ...props }: TabListProps<T>) {\n return (\n <AriaTabList className={tabListStyles} {...props}>\n {children}\n </AriaTabList>\n );\n}\n\nexport function Tab({ children, ...props }: TabProps) {\n return (\n <AriaTab className={tabStyles} {...props}>\n {children}\n </AriaTab>\n );\n}\n\nexport function TabPanel({ children, ...props }: TabPanelProps) {\n return (\n <AriaTabPanel className={tabPanelStyles} {...props}>\n {children}\n </AriaTabPanel>\n );\n}\n","import {\n TextArea as AriaTextArea,\n TextField as AriaTextField,\n Label,\n} from 'react-aria-components';\nimport { style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { textFieldErrorStyles, textFieldLabelStyles, type TextFieldProps, textFieldStyles } from './TextField';\nimport { textFieldInputStyles } from '../utils/styleUtils' with { type: 'macro' };\nimport { useRef, useEffect } from 'react';\nimport { safeWindow } from '../utils/browser';\n\nexport interface TextAreaProps extends Omit<TextFieldProps, 'type'> { }\n\nconst textareaStyles = style({\n ...textFieldInputStyles(),\n resize: 'none',\n minHeight: 75, // ~3 lines\n maxHeight: '75vh',\n overflowY: 'auto',\n});\n\nexport function TextArea({ label, error, placeholder, value, ...props }: TextAreaProps) {\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n\n // Auto-grow on value change\n useEffect(() => {\n const textarea = textareaRef.current;\n if (!textarea) return;\n\n // Reset height to recalculate\n textarea.style.height = '75px';\n\n // Set to scrollHeight (content height)\n const newHeight = Math.min(textarea.scrollHeight, safeWindow.innerHeight * 0.75 || 64);\n textarea.style.height = `${newHeight}px`;\n }, [value]);\n\n return (\n <AriaTextField className={textFieldStyles} {...props} value={value}>\n <Label className={textFieldLabelStyles}>{label}</Label>\n <AriaTextArea\n ref={textareaRef}\n className={textareaStyles}\n placeholder={placeholder}\n />\n {error && <div className={textFieldErrorStyles}>{error}</div>}\n </AriaTextField>\n );\n}\n","/* Checkbox styles for svg checked state */\n\n.checkbox-checkmark {\n width: 70%;\n height: 70%;\n fill: none;\n stroke: white;\n stroke-width: 2;\n stroke-linecap: round;\n stroke-linejoin: round;\n transition: stroke-dashoffset 150ms;\n}\n\n.checkbox-checkmark-path {\n stroke-dasharray: 22;\n stroke-dashoffset: 66;\n transition: stroke-dashoffset 150ms;\n}\n\n.checkbox-checkmark-path.checkbox-checkmark-selected {\n stroke-dashoffset: 44;\n}","/* Progress bar animations and transitions */\n.progress-fill {\n transition: width 200ms ease-in-out;\n}\n\n.progress-fill-indeterminate {\n animation: progress-indeterminate 1.5s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n}\n\n@keyframes progress-indeterminate {\n 0% {\n transform: translateX(-100%);\n }\n\n 100% {\n transform: translateX(350%);\n }\n}","/**\n * HealthCheck Component - Animations Only\n * All other styles are in S2 style macros\n */\n\n/* Chevron icon rotation on expansion */\n.disclosure-chevron[aria-expanded=\"true\"] svg {\n transform: rotate(90deg);\n}\n\n.disclosure-chevron svg {\n transition: transform 200ms;\n}\n\n@keyframes slideDown {\n from {\n opacity: 0;\n transform: translateY(-10px);\n }\n\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n@keyframes slideUp {\n from {\n opacity: 1;\n transform: translateY(0);\n }\n\n to {\n opacity: 0;\n transform: translateY(-10px);\n }\n}"],"names":[],"version":3,"file":"module.css.map"}
|
|
1
|
+
{"mappings":"ACMsB;EAAA;;;;EAAA;;;;EAUF;;;;EAAA;;;;EAAA;;;;EAiBE;;;;EAAA;;;;EAcI;;;;ECjCL;;;;;;EAAA;;;;EAAA;;;;EAAA;;;;;EAAA;;;;;EAAA;;;;EAAA;;;;;EAAA;;;;EAAA;;;;EC2CI;;;;EAsDE;;;;;EAeJ;;;;EAAA;;;;ECxHC;;;;EAAA;;;;EAOK;;;;ECkCT;;;;EA6BF;;;;;EAkBC;;;;;EAAA;;;;EAiBQ;;;;EAQF;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAaG;;;;ECxHL;;;;EAiBL;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAuCC;;;;EAAA;;;;EClDC;;;;EAcY;;;;EAAA;;;;ECFL;;;;EAAA;;;;EAAA;;;;ECrBP;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;ECoCO;;;;EAAA;;;;ECvCK;;;;EAkBZ;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EA6BC;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAqBC;;;;EA6CI;;;;EAKN;;;;EC5Gc;;;;EAAA;;;;EAWF;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EA8BF;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;ECtCH;;;;EC0BL;;;;EClBE;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;ECRJ;;;;EAAA;;;;EAAA;;;;EAaA;;;;EAqBM;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAsBE;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAWR;;;;EC5DM;;;;EAAA;;;;EAiBN;;;;EAeM;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;ECpCC;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAgCM;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;;;EC7BZ;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;;EAAA;;;;EAAA;;;;EAAA;;;;EA6BC;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAkBI;;;;EAAA;;;;EAAA;;;;EAAA;;;;ECzDP;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAcK;;;;EAMA;;;;EAWE;;;;EAAA;;;;EAAA;;;;EAOJ;;;;EAAA;;;;EAKI;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;ECjDJ;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAWD;;;;EAKC;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;ECnBQ;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAiBJ;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAuBA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EC1CP;;;;EAAA;;;;EAAA;;;;EAMG;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAYJ;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAmCK;;;;EAAA;;;;EAAA;;;;EC5CA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;;AvBGH;EeGA;;;;EAAA;;;;EAAA;;;;EKME;;;;EAAA;;;;ECnBQ;;;;ECgBZ;;;;EAAA;;;;EAAA;;;;ECTK;;;;EAAA;;;;;AvBGH;EmBaI;;;;EAAA;;;;EAAA;;;;ECJF;;;;EAAA;;;;EAAA;;;;ECnBQ;;;;ECgBZ;;;;;AtBNE;EAAA;IAAA;;;;;;AAAA;EAAA;IAAA;;;;;;AAiBE;EmBJE;;;;EAAA;;;;EAAA;;;;ECJF;;;;EAAA;;;;EAAA;;;;ECnBQ;;;;;ArB2BR;EoBRA;;;;EAAA;;;;EAAA;;;;ECnBQ;;;;;ArB2BR;EoBRA;;;;EAAA;;;;EAAA;;;;ECnBQ;;;;;ArB2BR;EAAA;;;;;AAAA;EAAA;;;;;AAAA;EAAA;;;;;ACnBD;EAAA;IAAA;;;;IgBwCY;;;;IAAA;;;;II/BP;;;;IAAA;;;;IAAA;;;;IAAA;;;;IAAA;;;;IAAA;;;;IAuBA;;;;;;AG5C1B;;;;;;;;;;;AAWA;;;;;;AAMA;;;;AClBA;;;;AAIA;;;;AAIA;;;;;;;;;;ACHA;;;;AAIA;;;;AAIA;;;;;;;;;;;;AAYA;;;;;;;;;;;;AV6FwF;;AAAA;EAAA;IAAA","sources":["27d1e9fc0b161a21","packages/shared/src/components/AlertDialog.tsx","packages/shared/src/components/Button.tsx","packages/shared/src/components/Autocomplete.tsx","packages/shared/src/components/AutocompleteTable.tsx","packages/shared/src/components/Table.tsx","packages/shared/src/components/Checkbox.tsx","packages/shared/src/components/ProgressBar.tsx","packages/shared/src/components/AvatarButton.tsx","packages/shared/src/components/Badge.tsx","packages/shared/src/components/CloseButton.tsx","packages/shared/src/components/ComboBox.tsx","packages/shared/src/components/DisclosureGroup.tsx","packages/shared/src/components/LabeledValue.tsx","packages/shared/src/components/Menu.tsx","packages/shared/src/components/PasswordStrength.tsx","packages/shared/src/components/RadioGroup.tsx","packages/shared/src/components/SearchField.tsx","packages/shared/src/components/SegmentedControl.tsx","packages/shared/src/components/Select.tsx","packages/shared/src/components/StatCard.tsx","packages/shared/src/components/Popover.tsx","packages/shared/src/components/Switch.tsx","packages/shared/src/components/Tabs.tsx","packages/shared/src/components/TextArea.tsx","packages/shared/src/components/Checkbox.css","packages/shared/src/components/ProgressBar.css","packages/shared/src/components/DisclosureGroup.css"],"sourcesContent":["@import \"9f47f68ba38c295a\";\n@import \"78c590a309b0e6a4\";\n@import \"290522a232eb340f\";\n@import \"9d168f2695c440b5\";\n@import \"cc4d3a5fac51363d\";\n@import \"8ee7d6e801b40a1c\";\n@import \"6ffa14505a299e4e\";\n@import \"cfef988437953ae9\";\n@import \"93c21ad3845ca137\";\n@import \"83429286ceb3be13\";\n@import \"03dd1cfb5962a09d\";\n@import \"620de91e79ad0a16\";\n@import \"d274ac5a14987bf9\";\n@import \"416838a95541a7c4\";\n@import \"53f941c4c340fa03\";\n@import \"82593e9a11ceb790\";\n@import \"4312b6d61177a486\";\n@import \"7f7804fa3c921faf\";\n@import \"29e82be3069c1c2c\";\n@import \"26b1a04880d0efb1\";\n@import \"f315d14c1596594f\";\n@import \"245a17a9caf6065d\";\n@import \"03b25ce5fcd826d5\";\n@import \"f64b1199c5ac10c2\";\n@import \"d00995e4bf421650\";\n@import \"418c33e40e8fa307\";\n@import \"77a381a4ad5e4deb\";\n@import \"79a47b3e11bde76b\";\n@import \"4c61b37e83b6cbaf\";\n@import \"7d6c90f6382f216d\";\n@import \"d5a82419c43c0258\";\n@import \"4c5c6d90d8125894\";\n@import \"d7830e0fdb5f5a4f\";\n@import \"3cb9bcfb6f0038f8\";\n@import \"8d4f769c29e4ee31\";\n@import \"e8338bb3ae3fb1c4\";\n@import \"541891615c093f21\";\n@import \"3415552572bd8d79\";\n@import \"30bf95091f7ade51\";\n@import \"f03216825356c24c\";\n@import \"a035a1ff61c82329\";\n@import \"f0b45c7a5a4ee6b4\";\n@import \"00d20b13f70db88f\";\n@import \"99348462226d32b0\";\n@import \"f133ffb8107f1499\";\n@import \"060090694d7abb5e\";\n@import \"02d1fda45f088142\";\n@import \"0be6be6f1b4df772\";\n@import \"17a5281dc3bf68c3\";\n@import \"5cbf8c2ecc386b8a\";\n@import \"d000b87b862d640a\";\n@import \"99cf12a0bc5155d4\";\n@import \"3dea62b5608bc4c7\";\n@import \"00dffe07cf30c480\";\n@import \"daa2dcfcc6aba2ec\";\n@import \"22d59f7061eee5c3\";\n@import \"c730c2c683575e83\";\n@import \"4c4abb4dd24ed78d\";\n@import \"55d42732ed05bd7c\";\n@import \"7e7d0afbf7be7aaf\";\n@import \"fe26295011b80084\";\n@import \"98f373d7d990b86e\";\n@import \"44556bb5ffa970fa\";\n@import \"ffb67edd6d59f4d0\";\n@import \"3f8a3d39101fb8de\";\n@import \"da5d3570a6be5108\";\n@import \"a81b95f6ba098d94\";\n@import \"6e45401afedcc213\";\n@import \"823e68b0820e33b0\";\n@import \"6098b6422a6b5107\";\n@import \"e704ae4b0ea5b6a4\";\n@import \"18ca83a63b402953\";\n@import \"9934fabeca5dc14b\";\n@import \"08e05744d70ceb2e\";\n@import \"51f566c4383bc873\";\n@import \"912ea4a8b470ca7f\";\n@import \"6240e539776dd470\";\n@import \"4728afee3b53ed4d\";\n@import \"0239ad58c0477121\";\n@import \"be422f30337666a7\";\n@import \"c390e1b28b674d46\";\n@import \"00f37031e59ae452\";\n@import \"b2e8234656a0acab\";\n@import \"1c127e7148e69ee6\";\n@import \"31b2213af713491b\";\n@import \"c313bd17239b6dfb\";\n@import \"cc3cacc59937fd97\";\n@import \"1b5034e53b6b6e9d\";\n@import \"f43b41886b283acc\";\n@import \"b30e558e03268eb5\";\n@import \"16fa3c2822ce8fa6\";\n@import \"6cc8a6d86b48a0f8\";\n@import \"2d5249b468c9ed81\";\n@import \"d69789ae2771f37d\";\n@import \"a750f611c47db198\";\n@import \"2243763201ee6943\";\n@import \"2c30d3e83234d14f\";\n@import \"e9c2f06a78f0fa1b\";\n@import \"1629b57b41de7f33\";\n@import \"d38717759287a5cb\";\n@import \"5046bf9ea1029bc4\";\n@import \"a33499d27b9fcd76\";\n@import \"a31990ea1642f273\";\n@import \"d1b031cf4bda7480\";\n@import \"e81d7eb3988d206b\";\n@import \"6f65c616b892c898\";\n@import \"adda47912e5baae7\";\n@import \"d9ef614c1a51f3a0\";\n@import \"b016d2cb291dd280\";\n@import \"c119c6a8a6f154f5\";\n@import \"a3a45a76d021f061\";\n@import \"cc9d46036b811441\";\n@import \"3d06ffe03e3c9be8\";\n@import \"515729e51489b225\";\n@import \"36dbb656eeb11098\";\n@import \"d3b3b5097317263d\";\n@import \"c7bfffb20d48ffa6\";\n@import \"75d222754bbb0b8f\";\n@import \"b79287f289978582\";\n@import \"55cf679aaecacd02\";\n@import \"a4a1c07b3ebdf826\";\n@import \"5a19908ab4aad173\";\n","import { Button } from './Button';\nimport { Dialog, Heading, Modal, ModalOverlay } from 'react-aria-components';\nimport { style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { TextField } from './TextField';\nimport { useState } from 'react';\n\nconst overlayStyles = style({\n position: 'fixed',\n inset: 0,\n backgroundColor: 'transparent-black-500',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n isolation: 'isolate',\n});\n\nconst modalStyles = style({\n backgroundColor: 'layer-1',\n borderRadius: 'xl',\n boxShadow: 'elevated',\n padding: 24,\n width: {\n default: 'calc(100vw - 32px)',\n sm: 400,\n md: 500\n },\n maxWidth: 500,\n transform: {\n entering: 'scale(0.95)',\n default: 'scale(1)'\n },\n});\n\nconst headingStyles = style({\n font: 'heading-xl',\n color: 'heading',\n margin: 0,\n marginBottom: 12\n});\n\nconst messageStyles = style({\n font: 'body',\n color: 'neutral',\n margin: 0,\n marginBottom: 24\n});\n\nconst buttonGroupStyles = style({\n display: 'flex',\n gap: 12,\n justifyContent: 'end'\n});\n\nexport interface AlertDialogProps {\n isOpen: boolean;\n onClose: () => void;\n title: string;\n message: string;\n variant?: 'danger' | 'warning' | 'info' | 'success';\n confirmLabel?: string;\n cancelLabel?: string;\n onConfirm: () => void | Promise<void>;\n showCancel?: boolean;\n children?: React.ReactNode; // Optional custom content (e.g., Switch, Checkbox)\n}\n\nexport function AlertDialog({\n isOpen,\n onClose,\n title,\n message,\n variant = 'info',\n confirmLabel = 'OK',\n cancelLabel = 'Cancel',\n onConfirm,\n showCancel = true,\n children\n}: AlertDialogProps) {\n const [isProcessing, setIsProcessing] = useState(false);\n\n const handleConfirm = async () => {\n setIsProcessing(true);\n try {\n await onConfirm();\n onClose();\n } finally {\n setIsProcessing(false);\n }\n };\n\n // Map variant to button variant\n const buttonVariant = variant === 'danger' ? 'danger' :\n variant === 'success' ? 'primary' :\n 'primary';\n\n return (\n <ModalOverlay isOpen={isOpen} onOpenChange={(open) => !open && onClose()} className={overlayStyles}>\n <Modal className={modalStyles}>\n <Dialog>\n {({ close }) => (\n <>\n <Heading slot=\"title\" className={headingStyles}>\n {title}\n </Heading>\n <p className={messageStyles}>{message}</p>\n {children && (\n <div className={style({ marginBottom: 24 })}>\n {children}\n </div>\n )}\n <div className={buttonGroupStyles}>\n {showCancel && (\n <Button\n variant=\"secondary\"\n size=\"M\"\n onPress={close}\n isDisabled={isProcessing}\n >\n {cancelLabel}\n </Button>\n )}\n <Button\n variant={buttonVariant}\n size=\"M\"\n onPress={handleConfirm}\n isDisabled={isProcessing}\n >\n {isProcessing ? 'Processing...' : confirmLabel}\n </Button>\n </div>\n </>\n )}\n </Dialog>\n </Modal>\n </ModalOverlay>\n );\n}\n\nexport interface PromptDialogProps {\n isOpen: boolean;\n onClose: () => void;\n title: string;\n message: string;\n inputLabel: string;\n inputPlaceholder?: string;\n confirmLabel?: string;\n cancelLabel?: string;\n onConfirm: (value: string) => void | Promise<void>;\n validator?: (value: string) => string | null;\n}\n\nexport function PromptDialog({\n isOpen,\n onClose,\n title,\n message,\n inputLabel,\n inputPlaceholder = '',\n confirmLabel = 'OK',\n cancelLabel = 'Cancel',\n onConfirm,\n validator\n}: PromptDialogProps) {\n const [value, setValue] = useState('');\n const [error, setError] = useState<string | null>(null);\n const [isProcessing, setIsProcessing] = useState(false);\n\n const handleConfirm = async () => {\n // Validate\n if (validator) {\n const validationError = validator(value);\n if (validationError) {\n setError(validationError);\n return;\n }\n }\n\n setIsProcessing(true);\n try {\n await onConfirm(value);\n setValue('');\n setError(null);\n onClose();\n } finally {\n setIsProcessing(false);\n }\n };\n\n const handleClose = () => {\n setValue('');\n setError(null);\n onClose();\n };\n\n return (\n <ModalOverlay isOpen={isOpen} onOpenChange={(open) => !open && handleClose()} className={overlayStyles}>\n <Modal className={modalStyles}>\n <Dialog>\n {({ close }) => (\n <>\n <Heading slot=\"title\" className={headingStyles}>\n {title}\n </Heading>\n <p className={messageStyles}>{message}</p>\n <div className={style({ marginBottom: 24 })}>\n <TextField\n label={inputLabel}\n value={value}\n onChange={(newValue) => {\n setValue(newValue);\n setError(null);\n }}\n placeholder={inputPlaceholder}\n error={error || undefined}\n autoFocus\n />\n </div>\n <div className={buttonGroupStyles}>\n <Button\n variant=\"secondary\"\n size=\"M\"\n onPress={() => {\n setValue('');\n setError(null);\n close();\n }}\n isDisabled={isProcessing}\n >\n {cancelLabel}\n </Button>\n <Button\n variant=\"primary\"\n size=\"M\"\n onPress={handleConfirm}\n isDisabled={isProcessing}\n >\n {isProcessing ? 'Processing...' : confirmLabel}\n </Button>\n </div>\n </>\n )}\n </Dialog>\n </Modal>\n </ModalOverlay>\n );\n}\n","import { Button as AriaButton, ButtonProps as AriaButtonProps } from 'react-aria-components';\nimport { focusRing, style } from '@react-spectrum/s2/style' with { type: 'macro' };\n\n// For props: https://react-spectrum.adobe.com/react-aria/Button.html#props\n// For styling: https://react-spectrum.adobe.com/react-aria/Button.html#styling\n// For conditional styling: https://react-spectrum.adobe.com/beta/s2/styling.html#conditional-styles\n\nexport interface ButtonProps extends AriaButtonProps {\n children?: React.ReactNode;\n variant?: 'primary' | 'secondary' | 'danger' | 'navigation';\n size?: 'S' | 'M' | 'L';\n}\n\n// Primary variant styles\nconst buttonStyles = style({\n ...focusRing(),\n font: {\n default: 'body',\n size: {\n S: 'body-sm',\n L: 'body-lg',// or `body` too?\n },\n },\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: {\n default: 8,\n size: {\n S: 4,\n },\n },\n paddingY: {\n default: 12,\n size: {\n S: 8,\n L: 16,\n },\n variant: {\n navigation: {\n default: 4\n }\n }\n },\n paddingStart: {\n default: 16,\n size: {\n S: 12,\n L: 24,\n },\n variant: {\n navigation: {\n default: 8\n }\n }\n },\n paddingEnd: {\n default: 16,\n size: {\n S: 12,\n L: 24,\n },\n variant: {\n navigation: 12\n }\n },\n width: {\n variant: {\n navigation: 'fit'\n }\n },\n color: {\n variant: {\n secondary: {\n default: '--plum-700',\n },\n danger: {\n default: 'white',\n },\n navigation: {\n default: '--plum-700',\n }\n },\n forcedColors: 'ButtonText',\n isDisabled: {\n default: 'gray-400',\n forcedColors: 'GrayText',\n },\n },\n backgroundColor: {\n default: '--plum-300',\n variant: {\n primary: {\n default: '--plum-300',\n isHovered: '--plum-400',\n isFocusVisibile: '--plum-400',\n isPressed: '--plum-400',\n },\n secondary: {\n default: '--yogurt-200',\n isHovered: '--yogurt-300',\n isFocusVisibile: '--yogurt-300',\n isPressed: '--yogurt-400',\n },\n navigation: {\n default: 'transparent',\n isHovered: '--yogurt-200',\n isFocusVisibile: '--yogurt-200',\n isPressed: '--yogurt-300',\n },\n danger: {\n default: '--berry-500',\n isHovered: '--berry-600',\n isFocusVisibile: '--berry-600',\n isPressed: '--berry-700',\n },\n },\n forcedColors: 'ButtonFace',\n isDisabled: {\n default: 'gray-100',\n isDisabled: 'GrayText',\n },\n },\n borderRadius: 'lg',\n borderStyle: {\n default: 'none',\n forcedColors: 'solid'\n },\n borderColor: {\n forcedColors: 'ButtonBorder'\n },\n borderWidth: {\n forcedColors: 1\n },\n cursor: {\n default: 'pointer',\n isDisabled: 'not-allowed'\n }\n});\n\nexport const Button = ({\n children,\n variant = 'primary',\n size = 'M',\n className,\n ...props\n}: ButtonProps) => {\n return (\n <AriaButton\n {...props}\n className={renderProps => `${className || ''} ` + buttonStyles({ ...renderProps, size, variant })}\n >\n {children}\n </AriaButton>\n );\n};\n","import { useState, useMemo } from 'react';\nimport {\n SearchField as AriaSearchField,\n Autocomplete as AriaAutocomplete,\n Input,\n Label,\n ListBox,\n ListBoxItem,\n useFilter,\n Popover\n} from 'react-aria-components';\nimport { focusRing, style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport Search from '@react-spectrum/s2/icons/Search';\nimport CheckmarkCircle from '@react-spectrum/s2/icons/CheckmarkCircle';\nimport type { Selection } from 'react-aria-components';\n\nconst containerStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 4,\n width: 'full',\n});\n\nconst labelStyles = style({\n font: 'body',\n color: '--plum-700',\n});\n\nconst searchFieldStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 4,\n});\n\nconst inputContainerStyles = style({\n position: 'relative',\n display: 'flex',\n alignItems: 'center',\n});\n\nconst inputStyles = style({\n ...focusRing(),\n width: 'full',\n padding: 8,\n paddingStart: 40,\n font: 'body',\n borderRadius: 'lg',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--yogurt-400',\n backgroundColor: {\n default: '--yogurt-100',\n isHovered: '--yogurt-200',\n isFocusVisible: '--yogurt-200',\n },\n});\n\nconst searchIconStyles = style({\n position: 'absolute',\n left: 12,\n top: '50%',\n transform: 'translateY(-50%)',\n pointerEvents: 'none',\n color: '--yogurt-600',\n});\n\nconst popoverStyles = style({\n backgroundColor: '--yogurt-50',\n borderRadius: 'lg',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--yogurt-400',\n boxShadow: 'elevated',\n marginTop: 4,\n maxHeight: 300,\n overflow: 'auto',\n isolation: 'isolate',\n // width: 'full',\n // minWidth: 300,\n});\n\nconst listBoxStyles = style({\n padding: 4,\n outline: 'none',\n});\n\nconst listBoxItemStyles = style({\n ...focusRing(),\n font: 'body',\n paddingX: 8,\n paddingY: 8,\n borderRadius: 'sm',\n cursor: 'pointer',\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n backgroundColor: {\n default: 'transparent',\n isHovered: '--yogurt-200',\n isFocused: '--yogurt-200',\n isPressed: '--yogurt-300',\n },\n color: {\n default: '--plum-700',\n isHovered: '--plum-800',\n isFocused: '--plum-800',\n isPressed: '--plum-900',\n },\n outline: 'none',\n});\n\nconst selectedItemStyles = style({\n backgroundColor: {\n default: '--plum-100',\n isHovered: '--plum-200',\n isFocused: '--plum-200',\n },\n color: '--plum-900',\n});\n\nconst checkboxStyles = style({\n width: 16,\n height: 16,\n flexShrink: 0,\n});\n\nconst itemTextStyles = style({\n flex: 1,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n});\n\nconst errorStyles = style({\n font: 'body-sm',\n color: '--berry-600',\n});\n\nexport interface AutocompleteItem {\n id: string | number;\n name: string;\n [key: string]: any;\n}\n\nexport interface AutocompleteProps<T extends AutocompleteItem> {\n items: T[];\n selectedKeys?: Selection;\n onSelectionChange?: (keys: Selection) => void;\n selectionMode?: 'none' | 'single' | 'multiple';\n label: string;\n placeholder?: string;\n ariaLabel?: string;\n error?: string;\n getItemText?: (item: T) => string;\n}\n\n/**\n * Autocomplete component using React Aria SearchField + ListBox\n * \n * Features:\n * - SearchField for filtering items\n * - Optional checkboxes for multi-selection\n * - Shows both selected and unselected items when filtered\n * - Selected items highlighted with background color\n * - Keyboard navigation (arrow keys, enter, escape)\n * - Accessible (ARIA labels, focus management)\n * \n * @example\n * ```tsx\n * // Single select\n * <Autocomplete\n * items={sources}\n * selectedKeys={selectedKey}\n * onSelectionChange={setSelectedKey}\n * selectionMode=\"single\"\n * label=\"Select Source\"\n * placeholder=\"Search sources...\"\n * />\n * \n * // Multi select\n * <Autocomplete\n * items={sources}\n * selectedKeys={selectedKeys}\n * onSelectionChange={setSelectedKeys}\n * selectionMode=\"multiple\"\n * label=\"Select Sources\"\n * placeholder=\"Search sources...\"\n * />\n * \n * // No selection (just filtering)\n * <Autocomplete\n * items={sources}\n * selectionMode=\"none\"\n * label=\"Search Sources\"\n * placeholder=\"Search sources...\"\n * />\n * ```\n */\nexport function Autocomplete<T extends AutocompleteItem>({\n items,\n selectedKeys,\n onSelectionChange,\n selectionMode = 'none',\n label,\n placeholder,\n ariaLabel,\n error,\n getItemText = (item) => item.name,\n}: AutocompleteProps<T>) {\n const [inputValue, setInputValue] = useState('');\n const { contains } = useFilter({ sensitivity: 'base' });\n\n // Filter items based on input value\n const filteredItems = useMemo(() => {\n if (!inputValue) return items;\n const searchLower = inputValue.toLowerCase();\n return items.filter((item) =>\n getItemText(item).toLowerCase().includes(searchLower)\n );\n }, [items, inputValue, getItemText]);\n\n // Sort to show selected items first when in selection mode\n const sortedItems = useMemo(() => {\n if (selectionMode === 'none' || !selectedKeys) return filteredItems;\n\n return [...filteredItems].sort((a, b) => {\n const aSelected = selectedKeys === 'all' || selectedKeys.has(String(a.id));\n const bSelected = selectedKeys === 'all' || selectedKeys.has(String(b.id));\n if (aSelected && !bSelected) return -1;\n if (!aSelected && bSelected) return 1;\n return 0;\n });\n }, [filteredItems, selectedKeys, selectionMode]);\n\n const showCheckboxes = selectionMode !== 'none';\n\n return (\n <div className={containerStyles}>\n <AriaAutocomplete\n filter={contains}\n inputValue={inputValue}\n onInputChange={setInputValue}\n >\n <AriaSearchField\n className={searchFieldStyles}\n aria-label={ariaLabel || label}\n >\n <Label className={labelStyles}>{label}</Label>\n <div className={inputContainerStyles}>\n <div className={searchIconStyles}><Search /></div>\n <Input\n className={inputStyles}\n placeholder={placeholder}\n />\n </div>\n </AriaSearchField>\n\n <ListBox\n className={listBoxStyles}\n items={sortedItems}\n selectionMode={selectionMode}\n selectedKeys={selectedKeys}\n onSelectionChange={onSelectionChange}\n aria-label={ariaLabel || `${label} options`}\n >\n {(item) => {\n const isSelected = selectedKeys === 'all' || (selectedKeys && selectedKeys.has(String(item.id)));\n return (\n <ListBoxItem\n key={item.id}\n id={String(item.id)}\n textValue={getItemText(item)}\n className={(renderProps) =>\n `${listBoxItemStyles(renderProps)} ${isSelected ? selectedItemStyles(renderProps) : ''}`\n }\n >\n {showCheckboxes && (\n <div className={checkboxStyles}>\n {isSelected && <CheckmarkCircle />}\n </div>\n )}\n <span className={itemTextStyles}>{getItemText(item)}</span>\n </ListBoxItem>\n );\n }}\n </ListBox>\n\n {error && <div className={errorStyles}>{error}</div>}\n </AriaAutocomplete>\n </div>\n );\n}\n","import { useMemo } from 'react';\nimport type { Selection } from 'react-aria-components';\nimport { Autocomplete, type AutocompleteItem } from './Autocomplete';\nimport { Table, type ColumnDef } from './Table';\nimport { style } from '@react-spectrum/s2/style' with { type: 'macro' };\n\nconst containerStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 16,\n width: 'full',\n});\n\nconst tableContainerStyles = style({\n maxHeight: 384,\n overflow: 'auto',\n});\n\nexport interface AutocompleteTableProps<T extends AutocompleteItem> {\n // Autocomplete props\n allItems: T[];\n selectedKeys: Selection;\n onSelectionChange: (keys: Selection) => void;\n selectionMode?: 'single' | 'multiple';\n searchLabel: string;\n searchPlaceholder?: string;\n getItemText?: (item: T) => string;\n\n // Table props\n columns: ColumnDef<T>[];\n tableLabel: string;\n}\n\n/**\n * Composite component combining Autocomplete and Table\n * Useful for search-and-select workflows where selected items are displayed in a table\n * \n * Features:\n * - Autocomplete search field at top\n * - Table below showing selected items\n * - Filters all items in autocomplete\n * - Shows only selected items in table\n * - Scrollable table with max height of 384px\n * \n * @example\n * ```tsx\n * <AutocompleteTable\n * allItems={allSources}\n * selectedKeys={selectedSourceIds}\n * onSelectionChange={setSelectedSourceIds}\n * selectionMode=\"multiple\"\n * searchLabel=\"Search Sources\"\n * searchPlaceholder=\"Search by name or URL...\"\n * columns={[\n * { key: 'name', label: 'Name', isRowHeader: true },\n * { key: 'domain', label: 'Domain' },\n * { key: 'actions', label: 'Actions', align: 'end' },\n * ]}\n * tableLabel=\"Selected Sources\"\n * />\n * ```\n */\nexport function AutocompleteTable<T extends AutocompleteItem>({\n allItems,\n selectedKeys,\n onSelectionChange,\n selectionMode = 'multiple',\n searchLabel,\n searchPlaceholder,\n getItemText,\n columns,\n tableLabel,\n}: AutocompleteTableProps<T>) {\n // Filter to show only selected items in table\n const selectedItems = useMemo(() => {\n if (selectedKeys === 'all') return allItems;\n if (!selectedKeys || selectedKeys.size === 0) return [];\n\n return allItems.filter(item => selectedKeys.has(String(item.id)));\n }, [allItems, selectedKeys]);\n\n return (\n <div className={containerStyles}>\n <Autocomplete\n items={allItems}\n selectedKeys={selectedKeys}\n onSelectionChange={onSelectionChange}\n selectionMode={selectionMode}\n label={searchLabel}\n placeholder={searchPlaceholder}\n getItemText={getItemText}\n />\n\n <div className={tableContainerStyles}>\n <Table\n columns={columns}\n data={selectedItems}\n ariaLabel={tableLabel}\n />\n </div>\n </div>\n );\n}\n","import {\n Cell,\n Column,\n Row,\n Table as AriaTable,\n TableBody,\n TableHeader,\n type Key,\n type SortDescriptor\n} from 'react-aria-components';\nimport { focusRing, style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { Checkbox } from './Checkbox';\nimport { ProgressBar } from './ProgressBar';\nimport { ReactNode, useRef, useEffect } from 'react';\n\nexport interface ColumnDef<T> {\n key: string;\n label: string;\n renderCell: (item: T) => ReactNode;\n width?: string;\n isRowHeader?: boolean;\n align?: 'start' | 'center' | 'end';\n}\n\nexport interface TableProps<T> {\n data: T[];\n columns: ColumnDef<T>[];\n onRowAction?: (key: Key) => void;\n ariaLabel: string;\n sortDescriptor?: SortDescriptor;\n onSortChange?: (descriptor: SortDescriptor) => void;\n selectionMode?: 'none' | 'single' | 'multiple';\n selectedKeys?: Set<Key>;\n onSelectionChange?: (keys: Set<Key>) => void;\n onLoadMore?: () => void;\n isLoadingMore?: boolean;\n}\n\nconst tableContainerStyles = style({\n backgroundColor: 'layer-1',\n borderRadius: 'xl',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--plum-200',\n overflow: 'auto'\n});\n\nconst tableStyles = style({\n width: 'full',\n borderCollapse: 'collapse'\n});\n\nconst tableHeaderStyles = style({\n backgroundColor: '--yogurt-200',\n borderBottomWidth: 1,\n borderBottomStyle: 'solid',\n borderBottomColor: '--plum-200'\n});\n\nconst columnHeaderStyles = style({\n padding: 16,\n textAlign: {\n default: 'start',\n align: {\n start: 'start',\n center: 'center',\n end: 'end',\n },\n },\n font: 'title-lg',\n color: '--plum-700',\n cursor: {\n allowsSorting: 'pointer'\n }\n});\n\nconst rowStyles = style({\n ...focusRing(),\n backgroundColor: {\n default: 'transparent',\n isHovered: '--yogurt-100',\n isSelected: '--plum-100',\n isFocusVisible: '--yogurt-100'\n },\n cursor: {\n default: 'default',\n onRowAction: 'pointer',\n selectionMode: {\n single: 'pointer',\n multiple: 'pointer'\n }\n }\n});\n\nconst cellStyles = style({\n padding: 16,\n textAlign: {\n default: 'start',\n align: {\n start: 'start',\n center: 'center',\n end: 'end',\n },\n },\n font: 'body',\n color: '--plum-700',\n borderBottomWidth: 1,\n borderBottomStyle: 'solid',\n borderBottomColor: '--plum-100'\n});\n\nconst checkboxCellStyles = style({\n padding: 16,\n width: 48,\n borderBottomWidth: 1,\n borderBottomStyle: 'solid',\n borderBottomColor: '--plum-100'\n});\n\nconst emptyStateStyles = style({\n padding: 48,\n textAlign: 'center',\n font: 'body',\n color: '--plum-600',\n});\n\nconst loadingStyles = style({\n padding: 24,\n display: 'flex',\n justifyContent: 'center'\n});\n\nconst scrollTriggerStyles = style({\n height: 1,\n pointerEvents: 'none'\n});\n\n/**\n * Reusable Table component using React Aria Components\n * \n * Features:\n * - Collection-based API for better performance\n * - Built-in virtualization support (via RAC)\n * - Keyboard navigation\n * - Screen reader support\n * - Optional sorting\n * - Optional row selection\n * - Optional row actions (clickable rows)\n * - Checkbox column for multiple selection\n * - Hover and selected states\n * \n * @example\n * ```tsx\n * const columns: ColumnDef<Run>[] = [\n * { key: 'id', label: 'ID', renderCell: (run) => `#${run.id}` },\n * { key: 'status', label: 'Status', renderCell: (run) => <Badge variant={run.status}>{run.status}</Badge> }\n * ];\n * \n * <Table\n * data={runs}\n * columns={columns}\n * ariaLabel=\"Scraper runs\"\n * selectionMode=\"multiple\"\n * selectedKeys={selectedIds}\n * onSelectionChange={setSelectedIds}\n * />\n * ```\n */\nexport function Table<T extends { id: number | string }>({\n data,\n columns,\n onRowAction,\n ariaLabel,\n sortDescriptor,\n onSortChange,\n selectionMode = 'none',\n selectedKeys,\n onSelectionChange,\n onLoadMore,\n isLoadingMore\n}: TableProps<T>) {\n const observerTarget = useRef<HTMLDivElement>(null);\n\n // Infinite scroll observer\n useEffect(() => {\n if (!onLoadMore) return;\n\n const observer = new IntersectionObserver(\n (entries) => {\n if (entries[0].isIntersecting && !isLoadingMore) {\n onLoadMore();\n }\n },\n { threshold: 0.1, rootMargin: '100px' }\n );\n\n if (observerTarget.current) {\n observer.observe(observerTarget.current);\n }\n\n return () => observer.disconnect();\n }, [onLoadMore, isLoadingMore]);\n\n return (\n <>\n <div className={tableContainerStyles}>\n <AriaTable\n aria-label={ariaLabel}\n selectionMode={selectionMode}\n selectedKeys={selectedKeys}\n onSelectionChange={onSelectionChange as any}\n onRowAction={onRowAction}\n sortDescriptor={sortDescriptor}\n onSortChange={onSortChange}\n className={tableStyles}\n >\n <TableHeader className={tableHeaderStyles}>\n {selectionMode === 'multiple' && (\n <Column className={columnHeaderStyles({ align: 'center', alignItems: 'center' })}>\n <Checkbox slot=\"selection\" size=\"S\" />\n </Column>\n )}\n {columns.map((column) => (\n <Column\n key={column.key}\n id={column.key}\n isRowHeader={column.isRowHeader}\n allowsSorting={!!onSortChange}\n className={columnHeaderStyles({ align: column.align })}\n >\n {column.label}\n </Column>\n ))}\n </TableHeader>\n <TableBody\n items={data}\n renderEmptyState={() => (\n <div className={emptyStateStyles}>\n No data available\n </div>\n )}\n >\n {(item) => (\n <Row\n key={item.id}\n id={item.id}\n className={(renderProps) => rowStyles({\n ...renderProps,\n onRowAction: !!onRowAction,\n selectionMode\n })}\n >\n {selectionMode === 'multiple' && (\n <Cell className={checkboxCellStyles}>\n <Checkbox slot=\"selection\" size=\"S\" />\n </Cell>\n )}\n {columns.map((column) => (\n <Cell key={column.key} className={cellStyles({ align: column.align })}>\n {column.renderCell(item)}\n </Cell>\n ))}\n </Row>\n )}\n </TableBody>\n </AriaTable>\n </div>\n\n {/* Infinite scroll trigger */}\n {onLoadMore && <div ref={observerTarget} className={scrollTriggerStyles} />}\n\n {/* Loading indicator */}\n {isLoadingMore && (\n <div className={loadingStyles}>\n <ProgressBar isIndeterminate aria-label=\"Loading more items\" />\n </div>\n )}\n </>\n );\n}\n\n","import { Checkbox as AriaCheckbox, type CheckboxProps as AriaCheckboxProps } from 'react-aria-components';\nimport { focusRing, style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport './Checkbox.css';\n\nexport interface CheckboxProps extends Omit<AriaCheckboxProps, 'className' | 'style'> {\n /**\n * The size of the Checkbox.\n * @default 'M'\n */\n size?: 'S' | 'M' | 'L';\n}\n\nconst checkboxStyles = style({\n display: 'flex',\n position: 'relative',\n alignItems: 'center',\n gap: 8,\n font: 'body',\n color: {\n default: '--plum-700',\n isDisabled: 'disabled'\n },\n cursor: {\n default: 'pointer',\n isDisabled: 'default'\n },\n disableTapHighlight: true\n});\n\nconst boxStyles = style({\n ...focusRing(),\n size: {\n default: 20,\n size: {\n S: 16,\n M: 20,\n L: 24\n }\n },\n flexShrink: 0,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n borderWidth: 2,\n borderStyle: 'solid',\n borderRadius: 'sm',\n transition: 'default',\n backgroundColor: {\n default: 'layer-1',\n isSelected: {\n default: '--plum-600',\n isHovered: '--plum-700',\n isPressed: '--plum-800'\n },\n isDisabled: {\n default: 'gray-100',\n isSelected: 'gray-400'\n }\n },\n borderColor: {\n default: '--plum-400',\n isHovered: '--plum-500',\n isPressed: '--plum-600',\n isSelected: 'transparent',\n isDisabled: 'gray-300'\n }\n});\n\nconst dashStyles = style({\n width: '60%',\n height: 2,\n backgroundColor: 'white',\n borderRadius: 'sm'\n});\n\n/**\n * Checkbox component for selection within tables and forms.\n * Simplified version based on React Spectrum S2 Checkbox.\n * \n * @example\n * ```tsx\n * <Checkbox isSelected={selected} onChange={setSelected}>\n * Subscribe to newsletter\n * </Checkbox>\n * \n * // In table header/cell (no label)\n * <Checkbox slot=\"selection\" />\n * ```\n */\nexport function Checkbox({ size = 'M', children, ...props }: CheckboxProps) {\n return (\n <AriaCheckbox\n {...props}\n className={(renderProps) => checkboxStyles(renderProps)}\n >\n {(renderProps) => {\n const { isSelected, isIndeterminate } = renderProps;\n\n return (\n <>\n <div className={boxStyles({ ...renderProps, isSelected: isSelected || isIndeterminate, size })}>\n {isIndeterminate ? (\n <div className={dashStyles} />\n ) : isSelected ? (\n <svg viewBox=\"0 0 18 18\" aria-hidden=\"true\" className=\"checkbox-checkmark\">\n <polyline points=\"2 9 7 14 16 4\" className=\"checkbox-checkmark-path checkbox-checkmark-selected\" />\n </svg>\n ) : null}\n </div>\n {children}\n </>\n );\n }}\n </AriaCheckbox>\n );\n}\n","import { ProgressBar as AriaProgressBar } from 'react-aria-components';\nimport { style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport './ProgressBar.css';\n\nconst progressBarStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 4,\n width: 'full',\n});\n\nconst labelStyles = style({\n font: 'body',\n color: '--plum-700',\n display: 'flex',\n justifyContent: 'space-between',\n});\n\nconst trackStyles = style({\n height: 6,\n backgroundColor: '--yogurt-200',\n borderRadius: 'full',\n overflow: 'hidden',\n position: 'relative',\n});\n\nconst fillStyles = style({\n height: 'full',\n backgroundColor: '--plum-500',\n borderRadius: 'full',\n});\n\nconst indeterminateFillStyles = style({\n height: 'full',\n backgroundColor: '--plum-500',\n borderRadius: 'full',\n width: '40%',\n});\n\ninterface ProgressBarProps {\n label?: string;\n value?: number;\n maxValue?: number;\n isIndeterminate?: boolean;\n showValueLabel?: boolean;\n 'aria-label'?: string;\n}\n\n/**\n * ProgressBar Component\n * \n * Accessible progress indicator using React Aria Components.\n * Supports both determinate (with value) and indeterminate (loading) states.\n * \n * @example\n * // Determinate progress\n * <ProgressBar label=\"Loading...\" value={50} maxValue={100} />\n * \n * @example\n * // Indeterminate progress\n * <ProgressBar label=\"Loading...\" isIndeterminate />\n */\nexport function ProgressBar({\n label,\n value = 0,\n maxValue = 100,\n isIndeterminate = false,\n showValueLabel = true,\n 'aria-label': ariaLabel,\n}: ProgressBarProps) {\n return (\n <AriaProgressBar\n value={isIndeterminate ? undefined : value}\n maxValue={maxValue}\n isIndeterminate={isIndeterminate}\n aria-label={ariaLabel || label}\n className={progressBarStyles}\n >\n {({ percentage, valueText }) => (\n <>\n {label && (\n <div className={labelStyles}>\n <span>{label}</span>\n {!isIndeterminate && showValueLabel && <span>{valueText}</span>}\n </div>\n )}\n <div className={trackStyles}>\n <div\n className={`${isIndeterminate ? 'progress-fill-indeterminate' : 'progress-fill'} ${isIndeterminate ? indeterminateFillStyles : fillStyles}`}\n style={isIndeterminate ? undefined : { width: `${percentage}%` }}\n />\n </div>\n </>\n )}\n </AriaProgressBar>\n );\n}\n","import { Button as AriaButton, ButtonProps as AriaButtonProps } from 'react-aria-components';\nimport { focusRing, style } from '@react-spectrum/s2/style' with { type: 'macro' };\n\nexport interface AvatarButtonProps extends AriaButtonProps {\n initials: string;\n name: string;\n}\n\nconst avatarButtonStyles = style({\n ...focusRing(),\n display: 'flex',\n alignItems: 'center',\n gap: 12,\n padding: 8,\n paddingX: 16,\n backgroundColor: {\n default: '--plum-100',\n isHovered: '--plum-200',\n isPressed: '--plum-300',\n isFocusVisible: '--plum-200'\n },\n color: '--plum-800',\n borderRadius: 'full',\n borderWidth: 2,\n borderStyle: 'solid',\n borderColor: '--plum-300',\n font: 'title-lg',\n cursor: 'pointer'\n});\n\nconst avatarCircleStyles = style({\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: 32,\n height: 32,\n borderRadius: 'full',\n backgroundColor: '--plum-500',\n color: 'white',\n font: 'title-lg'\n});\n\nexport const AvatarButton = ({ initials, name, ...props }: AvatarButtonProps) => {\n return (\n <AriaButton\n {...props}\n className={renderProps => avatarButtonStyles(renderProps)}\n >\n <div className={avatarCircleStyles}>\n {initials}\n </div>\n <span>{name}</span>\n </AriaButton>\n );\n};\n","import { style } from '@react-spectrum/s2/style' with { type: 'macro' };\n\nexport type BadgeVariant = 'completed' | 'running' | 'failed' | 'cancelled' | 'pending' | 'plum' | 'cucumber' | 'berry' | 'ginger' | 'peach' | 'yogurt';\n\nexport interface BadgeProps {\n variant?: BadgeVariant;\n children: React.ReactNode;\n}\n\nconst badgeStyles = style({\n paddingY: 4,\n paddingX: 12,\n borderRadius: 'lg',\n font: 'ui-sm',\n display: 'inline-block',\n backgroundColor: {\n variant: {\n completed: '--cucumber-100',\n running: '--ginger-100',\n failed: '--berry-100',\n cancelled: '--plum-100',\n pending: '--plum-100',\n plum: '--plum-100',\n cucumber: '--cucumber-100',\n berry: '--berry-100',\n ginger: '--ginger-100',\n peach: '--peach-100',\n yogurt: '--yogurt-100',\n }\n },\n color: {\n variant: {\n completed: '--cucumber-700',\n running: '--ginger-700',\n failed: '--berry-700',\n cancelled: '--plum-700',\n pending: '--plum-700',\n plum: '--plum-700',\n cucumber: '--cucumber-700',\n berry: '--berry-700',\n ginger: '--ginger-700',\n peach: '--peach-700',\n yogurt: '--yogurt-700',\n }\n }\n});\n\n/**\n * Badge component for displaying status or category with conditional colors\n * \n * @example\n * <Badge variant=\"completed\">Completed</Badge>\n * <Badge variant=\"cucumber\">Vegetable</Badge>\n */\nexport function Badge({ variant, children }: BadgeProps) {\n return (\n <span className={badgeStyles({ variant })}>\n {children}\n </span>\n );\n}\n","import { Button as AriaButton, ButtonProps as AriaButtonProps } from 'react-aria-components';\nimport CloseCircle from '@react-spectrum/s2/icons/CloseCircle';\nimport { focusRing, iconStyle, style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { useIntl } from '../lib/i18n-context';\n\n// For props: https://react-spectrum.adobe.com/react-aria/Button.html#props\n// For styling: https://react-spectrum.adobe.com/react-aria/Button.html#styling\n\nexport interface CloseButtonProps extends Omit<AriaButtonProps, 'onPress'> {\n onClose: () => void;\n}\n\nconst closeButtonStyles = style({\n ...focusRing(),\n padding: 8,\n backgroundColor: {\n default: 'transparent',\n isHovered: '--yogurt-100',\n isPressed: '--yogurt-200'\n },\n borderStyle: 'none',\n borderRadius: 'lg',\n cursor: 'pointer',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n forcedColors: {\n backgroundColor: 'ButtonFace',\n color: 'ButtonText',\n borderColor: 'ButtonBorder',\n borderWidth: 1,\n borderStyle: 'solid'\n }\n});\n\nexport const CloseButton = ({ onClose, ...props }: CloseButtonProps) => {\n const intl = useIntl();\n\n return (\n <AriaButton\n {...props}\n onPress={onClose}\n className={renderProps => closeButtonStyles(renderProps)}\n aria-label={intl.formatMessage({ id: 'ferment.detail.close' })}\n >\n <CloseCircle styles={iconStyle({ size: 'XL' })} />\n </AriaButton>\n );\n};\n","import { Button, ComboBox as AriaComboBox, ComboBoxRenderProps, FieldError, Group, Input, Label, ListBox, ListBoxItem, Popover, Text } from 'react-aria-components';\nimport type { Key } from 'react-aria-components';\nimport { focusRing, style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { useEffect, useState } from 'react';\nimport ChevronDown from '@react-spectrum/s2/icons/ChevronDown';\n\nconst comboBoxContainerStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 4,\n minWidth: 192,\n});\n\nconst labelStyles = style({\n font: 'body',\n color: '--plum-700',\n});\n\nconst inputWrapperStyles = style({\n position: 'relative',\n display: 'flex',\n alignItems: 'center',\n});\n\nconst inputStyles = style({\n ...focusRing(),\n width: 'calc(100% - 44px)', // Padding (12px+32px) and border (1px+1px)\n font: 'body',\n flexGrow: 1,\n flexShrink: 1,\n paddingStart: 12,\n paddingEnd: 32,\n paddingY: 8,\n backgroundColor: {\n default: '--yogurt-100',\n isFocusVisible: '--yogurt-200',\n isDisabled: 'gray-100',\n },\n borderRadius: 'lg',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: {\n default: '--yogurt-400',\n isFocusVisible: '--plum-600',\n isDisabled: 'gray-300',\n },\n color: {\n default: '--plum-700',\n isDisabled: 'gray-500',\n },\n outlineStyle: 'none',\n});\n\nconst buttonStyles = style({\n verticalAlign: 'middle',\n marginStart: -36, // 32px for button + padding and 4px this padding\n marginEnd: 8,\n padding: 4,\n borderRadius: 'sm',\n backgroundColor: {\n default: 'transparent',\n isHovered: 'gray-200',\n },\n borderStyle: 'none',\n color: {\n default: 'gray-600',\n isDisabled: 'gray-400',\n },\n cursor: {\n default: 'pointer',\n isDisabled: 'default',\n },\n});\n\nconst popoverStyles = style({\n backgroundColor: '--yogurt-50',\n borderRadius: 'lg',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--yogurt-400',\n boxShadow: 'elevated',\n marginTop: 4,\n maxHeight: 320,\n overflow: 'auto',\n isolation: 'isolate',\n});\n\nconst listBoxStyles = style({\n padding: 4,\n outline: 'none',\n});\n\nconst listBoxItemStyles = style({\n font: 'body',\n paddingX: 8,\n paddingY: 4,\n borderRadius: 'sm',\n cursor: {\n default: 'pointer',\n isDisabled: 'not-allowed',\n },\n backgroundColor: {\n default: 'transparent',\n isHovered: '--yogurt-200',\n isSelected: '--plum-200',\n isFocused: '--yogurt-200',\n isDisabled: '--yogurt-100',\n },\n color: {\n default: '--plum-700',\n isHovered: '--plum-800',\n isFocused: '--plum-800',\n isPressed: '--plum-900',\n isSelected: '--plum-700',\n isDisabled: 'gray-400',\n },\n outline: 'none',\n});\n\nconst descriptionStyles = style({\n font: 'detail',\n color: 'gray-600',\n});\n\nconst errorStyles = style({\n font: 'detail',\n color: '--berry-700',\n});\n\nexport interface ComboBoxItem {\n id: Key;\n label: string;\n textValue?: string;\n}\n\nexport interface ComboBoxProps {\n label?: string;\n description?: string;\n errorMessage?: string;\n isRequired?: boolean;\n isDisabled?: boolean;\n selectedKey?: Key | null;\n onSelectionChange?: (key: Key | null) => void;\n items: ComboBoxItem[];\n placeholder?: string;\n 'aria-label'?: string;\n inputValue?: string;\n onInputChange?: (value: string) => void;\n}\n\n/**\n * Generic ComboBox component for searchable single-selection.\n * \n * Uses React Aria Components ComboBox for accessible selection with filtering.\n * Reusable for any data type - countries, users, categories, etc.\n * \n * @example\n * ```tsx\n * const items = [\n * { id: 1, label: 'Apple' },\n * { id: 2, label: 'Banana' },\n * { id: 3, label: 'Orange' },\n * ];\n * \n * <ComboBox\n * label=\"Fruit\"\n * items={items}\n * selectedKey={selectedId}\n * onSelectionChange={setSelectedId}\n * placeholder=\"Select a fruit\"\n * />\n * ```\n */\nexport function ComboBox({\n label,\n description,\n errorMessage,\n isRequired = false,\n isDisabled = false,\n selectedKey,\n onSelectionChange,\n items,\n placeholder,\n 'aria-label': ariaLabel,\n inputValue: controlledInputValue,\n onInputChange: controlledOnInputChange,\n}: ComboBoxProps) {\n const [internalInputValue, setInternalInputValue] = useState('');\n\n // Sync input value with selected item\n useEffect(() => {\n if (selectedKey) {\n const selected = items.find(item => item.id === selectedKey);\n if (selected) {\n setInternalInputValue(selected.label);\n }\n } else {\n setInternalInputValue('');\n }\n }, [selectedKey, items]);\n\n // Use controlled inputValue if provided, otherwise use internal state\n const effectiveInputValue = controlledInputValue !== undefined\n ? controlledInputValue\n : internalInputValue;\n\n const effectiveOnInputChange = controlledOnInputChange !== undefined\n ? controlledOnInputChange\n : setInternalInputValue;\n\n return (\n <AriaComboBox\n className={comboBoxContainerStyles}\n inputValue={effectiveInputValue}\n onInputChange={effectiveOnInputChange}\n selectedKey={selectedKey}\n onSelectionChange={onSelectionChange}\n isDisabled={isDisabled}\n isRequired={isRequired}\n aria-label={ariaLabel || label}\n >\n {label && <Label className={labelStyles}>{label}</Label>}\n <div className={inputWrapperStyles}>\n <Input className={inputStyles} placeholder={placeholder} />\n <Button className={buttonStyles}>\n <ChevronDown />\n </Button>\n </div>\n {description && !errorMessage && (\n <Text slot=\"description\" className={descriptionStyles}>\n {description}\n </Text>\n )}\n {errorMessage && <FieldError className={errorStyles}>{errorMessage}</FieldError>}\n <Popover className={popoverStyles}>\n <ListBox className={listBoxStyles} items={items}>\n {(item) => (\n <ListBoxItem\n key={item.id}\n id={item.id}\n textValue={item.textValue || item.label}\n className={listBoxItemStyles}\n >\n {item.label}\n </ListBoxItem>\n )}\n </ListBox>\n </Popover>\n </AriaComboBox>\n );\n}\n","import {\n Button as AriaButton,\n Disclosure,\n DisclosureGroup as AriaDisclosureGroup,\n DisclosurePanel,\n DisclosureStateContext,\n Heading\n} from 'react-aria-components';\nimport ChevronRight from '@react-spectrum/s2/icons/ChevronRight';\nimport type { DisclosureGroupProps } from 'react-aria-components';\nimport { style, iconStyle, focusRing } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { useContext } from 'react';\n\nimport './DisclosureGroup.css';\n\n// Disclosure container - very thin grey border, full width button appearance\nconst disclosureContainerStyles = style({\n borderStyle: 'none',\n borderWidth: 0,\n borderColor: 'transparent',\n borderRadius: 'lg',\n overflow: 'hidden',\n width: 'full',\n backgroundColor: '--yogurt-100'\n});\n\n// Disclosure trigger button - blends seamlessly with container\nconst disclosureTriggerStyles = style({\n ...focusRing(),\n font: 'title',\n color: {\n default: '--plum-700',\n isHovered: '--plum-800',\n isFocusVisible: '--plum-800',\n isPressed: '--plum-900'\n },\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n padding: 16,\n paddingX: 20,\n width: 'full',\n backgroundColor: {\n default: '--yogurt-100',\n isHovered: '--yogurt-200',\n isPressed: '--yogurt-300'\n },\n borderStyle: 'none',\n borderRadius: 'none',\n cursor: 'pointer'\n});\n\nconst disclosureHeaderStyles = style({\n margin: 0,\n});\n\n// Disclosure panel - white/light background, blends with button header\nconst disclosurePanelStyles = style({\n padding: {\n default: 0,\n isExpanded: 20,\n },\n backgroundColor: '--yogurt-50',\n borderTopWidth: 1,\n borderTopStyle: 'solid',\n borderColor: '--yogurt-300'\n});\n\nexport interface DisclosureItem {\n id: string;\n title: string;\n content: React.ReactNode;\n onPress?: () => void;\n}\n\ninterface CustomDisclosureGroupProps extends Omit<DisclosureGroupProps, 'children'> {\n items: DisclosureItem[];\n 'aria-label'?: string;\n}\n\n/**\n * Reusable DisclosureGroup component\n * Displays collapsible sections with consistent styling\n * \n * @example\n * <DisclosureGroup\n * items={[\n * {\n * id: 'section1',\n * title: 'Section 1',\n * content: <div>Content goes here</div>\n * }\n * ]}\n * />\n */\nexport function DisclosureGroup({ items, ...props }: CustomDisclosureGroupProps) {\n return (\n <AriaDisclosureGroup {...props} className={disclosureContainerStyles}>\n {items.map((item) => (\n <Disclosure key={item.id}>\n <DisclosureContents key={item.id} {...item} />\n </Disclosure>\n ))}\n </AriaDisclosureGroup>\n );\n}\n\nconst DisclosureContents = (item: DisclosureItem) => {\n let { isExpanded } = useContext(DisclosureStateContext)!;\n\n return (\n <>\n <Heading className={disclosureHeaderStyles}>\n <AriaButton\n slot=\"trigger\"\n className={(renderProps) =>\n `disclosure-chevron ${disclosureTriggerStyles(renderProps)}`\n }\n onPress={item.onPress}\n >\n <ChevronRight styles={iconStyle({ size: 'S' })} />\n {item.title}\n </AriaButton>\n </Heading>\n <DisclosurePanel className={disclosurePanelStyles({ isExpanded })}>\n {item.content}\n </DisclosurePanel>\n </>\n );\n}\n","import {\n TextField as AriaTextField,\n Label,\n Text,\n} from 'react-aria-components';\nimport { style, } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport {\n textFieldDescriptionStyles as labeledValueDescriptionStyles,\n textFieldErrorStyles as labeledValueErrorStyles,\n textFieldLabelStyles as labeledValueLabelStyles,\n} from './TextField';\n\nexport interface LabeledValueProps {\n label: string;\n error?: string;\n description?: string;\n children: React.ReactNode;\n}\n\nconst labeledValueStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 8,\n marginBottom: 20,\n});\n\nconst labeledValueValueStyles = style({\n font: 'body',\n color: '--plum-700',\n});\n\n\nexport function LabeledValue({ label, children, error, description, ...props }: LabeledValueProps) {\n return (\n <AriaTextField className={labeledValueStyles} {...props}>\n <Label className={labeledValueLabelStyles}>{label}</Label>\n <span className={labeledValueValueStyles}>\n {children}\n </span>\n {description && !error && (\n <Text slot=\"description\" className={labeledValueDescriptionStyles}>\n {description}\n </Text>\n )}\n {error && (\n <Text slot=\"errorMessage\" className={labeledValueErrorStyles}>\n {error}\n </Text>\n )}\n </AriaTextField>\n );\n}\n","import {\n MenuTrigger,\n Menu as AriaMenu,\n MenuItem as AriaMenuItem,\n Popover,\n Separator\n} from 'react-aria-components';\nimport { style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { ReactNode } from 'react';\n\nconst menuStyles = style({\n backgroundColor: '--yogurt-50',\n borderRadius: 'lg',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--yogurt-400',\n boxShadow: 'elevated',\n padding: 4,\n minWidth: 200,\n outline: 'none'\n});\n\nconst menuItemStyles = style({\n display: 'flex',\n alignItems: 'center',\n gap: 12,\n paddingX: 12,\n paddingY: 8,\n borderRadius: 'lg',\n font: 'body',\n color: {\n default: '--plum-700',\n isHovered: '--plum-800',\n isFocused: '--plum-800',\n isPressed: '--plum-900'\n },\n backgroundColor: {\n default: 'transparent',\n isHovered: '--yogurt-200',\n isFocused: '--yogurt-200'\n },\n cursor: 'pointer',\n outline: 'none'\n});\n\nconst dividerStyles = style({\n height: 1,\n backgroundColor: '--yogurt-400',\n marginY: 4\n});\n\nexport interface MenuItem {\n id: string;\n label: string;\n icon?: ReactNode;\n onAction: () => void;\n}\n\nexport interface MenuProps {\n trigger: ReactNode;\n items: MenuItem[];\n placement?: 'bottom' | 'bottom start' | 'bottom end' | 'top' | 'top start' | 'top end';\n 'aria-label'?: string;\n}\n\n/**\n * Menu component styled to match Select component\n * Uses yogurt colors for consistency with Select\n */\nexport function Menu({ trigger, items, placement = 'bottom end', 'aria-label': ariaLabel }: MenuProps) {\n return (\n <MenuTrigger>\n {trigger}\n <Popover placement={placement}>\n <AriaMenu className={menuStyles} aria-label={ariaLabel}>\n {items.map((item, index) =>\n item.id === 'divider' ? (\n <Separator key={`divider-${index}`} className={dividerStyles} />\n ) : (\n <AriaMenuItem\n key={item.id}\n id={item.id}\n onAction={item.onAction}\n className={menuItemStyles}\n >\n {item.icon}\n {item.label}\n </AriaMenuItem>\n )\n )}\n </AriaMenu>\n </Popover>\n </MenuTrigger>\n );\n}\n","import { style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { useIntl } from '../lib/i18n-context';\n\nexport interface PasswordValidation {\n minLength: boolean;\n hasLetter: boolean;\n hasNumber: boolean;\n hasSpecial: boolean;\n}\n\nexport interface PasswordStrengthProps {\n validation: PasswordValidation;\n}\n\nconst containerStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 8\n});\n\nconst requirementStyles = style({\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n font: 'body-sm'\n});\n\nconst indicatorStyles = style({\n width: 16,\n height: 16,\n borderRadius: 'full',\n flexShrink: 0,\n backgroundColor: {\n default: '--gray-300',\n isValid: '--cucumber-500'\n },\n color: {\n default: 'inherit',\n isValid: '--cucumber-700'\n }\n});\n\n/**\n * Visual password strength indicator showing validation requirements\n * Displays checkmarks (green) for met requirements, circles (gray) for unmet\n */\nexport function PasswordStrength({ validation }: PasswordStrengthProps) {\n const intl = useIntl();\n\n const requirements = [\n {\n key: 'minLength',\n met: validation.minLength,\n label: intl.formatMessage({ id: 'validation.password.minLength' })\n },\n {\n key: 'hasLetter',\n met: validation.hasLetter,\n label: intl.formatMessage({ id: 'validation.password.letter' })\n },\n {\n key: 'hasNumber',\n met: validation.hasNumber,\n label: intl.formatMessage({ id: 'validation.password.number' })\n },\n {\n key: 'hasSpecial',\n met: validation.hasSpecial,\n label: intl.formatMessage({ id: 'validation.password.special' })\n }\n ];\n\n console.log('PasswordStrength requirements:', requirements, intl, intl.formatMessage({ id: 'validation.password.special' }));\n\n return (\n <div className={containerStyles}>\n {requirements.map(req => (\n <div key={req.key} className={requirementStyles}>\n <div\n className={indicatorStyles({ isValid: req.met })}\n />\n <span>{req.label}</span>\n </div>\n ))}\n </div>\n );\n}\n","import { Radio as AriaRadio, RadioGroup as AriaRadioGroup, type RadioGroupProps as AriaRadioGroupProps, type RadioProps as AriaRadioProps } from 'react-aria-components';\nimport { style, focusRing } from '@react-spectrum/s2/style' with { type: 'macro' };\n\nexport interface RadioGroupProps extends Omit<AriaRadioGroupProps, 'children'> {\n label: string;\n options: Array<{ id: string; label: string; value: string }>;\n error?: string;\n}\n\nexport interface RadioProps extends AriaRadioProps {\n children: React.ReactNode;\n}\n\nconst radioGroupStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 8,\n});\n\nconst labelStyles = style({\n font: 'body',\n fontWeight: 'medium',\n color: '--plum-700',\n marginBottom: 8,\n});\n\nconst radioContainerStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 12,\n});\n\nconst radioStyles = style({\n ...focusRing(),\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n font: 'body',\n color: {\n default: '--plum-700',\n isSelected: '--plum-800',\n isHovered: '--plum-600',\n isDisabled: 'gray-400',\n },\n cursor: 'pointer',\n padding: 8,\n borderRadius: 'lg',\n backgroundColor: {\n default: 'transparent',\n isHovered: '--plum-50',\n },\n});\n\nconst radioButtonStyles = style({\n width: 20,\n height: 20,\n borderRadius: 'full',\n borderWidth: 2,\n borderStyle: 'solid',\n borderColor: {\n default: 'gray-400',\n isSelected: '--plum-500',\n isHovered: '--plum-300',\n isDisabled: 'gray-200',\n },\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n flexShrink: 0,\n backgroundColor: {\n default: 'layer-1',\n isDisabled: 'gray-100',\n },\n});\n\nconst radioInnerDotStyles = style({\n width: 10,\n height: 10,\n borderRadius: 'full',\n backgroundColor: '--plum-500',\n opacity: {\n default: 0,\n isSelected: 1,\n },\n});\n\nconst errorStyles = style({\n font: 'body-sm',\n color: '--berry-600',\n marginTop: 4,\n});\n\nexport function Radio({ children, ...props }: RadioProps) {\n return (\n <AriaRadio {...props} className={renderProps => radioStyles(renderProps)}>\n {renderProps => (\n <>\n <div className={radioButtonStyles(renderProps)}>\n <div className={radioInnerDotStyles(renderProps)} />\n </div>\n <span>{children}</span>\n </>\n )}\n </AriaRadio>\n );\n}\n\nexport function RadioGroup({ label, options, error, ...props }: RadioGroupProps) {\n return (\n <AriaRadioGroup {...props} className={radioGroupStyles}>\n <span className={labelStyles}>{label}</span>\n <div className={radioContainerStyles}>\n {options.map(option => (\n <Radio key={option.id} value={option.value}>\n {option.label}\n </Radio>\n ))}\n </div>\n {error && <span className={errorStyles}>{error}</span>}\n </AriaRadioGroup>\n );\n}\n","import { SearchField as AriaSearchField, Input, Label, Button } from 'react-aria-components';\nimport Close from '@react-spectrum/s2/icons/Close';\nimport { focusRing, style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { raw } from '../utils/styleMacro' with { type: 'macro' };\nimport Search from '@react-spectrum/s2/icons/Search';\n\n\nconst searchFieldContainerStyles = style({\n flex: {\n default: 'none',\n md: 1,\n },\n});\n\nconst searchFieldStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 4,\n});\n\nconst searchInputContainerStyles = style({\n position: 'relative',\n display: 'flex',\n alignItems: 'center',\n});\n\nconst searchInputStyles = style({\n ...focusRing(),\n width: 'full',\n padding: 8,\n paddingStart: 40,\n font: 'body',\n borderRadius: 'lg',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--yogurt-400',\n backgroundColor: {\n default: '--yogurt-100',\n isHovered: '--yogurt-200',\n isFocusVisible: '--yogurt-200',\n },\n});\n\nconst labelStyles = style({\n font: 'body',\n color: '--plum-700',\n marginBottom: 4,\n});\n\nconst searchIconStyles = {\n position: 'absolute' as const,\n left: 12,\n top: '50%',\n transform: 'translateY(-50%)',\n pointerEvents: 'none' as const,\n color: '--yogurt-600',\n};\n\nconst clearButtonStyles = style({\n ...focusRing(),\n position: 'absolute',\n right: 8,\n top: '50%',\n transform: 'translateY(-50%)',\n padding: 4,\n backgroundColor: 'transparent',\n borderStyle: 'none',\n borderRadius: 'default',\n cursor: 'pointer',\n color: {\n default: '--yogurt-600',\n isHovered: '--yogurt-700',\n isPressed: '--yogurt-800',\n },\n});\n\nexport interface SearchFieldProps {\n value: string;\n onChange: (value: string) => void;\n label: string;\n placeholder: string;\n ariaLabel: string;\n}\n\n/**\n * Reusable SearchField component using React Aria Components\n * Styled with S2 design tokens and includes search icon\n * \n * @example\n * <SearchField\n * value={searchQuery}\n * onChange={setSearchTerm}\n * label=\"Search\"\n * placeholder=\"Search ferments...\"\n * ariaLabel=\"Search ferments by name or description\"\n * />\n */\nexport function SearchField({\n value,\n onChange,\n label,\n placeholder,\n ariaLabel,\n}: SearchFieldProps) {\n return (\n <div className={searchFieldContainerStyles}>\n <AriaSearchField\n value={value}\n onChange={onChange}\n className={searchFieldStyles}\n aria-label={ariaLabel}\n >\n {({ isEmpty }) => (\n <>\n <Label className={labelStyles}>{label}</Label>\n <div className={searchInputContainerStyles}>\n <div style={searchIconStyles}>\n <Search />\n </div>\n <Input className={(renderProps) => searchInputStyles(renderProps) + ' ' + raw('&::-webkit-search-cancel-button { display: none }', 'custom_layer')} placeholder={placeholder} />\n {!isEmpty && <Button className={clearButtonStyles} aria-label=\"Clear search\">\n <Close />\n </Button>}\n </div>\n </>\n )}\n </AriaSearchField>\n </div>\n );\n}\n","import { useRouter } from 'next/navigation';\nimport {\n ToggleButton,\n ToggleButtonGroup,\n SelectionIndicator\n} from 'react-aria-components';\nimport { focusRing, style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { useIntl } from '../lib/i18n-context';\nimport GlobeGrid from '@react-spectrum/s2/icons/GlobeGrid';\nimport ListIcon from '@react-spectrum/s2/icons/ViewList';\n\nconst segmentedControlStyles = style({\n display: 'flex',\n gap: 0,\n padding: 4,\n backgroundColor: '--yogurt-200',\n borderRadius: 'lg',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--yogurt-400',\n});\n\nconst toggleButtonStyles = style({\n ...focusRing(),\n position: 'relative',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: 8,\n paddingX: 16,\n paddingY: 8,\n font: 'body',\n color: {\n default: '--plum-700',\n isHovered: '--plum-800',\n isFocusVisible: '--plum-800',\n isPressed: '--plum-900'\n },\n backgroundColor: 'transparent',\n borderStyle: 'none',\n borderRadius: 'default',\n cursor: 'pointer',\n zIndex: 1,\n '--iconPrimary': {\n type: 'color',\n value: {\n default: '--plum-700',\n isHovered: '--plum-800',\n isFocusVisible: '--plum-800',\n isPressed: '--plum-900'\n },\n }\n});\n\nconst selectionIndicatorStyles = style({\n position: 'absolute',\n inset: 0,\n borderRadius: 'default',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: 'gray-300',\n boxShadow: 'elevated',\n zIndex: -1,\n color: {\n isDisabled: 'gray-400',\n forcedColors: 'ButtonText'\n },\n backgroundColor: {\n default: '--plum-300',\n isHovered: '--plum-400',\n isFocusVisibile: '--plum-400',\n isPressed: '--plum-400',\n isDisabled: 'gray-100',\n forcedColors: 'ButtonFace'\n },\n});\n\ninterface SegmentedControlProps {\n value: 'map' | 'list';\n}\n\n/**\n * SegmentedControl for Map/List view toggle\n * Uses RAC ToggleButtonGroup for accessible segmented control\n */\nexport function SegmentedControl({ value }: SegmentedControlProps) {\n const intl = useIntl();\n const router = useRouter();\n\n const handleSelectionChange = (newValue: 'map' | 'list') => {\n if (newValue === 'map') {\n router.push('/');\n } else {\n router.push('/ferments');\n }\n };\n\n return (\n <ToggleButtonGroup\n selectionMode=\"single\"\n selectedKeys={new Set([value])}\n disallowEmptySelection\n onSelectionChange={(keys) => {\n const selected = Array.from(keys)[0] as 'map' | 'list';\n handleSelectionChange(selected);\n }}\n aria-label={intl.formatMessage({ id: 'nav.viewToggle.ariaLabel' })}\n className={segmentedControlStyles}\n >\n <ToggleButton\n id=\"map\"\n className={(renderProps) => toggleButtonStyles(renderProps)}\n aria-label={intl.formatMessage({ id: 'nav.map' })}\n >\n {(renderProps) => (\n <>\n <SelectionIndicator className={selectionIndicatorStyles(renderProps)} />\n <GlobeGrid aria-hidden=\"true\" />\n <span>{intl.formatMessage({ id: 'nav.map' })}</span>\n </>\n )}\n </ToggleButton>\n\n <ToggleButton\n id=\"list\"\n className={(renderProps) => toggleButtonStyles(renderProps)}\n aria-label={intl.formatMessage({ id: 'nav.list' })}\n >\n {(renderProps) => (\n <>\n <SelectionIndicator className={selectionIndicatorStyles(renderProps)} />\n <ListIcon aria-hidden=\"true\" />\n <span>{intl.formatMessage({ id: 'nav.list' })}</span>\n </>\n )}\n </ToggleButton>\n </ToggleButtonGroup>\n );\n}\n","import {\n Select as AriaSelect,\n SelectProps,\n Label,\n Button,\n SelectValue,\n Popover,\n ListBox,\n ListBoxItem\n} from 'react-aria-components';\nimport { focusRing, style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport ChevronDown from '@react-spectrum/s2/icons/ChevronDown';\n\nconst selectContainerStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 4,\n minWidth: 200\n});\n\nconst labelStyles = style({\n font: 'body',\n color: '--plum-700',\n});\n\nconst buttonStyles = style({\n ...focusRing(),\n paddingX: 12,\n paddingY: 8,\n backgroundColor: {\n default: '--yogurt-100',\n isHovered: '--yogurt-200',\n isFocusVisible: '--yogurt-200',\n isPressed: '--yogurt-300'\n },\n borderRadius: 'lg',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--yogurt-400',\n font: 'body',\n color: {\n default: '--plum-700',\n isHovered: '--plum-800',\n isFocused: '--plum-800',\n isPressed: '--plum-900',\n },\n cursor: 'pointer',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: 8,\n minWidth: 200\n});\n\nconst popoverStyles = style({\n backgroundColor: '--yogurt-50',\n borderRadius: 'lg',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--yogurt-400',\n boxShadow: 'elevated',\n marginTop: 4,\n maxHeight: 300,\n overflow: 'auto',\n isolation: 'isolate',\n});\n\nconst listBoxStyles = style({\n padding: 4,\n outline: 'none'\n});\n\nconst listBoxItemStyles = style({\n font: 'body',\n paddingX: 8,\n paddingY: 4,\n borderRadius: 'sm',\n cursor: {\n default: 'pointer',\n isDisabled: 'not-allowed'\n },\n backgroundColor: {\n default: 'transparent',\n isHovered: '--yogurt-200',\n isSelected: '--plum-200',\n isFocused: '--yogurt-200',\n isDisabled: '--yogurt-100'\n },\n color: {\n default: '--plum-700',\n isHovered: '--plum-800',\n isFocused: '--plum-800',\n isPressed: '--plum-900',\n isSelected: '--plum-700',\n isDisabled: 'gray-400'\n },\n outline: 'none'\n});\n\nexport interface SelectOption {\n id: string;\n label: string;\n value: string | null;\n isDisabled?: boolean;\n}\n\ninterface CustomSelectProps extends Omit<SelectProps<SelectOption>, 'children'> {\n label: string;\n options: SelectOption[];\n placeholder?: string;\n}\n\n/**\n * Reusable Select component following RAC pattern\n * Label is inside the Select wrapper for proper a11y\n */\nexport function Select({ label, options, placeholder, ...props }: CustomSelectProps) {\n return (\n <AriaSelect {...props} className={selectContainerStyles}>\n <Label className={labelStyles}>{label}</Label>\n <Button className={buttonStyles}>\n <SelectValue />\n <ChevronDown aria-hidden=\"true\" />\n </Button>\n <Popover className={popoverStyles}>\n <ListBox className={listBoxStyles} items={options}>\n {(item) => (\n <ListBoxItem\n key={item.id}\n id={item.id}\n textValue={item.label}\n isDisabled={item.isDisabled}\n className={listBoxItemStyles}\n >\n {item.label}\n </ListBoxItem>\n )}\n </ListBox>\n </Popover>\n </AriaSelect>\n );\n}\n","import { Button } from './Button';\nimport { DialogTrigger, ListBox, ListBoxItem } from 'react-aria-components';\nimport { Key, ReactNode } from 'react';\nimport { Popover } from './Popover';\nimport { style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { useIntl } from '../lib/i18n-context';\n\nexport interface StatCardProps {\n title: string;\n value: ReactNode;\n description?: string;\n list?: Array<{ id: string | number | Key; name: string }>;\n listTitle?: string;\n}\n\nconst cardStyles = style({\n backgroundColor: 'layer-1',\n borderRadius: 'xl',\n padding: 24,\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--plum-200',\n display: 'flex',\n flexDirection: 'column',\n gap: 12,\n alignItems: 'center',\n textAlign: 'center'\n});\n\nconst cardTitleStyles = style({\n font: 'title-lg',\n color: '--plum-700',\n margin: 0\n});\n\nconst cardValueStyles = style({\n font: 'heading-2xl',\n color: '--plum-700',\n});\n\nconst cardDescStyles = style({\n font: 'body',\n color: '--plum-700',\n margin: 0\n});\n\nconst listPreviewStyles = style({\n font: 'body-sm',\n color: '--plum-700',\n margin: 0,\n marginTop: 8\n});\n\nconst listBoxStyles = style({\n padding: 4,\n outline: 'none'\n});\n\nconst listBoxItemStyles = style({\n font: 'body',\n paddingX: 8,\n paddingY: 4,\n borderRadius: 'sm',\n backgroundColor: 'transparent',\n color: '--plum-700',\n outline: 'none'\n});\n\n/**\n * StatCard component for displaying dashboard statistics\n *\n * @example\n * <StatCard \n * title=\"Total Sources\"\n * value={42}\n * description=\"Active scraping sources\"\n * />\n *\n * @example\n * // With icon as value\n * <StatCard \n * title=\"System Status\"\n * value={<CheckmarkCircle />}\n * description=\"All systems operational\"\n * />\n *\n * @example\n * // With list and popover\n * <StatCard\n * title=\"Countries Without Ferments\"\n * value={5}\n * list={countries}\n * listTitle=\"Countries Without Ferments\"\n * />\n */\nexport function StatCard({ title, value, description, list, listTitle }: StatCardProps) {\n const intl = useIntl();\n\n return (\n <div className={cardStyles}>\n <h3 className={cardTitleStyles}>{title}</h3>\n <div className={cardValueStyles}>{value}</div>\n {description && <p className={cardDescStyles}>{description}</p>}\n {list && list.length > 0 && (\n <div className={listPreviewStyles}>\n <DialogTrigger>\n <Button variant=\"navigation\">{intl.formatMessage({ id: 'statCard.viewAll' })}</Button>\n <Popover\n title={listTitle || title}>\n <ListBox className={listBoxStyles}>\n {list.map(item => <ListBoxItem key={item.id} className={listBoxItemStyles}>{item.name}</ListBoxItem>)}\n </ListBox>\n </Popover>\n </DialogTrigger>\n </div>\n )}\n </div>\n );\n}\n","import { Dialog, Heading, Popover as RACPopover } from 'react-aria-components';\nimport type { ReactNode } from 'react';\nimport { style } from '@react-spectrum/s2/style' with { type: 'macro' };\n\nexport interface PopoverProps {\n title: string;\n children: ReactNode;\n}\n\nconst popoverStyles = style({\n backgroundColor: 'layer-1',\n borderRadius: 'lg',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--plum-300',\n padding: 24,\n maxWidth: 400,\n boxShadow: 'elevated'\n});\n\nconst dialogStyles = style({\n maxHeight: '33vh',\n overflow: 'auto'\n});\n\nconst headingStyles = style({\n font: 'heading',\n margin: 0,\n marginBottom: 16,\n color: '--plum-700'\n});\n\n/**\n * Popover component with list of items\n * \n * Uses React Aria Components Dialog, Popover, Dialog, and Heading\n * \n * @example\n * <Popover\n * title=\"Countries Without Ferments\"\n * items={countries}\n * />\n */\nexport function Popover({ title, children }: PopoverProps) {\n return (\n <RACPopover className={popoverStyles}>\n <Dialog className={dialogStyles}>\n <Heading slot=\"title\" className={headingStyles}>\n {title}\n </Heading>\n {children}\n </Dialog>\n </RACPopover>\n );\n}\n","import { Switch as AriaSwitch, SwitchProps as AriaSwitchProps } from 'react-aria-components';\nimport { focusRing, style } from '@react-spectrum/s2/style' with { type: 'macro' };\n\n// For props: https://react-spectrum.adobe.com/react-aria/Switch.html#props\n// For styling: https://react-spectrum.adobe.com/react-aria/Switch.html#styling\n\nconst switchContainerStyles = style({\n ...focusRing(),\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n paddingEnd: 2,\n cursor: 'pointer',\n font: 'ui-lg',\n color: {\n default: '--plum-700',\n isHovered: '--plum-800',\n isFocusVisible: '--plum-800',\n isPressed: '--plum-900'\n },\n borderRadius: 'full'\n});\n\nconst switchTrackStyles = style({\n width: 44,\n height: 24,\n borderRadius: 'full',\n padding: 2,\n display: 'flex',\n alignItems: 'center',\n backgroundColor: {\n default: '--yogurt-400',\n isSelected: '--plum-500',\n isDisabled: 'disabled'\n },\n borderStyle: {\n forcedColors: 'solid'\n },\n borderColor: {\n forcedColors: 'ButtonBorder'\n },\n borderWidth: {\n forcedColors: 1\n }\n});\n\nconst switchThumbStyles = style({\n width: 20,\n height: 20,\n borderRadius: 'full',\n backgroundColor: {\n default: 'white',\n forcedColors: 'ButtonText'\n },\n transform: {\n default: 'translateX(0)',\n isSelected: 'translateX(22px)'\n }\n});\n\nexport interface SwitchProps extends Omit<AriaSwitchProps, 'children'> {\n children?: React.ReactNode;\n}\n\nexport const Switch = ({ children, ...props }: SwitchProps) => {\n return (\n <AriaSwitch\n {...props}\n className={renderProps => switchContainerStyles(renderProps)}\n >\n {renderProps => (\n <>\n <div className={switchTrackStyles(renderProps)}>\n <div className={switchThumbStyles(renderProps)} />\n </div>\n {children}\n </>\n )}\n </AriaSwitch>\n );\n};","import { focusRing, style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { Tabs as AriaTabs, TabList as AriaTabList, Tab as AriaTab, TabPanel as AriaTabPanel } from 'react-aria-components';\nimport type { TabListProps, TabPanelProps, TabProps, TabsProps } from 'react-aria-components';\n\nconst tabsStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 0\n});\n\nconst tabListStyles = style({\n display: 'flex',\n gap: 4,\n borderBottomWidth: 2,\n borderBottomStyle: 'solid',\n borderBottomColor: '--plum-200',\n marginBottom: 24,\n overflowX: 'auto',\n overflowY: 'hidden',\n WebkitOverflowScrolling: 'touch'\n});\n\nconst tabStyles = style({\n ...focusRing(),\n paddingX: 20,\n paddingY: 12,\n font: 'body',\n fontWeight: {\n isSelected: 'bold'\n },\n color: {\n default: '--plum-700',\n isSelected: '--plum-800',\n isHovered: '--plum-800',\n isDisabled: '--plum-300'\n },\n backgroundColor: {\n default: 'transparent',\n isHovered: '--plum-50',\n isSelected: '--plum-100'\n },\n borderWidth: 0,\n borderBottomWidth: 2,\n borderStyle: 'solid',\n borderColor: {\n default: 'transparent',\n isSelected: '--plum-700',\n isHovered: '--plum-300'\n },\n cursor: {\n default: 'pointer',\n isDisabled: 'not-allowed'\n },\n transitionProperty: 'all',\n transitionDuration: '200ms'\n});\n\nconst tabPanelStyles = style({\n paddingY: 0,\n outlineStyle: 'none'\n});\n\nexport function Tabs({ children, ...props }: TabsProps) {\n return (\n <AriaTabs className={tabsStyles} {...props}>\n {children}\n </AriaTabs>\n );\n}\n\nexport function TabList<T extends object>({ children, ...props }: TabListProps<T>) {\n return (\n <AriaTabList className={tabListStyles} {...props}>\n {children}\n </AriaTabList>\n );\n}\n\nexport function Tab({ children, ...props }: TabProps) {\n return (\n <AriaTab className={tabStyles} {...props}>\n {children}\n </AriaTab>\n );\n}\n\nexport function TabPanel({ children, ...props }: TabPanelProps) {\n return (\n <AriaTabPanel className={tabPanelStyles} {...props}>\n {children}\n </AriaTabPanel>\n );\n}\n","import {\n TextArea as AriaTextArea,\n TextField as AriaTextField,\n Label,\n} from 'react-aria-components';\nimport { style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { textFieldErrorStyles, textFieldLabelStyles, type TextFieldProps, textFieldStyles } from './TextField';\nimport { textFieldInputStyles } from '../utils/styleUtils' with { type: 'macro' };\nimport { useRef, useEffect } from 'react';\nimport { safeWindow } from '../utils/browser';\n\nexport interface TextAreaProps extends Omit<TextFieldProps, 'type'> { }\n\nconst textareaStyles = style({\n ...textFieldInputStyles(),\n resize: 'none',\n minHeight: 75, // ~3 lines\n maxHeight: '75vh',\n overflowY: 'auto',\n});\n\nexport function TextArea({ label, error, placeholder, value, ...props }: TextAreaProps) {\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n\n // Auto-grow on value change\n useEffect(() => {\n const textarea = textareaRef.current;\n if (!textarea) return;\n\n // Reset height to recalculate\n textarea.style.height = '75px';\n\n // Set to scrollHeight (content height)\n const newHeight = Math.min(textarea.scrollHeight, safeWindow.innerHeight * 0.75 || 64);\n textarea.style.height = `${newHeight}px`;\n }, [value]);\n\n return (\n <AriaTextField className={textFieldStyles} {...props} value={value}>\n <Label className={textFieldLabelStyles}>{label}</Label>\n <AriaTextArea\n ref={textareaRef}\n className={textareaStyles}\n placeholder={placeholder}\n />\n {error && <div className={textFieldErrorStyles}>{error}</div>}\n </AriaTextField>\n );\n}\n","/* Checkbox styles for svg checked state */\n\n.checkbox-checkmark {\n width: 70%;\n height: 70%;\n fill: none;\n stroke: white;\n stroke-width: 2;\n stroke-linecap: round;\n stroke-linejoin: round;\n transition: stroke-dashoffset 150ms;\n}\n\n.checkbox-checkmark-path {\n stroke-dasharray: 22;\n stroke-dashoffset: 66;\n transition: stroke-dashoffset 150ms;\n}\n\n.checkbox-checkmark-path.checkbox-checkmark-selected {\n stroke-dashoffset: 44;\n}","/* Progress bar animations and transitions */\n.progress-fill {\n transition: width 200ms ease-in-out;\n}\n\n.progress-fill-indeterminate {\n animation: progress-indeterminate 1.5s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n}\n\n@keyframes progress-indeterminate {\n 0% {\n transform: translateX(-100%);\n }\n\n 100% {\n transform: translateX(350%);\n }\n}","/**\n * HealthCheck Component - Animations Only\n * All other styles are in S2 style macros\n */\n\n/* Chevron icon rotation on expansion */\n.disclosure-chevron[aria-expanded=\"true\"] svg {\n transform: rotate(90deg);\n}\n\n.disclosure-chevron svg {\n transition: transform 200ms;\n}\n\n@keyframes slideDown {\n from {\n opacity: 0;\n transform: translateY(-10px);\n }\n\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n@keyframes slideUp {\n from {\n opacity: 1;\n transform: translateY(0);\n }\n\n to {\n opacity: 0;\n transform: translateY(-10px);\n }\n}"],"names":[],"version":3,"file":"module.css.map"}
|
package/dist/module.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "./module.css";
|
|
2
2
|
import {jsx as $3A86U$jsx, jsxs as $3A86U$jsxs, Fragment as $3A86U$Fragment} from "react/jsx-runtime";
|
|
3
|
-
import {ModalOverlay as $3A86U$ModalOverlay, Modal as $3A86U$Modal, Dialog as $3A86U$Dialog, Heading as $3A86U$Heading, Button as $3A86U$Button, TextField as $3A86U$TextField, Label as $3A86U$Label, Input as $3A86U$Input, Text as $3A86U$Text, useFilter as $3A86U$useFilter, Autocomplete as $3A86U$Autocomplete, SearchField as $3A86U$SearchField, ListBox as $3A86U$ListBox, ListBoxItem as $3A86U$ListBoxItem, Table as $3A86U$Table, TableHeader as $3A86U$TableHeader, Column as $3A86U$Column, TableBody as $3A86U$TableBody, Row as $3A86U$Row, Cell as $3A86U$Cell, Checkbox as $3A86U$Checkbox, ProgressBar as $3A86U$ProgressBar, ComboBox as $3A86U$ComboBox, FieldError as $3A86U$FieldError, Popover as $3A86U$Popover, DisclosureGroup as $3A86U$DisclosureGroup, Disclosure as $3A86U$Disclosure, DisclosureStateContext as $3A86U$DisclosureStateContext, DisclosurePanel as $3A86U$DisclosurePanel, ToggleButtonGroup as $3A86U$ToggleButtonGroup, ToggleButton as $3A86U$ToggleButton, SelectionIndicator as $3A86U$SelectionIndicator, Link as $3A86U$Link, MenuTrigger as $3A86U$MenuTrigger, Menu as $3A86U$Menu, Separator as $3A86U$Separator, MenuItem as $3A86U$MenuItem, Radio as $3A86U$Radio, RadioGroup as $3A86U$RadioGroup, Select as $3A86U$Select, SelectValue as $3A86U$SelectValue, Switch as $3A86U$Switch, Tabs as $3A86U$Tabs, TabList as $3A86U$TabList, Tab as $3A86U$Tab, TabPanel as $3A86U$TabPanel, TextArea as $3A86U$TextArea} from "react-aria-components";
|
|
3
|
+
import {ModalOverlay as $3A86U$ModalOverlay, Modal as $3A86U$Modal, Dialog as $3A86U$Dialog, Heading as $3A86U$Heading, Button as $3A86U$Button, TextField as $3A86U$TextField, Label as $3A86U$Label, Input as $3A86U$Input, Text as $3A86U$Text, useFilter as $3A86U$useFilter, Autocomplete as $3A86U$Autocomplete, SearchField as $3A86U$SearchField, ListBox as $3A86U$ListBox, ListBoxItem as $3A86U$ListBoxItem, Table as $3A86U$Table, TableHeader as $3A86U$TableHeader, Column as $3A86U$Column, TableBody as $3A86U$TableBody, Row as $3A86U$Row, Cell as $3A86U$Cell, Checkbox as $3A86U$Checkbox, ProgressBar as $3A86U$ProgressBar, ComboBox as $3A86U$ComboBox, FieldError as $3A86U$FieldError, Popover as $3A86U$Popover, DisclosureGroup as $3A86U$DisclosureGroup, Disclosure as $3A86U$Disclosure, DisclosureStateContext as $3A86U$DisclosureStateContext, DisclosurePanel as $3A86U$DisclosurePanel, ToggleButtonGroup as $3A86U$ToggleButtonGroup, ToggleButton as $3A86U$ToggleButton, SelectionIndicator as $3A86U$SelectionIndicator, Link as $3A86U$Link, MenuTrigger as $3A86U$MenuTrigger, Menu as $3A86U$Menu, Separator as $3A86U$Separator, MenuItem as $3A86U$MenuItem, Radio as $3A86U$Radio, RadioGroup as $3A86U$RadioGroup, Select as $3A86U$Select, SelectValue as $3A86U$SelectValue, DialogTrigger as $3A86U$DialogTrigger, Switch as $3A86U$Switch, Tabs as $3A86U$Tabs, TabList as $3A86U$TabList, Tab as $3A86U$Tab, TabPanel as $3A86U$TabPanel, TextArea as $3A86U$TextArea} from "react-aria-components";
|
|
4
4
|
import {useState as $3A86U$useState, useMemo as $3A86U$useMemo, useRef as $3A86U$useRef, useEffect as $3A86U$useEffect, createContext as $3A86U$createContext, useContext as $3A86U$useContext} from "react";
|
|
5
5
|
import $3A86U$reactspectrums2iconsSearch from "@react-spectrum/s2/icons/Search";
|
|
6
6
|
import $3A86U$reactspectrums2iconsCheckmarkCircle from "@react-spectrum/s2/icons/CheckmarkCircle";
|
|
@@ -53,6 +53,7 @@ $parcel$export($8a93cc78f6eaa206$exports, "LabeledValue", () => $e6c4d3d9e51c440
|
|
|
53
53
|
$parcel$export($8a93cc78f6eaa206$exports, "Link", () => $7e3a08a0d5ffc4c6$export$a6c7ac8248d6e38a);
|
|
54
54
|
$parcel$export($8a93cc78f6eaa206$exports, "Menu", () => $9317e67b0b0534b2$export$d9b273488cd8ce6f);
|
|
55
55
|
$parcel$export($8a93cc78f6eaa206$exports, "PasswordStrength", () => $3b45ce15fb15c24d$export$ace2b65713be14d8);
|
|
56
|
+
$parcel$export($8a93cc78f6eaa206$exports, "Popover", () => $0cce5ab1682825e8$export$5b6b19405a83ff9d);
|
|
56
57
|
$parcel$export($8a93cc78f6eaa206$exports, "ProgressBar", () => $61479464bb5fd0ad$export$c17561cb55d4db30);
|
|
57
58
|
$parcel$export($8a93cc78f6eaa206$exports, "RadioGroup", () => $4f3552ad992cebcc$export$a98f0dcb43a68a25);
|
|
58
59
|
$parcel$export($8a93cc78f6eaa206$exports, "SearchField", () => $9260936bb33ab2d4$export$b94867ecbd698f21);
|
|
@@ -1950,6 +1951,29 @@ function $3b45ce15fb15c24d$export$ace2b65713be14d8({ validation: validation }) {
|
|
|
1950
1951
|
|
|
1951
1952
|
|
|
1952
1953
|
|
|
1954
|
+
const $0cce5ab1682825e8$var$popoverStyles = " gqlRAZb12 oc12 nc12 kc12 jc12 _kb12 hb12 mb12 lb12 _jf12 iDr3Osb12 Th12 Qh12 Sh12 Rh12 L6Neosc12 _nLeasBb12";
|
|
1955
|
+
const $0cce5ab1682825e8$var$dialogStyles = " KBxJurc12 _Na12 Pa12";
|
|
1956
|
+
const $0cce5ab1682825e8$var$headingStyles = " uk12 uch12 udi12 uea12 ugb12 uhd12 uje12 u2NhKxcl12 uic12 -_6BNtrc-g12 vx12 we12 xe12 _xa12 wX0cczbd12 xX0cczbd12 _xX0cczba12 wezxGHbd12 xezxGHbd12 _xezxGHba12 wfe12 xfe12 _xfa12 wfX0cczbd12 xfX0cczbd12 _xfX0cczba12 wfezxGHbd12 xfezxGHbd12 _xfezxGHba12 _Fd12 _FezxGHba12 _FnuYUweb12 p9PEPsb12 Jy12 GC12 Iy12 Hy12";
|
|
1957
|
+
function $0cce5ab1682825e8$export$5b6b19405a83ff9d({ title: title, children: children }) {
|
|
1958
|
+
return (0, $3A86U$jsx)((0, $3A86U$Popover), {
|
|
1959
|
+
className: $0cce5ab1682825e8$var$popoverStyles,
|
|
1960
|
+
children: (0, $3A86U$jsxs)((0, $3A86U$Dialog), {
|
|
1961
|
+
className: $0cce5ab1682825e8$var$dialogStyles,
|
|
1962
|
+
children: [
|
|
1963
|
+
(0, $3A86U$jsx)((0, $3A86U$Heading), {
|
|
1964
|
+
slot: "title",
|
|
1965
|
+
className: $0cce5ab1682825e8$var$headingStyles,
|
|
1966
|
+
children: title
|
|
1967
|
+
}),
|
|
1968
|
+
children
|
|
1969
|
+
]
|
|
1970
|
+
})
|
|
1971
|
+
});
|
|
1972
|
+
}
|
|
1973
|
+
|
|
1974
|
+
|
|
1975
|
+
|
|
1976
|
+
|
|
1953
1977
|
|
|
1954
1978
|
const $4f3552ad992cebcc$var$radioGroupStyles = " sd12 _ta12 Ue12 qe12";
|
|
1955
1979
|
const $4f3552ad992cebcc$var$labelStyles = " uk12 uch12 udi12 uea12 ugb12 uhd12 uje12 u2NhKxcl12 uic12 -_6BNtrc-d12 vx12 wc12 xc12 _xa12 wezxGHbtN5kwb12 xezxGHbg12 _xezxGHba12 _Fb12 _FnuYUwec12 p9PEPsb12 Gv12";
|
|
@@ -2505,11 +2529,19 @@ function $4c94f3494e92f172$export$ef9b1a59e592288f({ label: label, options: opti
|
|
|
2505
2529
|
|
|
2506
2530
|
|
|
2507
2531
|
|
|
2532
|
+
|
|
2533
|
+
|
|
2534
|
+
|
|
2535
|
+
|
|
2508
2536
|
const $c7af66d6eab82960$var$cardStyles = " gqlRAZb12 oe12 ne12 ke12 je12 Th12 Qh12 Sh12 Rh12 _kb12 hb12 mb12 lb12 _jf12 i05TOsb12 sd12 _ta12 Ug12 qg12 eb12 _Wa12";
|
|
2509
2537
|
const $c7af66d6eab82960$var$cardTitleStyles = " uk12 uch12 udi12 uea12 ugb12 uhd12 uje12 u2NhKxcl12 uic12 -_6BNtrc-e12 vx12 wd12 xd12 _xa12 wX0cczbc12 xX0cczbc12 _xX0cczba12 wfd12 xfd12 _xfa12 wfX0cczbc12 xfX0cczbc12 _xfX0cczba12 _Fd12 _FezxGHba12 _FnuYUweb12 p9PEPsb12 Jy12 Gy12 Iy12 Hy12";
|
|
2510
2538
|
const $c7af66d6eab82960$var$cardValueStyles = " uk12 uch12 udi12 uea12 ugb12 uhd12 uje12 u2NhKxcl12 uic12 -_6BNtrc-snabcc12 vx12 we12 xe12 _xa12 wX0cczbd12 xX0cczbd12 _xX0cczba12 wezxGHbd12 xezxGHbd12 _xezxGHba12 wfe12 xfe12 _xfa12 wfX0cczbd12 xfX0cczbd12 _xfX0cczba12 wfezxGHbd12 xfezxGHbd12 _xfezxGHba12 _Fd12 _FezxGHba12 _FnuYUweb12 p9PEPsb12";
|
|
2511
2539
|
const $c7af66d6eab82960$var$cardDescStyles = " uk12 uch12 udi12 uea12 ugb12 uhd12 uje12 u2NhKxcl12 uic12 -_6BNtrc-d12 vx12 wb12 xb12 _xa12 _Fb12 _FnuYUwec12 p9PEPsb12 Jy12 Gy12 Iy12 Hy12";
|
|
2512
|
-
|
|
2540
|
+
const $c7af66d6eab82960$var$listPreviewStyles = " uk12 uch12 udi12 uea12 ugb12 uhd12 uje12 u2NhKxcl12 uic12 -_6BNtrc-c12 vx12 wb12 xb12 _xa12 _Fb12 _FnuYUwec12 p9PEPsb12 Jv12 Gy12 Iy12 Hy12";
|
|
2541
|
+
const $c7af66d6eab82960$var$listBoxStyles = " Tp12 Qp12 Sp12 Rp12";
|
|
2542
|
+
const $c7af66d6eab82960$var$listBoxItemStyles = " uk12 uch12 udi12 uea12 ugb12 uhd12 uje12 u2NhKxcl12 uic12 -_6BNtrc-d12 vx12 wb12 xb12 _xa12 _Fb12 _FnuYUwec12 p9PEPsb12 St12 Rt12 Tp12 Qp12 oa12 na12 ka12 ja12 g912";
|
|
2543
|
+
function $c7af66d6eab82960$export$b5cddb1a6bf990a0({ title: title, value: value, description: description, list: list, listTitle: listTitle }) {
|
|
2544
|
+
const intl = (0, $e4a23425e1acf543$export$5aebe9a147f4d146)();
|
|
2513
2545
|
return (0, $3A86U$jsxs)("div", {
|
|
2514
2546
|
className: $c7af66d6eab82960$var$cardStyles,
|
|
2515
2547
|
children: [
|
|
@@ -2524,6 +2556,29 @@ function $c7af66d6eab82960$export$b5cddb1a6bf990a0({ title: title, value: value,
|
|
|
2524
2556
|
description && (0, $3A86U$jsx)("p", {
|
|
2525
2557
|
className: $c7af66d6eab82960$var$cardDescStyles,
|
|
2526
2558
|
children: description
|
|
2559
|
+
}),
|
|
2560
|
+
list && list.length > 0 && (0, $3A86U$jsx)("div", {
|
|
2561
|
+
className: $c7af66d6eab82960$var$listPreviewStyles,
|
|
2562
|
+
children: (0, $3A86U$jsxs)((0, $3A86U$DialogTrigger), {
|
|
2563
|
+
children: [
|
|
2564
|
+
(0, $3A86U$jsx)((0, $4aac1ae7c2a46df4$export$353f5b6fc5456de1), {
|
|
2565
|
+
variant: "navigation",
|
|
2566
|
+
children: intl.formatMessage({
|
|
2567
|
+
id: 'statCard.viewAll'
|
|
2568
|
+
})
|
|
2569
|
+
}),
|
|
2570
|
+
(0, $3A86U$jsx)((0, $0cce5ab1682825e8$export$5b6b19405a83ff9d), {
|
|
2571
|
+
title: listTitle || title,
|
|
2572
|
+
children: (0, $3A86U$jsx)((0, $3A86U$ListBox), {
|
|
2573
|
+
className: $c7af66d6eab82960$var$listBoxStyles,
|
|
2574
|
+
children: list.map((item)=>(0, $3A86U$jsx)((0, $3A86U$ListBoxItem), {
|
|
2575
|
+
className: $c7af66d6eab82960$var$listBoxItemStyles,
|
|
2576
|
+
children: item.name
|
|
2577
|
+
}, item.id))
|
|
2578
|
+
})
|
|
2579
|
+
})
|
|
2580
|
+
]
|
|
2581
|
+
})
|
|
2527
2582
|
})
|
|
2528
2583
|
]
|
|
2529
2584
|
});
|
|
@@ -3174,5 +3229,5 @@ $parcel$exportWildcard($be9c1f6b736f678e$exports, $2c570e2bb65234ea$exports);
|
|
|
3174
3229
|
|
|
3175
3230
|
|
|
3176
3231
|
|
|
3177
|
-
export {$a6c6e9de70b2177d$export$de466dd8317b0b75 as AlertDialog, $a6c6e9de70b2177d$export$a551a871839880f9 as PromptDialog, $f156c2ac5fd12c51$export$2f2b9559550c7bbc as Autocomplete, $07375c4c274a5e99$export$5f8b5a1eceff31bd as AutocompleteTable, $038bc2c2a6874c27$export$5525762649f7bdac as AvatarButton, $e2023e6e190b3690$export$37acb3580601e69a as Badge, $4aac1ae7c2a46df4$export$353f5b6fc5456de1 as Button, $35fcc32020885daa$export$48513f6b9f8ce62d as Checkbox, $07aaa6869c77f679$export$de65de8213222d10 as CloseButton, $d6f6f2bb59cbf8cf$export$72b9695b8216309a as ComboBox, $5ddfe94eba6154eb$export$944aceb4f8c89f10 as DisclosureGroup, $a7c96df248e47957$export$221f31a87e5a826c as FilterTabs, $e6c4d3d9e51c440a$export$d1328f67a56fa517 as LabeledValue, $7e3a08a0d5ffc4c6$export$a6c7ac8248d6e38a as Link, $9317e67b0b0534b2$export$d9b273488cd8ce6f as Menu, $3b45ce15fb15c24d$export$ace2b65713be14d8 as PasswordStrength, $61479464bb5fd0ad$export$c17561cb55d4db30 as ProgressBar, $4f3552ad992cebcc$export$a98f0dcb43a68a25 as RadioGroup, $9260936bb33ab2d4$export$b94867ecbd698f21 as SearchField, $dc7b7ce521817383$export$668709c620d0b8e2 as SegmentedControl, $4c94f3494e92f172$export$ef9b1a59e592288f as Select, $c7af66d6eab82960$export$b5cddb1a6bf990a0 as StatCard, $b32787d33db6deae$export$b5d5cf8927ab7262 as Switch, $8fd64f6a3d6adad1$export$54ec01a60f47d33d as Table, $73303ac6ce50c127$export$b2539bed5023c21c as Tabs, $73303ac6ce50c127$export$e51a686c67fdaa2d as TabList, $73303ac6ce50c127$export$3e41faf802a29e71 as Tab, $73303ac6ce50c127$export$3d96ec278d3efce4 as TabPanel, $db470a4073e4639a$export$f5c9f3c2c4054eec as TextArea, $f62becf1f473d668$export$2c73285ae9390cec as TextField, $14dac782240849bf$export$d8964aec282183a3 as ThemeProvider, $14dac782240849bf$export$93d4e7f90805808f as useTheme, $e4a23425e1acf543$export$a54013f0d02a8f82 as I18nProvider, $e4a23425e1acf543$export$5aebe9a147f4d146 as useIntl, $5035dda096f080f5$export$66b627500eff8faa as FilterProvider, $5035dda096f080f5$export$973ca22476b0e05f as useFilters, $63f988b4583af0b2$export$42d0f19526e5d9da as DEFAULT_FILTERS, $63f988b4583af0b2$export$a15c90bdf31a4ff3 as hasActiveFilters, $63f988b4583af0b2$export$d0e6b6bfede2a57 as clearFilters, $63f988b4583af0b2$export$fa59a93dacecf201 as updateFilter, $63f988b4583af0b2$export$5d7f9913734d3d5f as filtersToQueryParams, $4ae0d6a0ccb53610$export$457d6d213bf79459 as textFieldInputStyles, $b413b04534c20d7d$export$2514cbae0d5c71c5 as getCategoryColors, $0e62a2c35fb1f646$export$644d8ea042df96a6 as api, $20eaf1306ff19a16$export$4e09c449d6c407f7 as isBrowser, $20eaf1306ff19a16$export$62858bae88b53fd0 as isDocument, $20eaf1306ff19a16$export$7dd80fbc14cd23b1 as safeWindow, $20eaf1306ff19a16$export$9aee702616bdf23c as safeDocument, $20eaf1306ff19a16$export$2aa78e70b3d79ddc as safeLocalStorage};
|
|
3232
|
+
export {$a6c6e9de70b2177d$export$de466dd8317b0b75 as AlertDialog, $a6c6e9de70b2177d$export$a551a871839880f9 as PromptDialog, $f156c2ac5fd12c51$export$2f2b9559550c7bbc as Autocomplete, $07375c4c274a5e99$export$5f8b5a1eceff31bd as AutocompleteTable, $038bc2c2a6874c27$export$5525762649f7bdac as AvatarButton, $e2023e6e190b3690$export$37acb3580601e69a as Badge, $4aac1ae7c2a46df4$export$353f5b6fc5456de1 as Button, $35fcc32020885daa$export$48513f6b9f8ce62d as Checkbox, $07aaa6869c77f679$export$de65de8213222d10 as CloseButton, $d6f6f2bb59cbf8cf$export$72b9695b8216309a as ComboBox, $5ddfe94eba6154eb$export$944aceb4f8c89f10 as DisclosureGroup, $a7c96df248e47957$export$221f31a87e5a826c as FilterTabs, $e6c4d3d9e51c440a$export$d1328f67a56fa517 as LabeledValue, $7e3a08a0d5ffc4c6$export$a6c7ac8248d6e38a as Link, $9317e67b0b0534b2$export$d9b273488cd8ce6f as Menu, $3b45ce15fb15c24d$export$ace2b65713be14d8 as PasswordStrength, $0cce5ab1682825e8$export$5b6b19405a83ff9d as Popover, $61479464bb5fd0ad$export$c17561cb55d4db30 as ProgressBar, $4f3552ad992cebcc$export$a98f0dcb43a68a25 as RadioGroup, $9260936bb33ab2d4$export$b94867ecbd698f21 as SearchField, $dc7b7ce521817383$export$668709c620d0b8e2 as SegmentedControl, $4c94f3494e92f172$export$ef9b1a59e592288f as Select, $c7af66d6eab82960$export$b5cddb1a6bf990a0 as StatCard, $b32787d33db6deae$export$b5d5cf8927ab7262 as Switch, $8fd64f6a3d6adad1$export$54ec01a60f47d33d as Table, $73303ac6ce50c127$export$b2539bed5023c21c as Tabs, $73303ac6ce50c127$export$e51a686c67fdaa2d as TabList, $73303ac6ce50c127$export$3e41faf802a29e71 as Tab, $73303ac6ce50c127$export$3d96ec278d3efce4 as TabPanel, $db470a4073e4639a$export$f5c9f3c2c4054eec as TextArea, $f62becf1f473d668$export$2c73285ae9390cec as TextField, $14dac782240849bf$export$d8964aec282183a3 as ThemeProvider, $14dac782240849bf$export$93d4e7f90805808f as useTheme, $e4a23425e1acf543$export$a54013f0d02a8f82 as I18nProvider, $e4a23425e1acf543$export$5aebe9a147f4d146 as useIntl, $5035dda096f080f5$export$66b627500eff8faa as FilterProvider, $5035dda096f080f5$export$973ca22476b0e05f as useFilters, $63f988b4583af0b2$export$42d0f19526e5d9da as DEFAULT_FILTERS, $63f988b4583af0b2$export$a15c90bdf31a4ff3 as hasActiveFilters, $63f988b4583af0b2$export$d0e6b6bfede2a57 as clearFilters, $63f988b4583af0b2$export$fa59a93dacecf201 as updateFilter, $63f988b4583af0b2$export$5d7f9913734d3d5f as filtersToQueryParams, $4ae0d6a0ccb53610$export$457d6d213bf79459 as textFieldInputStyles, $b413b04534c20d7d$export$2514cbae0d5c71c5 as getCategoryColors, $0e62a2c35fb1f646$export$644d8ea042df96a6 as api, $20eaf1306ff19a16$export$4e09c449d6c407f7 as isBrowser, $20eaf1306ff19a16$export$62858bae88b53fd0 as isDocument, $20eaf1306ff19a16$export$7dd80fbc14cd23b1 as safeWindow, $20eaf1306ff19a16$export$9aee702616bdf23c as safeDocument, $20eaf1306ff19a16$export$2aa78e70b3d79ddc as safeLocalStorage};
|
|
3178
3233
|
//# sourceMappingURL=module.mjs.map
|