diff --git a/package-lock.json b/package-lock.json
index 3703baa6c..d9ef86a07 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -33621,6 +33621,7 @@
"@spark-ui/icon-button": "^2.2.3",
"@spark-ui/icons": "^1.21.6",
"@spark-ui/popover": "^1.5.5",
+ "@spark-ui/spinner": "^2.2.2",
"@spark-ui/use-merge-refs": "^0.4.0",
"@spark-ui/visually-hidden": "^1.2.0",
"class-variance-authority": "0.7.0",
diff --git a/packages/components/combobox/package.json b/packages/components/combobox/package.json
index fa89bf40d..a03815cb9 100644
--- a/packages/components/combobox/package.json
+++ b/packages/components/combobox/package.json
@@ -34,6 +34,7 @@
"@spark-ui/icon-button": "^2.2.3",
"@spark-ui/icons": "^1.21.6",
"@spark-ui/popover": "^1.5.5",
+ "@spark-ui/spinner": "^2.2.2",
"@spark-ui/use-merge-refs": "^0.4.0",
"@spark-ui/visually-hidden": "^1.2.0",
"class-variance-authority": "0.7.0",
diff --git a/packages/components/combobox/src/Combobox.doc.mdx b/packages/components/combobox/src/Combobox.doc.mdx
index 6475b4295..cfc5b676c 100644
--- a/packages/components/combobox/src/Combobox.doc.mdx
+++ b/packages/components/combobox/src/Combobox.doc.mdx
@@ -155,6 +155,13 @@ Use `readOnly` prop to indicate the combobox is only readable.
+### isLoading
+
+Use the `isLoading` prop to render the combobox in loading state.
+This will prepend a spinner inside the items-list.
+
+
+
### Status
Use `state` prop to assign a specific state to the combobox, choosing from: `error`, `alert` and `success`. By doing so, the outline styles will be updated, and a status indicator will be displayed accordingly.
@@ -215,6 +222,10 @@ If your `Combobox.Item` contains anything else than raw text, you may use any JS
+### Search Modal
+
+
+
## Form field
### Label
diff --git a/packages/components/combobox/src/Combobox.stories.tsx b/packages/components/combobox/src/Combobox.stories.tsx
index c63984fcc..f2ad64df3 100644
--- a/packages/components/combobox/src/Combobox.stories.tsx
+++ b/packages/components/combobox/src/Combobox.stories.tsx
@@ -1,6 +1,9 @@
/* eslint-disable max-lines */
// import { Button } from '@spark-ui/button'
+import { Button } from '@spark-ui/button'
import { Checkbox, CheckboxGroup } from '@spark-ui/checkbox'
+import { Chip } from '@spark-ui/chip'
+import { Dialog } from '@spark-ui/dialog'
import { FormField } from '@spark-ui/form-field'
import { PenOutline } from '@spark-ui/icons/dist/icons/PenOutline'
import { RadioGroup } from '@spark-ui/radio-group'
@@ -780,3 +783,122 @@ export const FormFieldValidation: StoryFn = () => {
)
}
+
+export const IsLoading: StoryFn = _args => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ No results found
+ To Kill a Mockingbird
+ War and Peace
+ The Idiot
+ A Picture of Dorian Gray
+ 1984
+
+ Pride and Prejudice but it is an extremely long title
+
+
+
+
+
+ )
+}
+export const ModalSearch: StoryFn = () => {
+ const books = [
+ { id: 1, name: 'Things Fall Apart' },
+ { id: 2, name: 'The Catcher in the Rye' },
+ { id: 3, name: 'The Great Gatsby' },
+ { id: 4, name: 'Fairy tales' },
+ { id: 5, name: 'The Hobbit' },
+ { id: 6, name: 'The Lord of the Rings' },
+ { id: 7, name: 'And Then There Were None' },
+ { id: 8, name: 'The Da Vinci Code' },
+ { id: 9, name: 'The Alchemist' },
+ { id: 10, name: 'The Epic Of Gilgamesh' },
+ { id: 11, name: 'The Book Thief' },
+ { id: 12, name: 'The Little Prince' },
+ { id: 13, name: 'The Book Of Job' },
+ { id: 14, name: 'The Grapes Of Wrath' },
+ { id: 15, name: 'Pride and Prejudice' },
+ { id: 16, name: 'The Odyssey' },
+ { id: 17, name: 'One Hundred Years of Solitude' },
+ { id: 18, name: 'Crime and Punishment' },
+ { id: 19, name: 'Gypsy Ballads' },
+ { id: 20, name: 'Love in the Time of Cholera' },
+ { id: 21, name: 'Hunger' },
+ { id: 22, name: 'The Old Man and the Sea' },
+ { id: 23, name: 'To Kill a Mockingbird' },
+ { id: 24, name: 'War and Peace' },
+ { id: 25, name: 'The Idiot' },
+ { id: 26, name: 'Scaramouche' },
+ { id: 27, name: 'A Picture of Dorian Gray' },
+ { id: 28, name: '1984' },
+ ]
+ const [value, setValue] = useState()
+ const [isOpen, setIsOpen] = useState(false)
+
+ return (
+
+
+
+ )
+}
diff --git a/packages/components/combobox/src/ComboboxContext.tsx b/packages/components/combobox/src/ComboboxContext.tsx
index 7fc51c92f..554e856c8 100644
--- a/packages/components/combobox/src/ComboboxContext.tsx
+++ b/packages/components/combobox/src/ComboboxContext.tsx
@@ -35,6 +35,7 @@ export interface ComboboxContextState extends DownshiftState {
setLastInteractionType: (type: 'mouse' | 'keyboard') => void
innerInputRef: React.RefObject
triggerAreaRef: React.RefObject
+ isLoading?: boolean
}
export type ComboboxContextCommonProps = PropsWithChildren<{
@@ -75,6 +76,10 @@ export type ComboboxContextCommonProps = PropsWithChildren<{
* If you wish to keep every item on a single line, disabled this property.
*/
wrap?: boolean
+ /**
+ * Display a spinner to indicate to the user that the combobox is loading results for .
+ */
+ isLoading?: boolean
}>
interface ComboboxPropsSingle {
@@ -145,6 +150,7 @@ export const ComboboxProvider = ({
open: controlledOpen,
defaultOpen,
onOpenChange,
+ isLoading,
}: ComboboxContextProps) => {
const isMounted = useRef(false)
@@ -395,6 +401,7 @@ export const ComboboxProvider = ({
setInputValue,
selectItem: onInternalSelectedItemChange,
setSelectedItems: onInternalSelectedItemsChange,
+ isLoading,
}}
>
{children}
diff --git a/packages/components/combobox/src/ComboboxItems.tsx b/packages/components/combobox/src/ComboboxItems.tsx
index 0116a1b03..95655fdd8 100644
--- a/packages/components/combobox/src/ComboboxItems.tsx
+++ b/packages/components/combobox/src/ComboboxItems.tsx
@@ -1,3 +1,4 @@
+import { Spinner } from '@spark-ui/spinner'
import { useMergeRefs } from '@spark-ui/use-merge-refs'
import { cx } from 'class-variance-authority'
import { forwardRef, ReactNode, type Ref } from 'react'
@@ -21,19 +22,25 @@ export const Items = forwardRef(
const ref = useMergeRefs(forwardedRef, downshiftRef)
+ const isOpen = ctx.hasPopover ? ctx.isOpen : true
+
+ console.log('isLoading', ctx.isLoading)
+
return (