@openrails/contentkit-ui 0.103.1 → 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.d.ts +1 -1
- package/dist/components/admin/content-manager.js +342 -302
- package/dist/components/admin/content-manager.js.map +1 -1
- package/dist/components/admin/page-review.js +106 -97
- package/dist/components/admin/page-review.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.d.ts +6 -0
- package/dist/components/admin/reader-content-editor.js +733 -0
- package/dist/components/admin/reader-content-editor.js.map +1 -0
- package/dist/components/admin/reader-manifest.js +120 -105
- package/dist/components/admin/reader-manifest.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/editor-save.d.ts +32 -0
- package/dist/components/content/editor-save.js +51 -0
- package/dist/components/content/editor-save.js.map +1 -0
- package/dist/components/content/editors.d.ts +17 -2
- package/dist/components/content/editors.js +359 -328
- package/dist/components/content/editors.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/offer-fields.d.ts +1 -1
- package/dist/components/content/offer-fields.js +86 -64
- package/dist/components/content/offer-fields.js.map +1 -1
- package/dist/components/content/search.d.ts +11 -4
- package/dist/components/content/search.js +22 -11
- package/dist/components/content/search.js.map +1 -1
- package/dist/components/content/summary-tile.d.ts +14 -0
- package/dist/components/content/summary-tile.js +63 -0
- package/dist/components/content/summary-tile.js.map +1 -0
- package/dist/components/groups/memberships.d.ts +11 -1
- package/dist/components/groups/memberships.js +1 -1
- package/dist/components/groups/memberships.js.map +1 -1
- package/dist/components/media-folder-editor.d.ts +4 -0
- package/dist/components/media-folder-editor.js +178 -168
- 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 +33 -8
- package/dist/locales/admin/de.js.map +1 -1
- package/dist/locales/admin/en.d.ts +29 -4
- package/dist/locales/admin/en.js +33 -8
- package/dist/locales/admin/en.js.map +1 -1
- package/dist/locales/admin/es.js +33 -8
- package/dist/locales/admin/es.js.map +1 -1
- package/dist/locales/admin/ja.js +33 -8
- package/dist/locales/admin/ja.js.map +1 -1
- package/dist/locales/admin/ko.js +33 -8
- package/dist/locales/admin/ko.js.map +1 -1
- package/dist/locales/admin/zh.js +33 -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 +29 -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/page-review.d.ts +1 -1
- package/dist/react/page-review.js +6 -5
- package/dist/react/page-review.js.map +1 -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-draft.d.ts +8 -0
- package/dist/react/use-draft.js +35 -0
- package/dist/react/use-draft.js.map +1 -0
- 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
|
@@ -12,7 +12,7 @@ import { Cancel01Icon as g, Clock01Icon as _, File01Icon as v, Folder01Icon as y
|
|
|
12
12
|
import { HugeiconsIcon as w } from "@hugeicons/react";
|
|
13
13
|
import { createPortal as re } from "react-dom";
|
|
14
14
|
//#region src/components/content/search.tsx
|
|
15
|
-
var T = (e) => e.type === "content" ? `c:${e.card.id}` : e.type === "group" ? `g:${e.group.id}` : `r:${e.query}`, E = (e) => e.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").normalize("NFC").toLowerCase().trim().replace(/\s+/g, " "), D = {
|
|
15
|
+
var T = (e) => e.type === "content" ? `c:${e.card.id}` : e.type === "group" ? `g:${e.group.id}` : `r:${e.query}`, E = (e) => e.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").normalize("NFC").toLowerCase().replace(/(^|[^\p{L}\p{N}\p{M}_])(?:the|a|an)(?=$|[^\p{L}\p{N}\p{M}_])/gu, "$1").trim().replace(/\s+/g, " "), D = {
|
|
16
16
|
artist: {
|
|
17
17
|
icon: C,
|
|
18
18
|
color: "text-blue-500 dark:text-blue-400"
|
|
@@ -46,7 +46,7 @@ var T = (e) => e.type === "content" ? `c:${e.card.id}` : e.type === "group" ? `g
|
|
|
46
46
|
color: "text-orange-500 dark:text-orange-400"
|
|
47
47
|
}
|
|
48
48
|
};
|
|
49
|
-
function
|
|
49
|
+
function ie(e, t, n, r) {
|
|
50
50
|
let [i, a] = d({
|
|
51
51
|
top: 0,
|
|
52
52
|
left: 0,
|
|
@@ -76,7 +76,7 @@ function O(e, t, n, r) {
|
|
|
76
76
|
r
|
|
77
77
|
]), i;
|
|
78
78
|
}
|
|
79
|
-
function
|
|
79
|
+
function O({ kind: e }) {
|
|
80
80
|
let t = D[e];
|
|
81
81
|
return /* @__PURE__ */ p(w, {
|
|
82
82
|
icon: t?.icon ?? x,
|
|
@@ -87,14 +87,19 @@ function ie({ kind: e }) {
|
|
|
87
87
|
});
|
|
88
88
|
}
|
|
89
89
|
function k(l) {
|
|
90
|
-
let { t: v, error: y } = t(), b = te(), S = c(r)?.navigate, [C, D] = d(l.defaultValue ?? ""), [k, A] = d(!1), [j, M] = d(), N = u(null), P = u(null), F = u(null), I = l.presentation === "header", L =
|
|
90
|
+
let { t: v, error: y } = t(), b = te(), S = c(r)?.navigate, [C, D] = d(l.defaultValue ?? ""), [k, A] = d(!1), [j, M] = d(), N = u(null), P = u(null), F = u(null), I = l.presentation === "header", L = ie(k, I, N, F), { top: R } = L, z = !C.trim(), B = o(C, {
|
|
91
91
|
kind: l.kind,
|
|
92
92
|
entity_kinds: l.groupKinds,
|
|
93
|
-
client: l.client
|
|
93
|
+
client: l.client,
|
|
94
|
+
request: l.requestSuggestions,
|
|
95
|
+
minLength: l.disabled ? Infinity : 1
|
|
94
96
|
}), V = a({
|
|
95
|
-
enabled: l.recent !== !1 && k,
|
|
97
|
+
enabled: l.recent !== !1 && k && !l.disabled,
|
|
96
98
|
client: l.client
|
|
97
|
-
}), H = E(C), U = B.query === C.trim(), W = ne(() => [...l.recent === !1 ? [] : V.searches.
|
|
99
|
+
}), H = E(C), U = B.query === C.trim(), W = ne(() => [...l.recent === !1 ? [] : V.searches.map((e) => ({
|
|
100
|
+
...e,
|
|
101
|
+
normalized: E(e.query)
|
|
102
|
+
})).filter((e) => z || H && e.normalized.includes(H)).sort((e, t) => Number(t.normalized.startsWith(H)) - Number(e.normalized.startsWith(H))).map((e) => ({
|
|
98
103
|
type: "recent",
|
|
99
104
|
query: e.query
|
|
100
105
|
})), ...!z && U ? [...B.entities.map((e) => ({
|
|
@@ -123,7 +128,7 @@ function k(l) {
|
|
|
123
128
|
return document.addEventListener("pointerdown", e), () => document.removeEventListener("pointerdown", e);
|
|
124
129
|
}, [k]);
|
|
125
130
|
let Y = () => {
|
|
126
|
-
A(!0);
|
|
131
|
+
l.disabled || A(!0);
|
|
127
132
|
}, X = (e) => {
|
|
128
133
|
A(!1), e && l.onSearch(e);
|
|
129
134
|
}, Z = (e) => {
|
|
@@ -162,6 +167,7 @@ function k(l) {
|
|
|
162
167
|
children: W.map((t, n) => /* @__PURE__ */ p("li", {
|
|
163
168
|
id: `${b}-${n}`,
|
|
164
169
|
role: "option",
|
|
170
|
+
"aria-label": I && t.type !== "recent" ? `${s(l.labels, t.type === "content" ? t.card.kind : t.group.kind)} ${t.type === "content" ? t.card.title || t.card.code : t.group.name || t.group.slug}` : void 0,
|
|
165
171
|
"aria-selected": n === G,
|
|
166
172
|
"data-ckui": "suggestion",
|
|
167
173
|
"data-type": t.type,
|
|
@@ -171,7 +177,7 @@ function k(l) {
|
|
|
171
177
|
e.preventDefault(), Z(t);
|
|
172
178
|
},
|
|
173
179
|
children: I && t.type !== "recent" ? /* @__PURE__ */ m(f, { children: [
|
|
174
|
-
/* @__PURE__ */ p(
|
|
180
|
+
/* @__PURE__ */ p(O, { kind: t.type === "content" ? t.card.kind : t.group.kind }),
|
|
175
181
|
/* @__PURE__ */ p("span", {
|
|
176
182
|
className: "sr-only",
|
|
177
183
|
children: s(l.labels, t.type === "content" ? t.card.kind : t.group.kind)
|
|
@@ -263,12 +269,15 @@ function k(l) {
|
|
|
263
269
|
role: "search",
|
|
264
270
|
className: "relative",
|
|
265
271
|
onSubmit: (e) => {
|
|
266
|
-
if (e.preventDefault(),
|
|
267
|
-
|
|
272
|
+
if (e.preventDefault(), !l.disabled) {
|
|
273
|
+
if (k && K) return Z(K);
|
|
274
|
+
l.selectionOnly || X(C.trim());
|
|
275
|
+
}
|
|
268
276
|
},
|
|
269
277
|
children: [
|
|
270
278
|
I ? /* @__PURE__ */ p("button", {
|
|
271
279
|
type: "submit",
|
|
280
|
+
disabled: l.disabled,
|
|
272
281
|
className: "absolute top-1/2 left-2.5 -translate-y-1/2 rounded text-muted-foreground hover:text-foreground focus-visible:outline-2 focus-visible:outline-ring",
|
|
273
282
|
"aria-label": v("contents.search"),
|
|
274
283
|
children: /* @__PURE__ */ p(w, {
|
|
@@ -293,6 +302,7 @@ function k(l) {
|
|
|
293
302
|
"aria-activedescendant": k && K ? `${b}-${G}` : void 0,
|
|
294
303
|
"aria-autocomplete": "list",
|
|
295
304
|
autoComplete: "off",
|
|
305
|
+
disabled: l.disabled,
|
|
296
306
|
className: h("h-9 ps-8", I && "rounded-full border-border/50 bg-foreground/10 pe-9 text-base shadow-none md:text-base"),
|
|
297
307
|
value: C,
|
|
298
308
|
placeholder: l.placeholder ?? v("contents.searchPlaceholder"),
|
|
@@ -311,6 +321,7 @@ function k(l) {
|
|
|
311
321
|
}),
|
|
312
322
|
I && (C || k) && /* @__PURE__ */ p("button", {
|
|
313
323
|
type: "button",
|
|
324
|
+
disabled: l.disabled,
|
|
314
325
|
className: "absolute top-1/2 right-2.5 -translate-y-1/2 rounded p-0.5 text-muted-foreground hover:text-foreground focus-visible:outline-2 focus-visible:outline-ring",
|
|
315
326
|
"aria-label": v(C ? "contents.clearSearch" : "common.close"),
|
|
316
327
|
onMouseDown: (e) => e.preventDefault(),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search.js","names":[],"sources":["../../../src/components/content/search.tsx"],"sourcesContent":["import { Cancel01Icon, Clock01Icon, File01Icon, Folder01Icon, Image01Icon, Search01Icon, TagsIcon, UserIcon } from \"@hugeicons/core-free-icons\";\nimport { HugeiconsIcon, type IconSvgElement } from \"@hugeicons/react\";\nimport { cn } from \"cn\";\nimport { useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, type RefObject, type KeyboardEvent } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport type { ContentKitUiAppearance } from \"../../appearance.js\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport type { Card, GroupCard } from \"../../client/generated/wire.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { ContentKitContext } from \"../../react/context.js\";\nimport { useRecentSearches, useTypeahead } from \"../../react/discovery.js\";\nimport { ContentKitUiRoot } from \"../../scope.js\";\nimport { Input } from \"#ckui/ui/input\";\nimport { RenditionImg } from \"../rendition-img.js\";\nimport { labelOf, type Labels } from \"../groups/parts.js\";\n\nexport interface SearchBoxProps {\n /** Submits the text: the host shows its search page. */\n onSearch: (q: string) => void;\n /** Suggest contents of this kind only. */\n kind?: string;\n /** The group kinds suggested; every kind with a page by default. */\n groupKinds?: readonly string[];\n defaultValue?: string;\n placeholder?: string;\n /** A suggestion's link; default its canonical path. */\n href?: (item: Card | GroupCard) => string | undefined;\n labels?: Labels;\n /** Recent searches first, prefix-filtered while typing. Default true. */\n recent?: boolean;\n /** A portal dropdown anchored to the header field; default stays inline. */\n presentation?: \"inline\" | \"header\";\n client?: ContentKitClient;\n className?: string;\n appearance?: ContentKitUiAppearance;\n}\n\ntype Suggestion = { type: \"content\"; card: Card } | { type: \"group\"; group: GroupCard } | { type: \"recent\"; query: string };\nconst suggestionKey = (s: Suggestion) => s.type === \"content\" ? `c:${s.card.id}` : s.type === \"group\" ? `g:${s.group.id}` : `r:${s.query}`;\n\n// Match the keyword index's case, Latin accent and whitespace normalization.\nconst prefixText = (text: string) => text.normalize(\"NFKD\").replace(/[\\u0300-\\u036f]/g, \"\").normalize(\"NFC\").toLowerCase().trim().replace(/\\s+/g, \" \");\n\nconst TYPE_ICONS: Record<string, { icon: IconSvgElement; color: string }> = {\n artist: { icon: UserIcon, color: \"text-blue-500 dark:text-blue-400\" },\n series: { icon: Folder01Icon, color: \"text-yellow-600 dark:text-yellow-400\" },\n gallery: { icon: Image01Icon, color: \"text-green-600 dark:text-green-400\" },\n post: { icon: File01Icon, color: \"text-purple-500 dark:text-purple-400\" },\n blog: { icon: File01Icon, color: \"text-purple-500 dark:text-purple-400\" },\n poll: { icon: File01Icon, color: \"text-purple-500 dark:text-purple-400\" },\n character: { icon: UserIcon, color: \"text-purple-500 dark:text-purple-400\" },\n tag: { icon: TagsIcon, color: \"text-orange-500 dark:text-orange-400\" },\n};\n\nfunction useHeaderAnchor(open: boolean, enabled: boolean, root: RefObject<HTMLDivElement | null>, input: RefObject<HTMLInputElement | null>) {\n const [position, setPosition] = useState({ top: 0, left: 0, width: 0 });\n useLayoutEffect(() => {\n if (!open || !enabled || !root.current) return;\n const anchor = input.current ?? root.current;\n const view = root.current.ownerDocument.defaultView;\n if (!view) return;\n const measure = () => {\n const rect = anchor.getBoundingClientRect();\n setPosition({ top: Math.max(0, Math.min(view.innerHeight, rect.bottom + 4)), left: rect.left, width: rect.width });\n };\n measure();\n const observer = typeof ResizeObserver === \"undefined\" ? undefined : new ResizeObserver(measure);\n observer?.observe(anchor);\n view.addEventListener(\"resize\", measure);\n view.addEventListener(\"scroll\", measure, true);\n view.visualViewport?.addEventListener(\"resize\", measure);\n view.visualViewport?.addEventListener(\"scroll\", measure);\n return () => {\n observer?.disconnect();\n view.removeEventListener(\"resize\", measure);\n view.removeEventListener(\"scroll\", measure, true);\n view.visualViewport?.removeEventListener(\"resize\", measure);\n view.visualViewport?.removeEventListener(\"scroll\", measure);\n };\n }, [open, enabled, root, input]);\n return position;\n}\n\nfunction SearchTypeIcon({ kind }: { kind: string }) {\n const type = TYPE_ICONS[kind];\n return <HugeiconsIcon icon={type?.icon ?? Search01Icon} strokeWidth={2} className={cn(\"size-4 shrink-0\", type?.color ?? \"text-muted-foreground\")} data-search-kind={kind} aria-hidden />;\n}\n\n/**\n * Recent query prefixes first, followed by entity and title prefixes. Enter\n * searches, or opens the chosen suggestion; full search can match more deeply.\n */\nexport function SearchBox(p: SearchBoxProps) {\n const { t, error } = useMessages();\n const id = useId();\n const navigate = useContext(ContentKitContext)?.navigate;\n const [text, setText] = useState(p.defaultValue ?? \"\");\n const [open, setOpen] = useState(false);\n // null requests the first result when opening an as-yet-unloaded panel.\n const [active, setActive] = useState<string | null>();\n const root = useRef<HTMLDivElement>(null);\n const panel = useRef<HTMLDivElement>(null);\n const input = useRef<HTMLInputElement>(null);\n const header = p.presentation === \"header\";\n const anchor = useHeaderAnchor(open, header, root, input);\n const { top } = anchor;\n const empty = !text.trim();\n const found = useTypeahead(text, { kind: p.kind, entity_kinds: p.groupKinds, client: p.client });\n const recent = useRecentSearches({ enabled: p.recent !== false && open, client: p.client });\n const prefix = prefixText(text);\n const current = found.query === text.trim();\n const visible = useMemo<Suggestion[]>(\n () => [\n ...(p.recent === false ? [] : recent.searches.filter((r) => prefixText(r.query).startsWith(prefix)).map((r) => ({ type: \"recent\" as const, query: r.query }))),\n ...(!empty && current ? [\n ...found.entities.map((group) => ({ type: \"group\" as const, group })),\n ...found.contents.map((card) => ({ type: \"content\" as const, card })),\n ] : []),\n ],\n [p.recent, prefix, empty, current, recent.searches, found.contents, found.entities],\n );\n // History and live suggestions can arrive independently. Track identity so\n // prepending history never changes what Enter will open.\n const activeIndex = active === null ? Math.min(0, visible.length - 1) : visible.findIndex((s) => suggestionKey(s) === active);\n const selected = visible[activeIndex];\n const activate = (index: number) => {\n const suggestion = visible[index];\n setActive(suggestion ? suggestionKey(suggestion) : index >= 0 ? null : undefined);\n };\n const loading = recent.loading || (!empty && (!current || found.loading));\n const showRecent = empty && p.recent !== false;\n const emptyMessage = empty ? !header && showRecent && !recent.error ? \"contents.noRecentSearches\" : \"contents.searchHint\" : \"contents.noResults\";\n useEffect(() => {\n if (!open) return;\n const outside = (event: PointerEvent) => {\n if (!root.current?.contains(event.target as Node) && !panel.current?.contains(event.target as Node)) setOpen(false);\n };\n document.addEventListener(\"pointerdown\", outside);\n return () => document.removeEventListener(\"pointerdown\", outside);\n }, [open]);\n const reveal = () => {\n setOpen(true);\n };\n const search = (q: string) => {\n setOpen(false);\n if (q) p.onSearch(q);\n };\n const go = (s: Suggestion) => {\n if (s.type === \"recent\") {\n setText(s.query);\n return search(s.query);\n }\n const url = p.href ? p.href(s.type === \"content\" ? s.card : s.group) : s.type === \"content\" ? s.card.path : s.group.path;\n setOpen(false);\n if (!url) return;\n if (navigate) navigate(url);\n else location.assign(url);\n };\n const panelKeyDown = (event: KeyboardEvent) => {\n if (event.key !== \"Escape\") return;\n event.preventDefault();\n input.current?.focus();\n setOpen(false);\n setActive(undefined);\n };\n const results = (<>\n {showRecent && (\n <div className={cn(\"flex items-center justify-between gap-2 text-muted-foreground\", header ? \"mb-4 text-base font-semibold\" : \"px-2 py-1 text-xs\")}>\n <span id={`${id}-recent`} className=\"flex items-center gap-2\">{header && <HugeiconsIcon icon={Clock01Icon} strokeWidth={2} className=\"size-5\" aria-hidden />}{t(\"contents.recentSearches\")}</span>\n {visible.length > 0 && <button\n type=\"button\"\n className=\"rounded px-1 hover:text-foreground\"\n onMouseDown={(e) => {\n e.preventDefault();\n }}\n onClick={() => void recent.clear()}\n >\n {t(header ? \"contents.clearAllRecent\" : \"contents.clearRecent\")}\n </button>}\n </div>\n )}\n <ul id={`${id}-list`} role=\"listbox\" aria-label={empty ? t(\"contents.recentSearches\") : t(\"contents.suggestions\")} className={cn(\"grid\", header && \"gap-2\")}>\n {visible.map((s, i) => (\n <li\n key={suggestionKey(s)}\n id={`${id}-${i}`}\n role=\"option\"\n aria-selected={i === activeIndex}\n data-ckui=\"suggestion\"\n data-type={s.type}\n className={cn(\"flex cursor-pointer items-center\", header ? s.type === \"recent\" ? \"gap-2 rounded-md p-3 text-base hover:bg-foreground/10\" : \"gap-3 rounded-md px-4 py-1 text-base font-medium hover:bg-foreground/10\" : \"gap-2 px-2 py-1.5\", i === activeIndex && \"bg-accent text-accent-foreground\")}\n onMouseEnter={() => setActive(suggestionKey(s))}\n onMouseDown={(e) => {\n e.preventDefault();\n go(s);\n }}\n >\n {header && s.type !== \"recent\" ? (\n <>\n <SearchTypeIcon kind={s.type === \"content\" ? s.card.kind : s.group.kind} />\n <span className=\"sr-only\">{labelOf(p.labels, s.type === \"content\" ? s.card.kind : s.group.kind)}</span>\n <span className=\"min-w-0 flex-1 truncate\">{s.type === \"content\" ? s.card.title || s.card.code : s.group.name || s.group.slug}</span>\n </>\n ) : s.type === \"content\" ? (\n <>\n <span className=\"h-10 w-8 shrink-0 overflow-hidden rounded bg-muted\">\n <RenditionImg outputs={s.card.cover} alt=\"\" fit=\"cover\" className=\"size-full\" />\n </span>\n <span className=\"min-w-0 truncate\">{s.card.title || s.card.code}</span>\n </>\n ) : s.type === \"group\" ? (\n <>\n <span className=\"min-w-0 flex-1 truncate\">{s.group.name || s.group.slug}</span>\n <span className=\"text-xs text-muted-foreground\">{labelOf(p.labels, s.group.kind)}</span>\n </>\n ) : (\n <>\n <HugeiconsIcon icon={header ? Search01Icon : Clock01Icon} strokeWidth={2} className=\"size-4 shrink-0 text-muted-foreground\" aria-hidden />\n <span className=\"min-w-0 flex-1 truncate\">{s.query}</span>\n <button\n type=\"button\"\n className=\"rounded p-0.5 text-muted-foreground hover:text-foreground\"\n aria-label={t(\"contents.removeRecent\", { q: s.query })}\n onMouseDown={(e) => {\n e.preventDefault();\n e.stopPropagation();\n }}\n onClick={() => void recent.clear(s.query)}\n >\n <HugeiconsIcon icon={Cancel01Icon} strokeWidth={2} className=\"size-3.5\" aria-hidden />\n </button>\n </>\n )}\n </li>\n ))}\n </ul>\n {loading ? <div role=\"status\" className={cn(\"text-muted-foreground\", header ? \"flex justify-center py-4\" : \"px-3 py-2\")}>{header ? <><span className=\"size-6 animate-spin rounded-full border-2 border-muted border-t-foreground motion-reduce:animate-none\" aria-hidden /><span className=\"sr-only\">{t(\"contents.loading\")}</span></> : t(\"contents.loading\")}</div>\n : !empty && found.error ? <p role=\"alert\" className={cn(\"px-3 py-2 text-destructive\", header && \"text-center\")}>{error(found.error)}</p>\n : visible.length === 0 && <div role=\"status\" className={cn(\"text-muted-foreground\", header ? \"flex flex-col items-center gap-2 py-8 text-center\" : \"px-3 py-2\")}>{header && <HugeiconsIcon icon={Search01Icon} strokeWidth={2} className=\"size-8\" aria-hidden />}<p>{t(emptyMessage, { q: text.trim() })}</p></div>}\n </>);\n return (\n <ContentKitUiRoot ref={root} appearance={p.appearance} className={cn(\"relative text-sm\", p.className)} data-ckui=\"search-box\" onBlur={(event) => { if (!event.currentTarget.contains(event.relatedTarget) && !panel.current?.contains(event.relatedTarget)) setOpen(false); }}>\n <form\n role=\"search\"\n className=\"relative\"\n onSubmit={(e) => {\n e.preventDefault();\n if (open && selected) return go(selected);\n search(text.trim());\n }}\n >\n {header ? <button type=\"submit\" className=\"absolute top-1/2 left-2.5 -translate-y-1/2 rounded text-muted-foreground hover:text-foreground focus-visible:outline-2 focus-visible:outline-ring\" aria-label={t(\"contents.search\")}><HugeiconsIcon icon={Search01Icon} strokeWidth={2} className=\"size-4\" aria-hidden /></button>\n : <HugeiconsIcon icon={Search01Icon} strokeWidth={2} className=\"pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2 text-muted-foreground\" aria-hidden />}\n <Input\n ref={input}\n type={header ? \"text\" : \"search\"}\n role=\"combobox\"\n aria-label={t(\"contents.search\")}\n aria-expanded={open}\n aria-controls={`${id}-list`}\n aria-activedescendant={open && selected ? `${id}-${activeIndex}` : undefined}\n aria-autocomplete=\"list\"\n autoComplete=\"off\"\n className={cn(\"h-9 ps-8\", header && \"rounded-full border-border/50 bg-foreground/10 pe-9 text-base shadow-none md:text-base\")}\n value={text}\n placeholder={p.placeholder ?? t(\"contents.searchPlaceholder\")}\n onChange={(e) => {\n setText(e.target.value);\n setOpen(true);\n setActive(undefined);\n }}\n onFocus={reveal}\n onClick={reveal}\n onKeyDown={(e) => {\n if (e.key === \"Escape\") {\n e.preventDefault();\n setOpen(false);\n setActive(undefined);\n return;\n }\n if (e.key === \"ArrowDown\") {\n e.preventDefault();\n reveal();\n activate(open ? Math.min(visible.length - 1, activeIndex + 1) : 0);\n } else if (e.key === \"ArrowUp\" && open) {\n e.preventDefault();\n activate(Math.max(-1, activeIndex - 1));\n }\n }}\n />\n {header && (text || open) && (\n <button\n type=\"button\"\n className=\"absolute top-1/2 right-2.5 -translate-y-1/2 rounded p-0.5 text-muted-foreground hover:text-foreground focus-visible:outline-2 focus-visible:outline-ring\"\n aria-label={text ? t(\"contents.clearSearch\") : t(\"common.close\")}\n onMouseDown={(event) => event.preventDefault()}\n onClick={() => {\n input.current?.focus();\n setActive(undefined);\n if (text) { setText(\"\"); setOpen(true); }\n else setOpen(false);\n }}\n ><HugeiconsIcon icon={Cancel01Icon} strokeWidth={2} className=\"size-4\" aria-hidden /></button>\n )}\n </form>\n {open && (header ? createPortal(\n <ContentKitUiRoot appearance={p.appearance} className=\"fixed inset-x-0 bottom-0 z-[70] text-sm\" style={{ top }} data-ckui=\"search-layer\">\n <div className=\"absolute inset-0 bg-black/30\" data-ckui=\"search-backdrop\" aria-hidden />\n <div ref={panel} style={{ width: anchor.width, marginLeft: anchor.left }} className=\"relative origin-top animate-[search-expand_300ms_ease-out] rounded-lg border border-border bg-background text-foreground shadow-lg transition-none motion-reduce:animate-none\" data-ckui=\"search-panel\" onKeyDown={panelKeyDown}>\n <div className=\"w-full px-3 pt-3\">\n <div className=\"[--search-max-height:70vh] overflow-y-auto pb-7 md:[--search-max-height:60vh]\" style={{ maxHeight: `min(var(--search-max-height), calc(100dvh - ${top}px - 1.875rem))` }}>\n <div>{results}</div>\n </div>\n </div>\n </div>\n </ContentKitUiRoot>, document.body,\n ) : <div ref={panel} className=\"absolute top-full z-20 mt-1 grid max-h-96 w-full overflow-auto rounded-md border border-border bg-popover py-1 text-popover-foreground shadow-md\" data-ckui=\"search-panel\" onKeyDown={panelKeyDown}>{results}</div>)}\n\n </ContentKitUiRoot>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"search.js","names":[],"sources":["../../../src/components/content/search.tsx"],"sourcesContent":["import { Cancel01Icon, Clock01Icon, File01Icon, Folder01Icon, Image01Icon, Search01Icon, TagsIcon, UserIcon } from \"@hugeicons/core-free-icons\";\nimport { HugeiconsIcon, type IconSvgElement } from \"@hugeicons/react\";\nimport { cn } from \"cn\";\nimport { useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, type RefObject, type KeyboardEvent } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport type { ContentKitUiAppearance } from \"../../appearance.js\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport type { TypeaheadQuery } from \"../../client/discovery.js\";\nimport type { Card, GroupCard, Typeahead } from \"../../client/generated/wire.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { ContentKitContext } from \"../../react/context.js\";\nimport { useRecentSearches, useTypeahead } from \"../../react/discovery.js\";\nimport { ContentKitUiRoot } from \"../../scope.js\";\nimport { Input } from \"#ckui/ui/input\";\nimport { RenditionImg } from \"../rendition-img.js\";\nimport { labelOf, type Labels } from \"../groups/parts.js\";\n\nexport interface SearchBoxProps {\n /** Submits the text: the host shows its search page. */\n onSearch: (q: string) => void;\n /** Suggest contents of this kind only. */\n kind?: string;\n /** The group kinds suggested; every kind with a page by default. */\n groupKinds?: readonly string[];\n defaultValue?: string;\n placeholder?: string;\n /** A suggestion's link; default its canonical path. */\n href?: (item: Card | GroupCard) => string | undefined;\n labels?: Labels;\n /** Matching recent searches first, with prefixes before interior matches. Default true. */\n recent?: boolean;\n /** An authenticated or host-specific suggestion source; public typeahead by default. */\n requestSuggestions?: (query: TypeaheadQuery, signal: AbortSignal) => Promise<Typeahead>;\n /** Enter opens an explicitly selected suggestion; free text never submits a results search. */\n selectionOnly?: boolean;\n disabled?: boolean;\n /** A portal dropdown anchored to the header field; default stays inline. */\n presentation?: \"inline\" | \"header\";\n client?: ContentKitClient;\n className?: string;\n appearance?: ContentKitUiAppearance;\n}\n\ntype Suggestion = { type: \"content\"; card: Card } | { type: \"group\"; group: GroupCard } | { type: \"recent\"; query: string };\nconst suggestionKey = (s: Suggestion) => s.type === \"content\" ? `c:${s.card.id}` : s.type === \"group\" ? `g:${s.group.id}` : `r:${s.query}`;\n\n// Mirror search-only normalization. Identity/slug normalization stays separate;\n// complete English filler words never change Japanese/CJK or interior letters.\nconst searchText = (text: string) => text.normalize(\"NFKD\").replace(/[\\u0300-\\u036f]/g, \"\").normalize(\"NFC\").toLowerCase()\n .replace(/(^|[^\\p{L}\\p{N}\\p{M}_])(?:the|a|an)(?=$|[^\\p{L}\\p{N}\\p{M}_])/gu, \"$1\").trim().replace(/\\s+/g, \" \");\n\nconst TYPE_ICONS: Record<string, { icon: IconSvgElement; color: string }> = {\n artist: { icon: UserIcon, color: \"text-blue-500 dark:text-blue-400\" },\n series: { icon: Folder01Icon, color: \"text-yellow-600 dark:text-yellow-400\" },\n gallery: { icon: Image01Icon, color: \"text-green-600 dark:text-green-400\" },\n post: { icon: File01Icon, color: \"text-purple-500 dark:text-purple-400\" },\n blog: { icon: File01Icon, color: \"text-purple-500 dark:text-purple-400\" },\n poll: { icon: File01Icon, color: \"text-purple-500 dark:text-purple-400\" },\n character: { icon: UserIcon, color: \"text-purple-500 dark:text-purple-400\" },\n tag: { icon: TagsIcon, color: \"text-orange-500 dark:text-orange-400\" },\n};\n\nfunction useHeaderAnchor(open: boolean, enabled: boolean, root: RefObject<HTMLDivElement | null>, input: RefObject<HTMLInputElement | null>) {\n const [position, setPosition] = useState({ top: 0, left: 0, width: 0 });\n useLayoutEffect(() => {\n if (!open || !enabled || !root.current) return;\n const anchor = input.current ?? root.current;\n const view = root.current.ownerDocument.defaultView;\n if (!view) return;\n const measure = () => {\n const rect = anchor.getBoundingClientRect();\n setPosition({ top: Math.max(0, Math.min(view.innerHeight, rect.bottom + 4)), left: rect.left, width: rect.width });\n };\n measure();\n const observer = typeof ResizeObserver === \"undefined\" ? undefined : new ResizeObserver(measure);\n observer?.observe(anchor);\n view.addEventListener(\"resize\", measure);\n view.addEventListener(\"scroll\", measure, true);\n view.visualViewport?.addEventListener(\"resize\", measure);\n view.visualViewport?.addEventListener(\"scroll\", measure);\n return () => {\n observer?.disconnect();\n view.removeEventListener(\"resize\", measure);\n view.removeEventListener(\"scroll\", measure, true);\n view.visualViewport?.removeEventListener(\"resize\", measure);\n view.visualViewport?.removeEventListener(\"scroll\", measure);\n };\n }, [open, enabled, root, input]);\n return position;\n}\n\nfunction SearchTypeIcon({ kind }: { kind: string }) {\n const type = TYPE_ICONS[kind];\n return <HugeiconsIcon icon={type?.icon ?? Search01Icon} strokeWidth={2} className={cn(\"size-4 shrink-0\", type?.color ?? \"text-muted-foreground\")} data-search-kind={kind} aria-hidden />;\n}\n\n/**\n * Matching recent queries first, followed by entity/title substrings. Prefixes\n * are preferred. Enter searches or opens the chosen suggestion; full search can\n * match contextual fields more deeply.\n */\nexport function SearchBox(p: SearchBoxProps) {\n const { t, error } = useMessages();\n const id = useId();\n const navigate = useContext(ContentKitContext)?.navigate;\n const [text, setText] = useState(p.defaultValue ?? \"\");\n const [open, setOpen] = useState(false);\n // null requests the first result when opening an as-yet-unloaded panel.\n const [active, setActive] = useState<string | null>();\n const root = useRef<HTMLDivElement>(null);\n const panel = useRef<HTMLDivElement>(null);\n const input = useRef<HTMLInputElement>(null);\n const header = p.presentation === \"header\";\n const anchor = useHeaderAnchor(open, header, root, input);\n const { top } = anchor;\n const empty = !text.trim();\n const found = useTypeahead(text, { kind: p.kind, entity_kinds: p.groupKinds, client: p.client, request: p.requestSuggestions, minLength: p.disabled ? Infinity : 1 });\n const recent = useRecentSearches({ enabled: p.recent !== false && open && !p.disabled, client: p.client });\n const normalized = searchText(text);\n const current = found.query === text.trim();\n const visible = useMemo<Suggestion[]>(\n () => [\n ...(p.recent === false ? [] : recent.searches\n .map((r) => ({ ...r, normalized: searchText(r.query) }))\n .filter((r) => empty || (normalized && r.normalized.includes(normalized)))\n .sort((a, b) => Number(b.normalized.startsWith(normalized)) - Number(a.normalized.startsWith(normalized)))\n .map((r) => ({ type: \"recent\" as const, query: r.query }))),\n ...(!empty && current ? [\n ...found.entities.map((group) => ({ type: \"group\" as const, group })),\n ...found.contents.map((card) => ({ type: \"content\" as const, card })),\n ] : []),\n ],\n [p.recent, normalized, empty, current, recent.searches, found.contents, found.entities],\n );\n // History and live suggestions can arrive independently. Track identity so\n // prepending history never changes what Enter will open.\n const activeIndex = active === null ? Math.min(0, visible.length - 1) : visible.findIndex((s) => suggestionKey(s) === active);\n const selected = visible[activeIndex];\n const activate = (index: number) => {\n const suggestion = visible[index];\n setActive(suggestion ? suggestionKey(suggestion) : index >= 0 ? null : undefined);\n };\n const loading = recent.loading || (!empty && (!current || found.loading));\n const showRecent = empty && p.recent !== false;\n const emptyMessage = empty ? !header && showRecent && !recent.error ? \"contents.noRecentSearches\" : \"contents.searchHint\" : \"contents.noResults\";\n useEffect(() => {\n if (!open) return;\n const outside = (event: PointerEvent) => {\n if (!root.current?.contains(event.target as Node) && !panel.current?.contains(event.target as Node)) setOpen(false);\n };\n document.addEventListener(\"pointerdown\", outside);\n return () => document.removeEventListener(\"pointerdown\", outside);\n }, [open]);\n const reveal = () => {\n if (!p.disabled) setOpen(true);\n };\n const search = (q: string) => {\n setOpen(false);\n if (q) p.onSearch(q);\n };\n const go = (s: Suggestion) => {\n if (s.type === \"recent\") {\n setText(s.query);\n return search(s.query);\n }\n const url = p.href ? p.href(s.type === \"content\" ? s.card : s.group) : s.type === \"content\" ? s.card.path : s.group.path;\n setOpen(false);\n if (!url) return;\n if (navigate) navigate(url);\n else location.assign(url);\n };\n const panelKeyDown = (event: KeyboardEvent) => {\n if (event.key !== \"Escape\") return;\n event.preventDefault();\n input.current?.focus();\n setOpen(false);\n setActive(undefined);\n };\n const results = (<>\n {showRecent && (\n <div className={cn(\"flex items-center justify-between gap-2 text-muted-foreground\", header ? \"mb-4 text-base font-semibold\" : \"px-2 py-1 text-xs\")}>\n <span id={`${id}-recent`} className=\"flex items-center gap-2\">{header && <HugeiconsIcon icon={Clock01Icon} strokeWidth={2} className=\"size-5\" aria-hidden />}{t(\"contents.recentSearches\")}</span>\n {visible.length > 0 && <button\n type=\"button\"\n className=\"rounded px-1 hover:text-foreground\"\n onMouseDown={(e) => {\n e.preventDefault();\n }}\n onClick={() => void recent.clear()}\n >\n {t(header ? \"contents.clearAllRecent\" : \"contents.clearRecent\")}\n </button>}\n </div>\n )}\n <ul id={`${id}-list`} role=\"listbox\" aria-label={empty ? t(\"contents.recentSearches\") : t(\"contents.suggestions\")} className={cn(\"grid\", header && \"gap-2\")}>\n {visible.map((s, i) => (\n <li\n key={suggestionKey(s)}\n id={`${id}-${i}`}\n role=\"option\"\n aria-label={header && s.type !== \"recent\" ? `${labelOf(p.labels, s.type === \"content\" ? s.card.kind : s.group.kind)} ${s.type === \"content\" ? s.card.title || s.card.code : s.group.name || s.group.slug}` : undefined}\n aria-selected={i === activeIndex}\n data-ckui=\"suggestion\"\n data-type={s.type}\n className={cn(\"flex cursor-pointer items-center\", header ? s.type === \"recent\" ? \"gap-2 rounded-md p-3 text-base hover:bg-foreground/10\" : \"gap-3 rounded-md px-4 py-1 text-base font-medium hover:bg-foreground/10\" : \"gap-2 px-2 py-1.5\", i === activeIndex && \"bg-accent text-accent-foreground\")}\n onMouseEnter={() => setActive(suggestionKey(s))}\n onMouseDown={(e) => {\n e.preventDefault();\n go(s);\n }}\n >\n {header && s.type !== \"recent\" ? (\n <>\n <SearchTypeIcon kind={s.type === \"content\" ? s.card.kind : s.group.kind} />\n <span className=\"sr-only\">{labelOf(p.labels, s.type === \"content\" ? s.card.kind : s.group.kind)}</span>\n <span className=\"min-w-0 flex-1 truncate\">{s.type === \"content\" ? s.card.title || s.card.code : s.group.name || s.group.slug}</span>\n </>\n ) : s.type === \"content\" ? (\n <>\n <span className=\"h-10 w-8 shrink-0 overflow-hidden rounded bg-muted\">\n <RenditionImg outputs={s.card.cover} alt=\"\" fit=\"cover\" className=\"size-full\" />\n </span>\n <span className=\"min-w-0 truncate\">{s.card.title || s.card.code}</span>\n </>\n ) : s.type === \"group\" ? (\n <>\n <span className=\"min-w-0 flex-1 truncate\">{s.group.name || s.group.slug}</span>\n <span className=\"text-xs text-muted-foreground\">{labelOf(p.labels, s.group.kind)}</span>\n </>\n ) : (\n <>\n <HugeiconsIcon icon={header ? Search01Icon : Clock01Icon} strokeWidth={2} className=\"size-4 shrink-0 text-muted-foreground\" aria-hidden />\n <span className=\"min-w-0 flex-1 truncate\">{s.query}</span>\n <button\n type=\"button\"\n className=\"rounded p-0.5 text-muted-foreground hover:text-foreground\"\n aria-label={t(\"contents.removeRecent\", { q: s.query })}\n onMouseDown={(e) => {\n e.preventDefault();\n e.stopPropagation();\n }}\n onClick={() => void recent.clear(s.query)}\n >\n <HugeiconsIcon icon={Cancel01Icon} strokeWidth={2} className=\"size-3.5\" aria-hidden />\n </button>\n </>\n )}\n </li>\n ))}\n </ul>\n {loading ? <div role=\"status\" className={cn(\"text-muted-foreground\", header ? \"flex justify-center py-4\" : \"px-3 py-2\")}>{header ? <><span className=\"size-6 animate-spin rounded-full border-2 border-muted border-t-foreground motion-reduce:animate-none\" aria-hidden /><span className=\"sr-only\">{t(\"contents.loading\")}</span></> : t(\"contents.loading\")}</div>\n : !empty && found.error ? <p role=\"alert\" className={cn(\"px-3 py-2 text-destructive\", header && \"text-center\")}>{error(found.error)}</p>\n : visible.length === 0 && <div role=\"status\" className={cn(\"text-muted-foreground\", header ? \"flex flex-col items-center gap-2 py-8 text-center\" : \"px-3 py-2\")}>{header && <HugeiconsIcon icon={Search01Icon} strokeWidth={2} className=\"size-8\" aria-hidden />}<p>{t(emptyMessage, { q: text.trim() })}</p></div>}\n </>);\n return (\n <ContentKitUiRoot ref={root} appearance={p.appearance} className={cn(\"relative text-sm\", p.className)} data-ckui=\"search-box\" onBlur={(event) => { if (!event.currentTarget.contains(event.relatedTarget) && !panel.current?.contains(event.relatedTarget)) setOpen(false); }}>\n <form\n role=\"search\"\n className=\"relative\"\n onSubmit={(e) => {\n e.preventDefault();\n if (p.disabled) return;\n if (open && selected) return go(selected);\n if (p.selectionOnly) return;\n search(text.trim());\n }}\n >\n {header ? <button type=\"submit\" disabled={p.disabled} className=\"absolute top-1/2 left-2.5 -translate-y-1/2 rounded text-muted-foreground hover:text-foreground focus-visible:outline-2 focus-visible:outline-ring\" aria-label={t(\"contents.search\")}><HugeiconsIcon icon={Search01Icon} strokeWidth={2} className=\"size-4\" aria-hidden /></button>\n : <HugeiconsIcon icon={Search01Icon} strokeWidth={2} className=\"pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2 text-muted-foreground\" aria-hidden />}\n <Input\n ref={input}\n type={header ? \"text\" : \"search\"}\n role=\"combobox\"\n aria-label={t(\"contents.search\")}\n aria-expanded={open}\n aria-controls={`${id}-list`}\n aria-activedescendant={open && selected ? `${id}-${activeIndex}` : undefined}\n aria-autocomplete=\"list\"\n autoComplete=\"off\"\n disabled={p.disabled}\n className={cn(\"h-9 ps-8\", header && \"rounded-full border-border/50 bg-foreground/10 pe-9 text-base shadow-none md:text-base\")}\n value={text}\n placeholder={p.placeholder ?? t(\"contents.searchPlaceholder\")}\n onChange={(e) => {\n setText(e.target.value);\n setOpen(true);\n setActive(undefined);\n }}\n onFocus={reveal}\n onClick={reveal}\n onKeyDown={(e) => {\n if (e.key === \"Escape\") {\n e.preventDefault();\n setOpen(false);\n setActive(undefined);\n return;\n }\n if (e.key === \"ArrowDown\") {\n e.preventDefault();\n reveal();\n activate(open ? Math.min(visible.length - 1, activeIndex + 1) : 0);\n } else if (e.key === \"ArrowUp\" && open) {\n e.preventDefault();\n activate(Math.max(-1, activeIndex - 1));\n }\n }}\n />\n {header && (text || open) && (\n <button\n type=\"button\"\n disabled={p.disabled}\n className=\"absolute top-1/2 right-2.5 -translate-y-1/2 rounded p-0.5 text-muted-foreground hover:text-foreground focus-visible:outline-2 focus-visible:outline-ring\"\n aria-label={text ? t(\"contents.clearSearch\") : t(\"common.close\")}\n onMouseDown={(event) => event.preventDefault()}\n onClick={() => {\n input.current?.focus();\n setActive(undefined);\n if (text) { setText(\"\"); setOpen(true); }\n else setOpen(false);\n }}\n ><HugeiconsIcon icon={Cancel01Icon} strokeWidth={2} className=\"size-4\" aria-hidden /></button>\n )}\n </form>\n {open && (header ? createPortal(\n <ContentKitUiRoot appearance={p.appearance} className=\"fixed inset-x-0 bottom-0 z-[70] text-sm\" style={{ top }} data-ckui=\"search-layer\">\n <div className=\"absolute inset-0 bg-black/30\" data-ckui=\"search-backdrop\" aria-hidden />\n <div ref={panel} style={{ width: anchor.width, marginLeft: anchor.left }} className=\"relative origin-top animate-[search-expand_300ms_ease-out] rounded-lg border border-border bg-background text-foreground shadow-lg transition-none motion-reduce:animate-none\" data-ckui=\"search-panel\" onKeyDown={panelKeyDown}>\n <div className=\"w-full px-3 pt-3\">\n <div className=\"[--search-max-height:70vh] overflow-y-auto pb-7 md:[--search-max-height:60vh]\" style={{ maxHeight: `min(var(--search-max-height), calc(100dvh - ${top}px - 1.875rem))` }}>\n <div>{results}</div>\n </div>\n </div>\n </div>\n </ContentKitUiRoot>, document.body,\n ) : <div ref={panel} className=\"absolute top-full z-20 mt-1 grid max-h-96 w-full overflow-auto rounded-md border border-border bg-popover py-1 text-popover-foreground shadow-md\" data-ckui=\"search-panel\" onKeyDown={panelKeyDown}>{results}</div>)}\n\n </ContentKitUiRoot>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { MouseEvent } from 'react';
|
|
2
|
+
import { Card } from '../../client/generated/wire.js';
|
|
3
|
+
import { CardListProps } from './grid.js';
|
|
4
|
+
/** One cover-sized control shared by complete lists and daily release sections. */
|
|
5
|
+
export declare function ContentSummaryTile({ tileKey, kind, contentKind, preview, open, label, onClick, cardProps: p, }: {
|
|
6
|
+
tileKey: string;
|
|
7
|
+
kind: "premium" | "filtered";
|
|
8
|
+
contentKind?: string;
|
|
9
|
+
preview?: Card;
|
|
10
|
+
open: boolean;
|
|
11
|
+
label: string;
|
|
12
|
+
onClick: (event: MouseEvent<HTMLButtonElement>) => void;
|
|
13
|
+
cardProps: CardListProps;
|
|
14
|
+
}): import('react').JSX.Element;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { RenditionImg as e } from "../rendition-img.js";
|
|
2
|
+
import { ratio as t } from "../../client/aspect.js";
|
|
3
|
+
import { useMessages as n } from "../../i18n/context.js";
|
|
4
|
+
import { usePresets as r } from "../../react/public.js";
|
|
5
|
+
import { Button as i } from "../../ui/button.js";
|
|
6
|
+
import { useNearViewport as a } from "../../react/viewport.js";
|
|
7
|
+
import { jsx as o, jsxs as s } from "react/jsx-runtime";
|
|
8
|
+
import { LockIcon as c, Settings01Icon as l } from "@hugeicons/core-free-icons";
|
|
9
|
+
import { HugeiconsIcon as u } from "@hugeicons/react";
|
|
10
|
+
//#region src/components/content/summary-tile.tsx
|
|
11
|
+
function d({ tileKey: d, kind: f, contentKind: p, preview: m, open: h, label: g, onClick: _, cardProps: v }) {
|
|
12
|
+
let { t: y } = n(), [b, x] = a({
|
|
13
|
+
margin: "0px",
|
|
14
|
+
leaveMargin: "1200px 0px"
|
|
15
|
+
}), { presets: S } = r({ client: v.client }), C = v.aspectRatio ?? t(S.find((e) => e.kind === p && e.name === "cover")?.aspect) ?? (v.shape === "landscape" ? "16 / 9" : "3 / 4");
|
|
16
|
+
return /* @__PURE__ */ s("li", {
|
|
17
|
+
ref: b,
|
|
18
|
+
"data-release-key": d,
|
|
19
|
+
children: [/* @__PURE__ */ s(i, {
|
|
20
|
+
type: "button",
|
|
21
|
+
variant: "outline",
|
|
22
|
+
"data-ckui": `${f}-release-tile`,
|
|
23
|
+
"aria-label": g,
|
|
24
|
+
"aria-expanded": f !== "premium" && void 0,
|
|
25
|
+
"aria-haspopup": f === "filtered" ? "dialog" : void 0,
|
|
26
|
+
className: "relative flex h-auto w-full flex-col justify-center overflow-hidden rounded-lg p-0 text-center whitespace-normal",
|
|
27
|
+
style: { aspectRatio: C },
|
|
28
|
+
onClick: _,
|
|
29
|
+
children: [m && h && x && /* @__PURE__ */ o(e, {
|
|
30
|
+
outputs: m.cover,
|
|
31
|
+
alt: "",
|
|
32
|
+
fit: "cover",
|
|
33
|
+
loading: "lazy",
|
|
34
|
+
className: "absolute inset-0 size-full opacity-20"
|
|
35
|
+
}), /* @__PURE__ */ s("span", {
|
|
36
|
+
className: "relative grid justify-items-center gap-3 p-4",
|
|
37
|
+
children: [
|
|
38
|
+
/* @__PURE__ */ o(u, {
|
|
39
|
+
icon: f === "premium" ? c : l,
|
|
40
|
+
strokeWidth: 2,
|
|
41
|
+
className: "size-8",
|
|
42
|
+
"aria-hidden": !0
|
|
43
|
+
}),
|
|
44
|
+
/* @__PURE__ */ o("span", {
|
|
45
|
+
className: "text-xl font-semibold",
|
|
46
|
+
children: g
|
|
47
|
+
}),
|
|
48
|
+
/* @__PURE__ */ o("span", {
|
|
49
|
+
className: "text-sm text-muted-foreground",
|
|
50
|
+
children: y(f === "premium" ? "releases.premiumHint" : "releases.manageFilters")
|
|
51
|
+
})
|
|
52
|
+
]
|
|
53
|
+
})]
|
|
54
|
+
}), m && /* @__PURE__ */ o("p", {
|
|
55
|
+
className: "mt-2 line-clamp-2 text-lg leading-snug text-muted-foreground",
|
|
56
|
+
children: m.title
|
|
57
|
+
})]
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
//#endregion
|
|
61
|
+
export { d as ContentSummaryTile };
|
|
62
|
+
|
|
63
|
+
//# sourceMappingURL=summary-tile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"summary-tile.js","names":[],"sources":["../../../src/components/content/summary-tile.tsx"],"sourcesContent":["import { LockIcon, Settings01Icon } from \"@hugeicons/core-free-icons\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport type { CSSProperties, MouseEvent } from \"react\";\nimport { ratio } from \"../../client/aspect.js\";\nimport type { Card } from \"../../client/generated/wire.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { usePresets } from \"../../react/public.js\";\nimport { useNearViewport } from \"../../react/viewport.js\";\nimport { Button } from \"#ckui/ui/button\";\nimport { RenditionImg } from \"../rendition-img.js\";\nimport type { CardListProps } from \"./grid.js\";\n\n/** One cover-sized control shared by complete lists and daily release sections. */\nexport function ContentSummaryTile({\n tileKey,\n kind,\n contentKind,\n preview,\n open,\n label,\n onClick,\n cardProps: p,\n}: {\n tileKey: string;\n kind: \"premium\" | \"filtered\";\n contentKind?: string;\n preview?: Card;\n open: boolean;\n label: string;\n onClick: (event: MouseEvent<HTMLButtonElement>) => void;\n cardProps: CardListProps;\n}) {\n const { t } = useMessages();\n const [ref, near] = useNearViewport({ margin: \"0px\", leaveMargin: \"1200px 0px\" });\n const { presets } = usePresets({ client: p.client });\n const aspect: CSSProperties[\"aspectRatio\"] =\n p.aspectRatio ??\n ratio(presets.find((preset) => preset.kind === contentKind && preset.name === \"cover\")?.aspect) ??\n (p.shape === \"landscape\" ? \"16 / 9\" : \"3 / 4\");\n return (\n <li ref={ref} data-release-key={tileKey}>\n <Button\n type=\"button\"\n variant=\"outline\"\n data-ckui={`${kind}-release-tile`}\n aria-label={label}\n aria-expanded={kind === \"premium\" ? false : undefined}\n aria-haspopup={kind === \"filtered\" ? \"dialog\" : undefined}\n className=\"relative flex h-auto w-full flex-col justify-center overflow-hidden rounded-lg p-0 text-center whitespace-normal\"\n style={{ aspectRatio: aspect }}\n onClick={onClick}\n >\n {preview && open && near && (\n <RenditionImg outputs={preview.cover} alt=\"\" fit=\"cover\" loading=\"lazy\" className=\"absolute inset-0 size-full opacity-20\" />\n )}\n <span className=\"relative grid justify-items-center gap-3 p-4\">\n <HugeiconsIcon icon={kind === \"premium\" ? LockIcon : Settings01Icon} strokeWidth={2} className=\"size-8\" aria-hidden />\n <span className=\"text-xl font-semibold\">{label}</span>\n <span className=\"text-sm text-muted-foreground\">{t(kind === \"premium\" ? \"releases.premiumHint\" : \"releases.manageFilters\")}</span>\n </span>\n </Button>\n {preview && <p className=\"mt-2 line-clamp-2 text-lg leading-snug text-muted-foreground\">{preview.title}</p>}\n </li>\n );\n}\n"],"mappings":";;;;;;;;;;AAaA,SAAgB,EAAmB,EACjC,YACA,SACA,gBACA,YACA,SACA,UACA,YACA,WAAW,KAUV;CACD,IAAM,EAAE,SAAM,EAAY,GACpB,CAAC,GAAK,KAAQ,EAAgB;EAAE,QAAQ;EAAO,aAAa;CAAa,CAAC,GAC1E,EAAE,eAAY,EAAW,EAAE,QAAQ,EAAE,OAAO,CAAC,GAC7C,IACJ,EAAE,eACF,EAAM,EAAQ,MAAM,MAAW,EAAO,SAAS,KAAe,EAAO,SAAS,OAAO,CAAC,EAAE,MAAM,MAC7F,EAAE,UAAU,cAAc,WAAW;CACxC,OACE,kBAAC,MAAD;EAAS;EAAK,oBAAkB;EAAhC,UAAA,CACE,kBAAC,GAAD;GACE,MAAK;GACL,SAAQ;GACR,aAAW,GAAG,EAAK;GACnB,cAAY;GACZ,iBAAe,MAAS,aAAoB,KAAA;GAC5C,iBAAe,MAAS,aAAa,WAAW,KAAA;GAChD,WAAU;GACV,OAAO,EAAE,aAAa,EAAO;GACpB;GATX,UAAA,CAWG,KAAW,KAAQ,KAClB,kBAAC,GAAD;IAAc,SAAS,EAAQ;IAAO,KAAI;IAAG,KAAI;IAAQ,SAAQ;IAAO,WAAU;GAAyC,CAAA,GAE7H,kBAAC,QAAD;IAAM,WAAU;IAAhB,UAAA;KACE,kBAAC,GAAD;MAAe,MAAM,MAAS,YAAY,IAAW;MAAgB,aAAa;MAAG,WAAU;MAAS,eAAA;KAAa,CAAA;KACrH,kBAAC,QAAD;MAAM,WAAU;MAAyB,UAAA;KAAY,CAAA;KACrD,kBAAC,QAAD;MAAM,WAAU;MAAiC,UAAA,EAAE,MAAS,YAAY,yBAAyB,wBAAwB;KAAQ,CAAA;IAC7H;GACA,CAAA,CAAA;EACP,CAAA,GAAA,KAAW,kBAAC,KAAD;GAAG,WAAU;GAAgE,UAAA,EAAQ;EAAS,CAAA,CACxG;;AAER"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
2
|
import { ContentKitUiAppearance } from '../../appearance.js';
|
|
3
3
|
import { ContentKitClient } from '../../client/client.js';
|
|
4
|
-
import { ContentMemberships, GroupDetail, Membership } from '../../client/generated/wire.js';
|
|
4
|
+
import { ContentMemberships, GroupCard, GroupDetail, GroupKind, Membership } from '../../client/generated/wire.js';
|
|
5
5
|
import { ContentKitErrorHandler } from '../../react/context.js';
|
|
6
6
|
import { Labels } from './parts.js';
|
|
7
7
|
/** A version a membership may target. */
|
|
@@ -36,6 +36,16 @@ export interface MembershipEditorProps {
|
|
|
36
36
|
* content for the kinds shown.
|
|
37
37
|
*/
|
|
38
38
|
export declare function MembershipEditor(p: MembershipEditorProps): import('react').JSX.Element;
|
|
39
|
+
export declare function MembershipRow({ m: x, card, spec, labels, versions, versionLabel, onChange, onRemove, }: {
|
|
40
|
+
m: Membership;
|
|
41
|
+
card?: GroupCard;
|
|
42
|
+
spec: GroupKind;
|
|
43
|
+
labels?: Labels;
|
|
44
|
+
versions?: readonly VersionChoice[];
|
|
45
|
+
versionLabel: (id: string) => string;
|
|
46
|
+
onChange: (patch: Partial<Membership>) => void;
|
|
47
|
+
onRemove: () => void;
|
|
48
|
+
}): import('react').JSX.Element;
|
|
39
49
|
export interface GroupMembersProps {
|
|
40
50
|
group: Pick<GroupDetail, "id" | "kind" | "name" | "slug" | "cover">;
|
|
41
51
|
labels?: Labels;
|
|
@@ -529,6 +529,6 @@ function H({ spec: t, labels: n, onAdd: r }) {
|
|
|
529
529
|
});
|
|
530
530
|
}
|
|
531
531
|
//#endregion
|
|
532
|
-
export { B as GroupMembers, I as MembershipEditor };
|
|
532
|
+
export { B as GroupMembers, I as MembershipEditor, L as MembershipRow };
|
|
533
533
|
|
|
534
534
|
//# sourceMappingURL=memberships.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memberships.js","names":[],"sources":["../../../src/components/groups/memberships.tsx"],"sourcesContent":["import { cn } from \"cn\";\nimport { useEffect, useId, useMemo, useRef, useState, type ReactNode } from \"react\";\nimport type { ContentKitUiAppearance } from \"../../appearance.js\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport { fromLocalInput, toLocalInput } from \"../../client/group-fields.js\";\nimport type { ContentMemberships, GroupCard, GroupDetail, GroupKind, Membership } from \"../../client/generated/wire.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport type { ContentKitErrorHandler } from \"../../react/context.js\";\nimport { useContentGroups, useGroupMembers, useKinds, type Member } from \"../../react/groups.js\";\nimport { ContentKitUiRoot } from \"../../scope.js\";\nimport { Badge } from \"#ckui/ui/badge\";\nimport { Button } from \"#ckui/ui/button\";\nimport { Input } from \"#ckui/ui/input\";\nimport { Label } from \"#ckui/ui/label\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"#ckui/ui/select\";\nimport { Skeleton } from \"#ckui/ui/skeleton\";\nimport { SortableList } from \"../sortable-list.js\";\nimport { contentError, ErrorLine, formatDate, useAction } from \"../social/parts.js\";\nimport { GroupPicker, labelOf, type Labels } from \"./parts.js\";\n\nconst NONE = \"__none__\";\n\n/** A version a membership may target. */\nexport interface VersionChoice {\n id: string;\n label: string;\n}\n\nexport interface MembershipEditorProps {\n /** The content whose memberships are edited, its versions' included. */\n content: string;\n /** Its kind: the group kinds it joins. */\n contentKind: string;\n /** Staff lookup, including internal classifications. */\n management?: boolean;\n /** Its versions, for version-level memberships (voice actors, version-only tags). */\n versions?: readonly VersionChoice[];\n /** Only these group kinds; default every kind its kind joins. */\n groupKinds?: readonly string[];\n labels?: Labels;\n /** Group names in this language; default the provider's. */\n language?: string;\n onSaved?: (saved: ContentMemberships) => void;\n client?: ContentKitClient;\n onError?: ContentKitErrorHandler;\n className?: string;\n appearance?: ContentKitUiAppearance;\n}\n\ntype Draft = Membership & { key: number };\n\nconst strip = ({ key: _key, ...m }: Draft): Membership => m;\nconst same = (a: Membership[], b: Membership[]) => JSON.stringify(a) === JSON.stringify(b);\n\n/**\n * Staff: a content's groups, per group kind: add and remove groups, and per\n * membership the version it targets, its role, its position in an ordered\n * group, its window and acceptance of a proposed one. Saved in one write per\n * content for the kinds shown.\n */\nexport function MembershipEditor(p: MembershipEditorProps) {\n const m = useMessages();\n const { t } = m;\n const { kinds, loading: kindsLoading } = useKinds({ client: p.client });\n const cg = useContentGroups(p.content, { language: p.language, client: p.client });\n const groupKinds = useMemo(() => p.groupKinds ?? kinds?.kinds[p.contentKind]?.entities ?? [], [p.groupKinds, kinds, p.contentKind]);\n const [picked, setPicked] = useState<ReadonlyMap<string, GroupCard>>(new Map());\n const cards = useMemo(() => new Map([...cg.entities, ...picked]), [cg.entities, picked]);\n const seq = useRef(0);\n const initial = useMemo(\n () => cg.memberships.filter((x) => groupKinds.includes(cg.entities.get(x.group_id ?? \"\")?.kind ?? \"\")).map((x) => ({ ...x, content_id: undefined })),\n [cg.memberships, cg.entities, groupKinds],\n );\n const [draft, setDraft] = useState<Draft[]>([]);\n const dirty = !same(draft.map(strip), initial);\n const dirtyRef = useRef(dirty);\n dirtyRef.current = dirty;\n useEffect(() => {\n if (!dirtyRef.current || !draft.length) setDraft(initial.map((x) => ({ ...x, key: ++seq.current })));\n // Server data replaces an untouched draft only.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [initial]);\n const save = useAction(\"membership.save\", p.onError);\n const [saved, setSaved] = useState(false);\n const change = (key: number, patch: Partial<Membership>) => {\n setSaved(false);\n setDraft((d) => d.map((x) => (x.key === key ? { ...x, ...patch } : x)));\n };\n const root = (children: ReactNode) => (\n <ContentKitUiRoot appearance={p.appearance} className={cn(\"grid gap-4 text-sm\", p.className)} data-ckui=\"membership-editor\">\n {children}\n </ContentKitUiRoot>\n );\n if (kindsLoading || (cg.loading && !cg.memberships.length)) return root(<Skeleton className=\"h-32 w-full\" />);\n if (cg.error && !cg.memberships.length)\n return root(\n <div role=\"alert\" className=\"grid justify-items-start gap-2\">\n <p className=\"text-destructive\">{contentError(m, cg.error)}</p>\n <Button size=\"sm\" variant=\"outline\" onClick={cg.reload}>\n {t(\"common.retry\")}\n </Button>\n </div>,\n );\n const versionLabel = (id: string) => p.versions?.find((v) => v.id === id)?.label ?? id.slice(0, 8);\n return root(\n <form\n className=\"grid gap-5\"\n onSubmit={(e) => {\n e.preventDefault();\n setSaved(false);\n void save.run(() => cg.set(draft.map(strip), [...groupKinds])).then((out) => {\n if (!out) return;\n setSaved(true);\n setDraft(out.memberships.filter((x) => groupKinds.includes(out.entities.find((g) => g.id === x.group_id)?.kind ?? \"\")).map((x) => ({ ...x, content_id: undefined, key: ++seq.current })));\n p.onSaved?.(out);\n });\n }}\n >\n {groupKinds.map((kind) => {\n const spec: GroupKind = kinds?.entity_kinds?.[kind] ?? {};\n const rows = draft.filter((x) => cards.get(x.group_id ?? \"\")?.kind === kind);\n const label = labelOf(p.labels, kind);\n return (\n <fieldset key={kind} className=\"grid gap-2\" data-ckui=\"membership-kind\" data-group-kind={kind}>\n <legend className=\"mb-1 font-medium\">{label}</legend>\n {rows.length > 0 && (\n <ul className=\"grid gap-2\">\n {rows.map((x) => (\n <MembershipRow\n key={x.key}\n m={x}\n card={cards.get(x.group_id ?? \"\")}\n spec={spec}\n labels={p.labels}\n versions={p.versions}\n versionLabel={versionLabel}\n onChange={(patch) => change(x.key, patch)}\n onRemove={() => {\n setSaved(false);\n setDraft((d) => d.filter((y) => y.key !== x.key));\n }}\n />\n ))}\n </ul>\n )}\n <GroupPicker\n management={p.management}\n kind={kind}\n language={p.language}\n label={t(\"memberships.add\", { kind: label })}\n exclude={rows.filter((x) => !x.version_id && !x.role).map((x) => x.group_id!)}\n client={p.client}\n onPick={(g) => {\n setSaved(false);\n setPicked((all) => new Map(all).set(g.id, g));\n setDraft((d) => [...d, { group_id: g.id, key: ++seq.current }]);\n }}\n />\n </fieldset>\n );\n })}\n {!groupKinds.length && <p className=\"text-muted-foreground\">{t(\"memberships.noKinds\")}</p>}\n <ErrorLine error={save.error} />\n <div className=\"flex items-center gap-3\">\n <Button type=\"submit\" size=\"sm\" disabled={save.pending || !dirty}>\n {save.pending ? t(\"common.saving\") : t(\"common.save\")}\n </Button>\n {dirty && (\n <Button type=\"button\" size=\"sm\" variant=\"ghost\" disabled={save.pending} onClick={() => setDraft(initial.map((x) => ({ ...x, key: ++seq.current })))}>\n {t(\"memberships.discard\")}\n </Button>\n )}\n {saved && !dirty && (\n <span role=\"status\" className=\"text-muted-foreground\">\n {t(\"memberships.saved\")}\n </span>\n )}\n </div>\n </form>,\n );\n}\n\nfunction MembershipRow({\n m: x,\n card,\n spec,\n labels,\n versions,\n versionLabel,\n onChange,\n onRemove,\n}: {\n m: Membership;\n card?: GroupCard;\n spec: GroupKind;\n labels?: Labels;\n versions?: readonly VersionChoice[];\n versionLabel: (id: string) => string;\n onChange: (patch: Partial<Membership>) => void;\n onRemove: () => void;\n}) {\n const { t } = useMessages();\n const id = useId();\n const name = card ? card.name || card.slug : (x.group_id ?? \"\");\n const versionIds = [...new Set([...(versions ?? []).map((v) => v.id), ...(x.version_id ? [x.version_id] : [])])];\n return (\n <li className=\"flex flex-wrap items-center gap-2 rounded-md border border-border p-2\" data-ckui=\"membership\" data-group={card?.slug} data-state={x.state ?? \"active\"}>\n <span className=\"min-w-0 flex-1 font-medium break-words\">{name}</span>\n {x.state === \"proposed\" && (\n <>\n <Badge variant=\"secondary\">{t(\"memberships.proposed\")}</Badge>\n <Button type=\"button\" size=\"xs\" variant=\"outline\" onClick={() => onChange({ state: \"active\" })}>\n {t(\"memberships.accept\")}\n </Button>\n </>\n )}\n {spec.version_level && versionIds.length > 0 && (\n <Select value={x.version_id ?? NONE} onValueChange={(v) => onChange({ version_id: v === NONE ? undefined : String(v) })}>\n <SelectTrigger size=\"sm\" aria-label={t(\"memberships.versionOf\", { name })}>\n <SelectValue>{(v: string) => (v === NONE ? t(\"memberships.wholeContent\") : versionLabel(v))}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={NONE}>{t(\"memberships.wholeContent\")}</SelectItem>\n {versionIds.map((v) => (\n <SelectItem key={v} value={v}>\n {versionLabel(v)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n {!!spec.roles?.length && (\n <Select value={x.role || NONE} onValueChange={(v) => onChange({ role: v === NONE ? undefined : String(v) })}>\n <SelectTrigger size=\"sm\" aria-label={t(\"memberships.roleOf\", { name })}>\n <SelectValue>{(v: string) => (v === NONE ? t(\"memberships.noRole\") : labelOf(labels, v))}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={NONE}>{t(\"memberships.noRole\")}</SelectItem>\n {spec.roles.map((r) => (\n <SelectItem key={r} value={r}>\n {labelOf(labels, r)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n {spec.ordered && (\n <div className=\"flex items-center gap-1\">\n <Label htmlFor={`${id}-pos`} className=\"text-xs text-muted-foreground\">\n {t(\"memberships.position\")}\n </Label>\n <Input\n id={`${id}-pos`}\n className=\"h-8 w-20\"\n type=\"number\"\n min={0}\n step={1}\n value={x.position ?? \"\"}\n placeholder={t(\"memberships.last\")}\n onChange={(e) => onChange({ position: e.target.value === \"\" ? undefined : Math.max(0, Math.trunc(Number(e.target.value))) })}\n />\n </div>\n )}\n {spec.windows && <WindowFields from={x.active_from} until={x.active_until} name={name} onChange={onChange} />}\n <Button type=\"button\" size=\"xs\" variant=\"ghost\" aria-label={t(\"memberships.remove\", { name })} onClick={onRemove}>\n {t(\"common.remove\")}\n </Button>\n </li>\n );\n}\n\nfunction WindowFields({ from, until, name, onChange }: { from?: string; until?: string; name: string; onChange: (patch: Partial<Membership>) => void }) {\n const { t } = useMessages();\n const id = useId();\n return (\n <div className=\"flex flex-wrap items-center gap-1\" data-ckui=\"membership-window\">\n <Label htmlFor={`${id}-from`} className=\"text-xs text-muted-foreground\">\n {t(\"memberships.from\")}\n </Label>\n <Input\n id={`${id}-from`}\n className=\"h-8 w-48\"\n type=\"datetime-local\"\n aria-label={t(\"memberships.fromOf\", { name })}\n value={toLocalInput(from)}\n onChange={(e) => onChange({ active_from: fromLocalInput(e.target.value) })}\n />\n <Label htmlFor={`${id}-until`} className=\"text-xs text-muted-foreground\">\n {t(\"memberships.until\")}\n </Label>\n <Input\n id={`${id}-until`}\n className=\"h-8 w-48\"\n type=\"datetime-local\"\n aria-label={t(\"memberships.untilOf\", { name })}\n value={toLocalInput(until)}\n onChange={(e) => onChange({ active_until: fromLocalInput(e.target.value) })}\n />\n </div>\n );\n}\n\nexport interface GroupMembersProps {\n group: Pick<GroupDetail, \"id\" | \"kind\" | \"name\" | \"slug\" | \"cover\">;\n labels?: Labels;\n /** One member, e.g. a link with its title; default its content id. */\n renderContent?: (member: Membership) => ReactNode;\n /** Select a member's cover as the group's. */\n onUseCover?: (contentId: string) => Promise<unknown>;\n pageSize?: number;\n client?: ContentKitClient;\n onError?: ContentKitErrorHandler;\n className?: string;\n appearance?: ContentKitUiAppearance;\n}\n\nconst refKey = (x: Membership) => `${x.content_id}|${x.version_id ?? \"\"}`;\n\n/**\n * Staff: a group's members in member order: add by content (and version),\n * remove, reorder an ordered group, edit windows, accept proposed members,\n * and use a member's cover as the group's.\n */\nexport function GroupMembers(p: GroupMembersProps) {\n const m = useMessages();\n const { t } = m;\n const { kinds } = useKinds({ client: p.client });\n const spec: GroupKind = kinds?.entity_kinds?.[p.group.kind] ?? {};\n const members = useGroupMembers(p.group.id, { pageSize: p.pageSize, client: p.client });\n const save = useAction(\"membership.save\", p.onError);\n const [editing, setEditing] = useState<string | null>(null);\n const [win, setWin] = useState<{ from?: string; until?: string }>({});\n const describe = (x: Membership) => (p.renderContent ? p.renderContent(x) : <span className=\"font-mono text-xs break-all\">{x.content_id}</span>);\n const spoken = (x: Member) => `${x.content_id}${x.version_id ? ` ${x.version_id}` : \"\"}`;\n // Every role of one member shares its place: order unique content and version pairs.\n const order = (items: Member[]) => [...new Map(items.map((x) => [refKey(x), { content_id: x.content_id!, version_id: x.version_id }])).values()];\n const strip = ({ id: _id, ...x }: Member): Membership => x;\n const row = (x: Member, handle?: ReactNode) => (\n <div className=\"flex flex-wrap items-center gap-2\" data-ckui=\"group-member\" data-content={x.content_id} data-state={x.state ?? \"active\"}>\n {handle}\n {spec.ordered && x.position !== undefined && <span className=\"w-8 text-xs text-muted-foreground tabular-nums\">{x.position}</span>}\n <span className=\"min-w-0 flex-1\">{describe(x)}</span>\n {x.version_id && <Badge variant=\"outline\">{t(\"memberships.version\")}</Badge>}\n {x.role && <Badge variant=\"secondary\">{labelOf(p.labels, x.role)}</Badge>}\n {x.state === \"proposed\" && (\n <>\n <Badge variant=\"secondary\">{t(\"memberships.proposed\")}</Badge>\n <Button size=\"xs\" variant=\"outline\" disabled={save.pending} onClick={() => void save.run(() => members.add([{ ...strip(x), state: \"active\" }]))}>\n {t(\"memberships.accept\")}\n </Button>\n </>\n )}\n {spec.windows &&\n (editing === x.id ? (\n <span className=\"flex flex-wrap items-center gap-1\">\n <WindowFields from={win.from} until={win.until} name={spoken(x)} onChange={(w) => setWin((cur) => ({ from: \"active_from\" in w ? w.active_from : cur.from, until: \"active_until\" in w ? w.active_until : cur.until }))} />\n <Button\n size=\"xs\"\n disabled={save.pending}\n onClick={() =>\n void save.run(() => members.add([{ ...strip(x), active_from: win.from, active_until: win.until }])).then((d) => d !== undefined && setEditing(null))\n }\n >\n {t(\"common.save\")}\n </Button>\n <Button size=\"xs\" variant=\"ghost\" onClick={() => setEditing(null)}>\n {t(\"common.cancel\")}\n </Button>\n </span>\n ) : (\n <>\n <span className=\"text-xs text-muted-foreground\" data-ckui=\"member-window\">\n {x.active_from || x.active_until\n ? t(\"memberships.window\", { from: x.active_from ? formatDate(m, x.active_from) : \"…\", until: x.active_until ? formatDate(m, x.active_until) : \"…\" })\n : t(\"memberships.always\")}\n </span>\n <Button\n size=\"xs\"\n variant=\"ghost\"\n onClick={() => {\n setWin({ from: x.active_from, until: x.active_until });\n setEditing(x.id);\n }}\n >\n {t(\"memberships.editWindow\")}\n </Button>\n </>\n ))}\n {p.onUseCover && !x.version_id && (\n <Button size=\"xs\" variant=\"ghost\" disabled={save.pending || (p.group.cover?.content_id === x.content_id && !p.group.cover?.auto)} onClick={() => void save.run(() => p.onUseCover!(x.content_id!))}>\n {t(\"memberships.useCover\")}\n </Button>\n )}\n <Button size=\"xs\" variant=\"ghost\" disabled={save.pending} aria-label={t(\"memberships.remove\", { name: spoken(x) })} onClick={() => void save.run(() => members.remove([strip(x)]))}>\n {t(\"common.remove\")}\n </Button>\n </div>\n );\n return (\n <ContentKitUiRoot appearance={p.appearance} className={cn(\"grid gap-3 text-sm\", p.className)} data-ckui=\"group-members\">\n <h3 className=\"font-medium\">{t(\"memberships.members\")}</h3>\n {members.loading && !members.items.length ? (\n <Skeleton className=\"h-20 w-full\" />\n ) : members.error && !members.items.length ? (\n <div role=\"alert\" className=\"grid justify-items-start gap-2\">\n <p className=\"text-destructive\">{contentError(m, members.error)}</p>\n <Button size=\"sm\" variant=\"outline\" onClick={members.reload}>\n {t(\"common.retry\")}\n </Button>\n </div>\n ) : !members.items.length ? (\n <p className=\"text-muted-foreground\">{t(\"memberships.noMembers\")}</p>\n ) : spec.ordered ? (\n <SortableList\n items={members.items}\n id={(x) => x.id}\n name={spoken}\n label={t(\"memberships.members\")}\n disabled={save.pending}\n onMove={(from, to) => {\n const items = [...members.items];\n const [moved] = items.splice(from, 1);\n items.splice(to, 0, moved!);\n void save.run(() => members.reorder(order(items)));\n }}\n >\n {(x, handle) => row(x, handle)}\n </SortableList>\n ) : (\n <ul className=\"grid divide-y divide-border rounded-lg border border-border\">\n {members.items.map((x) => (\n <li key={x.id} className=\"p-2\">\n {row(x)}\n </li>\n ))}\n </ul>\n )}\n {members.hasMore && (\n <div className=\"grid justify-items-center gap-2\">\n {members.error && <p role=\"alert\" className=\"text-destructive\">{contentError(m, members.error)}</p>}\n <Button variant=\"outline\" size=\"sm\" disabled={members.loadingMore} onClick={() => void members.loadMore()}>\n {members.loadingMore ? t(\"groups.loading\") : members.error ? t(\"common.retry\") : t(\"groups.loadMore\")}\n </Button>\n </div>\n )}\n <AddMember spec={spec} labels={p.labels} onAdd={(x) => save.run(() => members.add([x]))} />\n <ErrorLine error={save.error} />\n </ContentKitUiRoot>\n );\n}\n\nconst ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;\n\nfunction AddMember({ spec, labels, onAdd }: { spec: GroupKind; labels?: Labels; onAdd: (m: Membership) => Promise<unknown> }) {\n const { t } = useMessages();\n const id = useId();\n const [content, setContent] = useState(\"\");\n const [version, setVersion] = useState(\"\");\n const [role, setRole] = useState(\"\");\n const [from, setFrom] = useState<string>();\n const [until, setUntil] = useState<string>();\n const ok = ID.test(content.trim()) && (!version.trim() || ID.test(version.trim()));\n return (\n <form\n className=\"flex flex-wrap items-end gap-2\"\n data-ckui=\"add-member\"\n onSubmit={(e) => {\n e.preventDefault();\n if (!ok) return;\n void onAdd({ content_id: content.trim(), version_id: version.trim() || undefined, role: role || undefined, active_from: from, active_until: until }).then((d) => {\n if (d === undefined) return;\n setContent(\"\");\n setVersion(\"\");\n });\n }}\n >\n <div className=\"grid gap-1\">\n <Label htmlFor={`${id}-c`} className=\"text-xs text-muted-foreground\">\n {t(\"memberships.contentId\")}\n </Label>\n <Input id={`${id}-c`} className=\"h-8 w-80 max-w-full font-mono\" value={content} onChange={(e) => setContent(e.target.value)} />\n </div>\n {spec.version_level && (\n <div className=\"grid gap-1\">\n <Label htmlFor={`${id}-v`} className=\"text-xs text-muted-foreground\">\n {t(\"memberships.versionId\")}\n </Label>\n <Input id={`${id}-v`} className=\"h-8 w-80 max-w-full font-mono\" value={version} onChange={(e) => setVersion(e.target.value)} />\n </div>\n )}\n {!!spec.roles?.length && (\n <Select value={role || NONE} onValueChange={(v) => setRole(v === NONE ? \"\" : String(v))}>\n <SelectTrigger size=\"sm\" aria-label={t(\"memberships.role\")}>\n <SelectValue>{(v: string) => (v === NONE ? t(\"memberships.noRole\") : labelOf(labels, v))}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={NONE}>{t(\"memberships.noRole\")}</SelectItem>\n {spec.roles.map((r) => (\n <SelectItem key={r} value={r}>\n {labelOf(labels, r)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n {spec.windows && (\n <WindowFields\n from={from}\n until={until}\n name={t(\"memberships.newMember\")}\n onChange={(w) => {\n if (\"active_from\" in w) setFrom(w.active_from);\n if (\"active_until\" in w) setUntil(w.active_until);\n }}\n />\n )}\n <Button type=\"submit\" size=\"sm\" variant=\"outline\" disabled={!ok}>\n {t(\"memberships.addMember\")}\n </Button>\n </form>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"memberships.js","names":[],"sources":["../../../src/components/groups/memberships.tsx"],"sourcesContent":["import { cn } from \"cn\";\nimport { useEffect, useId, useMemo, useRef, useState, type ReactNode } from \"react\";\nimport type { ContentKitUiAppearance } from \"../../appearance.js\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport { fromLocalInput, toLocalInput } from \"../../client/group-fields.js\";\nimport type { ContentMemberships, GroupCard, GroupDetail, GroupKind, Membership } from \"../../client/generated/wire.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport type { ContentKitErrorHandler } from \"../../react/context.js\";\nimport { useContentGroups, useGroupMembers, useKinds, type Member } from \"../../react/groups.js\";\nimport { ContentKitUiRoot } from \"../../scope.js\";\nimport { Badge } from \"#ckui/ui/badge\";\nimport { Button } from \"#ckui/ui/button\";\nimport { Input } from \"#ckui/ui/input\";\nimport { Label } from \"#ckui/ui/label\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"#ckui/ui/select\";\nimport { Skeleton } from \"#ckui/ui/skeleton\";\nimport { SortableList } from \"../sortable-list.js\";\nimport { contentError, ErrorLine, formatDate, useAction } from \"../social/parts.js\";\nimport { GroupPicker, labelOf, type Labels } from \"./parts.js\";\n\nconst NONE = \"__none__\";\n\n/** A version a membership may target. */\nexport interface VersionChoice {\n id: string;\n label: string;\n}\n\nexport interface MembershipEditorProps {\n /** The content whose memberships are edited, its versions' included. */\n content: string;\n /** Its kind: the group kinds it joins. */\n contentKind: string;\n /** Staff lookup, including internal classifications. */\n management?: boolean;\n /** Its versions, for version-level memberships (voice actors, version-only tags). */\n versions?: readonly VersionChoice[];\n /** Only these group kinds; default every kind its kind joins. */\n groupKinds?: readonly string[];\n labels?: Labels;\n /** Group names in this language; default the provider's. */\n language?: string;\n onSaved?: (saved: ContentMemberships) => void;\n client?: ContentKitClient;\n onError?: ContentKitErrorHandler;\n className?: string;\n appearance?: ContentKitUiAppearance;\n}\n\ntype Draft = Membership & { key: number };\n\nconst strip = ({ key: _key, ...m }: Draft): Membership => m;\nconst same = (a: Membership[], b: Membership[]) => JSON.stringify(a) === JSON.stringify(b);\n\n/**\n * Staff: a content's groups, per group kind: add and remove groups, and per\n * membership the version it targets, its role, its position in an ordered\n * group, its window and acceptance of a proposed one. Saved in one write per\n * content for the kinds shown.\n */\nexport function MembershipEditor(p: MembershipEditorProps) {\n const m = useMessages();\n const { t } = m;\n const { kinds, loading: kindsLoading } = useKinds({ client: p.client });\n const cg = useContentGroups(p.content, { language: p.language, client: p.client });\n const groupKinds = useMemo(() => p.groupKinds ?? kinds?.kinds[p.contentKind]?.entities ?? [], [p.groupKinds, kinds, p.contentKind]);\n const [picked, setPicked] = useState<ReadonlyMap<string, GroupCard>>(new Map());\n const cards = useMemo(() => new Map([...cg.entities, ...picked]), [cg.entities, picked]);\n const seq = useRef(0);\n const initial = useMemo(\n () => cg.memberships.filter((x) => groupKinds.includes(cg.entities.get(x.group_id ?? \"\")?.kind ?? \"\")).map((x) => ({ ...x, content_id: undefined })),\n [cg.memberships, cg.entities, groupKinds],\n );\n const [draft, setDraft] = useState<Draft[]>([]);\n const dirty = !same(draft.map(strip), initial);\n const dirtyRef = useRef(dirty);\n dirtyRef.current = dirty;\n useEffect(() => {\n if (!dirtyRef.current || !draft.length) setDraft(initial.map((x) => ({ ...x, key: ++seq.current })));\n // Server data replaces an untouched draft only.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [initial]);\n const save = useAction(\"membership.save\", p.onError);\n const [saved, setSaved] = useState(false);\n const change = (key: number, patch: Partial<Membership>) => {\n setSaved(false);\n setDraft((d) => d.map((x) => (x.key === key ? { ...x, ...patch } : x)));\n };\n const root = (children: ReactNode) => (\n <ContentKitUiRoot appearance={p.appearance} className={cn(\"grid gap-4 text-sm\", p.className)} data-ckui=\"membership-editor\">\n {children}\n </ContentKitUiRoot>\n );\n if (kindsLoading || (cg.loading && !cg.memberships.length)) return root(<Skeleton className=\"h-32 w-full\" />);\n if (cg.error && !cg.memberships.length)\n return root(\n <div role=\"alert\" className=\"grid justify-items-start gap-2\">\n <p className=\"text-destructive\">{contentError(m, cg.error)}</p>\n <Button size=\"sm\" variant=\"outline\" onClick={cg.reload}>\n {t(\"common.retry\")}\n </Button>\n </div>,\n );\n const versionLabel = (id: string) => p.versions?.find((v) => v.id === id)?.label ?? id.slice(0, 8);\n return root(\n <form\n className=\"grid gap-5\"\n onSubmit={(e) => {\n e.preventDefault();\n setSaved(false);\n void save.run(() => cg.set(draft.map(strip), [...groupKinds])).then((out) => {\n if (!out) return;\n setSaved(true);\n setDraft(out.memberships.filter((x) => groupKinds.includes(out.entities.find((g) => g.id === x.group_id)?.kind ?? \"\")).map((x) => ({ ...x, content_id: undefined, key: ++seq.current })));\n p.onSaved?.(out);\n });\n }}\n >\n {groupKinds.map((kind) => {\n const spec: GroupKind = kinds?.entity_kinds?.[kind] ?? {};\n const rows = draft.filter((x) => cards.get(x.group_id ?? \"\")?.kind === kind);\n const label = labelOf(p.labels, kind);\n return (\n <fieldset key={kind} className=\"grid gap-2\" data-ckui=\"membership-kind\" data-group-kind={kind}>\n <legend className=\"mb-1 font-medium\">{label}</legend>\n {rows.length > 0 && (\n <ul className=\"grid gap-2\">\n {rows.map((x) => (\n <MembershipRow\n key={x.key}\n m={x}\n card={cards.get(x.group_id ?? \"\")}\n spec={spec}\n labels={p.labels}\n versions={p.versions}\n versionLabel={versionLabel}\n onChange={(patch) => change(x.key, patch)}\n onRemove={() => {\n setSaved(false);\n setDraft((d) => d.filter((y) => y.key !== x.key));\n }}\n />\n ))}\n </ul>\n )}\n <GroupPicker\n management={p.management}\n kind={kind}\n language={p.language}\n label={t(\"memberships.add\", { kind: label })}\n exclude={rows.filter((x) => !x.version_id && !x.role).map((x) => x.group_id!)}\n client={p.client}\n onPick={(g) => {\n setSaved(false);\n setPicked((all) => new Map(all).set(g.id, g));\n setDraft((d) => [...d, { group_id: g.id, key: ++seq.current }]);\n }}\n />\n </fieldset>\n );\n })}\n {!groupKinds.length && <p className=\"text-muted-foreground\">{t(\"memberships.noKinds\")}</p>}\n <ErrorLine error={save.error} />\n <div className=\"flex items-center gap-3\">\n <Button type=\"submit\" size=\"sm\" disabled={save.pending || !dirty}>\n {save.pending ? t(\"common.saving\") : t(\"common.save\")}\n </Button>\n {dirty && (\n <Button type=\"button\" size=\"sm\" variant=\"ghost\" disabled={save.pending} onClick={() => setDraft(initial.map((x) => ({ ...x, key: ++seq.current })))}>\n {t(\"memberships.discard\")}\n </Button>\n )}\n {saved && !dirty && (\n <span role=\"status\" className=\"text-muted-foreground\">\n {t(\"memberships.saved\")}\n </span>\n )}\n </div>\n </form>,\n );\n}\n\nexport function MembershipRow({\n m: x,\n card,\n spec,\n labels,\n versions,\n versionLabel,\n onChange,\n onRemove,\n}: {\n m: Membership;\n card?: GroupCard;\n spec: GroupKind;\n labels?: Labels;\n versions?: readonly VersionChoice[];\n versionLabel: (id: string) => string;\n onChange: (patch: Partial<Membership>) => void;\n onRemove: () => void;\n}) {\n const { t } = useMessages();\n const id = useId();\n const name = card ? card.name || card.slug : (x.group_id ?? \"\");\n const versionIds = [...new Set([...(versions ?? []).map((v) => v.id), ...(x.version_id ? [x.version_id] : [])])];\n return (\n <li className=\"flex flex-wrap items-center gap-2 rounded-md border border-border p-2\" data-ckui=\"membership\" data-group={card?.slug} data-state={x.state ?? \"active\"}>\n <span className=\"min-w-0 flex-1 font-medium break-words\">{name}</span>\n {x.state === \"proposed\" && (\n <>\n <Badge variant=\"secondary\">{t(\"memberships.proposed\")}</Badge>\n <Button type=\"button\" size=\"xs\" variant=\"outline\" onClick={() => onChange({ state: \"active\" })}>\n {t(\"memberships.accept\")}\n </Button>\n </>\n )}\n {spec.version_level && versionIds.length > 0 && (\n <Select value={x.version_id ?? NONE} onValueChange={(v) => onChange({ version_id: v === NONE ? undefined : String(v) })}>\n <SelectTrigger size=\"sm\" aria-label={t(\"memberships.versionOf\", { name })}>\n <SelectValue>{(v: string) => (v === NONE ? t(\"memberships.wholeContent\") : versionLabel(v))}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={NONE}>{t(\"memberships.wholeContent\")}</SelectItem>\n {versionIds.map((v) => (\n <SelectItem key={v} value={v}>\n {versionLabel(v)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n {!!spec.roles?.length && (\n <Select value={x.role || NONE} onValueChange={(v) => onChange({ role: v === NONE ? undefined : String(v) })}>\n <SelectTrigger size=\"sm\" aria-label={t(\"memberships.roleOf\", { name })}>\n <SelectValue>{(v: string) => (v === NONE ? t(\"memberships.noRole\") : labelOf(labels, v))}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={NONE}>{t(\"memberships.noRole\")}</SelectItem>\n {spec.roles.map((r) => (\n <SelectItem key={r} value={r}>\n {labelOf(labels, r)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n {spec.ordered && (\n <div className=\"flex items-center gap-1\">\n <Label htmlFor={`${id}-pos`} className=\"text-xs text-muted-foreground\">\n {t(\"memberships.position\")}\n </Label>\n <Input\n id={`${id}-pos`}\n className=\"h-8 w-20\"\n type=\"number\"\n min={0}\n step={1}\n value={x.position ?? \"\"}\n placeholder={t(\"memberships.last\")}\n onChange={(e) => onChange({ position: e.target.value === \"\" ? undefined : Math.max(0, Math.trunc(Number(e.target.value))) })}\n />\n </div>\n )}\n {spec.windows && <WindowFields from={x.active_from} until={x.active_until} name={name} onChange={onChange} />}\n <Button type=\"button\" size=\"xs\" variant=\"ghost\" aria-label={t(\"memberships.remove\", { name })} onClick={onRemove}>\n {t(\"common.remove\")}\n </Button>\n </li>\n );\n}\n\nfunction WindowFields({ from, until, name, onChange }: { from?: string; until?: string; name: string; onChange: (patch: Partial<Membership>) => void }) {\n const { t } = useMessages();\n const id = useId();\n return (\n <div className=\"flex flex-wrap items-center gap-1\" data-ckui=\"membership-window\">\n <Label htmlFor={`${id}-from`} className=\"text-xs text-muted-foreground\">\n {t(\"memberships.from\")}\n </Label>\n <Input\n id={`${id}-from`}\n className=\"h-8 w-48\"\n type=\"datetime-local\"\n aria-label={t(\"memberships.fromOf\", { name })}\n value={toLocalInput(from)}\n onChange={(e) => onChange({ active_from: fromLocalInput(e.target.value) })}\n />\n <Label htmlFor={`${id}-until`} className=\"text-xs text-muted-foreground\">\n {t(\"memberships.until\")}\n </Label>\n <Input\n id={`${id}-until`}\n className=\"h-8 w-48\"\n type=\"datetime-local\"\n aria-label={t(\"memberships.untilOf\", { name })}\n value={toLocalInput(until)}\n onChange={(e) => onChange({ active_until: fromLocalInput(e.target.value) })}\n />\n </div>\n );\n}\n\nexport interface GroupMembersProps {\n group: Pick<GroupDetail, \"id\" | \"kind\" | \"name\" | \"slug\" | \"cover\">;\n labels?: Labels;\n /** One member, e.g. a link with its title; default its content id. */\n renderContent?: (member: Membership) => ReactNode;\n /** Select a member's cover as the group's. */\n onUseCover?: (contentId: string) => Promise<unknown>;\n pageSize?: number;\n client?: ContentKitClient;\n onError?: ContentKitErrorHandler;\n className?: string;\n appearance?: ContentKitUiAppearance;\n}\n\nconst refKey = (x: Membership) => `${x.content_id}|${x.version_id ?? \"\"}`;\n\n/**\n * Staff: a group's members in member order: add by content (and version),\n * remove, reorder an ordered group, edit windows, accept proposed members,\n * and use a member's cover as the group's.\n */\nexport function GroupMembers(p: GroupMembersProps) {\n const m = useMessages();\n const { t } = m;\n const { kinds } = useKinds({ client: p.client });\n const spec: GroupKind = kinds?.entity_kinds?.[p.group.kind] ?? {};\n const members = useGroupMembers(p.group.id, { pageSize: p.pageSize, client: p.client });\n const save = useAction(\"membership.save\", p.onError);\n const [editing, setEditing] = useState<string | null>(null);\n const [win, setWin] = useState<{ from?: string; until?: string }>({});\n const describe = (x: Membership) => (p.renderContent ? p.renderContent(x) : <span className=\"font-mono text-xs break-all\">{x.content_id}</span>);\n const spoken = (x: Member) => `${x.content_id}${x.version_id ? ` ${x.version_id}` : \"\"}`;\n // Every role of one member shares its place: order unique content and version pairs.\n const order = (items: Member[]) => [...new Map(items.map((x) => [refKey(x), { content_id: x.content_id!, version_id: x.version_id }])).values()];\n const strip = ({ id: _id, ...x }: Member): Membership => x;\n const row = (x: Member, handle?: ReactNode) => (\n <div className=\"flex flex-wrap items-center gap-2\" data-ckui=\"group-member\" data-content={x.content_id} data-state={x.state ?? \"active\"}>\n {handle}\n {spec.ordered && x.position !== undefined && <span className=\"w-8 text-xs text-muted-foreground tabular-nums\">{x.position}</span>}\n <span className=\"min-w-0 flex-1\">{describe(x)}</span>\n {x.version_id && <Badge variant=\"outline\">{t(\"memberships.version\")}</Badge>}\n {x.role && <Badge variant=\"secondary\">{labelOf(p.labels, x.role)}</Badge>}\n {x.state === \"proposed\" && (\n <>\n <Badge variant=\"secondary\">{t(\"memberships.proposed\")}</Badge>\n <Button size=\"xs\" variant=\"outline\" disabled={save.pending} onClick={() => void save.run(() => members.add([{ ...strip(x), state: \"active\" }]))}>\n {t(\"memberships.accept\")}\n </Button>\n </>\n )}\n {spec.windows &&\n (editing === x.id ? (\n <span className=\"flex flex-wrap items-center gap-1\">\n <WindowFields from={win.from} until={win.until} name={spoken(x)} onChange={(w) => setWin((cur) => ({ from: \"active_from\" in w ? w.active_from : cur.from, until: \"active_until\" in w ? w.active_until : cur.until }))} />\n <Button\n size=\"xs\"\n disabled={save.pending}\n onClick={() =>\n void save.run(() => members.add([{ ...strip(x), active_from: win.from, active_until: win.until }])).then((d) => d !== undefined && setEditing(null))\n }\n >\n {t(\"common.save\")}\n </Button>\n <Button size=\"xs\" variant=\"ghost\" onClick={() => setEditing(null)}>\n {t(\"common.cancel\")}\n </Button>\n </span>\n ) : (\n <>\n <span className=\"text-xs text-muted-foreground\" data-ckui=\"member-window\">\n {x.active_from || x.active_until\n ? t(\"memberships.window\", { from: x.active_from ? formatDate(m, x.active_from) : \"…\", until: x.active_until ? formatDate(m, x.active_until) : \"…\" })\n : t(\"memberships.always\")}\n </span>\n <Button\n size=\"xs\"\n variant=\"ghost\"\n onClick={() => {\n setWin({ from: x.active_from, until: x.active_until });\n setEditing(x.id);\n }}\n >\n {t(\"memberships.editWindow\")}\n </Button>\n </>\n ))}\n {p.onUseCover && !x.version_id && (\n <Button size=\"xs\" variant=\"ghost\" disabled={save.pending || (p.group.cover?.content_id === x.content_id && !p.group.cover?.auto)} onClick={() => void save.run(() => p.onUseCover!(x.content_id!))}>\n {t(\"memberships.useCover\")}\n </Button>\n )}\n <Button size=\"xs\" variant=\"ghost\" disabled={save.pending} aria-label={t(\"memberships.remove\", { name: spoken(x) })} onClick={() => void save.run(() => members.remove([strip(x)]))}>\n {t(\"common.remove\")}\n </Button>\n </div>\n );\n return (\n <ContentKitUiRoot appearance={p.appearance} className={cn(\"grid gap-3 text-sm\", p.className)} data-ckui=\"group-members\">\n <h3 className=\"font-medium\">{t(\"memberships.members\")}</h3>\n {members.loading && !members.items.length ? (\n <Skeleton className=\"h-20 w-full\" />\n ) : members.error && !members.items.length ? (\n <div role=\"alert\" className=\"grid justify-items-start gap-2\">\n <p className=\"text-destructive\">{contentError(m, members.error)}</p>\n <Button size=\"sm\" variant=\"outline\" onClick={members.reload}>\n {t(\"common.retry\")}\n </Button>\n </div>\n ) : !members.items.length ? (\n <p className=\"text-muted-foreground\">{t(\"memberships.noMembers\")}</p>\n ) : spec.ordered ? (\n <SortableList\n items={members.items}\n id={(x) => x.id}\n name={spoken}\n label={t(\"memberships.members\")}\n disabled={save.pending}\n onMove={(from, to) => {\n const items = [...members.items];\n const [moved] = items.splice(from, 1);\n items.splice(to, 0, moved!);\n void save.run(() => members.reorder(order(items)));\n }}\n >\n {(x, handle) => row(x, handle)}\n </SortableList>\n ) : (\n <ul className=\"grid divide-y divide-border rounded-lg border border-border\">\n {members.items.map((x) => (\n <li key={x.id} className=\"p-2\">\n {row(x)}\n </li>\n ))}\n </ul>\n )}\n {members.hasMore && (\n <div className=\"grid justify-items-center gap-2\">\n {members.error && <p role=\"alert\" className=\"text-destructive\">{contentError(m, members.error)}</p>}\n <Button variant=\"outline\" size=\"sm\" disabled={members.loadingMore} onClick={() => void members.loadMore()}>\n {members.loadingMore ? t(\"groups.loading\") : members.error ? t(\"common.retry\") : t(\"groups.loadMore\")}\n </Button>\n </div>\n )}\n <AddMember spec={spec} labels={p.labels} onAdd={(x) => save.run(() => members.add([x]))} />\n <ErrorLine error={save.error} />\n </ContentKitUiRoot>\n );\n}\n\nconst ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;\n\nfunction AddMember({ spec, labels, onAdd }: { spec: GroupKind; labels?: Labels; onAdd: (m: Membership) => Promise<unknown> }) {\n const { t } = useMessages();\n const id = useId();\n const [content, setContent] = useState(\"\");\n const [version, setVersion] = useState(\"\");\n const [role, setRole] = useState(\"\");\n const [from, setFrom] = useState<string>();\n const [until, setUntil] = useState<string>();\n const ok = ID.test(content.trim()) && (!version.trim() || ID.test(version.trim()));\n return (\n <form\n className=\"flex flex-wrap items-end gap-2\"\n data-ckui=\"add-member\"\n onSubmit={(e) => {\n e.preventDefault();\n if (!ok) return;\n void onAdd({ content_id: content.trim(), version_id: version.trim() || undefined, role: role || undefined, active_from: from, active_until: until }).then((d) => {\n if (d === undefined) return;\n setContent(\"\");\n setVersion(\"\");\n });\n }}\n >\n <div className=\"grid gap-1\">\n <Label htmlFor={`${id}-c`} className=\"text-xs text-muted-foreground\">\n {t(\"memberships.contentId\")}\n </Label>\n <Input id={`${id}-c`} className=\"h-8 w-80 max-w-full font-mono\" value={content} onChange={(e) => setContent(e.target.value)} />\n </div>\n {spec.version_level && (\n <div className=\"grid gap-1\">\n <Label htmlFor={`${id}-v`} className=\"text-xs text-muted-foreground\">\n {t(\"memberships.versionId\")}\n </Label>\n <Input id={`${id}-v`} className=\"h-8 w-80 max-w-full font-mono\" value={version} onChange={(e) => setVersion(e.target.value)} />\n </div>\n )}\n {!!spec.roles?.length && (\n <Select value={role || NONE} onValueChange={(v) => setRole(v === NONE ? \"\" : String(v))}>\n <SelectTrigger size=\"sm\" aria-label={t(\"memberships.role\")}>\n <SelectValue>{(v: string) => (v === NONE ? t(\"memberships.noRole\") : labelOf(labels, v))}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={NONE}>{t(\"memberships.noRole\")}</SelectItem>\n {spec.roles.map((r) => (\n <SelectItem key={r} value={r}>\n {labelOf(labels, r)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n {spec.windows && (\n <WindowFields\n from={from}\n until={until}\n name={t(\"memberships.newMember\")}\n onChange={(w) => {\n if (\"active_from\" in w) setFrom(w.active_from);\n if (\"active_until\" in w) setUntil(w.active_until);\n }}\n />\n )}\n <Button type=\"submit\" size=\"sm\" variant=\"outline\" disabled={!ok}>\n {t(\"memberships.addMember\")}\n </Button>\n </form>\n );\n}\n"],"mappings":"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"}
|
|
@@ -16,6 +16,10 @@ export interface MediaFolderEditorViewProps {
|
|
|
16
16
|
ref?: Ref<MediaFolderEditorHandle>;
|
|
17
17
|
/** Heading; default "Media". */
|
|
18
18
|
label?: ReactNode;
|
|
19
|
+
/** Uploaded page images may be arranged as thumbnail tiles. Other groups remain lists. */
|
|
20
|
+
layout?: "list" | "grid";
|
|
21
|
+
/** Accessible label of the file inputs. */
|
|
22
|
+
inputLabel?: string;
|
|
19
23
|
/** Beside "Add files" in the header, e.g. a ZIP import. */
|
|
20
24
|
toolbar?: ReactNode;
|
|
21
25
|
/** Extra actions on each upload, beside crop, rename, replace and remove. */
|