diff --git a/common/changes/@visactor/vtable/fix-react-update-cell_2024-07-05-09-29.json b/common/changes/@visactor/vtable/fix-react-update-cell_2024-07-05-09-29.json
new file mode 100644
index 000000000..53d1ea43d
--- /dev/null
+++ b/common/changes/@visactor/vtable/fix-react-update-cell_2024-07-05-09-29.json
@@ -0,0 +1,10 @@
+{
+ "changes": [
+ {
+ "packageName": "@visactor/vtable",
+ "comment": "fix: fix react component error in updateCell() #2038",
+ "type": "none"
+ }
+ ],
+ "packageName": "@visactor/vtable"
+}
\ No newline at end of file
diff --git a/common/changes/@visactor/vtable/fix-react-update-cell_2024-07-05-09-30.json b/common/changes/@visactor/vtable/fix-react-update-cell_2024-07-05-09-30.json
new file mode 100644
index 000000000..179cfa49e
--- /dev/null
+++ b/common/changes/@visactor/vtable/fix-react-update-cell_2024-07-05-09-30.json
@@ -0,0 +1,10 @@
+{
+ "changes": [
+ {
+ "packageName": "@visactor/vtable",
+ "comment": "feat: add renderDefault prop in react customLayout component",
+ "type": "none"
+ }
+ ],
+ "packageName": "@visactor/vtable"
+}
\ No newline at end of file
diff --git a/common/changes/@visactor/vtable/fix-react-update-cell_2024-07-05-09-31.json b/common/changes/@visactor/vtable/fix-react-update-cell_2024-07-05-09-31.json
new file mode 100644
index 000000000..2ec232b13
--- /dev/null
+++ b/common/changes/@visactor/vtable/fix-react-update-cell_2024-07-05-09-31.json
@@ -0,0 +1,10 @@
+{
+ "changes": [
+ {
+ "packageName": "@visactor/vtable",
+ "comment": "feat: support multiple columns tag in react-vtable",
+ "type": "none"
+ }
+ ],
+ "packageName": "@visactor/vtable"
+}
\ No newline at end of file
diff --git a/packages/react-vtable/README.md b/packages/react-vtable/README.md
index 71ebf936d..ef2e0a987 100644
--- a/packages/react-vtable/README.md
+++ b/packages/react-vtable/README.md
@@ -43,19 +43,19 @@ const option = {
header: [
{
field: "0",
- caption: "name",
+ title: "name",
},
{
field: "1",
- caption: "age",
+ title: "age",
},
{
field: "2",
- caption: "gender",
+ title: "gender",
},
{
field: "3",
- caption: "hobby",
+ title: "hobby",
},
],
records: new Array(1000).fill(["John", 18, "male", "🏀"]),
diff --git a/packages/react-vtable/demo/data/pivot-data.json b/packages/react-vtable/demo/data/pivot-data.json
new file mode 100644
index 000000000..3b448a39d
--- /dev/null
+++ b/packages/react-vtable/demo/data/pivot-data.json
@@ -0,0 +1,2252 @@
+[
+ {
+ "Category": "Office Supplies",
+ "Quantity": "3",
+ "City": "Aberdeen"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "25.5",
+ "City": "Aberdeen"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "6.630000114440918",
+ "City": "Aberdeen"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": null,
+ "City": "Aberdeen"
+ },
+ {
+ "Category": "Technology",
+ "Sales": null,
+ "City": "Aberdeen"
+ },
+ {
+ "Category": "Technology",
+ "Profit": null,
+ "City": "Aberdeen"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": null,
+ "City": "Aberdeen"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": null,
+ "City": "Aberdeen"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": null,
+ "City": "Aberdeen"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "2",
+ "City": "Abilene"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "1.3919999599456787",
+ "City": "Abilene"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "-3.757999897003174",
+ "City": "Abilene"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": null,
+ "City": "Abilene"
+ },
+ {
+ "Category": "Technology",
+ "Sales": null,
+ "City": "Abilene"
+ },
+ {
+ "Category": "Technology",
+ "Profit": null,
+ "City": "Abilene"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": null,
+ "City": "Abilene"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": null,
+ "City": "Abilene"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": null,
+ "City": "Abilene"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "45",
+ "City": "Akron"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "1113.3880207538605",
+ "City": "Akron"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "22.31399953365326",
+ "City": "Akron"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": "15",
+ "City": "Akron"
+ },
+ {
+ "Category": "Technology",
+ "Sales": "1183.0019836425781",
+ "City": "Akron"
+ },
+ {
+ "Category": "Technology",
+ "Profit": "-136.85100328922272",
+ "City": "Akron"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": "5",
+ "City": "Akron"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": "433.59600830078125",
+ "City": "Akron"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": "-72.09900188446045",
+ "City": "Akron"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "41",
+ "City": "Albuquerque"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "943.1220030784607",
+ "City": "Albuquerque"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "384.5169929265976",
+ "City": "Albuquerque"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": "24",
+ "City": "Albuquerque"
+ },
+ {
+ "Category": "Technology",
+ "Sales": "1277.0379962921143",
+ "City": "Albuquerque"
+ },
+ {
+ "Category": "Technology",
+ "Profit": "249.57199335098267",
+ "City": "Albuquerque"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": null,
+ "City": "Albuquerque"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": null,
+ "City": "Albuquerque"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": null,
+ "City": "Albuquerque"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "48",
+ "City": "Alexandria"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "4879.789813041687",
+ "City": "Alexandria"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "153.12399864196777",
+ "City": "Alexandria"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": "16",
+ "City": "Alexandria"
+ },
+ {
+ "Category": "Technology",
+ "Sales": "372.32000160217285",
+ "City": "Alexandria"
+ },
+ {
+ "Category": "Technology",
+ "Profit": "61.040998458862305",
+ "City": "Alexandria"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": "20",
+ "City": "Alexandria"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": "267.4600009918213",
+ "City": "Alexandria"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": "104.45199728012085",
+ "City": "Alexandria"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "8",
+ "City": "Allen"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "24.72000026702881",
+ "City": "Allen"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "-8.236999988555908",
+ "City": "Allen"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": "3",
+ "City": "Allen"
+ },
+ {
+ "Category": "Technology",
+ "Sales": "21.479999542236328",
+ "City": "Allen"
+ },
+ {
+ "Category": "Technology",
+ "Profit": "-0.26899999380111694",
+ "City": "Allen"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": "2",
+ "City": "Allen"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": "244.00599670410156",
+ "City": "Allen"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": "-31.371999740600586",
+ "City": "Allen"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "14",
+ "City": "Allentown"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "819.8760194778442",
+ "City": "Allentown"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "-233.60200348496437",
+ "City": "Allentown"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": "4",
+ "City": "Allentown"
+ },
+ {
+ "Category": "Technology",
+ "Sales": "21.72800064086914",
+ "City": "Allentown"
+ },
+ {
+ "Category": "Technology",
+ "Profit": "3.802000045776367",
+ "City": "Allentown"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": "2",
+ "City": "Allentown"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": "11.64799976348877",
+ "City": "Allentown"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": "3.3489999771118164",
+ "City": "Allentown"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "9",
+ "City": "Altoona"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "20.45099973678589",
+ "City": "Altoona"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "-1.184000015258789",
+ "City": "Altoona"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": null,
+ "City": "Altoona"
+ },
+ {
+ "Category": "Technology",
+ "Sales": null,
+ "City": "Altoona"
+ },
+ {
+ "Category": "Technology",
+ "Profit": null,
+ "City": "Altoona"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": null,
+ "City": "Altoona"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": null,
+ "City": "Altoona"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": null,
+ "City": "Altoona"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "8",
+ "City": "Amarillo"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "73.92000007629395",
+ "City": "Amarillo"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "25.271999835968018",
+ "City": "Amarillo"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": "10",
+ "City": "Amarillo"
+ },
+ {
+ "Category": "Technology",
+ "Sales": "650.5600051879883",
+ "City": "Amarillo"
+ },
+ {
+ "Category": "Technology",
+ "Profit": "94.46999931335449",
+ "City": "Amarillo"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": "14",
+ "City": "Amarillo"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": "3048.5829124450684",
+ "City": "Amarillo"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": "-507.70899391174316",
+ "City": "Amarillo"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "60",
+ "City": "Anaheim"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "3812.2500455379486",
+ "City": "Anaheim"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "824.0999948382378",
+ "City": "Anaheim"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": "7",
+ "City": "Anaheim"
+ },
+ {
+ "Category": "Technology",
+ "Sales": "422.3360061645508",
+ "City": "Anaheim"
+ },
+ {
+ "Category": "Technology",
+ "Profit": "47.869998931884766",
+ "City": "Anaheim"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": "45",
+ "City": "Anaheim"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": "3752.284019470215",
+ "City": "Anaheim"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": "362.0360040664673",
+ "City": "Anaheim"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "8",
+ "City": "Andover"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "80.94999980926514",
+ "City": "Andover"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "35.46399974822998",
+ "City": "Andover"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": null,
+ "City": "Andover"
+ },
+ {
+ "Category": "Technology",
+ "Sales": null,
+ "City": "Andover"
+ },
+ {
+ "Category": "Technology",
+ "Profit": null,
+ "City": "Andover"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": "5",
+ "City": "Andover"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": "354.8999938964844",
+ "City": "Andover"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": "88.7249984741211",
+ "City": "Andover"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "15",
+ "City": "Ann Arbor"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "240.16300201416016",
+ "City": "Ann Arbor"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "108.8680009841919",
+ "City": "Ann Arbor"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": "8",
+ "City": "Ann Arbor"
+ },
+ {
+ "Category": "Technology",
+ "Sales": "649.1100120544434",
+ "City": "Ann Arbor"
+ },
+ {
+ "Category": "Technology",
+ "Profit": "120.04700374603271",
+ "City": "Ann Arbor"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": null,
+ "City": "Ann Arbor"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": null,
+ "City": "Ann Arbor"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": null,
+ "City": "Ann Arbor"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "3",
+ "City": "Antioch"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "19.440000534057617",
+ "City": "Antioch"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "9.331000328063965",
+ "City": "Antioch"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": null,
+ "City": "Antioch"
+ },
+ {
+ "Category": "Technology",
+ "Sales": null,
+ "City": "Antioch"
+ },
+ {
+ "Category": "Technology",
+ "Profit": null,
+ "City": "Antioch"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": null,
+ "City": "Antioch"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": null,
+ "City": "Antioch"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": null,
+ "City": "Antioch"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "25",
+ "City": "Apopka"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "774.6660053730011",
+ "City": "Apopka"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "41.375",
+ "City": "Apopka"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": null,
+ "City": "Apopka"
+ },
+ {
+ "Category": "Technology",
+ "Sales": null,
+ "City": "Apopka"
+ },
+ {
+ "Category": "Technology",
+ "Profit": null,
+ "City": "Apopka"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": "6",
+ "City": "Apopka"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": "129.88800048828125",
+ "City": "Apopka"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": "12.98900032043457",
+ "City": "Apopka"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "19",
+ "City": "Apple Valley"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "251.76000690460205",
+ "City": "Apple Valley"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "59.69400119781494",
+ "City": "Apple Valley"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": "3",
+ "City": "Apple Valley"
+ },
+ {
+ "Category": "Technology",
+ "Sales": "43.17599868774414",
+ "City": "Apple Valley"
+ },
+ {
+ "Category": "Technology",
+ "Profit": "15.112000465393066",
+ "City": "Apple Valley"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": "10",
+ "City": "Apple Valley"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": "1758.0850219726562",
+ "City": "Apple Valley"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": "217.78400039672852",
+ "City": "Apple Valley"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "7",
+ "City": "Appleton"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "21.559999465942383",
+ "City": "Appleton"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "10.348999977111816",
+ "City": "Appleton"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": "5",
+ "City": "Appleton"
+ },
+ {
+ "Category": "Technology",
+ "Sales": "1649.75",
+ "City": "Appleton"
+ },
+ {
+ "Category": "Technology",
+ "Profit": "544.4169921875",
+ "City": "Appleton"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": null,
+ "City": "Appleton"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": null,
+ "City": "Appleton"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": null,
+ "City": "Appleton"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "160",
+ "City": "Arlington"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "3773.852028489113",
+ "City": "Arlington"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "349.5840001106262",
+ "City": "Arlington"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": "48",
+ "City": "Arlington"
+ },
+ {
+ "Category": "Technology",
+ "Sales": "12797.406238555908",
+ "City": "Arlington"
+ },
+ {
+ "Category": "Technology",
+ "Profit": "3805.245924949646",
+ "City": "Arlington"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": "51",
+ "City": "Arlington"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": "3643.2739720344543",
+ "City": "Arlington"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": "14.86799430847168",
+ "City": "Arlington"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "6",
+ "City": "Arlington Heights"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "14.112000465393066",
+ "City": "Arlington Heights"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "1.2350000143051147",
+ "City": "Arlington Heights"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": null,
+ "City": "Arlington Heights"
+ },
+ {
+ "Category": "Technology",
+ "Sales": null,
+ "City": "Arlington Heights"
+ },
+ {
+ "Category": "Technology",
+ "Profit": null,
+ "City": "Arlington Heights"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": null,
+ "City": "Arlington Heights"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": null,
+ "City": "Arlington Heights"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": null,
+ "City": "Arlington Heights"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "3",
+ "City": "Arvada"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "6.263999938964844",
+ "City": "Arvada"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "2.0360000133514404",
+ "City": "Arvada"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": null,
+ "City": "Arvada"
+ },
+ {
+ "Category": "Technology",
+ "Sales": null,
+ "City": "Arvada"
+ },
+ {
+ "Category": "Technology",
+ "Profit": null,
+ "City": "Arvada"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": "7",
+ "City": "Arvada"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": "497.1360054016113",
+ "City": "Arvada"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": "57.82000160217285",
+ "City": "Arvada"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "23",
+ "City": "Asheville"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "111.41599893569946",
+ "City": "Asheville"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "-7.741000175476074",
+ "City": "Asheville"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": "5",
+ "City": "Asheville"
+ },
+ {
+ "Category": "Technology",
+ "Sales": "1363.9599609375",
+ "City": "Asheville"
+ },
+ {
+ "Category": "Technology",
+ "Profit": "85.24700164794922",
+ "City": "Asheville"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": null,
+ "City": "Asheville"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": null,
+ "City": "Asheville"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": null,
+ "City": "Asheville"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "14",
+ "City": "Athens"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "198.24999332427979",
+ "City": "Athens"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "59.85999774932861",
+ "City": "Athens"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": "13",
+ "City": "Athens"
+ },
+ {
+ "Category": "Technology",
+ "Sales": "1336.0200576782227",
+ "City": "Athens"
+ },
+ {
+ "Category": "Technology",
+ "Profit": "378.4230098724365",
+ "City": "Athens"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": "3",
+ "City": "Athens"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": "186.5399932861328",
+ "City": "Athens"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": "41.03900146484375",
+ "City": "Athens"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "115",
+ "City": "Atlanta"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "11011.940182685852",
+ "City": "Atlanta"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "4833.85907793045",
+ "City": "Atlanta"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": "24",
+ "City": "Atlanta"
+ },
+ {
+ "Category": "Technology",
+ "Sales": "4333.559967041016",
+ "City": "Atlanta"
+ },
+ {
+ "Category": "Technology",
+ "Profit": "1744.5989561080933",
+ "City": "Atlanta"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": "17",
+ "City": "Atlanta"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": "1852.3399353027344",
+ "City": "Atlanta"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": "415.2059965133667",
+ "City": "Atlanta"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "2",
+ "City": "Atlantic City"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "23.360000610351562",
+ "City": "Atlantic City"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "11.680000305175781",
+ "City": "Atlantic City"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": null,
+ "City": "Atlantic City"
+ },
+ {
+ "Category": "Technology",
+ "Sales": null,
+ "City": "Atlantic City"
+ },
+ {
+ "Category": "Technology",
+ "Profit": null,
+ "City": "Atlantic City"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": null,
+ "City": "Atlantic City"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": null,
+ "City": "Atlantic City"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": null,
+ "City": "Atlantic City"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "69",
+ "City": "Auburn"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "2244.8380155563354",
+ "City": "Auburn"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "567.2389969229698",
+ "City": "Auburn"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": "4",
+ "City": "Auburn"
+ },
+ {
+ "Category": "Technology",
+ "Sales": "35.119998931884766",
+ "City": "Auburn"
+ },
+ {
+ "Category": "Technology",
+ "Profit": "9.130999565124512",
+ "City": "Auburn"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": "7",
+ "City": "Auburn"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": "875.2099986076355",
+ "City": "Auburn"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": "158.54699981212616",
+ "City": "Auburn"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "140",
+ "City": "Aurora"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "3308.089991927147",
+ "City": "Aurora"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "-392.2250031232834",
+ "City": "Aurora"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": "47",
+ "City": "Aurora"
+ },
+ {
+ "Category": "Technology",
+ "Sales": "3108.4079661369324",
+ "City": "Aurora"
+ },
+ {
+ "Category": "Technology",
+ "Profit": "291.6539967060089",
+ "City": "Aurora"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": "71",
+ "City": "Aurora"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": "5239.980005264282",
+ "City": "Aurora"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": "-2591.1669788360596",
+ "City": "Aurora"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "93",
+ "City": "Austin"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "2859.2999608516693",
+ "City": "Austin"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "-61.0089918076992",
+ "City": "Austin"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": "29",
+ "City": "Austin"
+ },
+ {
+ "Category": "Technology",
+ "Sales": "2662.998046875",
+ "City": "Austin"
+ },
+ {
+ "Category": "Technology",
+ "Profit": "75.02499508857727",
+ "City": "Austin"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": "9",
+ "City": "Austin"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": "535.6840033531189",
+ "City": "Austin"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": "-34.41000056266785",
+ "City": "Austin"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "16",
+ "City": "Avondale"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "79.03200149536133",
+ "City": "Avondale"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "7.819999933242798",
+ "City": "Avondale"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": "14",
+ "City": "Avondale"
+ },
+ {
+ "Category": "Technology",
+ "Sales": "867.7759971618652",
+ "City": "Avondale"
+ },
+ {
+ "Category": "Technology",
+ "Profit": "68.3490002155304",
+ "City": "Avondale"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": null,
+ "City": "Avondale"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": null,
+ "City": "Avondale"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": null,
+ "City": "Avondale"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "33",
+ "City": "Bakersfield"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "327.737998008728",
+ "City": "Bakersfield"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "122.97699844837189",
+ "City": "Bakersfield"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": "2",
+ "City": "Bakersfield"
+ },
+ {
+ "Category": "Technology",
+ "Sales": "201.58399963378906",
+ "City": "Bakersfield"
+ },
+ {
+ "Category": "Technology",
+ "Profit": "12.598999977111816",
+ "City": "Bakersfield"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": "12",
+ "City": "Bakersfield"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": "847.9680099487305",
+ "City": "Bakersfield"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": "52.348999977111816",
+ "City": "Bakersfield"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "120",
+ "City": "Baltimore"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "1637.9699934720993",
+ "City": "Baltimore"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "617.63499802351",
+ "City": "Baltimore"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": "18",
+ "City": "Baltimore"
+ },
+ {
+ "Category": "Technology",
+ "Sales": "503.0400085449219",
+ "City": "Baltimore"
+ },
+ {
+ "Category": "Technology",
+ "Profit": "133.524995803833",
+ "City": "Baltimore"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": "46",
+ "City": "Baltimore"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": "4015.8319511413574",
+ "City": "Baltimore"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": "925.0369925498962",
+ "City": "Baltimore"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "15",
+ "City": "Bangor"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "361.69998931884766",
+ "City": "Bangor"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "152.2040023803711",
+ "City": "Bangor"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": "12",
+ "City": "Bangor"
+ },
+ {
+ "Category": "Technology",
+ "Sales": "693.2700042724609",
+ "City": "Bangor"
+ },
+ {
+ "Category": "Technology",
+ "Profit": "236.07699584960938",
+ "City": "Bangor"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": "2",
+ "City": "Bangor"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": "109.4800033569336",
+ "City": "Bangor"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": "33.93899917602539",
+ "City": "Bangor"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "8",
+ "City": "Bartlett"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "88.95999908447266",
+ "City": "Bartlett"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "10.008000373840332",
+ "City": "Bartlett"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": null,
+ "City": "Bartlett"
+ },
+ {
+ "Category": "Technology",
+ "Sales": null,
+ "City": "Bartlett"
+ },
+ {
+ "Category": "Technology",
+ "Profit": null,
+ "City": "Bartlett"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": null,
+ "City": "Bartlett"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": null,
+ "City": "Bartlett"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": null,
+ "City": "Bartlett"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "3",
+ "City": "Bayonne"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "14.460000038146973",
+ "City": "Bayonne"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "7.085000038146973",
+ "City": "Bayonne"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": "6",
+ "City": "Bayonne"
+ },
+ {
+ "Category": "Technology",
+ "Sales": "168.94000244140625",
+ "City": "Bayonne"
+ },
+ {
+ "Category": "Technology",
+ "Profit": "72.31800079345703",
+ "City": "Bayonne"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": null,
+ "City": "Bayonne"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": null,
+ "City": "Bayonne"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": null,
+ "City": "Bayonne"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "2",
+ "City": "Baytown"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "10.368000030517578",
+ "City": "Baytown"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "3.628999948501587",
+ "City": "Baytown"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": null,
+ "City": "Baytown"
+ },
+ {
+ "Category": "Technology",
+ "Sales": null,
+ "City": "Baytown"
+ },
+ {
+ "Category": "Technology",
+ "Profit": null,
+ "City": "Baytown"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": null,
+ "City": "Baytown"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": null,
+ "City": "Baytown"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": null,
+ "City": "Baytown"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "7",
+ "City": "Beaumont"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "23.547999382019043",
+ "City": "Beaumont"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "-16.289999186992645",
+ "City": "Beaumont"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": "7",
+ "City": "Beaumont"
+ },
+ {
+ "Category": "Technology",
+ "Sales": "439.94398498535156",
+ "City": "Beaumont"
+ },
+ {
+ "Category": "Technology",
+ "Profit": "77.99399757385254",
+ "City": "Beaumont"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": "7",
+ "City": "Beaumont"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": "8.62399959564209",
+ "City": "Beaumont"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": "-2.5869998931884766",
+ "City": "Beaumont"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "23",
+ "City": "Bedford"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "72.67199897766113",
+ "City": "Bedford"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "-2.8179993629455566",
+ "City": "Bedford"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": "2",
+ "City": "Bedford"
+ },
+ {
+ "Category": "Technology",
+ "Sales": "153.58399963378906",
+ "City": "Bedford"
+ },
+ {
+ "Category": "Technology",
+ "Profit": "13.439000129699707",
+ "City": "Bedford"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": null,
+ "City": "Bedford"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": null,
+ "City": "Bedford"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": null,
+ "City": "Bedford"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "23",
+ "City": "Belleville"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "1346.069978237152",
+ "City": "Belleville"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "137.16099667549133",
+ "City": "Belleville"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": "3",
+ "City": "Belleville"
+ },
+ {
+ "Category": "Technology",
+ "Sales": "239.97000122070312",
+ "City": "Belleville"
+ },
+ {
+ "Category": "Technology",
+ "Profit": "71.99099731445312",
+ "City": "Belleville"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": "6",
+ "City": "Belleville"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": "305.5600051879883",
+ "City": "Belleville"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": "74.49799728393555",
+ "City": "Belleville"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "17",
+ "City": "Bellevue"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "83.38200044631958",
+ "City": "Bellevue"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "29.16100013256073",
+ "City": "Bellevue"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": null,
+ "City": "Bellevue"
+ },
+ {
+ "Category": "Technology",
+ "Sales": null,
+ "City": "Bellevue"
+ },
+ {
+ "Category": "Technology",
+ "Profit": null,
+ "City": "Bellevue"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": "1",
+ "City": "Bellevue"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": "20.239999771118164",
+ "City": "Bellevue"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": "7.894000053405762",
+ "City": "Bellevue"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "5",
+ "City": "Bellingham"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "25.1200008392334",
+ "City": "Bellingham"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "7.849999904632568",
+ "City": "Bellingham"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": "10",
+ "City": "Bellingham"
+ },
+ {
+ "Category": "Technology",
+ "Sales": "1099.5",
+ "City": "Bellingham"
+ },
+ {
+ "Category": "Technology",
+ "Profit": "362.8349914550781",
+ "City": "Bellingham"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": "9",
+ "City": "Bellingham"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": "2665.6201171875",
+ "City": "Bellingham"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": "239.906005859375",
+ "City": "Bellingham"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "9",
+ "City": "Bethlehem"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "1379.2440376281738",
+ "City": "Bethlehem"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "-912.4650253653526",
+ "City": "Bethlehem"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": null,
+ "City": "Bethlehem"
+ },
+ {
+ "Category": "Technology",
+ "Sales": null,
+ "City": "Bethlehem"
+ },
+ {
+ "Category": "Technology",
+ "Profit": null,
+ "City": "Bethlehem"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": "9",
+ "City": "Bethlehem"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": "310.38999938964844",
+ "City": "Bethlehem"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": "-90.63100290298462",
+ "City": "Bethlehem"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "15",
+ "City": "Beverly"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "2583.1900482177734",
+ "City": "Beverly"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "654.9190139770508",
+ "City": "Beverly"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": null,
+ "City": "Beverly"
+ },
+ {
+ "Category": "Technology",
+ "Sales": null,
+ "City": "Beverly"
+ },
+ {
+ "Category": "Technology",
+ "Profit": null,
+ "City": "Beverly"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": null,
+ "City": "Beverly"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": null,
+ "City": "Beverly"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": null,
+ "City": "Beverly"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "2",
+ "City": "Billings"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "8.288000106811523",
+ "City": "Billings"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "2.694000005722046",
+ "City": "Billings"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": null,
+ "City": "Billings"
+ },
+ {
+ "Category": "Technology",
+ "Sales": null,
+ "City": "Billings"
+ },
+ {
+ "Category": "Technology",
+ "Profit": null,
+ "City": "Billings"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": null,
+ "City": "Billings"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": null,
+ "City": "Billings"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": null,
+ "City": "Billings"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "41",
+ "City": "Bloomington"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "605.8720030784607",
+ "City": "Bloomington"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "111.08999979496002",
+ "City": "Bloomington"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": "6",
+ "City": "Bloomington"
+ },
+ {
+ "Category": "Technology",
+ "Sales": "174.66600799560547",
+ "City": "Bloomington"
+ },
+ {
+ "Category": "Technology",
+ "Profit": "45.222999572753906",
+ "City": "Bloomington"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": "11",
+ "City": "Bloomington"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": "787.2020072937012",
+ "City": "Bloomington"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": "-405.66800689697266",
+ "City": "Bloomington"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "6",
+ "City": "Boca Raton"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "44.54400062561035",
+ "City": "Boca Raton"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "-24.95400059223175",
+ "City": "Boca Raton"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": "2",
+ "City": "Boca Raton"
+ },
+ {
+ "Category": "Technology",
+ "Sales": "18.464000701904297",
+ "City": "Boca Raton"
+ },
+ {
+ "Category": "Technology",
+ "Profit": "2.308000087738037",
+ "City": "Boca Raton"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": null,
+ "City": "Boca Raton"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": null,
+ "City": "Boca Raton"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": null,
+ "City": "Boca Raton"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "1",
+ "City": "Boise"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "3.303999900817871",
+ "City": "Boise"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "1.1150000095367432",
+ "City": "Boise"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": "6",
+ "City": "Boise"
+ },
+ {
+ "Category": "Technology",
+ "Sales": "394.7460021972656",
+ "City": "Boise"
+ },
+ {
+ "Category": "Technology",
+ "Profit": "60.64499855041504",
+ "City": "Boise"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": "2",
+ "City": "Boise"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": "696.4199829101562",
+ "City": "Boise"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": "160.177001953125",
+ "City": "Boise"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "21",
+ "City": "Bolingbrook"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "69.82799959182739",
+ "City": "Bolingbrook"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "-21.364999294281006",
+ "City": "Bolingbrook"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": "2",
+ "City": "Bolingbrook"
+ },
+ {
+ "Category": "Technology",
+ "Sales": "148.47999572753906",
+ "City": "Bolingbrook"
+ },
+ {
+ "Category": "Technology",
+ "Profit": "16.70400047302246",
+ "City": "Bolingbrook"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": null,
+ "City": "Bolingbrook"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": null,
+ "City": "Bolingbrook"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": null,
+ "City": "Bolingbrook"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "14",
+ "City": "Bossier City"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "640.5200152397156",
+ "City": "Bossier City"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "67.8709990978241",
+ "City": "Bossier City"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": "6",
+ "City": "Bossier City"
+ },
+ {
+ "Category": "Technology",
+ "Sales": "646.739990234375",
+ "City": "Bossier City"
+ },
+ {
+ "Category": "Technology",
+ "Profit": "258.6960144042969",
+ "City": "Bossier City"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": "4",
+ "City": "Bossier City"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": "129.9199981689453",
+ "City": "Bossier City"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": "10.394000053405762",
+ "City": "Bossier City"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "14",
+ "City": "Bowling Green"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "798.6410026550293",
+ "City": "Bowling Green"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "72.6489987373352",
+ "City": "Bowling Green"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": "15",
+ "City": "Bowling Green"
+ },
+ {
+ "Category": "Technology",
+ "Sales": "1137.9240016937256",
+ "City": "Bowling Green"
+ },
+ {
+ "Category": "Technology",
+ "Profit": "104.98500156402588",
+ "City": "Bowling Green"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": "1",
+ "City": "Bowling Green"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": "140.80999755859375",
+ "City": "Bowling Green"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": "39.426998138427734",
+ "City": "Bowling Green"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "18",
+ "City": "Boynton Beach"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "95.43299865722656",
+ "City": "Boynton Beach"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "-10.462000489234924",
+ "City": "Boynton Beach"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": null,
+ "City": "Boynton Beach"
+ },
+ {
+ "Category": "Technology",
+ "Sales": null,
+ "City": "Boynton Beach"
+ },
+ {
+ "Category": "Technology",
+ "Profit": null,
+ "City": "Boynton Beach"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": "10",
+ "City": "Boynton Beach"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": "374.1520004272461",
+ "City": "Boynton Beach"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": "61.12200117111206",
+ "City": "Boynton Beach"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "5",
+ "City": "Bozeman"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "43.660000801086426",
+ "City": "Bozeman"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "20.80999994277954",
+ "City": "Bozeman"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": null,
+ "City": "Bozeman"
+ },
+ {
+ "Category": "Technology",
+ "Sales": null,
+ "City": "Bozeman"
+ },
+ {
+ "Category": "Technology",
+ "Profit": null,
+ "City": "Bozeman"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": null,
+ "City": "Bozeman"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": null,
+ "City": "Bozeman"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": null,
+ "City": "Bozeman"
+ },
+ {
+ "Category": "Office Supplies",
+ "Quantity": "44",
+ "City": "Brentwood"
+ },
+ {
+ "Category": "Office Supplies",
+ "Sales": "3772.8760557174683",
+ "City": "Brentwood"
+ },
+ {
+ "Category": "Office Supplies",
+ "Profit": "1045.7260146141052",
+ "City": "Brentwood"
+ },
+ {
+ "Category": "Technology",
+ "Quantity": "19",
+ "City": "Brentwood"
+ },
+ {
+ "Category": "Technology",
+ "Sales": "1293.3579940795898",
+ "City": "Brentwood"
+ },
+ {
+ "Category": "Technology",
+ "Profit": "160.57699871063232",
+ "City": "Brentwood"
+ },
+ {
+ "Category": "Furniture",
+ "Quantity": "3",
+ "City": "Brentwood"
+ },
+ {
+ "Category": "Furniture",
+ "Sales": "23.8799991607666",
+ "City": "Brentwood"
+ },
+ {
+ "Category": "Furniture",
+ "Profit": "10.506999969482422",
+ "City": "Brentwood"
+ }
+]
\ No newline at end of file
diff --git a/packages/react-vtable/demo/src/App.tsx b/packages/react-vtable/demo/src/App.tsx
index bbfe21696..270912c61 100644
--- a/packages/react-vtable/demo/src/App.tsx
+++ b/packages/react-vtable/demo/src/App.tsx
@@ -18,6 +18,7 @@ import customLayout from './component/custom-layout';
import customLayoutDom from './component/custom-layout-dom';
import customLayoutDomSite from './component/custom-layout-dom-site';
import customLayoutDomSite1 from './component/custom-layout-dom-site-1';
+import customLayoutPivot from './component/custom-layout-pivot';
// export default listEditor;
// export default listOptionRecord;
@@ -34,7 +35,8 @@ import customLayoutDomSite1 from './component/custom-layout-dom-site-1';
// export default eventRebind;
// export default componentContainer;
-export default customLayout;
+// export default customLayout;
// export default customLayoutDom;
-// export default customLayoutDomSite;
+export default customLayoutDomSite;
// export default customLayoutDomSite1;
+// export default customLayoutPivot;
diff --git a/packages/react-vtable/demo/src/component/custom-layout-dom-site.tsx b/packages/react-vtable/demo/src/component/custom-layout-dom-site.tsx
index 7191c10e9..ff8a0ff5b 100644
--- a/packages/react-vtable/demo/src/component/custom-layout-dom-site.tsx
+++ b/packages/react-vtable/demo/src/component/custom-layout-dom-site.tsx
@@ -9,6 +9,58 @@ const { Meta } = Card;
import '@arco-design/web-react/dist/css/arco.css';
+const HeaderCustomLayoutComponent = (props: CustomLayoutFunctionArg & { text: string }) => {
+ const { table, row, col, rect, text } = props;
+ if (!table || row === undefined || col === undefined) {
+ return null;
+ }
+ const { height, width } = rect || table.getCellRect(col, row);
+ const [hover, setHover] = useState(false);
+
+ const groupRef = useRef(null);
+
+ return (
+
+ {
+ // eslint-disable-next-line no-console, no-undef
+ console.log('groupRef-header', groupRef.current);
+ setHover(true);
+ event.currentTarget.stage.renderNextFrame();
+ }}
+ onMouseLeave={(event: any) => {
+ setHover(false);
+ event.currentTarget.stage.renderNextFrame();
+ }}
+ >
+ {/* {hover && (
+ // to do: component add/remove sync
+
+ )} */}
+
+ );
+};
+
const UserProfileComponent = (props: CustomLayoutFunctionArg) => {
const { table, row, col, rect, dataValue } = props;
if (!table || row === undefined || col === undefined) {
@@ -35,7 +87,7 @@ const UserProfileComponent = (props: CustomLayoutFunctionArg) => {
attribute={{
width: 190,
height: 25,
- fill: '#e6fffb',
+ // fill: '#e6fffb',
lineWidth: 1,
cornerRadius: 10,
display: 'flex',
@@ -209,17 +261,24 @@ function App() {
// defaultRowHeight={80}
onReady={table => {
// eslint-disable-next-line no-undef
- // (window as any).tableInstance = table;
+ (window as any).tableInstance = table;
}}
ReactDOM={ReactDOM}
+ rowSeriesNumber={{ title: '序号' }}
+ dragHeaderMode="column"
>
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
);
}
diff --git a/packages/react-vtable/demo/src/component/custom-layout-pivot.tsx b/packages/react-vtable/demo/src/component/custom-layout-pivot.tsx
new file mode 100644
index 000000000..8c6ae7ffe
--- /dev/null
+++ b/packages/react-vtable/demo/src/component/custom-layout-pivot.tsx
@@ -0,0 +1,219 @@
+import { useRef, useState } from 'react';
+import ReactDOM from 'react-dom/client';
+import type { CustomLayoutFunctionArg } from '../../../src';
+import {
+ PivotColumnDimension,
+ PivotRowDimension,
+ PivotIndicator,
+ PivotColumnHeaderTitle,
+ PivotTable,
+ PivotCorner,
+ Tooltip,
+ Group,
+ Text,
+ Image
+} from '../../../src';
+import pivotData from '../../data/pivot-data.json';
+import { Avatar, Button, Card, Popover, Space, Typography } from '@arco-design/web-react';
+import { IconThumbUp, IconShareInternal, IconMore } from '@arco-design/web-react/icon';
+const { Meta } = Card;
+
+import '@arco-design/web-react/dist/css/arco.css';
+
+const HeaderCustomLayoutComponent = (props: CustomLayoutFunctionArg & { text: string }) => {
+ const { table, row, col, rect, text, value } = props;
+ if (!table || row === undefined || col === undefined) {
+ return null;
+ }
+ const { height, width } = rect || table.getCellRect(col, row);
+ const [hover, setHover] = useState(false);
+
+ const groupRef = useRef(null);
+
+ return (
+
+ {
+ // eslint-disable-next-line no-console, no-undef
+ console.log('groupRef-header', groupRef.current);
+ setHover(true);
+ event.currentTarget.stage.renderNextFrame();
+ }}
+ onMouseLeave={(event: any) => {
+ setHover(false);
+ event.currentTarget.stage.renderNextFrame();
+ }}
+ >
+ {/* {hover && (
+ // to do: component add/remove sync
+
+ )} */}
+
+ );
+};
+
+const UserProfileComponent = (props: CustomLayoutFunctionArg) => {
+ const { table, row, col, rect, dataValue } = props;
+ if (!table || row === undefined || col === undefined) {
+ return null;
+ }
+ const { height, width } = rect || table.getCellRect(col, row);
+ const record = table.getRecordByCell(col, row);
+
+ const [hover, setHover] = useState(false);
+
+ return (
+
+
+ }
+ }}
+ onMouseEnter={(event: any) => {
+ setHover(true);
+ event.currentTarget.stage.renderNextFrame(); // to do: auto execute in react-vtable
+ }}
+ onMouseLeave={(event: any) => {
+ setHover(false);
+ event.currentTarget.stage.renderNextFrame();
+ }}
+ >
+
+
+
+ );
+};
+
+const CardInfo = (props: { record: any; hover: boolean; row?: number }) => {
+ // const { bloggerName, bloggerAvatar, introduction, city } = props.record;
+ const bloggerAvatar = 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg';
+ const introduction =
+ 'Hi everyone, I am Xiaohua, the virtual host. I am a little fairy who likes games, animation and food.';
+ const city = 'Dream City';
+ const bloggerName = 'Virtual Anchor Xiaohua';
+
+ return props.hover ? (
+
+
+
+ }
+ actions={[
+
+
+ ,
+
+
+ ,
+
+
+
+ ]}
+ >
+
+ {city.slice(0, 1)}
+ {city}
+
+ }
+ title={bloggerName}
+ description={introduction}
+ />
+
+ ) : null;
+};
+
+function App() {
+ return (
+ {
+ // eslint-disable-next-line no-undef
+ (window as any).tableInstance = table;
+ }}
+ >
+
+
+
+
+
+
+
+
+
+
+ {/* */}
+
+ );
+}
+
+export default App;
diff --git a/packages/react-vtable/src/components/base-component.tsx b/packages/react-vtable/src/components/base-component.tsx
index 87cb3520d..8c84f3f0b 100644
--- a/packages/react-vtable/src/components/base-component.tsx
+++ b/packages/react-vtable/src/components/base-component.tsx
@@ -1,4 +1,4 @@
-import type { ReactElement } from 'react';
+import type { ReactElement, ReactNode } from 'react';
import React, { useContext, useEffect } from 'react';
import { isEqual, isNil, pickWithout } from '@visactor/vutils';
@@ -13,7 +13,7 @@ export interface BaseComponentProps {
children?: React.ReactNode;
}
-type ComponentProps = BaseComponentProps & { updateId?: number; componentId?: number; componentIndex?: number };
+type ComponentProps = BaseComponentProps & { updateId?: number; componentId?: string; componentIndex?: number };
export const createComponent = (
componentName: string,
@@ -21,7 +21,7 @@ export const createComponent = (
supportedEvents?: Record | null,
isSingle?: boolean
) => {
- const ignoreKeys = ['id', 'updateId', 'componentId', 'componentIndex', 'children'];
+ const ignoreKeys = ['id', 'updateId', 'componentIndex', 'children'];
const notOptionKeys = supportedEvents ? Object.keys(supportedEvents).concat(ignoreKeys) : ignoreKeys;
const Comp: React.FC = (props: T) => {
@@ -65,29 +65,25 @@ export const createComponent = (
// return props.children
// ? React.cloneElement(props.children as ReactElement, { componentIndex: props.componentIndex })
// : null;
- if (props.children) {
- return React.Children.map(props.children as ReactElement, (child: ReactElement) => {
- return React.createElement(CustomLayout, { componentIndex: props.componentIndex }, child);
- });
- }
- return null;
+ // if (props.children) {
+ // return React.Children.map(props.children as ReactElement, (child: ReactElement) => {
+ // if (child.props.role === 'custom-layout' || child.props.role === 'header-custom-layout') {
+ // return React.createElement(CustomLayout, { componentId: props.componentId }, child);
+ // }
+ // });
+ // }
+ return parseCustomChildren(props.children, props.componentId);
};
Comp.displayName = componentName;
(Comp as any).parseOption = (props: T & { updateId?: number; componentId?: string }) => {
const newComponentOption: Partial = pickWithout(props, notOptionKeys);
-
// deal width customLayout
if (props.children) {
const { children } = props;
- React.Children.map(children as ReactElement, (child: ReactElement) => {
- if (child.props.role === 'custom-layout') {
- (newComponentOption as any).customLayout = 'react-custom-layout';
- }
- if (child.props.role === 'header-custom-layout') {
- (newComponentOption as any).headerCustomLayout = 'react-custom-layout';
- }
+ React.Children.map(children as ReactElement, (child: ReactElement, index) => {
+ parseChild(child, props, newComponentOption, notOptionKeys, props.componentId + '-' + index);
});
}
// if (props.children && (props.children as React.ReactElement).props.role === 'custom-layout') {
@@ -104,58 +100,50 @@ export const createComponent = (
return Comp;
};
-// const updateToContext = (
-// context: TableContextType,
-// id: string | number,
-// optionName: string,
-// isSingle: boolean,
-// props: Partial
-// ) => {
-// if (!context.optionFromChildren) {
-// return;
-// }
-
-// if (isSingle) {
-// context.optionFromChildren[optionName] = { ...props };
-// } else {
-// if (!context.optionFromChildren[optionName]) {
-// context.optionFromChildren[optionName] = [];
-// }
-
-// const comps = context.optionFromChildren[optionName];
-// const index = comps.findIndex((entry: any) => entry.id === id);
-
-// if (index >= 0) {
-// comps[index] = {
-// id,
-// ...props
-// };
-// } else {
-// context.optionFromChildren[optionName].push({
-// id,
-// ...props
-// });
-// }
-// }
-// context.isChildrenUpdated = true;
-// };
-
-// const deleteToContext = (context: TableContextType, id: string | number, optionName: string, isSingle: boolean) => {
-// if (!context.optionFromChildren) {
-// return;
-// }
-
-// if (isSingle) {
-// context.optionFromChildren[optionName] = null;
-// } else {
-// const comps = context.optionFromChildren[optionName] ?? [];
-// const index = comps.findIndex((entry: any) => entry.id === id);
-
-// if (index >= 0) {
-// const newComps = comps.slice(0, index - 1).concat(comps.slice(index + 1));
-
-// context.optionFromChildren[optionName] = newComps;
-// context.isChildrenUpdated = true;
-// }
-// }
-// };
+function parseChild(
+ child: ReactElement,
+ componentProps: any,
+ newComponentOption: any,
+ notOptionKeys: string[],
+ componentId: string
+) {
+ if (child.props.role === 'custom-layout') {
+ (newComponentOption as any).customLayout = 'react-custom-layout';
+ (newComponentOption as any).customLayoutComponentId = componentId;
+ }
+ if (child.props.role === 'header-custom-layout') {
+ (newComponentOption as any).headerCustomLayout = 'react-custom-layout';
+ (newComponentOption as any).headerCustomLayoutComponentId = componentId;
+ }
+ if ((child.type as any).displayName === 'ListColumn') {
+ if (!newComponentOption.columns) {
+ newComponentOption.columns = [];
+ }
+ const childOption = pickWithout(child.props, notOptionKeys);
+ newComponentOption.columns.push(childOption);
+ childOption.componentId = componentId;
+ if (child.props.children) {
+ React.Children.map(child.props.children as ReactElement, (child: ReactElement, index) => {
+ parseChild(child, componentProps, childOption, notOptionKeys, componentId + '-' + index);
+ });
+ }
+ }
+}
+
+function parseCustomChildren(children: ReactNode, componentId: string): ReactNode | undefined {
+ if (isReactElement(children) || Array.isArray(children)) {
+ return React.Children.map(children as ReactElement, (child: ReactElement, index) => {
+ if (child.props.children) {
+ return parseCustomChildren(child.props.children as ReactElement, componentId + '-' + index);
+ } else if (child.props.role === 'custom-layout' || child.props.role === 'header-custom-layout') {
+ return React.createElement(CustomLayout, { componentId: componentId }, child);
+ }
+ return null;
+ });
+ }
+ return null;
+}
+
+function isReactElement(obj: any) {
+ return obj && obj.$$typeof === Symbol.for('react.element');
+}
diff --git a/packages/react-vtable/src/components/custom/custom-layout.tsx b/packages/react-vtable/src/components/custom/custom-layout.tsx
index f6ef5838e..199f6642c 100644
--- a/packages/react-vtable/src/components/custom/custom-layout.tsx
+++ b/packages/react-vtable/src/components/custom/custom-layout.tsx
@@ -1,5 +1,5 @@
/* eslint-disable react-hooks/rules-of-hooks */
-import type { PropsWithChildren } from 'react';
+import type { PropsWithChildren, ReactElement } from 'react';
import React, { isValidElement, useCallback, useContext, useLayoutEffect, useRef } from 'react';
import RootTableContext from '../../context/table';
import { VRender } from '@visactor/vtable';
@@ -9,14 +9,15 @@ import { reconcilor } from './reconciler';
import { LegacyRoot } from 'react-reconciler/constants';
const { Group } = VRender;
-type CustomLayoutProps = { componentIndex?: number };
+type CustomLayoutProps = { componentId: string };
export type CustomLayoutFunctionArg = Partial & {
role?: 'custom-layout' | 'header-custom-layout';
+ renderDefault?: boolean;
};
export const CustomLayout: React.FC = (props: PropsWithChildren, ref) => {
- const { componentIndex, children } = props;
+ const { componentId, children } = props;
if (!isValidElement(children)) {
return null;
}
@@ -49,7 +50,7 @@ export const CustomLayout: React.FC = (props: PropsWithChildr
return {
rootContainer: group,
- renderDefault: false
+ renderDefault: !!children.props.renderDefault
};
},
[children]
@@ -83,15 +84,15 @@ export const CustomLayout: React.FC = (props: PropsWithChildr
console.log('update props', props, table);
table?.checkReactCustomLayout(); // init reactCustomLayout component
- if (table && !table.reactCustomLayout?.hasReactCreateGraphic(componentIndex, isHeaderCustomLayout)) {
+ if (table && !table.reactCustomLayout?.hasReactCreateGraphic(componentId, isHeaderCustomLayout)) {
table.reactCustomLayout?.setReactCreateGraphic(
- componentIndex,
+ componentId,
createGraphic,
// container.current,
isHeaderCustomLayout
); // set customLayout function
- table.reactCustomLayout?.setReactRemoveGraphic(componentIndex, removeContainer, isHeaderCustomLayout); // set customLayout function
- table.reactCustomLayout?.updateCustomCell(componentIndex, isHeaderCustomLayout); // update cell content
+ table.reactCustomLayout?.setReactRemoveGraphic(componentId, removeContainer, isHeaderCustomLayout); // set customLayout function
+ table.reactCustomLayout?.updateCustomCell(componentId, isHeaderCustomLayout); // update cell content
} else if (table) {
// update all container
container.current.forEach((value, key) => {
@@ -126,7 +127,7 @@ export const CustomLayout: React.FC = (props: PropsWithChildr
return null;
};
-function reconcilorUpdateContainer(children, currentContainer, group, args) {
+function reconcilorUpdateContainer(children: ReactElement, currentContainer: any, group: typeof Group, args: any) {
reconcilor.updateContainer(React.cloneElement(children, { ...args }), currentContainer, null);
// group = group.firstChild;
// if (isReactElement(group.attribute.html?.dom)) {
diff --git a/packages/react-vtable/src/components/list/list-column.tsx b/packages/react-vtable/src/components/list/list-column.tsx
index 699d5b7ae..87b022b50 100644
--- a/packages/react-vtable/src/components/list/list-column.tsx
+++ b/packages/react-vtable/src/components/list/list-column.tsx
@@ -1,7 +1,8 @@
+import type { ReactElement, ReactNode } from 'react';
import type { BaseComponentProps } from '../base-component';
import { createComponent } from '../base-component';
import type { ColumnDefine } from '@visactor/vtable';
-export type ListColumnProps = ColumnDefine & BaseComponentProps;
+export type ListColumnProps = Omit & BaseComponentProps;
export const ListColumn = createComponent('ListColumn', 'columns');
diff --git a/packages/vtable/src/components/react/react-custom-layout.ts b/packages/vtable/src/components/react/react-custom-layout.ts
index 4066d9092..d5067a094 100644
--- a/packages/vtable/src/components/react/react-custom-layout.ts
+++ b/packages/vtable/src/components/react/react-custom-layout.ts
@@ -1,6 +1,8 @@
import { Group } from '@src/vrender';
import type { CustomRenderFunctionArg, ICustomLayoutFuc } from '../../ts-types';
import type { BaseTableAPI } from '../../ts-types/base-table';
+import type { PivotHeaderLayoutMap } from '../../layout/pivot-header-layout';
+import type { SimpleHeaderLayoutMap } from '../../layout';
export function emptyCustomLayout(args: CustomRenderFunctionArg) {
const group = new Group({});
@@ -12,10 +14,10 @@ export function emptyCustomLayout(args: CustomRenderFunctionArg) {
export class ReactCustomLayout {
table: BaseTableAPI;
- customLayoutFuncCache: Map;
- reactRemoveGraphicCache: Map void>;
- headerCustomLayoutFuncCache: Map;
- headerReactRemoveGraphicCache: Map void>;
+ customLayoutFuncCache: Map;
+ reactRemoveGraphicCache: Map void>;
+ headerCustomLayoutFuncCache: Map;
+ headerReactRemoveGraphicCache: Map void>;
// reactContainerCache: Map>;
constructor(table: BaseTableAPI) {
this.table = table;
@@ -27,50 +29,69 @@ export class ReactCustomLayout {
this.headerReactRemoveGraphicCache = new Map();
}
- hasReactCreateGraphic(componentIndex: number, isHeaderCustomLayout?: boolean) {
+ hasReactCreateGraphic(componentId: string, isHeaderCustomLayout?: boolean) {
if (isHeaderCustomLayout) {
- return this.headerCustomLayoutFuncCache.has(componentIndex);
+ return this.headerCustomLayoutFuncCache.has(componentId);
}
- return this.customLayoutFuncCache.has(componentIndex);
+ return this.customLayoutFuncCache.has(componentId);
}
setReactCreateGraphic(
- componentIndex: number,
+ componentId: string,
createGraphic: ICustomLayoutFuc,
// containerCache: Map,
isHeaderCustomLayout?: boolean
) {
if (isHeaderCustomLayout) {
- this.headerCustomLayoutFuncCache.set(componentIndex, createGraphic);
+ this.headerCustomLayoutFuncCache.set(componentId, createGraphic);
} else {
- this.customLayoutFuncCache.set(componentIndex, createGraphic);
+ this.customLayoutFuncCache.set(componentId, createGraphic);
}
- // this.reactContainerCache.set(componentIndex, containerCache);
+ // this.reactContainerCache.set(componentId, containerCache);
}
setReactRemoveGraphic(
- componentIndex: number,
+ componentId: string,
removeGraphic: (col: number, row: number) => void,
isHeaderCustomLayout?: boolean
) {
if (isHeaderCustomLayout) {
- this.headerReactRemoveGraphicCache.set(componentIndex, removeGraphic);
+ this.headerReactRemoveGraphicCache.set(componentId, removeGraphic);
} else {
- this.reactRemoveGraphicCache.set(componentIndex, removeGraphic);
+ this.reactRemoveGraphicCache.set(componentId, removeGraphic);
}
}
- updateCustomCell(componentIndex: number, isHeaderCustomLayout?: boolean) {
+ updateCustomCell(componentId: string, isHeaderCustomLayout?: boolean) {
const table = this.table;
- const col = componentIndex;
- // to do: deal with transpose table
- if (isHeaderCustomLayout) {
- for (let row = 0; row < table.columnHeaderLevelCount; row++) {
- table.scenegraph.updateCellContent(col, row);
+ // const col = componentId;
+ // // to do: deal with transpose table
+ // if (isHeaderCustomLayout) {
+ // for (let row = 0; row < table.columnHeaderLevelCount; row++) {
+ // table.scenegraph.updateCellContent(col, row);
+ // }
+ // } else {
+ // for (let row = table.columnHeaderLevelCount; row < table.rowCount; row++) {
+ // table.scenegraph.updateCellContent(col, row);
+ // }
+ // }
+
+ if (table.isPivotTable()) {
+ const ranges = getUpdateCustomCellRangeInPivotTable(componentId, table, isHeaderCustomLayout);
+ for (let i = 0; i < ranges.length; i++) {
+ const range = ranges[i];
+ for (let col = range.start.col; col <= range.end.col; col++) {
+ for (let row = range.start.row; row <= range.end.row; row++) {
+ table.scenegraph.updateCellContent(col, row);
+ }
+ }
}
} else {
- for (let row = table.columnHeaderLevelCount; row < table.rowCount; row++) {
- table.scenegraph.updateCellContent(col, row);
+ const range = getUpdateCustomCellRangeInListTable(componentId, table, isHeaderCustomLayout);
+ for (let col = range.start.col; col <= range.end.col; col++) {
+ for (let row = range.start.row; row <= range.end.row; row++) {
+ table.scenegraph.updateCellContent(col, row);
+ }
}
}
// table.scenegraph.updateNextFrame();
@@ -78,22 +99,218 @@ export class ReactCustomLayout {
}
getCustomLayoutFunc(col: number, row: number) {
- const { startInTotal } = this.table.getBodyColumnDefine(col, row) as any;
const isHeader = this.table.isHeader(col, row);
- return (
- (isHeader ? this.headerCustomLayoutFuncCache.get(startInTotal) : this.customLayoutFuncCache.get(startInTotal)) ||
- emptyCustomLayout
- );
+ if (isHeader) {
+ const { componentId } = this.table.getHeaderDefine(col, row) as any;
+ return this.headerCustomLayoutFuncCache.get(componentId) ?? emptyCustomLayout;
+ }
+ const { componentId } = this.table.getBodyColumnDefine(col, row) as any;
+ return this.customLayoutFuncCache.get(componentId) || emptyCustomLayout;
}
removeCustomCell(col: number, row: number) {
- const { startInTotal } = this.table.getBodyColumnDefine(col, row) as any;
+ // const { startInTotal } = this.table.getBodyColumnDefine(col, row) as any;
const isHeader = this.table.isHeader(col, row);
- const removeFun = isHeader
- ? this.headerReactRemoveGraphicCache.get(startInTotal)
- : this.reactRemoveGraphicCache.get(startInTotal);
+ let removeFun;
+ if (isHeader) {
+ const define = this.table.getHeaderDefine(col, row);
+ const { componentId } = define as any;
+ removeFun = this.headerReactRemoveGraphicCache.get(componentId);
+ } else {
+ const define = this.table.getBodyColumnDefine(col, row);
+ const { componentId } = define as any;
+ removeFun = this.reactRemoveGraphicCache.get(componentId);
+ }
if (removeFun) {
removeFun(col, row);
}
}
}
+
+function getUpdateCustomCellRangeInListTable(componentId: string, table: BaseTableAPI, isHeaderCustomLayout?: boolean) {
+ const rowSeriesNumber = table.internalProps.rowSeriesNumber ? 1 : 0;
+ if (isHeaderCustomLayout) {
+ const layoutMap = table.internalProps.layoutMap as SimpleHeaderLayoutMap;
+ const { headerObjects } = table.internalProps.layoutMap;
+ let headerId: number;
+ for (let i = 0; i < headerObjects.length; i++) {
+ const headerObject = headerObjects[i];
+ if ((headerObject.define as any).componentId === componentId) {
+ headerId = headerObject.id as number;
+ break;
+ }
+ }
+
+ const startCell = layoutMap.getHeaderCellAdressById(headerId);
+ const range = layoutMap.getCellRange(startCell.col, startCell.row);
+ return range;
+
+ // let startCol = -1;
+ // let endCol = -1;
+ // let startRow = -1;
+ // let endRow = -1;
+ // for (let i = 0; i < _headerCellIds.length; i++) {
+ // const row = _headerCellIds[i];
+ // let rowHasObject = false;
+ // for (let j = 0; j < row.length; j++) {
+ // if (row[j] === headerId) {
+ // rowHasObject = true;
+ // if (transpose) {
+ // // return { col: i, row: j };
+ // startCol === -1 && (startCol = i);
+ // startRow === -1 && (startRow = j);
+ // } else {
+ // // return { col: j + this.leftRowSeriesNumberColumnCount, row: i };
+ // startCol === -1 && (startCol = j);
+ // startRow === -1 && (startRow = i);
+ // }
+ // } else {
+ // if (transpose) {
+ // // endCol === -1 && startCol !== -1 && (endCol = i);
+ // endRow === -1 && startRow !== -1 && (endRow = j - 1);
+ // } else {
+ // endCol === -1 && startCol !== -1 && (endCol = j - 1);
+ // // endRow === -1 && startRow !== -1 && (endRow = i);
+ // }
+ // }
+
+ // if (startCol !== -1 && endCol !== -1 && startRow !== -1 && endRow !== -1) {
+ // break;
+ // }
+ // }
+
+ // if (!rowHasObject) {
+ // if (transpose) {
+ // endCol === -1 && startCol !== -1 && (endCol = i - 1);
+ // } else {
+ // endRow === -1 && startRow !== -1 && (endRow = i - 1);
+ // }
+ // }
+
+ // if (startCol !== -1 && endCol !== -1 && startRow !== -1 && endRow !== -1) {
+ // break;
+ // }
+ // }
+
+ // return {
+ // startCol: startCol + rowSeriesNumber,
+ // endCol: endCol + rowSeriesNumber,
+ // startRow,
+ // endRow
+ // };
+ }
+ const { columnObjects } = table.internalProps.layoutMap;
+ for (let i = 0; i < columnObjects.length; i++) {
+ const columnObject = columnObjects[i];
+ if ((columnObject.define as any).componentId === componentId) {
+ return {
+ start: {
+ col: rowSeriesNumber + i,
+ row: table.columnHeaderLevelCount
+ },
+ end: {
+ col: rowSeriesNumber + i,
+ row: table.rowCount - 1
+ }
+ };
+ }
+ }
+
+ return {
+ start: {
+ col: 0,
+ row: 0
+ },
+ end: {
+ col: table.colCount - 1,
+ row: table.rowCount - 1
+ }
+ };
+}
+
+function getUpdateCustomCellRangeInPivotTable(
+ componentId: string,
+ table: BaseTableAPI,
+ isHeaderCustomLayout?: boolean
+) {
+ const rowSeriesNumber = table.internalProps.rowSeriesNumber ? 1 : 0;
+ const ranges = [];
+ const layoutMap = table.internalProps.layoutMap as PivotHeaderLayoutMap;
+ if (isHeaderCustomLayout) {
+ const { headerObjects } = layoutMap;
+ const headerIds: number[] = [];
+ for (let i = 0; i < headerObjects.length; i++) {
+ const headerObject = headerObjects[i];
+ if (!headerObject) {
+ continue;
+ }
+ if ((headerObject.define as any).componentId === componentId) {
+ headerIds.push(headerObject.id as number);
+ }
+ }
+
+ for (let i = 0; i < headerIds.length; i++) {
+ const headerId = headerIds[i];
+ const startCell = layoutMap.getHeaderCellAdressById(headerId);
+ const range = layoutMap.getCellRange(startCell.col, startCell.row);
+ ranges.push(range);
+ }
+ } else {
+ let columnIndex: number;
+ const { columnObjects, indicatorsAsCol } = layoutMap;
+ for (let i = 0; i < columnObjects.length; i++) {
+ const columnObject = columnObjects[i];
+ if ((columnObject.define as any).componentId === componentId) {
+ columnIndex = i;
+ break;
+ }
+ }
+
+ if (indicatorsAsCol) {
+ for (
+ let column = layoutMap.rowHeaderLevelCount + columnIndex;
+ column < layoutMap.colCount;
+ column += columnObjects.length
+ ) {
+ const range = {
+ start: {
+ col: column + rowSeriesNumber,
+ row: layoutMap.columnHeaderLevelCount
+ },
+ end: {
+ col: column + rowSeriesNumber,
+ row: layoutMap.rowCount - 1
+ }
+ };
+ ranges.push(range);
+ }
+ } else {
+ for (
+ let row = layoutMap.columnHeaderLevelCount + columnIndex;
+ row < layoutMap.rowCount;
+ row += columnObjects.length
+ ) {
+ const range = {
+ start: {
+ col: layoutMap.rowHeaderLevelCount + rowSeriesNumber,
+ row: row
+ },
+ end: {
+ col: layoutMap.colCount - 1,
+ row: row
+ }
+ };
+ ranges.push(range);
+ }
+ }
+ }
+
+ return ranges;
+
+ // return {
+ // startCol: 0,
+ // endCol: table.colCount - 1,
+ // startRow: 0,
+ // endRow: table.rowCount - 1
+ // };
+}
diff --git a/packages/vtable/src/scenegraph/component/custom.ts b/packages/vtable/src/scenegraph/component/custom.ts
index 38b484ea2..a8693912e 100644
--- a/packages/vtable/src/scenegraph/component/custom.ts
+++ b/packages/vtable/src/scenegraph/component/custom.ts
@@ -523,6 +523,9 @@ function onBeforeAttributeUpdate(val: Record, attribute: any) {
return;
}
const cellGroup = getTargetCell(graphic) as Group;
+ if (!cellGroup || cellGroup.stage) {
+ return;
+ }
const table = ((cellGroup as any).stage as any).table as BaseTableAPI;
graphic.skipAttributeUpdate = true;
const { mergeStartCol, mergeEndCol, mergeStartRow, mergeEndRow } = cellGroup;
diff --git a/packages/vtable/src/scenegraph/group-creater/cell-helper.ts b/packages/vtable/src/scenegraph/group-creater/cell-helper.ts
index 133dbec09..096646f05 100644
--- a/packages/vtable/src/scenegraph/group-creater/cell-helper.ts
+++ b/packages/vtable/src/scenegraph/group-creater/cell-helper.ts
@@ -426,10 +426,11 @@ export function updateCell(col: number, row: number, table: BaseTableAPI, addNew
colForDefine = range.start.col;
rowForDefine = range.start.row;
}
- const define =
+ const define: TextColumnDefine = (
cellLocation !== 'body'
? table.getHeaderDefine(colForDefine, rowForDefine)
- : table.getBodyColumnDefine(colForDefine, rowForDefine);
+ : table.getBodyColumnDefine(colForDefine, rowForDefine)
+ ) as any;
if (!range && (cellLocation !== 'body' || (define as TextColumnDefine)?.mergeCell)) {
// 只有表头或者column配置合并单元格后再进行信息获取
@@ -449,7 +450,12 @@ export function updateCell(col: number, row: number, table: BaseTableAPI, addNew
cellTheme.group.cornerRadius = getCellCornerRadius(col, row, table);
// fast method for text
- if (!addNew && !isMerge && canUseFastUpdate(col, row, oldCellGroup, autoWrapText, table)) {
+ if (
+ !addNew &&
+ !isMerge &&
+ !(define.customLayout || define.customRender || define.headerCustomLayout || define.headerCustomRender) &&
+ canUseFastUpdate(col, row, oldCellGroup, autoWrapText, table)
+ ) {
// update group
const cellWidth = table.getColWidth(col);
const cellHeight = table.getRowHeight(row);
@@ -678,8 +684,10 @@ function updateCellContent(
// clear react container
if (table.reactCustomLayout) {
const reactGroup = oldCellGroup.getChildByName('custom-container');
- const { col, row } = reactGroup;
- table.reactCustomLayout.removeCustomCell(col, row);
+ if (reactGroup) {
+ const { col, row } = reactGroup;
+ table.reactCustomLayout.removeCustomCell(col, row);
+ }
}
}
const newCellGroup = createCell(
diff --git a/packages/vtable/src/scenegraph/layout/update-height.ts b/packages/vtable/src/scenegraph/layout/update-height.ts
index ec86864ad..ace751454 100644
--- a/packages/vtable/src/scenegraph/layout/update-height.ts
+++ b/packages/vtable/src/scenegraph/layout/update-height.ts
@@ -187,6 +187,10 @@ export function updateCellHeight(
(cell.getChildByName(CUSTOM_CONTAINER_NAME) as Group) ||
(cell.getChildByName(CUSTOM_MERGE_CONTAINER_NAME) as Group);
if (customContainer) {
+ if (scene.table.reactCustomLayout) {
+ scene.table.reactCustomLayout.removeCustomCell(col, row);
+ }
+
let customElementsGroup;
customContainer.removeAllChild();
cell.removeChild(customContainer);
diff --git a/packages/vtable/src/scenegraph/layout/update-width.ts b/packages/vtable/src/scenegraph/layout/update-width.ts
index cc6c521f6..c4724ec23 100644
--- a/packages/vtable/src/scenegraph/layout/update-width.ts
+++ b/packages/vtable/src/scenegraph/layout/update-width.ts
@@ -346,6 +346,9 @@ function updateCellWidth(
(cell.getChildByName(CUSTOM_CONTAINER_NAME) as Group) ||
(cell.getChildByName(CUSTOM_MERGE_CONTAINER_NAME) as Group);
if (customContainer) {
+ if (scene.table.reactCustomLayout) {
+ scene.table.reactCustomLayout.removeCustomCell(col, row);
+ }
let customElementsGroup;
customContainer.removeAllChild();
cell.removeChild(customContainer);