@canvas-components/react-list-table 0.3.46 → 0.3.47

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.js CHANGED
@@ -1,6 +1,6 @@
1
- import { createContext, memo, forwardRef, useMemo, useRef, useState, useEffect, useImperativeHandle, useLayoutEffect, useContext, useId, Fragment as Fragment$1 } from 'react';
2
- import { createPortal } from 'react-dom';
3
- import { getSharedScrollbarSettings, subscribeSharedScrollbarSettings, setSharedScrollbarSettings, ReactImagePreview, ReactCheckbox, ReactSortableList, ReactNumberInput, ReactSelect, ReactPagination, ReactFilterPanel, ReactPopup, ReactScrollbarSettingsDialog, ReactScrollbarPositionDialog, ReactModal, ReactInput, ReactPopconfirm, ReactContentScaleProvider, ReactButton, ReactColorPicker, ReactInputNumber, resolveReactScrollbarMode, ReactAutoComplete, ReactTimeRangePicker, ReactDateTimeRangePicker, ReactDateTimePicker, ReactTimePicker, ReactDateRangePicker, ReactDatePicker, ReactMonthPicker, ReactQuarterPicker, ReactYearPicker, ReactInputRange, ReactCascader, ReactTreeSelect } from '@canvas-components/react-components';
1
+ import { createContext, memo, forwardRef, useMemo, useRef, useState, useEffect, useImperativeHandle, useLayoutEffect, useContext, useId, Fragment as Fragment$1, useCallback } from 'react';
2
+ import { createPortal, flushSync } from 'react-dom';
3
+ import { getSharedScrollbarSettings, subscribeSharedScrollbarSettings, ReactTooltip, setSharedScrollbarSettings, ReactImagePreview, ReactCheckbox, ReactSortableList, ReactInputNumber, ReactSelect, ReactPagination, ReactFilterPanel, ReactPopup, ReactScrollbarSettingsDialog, ReactScrollbarPositionDialog, ReactModal, ReactButton, ReactInput, ReactPopconfirm, ReactContentScaleProvider, ReactColorPicker, resolveReactScrollbarMode, ReactProgress, ReactAutoComplete, ReactTimeRangePicker, ReactDateTimeRangePicker, ReactDateTimePicker, ReactTimePicker, ReactDateRangePicker, ReactDatePicker, ReactMonthPicker, ReactQuarterPicker, ReactYearPicker, ReactInputRange, ReactCascader, ReactTreeSelect } from '@canvas-components/react-components';
4
4
  import { readPrintDesignTemplates, isPrintDesignDocumentCompatible, isPrintDesignTemplateInvalid, ReactPrintDesign } from '@canvas-components/react-print-design';
5
5
  import { EXPAND_COLUMN, SELECTION_COLUMN, ROW_SELECTION_COLUMN, resetListTableStoredColumnConfig, ListTable, getListTableStoredColumnConfig, saveListTableStoredColumnConfig } from '@canvas-components/list-table';
6
6
  export { EXPAND_COLUMN, ROW_SELECTION_COLUMN, SELECTION_COLUMN, getListTableStoredPageSize } from '@canvas-components/list-table';
@@ -8,8 +8,10 @@ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
8
8
  import { CanvasChart } from '@canvas-components/chart';
9
9
  import { formatDateTimeValue, isDateTimeFormatPattern } from '@canvas-components/utils/date';
10
10
  import { formatNumberValue, formatPercentValue, formatScientificValue, formatCustomNumberPattern } from '@canvas-components/utils/number';
11
- import { printPrintDesignHtml, transformPrintDesignToHtml, createPrintDesignPage, createPrintDesignDocument } from '@canvas-components/print-design';
11
+ import { mapInBatches } from '@canvas-components/utils';
12
+ import { transformPrintDesignToHtmlBatches, createPrintDesignPage, createPrintDesignDocument } from '@canvas-components/print-design';
12
13
  import { BROWSER_PRINT_PAPER_SIZES } from '@canvas-components/print';
14
+ import { createRoot } from 'react-dom/client';
13
15
 
14
16
  // #style-inject:#style-inject
15
17
  function styleInject(css, { insertAt } = {}) {
@@ -4289,16 +4291,34 @@ function mapPrintSourceRows(source) {
4289
4291
  )
4290
4292
  );
4291
4293
  }
4292
- function createTableData(source, document2, rows = mapPrintSourceRows(source)) {
4294
+ function mapPrintSourceRowsAsync(source, options) {
4295
+ return mapInBatches(
4296
+ source.rows.length,
4297
+ (index) => Object.fromEntries(
4298
+ source.columns.map((column, columnIndex) => [
4299
+ column.key,
4300
+ source.rows[index][columnIndex] ?? ""
4301
+ ])
4302
+ ),
4303
+ options
4304
+ );
4305
+ }
4306
+ function getRowsPerPage(document2, rowCount) {
4293
4307
  const firstTable = document2.pages[0]?.elements.find(
4294
4308
  (item) => item.type === "table"
4295
4309
  );
4296
- const rowsPerPage = firstTable?.type === "table" ? Math.max(
4310
+ return firstTable?.type === "table" ? Math.max(
4297
4311
  1,
4298
4312
  Math.floor(
4299
4313
  (firstTable.height - (firstTable.showHeader === false ? 0 : firstTable.headerRowHeight ?? 9)) / (firstTable.rowHeight ?? 8)
4300
4314
  )
4301
- ) : Math.max(1, Math.ceil(rows.length / document2.pages.length));
4315
+ ) : Math.max(1, Math.ceil(rowCount / Math.max(1, document2.pages.length)));
4316
+ }
4317
+ function getPrintDesignSampleSize() {
4318
+ return getRowsPerPage(createTableDocument({ columns: [], rows: [] }), 0);
4319
+ }
4320
+ function createTableData(source, document2, rows = mapPrintSourceRows(source)) {
4321
+ const rowsPerPage = getRowsPerPage(document2, rows.length);
4302
4322
  return {
4303
4323
  listTable: {
4304
4324
  rows,
@@ -4309,20 +4329,165 @@ function createTableData(source, document2, rows = mapPrintSourceRows(source)) {
4309
4329
  }
4310
4330
  };
4311
4331
  }
4332
+ async function createTableDataAsync(source, document2, rows, options) {
4333
+ const rowsPerPage = getRowsPerPage(document2, rows.length);
4334
+ const pages = await mapInBatches(
4335
+ document2.pages.length,
4336
+ (index) => ({
4337
+ rows: rows.slice(index * rowsPerPage, (index + 1) * rowsPerPage)
4338
+ }),
4339
+ options
4340
+ );
4341
+ return {
4342
+ listTable: {
4343
+ rows,
4344
+ columns: source.columns.map((column) => ({ key: column.key })),
4345
+ pages
4346
+ }
4347
+ };
4348
+ }
4349
+ async function createTablePreviewSource(source, template, options, preparedRows) {
4350
+ const rows = preparedRows ?? await mapPrintSourceRowsAsync(source, options);
4351
+ const pageCount = Math.max(
4352
+ template.pages.length,
4353
+ Math.ceil(rows.length / getRowsPerPage(template, rows.length))
4354
+ );
4355
+ const pages = await mapInBatches(
4356
+ pageCount,
4357
+ (index) => {
4358
+ const page = template.pages[index % template.pages.length];
4359
+ return {
4360
+ ...page,
4361
+ id: `${page.id}:print:${index}`,
4362
+ name: `\u8868\u683C\u6253\u5370 ${index + 1}`,
4363
+ elements: page.elements.map((element) => {
4364
+ const id = `${element.id}:print:${index}`;
4365
+ if (element.type === "table" && (element.dataPath === "listTable.rows" || /^listTable\.pages\.\d+\.rows$/.test(element.dataPath ?? ""))) {
4366
+ return {
4367
+ ...element,
4368
+ id,
4369
+ dataPath: `listTable.pages.${index}.rows`
4370
+ };
4371
+ }
4372
+ if (element.type === "text" && element.id.startsWith("list-table-page-number-")) {
4373
+ return {
4374
+ ...element,
4375
+ id,
4376
+ text: `\u7B2C ${index + 1} \u9875 / \u5171 ${pageCount} \u9875`
4377
+ };
4378
+ }
4379
+ return { ...element, id };
4380
+ })
4381
+ };
4382
+ },
4383
+ options
4384
+ );
4385
+ const document2 = { ...template, pages };
4386
+ return {
4387
+ document: document2,
4388
+ data: await createTableDataAsync(source, document2, rows, options)
4389
+ };
4390
+ }
4312
4391
  function getPrintSourceFormat(source) {
4313
4392
  return JSON.stringify(
4314
4393
  source.columns.map((column) => String(column.key)).sort()
4315
4394
  );
4316
4395
  }
4317
- function printTableTemplate(source, popup, document2 = createTableDocument(source)) {
4318
- const data = createTableData(source, document2);
4319
- printPrintDesignHtml(popup, transformPrintDesignToHtml(document2, { data }));
4320
- return true;
4321
- }
4322
4396
  function ReactListTablePrintDesignDialog(props) {
4323
4397
  const [fullscreen, setFullscreen] = useState(true);
4398
+ const [source, setSource] = useState(null);
4399
+ const [error, setError] = useState("");
4400
+ const [attempt, setAttempt] = useState(0);
4401
+ const [emptyDocument] = useState(
4402
+ () => createPrintDesignDocument({ name: "\u8868\u683C\u6253\u5370\u6A21\u677F" })
4403
+ );
4404
+ useEffect(() => {
4405
+ const controller = new AbortController();
4406
+ setSource(null);
4407
+ setError("");
4408
+ const timer = setTimeout(() => {
4409
+ void Promise.resolve().then(
4410
+ () => props.loadSource({
4411
+ rowLimit: getPrintDesignSampleSize(),
4412
+ includeSummary: false,
4413
+ signal: controller.signal
4414
+ })
4415
+ ).then((value) => {
4416
+ if (!controller.signal.aborted) setSource(value);
4417
+ }).catch((cause) => {
4418
+ if (!controller.signal.aborted)
4419
+ setError(
4420
+ cause instanceof Error ? cause.message : "\u8BFB\u53D6\u6253\u5370\u6570\u636E\u5931\u8D25"
4421
+ );
4422
+ });
4423
+ }, 0);
4424
+ return () => {
4425
+ clearTimeout(timer);
4426
+ controller.abort();
4427
+ };
4428
+ }, [props.loadSource, attempt]);
4429
+ return /* @__PURE__ */ jsx(
4430
+ ReactModal,
4431
+ {
4432
+ open: true,
4433
+ fullscreen,
4434
+ closable: false,
4435
+ fullscreenable: false,
4436
+ footer: false,
4437
+ width: "min(96vw, 1440px)",
4438
+ style: { height: fullscreen ? "100%" : "min(88vh, 900px)" },
4439
+ bodyStyle: {
4440
+ padding: 0,
4441
+ overflow: "hidden",
4442
+ display: "flex",
4443
+ position: "relative"
4444
+ },
4445
+ onCancel: props.onClose,
4446
+ children: source ? /* @__PURE__ */ jsx(
4447
+ LoadedPrintDesigner,
4448
+ {
4449
+ ...props,
4450
+ source,
4451
+ fullscreen,
4452
+ onFullscreenChange: setFullscreen
4453
+ }
4454
+ ) : /* @__PURE__ */ jsxs(Fragment, { children: [
4455
+ /* @__PURE__ */ jsx(
4456
+ ReactPrintDesign,
4457
+ {
4458
+ document: emptyDocument,
4459
+ fullscreen,
4460
+ onClose: props.onClose,
4461
+ style: { flex: 1 }
4462
+ }
4463
+ ),
4464
+ /* @__PURE__ */ jsxs(
4465
+ "div",
4466
+ {
4467
+ role: "status",
4468
+ style: {
4469
+ position: "absolute",
4470
+ inset: 0,
4471
+ background: "rgba(255,255,255,0.85)",
4472
+ display: "flex",
4473
+ alignItems: "center",
4474
+ justifyContent: "center",
4475
+ gap: 12
4476
+ },
4477
+ children: [
4478
+ /* @__PURE__ */ jsx("span", { children: error || "\u6B63\u5728\u8BFB\u53D6\u7B2C\u4E00\u9875\u6570\u636E\u2026" }),
4479
+ error ? /* @__PURE__ */ jsx(ReactButton, { onClick: () => setAttempt((value) => value + 1), children: "\u91CD\u8BD5" }) : null,
4480
+ /* @__PURE__ */ jsx(ReactButton, { onClick: props.onClose, children: "\u5173\u95ED" })
4481
+ ]
4482
+ }
4483
+ )
4484
+ ] })
4485
+ }
4486
+ );
4487
+ }
4488
+ function LoadedPrintDesigner(props) {
4324
4489
  const document2 = useMemo(
4325
- () => createTableDocument(props.source),
4490
+ () => createTableDocument({ ...props.source, rows: [] }),
4326
4491
  [props.source]
4327
4492
  );
4328
4493
  const [boundDocument, setBoundDocument] = useState(document2);
@@ -4331,7 +4496,7 @@ function ReactListTablePrintDesignDialog(props) {
4331
4496
  () => [
4332
4497
  document2,
4333
4498
  ...props.printPresets?.map(
4334
- (preset) => createTableDocument(props.source, preset)
4499
+ (preset) => createTableDocument({ ...props.source, rows: [] }, preset)
4335
4500
  ) ?? [],
4336
4501
  ...props.printTemplates ?? []
4337
4502
  ],
@@ -4341,35 +4506,408 @@ function ReactListTablePrintDesignDialog(props) {
4341
4506
  () => createTableData(props.source, boundDocument, rows),
4342
4507
  [props.source, boundDocument, rows]
4343
4508
  );
4509
+ const loadPreviewData = useCallback(
4510
+ async (template, options) => {
4511
+ const source = await props.loadSource(options);
4512
+ options.signal?.throwIfAborted();
4513
+ return createTablePreviewSource(source, template, options);
4514
+ },
4515
+ [props.loadSource]
4516
+ );
4344
4517
  return /* @__PURE__ */ jsx(
4345
- ReactModal,
4518
+ ReactPrintDesign,
4346
4519
  {
4347
- open: true,
4348
- fullscreen,
4349
- closable: false,
4350
- fullscreenable: false,
4351
- footer: false,
4352
- width: "min(96vw, 1440px)",
4353
- style: { height: fullscreen ? "100%" : "min(88vh, 900px)" },
4354
- bodyStyle: { padding: 0, overflow: "hidden", display: "flex" },
4355
- onCancel: props.onClose,
4356
- children: /* @__PURE__ */ jsx(
4357
- ReactPrintDesign,
4358
- {
4359
- document: document2,
4360
- defaultTemplates,
4361
- data,
4362
- dataFormatKey: getPrintSourceFormat(props.source),
4363
- onChange: setBoundDocument,
4364
- storeKey: props.storeKey,
4365
- fullscreen,
4366
- onFullscreenChange: setFullscreen,
4367
- onClose: props.onClose,
4368
- style: { flex: 1 }
4520
+ document: document2,
4521
+ defaultTemplates,
4522
+ data,
4523
+ dataFormatKey: getPrintSourceFormat(props.source),
4524
+ onChange: setBoundDocument,
4525
+ storeKey: props.storeKey,
4526
+ loadPreviewData,
4527
+ fullscreen: props.fullscreen,
4528
+ onFullscreenChange: props.onFullscreenChange,
4529
+ onClose: props.onClose,
4530
+ style: { flex: 1 }
4531
+ }
4532
+ );
4533
+ }
4534
+ var PRINT_PROGRESS_MIN_DURATION = 500;
4535
+ function createPrintProgress(container, onCancel) {
4536
+ const host = container.ownerDocument.createElement("div");
4537
+ host.dataset.listTablePrintProgress = "";
4538
+ container.append(host);
4539
+ const root = createRoot(host);
4540
+ const startedAt = performance.now();
4541
+ let disposed = false;
4542
+ let open = true;
4543
+ let message = "\u6B63\u5728\u8BFB\u53D6\u6253\u5370\u6570\u636E";
4544
+ let percent = 0;
4545
+ let failed = false;
4546
+ let closeTimer;
4547
+ let resolveClose;
4548
+ let configuring = false;
4549
+ let batchSize = 50;
4550
+ let action;
4551
+ const confirm = () => {
4552
+ const pending = action;
4553
+ action = void 0;
4554
+ configuring = false;
4555
+ render();
4556
+ pending?.resolve();
4557
+ };
4558
+ const dispose = () => {
4559
+ if (disposed) return;
4560
+ disposed = true;
4561
+ clearTimeout(closeTimer);
4562
+ root.unmount();
4563
+ host.remove();
4564
+ resolveClose?.();
4565
+ action?.resolve();
4566
+ action = void 0;
4567
+ };
4568
+ const render = () => {
4569
+ if (disposed) return;
4570
+ flushSync(
4571
+ () => root.render(
4572
+ /* @__PURE__ */ jsxs(
4573
+ ReactModal,
4574
+ {
4575
+ open,
4576
+ title: configuring ? "\u76F4\u63A5\u6253\u5370\u8BBE\u7F6E" : failed ? "\u6253\u5370\u5931\u8D25" : "\u6B63\u5728\u51C6\u5907\u6253\u5370",
4577
+ width: 440,
4578
+ centered: true,
4579
+ maskClosable: false,
4580
+ fullscreenable: false,
4581
+ getContainer: container,
4582
+ onCancel,
4583
+ afterClose: () => queueMicrotask(dispose),
4584
+ footer: /* @__PURE__ */ jsxs(Fragment, { children: [
4585
+ /* @__PURE__ */ jsx(ReactButton, { onClick: onCancel, children: failed ? "\u5173\u95ED" : "\u53D6\u6D88\u6253\u5370" }),
4586
+ action && /* @__PURE__ */ jsx(
4587
+ ReactButton,
4588
+ {
4589
+ disabled: configuring && (!batchSize || !Number.isInteger(batchSize) || batchSize < 1),
4590
+ onClick: confirm,
4591
+ children: action.label
4592
+ }
4593
+ )
4594
+ ] }),
4595
+ children: [
4596
+ /* @__PURE__ */ jsx("p", { role: "status", "aria-live": "polite", children: message }),
4597
+ configuring ? /* @__PURE__ */ jsxs("label", { style: { display: "flex", alignItems: "center", gap: 12 }, children: [
4598
+ /* @__PURE__ */ jsx("span", { style: { whiteSpace: "nowrap", flexShrink: 0 }, children: "\u6BCF\u6B21\u6253\u5370\u9875\u6570" }),
4599
+ /* @__PURE__ */ jsx(
4600
+ ReactInputNumber,
4601
+ {
4602
+ "aria-label": "\u6BCF\u6B21\u6253\u5370\u9875\u6570",
4603
+ style: { flex: 1, minWidth: 0 },
4604
+ min: 1,
4605
+ precision: 0,
4606
+ value: batchSize,
4607
+ onChange: (value) => {
4608
+ batchSize = value;
4609
+ render();
4610
+ }
4611
+ }
4612
+ )
4613
+ ] }) : /* @__PURE__ */ jsx(
4614
+ ReactProgress,
4615
+ {
4616
+ percent,
4617
+ label: "\u6253\u5370\u51C6\u5907\u8FDB\u5EA6",
4618
+ status: failed ? "exception" : "normal"
4619
+ }
4620
+ )
4621
+ ]
4622
+ }
4623
+ )
4624
+ )
4625
+ );
4626
+ };
4627
+ render();
4628
+ return {
4629
+ host,
4630
+ async configure(defaultPages) {
4631
+ configuring = true;
4632
+ batchSize = Number.isFinite(defaultPages) ? Math.max(1, Math.floor(defaultPages)) : 50;
4633
+ message = "\u6309\u6279\u6B21\u751F\u6210\u6253\u5370\u9875\u9762\uFF0C\u907F\u514D\u4E00\u6B21\u52A0\u8F7D\u8FC7\u591A\u5185\u5BB9\u3002";
4634
+ await new Promise((resolve) => {
4635
+ action = { label: "\u5F00\u59CB\u6253\u5370", resolve };
4636
+ render();
4637
+ });
4638
+ return batchSize ?? 50;
4639
+ },
4640
+ async continueBatch(firstPage, lastPage) {
4641
+ message = `\u7B2C ${firstPage}\u2013${lastPage} \u9875\u7684\u6253\u5370\u7A97\u53E3\u5DF2\u5173\u95ED\uFF0C\u662F\u5426\u7EE7\u7EED\u4E0B\u4E00\u6279\uFF1F`;
4642
+ await new Promise((resolve) => {
4643
+ action = { label: "\u7EE7\u7EED\u4E0B\u4E00\u6279", resolve };
4644
+ render();
4645
+ });
4646
+ },
4647
+ update(text, value) {
4648
+ if (percent === value && message === text) return;
4649
+ message = text;
4650
+ percent = value;
4651
+ render();
4652
+ },
4653
+ error(text) {
4654
+ message = text;
4655
+ failed = true;
4656
+ render();
4657
+ },
4658
+ close(immediate = false) {
4659
+ if (disposed) return Promise.resolve();
4660
+ if (immediate) {
4661
+ dispose();
4662
+ return Promise.resolve();
4663
+ }
4664
+ return new Promise((resolve) => {
4665
+ resolveClose = resolve;
4666
+ closeTimer = setTimeout(
4667
+ () => {
4668
+ open = false;
4669
+ render();
4670
+ },
4671
+ Math.max(
4672
+ 0,
4673
+ PRINT_PROGRESS_MIN_DURATION - (performance.now() - startedAt)
4674
+ )
4675
+ );
4676
+ });
4677
+ }
4678
+ };
4679
+ }
4680
+ var PRINT_ROWS_WORKER_SOURCE = `
4681
+ self.onmessage = function(event) {
4682
+ try {
4683
+ const { keys, rows } = event.data;
4684
+ self.postMessage({ rows: rows.map(row => Object.fromEntries(keys.map((key, index) => [key, row[index] ?? ""]))) });
4685
+ } catch (error) {
4686
+ self.postMessage({ error: String(error) });
4687
+ }
4688
+ };`;
4689
+ async function preparePrintRows(source, options = {}) {
4690
+ const keys = source.columns.map((column) => column.key);
4691
+ let worker;
4692
+ let url;
4693
+ try {
4694
+ options.signal?.throwIfAborted();
4695
+ url = URL.createObjectURL(
4696
+ new Blob([PRINT_ROWS_WORKER_SOURCE], { type: "text/javascript" })
4697
+ );
4698
+ worker = new Worker(url);
4699
+ const result = new Array(source.rows.length);
4700
+ for (let offset = 0; offset < source.rows.length; offset += 2e3) {
4701
+ options.signal?.throwIfAborted();
4702
+ const rows = await new Promise(
4703
+ (resolve, reject) => {
4704
+ const cleanup = () => {
4705
+ clearTimeout(timeout);
4706
+ options.signal?.removeEventListener("abort", abort);
4707
+ };
4708
+ const fail = (error) => {
4709
+ cleanup();
4710
+ reject(error);
4711
+ };
4712
+ const abort = () => fail(options.signal?.reason);
4713
+ const timeout = setTimeout(
4714
+ () => fail(new Error("\u6253\u5370 Worker \u54CD\u5E94\u8D85\u65F6")),
4715
+ 15e3
4716
+ );
4717
+ options.signal?.addEventListener("abort", abort, { once: true });
4718
+ worker.onerror = () => fail(new Error("\u6253\u5370 Worker \u6267\u884C\u5931\u8D25"));
4719
+ worker.onmessageerror = () => fail(new Error("\u6253\u5370 Worker \u6570\u636E\u4F20\u8F93\u5931\u8D25"));
4720
+ worker.onmessage = (event) => {
4721
+ cleanup();
4722
+ if (event.data.error) reject(new Error(event.data.error));
4723
+ else resolve(event.data.rows);
4724
+ };
4725
+ try {
4726
+ worker.postMessage({
4727
+ keys,
4728
+ rows: source.rows.slice(offset, offset + 2e3)
4729
+ });
4730
+ } catch (error) {
4731
+ fail(error);
4732
+ }
4369
4733
  }
4734
+ );
4735
+ for (let index = 0; index < rows.length; index++)
4736
+ result[offset + index] = rows[index];
4737
+ options.onProgress?.(
4738
+ Math.min(offset + rows.length, source.rows.length),
4739
+ source.rows.length
4740
+ );
4741
+ }
4742
+ return result;
4743
+ } catch {
4744
+ worker?.terminate();
4745
+ worker = void 0;
4746
+ options.signal?.throwIfAborted();
4747
+ return await mapInBatches(
4748
+ source.rows.length,
4749
+ (index) => Object.fromEntries(
4750
+ keys.map((key, column) => [key, source.rows[index][column] ?? ""])
4751
+ ),
4752
+ options
4753
+ );
4754
+ } finally {
4755
+ worker?.terminate();
4756
+ if (url) URL.revokeObjectURL(url);
4757
+ }
4758
+ }
4759
+
4760
+ // src/features/print/direct-print.ts
4761
+ async function runDirectPrint(container, loadSource, resolveTemplate = (source) => createTableDocument({ ...source, rows: [] }), validate, defaultPagesPerBatch = 50) {
4762
+ const controller = new AbortController();
4763
+ const { signal } = controller;
4764
+ const ownerDocument = container.ownerDocument;
4765
+ const previousFocus = ownerDocument.activeElement;
4766
+ let frame;
4767
+ const progress = createPrintProgress(container, () => {
4768
+ controller.abort();
4769
+ void cleanup(true);
4770
+ });
4771
+ const cleanup = async (immediate = false) => {
4772
+ frame?.remove();
4773
+ await progress.close(immediate);
4774
+ if (previousFocus?.isConnected)
4775
+ previousFocus.focus({ preventScroll: true });
4776
+ };
4777
+ const timer = setInterval(() => {
4778
+ if (!progress.host.isConnected) controller.abort();
4779
+ }, 250);
4780
+ let maximum = 0;
4781
+ const report = (text, value) => {
4782
+ signal.throwIfAborted();
4783
+ maximum = Math.max(maximum, Math.floor(value));
4784
+ progress.update(text, maximum);
4785
+ };
4786
+ try {
4787
+ const pagesPerBatch = await progress.configure(defaultPagesPerBatch);
4788
+ signal.throwIfAborted();
4789
+ report("\u6B63\u5728\u8BFB\u53D6\u6253\u5370\u6570\u636E", 0);
4790
+ await new Promise((resolve) => setTimeout(resolve, 0));
4791
+ const source = await loadSource({
4792
+ signal,
4793
+ onProgress: (done, total) => report(
4794
+ `\u6B63\u5728\u8BFB\u53D6\u6253\u5370\u6570\u636E ${done}/${total}`,
4795
+ total ? done / total * 30 : 30
4370
4796
  )
4797
+ });
4798
+ report("\u6B63\u5728\u8F6C\u6362\u6253\u5370\u6570\u636E", 30);
4799
+ const rows = await preparePrintRows(source, {
4800
+ signal,
4801
+ onProgress: (done, total) => report(
4802
+ `\u6B63\u5728\u8F6C\u6362\u6253\u5370\u6570\u636E ${done}/${total}`,
4803
+ 30 + (total ? done / total : 1) * 25
4804
+ )
4805
+ });
4806
+ const template = resolveTemplate(source);
4807
+ validate?.(source, template, createTableData(source, template, rows));
4808
+ report("\u6B63\u5728\u751F\u6210\u5206\u9875", 55);
4809
+ const prepared = await createTablePreviewSource(
4810
+ source,
4811
+ template,
4812
+ {
4813
+ signal,
4814
+ onProgress: (done, total) => report(
4815
+ `\u6B63\u5728\u751F\u6210\u5206\u9875 ${done}/${total}`,
4816
+ 55 + (total ? done / total : 1) * 15
4817
+ )
4818
+ },
4819
+ rows
4820
+ );
4821
+ report("\u6B63\u5728\u751F\u6210\u6253\u5370\u9875\u9762", 70);
4822
+ for await (const batch of transformPrintDesignToHtmlBatches(
4823
+ prepared.document,
4824
+ {
4825
+ pagesPerBatch,
4826
+ data: prepared.data,
4827
+ signal,
4828
+ onProgress: (value) => report("\u6B63\u5728\u751F\u6210\u6253\u5370\u9875\u9762", 70 + value * 29)
4829
+ }
4830
+ )) {
4831
+ report(`\u6B63\u5728\u6253\u5370\u7B2C ${batch.firstPage}\u2013${batch.lastPage} \u9875`, maximum);
4832
+ await new Promise((resolve) => setTimeout(resolve, 0));
4833
+ signal.throwIfAborted();
4834
+ frame = ownerDocument.createElement("iframe");
4835
+ frame.title = "\u8868\u683C\u6253\u5370";
4836
+ frame.setAttribute("aria-hidden", "true");
4837
+ frame.style.cssText = "position:fixed;left:-10000px;top:0;width:1px;height:1px;border:0";
4838
+ container.append(frame);
4839
+ const printWindow = frame.contentWindow;
4840
+ if (!printWindow) throw new Error("\u65E0\u6CD5\u521B\u5EFA\u6253\u5370\u9875\u9762");
4841
+ await new Promise((resolve, reject) => {
4842
+ const abort = () => {
4843
+ dispose();
4844
+ reject(signal.reason);
4845
+ };
4846
+ const dispose = () => {
4847
+ frame?.removeEventListener("load", loaded);
4848
+ signal.removeEventListener("abort", abort);
4849
+ };
4850
+ const loaded = () => {
4851
+ dispose();
4852
+ resolve();
4853
+ };
4854
+ frame.addEventListener("load", loaded, { once: true });
4855
+ signal.addEventListener("abort", abort, { once: true });
4856
+ printWindow.document.open();
4857
+ printWindow.document.write(batch.html);
4858
+ printWindow.document.close();
4859
+ });
4860
+ await Promise.all([
4861
+ ...Array.from(
4862
+ printWindow.document.images,
4863
+ (image) => image.decode?.().catch(() => void 0)
4864
+ ),
4865
+ printWindow.document.fonts?.ready
4866
+ ]);
4867
+ signal.throwIfAborted();
4868
+ await new Promise((resolve, reject) => {
4869
+ const dispose = () => {
4870
+ printWindow.removeEventListener("afterprint", finished);
4871
+ signal.removeEventListener("abort", aborted);
4872
+ };
4873
+ const finished = () => {
4874
+ dispose();
4875
+ resolve();
4876
+ };
4877
+ const aborted = () => {
4878
+ dispose();
4879
+ reject(signal.reason);
4880
+ };
4881
+ printWindow.addEventListener("afterprint", finished, { once: true });
4882
+ signal.addEventListener("abort", aborted, { once: true });
4883
+ try {
4884
+ printWindow.focus();
4885
+ printWindow.print();
4886
+ } catch (error) {
4887
+ dispose();
4888
+ reject(error);
4889
+ }
4890
+ });
4891
+ frame.remove();
4892
+ frame = void 0;
4893
+ if (batch.hasMore) {
4894
+ await progress.continueBatch(batch.firstPage, batch.lastPage);
4895
+ signal.throwIfAborted();
4896
+ report("\u6B63\u5728\u751F\u6210\u4E0B\u4E00\u6279\u6253\u5370\u9875\u9762", maximum);
4897
+ }
4371
4898
  }
4372
- );
4899
+ report("\u6253\u5370\u7A97\u53E3\u5DF2\u5173\u95ED", 100);
4900
+ await cleanup();
4901
+ } catch (error) {
4902
+ frame?.remove();
4903
+ if (!signal.aborted) {
4904
+ progress.error(
4905
+ `\u6253\u5370\u5931\u8D25\uFF1A${error instanceof Error ? error.message : String(error)}`
4906
+ );
4907
+ } else await cleanup(true);
4908
+ } finally {
4909
+ clearInterval(timer);
4910
+ }
4373
4911
  }
4374
4912
 
4375
4913
  // src/features/editor/utils.ts
@@ -4412,12 +4950,13 @@ function getEditorInputStyle(request) {
4412
4950
  height,
4413
4951
  minHeight: height,
4414
4952
  color: request.theme.cellColor ?? request.theme.textColor,
4953
+ textAlign: request.theme.textAlign,
4415
4954
  fontFamily: request.theme.fontFamily,
4416
4955
  fontSize,
4417
4956
  // 用像素 token,确保 select/time/color 触发器与内部 Input 同步缩放。
4418
4957
  ["--canvas-react-control-height"]: `${height}px`,
4419
4958
  ["--canvas-react-font-size"]: fontSize != null ? `${fontSize}px` : void 0,
4420
- ["--canvas-react-control-padding-inline"]: `${paddingInline}px`,
4959
+ ["--canvas-react-control-padding-inline"]: `${Math.max(paddingInline - 1, 0)}px`,
4421
4960
  ["--canvas-react-control-padding-block"]: `${paddingBlock}px`,
4422
4961
  ["--canvas-react-content-zoom"]: String(contentZoom),
4423
4962
  ["--canvas-react-radius-sm"]: "0px",
@@ -4444,7 +4983,7 @@ function resolveEditorOverlayStyle(params) {
4444
4983
  boxSizing: "border-box",
4445
4984
  // 覆盖表单组件 token,使输入框高度/字号/内边距跟随表格缩放。
4446
4985
  ["--canvas-react-control-height"]: `${height}px`,
4447
- ["--canvas-react-control-padding-inline"]: `${resolvedPaddingInline}px`,
4986
+ ["--canvas-react-control-padding-inline"]: `${Math.max(resolvedPaddingInline - 1, 0)}px`,
4448
4987
  ["--canvas-react-control-padding-block"]: `${resolvedPaddingBlock}px`,
4449
4988
  ["--canvas-react-content-zoom"]: String(resolvedContentZoom),
4450
4989
  ["--canvas-react-radius-sm"]: "0px",
@@ -4596,9 +5135,10 @@ function NumberEditor(props) {
4596
5135
  setValue(toNumberValue(request.value));
4597
5136
  }, [request.value]);
4598
5137
  return /* @__PURE__ */ jsx("div", { ref: hostRef, style, children: /* @__PURE__ */ jsx(
4599
- ReactNumberInput,
5138
+ ReactInputNumber,
4600
5139
  {
4601
5140
  value,
5141
+ initialText: typeof request.value === "string" ? request.value : void 0,
4602
5142
  min: toNumericProp(request.config.min),
4603
5143
  max: toNumericProp(request.config.max),
4604
5144
  step: toNumericProp(request.config.step),
@@ -5476,47 +6016,6 @@ function ReactTableDangerConfirmView(props) {
5476
6016
  }
5477
6017
  );
5478
6018
  }
5479
- function ReactTableTooltipView(props) {
5480
- const { request } = props;
5481
- const tooltipRef = useRef(null);
5482
- useLayoutEffect(() => {
5483
- const tooltip = tooltipRef.current;
5484
- if (!tooltip || !request || typeof window === "undefined") return;
5485
- const width = tooltip.getBoundingClientRect().width;
5486
- const viewportWidth = window.innerWidth;
5487
- tooltip.style.left = `${Math.max(8, Math.min(request.x - width / 2, viewportWidth - width - 8))}px`;
5488
- }, [request]);
5489
- if (!request) {
5490
- return null;
5491
- }
5492
- const zoom = typeof request.contentZoom === "number" && Number.isFinite(request.contentZoom) && request.contentZoom > 0 ? request.contentZoom : 1;
5493
- const fontSize = 14 * zoom;
5494
- const paddingBlock = 6 * zoom;
5495
- const paddingInline = 10 * zoom;
5496
- const borderRadius = 6 * zoom;
5497
- const maxWidth = 320 * zoom;
5498
- const style = {
5499
- position: "fixed",
5500
- left: request.x,
5501
- top: request.y,
5502
- maxWidth: request.variant === "resize" ? "calc(100vw - 16px)" : `min(${maxWidth}px, calc(100vw - 16px))`,
5503
- padding: `${paddingBlock}px ${paddingInline}px`,
5504
- borderRadius,
5505
- background: request.variant === "error" ? "#ffffff" : "rgba(17, 24, 39, 0.92)",
5506
- color: request.variant === "error" ? "#ff4d4f" : "#fff",
5507
- fontSize,
5508
- lineHeight: 1.5,
5509
- boxShadow: request.variant === "error" ? "0 10px 30px rgba(255, 77, 79, 0.16)" : "0 10px 30px rgba(15, 23, 42, 0.18)",
5510
- border: request.variant === "error" ? "1px solid rgba(255, 77, 79, 0.2)" : "none",
5511
- pointerEvents: "none",
5512
- zIndex: 1200,
5513
- whiteSpace: request.variant === "resize" ? "nowrap" : "pre-wrap",
5514
- wordBreak: "break-word",
5515
- overflow: "hidden",
5516
- textOverflow: request.variant === "resize" ? "ellipsis" : void 0
5517
- };
5518
- return /* @__PURE__ */ jsx("div", { ref: tooltipRef, style, children: request.content });
5519
- }
5520
6019
  function ToolbarIcon(props) {
5521
6020
  const { path, ...svgProps } = props;
5522
6021
  return /* @__PURE__ */ jsx(
@@ -5763,13 +6262,12 @@ function ReactTableToolbar(props) {
5763
6262
  ),
5764
6263
  !column.isGroup ? /* @__PURE__ */ jsxs("span", { className: "react-list-table-column-actions", children: [
5765
6264
  /* @__PURE__ */ jsx(
5766
- ReactNumberInput,
6265
+ ReactInputNumber,
5767
6266
  {
5768
6267
  className: "react-list-table-column-width-input",
5769
6268
  min: column.minWidth,
5770
6269
  max: column.maxWidth,
5771
6270
  value: column.width,
5772
- allowClear: false,
5773
6271
  "aria-label": `${column.label}\u5217\u5BBD`,
5774
6272
  style: { width: 80, height: 24 },
5775
6273
  onChange: (width) => {
@@ -6521,9 +7019,8 @@ function ReactListTableInner(props, ref) {
6521
7019
  closeDangerConfirmRequest
6522
7020
  } = useReactListTableState(options);
6523
7021
  const hasPagination = paginationConfig != null;
6524
- const [printSource, setPrintSource] = useState(null);
7022
+ const [printRequest, setPrintRequest] = useState(null);
6525
7023
  const [savedPrintTemplates, setSavedPrintTemplates] = useState([]);
6526
- const [printMenuSource, setPrintMenuSource] = useState(null);
6527
7024
  const printStoreKey = storageKey ? `list:${storageKey}` : void 0;
6528
7025
  const printMenuOpen = Boolean(headerContextMenuRequest);
6529
7026
  useEffect(() => {
@@ -6541,28 +7038,6 @@ function ReactListTableInner(props, ref) {
6541
7038
  disposed = true;
6542
7039
  };
6543
7040
  }, [printMenuOpen, printStoreKey]);
6544
- useEffect(() => {
6545
- if (!printMenuOpen) {
6546
- setPrintMenuSource(null);
6547
- return;
6548
- }
6549
- const getPrintData = headerContextMenuRequest?.onGetPrintData;
6550
- if (!getPrintData) return;
6551
- let disposed = false;
6552
- void Promise.resolve(getPrintData()).then((source) => {
6553
- if (!disposed) setPrintMenuSource(source);
6554
- }).catch(() => {
6555
- if (!disposed) setPrintMenuSource(null);
6556
- });
6557
- return () => {
6558
- disposed = true;
6559
- };
6560
- }, [printMenuOpen]);
6561
- const printDataFormat = printMenuSource ? getPrintSourceFormat(printMenuSource) : void 0;
6562
- const printMenuRows = useMemo(
6563
- () => printMenuSource ? mapPrintSourceRows(printMenuSource) : [],
6564
- [printMenuSource]
6565
- );
6566
7041
  const latestSavedTemplates = [
6567
7042
  ...new Set(savedPrintTemplates.map((item) => item.templateId))
6568
7043
  ].map((templateId) => {
@@ -6577,33 +7052,14 @@ function ReactListTableInner(props, ref) {
6577
7052
  key: `preset:${index}`,
6578
7053
  label: item.name
6579
7054
  })) ?? [],
6580
- ...props.printTemplates?.map((item, index) => {
6581
- const invalid = Boolean(
6582
- printMenuSource && !isPrintDesignDocumentCompatible(
6583
- item,
6584
- createTableData(printMenuSource, item, printMenuRows)
6585
- )
6586
- );
6587
- return {
6588
- key: `document:${index}`,
6589
- label: `${item.name}${invalid ? "\uFF08\u5DF2\u5931\u6548\uFF09" : ""}`,
6590
- disabled: invalid
6591
- };
6592
- }) ?? [],
6593
- ...latestSavedTemplates.map((item) => {
6594
- const invalid = Boolean(
6595
- printMenuSource && isPrintDesignTemplateInvalid(
6596
- item,
6597
- printDataFormat,
6598
- createTableData(printMenuSource, item.document, printMenuRows)
6599
- )
6600
- );
6601
- return {
6602
- key: `saved:${item.id}`,
6603
- label: `${item.document.name} (v${item.version})${invalid ? "\uFF08\u5DF2\u5931\u6548\uFF09" : ""}`,
6604
- disabled: invalid
6605
- };
6606
- })
7055
+ ...props.printTemplates?.map((item, index) => ({
7056
+ key: `document:${index}`,
7057
+ label: item.name
7058
+ })) ?? [],
7059
+ ...latestSavedTemplates.map((item) => ({
7060
+ key: `saved:${item.id}`,
7061
+ label: `${item.document.name} (v${item.version})`
7062
+ }))
6607
7063
  ];
6608
7064
  const [cacheConfigOpen, setCacheConfigOpen] = useState(false);
6609
7065
  const paginationLayout = resolveReactListTablePaginationLayout(resolvedRowHeight);
@@ -6778,7 +7234,7 @@ function ReactListTableInner(props, ref) {
6778
7234
  }
6779
7235
  ),
6780
7236
  tooltipRequest ? createPortal(
6781
- /* @__PURE__ */ jsx(ReactTableTooltipView, { request: tooltipRequest }),
7237
+ /* @__PURE__ */ jsx(ReactTooltip, { request: tooltipRequest }),
6782
7238
  portalContainer
6783
7239
  ) : null,
6784
7240
  filterPanelRequest ? createPortal(
@@ -6798,30 +7254,31 @@ function ReactListTableInner(props, ref) {
6798
7254
  const request = headerContextMenuRequest;
6799
7255
  if (!request?.onGetPrintData) return;
6800
7256
  request.onClose();
6801
- const popup = window.open("", "_blank");
6802
- if (!popup) return;
6803
- void Promise.resolve(request.onGetPrintData()).then((source) => {
6804
- const document2 = key.startsWith("saved:") ? savedPrintTemplates.find((item) => item.id === key.slice(6))?.document : key.startsWith("preset:") ? createTableDocument(
6805
- source,
6806
- props.printPresets?.[Number(key.slice(7))]
6807
- ) : key.startsWith("document:") ? props.printTemplates?.[Number(key.slice(9))] : createTableDocument(source);
6808
- if (!document2) throw new Error("\u627E\u4E0D\u5230\u6253\u5370\u6A21\u677F");
6809
- const data = createTableData(source, document2);
6810
- if (!isPrintDesignDocumentCompatible(document2, data))
6811
- throw new Error("\u6253\u5370\u6A21\u677F\u5DF2\u5931\u6548");
6812
- if (key.startsWith("saved:") && isPrintDesignTemplateInvalid(
6813
- savedPrintTemplates.find(
6814
- (item) => item.id === key.slice(6)
6815
- ),
6816
- getPrintSourceFormat(source),
6817
- data
6818
- ))
6819
- throw new Error("\u6253\u5370\u6A21\u677F\u5DF2\u5931\u6548");
6820
- printTableTemplate(source, popup, document2);
6821
- }).catch((error) => {
6822
- popup.close();
6823
- console.error("\u76F4\u63A5\u6253\u5370\u5931\u8D25", error);
6824
- });
7257
+ void runDirectPrint(
7258
+ portalContainer,
7259
+ request.onGetPrintData,
7260
+ (source) => {
7261
+ const document2 = key.startsWith("saved:") ? savedPrintTemplates.find((item) => item.id === key.slice(6))?.document : key.startsWith("preset:") ? createTableDocument(
7262
+ { ...source, rows: [] },
7263
+ props.printPresets?.[Number(key.slice(7))]
7264
+ ) : key.startsWith("document:") ? props.printTemplates?.[Number(key.slice(9))] : createTableDocument({ ...source, rows: [] });
7265
+ if (!document2) throw new Error("\u627E\u4E0D\u5230\u6253\u5370\u6A21\u677F");
7266
+ return document2;
7267
+ },
7268
+ (source, document2, data) => {
7269
+ if (!isPrintDesignDocumentCompatible(document2, data))
7270
+ throw new Error("\u6253\u5370\u6A21\u677F\u5DF2\u5931\u6548");
7271
+ if (key.startsWith("saved:") && isPrintDesignTemplateInvalid(
7272
+ savedPrintTemplates.find(
7273
+ (item) => item.id === key.slice(6)
7274
+ ),
7275
+ getPrintSourceFormat(source),
7276
+ data
7277
+ ))
7278
+ throw new Error("\u6253\u5370\u6A21\u677F\u5DF2\u5931\u6548");
7279
+ },
7280
+ props.directPrintPagesPerBatch
7281
+ );
6825
7282
  },
6826
7283
  onViewCacheConfig: () => {
6827
7284
  headerContextMenuRequest?.onClose();
@@ -6830,21 +7287,20 @@ function ReactListTableInner(props, ref) {
6830
7287
  onPrint: () => {
6831
7288
  const getPrintData = headerContextMenuRequest?.onGetPrintData;
6832
7289
  if (!getPrintData) return;
6833
- void Promise.resolve(getPrintData()).then(setPrintSource);
7290
+ setPrintRequest({ loadSource: getPrintData });
6834
7291
  headerContextMenuRequest?.onClose();
6835
7292
  },
6836
7293
  onDirectPrint: () => {
6837
7294
  const request = headerContextMenuRequest;
6838
7295
  if (!request?.onGetPrintData) return;
6839
7296
  request.onClose();
6840
- const popup = window.open("", "_blank");
6841
- if (!popup) return;
6842
- void Promise.resolve(request.onGetPrintData()).then((source) => {
6843
- printTableTemplate(source, popup);
6844
- }).catch((error) => {
6845
- popup.close();
6846
- console.error("\u76F4\u63A5\u6253\u5370\u5931\u8D25", error);
6847
- });
7297
+ void runDirectPrint(
7298
+ portalContainer,
7299
+ request.onGetPrintData,
7300
+ void 0,
7301
+ void 0,
7302
+ props.directPrintPagesPerBatch
7303
+ );
6848
7304
  },
6849
7305
  request: headerContextMenuRequest ? {
6850
7306
  ...headerContextMenuRequest,
@@ -6866,15 +7322,15 @@ function ReactListTableInner(props, ref) {
6866
7322
  ),
6867
7323
  portalContainer
6868
7324
  ),
6869
- printSource ? createPortal(
7325
+ printRequest ? createPortal(
6870
7326
  /* @__PURE__ */ jsx(
6871
7327
  ReactListTablePrintDesignDialog,
6872
7328
  {
6873
- source: printSource,
7329
+ loadSource: printRequest.loadSource,
6874
7330
  storeKey: storageKey ? `list:${storageKey}` : void 0,
6875
7331
  printPresets: props.printPresets,
6876
7332
  printTemplates: props.printTemplates,
6877
- onClose: () => setPrintSource(null)
7333
+ onClose: () => setPrintRequest(null)
6878
7334
  }
6879
7335
  ),
6880
7336
  portalContainer