@a3s-lab/office 0.37.1 → 0.37.3

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.
@@ -1,7 +1,7 @@
1
1
  import { __webpack_require__ } from "./0~rslib-runtime.js";
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import { Workbook } from "@fortune-sheet/react";
4
- import { AArrowDown, AArrowUp, AlignCenter, AlignLeft, AlignRight, AlignVerticalJustifyCenter, AlignVerticalJustifyEnd, AlignVerticalJustifyStart, ArrowDown, ArrowDownToLine, ArrowLeft, ArrowLeftToLine, ArrowRight, ArrowRightToLine, ArrowUp, ArrowUpToLine, BadgeJapaneseYen, BarChart3, BetweenHorizontalEnd, BetweenHorizontalStart, BetweenVerticalEnd, BetweenVerticalStart, Bold, Bookmark, Calculator, CalendarClock, CalendarDays, Check, ChevronDown, ChevronLeft, ChevronRight, ClipboardPaste, Clock, Cloud, Columns3, Copy, CopyPlus, DecimalsArrowLeft, DecimalsArrowRight, Eraser, Eye, EyeOff, FileX2, Globe2, Grid2X2, Grid3X3, Hash, Info, Italic, KeyRound, Layers2, Link2, Link2Off, ListChecks, ListFilter, ListPlus, LocateFixed, Merge, MessageSquareX, MoveHorizontal, MoveVertical, Paintbrush, Palette, PanelLeft, PanelTop, PanelsTopLeft, Pencil, Percent, Plus, Printer, Redo2, RefreshCw, Rows3, Save, Scissors, Search, Settings2, ShieldCheck, Sigma, SlidersHorizontal, SortAsc, SortDesc, Strikethrough, SwatchBook, Table2, TableProperties, Trash2, TriangleAlert, Underline, Undo2, WrapText, X } from "lucide-react";
4
+ import { AArrowDown, AArrowUp, AlignCenter, AlignLeft, AlignRight, AlignVerticalJustifyCenter, AlignVerticalJustifyEnd, AlignVerticalJustifyStart, ArrowDown, ArrowDownToLine, ArrowLeft, ArrowLeftToLine, ArrowRight, ArrowRightToLine, ArrowUp, ArrowUpToLine, BadgeJapaneseYen, BarChart3, BetweenHorizontalEnd, BetweenHorizontalStart, BetweenVerticalEnd, BetweenVerticalStart, Bold, Bookmark, Calculator, CalendarClock, CalendarDays, Check, ChevronDown, ChevronLeft, ChevronRight, ClipboardPaste, Clock, Cloud, Columns3, Copy, CopyPlus, DecimalsArrowLeft, DecimalsArrowRight, Eraser, Eye, EyeOff, FileX2, Globe2, Grid2X2, Grid3X3, Hash, Info, Italic, KeyRound, Layers2, Link2, Link2Off, ListChecks, ListFilter, ListOrdered, ListPlus, LocateFixed, Merge, MessageSquareX, MoveHorizontal, MoveVertical, Paintbrush, Palette, PanelLeft, PanelTop, PanelsTopLeft, Pencil, Percent, Plus, Printer, Redo2, RefreshCw, Rows3, Save, Scissors, Search, Settings2, ShieldCheck, Sigma, SlidersHorizontal, SortAsc, SortDesc, Strikethrough, SwatchBook, Table2, TableProperties, Trash2, TriangleAlert, Underline, Undo2, WrapText, X } from "lucide-react";
5
5
  import { Fragment as external_react_Fragment, cloneElement, createElement, memo, useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
6
6
  import { convertSpanToShareString, genarate, indexToColumnChar, update as core_update } from "@fortune-sheet/core";
7
7
  import { freezeImportedSpreadsheetCell, normalizeXlsxSemanticColorOrigin, spreadsheetMatrixProfile, withXlsxPatternFill, withoutEditableRange, normalizeSpreadsheetConditionalVisualOptions, xlsxSemanticColorMatchesValue, withXlsxGradientFill, effectiveSpreadsheetPageSetup, attachSpreadsheetShownCommentCells, normalizeXlsxPatternFill, withEditableRange, sameSpreadsheetHistoryValue, isSpreadsheetConditionalComparisonOperator, spreadsheetConditionalIconForValue, defaultSpreadsheetConditionalIconThresholds, xlsxPatternFillTypes, sheetProtectionAuthority, normalizeXlsxGradientFill, xlsxGradientFillFallbackColor, editableRangeCellCount, unlockedCellCount, registerDerivedSpreadsheetMatrix, editableRangeRequiresCredentials, SPREADSHEET_CONDITIONAL_COMPARISON_OPERATORS, normalizeSpreadsheetConditionalIconSetFormat, defaultSpreadsheetDataBarOptions, spreadsheetConditionalThresholdValue, normalizeSheetProtectionAuthority, withSheetProtection, spreadsheetProtectionKey, defaultSpreadsheetColorScaleThresholds, protectedSheetCount, SPREADSHEET_CONDITIONAL_ICON_SETS, withSheetSelectionPermissions, spreadsheetConditionalComparisonNeedsUpperValue, drawSpreadsheetConditionalIcon, isSpreadsheetConditionalIconSetName, spreadsheetConditionalThresholdsEqual, spreadsheetConditionalIconSetCount, spreadsheetConditionalIconAppearance } from "./9333.js";
@@ -4561,6 +4561,7 @@ function isSpreadsheetNativeTextUndoTarget(target) {
4561
4561
  return target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement || target instanceof HTMLSelectElement || target.isContentEditable || Boolean(target.closest('[contenteditable="true"]'));
4562
4562
  }
4563
4563
  function isSpreadsheetCellEditingTarget(target) {
4564
+ if (!isSpreadsheetCellEditorTarget(target)) return false;
4564
4565
  if (!(target instanceof Element)) return false;
4565
4566
  const formulaInput = target.closest('.fortune-fx-input');
4566
4567
  if (formulaInput) return true;
@@ -4571,6 +4572,9 @@ function isSpreadsheetCellEditingTarget(target) {
4571
4572
  const zIndex = Number.parseInt(inputBox.style.zIndex || getComputedStyle(inputBox).zIndex, 10);
4572
4573
  return !Number.isFinite(zIndex) || zIndex >= 0;
4573
4574
  }
4575
+ function isSpreadsheetCellEditorTarget(target) {
4576
+ return target instanceof Element && Boolean(target.closest('.fortune-fx-input, .luckysheet-cell-input'));
4577
+ }
4574
4578
  function spreadsheetFormulaBarSelectAllTarget(event) {
4575
4579
  if (event.defaultPrevented || event.repeat || event.altKey || event.shiftKey || !(event.metaKey || event.ctrlKey) || 'a' !== event.key.toLocaleLowerCase() || !(event.target instanceof Element)) return null;
4576
4580
  const formulaBar = event.target.closest('.fortune-fx-input');
@@ -25185,6 +25189,11 @@ function spreadsheetClipboardSnapshotForText(plainText) {
25185
25189
  }
25186
25190
  return createSpreadsheetTextClipboardSnapshot(normalized);
25187
25191
  }
25192
+ function clearRichSpreadsheetClipboard() {
25193
+ richSpreadsheetClipboard = null;
25194
+ browserClipboardWriteRevision += 1;
25195
+ preferLocalSpreadsheetClipboardRead = false;
25196
+ }
25188
25197
  async function readSpreadsheetClipboardText(readSystem, readLocal, timeoutMs = spreadsheetSystemClipboardTimeoutMs, canReadSystem = ()=>true) {
25189
25198
  if (readSystem && await canReadSystem()) try {
25190
25199
  const value = await withSpreadsheetClipboardTimeout(readSystem(), timeoutMs);
@@ -25758,25 +25767,26 @@ function useSpreadsheetClipboard({ clipboard = browserSpreadsheetClipboard, canA
25758
25767
  }, [
25759
25768
  editable
25760
25769
  ]);
25761
- const currentSnapshot = useCallback(()=>{
25770
+ const currentSource = useCallback(()=>{
25762
25771
  if (!editable) return null;
25763
25772
  const selection = getSelection();
25764
25773
  if (!selection) return null;
25765
25774
  const snapshot = captureSpreadsheetClipboardSnapshot(contentRef.current, selection.sheetId, selection.range, selection.plainText);
25766
- return snapshot ? {
25775
+ return {
25767
25776
  selection,
25768
25777
  snapshot
25769
- } : null;
25778
+ };
25770
25779
  }, [
25771
25780
  contentRef,
25772
25781
  editable,
25773
25782
  getSelection
25774
25783
  ]);
25775
25784
  const copy = useCallback((cut)=>{
25776
- const source = currentSnapshot();
25785
+ const source = currentSource();
25777
25786
  if (!source) return false;
25778
- storeRichSpreadsheetClipboard(source.snapshot);
25779
- copySpreadsheetSelection(clipboard, source.snapshot.plainText, cut);
25787
+ if (source.snapshot) storeRichSpreadsheetClipboard(source.snapshot);
25788
+ else clearRichSpreadsheetClipboard();
25789
+ copySpreadsheetSelection(clipboard, source.selection.plainText, cut);
25780
25790
  if (cut && !clearSelection()) {
25781
25791
  showToast('选区已复制,但无法清除原内容。', 'error');
25782
25792
  return false;
@@ -25785,7 +25795,7 @@ function useSpreadsheetClipboard({ clipboard = browserSpreadsheetClipboard, canA
25785
25795
  }, [
25786
25796
  clearSelection,
25787
25797
  clipboard,
25788
- currentSnapshot
25798
+ currentSource
25789
25799
  ]);
25790
25800
  const applySnapshot = useCallback((snapshot, targetSheetId, targetRange, options, notify = true)=>{
25791
25801
  const request = {
@@ -25913,15 +25923,16 @@ function useSpreadsheetClipboard({ clipboard = browserSpreadsheetClipboard, canA
25913
25923
  fallbackFocusTarget
25914
25924
  ]);
25915
25925
  const copyToDataTransfer = useCallback((data, cut)=>{
25916
- const source = currentSnapshot();
25926
+ const source = currentSource();
25917
25927
  if (!source) return false;
25918
- storeRichSpreadsheetClipboard(source.snapshot);
25919
- data.setData('text/plain', source.snapshot.plainText);
25928
+ if (source.snapshot) storeRichSpreadsheetClipboard(source.snapshot);
25929
+ else clearRichSpreadsheetClipboard();
25930
+ data.setData('text/plain', source.selection.plainText);
25920
25931
  if (cut && !clearSelection()) return false;
25921
25932
  return true;
25922
25933
  }, [
25923
25934
  clearSelection,
25924
- currentSnapshot
25935
+ currentSource
25925
25936
  ]);
25926
25937
  return {
25927
25938
  applyDialog,
@@ -27397,6 +27408,385 @@ function SpreadsheetSortCustomListEditor({ error, level, text, onCancel, onChang
27397
27408
  ]
27398
27409
  });
27399
27410
  }
27411
+ function SpreadsheetSortCustomListManagerDialog({ customLists, restoreFocusTarget, onApply, onClose }) {
27412
+ const builtInLists = useMemo(()=>customLists.filter((list)=>'built-in' === list.source), [
27413
+ customLists
27414
+ ]);
27415
+ const initialUserListsRef = useRef(null);
27416
+ initialUserListsRef.current ??= customLists.filter((list)=>'built-in' !== list.source).map((list, index)=>({
27417
+ id: index,
27418
+ entries: Object.freeze([
27419
+ ...list.entries
27420
+ ]),
27421
+ originalEntries: Object.freeze([
27422
+ ...list.entries
27423
+ ])
27424
+ }));
27425
+ const initialUserLists = initialUserListsRef.current;
27426
+ const nextIdRef = useRef(initialUserLists.length);
27427
+ const listRef = useRef(null);
27428
+ const [userLists, setUserLists] = useState(()=>initialUserLists.map(cloneManagedCustomList));
27429
+ const [selection, setSelection] = useState('built-in:0');
27430
+ const [text, setText] = useState(builtInLists[0]?.entries.join('\n') ?? '');
27431
+ const [error, setError] = useState(null);
27432
+ const userListLabels = useMemo(()=>new Map(userLists.map((list)=>[
27433
+ list.id,
27434
+ managedCustomListLabel(list.entries)
27435
+ ])), [
27436
+ userLists
27437
+ ]);
27438
+ useEffect(()=>listRef.current?.focus(), []);
27439
+ const selectedUserIndex = managedUserListIndex(selection, userLists);
27440
+ const selectedUser = userLists[selectedUserIndex];
27441
+ const selectedBuiltIn = managedBuiltInList(selection, builtInLists);
27442
+ const editingNewList = 'new' === selection;
27443
+ const validateDraft = (rows, excludedId)=>{
27444
+ const validation = parseSpreadsheetSortCustomList(text);
27445
+ if (!validation.ok) {
27446
+ setError(validation.message);
27447
+ return null;
27448
+ }
27449
+ const duplicate = [
27450
+ ...builtInLists.map((list)=>list.entries),
27451
+ ...rows.filter((row)=>row.id !== excludedId).map((row)=>row.entries)
27452
+ ].some((entries)=>spreadsheetSortCustomListsEqual(entries, validation.entries));
27453
+ if (duplicate) {
27454
+ setError('该自定义序列已存在。');
27455
+ return null;
27456
+ }
27457
+ setError(null);
27458
+ return Object.freeze([
27459
+ ...validation.entries
27460
+ ]);
27461
+ };
27462
+ const commitSelectedUser = (rows)=>{
27463
+ const index = managedUserListIndex(selection, rows);
27464
+ const selected = rows[index];
27465
+ if (!selected) return rows;
27466
+ const entries = validateDraft(rows, selected.id);
27467
+ if (!entries) return null;
27468
+ return rows.map((row)=>row.id === selected.id ? {
27469
+ ...row,
27470
+ entries
27471
+ } : row);
27472
+ };
27473
+ const select = (nextSelection)=>{
27474
+ let rows = userLists;
27475
+ if (selectedUser) {
27476
+ const committed = commitSelectedUser(rows);
27477
+ if (!committed) return;
27478
+ rows = committed;
27479
+ setUserLists(rows);
27480
+ } else if (editingNewList && text.trim()) return void setError('请先添加当前序列,或清空项目后再切换。');
27481
+ setSelection(nextSelection);
27482
+ setText(managedSelectionText(nextSelection, builtInLists, rows));
27483
+ setError(null);
27484
+ };
27485
+ const saveSelectedUser = ()=>{
27486
+ const committed = commitSelectedUser(userLists);
27487
+ if (!committed) return;
27488
+ setUserLists(committed);
27489
+ setText(managedSelectionText(selection, builtInLists, committed));
27490
+ };
27491
+ const addNewList = ()=>{
27492
+ if (userLists.length >= 32) return void setError("当前编辑器最多保留 32 个自定义序列。");
27493
+ const entries = validateDraft(userLists, null);
27494
+ if (!entries) return;
27495
+ const id = nextIdRef.current;
27496
+ nextIdRef.current += 1;
27497
+ const next = [
27498
+ ...userLists,
27499
+ {
27500
+ id,
27501
+ entries,
27502
+ originalEntries: null
27503
+ }
27504
+ ];
27505
+ setUserLists(next);
27506
+ setSelection(`user:${id}`);
27507
+ setText(entries.join('\n'));
27508
+ };
27509
+ const moveSelectedUser = (offset)=>{
27510
+ const committed = commitSelectedUser(userLists);
27511
+ if (!committed) return;
27512
+ const index = managedUserListIndex(selection, committed);
27513
+ const nextIndex = index + offset;
27514
+ if (index < 0 || nextIndex < 0 || nextIndex >= committed.length) return;
27515
+ const next = committed.map(cloneManagedCustomList);
27516
+ const selected = next[index];
27517
+ const target = next[nextIndex];
27518
+ if (!selected || !target) return;
27519
+ next[index] = target;
27520
+ next[nextIndex] = selected;
27521
+ setUserLists(next);
27522
+ setText(selected.entries.join('\n'));
27523
+ };
27524
+ const deleteSelectedUser = ()=>{
27525
+ if (!selectedUser) return;
27526
+ const next = userLists.filter((row)=>row.id !== selectedUser.id);
27527
+ setUserLists(next);
27528
+ const fallback = next[selectedUserIndex] ?? next[selectedUserIndex - 1];
27529
+ const nextSelection = fallback ? `user:${fallback.id}` : 'built-in:0';
27530
+ setSelection(nextSelection);
27531
+ setText(managedSelectionText(nextSelection, builtInLists, next));
27532
+ setError(null);
27533
+ };
27534
+ const apply = ()=>{
27535
+ let rows = userLists;
27536
+ if (selectedUser) {
27537
+ const committed = commitSelectedUser(rows);
27538
+ if (!committed) return;
27539
+ rows = committed;
27540
+ } else if (editingNewList && text.trim()) {
27541
+ if (rows.length >= 32) return void setError("当前编辑器最多保留 32 个自定义序列。");
27542
+ const entries = validateDraft(rows, null);
27543
+ if (!entries) return;
27544
+ const id = nextIdRef.current;
27545
+ nextIdRef.current += 1;
27546
+ rows = [
27547
+ ...rows,
27548
+ {
27549
+ id,
27550
+ entries,
27551
+ originalEntries: null
27552
+ }
27553
+ ];
27554
+ }
27555
+ onApply(managedCustomListResult(initialUserLists, rows));
27556
+ onClose();
27557
+ };
27558
+ return /*#__PURE__*/ jsxs(Dialog, {
27559
+ title: "自定义序列",
27560
+ description: "管理排序时可复用的本地序列。内置月份和星期序列保持只读。",
27561
+ className: "work-spreadsheet-sort-custom-list-manager",
27562
+ restoreFocusTarget: restoreFocusTarget,
27563
+ onClose: onClose,
27564
+ footer: /*#__PURE__*/ jsxs(Fragment, {
27565
+ children: [
27566
+ /*#__PURE__*/ jsx(button_Button, {
27567
+ tone: "quiet",
27568
+ onClick: onClose,
27569
+ children: "取消"
27570
+ }),
27571
+ /*#__PURE__*/ jsx(button_Button, {
27572
+ tone: "primary",
27573
+ onClick: apply,
27574
+ children: "确定"
27575
+ })
27576
+ ]
27577
+ }),
27578
+ children: [
27579
+ /*#__PURE__*/ jsxs("div", {
27580
+ className: "work-spreadsheet-sort-custom-list-manager-layout",
27581
+ children: [
27582
+ /*#__PURE__*/ jsxs("section", {
27583
+ "aria-label": "可用序列",
27584
+ children: [
27585
+ /*#__PURE__*/ jsxs("label", {
27586
+ children: [
27587
+ /*#__PURE__*/ jsx("span", {
27588
+ children: "自定义序列"
27589
+ }),
27590
+ /*#__PURE__*/ jsxs("select", {
27591
+ ref: listRef,
27592
+ "aria-label": "自定义序列列表",
27593
+ size: 10,
27594
+ value: selection,
27595
+ onChange: (event)=>select(event.currentTarget.value),
27596
+ children: [
27597
+ /*#__PURE__*/ jsx("optgroup", {
27598
+ label: "内置序列",
27599
+ children: builtInLists.map((list, index)=>/*#__PURE__*/ jsx("option", {
27600
+ value: `built-in:${index}`,
27601
+ children: list.label
27602
+ }, `built-in:${index}`))
27603
+ }),
27604
+ userLists.length ? /*#__PURE__*/ jsx("optgroup", {
27605
+ label: "用户序列",
27606
+ children: userLists.map((list)=>/*#__PURE__*/ jsx("option", {
27607
+ value: `user:${list.id}`,
27608
+ children: userListLabels.get(list.id)
27609
+ }, list.id))
27610
+ }) : null
27611
+ ]
27612
+ })
27613
+ ]
27614
+ }),
27615
+ /*#__PURE__*/ jsxs(button_Button, {
27616
+ tone: "quiet",
27617
+ type: "button",
27618
+ disabled: userLists.length >= 32,
27619
+ onClick: ()=>select('new'),
27620
+ children: [
27621
+ /*#__PURE__*/ jsx(ListPlus, {
27622
+ size: 15,
27623
+ "aria-hidden": "true"
27624
+ }),
27625
+ "新建序列"
27626
+ ]
27627
+ })
27628
+ ]
27629
+ }),
27630
+ /*#__PURE__*/ jsxs("section", {
27631
+ "aria-label": "序列项目",
27632
+ children: [
27633
+ /*#__PURE__*/ jsxs("label", {
27634
+ children: [
27635
+ /*#__PURE__*/ jsx("span", {
27636
+ children: "序列项目(每行一个项目)"
27637
+ }),
27638
+ /*#__PURE__*/ jsx("textarea", {
27639
+ "aria-label": "自定义序列项目",
27640
+ "aria-invalid": error ? true : void 0,
27641
+ readOnly: Boolean(selectedBuiltIn),
27642
+ rows: 12,
27643
+ value: text,
27644
+ onChange: (event)=>{
27645
+ setText(event.currentTarget.value);
27646
+ setError(null);
27647
+ }
27648
+ })
27649
+ ]
27650
+ }),
27651
+ /*#__PURE__*/ jsx("p", {
27652
+ children: selectedBuiltIn ? '内置序列不可修改或删除。' : editingNewList ? '每行、英文逗号或中文逗号可分隔一个项目。' : '修改项目后保存;排序和删除在点击“确定”后统一提交。'
27653
+ }),
27654
+ error ? /*#__PURE__*/ jsx("p", {
27655
+ className: "work-spreadsheet-sort-custom-list-error",
27656
+ role: "alert",
27657
+ children: error
27658
+ }) : null,
27659
+ /*#__PURE__*/ jsxs("div", {
27660
+ className: "work-spreadsheet-sort-custom-list-manager-actions",
27661
+ children: [
27662
+ editingNewList ? /*#__PURE__*/ jsxs(button_Button, {
27663
+ tone: "primary",
27664
+ type: "button",
27665
+ onClick: addNewList,
27666
+ children: [
27667
+ /*#__PURE__*/ jsx(ListPlus, {
27668
+ size: 15,
27669
+ "aria-hidden": "true"
27670
+ }),
27671
+ "添加序列"
27672
+ ]
27673
+ }) : selectedUser ? /*#__PURE__*/ jsxs(button_Button, {
27674
+ tone: "primary",
27675
+ type: "button",
27676
+ onClick: saveSelectedUser,
27677
+ children: [
27678
+ /*#__PURE__*/ jsx(Save, {
27679
+ size: 15,
27680
+ "aria-hidden": "true"
27681
+ }),
27682
+ "保存更改"
27683
+ ]
27684
+ }) : null,
27685
+ /*#__PURE__*/ jsx(button_Button, {
27686
+ tone: "quiet",
27687
+ type: "button",
27688
+ "aria-label": "上移序列",
27689
+ title: "上移序列",
27690
+ disabled: selectedUserIndex <= 0,
27691
+ onClick: ()=>moveSelectedUser(-1),
27692
+ children: /*#__PURE__*/ jsx(ArrowUp, {
27693
+ size: 15,
27694
+ "aria-hidden": "true"
27695
+ })
27696
+ }),
27697
+ /*#__PURE__*/ jsx(button_Button, {
27698
+ tone: "quiet",
27699
+ type: "button",
27700
+ "aria-label": "下移序列",
27701
+ title: "下移序列",
27702
+ disabled: selectedUserIndex < 0 || selectedUserIndex >= userLists.length - 1,
27703
+ onClick: ()=>moveSelectedUser(1),
27704
+ children: /*#__PURE__*/ jsx(ArrowDown, {
27705
+ size: 15,
27706
+ "aria-hidden": "true"
27707
+ })
27708
+ }),
27709
+ /*#__PURE__*/ jsx(button_Button, {
27710
+ tone: "quiet",
27711
+ type: "button",
27712
+ "aria-label": "删除序列",
27713
+ title: "删除序列",
27714
+ disabled: !selectedUser,
27715
+ onClick: deleteSelectedUser,
27716
+ children: /*#__PURE__*/ jsx(Trash2, {
27717
+ size: 15,
27718
+ "aria-hidden": "true"
27719
+ })
27720
+ })
27721
+ ]
27722
+ })
27723
+ ]
27724
+ })
27725
+ ]
27726
+ }),
27727
+ /*#__PURE__*/ jsxs("p", {
27728
+ className: "work-spreadsheet-sort-custom-list-manager-note",
27729
+ children: [
27730
+ "最多保存 ",
27731
+ 32,
27732
+ ' ',
27733
+ "个用户序列。修改或删除当前使用的序列会同步更新对应排序条件。"
27734
+ ]
27735
+ })
27736
+ ]
27737
+ });
27738
+ }
27739
+ function managedCustomListResult(initial, current) {
27740
+ const changes = [];
27741
+ for (const original of initial){
27742
+ if (!original.originalEntries) continue;
27743
+ const next = current.find((row)=>row.id === original.id);
27744
+ if (next) {
27745
+ if (!spreadsheetSortCustomListsEqual(original.originalEntries, next.entries)) changes.push({
27746
+ previous: original.originalEntries,
27747
+ next: Object.freeze([
27748
+ ...next.entries
27749
+ ])
27750
+ });
27751
+ } else changes.push({
27752
+ previous: original.originalEntries,
27753
+ next: null
27754
+ });
27755
+ }
27756
+ return Object.freeze({
27757
+ lists: Object.freeze(current.map((row)=>Object.freeze([
27758
+ ...row.entries
27759
+ ]))),
27760
+ changes: Object.freeze(changes)
27761
+ });
27762
+ }
27763
+ function managedUserListIndex(selection, lists) {
27764
+ if (!selection.startsWith('user:')) return -1;
27765
+ const id = Number(selection.slice(5));
27766
+ return Number.isSafeInteger(id) ? lists.findIndex((list)=>list.id === id) : -1;
27767
+ }
27768
+ function managedBuiltInList(selection, lists) {
27769
+ if (!selection.startsWith('built-in:')) return null;
27770
+ const index = Number(selection.slice(9));
27771
+ return Number.isSafeInteger(index) ? lists[index] ?? null : null;
27772
+ }
27773
+ function managedSelectionText(selection, builtInLists, userLists) {
27774
+ return (managedBuiltInList(selection, builtInLists)?.entries ?? userLists[managedUserListIndex(selection, userLists)]?.entries ?? []).join('\n');
27775
+ }
27776
+ function cloneManagedCustomList(list) {
27777
+ return {
27778
+ ...list,
27779
+ entries: Object.freeze([
27780
+ ...list.entries
27781
+ ]),
27782
+ originalEntries: list.originalEntries ? Object.freeze([
27783
+ ...list.originalEntries
27784
+ ]) : null
27785
+ };
27786
+ }
27787
+ function managedCustomListLabel(entries) {
27788
+ return createSpreadsheetSortCustomList(entries, 'session')?.label ?? entries.join(' → ');
27789
+ }
27400
27790
  function SpreadsheetSortOptionsDialog({ value, restoreFocusTarget, onApply, onClose }) {
27401
27791
  const [draft, setDraft] = useState(value);
27402
27792
  const formId = useId();
@@ -27855,7 +28245,7 @@ function spreadsheetSortPositionLabel(orientation, position) {
27855
28245
  function isSpreadsheetSortAppearanceKind(value) {
27856
28246
  return 'cell-color' === value || 'font-color' === value || 'icon' === value;
27857
28247
  }
27858
- function SpreadsheetSortDialog({ source, restoreFocusTarget, onApply, onRememberCustomList, onClose }) {
28248
+ function SpreadsheetSortDialog({ source, restoreFocusTarget, onApply, onRememberCustomList, onUpdateCustomLists, onClose }) {
27859
28249
  const [value, setValue] = useState(()=>({
27860
28250
  hasHeader: source.value.hasHeader,
27861
28251
  keys: source.value.keys.map(cloneSpreadsheetSortKey),
@@ -27865,6 +28255,7 @@ function SpreadsheetSortDialog({ source, restoreFocusTarget, onApply, onRemember
27865
28255
  }));
27866
28256
  const [verticalHasHeader, setVerticalHasHeader] = useState(source.value.hasHeader);
27867
28257
  const [optionsOpen, setOptionsOpen] = useState(false);
28258
+ const [customListManagerOpen, setCustomListManagerOpen] = useState(false);
27868
28259
  const [customLists, setCustomLists] = useState(()=>initialSpreadsheetSortCustomLists(source));
27869
28260
  const fields = 'top-to-bottom' === value.orientation ? source.columns : source.rows;
27870
28261
  const appearanceFields = useMemo(()=>spreadsheetSortAppearanceFields(source.appearanceRows, source.range, value.orientation, value.hasHeader), [
@@ -27876,6 +28267,7 @@ function SpreadsheetSortDialog({ source, restoreFocusTarget, onApply, onRemember
27876
28267
  const [customListDraft, setCustomListDraft] = useState(null);
27877
28268
  const formId = useId();
27878
28269
  const optionsButtonRef = useRef(null);
28270
+ const customListManagerButtonRef = useRef(null);
27879
28271
  const nextKey = value.keys.length < 64 ? nextSpreadsheetSortKey(value.keys, fields, appearanceFields) : null;
27880
28272
  const replaceKey = (index, replacement)=>{
27881
28273
  setValue((current)=>({
@@ -27974,6 +28366,18 @@ function SpreadsheetSortDialog({ source, restoreFocusTarget, onApply, onRemember
27974
28366
  ]
27975
28367
  });
27976
28368
  };
28369
+ const applyCustomListManagement = (result)=>{
28370
+ const currentUserLists = customLists.filter((list)=>'built-in' !== list.source);
28371
+ const preferencesChanged = currentUserLists.length !== result.lists.length || result.lists.some((entries, index)=>!spreadsheetSortCustomListsEqual(currentUserLists[index]?.entries ?? [], entries));
28372
+ const fallback = result.lists.map((entries)=>createSpreadsheetSortCustomList(entries, 'session')).filter((list)=>null !== list);
28373
+ const updated = preferencesChanged ? onUpdateCustomLists?.(result.lists) ?? fallback : currentUserLists;
28374
+ setCustomLists(mergeSpreadsheetSortCustomLists(updated));
28375
+ setCustomListDraft(null);
28376
+ setValue((current)=>({
28377
+ ...current,
28378
+ keys: current.keys.map((key)=>reconcileManagedCustomListKey(key, result.changes))
28379
+ }));
28380
+ };
27977
28381
  return /*#__PURE__*/ jsxs(Fragment, {
27978
28382
  children: [
27979
28383
  /*#__PURE__*/ jsx(Dialog, {
@@ -28004,40 +28408,60 @@ function SpreadsheetSortDialog({ source, restoreFocusTarget, onApply, onRemember
28004
28408
  /*#__PURE__*/ jsxs("div", {
28005
28409
  className: "work-spreadsheet-sort-toolbar",
28006
28410
  children: [
28007
- /*#__PURE__*/ jsxs(button_Button, {
28008
- tone: "quiet",
28009
- type: "button",
28010
- disabled: !nextKey,
28011
- onClick: ()=>{
28012
- if (!nextKey) return;
28013
- setCustomListDraft(null);
28014
- setValue((current)=>({
28015
- ...current,
28016
- keys: [
28017
- ...current.keys,
28018
- cloneSpreadsheetSortKey(nextKey)
28019
- ]
28020
- }));
28021
- },
28411
+ /*#__PURE__*/ jsxs("div", {
28412
+ className: "work-spreadsheet-sort-toolbar-actions",
28022
28413
  children: [
28023
- /*#__PURE__*/ jsx(ListPlus, {
28024
- size: 15,
28025
- "aria-hidden": "true"
28414
+ /*#__PURE__*/ jsxs(button_Button, {
28415
+ tone: "quiet",
28416
+ type: "button",
28417
+ disabled: !nextKey,
28418
+ onClick: ()=>{
28419
+ if (!nextKey) return;
28420
+ setCustomListDraft(null);
28421
+ setValue((current)=>({
28422
+ ...current,
28423
+ keys: [
28424
+ ...current.keys,
28425
+ cloneSpreadsheetSortKey(nextKey)
28426
+ ]
28427
+ }));
28428
+ },
28429
+ children: [
28430
+ /*#__PURE__*/ jsx(ListPlus, {
28431
+ size: 15,
28432
+ "aria-hidden": "true"
28433
+ }),
28434
+ "添加条件"
28435
+ ]
28026
28436
  }),
28027
- "添加条件"
28028
- ]
28029
- }),
28030
- /*#__PURE__*/ jsxs(button_Button, {
28031
- ref: optionsButtonRef,
28032
- tone: "quiet",
28033
- type: "button",
28034
- onClick: ()=>setOptionsOpen(true),
28035
- children: [
28036
- /*#__PURE__*/ jsx(Settings2, {
28037
- size: 15,
28038
- "aria-hidden": "true"
28437
+ /*#__PURE__*/ jsxs(button_Button, {
28438
+ ref: optionsButtonRef,
28439
+ tone: "quiet",
28440
+ type: "button",
28441
+ onClick: ()=>setOptionsOpen(true),
28442
+ children: [
28443
+ /*#__PURE__*/ jsx(Settings2, {
28444
+ size: 15,
28445
+ "aria-hidden": "true"
28446
+ }),
28447
+ "选项…"
28448
+ ]
28039
28449
  }),
28040
- "选项…"
28450
+ /*#__PURE__*/ jsxs(button_Button, {
28451
+ ref: customListManagerButtonRef,
28452
+ tone: "quiet",
28453
+ type: "button",
28454
+ "aria-label": "管理自定义序列",
28455
+ title: "管理自定义序列",
28456
+ onClick: ()=>setCustomListManagerOpen(true),
28457
+ children: [
28458
+ /*#__PURE__*/ jsx(ListOrdered, {
28459
+ size: 15,
28460
+ "aria-hidden": "true"
28461
+ }),
28462
+ "自定义序列…"
28463
+ ]
28464
+ })
28041
28465
  ]
28042
28466
  }),
28043
28467
  /*#__PURE__*/ jsx(OfficeCheckbox, {
@@ -28191,10 +28615,30 @@ function SpreadsheetSortDialog({ source, restoreFocusTarget, onApply, onRemember
28191
28615
  restoreFocusTarget: ()=>optionsButtonRef.current,
28192
28616
  onApply: applyOptions,
28193
28617
  onClose: ()=>setOptionsOpen(false)
28618
+ }) : null,
28619
+ customListManagerOpen ? /*#__PURE__*/ jsx(SpreadsheetSortCustomListManagerDialog, {
28620
+ customLists: customLists,
28621
+ restoreFocusTarget: ()=>customListManagerButtonRef.current,
28622
+ onApply: applyCustomListManagement,
28623
+ onClose: ()=>setCustomListManagerOpen(false)
28194
28624
  }) : null
28195
28625
  ]
28196
28626
  });
28197
28627
  }
28628
+ function reconcileManagedCustomListKey(key, changes) {
28629
+ if (void 0 === key.customList) return key;
28630
+ const change = changes.find((candidate)=>spreadsheetSortCustomListsEqual(candidate.previous, key.customList ?? []));
28631
+ if (!change) return key;
28632
+ return change.next ? {
28633
+ index: key.index,
28634
+ customList: [
28635
+ ...change.next
28636
+ ]
28637
+ } : {
28638
+ index: key.index,
28639
+ direction: 'ascending'
28640
+ };
28641
+ }
28198
28642
  function cloneSpreadsheetSortKey(key) {
28199
28643
  if ('cell-color' === key.sortOn || 'font-color' === key.sortOn) return {
28200
28644
  ...key
@@ -28384,6 +28828,22 @@ function useSpreadsheetSort({ commandsRef, contentRef, focusGrid, getGridFocusTa
28384
28828
  }, [
28385
28829
  customListStore
28386
28830
  ]);
28831
+ const updateCustomLists = useCallback((lists)=>{
28832
+ const normalized = normalizeStoredSpreadsheetSortCustomLists(lists);
28833
+ let source = customListStore ? 'stored' : 'session';
28834
+ if (customListStore) try {
28835
+ customListStore.save(normalized);
28836
+ } catch {
28837
+ source = 'session';
28838
+ showToast('无法写入本地自定义序列;本次更改仅在当前会话中保留。', 'error');
28839
+ }
28840
+ const next = Object.freeze(normalized.map((entries)=>createSpreadsheetSortCustomList(entries, source)).filter((list)=>null !== list));
28841
+ customListsRef.current = next;
28842
+ setCustomLists(next);
28843
+ return next;
28844
+ }, [
28845
+ customListStore
28846
+ ]);
28387
28847
  const sourceForCandidate = useCallback((request, candidate)=>{
28388
28848
  if (!candidate.available) return null;
28389
28849
  const sheet = contentRef.current.sheets.find((item)=>item.id === request.sheetId);
@@ -28410,28 +28870,13 @@ function useSpreadsheetSort({ commandsRef, contentRef, focusGrid, getGridFocusTa
28410
28870
  const list = createSpreadsheetSortCustomList(candidate.entries, source);
28411
28871
  if (!list) return candidate;
28412
28872
  if (customListsRef.current.length >= 32) return candidate;
28413
- const next = Object.freeze([
28414
- ...customListsRef.current,
28415
- list
28873
+ const next = updateCustomLists([
28874
+ ...customListsRef.current.map((item)=>item.entries),
28875
+ list.entries
28416
28876
  ]);
28417
- if (customListStore) try {
28418
- customListStore.save(next.filter((item)=>'stored' === item.source).map((item)=>item.entries));
28419
- } catch {
28420
- showToast('无法写入本地自定义序列;该序列仅在本次会话中保留。', 'error');
28421
- const session = createSpreadsheetSortCustomList(candidate.entries, 'session') ?? candidate;
28422
- const fallback = Object.freeze([
28423
- ...customListsRef.current,
28424
- session
28425
- ]);
28426
- customListsRef.current = fallback;
28427
- setCustomLists(fallback);
28428
- return session;
28429
- }
28430
- customListsRef.current = next;
28431
- setCustomLists(next);
28432
- return list;
28877
+ return next.find((item)=>spreadsheetSortCustomListsEqual(item.entries, list.entries)) ?? list;
28433
28878
  }, [
28434
- customListStore
28879
+ updateCustomLists
28435
28880
  ]);
28436
28881
  const applyAuthorizedRequest = useCallback((request, selectedRange)=>{
28437
28882
  authorizedRequestRef.current = {
@@ -28583,6 +29028,7 @@ function useSpreadsheetSort({ commandsRef, contentRef, focusGrid, getGridFocusTa
28583
29028
  restoreFocusTarget: restoreFocusTarget,
28584
29029
  onApply: apply,
28585
29030
  onRememberCustomList: rememberCustomList,
29031
+ onUpdateCustomLists: updateCustomLists,
28586
29032
  onClose: close
28587
29033
  }) : surface?.kind === 'range' ? /*#__PURE__*/ jsx(SpreadsheetSortRangeDialog, {
28588
29034
  source: surface.source,
@@ -28896,6 +29342,7 @@ function SpreadsheetEditorSurface({ autoFocus = true, content, collaborationHist
28896
29342
  const previewRef = useRef(preview);
28897
29343
  const spreadsheetRootRef = useRef(null);
28898
29344
  const spreadsheetCanvasRef = useRef(null);
29345
+ const spreadsheetEditingEscapePendingRef = useRef(false);
28899
29346
  const editorFocusOrigin = useOfficeEditorFocusOrigin();
28900
29347
  const workbookRef = useRef(null);
28901
29348
  const projectedWorkbookSheetsRef = useRef([]);
@@ -28953,6 +29400,32 @@ function SpreadsheetEditorSurface({ autoFocus = true, content, collaborationHist
28953
29400
  }, [
28954
29401
  preview
28955
29402
  ]);
29403
+ useEffect(()=>{
29404
+ const container = spreadsheetCanvasRef.current;
29405
+ if (!container || preview) return;
29406
+ const restoreGridFocus = ()=>requestAnimationFrame(()=>focusSpreadsheetGrid(container, {
29407
+ forceCellEditingExit: true
29408
+ }));
29409
+ const handleKeyDown = (event)=>{
29410
+ if ('Escape' !== event.key || !isSpreadsheetCellEditorTarget(event.target)) return;
29411
+ spreadsheetEditingEscapePendingRef.current = true;
29412
+ restoreGridFocus();
29413
+ };
29414
+ const handleKeyUp = (event)=>{
29415
+ if ('Escape' !== event.key || !spreadsheetEditingEscapePendingRef.current) return;
29416
+ spreadsheetEditingEscapePendingRef.current = false;
29417
+ restoreGridFocus();
29418
+ };
29419
+ container.addEventListener('keydown', handleKeyDown, true);
29420
+ container.addEventListener('keyup', handleKeyUp, true);
29421
+ return ()=>{
29422
+ container.removeEventListener('keydown', handleKeyDown, true);
29423
+ container.removeEventListener('keyup', handleKeyUp, true);
29424
+ spreadsheetEditingEscapePendingRef.current = false;
29425
+ };
29426
+ }, [
29427
+ preview
29428
+ ]);
28956
29429
  const spreadsheetZoomRef = useRef(100);
28957
29430
  const [workbookInstance, setWorkbookInstance] = useState(null);
28958
29431
  const bindWorkbookInstance = useCallback((instance)=>{
@@ -29383,6 +29856,20 @@ function SpreadsheetEditorSurface({ autoFocus = true, content, collaborationHist
29383
29856
  });
29384
29857
  const currentClipboardSelection = useCallback(()=>{
29385
29858
  if (previewRef.current) return null;
29859
+ if (contextMenu) return {
29860
+ sheetId: contextMenu.selection.sheetId,
29861
+ range: {
29862
+ row: [
29863
+ contextMenu.range.row[0] ?? 0,
29864
+ contextMenu.range.row[1] ?? contextMenu.range.row[0] ?? 0
29865
+ ],
29866
+ column: [
29867
+ contextMenu.range.column[0] ?? 0,
29868
+ contextMenu.range.column[1] ?? contextMenu.range.column[0] ?? 0
29869
+ ]
29870
+ },
29871
+ plainText: contextMenu.selection.clipboard
29872
+ };
29386
29873
  const sheetId = selectionState?.sheetId ?? activeSheetIdRef.current;
29387
29874
  const sheet = contentRef.current.sheets.find((candidate)=>candidate.id === sheetId);
29388
29875
  const selection = selectionState?.selection ?? sheet?.luckysheet_select_save?.at(-1);
@@ -29406,6 +29893,7 @@ function SpreadsheetEditorSurface({ autoFocus = true, content, collaborationHist
29406
29893
  plainText: agentSelection.clipboard
29407
29894
  } : null;
29408
29895
  }, [
29896
+ contextMenu,
29409
29897
  selectionState
29410
29898
  ]);
29411
29899
  const spreadsheetClipboard = useSpreadsheetClipboard({
@@ -29754,9 +30242,6 @@ function SpreadsheetEditorSurface({ autoFocus = true, content, collaborationHist
29754
30242
  onZoomIn: ()=>changeSpreadsheetWheelZoom('in'),
29755
30243
  onZoomOut: ()=>changeSpreadsheetWheelZoom('out')
29756
30244
  });
29757
- const handleSpreadsheetEditingEscape = (event)=>{
29758
- if ('Escape' === event.key && isSpreadsheetCellEditingTarget(event.target)) requestAnimationFrame(()=>focusSpreadsheetGrid(spreadsheetCanvasRef.current));
29759
- };
29760
30245
  const handleSpreadsheetKeyDownCapture = (event)=>{
29761
30246
  releaseSpreadsheetSelectionRequest(selectionStateRef.current);
29762
30247
  if ('Alt' === event.key && !event.repeat && event.target instanceof Element && event.target.closest('.fortune-sheet-overlay') && reserveAutoFilterAltKey()) {
@@ -29764,7 +30249,6 @@ function SpreadsheetEditorSurface({ autoFocus = true, content, collaborationHist
29764
30249
  event.stopPropagation();
29765
30250
  return;
29766
30251
  }
29767
- handleSpreadsheetEditingEscape(event);
29768
30252
  };
29769
30253
  const handleSpreadsheetPointerDownCapture = (event)=>{
29770
30254
  releaseSpreadsheetSelectionRequest(selectionStateRef.current);
@@ -30135,7 +30619,7 @@ function SpreadsheetEditorSurface({ autoFocus = true, content, collaborationHist
30135
30619
  ]
30136
30620
  });
30137
30621
  }
30138
- function focusSpreadsheetGrid(container, { focusOrigin = document.activeElement, forceInitial = true } = {}) {
30622
+ function focusSpreadsheetGrid(container, { focusOrigin = document.activeElement, forceCellEditingExit = false, forceInitial = true } = {}) {
30139
30623
  if (!container) return;
30140
30624
  spreadsheetFocusCleanups.get(container)?.();
30141
30625
  let remainingFrames = spreadsheetFocusRetryFrames;
@@ -30174,8 +30658,9 @@ function focusSpreadsheetGrid(container, { focusOrigin = document.activeElement,
30174
30658
  const restoreFocus = (force)=>{
30175
30659
  if (focusRestorationStopped) return;
30176
30660
  const activeElement = document.activeElement;
30177
- if (!force && isSpreadsheetCellEditingTarget(activeElement)) return void stopObservingFocusTarget();
30178
- if (!force && activeElement !== document.body && activeElement !== document.documentElement && activeElement !== commandTrigger && activeElement !== lastFocusedTarget) return void stopObservingFocusTarget();
30661
+ const activeCellEditor = isSpreadsheetCellEditorTarget(activeElement);
30662
+ if (!force && !forceCellEditingExit && isSpreadsheetCellEditingTarget(activeElement)) return void stopObservingFocusTarget();
30663
+ if (!force && !(forceCellEditingExit && activeCellEditor) && activeElement !== document.body && activeElement !== document.documentElement && activeElement !== commandTrigger && activeElement !== lastFocusedTarget) return void stopObservingFocusTarget();
30179
30664
  const focusTarget = spreadsheetGridFocusTarget(container);
30180
30665
  if (focusTarget) {
30181
30666
  focusTarget.focus({