@openrails/contentkit-ui 0.83.0 → 0.86.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -7
- package/dist/client/beacons.d.ts +18 -6
- package/dist/client/beacons.js +26 -11
- package/dist/client/beacons.js.map +1 -1
- package/dist/client/client.d.ts +3 -0
- package/dist/client/client.js +29 -27
- package/dist/client/client.js.map +1 -1
- package/dist/client/codes.d.ts +6 -1
- package/dist/client/codes.js +4 -1
- package/dist/client/codes.js.map +1 -1
- package/dist/client/content/moderation.d.ts +14 -2
- package/dist/client/content/moderation.js +34 -0
- package/dist/client/content/moderation.js.map +1 -1
- package/dist/client/content/types.d.ts +13 -0
- package/dist/client/content/types.js.map +1 -1
- package/dist/client/discovery.d.ts +6 -0
- package/dist/client/discovery.js.map +1 -1
- package/dist/client/generated/error-codes.d.ts +1 -1
- package/dist/client/generated/error-codes.js +9 -1
- package/dist/client/generated/error-codes.js.map +1 -1
- package/dist/client/generated/routes.d.ts +91 -13
- package/dist/client/generated/routes.js +123 -2
- package/dist/client/generated/routes.js.map +1 -1
- package/dist/client/generated/wire.d.ts +81 -0
- package/dist/client/generated/wire.js.map +1 -1
- package/dist/client/index.d.ts +4 -2
- package/dist/client/media/queue.d.ts +4 -2
- package/dist/client/media/queue.js +18 -12
- package/dist/client/media/queue.js.map +1 -1
- package/dist/client/media/rules.d.ts +7 -1
- package/dist/client/media/rules.js +27 -21
- package/dist/client/media/rules.js.map +1 -1
- package/dist/client/reader-meta.d.ts +27 -0
- package/dist/client/reader-meta.js +99 -0
- package/dist/client/reader-meta.js.map +1 -0
- package/dist/client/stats.d.ts +32 -0
- package/dist/client/stats.js +35 -0
- package/dist/client/stats.js.map +1 -0
- package/dist/client/versions.d.ts +1 -1
- package/dist/client/versions.js +7 -2
- package/dist/client/versions.js.map +1 -1
- package/dist/client.js +30 -28
- package/dist/components/admin/content-manager.js +206 -206
- package/dist/components/admin/content-manager.js.map +1 -1
- package/dist/components/admin/group-manager.js +71 -70
- package/dist/components/admin/group-manager.js.map +1 -1
- package/dist/components/admin/media-item-panel.d.ts +24 -7
- package/dist/components/admin/media-item-panel.js +209 -197
- package/dist/components/admin/media-item-panel.js.map +1 -1
- package/dist/components/admin/poll-manager.js +25 -25
- package/dist/components/admin/reader-manifest.d.ts +18 -0
- package/dist/components/admin/reader-manifest.js +490 -0
- package/dist/components/admin/reader-manifest.js.map +1 -0
- package/dist/components/admin/stats-dashboard.d.ts +22 -0
- package/dist/components/admin/stats-dashboard.js +257 -0
- package/dist/components/admin/stats-dashboard.js.map +1 -0
- package/dist/components/content/card.js +43 -43
- package/dist/components/content/card.js.map +1 -1
- package/dist/components/content/editors.d.ts +2 -1
- package/dist/components/content/editors.js +479 -416
- package/dist/components/content/editors.js.map +1 -1
- package/dist/components/content/grid.js +47 -47
- package/dist/components/content/index.d.ts +2 -1
- package/dist/components/content/listing.js +34 -34
- package/dist/components/content/listing.js.map +1 -1
- package/dist/components/content/paywall.d.ts +17 -5
- package/dist/components/content/paywall.js +115 -100
- package/dist/components/content/paywall.js.map +1 -1
- package/dist/components/content/reader-stats.d.ts +12 -0
- package/dist/components/content/reader-stats.js +47 -0
- package/dist/components/content/reader-stats.js.map +1 -0
- package/dist/components/groups/banner.d.ts +1 -1
- package/dist/components/groups/banner.js +34 -34
- package/dist/components/groups/banner.js.map +1 -1
- package/dist/components/groups/directory.js +11 -11
- package/dist/components/groups/editor.js +294 -294
- package/dist/components/groups/editor.js.map +1 -1
- package/dist/components/groups/header.js +27 -27
- package/dist/components/groups/header.js.map +1 -1
- package/dist/components/groups/memberships.js +180 -180
- package/dist/components/groups/parts.d.ts +2 -4
- package/dist/components/groups/parts.js +69 -88
- package/dist/components/groups/parts.js.map +1 -1
- package/dist/components/media-folder-editor.js +239 -189
- package/dist/components/media-folder-editor.js.map +1 -1
- package/dist/components/notifications.js +17 -17
- package/dist/components/slot-upload.d.ts +2 -2
- package/dist/components/slot-upload.js +84 -79
- package/dist/components/slot-upload.js.map +1 -1
- package/dist/components/social/bans.js +34 -34
- package/dist/components/social/comments.js +59 -45
- package/dist/components/social/comments.js.map +1 -1
- package/dist/components/social/index.d.ts +1 -0
- package/dist/components/social/moderation.d.ts +5 -4
- package/dist/components/social/moderation.js +177 -160
- package/dist/components/social/moderation.js.map +1 -1
- package/dist/components/social/parts.js +22 -14
- package/dist/components/social/parts.js.map +1 -1
- package/dist/components/social/rules.d.ts +23 -0
- package/dist/components/social/rules.js +261 -0
- package/dist/components/social/rules.js.map +1 -0
- package/dist/components/video-player.d.ts +2 -0
- package/dist/components/video-player.js +318 -314
- package/dist/components/video-player.js.map +1 -1
- package/dist/i18n/context.d.ts +2 -0
- package/dist/i18n/context.js +23 -5
- package/dist/i18n/context.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +28 -24
- package/dist/locales/admin/de.js +105 -0
- package/dist/locales/admin/de.js.map +1 -1
- package/dist/locales/admin/en.d.ts +105 -0
- package/dist/locales/admin/en.js +105 -0
- package/dist/locales/admin/en.js.map +1 -1
- package/dist/locales/admin/es.js +105 -0
- package/dist/locales/admin/es.js.map +1 -1
- package/dist/locales/admin/ja.js +105 -0
- package/dist/locales/admin/ja.js.map +1 -1
- package/dist/locales/admin/ko.js +105 -0
- package/dist/locales/admin/ko.js.map +1 -1
- package/dist/locales/admin/zh.js +105 -0
- package/dist/locales/admin/zh.js.map +1 -1
- package/dist/locales/content/de.js +6 -0
- package/dist/locales/content/de.js.map +1 -1
- package/dist/locales/content/en.d.ts +6 -0
- package/dist/locales/content/en.js +6 -0
- package/dist/locales/content/en.js.map +1 -1
- package/dist/locales/content/es.js +6 -0
- package/dist/locales/content/es.js.map +1 -1
- package/dist/locales/content/ja.js +6 -0
- package/dist/locales/content/ja.js.map +1 -1
- package/dist/locales/content/ko.js +6 -0
- package/dist/locales/content/ko.js.map +1 -1
- package/dist/locales/content/zh.js +6 -0
- package/dist/locales/content/zh.js.map +1 -1
- package/dist/locales/de.js +4 -1
- package/dist/locales/de.js.map +1 -1
- package/dist/locales/discovery/de.js +1 -0
- package/dist/locales/discovery/de.js.map +1 -1
- package/dist/locales/discovery/en.d.ts +1 -0
- package/dist/locales/discovery/en.js +1 -0
- package/dist/locales/discovery/en.js.map +1 -1
- package/dist/locales/discovery/es.js +1 -0
- package/dist/locales/discovery/es.js.map +1 -1
- package/dist/locales/discovery/ja.js +1 -0
- package/dist/locales/discovery/ja.js.map +1 -1
- package/dist/locales/discovery/ko.js +1 -0
- package/dist/locales/discovery/ko.js.map +1 -1
- package/dist/locales/discovery/zh.js +1 -0
- package/dist/locales/discovery/zh.js.map +1 -1
- package/dist/locales/en.d.ts +118 -0
- package/dist/locales/en.js +4 -1
- package/dist/locales/en.js.map +1 -1
- package/dist/locales/es.js +4 -1
- package/dist/locales/es.js.map +1 -1
- package/dist/locales/ja.js +4 -1
- package/dist/locales/ja.js.map +1 -1
- package/dist/locales/ko.js +4 -1
- package/dist/locales/ko.js.map +1 -1
- package/dist/locales/social/de.js +3 -0
- package/dist/locales/social/de.js.map +1 -1
- package/dist/locales/social/en.d.ts +3 -0
- package/dist/locales/social/en.js +3 -0
- package/dist/locales/social/en.js.map +1 -1
- package/dist/locales/social/es.js +3 -0
- package/dist/locales/social/es.js.map +1 -1
- package/dist/locales/social/ja.js +3 -0
- package/dist/locales/social/ja.js.map +1 -1
- package/dist/locales/social/ko.js +3 -0
- package/dist/locales/social/ko.js.map +1 -1
- package/dist/locales/social/zh.js +3 -0
- package/dist/locales/social/zh.js.map +1 -1
- package/dist/locales/zh.js +4 -1
- package/dist/locales/zh.js.map +1 -1
- package/dist/react/admin.d.ts +1 -1
- package/dist/react/admin.js +24 -23
- package/dist/react/admin.js.map +1 -1
- package/dist/react/canonical.d.ts +4 -3
- package/dist/react/canonical.js +34 -33
- package/dist/react/canonical.js.map +1 -1
- package/dist/react/comments.d.ts +29 -1
- package/dist/react/comments.js +30 -2
- package/dist/react/comments.js.map +1 -1
- package/dist/react/contents.js +144 -144
- package/dist/react/contents.js.map +1 -1
- package/dist/react/context.d.ts +1 -1
- package/dist/react/context.js.map +1 -1
- package/dist/react/detail.d.ts +12 -6
- package/dist/react/detail.js +31 -24
- package/dist/react/detail.js.map +1 -1
- package/dist/react/discovery.js +1 -1
- package/dist/react/discovery.js.map +1 -1
- package/dist/react/folder.d.ts +7 -2
- package/dist/react/folder.js +165 -147
- package/dist/react/folder.js.map +1 -1
- package/dist/react/groups.d.ts +0 -1
- package/dist/react/groups.js +13 -12
- package/dist/react/groups.js.map +1 -1
- package/dist/react/index.d.ts +5 -3
- package/dist/react/location.d.ts +11 -0
- package/dist/react/location.js +18 -0
- package/dist/react/location.js.map +1 -0
- package/dist/react/public.d.ts +8 -0
- package/dist/react/public.js +32 -27
- package/dist/react/public.js.map +1 -1
- package/dist/react/resources.js +6 -0
- package/dist/react/resources.js.map +1 -1
- package/dist/react/stats.d.ts +24 -0
- package/dist/react/stats.js +28 -0
- package/dist/react/stats.js.map +1 -0
- package/dist/react/use-resource.js +8 -7
- package/dist/react/use-resource.js.map +1 -1
- package/dist/react/versions.js +20 -20
- package/dist/react.js +26 -24
- package/dist/styles.css +1 -1
- package/dist/urls/index.d.ts +35 -10
- package/dist/urls.js +100 -70
- package/dist/urls.js.map +1 -1
- package/package.json +1 -1
|
@@ -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 { 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, useLanguageName } from \"../groups/parts.js\";\nimport { ContentGrid, type CardListProps } 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 /** 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 } 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 /** 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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"}
|
|
@@ -40,14 +40,26 @@ export declare function Paywall({ decision, onCheckout, tiers, onSignIn, labels,
|
|
|
40
40
|
export interface VersionPickerProps {
|
|
41
41
|
/** The content's live versions (useContentDetail().versions). */
|
|
42
42
|
versions: readonly Version[];
|
|
43
|
-
/** The version shown. */
|
|
44
|
-
value
|
|
45
|
-
|
|
43
|
+
/** The version shown, by code or id; default the page's ?v=, else the reader's language's default. */
|
|
44
|
+
value?: string | null;
|
|
45
|
+
/** Receives the chosen version; default writes its code to the page's ?v=. */
|
|
46
|
+
onChange?: (version: Version) => void;
|
|
46
47
|
labels?: Labels;
|
|
47
48
|
className?: string;
|
|
48
49
|
appearance?: ContentKitUiAppearance;
|
|
49
50
|
}
|
|
50
|
-
/**
|
|
51
|
+
/**
|
|
52
|
+
* Chooses among a content's versions: a language first when they differ in
|
|
53
|
+
* language, then its variants (traits, labels). The choice is the page's ?v=
|
|
54
|
+
* unless value and onChange say otherwise.
|
|
55
|
+
*/
|
|
51
56
|
export declare function VersionPicker({ versions, value, onChange, labels, className, appearance }: VersionPickerProps): import('react').JSX.Element | null;
|
|
57
|
+
export interface VersionMenuOptions {
|
|
58
|
+
/** The version shown, by code or id; default the page's ?v=, else the reader's language's default. */
|
|
59
|
+
value?: string | null;
|
|
60
|
+
/** Receives the chosen version; default writes its code to the page's ?v=. */
|
|
61
|
+
onChange?: (version: Version) => void;
|
|
62
|
+
labels?: Labels;
|
|
63
|
+
}
|
|
52
64
|
/** VersionPicker as VideoPlayer settings entries (menuItems), for a watch page. */
|
|
53
|
-
export declare function useVersionMenuItems(versions: readonly Version[],
|
|
65
|
+
export declare function useVersionMenuItems(versions: readonly Version[], o?: VersionMenuOptions): PlayerMenuItem[];
|
|
@@ -1,18 +1,20 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { ContentKitUiRoot as
|
|
3
|
-
import { ContentKitContext as
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
1
|
+
import { useLanguageName as e, useMessages as t } from "../../i18n/context.js";
|
|
2
|
+
import { ContentKitUiRoot as n } from "../../scope.js";
|
|
3
|
+
import { ContentKitContext as r, useContentKitClient as i } from "../../react/context.js";
|
|
4
|
+
import { useKinds as a } from "../../react/groups.js";
|
|
5
|
+
import { Button as o } from "../../ui/button.js";
|
|
6
|
+
import { Select as s, SelectContent as c, SelectItem as l, SelectTrigger as u, SelectValue as d } from "../../ui/select.js";
|
|
7
|
+
import { ErrorLine as f, contentError as p, formatDate as m, useAction as h } from "../social/parts.js";
|
|
8
|
+
import { useVersionParam as g } from "../../react/location.js";
|
|
9
|
+
import { labelOf as _ } from "../groups/parts.js";
|
|
10
|
+
import { findVersion as v } from "../../react/detail.js";
|
|
11
|
+
import { useCallback as y, useContext as b, useMemo as x } from "react";
|
|
12
|
+
import { jsx as S, jsxs as C } from "react/jsx-runtime";
|
|
13
|
+
import { cn as w } from "cn";
|
|
14
|
+
import { LockIcon as T } from "@hugeicons/core-free-icons";
|
|
15
|
+
import { HugeiconsIcon as E } from "@hugeicons/react";
|
|
14
16
|
//#region src/components/content/paywall.tsx
|
|
15
|
-
function
|
|
17
|
+
function D(e, t) {
|
|
16
18
|
let n;
|
|
17
19
|
try {
|
|
18
20
|
n = new Intl.NumberFormat(e.language, {
|
|
@@ -31,47 +33,47 @@ function w(e, t) {
|
|
|
31
33
|
period: r(t.access_hours)
|
|
32
34
|
}) : n;
|
|
33
35
|
}
|
|
34
|
-
function
|
|
35
|
-
let O =
|
|
36
|
-
if (
|
|
37
|
-
let P =
|
|
38
|
-
return /* @__PURE__ */
|
|
39
|
-
appearance:
|
|
40
|
-
className:
|
|
36
|
+
function O({ decision: e, onCheckout: s, tiers: c, onSignIn: l, labels: u, children: d, client: g, onError: v, className: y, appearance: x }) {
|
|
37
|
+
let O = t(), { t: k } = O, A = b(r), j = i(g), { kinds: M } = a({ client: g }), N = h("membership.join", v);
|
|
38
|
+
if (e.allowed) return null;
|
|
39
|
+
let P = l ?? A?.onSignIn, F = e.offers ?? [], I = new Set(F.map((e) => e.key)), L = M?.tiers ?? Object.keys(c ?? {}), R = (e.sell ?? []).filter((e) => !I.has(e) && (L.includes(e) || !!c?.[e])), z = e.requires, B = z && !F.some((e) => e.key === z) ? z : void 0, V = (e) => !!z && e !== z, H = e.preview;
|
|
40
|
+
return /* @__PURE__ */ C(n, {
|
|
41
|
+
appearance: x,
|
|
42
|
+
className: w("grid gap-3 rounded-lg border border-border p-4 text-sm", y),
|
|
41
43
|
"data-ckui": "paywall",
|
|
42
44
|
children: [
|
|
43
|
-
/* @__PURE__ */
|
|
45
|
+
/* @__PURE__ */ C("p", {
|
|
44
46
|
className: "flex items-center gap-2 font-medium",
|
|
45
|
-
children: [/* @__PURE__ */
|
|
46
|
-
icon:
|
|
47
|
+
children: [/* @__PURE__ */ S(E, {
|
|
48
|
+
icon: T,
|
|
47
49
|
strokeWidth: 2,
|
|
48
50
|
className: "size-4",
|
|
49
51
|
"aria-hidden": !0
|
|
50
|
-
}),
|
|
52
|
+
}), e.unknown ? k("paywall.unknown") : k(z ? "paywall.requiresFirst" : "paywall.locked")]
|
|
51
53
|
}),
|
|
52
|
-
H?.pages ? /* @__PURE__ */
|
|
54
|
+
H?.pages ? /* @__PURE__ */ S("p", {
|
|
53
55
|
className: "text-muted-foreground",
|
|
54
56
|
children: O.plural("paywall.previewPages", H.pages)
|
|
55
57
|
}) : null,
|
|
56
|
-
H?.segments ? /* @__PURE__ */
|
|
58
|
+
H?.segments ? /* @__PURE__ */ S("p", {
|
|
57
59
|
className: "text-muted-foreground",
|
|
58
60
|
children: O.plural("paywall.previewSegments", H.segments)
|
|
59
61
|
}) : null,
|
|
60
|
-
|
|
62
|
+
e.free_after && /* @__PURE__ */ S("p", {
|
|
61
63
|
className: "text-muted-foreground",
|
|
62
|
-
children: k("paywall.freeFrom", { date:
|
|
64
|
+
children: k("paywall.freeFrom", { date: m(O, e.free_after, !1) })
|
|
63
65
|
}),
|
|
64
|
-
!
|
|
66
|
+
!e.unknown && /* @__PURE__ */ C("div", {
|
|
65
67
|
className: "flex flex-wrap gap-2",
|
|
66
68
|
"data-ckui": "paywall-offers",
|
|
67
69
|
children: [
|
|
68
|
-
B && /* @__PURE__ */
|
|
70
|
+
B && /* @__PURE__ */ S(o, {
|
|
69
71
|
size: "sm",
|
|
70
72
|
variant: "outline",
|
|
71
|
-
onClick: () =>
|
|
73
|
+
onClick: () => s({ key: B }),
|
|
72
74
|
children: k("paywall.joinFirst")
|
|
73
75
|
}),
|
|
74
|
-
F.map((e) => e.target.kind === "group" && Number(e.terms.amount) === 0 ? /* @__PURE__ */
|
|
76
|
+
F.map((e) => e.target.kind === "group" && Number(e.terms.amount) === 0 ? /* @__PURE__ */ S(o, {
|
|
75
77
|
size: "sm",
|
|
76
78
|
disabled: V(e.key) || N.pending,
|
|
77
79
|
onClick: () => A?.viewer === null && P ? P() : void N.run(() => j.offers.join(e.target.id)),
|
|
@@ -79,37 +81,37 @@ function T({ decision: a, onCheckout: o, tiers: s, onSignIn: c, labels: l, child
|
|
|
79
81
|
"data-target": "group",
|
|
80
82
|
"data-free": "",
|
|
81
83
|
children: k("paywall.joinFirst")
|
|
82
|
-
}, e.key) : /* @__PURE__ */
|
|
84
|
+
}, e.key) : /* @__PURE__ */ S(o, {
|
|
83
85
|
size: "sm",
|
|
84
86
|
disabled: V(e.key),
|
|
85
|
-
onClick: () =>
|
|
87
|
+
onClick: () => s({
|
|
86
88
|
key: e.key,
|
|
87
89
|
offer: e
|
|
88
90
|
}),
|
|
89
91
|
"data-ckui": "paywall-offer",
|
|
90
92
|
"data-target": e.target.kind,
|
|
91
|
-
children: k(e.target.kind === "group" ? "paywall.joinFor" : e.target.kind === "version" ? "paywall.unlockVersionFor" : "paywall.unlockFor", { price:
|
|
93
|
+
children: k(e.target.kind === "group" ? "paywall.joinFor" : e.target.kind === "version" ? "paywall.unlockVersionFor" : "paywall.unlockFor", { price: D(O, e.terms) })
|
|
92
94
|
}, e.key)),
|
|
93
|
-
R.map((e) => /* @__PURE__ */
|
|
95
|
+
R.map((e) => /* @__PURE__ */ C(o, {
|
|
94
96
|
size: "sm",
|
|
95
97
|
variant: F.length ? "outline" : "default",
|
|
96
|
-
onClick: () =>
|
|
98
|
+
onClick: () => s({ key: e }),
|
|
97
99
|
"data-ckui": "paywall-tier",
|
|
98
100
|
"data-tier": e,
|
|
99
|
-
children: [k("paywall.subscribe", { tier:
|
|
101
|
+
children: [k("paywall.subscribe", { tier: c?.[e]?.label ?? _(u, e) }), c?.[e]?.price && /* @__PURE__ */ S("span", {
|
|
100
102
|
className: "ms-1 opacity-80",
|
|
101
|
-
children:
|
|
103
|
+
children: c[e].price
|
|
102
104
|
})]
|
|
103
105
|
}, e))
|
|
104
106
|
]
|
|
105
107
|
}),
|
|
106
|
-
N.error && /* @__PURE__ */
|
|
107
|
-
A?.viewer === null && P && /* @__PURE__ */
|
|
108
|
+
N.error && /* @__PURE__ */ S(f, { children: p(O, N.error) }),
|
|
109
|
+
A?.viewer === null && P && /* @__PURE__ */ C("p", {
|
|
108
110
|
className: "text-muted-foreground",
|
|
109
111
|
children: [
|
|
110
112
|
k("paywall.haveIt"),
|
|
111
113
|
" ",
|
|
112
|
-
/* @__PURE__ */
|
|
114
|
+
/* @__PURE__ */ S("button", {
|
|
113
115
|
type: "button",
|
|
114
116
|
className: "font-medium text-foreground underline-offset-4 hover:underline",
|
|
115
117
|
onClick: P,
|
|
@@ -117,105 +119,118 @@ function T({ decision: a, onCheckout: o, tiers: s, onSignIn: c, labels: l, child
|
|
|
117
119
|
})
|
|
118
120
|
]
|
|
119
121
|
}),
|
|
120
|
-
|
|
122
|
+
d
|
|
121
123
|
]
|
|
122
124
|
});
|
|
123
125
|
}
|
|
124
|
-
function
|
|
125
|
-
return e.label || (e.traits.length ? e.traits.map((e) =>
|
|
126
|
+
function k(e, t, n) {
|
|
127
|
+
return e.label || (e.traits.length ? e.traits.map((e) => _(n, e)).join(" · ") : e.languages.map(t).join(", "));
|
|
126
128
|
}
|
|
127
|
-
var
|
|
128
|
-
function
|
|
129
|
-
let
|
|
130
|
-
return {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
129
|
+
var A = (e) => e.languages[0] ?? "";
|
|
130
|
+
function j(e, t, n) {
|
|
131
|
+
let [i, a] = g(), o = b(r)?.language, s = t === void 0 ? i : t, c = y((e) => n ? n(e) : a(e.code), [n, a]);
|
|
132
|
+
return x(() => {
|
|
133
|
+
let t = v(e, s) ?? (o ? e.find((e) => e.languages.includes(o)) : void 0) ?? e[0], n = t ? A(t) : "";
|
|
134
|
+
return {
|
|
135
|
+
current: t,
|
|
136
|
+
language: n,
|
|
137
|
+
languages: [...new Set(e.map(A))],
|
|
138
|
+
variants: e.filter((e) => A(e) === n),
|
|
139
|
+
change: c
|
|
140
|
+
};
|
|
141
|
+
}, [
|
|
142
|
+
e,
|
|
143
|
+
s,
|
|
144
|
+
o,
|
|
145
|
+
c
|
|
146
|
+
]);
|
|
136
147
|
}
|
|
137
|
-
function
|
|
138
|
-
let { t:
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
appearance: p,
|
|
143
|
-
className: x("flex flex-wrap items-center gap-2 text-sm", f),
|
|
148
|
+
function M({ versions: r, value: i, onChange: a, labels: f, className: p, appearance: m }) {
|
|
149
|
+
let { t: h } = t(), g = e(), { current: _, language: v, languages: y, variants: b, change: x } = j(r, i, a);
|
|
150
|
+
return r.length < 2 || !_ ? null : /* @__PURE__ */ C(n, {
|
|
151
|
+
appearance: m,
|
|
152
|
+
className: w("flex flex-wrap items-center gap-2 text-sm", p),
|
|
144
153
|
"data-ckui": "version-picker",
|
|
145
|
-
children: [
|
|
154
|
+
children: [y.length > 1 && /* @__PURE__ */ C(s, {
|
|
146
155
|
value: v,
|
|
147
156
|
onValueChange: (e) => {
|
|
148
|
-
let t =
|
|
149
|
-
t &&
|
|
157
|
+
let t = r.find((t) => A(t) === e);
|
|
158
|
+
t && x(t);
|
|
150
159
|
},
|
|
151
|
-
children: [/* @__PURE__ */
|
|
160
|
+
children: [/* @__PURE__ */ S(u, {
|
|
152
161
|
size: "sm",
|
|
153
|
-
"aria-label":
|
|
154
|
-
children: /* @__PURE__ */
|
|
155
|
-
}), /* @__PURE__ */
|
|
162
|
+
"aria-label": h("versions.language"),
|
|
163
|
+
children: /* @__PURE__ */ S(d, { children: (e) => g(e) })
|
|
164
|
+
}), /* @__PURE__ */ S(c, { children: y.map((e) => /* @__PURE__ */ S(l, {
|
|
156
165
|
value: e,
|
|
157
|
-
children:
|
|
166
|
+
children: g(e)
|
|
158
167
|
}, e)) })]
|
|
159
|
-
}),
|
|
168
|
+
}), b.length > 1 && /* @__PURE__ */ S("div", {
|
|
160
169
|
role: "radiogroup",
|
|
161
|
-
"aria-label":
|
|
170
|
+
"aria-label": h("versions.version"),
|
|
162
171
|
className: "flex flex-wrap gap-1",
|
|
163
|
-
children:
|
|
172
|
+
children: b.map((e) => /* @__PURE__ */ C(o, {
|
|
164
173
|
size: "sm",
|
|
165
174
|
role: "radio",
|
|
166
175
|
"aria-checked": e.id === _.id,
|
|
167
176
|
variant: e.id === _.id ? "default" : "outline",
|
|
168
|
-
onClick: () =>
|
|
177
|
+
onClick: () => x(e),
|
|
169
178
|
"data-ckui": "version-option",
|
|
170
179
|
"data-locked": e.decision.allowed ? void 0 : "",
|
|
171
|
-
children: [!e.decision.allowed && /* @__PURE__ */
|
|
172
|
-
icon:
|
|
180
|
+
children: [!e.decision.allowed && /* @__PURE__ */ S(E, {
|
|
181
|
+
icon: T,
|
|
173
182
|
strokeWidth: 2,
|
|
174
183
|
className: "size-3.5",
|
|
175
|
-
"aria-label":
|
|
176
|
-
}),
|
|
184
|
+
"aria-label": h("versions.locked")
|
|
185
|
+
}), k(e, g, f)]
|
|
177
186
|
}, e.id))
|
|
178
187
|
})]
|
|
179
188
|
});
|
|
180
189
|
}
|
|
181
|
-
function
|
|
182
|
-
let { t:
|
|
183
|
-
return
|
|
184
|
-
if (
|
|
185
|
-
let
|
|
186
|
-
return c.length > 1 &&
|
|
187
|
-
label:
|
|
190
|
+
function N(n, r = {}) {
|
|
191
|
+
let { t: i } = t(), a = e(), { current: o, language: s, languages: c, variants: l, change: u } = j(n, r.value, r.onChange), { labels: d } = r;
|
|
192
|
+
return x(() => {
|
|
193
|
+
if (n.length < 2 || !o) return [];
|
|
194
|
+
let e = [];
|
|
195
|
+
return c.length > 1 && e.push({
|
|
196
|
+
label: i("versions.language"),
|
|
188
197
|
value: s,
|
|
189
198
|
options: c.map((e) => ({
|
|
190
199
|
value: e,
|
|
191
|
-
label:
|
|
200
|
+
label: a(e)
|
|
192
201
|
})),
|
|
193
202
|
onChange: (e) => {
|
|
194
|
-
let
|
|
195
|
-
|
|
203
|
+
let t = n.find((t) => A(t) === e);
|
|
204
|
+
t && u(t);
|
|
196
205
|
}
|
|
197
|
-
}), l.length > 1 &&
|
|
198
|
-
label:
|
|
199
|
-
value:
|
|
206
|
+
}), l.length > 1 && e.push({
|
|
207
|
+
label: i("versions.version"),
|
|
208
|
+
value: o.id,
|
|
200
209
|
options: l.map((e) => {
|
|
201
|
-
let t =
|
|
210
|
+
let t = k(e, a, d);
|
|
202
211
|
return {
|
|
203
212
|
value: e.id,
|
|
204
|
-
label: e.decision.allowed ? t :
|
|
213
|
+
label: e.decision.allowed ? t : i("versions.lockedOption", { name: t })
|
|
205
214
|
};
|
|
206
215
|
}),
|
|
207
|
-
onChange:
|
|
208
|
-
|
|
216
|
+
onChange: (e) => {
|
|
217
|
+
let t = n.find((t) => t.id === e);
|
|
218
|
+
t && u(t);
|
|
219
|
+
}
|
|
220
|
+
}), e;
|
|
209
221
|
}, [
|
|
210
|
-
t,
|
|
211
222
|
n,
|
|
212
|
-
|
|
223
|
+
o,
|
|
224
|
+
s,
|
|
225
|
+
c,
|
|
226
|
+
l,
|
|
227
|
+
u,
|
|
228
|
+
d,
|
|
213
229
|
i,
|
|
214
|
-
a
|
|
215
|
-
o
|
|
230
|
+
a
|
|
216
231
|
]);
|
|
217
232
|
}
|
|
218
233
|
//#endregion
|
|
219
|
-
export {
|
|
234
|
+
export { O as Paywall, M as VersionPicker, D as priceLabel, N as useVersionMenuItems };
|
|
220
235
|
|
|
221
236
|
//# sourceMappingURL=paywall.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paywall.js","names":[],"sources":["../../../src/components/content/paywall.tsx"],"sourcesContent":["import { LockIcon } from \"@hugeicons/core-free-icons\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport { cn } from \"cn\";\nimport { useContext, useMemo, type ReactNode } from \"react\";\nimport type { ContentKitUiAppearance } from \"../../appearance.js\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport type { Decision, Sale, Terms, Version } from \"../../client/generated/wire.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport type { Translator } from \"../../i18n/messages.js\";\nimport { ContentKitContext, useContentKitClient, type ContentKitErrorHandler } from \"../../react/context.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 { contentError, ErrorLine, formatDate, useAction } from \"../social/parts.js\";\nimport { labelOf, useLanguageName, type Labels } from \"../groups/parts.js\";\nimport type { PlayerMenuItem } from \"../video-player.js\";\n\n/** An offer's price and period in words: \"$2.99\", \"$4.99 / 30 days\", \"$1.99 for 48 hours\". */\nexport function priceLabel(m: Translator, terms: Terms): string {\n let price: string;\n try {\n price = new Intl.NumberFormat(m.language, { style: \"currency\", currency: terms.currency }).format(Number(terms.amount));\n } catch {\n price = `${terms.amount} ${terms.currency}`;\n }\n const period = (hours: number) => (hours % 24 === 0 ? m.plural(\"paywall.days\", hours / 24) : m.plural(\"paywall.hours\", hours));\n if (terms.renew_hours) return m.t(\"paywall.renews\", { price, period: period(terms.renew_hours) });\n if (terms.access_hours) return m.t(\"paywall.rents\", { price, period: period(terms.access_hours) });\n return price;\n}\n\nexport interface PaywallProps {\n /** The locked version's (or card's) decision. */\n decision: Decision;\n /**\n * Starts a purchase: the host creates the order through billing-ui\n * (createOrder with offer.price_id) and shows OrderCheckout; for a tier\n * (no offer), its subscription page. A free group offer joins here instead.\n */\n onCheckout: (o: { key: string; offer?: Sale }) => void;\n /** Tiers' names and prices from the host's catalog, by tier key. */\n tiers?: Readonly<Record<string, { label?: string; price?: string }>>;\n /** Asks a signed-out reader to sign in (they may hold it already); default the provider's. */\n onSignIn?: () => void;\n labels?: Labels;\n /** Below the offers: the host's own. */\n children?: ReactNode;\n client?: ContentKitClient;\n onError?: ContentKitErrorHandler;\n className?: string;\n appearance?: ContentKitUiAppearance;\n}\n\n/** Why content is locked and how to open it: its offers, the tiers that include it, previews and when it turns free. */\nexport function Paywall({ decision, onCheckout, tiers, onSignIn, labels, children, client: own, onError, className, appearance }: PaywallProps) {\n const m = useMessages();\n const { t } = m;\n const ctx = useContext(ContentKitContext);\n const client = useContentKitClient(own);\n const { kinds } = useKinds({ client: own });\n const join = useAction(\"membership.join\", onError);\n if (decision.allowed) return null;\n const signIn = onSignIn ?? ctx?.onSignIn;\n const offers = decision.offers ?? [];\n const sold = new Set(offers.map((o) => o.key));\n const siteTiers = kinds?.tiers ?? Object.keys(tiers ?? {});\n const tierKeys = (decision.sell ?? []).filter((k) => !sold.has(k) && (siteTiers.includes(k) || !!tiers?.[k]));\n const required = decision.requires;\n // A required key nothing here offers gets its own button; the others wait for it.\n const requires = required && !offers.some((o) => o.key === required) ? required : undefined;\n const waits = (key: string) => !!required && key !== required;\n const preview = decision.preview;\n return (\n <ContentKitUiRoot appearance={appearance} className={cn(\"grid gap-3 rounded-lg border border-border p-4 text-sm\", className)} data-ckui=\"paywall\">\n <p className=\"flex items-center gap-2 font-medium\">\n <HugeiconsIcon icon={LockIcon} strokeWidth={2} className=\"size-4\" aria-hidden />\n {decision.unknown ? t(\"paywall.unknown\") : required ? t(\"paywall.requiresFirst\") : t(\"paywall.locked\")}\n </p>\n {preview?.pages ? <p className=\"text-muted-foreground\">{m.plural(\"paywall.previewPages\", preview.pages)}</p> : null}\n {preview?.segments ? <p className=\"text-muted-foreground\">{m.plural(\"paywall.previewSegments\", preview.segments)}</p> : null}\n {decision.free_after && <p className=\"text-muted-foreground\">{t(\"paywall.freeFrom\", { date: formatDate(m, decision.free_after, false) })}</p>}\n {!decision.unknown && (\n <div className=\"flex flex-wrap gap-2\" data-ckui=\"paywall-offers\">\n {requires && (\n <Button size=\"sm\" variant=\"outline\" onClick={() => onCheckout({ key: requires })}>\n {t(\"paywall.joinFirst\")}\n </Button>\n )}\n {offers.map((o) =>\n o.target.kind === \"group\" && Number(o.terms.amount) === 0 ? (\n <Button\n key={o.key}\n size=\"sm\"\n disabled={waits(o.key) || join.pending}\n onClick={() => (ctx?.viewer === null && signIn ? signIn() : void join.run(() => client.offers.join(o.target.id)))}\n data-ckui=\"paywall-offer\"\n data-target=\"group\"\n data-free=\"\"\n >\n {t(\"paywall.joinFirst\")}\n </Button>\n ) : (\n <Button key={o.key} size=\"sm\" disabled={waits(o.key)} onClick={() => onCheckout({ key: o.key, offer: o })} data-ckui=\"paywall-offer\" data-target={o.target.kind}>\n {t(o.target.kind === \"group\" ? \"paywall.joinFor\" : o.target.kind === \"version\" ? \"paywall.unlockVersionFor\" : \"paywall.unlockFor\", { price: priceLabel(m, o.terms) })}\n </Button>\n ),\n )}\n {tierKeys.map((k) => (\n <Button key={k} size=\"sm\" variant={offers.length ? \"outline\" : \"default\"} onClick={() => onCheckout({ key: k })} data-ckui=\"paywall-tier\" data-tier={k}>\n {t(\"paywall.subscribe\", { tier: tiers?.[k]?.label ?? labelOf(labels, k) })}\n {tiers?.[k]?.price && <span className=\"ms-1 opacity-80\">{tiers[k]!.price}</span>}\n </Button>\n ))}\n </div>\n )}\n {join.error && <ErrorLine>{contentError(m, join.error)}</ErrorLine>}\n {ctx?.viewer === null && signIn && (\n <p className=\"text-muted-foreground\">\n {t(\"paywall.haveIt\")}{\" \"}\n <button type=\"button\" className=\"font-medium text-foreground underline-offset-4 hover:underline\" onClick={signIn}>\n {t(\"paywall.signIn\")}\n </button>\n </p>\n )}\n {children}\n </ContentKitUiRoot>\n );\n}\n\nexport interface VersionPickerProps {\n /** The content's live versions (useContentDetail().versions). */\n versions: readonly Version[];\n /** The version shown. */\n value: string | null | undefined;\n onChange: (version: string) => void;\n labels?: Labels;\n className?: string;\n appearance?: ContentKitUiAppearance;\n}\n\n/** A version's name: its label, else its traits, else its languages. */\nfunction versionName(v: Version, languageName: (l: string) => string, labels?: Labels): string {\n return v.label || (v.traits.length ? v.traits.map((x) => labelOf(labels, x)).join(\" · \") : v.languages.map(languageName).join(\", \"));\n}\n\nconst primary = (v: Version) => v.languages[0] ?? \"\";\n\n/** The shown version, the versions' languages and the shown language's variants. */\nfunction choices(versions: readonly Version[], value: string | null | undefined) {\n const current = versions.find((v) => v.id === value) ?? versions[0]!;\n const language = primary(current);\n return { current, language, languages: [...new Set(versions.map(primary))], variants: versions.filter((v) => primary(v) === language) };\n}\n\n/** Chooses among a content's versions: a language first when they differ in language, then its variants (traits, labels). */\nexport function VersionPicker({ versions, value, onChange, labels, className, appearance }: VersionPickerProps) {\n const { t } = useMessages();\n const languageName = useLanguageName();\n if (versions.length < 2) return null;\n const { current, language, languages, variants } = choices(versions, value);\n return (\n <ContentKitUiRoot appearance={appearance} className={cn(\"flex flex-wrap items-center gap-2 text-sm\", className)} data-ckui=\"version-picker\">\n {languages.length > 1 && (\n <Select\n value={language}\n onValueChange={(l) => {\n const v = versions.find((x) => primary(x) === l);\n if (v) onChange(v.id);\n }}\n >\n <SelectTrigger size=\"sm\" aria-label={t(\"versions.language\")}>\n <SelectValue>{(l: string) => languageName(l)}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n {languages.map((l) => (\n <SelectItem key={l} value={l}>\n {languageName(l)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n {variants.length > 1 && (\n <div role=\"radiogroup\" aria-label={t(\"versions.version\")} className=\"flex flex-wrap gap-1\">\n {variants.map((v) => (\n <Button\n key={v.id}\n size=\"sm\"\n role=\"radio\"\n aria-checked={v.id === current.id}\n variant={v.id === current.id ? \"default\" : \"outline\"}\n onClick={() => onChange(v.id)}\n data-ckui=\"version-option\"\n data-locked={v.decision.allowed ? undefined : \"\"}\n >\n {!v.decision.allowed && <HugeiconsIcon icon={LockIcon} strokeWidth={2} className=\"size-3.5\" aria-label={t(\"versions.locked\")} />}\n {versionName(v, languageName, labels)}\n </Button>\n ))}\n </div>\n )}\n </ContentKitUiRoot>\n );\n}\n\n/** VersionPicker as VideoPlayer settings entries (menuItems), for a watch page. */\nexport function useVersionMenuItems(\n versions: readonly Version[],\n value: string | null | undefined,\n onChange: (version: string) => void,\n labels?: Labels,\n): PlayerMenuItem[] {\n const { t } = useMessages();\n const languageName = useLanguageName();\n return useMemo(() => {\n if (versions.length < 2) return [];\n const { current, language, languages, variants } = choices(versions, value);\n const out: PlayerMenuItem[] = [];\n if (languages.length > 1)\n out.push({\n label: t(\"versions.language\"),\n value: language,\n options: languages.map((l) => ({ value: l, label: languageName(l) })),\n onChange: (l) => {\n const v = versions.find((x) => primary(x) === l);\n if (v) onChange(v.id);\n },\n });\n if (variants.length > 1)\n out.push({\n label: t(\"versions.version\"),\n value: current.id,\n options: variants.map((v) => {\n const name = versionName(v, languageName, labels);\n return { value: v.id, label: v.decision.allowed ? name : t(\"versions.lockedOption\", { name }) };\n }),\n onChange,\n });\n return out;\n }, [versions, value, onChange, labels, t, languageName]);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"paywall.js","names":[],"sources":["../../../src/components/content/paywall.tsx"],"sourcesContent":["import { LockIcon } from \"@hugeicons/core-free-icons\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport { cn } from \"cn\";\nimport { useCallback, useContext, useMemo, type ReactNode } from \"react\";\nimport type { ContentKitUiAppearance } from \"../../appearance.js\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport type { Decision, Sale, Terms, Version } from \"../../client/generated/wire.js\";\nimport { useLanguageName, useMessages } from \"../../i18n/context.js\";\nimport type { Translator } from \"../../i18n/messages.js\";\nimport { ContentKitContext, useContentKitClient, type ContentKitErrorHandler } from \"../../react/context.js\";\nimport { findVersion } from \"../../react/detail.js\";\nimport { useVersionParam } from \"../../react/location.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 { contentError, ErrorLine, formatDate, useAction } from \"../social/parts.js\";\nimport { labelOf, type Labels } from \"../groups/parts.js\";\nimport type { PlayerMenuItem } from \"../video-player.js\";\n\n/** An offer's price and period in words: \"$2.99\", \"$4.99 / 30 days\", \"$1.99 for 48 hours\". */\nexport function priceLabel(m: Translator, terms: Terms): string {\n let price: string;\n try {\n price = new Intl.NumberFormat(m.language, { style: \"currency\", currency: terms.currency }).format(Number(terms.amount));\n } catch {\n price = `${terms.amount} ${terms.currency}`;\n }\n const period = (hours: number) => (hours % 24 === 0 ? m.plural(\"paywall.days\", hours / 24) : m.plural(\"paywall.hours\", hours));\n if (terms.renew_hours) return m.t(\"paywall.renews\", { price, period: period(terms.renew_hours) });\n if (terms.access_hours) return m.t(\"paywall.rents\", { price, period: period(terms.access_hours) });\n return price;\n}\n\nexport interface PaywallProps {\n /** The locked version's (or card's) decision. */\n decision: Decision;\n /**\n * Starts a purchase: the host creates the order through billing-ui\n * (createOrder with offer.price_id) and shows OrderCheckout; for a tier\n * (no offer), its subscription page. A free group offer joins here instead.\n */\n onCheckout: (o: { key: string; offer?: Sale }) => void;\n /** Tiers' names and prices from the host's catalog, by tier key. */\n tiers?: Readonly<Record<string, { label?: string; price?: string }>>;\n /** Asks a signed-out reader to sign in (they may hold it already); default the provider's. */\n onSignIn?: () => void;\n labels?: Labels;\n /** Below the offers: the host's own. */\n children?: ReactNode;\n client?: ContentKitClient;\n onError?: ContentKitErrorHandler;\n className?: string;\n appearance?: ContentKitUiAppearance;\n}\n\n/** Why content is locked and how to open it: its offers, the tiers that include it, previews and when it turns free. */\nexport function Paywall({ decision, onCheckout, tiers, onSignIn, labels, children, client: own, onError, className, appearance }: PaywallProps) {\n const m = useMessages();\n const { t } = m;\n const ctx = useContext(ContentKitContext);\n const client = useContentKitClient(own);\n const { kinds } = useKinds({ client: own });\n const join = useAction(\"membership.join\", onError);\n if (decision.allowed) return null;\n const signIn = onSignIn ?? ctx?.onSignIn;\n const offers = decision.offers ?? [];\n const sold = new Set(offers.map((o) => o.key));\n const siteTiers = kinds?.tiers ?? Object.keys(tiers ?? {});\n const tierKeys = (decision.sell ?? []).filter((k) => !sold.has(k) && (siteTiers.includes(k) || !!tiers?.[k]));\n const required = decision.requires;\n // A required key nothing here offers gets its own button; the others wait for it.\n const requires = required && !offers.some((o) => o.key === required) ? required : undefined;\n const waits = (key: string) => !!required && key !== required;\n const preview = decision.preview;\n return (\n <ContentKitUiRoot appearance={appearance} className={cn(\"grid gap-3 rounded-lg border border-border p-4 text-sm\", className)} data-ckui=\"paywall\">\n <p className=\"flex items-center gap-2 font-medium\">\n <HugeiconsIcon icon={LockIcon} strokeWidth={2} className=\"size-4\" aria-hidden />\n {decision.unknown ? t(\"paywall.unknown\") : required ? t(\"paywall.requiresFirst\") : t(\"paywall.locked\")}\n </p>\n {preview?.pages ? <p className=\"text-muted-foreground\">{m.plural(\"paywall.previewPages\", preview.pages)}</p> : null}\n {preview?.segments ? <p className=\"text-muted-foreground\">{m.plural(\"paywall.previewSegments\", preview.segments)}</p> : null}\n {decision.free_after && <p className=\"text-muted-foreground\">{t(\"paywall.freeFrom\", { date: formatDate(m, decision.free_after, false) })}</p>}\n {!decision.unknown && (\n <div className=\"flex flex-wrap gap-2\" data-ckui=\"paywall-offers\">\n {requires && (\n <Button size=\"sm\" variant=\"outline\" onClick={() => onCheckout({ key: requires })}>\n {t(\"paywall.joinFirst\")}\n </Button>\n )}\n {offers.map((o) =>\n o.target.kind === \"group\" && Number(o.terms.amount) === 0 ? (\n <Button\n key={o.key}\n size=\"sm\"\n disabled={waits(o.key) || join.pending}\n onClick={() => (ctx?.viewer === null && signIn ? signIn() : void join.run(() => client.offers.join(o.target.id)))}\n data-ckui=\"paywall-offer\"\n data-target=\"group\"\n data-free=\"\"\n >\n {t(\"paywall.joinFirst\")}\n </Button>\n ) : (\n <Button key={o.key} size=\"sm\" disabled={waits(o.key)} onClick={() => onCheckout({ key: o.key, offer: o })} data-ckui=\"paywall-offer\" data-target={o.target.kind}>\n {t(o.target.kind === \"group\" ? \"paywall.joinFor\" : o.target.kind === \"version\" ? \"paywall.unlockVersionFor\" : \"paywall.unlockFor\", { price: priceLabel(m, o.terms) })}\n </Button>\n ),\n )}\n {tierKeys.map((k) => (\n <Button key={k} size=\"sm\" variant={offers.length ? \"outline\" : \"default\"} onClick={() => onCheckout({ key: k })} data-ckui=\"paywall-tier\" data-tier={k}>\n {t(\"paywall.subscribe\", { tier: tiers?.[k]?.label ?? labelOf(labels, k) })}\n {tiers?.[k]?.price && <span className=\"ms-1 opacity-80\">{tiers[k]!.price}</span>}\n </Button>\n ))}\n </div>\n )}\n {join.error && <ErrorLine>{contentError(m, join.error)}</ErrorLine>}\n {ctx?.viewer === null && signIn && (\n <p className=\"text-muted-foreground\">\n {t(\"paywall.haveIt\")}{\" \"}\n <button type=\"button\" className=\"font-medium text-foreground underline-offset-4 hover:underline\" onClick={signIn}>\n {t(\"paywall.signIn\")}\n </button>\n </p>\n )}\n {children}\n </ContentKitUiRoot>\n );\n}\n\nexport interface VersionPickerProps {\n /** The content's live versions (useContentDetail().versions). */\n versions: readonly Version[];\n /** The version shown, by code or id; default the page's ?v=, else the reader's language's default. */\n value?: string | null;\n /** Receives the chosen version; default writes its code to the page's ?v=. */\n onChange?: (version: Version) => void;\n labels?: Labels;\n className?: string;\n appearance?: ContentKitUiAppearance;\n}\n\n/** A version's name: its label, else its traits, else its languages. */\nfunction versionName(v: Version, languageName: (l: string) => string, labels?: Labels): string {\n return v.label || (v.traits.length ? v.traits.map((x) => labelOf(labels, x)).join(\" · \") : v.languages.map(languageName).join(\", \"));\n}\n\nconst primary = (v: Version) => v.languages[0] ?? \"\";\n\n/**\n * The shown version (value, else the page's ?v=, else the reader's\n * language's default), the versions' languages, the shown language's\n * variants, and the change handler (default: write ?v=).\n */\nfunction useChoices(versions: readonly Version[], value: string | null | undefined, onChange: ((v: Version) => void) | undefined) {\n const [param, setParam] = useVersionParam();\n const reader = useContext(ContentKitContext)?.language;\n const selected = value === undefined ? param : value;\n const change = useCallback((v: Version) => (onChange ? onChange(v) : setParam(v.code)), [onChange, setParam]);\n return useMemo(() => {\n const current = findVersion(versions, selected) ?? (reader ? versions.find((v) => v.languages.includes(reader)) : undefined) ?? versions[0];\n const language = current ? primary(current) : \"\";\n return { current, language, languages: [...new Set(versions.map(primary))], variants: versions.filter((v) => primary(v) === language), change };\n }, [versions, selected, reader, change]);\n}\n\n/**\n * Chooses among a content's versions: a language first when they differ in\n * language, then its variants (traits, labels). The choice is the page's ?v=\n * unless value and onChange say otherwise.\n */\nexport function VersionPicker({ versions, value, onChange, labels, className, appearance }: VersionPickerProps) {\n const { t } = useMessages();\n const languageName = useLanguageName();\n const { current, language, languages, variants, change } = useChoices(versions, value, onChange);\n if (versions.length < 2 || !current) return null;\n return (\n <ContentKitUiRoot appearance={appearance} className={cn(\"flex flex-wrap items-center gap-2 text-sm\", className)} data-ckui=\"version-picker\">\n {languages.length > 1 && (\n <Select\n value={language}\n onValueChange={(l) => {\n const v = versions.find((x) => primary(x) === l);\n if (v) change(v);\n }}\n >\n <SelectTrigger size=\"sm\" aria-label={t(\"versions.language\")}>\n <SelectValue>{(l: string) => languageName(l)}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n {languages.map((l) => (\n <SelectItem key={l} value={l}>\n {languageName(l)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n {variants.length > 1 && (\n <div role=\"radiogroup\" aria-label={t(\"versions.version\")} className=\"flex flex-wrap gap-1\">\n {variants.map((v) => (\n <Button\n key={v.id}\n size=\"sm\"\n role=\"radio\"\n aria-checked={v.id === current.id}\n variant={v.id === current.id ? \"default\" : \"outline\"}\n onClick={() => change(v)}\n data-ckui=\"version-option\"\n data-locked={v.decision.allowed ? undefined : \"\"}\n >\n {!v.decision.allowed && <HugeiconsIcon icon={LockIcon} strokeWidth={2} className=\"size-3.5\" aria-label={t(\"versions.locked\")} />}\n {versionName(v, languageName, labels)}\n </Button>\n ))}\n </div>\n )}\n </ContentKitUiRoot>\n );\n}\n\nexport interface VersionMenuOptions {\n /** The version shown, by code or id; default the page's ?v=, else the reader's language's default. */\n value?: string | null;\n /** Receives the chosen version; default writes its code to the page's ?v=. */\n onChange?: (version: Version) => void;\n labels?: Labels;\n}\n\n/** VersionPicker as VideoPlayer settings entries (menuItems), for a watch page. */\nexport function useVersionMenuItems(versions: readonly Version[], o: VersionMenuOptions = {}): PlayerMenuItem[] {\n const { t } = useMessages();\n const languageName = useLanguageName();\n const { current, language, languages, variants, change } = useChoices(versions, o.value, o.onChange);\n const { labels } = o;\n return useMemo(() => {\n if (versions.length < 2 || !current) return [];\n const out: PlayerMenuItem[] = [];\n if (languages.length > 1)\n out.push({\n label: t(\"versions.language\"),\n value: language,\n options: languages.map((l) => ({ value: l, label: languageName(l) })),\n onChange: (l) => {\n const v = versions.find((x) => primary(x) === l);\n if (v) change(v);\n },\n });\n if (variants.length > 1)\n out.push({\n label: t(\"versions.version\"),\n value: current.id,\n options: variants.map((v) => {\n const name = versionName(v, languageName, labels);\n return { value: v.id, label: v.decision.allowed ? name : t(\"versions.lockedOption\", { name }) };\n }),\n onChange: (id) => {\n const v = versions.find((x) => x.id === id);\n if (v) change(v);\n },\n });\n return out;\n }, [versions, current, language, languages, variants, change, labels, t, languageName]);\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ContentKitUiAppearance } from '../../appearance.js';
|
|
2
|
+
import { ContentKitClient } from '../../client/client.js';
|
|
3
|
+
import { Labels } from '../groups/parts.js';
|
|
4
|
+
export interface ReaderStatsProps {
|
|
5
|
+
/** Display names of content kinds by identifier. */
|
|
6
|
+
labels?: Labels;
|
|
7
|
+
client?: ContentKitClient;
|
|
8
|
+
appearance?: ContentKitUiAppearance;
|
|
9
|
+
className?: string;
|
|
10
|
+
}
|
|
11
|
+
/** The signed-in reader's activity: contents finished per kind, their comments and the reactions those received. Nothing signed out. */
|
|
12
|
+
export declare function ReaderStats({ labels, client, appearance, className }: ReaderStatsProps): import('react').JSX.Element | null;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { useMessages as e } from "../../i18n/context.js";
|
|
2
|
+
import { ContentKitUiRoot as t } from "../../scope.js";
|
|
3
|
+
import { Skeleton as n } from "../../ui/skeleton.js";
|
|
4
|
+
import { contentError as r } from "../social/parts.js";
|
|
5
|
+
import { labelOf as i } from "../groups/parts.js";
|
|
6
|
+
import { useMyStats as a } from "../../react/stats.js";
|
|
7
|
+
import { jsx as o, jsxs as s } from "react/jsx-runtime";
|
|
8
|
+
import { cn as c } from "cn";
|
|
9
|
+
//#region src/components/content/reader-stats.tsx
|
|
10
|
+
function l({ labels: l, client: u, appearance: d, className: f }) {
|
|
11
|
+
let p = e(), { t: m } = p, h = a({ client: u }), g = (e) => /* @__PURE__ */ o(t, {
|
|
12
|
+
appearance: d,
|
|
13
|
+
className: c("grid gap-3 text-sm", f),
|
|
14
|
+
"data-ckui": "reader-stats",
|
|
15
|
+
children: e
|
|
16
|
+
});
|
|
17
|
+
if (h.error) return g(/* @__PURE__ */ o("p", {
|
|
18
|
+
role: "alert",
|
|
19
|
+
className: "text-destructive",
|
|
20
|
+
children: r(p, h.error)
|
|
21
|
+
}));
|
|
22
|
+
if (!h.data) return h.loading ? g(/* @__PURE__ */ o(n, { className: "h-16 w-full" })) : null;
|
|
23
|
+
let _ = [
|
|
24
|
+
...Object.entries(h.data.completed).map(([e, t]) => [m("myStats.completed", { kind: i(l, e) }), t]),
|
|
25
|
+
[m("myStats.comments"), h.data.comments],
|
|
26
|
+
[m("myStats.likes"), h.data.comment_likes],
|
|
27
|
+
[m("myStats.dislikes"), h.data.comment_dislikes]
|
|
28
|
+
];
|
|
29
|
+
return g(/* @__PURE__ */ o("dl", {
|
|
30
|
+
className: "grid grid-cols-2 gap-3 sm:grid-cols-4",
|
|
31
|
+
children: _.map(([e, t]) => /* @__PURE__ */ s("div", {
|
|
32
|
+
className: "grid gap-0.5 rounded-lg border border-border p-3",
|
|
33
|
+
"data-ckui": "reader-stat",
|
|
34
|
+
children: [/* @__PURE__ */ o("dt", {
|
|
35
|
+
className: "text-xs text-muted-foreground",
|
|
36
|
+
children: e
|
|
37
|
+
}), /* @__PURE__ */ o("dd", {
|
|
38
|
+
className: "text-lg font-semibold tabular-nums",
|
|
39
|
+
children: t
|
|
40
|
+
})]
|
|
41
|
+
}, e))
|
|
42
|
+
}));
|
|
43
|
+
}
|
|
44
|
+
//#endregion
|
|
45
|
+
export { l as ReaderStats };
|
|
46
|
+
|
|
47
|
+
//# sourceMappingURL=reader-stats.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reader-stats.js","names":[],"sources":["../../../src/components/content/reader-stats.tsx"],"sourcesContent":["import { cn } from \"cn\";\nimport type { ReactNode } from \"react\";\nimport type { ContentKitUiAppearance } from \"../../appearance.js\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { useMyStats } from \"../../react/stats.js\";\nimport { ContentKitUiRoot } from \"../../scope.js\";\nimport { Skeleton } from \"#ckui/ui/skeleton\";\nimport { labelOf, type Labels } from \"../groups/parts.js\";\nimport { contentError } from \"../social/parts.js\";\n\nexport interface ReaderStatsProps {\n /** Display names of content kinds by identifier. */\n labels?: Labels;\n client?: ContentKitClient;\n appearance?: ContentKitUiAppearance;\n className?: string;\n}\n\n/** The signed-in reader's activity: contents finished per kind, their comments and the reactions those received. Nothing signed out. */\nexport function ReaderStats({ labels, client, appearance, className }: ReaderStatsProps) {\n const m = useMessages();\n const { t } = m;\n const s = useMyStats({ client });\n const root = (children: ReactNode) => (\n <ContentKitUiRoot appearance={appearance} className={cn(\"grid gap-3 text-sm\", className)} data-ckui=\"reader-stats\">\n {children}\n </ContentKitUiRoot>\n );\n if (s.error) return root(<p role=\"alert\" className=\"text-destructive\">{contentError(m, s.error)}</p>);\n if (!s.data) return s.loading ? root(<Skeleton className=\"h-16 w-full\" />) : null;\n const stats: [string, number][] = [\n ...Object.entries(s.data.completed).map(([kind, n]): [string, number] => [t(\"myStats.completed\", { kind: labelOf(labels, kind) }), n]),\n [t(\"myStats.comments\"), s.data.comments],\n [t(\"myStats.likes\"), s.data.comment_likes],\n [t(\"myStats.dislikes\"), s.data.comment_dislikes],\n ];\n return root(\n <dl className=\"grid grid-cols-2 gap-3 sm:grid-cols-4\">\n {stats.map(([label, n]) => (\n <div key={label} className=\"grid gap-0.5 rounded-lg border border-border p-3\" data-ckui=\"reader-stat\">\n <dt className=\"text-xs text-muted-foreground\">{label}</dt>\n <dd className=\"text-lg font-semibold tabular-nums\">{n}</dd>\n </div>\n ))}\n </dl>,\n );\n}\n"],"mappings":";;;;;;;;;AAoBA,SAAgB,EAAY,EAAE,WAAQ,WAAQ,eAAY,gBAA+B;CACvF,IAAM,IAAI,EAAY,GAChB,EAAE,SAAM,GACR,IAAI,EAAW,EAAE,UAAO,CAAC,GACzB,KAAQ,MACZ,kBAAC,GAAD;EAA8B;EAAY,WAAW,EAAG,sBAAsB,CAAS;EAAG,aAAU;EACjG;CACe,CAAA;CAEpB,IAAI,EAAE,OAAO,OAAO,EAAK,kBAAC,KAAD;EAAG,MAAK;EAAQ,WAAU;EAAoB,UAAA,EAAa,GAAG,EAAE,KAAK;CAAK,CAAA,CAAC;CACpG,IAAI,CAAC,EAAE,MAAM,OAAO,EAAE,UAAU,EAAK,kBAAC,GAAD,EAAU,WAAU,cAAe,CAAA,CAAC,IAAI;CAC7E,IAAM,IAA4B;EAChC,GAAG,OAAO,QAAQ,EAAE,KAAK,SAAS,CAAC,CAAC,KAAK,CAAC,GAAM,OAAyB,CAAC,EAAE,qBAAqB,EAAE,MAAM,EAAQ,GAAQ,CAAI,EAAE,CAAC,GAAG,CAAC,CAAC;EACrI,CAAC,EAAE,kBAAkB,GAAG,EAAE,KAAK,QAAQ;EACvC,CAAC,EAAE,eAAe,GAAG,EAAE,KAAK,aAAa;EACzC,CAAC,EAAE,kBAAkB,GAAG,EAAE,KAAK,gBAAgB;CACjD;CACA,OAAO,EACL,kBAAC,MAAD;EAAI,WAAU;EACX,UAAA,EAAM,KAAK,CAAC,GAAO,OAClB,kBAAC,OAAD;GAAiB,WAAU;GAAmD,aAAU;GAAxF,UAAA,CACE,kBAAC,MAAD;IAAI,WAAU;IAAiC,UAAA;GAAU,CAAA,GACzD,kBAAC,MAAD;IAAI,WAAU;IAAsC,UAAA;GAAM,CAAA,CACvD;EAHK,GAAA,CAGL,CACN;CACC,CAAA,CACN;AACF"}
|
|
@@ -21,7 +21,7 @@ export interface MembershipBoxProps {
|
|
|
21
21
|
className?: string;
|
|
22
22
|
appearance?: ContentKitUiAppearance;
|
|
23
23
|
}
|
|
24
|
-
/** A group's membership for the reader: member, join free, or join for its price. */
|
|
24
|
+
/** A group's membership for the reader: member, join free, or join for its price; nothing to join while it sells no membership. */
|
|
25
25
|
export declare function MembershipBox({ group, onCheckout, onSignIn, client: own, onError, className, appearance }: MembershipBoxProps): import('react').JSX.Element | null;
|
|
26
26
|
export interface GroupBannerProps {
|
|
27
27
|
/** The group the host already read; or give id. */
|