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