@openrails/contentkit-ui 0.103.1 → 0.103.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/admin/content-manager.d.ts +1 -1
- package/dist/components/admin/content-manager.js +346 -304
- 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/reader-content-editor.d.ts +6 -0
- package/dist/components/admin/reader-content-editor.js +709 -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/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/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/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 +177 -167
- package/dist/components/media-folder-editor.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/locales/admin/de.js +25 -0
- package/dist/locales/admin/de.js.map +1 -1
- package/dist/locales/admin/en.d.ts +25 -0
- package/dist/locales/admin/en.js +25 -0
- package/dist/locales/admin/en.js.map +1 -1
- package/dist/locales/admin/es.js +25 -0
- package/dist/locales/admin/es.js.map +1 -1
- package/dist/locales/admin/ja.js +25 -0
- package/dist/locales/admin/ja.js.map +1 -1
- package/dist/locales/admin/ko.js +25 -0
- package/dist/locales/admin/ko.js.map +1 -1
- package/dist/locales/admin/zh.js +25 -0
- package/dist/locales/admin/zh.js.map +1 -1
- package/dist/locales/en.d.ts +25 -0
- 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/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/styles.css +1 -1
- package/package.json +1 -1
|
@@ -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. */
|