Skip to content

Commit

Permalink
ux: add transition on search (#1933)
Browse files Browse the repository at this point in the history
  • Loading branch information
abvthecity authored Dec 21, 2024
1 parent 7fc7fa4 commit 5975c21
Show file tree
Hide file tree
Showing 8 changed files with 109 additions and 19 deletions.
1 change: 1 addition & 0 deletions packages/fern-docs/search-server/src/algolia/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export { browseAllObjectsForFilters } from "./browse-all-objects";
export * from "./constants";
export * from "./create-search-filters";
export * from "./edge";
export * from "./fetch-facet-values";
export * from "./tasks";
export * from "./types";
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useSearchParams } from "next/navigation";
import React from "react";

// This is sample data.
const data = {
Expand Down
12 changes: 2 additions & 10 deletions packages/fern-docs/search-ui/src/app/(demo)/search-demo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import {
} from "@/components";
import { ChatbotModelSelect } from "@/components/chatbot/model-select";
import { DesktopCommandWithAskAI } from "@/components/desktop/desktop-ask-ai";
import { CommandAskAIGroup } from "@/components/shared/command-ask-ai";
import { useIsMobile } from "@/hooks/use-mobile";
import { FacetsResponse, SEARCH_INDEX } from "@fern-docs/search-server/algolia";
import { useAtom } from "jotai";
Expand Down Expand Up @@ -137,7 +136,7 @@ export function DemoInstantSearchClient({
</MobileCommand>
</AppSidebar>
) : (
<DesktopSearchDialog open={open} onOpenChange={setOpen} asChild>
<DesktopSearchDialog open={open} onOpenChange={setOpen}>
<DesktopCommandWithAskAI
domain={domain}
askAI={askAi}
Expand All @@ -146,6 +145,7 @@ export function DemoInstantSearchClient({
api="/api/chat"
suggestionsApi="/api/suggest"
initialInput={initialInput}
setInitialInput={setInitialInput}
body={{
algoliaSearchKey: apiKey,
domain,
Expand All @@ -158,14 +158,6 @@ export function DemoInstantSearchClient({
>
<DefaultDesktopBackButton />

<CommandAskAIGroup
onAskAI={(initialInput) => {
setInitialInput(initialInput);
setAskAi(true);
}}
forceMount
/>

<CommandGroupFilters />
<CommandEmpty />
<CommandSearchHits onSelect={handleSubmit} domain={domain} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import * as Command from "../cmdk";
import { CodeBlock } from "../code-block";
import { MarkdownContent } from "../md-content";
import { useFacetFilters } from "../search-client";
import { CommandAskAIGroup } from "../shared";
import { CommandLink } from "../shared/command-link";
import tunnel from "../tunnel-rat";
import { cn } from "../ui/cn";
Expand All @@ -60,7 +61,7 @@ const headerActions = tunnel();

export const DesktopCommandWithAskAI = forwardRef<
HTMLDivElement,
ComponentPropsWithoutRef<typeof DesktopCommandRoot> & {
Omit<ComponentPropsWithoutRef<typeof DesktopCommandRoot>, "children"> & {
askAI?: boolean;
defaultAskAI?: boolean;
setAskAI?: (askAI: boolean) => void;
Expand All @@ -75,6 +76,8 @@ export const DesktopCommandWithAskAI = forwardRef<
composerActions?: ReactNode;
domain: string;
renderActions?: (message: SqueezedMessage) => ReactNode;
setInitialInput?: (initialInput: string) => void;
children?: ReactNode;
}
>(
(
Expand All @@ -94,22 +97,65 @@ export const DesktopCommandWithAskAI = forwardRef<
composerActions,
domain,
renderActions,
setInitialInput,
...props
},
ref
forwardedRef
) => {
const ref = useRef<HTMLDivElement>(null);

const [askAI, setAskAI] = useControllableState<boolean>({
defaultProp: defaultAskAI,
prop: askAIProp,
onChange: setAskAIProp,
});
const { filters, handlePopState: handlePopFilters } = useFacetFilters();

function glow() {
if (ref.current) {
ref.current.animate(
{
boxShadow: [
"0 0 0px var(--accent-a5), var(--cmdk-shadow)",
"0 0 75px var(--accent-a5), var(--cmdk-shadow)",
"0 0 150px transparent, var(--cmdk-shadow)",
],
},
{ duration: 800, easing: "ease-out" }
);
}
}

// animate on presence
useEffect(() => {
if (ref.current) {
ref.current.animate(
{ transform: ["scale(0.96)", "scale(1)"] },
{ duration: 100, easing: "cubic-bezier(0.25, 0.46, 0.45, 0.94)" }
);

if (askAI) {
glow();
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

// bounce on action
function bounce() {
if (ref.current) {
ref.current.animate(
{ transform: ["scale(1)", "scale(0.96)", "scale(1)"] },
{ duration: 200, easing: "cubic-bezier(0.25, 0.46, 0.45, 0.94)" }
);
}
}

return (
<DesktopCommandRoot
label={askAI ? "Ask AI" : "Search"}
{...props}
ref={ref}
ref={composeRefs(forwardedRef, ref)}
shouldFilter={!askAI}
disableAutoSelection={askAI}
onPopState={
Expand All @@ -121,14 +167,18 @@ export const DesktopCommandWithAskAI = forwardRef<
}
onEscapeKeyDown={props.onEscapeKeyDown}
escapeKeyShouldPopState={!askAI && filters.length > 0}
data-mode={askAI ? "ask-ai" : "search"}
>
{askAI ? (
<DesktopAskAIContent
api={api}
suggestionsApi={suggestionsApi}
body={body}
headers={headers}
onReturnToSearch={() => setAskAI(false)}
onReturnToSearch={() => {
setAskAI(false);
bounce();
}}
initialInput={initialInput}
chatId={chatId}
onSelectHit={onSelectHit}
Expand All @@ -138,7 +188,18 @@ export const DesktopCommandWithAskAI = forwardRef<
renderActions={renderActions}
/>
) : (
<DesktopCommandContent>{children}</DesktopCommandContent>
<DesktopCommandContent>
<CommandAskAIGroup
onAskAI={(initialInput) => {
setInitialInput?.(initialInput);
setAskAI(true);
bounce();
glow();
}}
forceMount
/>
{children}
</DesktopCommandContent>
)}
</DesktopCommandRoot>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,23 @@ const DesktopCommand = forwardRef<
DesktopCommandProps & ComponentPropsWithoutRef<typeof DesktopCommandRoot>
>(({ onPopState, children, placeholder, ...props }, forwardedRef) => {
const { filters, handlePopState: handlePopFilters } = useFacetFilters();
const ref = useRef<HTMLDivElement>(null);

// animate on presence
useEffect(() => {
if (ref.current) {
ref.current.animate(
{ transform: ["scale(0.96)", "scale(1)"] },
{ duration: 100, easing: "cubic-bezier(0.25, 0.46, 0.45, 0.94)" }
);
}
}, []);

return (
<DesktopCommandRoot
label="Search"
{...props}
ref={forwardedRef}
ref={composeRefs(forwardedRef, ref)}
onPopState={composeEventHandlers(onPopState, handlePopFilters, {
checkForDefaultPrevented: false,
})}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,7 @@ export const DesktopSearchDialog = memo(

<Dialog.Content
className={cn(
"fixed left-1/2 top-[15%] w-[640px] origin-left -translate-x-1/2 overflow-hidden shadow-xl outline-none",
"before:pointer-events-none before:absolute before:inset-0 before:-z-50 before:bg-[var(--white-a9)] dark:before:bg-[var(--black-a9)]"
"fixed left-1/2 top-[15%] w-[640px] origin-center -translate-x-1/2 outline-none"
)}
asChild={asChild}
onEscapeKeyDown={(e) => {
Expand Down
25 changes: 24 additions & 1 deletion packages/fern-docs/search-ui/src/components/desktop/desktop.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,26 @@
border: 1px solid var(--grayscale-a6);
background-color: var(--grayscale-a1);
backdrop-filter: blur(40px);
border-radius: 0.5rem;
border-radius: 0.75rem;
transition: transform 100ms ease;
position: relative;

--cmdk-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
box-shadow: var(--cmdk-shadow);

&::before {
content: "";
pointer-events: none;
position: absolute;
inset: 0;
z-index: -50;
background: var(--white-a9);
backdrop-filter: blur(40px);

:is(.dark) & {
background: var(--black-a9);
}
}

:is(.dark) & {
background-color: var(--grayscale-a2);
Expand Down Expand Up @@ -83,6 +102,10 @@
}
}

&[data-mode="ask-ai"] [data-cmdk-list] {
height: 400px;
}

[data-cmdk-list-sizer] {
padding: 0.75rem 0.5rem;

Expand Down
1 change: 1 addition & 0 deletions packages/fern-docs/ui/src/search/SearchV2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ export function SearchV2(): ReactElement | false {
api={chatEndpoint}
suggestionsApi={suggestEndpoint}
initialInput={initialInput}
setInitialInput={setInitialInput}
body={{ algoliaSearchKey: apiKey }}
onSelectHit={handleNavigate}
onEscapeKeyDown={() => setOpen(false)}
Expand Down

0 comments on commit 5975c21

Please sign in to comment.