@openrails/contentkit-ui 0.98.5 → 0.99.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -0
- package/dist/client/content/comments.d.ts +4 -2
- package/dist/client/content/comments.js +10 -6
- package/dist/client/content/comments.js.map +1 -1
- package/dist/client/generated/routes.d.ts +28 -2
- package/dist/client/generated/routes.js +39 -2
- package/dist/client/generated/routes.js.map +1 -1
- package/dist/client/generated/wire.d.ts +85 -0
- package/dist/client/generated/wire.js.map +1 -1
- package/dist/client/media/client.d.ts +4 -0
- package/dist/client/media/client.js +8 -4
- package/dist/client/media/client.js.map +1 -1
- package/dist/client/media/queue.js +42 -26
- package/dist/client/media/queue.js.map +1 -1
- package/dist/client/media/rules.d.ts +6 -0
- package/dist/client/media/rules.js +64 -10
- package/dist/client/media/rules.js.map +1 -1
- package/dist/client/versions.d.ts +6 -2
- package/dist/client/versions.js +26 -0
- package/dist/client/versions.js.map +1 -1
- package/dist/components/admin/page-review.d.ts +13 -0
- package/dist/components/admin/page-review.js +216 -0
- package/dist/components/admin/page-review.js.map +1 -0
- package/dist/components/content/card.js +98 -103
- package/dist/components/content/card.js.map +1 -1
- package/dist/components/content/daily-releases.d.ts +8 -1
- package/dist/components/content/daily-releases.js +291 -133
- package/dist/components/content/daily-releases.js.map +1 -1
- package/dist/components/content/editors.js +216 -210
- package/dist/components/content/editors.js.map +1 -1
- package/dist/components/content/filter-dialog.d.ts +11 -0
- package/dist/components/content/filter-dialog.js +29 -0
- package/dist/components/content/filter-dialog.js.map +1 -0
- package/dist/components/content/filters.d.ts +10 -0
- package/dist/components/content/filters.js +5 -2
- package/dist/components/content/filters.js.map +1 -1
- package/dist/components/content/grid.js +19 -14
- package/dist/components/content/grid.js.map +1 -1
- package/dist/components/content/index.d.ts +2 -1
- package/dist/components/content/release-reveal.d.ts +5 -0
- package/dist/components/content/release-reveal.js +62 -0
- package/dist/components/content/release-reveal.js.map +1 -0
- package/dist/components/content/search.js +95 -107
- package/dist/components/content/search.js.map +1 -1
- package/dist/components/social/comments.d.ts +1 -1
- package/dist/components/social/comments.js +15 -15
- package/dist/components/social/comments.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +24 -22
- package/dist/locales/admin/de.js +23 -0
- package/dist/locales/admin/de.js.map +1 -1
- package/dist/locales/admin/en.d.ts +23 -0
- package/dist/locales/admin/en.js +23 -0
- package/dist/locales/admin/en.js.map +1 -1
- package/dist/locales/admin/es.js +23 -0
- package/dist/locales/admin/es.js.map +1 -1
- package/dist/locales/admin/ja.js +23 -0
- package/dist/locales/admin/ja.js.map +1 -1
- package/dist/locales/admin/ko.js +23 -0
- package/dist/locales/admin/ko.js.map +1 -1
- package/dist/locales/admin/zh.js +23 -0
- package/dist/locales/admin/zh.js.map +1 -1
- package/dist/locales/discovery/de.js +11 -0
- package/dist/locales/discovery/de.js.map +1 -1
- package/dist/locales/discovery/en.d.ts +11 -0
- package/dist/locales/discovery/en.js +11 -0
- package/dist/locales/discovery/en.js.map +1 -1
- package/dist/locales/discovery/es.js +11 -0
- package/dist/locales/discovery/es.js.map +1 -1
- package/dist/locales/discovery/ja.js +11 -0
- package/dist/locales/discovery/ja.js.map +1 -1
- package/dist/locales/discovery/ko.js +11 -0
- package/dist/locales/discovery/ko.js.map +1 -1
- package/dist/locales/discovery/zh.js +11 -0
- package/dist/locales/discovery/zh.js.map +1 -1
- package/dist/locales/en.d.ts +34 -0
- package/dist/locales/social/en.js +2 -2
- package/dist/locales/social/en.js.map +1 -1
- package/dist/react/comments.d.ts +2 -1
- package/dist/react/comments.js +10 -8
- package/dist/react/comments.js.map +1 -1
- package/dist/react/folder.js +39 -39
- package/dist/react/folder.js.map +1 -1
- package/dist/react/index.d.ts +1 -0
- package/dist/react/page-review.d.ts +14 -0
- package/dist/react/page-review.js +36 -0
- package/dist/react/page-review.js.map +1 -0
- package/dist/react/public.d.ts +3 -1
- package/dist/react/public.js +76 -72
- package/dist/react/public.js.map +1 -1
- package/dist/react/resources.d.ts +5 -13
- package/dist/react/resources.js +58 -49
- package/dist/react/resources.js.map +1 -1
- package/dist/react/use-resource.d.ts +1 -1
- package/dist/react/use-resource.js +23 -13
- package/dist/react/use-resource.js.map +1 -1
- package/dist/react/versions.js +70 -42
- package/dist/react/versions.js.map +1 -1
- package/dist/react.js +8 -7
- package/dist/styles.css +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"grid.js","names":[],"sources":["../../../src/components/content/grid.tsx"],"sourcesContent":["import { cn } from \"cn\";\nimport { useContext, useEffect, useMemo, useState, type CSSProperties, type ReactNode } from \"react\";\nimport type { ContentKitUiAppearance } from \"../../appearance.js\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport type { ContentQuery, HistoryQuery, RailQuery, SearchQuery } from \"../../client/discovery.js\";\nimport type { Card } from \"../../client/generated/wire.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { ContentKitContext } from \"../../react/context.js\";\nimport { useContents, useExposure, useHistory, useLockedCount, useRecommendations, useSearch, useSimilar, type UseRail } from \"../../react/discovery.js\";\nimport { useKinds } from \"../../react/groups.js\";\nimport type { UseList } from \"../../react/use-resource.js\";\nimport { useNearViewport } from \"../../react/viewport.js\";\nimport { ContentKitUiRoot } from \"../../scope.js\";\nimport { Button } from \"#ckui/ui/button\";\nimport { Skeleton } from \"#ckui/ui/skeleton\";\nimport { contentError, useAction } from \"../social/parts.js\";\nimport { ContentCard, type ContentCardProps } from \"./card.js\";\n\nexport interface CardListProps extends Omit<ContentCardProps, \"card\" | \"onOpen\" | \"children\" | \"className\"> {\n /** Where the list is, for exposures and click attribution: \"home\", \"search\", \"similar\". */\n surface?: string;\n /** Loads the next page as the end nears, instead of a button. */\n infinite?: boolean;\n /** A card's entry instead of ContentCard. */\n renderCard?: (card: Card, position: number) => ReactNode;\n /** Shown when nothing matches. */\n empty?: ReactNode;\n pageSize?: number;\n client?: ContentKitClient;\n className?: string;\n appearance?: ContentKitUiAppearance;\n /** Opt into a fluid grid, optionally with a desktop column ceiling. */\n grid?: CardGridLayout;\n}\n\nconst GRID = \"grid grid-cols-[repeat(auto-fill,minmax(min(100%,10rem),1fr))] gap-x-4 gap-y-6\";\n\nexport interface CardGridLayout {\n /** Minimum desktop card width: CSS pixels or a CSS length such as clamp(...). Cards fill the available space. */\n minCardWidth: number | string;\n /** Maximum desktop columns. Omit to use all available width without a ceiling. */\n maxColumns?: number;\n /** Equal-width columns below the desktop breakpoint. Default 2. */\n mobileColumns?: number;\n}\n\n/** Shared by complete lists, release-day sections and grid-style previews. */\nexport function cardGridLayout(layout?: CardGridLayout): { className: string; style?: CSSProperties } {\n if (!layout) return { className: GRID };\n const width = typeof layout.minCardWidth === \"string\" ? layout.minCardWidth.trim() || \"160px\" : `${Number.isFinite(layout.minCardWidth) ? Math.max(1, layout.minCardWidth) : 160}px`;\n const columns = layout.mobileColumns !== undefined && Number.isFinite(layout.mobileColumns) ? Math.max(1, Math.floor(layout.mobileColumns)) : 2;\n const maximum = layout.maxColumns !== undefined && Number.isFinite(layout.maxColumns) ? Math.max(1, Math.floor(layout.maxColumns)) : undefined;\n const trackWidth = maximum === undefined ? width : `max(${width}, calc((100% - ${maximum - 1}rem) / ${maximum}))`;\n return {\n className: \"grid grid-cols-[repeat(var(--ckui-card-mobile-columns),minmax(0,1fr))] gap-x-4 gap-y-6 md:grid-cols-[repeat(auto-fill,minmax(min(100%,var(--ckui-card-min-width)),1fr))]\",\n style: { \"--ckui-card-min-width\": trackWidth, \"--ckui-card-mobile-columns\": columns } as CSSProperties,\n };\n}\n\n/** A list's cards in a responsive grid, paged, with exposures and click signals. */\nexport function CardGrid({ list, render, ...p }: CardListProps & { list: UseList<Card>; render: string }) {\n const m = useMessages();\n const { t } = m;\n const { kinds } = useKinds({ client: p.client });\n const exposure = useExposure(list.items, { surface: p.surface ?? \"list\", render, client: p.client });\n const [sentinel, near] = useNearViewport({ margin: \"600px 0px\", leaveMargin: \"1200px 0px\" });\n const { hasMore, loadingMore, loadMore } = list;\n useEffect(() => {\n if (p.infinite && near && hasMore && !loadingMore) void loadMore();\n }, [p.infinite, near, hasMore, loadingMore, loadMore]);\n const tiers = p.tiers ?? kinds?.tiers;\n const layout = cardGridLayout(p.grid);\n return (\n <ContentKitUiRoot appearance={p.appearance} className={cn(\"grid gap-4 text-sm\", p.className)} data-ckui=\"content-grid\" aria-busy={list.loading}>\n {list.loading && !list.items.length ? (\n <div className={layout.className} style={layout.style}>\n {Array.from({ length: 8 }, (_, i) => (\n <Skeleton key={i} className={p.aspectRatio === undefined ? p.shape === \"landscape\" ? \"aspect-video w-full\" : \"aspect-[3/4] w-full\" : \"w-full\"} style={p.aspectRatio === undefined ? undefined : { aspectRatio: p.aspectRatio }} />\n ))}\n </div>\n ) : list.error && !list.items.length ? (\n <div role=\"alert\" className=\"grid justify-items-start gap-2\">\n <p className=\"text-destructive\">{contentError(m, list.error)}</p>\n <Button size=\"sm\" variant=\"outline\" onClick={list.reload}>\n {t(\"common.retry\")}\n </Button>\n </div>\n ) : !list.items.length ? (\n <div className=\"text-muted-foreground\" role=\"status\">\n {p.empty ?? t(\"contents.empty\")}\n </div>\n ) : (\n <ol className={layout.className} style={layout.style}>\n {list.items.map((card, i) => (\n <li key={card.id} ref={exposure.track(card.id, i + 1)}>\n {p.renderCard ? (\n p.renderCard(card, i + 1)\n ) : (\n <ContentCard\n card={card}\n client={p.client}\n href={p.href}\n shape={p.shape}\n aspectRatio={p.aspectRatio}\n titlePosition={p.titlePosition}\n loadMedia={p.loadMedia}\n groupKinds={p.groupKinds}\n chips={p.chips}\n filterEntity={p.filterEntity}\n entityStyle={p.entityStyle}\n entityLayout={p.entityLayout}\n metadata={p.metadata}\n showCounts={p.showCounts}\n tiers={tiers}\n labels={p.labels}\n owner={p.owner}\n ownerHref={p.ownerHref}\n decorate={p.decorate}\n onOpen={() => exposure.click(card.id, i + 1)}\n />\n )}\n </li>\n ))}\n </ol>\n )}\n {list.hasMore && (\n <div ref={sentinel} className=\"grid justify-items-center\">\n <Button variant=\"outline\" size=\"sm\" disabled={list.loadingMore} onClick={() => void list.loadMore()}>\n {list.loadingMore ? t(\"contents.loading\") : t(\"contents.loadMore\")}\n </Button>\n </div>\n )}\n </ContentKitUiRoot>\n );\n}\n\n/**\n * What a list does with cards the reader doesn't open: shows them marked\n * (show), leaves them out (hide), or lists them apart, counted, behind a\n * toggle that loads them when opened (collapse).\n */\nexport type LockedCards = \"show\" | \"hide\" | \"collapse\";\n\nconst LOCKED_LEVELS = [\"tier\", \"group\", \"ppv\", \"group_ppv\"] as const;\n\n/** Contents as cards (GET /v1/contents): any sort and filter, paged. */\nexport function ContentGrid({ query, locked = \"show\", ...p }: CardListProps & { query: ContentQuery; locked?: LockedCards }) {\n const q = useMemo<ContentQuery>(\n () => ({ ...query, limit: query.limit ?? p.pageSize, ...(locked === \"show\" ? {} : { mode: \"hide\" }) }),\n [query, p.pageSize, locked],\n );\n const list = useContents(q, { client: p.client });\n return (\n <>\n {locked === \"collapse\" && <LockedSection {...p} query={query} />}\n <CardGrid {...p} list={list} render={JSON.stringify(q)} />\n </>\n );\n}\n\n/** A list's locked cards, counted, loaded and shown on opening. */\nfunction LockedSection({ query, ...p }: CardListProps & { query: ContentQuery }) {\n const m = useMessages();\n const [open, setOpen] = useState(false);\n const { count } = useLockedCount(query, { client: p.client });\n const q = useMemo<ContentQuery | null>(\n () => (open ? { ...query, mode: \"lock\", access: query.access ?? LOCKED_LEVELS, limit: query.limit ?? p.pageSize } : null),\n [open, query, p.pageSize],\n );\n const list = useContents(q, { client: p.client });\n const items = useMemo(() => list.items.filter((c) => !c.access.allowed), [list.items]);\n if (!count) return null;\n return (\n <ContentKitUiRoot appearance={p.appearance} className=\"grid gap-3 text-sm\" data-ckui=\"locked-cards\">\n <Button variant=\"outline\" size=\"sm\" className=\"justify-self-start\" aria-expanded={open} onClick={() => setOpen(!open)}>\n {open ? m.t(\"contents.lockedHide\") : m.plural(\"contents.lockedShow\", count)}\n </Button>\n {open && <CardGrid {...p} list={{ ...list, items }} render={JSON.stringify(q)} />}\n </ContentKitUiRoot>\n );\n}\n\n/** Search results as cards; nothing until the query has text. */\nexport function SearchResults({ query, ...p }: CardListProps & { query: SearchQuery }) {\n const { t } = useMessages();\n const q = useMemo(() => ({ ...query, limit: query.limit ?? p.pageSize }), [query, p.pageSize]);\n const list = useSearch(q, { client: p.client });\n if (!query.q.trim()) return null;\n return <CardGrid surface=\"search\" empty={t(\"contents.noResults\", { q: query.q.trim() })} {...p} list={list} render={JSON.stringify(q)} />;\n}\n\n/** The signed-in reader's history as cards, each removable, with a clear-all. */\nexport function ContentHistory({ query = {}, ...p }: CardListProps & { query?: HistoryQuery }) {\n const { t } = useMessages();\n const ctx = useContext(ContentKitContext);\n const q = useMemo(() => ({ ...query, limit: query.limit ?? p.pageSize }), [query, p.pageSize]);\n const list = useHistory(q, { client: p.client });\n const forget = useAction(\"history.forget\");\n if (ctx?.viewer === null) return <p className=\"text-muted-foreground\">{t(\"contents.historySignedOut\")}</p>;\n return (\n <div className=\"grid gap-3\" data-ckui=\"content-history\">\n {list.items.length > 0 && (\n <Button size=\"sm\" variant=\"outline\" className=\"justify-self-end\" disabled={forget.pending} onClick={() => void forget.run(() => list.forget(query.kind ? { kind: query.kind } : {}))}>\n {t(\"contents.clearHistory\")}\n </Button>\n )}\n <CardGrid\n surface=\"history\"\n empty={t(\"contents.historyEmpty\")}\n {...p}\n list={list}\n render={JSON.stringify(q)}\n renderCard={\n p.renderCard ??\n ((card) => (\n <ContentCard\n card={card}\n client={p.client}\n href={p.href}\n shape={p.shape}\n aspectRatio={p.aspectRatio}\n titlePosition={p.titlePosition}\n loadMedia={p.loadMedia}\n groupKinds={p.groupKinds}\n chips={p.chips}\n filterEntity={p.filterEntity}\n entityStyle={p.entityStyle}\n entityLayout={p.entityLayout}\n metadata={p.metadata}\n showCounts={p.showCounts}\n tiers={p.tiers}\n labels={p.labels}\n owner={p.owner}\n ownerHref={p.ownerHref}\n decorate={p.decorate}\n >\n <Button size=\"xs\" variant=\"ghost\" className=\"justify-self-start\" disabled={forget.pending} onClick={() => void forget.run(() => list.forget({ content_id: card.id }))}>\n {t(\"contents.removeFromHistory\")}\n </Button>\n </ContentCard>\n ))\n }\n />\n </div>\n );\n}\n\nexport interface ContentRailProps extends Omit<CardListProps, \"infinite\" | \"empty\"> {\n /** The rail's heading. */\n title: ReactNode;\n /** Contents like this one. */\n similar?: string;\n /** For the signed-in reader. */\n recommendations?: boolean;\n /** The reader's history: { status: \"in_progress\" } is a continue-watching rail. */\n history?: HistoryQuery;\n /** An ordered group's members in its order: a featured rail (its windows apply). */\n group?: string;\n /** Any list: a sort and filters. */\n list?: ContentQuery;\n /** Filters and reader options of a similar or recommended rail. */\n query?: RailQuery;\n /** A link beside the heading, e.g. to the whole list. */\n more?: ReactNode;\n}\n\n/**\n * A horizontal row of contents: similar, recommended, the reader's history,\n * an ordered group's members or any list. It hides itself when it has none or\n * the site runs without what it needs.\n */\nexport function ContentRail({ title, similar, recommendations, history, group, list: listQuery, query = {}, more, ...p }: ContentRailProps) {\n const limit = query.limit ?? p.pageSize ?? 12;\n const q = useMemo(() => ({ ...query, limit }), [query, limit]);\n const listing = useMemo(\n () => (listQuery ? { ...listQuery, limit: listQuery.limit ?? limit } : group ? { groups: [group], sort: \"position\" as const, limit } : null),\n [listQuery, group, limit],\n );\n const like = useSimilar(recommendations || history || listing ? null : similar, q, { client: p.client });\n const mine = useRecommendations(recommendations ? q : null, { client: p.client });\n const seen = useHistory(history ? { ...history, limit: history.limit ?? limit } : null, { client: p.client });\n const listed = useContents(listing, { client: p.client });\n const rail: UseRail = recommendations\n ? mine\n : history || listing\n ? (() => {\n const l = history ? seen : listed;\n return { items: l.items, loading: l.loading, error: l.error, unavailable: l.error?.code === \"not_configured\", reload: l.reload };\n })()\n : like;\n const { kinds } = useKinds({ client: p.client });\n const surface = p.surface ?? (recommendations ? \"recommendations\" : history ? \"history\" : group ? \"featured\" : listing ? \"list\" : \"similar\");\n const exposure = useExposure(rail.items, { surface, render: JSON.stringify([similar, recommendations, history, group, listing, q]), client: p.client });\n const source = similar || recommendations || history || group || listing;\n const layout = p.grid ? cardGridLayout(p.grid) : undefined;\n if (!source || rail.unavailable || (!rail.loading && !rail.items.length)) return null;\n return (\n <ContentKitUiRoot appearance={p.appearance} className={cn(\"grid gap-3 text-sm\", p.className)} data-ckui=\"content-rail\" data-surface={surface}>\n <div className=\"flex items-baseline justify-between gap-2\">\n <h2 className=\"text-base font-semibold\">{title}</h2>\n {more}\n </div>\n <ol className={layout?.className ?? \"flex snap-x gap-4 overflow-x-auto pb-2\"} style={layout?.style}>\n {(rail.loading && !rail.items.length ? Array.from({ length: 6 }, () => null) : rail.items).map((card, i) => (\n <li key={card?.id ?? i} className={layout ? undefined : \"w-36 shrink-0 snap-start sm:w-44\"} ref={card ? exposure.track(card.id, i + 1) : undefined}>\n {!card ? (\n <Skeleton className={p.aspectRatio === undefined ? p.shape === \"landscape\" ? \"aspect-video w-full\" : \"aspect-[3/4] w-full\" : \"w-full\"} style={p.aspectRatio === undefined ? undefined : { aspectRatio: p.aspectRatio }} />\n ) : p.renderCard ? (\n p.renderCard(card, i + 1)\n ) : (\n <ContentCard\n card={card}\n client={p.client}\n href={p.href}\n shape={p.shape}\n aspectRatio={p.aspectRatio}\n titlePosition={p.titlePosition}\n loadMedia={p.loadMedia}\n groupKinds={p.groupKinds}\n chips={p.chips}\n filterEntity={p.filterEntity}\n entityStyle={p.entityStyle}\n entityLayout={p.entityLayout}\n metadata={p.metadata}\n showCounts={p.showCounts}\n tiers={p.tiers ?? kinds?.tiers}\n labels={p.labels}\n owner={p.owner}\n ownerHref={p.ownerHref}\n decorate={p.decorate}\n onOpen={() => exposure.click(card.id, i + 1)}\n />\n )}\n </li>\n ))}\n </ol>\n </ContentKitUiRoot>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"grid.js","names":[],"sources":["../../../src/components/content/grid.tsx"],"sourcesContent":["import { cn } from \"cn\";\nimport { useContext, useEffect, useMemo, useState, type CSSProperties, type ReactNode } from \"react\";\nimport type { ContentKitUiAppearance } from \"../../appearance.js\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport type { ContentQuery, HistoryQuery, RailQuery, SearchQuery } from \"../../client/discovery.js\";\nimport type { Card } from \"../../client/generated/wire.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { ContentKitContext } from \"../../react/context.js\";\nimport { useContents, useExposure, useHistory, useLockedCount, useRecommendations, useSearch, useSimilar, type UseRail } from \"../../react/discovery.js\";\nimport { useKinds } from \"../../react/groups.js\";\nimport type { UseList } from \"../../react/use-resource.js\";\nimport { useNearViewport } from \"../../react/viewport.js\";\nimport { ContentKitUiRoot } from \"../../scope.js\";\nimport { Button } from \"#ckui/ui/button\";\nimport { Skeleton } from \"#ckui/ui/skeleton\";\nimport { contentError, useAction } from \"../social/parts.js\";\nimport { ContentCard, type ContentCardProps } from \"./card.js\";\n\nexport interface CardListProps extends Omit<ContentCardProps, \"card\" | \"onOpen\" | \"children\" | \"className\"> {\n /** Where the list is, for exposures and click attribution: \"home\", \"search\", \"similar\". */\n surface?: string;\n /** Loads the next page as the end nears, instead of a button. */\n infinite?: boolean;\n /** A card's entry instead of ContentCard. */\n renderCard?: (card: Card, position: number) => ReactNode;\n /** Shown when nothing matches. */\n empty?: ReactNode;\n pageSize?: number;\n client?: ContentKitClient;\n className?: string;\n appearance?: ContentKitUiAppearance;\n /** Opt into a fluid grid, optionally with a desktop column ceiling. */\n grid?: CardGridLayout;\n}\n\nconst GRID = \"grid grid-cols-[repeat(auto-fill,minmax(min(100%,10rem),1fr))] gap-x-4 gap-y-6\";\n\nexport interface CardGridLayout {\n /** Minimum desktop card width: CSS pixels or a CSS length such as clamp(...). Cards fill the available space. */\n minCardWidth: number | string;\n /** Maximum desktop columns. Omit to use all available width without a ceiling. */\n maxColumns?: number;\n /** Equal-width columns below the desktop breakpoint. Default 2. */\n mobileColumns?: number;\n}\n\n/** Shared by complete lists, release-day sections and grid-style previews. */\nexport function cardGridLayout(layout?: CardGridLayout): { className: string; style?: CSSProperties } {\n if (!layout) return { className: GRID };\n const width = typeof layout.minCardWidth === \"string\" ? layout.minCardWidth.trim() || \"160px\" : `${Number.isFinite(layout.minCardWidth) ? Math.max(1, layout.minCardWidth) : 160}px`;\n const columns = layout.mobileColumns !== undefined && Number.isFinite(layout.mobileColumns) ? Math.max(1, Math.floor(layout.mobileColumns)) : 2;\n const maximum = layout.maxColumns !== undefined && Number.isFinite(layout.maxColumns) ? Math.max(1, Math.floor(layout.maxColumns)) : undefined;\n const trackWidth = maximum === undefined ? width : `max(${width}, calc((100% - ${maximum - 1}rem) / ${maximum}))`;\n return {\n className: \"grid grid-cols-[repeat(var(--ckui-card-mobile-columns),minmax(0,1fr))] gap-x-4 gap-y-6 md:grid-cols-[repeat(auto-fill,minmax(min(100%,var(--ckui-card-min-width)),1fr))]\",\n style: { \"--ckui-card-min-width\": trackWidth, \"--ckui-card-mobile-columns\": columns } as CSSProperties,\n };\n}\n\n/** A list's cards in a responsive grid, paged, with exposures and click signals. */\nexport function CardGrid({ list, render, ...p }: CardListProps & { list: UseList<Card>; render: string }) {\n const m = useMessages();\n const { t } = m;\n const { kinds } = useKinds({ client: p.client });\n const exposure = useExposure(list.items, { surface: p.surface ?? \"list\", render, client: p.client });\n const [sentinel, near] = useNearViewport({ margin: \"600px 0px\", leaveMargin: \"1200px 0px\" });\n const { hasMore, loadingMore, loadMore, error } = list;\n useEffect(() => {\n if (p.infinite && near && hasMore && !loadingMore && !error) void loadMore();\n }, [p.infinite, near, hasMore, loadingMore, loadMore, error]);\n const tiers = p.tiers ?? kinds?.tiers;\n const layout = cardGridLayout(p.grid);\n return (\n <ContentKitUiRoot appearance={p.appearance} className={cn(\"grid gap-4 text-sm\", p.className)} data-ckui=\"content-grid\" aria-busy={list.loading}>\n {list.loading && !list.items.length ? (\n <div className={layout.className} style={layout.style}>\n {Array.from({ length: 8 }, (_, i) => (\n <Skeleton key={i} className={p.aspectRatio === undefined ? p.shape === \"landscape\" ? \"aspect-video w-full\" : \"aspect-[3/4] w-full\" : \"w-full\"} style={p.aspectRatio === undefined ? undefined : { aspectRatio: p.aspectRatio }} />\n ))}\n </div>\n ) : list.error && !list.items.length ? (\n <div role=\"alert\" className=\"grid justify-items-start gap-2\">\n <p className=\"text-destructive\">{contentError(m, list.error)}</p>\n <Button size=\"sm\" variant=\"outline\" onClick={list.reload}>\n {t(\"common.retry\")}\n </Button>\n </div>\n ) : !list.items.length ? (\n <div className=\"text-muted-foreground\" role=\"status\">\n {p.empty ?? t(\"contents.empty\")}\n </div>\n ) : (\n <ol className={layout.className} style={layout.style}>\n {list.items.map((card, i) => (\n <li key={card.id} ref={exposure.track(card.id, i + 1)}>\n {p.renderCard ? (\n p.renderCard(card, i + 1)\n ) : (\n <ContentCard\n card={card}\n client={p.client}\n href={p.href}\n shape={p.shape}\n aspectRatio={p.aspectRatio}\n titlePosition={p.titlePosition}\n loadMedia={p.loadMedia}\n groupKinds={p.groupKinds}\n chips={p.chips}\n filterEntity={p.filterEntity}\n entityStyle={p.entityStyle}\n entityLayout={p.entityLayout}\n metadata={p.metadata}\n showCounts={p.showCounts}\n tiers={tiers}\n labels={p.labels}\n owner={p.owner}\n ownerHref={p.ownerHref}\n decorate={p.decorate}\n onOpen={() => exposure.click(card.id, i + 1)}\n />\n )}\n </li>\n ))}\n </ol>\n )}\n {list.hasMore && (\n <div ref={sentinel} className=\"grid justify-items-center gap-2\">\n {list.error && list.items.length > 0 && <p role=\"alert\" className=\"text-destructive\">{contentError(m, list.error)}</p>}\n <Button variant=\"outline\" size=\"sm\" disabled={list.loadingMore} onClick={() => void list.loadMore()}>\n {list.loadingMore ? t(\"contents.loading\") : list.error ? t(\"common.retry\") : t(\"contents.loadMore\")}\n </Button>\n </div>\n )}\n </ContentKitUiRoot>\n );\n}\n\n/**\n * What a list does with cards the reader doesn't open: shows them marked\n * (show), leaves them out (hide), or lists them apart, counted, behind a\n * toggle that loads them when opened (collapse).\n */\nexport type LockedCards = \"show\" | \"hide\" | \"collapse\";\n\nconst LOCKED_LEVELS = [\"tier\", \"group\", \"ppv\", \"group_ppv\"] as const;\n\n/** Contents as cards (GET /v1/contents): any sort and filter, paged. */\nexport function ContentGrid({ query, locked = \"show\", ...p }: CardListProps & { query: ContentQuery; locked?: LockedCards }) {\n const q = useMemo<ContentQuery>(\n () => ({ ...query, limit: query.limit ?? p.pageSize, ...(locked === \"show\" ? {} : { mode: \"hide\" }) }),\n [query, p.pageSize, locked],\n );\n const list = useContents(q, { client: p.client });\n return (\n <>\n {locked === \"collapse\" && <LockedSection {...p} query={query} />}\n <CardGrid {...p} list={list} render={JSON.stringify(q)} />\n </>\n );\n}\n\n/** A list's locked cards, counted, loaded and shown on opening. */\nfunction LockedSection({ query, ...p }: CardListProps & { query: ContentQuery }) {\n const m = useMessages();\n const [open, setOpen] = useState(false);\n const { count } = useLockedCount(query, { client: p.client });\n const q = useMemo<ContentQuery | null>(\n () => (open ? { ...query, mode: \"lock\", access: query.access ?? LOCKED_LEVELS, limit: query.limit ?? p.pageSize } : null),\n [open, query, p.pageSize],\n );\n const list = useContents(q, { client: p.client });\n const items = useMemo(() => list.items.filter((c) => !c.access.allowed), [list.items]);\n if (!count) return null;\n return (\n <ContentKitUiRoot appearance={p.appearance} className=\"grid gap-3 text-sm\" data-ckui=\"locked-cards\">\n <Button variant=\"outline\" size=\"sm\" className=\"justify-self-start\" aria-expanded={open} onClick={() => setOpen(!open)}>\n {open ? m.t(\"contents.lockedHide\") : m.plural(\"contents.lockedShow\", count)}\n </Button>\n {open && <CardGrid {...p} list={{ ...list, items }} render={JSON.stringify(q)} />}\n </ContentKitUiRoot>\n );\n}\n\n/** Search results as cards; nothing until the query has text. */\nexport function SearchResults({ query, ...p }: CardListProps & { query: SearchQuery }) {\n const { t } = useMessages();\n const q = useMemo(() => ({ ...query, limit: query.limit ?? p.pageSize }), [query, p.pageSize]);\n const list = useSearch(q, { client: p.client });\n if (!query.q.trim()) return null;\n return <CardGrid surface=\"search\" empty={t(\"contents.noResults\", { q: query.q.trim() })} {...p} list={list} render={JSON.stringify(q)} />;\n}\n\n/** The signed-in reader's history as cards, each removable, with a clear-all. */\nexport function ContentHistory({ query = {}, ...p }: CardListProps & { query?: HistoryQuery }) {\n const { t } = useMessages();\n const ctx = useContext(ContentKitContext);\n const q = useMemo(() => ({ ...query, limit: query.limit ?? p.pageSize }), [query, p.pageSize]);\n const list = useHistory(q, { client: p.client });\n const forget = useAction(\"history.forget\");\n if (ctx?.viewer === null) return <p className=\"text-muted-foreground\">{t(\"contents.historySignedOut\")}</p>;\n return (\n <div className=\"grid gap-3\" data-ckui=\"content-history\">\n {list.items.length > 0 && (\n <Button size=\"sm\" variant=\"outline\" className=\"justify-self-end\" disabled={forget.pending} onClick={() => void forget.run(() => list.forget(query.kind ? { kind: query.kind } : {}))}>\n {t(\"contents.clearHistory\")}\n </Button>\n )}\n <CardGrid\n surface=\"history\"\n empty={t(\"contents.historyEmpty\")}\n {...p}\n list={list}\n render={JSON.stringify(q)}\n renderCard={\n p.renderCard ??\n ((card) => (\n <ContentCard\n card={card}\n client={p.client}\n href={p.href}\n shape={p.shape}\n aspectRatio={p.aspectRatio}\n titlePosition={p.titlePosition}\n loadMedia={p.loadMedia}\n groupKinds={p.groupKinds}\n chips={p.chips}\n filterEntity={p.filterEntity}\n entityStyle={p.entityStyle}\n entityLayout={p.entityLayout}\n metadata={p.metadata}\n showCounts={p.showCounts}\n tiers={p.tiers}\n labels={p.labels}\n owner={p.owner}\n ownerHref={p.ownerHref}\n decorate={p.decorate}\n >\n <Button size=\"xs\" variant=\"ghost\" className=\"justify-self-start\" disabled={forget.pending} onClick={() => void forget.run(() => list.forget({ content_id: card.id }))}>\n {t(\"contents.removeFromHistory\")}\n </Button>\n </ContentCard>\n ))\n }\n />\n </div>\n );\n}\n\nexport interface ContentRailProps extends Omit<CardListProps, \"infinite\" | \"empty\"> {\n /** The rail's heading. */\n title: ReactNode;\n /** Contents like this one. */\n similar?: string;\n /** For the signed-in reader. */\n recommendations?: boolean;\n /** The reader's history: { status: \"in_progress\" } is a continue-watching rail. */\n history?: HistoryQuery;\n /** An ordered group's members in its order: a featured rail (its windows apply). */\n group?: string;\n /** Any list: a sort and filters. */\n list?: ContentQuery;\n /** Filters and reader options of a similar or recommended rail. */\n query?: RailQuery;\n /** A link beside the heading, e.g. to the whole list. */\n more?: ReactNode;\n}\n\n/**\n * A horizontal row of contents: similar, recommended, the reader's history,\n * an ordered group's members or any list. It hides itself when it has none or\n * the site runs without what it needs.\n */\nexport function ContentRail({ title, similar, recommendations, history, group, list: listQuery, query = {}, more, ...p }: ContentRailProps) {\n const limit = query.limit ?? p.pageSize ?? 12;\n const q = useMemo(() => ({ ...query, limit }), [query, limit]);\n const listing = useMemo(\n () => (listQuery ? { ...listQuery, limit: listQuery.limit ?? limit } : group ? { groups: [group], sort: \"position\" as const, limit } : null),\n [listQuery, group, limit],\n );\n const like = useSimilar(recommendations || history || listing ? null : similar, q, { client: p.client });\n const mine = useRecommendations(recommendations ? q : null, { client: p.client });\n const seen = useHistory(history ? { ...history, limit: history.limit ?? limit } : null, { client: p.client });\n const listed = useContents(listing, { client: p.client });\n const rail: UseRail = recommendations\n ? mine\n : history || listing\n ? (() => {\n const l = history ? seen : listed;\n return { items: l.items, loading: l.loading, error: l.error, unavailable: l.error?.code === \"not_configured\", reload: l.reload };\n })()\n : like;\n const { kinds } = useKinds({ client: p.client });\n const surface = p.surface ?? (recommendations ? \"recommendations\" : history ? \"history\" : group ? \"featured\" : listing ? \"list\" : \"similar\");\n const exposure = useExposure(rail.items, { surface, render: JSON.stringify([similar, recommendations, history, group, listing, q]), client: p.client });\n const source = similar || recommendations || history || group || listing;\n const layout = p.grid ? cardGridLayout(p.grid) : undefined;\n if (!source || rail.unavailable || (!rail.loading && !rail.items.length)) return null;\n return (\n <ContentKitUiRoot appearance={p.appearance} className={cn(\"grid gap-3 text-sm\", p.className)} data-ckui=\"content-rail\" data-surface={surface}>\n <div className=\"flex items-baseline justify-between gap-2\">\n <h2 className=\"text-base font-semibold\">{title}</h2>\n {more}\n </div>\n <ol className={layout?.className ?? \"flex snap-x gap-4 overflow-x-auto pb-2\"} style={layout?.style}>\n {(rail.loading && !rail.items.length ? Array.from({ length: 6 }, () => null) : rail.items).map((card, i) => (\n <li key={card?.id ?? i} className={layout ? undefined : \"w-36 shrink-0 snap-start sm:w-44\"} ref={card ? exposure.track(card.id, i + 1) : undefined}>\n {!card ? (\n <Skeleton className={p.aspectRatio === undefined ? p.shape === \"landscape\" ? \"aspect-video w-full\" : \"aspect-[3/4] w-full\" : \"w-full\"} style={p.aspectRatio === undefined ? undefined : { aspectRatio: p.aspectRatio }} />\n ) : p.renderCard ? (\n p.renderCard(card, i + 1)\n ) : (\n <ContentCard\n card={card}\n client={p.client}\n href={p.href}\n shape={p.shape}\n aspectRatio={p.aspectRatio}\n titlePosition={p.titlePosition}\n loadMedia={p.loadMedia}\n groupKinds={p.groupKinds}\n chips={p.chips}\n filterEntity={p.filterEntity}\n entityStyle={p.entityStyle}\n entityLayout={p.entityLayout}\n metadata={p.metadata}\n showCounts={p.showCounts}\n tiers={p.tiers ?? kinds?.tiers}\n labels={p.labels}\n owner={p.owner}\n ownerHref={p.ownerHref}\n decorate={p.decorate}\n onOpen={() => exposure.click(card.id, i + 1)}\n />\n )}\n </li>\n ))}\n </ol>\n </ContentKitUiRoot>\n );\n}\n"],"mappings":"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"}
|
|
@@ -4,7 +4,8 @@ export { DailyReleases, type DailyReleasesProps } from './daily-releases.js';
|
|
|
4
4
|
export { PopularContentGrid, type PopularContentGridProps } from './popular.js';
|
|
5
5
|
export { SearchBox, type SearchBoxProps } from './search.js';
|
|
6
6
|
export { ReaderSettings, type ReaderSettingsProps } from './reader-settings.js';
|
|
7
|
-
export {
|
|
7
|
+
export { ContentFilterDialog, type ContentFilterDialogProps } from './filter-dialog.js';
|
|
8
|
+
export { ContentFilterSettings, ReleaseDateIndex, type ContentFilterSettingsProps, type ContentFilterCount, type ReleaseDateIndexProps } from './filters.js';
|
|
8
9
|
export { ContentListing, sortLabel, type ContentListingProps } from './listing.js';
|
|
9
10
|
export { Paywall, VersionPicker, priceLabel, useVersionMenuItems, type PaywallProps, type VersionMenuOptions, type VersionPickerProps } from './paywall.js';
|
|
10
11
|
export { AccessEditor, ContentDetailsEditor, ContentSections, SegmentsEditor, VersionCover, VersionsEditor, versionLabel } from './editors.js';
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { useReducedMotion as e } from "../../react/inline-preview.js";
|
|
2
|
+
import { useCallback as t, useEffect as n, useLayoutEffect as r, useRef as i } from "react";
|
|
3
|
+
//#region src/components/content/release-reveal.ts
|
|
4
|
+
function a(a) {
|
|
5
|
+
let o = i(null), s = i(null), c = i([]), l = e(), u = t((e = !1) => {
|
|
6
|
+
let t = o.current;
|
|
7
|
+
t && (s.current = {
|
|
8
|
+
height: t.getBoundingClientRect().height,
|
|
9
|
+
positions: new Map([...t.children].map((e) => [e.getAttribute("data-release-key") ?? "", e.getBoundingClientRect()])),
|
|
10
|
+
focus: e
|
|
11
|
+
});
|
|
12
|
+
}, []);
|
|
13
|
+
return r(() => {
|
|
14
|
+
let e = s.current;
|
|
15
|
+
s.current = null;
|
|
16
|
+
let t = o.current;
|
|
17
|
+
if (!e || !t) return;
|
|
18
|
+
c.current.forEach((e) => e.cancel()), c.current = [];
|
|
19
|
+
let n = [...t.children].filter((e) => e instanceof HTMLElement), r = n.filter((t) => !e.positions.has(t.dataset.releaseKey ?? ""));
|
|
20
|
+
if (e.focus && r[0]?.querySelector("a[href]:not([tabindex='-1']), button:not([disabled])")?.focus({ preventScroll: !0 }), l || typeof t.animate != "function") return;
|
|
21
|
+
let i = "cubic-bezier(0.22, 1, 0.36, 1)", a = 0;
|
|
22
|
+
for (let t of n) {
|
|
23
|
+
let n = e.positions.get(t.dataset.releaseKey ?? "");
|
|
24
|
+
if (n) {
|
|
25
|
+
let e = t.getBoundingClientRect(), r = n.left - e.left, a = n.top - e.top;
|
|
26
|
+
(r || a) && c.current.push(t.animate([{ transform: `translate(${r}px, ${a}px)` }, { transform: "translate(0, 0)" }], {
|
|
27
|
+
duration: 400,
|
|
28
|
+
easing: i
|
|
29
|
+
}));
|
|
30
|
+
} else c.current.push(t.animate([{
|
|
31
|
+
opacity: 0,
|
|
32
|
+
transform: "translateY(16px) scale(0.97)"
|
|
33
|
+
}, {
|
|
34
|
+
opacity: 1,
|
|
35
|
+
transform: "translateY(0) scale(1)"
|
|
36
|
+
}], {
|
|
37
|
+
duration: 400,
|
|
38
|
+
delay: Math.min(a++, 5) * 45,
|
|
39
|
+
easing: i,
|
|
40
|
+
fill: "backwards"
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
43
|
+
let u = t.getBoundingClientRect().height;
|
|
44
|
+
Math.abs(u - e.height) > 1 && c.current.push(t.animate([{
|
|
45
|
+
height: `${e.height}px`,
|
|
46
|
+
overflow: "clip"
|
|
47
|
+
}, {
|
|
48
|
+
height: `${u}px`,
|
|
49
|
+
overflow: "clip"
|
|
50
|
+
}], {
|
|
51
|
+
duration: 400,
|
|
52
|
+
easing: i
|
|
53
|
+
}));
|
|
54
|
+
}, [a, l]), n(() => () => c.current.forEach((e) => e.cancel()), []), {
|
|
55
|
+
grid: o,
|
|
56
|
+
prepare: u
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
//#endregion
|
|
60
|
+
export { a as useReleaseReveal };
|
|
61
|
+
|
|
62
|
+
//# sourceMappingURL=release-reveal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"release-reveal.js","names":[],"sources":["../../../src/components/content/release-reveal.ts"],"sourcesContent":["import { useCallback, useEffect, useLayoutEffect, useRef } from \"react\";\nimport { useReducedMotion } from \"../../react/inline-preview.js\";\n\n/** A local layout transition for a cached reveal, not an image-loading policy. */\nexport function useReleaseReveal(identity: string) {\n const grid = useRef<HTMLOListElement>(null);\n const pending = useRef<{ height: number; positions: Map<string, DOMRect>; focus: boolean } | null>(null);\n const animations = useRef<Animation[]>([]);\n const reduced = useReducedMotion();\n const prepare = useCallback((focus = false) => {\n const element = grid.current;\n if (!element) return;\n pending.current = {\n height: element.getBoundingClientRect().height,\n positions: new Map([...element.children].map((child) => [child.getAttribute(\"data-release-key\") ?? \"\", child.getBoundingClientRect()])),\n focus,\n };\n }, []);\n useLayoutEffect(() => {\n const snapshot = pending.current;\n pending.current = null;\n const element = grid.current;\n if (!snapshot || !element) return;\n animations.current.forEach((animation) => animation.cancel());\n animations.current = [];\n const children = [...element.children].filter((child): child is HTMLElement => child instanceof HTMLElement);\n const added = children.filter((child) => !snapshot.positions.has(child.dataset.releaseKey ?? \"\"));\n if (snapshot.focus) added[0]?.querySelector<HTMLElement>(\"a[href]:not([tabindex='-1']), button:not([disabled])\")?.focus({ preventScroll: true });\n if (reduced || typeof element.animate !== \"function\") return;\n const easing = \"cubic-bezier(0.22, 1, 0.36, 1)\";\n let revealed = 0;\n for (const child of children) {\n const previous = snapshot.positions.get(child.dataset.releaseKey ?? \"\");\n if (previous) {\n const next = child.getBoundingClientRect();\n const x = previous.left - next.left;\n const y = previous.top - next.top;\n if (x || y) animations.current.push(child.animate([{ transform: `translate(${x}px, ${y}px)` }, { transform: \"translate(0, 0)\" }], { duration: 400, easing }));\n } else {\n animations.current.push(child.animate([{ opacity: 0, transform: \"translateY(16px) scale(0.97)\" }, { opacity: 1, transform: \"translateY(0) scale(1)\" }], { duration: 400, delay: Math.min(revealed++, 5) * 45, easing, fill: \"backwards\" }));\n }\n }\n const height = element.getBoundingClientRect().height;\n if (Math.abs(height - snapshot.height) > 1) animations.current.push(element.animate([{ height: `${snapshot.height}px`, overflow: \"clip\" }, { height: `${height}px`, overflow: \"clip\" }], { duration: 400, easing }));\n }, [identity, reduced]);\n useEffect(() => () => animations.current.forEach((animation) => animation.cancel()), []);\n return { grid, prepare };\n}\n"],"mappings":";;;AAIA,SAAgB,EAAiB,GAAkB;CACjD,IAAM,IAAO,EAAyB,IAAI,GACpC,IAAU,EAAmF,IAAI,GACjG,IAAa,EAAoB,CAAC,CAAC,GACnC,IAAU,EAAiB,GAC3B,IAAU,GAAa,IAAQ,OAAU;EAC7C,IAAM,IAAU,EAAK;EAChB,MACL,EAAQ,UAAU;GAChB,QAAQ,EAAQ,sBAAsB,CAAC,CAAC;GACxC,WAAW,IAAI,IAAI,CAAC,GAAG,EAAQ,QAAQ,CAAC,CAAC,KAAK,MAAU,CAAC,EAAM,aAAa,kBAAkB,KAAK,IAAI,EAAM,sBAAsB,CAAC,CAAC,CAAC;GACtI;EACF;CACF,GAAG,CAAC,CAAC;CA6BL,OA5BA,QAAsB;EACpB,IAAM,IAAW,EAAQ;EACzB,EAAQ,UAAU;EAClB,IAAM,IAAU,EAAK;EACrB,IAAI,CAAC,KAAY,CAAC,GAAS;EAE3B,AADA,EAAW,QAAQ,SAAS,MAAc,EAAU,OAAO,CAAC,GAC5D,EAAW,UAAU,CAAC;EACtB,IAAM,IAAW,CAAC,GAAG,EAAQ,QAAQ,CAAC,CAAC,QAAQ,MAAgC,aAAiB,WAAW,GACrG,IAAQ,EAAS,QAAQ,MAAU,CAAC,EAAS,UAAU,IAAI,EAAM,QAAQ,cAAc,EAAE,CAAC;EAEhG,IADI,EAAS,SAAO,EAAM,EAAE,EAAE,cAA2B,sDAAsD,CAAC,EAAE,MAAM,EAAE,eAAe,GAAK,CAAC,GAC3I,KAAW,OAAO,EAAQ,WAAY,YAAY;EACtD,IAAM,IAAS,kCACX,IAAW;EACf,KAAK,IAAM,KAAS,GAAU;GAC5B,IAAM,IAAW,EAAS,UAAU,IAAI,EAAM,QAAQ,cAAc,EAAE;GACtE,IAAI,GAAU;IACZ,IAAM,IAAO,EAAM,sBAAsB,GACnC,IAAI,EAAS,OAAO,EAAK,MACzB,IAAI,EAAS,MAAM,EAAK;IAC9B,CAAI,KAAK,MAAG,EAAW,QAAQ,KAAK,EAAM,QAAQ,CAAC,EAAE,WAAW,aAAa,EAAE,MAAM,EAAE,KAAK,GAAG,EAAE,WAAW,kBAAkB,CAAC,GAAG;KAAE,UAAU;KAAK;IAAO,CAAC,CAAC;GAC9J,OACE,EAAW,QAAQ,KAAK,EAAM,QAAQ,CAAC;IAAE,SAAS;IAAG,WAAW;GAA+B,GAAG;IAAE,SAAS;IAAG,WAAW;GAAyB,CAAC,GAAG;IAAE,UAAU;IAAK,OAAO,KAAK,IAAI,KAAY,CAAC,IAAI;IAAI;IAAQ,MAAM;GAAY,CAAC,CAAC;EAE9O;EACA,IAAM,IAAS,EAAQ,sBAAsB,CAAC,CAAC;EAC/C,AAAI,KAAK,IAAI,IAAS,EAAS,MAAM,IAAI,KAAG,EAAW,QAAQ,KAAK,EAAQ,QAAQ,CAAC;GAAE,QAAQ,GAAG,EAAS,OAAO;GAAK,UAAU;EAAO,GAAG;GAAE,QAAQ,GAAG,EAAO;GAAK,UAAU;EAAO,CAAC,GAAG;GAAE,UAAU;GAAK;EAAO,CAAC,CAAC;CACrN,GAAG,CAAC,GAAU,CAAO,CAAC,GACtB,cAAsB,EAAW,QAAQ,SAAS,MAAc,EAAU,OAAO,CAAC,GAAG,CAAC,CAAC,GAChF;EAAE;EAAM;CAAQ;AACzB"}
|
|
@@ -5,14 +5,14 @@ import { ContentKitContext as r } from "../../react/context.js";
|
|
|
5
5
|
import { Input as i } from "../../ui/input.js";
|
|
6
6
|
import { useRecentSearches as a, useTypeahead as o } from "../../react/discovery.js";
|
|
7
7
|
import { labelOf as s } from "../groups/parts.js";
|
|
8
|
-
import { useContext as c, useEffect as
|
|
8
|
+
import { useContext as c, useEffect as l, useId as ee, useLayoutEffect as u, useMemo as te, useRef as d, useState as f } from "react";
|
|
9
9
|
import { Fragment as p, jsx as m, jsxs as h } from "react/jsx-runtime";
|
|
10
10
|
import { cn as g } from "cn";
|
|
11
11
|
import { Cancel01Icon as _, Clock01Icon as v, File01Icon as y, Folder01Icon as b, Image01Icon as x, Search01Icon as S, TagsIcon as C, UserIcon as w } from "@hugeicons/core-free-icons";
|
|
12
12
|
import { HugeiconsIcon as T } from "@hugeicons/react";
|
|
13
|
-
import { createPortal as
|
|
13
|
+
import { createPortal as ne } from "react-dom";
|
|
14
14
|
//#region src/components/content/search.tsx
|
|
15
|
-
var
|
|
15
|
+
var E = {
|
|
16
16
|
artist: {
|
|
17
17
|
icon: w,
|
|
18
18
|
color: "text-blue-500 dark:text-blue-400"
|
|
@@ -46,13 +46,13 @@ var D = {
|
|
|
46
46
|
color: "text-orange-500 dark:text-orange-400"
|
|
47
47
|
}
|
|
48
48
|
};
|
|
49
|
-
function
|
|
49
|
+
function D(e, t, n, r) {
|
|
50
50
|
let [i, a] = f({
|
|
51
51
|
top: 0,
|
|
52
52
|
left: 0,
|
|
53
53
|
width: 0
|
|
54
54
|
});
|
|
55
|
-
return
|
|
55
|
+
return u(() => {
|
|
56
56
|
if (!e || !t || !n.current) return;
|
|
57
57
|
let i = r.current ?? n.current, o = n.current.ownerDocument.defaultView;
|
|
58
58
|
if (!o) return;
|
|
@@ -76,8 +76,8 @@ function O(e, t, n, r) {
|
|
|
76
76
|
r
|
|
77
77
|
]), i;
|
|
78
78
|
}
|
|
79
|
-
function
|
|
80
|
-
let t =
|
|
79
|
+
function O({ kind: e }) {
|
|
80
|
+
let t = E[e];
|
|
81
81
|
return /* @__PURE__ */ m(T, {
|
|
82
82
|
icon: t?.icon ?? S,
|
|
83
83
|
strokeWidth: 2,
|
|
@@ -86,97 +86,89 @@ function k({ kind: e }) {
|
|
|
86
86
|
"aria-hidden": !0
|
|
87
87
|
});
|
|
88
88
|
}
|
|
89
|
-
function
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
kind: l.kind,
|
|
99
|
-
entity_kinds: l.groupKinds,
|
|
100
|
-
client: l.client
|
|
101
|
-
}), W = a({
|
|
102
|
-
enabled: l.recent !== !1 && j && H,
|
|
103
|
-
client: l.client
|
|
104
|
-
}), re = u(() => H ? W.searches.map((e) => ({
|
|
89
|
+
function k(u) {
|
|
90
|
+
let { t: y, error: b } = t(), x = ee(), C = c(r)?.navigate, [w, E] = f(u.defaultValue ?? ""), [k, A] = f(!1), [j, M] = f(-1), N = d(null), P = d(null), F = d(null), I = u.presentation === "header", L = D(k, I, N, F), { top: R } = L, z = !w.trim(), B = o(w, {
|
|
91
|
+
kind: u.kind,
|
|
92
|
+
entity_kinds: u.groupKinds,
|
|
93
|
+
client: u.client
|
|
94
|
+
}), V = a({
|
|
95
|
+
enabled: u.recent !== !1 && k && z,
|
|
96
|
+
client: u.client
|
|
97
|
+
}), H = te(() => z ? V.searches.map((e) => ({
|
|
105
98
|
type: "recent",
|
|
106
|
-
query: e.query
|
|
107
|
-
|
|
108
|
-
})) : [...U.contents.map((e) => ({
|
|
99
|
+
query: e.query
|
|
100
|
+
})) : [...B.contents.map((e) => ({
|
|
109
101
|
type: "content",
|
|
110
102
|
card: e
|
|
111
|
-
})), ...
|
|
103
|
+
})), ...B.entities.map((e) => ({
|
|
112
104
|
type: "group",
|
|
113
105
|
group: e
|
|
114
106
|
}))], [
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
]),
|
|
120
|
-
|
|
121
|
-
if (!
|
|
107
|
+
z,
|
|
108
|
+
V.searches,
|
|
109
|
+
B.contents,
|
|
110
|
+
B.entities
|
|
111
|
+
]), U = z || B.query === w.trim(), W = U ? H : [], G = W[j], K = z ? V.loading : !U || B.loading, q = z && u.recent !== !1, J = z ? !I && q && !V.error ? "contents.noRecentSearches" : "contents.searchHint" : "contents.noResults";
|
|
112
|
+
l(() => {
|
|
113
|
+
if (!k) return;
|
|
122
114
|
let e = (e) => {
|
|
123
|
-
!
|
|
115
|
+
!N.current?.contains(e.target) && !P.current?.contains(e.target) && A(!1);
|
|
124
116
|
};
|
|
125
117
|
return document.addEventListener("pointerdown", e), () => document.removeEventListener("pointerdown", e);
|
|
126
|
-
}, [
|
|
118
|
+
}, [k]);
|
|
127
119
|
let Y = () => {
|
|
128
|
-
|
|
120
|
+
A(!0);
|
|
129
121
|
}, X = (e) => {
|
|
130
|
-
|
|
122
|
+
A(!1), e && u.onSearch(e);
|
|
131
123
|
}, Z = (e) => {
|
|
132
|
-
if (e.type === "recent") return
|
|
133
|
-
let t =
|
|
134
|
-
|
|
124
|
+
if (e.type === "recent") return E(e.query), X(e.query);
|
|
125
|
+
let t = u.href ? u.href(e.type === "content" ? e.card : e.group) : e.type === "content" ? e.card.path : e.group.path;
|
|
126
|
+
A(!1), t && (C ? C(t) : location.assign(t));
|
|
135
127
|
}, Q = (e) => {
|
|
136
|
-
e.key === "Escape" && (e.preventDefault(),
|
|
128
|
+
e.key === "Escape" && (e.preventDefault(), F.current?.focus(), A(!1), M(-1));
|
|
137
129
|
}, $ = /* @__PURE__ */ h(p, { children: [
|
|
138
|
-
|
|
139
|
-
className: g("flex items-center justify-between gap-2 text-muted-foreground",
|
|
130
|
+
q && /* @__PURE__ */ h("div", {
|
|
131
|
+
className: g("flex items-center justify-between gap-2 text-muted-foreground", I ? "mb-4 text-base font-semibold" : "px-2 py-1 text-xs"),
|
|
140
132
|
children: [/* @__PURE__ */ h("span", {
|
|
141
|
-
id: `${
|
|
133
|
+
id: `${x}-recent`,
|
|
142
134
|
className: "flex items-center gap-2",
|
|
143
|
-
children: [
|
|
135
|
+
children: [I && /* @__PURE__ */ m(T, {
|
|
144
136
|
icon: v,
|
|
145
137
|
strokeWidth: 2,
|
|
146
138
|
className: "size-5",
|
|
147
139
|
"aria-hidden": !0
|
|
148
140
|
}), y("contents.recentSearches")]
|
|
149
|
-
}),
|
|
141
|
+
}), W.length > 0 && /* @__PURE__ */ m("button", {
|
|
150
142
|
type: "button",
|
|
151
143
|
className: "rounded px-1 hover:text-foreground",
|
|
152
144
|
onMouseDown: (e) => {
|
|
153
145
|
e.preventDefault();
|
|
154
146
|
},
|
|
155
|
-
onClick: () => void
|
|
156
|
-
children: y(
|
|
147
|
+
onClick: () => void V.clear(),
|
|
148
|
+
children: y(I ? "contents.clearAllRecent" : "contents.clearRecent")
|
|
157
149
|
})]
|
|
158
150
|
}),
|
|
159
151
|
/* @__PURE__ */ m("ul", {
|
|
160
|
-
id: `${
|
|
152
|
+
id: `${x}-list`,
|
|
161
153
|
role: "listbox",
|
|
162
|
-
"aria-label": y(
|
|
163
|
-
className: g("grid",
|
|
164
|
-
children:
|
|
165
|
-
id: `${
|
|
154
|
+
"aria-label": y(z ? "contents.recentSearches" : "contents.suggestions"),
|
|
155
|
+
className: g("grid", I && "gap-2"),
|
|
156
|
+
children: W.map((t, n) => /* @__PURE__ */ m("li", {
|
|
157
|
+
id: `${x}-${n}`,
|
|
166
158
|
role: "option",
|
|
167
|
-
"aria-selected": n ===
|
|
159
|
+
"aria-selected": n === j,
|
|
168
160
|
"data-ckui": "suggestion",
|
|
169
161
|
"data-type": t.type,
|
|
170
|
-
className: g("flex cursor-pointer items-center",
|
|
171
|
-
onMouseEnter: () =>
|
|
162
|
+
className: g("flex cursor-pointer items-center", I ? t.type === "recent" ? "gap-2 rounded-md p-3 text-base hover:bg-foreground/10" : "gap-3 rounded-md px-4 py-1 text-base font-medium hover:bg-foreground/10" : "gap-2 px-2 py-1.5", n === j && "bg-accent text-accent-foreground"),
|
|
163
|
+
onMouseEnter: () => M(n),
|
|
172
164
|
onMouseDown: (e) => {
|
|
173
165
|
e.preventDefault(), Z(t);
|
|
174
166
|
},
|
|
175
|
-
children:
|
|
176
|
-
/* @__PURE__ */ m(
|
|
167
|
+
children: I && t.type !== "recent" ? /* @__PURE__ */ h(p, { children: [
|
|
168
|
+
/* @__PURE__ */ m(O, { kind: t.type === "content" ? t.card.kind : t.group.kind }),
|
|
177
169
|
/* @__PURE__ */ m("span", {
|
|
178
170
|
className: "sr-only",
|
|
179
|
-
children: s(
|
|
171
|
+
children: s(u.labels, t.type === "content" ? t.card.kind : t.group.kind)
|
|
180
172
|
}),
|
|
181
173
|
/* @__PURE__ */ m("span", {
|
|
182
174
|
className: "min-w-0 flex-1 truncate",
|
|
@@ -198,10 +190,10 @@ function A(l) {
|
|
|
198
190
|
children: t.group.name || t.group.slug
|
|
199
191
|
}), /* @__PURE__ */ m("span", {
|
|
200
192
|
className: "text-xs text-muted-foreground",
|
|
201
|
-
children: s(
|
|
193
|
+
children: s(u.labels, t.group.kind)
|
|
202
194
|
})] }) : /* @__PURE__ */ h(p, { children: [
|
|
203
195
|
/* @__PURE__ */ m(T, {
|
|
204
|
-
icon:
|
|
196
|
+
icon: I ? S : v,
|
|
205
197
|
strokeWidth: 2,
|
|
206
198
|
className: "size-4 shrink-0 text-muted-foreground",
|
|
207
199
|
"aria-hidden": !0
|
|
@@ -210,10 +202,6 @@ function A(l) {
|
|
|
210
202
|
className: "min-w-0 flex-1 truncate",
|
|
211
203
|
children: t.query
|
|
212
204
|
}),
|
|
213
|
-
R && z.width >= 320 && /* @__PURE__ */ m(ne, {
|
|
214
|
-
value: t.searchedAt,
|
|
215
|
-
formatter: V
|
|
216
|
-
}),
|
|
217
205
|
/* @__PURE__ */ m("button", {
|
|
218
206
|
type: "button",
|
|
219
207
|
className: "rounded p-0.5 text-muted-foreground hover:text-foreground",
|
|
@@ -221,7 +209,7 @@ function A(l) {
|
|
|
221
209
|
onMouseDown: (e) => {
|
|
222
210
|
e.preventDefault(), e.stopPropagation();
|
|
223
211
|
},
|
|
224
|
-
onClick: () => void
|
|
212
|
+
onClick: () => void V.clear(t.query),
|
|
225
213
|
children: /* @__PURE__ */ m(T, {
|
|
226
214
|
icon: _,
|
|
227
215
|
strokeWidth: 2,
|
|
@@ -232,48 +220,48 @@ function A(l) {
|
|
|
232
220
|
] })
|
|
233
221
|
}, t.type === "content" ? `c:${t.card.id}` : t.type === "group" ? `g:${t.group.id}` : `r:${t.query}`))
|
|
234
222
|
}),
|
|
235
|
-
|
|
223
|
+
K ? /* @__PURE__ */ m("div", {
|
|
236
224
|
role: "status",
|
|
237
|
-
className: g("text-muted-foreground",
|
|
238
|
-
children:
|
|
225
|
+
className: g("text-muted-foreground", I ? "flex justify-center py-4" : "px-3 py-2"),
|
|
226
|
+
children: I ? /* @__PURE__ */ h(p, { children: [/* @__PURE__ */ m("span", {
|
|
239
227
|
className: "size-6 animate-spin rounded-full border-2 border-muted border-t-foreground motion-reduce:animate-none",
|
|
240
228
|
"aria-hidden": !0
|
|
241
229
|
}), /* @__PURE__ */ m("span", {
|
|
242
230
|
className: "sr-only",
|
|
243
231
|
children: y("contents.loading")
|
|
244
232
|
})] }) : y("contents.loading")
|
|
245
|
-
}) : !
|
|
233
|
+
}) : !z && B.error ? /* @__PURE__ */ m("p", {
|
|
246
234
|
role: "alert",
|
|
247
|
-
className: g("px-3 py-2 text-destructive",
|
|
248
|
-
children: b(
|
|
249
|
-
}) :
|
|
235
|
+
className: g("px-3 py-2 text-destructive", I && "text-center"),
|
|
236
|
+
children: b(B.error)
|
|
237
|
+
}) : W.length === 0 && /* @__PURE__ */ h("div", {
|
|
250
238
|
role: "status",
|
|
251
|
-
className: g("text-muted-foreground",
|
|
252
|
-
children: [
|
|
239
|
+
className: g("text-muted-foreground", I ? "flex flex-col items-center gap-2 py-8 text-center" : "px-3 py-2"),
|
|
240
|
+
children: [I && /* @__PURE__ */ m(T, {
|
|
253
241
|
icon: S,
|
|
254
242
|
strokeWidth: 2,
|
|
255
243
|
className: "size-8",
|
|
256
244
|
"aria-hidden": !0
|
|
257
|
-
}), /* @__PURE__ */ m("p", { children: y(
|
|
245
|
+
}), /* @__PURE__ */ m("p", { children: y(J, { q: w.trim() }) })]
|
|
258
246
|
})
|
|
259
247
|
] });
|
|
260
248
|
return /* @__PURE__ */ h(n, {
|
|
261
|
-
ref:
|
|
262
|
-
appearance:
|
|
263
|
-
className: g("relative text-sm",
|
|
249
|
+
ref: N,
|
|
250
|
+
appearance: u.appearance,
|
|
251
|
+
className: g("relative text-sm", u.className),
|
|
264
252
|
"data-ckui": "search-box",
|
|
265
253
|
onBlur: (e) => {
|
|
266
|
-
!e.currentTarget.contains(e.relatedTarget) && !
|
|
254
|
+
!e.currentTarget.contains(e.relatedTarget) && !P.current?.contains(e.relatedTarget) && A(!1);
|
|
267
255
|
},
|
|
268
256
|
children: [/* @__PURE__ */ h("form", {
|
|
269
257
|
role: "search",
|
|
270
258
|
className: "relative",
|
|
271
259
|
onSubmit: (e) => {
|
|
272
|
-
if (e.preventDefault(),
|
|
273
|
-
X(
|
|
260
|
+
if (e.preventDefault(), k && G) return Z(G);
|
|
261
|
+
X(w.trim());
|
|
274
262
|
},
|
|
275
263
|
children: [
|
|
276
|
-
|
|
264
|
+
I ? /* @__PURE__ */ m("button", {
|
|
277
265
|
type: "submit",
|
|
278
266
|
className: "absolute top-1/2 left-2.5 -translate-y-1/2 rounded text-muted-foreground hover:text-foreground focus-visible:outline-2 focus-visible:outline-ring",
|
|
279
267
|
"aria-label": y("contents.search"),
|
|
@@ -290,38 +278,38 @@ function A(l) {
|
|
|
290
278
|
"aria-hidden": !0
|
|
291
279
|
}),
|
|
292
280
|
/* @__PURE__ */ m(i, {
|
|
293
|
-
ref:
|
|
294
|
-
type:
|
|
281
|
+
ref: F,
|
|
282
|
+
type: I ? "text" : "search",
|
|
295
283
|
role: "combobox",
|
|
296
284
|
"aria-label": y("contents.search"),
|
|
297
|
-
"aria-expanded":
|
|
298
|
-
"aria-controls": `${
|
|
299
|
-
"aria-activedescendant":
|
|
285
|
+
"aria-expanded": k,
|
|
286
|
+
"aria-controls": `${x}-list`,
|
|
287
|
+
"aria-activedescendant": k && G ? `${x}-${j}` : void 0,
|
|
300
288
|
"aria-autocomplete": "list",
|
|
301
289
|
autoComplete: "off",
|
|
302
|
-
className: g("h-9 ps-8",
|
|
303
|
-
value:
|
|
304
|
-
placeholder:
|
|
290
|
+
className: g("h-9 ps-8", I && "rounded-full border-border/50 bg-foreground/10 pe-9 text-base shadow-none md:text-base"),
|
|
291
|
+
value: w,
|
|
292
|
+
placeholder: u.placeholder ?? y("contents.searchPlaceholder"),
|
|
305
293
|
onChange: (e) => {
|
|
306
|
-
|
|
294
|
+
E(e.target.value), A(!0), M(-1);
|
|
307
295
|
},
|
|
308
296
|
onFocus: Y,
|
|
309
297
|
onClick: Y,
|
|
310
298
|
onKeyDown: (e) => {
|
|
311
299
|
if (e.key === "Escape") {
|
|
312
|
-
e.preventDefault(),
|
|
300
|
+
e.preventDefault(), A(!1), M(-1);
|
|
313
301
|
return;
|
|
314
302
|
}
|
|
315
|
-
e.key === "ArrowDown" ? (e.preventDefault(), Y(),
|
|
303
|
+
e.key === "ArrowDown" ? (e.preventDefault(), Y(), M((e) => k ? Math.min(W.length - 1, e + 1) : 0)) : e.key === "ArrowUp" && k && (e.preventDefault(), M((e) => Math.max(-1, e - 1)));
|
|
316
304
|
}
|
|
317
305
|
}),
|
|
318
|
-
|
|
306
|
+
I && (w || k) && /* @__PURE__ */ m("button", {
|
|
319
307
|
type: "button",
|
|
320
308
|
className: "absolute top-1/2 right-2.5 -translate-y-1/2 rounded p-0.5 text-muted-foreground hover:text-foreground focus-visible:outline-2 focus-visible:outline-ring",
|
|
321
|
-
"aria-label": y(
|
|
309
|
+
"aria-label": y(w ? "contents.clearSearch" : "common.close"),
|
|
322
310
|
onMouseDown: (e) => e.preventDefault(),
|
|
323
311
|
onClick: () => {
|
|
324
|
-
|
|
312
|
+
F.current?.focus(), M(-1), w ? (E(""), A(!0)) : A(!1);
|
|
325
313
|
},
|
|
326
314
|
children: /* @__PURE__ */ m(T, {
|
|
327
315
|
icon: _,
|
|
@@ -331,20 +319,20 @@ function A(l) {
|
|
|
331
319
|
})
|
|
332
320
|
})
|
|
333
321
|
]
|
|
334
|
-
}),
|
|
335
|
-
appearance:
|
|
322
|
+
}), k && (I ? ne(/* @__PURE__ */ h(n, {
|
|
323
|
+
appearance: u.appearance,
|
|
336
324
|
className: "fixed inset-x-0 bottom-0 z-[70] text-sm",
|
|
337
|
-
style: { top:
|
|
325
|
+
style: { top: R },
|
|
338
326
|
"data-ckui": "search-layer",
|
|
339
327
|
children: [/* @__PURE__ */ m("div", {
|
|
340
328
|
className: "absolute inset-0 bg-black/30 backdrop-blur-sm",
|
|
341
329
|
"data-ckui": "search-backdrop",
|
|
342
330
|
"aria-hidden": !0
|
|
343
331
|
}), /* @__PURE__ */ m("div", {
|
|
344
|
-
ref:
|
|
332
|
+
ref: P,
|
|
345
333
|
style: {
|
|
346
|
-
width:
|
|
347
|
-
marginLeft:
|
|
334
|
+
width: L.width,
|
|
335
|
+
marginLeft: L.left
|
|
348
336
|
},
|
|
349
337
|
className: "relative origin-top animate-[search-expand_300ms_ease-out] rounded-lg border border-border bg-background text-foreground shadow-lg transition-none motion-reduce:animate-none",
|
|
350
338
|
"data-ckui": "search-panel",
|
|
@@ -353,13 +341,13 @@ function A(l) {
|
|
|
353
341
|
className: "w-full px-3 pt-3",
|
|
354
342
|
children: /* @__PURE__ */ m("div", {
|
|
355
343
|
className: "[--search-max-height:70vh] overflow-y-auto pb-7 md:[--search-max-height:60vh]",
|
|
356
|
-
style: { maxHeight: `min(var(--search-max-height), calc(100dvh - ${
|
|
344
|
+
style: { maxHeight: `min(var(--search-max-height), calc(100dvh - ${R}px - 1.875rem))` },
|
|
357
345
|
children: /* @__PURE__ */ m("div", { children: $ })
|
|
358
346
|
})
|
|
359
347
|
})
|
|
360
348
|
})]
|
|
361
349
|
}), document.body) : /* @__PURE__ */ m("div", {
|
|
362
|
-
ref:
|
|
350
|
+
ref: P,
|
|
363
351
|
className: "absolute top-full z-20 mt-1 grid max-h-96 w-full overflow-auto rounded-md border border-border bg-popover py-1 text-popover-foreground shadow-md",
|
|
364
352
|
"data-ckui": "search-panel",
|
|
365
353
|
onKeyDown: Q,
|
|
@@ -368,6 +356,6 @@ function A(l) {
|
|
|
368
356
|
});
|
|
369
357
|
}
|
|
370
358
|
//#endregion
|
|
371
|
-
export {
|
|
359
|
+
export { k as SearchBox };
|
|
372
360
|
|
|
373
361
|
//# sourceMappingURL=search.js.map
|