diff --git a/.changeset/pre.json b/.changeset/pre.json index ec9e135..3392213 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -16,6 +16,8 @@ "neat-tables-occur", "pretty-worms-help", "proud-brooms-laugh", + "tall-rocks-hear", + "tricky-carpets-fly", "unlucky-keys-join", "weak-swans-heal" ] diff --git a/.changeset/tall-rocks-hear.md b/.changeset/tall-rocks-hear.md new file mode 100644 index 0000000..c00e3ba --- /dev/null +++ b/.changeset/tall-rocks-hear.md @@ -0,0 +1,7 @@ +--- +'pliny': patch +--- + +Remove next/dynamic from search component +Remove redundant context components +load kbar data in advance and refactor code diff --git a/.changeset/tricky-carpets-fly.md b/.changeset/tricky-carpets-fly.md new file mode 100644 index 0000000..21c1139 --- /dev/null +++ b/.changeset/tricky-carpets-fly.md @@ -0,0 +1,5 @@ +--- +'pliny': patch +--- + +Fix newsletter typo diff --git a/packages/pliny/CHANGELOG.md b/packages/pliny/CHANGELOG.md index afd2c69..cc13055 100644 --- a/packages/pliny/CHANGELOG.md +++ b/packages/pliny/CHANGELOG.md @@ -1,5 +1,14 @@ # pliny +## 0.1.0-beta.7 + +### Patch Changes + +- 33d15e3: Remove next/dynamic from search component + Remove redundant context components + load kbar data in advance and refactor code +- 7f71035: Fix newsletter typo + ## 0.1.0-beta.6 ### Patch Changes diff --git a/packages/pliny/add-use-client.mjs b/packages/pliny/add-use-client.mjs index db8ff92..26d62f9 100644 --- a/packages/pliny/add-use-client.mjs +++ b/packages/pliny/add-use-client.mjs @@ -8,7 +8,10 @@ import globby from 'globby' 'comments/Disqus.js', 'comments/Giscus.js', 'comments/Utterances.js', - 'search/*.js', + 'search/Algolia.js', + 'search/KBar.js', + 'search/KBarModal.js', + 'search/KBarPortal.js', 'ui/NewsletterForm.js', 'ui/Pre.js', ]) diff --git a/packages/pliny/package.json b/packages/pliny/package.json index fe00ebc..27542dc 100644 --- a/packages/pliny/package.json +++ b/packages/pliny/package.json @@ -2,7 +2,7 @@ "name": "pliny", "description": "Main entry point for pliny components", "homepage": "https://github.com/timlrx/pliny", - "version": "0.1.0-beta.6", + "version": "0.1.0-beta.7", "type": "module", "exports": { "./*": "./*", diff --git a/packages/pliny/src/newsletter/index.ts b/packages/pliny/src/newsletter/index.ts index 952a0b5..aed42a9 100644 --- a/packages/pliny/src/newsletter/index.ts +++ b/packages/pliny/src/newsletter/index.ts @@ -54,7 +54,7 @@ async function NewsletterAPIHandler( if (response.status >= 400) { res.status(response.status).json({ error: `There was an error subscribing to the list.` }) } - res.status(201).json({ error: '' }) + res.status(201).json({ message: 'Successfully subscribed to the newsletter' }) } catch (error) { res.status(500).json({ error: error.message || error.toString() }) } @@ -89,18 +89,16 @@ async function NewsletterRouteHandler(req: NextRequest, options: NewsletterConfi default: return NextResponse.json({ error: `${options.provider} not supported` }, { status: 500 }) } - if (response.status == 200) { - return NextResponse.json( - { message: 'Successfully subscribed to the newsletter' }, - { status: response.status } - ) - } else if (response.status >= 400) { + if (response.status >= 400) { return NextResponse.json( { error: `There was an error subscribing to the list` }, { status: response.status } ) } - return NextResponse.json({ error: '' }, { status: 201 }) + return NextResponse.json( + { message: 'Successfully subscribed to the newsletter' }, + { status: 201 } + ) } catch (error) { return NextResponse.json({ error: error.message || error.toString() }, { status: 500 }) } diff --git a/packages/pliny/src/search/Algolia.tsx b/packages/pliny/src/search/Algolia.tsx index 22064ff..49b2548 100644 --- a/packages/pliny/src/search/Algolia.tsx +++ b/packages/pliny/src/search/Algolia.tsx @@ -61,6 +61,15 @@ export const AlgoliaSearchContext = React.createContext( {} as AlgoliaSearchContext ) +/** + * Command palette like search component for algolia - `ctrl-k` to open the palette. + * To toggle the modal or search from child components, use the search context: + * ``` + * import { AlgoliaSearchContext } from 'pliny/search/algolia' + * const { query } = useContext(AlgoliaSearchContext) + * ``` + * + */ export const AlgoliaSearchProvider: React.FC> = ( props ) => { diff --git a/packages/pliny/src/search/KBar.tsx b/packages/pliny/src/search/KBar.tsx index 817668e..2dc9c83 100644 --- a/packages/pliny/src/search/KBar.tsx +++ b/packages/pliny/src/search/KBar.tsx @@ -1,7 +1,9 @@ -import { useState, useEffect, useCallback, FC, ReactNode } from 'react' +import { useState, useEffect, useCallback, FC, ReactNode, useMemo } from 'react' import type { Action } from 'kbar' import { useRouter } from 'next/navigation.js' import { KBarModal as KBarModalType } from './KBarModal' +import { CoreContent, MDXDocument } from '../utils/contentlayer' +import { formatDate } from '../utils/formatDate' export interface KBarSearchProps { searchDocumentsPath: string @@ -15,13 +17,41 @@ export interface KBarConfig { let KBarModal: typeof KBarModalType | null = null +/** + * Command palette like search component with kbar - `ctrl-k` to open the palette. + * To toggle the modal or search from child components, use the search context: + * ``` + * import { useKBar } from 'kbar' + * const { query } = useKBar() + * ``` + * See https://github.com/timc1/kbar/blob/main/src/types.ts#L98-L106 for typings. + * + * @param {*} { kbarConfig, children } + * @return {*} + */ export const KBarSearchProvider: FC<{ children: ReactNode kbarConfig: KBarSearchProps }> = ({ kbarConfig, children }) => { const router = useRouter() const { searchDocumentsPath, defaultActions } = kbarConfig - const [loaded, setLoaded] = useState(false) + const [searchActions, setSearchActions] = useState([]) + const [modalLoaded, setModalLoaded] = useState(false) + const [dataLoaded, setDataLoaded] = useState(false) + + const startingActions = useMemo(() => { + return Array.isArray(defaultActions) + ? defaultActions + : [ + { + id: 'homepage', + name: 'Homepage', + keywords: '', + section: 'Home', + perform: () => router.push('/'), + }, + ] + }, [defaultActions, router]) const importDocSearchModalIfNeeded = useCallback(() => { if (KBarModal) { @@ -37,34 +67,59 @@ export const KBarSearchProvider: FC<{ if (event.ctrlKey && event.key === 'k') { event.preventDefault() importDocSearchModalIfNeeded().then(() => { - setLoaded(true) + setModalLoaded(true) window.removeEventListener('keydown', handleKeyDown) }) } } - if (!loaded) window.addEventListener('keydown', handleKeyDown) + const mapPosts = (posts: CoreContent[]) => { + const actions: Action[] = [] + for (const post of posts) { + actions.push({ + id: post.path, + name: post.title, + keywords: post?.summary || '', + section: 'Content', + subtitle: formatDate(post.date, 'en-US'), + perform: () => router.push('/' + post.path), + }) + } + return actions + } + async function fetchData() { + const res = await fetch(searchDocumentsPath) + const json = await res.json() + const actions = mapPosts(json) + setSearchActions(actions) + setDataLoaded(true) + } + if (!modalLoaded) { + window.addEventListener('keydown', handleKeyDown) + } + if (!dataLoaded) { + fetchData() + } return () => { /*removes event listener on cleanup*/ window.removeEventListener('keydown', handleKeyDown) } - }, [importDocSearchModalIfNeeded, loaded]) - - const startingActions: Action[] = Array.isArray(defaultActions) - ? defaultActions - : [ - { - id: 'homepage', - name: 'Homepage', - keywords: '', - section: 'Home', - perform: () => router.push('/'), - }, - ] + }, [ + importDocSearchModalIfNeeded, + modalLoaded, + dataLoaded, + startingActions, + router, + searchDocumentsPath, + ]) return ( <> - {loaded && KBarModal ? ( - + {modalLoaded && KBarModal ? ( + {children} ) : ( diff --git a/packages/pliny/src/search/KBarModal.tsx b/packages/pliny/src/search/KBarModal.tsx index c6ae9fa..25d24e4 100644 --- a/packages/pliny/src/search/KBarModal.tsx +++ b/packages/pliny/src/search/KBarModal.tsx @@ -4,12 +4,13 @@ import { Portal } from './KBarPortal' export const KBarModal: FC<{ children: ReactNode - startingActions: Action[] + actions: Action[] searchDocumentsPath: string -}> = ({ startingActions, searchDocumentsPath, children }) => { + isLoading: boolean +}> = ({ actions, children, isLoading }) => { return ( - - + + {children} ) diff --git a/packages/pliny/src/search/KBarPortal.tsx b/packages/pliny/src/search/KBarPortal.tsx index 8f62959..b64cecd 100644 --- a/packages/pliny/src/search/KBarPortal.tsx +++ b/packages/pliny/src/search/KBarPortal.tsx @@ -1,7 +1,5 @@ -import React, { useState, useEffect } from 'react' -import { useRouter } from 'next/navigation.js' -import { CoreContent, MDXDocument } from '../utils/contentlayer' -import { formatDate } from '../utils/formatDate' +import { useEffect } from 'react' + import { KBarPortal, KBarSearch, @@ -9,18 +7,13 @@ import { KBarPositioner, KBarResults, useMatches, - useRegisterActions, useKBar, - Action, - ActionImpl, } from 'kbar' let init = false -export const Portal = ({ searchDocumentsPath }: { searchDocumentsPath: string }) => { - const [searchActions, setSearchActions] = useState([]) +export const Portal = ({ isLoading }: { isLoading: boolean }) => { const { query } = useKBar() - const router = useRouter() // Display on load as we already wait for crtl+k event to load it useEffect(() => { @@ -28,34 +21,7 @@ export const Portal = ({ searchDocumentsPath }: { searchDocumentsPath: string }) init = true query.toggle() } - }, []) - - useEffect(() => { - const mapPosts = (posts: CoreContent[]) => { - const actions: Action[] = [] - for (const post of posts) { - actions.push({ - id: post.path, - name: post.title, - keywords: post?.summary || '', - section: 'Content', - subtitle: formatDate(post.date, 'en-US'), - perform: () => router.push('/' + post.path), - }) - } - return actions - } - - async function fetchData() { - const res = await fetch(searchDocumentsPath) - const json = await res.json() - const actions = mapPosts(json) - setSearchActions(actions) - } - fetchData() - }, [searchDocumentsPath]) - - useRegisterActions(searchActions, [searchActions]) + }, [query]) return ( @@ -84,7 +50,12 @@ export const Portal = ({ searchDocumentsPath }: { searchDocumentsPath: string }) ESC - + {!isLoading && } + {isLoading && ( +
+ Loading... +
+ )} @@ -92,14 +63,6 @@ export const Portal = ({ searchDocumentsPath }: { searchDocumentsPath: string }) ) } -interface RenderParams { - item: T - active: boolean -} - -// The default Kbar results component has some issues with preact. -// https://github.com/timc1/kbar/issues/208 -// Using custom, non-virtualized implementation in the meantime. const RenderResults = () => { const { results } = useMatches() @@ -111,7 +74,7 @@ const RenderResults = () => {
{typeof item === 'string' ? (
-
+
{item}
diff --git a/packages/pliny/src/search/index.tsx b/packages/pliny/src/search/index.tsx index c3d8adf..6904e60 100644 --- a/packages/pliny/src/search/index.tsx +++ b/packages/pliny/src/search/index.tsx @@ -1,62 +1,35 @@ import React from 'react' -import dynamic from 'next/dynamic.js' -// import { AlgoliaSearchContext } from './Algolia' -// import { KBarContext } from 'kbar' +import { AlgoliaSearchProvider } from './Algolia' +import { KBarSearchProvider } from './KBar' import type { AlgoliaConfig } from './Algolia' import type { KBarConfig } from './KBar' export type SearchConfig = AlgoliaConfig | KBarConfig - export interface SearchConfigProps { searchConfig: SearchConfig children: React.ReactNode } -export interface SearchQuery { - setSearch: (search: string) => void - toggle: () => void -} - -export interface SearchContext { - query: SearchQuery -} - -const AlgoliaSearchProvider = dynamic( - () => { - return import('./Algolia').then((mod) => mod.AlgoliaSearchProvider) - }, - { ssr: false } -) - -const KBarSearchProvider = dynamic( - () => { - return import('./KBar').then((mod) => mod.KBarSearchProvider) - }, - { ssr: false } -) - -const KBarContext = dynamic( - // @ts-ignore - () => { - return import('kbar').then((mod) => mod.KBarContext) - }, - { ssr: false } -) - -const AlgoliaSearchContext = dynamic( - // @ts-ignore - () => { - return import('./Algolia').then((mod) => mod.AlgoliaSearchContext) - }, - { ssr: false } -) - /** * Command palette like search component - `ctrl-k` to open the palette. * Or use the search context to bind toggle to an onOpen event. * Currently supports Algolia or Kbar (local search). * + * To toggle the modal or search from child components, use the search context: + * + * For Algolia: + * ``` + * import { AlgoliaSearchContext } from 'pliny/search/algolia' + * const { query } = useContext(AlgoliaSearchContext) + * ``` + * + * For Kbar: + * ``` + * import { useKBar } from 'kbar' + * const { query } = useKBar() + * ``` + * * @param {SearchConfig} searchConfig * @return {*} */ @@ -81,14 +54,3 @@ export const SearchProvider = ({ searchConfig, children }: SearchConfigProps) => return <>{children} } } - -export const SearchContext = (provider: string): React.Context => { - switch (provider) { - case 'algolia': - // @ts-ignore - return AlgoliaSearchContext - case 'kbar': - // @ts-ignore - return KBarContext - } -} diff --git a/packages/pliny/src/ui/NewsletterForm.tsx b/packages/pliny/src/ui/NewsletterForm.tsx index 54d9ae0..ad81594 100644 --- a/packages/pliny/src/ui/NewsletterForm.tsx +++ b/packages/pliny/src/ui/NewsletterForm.tsx @@ -37,7 +37,6 @@ const NewsletterForm = ({ inputEl.current.value = '' setError(false) setSubscribed(true) - setMessage('Successfully! 🎉 You are now subscribed.') } return (