@ai-table/grid 0.0.55 → 0.0.56

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.
Files changed (40) hide show
  1. package/components/drag/drag.component.d.ts +1 -0
  2. package/components/drag/drag.component.d.ts.map +1 -1
  3. package/constants/icon.d.ts +1 -0
  4. package/constants/icon.d.ts.map +1 -1
  5. package/constants/table.d.ts +3 -1
  6. package/constants/table.d.ts.map +1 -1
  7. package/core/types/core.d.ts +4 -0
  8. package/core/types/core.d.ts.map +1 -1
  9. package/esm2022/components/drag/drag.component.mjs +42 -2
  10. package/esm2022/constants/icon.mjs +2 -1
  11. package/esm2022/constants/table.mjs +4 -2
  12. package/esm2022/core/types/core.mjs +1 -1
  13. package/esm2022/grid-base.component.mjs +5 -3
  14. package/esm2022/grid.component.mjs +34 -3
  15. package/esm2022/renderer/components/frozen-heads.component.mjs +17 -5
  16. package/esm2022/renderer/components/hover-row-heads.component.mjs +20 -3
  17. package/esm2022/renderer/components/icon.component.mjs +6 -2
  18. package/esm2022/renderer/drawers/add-row-layout-drawer.mjs +5 -5
  19. package/esm2022/renderer/drawers/cell-drawer.mjs +26 -27
  20. package/esm2022/renderer/drawers/record-row-layout-drawer.mjs +5 -5
  21. package/esm2022/utils/cell.mjs +5 -3
  22. package/esm2022/utils/style.mjs +9 -3
  23. package/fesm2022/ai-table-grid.mjs +1227 -1115
  24. package/fesm2022/ai-table-grid.mjs.map +1 -1
  25. package/grid-base.component.d.ts +4 -2
  26. package/grid-base.component.d.ts.map +1 -1
  27. package/grid.component.d.ts +1 -0
  28. package/grid.component.d.ts.map +1 -1
  29. package/package.json +1 -1
  30. package/renderer/components/frozen-heads.component.d.ts +1 -0
  31. package/renderer/components/frozen-heads.component.d.ts.map +1 -1
  32. package/renderer/components/hover-row-heads.component.d.ts.map +1 -1
  33. package/renderer/components/icon.component.d.ts.map +1 -1
  34. package/renderer/drawers/add-row-layout-drawer.d.ts.map +1 -1
  35. package/renderer/drawers/cell-drawer.d.ts.map +1 -1
  36. package/renderer/drawers/record-row-layout-drawer.d.ts.map +1 -1
  37. package/utils/cell.d.ts +1 -1
  38. package/utils/cell.d.ts.map +1 -1
  39. package/utils/style.d.ts +1 -1
  40. package/utils/style.d.ts.map +1 -1
@@ -28,7 +28,7 @@ import { ThyPopoverRef, ThyPopover, ThyPopoverModule } from 'ngx-tethys/popover'
28
28
  import ObjectID from 'bson-objectid';
29
29
  import { customAlphabet } from 'nanoid';
30
30
  import * as _ from 'lodash';
31
- import ___default, { isNumber, includes, values, isString, isNil } from 'lodash';
31
+ import { isNumber, includes, values, isString, isNil } from 'lodash';
32
32
  import { isUndefinedOrNull, isArray, isEmpty as isEmpty$1, isObject, TinyDate, helpers } from 'ngx-tethys/util';
33
33
  import * as i1 from '@angular/forms';
34
34
  import { FormsModule } from '@angular/forms';
@@ -65,9 +65,9 @@ import { ThyCheckboxModule } from 'ngx-tethys/checkbox';
65
65
  import { ThyProgress } from 'ngx-tethys/progress';
66
66
  import { ThyRate } from 'ngx-tethys/rate';
67
67
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
68
- import { LRUCache } from 'lru-cache';
69
68
  import { fromUnixTime, subDays } from 'date-fns';
70
69
  import { DEFAULT_COLORS } from 'ngx-tethys/color-picker';
70
+ import { LRUCache } from 'lru-cache';
71
71
  import GraphemeSplitter from 'grapheme-splitter';
72
72
 
73
73
  const KO_CONTAINER_TOKEN = new InjectionToken('KO_CONTAINER_TOKEN');
@@ -2377,7 +2377,8 @@ const AI_TABLE_SCROLL_BAR_PADDING = 3; // 单元格滑动容器的滚动条宽
2377
2377
  const AI_TABLE_OFFSET = 0.5; // 边框线偏移值
2378
2378
  const AI_TABLE_TEXT_GAP = 8; // 文本间距
2379
2379
  const AI_TABLE_ROW_HEAD = 'AI_TABLE_ROW_HEAD'; // 行头
2380
- const AI_TABLE_ROW_HEAD_WIDTH = 44; // 表格行头 checkbox 列的宽度
2380
+ const AI_TABLE_ROW_DRAG_ICON_WIDTH = 18; // 行拖拽宽度
2381
+ const AI_TABLE_ROW_HEAD_WIDTH = 44 + AI_TABLE_ROW_DRAG_ICON_WIDTH; // 表格行头 checkbox 列的宽度
2381
2382
  const AI_TABLE_ROW_HEAD_SIZE = 16; // 添加行按钮的尺寸
2382
2383
  const AI_TABLE_ROW_ADD_BUTTON = 'AI_TABLE_ROW_ADD_BUTTON'; // 添加行名称
2383
2384
  const AI_TABLE_BLANK = 'AI_TABLE_BLANK'; // 空白区域
@@ -2397,6 +2398,7 @@ const AI_TABLE_FIELD_ADD_BUTTON_WIDTH = 100; // 添加列宽度
2397
2398
  const AI_TABLE_FIELD_HEAD_ICON_GAP_SIZE = 8; // 字段表列头图标的间距
2398
2399
  const AI_TABLE_FIELD_HEAD_MORE = 'AI_TABLE_FIELD_HEAD_MORE'; // 更多图标名称
2399
2400
  const AI_TABLE_FIELD_HEAD_OPACITY_LINE = 'AI_TABLE_FIELD_HEAD_OPACITY_LINE'; // 字段列头透明线
2401
+ const AI_TABLE_ROW_DRAG = 'AI_TABLE_ROW_DRAG'; // 行拖拽
2400
2402
  const AI_TABLE_PREVENT_CLEAR_SELECTION_CLASS = '.ai-table-prevent-clear-selection';
2401
2403
  const AI_TABLE_ICON_COMMON_SIZE = 16; // 表格图标的通用尺寸
2402
2404
  const AI_TABLE_CELL = 'AI_TABLE_CELL'; // 单元格标识
@@ -2504,6 +2506,7 @@ const WebOutlinedPath = `M1 8C1 4.13401 4.13401 1 8 1C11.866 1 15 4.13401 15 8C1
2504
2506
  const DepartmentOutlinedPath = `M3 5.5C3 3.567 4.567 2 6.5 2C8.433 2 10 3.567 10 5.5C10 6.49548 9.5844 7.3939 8.91724 8.0312C10.7204 8.86779 12.017 10.6115 12.2217 12.6767C12.2976 13.4424 11.6682 14 11 14H2C1.33185 14 0.702358 13.4424 0.778277 12.6767C0.983033 10.6115 2.27961 8.86779 4.08276 8.0312C3.4156 7.3939 3 6.49548 3 5.5ZM8.5 5.5C8.5 4.39543 7.60457 3.5 6.5 3.5C5.39543 3.5 4.5 4.39543 4.5 5.5C4.5 6.60457 5.39543 7.5 6.5 7.5C7.60457 7.5 8.5 6.60457 8.5 5.5ZM2.31585 12.5H10.6841C10.3303 10.5108 8.59114 9 6.5 9C4.40886 9 2.66975 10.5108 2.31585 12.5Z', 'M11 3C10.5858 3 10.25 3.33579 10.25 3.75C10.25 4.16421 10.5858 4.5 11 4.5C11.6904 4.5 12.25 5.05964 12.25 5.75C12.25 6.44036 11.6904 7 11 7C10.5858 7 10.25 7.33579 10.25 7.75C10.25 8.16421 10.5858 8.5 11 8.5C12.6569 8.5 14 9.84315 14 11.5C14 11.9142 14.3358 12.25 14.75 12.25C15.1642 12.25 15.5 11.9142 15.5 11.5C15.5 9.77473 14.5291 8.27622 13.1038 7.52106C13.507 7.0426 13.75 6.42467 13.75 5.75C13.75 4.23122 12.5188 3 11 3Z`;
2505
2507
  const AttachmentPath = `M9.1773124,11.8057395 C7.96668709,13.0163648 6.00387566,13.0163648 4.79325035,11.8057395 C3.58262505,10.5951142 3.58262505,8.63230278 4.78926443,7.4257012 L8.27628904,3.87217601 C8.50840068,3.63567937 8.88828274,3.63209333 9.12479868,3.86418566 C9.36131462,4.096278 9.36490066,4.47616006 9.13280833,4.712676 L5.64177849,8.27020561 C4.89978234,9.01220177 4.89978234,10.2152152 5.64177849,10.9572114 C6.38377464,11.6992075 7.5867881,11.6992075 8.32878426,10.9572114 L12.2321177,7.05387799 C13.3493901,5.93660552 13.2780395,4.02707269 12.0410949,2.79012806 C10.8041502,1.55318343 8.8946174,1.4818328 7.77734493,2.59910526 L3.72686067,6.64958953 C2.02034367,8.35610653 2.02835883,11.1329242 3.74721224,12.8517776 C5.46606565,14.570631 8.24288334,14.5786462 9.94940034,12.8721292 L13.5980637,9.22346588 C13.8323782,8.98915131 14.2122772,8.98915131 14.4465918,9.22346588 C14.6809064,9.45778046 14.6809064,9.83767945 14.4465918,10.071994 L10.7979285,13.7206573 C8.62168228,15.8969035 5.08507361,15.8866953 2.8986841,13.7003058 C0.712294592,11.5139163 0.702086332,7.97730759 2.87833253,5.8010614 L6.9288168,1.75057713 C8.52947856,0.149915361 11.1976354,0.249612361 12.889623,1.94159992 C14.5816106,3.63358749 14.6813076,6.30174436 13.0806458,7.90240612 L9.1773124,11.8057395 Z`;
2506
2508
  const EditPath = `M2,8.33918294 L10.7095952,0 L15,4.17020474 L6.53816398,13 L2,13 L2,8.33918294 Z M3.2,8.85157902 L3.2,11.8 L6.02608482,11.8 L13.3091776,4.20020516 L10.7033283,1.66736141 L3.2,8.85157902 Z M0,16 L0,14.8 L16,14.8 L16,16 L0,16 Z`;
2509
+ const RowDragPath = `M6,3 C6.55228475,3 7,2.55228475 7,2 C7,1.44771525 6.55228475,1 6,1 C5.44771525,1 5,1.44771525 5,2 C5,2.55228475 5.44771525,3 6,3 Z M10,3 C10.5522847,3 11,2.55228475 11,2 C11,1.44771525 10.5522847,1 10,1 C9.44771525,1 9,1.44771525 9,2 C9,2.55228475 9.44771525,3 10,3 Z M6,7 C6.55228475,7 7,6.55228475 7,6 C7,5.44771525 6.55228475,5 6,5 C5.44771525,5 5,5.44771525 5,6 C5,6.55228475 5.44771525,7 6,7 Z M10,7 C10.5522847,7 11,6.55228475 11,6 C11,5.44771525 10.5522847,5 10,5 C9.44771525,5 9,5.44771525 9,6 C9,6.55228475 9.44771525,7 10,7 Z M6,11 C6.55228475,11 7,10.5522847 7,10 C7,9.44771525 6.55228475,9 6,9 C5.44771525,9 5,9.44771525 5,10 C5,10.5522847 5.44771525,11 6,11 Z M10,11 C10.5522847,11 11,10.5522847 11,10 C11,9.44771525 10.5522847,9 10,9 C9.44771525,9 9,9.44771525 9,10 C9,10.5522847 9.44771525,11 10,11 Z M6,15 C6.55228475,15 7,14.5522847 7,14 C7,13.4477153 6.55228475,13 6,13 C5.44771525,13 5,13.4477153 5,14 C5,14.5522847 5.44771525,15 6,15 Z M10,15 C10.5522847,15 11,14.5522847 11,14 C11,13.4477153 10.5522847,13 10,13 C9.44771525,13 9,13.4477153 9,14 C9,14.5522847 9.44771525,15 10,15 Z`;
2507
2510
 
2508
2511
  const DEFAULT_FONT_SIZE = 14;
2509
2512
  const DEFAULT_FONT_FAMILY = '-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,PingFang SC,Helvetica Neue,Noto Sans,Noto Sans CJK SC,Microsoft Yahei,Arial,Hiragino Sans GB,sans-serif';
@@ -2656,79 +2659,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
2656
2659
  type: Injectable
2657
2660
  }], ctorParameters: () => [{ type: i1$1.ThyPopover }] });
2658
2661
 
2659
- function getColumnIndicesSizeMap(aiTable, fields) {
2660
- const fieldSizeMap = aiTable.gridData().fieldsSizeMap;
2661
- const columnIndicesSizeMap = {};
2662
- fields?.forEach((field, index) => {
2663
- columnIndicesSizeMap[index] = fieldSizeMap[field._id] ?? getFieldOptionByField(aiTable, field).width;
2664
- });
2665
- return columnIndicesSizeMap;
2666
- }
2667
- /**
2668
- * 获取单元格位置
2669
- * 根据单元格是否是第一列/最后一列确定单元格所在的位置
2670
- */
2671
- function getCellHorizontalPosition(options) {
2672
- const { columnWidth } = options;
2673
- return { width: columnWidth, offset: 0 };
2674
- }
2675
- function transformCellValue(aiTable, field, cellValue) {
2676
- if (cellValue === undefined || cellValue === null) {
2677
- return cellValue;
2678
- }
2679
- const fieldService = AI_TABLE_GRID_FIELD_SERVICE_MAP.get(aiTable);
2680
- if (!fieldService) {
2681
- return cellValue;
2682
- }
2683
- const fieldRenderers = fieldService.aiFieldConfig?.fieldRenderers;
2684
- if (!fieldRenderers) {
2685
- return cellValue;
2686
- }
2687
- const cellTransform = fieldRenderers[field.type]?.transform;
2688
- if (!cellTransform) {
2689
- return cellValue;
2690
- }
2691
- const cellText = cellTransform(field, cellValue);
2692
- if (cellText == null) {
2693
- return cellValue;
2694
- }
2695
- return cellText;
2696
- }
2697
- /**
2698
- * `\u4e00`: https://www.compart.com/en/unicode/U+4E00
2699
- * `\u9fa5`: https://www.compart.com/en/unicode/U+9FA5
2700
- */
2701
- const UNIFIED_IDEOGRAPHS_REGEX = /^[\u4e00-\u9fa5]+$/;
2702
- const SET_OF_LETTERS_REGEX = /^[a-zA-Z\/ ]+$/;
2703
- function getAvatarShortName(name) {
2704
- if (!name) {
2705
- return '';
2706
- }
2707
- name = name.trim();
2708
- if (UNIFIED_IDEOGRAPHS_REGEX.test(name) && name.length > 2) {
2709
- return name.slice(name.length - 2);
2710
- }
2711
- if (SET_OF_LETTERS_REGEX.test(name) && name.indexOf(' ') > 0) {
2712
- const words = name.split(' ');
2713
- return (words[0].slice(0, 1) + words[1].slice(0, 1)).toUpperCase();
2714
- }
2715
- return name.length > 2 ? name.slice(0, 2).toUpperCase() : name.toUpperCase();
2716
- }
2717
- function getAvatarBgColor(name) {
2718
- if (!name) {
2719
- return;
2720
- }
2721
- const colors = ['#56abfb', '#5dcfff', '#84e17e', '#73d897', '#ff9f73', '#fa8888', '#fb7fb7', '#9a7ef4', '#868af6'];
2722
- const nameArray = name.split('');
2723
- const code = name && name.length > 0
2724
- ? nameArray.reduce(function (result, item) {
2725
- result.value += item.charCodeAt(0);
2726
- return result;
2727
- }, { value: 0 }).value
2728
- : 0;
2729
- return colors[code % 9];
2730
- }
2731
-
2732
2662
  /**
2733
2663
  * 生成目标名称
2734
2664
  */
@@ -2787,360 +2717,72 @@ function isEmpty(value) {
2787
2717
  return isUndefinedOrNull(value) || value === '';
2788
2718
  }
2789
2719
 
2790
- function getPlaceHolderCellsConfigs(options) {
2791
- const { aiTable, coordinate, columnStartIndex, columnStopIndex, rowStartIndex, rowStopIndex } = options;
2792
- const { linearRows } = aiTable.context;
2793
- const { rowHeight, columnCount, rowCount } = coordinate;
2794
- const visibleColumns = AITable.getVisibleFields(aiTable);
2795
- let configs = [];
2796
- for (let columnIndex = columnStartIndex; columnIndex <= columnStopIndex; columnIndex++) {
2797
- // 当前列索引超出总列数范围,返回空
2798
- if (columnIndex > columnCount - 1) {
2799
- return [];
2800
- }
2801
- const field = visibleColumns[columnIndex];
2802
- const fieldId = field._id;
2803
- // 当前列不存在,返回空
2804
- if (field == null) {
2805
- return [];
2806
- }
2807
- // 当前列的 X 轴偏移量和列宽度
2808
- const x = coordinate.getColumnOffset(columnIndex) + AI_TABLE_OFFSET * 2;
2809
- const columnWidth = coordinate.getColumnWidth(columnIndex);
2810
- for (let rowIndex = rowStartIndex; rowIndex <= rowStopIndex; rowIndex++) {
2811
- // 当前行索引是否超出总行数范围,超出则退出循环
2812
- if (rowIndex > rowCount - 1) {
2813
- break;
2814
- }
2815
- const row = linearRows()[rowIndex];
2816
- const { _id: recordId, type } = row;
2817
- if (type !== AITableRowType.record) {
2818
- continue;
2819
- }
2820
- // 当前行的 Y 轴偏移量,并根据列宽和列索引获取单元格的水平位置(宽度和偏移量)
2821
- const y = coordinate.getRowOffset(rowIndex) + AI_TABLE_OFFSET * 2;
2822
- const { width, offset } = getCellHorizontalPosition({
2823
- columnWidth,
2824
- columnIndex,
2825
- columnCount
2826
- });
2827
- const height = rowHeight - AI_TABLE_OFFSET * 4;
2828
- configs.unshift({
2829
- key: `placeholder-cell-${fieldId}-${recordId}`,
2830
- name: generateTargetName({
2831
- targetName: AI_TABLE_CELL,
2832
- fieldId,
2833
- recordId
2834
- }),
2835
- x: x + offset,
2836
- y,
2837
- width: width - AI_TABLE_OFFSET * 4,
2838
- height,
2839
- fill: Colors.transparent,
2840
- strokeEnabled: false,
2841
- hitStrokeWidth: 0,
2842
- transformsEnabled: 'position',
2843
- perfectDrawEnabled: false,
2844
- shadowEnabled: false
2845
- });
2846
- }
2720
+ const zhIntlCollator = typeof Intl !== 'undefined' ? new Intl.Collator('zh-CN') : undefined;
2721
+ function compareNumber(a, b) {
2722
+ if (isEmpty(a) && isEmpty(b)) {
2723
+ return 0;
2847
2724
  }
2848
- return configs;
2725
+ if (isEmpty(a)) {
2726
+ return -1;
2727
+ }
2728
+ if (isEmpty(b)) {
2729
+ return 1;
2730
+ }
2731
+ return a === b ? 0 : a > b ? 1 : -1;
2732
+ }
2733
+ function compareString(a, b) {
2734
+ if (a === b) {
2735
+ return 0;
2736
+ }
2737
+ if (a == null) {
2738
+ return -1;
2739
+ }
2740
+ if (b == null) {
2741
+ return 1;
2742
+ }
2743
+ // pinyin sort
2744
+ return a === b ? 0 : zhIntlCollator ? zhIntlCollator.compare(a, b) : a.localeCompare(b, 'zh-CN') > 0 ? 1 : -1;
2745
+ }
2746
+ function stringInclude(str, searchStr) {
2747
+ return str.toLowerCase().includes(searchStr.trim().toLowerCase());
2849
2748
  }
2850
-
2851
- const fontCache = {};
2852
- const textDataCache = new LRUCache({
2853
- max: DEFAULT_TEXT_MAX_CACHE
2854
- });
2855
2749
  /**
2856
- * 计算给定文本在指定字体和 Canvas 环境下的宽度。
2857
- * 它通过缓存机制来优化性能,避免重复计算相同文本的宽度
2750
+ * 两数组是否有交集
2858
2751
  */
2859
- const getTextWidth = (ctx, text, font) => {
2860
- let width = 0;
2861
- if (!text || typeof text !== 'string') {
2862
- return width;
2863
- }
2864
- let cacheOfFont = fontCache[font];
2865
- if (!cacheOfFont) {
2866
- cacheOfFont = fontCache[font] = new LRUCache({
2867
- max: 500
2868
- });
2752
+ function hasIntersect(array1, array2) {
2753
+ if (!Array.isArray(array1) || !Array.isArray(array2)) {
2754
+ return false;
2869
2755
  }
2870
- width = cacheOfFont.get(text);
2871
- if (width == null) {
2872
- ctx.font = font;
2873
- width = ctx.measureText(text).width;
2874
- cacheOfFont.set(text, width);
2756
+ const set1 = new Set(array1);
2757
+ const set2 = new Set(array2);
2758
+ for (const element of set1) {
2759
+ if (set2.has(element)) {
2760
+ return true;
2761
+ }
2875
2762
  }
2876
- return width;
2877
- };
2763
+ return false;
2764
+ }
2878
2765
 
2879
- const imageCache = (() => {
2880
- const imageMap = {};
2881
- const imgPromises = [];
2882
- function loadImage(name, src, option) {
2883
- imgPromises.push(new Promise((resolve, reject) => {
2884
- const img = new Image();
2885
- img.src = src;
2886
- img.referrerPolicy = 'no-referrer';
2887
- if (!option?.crossOrigin) {
2888
- img.crossOrigin = 'Anonymous';
2889
- }
2890
- imageMap[name] = {
2891
- img,
2892
- success: false
2893
- };
2894
- try {
2895
- img.onload = () => {
2896
- imageMap[name] = {
2897
- img,
2898
- success: true
2899
- };
2900
- resolve({
2901
- name,
2902
- img
2903
- });
2904
- };
2766
+ class Field {
2767
+ // 筛选
2768
+ isMeetFilter(condition, cellValue, options) {
2769
+ switch (condition.operation) {
2770
+ case AITableFilterOperation.empty:
2771
+ case AITableFilterOperation.exists: {
2772
+ return this.isEmptyOrNot(condition.operation, cellValue);
2905
2773
  }
2906
- catch (err) {
2907
- imageMap[name] = {
2908
- img,
2909
- success: false
2910
- };
2911
- reject(err);
2774
+ default: {
2775
+ return true;
2912
2776
  }
2913
- }));
2914
- }
2915
- function imageMapOnload(callback) {
2916
- Promise.all(imgPromises).then(callback);
2777
+ }
2917
2778
  }
2918
- function getImage(name) {
2919
- const imgInfo = imageMap[name];
2920
- if (imgInfo == null) {
2921
- return null;
2779
+ // 查找
2780
+ cellFullText(transformValue, field, references) {
2781
+ let fullText = [];
2782
+ if (!isEmpty(transformValue)) {
2783
+ fullText.push(String(transformValue));
2922
2784
  }
2923
- const { img, success } = imgInfo;
2924
- if (!success)
2925
- return false;
2926
- return img;
2927
- }
2928
- return {
2929
- loadImage,
2930
- getImage,
2931
- imageMapOnload,
2932
- imageMap
2933
- };
2934
- })();
2935
-
2936
- const isWindowsOS = () => {
2937
- const agent = navigator.userAgent.toLowerCase();
2938
- if (agent.indexOf('win32') >= 0 || agent.indexOf('wow32') >= 0) {
2939
- return true;
2940
- }
2941
- if (agent.indexOf('win64') >= 0 || agent.indexOf('wow64') >= 0) {
2942
- return true;
2943
- }
2944
- return false;
2945
- };
2946
- const isWindows = isWindowsOS();
2947
- const isMac = () => {
2948
- const agent = navigator.userAgent;
2949
- return /macintosh/i.test(agent);
2950
- };
2951
-
2952
- const isClipboardWriteSupported = () => {
2953
- return 'clipboard' in navigator && 'write' in navigator.clipboard;
2954
- };
2955
- const isClipboardWriteTextSupported = () => {
2956
- return 'clipboard' in navigator && 'writeText' in navigator.clipboard;
2957
- };
2958
- const isClipboardReadSupported = () => {
2959
- return 'clipboard' in navigator && 'read' in navigator.clipboard;
2960
- };
2961
- const isClipboardReadTextSupported = () => {
2962
- return 'clipboard' in navigator && 'readText' in navigator.clipboard;
2963
- };
2964
- const writeToClipboard = async (data) => {
2965
- try {
2966
- const { text, html } = data;
2967
- if (isClipboardWriteSupported()) {
2968
- const clipboardItem = new ClipboardItem({
2969
- 'text/plain': new Blob([text], { type: 'text/plain' }),
2970
- 'text/html': new Blob([html], { type: 'text/html' })
2971
- });
2972
- await navigator.clipboard.write([clipboardItem]);
2973
- }
2974
- else if (isClipboardWriteTextSupported()) {
2975
- await navigator.clipboard.writeText(text);
2976
- }
2977
- else {
2978
- const textarea = document.createElement('textarea');
2979
- textarea.value = text;
2980
- document.body.appendChild(textarea);
2981
- textarea.select();
2982
- document.execCommand('copy');
2983
- document.body.removeChild(textarea);
2984
- }
2985
- }
2986
- catch (error) {
2987
- console.warn('Failed to write clipboard:', error);
2988
- }
2989
- };
2990
- const readFromClipboard = async () => {
2991
- try {
2992
- let clipboardData = {};
2993
- if (isClipboardReadSupported()) {
2994
- const clipboardItems = await navigator.clipboard.read();
2995
- if (Array.isArray(clipboardItems) && clipboardItems[0] instanceof ClipboardItem) {
2996
- for (const item of clipboardItems) {
2997
- if (item.types.includes('text/html')) {
2998
- const blob = await item.getType('text/html');
2999
- clipboardData.html = await blob.text();
3000
- }
3001
- if (item.types.includes('text/plain')) {
3002
- const blob = await item.getType('text/plain');
3003
- clipboardData.text = await blob.text();
3004
- }
3005
- }
3006
- }
3007
- }
3008
- else if (isClipboardReadTextSupported()) {
3009
- const clipboardText = await navigator.clipboard.readText();
3010
- clipboardData.text = clipboardText;
3011
- }
3012
- else {
3013
- const pastePromise = new Promise((resolve) => {
3014
- const textarea = document.createElement('textarea');
3015
- document.body.appendChild(textarea);
3016
- const handlePaste = (e) => {
3017
- const text = e.clipboardData?.getData('text') || '';
3018
- const html = e.clipboardData?.getData('text/html') || '';
3019
- resolve({
3020
- text,
3021
- html: html || undefined
3022
- });
3023
- textarea.removeEventListener('paste', handlePaste);
3024
- };
3025
- textarea.addEventListener('paste', handlePaste);
3026
- textarea.focus();
3027
- document.execCommand('paste');
3028
- document.body.removeChild(textarea);
3029
- });
3030
- clipboardData = await pastePromise;
3031
- }
3032
- return clipboardData;
3033
- }
3034
- catch (error) {
3035
- console.warn('Failed to read clipboard:', error);
3036
- return null;
3037
- }
3038
- };
3039
-
3040
- function extractText(text) {
3041
- if (!text)
3042
- return '';
3043
- // 如果文本包含HTML链接标签,提取链接文本
3044
- if (text.includes('<a')) {
3045
- const aTagMatch = text.match(/<a[^>]*>(.*?)<\/a>/i);
3046
- if (aTagMatch?.[1]?.trim()) {
3047
- return aTagMatch[1].trim();
3048
- }
3049
- }
3050
- return text;
3051
- }
3052
- function extractLinkUrl(text) {
3053
- if (!text)
3054
- return null;
3055
- // 1. 从HTML链接标签中提取URL,比如 https://mail.qq.com
3056
- const hrefMatch = text.match(/href="([^"]+)"/);
3057
- if (hrefMatch?.[1]) {
3058
- return hrefMatch[1];
3059
- }
3060
- // 2. 匹配完整的URL(带协议),比如 https://mail.qq.com
3061
- const protocolAndDomainMatch = text.match(/^(?:\w+:)?\/\/(\S+)$/);
3062
- if (protocolAndDomainMatch?.[0]) {
3063
- return protocolAndDomainMatch[0];
3064
- }
3065
- // 3. 匹配localhost格式,比如 localhost:6100
3066
- const localhostDomainMatch = text.match(/^localhost[\:?\d]*(?:[^\:?\d]\S*)?$/);
3067
- if (localhostDomainMatch?.[0]) {
3068
- return `http://${localhostDomainMatch[0]}`;
3069
- }
3070
- // 4. 匹配普通域名,比如 www.baidu.com
3071
- const ordinaryDomainMatch = text.match(/^[^\s\.]+\.\S{2,}$/);
3072
- if (ordinaryDomainMatch?.[0]) {
3073
- return `http://${ordinaryDomainMatch[0]}`;
3074
- }
3075
- return null;
3076
- }
3077
-
3078
- const zhIntlCollator = typeof Intl !== 'undefined' ? new Intl.Collator('zh-CN') : undefined;
3079
- function compareNumber(a, b) {
3080
- if (isEmpty(a) && isEmpty(b)) {
3081
- return 0;
3082
- }
3083
- if (isEmpty(a)) {
3084
- return -1;
3085
- }
3086
- if (isEmpty(b)) {
3087
- return 1;
3088
- }
3089
- return a === b ? 0 : a > b ? 1 : -1;
3090
- }
3091
- function compareString(a, b) {
3092
- if (a === b) {
3093
- return 0;
3094
- }
3095
- if (a == null) {
3096
- return -1;
3097
- }
3098
- if (b == null) {
3099
- return 1;
3100
- }
3101
- // pinyin sort
3102
- return a === b ? 0 : zhIntlCollator ? zhIntlCollator.compare(a, b) : a.localeCompare(b, 'zh-CN') > 0 ? 1 : -1;
3103
- }
3104
- function stringInclude(str, searchStr) {
3105
- return str.toLowerCase().includes(searchStr.trim().toLowerCase());
3106
- }
3107
- /**
3108
- * 两数组是否有交集
3109
- */
3110
- function hasIntersect(array1, array2) {
3111
- if (!Array.isArray(array1) || !Array.isArray(array2)) {
3112
- return false;
3113
- }
3114
- const set1 = new Set(array1);
3115
- const set2 = new Set(array2);
3116
- for (const element of set1) {
3117
- if (set2.has(element)) {
3118
- return true;
3119
- }
3120
- }
3121
- return false;
3122
- }
3123
-
3124
- class Field {
3125
- // 筛选
3126
- isMeetFilter(condition, cellValue, options) {
3127
- switch (condition.operation) {
3128
- case AITableFilterOperation.empty:
3129
- case AITableFilterOperation.exists: {
3130
- return this.isEmptyOrNot(condition.operation, cellValue);
3131
- }
3132
- default: {
3133
- return true;
3134
- }
3135
- }
3136
- }
3137
- // 查找
3138
- cellFullText(transformValue, field, references) {
3139
- let fullText = [];
3140
- if (!isEmpty(transformValue)) {
3141
- fullText.push(String(transformValue));
3142
- }
3143
- return fullText;
2785
+ return fullText;
3144
2786
  }
3145
2787
  isEmptyOrNot(operation, cellValue) {
3146
2788
  switch (operation) {
@@ -3346,61 +2988,222 @@ function transformDateValue(text) {
3346
2988
  }
3347
2989
  }
3348
2990
 
3349
- class LinkField extends Field {
3350
- isValid(cellValue) {
3351
- return (cellValue && typeof cellValue === 'object' && 'url' in cellValue && 'text' in cellValue) || cellValue === null;
3352
- }
3353
- isMeetFilter(condition, cellValue) {
3354
- const cellTextValue = cellValue?.text;
3355
- switch (condition.operation) {
3356
- case AITableFilterOperation.empty:
3357
- return isEmpty(cellTextValue);
3358
- case AITableFilterOperation.exists:
3359
- return !isEmpty(cellTextValue);
3360
- case AITableFilterOperation.contain:
3361
- return !isEmpty(cellTextValue) && stringInclude(cellTextValue, condition.value);
3362
- default:
3363
- return super.isMeetFilter(condition, cellTextValue);
2991
+ const isClipboardWriteSupported = () => {
2992
+ return 'clipboard' in navigator && 'write' in navigator.clipboard;
2993
+ };
2994
+ const isClipboardWriteTextSupported = () => {
2995
+ return 'clipboard' in navigator && 'writeText' in navigator.clipboard;
2996
+ };
2997
+ const isClipboardReadSupported = () => {
2998
+ return 'clipboard' in navigator && 'read' in navigator.clipboard;
2999
+ };
3000
+ const isClipboardReadTextSupported = () => {
3001
+ return 'clipboard' in navigator && 'readText' in navigator.clipboard;
3002
+ };
3003
+ const writeToClipboard = async (data) => {
3004
+ try {
3005
+ const { text, html } = data;
3006
+ if (isClipboardWriteSupported()) {
3007
+ const clipboardItem = new ClipboardItem({
3008
+ 'text/plain': new Blob([text], { type: 'text/plain' }),
3009
+ 'text/html': new Blob([html], { type: 'text/html' })
3010
+ });
3011
+ await navigator.clipboard.write([clipboardItem]);
3364
3012
  }
3365
- }
3366
- compare(cellValue1, cellValue2) {
3367
- return compareString(cellValueToSortValue$3(cellValue1), cellValueToSortValue$3(cellValue2));
3368
- }
3369
- cellFullText(transformValue) {
3370
- let texts = [];
3371
- if (!isEmpty(transformValue?.text)) {
3372
- texts.push(transformValue.text);
3013
+ else if (isClipboardWriteTextSupported()) {
3014
+ await navigator.clipboard.writeText(text);
3015
+ }
3016
+ else {
3017
+ const textarea = document.createElement('textarea');
3018
+ textarea.value = text;
3019
+ document.body.appendChild(textarea);
3020
+ textarea.select();
3021
+ document.execCommand('copy');
3022
+ document.body.removeChild(textarea);
3373
3023
  }
3374
- return texts;
3375
3024
  }
3376
- toFieldValue(plainText, targetField, originData) {
3377
- return toLinkFieldValue(plainText, targetField, originData);
3025
+ catch (error) {
3026
+ console.warn('Failed to write clipboard:', error);
3378
3027
  }
3379
- }
3380
- function toLinkFieldValue(plainText, targetField, originData) {
3381
- if (originData) {
3382
- const { field, cellValue } = originData;
3383
- if (field.type === AITableFieldType.link) {
3384
- return cellValue;
3028
+ };
3029
+ const readFromClipboard = async () => {
3030
+ try {
3031
+ let clipboardData = {};
3032
+ if (isClipboardReadSupported()) {
3033
+ const clipboardItems = await navigator.clipboard.read();
3034
+ if (Array.isArray(clipboardItems) && clipboardItems[0] instanceof ClipboardItem) {
3035
+ for (const item of clipboardItems) {
3036
+ if (item.types.includes('text/html')) {
3037
+ const blob = await item.getType('text/html');
3038
+ clipboardData.html = await blob.text();
3039
+ }
3040
+ if (item.types.includes('text/plain')) {
3041
+ const blob = await item.getType('text/plain');
3042
+ clipboardData.text = await blob.text();
3043
+ }
3044
+ }
3045
+ }
3046
+ }
3047
+ else if (isClipboardReadTextSupported()) {
3048
+ const clipboardText = await navigator.clipboard.readText();
3049
+ clipboardData.text = clipboardText;
3050
+ }
3051
+ else {
3052
+ const pastePromise = new Promise((resolve) => {
3053
+ const textarea = document.createElement('textarea');
3054
+ document.body.appendChild(textarea);
3055
+ const handlePaste = (e) => {
3056
+ const text = e.clipboardData?.getData('text') || '';
3057
+ const html = e.clipboardData?.getData('text/html') || '';
3058
+ resolve({
3059
+ text,
3060
+ html: html || undefined
3061
+ });
3062
+ textarea.removeEventListener('paste', handlePaste);
3063
+ };
3064
+ textarea.addEventListener('paste', handlePaste);
3065
+ textarea.focus();
3066
+ document.execCommand('paste');
3067
+ document.body.removeChild(textarea);
3068
+ });
3069
+ clipboardData = await pastePromise;
3385
3070
  }
3071
+ return clipboardData;
3386
3072
  }
3387
- else {
3388
- const url = extractLinkUrl(plainText);
3389
- const text = extractText(plainText);
3390
- if (url && text) {
3391
- return {
3392
- url,
3393
- text
3394
- };
3073
+ catch (error) {
3074
+ console.warn('Failed to read clipboard:', error);
3075
+ return null;
3076
+ }
3077
+ };
3078
+
3079
+ function extractText(text) {
3080
+ if (!text)
3081
+ return '';
3082
+ // 如果文本包含HTML链接标签,提取链接文本
3083
+ if (text.includes('<a')) {
3084
+ const aTagMatch = text.match(/<a[^>]*>(.*?)<\/a>/i);
3085
+ if (aTagMatch?.[1]?.trim()) {
3086
+ return aTagMatch[1].trim();
3395
3087
  }
3396
3088
  }
3089
+ return text;
3090
+ }
3091
+ function extractLinkUrl(text) {
3092
+ if (!text)
3093
+ return null;
3094
+ // 1. 从HTML链接标签中提取URL,比如 https://mail.qq.com
3095
+ const hrefMatch = text.match(/href="([^"]+)"/);
3096
+ if (hrefMatch?.[1]) {
3097
+ return hrefMatch[1];
3098
+ }
3099
+ // 2. 匹配完整的URL(带协议),比如 https://mail.qq.com
3100
+ const protocolAndDomainMatch = text.match(/^(?:\w+:)?\/\/(\S+)$/);
3101
+ if (protocolAndDomainMatch?.[0]) {
3102
+ return protocolAndDomainMatch[0];
3103
+ }
3104
+ // 3. 匹配localhost格式,比如 localhost:6100
3105
+ const localhostDomainMatch = text.match(/^localhost[\:?\d]*(?:[^\:?\d]\S*)?$/);
3106
+ if (localhostDomainMatch?.[0]) {
3107
+ return `http://${localhostDomainMatch[0]}`;
3108
+ }
3109
+ // 4. 匹配普通域名,比如 www.baidu.com
3110
+ const ordinaryDomainMatch = text.match(/^[^\s\.]+\.\S{2,}$/);
3111
+ if (ordinaryDomainMatch?.[0]) {
3112
+ return `http://${ordinaryDomainMatch[0]}`;
3113
+ }
3397
3114
  return null;
3398
3115
  }
3399
- function cellValueToSortValue$3(cellValue) {
3400
- return (cellValue && cellValue.text && cellValue.text.trim()) || null;
3116
+
3117
+ const aiTableFragmentAttribute = 'ai-table-fragment';
3118
+ const buildClipboardData = (aiTable) => {
3119
+ const copiedCells = Array.from(aiTable.selection().selectedCells);
3120
+ if (!copiedCells.length) {
3121
+ return null;
3122
+ }
3123
+ let copiedFieldIds = new Set();
3124
+ let copiedRecordIds = new Set();
3125
+ copiedCells.forEach((cellPath) => {
3126
+ const [recordId, fieldId] = cellPath.split(':');
3127
+ copiedFieldIds.add(fieldId);
3128
+ copiedRecordIds.add(recordId);
3129
+ });
3130
+ const fieldIds = Array.from(copiedFieldIds);
3131
+ const recordIds = Array.from(copiedRecordIds);
3132
+ const aiTableContent = buildAITableContent(aiTable, fieldIds, recordIds);
3133
+ const clipboardContent = buildClipboardContent(aiTable, fieldIds, recordIds);
3134
+ return mergeClipboardContent(clipboardContent, aiTableContent);
3135
+ };
3136
+ const encodeAITableContent = (aiTableContent) => {
3137
+ const stringifiedData = JSON.stringify(aiTableContent);
3138
+ return window.btoa(encodeURIComponent(stringifiedData));
3139
+ };
3140
+ function mergeClipboardContent(clipboardContent, aiTableContent) {
3141
+ const encodedAITableContent = encodeAITableContent(aiTableContent);
3142
+ const formattedContent = {
3143
+ text: clipboardContent.map((row) => row.map((column) => column.text).join('\t')).join('\n'),
3144
+ html: `<table ${aiTableFragmentAttribute}="${encodedAITableContent}">${clipboardContent.map((row) => `<tr>${row.map((column) => `<td>${column.html}</td>`).join('')}</tr>`).join('')}</table>`
3145
+ };
3146
+ return formattedContent;
3147
+ }
3148
+ function buildAITableContent(aiTable, fieldIds, recordIds) {
3149
+ const fields = fieldIds.map((fieldId) => {
3150
+ return aiTable.fieldsMap()[fieldId];
3151
+ });
3152
+ const records = recordIds.map((recordId) => {
3153
+ const record = aiTable.recordsMap()[recordId];
3154
+ let newRecord = {
3155
+ _id: record._id,
3156
+ values: {}
3157
+ };
3158
+ fieldIds.forEach((fieldId) => {
3159
+ const field = aiTable.fieldsMap()[fieldId];
3160
+ if (isSystemField(field)) {
3161
+ const fieldType = field.type;
3162
+ newRecord = {
3163
+ ...newRecord,
3164
+ [fieldType]: getSystemFieldValue(record, fieldType)
3165
+ };
3166
+ }
3167
+ else {
3168
+ newRecord.values = {
3169
+ ...newRecord.values,
3170
+ [fieldId]: getFieldValue(record, field)
3171
+ };
3172
+ }
3173
+ });
3174
+ return newRecord;
3175
+ });
3176
+ return {
3177
+ fields,
3178
+ records
3179
+ };
3180
+ }
3181
+ function buildClipboardContent(aiTable, fieldIds, recordIds) {
3182
+ const clipboardContent = [];
3183
+ const references = aiTable.context.references();
3184
+ recordIds.forEach((recordId) => {
3185
+ const record = aiTable.recordsMap()[recordId];
3186
+ const row = [];
3187
+ fieldIds.forEach((fieldId) => {
3188
+ const field = aiTable.fieldsMap()[fieldId];
3189
+ const cellValue = getFieldValue(record, field);
3190
+ const transformValue = transformCellValue(aiTable, field, cellValue);
3191
+ const cellTexts = FieldModelMap[field.type].cellFullText(transformValue, field, references);
3192
+ let cellContent = {
3193
+ text: cellTexts.join(','),
3194
+ html: cellTexts.join(',')
3195
+ };
3196
+ if (field.type === AITableFieldType.link && cellValue && cellValue.url) {
3197
+ cellContent.html = `<a href="${cellValue.url}" target="_blank">${cellValue.text}</a>`;
3198
+ }
3199
+ row.push(cellContent);
3200
+ });
3201
+ clipboardContent.push(row);
3202
+ });
3203
+ return clipboardContent;
3401
3204
  }
3402
3205
 
3403
- class MemberField extends Field {
3206
+ class SelectField extends Field {
3404
3207
  isValid(cellValue) {
3405
3208
  return Array.isArray(cellValue) || cellValue === null;
3406
3209
  }
@@ -3419,310 +3222,366 @@ class MemberField extends Field {
3419
3222
  }
3420
3223
  }
3421
3224
  compare(cellValue1, cellValue2, references, sortKey, options) {
3422
- const value1 = cellValueToSortValue$2(cellValue1, options.field, references, sortKey);
3423
- const value2 = cellValueToSortValue$2(cellValue2, options.field, references, sortKey);
3225
+ const value1 = cellValueToSortValue$3(cellValue1, options.field);
3226
+ const value2 = cellValueToSortValue$3(cellValue2, options.field);
3424
3227
  return compareString(value1, value2);
3425
3228
  }
3426
- cellFullText(transformValue, field, references) {
3229
+ cellFullText(transformValue, field) {
3427
3230
  let fullText = [];
3428
- if (transformValue?.length && references) {
3429
- for (let index = 0; index < transformValue.length; index++) {
3430
- const userInfo = references?.members[transformValue[index]];
3431
- if (!userInfo) {
3432
- continue;
3433
- }
3434
- if (userInfo.display_name) {
3435
- fullText.push(userInfo.display_name);
3231
+ const optionsMap = helpers.keyBy(field.settings.options || [], '_id');
3232
+ if (transformValue && Array.isArray(transformValue) && transformValue.length) {
3233
+ transformValue.forEach((optionId) => {
3234
+ const option = optionsMap[optionId];
3235
+ if (option && option.text) {
3236
+ fullText.push(option.text);
3436
3237
  }
3437
- }
3238
+ });
3438
3239
  }
3439
3240
  return fullText;
3440
3241
  }
3441
- toFieldValue(plainText, targetField, originData, references) {
3442
- return toMemberFieldValue(plainText, targetField, originData, references);
3242
+ toFieldValue(plainText, targetField, originData) {
3243
+ return toSelectFieldValue(plainText, targetField, originData);
3443
3244
  }
3444
3245
  }
3445
- function toMemberFieldValue(plainText, targetField, originData, references) {
3446
- if (targetField.type == AITableFieldType.createdBy || targetField.type == AITableFieldType.updatedBy) {
3447
- return null;
3448
- }
3449
- const isMultiple = targetField.settings?.is_multiple;
3450
- if (originData) {
3451
- const { field, cellValue } = originData;
3452
- switch (field.type) {
3453
- case AITableFieldType.member:
3454
- if (Array.isArray(cellValue) && cellValue.length) {
3455
- return isMultiple ? cellValue : [cellValue[0]];
3246
+ function toSelectFieldValue(plainText, targetField, originData) {
3247
+ return null;
3248
+ }
3249
+ function processPastedValueForSelect(plainText, targetField, originData) {
3250
+ const targetFieldOptions = targetField.settings?.options || [];
3251
+ const targetOptionStyle = targetField.settings?.option_style || AITableSelectOptionStyle.text;
3252
+ let existOptionIds = [];
3253
+ let newOptions = [];
3254
+ let cellFullTexts = plainText.split(',').map((text) => text.trim());
3255
+ const { field, cellValue } = originData || {};
3256
+ if (field && field.type === AITableFieldType.select) {
3257
+ if (cellValue && Array.isArray(cellValue) && cellValue.length) {
3258
+ const targetOptionIds = targetFieldOptions.map((option) => option._id);
3259
+ const originOptionsMap = helpers.keyBy(field.settings?.options || [], '_id');
3260
+ cellValue.forEach((id) => {
3261
+ if (targetOptionIds.includes(id)) {
3262
+ existOptionIds.push(id);
3456
3263
  }
3457
- break;
3458
- default:
3459
- break;
3264
+ else if (targetFieldOptions.some((option) => option.text === originOptionsMap[id]?.text)) {
3265
+ const option = targetFieldOptions.find((option) => option.text === originOptionsMap[id].text);
3266
+ existOptionIds.push(option._id);
3267
+ }
3268
+ else {
3269
+ const originOption = originOptionsMap[id];
3270
+ if (originOption) {
3271
+ const newOption = copyOption(originOption, targetFieldOptions, targetOptionStyle);
3272
+ newOptions.push(newOption);
3273
+ }
3274
+ }
3275
+ });
3460
3276
  }
3461
3277
  }
3462
- plainText = plainText.trim();
3463
- const hasMemberInfo = references && references.members && Object.keys(references.members).length;
3464
- if (plainText && hasMemberInfo) {
3465
- const memberNames = plainText.split(',').map((id) => id.trim());
3466
- const memberInfos = Object.values(references.members);
3467
- let validMemberIds = [];
3468
- memberNames.forEach((memberName) => {
3469
- const memberInfo = memberInfos.find((member) => member.display_name === memberName);
3470
- if (memberInfo) {
3471
- validMemberIds.push(memberInfo.uid);
3278
+ else {
3279
+ cellFullTexts.forEach((text) => {
3280
+ const option = targetFieldOptions.find((option) => option.text === text);
3281
+ if (option) {
3282
+ existOptionIds.push(option._id);
3283
+ }
3284
+ else {
3285
+ const originOption = { text };
3286
+ const newOption = copyOption(originOption, targetFieldOptions, targetOptionStyle);
3287
+ newOptions.push(newOption);
3472
3288
  }
3473
3289
  });
3474
- if (validMemberIds.length) {
3475
- return isMultiple ? validMemberIds : [validMemberIds[0]];
3476
- }
3477
3290
  }
3478
- return null;
3479
- }
3480
- function cellValueToSortValue$2(cellValue, field, references, sortKey = 'display_name') {
3481
- let values = [];
3482
- if (cellValue?.length && references) {
3483
- for (let index = 0; index < cellValue.length; index++) {
3484
- const userInfo = references?.members[cellValue[index]];
3485
- if (!userInfo) {
3486
- continue;
3291
+ const isMultiple = targetField.settings?.is_multiple;
3292
+ if (isMultiple) {
3293
+ return { existOptionIds, newOptions };
3294
+ }
3295
+ else {
3296
+ if (existOptionIds.length) {
3297
+ return { existOptionIds: [existOptionIds[0]], newOptions: [] };
3298
+ }
3299
+ else {
3300
+ if (newOptions.length) {
3301
+ return { existOptionIds: [], newOptions: [newOptions[0]] };
3487
3302
  }
3488
- const value = userInfo[sortKey];
3489
- if (value) {
3490
- values.push(value);
3303
+ else {
3304
+ return { existOptionIds: [], newOptions: [] };
3491
3305
  }
3492
3306
  }
3493
3307
  }
3494
- return values && values.length ? values.join(', ') : null;
3495
3308
  }
3496
-
3497
- class NumberField extends Field {
3498
- isValid(cellValue) {
3499
- return typeof cellValue === 'number' || cellValue === null;
3500
- }
3501
- isMeetFilter(condition, cellValue) {
3502
- switch (condition.operation) {
3503
- case AITableFilterOperation.empty:
3504
- return isEmpty(cellValue);
3505
- case AITableFilterOperation.exists:
3506
- return !isEmpty(cellValue);
3507
- case AITableFilterOperation.eq:
3508
- return !Number.isNaN(condition.value) && cellValue != null && cellValue !== '' && condition.value === cellValue;
3509
- case AITableFilterOperation.gte:
3510
- return cellValue != null && cellValue !== '' && cellValue >= condition.value;
3511
- case AITableFilterOperation.lte:
3512
- return cellValue != null && cellValue !== '' && cellValue <= condition.value;
3513
- case AITableFilterOperation.gt:
3514
- return cellValue != null && cellValue !== '' && cellValue > condition.value;
3515
- case AITableFilterOperation.lt:
3516
- return cellValue != null && cellValue !== '' && cellValue < condition.value;
3517
- case AITableFilterOperation.ne:
3518
- return cellValue == null || cellValue == '' || Number.isNaN(condition.value) || cellValue !== condition.value;
3519
- default:
3520
- return super.isMeetFilter(condition, cellValue);
3521
- }
3309
+ function copyOption(originOption, targetFieldOptions, targetOptionStyle) {
3310
+ let newOption = {
3311
+ _id: idCreator(),
3312
+ text: originOption.text
3313
+ };
3314
+ if (targetOptionStyle !== AITableSelectOptionStyle.text) {
3315
+ const originBgColor = originOption.bg_color;
3316
+ const existBgColors = targetFieldOptions.map((option) => option.bg_color);
3317
+ const defaultBgColor = DEFAULT_COLORS[10 + (targetFieldOptions?.length || 0)];
3318
+ newOption = {
3319
+ ...newOption,
3320
+ bg_color: originBgColor && !existBgColors.includes(originBgColor) ? originBgColor : defaultBgColor
3321
+ };
3522
3322
  }
3523
- compare(cellValue1, cellValue2) {
3524
- return compareNumber(cellValue1, cellValue2);
3323
+ return newOption;
3324
+ }
3325
+ function cellValueToSortValue$3(cellValue, field) {
3326
+ if (!cellValue) {
3327
+ return null;
3525
3328
  }
3526
- toFieldValue(plainText, targetField, originData) {
3527
- return toNumberFieldValue(plainText, targetField, originData);
3329
+ const texts = [];
3330
+ const optionsMap = helpers.keyBy(field.settings.options || [], '_id');
3331
+ if (cellValue && Array.isArray(cellValue) && cellValue.length) {
3332
+ cellValue.forEach((optionId) => {
3333
+ const option = optionsMap[optionId];
3334
+ if (option && option.text) {
3335
+ texts.push(option.text);
3336
+ }
3337
+ });
3528
3338
  }
3339
+ return texts && texts.length ? texts.join(',') : null;
3529
3340
  }
3530
- function toNumberFieldValue(plainText, targetField, originData) {
3531
- let text = plainText.trim();
3532
- if (originData) {
3533
- const { field, cellValue } = originData;
3534
- const fieldType = field.type;
3535
- switch (fieldType) {
3536
- case AITableFieldType.number:
3537
- case AITableFieldType.rate:
3538
- case AITableFieldType.progress:
3539
- return cellValue;
3540
- case AITableFieldType.select:
3541
- if (cellValue && Array.isArray(cellValue) && cellValue.length) {
3542
- const optionsMap = helpers.keyBy(field.settings.options || [], '_id');
3543
- text = optionsMap[cellValue[0]]?.text;
3544
- }
3545
- break;
3546
- default:
3547
- break;
3548
- }
3341
+
3342
+ const aiTableAttributePattern = new RegExp(`${aiTableFragmentAttribute}="(.+?)"`, 'm');
3343
+ const decodeClipboardJsonData = (encoded) => {
3344
+ const decoded = decodeURIComponent(window.atob(encoded));
3345
+ return JSON.parse(decoded);
3346
+ };
3347
+ function extractContentFromClipboardText(clipboardText) {
3348
+ const contents = clipboardText
3349
+ .split('\n')
3350
+ .map((row) => row.split('\t'))
3351
+ .filter((row) => row.length > 0 && row.some((cell) => cell.trim().length > 0));
3352
+ return contents;
3353
+ }
3354
+ function processTableCell(cellHtml) {
3355
+ const linkPattern = /<a[^>]*?href=["']([^"']+)["'][^>]*?>([^<]*?)<\/a>/i;
3356
+ const match = cellHtml.match(linkPattern);
3357
+ const link = match ? { href: match[1], text: match[2] } : null;
3358
+ const content = link ? cellHtml.replace(linkPattern, `[LINK:${link.text}](${link.href})`) : cellHtml;
3359
+ const cleanContent = content
3360
+ .replace(/<[^>]+>/g, '')
3361
+ .replace(/&nbsp;/g, ' ')
3362
+ .replace(/\s+/g, ' ')
3363
+ .trim();
3364
+ return link ? cleanContent.replace(`[LINK:${link.text}](${link.href})`, `<a href="${link.href}">${link.text}</a>`) : cleanContent;
3365
+ }
3366
+ function extractContentFromClipboardHtml(clipboardHtml) {
3367
+ const tablePattern = /<table[^>]*>([\s\S]*?)<\/table>/i;
3368
+ const trPattern = /<tr[^>]*>([\s\S]*?)<\/tr>/gi;
3369
+ const tdPattern = /<td[^>]*?>([\s\S]*?)<\/td>/gi;
3370
+ try {
3371
+ const tableMatch = clipboardHtml.match(tablePattern);
3372
+ const tableContent = tableMatch ? tableMatch[1] : clipboardHtml;
3373
+ const rows = tableContent.match(trPattern) || [];
3374
+ return rows
3375
+ .map((row) => {
3376
+ const contents = [];
3377
+ let tdMatch;
3378
+ while ((tdMatch = tdPattern.exec(row)) !== null) {
3379
+ contents.push(processTableCell(tdMatch[1]));
3380
+ }
3381
+ return contents;
3382
+ })
3383
+ .filter((row) => row.length > 0);
3549
3384
  }
3550
- if (text && !isEmpty(text) && !Number.isNaN(Number(text))) {
3551
- return Number(text);
3385
+ catch (error) {
3386
+ console.warn('Failed to extract content from HTML:', error);
3387
+ return [];
3552
3388
  }
3553
- return null;
3554
3389
  }
3555
-
3556
- class ProgressField extends Field {
3557
- isValid(cellValue) {
3558
- return typeof cellValue === 'number' || cellValue === null;
3390
+ function extractAITableContentFromClipboardHtml(clipboardHtml) {
3391
+ const aiTableFragment = clipboardHtml.match(aiTableAttributePattern);
3392
+ if (aiTableFragment && !!aiTableFragment.length) {
3393
+ return decodeClipboardJsonData(aiTableFragment[1]);
3559
3394
  }
3560
- isMeetFilter(condition, cellValue) {
3561
- switch (condition.operation) {
3562
- case AITableFilterOperation.empty:
3563
- return isEmpty(cellValue);
3564
- case AITableFilterOperation.exists:
3565
- return !isEmpty(cellValue);
3566
- case AITableFilterOperation.eq:
3567
- return !Number.isNaN(condition.value) && cellValue != null && cellValue !== '' && condition.value === cellValue;
3568
- case AITableFilterOperation.gte:
3569
- return cellValue != null && cellValue !== '' && cellValue >= condition.value;
3570
- case AITableFilterOperation.lte:
3571
- return cellValue != null && cellValue !== '' && cellValue <= condition.value;
3572
- case AITableFilterOperation.gt:
3573
- return cellValue != null && cellValue !== '' && cellValue > condition.value;
3574
- case AITableFilterOperation.lt:
3575
- return cellValue != null && cellValue !== '' && cellValue < condition.value;
3576
- case AITableFilterOperation.ne:
3577
- return cellValue == null || cellValue == '' || Number.isNaN(condition.value) || cellValue !== condition.value;
3578
- default:
3579
- return super.isMeetFilter(condition, cellValue);
3395
+ return null;
3396
+ }
3397
+ const readClipboardData = async () => {
3398
+ const clipboardData = await readFromClipboard();
3399
+ let clipboardContent = [];
3400
+ let aiTableContent = null;
3401
+ if (clipboardData) {
3402
+ const clipboardHtml = clipboardData.html;
3403
+ const clipboardText = clipboardData.text;
3404
+ if (clipboardHtml) {
3405
+ aiTableContent = extractAITableContentFromClipboardHtml(clipboardHtml);
3406
+ clipboardContent = extractContentFromClipboardHtml(clipboardHtml);
3407
+ }
3408
+ if (!clipboardContent.length && clipboardText) {
3409
+ clipboardContent = extractContentFromClipboardText(clipboardText);
3580
3410
  }
3581
3411
  }
3582
- compare(cellValue1, cellValue2) {
3583
- return compareNumber(cellValue1, cellValue2);
3412
+ return {
3413
+ clipboardContent,
3414
+ aiTableContent
3415
+ };
3416
+ };
3417
+ function getPasteValue(plainText, aiTableContent, recordIndex, fieldIndex, targetField, references) {
3418
+ let field = null;
3419
+ let record = null;
3420
+ if (aiTableContent) {
3421
+ const { fields, records } = aiTableContent;
3422
+ field = fields[fieldIndex];
3423
+ record = records[recordIndex];
3584
3424
  }
3585
- cellFullText(transformValue) {
3586
- let fullText = [];
3587
- if (!isEmpty(transformValue)) {
3588
- fullText.push(`${transformValue}%`);
3589
- }
3590
- return fullText;
3425
+ if (targetField.type === AITableFieldType.attachment || (field && field.type === AITableFieldType.attachment)) {
3426
+ return { value: null, newField: null };
3591
3427
  }
3592
- toFieldValue(plainText, targetField, originData) {
3593
- return toProgressFieldValue(plainText, targetField, originData);
3428
+ if (targetField.type !== AITableFieldType.link) {
3429
+ plainText = extractText(plainText);
3594
3430
  }
3595
- }
3596
- function toProgressFieldValue(plainText, targetField, originData) {
3597
- let value = plainText.trim();
3598
- if (originData) {
3599
- const { field, cellValue } = originData;
3600
- switch (field.type) {
3601
- case AITableFieldType.progress:
3602
- case AITableFieldType.rate:
3603
- case AITableFieldType.number:
3604
- value = cellValue;
3605
- break;
3606
- case AITableFieldType.select:
3607
- if (cellValue && Array.isArray(cellValue) && cellValue.length) {
3608
- const optionsMap = helpers.keyBy(field.settings.options || [], '_id');
3609
- value = optionsMap[cellValue[0]]?.text;
3431
+ let originData = field && record ? { field, cellValue: getFieldValue(record, field) } : null;
3432
+ if (targetField.type === AITableFieldType.select) {
3433
+ let { existOptionIds, newOptions } = processPastedValueForSelect(plainText, targetField, originData);
3434
+ let newField = null;
3435
+ let newOptionIds = [];
3436
+ if (newOptions.length) {
3437
+ newField = {
3438
+ ...targetField,
3439
+ settings: {
3440
+ ...targetField.settings,
3441
+ options: [...(targetField.settings?.options || []), ...newOptions]
3610
3442
  }
3611
- break;
3612
- default:
3613
- break;
3614
- }
3615
- }
3616
- const progressRegex = /^(?:100|[1-9]?\d(?:\.\d+)?)\s*%$/;
3617
- if (progressRegex.test(value)) {
3618
- value = parseFloat(value);
3619
- }
3620
- if (!isEmpty(value)) {
3621
- let progressValue = Number(value);
3622
- if (!Number.isNaN(progressValue)) {
3623
- progressValue = Math.round(progressValue);
3624
- if (progressValue >= 0 && progressValue <= 100) {
3625
- return progressValue;
3626
- }
3443
+ };
3444
+ newOptionIds = newOptions.map((option) => option._id).filter((id) => !!id);
3627
3445
  }
3446
+ const selectFieldValue = newOptionIds?.length ? [...existOptionIds, ...newOptionIds] : existOptionIds;
3447
+ return {
3448
+ value: selectFieldValue,
3449
+ newField
3450
+ };
3628
3451
  }
3629
- return null;
3452
+ return { value: FieldModelMap[targetField.type].toFieldValue(plainText, targetField, originData, references), newField: null };
3630
3453
  }
3631
-
3632
- class RateField extends Field {
3633
- isValid(cellValue) {
3634
- return typeof cellValue === 'number' || cellValue === null;
3635
- }
3636
- isMeetFilter(condition, cellValue) {
3637
- switch (condition.operation) {
3638
- case AITableFilterOperation.empty:
3639
- return isEmpty(cellValue);
3640
- case AITableFilterOperation.exists:
3641
- return !isEmpty(cellValue);
3642
- case AITableFilterOperation.in:
3643
- const isContain = condition.value.some((item) => String(item) === String(cellValue));
3644
- return !isEmpty(cellValue) && isContain;
3645
- case AITableFilterOperation.nin:
3646
- const noContain = condition.value.every((item) => String(item) !== String(cellValue));
3647
- return isEmpty(cellValue) || noContain;
3648
- default:
3649
- return super.isMeetFilter(condition, cellValue);
3650
- }
3651
- }
3652
- compare(cellValue1, cellValue2) {
3653
- return compareNumber(cellValue1, cellValue2);
3454
+ function appendRecord(aiTable, actions) {
3455
+ const allRecords = aiTable.records();
3456
+ const lastRecordId = allRecords.length > 0 ? allRecords[allRecords.length - 1]._id : '';
3457
+ actions.addRecord({
3458
+ originId: lastRecordId
3459
+ });
3460
+ }
3461
+ function appendField(aiTable, originField, actions) {
3462
+ const fields = aiTable.gridData().fields;
3463
+ const lastFieldId = fields.length > 0 ? fields[fields.length - 1]._id : '';
3464
+ let defaultFieldValue;
3465
+ if (originField) {
3466
+ const fieldOptions = getFieldOptions(aiTable);
3467
+ defaultFieldValue = {
3468
+ ...originField,
3469
+ name: createDefaultFieldName(aiTable, fieldOptions.find((item) => item.type === originField.type)),
3470
+ _id: idCreator()
3471
+ };
3654
3472
  }
3655
- toFieldValue(plainText, targetField, originData) {
3656
- return toRateFieldValue(plainText, targetField, originData);
3473
+ else {
3474
+ defaultFieldValue = createDefaultField(aiTable, AITableFieldType.text);
3657
3475
  }
3476
+ actions.addField({
3477
+ originId: lastFieldId,
3478
+ defaultValue: defaultFieldValue
3479
+ });
3658
3480
  }
3659
- function toRateFieldValue(plainText, targetField, originData) {
3660
- let value = plainText.trim();
3661
- if (originData) {
3662
- const { field, cellValue } = originData;
3663
- switch (field.type) {
3664
- case AITableFieldType.rate:
3665
- case AITableFieldType.number:
3666
- case AITableFieldType.progress:
3667
- value = cellValue;
3668
- break;
3669
- case AITableFieldType.select:
3670
- if (cellValue && Array.isArray(cellValue) && cellValue.length) {
3671
- const optionsMap = helpers.keyBy(field.settings.options || [], '_id');
3672
- value = optionsMap[cellValue[0]]?.text;
3673
- }
3674
- break;
3675
- default:
3676
- break;
3677
- }
3481
+ const writeToAITable = async (aiTable, actions) => {
3482
+ const selectedCells = Array.from(aiTable.selection().selectedCells);
3483
+ if (!selectedCells.length) {
3484
+ return;
3678
3485
  }
3679
- if (!isEmpty(value)) {
3680
- const rateValue = Number(value);
3681
- if (!Number.isNaN(rateValue) && rateValue > 0 && rateValue < 5) {
3682
- return Math.round(rateValue);
3683
- }
3684
- return 5;
3486
+ const { clipboardContent, aiTableContent } = await readClipboardData();
3487
+ if (!clipboardContent.length) {
3488
+ return;
3685
3489
  }
3686
- return null;
3687
- }
3490
+ const [firstCell] = selectedCells;
3491
+ const [startRecordId, startFieldId] = firstCell.split(':');
3492
+ const startRowIndex = aiTable.context.visibleRowsIndexMap().get(startRecordId) ?? 0;
3493
+ const startColIndex = aiTable.context.visibleColumnsIndexMap().get(startFieldId) ?? 0;
3494
+ const references = aiTable.context.references();
3495
+ let isPasteSuccess = false;
3496
+ clipboardContent.forEach((row, i) => {
3497
+ const targetRowIndex = startRowIndex + i;
3498
+ if (targetRowIndex >= aiTable.context.linearRows().length - 1) {
3499
+ appendRecord(aiTable, actions);
3500
+ }
3501
+ row.forEach((plainText, j) => {
3502
+ const targetColIndex = startColIndex + j;
3503
+ if (targetColIndex >= AITable.getVisibleFields(aiTable).length) {
3504
+ const originField = aiTableContent?.fields[j] || null;
3505
+ appendField(aiTable, originField, actions);
3506
+ }
3507
+ const targetRecord = aiTable.context.linearRows()[targetRowIndex];
3508
+ const targetField = AITable.getVisibleFields(aiTable)[targetColIndex];
3509
+ const recordIndex = i;
3510
+ const fieldIndex = j;
3511
+ const { value, newField } = getPasteValue(plainText, aiTableContent, recordIndex, fieldIndex, targetField, references);
3512
+ if (newField) {
3513
+ actions.setField(newField);
3514
+ }
3515
+ if (value !== null) {
3516
+ try {
3517
+ actions.updateFieldValue({
3518
+ value,
3519
+ path: [targetRecord._id, targetField._id]
3520
+ });
3521
+ isPasteSuccess = true;
3522
+ }
3523
+ catch (error) { }
3524
+ }
3525
+ });
3526
+ });
3527
+ return isPasteSuccess;
3528
+ };
3688
3529
 
3689
- class RichTextField extends Field {
3530
+ class LinkField extends Field {
3690
3531
  isValid(cellValue) {
3691
- return Array.isArray(cellValue) || cellValue === null;
3532
+ return (cellValue && typeof cellValue === 'object' && 'url' in cellValue && 'text' in cellValue) || cellValue === null;
3692
3533
  }
3693
- isMeetFilter(condition, cellValue, options) {
3694
- const textValue = transformCellValue(options.aiTable, options.field, cellValue || []);
3534
+ isMeetFilter(condition, cellValue) {
3535
+ const cellTextValue = cellValue?.text;
3695
3536
  switch (condition.operation) {
3696
3537
  case AITableFilterOperation.empty:
3697
- return isEmpty(textValue);
3538
+ return isEmpty(cellTextValue);
3698
3539
  case AITableFilterOperation.exists:
3699
- return !isEmpty(textValue);
3540
+ return !isEmpty(cellTextValue);
3700
3541
  case AITableFilterOperation.contain:
3701
- return !isEmpty(textValue) && stringInclude(textValue, condition.value);
3542
+ return !isEmpty(cellTextValue) && stringInclude(cellTextValue, condition.value);
3702
3543
  default:
3703
- return super.isMeetFilter(condition, textValue);
3544
+ return super.isMeetFilter(condition, cellTextValue);
3704
3545
  }
3705
3546
  }
3706
- compare(cellValue1, cellValue2, references, sortKey, options) {
3707
- const value1 = transformCellValue(options.aiTable, options.field, cellValue1 || []);
3708
- const value2 = transformCellValue(options.aiTable, options.field, cellValue2 || []);
3709
- return compareString(value1, value2);
3547
+ compare(cellValue1, cellValue2) {
3548
+ return compareString(cellValueToSortValue$2(cellValue1), cellValueToSortValue$2(cellValue2));
3549
+ }
3550
+ cellFullText(transformValue) {
3551
+ let texts = [];
3552
+ if (!isEmpty(transformValue?.text)) {
3553
+ texts.push(transformValue.text);
3554
+ }
3555
+ return texts;
3710
3556
  }
3711
3557
  toFieldValue(plainText, targetField, originData) {
3712
- return toRichTextFieldValue(plainText, targetField, originData);
3558
+ return toLinkFieldValue(plainText, targetField, originData);
3713
3559
  }
3714
3560
  }
3715
- function toRichTextFieldValue(plainText, targetField, originData) {
3561
+ function toLinkFieldValue(plainText, targetField, originData) {
3716
3562
  if (originData) {
3717
3563
  const { field, cellValue } = originData;
3718
- if (field.type === AITableFieldType.richText) {
3564
+ if (field.type === AITableFieldType.link) {
3719
3565
  return cellValue;
3720
3566
  }
3721
3567
  }
3568
+ else {
3569
+ const url = extractLinkUrl(plainText);
3570
+ const text = extractText(plainText);
3571
+ if (url && text) {
3572
+ return {
3573
+ url,
3574
+ text
3575
+ };
3576
+ }
3577
+ }
3722
3578
  return null;
3723
3579
  }
3580
+ function cellValueToSortValue$2(cellValue) {
3581
+ return (cellValue && cellValue.text && cellValue.text.trim()) || null;
3582
+ }
3724
3583
 
3725
- class SelectField extends Field {
3584
+ class MemberField extends Field {
3726
3585
  isValid(cellValue) {
3727
3586
  return Array.isArray(cellValue) || cellValue === null;
3728
3587
  }
@@ -3741,126 +3600,143 @@ class SelectField extends Field {
3741
3600
  }
3742
3601
  }
3743
3602
  compare(cellValue1, cellValue2, references, sortKey, options) {
3744
- const value1 = cellValueToSortValue$1(cellValue1, options.field);
3745
- const value2 = cellValueToSortValue$1(cellValue2, options.field);
3603
+ const value1 = cellValueToSortValue$1(cellValue1, options.field, references, sortKey);
3604
+ const value2 = cellValueToSortValue$1(cellValue2, options.field, references, sortKey);
3746
3605
  return compareString(value1, value2);
3747
3606
  }
3748
- cellFullText(transformValue, field) {
3607
+ cellFullText(transformValue, field, references) {
3749
3608
  let fullText = [];
3750
- const optionsMap = helpers.keyBy(field.settings.options || [], '_id');
3751
- if (transformValue && Array.isArray(transformValue) && transformValue.length) {
3752
- transformValue.forEach((optionId) => {
3753
- const option = optionsMap[optionId];
3754
- if (option && option.text) {
3755
- fullText.push(option.text);
3609
+ if (transformValue?.length && references) {
3610
+ for (let index = 0; index < transformValue.length; index++) {
3611
+ const userInfo = references?.members[transformValue[index]];
3612
+ if (!userInfo) {
3613
+ continue;
3756
3614
  }
3757
- });
3615
+ if (userInfo.display_name) {
3616
+ fullText.push(userInfo.display_name);
3617
+ }
3618
+ }
3758
3619
  }
3759
3620
  return fullText;
3760
3621
  }
3761
- toFieldValue(plainText, targetField, originData) {
3762
- return toSelectFieldValue(plainText, targetField, originData);
3622
+ toFieldValue(plainText, targetField, originData, references) {
3623
+ return toMemberFieldValue(plainText, targetField, originData, references);
3763
3624
  }
3764
3625
  }
3765
- function toSelectFieldValue(plainText, targetField, originData) {
3766
- return null;
3767
- }
3768
- function processPastedValueForSelect(plainText, targetField, originData) {
3769
- const targetFieldOptions = targetField.settings?.options || [];
3770
- const targetOptionStyle = targetField.settings?.option_style || AITableSelectOptionStyle.text;
3771
- let existOptionIds = [];
3772
- let newOptions = [];
3773
- let cellFullTexts = plainText.split(',').map((text) => text.trim());
3774
- const { field, cellValue } = originData || {};
3775
- if (field && field.type === AITableFieldType.select) {
3776
- if (cellValue && Array.isArray(cellValue) && cellValue.length) {
3777
- const targetOptionIds = targetFieldOptions.map((option) => option._id);
3778
- const originOptionsMap = helpers.keyBy(field.settings?.options || [], '_id');
3779
- cellValue.forEach((id) => {
3780
- if (targetOptionIds.includes(id)) {
3781
- existOptionIds.push(id);
3782
- }
3783
- else if (targetFieldOptions.some((option) => option.text === originOptionsMap[id]?.text)) {
3784
- const option = targetFieldOptions.find((option) => option.text === originOptionsMap[id].text);
3785
- existOptionIds.push(option._id);
3786
- }
3787
- else {
3788
- const originOption = originOptionsMap[id];
3789
- if (originOption) {
3790
- const newOption = copyOption(originOption, targetFieldOptions, targetOptionStyle);
3791
- newOptions.push(newOption);
3792
- }
3626
+ function toMemberFieldValue(plainText, targetField, originData, references) {
3627
+ if (targetField.type == AITableFieldType.createdBy || targetField.type == AITableFieldType.updatedBy) {
3628
+ return null;
3629
+ }
3630
+ const isMultiple = targetField.settings?.is_multiple;
3631
+ if (originData) {
3632
+ const { field, cellValue } = originData;
3633
+ switch (field.type) {
3634
+ case AITableFieldType.member:
3635
+ if (Array.isArray(cellValue) && cellValue.length) {
3636
+ return isMultiple ? cellValue : [cellValue[0]];
3793
3637
  }
3794
- });
3638
+ break;
3639
+ default:
3640
+ break;
3795
3641
  }
3796
3642
  }
3797
- else {
3798
- cellFullTexts.forEach((text) => {
3799
- const option = targetFieldOptions.find((option) => option.text === text);
3800
- if (option) {
3801
- existOptionIds.push(option._id);
3802
- }
3803
- else {
3804
- const originOption = { text };
3805
- const newOption = copyOption(originOption, targetFieldOptions, targetOptionStyle);
3806
- newOptions.push(newOption);
3643
+ plainText = plainText.trim();
3644
+ const hasMemberInfo = references && references.members && Object.keys(references.members).length;
3645
+ if (plainText && hasMemberInfo) {
3646
+ const memberNames = plainText.split(',').map((id) => id.trim());
3647
+ const memberInfos = Object.values(references.members);
3648
+ let validMemberIds = [];
3649
+ memberNames.forEach((memberName) => {
3650
+ const memberInfo = memberInfos.find((member) => member.display_name === memberName);
3651
+ if (memberInfo) {
3652
+ validMemberIds.push(memberInfo.uid);
3807
3653
  }
3808
3654
  });
3809
- }
3810
- const isMultiple = targetField.settings?.is_multiple;
3811
- if (isMultiple) {
3812
- return { existOptionIds, newOptions };
3813
- }
3814
- else {
3815
- if (existOptionIds.length) {
3816
- return { existOptionIds: [existOptionIds[0]], newOptions: [] };
3655
+ if (validMemberIds.length) {
3656
+ return isMultiple ? validMemberIds : [validMemberIds[0]];
3817
3657
  }
3818
- else {
3819
- if (newOptions.length) {
3820
- return { existOptionIds: [], newOptions: [newOptions[0]] };
3658
+ }
3659
+ return null;
3660
+ }
3661
+ function cellValueToSortValue$1(cellValue, field, references, sortKey = 'display_name') {
3662
+ let values = [];
3663
+ if (cellValue?.length && references) {
3664
+ for (let index = 0; index < cellValue.length; index++) {
3665
+ const userInfo = references?.members[cellValue[index]];
3666
+ if (!userInfo) {
3667
+ continue;
3821
3668
  }
3822
- else {
3823
- return { existOptionIds: [], newOptions: [] };
3669
+ const value = userInfo[sortKey];
3670
+ if (value) {
3671
+ values.push(value);
3824
3672
  }
3825
3673
  }
3826
3674
  }
3675
+ return values && values.length ? values.join(', ') : null;
3827
3676
  }
3828
- function copyOption(originOption, targetFieldOptions, targetOptionStyle) {
3829
- let newOption = {
3830
- _id: idCreator(),
3831
- text: originOption.text
3832
- };
3833
- if (targetOptionStyle !== AITableSelectOptionStyle.text) {
3834
- const originBgColor = originOption.bg_color;
3835
- const existBgColors = targetFieldOptions.map((option) => option.bg_color);
3836
- const defaultBgColor = DEFAULT_COLORS[10 + (targetFieldOptions?.length || 0)];
3837
- newOption = {
3838
- ...newOption,
3839
- bg_color: originBgColor && !existBgColors.includes(originBgColor) ? originBgColor : defaultBgColor
3840
- };
3677
+
3678
+ class NumberField extends Field {
3679
+ isValid(cellValue) {
3680
+ return typeof cellValue === 'number' || cellValue === null;
3681
+ }
3682
+ isMeetFilter(condition, cellValue) {
3683
+ switch (condition.operation) {
3684
+ case AITableFilterOperation.empty:
3685
+ return isEmpty(cellValue);
3686
+ case AITableFilterOperation.exists:
3687
+ return !isEmpty(cellValue);
3688
+ case AITableFilterOperation.eq:
3689
+ return !Number.isNaN(condition.value) && cellValue != null && cellValue !== '' && condition.value === cellValue;
3690
+ case AITableFilterOperation.gte:
3691
+ return cellValue != null && cellValue !== '' && cellValue >= condition.value;
3692
+ case AITableFilterOperation.lte:
3693
+ return cellValue != null && cellValue !== '' && cellValue <= condition.value;
3694
+ case AITableFilterOperation.gt:
3695
+ return cellValue != null && cellValue !== '' && cellValue > condition.value;
3696
+ case AITableFilterOperation.lt:
3697
+ return cellValue != null && cellValue !== '' && cellValue < condition.value;
3698
+ case AITableFilterOperation.ne:
3699
+ return cellValue == null || cellValue == '' || Number.isNaN(condition.value) || cellValue !== condition.value;
3700
+ default:
3701
+ return super.isMeetFilter(condition, cellValue);
3702
+ }
3703
+ }
3704
+ compare(cellValue1, cellValue2) {
3705
+ return compareNumber(cellValue1, cellValue2);
3706
+ }
3707
+ toFieldValue(plainText, targetField, originData) {
3708
+ return toNumberFieldValue(plainText, targetField, originData);
3841
3709
  }
3842
- return newOption;
3843
3710
  }
3844
- function cellValueToSortValue$1(cellValue, field) {
3845
- if (!cellValue) {
3846
- return null;
3711
+ function toNumberFieldValue(plainText, targetField, originData) {
3712
+ let text = plainText.trim();
3713
+ if (originData) {
3714
+ const { field, cellValue } = originData;
3715
+ const fieldType = field.type;
3716
+ switch (fieldType) {
3717
+ case AITableFieldType.number:
3718
+ case AITableFieldType.rate:
3719
+ case AITableFieldType.progress:
3720
+ return cellValue;
3721
+ case AITableFieldType.select:
3722
+ if (cellValue && Array.isArray(cellValue) && cellValue.length) {
3723
+ const optionsMap = helpers.keyBy(field.settings.options || [], '_id');
3724
+ text = optionsMap[cellValue[0]]?.text;
3725
+ }
3726
+ break;
3727
+ default:
3728
+ break;
3729
+ }
3847
3730
  }
3848
- const texts = [];
3849
- const optionsMap = helpers.keyBy(field.settings.options || [], '_id');
3850
- if (cellValue && Array.isArray(cellValue) && cellValue.length) {
3851
- cellValue.forEach((optionId) => {
3852
- const option = optionsMap[optionId];
3853
- if (option && option.text) {
3854
- texts.push(option.text);
3855
- }
3856
- });
3731
+ if (text && !isEmpty(text) && !Number.isNaN(Number(text))) {
3732
+ return Number(text);
3857
3733
  }
3858
- return texts && texts.length ? texts.join(',') : null;
3734
+ return null;
3859
3735
  }
3860
3736
 
3861
- class TextField extends Field {
3737
+ class ProgressField extends Field {
3862
3738
  isValid(cellValue) {
3863
- return typeof cellValue === 'string' || cellValue === null;
3739
+ return typeof cellValue === 'number' || cellValue === null;
3864
3740
  }
3865
3741
  isMeetFilter(condition, cellValue) {
3866
3742
  switch (condition.operation) {
@@ -3868,320 +3744,448 @@ class TextField extends Field {
3868
3744
  return isEmpty(cellValue);
3869
3745
  case AITableFilterOperation.exists:
3870
3746
  return !isEmpty(cellValue);
3871
- case AITableFilterOperation.contain:
3872
- return !isEmpty(cellValue) && stringInclude(cellValue, condition.value);
3747
+ case AITableFilterOperation.eq:
3748
+ return !Number.isNaN(condition.value) && cellValue != null && cellValue !== '' && condition.value === cellValue;
3749
+ case AITableFilterOperation.gte:
3750
+ return cellValue != null && cellValue !== '' && cellValue >= condition.value;
3751
+ case AITableFilterOperation.lte:
3752
+ return cellValue != null && cellValue !== '' && cellValue <= condition.value;
3753
+ case AITableFilterOperation.gt:
3754
+ return cellValue != null && cellValue !== '' && cellValue > condition.value;
3755
+ case AITableFilterOperation.lt:
3756
+ return cellValue != null && cellValue !== '' && cellValue < condition.value;
3757
+ case AITableFilterOperation.ne:
3758
+ return cellValue == null || cellValue == '' || Number.isNaN(condition.value) || cellValue !== condition.value;
3873
3759
  default:
3874
3760
  return super.isMeetFilter(condition, cellValue);
3875
3761
  }
3876
3762
  }
3877
3763
  compare(cellValue1, cellValue2) {
3878
- const value1 = cellValueToSortValue(cellValue1);
3879
- const value2 = cellValueToSortValue(cellValue2);
3880
- return compareString(value1, value2);
3764
+ return compareNumber(cellValue1, cellValue2);
3881
3765
  }
3882
- toFieldValue(plainText) {
3883
- return toTextFieldValue(plainText);
3766
+ cellFullText(transformValue) {
3767
+ let fullText = [];
3768
+ if (!isEmpty(transformValue)) {
3769
+ fullText.push(`${transformValue}%`);
3770
+ }
3771
+ return fullText;
3772
+ }
3773
+ toFieldValue(plainText, targetField, originData) {
3774
+ return toProgressFieldValue(plainText, targetField, originData);
3884
3775
  }
3885
3776
  }
3886
- function toTextFieldValue(plainText) {
3887
- return plainText.trim();
3888
- }
3889
- function cellValueToSortValue(cellValue) {
3890
- return (cellValue && cellValue.trim()) || null;
3777
+ function toProgressFieldValue(plainText, targetField, originData) {
3778
+ let value = plainText.trim();
3779
+ if (originData) {
3780
+ const { field, cellValue } = originData;
3781
+ switch (field.type) {
3782
+ case AITableFieldType.progress:
3783
+ case AITableFieldType.rate:
3784
+ case AITableFieldType.number:
3785
+ value = cellValue;
3786
+ break;
3787
+ case AITableFieldType.select:
3788
+ if (cellValue && Array.isArray(cellValue) && cellValue.length) {
3789
+ const optionsMap = helpers.keyBy(field.settings.options || [], '_id');
3790
+ value = optionsMap[cellValue[0]]?.text;
3791
+ }
3792
+ break;
3793
+ default:
3794
+ break;
3795
+ }
3796
+ }
3797
+ const progressRegex = /^(?:100|[1-9]?\d(?:\.\d+)?)\s*%$/;
3798
+ if (progressRegex.test(value)) {
3799
+ value = parseFloat(value);
3800
+ }
3801
+ if (!isEmpty(value)) {
3802
+ let progressValue = Number(value);
3803
+ if (!Number.isNaN(progressValue)) {
3804
+ progressValue = Math.round(progressValue);
3805
+ if (progressValue >= 0 && progressValue <= 100) {
3806
+ return progressValue;
3807
+ }
3808
+ }
3809
+ }
3810
+ return null;
3891
3811
  }
3892
3812
 
3893
- const FieldModelMap = {
3894
- [AITableFieldType.text]: new TextField(),
3895
- [AITableFieldType.richText]: new RichTextField(),
3896
- [AITableFieldType.select]: new SelectField(),
3897
- [AITableFieldType.date]: new DateField(),
3898
- [AITableFieldType.createdAt]: new DateField(),
3899
- [AITableFieldType.updatedAt]: new DateField(),
3900
- [AITableFieldType.number]: new NumberField(),
3901
- [AITableFieldType.rate]: new RateField(),
3902
- [AITableFieldType.link]: new LinkField(),
3903
- [AITableFieldType.member]: new MemberField(),
3904
- [AITableFieldType.progress]: new ProgressField(),
3905
- [AITableFieldType.createdBy]: new MemberField(),
3906
- [AITableFieldType.updatedBy]: new MemberField(),
3907
- [AITableFieldType.attachment]: new AttachmentField()
3908
- };
3909
-
3910
- const aiTableFragmentAttribute = 'ai-table-fragment';
3911
- const buildClipboardData = (aiTable) => {
3912
- const copiedCells = Array.from(aiTable.selection().selectedCells);
3913
- if (!copiedCells.length) {
3914
- return null;
3813
+ class RateField extends Field {
3814
+ isValid(cellValue) {
3815
+ return typeof cellValue === 'number' || cellValue === null;
3816
+ }
3817
+ isMeetFilter(condition, cellValue) {
3818
+ switch (condition.operation) {
3819
+ case AITableFilterOperation.empty:
3820
+ return isEmpty(cellValue);
3821
+ case AITableFilterOperation.exists:
3822
+ return !isEmpty(cellValue);
3823
+ case AITableFilterOperation.in:
3824
+ const isContain = condition.value.some((item) => String(item) === String(cellValue));
3825
+ return !isEmpty(cellValue) && isContain;
3826
+ case AITableFilterOperation.nin:
3827
+ const noContain = condition.value.every((item) => String(item) !== String(cellValue));
3828
+ return isEmpty(cellValue) || noContain;
3829
+ default:
3830
+ return super.isMeetFilter(condition, cellValue);
3831
+ }
3832
+ }
3833
+ compare(cellValue1, cellValue2) {
3834
+ return compareNumber(cellValue1, cellValue2);
3835
+ }
3836
+ toFieldValue(plainText, targetField, originData) {
3837
+ return toRateFieldValue(plainText, targetField, originData);
3915
3838
  }
3916
- let copiedFieldIds = new Set();
3917
- let copiedRecordIds = new Set();
3918
- copiedCells.forEach((cellPath) => {
3919
- const [recordId, fieldId] = cellPath.split(':');
3920
- copiedFieldIds.add(fieldId);
3921
- copiedRecordIds.add(recordId);
3922
- });
3923
- const fieldIds = Array.from(copiedFieldIds);
3924
- const recordIds = Array.from(copiedRecordIds);
3925
- const aiTableContent = buildAITableContent(aiTable, fieldIds, recordIds);
3926
- const clipboardContent = buildClipboardContent(aiTable, fieldIds, recordIds);
3927
- return mergeClipboardContent(clipboardContent, aiTableContent);
3928
- };
3929
- const encodeAITableContent = (aiTableContent) => {
3930
- const stringifiedData = JSON.stringify(aiTableContent);
3931
- return window.btoa(encodeURIComponent(stringifiedData));
3932
- };
3933
- function mergeClipboardContent(clipboardContent, aiTableContent) {
3934
- const encodedAITableContent = encodeAITableContent(aiTableContent);
3935
- const formattedContent = {
3936
- text: clipboardContent.map((row) => row.map((column) => column.text).join('\t')).join('\n'),
3937
- html: `<table ${aiTableFragmentAttribute}="${encodedAITableContent}">${clipboardContent.map((row) => `<tr>${row.map((column) => `<td>${column.html}</td>`).join('')}</tr>`).join('')}</table>`
3938
- };
3939
- return formattedContent;
3940
- }
3941
- function buildAITableContent(aiTable, fieldIds, recordIds) {
3942
- const fields = fieldIds.map((fieldId) => {
3943
- return aiTable.fieldsMap()[fieldId];
3944
- });
3945
- const records = recordIds.map((recordId) => {
3946
- const record = aiTable.recordsMap()[recordId];
3947
- let newRecord = {
3948
- _id: record._id,
3949
- values: {}
3950
- };
3951
- fieldIds.forEach((fieldId) => {
3952
- const field = aiTable.fieldsMap()[fieldId];
3953
- if (isSystemField(field)) {
3954
- const fieldType = field.type;
3955
- newRecord = {
3956
- ...newRecord,
3957
- [fieldType]: getSystemFieldValue(record, fieldType)
3958
- };
3959
- }
3960
- else {
3961
- newRecord.values = {
3962
- ...newRecord.values,
3963
- [fieldId]: getFieldValue(record, field)
3964
- };
3965
- }
3966
- });
3967
- return newRecord;
3968
- });
3969
- return {
3970
- fields,
3971
- records
3972
- };
3973
3839
  }
3974
- function buildClipboardContent(aiTable, fieldIds, recordIds) {
3975
- const clipboardContent = [];
3976
- const references = aiTable.context.references();
3977
- recordIds.forEach((recordId) => {
3978
- const record = aiTable.recordsMap()[recordId];
3979
- const row = [];
3980
- fieldIds.forEach((fieldId) => {
3981
- const field = aiTable.fieldsMap()[fieldId];
3982
- const cellValue = getFieldValue(record, field);
3983
- const transformValue = transformCellValue(aiTable, field, cellValue);
3984
- const cellTexts = FieldModelMap[field.type].cellFullText(transformValue, field, references);
3985
- let cellContent = {
3986
- text: cellTexts.join(','),
3987
- html: cellTexts.join(',')
3988
- };
3989
- if (field.type === AITableFieldType.link && cellValue && cellValue.url) {
3990
- cellContent.html = `<a href="${cellValue.url}" target="_blank">${cellValue.text}</a>`;
3991
- }
3992
- row.push(cellContent);
3993
- });
3994
- clipboardContent.push(row);
3995
- });
3996
- return clipboardContent;
3840
+ function toRateFieldValue(plainText, targetField, originData) {
3841
+ let value = plainText.trim();
3842
+ if (originData) {
3843
+ const { field, cellValue } = originData;
3844
+ switch (field.type) {
3845
+ case AITableFieldType.rate:
3846
+ case AITableFieldType.number:
3847
+ case AITableFieldType.progress:
3848
+ value = cellValue;
3849
+ break;
3850
+ case AITableFieldType.select:
3851
+ if (cellValue && Array.isArray(cellValue) && cellValue.length) {
3852
+ const optionsMap = helpers.keyBy(field.settings.options || [], '_id');
3853
+ value = optionsMap[cellValue[0]]?.text;
3854
+ }
3855
+ break;
3856
+ default:
3857
+ break;
3858
+ }
3859
+ }
3860
+ if (!isEmpty(value)) {
3861
+ const rateValue = Number(value);
3862
+ if (!Number.isNaN(rateValue) && rateValue > 0 && rateValue < 5) {
3863
+ return Math.round(rateValue);
3864
+ }
3865
+ return 5;
3866
+ }
3867
+ return null;
3997
3868
  }
3998
3869
 
3999
- const aiTableAttributePattern = new RegExp(`${aiTableFragmentAttribute}="(.+?)"`, 'm');
4000
- const decodeClipboardJsonData = (encoded) => {
4001
- const decoded = decodeURIComponent(window.atob(encoded));
4002
- return JSON.parse(decoded);
4003
- };
4004
- function extractContentFromClipboardText(clipboardText) {
4005
- const contents = clipboardText
4006
- .split('\n')
4007
- .map((row) => row.split('\t'))
4008
- .filter((row) => row.length > 0 && row.some((cell) => cell.trim().length > 0));
4009
- return contents;
4010
- }
4011
- function processTableCell(cellHtml) {
4012
- const linkPattern = /<a[^>]*?href=["']([^"']+)["'][^>]*?>([^<]*?)<\/a>/i;
4013
- const match = cellHtml.match(linkPattern);
4014
- const link = match ? { href: match[1], text: match[2] } : null;
4015
- const content = link ? cellHtml.replace(linkPattern, `[LINK:${link.text}](${link.href})`) : cellHtml;
4016
- const cleanContent = content
4017
- .replace(/<[^>]+>/g, '')
4018
- .replace(/&nbsp;/g, ' ')
4019
- .replace(/\s+/g, ' ')
4020
- .trim();
4021
- return link ? cleanContent.replace(`[LINK:${link.text}](${link.href})`, `<a href="${link.href}">${link.text}</a>`) : cleanContent;
4022
- }
4023
- function extractContentFromClipboardHtml(clipboardHtml) {
4024
- const tablePattern = /<table[^>]*>([\s\S]*?)<\/table>/i;
4025
- const trPattern = /<tr[^>]*>([\s\S]*?)<\/tr>/gi;
4026
- const tdPattern = /<td[^>]*?>([\s\S]*?)<\/td>/gi;
4027
- try {
4028
- const tableMatch = clipboardHtml.match(tablePattern);
4029
- const tableContent = tableMatch ? tableMatch[1] : clipboardHtml;
4030
- const rows = tableContent.match(trPattern) || [];
4031
- return rows
4032
- .map((row) => {
4033
- const contents = [];
4034
- let tdMatch;
4035
- while ((tdMatch = tdPattern.exec(row)) !== null) {
4036
- contents.push(processTableCell(tdMatch[1]));
4037
- }
4038
- return contents;
4039
- })
4040
- .filter((row) => row.length > 0);
3870
+ class RichTextField extends Field {
3871
+ isValid(cellValue) {
3872
+ return Array.isArray(cellValue) || cellValue === null;
4041
3873
  }
4042
- catch (error) {
4043
- console.warn('Failed to extract content from HTML:', error);
4044
- return [];
3874
+ isMeetFilter(condition, cellValue, options) {
3875
+ const textValue = transformCellValue(options.aiTable, options.field, cellValue || []);
3876
+ switch (condition.operation) {
3877
+ case AITableFilterOperation.empty:
3878
+ return isEmpty(textValue);
3879
+ case AITableFilterOperation.exists:
3880
+ return !isEmpty(textValue);
3881
+ case AITableFilterOperation.contain:
3882
+ return !isEmpty(textValue) && stringInclude(textValue, condition.value);
3883
+ default:
3884
+ return super.isMeetFilter(condition, textValue);
3885
+ }
3886
+ }
3887
+ compare(cellValue1, cellValue2, references, sortKey, options) {
3888
+ const value1 = transformCellValue(options.aiTable, options.field, cellValue1 || []);
3889
+ const value2 = transformCellValue(options.aiTable, options.field, cellValue2 || []);
3890
+ return compareString(value1, value2);
3891
+ }
3892
+ toFieldValue(plainText, targetField, originData) {
3893
+ return toRichTextFieldValue(plainText, targetField, originData);
4045
3894
  }
4046
3895
  }
4047
- function extractAITableContentFromClipboardHtml(clipboardHtml) {
4048
- const aiTableFragment = clipboardHtml.match(aiTableAttributePattern);
4049
- if (aiTableFragment && !!aiTableFragment.length) {
4050
- return decodeClipboardJsonData(aiTableFragment[1]);
3896
+ function toRichTextFieldValue(plainText, targetField, originData) {
3897
+ if (originData) {
3898
+ const { field, cellValue } = originData;
3899
+ if (field.type === AITableFieldType.richText) {
3900
+ return cellValue;
3901
+ }
4051
3902
  }
4052
3903
  return null;
4053
3904
  }
4054
- const readClipboardData = async () => {
4055
- const clipboardData = await readFromClipboard();
4056
- let clipboardContent = [];
4057
- let aiTableContent = null;
4058
- if (clipboardData) {
4059
- const clipboardHtml = clipboardData.html;
4060
- const clipboardText = clipboardData.text;
4061
- if (clipboardHtml) {
4062
- aiTableContent = extractAITableContentFromClipboardHtml(clipboardHtml);
4063
- clipboardContent = extractContentFromClipboardHtml(clipboardHtml);
4064
- }
4065
- if (!clipboardContent.length && clipboardText) {
4066
- clipboardContent = extractContentFromClipboardText(clipboardText);
3905
+
3906
+ class TextField extends Field {
3907
+ isValid(cellValue) {
3908
+ return typeof cellValue === 'string' || cellValue === null;
3909
+ }
3910
+ isMeetFilter(condition, cellValue) {
3911
+ switch (condition.operation) {
3912
+ case AITableFilterOperation.empty:
3913
+ return isEmpty(cellValue);
3914
+ case AITableFilterOperation.exists:
3915
+ return !isEmpty(cellValue);
3916
+ case AITableFilterOperation.contain:
3917
+ return !isEmpty(cellValue) && stringInclude(cellValue, condition.value);
3918
+ default:
3919
+ return super.isMeetFilter(condition, cellValue);
4067
3920
  }
4068
3921
  }
4069
- return {
4070
- clipboardContent,
4071
- aiTableContent
4072
- };
3922
+ compare(cellValue1, cellValue2) {
3923
+ const value1 = cellValueToSortValue(cellValue1);
3924
+ const value2 = cellValueToSortValue(cellValue2);
3925
+ return compareString(value1, value2);
3926
+ }
3927
+ toFieldValue(plainText) {
3928
+ return toTextFieldValue(plainText);
3929
+ }
3930
+ }
3931
+ function toTextFieldValue(plainText) {
3932
+ return plainText.trim();
3933
+ }
3934
+ function cellValueToSortValue(cellValue) {
3935
+ return (cellValue && cellValue.trim()) || null;
3936
+ }
3937
+
3938
+ const FieldModelMap = {
3939
+ [AITableFieldType.text]: new TextField(),
3940
+ [AITableFieldType.richText]: new RichTextField(),
3941
+ [AITableFieldType.select]: new SelectField(),
3942
+ [AITableFieldType.date]: new DateField(),
3943
+ [AITableFieldType.createdAt]: new DateField(),
3944
+ [AITableFieldType.updatedAt]: new DateField(),
3945
+ [AITableFieldType.number]: new NumberField(),
3946
+ [AITableFieldType.rate]: new RateField(),
3947
+ [AITableFieldType.link]: new LinkField(),
3948
+ [AITableFieldType.member]: new MemberField(),
3949
+ [AITableFieldType.progress]: new ProgressField(),
3950
+ [AITableFieldType.createdBy]: new MemberField(),
3951
+ [AITableFieldType.updatedBy]: new MemberField(),
3952
+ [AITableFieldType.attachment]: new AttachmentField()
4073
3953
  };
4074
- function getPasteValue(plainText, aiTableContent, recordIndex, fieldIndex, targetField, references) {
4075
- let field = null;
4076
- let record = null;
4077
- if (aiTableContent) {
4078
- const { fields, records } = aiTableContent;
4079
- field = fields[fieldIndex];
4080
- record = records[recordIndex];
3954
+
3955
+ function getColumnIndicesSizeMap(aiTable, fields) {
3956
+ const fieldSizeMap = aiTable.gridData().fieldsSizeMap;
3957
+ const columnIndicesSizeMap = {};
3958
+ fields?.forEach((field, index) => {
3959
+ columnIndicesSizeMap[index] = fieldSizeMap[field._id] ?? getFieldOptionByField(aiTable, field).width;
3960
+ });
3961
+ return columnIndicesSizeMap;
3962
+ }
3963
+ /**
3964
+ * 获取单元格位置
3965
+ * 根据单元格是否是第一列/最后一列确定单元格所在的位置
3966
+ */
3967
+ function getCellHorizontalPosition(options) {
3968
+ const { columnWidth } = options;
3969
+ return { width: columnWidth, offset: 0 };
3970
+ }
3971
+ function transformCellValue(aiTable, field, cellValue) {
3972
+ const richTextField = FieldModelMap[field.type];
3973
+ if (!richTextField.isValid(cellValue)) {
3974
+ return null;
4081
3975
  }
4082
- if (targetField.type === AITableFieldType.attachment || (field && field.type === AITableFieldType.attachment)) {
4083
- return { value: null, newField: null };
3976
+ const fieldService = AI_TABLE_GRID_FIELD_SERVICE_MAP.get(aiTable);
3977
+ if (!fieldService) {
3978
+ return cellValue;
4084
3979
  }
4085
- if (targetField.type !== AITableFieldType.link) {
4086
- plainText = extractText(plainText);
3980
+ const fieldRenderers = fieldService.aiFieldConfig?.fieldRenderers;
3981
+ if (!fieldRenderers) {
3982
+ return cellValue;
4087
3983
  }
4088
- let originData = field && record ? { field, cellValue: getFieldValue(record, field) } : null;
4089
- if (targetField.type === AITableFieldType.select) {
4090
- let { existOptionIds, newOptions } = processPastedValueForSelect(plainText, targetField, originData);
4091
- let newField = null;
4092
- let newOptionIds = [];
4093
- if (newOptions.length) {
4094
- newField = {
4095
- ...targetField,
4096
- settings: {
4097
- ...targetField.settings,
4098
- options: [...(targetField.settings?.options || []), ...newOptions]
4099
- }
4100
- };
4101
- newOptionIds = newOptions.map((option) => option._id).filter((id) => !!id);
3984
+ const cellTransform = fieldRenderers[field.type]?.transform;
3985
+ if (!cellTransform) {
3986
+ return cellValue;
3987
+ }
3988
+ const cellText = cellTransform(field, cellValue);
3989
+ if (cellText == null) {
3990
+ return cellValue;
3991
+ }
3992
+ return cellText;
3993
+ }
3994
+ /**
3995
+ * `\u4e00`: https://www.compart.com/en/unicode/U+4E00
3996
+ * `\u9fa5`: https://www.compart.com/en/unicode/U+9FA5
3997
+ */
3998
+ const UNIFIED_IDEOGRAPHS_REGEX = /^[\u4e00-\u9fa5]+$/;
3999
+ const SET_OF_LETTERS_REGEX = /^[a-zA-Z\/ ]+$/;
4000
+ function getAvatarShortName(name) {
4001
+ if (!name) {
4002
+ return '';
4003
+ }
4004
+ name = name.trim();
4005
+ if (UNIFIED_IDEOGRAPHS_REGEX.test(name) && name.length > 2) {
4006
+ return name.slice(name.length - 2);
4007
+ }
4008
+ if (SET_OF_LETTERS_REGEX.test(name) && name.indexOf(' ') > 0) {
4009
+ const words = name.split(' ');
4010
+ return (words[0].slice(0, 1) + words[1].slice(0, 1)).toUpperCase();
4011
+ }
4012
+ return name.length > 2 ? name.slice(0, 2).toUpperCase() : name.toUpperCase();
4013
+ }
4014
+ function getAvatarBgColor(name) {
4015
+ if (!name) {
4016
+ return;
4017
+ }
4018
+ const colors = ['#56abfb', '#5dcfff', '#84e17e', '#73d897', '#ff9f73', '#fa8888', '#fb7fb7', '#9a7ef4', '#868af6'];
4019
+ const nameArray = name.split('');
4020
+ const code = name && name.length > 0
4021
+ ? nameArray.reduce(function (result, item) {
4022
+ result.value += item.charCodeAt(0);
4023
+ return result;
4024
+ }, { value: 0 }).value
4025
+ : 0;
4026
+ return colors[code % 9];
4027
+ }
4028
+
4029
+ function getPlaceHolderCellsConfigs(options) {
4030
+ const { aiTable, coordinate, columnStartIndex, columnStopIndex, rowStartIndex, rowStopIndex } = options;
4031
+ const { linearRows } = aiTable.context;
4032
+ const { rowHeight, columnCount, rowCount } = coordinate;
4033
+ const visibleColumns = AITable.getVisibleFields(aiTable);
4034
+ let configs = [];
4035
+ for (let columnIndex = columnStartIndex; columnIndex <= columnStopIndex; columnIndex++) {
4036
+ // 当前列索引超出总列数范围,返回空
4037
+ if (columnIndex > columnCount - 1) {
4038
+ return [];
4039
+ }
4040
+ const field = visibleColumns[columnIndex];
4041
+ const fieldId = field._id;
4042
+ // 当前列不存在,返回空
4043
+ if (field == null) {
4044
+ return [];
4045
+ }
4046
+ // 当前列的 X 轴偏移量和列宽度
4047
+ const x = coordinate.getColumnOffset(columnIndex) + AI_TABLE_OFFSET * 2;
4048
+ const columnWidth = coordinate.getColumnWidth(columnIndex);
4049
+ for (let rowIndex = rowStartIndex; rowIndex <= rowStopIndex; rowIndex++) {
4050
+ // 当前行索引是否超出总行数范围,超出则退出循环
4051
+ if (rowIndex > rowCount - 1) {
4052
+ break;
4053
+ }
4054
+ const row = linearRows()[rowIndex];
4055
+ const { _id: recordId, type } = row;
4056
+ if (type !== AITableRowType.record) {
4057
+ continue;
4058
+ }
4059
+ // 当前行的 Y 轴偏移量,并根据列宽和列索引获取单元格的水平位置(宽度和偏移量)
4060
+ const y = coordinate.getRowOffset(rowIndex) + AI_TABLE_OFFSET * 2;
4061
+ const { width, offset } = getCellHorizontalPosition({
4062
+ columnWidth,
4063
+ columnIndex,
4064
+ columnCount
4065
+ });
4066
+ const height = rowHeight - AI_TABLE_OFFSET * 4;
4067
+ configs.unshift({
4068
+ key: `placeholder-cell-${fieldId}-${recordId}`,
4069
+ name: generateTargetName({
4070
+ targetName: AI_TABLE_CELL,
4071
+ fieldId,
4072
+ recordId
4073
+ }),
4074
+ x: x + offset,
4075
+ y,
4076
+ width: width - AI_TABLE_OFFSET * 4,
4077
+ height,
4078
+ fill: Colors.transparent,
4079
+ strokeEnabled: false,
4080
+ hitStrokeWidth: 0,
4081
+ transformsEnabled: 'position',
4082
+ perfectDrawEnabled: false,
4083
+ shadowEnabled: false
4084
+ });
4102
4085
  }
4103
- const selectFieldValue = newOptionIds?.length ? [...existOptionIds, ...newOptionIds] : existOptionIds;
4104
- return {
4105
- value: selectFieldValue,
4106
- newField
4107
- };
4108
4086
  }
4109
- return { value: FieldModelMap[targetField.type].toFieldValue(plainText, targetField, originData, references), newField: null };
4110
- }
4111
- function appendRecord(aiTable, actions) {
4112
- const allRecords = aiTable.records();
4113
- const lastRecordId = allRecords.length > 0 ? allRecords[allRecords.length - 1]._id : '';
4114
- actions.addRecord({
4115
- originId: lastRecordId
4116
- });
4087
+ return configs;
4117
4088
  }
4118
- function appendField(aiTable, originField, actions) {
4119
- const fields = aiTable.gridData().fields;
4120
- const lastFieldId = fields.length > 0 ? fields[fields.length - 1]._id : '';
4121
- let defaultFieldValue;
4122
- if (originField) {
4123
- const fieldOptions = getFieldOptions(aiTable);
4124
- defaultFieldValue = {
4125
- ...originField,
4126
- name: createDefaultFieldName(aiTable, fieldOptions.find((item) => item.type === originField.type)),
4127
- _id: idCreator()
4128
- };
4129
- }
4130
- else {
4131
- defaultFieldValue = createDefaultField(aiTable, AITableFieldType.text);
4089
+
4090
+ const fontCache = {};
4091
+ const textDataCache = new LRUCache({
4092
+ max: DEFAULT_TEXT_MAX_CACHE
4093
+ });
4094
+ /**
4095
+ * 计算给定文本在指定字体和 Canvas 环境下的宽度。
4096
+ * 它通过缓存机制来优化性能,避免重复计算相同文本的宽度
4097
+ */
4098
+ const getTextWidth = (ctx, text, font) => {
4099
+ let width = 0;
4100
+ if (!text || typeof text !== 'string') {
4101
+ return width;
4132
4102
  }
4133
- actions.addField({
4134
- originId: lastFieldId,
4135
- defaultValue: defaultFieldValue
4136
- });
4137
- }
4138
- const writeToAITable = async (aiTable, actions) => {
4139
- const selectedCells = Array.from(aiTable.selection().selectedCells);
4140
- if (!selectedCells.length) {
4141
- return;
4103
+ let cacheOfFont = fontCache[font];
4104
+ if (!cacheOfFont) {
4105
+ cacheOfFont = fontCache[font] = new LRUCache({
4106
+ max: 500
4107
+ });
4142
4108
  }
4143
- const { clipboardContent, aiTableContent } = await readClipboardData();
4144
- if (!clipboardContent.length) {
4145
- return;
4109
+ width = cacheOfFont.get(text);
4110
+ if (width == null) {
4111
+ ctx.font = font;
4112
+ width = ctx.measureText(text).width;
4113
+ cacheOfFont.set(text, width);
4146
4114
  }
4147
- const [firstCell] = selectedCells;
4148
- const [startRecordId, startFieldId] = firstCell.split(':');
4149
- const startRowIndex = aiTable.context.visibleRowsIndexMap().get(startRecordId) ?? 0;
4150
- const startColIndex = aiTable.context.visibleColumnsIndexMap().get(startFieldId) ?? 0;
4151
- const references = aiTable.context.references();
4152
- let isPasteSuccess = false;
4153
- clipboardContent.forEach((row, i) => {
4154
- const targetRowIndex = startRowIndex + i;
4155
- if (targetRowIndex >= aiTable.context.linearRows().length - 1) {
4156
- appendRecord(aiTable, actions);
4157
- }
4158
- row.forEach((plainText, j) => {
4159
- const targetColIndex = startColIndex + j;
4160
- if (targetColIndex >= AITable.getVisibleFields(aiTable).length) {
4161
- const originField = aiTableContent?.fields[j] || null;
4162
- appendField(aiTable, originField, actions);
4163
- }
4164
- const targetRecord = aiTable.context.linearRows()[targetRowIndex];
4165
- const targetField = AITable.getVisibleFields(aiTable)[targetColIndex];
4166
- const recordIndex = i;
4167
- const fieldIndex = j;
4168
- const { value, newField } = getPasteValue(plainText, aiTableContent, recordIndex, fieldIndex, targetField, references);
4169
- if (newField) {
4170
- actions.setField(newField);
4115
+ return width;
4116
+ };
4117
+
4118
+ const imageCache = (() => {
4119
+ const imageMap = {};
4120
+ const imgPromises = [];
4121
+ function loadImage(name, src, option) {
4122
+ imgPromises.push(new Promise((resolve, reject) => {
4123
+ const img = new Image();
4124
+ img.src = src;
4125
+ img.referrerPolicy = 'no-referrer';
4126
+ if (!option?.crossOrigin) {
4127
+ img.crossOrigin = 'Anonymous';
4171
4128
  }
4172
- if (value !== null) {
4173
- try {
4174
- actions.updateFieldValue({
4175
- value,
4176
- path: [targetRecord._id, targetField._id]
4129
+ imageMap[name] = {
4130
+ img,
4131
+ success: false
4132
+ };
4133
+ try {
4134
+ img.onload = () => {
4135
+ imageMap[name] = {
4136
+ img,
4137
+ success: true
4138
+ };
4139
+ resolve({
4140
+ name,
4141
+ img
4177
4142
  });
4178
- isPasteSuccess = true;
4179
- }
4180
- catch (error) { }
4143
+ };
4181
4144
  }
4182
- });
4183
- });
4184
- return isPasteSuccess;
4145
+ catch (err) {
4146
+ imageMap[name] = {
4147
+ img,
4148
+ success: false
4149
+ };
4150
+ reject(err);
4151
+ }
4152
+ }));
4153
+ }
4154
+ function imageMapOnload(callback) {
4155
+ Promise.all(imgPromises).then(callback);
4156
+ }
4157
+ function getImage(name) {
4158
+ const imgInfo = imageMap[name];
4159
+ if (imgInfo == null) {
4160
+ return null;
4161
+ }
4162
+ const { img, success } = imgInfo;
4163
+ if (!success)
4164
+ return false;
4165
+ return img;
4166
+ }
4167
+ return {
4168
+ loadImage,
4169
+ getImage,
4170
+ imageMapOnload,
4171
+ imageMap
4172
+ };
4173
+ })();
4174
+
4175
+ const isWindowsOS = () => {
4176
+ const agent = navigator.userAgent.toLowerCase();
4177
+ if (agent.indexOf('win32') >= 0 || agent.indexOf('wow32') >= 0) {
4178
+ return true;
4179
+ }
4180
+ if (agent.indexOf('win64') >= 0 || agent.indexOf('wow64') >= 0) {
4181
+ return true;
4182
+ }
4183
+ return false;
4184
+ };
4185
+ const isWindows = isWindowsOS();
4186
+ const isMac = () => {
4187
+ const agent = navigator.userAgent;
4188
+ return /macintosh/i.test(agent);
4185
4189
  };
4186
4190
 
4187
4191
  const getVisibleRangeInfo = (coordinate, scrollState) => {
@@ -4264,7 +4268,7 @@ const getHoverEditorBoxOffset = () => {
4264
4268
  return borderSpace / 2;
4265
4269
  };
4266
4270
 
4267
- const handleMouseStyle = (realTargetName, areaType = AITableAreaType.grid, container, isReadOnly) => {
4271
+ const handleMouseStyle = (realTargetName, areaType = AITableAreaType.grid, container, isReadOnly, isRowDragDisabled) => {
4268
4272
  const { targetName, mouseStyle } = getDetailByTargetName(realTargetName);
4269
4273
  if (mouseStyle)
4270
4274
  return setMouseStyle(mouseStyle, container);
@@ -4284,6 +4288,12 @@ const handleMouseStyle = (realTargetName, areaType = AITableAreaType.grid, conta
4284
4288
  }
4285
4289
  return setMouseStyle('col-resize', container);
4286
4290
  }
4291
+ case AI_TABLE_ROW_DRAG: {
4292
+ if (isReadOnly || isRowDragDisabled) {
4293
+ return setMouseStyle('default', container);
4294
+ }
4295
+ return setMouseStyle('pointer', container);
4296
+ }
4287
4297
  default:
4288
4298
  return setMouseStyle('default', container);
4289
4299
  }
@@ -6428,20 +6438,20 @@ class AddRowLayout extends Layout {
6428
6438
  const frozenOffset = AI_TABLE_OFFSET;
6429
6439
  const fill = isHoverRow ? this.colors.gray80 : this.colors.transparent;
6430
6440
  this.rect({
6431
- x: frozenOffset,
6441
+ x: frozenOffset + AI_TABLE_ROW_DRAG_ICON_WIDTH,
6432
6442
  y: y + AI_TABLE_OFFSET,
6433
6443
  width: columnWidth + AI_TABLE_ROW_HEAD_WIDTH - frozenOffset + 1,
6434
6444
  height: rowHeight,
6435
6445
  fill
6436
6446
  });
6437
6447
  this.line({
6438
- x: frozenOffset,
6448
+ x: frozenOffset + AI_TABLE_ROW_DRAG_ICON_WIDTH,
6439
6449
  y,
6440
6450
  points: [0, rowHeight, columnWidth + AI_TABLE_ROW_HEAD_WIDTH - frozenOffset + 1, rowHeight],
6441
6451
  stroke: this.colors.gray200
6442
6452
  });
6443
6453
  this.path({
6444
- x: AI_TABLE_CELL_PADDING,
6454
+ x: AI_TABLE_CELL_PADDING + AI_TABLE_ROW_DRAG_ICON_WIDTH,
6445
6455
  y: y + (rowHeight - AI_TABLE_ICON_COMMON_SIZE) / 2 - AI_TABLE_OFFSET,
6446
6456
  data: AddOutlinedPath,
6447
6457
  size: AI_TABLE_ROW_HEAD_SIZE,
@@ -6511,7 +6521,7 @@ class CellDrawer extends Drawer {
6511
6521
  const { field, cellValue } = render;
6512
6522
  const fieldType = field.type;
6513
6523
  const fieldMethod = FieldModelMap[fieldType];
6514
- if (!fieldMethod.isValid(cellValue) || cellValue == null) {
6524
+ if (!fieldMethod.isValid(cellValue)) {
6515
6525
  return;
6516
6526
  }
6517
6527
  switch (fieldType) {
@@ -6541,7 +6551,10 @@ class CellDrawer extends Drawer {
6541
6551
  }
6542
6552
  }
6543
6553
  renderCellText(render, ctx) {
6544
- const { x, y, transformValue, cellValue, field, columnWidth, style } = render;
6554
+ const { x, y, transformValue, field, columnWidth, style } = render;
6555
+ if (isNil(transformValue)) {
6556
+ return;
6557
+ }
6545
6558
  const fieldType = field.type;
6546
6559
  let renderText = fieldType === AITableFieldType.link ? transformValue?.text : transformValue;
6547
6560
  if (renderText == null) {
@@ -6905,12 +6918,11 @@ class CellDrawer extends Drawer {
6905
6918
  renderCellDate(render, ctx) {
6906
6919
  const { x, y, transformValue, columnWidth, style } = render;
6907
6920
  const colors = AITable.getColors();
6908
- let cellText = transformValue;
6909
- if (cellText == null || !___default.isString(cellText)) {
6921
+ if (isNil(transformValue)) {
6910
6922
  return;
6911
6923
  }
6912
6924
  const textMaxWidth = columnWidth - 2 * AI_TABLE_CELL_PADDING;
6913
- const { text } = this.textEllipsis({ text: cellText, maxWidth: columnWidth && textMaxWidth });
6925
+ const { text } = this.textEllipsis({ text: transformValue, maxWidth: columnWidth && textMaxWidth });
6914
6926
  if (ctx) {
6915
6927
  const color = style?.color || colors.gray800;
6916
6928
  this.text({
@@ -6924,13 +6936,12 @@ class CellDrawer extends Drawer {
6924
6936
  }
6925
6937
  }
6926
6938
  renderCellRate(render, ctx) {
6927
- const { x, y, transformValue: _cellValue } = render;
6939
+ const { x, y, transformValue } = render;
6928
6940
  const max = AI_TABLE_RATE_MAX;
6929
- const cellValue = _cellValue;
6930
6941
  const size = AI_TABLE_CELL_EMOJI_SIZE;
6931
6942
  return [...Array(max).keys()].map((item, index) => {
6932
6943
  const value = index + 1;
6933
- const checked = value <= cellValue;
6944
+ const checked = value <= (transformValue || 0);
6934
6945
  const iconX = index * size + AI_TABLE_CELL_PADDING + index * AI_TABLE_CELL_EMOJI_PADDING;
6935
6946
  const iconY = (AI_TABLE_ROW_BLANK_HEIGHT - size) / 2;
6936
6947
  if (ctx) {
@@ -6949,12 +6960,9 @@ class CellDrawer extends Drawer {
6949
6960
  renderCellProgress(render, ctx) {
6950
6961
  const { x, y, transformValue, columnWidth, style } = render;
6951
6962
  const colors = AITable.getColors();
6952
- let cellValue = transformValue;
6953
- if (isNil(cellValue)) {
6954
- cellValue = 0;
6955
- }
6956
- if (!___default.isNumber(cellValue)) {
6957
- return;
6963
+ let validateTransformValue = transformValue;
6964
+ if (isNil(validateTransformValue)) {
6965
+ validateTransformValue = 0;
6958
6966
  }
6959
6967
  const width = columnWidth - 2 * AI_TABLE_CELL_PADDING - AI_TABLE_PROGRESS_TEXT_Width;
6960
6968
  const height = AI_TABLE_PROGRESS_BAR_HEIGHT;
@@ -6972,7 +6980,7 @@ class CellDrawer extends Drawer {
6972
6980
  fill: colors.gray200
6973
6981
  });
6974
6982
  // 计算并绘制进度
6975
- const progressWidth = (cellValue / 100) * width;
6983
+ const progressWidth = (validateTransformValue / 100) * width;
6976
6984
  this.rect({
6977
6985
  x: x + offsetX,
6978
6986
  y: y + offsetY,
@@ -6984,14 +6992,13 @@ class CellDrawer extends Drawer {
6984
6992
  this.text({
6985
6993
  x: x + offsetX + width + AI_TABLE_TEXT_GAP,
6986
6994
  y: y + textOffsetY,
6987
- text: `${cellValue}%`,
6995
+ text: `${validateTransformValue}%`,
6988
6996
  fillStyle: colors.gray800
6989
6997
  });
6990
6998
  }
6991
6999
  renderCellMember(render, ctx) {
6992
- const { references, x, y, field, transformValue: _cellValue, rowHeight, columnWidth, isActive } = render;
6993
- const cellValue = _cellValue;
6994
- if (!cellValue?.length || !references) {
7000
+ const { references, x, y, field, transformValue, rowHeight, columnWidth, isActive } = render;
7001
+ if (!transformValue?.length || !references) {
6995
7002
  return;
6996
7003
  }
6997
7004
  const settings = field.settings;
@@ -7006,10 +7013,10 @@ class CellDrawer extends Drawer {
7006
7013
  const maxTextWidth = isOperating
7007
7014
  ? columnWidth - 2 * AI_TABLE_CELL_PADDING - itemOtherWidth - AI_TABLE_CELL_DELETE_ITEM_BUTTON_SIZE - 12
7008
7015
  : columnWidth - 2 * AI_TABLE_CELL_PADDING - itemOtherWidth;
7009
- const listCount = cellValue.length;
7016
+ const listCount = transformValue.length;
7010
7017
  let isOverflow = false;
7011
7018
  for (let index = 0; index < listCount; index++) {
7012
- const userInfo = references?.members[cellValue[index]];
7019
+ const userInfo = references?.members[transformValue[index]];
7013
7020
  if (!userInfo)
7014
7021
  continue;
7015
7022
  const { uid, display_name, avatar } = userInfo;
@@ -7097,8 +7104,10 @@ class CellDrawer extends Drawer {
7097
7104
  }
7098
7105
  }
7099
7106
  renderCellAttachment(render, ctx) {
7100
- const { references, x, y, field, transformValue: _cellValue, rowHeight, columnWidth, isActive } = render;
7101
- const cellValue = _cellValue || [];
7107
+ const { references, x, y, field, transformValue, rowHeight, columnWidth, isActive } = render;
7108
+ if (isNil(transformValue)) {
7109
+ return;
7110
+ }
7102
7111
  const fileIconSize = AI_TABLE_FILE_ICON_SIZE;
7103
7112
  const itemHeight = AI_TABLE_FILE_ICON_ITEM_HEIGHT;
7104
7113
  const isOperating = isActive;
@@ -7109,10 +7118,10 @@ class CellDrawer extends Drawer {
7109
7118
  const maxTextWidth = isOperating
7110
7119
  ? columnWidth - 2 * AI_TABLE_CELL_PADDING - itemOtherWidth - AI_TABLE_CELL_DELETE_ITEM_BUTTON_SIZE - 12
7111
7120
  : columnWidth - 2 * AI_TABLE_CELL_PADDING - itemOtherWidth;
7112
- const listCount = cellValue.length;
7121
+ const listCount = transformValue.length;
7113
7122
  let isOverflow = false;
7114
7123
  for (let index = 0; index < listCount; index++) {
7115
- const attachmentInfo = references?.attachments[cellValue[index]];
7124
+ const attachmentInfo = references?.attachments[transformValue[index]];
7116
7125
  if (!attachmentInfo)
7117
7126
  continue;
7118
7127
  const { title, addition } = attachmentInfo;
@@ -7200,9 +7209,9 @@ class RecordRowLayout extends Layout {
7200
7209
  fillBg = colors.gray80;
7201
7210
  }
7202
7211
  this.customRect({
7203
- x: AI_TABLE_OFFSET,
7212
+ x: AI_TABLE_OFFSET + AI_TABLE_ROW_DRAG_ICON_WIDTH,
7204
7213
  y,
7205
- width: AI_TABLE_ROW_HEAD_WIDTH - AI_TABLE_OFFSET,
7214
+ width: AI_TABLE_ROW_HEAD_WIDTH - AI_TABLE_OFFSET - AI_TABLE_ROW_DRAG_ICON_WIDTH,
7206
7215
  height: rowHeight,
7207
7216
  fill: fillBg,
7208
7217
  strokes: {
@@ -7223,7 +7232,7 @@ class RecordRowLayout extends Layout {
7223
7232
  // 设置字体样式,居中绘制行号
7224
7233
  this.setStyle({ fontSize: DEFAULT_FONT_SIZE });
7225
7234
  this.text({
7226
- x: AI_TABLE_ROW_HEAD_WIDTH / 2,
7235
+ x: (AI_TABLE_ROW_HEAD_WIDTH + AI_TABLE_ROW_DRAG_ICON_WIDTH) / 2,
7227
7236
  y: y + AI_TABLE_FIELD_HEAD_HEIGHT / 2,
7228
7237
  text: String(row.displayIndex),
7229
7238
  textAlign: DEFAULT_TEXT_ALIGN_CENTER,
@@ -7483,6 +7492,9 @@ class AITableIcon {
7483
7492
  case AITableCheckType.unchecked:
7484
7493
  pathData = Unchecked;
7485
7494
  break;
7495
+ case DragType.record:
7496
+ pathData = RowDragPath;
7497
+ break;
7486
7498
  }
7487
7499
  return {
7488
7500
  x: backgroundWidth && (backgroundWidth - size * (scaleX || 1)) / 2,
@@ -7948,6 +7960,16 @@ class AITableFrozenColumnHeads {
7948
7960
  columnStopIndex: this.coordinate().frozenColumnCount - 1
7949
7961
  });
7950
7962
  });
7963
+ this.dragHeadBgConfig = computed(() => {
7964
+ return {
7965
+ x: AI_TABLE_OFFSET,
7966
+ y: AI_TABLE_OFFSET,
7967
+ width: AI_TABLE_ROW_DRAG_ICON_WIDTH,
7968
+ height: this.fieldHeadHeight(),
7969
+ fill: Colors.white,
7970
+ listening: false
7971
+ };
7972
+ });
7951
7973
  this.numberHeadBgConfig = computed(() => {
7952
7974
  return {
7953
7975
  x: AI_TABLE_OFFSET,
@@ -7959,7 +7981,7 @@ class AITableFrozenColumnHeads {
7959
7981
  };
7960
7982
  });
7961
7983
  this.topLineConfig = {
7962
- x: AI_TABLE_OFFSET,
7984
+ x: AI_TABLE_OFFSET + AI_TABLE_ROW_DRAG_ICON_WIDTH,
7963
7985
  y: AI_TABLE_OFFSET,
7964
7986
  points: [0, 0, AI_TABLE_ROW_HEAD_WIDTH, 0],
7965
7987
  stroke: Colors.gray200,
@@ -7968,7 +7990,7 @@ class AITableFrozenColumnHeads {
7968
7990
  };
7969
7991
  this.bottomLineConfig = computed(() => {
7970
7992
  return {
7971
- x: AI_TABLE_OFFSET,
7993
+ x: AI_TABLE_OFFSET + AI_TABLE_ROW_DRAG_ICON_WIDTH,
7972
7994
  y: AI_TABLE_OFFSET,
7973
7995
  points: [AI_TABLE_ROW_HEAD_WIDTH, this.fieldHeadHeight(), 0, this.fieldHeadHeight()],
7974
7996
  stroke: Colors.gray200,
@@ -7979,7 +8001,7 @@ class AITableFrozenColumnHeads {
7979
8001
  this.iconConfig = computed(() => {
7980
8002
  return {
7981
8003
  name: AI_TABLE_FIELD_HEAD_SELECT_CHECKBOX,
7982
- x: AI_TABLE_CELL_PADDING,
8004
+ x: AI_TABLE_CELL_PADDING + AI_TABLE_ROW_DRAG_ICON_WIDTH,
7983
8005
  y: (this.fieldHeadHeight() - AI_TABLE_ICON_COMMON_SIZE) / 2,
7984
8006
  type: this.isChecked() ? AITableCheckType.checked : AITableCheckType.unchecked,
7985
8007
  fill: this.isChecked() || (this.config().pointPosition.targetName === AI_TABLE_FIELD_HEAD_SELECT_CHECKBOX && !this.isChecked())
@@ -8003,6 +8025,7 @@ class AITableFrozenColumnHeads {
8003
8025
  }
8004
8026
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AITableFrozenColumnHeads, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8005
8027
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: AITableFrozenColumnHeads, isStandalone: true, selector: "ai-table-frozen-column-heads", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: `
8028
+ <ko-rect [config]="dragHeadBgConfig()"></ko-rect>
8006
8029
  <ko-rect [config]="numberHeadBgConfig()"></ko-rect>
8007
8030
  <ko-line [config]="topLineConfig"></ko-line>
8008
8031
  <ko-line [config]="bottomLineConfig()"></ko-line>
@@ -8020,6 +8043,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
8020
8043
  args: [{
8021
8044
  selector: 'ai-table-frozen-column-heads',
8022
8045
  template: `
8046
+ <ko-rect [config]="dragHeadBgConfig()"></ko-rect>
8023
8047
  <ko-rect [config]="numberHeadBgConfig()"></ko-rect>
8024
8048
  <ko-line [config]="topLineConfig"></ko-line>
8025
8049
  <ko-line [config]="bottomLineConfig()"></ko-line>
@@ -8158,11 +8182,21 @@ class AITableHoverRowHeads {
8158
8182
  targetName: AI_TABLE_ROW_SELECT_CHECKBOX,
8159
8183
  recordId
8160
8184
  }),
8161
- x: AI_TABLE_CELL_PADDING,
8185
+ x: AI_TABLE_CELL_PADDING + AI_TABLE_ROW_DRAG_ICON_WIDTH,
8162
8186
  y: iconOffsetY,
8163
8187
  type: isCheckedRow ? AITableCheckType.checked : AITableCheckType.unchecked,
8164
8188
  fill: isCheckedRow || (targetName === AI_TABLE_ROW_SELECT_CHECKBOX && !isCheckedRow) ? Colors.primary : Colors.gray300
8165
8189
  };
8190
+ operationGroup.dragConfig = {
8191
+ name: generateTargetName({
8192
+ targetName: AI_TABLE_ROW_DRAG,
8193
+ recordId
8194
+ }),
8195
+ x: 0,
8196
+ y: iconOffsetY,
8197
+ type: DragType.record,
8198
+ fill: Colors.gray600
8199
+ };
8166
8200
  headConfigs.push(operationGroup);
8167
8201
  }
8168
8202
  }
@@ -8179,6 +8213,9 @@ class AITableHoverRowHeads {
8179
8213
  @if (config.iconConfig) {
8180
8214
  <ai-table-icon [config]="config.iconConfig"></ai-table-icon>
8181
8215
  }
8216
+ @if (config.dragConfig) {
8217
+ <ai-table-icon [config]="config.dragConfig"></ai-table-icon>
8218
+ }
8182
8219
  </ko-group>
8183
8220
  }
8184
8221
  `, isInline: true, dependencies: [{ kind: "component", type: KoContainer, selector: "ko-layer, ko-fastlayer, ko-group" }, { kind: "component", type: KoShape, selector: "ko-shape, ko-circle, ko-label, ko-rect, ko-ellipse, ko-wedge, ko-line, ko-sprite, ko-image, ko-text, ko-text-path, ko-star, ko-ring, ko-arc, ko-tag, ko-path, ko-regular-polygon, ko-arrow, ko-transformer", inputs: ["config"], outputs: ["koMouseover", "koMousemove", "koMouseout", "koMouseenter", "koMouseleave", "koMousedown", "koMouseup", "koWheel", "koContextmenu", "koClick", "koDblclick", "koTouchstart", "koTouchmove", "koTouchend", "koTap", "koDbltap", "koDragstart", "koDragmove", "koDragend"] }, { kind: "component", type: AITableIcon, selector: "ai-table-icon", inputs: ["config"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
@@ -8196,6 +8233,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
8196
8233
  @if (config.iconConfig) {
8197
8234
  <ai-table-icon [config]="config.iconConfig"></ai-table-icon>
8198
8235
  }
8236
+ @if (config.dragConfig) {
8237
+ <ai-table-icon [config]="config.dragConfig"></ai-table-icon>
8238
+ }
8199
8239
  </ko-group>
8200
8240
  }
8201
8241
  `,
@@ -8991,6 +9031,7 @@ class AITableGridBase {
8991
9031
  this.aiContextMenuItems = input();
8992
9032
  this.aiFieldConfig = input();
8993
9033
  this.aiReadonly = input(false);
9034
+ this.aiRowDragDisabled = input(false);
8994
9035
  this.aiPlugins = input();
8995
9036
  this.aiReferences = input.required();
8996
9037
  this.aiBuildRenderDataFn = input();
@@ -9008,6 +9049,7 @@ class AITableGridBase {
9008
9049
  this.aiUpdateFieldValue = output();
9009
9050
  this.aiSetField = output();
9010
9051
  this.aiSetFieldWidth = output();
9052
+ this.aiMoveRecords = output();
9011
9053
  this.aiClick = output();
9012
9054
  this.aiDbClick = output();
9013
9055
  this.fieldMenus = computed(() => {
@@ -9056,7 +9098,7 @@ class AITableGridBase {
9056
9098
  AI_TABLE_GRID_FIELD_SERVICE_MAP.set(this.aiTable, this.aiTableGridFieldService);
9057
9099
  }
9058
9100
  addRecord() {
9059
- const records = this.aiRecords();
9101
+ const records = this.aiTable.gridData().records;
9060
9102
  const recordCount = records.length;
9061
9103
  this.aiAddRecord.emit({
9062
9104
  originId: recordCount > 0 ? records[records.length - 1]._id : ''
@@ -9132,7 +9174,7 @@ class AITableGridBase {
9132
9174
  }
9133
9175
  }
9134
9176
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AITableGridBase, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
9135
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.2.13", type: AITableGridBase, isStandalone: true, selector: "ai-table-grid-base", inputs: { aiRecords: { classPropertyName: "aiRecords", publicName: "aiRecords", isSignal: true, isRequired: true, transformFunction: null }, aiFields: { classPropertyName: "aiFields", publicName: "aiFields", isSignal: true, isRequired: true, transformFunction: null }, aiFieldsSizeMap: { classPropertyName: "aiFieldsSizeMap", publicName: "aiFieldsSizeMap", isSignal: true, isRequired: true, transformFunction: null }, aiContextMenuItems: { classPropertyName: "aiContextMenuItems", publicName: "aiContextMenuItems", isSignal: true, isRequired: false, transformFunction: null }, aiFieldConfig: { classPropertyName: "aiFieldConfig", publicName: "aiFieldConfig", isSignal: true, isRequired: false, transformFunction: null }, aiReadonly: { classPropertyName: "aiReadonly", publicName: "aiReadonly", isSignal: true, isRequired: false, transformFunction: null }, aiPlugins: { classPropertyName: "aiPlugins", publicName: "aiPlugins", isSignal: true, isRequired: false, transformFunction: null }, aiReferences: { classPropertyName: "aiReferences", publicName: "aiReferences", isSignal: true, isRequired: true, transformFunction: null }, aiBuildRenderDataFn: { classPropertyName: "aiBuildRenderDataFn", publicName: "aiBuildRenderDataFn", isSignal: true, isRequired: false, transformFunction: null }, aiGetI18nTextByKey: { classPropertyName: "aiGetI18nTextByKey", publicName: "aiGetI18nTextByKey", isSignal: true, isRequired: false, transformFunction: null }, aiKeywords: { classPropertyName: "aiKeywords", publicName: "aiKeywords", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { aiRecords: "aiRecordsChange", aiFields: "aiFieldsChange", aiFieldsSizeMap: "aiFieldsSizeMapChange", aiTableInitialized: "aiTableInitialized", aiAddRecord: "aiAddRecord", aiAddField: "aiAddField", aiMoveField: "aiMoveField", aiUpdateFieldValue: "aiUpdateFieldValue", aiSetField: "aiSetField", aiSetFieldWidth: "aiSetFieldWidth", aiClick: "aiClick", aiDbClick: "aiDbClick" }, ngImport: i0, template: '', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
9177
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.2.13", type: AITableGridBase, isStandalone: true, selector: "ai-table-grid-base", inputs: { aiRecords: { classPropertyName: "aiRecords", publicName: "aiRecords", isSignal: true, isRequired: true, transformFunction: null }, aiFields: { classPropertyName: "aiFields", publicName: "aiFields", isSignal: true, isRequired: true, transformFunction: null }, aiFieldsSizeMap: { classPropertyName: "aiFieldsSizeMap", publicName: "aiFieldsSizeMap", isSignal: true, isRequired: true, transformFunction: null }, aiContextMenuItems: { classPropertyName: "aiContextMenuItems", publicName: "aiContextMenuItems", isSignal: true, isRequired: false, transformFunction: null }, aiFieldConfig: { classPropertyName: "aiFieldConfig", publicName: "aiFieldConfig", isSignal: true, isRequired: false, transformFunction: null }, aiReadonly: { classPropertyName: "aiReadonly", publicName: "aiReadonly", isSignal: true, isRequired: false, transformFunction: null }, aiRowDragDisabled: { classPropertyName: "aiRowDragDisabled", publicName: "aiRowDragDisabled", isSignal: true, isRequired: false, transformFunction: null }, aiPlugins: { classPropertyName: "aiPlugins", publicName: "aiPlugins", isSignal: true, isRequired: false, transformFunction: null }, aiReferences: { classPropertyName: "aiReferences", publicName: "aiReferences", isSignal: true, isRequired: true, transformFunction: null }, aiBuildRenderDataFn: { classPropertyName: "aiBuildRenderDataFn", publicName: "aiBuildRenderDataFn", isSignal: true, isRequired: false, transformFunction: null }, aiGetI18nTextByKey: { classPropertyName: "aiGetI18nTextByKey", publicName: "aiGetI18nTextByKey", isSignal: true, isRequired: false, transformFunction: null }, aiKeywords: { classPropertyName: "aiKeywords", publicName: "aiKeywords", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { aiRecords: "aiRecordsChange", aiFields: "aiFieldsChange", aiFieldsSizeMap: "aiFieldsSizeMapChange", aiTableInitialized: "aiTableInitialized", aiAddRecord: "aiAddRecord", aiAddField: "aiAddField", aiMoveField: "aiMoveField", aiUpdateFieldValue: "aiUpdateFieldValue", aiSetField: "aiSetField", aiSetFieldWidth: "aiSetFieldWidth", aiMoveRecords: "aiMoveRecords", aiClick: "aiClick", aiDbClick: "aiDbClick" }, ngImport: i0, template: '', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
9136
9178
  }
9137
9179
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AITableGridBase, decorators: [{
9138
9180
  type: Component,
@@ -9271,11 +9313,13 @@ class AITableDragComponent {
9271
9313
  }
9272
9314
  this.setDisplayStyle('block');
9273
9315
  const moveX = e.x - (this.mouseStartPosition?.x || 0);
9316
+ const moveY = e.y - (this.mouseStartPosition?.y || 0);
9274
9317
  switch (drag.type) {
9275
9318
  case DragType.field:
9276
9319
  this.movingColumn(drag, moveX);
9277
9320
  break;
9278
9321
  case DragType.record:
9322
+ this.movingRecord(drag, moveY);
9279
9323
  break;
9280
9324
  case DragType.columnWidth:
9281
9325
  this.movingColumnWidth(drag, moveX);
@@ -9362,6 +9406,43 @@ class AITableDragComponent {
9362
9406
  width: Math.max(MIN_COLUMN_WIDTH, sourceColumnWidth + moveX)
9363
9407
  };
9364
9408
  }
9409
+ movingRecord(drag, moveY) {
9410
+ const aiTable = this.aiTableGridSelectionService.aiTable;
9411
+ const scroll = drag.scroll || { x: 0, y: 0 };
9412
+ const coordinate = drag.coordinate;
9413
+ const visibleRowIndexMap = aiTable.context.visibleRowsIndexMap();
9414
+ const sourceRowId = drag.sourceIds.values().next().value;
9415
+ const sourceRowIndex = visibleRowIndexMap.get(sourceRowId) || 0;
9416
+ const sourceRowStartY = coordinate.getRowOffset(sourceRowIndex);
9417
+ const sourceRowHeight = coordinate.getRowHeight(sourceRowIndex);
9418
+ this.setRectStyles({
9419
+ width: '100%',
9420
+ height: `${sourceRowHeight}px`,
9421
+ top: `${sourceRowStartY + moveY}px`,
9422
+ left: '0'
9423
+ });
9424
+ const pointerY = moveY + sourceRowStartY + sourceRowHeight / 2;
9425
+ const targetRowIndex = coordinate.getRowStartIndex(pointerY + scroll.y);
9426
+ const targetRowStartY = coordinate.getRowOffset(targetRowIndex);
9427
+ if ((targetRowIndex >= 0 && sourceRowIndex > targetRowIndex && sourceRowIndex - targetRowIndex > 0) ||
9428
+ (sourceRowIndex < targetRowIndex && targetRowIndex - sourceRowIndex > 1)) {
9429
+ this.setAuxiliaryLineStyles({
9430
+ width: `calc(100% - ${AI_TABLE_ROW_DRAG_ICON_WIDTH}px)`,
9431
+ height: '2px',
9432
+ top: `${targetRowStartY}px`,
9433
+ left: `${AI_TABLE_ROW_DRAG_ICON_WIDTH}px`
9434
+ });
9435
+ this.draggedData = {
9436
+ type: DragType.record,
9437
+ recordIds: drag.sourceIds,
9438
+ targetIndex: targetRowIndex
9439
+ };
9440
+ }
9441
+ else {
9442
+ this.resetAuxiliaryLine();
9443
+ this.draggedData = null;
9444
+ }
9445
+ }
9365
9446
  handleDragEnd() {
9366
9447
  if (this.draggedData) {
9367
9448
  this.dragEnd.emit({ ...this.draggedData });
@@ -9410,7 +9491,7 @@ class AITableDragComponent {
9410
9491
  });
9411
9492
  }
9412
9493
  resetAuxiliaryLine() {
9413
- this.setAuxiliaryLineStyles({ width: 0 });
9494
+ this.setAuxiliaryLineStyles({ width: 0, height: 0, top: '0', left: '0' });
9414
9495
  }
9415
9496
  ngOnDestroy() {
9416
9497
  if (this.mousedownListener)
@@ -9621,7 +9702,7 @@ class AITableGrid extends AITableGridBase {
9621
9702
  const { context } = this.aiTable;
9622
9703
  const { x, y } = pos;
9623
9704
  const curMousePosition = getMousePosition(this.aiTable, x, y, this.coordinate(), AITable.getVisibleFields(this.aiTable), context, targetName);
9624
- handleMouseStyle(curMousePosition.realTargetName, curMousePosition.areaType, this.containerElement(), this.aiReadonly());
9705
+ handleMouseStyle(curMousePosition.realTargetName, curMousePosition.areaType, this.containerElement(), this.aiReadonly(), this.aiRowDragDisabled());
9625
9706
  context.setPointPosition(curMousePosition);
9626
9707
  this.timer = null;
9627
9708
  if (this.isDragSelecting) {
@@ -9667,6 +9748,21 @@ class AITableGrid extends AITableGridBase {
9667
9748
  this.updateDragSelectionState(true, dragSelectionStart);
9668
9749
  this.aiTableGridSelectionService.selectCells(dragSelectionStart);
9669
9750
  return;
9751
+ case AI_TABLE_ROW_DRAG:
9752
+ if (!recordId)
9753
+ return;
9754
+ mouseEvent.preventDefault();
9755
+ const selectedRecords = this.aiTable.selection().selectedRecords;
9756
+ let dragRecords = [];
9757
+ if (selectedRecords.has(recordId)) {
9758
+ dragRecords = [recordId, ...selectedRecords.values()];
9759
+ }
9760
+ else {
9761
+ // 当前拖拽行不在选中行中,只拖拽当前行
9762
+ dragRecords = [recordId];
9763
+ }
9764
+ this.handleRowDragStart(dragRecords);
9765
+ return;
9670
9766
  case AI_TABLE_ROW_ADD_BUTTON:
9671
9767
  case AI_TABLE_FIELD_ADD_BUTTON:
9672
9768
  case AI_TABLE_ROW_SELECT_CHECKBOX:
@@ -9995,6 +10091,16 @@ class AITableGrid extends AITableGridBase {
9995
10091
  });
9996
10092
  }
9997
10093
  }
10094
+ handleRowDragStart(recordIds) {
10095
+ if (!this.aiReadonly() && !this.aiRowDragDisabled() && recordIds.length > 0) {
10096
+ this.aiTableGridSelectionService.drag({
10097
+ type: DragType.record,
10098
+ sourceIds: new Set(recordIds),
10099
+ scroll: this.getScrollPosition(),
10100
+ coordinate: this.coordinate()
10101
+ });
10102
+ }
10103
+ }
9998
10104
  getScrollPosition() {
9999
10105
  const horizontalBar = this.horizontalBarRef()?.nativeElement;
10000
10106
  const verticalBar = this.verticalBarRef()?.nativeElement;
@@ -10024,6 +10130,12 @@ class AITableGrid extends AITableGridBase {
10024
10130
  }
10025
10131
  break;
10026
10132
  case DragType.record:
10133
+ if (data.recordIds && isNumber(data.targetIndex)) {
10134
+ this.aiMoveRecords.emit({
10135
+ recordIds: Array.from(data.recordIds).map((id) => [id]),
10136
+ newPath: [data.targetIndex]
10137
+ });
10138
+ }
10027
10139
  return;
10028
10140
  }
10029
10141
  this.aiTableGridSelectionService.clearDrag();
@@ -10042,5 +10154,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
10042
10154
  * Generated bundle index. Do not edit.
10043
10155
  */
10044
10156
 
10045
- export { AITable, AITableAreaType, AITableAvatarSize, AITableAvatarType, AITableCheckType, AITableContextMenu, AITableDomGrid, AITableFieldIsSameOptionPipe, AITableFieldSetting, AITableFieldType, AITableFilterOperation, AITableGrid, AITableGridEventService, AITableGridFieldService, AITableGridI18nKey, AITableGridSelectionService, AITableMemberType, AITableMouseDownType, AITableQueries, AITableRenderer, AITableRowColumnType, AITableRowType, AITableSelectAllState, AITableSelectOptionStyle, AITableStatType, AI_TABLE_ACTION_COMMON_RADIUS, AI_TABLE_ACTION_COMMON_RIGHT_PADDING, AI_TABLE_ACTION_COMMON_SIZE, AI_TABLE_BLANK, AI_TABLE_CELL, AI_TABLE_CELL_ACTIVE_BORDER_WIDTH, AI_TABLE_CELL_ADD_ITEM_BUTTON_SIZE, AI_TABLE_CELL_ATTACHMENT_ADD, AI_TABLE_CELL_ATTACHMENT_FILE, AI_TABLE_CELL_BORDER, AI_TABLE_CELL_DELETE_ITEM_BUTTON_SIZE, AI_TABLE_CELL_DELETE_ITEM_BUTTON_SIZE_OFFSET, AI_TABLE_CELL_EDIT, AI_TABLE_CELL_EMOJI_PADDING, AI_TABLE_CELL_EMOJI_SIZE, AI_TABLE_CELL_FIELD_ITEM_HEIGHT, AI_TABLE_CELL_MAX_ROW_COUNT, AI_TABLE_CELL_MEMBER_ITEM_HEIGHT, AI_TABLE_CELL_MEMBER_ITEM_PADDING, AI_TABLE_CELL_MEMBER_MAX_HEIGHT, AI_TABLE_CELL_MULTI_DOT_RADIUS, AI_TABLE_CELL_MULTI_ITEM_MARGIN_LEFT, AI_TABLE_CELL_MULTI_ITEM_MARGIN_TOP, AI_TABLE_CELL_MULTI_ITEM_MIN_WIDTH, AI_TABLE_CELL_MULTI_PADDING_LEFT, AI_TABLE_CELL_MULTI_PADDING_TOP, AI_TABLE_CELL_PADDING, AI_TABLE_COMMON_FONT_SIZE, AI_TABLE_DEFAULT_COLUMN_WIDTH, AI_TABLE_DOT_RADIUS, AI_TABLE_FIELD_ADD_BUTTON, AI_TABLE_FIELD_ADD_BUTTON_WIDTH, AI_TABLE_FIELD_HEAD, AI_TABLE_FIELD_HEAD_HEIGHT, AI_TABLE_FIELD_HEAD_ICON_GAP_SIZE, AI_TABLE_FIELD_HEAD_MORE, AI_TABLE_FIELD_HEAD_OPACITY_LINE, AI_TABLE_FIELD_HEAD_SELECT_CHECKBOX, AI_TABLE_FIELD_HEAD_TEXT_MIN_WIDTH, AI_TABLE_FIELD_ITEM_MARGIN_RIGHT, AI_TABLE_FIELD_MAX_WIDTH, AI_TABLE_FIELD_MIDDLE_WIDTH, AI_TABLE_FIELD_MINI_WIDTH, AI_TABLE_FIELD_MIN_WIDTH, AI_TABLE_FILE_ICON_ITEM_HEIGHT, AI_TABLE_FILE_ICON_SIZE, AI_TABLE_GRID_FIELD_SERVICE_MAP, AI_TABLE_ICON_COMMON_SIZE, AI_TABLE_MEMBER_AVATAR_SIZE, AI_TABLE_MEMBER_ITEM_AVATAR_MARGIN_RIGHT, AI_TABLE_MEMBER_ITEM_PADDING_RIGHT, AI_TABLE_MIN_TEXT_WIDTH, AI_TABLE_OFFSET, AI_TABLE_OPTION_ITEM_FONT_SIZE, AI_TABLE_OPTION_ITEM_HEIGHT, AI_TABLE_OPTION_ITEM_PADDING, AI_TABLE_OPTION_ITEM_RADIUS, AI_TABLE_PIECE_RADIUS, AI_TABLE_PIECE_WIDTH, AI_TABLE_POPOVER_LEFT_OFFSET, AI_TABLE_PREVENT_CLEAR_SELECTION_CLASS, AI_TABLE_PROGRESS_BAR_HEIGHT, AI_TABLE_PROGRESS_BAR_RADIUS, AI_TABLE_PROGRESS_TEXT_Width, AI_TABLE_RATE_MAX, AI_TABLE_ROW_ADD_BUTTON, AI_TABLE_ROW_BLANK_HEIGHT, AI_TABLE_ROW_HEAD, AI_TABLE_ROW_HEAD_SIZE, AI_TABLE_ROW_HEAD_WIDTH, AI_TABLE_ROW_HEIGHT, AI_TABLE_ROW_SELECT_CHECKBOX, AI_TABLE_SCROLL_BAR_PADDING, AI_TABLE_TAG_FONT_SIZE, AI_TABLE_TAG_PADDING, AI_TABLE_TEXT_GAP, AbstractEditCellEditor, AddOutlinedPath, AttachmentPath, Check, Colors, ColumnCalendarFilledPath, ColumnLinkOutlinedPath, ColumnMemberFilledPath, ColumnMultipleFillPath, ColumnNumberFilledPath, ColumnProgressFilledPath, ColumnRatingFilledPath, ColumnRichTextFilledPath, ColumnSelectFilledPath, ColumnTextFilledPath, Coordinate, DBL_CLICK_EDIT_TYPE, DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE, DEFAULT_FONT_STYLE, DEFAULT_FONT_WEIGHT, DEFAULT_ICON_SHAPE, DEFAULT_ICON_SIZE, DEFAULT_POINT_POSITION, DEFAULT_SCROLL_STATE, DEFAULT_TEXT_ALIGN_CENTER, DEFAULT_TEXT_ALIGN_LEFT, DEFAULT_TEXT_ALIGN_RIGHT, DEFAULT_TEXT_DECORATION, DEFAULT_TEXT_ELLIPSIS, DEFAULT_TEXT_FILL, DEFAULT_TEXT_LINE_HEIGHT, DEFAULT_TEXT_LISTENING, DEFAULT_TEXT_MAX_CACHE, DEFAULT_TEXT_MAX_HEIGHT, DEFAULT_TEXT_SCALE, DEFAULT_TEXT_TRANSFORMS_ENABLED, DEFAULT_TEXT_VERTICAL_ALIGN_MIDDLE, DEFAULT_TEXT_VERTICAL_ALIGN_TOP, DEFAULT_TEXT_WRAP, DEFAULT_WRAP_TEXT_MAX_ROW, DateCellEditorComponent, DepartmentOutlinedPath, Direction, DragType, EditPath, FONT_SIZE_SM, FieldModelMap, GRID_CELL_EDITOR_MAP, IsSelectRecordPipe, KO_CONTAINER_TOKEN, KoComponent, KoContainer, KoShape, KoShapeTypes, KoStage, LinkCellEditorComponent, MIN_COLUMN_WIDTH, MOUSEOVER_EDIT_TYPE, MemberSettingPipe, MoreStandOutlinedPath, NumberCellEditorComponent, ProgressEditorComponent, RendererContext, RowHeight, SelectCellEditorComponent, SelectOptionComponent, SelectOptionPipe, SelectOptionsPipe, SelectSettingPipe, StarFill, TextCellEditorComponent, TextMeasure, Unchecked, UserPipe, WebOutlinedPath, aiTableFragmentAttribute, applyNodeProps, buildClipboardData, buildGridData, buildGridLinearRows, castToString, compareNumber, compareString, createAITable, createActiveCellBorder, createCells, createDefaultField, createDefaultFieldName, createListener, extractLinkUrl, extractText, generateNewName, generateTargetName, getAvatarBgColor, getAvatarShortName, getCellEditorBorderSpace, getCellHorizontalPosition, getColumnIndicesSizeMap, getDefaultFieldValue, getDefaultI18nTextByKey, getDetailByTargetName, getEditorBoxOffset, getEditorSpace, getFieldOptionByField, getFieldOptions, getFieldValue, getHoverCell, getHoverEditorBoxOffset, getHoverEditorSpace, getI18nTextByKey, getMousePosition, getName, getPlaceHolderCellsConfigs, getSystemFieldValue, getTargetName, getTextWidth, getVisibleRangeInfo, handleMouseStyle, hasIntersect, idCreator, idsCreator, imageCache, isActiveCell, isArrayField, isCellMatchKeywords, isClipboardReadSupported, isClipboardReadTextSupported, isClipboardWriteSupported, isClipboardWriteTextSupported, isDateFiled, isEmpty, isMac, isNumberFiled, isSameFieldOption, isSelectedField, isSystemField, isWindows, isWindowsOS, isWithinFrozenColumnBoundary, readFromClipboard, scrollMax, setMouseStyle, shortIdCreator, shortIdsCreator, stringInclude, textDataCache, transformCellValue, updatePicture, writeToAITable, writeToClipboard, zhIntlCollator };
10157
+ export { AITable, AITableAreaType, AITableAvatarSize, AITableAvatarType, AITableCheckType, AITableContextMenu, AITableDomGrid, AITableFieldIsSameOptionPipe, AITableFieldSetting, AITableFieldType, AITableFilterOperation, AITableGrid, AITableGridEventService, AITableGridFieldService, AITableGridI18nKey, AITableGridSelectionService, AITableMemberType, AITableMouseDownType, AITableQueries, AITableRenderer, AITableRowColumnType, AITableRowType, AITableSelectAllState, AITableSelectOptionStyle, AITableStatType, AI_TABLE_ACTION_COMMON_RADIUS, AI_TABLE_ACTION_COMMON_RIGHT_PADDING, AI_TABLE_ACTION_COMMON_SIZE, AI_TABLE_BLANK, AI_TABLE_CELL, AI_TABLE_CELL_ACTIVE_BORDER_WIDTH, AI_TABLE_CELL_ADD_ITEM_BUTTON_SIZE, AI_TABLE_CELL_ATTACHMENT_ADD, AI_TABLE_CELL_ATTACHMENT_FILE, AI_TABLE_CELL_BORDER, AI_TABLE_CELL_DELETE_ITEM_BUTTON_SIZE, AI_TABLE_CELL_DELETE_ITEM_BUTTON_SIZE_OFFSET, AI_TABLE_CELL_EDIT, AI_TABLE_CELL_EMOJI_PADDING, AI_TABLE_CELL_EMOJI_SIZE, AI_TABLE_CELL_FIELD_ITEM_HEIGHT, AI_TABLE_CELL_MAX_ROW_COUNT, AI_TABLE_CELL_MEMBER_ITEM_HEIGHT, AI_TABLE_CELL_MEMBER_ITEM_PADDING, AI_TABLE_CELL_MEMBER_MAX_HEIGHT, AI_TABLE_CELL_MULTI_DOT_RADIUS, AI_TABLE_CELL_MULTI_ITEM_MARGIN_LEFT, AI_TABLE_CELL_MULTI_ITEM_MARGIN_TOP, AI_TABLE_CELL_MULTI_ITEM_MIN_WIDTH, AI_TABLE_CELL_MULTI_PADDING_LEFT, AI_TABLE_CELL_MULTI_PADDING_TOP, AI_TABLE_CELL_PADDING, AI_TABLE_COMMON_FONT_SIZE, AI_TABLE_DEFAULT_COLUMN_WIDTH, AI_TABLE_DOT_RADIUS, AI_TABLE_FIELD_ADD_BUTTON, AI_TABLE_FIELD_ADD_BUTTON_WIDTH, AI_TABLE_FIELD_HEAD, AI_TABLE_FIELD_HEAD_HEIGHT, AI_TABLE_FIELD_HEAD_ICON_GAP_SIZE, AI_TABLE_FIELD_HEAD_MORE, AI_TABLE_FIELD_HEAD_OPACITY_LINE, AI_TABLE_FIELD_HEAD_SELECT_CHECKBOX, AI_TABLE_FIELD_HEAD_TEXT_MIN_WIDTH, AI_TABLE_FIELD_ITEM_MARGIN_RIGHT, AI_TABLE_FIELD_MAX_WIDTH, AI_TABLE_FIELD_MIDDLE_WIDTH, AI_TABLE_FIELD_MINI_WIDTH, AI_TABLE_FIELD_MIN_WIDTH, AI_TABLE_FILE_ICON_ITEM_HEIGHT, AI_TABLE_FILE_ICON_SIZE, AI_TABLE_GRID_FIELD_SERVICE_MAP, AI_TABLE_ICON_COMMON_SIZE, AI_TABLE_MEMBER_AVATAR_SIZE, AI_TABLE_MEMBER_ITEM_AVATAR_MARGIN_RIGHT, AI_TABLE_MEMBER_ITEM_PADDING_RIGHT, AI_TABLE_MIN_TEXT_WIDTH, AI_TABLE_OFFSET, AI_TABLE_OPTION_ITEM_FONT_SIZE, AI_TABLE_OPTION_ITEM_HEIGHT, AI_TABLE_OPTION_ITEM_PADDING, AI_TABLE_OPTION_ITEM_RADIUS, AI_TABLE_PIECE_RADIUS, AI_TABLE_PIECE_WIDTH, AI_TABLE_POPOVER_LEFT_OFFSET, AI_TABLE_PREVENT_CLEAR_SELECTION_CLASS, AI_TABLE_PROGRESS_BAR_HEIGHT, AI_TABLE_PROGRESS_BAR_RADIUS, AI_TABLE_PROGRESS_TEXT_Width, AI_TABLE_RATE_MAX, AI_TABLE_ROW_ADD_BUTTON, AI_TABLE_ROW_BLANK_HEIGHT, AI_TABLE_ROW_DRAG, AI_TABLE_ROW_DRAG_ICON_WIDTH, AI_TABLE_ROW_HEAD, AI_TABLE_ROW_HEAD_SIZE, AI_TABLE_ROW_HEAD_WIDTH, AI_TABLE_ROW_HEIGHT, AI_TABLE_ROW_SELECT_CHECKBOX, AI_TABLE_SCROLL_BAR_PADDING, AI_TABLE_TAG_FONT_SIZE, AI_TABLE_TAG_PADDING, AI_TABLE_TEXT_GAP, AbstractEditCellEditor, AddOutlinedPath, AttachmentPath, Check, Colors, ColumnCalendarFilledPath, ColumnLinkOutlinedPath, ColumnMemberFilledPath, ColumnMultipleFillPath, ColumnNumberFilledPath, ColumnProgressFilledPath, ColumnRatingFilledPath, ColumnRichTextFilledPath, ColumnSelectFilledPath, ColumnTextFilledPath, Coordinate, DBL_CLICK_EDIT_TYPE, DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE, DEFAULT_FONT_STYLE, DEFAULT_FONT_WEIGHT, DEFAULT_ICON_SHAPE, DEFAULT_ICON_SIZE, DEFAULT_POINT_POSITION, DEFAULT_SCROLL_STATE, DEFAULT_TEXT_ALIGN_CENTER, DEFAULT_TEXT_ALIGN_LEFT, DEFAULT_TEXT_ALIGN_RIGHT, DEFAULT_TEXT_DECORATION, DEFAULT_TEXT_ELLIPSIS, DEFAULT_TEXT_FILL, DEFAULT_TEXT_LINE_HEIGHT, DEFAULT_TEXT_LISTENING, DEFAULT_TEXT_MAX_CACHE, DEFAULT_TEXT_MAX_HEIGHT, DEFAULT_TEXT_SCALE, DEFAULT_TEXT_TRANSFORMS_ENABLED, DEFAULT_TEXT_VERTICAL_ALIGN_MIDDLE, DEFAULT_TEXT_VERTICAL_ALIGN_TOP, DEFAULT_TEXT_WRAP, DEFAULT_WRAP_TEXT_MAX_ROW, DateCellEditorComponent, DepartmentOutlinedPath, Direction, DragType, EditPath, FONT_SIZE_SM, FieldModelMap, GRID_CELL_EDITOR_MAP, IsSelectRecordPipe, KO_CONTAINER_TOKEN, KoComponent, KoContainer, KoShape, KoShapeTypes, KoStage, LinkCellEditorComponent, MIN_COLUMN_WIDTH, MOUSEOVER_EDIT_TYPE, MemberSettingPipe, MoreStandOutlinedPath, NumberCellEditorComponent, ProgressEditorComponent, RendererContext, RowDragPath, RowHeight, SelectCellEditorComponent, SelectOptionComponent, SelectOptionPipe, SelectOptionsPipe, SelectSettingPipe, StarFill, TextCellEditorComponent, TextMeasure, Unchecked, UserPipe, WebOutlinedPath, aiTableFragmentAttribute, applyNodeProps, buildClipboardData, buildGridData, buildGridLinearRows, castToString, compareNumber, compareString, createAITable, createActiveCellBorder, createCells, createDefaultField, createDefaultFieldName, createListener, extractLinkUrl, extractText, generateNewName, generateTargetName, getAvatarBgColor, getAvatarShortName, getCellEditorBorderSpace, getCellHorizontalPosition, getColumnIndicesSizeMap, getDefaultFieldValue, getDefaultI18nTextByKey, getDetailByTargetName, getEditorBoxOffset, getEditorSpace, getFieldOptionByField, getFieldOptions, getFieldValue, getHoverCell, getHoverEditorBoxOffset, getHoverEditorSpace, getI18nTextByKey, getMousePosition, getName, getPlaceHolderCellsConfigs, getSystemFieldValue, getTargetName, getTextWidth, getVisibleRangeInfo, handleMouseStyle, hasIntersect, idCreator, idsCreator, imageCache, isActiveCell, isArrayField, isCellMatchKeywords, isClipboardReadSupported, isClipboardReadTextSupported, isClipboardWriteSupported, isClipboardWriteTextSupported, isDateFiled, isEmpty, isMac, isNumberFiled, isSameFieldOption, isSelectedField, isSystemField, isWindows, isWindowsOS, isWithinFrozenColumnBoundary, readFromClipboard, scrollMax, setMouseStyle, shortIdCreator, shortIdsCreator, stringInclude, textDataCache, transformCellValue, updatePicture, writeToAITable, writeToClipboard, zhIntlCollator };
10046
10158
  //# sourceMappingURL=ai-table-grid.mjs.map