@openrails/contentkit-ui 0.103.2 → 0.104.0-rc.2
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 +111 -49
- package/dist/client/beacons.d.ts +3 -13
- package/dist/client/beacons.js +8 -46
- package/dist/client/beacons.js.map +1 -1
- package/dist/client/client.d.ts +3 -0
- package/dist/client/client.js +45 -43
- package/dist/client/client.js.map +1 -1
- package/dist/client/content/favorites.d.ts +2 -0
- package/dist/client/content/favorites.js.map +1 -1
- package/dist/client/content/types.d.ts +3 -0
- package/dist/client/content/types.js.map +1 -1
- package/dist/client/contents.d.ts +7 -1
- package/dist/client/contents.js +8 -0
- package/dist/client/contents.js.map +1 -1
- package/dist/client/discovery.d.ts +34 -5
- package/dist/client/discovery.js +23 -2
- package/dist/client/discovery.js.map +1 -1
- package/dist/client/engagement.d.ts +70 -0
- package/dist/client/engagement.js +191 -0
- package/dist/client/engagement.js.map +1 -0
- package/dist/client/generated/routes.d.ts +74 -35
- package/dist/client/generated/routes.js +119 -60
- package/dist/client/generated/routes.js.map +1 -1
- package/dist/client/generated/wire.d.ts +88 -85
- package/dist/client/generated/wire.js.map +1 -1
- package/dist/client/guest-reading-history.d.ts +40 -0
- package/dist/client/guest-reading-history.js +144 -0
- package/dist/client/guest-reading-history.js.map +1 -0
- package/dist/client/index.d.ts +5 -3
- package/dist/client/media/windows.d.ts +2 -0
- package/dist/client/media/windows.js +4 -1
- package/dist/client/media/windows.js.map +1 -1
- package/dist/client/stats.d.ts +4 -20
- package/dist/client/stats.js +0 -11
- package/dist/client/stats.js.map +1 -1
- package/dist/client/versions.d.ts +8 -6
- package/dist/client/versions.js +15 -10
- package/dist/client/versions.js.map +1 -1
- package/dist/client.js +24 -22
- package/dist/components/admin/bulk-drafts.d.ts +17 -0
- package/dist/components/admin/bulk-drafts.js +41 -0
- package/dist/components/admin/bulk-drafts.js.map +1 -0
- package/dist/components/admin/content-admin.d.ts +1 -1
- package/dist/components/admin/content-admin.js.map +1 -1
- package/dist/components/admin/content-manager.d.ts +11 -3
- package/dist/components/admin/content-manager.js +751 -394
- package/dist/components/admin/content-manager.js.map +1 -1
- package/dist/components/admin/group-manager.d.ts +8 -3
- package/dist/components/admin/group-manager.js +218 -146
- package/dist/components/admin/group-manager.js.map +1 -1
- package/dist/components/admin/page-notices.d.ts +16 -0
- package/dist/components/admin/page-notices.js +113 -0
- package/dist/components/admin/page-notices.js.map +1 -0
- package/dist/components/admin/page-picker.d.ts +38 -0
- package/dist/components/admin/page-picker.js +159 -0
- package/dist/components/admin/page-picker.js.map +1 -0
- package/dist/components/admin/quick-search.d.ts +14 -0
- package/dist/components/admin/quick-search.js +60 -0
- package/dist/components/admin/quick-search.js.map +1 -0
- package/dist/components/admin/reader-content-editor.js +382 -321
- package/dist/components/admin/reader-content-editor.js.map +1 -1
- package/dist/components/admin/reader-manifest.js +181 -179
- package/dist/components/admin/reader-manifest.js.map +1 -1
- package/dist/components/admin/shell.d.ts +10 -2
- package/dist/components/admin/shell.js +114 -42
- package/dist/components/admin/shell.js.map +1 -1
- package/dist/components/admin/stats-dashboard.d.ts +4 -9
- package/dist/components/admin/stats-dashboard.js +303 -207
- package/dist/components/admin/stats-dashboard.js.map +1 -1
- package/dist/components/admin/upload-queue.d.ts +14 -0
- package/dist/components/admin/upload-queue.js +115 -98
- package/dist/components/admin/upload-queue.js.map +1 -1
- package/dist/components/content/card.d.ts +31 -3
- package/dist/components/content/card.js +324 -158
- package/dist/components/content/card.js.map +1 -1
- package/dist/components/content/daily-releases.js +144 -190
- package/dist/components/content/daily-releases.js.map +1 -1
- package/dist/components/content/editor-save.js +27 -25
- package/dist/components/content/editor-save.js.map +1 -1
- package/dist/components/content/editors.js +274 -253
- package/dist/components/content/editors.js.map +1 -1
- package/dist/components/content/favorites.d.ts +6 -0
- package/dist/components/content/favorites.js +47 -0
- package/dist/components/content/favorites.js.map +1 -0
- package/dist/components/content/filter-dialog.js +19 -15
- package/dist/components/content/filter-dialog.js.map +1 -1
- package/dist/components/content/filters.d.ts +2 -0
- package/dist/components/content/filters.js +5 -1
- package/dist/components/content/filters.js.map +1 -1
- package/dist/components/content/grid.d.ts +44 -16
- package/dist/components/content/grid.js +308 -270
- package/dist/components/content/grid.js.map +1 -1
- package/dist/components/content/history.d.ts +6 -0
- package/dist/components/content/history.js +111 -0
- package/dist/components/content/history.js.map +1 -0
- package/dist/components/content/index.d.ts +6 -3
- package/dist/components/content/listing.d.ts +14 -3
- package/dist/components/content/listing.js +65 -56
- package/dist/components/content/listing.js.map +1 -1
- package/dist/components/content/offer-fields.js +6 -6
- package/dist/components/content/paged-grid.d.ts +10 -0
- package/dist/components/content/paged-grid.js +116 -0
- package/dist/components/content/paged-grid.js.map +1 -0
- package/dist/components/content/pagination.d.ts +19 -0
- package/dist/components/content/pagination.js +97 -0
- package/dist/components/content/pagination.js.map +1 -0
- package/dist/components/content/paywall.d.ts +9 -0
- package/dist/components/content/paywall.js +27 -3
- package/dist/components/content/paywall.js.map +1 -1
- package/dist/components/content/post-list.d.ts +37 -0
- package/dist/components/content/post-list.js +203 -0
- package/dist/components/content/post-list.js.map +1 -0
- package/dist/components/content/reader-settings.d.ts +2 -0
- package/dist/components/content/reader-settings.js +1 -1
- package/dist/components/content/reader-settings.js.map +1 -1
- package/dist/components/content/search.d.ts +11 -4
- package/dist/components/content/search.js +22 -11
- package/dist/components/content/search.js.map +1 -1
- package/dist/components/content/summary-tile.d.ts +14 -0
- package/dist/components/content/summary-tile.js +63 -0
- package/dist/components/content/summary-tile.js.map +1 -0
- package/dist/components/groups/banner.d.ts +6 -2
- package/dist/components/groups/banner.js +79 -61
- package/dist/components/groups/banner.js.map +1 -1
- package/dist/components/groups/directory.d.ts +12 -2
- package/dist/components/groups/directory.js +168 -105
- package/dist/components/groups/directory.js.map +1 -1
- package/dist/components/groups/editor.js +10 -10
- package/dist/components/groups/editor.js.map +1 -1
- package/dist/components/groups/entity-chips.d.ts +61 -0
- package/dist/components/groups/entity-chips.js +382 -0
- package/dist/components/groups/entity-chips.js.map +1 -0
- package/dist/components/groups/group-members.d.ts +29 -0
- package/dist/components/groups/group-members.js +474 -0
- package/dist/components/groups/group-members.js.map +1 -0
- package/dist/components/groups/header.js +53 -45
- package/dist/components/groups/header.js.map +1 -1
- package/dist/components/groups/index.d.ts +3 -1
- package/dist/components/groups/memberships.d.ts +3 -36
- package/dist/components/groups/memberships.js +105 -480
- package/dist/components/groups/memberships.js.map +1 -1
- package/dist/components/groups/scoped.d.ts +23 -0
- package/dist/components/groups/scoped.js +92 -0
- package/dist/components/groups/scoped.js.map +1 -0
- package/dist/components/media-folder-editor.d.ts +5 -0
- package/dist/components/media-folder-editor.js +419 -231
- package/dist/components/media-folder-editor.js.map +1 -1
- package/dist/components/sortable-list.d.ts +5 -1
- package/dist/components/sortable-list.js +97 -81
- package/dist/components/sortable-list.js.map +1 -1
- package/dist/components/video-mini-player.d.ts +4 -1
- package/dist/components/video-mini-player.js +77 -71
- package/dist/components/video-mini-player.js.map +1 -1
- package/dist/components/video-player.d.ts +4 -1
- package/dist/components/video-player.js +280 -276
- package/dist/components/video-player.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +41 -34
- package/dist/locales/admin/de.js +80 -47
- package/dist/locales/admin/de.js.map +1 -1
- package/dist/locales/admin/en.d.ts +74 -41
- package/dist/locales/admin/en.js +80 -47
- package/dist/locales/admin/en.js.map +1 -1
- package/dist/locales/admin/es.js +80 -47
- package/dist/locales/admin/es.js.map +1 -1
- package/dist/locales/admin/ja.js +80 -47
- package/dist/locales/admin/ja.js.map +1 -1
- package/dist/locales/admin/ko.js +80 -47
- package/dist/locales/admin/ko.js.map +1 -1
- package/dist/locales/admin/zh.js +80 -47
- package/dist/locales/admin/zh.js.map +1 -1
- package/dist/locales/de.js +1 -0
- package/dist/locales/de.js.map +1 -1
- package/dist/locales/discovery/de.js +25 -2
- package/dist/locales/discovery/de.js.map +1 -1
- package/dist/locales/discovery/en.d.ts +24 -1
- package/dist/locales/discovery/en.js +25 -2
- package/dist/locales/discovery/en.js.map +1 -1
- package/dist/locales/discovery/es.js +25 -2
- package/dist/locales/discovery/es.js.map +1 -1
- package/dist/locales/discovery/ja.js +25 -2
- package/dist/locales/discovery/ja.js.map +1 -1
- package/dist/locales/discovery/ko.js +25 -2
- package/dist/locales/discovery/ko.js.map +1 -1
- package/dist/locales/discovery/zh.js +25 -2
- package/dist/locales/discovery/zh.js.map +1 -1
- package/dist/locales/en.d.ts +119 -44
- package/dist/locales/en.js +1 -0
- package/dist/locales/en.js.map +1 -1
- package/dist/locales/es.js +1 -0
- package/dist/locales/es.js.map +1 -1
- package/dist/locales/groups/de.js +22 -4
- package/dist/locales/groups/de.js.map +1 -1
- package/dist/locales/groups/en.d.ts +20 -2
- package/dist/locales/groups/en.js +22 -4
- package/dist/locales/groups/en.js.map +1 -1
- package/dist/locales/groups/es.js +22 -4
- package/dist/locales/groups/es.js.map +1 -1
- package/dist/locales/groups/ja.js +22 -4
- package/dist/locales/groups/ja.js.map +1 -1
- package/dist/locales/groups/ko.js +22 -4
- package/dist/locales/groups/ko.js.map +1 -1
- package/dist/locales/groups/zh.js +22 -4
- package/dist/locales/groups/zh.js.map +1 -1
- package/dist/locales/ja.js +1 -0
- package/dist/locales/ja.js.map +1 -1
- package/dist/locales/ko.js +1 -0
- package/dist/locales/ko.js.map +1 -1
- package/dist/locales/zh.js +1 -0
- package/dist/locales/zh.js.map +1 -1
- package/dist/react/discovery.d.ts +36 -17
- package/dist/react/discovery.js +305 -221
- package/dist/react/discovery.js.map +1 -1
- package/dist/react/groups.d.ts +19 -3
- package/dist/react/groups.js +49 -22
- package/dist/react/groups.js.map +1 -1
- package/dist/react/index.d.ts +7 -4
- package/dist/react/page-notices.d.ts +13 -0
- package/dist/react/page-notices.js +38 -0
- package/dist/react/page-notices.js.map +1 -0
- package/dist/react/post-reads.d.ts +23 -0
- package/dist/react/post-reads.js +90 -0
- package/dist/react/post-reads.js.map +1 -0
- package/dist/react/remembered-sort.d.ts +5 -0
- package/dist/react/remembered-sort.js +29 -0
- package/dist/react/remembered-sort.js.map +1 -0
- package/dist/react/resources.d.ts +3 -1
- package/dist/react/resources.js +6 -2
- package/dist/react/resources.js.map +1 -1
- package/dist/react/stats.d.ts +3 -7
- package/dist/react/stats.js +2 -6
- package/dist/react/stats.js.map +1 -1
- package/dist/react/use-engagement.d.ts +28 -0
- package/dist/react/use-engagement.js +226 -0
- package/dist/react/use-engagement.js.map +1 -0
- package/dist/react/use-resource.d.ts +4 -3
- package/dist/react/use-resource.js +2 -1
- package/dist/react/use-resource.js.map +1 -1
- package/dist/react.js +29 -26
- package/dist/styles.css +1 -1
- package/dist/ui/dialog.d.ts +2 -1
- package/dist/ui/dialog.js +23 -20
- package/dist/ui/dialog.js.map +1 -1
- package/dist/urls/index.d.ts +1 -1
- package/dist/urls.js +2 -2
- package/dist/urls.js.map +1 -1
- package/package.json +1 -1
- package/dist/components/admin/page-review.d.ts +0 -13
- package/dist/components/admin/page-review.js +0 -225
- package/dist/components/admin/page-review.js.map +0 -1
- package/dist/react/page-review.d.ts +0 -14
- package/dist/react/page-review.js +0 -37
- package/dist/react/page-review.js.map +0 -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, 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 ?? \"relative 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 { useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState, type CSSProperties, type ReactNode } from \"react\";\nimport type { ContentKitUiAppearance } from \"../../appearance.js\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport type { AccessMode, ContentQuery, HistoryQuery, RailQuery, SearchQuery } from \"../../client/discovery.js\";\nimport type { Card, ListingSummary } from \"../../client/generated/wire.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { useContents, useExposure, useHistory, useRecommendations, useSearch, useSimilar, type UseRail } from \"../../react/discovery.js\";\nimport { useKinds } from \"../../react/groups.js\";\nimport type { UseList } from \"../../react/use-resource.js\";\nimport { ContentKitContext } from \"../../react/context.js\";\nimport { useContentScope } 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 } from \"../social/parts.js\";\nimport { ContentFilterDialog } from \"./filter-dialog.js\";\nimport { useReleaseReveal } from \"./release-reveal.js\";\nimport { ContentSummaryTile } from \"./summary-tile.js\";\nimport { ContentCard, type ContentCardProps } from \"./card.js\";\nimport { PagedContentGrid, PagedSearchResults } from \"./paged-grid.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 /** Collapse paid/locked cards into a counted tile, or hide them; default show. */\n locked?: LockedCards;\n /** Optional-filter summary tiles are on by default; hosts may suppress them in previews. */\n showFilteredSummary?: boolean;\n /** Show the collapsed locked tile only at this measured grid width. Default 0. */\n lockedSummaryMinColumns?: number;\n /** Limit rendered preview tiles without changing the matching selection or summary counts. */\n cardLimit?: number;\n /** Limit cards in mobile previews without mounting their images. */\n mobileCardLimit?: number;\n /** Host-specific nouns for summary controls. */\n summaryLabels?: { premium?: (count: number) => string; filtered?: (count: number) => string };\n /** Opt into a fluid grid, optionally with a desktop column ceiling. */\n grid?: CardGridLayout;\n /** Numbered pages with a shown range instead of load more or infinite scroll. */\n pagination?: \"pages\";\n /** The numbered page, from 1; with onPageChange the host keeps it (in its URL). */\n page?: number;\n onPageChange?: (page: number) => void;\n /** Each numbered page's link, for crawlers and opening in a new tab. */\n pageHref?: (page: number) => string;\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 =\n 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:\n \"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 carries exact selection-wide counts when the API was asked for them. */\ntype SummaryList = UseList<Card> & { summary?: ListingSummary };\ntype GridProps = CardListProps & { list: SummaryList; render: string; filterKinds?: readonly string[]; filterLanguage?: string; horizontal?: boolean };\n\n/** A list's cards and summary controls share one responsive, animated grid. */\nexport function CardGrid(p: GridProps) {\n const { client, viewer, language } = useContentScope(p.client);\n const context = useContext(ContentKitContext);\n const [boundary, setBoundary] = useState({ client, viewer, language, access: context?.accessRevision, render: p.render, key: 0 });\n let session = boundary;\n if (boundary.client !== client || boundary.viewer !== viewer || boundary.language !== language || boundary.access !== context?.accessRevision || boundary.render !== p.render) {\n session = { client, viewer, language, access: context?.accessRevision, render: p.render, key: boundary.key + 1 };\n setBoundary(session);\n }\n return <CardGridBody key={session.key} {...p} />;\n}\n\nfunction CardGridBody({ list, render, filterKinds, filterLanguage, horizontal, ...p }: GridProps) {\n const m = useMessages();\n const { t } = m;\n const { kinds } = useKinds({ client: p.client });\n const [expanded, setExpanded] = useState(false);\n const [filtersOpen, setFiltersOpen] = useState(false);\n const filterTrigger = useRef<HTMLElement | null>(null);\n const [element, setElement] = useState<HTMLOListElement | null>(null);\n const [columns, setColumns] = useState(0);\n const [mobile, setMobile] = useState(() => globalThis.matchMedia?.(\"(max-width: 767px)\").matches ?? false);\n useLayoutEffect(() => {\n if (!element) return;\n const measure = () => {\n const tracks = getComputedStyle(element).gridTemplateColumns.trim();\n setColumns(tracks && tracks !== \"none\" ? tracks.split(/\\s+/).length : 0);\n setMobile(globalThis.matchMedia?.(\"(max-width: 767px)\").matches ?? false);\n };\n measure();\n const observer = typeof ResizeObserver === \"function\" ? new ResizeObserver(measure) : undefined;\n observer?.observe(element);\n addEventListener(\"resize\", measure);\n return () => {\n observer?.disconnect();\n removeEventListener(\"resize\", measure);\n };\n }, [element]);\n const lockedCount = list.summary?.locked_count ?? 0;\n const filteredCount = p.showFilteredSummary === false ? 0 : (list.summary?.filtered_count ?? 0);\n const collapse = p.locked === \"collapse\" && !expanded && lockedCount > 0;\n const lockedTile = collapse && lockedCount > 0 && columns >= Math.max(0, p.lockedSummaryMinColumns ?? 0);\n const entries = useMemo(() => {\n const result: ({ key: string; card: Card; position: number } | { key: string; premium: true; card?: Card })[] = [];\n let inserted = false;\n for (const [i, card] of list.items.entries()) {\n if (isLockedCard(card) && (collapse || p.locked === \"hide\")) {\n if (lockedTile && !inserted) {\n result.push({ key: \"premium-summary\", premium: true, card });\n inserted = true;\n }\n continue;\n }\n result.push({ key: card.id, card, position: i + 1 });\n }\n if (lockedTile && !inserted) result.push({ key: \"premium-summary\", premium: true });\n const limit = mobile && p.mobileCardLimit !== undefined ? p.mobileCardLimit : p.cardLimit;\n return limit !== undefined && Number.isFinite(limit) ? result.slice(0, Math.max(0, Math.floor(limit))) : result;\n }, [list.items, collapse, p.locked, lockedTile, mobile, p.mobileCardLimit, p.cardLimit]);\n const shown = useMemo(() => entries.flatMap((entry) => (\"premium\" in entry ? [] : [{ ...entry.card, position: entry.position }])), [entries]);\n const exposure = useExposure(shown, { 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 { grid: animationGrid, prepare } = useReleaseReveal(entries.map((entry) => entry.key).join(\"\\0\"));\n const grid = useCallback(\n (node: HTMLOListElement | null) => {\n animationGrid.current = node;\n setElement(node);\n },\n [animationGrid],\n );\n const layout: ReturnType<typeof cardGridLayout> = horizontal\n ? { className: \"relative flex snap-x gap-4 overflow-x-auto pb-2 [&>li]:w-36 [&>li]:shrink-0 [&>li]:snap-start sm:[&>li]:w-44\" }\n : cardGridLayout(p.grid);\n const lowerBound = list.summary?.truncated ? \"≥ \" : \"\";\n const premiumLabel = lowerBound + (p.summaryLabels?.premium?.(lockedCount) ?? m.plural(\"contents.lockedShow\", lockedCount));\n const filteredLabel = lowerBound + (p.summaryLabels?.filtered?.(filteredCount) ?? m.plural(\"releases.filteredMore\", filteredCount));\n const showGrid = entries.length > 0 || filteredCount > 0 || (collapse && lockedCount > 0);\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 <ol aria-hidden className={layout.className} style={layout.style}>\n {Array.from({ length: 8 }, (_, i) => (\n <li key={i}>\n <Skeleton\n className={p.aspectRatio === undefined ? (p.shape === \"landscape\" ? \"aspect-video w-full\" : \"aspect-[3/4] w-full\") : \"w-full\"}\n style={p.aspectRatio === undefined ? undefined : { aspectRatio: p.aspectRatio }}\n />\n </li>\n ))}\n </ol>\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 ) : !showGrid ? (\n <div className=\"text-muted-foreground\" role=\"status\">\n {p.empty ?? t(\"contents.empty\")}\n </div>\n ) : (\n <ol ref={grid} tabIndex={-1} className={layout.className} style={layout.style}>\n {entries.map((entry) =>\n \"premium\" in entry ? (\n <ContentSummaryTile\n key={entry.key}\n tileKey={entry.key}\n kind=\"premium\"\n contentKind={filterKinds?.[0] ?? entry.card?.kind}\n preview={entry.card}\n open\n cardProps={p}\n label={premiumLabel}\n onClick={(event) => {\n prepare(event.detail === 0);\n setExpanded(true);\n }}\n />\n ) : (\n <li key={entry.key} data-release-key={entry.key} ref={exposure.track(entry.card.id, entry.position)}>\n {p.renderCard ? (\n p.renderCard(entry.card, entry.position)\n ) : (\n <ContentCard\n card={entry.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 chipOverflow={p.chipOverflow}\n chipFit={p.chipFit}\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(entry.card.id, entry.position)}\n />\n )}\n </li>\n ),\n )}\n {filteredCount > 0 && (\n <ContentSummaryTile\n tileKey=\"filtered-summary\"\n kind=\"filtered\"\n contentKind={filterKinds?.[0] ?? list.items[0]?.kind}\n open\n cardProps={p}\n label={filteredLabel}\n onClick={(event) => {\n filterTrigger.current = event.currentTarget;\n setFiltersOpen(true);\n }}\n />\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 && (\n <p role=\"alert\" className=\"text-destructive\">\n {contentError(m, list.error)}\n </p>\n )}\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 <ContentFilterDialog\n open={filtersOpen}\n onOpenChange={setFiltersOpen}\n returnFocus={() => (filterTrigger.current?.isConnected ? filterTrigger.current : element)}\n client={p.client}\n kinds={filterKinds}\n language={filterLanguage}\n counts={list.summary?.filtered_entities}\n countsLowerBound={list.summary?.truncated}\n appearance={p.appearance}\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 replaces them with a counted tile\n * that reveals the cached previews in place (collapse).\n */\nexport type LockedCards = \"show\" | \"hide\" | \"collapse\";\n\n/** Settle locked-card admission before pagination, retaining explicit reader modes otherwise. */\nexport function cardListMode(locked: LockedCards | undefined, mode?: AccessMode): AccessMode | undefined {\n return locked === \"hide\" ? \"hide\" : locked === \"collapse\" ? \"lock\" : mode;\n}\n\nconst LOCKED_LEVELS: readonly string[] = [\"tier\", \"group\", \"ppv\", \"group_ppv\"];\nconst isLockedCard = (card: Card) => !card.access.allowed && !card.access.unknown && LOCKED_LEVELS.includes(card.access.level);\n\n/** Contents as cards (GET /v1/contents): any sort and filter, paged by load more, infinite scroll or numbered pages. */\nexport function ContentGrid(p: CardListProps & { query: ContentQuery }) {\n return p.pagination === \"pages\" ? <PagedContentGrid {...p} /> : <CursorContentGrid {...p} />;\n}\n\nfunction CursorContentGrid({ query, locked = \"show\", ...p }: CardListProps & { query: ContentQuery }) {\n const q = useMemo<ContentQuery>(\n () => ({\n ...query,\n summary: query.summary ?? true,\n limit: query.limit ?? p.pageSize,\n mode: cardListMode(locked, query.mode),\n }),\n [query, p.pageSize, locked],\n );\n const list = useContents(q, { client: p.client });\n return (\n <CardGrid\n {...p}\n locked={locked}\n list={list}\n render={JSON.stringify(q)}\n filterKinds={query.kind ? [query.kind] : undefined}\n filterLanguage={query.display_language ?? query.language}\n />\n );\n}\n\n/** Search results as cards; nothing until the query has text. */\nexport function SearchResults(p: CardListProps & { query: SearchQuery }) {\n return p.pagination === \"pages\" ? <PagedSearchResults {...p} /> : <CursorSearchResults {...p} />;\n}\n\nfunction CursorSearchResults({ query, ...p }: CardListProps & { query: SearchQuery }) {\n const { t } = useMessages();\n const q = useMemo(\n () => ({ ...query, mode: cardListMode(p.locked, query.mode), summary: query.summary ?? true, limit: query.limit ?? p.pageSize }),\n [query, p.pageSize, p.locked],\n );\n const list = useSearch(q, { client: p.client });\n if (!query.q.trim()) return null;\n return (\n <CardGrid\n surface=\"search\"\n empty={t(\"contents.noResults\", { q: query.q.trim() })}\n {...p}\n list={list}\n render={JSON.stringify(q)}\n filterKinds={query.kind ? [query.kind] : undefined}\n filterLanguage={query.display_language ?? query.language}\n />\n );\n}\n\nexport { ContentHistory } from \"./history.js\";\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, mode: cardListMode(p.locked, query.mode), summary: query.summary ?? true, limit }), [query, limit, p.locked]);\n const listing = useMemo(\n () =>\n listQuery\n ? { ...listQuery, mode: cardListMode(p.locked, listQuery.mode), summary: listQuery.summary ?? true, limit: listQuery.limit ?? limit }\n : group\n ? { groups: [group], sort: \"position\" as const, mode: cardListMode(p.locked), summary: true, limit }\n : null,\n [listQuery, group, limit, p.locked],\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, mode: cardListMode(p.locked, history.mode), summary: history.summary ?? true, limit: history.limit ?? limit } : null, {\n client: p.client,\n });\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, summary: l.summary, loading: l.loading, error: l.error, unavailable: l.error?.code === \"not_configured\", reload: l.reload };\n })()\n : like;\n const surface = p.surface ?? (recommendations ? \"recommendations\" : history ? \"history\" : group ? \"featured\" : listing ? \"list\" : \"similar\");\n const source = similar || recommendations || history || group || listing;\n if (!source || rail.unavailable || (!rail.loading && !rail.items.length && !rail.summary?.filtered_count && !rail.summary?.locked_count)) return null;\n const list: SummaryList = { ...rail, loadingMore: false, hasMore: false, loadMore: async () => {} };\n const kind = listQuery?.kind ?? history?.kind ?? q.kind;\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 <CardGrid\n {...p}\n className={undefined}\n surface={surface}\n horizontal={!p.grid}\n list={list}\n render={JSON.stringify([similar, recommendations, history, group, listing, q])}\n filterKinds={kind ? [kind] : undefined}\n filterLanguage={listQuery?.display_language ?? history?.display_language ?? q.display_language ?? listQuery?.language ?? history?.language ?? q.language}\n />\n </ContentKitUiRoot>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { HistoryQuery } from '../../client/discovery.js';
|
|
2
|
+
import { CardListProps } from './grid.js';
|
|
3
|
+
/** Account history or this browser's guest history, each removable, with a clear-all. */
|
|
4
|
+
export declare function ContentHistory({ query, ...p }: CardListProps & {
|
|
5
|
+
query?: HistoryQuery;
|
|
6
|
+
}): import('react').JSX.Element;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { useMessages as e } from "../../i18n/context.js";
|
|
2
|
+
import { useKinds as t } from "../../react/groups.js";
|
|
3
|
+
import { Button as n } from "../../ui/button.js";
|
|
4
|
+
import { formatDate as r, relativeTime as i, useAction as a } from "../social/parts.js";
|
|
5
|
+
import { useHistory as o } from "../../react/discovery.js";
|
|
6
|
+
import { ContentFilterDialog as s } from "./filter-dialog.js";
|
|
7
|
+
import { ContentCard as c } from "./card.js";
|
|
8
|
+
import { CardGrid as l } from "./grid.js";
|
|
9
|
+
import { useMemo as u, useRef as d, useState as f } from "react";
|
|
10
|
+
import { jsx as p, jsxs as m } from "react/jsx-runtime";
|
|
11
|
+
//#region src/components/content/history.tsx
|
|
12
|
+
function h({ query: r = {}, ...i }) {
|
|
13
|
+
let { t: h } = e(), _ = u(() => ({
|
|
14
|
+
...r,
|
|
15
|
+
summary: !0,
|
|
16
|
+
mode: i.locked === "collapse" ? "lock" : i.locked === "hide" ? "hide" : r.mode,
|
|
17
|
+
limit: r.limit ?? i.pageSize
|
|
18
|
+
}), [
|
|
19
|
+
r,
|
|
20
|
+
i.pageSize,
|
|
21
|
+
i.locked
|
|
22
|
+
]), v = o(_, { client: i.client }), y = a("history.forget"), { kinds: b } = t({ client: i.client }), [x, S] = f(!1), C = d(null);
|
|
23
|
+
return /* @__PURE__ */ m("div", {
|
|
24
|
+
className: "grid gap-3",
|
|
25
|
+
"data-ckui": "content-history",
|
|
26
|
+
children: [
|
|
27
|
+
v.items.length > 0 && /* @__PURE__ */ p(n, {
|
|
28
|
+
size: "sm",
|
|
29
|
+
variant: "outline",
|
|
30
|
+
className: "justify-self-end",
|
|
31
|
+
disabled: y.pending,
|
|
32
|
+
onClick: () => void y.run(() => v.forget(r.kind ? { kind: r.kind } : {})),
|
|
33
|
+
children: h("contents.clearHistory")
|
|
34
|
+
}),
|
|
35
|
+
v.filterSummaryUnknown && i.showFilteredSummary !== !1 && /* @__PURE__ */ p(n, {
|
|
36
|
+
ref: C,
|
|
37
|
+
variant: "outline",
|
|
38
|
+
className: "justify-self-start",
|
|
39
|
+
onClick: () => S(!0),
|
|
40
|
+
children: h("releases.manageFilters")
|
|
41
|
+
}),
|
|
42
|
+
/* @__PURE__ */ p(s, {
|
|
43
|
+
client: i.client,
|
|
44
|
+
language: _.language,
|
|
45
|
+
open: x,
|
|
46
|
+
onOpenChange: S,
|
|
47
|
+
returnFocus: () => C.current
|
|
48
|
+
}),
|
|
49
|
+
/* @__PURE__ */ p(l, {
|
|
50
|
+
surface: "history",
|
|
51
|
+
empty: h("contents.historyEmpty"),
|
|
52
|
+
...i,
|
|
53
|
+
list: v,
|
|
54
|
+
render: JSON.stringify(_),
|
|
55
|
+
filterLanguage: _.language,
|
|
56
|
+
renderCard: i.renderCard ?? ((e) => /* @__PURE__ */ m(c, {
|
|
57
|
+
card: e,
|
|
58
|
+
client: i.client,
|
|
59
|
+
href: i.href,
|
|
60
|
+
shape: i.shape,
|
|
61
|
+
aspectRatio: i.aspectRatio,
|
|
62
|
+
titlePosition: i.titlePosition,
|
|
63
|
+
loadMedia: i.loadMedia,
|
|
64
|
+
groupKinds: i.groupKinds,
|
|
65
|
+
chips: i.chips,
|
|
66
|
+
chipOverflow: i.chipOverflow,
|
|
67
|
+
chipFit: i.chipFit,
|
|
68
|
+
filterEntity: i.filterEntity,
|
|
69
|
+
entityStyle: i.entityStyle,
|
|
70
|
+
entityLayout: i.entityLayout,
|
|
71
|
+
metadata: i.metadata,
|
|
72
|
+
showCounts: i.showCounts,
|
|
73
|
+
tiers: i.tiers,
|
|
74
|
+
labels: i.labels,
|
|
75
|
+
owner: i.owner,
|
|
76
|
+
ownerHref: i.ownerHref,
|
|
77
|
+
decorate: i.decorate,
|
|
78
|
+
children: [/* @__PURE__ */ p(g, {
|
|
79
|
+
card: e,
|
|
80
|
+
presentation: b?.kinds[e.kind]?.presentation
|
|
81
|
+
}), /* @__PURE__ */ p(n, {
|
|
82
|
+
size: "xs",
|
|
83
|
+
variant: "ghost",
|
|
84
|
+
className: "justify-self-start",
|
|
85
|
+
disabled: y.pending,
|
|
86
|
+
onClick: () => void y.run(() => v.forget({ content_id: e.id })),
|
|
87
|
+
children: h("contents.removeFromHistory")
|
|
88
|
+
})]
|
|
89
|
+
}))
|
|
90
|
+
})
|
|
91
|
+
]
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
function g({ card: t, presentation: n }) {
|
|
95
|
+
let a = e(), o = t.viewer;
|
|
96
|
+
if (!o) return null;
|
|
97
|
+
let s = o.completed ? o.progress_max : Math.min(o.progress, o.progress_max), c = o.progress_max > 0 && n === "reader" ? a.plural("contents.pageProgress", o.progress_max, { progress: s }) : o.progress_max > 0 && n === "player" ? a.plural("contents.minuteProgress", Math.max(1, Math.round(o.progress_max / 60)), { progress: Math.round(s / 60) }) : null;
|
|
98
|
+
return !o.last_read_at && !c ? null : /* @__PURE__ */ m("p", {
|
|
99
|
+
className: "flex flex-wrap gap-x-2 text-xs tabular-nums text-muted-foreground",
|
|
100
|
+
"data-ckui": "history-progress",
|
|
101
|
+
children: [o.last_read_at && /* @__PURE__ */ p("time", {
|
|
102
|
+
dateTime: o.last_read_at,
|
|
103
|
+
title: r(a, o.last_read_at),
|
|
104
|
+
children: a.t(n === "player" ? "contents.watchedAgo" : "contents.readAgo", { time: i(a, o.last_read_at) })
|
|
105
|
+
}), c && /* @__PURE__ */ p("span", { children: c })]
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
//#endregion
|
|
109
|
+
export { h as ContentHistory };
|
|
110
|
+
|
|
111
|
+
//# sourceMappingURL=history.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history.js","names":[],"sources":["../../../src/components/content/history.tsx"],"sourcesContent":["import { useMemo, useRef, useState } from \"react\";\nimport type { Card } from \"../../client/generated/wire.js\";\nimport type { HistoryQuery } from \"../../client/discovery.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { useHistory } from \"../../react/discovery.js\";\nimport { useKinds } from \"../../react/groups.js\";\nimport { Button } from \"#ckui/ui/button\";\nimport { formatDate, relativeTime, useAction } from \"../social/parts.js\";\nimport { ContentFilterDialog } from \"./filter-dialog.js\";\nimport { ContentCard } from \"./card.js\";\nimport { CardGrid, type CardListProps } from \"./grid.js\";\n\n/** Account history or this browser's guest history, each removable, with a clear-all. */\nexport function ContentHistory({ query = {}, ...p }: CardListProps & { query?: HistoryQuery }) {\n const { t } = useMessages();\n const q = useMemo(() => ({ ...query, summary: true, mode: p.locked === \"collapse\" ? \"lock\" as const : p.locked === \"hide\" ? \"hide\" as const : query.mode, limit: query.limit ?? p.pageSize }), [query, p.pageSize, p.locked]);\n const list = useHistory(q, { client: p.client });\n const forget = useAction(\"history.forget\");\n const { kinds } = useKinds({ client: p.client });\n const [filtersOpen, setFiltersOpen] = useState(false);\n const filterTrigger = useRef<HTMLButtonElement | null>(null);\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 {list.filterSummaryUnknown && p.showFilteredSummary !== false && (\n <Button ref={filterTrigger} variant=\"outline\" className=\"justify-self-start\" onClick={() => setFiltersOpen(true)}>{t(\"releases.manageFilters\")}</Button>\n )}\n <ContentFilterDialog client={p.client} language={q.language} open={filtersOpen} onOpenChange={setFiltersOpen} returnFocus={() => filterTrigger.current} />\n <CardGrid\n surface=\"history\"\n empty={t(\"contents.historyEmpty\")}\n {...p}\n list={list}\n render={JSON.stringify(q)}\n filterLanguage={q.language}\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 chipOverflow={p.chipOverflow}\n chipFit={p.chipFit}\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 <HistoryProgress card={card} presentation={kinds?.kinds[card.kind]?.presentation} />\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\n/** When the reader last read or watched it, and how far: pages for a reader, minutes for a player. */\nfunction HistoryProgress({ card, presentation }: { card: Card; presentation?: string }) {\n const m = useMessages();\n const v = card.viewer;\n if (!v) return null;\n const done = v.completed ? v.progress_max : Math.min(v.progress, v.progress_max);\n const amount =\n v.progress_max > 0 && presentation === \"reader\"\n ? m.plural(\"contents.pageProgress\", v.progress_max, { progress: done })\n : v.progress_max > 0 && presentation === \"player\"\n ? m.plural(\"contents.minuteProgress\", Math.max(1, Math.round(v.progress_max / 60)), { progress: Math.round(done / 60) })\n : null;\n if (!v.last_read_at && !amount) return null;\n return (\n <p className=\"flex flex-wrap gap-x-2 text-xs tabular-nums text-muted-foreground\" data-ckui=\"history-progress\">\n {v.last_read_at && (\n <time dateTime={v.last_read_at} title={formatDate(m, v.last_read_at)}>\n {m.t(presentation === \"player\" ? \"contents.watchedAgo\" : \"contents.readAgo\", { time: relativeTime(m, v.last_read_at) })}\n </time>\n )}\n {amount && <span>{amount}</span>}\n </p>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { ContentCard, OwnerChip, PostCard, lockLabel, type CardMetadata, type ContentCardProps, type PostCardProps } from './card.js';
|
|
1
|
+
export { CardStatusBadges, ContentCard, OwnerChip, PostCard, cardStatus, lockLabel, type CardMetadata, type CardStatus, type ContentCardProps, type PostCardProps } from './card.js';
|
|
2
2
|
export { cardGridLayout, CardGrid, ContentGrid, ContentHistory, ContentRail, SearchResults, type CardGridLayout, type CardListProps, type ContentRailProps, type LockedCards } from './grid.js';
|
|
3
3
|
export { DailyReleases, type DailyReleasesProps } from './daily-releases.js';
|
|
4
4
|
export { ReleaseDatePicker, isReleaseDate, localReleaseDate, type ReleaseDatePickerProps } from './release-date-picker.js';
|
|
@@ -7,9 +7,12 @@ export { SearchBox, type SearchBoxProps } from './search.js';
|
|
|
7
7
|
export { ReaderSettings, type ReaderSettingsProps } from './reader-settings.js';
|
|
8
8
|
export { ContentFilterDialog, type ContentFilterDialogProps } from './filter-dialog.js';
|
|
9
9
|
export { ContentFilterSettings, ReleaseDateIndex, type ContentFilterSettingsProps, type ContentFilterCount, type ReleaseDateIndexProps } from './filters.js';
|
|
10
|
-
export { ContentListing, sortLabel, type ContentListingProps } from './listing.js';
|
|
11
|
-
export {
|
|
10
|
+
export { ContentListing, SortMenu, sortLabel, type ContentListingProps, type SortMenuProps } from './listing.js';
|
|
11
|
+
export { Pagination, pageWindow, type PaginationProps } from './pagination.js';
|
|
12
|
+
export { LanguageFallbackNotice, Paywall, VersionPicker, priceLabel, useVersionMenuItems, type LanguageFallbackNoticeProps, type PaywallProps, type VersionMenuOptions, type VersionPickerProps } from './paywall.js';
|
|
12
13
|
export { AccessEditor, ContentDetailsEditor, ContentSections, SegmentsEditor, VersionCover, VersionsEditor, versionLabel } from './editors.js';
|
|
13
14
|
export { OfferFields } from './offer-fields.js';
|
|
14
15
|
export { ReaderStats, type ReaderStatsProps } from './reader-stats.js';
|
|
15
16
|
export { VersionDownloadButton, type VersionDownloadButtonProps } from './download-button.js';
|
|
17
|
+
export { ContentFavorites } from './favorites.js';
|
|
18
|
+
export { PostList, excerpt, type PostListProps } from './post-list.js';
|
|
@@ -1,12 +1,23 @@
|
|
|
1
1
|
import { ContentQuery, ListSort } from '../../client/discovery.js';
|
|
2
2
|
import { Translator } from '../../i18n/messages.js';
|
|
3
3
|
import { CardListProps, LockedCards } from './grid.js';
|
|
4
|
-
/** A sort's name: "Newest", "Popular, this week". */
|
|
5
|
-
export declare function sortLabel(m: Translator, sort: ListSort): string;
|
|
4
|
+
/** A sort's name: "Newest", "Popular, this week", "Best match". */
|
|
5
|
+
export declare function sortLabel(m: Translator, sort: ListSort | "relevance"): string;
|
|
6
|
+
export interface SortMenuProps<S extends string> {
|
|
7
|
+
value: S;
|
|
8
|
+
/** The sorts offered, in menu order. */
|
|
9
|
+
sorts: readonly S[];
|
|
10
|
+
onChange: (sort: S) => void;
|
|
11
|
+
/** A sort's name; default the list sorts' names. */
|
|
12
|
+
label?: (sort: S) => string;
|
|
13
|
+
className?: string;
|
|
14
|
+
}
|
|
15
|
+
/** A list's sort control; hosts keep the choice (URL, remembered per page type). */
|
|
16
|
+
export declare function SortMenu<S extends string>({ value, sorts, onChange, label, className }: SortMenuProps<S>): import('react').JSX.Element;
|
|
6
17
|
export interface ContentListingProps extends CardListProps {
|
|
7
18
|
/** The listed kind: its version traits and filterable attributes become filters, its sort the default. */
|
|
8
19
|
kind?: string;
|
|
9
|
-
/** The sort and filters; with onQueryChange the host keeps them (e.g. in the URL). Other fields (groups, a tag page's) pass through. */
|
|
20
|
+
/** The sort and filters; with onQueryChange the host keeps them (e.g. in the URL). Other fields (groups, a tag page's) pass through. A changed query starts again at page 1: a host keeping the page drops it with the query. */
|
|
10
21
|
query?: ContentQuery;
|
|
11
22
|
onQueryChange?: (query: ContentQuery) => void;
|
|
12
23
|
/** The sorts offered; default newest, popular (week, month, all time), top rated, most favorited, title, oldest. */
|
|
@@ -26,26 +26,43 @@ function b(e, t) {
|
|
|
26
26
|
period: e.t(`listing.period_${r}`)
|
|
27
27
|
}) : i;
|
|
28
28
|
}
|
|
29
|
-
function x({
|
|
30
|
-
let
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
29
|
+
function x({ value: e, sorts: n, onChange: r, label: i, className: u }) {
|
|
30
|
+
let d = t(), f = i ?? ((e) => b(d, e));
|
|
31
|
+
return /* @__PURE__ */ g(a, {
|
|
32
|
+
value: e,
|
|
33
|
+
onValueChange: (e) => r(e),
|
|
34
|
+
children: [/* @__PURE__ */ h(c, {
|
|
35
|
+
size: "sm",
|
|
36
|
+
"aria-label": d.t("listing.sort"),
|
|
37
|
+
className: u,
|
|
38
|
+
"data-ckui": "listing-sort",
|
|
39
|
+
children: /* @__PURE__ */ h(l, { children: (e) => f(e) })
|
|
40
|
+
}), /* @__PURE__ */ h(o, { children: n.map((e) => /* @__PURE__ */ h(s, {
|
|
41
|
+
value: e,
|
|
42
|
+
children: f(e)
|
|
43
|
+
}, e)) })]
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
function S({ kind: b, query: S, onQueryChange: C, sorts: w = v, languages: T, filters: E, ...D }) {
|
|
47
|
+
let { t: O } = t(), k = e(), { kinds: A } = r({ client: D.client }), [j, M] = m({}), N = S ?? j, P = f((e) => {
|
|
48
|
+
S === void 0 && M(e), C?.(e);
|
|
49
|
+
}, [S, C]), F = b ? A?.kinds[b] : void 0, I = N.sort ?? F?.sort ?? w[0] ?? "newest", L = (e) => !E || E.includes(e), R = (F?.versions.traits ?? []).filter(L), z = Object.entries(F?.attributes ?? {}).filter(([e, t]) => t.filterable && (t.values?.length || t.type === "bool") && L(e)), B = (T ?? (A?.languages.length ?? 0) > 1) && !!A?.languages.length, V = (e) => N.attribute?.find((t) => t.startsWith(`${e}:`))?.slice(e.length + 1), H = (e, t) => P({
|
|
50
|
+
...N,
|
|
51
|
+
attribute: [...(N.attribute ?? []).filter((t) => !t.startsWith(`${e}:`)), ...t === void 0 ? [] : [`${e}:${t}`]]
|
|
52
|
+
}), U = (e) => {
|
|
53
|
+
let t = N.traits?.includes(e);
|
|
54
|
+
P({
|
|
55
|
+
...N,
|
|
56
|
+
traits: t ? N.traits.filter((t) => t !== e) : [...N.traits ?? [], e]
|
|
40
57
|
});
|
|
41
|
-
},
|
|
42
|
-
...
|
|
43
|
-
kind:
|
|
44
|
-
sort:
|
|
58
|
+
}, W = !!(N.language || N.traits?.length || N.attribute?.length), G = p(() => ({
|
|
59
|
+
...N,
|
|
60
|
+
kind: b ?? N.kind,
|
|
61
|
+
sort: I
|
|
45
62
|
}), [
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
63
|
+
N,
|
|
64
|
+
b,
|
|
65
|
+
I
|
|
49
66
|
]);
|
|
50
67
|
return /* @__PURE__ */ g(n, {
|
|
51
68
|
appearance: D.appearance,
|
|
@@ -53,55 +70,47 @@ function x({ kind: x, query: S, onQueryChange: C, sorts: w = v, languages: T, fi
|
|
|
53
70
|
"data-ckui": "content-listing",
|
|
54
71
|
children: [/* @__PURE__ */ g("div", {
|
|
55
72
|
role: "toolbar",
|
|
56
|
-
"aria-label":
|
|
73
|
+
"aria-label": O("listing.filters"),
|
|
57
74
|
className: "flex flex-wrap items-center gap-2",
|
|
58
75
|
children: [
|
|
59
|
-
/* @__PURE__ */
|
|
60
|
-
value:
|
|
61
|
-
|
|
62
|
-
|
|
76
|
+
/* @__PURE__ */ h(x, {
|
|
77
|
+
value: I,
|
|
78
|
+
sorts: w,
|
|
79
|
+
onChange: (e) => P({
|
|
80
|
+
...N,
|
|
63
81
|
sort: e
|
|
64
|
-
})
|
|
65
|
-
children: [/* @__PURE__ */ h(c, {
|
|
66
|
-
size: "sm",
|
|
67
|
-
"aria-label": k("listing.sort"),
|
|
68
|
-
"data-ckui": "listing-sort",
|
|
69
|
-
children: /* @__PURE__ */ h(l, { children: (e) => b(O, e) })
|
|
70
|
-
}), /* @__PURE__ */ h(o, { children: w.map((e) => /* @__PURE__ */ h(s, {
|
|
71
|
-
value: e,
|
|
72
|
-
children: b(O, e)
|
|
73
|
-
}, e)) })]
|
|
82
|
+
})
|
|
74
83
|
}),
|
|
75
|
-
|
|
76
|
-
value:
|
|
77
|
-
onValueChange: (e) =>
|
|
78
|
-
...
|
|
84
|
+
B && /* @__PURE__ */ g(a, {
|
|
85
|
+
value: N.language ?? y,
|
|
86
|
+
onValueChange: (e) => P({
|
|
87
|
+
...N,
|
|
79
88
|
language: e === y ? void 0 : e
|
|
80
89
|
}),
|
|
81
90
|
children: [/* @__PURE__ */ h(c, {
|
|
82
91
|
size: "sm",
|
|
83
|
-
"aria-label":
|
|
92
|
+
"aria-label": O("listing.language"),
|
|
84
93
|
"data-ckui": "listing-language",
|
|
85
|
-
children: /* @__PURE__ */ h(l, { children: (e) => e === y ?
|
|
94
|
+
children: /* @__PURE__ */ h(l, { children: (e) => e === y ? O("listing.anyLanguage") : k(e) })
|
|
86
95
|
}), /* @__PURE__ */ g(o, { children: [/* @__PURE__ */ h(s, {
|
|
87
96
|
value: y,
|
|
88
|
-
children:
|
|
89
|
-
}),
|
|
97
|
+
children: O("listing.anyLanguage")
|
|
98
|
+
}), A.languages.map((e) => /* @__PURE__ */ h(s, {
|
|
90
99
|
value: e,
|
|
91
|
-
children:
|
|
100
|
+
children: k(e)
|
|
92
101
|
}, e))] })]
|
|
93
102
|
}),
|
|
94
|
-
|
|
95
|
-
let n =
|
|
103
|
+
z.map(([e, t]) => {
|
|
104
|
+
let n = V(e);
|
|
96
105
|
return t.type === "bool" ? /* @__PURE__ */ h(i, {
|
|
97
106
|
size: "sm",
|
|
98
107
|
variant: n === "true" ? "default" : "outline",
|
|
99
108
|
"aria-pressed": n === "true",
|
|
100
|
-
onClick: () =>
|
|
109
|
+
onClick: () => H(e, n === "true" ? void 0 : "true"),
|
|
101
110
|
children: u(D.labels, e)
|
|
102
111
|
}, e) : /* @__PURE__ */ g(a, {
|
|
103
112
|
value: n ?? y,
|
|
104
|
-
onValueChange: (t) =>
|
|
113
|
+
onValueChange: (t) => H(e, t === y ? void 0 : t),
|
|
105
114
|
children: [/* @__PURE__ */ h(c, {
|
|
106
115
|
size: "sm",
|
|
107
116
|
"aria-label": u(D.labels, e),
|
|
@@ -110,41 +119,41 @@ function x({ kind: x, query: S, onQueryChange: C, sorts: w = v, languages: T, fi
|
|
|
110
119
|
children: /* @__PURE__ */ h(l, { children: (t) => t === y ? u(D.labels, e) : u(D.labels, t) })
|
|
111
120
|
}), /* @__PURE__ */ g(o, { children: [/* @__PURE__ */ h(s, {
|
|
112
121
|
value: y,
|
|
113
|
-
children:
|
|
122
|
+
children: O("listing.any")
|
|
114
123
|
}), t.values.map((e) => /* @__PURE__ */ h(s, {
|
|
115
124
|
value: e,
|
|
116
125
|
children: u(D.labels, e)
|
|
117
126
|
}, e))] })]
|
|
118
127
|
}, e);
|
|
119
128
|
}),
|
|
120
|
-
|
|
121
|
-
let t = !!
|
|
129
|
+
R.map((e) => {
|
|
130
|
+
let t = !!N.traits?.includes(e);
|
|
122
131
|
return /* @__PURE__ */ h(i, {
|
|
123
132
|
size: "sm",
|
|
124
133
|
variant: t ? "default" : "outline",
|
|
125
134
|
"aria-pressed": t,
|
|
126
|
-
onClick: () =>
|
|
135
|
+
onClick: () => U(e),
|
|
127
136
|
"data-ckui": "listing-trait",
|
|
128
137
|
children: u(D.labels, e)
|
|
129
138
|
}, e);
|
|
130
139
|
}),
|
|
131
|
-
|
|
140
|
+
W && /* @__PURE__ */ h(i, {
|
|
132
141
|
size: "sm",
|
|
133
142
|
variant: "ghost",
|
|
134
143
|
onClick: () => {
|
|
135
|
-
let { language: e, traits: t, attribute: n, ...r } =
|
|
136
|
-
|
|
144
|
+
let { language: e, traits: t, attribute: n, ...r } = N;
|
|
145
|
+
P(r);
|
|
137
146
|
},
|
|
138
|
-
children:
|
|
147
|
+
children: O("listing.clear")
|
|
139
148
|
})
|
|
140
149
|
]
|
|
141
150
|
}), /* @__PURE__ */ h(d, {
|
|
142
151
|
...D,
|
|
143
|
-
query:
|
|
152
|
+
query: G
|
|
144
153
|
})]
|
|
145
154
|
});
|
|
146
155
|
}
|
|
147
156
|
//#endregion
|
|
148
|
-
export {
|
|
157
|
+
export { S as ContentListing, x as SortMenu, b as sortLabel };
|
|
149
158
|
|
|
150
159
|
//# sourceMappingURL=listing.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"listing.js","names":[],"sources":["../../../src/components/content/listing.tsx"],"sourcesContent":["import { cn } from \"cn\";\nimport { useCallback, useMemo, useState } from \"react\";\nimport type { ContentQuery, ListSort } from \"../../client/discovery.js\";\nimport { useLanguageName, useMessages } from \"../../i18n/context.js\";\nimport type { MessageKey, Translator } from \"../../i18n/messages.js\";\nimport { useKinds } from \"../../react/groups.js\";\nimport { ContentKitUiRoot } from \"../../scope.js\";\nimport { Button } from \"#ckui/ui/button\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"#ckui/ui/select\";\nimport { labelOf } from \"../groups/parts.js\";\nimport { ContentGrid, type CardListProps, type LockedCards } from \"./grid.js\";\n\nconst SORTS: readonly ListSort[] = [\"newest\", \"popular:7d\", \"popular:30d\", \"popular\", \"top_rated\", \"most_favorited\", \"title\", \"oldest\"];\nconst ANY = \"*\";\n\n/** A sort's name: \"Newest\", \"Popular, this week\". */\nexport function sortLabel(m: Translator, sort: ListSort): string {\n const [base, period] = sort.split(\":\");\n const name = m.t(`listing.sort_${base}` as MessageKey);\n return period ? m.t(\"listing.sortPeriod\", { sort: name, period: m.t(`listing.period_${period}` as MessageKey) }) : name;\n}\n\nexport interface ContentListingProps extends CardListProps {\n /** The listed kind: its version traits and filterable attributes become filters, its sort the default. */\n kind?: string;\n /** The sort and filters; with onQueryChange the host keeps them (e.g. in the URL). Other fields (groups, a tag page's) pass through. */\n query?: ContentQuery;\n onQueryChange?: (query: ContentQuery) => void;\n /** The sorts offered; default newest, popular (week, month, all time), top rated, most favorited, title, oldest. */\n sorts?: readonly ListSort[];\n /** Cards the reader doesn't open: show (default), hide or collapse. */\n locked?: LockedCards;\n /** Offer a language filter; default when the site has several languages. */\n languages?: boolean;\n /** The traits and attributes offered; default every trait and filterable attribute of the kind. */\n filters?: readonly string[];\n}\n\n/** A listing page: sort, language, trait and attribute filters over a paged grid of a kind's contents. */\nexport function ContentListing({ kind, query: controlled, onQueryChange, sorts = SORTS, languages, filters, ...p }: ContentListingProps) {\n const m = useMessages();\n const { t } = m;\n const languageName = useLanguageName();\n const { kinds: model } = useKinds({ client: p.client });\n const [own, setOwn] = useState<ContentQuery>({});\n const query = controlled ?? own;\n const set = useCallback(\n (next: ContentQuery) => {\n if (controlled === undefined) setOwn(next);\n onQueryChange?.(next);\n },\n [controlled, onQueryChange],\n );\n const spec = kind ? model?.kinds[kind] : undefined;\n const sort = query.sort ?? (spec?.sort as ListSort | undefined) ?? sorts[0];\n const offered = (name: string) => !filters || filters.includes(name);\n const traits = (spec?.versions.traits ?? []).filter(offered);\n const attributes = Object.entries(spec?.attributes ?? {}).filter(([n, a]) => a.filterable && (a.values?.length || a.type === \"bool\") && offered(n));\n const languageFilter = (languages ?? (model?.languages.length ?? 0) > 1) && !!model?.languages.length;\n const attributeValue = (name: string) => query.attribute?.find((a) => a.startsWith(`${name}:`))?.slice(name.length + 1);\n const setAttribute = (name: string, value: string | undefined) =>\n set({ ...query, attribute: [...(query.attribute ?? []).filter((a) => !a.startsWith(`${name}:`)), ...(value === undefined ? [] : [`${name}:${value}`])] });\n const toggleTrait = (x: string) => {\n const on = query.traits?.includes(x);\n set({ ...query, traits: on ? query.traits!.filter((y) => y !== x) : [...(query.traits ?? []), x] });\n };\n const filtered = !!(query.language || query.traits?.length || query.attribute?.length);\n const list = useMemo(() => ({ ...query, kind: kind ?? query.kind, sort }), [query, kind, sort]);\n return (\n <ContentKitUiRoot appearance={p.appearance} className={cn(\"grid gap-4 text-sm\", p.className)} data-ckui=\"content-listing\">\n <div role=\"toolbar\" aria-label={t(\"listing.filters\")} className=\"flex flex-wrap items-center gap-2\">\n <Select value={sort} onValueChange={(v) => set({ ...query, sort: v as ListSort })}>\n <SelectTrigger size=\"sm\" aria-label={t(\"listing.sort\")} data-ckui=\"listing-sort\">\n <SelectValue>{(v: string) => sortLabel(m, v as ListSort)}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n {sorts.map((s) => (\n <SelectItem key={s} value={s}>\n {sortLabel(m, s)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n {languageFilter && (\n <Select value={query.language ?? ANY} onValueChange={(v) => set({ ...query, language: v === ANY ? undefined : (v as string) })}>\n <SelectTrigger size=\"sm\" aria-label={t(\"listing.language\")} data-ckui=\"listing-language\">\n <SelectValue>{(v: string) => (v === ANY ? t(\"listing.anyLanguage\") : languageName(v))}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={ANY}>{t(\"listing.anyLanguage\")}</SelectItem>\n {model!.languages.map((l) => (\n <SelectItem key={l} value={l}>\n {languageName(l)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n {attributes.map(([name, a]) => {\n const value = attributeValue(name);\n if (a.type === \"bool\")\n return (\n <Button key={name} size=\"sm\" variant={value === \"true\" ? \"default\" : \"outline\"} aria-pressed={value === \"true\"} onClick={() => setAttribute(name, value === \"true\" ? undefined : \"true\")}>\n {labelOf(p.labels, name)}\n </Button>\n );\n return (\n <Select key={name} value={value ?? ANY} onValueChange={(v) => setAttribute(name, v === ANY ? undefined : (v as string))}>\n <SelectTrigger size=\"sm\" aria-label={labelOf(p.labels, name)} data-ckui=\"listing-attribute\" data-name={name}>\n <SelectValue>{(v: string) => (v === ANY ? labelOf(p.labels, name) : labelOf(p.labels, v))}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={ANY}>{t(\"listing.any\")}</SelectItem>\n {a.values!.map((v) => (\n <SelectItem key={v} value={v}>\n {labelOf(p.labels, v)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n );\n })}\n {traits.map((x) => {\n const on = !!query.traits?.includes(x);\n return (\n <Button key={x} size=\"sm\" variant={on ? \"default\" : \"outline\"} aria-pressed={on} onClick={() => toggleTrait(x)} data-ckui=\"listing-trait\">\n {labelOf(p.labels, x)}\n </Button>\n );\n })}\n {filtered && (\n <Button\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => {\n const { language: _l, traits: _t, attribute: _a, ...rest } = query;\n set(rest);\n }}\n >\n {t(\"listing.clear\")}\n </Button>\n )}\n </div>\n <ContentGrid {...p} query={list} />\n </ContentKitUiRoot>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"listing.js","names":[],"sources":["../../../src/components/content/listing.tsx"],"sourcesContent":["import { cn } from \"cn\";\nimport { useCallback, useMemo, useState } from \"react\";\nimport type { ContentQuery, ListSort } from \"../../client/discovery.js\";\nimport { useLanguageName, useMessages } from \"../../i18n/context.js\";\nimport type { MessageKey, Translator } from \"../../i18n/messages.js\";\nimport { useKinds } from \"../../react/groups.js\";\nimport { ContentKitUiRoot } from \"../../scope.js\";\nimport { Button } from \"#ckui/ui/button\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"#ckui/ui/select\";\nimport { labelOf } from \"../groups/parts.js\";\nimport { ContentGrid, type CardListProps, type LockedCards } from \"./grid.js\";\n\nconst SORTS: readonly ListSort[] = [\"newest\", \"popular:7d\", \"popular:30d\", \"popular\", \"top_rated\", \"most_favorited\", \"title\", \"oldest\"];\nconst ANY = \"*\";\n\n/** A sort's name: \"Newest\", \"Popular, this week\", \"Best match\". */\nexport function sortLabel(m: Translator, sort: ListSort | \"relevance\"): string {\n const [base, period] = sort.split(\":\");\n const name = m.t(`listing.sort_${base}` as MessageKey);\n return period ? m.t(\"listing.sortPeriod\", { sort: name, period: m.t(`listing.period_${period}` as MessageKey) }) : name;\n}\n\nexport interface SortMenuProps<S extends string> {\n value: S;\n /** The sorts offered, in menu order. */\n sorts: readonly S[];\n onChange: (sort: S) => void;\n /** A sort's name; default the list sorts' names. */\n label?: (sort: S) => string;\n className?: string;\n}\n\n/** A list's sort control; hosts keep the choice (URL, remembered per page type). */\nexport function SortMenu<S extends string>({ value, sorts, onChange, label, className }: SortMenuProps<S>) {\n const m = useMessages();\n const name = label ?? ((s: S) => sortLabel(m, s as ListSort));\n return (\n <Select value={value} onValueChange={(v) => onChange(v as S)}>\n <SelectTrigger size=\"sm\" aria-label={m.t(\"listing.sort\")} className={className} data-ckui=\"listing-sort\">\n <SelectValue>{(v: string) => name(v as S)}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n {sorts.map((s) => (\n <SelectItem key={s} value={s}>\n {name(s)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n );\n}\n\nexport interface ContentListingProps extends CardListProps {\n /** The listed kind: its version traits and filterable attributes become filters, its sort the default. */\n kind?: string;\n /** The sort and filters; with onQueryChange the host keeps them (e.g. in the URL). Other fields (groups, a tag page's) pass through. A changed query starts again at page 1: a host keeping the page drops it with the query. */\n query?: ContentQuery;\n onQueryChange?: (query: ContentQuery) => void;\n /** The sorts offered; default newest, popular (week, month, all time), top rated, most favorited, title, oldest. */\n sorts?: readonly ListSort[];\n /** Cards the reader doesn't open: show (default), hide or collapse. */\n locked?: LockedCards;\n /** Offer a language filter; default when the site has several languages. */\n languages?: boolean;\n /** The traits and attributes offered; default every trait and filterable attribute of the kind. */\n filters?: readonly string[];\n}\n\n/** A listing page: sort, language, trait and attribute filters over a paged grid of a kind's contents. */\nexport function ContentListing({ kind, query: controlled, onQueryChange, sorts = SORTS, languages, filters, ...p }: ContentListingProps) {\n const m = useMessages();\n const { t } = m;\n const languageName = useLanguageName();\n const { kinds: model } = useKinds({ client: p.client });\n const [own, setOwn] = useState<ContentQuery>({});\n const query = controlled ?? own;\n const set = useCallback(\n (next: ContentQuery) => {\n if (controlled === undefined) setOwn(next);\n onQueryChange?.(next);\n },\n [controlled, onQueryChange],\n );\n const spec = kind ? model?.kinds[kind] : undefined;\n const sort: ListSort = query.sort ?? (spec?.sort as ListSort | undefined) ?? sorts[0] ?? \"newest\";\n const offered = (name: string) => !filters || filters.includes(name);\n const traits = (spec?.versions.traits ?? []).filter(offered);\n const attributes = Object.entries(spec?.attributes ?? {}).filter(([n, a]) => a.filterable && (a.values?.length || a.type === \"bool\") && offered(n));\n const languageFilter = (languages ?? (model?.languages.length ?? 0) > 1) && !!model?.languages.length;\n const attributeValue = (name: string) => query.attribute?.find((a) => a.startsWith(`${name}:`))?.slice(name.length + 1);\n const setAttribute = (name: string, value: string | undefined) =>\n set({ ...query, attribute: [...(query.attribute ?? []).filter((a) => !a.startsWith(`${name}:`)), ...(value === undefined ? [] : [`${name}:${value}`])] });\n const toggleTrait = (x: string) => {\n const on = query.traits?.includes(x);\n set({ ...query, traits: on ? query.traits!.filter((y) => y !== x) : [...(query.traits ?? []), x] });\n };\n const filtered = !!(query.language || query.traits?.length || query.attribute?.length);\n const list = useMemo(() => ({ ...query, kind: kind ?? query.kind, sort }), [query, kind, sort]);\n return (\n <ContentKitUiRoot appearance={p.appearance} className={cn(\"grid gap-4 text-sm\", p.className)} data-ckui=\"content-listing\">\n <div role=\"toolbar\" aria-label={t(\"listing.filters\")} className=\"flex flex-wrap items-center gap-2\">\n <SortMenu<ListSort> value={sort} sorts={sorts} onChange={(s) => set({ ...query, sort: s })} />\n {languageFilter && (\n <Select value={query.language ?? ANY} onValueChange={(v) => set({ ...query, language: v === ANY ? undefined : (v as string) })}>\n <SelectTrigger size=\"sm\" aria-label={t(\"listing.language\")} data-ckui=\"listing-language\">\n <SelectValue>{(v: string) => (v === ANY ? t(\"listing.anyLanguage\") : languageName(v))}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={ANY}>{t(\"listing.anyLanguage\")}</SelectItem>\n {model!.languages.map((l) => (\n <SelectItem key={l} value={l}>\n {languageName(l)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n {attributes.map(([name, a]) => {\n const value = attributeValue(name);\n if (a.type === \"bool\")\n return (\n <Button key={name} size=\"sm\" variant={value === \"true\" ? \"default\" : \"outline\"} aria-pressed={value === \"true\"} onClick={() => setAttribute(name, value === \"true\" ? undefined : \"true\")}>\n {labelOf(p.labels, name)}\n </Button>\n );\n return (\n <Select key={name} value={value ?? ANY} onValueChange={(v) => setAttribute(name, v === ANY ? undefined : (v as string))}>\n <SelectTrigger size=\"sm\" aria-label={labelOf(p.labels, name)} data-ckui=\"listing-attribute\" data-name={name}>\n <SelectValue>{(v: string) => (v === ANY ? labelOf(p.labels, name) : labelOf(p.labels, v))}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={ANY}>{t(\"listing.any\")}</SelectItem>\n {a.values!.map((v) => (\n <SelectItem key={v} value={v}>\n {labelOf(p.labels, v)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n );\n })}\n {traits.map((x) => {\n const on = !!query.traits?.includes(x);\n return (\n <Button key={x} size=\"sm\" variant={on ? \"default\" : \"outline\"} aria-pressed={on} onClick={() => toggleTrait(x)} data-ckui=\"listing-trait\">\n {labelOf(p.labels, x)}\n </Button>\n );\n })}\n {filtered && (\n <Button\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => {\n const { language: _l, traits: _t, attribute: _a, ...rest } = query;\n set(rest);\n }}\n >\n {t(\"listing.clear\")}\n </Button>\n )}\n </div>\n <ContentGrid {...p} query={list} />\n </ContentKitUiRoot>\n );\n}\n"],"mappings":"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"}
|
|
@@ -4,14 +4,14 @@ import { Badge as n } from "../../ui/badge.js";
|
|
|
4
4
|
import { Input as r } from "../../ui/input.js";
|
|
5
5
|
import { Label as i } from "../../ui/label.js";
|
|
6
6
|
import { Select as a, SelectContent as o, SelectItem as s, SelectTrigger as c, SelectValue as l } from "../../ui/select.js";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
7
|
+
import { priceLabel as u } from "./paywall.js";
|
|
8
|
+
import { useDraft as d } from "../../react/use-draft.js";
|
|
9
|
+
import { useEditorSaveTask as f } from "./editor-save.js";
|
|
10
10
|
import { useId as p, useMemo as m } from "react";
|
|
11
11
|
import { jsx as h, jsxs as g } from "react/jsx-runtime";
|
|
12
12
|
//#region src/components/content/offer-fields.tsx
|
|
13
13
|
function _({ offer: _, onSell: v, onWithdraw: y, pending: b }) {
|
|
14
|
-
let x = e(), { t: S } = x, C = p(), w = _ && (_.state ?? "active") === "active" ? _ : void 0, T =
|
|
14
|
+
let x = e(), { t: S } = x, C = p(), w = _ && (_.state ?? "active") === "active" ? _ : void 0, T = d(m(() => ({
|
|
15
15
|
amount: w?.terms.amount ?? "",
|
|
16
16
|
currency: w?.terms.currency ?? "USD",
|
|
17
17
|
mode: w?.terms.renew_hours ? "renew" : w?.terms.access_hours ? "rent" : "once",
|
|
@@ -33,7 +33,7 @@ function _({ offer: _, onSell: v, onWithdraw: y, pending: b }) {
|
|
|
33
33
|
days: k
|
|
34
34
|
}) : !1;
|
|
35
35
|
};
|
|
36
|
-
return
|
|
36
|
+
return f(`offer:${C}`, {
|
|
37
37
|
label: S("contentEditor.price"),
|
|
38
38
|
dirty: T.dirty.length > 0,
|
|
39
39
|
pending: b,
|
|
@@ -51,7 +51,7 @@ function _({ offer: _, onSell: v, onWithdraw: y, pending: b }) {
|
|
|
51
51
|
variant: "secondary",
|
|
52
52
|
children: S("contentEditor.onSale")
|
|
53
53
|
}),
|
|
54
|
-
/* @__PURE__ */ h("span", { children:
|
|
54
|
+
/* @__PURE__ */ h("span", { children: u(x, w.terms) }),
|
|
55
55
|
w.synced === !1 && /* @__PURE__ */ h("span", {
|
|
56
56
|
className: "text-xs text-muted-foreground",
|
|
57
57
|
children: S("contentEditor.syncing")
|