@shbernal/ts-xlsx 3.1.0 → 3.2.0
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/README.md +30 -23
- package/dist/bytes.d.ts +3 -2
- package/dist/core/address.d.ts +36 -0
- package/dist/core/address.js +47 -9
- package/dist/core/array-formula-ranges.d.ts +33 -0
- package/dist/core/array-formula-ranges.js +62 -0
- package/dist/core/color-resolution.js +2 -2
- package/dist/core/column.js +3 -2
- package/dist/core/comment-thread.d.ts +7 -3
- package/dist/core/conditional-formatting-overlay.d.ts +9 -0
- package/dist/core/conditional-formatting-overlay.js +28 -1
- package/dist/core/conditional-formatting.d.ts +98 -10
- package/dist/core/conditional-formatting.js +42 -1
- package/dist/core/data-validation-overlay.d.ts +5 -0
- package/dist/core/data-validation-overlay.js +15 -0
- package/dist/core/data-validation.d.ts +16 -0
- package/dist/core/data-validation.js +15 -0
- package/dist/core/date-format.js +14 -4
- package/dist/core/date.js +1 -1
- package/dist/core/formula-references.d.ts +26 -0
- package/dist/core/formula-references.js +173 -0
- package/dist/core/formula-scan.d.ts +2 -0
- package/dist/core/formula-scan.js +58 -0
- package/dist/core/formula.d.ts +51 -18
- package/dist/core/formula.js +77 -86
- package/dist/core/function-values.d.ts +4 -0
- package/dist/core/function-values.js +58 -0
- package/dist/core/future-functions.d.ts +3 -0
- package/dist/core/{modern-functions.js → future-functions.js} +119 -88
- package/dist/core/grid-edits.d.ts +28 -0
- package/dist/core/grid-edits.js +193 -25
- package/dist/core/hyperlink.d.ts +39 -0
- package/dist/core/hyperlink.js +88 -0
- package/dist/core/image.d.ts +36 -5
- package/dist/core/image.js +9 -0
- package/dist/core/internal.d.ts +3 -1
- package/dist/core/limits.d.ts +5 -3
- package/dist/core/merge.d.ts +13 -0
- package/dist/core/merge.js +15 -1
- package/dist/core/page-setup.d.ts +26 -11
- package/dist/core/pivot-table.d.ts +49 -10
- package/dist/core/pivot-table.js +55 -21
- package/dist/core/protection.d.ts +66 -4
- package/dist/core/protection.js +4 -0
- package/dist/core/row.js +3 -2
- package/dist/core/structured-reference.d.ts +7 -0
- package/dist/core/structured-reference.js +7 -0
- package/dist/core/table.d.ts +38 -8
- package/dist/core/table.js +37 -7
- package/dist/core/theme.d.ts +2 -0
- package/dist/core/theme.js +4 -1
- package/dist/core/value.d.ts +51 -28
- package/dist/core/value.js +15 -12
- package/dist/core/workbook-styles.d.ts +17 -1
- package/dist/core/workbook-styles.js +4 -0
- package/dist/core/workbook-theme.js +5 -3
- package/dist/core/workbook.d.ts +20 -3
- package/dist/core/workbook.js +26 -5
- package/dist/core/worksheet-merges.d.ts +9 -4
- package/dist/core/worksheet-merges.js +17 -11
- package/dist/core/worksheet-model.d.ts +1 -1
- package/dist/core/worksheet-model.js +4 -0
- package/dist/core/worksheet-pictures.d.ts +3 -3
- package/dist/core/worksheet-pictures.js +7 -6
- package/dist/core/worksheet.d.ts +110 -21
- package/dist/core/worksheet.js +174 -49
- package/dist/entries/core.d.ts +7 -5
- package/dist/entries/core.js +4 -3
- package/dist/entries/errors.d.ts +1 -0
- package/dist/entries/errors.js +1 -0
- package/dist/errors.js +1 -1
- package/dist/io/cell-metadata/metadata.d.ts +42 -0
- package/dist/io/cell-metadata/metadata.js +35 -0
- package/dist/io/cell-metadata/rich-values.d.ts +11 -0
- package/dist/io/cell-metadata/rich-values.js +80 -0
- package/dist/io/csv/delimiter.d.ts +14 -2
- package/dist/io/csv/delimiter.js +12 -0
- package/dist/io/csv/errors.d.ts +14 -0
- package/dist/io/csv/errors.js +5 -0
- package/dist/io/csv/read.d.ts +6 -1
- package/dist/io/csv/read.js +46 -24
- package/dist/io/csv/write.d.ts +10 -3
- package/dist/io/csv/write.js +16 -7
- package/dist/io/opc/part-paths.js +4 -4
- package/dist/io/opc/read-opc.d.ts +4 -0
- package/dist/io/opc/read-opc.js +10 -4
- package/dist/io/opc/strict-relationships.d.ts +2 -0
- package/dist/io/opc/strict-relationships.js +12 -0
- package/dist/io/read-policy/column-budget.d.ts +26 -0
- package/dist/io/{xlsx → read-policy}/column-budget.js +4 -8
- package/dist/io/read-policy/read-repair.d.ts +74 -0
- package/dist/io/read-policy/read-repair.js +119 -0
- package/dist/io/style/cell-style-resolution.d.ts +25 -0
- package/dist/io/style/cell-style-resolution.js +30 -0
- package/dist/io/xlsb/formula.js +4 -2
- package/dist/io/xlsb/ptg-functions.js +110 -0
- package/dist/io/xlsb/read-metadata.d.ts +7 -0
- package/dist/io/xlsb/read-metadata.js +69 -0
- package/dist/io/xlsb/read-worksheet.d.ts +6 -0
- package/dist/io/xlsb/read-worksheet.js +95 -37
- package/dist/io/xlsb/read.js +26 -8
- package/dist/io/xlsb/record-types.d.ts +13 -0
- package/dist/io/xlsb/record-types.js +13 -0
- package/dist/io/xlsb/sheet-protection.d.ts +20 -0
- package/dist/io/xlsb/sheet-protection.js +60 -0
- package/dist/io/xlsx/cell-accumulator.d.ts +20 -12
- package/dist/io/xlsx/cell-accumulator.js +49 -19
- package/dist/io/xlsx/cell-metadata.d.ts +39 -0
- package/dist/io/xlsx/cell-metadata.js +162 -0
- package/dist/io/xlsx/cell-value.d.ts +16 -4
- package/dist/io/xlsx/cell-value.js +38 -21
- package/dist/io/xlsx/color-xml.js +1 -1
- package/dist/io/xlsx/column-span.d.ts +11 -0
- package/dist/io/xlsx/column-span.js +9 -0
- package/dist/io/xlsx/comments.d.ts +3 -28
- package/dist/io/xlsx/comments.js +3 -65
- package/dist/io/xlsx/conditional-formatting.d.ts +42 -25
- package/dist/io/xlsx/conditional-formatting.js +424 -129
- package/dist/io/xlsx/data-validation.d.ts +12 -7
- package/dist/io/xlsx/data-validation.js +41 -29
- package/dist/io/xlsx/edit-vba.js +43 -36
- package/dist/io/xlsx/hyperlinks.d.ts +16 -50
- package/dist/io/xlsx/hyperlinks.js +8 -37
- package/dist/io/xlsx/images.d.ts +49 -16
- package/dist/io/xlsx/images.js +145 -44
- package/dist/io/xlsx/namespaces.d.ts +8 -0
- package/dist/io/xlsx/namespaces.js +4 -0
- package/dist/io/xlsx/package-plan.d.ts +48 -15
- package/dist/io/xlsx/package-plan.js +46 -9
- package/dist/io/xlsx/part-names.d.ts +6 -8
- package/dist/io/xlsx/part-names.js +3 -7
- package/dist/io/xlsx/pivot.d.ts +5 -5
- package/dist/io/xlsx/pivot.js +23 -22
- package/dist/io/xlsx/preserved-splices.d.ts +6 -0
- package/dist/io/xlsx/preserved-splices.js +74 -0
- package/dist/io/xlsx/read-comments.d.ts +29 -0
- package/dist/io/xlsx/read-comments.js +75 -0
- package/dist/io/xlsx/read-parts.d.ts +2 -2
- package/dist/io/xlsx/read-parts.js +44 -37
- package/dist/io/xlsx/read-pivot.js +9 -3
- package/dist/io/xlsx/read-rich-runs.d.ts +8 -5
- package/dist/io/xlsx/read-rich-runs.js +18 -1
- package/dist/io/xlsx/read-rows.d.ts +18 -7
- package/dist/io/xlsx/read-rows.js +47 -37
- package/dist/io/xlsx/read-styles.d.ts +0 -25
- package/dist/io/xlsx/read-styles.js +39 -34
- package/dist/io/xlsx/read-workbook-xml.d.ts +6 -0
- package/dist/io/xlsx/read-workbook-xml.js +9 -4
- package/dist/io/xlsx/read-worksheet.d.ts +8 -3
- package/dist/io/xlsx/read-worksheet.js +45 -23
- package/dist/io/xlsx/read.d.ts +0 -4
- package/dist/io/xlsx/read.js +17 -21
- package/dist/io/xlsx/relationships.d.ts +3 -0
- package/dist/io/xlsx/relationships.js +4 -0
- package/dist/io/xlsx/row-position.d.ts +3 -1
- package/dist/io/xlsx/row-xml.d.ts +18 -7
- package/dist/io/xlsx/row-xml.js +54 -35
- package/dist/io/xlsx/sheet-properties.js +68 -42
- package/dist/io/xlsx/strict-parts.d.ts +7 -0
- package/dist/io/xlsx/strict-parts.js +171 -0
- package/dist/io/xlsx/style-elements.d.ts +0 -2
- package/dist/io/xlsx/style-elements.js +9 -29
- package/dist/io/xlsx/styles.d.ts +5 -0
- package/dist/io/xlsx/styles.js +11 -5
- package/dist/io/xlsx/tables.d.ts +10 -2
- package/dist/io/xlsx/tables.js +49 -42
- package/dist/io/xlsx/theme-xml.d.ts +6 -0
- package/dist/io/xlsx/theme-xml.js +16 -19
- package/dist/io/xlsx/threaded-comments.d.ts +6 -1
- package/dist/io/xlsx/threaded-comments.js +14 -7
- package/dist/io/xlsx/workbook-xml.d.ts +13 -24
- package/dist/io/xlsx/workbook-xml.js +15 -33
- package/dist/io/xlsx/worksheet-xml.d.ts +5 -2
- package/dist/io/xlsx/worksheet-xml.js +37 -69
- package/dist/io/xlsx/write-stream.d.ts +28 -6
- package/dist/io/xlsx/write-stream.js +42 -17
- package/dist/io/xlsx/write.d.ts +11 -4
- package/dist/io/xlsx/write.js +153 -90
- package/dist/rel-type.d.ts +9 -0
- package/dist/rel-type.js +3 -0
- package/dist/vba/bytes.d.ts +2 -0
- package/dist/vba/bytes.js +11 -0
- package/dist/vba/cfb-format.d.ts +47 -0
- package/dist/vba/cfb-format.js +34 -0
- package/dist/vba/cfb-writer.d.ts +1 -2
- package/dist/vba/cfb-writer.js +33 -36
- package/dist/vba/cfb.d.ts +9 -4
- package/dist/vba/cfb.js +55 -43
- package/dist/vba/dir-records.d.ts +6 -0
- package/dist/vba/dir-records.js +10 -2
- package/dist/vba/errors.d.ts +4 -4
- package/dist/vba/index.d.ts +0 -2
- package/dist/vba/index.js +0 -2
- package/dist/vba/ms-ovba.d.ts +6 -2
- package/dist/vba/ms-ovba.js +15 -6
- package/dist/vba/project-editor.js +102 -87
- package/dist/vba/project.d.ts +4 -0
- package/dist/vba/project.js +12 -6
- package/dist/vba/vba-encoding.d.ts +4 -10
- package/dist/vba/vba-encoding.js +2 -15
- package/dist/xml/xml-read.d.ts +29 -1
- package/dist/xml/xml-read.js +56 -0
- package/dist/xml/xml-scan.d.ts +13 -0
- package/dist/xml/xml-scan.js +58 -7
- package/dist/xml/xml.d.ts +33 -1
- package/dist/xml/xml.js +22 -0
- package/package.json +5 -4
- package/skills/ts-xlsx-upstream/SKILL.md +21 -13
- package/dist/core/modern-functions.d.ts +0 -1
- package/dist/io/xlsx/cell-style-resolution.d.ts +0 -25
- package/dist/io/xlsx/cell-style-resolution.js +0 -29
- package/dist/io/xlsx/column-budget.d.ts +0 -26
- package/dist/io/xlsx/read-repair.d.ts +0 -26
- package/dist/io/xlsx/read-repair.js +0 -39
|
@@ -2,18 +2,30 @@ import { type ClonePlan } from './clone.ts';
|
|
|
2
2
|
import type { AssertNever } from './internal.ts';
|
|
3
3
|
import { type Color } from './style.ts';
|
|
4
4
|
import type { DifferentialStyle } from './workbook-styles.ts';
|
|
5
|
-
/**
|
|
6
|
-
|
|
5
|
+
/**
|
|
6
|
+
* How a {@link CfValueObject} reads its `value`: `ST_CfvoType` verbatim, with the `autoMin` and
|
|
7
|
+
* `autoMax` the 2009 extension adds.
|
|
8
|
+
*/
|
|
9
|
+
export type CfValueObjectType = 'num' | 'percent' | 'max' | 'min' | 'percentile' | 'formula' | 'autoMin' | 'autoMax';
|
|
7
10
|
/** Narrow a raw `<cfvo type>` token to a known {@link CfValueObjectType}. */
|
|
8
11
|
export declare const isCfValueObjectType: (value: string) => value is CfValueObjectType;
|
|
9
12
|
/**
|
|
10
13
|
* One anchor of a colour-scale, data-bar, or icon-set scale: a "conditional format value object".
|
|
11
14
|
* `type` names how `value` is read: a literal `num`, a `percent`/`percentile` of the range, a
|
|
12
15
|
* `formula`, or the range's own `min`/`max` (which carry no value).
|
|
16
|
+
*
|
|
17
|
+
* `autoMin` and `autoMax` are the automatic anchors Excel 2010 gives a new data bar, and carry no value
|
|
18
|
+
* either. Only the extension form spells them, so a data bar using one is written with its extension,
|
|
19
|
+
* its classic element saying `min` or `max` in their place as Excel's does.
|
|
13
20
|
*/
|
|
14
21
|
export interface CfValueObject {
|
|
15
22
|
type: CfValueObjectType;
|
|
16
23
|
value?: number | string;
|
|
24
|
+
/**
|
|
25
|
+
* Whether a value equal to this threshold reaches it. `false` makes an icon set's threshold a strict
|
|
26
|
+
* `>` rather than the `>=` the schema defaults to.
|
|
27
|
+
*/
|
|
28
|
+
gte?: boolean;
|
|
17
29
|
}
|
|
18
30
|
/**
|
|
19
31
|
* What a rule tests, as `<cfRule type>` carries it: `ST_CfType` verbatim.
|
|
@@ -40,21 +52,49 @@ export declare const isConditionalFormattingOperator: (value: string) => value i
|
|
|
40
52
|
export type CfTimePeriod = 'today' | 'yesterday' | 'tomorrow' | 'last7Days' | 'thisMonth' | 'lastMonth' | 'nextMonth' | 'thisWeek' | 'lastWeek' | 'nextWeek';
|
|
41
53
|
/** Narrow a raw `<cfRule timePeriod>` token to a known {@link CfTimePeriod}. */
|
|
42
54
|
export declare const isCfTimePeriod: (value: string) => value is CfTimePeriod;
|
|
55
|
+
/**
|
|
56
|
+
* Which way a data bar grows, as `ST_DataBarDirection` enumerates it: `context` follows the sheet's
|
|
57
|
+
* reading direction.
|
|
58
|
+
*/
|
|
59
|
+
export type DataBarDirection = 'context' | 'leftToRight' | 'rightToLeft';
|
|
60
|
+
/** Narrow a raw `<x14:dataBar direction>` token to a known {@link DataBarDirection}. */
|
|
61
|
+
export declare const isDataBarDirection: (value: string) => value is DataBarDirection;
|
|
62
|
+
/**
|
|
63
|
+
* Where a data bar draws the axis between its negative and positive bars, as `ST_DataBarAxisPosition`
|
|
64
|
+
* enumerates it: where the values put it, at the middle of the cell, or nowhere, which grows a
|
|
65
|
+
* negative bar the way a positive one grows.
|
|
66
|
+
*/
|
|
67
|
+
export type DataBarAxisPosition = 'automatic' | 'middle' | 'none';
|
|
68
|
+
/** Narrow a raw `<x14:dataBar axisPosition>` token to a known {@link DataBarAxisPosition}. */
|
|
69
|
+
export declare const isDataBarAxisPosition: (value: string) => value is DataBarAxisPosition;
|
|
43
70
|
/**
|
|
44
71
|
* The named icon family an `iconSet` rule draws from, as `ST_IconSetType` enumerates it. The leading
|
|
45
72
|
* digit is the number of icons, which is also how many {@link CfValueObject} anchors the rule needs.
|
|
46
73
|
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* carry them; a file using one states it in the extension, which the library round-trips verbatim.
|
|
74
|
+
* `3Stars`, `3Triangles` and `5Boxes` are the families the 2009 extension added. The classic
|
|
75
|
+
* `<iconSet>` element cannot name them, so a rule drawing from one is written in the extension form.
|
|
50
76
|
*/
|
|
51
|
-
export type IconSetType = '3Arrows' | '3ArrowsGray' | '3Flags' | '3TrafficLights1' | '3TrafficLights2' | '3Signs' | '3Symbols' | '3Symbols2' | '4Arrows' | '4ArrowsGray' | '4RedToBlack' | '4Rating' | '4TrafficLights' | '5Arrows' | '5ArrowsGray' | '5Rating' | '5Quarters';
|
|
77
|
+
export type IconSetType = '3Arrows' | '3ArrowsGray' | '3Flags' | '3TrafficLights1' | '3TrafficLights2' | '3Signs' | '3Symbols' | '3Symbols2' | '4Arrows' | '4ArrowsGray' | '4RedToBlack' | '4Rating' | '4TrafficLights' | '5Arrows' | '5ArrowsGray' | '5Rating' | '5Quarters' | '3Stars' | '3Triangles' | '5Boxes';
|
|
52
78
|
/** Narrow a raw `<iconSet iconSet>` token to a known {@link IconSetType}. */
|
|
53
79
|
export declare const isIconSetType: (value: string) => value is IconSetType;
|
|
80
|
+
/** The families only the 2009 extension can name: a rule drawing from one is written there. */
|
|
81
|
+
export declare function isExtensionIconSet(iconSet: IconSetType): boolean;
|
|
82
|
+
/** The family a custom icon comes from: any {@link IconSetType}, or `NoIcons` for a threshold showing none. */
|
|
83
|
+
export type CfIconSetType = IconSetType | 'NoIcons';
|
|
84
|
+
/** Narrow a raw `<x14:cfIcon iconSet>` token to a known {@link CfIconSetType}. */
|
|
85
|
+
export declare function isCfIconSetType(value: string): value is CfIconSetType;
|
|
86
|
+
/** One threshold's icon in a custom icon set: icon number `iconId`, from 0, of the family `iconSet`. */
|
|
87
|
+
export interface CfIcon {
|
|
88
|
+
iconSet: CfIconSetType;
|
|
89
|
+
iconId: number;
|
|
90
|
+
}
|
|
54
91
|
/**
|
|
55
92
|
* A single conditional-formatting rule. `type` is the OOXML cfRule type; the remaining fields carry
|
|
56
93
|
* the operands that type needs and are absent otherwise. A rule the library does not model in depth
|
|
57
94
|
* still preserves `type`, `priority`, `operator`, `formulae`, and `dxfId` across a round-trip.
|
|
95
|
+
*
|
|
96
|
+
* A data bar's facets marked as x14 properties have no place in the classic `<dataBar>` element, so a
|
|
97
|
+
* bar carrying any of them, or an automatic anchor, is written with a linked `<x14:dataBar>` as well.
|
|
58
98
|
*/
|
|
59
99
|
export interface ConditionalFormattingRule {
|
|
60
100
|
type: ConditionalFormattingType;
|
|
@@ -78,14 +118,48 @@ export interface ConditionalFormattingRule {
|
|
|
78
118
|
color?: Color;
|
|
79
119
|
/** A colorScale's colours, one per {@link cfvo}. */
|
|
80
120
|
colors?: Color[];
|
|
81
|
-
/** A dataBar's
|
|
121
|
+
/** A dataBar's shortest bar, as a percentage of the cell. */
|
|
122
|
+
minLength?: number;
|
|
123
|
+
/** A dataBar's longest bar, as a percentage of the cell. */
|
|
124
|
+
maxLength?: number;
|
|
125
|
+
/** A dataBar's gradient-fill flag. An x14 property. */
|
|
82
126
|
gradient?: boolean;
|
|
83
|
-
/**
|
|
127
|
+
/** Whether a dataBar has a border, in {@link borderColor}. An x14 property. */
|
|
128
|
+
border?: boolean;
|
|
129
|
+
/** A dataBar's border colour. An x14 property. */
|
|
130
|
+
borderColor?: Color;
|
|
131
|
+
/** Which way a dataBar grows. An x14 property. */
|
|
132
|
+
direction?: DataBarDirection;
|
|
133
|
+
/** A dataBar's fill colour for negative values. An x14 property. */
|
|
84
134
|
negativeFillColor?: Color;
|
|
135
|
+
/** A dataBar's border colour for negative values. An x14 property. */
|
|
136
|
+
negativeBorderColor?: Color;
|
|
137
|
+
/**
|
|
138
|
+
* Whether a dataBar fills a negative bar in its positive colour rather than
|
|
139
|
+
* {@link negativeFillColor}. An x14 property.
|
|
140
|
+
*/
|
|
141
|
+
negativeBarColorSameAsPositive?: boolean;
|
|
142
|
+
/**
|
|
143
|
+
* Whether a dataBar borders a negative bar in its positive border colour rather than
|
|
144
|
+
* {@link negativeBorderColor}. An x14 property.
|
|
145
|
+
*/
|
|
146
|
+
negativeBarBorderColorSameAsPositive?: boolean;
|
|
147
|
+
/** Where a dataBar draws the axis between negative and positive bars. An x14 property. */
|
|
148
|
+
axisPosition?: DataBarAxisPosition;
|
|
85
149
|
/** A dataBar's axis colour (the zero line between positive and negative bars). An x14 property. */
|
|
86
150
|
axisColor?: Color;
|
|
87
151
|
/** An iconSet's named icon family (e.g. `3TrafficLights1`). */
|
|
88
152
|
iconSet?: IconSetType;
|
|
153
|
+
/**
|
|
154
|
+
* A custom iconSet's icons, one per {@link cfvo} threshold in order, each replacing the icon
|
|
155
|
+
* {@link iconSet} would show there. Only the extension form carries them, so a rule with icons is
|
|
156
|
+
* written in it.
|
|
157
|
+
*/
|
|
158
|
+
icons?: CfIcon[];
|
|
159
|
+
/** iconSet: the icons in reverse order, the highest threshold showing the family's first icon. */
|
|
160
|
+
reverse?: boolean;
|
|
161
|
+
/** dataBar / iconSet: whether the cell still shows its value beside the bar or icon. */
|
|
162
|
+
showValue?: boolean;
|
|
89
163
|
/** top10 rank cutoff. */
|
|
90
164
|
rank?: number;
|
|
91
165
|
/** top10: the rank is a percentage rather than a count. */
|
|
@@ -101,16 +175,30 @@ export interface ConditionalFormattingRule {
|
|
|
101
175
|
/** timePeriod window (`today`, `lastWeek`, …). */
|
|
102
176
|
timePeriod?: CfTimePeriod;
|
|
103
177
|
}
|
|
104
|
-
/**
|
|
178
|
+
/**
|
|
179
|
+
* A set of rules bound to the range(s) they cover. `ref` is an OOXML `sqref`: one or more
|
|
105
180
|
* space-separated areas (`"A1:C1 A3:C3 A5:C5"`), the shape Excel writes when one rule is applied to
|
|
106
|
-
* several non-contiguous selections at once.
|
|
181
|
+
* several non-contiguous selections at once.
|
|
182
|
+
*
|
|
183
|
+
* `extended` marks a set stored in the 2009 extension form (`<x14:conditionalFormatting>` inside the
|
|
184
|
+
* worksheet `<extLst>`), where Excel puts a rule whose formula reaches another sheet. The reader sets
|
|
185
|
+
* it for a set found in that form so a round-trip writes the set back there. A rule only that form can
|
|
186
|
+
* express, one drawing from a 2009 icon family or carrying custom icons, is written there whatever the
|
|
187
|
+
* flag says.
|
|
188
|
+
*/
|
|
107
189
|
export interface ConditionalFormatting {
|
|
108
190
|
ref: string;
|
|
109
191
|
rules: ConditionalFormattingRule[];
|
|
192
|
+
extended?: boolean;
|
|
110
193
|
}
|
|
111
194
|
/** A defensive deep copy, so a stored conditional formatting never aliases the caller's object nor
|
|
112
195
|
* any of its nested arrays (rules, formulae, cfvo, colours) or the differential style. */
|
|
113
196
|
export declare function cloneConditionalFormatting(cf: ConditionalFormatting): ConditionalFormatting;
|
|
197
|
+
/**
|
|
198
|
+
* Whether a rule needs the extension form: it names a family or custom icons the classic element has
|
|
199
|
+
* no way to spell.
|
|
200
|
+
*/
|
|
201
|
+
export declare function ruleNeedsExtension(rule: ConditionalFormattingRule): boolean;
|
|
114
202
|
declare const RULE_CLONE: ClonePlan<ConditionalFormattingRule>;
|
|
115
203
|
export type EveryRuleFieldIsCloned = AssertNever<Exclude<keyof Required<ConditionalFormattingRule>, keyof typeof RULE_CLONE>>;
|
|
116
204
|
declare const STYLE_CLONE: ClonePlan<DifferentialStyle>;
|
|
@@ -8,6 +8,8 @@ export const isCfValueObjectType = tokenSet({
|
|
|
8
8
|
min: true,
|
|
9
9
|
percentile: true,
|
|
10
10
|
formula: true,
|
|
11
|
+
autoMin: true,
|
|
12
|
+
autoMax: true,
|
|
11
13
|
});
|
|
12
14
|
export const isConditionalFormattingType = tokenSet({
|
|
13
15
|
expression: true,
|
|
@@ -55,6 +57,16 @@ export const isCfTimePeriod = tokenSet({
|
|
|
55
57
|
lastWeek: true,
|
|
56
58
|
nextWeek: true,
|
|
57
59
|
});
|
|
60
|
+
export const isDataBarDirection = tokenSet({
|
|
61
|
+
context: true,
|
|
62
|
+
leftToRight: true,
|
|
63
|
+
rightToLeft: true,
|
|
64
|
+
});
|
|
65
|
+
export const isDataBarAxisPosition = tokenSet({
|
|
66
|
+
automatic: true,
|
|
67
|
+
middle: true,
|
|
68
|
+
none: true,
|
|
69
|
+
});
|
|
58
70
|
export const isIconSetType = tokenSet({
|
|
59
71
|
'3Arrows': true,
|
|
60
72
|
'3ArrowsGray': true,
|
|
@@ -73,9 +85,26 @@ export const isIconSetType = tokenSet({
|
|
|
73
85
|
'5ArrowsGray': true,
|
|
74
86
|
'5Rating': true,
|
|
75
87
|
'5Quarters': true,
|
|
88
|
+
'3Stars': true,
|
|
89
|
+
'3Triangles': true,
|
|
90
|
+
'5Boxes': true,
|
|
76
91
|
});
|
|
92
|
+
export function isExtensionIconSet(iconSet) {
|
|
93
|
+
return iconSet === '3Stars' || iconSet === '3Triangles' || iconSet === '5Boxes';
|
|
94
|
+
}
|
|
95
|
+
export function isCfIconSetType(value) {
|
|
96
|
+
return value === 'NoIcons' || isIconSetType(value);
|
|
97
|
+
}
|
|
77
98
|
export function cloneConditionalFormatting(cf) {
|
|
78
|
-
|
|
99
|
+
const copy = { ref: cf.ref, rules: cf.rules.map(cloneRule) };
|
|
100
|
+
if (cf.extended !== undefined)
|
|
101
|
+
copy.extended = cf.extended;
|
|
102
|
+
return copy;
|
|
103
|
+
}
|
|
104
|
+
export function ruleNeedsExtension(rule) {
|
|
105
|
+
return (rule.type === 'iconSet' &&
|
|
106
|
+
((rule.iconSet !== undefined && isExtensionIconSet(rule.iconSet)) ||
|
|
107
|
+
(rule.icons !== undefined && rule.icons.length > 0)));
|
|
79
108
|
}
|
|
80
109
|
const RULE_CLONE = {
|
|
81
110
|
type: 'value',
|
|
@@ -89,10 +118,22 @@ const RULE_CLONE = {
|
|
|
89
118
|
cfvo: 'records',
|
|
90
119
|
color: 'record',
|
|
91
120
|
colors: 'records',
|
|
121
|
+
minLength: 'value',
|
|
122
|
+
maxLength: 'value',
|
|
92
123
|
gradient: 'value',
|
|
124
|
+
border: 'value',
|
|
125
|
+
borderColor: 'record',
|
|
126
|
+
direction: 'value',
|
|
93
127
|
negativeFillColor: 'record',
|
|
128
|
+
negativeBorderColor: 'record',
|
|
129
|
+
negativeBarColorSameAsPositive: 'value',
|
|
130
|
+
negativeBarBorderColorSameAsPositive: 'value',
|
|
131
|
+
axisPosition: 'value',
|
|
94
132
|
axisColor: 'record',
|
|
95
133
|
iconSet: 'value',
|
|
134
|
+
icons: 'records',
|
|
135
|
+
reverse: 'value',
|
|
136
|
+
showValue: 'value',
|
|
96
137
|
rank: 'value',
|
|
97
138
|
percent: 'value',
|
|
98
139
|
bottom: 'value',
|
|
@@ -35,6 +35,11 @@ export declare class DataValidationOverlay {
|
|
|
35
35
|
* inside a deleted span is dropped with them.
|
|
36
36
|
*/
|
|
37
37
|
shift(splice: AxisSplice): void;
|
|
38
|
+
/**
|
|
39
|
+
* Rewrite every rule's formula operands through `rewrite`, keeping a rule whose operands did not
|
|
40
|
+
* change as the same object. A numeric operand is not a formula and passes through.
|
|
41
|
+
*/
|
|
42
|
+
mapFormulas(rewrite: (formula: string) => string): void;
|
|
38
43
|
/** Drop every validation, leaving the overlay empty. */
|
|
39
44
|
clear(): void;
|
|
40
45
|
}
|
|
@@ -43,6 +43,21 @@ export class DataValidationOverlay {
|
|
|
43
43
|
replaceContents(this.#entries, entries);
|
|
44
44
|
replaceContents(this.#rects, rects);
|
|
45
45
|
}
|
|
46
|
+
mapFormulas(rewrite) {
|
|
47
|
+
for (const [index, entry] of this.#entries.entries()) {
|
|
48
|
+
const formulae = entry.rule.formulae;
|
|
49
|
+
if (formulae === undefined)
|
|
50
|
+
continue;
|
|
51
|
+
const next = formulae.map((operand) => typeof operand === 'string' ? rewrite(operand) : operand);
|
|
52
|
+
if (next.every((operand, i) => operand === formulae[i]))
|
|
53
|
+
continue;
|
|
54
|
+
const rule = { ...entry.rule, formulae: next };
|
|
55
|
+
this.#entries[index] = { ...entry, rule };
|
|
56
|
+
const indexed = this.#rects[index];
|
|
57
|
+
if (indexed !== undefined)
|
|
58
|
+
this.#rects[index] = { rects: indexed.rects, rule };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
46
61
|
clear() {
|
|
47
62
|
this.#entries.length = 0;
|
|
48
63
|
this.#rects.length = 0;
|
|
@@ -15,6 +15,14 @@ export declare const isDataValidationOperator: (value: string) => value is DataV
|
|
|
15
15
|
export type DataValidationErrorStyle = 'stop' | 'warning' | 'information';
|
|
16
16
|
/** Narrow a raw `<dataValidation errorStyle>` token to a known {@link DataValidationErrorStyle}. */
|
|
17
17
|
export declare const isDataValidationErrorStyle: (value: string) => value is DataValidationErrorStyle;
|
|
18
|
+
/**
|
|
19
|
+
* How the input method editor behaves while a covered cell is edited. Only an East Asian input method
|
|
20
|
+
* acts on it: `hiragana`, the katakana, alpha and hangul widths switch its mode, `on` and `off` turn it
|
|
21
|
+
* on and off, `disabled` turns it off and keeps it off, and `noControl` leaves it as the user had it.
|
|
22
|
+
*/
|
|
23
|
+
export type DataValidationImeMode = 'noControl' | 'off' | 'on' | 'disabled' | 'hiragana' | 'fullKatakana' | 'halfKatakana' | 'fullAlpha' | 'halfAlpha' | 'fullHangul' | 'halfHangul';
|
|
24
|
+
/** Narrow a raw `<dataValidation imeMode>` token to a known {@link DataValidationImeMode}. */
|
|
25
|
+
export declare const isDataValidationImeMode: (value: string) => value is DataValidationImeMode;
|
|
18
26
|
/** One validation rule. `formulae` holds the operand(s), `formula1` then optional `formula2`: a
|
|
19
27
|
* numeric literal is stored as a number, while a cell reference, defined name, or list source keeps
|
|
20
28
|
* its verbatim string. */
|
|
@@ -23,9 +31,17 @@ export interface DataValidation {
|
|
|
23
31
|
operator?: DataValidationOperator;
|
|
24
32
|
formulae?: (string | number)[];
|
|
25
33
|
allowBlank?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Hide the in-cell dropdown arrow of a `list` rule while still enforcing the list, the usual way to
|
|
36
|
+
* validate against a list without offering a picker. Stored as `showDropDown="1"`, an attribute
|
|
37
|
+
* whose name says the opposite of what it does.
|
|
38
|
+
*/
|
|
39
|
+
suppressDropDown?: boolean;
|
|
26
40
|
showInputMessage?: boolean;
|
|
27
41
|
showErrorMessage?: boolean;
|
|
28
42
|
errorStyle?: DataValidationErrorStyle;
|
|
43
|
+
/** How the input method editor behaves in a covered cell; absent leaves it as the user had it. */
|
|
44
|
+
imeMode?: DataValidationImeMode;
|
|
29
45
|
error?: string;
|
|
30
46
|
errorTitle?: string;
|
|
31
47
|
prompt?: string;
|
|
@@ -25,14 +25,29 @@ export const isDataValidationErrorStyle = tokenSet({
|
|
|
25
25
|
warning: true,
|
|
26
26
|
information: true,
|
|
27
27
|
});
|
|
28
|
+
export const isDataValidationImeMode = tokenSet({
|
|
29
|
+
noControl: true,
|
|
30
|
+
off: true,
|
|
31
|
+
on: true,
|
|
32
|
+
disabled: true,
|
|
33
|
+
hiragana: true,
|
|
34
|
+
fullKatakana: true,
|
|
35
|
+
halfKatakana: true,
|
|
36
|
+
fullAlpha: true,
|
|
37
|
+
halfAlpha: true,
|
|
38
|
+
fullHangul: true,
|
|
39
|
+
halfHangul: true,
|
|
40
|
+
});
|
|
28
41
|
const RULE_CLONE = {
|
|
29
42
|
type: 'value',
|
|
30
43
|
operator: 'value',
|
|
31
44
|
formulae: 'values',
|
|
32
45
|
allowBlank: 'value',
|
|
46
|
+
suppressDropDown: 'value',
|
|
33
47
|
showInputMessage: 'value',
|
|
34
48
|
showErrorMessage: 'value',
|
|
35
49
|
errorStyle: 'value',
|
|
50
|
+
imeMode: 'value',
|
|
36
51
|
error: 'value',
|
|
37
52
|
errorTitle: 'value',
|
|
38
53
|
prompt: 'value',
|
package/dist/core/date-format.js
CHANGED
|
@@ -48,7 +48,8 @@ function formatRuns(code) {
|
|
|
48
48
|
continue;
|
|
49
49
|
}
|
|
50
50
|
const fraction = /^\.0+/.exec(code.slice(i));
|
|
51
|
-
|
|
51
|
+
const previous = runs.at(-1);
|
|
52
|
+
if (fraction !== null && previous?.kind === 'placeholder' && previous.text === 's') {
|
|
52
53
|
runs.push({ kind: 'placeholder', text: '.0', count: fraction[0].length - 1 });
|
|
53
54
|
i += fraction[0].length - 1;
|
|
54
55
|
continue;
|
|
@@ -64,8 +65,12 @@ function formatRuns(code) {
|
|
|
64
65
|
}
|
|
65
66
|
const meridiem = /^(AM\/PM|A\/P)/i.exec(code.slice(i));
|
|
66
67
|
if (meridiem !== null) {
|
|
67
|
-
|
|
68
|
-
|
|
68
|
+
const token = meridiem[0];
|
|
69
|
+
if (token.length === 3)
|
|
70
|
+
runs.push({ kind: 'placeholder', text: 'a/p', count: 3, written: token });
|
|
71
|
+
else
|
|
72
|
+
runs.push({ kind: 'placeholder', text: 'am/pm', count: token.length });
|
|
73
|
+
i += token.length - 1;
|
|
69
74
|
continue;
|
|
70
75
|
}
|
|
71
76
|
literal(ch);
|
|
@@ -100,7 +105,7 @@ export function formatSerialDate(date, code, utc) {
|
|
|
100
105
|
const hour24 = utc ? date.getUTCHours() : date.getHours();
|
|
101
106
|
const minute = utc ? date.getUTCMinutes() : date.getMinutes();
|
|
102
107
|
const second = utc ? date.getUTCSeconds() : date.getSeconds();
|
|
103
|
-
const twelveHour = runs.some((run) => run.kind === 'placeholder' && run.text === 'am/pm');
|
|
108
|
+
const twelveHour = runs.some((run) => run.kind === 'placeholder' && (run.text === 'am/pm' || run.text === 'a/p'));
|
|
104
109
|
const hour = twelveHour ? hour24 % 12 || 12 : hour24;
|
|
105
110
|
const pad = (value, width) => String(value).padStart(width, '0');
|
|
106
111
|
let out = '';
|
|
@@ -152,6 +157,11 @@ export function formatSerialDate(date, code, utc) {
|
|
|
152
157
|
case 'am/pm':
|
|
153
158
|
out += hour24 < 12 ? 'AM' : 'PM';
|
|
154
159
|
break;
|
|
160
|
+
case 'a/p': {
|
|
161
|
+
const written = run.written ?? 'A/P';
|
|
162
|
+
out += hour24 < 12 ? written.charAt(0) : written.charAt(2);
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
155
165
|
default:
|
|
156
166
|
break;
|
|
157
167
|
}
|
package/dist/core/date.js
CHANGED
|
@@ -7,7 +7,7 @@ export function dateToSerial(date, epoch) {
|
|
|
7
7
|
if (epoch === 1904)
|
|
8
8
|
return (date.getTime() - EPOCH_1904_UTC) / MS_PER_DAY;
|
|
9
9
|
const days = (date.getTime() - EPOCH_1900_UTC) / MS_PER_DAY;
|
|
10
|
-
return days <= PHANTOM_SERIAL ? days - 1 : days;
|
|
10
|
+
return Math.floor(days) <= PHANTOM_SERIAL ? days - 1 : days;
|
|
11
11
|
}
|
|
12
12
|
export function serialToDate(serial, epoch) {
|
|
13
13
|
if (epoch === 1904)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { AxisSplice } from './grid-shift.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Shift every relative reference in a formula by `colDelta` columns and `rowDelta` rows, leaving
|
|
4
|
+
* absolute (`$`-anchored) axes fixed. This is how a shared-formula clone recovers its own formula from
|
|
5
|
+
* the master's: a master `A1*2` shared one row down reads back as `A2*2`, `$A$1*B1` shared one row and
|
|
6
|
+
* one column across as `$A$1*C2`, and `COUNTIF(A:A,A1)` shared one column right as `COUNTIF(B:B,B1)`.
|
|
7
|
+
* Whole-column and whole-row ranges shift at each relative end, as cells do. String literals,
|
|
8
|
+
* single-quoted sheet names, and bracketed structured references are copied verbatim, and a
|
|
9
|
+
* sheet-qualified reference shifts while its sheet name is untouched. Function names and defined names
|
|
10
|
+
* are not reference-shaped, so they pass through.
|
|
11
|
+
*/
|
|
12
|
+
export declare function translateFormula(formula: string, colDelta: number, rowDelta: number): string;
|
|
13
|
+
/** A splice as a formula sees it: which sheet's lines moved, and how. */
|
|
14
|
+
export interface SheetSplice {
|
|
15
|
+
/** The spliced sheet's name, matched in any case. */
|
|
16
|
+
readonly sheet: string;
|
|
17
|
+
readonly splice: AxisSplice;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Rewrite a formula's references for a splice of `edit.sheet`. `home` is the sheet the formula belongs
|
|
21
|
+
* to, whose name an unqualified reference means; `undefined` for a formula that has none, such as a
|
|
22
|
+
* workbook-scoped defined name, whose references to a sheet are always qualified.
|
|
23
|
+
*
|
|
24
|
+
* Returns the formula unchanged, as the same string, when nothing in it moved.
|
|
25
|
+
*/
|
|
26
|
+
export declare function spliceFormula(formula: string, home: string | undefined, edit: SheetSplice): string;
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { MAX_COLUMN, MAX_ROW, numberToColumn, tryColumnToNumber } from './address.js';
|
|
2
|
+
import { scanFormula, skipOpaque } from './formula-scan.js';
|
|
3
|
+
import { REF_ERROR } from './value.js';
|
|
4
|
+
const SHIFTABLE_REFERENCE = /(?<![A-Za-z0-9_.])(?:(\$?)([A-Z]{1,3}):(\$?)([A-Z]{1,3})|(\$?)([0-9]{1,7}):(\$?)([0-9]{1,7})|(\$?)([A-Z]{1,3})(\$?)([0-9]{1,7}))(?![A-Za-z0-9_.!(])/g;
|
|
5
|
+
export function translateFormula(formula, colDelta, rowDelta) {
|
|
6
|
+
if (colDelta === 0 && rowDelta === 0)
|
|
7
|
+
return formula;
|
|
8
|
+
const column = (anchor, letters) => {
|
|
9
|
+
const decoded = tryColumnToNumber(letters);
|
|
10
|
+
if (decoded === undefined)
|
|
11
|
+
return undefined;
|
|
12
|
+
const col = anchor === '$' ? decoded : decoded + colDelta;
|
|
13
|
+
return col < 1 || col > MAX_COLUMN ? undefined : `${anchor}${numberToColumn(col)}`;
|
|
14
|
+
};
|
|
15
|
+
const row = (anchor, digits) => {
|
|
16
|
+
const index = anchor === '$' ? Number(digits) : Number(digits) + rowDelta;
|
|
17
|
+
return index < 1 || index > MAX_ROW ? undefined : `${anchor}${index}`;
|
|
18
|
+
};
|
|
19
|
+
const range = (first, last) => first === undefined || last === undefined ? REF_ERROR : `${first}:${last}`;
|
|
20
|
+
return scanFormula(formula, (code) => code.replace(SHIFTABLE_REFERENCE, (_match, firstColumnAnchor, firstColumn, lastColumnAnchor, lastColumn, firstRowAnchor, firstRow, lastRowAnchor, lastRow, cellColumnAnchor, cellColumn, cellRowAnchor, cellRow) => {
|
|
21
|
+
if (firstColumn !== undefined && lastColumn !== undefined) {
|
|
22
|
+
return range(column(firstColumnAnchor ?? '', firstColumn), column(lastColumnAnchor ?? '', lastColumn));
|
|
23
|
+
}
|
|
24
|
+
if (firstRow !== undefined && lastRow !== undefined) {
|
|
25
|
+
return range(row(firstRowAnchor ?? '', firstRow), row(lastRowAnchor ?? '', lastRow));
|
|
26
|
+
}
|
|
27
|
+
const shiftedColumn = column(cellColumnAnchor ?? '', cellColumn ?? '');
|
|
28
|
+
const shiftedRow = row(cellRowAnchor ?? '', cellRow ?? '');
|
|
29
|
+
return shiftedColumn === undefined || shiftedRow === undefined
|
|
30
|
+
? REF_ERROR
|
|
31
|
+
: `${shiftedColumn}${shiftedRow}`;
|
|
32
|
+
}));
|
|
33
|
+
}
|
|
34
|
+
const REFERENCE = /(?:(\$?)([A-Z]{1,3})(\$?)(\d{1,7}):(\$?)([A-Z]{1,3})(\$?)(\d{1,7})|(\$?)([A-Z]{1,3})(\$?)(\d{1,7})|(\$?)([A-Z]{1,3}):(\$?)([A-Z]{1,3})|(\$?)(\d{1,7}):(\$?)(\d{1,7}))(?![\p{L}\p{N}_.(![])/uy;
|
|
35
|
+
const NAME_CHAR = /[\p{L}\p{N}_.\\]/u;
|
|
36
|
+
export function spliceFormula(formula, home, edit) {
|
|
37
|
+
const target = edit.sheet.toLowerCase();
|
|
38
|
+
const homeIsTarget = home !== undefined && home.toLowerCase() === target;
|
|
39
|
+
let out = '';
|
|
40
|
+
let i = 0;
|
|
41
|
+
const n = formula.length;
|
|
42
|
+
const atTokenStart = (index) => index === 0 || !NAME_CHAR.test(formula[index - 1] ?? '');
|
|
43
|
+
const reference = (moves) => {
|
|
44
|
+
REFERENCE.lastIndex = i;
|
|
45
|
+
const match = REFERENCE.exec(formula);
|
|
46
|
+
if (match === null)
|
|
47
|
+
return false;
|
|
48
|
+
out += moves ? moved(match, edit.splice) : match[0];
|
|
49
|
+
i = REFERENCE.lastIndex;
|
|
50
|
+
return true;
|
|
51
|
+
};
|
|
52
|
+
while (i < n) {
|
|
53
|
+
const ch = formula[i] ?? '';
|
|
54
|
+
if (ch === '"') {
|
|
55
|
+
const end = skipOpaque(formula, i);
|
|
56
|
+
out += formula.slice(i, end);
|
|
57
|
+
i = end;
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (ch === '[') {
|
|
61
|
+
const end = skipOpaque(formula, i);
|
|
62
|
+
out += formula.slice(i, end);
|
|
63
|
+
i = end;
|
|
64
|
+
const name = nameRun(formula, i);
|
|
65
|
+
if (name > i && formula[name] === '!') {
|
|
66
|
+
out += formula.slice(i, name + 1);
|
|
67
|
+
i = name + 1;
|
|
68
|
+
reference(false);
|
|
69
|
+
}
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (ch === "'") {
|
|
73
|
+
const end = skipOpaque(formula, i);
|
|
74
|
+
const quoted = formula.slice(i + 1, end - 1).replaceAll("''", "'");
|
|
75
|
+
out += formula.slice(i, end);
|
|
76
|
+
i = end;
|
|
77
|
+
if (formula[i] !== '!')
|
|
78
|
+
continue;
|
|
79
|
+
out += '!';
|
|
80
|
+
i += 1;
|
|
81
|
+
reference(!quoted.includes(':') && !quoted.includes('[') && quoted.toLowerCase() === target);
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (atTokenStart(i) && NAME_CHAR.test(ch)) {
|
|
85
|
+
const end = nameRun(formula, i);
|
|
86
|
+
const next = formula[end];
|
|
87
|
+
if (next === '!') {
|
|
88
|
+
out += formula.slice(i, end + 1);
|
|
89
|
+
const name = formula.slice(i, end);
|
|
90
|
+
i = end + 1;
|
|
91
|
+
reference(name.toLowerCase() === target);
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
if (next === ':') {
|
|
95
|
+
const last = nameRun(formula, end + 1);
|
|
96
|
+
if (last > end + 1 && formula[last] === '!') {
|
|
97
|
+
out += formula.slice(i, last + 1);
|
|
98
|
+
i = last + 1;
|
|
99
|
+
reference(false);
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (reference(homeIsTarget))
|
|
104
|
+
continue;
|
|
105
|
+
out += formula.slice(i, end);
|
|
106
|
+
i = end;
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (atTokenStart(i) && ch === '$' && reference(homeIsTarget))
|
|
110
|
+
continue;
|
|
111
|
+
out += ch;
|
|
112
|
+
i += 1;
|
|
113
|
+
}
|
|
114
|
+
return out === formula ? formula : out;
|
|
115
|
+
}
|
|
116
|
+
function nameRun(formula, start) {
|
|
117
|
+
let j = start;
|
|
118
|
+
while (j < formula.length && NAME_CHAR.test(formula[j] ?? ''))
|
|
119
|
+
j += 1;
|
|
120
|
+
return j;
|
|
121
|
+
}
|
|
122
|
+
function moved(match, splice) {
|
|
123
|
+
const g = (index) => match[index] ?? '';
|
|
124
|
+
const ends = match[2] !== undefined
|
|
125
|
+
? [
|
|
126
|
+
[g(1), g(2), g(3), g(4)],
|
|
127
|
+
[g(5), g(6), g(7), g(8)],
|
|
128
|
+
]
|
|
129
|
+
: match[10] !== undefined
|
|
130
|
+
? [[g(9), g(10), g(11), g(12)]]
|
|
131
|
+
: match[14] !== undefined
|
|
132
|
+
? [
|
|
133
|
+
[g(13), g(14), '', ''],
|
|
134
|
+
[g(15), g(16), '', ''],
|
|
135
|
+
]
|
|
136
|
+
: [
|
|
137
|
+
['', '', g(17), g(18)],
|
|
138
|
+
['', '', g(19), g(20)],
|
|
139
|
+
];
|
|
140
|
+
const rows = splice.axis === 'row';
|
|
141
|
+
const lines = ends.map(([, letters = '', , digits = '']) => (rows ? digits : letters));
|
|
142
|
+
if (lines.includes(''))
|
|
143
|
+
return match[0];
|
|
144
|
+
const [first = 0, last = first] = lines.map((line) => (rows ? Number(line) : columnOf(line)));
|
|
145
|
+
const edges = movedEdges(Math.min(first, last), Math.max(first, last), splice, rows ? MAX_ROW : MAX_COLUMN);
|
|
146
|
+
if (edges === undefined)
|
|
147
|
+
return REF_ERROR;
|
|
148
|
+
const placed = first <= last ? edges : [edges[1], edges[0]];
|
|
149
|
+
return ends
|
|
150
|
+
.map(([columnAnchor = '', letters = '', rowAnchor = '', digits = ''], i) => {
|
|
151
|
+
const at = placed[i] ?? 0;
|
|
152
|
+
return rows
|
|
153
|
+
? `${columnAnchor}${letters}${rowAnchor}${at}`
|
|
154
|
+
: `${columnAnchor}${numberToColumn(at)}${rowAnchor}${digits}`;
|
|
155
|
+
})
|
|
156
|
+
.join(':');
|
|
157
|
+
}
|
|
158
|
+
function columnOf(letters) {
|
|
159
|
+
return tryColumnToNumber(letters) ?? MAX_COLUMN + 1;
|
|
160
|
+
}
|
|
161
|
+
function movedEdges(lo, hi, splice, bound) {
|
|
162
|
+
if (hi > bound)
|
|
163
|
+
return [lo, hi];
|
|
164
|
+
const { start, count, delta } = splice;
|
|
165
|
+
const end = start + count;
|
|
166
|
+
if (lo >= start && hi < end)
|
|
167
|
+
return undefined;
|
|
168
|
+
const top = lo < start ? lo : lo < end ? end + delta : lo + delta;
|
|
169
|
+
const bottom = hi < start ? hi : hi < end ? start - 1 : hi + delta;
|
|
170
|
+
if (top > bound)
|
|
171
|
+
return undefined;
|
|
172
|
+
return [top, Math.min(bottom, bound)];
|
|
173
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export function skipOpaque(formula, index) {
|
|
2
|
+
const opener = formula[index];
|
|
3
|
+
const n = formula.length;
|
|
4
|
+
if (opener === '"' || opener === "'") {
|
|
5
|
+
let j = index + 1;
|
|
6
|
+
while (j < n) {
|
|
7
|
+
if (formula[j] === opener) {
|
|
8
|
+
if (formula[j + 1] === opener) {
|
|
9
|
+
j += 2;
|
|
10
|
+
continue;
|
|
11
|
+
}
|
|
12
|
+
return j + 1;
|
|
13
|
+
}
|
|
14
|
+
j += 1;
|
|
15
|
+
}
|
|
16
|
+
return n;
|
|
17
|
+
}
|
|
18
|
+
if (opener === '[') {
|
|
19
|
+
let depth = 0;
|
|
20
|
+
let j = index;
|
|
21
|
+
while (j < n) {
|
|
22
|
+
const ch = formula[j];
|
|
23
|
+
if (ch === "'") {
|
|
24
|
+
j += 2;
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
if (ch === '[')
|
|
28
|
+
depth += 1;
|
|
29
|
+
else if (ch === ']') {
|
|
30
|
+
depth -= 1;
|
|
31
|
+
if (depth === 0)
|
|
32
|
+
return j + 1;
|
|
33
|
+
}
|
|
34
|
+
j += 1;
|
|
35
|
+
}
|
|
36
|
+
return n;
|
|
37
|
+
}
|
|
38
|
+
return index;
|
|
39
|
+
}
|
|
40
|
+
export function scanFormula(formula, transform) {
|
|
41
|
+
let out = '';
|
|
42
|
+
let codeStart = 0;
|
|
43
|
+
let i = 0;
|
|
44
|
+
const n = formula.length;
|
|
45
|
+
while (i < n) {
|
|
46
|
+
const past = skipOpaque(formula, i);
|
|
47
|
+
if (past > i) {
|
|
48
|
+
out += transform(formula.slice(codeStart, i));
|
|
49
|
+
out += formula.slice(i, past);
|
|
50
|
+
i = past;
|
|
51
|
+
codeStart = past;
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
i += 1;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return out + transform(formula.slice(codeStart));
|
|
58
|
+
}
|