@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.
Files changed (101) hide show
  1. package/README.md +26 -0
  2. package/dist/client/content/comments.d.ts +4 -2
  3. package/dist/client/content/comments.js +10 -6
  4. package/dist/client/content/comments.js.map +1 -1
  5. package/dist/client/generated/routes.d.ts +28 -2
  6. package/dist/client/generated/routes.js +39 -2
  7. package/dist/client/generated/routes.js.map +1 -1
  8. package/dist/client/generated/wire.d.ts +85 -0
  9. package/dist/client/generated/wire.js.map +1 -1
  10. package/dist/client/media/client.d.ts +4 -0
  11. package/dist/client/media/client.js +8 -4
  12. package/dist/client/media/client.js.map +1 -1
  13. package/dist/client/media/queue.js +42 -26
  14. package/dist/client/media/queue.js.map +1 -1
  15. package/dist/client/media/rules.d.ts +6 -0
  16. package/dist/client/media/rules.js +64 -10
  17. package/dist/client/media/rules.js.map +1 -1
  18. package/dist/client/versions.d.ts +6 -2
  19. package/dist/client/versions.js +26 -0
  20. package/dist/client/versions.js.map +1 -1
  21. package/dist/components/admin/page-review.d.ts +13 -0
  22. package/dist/components/admin/page-review.js +216 -0
  23. package/dist/components/admin/page-review.js.map +1 -0
  24. package/dist/components/content/card.js +98 -103
  25. package/dist/components/content/card.js.map +1 -1
  26. package/dist/components/content/daily-releases.d.ts +8 -1
  27. package/dist/components/content/daily-releases.js +291 -133
  28. package/dist/components/content/daily-releases.js.map +1 -1
  29. package/dist/components/content/editors.js +216 -210
  30. package/dist/components/content/editors.js.map +1 -1
  31. package/dist/components/content/filter-dialog.d.ts +11 -0
  32. package/dist/components/content/filter-dialog.js +29 -0
  33. package/dist/components/content/filter-dialog.js.map +1 -0
  34. package/dist/components/content/filters.d.ts +10 -0
  35. package/dist/components/content/filters.js +5 -2
  36. package/dist/components/content/filters.js.map +1 -1
  37. package/dist/components/content/grid.js +19 -14
  38. package/dist/components/content/grid.js.map +1 -1
  39. package/dist/components/content/index.d.ts +2 -1
  40. package/dist/components/content/release-reveal.d.ts +5 -0
  41. package/dist/components/content/release-reveal.js +62 -0
  42. package/dist/components/content/release-reveal.js.map +1 -0
  43. package/dist/components/content/search.js +95 -107
  44. package/dist/components/content/search.js.map +1 -1
  45. package/dist/components/social/comments.d.ts +1 -1
  46. package/dist/components/social/comments.js +15 -15
  47. package/dist/components/social/comments.js.map +1 -1
  48. package/dist/index.d.ts +1 -0
  49. package/dist/index.js +24 -22
  50. package/dist/locales/admin/de.js +23 -0
  51. package/dist/locales/admin/de.js.map +1 -1
  52. package/dist/locales/admin/en.d.ts +23 -0
  53. package/dist/locales/admin/en.js +23 -0
  54. package/dist/locales/admin/en.js.map +1 -1
  55. package/dist/locales/admin/es.js +23 -0
  56. package/dist/locales/admin/es.js.map +1 -1
  57. package/dist/locales/admin/ja.js +23 -0
  58. package/dist/locales/admin/ja.js.map +1 -1
  59. package/dist/locales/admin/ko.js +23 -0
  60. package/dist/locales/admin/ko.js.map +1 -1
  61. package/dist/locales/admin/zh.js +23 -0
  62. package/dist/locales/admin/zh.js.map +1 -1
  63. package/dist/locales/discovery/de.js +11 -0
  64. package/dist/locales/discovery/de.js.map +1 -1
  65. package/dist/locales/discovery/en.d.ts +11 -0
  66. package/dist/locales/discovery/en.js +11 -0
  67. package/dist/locales/discovery/en.js.map +1 -1
  68. package/dist/locales/discovery/es.js +11 -0
  69. package/dist/locales/discovery/es.js.map +1 -1
  70. package/dist/locales/discovery/ja.js +11 -0
  71. package/dist/locales/discovery/ja.js.map +1 -1
  72. package/dist/locales/discovery/ko.js +11 -0
  73. package/dist/locales/discovery/ko.js.map +1 -1
  74. package/dist/locales/discovery/zh.js +11 -0
  75. package/dist/locales/discovery/zh.js.map +1 -1
  76. package/dist/locales/en.d.ts +34 -0
  77. package/dist/locales/social/en.js +2 -2
  78. package/dist/locales/social/en.js.map +1 -1
  79. package/dist/react/comments.d.ts +2 -1
  80. package/dist/react/comments.js +10 -8
  81. package/dist/react/comments.js.map +1 -1
  82. package/dist/react/folder.js +39 -39
  83. package/dist/react/folder.js.map +1 -1
  84. package/dist/react/index.d.ts +1 -0
  85. package/dist/react/page-review.d.ts +14 -0
  86. package/dist/react/page-review.js +36 -0
  87. package/dist/react/page-review.js.map +1 -0
  88. package/dist/react/public.d.ts +3 -1
  89. package/dist/react/public.js +76 -72
  90. package/dist/react/public.js.map +1 -1
  91. package/dist/react/resources.d.ts +5 -13
  92. package/dist/react/resources.js +58 -49
  93. package/dist/react/resources.js.map +1 -1
  94. package/dist/react/use-resource.d.ts +1 -1
  95. package/dist/react/use-resource.js +23 -13
  96. package/dist/react/use-resource.js.map +1 -1
  97. package/dist/react/versions.js +70 -42
  98. package/dist/react/versions.js.map +1 -1
  99. package/dist/react.js +8 -7
  100. package/dist/styles.css +1 -1
  101. 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":";;;;;;;;;;;;;;AAmCA,IAAM,IAAO;AAYb,SAAgB,EAAe,GAAuE;CACpG,IAAI,CAAC,GAAQ,OAAO,EAAE,WAAW,EAAK;CACtC,IAAM,IAAQ,OAAO,EAAO,gBAAiB,WAAW,EAAO,aAAa,KAAK,KAAK,UAAU,GAAG,OAAO,SAAS,EAAO,YAAY,IAAI,KAAK,IAAI,GAAG,EAAO,YAAY,IAAI,IAAI,KAC3K,IAAU,EAAO,kBAAkB,KAAA,KAAa,OAAO,SAAS,EAAO,aAAa,IAAI,KAAK,IAAI,GAAG,KAAK,MAAM,EAAO,aAAa,CAAC,IAAI,GACxI,IAAU,EAAO,eAAe,KAAA,KAAa,OAAO,SAAS,EAAO,UAAU,IAAI,KAAK,IAAI,GAAG,KAAK,MAAM,EAAO,UAAU,CAAC,IAAI,KAAA;CAErI,OAAO;EACL,WAAW;EACX,OAAO;GAAE,yBAHQ,MAAY,KAAA,IAAY,IAAQ,OAAO,EAAM,iBAAiB,IAAU,EAAE,SAAS,EAAQ;GAG9D,8BAA8B;EAAQ;CACtF;AACF;AAGA,SAAgB,EAAS,EAAE,SAAM,WAAQ,GAAG,KAA8D;CACxG,IAAM,IAAI,EAAY,GAChB,EAAE,SAAM,GACR,EAAE,aAAU,EAAS,EAAE,QAAQ,EAAE,OAAO,CAAC,GACzC,IAAW,EAAY,EAAK,OAAO;EAAE,SAAS,EAAE,WAAW;EAAQ;EAAQ,QAAQ,EAAE;CAAO,CAAC,GAC7F,CAAC,GAAU,KAAQ,EAAgB;EAAE,QAAQ;EAAa,aAAa;CAAa,CAAC,GACrF,EAAE,YAAS,gBAAa,gBAAa;CAC3C,QAAgB;EACd,AAAI,EAAE,YAAY,KAAQ,KAAW,CAAC,KAAa,EAAc;CACnE,GAAG;EAAC,EAAE;EAAU;EAAM;EAAS;EAAa;CAAQ,CAAC;CACrD,IAAM,IAAQ,EAAE,SAAS,GAAO,OAC1B,IAAS,EAAe,EAAE,IAAI;CACpC,OACE,kBAAC,GAAD;EAAkB,YAAY,EAAE;EAAY,WAAW,EAAG,sBAAsB,EAAE,SAAS;EAAG,aAAU;EAAe,aAAW,EAAK;EAAvI,UAAA,CACG,EAAK,WAAW,CAAC,EAAK,MAAM,SAC3B,kBAAC,OAAD;GAAK,WAAW,EAAO;GAAW,OAAO,EAAO;GAC7C,UAAA,MAAM,KAAK,EAAE,QAAQ,EAAE,IAAI,GAAG,MAC7B,kBAAC,GAAD;IAAkB,WAAW,EAAE,gBAAgB,KAAA,IAAY,EAAE,UAAU,cAAc,wBAAwB,wBAAwB;IAAU,OAAO,EAAE,gBAAgB,KAAA,IAAY,KAAA,IAAY,EAAE,aAAa,EAAE,YAAY;GAAI,GAAlN,CAAkN,CAClO;EACE,CAAA,IACH,EAAK,SAAS,CAAC,EAAK,MAAM,SAC5B,kBAAC,OAAD;GAAK,MAAK;GAAQ,WAAU;GAA5B,UAAA,CACE,kBAAC,KAAD;IAAG,WAAU;IAAoB,UAAA,EAAa,GAAG,EAAK,KAAK;GAAK,CAAA,GAChE,kBAAC,GAAD;IAAQ,MAAK;IAAK,SAAQ;IAAU,SAAS,EAAK;IAC/C,UAAA,EAAE,cAAc;GACX,CAAA,CACL;EACH,CAAA,IAAC,EAAK,MAAM,SAKd,kBAAC,MAAD;GAAI,WAAW,EAAO;GAAW,OAAO,EAAO;GAC5C,UAAA,EAAK,MAAM,KAAK,GAAM,MACrB,kBAAC,MAAD;IAAkB,KAAK,EAAS,MAAM,EAAK,IAAI,IAAI,CAAC;IACjD,UAAA,EAAE,aACD,EAAE,WAAW,GAAM,IAAI,CAAC,IAExB,kBAAC,GAAD;KACQ;KACN,QAAQ,EAAE;KACV,MAAM,EAAE;KACR,OAAO,EAAE;KACT,aAAa,EAAE;KACf,eAAe,EAAE;KACjB,WAAW,EAAE;KACb,YAAY,EAAE;KACd,OAAO,EAAE;KACT,cAAc,EAAE;KAChB,aAAa,EAAE;KACf,cAAc,EAAE;KAChB,UAAU,EAAE;KACZ,YAAY,EAAE;KACP;KACP,QAAQ,EAAE;KACV,OAAO,EAAE;KACT,WAAW,EAAE;KACb,UAAU,EAAE;KACZ,cAAc,EAAS,MAAM,EAAK,IAAI,IAAI,CAAC;IAC5C,CAAA;GAED,GA3BK,EAAK,EA2BV,CACL;EACC,CAAA,IAnCJ,kBAAC,OAAD;GAAK,WAAU;GAAwB,MAAK;GACzC,UAAA,EAAE,SAAS,EAAE,gBAAgB;EAC3B,CAAA,GAmCN,EAAK,WACJ,kBAAC,OAAD;GAAK,KAAK;GAAU,WAAU;GAC5B,UAAA,kBAAC,GAAD;IAAQ,SAAQ;IAAU,MAAK;IAAK,UAAU,EAAK;IAAa,eAAe,KAAK,EAAK,SAAS;IAC/F,UAAA,EAAK,cAAc,EAAE,kBAAkB,IAAI,EAAE,mBAAmB;GAC3D,CAAA;EACL,CAAA,CAES;;AAEtB;AASA,IAAM,IAAgB;CAAC;CAAQ;CAAS;CAAO;AAAW;AAG1D,SAAgB,EAAY,EAAE,UAAO,YAAS,QAAQ,GAAG,KAAoE;CAC3H,IAAM,IAAI,SACD;EAAE,GAAG;EAAO,OAAO,EAAM,SAAS,EAAE;EAAU,GAAI,MAAW,SAAS,CAAC,IAAI,EAAE,MAAM,OAAO;CAAG,IACpG;EAAC;EAAO,EAAE;EAAU;CAAM,CAC5B,GACM,IAAO,EAAY,GAAG,EAAE,QAAQ,EAAE,OAAO,CAAC;CAChD,OACE,kBAAA,GAAA,EAAA,UAAA,CACG,MAAW,cAAc,kBAAC,GAAD;EAAe,GAAI;EAAU;CAAQ,CAAA,GAC/D,kBAAC,GAAD;EAAU,GAAI;EAAS;EAAM,QAAQ,KAAK,UAAU,CAAC;CAAI,CAAA,CACzD,EAAA,CAAA;AAEN;AAGA,SAAS,EAAc,EAAE,UAAO,GAAG,KAA8C;CAC/E,IAAM,IAAI,EAAY,GAChB,CAAC,GAAM,KAAW,EAAS,EAAK,GAChC,EAAE,aAAU,EAAe,GAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,GACtD,IAAI,QACD,IAAO;EAAE,GAAG;EAAO,MAAM;EAAQ,QAAQ,EAAM,UAAU;EAAe,OAAO,EAAM,SAAS,EAAE;CAAS,IAAI,MACpH;EAAC;EAAM;EAAO,EAAE;CAAQ,CAC1B,GACM,IAAO,EAAY,GAAG,EAAE,QAAQ,EAAE,OAAO,CAAC,GAC1C,IAAQ,QAAc,EAAK,MAAM,QAAQ,MAAM,CAAC,EAAE,OAAO,OAAO,GAAG,CAAC,EAAK,KAAK,CAAC;CAErF,OADK,IAEH,kBAAC,GAAD;EAAkB,YAAY,EAAE;EAAY,WAAU;EAAqB,aAAU;EAArF,UAAA,CACE,kBAAC,GAAD;GAAQ,SAAQ;GAAU,MAAK;GAAK,WAAU;GAAqB,iBAAe;GAAM,eAAe,EAAQ,CAAC,CAAI;GACjH,UAAA,IAAO,EAAE,EAAE,qBAAqB,IAAI,EAAE,OAAO,uBAAuB,CAAK;EACpE,CAAA,GACP,KAAQ,kBAAC,GAAD;GAAU,GAAI;GAAG,MAAM;IAAE,GAAG;IAAM;GAAM;GAAG,QAAQ,KAAK,UAAU,CAAC;EAAI,CAAA,CAChE;MAPD;AASrB;AAGA,SAAgB,EAAc,EAAE,UAAO,GAAG,KAA6C;CACrF,IAAM,EAAE,SAAM,EAAY,GACpB,IAAI,SAAe;EAAE,GAAG;EAAO,OAAO,EAAM,SAAS,EAAE;CAAS,IAAI,CAAC,GAAO,EAAE,QAAQ,CAAC,GACvF,IAAO,EAAU,GAAG,EAAE,QAAQ,EAAE,OAAO,CAAC;CAE9C,OADK,EAAM,EAAE,KAAK,IACX,kBAAC,GAAD;EAAU,SAAQ;EAAS,OAAO,EAAE,sBAAsB,EAAE,GAAG,EAAM,EAAE,KAAK,EAAE,CAAC;EAAG,GAAI;EAAS;EAAM,QAAQ,KAAK,UAAU,CAAC;CAAI,CAAA,IAD5G;AAE9B;AAGA,SAAgB,EAAe,EAAE,WAAQ,CAAC,GAAG,GAAG,KAA+C;CAC7F,IAAM,EAAE,SAAM,EAAY,GACpB,IAAM,EAAW,CAAiB,GAClC,IAAI,SAAe;EAAE,GAAG;EAAO,OAAO,EAAM,SAAS,EAAE;CAAS,IAAI,CAAC,GAAO,EAAE,QAAQ,CAAC,GACvF,IAAO,EAAW,GAAG,EAAE,QAAQ,EAAE,OAAO,CAAC,GACzC,IAAS,EAAU,gBAAgB;CAEzC,OADI,GAAK,WAAW,OAAa,kBAAC,KAAD;EAAG,WAAU;EAAyB,UAAA,EAAE,2BAA2B;CAAK,CAAA,IAEvG,kBAAC,OAAD;EAAK,WAAU;EAAa,aAAU;EAAtC,UAAA,CACG,EAAK,MAAM,SAAS,KACnB,kBAAC,GAAD;GAAQ,MAAK;GAAK,SAAQ;GAAU,WAAU;GAAmB,UAAU,EAAO;GAAS,eAAe,KAAK,EAAO,UAAU,EAAK,OAAO,EAAM,OAAO,EAAE,MAAM,EAAM,KAAK,IAAI,CAAC,CAAC,CAAC;GAChL,UAAA,EAAE,uBAAuB;EACpB,CAAA,GAEV,kBAAC,GAAD;GACE,SAAQ;GACR,OAAO,EAAE,uBAAuB;GAChC,GAAI;GACE;GACN,QAAQ,KAAK,UAAU,CAAC;GACxB,YACE,EAAE,gBACA,MACA,kBAAC,GAAD;IACQ;IACN,QAAQ,EAAE;IACV,MAAM,EAAE;IACR,OAAO,EAAE;IACT,aAAa,EAAE;IACf,eAAe,EAAE;IACjB,WAAW,EAAE;IACb,YAAY,EAAE;IACd,OAAO,EAAE;IACT,cAAc,EAAE;IAChB,aAAa,EAAE;IACf,cAAc,EAAE;IAChB,UAAU,EAAE;IACZ,YAAY,EAAE;IACd,OAAO,EAAE;IACT,QAAQ,EAAE;IACV,OAAO,EAAE;IACT,WAAW,EAAE;IACb,UAAU,EAAE;IAEZ,UAAA,kBAAC,GAAD;KAAQ,MAAK;KAAK,SAAQ;KAAQ,WAAU;KAAqB,UAAU,EAAO;KAAS,eAAe,KAAK,EAAO,UAAU,EAAK,OAAO,EAAE,YAAY,EAAK,GAAG,CAAC,CAAC;KACjK,UAAA,EAAE,4BAA4B;IACzB,CAAA;GACG,CAAA;EAGlB,CAAA,CACE;;AAET;AA0BA,SAAgB,EAAY,EAAE,UAAO,YAAS,oBAAiB,YAAS,UAAO,MAAM,GAAW,WAAQ,CAAC,GAAG,SAAM,GAAG,KAAuB;CAC1I,IAAM,IAAQ,EAAM,SAAS,EAAE,YAAY,IACrC,IAAI,SAAe;EAAE,GAAG;EAAO;CAAM,IAAI,CAAC,GAAO,CAAK,CAAC,GACvD,IAAU,QACP,IAAY;EAAE,GAAG;EAAW,OAAO,EAAU,SAAS;CAAM,IAAI,IAAQ;EAAE,QAAQ,CAAC,CAAK;EAAG,MAAM;EAAqB;CAAM,IAAI,MACvI;EAAC;EAAW;EAAO;CAAK,CAC1B,GACM,IAAO,EAAW,KAAmB,KAAW,IAAU,OAAO,GAAS,GAAG,EAAE,QAAQ,EAAE,OAAO,CAAC,GACjG,IAAO,EAAmB,IAAkB,IAAI,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,GAC1E,IAAO,EAAW,IAAU;EAAE,GAAG;EAAS,OAAO,EAAQ,SAAS;CAAM,IAAI,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,GACtG,IAAS,EAAY,GAAS,EAAE,QAAQ,EAAE,OAAO,CAAC,GAClD,IAAgB,IAClB,IACA,KAAW,WACF;EACL,IAAM,IAAI,IAAU,IAAO;EAC3B,OAAO;GAAE,OAAO,EAAE;GAAO,SAAS,EAAE;GAAS,OAAO,EAAE;GAAO,aAAa,EAAE,OAAO,SAAS;GAAkB,QAAQ,EAAE;EAAO;CACjI,EAAA,CAAG,IACH,GACA,EAAE,aAAU,EAAS,EAAE,QAAQ,EAAE,OAAO,CAAC,GACzC,IAAU,EAAE,YAAY,IAAkB,oBAAoB,IAAU,YAAY,IAAQ,aAAa,IAAU,SAAS,YAC5H,IAAW,EAAY,EAAK,OAAO;EAAE;EAAS,QAAQ,KAAK,UAAU;GAAC;GAAS;GAAiB;GAAS;GAAO;GAAS;EAAC,CAAC;EAAG,QAAQ,EAAE;CAAO,CAAC,GAChJ,IAAS,KAAW,KAAmB,KAAW,KAAS,GAC3D,IAAS,EAAE,OAAO,EAAe,EAAE,IAAI,IAAI,KAAA;CAEjD,OADI,CAAC,KAAU,EAAK,eAAgB,CAAC,EAAK,WAAW,CAAC,EAAK,MAAM,SAAgB,OAE/E,kBAAC,GAAD;EAAkB,YAAY,EAAE;EAAY,WAAW,EAAG,sBAAsB,EAAE,SAAS;EAAG,aAAU;EAAe,gBAAc;EAArI,UAAA,CACE,kBAAC,OAAD;GAAK,WAAU;GAAf,UAAA,CACE,kBAAC,MAAD;IAAI,WAAU;IAA2B,UAAA;GAAU,CAAA,GAClD,CACE;EACL,CAAA,GAAA,kBAAC,MAAD;GAAI,WAAW,GAAQ,aAAa;GAA0C,OAAO,GAAQ;GACzF,WAAA,EAAK,WAAW,CAAC,EAAK,MAAM,SAAS,MAAM,KAAK,EAAE,QAAQ,EAAE,SAAS,IAAI,IAAI,EAAK,MAAA,CAAO,KAAK,GAAM,MACpG,kBAAC,MAAD;IAAwB,WAAW,IAAS,KAAA,IAAY;IAAoC,KAAK,IAAO,EAAS,MAAM,EAAK,IAAI,IAAI,CAAC,IAAI,KAAA;IACtI,UAAC,IAEE,EAAE,aACJ,EAAE,WAAW,GAAM,IAAI,CAAC,IAExB,kBAAC,GAAD;KACQ;KACN,QAAQ,EAAE;KACV,MAAM,EAAE;KACR,OAAO,EAAE;KACT,aAAa,EAAE;KACf,eAAe,EAAE;KACjB,WAAW,EAAE;KACb,YAAY,EAAE;KACd,OAAO,EAAE;KACT,cAAc,EAAE;KAChB,aAAa,EAAE;KACf,cAAc,EAAE;KAChB,UAAU,EAAE;KACZ,YAAY,EAAE;KACd,OAAO,EAAE,SAAS,GAAO;KACzB,QAAQ,EAAE;KACV,OAAO,EAAE;KACT,WAAW,EAAE;KACb,UAAU,EAAE;KACZ,cAAc,EAAS,MAAM,EAAK,IAAI,IAAI,CAAC;IAC5C,CAAA,IAzBD,kBAAC,GAAD;KAAU,WAAW,EAAE,gBAAgB,KAAA,IAAY,EAAE,UAAU,cAAc,wBAAwB,wBAAwB;KAAU,OAAO,EAAE,gBAAgB,KAAA,IAAY,KAAA,IAAY,EAAE,aAAa,EAAE,YAAY;IAAI,CAAA;GA2BzN,GA7BK,GAAM,MAAM,CA6BjB,CACL;EACC,CAAA,CACY;;AAEtB"}
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 { ContentFilterSettings, ReleaseDateIndex, type ContentFilterSettingsProps, type ReleaseDateIndexProps } from './filters.js';
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,5 @@
1
+ /** A local layout transition for a cached reveal, not an image-loading policy. */
2
+ export declare function useReleaseReveal(identity: string): {
3
+ grid: import('react').RefObject<HTMLOListElement | null>;
4
+ prepare: (focus?: boolean) => void;
5
+ };
@@ -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 ee, useId as te, useLayoutEffect as l, useMemo as u, useRef as d, useState as f } from "react";
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 E } from "react-dom";
13
+ import { createPortal as ne } from "react-dom";
14
14
  //#region src/components/content/search.tsx
15
- var D = {
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 O(e, t, n, r) {
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 l(() => {
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 k({ kind: e }) {
80
- let t = D[e];
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 ne({ value: e, formatter: t }) {
90
- return !e || Number.isNaN(Date.parse(e)) ? null : /* @__PURE__ */ m("time", {
91
- dateTime: e,
92
- className: "shrink-0 text-xs text-muted-foreground",
93
- children: t.format(new Date(e))
94
- });
95
- }
96
- function A(l) {
97
- let { t: y, error: b, language: x } = t(), C = te(), w = c(r)?.navigate, [D, A] = f(l.defaultValue ?? ""), [j, M] = f(!1), [N, P] = f(-1), F = d(null), I = d(null), L = d(null), R = l.presentation === "header", z = O(j, R, F, L), { top: B } = z, V = u(() => new Intl.DateTimeFormat(x, { dateStyle: "short" }), [x]), H = !D.trim(), U = o(D, {
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
- searchedAt: e.searched_at
108
- })) : [...U.contents.map((e) => ({
99
+ query: e.query
100
+ })) : [...B.contents.map((e) => ({
109
101
  type: "content",
110
102
  card: e
111
- })), ...U.entities.map((e) => ({
103
+ })), ...B.entities.map((e) => ({
112
104
  type: "group",
113
105
  group: e
114
106
  }))], [
115
- H,
116
- W.searches,
117
- U.contents,
118
- U.entities
119
- ]), G = H || U.query === D.trim(), K = G ? re : [], q = K[N], ie = H ? W.loading : !G || U.loading, J = H && l.recent !== !1, ae = H ? !R && J && !W.error ? "contents.noRecentSearches" : "contents.searchHint" : "contents.noResults";
120
- ee(() => {
121
- if (!j) return;
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
- !F.current?.contains(e.target) && !I.current?.contains(e.target) && M(!1);
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
- }, [j]);
118
+ }, [k]);
127
119
  let Y = () => {
128
- M(!0);
120
+ A(!0);
129
121
  }, X = (e) => {
130
- M(!1), e && l.onSearch(e);
122
+ A(!1), e && u.onSearch(e);
131
123
  }, Z = (e) => {
132
- if (e.type === "recent") return A(e.query), X(e.query);
133
- let t = l.href ? l.href(e.type === "content" ? e.card : e.group) : e.type === "content" ? e.card.path : e.group.path;
134
- M(!1), t && (w ? w(t) : location.assign(t));
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(), L.current?.focus(), M(!1), P(-1));
128
+ e.key === "Escape" && (e.preventDefault(), F.current?.focus(), A(!1), M(-1));
137
129
  }, $ = /* @__PURE__ */ h(p, { children: [
138
- J && /* @__PURE__ */ h("div", {
139
- className: g("flex items-center justify-between gap-2 text-muted-foreground", R ? "mb-4 text-base font-semibold" : "px-2 py-1 text-xs"),
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: `${C}-recent`,
133
+ id: `${x}-recent`,
142
134
  className: "flex items-center gap-2",
143
- children: [R && /* @__PURE__ */ m(T, {
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
- }), K.length > 0 && /* @__PURE__ */ m("button", {
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 W.clear(),
156
- children: y(R ? "contents.clearAllRecent" : "contents.clearRecent")
147
+ onClick: () => void V.clear(),
148
+ children: y(I ? "contents.clearAllRecent" : "contents.clearRecent")
157
149
  })]
158
150
  }),
159
151
  /* @__PURE__ */ m("ul", {
160
- id: `${C}-list`,
152
+ id: `${x}-list`,
161
153
  role: "listbox",
162
- "aria-label": y(H ? "contents.recentSearches" : "contents.suggestions"),
163
- className: g("grid", R && "gap-2"),
164
- children: K.map((t, n) => /* @__PURE__ */ m("li", {
165
- id: `${C}-${n}`,
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 === 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", R ? 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 === N && "bg-accent text-accent-foreground"),
171
- onMouseEnter: () => P(n),
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: R && t.type !== "recent" ? /* @__PURE__ */ h(p, { children: [
176
- /* @__PURE__ */ m(k, { kind: t.type === "content" ? t.card.kind : t.group.kind }),
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(l.labels, t.type === "content" ? t.card.kind : t.group.kind)
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(l.labels, t.group.kind)
193
+ children: s(u.labels, t.group.kind)
202
194
  })] }) : /* @__PURE__ */ h(p, { children: [
203
195
  /* @__PURE__ */ m(T, {
204
- icon: R ? S : v,
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 W.clear(t.query),
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
- ie ? /* @__PURE__ */ m("div", {
223
+ K ? /* @__PURE__ */ m("div", {
236
224
  role: "status",
237
- className: g("text-muted-foreground", R ? "flex justify-center py-4" : "px-3 py-2"),
238
- children: R ? /* @__PURE__ */ h(p, { children: [/* @__PURE__ */ m("span", {
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
- }) : !H && U.error ? /* @__PURE__ */ m("p", {
233
+ }) : !z && B.error ? /* @__PURE__ */ m("p", {
246
234
  role: "alert",
247
- className: g("px-3 py-2 text-destructive", R && "text-center"),
248
- children: b(U.error)
249
- }) : K.length === 0 && /* @__PURE__ */ h("div", {
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", R ? "flex flex-col items-center gap-2 py-8 text-center" : "px-3 py-2"),
252
- children: [R && /* @__PURE__ */ m(T, {
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(ae, { q: D.trim() }) })]
245
+ }), /* @__PURE__ */ m("p", { children: y(J, { q: w.trim() }) })]
258
246
  })
259
247
  ] });
260
248
  return /* @__PURE__ */ h(n, {
261
- ref: F,
262
- appearance: l.appearance,
263
- className: g("relative text-sm", l.className),
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) && !I.current?.contains(e.relatedTarget) && M(!1);
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(), j && q) return Z(q);
273
- X(D.trim());
260
+ if (e.preventDefault(), k && G) return Z(G);
261
+ X(w.trim());
274
262
  },
275
263
  children: [
276
- R ? /* @__PURE__ */ m("button", {
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: L,
294
- type: R ? "text" : "search",
281
+ ref: F,
282
+ type: I ? "text" : "search",
295
283
  role: "combobox",
296
284
  "aria-label": y("contents.search"),
297
- "aria-expanded": j,
298
- "aria-controls": `${C}-list`,
299
- "aria-activedescendant": j && q ? `${C}-${N}` : void 0,
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", R && "rounded-full border-border/50 bg-foreground/10 pe-9 text-base shadow-none md:text-base"),
303
- value: D,
304
- placeholder: l.placeholder ?? y("contents.searchPlaceholder"),
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
- A(e.target.value), M(!0), P(-1);
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(), M(!1), P(-1);
300
+ e.preventDefault(), A(!1), M(-1);
313
301
  return;
314
302
  }
315
- e.key === "ArrowDown" ? (e.preventDefault(), Y(), P((e) => j ? Math.min(K.length - 1, e + 1) : 0)) : e.key === "ArrowUp" && j && (e.preventDefault(), P((e) => Math.max(-1, e - 1)));
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
- R && (D || j) && /* @__PURE__ */ m("button", {
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(D ? "contents.clearSearch" : "common.close"),
309
+ "aria-label": y(w ? "contents.clearSearch" : "common.close"),
322
310
  onMouseDown: (e) => e.preventDefault(),
323
311
  onClick: () => {
324
- L.current?.focus(), P(-1), D ? (A(""), M(!0)) : M(!1);
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
- }), j && (R ? E(/* @__PURE__ */ h(n, {
335
- appearance: l.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: B },
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: I,
332
+ ref: P,
345
333
  style: {
346
- width: z.width,
347
- marginLeft: z.left
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 - ${B}px - 1.875rem))` },
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: I,
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 { A as SearchBox };
359
+ export { k as SearchBox };
372
360
 
373
361
  //# sourceMappingURL=search.js.map