@sia.soul/sia-react-ui 0.1.28 → 0.1.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -8109,136 +8109,426 @@ function buildFlatTree(items, options = {}) {
8109
8109
 
8110
8110
  // src/components/Upload.tsx
8111
8111
  var import_react37 = require("react");
8112
+
8113
+ // src/components/uploadFiles.ts
8114
+ function acceptsUploadFile(file, accept) {
8115
+ if (!accept?.trim()) return true;
8116
+ return accept.split(",").some((part) => {
8117
+ const rule = part.trim().toLowerCase();
8118
+ const type = file.type.toLowerCase();
8119
+ return rule.startsWith(".") ? file.name.toLowerCase().endsWith(rule) : rule.endsWith("/*") ? type.startsWith(rule.slice(0, -1)) : type === rule;
8120
+ });
8121
+ }
8122
+ function includeUploadPath(path, recursive) {
8123
+ return recursive || path.split("/").filter(Boolean).length <= 2;
8124
+ }
8125
+ async function readUploadEntries(entries, recursive) {
8126
+ const files = [];
8127
+ async function visit(entry, path, depth) {
8128
+ if (entry.isFile) {
8129
+ const file = await new Promise((resolve, reject) => entry.file(resolve, reject));
8130
+ files.push({ file, path });
8131
+ } else if (entry.isDirectory && (recursive || depth === 0)) {
8132
+ const reader = entry.createReader();
8133
+ while (true) {
8134
+ const batch = await new Promise((resolve, reject) => reader.readEntries(resolve, reject));
8135
+ if (!batch.length) break;
8136
+ for (const child of batch) await visit(child, `${path}/${child.name}`, depth + 1);
8137
+ }
8138
+ }
8139
+ }
8140
+ for (const entry of entries) await visit(entry, entry.name, 0);
8141
+ return files;
8142
+ }
8143
+
8144
+ // src/components/uploadRequest.ts
8145
+ function sendUploadRequest(config, options) {
8146
+ const xhr = new XMLHttpRequest();
8147
+ const abort = () => xhr.abort();
8148
+ const cleanup2 = () => options.signal.removeEventListener("abort", abort);
8149
+ xhr.open(config.method ?? "POST", config.action, true);
8150
+ xhr.withCredentials = config.withCredentials ?? false;
8151
+ xhr.timeout = config.timeout ?? 0;
8152
+ for (const [name, value] of Object.entries(config.headers ?? {})) xhr.setRequestHeader(name, value);
8153
+ xhr.upload.onprogress = (event) => {
8154
+ if (event.lengthComputable) options.onProgress(event.loaded / event.total * 100);
8155
+ };
8156
+ xhr.onload = () => {
8157
+ cleanup2();
8158
+ if (xhr.status < 200 || xhr.status >= 300) {
8159
+ options.onError(new Error(`\u4E0A\u4F20\u5931\u8D25\uFF1AHTTP ${xhr.status}`));
8160
+ return;
8161
+ }
8162
+ let response = xhr.responseText;
8163
+ try {
8164
+ response = JSON.parse(xhr.responseText);
8165
+ } catch {
8166
+ }
8167
+ options.onSuccess(response, config.url);
8168
+ };
8169
+ xhr.onerror = () => {
8170
+ cleanup2();
8171
+ options.onError(new Error("\u4E0A\u4F20\u7F51\u7EDC\u9519\u8BEF\uFF0C\u8BF7\u68C0\u67E5\u8FDE\u63A5\u4E0E\u8DE8\u57DF\u914D\u7F6E"));
8172
+ };
8173
+ xhr.ontimeout = () => {
8174
+ cleanup2();
8175
+ options.onError(new Error("\u4E0A\u4F20\u8D85\u65F6"));
8176
+ };
8177
+ xhr.onabort = cleanup2;
8178
+ options.signal.addEventListener("abort", abort, { once: true });
8179
+ if (options.signal.aborted) {
8180
+ cleanup2();
8181
+ return { abort };
8182
+ }
8183
+ try {
8184
+ if (config.body === "file") xhr.send(options.file);
8185
+ else {
8186
+ const body = new FormData();
8187
+ for (const [name, value] of Object.entries(config.data ?? {})) body.append(name, value);
8188
+ body.append(config.name ?? "file", options.file, options.filename);
8189
+ xhr.send(body);
8190
+ }
8191
+ } catch (error) {
8192
+ cleanup2();
8193
+ throw error;
8194
+ }
8195
+ return { abort };
8196
+ }
8197
+
8198
+ // src/components/Upload.tsx
8112
8199
  var import_jsx_runtime37 = require("react/jsx-runtime");
8113
8200
  var LIST_IGNORE = /* @__PURE__ */ Symbol("SIA_UPLOAD_LIST_IGNORE");
8114
- function UploadRoot({
8201
+ function useThumbnail(file) {
8202
+ const [local, setLocal] = (0, import_react37.useState)();
8203
+ (0, import_react37.useEffect)(() => {
8204
+ if (!file?.thumbUrl && file?.originFileObj?.type.startsWith("image/")) {
8205
+ const url = URL.createObjectURL(file.originFileObj);
8206
+ setLocal(url);
8207
+ return () => URL.revokeObjectURL(url);
8208
+ }
8209
+ setLocal(void 0);
8210
+ }, [file?.originFileObj, file?.thumbUrl]);
8211
+ return file?.thumbUrl || local || (file?.type?.startsWith("image/") || /\.(png|jpe?g|gif|webp|avif|svg)(\?|$)/i.test(file?.url ?? "") ? file?.url : void 0);
8212
+ }
8213
+ function UploadItem({ file, files, props, actions }) {
8214
+ const source = useThumbnail(file);
8215
+ const [failed, setFailed] = (0, import_react37.useState)(false);
8216
+ (0, import_react37.useEffect)(() => setFailed(false), [source]);
8217
+ const node = /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: `sia-upload__item sia-upload__item--${file.status ?? "ready"}`, children: [
8218
+ props.listType !== "text" ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Button, { variant: "text", className: "sia-upload__thumbnail", "aria-label": `\u9884\u89C8 ${file.name}`, onClick: actions.preview, disabled: !source && !props.onPreview, children: source && !failed ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("img", { src: source, alt: file.name, onError: () => setFailed(true) }) : /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Icon, { name: file.type?.startsWith("image/") ? "image" : "file", size: 28 }) }) : /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Icon, { name: file.status === "done" ? "circle-check" : "file", size: 16 }),
8219
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "sia-upload__name", ...withTitleTooltip({ title: file.relativePath || file.name }), children: file.url ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("a", { href: file.url, target: "_blank", rel: "noreferrer", children: file.name }) : file.name }),
8220
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Button, { variant: "text", className: "sia-upload__remove", disabled: props.disabled, "aria-label": `\u79FB\u9664 ${file.name}`, onClick: () => void actions.remove(), children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Icon, { name: "close", size: 14 }) }),
8221
+ file.status === "uploading" && /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "sia-upload__uploading", children: [
8222
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("span", { children: [
8223
+ "\u4E0A\u4F20\u4E2D ",
8224
+ Math.round(file.percent ?? 0),
8225
+ "%"
8226
+ ] }),
8227
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "sia-upload__progress", role: "progressbar", "aria-label": `${file.name} \u4E0A\u4F20\u8FDB\u5EA6`, "aria-valuemin": 0, "aria-valuemax": 100, "aria-valuenow": file.percent ?? 0, children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { style: { width: `${file.percent ?? 0}%` } }) })
8228
+ ] }),
8229
+ file.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "sia-upload__error", children: "\u4E0A\u4F20\u5931\u8D25" })
8230
+ ] });
8231
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_jsx_runtime37.Fragment, { children: props.itemRender ? props.itemRender(node, file, files, actions) : node });
8232
+ }
8233
+ var UploadRoot = (0, import_react37.forwardRef)(function UploadRoot2({
8115
8234
  accept,
8116
8235
  multiple = false,
8117
8236
  directory = false,
8237
+ recursive = true,
8238
+ pastable = false,
8118
8239
  disabled = false,
8240
+ autoUpload = true,
8119
8241
  maxCount: maxCount2,
8120
8242
  fileList,
8121
8243
  defaultFileList = [],
8122
8244
  listType = "text",
8123
8245
  showUploadList = true,
8124
8246
  beforeUpload,
8247
+ request,
8125
8248
  customRequest,
8126
8249
  onChange,
8127
8250
  onRemove,
8251
+ onReject,
8252
+ onReadError,
8253
+ onPreview,
8254
+ itemRender,
8128
8255
  children,
8129
8256
  className = "",
8130
8257
  onDragOver,
8258
+ onDragLeave,
8131
8259
  onDrop,
8260
+ onPaste,
8261
+ tabIndex,
8132
8262
  ...props
8133
- }) {
8263
+ }, ref) {
8134
8264
  const inputRef = (0, import_react37.useRef)(null);
8135
- const filesRef = (0, import_react37.useRef)(fileList ?? defaultFileList);
8136
- const id = (0, import_react37.useId)();
8137
- const [files, setFiles] = useControllableState({ value: fileList, defaultValue: defaultFileList });
8265
+ const [internalFiles, setInternalFiles] = (0, import_react37.useState)(defaultFileList);
8266
+ const files = fileList ?? internalFiles;
8267
+ const filesRef = (0, import_react37.useRef)(files);
8138
8268
  filesRef.current = files;
8139
- function emit(file, nextList) {
8140
- filesRef.current = nextList;
8141
- setFiles(nextList);
8142
- onChange?.({ file, fileList: nextList });
8143
- }
8144
- async function processFile(file, allFiles) {
8145
- const beforeResult = await beforeUpload?.(file, allFiles);
8146
- if (beforeResult === LIST_IGNORE) return;
8147
- if (beforeResult === false) {
8148
- const pending = { uid: `${Date.now()}-${file.name}`, name: file.name, size: file.size, type: file.type, status: "ready", originFileObj: file };
8149
- emit(pending, maxCount2 === 1 ? [pending] : [...filesRef.current, pending].slice(-(maxCount2 ?? Number.POSITIVE_INFINITY)));
8150
- return;
8269
+ const tasks = (0, import_react37.useRef)(/* @__PURE__ */ new Map());
8270
+ const mounted = (0, import_react37.useRef)(true);
8271
+ const [dragging, setDragging] = (0, import_react37.useState)(false);
8272
+ const [preview, setPreview] = (0, import_react37.useState)();
8273
+ const previewSource = useThumbnail(preview);
8274
+ const limit = maxCount2 === void 0 || !Number.isFinite(maxCount2) ? Infinity : Math.max(0, Math.floor(maxCount2));
8275
+ const full = files.length >= limit;
8276
+ const current = (0, import_react37.useRef)({ disabled, directory, recursive });
8277
+ current.current = { disabled, directory, recursive };
8278
+ function cancel(uid) {
8279
+ const task = tasks.current.get(uid);
8280
+ tasks.current.delete(uid);
8281
+ task?.controller.abort();
8282
+ try {
8283
+ task?.handle?.abort?.();
8284
+ } catch {
8151
8285
  }
8152
- const uploadFile = beforeResult instanceof File ? beforeResult : file;
8153
- const item = { uid: `${Date.now()}-${Math.random().toString(36).slice(2)}`, name: uploadFile.name, size: uploadFile.size, type: uploadFile.type, status: "uploading", percent: 0, originFileObj: uploadFile };
8154
- const nextList = maxCount2 === 1 ? [item] : [...filesRef.current, item].slice(-(maxCount2 ?? Number.POSITIVE_INFINITY));
8155
- emit(item, nextList);
8156
- const request = {
8157
- file: uploadFile,
8158
- filename: uploadFile.name,
8286
+ task?.finish();
8287
+ }
8288
+ (0, import_react37.useEffect)(() => {
8289
+ mounted.current = true;
8290
+ return () => {
8291
+ mounted.current = false;
8292
+ [...tasks.current.keys()].forEach(cancel);
8293
+ };
8294
+ }, []);
8295
+ (0, import_react37.useEffect)(() => {
8296
+ for (const uid of tasks.current.keys()) if (!files.some((file) => file.uid === uid)) cancel(uid);
8297
+ if (preview && !files.some((file) => file.uid === preview.uid)) setPreview(void 0);
8298
+ }, [files, preview]);
8299
+ function emit(file, next, event) {
8300
+ if (!mounted.current) return;
8301
+ filesRef.current = next;
8302
+ if (fileList === void 0) setInternalFiles(next);
8303
+ onChange?.({ file, fileList: next, event });
8304
+ }
8305
+ function start(file) {
8306
+ if (current.current.disabled || !mounted.current || file.status === "done" || !file.originFileObj) return Promise.resolve();
8307
+ const existing = tasks.current.get(file.uid);
8308
+ if (existing) return existing.done;
8309
+ let finish = () => {
8310
+ };
8311
+ const done = new Promise((resolve) => {
8312
+ finish = resolve;
8313
+ });
8314
+ const task = { controller: new AbortController(), done, finish };
8315
+ tasks.current.set(file.uid, task);
8316
+ const active = () => mounted.current && tasks.current.get(file.uid) === task && !task.controller.signal.aborted && filesRef.current.some((entry) => entry.uid === file.uid);
8317
+ const update = (patch, event) => {
8318
+ if (!active()) return;
8319
+ const next = { ...filesRef.current.find((entry) => entry.uid === file.uid), ...patch };
8320
+ emit(next, filesRef.current.map((entry) => entry.uid === file.uid ? next : entry), event);
8321
+ };
8322
+ const settle = () => {
8323
+ if (tasks.current.get(file.uid) === task) tasks.current.delete(file.uid);
8324
+ finish();
8325
+ };
8326
+ const options = {
8327
+ file: file.originFileObj,
8328
+ filename: file.name,
8329
+ uploadFile: file,
8330
+ relativePath: file.relativePath || file.name,
8331
+ signal: task.controller.signal,
8159
8332
  onProgress: (percent2) => {
8160
- const progressing = { ...item, status: "uploading", percent: percent2 };
8161
- const progressList = filesRef.current.map((entry) => entry.uid === item.uid ? progressing : entry);
8162
- emit(progressing, progressList);
8333
+ if (Number.isFinite(percent2)) update({ percent: Math.max(0, Math.min(100, percent2)) }, "progress");
8163
8334
  },
8164
- onSuccess: (response) => {
8165
- const done = { ...item, status: "done", percent: 100, response };
8166
- const completed = filesRef.current.map((entry) => entry.uid === item.uid ? done : entry);
8167
- emit(done, completed);
8335
+ onSuccess: (response, url) => {
8336
+ if (!active()) return;
8337
+ update({ status: "done", percent: 100, response, ...url ? { url } : {} }, "success");
8338
+ settle();
8168
8339
  },
8169
8340
  onError: (error) => {
8170
- const failed = { ...item, status: "error", error };
8171
- const completed = filesRef.current.map((entry) => entry.uid === item.uid ? failed : entry);
8172
- emit(failed, completed);
8341
+ if (!active()) return;
8342
+ update({ status: "error", error }, "error");
8343
+ settle();
8173
8344
  }
8174
8345
  };
8175
- if (customRequest) customRequest(request);
8176
- else window.setTimeout(() => request.onSuccess({ local: true }), 180);
8346
+ update({ status: "uploading", percent: 0, error: void 0 }, "start");
8347
+ void (async () => {
8348
+ try {
8349
+ if (!active()) return;
8350
+ const result = await beforeUpload?.(options.file, filesRef.current.flatMap((entry) => entry.originFileObj ? [entry.originFileObj] : []));
8351
+ if (!active()) return;
8352
+ if (result === LIST_IGNORE) {
8353
+ settle();
8354
+ emit(file, filesRef.current.filter((entry) => entry.uid !== file.uid), "remove");
8355
+ return;
8356
+ }
8357
+ if (result === false || !customRequest && !request) {
8358
+ update({ status: "ready" }, "cancel");
8359
+ settle();
8360
+ return;
8361
+ }
8362
+ if (result instanceof File) {
8363
+ options.file = result;
8364
+ options.filename = result.name;
8365
+ }
8366
+ if (customRequest) task.handle = await customRequest(options) || void 0;
8367
+ else {
8368
+ const config = typeof request === "function" ? await request({ ...file, originFileObj: options.file, name: options.filename }, task.controller.signal) : request;
8369
+ if (active()) task.handle = sendUploadRequest(config, options);
8370
+ }
8371
+ if (task.controller.signal.aborted) task.handle?.abort?.();
8372
+ } catch (error) {
8373
+ options.onError(error);
8374
+ }
8375
+ })();
8376
+ return done;
8377
+ }
8378
+ function abort(uid) {
8379
+ const ids = uid ? [uid] : [...tasks.current.keys()];
8380
+ for (const id of ids) {
8381
+ if (!tasks.current.has(id)) continue;
8382
+ cancel(id);
8383
+ const file = filesRef.current.find((entry) => entry.uid === id);
8384
+ if (file) {
8385
+ const next = { ...file, status: "ready", percent: 0 };
8386
+ emit(next, filesRef.current.map((entry) => entry.uid === id ? next : entry), "cancel");
8387
+ }
8388
+ }
8177
8389
  }
8178
- function handleFiles(event) {
8179
- const selected = [...event.currentTarget.files ?? []];
8180
- selected.forEach((file) => void processFile(file, selected));
8181
- event.currentTarget.value = "";
8390
+ async function remove(uid) {
8391
+ if (current.current.disabled) return;
8392
+ const file = filesRef.current.find((entry) => entry.uid === uid);
8393
+ if (!file) return;
8394
+ try {
8395
+ if (await onRemove?.(file) === false || !mounted.current || current.current.disabled) return;
8396
+ cancel(uid);
8397
+ emit(file, filesRef.current.filter((entry) => entry.uid !== uid), "remove");
8398
+ } catch {
8399
+ }
8182
8400
  }
8183
- function handleDrop(event) {
8401
+ (0, import_react37.useImperativeHandle)(ref, () => ({
8402
+ upload: async (uid) => {
8403
+ await Promise.all(filesRef.current.filter((file) => (!uid || file.uid === uid) && file.status !== "done").map(start));
8404
+ },
8405
+ abort,
8406
+ remove
8407
+ }));
8408
+ function ingest(entries) {
8409
+ if (current.current.disabled || !mounted.current) return;
8410
+ const filtered = directory ? entries.filter((entry) => includeUploadPath(entry.path, recursive)) : entries;
8411
+ const selected = multiple || directory ? filtered : filtered.slice(0, 1);
8412
+ const added = [];
8413
+ for (const { file, path } of selected) {
8414
+ if (!acceptsUploadFile(file, accept)) {
8415
+ onReject?.({ file, reason: "accept" });
8416
+ continue;
8417
+ }
8418
+ if (filesRef.current.length >= limit) {
8419
+ onReject?.({ file, reason: "max-count" });
8420
+ continue;
8421
+ }
8422
+ const item = { uid: `upload-${Date.now()}-${Math.random().toString(36).slice(2)}`, name: file.name, size: file.size, type: file.type, relativePath: path, originFileObj: file, status: "ready", percent: 0 };
8423
+ emit(item, [...filesRef.current, item], "add");
8424
+ added.push(item);
8425
+ }
8426
+ if (autoUpload) added.forEach((file) => {
8427
+ void start(file);
8428
+ });
8429
+ }
8430
+ async function handleDrop(event) {
8184
8431
  onDrop?.(event);
8185
- if (event.defaultPrevented || disabled) return;
8432
+ setDragging(false);
8433
+ if (event.defaultPrevented) return;
8186
8434
  event.preventDefault();
8187
- const selected = [...event.dataTransfer.files];
8188
- selected.forEach((file) => void processFile(file, selected));
8189
- }
8190
- async function remove(file) {
8191
- if (await onRemove?.(file) === false) return;
8192
- const next = files.filter((item) => item.uid !== file.uid);
8193
- emit({ ...file, status: "ready" }, next);
8435
+ if (disabled) return;
8436
+ const fallback = [...event.dataTransfer.files].map((file) => ({ file, path: file.webkitRelativePath || file.name }));
8437
+ const entries = [...event.dataTransfer.items].map((item) => item.webkitGetAsEntry?.()).filter((entry) => !!entry);
8438
+ try {
8439
+ if (entries.length) ingest(await readUploadEntries(entries.filter((entry) => directory || !entry.isDirectory), recursive));
8440
+ else ingest(fallback);
8441
+ } catch (error) {
8442
+ if (mounted.current) onReadError?.(error);
8443
+ }
8194
8444
  }
8445
+ function handlePaste(event) {
8446
+ onPaste?.(event);
8447
+ if (event.defaultPrevented || disabled || !pastable) return;
8448
+ const selected = [...event.clipboardData.files];
8449
+ if (!selected.length) return;
8450
+ event.preventDefault();
8451
+ ingest(selected.map((file) => ({ file, path: file.name })));
8452
+ }
8453
+ const trigger = !(listType === "picture-card" && full) && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "sia-upload__trigger", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Button, { className: "sia-upload__choose", disabled: disabled || full, onClick: () => inputRef.current?.click(), children: children ?? (listType === "picture-card" ? /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_jsx_runtime37.Fragment, { children: [
8454
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Icon, { name: "plus", size: 24 }),
8455
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { children: "\u4E0A\u4F20\u56FE\u7247" })
8456
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_jsx_runtime37.Fragment, { children: [
8457
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Icon, { name: "upload", size: 16 }),
8458
+ directory ? "\u9009\u62E9\u6587\u4EF6\u5939" : "\u9009\u62E9\u6587\u4EF6"
8459
+ ] })) }) });
8195
8460
  return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
8196
8461
  "div",
8197
8462
  {
8198
- className: `sia-upload sia-upload--${listType} ${className}`.trim(),
8463
+ ...withTitleTooltip(props),
8464
+ className: `sia-upload sia-upload--${listType}${dragging ? " is-dragging" : ""}${disabled ? " is-disabled" : ""} ${className}`.trim(),
8465
+ tabIndex: tabIndex ?? (pastable && !disabled ? 0 : void 0),
8466
+ onPaste: handlePaste,
8199
8467
  onDragOver: (event) => {
8200
8468
  onDragOver?.(event);
8201
- if (!event.defaultPrevented && !disabled) event.preventDefault();
8469
+ if (!event.defaultPrevented) {
8470
+ event.preventDefault();
8471
+ if (!disabled) setDragging(true);
8472
+ }
8202
8473
  },
8203
- onDrop: handleDrop,
8204
- ...withTitleTooltip(props),
8474
+ onDragLeave: (event) => {
8475
+ onDragLeave?.(event);
8476
+ if (!event.currentTarget.contains(event.relatedTarget)) setDragging(false);
8477
+ },
8478
+ onDrop: (event) => void handleDrop(event),
8205
8479
  children: [
8206
8480
  /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
8207
8481
  "input",
8208
8482
  {
8209
8483
  ref: inputRef,
8210
- id,
8211
8484
  className: "sia-upload__input",
8212
8485
  type: "file",
8213
8486
  accept,
8214
- multiple,
8215
- disabled,
8216
- ...withTitleTooltip(directory ? { webkitdirectory: "", directory: "" } : {}),
8217
- onChange: handleFiles
8487
+ multiple: multiple || directory,
8488
+ disabled: disabled || full,
8489
+ ...directory ? { webkitdirectory: "", directory: "" } : {},
8490
+ onChange: (event) => {
8491
+ ingest([...event.currentTarget.files ?? []].map((file) => ({ file, path: file.webkitRelativePath || file.name })));
8492
+ event.currentTarget.value = "";
8493
+ }
8218
8494
  }
8219
8495
  ),
8220
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "sia-upload__trigger", onClick: () => !disabled && inputRef.current?.click(), children: children ?? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Button, { icon: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Icon, { name: "upload", size: 16 }), disabled, children: "\u9009\u62E9\u6587\u4EF6" }) }),
8221
- showUploadList && files.length ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "sia-upload__list", children: files.map((file) => /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: `sia-upload__item sia-upload__item--${file.status ?? "ready"}`, children: [
8222
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Icon, { name: file.status === "done" ? "circle-check" : "file", size: 16 }),
8223
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "sia-upload__name", ...withTitleTooltip({ title: file.name }), children: file.name }),
8224
- file.status === "uploading" ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "sia-upload__progress", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { style: { width: `${file.percent ?? 0}%` } }) }) : null,
8225
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("button", { type: "button", "aria-label": `\u79FB\u9664 ${file.name}`, onClick: () => void remove(file), children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Icon, { name: "close", size: 14 }) })
8226
- ] }, file.uid)) }) : null
8496
+ listType !== "picture-card" && trigger,
8497
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "sia-upload__list", children: [
8498
+ showUploadList && files.map((file) => /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(UploadItem, { file, files, props: { listType, disabled, itemRender, onPreview }, actions: {
8499
+ upload: () => start(file),
8500
+ abort: () => abort(file.uid),
8501
+ remove: () => remove(file.uid),
8502
+ preview: () => {
8503
+ if (onPreview) onPreview(file);
8504
+ else setPreview(file);
8505
+ }
8506
+ } }, file.uid)),
8507
+ listType === "picture-card" && trigger
8508
+ ] }),
8509
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(ImagePreview, { open: !!preview && !!(preview.url || previewSource), src: preview?.url || previewSource, alt: preview?.name, onOpenChange: (open) => {
8510
+ if (!open) setPreview(void 0);
8511
+ } })
8227
8512
  ]
8228
8513
  }
8229
8514
  );
8230
- }
8231
- function UploadDragger({ hint = "\u652F\u6301\u5355\u4E2A\u6216\u6279\u91CF\u4E0A\u4F20", children, className = "", ...props }) {
8232
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(UploadRoot, { ...props, className: `sia-upload-dragger ${className}`.trim(), children: children ?? /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "sia-upload-dragger__content", children: [
8233
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Icon, { name: "upload", size: 28 }),
8234
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("strong", { children: "\u70B9\u51FB\u6216\u62D6\u62FD\u6587\u4EF6\u5230\u6B64\u533A\u57DF\u4E0A\u4F20" }),
8235
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { children: hint })
8515
+ });
8516
+ var UploadDragger = (0, import_react37.forwardRef)(function UploadDragger2({ hint = "\u652F\u6301\u5355\u4E2A\u6216\u6279\u91CF\u4E0A\u4F20", children, className = "", ...props }, ref) {
8517
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(UploadRoot, { ...props, ref, className: `sia-upload-dragger ${className}`.trim(), children: children ?? /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("span", { className: "sia-upload-dragger__content", children: [
8518
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Icon, { name: "upload", size: 32 }),
8519
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("strong", { children: [
8520
+ "\u70B9\u51FB\u6216\u62D6\u62FD",
8521
+ props.directory ? "\u6587\u4EF6\u5939" : "\u6587\u4EF6",
8522
+ "\u5230\u6B64\u533A\u57DF\u4E0A\u4F20"
8523
+ ] }),
8524
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { children: hint }),
8525
+ props.pastable && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { children: "\u805A\u7126\u6B64\u533A\u57DF\u540E\u6309 Ctrl / \u2318 + V \u7C98\u8D34\u6587\u4EF6\u6216\u622A\u56FE" })
8236
8526
  ] }) });
8237
- }
8527
+ });
8238
8528
  var Upload = Object.assign(UploadRoot, {
8239
- /** 支持拖入文件上传的区域组件。 */
8529
+ /** 可点击、键盘操作和拖拽的上传区域,ref 支持手动上传、取消及移除。 */
8240
8530
  Dragger: UploadDragger,
8241
- /** beforeUpload 返回此标记时忽略该文件,不加入上传列表。 */
8531
+ /** beforeUpload 返回时移除该文件,不进行上传。 */
8242
8532
  LIST_IGNORE
8243
8533
  });
8244
8534
 
@@ -12230,36 +12520,26 @@ function createSortPlugin(options = {}) {
12230
12520
  ...column,
12231
12521
  renderHeader: (currentColumn, coreContext) => {
12232
12522
  const info = current(context);
12523
+ const order = info?.field === field ? info.order : null;
12524
+ const sortHint = `${getColumnLabel(currentColumn)}\uFF1A${order === "ascend" ? "\u5F53\u524D\u5347\u5E8F\uFF0C\u70B9\u51FB\u5207\u6362\u4E3A\u964D\u5E8F" : order === "descend" ? "\u5F53\u524D\u964D\u5E8F\uFF0C\u70B9\u51FB\u53D6\u6D88\u6392\u5E8F" : "\u672A\u6392\u5E8F\uFF0C\u70B9\u51FB\u6309\u5347\u5E8F\u6392\u5E8F"}`;
12233
12525
  return /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("span", { className: "sia-table__sortable-header", children: [
12234
12526
  /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { children: previousHeader ? previousHeader(currentColumn, coreContext) : currentColumn.title ?? currentColumn.name ?? currentColumn.key }),
12235
- /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(
12527
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
12236
12528
  "button",
12237
12529
  {
12238
12530
  type: "button",
12239
12531
  className: "sia-table__sort-button",
12240
- "aria-label": `\u5207\u6362${getColumnLabel(currentColumn)}\u6392\u5E8F`,
12532
+ "data-sort-order": order ?? "none",
12533
+ "aria-label": sortHint,
12534
+ ...withTitleTooltip({ title: sortHint }),
12241
12535
  onClick: (event) => {
12242
12536
  event.stopPropagation();
12243
12537
  cycle(field, context);
12244
12538
  },
12245
- children: [
12246
- /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
12247
- Icon,
12248
- {
12249
- name: "chevron-up",
12250
- size: 11,
12251
- className: info?.field === field && info.order === "ascend" ? "is-active" : ""
12252
- }
12253
- ),
12254
- /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
12255
- Icon,
12256
- {
12257
- name: "chevron-down",
12258
- size: 11,
12259
- className: info?.field === field && info.order === "descend" ? "is-active" : ""
12260
- }
12261
- )
12262
- ]
12539
+ children: order ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(Icon, { name: order === "ascend" ? "arrow-up" : "arrow-down", size: 18 }) : /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(import_jsx_runtime50.Fragment, { children: [
12540
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(Icon, { name: "chevron-up", size: 11 }),
12541
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(Icon, { name: "chevron-down", size: 11 })
12542
+ ] })
12263
12543
  }
12264
12544
  )
12265
12545
  ] });
@@ -13685,6 +13965,12 @@ var EMPTY_COLUMNS = [];
13685
13965
  var EMPTY_PLUGINS = [];
13686
13966
  var DEFAULT_WIDTH = 160;
13687
13967
  var SIZE_ROW_HEIGHT = { small: 32, medium: 38, large: 46 };
13968
+ function getHeaderTitleText(content) {
13969
+ if (typeof content === "string" || typeof content === "number") return String(content);
13970
+ if (Array.isArray(content)) return content.map(getHeaderTitleText).join("");
13971
+ if ((0, import_react50.isValidElement)(content)) return getHeaderTitleText(content.props.children);
13972
+ return "";
13973
+ }
13688
13974
  function getColumnDepth(columns) {
13689
13975
  let depth = 1;
13690
13976
  for (const column of columns) {
@@ -14206,10 +14492,10 @@ function TableInner(props, ref) {
14206
14492
  return fixed === "left" ? { left: leftOffsets.get(column.key) } : fixed === "right" ? { right: rightOffsets.get(column.key) } : void 0;
14207
14493
  }
14208
14494
  function renderHeaderContent(column, content) {
14209
- if (column.description === void 0 || column.description === null) return content;
14495
+ const title = getHeaderTitleText(column.title ?? column.name) || column.name || column.key;
14210
14496
  return /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("span", { className: "sia-table__header-label", children: [
14211
- /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("span", { className: "sia-table__header-label-content", children: content }),
14212
- /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(Tooltip, { title: column.description, placement: "top", trigger: ["hover", "focus"], children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("button", { type: "button", className: "sia-table__header-description", "aria-label": `${column.name ?? column.key}\u5217\u8BF4\u660E`, onClick: (event) => event.stopPropagation(), children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(Icon, { name: "info", size: 14 }) }) })
14497
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("span", { className: "sia-table__header-label-content", ...withTitleTooltip({ title }), children: content }),
14498
+ column.description != null && /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(Tooltip, { title: column.description, placement: "top", trigger: ["hover", "focus"], children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("button", { type: "button", className: "sia-table__header-description", "aria-label": `${column.name ?? column.key}\u5217\u8BF4\u660E`, onClick: (event) => event.stopPropagation(), children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(Icon, { name: "info", size: 14 }) }) })
14213
14499
  ] });
14214
14500
  }
14215
14501
  function getPluginCellProps(row, column) {