@blakesteve/roster 5.3.0 → 5.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +177 -1
- package/dist/components/atoms/Button/Button.cjs +1 -1
- package/dist/components/atoms/Button/Button.d.ts +11 -0
- package/dist/components/atoms/Button/Button.js +42 -32
- package/dist/components/atoms/Button/button-variants.cjs +1 -1
- package/dist/components/atoms/Button/button-variants.js +3 -1
- package/dist/components/molecules/FileUpload/FileUpload.cjs +1 -0
- package/dist/components/molecules/FileUpload/FileUpload.d.ts +111 -0
- package/dist/components/molecules/FileUpload/FileUpload.js +403 -0
- package/dist/components/molecules/FileUpload/file-rules.cjs +1 -0
- package/dist/components/molecules/FileUpload/file-rules.d.ts +22 -0
- package/dist/components/molecules/FileUpload/file-rules.js +43 -0
- package/dist/components/molecules/MediaPreview/MediaPreview.cjs +1 -0
- package/dist/components/molecules/MediaPreview/MediaPreview.d.ts +27 -0
- package/dist/components/molecules/MediaPreview/MediaPreview.js +55 -0
- package/dist/components/organisms/Dialog/Dialog.cjs +1 -1
- package/dist/components/organisms/Dialog/Dialog.d.ts +9 -1
- package/dist/components/organisms/Dialog/Dialog.js +26 -24
- package/dist/components/organisms/ModerationQueue/ModerationQueue.cjs +1 -0
- package/dist/components/organisms/ModerationQueue/ModerationQueue.d.ts +73 -0
- package/dist/components/organisms/ModerationQueue/ModerationQueue.js +428 -0
- package/dist/components/organisms/ModerationQueue/QueueEditDialog.cjs +1 -0
- package/dist/components/organisms/ModerationQueue/QueueEditDialog.d.ts +8 -0
- package/dist/components/organisms/ModerationQueue/QueueEditDialog.js +37 -0
- package/dist/components/organisms/ModerationQueue/QueueEditSheet.cjs +1 -0
- package/dist/components/organisms/ModerationQueue/QueueEditSheet.d.ts +12 -0
- package/dist/components/organisms/ModerationQueue/QueueEditSheet.js +30 -0
- package/dist/components/organisms/ModerationQueue/queue-actions.cjs +1 -0
- package/dist/components/organisms/ModerationQueue/queue-actions.d.ts +157 -0
- package/dist/components/organisms/ModerationQueue/queue-actions.js +6 -0
- package/dist/components/organisms/Navbar/Navbar.cjs +1 -1
- package/dist/components/organisms/Navbar/Navbar.d.ts +2 -0
- package/dist/components/organisms/Navbar/Navbar.js +49 -49
- package/dist/index.d.ts +6 -0
- package/dist/meta.json +3 -3
- package/dist/roster.cjs +1 -1
- package/dist/roster.css +1 -1
- package/dist/roster.js +123 -111
- package/package.json +4 -3
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsxs as t, Fragment as
|
|
3
|
-
import { useState as
|
|
4
|
-
import { FontAwesomeIcon as
|
|
5
|
-
import { faXmark as
|
|
2
|
+
import { jsxs as t, Fragment as k, jsx as r } from "react/jsx-runtime";
|
|
3
|
+
import { useState as w, useLayoutEffect as c, useRef as N, Fragment as d } from "react";
|
|
4
|
+
import { FontAwesomeIcon as _ } from "../../../node_modules/@fortawesome/react-fontawesome/dist/index.js";
|
|
5
|
+
import { faXmark as F } from "../../../node_modules/@fortawesome/free-solid-svg-icons/index.js";
|
|
6
6
|
import { cva as a } from "../../../node_modules/class-variance-authority/dist/index.js";
|
|
7
7
|
import { cn as s } from "../../../lib/utils.js";
|
|
8
|
-
import { lockPage as
|
|
9
|
-
import { Dialog as
|
|
8
|
+
import { lockPage as T } from "../../../internal/page-inert.js";
|
|
9
|
+
import { Dialog as j, DialogBackdrop as z, DialogPanel as D, DialogTitle as V } from "../../../node_modules/@headlessui/react/dist/components/dialog/dialog.js";
|
|
10
10
|
import { TransitionChild as m } from "../../../node_modules/@headlessui/react/dist/components/transition/transition.js";
|
|
11
|
-
const
|
|
11
|
+
const L = /* @__PURE__ */ a(
|
|
12
12
|
"rst:relative rst:w-full rst:transform rst:overflow-hidden rst:rounded-2xl rst:p-6 rst:text-left rst:align-middle rst:elevation-overlay rst:transition-all rst:border",
|
|
13
13
|
{
|
|
14
14
|
variants: {
|
|
@@ -77,9 +77,9 @@ const V = /* @__PURE__ */ a(
|
|
|
77
77
|
status: "default"
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
|
-
), C = /* @__PURE__ */ a("rst:text-xl rst:font-bold rst:leading-6 rst:text-inherit"),
|
|
80
|
+
), C = /* @__PURE__ */ a("rst:text-xl rst:font-bold rst:leading-6 rst:text-inherit"), P = /* @__PURE__ */ a("rst:mt-1 rst:text-sm rst:text-inherit rst:opacity-75"), R = /* @__PURE__ */ a(
|
|
81
81
|
"rst:inline-flex rst:size-11 rst:shrink-0 rst:items-center rst:justify-center rst:-m-3 rst:cursor-pointer rst:rounded-md rst:bg-transparent rst:text-inherit rst:opacity-50 rst:hover:opacity-100 rst:focus:outline-hidden rst:focus-visible:ring-2 rst:focus-visible:ring-ring rst:focus-visible:ring-offset-2 rst:ring-offset-background rst:transition-opacity"
|
|
82
|
-
),
|
|
82
|
+
), $ = ({
|
|
83
83
|
isOpen: f,
|
|
84
84
|
onClose: o,
|
|
85
85
|
title: p,
|
|
@@ -88,20 +88,21 @@ const V = /* @__PURE__ */ a(
|
|
|
88
88
|
variant: n,
|
|
89
89
|
status: g,
|
|
90
90
|
children: v,
|
|
91
|
-
className: u
|
|
91
|
+
className: u,
|
|
92
|
+
onAfterClose: x
|
|
92
93
|
}) => {
|
|
93
|
-
const [
|
|
94
|
+
const [y, h] = w(!1);
|
|
94
95
|
c(() => {
|
|
95
|
-
|
|
96
|
+
h(!0);
|
|
96
97
|
}, []);
|
|
97
|
-
const e = f &&
|
|
98
|
+
const e = f && y, l = N(null);
|
|
98
99
|
return c(() => {
|
|
99
100
|
if (e)
|
|
100
|
-
return
|
|
101
|
-
}, [e]), /* @__PURE__ */ t(
|
|
101
|
+
return T(l.current);
|
|
102
|
+
}, [e]), /* @__PURE__ */ t(k, { children: [
|
|
102
103
|
e && /* @__PURE__ */ r("span", { ref: l, hidden: !0 }),
|
|
103
104
|
/* @__PURE__ */ t(
|
|
104
|
-
|
|
105
|
+
j,
|
|
105
106
|
{
|
|
106
107
|
open: e,
|
|
107
108
|
as: "div",
|
|
@@ -119,7 +120,7 @@ const V = /* @__PURE__ */ a(
|
|
|
119
120
|
leaveFrom: "rst:opacity-100",
|
|
120
121
|
leaveTo: "rst:opacity-0",
|
|
121
122
|
children: /* @__PURE__ */ r(
|
|
122
|
-
|
|
123
|
+
z,
|
|
123
124
|
{
|
|
124
125
|
className: s(
|
|
125
126
|
"rst:fixed rst:inset-0 rst:transition-opacity",
|
|
@@ -139,27 +140,28 @@ const V = /* @__PURE__ */ a(
|
|
|
139
140
|
leave: "rst:ease-in rst:duration-200",
|
|
140
141
|
leaveFrom: "rst:opacity-100 rst:scale-100",
|
|
141
142
|
leaveTo: "rst:opacity-0 rst:scale-95",
|
|
143
|
+
afterLeave: x,
|
|
142
144
|
children: /* @__PURE__ */ t(
|
|
143
|
-
|
|
145
|
+
D,
|
|
144
146
|
{
|
|
145
147
|
className: s(
|
|
146
|
-
|
|
148
|
+
L({ size: b, variant: n, status: g }),
|
|
147
149
|
u
|
|
148
150
|
),
|
|
149
151
|
children: [
|
|
150
152
|
/* @__PURE__ */ t("div", { className: "rst:flex rst:items-start rst:justify-between", children: [
|
|
151
153
|
/* @__PURE__ */ t("div", { children: [
|
|
152
|
-
/* @__PURE__ */ r(
|
|
153
|
-
i && /* @__PURE__ */ r("p", { className: s(
|
|
154
|
+
/* @__PURE__ */ r(V, { as: "h2", className: s(C()), children: p }),
|
|
155
|
+
i && /* @__PURE__ */ r("p", { className: s(P()), children: i })
|
|
154
156
|
] }),
|
|
155
157
|
/* @__PURE__ */ r("div", { className: "rst:ml-4 rst:flex rst:shrink-0", children: /* @__PURE__ */ r(
|
|
156
158
|
"button",
|
|
157
159
|
{
|
|
158
160
|
type: "button",
|
|
159
161
|
onClick: o,
|
|
160
|
-
className: s(
|
|
162
|
+
className: s(R()),
|
|
161
163
|
"aria-label": "Close dialog",
|
|
162
|
-
children: /* @__PURE__ */ r(
|
|
164
|
+
children: /* @__PURE__ */ r(_, { icon: F, className: "rst:h-5 rst:w-5" })
|
|
163
165
|
}
|
|
164
166
|
) })
|
|
165
167
|
] }),
|
|
@@ -175,5 +177,5 @@ const V = /* @__PURE__ */ a(
|
|
|
175
177
|
] });
|
|
176
178
|
};
|
|
177
179
|
export {
|
|
178
|
-
|
|
180
|
+
$ as Dialog
|
|
179
181
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const n=require("react/jsx-runtime"),a=require("react"),P=require("../../../lib/utils.cjs"),ge=require("../../atoms/Button/Button.cjs"),Le=require("../../atoms/Card/Card.cjs"),Oe=require("../../atoms/Spinner/Spinner.cjs"),Pe=require("../../atoms/Textarea/Textarea.cjs"),Y=require("../../molecules/Alert/Alert.cjs"),ze=require("../../molecules/EmptyState/EmptyState.cjs"),De=require("../../molecules/RadioGroup/RadioGroup.cjs"),He=require("./queue-actions.cjs"),me={primary:{colorScheme:"primary",variant:"solid"},success:{colorScheme:"success",variant:"solid"},danger:{colorScheme:"error",variant:"soft"},neutral:{colorScheme:"neutral",variant:"outline"}},Ke={edit:He.editAction},R=c=>c.edit?c:null,Qe=c=>c===0?"None left.":`${c} left.`,Ge=c=>Math.min(6,Math.max(1,c)),We=c=>c instanceof Error&&c.message?c.message:"Something went wrong.";function B(c,d){const p={...c};return delete p[d],p}function Z({busy:c=!1,blocked:d=!1,onClick:p,children:j,className:v,...y}){return n.jsx(ge.Button,{size:"lg",...y,isLoading:c,"aria-disabled":d||void 0,className:P.cn(d&&!c&&"rst:cursor-not-allowed rst:opacity-50",v),onClick:g=>{if(d){g.preventDefault();return}p?.(g)},children:j})}function Je({label:c,hideLabel:d=!1,headingLevel:p=2,itemHeadingLevel:j,items:v,getKey:y,itemLabel:g,renderMeta:xe,renderContent:he,actions:z,status:_="ready",error:ve,onRetry:ee,empty:te={title:"Nothing waiting"},density:re="comfortable",footer:be,className:ye}){const Se=typeof z=="function"?z(Ke):z,N=a.useId(),q=a.useRef(null),k=v.map(y),T=a.useRef(new Map);T.current=new Map(v.map((e,t)=>[k[t],e]));const[D,je]=a.useState({}),$=a.useRef({}),ne=e=>{$.current=e($.current),je($.current)},[se,E]=a.useState({}),[oe,qe]=a.useState({}),x=a.useRef({}),h=(e,t)=>{x.current=t?{...x.current,[e]:t}:B(x.current,e),qe(x.current)},[H,ke]=a.useState({text:"",n:0}),K=a.useRef([]),w=a.useRef(null),ce=a.useCallback(e=>{K.current.push(e),!w.current&&(w.current=setTimeout(()=>{w.current=null;const t=K.current.join(" ");K.current=[],ke(r=>({text:t,n:r.n+1}))},0))},[]);a.useLayoutEffect(()=>()=>{w.current&&clearTimeout(w.current)},[]);const[$e,Re]=a.useState(0),Q=a.useRef(null),S=a.useCallback(e=>{Q.current=e,Re(t=>t+1)},[]),ie=a.useCallback(e=>{const t=q.current;if(!t)return null;const r="key"in e?CSS.escape(e.key):"";switch(e.kind){case"item":return t.querySelector(`[data-queue-item="${r}"]`);case"empty":return t.querySelector("[data-queue-empty]");case"trigger":return t.querySelector(`[data-queue-item="${r}"] [data-queue-action="${CSS.escape(e.actionId)}"]`);case"step":return t.querySelector(`[data-queue-item="${r}"] [data-queue-step]`)}},[]);a.useLayoutEffect(()=>{const e=Q.current;if(!e)return;Q.current=null,(ie(e)??q.current?.querySelector("[data-queue-item]")??q.current?.querySelector("[data-queue-empty]"))?.focus()},[$e,ie]);const G=a.useRef(null),Ne=e=>()=>{G.current=e},we=e=>{e.relatedTarget&&!q.current?.contains(e.relatedTarget)&&(G.current=null)},F=e=>{if(G.current!==e)return!1;const t=document.activeElement;return!t||t===document.body?!0:q.current?.contains(t)?!!t.closest(`[data-queue-item="${CSS.escape(e)}"]`):!1},C=a.useRef(null),A=(e,t)=>{C.current=t,h(e.key,{...e,open:!1})},ae=a.useRef(k),W=a.useRef(new Map),[,Ce]=a.useState(0),J=a.useRef(new Set),ue=(e,t,r)=>{const o=t.indexOf(e),f=t.slice(o+1).find(i=>r.has(i));if(f)return{kind:"item",key:f};const s=t.slice(0,o).reverse().find(i=>r.has(i));return s?{kind:"item",key:s}:{kind:"empty"}};a.useLayoutEffect(()=>{const e=ae.current;ae.current=k;const t=new Set(k),r=W.current;W.current=new Map;const o=[];let f=!1;for(const[s,i]of r){const u=t.has(s);o.push(i.announce??i.sentence),u||(f=!0),J.current.delete(s);const l=u?{kind:"trigger",key:s,actionId:i.actionId}:e.includes(s)?ue(s,e,t):null,m=x.current[s];if(m?.surface)A(m,l??{kind:"empty"});else{const I=F(s);m&&h(s,null),l&&I&&S(l)}}for(const s of e.filter(i=>!t.has(i))){if(r.has(s))continue;E(m=>s in m?B(m,s):m);const i=ue(s,e,t),u=C.current;if(u&&"key"in u&&u.key===s&&(C.current=i),$.current[s]){J.current.add(s),F(s)&&S(i);continue}const l=x.current[s];if(l&&!(l.surface&&!l.open))if(o.push(`${l.label} was decided elsewhere.`),f=!0,l.surface)A(l,i);else{const m=F(s);h(s,null),m&&S(i)}else!l&&F(s)&&S(i)}o.length&&ce(`${o.join(" ")}${f?` ${Qe(k.length)}`:""}`)});const M=async(e,t,r,o)=>{const f=T.current.get(e)??t,s=g(f),i=r.edit?R(r).run:r.run;ne(u=>({...u,[e]:r.id})),E(u=>B(u,e));try{const u=await i(f,o);W.current.set(e,{actionId:r.id,sentence:`${r.done??r.label}: ${s}.`,announce:u?u.announce:void 0}),Ce(l=>l+1)}catch(u){const l=We(u);ce(`${r.label} didn't go through for ${s}. ${l}`);const m=x.current[e];J.current.delete(e)?m?.surface?A(m,{kind:"empty"}):m&&h(e,null):m?h(e,{...m,error:l}):E(I=>({...I,[e]:l}))}finally{ne(u=>B(u,e))}},Ie=(e,t)=>{const r=y(e),o=R(t);if(!t.confirm&&!t.reason&&!o){M(r,e,t,{});return}E(f=>B(f,r)),h(r,{key:r,label:g(e),item:e,action:t,draft:o?o.edit.initial(e):void 0,reason:{},surface:!!o?.edit.surface,open:!0}),o?.edit.surface||S({kind:"step",key:r})},U=e=>{const t=x.current[e];if(!t||$.current[e])return;const r={kind:"trigger",key:e,actionId:t.action.id};t.surface?A(t,r):(h(e,null),S(r))},Be=e=>{const t=x.current[e];!t||$.current[e]||pe(t,T.current.get(e)??t.item)||(t.action.edit?M(e,t.item,t.action,{draft:t.draft}):t.action.reason?M(e,t.item,t.action,{reason:Ue(t.reason)}):M(e,t.item,t.action,{}))},Te=e=>{const t=C.current;C.current=null,h(e,null),t&&S(t)},Ee=`h${p}`,le=j??Ge(p+1),Fe=`h${le}`,V=`${N}-label`,de=(e,t)=>{const{action:r}=e,o=R(r),f=T.current.get(e.key)??e.item,s=!!D[e.key],i=pe(e,f),u=`${N}-step-${t}-message`,l=n.jsx(Z,{...me[r.tone??"neutral"],busy:s,blocked:!!i,"aria-describedby":i?u:void 0,onClick:()=>Be(e.key),"data-queue-submit":"",children:o?o.edit.submitLabel:r.label}),m=n.jsx(Z,{variant:"ghost",colorScheme:"neutral",blocked:s,onClick:()=>U(e.key),children:"Cancel"}),I=n.jsxs(n.Fragment,{children:[r.reason&&n.jsx(Ve,{id:`${N}-step-${t}`,step:r.reason,value:e.reason,disabled:s,onChange:X=>h(e.key,{...x.current[e.key],reason:X})}),o&&n.jsx(o.edit.Form,{item:f,value:e.draft,onChange:X=>h(e.key,{...x.current[e.key],draft:X})}),i&&n.jsx("p",{id:u,className:"rst:text-sm rst:text-error-600 rst:dark:text-error-400","data-queue-validation":"",children:i}),e.error&&n.jsx(Y.Alert,{colorScheme:"error",role:"group","aria-live":"off",children:e.error})]});return{submitButton:l,cancelButton:m,fields:I}},Ae=(e,t)=>{const{submitButton:r,cancelButton:o,fields:f}=de(e,t),s=`${N}-step-${t}-prompt`,i=R(e.action),u=e.action.confirm?.prompt??e.action.reason?.prompt??i?.edit.title;return n.jsxs("div",{role:"group","aria-labelledby":s,tabIndex:-1,"data-queue-step":"",onKeyDown:l=>{l.key==="Escape"&&(l.stopPropagation(),U(e.key))},className:"rst:flex rst:flex-col rst:gap-3 rst:rounded-lg rst:border rst:border-[var(--roster-card-border)] rst:p-4 rst:focus-visible:outline-hidden rst:focus-visible:ring-2 rst:focus-visible:ring-ring",children:[n.jsx("p",{id:s,className:"rst:text-sm rst:font-medium rst:text-[var(--roster-control-text)]",children:u}),i?.edit.description&&n.jsx("p",{className:"rst:text-sm rst:text-gray-600 rst:dark:text-gray-300",children:i.edit.description}),f,n.jsxs("div",{className:"rst:flex rst:flex-wrap rst:gap-2",children:[r,o]})]})},Me=(e,t)=>{const r=D[t];return n.jsx("div",{role:"group","aria-label":`Decide: ${g(e)}`,className:"rst:flex rst:flex-wrap rst:gap-2",children:Se.filter(o=>!o.hidden?.(e)).map(o=>n.jsx(Z,{...me[o.tone??"neutral"],"data-queue-action":o.id,busy:r===o.id,blocked:!!r&&r!==o.id,onClick:()=>Ie(e,o),children:o.label},o.id))})},b=Object.values(oe).find(e=>e.surface)??null,L=b?R(b.action):null,fe=L?.edit.surface,O=b?de(b,-1):null;return n.jsxs("section",{ref:q,"aria-labelledby":V,onBlur:we,className:P.cn("rst:font-ui rst:flex rst:flex-col rst:gap-4",ye),children:[n.jsx(Ee,{id:V,className:P.cn("rst:text-lg rst:font-semibold rst:text-[var(--roster-control-text)]",d&&"rst:sr-only"),children:c}),n.jsxs("div",{role:"status","aria-live":"polite","aria-atomic":"true",className:"rst:sr-only","data-queue-live":"","data-queue-live-n":H.n,children:[H.text,H.n%2?" ":""]}),_==="loading"?n.jsx("div",{className:"rst:flex rst:justify-center rst:py-12","aria-busy":"true",children:n.jsx(Oe.Spinner,{size:"lg",label:`Loading ${c}`})}):_==="error"?n.jsx(Y.Alert,{colorScheme:"error",children:n.jsxs("div",{className:"rst:flex rst:flex-col rst:items-start rst:gap-3",children:[n.jsx("div",{children:ve??"The queue couldn't load."}),ee&&n.jsx(ge.Button,{size:"lg",variant:"outline",colorScheme:"neutral",onClick:ee,children:"Retry"})]})}):v.length===0?n.jsx(ze.EmptyState,{title:te.title,description:te.description,headingLevel:le,tabIndex:-1,"data-queue-empty":"",className:"rst:focus-visible:outline-hidden rst:focus-visible:ring-2 rst:focus-visible:ring-ring"}):n.jsx("ul",{role:"list","aria-labelledby":V,className:"rst:m-0 rst:flex rst:list-none rst:flex-col rst:gap-3 rst:p-0",children:v.map((e,t)=>{const r=k[t],o=`${N}-item-${t}`,f=xe?.(e),s=oe[r];return n.jsx("li",{children:n.jsx("article",{"data-queue-item":r,tabIndex:-1,"aria-labelledby":o,onFocus:Ne(r),className:"rst:rounded-xl rst:focus-visible:outline-hidden rst:focus-visible:ring-2 rst:focus-visible:ring-ring rst:focus-visible:ring-offset-2 rst:ring-offset-background",children:n.jsx(Le.Card,{padding:re==="compact"?"sm":"md",children:n.jsxs("div",{className:P.cn("rst:flex rst:flex-col",re==="compact"?"rst:gap-3":"rst:gap-4"),children:[n.jsxs("div",{className:"rst:flex rst:flex-wrap rst:items-baseline rst:gap-x-3 rst:gap-y-1",children:[n.jsx(Fe,{id:o,className:"rst:text-base rst:font-semibold",children:g(e)}),f&&n.jsx("div",{className:"rst:flex rst:flex-wrap rst:items-center rst:gap-2 rst:text-sm rst:text-gray-600 rst:dark:text-gray-300",children:f})]}),n.jsx("div",{className:"rst:min-w-0",children:he(e)}),se[r]&&n.jsx(Y.Alert,{colorScheme:"error",role:"group","aria-live":"off",children:se[r]}),s&&!s.surface?Ae(s,t):Me(e,r)]})})})},r)})}),be,fe&&b&&L&&O&&n.jsx(fe,{open:b.open,title:L.edit.title,description:L.edit.description,busy:!!D[b.key],onCancel:()=>U(b.key),onAfterClose:()=>Te(b.key),submitButton:O.submitButton,cancelButton:O.cancelButton,children:n.jsx("div",{className:"rst:flex rst:flex-col rst:gap-4",children:O.fields})})]})}function pe(c,d){const p=R(c.action);if(p)return p.edit.validate?.(c.draft,d);if(c.action.reason){if(c.action.reason.required&&!c.reason.value)return"Pick a reason.";if(c.action.reason.note==="required"&&!c.reason.note?.trim())return"Add a note."}}function Ue(c){const d=c.note?.trim();return{...c.value?{value:c.value}:{},...d?{note:d}:{}}}function Ve({id:c,step:d,value:p,disabled:j,onChange:v}){const y=d.note??"optional";return n.jsxs(n.Fragment,{children:[d.options&&d.options.length>0&&n.jsx(De.RadioGroup,{label:d.required?"Reason":"Reason (optional)",options:d.options,value:p.value??"",onChange:g=>v({...p,value:g}),disabled:j}),y!=="none"&&n.jsx(Pe.Textarea,{id:`${c}-note`,label:d.noteLabel??(y==="required"?"Note":"Note (optional)"),rows:3,value:p.note??"",onChange:g=>v({...p,note:g.target.value}),disabled:j})]})}exports.ModerationQueue=Je;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { QueueAction, QueueActionKit, QueueCheckedEditAction, QueueHeadingLevel } from './queue-actions';
|
|
3
|
+
export interface ModerationQueueProps<T> {
|
|
4
|
+
/** The queue's heading, and the list's accessible name. */
|
|
5
|
+
label: string;
|
|
6
|
+
/** Keep the heading for assistive technology only, when the page already titles the queue. */
|
|
7
|
+
hideLabel?: boolean;
|
|
8
|
+
/** The queue heading's level. `2` by default. */
|
|
9
|
+
headingLevel?: QueueHeadingLevel;
|
|
10
|
+
/** Each item's heading level. One below `headingLevel` by default. */
|
|
11
|
+
itemHeadingLevel?: QueueHeadingLevel;
|
|
12
|
+
/** What's waiting. Remove an item once its decision lands; the queue notices. */
|
|
13
|
+
items: readonly T[];
|
|
14
|
+
getKey: (item: T) => string;
|
|
15
|
+
/** The item's heading, and what announcements call it: "Recipe from Dana". */
|
|
16
|
+
itemLabel: (item: T) => string;
|
|
17
|
+
/** Beside the heading: badges, a time, who sent it. */
|
|
18
|
+
renderMeta?: (item: T) => ReactNode;
|
|
19
|
+
/** The item's body: its text, its previews, links out. */
|
|
20
|
+
renderContent: (item: T) => ReactNode;
|
|
21
|
+
/**
|
|
22
|
+
* What a reviewer can decide. A list, or a function returning one: the
|
|
23
|
+
* function form is handed `edit`, which types an edit action's draft, and
|
|
24
|
+
* is the one to use inline in JSX.
|
|
25
|
+
*
|
|
26
|
+
* actions={(a) => [
|
|
27
|
+
* { id: "approve", label: "Approve", run: (r) => approve(r.id) },
|
|
28
|
+
* a.edit({ id: "edit", label: "Edit", edit: { initial: (r) => ({ … }), … },
|
|
29
|
+
* run: (r, { draft }) => save(r.id, draft) }),
|
|
30
|
+
* ]}
|
|
31
|
+
*/
|
|
32
|
+
actions: readonly QueueAction<T>[] | ((kit: QueueActionKit<T>) => readonly QueueAction<T>[]);
|
|
33
|
+
/** `"loading"` before the first answer, `"error"` when the queue itself can't load. */
|
|
34
|
+
status?: "ready" | "loading" | "error";
|
|
35
|
+
/** The whole-queue error, shown with a Retry when `onRetry` is passed. */
|
|
36
|
+
error?: ReactNode;
|
|
37
|
+
onRetry?: () => void;
|
|
38
|
+
empty?: {
|
|
39
|
+
title: string;
|
|
40
|
+
description?: string;
|
|
41
|
+
};
|
|
42
|
+
density?: "comfortable" | "compact";
|
|
43
|
+
/** After the list: "Load more", if the app pages. */
|
|
44
|
+
footer?: ReactNode;
|
|
45
|
+
className?: string;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* A queue of things waiting for a decision: submissions to approve, reports to
|
|
49
|
+
* resolve, claims to check before they publish.
|
|
50
|
+
*
|
|
51
|
+
* The app owns the data and every request; this owns the layout, the states,
|
|
52
|
+
* the steps and the keyboard. It never fetches. An action's `run` is the only
|
|
53
|
+
* path from a decision to the network, and the app says whether the item has
|
|
54
|
+
* left by removing it from `items`.
|
|
55
|
+
*
|
|
56
|
+
* What it promises, each pinned in ModerationQueue.checks.stories.tsx:
|
|
57
|
+
* - Only the clicked action is busy, and it keeps focus while it is. That
|
|
58
|
+
* item's other actions wait; every other item stays live.
|
|
59
|
+
* - A decision that lands and removes the item is announced with how many
|
|
60
|
+
* are left, and focus moves to the next item, the previous one, or the
|
|
61
|
+
* empty state. Never to the page.
|
|
62
|
+
* - A decision that fails leaves the item where it was, says why, and leaves
|
|
63
|
+
* focus on the button that tried. A step stays open with its draft.
|
|
64
|
+
* - Confirm, reason and edit steps open in place (an edit may open in a
|
|
65
|
+
* Dialog or Sheet instead), take focus, and give it back on Escape.
|
|
66
|
+
* - An item that disappears for any other reason, with its step open,
|
|
67
|
+
* closes the step without deciding and is announced once as decided
|
|
68
|
+
* elsewhere. Focus moves only if it was inside the item.
|
|
69
|
+
* - Several outcomes in one render are said in one sentence.
|
|
70
|
+
*/
|
|
71
|
+
declare function ModerationQueue<T>({ label, hideLabel, headingLevel, itemHeadingLevel, items, getKey, itemLabel, renderMeta, renderContent, actions: actionsProp, status, error, onRetry, empty, density, footer, className, }: ModerationQueueProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
72
|
+
export type { QueueCheckedEditAction };
|
|
73
|
+
export { ModerationQueue };
|
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsxs as y, jsx as c, Fragment as xe } from "react/jsx-runtime";
|
|
3
|
+
import { useId as He, useRef as p, useState as C, useCallback as _, useLayoutEffect as ee } from "react";
|
|
4
|
+
import { cn as D } from "../../../lib/utils.js";
|
|
5
|
+
import { Button as ye } from "../../atoms/Button/Button.js";
|
|
6
|
+
import { Card as Ke } from "../../atoms/Card/Card.js";
|
|
7
|
+
import { Spinner as Qe } from "../../atoms/Spinner/Spinner.js";
|
|
8
|
+
import { Textarea as Ge } from "../../atoms/Textarea/Textarea.js";
|
|
9
|
+
import { Alert as te } from "../../molecules/Alert/Alert.js";
|
|
10
|
+
import { EmptyState as We } from "../../molecules/EmptyState/EmptyState.js";
|
|
11
|
+
import { RadioGroup as Je } from "../../molecules/RadioGroup/RadioGroup.js";
|
|
12
|
+
import { editAction as Ue } from "./queue-actions.js";
|
|
13
|
+
const ve = {
|
|
14
|
+
primary: { colorScheme: "primary", variant: "solid" },
|
|
15
|
+
success: { colorScheme: "success", variant: "solid" },
|
|
16
|
+
danger: { colorScheme: "error", variant: "soft" },
|
|
17
|
+
neutral: { colorScheme: "neutral", variant: "outline" }
|
|
18
|
+
}, Ve = { edit: Ue }, I = (o) => o.edit ? o : null, Xe = (o) => o === 0 ? "None left." : `${o} left.`, Ye = (o) => Math.min(6, Math.max(1, o)), Ze = (o) => o instanceof Error && o.message ? o.message : "Something went wrong.";
|
|
19
|
+
function E(o, u) {
|
|
20
|
+
const m = { ...o };
|
|
21
|
+
return delete m[u], m;
|
|
22
|
+
}
|
|
23
|
+
function re({
|
|
24
|
+
busy: o = !1,
|
|
25
|
+
blocked: u = !1,
|
|
26
|
+
onClick: m,
|
|
27
|
+
children: q,
|
|
28
|
+
className: b,
|
|
29
|
+
...S
|
|
30
|
+
}) {
|
|
31
|
+
return /* @__PURE__ */ c(
|
|
32
|
+
ye,
|
|
33
|
+
{
|
|
34
|
+
size: "lg",
|
|
35
|
+
...S,
|
|
36
|
+
isLoading: o,
|
|
37
|
+
"aria-disabled": u || void 0,
|
|
38
|
+
className: D(u && !o && "rst:cursor-not-allowed rst:opacity-50", b),
|
|
39
|
+
onClick: (g) => {
|
|
40
|
+
if (u) {
|
|
41
|
+
g.preventDefault();
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
m?.(g);
|
|
45
|
+
},
|
|
46
|
+
children: q
|
|
47
|
+
}
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
function ft({
|
|
51
|
+
label: o,
|
|
52
|
+
hideLabel: u = !1,
|
|
53
|
+
headingLevel: m = 2,
|
|
54
|
+
itemHeadingLevel: q,
|
|
55
|
+
items: b,
|
|
56
|
+
getKey: S,
|
|
57
|
+
itemLabel: g,
|
|
58
|
+
renderMeta: Se,
|
|
59
|
+
renderContent: ke,
|
|
60
|
+
actions: H,
|
|
61
|
+
status: ne = "ready",
|
|
62
|
+
error: qe,
|
|
63
|
+
onRetry: se,
|
|
64
|
+
empty: oe = { title: "Nothing waiting" },
|
|
65
|
+
density: ce = "comfortable",
|
|
66
|
+
footer: $e,
|
|
67
|
+
className: Ne
|
|
68
|
+
}) {
|
|
69
|
+
const we = typeof H == "function" ? H(Ve) : H, B = He(), $ = p(null), N = b.map(S), j = p(/* @__PURE__ */ new Map());
|
|
70
|
+
j.current = new Map(b.map((e, t) => [N[t], e]));
|
|
71
|
+
const [K, Ce] = C({}), w = p({}), ie = (e) => {
|
|
72
|
+
w.current = e(w.current), Ce(w.current);
|
|
73
|
+
}, [ae, M] = C({}), [le, Ie] = C({}), h = p({}), v = (e, t) => {
|
|
74
|
+
h.current = t ? { ...h.current, [e]: t } : E(h.current, e), Ie(h.current);
|
|
75
|
+
}, [Q, Be] = C({ text: "", n: 0 }), G = p([]), R = p(null), ue = _((e) => {
|
|
76
|
+
G.current.push(e), !R.current && (R.current = setTimeout(() => {
|
|
77
|
+
R.current = null;
|
|
78
|
+
const t = G.current.join(" ");
|
|
79
|
+
G.current = [], Be((r) => ({ text: t, n: r.n + 1 }));
|
|
80
|
+
}, 0));
|
|
81
|
+
}, []);
|
|
82
|
+
ee(
|
|
83
|
+
() => () => {
|
|
84
|
+
R.current && clearTimeout(R.current);
|
|
85
|
+
},
|
|
86
|
+
[]
|
|
87
|
+
);
|
|
88
|
+
const [Re, Te] = C(0), W = p(null), k = _((e) => {
|
|
89
|
+
W.current = e, Te((t) => t + 1);
|
|
90
|
+
}, []), de = _((e) => {
|
|
91
|
+
const t = $.current;
|
|
92
|
+
if (!t) return null;
|
|
93
|
+
const r = "key" in e ? CSS.escape(e.key) : "";
|
|
94
|
+
switch (e.kind) {
|
|
95
|
+
case "item":
|
|
96
|
+
return t.querySelector(`[data-queue-item="${r}"]`);
|
|
97
|
+
case "empty":
|
|
98
|
+
return t.querySelector("[data-queue-empty]");
|
|
99
|
+
case "trigger":
|
|
100
|
+
return t.querySelector(`[data-queue-item="${r}"] [data-queue-action="${CSS.escape(e.actionId)}"]`);
|
|
101
|
+
case "step":
|
|
102
|
+
return t.querySelector(`[data-queue-item="${r}"] [data-queue-step]`);
|
|
103
|
+
}
|
|
104
|
+
}, []);
|
|
105
|
+
ee(() => {
|
|
106
|
+
const e = W.current;
|
|
107
|
+
if (!e) return;
|
|
108
|
+
W.current = null, (de(e) ?? $.current?.querySelector("[data-queue-item]") ?? $.current?.querySelector("[data-queue-empty]"))?.focus();
|
|
109
|
+
}, [Re, de]);
|
|
110
|
+
const J = p(null), Fe = (e) => () => {
|
|
111
|
+
J.current = e;
|
|
112
|
+
}, Ee = (e) => {
|
|
113
|
+
e.relatedTarget && !$.current?.contains(e.relatedTarget) && (J.current = null);
|
|
114
|
+
}, A = (e) => {
|
|
115
|
+
if (J.current !== e) return !1;
|
|
116
|
+
const t = document.activeElement;
|
|
117
|
+
return !t || t === document.body ? !0 : $.current?.contains(t) ? !!t.closest(`[data-queue-item="${CSS.escape(e)}"]`) : !1;
|
|
118
|
+
}, T = p(null), L = (e, t) => {
|
|
119
|
+
T.current = t, v(e.key, { ...e, open: !1 });
|
|
120
|
+
}, fe = p(N), U = p(/* @__PURE__ */ new Map()), [, je] = C(0), V = p(/* @__PURE__ */ new Set()), me = (e, t, r) => {
|
|
121
|
+
const s = t.indexOf(e), d = t.slice(s + 1).find((i) => r.has(i));
|
|
122
|
+
if (d) return { kind: "item", key: d };
|
|
123
|
+
const n = t.slice(0, s).reverse().find((i) => r.has(i));
|
|
124
|
+
return n ? { kind: "item", key: n } : { kind: "empty" };
|
|
125
|
+
};
|
|
126
|
+
ee(() => {
|
|
127
|
+
const e = fe.current;
|
|
128
|
+
fe.current = N;
|
|
129
|
+
const t = new Set(N), r = U.current;
|
|
130
|
+
U.current = /* @__PURE__ */ new Map();
|
|
131
|
+
const s = [];
|
|
132
|
+
let d = !1;
|
|
133
|
+
for (const [n, i] of r) {
|
|
134
|
+
const a = t.has(n);
|
|
135
|
+
s.push(i.announce ?? i.sentence), a || (d = !0), V.current.delete(n);
|
|
136
|
+
const l = a ? { kind: "trigger", key: n, actionId: i.actionId } : e.includes(n) ? me(n, e, t) : null, f = h.current[n];
|
|
137
|
+
if (f?.surface)
|
|
138
|
+
L(f, l ?? { kind: "empty" });
|
|
139
|
+
else {
|
|
140
|
+
const F = A(n);
|
|
141
|
+
f && v(n, null), l && F && k(l);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
for (const n of e.filter((i) => !t.has(i))) {
|
|
145
|
+
if (r.has(n)) continue;
|
|
146
|
+
M((f) => n in f ? E(f, n) : f);
|
|
147
|
+
const i = me(n, e, t), a = T.current;
|
|
148
|
+
if (a && "key" in a && a.key === n && (T.current = i), w.current[n]) {
|
|
149
|
+
V.current.add(n), A(n) && k(i);
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
const l = h.current[n];
|
|
153
|
+
if (l && !(l.surface && !l.open))
|
|
154
|
+
if (s.push(`${l.label} was decided elsewhere.`), d = !0, l.surface) L(l, i);
|
|
155
|
+
else {
|
|
156
|
+
const f = A(n);
|
|
157
|
+
v(n, null), f && k(i);
|
|
158
|
+
}
|
|
159
|
+
else !l && A(n) && k(i);
|
|
160
|
+
}
|
|
161
|
+
s.length && ue(`${s.join(" ")}${d ? ` ${Xe(N.length)}` : ""}`);
|
|
162
|
+
});
|
|
163
|
+
const O = async (e, t, r, s) => {
|
|
164
|
+
const d = j.current.get(e) ?? t, n = g(d), i = r.edit ? I(r).run : r.run;
|
|
165
|
+
ie((a) => ({ ...a, [e]: r.id })), M((a) => E(a, e));
|
|
166
|
+
try {
|
|
167
|
+
const a = await i(d, s);
|
|
168
|
+
U.current.set(e, {
|
|
169
|
+
actionId: r.id,
|
|
170
|
+
sentence: `${r.done ?? r.label}: ${n}.`,
|
|
171
|
+
announce: a ? a.announce : void 0
|
|
172
|
+
}), je((l) => l + 1);
|
|
173
|
+
} catch (a) {
|
|
174
|
+
const l = Ze(a);
|
|
175
|
+
ue(`${r.label} didn't go through for ${n}. ${l}`);
|
|
176
|
+
const f = h.current[e];
|
|
177
|
+
V.current.delete(e) ? f?.surface ? L(f, { kind: "empty" }) : f && v(e, null) : f ? v(e, { ...f, error: l }) : M((F) => ({ ...F, [e]: l }));
|
|
178
|
+
} finally {
|
|
179
|
+
ie((a) => E(a, e));
|
|
180
|
+
}
|
|
181
|
+
}, Me = (e, t) => {
|
|
182
|
+
const r = S(e), s = I(t);
|
|
183
|
+
if (!t.confirm && !t.reason && !s) {
|
|
184
|
+
O(r, e, t, {});
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
M((d) => E(d, r)), v(r, {
|
|
188
|
+
key: r,
|
|
189
|
+
label: g(e),
|
|
190
|
+
item: e,
|
|
191
|
+
action: t,
|
|
192
|
+
draft: s ? s.edit.initial(e) : void 0,
|
|
193
|
+
reason: {},
|
|
194
|
+
surface: !!s?.edit.surface,
|
|
195
|
+
open: !0
|
|
196
|
+
}), s?.edit.surface || k({ kind: "step", key: r });
|
|
197
|
+
}, X = (e) => {
|
|
198
|
+
const t = h.current[e];
|
|
199
|
+
if (!t || w.current[e]) return;
|
|
200
|
+
const r = { kind: "trigger", key: e, actionId: t.action.id };
|
|
201
|
+
t.surface ? L(t, r) : (v(e, null), k(r));
|
|
202
|
+
}, Ae = (e) => {
|
|
203
|
+
const t = h.current[e];
|
|
204
|
+
!t || w.current[e] || be(t, j.current.get(e) ?? t.item) || (t.action.edit ? O(e, t.item, t.action, { draft: t.draft }) : t.action.reason ? O(e, t.item, t.action, { reason: _e(t.reason) }) : O(e, t.item, t.action, {}));
|
|
205
|
+
}, Le = (e) => {
|
|
206
|
+
const t = T.current;
|
|
207
|
+
T.current = null, v(e, null), t && k(t);
|
|
208
|
+
}, Oe = `h${m}`, pe = q ?? Ye(m + 1), Pe = `h${pe}`, Y = `${B}-label`, ge = (e, t) => {
|
|
209
|
+
const { action: r } = e, s = I(r), d = j.current.get(e.key) ?? e.item, n = !!K[e.key], i = be(e, d), a = `${B}-step-${t}-message`, l = /* @__PURE__ */ c(
|
|
210
|
+
re,
|
|
211
|
+
{
|
|
212
|
+
...ve[r.tone ?? "neutral"],
|
|
213
|
+
busy: n,
|
|
214
|
+
blocked: !!i,
|
|
215
|
+
"aria-describedby": i ? a : void 0,
|
|
216
|
+
onClick: () => Ae(e.key),
|
|
217
|
+
"data-queue-submit": "",
|
|
218
|
+
children: s ? s.edit.submitLabel : r.label
|
|
219
|
+
}
|
|
220
|
+
), f = /* @__PURE__ */ c(re, { variant: "ghost", colorScheme: "neutral", blocked: n, onClick: () => X(e.key), children: "Cancel" }), F = /* @__PURE__ */ y(xe, { children: [
|
|
221
|
+
r.reason && /* @__PURE__ */ c(
|
|
222
|
+
et,
|
|
223
|
+
{
|
|
224
|
+
id: `${B}-step-${t}`,
|
|
225
|
+
step: r.reason,
|
|
226
|
+
value: e.reason,
|
|
227
|
+
disabled: n,
|
|
228
|
+
onChange: (Z) => v(e.key, { ...h.current[e.key], reason: Z })
|
|
229
|
+
}
|
|
230
|
+
),
|
|
231
|
+
s && /* @__PURE__ */ c(
|
|
232
|
+
s.edit.Form,
|
|
233
|
+
{
|
|
234
|
+
item: d,
|
|
235
|
+
value: e.draft,
|
|
236
|
+
onChange: (Z) => v(e.key, { ...h.current[e.key], draft: Z })
|
|
237
|
+
}
|
|
238
|
+
),
|
|
239
|
+
i && /* @__PURE__ */ c("p", { id: a, className: "rst:text-sm rst:text-error-600 rst:dark:text-error-400", "data-queue-validation": "", children: i }),
|
|
240
|
+
e.error && /* @__PURE__ */ c(te, { colorScheme: "error", role: "group", "aria-live": "off", children: e.error })
|
|
241
|
+
] });
|
|
242
|
+
return { submitButton: l, cancelButton: f, fields: F };
|
|
243
|
+
}, ze = (e, t) => {
|
|
244
|
+
const { submitButton: r, cancelButton: s, fields: d } = ge(e, t), n = `${B}-step-${t}-prompt`, i = I(e.action), a = e.action.confirm?.prompt ?? e.action.reason?.prompt ?? i?.edit.title;
|
|
245
|
+
return /* @__PURE__ */ y(
|
|
246
|
+
"div",
|
|
247
|
+
{
|
|
248
|
+
role: "group",
|
|
249
|
+
"aria-labelledby": n,
|
|
250
|
+
tabIndex: -1,
|
|
251
|
+
"data-queue-step": "",
|
|
252
|
+
onKeyDown: (l) => {
|
|
253
|
+
l.key === "Escape" && (l.stopPropagation(), X(e.key));
|
|
254
|
+
},
|
|
255
|
+
className: "rst:flex rst:flex-col rst:gap-3 rst:rounded-lg rst:border rst:border-[var(--roster-card-border)] rst:p-4 rst:focus-visible:outline-hidden rst:focus-visible:ring-2 rst:focus-visible:ring-ring",
|
|
256
|
+
children: [
|
|
257
|
+
/* @__PURE__ */ c("p", { id: n, className: "rst:text-sm rst:font-medium rst:text-[var(--roster-control-text)]", children: a }),
|
|
258
|
+
i?.edit.description && /* @__PURE__ */ c("p", { className: "rst:text-sm rst:text-gray-600 rst:dark:text-gray-300", children: i.edit.description }),
|
|
259
|
+
d,
|
|
260
|
+
/* @__PURE__ */ y("div", { className: "rst:flex rst:flex-wrap rst:gap-2", children: [
|
|
261
|
+
r,
|
|
262
|
+
s
|
|
263
|
+
] })
|
|
264
|
+
]
|
|
265
|
+
}
|
|
266
|
+
);
|
|
267
|
+
}, De = (e, t) => {
|
|
268
|
+
const r = K[t];
|
|
269
|
+
return /* @__PURE__ */ c("div", { role: "group", "aria-label": `Decide: ${g(e)}`, className: "rst:flex rst:flex-wrap rst:gap-2", children: we.filter((s) => !s.hidden?.(e)).map((s) => /* @__PURE__ */ c(
|
|
270
|
+
re,
|
|
271
|
+
{
|
|
272
|
+
...ve[s.tone ?? "neutral"],
|
|
273
|
+
"data-queue-action": s.id,
|
|
274
|
+
busy: r === s.id,
|
|
275
|
+
blocked: !!r && r !== s.id,
|
|
276
|
+
onClick: () => Me(e, s),
|
|
277
|
+
children: s.label
|
|
278
|
+
},
|
|
279
|
+
s.id
|
|
280
|
+
)) });
|
|
281
|
+
}, x = Object.values(le).find((e) => e.surface) ?? null, P = x ? I(x.action) : null, he = P?.edit.surface, z = x ? ge(x, -1) : null;
|
|
282
|
+
return /* @__PURE__ */ y(
|
|
283
|
+
"section",
|
|
284
|
+
{
|
|
285
|
+
ref: $,
|
|
286
|
+
"aria-labelledby": Y,
|
|
287
|
+
onBlur: Ee,
|
|
288
|
+
className: D("rst:font-ui rst:flex rst:flex-col rst:gap-4", Ne),
|
|
289
|
+
children: [
|
|
290
|
+
/* @__PURE__ */ c(
|
|
291
|
+
Oe,
|
|
292
|
+
{
|
|
293
|
+
id: Y,
|
|
294
|
+
className: D(
|
|
295
|
+
"rst:text-lg rst:font-semibold rst:text-[var(--roster-control-text)]",
|
|
296
|
+
u && "rst:sr-only"
|
|
297
|
+
),
|
|
298
|
+
children: o
|
|
299
|
+
}
|
|
300
|
+
),
|
|
301
|
+
/* @__PURE__ */ y(
|
|
302
|
+
"div",
|
|
303
|
+
{
|
|
304
|
+
role: "status",
|
|
305
|
+
"aria-live": "polite",
|
|
306
|
+
"aria-atomic": "true",
|
|
307
|
+
className: "rst:sr-only",
|
|
308
|
+
"data-queue-live": "",
|
|
309
|
+
"data-queue-live-n": Q.n,
|
|
310
|
+
children: [
|
|
311
|
+
Q.text,
|
|
312
|
+
Q.n % 2 ? " " : ""
|
|
313
|
+
]
|
|
314
|
+
}
|
|
315
|
+
),
|
|
316
|
+
ne === "loading" ? /* @__PURE__ */ c("div", { className: "rst:flex rst:justify-center rst:py-12", "aria-busy": "true", children: /* @__PURE__ */ c(Qe, { size: "lg", label: `Loading ${o}` }) }) : ne === "error" ? /* @__PURE__ */ c(te, { colorScheme: "error", children: /* @__PURE__ */ y("div", { className: "rst:flex rst:flex-col rst:items-start rst:gap-3", children: [
|
|
317
|
+
/* @__PURE__ */ c("div", { children: qe ?? "The queue couldn't load." }),
|
|
318
|
+
se && /* @__PURE__ */ c(ye, { size: "lg", variant: "outline", colorScheme: "neutral", onClick: se, children: "Retry" })
|
|
319
|
+
] }) }) : b.length === 0 ? /* @__PURE__ */ c(
|
|
320
|
+
We,
|
|
321
|
+
{
|
|
322
|
+
title: oe.title,
|
|
323
|
+
description: oe.description,
|
|
324
|
+
headingLevel: pe,
|
|
325
|
+
tabIndex: -1,
|
|
326
|
+
"data-queue-empty": "",
|
|
327
|
+
className: "rst:focus-visible:outline-hidden rst:focus-visible:ring-2 rst:focus-visible:ring-ring"
|
|
328
|
+
}
|
|
329
|
+
) : (
|
|
330
|
+
/* `role="list"` is restated because Safari drops a list's role once
|
|
331
|
+
its markers are styled away. */
|
|
332
|
+
/* @__PURE__ */ c(
|
|
333
|
+
"ul",
|
|
334
|
+
{
|
|
335
|
+
role: "list",
|
|
336
|
+
"aria-labelledby": Y,
|
|
337
|
+
className: "rst:m-0 rst:flex rst:list-none rst:flex-col rst:gap-3 rst:p-0",
|
|
338
|
+
children: b.map((e, t) => {
|
|
339
|
+
const r = N[t], s = `${B}-item-${t}`, d = Se?.(e), n = le[r];
|
|
340
|
+
return /* @__PURE__ */ c("li", { children: /* @__PURE__ */ c(
|
|
341
|
+
"article",
|
|
342
|
+
{
|
|
343
|
+
"data-queue-item": r,
|
|
344
|
+
tabIndex: -1,
|
|
345
|
+
"aria-labelledby": s,
|
|
346
|
+
onFocus: Fe(r),
|
|
347
|
+
className: "rst:rounded-xl rst:focus-visible:outline-hidden rst:focus-visible:ring-2 rst:focus-visible:ring-ring rst:focus-visible:ring-offset-2 rst:ring-offset-background",
|
|
348
|
+
children: /* @__PURE__ */ c(Ke, { padding: ce === "compact" ? "sm" : "md", children: /* @__PURE__ */ y("div", { className: D("rst:flex rst:flex-col", ce === "compact" ? "rst:gap-3" : "rst:gap-4"), children: [
|
|
349
|
+
/* @__PURE__ */ y("div", { className: "rst:flex rst:flex-wrap rst:items-baseline rst:gap-x-3 rst:gap-y-1", children: [
|
|
350
|
+
/* @__PURE__ */ c(Pe, { id: s, className: "rst:text-base rst:font-semibold", children: g(e) }),
|
|
351
|
+
d && /* @__PURE__ */ c("div", { className: "rst:flex rst:flex-wrap rst:items-center rst:gap-2 rst:text-sm rst:text-gray-600 rst:dark:text-gray-300", children: d })
|
|
352
|
+
] }),
|
|
353
|
+
/* @__PURE__ */ c("div", { className: "rst:min-w-0", children: ke(e) }),
|
|
354
|
+
ae[r] && /* @__PURE__ */ c(te, { colorScheme: "error", role: "group", "aria-live": "off", children: ae[r] }),
|
|
355
|
+
n && !n.surface ? ze(n, t) : De(e, r)
|
|
356
|
+
] }) })
|
|
357
|
+
}
|
|
358
|
+
) }, r);
|
|
359
|
+
})
|
|
360
|
+
}
|
|
361
|
+
)
|
|
362
|
+
),
|
|
363
|
+
$e,
|
|
364
|
+
he && x && P && z && /* @__PURE__ */ c(
|
|
365
|
+
he,
|
|
366
|
+
{
|
|
367
|
+
open: x.open,
|
|
368
|
+
title: P.edit.title,
|
|
369
|
+
description: P.edit.description,
|
|
370
|
+
busy: !!K[x.key],
|
|
371
|
+
onCancel: () => X(x.key),
|
|
372
|
+
onAfterClose: () => Le(x.key),
|
|
373
|
+
submitButton: z.submitButton,
|
|
374
|
+
cancelButton: z.cancelButton,
|
|
375
|
+
children: /* @__PURE__ */ c("div", { className: "rst:flex rst:flex-col rst:gap-4", children: z.fields })
|
|
376
|
+
}
|
|
377
|
+
)
|
|
378
|
+
]
|
|
379
|
+
}
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
function be(o, u) {
|
|
383
|
+
const m = I(o.action);
|
|
384
|
+
if (m) return m.edit.validate?.(o.draft, u);
|
|
385
|
+
if (o.action.reason) {
|
|
386
|
+
if (o.action.reason.required && !o.reason.value) return "Pick a reason.";
|
|
387
|
+
if (o.action.reason.note === "required" && !o.reason.note?.trim()) return "Add a note.";
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
function _e(o) {
|
|
391
|
+
const u = o.note?.trim();
|
|
392
|
+
return { ...o.value ? { value: o.value } : {}, ...u ? { note: u } : {} };
|
|
393
|
+
}
|
|
394
|
+
function et({
|
|
395
|
+
id: o,
|
|
396
|
+
step: u,
|
|
397
|
+
value: m,
|
|
398
|
+
disabled: q,
|
|
399
|
+
onChange: b
|
|
400
|
+
}) {
|
|
401
|
+
const S = u.note ?? "optional";
|
|
402
|
+
return /* @__PURE__ */ y(xe, { children: [
|
|
403
|
+
u.options && u.options.length > 0 && /* @__PURE__ */ c(
|
|
404
|
+
Je,
|
|
405
|
+
{
|
|
406
|
+
label: u.required ? "Reason" : "Reason (optional)",
|
|
407
|
+
options: u.options,
|
|
408
|
+
value: m.value ?? "",
|
|
409
|
+
onChange: (g) => b({ ...m, value: g }),
|
|
410
|
+
disabled: q
|
|
411
|
+
}
|
|
412
|
+
),
|
|
413
|
+
S !== "none" && /* @__PURE__ */ c(
|
|
414
|
+
Ge,
|
|
415
|
+
{
|
|
416
|
+
id: `${o}-note`,
|
|
417
|
+
label: u.noteLabel ?? (S === "required" ? "Note" : "Note (optional)"),
|
|
418
|
+
rows: 3,
|
|
419
|
+
value: m.note ?? "",
|
|
420
|
+
onChange: (g) => b({ ...m, note: g.target.value }),
|
|
421
|
+
disabled: q
|
|
422
|
+
}
|
|
423
|
+
)
|
|
424
|
+
] });
|
|
425
|
+
}
|
|
426
|
+
export {
|
|
427
|
+
ft as ModerationQueue
|
|
428
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),c=require("../Dialog/Dialog.cjs");function d({open:s,title:i,description:t,busy:r,onCancel:l,onAfterClose:n,children:o,submitButton:u,cancelButton:a}){return e.jsx(c.Dialog,{isOpen:s,onClose:r?()=>{}:l,title:i,description:t,size:"lg",onAfterClose:n,children:e.jsxs("div",{className:"rst:flex rst:flex-col rst:gap-6",children:[o,e.jsxs("div",{className:"rst:flex rst:flex-wrap rst:justify-end rst:gap-2",children:[a,u]})]})})}exports.QueueEditDialog=d;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { QueueSurfaceProps } from './queue-actions';
|
|
2
|
+
/**
|
|
3
|
+
* Shows a queue's edit step in a Dialog, for a form with more than a field or
|
|
4
|
+
* two. Pass it as the edit's `surface`. A separate export, so a queue that
|
|
5
|
+
* never edits in a Dialog never ships one.
|
|
6
|
+
*/
|
|
7
|
+
declare function QueueEditDialog({ open, title, description, busy, onCancel, onAfterClose, children, submitButton, cancelButton, }: QueueSurfaceProps): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export { QueueEditDialog };
|