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