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": [ {