@flipdish/portal-library 7.12.1 → 8.0.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/README.md +22 -4
- package/dist/components/atoms/Tag/index.cjs.js +1 -1
- package/dist/components/atoms/Tag/index.cjs.js.map +1 -1
- package/dist/components/atoms/Tag/index.d.ts +3 -7
- package/dist/components/atoms/Tag/index.js +1 -1
- package/dist/components/atoms/Tag/index.js.map +1 -1
- package/dist/components/atoms/TextArea/index.cjs.js.map +1 -1
- package/dist/components/atoms/TextArea/index.d.ts +1 -1
- package/dist/components/atoms/TextArea/index.js.map +1 -1
- package/dist/components/atoms/TextField/index.cjs.js +1 -1
- package/dist/components/atoms/TextField/index.cjs.js.map +1 -1
- package/dist/components/atoms/TextField/index.d.ts +5 -22
- package/dist/components/atoms/TextField/index.js +1 -1
- package/dist/components/atoms/TextField/index.js.map +1 -1
- package/dist/components/molecules/AlertGlobal/index.cjs.js +1 -1
- package/dist/components/molecules/AlertGlobal/index.cjs.js.map +1 -1
- package/dist/components/molecules/AlertGlobal/index.d.ts +1 -8
- package/dist/components/molecules/AlertGlobal/index.js +1 -1
- package/dist/components/molecules/AlertGlobal/index.js.map +1 -1
- package/dist/components/molecules/Autocomplete/index.cjs.js +1 -1
- package/dist/components/molecules/Autocomplete/index.cjs.js.map +1 -1
- package/dist/components/molecules/Autocomplete/index.d.ts +1 -16
- package/dist/components/molecules/Autocomplete/index.js +1 -1
- package/dist/components/molecules/Autocomplete/index.js.map +1 -1
- package/dist/components/molecules/Pagination/index.cjs.js +1 -1
- package/dist/components/molecules/Pagination/index.cjs.js.map +1 -1
- package/dist/components/molecules/Pagination/index.d.ts +1 -12
- package/dist/components/molecules/Pagination/index.js +1 -1
- package/dist/components/molecules/Pagination/index.js.map +1 -1
- package/dist/components/molecules/QuantitySelector/index.cjs.js +1 -1
- package/dist/components/molecules/QuantitySelector/index.cjs.js.map +1 -1
- package/dist/components/molecules/QuantitySelector/index.d.ts +1 -7
- package/dist/components/molecules/QuantitySelector/index.js +1 -1
- package/dist/components/molecules/QuantitySelector/index.js.map +1 -1
- package/dist/components/molecules/Rating/index.cjs.js +1 -1
- package/dist/components/molecules/Rating/index.cjs.js.map +1 -1
- package/dist/components/molecules/Rating/index.d.ts +0 -15
- package/dist/components/molecules/Rating/index.js +1 -1
- package/dist/components/molecules/Rating/index.js.map +1 -1
- package/dist/components/molecules/SearchInput/index.cjs.js +1 -1
- package/dist/components/molecules/SearchInput/index.cjs.js.map +1 -1
- package/dist/components/molecules/SearchInput/index.d.ts +1 -5
- package/dist/components/molecules/SearchInput/index.js +1 -1
- package/dist/components/molecules/SearchInput/index.js.map +1 -1
- package/dist/components/organisms/FileUpload/components/FileDropZone.cjs.js +1 -1
- package/dist/components/organisms/FileUpload/components/FileDropZone.cjs.js.map +1 -1
- package/dist/components/organisms/FileUpload/components/FileDropZone.js +1 -1
- package/dist/components/organisms/FileUpload/components/FileDropZone.js.map +1 -1
- package/dist/components/organisms/Heading/Heading.cjs.js +1 -1
- package/dist/components/organisms/Heading/Heading.cjs.js.map +1 -1
- package/dist/components/organisms/Heading/Heading.d.ts +0 -1
- package/dist/components/organisms/Heading/Heading.js +1 -1
- package/dist/components/organisms/Heading/Heading.js.map +1 -1
- package/dist/localization/de.json.cjs.js +1 -1
- package/dist/localization/de.json.js +1 -1
- package/dist/localization/en-US.json.cjs.js +1 -1
- package/dist/localization/en-US.json.js +1 -1
- package/dist/localization/en.json.cjs.js +1 -1
- package/dist/localization/en.json.d.ts +116 -1
- package/dist/localization/en.json.js +1 -1
- package/dist/localization/es-MX.json.cjs.js +1 -1
- package/dist/localization/es-MX.json.js +1 -1
- package/dist/localization/es.json.cjs.js +1 -1
- package/dist/localization/es.json.js +1 -1
- package/dist/localization/fr.json.cjs.js +1 -1
- package/dist/localization/fr.json.js +1 -1
- package/dist/localization/it.json.cjs.js +1 -1
- package/dist/localization/it.json.js +1 -1
- package/dist/localization/nl.json.cjs.js +1 -1
- package/dist/localization/nl.json.js +1 -1
- package/dist/localization/pt.json.cjs.js +1 -1
- package/dist/localization/pt.json.js +1 -1
- package/dist/providers/TranslationProvider.cjs.js.map +1 -1
- package/dist/providers/TranslationProvider.d.ts +6 -2
- package/dist/providers/TranslationProvider.js +1 -1
- package/dist/providers/TranslationProvider.js.map +1 -1
- package/dist/utilities/renderUtilities.cjs.js +1 -1
- package/dist/utilities/renderUtilities.cjs.js.map +1 -1
- package/dist/utilities/renderUtilities.d.ts +14 -1
- package/dist/utilities/renderUtilities.js +1 -1
- package/dist/utilities/renderUtilities.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{jsxs as e,jsx as
|
|
1
|
+
import{jsxs as e,jsx as t}from"react/jsx-runtime";import"react";import"@mui/material/Pagination";import a from"@mui/material/usePagination/usePagination";import i from"@mui/material/Box";import{Button as r}from"../../atoms/Button/index.js";import{IconButton as n}from"../../atoms/IconButton/index.js";import o from"@mui/material/Typography";import s from"@mui/material/Stack";import d from"../../../icons/ArrowLeft02/index.js";import l from"../../../icons/ArrowRight02/index.js";import{useTranslation as p}from"../../../providers/TranslationProvider.js";import{styled as c}from"@mui/material/styles";import m from"@mui/material/useMediaQuery";import g from"@mui/material/styles/useTheme";const u=c("nav")((({theme:e})=>({display:"flex",flexDirection:"row",alignItems:"center",width:"100%",justifyContent:"space-between",padding:e.spacing(.5),gap:e.spacing(2)}))),b=c("nav")((({theme:e,willRenderRowsPerPage:t})=>({display:"flex",flexDirection:"row",alignItems:"center",width:"100%",justifyContent:t?"space-between":"center",paddingLeft:e.spacing(1),paddingRight:e.spacing(1),gap:e.spacing(2)}))),x=c(n)((({theme:e})=>({borderRadius:e.radius["radius-8"],border:`1px solid ${e.palette.semantic.stroke["stroke-strong"]}`,"&:hover":{borderRadius:e.radius["radius-8"],border:`1px solid ${e.palette.semantic.stroke["stroke-strong"]}`},"&:focus":{borderRadius:e.radius["radius-8"],border:`1px solid ${e.palette.semantic.stroke["stroke-strong"]}`},"&:active":{borderRadius:e.radius["radius-8"],border:`1px solid ${e.palette.semantic.stroke["stroke-strong"]}`},"&:disabled":{borderRadius:e.radius["radius-8"],border:`1px solid ${e.palette.semantic.stroke["stroke-strong"]}`}}))),f=c(o)((({theme:e})=>({color:e.palette.semantic.text["text-weak"]}))),k=c(r)((()=>({textDecoration:"none"}))),h=(e,t,a,i)=>i("Showing_start_end_of_total",{start:t*(e-1)+1,end:t*e,total:t*a}),y=({pageCount:r=1,boundaryPageCount:o=1,disabled:c=!1,rowsPerPage:y,page:v,onChange:w,size:C="medium"})=>{const P=m((e=>e.breakpoints.down("tablet"))),{items:_}=a({boundaryCount:o,count:r,disabled:c,onChange:w,page:v}),z=g(),{translate:R}=p();if(P){const a=_.find((e=>"previous"===e.type)),i=_.find((e=>"next"===e.type));return e(u,{children:[!!a&&t(n,{"aria-label":R("Previous_page"),"data-testid":"pagination-prev-btn-mobile",disabled:c||a.disabled,onClick:a.onClick,size:C,tone:"neutral",variant:"tertiary",children:t(d,{color:z.palette.semantic.text["text-weak"],size:"md"})}),t(s,{alignItems:"center",flexGrow:1,children:t(f,{"data-testid":"pagination-page-mobile",variant:"captionWeak",children:R("page_of_count",{page:v,count:r})})}),!!i&&t(n,{"aria-label":R("Next_page"),"data-testid":"pagination-next-btn-mobile",disabled:c||i.disabled,onClick:i.onClick,size:C,tone:"neutral",variant:"tertiary",children:t(l,{color:z.palette.semantic.text["text-weak"],size:"md"})})]})}return e(b,{willRenderRowsPerPage:"number"==typeof y,children:[t(s,{alignItems:"center",direction:"row",gap:z.spacing(1),children:_.map((e=>"previous"===e.type?t(k,{"aria-label":R("Previous_page"),"data-testid":"pagination-prev-btn",disabled:c||e.disabled,fdKey:"pagination-prev-btn",onClick:e.onClick,size:C,startIcon:t(d,{color:z.palette.semantic.text["text-weak"],size:"md"}),tone:"neutral",variant:"tertiary",children:t(f,{variant:"captionWeak",children:R("Previous")})},e.type):"start-ellipsis"===e.type||"end-ellipsis"===e.type?t(i,{children:t(f,{"data-testid":"pagination-ellipsis",variant:"captionWeak",children:"..."})},e.type):"next"===e.type?t(k,{"aria-label":R("Next_page"),"data-testid":"pagination-next-btn",disabled:c||e.disabled,endIcon:t(l,{color:z.palette.semantic.text["text-weak"],size:"md"}),fdKey:"pagination-next-btn",onClick:e.onClick,size:C,tone:"neutral",variant:"tertiary",children:t(f,{variant:"captionWeak",children:R("Next")})},e.type):e.selected?t(x,{"aria-current":"page","aria-label":R("Selected_page_number",{number:String(e.page)}),"data-testid":"pagination-current-page",disabled:c||e.disabled,fdKey:"pagination-page",onClick:e.onClick,size:C,tone:"neutral",variant:"tertiary",children:t(f,{variant:"captionWeak",children:e.page})},e.page):t(n,{"aria-label":R("Page_number",{number:String(e.page)}),"data-testid":"pagination-page",disabled:c||e.disabled,fdKey:"pagination-page",onClick:e.onClick,size:C,tone:"neutral",variant:"tertiary",children:t(f,{variant:"captionWeak",children:e.page})},e.page)))}),t(s,{alignItems:"center",flexGrow:0,children:"number"==typeof y&&t(f,{"data-testid":"pagination-page-range",variant:"captionWeak",children:h(v,y,r,R)})})]})};export{y as Pagination,y as default};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../../../src/components/molecules/Pagination/index.tsx"],"sourcesContent":["import React from \"react\";\n\nimport { type PaginationProps as MUIPaginationProps } from \"@mui/material/Pagination\";\nimport usePagination from \"@mui/material/usePagination/usePagination\";\n\nimport Box from \"@fd/components/atoms/Box\";\nimport Button from \"@fd/components/atoms/Button\";\nimport IconButton from \"@fd/components/atoms/IconButton\";\nimport Typography from \"@fd/components/atoms/Typography\";\nimport Stack from '@fd/components/molecules/Stack';\nimport ArrowLeft02Icon from \"@fd/icons/ArrowLeft02\";\nimport ArrowRight02Icon from \"@fd/icons/ArrowRight02\";\nimport styled from \"@fd/utilities/styledUtilities\"\nimport useMediaQuery from \"@fd/utilities/useMediaQuery\";\nimport useTheme from \"@fd/utilities/useTheme\";\n\nconst StyledPaginationMobileContainer = styled('nav')(({theme}) => ({\n display: 'flex',\n flexDirection: 'row',\n alignItems: 'center',\n width: '100%',\n justifyContent: 'space-between',\n padding: theme.spacing(0.5),\n gap: theme.spacing(2),\n}));\n\nconst StyledPaginationDesktopContainer = styled('nav')<{ willRenderRowsPerPage: boolean }>(({theme, willRenderRowsPerPage}) => ({\n display: 'flex',\n flexDirection: 'row',\n alignItems: 'center',\n width: '100%',\n justifyContent: willRenderRowsPerPage ? 'space-between' : 'center',\n paddingLeft: theme.spacing(1),\n paddingRight: theme.spacing(1),\n gap: theme.spacing(2),\n}));\n\nconst StyledSelectedPageBtn = styled(IconButton)(({ theme }) =>({\n borderRadius: theme.radius['radius-8'],\n border: `1px solid ${theme.palette.semantic.stroke['stroke-strong']}`,\n '&:hover': {\n borderRadius: theme.radius['radius-8'],\n border: `1px solid ${theme.palette.semantic.stroke['stroke-strong']}`,\n },\n '&:focus': {\n borderRadius: theme.radius['radius-8'],\n border: `1px solid ${theme.palette.semantic.stroke['stroke-strong']}`,\n },\n '&:active': {\n borderRadius: theme.radius['radius-8'],\n border: `1px solid ${theme.palette.semantic.stroke['stroke-strong']}`,\n },\n '&:disabled': {\n borderRadius: theme.radius['radius-8'],\n border: `1px solid ${theme.palette.semantic.stroke['stroke-strong']}`,\n },\n}));\n\nconst StyledTypography = styled(Typography)(({ theme }) => ({\n color: theme.palette.semantic.text['text-weak'],\n}))\n\nconst StyledPrevNextBtn = styled(Button)(() =>({\n textDecoration: 'none',\n}))\n\nconst getPaginationStatus = (page: number, rowsPerPage: number, pageCount: number, translations: PaginationTranslations): string => {\n const start = rowsPerPage * (page - 1) + 1;\n const end = rowsPerPage * page;\n return `${translations.showing} ${start} - ${end} ${translations.of} ${rowsPerPage * pageCount}`;\n}\n\nexport interface PaginationTranslations {\n previousPageAriaLabel: string;\n nextPageAriaLabel: string;\n selectedPageAriaLabel: string;\n paginationPageAriaLabel: string;\n next: string;\n previous: string;\n showing: string;\n of: string;\n}\n\nexport interface PaginationProps {\n pageCount?: number;\n boundaryPageCount?: number;\n disabled?: boolean;\n rowsPerPage?: number;\n size?: 'medium' | 'small';\n page: number;\n onChange: MUIPaginationProps['onChange'];\n translations: PaginationTranslations\n}\n\nexport const Pagination: React.FC<PaginationProps> = ({\n pageCount = 1,\n boundaryPageCount = 1,\n disabled = false,\n rowsPerPage,\n page,\n onChange,\n size = 'medium',\n translations,\n}) => {\n const isMobile = useMediaQuery((theme) => theme.breakpoints.down('tablet'));\n const { items } = usePagination({\n boundaryCount: boundaryPageCount,\n count: pageCount,\n disabled,\n onChange,\n page\n });\n\n const theme = useTheme();\n\n if (isMobile) {\n const previousBtn = items.find(item => item.type === 'previous');\n const nextBtn = items.find(item => item.type === 'next');\n return (\n <StyledPaginationMobileContainer>\n {\n !!previousBtn &&\n <IconButton\n aria-label={translations.previousPageAriaLabel}\n data-testid=\"pagination-prev-btn-mobile\"\n disabled={disabled || previousBtn.disabled}\n onClick={previousBtn.onClick}\n size={size}\n tone='neutral'\n variant='tertiary'\n >\n <ArrowLeft02Icon color={theme.palette.semantic.text[\"text-weak\"]} size=\"md\" />\n </IconButton>\n }\n <Stack alignItems='center' flexGrow={1}>\n <StyledTypography data-testid=\"pagination-page-mobile\" variant=\"captionWeak\">\n {page} {translations.of} {pageCount}\n </StyledTypography>\n </Stack>\n {\n !!nextBtn &&\n <IconButton\n aria-label={translations.nextPageAriaLabel}\n data-testid=\"pagination-next-btn-mobile\"\n disabled={disabled || nextBtn.disabled}\n onClick={nextBtn.onClick}\n size={size}\n tone='neutral'\n variant='tertiary'\n >\n <ArrowRight02Icon color={theme.palette.semantic.text[\"text-weak\"]} size=\"md\" />\n </IconButton>\n }\n\n </StyledPaginationMobileContainer>\n );\n }\n\n const willRenderRowsPerPage = typeof rowsPerPage === \"number\";\n\n return (\n <StyledPaginationDesktopContainer willRenderRowsPerPage={willRenderRowsPerPage}>\n <Stack\n alignItems='center'\n direction='row'\n gap={theme.spacing(1)}\n >\n {items.map((item) => {\n if (item.type === 'previous') {\n return (\n <StyledPrevNextBtn\n key={item.type}\n aria-label={translations.previousPageAriaLabel}\n data-testid=\"pagination-prev-btn\"\n disabled={disabled || item.disabled}\n fdKey=\"pagination-prev-btn\"\n onClick={item.onClick}\n size={size}\n startIcon={<ArrowLeft02Icon color={theme.palette.semantic.text[\"text-weak\"]} size=\"md\" />}\n tone=\"neutral\"\n variant=\"tertiary\"\n >\n <StyledTypography variant=\"captionWeak\">\n {translations.previous}\n </StyledTypography>\n </StyledPrevNextBtn>\n )\n }\n\n if (item.type === 'start-ellipsis' || item.type === 'end-ellipsis') {\n return (\n <Box\n key={item.type}\n >\n <StyledTypography data-testid=\"pagination-ellipsis\" variant=\"captionWeak\">\n ...\n </StyledTypography>\n\n </Box>\n )\n }\n\n if (item.type === \"next\") {\n return (\n <StyledPrevNextBtn\n key={item.type}\n aria-label={translations.nextPageAriaLabel}\n data-testid=\"pagination-next-btn\"\n disabled={disabled || item.disabled}\n endIcon={<ArrowRight02Icon color={theme.palette.semantic.text[\"text-weak\"]} size=\"md\" />}\n fdKey=\"pagination-next-btn\"\n onClick={item.onClick}\n size={size}\n tone=\"neutral\"\n variant=\"tertiary\"\n >\n <StyledTypography variant=\"captionWeak\">\n {translations.next}\n </StyledTypography>\n </StyledPrevNextBtn>\n )\n }\n\n if (item.selected) {\n return (\n <StyledSelectedPageBtn\n key={item.page}\n aria-label={translations.selectedPageAriaLabel}\n data-testid=\"pagination-current-page\"\n disabled={disabled || item.disabled}\n fdKey=\"pagination-page\"\n onClick={item.onClick}\n size={size}\n tone=\"neutral\"\n variant=\"tertiary\"\n >\n <StyledTypography variant=\"captionWeak\">\n {item.page}\n </StyledTypography>\n </StyledSelectedPageBtn>\n )\n }\n\n return (\n <IconButton\n key={item.page}\n aria-label={`${translations.paginationPageAriaLabel} ${item.page}`}\n data-testid=\"pagination-page\"\n disabled={disabled || item.disabled}\n fdKey=\"pagination-page\"\n onClick={item.onClick}\n size={size}\n tone=\"neutral\"\n variant=\"tertiary\"\n >\n <StyledTypography variant=\"captionWeak\">\n {item.page}\n </StyledTypography>\n </IconButton>\n )\n })}\n </Stack>\n <Stack\n alignItems='center'\n flexGrow={0}\n >\n {typeof rowsPerPage === \"number\" && (\n <StyledTypography data-testid=\"pagination-page-range\" variant=\"captionWeak\">\n {getPaginationStatus(page, rowsPerPage, pageCount, translations)}\n </StyledTypography>\n )}\n </Stack>\n </StyledPaginationDesktopContainer>\n );\n}\n\nexport default Pagination;"],"names":["StyledPaginationMobileContainer","styled","theme","display","flexDirection","alignItems","width","justifyContent","padding","spacing","gap","StyledPaginationDesktopContainer","willRenderRowsPerPage","paddingLeft","paddingRight","StyledSelectedPageBtn","IconButton","borderRadius","radius","border","palette","semantic","stroke","StyledTypography","Typography","color","text","StyledPrevNextBtn","Button","textDecoration","getPaginationStatus","page","rowsPerPage","pageCount","translations","start","end","showing","of","Pagination","boundaryPageCount","disabled","onChange","size","isMobile","useMediaQuery","breakpoints","down","items","usePagination","boundaryCount","count","useTheme","previousBtn","find","item","type","nextBtn","_jsxs","children","_jsx","previousPageAriaLabel","onClick","tone","variant","ArrowLeft02Icon","Stack","flexGrow","nextPageAriaLabel","ArrowRight02Icon","direction","map","fdKey","startIcon","previous","Box","endIcon","next","selected","selectedPageAriaLabel","paginationPageAriaLabel"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../../src/components/molecules/Pagination/index.tsx"],"sourcesContent":["import React from 'react';\n\nimport { type PaginationProps as MUIPaginationProps } from '@mui/material/Pagination';\nimport usePagination from '@mui/material/usePagination/usePagination';\n\nimport Box from '@fd/components/atoms/Box';\nimport Button from '@fd/components/atoms/Button';\nimport IconButton from '@fd/components/atoms/IconButton';\nimport Typography from '@fd/components/atoms/Typography';\nimport Stack from '@fd/components/molecules/Stack';\nimport ArrowLeft02Icon from '@fd/icons/ArrowLeft02';\nimport ArrowRight02Icon from '@fd/icons/ArrowRight02';\nimport { type TranslateFunction, useTranslation } from '@fd/providers/TranslationProvider';\nimport styled from '@fd/utilities/styledUtilities';\nimport useMediaQuery from '@fd/utilities/useMediaQuery';\nimport useTheme from '@fd/utilities/useTheme';\n\nconst StyledPaginationMobileContainer = styled('nav')(({ theme }) => ({\n display: 'flex',\n flexDirection: 'row',\n alignItems: 'center',\n width: '100%',\n justifyContent: 'space-between',\n padding: theme.spacing(0.5),\n gap: theme.spacing(2),\n}));\n\nconst StyledPaginationDesktopContainer = styled('nav')<{ willRenderRowsPerPage: boolean }>(\n ({ theme, willRenderRowsPerPage }) => ({\n display: 'flex',\n flexDirection: 'row',\n alignItems: 'center',\n width: '100%',\n justifyContent: willRenderRowsPerPage ? 'space-between' : 'center',\n paddingLeft: theme.spacing(1),\n paddingRight: theme.spacing(1),\n gap: theme.spacing(2),\n }),\n);\n\nconst StyledSelectedPageBtn = styled(IconButton)(({ theme }) => ({\n borderRadius: theme.radius['radius-8'],\n border: `1px solid ${theme.palette.semantic.stroke['stroke-strong']}`,\n '&:hover': {\n borderRadius: theme.radius['radius-8'],\n border: `1px solid ${theme.palette.semantic.stroke['stroke-strong']}`,\n },\n '&:focus': {\n borderRadius: theme.radius['radius-8'],\n border: `1px solid ${theme.palette.semantic.stroke['stroke-strong']}`,\n },\n '&:active': {\n borderRadius: theme.radius['radius-8'],\n border: `1px solid ${theme.palette.semantic.stroke['stroke-strong']}`,\n },\n '&:disabled': {\n borderRadius: theme.radius['radius-8'],\n border: `1px solid ${theme.palette.semantic.stroke['stroke-strong']}`,\n },\n}));\n\nconst StyledTypography = styled(Typography)(({ theme }) => ({\n color: theme.palette.semantic.text['text-weak'],\n}));\n\nconst StyledPrevNextBtn = styled(Button)(() => ({\n textDecoration: 'none',\n}));\n\nconst getPaginationStatus = (\n page: number,\n rowsPerPage: number,\n pageCount: number,\n translate: TranslateFunction,\n): string => {\n const start = rowsPerPage * (page - 1) + 1;\n const end = rowsPerPage * page;\n return translate('Showing_start_end_of_total', { start, end, total: rowsPerPage * pageCount });\n};\n\nexport interface PaginationProps {\n pageCount?: number;\n boundaryPageCount?: number;\n disabled?: boolean;\n rowsPerPage?: number;\n size?: 'medium' | 'small';\n page: number;\n onChange: MUIPaginationProps['onChange'];\n}\n\nexport const Pagination: React.FC<PaginationProps> = ({\n pageCount = 1,\n boundaryPageCount = 1,\n disabled = false,\n rowsPerPage,\n page,\n onChange,\n size = 'medium',\n}) => {\n const isMobile = useMediaQuery((theme) => theme.breakpoints.down('tablet'));\n const { items } = usePagination({\n boundaryCount: boundaryPageCount,\n count: pageCount,\n disabled,\n onChange,\n page,\n });\n\n const theme = useTheme();\n const { translate } = useTranslation();\n\n if (isMobile) {\n const previousBtn = items.find((item) => item.type === 'previous');\n const nextBtn = items.find((item) => item.type === 'next');\n return (\n <StyledPaginationMobileContainer>\n {!!previousBtn && (\n <IconButton\n aria-label={translate('Previous_page')}\n data-testid=\"pagination-prev-btn-mobile\"\n disabled={disabled || previousBtn.disabled}\n onClick={previousBtn.onClick}\n size={size}\n tone=\"neutral\"\n variant=\"tertiary\"\n >\n <ArrowLeft02Icon color={theme.palette.semantic.text['text-weak']} size=\"md\" />\n </IconButton>\n )}\n <Stack alignItems=\"center\" flexGrow={1}>\n <StyledTypography data-testid=\"pagination-page-mobile\" variant=\"captionWeak\">\n {translate('page_of_count', { page, count: pageCount })}\n </StyledTypography>\n </Stack>\n {!!nextBtn && (\n <IconButton\n aria-label={translate('Next_page')}\n data-testid=\"pagination-next-btn-mobile\"\n disabled={disabled || nextBtn.disabled}\n onClick={nextBtn.onClick}\n size={size}\n tone=\"neutral\"\n variant=\"tertiary\"\n >\n <ArrowRight02Icon color={theme.palette.semantic.text['text-weak']} size=\"md\" />\n </IconButton>\n )}\n </StyledPaginationMobileContainer>\n );\n }\n\n const willRenderRowsPerPage = typeof rowsPerPage === 'number';\n\n return (\n <StyledPaginationDesktopContainer willRenderRowsPerPage={willRenderRowsPerPage}>\n <Stack alignItems=\"center\" direction=\"row\" gap={theme.spacing(1)}>\n {items.map((item) => {\n if (item.type === 'previous') {\n return (\n <StyledPrevNextBtn\n key={item.type}\n aria-label={translate('Previous_page')}\n data-testid=\"pagination-prev-btn\"\n disabled={disabled || item.disabled}\n fdKey=\"pagination-prev-btn\"\n onClick={item.onClick}\n size={size}\n startIcon={<ArrowLeft02Icon color={theme.palette.semantic.text['text-weak']} size=\"md\" />}\n tone=\"neutral\"\n variant=\"tertiary\"\n >\n <StyledTypography variant=\"captionWeak\">{translate('Previous')}</StyledTypography>\n </StyledPrevNextBtn>\n );\n }\n\n if (item.type === 'start-ellipsis' || item.type === 'end-ellipsis') {\n return (\n <Box key={item.type}>\n <StyledTypography data-testid=\"pagination-ellipsis\" variant=\"captionWeak\">\n ...\n </StyledTypography>\n </Box>\n );\n }\n\n if (item.type === 'next') {\n return (\n <StyledPrevNextBtn\n key={item.type}\n aria-label={translate('Next_page')}\n data-testid=\"pagination-next-btn\"\n disabled={disabled || item.disabled}\n endIcon={<ArrowRight02Icon color={theme.palette.semantic.text['text-weak']} size=\"md\" />}\n fdKey=\"pagination-next-btn\"\n onClick={item.onClick}\n size={size}\n tone=\"neutral\"\n variant=\"tertiary\"\n >\n <StyledTypography variant=\"captionWeak\">{translate('Next')}</StyledTypography>\n </StyledPrevNextBtn>\n );\n }\n\n if (item.selected) {\n return (\n <StyledSelectedPageBtn\n key={item.page}\n aria-current=\"page\"\n aria-label={translate('Selected_page_number', { number: String(item.page) })}\n data-testid=\"pagination-current-page\"\n disabled={disabled || item.disabled}\n fdKey=\"pagination-page\"\n onClick={item.onClick}\n size={size}\n tone=\"neutral\"\n variant=\"tertiary\"\n >\n <StyledTypography variant=\"captionWeak\">{item.page}</StyledTypography>\n </StyledSelectedPageBtn>\n );\n }\n\n return (\n <IconButton\n key={item.page}\n aria-label={translate('Page_number', { number: String(item.page) })}\n data-testid=\"pagination-page\"\n disabled={disabled || item.disabled}\n fdKey=\"pagination-page\"\n onClick={item.onClick}\n size={size}\n tone=\"neutral\"\n variant=\"tertiary\"\n >\n <StyledTypography variant=\"captionWeak\">{item.page}</StyledTypography>\n </IconButton>\n );\n })}\n </Stack>\n <Stack alignItems=\"center\" flexGrow={0}>\n {typeof rowsPerPage === 'number' && (\n <StyledTypography data-testid=\"pagination-page-range\" variant=\"captionWeak\">\n {getPaginationStatus(page, rowsPerPage, pageCount, translate)}\n </StyledTypography>\n )}\n </Stack>\n </StyledPaginationDesktopContainer>\n );\n};\n\nexport default Pagination;\n"],"names":["StyledPaginationMobileContainer","styled","theme","display","flexDirection","alignItems","width","justifyContent","padding","spacing","gap","StyledPaginationDesktopContainer","willRenderRowsPerPage","paddingLeft","paddingRight","StyledSelectedPageBtn","IconButton","borderRadius","radius","border","palette","semantic","stroke","StyledTypography","Typography","color","text","StyledPrevNextBtn","Button","textDecoration","getPaginationStatus","page","rowsPerPage","pageCount","translate","start","end","total","Pagination","boundaryPageCount","disabled","onChange","size","isMobile","useMediaQuery","breakpoints","down","items","usePagination","boundaryCount","count","useTheme","useTranslation","previousBtn","find","item","type","nextBtn","_jsxs","children","_jsx","onClick","tone","variant","ArrowLeft02Icon","Stack","flexGrow","ArrowRight02Icon","direction","map","fdKey","startIcon","Box","endIcon","selected","number","String"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("react/jsx-runtime"),t=require("@mui/material/InputLabel"),r=require("@mui/material/styles"),i=require("../../../utilities/numericUtilities.cjs.js"),
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("react/jsx-runtime"),t=require("@mui/material/InputLabel"),r=require("@mui/material/styles"),i=require("../../../providers/TranslationProvider.cjs.js"),o=require("../../../utilities/numericUtilities.cjs.js"),s=require("../../../icons/Add/index.cjs.js"),n=require("../../../icons/CancelCircle/index.cjs.js"),a=require("../../../icons/Remove/index.cjs.js"),l=require("@mui/material/Box"),d=require("../../atoms/IconButton/index.cjs.js"),u=require("../../atoms/TextField/index.cjs.js"),c=require("@mui/material/Typography");const p=r.styled(l)((()=>({display:"flex",flexDirection:"column"}))),b=r.styled(l,{shouldForwardProp:e=>!["isDisabled","isError"].includes(e)})((({theme:e,isDisabled:t,isError:r})=>({display:"flex",alignItems:"stretch",boxShadow:e.customShadows?.raised,border:(t?"1px":r?"2px":"1px")+" solid",borderColor:t?e.palette.semantic.stroke["stroke-disabled"]:r?e.palette.semantic.stroke["stroke-error-strong"]:e.palette.semantic.stroke["stroke-strong"],borderRadius:e.radius["radius-8"],overflow:"visible",position:"relative",backgroundColor:e.palette.semantic.fill["fill-inverse-strong"],marginTop:e.spacing(.5)}))),m=r.styled(d.IconButton)((({theme:e})=>({border:"none",boxShadow:"none",borderRadius:0,padding:0,"&&:focus, &&:focus-visible":{outline:"none",borderRadius:0,zIndex:2},"&&:focus::after, &&:focus-visible::after":{content:'""',position:"absolute",top:-4.5,right:-2.5,bottom:-4.5,left:-4.5,border:`2px solid ${e.palette.semantic.stroke["stroke-focus"]}`,borderRadius:0,pointerEvents:"none",zIndex:3},"&&:first-of-type:focus::after, &&:first-of-type:focus-visible::after":{borderTopLeftRadius:e.radius["radius-12"],borderBottomLeftRadius:e.radius["radius-12"]},"&&:last-of-type:focus::after, &&:last-of-type:focus-visible::after":{borderTopRightRadius:e.radius["radius-12"],borderBottomRightRadius:e.radius["radius-12"],left:-2.5,right:-4.5},"&:hover":{boxShadow:"none",borderRadius:0},"&.Mui-disabled":{backgroundColor:e.palette.semantic.fill["fill-inverse-strong"],border:"none",boxShadow:"none"},"&.Mui-disabled > svg":{color:e.palette.semantic.icon["icon-disabled"]}}))),x=r.styled(l)((({theme:e})=>({display:"flex",alignItems:"stretch",width:72,minWidth:56,backgroundColor:e.palette.semantic.fill["fill-inverse-strong"],position:"relative",borderRadius:0,"&:focus-within::after":{content:'""',position:"absolute",top:-4,right:-2,bottom:-4,left:-2,border:`2px solid ${e.palette.semantic.stroke["stroke-focus"]}`,borderRadius:0,pointerEvents:"none",zIndex:3}}))),f=r.styled(u.TextField,{shouldForwardProp:e=>"isError"!==e})((({theme:e,isError:t=!1})=>({marginTop:"-4px",marginBottom:"-4px",'& [role="textbox"], & input':{textAlign:"center",outline:"none"},"& .MuiOutlinedInput-root":{border:"none",boxShadow:"none",outline:"none",borderRadius:0,backgroundColor:t?e.palette.semantic.fill["fill-error-weak"]:"transparent","&.Mui-disabled":{backgroundColor:"transparent"},"&.Mui-focused":{outline:"none !important"},"&:focus-within":{outline:"none !important"},"&:focus":{outline:"none !important",boxShadow:"none !important"},"&:hover":{background:"none !important",backgroundImage:"none !important"},"& fieldset":{border:"0 !important",borderRadius:0},"&:hover fieldset":{border:"0 !important"},"&.Mui-focused fieldset":{border:"0"}},"& .MuiOutlinedInput-input":{outline:"none",boxShadow:"none","&:focus":{outline:"none",boxShadow:"none"},"&.Mui-disabled":{color:e.palette.semantic.text["text-disabled"],WebkitTextFillColor:e.palette.semantic.text["text-disabled"]}},"& input:focus":{outline:"none !important",boxShadow:"none !important"}}))),h=r.styled("div",{shouldForwardProp:e=>!["isDisabled","isError"].includes(e)})((({theme:e,isDisabled:t=!1,isError:r=!1})=>({width:1,alignSelf:"stretch",backgroundColor:t?e.palette.semantic.stroke["stroke-disabled"]:r?e.palette.semantic.stroke["stroke-error-strong"]:e.palette.semantic.stroke["stroke-strong"]}))),g=r.styled(n)((({theme:e})=>({color:e.palette.semantic.icon["icon-error"]}))),v=r.styled(l)((({theme:e})=>({display:"flex",alignItems:"center",gap:e.spacing(1),marginTop:e.spacing(.5)}))),y=r.styled(c)((({theme:e})=>({color:e.palette.semantic.text["text-error"]}))),j=r.styled(c,{shouldForwardProp:e=>"isDisabled"!==e})((({theme:e,isDisabled:t=!1})=>({color:t?e.palette.semantic.text["text-disabled"]:e.palette.semantic.text["text-weak"]}))),k=({value:r,onChange:n,min:l,max:d,step:u,disabled:c=!1,fdKey:k,label:w="",hint:q="",required:R=!1,valid:C=!0,errorMessage:S=""})=>{const{translate:I}=i.useTranslation(),D=!c&&r>l,E=!c&&r<d;return e.jsxs(p,{children:[w&&e.jsx(t,{disabled:c,htmlFor:k?`${k}-input`:"quantity-input",required:R,children:w}),q&&e.jsx(j,{isDisabled:c,variant:"caption",children:q}),!C&&!!S&&e.jsxs(v,{children:[e.jsx(g,{}),e.jsx(y,{variant:"captionStrong",children:S})]}),e.jsxs(b,{"data-fd":k,isDisabled:c,isError:!C,role:"group",children:[e.jsx(m,{"aria-label":I("Decrease_quantity"),disabled:!D,fdKey:k?`${k}-decrement`:void 0,onClick:()=>{if(!D)return;const e=o.clamp(r-u,l,d);void 0!==e&&n(e)},tone:C?"neutral":"destructive",variant:"tertiary",children:e.jsx(a,{})}),e.jsx(h,{isDisabled:c,isError:!C}),e.jsx(x,{children:e.jsx(f,{fullWidth:!0,"aria-label":I("Quantity"),disabled:c,fdKey:k?`${k}-input`:"quantity-input",isError:!C,label:"",onChange:e=>{const t=e.target.value;if(""===t)return void n(l);const r=o.clamp(Number(t),l,d);void 0!==r&&n(r)},value:String(r)})}),e.jsx(h,{isDisabled:c,isError:!C}),e.jsx(m,{"aria-label":I("Increase_quantity"),disabled:!E,fdKey:k?`${k}-increment`:void 0,onClick:()=>{if(!E)return;const e=o.clamp(r+u,l,d);void 0!==e&&n(e)},tone:C?"neutral":"destructive",variant:"tertiary",children:e.jsx(s,{})})]})]})};exports.QuantitySelector=k,exports.default=k;
|
|
2
2
|
//# sourceMappingURL=index.cjs.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs.js","sources":["../../../../src/components/molecules/QuantitySelector/index.tsx"],"sourcesContent":["import MuiInputLabel from '@mui/material/InputLabel';\nimport { styled } from '@mui/material/styles';\n\nimport { clamp } from '@fd/utilities/numericUtilities';\nimport type { ChangeEvent } from 'react';\n\nimport AddIcon from '../../../icons/Add';\nimport CancelCircleIcon from '../../../icons/CancelCircle';\nimport RemoveIcon from '../../../icons/Remove';\nimport Box from '../../atoms/Box';\nimport IconButton from '../../atoms/IconButton';\nimport TextField from '../../atoms/TextField';\nimport Typography from '../../atoms/Typography';\n\n/**\n * Props for the QuantitySelector component.\n * Controls a numeric input with increment/decrement buttons and min/max bounds.\n */\nexport interface QuantitySelectorProps {\n /** Current numeric value */\n value: number;\n /** Callback fired when the value changes */\n onChange: (nextValue: number) => void;\n /** Minimum allowed value */\n min: number;\n /** Maximum allowed value */\n max: number;\n /** Amount to increment/decrement on button clicks */\n step: number;\n /** Whether the control is disabled */\n disabled?: boolean;\n /** Unique identifier for testing/tracking */\n fdKey?: string;\n /** Label text displayed above the control */\n label?: string;\n /** Hint text displayed below the label */\n hint?: string;\n /** Whether the field is required */\n required?: boolean;\n /** Whether the current value is valid */\n valid?: boolean;\n /** Error message to display when valid is false */\n errorMessage?: string;\n /** Localized aria-label for the decrement button */\n decreaseAriaLabel: string;\n /** Localized aria-label for the increment button */\n increaseAriaLabel: string;\n /** Localized aria-label for the numeric input field */\n valueAriaLabel: string;\n}\n\nconst OuterContainer = styled(Box)(() => ({\n display: 'flex',\n flexDirection: 'column'\n}));\n\nconst InnerContainer = styled(Box, {\n shouldForwardProp: (prop) => !['isDisabled', 'isError'].includes(prop as string),\n})<{ isDisabled?: boolean; isError?: boolean }>(({ theme, isDisabled, isError }) => ({\n display: 'flex',\n alignItems: 'stretch',\n boxShadow: theme.customShadows?.raised,\n // Disabled state should overwrite all (including error thickness)\n border: `${isDisabled ? '1px' : isError ? '2px' : '1px'} solid`,\n borderColor: isDisabled\n ? theme.palette.semantic.stroke['stroke-disabled']\n : isError\n ? theme.palette.semantic.stroke['stroke-error-strong']\n : theme.palette.semantic.stroke['stroke-strong'],\n borderRadius: theme.radius['radius-8'],\n overflow: 'visible',\n position: 'relative',\n backgroundColor: theme.palette.semantic.fill['fill-inverse-strong'],\n marginTop: theme.spacing(0.5),\n}));\n\nconst ActionButton = styled(IconButton)(({ theme }) => ({\n border: 'none',\n boxShadow: 'none',\n borderRadius: 0,\n padding: 0,\n // Limit transitions to avoid perceived zoom effects\n // Robust square focus indicator drawn inside using a pseudo-element\n '&&:focus, &&:focus-visible': {\n outline: 'none',\n borderRadius: 0,\n zIndex: 2,\n },\n '&&:focus::after, &&:focus-visible::after': {\n content: '\"\"',\n position: 'absolute',\n top: -4.5,\n right: -2.5,\n bottom: -4.5,\n left: -4.5,\n border: `2px solid ${theme.palette.semantic.stroke['stroke-focus']}`,\n borderRadius: 0,\n pointerEvents: 'none',\n zIndex: 3,\n },\n // Round external corners when focusing the edge buttons\n '&&:first-of-type:focus::after, &&:first-of-type:focus-visible::after': {\n borderTopLeftRadius: theme.radius['radius-12'],\n borderBottomLeftRadius: theme.radius['radius-12'],\n },\n '&&:last-of-type:focus::after, &&:last-of-type:focus-visible::after': {\n borderTopRightRadius: theme.radius['radius-12'],\n borderBottomRightRadius: theme.radius['radius-12'],\n left: -2.5,\n right: -4.5,\n },\n '&:hover': {\n boxShadow: 'none',\n borderRadius: 0,\n },\n '&.Mui-disabled': {\n backgroundColor: theme.palette.semantic.fill['fill-inverse-strong'],\n border: 'none',\n boxShadow: 'none'\n },\n '&.Mui-disabled > svg': {\n color: theme.palette.semantic.icon['icon-disabled'],\n },\n}));\nconst ValueContainer = styled(Box)(({ theme }) => ({\n display: 'flex',\n alignItems: 'stretch',\n width: 72,\n minWidth: 56,\n backgroundColor: theme.palette.semantic.fill['fill-inverse-strong'],\n position: 'relative',\n borderRadius: 0,\n // Draw an outer square focus ring for the input area\n '&:focus-within::after': {\n content: '\"\"',\n position: 'absolute',\n top: -4,\n right: -2,\n bottom: -4,\n left: -2,\n border: `2px solid ${theme.palette.semantic.stroke['stroke-focus']}`,\n borderRadius: 0,\n pointerEvents: 'none',\n zIndex: 3,\n },\n}));\nconst ValueField = styled(TextField, {\n shouldForwardProp: (prop) => prop !== 'isError',\n})<{ isError?: boolean }>(({ theme, isError = false }) => ({\n marginTop: '-4px',\n marginBottom: '-4px',\n '& [role=\"textbox\"], & input': {\n textAlign: 'center',\n outline: 'none',\n },\n '& .MuiOutlinedInput-root': {\n border: 'none',\n boxShadow: 'none',\n outline: 'none',\n borderRadius: 0,\n backgroundColor: isError ? theme.palette.semantic.fill['fill-error-weak'] : 'transparent',\n // Ensure disabled overrides error background\n '&.Mui-disabled': {\n backgroundColor: 'transparent',\n },\n // Remove MUI's internal focus ring; the ring is applied on ValueContainer instead\n '&.Mui-focused': {\n outline: 'none !important',\n },\n '&:focus-within': {\n outline: 'none !important',\n },\n '&:focus': {\n outline: 'none !important',\n boxShadow: 'none !important',\n },\n '&:hover': {\n background: 'none !important',\n backgroundImage: 'none !important',\n },\n '& fieldset': {\n border: '0 !important',\n borderRadius: 0,\n },\n '&:hover fieldset': {\n border: '0 !important',\n },\n '&.Mui-focused fieldset': {\n border: '0',\n },\n },\n '& .MuiOutlinedInput-input': {\n outline: 'none',\n boxShadow: 'none',\n '&:focus': {\n outline: 'none',\n boxShadow: 'none',\n },\n '&.Mui-disabled': {\n color: theme.palette.semantic.text['text-disabled'],\n WebkitTextFillColor: theme.palette.semantic.text['text-disabled'],\n },\n },\n '& input:focus': {\n outline: 'none !important',\n boxShadow: 'none !important',\n },\n}));\n\nconst VerticalDivider = styled('div', {\n shouldForwardProp: (prop) => !['isDisabled', 'isError'].includes(prop as string),\n})<{ isDisabled?: boolean; isError?: boolean }>(({ theme, isDisabled = false, isError = false }) => ({\n width: 1,\n alignSelf: 'stretch',\n backgroundColor: isDisabled\n ? theme.palette.semantic.stroke['stroke-disabled']\n : isError\n ? theme.palette.semantic.stroke['stroke-error-strong']\n : theme.palette.semantic.stroke['stroke-strong'],\n}));\n\nconst ErrorIcon = styled(CancelCircleIcon)(({ theme }) => ({\n color: theme.palette.semantic.icon['icon-error'],\n}));\n\nconst ErrorRow = styled(Box)(({ theme }) => ({\n display: 'flex',\n alignItems: 'center',\n gap: theme.spacing(1),\n marginTop: theme.spacing(0.5)\n}));\n\nconst ErrorText = styled(Typography)(({ theme }) => ({\n color: theme.palette.semantic.text['text-error']\n}));\n\nconst HintText = styled(Typography, {\n shouldForwardProp: (prop) => prop !== 'isDisabled',\n})<{ isDisabled?: boolean }>(({ theme, isDisabled = false }) => ({\n color: isDisabled ? theme.palette.semantic.text['text-disabled'] : theme.palette.semantic.text['text-weak'],\n}));\n\n/**\n * A quantity selector component with increment/decrement buttons and a numeric input field.\n * Provides min/max clamping, step-based adjustments, and optional label/hint/error display.\n *\n * @param props - Component props\n * @returns A quantity selector control\n */\nexport const QuantitySelector = ({\n value,\n onChange,\n min,\n max,\n step,\n disabled = false,\n fdKey,\n label = \"\",\n hint = \"\",\n required = false,\n valid = true,\n errorMessage = '',\n decreaseAriaLabel,\n increaseAriaLabel,\n valueAriaLabel,\n}: QuantitySelectorProps): JSX.Element => {\n const canDecrement = !disabled && value > min;\n const canIncrement = !disabled && value < max;\n\n const handleDecrement = (): void => {\n if (!canDecrement) return;\n const next = clamp(value - step, min, max);\n if (next === undefined) return;\n onChange(next);\n };\n\n const handleIncrement = (): void => {\n if (!canIncrement) return;\n const next = clamp(value + step, min, max);\n if (next === undefined) return;\n onChange(next);\n };\n\n const handleInputChange = (e: ChangeEvent<HTMLInputElement>): void => {\n const raw = e.target.value;\n if (raw === '') {\n onChange(min);\n return;\n }\n const parsed = clamp(Number(raw), min, max)\n if (parsed === undefined) return;\n onChange(parsed);\n };\n\n return (\n <OuterContainer>\n {label && (\n <MuiInputLabel disabled={disabled} htmlFor={fdKey ? `${fdKey}-input` : 'quantity-input'} required={required}>\n {label}\n </MuiInputLabel>\n\n )}\n {hint && (\n <HintText isDisabled={disabled} variant=\"caption\">\n {hint}\n </HintText>)}\n {!valid && !!errorMessage && (\n <ErrorRow>\n <ErrorIcon />\n <ErrorText variant=\"captionStrong\">\n {errorMessage}\n </ErrorText>\n </ErrorRow>\n )}\n <InnerContainer data-fd={fdKey} isDisabled={disabled} isError={!valid} role=\"group\">\n <ActionButton\n aria-label={decreaseAriaLabel}\n disabled={!canDecrement}\n fdKey={fdKey ? `${fdKey}-decrement` : undefined}\n onClick={handleDecrement}\n tone={valid ? 'neutral' : 'destructive'}\n variant=\"tertiary\"\n >\n <RemoveIcon />\n </ActionButton>\n <VerticalDivider isDisabled={disabled} isError={!valid} />\n <ValueContainer>\n <ValueField\n fullWidth\n aria-label={valueAriaLabel}\n disabled={disabled}\n fdKey={fdKey ? `${fdKey}-input` : 'quantity-input'}\n isError={!valid}\n label=\"\"\n onChange={handleInputChange}\n value={String(value)}\n />\n </ValueContainer>\n <VerticalDivider isDisabled={disabled} isError={!valid} />\n <ActionButton\n aria-label={increaseAriaLabel}\n disabled={!canIncrement}\n fdKey={fdKey ? `${fdKey}-increment` : undefined}\n onClick={handleIncrement}\n tone={valid ? 'neutral' : 'destructive'}\n variant=\"tertiary\"\n >\n <AddIcon />\n </ActionButton>\n </InnerContainer>\n </OuterContainer>\n );\n};\n\nexport default QuantitySelector;\n"],"names":["OuterContainer","styled","Box","display","flexDirection","InnerContainer","shouldForwardProp","prop","includes","theme","isDisabled","isError","alignItems","boxShadow","customShadows","raised","border","borderColor","palette","semantic","stroke","borderRadius","radius","overflow","position","backgroundColor","fill","marginTop","spacing","ActionButton","IconButton","padding","outline","zIndex","content","top","right","bottom","left","pointerEvents","borderTopLeftRadius","borderBottomLeftRadius","borderTopRightRadius","borderBottomRightRadius","color","icon","ValueContainer","width","minWidth","ValueField","TextField","marginBottom","textAlign","background","backgroundImage","text","WebkitTextFillColor","VerticalDivider","alignSelf","ErrorIcon","CancelCircleIcon","ErrorRow","gap","ErrorText","Typography","HintText","QuantitySelector","value","onChange","min","max","step","disabled","fdKey","label","hint","required","valid","errorMessage","decreaseAriaLabel","increaseAriaLabel","valueAriaLabel","canDecrement","canIncrement","_jsxs","children","_jsx","MuiInputLabel","htmlFor","variant","role","undefined","onClick","next","clamp","tone","RemoveIcon","fullWidth","e","raw","target","parsed","Number","String","AddIcon"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":["../../../../src/components/molecules/QuantitySelector/index.tsx"],"sourcesContent":["import MuiInputLabel from '@mui/material/InputLabel';\nimport { styled } from '@mui/material/styles';\n\nimport { useTranslation } from '@fd/providers/TranslationProvider';\nimport { clamp } from '@fd/utilities/numericUtilities';\nimport type { ChangeEvent } from 'react';\n\nimport AddIcon from '../../../icons/Add';\nimport CancelCircleIcon from '../../../icons/CancelCircle';\nimport RemoveIcon from '../../../icons/Remove';\nimport Box from '../../atoms/Box';\nimport IconButton from '../../atoms/IconButton';\nimport TextField from '../../atoms/TextField';\nimport Typography from '../../atoms/Typography';\n\n/**\n * Props for the QuantitySelector component.\n * Controls a numeric input with increment/decrement buttons and min/max bounds.\n */\nexport interface QuantitySelectorProps {\n /** Current numeric value */\n value: number;\n /** Callback fired when the value changes */\n onChange: (nextValue: number) => void;\n /** Minimum allowed value */\n min: number;\n /** Maximum allowed value */\n max: number;\n /** Amount to increment/decrement on button clicks */\n step: number;\n /** Whether the control is disabled */\n disabled?: boolean;\n /** Unique identifier for testing/tracking */\n fdKey?: string;\n /** Label text displayed above the control */\n label?: string;\n /** Hint text displayed below the label */\n hint?: string;\n /** Whether the field is required */\n required?: boolean;\n /** Whether the current value is valid */\n valid?: boolean;\n /** Error message to display when valid is false */\n errorMessage?: string;\n}\n\nconst OuterContainer = styled(Box)(() => ({\n display: 'flex',\n flexDirection: 'column',\n}));\n\nconst InnerContainer = styled(Box, {\n shouldForwardProp: (prop) => !['isDisabled', 'isError'].includes(prop as string),\n})<{ isDisabled?: boolean; isError?: boolean }>(({ theme, isDisabled, isError }) => ({\n display: 'flex',\n alignItems: 'stretch',\n boxShadow: theme.customShadows?.raised,\n // Disabled state should overwrite all (including error thickness)\n border: `${isDisabled ? '1px' : isError ? '2px' : '1px'} solid`,\n borderColor: isDisabled\n ? theme.palette.semantic.stroke['stroke-disabled']\n : isError\n ? theme.palette.semantic.stroke['stroke-error-strong']\n : theme.palette.semantic.stroke['stroke-strong'],\n borderRadius: theme.radius['radius-8'],\n overflow: 'visible',\n position: 'relative',\n backgroundColor: theme.palette.semantic.fill['fill-inverse-strong'],\n marginTop: theme.spacing(0.5),\n}));\n\nconst ActionButton = styled(IconButton)(({ theme }) => ({\n border: 'none',\n boxShadow: 'none',\n borderRadius: 0,\n padding: 0,\n // Limit transitions to avoid perceived zoom effects\n // Robust square focus indicator drawn inside using a pseudo-element\n '&&:focus, &&:focus-visible': {\n outline: 'none',\n borderRadius: 0,\n zIndex: 2,\n },\n '&&:focus::after, &&:focus-visible::after': {\n content: '\"\"',\n position: 'absolute',\n top: -4.5,\n right: -2.5,\n bottom: -4.5,\n left: -4.5,\n border: `2px solid ${theme.palette.semantic.stroke['stroke-focus']}`,\n borderRadius: 0,\n pointerEvents: 'none',\n zIndex: 3,\n },\n // Round external corners when focusing the edge buttons\n '&&:first-of-type:focus::after, &&:first-of-type:focus-visible::after': {\n borderTopLeftRadius: theme.radius['radius-12'],\n borderBottomLeftRadius: theme.radius['radius-12'],\n },\n '&&:last-of-type:focus::after, &&:last-of-type:focus-visible::after': {\n borderTopRightRadius: theme.radius['radius-12'],\n borderBottomRightRadius: theme.radius['radius-12'],\n left: -2.5,\n right: -4.5,\n },\n '&:hover': {\n boxShadow: 'none',\n borderRadius: 0,\n },\n '&.Mui-disabled': {\n backgroundColor: theme.palette.semantic.fill['fill-inverse-strong'],\n border: 'none',\n boxShadow: 'none',\n },\n '&.Mui-disabled > svg': {\n color: theme.palette.semantic.icon['icon-disabled'],\n },\n}));\nconst ValueContainer = styled(Box)(({ theme }) => ({\n display: 'flex',\n alignItems: 'stretch',\n width: 72,\n minWidth: 56,\n backgroundColor: theme.palette.semantic.fill['fill-inverse-strong'],\n position: 'relative',\n borderRadius: 0,\n // Draw an outer square focus ring for the input area\n '&:focus-within::after': {\n content: '\"\"',\n position: 'absolute',\n top: -4,\n right: -2,\n bottom: -4,\n left: -2,\n border: `2px solid ${theme.palette.semantic.stroke['stroke-focus']}`,\n borderRadius: 0,\n pointerEvents: 'none',\n zIndex: 3,\n },\n}));\nconst ValueField = styled(TextField, {\n shouldForwardProp: (prop) => prop !== 'isError',\n})<{ isError?: boolean }>(({ theme, isError = false }) => ({\n marginTop: '-4px',\n marginBottom: '-4px',\n '& [role=\"textbox\"], & input': {\n textAlign: 'center',\n outline: 'none',\n },\n '& .MuiOutlinedInput-root': {\n border: 'none',\n boxShadow: 'none',\n outline: 'none',\n borderRadius: 0,\n backgroundColor: isError ? theme.palette.semantic.fill['fill-error-weak'] : 'transparent',\n // Ensure disabled overrides error background\n '&.Mui-disabled': {\n backgroundColor: 'transparent',\n },\n // Remove MUI's internal focus ring; the ring is applied on ValueContainer instead\n '&.Mui-focused': {\n outline: 'none !important',\n },\n '&:focus-within': {\n outline: 'none !important',\n },\n '&:focus': {\n outline: 'none !important',\n boxShadow: 'none !important',\n },\n '&:hover': {\n background: 'none !important',\n backgroundImage: 'none !important',\n },\n '& fieldset': {\n border: '0 !important',\n borderRadius: 0,\n },\n '&:hover fieldset': {\n border: '0 !important',\n },\n '&.Mui-focused fieldset': {\n border: '0',\n },\n },\n '& .MuiOutlinedInput-input': {\n outline: 'none',\n boxShadow: 'none',\n '&:focus': {\n outline: 'none',\n boxShadow: 'none',\n },\n '&.Mui-disabled': {\n color: theme.palette.semantic.text['text-disabled'],\n WebkitTextFillColor: theme.palette.semantic.text['text-disabled'],\n },\n },\n '& input:focus': {\n outline: 'none !important',\n boxShadow: 'none !important',\n },\n}));\n\nconst VerticalDivider = styled('div', {\n shouldForwardProp: (prop) => !['isDisabled', 'isError'].includes(prop as string),\n})<{ isDisabled?: boolean; isError?: boolean }>(({ theme, isDisabled = false, isError = false }) => ({\n width: 1,\n alignSelf: 'stretch',\n backgroundColor: isDisabled\n ? theme.palette.semantic.stroke['stroke-disabled']\n : isError\n ? theme.palette.semantic.stroke['stroke-error-strong']\n : theme.palette.semantic.stroke['stroke-strong'],\n}));\n\nconst ErrorIcon = styled(CancelCircleIcon)(({ theme }) => ({\n color: theme.palette.semantic.icon['icon-error'],\n}));\n\nconst ErrorRow = styled(Box)(({ theme }) => ({\n display: 'flex',\n alignItems: 'center',\n gap: theme.spacing(1),\n marginTop: theme.spacing(0.5),\n}));\n\nconst ErrorText = styled(Typography)(({ theme }) => ({\n color: theme.palette.semantic.text['text-error'],\n}));\n\nconst HintText = styled(Typography, {\n shouldForwardProp: (prop) => prop !== 'isDisabled',\n})<{ isDisabled?: boolean }>(({ theme, isDisabled = false }) => ({\n color: isDisabled ? theme.palette.semantic.text['text-disabled'] : theme.palette.semantic.text['text-weak'],\n}));\n\n/**\n * A quantity selector component with increment/decrement buttons and a numeric input field.\n * Provides min/max clamping, step-based adjustments, and optional label/hint/error display.\n *\n * @param props - Component props\n * @returns A quantity selector control\n */\nexport const QuantitySelector = ({\n value,\n onChange,\n min,\n max,\n step,\n disabled = false,\n fdKey,\n label = '',\n hint = '',\n required = false,\n valid = true,\n errorMessage = '',\n}: QuantitySelectorProps): JSX.Element => {\n const { translate } = useTranslation();\n\n const canDecrement = !disabled && value > min;\n const canIncrement = !disabled && value < max;\n\n const handleDecrement = (): void => {\n if (!canDecrement) return;\n const next = clamp(value - step, min, max);\n if (next === undefined) return;\n onChange(next);\n };\n\n const handleIncrement = (): void => {\n if (!canIncrement) return;\n const next = clamp(value + step, min, max);\n if (next === undefined) return;\n onChange(next);\n };\n\n const handleInputChange = (e: ChangeEvent<HTMLInputElement>): void => {\n const raw = e.target.value;\n if (raw === '') {\n onChange(min);\n return;\n }\n const parsed = clamp(Number(raw), min, max);\n if (parsed === undefined) return;\n onChange(parsed);\n };\n\n return (\n <OuterContainer>\n {label && (\n <MuiInputLabel\n disabled={disabled}\n htmlFor={fdKey ? `${fdKey}-input` : 'quantity-input'}\n required={required}\n >\n {label}\n </MuiInputLabel>\n )}\n {hint && (\n <HintText isDisabled={disabled} variant=\"caption\">\n {hint}\n </HintText>\n )}\n {!valid && !!errorMessage && (\n <ErrorRow>\n <ErrorIcon />\n <ErrorText variant=\"captionStrong\">{errorMessage}</ErrorText>\n </ErrorRow>\n )}\n <InnerContainer data-fd={fdKey} isDisabled={disabled} isError={!valid} role=\"group\">\n <ActionButton\n aria-label={translate('Decrease_quantity')}\n disabled={!canDecrement}\n fdKey={fdKey ? `${fdKey}-decrement` : undefined}\n onClick={handleDecrement}\n tone={valid ? 'neutral' : 'destructive'}\n variant=\"tertiary\"\n >\n <RemoveIcon />\n </ActionButton>\n <VerticalDivider isDisabled={disabled} isError={!valid} />\n <ValueContainer>\n <ValueField\n fullWidth\n aria-label={translate('Quantity')}\n disabled={disabled}\n fdKey={fdKey ? `${fdKey}-input` : 'quantity-input'}\n isError={!valid}\n label=\"\"\n onChange={handleInputChange}\n value={String(value)}\n />\n </ValueContainer>\n <VerticalDivider isDisabled={disabled} isError={!valid} />\n <ActionButton\n aria-label={translate('Increase_quantity')}\n disabled={!canIncrement}\n fdKey={fdKey ? `${fdKey}-increment` : undefined}\n onClick={handleIncrement}\n tone={valid ? 'neutral' : 'destructive'}\n variant=\"tertiary\"\n >\n <AddIcon />\n </ActionButton>\n </InnerContainer>\n </OuterContainer>\n );\n};\n\nexport default QuantitySelector;\n"],"names":["OuterContainer","styled","Box","display","flexDirection","InnerContainer","shouldForwardProp","prop","includes","theme","isDisabled","isError","alignItems","boxShadow","customShadows","raised","border","borderColor","palette","semantic","stroke","borderRadius","radius","overflow","position","backgroundColor","fill","marginTop","spacing","ActionButton","IconButton","padding","outline","zIndex","content","top","right","bottom","left","pointerEvents","borderTopLeftRadius","borderBottomLeftRadius","borderTopRightRadius","borderBottomRightRadius","color","icon","ValueContainer","width","minWidth","ValueField","TextField","marginBottom","textAlign","background","backgroundImage","text","WebkitTextFillColor","VerticalDivider","alignSelf","ErrorIcon","CancelCircleIcon","ErrorRow","gap","ErrorText","Typography","HintText","QuantitySelector","value","onChange","min","max","step","disabled","fdKey","label","hint","required","valid","errorMessage","translate","useTranslation","canDecrement","canIncrement","_jsxs","children","_jsx","MuiInputLabel","htmlFor","variant","role","undefined","onClick","next","clamp","tone","RemoveIcon","fullWidth","e","raw","target","parsed","Number","String","AddIcon"],"mappings":"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"}
|
|
@@ -27,12 +27,6 @@ interface QuantitySelectorProps {
|
|
|
27
27
|
valid?: boolean;
|
|
28
28
|
/** Error message to display when valid is false */
|
|
29
29
|
errorMessage?: string;
|
|
30
|
-
/** Localized aria-label for the decrement button */
|
|
31
|
-
decreaseAriaLabel: string;
|
|
32
|
-
/** Localized aria-label for the increment button */
|
|
33
|
-
increaseAriaLabel: string;
|
|
34
|
-
/** Localized aria-label for the numeric input field */
|
|
35
|
-
valueAriaLabel: string;
|
|
36
30
|
}
|
|
37
31
|
/**
|
|
38
32
|
* A quantity selector component with increment/decrement buttons and a numeric input field.
|
|
@@ -41,7 +35,7 @@ interface QuantitySelectorProps {
|
|
|
41
35
|
* @param props - Component props
|
|
42
36
|
* @returns A quantity selector control
|
|
43
37
|
*/
|
|
44
|
-
declare const QuantitySelector: ({ value, onChange, min, max, step, disabled, fdKey, label, hint, required, valid, errorMessage,
|
|
38
|
+
declare const QuantitySelector: ({ value, onChange, min, max, step, disabled, fdKey, label, hint, required, valid, errorMessage, }: QuantitySelectorProps) => JSX.Element;
|
|
45
39
|
|
|
46
40
|
export { QuantitySelector, QuantitySelector as default };
|
|
47
41
|
export type { QuantitySelectorProps };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{jsxs as e,jsx as t}from"react/jsx-runtime";import o from"@mui/material/InputLabel";import{styled as r}from"@mui/material/styles";import{
|
|
1
|
+
import{jsxs as e,jsx as t}from"react/jsx-runtime";import o from"@mui/material/InputLabel";import{styled as r}from"@mui/material/styles";import{useTranslation as i}from"../../../providers/TranslationProvider.js";import{clamp as n}from"../../../utilities/numericUtilities.js";import a from"../../../icons/Add/index.js";import s from"../../../icons/CancelCircle/index.js";import d from"../../../icons/Remove/index.js";import l from"@mui/material/Box";import{IconButton as u}from"../../atoms/IconButton/index.js";import{TextField as c}from"../../atoms/TextField/index.js";import p from"@mui/material/Typography";const m=r(l)((()=>({display:"flex",flexDirection:"column"}))),b=r(l,{shouldForwardProp:e=>!["isDisabled","isError"].includes(e)})((({theme:e,isDisabled:t,isError:o})=>({display:"flex",alignItems:"stretch",boxShadow:e.customShadows?.raised,border:(t?"1px":o?"2px":"1px")+" solid",borderColor:t?e.palette.semantic.stroke["stroke-disabled"]:o?e.palette.semantic.stroke["stroke-error-strong"]:e.palette.semantic.stroke["stroke-strong"],borderRadius:e.radius["radius-8"],overflow:"visible",position:"relative",backgroundColor:e.palette.semantic.fill["fill-inverse-strong"],marginTop:e.spacing(.5)}))),f=r(u)((({theme:e})=>({border:"none",boxShadow:"none",borderRadius:0,padding:0,"&&:focus, &&:focus-visible":{outline:"none",borderRadius:0,zIndex:2},"&&:focus::after, &&:focus-visible::after":{content:'""',position:"absolute",top:-4.5,right:-2.5,bottom:-4.5,left:-4.5,border:`2px solid ${e.palette.semantic.stroke["stroke-focus"]}`,borderRadius:0,pointerEvents:"none",zIndex:3},"&&:first-of-type:focus::after, &&:first-of-type:focus-visible::after":{borderTopLeftRadius:e.radius["radius-12"],borderBottomLeftRadius:e.radius["radius-12"]},"&&:last-of-type:focus::after, &&:last-of-type:focus-visible::after":{borderTopRightRadius:e.radius["radius-12"],borderBottomRightRadius:e.radius["radius-12"],left:-2.5,right:-4.5},"&:hover":{boxShadow:"none",borderRadius:0},"&.Mui-disabled":{backgroundColor:e.palette.semantic.fill["fill-inverse-strong"],border:"none",boxShadow:"none"},"&.Mui-disabled > svg":{color:e.palette.semantic.icon["icon-disabled"]}}))),h=r(l)((({theme:e})=>({display:"flex",alignItems:"stretch",width:72,minWidth:56,backgroundColor:e.palette.semantic.fill["fill-inverse-strong"],position:"relative",borderRadius:0,"&:focus-within::after":{content:'""',position:"absolute",top:-4,right:-2,bottom:-4,left:-2,border:`2px solid ${e.palette.semantic.stroke["stroke-focus"]}`,borderRadius:0,pointerEvents:"none",zIndex:3}}))),x=r(c,{shouldForwardProp:e=>"isError"!==e})((({theme:e,isError:t=!1})=>({marginTop:"-4px",marginBottom:"-4px",'& [role="textbox"], & input':{textAlign:"center",outline:"none"},"& .MuiOutlinedInput-root":{border:"none",boxShadow:"none",outline:"none",borderRadius:0,backgroundColor:t?e.palette.semantic.fill["fill-error-weak"]:"transparent","&.Mui-disabled":{backgroundColor:"transparent"},"&.Mui-focused":{outline:"none !important"},"&:focus-within":{outline:"none !important"},"&:focus":{outline:"none !important",boxShadow:"none !important"},"&:hover":{background:"none !important",backgroundImage:"none !important"},"& fieldset":{border:"0 !important",borderRadius:0},"&:hover fieldset":{border:"0 !important"},"&.Mui-focused fieldset":{border:"0"}},"& .MuiOutlinedInput-input":{outline:"none",boxShadow:"none","&:focus":{outline:"none",boxShadow:"none"},"&.Mui-disabled":{color:e.palette.semantic.text["text-disabled"],WebkitTextFillColor:e.palette.semantic.text["text-disabled"]}},"& input:focus":{outline:"none !important",boxShadow:"none !important"}}))),g=r("div",{shouldForwardProp:e=>!["isDisabled","isError"].includes(e)})((({theme:e,isDisabled:t=!1,isError:o=!1})=>({width:1,alignSelf:"stretch",backgroundColor:t?e.palette.semantic.stroke["stroke-disabled"]:o?e.palette.semantic.stroke["stroke-error-strong"]:e.palette.semantic.stroke["stroke-strong"]}))),v=r(s)((({theme:e})=>({color:e.palette.semantic.icon["icon-error"]}))),k=r(l)((({theme:e})=>({display:"flex",alignItems:"center",gap:e.spacing(1),marginTop:e.spacing(.5)}))),y=r(p)((({theme:e})=>({color:e.palette.semantic.text["text-error"]}))),w=r(p,{shouldForwardProp:e=>"isDisabled"!==e})((({theme:e,isDisabled:t=!1})=>({color:t?e.palette.semantic.text["text-disabled"]:e.palette.semantic.text["text-weak"]}))),R=({value:r,onChange:s,min:l,max:u,step:c,disabled:p=!1,fdKey:R,label:C="",hint:S="",required:D=!1,valid:E=!0,errorMessage:I=""})=>{const{translate:M}=i(),T=!p&&r>l,j=!p&&r<u;return e(m,{children:[C&&t(o,{disabled:p,htmlFor:R?`${R}-input`:"quantity-input",required:D,children:C}),S&&t(w,{isDisabled:p,variant:"caption",children:S}),!E&&!!I&&e(k,{children:[t(v,{}),t(y,{variant:"captionStrong",children:I})]}),e(b,{"data-fd":R,isDisabled:p,isError:!E,role:"group",children:[t(f,{"aria-label":M("Decrease_quantity"),disabled:!T,fdKey:R?`${R}-decrement`:void 0,onClick:()=>{if(!T)return;const e=n(r-c,l,u);void 0!==e&&s(e)},tone:E?"neutral":"destructive",variant:"tertiary",children:t(d,{})}),t(g,{isDisabled:p,isError:!E}),t(h,{children:t(x,{fullWidth:!0,"aria-label":M("Quantity"),disabled:p,fdKey:R?`${R}-input`:"quantity-input",isError:!E,label:"",onChange:e=>{const t=e.target.value;if(""===t)return void s(l);const o=n(Number(t),l,u);void 0!==o&&s(o)},value:String(r)})}),t(g,{isDisabled:p,isError:!E}),t(f,{"aria-label":M("Increase_quantity"),disabled:!j,fdKey:R?`${R}-increment`:void 0,onClick:()=>{if(!j)return;const e=n(r+c,l,u);void 0!==e&&s(e)},tone:E?"neutral":"destructive",variant:"tertiary",children:t(a,{})})]})]})};export{R as QuantitySelector,R as default};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../../../src/components/molecules/QuantitySelector/index.tsx"],"sourcesContent":["import MuiInputLabel from '@mui/material/InputLabel';\nimport { styled } from '@mui/material/styles';\n\nimport { clamp } from '@fd/utilities/numericUtilities';\nimport type { ChangeEvent } from 'react';\n\nimport AddIcon from '../../../icons/Add';\nimport CancelCircleIcon from '../../../icons/CancelCircle';\nimport RemoveIcon from '../../../icons/Remove';\nimport Box from '../../atoms/Box';\nimport IconButton from '../../atoms/IconButton';\nimport TextField from '../../atoms/TextField';\nimport Typography from '../../atoms/Typography';\n\n/**\n * Props for the QuantitySelector component.\n * Controls a numeric input with increment/decrement buttons and min/max bounds.\n */\nexport interface QuantitySelectorProps {\n /** Current numeric value */\n value: number;\n /** Callback fired when the value changes */\n onChange: (nextValue: number) => void;\n /** Minimum allowed value */\n min: number;\n /** Maximum allowed value */\n max: number;\n /** Amount to increment/decrement on button clicks */\n step: number;\n /** Whether the control is disabled */\n disabled?: boolean;\n /** Unique identifier for testing/tracking */\n fdKey?: string;\n /** Label text displayed above the control */\n label?: string;\n /** Hint text displayed below the label */\n hint?: string;\n /** Whether the field is required */\n required?: boolean;\n /** Whether the current value is valid */\n valid?: boolean;\n /** Error message to display when valid is false */\n errorMessage?: string;\n /** Localized aria-label for the decrement button */\n decreaseAriaLabel: string;\n /** Localized aria-label for the increment button */\n increaseAriaLabel: string;\n /** Localized aria-label for the numeric input field */\n valueAriaLabel: string;\n}\n\nconst OuterContainer = styled(Box)(() => ({\n display: 'flex',\n flexDirection: 'column'\n}));\n\nconst InnerContainer = styled(Box, {\n shouldForwardProp: (prop) => !['isDisabled', 'isError'].includes(prop as string),\n})<{ isDisabled?: boolean; isError?: boolean }>(({ theme, isDisabled, isError }) => ({\n display: 'flex',\n alignItems: 'stretch',\n boxShadow: theme.customShadows?.raised,\n // Disabled state should overwrite all (including error thickness)\n border: `${isDisabled ? '1px' : isError ? '2px' : '1px'} solid`,\n borderColor: isDisabled\n ? theme.palette.semantic.stroke['stroke-disabled']\n : isError\n ? theme.palette.semantic.stroke['stroke-error-strong']\n : theme.palette.semantic.stroke['stroke-strong'],\n borderRadius: theme.radius['radius-8'],\n overflow: 'visible',\n position: 'relative',\n backgroundColor: theme.palette.semantic.fill['fill-inverse-strong'],\n marginTop: theme.spacing(0.5),\n}));\n\nconst ActionButton = styled(IconButton)(({ theme }) => ({\n border: 'none',\n boxShadow: 'none',\n borderRadius: 0,\n padding: 0,\n // Limit transitions to avoid perceived zoom effects\n // Robust square focus indicator drawn inside using a pseudo-element\n '&&:focus, &&:focus-visible': {\n outline: 'none',\n borderRadius: 0,\n zIndex: 2,\n },\n '&&:focus::after, &&:focus-visible::after': {\n content: '\"\"',\n position: 'absolute',\n top: -4.5,\n right: -2.5,\n bottom: -4.5,\n left: -4.5,\n border: `2px solid ${theme.palette.semantic.stroke['stroke-focus']}`,\n borderRadius: 0,\n pointerEvents: 'none',\n zIndex: 3,\n },\n // Round external corners when focusing the edge buttons\n '&&:first-of-type:focus::after, &&:first-of-type:focus-visible::after': {\n borderTopLeftRadius: theme.radius['radius-12'],\n borderBottomLeftRadius: theme.radius['radius-12'],\n },\n '&&:last-of-type:focus::after, &&:last-of-type:focus-visible::after': {\n borderTopRightRadius: theme.radius['radius-12'],\n borderBottomRightRadius: theme.radius['radius-12'],\n left: -2.5,\n right: -4.5,\n },\n '&:hover': {\n boxShadow: 'none',\n borderRadius: 0,\n },\n '&.Mui-disabled': {\n backgroundColor: theme.palette.semantic.fill['fill-inverse-strong'],\n border: 'none',\n boxShadow: 'none'\n },\n '&.Mui-disabled > svg': {\n color: theme.palette.semantic.icon['icon-disabled'],\n },\n}));\nconst ValueContainer = styled(Box)(({ theme }) => ({\n display: 'flex',\n alignItems: 'stretch',\n width: 72,\n minWidth: 56,\n backgroundColor: theme.palette.semantic.fill['fill-inverse-strong'],\n position: 'relative',\n borderRadius: 0,\n // Draw an outer square focus ring for the input area\n '&:focus-within::after': {\n content: '\"\"',\n position: 'absolute',\n top: -4,\n right: -2,\n bottom: -4,\n left: -2,\n border: `2px solid ${theme.palette.semantic.stroke['stroke-focus']}`,\n borderRadius: 0,\n pointerEvents: 'none',\n zIndex: 3,\n },\n}));\nconst ValueField = styled(TextField, {\n shouldForwardProp: (prop) => prop !== 'isError',\n})<{ isError?: boolean }>(({ theme, isError = false }) => ({\n marginTop: '-4px',\n marginBottom: '-4px',\n '& [role=\"textbox\"], & input': {\n textAlign: 'center',\n outline: 'none',\n },\n '& .MuiOutlinedInput-root': {\n border: 'none',\n boxShadow: 'none',\n outline: 'none',\n borderRadius: 0,\n backgroundColor: isError ? theme.palette.semantic.fill['fill-error-weak'] : 'transparent',\n // Ensure disabled overrides error background\n '&.Mui-disabled': {\n backgroundColor: 'transparent',\n },\n // Remove MUI's internal focus ring; the ring is applied on ValueContainer instead\n '&.Mui-focused': {\n outline: 'none !important',\n },\n '&:focus-within': {\n outline: 'none !important',\n },\n '&:focus': {\n outline: 'none !important',\n boxShadow: 'none !important',\n },\n '&:hover': {\n background: 'none !important',\n backgroundImage: 'none !important',\n },\n '& fieldset': {\n border: '0 !important',\n borderRadius: 0,\n },\n '&:hover fieldset': {\n border: '0 !important',\n },\n '&.Mui-focused fieldset': {\n border: '0',\n },\n },\n '& .MuiOutlinedInput-input': {\n outline: 'none',\n boxShadow: 'none',\n '&:focus': {\n outline: 'none',\n boxShadow: 'none',\n },\n '&.Mui-disabled': {\n color: theme.palette.semantic.text['text-disabled'],\n WebkitTextFillColor: theme.palette.semantic.text['text-disabled'],\n },\n },\n '& input:focus': {\n outline: 'none !important',\n boxShadow: 'none !important',\n },\n}));\n\nconst VerticalDivider = styled('div', {\n shouldForwardProp: (prop) => !['isDisabled', 'isError'].includes(prop as string),\n})<{ isDisabled?: boolean; isError?: boolean }>(({ theme, isDisabled = false, isError = false }) => ({\n width: 1,\n alignSelf: 'stretch',\n backgroundColor: isDisabled\n ? theme.palette.semantic.stroke['stroke-disabled']\n : isError\n ? theme.palette.semantic.stroke['stroke-error-strong']\n : theme.palette.semantic.stroke['stroke-strong'],\n}));\n\nconst ErrorIcon = styled(CancelCircleIcon)(({ theme }) => ({\n color: theme.palette.semantic.icon['icon-error'],\n}));\n\nconst ErrorRow = styled(Box)(({ theme }) => ({\n display: 'flex',\n alignItems: 'center',\n gap: theme.spacing(1),\n marginTop: theme.spacing(0.5)\n}));\n\nconst ErrorText = styled(Typography)(({ theme }) => ({\n color: theme.palette.semantic.text['text-error']\n}));\n\nconst HintText = styled(Typography, {\n shouldForwardProp: (prop) => prop !== 'isDisabled',\n})<{ isDisabled?: boolean }>(({ theme, isDisabled = false }) => ({\n color: isDisabled ? theme.palette.semantic.text['text-disabled'] : theme.palette.semantic.text['text-weak'],\n}));\n\n/**\n * A quantity selector component with increment/decrement buttons and a numeric input field.\n * Provides min/max clamping, step-based adjustments, and optional label/hint/error display.\n *\n * @param props - Component props\n * @returns A quantity selector control\n */\nexport const QuantitySelector = ({\n value,\n onChange,\n min,\n max,\n step,\n disabled = false,\n fdKey,\n label = \"\",\n hint = \"\",\n required = false,\n valid = true,\n errorMessage = '',\n decreaseAriaLabel,\n increaseAriaLabel,\n valueAriaLabel,\n}: QuantitySelectorProps): JSX.Element => {\n const canDecrement = !disabled && value > min;\n const canIncrement = !disabled && value < max;\n\n const handleDecrement = (): void => {\n if (!canDecrement) return;\n const next = clamp(value - step, min, max);\n if (next === undefined) return;\n onChange(next);\n };\n\n const handleIncrement = (): void => {\n if (!canIncrement) return;\n const next = clamp(value + step, min, max);\n if (next === undefined) return;\n onChange(next);\n };\n\n const handleInputChange = (e: ChangeEvent<HTMLInputElement>): void => {\n const raw = e.target.value;\n if (raw === '') {\n onChange(min);\n return;\n }\n const parsed = clamp(Number(raw), min, max)\n if (parsed === undefined) return;\n onChange(parsed);\n };\n\n return (\n <OuterContainer>\n {label && (\n <MuiInputLabel disabled={disabled} htmlFor={fdKey ? `${fdKey}-input` : 'quantity-input'} required={required}>\n {label}\n </MuiInputLabel>\n\n )}\n {hint && (\n <HintText isDisabled={disabled} variant=\"caption\">\n {hint}\n </HintText>)}\n {!valid && !!errorMessage && (\n <ErrorRow>\n <ErrorIcon />\n <ErrorText variant=\"captionStrong\">\n {errorMessage}\n </ErrorText>\n </ErrorRow>\n )}\n <InnerContainer data-fd={fdKey} isDisabled={disabled} isError={!valid} role=\"group\">\n <ActionButton\n aria-label={decreaseAriaLabel}\n disabled={!canDecrement}\n fdKey={fdKey ? `${fdKey}-decrement` : undefined}\n onClick={handleDecrement}\n tone={valid ? 'neutral' : 'destructive'}\n variant=\"tertiary\"\n >\n <RemoveIcon />\n </ActionButton>\n <VerticalDivider isDisabled={disabled} isError={!valid} />\n <ValueContainer>\n <ValueField\n fullWidth\n aria-label={valueAriaLabel}\n disabled={disabled}\n fdKey={fdKey ? `${fdKey}-input` : 'quantity-input'}\n isError={!valid}\n label=\"\"\n onChange={handleInputChange}\n value={String(value)}\n />\n </ValueContainer>\n <VerticalDivider isDisabled={disabled} isError={!valid} />\n <ActionButton\n aria-label={increaseAriaLabel}\n disabled={!canIncrement}\n fdKey={fdKey ? `${fdKey}-increment` : undefined}\n onClick={handleIncrement}\n tone={valid ? 'neutral' : 'destructive'}\n variant=\"tertiary\"\n >\n <AddIcon />\n </ActionButton>\n </InnerContainer>\n </OuterContainer>\n );\n};\n\nexport default QuantitySelector;\n"],"names":["OuterContainer","styled","Box","display","flexDirection","InnerContainer","shouldForwardProp","prop","includes","theme","isDisabled","isError","alignItems","boxShadow","customShadows","raised","border","borderColor","palette","semantic","stroke","borderRadius","radius","overflow","position","backgroundColor","fill","marginTop","spacing","ActionButton","IconButton","padding","outline","zIndex","content","top","right","bottom","left","pointerEvents","borderTopLeftRadius","borderBottomLeftRadius","borderTopRightRadius","borderBottomRightRadius","color","icon","ValueContainer","width","minWidth","ValueField","TextField","marginBottom","textAlign","background","backgroundImage","text","WebkitTextFillColor","VerticalDivider","alignSelf","ErrorIcon","CancelCircleIcon","ErrorRow","gap","ErrorText","Typography","HintText","QuantitySelector","value","onChange","min","max","step","disabled","fdKey","label","hint","required","valid","errorMessage","decreaseAriaLabel","increaseAriaLabel","valueAriaLabel","canDecrement","canIncrement","_jsxs","children","_jsx","MuiInputLabel","htmlFor","variant","role","undefined","onClick","next","clamp","tone","RemoveIcon","fullWidth","e","raw","target","parsed","Number","String","AddIcon"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../../src/components/molecules/QuantitySelector/index.tsx"],"sourcesContent":["import MuiInputLabel from '@mui/material/InputLabel';\nimport { styled } from '@mui/material/styles';\n\nimport { useTranslation } from '@fd/providers/TranslationProvider';\nimport { clamp } from '@fd/utilities/numericUtilities';\nimport type { ChangeEvent } from 'react';\n\nimport AddIcon from '../../../icons/Add';\nimport CancelCircleIcon from '../../../icons/CancelCircle';\nimport RemoveIcon from '../../../icons/Remove';\nimport Box from '../../atoms/Box';\nimport IconButton from '../../atoms/IconButton';\nimport TextField from '../../atoms/TextField';\nimport Typography from '../../atoms/Typography';\n\n/**\n * Props for the QuantitySelector component.\n * Controls a numeric input with increment/decrement buttons and min/max bounds.\n */\nexport interface QuantitySelectorProps {\n /** Current numeric value */\n value: number;\n /** Callback fired when the value changes */\n onChange: (nextValue: number) => void;\n /** Minimum allowed value */\n min: number;\n /** Maximum allowed value */\n max: number;\n /** Amount to increment/decrement on button clicks */\n step: number;\n /** Whether the control is disabled */\n disabled?: boolean;\n /** Unique identifier for testing/tracking */\n fdKey?: string;\n /** Label text displayed above the control */\n label?: string;\n /** Hint text displayed below the label */\n hint?: string;\n /** Whether the field is required */\n required?: boolean;\n /** Whether the current value is valid */\n valid?: boolean;\n /** Error message to display when valid is false */\n errorMessage?: string;\n}\n\nconst OuterContainer = styled(Box)(() => ({\n display: 'flex',\n flexDirection: 'column',\n}));\n\nconst InnerContainer = styled(Box, {\n shouldForwardProp: (prop) => !['isDisabled', 'isError'].includes(prop as string),\n})<{ isDisabled?: boolean; isError?: boolean }>(({ theme, isDisabled, isError }) => ({\n display: 'flex',\n alignItems: 'stretch',\n boxShadow: theme.customShadows?.raised,\n // Disabled state should overwrite all (including error thickness)\n border: `${isDisabled ? '1px' : isError ? '2px' : '1px'} solid`,\n borderColor: isDisabled\n ? theme.palette.semantic.stroke['stroke-disabled']\n : isError\n ? theme.palette.semantic.stroke['stroke-error-strong']\n : theme.palette.semantic.stroke['stroke-strong'],\n borderRadius: theme.radius['radius-8'],\n overflow: 'visible',\n position: 'relative',\n backgroundColor: theme.palette.semantic.fill['fill-inverse-strong'],\n marginTop: theme.spacing(0.5),\n}));\n\nconst ActionButton = styled(IconButton)(({ theme }) => ({\n border: 'none',\n boxShadow: 'none',\n borderRadius: 0,\n padding: 0,\n // Limit transitions to avoid perceived zoom effects\n // Robust square focus indicator drawn inside using a pseudo-element\n '&&:focus, &&:focus-visible': {\n outline: 'none',\n borderRadius: 0,\n zIndex: 2,\n },\n '&&:focus::after, &&:focus-visible::after': {\n content: '\"\"',\n position: 'absolute',\n top: -4.5,\n right: -2.5,\n bottom: -4.5,\n left: -4.5,\n border: `2px solid ${theme.palette.semantic.stroke['stroke-focus']}`,\n borderRadius: 0,\n pointerEvents: 'none',\n zIndex: 3,\n },\n // Round external corners when focusing the edge buttons\n '&&:first-of-type:focus::after, &&:first-of-type:focus-visible::after': {\n borderTopLeftRadius: theme.radius['radius-12'],\n borderBottomLeftRadius: theme.radius['radius-12'],\n },\n '&&:last-of-type:focus::after, &&:last-of-type:focus-visible::after': {\n borderTopRightRadius: theme.radius['radius-12'],\n borderBottomRightRadius: theme.radius['radius-12'],\n left: -2.5,\n right: -4.5,\n },\n '&:hover': {\n boxShadow: 'none',\n borderRadius: 0,\n },\n '&.Mui-disabled': {\n backgroundColor: theme.palette.semantic.fill['fill-inverse-strong'],\n border: 'none',\n boxShadow: 'none',\n },\n '&.Mui-disabled > svg': {\n color: theme.palette.semantic.icon['icon-disabled'],\n },\n}));\nconst ValueContainer = styled(Box)(({ theme }) => ({\n display: 'flex',\n alignItems: 'stretch',\n width: 72,\n minWidth: 56,\n backgroundColor: theme.palette.semantic.fill['fill-inverse-strong'],\n position: 'relative',\n borderRadius: 0,\n // Draw an outer square focus ring for the input area\n '&:focus-within::after': {\n content: '\"\"',\n position: 'absolute',\n top: -4,\n right: -2,\n bottom: -4,\n left: -2,\n border: `2px solid ${theme.palette.semantic.stroke['stroke-focus']}`,\n borderRadius: 0,\n pointerEvents: 'none',\n zIndex: 3,\n },\n}));\nconst ValueField = styled(TextField, {\n shouldForwardProp: (prop) => prop !== 'isError',\n})<{ isError?: boolean }>(({ theme, isError = false }) => ({\n marginTop: '-4px',\n marginBottom: '-4px',\n '& [role=\"textbox\"], & input': {\n textAlign: 'center',\n outline: 'none',\n },\n '& .MuiOutlinedInput-root': {\n border: 'none',\n boxShadow: 'none',\n outline: 'none',\n borderRadius: 0,\n backgroundColor: isError ? theme.palette.semantic.fill['fill-error-weak'] : 'transparent',\n // Ensure disabled overrides error background\n '&.Mui-disabled': {\n backgroundColor: 'transparent',\n },\n // Remove MUI's internal focus ring; the ring is applied on ValueContainer instead\n '&.Mui-focused': {\n outline: 'none !important',\n },\n '&:focus-within': {\n outline: 'none !important',\n },\n '&:focus': {\n outline: 'none !important',\n boxShadow: 'none !important',\n },\n '&:hover': {\n background: 'none !important',\n backgroundImage: 'none !important',\n },\n '& fieldset': {\n border: '0 !important',\n borderRadius: 0,\n },\n '&:hover fieldset': {\n border: '0 !important',\n },\n '&.Mui-focused fieldset': {\n border: '0',\n },\n },\n '& .MuiOutlinedInput-input': {\n outline: 'none',\n boxShadow: 'none',\n '&:focus': {\n outline: 'none',\n boxShadow: 'none',\n },\n '&.Mui-disabled': {\n color: theme.palette.semantic.text['text-disabled'],\n WebkitTextFillColor: theme.palette.semantic.text['text-disabled'],\n },\n },\n '& input:focus': {\n outline: 'none !important',\n boxShadow: 'none !important',\n },\n}));\n\nconst VerticalDivider = styled('div', {\n shouldForwardProp: (prop) => !['isDisabled', 'isError'].includes(prop as string),\n})<{ isDisabled?: boolean; isError?: boolean }>(({ theme, isDisabled = false, isError = false }) => ({\n width: 1,\n alignSelf: 'stretch',\n backgroundColor: isDisabled\n ? theme.palette.semantic.stroke['stroke-disabled']\n : isError\n ? theme.palette.semantic.stroke['stroke-error-strong']\n : theme.palette.semantic.stroke['stroke-strong'],\n}));\n\nconst ErrorIcon = styled(CancelCircleIcon)(({ theme }) => ({\n color: theme.palette.semantic.icon['icon-error'],\n}));\n\nconst ErrorRow = styled(Box)(({ theme }) => ({\n display: 'flex',\n alignItems: 'center',\n gap: theme.spacing(1),\n marginTop: theme.spacing(0.5),\n}));\n\nconst ErrorText = styled(Typography)(({ theme }) => ({\n color: theme.palette.semantic.text['text-error'],\n}));\n\nconst HintText = styled(Typography, {\n shouldForwardProp: (prop) => prop !== 'isDisabled',\n})<{ isDisabled?: boolean }>(({ theme, isDisabled = false }) => ({\n color: isDisabled ? theme.palette.semantic.text['text-disabled'] : theme.palette.semantic.text['text-weak'],\n}));\n\n/**\n * A quantity selector component with increment/decrement buttons and a numeric input field.\n * Provides min/max clamping, step-based adjustments, and optional label/hint/error display.\n *\n * @param props - Component props\n * @returns A quantity selector control\n */\nexport const QuantitySelector = ({\n value,\n onChange,\n min,\n max,\n step,\n disabled = false,\n fdKey,\n label = '',\n hint = '',\n required = false,\n valid = true,\n errorMessage = '',\n}: QuantitySelectorProps): JSX.Element => {\n const { translate } = useTranslation();\n\n const canDecrement = !disabled && value > min;\n const canIncrement = !disabled && value < max;\n\n const handleDecrement = (): void => {\n if (!canDecrement) return;\n const next = clamp(value - step, min, max);\n if (next === undefined) return;\n onChange(next);\n };\n\n const handleIncrement = (): void => {\n if (!canIncrement) return;\n const next = clamp(value + step, min, max);\n if (next === undefined) return;\n onChange(next);\n };\n\n const handleInputChange = (e: ChangeEvent<HTMLInputElement>): void => {\n const raw = e.target.value;\n if (raw === '') {\n onChange(min);\n return;\n }\n const parsed = clamp(Number(raw), min, max);\n if (parsed === undefined) return;\n onChange(parsed);\n };\n\n return (\n <OuterContainer>\n {label && (\n <MuiInputLabel\n disabled={disabled}\n htmlFor={fdKey ? `${fdKey}-input` : 'quantity-input'}\n required={required}\n >\n {label}\n </MuiInputLabel>\n )}\n {hint && (\n <HintText isDisabled={disabled} variant=\"caption\">\n {hint}\n </HintText>\n )}\n {!valid && !!errorMessage && (\n <ErrorRow>\n <ErrorIcon />\n <ErrorText variant=\"captionStrong\">{errorMessage}</ErrorText>\n </ErrorRow>\n )}\n <InnerContainer data-fd={fdKey} isDisabled={disabled} isError={!valid} role=\"group\">\n <ActionButton\n aria-label={translate('Decrease_quantity')}\n disabled={!canDecrement}\n fdKey={fdKey ? `${fdKey}-decrement` : undefined}\n onClick={handleDecrement}\n tone={valid ? 'neutral' : 'destructive'}\n variant=\"tertiary\"\n >\n <RemoveIcon />\n </ActionButton>\n <VerticalDivider isDisabled={disabled} isError={!valid} />\n <ValueContainer>\n <ValueField\n fullWidth\n aria-label={translate('Quantity')}\n disabled={disabled}\n fdKey={fdKey ? `${fdKey}-input` : 'quantity-input'}\n isError={!valid}\n label=\"\"\n onChange={handleInputChange}\n value={String(value)}\n />\n </ValueContainer>\n <VerticalDivider isDisabled={disabled} isError={!valid} />\n <ActionButton\n aria-label={translate('Increase_quantity')}\n disabled={!canIncrement}\n fdKey={fdKey ? `${fdKey}-increment` : undefined}\n onClick={handleIncrement}\n tone={valid ? 'neutral' : 'destructive'}\n variant=\"tertiary\"\n >\n <AddIcon />\n </ActionButton>\n </InnerContainer>\n </OuterContainer>\n );\n};\n\nexport default QuantitySelector;\n"],"names":["OuterContainer","styled","Box","display","flexDirection","InnerContainer","shouldForwardProp","prop","includes","theme","isDisabled","isError","alignItems","boxShadow","customShadows","raised","border","borderColor","palette","semantic","stroke","borderRadius","radius","overflow","position","backgroundColor","fill","marginTop","spacing","ActionButton","IconButton","padding","outline","zIndex","content","top","right","bottom","left","pointerEvents","borderTopLeftRadius","borderBottomLeftRadius","borderTopRightRadius","borderBottomRightRadius","color","icon","ValueContainer","width","minWidth","ValueField","TextField","marginBottom","textAlign","background","backgroundImage","text","WebkitTextFillColor","VerticalDivider","alignSelf","ErrorIcon","CancelCircleIcon","ErrorRow","gap","ErrorText","Typography","HintText","QuantitySelector","value","onChange","min","max","step","disabled","fdKey","label","hint","required","valid","errorMessage","translate","useTranslation","canDecrement","canIncrement","_jsxs","children","_jsx","MuiInputLabel","htmlFor","variant","role","undefined","onClick","next","clamp","tone","RemoveIcon","fullWidth","e","raw","target","parsed","Number","String","AddIcon"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("react/jsx-runtime"),t=require("@mui/material/Rating"),a=require("@mui/material/styles"),
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("react/jsx-runtime"),t=require("@mui/material/Rating"),a=require("@mui/material/styles"),r=require("@mui/material/Box"),i=require("@mui/material/Typography"),n=require("../../../providers/TranslationProvider.cjs.js"),s=require("../../atoms/Link/index.cjs.js"),l=require("./icons/index.cjs.js");const o=a.styled(r,{shouldForwardProp:e=>"layout"!==e})((({theme:e,layout:t})=>({display:"flex",flexDirection:"horizontal"===t?"row":"column",gap:"horizontal"===t?e.spacing(1):e.spacing(.5),justifyContent:"center"}))),d=a.styled(r)((({theme:e})=>({alignItems:"center",display:"flex",gap:e.spacing(1),justifyContent:"center"}))),c=a.styled(r)((({theme:e})=>({alignItems:"center",display:"flex",gap:e.spacing(.5),justifyContent:"flex-start"}))),u=a.styled(i)((({theme:e})=>({color:e.palette.semantic.text["text-strong"]}))),m=a.styled(i)((({theme:e})=>({color:e.palette.semantic.text["text-weak"]}))),p={heart:{empty:e.jsx(l.HeartEmptyIcon,{"data-testid":"heart-empty-icon"}),filled:e.jsx(l.HeartFilledIcon,{"data-testid":"heart-filled-icon"})},star:{empty:e.jsx(l.StarEmptyIcon,{"data-testid":"star-empty-icon"}),filled:e.jsx(l.StarFilledIcon,{"data-testid":"star-filled-icon"})}},y=({fdKey:a,layout:r="horizontal",linkFdKey:i,linkHref:l,max:y=5,onChange:x,reviews:h=0,type:f="star",value:j,...g})=>{const{translate:v}=n.useTranslation(),_=p[f].empty,q=p[f].filled,I=h>0&&i&&l;return e.jsxs(o,{layout:r,children:[e.jsxs(d,{children:[e.jsx(t,{...g,"data-fd":a,emptyIcon:_,getLabelText:e=>v("heart"===f?"number_out_of_total_hearts":"number_out_of_total_stars",{number:e,total:y}),icon:q,max:y,name:a,onChange:(e,t)=>{x?.(e,null!==t?t:0)},precision:.5,readOnly:void 0===x,value:j}),e.jsx(u,{"aria-hidden":!0,variant:"b1Strong",children:j?.toFixed(1)})]}),I&&e.jsxs(c,{children:["vertical"===r&&e.jsxs(m,{variant:"captionWeak",children:[v("From")," "]}),e.jsx(s.Link,{fdKey:i,href:l,size:"caption",tone:"Brand",children:1===h?v("one_review"):v("total_reviews",{total:h})})]})]})};exports.Rating=y,exports.default=y;
|
|
2
2
|
//# sourceMappingURL=index.cjs.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs.js","sources":["../../../../src/components/molecules/Rating/index.tsx"],"sourcesContent":["import MuiRating, { type RatingProps as MuiRatingProps } from '@mui/material/Rating';\nimport { styled } from '@mui/material/styles';\n\nimport Box from '@fd/components/atoms/Box';\nimport Typography from '@fd/components/atoms/Typography';\n\nimport Link from '../../atoms/Link';\nimport { HeartEmptyIcon, HeartFilledIcon, StarEmptyIcon, StarFilledIcon } from './icons';\n\
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":["../../../../src/components/molecules/Rating/index.tsx"],"sourcesContent":["import MuiRating, { type RatingProps as MuiRatingProps } from '@mui/material/Rating';\nimport { styled } from '@mui/material/styles';\n\nimport Box from '@fd/components/atoms/Box';\nimport Typography from '@fd/components/atoms/Typography';\nimport { useTranslation } from '@fd/providers/TranslationProvider';\n\nimport Link from '../../atoms/Link';\nimport { HeartEmptyIcon, HeartFilledIcon, StarEmptyIcon, StarFilledIcon } from './icons';\n\ntype BaseRatingProps = Omit<\n MuiRatingProps,\n 'emptyIcon' | 'icon' | 'onChange' | 'precision' | 'readOnly' | 'value'\n> & {\n /** The identifier for the rating component */\n fdKey: string;\n /** The function to call when the rating value changes */\n onChange?: (event: React.SyntheticEvent, value: number) => void;\n /** The type of rating component to use */\n type?: 'heart' | 'star';\n /** The value of the rating component */\n value: number;\n};\n\ninterface NonReviewRatingProps extends BaseRatingProps {\n /** The layout of the rating component */\n layout?: 'horizontal' | undefined;\n /** The identifier for the link component */\n linkFdKey?: never;\n /** The href for the link component */\n linkHref?: never;\n /** The number of reviews */\n reviews?: never;\n}\n\ninterface ReviewRatingProps extends BaseRatingProps {\n /** The layout of the rating component */\n layout?: 'horizontal' | 'vertical';\n /** The identifier for the link component */\n linkFdKey: string;\n /** The href for the link component */\n linkHref: string;\n /** The number of reviews */\n reviews: number;\n}\n\n/**\n * The props for the Rating component.\n */\nexport type RatingProps = NonReviewRatingProps | ReviewRatingProps;\n\nconst StyledContainer = styled(Box, { shouldForwardProp: (prop) => prop !== 'layout' })<{\n layout: RatingProps['layout'];\n}>(({ theme, layout }) => ({\n display: 'flex',\n flexDirection: layout === 'horizontal' ? 'row' : 'column',\n gap: layout === 'horizontal' ? theme.spacing(1) : theme.spacing(0.5),\n justifyContent: 'center',\n}));\n\nconst StyledIconContainer = styled(Box)(({ theme }) => ({\n alignItems: 'center',\n display: 'flex',\n gap: theme.spacing(1),\n justifyContent: 'center',\n}));\n\nconst StyledReviewsContainer = styled(Box)(({ theme }) => ({\n alignItems: 'center',\n display: 'flex',\n gap: theme.spacing(0.5),\n justifyContent: 'flex-start',\n}));\n\nconst StyledValueText = styled(Typography)(({ theme }) => ({\n color: theme.palette.semantic.text['text-strong'],\n}));\n\nconst StyledReviewsText = styled(Typography)(({ theme }) => ({\n color: theme.palette.semantic.text['text-weak'],\n}));\n\nconst iconMap = {\n heart: {\n empty: <HeartEmptyIcon data-testid=\"heart-empty-icon\" />,\n filled: <HeartFilledIcon data-testid=\"heart-filled-icon\" />,\n },\n star: {\n empty: <StarEmptyIcon data-testid=\"star-empty-icon\" />,\n filled: <StarFilledIcon data-testid=\"star-filled-icon\" />,\n },\n};\n\n/**\n * Rating component allows users to provide feedback by rating items with stars.\n *\n * @param props - Additional props to pass to the underlying MUI Rating component\n * @returns A Rating component\n */\nexport const Rating: React.FC<RatingProps> = ({\n fdKey,\n layout = 'horizontal',\n linkFdKey,\n linkHref,\n max = 5,\n onChange,\n reviews = 0,\n type = 'star',\n value,\n ...props\n}) => {\n const { translate } = useTranslation();\n\n const emptyIcon = iconMap[type].empty;\n const icon = iconMap[type].filled;\n\n const isReview = reviews > 0 && linkFdKey && linkHref;\n\n const handleChange = (event: React.SyntheticEvent, value: number | null): void => {\n if (value !== null) onChange?.(event, value);\n else onChange?.(event, 0);\n };\n\n const getLabelText = (labelValue: number): string => {\n if (type === 'heart') {\n return translate('number_out_of_total_hearts', { number: labelValue, total: max });\n }\n return translate('number_out_of_total_stars', { number: labelValue, total: max });\n };\n\n return (\n <StyledContainer layout={layout}>\n <StyledIconContainer>\n <MuiRating\n {...props}\n data-fd={fdKey}\n emptyIcon={emptyIcon}\n getLabelText={getLabelText}\n icon={icon}\n max={max}\n name={fdKey}\n onChange={handleChange}\n precision={0.5}\n readOnly={onChange === undefined}\n value={value}\n />\n <StyledValueText aria-hidden variant=\"b1Strong\">\n {value?.toFixed(1)}\n </StyledValueText>\n </StyledIconContainer>\n {isReview && (\n <StyledReviewsContainer>\n {layout === 'vertical' && (\n <StyledReviewsText variant=\"captionWeak\">{translate('From')} </StyledReviewsText>\n )}\n <Link fdKey={linkFdKey} href={linkHref} size=\"caption\" tone=\"Brand\">\n {reviews === 1 ? translate('one_review') : translate('total_reviews', { total: reviews })}\n </Link>\n </StyledReviewsContainer>\n )}\n </StyledContainer>\n );\n};\n\nexport default Rating;\n"],"names":["StyledContainer","styled","Box","shouldForwardProp","prop","theme","layout","display","flexDirection","gap","spacing","justifyContent","StyledIconContainer","alignItems","StyledReviewsContainer","StyledValueText","Typography","color","palette","semantic","text","StyledReviewsText","iconMap","heart","empty","_jsx","HeartEmptyIcon","filled","HeartFilledIcon","star","StarEmptyIcon","StarFilledIcon","Rating","fdKey","linkFdKey","linkHref","max","onChange","reviews","type","value","props","translate","useTranslation","emptyIcon","icon","isReview","_jsxs","children","MuiRating","getLabelText","labelValue","number","total","name","event","precision","readOnly","undefined","variant","toFixed","Link","href","size","tone"],"mappings":"wXAmDA,MAAMA,EAAkBC,EAAAA,OAAOC,EAAK,CAAEC,kBAAoBC,GAAkB,WAATA,GAA3CH,EAErB,EAAGI,QAAOC,aAAQ,CACnBC,QAAS,OACTC,cAA0B,eAAXF,EAA0B,MAAQ,SACjDG,IAAgB,eAAXH,EAA0BD,EAAMK,QAAQ,GAAKL,EAAMK,QAAQ,IAChEC,eAAgB,aAGZC,EAAsBX,EAAAA,OAAOC,EAAPD,EAAY,EAAGI,YAAO,CAChDQ,WAAY,SACZN,QAAS,OACTE,IAAKJ,EAAMK,QAAQ,GACnBC,eAAgB,aAGZG,EAAyBb,EAAAA,OAAOC,EAAPD,EAAY,EAAGI,YAAO,CACnDQ,WAAY,SACZN,QAAS,OACTE,IAAKJ,EAAMK,QAAQ,IACnBC,eAAgB,iBAGZI,EAAkBd,EAAAA,OAAOe,EAAPf,EAAmB,EAAGI,YAAO,CACnDY,MAAOZ,EAAMa,QAAQC,SAASC,KAAK,mBAG/BC,EAAoBpB,EAAAA,OAAOe,EAAPf,EAAmB,EAAGI,YAAO,CACrDY,MAAOZ,EAAMa,QAAQC,SAASC,KAAK,iBAG/BE,EAAU,CACdC,MAAO,CACLC,MAAOC,EAAAA,IAACC,EAAAA,eAAc,CAAA,cAAa,qBACnCC,OAAQF,EAAAA,IAACG,EAAAA,gBAAe,CAAA,cAAa,uBAEvCC,KAAM,CACJL,MAAOC,EAAAA,IAACK,EAAAA,cAAa,CAAA,cAAa,oBAClCH,OAAQF,EAAAA,IAACM,EAAAA,eAAc,CAAA,cAAa,uBAU3BC,EAAgC,EAC3CC,QACA3B,SAAS,aACT4B,YACAC,WACAC,MAAM,EACNC,WACAC,UAAU,EACVC,OAAO,OACPC,WACGC,MAEH,MAAMC,UAAEA,GAAcC,mBAEhBC,EAAYtB,EAAQiB,GAAMf,MAC1BqB,EAAOvB,EAAQiB,GAAMZ,OAErBmB,EAAWR,EAAU,GAAKJ,GAAaC,EAc7C,OACEY,EAAAA,KAAC/C,EAAe,CAACM,OAAQA,EAAM0C,SAAA,CAC7BD,EAAAA,KAACnC,EAAmB,CAAAoC,SAAA,CAClBvB,MAACwB,MACKR,EAAK,UACAR,EACTW,UAAWA,EACXM,aAdcC,GAEXT,EADI,UAATH,EACe,6BAEF,4BAFgC,CAAEa,OAAQD,EAAYE,MAAOjB,IAaxES,KAAMA,EACNT,IAAKA,EACLkB,KAAMrB,EACNI,SAvBa,CAACkB,EAA6Bf,KAC7BH,IAAWkB,EAAjB,OAAVf,EAAkCA,EACf,IAsBjBgB,UAAW,GACXC,cAAuBC,IAAbrB,EACVG,MAAOA,IAETf,EAAAA,IAACV,oBAA4B4C,QAAQ,WAAUX,SAC5CR,GAAOoB,QAAQ,QAGnBd,GACCC,OAACjC,aACa,aAAXR,GACCyC,OAAC1B,EAAiB,CAACsC,QAAQ,cAAaX,SAAA,CAAEN,EAAU,QAAO,OAE7DjB,EAAAA,IAACoC,EAAAA,KAAI,CAAC5B,MAAOC,EAAW4B,KAAM3B,EAAU4B,KAAK,UAAUC,KAAK,QAAOhB,SACpD,IAAZV,EAAgBI,EAAU,cAAgBA,EAAU,gBAAiB,CAAEW,MAAOf"}
|
|
@@ -1,16 +1,5 @@
|
|
|
1
1
|
import { RatingProps as RatingProps$1 } from '@mui/material/Rating';
|
|
2
2
|
|
|
3
|
-
interface RatingTranslations {
|
|
4
|
-
/** The text to read for the accessibility of the rating component
|
|
5
|
-
* Should be of format: {value} out of {max} {iconType}
|
|
6
|
-
* e.g. "4.5 out of 5 stars"
|
|
7
|
-
*/
|
|
8
|
-
accessibilityRatingText: string;
|
|
9
|
-
/** The text to display before the number of reviews */
|
|
10
|
-
fromText: string;
|
|
11
|
-
/** The text to display after the number of reviews */
|
|
12
|
-
reviewsText: string;
|
|
13
|
-
}
|
|
14
3
|
type BaseRatingProps = Omit<RatingProps$1, 'emptyIcon' | 'icon' | 'onChange' | 'precision' | 'readOnly' | 'value'> & {
|
|
15
4
|
/** The identifier for the rating component */
|
|
16
5
|
fdKey: string;
|
|
@@ -30,8 +19,6 @@ interface NonReviewRatingProps extends BaseRatingProps {
|
|
|
30
19
|
linkHref?: never;
|
|
31
20
|
/** The number of reviews */
|
|
32
21
|
reviews?: never;
|
|
33
|
-
/** The translations for the rating component */
|
|
34
|
-
translations?: never;
|
|
35
22
|
}
|
|
36
23
|
interface ReviewRatingProps extends BaseRatingProps {
|
|
37
24
|
/** The layout of the rating component */
|
|
@@ -42,8 +29,6 @@ interface ReviewRatingProps extends BaseRatingProps {
|
|
|
42
29
|
linkHref: string;
|
|
43
30
|
/** The number of reviews */
|
|
44
31
|
reviews: number;
|
|
45
|
-
/** The translations for the rating component */
|
|
46
|
-
translations: RatingTranslations;
|
|
47
32
|
}
|
|
48
33
|
/**
|
|
49
34
|
* The props for the Rating component.
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{jsx as t,jsxs as e}from"react/jsx-runtime";import a from"@mui/material/Rating";import{styled as i}from"@mui/material/styles";import r from"@mui/material/Box";import
|
|
1
|
+
import{jsx as t,jsxs as e}from"react/jsx-runtime";import a from"@mui/material/Rating";import{styled as i}from"@mui/material/styles";import r from"@mui/material/Box";import o from"@mui/material/Typography";import{useTranslation as n}from"../../../providers/TranslationProvider.js";import{Link as l}from"../../atoms/Link/index.js";import{StarFilledIcon as m,StarEmptyIcon as s,HeartFilledIcon as d,HeartEmptyIcon as p}from"./icons/index.js";const c=i(r,{shouldForwardProp:t=>"layout"!==t})((({theme:t,layout:e})=>({display:"flex",flexDirection:"horizontal"===e?"row":"column",gap:"horizontal"===e?t.spacing(1):t.spacing(.5),justifyContent:"center"}))),f=i(r)((({theme:t})=>({alignItems:"center",display:"flex",gap:t.spacing(1),justifyContent:"center"}))),h=i(r)((({theme:t})=>({alignItems:"center",display:"flex",gap:t.spacing(.5),justifyContent:"flex-start"}))),u=i(o)((({theme:t})=>({color:t.palette.semantic.text["text-strong"]}))),y=i(o)((({theme:t})=>({color:t.palette.semantic.text["text-weak"]}))),x={heart:{empty:t(p,{"data-testid":"heart-empty-icon"}),filled:t(d,{"data-testid":"heart-filled-icon"})},star:{empty:t(s,{"data-testid":"star-empty-icon"}),filled:t(m,{"data-testid":"star-filled-icon"})}},g=({fdKey:i,layout:r="horizontal",linkFdKey:o,linkHref:m,max:s=5,onChange:d,reviews:p=0,type:g="star",value:v,..._})=>{const{translate:j}=n(),w=x[g].empty,b=x[g].filled,k=p>0&&o&&m;return e(c,{layout:r,children:[e(f,{children:[t(a,{..._,"data-fd":i,emptyIcon:w,getLabelText:t=>j("heart"===g?"number_out_of_total_hearts":"number_out_of_total_stars",{number:t,total:s}),icon:b,max:s,name:i,onChange:(t,e)=>{d?.(t,null!==e?e:0)},precision:.5,readOnly:void 0===d,value:v}),t(u,{"aria-hidden":!0,variant:"b1Strong",children:v?.toFixed(1)})]}),k&&e(h,{children:["vertical"===r&&e(y,{variant:"captionWeak",children:[j("From")," "]}),t(l,{fdKey:o,href:m,size:"caption",tone:"Brand",children:1===p?j("one_review"):j("total_reviews",{total:p})})]})]})};export{g as Rating,g as default};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../../../src/components/molecules/Rating/index.tsx"],"sourcesContent":["import MuiRating, { type RatingProps as MuiRatingProps } from '@mui/material/Rating';\nimport { styled } from '@mui/material/styles';\n\nimport Box from '@fd/components/atoms/Box';\nimport Typography from '@fd/components/atoms/Typography';\n\nimport Link from '../../atoms/Link';\nimport { HeartEmptyIcon, HeartFilledIcon, StarEmptyIcon, StarFilledIcon } from './icons';\n\
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../../src/components/molecules/Rating/index.tsx"],"sourcesContent":["import MuiRating, { type RatingProps as MuiRatingProps } from '@mui/material/Rating';\nimport { styled } from '@mui/material/styles';\n\nimport Box from '@fd/components/atoms/Box';\nimport Typography from '@fd/components/atoms/Typography';\nimport { useTranslation } from '@fd/providers/TranslationProvider';\n\nimport Link from '../../atoms/Link';\nimport { HeartEmptyIcon, HeartFilledIcon, StarEmptyIcon, StarFilledIcon } from './icons';\n\ntype BaseRatingProps = Omit<\n MuiRatingProps,\n 'emptyIcon' | 'icon' | 'onChange' | 'precision' | 'readOnly' | 'value'\n> & {\n /** The identifier for the rating component */\n fdKey: string;\n /** The function to call when the rating value changes */\n onChange?: (event: React.SyntheticEvent, value: number) => void;\n /** The type of rating component to use */\n type?: 'heart' | 'star';\n /** The value of the rating component */\n value: number;\n};\n\ninterface NonReviewRatingProps extends BaseRatingProps {\n /** The layout of the rating component */\n layout?: 'horizontal' | undefined;\n /** The identifier for the link component */\n linkFdKey?: never;\n /** The href for the link component */\n linkHref?: never;\n /** The number of reviews */\n reviews?: never;\n}\n\ninterface ReviewRatingProps extends BaseRatingProps {\n /** The layout of the rating component */\n layout?: 'horizontal' | 'vertical';\n /** The identifier for the link component */\n linkFdKey: string;\n /** The href for the link component */\n linkHref: string;\n /** The number of reviews */\n reviews: number;\n}\n\n/**\n * The props for the Rating component.\n */\nexport type RatingProps = NonReviewRatingProps | ReviewRatingProps;\n\nconst StyledContainer = styled(Box, { shouldForwardProp: (prop) => prop !== 'layout' })<{\n layout: RatingProps['layout'];\n}>(({ theme, layout }) => ({\n display: 'flex',\n flexDirection: layout === 'horizontal' ? 'row' : 'column',\n gap: layout === 'horizontal' ? theme.spacing(1) : theme.spacing(0.5),\n justifyContent: 'center',\n}));\n\nconst StyledIconContainer = styled(Box)(({ theme }) => ({\n alignItems: 'center',\n display: 'flex',\n gap: theme.spacing(1),\n justifyContent: 'center',\n}));\n\nconst StyledReviewsContainer = styled(Box)(({ theme }) => ({\n alignItems: 'center',\n display: 'flex',\n gap: theme.spacing(0.5),\n justifyContent: 'flex-start',\n}));\n\nconst StyledValueText = styled(Typography)(({ theme }) => ({\n color: theme.palette.semantic.text['text-strong'],\n}));\n\nconst StyledReviewsText = styled(Typography)(({ theme }) => ({\n color: theme.palette.semantic.text['text-weak'],\n}));\n\nconst iconMap = {\n heart: {\n empty: <HeartEmptyIcon data-testid=\"heart-empty-icon\" />,\n filled: <HeartFilledIcon data-testid=\"heart-filled-icon\" />,\n },\n star: {\n empty: <StarEmptyIcon data-testid=\"star-empty-icon\" />,\n filled: <StarFilledIcon data-testid=\"star-filled-icon\" />,\n },\n};\n\n/**\n * Rating component allows users to provide feedback by rating items with stars.\n *\n * @param props - Additional props to pass to the underlying MUI Rating component\n * @returns A Rating component\n */\nexport const Rating: React.FC<RatingProps> = ({\n fdKey,\n layout = 'horizontal',\n linkFdKey,\n linkHref,\n max = 5,\n onChange,\n reviews = 0,\n type = 'star',\n value,\n ...props\n}) => {\n const { translate } = useTranslation();\n\n const emptyIcon = iconMap[type].empty;\n const icon = iconMap[type].filled;\n\n const isReview = reviews > 0 && linkFdKey && linkHref;\n\n const handleChange = (event: React.SyntheticEvent, value: number | null): void => {\n if (value !== null) onChange?.(event, value);\n else onChange?.(event, 0);\n };\n\n const getLabelText = (labelValue: number): string => {\n if (type === 'heart') {\n return translate('number_out_of_total_hearts', { number: labelValue, total: max });\n }\n return translate('number_out_of_total_stars', { number: labelValue, total: max });\n };\n\n return (\n <StyledContainer layout={layout}>\n <StyledIconContainer>\n <MuiRating\n {...props}\n data-fd={fdKey}\n emptyIcon={emptyIcon}\n getLabelText={getLabelText}\n icon={icon}\n max={max}\n name={fdKey}\n onChange={handleChange}\n precision={0.5}\n readOnly={onChange === undefined}\n value={value}\n />\n <StyledValueText aria-hidden variant=\"b1Strong\">\n {value?.toFixed(1)}\n </StyledValueText>\n </StyledIconContainer>\n {isReview && (\n <StyledReviewsContainer>\n {layout === 'vertical' && (\n <StyledReviewsText variant=\"captionWeak\">{translate('From')} </StyledReviewsText>\n )}\n <Link fdKey={linkFdKey} href={linkHref} size=\"caption\" tone=\"Brand\">\n {reviews === 1 ? translate('one_review') : translate('total_reviews', { total: reviews })}\n </Link>\n </StyledReviewsContainer>\n )}\n </StyledContainer>\n );\n};\n\nexport default Rating;\n"],"names":["StyledContainer","styled","Box","shouldForwardProp","prop","theme","layout","display","flexDirection","gap","spacing","justifyContent","StyledIconContainer","alignItems","StyledReviewsContainer","StyledValueText","Typography","color","palette","semantic","text","StyledReviewsText","iconMap","heart","empty","_jsx","HeartEmptyIcon","filled","HeartFilledIcon","star","StarEmptyIcon","StarFilledIcon","Rating","fdKey","linkFdKey","linkHref","max","onChange","reviews","type","value","props","translate","useTranslation","emptyIcon","icon","isReview","_jsxs","children","MuiRating","getLabelText","labelValue","number","total","name","event","precision","readOnly","undefined","variant","toFixed","Link","href","size","tone"],"mappings":"ubAmDA,MAAMA,EAAkBC,EAAOC,EAAK,CAAEC,kBAAoBC,GAAkB,WAATA,GAA3CH,EAErB,EAAGI,QAAOC,aAAQ,CACnBC,QAAS,OACTC,cAA0B,eAAXF,EAA0B,MAAQ,SACjDG,IAAgB,eAAXH,EAA0BD,EAAMK,QAAQ,GAAKL,EAAMK,QAAQ,IAChEC,eAAgB,aAGZC,EAAsBX,EAAOC,EAAPD,EAAY,EAAGI,YAAO,CAChDQ,WAAY,SACZN,QAAS,OACTE,IAAKJ,EAAMK,QAAQ,GACnBC,eAAgB,aAGZG,EAAyBb,EAAOC,EAAPD,EAAY,EAAGI,YAAO,CACnDQ,WAAY,SACZN,QAAS,OACTE,IAAKJ,EAAMK,QAAQ,IACnBC,eAAgB,iBAGZI,EAAkBd,EAAOe,EAAPf,EAAmB,EAAGI,YAAO,CACnDY,MAAOZ,EAAMa,QAAQC,SAASC,KAAK,mBAG/BC,EAAoBpB,EAAOe,EAAPf,EAAmB,EAAGI,YAAO,CACrDY,MAAOZ,EAAMa,QAAQC,SAASC,KAAK,iBAG/BE,EAAU,CACdC,MAAO,CACLC,MAAOC,EAACC,EAAc,CAAA,cAAa,qBACnCC,OAAQF,EAACG,EAAe,CAAA,cAAa,uBAEvCC,KAAM,CACJL,MAAOC,EAACK,EAAa,CAAA,cAAa,oBAClCH,OAAQF,EAACM,EAAc,CAAA,cAAa,uBAU3BC,EAAgC,EAC3CC,QACA3B,SAAS,aACT4B,YACAC,WACAC,MAAM,EACNC,WACAC,UAAU,EACVC,OAAO,OACPC,WACGC,MAEH,MAAMC,UAAEA,GAAcC,IAEhBC,EAAYtB,EAAQiB,GAAMf,MAC1BqB,EAAOvB,EAAQiB,GAAMZ,OAErBmB,EAAWR,EAAU,GAAKJ,GAAaC,EAc7C,OACEY,EAAC/C,EAAe,CAACM,OAAQA,EAAM0C,SAAA,CAC7BD,EAACnC,EAAmB,CAAAoC,SAAA,CAClBvB,EAACwB,MACKR,EAAK,UACAR,EACTW,UAAWA,EACXM,aAdcC,GAEXT,EADI,UAATH,EACe,6BAEF,4BAFgC,CAAEa,OAAQD,EAAYE,MAAOjB,IAaxES,KAAMA,EACNT,IAAKA,EACLkB,KAAMrB,EACNI,SAvBa,CAACkB,EAA6Bf,KAC7BH,IAAWkB,EAAjB,OAAVf,EAAkCA,EACf,IAsBjBgB,UAAW,GACXC,cAAuBC,IAAbrB,EACVG,MAAOA,IAETf,EAACV,oBAA4B4C,QAAQ,WAAUX,SAC5CR,GAAOoB,QAAQ,QAGnBd,GACCC,EAACjC,aACa,aAAXR,GACCyC,EAAC1B,EAAiB,CAACsC,QAAQ,cAAaX,SAAA,CAAEN,EAAU,QAAO,OAE7DjB,EAACoC,EAAI,CAAC5B,MAAOC,EAAW4B,KAAM3B,EAAU4B,KAAK,UAAUC,KAAK,QAAOhB,SACpD,IAAZV,EAAgBI,EAAU,cAAgBA,EAAU,gBAAiB,CAAEW,MAAOf"}
|