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

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