@blakesteve/roster 5.4.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 +93 -0
- 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/organisms/ModerationQueue/ModerationQueue.cjs +1 -1
- package/dist/components/organisms/ModerationQueue/ModerationQueue.js +190 -192
- 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 +1 -0
- package/dist/meta.json +3 -3
- package/dist/roster.cjs +1 -1
- package/dist/roster.css +1 -1
- package/dist/roster.js +86 -84
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -1289,6 +1289,98 @@ Counts for a tab badge need no API: the app holds the items, so it's
|
|
|
1289
1289
|
image can't render (an expired link, a format the browser can't draw), it
|
|
1290
1290
|
falls back to a link to the original in the same footprint.
|
|
1291
1291
|
|
|
1292
|
+
### FileUpload
|
|
1293
|
+
|
|
1294
|
+
Picks files, uploads each one through the app, and shows where each stands.
|
|
1295
|
+
**It never sends anything itself.** The app passes `upload`, which is called
|
|
1296
|
+
once per file and returns a promise; FileUpload owns picking, the rules, the
|
|
1297
|
+
list, each file's state, the keyboard and what a screen reader hears.
|
|
1298
|
+
|
|
1299
|
+
```tsx
|
|
1300
|
+
import { FileUpload } from "@blakesteve/roster";
|
|
1301
|
+
|
|
1302
|
+
<FileUpload
|
|
1303
|
+
label="Photos"
|
|
1304
|
+
helperText="JPEG or PNG, up to 10 photos, 15 MB each."
|
|
1305
|
+
types={["image/jpeg", "image/png"]}
|
|
1306
|
+
maxFiles={10}
|
|
1307
|
+
maxSize={15 * 1024 * 1024}
|
|
1308
|
+
validate={(file) => (/\.hei[cf]$/i.test(file.name) ? "Export it as JPEG first." : undefined)}
|
|
1309
|
+
upload={async (file, { onProgress, onProcessing, signal }) => {
|
|
1310
|
+
const { id, url } = await api.presignPhoto(file.type, { signal });
|
|
1311
|
+
await putWithProgress(url, file, { onProgress, signal });
|
|
1312
|
+
onProcessing();
|
|
1313
|
+
await api.completePhoto(id, { signal });
|
|
1314
|
+
return id;
|
|
1315
|
+
}}
|
|
1316
|
+
onChange={(items) => setPhotoIds(items.filter((i) => i.status === "done").map((i) => i.result!))}
|
|
1317
|
+
/>;
|
|
1318
|
+
```
|
|
1319
|
+
|
|
1320
|
+
`upload(file, { onProgress, onProcessing, signal })`:
|
|
1321
|
+
|
|
1322
|
+
- **`onProgress(fraction)`** from 0 to 1, if the request can report it
|
|
1323
|
+
(`fetch` can't; an `XMLHttpRequest`'s `upload.onprogress` can). Without it the
|
|
1324
|
+
file reads Uploading with a bar that doesn't give a figure.
|
|
1325
|
+
- **`onProcessing()`** once the bytes are up and the app's server is working on
|
|
1326
|
+
them. The file reads Finishing up until the promise settles.
|
|
1327
|
+
- **`signal`** aborts when the person removes the file, or the field unmounts.
|
|
1328
|
+
Pass it to the request.
|
|
1329
|
+
- **Resolve** with what the form needs later, an id say; it comes back as that
|
|
1330
|
+
file's `result` in `onChange`. **Reject** with an Error whose message says
|
|
1331
|
+
why; it's shown, with Try again.
|
|
1332
|
+
|
|
1333
|
+
Each file is `queued`, `uploading`, `processing`, `done`, `failed` or
|
|
1334
|
+
`rejected`. A few upload at once (`concurrency`, 3 by default) and the rest
|
|
1335
|
+
wait.
|
|
1336
|
+
|
|
1337
|
+
**Rules** are checked as files arrive, picked or dropped: the same file picked
|
|
1338
|
+
twice is turned away first; then `validate(file)`, the app's own reason; then
|
|
1339
|
+
`types` (`image/jpeg`, `image/*`, `.png`,
|
|
1340
|
+
defaulting to `accept`); then `maxSize` in bytes; then `maxFiles`. A file
|
|
1341
|
+
turned away stays in the list with its reason and is never sent. A file that
|
|
1342
|
+
arrives with no type, as some cloud-backed picks on Android do, is judged by
|
|
1343
|
+
its extension.
|
|
1344
|
+
|
|
1345
|
+
**Phones first.** `accept` defaults to `"image/*"`, with `multiple` and no
|
|
1346
|
+
`capture`. That combination is what opens Android's system photo picker, with
|
|
1347
|
+
albums and cloud photos; a list of exact types or an extension gets a file
|
|
1348
|
+
browser instead. Keep `accept` broad and narrow with `types`.
|
|
1349
|
+
|
|
1350
|
+
What it promises:
|
|
1351
|
+
|
|
1352
|
+
- **Disabled, it keeps focus.** Its buttons are `aria-disabled` and ignore
|
|
1353
|
+
presses, so a form that disables the field while it sends doesn't drop
|
|
1354
|
+
whoever was on Remove to the top of the page.
|
|
1355
|
+
- **Choosing is a button** that opens the picker. Dragging files onto the
|
|
1356
|
+
field is an addition where there's a pointer, and the hint for it shows only
|
|
1357
|
+
there. A file let go over the field never replaces the page, even while
|
|
1358
|
+
disabled.
|
|
1359
|
+
- **Removing a file cancels its upload** and moves focus to the next file's
|
|
1360
|
+
Remove, the previous one's, or the Choose button. Never to the page. Try
|
|
1361
|
+
again moves focus to that file's Remove.
|
|
1362
|
+
- **A polite live region** says what happened, one sentence per moment: "3
|
|
1363
|
+
photos added. big.jpg wasn't added. It's over 15 MB.", "2 of 3 uploaded.",
|
|
1364
|
+
"c.jpg didn't upload. The server is busy." Of several counts at once, only
|
|
1365
|
+
the latest is said.
|
|
1366
|
+
- **Thumbnails** come from object URLs, revoked when the file goes. A picture
|
|
1367
|
+
the browser can't draw (HEIC outside Safari) shows a plain mark.
|
|
1368
|
+
- **Every word is replaceable** through `strings`, on screen and spoken.
|
|
1369
|
+
- **Phones.** Every button is a 44px target at 375px, and a long file name
|
|
1370
|
+
truncates rather than pushing Remove off its row.
|
|
1371
|
+
|
|
1372
|
+
### Buttons that load
|
|
1373
|
+
|
|
1374
|
+
`isLoading` shows a spinner and ignores presses, and since 5.5.0 the button
|
|
1375
|
+
keeps focus while it does. It used to set `disabled`, and a browser drops focus
|
|
1376
|
+
from a disabled element to the page, so a keyboard or screen reader user who
|
|
1377
|
+
pressed Save was sent back to the top. A loading button is now `aria-disabled`
|
|
1378
|
+
and `aria-busy` instead, and ignores presses itself (clicks, Enter, Space, and the
|
|
1379
|
+
app's own press handlers, while hover ones still run), which also keeps a
|
|
1380
|
+
submit button from sending its form twice. It no longer dims, since
|
|
1381
|
+
the spinner says it's busy. `loadingLabel` names the spinner ("loading" by
|
|
1382
|
+
default). `disabled` is unchanged and wins over `isLoading`.
|
|
1383
|
+
|
|
1292
1384
|
### Carousel
|
|
1293
1385
|
|
|
1294
1386
|
A sideways row: a row of cards, a selection strip, or a gallery of one per
|
|
@@ -1431,6 +1523,7 @@ function App() {
|
|
|
1431
1523
|
| `LiquidTabs` | Tab strip with a sliding pill; arrow keys, roving focus, 44px `lg` size |
|
|
1432
1524
|
| `MatchupCard` | Head-to-head comparison card |
|
|
1433
1525
|
| `MediaPreview` | Thumbnail that falls back to "Preview unavailable, open original" when it can't render |
|
|
1526
|
+
| `FileUpload` | Picks files and uploads each through the app's `upload`, with per-file progress, retry and rules |
|
|
1434
1527
|
| `Pullquote` | A line lifted out of prose, as `<figure>` + `<blockquote>` + `<figcaption>` |
|
|
1435
1528
|
| `RadioGroup` | Radio set reporting one `string`; descriptions, error, 44px targets |
|
|
1436
1529
|
| `Toast` | Transient floating notice; `Toaster` + the imperative `toast` handle |
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
1
|
+
"use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("react/jsx-runtime"),h=require("react"),j=require("../../../lib/utils.cjs"),p=require("../Spinner/Spinner.cjs"),y=require("./button-variants.cjs"),S=require("../../../node_modules/@headlessui/react/dist/components/button/button.cjs"),q=/^on(?:Click|DoubleClick|AuxClick|(?:Pointer|Mouse)(?:Down|Up)|Key(?:Down|Up|Press)|Touch(?:Start|End))(?:Capture)?$/;function P(u){const r={};for(const[e,a]of Object.entries(u))q.test(e)||(r[e]=a);return{...r,onClick:e=>e.preventDefault()}}const m=h.forwardRef(({className:u,colorScheme:r,variant:e,size:a,isLoading:s=!1,loadingLabel:b,startIcon:c,endIcon:o,surface:f="auto",disabled:l,children:k,...n},x)=>{const i=s&&!l,d=i?P(n):n;return t.jsxs(S.Button,{className:j.cn(y.buttonVariants({colorScheme:r,variant:e,size:a}),f==="dark"&&"dark",i&&"rst:cursor-progress",u),ref:x,disabled:l,...d,"aria-disabled":i?!0:n["aria-disabled"],"aria-busy":i?!0:n["aria-busy"],children:[s&&t.jsx("span",{className:"rst:mr-2 rst:flex rst:shrink-0 rst:items-center",children:t.jsx(p.Spinner,{size:"sm",variant:"current",label:b})}),!s&&c&&t.jsx("span",{className:"rst:mr-2 rst:inline-flex rst:shrink-0 rst:items-center",children:c}),k,!s&&o&&t.jsx("span",{className:"rst:ml-2 rst:inline-flex rst:shrink-0 rst:items-center",children:o})]})});m.displayName="Button";exports.Button=m;
|
|
@@ -2,7 +2,18 @@ import { default as React } from 'react';
|
|
|
2
2
|
import { VariantProps } from 'class-variance-authority';
|
|
3
3
|
import { buttonVariants } from './button-variants';
|
|
4
4
|
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
|
|
5
|
+
/**
|
|
6
|
+
* Shows a spinner and ignores presses while something the button started is
|
|
7
|
+
* in flight. Unlike `disabled`, it keeps the button focusable: a native
|
|
8
|
+
* `disabled` drops focus to the page the moment it lands, so a keyboard or
|
|
9
|
+
* screen reader user who just pressed Save is thrown back to the top. A
|
|
10
|
+
* loading button stays where it is, says it's busy (`aria-busy`) and
|
|
11
|
+
* unavailable (`aria-disabled`), and swallows clicks, Enter and Space, which
|
|
12
|
+
* also stops a submit button sending its form twice.
|
|
13
|
+
*/
|
|
5
14
|
isLoading?: boolean;
|
|
15
|
+
/** What a screen reader hears for the spinner while loading: "loading" by default. */
|
|
16
|
+
loadingLabel?: string;
|
|
6
17
|
startIcon?: React.ReactNode;
|
|
7
18
|
endIcon?: React.ReactNode;
|
|
8
19
|
/**
|
|
@@ -1,50 +1,60 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsxs as
|
|
3
|
-
import
|
|
2
|
+
import { jsxs as b, jsx as a } from "react/jsx-runtime";
|
|
3
|
+
import d from "react";
|
|
4
4
|
import { cn as x } from "../../../lib/utils.js";
|
|
5
|
-
import { Spinner as
|
|
6
|
-
import { buttonVariants as
|
|
7
|
-
import { Button as
|
|
8
|
-
const
|
|
5
|
+
import { Spinner as y } from "../Spinner/Spinner.js";
|
|
6
|
+
import { buttonVariants as C } from "./button-variants.js";
|
|
7
|
+
import { Button as N } from "../../../node_modules/@headlessui/react/dist/components/button/button.js";
|
|
8
|
+
const P = /^on(?:Click|DoubleClick|AuxClick|(?:Pointer|Mouse)(?:Down|Up)|Key(?:Down|Up|Press)|Touch(?:Start|End))(?:Capture)?$/;
|
|
9
|
+
function w(i) {
|
|
10
|
+
const t = {};
|
|
11
|
+
for (const [r, o] of Object.entries(i)) P.test(r) || (t[r] = o);
|
|
12
|
+
return { ...t, onClick: (r) => r.preventDefault() };
|
|
13
|
+
}
|
|
14
|
+
const D = d.forwardRef(
|
|
9
15
|
({
|
|
10
|
-
className:
|
|
11
|
-
colorScheme:
|
|
12
|
-
variant:
|
|
13
|
-
size:
|
|
14
|
-
isLoading:
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
16
|
+
className: i,
|
|
17
|
+
colorScheme: t,
|
|
18
|
+
variant: r,
|
|
19
|
+
size: o,
|
|
20
|
+
isLoading: e = !1,
|
|
21
|
+
loadingLabel: m,
|
|
22
|
+
startIcon: l,
|
|
23
|
+
endIcon: c,
|
|
24
|
+
surface: f = "auto",
|
|
25
|
+
disabled: u,
|
|
26
|
+
children: p,
|
|
27
|
+
...s
|
|
28
|
+
}, k) => {
|
|
29
|
+
const n = e && !u, h = n ? w(s) : s;
|
|
30
|
+
return /* @__PURE__ */ b(
|
|
31
|
+
N,
|
|
25
32
|
{
|
|
26
33
|
className: x(
|
|
27
|
-
|
|
34
|
+
C({ colorScheme: t, variant: r, size: o }),
|
|
28
35
|
// Roster's dark variant is `&:where(.dark, .dark *)`, so an element
|
|
29
36
|
// carrying `.dark` itself satisfies it. That is all forcing the
|
|
30
37
|
// dark surface requires.
|
|
31
|
-
|
|
32
|
-
n
|
|
38
|
+
f === "dark" && "dark",
|
|
39
|
+
n && "rst:cursor-progress",
|
|
40
|
+
i
|
|
33
41
|
),
|
|
34
|
-
ref:
|
|
42
|
+
ref: k,
|
|
35
43
|
disabled: u,
|
|
36
|
-
...
|
|
44
|
+
...h,
|
|
45
|
+
"aria-disabled": n ? !0 : s["aria-disabled"],
|
|
46
|
+
"aria-busy": n ? !0 : s["aria-busy"],
|
|
37
47
|
children: [
|
|
38
|
-
|
|
39
|
-
!
|
|
40
|
-
|
|
41
|
-
!
|
|
48
|
+
e && /* @__PURE__ */ a("span", { className: "rst:mr-2 rst:flex rst:shrink-0 rst:items-center", children: /* @__PURE__ */ a(y, { size: "sm", variant: "current", label: m }) }),
|
|
49
|
+
!e && l && /* @__PURE__ */ a("span", { className: "rst:mr-2 rst:inline-flex rst:shrink-0 rst:items-center", children: l }),
|
|
50
|
+
p,
|
|
51
|
+
!e && c && /* @__PURE__ */ a("span", { className: "rst:ml-2 rst:inline-flex rst:shrink-0 rst:items-center", children: c })
|
|
42
52
|
]
|
|
43
53
|
}
|
|
44
54
|
);
|
|
45
55
|
}
|
|
46
56
|
);
|
|
47
|
-
|
|
57
|
+
D.displayName = "Button";
|
|
48
58
|
export {
|
|
49
|
-
|
|
59
|
+
D as Button
|
|
50
60
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("../../../node_modules/class-variance-authority/dist/index.cjs"),t=r.cva("rst:font-ui rst:inline-flex rst:items-center rst:justify-center rst:cursor-pointer rst:whitespace-nowrap rst:rounded-md rst:text-sm rst:font-medium rst:ring-offset-background rst:transition-colors rst:focus-visible:outline-hidden rst:focus-visible:ring-2 rst:focus-visible:ring-ring rst:focus-visible:ring-offset-2 rst:disabled:pointer-events-none rst:disabled:opacity-50",{variants:{variant:{solid:"rst:border rst:border-transparent rst:elevation-control",soft:"rst:border-transparent rst:shadow-none",outline:"rst:border rst:bg-transparent rst:elevation-control",ghost:"rst:border rst:border-transparent rst:bg-transparent",link:"rst:bg-transparent rst:underline-offset-4 rst:hover:underline"},colorScheme:{primary:"",orange:"",teal:"",purple:"",amber:"",success:"",error:"",neutral:""},size:{xs:"rst:h-7 rst:rounded-sm rst:px-2 rst:text-xs",sm:"rst:h-9 rst:rounded-md rst:px-3",default:"rst:h-10 rst:px-4 rst:py-2",lg:"rst:h-11 rst:rounded-md rst:px-8",icon:"rst:h-10 rst:w-10"}},compoundVariants:[{variant:"solid",colorScheme:"primary",className:"rst:bg-primary-600 rst:hover:bg-primary-700 rst:dark:bg-primary-600 rst:dark:hover:bg-primary-500 rst:text-primary-600-ink rst:hover:text-primary-700-ink rst:dark:hover:text-primary-500-ink"},{variant:"solid",colorScheme:"orange",className:"rst:bg-orange-600 rst:hover:bg-orange-700 rst:dark:bg-orange-600 rst:dark:hover:bg-orange-700 rst:text-orange-600-ink rst:hover:text-orange-700-ink rst:dark:hover:text-orange-700-ink"},{variant:"solid",colorScheme:"teal",className:"rst:bg-teal-600 rst:hover:bg-teal-500 rst:dark:bg-teal-600 rst:dark:hover:bg-teal-500 rst:text-teal-600-ink rst:hover:text-teal-500-ink rst:dark:hover:text-teal-500-ink"},{variant:"solid",colorScheme:"purple",className:"rst:bg-purple-600 rst:hover:bg-purple-700 rst:dark:bg-purple-600 rst:dark:hover:bg-purple-700 rst:text-purple-600-ink rst:hover:text-purple-700-ink rst:dark:hover:text-purple-700-ink"},{variant:"solid",colorScheme:"amber",className:"rst:bg-amber-400 rst:hover:bg-amber-500 rst:dark:bg-amber-500 rst:dark:hover:bg-amber-400 rst:text-amber-400-ink rst:hover:text-amber-500-ink rst:dark:text-amber-500-ink rst:dark:hover:text-amber-400-ink"},{variant:"solid",colorScheme:"success",className:"rst:bg-success-600 rst:hover:bg-success-700 rst:dark:bg-success-600 rst:dark:hover:bg-success-700 rst:text-success-600-ink rst:hover:text-success-700-ink rst:dark:hover:text-success-700-ink"},{variant:"solid",colorScheme:"error",className:"rst:bg-error-600 rst:hover:bg-error-700 rst:dark:bg-error-600 rst:dark:hover:bg-error-500 rst:text-error-600-ink rst:hover:text-error-700-ink rst:dark:hover:text-error-500-ink"},{variant:"solid",colorScheme:"neutral",className:"rst:bg-gray-600 rst:hover:bg-gray-700 rst:dark:bg-gray-600 rst:dark:hover:bg-gray-500 rst:text-gray-600-ink rst:hover:text-gray-700-ink rst:dark:hover:text-gray-500-ink"},{variant:"soft",colorScheme:"primary",className:"rst:bg-primary-50 rst:text-primary-700 rst:hover:bg-primary-100 rst:dark:bg-primary-900/40 rst:dark:text-primary-300 rst:dark:hover:bg-primary-900/60"},{variant:"soft",colorScheme:"orange",className:"rst:bg-orange-50 rst:text-orange-700 rst:hover:bg-orange-100 rst:dark:bg-orange-900/30 rst:dark:text-orange-300 rst:dark:hover:bg-orange-900/50"},{variant:"soft",colorScheme:"teal",className:"rst:bg-teal-100 rst:text-teal-800 rst:hover:bg-teal-200 rst:dark:bg-teal-900/30 rst:dark:text-teal-300 rst:dark:hover:bg-teal-900/50"},{variant:"soft",colorScheme:"purple",className:"rst:bg-purple-50 rst:text-purple-700 rst:hover:bg-purple-100 rst:dark:bg-purple-900/30 rst:dark:text-purple-300 rst:dark:hover:bg-purple-900/50"},{variant:"soft",colorScheme:"amber",className:"rst:bg-amber-50 rst:text-amber-800 rst:hover:bg-amber-100 rst:dark:bg-amber-900/30 rst:dark:text-amber-300 rst:dark:hover:bg-amber-900/50"},{variant:"soft",colorScheme:"success",className:"rst:bg-success-50 rst:text-success-700 rst:hover:bg-success-100 rst:dark:bg-success-900/30 rst:dark:text-success-300 rst:dark:hover:bg-success-900/50"},{variant:"soft",colorScheme:"error",className:"rst:bg-error-50 rst:text-error-700 rst:hover:bg-error-100 rst:dark:bg-error-900/30 rst:dark:text-error-300 rst:dark:hover:bg-error-900/50"},{variant:"soft",colorScheme:"neutral",className:"rst:bg-gray-100 rst:text-gray-700 rst:hover:bg-gray-200 rst:dark:bg-gray-800/50 rst:dark:text-gray-300 rst:dark:hover:bg-gray-800"},{variant:"outline",colorScheme:"primary",className:"rst:border-primary-600 rst:text-primary-600 rst:hover:bg-primary-50 rst:dark:border-primary-500 rst:dark:text-primary-400 rst:dark:hover:bg-primary-500/10"},{variant:"outline",colorScheme:"orange",className:"rst:border-orange-600 rst:text-orange-600 rst:hover:bg-orange-50 rst:dark:border-orange-500 rst:dark:text-orange-400 rst:dark:hover:bg-orange-500/10"},{variant:"outline",colorScheme:"teal",className:"rst:border-teal-600 rst:text-teal-600 rst:hover:bg-teal-50 rst:dark:border-teal-500 rst:dark:text-teal-400 rst:dark:hover:bg-teal-500/10"},{variant:"outline",colorScheme:"purple",className:"rst:border-purple-600 rst:text-purple-600 rst:hover:bg-purple-50 rst:dark:border-purple-500 rst:dark:text-purple-400 rst:dark:hover:bg-purple-500/10"},{variant:"outline",colorScheme:"amber",className:"rst:border-amber-600 rst:text-amber-600 rst:hover:bg-amber-50 rst:dark:border-amber-500 rst:dark:text-amber-400 rst:dark:hover:bg-amber-500/10"},{variant:"outline",colorScheme:"success",className:"rst:border-success-600 rst:text-success-600 rst:hover:bg-success-50 rst:dark:border-success-500 rst:dark:text-success-400 rst:dark:hover:bg-success-500/10"},{variant:"outline",colorScheme:"error",className:"rst:border-error-600 rst:text-error-600 rst:hover:bg-error-50 rst:dark:border-error-500 rst:dark:text-error-400 rst:dark:hover:bg-error-500/10"},{variant:"outline",colorScheme:"neutral",className:"rst:border-gray-300 rst:text-gray-700 rst:hover:bg-gray-50 rst:dark:border-gray-600 rst:dark:text-gray-300 rst:dark:hover:bg-gray-800/50"},{variant:"ghost",colorScheme:"primary",className:"rst:text-primary-600 rst:hover:bg-primary-500/10 rst:dark:text-primary-400 rst:dark:hover:bg-primary-400/15"},{variant:"ghost",colorScheme:"orange",className:"rst:text-orange-600 rst:hover:bg-orange-500/10 rst:dark:text-orange-400 rst:dark:hover:bg-orange-400/15"},{variant:"ghost",colorScheme:"teal",className:"rst:text-teal-600 rst:hover:bg-teal-500/10 rst:dark:text-teal-400 rst:dark:hover:bg-teal-400/15"},{variant:"ghost",colorScheme:"purple",className:"rst:text-purple-600 rst:hover:bg-purple-500/10 rst:dark:text-purple-400 rst:dark:hover:bg-purple-400/15"},{variant:"ghost",colorScheme:"amber",className:"rst:text-amber-600 rst:hover:bg-amber-500/10 rst:dark:text-amber-400 rst:dark:hover:bg-amber-400/15"},{variant:"ghost",colorScheme:"success",className:"rst:text-success-600 rst:hover:bg-success-500/10 rst:dark:text-success-400 rst:dark:hover:bg-success-400/15"},{variant:"ghost",colorScheme:"error",className:"rst:text-error-600 rst:hover:bg-error-500/10 rst:dark:text-error-400 rst:dark:hover:bg-error-400/15"},{variant:"ghost",colorScheme:"neutral",className:"rst:text-gray-600 rst:hover:bg-gray-500/10 rst:dark:text-gray-400 rst:dark:hover:bg-gray-400/15"},{variant:"link",colorScheme:"primary",className:"rst:text-primary-600 rst:hover:text-primary-700 rst:dark:text-primary-400 rst:dark:hover:text-primary-300"},{variant:"link",colorScheme:"orange",className:"rst:text-orange-600 rst:hover:text-orange-700 rst:dark:text-orange-400 rst:dark:hover:text-orange-300"},{variant:"link",colorScheme:"teal",className:"rst:text-teal-600 rst:hover:text-teal-700 rst:dark:text-teal-400 rst:dark:hover:text-teal-300"},{variant:"link",colorScheme:"purple",className:"rst:text-purple-600 rst:hover:text-purple-700 rst:dark:text-purple-400 rst:dark:hover:text-purple-300"},{variant:"link",colorScheme:"amber",className:"rst:text-amber-600 rst:hover:text-amber-700 rst:dark:text-amber-400 rst:dark:hover:text-amber-300"},{variant:"link",colorScheme:"success",className:"rst:text-success-600 rst:hover:text-success-700 rst:dark:text-success-400 rst:dark:hover:text-success-300"},{variant:"link",colorScheme:"error",className:"rst:text-error-600 rst:hover:text-error-700 rst:dark:text-error-400 rst:dark:hover:text-error-300"},{variant:"link",colorScheme:"neutral",className:"rst:text-gray-600 rst:hover:text-gray-900 rst:dark:text-gray-400 rst:dark:hover:text-gray-100"}],defaultVariants:{colorScheme:"primary",variant:"solid",size:"default"}});exports.buttonVariants=t;
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("../../../node_modules/class-variance-authority/dist/index.cjs"),t=r.cva("rst:font-ui rst:inline-flex rst:items-center rst:justify-center rst:cursor-pointer rst:whitespace-nowrap rst:rounded-md rst:text-sm rst:font-medium rst:ring-offset-background rst:transition-colors rst:focus-visible:outline-hidden rst:focus-visible:ring-2 rst:focus-visible:ring-ring rst:focus-visible:ring-offset-2 rst:disabled:pointer-events-none rst:disabled:opacity-50",{variants:{variant:{solid:"rst:border rst:border-transparent rst:elevation-control",soft:"rst:border-transparent rst:shadow-none",outline:"rst:border rst:bg-transparent rst:elevation-control",ghost:"rst:border rst:border-transparent rst:bg-transparent",link:"rst:border-0 rst:bg-transparent rst:underline-offset-4 rst:hover:underline"},colorScheme:{primary:"",orange:"",teal:"",purple:"",amber:"",success:"",error:"",neutral:""},size:{xs:"rst:h-7 rst:rounded-sm rst:px-2 rst:text-xs",sm:"rst:h-9 rst:rounded-md rst:px-3",default:"rst:h-10 rst:px-4 rst:py-2",lg:"rst:h-11 rst:rounded-md rst:px-8",icon:"rst:h-10 rst:w-10"}},compoundVariants:[{variant:"solid",colorScheme:"primary",className:"rst:bg-primary-600 rst:hover:bg-primary-700 rst:dark:bg-primary-600 rst:dark:hover:bg-primary-500 rst:text-primary-600-ink rst:hover:text-primary-700-ink rst:dark:hover:text-primary-500-ink"},{variant:"solid",colorScheme:"orange",className:"rst:bg-orange-600 rst:hover:bg-orange-700 rst:dark:bg-orange-600 rst:dark:hover:bg-orange-700 rst:text-orange-600-ink rst:hover:text-orange-700-ink rst:dark:hover:text-orange-700-ink"},{variant:"solid",colorScheme:"teal",className:"rst:bg-teal-600 rst:hover:bg-teal-500 rst:dark:bg-teal-600 rst:dark:hover:bg-teal-500 rst:text-teal-600-ink rst:hover:text-teal-500-ink rst:dark:hover:text-teal-500-ink"},{variant:"solid",colorScheme:"purple",className:"rst:bg-purple-600 rst:hover:bg-purple-700 rst:dark:bg-purple-600 rst:dark:hover:bg-purple-700 rst:text-purple-600-ink rst:hover:text-purple-700-ink rst:dark:hover:text-purple-700-ink"},{variant:"solid",colorScheme:"amber",className:"rst:bg-amber-400 rst:hover:bg-amber-500 rst:dark:bg-amber-500 rst:dark:hover:bg-amber-400 rst:text-amber-400-ink rst:hover:text-amber-500-ink rst:dark:text-amber-500-ink rst:dark:hover:text-amber-400-ink"},{variant:"solid",colorScheme:"success",className:"rst:bg-success-600 rst:hover:bg-success-700 rst:dark:bg-success-600 rst:dark:hover:bg-success-700 rst:text-success-600-ink rst:hover:text-success-700-ink rst:dark:hover:text-success-700-ink"},{variant:"solid",colorScheme:"error",className:"rst:bg-error-600 rst:hover:bg-error-700 rst:dark:bg-error-600 rst:dark:hover:bg-error-500 rst:text-error-600-ink rst:hover:text-error-700-ink rst:dark:hover:text-error-500-ink"},{variant:"solid",colorScheme:"neutral",className:"rst:bg-gray-600 rst:hover:bg-gray-700 rst:dark:bg-gray-600 rst:dark:hover:bg-gray-500 rst:text-gray-600-ink rst:hover:text-gray-700-ink rst:dark:hover:text-gray-500-ink"},{variant:"soft",colorScheme:"primary",className:"rst:bg-primary-50 rst:text-primary-700 rst:hover:bg-primary-100 rst:dark:bg-primary-900/40 rst:dark:text-primary-300 rst:dark:hover:bg-primary-900/60"},{variant:"soft",colorScheme:"orange",className:"rst:bg-orange-50 rst:text-orange-700 rst:hover:bg-orange-100 rst:dark:bg-orange-900/30 rst:dark:text-orange-300 rst:dark:hover:bg-orange-900/50"},{variant:"soft",colorScheme:"teal",className:"rst:bg-teal-100 rst:text-teal-800 rst:hover:bg-teal-200 rst:dark:bg-teal-900/30 rst:dark:text-teal-300 rst:dark:hover:bg-teal-900/50"},{variant:"soft",colorScheme:"purple",className:"rst:bg-purple-50 rst:text-purple-700 rst:hover:bg-purple-100 rst:dark:bg-purple-900/30 rst:dark:text-purple-300 rst:dark:hover:bg-purple-900/50"},{variant:"soft",colorScheme:"amber",className:"rst:bg-amber-50 rst:text-amber-800 rst:hover:bg-amber-100 rst:dark:bg-amber-900/30 rst:dark:text-amber-300 rst:dark:hover:bg-amber-900/50"},{variant:"soft",colorScheme:"success",className:"rst:bg-success-50 rst:text-success-700 rst:hover:bg-success-100 rst:dark:bg-success-900/30 rst:dark:text-success-300 rst:dark:hover:bg-success-900/50"},{variant:"soft",colorScheme:"error",className:"rst:bg-error-50 rst:text-error-700 rst:hover:bg-error-100 rst:dark:bg-error-900/30 rst:dark:text-error-300 rst:dark:hover:bg-error-900/50"},{variant:"soft",colorScheme:"neutral",className:"rst:bg-gray-100 rst:text-gray-700 rst:hover:bg-gray-200 rst:dark:bg-gray-800/50 rst:dark:text-gray-300 rst:dark:hover:bg-gray-800"},{variant:"outline",colorScheme:"primary",className:"rst:border-primary-600 rst:text-primary-600 rst:hover:bg-primary-50 rst:dark:border-primary-500 rst:dark:text-primary-400 rst:dark:hover:bg-primary-500/10"},{variant:"outline",colorScheme:"orange",className:"rst:border-orange-600 rst:text-orange-600 rst:hover:bg-orange-50 rst:dark:border-orange-500 rst:dark:text-orange-400 rst:dark:hover:bg-orange-500/10"},{variant:"outline",colorScheme:"teal",className:"rst:border-teal-600 rst:text-teal-600 rst:hover:bg-teal-50 rst:dark:border-teal-500 rst:dark:text-teal-400 rst:dark:hover:bg-teal-500/10"},{variant:"outline",colorScheme:"purple",className:"rst:border-purple-600 rst:text-purple-600 rst:hover:bg-purple-50 rst:dark:border-purple-500 rst:dark:text-purple-400 rst:dark:hover:bg-purple-500/10"},{variant:"outline",colorScheme:"amber",className:"rst:border-amber-600 rst:text-amber-600 rst:hover:bg-amber-50 rst:dark:border-amber-500 rst:dark:text-amber-400 rst:dark:hover:bg-amber-500/10"},{variant:"outline",colorScheme:"success",className:"rst:border-success-600 rst:text-success-600 rst:hover:bg-success-50 rst:dark:border-success-500 rst:dark:text-success-400 rst:dark:hover:bg-success-500/10"},{variant:"outline",colorScheme:"error",className:"rst:border-error-600 rst:text-error-600 rst:hover:bg-error-50 rst:dark:border-error-500 rst:dark:text-error-400 rst:dark:hover:bg-error-500/10"},{variant:"outline",colorScheme:"neutral",className:"rst:border-gray-300 rst:text-gray-700 rst:hover:bg-gray-50 rst:dark:border-gray-600 rst:dark:text-gray-300 rst:dark:hover:bg-gray-800/50"},{variant:"ghost",colorScheme:"primary",className:"rst:text-primary-600 rst:hover:bg-primary-500/10 rst:dark:text-primary-400 rst:dark:hover:bg-primary-400/15"},{variant:"ghost",colorScheme:"orange",className:"rst:text-orange-600 rst:hover:bg-orange-500/10 rst:dark:text-orange-400 rst:dark:hover:bg-orange-400/15"},{variant:"ghost",colorScheme:"teal",className:"rst:text-teal-600 rst:hover:bg-teal-500/10 rst:dark:text-teal-400 rst:dark:hover:bg-teal-400/15"},{variant:"ghost",colorScheme:"purple",className:"rst:text-purple-600 rst:hover:bg-purple-500/10 rst:dark:text-purple-400 rst:dark:hover:bg-purple-400/15"},{variant:"ghost",colorScheme:"amber",className:"rst:text-amber-600 rst:hover:bg-amber-500/10 rst:dark:text-amber-400 rst:dark:hover:bg-amber-400/15"},{variant:"ghost",colorScheme:"success",className:"rst:text-success-600 rst:hover:bg-success-500/10 rst:dark:text-success-400 rst:dark:hover:bg-success-400/15"},{variant:"ghost",colorScheme:"error",className:"rst:text-error-600 rst:hover:bg-error-500/10 rst:dark:text-error-400 rst:dark:hover:bg-error-400/15"},{variant:"ghost",colorScheme:"neutral",className:"rst:text-gray-600 rst:hover:bg-gray-500/10 rst:dark:text-gray-400 rst:dark:hover:bg-gray-400/15"},{variant:"link",colorScheme:"primary",className:"rst:text-primary-600 rst:hover:text-primary-700 rst:dark:text-primary-400 rst:dark:hover:text-primary-300"},{variant:"link",colorScheme:"orange",className:"rst:text-orange-600 rst:hover:text-orange-700 rst:dark:text-orange-400 rst:dark:hover:text-orange-300"},{variant:"link",colorScheme:"teal",className:"rst:text-teal-600 rst:hover:text-teal-700 rst:dark:text-teal-400 rst:dark:hover:text-teal-300"},{variant:"link",colorScheme:"purple",className:"rst:text-purple-600 rst:hover:text-purple-700 rst:dark:text-purple-400 rst:dark:hover:text-purple-300"},{variant:"link",colorScheme:"amber",className:"rst:text-amber-600 rst:hover:text-amber-700 rst:dark:text-amber-400 rst:dark:hover:text-amber-300"},{variant:"link",colorScheme:"success",className:"rst:text-success-600 rst:hover:text-success-700 rst:dark:text-success-400 rst:dark:hover:text-success-300"},{variant:"link",colorScheme:"error",className:"rst:text-error-600 rst:hover:text-error-700 rst:dark:text-error-400 rst:dark:hover:text-error-300"},{variant:"link",colorScheme:"neutral",className:"rst:text-gray-600 rst:hover:text-gray-900 rst:dark:text-gray-400 rst:dark:hover:text-gray-100"}],defaultVariants:{colorScheme:"primary",variant:"solid",size:"default"}});exports.buttonVariants=t;
|
|
@@ -9,7 +9,9 @@ const e = /* @__PURE__ */ r(
|
|
|
9
9
|
soft: "rst:border-transparent rst:shadow-none",
|
|
10
10
|
outline: "rst:border rst:bg-transparent rst:elevation-control",
|
|
11
11
|
ghost: "rst:border rst:border-transparent rst:bg-transparent",
|
|
12
|
-
|
|
12
|
+
// `border-0`: a <button> draws the browser's own border unless told
|
|
13
|
+
// otherwise, and an app without a reset would see it round a link.
|
|
14
|
+
link: "rst:border-0 rst:bg-transparent rst:underline-offset-4 rst:hover:underline"
|
|
13
15
|
},
|
|
14
16
|
colorScheme: {
|
|
15
17
|
primary: "",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),o=require("react"),p=require("../../../lib/utils.cjs"),P=require("../../atoms/Button/Button.cjs"),R=require("./file-rules.cjs"),pt={choose:"Choose photos",dropHint:"or drag them here",dropHere:"Drop to add them",queued:"Waiting",uploading:n=>n===null?"Uploading":`Uploading, ${n}%`,processing:"Finishing up",done:"Uploaded",failed:"Didn't upload.",rejected:"Not added.",remove:n=>`Remove ${n}`,retry:n=>`Try ${n} again`,wrongType:()=>"That type of file isn't accepted.",tooLarge:(n,l)=>`It's over ${R.formatBytes(l)}.`,tooMany:n=>`Only ${n} can be added.`,duplicate:()=>"It's already added.",uploadError:n=>n instanceof Error&&n.message?n.message:"Something went wrong.",size:R.formatBytes,added:n=>`${n} ${n===1?"photo":"photos"} added.`,progress:(n,l)=>`${n} of ${l} uploaded.`,failedNotice:(n,l)=>`${n} didn't upload. ${l}`,rejectedNotice:(n,l)=>`${n} wasn't added. ${l}`,removed:n=>`${n} removed.`};function gt({file:n,status:l}){const[M,q]=o.useState(!1),B=!M&&(n.type.startsWith("image/")||/\.(jpe?g|png|gif|webp|avif|heic|heif)$/i.test(n.name)),N=o.useCallback(L=>{if(!L)return;const w=URL.createObjectURL(n);return L.src=w,()=>URL.revokeObjectURL(w)},[n]),x="rst:h-14 rst:w-14 rst:shrink-0 rst:rounded-md rst:border rst:border-[var(--roster-card-border)]";return B?e.jsx("img",{ref:N,alt:"",onError:()=>q(!0),className:p.cn(x,"rst:object-cover",l==="rejected"&&"rst:opacity-50"),"data-file-thumb":"image"}):e.jsx("span",{"aria-hidden":"true",className:p.cn(x,"rst:flex rst:items-center rst:justify-center rst:text-gray-500 rst:dark:text-gray-400"),"data-file-thumb":"placeholder",children:e.jsxs("svg",{viewBox:"0 0 24 24",width:"24",height:"24",fill:"none",stroke:"currentColor",strokeWidth:"1.6",strokeLinecap:"round",strokeLinejoin:"round",children:[e.jsx("rect",{x:"3",y:"5",width:"18",height:"14",rx:"2"}),e.jsx("circle",{cx:"9",cy:"10",r:"1.6"}),e.jsx("path",{d:"m21 15-4.2-4.2a1.6 1.6 0 0 0-2.3 0L7 18"})]})})}const W="rst:cursor-not-allowed rst:opacity-50",S={width:20,height:20,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"};function ht({label:n,upload:l,onChange:M,accept:q="image/*",types:B,maxSize:N,maxFiles:x,validate:L,concurrency:w=3,helperText:E,errorMessage:T,disabled:u=!1,strings:K,className:Q}){const c={...pt,...K},D=o.useId(),I=`${D}-label`,G=`${D}-help`,V=`${D}-error`,[U,Y]=o.useState([]),d=o.useRef(U),Z=o.useRef(0),m=o.useRef(new Map),F=o.useRef(null),X=o.useRef(null),g=o.useRef({upload:l,onChange:M,concurrency:w,t:c,pump:()=>{}}),[O,tt]=o.useState({text:"",n:0}),v=o.useRef([]),y=o.useRef(null),$=o.useCallback((t,s=!1)=>{s&&(v.current=v.current.filter(r=>!r.count)),v.current.push({text:t,count:s}),!y.current&&(y.current=setTimeout(()=>{y.current=null;const r=v.current.map(i=>i.text).join(" ");v.current=[],tt(i=>({text:r,n:i.n+1}))},0))},[]),z=o.useRef(null),[rt,et]=o.useState(0),_=t=>{z.current=t,et(s=>s+1)};o.useLayoutEffect(()=>{const t=z.current;if(!t)return;z.current=null,(F.current?.querySelector(t)??F.current?.querySelector("[data-file-choose]"))?.focus()},[rt]);const j=o.useCallback(t=>{d.current=t,Y(t),g.current.onChange?.(t)},[]),b=o.useCallback((t,s)=>{d.current.some(r=>r.id===t)&&j(d.current.map(r=>r.id===t?{...r,...s}:r))},[j]);function k(){const{concurrency:t}=g.current,s=d.current.filter(i=>i.status==="uploading"||i.status==="processing").length,r=d.current.filter(i=>i.status==="queued").slice(0,Math.max(0,Math.max(1,t)-s));for(const i of r)st(i)}function st(t){const s=new AbortController;m.current.set(t.id,s),b(t.id,{status:"uploading",progress:null,reason:void 0});const r=()=>!s.signal.aborted&&m.current.get(t.id)===s,i={onProgress:a=>{if(!r())return;const f=Math.round(Math.min(1,Math.max(0,Number.isFinite(a)?a:0))*100)/100,h=d.current.find(ft=>ft.id===t.id);h?.status==="uploading"&&h.progress!==f&&b(t.id,{progress:f})},onProcessing:()=>{r()&&b(t.id,{status:"processing"})},signal:s.signal};new Promise(a=>a(g.current.upload(t.file,i))).then(a=>{if(!r())return;m.current.delete(t.id),b(t.id,{status:"done",progress:1,result:a});const f=d.current.filter(h=>h.status!=="rejected");$(g.current.t.progress(f.filter(h=>h.status==="done").length,f.length),!0),k()},a=>{if(!r())return;m.current.delete(t.id);const f=g.current.t.uploadError(a);b(t.id,{status:"failed",reason:f}),$(g.current.t.failedNotice(t.file.name,f)),k()})}o.useLayoutEffect(()=>{g.current={upload:l,onChange:M,concurrency:w,t:c,pump:k}}),o.useEffect(()=>{const t=m.current;return d.current.some(r=>(r.status==="uploading"||r.status==="processing")&&!t.has(r.id))&&(j(d.current.map(r=>(r.status==="uploading"||r.status==="processing")&&!t.has(r.id)?{...r,status:"queued",progress:null}:r)),g.current.pump()),()=>{for(const r of t.values())r.abort();t.clear(),y.current&&clearTimeout(y.current),y.current=null,v.current=[]}},[j]);const nt=B??R.acceptList(q),at=(t,s)=>{if(s.some(i=>R.sameFile(i.file,t)))return c.duplicate(t);const r=L?.(t);if(r)return r;if(!R.matchesType(t,nt))return c.wrongType(t);if(N!==void 0&&t.size>N)return c.tooLarge(t,N);if(x!==void 0&&s.length>=x)return c.tooMany(x)},J=t=>{if(u||t.length===0)return;const s=d.current.filter(a=>a.status!=="rejected"),r=[];for(const a of t){const f=at(a,[...s,...r.filter(h=>h.status!=="rejected")]);r.push({id:`${D}-file-${Z.current++}`,file:a,status:f?"rejected":"queued",progress:null,reason:f})}j([...d.current,...r]);const i=r.filter(a=>a.status==="queued").length;i&&$(c.added(i));for(const a of r)a.reason&&$(c.rejectedNotice(a.file.name,a.reason));k()},ot=t=>{if(u)return;const s=d.current,r=s.findIndex(a=>a.id===t);if(r<0)return;m.current.get(t)?.abort(),m.current.delete(t);const i=s[r+1]??s[r-1];j(s.filter(a=>a.id!==t)),$(c.removed(s[r].file.name)),_(i?`[data-file-remove="${i.id}"]`:"[data-file-choose]"),k()},ct=t=>{u||(b(t,{status:"queued",progress:null,reason:void 0}),_(`[data-file-remove="${t}"]`),k())},[it,A]=o.useState(0),C=t=>Array.from(t.dataTransfer?.types??[]).includes("Files"),ut={onDragEnter:t=>{C(t)&&(t.preventDefault(),A(s=>s+1))},onDragOver:t=>{C(t)&&(t.preventDefault(),t.dataTransfer.dropEffect=u?"none":"copy")},onDragLeave:t=>{C(t)&&A(s=>Math.max(0,s-1))},onDrop:t=>{C(t)&&(t.preventDefault(),A(0),J(Array.from(t.dataTransfer.files)))}},H=it>0&&!u,lt=t=>{switch(t.status){case"queued":return c.queued;case"uploading":return c.uploading(t.progress===null?null:Math.round(t.progress*100));case"processing":return c.processing;case"done":return c.done;case"failed":return`${c.failed} ${t.reason??""}`.trim();case"rejected":return`${c.rejected} ${t.reason??""}`.trim()}},dt=[E?G:null,T?V:null].filter(Boolean).join(" ")||void 0;return e.jsxs("div",{ref:F,role:"group","aria-labelledby":I,className:p.cn("rst:font-ui rst:w-full",Q),"data-file-upload":"",children:[e.jsx("p",{id:I,className:p.cn("rst:mb-1.5 rst:text-left rst:text-sm rst:font-medium rst:leading-none rst:text-[var(--roster-control-text)]",u&&"rst:opacity-70"),children:n}),e.jsxs("div",{...ut,"data-file-drop":"","data-dragging":H?"":void 0,className:p.cn("rst:flex rst:flex-col rst:items-center rst:justify-center rst:gap-2 rst:rounded-lg rst:border-2 rst:border-dashed rst:px-4 rst:py-6 rst:text-center rst:transition-colors rst:motion-reduce:transition-none",H?"rst:border-primary-500 rst:bg-primary-500/10":T?"rst:border-error-500 rst:dark:border-error-400":"rst:border-gray-300 rst:dark:border-gray-600"),children:[e.jsx(P.Button,{type:"button",variant:"outline",colorScheme:"neutral",size:"lg",className:p.cn("rst:px-5",u&&W),"aria-disabled":u||void 0,"aria-describedby":dt,onClick:()=>X.current?.click(),"data-file-choose":"",startIcon:e.jsx("svg",{...S,"aria-hidden":"true",children:e.jsx("path",{d:"M12 16V4M7 9l5-5 5 5M5 20h14"})}),children:c.choose}),e.jsx("p",{className:"rst:hidden rst:text-xs rst:text-gray-600 rst:pointer-fine:block rst:dark:text-gray-400","data-file-drop-hint":"",children:H?c.dropHere:c.dropHint}),e.jsx("input",{ref:X,type:"file",hidden:!0,tabIndex:-1,accept:q,multiple:x!==1,disabled:u,onChange:t=>{J(Array.from(t.currentTarget.files??[])),t.currentTarget.value=""},"data-file-input":""})]}),E&&e.jsx("p",{id:G,className:"rst:mt-1.5 rst:text-left rst:text-xs rst:text-gray-500 rst:dark:text-gray-400",children:E}),T&&e.jsx("p",{id:V,className:"rst:mt-1.5 rst:text-left rst:text-xs rst:font-medium rst:text-error-600 rst:dark:text-error-400",children:T}),U.length>0&&e.jsx("ul",{className:"rst:mt-3 rst:flex rst:flex-col rst:gap-2","aria-labelledby":I,children:U.map(t=>{const s=t.status==="failed"||t.status==="rejected",r=t.status==="uploading"||t.status==="processing"||t.status==="queued";return e.jsxs("li",{"data-file-item":t.id,"data-status":t.status,className:"rst:flex rst:items-center rst:gap-3 rst:rounded-md rst:border rst:border-[var(--roster-card-border)] rst:p-2",children:[e.jsx(gt,{file:t.file,status:t.status}),e.jsxs("div",{className:"rst:min-w-0 rst:flex-1 rst:text-left",children:[e.jsx("p",{className:"rst:truncate rst:text-sm rst:font-medium rst:text-[var(--roster-control-text)]",title:t.file.name,children:t.file.name}),e.jsxs("p",{className:p.cn("rst:mt-0.5 rst:flex rst:items-start rst:gap-1 rst:text-xs",s?"rst:text-error-600 rst:dark:text-error-400":"rst:text-gray-600 rst:dark:text-gray-400"),"data-file-status":"",children:[t.status==="done"&&e.jsx("svg",{...S,width:14,height:14,"aria-hidden":"true",className:"rst:mt-px rst:shrink-0 rst:text-success-600 rst:dark:text-success-400",children:e.jsx("path",{d:"M20 6 9 17l-5-5"})}),e.jsxs("span",{children:[e.jsx("span",{className:"rst:tabular-nums",children:c.size(t.file.size)}),e.jsx("span",{"aria-hidden":"true",children:" · "}),lt(t)]})]}),r&&e.jsx("div",{role:"progressbar","aria-label":t.file.name,"aria-valuemin":0,"aria-valuemax":100,"aria-valuenow":t.status==="uploading"&&t.progress!==null?Math.round(t.progress*100):void 0,className:p.cn("rst:mt-1.5 rst:h-1.5 rst:overflow-hidden rst:rounded-full rst:bg-gray-200 rst:dark:bg-gray-700",t.status==="uploading"&&t.progress===null&&"rst:animate-pulse rst:motion-reduce:animate-none"),"data-file-progress":"",children:e.jsx("div",{className:p.cn("rst:h-full rst:origin-left rst:rounded-full rst:bg-primary-600 rst:transition-transform rst:motion-reduce:transition-none rst:dark:bg-primary-400",t.status==="processing"&&"rst:animate-pulse rst:motion-reduce:animate-none"),style:{transform:`scaleX(${t.status==="processing"?1:t.status==="uploading"?t.progress??0:0})`}})})]}),t.status==="failed"&&e.jsx(P.Button,{type:"button",variant:"ghost",colorScheme:"neutral",size:"icon",className:p.cn("rst:h-11 rst:w-11",u&&W),"aria-disabled":u||void 0,"aria-label":c.retry(t.file.name),onClick:()=>ct(t.id),"data-file-retry":t.id,children:e.jsxs("svg",{...S,"aria-hidden":"true",children:[e.jsx("path",{d:"M3 12a9 9 0 1 0 3-6.7L3 8"}),e.jsx("path",{d:"M3 3v5h5"})]})}),e.jsx(P.Button,{type:"button",variant:"ghost",colorScheme:"neutral",size:"icon",className:p.cn("rst:h-11 rst:w-11",u&&W),"aria-disabled":u||void 0,"aria-label":c.remove(t.file.name),onClick:()=>ot(t.id),"data-file-remove":t.id,children:e.jsx("svg",{...S,"aria-hidden":"true",children:e.jsx("path",{d:"M18 6 6 18M6 6l12 12"})})})]},t.id)})}),e.jsxs("div",{role:"status","aria-live":"polite","aria-atomic":"true",className:"rst:sr-only","data-file-live":"","data-file-live-n":O.n,children:[O.text,O.n%2?" ":""]})]})}exports.FileUpload=ht;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
/** Where a file is: waiting its turn, on its way, being handled by the app's server, or finished one way or another. */
|
|
3
|
+
export type FileUploadStatus = "queued" | "uploading" | "processing" | "done" | "failed" | "rejected";
|
|
4
|
+
/** What FileUpload hands `upload` with each file. */
|
|
5
|
+
export interface FileUploadContext {
|
|
6
|
+
/** Report how much is sent, from 0 to 1. Without it the bar runs without a figure. */
|
|
7
|
+
onProgress: (fraction: number) => void;
|
|
8
|
+
/** Say the bytes are up and the app's server is working on them. */
|
|
9
|
+
onProcessing: () => void;
|
|
10
|
+
/** Aborts when the person removes the file, or the component goes away. */
|
|
11
|
+
signal: AbortSignal;
|
|
12
|
+
}
|
|
13
|
+
/** A file in the list, as `onChange` reports it. */
|
|
14
|
+
export interface FileUploadItem<R = unknown> {
|
|
15
|
+
id: string;
|
|
16
|
+
file: File;
|
|
17
|
+
status: FileUploadStatus;
|
|
18
|
+
/** From 0 to 1 once the app reports any; `null` before. */
|
|
19
|
+
progress: number | null;
|
|
20
|
+
/** What `upload` resolved with, once `done`: an id to send with the form, say. */
|
|
21
|
+
result?: R;
|
|
22
|
+
/** Why it `failed`, or why it was `rejected`. */
|
|
23
|
+
reason?: string;
|
|
24
|
+
}
|
|
25
|
+
/** Every word FileUpload shows or says. Pass any of them in `strings` to replace it. */
|
|
26
|
+
export interface FileUploadStrings {
|
|
27
|
+
choose: string;
|
|
28
|
+
/** Beside the button where a pointer can drag; hidden on touch screens. */
|
|
29
|
+
dropHint: string;
|
|
30
|
+
/** While files are dragged over. */
|
|
31
|
+
dropHere: string;
|
|
32
|
+
queued: string;
|
|
33
|
+
uploading: (percent: number | null) => string;
|
|
34
|
+
processing: string;
|
|
35
|
+
done: string;
|
|
36
|
+
failed: string;
|
|
37
|
+
rejected: string;
|
|
38
|
+
remove: (name: string) => string;
|
|
39
|
+
retry: (name: string) => string;
|
|
40
|
+
wrongType: (file: File) => string;
|
|
41
|
+
tooLarge: (file: File, maxSize: number) => string;
|
|
42
|
+
tooMany: (maxFiles: number) => string;
|
|
43
|
+
duplicate: (file: File) => string;
|
|
44
|
+
/** The reason shown when `upload` rejects. */
|
|
45
|
+
uploadError: (error: unknown) => string;
|
|
46
|
+
size: (bytes: number) => string;
|
|
47
|
+
added: (count: number) => string;
|
|
48
|
+
progress: (done: number, total: number) => string;
|
|
49
|
+
failedNotice: (name: string, reason: string) => string;
|
|
50
|
+
rejectedNotice: (name: string, reason: string) => string;
|
|
51
|
+
removed: (name: string) => string;
|
|
52
|
+
}
|
|
53
|
+
export interface FileUploadProps<R = unknown> {
|
|
54
|
+
/** What the files are: "Photos". Names the group. */
|
|
55
|
+
label: string;
|
|
56
|
+
/**
|
|
57
|
+
* Sends one file, and resolves when it's done with whatever the app needs
|
|
58
|
+
* later (an id, a URL). Rejects with an Error whose message says why, and
|
|
59
|
+
* that message is shown. FileUpload never sends anything itself.
|
|
60
|
+
*/
|
|
61
|
+
upload: (file: File, context: FileUploadContext) => Promise<R>;
|
|
62
|
+
/** Every change to the list, with each file's status and result. */
|
|
63
|
+
onChange?: (items: FileUploadItem<R>[]) => void;
|
|
64
|
+
/**
|
|
65
|
+
* What the picker offers. Defaults to `"image/*"`, and on phones it should
|
|
66
|
+
* stay that way. Android's Chrome opens the system photo picker, with its
|
|
67
|
+
* albums and cloud photos, only when every accepted type is an image (or
|
|
68
|
+
* video) type and there's no `capture`; an extension among them gets a plain
|
|
69
|
+
* file browser. Exact types are riskier than they look: a picker that
|
|
70
|
+
* filters by the type a photo reports can hide photos whose reported type
|
|
71
|
+
* doesn't match. Narrow what's allowed with `types`, which is checked after
|
|
72
|
+
* picking.
|
|
73
|
+
*/
|
|
74
|
+
accept?: string;
|
|
75
|
+
/**
|
|
76
|
+
* The types allowed, checked on every picked or dropped file: `image/jpeg`,
|
|
77
|
+
* `image/*` or `.png`. Defaults to `accept`. A file that arrives without a
|
|
78
|
+
* type is judged by its extension.
|
|
79
|
+
*/
|
|
80
|
+
types?: string[];
|
|
81
|
+
/** The largest file allowed, in bytes. */
|
|
82
|
+
maxSize?: number;
|
|
83
|
+
/** How many files the list may hold, not counting the ones turned away. */
|
|
84
|
+
maxFiles?: number;
|
|
85
|
+
/**
|
|
86
|
+
* The app's own rule: return why a file can't be added, or nothing. Runs
|
|
87
|
+
* before the type and size rules, so its reason is the one shown.
|
|
88
|
+
*/
|
|
89
|
+
validate?: (file: File) => string | null | undefined;
|
|
90
|
+
/** How many files upload at once. The rest wait. */
|
|
91
|
+
concurrency?: number;
|
|
92
|
+
helperText?: ReactNode;
|
|
93
|
+
/** A problem with the field as a whole, from the form: "Add at least one photo." */
|
|
94
|
+
errorMessage?: string;
|
|
95
|
+
disabled?: boolean;
|
|
96
|
+
strings?: Partial<FileUploadStrings>;
|
|
97
|
+
className?: string;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Picks files, uploads each one through the app, and shows where each stands.
|
|
101
|
+
*
|
|
102
|
+
* The app owns every request: `upload` is called once per file, with a way to
|
|
103
|
+
* report progress, a way to say the server is working on it, and a signal
|
|
104
|
+
* that aborts when the file is removed. This owns picking, the rules, the
|
|
105
|
+
* list, each file's state, the keyboard and what a screen reader hears.
|
|
106
|
+
*
|
|
107
|
+
* Choosing is a button, which works everywhere; dragging files onto the field
|
|
108
|
+
* is an addition for a pointer, never the only way in.
|
|
109
|
+
*/
|
|
110
|
+
declare function FileUpload<R = unknown>({ label, upload, onChange, accept, types, maxSize, maxFiles, validate, concurrency, helperText, errorMessage, disabled, strings: overrides, className, }: FileUploadProps<R>): import("react/jsx-runtime").JSX.Element;
|
|
111
|
+
export { FileUpload };
|