@odoo/o-spreadsheet 16.4.0-alpha.1 → 16.4.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2218,249 +2218,6 @@ function splitReference(ref) {
2218
2218
  return { sheetName, xc };
2219
2219
  }
2220
2220
 
2221
- class RangeImpl {
2222
- getSheetSize;
2223
- _zone;
2224
- parts;
2225
- invalidXc;
2226
- prefixSheet = false;
2227
- sheetId; // the sheet on which the range is defined
2228
- invalidSheetName; // the name of any sheet that is invalid
2229
- constructor(args, getSheetSize) {
2230
- this.getSheetSize = getSheetSize;
2231
- this._zone = args.zone;
2232
- this.parts = args.parts;
2233
- this.prefixSheet = args.prefixSheet;
2234
- this.invalidXc = args.invalidXc;
2235
- this.sheetId = args.sheetId;
2236
- this.invalidSheetName = args.invalidSheetName;
2237
- }
2238
- static fromRange(range, getters) {
2239
- if (range instanceof RangeImpl) {
2240
- return range;
2241
- }
2242
- return new RangeImpl(range, getters.getSheetSize);
2243
- }
2244
- get unboundedZone() {
2245
- return this._zone;
2246
- }
2247
- get zone() {
2248
- const { left, top, bottom, right } = this._zone;
2249
- if (right !== undefined && bottom !== undefined)
2250
- return { left, top, right, bottom };
2251
- else if (bottom === undefined && right !== undefined) {
2252
- return { right, top, left, bottom: this.getSheetSize(this.sheetId).numberOfRows - 1 };
2253
- }
2254
- else if (right === undefined && bottom !== undefined) {
2255
- return { bottom, left, top, right: this.getSheetSize(this.sheetId).numberOfCols - 1 };
2256
- }
2257
- throw new Error(_lt("Bad zone format"));
2258
- }
2259
- static getRangeParts(xc, zone) {
2260
- const parts = xc.split(":").map((p) => {
2261
- const isFullRow = isRowReference(p);
2262
- return {
2263
- colFixed: isFullRow ? false : p.startsWith("$"),
2264
- rowFixed: isFullRow ? p.startsWith("$") : p.includes("$", 1),
2265
- };
2266
- });
2267
- const isFullCol = zone.bottom === undefined;
2268
- const isFullRow = zone.right === undefined;
2269
- if (isFullCol) {
2270
- parts[0].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
2271
- parts[1].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
2272
- if (zone.left === zone.right) {
2273
- parts[0].colFixed = parts[0].colFixed || parts[1].colFixed;
2274
- parts[1].colFixed = parts[0].colFixed || parts[1].colFixed;
2275
- }
2276
- }
2277
- if (isFullRow) {
2278
- parts[0].colFixed = parts[0].colFixed || parts[1].colFixed;
2279
- parts[1].colFixed = parts[0].colFixed || parts[1].colFixed;
2280
- if (zone.top === zone.bottom) {
2281
- parts[0].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
2282
- parts[1].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
2283
- }
2284
- }
2285
- return parts;
2286
- }
2287
- get isFullCol() {
2288
- return this._zone.bottom === undefined;
2289
- }
2290
- get isFullRow() {
2291
- return this._zone.right === undefined;
2292
- }
2293
- get rangeData() {
2294
- return {
2295
- _zone: this._zone,
2296
- _sheetId: this.sheetId,
2297
- };
2298
- }
2299
- /**
2300
- * Check that a zone is valid regarding the order of top-bottom and left-right.
2301
- * Left should be smaller than right, top should be smaller than bottom.
2302
- * If it's not the case, simply invert them, and invert the linked parts
2303
- */
2304
- orderZone() {
2305
- const zone = { ...this._zone };
2306
- let parts = this.parts;
2307
- if (zone.right !== undefined && zone.right < zone.left) {
2308
- let right = zone.right;
2309
- zone.right = zone.left;
2310
- zone.left = right;
2311
- parts = [
2312
- {
2313
- colFixed: parts[1]?.colFixed || false,
2314
- rowFixed: parts[0]?.rowFixed || false,
2315
- },
2316
- {
2317
- colFixed: parts[0]?.colFixed || false,
2318
- rowFixed: parts[1]?.rowFixed || false,
2319
- },
2320
- ];
2321
- }
2322
- if (zone.bottom !== undefined && zone.bottom < zone.top) {
2323
- let bottom = zone.bottom;
2324
- zone.bottom = zone.top;
2325
- zone.top = bottom;
2326
- parts = [
2327
- {
2328
- colFixed: parts[0]?.colFixed || false,
2329
- rowFixed: parts[1]?.rowFixed || false,
2330
- },
2331
- {
2332
- colFixed: parts[1]?.colFixed || false,
2333
- rowFixed: parts[0]?.rowFixed || false,
2334
- },
2335
- ];
2336
- }
2337
- return this.clone({ zone, parts });
2338
- }
2339
- /**
2340
- *
2341
- * @param rangeParams optional, values to put in the cloned range instead of the current values of the range
2342
- */
2343
- clone(rangeParams) {
2344
- return new RangeImpl({
2345
- zone: rangeParams?.zone ? rangeParams.zone : { ...this._zone },
2346
- sheetId: rangeParams?.sheetId ? rangeParams.sheetId : this.sheetId,
2347
- invalidSheetName: rangeParams && "invalidSheetName" in rangeParams // 'attr in obj' instead of just 'obj.attr' because we accept undefined values
2348
- ? rangeParams.invalidSheetName
2349
- : this.invalidSheetName,
2350
- invalidXc: rangeParams && "invalidXc" in rangeParams ? rangeParams.invalidXc : this.invalidXc,
2351
- parts: rangeParams?.parts
2352
- ? rangeParams.parts
2353
- : this.parts.map((part) => {
2354
- return { rowFixed: part.rowFixed, colFixed: part.colFixed };
2355
- }),
2356
- prefixSheet: rangeParams?.prefixSheet ? rangeParams.prefixSheet : this.prefixSheet,
2357
- }, this.getSheetSize);
2358
- }
2359
- }
2360
- /**
2361
- * Copy a range. If the range is on the sheetIdFrom, the range will target
2362
- * sheetIdTo.
2363
- */
2364
- function copyRangeWithNewSheetId(sheetIdFrom, sheetIdTo, range) {
2365
- const sheetId = range.sheetId === sheetIdFrom ? sheetIdTo : range.sheetId;
2366
- return range.clone({ sheetId });
2367
- }
2368
- /**
2369
- * Create a range from a xc. If the xc is empty, this function returns undefined.
2370
- */
2371
- function createRange(getters, sheetId, range) {
2372
- return range ? getters.getRangeFromSheetXC(sheetId, range) : undefined;
2373
- }
2374
-
2375
- /** Methods from Odoo Web Utils */
2376
- /**
2377
- * This function computes a score that represent the fact that the
2378
- * string contains the pattern, or not
2379
- *
2380
- * - If the score is 0, the string does not contain the letters of the pattern in
2381
- * the correct order.
2382
- * - if the score is > 0, it actually contains the letters.
2383
- *
2384
- * Better matches will get a higher score: consecutive letters are better,
2385
- * and a match closer to the beginning of the string is also scored higher.
2386
- */
2387
- function fuzzyMatch(pattern, str) {
2388
- pattern = pattern.toLocaleLowerCase();
2389
- str = str.toLocaleLowerCase();
2390
- let totalScore = 0;
2391
- let currentScore = 0;
2392
- let len = str.length;
2393
- let patternIndex = 0;
2394
- for (let i = 0; i < len; i++) {
2395
- if (str[i] === pattern[patternIndex]) {
2396
- patternIndex++;
2397
- currentScore += 100 + currentScore - i / 200;
2398
- }
2399
- else {
2400
- currentScore = 0;
2401
- }
2402
- totalScore = totalScore + currentScore;
2403
- }
2404
- return patternIndex === pattern.length ? totalScore : 0;
2405
- }
2406
- /**
2407
- * Return a list of things that matches a pattern, ordered by their 'score' (
2408
- * higher score first). An higher score means that the match is better. For
2409
- * example, consecutive letters are considered a better match.
2410
- */
2411
- function fuzzyLookup(pattern, list, fn) {
2412
- const results = [];
2413
- list.forEach((data) => {
2414
- const score = fuzzyMatch(pattern, fn(data));
2415
- if (score > 0) {
2416
- results.push({ score, elem: data });
2417
- }
2418
- });
2419
- // we want better matches first
2420
- results.sort((a, b) => b.score - a.score);
2421
- return results.map((r) => r.elem);
2422
- }
2423
-
2424
- function createDefaultRows(rowNumber) {
2425
- const rows = [];
2426
- for (let i = 0; i < rowNumber; i++) {
2427
- const row = {
2428
- cells: {},
2429
- };
2430
- rows.push(row);
2431
- }
2432
- return rows;
2433
- }
2434
-
2435
- /*
2436
- * https://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript
2437
- * */
2438
- class UuidGenerator {
2439
- isFastIdStrategy = false;
2440
- fastIdStart = 0;
2441
- setIsFastStrategy(isFast) {
2442
- this.isFastIdStrategy = isFast;
2443
- }
2444
- uuidv4() {
2445
- if (this.isFastIdStrategy) {
2446
- this.fastIdStart++;
2447
- return String(this.fastIdStart);
2448
- //@ts-ignore
2449
- }
2450
- else if (window.crypto && window.crypto.getRandomValues) {
2451
- //@ts-ignore
2452
- return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c) => (c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16));
2453
- }
2454
- else {
2455
- // mainly for jest and other browsers that do not have the crypto functionality
2456
- return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
2457
- var r = (Math.random() * 16) | 0, v = c == "x" ? r : (r & 0x3) | 0x8;
2458
- return v.toString(16);
2459
- });
2460
- }
2461
- }
2462
- }
2463
-
2464
2221
  /**
2465
2222
  * Convert from a cartesian reference to a Zone
2466
2223
  * The range boundaries will be kept in the same order as the
@@ -3067,6 +2824,287 @@ function getZonesRows(zones) {
3067
2824
  return set;
3068
2825
  }
3069
2826
 
2827
+ class RangeImpl {
2828
+ getSheetSize;
2829
+ _zone;
2830
+ parts;
2831
+ invalidXc;
2832
+ prefixSheet = false;
2833
+ sheetId; // the sheet on which the range is defined
2834
+ invalidSheetName; // the name of any sheet that is invalid
2835
+ constructor(args, getSheetSize) {
2836
+ this.getSheetSize = getSheetSize;
2837
+ this._zone = args.zone;
2838
+ this.parts = args.parts;
2839
+ this.prefixSheet = args.prefixSheet;
2840
+ this.invalidXc = args.invalidXc;
2841
+ this.sheetId = args.sheetId;
2842
+ this.invalidSheetName = args.invalidSheetName;
2843
+ }
2844
+ static fromRange(range, getters) {
2845
+ if (range instanceof RangeImpl) {
2846
+ return range;
2847
+ }
2848
+ return new RangeImpl(range, getters.getSheetSize);
2849
+ }
2850
+ get unboundedZone() {
2851
+ return this._zone;
2852
+ }
2853
+ get zone() {
2854
+ const { left, top, bottom, right } = this._zone;
2855
+ if (right !== undefined && bottom !== undefined)
2856
+ return { left, top, right, bottom };
2857
+ else if (bottom === undefined && right !== undefined) {
2858
+ return { right, top, left, bottom: this.getSheetSize(this.sheetId).numberOfRows - 1 };
2859
+ }
2860
+ else if (right === undefined && bottom !== undefined) {
2861
+ return { bottom, left, top, right: this.getSheetSize(this.sheetId).numberOfCols - 1 };
2862
+ }
2863
+ throw new Error(_lt("Bad zone format"));
2864
+ }
2865
+ static getRangeParts(xc, zone) {
2866
+ const parts = xc.split(":").map((p) => {
2867
+ const isFullRow = isRowReference(p);
2868
+ return {
2869
+ colFixed: isFullRow ? false : p.startsWith("$"),
2870
+ rowFixed: isFullRow ? p.startsWith("$") : p.includes("$", 1),
2871
+ };
2872
+ });
2873
+ const isFullCol = zone.bottom === undefined;
2874
+ const isFullRow = zone.right === undefined;
2875
+ if (isFullCol) {
2876
+ parts[0].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
2877
+ parts[1].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
2878
+ if (zone.left === zone.right) {
2879
+ parts[0].colFixed = parts[0].colFixed || parts[1].colFixed;
2880
+ parts[1].colFixed = parts[0].colFixed || parts[1].colFixed;
2881
+ }
2882
+ }
2883
+ if (isFullRow) {
2884
+ parts[0].colFixed = parts[0].colFixed || parts[1].colFixed;
2885
+ parts[1].colFixed = parts[0].colFixed || parts[1].colFixed;
2886
+ if (zone.top === zone.bottom) {
2887
+ parts[0].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
2888
+ parts[1].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
2889
+ }
2890
+ }
2891
+ return parts;
2892
+ }
2893
+ get isFullCol() {
2894
+ return this._zone.bottom === undefined;
2895
+ }
2896
+ get isFullRow() {
2897
+ return this._zone.right === undefined;
2898
+ }
2899
+ get rangeData() {
2900
+ return {
2901
+ _zone: this._zone,
2902
+ _sheetId: this.sheetId,
2903
+ };
2904
+ }
2905
+ /**
2906
+ * Check that a zone is valid regarding the order of top-bottom and left-right.
2907
+ * Left should be smaller than right, top should be smaller than bottom.
2908
+ * If it's not the case, simply invert them, and invert the linked parts
2909
+ */
2910
+ orderZone() {
2911
+ const zone = { ...this._zone };
2912
+ let parts = this.parts;
2913
+ if (zone.right !== undefined && zone.right < zone.left) {
2914
+ let right = zone.right;
2915
+ zone.right = zone.left;
2916
+ zone.left = right;
2917
+ parts = [
2918
+ {
2919
+ colFixed: parts[1]?.colFixed || false,
2920
+ rowFixed: parts[0]?.rowFixed || false,
2921
+ },
2922
+ {
2923
+ colFixed: parts[0]?.colFixed || false,
2924
+ rowFixed: parts[1]?.rowFixed || false,
2925
+ },
2926
+ ];
2927
+ }
2928
+ if (zone.bottom !== undefined && zone.bottom < zone.top) {
2929
+ let bottom = zone.bottom;
2930
+ zone.bottom = zone.top;
2931
+ zone.top = bottom;
2932
+ parts = [
2933
+ {
2934
+ colFixed: parts[0]?.colFixed || false,
2935
+ rowFixed: parts[1]?.rowFixed || false,
2936
+ },
2937
+ {
2938
+ colFixed: parts[1]?.colFixed || false,
2939
+ rowFixed: parts[0]?.rowFixed || false,
2940
+ },
2941
+ ];
2942
+ }
2943
+ return this.clone({ zone, parts });
2944
+ }
2945
+ /**
2946
+ *
2947
+ * @param rangeParams optional, values to put in the cloned range instead of the current values of the range
2948
+ */
2949
+ clone(rangeParams) {
2950
+ return new RangeImpl({
2951
+ zone: rangeParams?.zone ? rangeParams.zone : { ...this._zone },
2952
+ sheetId: rangeParams?.sheetId ? rangeParams.sheetId : this.sheetId,
2953
+ invalidSheetName: rangeParams && "invalidSheetName" in rangeParams // 'attr in obj' instead of just 'obj.attr' because we accept undefined values
2954
+ ? rangeParams.invalidSheetName
2955
+ : this.invalidSheetName,
2956
+ invalidXc: rangeParams && "invalidXc" in rangeParams ? rangeParams.invalidXc : this.invalidXc,
2957
+ parts: rangeParams?.parts
2958
+ ? rangeParams.parts
2959
+ : this.parts.map((part) => {
2960
+ return { rowFixed: part.rowFixed, colFixed: part.colFixed };
2961
+ }),
2962
+ prefixSheet: rangeParams?.prefixSheet ? rangeParams.prefixSheet : this.prefixSheet,
2963
+ }, this.getSheetSize);
2964
+ }
2965
+ }
2966
+ /**
2967
+ * Copy a range. If the range is on the sheetIdFrom, the range will target
2968
+ * sheetIdTo.
2969
+ */
2970
+ function copyRangeWithNewSheetId(sheetIdFrom, sheetIdTo, range) {
2971
+ const sheetId = range.sheetId === sheetIdFrom ? sheetIdTo : range.sheetId;
2972
+ return range.clone({ sheetId });
2973
+ }
2974
+ /**
2975
+ * Create a range from a xc. If the xc is empty, this function returns undefined.
2976
+ */
2977
+ function createRange(getters, sheetId, range) {
2978
+ return range ? getters.getRangeFromSheetXC(sheetId, range) : undefined;
2979
+ }
2980
+ /**
2981
+ * Spread multiple colrows zone to one row/col zone and add a many new input range as needed.
2982
+ * For example, A1:B4 will become [A1:A4, B1:B4]
2983
+ */
2984
+ function spreadRange(ranges) {
2985
+ const postProcessedRanges = [];
2986
+ for (const range of ranges) {
2987
+ const { sheetName } = splitReference(range);
2988
+ const sheetPrefix = sheetName ? `${sheetName}!` : "";
2989
+ const zone = toUnboundedZone(range);
2990
+ if (zone.bottom !== zone.top && zone.left != zone.right) {
2991
+ if (zone.right) {
2992
+ for (let j = zone.left; j <= zone.right; ++j) {
2993
+ postProcessedRanges.push(`${sheetPrefix}${zoneToXc({
2994
+ left: j,
2995
+ right: j,
2996
+ top: zone.top,
2997
+ bottom: zone.bottom,
2998
+ })}`);
2999
+ }
3000
+ }
3001
+ else {
3002
+ for (let j = zone.top; j <= zone.bottom; ++j) {
3003
+ postProcessedRanges.push(`${sheetPrefix}${zoneToXc({
3004
+ left: zone.left,
3005
+ right: zone.right,
3006
+ top: j,
3007
+ bottom: j,
3008
+ })}`);
3009
+ }
3010
+ }
3011
+ }
3012
+ else {
3013
+ postProcessedRanges.push(range);
3014
+ }
3015
+ }
3016
+ return postProcessedRanges;
3017
+ }
3018
+
3019
+ /** Methods from Odoo Web Utils */
3020
+ /**
3021
+ * This function computes a score that represent the fact that the
3022
+ * string contains the pattern, or not
3023
+ *
3024
+ * - If the score is 0, the string does not contain the letters of the pattern in
3025
+ * the correct order.
3026
+ * - if the score is > 0, it actually contains the letters.
3027
+ *
3028
+ * Better matches will get a higher score: consecutive letters are better,
3029
+ * and a match closer to the beginning of the string is also scored higher.
3030
+ */
3031
+ function fuzzyMatch(pattern, str) {
3032
+ pattern = pattern.toLocaleLowerCase();
3033
+ str = str.toLocaleLowerCase();
3034
+ let totalScore = 0;
3035
+ let currentScore = 0;
3036
+ let len = str.length;
3037
+ let patternIndex = 0;
3038
+ for (let i = 0; i < len; i++) {
3039
+ if (str[i] === pattern[patternIndex]) {
3040
+ patternIndex++;
3041
+ currentScore += 100 + currentScore - i / 200;
3042
+ }
3043
+ else {
3044
+ currentScore = 0;
3045
+ }
3046
+ totalScore = totalScore + currentScore;
3047
+ }
3048
+ return patternIndex === pattern.length ? totalScore : 0;
3049
+ }
3050
+ /**
3051
+ * Return a list of things that matches a pattern, ordered by their 'score' (
3052
+ * higher score first). An higher score means that the match is better. For
3053
+ * example, consecutive letters are considered a better match.
3054
+ */
3055
+ function fuzzyLookup(pattern, list, fn) {
3056
+ const results = [];
3057
+ list.forEach((data) => {
3058
+ const score = fuzzyMatch(pattern, fn(data));
3059
+ if (score > 0) {
3060
+ results.push({ score, elem: data });
3061
+ }
3062
+ });
3063
+ // we want better matches first
3064
+ results.sort((a, b) => b.score - a.score);
3065
+ return results.map((r) => r.elem);
3066
+ }
3067
+
3068
+ function createDefaultRows(rowNumber) {
3069
+ const rows = [];
3070
+ for (let i = 0; i < rowNumber; i++) {
3071
+ const row = {
3072
+ cells: {},
3073
+ };
3074
+ rows.push(row);
3075
+ }
3076
+ return rows;
3077
+ }
3078
+
3079
+ /*
3080
+ * https://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript
3081
+ * */
3082
+ class UuidGenerator {
3083
+ isFastIdStrategy = false;
3084
+ fastIdStart = 0;
3085
+ setIsFastStrategy(isFast) {
3086
+ this.isFastIdStrategy = isFast;
3087
+ }
3088
+ uuidv4() {
3089
+ if (this.isFastIdStrategy) {
3090
+ this.fastIdStart++;
3091
+ return String(this.fastIdStart);
3092
+ //@ts-ignore
3093
+ }
3094
+ else if (window.crypto && window.crypto.getRandomValues) {
3095
+ //@ts-ignore
3096
+ return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c) => (c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16));
3097
+ }
3098
+ else {
3099
+ // mainly for jest and other browsers that do not have the crypto functionality
3100
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
3101
+ var r = (Math.random() * 16) | 0, v = c == "x" ? r : (r & 0x3) | 0x8;
3102
+ return v.toString(16);
3103
+ });
3104
+ }
3105
+ }
3106
+ }
3107
+
3070
3108
  class ChartJsComponent extends owl.Component {
3071
3109
  static template = "o-spreadsheet-ChartJsComponent";
3072
3110
  canvas = owl.useRef("graphContainer");
@@ -17646,50 +17684,51 @@ const uuidGenerator$1 = new UuidGenerator();
17646
17684
  css /* scss */ `
17647
17685
  .o-selection {
17648
17686
  .o-selection-input {
17649
- display: flex;
17650
- flex-direction: row;
17687
+ padding: 2px 0px;
17651
17688
 
17652
17689
  input {
17653
17690
  padding: 4px 6px;
17654
17691
  border-radius: 4px;
17655
17692
  box-sizing: border-box;
17656
- flex-grow: 2;
17657
17693
  }
17658
17694
  input:focus {
17659
17695
  outline: none;
17660
17696
  }
17661
17697
  input.o-required,
17662
17698
  input.o-focused {
17663
- border-width: 2px;
17664
- padding: 3px 5px;
17699
+ border: 1px solid;
17665
17700
  }
17666
17701
  input.o-focused {
17667
- border-color: ${SELECTION_BORDER_COLOR};
17702
+ border-width: 2px;
17703
+ padding: 3px 5px;
17668
17704
  }
17669
17705
  input.o-invalid {
17670
- border-color: red;
17706
+ /* The background-color is similar to the bootstrap alert-danger class but, because of the commit 0358a76d,
17707
+ * which avoids being parasitized by the dark-mode in spreadsheet, we cannot use this class.
17708
+ * TODO: Replace with the bootstrap alert-danger class when we support dark mode
17709
+ */
17710
+ background-color: #ffdddd;
17711
+ border-width: 2px;
17671
17712
  }
17672
17713
  button.o-btn {
17673
- background: transparent;
17674
- border: none;
17675
17714
  color: #333;
17676
- cursor: pointer;
17677
17715
  }
17678
17716
  button.o-btn-action {
17679
17717
  margin: 8px 1px;
17680
17718
  border-radius: 4px;
17681
- background: transparent;
17682
17719
  border: 1px solid #dadce0;
17683
17720
  color: #188038;
17684
- font-weight: bold;
17685
17721
  font-size: 14px;
17686
17722
  height: 25px;
17687
17723
  }
17724
+ .error-icon {
17725
+ right: 7px;
17726
+ top: 7px;
17727
+ }
17688
17728
  }
17689
17729
  /** Make the character a bit bigger
17690
17730
  compared to its neighbor INPUT box */
17691
17731
  .o-remove-selection {
17692
- font-weight: bold;
17693
17732
  font-size: calc(100% + 4px);
17694
17733
  }
17695
17734
  }
@@ -17705,7 +17744,7 @@ css /* scss */ `
17705
17744
  class SelectionInput extends owl.Component {
17706
17745
  static template = "o-spreadsheet-SelectionInput";
17707
17746
  id = uuidGenerator$1.uuidv4();
17708
- previousRanges = this.props.ranges || [];
17747
+ previousRanges = this.props.ranges() || [];
17709
17748
  originSheet = this.env.model.getters.getActiveSheetId();
17710
17749
  state = owl.useState({
17711
17750
  isMissing: false,
@@ -17713,11 +17752,11 @@ class SelectionInput extends owl.Component {
17713
17752
  });
17714
17753
  focusedInput = owl.useRef("focusedInput");
17715
17754
  get ranges() {
17716
- const existingSelectionRange = this.env.model.getters.getSelectionInput(this.id);
17717
- const ranges = existingSelectionRange.length
17718
- ? existingSelectionRange
17719
- : this.props.ranges
17720
- ? this.props.ranges.map((xc, id) => ({
17755
+ const existingSelectionRanges = this.env.model.getters.getSelectionInput(this.id);
17756
+ const ranges = existingSelectionRanges.length
17757
+ ? existingSelectionRanges
17758
+ : this.props.ranges()
17759
+ ? this.props.ranges().map((xc, id) => ({
17721
17760
  xc,
17722
17761
  id,
17723
17762
  isFocused: false,
@@ -17737,6 +17776,12 @@ class SelectionInput extends owl.Component {
17737
17776
  get isInvalid() {
17738
17777
  return this.props.isInvalid || this.state.isMissing;
17739
17778
  }
17779
+ get isConfirmable() {
17780
+ return this.hasFocus && this.ranges.every((range) => range.isValidRange);
17781
+ }
17782
+ get isResettable() {
17783
+ return this.previousRanges.join() !== this.ranges.map((r) => r.xc).join();
17784
+ }
17740
17785
  setup() {
17741
17786
  owl.useEffect(() => this.focusedInput.el?.focus(), () => [this.focusedInput.el]);
17742
17787
  owl.onMounted(() => this.enableNewSelectionInput());
@@ -17746,7 +17791,7 @@ class SelectionInput extends owl.Component {
17746
17791
  enableNewSelectionInput() {
17747
17792
  this.env.model.dispatch("ENABLE_NEW_SELECTION_INPUT", {
17748
17793
  id: this.id,
17749
- initialRanges: this.props.ranges,
17794
+ initialRanges: this.props.ranges(),
17750
17795
  hasSingleRange: this.props.hasSingleRange,
17751
17796
  });
17752
17797
  }
@@ -17755,7 +17800,8 @@ class SelectionInput extends owl.Component {
17755
17800
  }
17756
17801
  checkChange() {
17757
17802
  const value = this.env.model.getters.getSelectionInputValue(this.id);
17758
- if (this.previousRanges.join() !== value.join()) {
17803
+ const valid = !this.isInvalid && this.ranges.every((range) => range.isValidRange);
17804
+ if (valid && this.previousRanges.join() !== value.join()) {
17759
17805
  this.triggerChange();
17760
17806
  }
17761
17807
  }
@@ -17766,7 +17812,6 @@ class SelectionInput extends owl.Component {
17766
17812
  triggerChange() {
17767
17813
  const ranges = this.env.model.getters.getSelectionInputValue(this.id);
17768
17814
  this.props.onSelectionChanged?.(ranges);
17769
- this.previousRanges = ranges;
17770
17815
  }
17771
17816
  onKeydown(ev) {
17772
17817
  if (ev.key === "F2") {
@@ -17816,10 +17861,37 @@ class SelectionInput extends owl.Component {
17816
17861
  });
17817
17862
  this.triggerChange();
17818
17863
  }
17864
+ reset() {
17865
+ const existingSelectionRanges = this.env.model.getters.getSelectionInput(this.id);
17866
+ for (const range of existingSelectionRanges) {
17867
+ this.env.model.dispatch("REMOVE_RANGE", {
17868
+ id: this.id,
17869
+ rangeId: range.id,
17870
+ });
17871
+ }
17872
+ for (const range of this.previousRanges) {
17873
+ this.env.model.dispatch("ADD_RANGE", { id: this.id, value: range });
17874
+ }
17875
+ this.confirm();
17876
+ }
17819
17877
  confirm() {
17820
- this.env.model.dispatch("UNFOCUS_SELECTION_INPUT");
17821
- const ranges = this.env.model.getters.getSelectionInputValue(this.id);
17822
- if (this.props.required && ranges.length === 0) {
17878
+ let anyValidInput = false;
17879
+ const existingSelectionRanges = this.env.model.getters.getSelectionInput(this.id);
17880
+ const existingSelectionXcs = [];
17881
+ for (const range of existingSelectionRanges) {
17882
+ if (range.xc === "") {
17883
+ this.env.model.dispatch("REMOVE_RANGE", {
17884
+ id: this.id,
17885
+ rangeId: range.id,
17886
+ });
17887
+ continue;
17888
+ }
17889
+ existingSelectionXcs.push(range.xc);
17890
+ if (this.env.model.getters.isRangeValid(range.xc)) {
17891
+ anyValidInput = true;
17892
+ }
17893
+ }
17894
+ if (this.props.required && !anyValidInput) {
17823
17895
  this.state.isMissing = true;
17824
17896
  }
17825
17897
  const activeSheetId = this.env.model.getters.getActiveSheetId();
@@ -17829,11 +17901,17 @@ class SelectionInput extends owl.Component {
17829
17901
  sheetIdTo: this.originSheet,
17830
17902
  });
17831
17903
  }
17904
+ this.props.onSelectionChanged?.(existingSelectionXcs);
17832
17905
  this.props.onSelectionConfirmed?.();
17906
+ this.previousRanges = this.props.ranges();
17907
+ if (existingSelectionXcs.join() !== this.previousRanges.join()) {
17908
+ this.enableNewSelectionInput();
17909
+ }
17910
+ this.env.model.dispatch("UNFOCUS_SELECTION_INPUT");
17833
17911
  }
17834
17912
  }
17835
17913
  SelectionInput.props = {
17836
- ranges: Array,
17914
+ ranges: Function,
17837
17915
  hasSingleRange: { type: Boolean, optional: true },
17838
17916
  required: { type: Boolean, optional: true },
17839
17917
  isInvalid: { type: Boolean, optional: true },
@@ -17881,10 +17959,14 @@ class LineBarPieConfigPanel extends owl.Component {
17881
17959
  this.dataSeriesRanges = ranges;
17882
17960
  }
17883
17961
  onDataSeriesConfirmed() {
17962
+ this.dataSeriesRanges = spreadRange(this.dataSeriesRanges);
17884
17963
  this.state.datasetDispatchResult = this.props.updateChart({
17885
17964
  dataSets: this.dataSeriesRanges,
17886
17965
  });
17887
17966
  }
17967
+ getDataSeriesRanges() {
17968
+ return this.dataSeriesRanges;
17969
+ }
17888
17970
  /**
17889
17971
  * Change the local labelRange. The model should be updated when the
17890
17972
  * button "confirm" is clicked
@@ -17897,6 +17979,9 @@ class LineBarPieConfigPanel extends owl.Component {
17897
17979
  labelRange: this.labelRange,
17898
17980
  });
17899
17981
  }
17982
+ getLabelRange() {
17983
+ return this.labelRange || "";
17984
+ }
17900
17985
  onUpdateAggregated(ev) {
17901
17986
  this.props.updateChart({
17902
17987
  aggregated: ev.target.checked,
@@ -18317,6 +18402,9 @@ class GaugeChartConfigPanel extends owl.Component {
18317
18402
  dataRange: this.dataRange,
18318
18403
  });
18319
18404
  }
18405
+ getDataRange() {
18406
+ return this.dataRange || "";
18407
+ }
18320
18408
  }
18321
18409
  GaugeChartConfigPanel.props = {
18322
18410
  figureId: String,
@@ -18515,6 +18603,9 @@ class ScorecardChartConfigPanel extends owl.Component {
18515
18603
  keyValue: this.keyValue,
18516
18604
  });
18517
18605
  }
18606
+ getKeyValueRange() {
18607
+ return this.keyValue || "";
18608
+ }
18518
18609
  onBaselineRangeChanged(ranges) {
18519
18610
  this.baseline = ranges[0];
18520
18611
  }
@@ -18523,6 +18614,9 @@ class ScorecardChartConfigPanel extends owl.Component {
18523
18614
  baseline: this.baseline,
18524
18615
  });
18525
18616
  }
18617
+ getBaselineRange() {
18618
+ return this.baseline || "";
18619
+ }
18526
18620
  updateBaselineMode(ev) {
18527
18621
  this.props.updateChart({ baselineMode: ev.target.value });
18528
18622
  }
@@ -28855,11 +28949,9 @@ class BasePlugin {
28855
28949
  */
28856
28950
  class CorePlugin extends BasePlugin {
28857
28951
  getters;
28858
- range;
28859
28952
  uuidGenerator;
28860
28953
  constructor({ getters, stateObserver, range, dispatch, uuidGenerator }) {
28861
28954
  super(stateObserver, dispatch);
28862
- this.range = range;
28863
28955
  range.addRangeProvider(this.adaptRanges.bind(this));
28864
28956
  this.getters = getters;
28865
28957
  this.uuidGenerator = uuidGenerator;
@@ -38622,6 +38714,7 @@ class SelectionInputPlugin extends UIPlugin {
38622
38714
  // ---------------------------------------------------------------------------
38623
38715
  allowDispatch(cmd) {
38624
38716
  switch (cmd.type) {
38717
+ case "ADD_RANGE":
38625
38718
  case "ADD_EMPTY_RANGE":
38626
38719
  if (this.inputHasSingleRange && this.ranges.length === 1) {
38627
38720
  return 30 /* CommandResult.MaximumRangesReached */;
@@ -38666,6 +38759,10 @@ class SelectionInputPlugin extends UIPlugin {
38666
38759
  this.insertNewRange(this.ranges.length, [""]);
38667
38760
  this.focusLast();
38668
38761
  break;
38762
+ case "ADD_RANGE":
38763
+ this.insertNewRange(this.ranges.length, [cmd.value]);
38764
+ this.focusLast();
38765
+ break;
38669
38766
  case "REMOVE_RANGE":
38670
38767
  const index = this.getIndex(cmd.rangeId);
38671
38768
  if (index !== null) {
@@ -38877,6 +38974,7 @@ class SelectionInputsManagerPlugin extends UIPlugin {
38877
38974
  case "UNFOCUS_SELECTION_INPUT":
38878
38975
  this.unfocus();
38879
38976
  break;
38977
+ case "ADD_RANGE":
38880
38978
  case "ADD_EMPTY_RANGE":
38881
38979
  case "REMOVE_RANGE":
38882
38980
  if (cmd.id !== this.focusedInputId) {
@@ -38890,9 +38988,11 @@ class SelectionInputsManagerPlugin extends UIPlugin {
38890
38988
  if (cmd.id !== this.focusedInputId) {
38891
38989
  const input = this.inputs[cmd.id];
38892
38990
  const range = input.ranges.find((range) => range.id === cmd.rangeId);
38893
- const sheetId = this.getters.getActiveSheetId();
38894
- const zone = this.getters.getRangeFromSheetXC(sheetId, range?.xc || "A1").zone;
38895
- this.selection.capture(input, { cell: { col: zone.left, row: zone.top }, zone }, { handleEvent: input.handleEvent.bind(input) });
38991
+ if (this.isRangeValid(range?.xc || "A1")) {
38992
+ const sheetId = this.getters.getActiveSheetId();
38993
+ const zone = this.getters.getRangeFromSheetXC(sheetId, range?.xc || "A1").zone;
38994
+ this.selection.capture(input, { cell: { col: zone.left, row: zone.top }, zone }, { handleEvent: input.handleEvent.bind(input) });
38995
+ }
38896
38996
  this.focusedInputId = cmd.id;
38897
38997
  }
38898
38998
  break;
@@ -48191,6 +48291,8 @@ exports.coreTypes = coreTypes;
48191
48291
  exports.findCellInNewZone = findCellInNewZone;
48192
48292
  exports.functionCache = functionCache;
48193
48293
  exports.helpers = helpers;
48294
+ exports.invalidateCFEvaluationCommands = invalidateCFEvaluationCommands;
48295
+ exports.invalidateDependenciesCommands = invalidateDependenciesCommands;
48194
48296
  exports.invalidateEvaluationCommands = invalidateEvaluationCommands;
48195
48297
  exports.links = links;
48196
48298
  exports.load = load;
@@ -48202,6 +48304,6 @@ exports.setTranslationMethod = setTranslationMethod;
48202
48304
  exports.tokenize = tokenize;
48203
48305
 
48204
48306
 
48205
- __info__.version = '16.4.0-alpha.1';
48206
- __info__.date = '2023-05-25T13:00:14.133Z';
48207
- __info__.hash = '564c094';
48307
+ __info__.version = '16.4.0-alpha.2';
48308
+ __info__.date = '2023-05-26T13:40:49.561Z';
48309
+ __info__.hash = '82328ad';