@lifesg/react-design-system 4.0.0-alpha.8 → 4.0.0-alpha.9
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/accordion/accordion-item.js.map +1 -1
- package/accordion/accordion.js.map +1 -1
- package/alert/alert.js.map +1 -1
- package/cjs/accordion/accordion-item.js.map +1 -1
- package/cjs/accordion/accordion.js.map +1 -1
- package/cjs/alert/alert.js.map +1 -1
- package/cjs/countdown-timer/countdown-timer.js.map +1 -1
- package/cjs/data-table/data-table.js +1 -1
- package/cjs/data-table/data-table.js.map +1 -1
- package/cjs/date-range-input/date-range-input.js.map +1 -1
- package/cjs/e-signature/e-signature-canvas.js.map +1 -1
- package/cjs/error-display/inactivity-timer.js.map +1 -1
- package/cjs/external/dompurify/dist/purify.es.js +2 -2
- package/cjs/external/dompurify/dist/purify.es.js.map +1 -1
- package/cjs/file-download/file-list-card/file-list-card.js.map +1 -1
- package/cjs/file-upload/custom-sensors.js.map +1 -1
- package/cjs/file-upload/file-list/file-list.js.map +1 -1
- package/cjs/filter/addons/filter-item-checkbox-helpers.js.map +1 -1
- package/cjs/filter/addons/filter-item-checkbox.js.map +1 -1
- package/cjs/filter/filter-item.js.map +1 -1
- package/cjs/filter/filter.js.map +1 -1
- package/cjs/form/form-textarea/form-textarea.js +1 -1
- package/cjs/form/form-textarea/form-textarea.js.map +1 -1
- package/cjs/fullscreen-image-carousel/stateful-image.js.map +1 -1
- package/cjs/histogram-slider/histogram-slider.js.map +1 -1
- package/cjs/input-range-slider/input-range-slider.js.map +1 -1
- package/cjs/input-slider/input-slider.js.map +1 -1
- package/cjs/input-textarea/textarea-counter.js.map +1 -1
- package/cjs/input-textarea/textarea.js +1 -1
- package/cjs/input-textarea/textarea.js.map +1 -1
- package/cjs/input-textarea/textarea.styles.js +1 -1
- package/cjs/input-textarea/textarea.styles.js.map +1 -1
- package/{input-textarea/textarea.styles_q77m5x.css → cjs/input-textarea/textarea.styles_6we7bs.css} +3 -1
- package/cjs/modal/index.js.map +1 -1
- package/cjs/modal-v2/modal-v2.js +1 -1
- package/cjs/modal-v2/modal-v2.js.map +1 -1
- package/cjs/modal-v2/slots/card.js +1 -1
- package/cjs/modal-v2/slots/card.js.map +1 -1
- package/cjs/modal-v2/slots/header.js +2 -0
- package/cjs/modal-v2/slots/header.js.map +1 -0
- package/cjs/modal-v2/slots/slot-styles.js +1 -1
- package/cjs/modal-v2/slots/slot-styles.js.map +1 -1
- package/cjs/modal-v2/slots/{slot-styles_13sd376.css → slot-styles_10ut1v7.css} +5 -1
- package/cjs/navbar/navbar-items.js +1 -1
- package/cjs/navbar/navbar-items.js.map +1 -1
- package/cjs/navbar/navbar-items.styles.js +1 -1
- package/cjs/navbar/navbar-items.styles.js.map +1 -1
- package/cjs/navbar/{navbar-items.styles_1i3ltot.css → navbar-items.styles_1b1zx.css} +9 -8
- package/cjs/navbar/navbar-logo-data.js +1 -1
- package/cjs/navbar/navbar-logo-data.js.map +1 -1
- package/cjs/navbar/navbar.js.map +1 -1
- package/cjs/otp-input/otp-input.js.map +1 -1
- package/cjs/overlay/overlay.js.map +1 -1
- package/cjs/pagination/pagination.js.map +1 -1
- package/cjs/phone-number-input/phone-number-input.js.map +1 -1
- package/cjs/predictive-text-input/predictive-text-input.js.map +1 -1
- package/cjs/shared/dropdown-list/dropdown-list.js.map +1 -1
- package/cjs/shared/internal-calendar/calendar-manager.js.map +1 -1
- package/cjs/shared/standalone-date-input/standalone-date-input.js.map +1 -1
- package/cjs/theme/types.js +1 -1
- package/cjs/theme/types.js.map +1 -1
- package/cjs/time-slot-bar/time-slot-bar.js.map +1 -1
- package/cjs/time-slot-bar-week/time-slot-bar-week-days.js.map +1 -1
- package/cjs/timetable/timetable.js.map +1 -1
- package/cjs/toggle/toggle-icon.js +1 -1
- package/cjs/toggle/toggle-icon.js.map +1 -1
- package/cjs/toggle/toggle-icon.styles.js +1 -1
- package/cjs/toggle/toggle-icon.styles.js.map +1 -1
- package/cjs/toggle/{toggle-icon.styles_ba71y.css → toggle-icon.styles_o6dxr3.css} +3 -2
- package/cjs/toggle/toggle.js +1 -1
- package/cjs/toggle/toggle.js.map +1 -1
- package/cjs/toggle/toggle.styles.js +1 -1
- package/cjs/toggle/toggle.styles.js.map +1 -1
- package/cjs/toggle/toggle.styles_11tmfsr.css +41 -0
- package/cjs/unit-number/unit-number-input.js.map +1 -1
- package/cjs/util/is-styled-components.js.map +1 -1
- package/cjs/util/string-helper.js +1 -1
- package/cjs/util/string-helper.js.map +1 -1
- package/cjs/util/use-event-listener.js.map +1 -1
- package/cjs/util/use-state-actions.js.map +1 -1
- package/countdown-timer/countdown-timer.js.map +1 -1
- package/data-table/data-table.js +1 -1
- package/data-table/data-table.js.map +1 -1
- package/data-table/types.d.ts +2 -2
- package/date-range-input/date-range-input.js.map +1 -1
- package/docs/component-catalog.json +1 -1
- package/e-signature/e-signature-canvas.js.map +1 -1
- package/error-display/inactivity-timer.js.map +1 -1
- package/external/dompurify/dist/purify.es.js +2 -2
- package/external/dompurify/dist/purify.es.js.map +1 -1
- package/file-download/file-list-card/file-list-card.js.map +1 -1
- package/file-upload/custom-sensors.d.ts +2 -2
- package/file-upload/custom-sensors.js.map +1 -1
- package/file-upload/file-list/file-list.js.map +1 -1
- package/filter/addons/filter-item-checkbox-helpers.js.map +1 -1
- package/filter/addons/filter-item-checkbox.js.map +1 -1
- package/filter/filter-item.js.map +1 -1
- package/filter/filter.js.map +1 -1
- package/form/form-textarea/form-textarea.js +1 -1
- package/form/form-textarea/form-textarea.js.map +1 -1
- package/fullscreen-image-carousel/stateful-image.js.map +1 -1
- package/fullscreen-image-carousel/types.d.ts +2 -2
- package/histogram-slider/histogram-slider.js.map +1 -1
- package/input-range-slider/input-range-slider.js.map +1 -1
- package/input-slider/input-slider.js.map +1 -1
- package/input-textarea/textarea-counter.js.map +1 -1
- package/input-textarea/textarea.js +1 -1
- package/input-textarea/textarea.js.map +1 -1
- package/input-textarea/textarea.styles.d.ts +2 -0
- package/input-textarea/textarea.styles.js +1 -1
- package/input-textarea/textarea.styles.js.map +1 -1
- package/{cjs/input-textarea/textarea.styles_q77m5x.css → input-textarea/textarea.styles_6we7bs.css} +3 -1
- package/input-textarea/types.d.ts +7 -0
- package/modal/index.d.ts +4 -0
- package/modal/index.js.map +1 -1
- package/modal/types.d.ts +2 -0
- package/modal-v2/modal-v2.d.ts +6 -0
- package/modal-v2/modal-v2.js +1 -1
- package/modal-v2/modal-v2.js.map +1 -1
- package/modal-v2/slots/card.js +1 -1
- package/modal-v2/slots/card.js.map +1 -1
- package/modal-v2/slots/header.d.ts +5 -0
- package/modal-v2/slots/header.js +2 -0
- package/modal-v2/slots/header.js.map +1 -0
- package/modal-v2/slots/index.d.ts +1 -0
- package/modal-v2/slots/slot-styles.d.ts +4 -0
- package/modal-v2/slots/slot-styles.js +1 -1
- package/modal-v2/slots/slot-styles.js.map +1 -1
- package/modal-v2/slots/{slot-styles_13sd376.css → slot-styles_10ut1v7.css} +5 -1
- package/modal-v2/types.d.ts +21 -0
- package/navbar/navbar-items.js +1 -1
- package/navbar/navbar-items.js.map +1 -1
- package/navbar/navbar-items.styles.d.ts +2 -1
- package/navbar/navbar-items.styles.js +1 -1
- package/navbar/navbar-items.styles.js.map +1 -1
- package/navbar/{navbar-items.styles_1i3ltot.css → navbar-items.styles_1b1zx.css} +9 -8
- package/navbar/navbar-logo-data.js +1 -1
- package/navbar/navbar-logo-data.js.map +1 -1
- package/navbar/navbar.js.map +1 -1
- package/otp-input/otp-input.js.map +1 -1
- package/overlay/overlay.js.map +1 -1
- package/package.json +1 -1
- package/pagination/pagination.js.map +1 -1
- package/phone-number-input/phone-number-input.js.map +1 -1
- package/predictive-text-input/predictive-text-input.js.map +1 -1
- package/shared/dropdown-list/dropdown-list.js.map +1 -1
- package/shared/internal-calendar/calendar-manager.js.map +1 -1
- package/shared/standalone-date-input/standalone-date-input.js.map +1 -1
- package/theme/styles/a11y-playground.css +358 -87
- package/theme/styles/bookingsg.css +391 -85
- package/theme/styles/careercompass.css +396 -90
- package/theme/styles/ccube.css +402 -45
- package/theme/styles/imda.css +348 -85
- package/theme/styles/lifesg.css +377 -87
- package/theme/styles/mylegacy.css +382 -85
- package/theme/styles/oneservice.css +349 -86
- package/theme/styles/pa.css +172 -63
- package/theme/styles/rbs.css +403 -46
- package/theme/styles/sgw-digital-lobby.css +352 -87
- package/theme/styles/smgs.css +401 -95
- package/theme/styles/spf.css +208 -99
- package/theme/styles/supportgowhere.css +387 -90
- package/theme/styles/tote-board.css +816 -0
- package/theme/types.d.ts +1 -1
- package/theme/types.js +1 -1
- package/theme/types.js.map +1 -1
- package/time-slot-bar/time-slot-bar.js.map +1 -1
- package/time-slot-bar-week/time-slot-bar-week-days.js.map +1 -1
- package/timetable/timetable.js.map +1 -1
- package/toggle/toggle-icon.d.ts +2 -1
- package/toggle/toggle-icon.js +1 -1
- package/toggle/toggle-icon.js.map +1 -1
- package/toggle/toggle-icon.styles.d.ts +1 -0
- package/toggle/toggle-icon.styles.js +1 -1
- package/toggle/toggle-icon.styles.js.map +1 -1
- package/toggle/{toggle-icon.styles_ba71y.css → toggle-icon.styles_o6dxr3.css} +3 -2
- package/toggle/toggle.js +1 -1
- package/toggle/toggle.js.map +1 -1
- package/toggle/toggle.styles.d.ts +18 -15
- package/toggle/toggle.styles.js +1 -1
- package/toggle/toggle.styles.js.map +1 -1
- package/toggle/toggle.styles_11tmfsr.css +41 -0
- package/unit-number/unit-number-input.js.map +1 -1
- package/util/is-styled-components.js.map +1 -1
- package/util/string-helper.d.ts +2 -2
- package/util/string-helper.js +1 -1
- package/util/string-helper.js.map +1 -1
- package/util/use-event-listener.js.map +1 -1
- package/util/use-state-actions.js.map +1 -1
- package/cjs/form/form-textarea/form-textarea.styles.js +0 -2
- package/cjs/form/form-textarea/form-textarea.styles.js.map +0 -1
- package/cjs/form/form-textarea/form-textarea.styles_1kneyif.css +0 -3
- package/cjs/toggle/toggle.styles_1t5qrye.css +0 -38
- package/form/form-textarea/form-textarea.styles.d.ts +0 -3
- package/form/form-textarea/form-textarea.styles.js +0 -2
- package/form/form-textarea/form-textarea.styles.js.map +0 -1
- package/form/form-textarea/form-textarea.styles_1kneyif.css +0 -3
- package/theme/styles/default.css +0 -158
- package/toggle/toggle.styles_1t5qrye.css +0 -38
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pagination.js","sources":["../../../src/pagination/pagination.tsx"],"sourcesContent":["import { Chevron2LeftIcon } from \"@lifesg/react-icons/chevron-2-left\";\nimport { Chevron2RightIcon } from \"@lifesg/react-icons/chevron-2-right\";\nimport { ChevronLeftIcon } from \"@lifesg/react-icons/chevron-left\";\nimport { ChevronLineLeftIcon } from \"@lifesg/react-icons/chevron-line-left\";\nimport { ChevronLineRightIcon } from \"@lifesg/react-icons/chevron-line-right\";\nimport { ChevronRightIcon } from \"@lifesg/react-icons/chevron-right\";\nimport { EllipsisHorizontalIcon } from \"@lifesg/react-icons/ellipsis-horizontal\";\nimport clsx from \"clsx\";\nimport React, { useEffect, useState } from \"react\";\n\nimport { Input } from \"../input\";\nimport { InputSelect } from \"../input-select\";\nimport { VisuallyHidden } from \"../shared/accessibility\";\nimport { ClickableIcon } from \"../shared/clickable-icon\";\nimport { useMaxWidthMediaQuery } from \"../theme\";\nimport { Typography } from \"../typography\";\nimport { useId, useIsMounted } from \"../util\";\nimport * as styles from \"./pagination.styles\";\nimport type { PageSizeItemProps, PaginationProps } from \"./types\";\n\nconst Component = (\n {\n id,\n \"data-testid\": dataTestId,\n className,\n pageSize = 10,\n totalItems,\n activePage,\n pageSizeOptions = DEFAULT_OPTIONS,\n showFirstAndLastNav,\n showPageSizeChanger = false,\n onPageChange,\n onPageSizeChange,\n }: PaginationProps,\n ref: React.Ref<HTMLDivElement>\n) => {\n // =============================================================================\n // CONST, STATE, REF\n // =============================================================================\n\n const isMounted = useIsMounted();\n const isMobile = useMaxWidthMediaQuery(\"sm\");\n const [hoverRightButton, setHoverRightButton] = useState(false);\n const [hoverLeftButton, setHoverLeftButton] = useState(false);\n const [inputText, setInputText] = useState<string>(\"\");\n\n const [selectedOption, setSelectedOption] = useState<\n PageSizeItemProps | undefined\n >(pageSizeOptions[0]);\n const [pageSizeLocal, setPageSize] = useState<number>(\n !isMobile && showPageSizeChanger\n ? selectedOption\n ? selectedOption.value\n : pageSize\n : pageSize\n );\n\n const boundaryRange = 1;\n const siblingRange = 1;\n\n const internalId = useId();\n const paginationId = `${internalId}-pagination`;\n\n const totalPages = Math.ceil(totalItems / pageSizeLocal);\n const isFirstPage = activePage === 1;\n const isLastPage = activePage === totalPages;\n\n const firstPaginationItem =\n activePage > 1 ? () => handlePaginationItemClick(1) : undefined;\n const lastPaginationItem =\n activePage < totalPages\n ? () => handlePaginationItemClick(totalPages)\n : undefined;\n const prevPaginationItem =\n activePage > 1\n ? () => handlePaginationItemClick(activePage - 1)\n : undefined;\n const nextPaginationItem =\n activePage < totalPages\n ? () =>\n handlePaginationItemClick(parseInt(activePage.toString()) + 1)\n : undefined;\n\n // =============================================================================\n // EFFECTS\n // =============================================================================\n useEffect(() => {\n if (activePage) {\n setInputValue(activePage);\n }\n }, [activePage]);\n\n useEffect(() => {\n setPageSize(pageSize);\n setSelectedOption(\n pageSizeOptions.find((option) => option.value === pageSize)\n );\n }, [pageSize]);\n\n // =============================================================================\n // HELPER FUNCTIONS\n // =============================================================================\n const setInputValue = (value: number) => {\n setInputText(value.toString());\n };\n\n const closeAllTooltips = () => {\n setHoverRightButton(false);\n setHoverLeftButton(false);\n };\n\n // =============================================================================\n // EVENT HANDLERS\n // =============================================================================\n const handlePaginationItemClick = (pageIndex: number) => {\n if (onPageChange) {\n onPageChange(pageIndex);\n setInputValue(pageIndex);\n }\n };\n\n const handleFastForwardClick = () => {\n const currentIndex = Math.min(totalPages, activePage + 5);\n handlePaginationItemClick(currentIndex);\n setInputValue(currentIndex);\n setHoverRightButton(true);\n setHoverLeftButton(false);\n };\n\n const handleFastBackwardClick = () => {\n const currentIndex = Math.max(1, activePage - 5);\n handlePaginationItemClick(currentIndex);\n setInputValue(currentIndex);\n setHoverRightButton(false);\n setHoverLeftButton(true);\n };\n\n const handleInput = (event: React.ChangeEvent<HTMLInputElement>) => {\n const value = event.target.value;\n const numericRegex = /^[0-9]+$/;\n if (value === undefined || value.length === 0) {\n setInputText(\"\");\n } else if (!numericRegex.test(value)) {\n setInputText(value.replace(/[^0-9]/g, \"\"));\n } else {\n const newPage = parseInt(value.replace(/[^0-9]/g, \"\"));\n setInputValue(Math.max(1, Math.min(totalPages, newPage)));\n }\n };\n\n const handleInputSubmit = (event: React.FormEvent) => {\n event.preventDefault();\n if (inputText) {\n onPageChange?.(parseInt(inputText));\n }\n };\n\n const onHoverRightButton = () => {\n setHoverRightButton(true);\n };\n\n const onBlurRightButton = () => {\n setHoverRightButton(false);\n };\n\n const onHoverLeftButton = () => {\n setHoverLeftButton(true);\n };\n\n const onBlurLeftButton = () => {\n setHoverLeftButton(false);\n };\n\n const handleListItemClick = (item: PageSizeItemProps) => {\n setSelectedOption(item);\n const pagesize = item.value;\n const totalPage = Math.ceil(totalItems / pagesize);\n\n setPageSize(pagesize);\n\n const page = activePage >= totalPage ? totalPage : activePage;\n\n if (onPageSizeChange) {\n onPageSizeChange(page, pagesize);\n }\n };\n\n // =============================================================================\n // RENDER FUNCTIONS\n // =============================================================================\n const renderPaginationItems = () => {\n return [...Array(totalPages)].map((e, i) => {\n const pageIndex = i + 1;\n const startRange = boundaryRange + 2 + siblingRange * 2;\n const endRange = totalPages - startRange;\n const totalRange = (boundaryRange + siblingRange) * 2 + 3;\n const active = activePage === pageIndex;\n if (totalPages <= totalRange) {\n return (\n <button\n key={pageIndex}\n onClick={() => handlePaginationItemClick(pageIndex)}\n className={clsx(\n styles.pageItem,\n active && styles.pageItemSelected\n )}\n aria-label={`page ${pageIndex} of ${totalPages}`}\n aria-current={active ? \"page\" : false}\n onMouseOver={closeAllTooltips}\n onFocus={closeAllTooltips}\n >\n {pageIndex}\n </button>\n );\n }\n\n const ellipsisStart =\n activePage + siblingRange > startRange &&\n pageIndex === boundaryRange + 1;\n if (ellipsisStart) {\n return renderStartEllipsis(pageIndex);\n }\n\n const ellipsisEnd =\n activePage - siblingRange <= endRange &&\n pageIndex === totalPages - boundaryRange - 1;\n if (ellipsisEnd) {\n return renderEndEllipsis(pageIndex);\n }\n\n const paginationStart =\n (pageIndex <= startRange &&\n activePage + siblingRange <= startRange) ||\n pageIndex <= boundaryRange;\n const paginationMiddle =\n pageIndex === activePage ||\n (pageIndex <= activePage + siblingRange &&\n pageIndex >= activePage - siblingRange - 1);\n const paginationEnd =\n (pageIndex > endRange &&\n activePage - siblingRange > endRange) ||\n pageIndex > totalPages - boundaryRange;\n if (paginationStart || paginationMiddle || paginationEnd) {\n return (\n <button\n key={pageIndex}\n onClick={() => handlePaginationItemClick(pageIndex)}\n className={clsx(\n styles.pageItem,\n active && styles.pageItemSelected\n )}\n aria-label={`page ${pageIndex} of ${totalPages}`}\n aria-current={active ? \"page\" : false}\n onMouseOver={closeAllTooltips}\n onFocus={closeAllTooltips}\n >\n {pageIndex}\n </button>\n );\n }\n\n return null;\n });\n };\n\n const renderStartEllipsis = (pageIndex: number) => (\n <div key={pageIndex} className={styles.ellipsisContainer}>\n <ClickableIcon\n className={styles.iconButton}\n focusHighlight={false}\n focusOutline=\"browser\"\n aria-label={\"Previous 5 pages\"}\n onMouseEnter={onHoverLeftButton}\n onMouseLeave={onBlurLeftButton}\n onFocus={onHoverLeftButton}\n onBlur={onBlurLeftButton}\n onClick={handleFastBackwardClick}\n >\n {hoverLeftButton ? (\n <Chevron2LeftIcon aria-hidden />\n ) : (\n <EllipsisHorizontalIcon aria-hidden />\n )}\n </ClickableIcon>\n {hoverLeftButton && (\n <div className={styles.hover}>Previous 5 pages</div>\n )}\n </div>\n );\n\n const renderEndEllipsis = (pageIndex: number) => (\n <div key={pageIndex} className={styles.ellipsisContainer}>\n <ClickableIcon\n className={styles.iconButton}\n focusHighlight={false}\n focusOutline=\"browser\"\n aria-label={\"Next 5 pages\"}\n onMouseEnter={onHoverRightButton}\n onMouseLeave={onBlurRightButton}\n onFocus={onHoverRightButton}\n onBlur={onBlurRightButton}\n onClick={handleFastForwardClick}\n >\n {hoverRightButton ? (\n <Chevron2RightIcon aria-hidden />\n ) : (\n <EllipsisHorizontalIcon aria-hidden />\n )}\n </ClickableIcon>\n {hoverRightButton && (\n <div className={styles.hover}>Next 5 pages</div>\n )}\n </div>\n );\n\n const renderMobile = () => (\n <div className={styles.paginationMobileInput}>\n <form onSubmit={handleInputSubmit}>\n <Input\n className={styles.inputView}\n value={inputText}\n onChange={handleInput}\n autoComplete=\"off\"\n type=\"numeric\"\n id={(id || \"pagination\") + \"-input\"}\n data-testid={(dataTestId || \"pagination\") + \"-input\"}\n aria-label={`Page ${activePage} of ${totalPages}`}\n />\n </form>\n <Typography.BodyBL className={styles.labelDivider}>\n /\n </Typography.BodyBL>\n <Typography.BodyBL className={styles.label}>\n {totalPages}\n </Typography.BodyBL>\n </div>\n );\n\n return (\n <nav\n className={clsx(styles.paginationWrapper, className)}\n ref={ref}\n id={id || \"pagination-wrapper\"}\n data-testid={dataTestId || \"pagination\"}\n aria-labelledby={paginationId}\n >\n <VisuallyHidden id={paginationId} aria-hidden>\n pagination\n </VisuallyHidden>\n <div className={styles.paginationList}>\n <div className={styles.paginationMenu}>\n {showFirstAndLastNav && (\n <ClickableIcon\n className={clsx(\n styles.iconButton,\n styles.iconButtonNavigation\n )}\n onClick={firstPaginationItem}\n disabled={isFirstPage}\n focusHighlight={false}\n aria-label=\"First page\"\n focusOutline=\"browser\"\n >\n <ChevronLineLeftIcon aria-hidden />\n </ClickableIcon>\n )}\n <ClickableIcon\n className={clsx(\n styles.iconButton,\n styles.iconButtonNavigation\n )}\n onClick={prevPaginationItem}\n disabled={isFirstPage}\n focusHighlight={false}\n aria-label=\"Previous page\"\n focusOutline=\"browser\"\n >\n <ChevronLeftIcon aria-hidden />\n </ClickableIcon>\n {isMounted && isMobile\n ? renderMobile()\n : renderPaginationItems()}\n <ClickableIcon\n className={clsx(\n styles.iconButton,\n styles.iconButtonNavigation\n )}\n onClick={nextPaginationItem}\n disabled={isLastPage}\n focusHighlight={false}\n aria-label=\"Next page\"\n focusOutline=\"browser\"\n >\n <ChevronRightIcon aria-hidden />\n </ClickableIcon>\n {showFirstAndLastNav && (\n <ClickableIcon\n className={clsx(\n styles.iconButton,\n styles.iconButtonNavigation\n )}\n onClick={lastPaginationItem}\n disabled={isLastPage}\n focusHighlight={false}\n aria-label=\"Last page\"\n focusOutline=\"browser\"\n >\n <ChevronLineRightIcon aria-hidden />\n </ClickableIcon>\n )}\n </div>\n </div>\n {showPageSizeChanger && !isMobile && (\n <div className={styles.inputSelectWrapper}>\n <VisuallyHidden id={`${paginationId}-page-size`}>\n Items per page\n </VisuallyHidden>\n <InputSelect\n aria-labelledby={`${paginationId}-page-size`}\n options={pageSizeOptions}\n valueExtractor={(item) => item.value}\n listExtractor={(item) => item.label}\n displayValueExtractor={(item) => item.label}\n selectedOption={selectedOption}\n onSelectOption={handleListItemClick}\n />\n </div>\n )}\n </nav>\n );\n};\n// @catalog\n/**\n * A navigation control for moving through paged content.\n *\n * Use `Pagination` to let users navigate between pages of a data set.\n */\nexport const Pagination = React.forwardRef(Component);\n\nconst DEFAULT_OPTIONS: PageSizeItemProps[] = [\n { value: 10, label: \"10 per page\" },\n { value: 20, label: \"20 per page\" },\n { value: 30, label: \"30 per page\" },\n];\n"],"names":["Pagination","React","forwardRef","id","dataTestId","className","pageSize","totalItems","activePage","pageSizeOptions","DEFAULT_OPTIONS","showFirstAndLastNav","showPageSizeChanger","onPageChange","onPageSizeChange","ref","isMounted","useIsMounted","isMobile","useMaxWidthMediaQuery","hoverRightButton","setHoverRightButton","useState","hoverLeftButton","setHoverLeftButton","inputText","setInputText","selectedOption","setSelectedOption","pageSizeLocal","setPageSize","value","paginationId","useId","totalPages","Math","ceil","isFirstPage","isLastPage","firstPaginationItem","handlePaginationItemClick","undefined","lastPaginationItem","prevPaginationItem","nextPaginationItem","parseInt","toString","useEffect","setInputValue","find","option","closeAllTooltips","pageIndex","handleFastForwardClick","currentIndex","min","handleFastBackwardClick","max","handleInput","event","target","length","test","newPage","replace","handleInputSubmit","preventDefault","onHoverRightButton","onBlurRightButton","onHoverLeftButton","onBlurLeftButton","renderStartEllipsis","_jsxs","styles.ellipsisContainer","children","_jsx","ClickableIcon","styles.iconButton","focusHighlight","focusOutline","onMouseEnter","onMouseLeave","onFocus","onBlur","onClick","Chevron2LeftIcon","EllipsisHorizontalIcon","styles.hover","renderEndEllipsis","Chevron2RightIcon","clsx","styles.paginationWrapper","VisuallyHidden","styles.paginationList","styles.paginationMenu","styles.iconButtonNavigation","disabled","ChevronLineLeftIcon","ChevronLeftIcon","styles.paginationMobileInput","onSubmit","Input","styles.inputView","onChange","autoComplete","type","Typography","BodyBL","styles.labelDivider","styles.label","Array","map","e","i","endRange","boundaryRange","active","styles.pageItem","styles.pageItemSelected","onMouseOver","ChevronRightIcon","ChevronLineRightIcon","styles.inputSelectWrapper","InputSelect","options","valueExtractor","item","listExtractor","label","displayValueExtractor","onSelectOption","pagesize","totalPage"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"pagination.js","sources":["../../../src/pagination/pagination.tsx"],"sourcesContent":["import { Chevron2LeftIcon } from \"@lifesg/react-icons/chevron-2-left\";\nimport { Chevron2RightIcon } from \"@lifesg/react-icons/chevron-2-right\";\nimport { ChevronLeftIcon } from \"@lifesg/react-icons/chevron-left\";\nimport { ChevronLineLeftIcon } from \"@lifesg/react-icons/chevron-line-left\";\nimport { ChevronLineRightIcon } from \"@lifesg/react-icons/chevron-line-right\";\nimport { ChevronRightIcon } from \"@lifesg/react-icons/chevron-right\";\nimport { EllipsisHorizontalIcon } from \"@lifesg/react-icons/ellipsis-horizontal\";\nimport clsx from \"clsx\";\nimport React, { useEffect, useState } from \"react\";\n\nimport { Input } from \"../input\";\nimport { InputSelect } from \"../input-select\";\nimport { VisuallyHidden } from \"../shared/accessibility\";\nimport { ClickableIcon } from \"../shared/clickable-icon\";\nimport { useMaxWidthMediaQuery } from \"../theme\";\nimport { Typography } from \"../typography\";\nimport { useId, useIsMounted } from \"../util\";\nimport * as styles from \"./pagination.styles\";\nimport type { PageSizeItemProps, PaginationProps } from \"./types\";\n\nconst Component = (\n {\n id,\n \"data-testid\": dataTestId,\n className,\n pageSize = 10,\n totalItems,\n activePage,\n pageSizeOptions = DEFAULT_OPTIONS,\n showFirstAndLastNav,\n showPageSizeChanger = false,\n onPageChange,\n onPageSizeChange,\n }: PaginationProps,\n ref: React.Ref<HTMLDivElement>\n) => {\n // =============================================================================\n // CONST, STATE, REF\n // =============================================================================\n\n const isMounted = useIsMounted();\n const isMobile = useMaxWidthMediaQuery(\"sm\");\n const [hoverRightButton, setHoverRightButton] = useState(false);\n const [hoverLeftButton, setHoverLeftButton] = useState(false);\n const [inputText, setInputText] = useState<string>(\"\");\n\n const [selectedOption, setSelectedOption] = useState<\n PageSizeItemProps | undefined\n >(pageSizeOptions[0]);\n const [pageSizeLocal, setPageSize] = useState<number>(\n !isMobile && showPageSizeChanger\n ? selectedOption\n ? selectedOption.value\n : pageSize\n : pageSize\n );\n\n const boundaryRange = 1;\n const siblingRange = 1;\n\n const internalId = useId();\n const paginationId = `${internalId}-pagination`;\n\n const totalPages = Math.ceil(totalItems / pageSizeLocal);\n const isFirstPage = activePage === 1;\n const isLastPage = activePage === totalPages;\n\n const firstPaginationItem =\n activePage > 1 ? () => handlePaginationItemClick(1) : undefined;\n const lastPaginationItem =\n activePage < totalPages\n ? () => handlePaginationItemClick(totalPages)\n : undefined;\n const prevPaginationItem =\n activePage > 1\n ? () => handlePaginationItemClick(activePage - 1)\n : undefined;\n const nextPaginationItem =\n activePage < totalPages\n ? () =>\n handlePaginationItemClick(parseInt(activePage.toString()) + 1)\n : undefined;\n\n // =============================================================================\n // EFFECTS\n // =============================================================================\n useEffect(() => {\n if (activePage) {\n setInputValue(activePage);\n }\n }, [activePage]);\n\n useEffect(() => {\n setPageSize(pageSize);\n setSelectedOption(\n pageSizeOptions.find((option) => option.value === pageSize)\n );\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [pageSize]);\n\n // =============================================================================\n // HELPER FUNCTIONS\n // =============================================================================\n const setInputValue = (value: number) => {\n setInputText(value.toString());\n };\n\n const closeAllTooltips = () => {\n setHoverRightButton(false);\n setHoverLeftButton(false);\n };\n\n // =============================================================================\n // EVENT HANDLERS\n // =============================================================================\n const handlePaginationItemClick = (pageIndex: number) => {\n if (onPageChange) {\n onPageChange(pageIndex);\n setInputValue(pageIndex);\n }\n };\n\n const handleFastForwardClick = () => {\n const currentIndex = Math.min(totalPages, activePage + 5);\n handlePaginationItemClick(currentIndex);\n setInputValue(currentIndex);\n setHoverRightButton(true);\n setHoverLeftButton(false);\n };\n\n const handleFastBackwardClick = () => {\n const currentIndex = Math.max(1, activePage - 5);\n handlePaginationItemClick(currentIndex);\n setInputValue(currentIndex);\n setHoverRightButton(false);\n setHoverLeftButton(true);\n };\n\n const handleInput = (event: React.ChangeEvent<HTMLInputElement>) => {\n const value = event.target.value;\n const numericRegex = /^[0-9]+$/;\n if (value === undefined || value.length === 0) {\n setInputText(\"\");\n } else if (!numericRegex.test(value)) {\n setInputText(value.replace(/[^0-9]/g, \"\"));\n } else {\n const newPage = parseInt(value.replace(/[^0-9]/g, \"\"));\n setInputValue(Math.max(1, Math.min(totalPages, newPage)));\n }\n };\n\n const handleInputSubmit = (event: React.FormEvent) => {\n event.preventDefault();\n if (inputText) {\n onPageChange?.(parseInt(inputText));\n }\n };\n\n const onHoverRightButton = () => {\n setHoverRightButton(true);\n };\n\n const onBlurRightButton = () => {\n setHoverRightButton(false);\n };\n\n const onHoverLeftButton = () => {\n setHoverLeftButton(true);\n };\n\n const onBlurLeftButton = () => {\n setHoverLeftButton(false);\n };\n\n const handleListItemClick = (item: PageSizeItemProps) => {\n setSelectedOption(item);\n const pagesize = item.value;\n const totalPage = Math.ceil(totalItems / pagesize);\n\n setPageSize(pagesize);\n\n const page = activePage >= totalPage ? totalPage : activePage;\n\n if (onPageSizeChange) {\n onPageSizeChange(page, pagesize);\n }\n };\n\n // =============================================================================\n // RENDER FUNCTIONS\n // =============================================================================\n const renderPaginationItems = () => {\n return [...Array(totalPages)].map((e, i) => {\n const pageIndex = i + 1;\n const startRange = boundaryRange + 2 + siblingRange * 2;\n const endRange = totalPages - startRange;\n const totalRange = (boundaryRange + siblingRange) * 2 + 3;\n const active = activePage === pageIndex;\n if (totalPages <= totalRange) {\n return (\n <button\n key={pageIndex}\n onClick={() => handlePaginationItemClick(pageIndex)}\n className={clsx(\n styles.pageItem,\n active && styles.pageItemSelected\n )}\n aria-label={`page ${pageIndex} of ${totalPages}`}\n aria-current={active ? \"page\" : false}\n onMouseOver={closeAllTooltips}\n onFocus={closeAllTooltips}\n >\n {pageIndex}\n </button>\n );\n }\n\n const ellipsisStart =\n activePage + siblingRange > startRange &&\n pageIndex === boundaryRange + 1;\n if (ellipsisStart) {\n return renderStartEllipsis(pageIndex);\n }\n\n const ellipsisEnd =\n activePage - siblingRange <= endRange &&\n pageIndex === totalPages - boundaryRange - 1;\n if (ellipsisEnd) {\n return renderEndEllipsis(pageIndex);\n }\n\n const paginationStart =\n (pageIndex <= startRange &&\n activePage + siblingRange <= startRange) ||\n pageIndex <= boundaryRange;\n const paginationMiddle =\n pageIndex === activePage ||\n (pageIndex <= activePage + siblingRange &&\n pageIndex >= activePage - siblingRange - 1);\n const paginationEnd =\n (pageIndex > endRange &&\n activePage - siblingRange > endRange) ||\n pageIndex > totalPages - boundaryRange;\n if (paginationStart || paginationMiddle || paginationEnd) {\n return (\n <button\n key={pageIndex}\n onClick={() => handlePaginationItemClick(pageIndex)}\n className={clsx(\n styles.pageItem,\n active && styles.pageItemSelected\n )}\n aria-label={`page ${pageIndex} of ${totalPages}`}\n aria-current={active ? \"page\" : false}\n onMouseOver={closeAllTooltips}\n onFocus={closeAllTooltips}\n >\n {pageIndex}\n </button>\n );\n }\n\n return null;\n });\n };\n\n const renderStartEllipsis = (pageIndex: number) => (\n <div key={pageIndex} className={styles.ellipsisContainer}>\n <ClickableIcon\n className={styles.iconButton}\n focusHighlight={false}\n focusOutline=\"browser\"\n aria-label={\"Previous 5 pages\"}\n onMouseEnter={onHoverLeftButton}\n onMouseLeave={onBlurLeftButton}\n onFocus={onHoverLeftButton}\n onBlur={onBlurLeftButton}\n onClick={handleFastBackwardClick}\n >\n {hoverLeftButton ? (\n <Chevron2LeftIcon aria-hidden />\n ) : (\n <EllipsisHorizontalIcon aria-hidden />\n )}\n </ClickableIcon>\n {hoverLeftButton && (\n <div className={styles.hover}>Previous 5 pages</div>\n )}\n </div>\n );\n\n const renderEndEllipsis = (pageIndex: number) => (\n <div key={pageIndex} className={styles.ellipsisContainer}>\n <ClickableIcon\n className={styles.iconButton}\n focusHighlight={false}\n focusOutline=\"browser\"\n aria-label={\"Next 5 pages\"}\n onMouseEnter={onHoverRightButton}\n onMouseLeave={onBlurRightButton}\n onFocus={onHoverRightButton}\n onBlur={onBlurRightButton}\n onClick={handleFastForwardClick}\n >\n {hoverRightButton ? (\n <Chevron2RightIcon aria-hidden />\n ) : (\n <EllipsisHorizontalIcon aria-hidden />\n )}\n </ClickableIcon>\n {hoverRightButton && (\n <div className={styles.hover}>Next 5 pages</div>\n )}\n </div>\n );\n\n const renderMobile = () => (\n <div className={styles.paginationMobileInput}>\n <form onSubmit={handleInputSubmit}>\n <Input\n className={styles.inputView}\n value={inputText}\n onChange={handleInput}\n autoComplete=\"off\"\n type=\"numeric\"\n id={(id || \"pagination\") + \"-input\"}\n data-testid={(dataTestId || \"pagination\") + \"-input\"}\n aria-label={`Page ${activePage} of ${totalPages}`}\n />\n </form>\n <Typography.BodyBL className={styles.labelDivider}>\n /\n </Typography.BodyBL>\n <Typography.BodyBL className={styles.label}>\n {totalPages}\n </Typography.BodyBL>\n </div>\n );\n\n return (\n <nav\n className={clsx(styles.paginationWrapper, className)}\n ref={ref}\n id={id || \"pagination-wrapper\"}\n data-testid={dataTestId || \"pagination\"}\n aria-labelledby={paginationId}\n >\n <VisuallyHidden id={paginationId} aria-hidden>\n pagination\n </VisuallyHidden>\n <div className={styles.paginationList}>\n <div className={styles.paginationMenu}>\n {showFirstAndLastNav && (\n <ClickableIcon\n className={clsx(\n styles.iconButton,\n styles.iconButtonNavigation\n )}\n onClick={firstPaginationItem}\n disabled={isFirstPage}\n focusHighlight={false}\n aria-label=\"First page\"\n focusOutline=\"browser\"\n >\n <ChevronLineLeftIcon aria-hidden />\n </ClickableIcon>\n )}\n <ClickableIcon\n className={clsx(\n styles.iconButton,\n styles.iconButtonNavigation\n )}\n onClick={prevPaginationItem}\n disabled={isFirstPage}\n focusHighlight={false}\n aria-label=\"Previous page\"\n focusOutline=\"browser\"\n >\n <ChevronLeftIcon aria-hidden />\n </ClickableIcon>\n {isMounted && isMobile\n ? renderMobile()\n : renderPaginationItems()}\n <ClickableIcon\n className={clsx(\n styles.iconButton,\n styles.iconButtonNavigation\n )}\n onClick={nextPaginationItem}\n disabled={isLastPage}\n focusHighlight={false}\n aria-label=\"Next page\"\n focusOutline=\"browser\"\n >\n <ChevronRightIcon aria-hidden />\n </ClickableIcon>\n {showFirstAndLastNav && (\n <ClickableIcon\n className={clsx(\n styles.iconButton,\n styles.iconButtonNavigation\n )}\n onClick={lastPaginationItem}\n disabled={isLastPage}\n focusHighlight={false}\n aria-label=\"Last page\"\n focusOutline=\"browser\"\n >\n <ChevronLineRightIcon aria-hidden />\n </ClickableIcon>\n )}\n </div>\n </div>\n {showPageSizeChanger && !isMobile && (\n <div className={styles.inputSelectWrapper}>\n <VisuallyHidden id={`${paginationId}-page-size`}>\n Items per page\n </VisuallyHidden>\n <InputSelect\n aria-labelledby={`${paginationId}-page-size`}\n options={pageSizeOptions}\n valueExtractor={(item) => item.value}\n listExtractor={(item) => item.label}\n displayValueExtractor={(item) => item.label}\n selectedOption={selectedOption}\n onSelectOption={handleListItemClick}\n />\n </div>\n )}\n </nav>\n );\n};\n// @catalog\n/**\n * A navigation control for moving through paged content.\n *\n * Use `Pagination` to let users navigate between pages of a data set.\n */\nexport const Pagination = React.forwardRef(Component);\n\nconst DEFAULT_OPTIONS: PageSizeItemProps[] = [\n { value: 10, label: \"10 per page\" },\n { value: 20, label: \"20 per page\" },\n { value: 30, label: \"30 per page\" },\n];\n"],"names":["Pagination","React","forwardRef","id","dataTestId","className","pageSize","totalItems","activePage","pageSizeOptions","DEFAULT_OPTIONS","showFirstAndLastNav","showPageSizeChanger","onPageChange","onPageSizeChange","ref","isMounted","useIsMounted","isMobile","useMaxWidthMediaQuery","hoverRightButton","setHoverRightButton","useState","hoverLeftButton","setHoverLeftButton","inputText","setInputText","selectedOption","setSelectedOption","pageSizeLocal","setPageSize","value","paginationId","useId","totalPages","Math","ceil","isFirstPage","isLastPage","firstPaginationItem","handlePaginationItemClick","undefined","lastPaginationItem","prevPaginationItem","nextPaginationItem","parseInt","toString","useEffect","setInputValue","find","option","closeAllTooltips","pageIndex","handleFastForwardClick","currentIndex","min","handleFastBackwardClick","max","handleInput","event","target","length","test","newPage","replace","handleInputSubmit","preventDefault","onHoverRightButton","onBlurRightButton","onHoverLeftButton","onBlurLeftButton","renderStartEllipsis","_jsxs","styles.ellipsisContainer","children","_jsx","ClickableIcon","styles.iconButton","focusHighlight","focusOutline","onMouseEnter","onMouseLeave","onFocus","onBlur","onClick","Chevron2LeftIcon","EllipsisHorizontalIcon","styles.hover","renderEndEllipsis","Chevron2RightIcon","clsx","styles.paginationWrapper","VisuallyHidden","styles.paginationList","styles.paginationMenu","styles.iconButtonNavigation","disabled","ChevronLineLeftIcon","ChevronLeftIcon","styles.paginationMobileInput","onSubmit","Input","styles.inputView","onChange","autoComplete","type","Typography","BodyBL","styles.labelDivider","styles.label","Array","map","e","i","endRange","boundaryRange","active","styles.pageItem","styles.pageItemSelected","onMouseOver","ChevronRightIcon","ChevronLineRightIcon","styles.inputSelectWrapper","InputSelect","options","valueExtractor","item","listExtractor","label","displayValueExtractor","onSelectOption","pagesize","totalPage"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"phone-number-input.js","sources":["../../../src/phone-number-input/phone-number-input.tsx"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\n\nimport { InputGroup } from \"../input-group/input-group\";\nimport type { AddonProps, LabelAddon, ListAddon } from \"../input-group/types\";\nimport { useNextInputState } from \"../util\";\nimport { PhoneNumberInputHelper } from \"./phone-number-input-helper\";\nimport type { CountryValue, PhoneNumberInputProps } from \"./types\";\n\n// @catalog\n/**\n * A phone number field composed of a country code selector and a number input.\n *\n * Use `PhoneNumberInput` when collecting an internationalised phone number.\n * The country code can be selected from a searchable dropdown or locked to a\n * fixed value via `fixedCountry`.\n */\nexport const PhoneNumberInput = ({\n id,\n onChange,\n value,\n allowClear,\n onClear,\n onBlur,\n error,\n fixedCountry = false,\n optionPlaceholder = \"Select\",\n optionSearchPlaceholder,\n enableSearch,\n onHideOptions,\n onShowOptions,\n placeholder,\n autoComplete,\n noBorder = false,\n getAriaLabel,\n ...otherProps\n}: PhoneNumberInputProps) => {\n // =============================================================================\n // CONST, STATE, REF\n // =============================================================================\n const [options] = useState<CountryValue[]>(\n PhoneNumberInputHelper.getCountries()\n );\n\n const [selectedCountry, setSelectedCountry] = useState<\n CountryValue | undefined\n >(undefined);\n const [inputValue, setInputValue] = useState<string>(\"\");\n\n const nodeRef = useRef<HTMLInputElement>(null);\n\n const getNextInputState = useNextInputState({\n ref: nodeRef,\n formatter: (value) =>\n PhoneNumberInputHelper.formatNumber(\n value.replace(/[^0-9]/g, \"\"),\n selectedCountry\n ),\n });\n\n const resolvedAriaLabel =\n getAriaLabel?.(selectedCountry) ?? \"Enter phone number\";\n\n // =============================================================================\n // EFFECTS\n // =============================================================================\n useEffect(() => {\n const selectedOption = options.filter(\n (country) =>\n country.countryCode === normaliseCountryCode(value?.countryCode)\n )[0];\n\n setSelectedCountry(selectedOption);\n setInputValue(\n PhoneNumberInputHelper.formatNumber(value?.number, selectedOption)\n );\n }, [value]);\n\n // =============================================================================\n // EVENT HANDLERS\n // =============================================================================\n const handleClear = () => {\n if (onClear) {\n onClear();\n } else {\n setInputValue(\"\");\n }\n };\n\n const handleInputChange = () => {\n const nextState = getNextInputState();\n if (!nextState) return;\n\n const { nextValue, updateCaretPosition } = nextState;\n\n updateCaretPosition();\n performLocalChangeHandler(nextValue, selectedCountry);\n if (onChange) {\n performOnChangeHandler(nextValue, selectedCountry);\n }\n };\n\n const handleSelectOption = (country: CountryValue) => {\n performLocalChangeHandler(inputValue, country);\n if (onChange) {\n performOnChangeHandler(inputValue, country);\n }\n };\n\n // =============================================================================\n // HELPER FUNCTIONS\n // =============================================================================\n const performOnChangeHandler = (\n inputValue: string,\n selectedCountry: CountryValue | undefined\n ) => {\n const formatedInputValue = PhoneNumberInputHelper.formatNumber(\n inputValue,\n selectedCountry\n );\n onChange?.({\n number: formatedInputValue.replace(/[\\s()]+/g, \"\"), // strip formatted spaces\n countryCode:\n selectedCountry && addPlusPrefix(selectedCountry.countryCode),\n });\n };\n\n const performLocalChangeHandler = (\n inputValue: string,\n selectedCountry: CountryValue | undefined\n ) => {\n setInputValue(\n PhoneNumberInputHelper.formatNumber(inputValue, selectedCountry)\n );\n\n setSelectedCountry(selectedCountry);\n };\n\n const getAddonProps = (): AddonProps<CountryValue, string> => {\n if (fixedCountry) {\n return {\n type: \"label\",\n attributes: {\n value: addPlusPrefix(selectedCountry?.countryCode),\n } as LabelAddon,\n };\n } else {\n return {\n type: \"list\",\n attributes: {\n \"aria-label\": \"Country code\",\n placeholder: optionPlaceholder,\n options,\n selectedOption: selectedCountry,\n enableSearch: enableSearch,\n searchPlaceholder: optionSearchPlaceholder,\n valueExtractor: (option) => `+${option.countryCode}`,\n listExtractor: (option) => ({\n title: option.name,\n secondaryLabel: addPlusPrefix(option.countryCode),\n }),\n onSelectOption: handleSelectOption,\n onHideOptions: onHideOptions,\n onShowOptions: onShowOptions,\n } as ListAddon<CountryValue, string>,\n };\n }\n };\n\n // =========================================================================\n // RENDER FUNCTIONS\n // =========================================================================\n return (\n <InputGroup\n id={id}\n ref={nodeRef}\n value={inputValue}\n onChange={handleInputChange}\n allowClear={allowClear && !!inputValue}\n onClear={handleClear}\n onBlur={onBlur}\n error={error}\n placeholder={placeholder}\n addon={getAddonProps()}\n inputMode=\"numeric\"\n autoComplete={autoComplete}\n noBorder={noBorder}\n aria-label={resolvedAriaLabel}\n {...otherProps}\n />\n );\n};\n\n// =============================================================================\n// HELPER FUNCTIONS\n// =============================================================================\n/**\n * This strips the + off the specified country code if it\n * is present.\n */\nconst normaliseCountryCode = (countryCode: string | undefined): string => {\n return countryCode ? countryCode.replace(\"+\", \"\") : \"\";\n};\n\nconst addPlusPrefix = (countryCode?: string): string => {\n if (!countryCode) return \"\";\n return countryCode.includes(\"+\") ? countryCode : `+${countryCode}`;\n};\n"],"names":["normaliseCountryCode","countryCode","replace","addPlusPrefix","includes","_a","id","onChange","value","allowClear","onClear","onBlur","error","fixedCountry","optionPlaceholder","optionSearchPlaceholder","enableSearch","onHideOptions","onShowOptions","placeholder","autoComplete","noBorder","getAriaLabel","otherProps","__rest","options","useState","PhoneNumberInputHelper","getCountries","selectedCountry","setSelectedCountry","undefined","inputValue","setInputValue","nodeRef","useRef","getNextInputState","useNextInputState","ref","formatter","formatNumber","resolvedAriaLabel","_b","useEffect","selectedOption","filter","country","number","handleSelectOption","performLocalChangeHandler","performOnChangeHandler","formatedInputValue","_jsx","InputGroup","Object","assign","nextState","nextValue","updateCaretPosition","addon","type","attributes","searchPlaceholder","valueExtractor","option","listExtractor","title","name","secondaryLabel","onSelectOption","inputMode"],"mappings":"kbAgBO,MAuLDA,EAAwBC,GACnBA,EAAcA,EAAYC,QAAQ,IAAK,IAAM,GAGlDC,EAAiBF,GACdA,EACEA,EAAYG,SAAS,KAAOH,EAAc,IAAIA,IAD5B,4BA5LII,WAAAC,GAC7BA,EAAEC,SACFA,EAAQC,MACRA,EAAKC,WACLA,EAAUC,QACVA,EAAOC,OACPA,EAAMC,MACNA,EAAKC,aACLA,GAAe,EAAKC,kBACpBA,EAAoB,SAAQC,wBAC5BA,EAAuBC,aACvBA,EAAYC,cACZA,EAAaC,cACbA,EAAaC,YACbA,EAAWC,aACXA,EAAYC,SACZA,GAAW,EAAKC,aAChBA,GAAYjB,EACTkB,EAAUC,EAAAA,OAAAnB,EAlBgB,sOAuB7B,MAAOoB,GAAWC,EAAAA,SACdC,EAAAA,uBAAuBC,iBAGpBC,EAAiBC,GAAsBJ,EAAAA,cAE5CK,IACKC,EAAYC,GAAiBP,EAAAA,SAAiB,IAE/CQ,EAAUC,EAAAA,OAAyB,MAEnCC,EAAoBC,EAAAA,kBAAkB,CACxCC,IAAKJ,EACLK,UAAY/B,GACRmB,EAAAA,uBAAuBa,aACnBhC,EAAMN,QAAQ,UAAW,IACzB2B,KAINY,EAC6B,QAA/BC,EAAApB,aAAY,EAAZA,EAAeO,UAAgB,IAAAa,EAAAA,EAAI,qBAKvCC,EAAAA,WAAU,KACN,MAAMC,EAAiBnB,EAAQoB,QAC1BC,GACGA,EAAQ7C,cAAgBD,EAAqBQ,aAAK,EAALA,EAAOP,eAC1D,GAEF6B,EAAmBc,GACnBX,EACIN,EAAAA,uBAAuBa,aAAahC,aAAK,EAALA,EAAOuC,OAAQH,GACtD,GACF,CAACpC,IAKJ,MAqBMwC,EAAsBF,IACxBG,EAA0BjB,EAAYc,GAClCvC,GACA2C,EAAuBlB,EAAYc,EACvC,EAMEI,EAAyB,CAC3BlB,EACAH,KAEA,MAAMsB,EAAqBxB,EAAAA,uBAAuBa,aAC9CR,EACAH,GAEJtB,SAAAA,EAAW,CACPwC,OAAQI,EAAmBjD,QAAQ,WAAY,IAC/CD,YACI4B,GAAmB1B,EAAc0B,EAAgB5B,cACvD,EAGAgD,EAA4B,CAC9BjB,EACAH,KAEAI,EACIN,EAAAA,uBAAuBa,aAAaR,EAAYH,IAGpDC,EAAmBD,EAAgB,EAqCvC,OACIuB,EAAAA,IAACC,EAAAA,WAAUC,OAAAC,OAAA,CACPjD,GAAIA,EACJgC,IAAKJ,EACL1B,MAAOwB,EACPzB,SAxFkB,KACtB,MAAMiD,EAAYpB,IAClB,IAAKoB,EAAW,OAEhB,MAAMC,UAAEA,EAASC,oBAAEA,GAAwBF,EAE3CE,IACAT,EAA0BQ,EAAW5B,GACjCtB,GACA2C,EAAuBO,EAAW5B,EACtC,EA+EIpB,WAAYA,KAAgBuB,EAC5BtB,QAlGY,KACZA,EACAA,IAEAuB,EAAc,GAClB,EA8FItB,OAAQA,EACRC,MAAOA,EACPO,YAAaA,EACbwC,MA5CA9C,EACO,CACH+C,KAAM,QACNC,WAAY,CACRrD,MAAOL,EAAc0B,aAAe,EAAfA,EAAiB5B,eAIvC,CACH2D,KAAM,OACNC,WAAY,CACR,aAAc,eACd1C,YAAaL,EACbW,UACAmB,eAAgBf,EAChBb,aAAcA,EACd8C,kBAAmB/C,EACnBgD,eAAiBC,GAAW,IAAIA,EAAO/D,cACvCgE,cAAgBD,IAAM,CAClBE,MAAOF,EAAOG,KACdC,eAAgBjE,EAAc6D,EAAO/D,eAEzCoE,eAAgBrB,EAChB/B,cAAeA,EACfC,cAAeA,IAqBvBoD,UAAU,UACVlD,aAAcA,EACdC,SAAUA,EAAQ,aACNoB,GACRlB,GACN"}
|
|
1
|
+
{"version":3,"file":"phone-number-input.js","sources":["../../../src/phone-number-input/phone-number-input.tsx"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\n\nimport { InputGroup } from \"../input-group/input-group\";\nimport type { AddonProps, LabelAddon, ListAddon } from \"../input-group/types\";\nimport { useNextInputState } from \"../util\";\nimport { PhoneNumberInputHelper } from \"./phone-number-input-helper\";\nimport type { CountryValue, PhoneNumberInputProps } from \"./types\";\n\n// @catalog\n/**\n * A phone number field composed of a country code selector and a number input.\n *\n * Use `PhoneNumberInput` when collecting an internationalised phone number.\n * The country code can be selected from a searchable dropdown or locked to a\n * fixed value via `fixedCountry`.\n */\nexport const PhoneNumberInput = ({\n id,\n onChange,\n value,\n allowClear,\n onClear,\n onBlur,\n error,\n fixedCountry = false,\n optionPlaceholder = \"Select\",\n optionSearchPlaceholder,\n enableSearch,\n onHideOptions,\n onShowOptions,\n placeholder,\n autoComplete,\n noBorder = false,\n getAriaLabel,\n ...otherProps\n}: PhoneNumberInputProps) => {\n // =============================================================================\n // CONST, STATE, REF\n // =============================================================================\n const [options] = useState<CountryValue[]>(\n PhoneNumberInputHelper.getCountries()\n );\n\n const [selectedCountry, setSelectedCountry] = useState<\n CountryValue | undefined\n >(undefined);\n const [inputValue, setInputValue] = useState<string>(\"\");\n\n const nodeRef = useRef<HTMLInputElement>(null);\n\n const getNextInputState = useNextInputState({\n ref: nodeRef,\n formatter: (value) =>\n PhoneNumberInputHelper.formatNumber(\n value.replace(/[^0-9]/g, \"\"),\n selectedCountry\n ),\n });\n\n const resolvedAriaLabel =\n getAriaLabel?.(selectedCountry) ?? \"Enter phone number\";\n\n // =============================================================================\n // EFFECTS\n // =============================================================================\n useEffect(() => {\n const selectedOption = options.filter(\n (country) =>\n country.countryCode === normaliseCountryCode(value?.countryCode)\n )[0];\n\n setSelectedCountry(selectedOption);\n setInputValue(\n PhoneNumberInputHelper.formatNumber(value?.number, selectedOption)\n );\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [value]);\n\n // =============================================================================\n // EVENT HANDLERS\n // =============================================================================\n const handleClear = () => {\n if (onClear) {\n onClear();\n } else {\n setInputValue(\"\");\n }\n };\n\n const handleInputChange = () => {\n const nextState = getNextInputState();\n if (!nextState) return;\n\n const { nextValue, updateCaretPosition } = nextState;\n\n updateCaretPosition();\n performLocalChangeHandler(nextValue, selectedCountry);\n if (onChange) {\n performOnChangeHandler(nextValue, selectedCountry);\n }\n };\n\n const handleSelectOption = (country: CountryValue) => {\n performLocalChangeHandler(inputValue, country);\n if (onChange) {\n performOnChangeHandler(inputValue, country);\n }\n };\n\n // =============================================================================\n // HELPER FUNCTIONS\n // =============================================================================\n const performOnChangeHandler = (\n inputValue: string,\n selectedCountry: CountryValue | undefined\n ) => {\n const formatedInputValue = PhoneNumberInputHelper.formatNumber(\n inputValue,\n selectedCountry\n );\n onChange?.({\n number: formatedInputValue.replace(/[\\s()]+/g, \"\"), // strip formatted spaces\n countryCode:\n selectedCountry && addPlusPrefix(selectedCountry.countryCode),\n });\n };\n\n const performLocalChangeHandler = (\n inputValue: string,\n selectedCountry: CountryValue | undefined\n ) => {\n setInputValue(\n PhoneNumberInputHelper.formatNumber(inputValue, selectedCountry)\n );\n\n setSelectedCountry(selectedCountry);\n };\n\n const getAddonProps = (): AddonProps<CountryValue, string> => {\n if (fixedCountry) {\n return {\n type: \"label\",\n attributes: {\n value: addPlusPrefix(selectedCountry?.countryCode),\n } as LabelAddon,\n };\n } else {\n return {\n type: \"list\",\n attributes: {\n \"aria-label\": \"Country code\",\n placeholder: optionPlaceholder,\n options,\n selectedOption: selectedCountry,\n enableSearch: enableSearch,\n searchPlaceholder: optionSearchPlaceholder,\n valueExtractor: (option) => `+${option.countryCode}`,\n listExtractor: (option) => ({\n title: option.name,\n secondaryLabel: addPlusPrefix(option.countryCode),\n }),\n onSelectOption: handleSelectOption,\n onHideOptions: onHideOptions,\n onShowOptions: onShowOptions,\n } as ListAddon<CountryValue, string>,\n };\n }\n };\n\n // =========================================================================\n // RENDER FUNCTIONS\n // =========================================================================\n return (\n <InputGroup\n id={id}\n ref={nodeRef}\n value={inputValue}\n onChange={handleInputChange}\n allowClear={allowClear && !!inputValue}\n onClear={handleClear}\n onBlur={onBlur}\n error={error}\n placeholder={placeholder}\n addon={getAddonProps()}\n inputMode=\"numeric\"\n autoComplete={autoComplete}\n noBorder={noBorder}\n aria-label={resolvedAriaLabel}\n {...otherProps}\n />\n );\n};\n\n// =============================================================================\n// HELPER FUNCTIONS\n// =============================================================================\n/**\n * This strips the + off the specified country code if it\n * is present.\n */\nconst normaliseCountryCode = (countryCode: string | undefined): string => {\n return countryCode ? countryCode.replace(\"+\", \"\") : \"\";\n};\n\nconst addPlusPrefix = (countryCode?: string): string => {\n if (!countryCode) return \"\";\n return countryCode.includes(\"+\") ? countryCode : `+${countryCode}`;\n};\n"],"names":["normaliseCountryCode","countryCode","replace","addPlusPrefix","includes","_a","id","onChange","value","allowClear","onClear","onBlur","error","fixedCountry","optionPlaceholder","optionSearchPlaceholder","enableSearch","onHideOptions","onShowOptions","placeholder","autoComplete","noBorder","getAriaLabel","otherProps","__rest","options","useState","PhoneNumberInputHelper","getCountries","selectedCountry","setSelectedCountry","undefined","inputValue","setInputValue","nodeRef","useRef","getNextInputState","useNextInputState","ref","formatter","formatNumber","resolvedAriaLabel","_b","useEffect","selectedOption","filter","country","number","handleSelectOption","performLocalChangeHandler","performOnChangeHandler","formatedInputValue","_jsx","InputGroup","Object","assign","nextState","nextValue","updateCaretPosition","addon","type","attributes","searchPlaceholder","valueExtractor","option","listExtractor","title","name","secondaryLabel","onSelectOption","inputMode"],"mappings":"kbAgBO,MAwLDA,EAAwBC,GACnBA,EAAcA,EAAYC,QAAQ,IAAK,IAAM,GAGlDC,EAAiBF,GACdA,EACEA,EAAYG,SAAS,KAAOH,EAAc,IAAIA,IAD5B,4BA7LII,WAAAC,GAC7BA,EAAEC,SACFA,EAAQC,MACRA,EAAKC,WACLA,EAAUC,QACVA,EAAOC,OACPA,EAAMC,MACNA,EAAKC,aACLA,GAAe,EAAKC,kBACpBA,EAAoB,SAAQC,wBAC5BA,EAAuBC,aACvBA,EAAYC,cACZA,EAAaC,cACbA,EAAaC,YACbA,EAAWC,aACXA,EAAYC,SACZA,GAAW,EAAKC,aAChBA,GAAYjB,EACTkB,EAAUC,EAAAA,OAAAnB,EAlBgB,sOAuB7B,MAAOoB,GAAWC,EAAAA,SACdC,EAAAA,uBAAuBC,iBAGpBC,EAAiBC,GAAsBJ,EAAAA,cAE5CK,IACKC,EAAYC,GAAiBP,EAAAA,SAAiB,IAE/CQ,EAAUC,EAAAA,OAAyB,MAEnCC,EAAoBC,EAAAA,kBAAkB,CACxCC,IAAKJ,EACLK,UAAY/B,GACRmB,EAAAA,uBAAuBa,aACnBhC,EAAMN,QAAQ,UAAW,IACzB2B,KAINY,EAC6B,QAA/BC,EAAApB,aAAY,EAAZA,EAAeO,UAAgB,IAAAa,EAAAA,EAAI,qBAKvCC,EAAAA,WAAU,KACN,MAAMC,EAAiBnB,EAAQoB,QAC1BC,GACGA,EAAQ7C,cAAgBD,EAAqBQ,aAAK,EAALA,EAAOP,eAC1D,GAEF6B,EAAmBc,GACnBX,EACIN,EAAAA,uBAAuBa,aAAahC,aAAK,EAALA,EAAOuC,OAAQH,GACtD,GAEF,CAACpC,IAKJ,MAqBMwC,EAAsBF,IACxBG,EAA0BjB,EAAYc,GAClCvC,GACA2C,EAAuBlB,EAAYc,EACvC,EAMEI,EAAyB,CAC3BlB,EACAH,KAEA,MAAMsB,EAAqBxB,EAAAA,uBAAuBa,aAC9CR,EACAH,GAEJtB,SAAAA,EAAW,CACPwC,OAAQI,EAAmBjD,QAAQ,WAAY,IAC/CD,YACI4B,GAAmB1B,EAAc0B,EAAgB5B,cACvD,EAGAgD,EAA4B,CAC9BjB,EACAH,KAEAI,EACIN,EAAAA,uBAAuBa,aAAaR,EAAYH,IAGpDC,EAAmBD,EAAgB,EAqCvC,OACIuB,EAAAA,IAACC,EAAAA,WAAUC,OAAAC,OAAA,CACPjD,GAAIA,EACJgC,IAAKJ,EACL1B,MAAOwB,EACPzB,SAxFkB,KACtB,MAAMiD,EAAYpB,IAClB,IAAKoB,EAAW,OAEhB,MAAMC,UAAEA,EAASC,oBAAEA,GAAwBF,EAE3CE,IACAT,EAA0BQ,EAAW5B,GACjCtB,GACA2C,EAAuBO,EAAW5B,EACtC,EA+EIpB,WAAYA,KAAgBuB,EAC5BtB,QAlGY,KACZA,EACAA,IAEAuB,EAAc,GAClB,EA8FItB,OAAQA,EACRC,MAAOA,EACPO,YAAaA,EACbwC,MA5CA9C,EACO,CACH+C,KAAM,QACNC,WAAY,CACRrD,MAAOL,EAAc0B,aAAe,EAAfA,EAAiB5B,eAIvC,CACH2D,KAAM,OACNC,WAAY,CACR,aAAc,eACd1C,YAAaL,EACbW,UACAmB,eAAgBf,EAChBb,aAAcA,EACd8C,kBAAmB/C,EACnBgD,eAAiBC,GAAW,IAAIA,EAAO/D,cACvCgE,cAAgBD,IAAM,CAClBE,MAAOF,EAAOG,KACdC,eAAgBjE,EAAc6D,EAAO/D,eAEzCoE,eAAgBrB,EAChB/B,cAAeA,EACfC,cAAeA,IAqBvBoD,UAAU,UACVlD,aAAcA,EACdC,SAAUA,EAAQ,aACNoB,GACRlB,GACN"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"predictive-text-input.js","sources":["../../../src/predictive-text-input/predictive-text-input.tsx"],"sourcesContent":["import debounce from \"lodash/debounce\";\nimport type React from \"react\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport { Input } from \"../input\";\nimport { concatIds, VisuallyHidden } from \"../shared/accessibility\";\nimport { DropdownList, DropdownListState } from \"../shared/dropdown-list\";\nimport type { ItemsLoadStateType } from \"../shared/dropdown-list/types\";\nimport { ElementWithDropdown } from \"../shared/dropdown-wrapper\";\nimport { InputWrapper } from \"../shared/input-wrapper\";\nimport { useId } from \"../util\";\nimport type { PredictiveTextInputProps } from \"./types\";\n\n// @catalog\n/**\n * A text input that fetches and displays matching suggestions as the user types.\n *\n * Use `PredictiveTextInput` for search-as-you-type or autocomplete scenarios\n * where options are loaded asynchronously based on the current input.\n */\nexport const PredictiveTextInput = <T, V>({\n className,\n \"data-testid\": testId,\n selectedOption,\n minimumCharacters = 3,\n fetchOptions,\n placeholder = \"Enter here...\",\n readOnly = false,\n disabled = false,\n error,\n valueExtractor,\n listExtractor,\n displayValueExtractor,\n onSelectOption,\n alignment,\n dropdownZIndex,\n dropdownRootNode,\n dropdownWidth,\n \"aria-describedby\": ariaDescribedBy,\n ...otherProps\n}: PredictiveTextInputProps<T, V>): JSX.Element => {\n const getDisplayValue = (item: T | undefined): string => {\n if (!item) return \"\";\n return displayValueExtractor\n ? displayValueExtractor(item)\n : item.toString();\n };\n\n // =============================================================================\n // CONST, STATE\n // =============================================================================\n const [input, setInput] = useState<string>(() =>\n selectedOption ? getDisplayValue(selectedOption) : \"\"\n );\n const [searchedInput, setSearchedInput] = useState<string>(() =>\n selectedOption ? getDisplayValue(selectedOption) : \"\"\n );\n const [options, setOptions] = useState<T[]>([]);\n const [isLoading, setIsLoading] = useState<boolean>(true);\n const [isError, setIsError] = useState<boolean>(false);\n const [isOptionSelected, setIsOptionSelected] = useState<boolean>(\n !!selectedOption\n );\n const [prevOptionSelected, setPrevOptionSelected] = useState<T | undefined>(\n selectedOption\n );\n const [isOpen, setIsOpen] = useState(false);\n const [isFocused, setIsFocused] = useState(false);\n\n const internalId = useId();\n const [resultAnnouncement, setResultAnnouncement] = useState<string | null>(\n null\n );\n const instructionId = `${internalId}-instruction`;\n\n const nodeRef = useRef<HTMLDivElement | null>(null);\n const selectorRef = useRef<HTMLInputElement | null>(null);\n\n const fetchOptionsRef = useRef(fetchOptions);\n\n // =============================================================================\n // DEBOUNCE FUNCTIONS\n // =============================================================================\n const handleFetchOptions = useCallback(async (input: string) => {\n if (!fetchOptionsRef.current) return;\n setIsError(false);\n setIsLoading(true);\n try {\n const fetchedOptions = await fetchOptionsRef.current(input);\n setSearchedInput(input);\n setOptions(fetchedOptions ?? []);\n setIsLoading(false);\n } catch {\n setIsError(true);\n setIsLoading(false);\n }\n }, []);\n\n const fetchOptionsDebounced = useCallback(\n debounce((input: string) => {\n handleFetchOptions(input);\n }, 500),\n [handleFetchOptions]\n );\n\n // =============================================================================\n // EFFECTS\n // =============================================================================\n\n useEffect(() => {\n fetchOptionsRef.current = fetchOptions;\n }, [fetchOptions]);\n\n useEffect(() => {\n /***\n * Should not fetch options for the following the scenario :\n * 1. When input < minimumCharacters\n * 2. When user selected option from dropdown becomes input\n * 3. Initial selectedOption passed in as input\n */\n if (input && input.length >= minimumCharacters) {\n if (!isOptionSelected) {\n setIsOpen(true);\n fetchOptionsDebounced(input);\n } else if (input !== searchedInput) {\n fetchOptionsDebounced(input);\n }\n } else {\n fetchOptionsDebounced.cancel();\n }\n\n if (input === \"\" && prevOptionSelected) {\n onSelectOption?.(undefined, undefined);\n handleDropdownDismiss();\n setPrevOptionSelected(undefined);\n }\n\n if (selectedOption && input !== getDisplayValue(selectedOption)) {\n setIsOptionSelected(false);\n }\n }, [input, selectedOption]);\n\n useEffect(() => {\n setInput(selectedOption ? getDisplayValue(selectedOption) : \"\");\n setSearchedInput(selectedOption ? getDisplayValue(selectedOption) : \"\");\n setPrevOptionSelected(selectedOption);\n setIsOptionSelected(!!selectedOption);\n }, [selectedOption]);\n\n useEffect(() => {\n const state = getItemsLoadState();\n const count = options?.length ?? 0;\n\n if (state === \"loading\" && input.length >= minimumCharacters) {\n setResultAnnouncement(\"Loading suggested results\");\n return;\n }\n\n if (state === \"fail\") {\n setResultAnnouncement(\"Suggestions failed to load. Try again.\");\n return;\n }\n\n if (isOpen && !isLoading && !isError) {\n if (count === 0) {\n setResultAnnouncement(input ? \"No results found.\" : null);\n } else {\n setResultAnnouncement(\n `${count} result${\n count > 1 ? \"s\" : \"\"\n } found. Press down arrow to scroll through the list.`\n );\n }\n }\n }, [options, input, isError, isLoading]);\n\n // =============================================================================\n // Cleanup: cancel debounce on unmount\n // =============================================================================\n useEffect(() => {\n return () => {\n fetchOptionsDebounced.cancel();\n };\n }, [fetchOptionsDebounced]);\n\n // =============================================================================\n // EVENT HANDLERS\n // =============================================================================\n const handleListItemClick = (item: T, extractedValue: V) => {\n selectorRef.current?.focus();\n setInput(getDisplayValue(item));\n setSearchedInput(item ? getDisplayValue(item) : \"\");\n setIsOptionSelected(true);\n setPrevOptionSelected(item);\n setIsOpen(false);\n onSelectOption?.(item, extractedValue);\n };\n\n const handleOpen = () => {\n if (!isOptionSelected && input.length >= minimumCharacters) {\n setIsFocused(true);\n }\n };\n\n const handleClose = () => {\n setIsOpen(false);\n setIsFocused(false);\n handleOnBlur();\n };\n\n const handleNodeFocus = () => {\n setIsFocused(true);\n };\n\n const handleNodeBlur = (e: React.FocusEvent) => {\n if (\n !isOpen &&\n nodeRef.current &&\n !nodeRef.current.contains(e.relatedTarget as Node)\n ) {\n setIsFocused(false);\n handleOnBlur();\n }\n };\n\n const handleListDismiss = () => {\n setIsOpen(false);\n setIsFocused(false);\n };\n\n const handleDismiss = () => {\n setIsOpen(false);\n setIsFocused(false);\n handleOnBlur();\n selectorRef.current?.focus();\n };\n\n const handleHide = () => {\n // reset dropdown state once it fully closes\n setOptions([]);\n setIsLoading(true);\n setIsError(false);\n };\n\n const handleOnClear = () => {\n setInput(\"\");\n setIsOptionSelected(false);\n setIsOpen(false);\n onSelectOption?.(undefined, undefined);\n };\n\n const handleOnBlur = () => {\n if (!isOptionSelected) {\n if (prevOptionSelected) {\n const prevValue = getDisplayValue(prevOptionSelected);\n setInput(prevValue);\n setIsOpen(false);\n } else {\n handleOnClear();\n }\n }\n };\n\n const handleTyping = (e: React.ChangeEvent<HTMLInputElement>) => {\n setInput(e.target.value);\n setIsOptionSelected(false);\n };\n\n const handleDropdownDismiss = (item?: T) => {\n setSearchedInput(item ? getDisplayValue(item) : \"\");\n setIsOptionSelected(!!item);\n setIsLoading(true);\n };\n\n // =============================================================================\n // HELPER FUNCTION\n // =============================================================================\n const getItemsLoadState = (): ItemsLoadStateType => {\n if (isError) return \"fail\";\n return isLoading ? \"loading\" : \"success\";\n };\n\n // =============================================================================\n // RENDER FUNCTION\n // =============================================================================\n const renderInputElement = () => {\n return (\n <InputWrapper\n className={className}\n data-testid={testId}\n ref={nodeRef}\n tabIndex={-1}\n onFocus={handleNodeFocus}\n onBlur={handleNodeBlur}\n focused={isFocused}\n disabled={disabled}\n readOnly={readOnly}\n error={error}\n >\n <VisuallyHidden id={instructionId} aria-hidden>\n Type in {minimumCharacters} or more characters for suggested\n results.\n </VisuallyHidden>\n {resultAnnouncement && (\n <VisuallyHidden aria-live=\"polite\">\n {resultAnnouncement}\n </VisuallyHidden>\n )}\n <Input\n role=\"combobox\"\n ref={selectorRef}\n id={internalId}\n type=\"text\"\n value={input}\n onChange={handleTyping}\n placeholder={placeholder}\n readOnly={readOnly}\n disabled={disabled}\n allowClear\n onClear={handleOnClear}\n aria-expanded={isOpen}\n aria-controls={internalId}\n aria-autocomplete=\"list\"\n aria-haspopup=\"listbox\"\n onBlur={\n input.length < minimumCharacters\n ? handleOnBlur\n : undefined\n }\n styleType=\"no-border\"\n aria-describedby={concatIds(ariaDescribedBy, instructionId)}\n {...otherProps}\n />\n </InputWrapper>\n );\n };\n\n const renderDropdown = () => {\n return (\n <DropdownList\n listboxId={internalId}\n listItems={options}\n onSelectItem={handleListItemClick}\n onDismiss={handleListDismiss}\n valueExtractor={valueExtractor}\n listExtractor={listExtractor}\n itemsLoadState={getItemsLoadState()}\n itemTruncationType={\"end\"}\n itemMaxLines={1}\n labelDisplayType={\"next-line\"}\n disableItemFocus\n onRetry={() => handleFetchOptions(input)}\n width={dropdownWidth}\n matchElementWidth\n />\n );\n };\n\n return (\n <DropdownListState>\n <ElementWithDropdown\n enabled={!readOnly && !disabled}\n isOpen={isOpen}\n renderElement={renderInputElement}\n renderDropdown={renderDropdown}\n onOpen={handleOpen}\n onClose={handleClose}\n onDismiss={handleDismiss}\n onHide={handleHide}\n clickToToggle={false}\n offset={8}\n alignment={alignment}\n fitAvailableHeight\n customZIndex={dropdownZIndex}\n rootNode={dropdownRootNode}\n />\n </DropdownListState>\n );\n};\n"],"names":["_a","className","testId","selectedOption","minimumCharacters","fetchOptions","placeholder","readOnly","disabled","error","valueExtractor","listExtractor","displayValueExtractor","onSelectOption","alignment","dropdownZIndex","dropdownRootNode","dropdownWidth","ariaDescribedBy","otherProps","__rest","getDisplayValue","item","toString","input","setInput","useState","searchedInput","setSearchedInput","options","setOptions","isLoading","setIsLoading","isError","setIsError","isOptionSelected","setIsOptionSelected","prevOptionSelected","setPrevOptionSelected","isOpen","setIsOpen","isFocused","setIsFocused","internalId","useId","resultAnnouncement","setResultAnnouncement","instructionId","nodeRef","useRef","selectorRef","fetchOptionsRef","handleFetchOptions","useCallback","__awaiter","current","fetchedOptions","fetchOptionsDebounced","debounce","useEffect","length","cancel","undefined","handleDropdownDismiss","state","getItemsLoadState","count","handleListItemClick","extractedValue","focus","handleNodeFocus","handleNodeBlur","e","contains","relatedTarget","handleOnBlur","handleListDismiss","handleOnClear","prevValue","handleTyping","target","value","_jsx","DropdownListState","children","ElementWithDropdown","enabled","renderElement","_jsxs","InputWrapper","ref","tabIndex","onFocus","onBlur","focused","VisuallyHidden","id","Input","Object","assign","role","type","onChange","allowClear","onClear","styleType","concatIds","renderDropdown","DropdownList","listboxId","listItems","onSelectItem","onDismiss","itemsLoadState","itemTruncationType","itemMaxLines","labelDisplayType","disableItemFocus","onRetry","width","matchElementWidth","onOpen","onClose","onHide","clickToToggle","offset","fitAvailableHeight","customZIndex","rootNode"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"predictive-text-input.js","sources":["../../../src/predictive-text-input/predictive-text-input.tsx"],"sourcesContent":["import debounce from \"lodash/debounce\";\nimport type React from \"react\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport { Input } from \"../input\";\nimport { concatIds, VisuallyHidden } from \"../shared/accessibility\";\nimport { DropdownList, DropdownListState } from \"../shared/dropdown-list\";\nimport type { ItemsLoadStateType } from \"../shared/dropdown-list/types\";\nimport { ElementWithDropdown } from \"../shared/dropdown-wrapper\";\nimport { InputWrapper } from \"../shared/input-wrapper\";\nimport { useId } from \"../util\";\nimport type { PredictiveTextInputProps } from \"./types\";\n\n// @catalog\n/**\n * A text input that fetches and displays matching suggestions as the user types.\n *\n * Use `PredictiveTextInput` for search-as-you-type or autocomplete scenarios\n * where options are loaded asynchronously based on the current input.\n */\nexport const PredictiveTextInput = <T, V>({\n className,\n \"data-testid\": testId,\n selectedOption,\n minimumCharacters = 3,\n fetchOptions,\n placeholder = \"Enter here...\",\n readOnly = false,\n disabled = false,\n error,\n valueExtractor,\n listExtractor,\n displayValueExtractor,\n onSelectOption,\n alignment,\n dropdownZIndex,\n dropdownRootNode,\n dropdownWidth,\n \"aria-describedby\": ariaDescribedBy,\n ...otherProps\n}: PredictiveTextInputProps<T, V>): JSX.Element => {\n const getDisplayValue = (item: T | undefined): string => {\n if (!item) return \"\";\n return displayValueExtractor\n ? displayValueExtractor(item)\n : item.toString();\n };\n\n // =============================================================================\n // CONST, STATE\n // =============================================================================\n const [input, setInput] = useState<string>(() =>\n selectedOption ? getDisplayValue(selectedOption) : \"\"\n );\n const [searchedInput, setSearchedInput] = useState<string>(() =>\n selectedOption ? getDisplayValue(selectedOption) : \"\"\n );\n const [options, setOptions] = useState<T[]>([]);\n const [isLoading, setIsLoading] = useState<boolean>(true);\n const [isError, setIsError] = useState<boolean>(false);\n const [isOptionSelected, setIsOptionSelected] = useState<boolean>(\n !!selectedOption\n );\n const [prevOptionSelected, setPrevOptionSelected] = useState<T | undefined>(\n selectedOption\n );\n const [isOpen, setIsOpen] = useState(false);\n const [isFocused, setIsFocused] = useState(false);\n\n const internalId = useId();\n const [resultAnnouncement, setResultAnnouncement] = useState<string | null>(\n null\n );\n const instructionId = `${internalId}-instruction`;\n\n const nodeRef = useRef<HTMLDivElement | null>(null);\n const selectorRef = useRef<HTMLInputElement | null>(null);\n\n const fetchOptionsRef = useRef(fetchOptions);\n\n // =============================================================================\n // DEBOUNCE FUNCTIONS\n // =============================================================================\n const handleFetchOptions = useCallback(async (input: string) => {\n if (!fetchOptionsRef.current) return;\n setIsError(false);\n setIsLoading(true);\n try {\n const fetchedOptions = await fetchOptionsRef.current(input);\n setSearchedInput(input);\n setOptions(fetchedOptions ?? []);\n setIsLoading(false);\n } catch {\n setIsError(true);\n setIsLoading(false);\n }\n }, []);\n\n // eslint-disable-next-line react-hooks/exhaustive-deps\n const fetchOptionsDebounced = useCallback(\n debounce((input: string) => {\n handleFetchOptions(input);\n }, 500),\n [handleFetchOptions]\n );\n\n // =============================================================================\n // EFFECTS\n // =============================================================================\n\n useEffect(() => {\n fetchOptionsRef.current = fetchOptions;\n }, [fetchOptions]);\n\n useEffect(() => {\n /***\n * Should not fetch options for the following the scenario :\n * 1. When input < minimumCharacters\n * 2. When user selected option from dropdown becomes input\n * 3. Initial selectedOption passed in as input\n */\n if (input && input.length >= minimumCharacters) {\n if (!isOptionSelected) {\n setIsOpen(true);\n fetchOptionsDebounced(input);\n } else if (input !== searchedInput) {\n fetchOptionsDebounced(input);\n }\n } else {\n fetchOptionsDebounced.cancel();\n }\n\n if (input === \"\" && prevOptionSelected) {\n onSelectOption?.(undefined, undefined);\n handleDropdownDismiss();\n setPrevOptionSelected(undefined);\n }\n\n if (selectedOption && input !== getDisplayValue(selectedOption)) {\n setIsOptionSelected(false);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [input, selectedOption]);\n\n useEffect(() => {\n setInput(selectedOption ? getDisplayValue(selectedOption) : \"\");\n setSearchedInput(selectedOption ? getDisplayValue(selectedOption) : \"\");\n setPrevOptionSelected(selectedOption);\n setIsOptionSelected(!!selectedOption);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [selectedOption]);\n\n useEffect(() => {\n const state = getItemsLoadState();\n const count = options?.length ?? 0;\n\n if (state === \"loading\" && input.length >= minimumCharacters) {\n setResultAnnouncement(\"Loading suggested results\");\n return;\n }\n\n if (state === \"fail\") {\n setResultAnnouncement(\"Suggestions failed to load. Try again.\");\n return;\n }\n\n if (isOpen && !isLoading && !isError) {\n if (count === 0) {\n setResultAnnouncement(input ? \"No results found.\" : null);\n } else {\n setResultAnnouncement(\n `${count} result${\n count > 1 ? \"s\" : \"\"\n } found. Press down arrow to scroll through the list.`\n );\n }\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [options, input, isError, isLoading]);\n\n // =============================================================================\n // Cleanup: cancel debounce on unmount\n // =============================================================================\n useEffect(() => {\n return () => {\n fetchOptionsDebounced.cancel();\n };\n }, [fetchOptionsDebounced]);\n\n // =============================================================================\n // EVENT HANDLERS\n // =============================================================================\n const handleListItemClick = (item: T, extractedValue: V) => {\n selectorRef.current?.focus();\n setInput(getDisplayValue(item));\n setSearchedInput(item ? getDisplayValue(item) : \"\");\n setIsOptionSelected(true);\n setPrevOptionSelected(item);\n setIsOpen(false);\n onSelectOption?.(item, extractedValue);\n };\n\n const handleOpen = () => {\n if (!isOptionSelected && input.length >= minimumCharacters) {\n setIsFocused(true);\n }\n };\n\n const handleClose = () => {\n setIsOpen(false);\n setIsFocused(false);\n handleOnBlur();\n };\n\n const handleNodeFocus = () => {\n setIsFocused(true);\n };\n\n const handleNodeBlur = (e: React.FocusEvent) => {\n if (\n !isOpen &&\n nodeRef.current &&\n !nodeRef.current.contains(e.relatedTarget as Node)\n ) {\n setIsFocused(false);\n handleOnBlur();\n }\n };\n\n const handleListDismiss = () => {\n setIsOpen(false);\n setIsFocused(false);\n };\n\n const handleDismiss = () => {\n setIsOpen(false);\n setIsFocused(false);\n handleOnBlur();\n selectorRef.current?.focus();\n };\n\n const handleHide = () => {\n // reset dropdown state once it fully closes\n setOptions([]);\n setIsLoading(true);\n setIsError(false);\n };\n\n const handleOnClear = () => {\n setInput(\"\");\n setIsOptionSelected(false);\n setIsOpen(false);\n onSelectOption?.(undefined, undefined);\n };\n\n const handleOnBlur = () => {\n if (!isOptionSelected) {\n if (prevOptionSelected) {\n const prevValue = getDisplayValue(prevOptionSelected);\n setInput(prevValue);\n setIsOpen(false);\n } else {\n handleOnClear();\n }\n }\n };\n\n const handleTyping = (e: React.ChangeEvent<HTMLInputElement>) => {\n setInput(e.target.value);\n setIsOptionSelected(false);\n };\n\n const handleDropdownDismiss = (item?: T) => {\n setSearchedInput(item ? getDisplayValue(item) : \"\");\n setIsOptionSelected(!!item);\n setIsLoading(true);\n };\n\n // =============================================================================\n // HELPER FUNCTION\n // =============================================================================\n const getItemsLoadState = (): ItemsLoadStateType => {\n if (isError) return \"fail\";\n return isLoading ? \"loading\" : \"success\";\n };\n\n // =============================================================================\n // RENDER FUNCTION\n // =============================================================================\n const renderInputElement = () => {\n return (\n <InputWrapper\n className={className}\n data-testid={testId}\n ref={nodeRef}\n tabIndex={-1}\n onFocus={handleNodeFocus}\n onBlur={handleNodeBlur}\n focused={isFocused}\n disabled={disabled}\n readOnly={readOnly}\n error={error}\n >\n <VisuallyHidden id={instructionId} aria-hidden>\n Type in {minimumCharacters} or more characters for suggested\n results.\n </VisuallyHidden>\n {resultAnnouncement && (\n <VisuallyHidden aria-live=\"polite\">\n {resultAnnouncement}\n </VisuallyHidden>\n )}\n <Input\n role=\"combobox\"\n ref={selectorRef}\n id={internalId}\n type=\"text\"\n value={input}\n onChange={handleTyping}\n placeholder={placeholder}\n readOnly={readOnly}\n disabled={disabled}\n allowClear\n onClear={handleOnClear}\n aria-expanded={isOpen}\n aria-controls={internalId}\n aria-autocomplete=\"list\"\n aria-haspopup=\"listbox\"\n onBlur={\n input.length < minimumCharacters\n ? handleOnBlur\n : undefined\n }\n styleType=\"no-border\"\n aria-describedby={concatIds(ariaDescribedBy, instructionId)}\n {...otherProps}\n />\n </InputWrapper>\n );\n };\n\n const renderDropdown = () => {\n return (\n <DropdownList\n listboxId={internalId}\n listItems={options}\n onSelectItem={handleListItemClick}\n onDismiss={handleListDismiss}\n valueExtractor={valueExtractor}\n listExtractor={listExtractor}\n itemsLoadState={getItemsLoadState()}\n itemTruncationType={\"end\"}\n itemMaxLines={1}\n labelDisplayType={\"next-line\"}\n disableItemFocus\n onRetry={() => handleFetchOptions(input)}\n width={dropdownWidth}\n matchElementWidth\n />\n );\n };\n\n return (\n <DropdownListState>\n <ElementWithDropdown\n enabled={!readOnly && !disabled}\n isOpen={isOpen}\n renderElement={renderInputElement}\n renderDropdown={renderDropdown}\n onOpen={handleOpen}\n onClose={handleClose}\n onDismiss={handleDismiss}\n onHide={handleHide}\n clickToToggle={false}\n offset={8}\n alignment={alignment}\n fitAvailableHeight\n customZIndex={dropdownZIndex}\n rootNode={dropdownRootNode}\n />\n </DropdownListState>\n );\n};\n"],"names":["_a","className","testId","selectedOption","minimumCharacters","fetchOptions","placeholder","readOnly","disabled","error","valueExtractor","listExtractor","displayValueExtractor","onSelectOption","alignment","dropdownZIndex","dropdownRootNode","dropdownWidth","ariaDescribedBy","otherProps","__rest","getDisplayValue","item","toString","input","setInput","useState","searchedInput","setSearchedInput","options","setOptions","isLoading","setIsLoading","isError","setIsError","isOptionSelected","setIsOptionSelected","prevOptionSelected","setPrevOptionSelected","isOpen","setIsOpen","isFocused","setIsFocused","internalId","useId","resultAnnouncement","setResultAnnouncement","instructionId","nodeRef","useRef","selectorRef","fetchOptionsRef","handleFetchOptions","useCallback","__awaiter","current","fetchedOptions","fetchOptionsDebounced","debounce","useEffect","length","cancel","undefined","handleDropdownDismiss","state","getItemsLoadState","count","handleListItemClick","extractedValue","focus","handleNodeFocus","handleNodeBlur","e","contains","relatedTarget","handleOnBlur","handleListDismiss","handleOnClear","prevValue","handleTyping","target","value","_jsx","DropdownListState","children","ElementWithDropdown","enabled","renderElement","_jsxs","InputWrapper","ref","tabIndex","onFocus","onBlur","focused","VisuallyHidden","id","Input","Object","assign","role","type","onChange","allowClear","onClear","styleType","concatIds","renderDropdown","DropdownList","listboxId","listItems","onSelectItem","onDismiss","itemsLoadState","itemTruncationType","itemMaxLines","labelDisplayType","disableItemFocus","onRetry","width","matchElementWidth","onOpen","onClose","onHide","clickToToggle","offset","fitAvailableHeight","customZIndex","rootNode"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dropdown-list.js","sources":["../../../../src/shared/dropdown-list/dropdown-list.tsx"],"sourcesContent":["import { ExclamationCircleFillIcon } from \"@lifesg/react-icons/exclamation-circle-fill\";\nimport { SquareIcon } from \"@lifesg/react-icons/square\";\nimport { SquareFillIcon } from \"@lifesg/react-icons/square-fill\";\nimport { SquareTickFillIcon } from \"@lifesg/react-icons/square-tick-fill\";\nimport { TickIcon } from \"@lifesg/react-icons/tick\";\nimport clsx from \"clsx\";\nimport find from \"lodash/find\";\nimport isEqual from \"lodash/isEqual\";\nimport type React from \"react\";\nimport {\n forwardRef,\n useCallback,\n useContext,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n} from \"react\";\nimport type { VirtuosoHandle } from \"react-virtuoso\";\nimport { Virtuoso } from \"react-virtuoso\";\n\nimport { Markup } from \"../../markup\";\nimport { useApplyStyle } from \"../../theme\";\nimport {\n mergeRefs,\n useCompare,\n useEvent,\n useEventListener,\n useIsMounted,\n} from \"../../util\";\nimport { VisuallyHidden } from \"../accessibility\";\nimport { ComponentLoadingSpinner } from \"../component-loading-spinner\";\nimport { useDropdownRender } from \"../dropdown-wrapper\";\nimport { BasicButton } from \"../input-wrapper\";\nimport { DropdownLabel } from \"./dropdown-label\";\nimport type { ContainerWidthType } from \"./dropdown-list.styles\";\nimport * as styles from \"./dropdown-list.styles\";\nimport { DropdownListStateContext } from \"./dropdown-list-state\";\nimport { DropdownSearch } from \"./dropdown-search\";\nimport type {\n DropdownListApi,\n DropdownListProps,\n ListItemDisplayProps,\n} from \"./types\";\nimport { VirtuosoItem, VirtuosoList } from \"./virtuoso-components\";\n\n/**\n * NOTE: This component is not directly exportable but forms part of a component\n */\nconst DropdownListInner = <T, V>(\n {\n listItems,\n multiSelect,\n maxSelectable,\n selectedItems,\n disableItemFocus,\n itemsLoadState = \"success\",\n itemTruncationType = \"end\",\n itemMaxLines = 2,\n labelDisplayType = \"inline\",\n variant = \"default\",\n listboxId,\n ariaLabel,\n matchElementWidth = false,\n width,\n topScrollItem,\n onSelectItem,\n onSelectAll,\n onDismiss,\n onRetry,\n /* DropdownDisplayProps */\n valueExtractor,\n listExtractor,\n renderListItem,\n renderCustomCallToAction,\n /* DropdownSearchProps */\n enableSearch,\n hideNoResultsDisplay,\n noResultsDescription: _noResultsDescription,\n customLabels,\n searchPlaceholder: _searchPlaceholder,\n searchFunction,\n onSearch,\n }: DropdownListProps<T, V>,\n ref: React.ForwardedRef<DropdownListApi>\n): JSX.Element => {\n // =========================================================================\n // CONST, REF, STATE\n // =========================================================================\n const {\n noResultsLabel = \"No results found.\",\n selectAllButtonLabel = \"Select all\",\n clearAllButtonLabel = \"Clear all\",\n } = customLabels || {};\n const searchPlaceholder =\n customLabels?.searchPlaceholder || _searchPlaceholder || \"Search\";\n const noResultsDescription =\n customLabels?.noResultsDescription || _noResultsDescription;\n const { focusedIndex, setFocusedIndex } = useContext(\n DropdownListStateContext\n );\n const {\n elementWidth,\n setFloatingRef,\n getFloatingProps,\n styles: floatingStyles,\n } = useDropdownRender();\n const [searchValue, setSearchValue] = useState<string>(\"\");\n const [displayListItems, setDisplayListItems] = useState(listItems ?? []);\n const itemsLoadStateChanged = useCompare(itemsLoadState);\n const mounted = useIsMounted();\n\n const nodeRef = useRef<HTMLDivElement | null>(null);\n const listItemRefs = useRef<(HTMLElement | null)[]>([]);\n const searchInputRef = useRef<HTMLInputElement>(null);\n const virtuosoRef = useRef<VirtuosoHandle>(null);\n\n const hasSelectedMax =\n !!maxSelectable &&\n !!selectedItems &&\n selectedItems?.length === maxSelectable;\n\n // =========================================================================\n // APPLY STYLES\n // =========================================================================\n let containerWidthType: ContainerWidthType = \"default\";\n let containerWidth: string | undefined = undefined;\n if (width) {\n containerWidth = width;\n containerWidthType = \"custom\";\n } else if (matchElementWidth && elementWidth) {\n containerWidth = `${elementWidth}px`;\n containerWidthType = \"match\";\n }\n\n useApplyStyle(nodeRef, {\n [styles.tokens.containerWidth]: containerWidth,\n });\n\n // =========================================================================\n // HELPER FUNCTIONS\n // =========================================================================\n const getValue = (item: T): V => {\n return valueExtractor ? valueExtractor(item) : (item as unknown as V);\n };\n\n const getItemKey = (item: T, index: number) => {\n const formattedValue = valueExtractor ? valueExtractor(item) : item;\n return `item_${index}__${formattedValue}`;\n };\n\n const getOptionLabel = (item: T): ListItemDisplayProps => {\n const value = listExtractor ? listExtractor(item) : item?.toString();\n\n if (typeof value === \"object\") {\n return { title: value.title, secondaryLabel: value.secondaryLabel };\n }\n\n return { title: value ?? \"\" };\n };\n\n const checkListItemSelected = useCallback(\n (item: T): boolean => {\n return !!find(selectedItems, (arrItem) => {\n return isEqual(arrItem, item);\n });\n },\n [selectedItems]\n );\n\n const filterItemsByCustomSearch = useEvent(() => {\n return searchFunction?.(searchValue);\n });\n\n const filterItemsByLabel = useEvent(() => {\n return listItems?.filter((item) => {\n const { title, secondaryLabel } = getOptionLabel(item);\n const updatedSearchValue = searchValue.trim().toLowerCase();\n return (\n title.toLowerCase().includes(updatedSearchValue) ||\n (secondaryLabel &&\n secondaryLabel.toLowerCase().includes(updatedSearchValue))\n );\n });\n });\n\n const refocus = useCallback(\n (opts?: { index?: number; preferSelected?: boolean }) => {\n if (disableItemFocus) return;\n\n if (searchInputRef.current) {\n setFocusedIndex(-1);\n setTimeout(() => searchInputRef.current?.focus(), 0);\n return;\n }\n\n const items = displayListItems ?? [];\n if (items.length === 0) return;\n\n if (opts?.index !== undefined) {\n const forced = Math.max(\n 0,\n Math.min(opts.index, items.length - 1)\n );\n virtuosoRef.current?.scrollToIndex({\n index: forced,\n align: \"center\",\n });\n setFocusedIndex(forced);\n setTimeout(() => listItemRefs.current[forced]?.focus(), 0);\n\n return;\n }\n\n const selectedIndex = opts?.preferSelected\n ? items.findIndex((item) => checkListItemSelected(item))\n : -1;\n\n const nextIndex =\n selectedIndex !== -1\n ? selectedIndex\n : focusedIndex >= 0 && focusedIndex < items.length\n ? focusedIndex\n : 0;\n\n virtuosoRef.current?.scrollToIndex({\n index: nextIndex,\n align: \"center\",\n });\n setFocusedIndex(nextIndex);\n setTimeout(() => listItemRefs.current[nextIndex]?.focus(), 0);\n },\n [\n checkListItemSelected,\n disableItemFocus,\n displayListItems,\n focusedIndex,\n setFocusedIndex,\n ]\n );\n\n // =========================================================================\n // EVENT HANDLERS\n // =========================================================================\n const handleKeyboardPress = (event: KeyboardEvent) => {\n switch (event.code) {\n case \"ArrowDown\":\n event.preventDefault();\n if (focusedIndex < displayListItems.length - 1) {\n const upcomingIndex = focusedIndex + 1;\n listItemRefs.current[upcomingIndex]?.focus();\n setFocusedIndex(upcomingIndex);\n }\n break;\n case \"ArrowUp\":\n event.preventDefault();\n if (focusedIndex > 0) {\n const upcomingIndex = focusedIndex - 1;\n listItemRefs.current[upcomingIndex]?.focus();\n setFocusedIndex(upcomingIndex);\n } else if (focusedIndex === 0 && searchInputRef.current) {\n searchInputRef.current.focus();\n setFocusedIndex(-1);\n }\n break;\n case \"Space\":\n case \"Enter\":\n if (\n focusedIndex >= 0 &&\n (document.activeElement ===\n listItemRefs.current[focusedIndex] ||\n document.activeElement === searchInputRef.current)\n ) {\n event.preventDefault();\n if (displayListItems[focusedIndex]) {\n handleListItemClick(\n displayListItems[focusedIndex],\n focusedIndex\n );\n }\n }\n break;\n default:\n break;\n }\n };\n\n const handleListItemClick = (item: T, upcomingIndex: number) => {\n if (hasSelectedMax && !checkListItemSelected(item)) return;\n setFocusedIndex(upcomingIndex);\n onSelectItem?.(item, getValue(item));\n };\n\n const handleListItemHover = (index: number) => setFocusedIndex(index);\n\n const handleSearchInputChange = (\n event: React.ChangeEvent<HTMLInputElement>\n ) => {\n const value = event.target.value;\n setSearchValue(value);\n onSearch?.();\n };\n\n const handleOnClear = () => {\n setSearchValue(\"\");\n searchInputRef.current?.focus();\n onSearch?.();\n };\n\n const handleTryAgain = () => onRetry?.();\n\n // =========================================================================\n // EFFECTS\n // =========================================================================\n useEventListener(\"keydown\", handleKeyboardPress);\n useImperativeHandle(ref, () => ({ refocus }), [refocus]);\n\n useEffect(() => {\n if (!topScrollItem) {\n virtuosoRef.current?.scrollTo({ top: 0 });\n return;\n }\n const timer = setTimeout(() => {\n if (!listItems) return;\n\n const index = listItems.indexOf(topScrollItem);\n if (virtuosoRef.current && index !== -1) {\n virtuosoRef.current.scrollToIndex({ index });\n setFocusedIndex(index);\n }\n }, 0);\n\n return () => clearTimeout(timer);\n }, [listItemRefs, listItems, setFocusedIndex, topScrollItem]);\n\n useEffect(() => {\n if (disableItemFocus) return;\n if (!mounted || !itemsLoadStateChanged) return;\n\n if (itemsLoadState === \"success\") {\n if (searchInputRef.current) {\n setFocusedIndex(-1);\n searchInputRef.current.focus();\n }\n }\n }, [\n mounted,\n itemsLoadStateChanged,\n itemsLoadState,\n setFocusedIndex,\n disableItemFocus,\n ]);\n\n useEffect(() => {\n const filterItems = () => {\n if (searchValue === \"\") {\n return listItems;\n } else if (searchFunction) {\n return filterItemsByCustomSearch();\n } else {\n return filterItemsByLabel();\n }\n };\n\n setDisplayListItems(filterItems() ?? []);\n }, [\n filterItemsByCustomSearch,\n filterItemsByLabel,\n listItems,\n searchFunction,\n searchValue,\n ]);\n\n useEffect(() => {\n if (mounted) {\n return;\n }\n\n if (disableItemFocus || !listItems) return;\n\n const index = listItems.findIndex((item) =>\n checkListItemSelected(item)\n );\n\n if (searchInputRef.current) {\n setFocusedIndex(-1);\n setTimeout(() => searchInputRef.current?.focus(), 200);\n } else if (focusedIndex > 0) {\n virtuosoRef.current?.scrollToIndex({\n index: focusedIndex,\n align: \"center\",\n });\n setTimeout(() => listItemRefs.current[focusedIndex]?.focus(), 200);\n } else if (index !== -1) {\n virtuosoRef.current?.scrollToIndex({ index, align: \"center\" });\n setFocusedIndex(index);\n setTimeout(() => listItemRefs.current[index]?.focus(), 200);\n } else {\n virtuosoRef.current?.scrollToIndex({ index: 0 });\n setFocusedIndex(0);\n setTimeout(() => listItemRefs.current[0]?.focus(), 200);\n }\n }, [\n checkListItemSelected,\n disableItemFocus,\n focusedIndex,\n listItems,\n mounted,\n setFocusedIndex,\n ]);\n\n // =========================================================================\n // RENDER FUNCTIONS\n // =========================================================================\n const renderListItemIcon = (selected: boolean) => {\n if (multiSelect) {\n if (hasSelectedMax && !selected) {\n return (\n <SquareFillIcon\n aria-hidden\n className={clsx(\n styles.baseCheckboxIndicatorStyle,\n styles.checkboxDisabledIndicator\n )}\n />\n );\n }\n\n return selected ? (\n <SquareTickFillIcon\n aria-hidden\n className={clsx(\n styles.baseCheckboxIndicatorStyle,\n styles.checkboxSelectedIndicator\n )}\n />\n ) : (\n <SquareIcon\n aria-hidden\n className={clsx(\n styles.baseCheckboxIndicatorStyle,\n styles.checkboxUnselectedIndicator\n )}\n />\n );\n }\n\n return selected ? (\n <TickIcon\n aria-hidden\n className={clsx(\n styles.baseIndicatorStyle,\n styles.selectedIndicator\n )}\n />\n ) : (\n <div className={styles.baseIndicatorStyle} />\n );\n };\n\n const renderDropdownLabel = (item: T, selected: boolean) => {\n const { title, secondaryLabel } = getOptionLabel(item);\n\n return (\n <DropdownLabel\n displayType={labelDisplayType}\n label={title}\n maxLines={itemMaxLines}\n selected={selected}\n disabled={!selected && hasSelectedMax}\n sublabel={secondaryLabel}\n truncationType={itemTruncationType}\n variant={variant}\n />\n );\n };\n\n const renderItem = (item: T, index: number) => {\n if (!onRetry || itemsLoadState === \"success\") {\n const selected = checkListItemSelected(item);\n const active = index === focusedIndex;\n const disabled = !selected && hasSelectedMax;\n return (\n <li\n aria-selected={selected}\n aria-multiselectable={multiSelect}\n aria-disabled={disabled}\n aria-posinset={index + 1}\n aria-setsize={displayListItems?.length}\n data-testid=\"list-item\"\n key={getItemKey(item, index)}\n onClick={() => handleListItemClick(item, index)}\n onMouseEnter={() => handleListItemHover(index)}\n ref={(element) => {\n listItemRefs.current[index] = element;\n }}\n role=\"option\"\n tabIndex={active ? 0 : -1}\n className={clsx(\n styles.listItem,\n (disabled && styles.listItemDisabled) ||\n (active &&\n selected &&\n styles.listItemActiveSelected) ||\n (active && styles.listItemActive)\n )}\n >\n {renderListItem ? (\n renderListItem(item, { selected })\n ) : (\n <>\n {renderListItemIcon(selected)}\n {renderDropdownLabel(item, selected)}\n </>\n )}\n </li>\n );\n }\n };\n\n const renderSearchInput = () => {\n if ((enableSearch || searchFunction) && itemsLoadState === \"success\") {\n return (\n <DropdownSearch\n ref={searchInputRef}\n onChange={handleSearchInputChange}\n value={searchValue}\n placeholder={searchPlaceholder}\n data-testid=\"search-input\"\n aria-label=\"Enter text to search\"\n onClear={handleOnClear}\n variant={variant}\n />\n );\n }\n };\n\n const renderSelectAll = () => {\n if (\n selectedItems &&\n multiSelect &&\n displayListItems.length > 0 &&\n !searchValue &&\n itemsLoadState === \"success\"\n ) {\n return (\n <div className={styles.selectAllContainer}>\n <BasicButton\n onClick={onSelectAll}\n type=\"button\"\n className={clsx(\n styles.baseButton,\n styles.selectAllButton\n )}\n >\n {maxSelectable || selectedItems.length !== 0\n ? clearAllButtonLabel\n : selectAllButtonLabel}\n </BasicButton>\n </div>\n );\n }\n };\n\n const renderNoResults = () => {\n if (\n !hideNoResultsDisplay &&\n (searchValue || !enableSearch) &&\n displayListItems.length === 0 &&\n itemsLoadState === \"success\"\n ) {\n return (\n <>\n <div\n data-testid=\"list-no-results\"\n className={styles.resultStateContainer}\n >\n <ExclamationCircleFillIcon\n data-testid=\"no-result-icon\"\n className={styles.labelIcon}\n />\n {noResultsLabel}\n </div>\n {noResultsDescription && (\n <Markup\n data-testid=\"no-result-desc\"\n className={styles.noResultDescContainer}\n >\n {noResultsDescription}\n </Markup>\n )}\n </>\n );\n }\n };\n\n const renderLoading = () => {\n if (onRetry && itemsLoadState === \"loading\") {\n return (\n <div\n data-testid=\"list-loading\"\n className={styles.resultStateContainer}\n >\n <ComponentLoadingSpinner className={styles.spinner} />\n Loading...\n </div>\n );\n }\n };\n\n const renderTryAgain = () => {\n if (onRetry && itemsLoadState === \"fail\") {\n return (\n <div\n data-testid=\"list-fail\"\n className={styles.resultStateContainer}\n >\n <ExclamationCircleFillIcon\n data-testid=\"load-error-icon\"\n className={styles.labelIcon}\n />\n Failed to load. \n <BasicButton\n onClick={handleTryAgain}\n type=\"button\"\n className={clsx(\n styles.baseButton,\n styles.tryAgainButton\n )}\n >\n Try again.\n </BasicButton>\n </div>\n );\n }\n };\n\n const renderVirtualisedList = () => {\n const isTestEnv = process.env.NODE_ENV === \"test\";\n\n return (\n <Virtuoso\n ref={virtuosoRef}\n style={{ height: \"100%\" }}\n data={displayListItems}\n customScrollParent={nodeRef.current ?? undefined}\n itemContent={(index, item) => renderItem(item, index)}\n components={{\n List: VirtuosoList,\n Item: VirtuosoItem,\n }}\n context={{\n listProps: {\n id: listboxId,\n role: \"listbox\",\n \"aria-multiselectable\": multiSelect,\n },\n listItemProps: {\n role: \"none\",\n },\n }}\n // disable virtualisation in tests\n // https://github.com/petyosi/react-virtuoso/issues/26#issuecomment-1040316576\n // explicitly set the `key` prop to avoid React warning\n key={isTestEnv ? displayListItems.length : undefined}\n // omit the `initialItemCount` prop to resolve NaN error\n {...(isTestEnv\n ? {\n initialItemCount: displayListItems.length,\n }\n : {})}\n />\n );\n };\n\n const renderList = () => {\n return (\n <div\n data-testid=\"dropdown-list\"\n role=\"group\"\n aria-label={ariaLabel}\n className={styles.list}\n >\n {renderSearchInput()}\n {renderSelectAll()}\n {renderNoResults()}\n {renderLoading()}\n {renderTryAgain()}\n {renderVirtualisedList()}\n </div>\n );\n };\n\n const renderBottomCta = () => {\n if (!renderCustomCallToAction) {\n return;\n }\n\n return (\n <div data-testid=\"custom-cta\">\n {renderCustomCallToAction(onDismiss as any, displayListItems)}\n </div>\n );\n };\n\n return (\n <div\n data-testid=\"dropdown-container\"\n ref={mergeRefs(nodeRef, setFloatingRef)}\n {...getFloatingProps()}\n data-width-type={containerWidthType}\n className={clsx(\n styles.container,\n variant === \"small\" && styles.containerVariantSmall\n )}\n style={floatingStyles}\n >\n <VisuallyHidden role=\"status\">{ariaLabel}</VisuallyHidden>\n {renderList()}\n {renderBottomCta()}\n </div>\n );\n};\n\nexport const DropdownList = forwardRef(DropdownListInner);\n"],"names":["DropdownList","forwardRef","listItems","multiSelect","maxSelectable","selectedItems","disableItemFocus","itemsLoadState","itemTruncationType","itemMaxLines","labelDisplayType","variant","listboxId","ariaLabel","matchElementWidth","width","topScrollItem","onSelectItem","onSelectAll","onDismiss","onRetry","valueExtractor","listExtractor","renderListItem","renderCustomCallToAction","enableSearch","hideNoResultsDisplay","noResultsDescription","_noResultsDescription","customLabels","searchPlaceholder","_searchPlaceholder","searchFunction","onSearch","ref","noResultsLabel","selectAllButtonLabel","clearAllButtonLabel","focusedIndex","setFocusedIndex","useContext","DropdownListStateContext","elementWidth","setFloatingRef","getFloatingProps","styles","floatingStyles","useDropdownRender","searchValue","setSearchValue","useState","displayListItems","setDisplayListItems","itemsLoadStateChanged","useCompare","mounted","useIsMounted","nodeRef","useRef","listItemRefs","searchInputRef","virtuosoRef","hasSelectedMax","length","containerWidth","containerWidthType","useApplyStyle","styles.tokens","getOptionLabel","item","value","toString","title","secondaryLabel","checkListItemSelected","useCallback","find","arrItem","isEqual","filterItemsByCustomSearch","useEvent","filterItemsByLabel","filter","updatedSearchValue","trim","toLowerCase","includes","refocus","opts","current","setTimeout","_a","focus","items","undefined","index","forced","Math","max","min","scrollToIndex","align","selectedIndex","preferSelected","findIndex","nextIndex","_b","handleListItemClick","upcomingIndex","getValue","handleSearchInputChange","event","target","handleOnClear","handleTryAgain","useEventListener","code","preventDefault","document","activeElement","useImperativeHandle","useEffect","scrollTo","top","timer","indexOf","clearTimeout","_c","renderListItemIcon","selected","_jsx","SquareFillIcon","className","clsx","styles.baseCheckboxIndicatorStyle","styles.checkboxDisabledIndicator","SquareTickFillIcon","styles.checkboxSelectedIndicator","SquareIcon","styles.checkboxUnselectedIndicator","TickIcon","styles.baseIndicatorStyle","styles.selectedIndicator","renderDropdownLabel","DropdownLabel","displayType","label","maxLines","disabled","sublabel","truncationType","renderItem","active","onClick","onMouseEnter","handleListItemHover","element","role","tabIndex","styles.listItem","styles.listItemDisabled","styles.listItemActiveSelected","styles.listItemActive","children","_jsxs","_Fragment","getItemKey","renderSearchInput","DropdownSearch","onChange","placeholder","onClear","renderSelectAll","styles.selectAllContainer","BasicButton","type","styles.baseButton","styles.selectAllButton","renderNoResults","styles.resultStateContainer","ExclamationCircleFillIcon","styles.labelIcon","Markup","styles.noResultDescContainer","renderLoading","ComponentLoadingSpinner","styles.spinner","renderTryAgain","styles.tryAgainButton","renderVirtualisedList","isTestEnv","process","env","NODE_ENV","Virtuoso","Object","assign","style","height","data","customScrollParent","itemContent","components","List","VirtuosoList","Item","VirtuosoItem","context","listProps","id","listItemProps","initialItemCount","mergeRefs","styles.container","styles.containerVariantSmall","VisuallyHidden","styles.list","renderBottomCta"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"dropdown-list.js","sources":["../../../../src/shared/dropdown-list/dropdown-list.tsx"],"sourcesContent":["import { ExclamationCircleFillIcon } from \"@lifesg/react-icons/exclamation-circle-fill\";\nimport { SquareIcon } from \"@lifesg/react-icons/square\";\nimport { SquareFillIcon } from \"@lifesg/react-icons/square-fill\";\nimport { SquareTickFillIcon } from \"@lifesg/react-icons/square-tick-fill\";\nimport { TickIcon } from \"@lifesg/react-icons/tick\";\nimport clsx from \"clsx\";\nimport find from \"lodash/find\";\nimport isEqual from \"lodash/isEqual\";\nimport type React from \"react\";\nimport {\n forwardRef,\n useCallback,\n useContext,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n} from \"react\";\nimport type { VirtuosoHandle } from \"react-virtuoso\";\nimport { Virtuoso } from \"react-virtuoso\";\n\nimport { Markup } from \"../../markup\";\nimport { useApplyStyle } from \"../../theme\";\nimport {\n mergeRefs,\n useCompare,\n useEvent,\n useEventListener,\n useIsMounted,\n} from \"../../util\";\nimport { VisuallyHidden } from \"../accessibility\";\nimport { ComponentLoadingSpinner } from \"../component-loading-spinner\";\nimport { useDropdownRender } from \"../dropdown-wrapper\";\nimport { BasicButton } from \"../input-wrapper\";\nimport { DropdownLabel } from \"./dropdown-label\";\nimport type { ContainerWidthType } from \"./dropdown-list.styles\";\nimport * as styles from \"./dropdown-list.styles\";\nimport { DropdownListStateContext } from \"./dropdown-list-state\";\nimport { DropdownSearch } from \"./dropdown-search\";\nimport type {\n DropdownListApi,\n DropdownListProps,\n ListItemDisplayProps,\n} from \"./types\";\nimport { VirtuosoItem, VirtuosoList } from \"./virtuoso-components\";\n\n/**\n * NOTE: This component is not directly exportable but forms part of a component\n */\nconst DropdownListInner = <T, V>(\n {\n listItems,\n multiSelect,\n maxSelectable,\n selectedItems,\n disableItemFocus,\n itemsLoadState = \"success\",\n itemTruncationType = \"end\",\n itemMaxLines = 2,\n labelDisplayType = \"inline\",\n variant = \"default\",\n listboxId,\n ariaLabel,\n matchElementWidth = false,\n width,\n topScrollItem,\n onSelectItem,\n onSelectAll,\n onDismiss,\n onRetry,\n /* DropdownDisplayProps */\n valueExtractor,\n listExtractor,\n renderListItem,\n renderCustomCallToAction,\n /* DropdownSearchProps */\n enableSearch,\n hideNoResultsDisplay,\n noResultsDescription: _noResultsDescription,\n customLabels,\n searchPlaceholder: _searchPlaceholder,\n searchFunction,\n onSearch,\n }: DropdownListProps<T, V>,\n ref: React.ForwardedRef<DropdownListApi>\n): JSX.Element => {\n // =========================================================================\n // CONST, REF, STATE\n // =========================================================================\n const {\n noResultsLabel = \"No results found.\",\n selectAllButtonLabel = \"Select all\",\n clearAllButtonLabel = \"Clear all\",\n } = customLabels || {};\n const searchPlaceholder =\n customLabels?.searchPlaceholder || _searchPlaceholder || \"Search\";\n const noResultsDescription =\n customLabels?.noResultsDescription || _noResultsDescription;\n const { focusedIndex, setFocusedIndex } = useContext(\n DropdownListStateContext\n );\n const {\n elementWidth,\n setFloatingRef,\n getFloatingProps,\n styles: floatingStyles,\n } = useDropdownRender();\n const [searchValue, setSearchValue] = useState<string>(\"\");\n const [displayListItems, setDisplayListItems] = useState(listItems ?? []);\n const itemsLoadStateChanged = useCompare(itemsLoadState);\n const mounted = useIsMounted();\n\n const nodeRef = useRef<HTMLDivElement | null>(null);\n const listItemRefs = useRef<(HTMLElement | null)[]>([]);\n const searchInputRef = useRef<HTMLInputElement>(null);\n const virtuosoRef = useRef<VirtuosoHandle>(null);\n\n const hasSelectedMax =\n !!maxSelectable &&\n !!selectedItems &&\n selectedItems?.length === maxSelectable;\n\n // =========================================================================\n // APPLY STYLES\n // =========================================================================\n let containerWidthType: ContainerWidthType = \"default\";\n let containerWidth: string | undefined = undefined;\n if (width) {\n containerWidth = width;\n containerWidthType = \"custom\";\n } else if (matchElementWidth && elementWidth) {\n containerWidth = `${elementWidth}px`;\n containerWidthType = \"match\";\n }\n\n useApplyStyle(nodeRef, {\n [styles.tokens.containerWidth]: containerWidth,\n });\n\n // =========================================================================\n // HELPER FUNCTIONS\n // =========================================================================\n const getValue = (item: T): V => {\n return valueExtractor ? valueExtractor(item) : (item as unknown as V);\n };\n\n const getItemKey = (item: T, index: number) => {\n const formattedValue = valueExtractor ? valueExtractor(item) : item;\n return `item_${index}__${formattedValue}`;\n };\n\n const getOptionLabel = (item: T): ListItemDisplayProps => {\n const value = listExtractor ? listExtractor(item) : item?.toString();\n\n if (typeof value === \"object\") {\n return { title: value.title, secondaryLabel: value.secondaryLabel };\n }\n\n return { title: value ?? \"\" };\n };\n\n const checkListItemSelected = useCallback(\n (item: T): boolean => {\n return !!find(selectedItems, (arrItem) => {\n return isEqual(arrItem, item);\n });\n },\n [selectedItems]\n );\n\n const filterItemsByCustomSearch = useEvent(() => {\n return searchFunction?.(searchValue);\n });\n\n const filterItemsByLabel = useEvent(() => {\n return listItems?.filter((item) => {\n const { title, secondaryLabel } = getOptionLabel(item);\n const updatedSearchValue = searchValue.trim().toLowerCase();\n return (\n title.toLowerCase().includes(updatedSearchValue) ||\n (secondaryLabel &&\n secondaryLabel.toLowerCase().includes(updatedSearchValue))\n );\n });\n });\n\n const refocus = useCallback(\n (opts?: { index?: number; preferSelected?: boolean }) => {\n if (disableItemFocus) return;\n\n if (searchInputRef.current) {\n setFocusedIndex(-1);\n setTimeout(() => searchInputRef.current?.focus(), 0);\n return;\n }\n\n const items = displayListItems ?? [];\n if (items.length === 0) return;\n\n if (opts?.index !== undefined) {\n const forced = Math.max(\n 0,\n Math.min(opts.index, items.length - 1)\n );\n virtuosoRef.current?.scrollToIndex({\n index: forced,\n align: \"center\",\n });\n setFocusedIndex(forced);\n setTimeout(() => listItemRefs.current[forced]?.focus(), 0);\n\n return;\n }\n\n const selectedIndex = opts?.preferSelected\n ? items.findIndex((item) => checkListItemSelected(item))\n : -1;\n\n const nextIndex =\n selectedIndex !== -1\n ? selectedIndex\n : focusedIndex >= 0 && focusedIndex < items.length\n ? focusedIndex\n : 0;\n\n virtuosoRef.current?.scrollToIndex({\n index: nextIndex,\n align: \"center\",\n });\n setFocusedIndex(nextIndex);\n setTimeout(() => listItemRefs.current[nextIndex]?.focus(), 0);\n },\n [\n checkListItemSelected,\n disableItemFocus,\n displayListItems,\n focusedIndex,\n setFocusedIndex,\n ]\n );\n\n // =========================================================================\n // EVENT HANDLERS\n // =========================================================================\n const handleKeyboardPress = (event: KeyboardEvent) => {\n switch (event.code) {\n case \"ArrowDown\":\n event.preventDefault();\n if (focusedIndex < displayListItems.length - 1) {\n const upcomingIndex = focusedIndex + 1;\n listItemRefs.current[upcomingIndex]?.focus();\n setFocusedIndex(upcomingIndex);\n }\n break;\n case \"ArrowUp\":\n event.preventDefault();\n if (focusedIndex > 0) {\n const upcomingIndex = focusedIndex - 1;\n listItemRefs.current[upcomingIndex]?.focus();\n setFocusedIndex(upcomingIndex);\n } else if (focusedIndex === 0 && searchInputRef.current) {\n searchInputRef.current.focus();\n setFocusedIndex(-1);\n }\n break;\n case \"Space\":\n case \"Enter\":\n if (\n focusedIndex >= 0 &&\n (document.activeElement ===\n listItemRefs.current[focusedIndex] ||\n document.activeElement === searchInputRef.current)\n ) {\n event.preventDefault();\n if (displayListItems[focusedIndex]) {\n handleListItemClick(\n displayListItems[focusedIndex],\n focusedIndex\n );\n }\n }\n break;\n default:\n break;\n }\n };\n\n const handleListItemClick = (item: T, upcomingIndex: number) => {\n if (hasSelectedMax && !checkListItemSelected(item)) return;\n setFocusedIndex(upcomingIndex);\n onSelectItem?.(item, getValue(item));\n };\n\n const handleListItemHover = (index: number) => setFocusedIndex(index);\n\n const handleSearchInputChange = (\n event: React.ChangeEvent<HTMLInputElement>\n ) => {\n const value = event.target.value;\n setSearchValue(value);\n onSearch?.();\n };\n\n const handleOnClear = () => {\n setSearchValue(\"\");\n searchInputRef.current?.focus();\n onSearch?.();\n };\n\n const handleTryAgain = () => onRetry?.();\n\n // =========================================================================\n // EFFECTS\n // =========================================================================\n useEventListener(\"keydown\", handleKeyboardPress);\n useImperativeHandle(ref, () => ({ refocus }), [refocus]);\n\n useEffect(() => {\n if (!topScrollItem) {\n virtuosoRef.current?.scrollTo({ top: 0 });\n return;\n }\n const timer = setTimeout(() => {\n if (!listItems) return;\n\n const index = listItems.indexOf(topScrollItem);\n if (virtuosoRef.current && index !== -1) {\n virtuosoRef.current.scrollToIndex({ index });\n setFocusedIndex(index);\n }\n }, 0);\n\n return () => clearTimeout(timer);\n }, [listItemRefs, listItems, setFocusedIndex, topScrollItem]);\n\n useEffect(() => {\n if (disableItemFocus) return;\n if (!mounted || !itemsLoadStateChanged) return;\n\n if (itemsLoadState === \"success\") {\n if (searchInputRef.current) {\n setFocusedIndex(-1);\n searchInputRef.current.focus();\n }\n }\n }, [\n mounted,\n itemsLoadStateChanged,\n itemsLoadState,\n setFocusedIndex,\n disableItemFocus,\n ]);\n\n useEffect(() => {\n const filterItems = () => {\n if (searchValue === \"\") {\n return listItems;\n } else if (searchFunction) {\n return filterItemsByCustomSearch();\n } else {\n return filterItemsByLabel();\n }\n };\n\n setDisplayListItems(filterItems() ?? []);\n }, [\n filterItemsByCustomSearch,\n filterItemsByLabel,\n listItems,\n searchFunction,\n searchValue,\n ]);\n\n useEffect(() => {\n if (mounted) {\n return;\n }\n\n if (disableItemFocus || !listItems) return;\n\n const index = listItems.findIndex((item) =>\n checkListItemSelected(item)\n );\n\n if (searchInputRef.current) {\n setFocusedIndex(-1);\n setTimeout(() => searchInputRef.current?.focus(), 200);\n } else if (focusedIndex > 0) {\n virtuosoRef.current?.scrollToIndex({\n index: focusedIndex,\n align: \"center\",\n });\n setTimeout(() => listItemRefs.current[focusedIndex]?.focus(), 200);\n } else if (index !== -1) {\n virtuosoRef.current?.scrollToIndex({ index, align: \"center\" });\n setFocusedIndex(index);\n setTimeout(() => listItemRefs.current[index]?.focus(), 200);\n } else {\n virtuosoRef.current?.scrollToIndex({ index: 0 });\n setFocusedIndex(0);\n setTimeout(() => listItemRefs.current[0]?.focus(), 200);\n }\n }, [\n checkListItemSelected,\n disableItemFocus,\n focusedIndex,\n listItems,\n mounted,\n setFocusedIndex,\n ]);\n\n // =========================================================================\n // RENDER FUNCTIONS\n // =========================================================================\n const renderListItemIcon = (selected: boolean) => {\n if (multiSelect) {\n if (hasSelectedMax && !selected) {\n return (\n <SquareFillIcon\n aria-hidden\n className={clsx(\n styles.baseCheckboxIndicatorStyle,\n styles.checkboxDisabledIndicator\n )}\n />\n );\n }\n\n return selected ? (\n <SquareTickFillIcon\n aria-hidden\n className={clsx(\n styles.baseCheckboxIndicatorStyle,\n styles.checkboxSelectedIndicator\n )}\n />\n ) : (\n <SquareIcon\n aria-hidden\n className={clsx(\n styles.baseCheckboxIndicatorStyle,\n styles.checkboxUnselectedIndicator\n )}\n />\n );\n }\n\n return selected ? (\n <TickIcon\n aria-hidden\n className={clsx(\n styles.baseIndicatorStyle,\n styles.selectedIndicator\n )}\n />\n ) : (\n <div className={styles.baseIndicatorStyle} />\n );\n };\n\n const renderDropdownLabel = (item: T, selected: boolean) => {\n const { title, secondaryLabel } = getOptionLabel(item);\n\n return (\n <DropdownLabel\n displayType={labelDisplayType}\n label={title}\n maxLines={itemMaxLines}\n selected={selected}\n disabled={!selected && hasSelectedMax}\n sublabel={secondaryLabel}\n truncationType={itemTruncationType}\n variant={variant}\n />\n );\n };\n\n const renderItem = (item: T, index: number) => {\n if (!onRetry || itemsLoadState === \"success\") {\n const selected = checkListItemSelected(item);\n const active = index === focusedIndex;\n const disabled = !selected && hasSelectedMax;\n return (\n <li\n aria-selected={selected}\n aria-multiselectable={multiSelect}\n aria-disabled={disabled}\n aria-posinset={index + 1}\n aria-setsize={displayListItems?.length}\n data-testid=\"list-item\"\n key={getItemKey(item, index)}\n onClick={() => handleListItemClick(item, index)}\n onMouseEnter={() => handleListItemHover(index)}\n ref={(element) => {\n listItemRefs.current[index] = element;\n }}\n role=\"option\"\n tabIndex={active ? 0 : -1}\n className={clsx(\n styles.listItem,\n (disabled && styles.listItemDisabled) ||\n (active &&\n selected &&\n styles.listItemActiveSelected) ||\n (active && styles.listItemActive)\n )}\n >\n {renderListItem ? (\n renderListItem(item, { selected })\n ) : (\n <>\n {renderListItemIcon(selected)}\n {renderDropdownLabel(item, selected)}\n </>\n )}\n </li>\n );\n }\n };\n\n const renderSearchInput = () => {\n if ((enableSearch || searchFunction) && itemsLoadState === \"success\") {\n return (\n <DropdownSearch\n ref={searchInputRef}\n onChange={handleSearchInputChange}\n value={searchValue}\n placeholder={searchPlaceholder}\n data-testid=\"search-input\"\n aria-label=\"Enter text to search\"\n onClear={handleOnClear}\n variant={variant}\n />\n );\n }\n };\n\n const renderSelectAll = () => {\n if (\n selectedItems &&\n multiSelect &&\n displayListItems.length > 0 &&\n !searchValue &&\n itemsLoadState === \"success\"\n ) {\n return (\n <div className={styles.selectAllContainer}>\n <BasicButton\n onClick={onSelectAll}\n type=\"button\"\n className={clsx(\n styles.baseButton,\n styles.selectAllButton\n )}\n >\n {maxSelectable || selectedItems.length !== 0\n ? clearAllButtonLabel\n : selectAllButtonLabel}\n </BasicButton>\n </div>\n );\n }\n };\n\n const renderNoResults = () => {\n if (\n !hideNoResultsDisplay &&\n (searchValue || !enableSearch) &&\n displayListItems.length === 0 &&\n itemsLoadState === \"success\"\n ) {\n return (\n <>\n <div\n data-testid=\"list-no-results\"\n className={styles.resultStateContainer}\n >\n <ExclamationCircleFillIcon\n data-testid=\"no-result-icon\"\n className={styles.labelIcon}\n />\n {noResultsLabel}\n </div>\n {noResultsDescription && (\n <Markup\n data-testid=\"no-result-desc\"\n className={styles.noResultDescContainer}\n >\n {noResultsDescription}\n </Markup>\n )}\n </>\n );\n }\n };\n\n const renderLoading = () => {\n if (onRetry && itemsLoadState === \"loading\") {\n return (\n <div\n data-testid=\"list-loading\"\n className={styles.resultStateContainer}\n >\n <ComponentLoadingSpinner className={styles.spinner} />\n Loading...\n </div>\n );\n }\n };\n\n const renderTryAgain = () => {\n if (onRetry && itemsLoadState === \"fail\") {\n return (\n <div\n data-testid=\"list-fail\"\n className={styles.resultStateContainer}\n >\n <ExclamationCircleFillIcon\n data-testid=\"load-error-icon\"\n className={styles.labelIcon}\n />\n Failed to load. \n <BasicButton\n onClick={handleTryAgain}\n type=\"button\"\n className={clsx(\n styles.baseButton,\n styles.tryAgainButton\n )}\n >\n Try again.\n </BasicButton>\n </div>\n );\n }\n };\n\n const renderVirtualisedList = () => {\n const isTestEnv = process.env.NODE_ENV === \"test\";\n\n return (\n <Virtuoso\n ref={virtuosoRef}\n style={{ height: \"100%\" }}\n data={displayListItems}\n customScrollParent={nodeRef.current ?? undefined}\n itemContent={(index, item) => renderItem(item, index)}\n components={{\n List: VirtuosoList,\n Item: VirtuosoItem,\n }}\n context={{\n listProps: {\n id: listboxId,\n role: \"listbox\",\n \"aria-multiselectable\": multiSelect,\n },\n listItemProps: {\n role: \"none\",\n },\n }}\n // disable virtualisation in tests\n // https://github.com/petyosi/react-virtuoso/issues/26#issuecomment-1040316576\n // explicitly set the `key` prop to avoid React warning\n key={isTestEnv ? displayListItems.length : undefined}\n // omit the `initialItemCount` prop to resolve NaN error\n {...(isTestEnv\n ? {\n initialItemCount: displayListItems.length,\n }\n : {})}\n />\n );\n };\n\n const renderList = () => {\n return (\n <div\n data-testid=\"dropdown-list\"\n role=\"group\"\n aria-label={ariaLabel}\n className={styles.list}\n >\n {renderSearchInput()}\n {renderSelectAll()}\n {renderNoResults()}\n {renderLoading()}\n {renderTryAgain()}\n {renderVirtualisedList()}\n </div>\n );\n };\n\n const renderBottomCta = () => {\n if (!renderCustomCallToAction) {\n return;\n }\n\n return (\n <div data-testid=\"custom-cta\">\n {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n renderCustomCallToAction(onDismiss as any, displayListItems)\n }\n </div>\n );\n };\n\n return (\n <div\n data-testid=\"dropdown-container\"\n ref={mergeRefs(nodeRef, setFloatingRef)}\n {...getFloatingProps()}\n data-width-type={containerWidthType}\n className={clsx(\n styles.container,\n variant === \"small\" && styles.containerVariantSmall\n )}\n style={floatingStyles}\n >\n <VisuallyHidden role=\"status\">{ariaLabel}</VisuallyHidden>\n {renderList()}\n {renderBottomCta()}\n </div>\n );\n};\n\nexport const DropdownList = forwardRef(DropdownListInner);\n"],"names":["DropdownList","forwardRef","listItems","multiSelect","maxSelectable","selectedItems","disableItemFocus","itemsLoadState","itemTruncationType","itemMaxLines","labelDisplayType","variant","listboxId","ariaLabel","matchElementWidth","width","topScrollItem","onSelectItem","onSelectAll","onDismiss","onRetry","valueExtractor","listExtractor","renderListItem","renderCustomCallToAction","enableSearch","hideNoResultsDisplay","noResultsDescription","_noResultsDescription","customLabels","searchPlaceholder","_searchPlaceholder","searchFunction","onSearch","ref","noResultsLabel","selectAllButtonLabel","clearAllButtonLabel","focusedIndex","setFocusedIndex","useContext","DropdownListStateContext","elementWidth","setFloatingRef","getFloatingProps","styles","floatingStyles","useDropdownRender","searchValue","setSearchValue","useState","displayListItems","setDisplayListItems","itemsLoadStateChanged","useCompare","mounted","useIsMounted","nodeRef","useRef","listItemRefs","searchInputRef","virtuosoRef","hasSelectedMax","length","containerWidth","containerWidthType","useApplyStyle","styles.tokens","getOptionLabel","item","value","toString","title","secondaryLabel","checkListItemSelected","useCallback","find","arrItem","isEqual","filterItemsByCustomSearch","useEvent","filterItemsByLabel","filter","updatedSearchValue","trim","toLowerCase","includes","refocus","opts","current","setTimeout","_a","focus","items","undefined","index","forced","Math","max","min","scrollToIndex","align","selectedIndex","preferSelected","findIndex","nextIndex","_b","handleListItemClick","upcomingIndex","getValue","handleSearchInputChange","event","target","handleOnClear","handleTryAgain","useEventListener","code","preventDefault","document","activeElement","useImperativeHandle","useEffect","scrollTo","top","timer","indexOf","clearTimeout","_c","renderListItemIcon","selected","_jsx","SquareFillIcon","className","clsx","styles.baseCheckboxIndicatorStyle","styles.checkboxDisabledIndicator","SquareTickFillIcon","styles.checkboxSelectedIndicator","SquareIcon","styles.checkboxUnselectedIndicator","TickIcon","styles.baseIndicatorStyle","styles.selectedIndicator","renderDropdownLabel","DropdownLabel","displayType","label","maxLines","disabled","sublabel","truncationType","renderItem","active","onClick","onMouseEnter","handleListItemHover","element","role","tabIndex","styles.listItem","styles.listItemDisabled","styles.listItemActiveSelected","styles.listItemActive","children","_jsxs","_Fragment","getItemKey","renderSearchInput","DropdownSearch","onChange","placeholder","onClear","renderSelectAll","styles.selectAllContainer","BasicButton","type","styles.baseButton","styles.selectAllButton","renderNoResults","styles.resultStateContainer","ExclamationCircleFillIcon","styles.labelIcon","Markup","styles.noResultDescContainer","renderLoading","ComponentLoadingSpinner","styles.spinner","renderTryAgain","styles.tryAgainButton","renderVirtualisedList","isTestEnv","process","env","NODE_ENV","Virtuoso","Object","assign","style","height","data","customScrollParent","itemContent","components","List","VirtuosoList","Item","VirtuosoItem","context","listProps","id","listItemProps","initialItemCount","mergeRefs","styles.container","styles.containerVariantSmall","VisuallyHidden","styles.list","renderBottomCta"],"mappings":"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"}
|