diff --git a/demo/data.js b/demo/data.js index dc2fe88de0..44874904c2 100644 --- a/demo/data.js +++ b/demo/data.js @@ -6,7 +6,7 @@ */ export const demoData = { - version: 19, + version: 26, sheets: [ { id: "sh1", @@ -17,97 +17,111 @@ export const demoData = { cols: {}, merges: ["H2:I5", "K3:K8"], cells: { - A21: { content: "[Sheet2 => B2:](o-spreadsheet://sh2)" }, - A23: { content: "https://odoo.com" }, - A27: { content: "Emily Anderson (Emmy)" }, - A28: { content: "Sophie Allen (Saffi)" }, - A29: { content: "Chloe Adams" }, - A30: { content: "Megan Alexander (Meg)" }, - A31: { content: "Lucy Arnold (Lulu)" }, - A32: { content: "Hannah Alvarez" }, - A33: { content: "Jessica Alcock (Jess)" }, - A34: { content: "Charlotte Anaya" }, - A35: { content: "Lauren Anthony" }, - B2: { style: 1, content: "[Owl is awesome](https://github.com/odoo/owl)" }, - B4: { style: 2, content: "Numbers" }, - B9: { style: 3, content: "Formulas" }, - B14: { style: 4, content: "Errors" }, - B21: { content: "=Charts!B2" }, - B26: { content: "first dataset" }, - B27: { content: "12" }, - B28: { content: "=floor(RAND()*50)" }, - B29: { content: "=floor(RAND()*50)" }, - B30: { content: "=floor(RAND()*50)" }, - B31: { content: "=floor(RAND()*50)" }, - B32: { content: "=floor(RAND()*50)" }, - B33: { content: "=floor(RAND()*50)" }, - B34: { content: "19" }, - B35: { content: "=floor(RAND()*50)" }, - C1: { content: "CF =42" }, - C4: { content: "12.4" }, - C5: { content: "42" }, - C7: { content: "3" }, - C9: { content: "= SUM( C4:C5 )" }, - C10: { content: "=SUM(C4:C7)" }, - C11: { content: "=-(3 + C7 *SUM(C4:C7))" }, - C12: { content: "=SUM(C9:C11)" }, - C14: { content: "=C14" }, - C15: { content: "=(+" }, - C16: { content: "=C15" }, - C17: { content: "=(+)" }, - C18: { content: "=C1{C2" }, - C22: { format: 1, content: "0.43" }, - C23: { format: 2, content: "10" }, - C24: { format: 2, content: "10.123" }, - C26: { content: "second dataset" }, - C27: { content: "=floor(RAND()*50)" }, - C28: { content: "=floor(RAND()*50)" }, - C29: { content: "=floor(RAND()*50)" }, - C30: { content: "=floor(RAND()*50)" }, - C31: { content: "=floor(RAND()*50)" }, - C32: { content: "=floor(RAND()*50)" }, - C33: { content: "=floor(RAND()*50)" }, - C34: { content: "=floor(RAND()*50)" }, - C35: { content: "=floor(RAND()*50)" }, - D10: { content: "note that C7 is empty" }, - D12: { content: "this is a sum of sums" }, - F2: { style: 5, content: "italic blablah" }, - F3: { style: 6, content: "strikethrough" }, - F4: { style: 7, content: "underline" }, - G1: { content: "CF color scale:" }, - G2: { content: "5" }, - G3: { content: "8" }, - G4: { content: "9" }, - G5: { content: "15" }, - G6: { content: "22" }, - G8: { content: "30" }, - G13: { content: "=A1+A2+A3+A4+A5+A6+A7+A8+A9+A10+A11+A12+A13+A14+A15+A16+A17+A18" }, - H2: { content: "merged content" }, - H22: { content: "Col 1" }, - H23: { content: "0" }, - H24: { content: "1" }, - H25: { content: "2" }, - H26: { content: "3" }, - H27: { content: "4" }, - H28: { content: "5" }, - H29: { content: "6" }, - H30: { content: "7" }, - H31: { content: "8" }, - H32: { content: "9" }, - H33: { content: "10" }, - I22: { content: "Col 2" }, - I23: { content: "0" }, - I24: { content: "1" }, - I25: { content: "2" }, - I26: { content: "3" }, - I27: { content: "4" }, - I28: { content: "5" }, - I29: { content: "4" }, - I30: { content: "3" }, - I31: { content: "2" }, - I32: { content: "1" }, - I33: { content: "0" }, + A21: "[Sheet2 => B2:](o-spreadsheet://sh2)", + A23: "https://odoo.com", + A27: "Emily Anderson (Emmy)", + A28: "Sophie Allen (Saffi)", + A29: "Chloe Adams", + A30: "Megan Alexander (Meg)", + A31: "Lucy Arnold (Lulu)", + A32: "Hannah Alvarez", + A33: "Jessica Alcock (Jess)", + A34: "Charlotte Anaya", + A35: "Lauren Anthony", + B2: "[Owl is awesome](https://github.com/odoo/owl)", + B4: "Numbers", + B9: "Formulas", + B14: "Errors", + B21: "=Charts!B2", + B26: "first dataset", + B27: "12", + B28: "=floor(RAND()*50)", + B29: "=floor(RAND()*50)", + B30: "=floor(RAND()*50)", + B31: "=floor(RAND()*50)", + B32: "=floor(RAND()*50)", + B33: "=floor(RAND()*50)", + B34: "19", + B35: "=floor(RAND()*50)", + C1: "CF =42", + C4: "12.4", + C5: "42", + C7: "3", + C9: "= SUM( C4:C5 )", + C10: "=SUM(C4:C7)", + C11: "=-(3 + C7 *SUM(C4:C7))", + C12: "=SUM(C9:C11)", + C14: "=C14", + C15: "=(+", + C16: "=C15", + C17: "=(+)", + C18: "=C1{C2", + C22: "0.43", + C23: "10", + C24: "10.123", + C26: "second dataset", + C27: "=floor(RAND()*50)", + C28: "=floor(RAND()*50)", + C29: "=floor(RAND()*50)", + C30: "=floor(RAND()*50)", + C31: "=floor(RAND()*50)", + C32: "=floor(RAND()*50)", + C33: "=floor(RAND()*50)", + C34: "=floor(RAND()*50)", + C35: "=floor(RAND()*50)", + D10: "note that C7 is empty", + D12: "this is a sum of sums", + F2: "italic blablah", + F3: "strikethrough", + F4: "underline", + G1: "CF color scale:", + G2: "5", + G3: "8", + G4: "9", + G5: "15", + G6: "22", + G8: "30", + G13: "=A1+A2+A3+A4+A5+A6+A7+A8+A9+A10+A11+A12+A13+A14+A15+A16+A17+A18", + H2: "merged content", + H22: "Col 1", + H23: "0", + H24: "1", + H25: "2", + H26: "3", + H27: "4", + H28: "5", + H29: "6", + H30: "7", + H31: "8", + H32: "9", + H33: "10", + I22: "Col 2", + I23: "0", + I24: "1", + I25: "2", + I26: "3", + I27: "4", + I28: "5", + I29: "4", + I30: "3", + I31: "2", + I32: "1", + I33: "0", }, + styles: { + B2: 1, + B4: 2, + B9: 3, + B14: 4, + F2: 5, + F3: 6, + F4: 7, + }, + formats: { + C22: 1, + "C23:C24": 2, + }, + borders: {}, conditionalFormats: [ { id: "1", @@ -116,7 +130,9 @@ export const demoData = { values: ["42"], operator: "Equal", type: "CellIsRule", - style: { fillColor: "#FF9900" }, + style: { + fillColor: "#FF9900", + }, }, }, { @@ -124,8 +140,14 @@ export const demoData = { ranges: ["G1:G100"], rule: { type: "ColorScaleRule", - minimum: { type: "value", color: 16777215 }, - maximum: { type: "value", color: 16711680 }, + minimum: { + type: "value", + color: 16777215, + }, + maximum: { + type: "value", + color: 16711680, + }, }, }, { @@ -133,9 +155,21 @@ export const demoData = { ranges: ["H23:H33"], rule: { type: "IconSetRule", - upperInflectionPoint: { type: "percentage", value: "66", operator: "gt" }, - lowerInflectionPoint: { type: "percentage", value: "33", operator: "gt" }, - icons: { upper: "arrowGood", middle: "dotNeutral", lower: "arrowBad" }, + upperInflectionPoint: { + type: "percentage", + value: "66", + operator: "gt", + }, + lowerInflectionPoint: { + type: "percentage", + value: "33", + operator: "gt", + }, + icons: { + upper: "arrowGood", + middle: "dotNeutral", + lower: "arrowBad", + }, }, }, { @@ -143,12 +177,25 @@ export const demoData = { ranges: ["I23:I33"], rule: { type: "IconSetRule", - upperInflectionPoint: { type: "number", value: "4", operator: "ge" }, - lowerInflectionPoint: { type: "number", value: "2", operator: "ge" }, - icons: { upper: "smileyGood", middle: "smileyNeutral", lower: "smileyBad" }, + upperInflectionPoint: { + type: "number", + value: "4", + operator: "ge", + }, + lowerInflectionPoint: { + type: "number", + value: "2", + operator: "ge", + }, + icons: { + upper: "smileyGood", + middle: "smileyNeutral", + lower: "smileyBad", + }, }, }, ], + dataValidationRules: [], figures: [], tables: [ { @@ -168,213 +215,235 @@ export const demoData = { ], areGridLinesVisible: true, isVisible: true, - headerGroups: { ROW: [], COL: [] }, - dataValidationRules: [], + headerGroups: { + ROW: [], + COL: [], + }, }, { id: "sh2", name: "Charts", colNumber: 26, - rowNumber: 100, + rowNumber: 174, rows: {}, cols: {}, merges: [], cells: { - B2: { content: "42" }, - R1: { content: "Country" }, - R2: { content: "Afghanistan" }, - R3: { content: "Albania" }, - R4: { content: "Algeria" }, - R5: { content: "Angola" }, - R6: { content: "Argentina" }, - R7: { content: "Armenia" }, - R8: { content: "Australia" }, - R9: { content: "Austria" }, - R10: { content: "Azerbaijan" }, - R11: { content: "Bahamas" }, - R12: { content: "Bangladesh" }, - R13: { content: "Belarus" }, - R14: { content: "Belgium" }, - R15: { content: "Belize" }, - R16: { content: "Benin" }, - R17: { content: "Bhutan" }, - R18: { content: "Bolivia" }, - R19: { content: "Bosnia And Herz." }, - R20: { content: "Botswana" }, - R21: { content: "Brazil" }, - R22: { content: "Brunei" }, - R23: { content: "Bulgaria" }, - R24: { content: "Burkina Faso" }, - R25: { content: "Burundi" }, - R26: { content: "Cambodia" }, - R27: { content: "Cameroon" }, - R28: { content: "Canada" }, - R29: { content: "Central African Rep." }, - R30: { content: "Chad" }, - R31: { content: "Chile" }, - R32: { content: "China" }, - R33: { content: "Colombia" }, - R34: { content: "Congo" }, - R35: { content: "Costa Rica" }, - R36: { content: "Côte D'ivoire" }, - R37: { content: "Croatia" }, - R38: { content: "Cuba" }, - R39: { content: "Cyprus" }, - R40: { content: "Czechia" }, - R41: { content: "Dem. Rep. Congo" }, - R42: { content: "Denmark" }, - R43: { content: "Djibouti" }, - R44: { content: "Dominican Rep." }, - R45: { content: "Ecuador" }, - R46: { content: "Egypt" }, - R47: { content: "El Salvador" }, - R48: { content: "Eq. Guinea" }, - R49: { content: "Eritrea" }, - R50: { content: "Estonia" }, - R51: { content: "Eswatini" }, - R52: { content: "Ethiopia" }, - R53: { content: "Falkland Is." }, - R54: { content: "Fiji" }, - R55: { content: "Finland" }, - R56: { content: "France" }, - R57: { content: "Gabon" }, - R58: { content: "Gambia" }, - R59: { content: "Georgia" }, - R60: { content: "Germany" }, - R61: { content: "Ghana" }, - R62: { content: "Greece" }, - R63: { content: "Greenland" }, - R64: { content: "Guatemala" }, - R65: { content: "Guinea" }, - R66: { content: "Guinea-bissau" }, - R67: { content: "Guyana" }, - R68: { content: "Haiti" }, - R69: { content: "Honduras" }, - R70: { content: "Hungary" }, - R71: { content: "Iceland" }, - R72: { content: "India" }, - R73: { content: "Indonesia" }, - R74: { content: "Iran" }, - R75: { content: "Iraq" }, - R76: { content: "Ireland" }, - R77: { content: "Israel" }, - R78: { content: "Italy" }, - R79: { content: "Jamaica" }, - R80: { content: "Japan" }, - R81: { content: "Jordan" }, - R82: { content: "Kazakhstan" }, - R83: { content: "Kenya" }, - R84: { content: "Kosovo" }, - R85: { content: "Kuwait" }, - R86: { content: "Kyrgyzstan" }, - R87: { content: "Laos" }, - R88: { content: "Latvia" }, - R89: { content: "Lebanon" }, - R90: { content: "Lesotho" }, - R91: { content: "Liberia" }, - R92: { content: "Libya" }, - R93: { content: "Lithuania" }, - R94: { content: "Luxembourg" }, - R95: { content: "Madagascar" }, - R96: { content: "Malawi" }, - R97: { content: "Malaysia" }, - R98: { content: "Mali" }, - R99: { content: "Mauritania" }, - R100: { content: "Mexico" }, - R101: { content: "Moldova" }, - R102: { content: "Mongolia" }, - R103: { content: "Montenegro" }, - R104: { content: "Morocco" }, - R105: { content: "Mozambique" }, - R106: { content: "Myanmar" }, - R107: { content: "Namibia" }, - R108: { content: "Nepal" }, - R109: { content: "Netherlands" }, - R110: { content: "New Caledonia" }, - R111: { content: "New Zealand" }, - R112: { content: "Nicaragua" }, - R113: { content: "Niger" }, - R114: { content: "Nigeria" }, - R115: { content: "North Korea" }, - R116: { content: "North Macedonia" }, - R117: { content: "Norway" }, - R118: { content: "Oman" }, - R119: { content: "Pakistan" }, - R120: { content: "Palestine" }, - R121: { content: "Panama" }, - R122: { content: "Papua New Guinea" }, - R123: { content: "Paraguay" }, - R124: { content: "Peru" }, - R125: { content: "Philippines" }, - R126: { content: "Poland" }, - R127: { content: "Portugal" }, - R128: { content: "Puerto Rico" }, - R129: { content: "Qatar" }, - R130: { content: "Romania" }, - R131: { content: "Russia" }, - R132: { content: "Rwanda" }, - R133: { content: "S. Sudan" }, - R134: { content: "Saudi Arabia" }, - R135: { content: "Senegal" }, - R136: { content: "Serbia" }, - R137: { content: "Sierra Leone" }, - R138: { content: "Slovakia" }, - R139: { content: "Slovenia" }, - R140: { content: "Solomon Is." }, - R141: { content: "Somalia" }, - R142: { content: "South Africa" }, - R143: { content: "South Korea" }, - R144: { content: "Spain" }, - R145: { content: "Sri Lanka" }, - R146: { content: "Sudan" }, - R147: { content: "Suriname" }, - R148: { content: "Sweden" }, - R149: { content: "Switzerland" }, - R150: { content: "Syria" }, - R151: { content: "Taiwan" }, - R152: { content: "Tajikistan" }, - R153: { content: "Tanzania" }, - R154: { content: "Thailand" }, - R155: { content: "Timor-leste" }, - R156: { content: "Togo" }, - R157: { content: "Trinidad And Tobago" }, - R158: { content: "Tunisia" }, - R159: { content: "Turkey" }, - R160: { content: "Turkmenistan" }, - R161: { content: "Uganda" }, - R162: { content: "Ukraine" }, - R163: { content: "United Arab Emirates" }, - R164: { content: "United Kingdom" }, - R165: { content: "United States Of America" }, - R166: { content: "Uruguay" }, - R167: { content: "Uzbekistan" }, - R168: { content: "Vanuatu" }, - R169: { content: "Venezuela" }, - R170: { content: "Vietnam" }, - R171: { content: "W. Sahara" }, - R172: { content: "Yemen" }, - R173: { content: "Zambia" }, - R174: { content: "Zimbabwe" }, - S1: { content: "Value" }, - S2: { content: "=ROUND(RANDARRAY(ROWS(R2:R174) -1)*1000)" }, + B2: "42", + R1: "Country", + R2: "Afghanistan", + R3: "Albania", + R4: "Algeria", + R5: "Angola", + R6: "Argentina", + R7: "Armenia", + R8: "Australia", + R9: "Austria", + R10: "Azerbaijan", + R11: "Bahamas", + R12: "Bangladesh", + R13: "Belarus", + R14: "Belgium", + R15: "Belize", + R16: "Benin", + R17: "Bhutan", + R18: "Bolivia", + R19: "Bosnia And Herz.", + R20: "Botswana", + R21: "Brazil", + R22: "Brunei", + R23: "Bulgaria", + R24: "Burkina Faso", + R25: "Burundi", + R26: "Cambodia", + R27: "Cameroon", + R28: "Canada", + R29: "Central African Rep.", + R30: "Chad", + R31: "Chile", + R32: "China", + R33: "Colombia", + R34: "Congo", + R35: "Costa Rica", + R36: "Côte D'ivoire", + R37: "Croatia", + R38: "Cuba", + R39: "Cyprus", + R40: "Czechia", + R41: "Dem. Rep. Congo", + R42: "Denmark", + R43: "Djibouti", + R44: "Dominican Rep.", + R45: "Ecuador", + R46: "Egypt", + R47: "El Salvador", + R48: "Eq. Guinea", + R49: "Eritrea", + R50: "Estonia", + R51: "Eswatini", + R52: "Ethiopia", + R53: "Falkland Is.", + R54: "Fiji", + R55: "Finland", + R56: "France", + R57: "Gabon", + R58: "Gambia", + R59: "Georgia", + R60: "Germany", + R61: "Ghana", + R62: "Greece", + R63: "Greenland", + R64: "Guatemala", + R65: "Guinea", + R66: "Guinea-bissau", + R67: "Guyana", + R68: "Haiti", + R69: "Honduras", + R70: "Hungary", + R71: "Iceland", + R72: "India", + R73: "Indonesia", + R74: "Iran", + R75: "Iraq", + R76: "Ireland", + R77: "Israel", + R78: "Italy", + R79: "Jamaica", + R80: "Japan", + R81: "Jordan", + R82: "Kazakhstan", + R83: "Kenya", + R84: "Kosovo", + R85: "Kuwait", + R86: "Kyrgyzstan", + R87: "Laos", + R88: "Latvia", + R89: "Lebanon", + R90: "Lesotho", + R91: "Liberia", + R92: "Libya", + R93: "Lithuania", + R94: "Luxembourg", + R95: "Madagascar", + R96: "Malawi", + R97: "Malaysia", + R98: "Mali", + R99: "Mauritania", + R100: "Mexico", + R101: "Moldova", + R102: "Mongolia", + R103: "Montenegro", + R104: "Morocco", + R105: "Mozambique", + R106: "Myanmar", + R107: "Namibia", + R108: "Nepal", + R109: "Netherlands", + R110: "New Caledonia", + R111: "New Zealand", + R112: "Nicaragua", + R113: "Niger", + R114: "Nigeria", + R115: "North Korea", + R116: "North Macedonia", + R117: "Norway", + R118: "Oman", + R119: "Pakistan", + R120: "Palestine", + R121: "Panama", + R122: "Papua New Guinea", + R123: "Paraguay", + R124: "Peru", + R125: "Philippines", + R126: "Poland", + R127: "Portugal", + R128: "Puerto Rico", + R129: "Qatar", + R130: "Romania", + R131: "Russia", + R132: "Rwanda", + R133: "S. Sudan", + R134: "Saudi Arabia", + R135: "Senegal", + R136: "Serbia", + R137: "Sierra Leone", + R138: "Slovakia", + R139: "Slovenia", + R140: "Solomon Is.", + R141: "Somalia", + R142: "South Africa", + R143: "South Korea", + R144: "Spain", + R145: "Sri Lanka", + R146: "Sudan", + R147: "Suriname", + R148: "Sweden", + R149: "Switzerland", + R150: "Syria", + R151: "Taiwan", + R152: "Tajikistan", + R153: "Tanzania", + R154: "Thailand", + R155: "Timor-leste", + R156: "Togo", + R157: "Trinidad And Tobago", + R158: "Tunisia", + R159: "Turkey", + R160: "Turkmenistan", + R161: "Uganda", + R162: "Ukraine", + R163: "United Arab Emirates", + R164: "United Kingdom", + R165: "United States Of America", + R166: "Uruguay", + R167: "Uzbekistan", + R168: "Vanuatu", + R169: "Venezuela", + R170: "Vietnam", + R171: "W. Sahara", + R172: "Yemen", + R173: "Zambia", + R174: "Zimbabwe", + S1: "Value", + S2: "=ROUND(RANDARRAY(ROWS(R2:R174) -1)*1000)", }, + styles: {}, + formats: {}, + borders: {}, conditionalFormats: [], + dataValidationRules: [], figures: [ { id: "1", tag: "chart", width: 400, height: 300, - x: 100, - y: 100, + anchor: { + col: 1, + row: 4, + }, + offset: { + x: 0, + y: 0, + }, + fixed_position: false, data: { type: "line", dataSetsHaveTitle: true, background: "#FFFFFF", - dataSets: [{ dataRange: "Sheet1!B26:B35" }, { dataRange: "Sheet1!C26:C35" }], + dataSets: [ + { + dataRange: "Sheet1!B26:B35", + }, + { + dataRange: "Sheet1!C26:C35", + }, + ], legendPosition: "top", labelRange: "Sheet1!A27:A35", - title: { text: "Line" }, + title: { + text: "Line", + }, stacked: false, }, }, @@ -383,16 +452,33 @@ export const demoData = { tag: "chart", width: 400, height: 300, - x: 600, - y: 100, + anchor: { + col: 5, + row: 4, + }, + offset: { + x: 75, + y: 0, + }, + fixed_position: false, data: { type: "bar", dataSetsHaveTitle: false, background: "#FFFFFF", - dataSets: [{ dataRange: "Sheet1!B27:B35" }, { dataRange: "Sheet1!C27:C35" }], + dataSets: [ + { + dataRange: "Sheet1!B27:B35", + }, + { + dataRange: "Sheet1!C27:C35", + }, + ], legendPosition: "top", labelRange: "Sheet1!A27:A35", - title: { text: "Bar" }, + fixed_position: false, + title: { + text: "Bar", + }, stacked: false, }, }, @@ -401,28 +487,55 @@ export const demoData = { tag: "chart", width: 900, height: 400, - x: 100, - y: 420, + anchor: { + col: 1, + row: 17, + }, + offset: { + x: 0, + y: 10, + }, + fixed_position: false, data: { type: "pie", dataSetsHaveTitle: true, background: "#FFFFFF", - dataSets: [{ dataRange: "Sheet1!B26:B35" }, { dataRange: "Sheet1!C26:C35" }], + dataSets: [ + { + dataRange: "Sheet1!B26:B35", + }, + { + dataRange: "Sheet1!C26:C35", + }, + ], legendPosition: "top", labelRange: "Sheet1!A27:A35", - title: { text: "Pie" }, + title: { + text: "Pie", + }, }, }, { id: "4", - x: 1015, - y: 102, + anchor: { + col: 10, + row: 4, + }, + offset: { + x: 50, + y: 0, + }, + fixed_position: false, height: 296, width: 465, tag: "chart", data: { + baselineColorDown: "#EA6175", + baselineColorUp: "#43C5B1", baselineMode: "absolute", - title: { text: "Scorecard" }, + title: { + text: "Scorecard", + }, type: "scorecard", background: "#FFFFFF", baseline: "Sheet1!B28", @@ -433,21 +546,42 @@ export const demoData = { }, { id: "5", - x: 1015, - y: 420, + anchor: { + col: 10, + row: 17, + }, + offset: { + x: 50, + y: 10, + }, + fixed_position: false, height: 400, width: 465, tag: "chart", data: { background: "#FFFFFF", sectionRule: { - colors: { lowerColor: "#cc0000", middleColor: "#f1c232", upperColor: "#6aa84f" }, + colors: { + lowerColor: "#cc0000", + middleColor: "#f1c232", + upperColor: "#6aa84f", + }, rangeMin: "0", rangeMax: "100", - lowerInflectionPoint: { type: "percentage", value: "15" }, - upperInflectionPoint: { type: "percentage", value: "40" }, + lowerInflectionPoint: { + type: "percentage", + value: "15", + operator: "<=", + }, + upperInflectionPoint: { + type: "percentage", + value: "40", + operator: "<=", + }, + }, + title: { + text: "Gauge", }, - title: { text: "Gauge" }, type: "gauge", dataRange: "Sheet1!B29", }, @@ -457,22 +591,42 @@ export const demoData = { tag: "chart", width: 500, height: 300, - x: 100, - y: 850, + anchor: { + col: 1, + row: 35, + }, + offset: { + x: 0, + y: 7, + }, + fixed_position: false, data: { type: "scatter", dataSetsHaveTitle: true, background: "#FFFFFF", - dataSets: [{ dataRange: "Sheet1!C26:C35" }], + dataSets: [ + { + dataRange: "Sheet1!C26:C35", + }, + ], legendPosition: "top", labelRange: "Sheet1!B27:B35", - title: { text: "Scatter" }, + title: { + text: "Scatter", + }, }, }, { id: "7", - x: 619, - y: 850, + anchor: { + col: 6, + row: 35, + }, + offset: { + x: 35, + y: 7, + }, + fixed_position: false, width: 500, height: 300, tag: "chart", @@ -480,17 +634,35 @@ export const demoData = { type: "combo", dataSetsHaveTitle: true, background: "#FFFFFF", - dataSets: [{ dataRange: "Sheet1!B26:B35" }, { dataRange: "Sheet1!C26:C35" }], + dataSets: [ + { + dataRange: "Sheet1!B26:B35", + type: "bar", + }, + { + dataRange: "Sheet1!C26:C35", + type: "line", + }, + ], legendPosition: "top", labelRange: "Sheet1!A27:A35", - title: { text: "Combo" }, + title: { + text: "Combo", + }, aggregated: false, }, }, { id: "8", - x: 1138, - y: 850, + anchor: { + col: 1, + row: 49, + }, + offset: { + x: 0, + y: 0, + }, + fixed_position: false, width: 500, height: 300, tag: "chart", @@ -498,11 +670,20 @@ export const demoData = { type: "waterfall", dataSetsHaveTitle: true, background: "#FFFFFF", - dataSets: [{ dataRange: "Sheet1!B26:B29" }, { dataRange: "Sheet1!C26:C29" }], + dataSets: [ + { + dataRange: "Sheet1!B26:B29", + }, + { + dataRange: "Sheet1!C26:C29", + }, + ], legendPosition: "top", verticalAxisPosition: "left", labelRange: "Sheet1!A26:A29", - title: { text: "Waterfall" }, + title: { + text: "Waterfall", + }, aggregated: false, showSubTotals: true, showConnectorLines: true, @@ -510,8 +691,15 @@ export const demoData = { }, { id: "9", - x: 950, - y: 1175, + anchor: { + col: 6, + row: 49, + }, + offset: { + x: 35, + y: 0, + }, + fixed_position: false, height: 300, width: 500, tag: "chart", @@ -519,17 +707,32 @@ export const demoData = { type: "radar", dataSetsHaveTitle: true, background: "#FFFFFF", - dataSets: [{ dataRange: "Sheet1!B26:B35" }, { dataRange: "Sheet1!C26:C35" }], + dataSets: [ + { + dataRange: "Sheet1!B26:B35", + }, + { + dataRange: "Sheet1!C26:C35", + }, + ], legendPosition: "top", labelRange: "Sheet1!A27:A35", - title: { text: "Radar" }, + title: { + text: "Radar", + }, aggregated: false, }, }, { id: "10", - x: 100, - y: 1175, + anchor: { + col: 1, + row: 62, + }, + offset: { + x: 0, + y: 15, + }, width: 800, height: 600, tag: "chart", @@ -537,10 +740,16 @@ export const demoData = { type: "geo", dataSetsHaveTitle: true, background: "#FFFFFF", - dataSets: [{ dataRange: "S:S" }], + dataSets: [ + { + dataRange: "S:S", + }, + ], legendPosition: "left", labelRange: "R:R", - title: { text: "Geo" }, + title: { + text: "Geo", + }, region: "world", }, }, @@ -564,8 +773,10 @@ export const demoData = { ], areGridLinesVisible: true, isVisible: true, - headerGroups: { ROW: [], COL: [] }, - dataValidationRules: [], + headerGroups: { + ROW: [], + COL: [], + }, }, { id: "sh3", @@ -574,143 +785,95 @@ export const demoData = { rowNumber: 60, rows: {}, cols: { - 0: { size: 61 }, - 1: { size: 97 }, - 2: { size: 25 }, - 3: { size: 25 }, - 4: { size: 25 }, - 5: { size: 25 }, - 6: { size: 25 }, - 7: { size: 25 }, - 8: { size: 25 }, - 9: { size: 25 }, - 10: { size: 25 }, - 11: { size: 25 }, - 12: { size: 25 }, - 13: { size: 25 }, - 14: { size: 25 }, - 15: { size: 25 }, - 16: { size: 25 }, + 0: { + size: 61, + }, + 1: { + size: 97, + }, + 2: { + size: 25, + }, + 3: { + size: 25, + }, + 4: { + size: 25, + }, + 5: { + size: 25, + }, + 6: { + size: 25, + }, + 7: { + size: 25, + }, + 8: { + size: 25, + }, + 9: { + size: 25, + }, + 10: { + size: 25, + }, + 11: { + size: 25, + }, + 12: { + size: 25, + }, + 13: { + size: 25, + }, + 14: { + size: 25, + }, + 15: { + size: 25, + }, + 16: { + size: 25, + }, }, merges: [], cells: { - B1: { content: "won't scroll" }, - B7: { content: "scroll vertically" }, - D2: { content: "scroll horizontally" }, - D6: { style: 8 }, - D7: { style: 8 }, - D8: { style: 8 }, - D9: { style: 8 }, - D10: { style: 8 }, - D11: { style: 8 }, - D12: { style: 8 }, - D13: { style: 8 }, - D14: { style: 8 }, - D15: { style: 8 }, - D16: { style: 8 }, - D17: { style: 8 }, - E6: { style: 8 }, - E7: { style: 8 }, - E8: { style: 8 }, - E9: { style: 8 }, - E14: { style: 8 }, - E15: { style: 8 }, - E16: { style: 8 }, - E17: { style: 8 }, - F6: { style: 8 }, - F7: { style: 8 }, - F8: { style: 8 }, - F15: { style: 8 }, - F16: { style: 8 }, - F17: { style: 8 }, - G6: { style: 8 }, - G7: { style: 8 }, - G16: { style: 8 }, - G17: { style: 8 }, - H6: { style: 8 }, - H8: { style: 9 }, - H9: { style: 9 }, - H10: { style: 9 }, - H17: { style: 8 }, - I6: { style: 8 }, - I7: { style: 9 }, - I8: { style: 9 }, - I9: { style: 9 }, - I10: { style: 9 }, - I11: { style: 9 }, - I17: { style: 8 }, - J6: { style: 8 }, - J7: { style: 9 }, - J8: { style: 9 }, - J10: { style: 9 }, - J11: { style: 9 }, - J14: { style: 9 }, - J17: { style: 8 }, - K6: { style: 8 }, - K7: { style: 9 }, - K8: { style: 9 }, - K9: { style: 9 }, - K10: { style: 9 }, - K11: { style: 9 }, - K17: { style: 8 }, - L6: { style: 8 }, - L7: { style: 9 }, - L8: { style: 9 }, - L9: { style: 9 }, - L10: { style: 9 }, - L11: { style: 9 }, - L12: { style: 9 }, - L16: { style: 9 }, - L17: { style: 8 }, - M6: { style: 8 }, - M7: { style: 8 }, - M8: { style: 9 }, - M9: { style: 9 }, - M10: { style: 9 }, - M11: { style: 9 }, - M12: { style: 9 }, - M13: { style: 9 }, - M14: { style: 9 }, - M15: { style: 9 }, - M16: { style: 8 }, - M17: { style: 8 }, - N6: { style: 8 }, - N7: { style: 8 }, - N8: { style: 8 }, - N9: { style: 9 }, - N10: { style: 9 }, - N11: { style: 9 }, - N12: { style: 9 }, - N13: { style: 9 }, - N14: { style: 9 }, - N15: { style: 8 }, - N16: { style: 8 }, - N17: { style: 8 }, - O6: { style: 8 }, - O7: { style: 8 }, - O8: { style: 8 }, - O9: { style: 8 }, - O10: { style: 9 }, - O11: { style: 9 }, - O12: { style: 9 }, - O13: { style: 9 }, - O14: { style: 8 }, - O15: { style: 8 }, - O16: { style: 8 }, - O17: { style: 8 }, - P6: { style: 8 }, - P7: { style: 8 }, - P8: { style: 8 }, - P9: { style: 8 }, - P10: { style: 8 }, - P11: { style: 8 }, - P12: { style: 8 }, - P13: { style: 8 }, - P14: { style: 8 }, - P15: { style: 8 }, - P16: { style: 8 }, - P17: { style: 8 }, + B1: "won't scroll", + B7: "scroll vertically", + D2: "scroll horizontally", + }, + styles: { + "D6:D17": 8, + "E6:E9": 8, + "E14:E17": 8, + "F6:F8": 8, + "F15:F17": 8, + "G6:G7": 8, + "G16:G17": 8, + "H6:L6": 8, + "H17:L17": 8, + "M6:M7": 8, + "M16:M17": 8, + "N6:N8": 8, + "N15:N17": 8, + "O6:O9": 8, + "O14:O17": 8, + "P6:P17": 8, + "H8:H10": 9, + "I7:I11": 9, + "J7:J8": 9, + "J10:J11": 9, + J14: 9, + "K7:K11": 9, + "L7:L12": 9, + L16: 9, + "M8:M15": 9, + "N9:N14": 9, + "O10:O13": 9, }, + formats: {}, + borders: {}, conditionalFormats: [ { id: "1AAAB", @@ -719,17 +882,25 @@ export const demoData = { values: [], operator: "IsEmpty", type: "CellIsRule", - style: { fillColor: "#B6D7A8" }, + style: { + fillColor: "#B6D7A8", + }, }, }, ], + dataValidationRules: [], figures: [], tables: [], areGridLinesVisible: true, isVisible: true, - panes: { xSplit: 2, ySplit: 4 }, - headerGroups: { ROW: [], COL: [] }, - dataValidationRules: [], + panes: { + xSplit: 2, + ySplit: 4, + }, + headerGroups: { + ROW: [], + COL: [], + }, }, { id: "sh4", @@ -737,1491 +908,1733 @@ export const demoData = { colNumber: 26, rowNumber: 224, rows: {}, - color: "#6AA84F", cols: {}, merges: [], cells: { - A1: { content: "formulas" }, - A2: { content: "ABS" }, - A3: { content: "ACOS" }, - A4: { content: "ACOSH" }, - A5: { content: "ACOT" }, - A6: { content: "ACOTH" }, - A7: { content: "AND" }, - A8: { content: "ASIN" }, - A9: { content: "ASINH" }, - A10: { content: "ATAN" }, - A11: { content: "ATAN2" }, - A12: { content: "ATANH" }, - A13: { content: "AVEDEV" }, - A14: { content: "AVERAGE" }, - A15: { content: "AVERAGEA" }, - A16: { content: "AVERAGEIF" }, - A17: { content: "AVERAGEIFS" }, - A18: { content: "CEILING" }, - A19: { content: "CEILING.MATH" }, - A20: { content: "CEILING.PRECISE" }, - A21: { content: "CHAR" }, - A22: { content: "COLUMN" }, - A23: { content: "COLUMNS" }, - A24: { content: "CONCAT" }, - A25: { content: "CONCATENATE" }, - A26: { content: "COS" }, - A27: { content: "COSH" }, - A28: { content: "COT" }, - A29: { content: "COTH" }, - A30: { content: "COUNT" }, - A31: { content: "COUNTA" }, - A32: { content: "COUNTBLANK" }, - A33: { content: "COUNTIF" }, - A34: { content: "COUNTIFS" }, - A35: { content: "COVAR" }, - A36: { content: "COVARIANCE.P" }, - A37: { content: "COVARIANCE.S" }, - A38: { content: "CSC" }, - A39: { content: "CSCH" }, - A40: { content: "DATE" }, - A41: { content: "DATEVALUE" }, - A42: { content: "DAVERAGE" }, - A43: { content: "DAY" }, - A44: { content: "DAYS" }, - A45: { content: "DCOUNT" }, - A46: { content: "DCOUNTA" }, - A47: { content: "DECIMAL" }, - A48: { content: "DEGREES" }, - A49: { content: "DGET" }, - A50: { content: "DMAX" }, - A51: { content: "DMIN" }, - A52: { content: "DPRODUCT" }, - A53: { content: "DSTDEV" }, - A54: { content: "DSTDEVP" }, - A55: { content: "DSUM" }, - A56: { content: "DVAR" }, - A57: { content: "DVARP" }, - A58: { content: "EDATE" }, - A59: { content: "EOMONTH" }, - A60: { content: "EXACT" }, - A61: { content: "EXP" }, - A62: { content: "FIND" }, - A63: { content: "FLOOR" }, - A64: { content: "FLOOR.MATH" }, - A65: { content: "FLOOR.PRECISE" }, - A66: { content: "HLOOKUP" }, - A67: { content: "HOUR" }, - A68: { content: "IF" }, - A69: { content: "IFERROR" }, - A70: { content: "IFS" }, - A71: { content: "ISERROR" }, - A72: { content: "ISEVEN" }, - A73: { content: "ISLOGICAL" }, - A74: { content: "ISNONTEXT" }, - A75: { content: "ISNUMBER" }, - A76: { content: "ISO.CEILING" }, - A77: { content: "ISODD" }, - A78: { content: "ISOWEEKNUM" }, - A79: { content: "ISTEXT" }, - A80: { content: "LARGE" }, - A81: { content: "LEFT" }, - A82: { content: "LEN" }, - A83: { content: "LN" }, - A84: { content: "LOOKUP" }, - A85: { content: "LOWER" }, - A86: { content: "MATCH" }, - A87: { content: "MAX" }, - A88: { content: "MAXA" }, - A89: { content: "MAXIFS" }, - A90: { content: "MEDIAN" }, - A91: { content: "MIN" }, - A92: { content: "MINA" }, - A93: { content: "MINIFS" }, - A94: { content: "MINUTE" }, - A95: { content: "MOD" }, - A96: { content: "MONTH" }, - A97: { content: "NETWORKDAYS" }, - A98: { content: "NETWORKDAYS.INTL" }, - A99: { content: "NOT" }, - A100: { content: "NOW" }, - A101: { content: "ODD" }, - A102: { content: "OR" }, - A103: { content: "PERCENTILE" }, - A104: { content: "PERCENTILE.EXC" }, - A105: { content: "PERCENTILE.INC" }, - A106: { content: "PI" }, - A107: { content: "POWER" }, - A108: { content: "PRODUCT" }, - A109: { content: "QUARTILE" }, - A110: { content: "QUARTILE.EXC" }, - A111: { content: "QUARTILE.INC" }, - A112: { content: "RAND" }, - A113: { content: "RANDBETWEEN" }, - A114: { content: "REPLACE" }, - A115: { content: "RIGHT" }, - A116: { content: "ROUND" }, - A117: { content: "ROUNDDOWN" }, - A118: { content: "ROUNDUP" }, - A119: { content: "ROW" }, - A120: { content: "ROWS" }, - A121: { content: "SEARCH" }, - A122: { content: "SEC" }, - A123: { content: "SECH" }, - A124: { content: "SECOND" }, - A125: { content: "SIN" }, - A126: { content: "SINH" }, - A127: { content: "SMALL" }, - A128: { content: "SQRT" }, - A129: { content: "STDEV" }, - A130: { content: "STDEV.P" }, - A131: { content: "STDEV.S" }, - A132: { content: "STDEVA" }, - A133: { content: "STDEVP" }, - A134: { content: "STDEVPA" }, - A135: { content: "SUBSTITUTE" }, - A136: { content: "SUM" }, - A137: { content: "SUMIF" }, - A138: { content: "SUMIFS" }, - A139: { content: "TAN" }, - A140: { content: "TANH" }, - A141: { content: "TEXTJOIN" }, - A142: { content: "TIME" }, - A143: { content: "TIMEVALUE" }, - A144: { content: "TODAY" }, - A145: { content: "TRIM" }, - A146: { content: "TRUNC" }, - A147: { content: "UPPER" }, - A148: { content: "VAR" }, - A149: { content: "VAR.P" }, - A150: { content: "VAR.S" }, - A151: { content: "VARA" }, - A152: { content: "VARP" }, - A153: { content: "VARPA" }, - A154: { content: "VLOOKUP" }, - A155: { content: "WEEKDAY" }, - A156: { content: "WEEKNUM" }, - A157: { content: "WORKDAY" }, - A158: { content: "WORKDAY.INTL" }, - A159: { content: "XOR" }, - A160: { content: "YEAR" }, - A161: { content: "DELTA" }, - A162: { content: "NA" }, - A163: { content: "ISNA" }, - A164: { content: "ISERR" }, - A165: { content: "TEXT" }, - A166: { content: "ISBLANK" }, - A167: { content: "IFNA" }, - A168: { content: "CLEAN" }, - A169: { content: "PROPER" }, - A170: { content: "MID" }, - A171: { content: "XLOOKUP" }, - A172: { content: "ACCRINTM" }, - A173: { content: "AMORLINC" }, - A174: { content: "COUPDAYS" }, - A175: { content: "COUPDAYBS" }, - A176: { content: "COUPDAYSNC" }, - A177: { content: "COUPNCD" }, - A178: { content: "COUPNUM" }, - A179: { content: "COUPPCD" }, - A180: { content: "CUMIPMT" }, - A181: { content: "CUMPRINC" }, - A182: { content: "DB" }, - A183: { content: "DDB" }, - A184: { content: "DISC" }, - A185: { content: "DOLLARDE" }, - A186: { content: "DOLLARFR" }, - A187: { content: "DURATION" }, - A188: { content: "EFFECT" }, - A189: { content: "FV" }, - A190: { content: "FVSCHEDULE" }, - A191: { content: "INTRATE" }, - A192: { content: "IPMT" }, - A193: { content: "IRR" }, - A194: { content: "ISPMT" }, - A195: { content: "MDURATION" }, - A196: { content: "MIRR" }, - A197: { content: "NOMINAL" }, - A198: { content: "NPER" }, - A199: { content: "NPV" }, - A200: { content: "PDURATION" }, - A201: { content: "PMT" }, - A202: { content: "PPMT" }, - A203: { content: "PV" }, - A204: { content: "PRICE" }, - A205: { content: "PRICEDISC" }, - A206: { content: "PRICEMAT" }, - A207: { content: "RATE" }, - A208: { content: "RECEIVED" }, - A209: { content: "RRI" }, - A210: { content: "SLN" }, - A211: { content: "SYD" }, - A212: { content: "TBILLPRICE" }, - A213: { content: "TBILLEQ" }, - A214: { content: "TBILLYIELD" }, - A215: { content: "VDB" }, - A216: { content: "XIRR" }, - A217: { content: "XNPV" }, - A218: { content: "YIELD" }, - A219: { content: "YIELDDISC" }, - A220: { content: "YIELDMAT" }, - A221: { content: "DAYS360" }, - A222: { content: "DATEDIF" }, - A223: { content: "ADDRESS" }, - A224: { content: "INDIRECT" }, - B1: { content: "evaluation" }, - B2: { content: "=ABS(-5.5)" }, - B3: { content: "=ACOS(1)" }, - B4: { content: "=ROUND(ACOSH(2),5)" }, - B5: { content: "=ROUND(ACOT(1),5)" }, - B6: { content: "=ROUND(ACOTH(2),5)" }, - B7: { content: "=AND(TRUE,TRUE)" }, - B8: { content: "=ROUND(ASIN(0.5),5)" }, - B9: { content: "=ROUND(ASINH(2), 5)" }, - B10: { content: "=ROUND(ATAN(1),5)" }, - B11: { content: "=ROUND(ATAN2(-1,0),5)" }, - B12: { content: "=ROUND(ATANH(0.7),5)" }, - B13: { content: "=ROUND(AVEDEV(I2:I9),5)" }, - B14: { content: "=ROUND(AVERAGE(H2:H9),5)" }, - B15: { content: "=AVERAGEA(G2:H9)" }, - B16: { content: '=ROUND(AVERAGEIF(J2:J9,">150000" ),5)' }, - B17: { content: '=ROUND(AVERAGEIFS(I2:I9,H2:H9,">=30",K2:K9, "<10"),5)' }, - B18: { content: "=CEILING(20.4,1)" }, - B19: { content: "=CEILING.MATH(-5.5,1,0)" }, - B20: { content: "=CEILING.PRECISE(230, 100)" }, - B21: { content: "=CHAR(74)" }, - B22: { content: "=COLUMN(C4)" }, - B23: { content: "=COLUMNS(A5:D12)" }, - B24: { content: "=CONCAT(1,23)" }, - B25: { content: '=CONCATENATE("BUT, ", "MICHEL")' }, - B26: { content: "=ROUND(COS(PI()/3),2)" }, - B27: { content: "=ROUND(COSH(2),5)" }, - B28: { content: "=ROUND(COT(PI()/6),5)" }, - B29: { content: "=ROUND(COTH(.5),5)" }, - B30: { content: '=COUNT(1,"a","5", "03/14/2021")' }, - B31: { content: '=COUNTA(1,"a","5", "03/14/2021")' }, - B32: { content: "=COUNTBLANK(F1:G1)" }, - B33: { content: '=COUNTIF(H2:H9,">30")' }, - B34: { content: '=COUNTIFS(H2:H9, ">25",K2:K9,"<4")' }, - B35: { content: "=ROUND(COVAR(H2:H9,K2:K9),5)" }, - B36: { content: "=ROUND(COVARIANCE.P(K2:K9,H2:H9),5)" }, - B37: { content: "=ROUND(COVARIANCE.P(I2:I9,J2:J9),5)" }, - B38: { content: "=ROUND(CSC(PI()/4),5)" }, - B39: { content: "=ROUND(CSCH(pi()/3),5)" }, - B40: { content: "=DATE(2020,5,25)" }, - B41: { content: '=DATEVALUE("1969/08/15")' }, - B42: { content: '=ROUND(DAVERAGE(G1:K9,"Tot. Score",J12:J13),5)' }, - B43: { content: '=DAY("2020/03/17")' }, - B44: { content: '=DAYS("2022/03/17", "2021/03/17")' }, - B45: { content: '=DCOUNT(G1:K9,"Name",H12:H13)' }, - B46: { content: '=DCOUNTA(G1:K9,"Name",H12:H13)' }, - B47: { content: "=DECIMAL(20,16)" }, - B48: { content: "=DEGREES(pi()/4)" }, - B49: { content: '=DGET(G1:K9, "Hours Played",G12:G13)' }, - B50: { content: '=DMAX(G1:K9,"Tot. Score", I12:I13)' }, - B51: { content: '=DMIN(G1:K9,"Tot. Score", H12:H13)' }, - B52: { content: '=DPRODUCT(G1:K9, "Age",K12:K13)' }, - B53: { content: '=ROUND(DSTDEV(G1:K9, "Age",H12:H13), 5)' }, - B54: { content: '=ROUND(DSTDEVP(G1:K9, "Age",H12:H13), 5)' }, - B55: { content: '=DSUM(G1:K9,"Age",I12:I13)' }, - B56: { content: '=ROUND(DVAR(G1:K9, "Hours Played",H12:H13),5)' }, - B57: { content: '=ROUND(DVARP(G1:K9, "Hours Played",H12:H13),5)' }, - B58: { content: '=EDATE("7/22/1969", -2)' }, - B59: { content: '=EOMONTH("7/21/2020", 1)' }, - B60: { content: '=EXACT("AbsSdf%", "AbsSdf%")' }, - B61: { content: "=ROUND(EXP(4),5)" }, - B62: { content: '=FIND("A", "qbdahbaazo A")' }, - B63: { content: "=FLOOR(5.5, 2)" }, - B64: { content: "=FLOOR.MATH(-5.55,2, 1)" }, - B65: { content: "=FLOOR.PRECISE(199,100)" }, - B66: { content: '=HLOOKUP("Tot. Score",H1:K9, 4,FALSE)' }, - B67: { content: '=HOUR("2:14:56 AM")' }, - B68: { content: '=IF(TRUE,"TABOURET","JAMBON")' }, - B69: { content: '=IFERROR(0/0, "no diving by zero.")' }, - B70: { content: '=IFS($H2>$H3,"first player is older",$H3>$H2, "second player is older")' }, - B71: { content: "=ISERROR(0/0)" }, - B72: { content: "=ISEVEN(3)" }, - B73: { content: '=ISLOGICAL("TRUE")' }, - B74: { content: "=ISNONTEXT(TRUE)" }, - B75: { content: "=ISNUMBER(1231.5)" }, - B76: { content: "=ISO.CEILING(-7.89)" }, - B77: { content: "=ISODD(4)" }, - B78: { content: '=ISOWEEKNUM("1/3/2016")' }, - B79: { content: '=ISTEXT("123")' }, - B80: { content: "=LARGE(H2:H9,3)" }, - B81: { content: '=LEFT("Mich",4)' }, - B82: { content: '=LEN("anticonstitutionnellement")' }, - B83: { content: "=ROUND(LN(2),5)" }, - B84: { content: "=LOOKUP(23000,H3:J3,H5:J5)" }, - B85: { content: '=LOWER("オAドB")' }, - B86: { content: "=MATCH(42,H2:H9,0)" }, - B87: { content: "=MAX(N1:N8)" }, - B88: { content: "=MAXA(N1:N8)" }, - B89: { content: '=MAXIFS(H2:H9,K2:K9, "<20",K2:K9, "<>4")' }, - B90: { content: "=MEDIAN(-1, 6, 7, 234, 163845)" }, - B91: { content: "=MIN(N1:N8)" }, - B92: { content: "=MINA(N1:N8)" }, - B93: { content: '=MINIFS(J2:J9,H2:H9, ">20")' }, - B94: { content: "=MINUTE(0.126)" }, - B95: { content: "=MOD(42,12)" }, - B96: { content: '=MONTH("5/2/1954")' }, - B97: { content: '=NETWORKDAYS("1/1/2013", "2/1/2013")' }, - B98: { content: '=NETWORKDAYS.INTL("1/1/2013", "2/1/2013", "0000111")' }, - B99: { content: "=NOT(FALSE)" }, - B100: { content: "=NOW()" }, - B101: { content: "=ODD(4)" }, - B102: { content: '=OR("true", FALSE)' }, - B103: { content: "=PERCENTILE(N1:N5,1)" }, - B104: { content: "=PERCENTILE.EXC(N1:N5,0.5)" }, - B105: { content: "=PERCENTILE.INC(N1:N5,0)" }, - B106: { content: "=ROUND(PI(), 5)" }, - B107: { content: "=POWER(42,2)" }, - B108: { content: "=PRODUCT(1,2,3)" }, - B109: { content: "=QUARTILE(N1:N5, 0)" }, - B110: { content: "=ROUND(QUARTILE.EXC(N1:N5, 1),5)" }, - B111: { content: "=QUARTILE.INC(N1:N5 ,4)" }, - B112: { content: "=RAND()" }, - B113: { content: "=RANDBETWEEN(1.1,2)" }, - B114: { content: '=REPLACE("ABZ", 2, 1, "Y")' }, - B115: { content: '=RIGHT("kikou", 2)' }, - B116: { content: "=ROUND(49.9)" }, - B117: { content: "=ROUNDDOWN(42, -1)" }, - B118: { content: "=ROUNDUP(-1.6,0)" }, - B119: { content: "=ROW(A234)" }, - B120: { content: "=ROWS(B3:C40)" }, - B121: { content: '=SEARCH("C", "ABCD")' }, - B122: { content: "=ROUND(SEC(PI()/3),5)" }, - B123: { content: "=ROUND(SECH(1), 5)" }, - B124: { content: '=SECOND("0:21:42")' }, - B125: { content: "=ROUND(SIN(PI()/6),5)" }, - B126: { content: "=ROUND(SINH(1),5)" }, - B127: { content: "=SMALL(H2:H9, 3)" }, - B128: { content: "=SQRT(4)" }, - B129: { content: "=STDEV(-2,0,2)" }, - B130: { content: "=STDEV.P(2,4)" }, - B131: { content: "=STDEV.S(2,4,6)" }, - B132: { content: "=STDEVA(TRUE, 3,5)" }, - B133: { content: "=ROUND(STDEVP(2,5,8),2)" }, - B134: { content: "=ROUND(STDEVPA(TRUE, 4,7),2)" }, - B135: { content: '=SUBSTITUTE("SAP is best", "SAP", "Odoo")' }, - B136: { content: "=SUM(1,2,3,4,5)" }, - B137: { content: '=SUMIF(K2:K9, "<100")' }, - B138: { content: '=SUMIFS(H2:H9,K2:K9, "<100")' }, - B139: { content: "=ROUND(TAN(PI()/4),5)" }, - B140: { content: "=ROUND(TANH(1),5)" }, - B141: { content: '=TEXTJOIN("-",TRUE,"","1","A","%")' }, - B142: { content: "=TIME(9,11,31)" }, - B143: { content: '=TIMEVALUE("1899 10 08 18:00")' }, - B144: { content: "=TODAY()" }, - B145: { content: '=TRIM(" Jean Ticonstitutionnalise ")' }, - B146: { content: "=TRUNC(42.42, 1)" }, - B147: { content: '=UPPER("grrrr !")' }, - B148: { content: "=ROUND(VAR(K1:K5),5)" }, - B149: { content: "=ROUND(VAR.P(K1:K5),5)" }, - B150: { content: "=VAR.S(2,5,8)" }, - B151: { content: "=ROUND(VARA(K1:K5),5)" }, - B152: { content: "=ROUND(VARP(K1:K5),5)" }, - B153: { content: "=ROUND(VARPA(K1:K5),5)" }, - B154: { content: '=VLOOKUP("NotACheater",G1:K9, 3, FALSE)' }, - B155: { content: '=WEEKDAY("6/12/2021")' }, - B156: { content: '=WEEKNUM("6/29/2021")' }, - B157: { content: '=WORKDAY("3/15/2021", 6)' }, - B158: { content: '=WORKDAY.INTL("3/15/2021", 6, "0111111")' }, - B159: { content: "=XOR(false, true, false, false)" }, - B160: { content: '=YEAR("3/12/2012")' }, - B161: { content: "=DELTA(1,1)" }, - B162: { content: "=NA()" }, - B163: { content: "=ISNA(NA())" }, - B164: { content: "=ISERR(NA())" }, - B165: { content: '=TEXT(5, "#,##0.00")' }, - B166: { content: "=ISBLANK(E166)" }, - B167: { content: '=IFNA(NA(), "hello")' }, - B168: { content: '=CLEAN("a"&CHAR(10))' }, - B169: { content: '=PROPER("this is a sentence")' }, - B170: { content: '=MID("Odoo", 2, 5)' }, - B171: { content: '=XLOOKUP("robot4", G2:G9, H2:H9)' }, - B172: { content: '=ACCRINTM("01/01/2020", "01/01/2021", 0.1, 100, 0)' }, - B173: { content: '=AMORLINC(50, "01/01/2021", "06/01/2021", 5, 1, 0.1)' }, - B174: { content: '=COUPDAYS("01/01/2021", "01/01/2022", 1, 0)' }, - B175: { content: '=COUPDAYBS("01/01/2021", "01/01/2022", 1, 0)' }, - B176: { content: '=COUPDAYSNC("01/01/2021", "01/01/2022", 1, 0)' }, - B177: { content: '=COUPNCD("01/01/2021", "01/01/2022", 1, 0)' }, - B178: { content: '=COUPNUM("01/01/2021", "01/01/2022", 1, 0)' }, - B179: { content: '=COUPPCD("01/01/2021", "01/01/2022", 1, 0)' }, - B180: { content: "=CUMIPMT(0.1, 12, 100, 1, 1, 1)" }, - B181: { content: "=CUMPRINC(0.1, 12, 100, 1, 1, 1)" }, - B182: { content: "=DB(50, 5, 12, 1, 1)" }, - B183: { content: "=DDB(50, 5, 12, 1, 2)" }, - B184: { content: '=DISC("01/01/2021", "01/01/2022", 50, 100, 0)' }, - B185: { content: "=DOLLARDE(10.25, 8)" }, - B186: { content: "=DOLLARFR(10.12, 8)" }, - B187: { content: '=DURATION("01/01/2021", "01/01/2022", 0.1, 50, 1, 0)' }, - B188: { content: "=EFFECT(0.1, 12)" }, - B189: { content: "=FV(0.1, 12, -10, 100, 1)" }, - B190: { content: "=FVSCHEDULE(100, I25:I27)" }, - B191: { content: '=INTRATE("01/01/2021", "01/01/2022", 100, 100, 0)' }, - B192: { content: "=IPMT(0.1, 1, 12, 100, 5, 1)" }, - B193: { content: "=IRR(H25:H27, 0.1)" }, - B194: { content: "=ISPMT(0.1, 1, 12, 100)" }, - B195: { content: '=MDURATION("01/01/2021", "01/01/2022", 0.1, 50, 1, 0)' }, - B196: { content: "=MIRR(H25:H27, 0.12, 0.1)" }, - B197: { content: "=NOMINAL(0.12, 12)" }, - B198: { content: "=NPER(0.1, -10, 100, 5, 1)" }, - B199: { content: "=NPV(0.1, 50, 60)" }, - B200: { content: "=PDURATION(0.1, 100, 5)" }, - B201: { content: "=PMT(0.1, 12, 100, 5, 1)" }, - B202: { content: "=PPMT(0.1, 1, 12, 100, 5, 1)" }, - B203: { content: "=PV(0.1, 12, -10, 5, 1)" }, - B204: { content: '=PRICE("01/01/2021", "01/01/2022", 0.1, 50, 100, 1, 0)' }, - B205: { content: '=PRICEDISC("01/01/2021", "01/01/2022", 0.1, 100, 0)' }, - B206: { content: '=PRICEMAT("01/01/2021", "01/01/2022", "01/01/2020", 0.1, 50, 0)' }, - B207: { content: "=RATE(12, -10, 100, 5, 1, 0.1)" }, - B208: { content: '=RECEIVED("01/01/2021", "01/01/2022", 100, 0.1, 0)' }, - B209: { content: "=RRI(12, 100, 5)" }, - B210: { content: "=SLN(50, 5, 12)" }, - B211: { content: "=SYD(50, 5, 12, 1)" }, - B212: { content: '=TBILLPRICE("01/01/2021", "01/01/2022", 0.1)' }, - B213: { content: '=TBILLEQ("01/01/2021", "01/01/2022", 0.1)' }, - B214: { content: '=TBILLYIELD("01/01/2021", "01/01/2022", 50)' }, - B215: { content: "=VDB(50, 5, 12, 1, 2, 2, 0)" }, - B216: { content: "=XIRR(H25:H27, J25:J27, 0.1)" }, - B217: { content: "=XNPV(0.1, H25:H27, J25:J27)" }, - B218: { content: '=YIELD("01/01/2021", "01/01/2022", 0.1, 50, 100, 1, 0)' }, - B219: { content: '=YIELDDISC("01/01/2021", "01/01/2022", 50, 100, 0)' }, - B220: { content: '=YIELDMAT("01/01/2021", "01/01/2022", "01/01/2020", 0.1, 50, 0)' }, - B221: { content: '=DAYS360("01/01/2020", "12/31/2020")' }, - B222: { content: '=DATEDIF("2001/09/15", "2003/06/10", "MD")' }, - B223: { content: '=ADDRESS(27, 53, 1, TRUE, "sheet!")' }, - B224: { content: '=INDIRECT("A1")' }, - C1: { content: "expected value" }, - C2: { content: "5.5" }, - C3: { content: "0" }, - C4: { content: "1.31696" }, - C5: { content: "0.7854" }, - C6: { content: "0.54931" }, - C7: { content: "TRUE" }, - C8: { content: "0.5236" }, - C9: { content: "1.44364" }, - C10: { content: "0.7854" }, - C11: { content: "3.14159" }, - C12: { content: "0.8673" }, - C13: { content: "2959.1625" }, - C14: { content: "26.25" }, - C15: { content: "13.125" }, - C16: { content: "222797" }, - C17: { content: "8376.65" }, - C18: { content: "21" }, - C19: { content: "-5" }, - C20: { content: "300" }, - C21: { content: "J" }, - C22: { content: "3" }, - C23: { content: "4" }, - C24: { content: '="123"' }, - C25: { content: "BUT, MICHEL" }, - C26: { content: "0.5" }, - C27: { content: "3.7622" }, - C28: { content: "=ROUND(SQRT(3),5)" }, - C29: { content: "2.16395" }, - C30: { content: "2" }, - C31: { content: "4" }, - C32: { content: "1" }, - C33: { content: "2" }, - C34: { content: "3" }, - C35: { content: "-2119.25" }, - C36: { content: "-2119.25" }, - C37: { content: "237217364.71641" }, - C38: { content: "=ROUND(SQRT(2),5)" }, - C39: { content: "0.80041" }, - C40: { format: 3, content: "43976" }, - C41: { content: "25430" }, - C42: { content: "151434.625" }, - C43: { content: "17" }, - C44: { content: "365" }, - C45: { content: "0" }, - C46: { content: "3" }, - C47: { content: "32" }, - C48: { content: "45" }, - C49: { content: "252.4" }, - C50: { content: "=J7" }, - C51: { content: "=J9" }, - C52: { content: "333" }, - C53: { content: "6.02771" }, - C54: { content: "4.92161" }, - C55: { content: "101" }, - C56: { content: "17560207.92333" }, - C57: { content: "11706805.28222" }, - C58: { format: 3, content: "25345" }, - C59: { format: 3, content: "44074" }, - C60: { content: "TRUE" }, - C61: { content: "54.59815" }, - C62: { content: "12" }, - C63: { content: "4" }, - C64: { content: "-4" }, - C65: { content: "100" }, - C66: { content: "110120.5" }, - C67: { content: "2" }, - C68: { content: "TABOURET" }, - C69: { content: "no diving by zero." }, - C70: { content: "first player is older" }, - C71: { content: "TRUE" }, - C72: { content: "FALSE" }, - C73: { content: "FALSE" }, - C74: { content: "TRUE" }, - C75: { content: "TRUE" }, - C76: { content: "-7" }, - C77: { content: "FALSE" }, - C78: { content: "53" }, - C79: { content: "TRUE" }, - C80: { content: "30" }, - C81: { content: "Mich" }, - C82: { content: "25" }, - C83: { content: "0.69315" }, - C84: { content: "50024" }, - C85: { content: "オaドb" }, - C86: { content: "4" }, - C87: { content: "0.6" }, - C88: { content: "1" }, - C89: { content: "30" }, - C90: { content: "7" }, - C91: { content: "0.1" }, - C92: { content: "0" }, - C93: { content: "5000" }, - C94: { content: "1" }, - C95: { content: "6" }, - C96: { content: "5" }, - C97: { content: "24" }, - C98: { content: "19" }, - C99: { content: "TRUE" }, - C101: { content: "5" }, - C102: { content: "TRUE" }, - C103: { content: "0.6" }, - C104: { content: "0.4" }, - C105: { content: "0.1" }, - C106: { content: "3.14159" }, - C107: { content: "1764" }, - C108: { content: "6" }, - C109: { content: "0.1" }, - C110: { content: "0.15" }, - C111: { content: "0.6" }, - C113: { content: "2" }, - C114: { content: "AYZ" }, - C115: { content: "ou" }, - C116: { content: "50" }, - C117: { content: "40" }, - C118: { content: "-2" }, - C119: { content: "234" }, - C120: { content: "38" }, - C121: { content: "3" }, - C122: { content: "2" }, - C123: { content: "0.64805" }, - C124: { content: "42" }, - C125: { content: "0.5" }, - C126: { content: "1.1752" }, - C127: { content: "26" }, - C128: { content: "2" }, - C129: { content: "2" }, - C130: { content: "1" }, - C131: { content: "2" }, - C132: { content: "2" }, - C133: { content: "2.45" }, - C134: { content: "2.45" }, - C135: { content: "Odoo is best" }, - C136: { content: "15" }, - C137: { content: "52" }, - C138: { content: "201" }, - C139: { content: "1" }, - C140: { content: "0.76159" }, - C141: { content: "1-A-%" }, - C142: { format: 4, content: "0.3829976851851852" }, - C143: { content: "0.75" }, - C145: { content: "Jean Ticonstitutionnalise" }, - C146: { content: "42.4" }, - C147: { content: "GRRRR !" }, - C148: { content: "2.91667" }, - C149: { content: "2.1875" }, - C150: { content: "9" }, - C151: { content: "6.7" }, - C152: { content: "2.1875" }, - C153: { content: "5.36" }, - C154: { content: "=252.4" }, - C155: { content: "7" }, - C156: { content: "27" }, - C157: { format: 3, content: "44278" }, - C158: { format: 3, content: "44312" }, - C159: { content: "TRUE" }, - C160: { content: "2012" }, - C161: { content: "1" }, - C162: { content: "#N/A" }, - C163: { content: "TRUE" }, - C164: { content: "FALSE" }, - C165: { content: '="5.00"' }, - C166: { content: "=TRUE" }, - C167: { content: '="hello"' }, - C168: { content: '="a"' }, - C169: { content: '="This Is A Sentence"' }, - C170: { content: '="doo"' }, - C171: { content: "=42" }, - C172: { content: "10" }, - C173: { content: "5" }, - C174: { content: "360" }, - C175: { content: "0" }, - C176: { content: "360" }, - C177: { content: "44562" }, - C178: { content: "1" }, - C179: { content: "44197" }, - C180: { content: "0" }, - C181: { content: "-13.34211955" }, - C182: { content: "0.729166667" }, - C183: { content: "8.333333333" }, - C184: { content: "0.5" }, - C185: { content: "10.3125" }, - C186: { content: "10.096" }, - C187: { content: "1" }, - C188: { content: "0.104713067" }, - C189: { content: "-78.61571623" }, - C190: { content: "158.125" }, - C191: { content: "0" }, - C192: { content: "0" }, - C193: { content: "0.421954446" }, - C194: { content: "-9.166666667" }, - C195: { content: "0.019607843" }, - C196: { content: "-0.060608807" }, - C197: { content: "0.113865515" }, - C198: { content: "25.62524843" }, - C199: { content: "95.04132231" }, - C200: { content: "-31.43139883" }, - C201: { content: "-13.55468008" }, - C202: { content: "-13.55468008" }, - C203: { content: "73.35745596" }, - C204: { content: "2.156862745" }, - C205: { content: "90" }, - C206: { content: "-7.647058824" }, - C207: { content: "0.027937424" }, - C208: { content: "111.1111111" }, - C209: { content: "-0.220922192" }, - C210: { content: "3.75" }, - C211: { content: "6.923076923" }, - C212: { content: "89.86111111" }, - C213: { content: "0.109813678" }, - C214: { content: "0.98630137" }, - C215: { content: "6.944444444" }, - C216: { content: "0.420899528" }, - C217: { content: "-404.5918575" }, - C218: { content: "1.2" }, - C219: { content: "1" }, - C220: { content: "1" }, - C221: { content: "360" }, - C222: { content: "26" }, - C223: { content: "'sheet!'!$BA$27" }, - C224: { content: "=A1" }, - D1: { content: "is it compatible ?" }, - D2: { content: "=IF(B2=C2,1, 0)" }, - D3: { content: "=IF(B3=C3,1, 0)" }, - D4: { content: "=IF(B4=C4,1, 0)" }, - D5: { content: "=IF(B5=C5,1, 0)" }, - D6: { content: "=IF(B6=C6,1, 0)" }, - D7: { content: "=IF(B7=C7,1, 0)" }, - D8: { content: "=IF(B8=C8,1, 0)" }, - D9: { content: "=IF(B9=C9,1, 0)" }, - D10: { content: "=IF(B10=C10,1, 0)" }, - D11: { content: "=IF(B11=C11,1, 0)" }, - D12: { content: "=IF(B12=C12,1, 0)" }, - D13: { content: "=IF(B13=C13,1, 0)" }, - D14: { content: "=IF(B14=C14,1, 0)" }, - D15: { content: "=IF(B15=C15,1, 0)" }, - D16: { content: "=IF(B16=C16,1, 0)" }, - D17: { content: "=IF(B17=C17,1, 0)" }, - D18: { content: "=IF(B18=C18,1, 0)" }, - D19: { content: "=IF(B19=C19,1, 0)" }, - D20: { content: "=IF(B20=C20,1, 0)" }, - D21: { content: "=IF(B21=C21,1, 0)" }, - D22: { content: "=IF(B22=C22,1, 0)" }, - D23: { content: "=IF(B23=C23,1, 0)" }, - D24: { content: "=IF(B24=C24,1, 0)" }, - D25: { content: "=IF(B25=C25,1, 0)" }, - D26: { content: "=IF(B26=C26,1, 0)" }, - D27: { content: "=IF(B27=C27,1, 0)" }, - D28: { content: "=IF(B28=C28,1, 0)" }, - D29: { content: "=IF(B29=C29,1, 0)" }, - D30: { content: "=IF(B30=C30,1, 0)" }, - D31: { content: "=IF(B31=C31,1, 0)" }, - D32: { content: "=IF(B32=C32,1, 0)" }, - D33: { content: "=IF(B33=C33,1, 0)" }, - D34: { content: "=IF(B34=C34,1, 0)" }, - D35: { content: "=IF(B35=C35,1, 0)" }, - D36: { content: "=IF(B36=C36,1, 0)" }, - D37: { content: "=IF(B37=C37,1, 0)" }, - D38: { content: "=IF(B38=C38,1, 0)" }, - D39: { content: "=IF(B39=C39,1, 0)" }, - D40: { content: "=IF(B40=C40,1, 0)" }, - D41: { content: "=IF(B41=C41,1, 0)" }, - D42: { content: "=IF(B42=C42,1, 0)" }, - D43: { content: "=IF(B43=C43,1, 0)" }, - D44: { content: "=IF(B44=C44,1, 0)" }, - D45: { content: "=IF(B45=C45,1, 0)" }, - D46: { content: "=IF(B46=C46,1, 0)" }, - D47: { content: "=IF(B47=C47,1, 0)" }, - D48: { content: "=IF(B48=C48,1, 0)" }, - D49: { content: "=IF(B49=C49,1, 0)" }, - D50: { content: "=IF(B50=C50,1, 0)" }, - D51: { content: "=IF(B51=C51,1, 0)" }, - D52: { content: "=IF(B52=C52,1, 0)" }, - D53: { content: "=IF(B53=C53,1, 0)" }, - D54: { content: "=IF(B54=C54,1, 0)" }, - D55: { content: "=IF(B55=C55,1, 0)" }, - D56: { content: "=IF(B56=C56,1, 0)" }, - D57: { content: "=IF(B57=C57,1, 0)" }, - D58: { content: "=IF(B58=C58,1, 0)" }, - D59: { content: "=IF(B59=C59,1, 0)" }, - D60: { content: "=IF(B60=C60,1, 0)" }, - D61: { content: "=IF(B61=C61,1, 0)" }, - D62: { content: "=IF(B62=C62,1, 0)" }, - D63: { content: "=IF(B63=C63,1, 0)" }, - D64: { content: "=IF(B64=C64,1, 0)" }, - D65: { content: "=IF(B65=C65,1, 0)" }, - D66: { content: "=IF(B66=C66,1, 0)" }, - D67: { content: "=IF(B67=C67,1, 0)" }, - D68: { content: "=IF(B68=C68,1, 0)" }, - D69: { content: "=IF(B69=C69,1, 0)" }, - D70: { content: "=IF(B70=C70,1, 0)" }, - D71: { content: "=IF(B71=C71,1, 0)" }, - D72: { content: "=IF(B72=C72,1, 0)" }, - D73: { content: "=IF(B73=C73,1, 0)" }, - D74: { content: "=IF(B74=C74,1, 0)" }, - D75: { content: "=IF(B75=C75,1, 0)" }, - D76: { content: "=IF(B76=C76,1, 0)" }, - D77: { content: "=IF(B77=C77,1, 0)" }, - D78: { content: "=IF(B78=C78,1, 0)" }, - D79: { content: "=IF(B79=C79,1, 0)" }, - D80: { content: "=IF(B80=C80,1, 0)" }, - D81: { content: "=IF(B81=C81,1, 0)" }, - D82: { content: "=IF(B82=C82,1, 0)" }, - D83: { content: "=IF(B83=C83,1, 0)" }, - D84: { content: "=IF(B84=C84,1, 0)" }, - D85: { content: "=IF(B85=C85,1, 0)" }, - D86: { content: "=IF(B86=C86,1, 0)" }, - D87: { content: "=IF(B87=C87,1, 0)" }, - D88: { content: "=IF(B88=C88,1, 0)" }, - D89: { content: "=IF(B89=C89,1, 0)" }, - D90: { content: "=IF(B90=C90,1, 0)" }, - D91: { content: "=IF(B91=C91,1, 0)" }, - D92: { content: "=IF(B92=C92,1, 0)" }, - D93: { content: "=IF(B93=C93,1, 0)" }, - D94: { content: "=IF(B94=C94,1, 0)" }, - D95: { content: "=IF(B95=C95,1, 0)" }, - D96: { content: "=IF(B96=C96,1, 0)" }, - D97: { content: "=IF(B97=C97,1, 0)" }, - D98: { content: "=IF(B98=C98,1, 0)" }, - D99: { content: "=IF(B99=C99,1, 0)" }, - D100: { content: "=IF(ISNUMBER(B100),1, 0)" }, - D101: { content: "=IF(B101=C101,1, 0)" }, - D102: { content: "=IF(B102=C102,1, 0)" }, - D103: { content: "=IF(B103=C103,1, 0)" }, - D104: { content: "=IF(B104=C104,1, 0)" }, - D105: { content: "=IF(B105=C105,1, 0)" }, - D106: { content: "=IF(B106=C106,1, 0)" }, - D107: { content: "=IF(B107=C107,1, 0)" }, - D108: { content: "=IF(B108=C108,1, 0)" }, - D109: { content: "=IF(B109=C109,1, 0)" }, - D110: { content: "=IF(B110=C110,1, 0)" }, - D111: { content: "=IF(B111=C111,1, 0)" }, - D112: { content: "=IF(AND(B112>=0,B112<1 ),1, 0)" }, - D113: { content: "=IF(B113=C113,1, 0)" }, - D114: { content: "=IF(B114=C114,1, 0)" }, - D115: { content: "=IF(B115=C115,1, 0)" }, - D116: { content: "=IF(B116=C116,1, 0)" }, - D117: { content: "=IF(B117=C117,1, 0)" }, - D118: { content: "=IF(B118=C118,1, 0)" }, - D119: { content: "=IF(B119=C119,1, 0)" }, - D120: { content: "=IF(B120=C120,1, 0)" }, - D121: { content: "=IF(B121=C121,1, 0)" }, - D122: { content: "=IF(B122=C122,1, 0)" }, - D123: { content: "=IF(B123=C123,1, 0)" }, - D124: { content: "=IF(B124=C124,1, 0)" }, - D125: { content: "=IF(B125=C125,1, 0)" }, - D126: { content: "=IF(B126=C126,1, 0)" }, - D127: { content: "=IF(B127=C127,1, 0)" }, - D128: { content: "=IF(B128=C128,1, 0)" }, - D129: { content: "=IF(B129=C129,1, 0)" }, - D130: { content: "=IF(B130=C130,1, 0)" }, - D131: { content: "=IF(B131=C131,1, 0)" }, - D132: { content: "=IF(B132=C132,1, 0)" }, - D133: { content: "=IF(B133=C133,1, 0)" }, - D134: { content: "=IF(B134=C134,1, 0)" }, - D135: { content: "=IF(B135=C135,1, 0)" }, - D136: { content: "=IF(B136=C136,1, 0)" }, - D137: { content: "=IF(B137=C137,1, 0)" }, - D138: { content: "=IF(B138=C138,1, 0)" }, - D139: { content: "=IF(B139=C139,1, 0)" }, - D140: { content: "=IF(B140=C140,1, 0)" }, - D141: { content: "=IF(B141=C141,1, 0)" }, - D142: { content: "=IF(B142=C142,1, 0)" }, - D143: { content: "=IF(B143=C143,1, 0)" }, - D144: { content: "=IF(ISNUMBER(B144),1, 0)" }, - D145: { content: "=IF(B145=C145,1, 0)" }, - D146: { content: "=IF(B146=C146,1, 0)" }, - D147: { content: "=IF(B147=C147,1, 0)" }, - D148: { content: "=IF(B148=C148,1, 0)" }, - D149: { content: "=IF(B149=C149,1, 0)" }, - D150: { content: "=IF(B150=C150,1, 0)" }, - D151: { content: "=IF(B151=C151,1, 0)" }, - D152: { content: "=IF(B152=C152,1, 0)" }, - D153: { content: "=IF(B153=C153,1, 0)" }, - D154: { content: "=IF(B154=C154,1, 0)" }, - D155: { content: "=IF(B155=C155,1, 0)" }, - D156: { content: "=IF(B156=C156,1, 0)" }, - D157: { content: "=IF(B157=C157,1, 0)" }, - D158: { content: "=IF(B158=C158,1, 0)" }, - D159: { content: "=IF(B159=C159,1, 0)" }, - D160: { content: "=IF(B160=C160,1, 0)" }, - D161: { content: "=IF(B161=C161,1, 0)" }, - D162: { content: "=IF(ISNA(B162),1, 0)" }, - D163: { content: "=IF(B163=C163,1, 0)" }, - D164: { content: "=IF(B164=C164,1, 0)" }, - D165: { content: "=IF(B165=C165,1, 0)" }, - D166: { content: "=IF(B166=C166,1, 0)" }, - D167: { content: "=IF(B167=C167,1, 0)" }, - D168: { content: "=IF(B168=C168,1, 0)" }, - D169: { content: "=IF(B169=C169,1, 0)" }, - D170: { content: "=IF(B170=C170,1, 0)" }, - D171: { content: "=IF(B171=C171,1, 0)" }, - D172: { content: "=IF(B172=C172, 1, 0)" }, - D173: { content: "=IF(B173=C173, 1, 0)" }, - D174: { content: "=IF(B174=C174, 1, 0)" }, - D175: { content: "=IF(B175=C175, 1, 0)" }, - D176: { content: "=IF(B176=C176, 1, 0)" }, - D177: { content: "=IF(B177=C177, 1, 0)" }, - D178: { content: "=IF(B178=C178, 1, 0)" }, - D179: { content: "=IF(B179=C179, 1, 0)" }, - D180: { content: "=IF(B180=C180, 1, 0)" }, - D181: { content: "=IF(FLOOR(B181, 0.0001)=FLOOR(C181, 0.0001), 1, 0)" }, - D182: { content: "=IF(FLOOR(B182, 0.0001)=FLOOR(C182, 0.0001), 1, 0)" }, - D183: { content: "=IF(FLOOR(B183, 0.0001)=FLOOR(C183, 0.0001), 1, 0)" }, - D184: { content: "=IF(FLOOR(B184, 0.0001)=FLOOR(C184, 0.0001), 1, 0)" }, - D185: { content: "=IF(FLOOR(B185, 0.0001)=FLOOR(C185, 0.0001), 1, 0)" }, - D186: { content: "=IF(FLOOR(B186, 0.0001)=FLOOR(C186, 0.0001), 1, 0)" }, - D187: { content: "=IF(B187=C187, 1, 0)" }, - D188: { content: "=IF(FLOOR(B188, 0.0001)=FLOOR(C188, 0.0001), 1, 0)" }, - D189: { content: "=IF(FLOOR(B189, 0.0001)=FLOOR(C189, 0.0001), 1, 0)" }, - D190: { content: "=IF(FLOOR(B190, 0.0001)=FLOOR(C190, 0.0001), 1, 0)" }, - D191: { content: "=IF(B191=C191, 1, 0)" }, - D192: { content: "=IF(B192=C192, 1, 0)" }, - D193: { content: "=IF(FLOOR(B193, 0.0001)=FLOOR(C193, 0.0001), 1, 0)" }, - D194: { content: "=IF(FLOOR(B194, 0.0001)=FLOOR(C194, 0.0001), 1, 0)" }, - D195: { content: "=IF(FLOOR(B195, 0.0001)=FLOOR(C195, 0.0001), 1, 0)" }, - D196: { content: "=IF(FLOOR(B196, 0.0001)=FLOOR(C196, 0.0001), 1, 0)" }, - D197: { content: "=IF(FLOOR(B197, 0.0001)=FLOOR(C197, 0.0001), 1, 0)" }, - D198: { content: "=IF(FLOOR(B198, 0.0001)=FLOOR(C198, 0.0001), 1, 0)" }, - D199: { content: "=IF(FLOOR(B199, 0.0001)=FLOOR(C199, 0.0001), 1, 0)" }, - D200: { content: "=IF(FLOOR(B200, 0.0001)=FLOOR(C200, 0.0001), 1, 0)" }, - D201: { content: "=IF(FLOOR(B201, 0.0001)=FLOOR(C201, 0.0001), 1, 0)" }, - D202: { content: "=IF(FLOOR(B202, 0.0001)=FLOOR(C202, 0.0001), 1, 0)" }, - D203: { content: "=IF(FLOOR(B203, 0.0001)=FLOOR(C203, 0.0001), 1, 0)" }, - D204: { content: "=IF(FLOOR(B204, 0.0001)=FLOOR(C204, 0.0001), 1, 0)" }, - D205: { content: "=IF(B205=C205, 1, 0)" }, - D206: { content: "=IF(FLOOR(B206, 0.0001)=FLOOR(C206, 0.0001), 1, 0)" }, - D207: { content: "=IF(FLOOR(B207, 0.0001)=FLOOR(C207, 0.0001), 1, 0)" }, - D208: { content: "=IF(FLOOR(B208, 0.0001)=FLOOR(C208, 0.0001), 1, 0)" }, - D209: { content: "=IF(FLOOR(B209, 0.0001)=FLOOR(C209, 0.0001), 1, 0)" }, - D210: { content: "=IF(FLOOR(B210, 0.0001)=FLOOR(C210, 0.0001), 1, 0)" }, - D211: { content: "=IF(FLOOR(B211, 0.0001)=FLOOR(C211, 0.0001), 1, 0)" }, - D212: { content: "=IF(FLOOR(B212, 0.0001)=FLOOR(C212, 0.0001), 1, 0)" }, - D213: { content: "=IF(FLOOR(B213, 0.0001)=FLOOR(C213, 0.0001), 1, 0)" }, - D214: { content: "=IF(FLOOR(B214, 0.0001)=FLOOR(C214, 0.0001), 1, 0)" }, - D215: { content: "=IF(FLOOR(B215, 0.0001)=FLOOR(C215, 0.0001), 1, 0)" }, - D216: { content: "=IF(FLOOR(B216, 0.0001)=FLOOR(C216, 0.0001), 1, 0)" }, - D217: { content: "=IF(FLOOR(B217, 0.0001)=FLOOR(C217, 0.0001), 1, 0)" }, - D218: { content: "=IF(FLOOR(B218, 0.0001)=FLOOR(C218, 0.0001), 1, 0)" }, - D219: { content: "=IF(B219=C219, 1, 0)" }, - D220: { content: "=IF(B220=C220, 1, 0)" }, - D221: { content: "=IF(B221=C221, 1, 0)" }, - D222: { content: "=IF(B222=C222, 1, 0)" }, - D223: { content: "=IF(B223=C223, 1, 0)" }, - D224: { content: "=IF(B224=C224, 1, 0)" }, - G1: { style: 10, content: "Name" }, - G2: { content: "Robot1" }, - G3: { content: "Robot2" }, - G4: { content: "NotACheater" }, - G5: { content: "Robot4" }, - G6: { content: "Robot3" }, - G7: { content: "Robot6" }, - G8: { content: "Michel" }, - G9: { content: "Robot7" }, - G11: { content: "criteria" }, - G12: { style: 10, content: "Name" }, - G13: { content: "NotACheater" }, - H1: { style: 10, content: "Age" }, - H2: { content: "26" }, - H3: { content: "13" }, - H4: { content: "26" }, - H5: { content: "42" }, - H6: { content: "9" }, - H7: { content: "27" }, - H8: { content: "30" }, - H9: { content: "37" }, - H12: { style: 10, content: "Age" }, - H13: { content: ">29" }, - H24: { style: 10, content: "Cashflows" }, - H25: { content: "1000" }, - H26: { content: "-1000" }, - H27: { content: "-600" }, - H34: { content: "UNIQUE", border: 2 }, - H38: { content: "EXPAND", border: 2 }, - H42: { content: "FILTER", border: 2 }, - H46: { content: "TRANSPOSE", border: 2 }, - H50: { content: "MUNIT", border: 2 }, - H54: { content: "FLATTEN", border: 2 }, - H60: { content: "FREQUENCY", border: 2 }, - H64: { content: "ARRAY.CONSTRAIN", border: 2 }, - H68: { content: "CHOOSECOLS", border: 2 }, - H72: { content: "CHOOSEROWS", border: 2 }, - H76: { content: "SUMPRODUCT", border: 2 }, - H80: { content: "MINVERSE", border: 2 }, - H84: { content: "MDETERM", border: 2 }, - H88: { content: "MMULT", border: 2 }, - H92: { content: "SUMX2MY2", border: 2 }, - H96: { content: "SUMX2PY2", border: 2 }, - H100: { content: "SUMXMY2", border: 2 }, - H104: { content: "TOCOL", border: 2 }, - H110: { content: "TOROW", border: 2 }, - H114: { content: "SPLIT", border: 2 }, - H117: { content: "HSTACK", border: 2 }, - H121: { content: "VSTACK", border: 2 }, - H127: { content: "WRAPCOLS", border: 2 }, - H132: { content: "WRAPROWS", border: 2 }, - I1: { style: 10, content: "Hours Played" }, - I2: { content: "1204.7" }, - I3: { content: "500.9" }, - I4: { content: "252.4" }, - I5: { content: "4701.3" }, - I6: { content: "12.1" }, - I7: { content: "4000" }, - I8: { content: "12052" }, - I9: { content: "4890.1" }, - I12: { style: 10, content: "Hours Played" }, - I13: { content: "<4500" }, - I24: { style: 10, content: "Rates" }, - I25: { content: "0.1" }, - I26: { content: "0.15" }, - I27: { content: "0.25" }, - I34: { content: "=IF(AND(L34=N34, M34=O34), 1, 0)", border: 4 }, - I38: { content: "=IF(AND(K38=M38, L38=N38, K39=M39, L39=N39), 1, 0)", border: 4 }, - I42: { content: "=IF(AND(L42=N42, M42=O42), 1, 0)", border: 4 }, - I46: { content: "=IF(AND(L46=N46, M46=O46, L47=N47, M47=O47), 1, 0)", border: 4 }, - I50: { content: "=IF(AND(J50=L50, K50=M50, J51=L51, K51=M51), 1, 0)", border: 4 }, - I54: { content: "=IF(AND(L54=M54, L55=M55, L56=M56, L57=M57), 1, 0)", border: 4 }, - I60: { content: "=IF(AND(L60=M60, L61=M61), 1, 0)", border: 4 }, - I64: { content: "=IF(AND(L64=M64, L65=M65), 1, 0)", border: 4 }, - I68: { content: "=IF(AND(L68=M68, L69=M69), 1, 0)", border: 4 }, - I72: { content: "=IF(AND(L72=N72, M72=O72), 1, 0)", border: 4 }, - I76: { content: "=IF(AND(L76=M76), 1, 0)", border: 4 }, - I80: { content: "=IF(AND(L80=N80, M80=O80, L81=N81, M81=O81), 1, 0)", border: 4 }, - I84: { content: "=IF(AND(L84=M84), 1, 0)", border: 4 }, - I88: { content: "=IF(AND(L88=N88, M88=O88, L89=N89, M89=O89), 1, 0)", border: 4 }, - I92: { content: "=IF(AND(L92=M92), 1, 0)", border: 4 }, - I96: { content: "=IF(AND(L96=M96), 1, 0)", border: 4 }, - I100: { content: "=IF(AND(L100=M100), 1, 0)", border: 4 }, - I104: { content: "=IF(AND(L104=M104, L105=M105, L106=M106, L107=M107), 1, 0)", border: 4 }, - I110: { content: "=IF(AND(L110=P110, M110=Q110, N110=R110, O110=S110), 1, 0)", border: 4 }, - I114: { content: "=IF(AND(K114=O114, L114=P114, M114=Q114, N114=R114), 1, 0)", border: 4 }, - I117: { content: "=IF(AND(L117=P117, M117=Q117, N117=R117, O117=S117), 1, 0)", border: 4 }, - I121: { content: "=IF(AND(L121=M121, L122=M122, L123=M123, L124=M124), 1, 0)", border: 4 }, - I127: { content: "=IF(AND(K127=L127, K128=L128, K129=L129), 1, 0)", border: 4 }, - I132: { content: "=IF(AND(K132=N132, L132=O132, M132=P132), 1, 0)", border: 4 }, - J1: { style: 10, content: "Tot. Score" }, - J2: { content: "25618" }, - J3: { content: "23000" }, - J4: { content: "110120.5" }, - J5: { content: "50024" }, - J6: { content: "2" }, - J7: { content: "189576" }, - J8: { content: "256018" }, - J9: { content: "5000" }, - J12: { style: 10, content: "Tot. Score" }, - J13: { content: ">42000" }, - J24: { style: 10, content: "Dates" }, - J25: { content: "=DATE(2020, 01, 01)" }, - J26: { content: "=DATE(2021, 01, 01)" }, - J27: { content: "=DATE(2022, 01, 01)" }, - J33: { content: "Arguments", border: 6 }, - J34: { content: "1", border: 7 }, - J35: { content: "1", border: 8 }, - J37: { content: "Arguments", border: 9 }, - J38: { content: "1", border: 10 }, - J41: { content: "Arguments", border: 6 }, - J42: { content: "1", border: 7 }, - J43: { content: "0", border: 8 }, - J45: { content: "Arguments", border: 6 }, - J46: { content: "1", border: 7 }, - J47: { content: "3", border: 8 }, - J49: { content: "Results", border: 6 }, - J50: { content: "=MUNIT(2)", border: 7 }, - J53: { content: "Arguments", border: 6 }, - J54: { content: "1", border: 7 }, - J55: { content: "3", border: 8 }, - J59: { content: "Arguments", border: 6 }, - J60: { content: "1", border: 7 }, - J61: { content: "0", border: 8 }, - J63: { content: "Arguments", border: 6 }, - J64: { content: "1", border: 7 }, - J65: { content: "3", border: 8 }, - J67: { content: "Arguments", border: 6 }, - J68: { content: "1", border: 7 }, - J69: { content: "3", border: 8 }, - J71: { content: "Arguments", border: 6 }, - J72: { content: "1", border: 7 }, - J73: { content: "3", border: 8 }, - J75: { content: "Arguments", border: 6 }, - J76: { content: "1", border: 7 }, - J77: { content: "3", border: 8 }, - J79: { content: "Arguments", border: 6 }, - J80: { content: "1", border: 7 }, - J81: { content: "3", border: 8 }, - J83: { content: "Arguments", border: 6 }, - J84: { content: "1", border: 7 }, - J85: { content: "3", border: 8 }, - J87: { content: "Arguments", border: 6 }, - J88: { content: "1", border: 7 }, - J89: { content: "3", border: 8 }, - J91: { content: "Arguments", border: 6 }, - J92: { content: "1", border: 7 }, - J93: { content: "3", border: 8 }, - J95: { content: "Arguments", border: 6 }, - J96: { content: "1", border: 7 }, - J97: { content: "3", border: 8 }, - J99: { content: "Arguments", border: 6 }, - J100: { content: "1", border: 7 }, - J101: { content: "3", border: 8 }, - J103: { content: "Arguments", border: 6 }, - J104: { content: "1", border: 7 }, - J105: { content: "3", border: 8 }, - J109: { content: "Arguments", border: 6 }, - J110: { content: "1", border: 7 }, - J111: { content: "3", border: 8 }, - J113: { content: "Arguments", border: 9 }, - J114: { content: "Hello there; General Kenobi", border: 10 }, - J116: { content: "Arguments", border: 6 }, - J117: { content: "1", border: 7 }, - J118: { content: "3", border: 8 }, - J120: { content: "Arguments", border: 6 }, - J121: { content: "1", border: 7 }, - J122: { content: "3", border: 8 }, - J126: { content: "Arguments", border: 9 }, - J127: { content: "1", border: 10 }, - J128: { content: "3", border: 11 }, - J131: { content: "Arguments", border: 9 }, - J132: { content: "1", border: 10 }, - J133: { content: "3", border: 11 }, - K1: { style: 10, content: "Rank (lower the better)" }, - K2: { content: "5" }, - K3: { content: "7" }, - K4: { content: "3" }, - K5: { content: "4" }, - K6: { content: "1000" }, - K7: { content: "2" }, - K8: { content: "1" }, - K9: { content: "30" }, - K12: { style: 10, content: "Rank (lower the better)" }, - K13: { content: ">25" }, - K34: { content: "2", border: 4 }, - K35: { content: "2", border: 5 }, - K37: { content: "Results", border: 6 }, - K38: { content: "=EXPAND(J38, 2, 2, 0)", border: 7 }, - K42: { content: "2", border: 4 }, - K43: { content: "4", border: 5 }, - K46: { content: "2", border: 4 }, - K47: { content: "4", border: 5 }, - K54: { content: "2", border: 4 }, - K55: { content: "4", border: 5 }, - K60: { content: "2", border: 4 }, - K61: { content: "4", border: 5 }, - K64: { content: "2", border: 4 }, - K65: { content: "4", border: 5 }, - K68: { content: "2", border: 4 }, - K69: { content: "4", border: 5 }, - K72: { content: "2", border: 4 }, - K73: { content: "4", border: 5 }, - K76: { content: "2", border: 4 }, - K77: { content: "4", border: 5 }, - K80: { content: "2", border: 4 }, - K81: { content: "4", border: 5 }, - K84: { content: "2", border: 4 }, - K85: { content: "4", border: 5 }, - K88: { content: "2", border: 4 }, - K89: { content: "4", border: 5 }, - K92: { content: "2", border: 4 }, - K93: { content: "4", border: 5 }, - K96: { content: "2", border: 4 }, - K97: { content: "4", border: 5 }, - K100: { content: "2", border: 4 }, - K101: { content: "4", border: 5 }, - K104: { content: "2", border: 4 }, - K105: { content: "4", border: 5 }, - K110: { content: "2", border: 4 }, - K111: { content: "4", border: 5 }, - K113: { content: "Results", border: 6 }, - K114: { content: '=SPLIT(J114, " ")', border: 7 }, - K117: { content: "2", border: 4 }, - K118: { content: "4", border: 5 }, - K121: { content: "2", border: 4 }, - K122: { content: "4", border: 5 }, - K126: { content: "Results", border: 9 }, - K127: { content: "=WRAPCOLS(J127:J128, 3, 0)", border: 10 }, - K131: { content: "Results", border: 6 }, - K132: { content: "=WRAPROWS(J132:J133, 3, 0)", border: 7 }, - L33: { content: "Results", border: 6 }, - L34: { content: "=UNIQUE(J34:K35)", border: 7 }, - L41: { content: "Results", border: 6 }, - L42: { content: "=FILTER(J42:K43, J42:J43)", border: 7 }, - L45: { content: "Results", border: 6 }, - L46: { content: "=TRANSPOSE(J46:K47)", border: 7 }, - L49: { content: "Expected", border: 6 }, - L50: { content: "1", border: 7 }, - L51: { content: "0", border: 8 }, - L53: { content: "Results", border: 9 }, - L54: { content: "=FLATTEN(J54:K55)", border: 10 }, - L59: { content: "Results", border: 9 }, - L60: { content: "=FREQUENCY(J60:K61, 2)", border: 10 }, - L63: { content: "Results", border: 9 }, - L64: { content: "=ARRAY.CONSTRAIN(J64:K65, 2, 1)", border: 10 }, - L67: { content: "Results", border: 9 }, - L68: { content: "=CHOOSECOLS(J68:K69, 2)", border: 10 }, - L71: { content: "Results", border: 6 }, - L72: { content: "=CHOOSEROWS(J72:K73, 2)", border: 7 }, - L75: { content: "Results", border: 9 }, - L76: { content: "=SUMPRODUCT(J76:J77, K76:K77)", border: 10 }, - L79: { content: "Results", border: 6 }, - L80: { content: "=MINVERSE(J80:K81)", border: 7 }, - L83: { content: "Results", border: 9 }, - L84: { content: "=MDETERM(J84:K85)", border: 10 }, - L87: { content: "Results", border: 6 }, - L88: { content: "=MMULT(J88:K89, J88:K89)", border: 7 }, - L91: { content: "Results", border: 9 }, - L92: { content: "=SUMX2MY2(J92:J93, K92:K93)", border: 10 }, - L95: { content: "Results", border: 9 }, - L96: { content: "=SUMX2PY2(J96:J97, K96:K97)", border: 10 }, - L99: { content: "Results", border: 9 }, - L100: { content: "=SUMXMY2(J100:J101, K100:K101)", border: 10 }, - L103: { content: "Results", border: 9 }, - L104: { content: "=TOCOL(J104:K105)", border: 10 }, - L109: { content: "Results", border: 6 }, - L110: { content: "=TOROW(J110:K111)", border: 7 }, - L116: { content: "Results", border: 6 }, - L117: { content: "=HSTACK(J117:K117, J118:K118)", border: 7 }, - L120: { content: "Results", border: 9 }, - L121: { content: "=VSTACK(J121:J122, K121:K122)", border: 10 }, - L126: { content: "Expected", border: 6 }, - L127: { content: "1", border: 7 }, - L128: { content: "3", border: 8 }, - L129: { content: "0", border: 8 }, - M37: { content: "Expected", border: 6 }, - M38: { content: "1", border: 7 }, - M39: { content: "0", border: 8 }, - M50: { content: "0", border: 2 }, - M51: { content: "1" }, - M53: { content: "Expected", border: 6 }, - M54: { content: "1", border: 7 }, - M55: { content: "2", border: 8 }, - M56: { content: "3", border: 8 }, - M57: { content: "4", border: 8 }, - M59: { content: "Expected", border: 6 }, - M60: { content: "3", border: 7 }, - M61: { content: "1", border: 8 }, - M63: { content: "Expected", border: 6 }, - M64: { content: "1", border: 7 }, - M65: { content: "3", border: 8 }, - M67: { content: "Expected", border: 6 }, - M68: { content: "2", border: 7 }, - M69: { content: "4", border: 8 }, - M75: { content: "Expected", border: 6 }, - M76: { content: "14", border: 7 }, - M83: { content: "Expected", border: 6 }, - M84: { content: "-2", border: 7 }, - M91: { content: "Expected", border: 6 }, - M92: { content: "-10", border: 7 }, - M95: { content: "Expected", border: 6 }, - M96: { content: "30", border: 7 }, - M99: { content: "Expected", border: 6 }, - M100: { content: "2", border: 7 }, - M103: { content: "Expected", border: 6 }, - M104: { content: "1", border: 7 }, - M105: { content: "2", border: 8 }, - M106: { content: "3", border: 8 }, - M107: { content: "4", border: 8 }, - M120: { content: "Expected", border: 6 }, - M121: { content: "1", border: 7 }, - M122: { content: "3", border: 8 }, - M123: { content: "2", border: 8 }, - M124: { content: "4", border: 8 }, - N1: { content: "0.1" }, - N2: { content: "0.2" }, - N3: { content: "0.4" }, - N4: { content: "0.5" }, - N5: { content: "0.6" }, - N6: { content: "A" }, - N7: { content: "TRUE" }, - N8: { content: "FALSE" }, - N33: { content: "Expected", border: 6 }, - N34: { content: "1", border: 7 }, - N38: { content: "0", border: 2 }, - N39: { content: "0" }, - N41: { content: "Expected", border: 6 }, - N42: { content: "1", border: 7 }, - N45: { content: "Expected", border: 6 }, - N46: { content: "1", border: 7 }, - N47: { content: "2", border: 8 }, - N71: { content: "Expected", border: 6 }, - N72: { content: "3", border: 7 }, - N79: { content: "Expected", border: 6 }, - N80: { content: "-2", border: 7 }, - N81: { content: "1.5", border: 8 }, - N87: { content: "Expected", border: 6 }, - N88: { content: "7", border: 7 }, - N89: { content: "15", border: 8 }, - N131: { content: "Expected", border: 6 }, - N132: { content: "1", border: 7 }, - O34: { content: "2", border: 2 }, - O42: { content: "2", border: 2 }, - O46: { content: "3", border: 2 }, - O47: { content: "4" }, - O72: { content: "4", border: 2 }, - O80: { content: "1", border: 2 }, - O81: { content: "-0.5" }, - O88: { content: "10", border: 2 }, - O89: { content: "22" }, - O113: { content: "Expected", border: 6 }, - O114: { content: "Hello", border: 7 }, - O132: { content: "3", border: 2 }, - P109: { content: "Expected", border: 6 }, - P110: { content: "1", border: 7 }, - P114: { content: "there;", border: 2 }, - P116: { content: "Expected", border: 6 }, - P117: { content: "1", border: 7 }, - P132: { content: "0", border: 2 }, - Q110: { content: "2", border: 2 }, - Q114: { content: "General", border: 2 }, - Q117: { content: "2", border: 2 }, - R110: { content: "3", border: 2 }, - R114: { content: "Kenobi", border: 2 }, - R117: { content: "3", border: 2 }, - S110: { content: "4", border: 2 }, - S117: { content: "4", border: 2 }, - H33: { border: 1 }, - H37: { border: 1 }, - H41: { border: 1 }, - H45: { border: 1 }, - H49: { border: 1 }, - H53: { border: 1 }, - H59: { border: 1 }, - H63: { border: 1 }, - H67: { border: 1 }, - H71: { border: 1 }, - H75: { border: 1 }, - H79: { border: 1 }, - H83: { border: 1 }, - H87: { border: 1 }, - H91: { border: 1 }, - H95: { border: 1 }, - H99: { border: 1 }, - H103: { border: 1 }, - H109: { border: 1 }, - H113: { border: 1 }, - H116: { border: 1 }, - H120: { border: 1 }, - H126: { border: 1 }, - H131: { border: 1 }, - I33: { border: 3 }, - I35: { border: 5 }, - I37: { border: 3 }, - I39: { border: 5 }, - I41: { border: 3 }, - I43: { border: 5 }, - I45: { border: 3 }, - I47: { border: 5 }, - I49: { border: 3 }, - I51: { border: 5 }, - I53: { border: 3 }, - I55: { border: 5 }, - I56: { border: 5 }, - I57: { border: 5 }, - I59: { border: 3 }, - I61: { border: 5 }, - I63: { border: 3 }, - I65: { border: 5 }, - I67: { border: 3 }, - I69: { border: 5 }, - I71: { border: 3 }, - I73: { border: 5 }, - I75: { border: 3 }, - I77: { border: 5 }, - I79: { border: 3 }, - I81: { border: 5 }, - I83: { border: 3 }, - I85: { border: 5 }, - I87: { border: 3 }, - I89: { border: 5 }, - I91: { border: 3 }, - I93: { border: 5 }, - I95: { border: 3 }, - I97: { border: 5 }, - I99: { border: 3 }, - I101: { border: 5 }, - I103: { border: 3 }, - I105: { border: 5 }, - I106: { border: 5 }, - I107: { border: 5 }, - I109: { border: 3 }, - I111: { border: 5 }, - I113: { border: 3 }, - I116: { border: 3 }, - I118: { border: 5 }, - I120: { border: 3 }, - I122: { border: 5 }, - I123: { border: 5 }, - I124: { border: 5 }, - I126: { border: 3 }, - I128: { border: 5 }, - I129: { border: 5 }, - I131: { border: 3 }, - I133: { border: 5 }, - J39: { border: 11 }, - J51: { border: 8 }, - J56: { border: 8 }, - J57: { border: 8 }, - J106: { border: 8 }, - J107: { border: 8 }, - J123: { border: 8 }, - J124: { border: 8 }, - J129: { border: 11 }, - K33: { border: 3 }, - K39: { border: 8 }, - K41: { border: 3 }, - K45: { border: 3 }, - K49: { border: 3 }, - K50: { border: 4 }, - K51: { border: 5 }, - K53: { border: 3 }, - K56: { border: 5 }, - K57: { border: 5 }, - K59: { border: 3 }, - K63: { border: 3 }, - K67: { border: 3 }, - K71: { border: 3 }, - K75: { border: 3 }, - K79: { border: 3 }, - K83: { border: 3 }, - K87: { border: 3 }, - K91: { border: 3 }, - K95: { border: 3 }, - K99: { border: 3 }, - K103: { border: 3 }, - K106: { border: 5 }, - K107: { border: 5 }, - K109: { border: 3 }, - K116: { border: 3 }, - K120: { border: 3 }, - K123: { border: 5 }, - K124: { border: 5 }, - K128: { border: 11 }, - K129: { border: 11 }, - K133: { border: 8 }, - L35: { border: 8 }, - L37: { border: 3 }, - L38: { border: 4 }, - L39: { border: 5 }, - L43: { border: 8 }, - L47: { border: 8 }, - L55: { border: 11 }, - L56: { border: 11 }, - L57: { border: 11 }, - L61: { border: 11 }, - L65: { border: 11 }, - L69: { border: 11 }, - L73: { border: 8 }, - L77: { border: 11 }, - L81: { border: 8 }, - L85: { border: 11 }, - L89: { border: 8 }, - L93: { border: 11 }, - L97: { border: 11 }, - L101: { border: 11 }, - L105: { border: 11 }, - L106: { border: 11 }, - L107: { border: 11 }, - L111: { border: 8 }, - L113: { border: 1 }, - L114: { border: 2 }, - L118: { border: 8 }, - L122: { border: 11 }, - L123: { border: 11 }, - L124: { border: 11 }, - L131: { border: 1 }, - L132: { border: 2 }, - M33: { border: 3 }, - M34: { border: 4 }, - M35: { border: 5 }, - M41: { border: 3 }, - M42: { border: 4 }, - M43: { border: 5 }, - M45: { border: 3 }, - M46: { border: 4 }, - M47: { border: 5 }, - M49: { border: 1 }, - M71: { border: 3 }, - M72: { border: 4 }, - M73: { border: 5 }, - M77: { border: 8 }, - M79: { border: 3 }, - M80: { border: 4 }, - M81: { border: 5 }, - M85: { border: 8 }, - M87: { border: 3 }, - M88: { border: 4 }, - M89: { border: 5 }, - M93: { border: 8 }, - M97: { border: 8 }, - M101: { border: 8 }, - M109: { border: 1 }, - M110: { border: 2 }, - M113: { border: 1 }, - M114: { border: 2 }, - M116: { border: 1 }, - M117: { border: 2 }, - M131: { border: 3 }, - M132: { border: 4 }, - M133: { border: 5 }, - N35: { border: 8 }, - N37: { border: 1 }, - N43: { border: 8 }, - N73: { border: 8 }, - N109: { border: 1 }, - N110: { border: 2 }, - N113: { border: 3 }, - N114: { border: 4 }, - N116: { border: 1 }, - N117: { border: 2 }, - N133: { border: 8 }, - O33: { border: 1 }, - O41: { border: 1 }, - O45: { border: 1 }, - O71: { border: 1 }, - O79: { border: 1 }, - O87: { border: 1 }, - O109: { border: 3 }, - O110: { border: 4 }, - O111: { border: 5 }, - O116: { border: 3 }, - O117: { border: 4 }, - O118: { border: 5 }, - O131: { border: 1 }, - P111: { border: 8 }, - P113: { border: 1 }, - P118: { border: 8 }, - P131: { border: 1 }, - Q109: { border: 1 }, - Q113: { border: 1 }, - Q116: { border: 1 }, - R109: { border: 1 }, - R113: { border: 1 }, - R116: { border: 1 }, - S109: { border: 1 }, - S116: { border: 1 }, + A1: "formulas", + A2: "ABS", + A3: "ACOS", + A4: "ACOSH", + A5: "ACOT", + A6: "ACOTH", + A7: "AND", + A8: "ASIN", + A9: "ASINH", + A10: "ATAN", + A11: "ATAN2", + A12: "ATANH", + A13: "AVEDEV", + A14: "AVERAGE", + A15: "AVERAGEA", + A16: "AVERAGEIF", + A17: "AVERAGEIFS", + A18: "CEILING", + A19: "CEILING.MATH", + A20: "CEILING.PRECISE", + A21: "CHAR", + A22: "COLUMN", + A23: "COLUMNS", + A24: "CONCAT", + A25: "CONCATENATE", + A26: "COS", + A27: "COSH", + A28: "COT", + A29: "COTH", + A30: "COUNT", + A31: "COUNTA", + A32: "COUNTBLANK", + A33: "COUNTIF", + A34: "COUNTIFS", + A35: "COVAR", + A36: "COVARIANCE.P", + A37: "COVARIANCE.S", + A38: "CSC", + A39: "CSCH", + A40: "DATE", + A41: "DATEVALUE", + A42: "DAVERAGE", + A43: "DAY", + A44: "DAYS", + A45: "DCOUNT", + A46: "DCOUNTA", + A47: "DECIMAL", + A48: "DEGREES", + A49: "DGET", + A50: "DMAX", + A51: "DMIN", + A52: "DPRODUCT", + A53: "DSTDEV", + A54: "DSTDEVP", + A55: "DSUM", + A56: "DVAR", + A57: "DVARP", + A58: "EDATE", + A59: "EOMONTH", + A60: "EXACT", + A61: "EXP", + A62: "FIND", + A63: "FLOOR", + A64: "FLOOR.MATH", + A65: "FLOOR.PRECISE", + A66: "HLOOKUP", + A67: "HOUR", + A68: "IF", + A69: "IFERROR", + A70: "IFS", + A71: "ISERROR", + A72: "ISEVEN", + A73: "ISLOGICAL", + A74: "ISNONTEXT", + A75: "ISNUMBER", + A76: "ISO.CEILING", + A77: "ISODD", + A78: "ISOWEEKNUM", + A79: "ISTEXT", + A80: "LARGE", + A81: "LEFT", + A82: "LEN", + A83: "LN", + A84: "LOOKUP", + A85: "LOWER", + A86: "MATCH", + A87: "MAX", + A88: "MAXA", + A89: "MAXIFS", + A90: "MEDIAN", + A91: "MIN", + A92: "MINA", + A93: "MINIFS", + A94: "MINUTE", + A95: "MOD", + A96: "MONTH", + A97: "NETWORKDAYS", + A98: "NETWORKDAYS.INTL", + A99: "NOT", + A100: "NOW", + A101: "ODD", + A102: "OR", + A103: "PERCENTILE", + A104: "PERCENTILE.EXC", + A105: "PERCENTILE.INC", + A106: "PI", + A107: "POWER", + A108: "PRODUCT", + A109: "QUARTILE", + A110: "QUARTILE.EXC", + A111: "QUARTILE.INC", + A112: "RAND", + A113: "RANDBETWEEN", + A114: "REPLACE", + A115: "RIGHT", + A116: "ROUND", + A117: "ROUNDDOWN", + A118: "ROUNDUP", + A119: "ROW", + A120: "ROWS", + A121: "SEARCH", + A122: "SEC", + A123: "SECH", + A124: "SECOND", + A125: "SIN", + A126: "SINH", + A127: "SMALL", + A128: "SQRT", + A129: "STDEV", + A130: "STDEV.P", + A131: "STDEV.S", + A132: "STDEVA", + A133: "STDEVP", + A134: "STDEVPA", + A135: "SUBSTITUTE", + A136: "SUM", + A137: "SUMIF", + A138: "SUMIFS", + A139: "TAN", + A140: "TANH", + A141: "TEXTJOIN", + A142: "TIME", + A143: "TIMEVALUE", + A144: "TODAY", + A145: "TRIM", + A146: "TRUNC", + A147: "UPPER", + A148: "VAR", + A149: "VAR.P", + A150: "VAR.S", + A151: "VARA", + A152: "VARP", + A153: "VARPA", + A154: "VLOOKUP", + A155: "WEEKDAY", + A156: "WEEKNUM", + A157: "WORKDAY", + A158: "WORKDAY.INTL", + A159: "XOR", + A160: "YEAR", + A161: "DELTA", + A162: "NA", + A163: "ISNA", + A164: "ISERR", + A165: "TEXT", + A166: "ISBLANK", + A167: "IFNA", + A168: "CLEAN", + A169: "PROPER", + A170: "MID", + A171: "XLOOKUP", + A172: "ACCRINTM", + A173: "AMORLINC", + A174: "COUPDAYS", + A175: "COUPDAYBS", + A176: "COUPDAYSNC", + A177: "COUPNCD", + A178: "COUPNUM", + A179: "COUPPCD", + A180: "CUMIPMT", + A181: "CUMPRINC", + A182: "DB", + A183: "DDB", + A184: "DISC", + A185: "DOLLARDE", + A186: "DOLLARFR", + A187: "DURATION", + A188: "EFFECT", + A189: "FV", + A190: "FVSCHEDULE", + A191: "INTRATE", + A192: "IPMT", + A193: "IRR", + A194: "ISPMT", + A195: "MDURATION", + A196: "MIRR", + A197: "NOMINAL", + A198: "NPER", + A199: "NPV", + A200: "PDURATION", + A201: "PMT", + A202: "PPMT", + A203: "PV", + A204: "PRICE", + A205: "PRICEDISC", + A206: "PRICEMAT", + A207: "RATE", + A208: "RECEIVED", + A209: "RRI", + A210: "SLN", + A211: "SYD", + A212: "TBILLPRICE", + A213: "TBILLEQ", + A214: "TBILLYIELD", + A215: "VDB", + A216: "XIRR", + A217: "XNPV", + A218: "YIELD", + A219: "YIELDDISC", + A220: "YIELDMAT", + A221: "DAYS360", + A222: "DATEDIF", + A223: "ADDRESS", + A224: "INDIRECT", + B1: "evaluation", + B2: "=ABS(-5.5)", + B3: "=ACOS(1)", + B4: "=ROUND(ACOSH(2),5)", + B5: "=ROUND(ACOT(1),5)", + B6: "=ROUND(ACOTH(2),5)", + B7: "=AND(TRUE,TRUE)", + B8: "=ROUND(ASIN(0.5),5)", + B9: "=ROUND(ASINH(2), 5)", + B10: "=ROUND(ATAN(1),5)", + B11: "=ROUND(ATAN2(-1,0),5)", + B12: "=ROUND(ATANH(0.7),5)", + B13: "=ROUND(AVEDEV(I2:I9),5)", + B14: "=ROUND(AVERAGE(H2:H9),5)", + B15: "=AVERAGEA(G2:H9)", + B16: '=ROUND(AVERAGEIF(J2:J9,">150000" ),5)', + B17: '=ROUND(AVERAGEIFS(I2:I9,H2:H9,">=30",K2:K9, "<10"),5)', + B18: "=CEILING(20.4,1)", + B19: "=CEILING.MATH(-5.5,1,0)", + B20: "=CEILING.PRECISE(230, 100)", + B21: "=CHAR(74)", + B22: "=COLUMN(C4)", + B23: "=COLUMNS(A5:D12)", + B24: "=CONCAT(1,23)", + B25: '=CONCATENATE("BUT, ", "MICHEL")', + B26: "=ROUND(COS(PI()/3),2)", + B27: "=ROUND(COSH(2),5)", + B28: "=ROUND(COT(PI()/6),5)", + B29: "=ROUND(COTH(.5),5)", + B30: '=COUNT(1,"a","5", "03/14/2021")', + B31: '=COUNTA(1,"a","5", "03/14/2021")', + B32: "=COUNTBLANK(F1:G1)", + B33: '=COUNTIF(H2:H9,">30")', + B34: '=COUNTIFS(H2:H9, ">25",K2:K9,"<4")', + B35: "=ROUND(COVAR(H2:H9,K2:K9),5)", + B36: "=ROUND(COVARIANCE.P(K2:K9,H2:H9),5)", + B37: "=ROUND(COVARIANCE.P(I2:I9,J2:J9),5)", + B38: "=ROUND(CSC(PI()/4),5)", + B39: "=ROUND(CSCH(pi()/3),5)", + B40: "=DATE(2020,5,25)", + B41: '=DATEVALUE("1969/08/15")', + B42: '=ROUND(DAVERAGE(G1:K9,"Tot. Score",J12:J13),5)', + B43: '=DAY("2020/03/17")', + B44: '=DAYS("2022/03/17", "2021/03/17")', + B45: '=DCOUNT(G1:K9,"Name",H12:H13)', + B46: '=DCOUNTA(G1:K9,"Name",H12:H13)', + B47: "=DECIMAL(20,16)", + B48: "=DEGREES(pi()/4)", + B49: '=DGET(G1:K9, "Hours Played",G12:G13)', + B50: '=DMAX(G1:K9,"Tot. Score", I12:I13)', + B51: '=DMIN(G1:K9,"Tot. Score", H12:H13)', + B52: '=DPRODUCT(G1:K9, "Age",K12:K13)', + B53: '=ROUND(DSTDEV(G1:K9, "Age",H12:H13), 5)', + B54: '=ROUND(DSTDEVP(G1:K9, "Age",H12:H13), 5)', + B55: '=DSUM(G1:K9,"Age",I12:I13)', + B56: '=ROUND(DVAR(G1:K9, "Hours Played",H12:H13),5)', + B57: '=ROUND(DVARP(G1:K9, "Hours Played",H12:H13),5)', + B58: '=EDATE("7/22/1969", -2)', + B59: '=EOMONTH("7/21/2020", 1)', + B60: '=EXACT("AbsSdf%", "AbsSdf%")', + B61: "=ROUND(EXP(4),5)", + B62: '=FIND("A", "qbdahbaazo A")', + B63: "=FLOOR(5.5, 2)", + B64: "=FLOOR.MATH(-5.55,2, 1)", + B65: "=FLOOR.PRECISE(199,100)", + B66: '=HLOOKUP("Tot. Score",H1:K9, 4,FALSE)', + B67: '=HOUR("2:14:56 AM")', + B68: '=IF(TRUE,"TABOURET","JAMBON")', + B69: '=IFERROR(0/0, "no diving by zero.")', + B70: '=IFS($H2>$H3,"first player is older",$H3>$H2, "second player is older")', + B71: "=ISERROR(0/0)", + B72: "=ISEVEN(3)", + B73: '=ISLOGICAL("TRUE")', + B74: "=ISNONTEXT(TRUE)", + B75: "=ISNUMBER(1231.5)", + B76: "=ISO.CEILING(-7.89)", + B77: "=ISODD(4)", + B78: '=ISOWEEKNUM("1/3/2016")', + B79: '=ISTEXT("123")', + B80: "=LARGE(H2:H9,3)", + B81: '=LEFT("Mich",4)', + B82: '=LEN("anticonstitutionnellement")', + B83: "=ROUND(LN(2),5)", + B84: "=LOOKUP(23000,H3:J3,H5:J5)", + B85: '=LOWER("オAドB")', + B86: "=MATCH(42,H2:H9,0)", + B87: "=MAX(N1:N8)", + B88: "=MAXA(N1:N8)", + B89: '=MAXIFS(H2:H9,K2:K9, "<20",K2:K9, "<>4")', + B90: "=MEDIAN(-1, 6, 7, 234, 163845)", + B91: "=MIN(N1:N8)", + B92: "=MINA(N1:N8)", + B93: '=MINIFS(J2:J9,H2:H9, ">20")', + B94: "=MINUTE(0.126)", + B95: "=MOD(42,12)", + B96: '=MONTH("5/2/1954")', + B97: '=NETWORKDAYS("1/1/2013", "2/1/2013")', + B98: '=NETWORKDAYS.INTL("1/1/2013", "2/1/2013", "0000111")', + B99: "=NOT(FALSE)", + B100: "=NOW()", + B101: "=ODD(4)", + B102: '=OR("true", FALSE)', + B103: "=PERCENTILE(N1:N5,1)", + B104: "=PERCENTILE.EXC(N1:N5,0.5)", + B105: "=PERCENTILE.INC(N1:N5,0)", + B106: "=ROUND(PI(), 5)", + B107: "=POWER(42,2)", + B108: "=PRODUCT(1,2,3)", + B109: "=QUARTILE(N1:N5, 0)", + B110: "=ROUND(QUARTILE.EXC(N1:N5, 1),5)", + B111: "=QUARTILE.INC(N1:N5 ,4)", + B112: "=RAND()", + B113: "=RANDBETWEEN(1.1,2)", + B114: '=REPLACE("ABZ", 2, 1, "Y")', + B115: '=RIGHT("kikou", 2)', + B116: "=ROUND(49.9)", + B117: "=ROUNDDOWN(42, -1)", + B118: "=ROUNDUP(-1.6,0)", + B119: "=ROW(A234)", + B120: "=ROWS(B3:C40)", + B121: '=SEARCH("C", "ABCD")', + B122: "=ROUND(SEC(PI()/3),5)", + B123: "=ROUND(SECH(1), 5)", + B124: '=SECOND("0:21:42")', + B125: "=ROUND(SIN(PI()/6),5)", + B126: "=ROUND(SINH(1),5)", + B127: "=SMALL(H2:H9, 3)", + B128: "=SQRT(4)", + B129: "=STDEV(-2,0,2)", + B130: "=STDEV.P(2,4)", + B131: "=STDEV.S(2,4,6)", + B132: "=STDEVA(TRUE, 3,5)", + B133: "=ROUND(STDEVP(2,5,8),2)", + B134: "=ROUND(STDEVPA(TRUE, 4,7),2)", + B135: '=SUBSTITUTE("SAP is best", "SAP", "Odoo")', + B136: "=SUM(1,2,3,4,5)", + B137: '=SUMIF(K2:K9, "<100")', + B138: '=SUMIFS(H2:H9,K2:K9, "<100")', + B139: "=ROUND(TAN(PI()/4),5)", + B140: "=ROUND(TANH(1),5)", + B141: '=TEXTJOIN("-",TRUE,"","1","A","%")', + B142: "=TIME(9,11,31)", + B143: '=TIMEVALUE("1899 10 08 18:00")', + B144: "=TODAY()", + B145: '=TRIM(" Jean Ticonstitutionnalise ")', + B146: "=TRUNC(42.42, 1)", + B147: '=UPPER("grrrr !")', + B148: "=ROUND(VAR(K1:K5),5)", + B149: "=ROUND(VAR.P(K1:K5),5)", + B150: "=VAR.S(2,5,8)", + B151: "=ROUND(VARA(K1:K5),5)", + B152: "=ROUND(VARP(K1:K5),5)", + B153: "=ROUND(VARPA(K1:K5),5)", + B154: '=VLOOKUP("NotACheater",G1:K9, 3, FALSE)', + B155: '=WEEKDAY("6/12/2021")', + B156: '=WEEKNUM("6/29/2021")', + B157: '=WORKDAY("3/15/2021", 6)', + B158: '=WORKDAY.INTL("3/15/2021", 6, "0111111")', + B159: "=XOR(false, true, false, false)", + B160: '=YEAR("3/12/2012")', + B161: "=DELTA(1,1)", + B162: "=NA()", + B163: "=ISNA(NA())", + B164: "=ISERR(NA())", + B165: '=TEXT(5, "#,##0.00")', + B166: "=ISBLANK(E166)", + B167: '=IFNA(NA(), "hello")', + B168: '=CLEAN("a"&CHAR(10))', + B169: '=PROPER("this is a sentence")', + B170: '=MID("Odoo", 2, 5)', + B171: '=XLOOKUP("robot4", G2:G9, H2:H9)', + B172: '=ACCRINTM("01/01/2020", "01/01/2021", 0.1, 100, 0)', + B173: '=AMORLINC(50, "01/01/2021", "06/01/2021", 5, 1, 0.1)', + B174: '=COUPDAYS("01/01/2021", "01/01/2022", 1, 0)', + B175: '=COUPDAYBS("01/01/2021", "01/01/2022", 1, 0)', + B176: '=COUPDAYSNC("01/01/2021", "01/01/2022", 1, 0)', + B177: '=COUPNCD("01/01/2021", "01/01/2022", 1, 0)', + B178: '=COUPNUM("01/01/2021", "01/01/2022", 1, 0)', + B179: '=COUPPCD("01/01/2021", "01/01/2022", 1, 0)', + B180: "=CUMIPMT(0.1, 12, 100, 1, 1, 1)", + B181: "=CUMPRINC(0.1, 12, 100, 1, 1, 1)", + B182: "=DB(50, 5, 12, 1, 1)", + B183: "=DDB(50, 5, 12, 1, 2)", + B184: '=DISC("01/01/2021", "01/01/2022", 50, 100, 0)', + B185: "=DOLLARDE(10.25, 8)", + B186: "=DOLLARFR(10.12, 8)", + B187: '=DURATION("01/01/2021", "01/01/2022", 0.1, 50, 1, 0)', + B188: "=EFFECT(0.1, 12)", + B189: "=FV(0.1, 12, -10, 100, 1)", + B190: "=FVSCHEDULE(100, I25:I27)", + B191: '=INTRATE("01/01/2021", "01/01/2022", 100, 100, 0)', + B192: "=IPMT(0.1, 1, 12, 100, 5, 1)", + B193: "=IRR(H25:H27, 0.1)", + B194: "=ISPMT(0.1, 1, 12, 100)", + B195: '=MDURATION("01/01/2021", "01/01/2022", 0.1, 50, 1, 0)', + B196: "=MIRR(H25:H27, 0.12, 0.1)", + B197: "=NOMINAL(0.12, 12)", + B198: "=NPER(0.1, -10, 100, 5, 1)", + B199: "=NPV(0.1, 50, 60)", + B200: "=PDURATION(0.1, 100, 5)", + B201: "=PMT(0.1, 12, 100, 5, 1)", + B202: "=PPMT(0.1, 1, 12, 100, 5, 1)", + B203: "=PV(0.1, 12, -10, 5, 1)", + B204: '=PRICE("01/01/2021", "01/01/2022", 0.1, 50, 100, 1, 0)', + B205: '=PRICEDISC("01/01/2021", "01/01/2022", 0.1, 100, 0)', + B206: '=PRICEMAT("01/01/2021", "01/01/2022", "01/01/2020", 0.1, 50, 0)', + B207: "=RATE(12, -10, 100, 5, 1, 0.1)", + B208: '=RECEIVED("01/01/2021", "01/01/2022", 100, 0.1, 0)', + B209: "=RRI(12, 100, 5)", + B210: "=SLN(50, 5, 12)", + B211: "=SYD(50, 5, 12, 1)", + B212: '=TBILLPRICE("01/01/2021", "01/01/2022", 0.1)', + B213: '=TBILLEQ("01/01/2021", "01/01/2022", 0.1)', + B214: '=TBILLYIELD("01/01/2021", "01/01/2022", 50)', + B215: "=VDB(50, 5, 12, 1, 2, 2, 0)", + B216: "=XIRR(H25:H27, J25:J27, 0.1)", + B217: "=XNPV(0.1, H25:H27, J25:J27)", + B218: '=YIELD("01/01/2021", "01/01/2022", 0.1, 50, 100, 1, 0)', + B219: '=YIELDDISC("01/01/2021", "01/01/2022", 50, 100, 0)', + B220: '=YIELDMAT("01/01/2021", "01/01/2022", "01/01/2020", 0.1, 50, 0)', + B221: '=DAYS360("01/01/2020", "12/31/2020")', + B222: '=DATEDIF("2001/09/15", "2003/06/10", "MD")', + B223: '=ADDRESS(27, 53, 1, TRUE, "sheet!")', + B224: '=INDIRECT("A1")', + C1: "expected value", + C2: "5.5", + C3: "0", + C4: "1.31696", + C5: "0.7854", + C6: "0.54931", + C7: "TRUE", + C8: "0.5236", + C9: "1.44364", + C10: "0.7854", + C11: "3.14159", + C12: "0.8673", + C13: "2959.1625", + C14: "26.25", + C15: "13.125", + C16: "222797", + C17: "8376.65", + C18: "21", + C19: "-5", + C20: "300", + C21: "J", + C22: "3", + C23: "4", + C24: '="123"', + C25: "BUT, MICHEL", + C26: "0.5", + C27: "3.7622", + C28: "=ROUND(SQRT(3),5)", + C29: "2.16395", + C30: "2", + C31: "4", + C32: "1", + C33: "2", + C34: "3", + C35: "-2119.25", + C36: "-2119.25", + C37: "237217364.71641", + C38: "=ROUND(SQRT(2),5)", + C39: "0.80041", + C40: "43976", + C41: "25430", + C42: "151434.625", + C43: "17", + C44: "365", + C45: "0", + C46: "3", + C47: "32", + C48: "45", + C49: "252.4", + C50: "=J7", + C51: "=J9", + C52: "333", + C53: "6.02771", + C54: "4.92161", + C55: "101", + C56: "17560207.92333", + C57: "11706805.28222", + C58: "25345", + C59: "44074", + C60: "TRUE", + C61: "54.59815", + C62: "12", + C63: "4", + C64: "-4", + C65: "100", + C66: "110120.5", + C67: "2", + C68: "TABOURET", + C69: "no diving by zero.", + C70: "first player is older", + C71: "TRUE", + C72: "FALSE", + C73: "FALSE", + C74: "TRUE", + C75: "TRUE", + C76: "-7", + C77: "FALSE", + C78: "53", + C79: "TRUE", + C80: "30", + C81: "Mich", + C82: "25", + C83: "0.69315", + C84: "50024", + C85: "オaドb", + C86: "4", + C87: "0.6", + C88: "1", + C89: "30", + C90: "7", + C91: "0.1", + C92: "0", + C93: "5000", + C94: "1", + C95: "6", + C96: "5", + C97: "24", + C98: "19", + C99: "TRUE", + C101: "5", + C102: "TRUE", + C103: "0.6", + C104: "0.4", + C105: "0.1", + C106: "3.14159", + C107: "1764", + C108: "6", + C109: "0.1", + C110: "0.15", + C111: "0.6", + C113: "2", + C114: "AYZ", + C115: "ou", + C116: "50", + C117: "40", + C118: "-2", + C119: "234", + C120: "38", + C121: "3", + C122: "2", + C123: "0.64805", + C124: "42", + C125: "0.5", + C126: "1.1752", + C127: "26", + C128: "2", + C129: "2", + C130: "1", + C131: "2", + C132: "2", + C133: "2.45", + C134: "2.45", + C135: "Odoo is best", + C136: "15", + C137: "52", + C138: "201", + C139: "1", + C140: "0.76159", + C141: "1-A-%", + C142: "0.3829976851851852", + C143: "0.75", + C145: "Jean Ticonstitutionnalise", + C146: "42.4", + C147: "GRRRR !", + C148: "2.91667", + C149: "2.1875", + C150: "9", + C151: "6.7", + C152: "2.1875", + C153: "5.36", + C154: "=252.4", + C155: "7", + C156: "27", + C157: "44278", + C158: "44312", + C159: "TRUE", + C160: "2012", + C161: "1", + C162: "#N/A", + C163: "TRUE", + C164: "FALSE", + C165: '="5.00"', + C166: "=TRUE", + C167: '="hello"', + C168: '="a"', + C169: '="This Is A Sentence"', + C170: '="doo"', + C171: "=42", + C172: "10", + C173: "5", + C174: "360", + C175: "0", + C176: "360", + C177: "44562", + C178: "1", + C179: "44197", + C180: "0", + C181: "-13.34211955", + C182: "0.729166667", + C183: "8.333333333", + C184: "0.5", + C185: "10.3125", + C186: "10.096", + C187: "1", + C188: "0.104713067", + C189: "-78.61571623", + C190: "158.125", + C191: "0", + C192: "0", + C193: "0.421954446", + C194: "-9.166666667", + C195: "0.019607843", + C196: "-0.060608807", + C197: "0.113865515", + C198: "25.62524843", + C199: "95.04132231", + C200: "-31.43139883", + C201: "-13.55468008", + C202: "-13.55468008", + C203: "73.35745596", + C204: "2.156862745", + C205: "90", + C206: "-7.647058824", + C207: "0.027937424", + C208: "111.1111111", + C209: "-0.220922192", + C210: "3.75", + C211: "6.923076923", + C212: "89.86111111", + C213: "0.109813678", + C214: "0.98630137", + C215: "6.944444444", + C216: "0.420899528", + C217: "-404.5918575", + C218: "1.2", + C219: "1", + C220: "1", + C221: "360", + C222: "26", + C223: "'sheet!'!$BA$27", + C224: "=A1", + D1: "is it compatible ?", + D2: "=IF(B2=C2,1, 0)", + D3: "=IF(B3=C3,1, 0)", + D4: "=IF(B4=C4,1, 0)", + D5: "=IF(B5=C5,1, 0)", + D6: "=IF(B6=C6,1, 0)", + D7: "=IF(B7=C7,1, 0)", + D8: "=IF(B8=C8,1, 0)", + D9: "=IF(B9=C9,1, 0)", + D10: "=IF(B10=C10,1, 0)", + D11: "=IF(B11=C11,1, 0)", + D12: "=IF(B12=C12,1, 0)", + D13: "=IF(B13=C13,1, 0)", + D14: "=IF(B14=C14,1, 0)", + D15: "=IF(B15=C15,1, 0)", + D16: "=IF(B16=C16,1, 0)", + D17: "=IF(B17=C17,1, 0)", + D18: "=IF(B18=C18,1, 0)", + D19: "=IF(B19=C19,1, 0)", + D20: "=IF(B20=C20,1, 0)", + D21: "=IF(B21=C21,1, 0)", + D22: "=IF(B22=C22,1, 0)", + D23: "=IF(B23=C23,1, 0)", + D24: "=IF(B24=C24,1, 0)", + D25: "=IF(B25=C25,1, 0)", + D26: "=IF(B26=C26,1, 0)", + D27: "=IF(B27=C27,1, 0)", + D28: "=IF(B28=C28,1, 0)", + D29: "=IF(B29=C29,1, 0)", + D30: "=IF(B30=C30,1, 0)", + D31: "=IF(B31=C31,1, 0)", + D32: "=IF(B32=C32,1, 0)", + D33: "=IF(B33=C33,1, 0)", + D34: "=IF(B34=C34,1, 0)", + D35: "=IF(B35=C35,1, 0)", + D36: "=IF(B36=C36,1, 0)", + D37: "=IF(B37=C37,1, 0)", + D38: "=IF(B38=C38,1, 0)", + D39: "=IF(B39=C39,1, 0)", + D40: "=IF(B40=C40,1, 0)", + D41: "=IF(B41=C41,1, 0)", + D42: "=IF(B42=C42,1, 0)", + D43: "=IF(B43=C43,1, 0)", + D44: "=IF(B44=C44,1, 0)", + D45: "=IF(B45=C45,1, 0)", + D46: "=IF(B46=C46,1, 0)", + D47: "=IF(B47=C47,1, 0)", + D48: "=IF(B48=C48,1, 0)", + D49: "=IF(B49=C49,1, 0)", + D50: "=IF(B50=C50,1, 0)", + D51: "=IF(B51=C51,1, 0)", + D52: "=IF(B52=C52,1, 0)", + D53: "=IF(B53=C53,1, 0)", + D54: "=IF(B54=C54,1, 0)", + D55: "=IF(B55=C55,1, 0)", + D56: "=IF(B56=C56,1, 0)", + D57: "=IF(B57=C57,1, 0)", + D58: "=IF(B58=C58,1, 0)", + D59: "=IF(B59=C59,1, 0)", + D60: "=IF(B60=C60,1, 0)", + D61: "=IF(B61=C61,1, 0)", + D62: "=IF(B62=C62,1, 0)", + D63: "=IF(B63=C63,1, 0)", + D64: "=IF(B64=C64,1, 0)", + D65: "=IF(B65=C65,1, 0)", + D66: "=IF(B66=C66,1, 0)", + D67: "=IF(B67=C67,1, 0)", + D68: "=IF(B68=C68,1, 0)", + D69: "=IF(B69=C69,1, 0)", + D70: "=IF(B70=C70,1, 0)", + D71: "=IF(B71=C71,1, 0)", + D72: "=IF(B72=C72,1, 0)", + D73: "=IF(B73=C73,1, 0)", + D74: "=IF(B74=C74,1, 0)", + D75: "=IF(B75=C75,1, 0)", + D76: "=IF(B76=C76,1, 0)", + D77: "=IF(B77=C77,1, 0)", + D78: "=IF(B78=C78,1, 0)", + D79: "=IF(B79=C79,1, 0)", + D80: "=IF(B80=C80,1, 0)", + D81: "=IF(B81=C81,1, 0)", + D82: "=IF(B82=C82,1, 0)", + D83: "=IF(B83=C83,1, 0)", + D84: "=IF(B84=C84,1, 0)", + D85: "=IF(B85=C85,1, 0)", + D86: "=IF(B86=C86,1, 0)", + D87: "=IF(B87=C87,1, 0)", + D88: "=IF(B88=C88,1, 0)", + D89: "=IF(B89=C89,1, 0)", + D90: "=IF(B90=C90,1, 0)", + D91: "=IF(B91=C91,1, 0)", + D92: "=IF(B92=C92,1, 0)", + D93: "=IF(B93=C93,1, 0)", + D94: "=IF(B94=C94,1, 0)", + D95: "=IF(B95=C95,1, 0)", + D96: "=IF(B96=C96,1, 0)", + D97: "=IF(B97=C97,1, 0)", + D98: "=IF(B98=C98,1, 0)", + D99: "=IF(B99=C99,1, 0)", + D100: "=IF(ISNUMBER(B100),1, 0)", + D101: "=IF(B101=C101,1, 0)", + D102: "=IF(B102=C102,1, 0)", + D103: "=IF(B103=C103,1, 0)", + D104: "=IF(B104=C104,1, 0)", + D105: "=IF(B105=C105,1, 0)", + D106: "=IF(B106=C106,1, 0)", + D107: "=IF(B107=C107,1, 0)", + D108: "=IF(B108=C108,1, 0)", + D109: "=IF(B109=C109,1, 0)", + D110: "=IF(B110=C110,1, 0)", + D111: "=IF(B111=C111,1, 0)", + D112: "=IF(AND(B112>=0,B112<1 ),1, 0)", + D113: "=IF(B113=C113,1, 0)", + D114: "=IF(B114=C114,1, 0)", + D115: "=IF(B115=C115,1, 0)", + D116: "=IF(B116=C116,1, 0)", + D117: "=IF(B117=C117,1, 0)", + D118: "=IF(B118=C118,1, 0)", + D119: "=IF(B119=C119,1, 0)", + D120: "=IF(B120=C120,1, 0)", + D121: "=IF(B121=C121,1, 0)", + D122: "=IF(B122=C122,1, 0)", + D123: "=IF(B123=C123,1, 0)", + D124: "=IF(B124=C124,1, 0)", + D125: "=IF(B125=C125,1, 0)", + D126: "=IF(B126=C126,1, 0)", + D127: "=IF(B127=C127,1, 0)", + D128: "=IF(B128=C128,1, 0)", + D129: "=IF(B129=C129,1, 0)", + D130: "=IF(B130=C130,1, 0)", + D131: "=IF(B131=C131,1, 0)", + D132: "=IF(B132=C132,1, 0)", + D133: "=IF(B133=C133,1, 0)", + D134: "=IF(B134=C134,1, 0)", + D135: "=IF(B135=C135,1, 0)", + D136: "=IF(B136=C136,1, 0)", + D137: "=IF(B137=C137,1, 0)", + D138: "=IF(B138=C138,1, 0)", + D139: "=IF(B139=C139,1, 0)", + D140: "=IF(B140=C140,1, 0)", + D141: "=IF(B141=C141,1, 0)", + D142: "=IF(B142=C142,1, 0)", + D143: "=IF(B143=C143,1, 0)", + D144: "=IF(ISNUMBER(B144),1, 0)", + D145: "=IF(B145=C145,1, 0)", + D146: "=IF(B146=C146,1, 0)", + D147: "=IF(B147=C147,1, 0)", + D148: "=IF(B148=C148,1, 0)", + D149: "=IF(B149=C149,1, 0)", + D150: "=IF(B150=C150,1, 0)", + D151: "=IF(B151=C151,1, 0)", + D152: "=IF(B152=C152,1, 0)", + D153: "=IF(B153=C153,1, 0)", + D154: "=IF(B154=C154,1, 0)", + D155: "=IF(B155=C155,1, 0)", + D156: "=IF(B156=C156,1, 0)", + D157: "=IF(B157=C157,1, 0)", + D158: "=IF(B158=C158,1, 0)", + D159: "=IF(B159=C159,1, 0)", + D160: "=IF(B160=C160,1, 0)", + D161: "=IF(B161=C161,1, 0)", + D162: "=IF(ISNA(B162),1, 0)", + D163: "=IF(B163=C163,1, 0)", + D164: "=IF(B164=C164,1, 0)", + D165: "=IF(B165=C165,1, 0)", + D166: "=IF(B166=C166,1, 0)", + D167: "=IF(B167=C167,1, 0)", + D168: "=IF(B168=C168,1, 0)", + D169: "=IF(B169=C169,1, 0)", + D170: "=IF(B170=C170,1, 0)", + D171: "=IF(B171=C171,1, 0)", + D172: "=IF(B172=C172, 1, 0)", + D173: "=IF(B173=C173, 1, 0)", + D174: "=IF(B174=C174, 1, 0)", + D175: "=IF(B175=C175, 1, 0)", + D176: "=IF(B176=C176, 1, 0)", + D177: "=IF(B177=C177, 1, 0)", + D178: "=IF(B178=C178, 1, 0)", + D179: "=IF(B179=C179, 1, 0)", + D180: "=IF(B180=C180, 1, 0)", + D181: "=IF(FLOOR(B181, 0.0001)=FLOOR(C181, 0.0001), 1, 0)", + D182: "=IF(FLOOR(B182, 0.0001)=FLOOR(C182, 0.0001), 1, 0)", + D183: "=IF(FLOOR(B183, 0.0001)=FLOOR(C183, 0.0001), 1, 0)", + D184: "=IF(FLOOR(B184, 0.0001)=FLOOR(C184, 0.0001), 1, 0)", + D185: "=IF(FLOOR(B185, 0.0001)=FLOOR(C185, 0.0001), 1, 0)", + D186: "=IF(FLOOR(B186, 0.0001)=FLOOR(C186, 0.0001), 1, 0)", + D187: "=IF(B187=C187, 1, 0)", + D188: "=IF(FLOOR(B188, 0.0001)=FLOOR(C188, 0.0001), 1, 0)", + D189: "=IF(FLOOR(B189, 0.0001)=FLOOR(C189, 0.0001), 1, 0)", + D190: "=IF(FLOOR(B190, 0.0001)=FLOOR(C190, 0.0001), 1, 0)", + D191: "=IF(B191=C191, 1, 0)", + D192: "=IF(B192=C192, 1, 0)", + D193: "=IF(FLOOR(B193, 0.0001)=FLOOR(C193, 0.0001), 1, 0)", + D194: "=IF(FLOOR(B194, 0.0001)=FLOOR(C194, 0.0001), 1, 0)", + D195: "=IF(FLOOR(B195, 0.0001)=FLOOR(C195, 0.0001), 1, 0)", + D196: "=IF(FLOOR(B196, 0.0001)=FLOOR(C196, 0.0001), 1, 0)", + D197: "=IF(FLOOR(B197, 0.0001)=FLOOR(C197, 0.0001), 1, 0)", + D198: "=IF(FLOOR(B198, 0.0001)=FLOOR(C198, 0.0001), 1, 0)", + D199: "=IF(FLOOR(B199, 0.0001)=FLOOR(C199, 0.0001), 1, 0)", + D200: "=IF(FLOOR(B200, 0.0001)=FLOOR(C200, 0.0001), 1, 0)", + D201: "=IF(FLOOR(B201, 0.0001)=FLOOR(C201, 0.0001), 1, 0)", + D202: "=IF(FLOOR(B202, 0.0001)=FLOOR(C202, 0.0001), 1, 0)", + D203: "=IF(FLOOR(B203, 0.0001)=FLOOR(C203, 0.0001), 1, 0)", + D204: "=IF(FLOOR(B204, 0.0001)=FLOOR(C204, 0.0001), 1, 0)", + D205: "=IF(B205=C205, 1, 0)", + D206: "=IF(FLOOR(B206, 0.0001)=FLOOR(C206, 0.0001), 1, 0)", + D207: "=IF(FLOOR(B207, 0.0001)=FLOOR(C207, 0.0001), 1, 0)", + D208: "=IF(FLOOR(B208, 0.0001)=FLOOR(C208, 0.0001), 1, 0)", + D209: "=IF(FLOOR(B209, 0.0001)=FLOOR(C209, 0.0001), 1, 0)", + D210: "=IF(FLOOR(B210, 0.0001)=FLOOR(C210, 0.0001), 1, 0)", + D211: "=IF(FLOOR(B211, 0.0001)=FLOOR(C211, 0.0001), 1, 0)", + D212: "=IF(FLOOR(B212, 0.0001)=FLOOR(C212, 0.0001), 1, 0)", + D213: "=IF(FLOOR(B213, 0.0001)=FLOOR(C213, 0.0001), 1, 0)", + D214: "=IF(FLOOR(B214, 0.0001)=FLOOR(C214, 0.0001), 1, 0)", + D215: "=IF(FLOOR(B215, 0.0001)=FLOOR(C215, 0.0001), 1, 0)", + D216: "=IF(FLOOR(B216, 0.0001)=FLOOR(C216, 0.0001), 1, 0)", + D217: "=IF(FLOOR(B217, 0.0001)=FLOOR(C217, 0.0001), 1, 0)", + D218: "=IF(FLOOR(B218, 0.0001)=FLOOR(C218, 0.0001), 1, 0)", + D219: "=IF(B219=C219, 1, 0)", + D220: "=IF(B220=C220, 1, 0)", + D221: "=IF(B221=C221, 1, 0)", + D222: "=IF(B222=C222, 1, 0)", + D223: "=IF(B223=C223, 1, 0)", + D224: "=IF(B224=C224, 1, 0)", + G1: "Name", + G2: "Robot1", + G3: "Robot2", + G4: "NotACheater", + G5: "Robot4", + G6: "Robot3", + G7: "Robot6", + G8: "Michel", + G9: "Robot7", + G11: "criteria", + G12: "Name", + G13: "NotACheater", + H1: "Age", + H2: "26", + H3: "13", + H4: "26", + H5: "42", + H6: "9", + H7: "27", + H8: "30", + H9: "37", + H12: "Age", + H13: ">29", + H24: "Cashflows", + H25: "1000", + H26: "-1000", + H27: "-600", + H34: "UNIQUE", + H38: "EXPAND", + H42: "FILTER", + H46: "TRANSPOSE", + H50: "MUNIT", + H54: "FLATTEN", + H60: "FREQUENCY", + H64: "ARRAY.CONSTRAIN", + H68: "CHOOSECOLS", + H72: "CHOOSEROWS", + H76: "SUMPRODUCT", + H80: "MINVERSE", + H84: "MDETERM", + H88: "MMULT", + H92: "SUMX2MY2", + H96: "SUMX2PY2", + H100: "SUMXMY2", + H104: "TOCOL", + H110: "TOROW", + H114: "SPLIT", + H117: "HSTACK", + H121: "VSTACK", + H127: "WRAPCOLS", + H132: "WRAPROWS", + I1: "Hours Played", + I2: "1204.7", + I3: "500.9", + I4: "252.4", + I5: "4701.3", + I6: "12.1", + I7: "4000", + I8: "12052", + I9: "4890.1", + I12: "Hours Played", + I13: "<4500", + I24: "Rates", + I25: "0.1", + I26: "0.15", + I27: "0.25", + I34: "=IF(AND(L34=N34, M34=O34), 1, 0)", + I38: "=IF(AND(K38=M38, L38=N38, K39=M39, L39=N39), 1, 0)", + I42: "=IF(AND(L42=N42, M42=O42), 1, 0)", + I46: "=IF(AND(L46=N46, M46=O46, L47=N47, M47=O47), 1, 0)", + I50: "=IF(AND(J50=L50, K50=M50, J51=L51, K51=M51), 1, 0)", + I54: "=IF(AND(L54=M54, L55=M55, L56=M56, L57=M57), 1, 0)", + I60: "=IF(AND(L60=M60, L61=M61), 1, 0)", + I64: "=IF(AND(L64=M64, L65=M65), 1, 0)", + I68: "=IF(AND(L68=M68, L69=M69), 1, 0)", + I72: "=IF(AND(L72=N72, M72=O72), 1, 0)", + I76: "=IF(AND(L76=M76), 1, 0)", + I80: "=IF(AND(L80=N80, M80=O80, L81=N81, M81=O81), 1, 0)", + I84: "=IF(AND(L84=M84), 1, 0)", + I88: "=IF(AND(L88=N88, M88=O88, L89=N89, M89=O89), 1, 0)", + I92: "=IF(AND(L92=M92), 1, 0)", + I96: "=IF(AND(L96=M96), 1, 0)", + I100: "=IF(AND(L100=M100), 1, 0)", + I104: "=IF(AND(L104=M104, L105=M105, L106=M106, L107=M107), 1, 0)", + I110: "=IF(AND(L110=P110, M110=Q110, N110=R110, O110=S110), 1, 0)", + I114: "=IF(AND(K114=O114, L114=P114, M114=Q114, N114=R114), 1, 0)", + I117: "=IF(AND(L117=P117, M117=Q117, N117=R117, O117=S117), 1, 0)", + I121: "=IF(AND(L121=M121, L122=M122, L123=M123, L124=M124), 1, 0)", + I127: "=IF(AND(K127=L127, K128=L128, K129=L129), 1, 0)", + I132: "=IF(AND(K132=N132, L132=O132, M132=P132), 1, 0)", + J1: "Tot. Score", + J2: "25618", + J3: "23000", + J4: "110120.5", + J5: "50024", + J6: "2", + J7: "189576", + J8: "256018", + J9: "5000", + J12: "Tot. Score", + J13: ">42000", + J24: "Dates", + J25: "=DATE(2020, 01, 01)", + J26: "=DATE(2021, 01, 01)", + J27: "=DATE(2022, 01, 01)", + J33: "Arguments", + J34: "1", + J35: "1", + J37: "Arguments", + J38: "1", + J41: "Arguments", + J42: "1", + J43: "0", + J45: "Arguments", + J46: "1", + J47: "3", + J49: "Results", + J50: "=MUNIT(2)", + J53: "Arguments", + J54: "1", + J55: "3", + J59: "Arguments", + J60: "1", + J61: "0", + J63: "Arguments", + J64: "1", + J65: "3", + J67: "Arguments", + J68: "1", + J69: "3", + J71: "Arguments", + J72: "1", + J73: "3", + J75: "Arguments", + J76: "1", + J77: "3", + J79: "Arguments", + J80: "1", + J81: "3", + J83: "Arguments", + J84: "1", + J85: "3", + J87: "Arguments", + J88: "1", + J89: "3", + J91: "Arguments", + J92: "1", + J93: "3", + J95: "Arguments", + J96: "1", + J97: "3", + J99: "Arguments", + J100: "1", + J101: "3", + J103: "Arguments", + J104: "1", + J105: "3", + J109: "Arguments", + J110: "1", + J111: "3", + J113: "Arguments", + J114: "Hello there; General Kenobi", + J116: "Arguments", + J117: "1", + J118: "3", + J120: "Arguments", + J121: "1", + J122: "3", + J126: "Arguments", + J127: "1", + J128: "3", + J131: "Arguments", + J132: "1", + J133: "3", + K1: "Rank (lower the better)", + K2: "5", + K3: "7", + K4: "3", + K5: "4", + K6: "1000", + K7: "2", + K8: "1", + K9: "30", + K12: "Rank (lower the better)", + K13: ">25", + K34: "2", + K35: "2", + K37: "Results", + K38: "=EXPAND(J38, 2, 2, 0)", + K42: "2", + K43: "4", + K46: "2", + K47: "4", + K54: "2", + K55: "4", + K60: "2", + K61: "4", + K64: "2", + K65: "4", + K68: "2", + K69: "4", + K72: "2", + K73: "4", + K76: "2", + K77: "4", + K80: "2", + K81: "4", + K84: "2", + K85: "4", + K88: "2", + K89: "4", + K92: "2", + K93: "4", + K96: "2", + K97: "4", + K100: "2", + K101: "4", + K104: "2", + K105: "4", + K110: "2", + K111: "4", + K113: "Results", + K114: '=SPLIT(J114, " ")', + K117: "2", + K118: "4", + K121: "2", + K122: "4", + K126: "Results", + K127: "=WRAPCOLS(J127:J128, 3, 0)", + K131: "Results", + K132: "=WRAPROWS(J132:J133, 3, 0)", + L33: "Results", + L34: "=UNIQUE(J34:K35)", + L41: "Results", + L42: "=FILTER(J42:K43, J42:J43)", + L45: "Results", + L46: "=TRANSPOSE(J46:K47)", + L49: "Expected", + L50: "1", + L51: "0", + L53: "Results", + L54: "=FLATTEN(J54:K55)", + L59: "Results", + L60: "=FREQUENCY(J60:K61, 2)", + L63: "Results", + L64: "=ARRAY.CONSTRAIN(J64:K65, 2, 1)", + L67: "Results", + L68: "=CHOOSECOLS(J68:K69, 2)", + L71: "Results", + L72: "=CHOOSEROWS(J72:K73, 2)", + L75: "Results", + L76: "=SUMPRODUCT(J76:J77, K76:K77)", + L79: "Results", + L80: "=MINVERSE(J80:K81)", + L83: "Results", + L84: "=MDETERM(J84:K85)", + L87: "Results", + L88: "=MMULT(J88:K89, J88:K89)", + L91: "Results", + L92: "=SUMX2MY2(J92:J93, K92:K93)", + L95: "Results", + L96: "=SUMX2PY2(J96:J97, K96:K97)", + L99: "Results", + L100: "=SUMXMY2(J100:J101, K100:K101)", + L103: "Results", + L104: "=TOCOL(J104:K105)", + L109: "Results", + L110: "=TOROW(J110:K111)", + L116: "Results", + L117: "=HSTACK(J117:K117, J118:K118)", + L120: "Results", + L121: "=VSTACK(J121:J122, K121:K122)", + L126: "Expected", + L127: "1", + L128: "3", + L129: "0", + M37: "Expected", + M38: "1", + M39: "0", + M50: "0", + M51: "1", + M53: "Expected", + M54: "1", + M55: "2", + M56: "3", + M57: "4", + M59: "Expected", + M60: "3", + M61: "1", + M63: "Expected", + M64: "1", + M65: "3", + M67: "Expected", + M68: "2", + M69: "4", + M75: "Expected", + M76: "14", + M83: "Expected", + M84: "-2", + M91: "Expected", + M92: "-10", + M95: "Expected", + M96: "30", + M99: "Expected", + M100: "2", + M103: "Expected", + M104: "1", + M105: "2", + M106: "3", + M107: "4", + M120: "Expected", + M121: "1", + M122: "3", + M123: "2", + M124: "4", + N1: "0.1", + N2: "0.2", + N3: "0.4", + N4: "0.5", + N5: "0.6", + N6: "A", + N7: "TRUE", + N8: "FALSE", + N33: "Expected", + N34: "1", + N38: "0", + N39: "0", + N41: "Expected", + N42: "1", + N45: "Expected", + N46: "1", + N47: "2", + N71: "Expected", + N72: "3", + N79: "Expected", + N80: "-2", + N81: "1.5", + N87: "Expected", + N88: "7", + N89: "15", + N131: "Expected", + N132: "1", + O34: "2", + O42: "2", + O46: "3", + O47: "4", + O72: "4", + O80: "1", + O81: "-0.5", + O88: "10", + O89: "22", + O113: "Expected", + O114: "Hello", + O132: "3", + P109: "Expected", + P110: "1", + P114: "there;", + P116: "Expected", + P117: "1", + P132: "0", + Q110: "2", + Q114: "General", + Q117: "2", + R110: "3", + R114: "Kenobi", + R117: "3", + S110: "4", + S117: "4", + }, + styles: { + "H24:J24": 10, + "G1:K1": 10, + "G12:K12": 10, + }, + formats: { + C40: 3, + "C58:C59": 3, + "C157:C158": 3, + C142: 4, + }, + borders: { + H33: 1, + H37: 1, + H41: 1, + H45: 1, + H49: 1, + H53: 1, + H59: 1, + H63: 1, + H67: 1, + H71: 1, + H75: 1, + H79: 1, + H83: 1, + H87: 1, + H91: 1, + H95: 1, + H99: 1, + H103: 1, + H109: 1, + H113: 1, + H116: 1, + H120: 1, + H126: 1, + H131: 1, + L131: 1, + M49: 1, + "L113:M113": 1, + N37: 1, + "M109:N109": 1, + "M116:N116": 1, + O33: 1, + O41: 1, + O45: 1, + O71: 1, + O79: 1, + O87: 1, + "O131:P131": 1, + "P113:R113": 1, + "Q109:S109": 1, + "Q116:S116": 1, + H34: 2, + H38: 2, + H42: 2, + H46: 2, + H50: 2, + H54: 2, + H60: 2, + H64: 2, + H68: 2, + H72: 2, + H76: 2, + H80: 2, + H84: 2, + H88: 2, + H92: 2, + H96: 2, + H100: 2, + H104: 2, + H110: 2, + H114: 2, + H117: 2, + H121: 2, + H127: 2, + H132: 2, + L132: 2, + M50: 2, + "L114:M114": 2, + N38: 2, + "M110:N110": 2, + "M117:N117": 2, + O34: 2, + O42: 2, + O46: 2, + O72: 2, + O80: 2, + O88: 2, + "O132:P132": 2, + "P114:R114": 2, + "Q110:S110": 2, + "Q117:S117": 2, + I33: 3, + I37: 3, + I41: 3, + I45: 3, + I49: 3, + I53: 3, + I59: 3, + I63: 3, + I67: 3, + I71: 3, + I75: 3, + I79: 3, + I83: 3, + I87: 3, + I91: 3, + I95: 3, + I99: 3, + I103: 3, + I109: 3, + I113: 3, + I116: 3, + I120: 3, + I126: 3, + I131: 3, + K33: 3, + K41: 3, + K45: 3, + K49: 3, + K53: 3, + K59: 3, + K63: 3, + K67: 3, + K71: 3, + K75: 3, + K79: 3, + K83: 3, + K87: 3, + K91: 3, + K95: 3, + K99: 3, + K103: 3, + K109: 3, + K116: 3, + K120: 3, + L37: 3, + M33: 3, + M41: 3, + M45: 3, + M71: 3, + M79: 3, + M87: 3, + M131: 3, + N113: 3, + O109: 3, + O116: 3, + I34: 4, + I38: 4, + I42: 4, + I46: 4, + I50: 4, + I54: 4, + I60: 4, + I64: 4, + I68: 4, + I72: 4, + I76: 4, + I80: 4, + I84: 4, + I88: 4, + I92: 4, + I96: 4, + I100: 4, + I104: 4, + I110: 4, + I114: 4, + I117: 4, + I121: 4, + I127: 4, + I132: 4, + K34: 4, + K42: 4, + K46: 4, + K50: 4, + K54: 4, + K60: 4, + K64: 4, + K68: 4, + K72: 4, + K76: 4, + K80: 4, + K84: 4, + K88: 4, + K92: 4, + K96: 4, + K100: 4, + K104: 4, + K110: 4, + K117: 4, + K121: 4, + L38: 4, + M34: 4, + M42: 4, + M46: 4, + M72: 4, + M80: 4, + M88: 4, + M132: 4, + N114: 4, + O110: 4, + O117: 4, + I35: 5, + I39: 5, + I43: 5, + I47: 5, + I51: 5, + "I55:I57": 5, + I61: 5, + I65: 5, + I69: 5, + I73: 5, + I77: 5, + I81: 5, + I85: 5, + I89: 5, + I93: 5, + I97: 5, + I101: 5, + "I105:I107": 5, + I111: 5, + I118: 5, + "I122:I124": 5, + "I128:I129": 5, + I133: 5, + K35: 5, + K43: 5, + K47: 5, + K51: 5, + "K55:K57": 5, + K61: 5, + K65: 5, + K69: 5, + K73: 5, + K77: 5, + K81: 5, + K85: 5, + K89: 5, + K93: 5, + K97: 5, + K101: 5, + "K105:K107": 5, + K111: 5, + K118: 5, + "K122:K124": 5, + L39: 5, + M35: 5, + M43: 5, + M47: 5, + M73: 5, + M81: 5, + M89: 5, + M133: 5, + O111: 5, + O118: 5, + J33: 6, + J41: 6, + J45: 6, + J49: 6, + J53: 6, + J59: 6, + J63: 6, + J67: 6, + J71: 6, + J75: 6, + J79: 6, + J83: 6, + J87: 6, + J91: 6, + J95: 6, + J99: 6, + J103: 6, + J109: 6, + J116: 6, + J120: 6, + K37: 6, + K113: 6, + K131: 6, + L33: 6, + L41: 6, + L45: 6, + L49: 6, + L71: 6, + L79: 6, + L87: 6, + L109: 6, + L116: 6, + L126: 6, + M37: 6, + M53: 6, + M59: 6, + M63: 6, + M67: 6, + M75: 6, + M83: 6, + M91: 6, + M95: 6, + M99: 6, + M103: 6, + M120: 6, + N33: 6, + N41: 6, + N45: 6, + N71: 6, + N79: 6, + N87: 6, + N131: 6, + O113: 6, + P109: 6, + P116: 6, + J34: 7, + J42: 7, + J46: 7, + J50: 7, + J54: 7, + J60: 7, + J64: 7, + J68: 7, + J72: 7, + J76: 7, + J80: 7, + J84: 7, + J88: 7, + J92: 7, + J96: 7, + J100: 7, + J104: 7, + J110: 7, + J117: 7, + J121: 7, + K38: 7, + K114: 7, + K132: 7, + L34: 7, + L42: 7, + L46: 7, + L50: 7, + L72: 7, + L80: 7, + L88: 7, + L110: 7, + L117: 7, + L127: 7, + M38: 7, + M54: 7, + M60: 7, + M64: 7, + M68: 7, + M76: 7, + M84: 7, + M92: 7, + M96: 7, + M100: 7, + M104: 7, + M121: 7, + N34: 7, + N42: 7, + N46: 7, + N72: 7, + N80: 7, + N88: 7, + N132: 7, + O114: 7, + P110: 7, + P117: 7, + J35: 8, + J43: 8, + J47: 8, + J51: 8, + "J55:J57": 8, + J61: 8, + J65: 8, + J69: 8, + J73: 8, + J77: 8, + J81: 8, + J85: 8, + J89: 8, + J93: 8, + J97: 8, + J101: 8, + "J105:J107": 8, + J111: 8, + J118: 8, + "J122:J124": 8, + K39: 8, + K133: 8, + L35: 8, + L43: 8, + L47: 8, + L51: 8, + L73: 8, + L81: 8, + L89: 8, + L111: 8, + L118: 8, + "L128:L129": 8, + M39: 8, + "M55:M57": 8, + M61: 8, + M65: 8, + M69: 8, + M77: 8, + M85: 8, + M93: 8, + M97: 8, + M101: 8, + "M105:M107": 8, + "M122:M124": 8, + N35: 8, + N43: 8, + N47: 8, + N73: 8, + N81: 8, + N89: 8, + N133: 8, + P111: 8, + P118: 8, + J37: 9, + J113: 9, + J131: 9, + "J126:K126": 9, + L53: 9, + L59: 9, + L63: 9, + L67: 9, + L75: 9, + L83: 9, + L91: 9, + L95: 9, + L99: 9, + L103: 9, + L120: 9, + J38: 10, + J114: 10, + J132: 10, + "J127:K127": 10, + L54: 10, + L60: 10, + L64: 10, + L68: 10, + L76: 10, + L84: 10, + L92: 10, + L96: 10, + L100: 10, + L104: 10, + L121: 10, + J39: 11, + J133: 11, + "J128:K129": 11, + "L55:L57": 11, + L61: 11, + L65: 11, + L69: 11, + L77: 11, + L85: 11, + L93: 11, + L97: 11, + L101: 11, + "L105:L107": 11, + "L122:L124": 11, }, conditionalFormats: [ { @@ -2231,7 +2644,9 @@ export const demoData = { values: ["1"], operator: "Equal", type: "CellIsRule", - style: { fillColor: "#B6D7A8" }, + style: { + fillColor: "#B6D7A8", + }, }, }, { @@ -2241,7 +2656,9 @@ export const demoData = { values: ["0"], operator: "Equal", type: "CellIsRule", - style: { fillColor: "#E06666" }, + style: { + fillColor: "#E06666", + }, }, }, { @@ -2251,7 +2668,9 @@ export const demoData = { values: ["1"], operator: "Equal", type: "CellIsRule", - style: { fillColor: "#B6D7A8" }, + style: { + fillColor: "#B6D7A8", + }, }, }, { @@ -2261,10 +2680,13 @@ export const demoData = { values: ["0"], operator: "Equal", type: "CellIsRule", - style: { fillColor: "#E06666" }, + style: { + fillColor: "#E06666", + }, }, }, ], + dataValidationRules: [], figures: [], tables: [ { @@ -2312,8 +2734,11 @@ export const demoData = { ], areGridLinesVisible: true, isVisible: true, - headerGroups: { ROW: [], COL: [] }, - dataValidationRules: [], + color: "#6AA84F", + headerGroups: { + ROW: [], + COL: [], + }, }, { id: "border_sheet", @@ -2324,49 +2749,67 @@ export const demoData = { cols: {}, merges: [], cells: { - B2: { content: "left", border: 8 }, - B4: { content: "top", border: 2 }, - B6: { content: "right", border: 5 }, - B8: { content: "bottom", border: 1 }, - B10: { content: "all", border: 13 }, - D2: { content: "thin (default)", border: 13 }, - D4: { content: "medium", border: 19 }, - D6: { content: "thick", border: 21 }, - D8: { content: "dashed", border: 23 }, - D10: { content: "dotted", border: 25 }, - F2: { content: "mixed", border: 33 }, - A2: { border: 5 }, - A10: { border: 5 }, - B3: { border: 1 }, - B9: { border: 12 }, - B11: { border: 2 }, - C2: { border: 5 }, - C4: { border: 14 }, - C6: { border: 15 }, - C8: { border: 16 }, - C10: { border: 17 }, - D1: { border: 1 }, - D3: { border: 18 }, - D5: { border: 20 }, - D7: { border: 22 }, - D9: { border: 24 }, - D11: { border: 26 }, - E2: { border: 27 }, - E4: { border: 28 }, - E6: { border: 29 }, - E8: { border: 30 }, - E10: { border: 31 }, - F1: { border: 32 }, - F3: { border: 34 }, - G2: { border: 35 }, + B2: "left", + B4: "top", + B6: "right", + B8: "bottom", + B10: "all", + D2: "thin (default)", + D4: "medium", + D6: "thick", + D8: "dashed", + D10: "dotted", + F2: "mixed", + }, + styles: {}, + formats: {}, + borders: { + B3: 1, + B8: 1, + D1: 1, + B4: 2, + B11: 2, + A2: 5, + A10: 5, + B6: 5, + C2: 5, + B2: 8, + B9: 12, + B10: 13, + D2: 13, + C4: 14, + C6: 15, + C8: 16, + C10: 17, + D3: 18, + D4: 19, + D5: 20, + D6: 21, + D7: 22, + D8: 23, + D9: 24, + D10: 25, + D11: 26, + E2: 27, + E4: 28, + E6: 29, + E8: 30, + E10: 31, + F1: 32, + F2: 33, + F3: 34, + G2: 35, }, conditionalFormats: [], + dataValidationRules: [], figures: [], tables: [], areGridLinesVisible: true, isVisible: true, - headerGroups: { ROW: [], COL: [] }, - dataValidationRules: [], + headerGroups: { + ROW: [], + COL: [], + }, }, { id: "sh5", @@ -2374,42 +2817,97 @@ export const demoData = { colNumber: 26, rowNumber: 50, rows: {}, - cols: { 5: { size: 150 } }, + cols: { + 5: { + size: 150, + }, + }, merges: [], cells: { - A1: { style: 10, content: "Data Validation Corner" }, - A2: { style: 10 }, - A3: { content: "Perceval" }, - A4: { content: "Arthur" }, - A5: { content: "Gauvin" }, - A6: { content: "Yvain" }, - B2: { style: 10, content: "Age" }, - B3: { content: "unknown" }, - B4: { content: "42" }, - B5: { content: "20" }, - B6: { content: "19" }, - C2: { style: 10, content: "Birthday" }, - C3: { format: 3, content: "unknown" }, - C4: { format: 3, content: "-453874" }, - C5: { format: 3, content: "-445626" }, - C6: { format: 3, content: "-445248" }, - D2: { style: 10, content: "Origin" }, - D3: { content: "Wales" }, - D4: { content: "Britain" }, - D5: { content: "Britain" }, - D6: { content: "Britain" }, - E2: { style: 10, content: "Is king ?" }, - E3: { style: 7, content: "FALSE" }, - E4: { style: 7, content: "TRUE" }, - E5: { style: 7, content: "FALSE" }, - E6: { style: 7, content: "FALSE" }, - F2: { style: 10, content: "Email" }, - F3: { content: "perceval@odoo.com" }, - F4: { content: "arthur@kaamelott.fr" }, - F5: { content: "Gauvin@odoo.com" }, - F6: { content: "Yvain{at}odoo.com" }, + A1: "Data Validation Corner", + A3: "Perceval", + A4: "Arthur", + A5: "Gauvin", + A6: "Yvain", + B2: "Age", + B3: "unknown", + B4: "42", + B5: "20", + B6: "19", + C2: "Birthday", + C3: "unknown", + C4: "-453874", + C5: "-445626", + C6: "-445248", + D2: "Origin", + D3: "Wales", + D4: "Britain", + D5: "Britain", + D6: "Britain", + E2: "Is king ?", + E3: "FALSE", + E4: "TRUE", + E5: "FALSE", + E6: "FALSE", + F2: "Email", + F3: "perceval@odoo.com", + F4: "arthur@kaamelott.fr", + F5: "Gauvin@odoo.com", + F6: "Yvain{at}odoo.com", }, + styles: { + "E3:E6": 7, + "A1:A2": 10, + "B2:F2": 10, + }, + formats: { + "C3:C6": 3, + }, + borders: {}, conditionalFormats: [], + dataValidationRules: [ + { + id: "dv1", + criterion: { + type: "isBetween", + values: ["0", "100"], + }, + ranges: ["B3:B6"], + }, + { + id: "dv2", + criterion: { + type: "dateIsValid", + values: [], + }, + ranges: ["C3:C6"], + }, + { + id: "dv3", + criterion: { + type: "isValueInList", + values: ["Wales", "Britain", "Rome"], + displayStyle: "arrow", + }, + ranges: ["D3:D6"], + }, + { + id: "dv4", + criterion: { + type: "isBoolean", + values: [], + }, + ranges: ["E3:E6"], + }, + { + id: "dv5", + criterion: { + type: "textIsEmail", + values: [], + }, + ranges: ["F3:F6"], + }, + ], figures: [], tables: [ { @@ -2429,22 +2927,10 @@ export const demoData = { ], areGridLinesVisible: true, isVisible: true, - headerGroups: { ROW: [], COL: [] }, - dataValidationRules: [ - { id: "dv1", criterion: { type: "isBetween", values: ["0", "100"] }, ranges: ["B3:B6"] }, - { id: "dv2", criterion: { type: "dateIsValid", values: [] }, ranges: ["C3:C6"] }, - { - id: "dv3", - criterion: { - type: "isValueInList", - values: ["Wales", "Britain", "Rome"], - displayStyle: "arrow", - }, - ranges: ["D3:D6"], - }, - { id: "dv4", criterion: { type: "isBoolean", values: [] }, ranges: ["E3:E6"] }, - { id: "dv5", criterion: { type: "textIsEmail", values: [] }, ranges: ["F3:F6"] }, - ], + headerGroups: { + ROW: [], + COL: [], + }, }, { id: "pivot", @@ -2452,192 +2938,220 @@ export const demoData = { colNumber: 26, rowNumber: 100, rows: {}, - color: "#FF9900", cols: { - 0: { size: 127 }, - 1: { size: 191 }, - 2: { size: 102 }, - 3: { size: 221 }, - 4: { size: 93 }, - 5: { size: 118 }, - 6: { size: 95 }, - 7: { size: 73 }, + 0: { + size: 127, + }, + 1: { + size: 191, + }, + 2: { + size: 102, + }, + 3: { + size: 221, + }, + 4: { + size: 93, + }, + 5: { + size: 118, + }, + 6: { + size: 95, + }, + 7: { + size: 73, + }, }, merges: [], cells: { - A1: { style: 11, content: "Created on" }, - A2: { format: 5, content: "45385.355219907404" }, - A3: { format: 5, content: "45385.371712962966" }, - A4: { format: 5, content: "45385.370416666665" }, - A5: { format: 5, content: "45384.24826388889" }, - A6: { format: 5, content: "45379.20659722222" }, - A7: { format: 5, content: "45384.24826388889" }, - A8: { format: 5, content: "45384.24826388889" }, - A9: { format: 5, content: "45378.20659722222" }, - A10: { format: 5, content: "45383.24826388889" }, - A11: { format: 5, content: "45384.24826388889" }, - A12: { format: 5, content: "45385.35695601852" }, - A13: { format: 5, content: "45325.20659722222" }, - A14: { format: 5, content: "45354.20659722222" }, - A15: { format: 5, content: "45377.20659722222" }, - A16: { format: 5, content: "45378.20659722222" }, - A17: { format: 5, content: "45378.20659722222" }, - A18: { format: 5, content: "45382.24826388889" }, - A19: { format: 5, content: "45384.24826388889" }, - A20: { format: 5, content: "45384.24826388889" }, - A21: { format: 5, content: "45378.20659722222" }, - A22: { format: 5, content: "45378.20659722222" }, - A25: { content: '=PIVOT("1")' }, - B1: { style: 11, content: "Opportunity" }, - B2: { content: "Alice's opportunity" }, - B3: { content: "Roger's opportunity" }, - B4: { content: "interested in tables" }, - B5: { content: "Interest in your products" }, - B6: { content: "Open Space Design" }, - B7: { content: "Modern Open Space" }, - B8: { content: "Office Design and Architecture" }, - B9: { content: "Distributor Contract" }, - B10: { content: "Furnitures" }, - B11: { content: "Office Design Project" }, - B12: { content: "Roger's opportunity" }, - B13: { content: "Quote for 600 Chairs" }, - B14: { content: "Devis pour 150 tapis" }, - B15: { content: "5 VP Chairs" }, - B16: { content: "Customizable Desk" }, - B17: { content: "10 Computer Desks" }, - B18: { content: "Potential Distributor" }, - B19: { content: "Info about services" }, - B20: { content: "Quote for 12 Tables" }, - B21: { content: "Need 20 Desks" }, - B22: { content: "Access to Online Catalog" }, - C1: { style: 11, content: "Contact Name" }, - C2: { content: "Alice" }, - C4: { content: "Charlie" }, - C7: { content: "Roger" }, - C9: { content: "Kevin" }, - C10: { content: "Robin" }, - C13: { content: "Erik" }, - C14: { content: "Erik" }, - C15: { content: "Benjamin" }, - C16: { content: "Eden" }, - C17: { content: "Carlos" }, - C18: { content: "Fernando" }, - C20: { content: "Charles" }, - D1: { style: 11, content: "Email" }, - D2: { content: "admin@yourcompany.example.com" }, - D3: { content: "info@yourcompany.example.com" }, - D4: { content: "charlie@example.com" }, - D5: { content: "info@mycompany.com" }, - D6: { content: "info@mycompany.com" }, - D7: { content: "henry@info.com" }, - D8: { content: "info@example.com" }, - D9: { content: "john@tech.info" }, - D10: { content: "info@example.com" }, - D11: { content: "info@mycompany.com" }, - D12: { content: "roger@yourcompany.example.com" }, - D13: { content: "erik@test.com" }, - D14: { content: "erik@test.com" }, - D15: { content: "roger@yourcompany.example.com" }, - D16: { content: "roger@yourcompany.example.com" }, - D17: { content: "info@example.com" }, - D18: { content: "olivier@inc.sa" }, - D19: { content: "info@mycompany.com" }, - D20: { content: "will@example.com" }, - D21: { content: "info@mycompany.net" }, - D22: { content: "charles@example.com" }, - E1: { style: 11, content: "Salesperson" }, - E2: { content: "Alice" }, - E3: { content: "Alice" }, - E4: { content: "Alice" }, - E5: { content: "Bob" }, - E6: { content: "Bob" }, - E7: { content: "Alice" }, - E8: { content: "Alice" }, - E9: { content: "Alice" }, - E10: { content: "Alice" }, - E11: { content: "Bob" }, - E12: { content: "Alice" }, - E13: { content: "Alice" }, - E14: { content: "Alice" }, - E15: { content: "Alice" }, - E16: { content: "Bob" }, - E17: { content: "Bob" }, - E18: { content: "Bob" }, - E19: { content: "Alice" }, - E20: { content: "Alice" }, - E21: { content: "Alice" }, - E22: { content: "Bob" }, - F1: { style: 11, content: "Expected Revenue" }, - F5: { format: 6, content: "2000" }, - F6: { format: 6, content: "11000" }, - F7: { format: 6, content: "4500" }, - F8: { format: 6, content: "9000" }, - F9: { format: 6, content: "19800" }, - F10: { format: 6, content: "3800" }, - F11: { format: 6, content: "24000" }, - F13: { format: 6, content: "22500" }, - F14: { format: 6, content: "40000" }, - F15: { format: 6, content: "5600" }, - F16: { format: 6, content: "15000" }, - F17: { format: 6, content: "35000" }, - F18: { format: 6, content: "1000" }, - F19: { format: 6, content: "25000" }, - F20: { format: 6, content: "40000" }, - F21: { format: 6, content: "60000" }, - F22: { format: 6, content: "2000" }, - G1: { style: 11, content: "Expected MRR" }, - G7: { format: 6, content: "333.33" }, - H1: { style: 11, content: "Stage" }, - H2: { content: "New" }, - H3: { content: "New" }, - H4: { content: "New" }, - H5: { content: "Won" }, - H6: { content: "Proposition" }, - H7: { content: "Won" }, - H8: { content: "Proposition" }, - H9: { content: "Won" }, - H10: { content: "Qualified" }, - H11: { content: "New" }, - H12: { content: "New" }, - H13: { content: "Qualified" }, - H14: { content: "New" }, - H15: { content: "Proposition" }, - H16: { content: "Proposition" }, - H17: { content: "Qualified" }, - H18: { content: "Qualified" }, - H19: { content: "Qualified" }, - H20: { content: "New" }, - H21: { content: "Proposition" }, - H22: { content: "Won" }, - I1: { style: 11, content: "Active" }, - I2: { content: "TRUE" }, - I3: { content: "TRUE" }, - I4: { content: "TRUE" }, - I5: { content: "TRUE" }, - I6: { content: "TRUE" }, - I7: { content: "TRUE" }, - I8: { content: "TRUE" }, - I9: { content: "TRUE" }, - I10: { content: "TRUE" }, - I11: { content: "TRUE" }, - I12: { content: "FALSE" }, - I13: { content: "FALSE" }, - I14: { content: "FALSE" }, - I15: { content: "FALSE" }, - I16: { content: "FALSE" }, - I17: { content: "FALSE" }, - I18: { content: "FALSE" }, - I19: { content: "FALSE" }, - I20: { content: "FALSE" }, - I21: { content: "FALSE" }, - I22: { content: "FALSE" }, - K1: { style: 11, content: "Commissions" }, - K2: { content: "Alice" }, - K3: { content: "Bob" }, - L2: { content: "0.01", format: 1 }, - L3: { content: "0.02", format: 1 }, + A1: "Created on", + A2: "45385.355219907404", + A3: "45385.371712962966", + A4: "45385.370416666665", + A5: "45384.24826388889", + A6: "45379.20659722222", + A7: "45384.24826388889", + A8: "45384.24826388889", + A9: "45378.20659722222", + A10: "45383.24826388889", + A11: "45384.24826388889", + A12: "45385.35695601852", + A13: "45325.20659722222", + A14: "45354.20659722222", + A15: "45377.20659722222", + A16: "45378.20659722222", + A17: "45378.20659722222", + A18: "45382.24826388889", + A19: "45384.24826388889", + A20: "45384.24826388889", + A21: "45378.20659722222", + A22: "45378.20659722222", + A25: '=PIVOT("1")', + B1: "Opportunity", + B2: "Alice's opportunity", + B3: "Roger's opportunity", + B4: "interested in tables", + B5: "Interest in your products", + B6: "Open Space Design", + B7: "Modern Open Space", + B8: "Office Design and Architecture", + B9: "Distributor Contract", + B10: "Furnitures", + B11: "Office Design Project", + B12: "Roger's opportunity", + B13: "Quote for 600 Chairs", + B14: "Devis pour 150 tapis", + B15: "5 VP Chairs", + B16: "Customizable Desk", + B17: "10 Computer Desks", + B18: "Potential Distributor", + B19: "Info about services", + B20: "Quote for 12 Tables", + B21: "Need 20 Desks", + B22: "Access to Online Catalog", + C1: "Contact Name", + C2: "Alice", + C4: "Charlie", + C7: "Roger", + C9: "Kevin", + C10: "Robin", + C13: "Erik", + C14: "Erik", + C15: "Benjamin", + C16: "Eden", + C17: "Carlos", + C18: "Fernando", + C20: "Charles", + D1: "Email", + D2: "admin@yourcompany.example.com", + D3: "info@yourcompany.example.com", + D4: "charlie@example.com", + D5: "info@mycompany.com", + D6: "info@mycompany.com", + D7: "henry@info.com", + D8: "info@example.com", + D9: "john@tech.info", + D10: "info@example.com", + D11: "info@mycompany.com", + D12: "roger@yourcompany.example.com", + D13: "erik@test.com", + D14: "erik@test.com", + D15: "roger@yourcompany.example.com", + D16: "roger@yourcompany.example.com", + D17: "info@example.com", + D18: "olivier@inc.sa", + D19: "info@mycompany.com", + D20: "will@example.com", + D21: "info@mycompany.net", + D22: "charles@example.com", + E1: "Salesperson", + E2: "Alice", + E3: "Alice", + E4: "Alice", + E5: "Bob", + E6: "Bob", + E7: "Alice", + E8: "Alice", + E9: "Alice", + E10: "Alice", + E11: "Bob", + E12: "Alice", + E13: "Alice", + E14: "Alice", + E15: "Alice", + E16: "Bob", + E17: "Bob", + E18: "Bob", + E19: "Alice", + E20: "Alice", + E21: "Alice", + E22: "Bob", + F1: "Expected Revenue", + F5: "2000", + F6: "11000", + F7: "4500", + F8: "9000", + F9: "19800", + F10: "3800", + F11: "24000", + F13: "22500", + F14: "40000", + F15: "5600", + F16: "15000", + F17: "35000", + F18: "1000", + F19: "25000", + F20: "40000", + F21: "60000", + F22: "2000", + G1: "Expected MRR", + G7: "333.33", + H1: "Stage", + H2: "New", + H3: "New", + H4: "New", + H5: "Won", + H6: "Proposition", + H7: "Won", + H8: "Proposition", + H9: "Won", + H10: "Qualified", + H11: "New", + H12: "New", + H13: "Qualified", + H14: "New", + H15: "Proposition", + H16: "Proposition", + H17: "Qualified", + H18: "Qualified", + H19: "Qualified", + H20: "New", + H21: "Proposition", + H22: "Won", + I1: "Active", + I2: "TRUE", + I3: "TRUE", + I4: "TRUE", + I5: "TRUE", + I6: "TRUE", + I7: "TRUE", + I8: "TRUE", + I9: "TRUE", + I10: "TRUE", + I11: "TRUE", + I12: "FALSE", + I13: "FALSE", + I14: "FALSE", + I15: "FALSE", + I16: "FALSE", + I17: "FALSE", + I18: "FALSE", + I19: "FALSE", + I20: "FALSE", + I21: "FALSE", + I22: "FALSE", + K1: "Commissions", + K2: "Alice", + K3: "Bob", + L2: "0.01", + L3: "0.02", + }, + styles: { + "A1:I1": 11, + K1: 11, + }, + formats: { + "L2:L3": 1, + "A2:A22": 5, + "F5:F11": 6, + "F13:F22": 6, + G7: 6, }, + borders: {}, conditionalFormats: [], + dataValidationRules: [], figures: [], tables: [ { @@ -2671,8 +3185,11 @@ export const demoData = { ], areGridLinesVisible: true, isVisible: true, - headerGroups: { ROW: [], COL: [] }, - dataValidationRules: [], + color: "#FF9900", + headerGroups: { + ROW: [], + COL: [], + }, }, { id: "cf", @@ -2683,31 +3200,34 @@ export const demoData = { cols: {}, merges: [], cells: { - A1: { content: "Data Bar" }, - A3: { content: "100" }, - A4: { content: "90" }, - A5: { content: "80" }, - A6: { content: "70" }, - A7: { content: "60" }, - A8: { content: "50" }, - A9: { content: "40" }, - A10: { content: "30" }, - A11: { content: "20" }, - A12: { content: "10" }, - A13: { content: "0" }, - B1: { content: "Data Bar based on range A3:A13" }, - B3: { content: "Lorem" }, - B4: { content: "ipsmum" }, - B5: { content: "dolor" }, - B6: { content: "sit" }, - B7: { content: "amet" }, - B8: { content: "consectetur" }, - B9: { content: "adipiscing" }, - B10: { content: "elit" }, - B11: { content: "Suspendisse" }, - B12: { content: "vitae" }, - B13: { content: "placerat" }, + A1: "Data Bar", + A3: "100", + A4: "90", + A5: "80", + A6: "70", + A7: "60", + A8: "50", + A9: "40", + A10: "30", + A11: "20", + A12: "10", + A13: "0", + B1: "Data Bar based on range A3:A13", + B3: "Lorem", + B4: "ipsmum", + B5: "dolor", + B6: "sit", + B7: "amet", + B8: "consectetur", + B9: "adipiscing", + B10: "elit", + B11: "Suspendisse", + B12: "vitae", + B13: "placerat", }, + styles: {}, + formats: {}, + borders: {}, conditionalFormats: [ { id: "1", @@ -2727,26 +3247,54 @@ export const demoData = { }, }, ], + dataValidationRules: [], figures: [], tables: [], areGridLinesVisible: true, isVisible: true, - headerGroups: { ROW: [], COL: [] }, - dataValidationRules: [], + headerGroups: { + ROW: [], + COL: [], + }, }, ], styles: { - 1: { bold: true, textColor: "#674EA7", fontSize: 18 }, - 2: { fillColor: "#FFF2CC" }, - 3: { fillColor: "#D9EAD3" }, - 4: { fillColor: "#B6D7A8" }, - 5: { italic: true }, - 6: { strikethrough: true }, - 7: { underline: true }, - 8: { fillColor: "#d9d2e9" }, - 9: { fillColor: "#000000" }, - 10: { bold: true, fontSize: 11 }, - 11: { bold: true }, + 1: { + bold: true, + textColor: "#674EA7", + fontSize: 18, + }, + 2: { + fillColor: "#FFF2CC", + }, + 3: { + fillColor: "#D9EAD3", + }, + 4: { + fillColor: "#B6D7A8", + }, + 5: { + italic: true, + }, + 6: { + strikethrough: true, + }, + 7: { + underline: true, + }, + 8: { + fillColor: "#d9d2e9", + }, + 9: { + fillColor: "#000000", + }, + 10: { + bold: true, + fontSize: 11, + }, + 11: { + bold: true, + }, }, formats: { 1: "0.00%", @@ -2757,79 +3305,356 @@ export const demoData = { 6: "[$$]#,##0.00", }, borders: { - 1: { bottom: { style: "thin", color: "#000" } }, - 2: { top: { style: "thin", color: "#000" } }, - 3: { bottom: { style: "thin", color: "#000" }, right: { style: "thin", color: "#000" } }, - 4: { top: { style: "thin", color: "#000" }, right: { style: "thin", color: "#000" } }, - 5: { right: { style: "thin", color: "#000" } }, - 6: { bottom: { style: "thin", color: "#000" }, left: { style: "thin", color: "#000" } }, - 7: { top: { style: "thin", color: "#000" }, left: { style: "thin", color: "#000" } }, - 8: { left: { style: "thin", color: "#000" } }, + 1: { + bottom: { + style: "thin", + color: "#000", + }, + }, + 2: { + top: { + style: "thin", + color: "#000", + }, + }, + 3: { + bottom: { + style: "thin", + color: "#000", + }, + right: { + style: "thin", + color: "#000", + }, + }, + 4: { + top: { + style: "thin", + color: "#000", + }, + right: { + style: "thin", + color: "#000", + }, + }, + 5: { + right: { + style: "thin", + color: "#000", + }, + }, + 6: { + bottom: { + style: "thin", + color: "#000", + }, + left: { + style: "thin", + color: "#000", + }, + }, + 7: { + top: { + style: "thin", + color: "#000", + }, + left: { + style: "thin", + color: "#000", + }, + }, + 8: { + left: { + style: "thin", + color: "#000", + }, + }, 9: { - bottom: { style: "thin", color: "#000" }, - left: { style: "thin", color: "#000" }, - right: { style: "thin", color: "#000" }, + bottom: { + style: "thin", + color: "#000", + }, + left: { + style: "thin", + color: "#000", + }, + right: { + style: "thin", + color: "#000", + }, }, 10: { - top: { style: "thin", color: "#000" }, - left: { style: "thin", color: "#000" }, - right: { style: "thin", color: "#000" }, + top: { + style: "thin", + color: "#000", + }, + left: { + style: "thin", + color: "#000", + }, + right: { + style: "thin", + color: "#000", + }, + }, + 11: { + left: { + style: "thin", + color: "#000", + }, + right: { + style: "thin", + color: "#000", + }, + }, + 12: { + top: { + style: "thin", + color: "#000", + }, + bottom: { + style: "thin", + color: "#000", + }, }, - 11: { left: { style: "thin", color: "#000" }, right: { style: "thin", color: "#000" } }, - 12: { top: { style: "thin", color: "#000" }, bottom: { style: "thin", color: "#000" } }, 13: { - top: { style: "thin", color: "#000" }, - bottom: { style: "thin", color: "#000" }, - left: { style: "thin", color: "#000" }, - right: { style: "thin", color: "#000" }, + top: { + style: "thin", + color: "#000", + }, + bottom: { + style: "thin", + color: "#000", + }, + left: { + style: "thin", + color: "#000", + }, + right: { + style: "thin", + color: "#000", + }, + }, + 14: { + right: { + style: "medium", + color: "#000", + }, + }, + 15: { + left: { + style: "thin", + color: "#000", + }, + right: { + style: "thick", + color: "#000", + }, + }, + 16: { + right: { + style: "dashed", + color: "#000", + }, + }, + 17: { + left: { + style: "thin", + color: "#000", + }, + right: { + style: "dotted", + color: "#000", + }, + }, + 18: { + top: { + style: "thin", + color: "#000", + }, + bottom: { + style: "medium", + color: "#000", + }, }, - 14: { right: { style: "medium", color: "#000" } }, - 15: { left: { style: "thin", color: "#000" }, right: { style: "thick", color: "#000" } }, - 16: { right: { style: "dashed", color: "#000" } }, - 17: { left: { style: "thin", color: "#000" }, right: { style: "dotted", color: "#000" } }, - 18: { top: { style: "thin", color: "#000" }, bottom: { style: "medium", color: "#000" } }, 19: { - top: { style: "medium", color: "#000" }, - bottom: { style: "medium", color: "#000" }, - left: { style: "medium", color: "#000" }, - right: { style: "medium", color: "#000" }, + top: { + style: "medium", + color: "#000", + }, + bottom: { + style: "medium", + color: "#000", + }, + left: { + style: "medium", + color: "#000", + }, + right: { + style: "medium", + color: "#000", + }, + }, + 20: { + top: { + style: "medium", + color: "#000", + }, + bottom: { + style: "thick", + color: "#000", + }, }, - 20: { top: { style: "medium", color: "#000" }, bottom: { style: "thick", color: "#000" } }, 21: { - top: { style: "thick", color: "#000" }, - bottom: { style: "thick", color: "#000" }, - left: { style: "thick", color: "#000" }, - right: { style: "thick", color: "#000" }, + top: { + style: "thick", + color: "#000", + }, + bottom: { + style: "thick", + color: "#000", + }, + left: { + style: "thick", + color: "#000", + }, + right: { + style: "thick", + color: "#000", + }, + }, + 22: { + top: { + style: "thick", + color: "#000", + }, + bottom: { + style: "dashed", + color: "#000", + }, }, - 22: { top: { style: "thick", color: "#000" }, bottom: { style: "dashed", color: "#000" } }, 23: { - top: { style: "dashed", color: "#000" }, - bottom: { style: "dashed", color: "#000" }, - left: { style: "dashed", color: "#000" }, - right: { style: "dashed", color: "#000" }, + top: { + style: "dashed", + color: "#000", + }, + bottom: { + style: "dashed", + color: "#000", + }, + left: { + style: "dashed", + color: "#000", + }, + right: { + style: "dashed", + color: "#000", + }, + }, + 24: { + top: { + style: "dashed", + color: "#000", + }, + bottom: { + style: "dotted", + color: "#000", + }, }, - 24: { top: { style: "dashed", color: "#000" }, bottom: { style: "dotted", color: "#000" } }, 25: { - top: { style: "dotted", color: "#000" }, - bottom: { style: "dotted", color: "#000" }, - left: { style: "dotted", color: "#000" }, - right: { style: "dotted", color: "#000" }, + top: { + style: "dotted", + color: "#000", + }, + bottom: { + style: "dotted", + color: "#000", + }, + left: { + style: "dotted", + color: "#000", + }, + right: { + style: "dotted", + color: "#000", + }, + }, + 26: { + top: { + style: "dotted", + color: "#000", + }, + }, + 27: { + left: { + style: "thin", + color: "#000", + }, + right: { + style: "thin", + color: "#FF0000", + }, + }, + 28: { + left: { + style: "medium", + color: "#000", + }, + }, + 29: { + left: { + style: "thick", + color: "#000", + }, + }, + 30: { + left: { + style: "dashed", + color: "#000", + }, + }, + 31: { + left: { + style: "dotted", + color: "#000", + }, + }, + 32: { + bottom: { + style: "thick", + color: "#00FF00", + }, }, - 26: { top: { style: "dotted", color: "#000" } }, - 27: { left: { style: "thin", color: "#000" }, right: { style: "thin", color: "#FF0000" } }, - 28: { left: { style: "medium", color: "#000" } }, - 29: { left: { style: "thick", color: "#000" } }, - 30: { left: { style: "dashed", color: "#000" } }, - 31: { left: { style: "dotted", color: "#000" } }, - 32: { bottom: { style: "thick", color: "#00FF00" } }, 33: { - top: { style: "thick", color: "#00FF00" }, - bottom: { style: "dotted", color: "#0000FF" }, - left: { style: "thin", color: "#FF0000" }, - right: { style: "dashed", color: "#93C47D" }, + top: { + style: "thick", + color: "#00FF00", + }, + bottom: { + style: "dotted", + color: "#0000FF", + }, + left: { + style: "thin", + color: "#FF0000", + }, + right: { + style: "dashed", + color: "#93C47D", + }, + }, + 34: { + top: { + style: "dotted", + color: "#0000FF", + }, + }, + 35: { + left: { + style: "dashed", + color: "#93C47D", + }, }, - 34: { top: { style: "dotted", color: "#0000FF" } }, - 35: { left: { style: "dashed", color: "#93C47D" } }, }, revisionId: "START_REVISION", uniqueFigureIds: true, @@ -2842,13 +3667,23 @@ export const demoData = { dateFormat: "m/d/yyyy", timeFormat: "hh:mm:ss a", formulaArgSeparator: ",", + weekStart: 7, }, }, pivots: { 1: { type: "SPREADSHEET", - columns: [{ fieldName: "Stage" }], - rows: [{ fieldName: "Salesperson", order: "asc" }], + columns: [ + { + fieldName: "Stage", + }, + ], + rows: [ + { + fieldName: "Salesperson", + order: "asc", + }, + ], measures: [ { id: "Expected Revenue:sum", @@ -2867,7 +3702,15 @@ export const demoData = { }, ], name: "My pivot", - dataSet: { sheetId: "pivot", zone: { top: 0, bottom: 21, left: 0, right: 8 } }, + dataSet: { + sheetId: "pivot", + zone: { + top: 0, + bottom: 21, + left: 0, + right: 8, + }, + }, formulaId: "1", }, }, diff --git a/src/actions/menu_items_actions.ts b/src/actions/menu_items_actions.ts index b7a9755601..1b8b107df8 100644 --- a/src/actions/menu_items_actions.ts +++ b/src/actions/menu_items_actions.ts @@ -1,10 +1,7 @@ import { CellPopoverStore } from "../components/popover"; import { DEFAULT_FIGURE_HEIGHT, DEFAULT_FIGURE_WIDTH } from "../constants"; import { parseOSClipboardContent } from "../helpers/clipboard/clipboard_helpers"; -import { - getChartPositionAtCenterOfViewport, - getSmartChartDefinition, -} from "../helpers/figures/charts"; +import { getSmartChartDefinition } from "../helpers/figures/charts"; import { centerFigurePosition, getMaxFigureSize } from "../helpers/figures/figure/figure"; import { areZonesContinuous, @@ -389,12 +386,13 @@ export const CREATE_CHART = (env: SpreadsheetChildEnv) => { } const size = { width: DEFAULT_FIGURE_WIDTH, height: DEFAULT_FIGURE_HEIGHT }; - const position = getChartPositionAtCenterOfViewport(getters, size); + const { anchor, offset } = centerFigurePosition(getters, size); const result = env.model.dispatch("CREATE_CHART", { sheetId, id, - position, + anchor, + offset, size, definition: getSmartChartDefinition(env.model.getters.getSelectedZone(), env.model.getters), }); @@ -474,17 +472,18 @@ async function requestImage(env: SpreadsheetChildEnv): Promise { if (env.imageProvider) { const sheetId = env.model.getters.getActiveSheetId(); - const figureId = env.model.uuidGenerator.uuidv4(); + const id = env.model.uuidGenerator.uuidv4(); const image = await requestImage(env); if (!image) { throw new Error("No image provider was given to the environment"); } const size = getMaxFigureSize(env.model.getters, image.size); - const position = centerFigurePosition(env.model.getters, size); + const { anchor, offset } = centerFigurePosition(env.model.getters, size); env.model.dispatch("CREATE_IMAGE", { sheetId, - figureId, - position, + id, + anchor, + offset, size, definition: image, }); diff --git a/src/clipboard_handlers/chart_clipboard.ts b/src/clipboard_handlers/chart_clipboard.ts index 888ec16af2..5232df24c1 100644 --- a/src/clipboard_handlers/chart_clipboard.ts +++ b/src/clipboard_handlers/chart_clipboard.ts @@ -56,22 +56,15 @@ export class ChartClipboardHandler extends AbstractFigureClipboardHandler { static template = "o-spreadsheet-ChartJsComponent"; static props = { - figure: Object, + figureUI: Object, }; private canvas = useRef("graphContainer"); @@ -32,7 +32,7 @@ export class ChartJsComponent extends Component { } get chartRuntime(): ChartJSRuntime { - const runtime = this.env.model.getters.getChartRuntime(this.props.figure.id); + const runtime = this.env.model.getters.getChartRuntime(this.props.figureUI.figure.id); if (!("chartJsConfig" in runtime)) { throw new Error("Unsupported chart runtime"); } diff --git a/src/components/figures/chart/gauge/gauge_chart_component.ts b/src/components/figures/chart/gauge/gauge_chart_component.ts index 6a3b434594..071aaa1c47 100644 --- a/src/components/figures/chart/gauge/gauge_chart_component.ts +++ b/src/components/figures/chart/gauge/gauge_chart_component.ts @@ -1,10 +1,10 @@ import { Component, useEffect, useRef } from "@odoo/owl"; import { drawGaugeChart } from "../../../../helpers/figures/charts/gauge_chart_rendering"; -import { Figure, SpreadsheetChildEnv } from "../../../../types"; +import { FigureUI, SpreadsheetChildEnv } from "../../../../types"; import { GaugeChartRuntime } from "../../../../types/chart"; interface Props { - figure: Figure; + figureUI: FigureUI; } export class GaugeChartComponent extends Component { @@ -12,7 +12,9 @@ export class GaugeChartComponent extends Component { private canvas = useRef("chartContainer"); get runtime(): GaugeChartRuntime { - return this.env.model.getters.getChartRuntime(this.props.figure.id) as GaugeChartRuntime; + return this.env.model.getters.getChartRuntime( + this.props.figureUI.figure.id + ) as GaugeChartRuntime; } setup() { @@ -28,5 +30,5 @@ export class GaugeChartComponent extends Component { } GaugeChartComponent.props = { - figure: Object, + figureUI: Object, }; diff --git a/src/components/figures/chart/scorecard/chart_scorecard.ts b/src/components/figures/chart/scorecard/chart_scorecard.ts index dfcf1de74f..afd811f779 100644 --- a/src/components/figures/chart/scorecard/chart_scorecard.ts +++ b/src/components/figures/chart/scorecard/chart_scorecard.ts @@ -2,26 +2,29 @@ import { Component, useEffect, useRef } from "@odoo/owl"; import { drawScoreChart } from "../../../../helpers/figures/charts/scorecard_chart"; import { getScorecardConfiguration } from "../../../../helpers/figures/charts/scorecard_chart_config_builder"; import { _t } from "../../../../translation"; -import { Figure, SpreadsheetChildEnv } from "../../../../types"; +import { FigureUI, SpreadsheetChildEnv } from "../../../../types"; import { ScorecardChartRuntime } from "../../../../types/chart/scorecard_chart"; interface Props { - figure: Figure; + figureUI: FigureUI; } export class ScorecardChart extends Component { static template = "o-spreadsheet-ScorecardChart"; static props = { - figure: Object, + figureUI: Object, }; private canvas = useRef("chartContainer"); get runtime(): ScorecardChartRuntime { - return this.env.model.getters.getChartRuntime(this.props.figure.id) as ScorecardChartRuntime; + return this.env.model.getters.getChartRuntime( + this.props.figureUI.figure.id + ) as ScorecardChartRuntime; } get title(): string { - const title = this.env.model.getters.getChartDefinition(this.props.figure.id).title.text ?? ""; + const title = + this.env.model.getters.getChartDefinition(this.props.figureUI.figure.id).title.text ?? ""; // chart titles are extracted from .json files and they are translated at runtime here return _t(title); } diff --git a/src/components/figures/figure/figure.ts b/src/components/figures/figure/figure.ts index 997f46e8ef..7bf4ff171c 100644 --- a/src/components/figures/figure/figure.ts +++ b/src/components/figures/figure/figure.ts @@ -9,7 +9,7 @@ import { figureRegistry } from "../../../registries/index"; import { CSSProperties, DOMCoordinates, - Figure, + FigureUI, Pixel, ResizeDirection, SpreadsheetChildEnv, @@ -113,7 +113,7 @@ css/*SCSS*/ ` `; interface Props { - figure: Figure; + figureUI: FigureUI; style: string; onFigureDeleted: () => void; onMouseDown: (ev: MouseEvent) => void; @@ -123,7 +123,7 @@ interface Props { export class FigureComponent extends Component { static template = "o-spreadsheet-FigureComponent"; static props = { - figure: Object, + figureUI: Object, style: { type: String, optional: true }, onFigureDeleted: { type: Function, optional: true }, onMouseDown: { type: Function, optional: true }, @@ -145,7 +145,7 @@ export class FigureComponent extends Component { private borderWidth!: number; get isSelected(): boolean { - return this.env.model.getters.getSelectedFigureId() === this.props.figure.id; + return this.env.model.getters.getSelectedFigureId() === this.props.figureUI.figure.id; } get figureRegistry() { @@ -164,7 +164,8 @@ export class FigureComponent extends Component { } get wrapperStyle() { - const { x, y, width, height } = this.props.figure; + const { x, y, figure } = this.props.figureUI; + const { width, height } = figure; return cssPropertiesToCss({ left: `${x}px`, top: `${y}px`, @@ -196,7 +197,7 @@ export class FigureComponent extends Component { } setup() { - const borderWidth = figureRegistry.get(this.props.figure.tag).borderWidth; + const borderWidth = figureRegistry.get(this.props.figureUI.figure.tag).borderWidth; this.borderWidth = borderWidth !== undefined ? borderWidth : BORDER_WIDTH; useEffect( (selectedFigureId: UID | null, thisFigureId: UID, el: HTMLElement | null) => { @@ -212,7 +213,11 @@ export class FigureComponent extends Component { el?.focus({ preventScroll: true }); } }, - () => [this.env.model.getters.getSelectedFigureId(), this.props.figure.id, this.figureRef.el] + () => [ + this.env.model.getters.getSelectedFigureId(), + this.props.figureUI.figure.id, + this.figureRef.el, + ] ); onWillUnmount(() => { @@ -229,7 +234,7 @@ export class FigureComponent extends Component { } onKeyDown(ev: KeyboardEvent) { - const figure = this.props.figure; + const figure = this.props.figureUI.figure; const keyDownShortcut = keyboardEventToShortcutString(ev); switch (keyDownShortcut) { @@ -253,12 +258,15 @@ export class FigureComponent extends Component { ArrowRight: [1, 0], ArrowUp: [0, -1], }; + // TODO check cell change const delta = deltaMap[ev.key]; this.env.model.dispatch("UPDATE_FIGURE", { sheetId: this.env.model.getters.getActiveSheetId(), id: figure.id, - x: figure.x + delta[0], - y: figure.y + delta[1], + offset: { + x: figure.offset.x + delta[0], + y: figure.offset.y + delta[1], + }, }); ev.preventDefault(); ev.stopPropagation(); @@ -303,7 +311,7 @@ export class FigureComponent extends Component { this.menuState.isOpen = true; this.menuState.position = position; this.menuState.menuItems = figureRegistry - .get(this.props.figure.tag) - .menuBuilder(this.props.figure.id, this.props.onFigureDeleted, this.env); + .get(this.props.figureUI.figure.tag) + .menuBuilder(this.props.figureUI.figure.id, this.props.onFigureDeleted, this.env); } } diff --git a/src/components/figures/figure/figure.xml b/src/components/figures/figure/figure.xml index 48bb4e081d..168884c657 100644 --- a/src/components/figures/figure/figure.xml +++ b/src/components/figures/figure/figure.xml @@ -7,15 +7,15 @@ t-on-contextmenu.prevent.stop="(ev) => !env.model.getters.isReadonly() and this.onContextMenu(ev)" t-ref="figure" t-att-style="props.style" - t-att-data-id="props.figure.id" + t-att-data-id="props.figureUI.figure.id" tabindex="0" t-on-keydown="(ev) => this.onKeyDown(ev)" t-on-keyup.stop="">
void; } export class ChartFigure extends Component { static template = "o-spreadsheet-ChartFigure"; static props = { - figure: Object, + figureUI: Object, onFigureDeleted: Function, }; static components = {}; onDoubleClick() { - this.env.model.dispatch("SELECT_FIGURE", { id: this.props.figure.id }); + this.env.model.dispatch("SELECT_FIGURE", { id: this.props.figureUI.figure.id }); this.env.openSidePanel("ChartPanel"); } get chartType(): ChartType { - return this.env.model.getters.getChartType(this.props.figure.id); + return this.env.model.getters.getChartType(this.props.figureUI.figure.id); } get chartComponent(): new (...args: any) => Component { diff --git a/src/components/figures/figure_chart/figure_chart.xml b/src/components/figures/figure_chart/figure_chart.xml index cedf0efd48..fb90a1f959 100644 --- a/src/components/figures/figure_chart/figure_chart.xml +++ b/src/components/figures/figure_chart/figure_chart.xml @@ -3,8 +3,8 @@
diff --git a/src/components/figures/figure_container/figure_container.ts b/src/components/figures/figure_container/figure_container.ts index a61416896d..650fe9da42 100644 --- a/src/components/figures/figure_container/figure_container.ts +++ b/src/components/figures/figure_container/figure_container.ts @@ -1,15 +1,18 @@ import { Component, onMounted, onWillUpdateProps, useState } from "@odoo/owl"; import { ComponentsImportance, MIN_FIG_SIZE } from "../../../constants"; import { isDefined } from "../../../helpers"; -import { rectIntersection, rectUnion } from "../../../helpers/rectangle"; +import { rectUnion } from "../../../helpers/rectangle"; import { figureRegistry } from "../../../registries"; -import { Figure, Rect, ResizeDirection, SpreadsheetChildEnv, UID } from "../../../types/index"; +import { + Figure, + FigureUI, + Rect, + ResizeDirection, + SpreadsheetChildEnv, + UID, +} from "../../../types/index"; import { css, cssPropertiesToCss } from "../../helpers"; import { startDnd } from "../../helpers/drag_and_drop"; -import { - internalFigureToScreen, - screenFigureToInternal, -} from "../../helpers/figure_container_helper"; import { dragFigureForMove, dragFigureForResize } from "../../helpers/figure_drag_helper"; import { HFigureAxisType, @@ -28,7 +31,7 @@ interface Props { interface Container { type: ContainerType; - figures: Figure[]; + figures: FigureUI[]; style: string; inverseViewportStyle: string; } @@ -40,7 +43,7 @@ interface Snap { } interface DndState { - draggedFigure?: Figure; + draggedFigure?: FigureUI; horizontalSnap?: Snap; verticalSnap?: Snap; cancelDnd: (() => void) | undefined; @@ -152,7 +155,7 @@ export class FiguresContainer extends Component { }); onWillUpdateProps(() => { const sheetId = this.env.model.getters.getActiveSheetId(); - const draggedFigureId = this.dnd.draggedFigure?.id; + const draggedFigureId = this.dnd.draggedFigure?.figure.id; if (draggedFigureId && !this.env.model.getters.getFigure(sheetId, draggedFigureId)) { if (this.dnd.cancelDnd) { this.dnd.cancelDnd(); @@ -165,18 +168,19 @@ export class FiguresContainer extends Component { }); } - private getVisibleFigures(): Figure[] { + private getVisibleFigures(): FigureUI[] { const visibleFigures = this.env.model.getters.getVisibleFigures(); if ( this.dnd.draggedFigure && - !visibleFigures.some((figure) => figure.id === this.dnd.draggedFigure?.id) + !visibleFigures.some((figureUI) => figureUI.figure.id === this.dnd.draggedFigure?.figure.id) ) { + const sheetId = this.env.model.getters.getActiveSheetId(); const draggedFigure = this.env.model.getters.getFigure( - this.env.model.getters.getActiveSheetId(), - this.dnd.draggedFigure?.id + sheetId, + this.dnd.draggedFigure?.figure.id ); if (draggedFigure) { - visibleFigures.push(draggedFigure); + visibleFigures.push(this.env.model.getters.getFigureUI(sheetId, draggedFigure)); } } return visibleFigures; @@ -221,7 +225,6 @@ export class FiguresContainer extends Component { private getContainerStyle(container: ContainerType): string { return this.rectToCss(this.getContainerRect(container)); } - private rectToCss(rect: Rect): string { return cssPropertiesToCss({ left: `${rect.x}px`, @@ -240,15 +243,28 @@ export class FiguresContainer extends Component { const y = ["bottomRight", "bottomLeft"].includes(container) ? viewportY : 0; const height = viewHeight - y; - return { x, y, width, height }; + return { x: x, y: y, width, height }; } private getInverseViewportPositionStyle(container: ContainerType): string { const { scrollX, scrollY } = this.env.model.getters.getActiveSheetScrollInfo(); const { x: viewportX, y: viewportY } = this.env.model.getters.getMainViewportCoordinates(); - const left = ["bottomRight", "topRight"].includes(container) ? -(viewportX + scrollX) : 0; - const top = ["bottomRight", "bottomLeft"].includes(container) ? -(viewportY + scrollY) : 0; + let left = 0, + top = 0; + + if (["bottomRight", "topRight", "dnd"].includes(container)) { + left -= scrollX; + if (container != "dnd") { + left -= viewportX; + } + } + if (["bottomRight", "bottomLeft", "dnd"].includes(container)) { + top -= scrollY; + if (container != "dnd") { + top -= viewportY; + } + } return cssPropertiesToCss({ left: `${left}px`, @@ -256,27 +272,27 @@ export class FiguresContainer extends Component { }); } - private getFigureContainer(figure: Figure): ContainerType { + private getFigureContainer(figureUI: FigureUI): ContainerType { const { x: viewportX, y: viewportY } = this.env.model.getters.getMainViewportCoordinates(); - if (figure.id === this.dnd.draggedFigure?.id) { + if (figureUI.figure.id === this.dnd.draggedFigure?.figure.id) { return "dnd"; - } else if (figure.x < viewportX && figure.y < viewportY) { + } else if (figureUI.x < viewportX && figureUI.y < viewportY) { return "topLeft"; - } else if (figure.x < viewportX) { + } else if (figureUI.x < viewportX) { return "bottomLeft"; - } else if (figure.y < viewportY) { + } else if (figureUI.y < viewportY) { return "topRight"; } else { return "bottomRight"; } } - startDraggingFigure(figure: Figure, ev: MouseEvent) { + startDraggingFigure(figureUI: FigureUI, ev: MouseEvent) { if (ev.button > 0 || this.env.model.getters.isReadonly()) { // not main button, probably a context menu and no d&d in readonly mode return; } - const selectResult = this.env.model.dispatch("SELECT_FIGURE", { id: figure.id }); + const selectResult = this.env.model.dispatch("SELECT_FIGURE", { id: figureUI.figure.id }); if (!selectResult.isSuccessful) { return; } @@ -284,6 +300,17 @@ export class FiguresContainer extends Component { const sheetId = this.env.model.getters.getActiveSheetId(); const initialMousePosition = { x: ev.clientX, y: ev.clientY }; + const initialScrollPosition = this.env.model.getters.getActiveSheetScrollInfo(); + + // switch to global viewport position + const container = this.getFigureContainer(figureUI); + const { scrollX, scrollY } = this.env.model.getters.getActiveSheetScrollInfo(); + if (["bottomLeft", "topLeft"].includes(container)) { + figureUI.x += scrollX; + } + if (["topLeft", "topRight"].includes(container)) { + figureUI.y += scrollY; + } const maxDimensions = { maxX: this.env.model.getters.getColDimensions( @@ -296,40 +323,39 @@ export class FiguresContainer extends Component { ).end, }; - const { x, y } = internalFigureToScreen(this.env.model.getters, figure); - - const initialFig = { ...figure, x, y }; - const onMouseMove = (ev: MouseEvent) => { const getters = this.env.model.getters; const currentMousePosition = { x: ev.clientX, y: ev.clientY }; const draggedFigure = dragFigureForMove( currentMousePosition, initialMousePosition, - initialFig, + figureUI, this.env.model.getters.getMainViewportCoordinates(), maxDimensions, + initialScrollPosition, getters.getActiveSheetScrollInfo() ); - - const otherFigures = this.getOtherFigures(figure.id); - const internalDragged = screenFigureToInternal(getters, draggedFigure); - const snapResult = snapForMove(getters, internalDragged, otherFigures); - - this.dnd.draggedFigure = internalFigureToScreen(getters, snapResult.snappedFigure); + const otherFigures = this.getOtherFigures(figureUI.figure.id); + const snapResult = snapForMove(getters, draggedFigure, otherFigures); + this.dnd.draggedFigure = snapResult.snappedFigure; this.dnd.horizontalSnap = this.getSnap(snapResult.horizontalSnapLine); this.dnd.verticalSnap = this.getSnap(snapResult.verticalSnapLine); }; + const onMouseUp = (ev: MouseEvent) => { if (!this.dnd.draggedFigure) { return; } - let { x, y } = screenFigureToInternal(this.env.model.getters, this.dnd.draggedFigure); + const { anchor, offset } = this.env.model.getters.getAnchorOffset( + sheetId, + this.dnd.draggedFigure + ); this.dnd.draggedFigure = undefined; this.dnd.horizontalSnap = undefined; this.dnd.verticalSnap = undefined; - this.env.model.dispatch("UPDATE_FIGURE", { sheetId, id: figure.id, x, y }); + this.env.model.dispatch("UPDATE_FIGURE", { sheetId, id: figureUI.figure.id, offset, anchor }); }; + this.dnd.cancelDnd = startDnd(onMouseMove, onMouseUp); } @@ -342,30 +368,29 @@ export class FiguresContainer extends Component { * resize from the bottom border of the figure * @param ev Mouse Event */ - startResize(figure: Figure, dirX: ResizeDirection, dirY: ResizeDirection, ev: MouseEvent) { + startResize(figureUI: FigureUI, dirX: ResizeDirection, dirY: ResizeDirection, ev: MouseEvent) { ev.stopPropagation(); const initialMousePosition = { x: ev.clientX, y: ev.clientY }; + const initialScrollPosition = this.env.model.getters.getActiveSheetScrollInfo(); - const { x, y } = internalFigureToScreen(this.env.model.getters, figure); - - const initialFig = { ...figure, x, y }; - const keepRatio = figureRegistry.get(figure.tag).keepRatio || false; - const minFigSize = figureRegistry.get(figure.tag).minFigSize || MIN_FIG_SIZE; + const keepRatio = figureRegistry.get(figureUI.figure.tag).keepRatio || false; + const minFigSize = figureRegistry.get(figureUI.figure.tag).minFigSize || MIN_FIG_SIZE; const onMouseMove = (ev: MouseEvent) => { const currentMousePosition = { x: ev.clientX, y: ev.clientY }; const draggedFigure = dragFigureForResize( - initialFig, + figureUI, dirX, dirY, currentMousePosition, initialMousePosition, keepRatio, minFigSize, + initialScrollPosition, this.env.model.getters.getActiveSheetScrollInfo() ); - const otherFigures = this.getOtherFigures(figure.id); + const otherFigures = this.getOtherFigures(figureUI.figure.id); const snapResult = snapForResize( this.env.model.getters, dirX, @@ -377,36 +402,44 @@ export class FiguresContainer extends Component { this.dnd.horizontalSnap = this.getSnap(snapResult.horizontalSnapLine); this.dnd.verticalSnap = this.getSnap(snapResult.verticalSnapLine); }; + const onMouseUp = (ev: MouseEvent) => { if (!this.dnd.draggedFigure) { return; } - let { x, y } = screenFigureToInternal(this.env.model.getters, this.dnd.draggedFigure); - const update: Partial
= { x, y }; + const sheetId = this.env.model.getters.getActiveSheetId(); + const { anchor, offset } = this.env.model.getters.getAnchorOffset( + sheetId, + this.dnd.draggedFigure + ); + const update: Partial
= { anchor, offset }; if (dirX) { - update.width = this.dnd.draggedFigure.width; + update.width = this.dnd.draggedFigure.figure.width; } if (dirY) { - update.height = this.dnd.draggedFigure.height; + update.height = this.dnd.draggedFigure.figure.height; } this.env.model.dispatch("UPDATE_FIGURE", { sheetId: this.env.model.getters.getActiveSheetId(), - id: figure.id, + id: figureUI.figure.id, ...update, }); this.dnd.draggedFigure = undefined; this.dnd.horizontalSnap = undefined; this.dnd.verticalSnap = undefined; }; + this.dnd.cancelDnd = startDnd(onMouseMove, onMouseUp); } - private getOtherFigures(figId: UID): Figure[] { - return this.getVisibleFigures().filter((f) => f.id !== figId); + private getOtherFigures(figId: UID): FigureUI[] { + return this.getVisibleFigures().filter((f) => f.figure.id !== figId); } - private getDndFigure(): Figure { - const figure = this.getVisibleFigures().find((fig) => fig.id === this.dnd.draggedFigure?.id); + private getDndFigure(): FigureUI { + const figure = this.getVisibleFigures().find( + (fig) => fig.figure.id === this.dnd.draggedFigure?.figure.id + ); if (!figure) throw new Error("Dnd figure not found"); return { ...figure, @@ -414,8 +447,8 @@ export class FiguresContainer extends Component { }; } - getFigureStyle(figure: Figure): string { - if (figure.id !== this.dnd.draggedFigure?.id) return ""; + getFigureStyle(figureUI: FigureUI): string { + if (figureUI.figure.id !== this.dnd.draggedFigure?.figure.id) return ""; return cssPropertiesToCss({ opacity: "0.9", cursor: "grabbing", @@ -426,19 +459,27 @@ export class FiguresContainer extends Component { snapLine: SnapLine | undefined ): Snap | undefined { if (!snapLine || !this.dnd.draggedFigure) return undefined; - + const { scrollX, scrollY } = this.env.model.getters.getActiveSheetScrollInfo(); const figureVisibleRects = snapLine.matchedFigIds - .map((id) => this.getVisibleFigures().find((fig) => fig.id === id)) + .map((id) => this.getVisibleFigures().find((figureUI) => figureUI.figure.id === id)) .filter(isDefined) - .map((fig) => { - const figOnSCreen = internalFigureToScreen(this.env.model.getters, fig); - const container = this.getFigureContainer(fig); - return rectIntersection(figOnSCreen, this.getContainerRect(container)); + .map((figureUI) => { + return { + x: figureUI.x - scrollX, + y: figureUI.y - scrollY, + width: figureUI.figure.width, + height: figureUI.figure.height, + }; }) .filter(isDefined); - - const containerRect = rectUnion(this.dnd.draggedFigure, ...figureVisibleRects); - + const containerRect = rectUnion( + { + x: this.dnd.draggedFigure.x - scrollX, + y: this.dnd.draggedFigure.y - scrollY, + ...this.dnd.draggedFigure.figure, + }, + ...figureVisibleRects + ); return { line: snapLine, containerStyle: this.rectToCss(containerRect), @@ -451,16 +492,17 @@ export class FiguresContainer extends Component { containerRect: Rect ): string { if (!snapLine) return ""; + const { scrollX, scrollY } = this.env.model.getters.getActiveSheetScrollInfo(); if (["top", "vCenter", "bottom"].includes(snapLine.snappedAxisType)) { return cssPropertiesToCss({ - top: `${snapLine.position - containerRect.y}px`, + top: `${snapLine.position - containerRect.y - scrollY}px`, left: `0px`, width: `100%`, }); } else { return cssPropertiesToCss({ top: `0px`, - left: `${snapLine.position - containerRect.x}px`, + left: `${snapLine.position - containerRect.x - scrollX}px`, height: `100%`, }); } diff --git a/src/components/figures/figure_container/figure_container.xml b/src/components/figures/figure_container/figure_container.xml index 2cff25b0c1..4c6a4bccc3 100644 --- a/src/components/figures/figure_container/figure_container.xml +++ b/src/components/figures/figure_container/figure_container.xml @@ -9,13 +9,13 @@ diff --git a/src/components/figures/figure_image/figure_image.ts b/src/components/figures/figure_image/figure_image.ts index 1e8ef7fecd..0877cf45e4 100644 --- a/src/components/figures/figure_image/figure_image.ts +++ b/src/components/figures/figure_image/figure_image.ts @@ -1,15 +1,15 @@ import { Component } from "@odoo/owl"; -import { Figure, SpreadsheetChildEnv, UID } from "../../../types"; +import { FigureUI, SpreadsheetChildEnv, UID } from "../../../types"; interface Props { - figure: Figure; + figureUI: FigureUI; onFigureDeleted: () => void; } export class ImageFigure extends Component { static template = "o-spreadsheet-ImageFigure"; static props = { - figure: Object, + figureUI: Object, onFigureDeleted: Function, }; static components = {}; @@ -19,7 +19,7 @@ export class ImageFigure extends Component { // --------------------------------------------------------------------------- get figureId(): UID { - return this.props.figure.id; + return this.props.figureUI.figure.id; } get getImagePath(): string { diff --git a/src/components/helpers/figure_container_helper.ts b/src/components/helpers/figure_container_helper.ts deleted file mode 100644 index 113d620b72..0000000000 --- a/src/components/helpers/figure_container_helper.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { DOMCoordinates, Figure, Getters } from "../../types"; - -/** - * Transform a figure with coordinates from the model, to coordinates as they are shown on the screen, - * taking into account the scroll position of the active sheet and the frozen panes. - */ -export function internalFigureToScreen(getters: Getters, fig: Figure): Figure { - return { ...fig, ...internalToScreenCoordinates(getters, { x: fig.x, y: fig.y }) }; -} - -/** - * Transform a figure with coordinates as they are shown on the screen, to coordinates as they are in the model, - * taking into account the scroll position of the active sheet and the frozen panes. - * - * Note that this isn't exactly the reverse operation as internalFigureToScreen, because the figure will always be on top - * of the frozen panes. - */ -export function screenFigureToInternal(getters: Getters, fig: Figure): Figure { - return { ...fig, ...screenCoordinatesToInternal(getters, { x: fig.x, y: fig.y }) }; -} - -function internalToScreenCoordinates(getters: Getters, { x, y }: DOMCoordinates): DOMCoordinates { - const { x: viewportX, y: viewportY } = getters.getMainViewportCoordinates(); - const { scrollX, scrollY } = getters.getActiveSheetScrollInfo(); - - x = x < viewportX ? x : x - scrollX; - y = y < viewportY ? y : y - scrollY; - - return { x, y }; -} - -function screenCoordinatesToInternal(getters: Getters, { x, y }: DOMCoordinates): DOMCoordinates { - const { x: viewportX, y: viewportY } = getters.getMainViewportCoordinates(); - const { scrollX, scrollY } = getters.getActiveSheetScrollInfo(); - - x = viewportX && x < viewportX ? x : x + scrollX; - y = viewportY && y < viewportY ? y : y + scrollY; - - return { x, y }; -} diff --git a/src/components/helpers/figure_drag_helper.ts b/src/components/helpers/figure_drag_helper.ts index 654bb303b8..0429800322 100644 --- a/src/components/helpers/figure_drag_helper.ts +++ b/src/components/helpers/figure_drag_helper.ts @@ -1,58 +1,71 @@ import { clip } from "../../helpers"; -import { Figure, PixelPosition, SheetScrollInfo } from "../../types"; +import { FigureUI, PixelPosition, SheetScrollInfo } from "../../types"; export function dragFigureForMove( { x: mouseX, y: mouseY }: PixelPosition, { x: mouseInitialX, y: mouseInitialY }: PixelPosition, - initialFigure: Figure, + initialFigure: FigureUI, { x: viewportX, y: viewportY }: PixelPosition, { maxX, maxY }: { maxX: number; maxY: number }, + { scrollX: initialScrollX, scrollY: initialScrollY }: SheetScrollInfo, { scrollX, scrollY }: SheetScrollInfo -): Figure { - const minX = viewportX ? 0 : -scrollX; - const minY = viewportY ? 0 : -scrollY; - const deltaX = mouseX - mouseInitialX; - const newX = clip(initialFigure.x + deltaX, minX, maxX - initialFigure.width - scrollX); - const deltaY = mouseY - mouseInitialY; - const newY = clip(initialFigure.y + deltaY, minY, maxY - initialFigure.height - scrollY); +): FigureUI { + const deltaX = mouseX - mouseInitialX + scrollX - initialScrollX; + const newX = clip(initialFigure.x + deltaX, 0, maxX - initialFigure.figure.width); + const deltaY = mouseY - mouseInitialY + scrollY - initialScrollY; + const newY = clip(initialFigure.y + deltaY, 0, maxY - initialFigure.figure.height); return { ...initialFigure, x: newX, y: newY }; } export function dragFigureForResize( - initialFigure: Figure, + initialFigure: FigureUI, dirX: -1 | 0 | 1, dirY: -1 | 0 | 1, { x: mouseX, y: mouseY }: PixelPosition, { x: mouseInitialX, y: mouseInitialY }: PixelPosition, keepRatio: boolean, minFigSize: number, + { scrollX: initialScrollX, scrollY: initialScrollY }: SheetScrollInfo, { scrollX, scrollY }: SheetScrollInfo -): Figure { - let { x, y, width, height } = initialFigure; +): FigureUI { + let { x, y, figure } = initialFigure; + let { width, height } = figure; if (keepRatio && dirX != 0 && dirY != 0) { - const deltaX = Math.min(dirX * (mouseInitialX - mouseX), initialFigure.width - minFigSize); - const deltaY = Math.min(dirY * (mouseInitialY - mouseY), initialFigure.height - minFigSize); - const fraction = Math.min(deltaX / initialFigure.width, deltaY / initialFigure.height); - width = initialFigure.width * (1 - fraction); - height = initialFigure.height * (1 - fraction); + const deltaX = Math.min( + dirX * (mouseInitialX - mouseX + scrollX - initialScrollX), + width - minFigSize + ); + const deltaY = Math.min( + dirY * (mouseInitialY - mouseY + scrollY - initialScrollY), + height - minFigSize + ); + const fraction = Math.min(deltaX / width, deltaY / height); + width = width * (1 - fraction); + height = height * (1 - fraction); if (dirX < 0) { - x = initialFigure.x + initialFigure.width * fraction; + x = x + width * fraction; } if (dirY < 0) { - y = initialFigure.y + initialFigure.height * fraction; + y = y + height * fraction; } } else { - const deltaX = Math.max(dirX * (mouseX - mouseInitialX), minFigSize - initialFigure.width); - const deltaY = Math.max(dirY * (mouseY - mouseInitialY), minFigSize - initialFigure.height); - width = initialFigure.width + deltaX; - height = initialFigure.height + deltaY; + const deltaX = Math.max( + dirX * (mouseX - mouseInitialX + scrollX - initialScrollX), + minFigSize - width + ); + const deltaY = Math.max( + dirY * (mouseY - mouseInitialY + scrollY - initialScrollY), + minFigSize - height + ); + width = width + deltaX; + height = height + deltaY; if (dirX < 0) { - x = initialFigure.x - deltaX; + x = x - deltaX; } if (dirY < 0) { - y = initialFigure.y - deltaY; + y = y - deltaY; } } @@ -66,5 +79,5 @@ export function dragFigureForResize( y = -scrollY; } - return { ...initialFigure, x, y, width, height }; + return { x, y, figure: { ...figure, width, height } }; } diff --git a/src/components/helpers/figure_snap_helper.ts b/src/components/helpers/figure_snap_helper.ts index 7d62c52d53..51e2c2ae82 100644 --- a/src/components/helpers/figure_snap_helper.ts +++ b/src/components/helpers/figure_snap_helper.ts @@ -1,6 +1,5 @@ -import { Figure, Getters, Pixel, PixelPosition, UID } from "../../types"; +import { FigureUI, Getters, Pixel, PixelPosition, UID } from "../../types"; import { FIGURE_BORDER_WIDTH } from "./../../constants"; -import { internalFigureToScreen } from "./figure_container_helper"; const SNAP_MARGIN: Pixel = 5; @@ -20,7 +19,7 @@ export interface SnapLine { } interface SnapReturn { - snappedFigure: Figure; + snappedFigure: FigureUI; verticalSnapLine?: SnapLine; horizontalSnapLine?: SnapLine; } @@ -31,8 +30,8 @@ interface SnapReturn { */ export function snapForMove( getters: Getters, - figureToSnap: Figure, - otherFigures: Figure[] + figureToSnap: FigureUI, + otherFigures: FigureUI[] ): SnapReturn { const snappedFigure = { ...figureToSnap }; @@ -87,8 +86,8 @@ export function snapForResize( getters: Getters, resizeDirX: -1 | 0 | 1, resizeDirY: -1 | 0 | 1, - figureToSnap: Figure, - otherFigures: Figure[] + figureToSnap: FigureUI, + otherFigures: FigureUI[] ): SnapReturn { const snappedFigure = { ...figureToSnap }; @@ -102,10 +101,10 @@ export function snapForResize( ); if (verticalSnapLine) { if (resizeDirX === 1) { - snappedFigure.width -= verticalSnapLine.snapOffset; + snappedFigure.figure.width -= verticalSnapLine.snapOffset; } else if (resizeDirX === -1) { snappedFigure.x -= verticalSnapLine.snapOffset; - snappedFigure.width += verticalSnapLine.snapOffset; + snappedFigure.figure.width += verticalSnapLine.snapOffset; } } @@ -119,17 +118,17 @@ export function snapForResize( ); if (horizontalSnapLine) { if (resizeDirY === 1) { - snappedFigure.height -= horizontalSnapLine.snapOffset; + snappedFigure.figure.height -= horizontalSnapLine.snapOffset; } else if (resizeDirY === -1) { snappedFigure.y -= horizontalSnapLine.snapOffset; - snappedFigure.height += horizontalSnapLine.snapOffset; + snappedFigure.figure.height += horizontalSnapLine.snapOffset; } } snappedFigure.x = Math.round(snappedFigure.x); snappedFigure.y = Math.round(snappedFigure.y); - snappedFigure.height = Math.round(snappedFigure.height); - snappedFigure.width = Math.round(snappedFigure.width); + snappedFigure.figure.height = Math.round(snappedFigure.figure.height); + snappedFigure.figure.width = Math.round(snappedFigure.figure.width); return { snappedFigure, verticalSnapLine, horizontalSnapLine }; } @@ -142,34 +141,16 @@ export function snapForResize( */ function getVisibleAxes( getters: Getters, - figure: Figure, + figure: FigureUI, axesTypes: T[] ): FigureAxis[] { - const axes = axesTypes.map((axisType) => getAxis(figure, axisType)); - return axes - .filter((axis) => isAxisVisible(getters, figure, axis)) - .map((axis) => getAxisScreenPosition(getters, figure, axis)); -} - -/** - * We need two positions for the figure axis : - * - the position (core) of the axis in the figure. This is used to know whether or not the axis is - * displayed, or is hidden by the scroll/the frozen panes - * - the position in the screen, which is used to find snap matches. We cannot use the core position for this, - * because figures partially in frozen panes aren't displayed at their actual coordinates - */ -function getAxisScreenPosition( - getters: Getters, - figure: Figure, - figureAxis: FigureAxis -): FigureAxis { - const screenFigure = internalFigureToScreen(getters, figure); - return getAxis(screenFigure, figureAxis.axisType); + const axes = axesTypes.map((axisType) => getAxis(getters, figure, false, axisType)); + return axes.filter((axis) => isAxisVisible(getters, figure, axis)); } function isAxisVisible( getters: Getters, - figure: Figure, + figureUI: FigureUI, axis: FigureAxis ): boolean { const { x: mainViewportX, y: mainViewportY } = getters.getMainViewportCoordinates(); @@ -179,16 +160,16 @@ function isAxisVisible( case "top": case "bottom": case "vCenter": - if (figure.y < mainViewportY) return true; - axisStartEndPositions.push({ x: figure.x, y: axis.position }); - axisStartEndPositions.push({ x: figure.x + figure.width, y: axis.position }); + if (figureUI.y < mainViewportY) return true; + axisStartEndPositions.push({ x: figureUI.x, y: axis.position }); + axisStartEndPositions.push({ x: figureUI.x + figureUI.figure.width, y: axis.position }); break; case "left": case "right": case "hCenter": - if (figure.x < mainViewportX) return true; - axisStartEndPositions.push({ x: axis.position, y: figure.y }); - axisStartEndPositions.push({ x: axis.position, y: figure.y + figure.height }); + if (figureUI.x < mainViewportX) return true; + axisStartEndPositions.push({ x: axis.position, y: figureUI.y }); + axisStartEndPositions.push({ x: axis.position, y: figureUI.y + figureUI.figure.height }); break; } @@ -206,18 +187,19 @@ function isAxisVisible( function getSnapLine( getters: Getters, - figureToSnap: Figure, + figureToSnap: FigureUI, figAxesTypes: T, - otherFigures: Figure[], + otherFigures: FigureUI[], otherAxesTypes: T ): SnapLine | undefined { - const axesOfFigure = getVisibleAxes(getters, figureToSnap, figAxesTypes); + const axesOfFigure = figAxesTypes.map((axisType) => + getAxis(getters, figureToSnap, true, axisType) + ); let closestMatch: SnapLine | undefined = undefined; for (const otherFigure of otherFigures) { const axesOfOtherFig = getVisibleAxes(getters, otherFigure, otherAxesTypes); - for (const axisOfFigure of axesOfFigure) { for (const axisOfOtherFig of axesOfOtherFig) { if (!canSnap(axisOfFigure.position, axisOfOtherFig.position)) continue; @@ -225,10 +207,10 @@ function getSnapLine( const snapOffset = axisOfFigure.position - axisOfOtherFig.position; if (closestMatch && snapOffset === closestMatch.snapOffset) { - closestMatch.matchedFigIds.push(otherFigure.id); + closestMatch.matchedFigIds.push(otherFigure.figure.id); } else if (!closestMatch || Math.abs(snapOffset) <= Math.abs(closestMatch.snapOffset)) { closestMatch = { - matchedFigIds: [otherFigure.id], + matchedFigIds: [otherFigure.figure.id], snapOffset, snappedAxisType: axisOfFigure.axisType, position: axisOfOtherFig.position, @@ -246,28 +228,35 @@ function canSnap(axisPosition1: Pixel, axisPosition2: Pixel) { } function getAxis( - fig: Figure, + getters: Getters, + figureUI: FigureUI, + dnd: boolean, axisType: T ): FigureAxis { let position = 0; + const { scrollX, scrollY } = getters.getActiveSheetScrollInfo(); + const { x: viewportX, y: viewportY } = getters.getMainViewportCoordinates(); + const y = !dnd && figureUI.y < viewportY ? figureUI.y + scrollY : figureUI.y; + const x = !dnd && figureUI.x < viewportX ? figureUI.x + scrollX : figureUI.x; + switch (axisType) { case "top": - position = fig.y; + position = y; break; case "bottom": - position = fig.y + fig.height - FIGURE_BORDER_WIDTH; + position = y + figureUI.figure.height - FIGURE_BORDER_WIDTH; break; case "vCenter": - position = fig.y + Math.floor(fig.height / 2) - FIGURE_BORDER_WIDTH; + position = y + Math.floor(figureUI.figure.height / 2) - FIGURE_BORDER_WIDTH; break; case "left": - position = fig.x; + position = x; break; case "right": - position = fig.x + fig.width - FIGURE_BORDER_WIDTH; + position = x + figureUI.figure.width - FIGURE_BORDER_WIDTH; break; case "hCenter": - position = fig.x + Math.floor(fig.width / 2) - FIGURE_BORDER_WIDTH; + position = x + Math.floor(figureUI.figure.width / 2) - FIGURE_BORDER_WIDTH; break; } diff --git a/src/components/side_panel/chart/building_blocks/general_design/general_design_editor.ts b/src/components/side_panel/chart/building_blocks/general_design/general_design_editor.ts index 9ab705a240..e9537d19f1 100644 --- a/src/components/side_panel/chart/building_blocks/general_design/general_design_editor.ts +++ b/src/components/side_panel/chart/building_blocks/general_design/general_design_editor.ts @@ -1,5 +1,6 @@ import { Component, useState } from "@odoo/owl"; import { CHART_TITLE_FONT_SIZE } from "../../../../../constants"; +import { _t } from "../../../../../translation"; import { ChartDefinition, Color, @@ -9,6 +10,7 @@ import { UID, } from "../../../../../types"; import { SidePanelCollapsible } from "../../../components/collapsible/side_panel_collapsible"; +import { RadioSelection } from "../../../components/radio_selection/radio_selection"; import { RoundColorPicker } from "../../../components/round_color_picker/round_color_picker"; import { Section } from "../../../components/section/section"; import { ChartTitle } from "../title/title"; @@ -31,6 +33,7 @@ export class GeneralDesignEditor extends Component { ChartTitle, Section, SidePanelCollapsible, + RadioSelection, }; static props = { figureId: String, @@ -44,6 +47,11 @@ export class GeneralDesignEditor extends Component { }; private state!: GeneralDesignEditorState; + fixedSelection = [ + { value: "fixed", label: _t("Don't move") }, + { value: "anchor", label: _t("Move with cell") }, + ]; + setup() { this.state = useState({ activeTool: "", @@ -106,4 +114,20 @@ export class GeneralDesignEditor extends Component { this.props.updateChart(this.props.figureId, { title }); this.state.activeTool = ""; } + + updateChartPositioning(value: string) { + if (value === "fixed") { + this.props.updateChart(this.props.figureId, { fixed_position: true }); + } else if (value === "anchor") { + this.props.updateChart(this.props.figureId, { fixed_position: false }); + } + } + + getSelectedPositioning() { + const figure = this.env.model.getters.getFigure( + this.env.model.getters.getActiveSheetId(), + this.props.figureId + ); + return figure?.fixed_position ? "fixed" : "anchor"; + } } diff --git a/src/components/side_panel/chart/building_blocks/general_design/general_design_editor.xml b/src/components/side_panel/chart/building_blocks/general_design/general_design_editor.xml index 0ba7ac1a63..edc6b11da7 100644 --- a/src/components/side_panel/chart/building_blocks/general_design/general_design_editor.xml +++ b/src/components/side_panel/chart/building_blocks/general_design/general_design_editor.xml @@ -20,6 +20,14 @@ style="titleStyle" onFontSizeChanged.bind="updateChartTitleFontSize" /> +
+ +
diff --git a/src/helpers/figures/charts/abstract_chart.ts b/src/helpers/figures/charts/abstract_chart.ts index 4870044cd5..d23e1f8b68 100644 --- a/src/helpers/figures/charts/abstract_chart.ts +++ b/src/helpers/figures/charts/abstract_chart.ts @@ -22,11 +22,13 @@ import { Validator } from "../../../types/validator"; export abstract class AbstractChart { readonly sheetId: UID; readonly title: TitleDesign; + readonly fixed_position: boolean; abstract readonly type: ChartType; protected readonly getters: CoreGetters; constructor(definition: ChartDefinition, sheetId: UID, getters: CoreGetters) { this.title = definition.title; + this.fixed_position = definition.fixed_position || false; this.sheetId = sheetId; this.getters = getters; } diff --git a/src/helpers/figures/charts/bar_chart.ts b/src/helpers/figures/charts/bar_chart.ts index f5abcbe96e..93f74c6d45 100644 --- a/src/helpers/figures/charts/bar_chart.ts +++ b/src/helpers/figures/charts/bar_chart.ts @@ -107,6 +107,7 @@ export class BarChart extends AbstractChart { dataSets: context.range ?? [], dataSetsHaveTitle: context.dataSetsHaveTitle ?? false, stacked: context.stacked ?? false, + fixed_position: context.fixed_position ?? false, aggregated: context.aggregated ?? false, legendPosition: context.legendPosition ?? "top", title: context.title || { text: "" }, @@ -175,6 +176,7 @@ export class BarChart extends AbstractChart { labelRange: labelRange ? this.getters.getRangeString(labelRange, targetSheetId || this.sheetId) : undefined, + fixed_position: this.fixed_position, title: this.title, stacked: this.stacked, aggregated: this.aggregated, diff --git a/src/helpers/figures/charts/chart_factory.ts b/src/helpers/figures/charts/chart_factory.ts index 4b97d2ef9e..ea184ea8bc 100644 --- a/src/helpers/figures/charts/chart_factory.ts +++ b/src/helpers/figures/charts/chart_factory.ts @@ -108,6 +108,7 @@ export function getSmartChartDefinition(zone: Zone, getters: Getters): ChartDefi return { type: "scorecard", title: {}, + fixed_position: false, background: topLeftCell.style?.fillColor || undefined, keyValue: zoneToXc(zone), baselineMode: DEFAULT_SCORECARD_BASELINE_MODE, @@ -138,6 +139,7 @@ export function getSmartChartDefinition(zone: Zone, getters: Getters): ChartDefi dataSets, labelsAsText: false, stacked: false, + fixed_position: false, aggregated: false, cumulative: false, labelRange: labelRangeXc, @@ -156,6 +158,7 @@ export function getSmartChartDefinition(zone: Zone, getters: Getters): ChartDefi dataSets: [{ dataRange }], aggregated: true, labelRange: dataRange, + fixed_position: false, type: "pie", legendPosition: "top", dataSetsHaveTitle: false, @@ -167,6 +170,7 @@ export function getSmartChartDefinition(zone: Zone, getters: Getters): ChartDefi labelRange: labelRangeXc, type: "bar", stacked: false, + fixed_position: false, aggregated: false, dataSetsHaveTitle, legendPosition: newLegendPos, diff --git a/src/helpers/figures/charts/combo_chart.ts b/src/helpers/figures/charts/combo_chart.ts index 2bedd46c41..0318e0cb9c 100644 --- a/src/helpers/figures/charts/combo_chart.ts +++ b/src/helpers/figures/charts/combo_chart.ts @@ -147,6 +147,7 @@ export class ComboChart extends AbstractChart { aggregated: this.aggregated, axesDesign: this.axesDesign, showValues: this.showValues, + fixed_position: this.fixed_position, }; } @@ -204,6 +205,7 @@ export class ComboChart extends AbstractChart { type: "combo", axesDesign: context.axesDesign, showValues: context.showValues, + fixed_position: context.fixed_position || false, }; } diff --git a/src/helpers/figures/charts/gauge_chart.ts b/src/helpers/figures/charts/gauge_chart.ts index 269f2e41f7..6da7a93033 100644 --- a/src/helpers/figures/charts/gauge_chart.ts +++ b/src/helpers/figures/charts/gauge_chart.ts @@ -181,6 +181,7 @@ export class GaugeChart extends AbstractChart { title: context.title || { text: "" }, type: "gauge", dataRange: context.range ? context.range[0].dataRange : undefined, + fixed_position: context.fixed_position || false, sectionRule: { colors: { lowerColor: DEFAULT_GAUGE_LOWER_COLOR, @@ -225,6 +226,7 @@ export class GaugeChart extends AbstractChart { return { background: this.background, sectionRule: this.sectionRule, + fixed_position: this.fixed_position || false, title: this.title, type: "gauge", dataRange: dataRange diff --git a/src/helpers/figures/charts/geo_chart.ts b/src/helpers/figures/charts/geo_chart.ts index d10b7cec92..447bc51264 100644 --- a/src/helpers/figures/charts/geo_chart.ts +++ b/src/helpers/figures/charts/geo_chart.ts @@ -100,6 +100,7 @@ export class GeoChart extends AbstractChart { type: "geo", labelRange: context.auxiliaryRange || undefined, aggregated: context.aggregated, + fixed_position: context.fixed_position || false, }; } @@ -165,6 +166,7 @@ export class GeoChart extends AbstractChart { colorScale: this.colorScale, missingValueColor: this.missingValueColor, region: this.region, + fixed_position: this.fixed_position, }; } diff --git a/src/helpers/figures/charts/line_chart.ts b/src/helpers/figures/charts/line_chart.ts index 037e711941..afe772192a 100644 --- a/src/helpers/figures/charts/line_chart.ts +++ b/src/helpers/figures/charts/line_chart.ts @@ -122,6 +122,7 @@ export class LineChart extends AbstractChart { axesDesign: context.axesDesign, fillArea: context.fillArea, showValues: context.showValues, + fixed_position: context.fixed_position || false, }; } @@ -158,6 +159,7 @@ export class LineChart extends AbstractChart { axesDesign: this.axesDesign, fillArea: this.fillArea, showValues: this.showValues, + fixed_position: this.fixed_position || false, }; } diff --git a/src/helpers/figures/charts/pie_chart.ts b/src/helpers/figures/charts/pie_chart.ts index 0894754ae3..524f9cb03b 100644 --- a/src/helpers/figures/charts/pie_chart.ts +++ b/src/helpers/figures/charts/pie_chart.ts @@ -102,6 +102,7 @@ export class PieChart extends AbstractChart { aggregated: context.aggregated ?? false, isDoughnut: false, showValues: context.showValues, + fixed_position: context.fixed_position || false, }; } @@ -141,6 +142,7 @@ export class PieChart extends AbstractChart { aggregated: this.aggregated, isDoughnut: this.isDoughnut, showValues: this.showValues, + fixed_position: this.fixed_position || false, }; } diff --git a/src/helpers/figures/charts/pyramid_chart.ts b/src/helpers/figures/charts/pyramid_chart.ts index 4b6f00a59f..39b05041c7 100644 --- a/src/helpers/figures/charts/pyramid_chart.ts +++ b/src/helpers/figures/charts/pyramid_chart.ts @@ -100,6 +100,7 @@ export class PyramidChart extends AbstractChart { legendPosition: context.legendPosition ?? "top", title: context.title || { text: "" }, type: "pyramid", + fixed_position: context.fixed_position || false, labelRange: context.auxiliaryRange || undefined, axesDesign: context.axesDesign, horizontal: true, @@ -159,6 +160,7 @@ export class PyramidChart extends AbstractChart { } return { type: "pyramid", + fixed_position: this.fixed_position, dataSetsHaveTitle: dataSets.length ? Boolean(dataSets[0].labelCell) : false, background: this.background, dataSets: ranges, diff --git a/src/helpers/figures/charts/radar_chart.ts b/src/helpers/figures/charts/radar_chart.ts index 632d805c57..99e00fb9a9 100644 --- a/src/helpers/figures/charts/radar_chart.ts +++ b/src/helpers/figures/charts/radar_chart.ts @@ -110,6 +110,7 @@ export class RadarChart extends AbstractChart { labelRange: context.auxiliaryRange || undefined, fillArea: context.fillArea ?? false, showValues: context.showValues ?? false, + fixed_position: context.fixed_position || false, }; } @@ -176,6 +177,7 @@ export class RadarChart extends AbstractChart { aggregated: this.aggregated, fillArea: this.fillArea, showValues: this.showValues, + fixed_position: this.fixed_position || false, }; } diff --git a/src/helpers/figures/charts/scatter_chart.ts b/src/helpers/figures/charts/scatter_chart.ts index a4e7607687..20556b54f2 100644 --- a/src/helpers/figures/charts/scatter_chart.ts +++ b/src/helpers/figures/charts/scatter_chart.ts @@ -111,6 +111,7 @@ export class ScatterChart extends AbstractChart { aggregated: context.aggregated ?? false, axesDesign: context.axesDesign, showValues: context.showValues, + fixed_position: context.fixed_position || false, }; } @@ -144,6 +145,7 @@ export class ScatterChart extends AbstractChart { aggregated: this.aggregated, axesDesign: this.axesDesign, showValues: this.showValues, + fixed_position: this.fixed_position || false, }; } diff --git a/src/helpers/figures/charts/scorecard_chart.ts b/src/helpers/figures/charts/scorecard_chart.ts index 483543fe9f..42a301c706 100644 --- a/src/helpers/figures/charts/scorecard_chart.ts +++ b/src/helpers/figures/charts/scorecard_chart.ts @@ -202,6 +202,7 @@ export class ScorecardChart extends AbstractChart { baselineColorUp: DEFAULT_SCORECARD_BASELINE_COLOR_UP, baselineColorDown: DEFAULT_SCORECARD_BASELINE_COLOR_DOWN, baseline: context.auxiliaryRange || "", + fixed_position: context.fixed_position || false, }; } @@ -273,6 +274,7 @@ export class ScorecardChart extends AbstractChart { ? this.getters.getRangeString(keyValue, targetSheetId || this.sheetId) : undefined, humanize: this.humanize, + fixed_position: this.fixed_position || false, }; } diff --git a/src/helpers/figures/charts/waterfall_chart.ts b/src/helpers/figures/charts/waterfall_chart.ts index 3110c6832e..a23709b667 100644 --- a/src/helpers/figures/charts/waterfall_chart.ts +++ b/src/helpers/figures/charts/waterfall_chart.ts @@ -121,6 +121,7 @@ export class WaterfallChart extends AbstractChart { firstValueAsSubtotal: context.firstValueAsSubtotal ?? false, axesDesign: context.axesDesign, showValues: context.showValues, + fixed_position: context.fixed_position || false, }; } @@ -193,6 +194,7 @@ export class WaterfallChart extends AbstractChart { firstValueAsSubtotal: this.firstValueAsSubtotal, axesDesign: this.axesDesign, showValues: this.showValues, + fixed_position: this.fixed_position || false, }; } diff --git a/src/helpers/figures/figure/figure.ts b/src/helpers/figures/figure/figure.ts index 6f612cdea9..2c716664c7 100644 --- a/src/helpers/figures/figure/figure.ts +++ b/src/helpers/figures/figure/figure.ts @@ -1,19 +1,33 @@ -import { FigureSize, Getters } from "../../../types"; +import { AnchorOffset, FigureSize, Getters } from "../../../types"; import { deepCopy } from "../../misc"; -export function centerFigurePosition(getters: Getters, size: FigureSize) { +export function centerFigurePosition(getters: Getters, size: FigureSize): AnchorOffset { + // TODO this is wrong (see fig creation) const { x: offsetCorrectionX, y: offsetCorrectionY } = getters.getMainViewportCoordinates(); const { scrollX, scrollY } = getters.getActiveSheetScrollInfo(); + const sheetId = getters.getActiveSheetId(); const dim = getters.getSheetViewDimension(); const rect = getters.getVisibleRect(getters.getActiveMainViewport()); const scrollableViewportWidth = Math.min(rect.width, dim.width - offsetCorrectionX); const scrollableViewportHeight = Math.min(rect.height, dim.height - offsetCorrectionY); - return { - x: offsetCorrectionX + scrollX + Math.max(0, (scrollableViewportWidth - size.width) / 2), - y: offsetCorrectionY + scrollY + Math.max(0, (scrollableViewportHeight - size.height) / 2), - }; // Position at the center of the scrollable viewport + const posX = + offsetCorrectionX + scrollX + Math.max(0, (scrollableViewportWidth - size.width) / 2); + const posY = + offsetCorrectionY + scrollY + Math.max(0, (scrollableViewportHeight - size.height) / 2); + + const anchor = { + col: getters.getColIndex(Math.max(0, (scrollableViewportWidth - size.width) / 2)), + row: getters.getRowIndex(Math.max(0, (scrollableViewportHeight - size.height) / 2)), + }; + + const offset = { + x: posX - getters.getColDimensions(sheetId, anchor.col)["start"], + y: posY - getters.getRowDimensions(sheetId, anchor.row)["start"], + }; + + return { anchor, offset }; } export function getMaxFigureSize(getters: Getters, figureSize: FigureSize): FigureSize { diff --git a/src/history/repeat_commands/repeat_commands_specific.ts b/src/history/repeat_commands/repeat_commands_specific.ts index 7dc57f6799..4aa9ad35ac 100644 --- a/src/history/repeat_commands/repeat_commands_specific.ts +++ b/src/history/repeat_commands/repeat_commands_specific.ts @@ -39,7 +39,7 @@ export function repeatCreateImageCommand( ): CreateImageOverCommand { return { ...repeatSheetDependantCommand(getters, cmd), - figureId: uuidGenerator.uuidv4(), + id: uuidGenerator.uuidv4(), }; } diff --git a/src/migrations/data.ts b/src/migrations/data.ts index e393391264..f7ab5cf372 100644 --- a/src/migrations/data.ts +++ b/src/migrations/data.ts @@ -19,7 +19,7 @@ import { migrationStepRegistry } from "./migration_steps"; * a breaking change is made in the way the state is handled, and an upgrade * function should be defined */ -export const CURRENT_VERSION = 25; +export const CURRENT_VERSION = 26; const INITIAL_SHEET_ID = "Sheet1"; /** diff --git a/src/migrations/migration_steps.ts b/src/migrations/migration_steps.ts index 0418ca18a5..96ed413444 100644 --- a/src/migrations/migration_steps.ts +++ b/src/migrations/migration_steps.ts @@ -450,6 +450,21 @@ migrationStepRegistry migrate(data: WorkbookData): any { return data; }, + }) + .add("migration_25", { + // Ensure fixed position, anchor and offset for existing figures based on current position + versionFrom: "25", + migrate(data: WorkbookData): any { + for (const sheet of data.sheets || []) { + for (const figure of sheet.figures || []) { + const data = figure.data; + figure.fixed_position = true; + figure.offset = { x: data.x || 0, y: data.y || 0 }; + figure.anchor = { col: 0, row: 0 }; + } + } + return data; + }, }); function fixOverlappingFilters(data: any): any { diff --git a/src/plugins/core/chart.ts b/src/plugins/core/chart.ts index 7e1346f850..887bf1722f 100644 --- a/src/plugins/core/chart.ts +++ b/src/plugins/core/chart.ts @@ -8,10 +8,11 @@ import { CommandResult, CoreCommand, CreateChartCommand, - DOMCoordinates, DOMDimension, Figure, FigureData, + PixelPosition, + Position, UID, UpdateChartCommand, WorkbookData, @@ -74,7 +75,7 @@ export class ChartPlugin extends CorePlugin implements ChartState { handle(cmd: CoreCommand) { switch (cmd.type) { case "CREATE_CHART": - this.addFigure(cmd.id, cmd.sheetId, cmd.position, cmd.size); + this.addFigure(cmd.id, cmd.sheetId, cmd.anchor, cmd.offset, cmd.fixed_position, cmd.size); this.addChart(cmd.id, cmd.definition); break; case "UPDATE_CHART": { @@ -91,7 +92,9 @@ export class ChartPlugin extends CorePlugin implements ChartState { if (chart) { this.dispatch("CREATE_CHART", { id: duplicatedFigureId, - position: { x: fig.x, y: fig.y }, + anchor: fig.anchor, + offset: fig.offset, + fixed_position: fig.fixed_position, size: { width: fig.width, height: fig.height }, definition: chart.getDefinition(), sheetId: cmd.sheetIdTo, @@ -202,7 +205,12 @@ export class ChartPlugin extends CorePlugin implements ChartState { private addFigure( id: UID, sheetId: UID, - position: DOMCoordinates = { x: 0, y: 0 }, + anchor: Position | undefined, + offset: PixelPosition = { + x: 0, + y: 0, + }, + fixed_position: boolean = true, size: DOMDimension = { width: DEFAULT_FIGURE_WIDTH, height: DEFAULT_FIGURE_HEIGHT, @@ -211,10 +219,15 @@ export class ChartPlugin extends CorePlugin implements ChartState { if (this.getters.getFigure(sheetId, id)) { return; } + if (!anchor) { + anchor = { col: 0, row: 0 }; + fixed_position = true; + } const figure: Figure = { id, - x: position.x, - y: position.y, + anchor, + offset, + fixed_position, width: size.width, height: size.height, tag: "chart", diff --git a/src/plugins/core/figures.ts b/src/plugins/core/figures.ts index 4ea878ab4b..8624666b6b 100644 --- a/src/plugins/core/figures.ts +++ b/src/plugins/core/figures.ts @@ -1,14 +1,19 @@ -import { isDefined } from "../../helpers/index"; +import { DEFAULT_CELL_HEIGHT } from "../../constants"; +import { clip, isDefined } from "../../helpers/index"; +import { AnchorOffset } from "../../types/figure"; import { CommandResult, CoreCommand, ExcelWorkbookData, Figure, + HeaderIndex, + Pixel, + PixelPosition, + Position, UID, WorkbookData, } from "../../types/index"; import { CorePlugin } from "../core_plugin"; -import { DEFAULT_CELL_HEIGHT } from "./../../constants"; interface FigureState { readonly figures: { [sheet: string]: Record | undefined }; @@ -58,64 +63,184 @@ export class FigurePlugin extends CorePlugin implements FigureState break; case "UPDATE_FIGURE": const { type, sheetId, ...update } = cmd; - const figure: Partial
= update; - this.updateFigure(sheetId, figure); + this.updateFigure(sheetId, update); break; case "DELETE_FIGURE": this.removeFigure(cmd.id, cmd.sheetId); break; + case "ADD_COLUMNS_ROWS": + let baseIdx = cmd.base; + if (cmd.position === "before") { + baseIdx--; + } + if (cmd.dimension === "COL") { + this.onColAdd(cmd.sheetId, baseIdx, cmd.quantity); + } else { + this.onRowAdd(cmd.sheetId, baseIdx, cmd.quantity); + } + break; case "REMOVE_COLUMNS_ROWS": - this.onRowColDelete(cmd.sheetId, cmd.dimension); + if (cmd.dimension === "COL") { + this.onColRemove(cmd.sheetId, cmd.elements); + } else { + this.onRowRemove(cmd.sheetId, cmd.elements); + } + break; } } - private onRowColDelete(sheetId: string, dimension: string) { - dimension === "ROW" ? this.onRowDeletion(sheetId) : this.onColDeletion(sheetId); + private onColAdd(sheetId: string, index: HeaderIndex, quantity: number) { + for (const figure of this.getFigures(sheetId)) { + if (figure.anchor.col > index) { + this.history.update("figures", sheetId, figure.id!, "anchor", { + row: figure.anchor.row, + col: figure.anchor.col + quantity, + } as Position); + } + } } - private onRowDeletion(sheetId: string) { - const numHeader = this.getters.getNumberRows(sheetId); - let gridHeight = 0; + private onRowAdd(sheetId: string, index: HeaderIndex, quantity: number) { + for (const figure of this.getFigures(sheetId)) { + if (figure.anchor.row > index) { + this.history.update("figures", sheetId, figure.id!, "anchor", { + row: figure.anchor.row + quantity, + col: figure.anchor.col, + } as Position); + } + } + } + + private onColRemove(sheetId: string, elements: number[]) { + const figures = this.getFigures(sheetId).sort((a, b) => a.anchor.col - b.anchor.col); + elements.sort((a, b) => a - b); + + const numHeader = this.getters.getNumberCols(sheetId); + let gridWidth = 0; for (let i = 0; i < numHeader; i++) { // TODO : since the row size is an UI value now, this doesn't work anymore. Using the default cell height is // a temporary solution at best, but is broken. - gridHeight += this.getters.getUserRowSize(sheetId, i) || DEFAULT_CELL_HEIGHT; + gridWidth += this.getters.getColSize(sheetId, i); } - const figures = this.getters.getFigures(sheetId); - for (const figure of figures) { - const newY = Math.min(figure.y, gridHeight - figure.height); - if (newY !== figure.y) { - this.dispatch("UPDATE_FIGURE", { sheetId, id: figure.id, y: newY }); + + let elements_index = 0; + for (const fig in figures) { + const figure = figures[fig]; + while (elements_index < elements.length && elements[elements_index] < figure.anchor.col) { + elements_index++; + } + if (elements_index && !figure.fixed_position) { + let col_offset = elements_index; + let x_offset = 0; + for ( + let col_size: Pixel; + (col_size = this.getters.getColSize(sheetId, figure.anchor.col - col_offset)); + col_size < figure.offset.x - x_offset + ) { + col_offset -= 1; + x_offset += col_size; + } + if (x_offset) { + this.history.update("figures", sheetId, figure.id!, "offset", { + x: figure.offset.x - x_offset, + y: figure.offset.y, + } as PixelPosition); + } + if (col_offset) { + this.history.update("figures", sheetId, figure.id!, "anchor", { + row: figure.anchor.row, + col: figure.anchor.col - col_offset, + } as Position); + } + } else if (figure.fixed_position && figure.offset.x + figure.width > gridWidth) { + this.history.update("figures", sheetId, figure.id!, "offset", { + x: Math.max(gridWidth - figure.width, 0), + y: figure.offset.y, + } as PixelPosition); } } } - private onColDeletion(sheetId: string) { - const numHeader = this.getters.getNumberCols(sheetId); - let gridWidth = 0; + private onRowRemove(sheetId: string, elements: number[]) { + const figures = this.getFigures(sheetId).sort((a, b) => a.anchor.row - b.anchor.row); + elements.sort((a, b) => a - b); + + const numHeader = this.getters.getNumberRows(sheetId); + let gridHeight = 0; for (let i = 0; i < numHeader; i++) { - gridWidth += this.getters.getColSize(sheetId, i); + // TODO : since the row size is an UI value now, this doesn't work anymore. Using the default cell height is + // a temporary solution at best, but is broken. + gridHeight += this.getters.getUserRowSize(sheetId, i) || DEFAULT_CELL_HEIGHT; } - const figures = this.getters.getFigures(sheetId); - for (const figure of figures) { - const newX = Math.min(figure.x, gridWidth - figure.width); - if (newX !== figure.x) { - this.dispatch("UPDATE_FIGURE", { sheetId, id: figure.id, x: newX }); + + let elements_index = 0; + for (const fig in figures) { + const figure = figures[fig]; + while (elements_index < elements.length && elements[elements_index] < figure.anchor.row) { + elements_index++; + } + if (elements_index && !figure.fixed_position) { + let row_offset = elements_index; + let y_offset = 0; + for ( + let row_size: Pixel; + (row_size = + this.getters.getUserRowSize(sheetId, figure.anchor.row - row_offset) || + DEFAULT_CELL_HEIGHT); + row_size < figure.offset.y - y_offset + ) { + row_offset -= 1; + y_offset += row_size; + } + if (y_offset) { + this.history.update("figures", sheetId, figure.id!, "offset", { + x: figure.offset.x, + y: figure.offset.y - y_offset, + } as PixelPosition); + } + if (row_offset) { + this.history.update("figures", sheetId, figure.id!, "anchor", { + row: figure.anchor.row - row_offset, + col: figure.anchor.col, + } as Position); + } + } else if (figure.fixed_position && figure.offset.y + figure.height > gridHeight) { + this.history.update("figures", sheetId, figure.id!, "offset", { + x: figure.offset.x, + y: Math.max(gridHeight - figure.height, 0), + } as PixelPosition); } } } - private updateFigure(sheetId: string, figure: Partial
) { - if (!("id" in figure)) { + private updateFigure(sheetId: string, update: Partial
) { + if (!("id" in update)) { return; } - for (const [key, value] of Object.entries(figure)) { + const figure: Figure = { ...this.getFigure(sheetId, update.id!)!, ...update }; + const figureUpdate: Partial
= { + ...update, + ...this.ensurePositionInside(sheetId, figure), + }; + for (const [key, value] of Object.entries(figureUpdate)) { switch (key) { - case "x": - case "y": - if (value !== undefined) { - this.history.update("figures", sheetId, figure.id!, key, Math.max(value as number, 0)); - } + case "fixed_position": + // TODO + this.history.update("figures", sheetId, figure.id!, key, value as boolean); + break; + case "offset": + const offset = value as PixelPosition; + this.history.update("figures", sheetId, figure.id!, key, { + x: Math.max(offset.x || 0, 0), + y: Math.max(offset.y || 0, 0), + }); + break; + case "anchor": + const anchor = value as Position; + this.history.update("figures", sheetId, figure.id!, key, { + col: Math.max(anchor.col || 0, 0), + row: Math.max(anchor.row || 0, 0), + }); break; case "width": case "height": @@ -127,7 +252,61 @@ export class FigurePlugin extends CorePlugin implements FigureState } } + private ensurePositionInside(sheetId: UID, figure: Figure): AnchorOffset { + return figure.fixed_position + ? this.ensurePositionInsideFixed(sheetId, figure) + : this.ensurePositionInsideAnchor(sheetId, figure); + } + + private ensurePositionInsideFixed(sheetId: UID, figure: Figure): AnchorOffset { + const { numberOfRows, numberOfCols } = this.getters.getSheetSize(sheetId); + let width = 0, + height = 0; + for (let rowNum = 0; rowNum < numberOfRows; rowNum++) { + height += this.getters.getUserRowSize(sheetId, rowNum) ?? DEFAULT_CELL_HEIGHT; + } + for (let colNum = 0; colNum < numberOfCols; colNum++) { + width += this.getters.getColSize(sheetId, colNum); + } + const offset = { + x: clip(figure.offset.x, 0, Math.max(width - figure.width, 0)), + y: clip(figure.offset.y, 0, Math.max(height - figure.height, 0)), + }; + + return { anchor: { col: 0, row: 0 }, offset }; + } + + private ensurePositionInsideAnchor(sheetId: UID, figure: Figure): AnchorOffset { + const { numberOfRows, numberOfCols } = this.getters.getSheetSize(sheetId); + let availableHeight = 0, + availableWidth = 0; + let rowNum, colNum; + let anchor = { ...figure.anchor }, + offset = { ...figure.offset }; + + for (rowNum = numberOfRows - 1; rowNum >= 0 && availableHeight >= figure.height; rowNum--) { + availableHeight += this.getters.getUserRowSize(sheetId, rowNum) ?? DEFAULT_CELL_HEIGHT; + } + + if (rowNum <= anchor.row) { + anchor.row = rowNum; + offset.y = clip(offset.y, 0, Math.max(availableHeight - figure.height, 0)); + } + + for (colNum = numberOfCols - 1; colNum >= 0 && availableWidth >= figure.width; colNum--) { + availableWidth += this.getters.getColSize(sheetId, colNum); + } + + if (colNum <= anchor.col) { + anchor.col = colNum; + offset.x = clip(offset.x, 0, Math.max(availableWidth - figure.width, 0)); + } + + return { anchor, offset }; + } + private addFigure(figure: Figure, sheetId: UID) { + figure = { ...figure, ...this.ensurePositionInside(sheetId, figure) }; this.history.update("figures", sheetId, figure.id, figure); } diff --git a/src/plugins/core/image.ts b/src/plugins/core/image.ts index f1ed070d2a..64da009410 100644 --- a/src/plugins/core/image.ts +++ b/src/plugins/core/image.ts @@ -5,11 +5,12 @@ import { Image } from "../../types/image"; import { CommandResult, CoreCommand, - DOMCoordinates, ExcelWorkbookData, Figure, FigureData, FigureSize, + PixelPosition, + Position, UID, WorkbookData, } from "../../types/index"; @@ -40,7 +41,7 @@ export class ImagePlugin extends CorePlugin implements ImageState { allowDispatch(cmd: CoreCommand) { switch (cmd.type) { case "CREATE_IMAGE": - if (this.getters.getFigure(cmd.sheetId, cmd.figureId)) { + if (this.getters.getFigure(cmd.sheetId, cmd.id)) { return CommandResult.InvalidFigureId; } return CommandResult.Success; @@ -52,8 +53,8 @@ export class ImagePlugin extends CorePlugin implements ImageState { handle(cmd: CoreCommand) { switch (cmd.type) { case "CREATE_IMAGE": - this.addImage(cmd.figureId, cmd.sheetId, cmd.position, cmd.size); - this.history.update("images", cmd.sheetId, cmd.figureId, cmd.definition); + this.addFigure(cmd.id, cmd.sheetId, cmd.anchor, cmd.offset, cmd.fixed_position, cmd.size); + this.history.update("images", cmd.sheetId, cmd.id, cmd.definition); this.syncedImages.add(cmd.definition.path); break; case "DUPLICATE_SHEET": { @@ -67,8 +68,10 @@ export class ImagePlugin extends CorePlugin implements ImageState { const size = { width: fig.width, height: fig.height }; this.dispatch("CREATE_IMAGE", { sheetId: cmd.sheetIdTo, - figureId: duplicatedFigureId, - position: { x: fig.x, y: fig.y }, + id: duplicatedFigureId, + offset: fig.offset, + anchor: fig.anchor, + fixed_position: fig.fixed_position, size, definition: deepCopy(image), }); @@ -123,11 +126,29 @@ export class ImagePlugin extends CorePlugin implements ImageState { // Private // --------------------------------------------------------------------------- - private addImage(id: UID, sheetId: UID, position: DOMCoordinates, size: FigureSize) { + private addFigure( + id: UID, + sheetId: UID, + anchor: Position | undefined, + offset: PixelPosition = { + x: 0, + y: 0, + }, + fixed_position: boolean = true, + size: FigureSize + ) { + if (this.getters.getFigure(sheetId, id)) { + return; + } + if (!anchor) { + anchor = { col: 0, row: 0 }; + fixed_position = true; + } const figure: Figure = { id, - x: position.x, - y: position.y, + anchor, + offset, + fixed_position, width: size.width, height: size.height, tag: "image", diff --git a/src/plugins/ui_stateful/sheetview.ts b/src/plugins/ui_stateful/sheetview.ts index 21cac6f1b3..fe290023b9 100644 --- a/src/plugins/ui_stateful/sheetview.ts +++ b/src/plugins/ui_stateful/sheetview.ts @@ -1,9 +1,10 @@ -import { getDefaultSheetViewSize } from "../../constants"; +import { DEFAULT_CELL_HEIGHT, getDefaultSheetViewSize } from "../../constants"; import { clip, findCellInNewZone, isDefined, largeMin, range } from "../../helpers"; import { scrollDelay } from "../../helpers/index"; import { InternalViewport } from "../../helpers/internal_viewport"; import { SelectionEvent } from "../../types/event_stream"; import { + AnchorOffset, CellPosition, Command, CommandResult, @@ -12,6 +13,7 @@ import { Dimension, EdgeScrollInfo, Figure, + FigureUI, HeaderIndex, LocalCommand, Pixel, @@ -104,6 +106,8 @@ export class SheetViewPlugin extends UIPlugin { "isPositionVisible", "getColDimensionsInViewport", "getRowDimensionsInViewport", + "getFigureUI", + "getAnchorOffset", ] as const; readonly viewports: Record = {}; @@ -296,6 +300,19 @@ export class SheetViewPlugin extends UIPlugin { return Math.max(...this.getSubViewports(sheetId).map((viewport) => viewport.getColIndex(x))); } + getColIndexHidden(x: Pixel): HeaderIndex { + // TODO Rename + const colIndex = this.getColIndex(x); + if (colIndex >= 0) return colIndex; + const sheetId = this.getters.getActiveSheetId(); + let i = 0; + x += this.getActiveSheetScrollInfo().scrollX; + for (; x > 0; i += 1) { + x -= this.getters.getColSize(sheetId, i); + } + return i - 1; + } + /** * Return the index of a row given an offset y, based on the viewport top * visible cell. @@ -306,6 +323,18 @@ export class SheetViewPlugin extends UIPlugin { return Math.max(...this.getSubViewports(sheetId).map((viewport) => viewport.getRowIndex(y))); } + getRowIndexHidden(y: Pixel): HeaderIndex { + const rowIndex = this.getRowIndex(y); + if (rowIndex >= 0) return rowIndex; + const sheetId = this.getters.getActiveSheetId(); + let i = 0; + y += this.getActiveSheetScrollInfo().scrollY; + for (; y > 0; i += 1) { + y -= this.getters.getUserRowSize(sheetId, i) ?? DEFAULT_CELL_HEIGHT; + } + return i - 1; + } + getSheetViewDimensionWithHeaders(): DOMDimension { return { width: this.sheetViewWidth + this.gridOffsetX, @@ -805,35 +834,79 @@ export class SheetViewPlugin extends UIPlugin { } } - getVisibleFigures(): Figure[] { + getVisibleFigures(): FigureUI[] { const sheetId = this.getters.getActiveSheetId(); - const result: Figure[] = []; + const result: FigureUI[] = []; const figures = this.getters.getFigures(sheetId); - const { scrollX, scrollY } = this.getActiveSheetScrollInfo(); - const { x: offsetCorrectionX, y: offsetCorrectionY } = - this.getters.getMainViewportCoordinates(); const { width, height } = this.getters.getSheetViewDimensionWithHeaders(); + const { x: offsetCorrectionX, y: offsetCorrectionY } = this.getMainViewportCoordinates(); + const { scrollX, scrollY } = this.getters.getActiveSheetScrollInfo(); for (const figure of figures) { + const figureUI = this.getFigureUI(sheetId, figure); + const { x, y } = figureUI; if ( - figure.x >= offsetCorrectionX && - (figure.x + figure.width <= offsetCorrectionX + scrollX || - figure.x >= width + scrollX + offsetCorrectionX) + x > offsetCorrectionX && + (x + figure.width < scrollX + offsetCorrectionX || x > width + scrollX + offsetCorrectionX) ) { continue; - } - if ( - figure.y >= offsetCorrectionY && - (figure.y + figure.height <= offsetCorrectionY + scrollY || - figure.y >= height + scrollY + offsetCorrectionY) + } else if ( + y > offsetCorrectionY && + (y + figure.height < scrollY + offsetCorrectionY || + y > height + scrollY + offsetCorrectionY) ) { continue; } - result.push(figure); + result.push(figureUI); } return result; } + getFigureUI(sheetId: string, figure: Figure): FigureUI { + const x = figure.offset.x + this.getters.getColDimensions(sheetId, figure.anchor.col)["start"]; + const y = figure.offset.y + this.getters.getRowDimensions(sheetId, figure.anchor.row)["start"]; + return { x, y, figure }; + } + + getAnchorOffset(sheetId: string, figureUI: FigureUI): AnchorOffset { + const { scrollX, scrollY } = this.getters.getActiveSheetScrollInfo(); + let anchor: Position; + if (figureUI.figure.fixed_position) { + anchor = { col: 0, row: 0 }; + } else { + anchor = { + col: this.getColIndexHidden(figureUI.x - scrollX), + row: this.getRowIndexHidden(figureUI.y - scrollY), + }; + } + return { + anchor, + offset: { + x: this.getColOffset(sheetId, anchor.col, figureUI.x), + y: this.getRowOffset(sheetId, anchor.row, figureUI.y), + }, + }; + } + + getColOffset(sheetId: string, col: HeaderIndex, x: Pixel): Pixel { + const { x: offsetCorrectionX } = this.getMainViewportCoordinates(); + const { scrollX } = this.getters.getActiveSheetScrollInfo(); + + if (0 <= x - scrollX && x - scrollX < offsetCorrectionX) { + return x - this.getters.getColDimensions(sheetId, col)["start"] - scrollX; + } + return x - this.getters.getColDimensions(sheetId, col)["start"]; + } + + getRowOffset(sheetId: string, row: HeaderIndex, y: Pixel): Pixel { + const { y: offsetCorrectionY } = this.getMainViewportCoordinates(); + const { scrollY } = this.getters.getActiveSheetScrollInfo(); + if (0 <= y - scrollY && y - scrollY < offsetCorrectionY) { + return y - this.getters.getRowDimensions(sheetId, row)["start"] - scrollY; + } + return y - this.getters.getRowDimensions(sheetId, row)["start"]; + } + isPositionVisible(position: PixelPosition): boolean { const { scrollX, scrollY } = this.getters.getActiveSheetScrollInfo(); const { x: mainViewportX, y: mainViewportY } = this.getters.getMainViewportCoordinates(); diff --git a/src/types/chart/chart.ts b/src/types/chart/chart.ts index da1195f6c0..c6dfa539f0 100644 --- a/src/types/chart/chart.ts +++ b/src/types/chart/chart.ts @@ -160,6 +160,7 @@ export interface ChartCreationContext { readonly axesDesign?: AxesDesign; readonly fillArea?: boolean; readonly showValues?: boolean; + readonly fixed_position?: boolean; } export type ChartAxisFormats = { [axisId: string]: Format | undefined } | undefined; diff --git a/src/types/chart/common_bar_combo.ts b/src/types/chart/common_bar_combo.ts index 3aa6e97030..817f4a405e 100644 --- a/src/types/chart/common_bar_combo.ts +++ b/src/types/chart/common_bar_combo.ts @@ -5,6 +5,7 @@ import { LegendPosition } from "./common_chart"; export interface ComboBarChartDefinition { readonly dataSets: CustomizedDataSet[]; readonly dataSetsHaveTitle: boolean; + readonly fixed_position: boolean; readonly labelRange?: string; readonly title: TitleDesign; readonly background?: Color; diff --git a/src/types/chart/gauge_chart.ts b/src/types/chart/gauge_chart.ts index bcae97be24..698e905a7d 100644 --- a/src/types/chart/gauge_chart.ts +++ b/src/types/chart/gauge_chart.ts @@ -5,6 +5,7 @@ import { TitleDesign } from "./chart"; export interface GaugeChartDefinition { readonly type: "gauge"; readonly title: TitleDesign; + readonly fixed_position: boolean; readonly dataRange?: string; readonly sectionRule: SectionRule; readonly background?: Color; diff --git a/src/types/chart/geo_chart.ts b/src/types/chart/geo_chart.ts index 5d14c6967d..6289e098fb 100644 --- a/src/types/chart/geo_chart.ts +++ b/src/types/chart/geo_chart.ts @@ -9,6 +9,7 @@ export interface GeoChartDefinition { readonly dataSetsHaveTitle: boolean; readonly labelRange?: string; readonly title: TitleDesign; + readonly fixed_position: boolean; readonly background?: Color; readonly legendPosition: LegendPosition; readonly axesDesign?: AxesDesign; diff --git a/src/types/chart/line_chart.ts b/src/types/chart/line_chart.ts index 79f97b5d04..5da7950304 100644 --- a/src/types/chart/line_chart.ts +++ b/src/types/chart/line_chart.ts @@ -12,6 +12,7 @@ export interface LineChartDefinition { readonly background?: Color; readonly legendPosition: LegendPosition; readonly labelsAsText: boolean; + readonly fixed_position: boolean; readonly stacked: boolean; readonly aggregated?: boolean; readonly cumulative: boolean; diff --git a/src/types/chart/pie_chart.ts b/src/types/chart/pie_chart.ts index e48c08e225..0cd3cecc80 100644 --- a/src/types/chart/pie_chart.ts +++ b/src/types/chart/pie_chart.ts @@ -11,6 +11,7 @@ export interface PieChartDefinition { readonly title: TitleDesign; readonly background?: Color; readonly legendPosition: LegendPosition; + readonly fixed_position: boolean; readonly aggregated?: boolean; readonly axesDesign?: AxesDesign; readonly isDoughnut?: boolean; diff --git a/src/types/chart/radar_chart.ts b/src/types/chart/radar_chart.ts index ff90395607..427c992ef2 100644 --- a/src/types/chart/radar_chart.ts +++ b/src/types/chart/radar_chart.ts @@ -8,6 +8,7 @@ export interface RadarChartDefinition { readonly dataSetsHaveTitle: boolean; readonly labelRange?: string; readonly title: TitleDesign; + readonly fixed_position: boolean; readonly background?: Color; readonly legendPosition: LegendPosition; readonly aggregated?: boolean; diff --git a/src/types/chart/scorecard_chart.ts b/src/types/chart/scorecard_chart.ts index 6255f3b317..05fadd6151 100644 --- a/src/types/chart/scorecard_chart.ts +++ b/src/types/chart/scorecard_chart.ts @@ -4,6 +4,7 @@ import { TitleDesign } from "./chart"; export interface ScorecardChartDefinition { readonly type: "scorecard"; readonly title: TitleDesign; + readonly fixed_position: boolean; readonly keyValue?: string; readonly baseline?: string; readonly baselineMode: BaselineMode; diff --git a/src/types/chart/waterfall_chart.ts b/src/types/chart/waterfall_chart.ts index 4afd0e6a7b..cd926c9616 100644 --- a/src/types/chart/waterfall_chart.ts +++ b/src/types/chart/waterfall_chart.ts @@ -9,6 +9,7 @@ export interface WaterfallChartDefinition { readonly dataSetsHaveTitle: boolean; readonly labelRange?: string; readonly title: TitleDesign; + readonly fixed_position: boolean; readonly background?: Color; readonly verticalAxisPosition: VerticalAxisPosition; readonly legendPosition: LegendPosition; diff --git a/src/types/commands.ts b/src/types/commands.ts index 301c950337..62a874c6e7 100644 --- a/src/types/commands.ts +++ b/src/types/commands.ts @@ -1,6 +1,5 @@ import { ConditionalFormat, - DOMCoordinates, DataValidationRule, Figure, Format, @@ -16,6 +15,8 @@ import { Dimension, HeaderIndex, Pixel, + PixelPosition, + Position, SetDecimalStep, SortDirection, SortOptions, @@ -496,15 +497,20 @@ export interface DeleteFigureCommand extends SheetDependentCommand { id: UID; } +interface SubFigureCommand extends SheetDependentCommand { + id: UID; + anchor?: Position; + offset?: PixelPosition; + fixed_position?: boolean; + size?: FigureSize; +} + //------------------------------------------------------------------------------ // Chart //------------------------------------------------------------------------------ -export interface CreateChartCommand extends SheetDependentCommand { +export interface CreateChartCommand extends SheetDependentCommand, SubFigureCommand { type: "CREATE_CHART"; - id: UID; - position?: DOMCoordinates; - size?: FigureSize; definition: ChartDefinition; } @@ -518,10 +524,8 @@ export interface UpdateChartCommand extends SheetDependentCommand { // Image //------------------------------------------------------------------------------ -export interface CreateImageOverCommand extends SheetDependentCommand { +export interface CreateImageOverCommand extends SheetDependentCommand, SubFigureCommand { type: "CREATE_IMAGE"; - figureId: UID; - position: DOMCoordinates; size: FigureSize; definition: Image; } diff --git a/src/types/figure.ts b/src/types/figure.ts index 52b04da130..fefaa67fe5 100644 --- a/src/types/figure.ts +++ b/src/types/figure.ts @@ -1,14 +1,24 @@ -import { Pixel, UID } from "."; +import { DOMCoordinates, Pixel, PixelPosition, Position, UID } from "."; export interface Figure { id: UID; - x: Pixel; - y: Pixel; + anchor: Position; + offset: PixelPosition; + fixed_position: boolean; width: Pixel; height: Pixel; tag: string; } +export interface FigureUI extends DOMCoordinates { + figure: Figure; +} + +export interface AnchorOffset { + anchor: Position; + offset: PixelPosition; +} + export interface FigureSize { width: Pixel; height: Pixel; diff --git a/src/types/workbook_data.ts b/src/types/workbook_data.ts index ce9037b8e3..00e7446808 100644 --- a/src/types/workbook_data.ts +++ b/src/types/workbook_data.ts @@ -2,7 +2,18 @@ import { CellValue, DataValidationRule, Format, Locale } from "."; import { ExcelChartDefinition } from "./chart/chart"; import { ConditionalFormat } from "./conditional_formatting"; import { Image } from "./image"; -import { Border, Color, Dimension, HeaderGroup, PaneDivision, Pixel, Style, UID } from "./misc"; +import { + Border, + Color, + Dimension, + HeaderGroup, + PaneDivision, + Pixel, + PixelPosition, + Position, + Style, + UID, +} from "./misc"; import { PivotCoreDefinition } from "./pivot"; import { CoreTableType, TableConfig, TableStyleTemplateName } from "./table"; @@ -19,8 +30,9 @@ export interface HeaderData { export interface FigureData { id: UID; - x: Pixel; - y: Pixel; + anchor: Position; + offset: PixelPosition; + fixed_position: boolean; width: Pixel; height: Pixel; tag: string; diff --git a/src/xlsx/conversion/figure_conversion.ts b/src/xlsx/conversion/figure_conversion.ts index 2256ce2f63..490fc8c071 100644 --- a/src/xlsx/conversion/figure_conversion.ts +++ b/src/xlsx/conversion/figure_conversion.ts @@ -5,7 +5,14 @@ import { toUnboundedZone, zoneToXc, } from "../../helpers"; -import { ChartDefinition, ExcelChartDefinition, FigureData } from "../../types"; +import { + ChartDefinition, + ExcelChartDefinition, + FigureData, + PixelPosition, + Position, +} from "../../types"; +import { AnchorOffset } from "../../types/figure"; import { ExcelImage } from "../../types/image"; import { XLSXFigure, XLSXWorksheet } from "../../types/xlsx"; import { convertEMUToDotValue, getColPosition, getRowPosition } from "../helpers/content_helpers"; @@ -24,32 +31,36 @@ function convertFigure( id: string, sheetData: XLSXWorksheet ): FigureData | undefined { - let x1: number, y1: number; + let anchor: Position; + let offset: PixelPosition; let height: number, width: number; if (figure.anchors.length === 1) { // one cell anchor - ({ x: x1, y: y1 } = getPositionFromAnchor(figure.anchors[0], sheetData)); + ({ anchor, offset } = convertAnchor(figure.anchors[0])); width = convertEMUToDotValue(figure.figureSize!.cx); height = convertEMUToDotValue(figure.figureSize!.cy); } else { - ({ x: x1, y: y1 } = getPositionFromAnchor(figure.anchors[0], sheetData)); + ({ anchor, offset } = convertAnchor(figure.anchors[0])); + const { x: x1, y: y1 } = getPositionFromAnchor(figure.anchors[1], sheetData); const { x: x2, y: y2 } = getPositionFromAnchor(figure.anchors[1], sheetData); width = x2 - x1; height = y2 - y1; } - const figureData = { id, x: x1, y: y1 }; + const figureData = { id, anchor, offset }; if (isChartData(figure.data)) { return { ...figureData, width, height, + fixed_position: false, tag: "chart", data: convertChartData(figure.data), }; } else if (isImageData(figure.data)) { return { ...figureData, + fixed_position: false, width: convertEMUToDotValue(figure.data.size.cx), height: convertEMUToDotValue(figure.data.size.cy), tag: "image", @@ -94,6 +105,7 @@ function convertChartData(chartData: ExcelChartDefinition): ChartDefinition | un dataSets, dataSetsHaveTitle, labelRange, + fixed_position: false, title: chartData.title ?? { text: "" }, type: chartData.type, background: convertColor({ rgb: chartData.backgroundColor }) || "#FFFFFF", @@ -121,6 +133,15 @@ function convertExcelRangeToSheetXC(range: string, dataSetsHaveTitle: boolean): return getFullReference(sheetName, dataXC); } +function convertAnchor(XLSXanchor: XLSXFigureAnchor): AnchorOffset { + const anchor = { col: XLSXanchor.col, row: XLSXanchor.row }; + const offset = { + x: convertEMUToDotValue(XLSXanchor.colOffset), + y: convertEMUToDotValue(XLSXanchor.rowOffset), + }; + return { anchor, offset }; +} + function getPositionFromAnchor( anchor: XLSXFigureAnchor, sheetData: XLSXWorksheet diff --git a/src/xlsx/functions/drawings.ts b/src/xlsx/functions/drawings.ts index 922a2ce13a..6ff0bbf995 100644 --- a/src/xlsx/functions/drawings.ts +++ b/src/xlsx/functions/drawings.ts @@ -1,5 +1,4 @@ -import { FIGURE_BORDER_WIDTH } from "../../constants"; -import { HeaderData, SheetData } from "../../types"; +import { SheetData } from "../../types"; import { ExcelChartDefinition } from "../../types/chart/chart"; import { XMLAttributes, XMLString } from "../../types/xlsx"; import { DRAWING_NS_A, DRAWING_NS_C, NAMESPACE, RELATIONSHIP_NSR } from "../constants"; @@ -69,27 +68,27 @@ function convertFigureData( figure: FigureData, sheet: SheetData ): FigurePosition { - const { x, y, height, width } = figure; - - const cols = Object.values(sheet.cols); - const rows = Object.values(sheet.rows); - const { index: colFrom, offset: offsetColFrom } = figureCoordinates(cols, x); - const { index: colTo, offset: offsetColTo } = figureCoordinates(cols, x + width); - const { index: rowFrom, offset: offsetRowFrom } = figureCoordinates(rows, y); - const { index: rowTo, offset: offsetRowTo } = figureCoordinates(rows, y + height); + const { anchor, offset } = figure; + // const cols = Object.values(sheet.cols); + // const rows = Object.values(sheet.rows); + // const { index: colFrom, offset: offsetColFrom } = figureCoordinates(cols, x); + // const { index: colTo, offset: offsetColTo } = figureCoordinates(cols, x + width); + // const { index: rowFrom, offset: offsetRowFrom } = figureCoordinates(rows, y); + // const { index: rowTo, offset: offsetRowTo } = figureCoordinates(rows, y + height); + // TODO return { from: { - col: colFrom, - colOff: offsetColFrom, - row: rowFrom, - rowOff: offsetRowFrom, + col: anchor.col, + colOff: offset.x, + row: anchor.row, + rowOff: offset.y, }, to: { - col: colTo, - colOff: offsetColTo, - row: rowTo, - rowOff: offsetRowTo, + col: anchor.col + 1, + colOff: offset.x, + row: anchor.row + 1, + rowOff: offset.y, }, }; } @@ -97,26 +96,26 @@ function convertFigureData( /** Returns figure coordinates in EMU for a specific header dimension * See https://docs.microsoft.com/en-us/windows/win32/vml/msdn-online-vml-units#other-units-of-measurement */ -function figureCoordinates( - headers: HeaderData[], - position: number -): { index: number; offset: number } { - let currentPosition = 0; - for (const [headerIndex, header] of headers.entries()) { - if (currentPosition <= position && position < currentPosition + header.size!) { - return { - index: headerIndex, - offset: convertDotValueToEMU(position - currentPosition + FIGURE_BORDER_WIDTH), - }; - } else if (headerIndex < headers.length - 1) { - currentPosition += header.size!; - } - } - return { - index: headers.length - 1, - offset: convertDotValueToEMU(position - currentPosition + FIGURE_BORDER_WIDTH), - }; -} +// function figureCoordinates( +// headers: HeaderData[], +// position: number +// ): { index: number; offset: number } { +// let currentPosition = 0; +// for (const [headerIndex, header] of headers.entries()) { +// if (currentPosition <= position && position < currentPosition + header.size!) { +// return { +// index: headerIndex, +// offset: convertDotValueToEMU(position - currentPosition + FIGURE_BORDER_WIDTH), +// }; +// } else if (headerIndex < headers.length - 1) { +// currentPosition += header.size!; +// } +// } +// return { +// index: headers.length - 1, +// offset: convertDotValueToEMU(position - currentPosition + FIGURE_BORDER_WIDTH), +// }; +// } function createChartDrawing( figure: FigureData, diff --git a/tests/clipboard/clipboard_figure_plugin.test.ts b/tests/clipboard/clipboard_figure_plugin.test.ts index a3b93d4798..58d4118c4c 100644 --- a/tests/clipboard/clipboard_figure_plugin.test.ts +++ b/tests/clipboard/clipboard_figure_plugin.test.ts @@ -30,7 +30,7 @@ describe.each(["chart", "image"])("Clipboard for %s figures", (type: string) => if (type === "chart") { createChart(model, { type: "bar" }, figureId); } else if (type === "image") { - createImage(model, { figureId }); + createImage(model, { id: figureId }); } await nextTick(); }); @@ -88,9 +88,10 @@ describe.each(["chart", "image"])("Clipboard for %s figures", (type: string) => model.dispatch("SELECT_FIGURE", { id: figureId }); copy(model); paste(model, "C3:C10, B8"); - const copiedFigure = model.getters.getFigure(sheetId, getCopiedFigureId()); - expect(copiedFigure?.x).toEqual(2 * DEFAULT_CELL_WIDTH); - expect(copiedFigure?.y).toEqual(2 * DEFAULT_CELL_HEIGHT); + const copiedFigure = model.getters.getFigure(sheetId, getCopiedFigureId())!; + const figureUI = model.getters.getFigureUI(sheetId, copiedFigure); + expect(figureUI.x).toEqual(2 * DEFAULT_CELL_WIDTH); + expect(figureUI.y).toEqual(2 * DEFAULT_CELL_HEIGHT); }); test("Figure size is copied", () => { @@ -140,6 +141,7 @@ describe.each(["chart", "image"])("Clipboard for %s figures", (type: string) => copy(model); paste(model, "Z100"); const copiedFigure = model.getters.getFigure(sheetId, getCopiedFigureId())!; + const figureUI = model.getters.getFigureUI(sheetId, copiedFigure); const maxX = model.getters.getColDimensions( sheetId, model.getters.getNumberCols(sheetId) - 1 @@ -148,8 +150,8 @@ describe.each(["chart", "image"])("Clipboard for %s figures", (type: string) => sheetId, model.getters.getNumberRows(sheetId) - 1 ).end; - expect(copiedFigure.x).toBe(maxX - copiedFigure.width); - expect(copiedFigure.y).toBe(maxY - copiedFigure.height); + expect(figureUI.x).toBe(maxX - copiedFigure.width); + expect(figureUI.y).toBe(maxY - copiedFigure.height); }); test("Can paste a chart with ranges that were deleted between the copy and the paste", () => { diff --git a/tests/collaborative/collaborative.test.ts b/tests/collaborative/collaborative.test.ts index 96e613e504..9fdcf910aa 100644 --- a/tests/collaborative/collaborative.test.ts +++ b/tests/collaborative/collaborative.test.ts @@ -482,8 +482,9 @@ describe("Multi users synchronisation", () => { tag: "hey", width: 100, height: 100, - x: 100, - y: 100, + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 100, y: 100 }, }; alice.dispatch("CREATE_FIGURE", { sheetId, @@ -532,7 +533,15 @@ describe("Multi users synchronisation", () => { }); test("Selected figure Id is not modified if the create sheet comes from someone else", () => { - const figure = { id: "42", x: 0, y: 0, width: 100, height: 100, tag: "text" }; + const figure = { + id: "42", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 0, y: 0 }, + width: 100, + height: 100, + tag: "text", + }; const sheetId = alice.getters.getActiveSheetId(); alice.dispatch("CREATE_FIGURE", { sheetId, figure }); alice.dispatch("SELECT_FIGURE", { id: "42" }); diff --git a/tests/collaborative/collaborative_history.test.ts b/tests/collaborative/collaborative_history.test.ts index 809d263045..d78c236c6e 100644 --- a/tests/collaborative/collaborative_history.test.ts +++ b/tests/collaborative/collaborative_history.test.ts @@ -406,7 +406,7 @@ describe("Collaborative local history", () => { type: "CREATE_CHART", sheetId: "sheet1", id: "fig2", - position: { + offset: { x: 0, y: 0, }, @@ -421,6 +421,7 @@ describe("Collaborative local history", () => { stacked: false, dataSetsHaveTitle: false, legendPosition: "none", + fixed_position: false, }, }, { diff --git a/tests/collaborative/collaborative_sheet_manipulations.test.ts b/tests/collaborative/collaborative_sheet_manipulations.test.ts index 8e43bc91a6..5ea8c82b97 100644 --- a/tests/collaborative/collaborative_sheet_manipulations.test.ts +++ b/tests/collaborative/collaborative_sheet_manipulations.test.ts @@ -150,8 +150,12 @@ describe("Collaborative Sheet manipulation", () => { width: 100, id: "456", tag: "test", - x: 0, - y: 0, + offset: { + x: 0, + y: 0, + }, + anchor: { col: 0, row: 0 }, + fixed_position: true, }, }); network.concurrent(() => { @@ -592,6 +596,7 @@ describe("Collaborative Sheet manipulation", () => { background: BACKGROUND_CHART_COLOR, legendPosition: "top", aggregated: false, + fixed_position: false, }; test(`Concurrently chart creation & update and add columns`, () => { diff --git a/tests/colors/custom_colors_plugin.test.ts b/tests/colors/custom_colors_plugin.test.ts index 81b13ba939..c920f77f6a 100644 --- a/tests/colors/custom_colors_plugin.test.ts +++ b/tests/colors/custom_colors_plugin.test.ts @@ -118,6 +118,7 @@ describe("custom colors are correctly handled when editing charts", () => { legendPosition: "none", background: "#112233", aggregated: false, + fixed_position: false, }, }); expect(model.getters.getCustomColors()).toEqual(["#112233", "#123456"]); @@ -137,6 +138,7 @@ describe("custom colors are correctly handled when editing charts", () => { title: { text: "a title" }, type: "gauge", dataRange: "B1:B4", + fixed_position: false, sectionRule: { rangeMin: "0", rangeMax: "100", diff --git a/tests/figures/chart/bar_chart_plugin.test.ts b/tests/figures/chart/bar_chart_plugin.test.ts index f784647634..edbdf41407 100644 --- a/tests/figures/chart/bar_chart_plugin.test.ts +++ b/tests/figures/chart/bar_chart_plugin.test.ts @@ -21,6 +21,7 @@ describe("bar chart", () => { auxiliaryRange: "Sheet1!A1:A4", legendPosition: "bottom", cumulative: true, + fixed_position: false, labelsAsText: true, dataSetsHaveTitle: true, aggregated: true, @@ -45,6 +46,7 @@ describe("bar chart", () => { stacked: true, axesDesign: {}, showValues: false, + fixed_position: false, }); }); diff --git a/tests/figures/chart/chart_plugin.test.ts b/tests/figures/chart/chart_plugin.test.ts index 3b2db7f55c..ccc2293b9a 100644 --- a/tests/figures/chart/chart_plugin.test.ts +++ b/tests/figures/chart/chart_plugin.test.ts @@ -916,7 +916,8 @@ describe("datasource tests", function () { sheetIdTo: "SheetWithFigure", }); activateSheet(model, "2"); - const { x, y, height, width, tag } = model.getters.getVisibleFigures()[0]; + const { x, y, figure } = model.getters.getVisibleFigures()[0]; + const { height, width, tag } = figure; activateSheet(model, "SheetWithFigure"); expect(model.getters.getVisibleFigures()).toMatchObject([{ x, y, height, width, tag }]); }); @@ -1637,6 +1638,7 @@ describe("Chart without labels", () => { type: "bar", stacked: false, aggregated: false, + fixed_position: false, }; test("The legend is displayed even when there is only one dataSet or no label", () => { @@ -1712,6 +1714,7 @@ describe("Chart design configuration", () => { labelRange: "A3", stacked: false, aggregated: false, + fixed_position: false, }; test("Legend position", () => { @@ -2429,6 +2432,7 @@ describe("Chart aggregate labels", () => { type: "bar", stacked: false, aggregated: false, + fixed_position: false, }; aggregatedModel = new Model({ sheets: [ diff --git a/tests/figures/chart/charts_component.test.ts b/tests/figures/chart/charts_component.test.ts index 22d70d7dc6..e65d0a3fab 100644 --- a/tests/figures/chart/charts_component.test.ts +++ b/tests/figures/chart/charts_component.test.ts @@ -162,8 +162,11 @@ describe("charts", () => { height: 335, tag: "chart", width: 536, - x: 0, - y: 0, + anchor: { col: 0, row: 0 }, + offset: { + x: 0, + y: 0, + }, }, ]); }); diff --git a/tests/figures/chart/combo_chart_plugin.test.ts b/tests/figures/chart/combo_chart_plugin.test.ts index cf865430e7..88d9a5a810 100644 --- a/tests/figures/chart/combo_chart_plugin.test.ts +++ b/tests/figures/chart/combo_chart_plugin.test.ts @@ -19,6 +19,7 @@ describe("combo chart", () => { auxiliaryRange: "Sheet1!A1:A4", legendPosition: "bottom", cumulative: true, + fixed_position: false, labelsAsText: true, dataSetsHaveTitle: true, aggregated: true, @@ -42,6 +43,7 @@ describe("combo chart", () => { aggregated: true, axesDesign: {}, showValues: false, + fixed_position: false, }); }); diff --git a/tests/figures/chart/common_chart_plugin.test.ts b/tests/figures/chart/common_chart_plugin.test.ts index a5a75aaed9..a3c3934787 100644 --- a/tests/figures/chart/common_chart_plugin.test.ts +++ b/tests/figures/chart/common_chart_plugin.test.ts @@ -110,10 +110,13 @@ describe("Single cell chart background color", () => { model.dispatch("UPDATE_FIGURE", { sheetId, id: firstSheetFigures[0].id, - x: 0, - y: 0, + offset: { + x: 0, + y: 0, + }, width: 123, height: 321, + fixed_position: true, }); model.dispatch("DUPLICATE_SHEET", { sheetIdTo: secondSheetId, diff --git a/tests/figures/chart/gauge/gauge_chart_plugin.test.ts b/tests/figures/chart/gauge/gauge_chart_plugin.test.ts index ad8bd5ed4e..e44c10a3b2 100644 --- a/tests/figures/chart/gauge/gauge_chart_plugin.test.ts +++ b/tests/figures/chart/gauge/gauge_chart_plugin.test.ts @@ -123,6 +123,7 @@ describe("datasource tests", function () { auxiliaryRange: "Sheet1!A1:A4", legendPosition: "bottom", cumulative: true, + fixed_position: false, labelsAsText: true, dataSetsHaveTitle: true, aggregated: true, @@ -141,6 +142,7 @@ describe("datasource tests", function () { title: { text: "hello there" }, dataRange: "Sheet1!B1:B4", sectionRule: expect.any(Object), + fixed_position: false, }); }); @@ -489,6 +491,7 @@ describe("Chart design configuration", () => { title: { text: "My chart" }, type: "gauge", sectionRule: deepCopy(defaultSectionRule), + fixed_position: false, }; }); diff --git a/tests/figures/chart/line_chart_plugin.test.ts b/tests/figures/chart/line_chart_plugin.test.ts index ed6431db41..76970b21f8 100644 --- a/tests/figures/chart/line_chart_plugin.test.ts +++ b/tests/figures/chart/line_chart_plugin.test.ts @@ -14,6 +14,7 @@ describe("line chart", () => { legendPosition: "bottom", cumulative: true, labelsAsText: true, + fixed_position: false, dataSetsHaveTitle: true, aggregated: true, stacked: true, @@ -33,6 +34,7 @@ describe("line chart", () => { labelRange: "Sheet1!A1:A4", legendPosition: "bottom", dataSetsHaveTitle: true, + fixed_position: false, aggregated: true, stacked: true, labelsAsText: true, diff --git a/tests/figures/chart/pie_chart_plugin.test.ts b/tests/figures/chart/pie_chart_plugin.test.ts index 5a8faa3cf6..ca79a55acd 100644 --- a/tests/figures/chart/pie_chart_plugin.test.ts +++ b/tests/figures/chart/pie_chart_plugin.test.ts @@ -16,6 +16,7 @@ describe("pie chart", () => { labelsAsText: true, dataSetsHaveTitle: true, aggregated: true, + fixed_position: false, stacked: true, firstValueAsSubtotal: true, showConnectorLines: false, @@ -36,6 +37,7 @@ describe("pie chart", () => { aggregated: true, isDoughnut: false, showValues: false, + fixed_position: false, }); }); diff --git a/tests/figures/chart/pyramid_chart_plugin.test.ts b/tests/figures/chart/pyramid_chart_plugin.test.ts index 8a194d72fd..299177ba84 100644 --- a/tests/figures/chart/pyramid_chart_plugin.test.ts +++ b/tests/figures/chart/pyramid_chart_plugin.test.ts @@ -14,6 +14,7 @@ describe("population pyramid chart", () => { legendPosition: "bottom", cumulative: true, labelsAsText: true, + fixed_position: false, dataSetsHaveTitle: true, aggregated: true, stacked: false, @@ -38,6 +39,7 @@ describe("population pyramid chart", () => { axesDesign: {}, horizontal: true, showValues: false, + fixed_position: false, }); }); diff --git a/tests/figures/chart/radar_chart_plugin.test.ts b/tests/figures/chart/radar_chart_plugin.test.ts index ee43c64d2b..c9e9721e4a 100644 --- a/tests/figures/chart/radar_chart_plugin.test.ts +++ b/tests/figures/chart/radar_chart_plugin.test.ts @@ -21,6 +21,7 @@ describe("radar chart", () => { legendPosition: "bottom", cumulative: true, labelsAsText: true, + fixed_position: false, dataSetsHaveTitle: true, aggregated: true, stacked: true, @@ -44,6 +45,7 @@ describe("radar chart", () => { fillArea: true, stacked: true, showValues: true, + fixed_position: false, }); }); diff --git a/tests/figures/chart/scorecard/scorecard_chart_component.test.ts b/tests/figures/chart/scorecard/scorecard_chart_component.test.ts index 5a6357977a..04b5c70829 100644 --- a/tests/figures/chart/scorecard/scorecard_chart_component.test.ts +++ b/tests/figures/chart/scorecard/scorecard_chart_component.test.ts @@ -34,8 +34,10 @@ function updateScorecardChartSize(width: Pixel, height: Pixel) { model.dispatch("UPDATE_FIGURE", { sheetId, id: chartId, - x: 0, - y: 0, + offset: { + x: 0, + y: 0, + }, width, height, }); diff --git a/tests/figures/chart/scorecard/scorecard_chart_plugin.test.ts b/tests/figures/chart/scorecard/scorecard_chart_plugin.test.ts index 31ab436588..5f190cd189 100644 --- a/tests/figures/chart/scorecard/scorecard_chart_plugin.test.ts +++ b/tests/figures/chart/scorecard/scorecard_chart_plugin.test.ts @@ -87,6 +87,7 @@ describe("datasource tests", function () { auxiliaryRange: "Sheet1!A1:A4", legendPosition: "bottom", cumulative: true, + fixed_position: false, labelsAsText: true, dataSetsHaveTitle: true, aggregated: true, @@ -108,6 +109,7 @@ describe("datasource tests", function () { baselineMode: DEFAULT_SCORECARD_BASELINE_MODE, baselineColorUp: DEFAULT_SCORECARD_BASELINE_COLOR_UP, baselineColorDown: DEFAULT_SCORECARD_BASELINE_COLOR_DOWN, + fixed_position: false, }); }); diff --git a/tests/figures/chart/waterfall/waterfall_chart_plugin.test.ts b/tests/figures/chart/waterfall/waterfall_chart_plugin.test.ts index 69658242dd..58254180b6 100644 --- a/tests/figures/chart/waterfall/waterfall_chart_plugin.test.ts +++ b/tests/figures/chart/waterfall/waterfall_chart_plugin.test.ts @@ -302,6 +302,7 @@ describe("Waterfall chart", () => { auxiliaryRange: "Sheet1!A1:A4", legendPosition: "bottom", cumulative: true, + fixed_position: false, labelsAsText: true, dataSetsHaveTitle: true, aggregated: true, @@ -329,6 +330,7 @@ describe("Waterfall chart", () => { axesDesign: {}, verticalAxisPosition: "left", showValues: false, + fixed_position: false, }); }); }); diff --git a/tests/figures/figure_component.test.ts b/tests/figures/figure_component.test.ts index 67a8b815c2..6124609bd3 100644 --- a/tests/figures/figure_component.test.ts +++ b/tests/figures/figure_component.test.ts @@ -64,8 +64,9 @@ function createFigure( ) { const defaultParameters: CreateFigureCommand["figure"] = { id: "someuuid", - x: 1, - y: 1, + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 1, y: 1 }, height: 100, width: 100, tag: "text", @@ -134,7 +135,15 @@ describe("figures", () => { test("can create a figure with some data", () => { createFigure(model); expect(model.getters.getFigures(sheetId)).toEqual([ - { id: "someuuid", height: 100, tag: "text", width: 100, x: 1, y: 1 }, + { + id: "someuuid", + height: 100, + tag: "text", + width: 100, + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 1, y: 1 }, + }, ]); }); test("focus a figure", async () => { @@ -188,7 +197,12 @@ describe("figures", () => { test("Can move a figure with keyboard", async () => { createFigure(model); let figure = model.getters.getFigure(sheetId, "someuuid"); - expect(figure).toMatchObject({ id: "someuuid", x: 1, y: 1 }); + expect(figure).toMatchObject({ + id: "someuuid", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 1, y: 1 }, + }); await nextTick(); await simulateClick(".o-figure"); await nextTick(); @@ -198,20 +212,40 @@ describe("figures", () => { keyDown({ key: "ArrowDown" }); await keyDown({ key: "ArrowDown" }); figure = model.getters.getFigure(sheetId, "someuuid"); - expect(figure).toMatchObject({ id: "someuuid", x: 1, y: 3 }); + expect(figure).toMatchObject({ + id: "someuuid", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 1, y: 3 }, + }); //right keyDown({ key: "ArrowRight" }); await keyDown({ key: "ArrowRight" }); figure = model.getters.getFigure(sheetId, "someuuid"); - expect(figure).toMatchObject({ id: "someuuid", x: 3, y: 3 }); + expect(figure).toMatchObject({ + id: "someuuid", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 3, y: 3 }, + }); //left await keyDown({ key: "ArrowLeft" }); figure = model.getters.getFigure(sheetId, "someuuid"); - expect(figure).toMatchObject({ id: "someuuid", x: 2, y: 3 }); + expect(figure).toMatchObject({ + id: "someuuid", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 2, y: 3 }, + }); //up await keyDown({ key: "ArrowUp" }); figure = model.getters.getFigure(sheetId, "someuuid"); - expect(figure).toMatchObject({ id: "someuuid", x: 2, y: 2 }); + expect(figure).toMatchObject({ + id: "someuuid", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 2, y: 2 }, + }); }); test("figure is focused after a SELECT_FIGURE", async () => { @@ -257,7 +291,14 @@ describe("figures", () => { ["topLeft", { mouseOffsetX: -50, mouseOffsetY: -50 }, { width: 150, height: 150 }], ])("Can resize a figure through its anchors", async (anchor: string, mouseMove, expectedSize) => { const figureId = "someuuid"; - createFigure(model, { id: figureId, y: 200, x: 200, width: 100, height: 100 }); + createFigure(model, { + id: figureId, + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 200, y: 200 }, + width: 100, + height: 100, + }); model.dispatch("SELECT_FIGURE", { id: figureId }); await nextTick(); await dragAnchor(anchor, mouseMove.mouseOffsetX, mouseMove.mouseOffsetY, true); @@ -273,7 +314,13 @@ describe("figures", () => { async (anchor: string, mouseMove: { mouseOffsetX: number; mouseOffsetY: number }) => { const figureId = "someuuid"; const figure = { width: 100, height: 100 }; - createFigure(model, { id: figureId, y: 0, x: 0, ...figure }); + createFigure(model, { + id: figureId, + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 0, y: 0 }, + ...figure, + }); model.dispatch("SELECT_FIGURE", { id: figureId }); await nextTick(); await dragAnchor(anchor, mouseMove.mouseOffsetX, mouseMove.mouseOffsetY, true); @@ -290,7 +337,13 @@ describe("figures", () => { async (anchor: string, mouseMove: { mouseOffsetX: number; mouseOffsetY: number }) => { const figureId = "someuuid"; const figure = { width: 200, height: 200 }; - createFigure(model, { id: figureId, y: 0, x: 0, ...figure }); + createFigure(model, { + id: figureId, + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 0, y: 0 }, + ...figure, + }); await nextTick(); setViewportOffset(model, 100, 100); await simulateClick(".o-figure"); @@ -306,12 +359,18 @@ describe("figures", () => { describe("Move a figure with drag & drop ", () => { test("Can move a figure with drag & drop", async () => { - createFigure(model, { id: "someuuid", x: 200, y: 100 }); + createFigure(model, { + id: "someuuid", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 200, y: 100 }, + }); await nextTick(); await dragElement(".o-figure", { x: 150, y: 100 }, undefined, true); expect(model.getters.getFigure(model.getters.getActiveSheetId(), "someuuid")).toMatchObject({ - x: 350, - y: 200, + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 350, y: 200 }, }); }); @@ -330,60 +389,68 @@ describe("figures", () => { }); test("Figure in frozen rows can be dragged to main viewport", async () => { - createFigure(model, { id, x: 16 * cellWidth, y: 4 * cellHeight }); + createFigure(model, { id, offset: { x: 16 * cellWidth, y: 4 * cellHeight } }); await nextTick(); await dragElement(figureSelector, { x: 0, y: 3 * cellHeight }, undefined, true); - expect(model.getters.getFigure(sheetId, id)).toMatchObject({ + expect(model.getters.getFigure(sheetId, id)?.offset).toMatchObject({ x: 16 * cellWidth, y: 17 * cellHeight, // initial position + drag offset + scroll offset }); }); test("Figure in main viewport can be dragged to frozen rows", async () => { - createFigure(model, { id, x: 16 * cellWidth, y: 16 * cellHeight }); + createFigure(model, { id, offset: { x: 16 * cellWidth, y: 16 * cellHeight } }); await nextTick(); await dragElement(figureSelector, { x: 0, y: -3 * cellHeight }, undefined, true); - expect(model.getters.getFigure(sheetId, id)).toMatchObject({ + expect(model.getters.getFigure(sheetId, id)?.offset).toMatchObject({ x: 16 * cellWidth, y: 3 * cellHeight, // initial position + drag offset - scroll offset }); }); test("Dragging figure that is half hidden by frozen rows will put in on top of the freeze pane", async () => { - createFigure(model, { id, x: 16 * cellWidth, y: 14 * cellHeight, height: 5 * cellHeight }); + createFigure(model, { + id, + offset: { x: 16 * cellWidth, y: 14 * cellHeight }, + height: 5 * cellHeight, + }); await nextTick(); await dragElement(figureSelector, { x: 1, y: 0 }, undefined, true); - expect(model.getters.getFigure(sheetId, id)).toMatchObject({ + expect(model.getters.getFigure(sheetId, id)?.offset).toMatchObject({ x: 16 * cellWidth + 1, y: 4 * cellHeight, // initial position - scroll offset }); }); test("Figure in frozen cols can be dragged to main viewport", async () => { - createFigure(model, { id, x: 4 * cellWidth, y: 16 * cellHeight }); + createFigure(model, { id, offset: { x: 4 * cellWidth, y: 16 * cellHeight } }); await nextTick(); await dragElement(figureSelector, { x: 3 * cellWidth, y: 0 }, undefined, true); - expect(model.getters.getFigure(sheetId, id)).toMatchObject({ + expect(model.getters.getFigure(sheetId, id)?.offset).toMatchObject({ x: 17 * cellWidth, // initial position + drag offset + scroll offset y: 16 * cellHeight, }); }); test("Figure in main viewport can be dragged to frozen cols", async () => { - createFigure(model, { id, x: 16 * cellWidth, y: 16 * cellHeight }); + createFigure(model, { id, offset: { x: 16 * cellWidth, y: 16 * cellHeight } }); await nextTick(); await dragElement(figureSelector, { x: -3 * cellWidth, y: 0 }, undefined, true); - expect(model.getters.getFigure(sheetId, id)).toMatchObject({ + expect(model.getters.getFigure(sheetId, id)?.offset).toMatchObject({ x: 3 * cellWidth, // initial position + drag offset - scroll offset y: 16 * cellHeight, }); }); test("Dragging figure that is half hidden by frozen cols will put in on top of the freeze pane", async () => { - createFigure(model, { id, x: 14 * cellWidth, y: 16 * cellHeight, width: 5 * cellWidth }); + createFigure(model, { + id, + offset: { x: 14 * cellWidth, y: 16 * cellHeight }, + width: 5 * cellWidth, + }); await nextTick(); await dragElement(figureSelector, { x: 0, y: 1 }, undefined, true); - expect(model.getters.getFigure(sheetId, id)).toMatchObject({ + expect(model.getters.getFigure(sheetId, id)?.offset).toMatchObject({ x: 4 * cellWidth, // initial position - scroll offset y: 16 * cellHeight + 1, }); @@ -399,7 +466,12 @@ describe("figures", () => { "Can scroll while dragging a figure", async ({ wheelX, wheelY }: { wheelX: number; wheelY: number }) => { addColumns(model, "after", "A", 50); - createFigure(model, { id: "someuuid", x: 200, y: 100 }); + createFigure(model, { + id: "someuuid", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 200, y: 100 }, + }); await nextTick(); const figureEl = fixture.querySelector(".o-figure")!; @@ -407,18 +479,22 @@ describe("figures", () => { triggerWheelEvent(figureEl, { deltaY: wheelY, deltaX: wheelX }); triggerMouseEvent(figureEl, "pointerup"); await nextTick(); - - expect(model.getters.getFigure(model.getters.getActiveSheetId(), "someuuid")).toMatchObject( - { - x: 200 + wheelX, - y: 100 + wheelY, - } - ); + expect( + model.getters.getFigure(model.getters.getActiveSheetId(), "someuuid")!.offset + ).toMatchObject({ + x: 200 + wheelX, + y: 100 + wheelY, + }); } ); test("Deleting a figure during drag and drop does not crash", async () => { - createFigure(model, { id: "someuuid", x: 200, y: 100 }); + createFigure(model, { + id: "someuuid", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 200, y: 100 }, + }); await nextTick(); await dragElement(".o-figure", { x: 150, y: 100 }, undefined, false); model.dispatch("DELETE_FIGURE", { id: "someuuid", sheetId }); @@ -429,7 +505,7 @@ describe("figures", () => { test("Cannot select/move figure in readonly mode", async () => { const figureId = "someuuid"; - createFigure(model, { id: figureId, y: 200 }); + createFigure(model, { id: figureId, offset: { x: 0, y: 200 } }); model.updateMode("readonly"); await nextTick(); const figure = fixture.querySelector(".o-figure")!; @@ -459,13 +535,13 @@ describe("figures", () => { }); test("Border for image figure", async () => { - createImage(model, { figureId: "figureId" }); + createImage(model, { id: "figureId" }); await nextTick(); expect(getElStyle(".o-figure-border", "border")).toEqual(`0px solid ${FIGURE_BORDER_COLOR}`); }); test("Border for selected image figure", async () => { - createImage(model, { figureId: "figureId" }); + createImage(model, { id: "figureId" }); model.dispatch("SELECT_FIGURE", { id: "figureId" }); await nextTick(); expect(getElStyle(".o-figure-border", "border")).toEqual( @@ -496,22 +572,22 @@ describe("figures", () => { "common tests for chart & image", (type: string) => { let sheetId: UID; - let figureId: UID; + let id: UID; beforeEach(async () => { sheetId = model.getters.getActiveSheetId(); - figureId = "figureId"; + id = "figureId"; switch (type) { case "image": - createImage(model, { sheetId, figureId }); + createImage(model, { sheetId, id }); break; case "basicChart": - createChart(model, TEST_CHART_DATA.basicChart, figureId); + createChart(model, TEST_CHART_DATA.basicChart, id); break; case "scorecard": - createScorecardChart(model, TEST_CHART_DATA.scorecard, figureId); + createScorecardChart(model, TEST_CHART_DATA.scorecard, id); break; case "gauge": - createGaugeChart(model, TEST_CHART_DATA.gauge, figureId); + createGaugeChart(model, TEST_CHART_DATA.gauge, id); break; } await nextTick(); @@ -524,11 +600,11 @@ describe("figures", () => { await simulateClick(".o-figure-menu-item"); expect(fixture.querySelector(".o-menu")).not.toBeNull(); await simulateClick(".o-menu div[data-name='delete']"); - expect(() => model.getters.getImage(figureId)).toThrow(); + expect(() => model.getters.getImage(id)).toThrow(); }); test(`Can copy/paste a figure ${type} with its context menu`, async () => { - const figureDef = getFigureDefinition(model, figureId, type); + const figureDef = getFigureDefinition(model, id, type); await simulateClick(".o-figure"); await simulateClick(".o-figure-menu-item"); await simulateClick(".o-menu div[data-name='copy']"); @@ -547,7 +623,7 @@ describe("figures", () => { }); test(`Can cut/paste a figure ${type} with its context menu`, async () => { - const figureDef = getFigureDefinition(model, figureId, type); + const figureDef = getFigureDefinition(model, id, type); await simulateClick(".o-figure"); await simulateClick(".o-figure-menu-item"); await simulateClick(".o-menu div[data-name='cut']"); @@ -568,11 +644,11 @@ describe("figures", () => { await simulateClick(".o-figure"); await simulateClick(".o-figure-menu-item"); await simulateClick(".o-menu div[data-name='copy']"); - expect(model.getters.getSelectedFigureId()).toEqual(figureId); + expect(model.getters.getSelectedFigureId()).toEqual(id); paste(model, "A1"); expect(getFigureIds(model, sheetId)).toHaveLength(2); const chartIds = getFigureIds(model, sheetId); - expect(model.getters.getSelectedFigureId()).not.toEqual(figureId); + expect(model.getters.getSelectedFigureId()).not.toEqual(id); expect(model.getters.getSelectedFigureId()).toEqual(chartIds[1]); }); @@ -655,11 +731,11 @@ describe("figures", () => { test("Selecting a figure and hitting Ctrl does not unselect it", async () => { await simulateClick(".o-figure"); - expect(model.getters.getSelectedFigureId()).toBe(figureId); + expect(model.getters.getSelectedFigureId()).toBe(id); keyDown({ key: "Control" }); - expect(model.getters.getSelectedFigureId()).toBe(figureId); + expect(model.getters.getSelectedFigureId()).toBe(id); keyUp({ key: "Control" }); - expect(model.getters.getSelectedFigureId()).toBe(figureId); + expect(model.getters.getSelectedFigureId()).toBe(id); }); } ); @@ -674,12 +750,14 @@ describe("figures", () => { test("Figure container is properly computed based on the sheetView size", async () => { createFigure(model, { id: "topLeft" }); // topLeft - createFigure(model, { id: "topRight", x: 4 * DEFAULT_CELL_WIDTH }); // topRight - createFigure(model, { id: "bottomLeft", y: 4 * DEFAULT_CELL_HEIGHT }); // bottomLeft + createFigure(model, { id: "topRight", offset: { x: 4 * DEFAULT_CELL_WIDTH, y: 0 } }); // topRight + createFigure(model, { id: "bottomLeft", offset: { x: 0, y: 4 * DEFAULT_CELL_HEIGHT } }); // bottomLeft createFigure(model, { id: "bottomRight", - x: 4 * DEFAULT_CELL_WIDTH, - y: 4 * DEFAULT_CELL_HEIGHT, + offset: { + x: 4 * DEFAULT_CELL_WIDTH, + y: 4 * DEFAULT_CELL_HEIGHT, + }, }); // bottomRight freezeRows(model, 2); freezeColumns(model, 2); @@ -726,11 +804,28 @@ describe("figures", () => { ])( "Snap x with horizontal mouseMove %s when moving figure", async (mouseMove: Pixel, expectedResult: Pixel) => { - createFigure(model, { id: "f1", x: 0, y: 0, width: 20, height: 20 }); - createFigure(model, { id: "f2", x: 50, y: 50, width: 50, height: 50 }); + createFigure(model, { + id: "f1", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 0, y: 0 }, + width: 20, + height: 20, + }); + createFigure(model, { + id: "f2", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 50, y: 50 }, + width: 50, + height: 50, + }); await nextTick(); await dragElement(".o-figure[data-id=f1]", { x: mouseMove, y: 0 }, undefined, true); - expect(model.getters.getFigure(sheetId, "f1")).toMatchObject({ x: expectedResult, y: 0 }); + expect(model.getters.getFigure(sheetId, "f1")?.offset).toMatchObject({ + x: expectedResult, + y: 0, + }); } ); @@ -747,11 +842,28 @@ describe("figures", () => { ])( "Snap y with vertical mouseMove %s when moving figure", async (mouseMove: Pixel, expectedResult: Pixel) => { - createFigure(model, { id: "f1", x: 0, y: 0, width: 20, height: 20 }); - createFigure(model, { id: "f2", x: 50, y: 50, width: 50, height: 50 }); + createFigure(model, { + id: "f1", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 0, y: 0 }, + width: 20, + height: 20, + }); + createFigure(model, { + id: "f2", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 50, y: 50 }, + width: 50, + height: 50, + }); await nextTick(); await dragElement(".o-figure[data-id=f1]", { x: 0, y: mouseMove }, undefined, true); - expect(model.getters.getFigure(sheetId, "f1")).toMatchObject({ x: 0, y: expectedResult }); + expect(model.getters.getFigure(sheetId, "f1")?.offset).toMatchObject({ + x: 0, + y: expectedResult, + }); } ); }); @@ -764,12 +876,29 @@ describe("figures", () => { [-48, { x: 150 - FIGURE_BORDER_WIDTH, width: 150 + FIGURE_BORDER_WIDTH }], // left border snaps with right border of other figure [-151, { x: 50, width: 250 }], // left border snaps with left border of other figure ])("snap with mouseMove %s", async (mouseMove: Pixel, expectedResult) => { - createFigure(model, { id: "f1", x: 200, y: 200, width: 100, height: 100 }); - createFigure(model, { id: "f2", x: 50, y: 50, width: 100, height: 100 }); + createFigure(model, { + id: "f1", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 200, y: 200 }, + width: 100, + height: 100, + }); + createFigure(model, { + id: "f2", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 50, y: 50 }, + width: 100, + height: 100, + }); model.dispatch("SELECT_FIGURE", { id: "f1" }); await nextTick(); await dragAnchor(anchor, mouseMove, 0, true); - expect(model.getters.getFigure(sheetId, "f1")).toMatchObject({ ...expectedResult }); + const figure = model.getters.getFigure(sheetId, "f1")!; + expect({ x: figure.offset.x, width: figure.width }).toMatchObject({ + ...expectedResult, + }); }); } ); @@ -781,12 +910,29 @@ describe("figures", () => { [47, { x: 50, width: 150 + FIGURE_BORDER_WIDTH }], // right border snaps with left border of other figure [152, { x: 50, width: 250 }], // right border snaps with right border of other figure ])("snap with mouseMove %s", async (mouseMove: Pixel, expectedResult) => { - createFigure(model, { id: "f1", x: 50, y: 50, width: 100, height: 100 }); - createFigure(model, { id: "f2", x: 200, y: 200, width: 100, height: 100 }); + createFigure(model, { + id: "f1", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 50, y: 50 }, + width: 100, + height: 100, + }); + createFigure(model, { + id: "f2", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 200, y: 200 }, + width: 100, + height: 100, + }); model.dispatch("SELECT_FIGURE", { id: "f1" }); await nextTick(); await dragAnchor(anchor, mouseMove, 0, true); - expect(model.getters.getFigure(sheetId, "f1")).toMatchObject({ ...expectedResult }); + const figure = model.getters.getFigure(sheetId, "f1")!; + expect({ x: figure.offset.x, width: figure.width }).toMatchObject({ + ...expectedResult, + }); }); } ); @@ -798,12 +944,29 @@ describe("figures", () => { [46, { y: 50, height: 150 + FIGURE_BORDER_WIDTH }], // bottom border snaps with top border of other figure [154, { y: 50, height: 250 }], // bottom border snaps with bottom border of other figure ])("snap with mouseMove %s", async (mouseMove: Pixel, expectedResult) => { - createFigure(model, { id: "f1", x: 50, y: 50, width: 100, height: 100 }); - createFigure(model, { id: "f2", x: 200, y: 200, width: 100, height: 100 }); + createFigure(model, { + id: "f1", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 50, y: 50 }, + width: 100, + height: 100, + }); + createFigure(model, { + id: "f2", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 200, y: 200 }, + width: 100, + height: 100, + }); model.dispatch("SELECT_FIGURE", { id: "f1" }); await nextTick(); await dragAnchor(anchor, 0, mouseMove, true); - expect(model.getters.getFigure(sheetId, "f1")).toMatchObject({ ...expectedResult }); + const figure = model.getters.getFigure(sheetId, "f1")!; + expect({ y: figure.offset.y, height: figure.height }).toMatchObject({ + ...expectedResult, + }); }); } ); @@ -815,12 +978,29 @@ describe("figures", () => { [-54, { y: 150 - FIGURE_BORDER_WIDTH, height: 150 + FIGURE_BORDER_WIDTH }], // top border snaps with bottom border of other figure [-153, { y: 50, height: 250 }], // top border snaps with top border of other figure ])("snap with mouseMove %s", async (mouseMove: Pixel, expectedResult) => { - createFigure(model, { id: "f1", x: 200, y: 200, width: 100, height: 100 }); - createFigure(model, { id: "f2", x: 50, y: 50, width: 100, height: 100 }); + createFigure(model, { + id: "f1", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 200, y: 200 }, + width: 100, + height: 100, + }); + createFigure(model, { + id: "f2", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 50, y: 50 }, + width: 100, + height: 100, + }); model.dispatch("SELECT_FIGURE", { id: "f1" }); await nextTick(); await dragAnchor(anchor, 0, mouseMove, true); - expect(model.getters.getFigure(sheetId, "f1")).toMatchObject({ ...expectedResult }); + const figure = model.getters.getFigure(sheetId, "f1")!; + expect({ y: figure.offset.y, height: figure.height }).toMatchObject({ + ...expectedResult, + }); }); } ); @@ -829,8 +1009,22 @@ describe("figures", () => { describe("Snap lines display", () => { describe("Snap lines are displayed during the drag & drop", () => { test("If the figure is snapping horizontally left of the other figure", async () => { - createFigure(model, { id: "f1", x: 50, y: 0, width: 20, height: 20 }); - createFigure(model, { id: "f2", x: 0, y: 0, width: 50, height: 50 }); + createFigure(model, { + id: "f1", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 50, y: 0 }, + width: 20, + height: 20, + }); + createFigure(model, { + id: "f2", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 0, y: 0 }, + width: 50, + height: 50, + }); await nextTick(); const selector = ".o-figure-container[data-id=HorizontalSnapContainer]"; expect(fixture.querySelectorAll(selector)).toHaveLength(0); @@ -844,8 +1038,22 @@ describe("figures", () => { }); test("If the figure is snapping horizontally right of the other figure", async () => { - createFigure(model, { id: "f1", x: 0, y: 0, width: 20, height: 20 }); - createFigure(model, { id: "f2", x: 50, y: 0, width: 50, height: 50 }); + createFigure(model, { + id: "f1", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 0, y: 0 }, + width: 20, + height: 20, + }); + createFigure(model, { + id: "f2", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 50, y: 0 }, + width: 50, + height: 50, + }); await nextTick(); const selector = ".o-figure-container[data-id=HorizontalSnapContainer]"; expect(fixture.querySelectorAll(selector)).toHaveLength(0); @@ -859,8 +1067,22 @@ describe("figures", () => { }); test("If the figure is snapping vertically above the other figure", async () => { - createFigure(model, { id: "f1", x: 0, y: 50, width: 20, height: 20 }); - createFigure(model, { id: "f2", x: 0, y: 0, width: 50, height: 50 }); + createFigure(model, { + id: "f1", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 0, y: 50 }, + width: 20, + height: 20, + }); + createFigure(model, { + id: "f2", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 0, y: 0 }, + width: 50, + height: 50, + }); await nextTick(); const selector = ".o-figure-container[data-id=VerticalSnapContainer]"; expect(fixture.querySelectorAll(selector)).toHaveLength(0); @@ -874,8 +1096,22 @@ describe("figures", () => { }); test("If the figure is snapping vertically below the other figure", async () => { - createFigure(model, { id: "f1", x: 0, y: 0, width: 20, height: 20 }); - createFigure(model, { id: "f2", x: 0, y: 50, width: 50, height: 50 }); + createFigure(model, { + id: "f1", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 0, y: 0 }, + width: 20, + height: 20, + }); + createFigure(model, { + id: "f2", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 0, y: 50 }, + width: 50, + height: 50, + }); await nextTick(); const selector = ".o-figure-container[data-id=VerticalSnapContainer]"; expect(fixture.querySelectorAll(selector)).toHaveLength(0); @@ -889,9 +1125,30 @@ describe("figures", () => { }); test("If there are multiple horizontal matches, the snap line include all of them", async () => { - createFigure(model, { id: "f1", x: 0, y: 50, width: 20, height: 20 }); - createFigure(model, { id: "f2", x: 50, y: 50, width: 50, height: 50 }); - createFigure(model, { id: "f3", x: 200, y: 50, width: 50, height: 50 }); + createFigure(model, { + id: "f1", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 0, y: 50 }, + width: 20, + height: 20, + }); + createFigure(model, { + id: "f2", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 50, y: 50 }, + width: 50, + height: 50, + }); + createFigure(model, { + id: "f3", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 200, y: 50 }, + width: 50, + height: 50, + }); await nextTick(); await dragElement(".o-figure[data-id=f1]", { x: 0, y: 0 }, undefined, false); @@ -903,9 +1160,30 @@ describe("figures", () => { }); test("If there are multiple vertical matches, the snap line include all of them", async () => { - createFigure(model, { id: "f1", x: 50, y: 0, width: 20, height: 20 }); - createFigure(model, { id: "f2", x: 50, y: 50, width: 50, height: 50 }); - createFigure(model, { id: "f3", x: 50, y: 200, width: 50, height: 50 }); + createFigure(model, { + id: "f1", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 50, y: 0 }, + width: 20, + height: 20, + }); + createFigure(model, { + id: "f2", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 50, y: 50 }, + width: 50, + height: 50, + }); + createFigure(model, { + id: "f3", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 50, y: 200 }, + width: 50, + height: 50, + }); await nextTick(); await dragElement(".o-figure[data-id=f1]", { x: 0, y: 0 }, undefined, false); @@ -918,8 +1196,22 @@ describe("figures", () => { }); test("Snap lines disappear after the drag & drop ends", async () => { - createFigure(model, { id: "f1", x: 0, y: 0, width: 20, height: 20 }); - createFigure(model, { id: "f2", x: 50, y: 50, width: 50, height: 50 }); + createFigure(model, { + id: "f1", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 0, y: 0 }, + width: 20, + height: 20, + }); + createFigure(model, { + id: "f2", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 50, y: 50 }, + width: 50, + height: 50, + }); await nextTick(); expect(fixture.querySelectorAll(".o-figure-snap-line")).toHaveLength(0); await dragElement(".o-figure[data-id=f1]", { x: 50, y: 50 }, undefined, false); @@ -937,8 +1229,22 @@ describe("figures", () => { { figHeight: 6 * cellHeight, scrollY: 2 * cellHeight }, // Figure half in frozen pane, with scroll ])("Can snap with figure in frozen row, %s ", async (params) => { freezeRows(model, 5); - createFigure(model, { id: "f1", x: 0, y: 0, width: 50, height: params.figHeight }); - createFigure(model, { id: "f2", x: 0, y: 0, width: 50, height: params.figHeight }); + createFigure(model, { + id: "f1", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 0, y: 0 }, + width: 50, + height: params.figHeight, + }); + createFigure(model, { + id: "f2", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 0, y: 0 }, + width: 50, + height: params.figHeight, + }); setViewportOffset(model, 0, params.scrollY); await nextTick(); @@ -966,8 +1272,22 @@ describe("figures", () => { { figWidth: 6 * cellWidth, scrollX: 2 * cellWidth }, // Figure half in frozen pane, with scroll ])("Can snap with figure in frozen cols, %s ", async (params) => { freezeColumns(model, 5); - createFigure(model, { id: "f1", x: 0, y: 0, width: params.figWidth, height: 50 }); - createFigure(model, { id: "f2", x: 0, y: 0, width: params.figWidth, height: 50 }); + createFigure(model, { + id: "f1", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 0, y: 0 }, + width: params.figWidth, + height: 50, + }); + createFigure(model, { + id: "f2", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 0, y: 0 }, + width: params.figWidth, + height: 50, + }); setViewportOffset(model, params.scrollX, 0); await nextTick(); @@ -992,89 +1312,187 @@ describe("figures", () => { test("Snap that makes the figure change pane in Y apply the right offset", async () => { freezeRows(model, 2); setViewportOffset(model, 0, 2 * cellHeight); - createFigure(model, { id: "f1", x: 0, y: 0, width: 50, height: 50 }); - createFigure(model, { id: "f2", x: 0, y: 4 * cellHeight + 1, width: 50, height: 20 }); + createFigure(model, { + id: "f1", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 0, y: 0 }, + width: 50, + height: 50, + }); + createFigure(model, { + id: "f2", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 0, y: 4 * cellHeight + 1 }, + width: 50, + height: 20, + }); await nextTick(); const selector = ".o-figure[data-id=f1]"; await dragElement(selector, { x: 0, y: 2 * cellHeight - 1 }, undefined, true); expect(model.getters.getFigure(sheetId, "f1")).toMatchObject({ - x: 0, - y: 4 * cellHeight + 1, + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 0, y: 4 * cellHeight + 1 }, }); }); test("Snap that makes the figure change pane in X apply the right offset", async () => { freezeColumns(model, 2); setViewportOffset(model, 2 * cellWidth, 0); - createFigure(model, { id: "f1", x: 0, y: 0, width: 50, height: 50 }); - createFigure(model, { id: "f2", x: 4 * cellWidth + 1, y: 0, width: 20, height: 50 }); + createFigure(model, { + id: "f1", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 0, y: 0 }, + width: 50, + height: 50, + }); + createFigure(model, { + id: "f2", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 4 * cellWidth + 1, y: 0 }, + width: 20, + height: 50, + }); await nextTick(); await dragElement(".o-figure[data-id=f1]", { x: 2 * cellWidth - 1, y: 0 }, undefined, true); expect(model.getters.getFigure(sheetId, "f1")).toMatchObject({ - x: 4 * cellWidth + 1, - y: 0, + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 4 * cellWidth + 1, y: 0 }, }); }); }); describe("Snap doesn't happen with borders that aren't visible", () => { test("No Y snap with top border above the viewport", async () => { - createFigure(model, { id: "f1", x: 50, y: 50, width: 100, height: 100 }); - createFigure(model, { id: "f2", x: 0, y: 0, width: 20, height: 20 }); + createFigure(model, { + id: "f1", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 50, y: 50 }, + width: 100, + height: 100, + }); + createFigure(model, { + id: "f2", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 0, y: 0 }, + width: 20, + height: 20, + }); setViewportOffset(model, 0, DEFAULT_CELL_HEIGHT); await nextTick(); await dragElement(".o-figure[data-id=f1]", { x: 0, y: -49 }, undefined, true); - expect(model.getters.getFigure(sheetId, "f1")).toMatchObject({ x: 50, y: 1 }); + expect(model.getters.getFigure(sheetId, "f1")).toMatchObject({ + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 50, y: 1 }, + }); }); test("No X snap with left border left of the viewport", async () => { - createFigure(model, { id: "f1", x: 50, y: 50, width: 100, height: 100 }); - createFigure(model, { id: "f2", x: 0, y: 0, width: 20, height: 20 }); + createFigure(model, { + id: "f1", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 50, y: 50 }, + width: 100, + height: 100, + }); + createFigure(model, { + id: "f2", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 0, y: 0 }, + width: 20, + height: 20, + }); setViewportOffset(model, DEFAULT_CELL_WIDTH, 0); await nextTick(); await dragElement(".o-figure[data-id=f1]", { x: -49, y: 0 }, undefined, true); - expect(model.getters.getFigure(sheetId, "f1")).toMatchObject({ x: 1, y: 50 }); + expect(model.getters.getFigure(sheetId, "f1")).toMatchObject({ + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 1, y: 50 }, + }); }); test("No Y snap with bottom border below the viewport", async () => { const { height: viewportHeight } = model.getters.getMainViewportRect(); - createFigure(model, { id: "f1", x: 0, y: 100, width: 100, height: 0.85 * viewportHeight }); + createFigure(model, { + id: "f1", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 0, y: 100 }, + width: 100, + height: 0.85 * viewportHeight, + }); createFigure(model, { id: "f2", - x: 0, - y: 0, + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 0, y: 0 }, width: 100, height: 0.85 * viewportHeight + 100, }); await nextTick(); await dragElement(".o-figure[data-id=f1]", { x: 0, y: 1 }, undefined, true); - expect(model.getters.getFigure(sheetId, "f1")).toMatchObject({ x: 0, y: 101 }); + expect(model.getters.getFigure(sheetId, "f1")).toMatchObject({ + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 0, y: 101 }, + }); }); test("No X snap with right border right of the viewport", async () => { const { width: viewportWidth } = model.getters.getMainViewportRect(); - createFigure(model, { id: "f1", x: 100, y: 0, width: 0.85 * viewportWidth, height: 100 }); + createFigure(model, { + id: "f1", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 100, y: 0 }, + width: 0.85 * viewportWidth, + height: 100, + }); createFigure(model, { id: "f2", - x: 0, - y: 0, + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 0, y: 0 }, width: 0.85 * viewportWidth + 100, height: 100, }); await nextTick(); await dragElement(".o-figure[data-id=f1]", { x: 1, y: 0 }, undefined, true); - expect(model.getters.getFigure(sheetId, "f1")).toMatchObject({ x: 101, y: 0 }); + expect(model.getters.getFigure(sheetId, "f1")).toMatchObject({ + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 101, y: 0 }, + }); }); test("No Y snap with top border below a frozen pane", async () => { freezeRows(model, 3); - createFigure(model, { id: "f1", x: 50, y: 0, width: 20, height: 20 }); + createFigure(model, { + id: "f1", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 50, y: 0 }, + width: 20, + height: 20, + }); createFigure(model, { id: "f2", - x: 50, - y: 4 * DEFAULT_CELL_HEIGHT, + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 50, y: 4 * DEFAULT_CELL_HEIGHT }, width: 100, height: 100, }); @@ -1087,32 +1505,46 @@ describe("figures", () => { true ); expect(model.getters.getFigure(sheetId, "f1")).toMatchObject({ - x: 50, - y: 2 * DEFAULT_CELL_HEIGHT - 1, + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 50, y: 2 * DEFAULT_CELL_HEIGHT - 1 }, }); }); test("No X snap with left border below a frozen pane", async () => { freezeColumns(model, 3); - createFigure(model, { id: "f1", x: 0, y: 50, width: 20, height: 20 }); + createFigure(model, { + id: "f1", + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 0, y: 50 }, + width: 20, + height: 20, + }); createFigure(model, { id: "f2", - x: 4 * DEFAULT_CELL_WIDTH, - y: 50, + offset: { + x: 4 * DEFAULT_CELL_WIDTH, + y: 50, + }, width: 100, height: 100, }); setViewportOffset(model, 2 * DEFAULT_CELL_WIDTH, 0); await nextTick(); await dragElement( - ".o-figure[data-id=f1]", + '.o-figure[data-id="f1"]', { x: 2 * DEFAULT_CELL_WIDTH - 1, y: 0 }, undefined, true ); expect(model.getters.getFigure(sheetId, "f1")).toMatchObject({ - x: 2 * DEFAULT_CELL_WIDTH - 1, - y: 50, + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { + x: 2 * DEFAULT_CELL_WIDTH - 1, + y: 50, + }, }); }); }); diff --git a/tests/figures/figures_plugin.test.ts b/tests/figures/figures_plugin.test.ts index ff53f6e3a9..7a8dd88955 100644 --- a/tests/figures/figures_plugin.test.ts +++ b/tests/figures/figures_plugin.test.ts @@ -25,19 +25,36 @@ describe("figure plugin", () => { tag: "hey", width: 100, height: 100, - x: 100, - y: 100, + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 100, y: 100 }, }, }); const data = model.exportData(); const sheet = data.sheets.find((s) => s.id === model.getters.getActiveSheetId())!; expect(sheet.figures).toEqual([ - { id: "someuuid", height: 100, tag: "hey", width: 100, x: 100, y: 100 }, + { + id: "someuuid", + height: 100, + tag: "hey", + width: 100, + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 100, y: 100 }, + }, ]); - expect(model.getters.getVisibleFigures()).toEqual([ - { id: "someuuid", height: 100, tag: "hey", width: 100, x: 100, y: 100 }, + expect(model.getters.getVisibleFigures().map((fUI) => fUI.figure)).toEqual([ + { + id: "someuuid", + height: 100, + tag: "hey", + width: 100, + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 100, y: 100 }, + }, ]); }); @@ -56,8 +73,9 @@ describe("figure plugin", () => { tag: "hey", width: 100, height: 100, - x: 100, - y: 100, + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 100, y: 100 }, }, }); expect(model.getters.getVisibleFigures().length).toBe(1); @@ -77,15 +95,24 @@ describe("figure plugin", () => { tag: "hey", width: 100, height: 100, - x: 100, - y: 100, + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 100, y: 100 }, }, }); const data = model.exportData(); const sheet = data.sheets.find((s) => s.id === sheetId)!; expect(sheet.figures).toEqual([ - { id: "someuuid", height: 100, tag: "hey", width: 100, x: 100, y: 100 }, + { + id: "someuuid", + height: 100, + tag: "hey", + width: 100, + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 100, y: 100 }, + }, ]); expect(model.getters.getVisibleFigures()).toEqual([]); // empty because active sheet is sheet1 @@ -97,8 +124,9 @@ describe("figure plugin", () => { sheetId: model.getters.getActiveSheetId(), figure: { id: "someuuid", - x: 10, - y: 10, + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 10, y: 10 }, tag: "hey", width: 100, height: 100, @@ -119,8 +147,9 @@ describe("figure plugin", () => { sheetId: model.getters.getActiveSheetId(), figure: { id: "someuuid", - x: 10, - y: 10, + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 10, y: 10 }, tag: "hey", width: 100, height: 100, @@ -134,8 +163,9 @@ describe("figure plugin", () => { sheetId: model.getters.getActiveSheetId(), figure: { id: "someuuid2", - x: 2.5 * DEFAULT_CELL_WIDTH, - y: 2.5 * DEFAULT_CELL_WIDTH, + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 2.5 * DEFAULT_CELL_WIDTH, y: 2.5 * DEFAULT_CELL_WIDTH }, tag: "hey", width: 10, height: 10, @@ -157,8 +187,9 @@ describe("figure plugin", () => { sheetId: model.getters.getActiveSheetId(), figure: { id: "someuuid", - x: 10, - y: 10, + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 10, y: 10 }, tag: "hey", width: 100, height: 100, @@ -177,8 +208,9 @@ describe("figure plugin", () => { sheetId: model.getters.getActiveSheetId(), figure: { id: "someuuid", - x: 10, - y: 10, + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 10, y: 10 }, tag: "hey", width: 100, height: 100, @@ -200,8 +232,9 @@ describe("figure plugin", () => { sheetId: model.getters.getActiveSheetId(), figure: { id: "someuuid", - x: 10, - y: 10, + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 10, y: 10 }, tag: "hey", width: 100, height: 100, @@ -215,8 +248,9 @@ describe("figure plugin", () => { model.dispatch("UPDATE_FIGURE", { sheetId: model.getters.getActiveSheetId(), id: "someuuid", - x: 100, - y: 200, + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 100, y: 200 }, }); const { x: newx, y: newy } = model.getters.getVisibleFigures()[0]; expect(newx).toBe(100); @@ -229,8 +263,9 @@ describe("figure plugin", () => { sheetId: model.getters.getActiveSheetId(), figure: { id: "someuuid", - x: 10, - y: 10, + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 10, y: 10 }, tag: "hey", width: 10, height: 10, @@ -240,8 +275,9 @@ describe("figure plugin", () => { model.dispatch("UPDATE_FIGURE", { sheetId: model.getters.getActiveSheetId(), id: "someuuid", - x: 100, - y: 200, + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 100, y: 200 }, }); const { x: x1, y: y1 } = model.getters.getVisibleFigures()[0]; expect(x1).toBe(100); @@ -259,8 +295,9 @@ describe("figure plugin", () => { sheetId: model.getters.getActiveSheetId(), figure: { id: "someuuid", - x: 10, - y: 10, + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 10, y: 10 }, tag: "hey", width: 100, height: 100, @@ -270,8 +307,9 @@ describe("figure plugin", () => { model.dispatch("UPDATE_FIGURE", { sheetId: model.getters.getActiveSheetId(), id: "someuuid", - x: -10, - y: 50, + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: -10, y: 50 }, }); const { x, y } = model.getters.getVisibleFigures()[0]; @@ -284,8 +322,9 @@ describe("figure plugin", () => { const result = model.dispatch("UPDATE_FIGURE", { sheetId: model.getters.getActiveSheetId(), id: "someuuid", - x: -10, - y: 50, + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: -10, y: 50 }, }); expect(result).toBeCancelledBecause(CommandResult.FigureDoesNotExist); }); @@ -306,8 +345,9 @@ describe("figure plugin", () => { sheetId, figure: { id: "someuuid", - x: 10, - y: 10, + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 10, y: 10 }, tag: "hey", width: 10, height: 10, @@ -335,8 +375,9 @@ describe("figure plugin", () => { sheetId: model.getters.getActiveSheetId(), figure: { id: "someuuid", - x: 10, - y: 10, + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 10, y: 10 }, tag: "hey", width: 10, height: 10, @@ -355,8 +396,9 @@ describe("figure plugin", () => { sheetId: model.getters.getActiveSheetId(), figure: { id: "someuuid", - x: 10, - y: 10, + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 10, y: 10 }, tag: "hey", width: 10, height: 10, @@ -374,8 +416,9 @@ describe("figure plugin", () => { const model = new Model(); const figure = { id: "someuuid", - x: 10, - y: 10, + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 10, y: 10 }, tag: "hey", width: 10, height: 10, @@ -419,8 +462,9 @@ describe("figure plugin", () => { const figure = { id: figureId, - x: maxX - rowSize, - y: maxY - colSize, + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: maxX - rowSize, y: maxY - colSize }, tag: "hey", width: 500, height: 500, @@ -433,8 +477,8 @@ describe("figure plugin", () => { deleteColumns(model, ["B"]); deleteRows(model, [1]); const figureAfter = model.getters.getFigure(sheetId, figureId)!; - expect(figureAfter.x).toBe(maxX - colSize - figureAfter.width); - expect(figureAfter.y).toBe(maxY - rowSize - figureAfter.height); + expect(figureAfter.offset.x).toBe(maxX - colSize - figureAfter.width); + expect(figureAfter.offset.y).toBe(maxY - rowSize - figureAfter.height); }); test("Move image at (0,0) if not enough space after removing rows and columns", async () => { @@ -443,8 +487,9 @@ describe("figure plugin", () => { const figureId = "someuuid"; const figureDef = { id: figureId, - x: 800, - y: 1200, + fixed_position: true, + anchor: { col: 0, row: 0 }, + offset: { x: 800, y: 1200 }, tag: "hey", width: 800, height: 1100, @@ -455,12 +500,12 @@ describe("figure plugin", () => { }); const figure = model.getters.getFigure(sheetId, figureId)!; - expect(figure.x).toBe(800); - expect(figure.y).toBe(1200); + expect(figure.offset.x).toBe(800); + expect(figure.offset.y).toBe(1200); deleteColumns(model, range(8, model.getters.getNumberCols(sheetId)).map(numberToLetters)); deleteRows(model, range(8, model.getters.getNumberRows(sheetId))); const figureAfter = model.getters.getFigure(sheetId, figureId)!; - expect(figureAfter.x).toBe(0); - expect(figureAfter.y).toBe(0); + expect(figureAfter.offset.x).toBe(0); + expect(figureAfter.offset.y).toBe(0); }); }); diff --git a/tests/figures/image/image_file_store.test.ts b/tests/figures/image/image_file_store.test.ts index a3b2dbd060..fa7a8267c6 100644 --- a/tests/figures/image/image_file_store.test.ts +++ b/tests/figures/image/image_file_store.test.ts @@ -25,8 +25,8 @@ describe("image file store", () => { { type: "CREATE_IMAGE", definition: { path: "/image/1", size, mimetype: "image/jpeg" }, - figureId: "figureId", - position: { x: 0, y: 0 }, + id: "figureId", + offset: { x: 0, y: 0 }, sheetId: data.sheets[0].id, size, }, @@ -60,8 +60,8 @@ describe("image file store", () => { { type: "CREATE_IMAGE", definition: { path: "/image/1", size, mimetype: "image/jpeg" }, - figureId: "figureId", - position: { x: 0, y: 0 }, + id: "figureId", + offset: { x: 0, y: 0 }, sheetId, size, }, @@ -96,8 +96,8 @@ describe("image file store", () => { { type: "CREATE_IMAGE", definition: { path: "/image/1", size, mimetype: "image/jpeg" }, - figureId: "figureId", - position: { x: 0, y: 0 }, + id: "figureId", + offset: { x: 0, y: 0 }, sheetId, size, }, @@ -138,8 +138,8 @@ describe("image file store", () => { { type: "CREATE_IMAGE", definition: { path: "/image/1", size, mimetype: "image/jpeg" }, - figureId: "figureId", - position: { x: 0, y: 0 }, + id: "figureId", + offset: { x: 0, y: 0 }, sheetId, size, }, @@ -187,16 +187,16 @@ describe("image file store", () => { { type: "CREATE_IMAGE", definition: { path: "/image/1", size, mimetype: "image/jpeg" }, - figureId: "figure_1", - position: { x: 0, y: 0 }, + id: "figure_1", + offset: { x: 0, y: 0 }, sheetId, size, }, { type: "CREATE_IMAGE", definition: { path: "/image/1", size }, - figureId: "figure_2", - position: { x: 0, y: 0 }, + id: "figure_2", + offset: { x: 0, y: 0 }, sheetId, size, }, @@ -231,8 +231,8 @@ describe("image file store", () => { { type: "CREATE_IMAGE", definition: { path: "/image/1", size, mimetype: "image/jpeg" }, - figureId: "figureId", - position: { x: 0, y: 0 }, + id: "figureId", + offset: { x: 0, y: 0 }, sheetId: "sheet_2", size, }, @@ -267,8 +267,8 @@ describe("image file store", () => { { type: "CREATE_IMAGE", definition: { path: "/image/1", size, mimetype: "image/jpeg" }, - figureId: "figureId", - position: { x: 0, y: 0 }, + id: "figureId", + offset: { x: 0, y: 0 }, sheetId: "sheet_2", size, }, @@ -297,8 +297,9 @@ describe("image file store", () => { tag: "image", height: 380, width: 380, - x: 0, - y: 0, + anchor: { col: 0, row: 0 }, + offset: { x: 0, y: 0 }, + fixed_position: true, data: { path: "/image/1", size: { width: 100, height: 100 }, diff --git a/tests/figures/image/image_plugin.test.ts b/tests/figures/image/image_plugin.test.ts index 390339eb9f..bce5a39a1d 100644 --- a/tests/figures/image/image_plugin.test.ts +++ b/tests/figures/image/image_plugin.test.ts @@ -14,7 +14,7 @@ describe("image plugin", function () { size: { width: 100, height: 100 }, mimetype: "image/jpeg", }; - createImage(model, { figureId: imageId, definition }); + createImage(model, { id: imageId, definition }); expect(model.getters.getImage(imageId)).toEqual(definition); }); @@ -22,7 +22,7 @@ describe("image plugin", function () { const model = new Model(); const sheetId = model.getters.getActiveSheetId(); const imageId = "Image1"; - createImage(model, { sheetId: sheetId, figureId: imageId }); + createImage(model, { sheetId: sheetId, id: imageId }); model.dispatch("DELETE_FIGURE", { sheetId, id: imageId }); const images = getFigureIds(model, sheetId); expect(images).toHaveLength(0); @@ -38,7 +38,7 @@ describe("image plugin", function () { size: { width: 100, height: 100 }, mimetype: "image/jpeg", }; - createImage(model, { figureId: imageId, definition }); + createImage(model, { id: imageId, definition }); model.dispatch("SELECT_FIGURE", { id: imageId }); model.dispatch("COPY"); paste(model, "D4"); @@ -59,7 +59,7 @@ describe("image plugin", function () { size: { width: 100, height: 100 }, mimetype: "image/jpeg", }; - createImage(model, { figureId: imageId, definition }); + createImage(model, { id: imageId, definition }); model.dispatch("SELECT_FIGURE", { id: imageId }); model.dispatch("CUT"); paste(model, "D4"); @@ -75,7 +75,7 @@ describe("test image in sheet", function () { const model = new Model(); const sheetId = model.getters.getActiveSheetId(); const imageId = "Image1"; - createImage(model, { sheetId: sheetId, figureId: imageId }); + createImage(model, { sheetId: sheetId, id: imageId }); const newSheetId = "Sheet2"; model.dispatch("DUPLICATE_SHEET", { sheetId, sheetIdTo: newSheetId }); const original = model.getters.getImage(imageId); @@ -90,7 +90,7 @@ describe("test image in sheet", function () { const imageId = "Image1"; const newSheetId = "Sheet2"; model.dispatch("CREATE_SHEET", { sheetId: newSheetId, position: 2 }); - createImage(model, { sheetId: newSheetId, figureId: imageId }); + createImage(model, { sheetId: newSheetId, id: imageId }); model.dispatch("DELETE_SHEET", { sheetId: newSheetId }); const images = getFigureIds(model, newSheetId); expect(images).toHaveLength(0); @@ -101,7 +101,7 @@ describe("test image in sheet", function () { const firstSheetId = model.getters.getActiveSheetId(); const secondSheetId = "42"; const thirdSheetId = "third"; - createImage(model, { sheetId: firstSheetId, figureId: "myImage" }); + createImage(model, { sheetId: firstSheetId, id: "myImage" }); model.dispatch("DUPLICATE_SHEET", { sheetId: firstSheetId, sheetIdTo: secondSheetId, @@ -138,7 +138,7 @@ describe("test image import & export", function () { test("can export an image", () => { const model = new Model(); const imageId = "Image1"; - createImage(model, { sheetId: "Sheet1", figureId: imageId }); + createImage(model, { sheetId: "Sheet1", id: imageId }); const data = model.exportData(); const activeSheetId = model.getters.getActiveSheetId(); const sheet = data.sheets.find((s) => s.id === activeSheetId)!; @@ -158,7 +158,7 @@ describe("test image import & export", function () { const model = new Model(); const sheetId = "Sheet1"; const imageId = "Image1"; - createImage(model, { sheetId, figureId: imageId }); + createImage(model, { sheetId, id: imageId }); const importedData = model.exportData(); const newModel = new Model(importedData); expect(newModel.getters.getImage(imageId)).toEqual(model.getters.getImage(imageId)); @@ -183,7 +183,7 @@ describe("test image undo/redo", () => { const model = new Model(); const sheetId = model.getters.getActiveSheetId(); const imageId = "Image1"; - createImage(model, { sheetId, figureId: imageId }); + createImage(model, { sheetId, id: imageId }); const before = model.exportData(); model.dispatch("DELETE_FIGURE", { sheetId, id: imageId }); const after = model.exportData(); @@ -196,7 +196,7 @@ describe("test image undo/redo", () => { test("undo/redo image cut & paste", () => { const model = new Model(); const imageId = "Image1"; - createImage(model, { figureId: imageId }); + createImage(model, { id: imageId }); const before = model.exportData(); model.dispatch("SELECT_FIGURE", { id: imageId }); model.dispatch("CUT"); @@ -212,7 +212,7 @@ describe("test image undo/redo", () => { const model = new Model(); const sheetId = model.getters.getActiveSheetId(); const imageId = "Image1"; - createImage(model, { sheetId, figureId: imageId }); + createImage(model, { sheetId, id: imageId }); const before = model.exportData(); const newSheetId = "Sheet2"; model.dispatch("DUPLICATE_SHEET", { sheetId, sheetIdTo: newSheetId }); diff --git a/tests/popover/error_tooltip_component.test.ts b/tests/popover/error_tooltip_component.test.ts index 513f356f23..4bf1442bdb 100644 --- a/tests/popover/error_tooltip_component.test.ts +++ b/tests/popover/error_tooltip_component.test.ts @@ -138,8 +138,12 @@ describe("Grid integration", () => { createChart(model, { ...TEST_CHART_DATA.basicChart }, "figureId"); model.dispatch("UPDATE_FIGURE", { id: "figureId", - y: 200, - x: 200, + fixed_position: true, + offset: { + y: 200, + x: 200, + }, + anchor: { col: 0, row: 0 }, width: 200, height: 200, sheetId: model.getters.getActiveSheetId(), diff --git a/tests/repeat_commands_plugin.test.ts b/tests/repeat_commands_plugin.test.ts index eb203f32d3..0b9157449b 100644 --- a/tests/repeat_commands_plugin.test.ts +++ b/tests/repeat_commands_plugin.test.ts @@ -229,7 +229,7 @@ describe("Repeat command transform specifics", () => { const command: CreateImageOverCommand = { ...TEST_COMMANDS.CREATE_IMAGE, sheetId, - figureId: "figureId", + id: "figureId", }; createSheet(model, { sheetId: "42" }); activateSheet(model, "42"); diff --git a/tests/sheet/selection_plugin.test.ts b/tests/sheet/selection_plugin.test.ts index 145efeb126..c9ff8ccfe2 100644 --- a/tests/sheet/selection_plugin.test.ts +++ b/tests/sheet/selection_plugin.test.ts @@ -423,8 +423,12 @@ describe("simple selection", () => { sheetId: model.getters.getActiveSheetId(), figure: { id: "someuuid", - x: 10, - y: 10, + offset: { + x: 10, + y: 10, + }, + anchor: { col: 0, row: 0 }, + fixed_position: true, tag: "hey", width: 100, height: 100, diff --git a/tests/sheet/sheets_plugin.test.ts b/tests/sheet/sheets_plugin.test.ts index 56e3015c52..88db0b2917 100644 --- a/tests/sheet/sheets_plugin.test.ts +++ b/tests/sheet/sheets_plugin.test.ts @@ -733,13 +733,17 @@ describe("sheets", () => { model.dispatch("UPDATE_FIGURE", { sheetId: sheetId, id: chartId, - x: 40, + offset: { x: 40, y: 0 }, }); const figure1 = model.getters.getFigures(sheetId); const figure2 = model.getters.getFigures("42"); - expect(figure1).toEqual([{ height: 335, id: chartId, tag: "chart", width: 536, x: 40, y: 0 }]); - expect(figure2).toMatchObject([{ height: 335, tag: "chart", width: 536, x: 0, y: 0 }]); + expect(figure1).toEqual([ + { height: 335, id: chartId, tag: "chart", width: 536, offset: { x: 40, y: 0 } }, + ]); + expect(figure2).toMatchObject([ + { height: 335, tag: "chart", width: 536, offset: { x: 0, y: 0 } }, + ]); }); test("Cols and Rows are correctly duplicated", () => { diff --git a/tests/test_helpers/commands_helpers.ts b/tests/test_helpers/commands_helpers.ts index 40992fb876..2454182337 100644 --- a/tests/test_helpers/commands_helpers.ts +++ b/tests/test_helpers/commands_helpers.ts @@ -21,6 +21,8 @@ import { DispatchResult, Locale, ParsedOSClipboardContent, + PixelPosition, + Position, SelectionStep, SortDirection, SortOptions, @@ -119,16 +121,20 @@ export function createImage( model: Model, partialParam: { sheetId?: UID; - figureId?: UID; - position?: { x: number; y: number }; + id?: UID; + offset?: PixelPosition; + anchor?: Position; + fixed_position?: boolean; definition?: Partial; size?: FigureSize; } ) { const param = { sheetId: model.getters.getActiveSheetId(), - figureId: model.uuidGenerator.uuidv4(), - position: { x: 0, y: 0 }, + id: model.uuidGenerator.uuidv4(), + offset: { x: 0, y: 0 }, + anchor: { col: 0, row: 0 }, + fixed_position: true, ...partialParam, definition: { path: "image path", @@ -139,8 +145,10 @@ export function createImage( const size = partialParam.size ?? { width: 380, height: 380 }; return model.dispatch("CREATE_IMAGE", { sheetId: param.sheetId, - figureId: param.figureId, - position: param.position, + id: param.id, + anchor: param.anchor, + offset: param.offset, + fixed_position: param.fixed_position, size, definition: { size, ...param.definition }, }); @@ -176,6 +184,7 @@ export function createChart( cumulative: ("cumulative" in data && data.cumulative) || false, showSubTotals: ("showSubTotals" in data && data.showSubTotals) || false, showConnectorLines: ("showConnectorLines" in data && data.showConnectorLines) || false, + fixed_position: data.fixed_position || false, }; return model.dispatch("CREATE_CHART", { id, @@ -205,6 +214,7 @@ export function createComboChart( background: data.background, legendPosition: data.legendPosition || "top", aggregated: ("aggregated" in data && data.aggregated) || false, + fixed_position: data.fixed_position || false, }, }); } @@ -232,6 +242,7 @@ export function createRadarChart( aggregated: ("aggregated" in data && data.aggregated) || false, fillArea: data.fillArea || false, stacked: data.stacked || false, + fixed_position: data.fixed_position || false, }, }); } @@ -265,6 +276,7 @@ export function createScorecardChart( baselineColorUp: data.baselineColorUp || DEFAULT_SCORECARD_BASELINE_COLOR_UP, background: data.background, humanize: data.humanize || false, + fixed_position: data.fixed_position || false, }, }); } @@ -286,6 +298,7 @@ export function createGaugeChart( background: data.background, title: data.title || { text: "" }, dataRange: data.dataRange || "", + fixed_position: data.fixed_position || false, sectionRule: data.sectionRule || { rangeMin: "0", rangeMax: "100", @@ -331,6 +344,7 @@ export function createGeoChart( colorScale: data.colorScale, missingValueColor: data.missingValueColor, region: data.region, + fixed_position: data.fixed_position || false, }, }); } diff --git a/tests/test_helpers/constants.ts b/tests/test_helpers/constants.ts index 7a79260bfe..96ee36f90c 100644 --- a/tests/test_helpers/constants.ts +++ b/tests/test_helpers/constants.ts @@ -21,6 +21,7 @@ export const TEST_CHART_DATA = { background: BACKGROUND_CHART_COLOR, stacked: false, legendPosition: "top" as const, + fixed_position: false, }, combo: { type: "combo" as const, @@ -239,8 +240,12 @@ export const TEST_COMMANDS: CommandMapping = { figure: { id: "figureId", tag: "tag", - x: 0, - y: 0, + offset: { + x: 0, + y: 0, + }, + anchor: { col: 0, row: 0 }, + fixed_position: true, width: 100, height: 100, }, @@ -259,7 +264,7 @@ export const TEST_COMMANDS: CommandMapping = { CREATE_CHART: { type: "CREATE_CHART", definition: TEST_CHART_DATA.basicChart, - position: { x: 0, y: 0 }, + offset: { x: 0, y: 0 }, size: { width: 200, height: 200 }, id: "figureId", sheetId: "sheetId", @@ -272,11 +277,11 @@ export const TEST_COMMANDS: CommandMapping = { }, CREATE_IMAGE: { type: "CREATE_IMAGE", - position: { x: 0, y: 0 }, + offset: { x: 0, y: 0 }, size: { width: 200, height: 200 }, definition: { path: "/image/1", size: { width: 200, height: 200 } }, sheetId: "sheetId", - figureId: "figureId", + id: "figureId", }, RESIZE_COLUMNS_ROWS: { type: "RESIZE_COLUMNS_ROWS", diff --git a/tests/xlsx/__snapshots__/xlsx_export.test.ts.snap b/tests/xlsx/__snapshots__/xlsx_export.test.ts.snap index 924364d40b..9fda8597e0 100644 --- a/tests/xlsx/__snapshots__/xlsx_export.test.ts.snap +++ b/tests/xlsx/__snapshots__/xlsx_export.test.ts.snap @@ -304,27 +304,27 @@ exports[`Test XLSX export Charts Chart legend is set to none position 1`] = ` 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 @@ -891,30 +891,30 @@ exports[`Test XLSX export Charts Export chart overflowing outside the sheet 1`] - 24 + 0 - 971550 + 2405 0 - 9525 + 0 - 24 + 1 - 6076950 + 2405 - 14 + 1 - 133350 + 0 @@ -1484,27 +1484,27 @@ exports[`Test XLSX export Charts chart dataset without title 1`] = ` 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 @@ -3305,27 +3305,27 @@ exports[`Test XLSX export Charts chart font color is white with a dark backgroun 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 @@ -3351,27 +3351,27 @@ exports[`Test XLSX export Charts chart font color is white with a dark backgroun 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 @@ -3397,27 +3397,27 @@ exports[`Test XLSX export Charts chart font color is white with a dark backgroun 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 @@ -3443,27 +3443,27 @@ exports[`Test XLSX export Charts chart font color is white with a dark backgroun 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 @@ -3489,27 +3489,27 @@ exports[`Test XLSX export Charts chart font color is white with a dark backgroun 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 @@ -3535,27 +3535,27 @@ exports[`Test XLSX export Charts chart font color is white with a dark backgroun 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 @@ -4216,27 +4216,27 @@ exports[`Test XLSX export Charts charts in different sheets 1`] = ` 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 @@ -4673,27 +4673,27 @@ exports[`Test XLSX export Charts charts in different sheets 1`] = ` 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 @@ -5523,27 +5523,27 @@ exports[`Test XLSX export Charts multiple charts in the same sheet 1`] = ` 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 @@ -5569,27 +5569,27 @@ exports[`Test XLSX export Charts multiple charts in the same sheet 1`] = ` 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 @@ -5978,27 +5978,27 @@ exports[`Test XLSX export Charts pie chart with only title dataset 1`] = ` 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 @@ -6439,27 +6439,27 @@ exports[`Test XLSX export Charts simple bar chart with customized axis 1`] = ` 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 @@ -7003,27 +7003,27 @@ exports[`Test XLSX export Charts simple bar chart with customized dataset 1`] = 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 @@ -7569,27 +7569,27 @@ exports[`Test XLSX export Charts simple bar chart with customized title 1`] = ` 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 @@ -8135,27 +8135,27 @@ exports[`Test XLSX export Charts simple bar chart with dataset [ [Object] ] 1`] 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 @@ -8739,27 +8739,27 @@ exports[`Test XLSX export Charts simple bar chart with dataset [ [Object], [Obje 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 @@ -9308,27 +9308,27 @@ exports[`Test XLSX export Charts simple combo chart with customized axis 1`] = ` 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 @@ -9871,27 +9871,27 @@ exports[`Test XLSX export Charts simple combo chart with customized dataset 1`] 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 @@ -10436,27 +10436,27 @@ exports[`Test XLSX export Charts simple combo chart with customized title 1`] = 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 @@ -11001,27 +11001,27 @@ exports[`Test XLSX export Charts simple combo chart with dataset [ [Object] ] 1` 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 @@ -11625,27 +11625,27 @@ exports[`Test XLSX export Charts simple combo chart with dataset [ [Object], [Ob 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 @@ -12205,27 +12205,27 @@ exports[`Test XLSX export Charts simple line chart with customized axis 1`] = ` 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 @@ -12779,27 +12779,27 @@ exports[`Test XLSX export Charts simple line chart with customized dataset 1`] = 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 @@ -13355,27 +13355,27 @@ exports[`Test XLSX export Charts simple line chart with customized title 1`] = ` 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 @@ -13931,27 +13931,27 @@ exports[`Test XLSX export Charts simple line chart with dataset [ [Object] ] 1`] 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 @@ -14558,27 +14558,27 @@ exports[`Test XLSX export Charts simple line chart with dataset [ [Object], [Obj 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 @@ -15039,27 +15039,27 @@ exports[`Test XLSX export Charts simple pie chart with dataset [ [Object] ] 1`] 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 @@ -15594,27 +15594,27 @@ exports[`Test XLSX export Charts simple pie chart with dataset [ [Object], [Obje 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 @@ -16169,27 +16169,27 @@ exports[`Test XLSX export Charts simple radar chart with customized axis 1`] = ` 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 @@ -16742,27 +16742,27 @@ exports[`Test XLSX export Charts simple radar chart with customized dataset 1`] 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 @@ -17317,27 +17317,27 @@ exports[`Test XLSX export Charts simple radar chart with customized title 1`] = 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 @@ -17892,27 +17892,27 @@ exports[`Test XLSX export Charts simple radar chart with dataset [ [Object] ] 1` 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 @@ -18518,27 +18518,27 @@ exports[`Test XLSX export Charts simple radar chart with dataset [ [Object], [Ob 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 @@ -19098,27 +19098,27 @@ exports[`Test XLSX export Charts simple scatter chart with customized axis 1`] = 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 @@ -19672,27 +19672,27 @@ exports[`Test XLSX export Charts simple scatter chart with customized dataset 1` 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 @@ -20248,27 +20248,27 @@ exports[`Test XLSX export Charts simple scatter chart with customized title 1`] 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 @@ -20824,27 +20824,27 @@ exports[`Test XLSX export Charts simple scatter chart with dataset [ [Object] ] 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 @@ -21451,27 +21451,27 @@ exports[`Test XLSX export Charts simple scatter chart with dataset [ [Object], [ 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 @@ -22055,27 +22055,27 @@ exports[`Test XLSX export Charts stacked bar chart 1`] = ` 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 @@ -25013,27 +25013,27 @@ exports[`Test XLSX export Images image larger than the sheet 1`] = ` 0 - 9525 + 0 0 - 9525 + 0 - 24 + 1 - 954338325 + 0 - 24 + 1 - 956776725 + 0 @@ -25232,27 +25232,27 @@ exports[`Test XLSX export Images image overflowing outside the sheet 1`] = ` 0 - 9525 + 0 0 - 9525 + 0 - 3 + 1 - 885825 + 0 - 16 + 1 - 123825 + 0 @@ -25452,27 +25452,27 @@ exports[`Test XLSX export Images images in different sheets 1`] = ` 0 - 9525 + 0 0 - 9525 + 0 - 3 + 1 - 885825 + 0 - 16 + 1 - 123825 + 0 @@ -25555,27 +25555,27 @@ exports[`Test XLSX export Images images in different sheets 1`] = ` 0 - 9525 + 0 0 - 9525 + 0 - 3 + 1 - 885825 + 0 - 16 + 1 - 123825 + 0 @@ -25796,27 +25796,27 @@ exports[`Test XLSX export Images multiple images in the same sheet 1`] = ` 0 - 9525 + 0 0 - 9525 + 0 - 3 + 1 - 885825 + 0 - 16 + 1 - 123825 + 0 @@ -25848,27 +25848,27 @@ exports[`Test XLSX export Images multiple images in the same sheet 1`] = ` 0 - 28575 + 2 0 - 28575 + 2 - 3 + 1 - 904875 + 2 - 16 + 1 - 142875 + 2 @@ -26068,27 +26068,27 @@ exports[`Test XLSX export Images simple image 1`] = ` 0 - 9525 + 0 0 - 9525 + 0 - 3 + 1 - 885825 + 0 - 16 + 1 - 123825 + 0 @@ -33252,27 +33252,27 @@ exports[`Test XLSX export multiple elements are exported in the correct order 1` 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 @@ -36428,27 +36428,27 @@ exports[`Test XLSX export references with headers should be converted to referen 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 @@ -36474,27 +36474,27 @@ exports[`Test XLSX export references with headers should be converted to referen 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 @@ -36520,27 +36520,27 @@ exports[`Test XLSX export references with headers should be converted to referen 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 @@ -36566,27 +36566,27 @@ exports[`Test XLSX export references with headers should be converted to referen 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 @@ -36612,27 +36612,27 @@ exports[`Test XLSX export references with headers should be converted to referen 0 - 9525 + 0 0 - 9525 + 0 - 5 + 1 - 542925 + 0 - 14 + 1 - 133350 + 0 diff --git a/tests/xlsx/xlsx_export.test.ts b/tests/xlsx/xlsx_export.test.ts index 9f9a5bac03..89cb43fe11 100644 --- a/tests/xlsx/xlsx_export.test.ts +++ b/tests/xlsx/xlsx_export.test.ts @@ -1416,7 +1416,7 @@ describe("Test XLSX export", () => { model.dispatch("UPDATE_FIGURE", { sheetId: "Sheet1", id: "1", - x: end + 5, + offset: { x: end + 5, y: 0 }, }); expect(await exportPrettifiedXlsx(model)).toMatchSnapshot(); }); @@ -1442,7 +1442,7 @@ describe("Test XLSX export", () => { test("multiple images in the same sheet", async () => { const model = new Model(getModelData()); createImage(model, {}); - createImage(model, { position: { x: 2, y: 2 } }); + createImage(model, { offset: { x: 2, y: 2 } }); expect(await exportPrettifiedXlsx(model)).toMatchSnapshot(); }); @@ -1467,7 +1467,7 @@ describe("Test XLSX export", () => { model.dispatch("UPDATE_FIGURE", { sheetId, id: "1", - x: end + 5, + offset: { x: end + 5, y: 0 }, }); expect(await exportPrettifiedXlsx(model)).toMatchSnapshot(); }); diff --git a/tests/xlsx/xlsx_import.test.ts b/tests/xlsx/xlsx_import.test.ts index 5541c54767..87d5b14c1f 100644 --- a/tests/xlsx/xlsx_import.test.ts +++ b/tests/xlsx/xlsx_import.test.ts @@ -698,11 +698,11 @@ describe("Import xlsx data", () => { // Don't test exact positions, because excel does some esoteric magic for units and sizes (+our conversion is wonky, hello hardcoded DPI) // We'll only test that the figure corners are located in the correct cells - expect(figure.x).toBeBetween( + expect(figure.offset.x).toBeBetween( getColPosition(figZone.left, testSheet), getColPosition(figZone.left + 1, testSheet) ); - expect(figure.y).toBeBetween( + expect(figure.offset.y).toBeBetween( getRowPosition(figZone.top, testSheet), getRowPosition(figZone.top + 1, testSheet) ); @@ -838,11 +838,11 @@ describe("Import xlsx data", () => { const testSheet = getWorkbookSheet("jestOneCellAnchor", convertedData)!; const figZone = toZone(figureZone); const figure = testSheet.figures.find((figure) => figure.tag === figureType)!; - expect(figure.x).toBeBetween( + expect(figure.offset.x).toBeBetween( getColPosition(figZone.left, testSheet), getColPosition(figZone.left + 1, testSheet) ); - expect(figure.y).toBeBetween( + expect(figure.offset.y).toBeBetween( getRowPosition(figZone.top, testSheet), getRowPosition(figZone.top + 1, testSheet) ); diff --git a/tests/xlsx/xlsx_import_export.test.ts b/tests/xlsx/xlsx_import_export.test.ts index a5c3b280b8..a6be7537f6 100644 --- a/tests/xlsx/xlsx_import_export.test.ts +++ b/tests/xlsx/xlsx_import_export.test.ts @@ -280,8 +280,8 @@ describe("Export data to xlsx then import it", () => { expect(importedFigure.height).toEqual(figure.height); expect(importedFigure.width).toBeBetween(figure.width - 1, figure.width + 1); // See explanation at the top of the file for +FIGURE_BORDER_WIDTH - expect(importedFigure.x).toEqual(figure.x + FIGURE_BORDER_WIDTH); - expect(importedFigure.y).toEqual(figure.y + FIGURE_BORDER_WIDTH); + expect(importedFigure.offset.x).toEqual(figure.offset.x + FIGURE_BORDER_WIDTH); + expect(importedFigure.offset.y).toEqual(figure.offset.y + FIGURE_BORDER_WIDTH); }); test.each([ @@ -370,7 +370,7 @@ describe("Export data to xlsx then import it", () => { test("Image", () => { createImage(model, { - figureId: "1", + id: "1", size: { width: 300, height: 400,