@openrails/contentkit-ui 0.103.2 → 0.104.0-rc.1
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 +105 -43
- package/dist/client/beacons.d.ts +3 -13
- package/dist/client/beacons.js +4 -45
- 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/discovery.d.ts +28 -3
- package/dist/client/discovery.js +22 -1
- 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 +35 -9
- package/dist/client/generated/routes.js +59 -2
- package/dist/client/generated/routes.js.map +1 -1
- package/dist/client/generated/wire.d.ts +53 -12
- 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 +4 -2
- package/dist/client/stats.d.ts +5 -5
- package/dist/client/stats.js.map +1 -1
- package/dist/client.js +24 -22
- package/dist/components/admin/content-manager.js +30 -32
- package/dist/components/admin/content-manager.js.map +1 -1
- 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 +452 -428
- package/dist/components/admin/reader-content-editor.js.map +1 -1
- package/dist/components/admin/stats-dashboard.d.ts +4 -4
- package/dist/components/admin/stats-dashboard.js +132 -134
- package/dist/components/admin/stats-dashboard.js.map +1 -1
- package/dist/components/content/daily-releases.js +143 -191
- package/dist/components/content/daily-releases.js.map +1 -1
- package/dist/components/content/favorites.d.ts +6 -0
- package/dist/components/content/favorites.js +33 -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 +34 -13
- package/dist/components/content/grid.js +300 -271
- package/dist/components/content/grid.js.map +1 -1
- package/dist/components/content/history.d.ts +6 -0
- package/dist/components/content/history.js +91 -0
- package/dist/components/content/history.js.map +1 -0
- package/dist/components/content/index.d.ts +1 -0
- 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/media-folder-editor.js +1 -1
- package/dist/components/media-folder-editor.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 +1 -0
- package/dist/index.js +17 -14
- package/dist/locales/admin/de.js +8 -8
- package/dist/locales/admin/de.js.map +1 -1
- package/dist/locales/admin/en.d.ts +4 -4
- package/dist/locales/admin/en.js +8 -8
- package/dist/locales/admin/en.js.map +1 -1
- package/dist/locales/admin/es.js +8 -8
- package/dist/locales/admin/es.js.map +1 -1
- package/dist/locales/admin/ja.js +8 -8
- package/dist/locales/admin/ja.js.map +1 -1
- package/dist/locales/admin/ko.js +8 -8
- package/dist/locales/admin/ko.js.map +1 -1
- package/dist/locales/admin/zh.js +8 -8
- package/dist/locales/admin/zh.js.map +1 -1
- package/dist/locales/discovery/de.js +0 -1
- package/dist/locales/discovery/de.js.map +1 -1
- package/dist/locales/discovery/en.d.ts +0 -1
- package/dist/locales/discovery/en.js +0 -1
- package/dist/locales/discovery/en.js.map +1 -1
- package/dist/locales/discovery/es.js +0 -1
- package/dist/locales/discovery/es.js.map +1 -1
- package/dist/locales/discovery/ja.js +0 -1
- package/dist/locales/discovery/ja.js.map +1 -1
- package/dist/locales/discovery/ko.js +0 -1
- package/dist/locales/discovery/ko.js.map +1 -1
- package/dist/locales/discovery/zh.js +0 -1
- package/dist/locales/discovery/zh.js.map +1 -1
- package/dist/locales/en.d.ts +4 -5
- package/dist/react/discovery.d.ts +20 -17
- package/dist/react/discovery.js +273 -221
- package/dist/react/discovery.js.map +1 -1
- package/dist/react/index.d.ts +2 -1
- package/dist/react/resources.d.ts +3 -1
- package/dist/react/resources.js +4 -0
- package/dist/react/resources.js.map +1 -1
- package/dist/react/stats.d.ts +2 -2
- package/dist/react/stats.js.map +1 -1
- package/dist/react/use-engagement.d.ts +26 -0
- package/dist/react/use-engagement.js +221 -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 +12 -11
- 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/package.json +1 -1
|
@@ -2,28 +2,32 @@ import { useMessages as e } from "../../i18n/context.js";
|
|
|
2
2
|
import { AppearanceContext as t } from "../../scope.js";
|
|
3
3
|
import { Dialog as n, DialogContent as r, DialogDescription as i, DialogHeader as a, DialogTitle as o } from "../../ui/dialog.js";
|
|
4
4
|
import { ContentFilterSettings as s } from "./filters.js";
|
|
5
|
-
import { useContext as c } from "react";
|
|
6
|
-
import { jsx as
|
|
5
|
+
import { useContext as c, useState as l } from "react";
|
|
6
|
+
import { jsx as u, jsxs as d } from "react/jsx-runtime";
|
|
7
7
|
//#region src/components/content/filter-dialog.tsx
|
|
8
|
-
function
|
|
9
|
-
let { t:
|
|
10
|
-
return
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
8
|
+
function f({ open: f, onOpenChange: p, description: m, returnFocus: h, ...g }) {
|
|
9
|
+
let { t: _ } = e(), v = c(t), [y, b] = l({ open: !1 }), x = y;
|
|
10
|
+
return y.open !== f && (x = {
|
|
11
|
+
open: f,
|
|
12
|
+
container: f ? h?.()?.closest("dialog[open]") ?? void 0 : y.container
|
|
13
|
+
}, b(x)), /* @__PURE__ */ u(t.Provider, {
|
|
14
|
+
value: g.appearance ?? v,
|
|
15
|
+
children: /* @__PURE__ */ u(n, {
|
|
16
|
+
open: f,
|
|
17
|
+
onOpenChange: (e) => p(e),
|
|
18
|
+
children: /* @__PURE__ */ d(r, {
|
|
19
|
+
finalFocus: h,
|
|
20
|
+
portalContainer: x.container,
|
|
17
21
|
className: "max-h-[calc(100dvh-2rem)] overflow-y-auto",
|
|
18
|
-
children: [/* @__PURE__ */
|
|
19
|
-
...
|
|
20
|
-
includeDeleted:
|
|
22
|
+
children: [/* @__PURE__ */ d(a, { children: [/* @__PURE__ */ u(o, { children: _("readerSettings.filterTitle") }), /* @__PURE__ */ u(i, { children: m ?? _("readerSettings.filterDescription") })] }), /* @__PURE__ */ u(s, {
|
|
23
|
+
...g,
|
|
24
|
+
includeDeleted: g.includeDeleted ?? !1
|
|
21
25
|
})]
|
|
22
26
|
})
|
|
23
27
|
})
|
|
24
28
|
});
|
|
25
29
|
}
|
|
26
30
|
//#endregion
|
|
27
|
-
export {
|
|
31
|
+
export { f as ContentFilterDialog };
|
|
28
32
|
|
|
29
33
|
//# sourceMappingURL=filter-dialog.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filter-dialog.js","names":[],"sources":["../../../src/components/content/filter-dialog.tsx"],"sourcesContent":["import { useContext, type ReactNode } from \"react\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { AppearanceContext } from \"../../scope.js\";\nimport { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from \"#ckui/ui/dialog\";\nimport { ContentFilterSettings, type ContentFilterSettingsProps } from \"./filters.js\";\n\nexport interface ContentFilterDialogProps extends ContentFilterSettingsProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n description?: ReactNode;\n /** A stable focus target when a preference change removes the opening tile. */\n returnFocus?: () => HTMLElement | null;\n}\n\n/** The same filter controls as ReaderSettings, without another language picker. */\nexport function ContentFilterDialog({ open, onOpenChange, description, returnFocus, ...settings }: ContentFilterDialogProps) {\n const { t } = useMessages();\n const inheritedAppearance = useContext(AppearanceContext);\n return <AppearanceContext.Provider value={settings.appearance ?? inheritedAppearance}>\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent
|
|
1
|
+
{"version":3,"file":"filter-dialog.js","names":[],"sources":["../../../src/components/content/filter-dialog.tsx"],"sourcesContent":["import { useContext, useState, type ReactNode } from \"react\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { AppearanceContext } from \"../../scope.js\";\nimport { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from \"#ckui/ui/dialog\";\nimport { ContentFilterSettings, type ContentFilterSettingsProps } from \"./filters.js\";\n\nexport interface ContentFilterDialogProps extends ContentFilterSettingsProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n description?: ReactNode;\n /** A stable focus target when a preference change removes the opening tile. */\n returnFocus?: () => HTMLElement | null;\n}\n\n/** The same filter controls as ReaderSettings, without another language picker. */\nexport function ContentFilterDialog({ open, onOpenChange, description, returnFocus, ...settings }: ContentFilterDialogProps) {\n const { t } = useMessages();\n const inheritedAppearance = useContext(AppearanceContext);\n // Native modal dialogs live above body portals. Keep the opening surface\n // through preference reloads, even when they remove the triggering tile.\n const [portal, setPortal] = useState<{ open: boolean; container?: HTMLDialogElement }>({ open: false });\n let placement = portal;\n if (portal.open !== open) {\n placement = { open, container: open ? returnFocus?.()?.closest<HTMLDialogElement>(\"dialog[open]\") ?? undefined : portal.container };\n setPortal(placement);\n }\n return <AppearanceContext.Provider value={settings.appearance ?? inheritedAppearance}>\n <Dialog open={open} onOpenChange={(next) => onOpenChange(next)}>\n <DialogContent\n finalFocus={returnFocus}\n portalContainer={placement.container}\n className=\"max-h-[calc(100dvh-2rem)] overflow-y-auto\"\n >\n <DialogHeader>\n <DialogTitle>{t(\"readerSettings.filterTitle\")}</DialogTitle>\n <DialogDescription>{description ?? t(\"readerSettings.filterDescription\")}</DialogDescription>\n </DialogHeader>\n <ContentFilterSettings {...settings} includeDeleted={settings.includeDeleted ?? false} />\n </DialogContent>\n </Dialog>\n </AppearanceContext.Provider>;\n}\n"],"mappings":";;;;;;;AAeA,SAAgB,EAAoB,EAAE,SAAM,iBAAc,gBAAa,gBAAa,GAAG,KAAsC;CAC3H,IAAM,EAAE,SAAM,EAAY,GACpB,IAAsB,EAAW,CAAiB,GAGlD,CAAC,GAAQ,KAAa,EAA2D,EAAE,MAAM,GAAM,CAAC,GAClG,IAAY;CAKhB,OAJI,EAAO,SAAS,MAClB,IAAY;EAAE;EAAM,WAAW,IAAO,IAAc,CAAC,EAAE,QAA2B,cAAc,KAAK,KAAA,IAAY,EAAO;CAAU,GAClI,EAAU,CAAS,IAEd,kBAAC,EAAkB,UAAnB;EAA4B,OAAO,EAAS,cAAc;EAC/D,UAAA,kBAAC,GAAD;GAAc;GAAM,eAAe,MAAS,EAAa,CAAI;GAC3D,UAAA,kBAAC,GAAD;IACE,YAAY;IACZ,iBAAiB,EAAU;IAC3B,WAAU;IAHZ,UAAA,CAKE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UAAc,EAAE,4BAA4B,EAAe,CAAA,GAC3D,kBAAC,GAAD,EAAA,UAAoB,KAAe,EAAE,kCAAkC,EAAqB,CAAA,CAChF,EAAA,CAAA,GACd,kBAAC,GAAD;KAAuB,GAAI;KAAU,gBAAgB,EAAS,kBAAkB;IAAQ,CAAA,CAC3E;;EACT,CAAA;CACkB,CAAA;AAC9B"}
|
|
@@ -16,6 +16,8 @@ export interface ContentFilterSettingsProps {
|
|
|
16
16
|
language?: string;
|
|
17
17
|
/** Currently excluded matches in this listing/day; omitted shows no counts. */
|
|
18
18
|
counts?: readonly ContentFilterCount[];
|
|
19
|
+
/** Counts cover a bounded candidate set, so each displayed count is a lower bound. */
|
|
20
|
+
countsLowerBound?: boolean;
|
|
19
21
|
/** Offer listing deleted content the reader holds. Default true. */
|
|
20
22
|
includeDeleted?: boolean;
|
|
21
23
|
client?: ContentKitClient;
|
|
@@ -41,7 +41,11 @@ function v(r) {
|
|
|
41
41
|
className: "min-w-0 flex-1",
|
|
42
42
|
children: [e.entity.name || e.entity.slug, r.counts !== void 0 && /* @__PURE__ */ g("span", {
|
|
43
43
|
className: "tabular-nums",
|
|
44
|
-
children: [
|
|
44
|
+
children: [
|
|
45
|
+
": ",
|
|
46
|
+
r.countsLowerBound ? "≥ " : "",
|
|
47
|
+
new Intl.NumberFormat(d.language).format(r.counts.filter((t) => t.entity.id === e.entity.id).reduce((e, t) => e + t.count, 0))
|
|
48
|
+
]
|
|
45
49
|
})]
|
|
46
50
|
})]
|
|
47
51
|
}, e.entity.id))
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filters.js","names":[],"sources":["../../../src/components/content/filters.tsx"],"sourcesContent":["import { cn } from \"cn\";\nimport { useContext, useId } from \"react\";\nimport type { ContentKitUiAppearance } from \"../../appearance.js\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport type { GroupCard } from \"../../client/generated/wire.js\";\nimport type { ReleaseDatesQuery } from \"../../client/discovery.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { ContentKitContext, type ContentKitErrorHandler } from \"../../react/context.js\";\nimport { useContentFilters, useReleaseDates } from \"../../react/discovery.js\";\nimport { ContentKitUiRoot } from \"../../scope.js\";\nimport { Button } from \"#ckui/ui/button\";\nimport { Label } from \"#ckui/ui/label\";\nimport { Skeleton } from \"#ckui/ui/skeleton\";\nimport { Switch } from \"#ckui/ui/switch\";\nimport { contentError, ErrorLine, useAction } from \"../social/parts.js\";\n\n/** Counts for one concrete listing selection, aggregated by entity.\n * Only rows offered by the current domain's filter inventory are displayed. */\nexport interface ContentFilterCount {\n entity: Pick<GroupCard, \"id\">;\n count: number;\n}\n\nexport interface ContentFilterSettingsProps {\n /** Listing context; shows shared controls when any kind is in applies_to. */\n kinds?: readonly string[];\n /** Entity names in this language; default the provider's. */\n language?: string;\n /** Currently excluded matches in this listing/day; omitted shows no counts. */\n counts?: readonly ContentFilterCount[];\n /** Offer listing deleted content the reader holds. Default true. */\n includeDeleted?: boolean;\n client?: ContentKitClient;\n onError?: ContentKitErrorHandler;\n className?: string;\n appearance?: ContentKitUiAppearance;\n}\n\n/** Show or hide configured content for this browser or signed-in account. */\nexport function ContentFilterSettings(p: ContentFilterSettingsProps) {\n const m = useMessages();\n const { t } = m;\n const id = useId();\n const ctx = useContext(ContentKitContext);\n const f = useContentFilters({ language: p.language, client: p.client });\n const save = useAction(\"filters.save\", p.onError);\n const root = (children: React.ReactNode) => (\n <ContentKitUiRoot appearance={p.appearance} className={cn(\"grid gap-3 text-sm\", p.className)} data-ckui=\"content-filters\">\n {children}\n </ContentKitUiRoot>\n );\n if (!f.filters) {\n if (f.error) return root(<ErrorLine>{contentError(m, f.error)}</ErrorLine>);\n return root(<Skeleton className=\"h-24 w-full\" />);\n }\n const filters = f.filters;\n const rows = !p.kinds || p.kinds.some((kind) => filters.applies_to.includes(kind)) ? filters.filters : [];\n if (!rows.length && p.includeDeleted === false) return null;\n return root(\n <>\n {rows.length > 0 && (\n <ul className=\"grid divide-y divide-border rounded-lg border border-border\">\n {rows.map((x) => (\n <li key={x.entity.id} className=\"flex flex-wrap items-center gap-3 p-3\" data-ckui=\"content-filter\" data-slug={x.entity.slug} data-visible={x.visible}>\n <Switch id={`${id}-${x.entity.id}`} checked={x.visible} disabled={save.pending} onCheckedChange={(v) => void save.run(() => f.setVisible(x.entity.id, v))} />\n <Label htmlFor={`${id}-${x.entity.id}`} className=\"min-w-0 flex-1\">\n {x.entity.name || x.entity.slug}\n {p.counts !== undefined && (\n <span className=\"tabular-nums\">: {new Intl.NumberFormat(m.language).format(p.counts.filter((entry) => entry.entity.id === x.entity.id).reduce((n, entry) => n + entry.count, 0))}</span>\n )}\n </Label>\n </li>\n ))}\n </ul>\n )}\n {p.includeDeleted !== false && ctx?.viewer !== null && (\n <div className=\"flex items-center gap-3\">\n <Switch id={`${id}-deleted`} checked={filters.include_deleted} disabled={save.pending} onCheckedChange={(v) => void save.run(() => f.setIncludeDeleted(v))} />\n <Label htmlFor={`${id}-deleted`}>{t(\"contents.includeDeleted\")}</Label>\n </div>\n )}\n <ErrorLine error={save.error} />\n </>,\n );\n}\n\nexport interface ReleaseDateIndexProps {\n /** Which contents count: the listing's filters. */\n query?: ReleaseDatesQuery;\n /** The bucket chosen (a day YYYY-MM-DD or month YYYY-MM): list with released_on set to it. */\n selected?: string;\n onSelect?: (date: string) => void;\n client?: ContentKitClient;\n className?: string;\n appearance?: ContentKitUiAppearance;\n}\n\n/** The release date index: per month or day, newest first, how many contents and how many free. */\nexport function ReleaseDateIndex({ query = {}, selected, onSelect, client, className, appearance }: ReleaseDateIndexProps) {\n const m = useMessages();\n const { t } = m;\n const dates = useReleaseDates(query, { client });\n const label = (date: string) => {\n const [y, mo, d] = date.split(\"-\").map(Number);\n const at = new Date(Date.UTC(y!, (mo ?? 1) - 1, d ?? 1));\n return new Intl.DateTimeFormat(m.language, d ? { dateStyle: \"medium\", timeZone: \"UTC\" } : { year: \"numeric\", month: \"long\", timeZone: \"UTC\" }).format(at);\n };\n return (\n <ContentKitUiRoot appearance={appearance} className={cn(\"grid gap-2 text-sm\", className)} data-ckui=\"release-dates\">\n {dates.loading && !dates.items.length ? (\n <Skeleton className=\"h-24 w-full\" />\n ) : dates.error && !dates.items.length ? (\n <div role=\"alert\" className=\"grid justify-items-start gap-2\">\n <p className=\"text-destructive\">{contentError(m, dates.error)}</p>\n <Button size=\"sm\" variant=\"outline\" onClick={dates.reload}>\n {t(\"common.retry\")}\n </Button>\n </div>\n ) : !dates.items.length ? (\n <p className=\"text-muted-foreground\">{t(\"contents.noDates\")}</p>\n ) : (\n <ul className=\"grid gap-1\">\n {dates.items.map((d) => (\n <li key={d.date}>\n <button\n type=\"button\"\n aria-pressed={selected === d.date}\n className={cn(\"flex w-full items-center justify-between gap-3 rounded-md px-2 py-1.5 text-start hover:bg-muted\", selected === d.date && \"bg-muted font-medium\")}\n onClick={() => onSelect?.(d.date)}\n data-ckui=\"release-date\"\n data-date={d.date}\n >\n <span>{label(d.date)}</span>\n <span className=\"text-xs text-muted-foreground tabular-nums\">\n {m.plural(\"contents.items\", d.count)}\n {d.free < d.count && ` · ${m.plural(\"contents.free\", d.free)}`}\n </span>\n </button>\n </li>\n ))}\n </ul>\n )}\n {dates.hasMore && (\n <div className=\"grid justify-items-center gap-2\">\n {dates.error && <p role=\"alert\" className=\"text-destructive\">{contentError(m, dates.error)}</p>}\n <Button variant=\"outline\" size=\"sm\" disabled={dates.loadingMore} onClick={() => void dates.loadMore()}>\n {dates.loadingMore ? t(\"contents.loading\") : dates.error ? t(\"common.retry\") : t(\"contents.loadMore\")}\n </Button>\n </div>\n )}\n </ContentKitUiRoot>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"filters.js","names":[],"sources":["../../../src/components/content/filters.tsx"],"sourcesContent":["import { cn } from \"cn\";\nimport { useContext, useId } from \"react\";\nimport type { ContentKitUiAppearance } from \"../../appearance.js\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport type { GroupCard } from \"../../client/generated/wire.js\";\nimport type { ReleaseDatesQuery } from \"../../client/discovery.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { ContentKitContext, type ContentKitErrorHandler } from \"../../react/context.js\";\nimport { useContentFilters, useReleaseDates } from \"../../react/discovery.js\";\nimport { ContentKitUiRoot } from \"../../scope.js\";\nimport { Button } from \"#ckui/ui/button\";\nimport { Label } from \"#ckui/ui/label\";\nimport { Skeleton } from \"#ckui/ui/skeleton\";\nimport { Switch } from \"#ckui/ui/switch\";\nimport { contentError, ErrorLine, useAction } from \"../social/parts.js\";\n\n/** Counts for one concrete listing selection, aggregated by entity.\n * Only rows offered by the current domain's filter inventory are displayed. */\nexport interface ContentFilterCount {\n entity: Pick<GroupCard, \"id\">;\n count: number;\n}\n\nexport interface ContentFilterSettingsProps {\n /** Listing context; shows shared controls when any kind is in applies_to. */\n kinds?: readonly string[];\n /** Entity names in this language; default the provider's. */\n language?: string;\n /** Currently excluded matches in this listing/day; omitted shows no counts. */\n counts?: readonly ContentFilterCount[];\n /** Counts cover a bounded candidate set, so each displayed count is a lower bound. */\n countsLowerBound?: boolean;\n /** Offer listing deleted content the reader holds. Default true. */\n includeDeleted?: boolean;\n client?: ContentKitClient;\n onError?: ContentKitErrorHandler;\n className?: string;\n appearance?: ContentKitUiAppearance;\n}\n\n/** Show or hide configured content for this browser or signed-in account. */\nexport function ContentFilterSettings(p: ContentFilterSettingsProps) {\n const m = useMessages();\n const { t } = m;\n const id = useId();\n const ctx = useContext(ContentKitContext);\n const f = useContentFilters({ language: p.language, client: p.client });\n const save = useAction(\"filters.save\", p.onError);\n const root = (children: React.ReactNode) => (\n <ContentKitUiRoot appearance={p.appearance} className={cn(\"grid gap-3 text-sm\", p.className)} data-ckui=\"content-filters\">\n {children}\n </ContentKitUiRoot>\n );\n if (!f.filters) {\n if (f.error) return root(<ErrorLine>{contentError(m, f.error)}</ErrorLine>);\n return root(<Skeleton className=\"h-24 w-full\" />);\n }\n const filters = f.filters;\n const rows = !p.kinds || p.kinds.some((kind) => filters.applies_to.includes(kind)) ? filters.filters : [];\n if (!rows.length && p.includeDeleted === false) return null;\n return root(\n <>\n {rows.length > 0 && (\n <ul className=\"grid divide-y divide-border rounded-lg border border-border\">\n {rows.map((x) => (\n <li key={x.entity.id} className=\"flex flex-wrap items-center gap-3 p-3\" data-ckui=\"content-filter\" data-slug={x.entity.slug} data-visible={x.visible}>\n <Switch id={`${id}-${x.entity.id}`} checked={x.visible} disabled={save.pending} onCheckedChange={(v) => void save.run(() => f.setVisible(x.entity.id, v))} />\n <Label htmlFor={`${id}-${x.entity.id}`} className=\"min-w-0 flex-1\">\n {x.entity.name || x.entity.slug}\n {p.counts !== undefined && (\n <span className=\"tabular-nums\">: {p.countsLowerBound ? \"≥ \" : \"\"}{new Intl.NumberFormat(m.language).format(p.counts.filter((entry) => entry.entity.id === x.entity.id).reduce((n, entry) => n + entry.count, 0))}</span>\n )}\n </Label>\n </li>\n ))}\n </ul>\n )}\n {p.includeDeleted !== false && ctx?.viewer !== null && (\n <div className=\"flex items-center gap-3\">\n <Switch id={`${id}-deleted`} checked={filters.include_deleted} disabled={save.pending} onCheckedChange={(v) => void save.run(() => f.setIncludeDeleted(v))} />\n <Label htmlFor={`${id}-deleted`}>{t(\"contents.includeDeleted\")}</Label>\n </div>\n )}\n <ErrorLine error={save.error} />\n </>,\n );\n}\n\nexport interface ReleaseDateIndexProps {\n /** Which contents count: the listing's filters. */\n query?: ReleaseDatesQuery;\n /** The bucket chosen (a day YYYY-MM-DD or month YYYY-MM): list with released_on set to it. */\n selected?: string;\n onSelect?: (date: string) => void;\n client?: ContentKitClient;\n className?: string;\n appearance?: ContentKitUiAppearance;\n}\n\n/** The release date index: per month or day, newest first, how many contents and how many free. */\nexport function ReleaseDateIndex({ query = {}, selected, onSelect, client, className, appearance }: ReleaseDateIndexProps) {\n const m = useMessages();\n const { t } = m;\n const dates = useReleaseDates(query, { client });\n const label = (date: string) => {\n const [y, mo, d] = date.split(\"-\").map(Number);\n const at = new Date(Date.UTC(y!, (mo ?? 1) - 1, d ?? 1));\n return new Intl.DateTimeFormat(m.language, d ? { dateStyle: \"medium\", timeZone: \"UTC\" } : { year: \"numeric\", month: \"long\", timeZone: \"UTC\" }).format(at);\n };\n return (\n <ContentKitUiRoot appearance={appearance} className={cn(\"grid gap-2 text-sm\", className)} data-ckui=\"release-dates\">\n {dates.loading && !dates.items.length ? (\n <Skeleton className=\"h-24 w-full\" />\n ) : dates.error && !dates.items.length ? (\n <div role=\"alert\" className=\"grid justify-items-start gap-2\">\n <p className=\"text-destructive\">{contentError(m, dates.error)}</p>\n <Button size=\"sm\" variant=\"outline\" onClick={dates.reload}>\n {t(\"common.retry\")}\n </Button>\n </div>\n ) : !dates.items.length ? (\n <p className=\"text-muted-foreground\">{t(\"contents.noDates\")}</p>\n ) : (\n <ul className=\"grid gap-1\">\n {dates.items.map((d) => (\n <li key={d.date}>\n <button\n type=\"button\"\n aria-pressed={selected === d.date}\n className={cn(\"flex w-full items-center justify-between gap-3 rounded-md px-2 py-1.5 text-start hover:bg-muted\", selected === d.date && \"bg-muted font-medium\")}\n onClick={() => onSelect?.(d.date)}\n data-ckui=\"release-date\"\n data-date={d.date}\n >\n <span>{label(d.date)}</span>\n <span className=\"text-xs text-muted-foreground tabular-nums\">\n {m.plural(\"contents.items\", d.count)}\n {d.free < d.count && ` · ${m.plural(\"contents.free\", d.free)}`}\n </span>\n </button>\n </li>\n ))}\n </ul>\n )}\n {dates.hasMore && (\n <div className=\"grid justify-items-center gap-2\">\n {dates.error && <p role=\"alert\" className=\"text-destructive\">{contentError(m, dates.error)}</p>}\n <Button variant=\"outline\" size=\"sm\" disabled={dates.loadingMore} onClick={() => void dates.loadMore()}>\n {dates.loadingMore ? t(\"contents.loading\") : dates.error ? t(\"common.retry\") : t(\"contents.loadMore\")}\n </Button>\n </div>\n )}\n </ContentKitUiRoot>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { CSSProperties, ReactNode } from 'react';
|
|
2
2
|
import { ContentKitUiAppearance } from '../../appearance.js';
|
|
3
3
|
import { ContentKitClient } from '../../client/client.js';
|
|
4
|
-
import { ContentQuery, HistoryQuery, RailQuery, SearchQuery } from '../../client/discovery.js';
|
|
5
|
-
import { Card } from '../../client/generated/wire.js';
|
|
4
|
+
import { AccessMode, ContentQuery, HistoryQuery, RailQuery, SearchQuery } from '../../client/discovery.js';
|
|
5
|
+
import { Card, ListingSummary } from '../../client/generated/wire.js';
|
|
6
6
|
import { UseList } from '../../react/use-resource.js';
|
|
7
7
|
import { ContentCardProps } from './card.js';
|
|
8
8
|
export interface CardListProps extends Omit<ContentCardProps, "card" | "onOpen" | "children" | "className"> {
|
|
@@ -18,6 +18,21 @@ export interface CardListProps extends Omit<ContentCardProps, "card" | "onOpen"
|
|
|
18
18
|
client?: ContentKitClient;
|
|
19
19
|
className?: string;
|
|
20
20
|
appearance?: ContentKitUiAppearance;
|
|
21
|
+
/** Collapse paid/locked cards into a counted tile, or hide them; default show. */
|
|
22
|
+
locked?: LockedCards;
|
|
23
|
+
/** Optional-filter summary tiles are on by default; hosts may suppress them in previews. */
|
|
24
|
+
showFilteredSummary?: boolean;
|
|
25
|
+
/** Show the collapsed locked tile only at this measured grid width. Default 0. */
|
|
26
|
+
lockedSummaryMinColumns?: number;
|
|
27
|
+
/** Limit rendered preview tiles without changing the matching selection or summary counts. */
|
|
28
|
+
cardLimit?: number;
|
|
29
|
+
/** Limit cards in mobile previews without mounting their images. */
|
|
30
|
+
mobileCardLimit?: number;
|
|
31
|
+
/** Host-specific nouns for summary controls. */
|
|
32
|
+
summaryLabels?: {
|
|
33
|
+
premium?: (count: number) => string;
|
|
34
|
+
filtered?: (count: number) => string;
|
|
35
|
+
};
|
|
21
36
|
/** Opt into a fluid grid, optionally with a desktop column ceiling. */
|
|
22
37
|
grid?: CardGridLayout;
|
|
23
38
|
}
|
|
@@ -34,30 +49,36 @@ export declare function cardGridLayout(layout?: CardGridLayout): {
|
|
|
34
49
|
className: string;
|
|
35
50
|
style?: CSSProperties;
|
|
36
51
|
};
|
|
37
|
-
/** A list
|
|
38
|
-
|
|
39
|
-
|
|
52
|
+
/** A list carries exact selection-wide counts when the API was asked for them. */
|
|
53
|
+
type SummaryList = UseList<Card> & {
|
|
54
|
+
summary?: ListingSummary;
|
|
55
|
+
};
|
|
56
|
+
type GridProps = CardListProps & {
|
|
57
|
+
list: SummaryList;
|
|
40
58
|
render: string;
|
|
41
|
-
|
|
59
|
+
filterKinds?: readonly string[];
|
|
60
|
+
filterLanguage?: string;
|
|
61
|
+
horizontal?: boolean;
|
|
62
|
+
};
|
|
63
|
+
/** A list's cards and summary controls share one responsive, animated grid. */
|
|
64
|
+
export declare function CardGrid(p: GridProps): import('react').JSX.Element;
|
|
42
65
|
/**
|
|
43
66
|
* What a list does with cards the reader doesn't open: shows them marked
|
|
44
|
-
* (show), leaves them out (hide), or
|
|
45
|
-
*
|
|
67
|
+
* (show), leaves them out (hide), or replaces them with a counted tile
|
|
68
|
+
* that reveals the cached previews in place (collapse).
|
|
46
69
|
*/
|
|
47
70
|
export type LockedCards = "show" | "hide" | "collapse";
|
|
71
|
+
/** Settle locked-card admission before pagination, retaining explicit reader modes otherwise. */
|
|
72
|
+
export declare function cardListMode(locked: LockedCards | undefined, mode?: AccessMode): AccessMode | undefined;
|
|
48
73
|
/** Contents as cards (GET /v1/contents): any sort and filter, paged. */
|
|
49
74
|
export declare function ContentGrid({ query, locked, ...p }: CardListProps & {
|
|
50
75
|
query: ContentQuery;
|
|
51
|
-
locked?: LockedCards;
|
|
52
76
|
}): import('react').JSX.Element;
|
|
53
77
|
/** Search results as cards; nothing until the query has text. */
|
|
54
78
|
export declare function SearchResults({ query, ...p }: CardListProps & {
|
|
55
79
|
query: SearchQuery;
|
|
56
80
|
}): import('react').JSX.Element | null;
|
|
57
|
-
|
|
58
|
-
export declare function ContentHistory({ query, ...p }: CardListProps & {
|
|
59
|
-
query?: HistoryQuery;
|
|
60
|
-
}): import('react').JSX.Element;
|
|
81
|
+
export { ContentHistory } from './history.js';
|
|
61
82
|
export interface ContentRailProps extends Omit<CardListProps, "infinite" | "empty"> {
|
|
62
83
|
/** The rail's heading. */
|
|
63
84
|
title: ReactNode;
|