@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.
- package/dist/cjs/api/datatype.d.ts.map +1 -1
- package/dist/cjs/api/datatype.js +8 -1
- package/dist/cjs/api/fill-series-spec.d.ts +35 -0
- package/dist/cjs/api/fill-series-spec.d.ts.map +1 -0
- package/dist/cjs/api/fill-series-spec.js +164 -0
- package/dist/cjs/api/fill.d.ts +95 -3
- package/dist/cjs/api/fill.d.ts.map +1 -1
- package/dist/cjs/api/fill.js +1186 -418
- package/dist/cjs/api/immer-utils.d.ts.map +1 -1
- package/dist/cjs/api/immer-utils.js +20 -15
- package/dist/cjs/api/intl.d.ts.map +1 -1
- package/dist/cjs/api/paint-format.d.ts +15 -1
- package/dist/cjs/api/paint-format.d.ts.map +1 -1
- package/dist/cjs/api/paint-format.js +69 -10
- package/dist/cjs/commands.d.ts +11 -1
- package/dist/cjs/commands.d.ts.map +1 -1
- package/dist/cjs/hooks/use-calculation.d.ts +1 -1
- package/dist/cjs/hooks/use-on-fill.d.ts +13 -4
- package/dist/cjs/hooks/use-on-fill.d.ts.map +1 -1
- package/dist/cjs/hooks/use-on-fill.js +28 -7
- package/dist/cjs/hooks/use-paint-format.d.ts.map +1 -1
- package/dist/cjs/hooks/use-paint-format.js +10 -7
- package/dist/cjs/hooks/use-spreadsheet-state.d.ts +1 -1
- package/dist/cjs/hooks/use-spreadsheet-state.d.ts.map +1 -1
- package/dist/cjs/hooks/use-spreadsheet-state.js +12 -1
- package/dist/cjs/interface/apply-fill.d.ts +3 -3
- package/dist/cjs/interface/apply-fill.d.ts.map +1 -1
- package/dist/cjs/interface/apply-fill.js +100 -53
- package/dist/cjs/interface/spreadsheet-interface.d.ts +1 -1
- package/dist/cjs/sheet-cell.d.ts.map +1 -1
- package/dist/cjs/sheet-cell.js +30 -6
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/api/datatype.d.ts.map +1 -1
- package/dist/esm/api/datatype.js +8 -1
- package/dist/esm/api/fill-series-spec.d.ts +35 -0
- package/dist/esm/api/fill-series-spec.d.ts.map +1 -0
- package/dist/esm/api/fill-series-spec.js +160 -0
- package/dist/esm/api/fill.d.ts +95 -3
- package/dist/esm/api/fill.d.ts.map +1 -1
- package/dist/esm/api/fill.js +1158 -418
- package/dist/esm/api/immer-utils.d.ts.map +1 -1
- package/dist/esm/api/immer-utils.js +20 -15
- package/dist/esm/api/intl.d.ts.map +1 -1
- package/dist/esm/api/paint-format.d.ts +15 -1
- package/dist/esm/api/paint-format.d.ts.map +1 -1
- package/dist/esm/api/paint-format.js +46 -1
- package/dist/esm/commands.d.ts +11 -1
- package/dist/esm/commands.d.ts.map +1 -1
- package/dist/esm/hooks/use-calculation.d.ts +1 -1
- package/dist/esm/hooks/use-on-fill.d.ts +13 -4
- package/dist/esm/hooks/use-on-fill.d.ts.map +1 -1
- package/dist/esm/hooks/use-on-fill.js +24 -4
- package/dist/esm/hooks/use-paint-format.d.ts.map +1 -1
- package/dist/esm/hooks/use-paint-format.js +10 -7
- package/dist/esm/hooks/use-spreadsheet-state.d.ts +1 -1
- package/dist/esm/hooks/use-spreadsheet-state.d.ts.map +1 -1
- package/dist/esm/hooks/use-spreadsheet-state.js +12 -1
- package/dist/esm/interface/apply-fill.d.ts +3 -3
- package/dist/esm/interface/apply-fill.d.ts.map +1 -1
- package/dist/esm/interface/apply-fill.js +93 -48
- package/dist/esm/interface/spreadsheet-interface.d.ts +1 -1
- package/dist/esm/sheet-cell.d.ts.map +1 -1
- package/dist/esm/sheet-cell.js +30 -6
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/package.json +10 -10
package/dist/cjs/api/fill.js
CHANGED
|
@@ -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
|
-
|
|
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],
|
|
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 =
|
|
26
|
-
if (Number.
|
|
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
|
|
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 =
|
|
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
|
|
207
|
-
|
|
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
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
326
|
-
|
|
327
|
-
|
|
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
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
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
|
|
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
|
-
:
|
|
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
|
-
|
|
454
|
-
|
|
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
|
|
466
|
-
var
|
|
998
|
+
var lastSerial = dateToSerial(lastDate);
|
|
999
|
+
var step = unitStep;
|
|
467
1000
|
if (previousDate) {
|
|
468
|
-
var diff =
|
|
469
|
-
if (diff
|
|
470
|
-
|
|
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
|
-
|
|
476
|
-
result.push(currentDate.toLocaleDateString(locale));
|
|
1007
|
+
result.push(serialToDate(lastSerial + step * (i + 1)));
|
|
477
1008
|
}
|
|
478
1009
|
}
|
|
479
1010
|
else {
|
|
480
|
-
|
|
481
|
-
|
|
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
|
-
|
|
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
|
-
|
|
512
|
-
currentDate.setDate(currentDate.getDate() +
|
|
513
|
-
|
|
514
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
-
//
|
|
533
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
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,
|
|
650
|
-
var
|
|
651
|
-
var
|
|
652
|
-
|
|
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
|
-
|
|
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
|
|
706
|
-
var
|
|
707
|
-
var
|
|
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
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
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
|
-
|
|
716
|
-
?
|
|
717
|
-
|
|
718
|
-
|
|
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
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
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
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
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
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
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
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
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
|
-
|
|
1617
|
+
return { fillType: seriesFillType, values: values };
|
|
846
1618
|
}
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
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
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
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
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
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
|
-
|
|
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];
|