@rowsncolumns/spreadsheet-state 28.0.74 → 28.0.75

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.
Files changed (65) hide show
  1. package/dist/cjs/api/datatype.d.ts.map +1 -1
  2. package/dist/cjs/api/datatype.js +8 -1
  3. package/dist/cjs/api/fill-series-spec.d.ts +35 -0
  4. package/dist/cjs/api/fill-series-spec.d.ts.map +1 -0
  5. package/dist/cjs/api/fill-series-spec.js +164 -0
  6. package/dist/cjs/api/fill.d.ts +95 -3
  7. package/dist/cjs/api/fill.d.ts.map +1 -1
  8. package/dist/cjs/api/fill.js +1186 -418
  9. package/dist/cjs/api/immer-utils.d.ts.map +1 -1
  10. package/dist/cjs/api/immer-utils.js +20 -15
  11. package/dist/cjs/api/intl.d.ts.map +1 -1
  12. package/dist/cjs/api/paint-format.d.ts +15 -1
  13. package/dist/cjs/api/paint-format.d.ts.map +1 -1
  14. package/dist/cjs/api/paint-format.js +69 -10
  15. package/dist/cjs/commands.d.ts +11 -1
  16. package/dist/cjs/commands.d.ts.map +1 -1
  17. package/dist/cjs/hooks/use-calculation.d.ts +1 -1
  18. package/dist/cjs/hooks/use-on-fill.d.ts +13 -4
  19. package/dist/cjs/hooks/use-on-fill.d.ts.map +1 -1
  20. package/dist/cjs/hooks/use-on-fill.js +28 -7
  21. package/dist/cjs/hooks/use-paint-format.d.ts.map +1 -1
  22. package/dist/cjs/hooks/use-paint-format.js +10 -7
  23. package/dist/cjs/hooks/use-spreadsheet-state.d.ts +1 -1
  24. package/dist/cjs/hooks/use-spreadsheet-state.d.ts.map +1 -1
  25. package/dist/cjs/hooks/use-spreadsheet-state.js +12 -1
  26. package/dist/cjs/interface/apply-fill.d.ts +3 -3
  27. package/dist/cjs/interface/apply-fill.d.ts.map +1 -1
  28. package/dist/cjs/interface/apply-fill.js +100 -53
  29. package/dist/cjs/interface/spreadsheet-interface.d.ts +1 -1
  30. package/dist/cjs/sheet-cell.d.ts.map +1 -1
  31. package/dist/cjs/sheet-cell.js +30 -6
  32. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  33. package/dist/esm/api/datatype.d.ts.map +1 -1
  34. package/dist/esm/api/datatype.js +8 -1
  35. package/dist/esm/api/fill-series-spec.d.ts +35 -0
  36. package/dist/esm/api/fill-series-spec.d.ts.map +1 -0
  37. package/dist/esm/api/fill-series-spec.js +160 -0
  38. package/dist/esm/api/fill.d.ts +95 -3
  39. package/dist/esm/api/fill.d.ts.map +1 -1
  40. package/dist/esm/api/fill.js +1158 -418
  41. package/dist/esm/api/immer-utils.d.ts.map +1 -1
  42. package/dist/esm/api/immer-utils.js +20 -15
  43. package/dist/esm/api/intl.d.ts.map +1 -1
  44. package/dist/esm/api/paint-format.d.ts +15 -1
  45. package/dist/esm/api/paint-format.d.ts.map +1 -1
  46. package/dist/esm/api/paint-format.js +46 -1
  47. package/dist/esm/commands.d.ts +11 -1
  48. package/dist/esm/commands.d.ts.map +1 -1
  49. package/dist/esm/hooks/use-calculation.d.ts +1 -1
  50. package/dist/esm/hooks/use-on-fill.d.ts +13 -4
  51. package/dist/esm/hooks/use-on-fill.d.ts.map +1 -1
  52. package/dist/esm/hooks/use-on-fill.js +24 -4
  53. package/dist/esm/hooks/use-paint-format.d.ts.map +1 -1
  54. package/dist/esm/hooks/use-paint-format.js +10 -7
  55. package/dist/esm/hooks/use-spreadsheet-state.d.ts +1 -1
  56. package/dist/esm/hooks/use-spreadsheet-state.d.ts.map +1 -1
  57. package/dist/esm/hooks/use-spreadsheet-state.js +12 -1
  58. package/dist/esm/interface/apply-fill.d.ts +3 -3
  59. package/dist/esm/interface/apply-fill.d.ts.map +1 -1
  60. package/dist/esm/interface/apply-fill.js +93 -48
  61. package/dist/esm/interface/spreadsheet-interface.d.ts +1 -1
  62. package/dist/esm/sheet-cell.d.ts.map +1 -1
  63. package/dist/esm/sheet-cell.js +30 -6
  64. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  65. package/package.json +10 -10
@@ -1,7 +1,98 @@
1
- import { __assign, __awaiter, __generator, __read, __spreadArray, __values } from "tslib";
1
+ import { __assign, __awaiter, __extends, __generator, __read, __rest, __spreadArray, __values } from "tslib";
2
2
  import { Direction } from "@rowsncolumns/common-types";
3
- import { toDate } from "@rowsncolumns/utils";
3
+ import { toDate, toSerial } from "@rowsncolumns/utils";
4
4
  import { SheetCell } from "../sheet-cell";
5
+ import { detectDecimalPattern } from "./decimals";
6
+ import { generateSeriesSpecValues } from "./fill-series-spec";
7
+ /** Fill types whose auto-detected outcome is a plain copy (the Ctrl toggle flips these to a series). */
8
+ var COPY_CLASS_FILL_TYPES = new Set([
9
+ "FillCopy",
10
+ "FillDefault",
11
+ ]);
12
+ /**
13
+ * Resolve the Ctrl/Cmd fill-handle toggle the way Excel does — ONE fill type
14
+ * for the whole drag, decided from what auto-detection would have done per
15
+ * fill line (each column of a vertical fill / row of a horizontal one).
16
+ * If ANY line would extend a series (2+ numbers, a date, a list name,
17
+ * "Item 1"), the toggle means Copy Cells for every line; only when every
18
+ * line would copy (lone numbers) does it mean Fill Series. Formula lines
19
+ * never vote (formulas always copy with relative-ref adjustment) and empty
20
+ * lines are ignored.
21
+ */
22
+ export var resolveToggledFillType = function (detectedPerLine) {
23
+ var votes = detectedPerLine.filter(function (t) { return t !== undefined; });
24
+ return votes.some(function (t) { return !COPY_CLASS_FILL_TYPES.has(t); })
25
+ ? "FillCopy"
26
+ : "FillSeries";
27
+ };
28
+ /**
29
+ * Excel's Auto Fill Options payload modifiers: "Fill Formatting Only" (`FillFormats`) and
30
+ * "Fill Without Formatting" (`FillWithoutFormatting`; `FillValues` is the VBA `xlFillValues`
31
+ * spelling the AI fill tool uses). Neither is a series mode — the series is detected exactly as
32
+ * for a plain drag; they only change WHAT lands in each target cell.
33
+ */
34
+ export var isFormatsOnlyFillType = function (fillType) {
35
+ return fillType === "FillFormats";
36
+ };
37
+ export var isValuesOnlyFillType = function (fillType) {
38
+ return fillType === "FillWithoutFormatting" || fillType === "FillValues";
39
+ };
40
+ /**
41
+ * Thrown when a fill source is touched by merges that don't tile it with identically sized
42
+ * blocks — Excel refuses the fill with this exact message and writes nothing.
43
+ */
44
+ var MergedFillSizeError = /** @class */ (function (_super) {
45
+ __extends(MergedFillSizeError, _super);
46
+ function MergedFillSizeError() {
47
+ var _this = _super.call(this, "This operation requires the merged cells to be identically sized.") || this;
48
+ _this.name = "MergedFillSizeError";
49
+ return _this;
50
+ }
51
+ return MergedFillSizeError;
52
+ }(Error));
53
+ export { MergedFillSizeError };
54
+ /**
55
+ * Marks a target cell the fill leaves exactly as it is: a Series-dialog `stop` value was reached
56
+ * before the selection's end, and Excel writes nothing into the remaining selected cells.
57
+ */
58
+ export var FILL_UNTOUCHED = Symbol("fill-untouched");
59
+ /**
60
+ * The merged-block geometry of a fill source: `undefined` when no merge touches it, the block
61
+ * size when identically sized merges tile it exactly (every merge inside the source, on the block
62
+ * lattice, and together covering it), else a {@link MergedFillSizeError}.
63
+ */
64
+ export var resolveMergedFillBlock = function (range, merges) {
65
+ var inside = (merges !== null && merges !== void 0 ? merges : []).filter(function (m) {
66
+ return m.startRowIndex <= range.endRowIndex &&
67
+ m.endRowIndex >= range.startRowIndex &&
68
+ m.startColumnIndex <= range.endColumnIndex &&
69
+ m.endColumnIndex >= range.startColumnIndex;
70
+ });
71
+ var first = inside[0];
72
+ if (!first)
73
+ return undefined;
74
+ var rows = first.endRowIndex - first.startRowIndex + 1;
75
+ var cols = first.endColumnIndex - first.startColumnIndex + 1;
76
+ var srcRows = range.endRowIndex - range.startRowIndex + 1;
77
+ var srcCols = range.endColumnIndex - range.startColumnIndex + 1;
78
+ var onLattice = inside.every(function (m) {
79
+ return m.endRowIndex - m.startRowIndex + 1 === rows &&
80
+ m.endColumnIndex - m.startColumnIndex + 1 === cols &&
81
+ m.startRowIndex >= range.startRowIndex &&
82
+ m.endRowIndex <= range.endRowIndex &&
83
+ m.startColumnIndex >= range.startColumnIndex &&
84
+ m.endColumnIndex <= range.endColumnIndex &&
85
+ (m.startRowIndex - range.startRowIndex) % rows === 0 &&
86
+ (m.startColumnIndex - range.startColumnIndex) % cols === 0;
87
+ });
88
+ var tiled = onLattice &&
89
+ srcRows % rows === 0 &&
90
+ srcCols % cols === 0 &&
91
+ inside.length === (srcRows / rows) * (srcCols / cols);
92
+ if (!tiled)
93
+ throw new MergedFillSizeError();
94
+ return { rows: rows, cols: cols };
95
+ };
5
96
  // Helper functions for pattern detection and fill operations
6
97
  /**
7
98
  * Extract numeric value from a string that may contain text
@@ -10,27 +101,285 @@ var extractNumber = function (value) {
10
101
  var match = value.match(/-?\d+\.?\d*/);
11
102
  return match ? parseFloat(match[0]) : null;
12
103
  };
13
- var TEXT_NUMBER_PATTERN_REGEX = /^(.*?)(-?\d+(?:\.\d+)?)(.*)$/;
104
+ /**
105
+ * Excel's text+number rule steps the LAST digit run of the string
106
+ * ("Text1Text2" → "Text1Text3", "Q1 2024" → "Q1 2025"). The lazy prefix plus
107
+ * the `\D*$` tail pins the captured run to the trailing one.
108
+ */
109
+ var TEXT_NUMBER_PATTERN_REGEX = /^(.*?)(\d+)(\D*)$/;
14
110
  var MONTH_NAME_REGEX = /\b(?:jan(?:uary)?|feb(?:ruary)?|mar(?:ch)?|apr(?:il)?|may|jun(?:e)?|jul(?:y)?|aug(?:ust)?|sep(?:t(?:ember)?)?|oct(?:ober)?|nov(?:ember)?|dec(?:ember)?)\b/i;
15
111
  var WEEKDAY_NAME_REGEX = /\b(?:mon(?:day)?|tue(?:s(?:day)?)?|wed(?:nesday)?|thu(?:rs(?:day)?)?|fri(?:day)?|sat(?:urday)?|sun(?:day)?)\b/i;
16
112
  var ISO_DATE_TIME_REGEX = /^\d{4}-\d{2}-\d{2}(?:[tT ]\d{2}:\d{2}(?::\d{2}(?:\.\d{1,3})?)?(?:Z|[+-]\d{2}:?\d{2})?)?$/;
17
113
  var NUMERIC_DATE_REGEX = /^\d{1,4}[\/.-]\d{1,2}(?:[\/.-]\d{1,4})?(?:\s+\d{1,2}:\d{2}(?::\d{2})?)?$/;
114
+ /** English ordinal suffix glued to a digit run ("1st Period", "2nd Quarter"). */
115
+ var ORDINAL_SUFFIX_REGEX = /^(st|nd|rd|th)(?![a-z])/i;
116
+ var BARE_DIGITS_REGEX = /^\d+$/;
117
+ /** Excel's built-in quarter tokens: Q1, Qtr 2, Quarter3 — whole-cell only. */
118
+ var QUARTER_TOKEN_REGEX = /^(q|qtr|quarter)(\s?)([1-4])$/i;
119
+ var QUARTER_ORDINAL_REGEX = /^([1-4])(st|nd|rd|th)(\s)(quarter|qtr)$/i;
120
+ var QUARTERS_IN_YEAR = 4;
18
121
  var extractTextNumberPattern = function (value) {
19
122
  var match = value.match(TEXT_NUMBER_PATTERN_REGEX);
20
123
  if (!match)
21
124
  return null;
22
- var _a = __read(match, 4), prefix = _a[1], numericPortion = _a[2], suffix = _a[3];
125
+ var _a = __read(match, 4), prefix = _a[1], run = _a[2], suffix = _a[3];
23
126
  if (!prefix && !suffix)
24
127
  return null;
25
- var parsed = parseFloat(numericPortion);
26
- if (Number.isNaN(parsed))
128
+ var parsed = Number(run);
129
+ if (!Number.isFinite(parsed))
27
130
  return null;
28
131
  return {
29
132
  prefix: prefix,
30
133
  suffix: suffix,
31
134
  number: parsed,
135
+ width: run.length,
32
136
  };
33
137
  };
138
+ var mod = function (value, len) { return ((value % len) + len) % len; };
139
+ /**
140
+ * Re-apply the seed's letter case to a canonical list entry: an all-caps seed
141
+ * continues in caps ("MONDAY" → "TUESDAY"), an all-lowercase one in lowercase
142
+ * ("jan" → "feb"); anything else keeps the list's own casing.
143
+ */
144
+ var applySeedCase = function (seed, canonical) {
145
+ // A seed without cased letters (`upper === lower`) keeps the list's casing.
146
+ var upper = seed.toUpperCase();
147
+ var lower = seed.toLowerCase();
148
+ if (seed === upper && upper !== lower)
149
+ return canonical.toUpperCase();
150
+ if (seed === lower && upper !== lower)
151
+ return canonical.toLowerCase();
152
+ return canonical;
153
+ };
154
+ var ordinalSuffix = function (n) {
155
+ var abs = Math.abs(Math.trunc(n));
156
+ var mod100 = abs % 100;
157
+ if (mod100 >= 11 && mod100 <= 13)
158
+ return "th";
159
+ switch (abs % 10) {
160
+ case 1:
161
+ return "st";
162
+ case 2:
163
+ return "nd";
164
+ case 3:
165
+ return "rd";
166
+ default:
167
+ return "th";
168
+ }
169
+ };
170
+ /** Render an integer digit run zero-padded to the seed's width (sign outside the padding). */
171
+ var formatDigitRun = function (n, width) {
172
+ var rounded = Math.round(n);
173
+ var digits = String(Math.abs(rounded)).padStart(width, "0");
174
+ return rounded < 0 ? "-".concat(digits) : digits;
175
+ };
176
+ var tokenizeDigitRuns = function (value) {
177
+ var parts = [];
178
+ var runs = [];
179
+ var re = /\d+/g;
180
+ var last = 0;
181
+ var match;
182
+ while ((match = re.exec(value)) !== null) {
183
+ parts.push(value.slice(last, match.index));
184
+ runs.push(match[0]);
185
+ last = match.index + match[0].length;
186
+ }
187
+ parts.push(value.slice(last));
188
+ return { parts: parts, runs: runs };
189
+ };
190
+ /**
191
+ * Align text seeds on their digit runs the way Excel does: every seed shares
192
+ * the same non-digit skeleton, exactly one run varies between seeds (the LAST
193
+ * run when nothing varies — the single-seed case) and it steps linearly.
194
+ * "Q1 2024","Q2 2024" → step the quarter; "Q1 2024" alone → step the year.
195
+ */
196
+ var matchTextNumberSeries = function (values) {
197
+ if (values.length === 0)
198
+ return null;
199
+ var tokens = values.map(tokenizeDigitRuns);
200
+ var first = tokens[0];
201
+ if (first.runs.length === 0)
202
+ return null;
203
+ var sameSkeleton = tokens.every(function (t) {
204
+ return t.runs.length === first.runs.length &&
205
+ t.parts.every(function (part, i) { return part === first.parts[i]; });
206
+ });
207
+ if (!sameSkeleton)
208
+ return null;
209
+ var varying = [];
210
+ var _loop_1 = function (r) {
211
+ if (tokens.some(function (t) { return Number(t.runs[r]) !== Number(first.runs[r]); })) {
212
+ varying.push(r);
213
+ }
214
+ };
215
+ for (var r = 0; r < first.runs.length; r++) {
216
+ _loop_1(r);
217
+ }
218
+ if (varying.length > 1)
219
+ return null;
220
+ var runIndex = varying.length === 1 ? varying[0] : first.runs.length - 1;
221
+ var numbers = tokens.map(function (t) { return Number(t.runs[runIndex]); });
222
+ if (numbers.some(function (n) { return !Number.isFinite(n); }))
223
+ return null;
224
+ var step = numbers.length >= 2 ? numbers[1] - numbers[0] : 1;
225
+ var linear = numbers.every(function (n, i) { return i === 0 || n - numbers[i - 1] === step; });
226
+ if (!linear)
227
+ return null;
228
+ var lastTokens = tokens[tokens.length - 1];
229
+ return {
230
+ parts: first.parts,
231
+ runs: lastTokens.runs,
232
+ runIndex: runIndex,
233
+ step: step,
234
+ last: numbers[numbers.length - 1],
235
+ width: lastTokens.runs[runIndex].length,
236
+ };
237
+ };
238
+ var renderTextNumber = function (series, n) {
239
+ var parts = series.parts, runs = series.runs, runIndex = series.runIndex, width = series.width;
240
+ var out = parts[0];
241
+ for (var r = 0; r < runs.length; r++) {
242
+ out += r === runIndex ? formatDigitRun(n, width) : runs[r];
243
+ var after = parts[r + 1];
244
+ var ordinal = r === runIndex ? after.match(ORDINAL_SUFFIX_REGEX) : null;
245
+ out += ordinal
246
+ ? "".concat(applySeedCase(ordinal[1], ordinalSuffix(n))).concat(after.slice(2))
247
+ : after;
248
+ }
249
+ return out;
250
+ };
251
+ /**
252
+ * Continue a text+number series ("Item 1" → "Item 2"; "001" → "002" keeps the
253
+ * zero padding; "1st Period" → "2nd Period" re-derives the ordinal). One seed
254
+ * steps by ±1 (by fill direction); two or more take the seeds' difference.
255
+ */
256
+ export var generateTextNumberSeries = function (values, count, descending) {
257
+ if (descending === void 0) { descending = false; }
258
+ var series = matchTextNumberSeries(values);
259
+ if (!series)
260
+ return null;
261
+ var step = values.length >= 2 ? series.step : descending ? -1 : 1;
262
+ return Array.from({ length: count }, function (_, i) {
263
+ return renderTextNumber(series, series.last + step * (i + 1));
264
+ });
265
+ };
266
+ /**
267
+ * Excel's built-in quarter list: `Q1`/`Qtr 1`/`Quarter 1` and the ordinal form
268
+ * `1st Quarter`, spelling and casing preserved, cycling mod 4. Whole-cell
269
+ * tokens only — with a year attached ("Q1 2024") the text+number rule applies.
270
+ */
271
+ var parseQuarterToken = function (value) {
272
+ var token = value.trim();
273
+ var plain = token.match(QUARTER_TOKEN_REGEX);
274
+ if (plain) {
275
+ var _a = __read(plain, 4), word_1 = _a[1], space_1 = _a[2], digit = _a[3];
276
+ return {
277
+ index: Number(digit) - 1,
278
+ render: function (quarter) { return "".concat(word_1).concat(space_1).concat(quarter); },
279
+ };
280
+ }
281
+ var ordinal = token.match(QUARTER_ORDINAL_REGEX);
282
+ if (ordinal) {
283
+ var _b = __read(ordinal, 5), digit = _b[1], suffix_1 = _b[2], space_2 = _b[3], word_2 = _b[4];
284
+ return {
285
+ index: Number(digit) - 1,
286
+ render: function (quarter) {
287
+ return "".concat(quarter).concat(applySeedCase(suffix_1, ordinalSuffix(quarter))).concat(space_2).concat(word_2);
288
+ },
289
+ };
290
+ }
291
+ return null;
292
+ };
293
+ var isQuarterToken = function (value) {
294
+ return typeof value === "string" && parseQuarterToken(value) !== null;
295
+ };
296
+ export var generateQuarterSeries = function (values, count, descending) {
297
+ if (descending === void 0) { descending = false; }
298
+ var tokens = values.map(parseQuarterToken);
299
+ if (tokens.length === 0 || tokens.some(function (t) { return t === null; }))
300
+ return null;
301
+ var quarters = tokens;
302
+ var indices = quarters.map(function (t) { return t.index; });
303
+ var step;
304
+ if (indices.length >= 2) {
305
+ step = mod(indices[1] - indices[0], QUARTERS_IN_YEAR);
306
+ var consistent = indices.every(function (idx, i) {
307
+ return i === 0 || mod(idx - indices[i - 1], QUARTERS_IN_YEAR) === step;
308
+ });
309
+ if (!consistent)
310
+ return null;
311
+ }
312
+ else {
313
+ step = descending ? -1 : 1;
314
+ }
315
+ var last = quarters[quarters.length - 1];
316
+ return Array.from({ length: count }, function (_, i) {
317
+ return last.render(mod(last.index + step * (i + 1), QUARTERS_IN_YEAR) + 1);
318
+ });
319
+ };
320
+ /**
321
+ * Locate a weekday/month seed in its locale list, case-insensitively: exact
322
+ * full name, exact 3-letter abbreviation, then the loose prefix match the
323
+ * detector accepts.
324
+ */
325
+ var resolveNameListPosition = function (value, names) {
326
+ var lower = value.trim().toLowerCase();
327
+ if (!lower)
328
+ return null;
329
+ var fullIndex = names.findIndex(function (n) { return n.toLowerCase() === lower; });
330
+ if (fullIndex !== -1)
331
+ return { index: fullIndex, abbreviated: false };
332
+ var abbrIndex = names.findIndex(function (n) { return n.toLowerCase().substring(0, 3) === lower; });
333
+ if (abbrIndex !== -1)
334
+ return { index: abbrIndex, abbreviated: true };
335
+ if (lower.length < 3)
336
+ return null;
337
+ var looseIndex = names.findIndex(function (n) {
338
+ return n.toLowerCase().startsWith(lower) ||
339
+ lower.startsWith(n.toLowerCase().substring(0, 3));
340
+ });
341
+ if (looseIndex !== -1) {
342
+ return { index: looseIndex, abbreviated: value.trim().length <= 3 };
343
+ }
344
+ return null;
345
+ };
346
+ /**
347
+ * Continue a weekday/month NAME series (cyclic). Two or more seeds fix the
348
+ * step from their list-index gap (Mon, Wed → Fri, Sun, Tue); a single seed
349
+ * steps ±1 by fill direction. Output keeps the seed's abbreviation and casing.
350
+ * Returns `[]` when the last seed is not a list member.
351
+ */
352
+ var generateNameListSeries = function (sourceValues, names, count, descending) {
353
+ if (descending === void 0) { descending = false; }
354
+ var seed = sourceValues[sourceValues.length - 1];
355
+ if (typeof seed !== "string")
356
+ return [];
357
+ var positions = sourceValues.map(function (v) {
358
+ return typeof v === "string" ? resolveNameListPosition(v, names) : null;
359
+ });
360
+ var last = positions[positions.length - 1];
361
+ if (!last)
362
+ return [];
363
+ var len = names.length;
364
+ var step;
365
+ if (positions.length >= 2 && positions.every(function (p) { return p !== null; })) {
366
+ var indices_1 = positions.map(function (p) { return p.index; });
367
+ step = mod(indices_1[1] - indices_1[0], len);
368
+ var consistent = indices_1.every(function (idx, i) { return i === 0 || mod(idx - indices_1[i - 1], len) === step; });
369
+ if (!consistent)
370
+ return [];
371
+ }
372
+ else {
373
+ step = descending ? -1 : 1;
374
+ }
375
+ var result = [];
376
+ for (var i = 0; i < count; i++) {
377
+ var canonical = names[mod(last.index + step * (i + 1), len)];
378
+ var form = last.abbreviated ? canonical.substring(0, 3) : canonical;
379
+ result.push(applySeedCase(seed, form));
380
+ }
381
+ return result;
382
+ };
34
383
  var isLikelyDateString = function (value) {
35
384
  var trimmed = value.trim();
36
385
  if (!trimmed || /^\d+$/.test(trimmed)) {
@@ -67,6 +416,75 @@ var parseDate = function (value) {
67
416
  var parsed = Date.parse(value);
68
417
  return isNaN(parsed) ? null : new Date(parsed);
69
418
  };
419
+ /**
420
+ * Calendar arithmetic for date fills runs in Excel-serial space (days since
421
+ * the 1900 epoch, wall-clock — no DST). `toDate`/`toSerial` round-trip on the
422
+ * 1900 epoch regardless of the workbook's date system, so the epoch offset
423
+ * cancels and only the caller that writes the cell needs `use1904`.
424
+ */
425
+ var dateToSerial = function (date) { return toSerial(date, false); };
426
+ var serialToDate = function (serial) { return toDate(serial, false); };
427
+ /**
428
+ * A time-of-day value (Excel serial in [0, 1) — `9:00` = 0.375) travels through
429
+ * the fill as a Date on Excel's day zero, 1899-12-31. No real date lives there
430
+ * (Excel starts at 1900-01-01), so "before 1900" ⇔ "time-only".
431
+ */
432
+ var isTimeOnlyDate = function (date) { return date.getFullYear() < 1900; };
433
+ var isDateLikeFormatType = function (type) {
434
+ return type === "DATE" || type === "DATE_TIME" || type === "TIME";
435
+ };
436
+ var HOURS_PER_DAY = 24;
437
+ var SERIAL_EPSILON = 1e-9;
438
+ var isWholeDayStep = function (serialStep) {
439
+ return Math.abs(serialStep - Math.round(serialStep)) < SERIAL_EPSILON;
440
+ };
441
+ var daysInMonth = function (year, monthIndex) {
442
+ return new Date(year, monthIndex + 1, 0).getDate();
443
+ };
444
+ var isLastDayOfMonth = function (date) {
445
+ return date.getDate() === daysInMonth(date.getFullYear(), date.getMonth());
446
+ };
447
+ /**
448
+ * Whole months between two dates (ignores day-of-month).
449
+ */
450
+ var monthsBetween = function (from, to) {
451
+ return (to.getFullYear() - from.getFullYear()) * 12 +
452
+ (to.getMonth() - from.getMonth());
453
+ };
454
+ /**
455
+ * `anchor` + `months`, Excel style: the anchor's day-of-month is kept and
456
+ * clamped to the target month's length (Jan 31 + 1 → Feb 29 in 2024, + 2 →
457
+ * Mar 31 — never JS `setMonth`'s overflow into the next month). `endOfMonth`
458
+ * forces the target month's last day (a month-end series: Jan 31, Feb 29 →
459
+ * Mar 31, Apr 30). Time-of-day rides along unchanged.
460
+ */
461
+ var addMonthsClamped = function (anchor, months, endOfMonth) {
462
+ if (endOfMonth === void 0) { endOfMonth = false; }
463
+ var total = anchor.getFullYear() * 12 + anchor.getMonth() + months;
464
+ var year = Math.floor(total / 12);
465
+ var monthIndex = total - year * 12;
466
+ var lastDay = daysInMonth(year, monthIndex);
467
+ var day = endOfMonth ? lastDay : Math.min(anchor.getDate(), lastDay);
468
+ return new Date(year, monthIndex, day, anchor.getHours(), anchor.getMinutes(), anchor.getSeconds(), anchor.getMilliseconds());
469
+ };
470
+ var isWeekend = function (date) {
471
+ return date.getDay() === 0 || date.getDay() === 6;
472
+ };
473
+ /**
474
+ * Signed count of weekdays (Mon–Fri) stepped from `from` to reach `to`.
475
+ * Weekend days are not counted, so Fri → next Fri is 5.
476
+ */
477
+ var weekdaysBetween = function (from, to) {
478
+ var dir = to.getTime() >= from.getTime() ? 1 : -1;
479
+ var cursor = new Date(from);
480
+ var steps = 0;
481
+ while (dir > 0 ? cursor.getTime() < to.getTime() : cursor.getTime() > to.getTime()) {
482
+ cursor.setDate(cursor.getDate() + dir);
483
+ if (!isWeekend(cursor))
484
+ steps += dir;
485
+ }
486
+ return steps;
487
+ };
70
488
  /**
71
489
  * Get day names array
72
490
  */
@@ -101,7 +519,7 @@ var matchCustomList = function (source, lists) {
101
519
  if (stringValues.some(function (v) { return v === null; }))
102
520
  return null;
103
521
  var lc = stringValues;
104
- var _loop_1 = function (list) {
522
+ var _loop_2 = function (list) {
105
523
  if (list.length < 2)
106
524
  return "continue";
107
525
  var listLower = list.map(function (s) { return s.toLowerCase(); });
@@ -137,7 +555,7 @@ var matchCustomList = function (source, lists) {
137
555
  try {
138
556
  for (var lists_1 = __values(lists), lists_1_1 = lists_1.next(); !lists_1_1.done; lists_1_1 = lists_1.next()) {
139
557
  var list = lists_1_1.value;
140
- var state_1 = _loop_1(list);
558
+ var state_1 = _loop_2(list);
141
559
  if (typeof state_1 === "object")
142
560
  return state_1.value;
143
561
  }
@@ -203,8 +621,59 @@ var isSimpleDecimalSeriesNumberPattern = function (pattern) {
203
621
  var normalizedPattern = (_a = pattern === null || pattern === void 0 ? void 0 : pattern.trim()) !== null && _a !== void 0 ? _a : "General";
204
622
  return SIMPLE_DECIMAL_SERIES_NUMBER_PATTERN_REGEX.test(normalizedPattern);
205
623
  };
206
- var shouldNormalizeSeriesNumberFormat = function (fillType, numberFormat, value) {
207
- if (fillType !== "FillSeries") {
624
+ var TREND_FILL_TYPES = new Set([
625
+ "LinearTrend",
626
+ "GrowthTrend",
627
+ ]);
628
+ var SIMPLE_SERIES_NUMBER_PATTERN_SHAPE_REGEX = /^(#,##0|#|0)(?:\.(0+))?$/i;
629
+ /**
630
+ * The shape of a simple NUMBER pattern — how many forced decimals it has
631
+ * and whether it groups thousands — ignoring the "#" vs "0" integer
632
+ * placeholder (the auto-stamp picks "0" only for |value| < 1).
633
+ */
634
+ var getSimpleNumberPatternShape = function (pattern) {
635
+ var _a, _b, _c;
636
+ var match = SIMPLE_SERIES_NUMBER_PATTERN_SHAPE_REGEX.exec((_a = pattern === null || pattern === void 0 ? void 0 : pattern.trim()) !== null && _a !== void 0 ? _a : "");
637
+ if (!match) {
638
+ return null;
639
+ }
640
+ return { decimals: (_c = (_b = match[2]) === null || _b === void 0 ? void 0 : _b.length) !== null && _c !== void 0 ? _c : 0, grouped: match[1].includes(",") };
641
+ };
642
+ /**
643
+ * Whether a seed's NUMBER pattern is the one the editor stamps itself when
644
+ * the value is typed (`SheetCell.setNumberValue` → `detectDecimalPattern`:
645
+ * typing 1.1 stamps one decimal, typing 1 stamps none). A pattern whose
646
+ * shape differs from what the seed's own value produces ("0.00" on the value
647
+ * 1) can only have been applied by the user or imported, and Excel carries a
648
+ * user-applied format onto the filled cells untouched.
649
+ *
650
+ * Type inference stamps a pattern only for TEXT entry — a JS number written
651
+ * by a fill (or a program) stays General (`detectValueTypeAndPattern`), so a
652
+ * generated value never picks up a decimal pattern of its own. This shape
653
+ * check is how fill tells the seed's auto-stamped pattern from an explicit
654
+ * one when deciding whether to carry it onto the target.
655
+ */
656
+ var isAutoDetectedNumberPattern = function (pattern, seedValue, locale) {
657
+ if (typeof seedValue !== "number" || !Number.isFinite(seedValue)) {
658
+ return false;
659
+ }
660
+ var shape = getSimpleNumberPatternShape(pattern);
661
+ var autoShape = getSimpleNumberPatternShape(detectDecimalPattern(seedValue, undefined, locale));
662
+ return (shape !== null &&
663
+ autoShape !== null &&
664
+ shape.decimals === autoShape.decimals &&
665
+ shape.grouped === autoShape.grouped);
666
+ };
667
+ /**
668
+ * Decide whether a generated series value should drop the pattern cloned
669
+ * from its seed so it renders under its own inferred pattern instead:
670
+ * an integer pattern on a fractional result (1, 2, 4 → 5.333… would show
671
+ * "5") or a decimal pattern on an integer result (1.1, 1.2 → 2 would show
672
+ * "2.0"). Only auto-stamped patterns are dropped — an explicit "0.00" stays
673
+ * "0.00" (3.00, 4.00), as in Excel.
674
+ */
675
+ var shouldNormalizeSeriesNumberFormat = function (fillType, numberFormat, value, seedValue, locale) {
676
+ if (fillType !== "FillSeries" && !TREND_FILL_TYPES.has(fillType)) {
208
677
  return false;
209
678
  }
210
679
  if (typeof value !== "number" || !Number.isFinite(value)) {
@@ -214,13 +683,12 @@ var shouldNormalizeSeriesNumberFormat = function (fillType, numberFormat, value)
214
683
  return false;
215
684
  }
216
685
  var pattern = numberFormat.pattern;
217
- if (isSimpleIntegerSeriesNumberPattern(pattern) && !isNearlyInteger(value)) {
218
- return true;
219
- }
220
- if (isSimpleDecimalSeriesNumberPattern(pattern) && isNearlyInteger(value)) {
221
- return true;
686
+ var integerPatternOnFraction = isSimpleIntegerSeriesNumberPattern(pattern) && !isNearlyInteger(value);
687
+ var decimalPatternOnInteger = isSimpleDecimalSeriesNumberPattern(pattern) && isNearlyInteger(value);
688
+ if (!integerPatternOnFraction && !decimalPatternOnInteger) {
689
+ return false;
222
690
  }
223
- return false;
691
+ return isAutoDetectedNumberPattern(pattern, seedValue, locale);
224
692
  };
225
693
  var getCellNumberFormat = function (sheetCell) {
226
694
  var effectiveNumberFormat = sheetCell.getNumberFormat();
@@ -235,9 +703,11 @@ var getCellNumberFormat = function (sheetCell) {
235
703
  }
236
704
  return undefined;
237
705
  };
238
- var shouldNormalizeSeriesNumberFormatForCell = function (sheetCell, fillType, value) {
706
+ var shouldNormalizeSeriesNumberFormatForCell = function (sheetCell, fillType, value, locale) {
239
707
  var numberFormat = getCellNumberFormat(sheetCell);
240
- return shouldNormalizeSeriesNumberFormat(fillType, numberFormat, value);
708
+ // `sheetCell` still holds the (wrapped) seed the target is cloned from.
709
+ var seedValue = sheetCell.getEffectiveValue();
710
+ return shouldNormalizeSeriesNumberFormat(fillType, numberFormat, value, seedValue, locale);
241
711
  };
242
712
  var clearCellNumberFormatForSeries = function (sheetCell) {
243
713
  if (sheetCell.userEnteredFormat) {
@@ -266,13 +736,19 @@ export var detectFillType = function (selectionData, locale, customAutofillLists
266
736
  if (matchCustomList(values, customAutofillLists)) {
267
737
  return "FillCustomList";
268
738
  }
739
+ // Excel's built-in quarter list (Q1 / Qtr 1 / Quarter 1 / 1st Quarter):
740
+ // a cyclic series of four, never "Q5".
741
+ if (values.every(isQuarterToken)) {
742
+ return "FillSeries";
743
+ }
269
744
  // Helper to check for day/month names
270
745
  var dayNames = getDayNames(locale);
271
746
  var monthNames = getMonthNames(locale);
272
747
  if (values.length === 1) {
273
748
  var singleValue = values[0];
274
749
  if (singleValue instanceof Date) {
275
- return "FillDays";
750
+ // A lone time (9:00) steps by the hour; a lone date by the day (Excel).
751
+ return isTimeOnlyDate(singleValue) ? "FillHours" : "FillDays";
276
752
  }
277
753
  // Single numbers → FillCopy (Excel standard: need 2+ cells for series)
278
754
  if (typeof singleValue === "number") {
@@ -322,22 +798,29 @@ export var detectFillType = function (selectionData, locale, customAutofillLists
322
798
  if (dates.length >= 2) {
323
799
  var firstDate = dates[0];
324
800
  var secondDate = dates[1];
325
- var timeDiff = secondDate.getTime() - firstDate.getTime();
326
- var dayDiff = timeDiff / (1000 * 60 * 60 * 24);
327
- if (Math.abs(dayDiff - 1) < 0.1)
801
+ // Time-of-day seeds (9:00, 9:30) continue at their own step (30 min).
802
+ if (dates.every(isTimeOnlyDate))
803
+ return "FillHours";
804
+ var serialStep = dateToSerial(secondDate) - dateToSerial(firstDate);
805
+ // A sub-day step (09:00, 15:00 → +6h) is linear on the serial — Excel only
806
+ // infers a calendar unit from whole-day differences.
807
+ if (!isWholeDayStep(serialStep))
328
808
  return "FillDays";
329
- if (Math.abs(dayDiff - 7) < 0.1)
330
- return "FillWeekdays";
331
809
  var sameDayOfMonth = firstDate.getDate() === secondDate.getDate();
332
- var sameMonth = firstDate.getMonth() === secondDate.getMonth();
333
- var monthDiff = (secondDate.getFullYear() - firstDate.getFullYear()) * 12 +
334
- (secondDate.getMonth() - firstDate.getMonth());
335
- if (sameDayOfMonth && monthDiff === 1)
810
+ var monthDiff = monthsBetween(firstDate, secondDate);
811
+ // Same day-of-month N months apart → a month series with step N (15-Jan,
812
+ // 15-Apr → 15-Jul, 15-Oct); whole years → a year series. Two month-ends
813
+ // (Jan 31, Feb 29) are a month series too, staying on the month-end.
814
+ // NOTE: Excel never infers "weekdays" — a 7-day gap is a 7-day step; Fill
815
+ // Weekdays is only ever an explicit choice.
816
+ if (sameDayOfMonth && monthDiff !== 0) {
817
+ return monthDiff % 12 === 0 ? "FillYears" : "FillMonths";
818
+ }
819
+ if (monthDiff !== 0 &&
820
+ isLastDayOfMonth(firstDate) &&
821
+ isLastDayOfMonth(secondDate)) {
336
822
  return "FillMonths";
337
- if (sameDayOfMonth &&
338
- sameMonth &&
339
- secondDate.getFullYear() - firstDate.getFullYear() === 1)
340
- return "FillYears";
823
+ }
341
824
  return "FillDays";
342
825
  }
343
826
  // Check for numeric series (after dates to avoid Date objects being treated as timestamps)
@@ -354,6 +837,26 @@ export var detectFillType = function (selectionData, locale, customAutofillLists
354
837
  if (isLinearSeries && diff_1 !== 0) {
355
838
  return "FillSeries";
356
839
  }
840
+ // Excel fits a least-squares line through 3+ bare numbers whose step is
841
+ // not constant (1, 2, 4 → 5.333…, 6.833…) instead of repeating them.
842
+ // Only bare numbers qualify: "Item 1", "Item 2", "Item 4" also extract
843
+ // numbers but stay a text pattern.
844
+ if (!isLinearSeries &&
845
+ numbers.length >= 3 &&
846
+ nonDateValues.every(function (v) { return typeof v === "number"; })) {
847
+ return "LinearTrend";
848
+ }
849
+ }
850
+ // Text+number seeds aligned on one varying digit run ("Text1Text2",
851
+ // "Text1Text3" → the trailing run steps) are a series even when the
852
+ // first number in each string does not move.
853
+ if (values.every(function (v) { return typeof v === "string"; }) &&
854
+ !values.every(function (v) { return BARE_DIGITS_REGEX.test(v); }) &&
855
+ !values.some(function (v) { return isLikelyDateString(v); })) {
856
+ var textSeries = matchTextNumberSeries(values);
857
+ if (textSeries && textSeries.step !== 0) {
858
+ return "FillSeries";
859
+ }
357
860
  }
358
861
  // Check for text patterns (days/months)
359
862
  var textValues = values.map(function (v) { return String(v).toLowerCase(); });
@@ -379,8 +882,16 @@ export var detectFillType = function (selectionData, locale, customAutofillLists
379
882
  /**
380
883
  * Generate fill values based on type and pattern
381
884
  */
382
- export var generateFillValues = function (sourceValues, fillType, count, locale, customAutofillLists) {
885
+ export var generateFillValues = function (sourceValues, fillType, count, locale, customAutofillLists,
886
+ /**
887
+ * The fill walks away from the selection's start (fill up / fill left), so a
888
+ * single seed continues backwards: "Item 5" → "Item 4", Wednesday → Tuesday.
889
+ * Multi-seed lines already encode the direction in their reversed order.
890
+ */
891
+ descending) {
892
+ if (descending === void 0) { descending = false; }
383
893
  var result = [];
894
+ var unitStep = descending ? -1 : 1;
384
895
  switch (fillType) {
385
896
  case "FillCustomList": {
386
897
  var match = matchCustomList(sourceValues, customAutofillLists);
@@ -392,16 +903,20 @@ export var generateFillValues = function (sourceValues, fillType, count, locale,
392
903
  }
393
904
  break;
394
905
  }
395
- var list = match.list, startIndex = match.startIndex, ascending = match.ascending;
906
+ var list = match.list, startIndex = match.startIndex;
907
+ // A single seed has no order of its own — the fill direction decides.
908
+ var ascending = sourceValues.length >= 2 ? match.ascending : !descending;
396
909
  var len = list.length;
397
- // Continue from where the source slice ended.
910
+ // Continue from where the source slice ended; the output follows the
911
+ // seed's casing pattern like the built-in lists ("low" → "medium").
912
+ var seed = String(sourceValues[sourceValues.length - 1]);
398
913
  var nextOffset = ascending
399
914
  ? startIndex + sourceValues.length
400
915
  : startIndex - sourceValues.length;
401
916
  for (var i = 0; i < count; i++) {
402
917
  var step = ascending ? i : -i;
403
918
  var idx = (((nextOffset + step) % len) + len) % len;
404
- result.push(list[idx]);
919
+ result.push(applySeedCase(seed, list[idx]));
405
920
  }
406
921
  break;
407
922
  }
@@ -412,28 +927,23 @@ export var generateFillValues = function (sourceValues, fillType, count, locale,
412
927
  break;
413
928
  }
414
929
  case "FillSeries": {
415
- var textPatterns = sourceValues
416
- .map(function (value) {
417
- return typeof value === "string" ? extractTextNumberPattern(value) : null;
418
- })
419
- .filter(function (pattern) { return pattern !== null; });
420
- if (textPatterns.length === sourceValues.length &&
421
- textPatterns.length > 0) {
422
- var _a = __read(textPatterns), firstPattern_1 = _a[0], restPatterns = _a.slice(1);
423
- var hasConsistentAffixes = restPatterns.every(function (_a) {
424
- var prefix = _a.prefix, suffix = _a.suffix;
425
- return prefix === firstPattern_1.prefix && suffix === firstPattern_1.suffix;
426
- });
427
- if (hasConsistentAffixes) {
428
- var numbers_1 = textPatterns.map(function (pattern) { return pattern.number; });
429
- var diff = numbers_1.length >= 2
430
- ? normalizeSeriesNumber(numbers_1[1] - numbers_1[0])
431
- : 1;
432
- var lastValue = normalizeSeriesNumber(numbers_1[numbers_1.length - 1]);
433
- for (var i = 0; i < count; i++) {
434
- var nextNumber = normalizeSeriesNumber(lastValue + diff * (i + 1));
435
- result.push("".concat(firstPattern_1.prefix).concat(String(nextNumber)).concat(firstPattern_1.suffix));
436
- }
930
+ var allStrings = sourceValues.every(function (value) { return typeof value === "string"; });
931
+ // Built-in quarter list first: Q4 wraps to Q1 instead of stepping to Q5.
932
+ if (allStrings) {
933
+ var quarters = generateQuarterSeries(sourceValues, count, descending);
934
+ if (quarters) {
935
+ result.push.apply(result, __spreadArray([], __read(quarters), false));
936
+ break;
937
+ }
938
+ }
939
+ // Text+number series ("Item 1" → "Item 2"). Bare digit strings stay on
940
+ // the numeric path below so "5" → 6, 7, 8 (numbers) as before.
941
+ if (allStrings &&
942
+ sourceValues.length > 0 &&
943
+ !sourceValues.every(function (value) { return BARE_DIGITS_REGEX.test(value); })) {
944
+ var textSeries = generateTextNumberSeries(sourceValues, count, descending);
945
+ if (textSeries) {
946
+ result.push.apply(result, __spreadArray([], __read(textSeries), false));
437
947
  break;
438
948
  }
439
949
  }
@@ -441,18 +951,38 @@ export var generateFillValues = function (sourceValues, fillType, count, locale,
441
951
  .map(function (v) { return (typeof v === "number" ? v : extractNumber(String(v))); })
442
952
  .filter(function (n) { return n !== null; });
443
953
  if (numbers.length >= 1) {
444
- // Default increment is 1 for single values, or calculated diff for 2+ values
954
+ // Default increment is 1 for single values (−1 when filling up /
955
+ // left), or calculated diff for 2+ values
445
956
  var diff = numbers.length >= 2
446
957
  ? normalizeSeriesNumber(numbers[1] - numbers[0])
447
- : 1;
958
+ : descending
959
+ ? -1
960
+ : 1;
448
961
  var lastValue = normalizeSeriesNumber(numbers[numbers.length - 1]);
449
962
  for (var i = 0; i < count; i++) {
450
963
  result.push(normalizeSeriesNumber(lastValue + diff * (i + 1)));
451
964
  }
965
+ break;
452
966
  }
453
- else {
454
- result.push.apply(result, __spreadArray([], __read(Array(count).fill(sourceValues[sourceValues.length - 1])), false));
967
+ // No numeric run at all: an explicit "Fill Series" on a list item walks
968
+ // the list (Monday → Tuesday…), like Excel's AutoFill Options menu.
969
+ if (allStrings) {
970
+ var days = generateNameListSeries(sourceValues, getDayNames(locale), count, descending);
971
+ if (days.length) {
972
+ result.push.apply(result, __spreadArray([], __read(days), false));
973
+ break;
974
+ }
975
+ var months = generateNameListSeries(sourceValues, getMonthNames(locale), count, descending);
976
+ if (months.length) {
977
+ result.push.apply(result, __spreadArray([], __read(months), false));
978
+ break;
979
+ }
980
+ if (matchCustomList(sourceValues, customAutofillLists)) {
981
+ result.push.apply(result, __spreadArray([], __read(generateFillValues(sourceValues, "FillCustomList", count, locale, customAutofillLists, descending)), false));
982
+ break;
983
+ }
455
984
  }
985
+ result.push.apply(result, __spreadArray([], __read(Array(count).fill(sourceValues[sourceValues.length - 1])), false));
456
986
  break;
457
987
  }
458
988
  case "FillDays": {
@@ -460,58 +990,52 @@ export var generateFillValues = function (sourceValues, fillType, count, locale,
460
990
  .map(function (value) { return parseDate(value); })
461
991
  .filter(function (date) { return date !== null; });
462
992
  if (parsedDates.length) {
993
+ // Step in serial space from the LAST two seeds so a sub-day step
994
+ // (09:00, 15:00 → 21:00, 03:00) and the time-of-day both survive;
995
+ // a lone date steps one day (backwards when filling up / left).
463
996
  var lastDate = parsedDates[parsedDates.length - 1];
464
997
  var previousDate = parsedDates.length >= 2 ? parsedDates[parsedDates.length - 2] : null;
465
- var dayMs = 1000 * 60 * 60 * 24;
466
- var dayIncrement = 1;
998
+ var lastSerial = dateToSerial(lastDate);
999
+ var step = unitStep;
467
1000
  if (previousDate) {
468
- var diff = Math.round((lastDate.getTime() - previousDate.getTime()) / dayMs);
469
- if (diff !== 0) {
470
- dayIncrement = diff;
1001
+ var diff = lastSerial - dateToSerial(previousDate);
1002
+ if (Math.abs(diff) > SERIAL_EPSILON) {
1003
+ step = diff;
471
1004
  }
472
1005
  }
473
- var currentDate = new Date(lastDate);
474
1006
  for (var i = 0; i < count; i++) {
475
- currentDate.setDate(currentDate.getDate() + dayIncrement);
476
- result.push(currentDate.toLocaleDateString(locale));
1007
+ result.push(serialToDate(lastSerial + step * (i + 1)));
477
1008
  }
478
1009
  }
479
1010
  else {
480
- var lastDayValue = sourceValues[sourceValues.length - 1];
481
- if (typeof lastDayValue === "string") {
482
- // Handle day names (like "Mon", "Tue", "Wed")
483
- var allDayNames = getDayNames(locale);
484
- var allDayAbbrevs = allDayNames.map(function (d) { return d.substring(0, 3); });
485
- // Find which day we're at
486
- var lastDayLower_1 = lastDayValue.toLowerCase();
487
- var dayIndex = allDayNames.findIndex(function (d) {
488
- return d.toLowerCase().includes(lastDayLower_1) ||
489
- lastDayLower_1.includes(d.toLowerCase());
490
- });
491
- if (dayIndex !== -1) {
492
- // Determine if we're using full names or abbreviations
493
- var isAbbreviated = lastDayValue.length <= 3;
494
- for (var i = 0; i < count; i++) {
495
- var nextDayIndex = (dayIndex + i + 1) % 7;
496
- var dayName = isAbbreviated
497
- ? allDayAbbrevs[nextDayIndex]
498
- : allDayNames[nextDayIndex];
499
- result.push(dayName);
500
- }
501
- }
502
- }
1011
+ // Day names ("Mon", "Tue", "Wed"): seed gap as the step, seed casing kept.
1012
+ result.push.apply(result, __spreadArray([], __read(generateNameListSeries(sourceValues, getDayNames(locale), count, descending)), false));
503
1013
  }
504
1014
  break;
505
1015
  }
506
1016
  case "FillWeekdays": {
507
- var lastWeekday = parseDate(sourceValues[sourceValues.length - 1]);
1017
+ // Explicit Fill Weekdays: step Mon–Fri only, from a weekend seed too
1018
+ // (Sat 1/6 → Mon 1/8). Two seeds set the step in weekdays (Fri, Fri →
1019
+ // 5); a lone seed steps one weekday (backwards when filling up / left).
1020
+ var weekdayDates = sourceValues
1021
+ .map(function (value) { return parseDate(value); })
1022
+ .filter(function (date) { return date !== null; });
1023
+ var lastWeekday = weekdayDates[weekdayDates.length - 1];
508
1024
  if (lastWeekday) {
1025
+ var previous = weekdayDates.length >= 2
1026
+ ? weekdayDates[weekdayDates.length - 2]
1027
+ : null;
1028
+ var seedStep = previous ? weekdaysBetween(previous, lastWeekday) : 0;
1029
+ var step = seedStep === 0 ? unitStep : seedStep;
1030
+ var dir = step > 0 ? 1 : -1;
509
1031
  var currentDate = new Date(lastWeekday);
510
1032
  for (var i = 0; i < count; i++) {
511
- do {
512
- currentDate.setDate(currentDate.getDate() + 1);
513
- } while (currentDate.getDay() === 0 || currentDate.getDay() === 6); // Skip weekends
514
- result.push(new Date(currentDate).toLocaleDateString(locale));
1033
+ for (var taken = 0; taken < Math.abs(step);) {
1034
+ currentDate.setDate(currentDate.getDate() + dir);
1035
+ if (!isWeekend(currentDate))
1036
+ taken += 1;
1037
+ }
1038
+ result.push(new Date(currentDate));
515
1039
  }
516
1040
  }
517
1041
  break;
@@ -521,44 +1045,73 @@ export var generateFillValues = function (sourceValues, fillType, count, locale,
521
1045
  var lastValue = sourceValues[sourceValues.length - 1];
522
1046
  var lastMonth = parseDate(lastValue);
523
1047
  if (lastMonth) {
524
- // Handle actual dates
1048
+ // Anchor on the last seed and add whole months, clamping the day to
1049
+ // the target month (Jan 31 → Feb 29, Mar 31, Apr 30). Two seeds set
1050
+ // the step (15-Jan, 15-Apr → 3 months); two month-ends (Jan 31, Feb
1051
+ // 29) keep the series on the month-end. A lone seed steps one month
1052
+ // (backwards when filling up / left).
1053
+ var monthDates = sourceValues
1054
+ .map(function (value) { return parseDate(value); })
1055
+ .filter(function (date) { return date !== null; });
1056
+ var previous = monthDates.length >= 2 ? monthDates[monthDates.length - 2] : null;
1057
+ var seedStep = previous ? monthsBetween(previous, lastMonth) : 0;
1058
+ var step = seedStep === 0 ? unitStep : seedStep;
1059
+ var endOfMonth = monthDates.length >= 2 && monthDates.every(isLastDayOfMonth);
525
1060
  for (var i = 0; i < count; i++) {
526
- var newDate = new Date(lastMonth);
527
- newDate.setMonth(lastMonth.getMonth() + i + 1);
528
- result.push(newDate.toLocaleDateString(locale));
1061
+ result.push(addMonthsClamped(lastMonth, step * (i + 1), endOfMonth));
529
1062
  }
530
1063
  }
531
1064
  else if (typeof lastValue === "string") {
532
- // Handle month names (like "Jan", "Feb", "Mar")
533
- var allMonthNames = getMonthNames(locale);
534
- var allMonthAbbrevs = allMonthNames.map(function (m) { return m.substring(0, 3); });
535
- // Find which month we're at
536
- var lastValueLower_1 = lastValue.toLowerCase();
537
- var monthIndex = allMonthNames.findIndex(function (m) {
538
- return m.toLowerCase().includes(lastValueLower_1) ||
539
- lastValueLower_1.includes(m.toLowerCase());
540
- });
541
- if (monthIndex !== -1) {
542
- // Determine if we're using full names or abbreviations
543
- var isAbbreviated = lastValue.length <= 3;
544
- for (var i = 0; i < count; i++) {
545
- var nextMonthIndex = (monthIndex + i + 1) % 12;
546
- var monthName = isAbbreviated
547
- ? allMonthAbbrevs[nextMonthIndex]
548
- : allMonthNames[nextMonthIndex];
549
- result.push(monthName);
550
- }
551
- }
1065
+ // Month names ("Jan", "Feb", "Mar"): seed gap as the step, seed casing kept.
1066
+ result.push.apply(result, __spreadArray([], __read(generateNameListSeries(sourceValues, getMonthNames(locale), count, descending)), false));
552
1067
  }
553
1068
  break;
554
1069
  }
555
1070
  case "FillYears": {
556
- var lastYear = parseDate(sourceValues[sourceValues.length - 1]);
1071
+ // Anchor on the last seed and add whole years, clamping Feb 29 to Feb 28
1072
+ // in non-leap years (2/29/2024 → 2/28/2025 … 2/29/2028 — the day comes
1073
+ // back, it is never lost). Two seeds set the step; a lone seed steps one
1074
+ // year (backwards when filling up / left).
1075
+ var yearDates = sourceValues
1076
+ .map(function (value) { return parseDate(value); })
1077
+ .filter(function (date) { return date !== null; });
1078
+ var lastYear = yearDates[yearDates.length - 1];
557
1079
  if (lastYear) {
1080
+ var previous = yearDates.length >= 2 ? yearDates[yearDates.length - 2] : null;
1081
+ var seedStep = previous
1082
+ ? lastYear.getFullYear() - previous.getFullYear()
1083
+ : 0;
1084
+ var step = seedStep === 0 ? unitStep : seedStep;
1085
+ for (var i = 0; i < count; i++) {
1086
+ result.push(addMonthsClamped(lastYear, 12 * step * (i + 1)));
1087
+ }
1088
+ }
1089
+ break;
1090
+ }
1091
+ case "FillHours": {
1092
+ // Time-of-day series on the raw serial: a lone time steps one hour
1093
+ // (9:00 → 10:00, 11:00; backwards when filling up / left), two set the
1094
+ // step (9:00, 9:30 → 10:00, 10:30). Emits serials, not Dates, so the
1095
+ // series can pass midnight (22:00 → 23:00, 0:00 = serial 1.0, 1:00)
1096
+ // while the cell keeps the seed's time format.
1097
+ var timeSerials = sourceValues
1098
+ .map(function (value) {
1099
+ return value instanceof Date
1100
+ ? dateToSerial(value)
1101
+ : typeof value === "number"
1102
+ ? value
1103
+ : null;
1104
+ })
1105
+ .filter(function (serial) { return serial !== null; });
1106
+ if (timeSerials.length) {
1107
+ var lastSerial = timeSerials[timeSerials.length - 1];
1108
+ var previous = timeSerials.length >= 2 ? timeSerials[timeSerials.length - 2] : null;
1109
+ var seedStep = previous === null ? 0 : lastSerial - previous;
1110
+ var step = Math.abs(seedStep) > SERIAL_EPSILON
1111
+ ? seedStep
1112
+ : unitStep / HOURS_PER_DAY;
558
1113
  for (var i = 0; i < count; i++) {
559
- var newDate = new Date(lastYear);
560
- newDate.setFullYear(lastYear.getFullYear() + i + 1);
561
- result.push(newDate.toLocaleDateString(locale));
1114
+ result.push(normalizeSeriesNumber(lastSerial + step * (i + 1)));
562
1115
  }
563
1116
  }
564
1117
  break;
@@ -578,7 +1131,7 @@ export var generateFillValues = function (sourceValues, fillType, count, locale,
578
1131
  var intercept = (sumY - slope * sumX) / n;
579
1132
  for (var i = 0; i < count; i++) {
580
1133
  var x = numValues.length + i;
581
- result.push(slope * x + intercept);
1134
+ result.push(normalizeSeriesNumber(slope * x + intercept));
582
1135
  }
583
1136
  }
584
1137
  break;
@@ -602,25 +1155,12 @@ export var generateFillValues = function (sourceValues, fillType, count, locale,
602
1155
  }
603
1156
  break;
604
1157
  }
605
- case "FillValues": {
606
- // Similar to FillSeries but preserves text formatting
607
- for (var i = 0; i < count; i++) {
608
- var sourceIndex = i % sourceValues.length;
609
- var value = sourceValues[sourceIndex];
610
- if (typeof value === "string") {
611
- var num = extractNumber(value);
612
- if (num !== null && sourceValues.length >= 2) {
613
- var nextNum = extractNumber(String(sourceValues[(sourceIndex + 1) % sourceValues.length]));
614
- if (nextNum !== null) {
615
- var diff = nextNum - num;
616
- var cycles = Math.floor(i / sourceValues.length);
617
- var newNum = num + diff * (cycles + 1);
618
- value = value.replace(String(num), String(newNum));
619
- }
620
- }
621
- }
622
- result.push(value);
623
- }
1158
+ case "FillValues":
1159
+ case "FillWithoutFormatting": {
1160
+ // Values-only is a payload modifier, not a series mode: the values extrapolate exactly as a
1161
+ // plain drag would (1,2 → 3,4,5,6); only the formatting is withheld by the caller.
1162
+ var detected = detectFillType([sourceValues], locale, customAutofillLists);
1163
+ result.push.apply(result, __spreadArray([], __read(generateFillValues(sourceValues, detected, count, locale, customAutofillLists, descending)), false));
624
1164
  break;
625
1165
  }
626
1166
  case "FillFormats": {
@@ -641,49 +1181,189 @@ export var generateFillValues = function (sourceValues, fillType, count, locale,
641
1181
  }
642
1182
  return result;
643
1183
  };
1184
+ var hasExcelTextPrefix = function (value) {
1185
+ return typeof value === "string" && value.startsWith("'") && value.length > 1;
1186
+ };
1187
+ /**
1188
+ * Classify one seed the way Excel partitions a mixed selection: by type, and
1189
+ * for text+number by its non-digit skeleton, so "Item 1" and "Box 1" run as
1190
+ * two independent series.
1191
+ */
1192
+ var classifyFillValue = function (value, dayNames, monthNames, customAutofillLists) {
1193
+ if (value == null || value === "")
1194
+ return { kind: "blank", key: "blank" };
1195
+ if (value instanceof Date)
1196
+ return { kind: "date", key: "date" };
1197
+ if (typeof value === "number")
1198
+ return { kind: "number", key: "number" };
1199
+ if (typeof value !== "string")
1200
+ return { kind: "text", key: "text" };
1201
+ if (matchCustomList([value], customAutofillLists)) {
1202
+ return { kind: "custom", key: "custom" };
1203
+ }
1204
+ if (parseQuarterToken(value))
1205
+ return { kind: "quarter", key: "quarter" };
1206
+ if (isLikelyDateString(value) && parseDate(value)) {
1207
+ return { kind: "date", key: "date" };
1208
+ }
1209
+ if (/\d/.test(value)) {
1210
+ var parts = tokenizeDigitRuns(value).parts;
1211
+ return { kind: "textnum", key: "textnum:".concat(parts.join(" ")) };
1212
+ }
1213
+ if (resolveNameListPosition(value, dayNames)) {
1214
+ return { kind: "day", key: "day" };
1215
+ }
1216
+ if (resolveNameListPosition(value, monthNames)) {
1217
+ return { kind: "month", key: "month" };
1218
+ }
1219
+ return { kind: "text", key: "text" };
1220
+ };
1221
+ var repeatValues = function (values, count) {
1222
+ return Array.from({ length: count }, function (_, i) { return values[i % values.length]; });
1223
+ };
1224
+ /**
1225
+ * Extend one class group. `mixedLine` is true when the group shares its line
1226
+ * with other classes or blanks — there a lone number steps (1, x → 2, x, 3, x)
1227
+ * where a lone number on its own copies (Excel needs two cells for a series).
1228
+ */
1229
+ var generateGroupValues = function (cls, values, count, mixedLine, _a) {
1230
+ var _b, _c;
1231
+ var locale = _a.locale, customAutofillLists = _a.customAutofillLists, descending = _a.descending;
1232
+ var fallback = function () { return repeatValues(values, count); };
1233
+ switch (cls.kind) {
1234
+ case "blank":
1235
+ return Array(count).fill(null);
1236
+ case "text":
1237
+ return fallback();
1238
+ case "number": {
1239
+ if (values.length === 1) {
1240
+ if (!mixedLine)
1241
+ return fallback();
1242
+ var step_1 = descending ? -1 : 1;
1243
+ var last_1 = normalizeSeriesNumber(values[0]);
1244
+ return Array.from({ length: count }, function (_, i) {
1245
+ return normalizeSeriesNumber(last_1 + step_1 * (i + 1));
1246
+ });
1247
+ }
1248
+ var type = detectFillType([values], locale, customAutofillLists);
1249
+ var out = generateFillValues(values, type, count, locale, customAutofillLists, descending);
1250
+ return out.length === count ? out : fallback();
1251
+ }
1252
+ case "date": {
1253
+ // `detectFillType` picks the calendar unit (a lone time → FillHours,
1254
+ // a lone date → FillDays, month-ends → FillMonths, …).
1255
+ var type = detectFillType([values], locale, customAutofillLists);
1256
+ var out = generateFillValues(values, type, count, locale, customAutofillLists, descending);
1257
+ return out.length === count ? out : fallback();
1258
+ }
1259
+ case "quarter":
1260
+ return (_b = generateQuarterSeries(values, count, descending)) !== null && _b !== void 0 ? _b : fallback();
1261
+ case "textnum":
1262
+ return (_c = generateTextNumberSeries(values, count, descending)) !== null && _c !== void 0 ? _c : fallback();
1263
+ case "day": {
1264
+ var out = generateNameListSeries(values, getDayNames(locale), count, descending);
1265
+ return out.length === count ? out : fallback();
1266
+ }
1267
+ case "month": {
1268
+ var out = generateNameListSeries(values, getMonthNames(locale), count, descending);
1269
+ return out.length === count ? out : fallback();
1270
+ }
1271
+ case "custom": {
1272
+ var out = generateFillValues(values, "FillCustomList", count, locale, customAutofillLists, descending);
1273
+ return out.length === count ? out : fallback();
1274
+ }
1275
+ }
1276
+ };
1277
+ /**
1278
+ * Compose a fill line from its seeds. A homogeneous line is one series; a
1279
+ * mixed line (text1, textA / 1, blank, 2, blank / Monday, 1) is partitioned by
1280
+ * class, each group extended on its own, and the results interleaved back
1281
+ * onto the source pattern so blanks stay blank and every slot continues its
1282
+ * own sub-series — Excel's "text1, textA → text2, textA, text3, textA".
1283
+ */
1284
+ var generateLineSeries = function (sourceValues, count, options) {
1285
+ var e_2, _a;
1286
+ var _b, _c;
1287
+ var locale = options.locale, customAutofillLists = options.customAutofillLists;
1288
+ var len = sourceValues.length;
1289
+ if (len === 0 || count <= 0)
1290
+ return { fillType: "FillDefault", values: [] };
1291
+ var dayNames = getDayNames(locale);
1292
+ var monthNames = getMonthNames(locale);
1293
+ var classes = sourceValues.map(function (v) {
1294
+ return classifyFillValue(v, dayNames, monthNames, customAutofillLists);
1295
+ });
1296
+ var groups = new Map();
1297
+ classes.forEach(function (cls, position) {
1298
+ var _a;
1299
+ var group = (_a = groups.get(cls.key)) !== null && _a !== void 0 ? _a : {
1300
+ cls: cls,
1301
+ values: [],
1302
+ generated: [],
1303
+ cursor: 0,
1304
+ };
1305
+ group.values.push(sourceValues[position]);
1306
+ groups.set(cls.key, group);
1307
+ });
1308
+ var nonBlankGroups = __spreadArray([], __read(groups.values()), false).filter(function (g) { return g.cls.kind !== "blank"; });
1309
+ if (nonBlankGroups.length === 0) {
1310
+ return { fillType: "FillDefault", values: Array(count).fill(null) };
1311
+ }
1312
+ var mixedLine = groups.size > 1;
1313
+ var needed = new Map();
1314
+ for (var i = 0; i < count; i++) {
1315
+ var key = classes[i % len].key;
1316
+ needed.set(key, ((_b = needed.get(key)) !== null && _b !== void 0 ? _b : 0) + 1);
1317
+ }
1318
+ try {
1319
+ for (var groups_1 = __values(groups), groups_1_1 = groups_1.next(); !groups_1_1.done; groups_1_1 = groups_1.next()) {
1320
+ var _d = __read(groups_1_1.value, 2), key = _d[0], group = _d[1];
1321
+ group.generated = generateGroupValues(group.cls, group.values, (_c = needed.get(key)) !== null && _c !== void 0 ? _c : 0, mixedLine, options);
1322
+ }
1323
+ }
1324
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
1325
+ finally {
1326
+ try {
1327
+ if (groups_1_1 && !groups_1_1.done && (_a = groups_1.return)) _a.call(groups_1);
1328
+ }
1329
+ finally { if (e_2) throw e_2.error; }
1330
+ }
1331
+ var values = [];
1332
+ for (var i = 0; i < count; i++) {
1333
+ var group = groups.get(classes[i % len].key);
1334
+ values.push(group.generated[group.cursor++]);
1335
+ }
1336
+ // The number group's own detection drives number-format normalisation.
1337
+ var numberGroup = groups.get("number");
1338
+ var lineFillType;
1339
+ if (numberGroup) {
1340
+ lineFillType =
1341
+ numberGroup.values.length >= 2
1342
+ ? detectFillType([numberGroup.values], locale, customAutofillLists)
1343
+ : mixedLine
1344
+ ? "FillSeries"
1345
+ : "FillCopy";
1346
+ }
1347
+ else if (mixedLine || nonBlankGroups.some(function (g) { return g.cls.kind !== "text"; })) {
1348
+ lineFillType = "FillSeries";
1349
+ }
1350
+ else {
1351
+ lineFillType = "FillDefault";
1352
+ }
1353
+ return { fillType: lineFillType, values: values };
1354
+ };
644
1355
  /**
645
1356
  * Get auto fill values
646
1357
  * @returns T[][]
647
1358
  */
648
1359
  export var getAutoFillValues = function (_a) { return __awaiter(void 0, [_a], void 0, function (_b) {
649
- var sheetCell, activeSheetName, textCell, getTextInfo, buildTextCell, getFillSourceValue, selectionData, rowIndex, rowData, columnIndex, cellData, selectionRows, selectionCols, fillValues, fillCount, columnIndex, sourceValues, sourceCells, rowIndex, cellValue, textInfo, columnData, actualFillType, newValues, i, targetRowIndex, targetColumnIndex, sourceRowIndex, sourceCellData, shouldNormalizeSeriesNumberFormat_1, fillCount, columnIndex, sourceValues, sourceCells, rowIndex, cellValue, textInfo, columnData, actualFillType, newValues, i, targetRowIndex, targetColumnIndex, sourceRowIndex, sourceCellData, shouldNormalizeSeriesNumberFormat_2, fillCount, rowIndex, sourceValues, sourceCells, textSourceCellData, columnIndex, cellValue, textInfo, rowData, actualFillType, newValues, targetRowIndex, i, targetColumnIndex, sourceColumnIndex, sourceCellData, shouldNormalizeSeriesNumberFormat_3, fillCount, rowIndex, sourceValues, sourceCells, textSourceCellData, columnIndex, cellValue, textInfo, rowData, actualFillType, newValues, targetRowIndex, i, targetColumnIndex, sourceColumnIndex, sourceCellData, shouldNormalizeSeriesNumberFormat_4;
650
- var _c, _d, _e, _f;
651
- var sheetId = _b.sheetId, direction = _b.direction, selection = _b.selection, fillBounds = _b.fillBounds, getCellData = _b.getCellData, getEffectiveValue = _b.getEffectiveValue, getFormattedValue = _b.getFormattedValue, getSheetName = _b.getSheetName, getDataValidationById = _b.getDataValidationById, cellXfsRegistry = _b.cellXfsRegistry, locale = _b.locale, fillType = _b.fillType, customAutofillLists = _b.customAutofillLists;
652
- return __generator(this, function (_g) {
1360
+ var sheetCell, activeSheetName, buildTextCell, readFillSource, toCellValue, writeFillValue, fillValues, vertical, descending, range, block, stride, lineStride, lineCount, seedCount, span, fillCount, snapped, seedCoord, targetCoord, originRow, originColumn, place, lines, line, sourceValues, sourceMeta, k, _c, rowIndex, columnIndex, _d, value, meta, fillType, isFormatsOnly, isValuesOnly, seriesFillType, formatsOnlyCell, finalizeCell, computeLine, coveredCell, placeBlock, markUntouched, line, _e, sourceValues, sourceMeta, _f, lineFillType, newValues, seeds, untouchedTail, seeds_1, seeds_1_1, seed, from, sourceCellData, i, target, k, from, sourceCellData;
1361
+ var e_3, _g;
1362
+ var _h;
1363
+ var sheetId = _b.sheetId, direction = _b.direction, selection = _b.selection, fillBounds = _b.fillBounds, getCellData = _b.getCellData, getEffectiveValue = _b.getEffectiveValue, getFormattedValue = _b.getFormattedValue, getSheetName = _b.getSheetName, getDataValidationById = _b.getDataValidationById, cellXfsRegistry = _b.cellXfsRegistry, locale = _b.locale, requestedFillType = _b.fillType, modifier = _b.modifier, series = _b.series, customAutofillLists = _b.customAutofillLists, merges = _b.merges;
1364
+ return __generator(this, function (_j) {
653
1365
  sheetCell = new SheetCell();
654
1366
  activeSheetName = getSheetName(sheetId);
655
- textCell = new SheetCell();
656
- getTextInfo = function (cells) {
657
- var e_2, _a;
658
- var _b, _c, _d, _e, _f;
659
- try {
660
- for (var cells_1 = __values(cells), cells_1_1 = cells_1.next(); !cells_1_1.done; cells_1_1 = cells_1.next()) {
661
- var _g = cells_1_1.value, rowIndex = _g.rowIndex, columnIndex = _g.columnIndex;
662
- var cellData = getCellData(sheetId, rowIndex, columnIndex);
663
- textCell.assign(sheetId, { rowIndex: rowIndex, columnIndex: columnIndex }, cellData, {
664
- locale: locale,
665
- cellXfsRegistry: cellXfsRegistry,
666
- getDataValidationById: getDataValidationById,
667
- });
668
- if (((_b = textCell.getNumberFormat()) === null || _b === void 0 ? void 0 : _b.type) === "TEXT") {
669
- var fallback = getFillSourceValue(sheetId, rowIndex, columnIndex);
670
- return {
671
- isText: true,
672
- textValue: (_d = (_c = textCell.getUserEnteredValue()) !== null && _c !== void 0 ? _c : textCell.getEffectiveValue()) !== null && _d !== void 0 ? _d : String(fallback !== null && fallback !== void 0 ? fallback : ""),
673
- pattern: (_f = (_e = textCell.getNumberFormat()) === null || _e === void 0 ? void 0 : _e.pattern) !== null && _f !== void 0 ? _f : "General",
674
- };
675
- }
676
- }
677
- }
678
- catch (e_2_1) { e_2 = { error: e_2_1 }; }
679
- finally {
680
- try {
681
- if (cells_1_1 && !cells_1_1.done && (_a = cells_1.return)) _a.call(cells_1);
682
- }
683
- finally { if (e_2) throw e_2.error; }
684
- }
685
- return { isText: false, textValue: "", pattern: "General" };
686
- };
687
1367
  buildTextCell = function (sourceCellData, value, pattern) {
688
1368
  var newCell = new SheetCell();
689
1369
  newCell.assign(sheetId, { rowIndex: selection.range.startRowIndex, columnIndex: 0 }, sourceCellData, { locale: locale, cellXfsRegistry: cellXfsRegistry, getDataValidationById: getDataValidationById });
@@ -694,7 +1374,7 @@ export var getAutoFillValues = function (_a) { return __awaiter(void 0, [_a], vo
694
1374
  newCell.setUserEnteredValue(value);
695
1375
  return newCell.getCellData();
696
1376
  };
697
- getFillSourceValue = function (sheetId, rowIndex, columnIndex) {
1377
+ readFillSource = function (rowIndex, columnIndex) {
698
1378
  var _a;
699
1379
  var cellData = getCellData(sheetId, rowIndex, columnIndex);
700
1380
  sheetCell.assign(sheetId, { rowIndex: rowIndex, columnIndex: columnIndex }, cellData, {
@@ -702,256 +1382,344 @@ export var getAutoFillValues = function (_a) { return __awaiter(void 0, [_a], vo
702
1382
  cellXfsRegistry: cellXfsRegistry,
703
1383
  getDataValidationById: getDataValidationById,
704
1384
  });
705
- var numFmtType = (_a = sheetCell.getNumberFormat()) === null || _a === void 0 ? void 0 : _a.type;
706
- var isDateTime = numFmtType === "DATE" || numFmtType === "DATE_TIME";
707
- var isText = numFmtType === "TEXT";
1385
+ var numberFormat = sheetCell.getNumberFormat();
1386
+ var numFmtType = numberFormat === null || numberFormat === void 0 ? void 0 : numberFormat.type;
1387
+ var isDateTime = isDateLikeFormatType(numFmtType);
1388
+ var textFormat = numFmtType === "TEXT";
1389
+ var userEntered = sheetCell.getUserEnteredValue();
1390
+ var quotePrefixed = !sheetCell.isFormula() && hasExcelTextPrefix(userEntered);
1391
+ var value;
708
1392
  if (isDateTime) {
709
1393
  var effectiveValue = getEffectiveValue === null || getEffectiveValue === void 0 ? void 0 : getEffectiveValue(sheetId, rowIndex, columnIndex);
710
- if (typeof effectiveValue === "number") {
711
- return toDate(effectiveValue, sheetCell.use1904);
712
- }
713
- return getFormattedValue === null || getFormattedValue === void 0 ? void 0 : getFormattedValue(sheetId, rowIndex, columnIndex);
1394
+ // A time-of-day (serial in [0, 1) — a typed `9:00` is DATE_TIME
1395
+ // 0.375) is epoch-independent: carry it on the 1900 epoch's day zero
1396
+ // so `detectFillType` can tell a time from a date.
1397
+ value =
1398
+ typeof effectiveValue === "number"
1399
+ ? effectiveValue >= 0 && effectiveValue < 1
1400
+ ? serialToDate(effectiveValue)
1401
+ : toDate(effectiveValue, sheetCell.use1904)
1402
+ : getFormattedValue === null || getFormattedValue === void 0 ? void 0 : getFormattedValue(sheetId, rowIndex, columnIndex);
1403
+ }
1404
+ else if (textFormat) {
1405
+ value = userEntered !== null && userEntered !== void 0 ? userEntered : sheetCell.getEffectiveValue();
714
1406
  }
715
- var cellValue = isText
716
- ? sheetCell.getUserEnteredValue()
717
- : getEffectiveValue === null || getEffectiveValue === void 0 ? void 0 : getEffectiveValue(sheetId, rowIndex, columnIndex);
718
- return cellValue;
1407
+ else {
1408
+ value = getEffectiveValue === null || getEffectiveValue === void 0 ? void 0 : getEffectiveValue(sheetId, rowIndex, columnIndex);
1409
+ }
1410
+ if (quotePrefixed && hasExcelTextPrefix(value)) {
1411
+ value = value.slice(1);
1412
+ }
1413
+ var textTyped = textFormat ||
1414
+ quotePrefixed ||
1415
+ (!sheetCell.isFormula() &&
1416
+ typeof userEntered === "string" &&
1417
+ typeof value === "string");
1418
+ return {
1419
+ value: value,
1420
+ meta: {
1421
+ textFormat: textFormat,
1422
+ textPattern: (_a = numberFormat === null || numberFormat === void 0 ? void 0 : numberFormat.pattern) !== null && _a !== void 0 ? _a : "General",
1423
+ quotePrefixed: quotePrefixed,
1424
+ textTyped: textTyped,
1425
+ formula: sheetCell.isFormula(),
1426
+ },
1427
+ };
1428
+ };
1429
+ toCellValue = function (value) {
1430
+ var _a;
1431
+ if (!(value instanceof Date))
1432
+ return value;
1433
+ if (!isDateLikeFormatType((_a = sheetCell.getNumberFormat()) === null || _a === void 0 ? void 0 : _a.type))
1434
+ return value;
1435
+ return isTimeOnlyDate(value)
1436
+ ? dateToSerial(value)
1437
+ : toSerial(value, sheetCell.use1904);
719
1438
  };
720
- selectionData = [];
721
- for (rowIndex = selection.range.startRowIndex; rowIndex <= selection.range.endRowIndex; rowIndex++) {
722
- rowData = [];
723
- for (columnIndex = selection.range.startColumnIndex; columnIndex <= selection.range.endColumnIndex; columnIndex++) {
724
- cellData = getFillSourceValue(sheetId, rowIndex, columnIndex);
725
- rowData.push(cellData || null);
1439
+ writeFillValue = function (value, meta, sourceCellData, from, to, lineFillType) {
1440
+ if (sheetCell.isFormula()) {
1441
+ sheetCell.moveFormula(from, to, activeSheetName, {
1442
+ preserveAbsolutes: true,
1443
+ });
1444
+ // Remove effective value for formulas
1445
+ sheetCell.clearEffectiveValueForFormula();
1446
+ return sheetCell.getCellData();
726
1447
  }
727
- selectionData.push(rowData);
728
- }
729
- selectionRows = selection.range.endRowIndex - selection.range.startRowIndex + 1;
730
- selectionCols = selection.range.endColumnIndex - selection.range.startColumnIndex + 1;
1448
+ if (meta.textFormat) {
1449
+ return buildTextCell(sourceCellData, value == null ? "" : String(value), meta.textPattern);
1450
+ }
1451
+ if (meta.quotePrefixed && typeof value === "string" && value !== "") {
1452
+ sheetCell.setUserEnteredValue("'".concat(value));
1453
+ return sheetCell.getCellData();
1454
+ }
1455
+ if (shouldNormalizeSeriesNumberFormatForCell(sheetCell, lineFillType, value, locale)) {
1456
+ clearCellNumberFormatForSeries(sheetCell);
1457
+ }
1458
+ sheetCell.setUserEnteredValue(toCellValue(value));
1459
+ if (meta.textTyped &&
1460
+ typeof value === "string" &&
1461
+ value !== "" &&
1462
+ (sheetCell.isFormula() ||
1463
+ typeof sheetCell.getUserEnteredValue() !== "string")) {
1464
+ sheetCell.setUserEnteredValue("'".concat(value));
1465
+ }
1466
+ return sheetCell.getCellData();
1467
+ };
731
1468
  fillValues = [];
732
- switch (direction) {
733
- case Direction.Down: {
734
- fillCount = fillBounds.endRowIndex - selection.range.endRowIndex;
735
- for (columnIndex = selection.range.startColumnIndex; columnIndex <= selection.range.endColumnIndex; columnIndex++) {
736
- sourceValues = [];
737
- sourceCells = [];
738
- for (rowIndex = selection.range.startRowIndex; rowIndex <= selection.range.endRowIndex; rowIndex++) {
739
- cellValue = getFillSourceValue(sheetId, rowIndex, columnIndex);
740
- sourceValues.push(cellValue);
741
- sourceCells.push({ rowIndex: rowIndex, columnIndex: columnIndex });
742
- }
743
- textInfo = getTextInfo(sourceCells);
744
- columnData = [sourceValues];
745
- actualFillType = fillType || detectFillType(columnData, locale, customAutofillLists);
746
- newValues = textInfo.isText
747
- ? Array(fillCount).fill(textInfo.textValue)
748
- : generateFillValues(sourceValues, actualFillType, fillCount, locale, customAutofillLists);
749
- // Fallback to FillDefault if no values were generated
750
- if (newValues.length === 0 && actualFillType !== "FillDefault") {
751
- newValues = generateFillValues(sourceValues, "FillDefault", fillCount, locale);
752
- }
753
- // Apply the values
754
- for (i = 0; i < fillCount; i++) {
755
- targetRowIndex = selection.range.endRowIndex + 1 + i;
756
- targetColumnIndex = columnIndex;
757
- if (!fillValues[i])
758
- fillValues[i] = [];
759
- if (actualFillType === "FillFormats") {
760
- // Copy format but not value
761
- fillValues[i][targetColumnIndex - fillBounds.startColumnIndex] =
762
- "";
763
- }
764
- else {
765
- sourceRowIndex = selection.range.startRowIndex + (i % selectionRows);
766
- sourceCellData = getCellData === null || getCellData === void 0 ? void 0 : getCellData(sheetId, sourceRowIndex, columnIndex);
767
- sheetCell.assign(sheetId, { rowIndex: sourceRowIndex, columnIndex: columnIndex }, sourceCellData, { locale: locale, cellXfsRegistry: cellXfsRegistry, getDataValidationById: getDataValidationById });
768
- if (sheetCell.isFormula()) {
769
- sheetCell.moveFormula({ rowIndex: sourceRowIndex, columnIndex: columnIndex }, { rowIndex: targetRowIndex, columnIndex: targetColumnIndex }, activeSheetName, { preserveAbsolutes: true });
770
- // Remove effective value for formulas
771
- sheetCell.clearEffectiveValueForFormula();
772
- }
773
- else if (textInfo.isText) {
774
- fillValues[i][targetColumnIndex - fillBounds.startColumnIndex] =
775
- buildTextCell(sourceCellData, String((_c = newValues[i]) !== null && _c !== void 0 ? _c : ""), textInfo.pattern);
776
- continue;
777
- }
778
- else {
779
- shouldNormalizeSeriesNumberFormat_1 = shouldNormalizeSeriesNumberFormatForCell(sheetCell, actualFillType, newValues[i]);
780
- if (shouldNormalizeSeriesNumberFormat_1) {
781
- clearCellNumberFormatForSeries(sheetCell);
782
- }
783
- sheetCell.setUserEnteredValue(newValues[i]);
784
- }
785
- fillValues[i][targetColumnIndex - fillBounds.startColumnIndex] =
786
- sheetCell.getCellData();
787
- }
788
- }
1469
+ vertical = direction === Direction.Down || direction === Direction.Up;
1470
+ descending = direction === Direction.Up || direction === Direction.Left;
1471
+ range = selection.range;
1472
+ block = resolveMergedFillBlock(range, merges);
1473
+ stride = block ? (vertical ? block.rows : block.cols) : 1;
1474
+ lineStride = block ? (vertical ? block.cols : block.rows) : 1;
1475
+ lineCount = (vertical
1476
+ ? range.endColumnIndex - range.startColumnIndex + 1
1477
+ : range.endRowIndex - range.startRowIndex + 1) / lineStride;
1478
+ seedCount = (vertical
1479
+ ? range.endRowIndex - range.startRowIndex + 1
1480
+ : range.endColumnIndex - range.startColumnIndex + 1) / stride;
1481
+ span = (function () {
1482
+ switch (direction) {
1483
+ case Direction.Down:
1484
+ return fillBounds.endRowIndex - range.endRowIndex;
1485
+ case Direction.Up:
1486
+ return range.startRowIndex - fillBounds.startRowIndex;
1487
+ case Direction.Right:
1488
+ return fillBounds.endColumnIndex - range.endColumnIndex;
1489
+ case Direction.Left:
1490
+ return range.startColumnIndex - fillBounds.startColumnIndex;
1491
+ default:
1492
+ return 0;
1493
+ }
1494
+ })();
1495
+ fillCount = Math.floor(Math.max(span, 0) / stride);
1496
+ snapped = __assign({}, fillBounds);
1497
+ if (block) {
1498
+ if (direction === Direction.Down) {
1499
+ snapped.endRowIndex = range.endRowIndex + fillCount * stride;
1500
+ }
1501
+ else if (direction === Direction.Up) {
1502
+ snapped.startRowIndex = range.startRowIndex - fillCount * stride;
1503
+ }
1504
+ else if (direction === Direction.Right) {
1505
+ snapped.endColumnIndex = range.endColumnIndex + fillCount * stride;
1506
+ }
1507
+ else {
1508
+ snapped.startColumnIndex = range.startColumnIndex - fillCount * stride;
1509
+ }
1510
+ fillValues.snappedFillBounds = snapped;
1511
+ fillValues.fillMerges = [];
1512
+ }
1513
+ seedCoord = function (line, k) {
1514
+ if (vertical) {
1515
+ var columnIndex_1 = range.startColumnIndex + line * lineStride;
1516
+ var rowIndex_1 = descending
1517
+ ? range.endRowIndex - (stride - 1) - k * stride
1518
+ : range.startRowIndex + k * stride;
1519
+ return { rowIndex: rowIndex_1, columnIndex: columnIndex_1 };
1520
+ }
1521
+ var rowIndex = range.startRowIndex + line * lineStride;
1522
+ var columnIndex = descending
1523
+ ? range.endColumnIndex - (stride - 1) - k * stride
1524
+ : range.startColumnIndex + k * stride;
1525
+ return { rowIndex: rowIndex, columnIndex: columnIndex };
1526
+ };
1527
+ targetCoord = function (line, i) {
1528
+ if (vertical) {
1529
+ var columnIndex_2 = range.startColumnIndex + line * lineStride;
1530
+ var rowIndex_2 = descending
1531
+ ? range.startRowIndex - (i + 1) * stride
1532
+ : range.endRowIndex + 1 + i * stride;
1533
+ return { rowIndex: rowIndex_2, columnIndex: columnIndex_2 };
1534
+ }
1535
+ var rowIndex = range.startRowIndex + line * lineStride;
1536
+ var columnIndex = descending
1537
+ ? range.startColumnIndex - (i + 1) * stride
1538
+ : range.endColumnIndex + 1 + i * stride;
1539
+ return { rowIndex: rowIndex, columnIndex: columnIndex };
1540
+ };
1541
+ originRow = direction === Direction.Down ? range.endRowIndex + 1 : snapped.startRowIndex;
1542
+ originColumn = direction === Direction.Right
1543
+ ? range.endColumnIndex + 1
1544
+ : snapped.startColumnIndex;
1545
+ place = function (target, cell) {
1546
+ var r = target.rowIndex - originRow;
1547
+ var c = target.columnIndex - originColumn;
1548
+ if (!fillValues[r])
1549
+ fillValues[r] = [];
1550
+ fillValues[r][c] = cell;
1551
+ };
1552
+ lines = [];
1553
+ for (line = 0; line < lineCount; line++) {
1554
+ sourceValues = [];
1555
+ sourceMeta = [];
1556
+ for (k = 0; k < seedCount; k++) {
1557
+ _c = seedCoord(line, k), rowIndex = _c.rowIndex, columnIndex = _c.columnIndex;
1558
+ _d = readFillSource(rowIndex, columnIndex), value = _d.value, meta = _d.meta;
1559
+ sourceValues.push(value);
1560
+ sourceMeta.push(meta);
1561
+ }
1562
+ lines.push({ sourceValues: sourceValues, sourceMeta: sourceMeta });
1563
+ }
1564
+ fillType = (function () {
1565
+ if (requestedFillType)
1566
+ return requestedFillType;
1567
+ if (modifier !== "toggle")
1568
+ return undefined;
1569
+ var detectedPerLine = lines.map(function (_a) {
1570
+ var sourceValues = _a.sourceValues, sourceMeta = _a.sourceMeta;
1571
+ if (sourceMeta.every(function (m) { return m.formula; }))
1572
+ return undefined;
1573
+ if (sourceValues.every(function (v) { return v == null || v === ""; }))
1574
+ return undefined;
1575
+ return generateLineSeries(sourceValues, 1, {
1576
+ locale: locale,
1577
+ customAutofillLists: customAutofillLists,
1578
+ descending: descending,
1579
+ }).fillType;
1580
+ });
1581
+ return resolveToggledFillType(detectedPerLine);
1582
+ })();
1583
+ isFormatsOnly = isFormatsOnlyFillType(fillType);
1584
+ isValuesOnly = isValuesOnlyFillType(fillType);
1585
+ seriesFillType = isFormatsOnly || isValuesOnly ? undefined : fillType;
1586
+ formatsOnlyCell = function (source, target) {
1587
+ var _a, _b;
1588
+ var sourceUf = (_a = getCellData(sheetId, source.rowIndex, source.columnIndex)) === null || _a === void 0 ? void 0 : _a.uf;
1589
+ var _c = ((_b = getCellData(sheetId, target.rowIndex, target.columnIndex)) !== null && _b !== void 0 ? _b : {}), _targetUf = _c.uf, rest = __rest(_c, ["uf"]);
1590
+ return (sourceUf === undefined ? rest : __assign(__assign({}, rest), { uf: sourceUf }));
1591
+ };
1592
+ finalizeCell = function (cell, target) {
1593
+ var _a;
1594
+ if (!isValuesOnly)
1595
+ return cell;
1596
+ var targetUf = (_a = getCellData(sheetId, target.rowIndex, target.columnIndex)) === null || _a === void 0 ? void 0 : _a.uf;
1597
+ var _b = cell, _sourceUf = _b.uf, rest = __rest(_b, ["uf"]);
1598
+ return (targetUf === undefined ? rest : __assign(__assign({}, rest), { uf: targetUf }));
1599
+ };
1600
+ computeLine = function (sourceValues, count) {
1601
+ if (series) {
1602
+ var specLine = generateSeriesSpecValues(sourceValues, series, count, locale);
1603
+ if (specLine) {
1604
+ return {
1605
+ fillType: seriesFillType !== null && seriesFillType !== void 0 ? seriesFillType : "FillSeries",
1606
+ values: specLine.values,
1607
+ seeds: specLine.seeds,
1608
+ untouchedTail: true,
1609
+ };
789
1610
  }
790
- break;
791
1611
  }
792
- case Direction.Up: {
793
- fillCount = selection.range.startRowIndex - fillBounds.startRowIndex;
794
- for (columnIndex = selection.range.startColumnIndex; columnIndex <= selection.range.endColumnIndex; columnIndex++) {
795
- sourceValues = [];
796
- sourceCells = [];
797
- for (rowIndex = selection.range.endRowIndex; rowIndex >= selection.range.startRowIndex; rowIndex--) {
798
- cellValue = getFillSourceValue(sheetId, rowIndex, columnIndex);
799
- sourceValues.push(cellValue);
800
- sourceCells.push({ rowIndex: rowIndex, columnIndex: columnIndex });
801
- }
802
- textInfo = getTextInfo(sourceCells);
803
- columnData = [sourceValues];
804
- actualFillType = fillType || detectFillType(columnData, locale, customAutofillLists);
805
- newValues = textInfo.isText
806
- ? Array(fillCount).fill(textInfo.textValue)
807
- : generateFillValues(sourceValues, actualFillType, fillCount, locale, customAutofillLists);
808
- // Fallback to FillDefault if no values were generated
809
- if (newValues.length === 0 && actualFillType !== "FillDefault") {
810
- newValues = generateFillValues(sourceValues, "FillDefault", fillCount, locale);
811
- }
812
- // Apply the values (in reverse order)
813
- for (i = 0; i < fillCount; i++) {
814
- targetRowIndex = selection.range.startRowIndex - 1 - i;
815
- targetColumnIndex = columnIndex;
816
- if (!fillValues[fillCount - 1 - i])
817
- fillValues[fillCount - 1 - i] = [];
818
- if (actualFillType === "FillFormats") {
819
- fillValues[fillCount - 1 - i][targetColumnIndex - fillBounds.startColumnIndex] = "";
820
- }
821
- else {
822
- sourceRowIndex = selection.range.endRowIndex - (i % selectionRows);
823
- sourceCellData = getCellData === null || getCellData === void 0 ? void 0 : getCellData(sheetId, sourceRowIndex, columnIndex);
824
- sheetCell.assign(sheetId, { rowIndex: sourceRowIndex, columnIndex: columnIndex }, sourceCellData, { locale: locale, cellXfsRegistry: cellXfsRegistry, getDataValidationById: getDataValidationById });
825
- if (sheetCell.isFormula()) {
826
- sheetCell.moveFormula({ rowIndex: sourceRowIndex, columnIndex: columnIndex }, { rowIndex: targetRowIndex, columnIndex: targetColumnIndex }, activeSheetName, { preserveAbsolutes: true });
827
- // Remove effective value for formulas
828
- sheetCell.clearEffectiveValueForFormula();
829
- }
830
- else if (textInfo.isText) {
831
- fillValues[fillCount - 1 - i][targetColumnIndex - fillBounds.startColumnIndex] = buildTextCell(sourceCellData, String((_d = newValues[i]) !== null && _d !== void 0 ? _d : ""), textInfo.pattern);
832
- continue;
833
- }
834
- else {
835
- shouldNormalizeSeriesNumberFormat_2 = shouldNormalizeSeriesNumberFormatForCell(sheetCell, actualFillType, newValues[i]);
836
- if (shouldNormalizeSeriesNumberFormat_2) {
837
- clearCellNumberFormatForSeries(sheetCell);
838
- }
839
- sheetCell.setUserEnteredValue(newValues[i]);
840
- }
841
- fillValues[fillCount - 1 - i][targetColumnIndex - fillBounds.startColumnIndex] = sheetCell.getCellData();
842
- }
843
- }
1612
+ if (seriesFillType) {
1613
+ var values = generateFillValues(sourceValues, seriesFillType, count, locale, customAutofillLists, descending);
1614
+ if (values.length === 0 && seriesFillType !== "FillDefault") {
1615
+ values = generateFillValues(sourceValues, "FillDefault", count, locale);
844
1616
  }
845
- break;
1617
+ return { fillType: seriesFillType, values: values };
846
1618
  }
847
- case Direction.Right: {
848
- fillCount = fillBounds.endColumnIndex - selection.range.endColumnIndex;
849
- for (rowIndex = selection.range.startRowIndex; rowIndex <= selection.range.endRowIndex; rowIndex++) {
850
- sourceValues = [];
851
- sourceCells = [];
852
- textSourceCellData = getCellData === null || getCellData === void 0 ? void 0 : getCellData(sheetId, rowIndex, selection.range.startColumnIndex);
853
- for (columnIndex = selection.range.startColumnIndex; columnIndex <= selection.range.endColumnIndex; columnIndex++) {
854
- cellValue = getFillSourceValue(sheetId, rowIndex, columnIndex);
855
- sourceValues.push(cellValue);
856
- sourceCells.push({ rowIndex: rowIndex, columnIndex: columnIndex });
857
- }
858
- textInfo = getTextInfo(sourceCells);
859
- rowData = [sourceValues];
860
- actualFillType = fillType || detectFillType(rowData, locale);
861
- newValues = textInfo.isText
862
- ? Array(fillCount).fill(textInfo.textValue)
863
- : generateFillValues(sourceValues, actualFillType, fillCount, locale, customAutofillLists);
864
- // Fallback to FillDefault if no values were generated
865
- if (newValues.length === 0 && actualFillType !== "FillDefault") {
866
- newValues = generateFillValues(sourceValues, "FillDefault", fillCount, locale);
867
- }
868
- targetRowIndex = rowIndex - fillBounds.startRowIndex;
869
- if (!fillValues[targetRowIndex])
870
- fillValues[targetRowIndex] = [];
871
- for (i = 0; i < fillCount; i++) {
872
- targetColumnIndex = selection.range.endColumnIndex + 1 + i;
873
- if (actualFillType === "FillFormats") {
874
- fillValues[targetRowIndex][i] = "";
875
- }
876
- else if (textInfo.isText) {
877
- fillValues[targetRowIndex][i] = buildTextCell(textSourceCellData, String((_e = newValues[i]) !== null && _e !== void 0 ? _e : ""), textInfo.pattern);
878
- }
879
- else {
880
- sourceColumnIndex = selection.range.startColumnIndex + (i % selectionCols);
881
- sourceCellData = getCellData === null || getCellData === void 0 ? void 0 : getCellData(sheetId, rowIndex, sourceColumnIndex);
882
- sheetCell.assign(sheetId, { rowIndex: rowIndex, columnIndex: sourceColumnIndex }, sourceCellData, { locale: locale, cellXfsRegistry: cellXfsRegistry, getDataValidationById: getDataValidationById });
883
- if (sheetCell.isFormula()) {
884
- sheetCell.moveFormula({ rowIndex: rowIndex, columnIndex: sourceColumnIndex }, { rowIndex: rowIndex, columnIndex: targetColumnIndex }, activeSheetName, { preserveAbsolutes: true });
885
- // Remove effective value for formulas
886
- sheetCell.clearEffectiveValueForFormula();
887
- }
888
- else {
889
- shouldNormalizeSeriesNumberFormat_3 = shouldNormalizeSeriesNumberFormatForCell(sheetCell, actualFillType, newValues[i]);
890
- if (shouldNormalizeSeriesNumberFormat_3) {
891
- clearCellNumberFormatForSeries(sheetCell);
892
- }
893
- sheetCell.setUserEnteredValue(newValues[i]);
894
- }
895
- fillValues[targetRowIndex][i] = sheetCell.getCellData();
896
- }
897
- }
1619
+ return generateLineSeries(sourceValues, count, {
1620
+ locale: locale,
1621
+ customAutofillLists: customAutofillLists,
1622
+ descending: descending,
1623
+ });
1624
+ };
1625
+ coveredCell = function (target, sourceCovered) {
1626
+ var _a;
1627
+ var targetCell = getCellData(sheetId, target.rowIndex, target.columnIndex);
1628
+ var sourceUf = (_a = getCellData(sheetId, sourceCovered.rowIndex, sourceCovered.columnIndex)) === null || _a === void 0 ? void 0 : _a.uf;
1629
+ if (isFormatsOnly) {
1630
+ return sourceUf === undefined
1631
+ ? (targetCell !== null && targetCell !== void 0 ? targetCell : undefined)
1632
+ : __assign(__assign({}, (targetCell !== null && targetCell !== void 0 ? targetCell : {})), { uf: sourceUf });
1633
+ }
1634
+ var uf = isValuesOnly ? targetCell === null || targetCell === void 0 ? void 0 : targetCell.uf : sourceUf;
1635
+ return uf === undefined ? undefined : { uf: uf };
1636
+ };
1637
+ placeBlock = function (target, cell, from) {
1638
+ var _a;
1639
+ place(target, cell);
1640
+ if (!block)
1641
+ return;
1642
+ var landed = {
1643
+ startRowIndex: target.rowIndex,
1644
+ endRowIndex: target.rowIndex + block.rows - 1,
1645
+ startColumnIndex: target.columnIndex,
1646
+ endColumnIndex: target.columnIndex + block.cols - 1,
1647
+ };
1648
+ for (var r = landed.startRowIndex; r <= landed.endRowIndex; r++) {
1649
+ for (var c = landed.startColumnIndex; c <= landed.endColumnIndex; c++) {
1650
+ if (r === target.rowIndex && c === target.columnIndex)
1651
+ continue;
1652
+ place({ rowIndex: r, columnIndex: c }, coveredCell({ rowIndex: r, columnIndex: c }, {
1653
+ rowIndex: from.rowIndex + (r - target.rowIndex),
1654
+ columnIndex: from.columnIndex + (c - target.columnIndex),
1655
+ }));
898
1656
  }
899
- break;
900
1657
  }
901
- case Direction.Left: {
902
- fillCount = selection.range.startColumnIndex - fillBounds.startColumnIndex;
903
- for (rowIndex = selection.range.startRowIndex; rowIndex <= selection.range.endRowIndex; rowIndex++) {
904
- sourceValues = [];
905
- sourceCells = [];
906
- textSourceCellData = getCellData === null || getCellData === void 0 ? void 0 : getCellData(sheetId, rowIndex, selection.range.endColumnIndex);
907
- for (columnIndex = selection.range.endColumnIndex; columnIndex >= selection.range.startColumnIndex; columnIndex--) {
908
- cellValue = getFillSourceValue(sheetId, rowIndex, columnIndex);
909
- sourceValues.push(cellValue);
910
- sourceCells.push({ rowIndex: rowIndex, columnIndex: columnIndex });
911
- }
912
- textInfo = getTextInfo(sourceCells);
913
- rowData = [sourceValues];
914
- actualFillType = fillType || detectFillType(rowData, locale);
915
- newValues = textInfo.isText
916
- ? Array(fillCount).fill(textInfo.textValue)
917
- : generateFillValues(sourceValues, actualFillType, fillCount, locale, customAutofillLists);
918
- // Fallback to FillDefault if no values were generated
919
- if (newValues.length === 0 && actualFillType !== "FillDefault") {
920
- newValues = generateFillValues(sourceValues, "FillDefault", fillCount, locale);
1658
+ (_a = fillValues.fillMerges) === null || _a === void 0 ? void 0 : _a.push(landed);
1659
+ };
1660
+ markUntouched = function (target) {
1661
+ var _a, _b;
1662
+ var rows = (_a = block === null || block === void 0 ? void 0 : block.rows) !== null && _a !== void 0 ? _a : 1;
1663
+ var cols = (_b = block === null || block === void 0 ? void 0 : block.cols) !== null && _b !== void 0 ? _b : 1;
1664
+ for (var r = 0; r < rows; r++) {
1665
+ for (var c = 0; c < cols; c++) {
1666
+ place({ rowIndex: target.rowIndex + r, columnIndex: target.columnIndex + c }, FILL_UNTOUCHED);
1667
+ }
1668
+ }
1669
+ };
1670
+ for (line = 0; line < lineCount; line++) {
1671
+ _e = lines[line], sourceValues = _e.sourceValues, sourceMeta = _e.sourceMeta;
1672
+ _f = computeLine(sourceValues, fillCount), lineFillType = _f.fillType, newValues = _f.values, seeds = _f.seeds, untouchedTail = _f.untouchedTail;
1673
+ // Series-dialog Trend: Excel REPLACES the starting values with the fitted trend — each numeric
1674
+ // seed cell is rewritten in place (formula shifting is moot: `from` = `to`) and keeps its format.
1675
+ if ((seeds === null || seeds === void 0 ? void 0 : seeds.length) && !isFormatsOnly) {
1676
+ (_h = fillValues.seedCells) !== null && _h !== void 0 ? _h : (fillValues.seedCells = []);
1677
+ try {
1678
+ for (seeds_1 = (e_3 = void 0, __values(seeds)), seeds_1_1 = seeds_1.next(); !seeds_1_1.done; seeds_1_1 = seeds_1.next()) {
1679
+ seed = seeds_1_1.value;
1680
+ from = seedCoord(line, seed.index);
1681
+ sourceCellData = getCellData === null || getCellData === void 0 ? void 0 : getCellData(sheetId, from.rowIndex, from.columnIndex);
1682
+ sheetCell.assign(sheetId, from, sourceCellData, {
1683
+ locale: locale,
1684
+ cellXfsRegistry: cellXfsRegistry,
1685
+ getDataValidationById: getDataValidationById,
1686
+ });
1687
+ fillValues.seedCells.push({
1688
+ rowIndex: from.rowIndex,
1689
+ columnIndex: from.columnIndex,
1690
+ cell: finalizeCell(writeFillValue(seed.value, sourceMeta[seed.index], sourceCellData, from, from, lineFillType), from),
1691
+ });
921
1692
  }
922
- targetRowIndex = rowIndex - fillBounds.startRowIndex;
923
- if (!fillValues[targetRowIndex])
924
- fillValues[targetRowIndex] = [];
925
- for (i = 0; i < fillCount; i++) {
926
- targetColumnIndex = selection.range.startColumnIndex - 1 - i;
927
- if (actualFillType === "FillFormats") {
928
- fillValues[targetRowIndex][fillCount - 1 - i] = "";
929
- }
930
- else if (textInfo.isText) {
931
- fillValues[targetRowIndex][fillCount - 1 - i] = buildTextCell(textSourceCellData, String((_f = newValues[i]) !== null && _f !== void 0 ? _f : ""), textInfo.pattern);
932
- }
933
- else {
934
- sourceColumnIndex = selection.range.endColumnIndex - (i % selectionCols);
935
- sourceCellData = getCellData === null || getCellData === void 0 ? void 0 : getCellData(sheetId, rowIndex, sourceColumnIndex);
936
- sheetCell.assign(sheetId, { rowIndex: rowIndex, columnIndex: sourceColumnIndex }, sourceCellData, { locale: locale, cellXfsRegistry: cellXfsRegistry, getDataValidationById: getDataValidationById });
937
- if (sheetCell.isFormula()) {
938
- sheetCell.moveFormula({ rowIndex: rowIndex, columnIndex: sourceColumnIndex }, { rowIndex: rowIndex, columnIndex: targetColumnIndex }, activeSheetName, { preserveAbsolutes: true });
939
- // Remove effective value for formulas
940
- sheetCell.clearEffectiveValueForFormula();
941
- }
942
- else {
943
- shouldNormalizeSeriesNumberFormat_4 = shouldNormalizeSeriesNumberFormatForCell(sheetCell, actualFillType, newValues[i]);
944
- if (shouldNormalizeSeriesNumberFormat_4) {
945
- clearCellNumberFormatForSeries(sheetCell);
946
- }
947
- sheetCell.setUserEnteredValue(newValues[i]);
948
- }
949
- fillValues[targetRowIndex][fillCount - 1 - i] =
950
- sheetCell.getCellData();
951
- }
1693
+ }
1694
+ catch (e_3_1) { e_3 = { error: e_3_1 }; }
1695
+ finally {
1696
+ try {
1697
+ if (seeds_1_1 && !seeds_1_1.done && (_g = seeds_1.return)) _g.call(seeds_1);
952
1698
  }
1699
+ finally { if (e_3) throw e_3.error; }
953
1700
  }
954
- break;
1701
+ }
1702
+ for (i = 0; i < fillCount; i++) {
1703
+ target = targetCoord(line, i);
1704
+ k = i % seedCount;
1705
+ from = seedCoord(line, k);
1706
+ if (untouchedTail && i >= newValues.length) {
1707
+ // The Series dialog's `stop` ended this line: Excel leaves the rest of the selection alone.
1708
+ markUntouched(target);
1709
+ continue;
1710
+ }
1711
+ if (isFormatsOnly) {
1712
+ // Copy format but not value
1713
+ placeBlock(target, formatsOnlyCell(from, target), from);
1714
+ continue;
1715
+ }
1716
+ sourceCellData = getCellData === null || getCellData === void 0 ? void 0 : getCellData(sheetId, from.rowIndex, from.columnIndex);
1717
+ sheetCell.assign(sheetId, from, sourceCellData, {
1718
+ locale: locale,
1719
+ cellXfsRegistry: cellXfsRegistry,
1720
+ getDataValidationById: getDataValidationById,
1721
+ });
1722
+ placeBlock(target, finalizeCell(writeFillValue(newValues[i], sourceMeta[k], sourceCellData, from, target, lineFillType), target), from);
955
1723
  }
956
1724
  }
957
1725
  return [2 /*return*/, fillValues];