entangle-ui 0.8.2 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +77 -0
- package/dist/esm/assets/src/components/controls/Combobox/Combobox.css.ts.vanilla-B7B5ttkq.css +210 -0
- package/dist/esm/assets/src/components/controls/FileUploader/FileUploader.css.ts.vanilla-T4nRiI7s.css +194 -0
- package/dist/esm/assets/src/components/controls/MultiSelect/MultiSelect.css.ts.vanilla-CdYayqaF.css +311 -0
- package/dist/esm/assets/src/components/controls/TagInput/TagInput.css.ts.vanilla-hnkMOPp1.css +141 -0
- package/dist/esm/assets/src/components/data/DataTable/DataTable.css.ts.vanilla-CmRgtjIW.css +231 -0
- package/dist/esm/assets/src/components/feedback/Alert/{Alert.css.ts.vanilla-CRAI-xHx.css → Alert.css.ts.vanilla-CfCDsIEg.css} +2 -0
- package/dist/esm/assets/src/components/feedback/CommandPalette/CommandPalette.css.ts.vanilla-DGdrLKYZ.css +160 -0
- package/dist/esm/assets/src/components/feedback/Drawer/Drawer.css.ts.vanilla-CLPTOUrA.css +247 -0
- package/dist/esm/assets/src/components/feedback/Skeleton/SkeletonLayout.css.ts.vanilla-Db7bpqiI.css +75 -0
- package/dist/esm/assets/src/components/feedback/Stat/Stat.css.ts.vanilla-GBk3JAMB.css +69 -0
- package/dist/esm/assets/src/components/layout/Card/Card.css.ts.vanilla-Ducn1gUX.css +124 -0
- package/dist/esm/assets/src/components/navigation/Pagination/Pagination.css.ts.vanilla-CmlFyyjh.css +103 -0
- package/dist/esm/assets/src/components/primitives/HoverCard/HoverCard.css.ts.vanilla-BYT0qbLp.css +41 -0
- package/dist/esm/components/Icons/CloudUploadIcon.js +24 -0
- package/dist/esm/components/Icons/CloudUploadIcon.js.map +1 -0
- package/dist/esm/components/Icons/ExternalLinkIcon.js +26 -0
- package/dist/esm/components/Icons/ExternalLinkIcon.js.map +1 -0
- package/dist/esm/components/Icons/FirstIcon.js +23 -0
- package/dist/esm/components/Icons/FirstIcon.js.map +1 -0
- package/dist/esm/components/Icons/LastIcon.js +23 -0
- package/dist/esm/components/Icons/LastIcon.js.map +1 -0
- package/dist/esm/components/Icons/UnlinkIcon.js +26 -0
- package/dist/esm/components/Icons/UnlinkIcon.js.map +1 -0
- package/dist/esm/components/controls/Combobox/Combobox.css.js +20 -0
- package/dist/esm/components/controls/Combobox/Combobox.css.js.map +1 -0
- package/dist/esm/components/controls/Combobox/Combobox.js +354 -0
- package/dist/esm/components/controls/Combobox/Combobox.js.map +1 -0
- package/dist/esm/components/controls/FileUploader/FileUploader.css.js +20 -0
- package/dist/esm/components/controls/FileUploader/FileUploader.css.js.map +1 -0
- package/dist/esm/components/controls/FileUploader/FileUploader.js +264 -0
- package/dist/esm/components/controls/FileUploader/FileUploader.js.map +1 -0
- package/dist/esm/components/controls/MultiSelect/MultiSelect.css.js +23 -0
- package/dist/esm/components/controls/MultiSelect/MultiSelect.css.js.map +1 -0
- package/dist/esm/components/controls/MultiSelect/MultiSelect.js +269 -0
- package/dist/esm/components/controls/MultiSelect/MultiSelect.js.map +1 -0
- package/dist/esm/components/controls/Select/Select.js +5 -4
- package/dist/esm/components/controls/Select/Select.js.map +1 -1
- package/dist/esm/components/controls/TagInput/TagInput.css.js +12 -0
- package/dist/esm/components/controls/TagInput/TagInput.css.js.map +1 -0
- package/dist/esm/components/controls/TagInput/TagInput.js +189 -0
- package/dist/esm/components/controls/TagInput/TagInput.js.map +1 -0
- package/dist/esm/components/controls/TreeView/TreeNode.js +87 -1
- package/dist/esm/components/controls/TreeView/TreeNode.js.map +1 -1
- package/dist/esm/components/controls/VectorInput/VectorInput.js +87 -4
- package/dist/esm/components/controls/VectorInput/VectorInput.js.map +1 -1
- package/dist/esm/components/data/DataTable/DataTable.css.js +25 -0
- package/dist/esm/components/data/DataTable/DataTable.css.js.map +1 -0
- package/dist/esm/components/data/DataTable/DataTable.js +502 -0
- package/dist/esm/components/data/DataTable/DataTable.js.map +1 -0
- package/dist/esm/components/editor/ChatPanel/ChatCodeBlock.js +87 -5
- package/dist/esm/components/editor/ChatPanel/ChatCodeBlock.js.map +1 -1
- package/dist/esm/components/editor/ChatPanel/ChatInput.js +87 -5
- package/dist/esm/components/editor/ChatPanel/ChatInput.js.map +1 -1
- package/dist/esm/components/editor/ChatPanel/ChatMessage.js +87 -2
- package/dist/esm/components/editor/ChatPanel/ChatMessage.js.map +1 -1
- package/dist/esm/components/editor/PropertyInspector/PropertyRow.js +87 -3
- package/dist/esm/components/editor/PropertyInspector/PropertyRow.js.map +1 -1
- package/dist/esm/components/editor/PropertyInspector/PropertySection.js +87 -3
- package/dist/esm/components/editor/PropertyInspector/PropertySection.js.map +1 -1
- package/dist/esm/components/feedback/Alert/Alert.css.js +1 -1
- package/dist/esm/components/feedback/Alert/Alert.js +3 -2
- package/dist/esm/components/feedback/Alert/Alert.js.map +1 -1
- package/dist/esm/components/feedback/CommandPalette/CommandPalette.css.js +20 -0
- package/dist/esm/components/feedback/CommandPalette/CommandPalette.css.js.map +1 -0
- package/dist/esm/components/feedback/CommandPalette/CommandPalette.js +261 -0
- package/dist/esm/components/feedback/CommandPalette/CommandPalette.js.map +1 -0
- package/dist/esm/components/feedback/CommandPalette/fuzzySearch.js +86 -0
- package/dist/esm/components/feedback/CommandPalette/fuzzySearch.js.map +1 -0
- package/dist/esm/components/feedback/CommandPalette/useRecentItems.js +63 -0
- package/dist/esm/components/feedback/CommandPalette/useRecentItems.js.map +1 -0
- package/dist/esm/components/feedback/Dialog/DialogHeader.js +2 -1
- package/dist/esm/components/feedback/Dialog/DialogHeader.js.map +1 -1
- package/dist/esm/components/feedback/Drawer/Drawer.css.js +17 -0
- package/dist/esm/components/feedback/Drawer/Drawer.css.js.map +1 -0
- package/dist/esm/components/feedback/Drawer/Drawer.js +120 -0
- package/dist/esm/components/feedback/Drawer/Drawer.js.map +1 -0
- package/dist/esm/components/feedback/Drawer/useDrawerAnimation.js +74 -0
- package/dist/esm/components/feedback/Drawer/useDrawerAnimation.js.map +1 -0
- package/dist/esm/components/feedback/Skeleton/SkeletonLayout.css.js +18 -0
- package/dist/esm/components/feedback/Skeleton/SkeletonLayout.css.js.map +1 -0
- package/dist/esm/components/feedback/Skeleton/SkeletonLayout.js +95 -0
- package/dist/esm/components/feedback/Skeleton/SkeletonLayout.js.map +1 -0
- package/dist/esm/components/feedback/Stat/Stat.css.js +15 -0
- package/dist/esm/components/feedback/Stat/Stat.css.js.map +1 -0
- package/dist/esm/components/feedback/Stat/Stat.js +55 -0
- package/dist/esm/components/feedback/Stat/Stat.js.map +1 -0
- package/dist/esm/components/feedback/Toast/ToastItem.js +12 -15
- package/dist/esm/components/feedback/Toast/ToastItem.js.map +1 -1
- package/dist/esm/components/layout/Accordion/Accordion.js +2 -1
- package/dist/esm/components/layout/Accordion/Accordion.js.map +1 -1
- package/dist/esm/components/layout/Accordion/AccordionTrigger.js +2 -3
- package/dist/esm/components/layout/Accordion/AccordionTrigger.js.map +1 -1
- package/dist/esm/components/layout/Card/Card.css.js +18 -0
- package/dist/esm/components/layout/Card/Card.css.js.map +1 -0
- package/dist/esm/components/layout/Card/Card.js +66 -0
- package/dist/esm/components/layout/Card/Card.js.map +1 -0
- package/dist/esm/components/navigation/Breadcrumbs/BreadcrumbEllipsis.js +1 -0
- package/dist/esm/components/navigation/Breadcrumbs/BreadcrumbEllipsis.js.map +1 -1
- package/dist/esm/components/navigation/Breadcrumbs/BreadcrumbItem.js +1 -0
- package/dist/esm/components/navigation/Breadcrumbs/BreadcrumbItem.js.map +1 -1
- package/dist/esm/components/navigation/Breadcrumbs/Breadcrumbs.js +5 -0
- package/dist/esm/components/navigation/Breadcrumbs/Breadcrumbs.js.map +1 -1
- package/dist/esm/components/navigation/Pagination/Pagination.css.js +12 -0
- package/dist/esm/components/navigation/Pagination/Pagination.css.js.map +1 -0
- package/dist/esm/components/navigation/Pagination/Pagination.js +107 -0
- package/dist/esm/components/navigation/Pagination/Pagination.js.map +1 -0
- package/dist/esm/components/navigation/Pagination/usePagination.js +143 -0
- package/dist/esm/components/navigation/Pagination/usePagination.js.map +1 -0
- package/dist/esm/components/primitives/Avatar/Avatar.js +87 -1
- package/dist/esm/components/primitives/Avatar/Avatar.js.map +1 -1
- package/dist/esm/components/primitives/Badge/Badge.js +87 -1
- package/dist/esm/components/primitives/Badge/Badge.js.map +1 -1
- package/dist/esm/components/primitives/Checkbox/Checkbox.js +5 -2
- package/dist/esm/components/primitives/Checkbox/Checkbox.js.map +1 -1
- package/dist/esm/components/primitives/Collapsible/Collapsible.js +2 -3
- package/dist/esm/components/primitives/Collapsible/Collapsible.js.map +1 -1
- package/dist/esm/components/primitives/HoverCard/HoverCard.css.js +7 -0
- package/dist/esm/components/primitives/HoverCard/HoverCard.css.js.map +1 -0
- package/dist/esm/components/primitives/HoverCard/HoverCard.js +169 -0
- package/dist/esm/components/primitives/HoverCard/HoverCard.js.map +1 -0
- package/dist/esm/components/primitives/Icon/Icon.js +16 -2
- package/dist/esm/components/primitives/Icon/Icon.js.map +1 -1
- package/dist/esm/components/primitives/Link/Link.js +3 -3
- package/dist/esm/components/primitives/Link/Link.js.map +1 -1
- package/dist/esm/components/primitives/Popover/PopoverClose.js +2 -3
- package/dist/esm/components/primitives/Popover/PopoverClose.js.map +1 -1
- package/dist/esm/components/primitives/Radio/Radio.js +1 -1
- package/dist/esm/hooks/useBreakpoint/useBreakpoint.js +44 -0
- package/dist/esm/hooks/useBreakpoint/useBreakpoint.js.map +1 -0
- package/dist/esm/hooks/useDebounced/useDebouncedCallback.js +97 -0
- package/dist/esm/hooks/useDebounced/useDebouncedCallback.js.map +1 -0
- package/dist/esm/hooks/useDebounced/useDebouncedValue.js +35 -0
- package/dist/esm/hooks/useDebounced/useDebouncedValue.js.map +1 -0
- package/dist/esm/hooks/useIntersectionObserver/useIntersectionObserver.js +73 -0
- package/dist/esm/hooks/useIntersectionObserver/useIntersectionObserver.js.map +1 -0
- package/dist/esm/hooks/useListboxNav/useListboxNav.js +181 -0
- package/dist/esm/hooks/useListboxNav/useListboxNav.js.map +1 -0
- package/dist/esm/hooks/useMediaQuery/useMediaQuery.js +54 -0
- package/dist/esm/hooks/useMediaQuery/useMediaQuery.js.map +1 -0
- package/dist/esm/hooks/useThrottledCallback/useThrottledCallback.js +78 -0
- package/dist/esm/hooks/useThrottledCallback/useThrottledCallback.js.map +1 -0
- package/dist/esm/index.js +25 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/theme/breakpoints.js +27 -0
- package/dist/esm/theme/breakpoints.js.map +1 -0
- package/dist/esm/theme/index.js +1 -0
- package/dist/esm/theme/index.js.map +1 -1
- package/dist/tokens/tokens.dark.css +1 -1
- package/dist/tokens/tokens.json +1 -1
- package/dist/tokens/tokens.light.css +1 -1
- package/dist/types/components/Icons/CloudUploadIcon.d.ts +27 -0
- package/dist/types/components/Icons/ExternalLinkIcon.d.ts +29 -0
- package/dist/types/components/Icons/FirstIcon.d.ts +26 -0
- package/dist/types/components/Icons/LastIcon.d.ts +26 -0
- package/dist/types/components/Icons/UnlinkIcon.d.ts +29 -0
- package/dist/types/components/controls/Combobox/Combobox.d.ts +29 -0
- package/dist/types/components/controls/Combobox/Combobox.types.d.ts +109 -0
- package/dist/types/components/controls/FileUploader/FileUploader.d.ts +34 -0
- package/dist/types/components/controls/FileUploader/FileUploader.types.d.ts +94 -0
- package/dist/types/components/controls/MultiSelect/MultiSelect.d.ts +31 -0
- package/dist/types/components/controls/MultiSelect/MultiSelect.types.d.ts +85 -0
- package/dist/types/components/controls/TagInput/TagInput.d.ts +24 -0
- package/dist/types/components/controls/TagInput/TagInput.types.d.ts +100 -0
- package/dist/types/components/data/DataTable/DataTable.d.ts +8 -0
- package/dist/types/components/data/DataTable/DataTable.types.d.ts +159 -0
- package/dist/types/components/feedback/Alert/Alert.d.ts +1 -0
- package/dist/types/components/feedback/Alert/Alert.types.d.ts +7 -0
- package/dist/types/components/feedback/CommandPalette/CommandPalette.d.ts +29 -0
- package/dist/types/components/feedback/CommandPalette/CommandPalette.types.d.ts +61 -0
- package/dist/types/components/feedback/CommandPalette/fuzzySearch.d.ts +6 -0
- package/dist/types/components/feedback/Drawer/Drawer.d.ts +12 -0
- package/dist/types/components/feedback/Drawer/Drawer.types.d.ts +70 -0
- package/dist/types/components/feedback/Skeleton/Skeleton.types.d.ts +44 -1
- package/dist/types/components/feedback/Skeleton/SkeletonLayout.d.ts +314 -0
- package/dist/types/components/feedback/Stat/Stat.d.ts +23 -0
- package/dist/types/components/feedback/Stat/Stat.types.d.ts +38 -0
- package/dist/types/components/layout/Accordion/Accordion.types.d.ts +7 -0
- package/dist/types/components/layout/Card/Card.d.ts +12 -0
- package/dist/types/components/layout/Card/Card.types.d.ts +54 -0
- package/dist/types/components/navigation/Pagination/Pagination.d.ts +22 -0
- package/dist/types/components/navigation/Pagination/Pagination.types.d.ts +49 -0
- package/dist/types/components/primitives/Button/Button.d.ts +1 -1
- package/dist/types/components/primitives/HoverCard/HoverCard.d.ts +10 -0
- package/dist/types/components/primitives/HoverCard/HoverCard.types.d.ts +64 -0
- package/dist/types/components/primitives/Icon/Icon.d.ts +14 -1
- package/dist/types/components/primitives/IconButton/IconButton.d.ts +1 -1
- package/dist/types/hooks/useBreakpoint/useBreakpoint.d.ts +19 -0
- package/dist/types/hooks/useBreakpoint/useBreakpoint.types.d.ts +20 -0
- package/dist/types/hooks/useDebounced/useDebounced.types.d.ts +15 -0
- package/dist/types/hooks/useDebounced/useDebouncedCallback.d.ts +22 -0
- package/dist/types/hooks/useDebounced/useDebouncedValue.d.ts +16 -0
- package/dist/types/hooks/useIntersectionObserver/useIntersectionObserver.d.ts +22 -0
- package/dist/types/hooks/useIntersectionObserver/useIntersectionObserver.types.d.ts +22 -0
- package/dist/types/hooks/useListboxNav/useListboxNav.d.ts +75 -0
- package/dist/types/hooks/useMediaQuery/useMediaQuery.d.ts +19 -0
- package/dist/types/hooks/useMediaQuery/useMediaQuery.types.d.ts +6 -0
- package/dist/types/hooks/useThrottledCallback/useThrottledCallback.d.ts +23 -0
- package/dist/types/hooks/useThrottledCallback/useThrottledCallback.types.d.ts +13 -0
- package/dist/types/index.d.ts +43 -1
- package/dist/types/theme/breakpoints.d.ts +22 -0
- package/dist/types/theme/index.d.ts +1 -0
- package/package.json +3 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Combobox.js","sources":["../../../../../../src/components/controls/Combobox/Combobox.tsx"],"sourcesContent":[null],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AA6CA;AACE;AACA;AACA;;AAGF;AAOA;AAIE;AACA;AACF;AAEA;AAKE;AAAY;;AAGZ;AACE;;;AAGE;;;;;;;;AAMA;;;;;AAIJ;AACA;AACF;AAEA;;;;;;;;;;;;;;;;;;;AAmBG;AACG;AAqCJ;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAIA;;;;AAIE;AACD;AAED;;AAEsB;AAClB;;AAEF;AAIF;;;;AAIA;;;;AAKE;AACE;;AAEJ;;AAGA;;;;;AAcA;AACE;AAAY;AACZ;;AAEF;AAEA;;;AAKA;AACE;;;AAGI;AACA;AACD;;AAEH;;AAGF;;;AAEE;;;AAEA;;AAGF;AACE;;;AAEA;AACF;AAEA;;;AAGI;;AAEE;;;;;;AAGA;;AAEF;AACA;AAEI;AACA;AAEJ;;;AAMF;;AAEA;AACA;AACD;AAED;AACE;AACA;;AACA;;AAEA;AAGA;;AAGI;AAEJ;;AAEE;AACA;AACE;;AAEI;AACD;AACH;AACH;AACH;;AAGE;;AACA;AACF;;;AAIE;;AACA;AACE;;;AAKE;;AAEJ;AACA;AACA;AACE;AACF;AACF;AAEA;AAEI;AACA;;AAEA;AACA;AAAa;;;;;AAKf;AAIF;;;AAII;;;AAGI;;;;AAKJ;;AAEE;;AAEE;AACE;;;;;;;;AAKF;AACA;;;;;;AAOF;;;;AAGE;;;;AAKJ;AACF;;;;;;;;;;;;;AAcC;AAGH;;AAEE;AAAiB;AACnB;AAEA;;AAEE;;;AAGE;;;AAIJ;;;;;AAMI;AACA;AACF;AAIF;;;AAEE;AAAY;;AACP;AACL;AACF;AAEA;AACA;;;AAII;;AAMF;AACE;;;AAGA;;AAEA;AACA;AACA;AASQ;AACA;AACA;;;AAMJ;AAEE;AAAiB;AACnB;;AAMI;AAYV;AACF;AAEA;AACA;;;;;;;AAuBO;;AAaK;;;AAsDN;AA8BR;AAEA;;"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import './../../../assets/src/components/controls/FileUploader/FileUploader.css.ts.vanilla-T4nRiI7s.css';
|
|
2
|
+
import { createRuntimeFn } from '@vanilla-extract/recipes/createRuntimeFn';
|
|
3
|
+
|
|
4
|
+
var containerStyle = 'FileUploader_containerStyle__yf371x0';
|
|
5
|
+
var dropZoneHintStyle = 'FileUploader_dropZoneHintStyle__yf371xc';
|
|
6
|
+
var dropZonePrimaryStyle = 'FileUploader_dropZonePrimaryStyle__yf371xb';
|
|
7
|
+
var dropZoneRecipe = createRuntimeFn({defaultClassName:'FileUploader_dropZoneRecipe__yf371x1',variantClassNames:{size:{sm:'FileUploader_dropZoneRecipe_size_sm__yf371x2',md:'FileUploader_dropZoneRecipe_size_md__yf371x3',lg:'FileUploader_dropZoneRecipe_size_lg__yf371x4'},dragging:{true:'FileUploader_dropZoneRecipe_dragging_true__yf371x5',false:'FileUploader_dropZoneRecipe_dragging_false__yf371x6'},disabled:{true:'FileUploader_dropZoneRecipe_disabled_true__yf371x7',false:'FileUploader_dropZoneRecipe_disabled_false__yf371x8'},error:{true:'FileUploader_dropZoneRecipe_error_true__yf371x9',false:'FileUploader_dropZoneRecipe_error_false__yf371xa'}},defaultVariants:{size:'md',dragging:false,disabled:false,error:false},compoundVariants:[]});
|
|
8
|
+
var fileListStyle = 'FileUploader_fileListStyle__yf371xe';
|
|
9
|
+
var fileMetaStyle = 'FileUploader_fileMetaStyle__yf371xk';
|
|
10
|
+
var fileNameStyle = 'FileUploader_fileNameStyle__yf371xl';
|
|
11
|
+
var fileRowRecipe = createRuntimeFn({defaultClassName:'FileUploader_fileRowRecipe__yf371xf',variantClassNames:{status:{pending:'FileUploader_fileRowRecipe_status_pending__yf371xg',uploading:'FileUploader_fileRowRecipe_status_uploading__yf371xh',done:'FileUploader_fileRowRecipe_status_done__yf371xi',error:'FileUploader_fileRowRecipe_status_error__yf371xj'}},defaultVariants:{status:'pending'},compoundVariants:[]});
|
|
12
|
+
var fileSubStyle = 'FileUploader_fileSubStyle__yf371xm';
|
|
13
|
+
var hiddenInputStyle = 'FileUploader_hiddenInputStyle__yf371xd';
|
|
14
|
+
var progressBarStyle = 'FileUploader_progressBarStyle__yf371xn';
|
|
15
|
+
var progressFillRecipe = createRuntimeFn({defaultClassName:'FileUploader_progressFillRecipe__yf371xo',variantClassNames:{status:{pending:'FileUploader_progressFillRecipe_status_pending__yf371xp',uploading:'FileUploader_progressFillRecipe_status_uploading__yf371xq',done:'FileUploader_progressFillRecipe_status_done__yf371xr',error:'FileUploader_progressFillRecipe_status_error__yf371xs'}},defaultVariants:{status:'uploading'},compoundVariants:[]});
|
|
16
|
+
var removeButtonStyle = 'FileUploader_removeButtonStyle__yf371xt';
|
|
17
|
+
var statusBadgeRecipe = createRuntimeFn({defaultClassName:'FileUploader_statusBadgeRecipe__yf371xu',variantClassNames:{status:{pending:'FileUploader_statusBadgeRecipe_status_pending__yf371xv',uploading:'FileUploader_statusBadgeRecipe_status_uploading__yf371xw',done:'FileUploader_statusBadgeRecipe_status_done__yf371xx',error:'FileUploader_statusBadgeRecipe_status_error__yf371xy'}},defaultVariants:{status:'pending'},compoundVariants:[]});
|
|
18
|
+
|
|
19
|
+
export { containerStyle, dropZoneHintStyle, dropZonePrimaryStyle, dropZoneRecipe, fileListStyle, fileMetaStyle, fileNameStyle, fileRowRecipe, fileSubStyle, hiddenInputStyle, progressBarStyle, progressFillRecipe, removeButtonStyle, statusBadgeRecipe };
|
|
20
|
+
//# sourceMappingURL=FileUploader.css.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FileUploader.css.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
|
+
import React, { useId, useRef, useState, useCallback, useMemo } from 'react';
|
|
4
|
+
import { CloudUploadIcon } from '../../Icons/CloudUploadIcon.js';
|
|
5
|
+
import { TrashIcon } from '../../Icons/TrashIcon.js';
|
|
6
|
+
import { FormHelperText } from '../../form/FormHelperText.js';
|
|
7
|
+
import { FormLabel } from '../../form/FormLabel.js';
|
|
8
|
+
import { useControlledState } from '../../../hooks/useControlledState/useControlledState.js';
|
|
9
|
+
import { useMergedRef } from '../../../hooks/useMergedRef/useMergedRef.js';
|
|
10
|
+
import { cx } from '../../../utils/cx.js';
|
|
11
|
+
import { dropZonePrimaryStyle, dropZoneHintStyle, hiddenInputStyle, dropZoneRecipe, fileListStyle, containerStyle, fileMetaStyle, fileNameStyle, fileSubStyle, statusBadgeRecipe, progressBarStyle, progressFillRecipe, removeButtonStyle, fileRowRecipe } from './FileUploader.css.js';
|
|
12
|
+
|
|
13
|
+
let __fileUploaderIdCounter = 0;
|
|
14
|
+
function nextFileId() {
|
|
15
|
+
__fileUploaderIdCounter += 1;
|
|
16
|
+
return `eui-file-${String(Date.now())}-${String(__fileUploaderIdCounter)}`;
|
|
17
|
+
}
|
|
18
|
+
const SIZE_UNITS = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
19
|
+
function formatBytes(bytes) {
|
|
20
|
+
if (!Number.isFinite(bytes) || bytes < 0)
|
|
21
|
+
return '';
|
|
22
|
+
if (bytes === 0)
|
|
23
|
+
return '0 B';
|
|
24
|
+
const exponent = Math.min(SIZE_UNITS.length - 1, Math.floor(Math.log(bytes) / Math.log(1024)));
|
|
25
|
+
const value = bytes / Math.pow(1024, exponent);
|
|
26
|
+
const formatted = value >= 100 || exponent === 0 ? value.toFixed(0) : value.toFixed(1);
|
|
27
|
+
return `${formatted} ${SIZE_UNITS[exponent] ?? ''}`;
|
|
28
|
+
}
|
|
29
|
+
function fileMatchesAccept(file, accept) {
|
|
30
|
+
const tokens = accept
|
|
31
|
+
.split(',')
|
|
32
|
+
.map(t => t.trim().toLowerCase())
|
|
33
|
+
.filter(Boolean);
|
|
34
|
+
if (tokens.length === 0)
|
|
35
|
+
return true;
|
|
36
|
+
const name = file.name.toLowerCase();
|
|
37
|
+
const type = file.type.toLowerCase();
|
|
38
|
+
for (const token of tokens) {
|
|
39
|
+
if (token.startsWith('.')) {
|
|
40
|
+
if (name.endsWith(token))
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
else if (token.endsWith('/*')) {
|
|
44
|
+
const prefix = token.slice(0, -2);
|
|
45
|
+
if (type.startsWith(`${prefix}/`))
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
else if (token === type) {
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Drag-and-drop file uploader with click-to-browse fallback. Renders a list
|
|
56
|
+
* of uploaded items with per-file status (`pending` / `uploading` / `done` /
|
|
57
|
+
* `error`) and an animated progress bar.
|
|
58
|
+
*
|
|
59
|
+
* The component itself is purely presentational around the file list — it
|
|
60
|
+
* does not perform the actual upload. The consumer is expected to:
|
|
61
|
+
* 1. Listen to `onFilesAdd` (or `onChange`) to know which files were
|
|
62
|
+
* accepted by the uploader.
|
|
63
|
+
* 2. Drive the upload (fetch / XHR / SDK) externally.
|
|
64
|
+
* 3. Reflect progress back into the component by passing a controlled
|
|
65
|
+
* `value` and updating the `status` / `progress` of each item.
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```tsx
|
|
69
|
+
* const [items, setItems] = useState<FileUploaderItem[]>([]);
|
|
70
|
+
* <FileUploader
|
|
71
|
+
* value={items}
|
|
72
|
+
* onChange={setItems}
|
|
73
|
+
* accept="image/*,.pdf"
|
|
74
|
+
* maxSize={5 * 1024 * 1024}
|
|
75
|
+
* onFilesAdd={files => uploadAll(files, setItems)}
|
|
76
|
+
* />
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
function FileUploader({ value, defaultValue, onChange, onFilesAdd, onFileRemove, onReject, accept, multiple = true, maxFiles, maxSize, minSize, validate, renderItem, size = 'md', label, helperText, error = false, errorMessage, disabled = false, required = false, dropZoneText = 'Drag files here or click to browse', dropZoneHint, name, className, style, testId, id: idProp, ref, ...rest }) {
|
|
80
|
+
const autoId = useId();
|
|
81
|
+
const fieldId = idProp ?? autoId;
|
|
82
|
+
const labelId = `${fieldId}-label`;
|
|
83
|
+
const helperId = `${fieldId}-helper`;
|
|
84
|
+
const inputRef = useRef(null);
|
|
85
|
+
const dropZoneRef = useRef(null);
|
|
86
|
+
const containerRef = useRef(null);
|
|
87
|
+
const setContainerRef = useMergedRef(containerRef, ref);
|
|
88
|
+
const [items, setItems] = useControlledState({
|
|
89
|
+
value,
|
|
90
|
+
defaultValue,
|
|
91
|
+
onChange,
|
|
92
|
+
fallback: [],
|
|
93
|
+
});
|
|
94
|
+
const [dragging, setDragging] = useState(false);
|
|
95
|
+
const dragCounterRef = useRef(0);
|
|
96
|
+
const validateFile = useCallback((file, currentCount) => {
|
|
97
|
+
if (typeof maxFiles === 'number' && currentCount >= maxFiles) {
|
|
98
|
+
return 'max-files';
|
|
99
|
+
}
|
|
100
|
+
if (accept && !fileMatchesAccept(file, accept)) {
|
|
101
|
+
return 'wrong-type';
|
|
102
|
+
}
|
|
103
|
+
if (typeof maxSize === 'number' && file.size > maxSize) {
|
|
104
|
+
return 'too-large';
|
|
105
|
+
}
|
|
106
|
+
if (typeof minSize === 'number' && file.size < minSize) {
|
|
107
|
+
return 'too-small';
|
|
108
|
+
}
|
|
109
|
+
if (validate) {
|
|
110
|
+
const result = validate(file);
|
|
111
|
+
if (result === false)
|
|
112
|
+
return 'invalid';
|
|
113
|
+
if (typeof result === 'string')
|
|
114
|
+
return result;
|
|
115
|
+
}
|
|
116
|
+
return null;
|
|
117
|
+
}, [accept, maxFiles, maxSize, minSize, validate]);
|
|
118
|
+
const acceptFiles = useCallback((incoming) => {
|
|
119
|
+
if (incoming.length === 0)
|
|
120
|
+
return;
|
|
121
|
+
const accepted = [];
|
|
122
|
+
const newItems = [];
|
|
123
|
+
let count = items.length;
|
|
124
|
+
const list = multiple ? incoming : incoming.slice(0, 1);
|
|
125
|
+
for (const file of list) {
|
|
126
|
+
const rejection = validateFile(file, count);
|
|
127
|
+
if (rejection) {
|
|
128
|
+
onReject?.(file, rejection);
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
accepted.push(file);
|
|
132
|
+
newItems.push({
|
|
133
|
+
id: nextFileId(),
|
|
134
|
+
file,
|
|
135
|
+
status: 'pending',
|
|
136
|
+
});
|
|
137
|
+
count += 1;
|
|
138
|
+
}
|
|
139
|
+
if (newItems.length === 0)
|
|
140
|
+
return;
|
|
141
|
+
// In single mode, replace any existing item.
|
|
142
|
+
const next = multiple ? [...items, ...newItems] : newItems;
|
|
143
|
+
setItems(next);
|
|
144
|
+
onFilesAdd?.(accepted);
|
|
145
|
+
}, [items, multiple, onFilesAdd, onReject, setItems, validateFile]);
|
|
146
|
+
const removeAt = useCallback((index) => {
|
|
147
|
+
if (index < 0 || index >= items.length)
|
|
148
|
+
return;
|
|
149
|
+
const removed = items[index];
|
|
150
|
+
const next = items.slice(0, index).concat(items.slice(index + 1));
|
|
151
|
+
setItems(next);
|
|
152
|
+
if (removed)
|
|
153
|
+
onFileRemove?.(removed);
|
|
154
|
+
}, [items, onFileRemove, setItems]);
|
|
155
|
+
const handleInputChange = useCallback((event) => {
|
|
156
|
+
const list = event.target.files;
|
|
157
|
+
if (!list)
|
|
158
|
+
return;
|
|
159
|
+
acceptFiles(Array.from(list));
|
|
160
|
+
// Reset so selecting the same file again still fires onChange.
|
|
161
|
+
event.target.value = '';
|
|
162
|
+
}, [acceptFiles]);
|
|
163
|
+
const openPicker = useCallback(() => {
|
|
164
|
+
if (disabled)
|
|
165
|
+
return;
|
|
166
|
+
inputRef.current?.click();
|
|
167
|
+
}, [disabled]);
|
|
168
|
+
const handleZoneKeyDown = useCallback((event) => {
|
|
169
|
+
if (disabled)
|
|
170
|
+
return;
|
|
171
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
172
|
+
event.preventDefault();
|
|
173
|
+
openPicker();
|
|
174
|
+
}
|
|
175
|
+
}, [disabled, openPicker]);
|
|
176
|
+
const handleDragEnter = useCallback((event) => {
|
|
177
|
+
if (disabled)
|
|
178
|
+
return;
|
|
179
|
+
event.preventDefault();
|
|
180
|
+
dragCounterRef.current += 1;
|
|
181
|
+
if (dragCounterRef.current === 1)
|
|
182
|
+
setDragging(true);
|
|
183
|
+
}, [disabled]);
|
|
184
|
+
const handleDragOver = useCallback((event) => {
|
|
185
|
+
if (disabled)
|
|
186
|
+
return;
|
|
187
|
+
event.preventDefault();
|
|
188
|
+
if (event.dataTransfer)
|
|
189
|
+
event.dataTransfer.dropEffect = 'copy';
|
|
190
|
+
}, [disabled]);
|
|
191
|
+
const handleDragLeave = useCallback((event) => {
|
|
192
|
+
if (disabled)
|
|
193
|
+
return;
|
|
194
|
+
event.preventDefault();
|
|
195
|
+
dragCounterRef.current = Math.max(0, dragCounterRef.current - 1);
|
|
196
|
+
if (dragCounterRef.current === 0)
|
|
197
|
+
setDragging(false);
|
|
198
|
+
}, [disabled]);
|
|
199
|
+
const handleDrop = useCallback((event) => {
|
|
200
|
+
if (disabled)
|
|
201
|
+
return;
|
|
202
|
+
event.preventDefault();
|
|
203
|
+
dragCounterRef.current = 0;
|
|
204
|
+
setDragging(false);
|
|
205
|
+
const dt = event.dataTransfer;
|
|
206
|
+
if (!dt)
|
|
207
|
+
return;
|
|
208
|
+
const dropped = [];
|
|
209
|
+
if (dt.files.length > 0) {
|
|
210
|
+
dropped.push(...Array.from(dt.files));
|
|
211
|
+
}
|
|
212
|
+
else if (dt.items.length > 0) {
|
|
213
|
+
for (const item of Array.from(dt.items)) {
|
|
214
|
+
if (item.kind === 'file') {
|
|
215
|
+
const f = item.getAsFile();
|
|
216
|
+
if (f)
|
|
217
|
+
dropped.push(f);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
acceptFiles(dropped);
|
|
222
|
+
}, [acceptFiles, disabled]);
|
|
223
|
+
const showHelperText = error && errorMessage ? errorMessage : helperText;
|
|
224
|
+
const renderRow = (item, index) => {
|
|
225
|
+
if (renderItem) {
|
|
226
|
+
return (jsx(React.Fragment, { children: renderItem({
|
|
227
|
+
item,
|
|
228
|
+
index,
|
|
229
|
+
disabled,
|
|
230
|
+
remove: () => {
|
|
231
|
+
removeAt(index);
|
|
232
|
+
},
|
|
233
|
+
}) }, item.id));
|
|
234
|
+
}
|
|
235
|
+
const progress = Math.max(0, Math.min(100, item.progress ?? 0));
|
|
236
|
+
const sub = item.errorMessage ?? formatBytes(item.file.size);
|
|
237
|
+
return (jsxs("div", { className: fileRowRecipe({ status: item.status }), "data-testid": testId ? `${testId}-item-${item.id}` : undefined, children: [jsxs("div", { className: fileMetaStyle, children: [jsx("span", { className: fileNameStyle, title: item.file.name, children: item.file.name }), jsxs("span", { className: fileSubStyle, children: [jsx("span", { className: statusBadgeRecipe({ status: item.status }), children: item.status }), jsx("span", { children: sub })] }), (item.status === 'uploading' ||
|
|
238
|
+
(item.status === 'pending' && progress > 0)) && (jsx("div", { className: progressBarStyle, "aria-hidden": "true", children: jsx("div", { className: progressFillRecipe({ status: item.status }), style: { width: `${String(progress)}%` } }) }))] }), jsx("button", { type: "button", className: removeButtonStyle, "aria-label": `Remove ${item.file.name}`, disabled: disabled, onClick: () => {
|
|
239
|
+
removeAt(index);
|
|
240
|
+
}, children: jsx(TrashIcon, { size: "sm", decorative: true }) })] }, item.id));
|
|
241
|
+
};
|
|
242
|
+
const acceptHint = useMemo(() => {
|
|
243
|
+
if (dropZoneHint !== undefined)
|
|
244
|
+
return dropZoneHint;
|
|
245
|
+
const parts = [];
|
|
246
|
+
if (accept)
|
|
247
|
+
parts.push(accept);
|
|
248
|
+
if (typeof maxSize === 'number')
|
|
249
|
+
parts.push(`up to ${formatBytes(maxSize)}`);
|
|
250
|
+
if (typeof maxFiles === 'number')
|
|
251
|
+
parts.push(`max ${String(maxFiles)} files`);
|
|
252
|
+
return parts.length > 0 ? parts.join(' • ') : null;
|
|
253
|
+
}, [accept, dropZoneHint, maxFiles, maxSize]);
|
|
254
|
+
return (jsxs("div", { ref: setContainerRef, className: cx(containerStyle, className), style: style, "data-testid": testId, ...rest, children: [label && (jsx(FormLabel, { id: labelId, htmlFor: fieldId, required: required, disabled: disabled, children: label })), jsxs("div", { ref: dropZoneRef, role: "button", tabIndex: disabled ? -1 : 0, "aria-disabled": disabled || undefined, "aria-labelledby": label ? labelId : undefined, "aria-describedby": showHelperText ? helperId : undefined, className: dropZoneRecipe({
|
|
255
|
+
size,
|
|
256
|
+
dragging,
|
|
257
|
+
disabled,
|
|
258
|
+
error,
|
|
259
|
+
}), onClick: openPicker, onKeyDown: handleZoneKeyDown, onDragEnter: handleDragEnter, onDragOver: handleDragOver, onDragLeave: handleDragLeave, onDrop: handleDrop, "data-testid": testId ? `${testId}-dropzone` : undefined, children: [jsx(CloudUploadIcon, { size: size === 'lg' ? 32 : size === 'sm' ? 18 : 24, decorative: true }), jsx("span", { className: dropZonePrimaryStyle, children: dropZoneText }), acceptHint && jsx("span", { className: dropZoneHintStyle, children: acceptHint }), jsx("input", { ref: inputRef, id: fieldId, type: "file", name: name, accept: accept, multiple: multiple, required: required, disabled: disabled, onChange: handleInputChange, className: hiddenInputStyle, "aria-hidden": "true", tabIndex: -1, "data-testid": testId ? `${testId}-input` : undefined })] }), items.length > 0 && (jsx("div", { className: fileListStyle, children: items.map((item, idx) => renderRow(item, idx)) })), showHelperText && (jsx(FormHelperText, { id: helperId, error: error, children: showHelperText }))] }));
|
|
260
|
+
}
|
|
261
|
+
FileUploader.displayName = 'FileUploader';
|
|
262
|
+
|
|
263
|
+
export { FileUploader };
|
|
264
|
+
//# sourceMappingURL=FileUploader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FileUploader.js","sources":["../../../../../../src/components/controls/FileUploader/FileUploader.tsx"],"sourcesContent":[null],"names":[],"mappings":";;;;;;;;;;;;AAgCA;AACA;;AAEE;AACF;AAEA;AAEA;;AAC4C;;AACzB;AACjB;AAIA;;;AAIF;AAEA;;;AAGK;;AAEH;AAAyB;;;AAKzB;AACE;AACE;AAA0B;;AACrB;;AAEL;AAAmC;;AAC9B;AACL;;;AAGJ;AACF;AAEA;;;;;;;;;;;;;;;;;;;;;;;;AAwBG;;AAgCD;AACA;AACA;AACA;AAEA;AACA;AACA;;AAGA;;;;AAIE;AACD;;AAGD;;;AAKM;;;AAGA;;;AAGA;;;AAGA;;;AAGA;;AACsB;;AACU;;AAElC;AACF;AAIF;AAEI;;;;AAGA;AAEA;AAEA;;;AAGI;;;AAGF;;;;AAIE;AACD;;;AAIH;;;AAGA;;AAEA;AACF;AAIF;;;AAGI;;;AAGA;AAAa;;AAKjB;AAEI;AACA;;;;AAGA;AACF;AAIF;AACE;;AACA;AACF;AAEA;AAEI;;AACA;;AAEE;;AAEJ;AAIF;AAEI;;;AAEA;AACA;;AACF;AAIF;AAEI;;;;AAEwB;AAC1B;AAIF;AAEI;;;AAEA;AACA;;AACF;AAIF;AAEI;;;AAEA;;AAEA;AACA;;;;AAGE;;;AAEA;AACE;AACE;AACA;AAAO;;;;;AAKf;AAIF;AAEA;;AAEI;;;;;;;AASK;;;AAMP;AAEA;AAiBQ;;AAgBF;AAMR;AAEA;;AACkC;;AAEhC;AAAY;;;;;AAKZ;;;;;;;;AAgFJ;AAEA;;"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import './../../../assets/src/components/controls/MultiSelect/MultiSelect.css.ts.vanilla-CdYayqaF.css';
|
|
2
|
+
import { createRuntimeFn } from '@vanilla-extract/recipes/createRuntimeFn';
|
|
3
|
+
|
|
4
|
+
var checkboxRecipe = createRuntimeFn({defaultClassName:'MultiSelect_checkboxRecipe__1a6uxqy1e',variantClassNames:{selected:{true:'MultiSelect_checkboxRecipe_selected_true__1a6uxqy1f',false:'MultiSelect_checkboxRecipe_selected_false__1a6uxqy1g'}},defaultVariants:{selected:false},compoundVariants:[]});
|
|
5
|
+
var chevronRecipe = createRuntimeFn({defaultClassName:'MultiSelect_chevronRecipe__1a6uxqyy',variantClassNames:{open:{true:'MultiSelect_chevronRecipe_open_true__1a6uxqyz',false:'MultiSelect_chevronRecipe_open_false__1a6uxqy10'}},defaultVariants:{open:false},compoundVariants:[]});
|
|
6
|
+
var chipLabelStyle = 'MultiSelect_chipLabelStyle__1a6uxqyt';
|
|
7
|
+
var chipRecipe = createRuntimeFn({defaultClassName:'MultiSelect_chipRecipe__1a6uxqyp',variantClassNames:{size:{sm:'MultiSelect_chipRecipe_size_sm__1a6uxqyq',md:'MultiSelect_chipRecipe_size_md__1a6uxqyr',lg:'MultiSelect_chipRecipe_size_lg__1a6uxqys'}},defaultVariants:{size:'md'},compoundVariants:[]});
|
|
8
|
+
var chipRemoveStyle = 'MultiSelect_chipRemoveStyle__1a6uxqyu';
|
|
9
|
+
var clearButtonStyle = 'MultiSelect_clearButtonStyle__1a6uxqyx';
|
|
10
|
+
var containerStyle = 'MultiSelect_containerStyle__1a6uxqy0';
|
|
11
|
+
var dropdownStyle = 'MultiSelect_dropdownStyle__1a6uxqy12';
|
|
12
|
+
var emptyMessageStyle = 'MultiSelect_emptyMessageStyle__1a6uxqy1d';
|
|
13
|
+
var groupLabelStyle = 'MultiSelect_groupLabelStyle__1a6uxqy1c';
|
|
14
|
+
var moreBadgeStyle = 'MultiSelect_moreBadgeStyle__1a6uxqyv';
|
|
15
|
+
var optionItemRecipe = createRuntimeFn({defaultClassName:'MultiSelect_optionItemRecipe__1a6uxqy15',variantClassNames:{active:{true:'MultiSelect_optionItemRecipe_active_true__1a6uxqy16',false:'MultiSelect_optionItemRecipe_active_false__1a6uxqy17'},selected:{true:'MultiSelect_optionItemRecipe_selected_true__1a6uxqy18',false:'MultiSelect_optionItemRecipe_selected_false__1a6uxqy19'},disabled:{true:'MultiSelect_optionItemRecipe_disabled_true__1a6uxqy1a',false:'MultiSelect_optionItemRecipe_disabled_false__1a6uxqy1b'}},defaultVariants:{active:false,selected:false,disabled:false},compoundVariants:[]});
|
|
16
|
+
var optionsListStyle = 'MultiSelect_optionsListStyle__1a6uxqy14';
|
|
17
|
+
var placeholderStyle = 'MultiSelect_placeholderStyle__1a6uxqyw';
|
|
18
|
+
var searchInputStyle = 'MultiSelect_searchInputStyle__1a6uxqy13';
|
|
19
|
+
var triggerContentStyle = 'MultiSelect_triggerContentStyle__1a6uxqyo';
|
|
20
|
+
var triggerRecipe = createRuntimeFn({defaultClassName:'MultiSelect_triggerRecipe__1a6uxqy1',variantClassNames:{size:{sm:'MultiSelect_triggerRecipe_size_sm__1a6uxqy2',md:'MultiSelect_triggerRecipe_size_md__1a6uxqy3',lg:'MultiSelect_triggerRecipe_size_lg__1a6uxqy4'},variant:{'default':'MultiSelect_triggerRecipe_variant_default__1a6uxqy5',ghost:'MultiSelect_triggerRecipe_variant_ghost__1a6uxqy6',filled:'MultiSelect_triggerRecipe_variant_filled__1a6uxqy7'},open:{true:'MultiSelect_triggerRecipe_open_true__1a6uxqy8',false:'MultiSelect_triggerRecipe_open_false__1a6uxqy9'},error:{true:'MultiSelect_triggerRecipe_error_true__1a6uxqya',false:'MultiSelect_triggerRecipe_error_false__1a6uxqyb'},disabled:{true:'MultiSelect_triggerRecipe_disabled_true__1a6uxqyc',false:'MultiSelect_triggerRecipe_disabled_false__1a6uxqyd'},empty:{true:'MultiSelect_triggerRecipe_empty_true__1a6uxqye',false:'MultiSelect_triggerRecipe_empty_false__1a6uxqyf'}},defaultVariants:{size:'md',variant:'default',open:false,disabled:false,error:false,empty:true},compoundVariants:[[{open:true},'MultiSelect_triggerRecipe_compound_0__1a6uxqyg'],[{error:true,variant:'default'},'MultiSelect_triggerRecipe_compound_1__1a6uxqyh'],[{error:true,variant:'filled'},'MultiSelect_triggerRecipe_compound_2__1a6uxqyi'],[{open:true,variant:'default'},'MultiSelect_triggerRecipe_compound_3__1a6uxqyj'],[{open:true,variant:'filled'},'MultiSelect_triggerRecipe_compound_4__1a6uxqyk'],[{empty:true,variant:'default'},'MultiSelect_triggerRecipe_compound_5__1a6uxqyl'],[{empty:true,variant:'ghost'},'MultiSelect_triggerRecipe_compound_6__1a6uxqym'],[{empty:true,variant:'filled'},'MultiSelect_triggerRecipe_compound_7__1a6uxqyn']]});
|
|
21
|
+
|
|
22
|
+
export { checkboxRecipe, chevronRecipe, chipLabelStyle, chipRecipe, chipRemoveStyle, clearButtonStyle, containerStyle, dropdownStyle, emptyMessageStyle, groupLabelStyle, moreBadgeStyle, optionItemRecipe, optionsListStyle, placeholderStyle, searchInputStyle, triggerContentStyle, triggerRecipe };
|
|
23
|
+
//# sourceMappingURL=MultiSelect.css.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MultiSelect.css.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
3
|
+
import { useId, useRef, useState, useMemo, useCallback, useEffect } from 'react';
|
|
4
|
+
import { createPortal } from 'react-dom';
|
|
5
|
+
import { CheckIcon } from '../../Icons/CheckIcon.js';
|
|
6
|
+
import { ChevronDownIcon } from '../../Icons/ChevronDownIcon.js';
|
|
7
|
+
import { CloseIcon } from '../../Icons/CloseIcon.js';
|
|
8
|
+
import { FormHelperText } from '../../form/FormHelperText.js';
|
|
9
|
+
import { FormLabel } from '../../form/FormLabel.js';
|
|
10
|
+
import { ScrollArea } from '../../layout/ScrollArea/ScrollArea.js';
|
|
11
|
+
import { useControlledState } from '../../../hooks/useControlledState/useControlledState.js';
|
|
12
|
+
import { useListboxNav } from '../../../hooks/useListboxNav/useListboxNav.js';
|
|
13
|
+
import { useMergedRef } from '../../../hooks/useMergedRef/useMergedRef.js';
|
|
14
|
+
import { cx } from '../../../utils/cx.js';
|
|
15
|
+
import { triggerContentStyle, placeholderStyle, chipLabelStyle, chipRemoveStyle, chipRecipe, moreBadgeStyle, clearButtonStyle, chevronRecipe, triggerRecipe, dropdownStyle, searchInputStyle, optionsListStyle, containerStyle, emptyMessageStyle, groupLabelStyle, checkboxRecipe, optionItemRecipe } from './MultiSelect.css.js';
|
|
16
|
+
|
|
17
|
+
function isOptionGroup(item) {
|
|
18
|
+
return 'options' in item && Array.isArray(item.options);
|
|
19
|
+
}
|
|
20
|
+
function flattenOptions(items) {
|
|
21
|
+
const result = [];
|
|
22
|
+
for (const item of items) {
|
|
23
|
+
if (isOptionGroup(item)) {
|
|
24
|
+
result.push(...item.options);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
result.push(item);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
32
|
+
function defaultFilter(option, query) {
|
|
33
|
+
const label = (option.label ?? option.value).toLowerCase();
|
|
34
|
+
return label.includes(query.toLowerCase());
|
|
35
|
+
}
|
|
36
|
+
const CHEVRON_SIZES = {
|
|
37
|
+
sm: 10,
|
|
38
|
+
md: 12,
|
|
39
|
+
lg: 14,
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Multi-value select that lets the user pick several options from a dropdown.
|
|
43
|
+
* Selected values are rendered as inline chips inside the trigger; the rest
|
|
44
|
+
* collapse into a "+N more" badge.
|
|
45
|
+
*
|
|
46
|
+
* Supports searchable mode, grouped options, controlled and uncontrolled
|
|
47
|
+
* modes, keyboard navigation, an optional `max` cap, and a clear-all button.
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```tsx
|
|
51
|
+
* <MultiSelect
|
|
52
|
+
* label="Tags"
|
|
53
|
+
* options={[
|
|
54
|
+
* { value: 'react', label: 'React' },
|
|
55
|
+
* { value: 'vue', label: 'Vue' },
|
|
56
|
+
* { value: 'svelte', label: 'Svelte' },
|
|
57
|
+
* ]}
|
|
58
|
+
* value={tags}
|
|
59
|
+
* onChange={setTags}
|
|
60
|
+
* />
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
function MultiSelect({ value, defaultValue, options, placeholder = 'Select...', searchable = false, searchPlaceholder = 'Search...', filterFn, emptyMessage = 'No results found', max, maxInlineChips = 3, size = 'md', variant = 'default', label, helperText, error = false, errorMessage, disabled = false, required = false, clearable = false, maxDropdownHeight = 240, minDropdownWidth, closeOnSelect = false, name, onChange, onOpenChange, className, style, testId, ref, id: idProp, ...rest }) {
|
|
64
|
+
const autoId = useId();
|
|
65
|
+
const fieldId = idProp ?? autoId;
|
|
66
|
+
const labelId = `${fieldId}-label`;
|
|
67
|
+
const helperId = `${fieldId}-helper`;
|
|
68
|
+
const listboxId = `${fieldId}-listbox`;
|
|
69
|
+
const triggerRef = useRef(null);
|
|
70
|
+
const dropdownRef = useRef(null);
|
|
71
|
+
const searchRef = useRef(null);
|
|
72
|
+
const setTriggerRef = useMergedRef(triggerRef, ref);
|
|
73
|
+
const [selected, setSelected] = useControlledState({
|
|
74
|
+
value,
|
|
75
|
+
defaultValue,
|
|
76
|
+
onChange,
|
|
77
|
+
fallback: [],
|
|
78
|
+
});
|
|
79
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
80
|
+
const [searchQuery, setSearchQuery] = useState('');
|
|
81
|
+
const [dropdownPos, setDropdownPos] = useState({});
|
|
82
|
+
const allOptions = useMemo(() => flattenOptions(options), [options]);
|
|
83
|
+
const filter = filterFn ?? defaultFilter;
|
|
84
|
+
const filteredOptions = useMemo(() => {
|
|
85
|
+
if (!searchable || !searchQuery)
|
|
86
|
+
return allOptions;
|
|
87
|
+
return allOptions.filter(opt => filter(opt, searchQuery));
|
|
88
|
+
}, [allOptions, searchable, searchQuery, filter]);
|
|
89
|
+
const selectedSet = useMemo(() => new Set(selected), [selected]);
|
|
90
|
+
const toggleValue = useCallback((val, isDisabled) => {
|
|
91
|
+
if (isDisabled)
|
|
92
|
+
return;
|
|
93
|
+
const isSelected = selectedSet.has(val);
|
|
94
|
+
if (isSelected) {
|
|
95
|
+
setSelected(selected.filter(v => v !== val));
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
if (typeof max === 'number' && selected.length >= max)
|
|
99
|
+
return;
|
|
100
|
+
setSelected([...selected, val]);
|
|
101
|
+
}
|
|
102
|
+
}, [max, selected, selectedSet, setSelected]);
|
|
103
|
+
const open = useCallback(() => {
|
|
104
|
+
if (disabled)
|
|
105
|
+
return;
|
|
106
|
+
setIsOpen(true);
|
|
107
|
+
setSearchQuery('');
|
|
108
|
+
onOpenChange?.(true);
|
|
109
|
+
}, [disabled, onOpenChange]);
|
|
110
|
+
const close = useCallback(() => {
|
|
111
|
+
setIsOpen(false);
|
|
112
|
+
setSearchQuery('');
|
|
113
|
+
onOpenChange?.(false);
|
|
114
|
+
triggerRef.current?.focus();
|
|
115
|
+
}, [onOpenChange]);
|
|
116
|
+
const listbox = useListboxNav({
|
|
117
|
+
items: filteredOptions,
|
|
118
|
+
isItemDisabled: opt => Boolean(opt.disabled),
|
|
119
|
+
onSelect: opt => {
|
|
120
|
+
toggleValue(opt.value, Boolean(opt.disabled));
|
|
121
|
+
if (closeOnSelect)
|
|
122
|
+
close();
|
|
123
|
+
},
|
|
124
|
+
onEscape: close,
|
|
125
|
+
});
|
|
126
|
+
const updateDropdownPosition = useCallback(() => {
|
|
127
|
+
const trigger = triggerRef.current;
|
|
128
|
+
if (!trigger)
|
|
129
|
+
return;
|
|
130
|
+
const rect = trigger.getBoundingClientRect();
|
|
131
|
+
const spaceBelow = window.innerHeight - rect.bottom;
|
|
132
|
+
const openAbove = spaceBelow < maxDropdownHeight + 8 && rect.top > spaceBelow;
|
|
133
|
+
const dropdownW = minDropdownWidth !== undefined
|
|
134
|
+
? Math.max(rect.width, minDropdownWidth)
|
|
135
|
+
: rect.width;
|
|
136
|
+
setDropdownPos({
|
|
137
|
+
left: rect.left,
|
|
138
|
+
width: dropdownW,
|
|
139
|
+
...(openAbove
|
|
140
|
+
? {
|
|
141
|
+
bottom: window.innerHeight - rect.top + 4,
|
|
142
|
+
transformOrigin: 'bottom',
|
|
143
|
+
}
|
|
144
|
+
: { top: rect.bottom + 4, transformOrigin: 'top' }),
|
|
145
|
+
});
|
|
146
|
+
}, [maxDropdownHeight, minDropdownWidth]);
|
|
147
|
+
useEffect(() => {
|
|
148
|
+
if (!isOpen)
|
|
149
|
+
return;
|
|
150
|
+
updateDropdownPosition();
|
|
151
|
+
if (searchable) {
|
|
152
|
+
requestAnimationFrame(() => {
|
|
153
|
+
searchRef.current?.focus();
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}, [isOpen, updateDropdownPosition, searchable]);
|
|
157
|
+
// Outside click handling
|
|
158
|
+
useEffect(() => {
|
|
159
|
+
if (!isOpen)
|
|
160
|
+
return;
|
|
161
|
+
const handleClick = (e) => {
|
|
162
|
+
const target = e.target;
|
|
163
|
+
if (!triggerRef.current?.contains(target) &&
|
|
164
|
+
!dropdownRef.current?.contains(target)) {
|
|
165
|
+
close();
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
document.addEventListener('mousedown', handleClick);
|
|
169
|
+
return () => {
|
|
170
|
+
document.removeEventListener('mousedown', handleClick);
|
|
171
|
+
};
|
|
172
|
+
}, [isOpen, close]);
|
|
173
|
+
const handleTriggerKeyDown = useCallback((e) => {
|
|
174
|
+
if (disabled)
|
|
175
|
+
return;
|
|
176
|
+
switch (e.key) {
|
|
177
|
+
case 'Enter':
|
|
178
|
+
case ' ':
|
|
179
|
+
case 'ArrowDown':
|
|
180
|
+
case 'ArrowUp':
|
|
181
|
+
e.preventDefault();
|
|
182
|
+
if (!isOpen)
|
|
183
|
+
open();
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
}, [disabled, isOpen, open]);
|
|
187
|
+
const handleDropdownKeyDown = useCallback((e) => {
|
|
188
|
+
listbox.handleKeyDown(e);
|
|
189
|
+
}, [listbox]);
|
|
190
|
+
const removeValue = useCallback((val) => {
|
|
191
|
+
setSelected(selected.filter(v => v !== val));
|
|
192
|
+
}, [selected, setSelected]);
|
|
193
|
+
const handleClearAll = useCallback((e) => {
|
|
194
|
+
e.stopPropagation();
|
|
195
|
+
setSelected([]);
|
|
196
|
+
}, [setSelected]);
|
|
197
|
+
const showHelperText = error && errorMessage ? errorMessage : helperText;
|
|
198
|
+
const chevronSize = CHEVRON_SIZES[size];
|
|
199
|
+
const selectedOptions = useMemo(() => selected
|
|
200
|
+
.map(v => allOptions.find(o => o.value === v))
|
|
201
|
+
.filter((o) => Boolean(o)), [allOptions, selected]);
|
|
202
|
+
const isEmpty = selected.length === 0;
|
|
203
|
+
const visibleChips = maxInlineChips === Infinity
|
|
204
|
+
? selectedOptions
|
|
205
|
+
: selectedOptions.slice(0, maxInlineChips);
|
|
206
|
+
const overflowCount = selectedOptions.length - visibleChips.length;
|
|
207
|
+
// Render options
|
|
208
|
+
const renderOptionsList = () => {
|
|
209
|
+
if (filteredOptions.length === 0) {
|
|
210
|
+
return jsx("div", { className: emptyMessageStyle, children: emptyMessage });
|
|
211
|
+
}
|
|
212
|
+
const hasGroups = options.some(isOptionGroup);
|
|
213
|
+
if (hasGroups && !searchQuery) {
|
|
214
|
+
let flatIndex = 0;
|
|
215
|
+
return options.map((item, groupIdx) => {
|
|
216
|
+
if (isOptionGroup(item)) {
|
|
217
|
+
const groupOptions = item.options;
|
|
218
|
+
const startIdx = flatIndex;
|
|
219
|
+
flatIndex += groupOptions.length;
|
|
220
|
+
return (jsxs("div", { role: "group", "aria-label": item.label, children: [jsx("div", { className: groupLabelStyle, children: item.label }), groupOptions.map((opt, optIdx) => renderOptionItem(opt, startIdx + optIdx))] }, `group-${String(groupIdx)}`));
|
|
221
|
+
}
|
|
222
|
+
const idx = flatIndex;
|
|
223
|
+
flatIndex += 1;
|
|
224
|
+
return renderOptionItem(item, idx);
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
return filteredOptions.map((opt, idx) => renderOptionItem(opt, idx));
|
|
228
|
+
};
|
|
229
|
+
const renderOptionItem = (opt, index) => {
|
|
230
|
+
const isSelected = selectedSet.has(opt.value);
|
|
231
|
+
const isActive = index === listbox.activeIndex;
|
|
232
|
+
const isDisabled = opt.disabled ?? false;
|
|
233
|
+
const reachedMax = typeof max === 'number' && !isSelected && selected.length >= max;
|
|
234
|
+
const interactiveDisabled = isDisabled || reachedMax;
|
|
235
|
+
return (jsxs("div", { role: "option", "aria-selected": isSelected, "aria-disabled": interactiveDisabled || undefined, id: `${listboxId}-${opt.value}`, className: optionItemRecipe({
|
|
236
|
+
active: isActive,
|
|
237
|
+
selected: isSelected,
|
|
238
|
+
disabled: interactiveDisabled,
|
|
239
|
+
}), onClick: () => {
|
|
240
|
+
if (!interactiveDisabled) {
|
|
241
|
+
toggleValue(opt.value, false);
|
|
242
|
+
if (closeOnSelect)
|
|
243
|
+
close();
|
|
244
|
+
}
|
|
245
|
+
}, onMouseEnter: () => {
|
|
246
|
+
if (!interactiveDisabled)
|
|
247
|
+
listbox.setActiveIndex(index);
|
|
248
|
+
}, children: [jsx("span", { className: checkboxRecipe({ selected: isSelected }), "aria-hidden": "true", children: isSelected && jsx(CheckIcon, { size: 10, decorative: true }) }), opt.icon && jsx("span", { children: opt.icon }), jsx("span", { children: opt.label ?? opt.value })] }, opt.value));
|
|
249
|
+
};
|
|
250
|
+
const formValue = selected.join(',');
|
|
251
|
+
return (jsxs("div", { className: cx(containerStyle, className), style: style, children: [label && (jsx(FormLabel, { id: labelId, htmlFor: fieldId, required: required, disabled: disabled, children: label })), jsxs("button", { ref: setTriggerRef, id: fieldId, type: "button", role: "combobox", "aria-haspopup": "listbox", "aria-expanded": isOpen, "aria-controls": isOpen ? listboxId : undefined, "aria-labelledby": label ? labelId : undefined, "aria-required": required || undefined, "aria-invalid": error || undefined, "aria-describedby": showHelperText ? helperId : undefined, disabled: disabled, onClick: () => (isOpen ? close() : open()), onKeyDown: handleTriggerKeyDown, className: triggerRecipe({
|
|
252
|
+
size,
|
|
253
|
+
variant,
|
|
254
|
+
open: isOpen,
|
|
255
|
+
disabled,
|
|
256
|
+
error,
|
|
257
|
+
empty: isEmpty,
|
|
258
|
+
}), "data-testid": testId, ...rest, children: [jsx("span", { className: triggerContentStyle, children: isEmpty ? (jsx("span", { className: placeholderStyle, children: placeholder })) : (jsxs(Fragment, { children: [visibleChips.map(opt => (jsxs("span", { className: chipRecipe({ size }), "data-testid": testId ? `${testId}-chip-${opt.value}` : undefined, children: [opt.icon && jsx("span", { children: opt.icon }), jsx("span", { className: chipLabelStyle, children: opt.label ?? opt.value }), !disabled && (jsx("span", { role: "button", tabIndex: -1, "aria-label": `Remove ${opt.label ?? opt.value}`, className: chipRemoveStyle, onClick: e => {
|
|
259
|
+
e.stopPropagation();
|
|
260
|
+
removeValue(opt.value);
|
|
261
|
+
}, children: jsx(CloseIcon, { size: "sm", decorative: true }) }))] }, opt.value))), overflowCount > 0 && (jsxs("span", { className: moreBadgeStyle, children: ["+", overflowCount, " more"] }))] })) }), clearable && !isEmpty && !disabled && (jsx("span", { role: "button", tabIndex: -1, "aria-label": "Clear selection", className: clearButtonStyle, onClick: handleClearAll, children: jsx(CloseIcon, { size: "sm", decorative: true }) })), jsx("span", { className: chevronRecipe({ open: isOpen }), children: jsx(ChevronDownIcon, { size: chevronSize, decorative: true }) })] }), name && jsx("input", { type: "hidden", name: name, value: formValue }), isOpen &&
|
|
262
|
+
createPortal(jsxs("div", { ref: dropdownRef, role: "listbox", id: listboxId, "aria-multiselectable": "true", "aria-labelledby": label ? labelId : undefined, className: dropdownStyle, style: dropdownPos, onKeyDown: handleDropdownKeyDown, tabIndex: -1, children: [searchable && (jsx("input", { ref: searchRef, type: "text", placeholder: searchPlaceholder, value: searchQuery, onChange: e => {
|
|
263
|
+
setSearchQuery(e.target.value);
|
|
264
|
+
}, onKeyDown: handleDropdownKeyDown, className: searchInputStyle, "aria-label": "Search options" })), jsx(ScrollArea, { className: optionsListStyle, maxHeight: maxDropdownHeight - (searchable ? 32 : 0), scrollbarWidth: 4, scrollbarVisibility: "auto", hideDelay: 600, children: renderOptionsList() })] }), document.body), showHelperText && (jsx(FormHelperText, { id: helperId, error: error, children: showHelperText }))] }));
|
|
265
|
+
}
|
|
266
|
+
MultiSelect.displayName = 'MultiSelect';
|
|
267
|
+
|
|
268
|
+
export { MultiSelect };
|
|
269
|
+
//# sourceMappingURL=MultiSelect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MultiSelect.js","sources":["../../../../../../src/components/controls/MultiSelect/MultiSelect.tsx"],"sourcesContent":[null],"names":[],"mappings":";;;;;;;;;;;;;;;;AA+CA;AAGE;AACF;AAEA;;AAIE;AACE;;;;AAGE;;;AAGJ;AACF;AAEA;AAIE;;AAEF;AAEA;AACE;AACA;AACA;;AAGF;;;;;;;;;;;;;;;;;;;;;AAqBG;AACG;AAiCJ;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAIA;;;;AAIE;AACD;;;;AAMD;AAEA;AACA;AACE;AAAiC;AACjC;;AAGF;;AAII;;;;AAGE;;;;;;;;AASN;AACE;;;;AAGA;AACF;AAEA;;;AAGE;AACA;AACF;;AAGE;;;AAGE;AACA;AAAmB;;AAErB;AACD;AAED;AACE;AACA;;AACA;;AAEA;AAGA;;AAGI;AAEJ;;AAEE;AACA;AACE;;AAEI;AACD;AACH;AACH;AACH;;AAGE;;AACA;;;AAGI;AACF;;;;;AAMF;;AACA;AACE;;;AAKE;;AAEJ;AACA;AACA;AACE;AACF;AACF;AAEA;AAEI;;AACA;AACE;AACA;AACA;AACA;;AAEE;AAAa;;;;AAOrB;AAEI;AACF;AAIF;AAEI;AACF;AAIF;;;AAIE;AAIF;AACA;AAEA;AAGO;AACA;AAIP;AACA;AAEI;;;;;AAMF;AACE;;;AAIF;;;AAGI;AACE;;AAEA;;;;;AAgBF;AACF;;AAGF;AACF;AAEA;;AAEE;AACA;AACA;AAEA;;AAUM;AACA;AACA;AACD;;AAGG;AACA;AAAmB;;AAEvB;AAEE;AAA0B;;AAalC;;AAIA;;;AA+BQ;;;AAGA;;;AA8Bc;;AAmChB;AAmBU;AACF;AA0BhB;AAEA;;"}
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
3
3
|
import { useId, useRef, useState, useMemo, useCallback, useEffect } from 'react';
|
|
4
4
|
import { createPortal } from 'react-dom';
|
|
5
|
+
import { CheckIcon } from '../../Icons/CheckIcon.js';
|
|
6
|
+
import { ChevronDownIcon } from '../../Icons/ChevronDownIcon.js';
|
|
7
|
+
import { CloseIcon } from '../../Icons/CloseIcon.js';
|
|
5
8
|
import { FormLabel } from '../../form/FormLabel.js';
|
|
6
9
|
import { FormHelperText } from '../../form/FormHelperText.js';
|
|
7
10
|
import { ScrollArea } from '../../layout/ScrollArea/ScrollArea.js';
|
|
@@ -34,8 +37,6 @@ const CHEVRON_SIZES = {
|
|
|
34
37
|
md: 12,
|
|
35
38
|
lg: 14,
|
|
36
39
|
};
|
|
37
|
-
// --- Chevron icon ---
|
|
38
|
-
const ChevronDownIcon = ({ size }) => (jsx("svg", { width: size, height: size, viewBox: "0 0 12 12", fill: "none", "aria-hidden": "true", children: jsx("path", { d: "M3 4.5L6 7.5L9 4.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }));
|
|
39
40
|
// --- Component ---
|
|
40
41
|
/**
|
|
41
42
|
* Select component for choosing single values from a dropdown list.
|
|
@@ -305,7 +306,7 @@ function Select({ value: valueProp, defaultValue, options, placeholder = 'Select
|
|
|
305
306
|
if (!opt.disabled) {
|
|
306
307
|
setHighlightedIndex(index);
|
|
307
308
|
}
|
|
308
|
-
}, children: [opt.icon && jsx("span", { children: opt.icon }), jsx("span", { children: opt.label ?? opt.value }), isSelected && (jsx("span", { className: checkmarkStyle, children: jsx(
|
|
309
|
+
}, children: [opt.icon && jsx("span", { children: opt.icon }), jsx("span", { children: opt.label ?? opt.value }), isSelected && (jsx("span", { className: checkmarkStyle, children: jsx(CheckIcon, { size: 10, decorative: true }) }))] }, opt.value));
|
|
309
310
|
};
|
|
310
311
|
return (jsxs("div", { className: cx(selectContainerStyle, className), style: style, children: [label && (jsx(FormLabel, { id: labelId, htmlFor: selectId, required: required, disabled: disabled, children: label })), jsxs("button", { ref: setTriggerRef, type: "button", role: "combobox", "aria-haspopup": "listbox", "aria-expanded": isOpen, "aria-controls": isOpen ? listboxId : undefined, "aria-labelledby": label ? labelId : undefined, "aria-required": required || undefined, "aria-invalid": error || undefined, "aria-describedby": showHelperText ? helperId : undefined, disabled: disabled, onClick: () => (isOpen ? close() : open()), onKeyDown: handleTriggerKeyDown, className: triggerRecipe({
|
|
311
312
|
size,
|
|
@@ -314,7 +315,7 @@ function Select({ value: valueProp, defaultValue, options, placeholder = 'Select
|
|
|
314
315
|
disabled,
|
|
315
316
|
error,
|
|
316
317
|
hasValue: currentValue !== null,
|
|
317
|
-
}), "data-testid": testId, ...rest, children: [jsx("span", { className: triggerContentStyle, children: selectedOption ? (jsxs(Fragment, { children: [selectedOption.icon && jsx("span", { children: selectedOption.icon }), jsx("span", { children: selectedOption.label ?? selectedOption.value })] })) : (jsx("span", { children: placeholder })) }), clearable && currentValue !== null && (jsx("span", { className: clearButtonStyle, role: "button", onClick: handleClear, "aria-label": "Clear selection", tabIndex: -1, children: jsx(
|
|
318
|
+
}), "data-testid": testId, ...rest, children: [jsx("span", { className: triggerContentStyle, children: selectedOption ? (jsxs(Fragment, { children: [selectedOption.icon && jsx("span", { children: selectedOption.icon }), jsx("span", { children: selectedOption.label ?? selectedOption.value })] })) : (jsx("span", { children: placeholder })) }), clearable && currentValue !== null && (jsx("span", { className: clearButtonStyle, role: "button", onClick: handleClear, "aria-label": "Clear selection", tabIndex: -1, children: jsx(CloseIcon, { size: "sm", decorative: true }) })), jsx("span", { className: chevronRecipe({ open: isOpen }), children: jsx(ChevronDownIcon, { size: chevronSize, decorative: true }) })] }), name && jsx("input", { type: "hidden", name: name, value: currentValue ?? '' }), isOpen &&
|
|
318
319
|
createPortal(jsxs("div", { ref: dropdownRef, role: "listbox", id: listboxId, "aria-labelledby": label ? labelId : undefined, className: dropdownStyle, style: dropdownStyleState, onKeyDown: handleDropdownKeyDown, tabIndex: -1, children: [searchable && (jsx("input", { ref: searchRef, type: "text", placeholder: searchPlaceholder, value: searchQuery, onChange: e => {
|
|
319
320
|
setSearchQuery(e.target.value);
|
|
320
321
|
setHighlightedIndex(-1);
|