diff --git a/docs/components/OAuthProviderInstructions/OAuthProviderSelect.tsx b/docs/components/OAuthProviderInstructions/OAuthProviderSelect.tsx
index 815c8b1885..aeff3bef6c 100644
--- a/docs/components/OAuthProviderInstructions/OAuthProviderSelect.tsx
+++ b/docs/components/OAuthProviderInstructions/OAuthProviderSelect.tsx
@@ -4,44 +4,68 @@ import {
ComboboxPopover,
ComboboxProvider,
} from "@ariakit/react"
-import { useOAuthProviderSelect } from "./useOAuthProviderSelect"
import dynamic from "next/dynamic"
-import type { ChangeEvent } from "react"
-
import { Link } from "@/components/Link"
import manifest from "@/data/manifest.json"
+import {
+ PreviewProviders,
+ type Provider,
+} from "@/components/SearchBarProviders/PreviewProviders"
+import { useSelectCombobox } from "@/hooks/use-select-combobox"
const OAuthProviderInstructions = dynamic(() =>
import("./content").then((mod) => mod.OAuthInstructions)
)
+const previewProviders: Provider[] = [
+ { id: "google", name: "Google" },
+ { id: "github", name: "GitHub" },
+ { id: "twitter", name: "Twitter" },
+ { id: "keycloak", name: "Keycloak" },
+ { id: "okta", name: "Okta" },
+]
+
+const items = Object.entries(manifest.providersOAuth).map(([id, name]) => ({
+ id,
+ name,
+}))
+
export function OAuthProviderSelect() {
- const { items, term, selected, handleSearchItem, handleSelectOption } =
- useOAuthProviderSelect()
+ const {
+ selectedItem,
+ filteredItems,
+ hasMatchItem,
+ handleChange,
+ handleSelect,
+ } = useSelectCombobox({
+ items,
+ })
return (
-
+
) =>
- handleSearchItem(e.target.value)
- }
+ value={selectedItem.name}
+ onChange={handleChange}
/>
- {items.map((item) => (
+ {filteredItems.map((item) => (
handleSelectOption(item)}
+ onClick={() => handleSelect(item)}
>
))}
- {!term ? (
+ {!selectedItem.name && (
<>
Or jump directly to one of the popular ones below.
-
-
- handleSelectOption({ id: "google", name: "Google" })
- }
- className="flex h-32 w-32 min-w-24 flex-col items-center justify-between rounded-lg border border-solid border-neutral-200 p-4 shadow-sm transition-colors duration-300 hover:bg-neutral-50 dark:border-neutral-800 dark:hover:bg-neutral-950"
- >
-
-
Google
-
-
- handleSelectOption({ id: "github", name: "GitHub" })
- }
- >
-
-
GitHub
-
-
- handleSelectOption({ id: "twitter", name: "Twitter" })
- }
- className="flex h-32 w-32 min-w-24 flex-col items-center justify-between rounded-lg border border-solid border-neutral-200 p-4 shadow-sm transition-colors duration-300 hover:bg-neutral-50 dark:border-neutral-800 dark:hover:bg-neutral-950"
- >
-
-
Twitter
-
-
- handleSelectOption({ id: "keycloak", name: "keycloak" })
- }
- className="flex h-32 w-32 min-w-24 flex-col items-center justify-between rounded-lg border border-solid border-neutral-200 p-4 shadow-sm transition-colors duration-300 hover:bg-neutral-50 dark:border-neutral-800 dark:hover:bg-neutral-950"
- >
-
-
Keycloak
-
-
handleSelectOption({ id: "okta", name: "okta" })}
- className="flex h-32 w-32 min-w-24 flex-col items-center justify-between rounded-lg border border-solid border-neutral-200 p-4 shadow-sm transition-colors duration-300 hover:bg-neutral-50 dark:border-neutral-800 dark:hover:bg-neutral-950"
- >
-
-
Okta
-
-
+
>
- ) : null}
- {term && items.length === 0 ? (
+ )}
+ {!hasMatchItem && filteredItems.length === 0 && (
Can't find the OAuth provider you're looking for? You can always{" "}
@@ -119,17 +95,11 @@ export function OAuthProviderSelect() {
.
- ) : null}
+ )}
- {selected && term && items.length !== 0 ? (
-
- ) : null}
+ {hasMatchItem && (
+
+ )}
)
}
diff --git a/docs/components/OAuthProviderInstructions/content/index.tsx b/docs/components/OAuthProviderInstructions/content/index.tsx
index da1ad24ea8..3d53c6a19b 100644
--- a/docs/components/OAuthProviderInstructions/content/index.tsx
+++ b/docs/components/OAuthProviderInstructions/content/index.tsx
@@ -1,8 +1,7 @@
import { useEffect, useState } from "react"
-import { type Highlighter, getHighlighter } from "shiki"
+import { type Highlighter, createHighlighter } from "shiki"
import cx from "classnames"
import { Callout, Pre, Code as NXCode } from "nextra/components"
-
import { StepTitle } from "./components/StepTitle"
import { SetupCode } from "./components/SetupCode"
import { SignInCode } from "./components/SignInCode"
@@ -19,7 +18,7 @@ export function OAuthInstructions({ providerId, disabled = false }: Props) {
const [highlighter, setHighlighter] = useState(null)
useEffect(() => {
;(async () => {
- const hl = await getHighlighter({
+ const hl = await createHighlighter({
themes: ["github-light", "github-dark"],
langs: ["ts", "tsx", "bash"],
})
@@ -52,10 +51,9 @@ export function OAuthInstructions({ providerId, disabled = false }: Props) {
return (
{/* Step 1 */}
diff --git a/docs/components/OAuthProviderInstructions/useOAuthProviderSelect.ts b/docs/components/OAuthProviderInstructions/useOAuthProviderSelect.ts
deleted file mode 100644
index 97d5072861..0000000000
--- a/docs/components/OAuthProviderInstructions/useOAuthProviderSelect.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import { useState } from "react"
-import manifest from "@/data/manifest.json"
-
-const providerList = Object.entries(manifest.providersOAuth).map(
- ([id, name]) => {
- return { id, name }
- }
-)
-
-export function useOAuthProviderSelect() {
- const [term, setTerm] = useState("")
- const [selected, setSelected] = useState("")
-
- function handleSearchItem(term: string) {
- setTerm(term)
- }
-
- function handleSelectOption(item: { id: string; name: string }) {
- setTerm(item.name)
- setSelected(item.id)
- }
-
- return {
- items: providerList.filter((item) =>
- item.name.toLowerCase().includes(term?.toLowerCase())
- ),
- term,
- selected,
- handleSearchItem,
- handleSelectOption,
- }
-}
diff --git a/docs/components/SearchBarProviders/PreviewProviders.tsx b/docs/components/SearchBarProviders/PreviewProviders.tsx
new file mode 100644
index 0000000000..d1fdfdb0f2
--- /dev/null
+++ b/docs/components/SearchBarProviders/PreviewProviders.tsx
@@ -0,0 +1,35 @@
+export interface Provider {
+ id: string
+ name: string
+}
+
+export interface PreviewProvidersProps {
+ className?: string
+ providers: Provider[]
+ onSelected: (provider: Provider) => void
+}
+
+export function PreviewProviders({
+ className,
+ providers,
+ onSelected,
+}: PreviewProvidersProps) {
+ return (
+
+ {providers.map((provider) => (
+ onSelected(provider)}
+ >
+
+
{provider.name}
+
+ ))}
+
+ )
+}
diff --git a/docs/hooks/use-select-combobox.ts b/docs/hooks/use-select-combobox.ts
new file mode 100644
index 0000000000..b8ee43a0dc
--- /dev/null
+++ b/docs/hooks/use-select-combobox.ts
@@ -0,0 +1,48 @@
+import { ChangeEvent, useState } from "react"
+
+interface SelectComboboxValue {
+ id: string
+ name: string
+}
+
+interface SelectComboboxProps {
+ defaultValue?: SelectComboboxValue
+ items: SelectComboboxValue[]
+}
+
+export const useSelectCombobox = ({
+ defaultValue = { id: "", name: "" },
+ items,
+}: SelectComboboxProps) => {
+ const [selectedItem, setSelectedItem] =
+ useState(defaultValue)
+ const [filteredItems, setFilteredItems] = useState(items)
+ const [hasMatchItem, setHasMatchItem] = useState(false)
+
+ const handleSelect = (value: SelectComboboxValue) => {
+ let hasMatchItem = false
+ setFilteredItems(
+ items.filter((item) => {
+ if (item.id === value.id) {
+ hasMatchItem = true
+ }
+ return item.name.toLowerCase().includes(value.name.toLowerCase())
+ })
+ )
+ setSelectedItem(value)
+ setHasMatchItem(hasMatchItem)
+ }
+
+ const handleChange = (event: ChangeEvent) => {
+ const { value } = event.target
+ handleSelect({ id: value, name: value })
+ }
+
+ return {
+ selectedItem,
+ filteredItems,
+ handleSelect,
+ handleChange,
+ hasMatchItem,
+ }
+}
diff --git a/docs/tsconfig.json b/docs/tsconfig.json
index f509920619..6d428110ce 100644
--- a/docs/tsconfig.json
+++ b/docs/tsconfig.json
@@ -20,7 +20,8 @@
"@/utils/*": ["utils/*"],
"@/icons/*": ["components/Icons/*"],
"@/icons": ["components/Icons"],
- "@/data/*": ["pages/data/*"]
+ "@/data/*": ["pages/data/*"],
+ "@/hooks/*": ["hooks/*"]
},
"plugins": [
{