@rowsncolumns/spreadsheet 10.4.1 → 10.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/components/filter-box/date-tree.d.ts +23 -5
- package/dist/cjs/components/filter-box/date-tree.d.ts.map +1 -1
- package/dist/cjs/components/filter-box/date-tree.js +76 -11
- package/dist/cjs/components/filter-box/filter-by-condition.d.ts +26 -1
- package/dist/cjs/components/filter-box/filter-by-condition.d.ts.map +1 -1
- package/dist/cjs/components/filter-box/filter-by-condition.js +108 -9
- package/dist/cjs/components/filter-box/index.d.ts.map +1 -1
- package/dist/cjs/components/filter-box/index.js +7 -2
- package/dist/cjs/components/formula-input/index.d.ts.map +1 -1
- package/dist/cjs/components/formula-input/index.js +26 -4
- package/dist/cjs/components/toolbar/table-style-editor.d.ts +85 -0
- package/dist/cjs/components/toolbar/table-style-editor.d.ts.map +1 -0
- package/dist/cjs/components/toolbar/table-style-editor.js +235 -0
- package/dist/cjs/components/toolbar/table-style-selector.d.ts +7 -7
- package/dist/cjs/components/toolbar/table-style-selector.d.ts.map +1 -1
- package/dist/cjs/components/toolbar/table-style-selector.js +77 -87
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/cjs/types.d.ts +10 -0
- package/dist/cjs/types.d.ts.map +1 -1
- package/dist/esm/components/filter-box/date-tree.d.ts +23 -5
- package/dist/esm/components/filter-box/date-tree.d.ts.map +1 -1
- package/dist/esm/components/filter-box/date-tree.js +62 -10
- package/dist/esm/components/filter-box/filter-by-condition.d.ts +26 -1
- package/dist/esm/components/filter-box/filter-by-condition.d.ts.map +1 -1
- package/dist/esm/components/filter-box/filter-by-condition.js +78 -12
- package/dist/esm/components/filter-box/index.d.ts.map +1 -1
- package/dist/esm/components/filter-box/index.js +7 -2
- package/dist/esm/components/formula-input/index.d.ts.map +1 -1
- package/dist/esm/components/formula-input/index.js +26 -4
- package/dist/esm/components/toolbar/table-style-editor.d.ts +85 -0
- package/dist/esm/components/toolbar/table-style-editor.d.ts.map +1 -0
- package/dist/esm/components/toolbar/table-style-editor.js +189 -0
- package/dist/esm/components/toolbar/table-style-selector.d.ts +7 -7
- package/dist/esm/components/toolbar/table-style-selector.d.ts.map +1 -1
- package/dist/esm/components/toolbar/table-style-selector.js +74 -81
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/esm/types.d.ts +10 -0
- package/dist/esm/types.d.ts.map +1 -1
- package/dist/spreadsheet.min.css +1 -1
- package/package.json +1 -1
|
@@ -2,10 +2,14 @@
|
|
|
2
2
|
* Excel groups a date column's AutoFilter checklist into a Year → Month → Day
|
|
3
3
|
* tree with tri-state parents. The checklist only knows each cell's DISPLAYED
|
|
4
4
|
* text, so a value is treated as a date when it parses as one AND looks like
|
|
5
|
-
* one —
|
|
6
|
-
* numbers ("2026") and text out. The tree is built only when every non-blank
|
|
5
|
+
* one — three digit groups separated by `/ - .` or a month name — which keeps
|
|
6
|
+
* plain numbers ("2026"), fractions / ranges ("1/2", "3-4") and text out. The tree is built only when every non-blank
|
|
7
7
|
* value in the column is such a date (a mixed column stays a flat list, as a
|
|
8
|
-
* text column would).
|
|
8
|
+
* text column would).
|
|
9
|
+
*
|
|
10
|
+
* Every level — years, months, days — is listed oldest-first (chronological).
|
|
11
|
+
* This deliberately diverges from Excel, which lists newest-first: product
|
|
12
|
+
* decision by Vinay, 2026-09-24. Do not "fix" it back to match Excel.
|
|
9
13
|
*/
|
|
10
14
|
export type DateTreeNode = {
|
|
11
15
|
/** Stable key: `y:2026`, `m:2026-09`, `d:2026-09-29`. */
|
|
@@ -19,10 +23,24 @@ export type DateTreeNode = {
|
|
|
19
23
|
export declare const parseFilterDateValue: (value: string) => Date | undefined;
|
|
20
24
|
/**
|
|
21
25
|
* Year → Month → Day nodes for `values`, or `undefined` when the column is
|
|
22
|
-
* not a date column
|
|
23
|
-
*
|
|
26
|
+
* not a date column — the same `classifyFilterColumn` verdict the condition
|
|
27
|
+
* menu uses, so a column of decimals or ranges ("1.5", "3-4", which V8 also
|
|
28
|
+
* parses as 2001 dates) stays a flat list. `blankLabel` (the "(Blanks)"
|
|
29
|
+
* entry) is skipped; callers render it beside the tree.
|
|
24
30
|
*/
|
|
25
31
|
export declare const buildDateTree: (values: readonly string[], blankLabel: string) => DateTreeNode[] | undefined;
|
|
26
32
|
/** `true` all checked, `false` none, `"mixed"` some — the tri-state of a node or the whole list. */
|
|
27
33
|
export declare const triState: (values: readonly string[], isVisible: (value: string) => boolean) => boolean | "mixed";
|
|
34
|
+
/**
|
|
35
|
+
* The family of AutoFilter conditions Excel offers for a column — "Text
|
|
36
|
+
* Filters", "Number Filters" or "Date Filters" — decided from the same
|
|
37
|
+
* displayed values the checklist shows. A column is a number column when every
|
|
38
|
+
* non-blank value is numeric, a date column when every non-blank value is a
|
|
39
|
+
* date (the rule `buildDateTree` uses), and a text column otherwise (mixed
|
|
40
|
+
* columns and empty columns included).
|
|
41
|
+
*/
|
|
42
|
+
export type FilterColumnType = "text" | "number" | "date";
|
|
43
|
+
/** `true` when a displayed value reads as a number: "42", "-1,234.5", "$1,200", "12%", "(300)". */
|
|
44
|
+
export declare const isFilterNumberValue: (value: string) => boolean;
|
|
45
|
+
export declare const classifyFilterColumn: (values: readonly string[], blankLabel: string) => FilterColumnType;
|
|
28
46
|
//# sourceMappingURL=date-tree.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"date-tree.d.ts","sourceRoot":"","sources":["../../../../components/filter-box/date-tree.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"date-tree.d.ts","sourceRoot":"","sources":["../../../../components/filter-box/date-tree.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,MAAM,YAAY,GAAG;IACzB,yDAAyD;IACzD,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,4DAA4D;IAC5D,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,CAAC,EAAE,YAAY,EAAE,CAAC;CAC3B,CAAC;AAwBF,0FAA0F;AAC1F,eAAO,MAAM,oBAAoB,GAAI,OAAO,MAAM,KAAG,IAAI,GAAG,SAS3D,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,GACxB,QAAQ,SAAS,MAAM,EAAE,EACzB,YAAY,MAAM,KACjB,YAAY,EAAE,GAAG,SAiDnB,CAAC;AAEF,oGAAoG;AACpG,eAAO,MAAM,QAAQ,GACnB,QAAQ,SAAS,MAAM,EAAE,EACzB,WAAW,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,KACpC,OAAO,GAAG,OAOZ,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAC;AAO1D,mGAAmG;AACnG,eAAO,MAAM,mBAAmB,GAAI,OAAO,MAAM,KAAG,OASnD,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,QAAQ,SAAS,MAAM,EAAE,EACzB,YAAY,MAAM,KACjB,gBAiBF,CAAC"}
|
|
@@ -3,17 +3,24 @@
|
|
|
3
3
|
* Excel groups a date column's AutoFilter checklist into a Year → Month → Day
|
|
4
4
|
* tree with tri-state parents. The checklist only knows each cell's DISPLAYED
|
|
5
5
|
* text, so a value is treated as a date when it parses as one AND looks like
|
|
6
|
-
* one —
|
|
7
|
-
* numbers ("2026") and text out. The tree is built only when every non-blank
|
|
6
|
+
* one — three digit groups separated by `/ - .` or a month name — which keeps
|
|
7
|
+
* plain numbers ("2026"), fractions / ranges ("1/2", "3-4") and text out. The tree is built only when every non-blank
|
|
8
8
|
* value in the column is such a date (a mixed column stays a flat list, as a
|
|
9
|
-
* text column would).
|
|
9
|
+
* text column would).
|
|
10
|
+
*
|
|
11
|
+
* Every level — years, months, days — is listed oldest-first (chronological).
|
|
12
|
+
* This deliberately diverges from Excel, which lists newest-first: product
|
|
13
|
+
* decision by Vinay, 2026-09-24. Do not "fix" it back to match Excel.
|
|
10
14
|
*/
|
|
11
15
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.triState = exports.buildDateTree = exports.parseFilterDateValue = void 0;
|
|
16
|
+
exports.classifyFilterColumn = exports.isFilterNumberValue = exports.triState = exports.buildDateTree = exports.parseFilterDateValue = void 0;
|
|
13
17
|
var tslib_1 = require("tslib");
|
|
14
18
|
var MIN_YEAR = 1900;
|
|
15
19
|
var MAX_YEAR = 2199;
|
|
16
|
-
|
|
20
|
+
// An all-digit date needs three parts ("9/29/2026", "3.4.26"): two-part
|
|
21
|
+
// shapes ("3-4", "1/2") are ranges or fractions, though V8 parses them as
|
|
22
|
+
// 2001 dates. Month-name shapes ("4-Mar", "15 Mar 2026") need no year.
|
|
23
|
+
var DATE_SHAPE = /(\d[/.\-]\d+[/.\-]\d)|([A-Za-z]{3,}.*\d)|(\d.*[A-Za-z]{3,})/;
|
|
17
24
|
var MONTH_NAMES = [
|
|
18
25
|
"January",
|
|
19
26
|
"February",
|
|
@@ -45,12 +52,16 @@ var parseFilterDateValue = function (value) {
|
|
|
45
52
|
exports.parseFilterDateValue = parseFilterDateValue;
|
|
46
53
|
/**
|
|
47
54
|
* Year → Month → Day nodes for `values`, or `undefined` when the column is
|
|
48
|
-
* not a date column
|
|
49
|
-
*
|
|
55
|
+
* not a date column — the same `classifyFilterColumn` verdict the condition
|
|
56
|
+
* menu uses, so a column of decimals or ranges ("1.5", "3-4", which V8 also
|
|
57
|
+
* parses as 2001 dates) stays a flat list. `blankLabel` (the "(Blanks)"
|
|
58
|
+
* entry) is skipped; callers render it beside the tree.
|
|
50
59
|
*/
|
|
51
60
|
var buildDateTree = function (values, blankLabel) {
|
|
52
61
|
var e_1, _a, e_2, _b;
|
|
53
62
|
var _c, _d, _e;
|
|
63
|
+
if ((0, exports.classifyFilterColumn)(values, blankLabel) !== "date")
|
|
64
|
+
return undefined;
|
|
54
65
|
var dated = [];
|
|
55
66
|
try {
|
|
56
67
|
for (var values_1 = tslib_1.__values(values), values_1_1 = values_1.next(); !values_1_1.done; values_1_1 = values_1.next()) {
|
|
@@ -96,12 +107,12 @@ var buildDateTree = function (values, blankLabel) {
|
|
|
96
107
|
}
|
|
97
108
|
finally { if (e_2) throw e_2.error; }
|
|
98
109
|
}
|
|
99
|
-
var
|
|
100
|
-
return tslib_1.__spreadArray([], tslib_1.__read(years.keys()), false).sort(
|
|
110
|
+
var asc = function (a, b) { return a - b; };
|
|
111
|
+
return tslib_1.__spreadArray([], tslib_1.__read(years.keys()), false).sort(asc).map(function (y) {
|
|
101
112
|
var months = years.get(y);
|
|
102
|
-
var monthNodes = tslib_1.__spreadArray([], tslib_1.__read(months.keys()), false).sort(
|
|
113
|
+
var monthNodes = tslib_1.__spreadArray([], tslib_1.__read(months.keys()), false).sort(asc).map(function (m) {
|
|
103
114
|
var days = months.get(m);
|
|
104
|
-
var dayNodes = tslib_1.__spreadArray([], tslib_1.__read(days.keys()), false).sort(
|
|
115
|
+
var dayNodes = tslib_1.__spreadArray([], tslib_1.__read(days.keys()), false).sort(asc).map(function (d) { return ({
|
|
105
116
|
key: "d:".concat(y, "-").concat(m + 1, "-").concat(d),
|
|
106
117
|
label: String(d),
|
|
107
118
|
values: days.get(d),
|
|
@@ -145,3 +156,57 @@ var triState = function (values, isVisible) {
|
|
|
145
156
|
return checked === values.length ? true : "mixed";
|
|
146
157
|
};
|
|
147
158
|
exports.triState = triState;
|
|
159
|
+
var CURRENCY_SYMBOLS = /^[$€£¥₹]|[$€£¥₹]$/g;
|
|
160
|
+
// `\s` already covers the no-break and narrow no-break spaces locales pad thousands with.
|
|
161
|
+
var THOUSANDS_SEPARATORS = /[,\s']/g;
|
|
162
|
+
var NUMBER_SHAPE = /^[-+]?(\d+\.?\d*|\.\d+)(e[-+]?\d+)?%?$/i;
|
|
163
|
+
/** `true` when a displayed value reads as a number: "42", "-1,234.5", "$1,200", "12%", "(300)". */
|
|
164
|
+
var isFilterNumberValue = function (value) {
|
|
165
|
+
var text = value.trim();
|
|
166
|
+
if (!text)
|
|
167
|
+
return false;
|
|
168
|
+
if (text.startsWith("(") && text.endsWith(")")) {
|
|
169
|
+
text = text.slice(1, -1).trim();
|
|
170
|
+
}
|
|
171
|
+
text = text.replace(CURRENCY_SYMBOLS, "").trim();
|
|
172
|
+
text = text.replace(THOUSANDS_SEPARATORS, "");
|
|
173
|
+
return NUMBER_SHAPE.test(text) && Number.isFinite(Number(text.replace(/%$/, "")));
|
|
174
|
+
};
|
|
175
|
+
exports.isFilterNumberValue = isFilterNumberValue;
|
|
176
|
+
var classifyFilterColumn = function (values, blankLabel) {
|
|
177
|
+
var e_4, _a;
|
|
178
|
+
var seen = 0;
|
|
179
|
+
var allDates = true;
|
|
180
|
+
var allNumbers = true;
|
|
181
|
+
try {
|
|
182
|
+
for (var values_3 = tslib_1.__values(values), values_3_1 = values_3.next(); !values_3_1.done; values_3_1 = values_3.next()) {
|
|
183
|
+
var value = values_3_1.value;
|
|
184
|
+
if (value === blankLabel || !value.trim())
|
|
185
|
+
continue;
|
|
186
|
+
seen++;
|
|
187
|
+
if (allDates && !(0, exports.parseFilterDateValue)(value))
|
|
188
|
+
allDates = false;
|
|
189
|
+
if (allNumbers && !(0, exports.isFilterNumberValue)(value))
|
|
190
|
+
allNumbers = false;
|
|
191
|
+
if (!allDates && !allNumbers)
|
|
192
|
+
return "text";
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
catch (e_4_1) { e_4 = { error: e_4_1 }; }
|
|
196
|
+
finally {
|
|
197
|
+
try {
|
|
198
|
+
if (values_3_1 && !values_3_1.done && (_a = values_3.return)) _a.call(values_3);
|
|
199
|
+
}
|
|
200
|
+
finally { if (e_4) throw e_4.error; }
|
|
201
|
+
}
|
|
202
|
+
if (!seen)
|
|
203
|
+
return "text";
|
|
204
|
+
// Numbers first: V8 also parses "1.5" or "3-4" as a date (Jan 5 / Mar 4,
|
|
205
|
+
// 2001), so a decimals column would otherwise be offered date filters.
|
|
206
|
+
if (allNumbers)
|
|
207
|
+
return "number";
|
|
208
|
+
if (allDates)
|
|
209
|
+
return "date";
|
|
210
|
+
return "text";
|
|
211
|
+
};
|
|
212
|
+
exports.classifyFilterColumn = classifyFilterColumn;
|
|
@@ -1,9 +1,34 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
+
import { CONDITION_LABELS } from "../../config";
|
|
2
3
|
import type { ConditionType, FilterConditionJoin } from "../../types";
|
|
4
|
+
import type { FilterColumnType } from "./date-tree";
|
|
3
5
|
/** Excel's "Top 10…" AutoFilter: the four `NUMBER_TOP_*` / `NUMBER_BOTTOM_*` condition types. */
|
|
4
6
|
export declare const isTop10ConditionType: (type: ConditionType | undefined) => type is "NUMBER_TOP_N" | "NUMBER_BOTTOM_N" | "NUMBER_TOP_PERCENT" | "NUMBER_BOTTOM_PERCENT";
|
|
5
7
|
export declare const top10ConditionType: (direction: "top" | "bottom", unit: "items" | "percent") => ConditionType;
|
|
8
|
+
type ConditionLabel = (typeof CONDITION_LABELS)[number];
|
|
9
|
+
/** One section of the condition menu; `label` renders as a group heading (Excel's "All Dates in the Period ▸"). */
|
|
10
|
+
export type ConditionOptionGroup = {
|
|
11
|
+
key: string;
|
|
12
|
+
label?: string;
|
|
13
|
+
options: ConditionLabel[];
|
|
14
|
+
};
|
|
15
|
+
export declare const DATE_PERIOD_GROUP_LABEL = "All dates in the period";
|
|
16
|
+
/**
|
|
17
|
+
* The condition menu Excel shows for a column: one family — "Text Filters",
|
|
18
|
+
* "Number Filters" or "Date Filters" (with the sixteen period conditions
|
|
19
|
+
* grouped under "All Dates in the Period") — plus the generic items. With no
|
|
20
|
+
* `columnType` (an unclassified column) every family is offered, grouped.
|
|
21
|
+
* A `current` condition from another family (a date filter stored on a column
|
|
22
|
+
* that now holds text) stays listed and selected so the user can see and
|
|
23
|
+
* clear it instead of it silently vanishing from the menu.
|
|
24
|
+
*/
|
|
25
|
+
export declare const conditionOptionsFor: (labels: readonly ConditionLabel[], columnType: FilterColumnType | undefined, current: ConditionType | undefined) => ConditionOptionGroup[];
|
|
6
26
|
type FilterByConditionProps = {
|
|
27
|
+
/**
|
|
28
|
+
* The column's predominant value type — picks the Excel family the
|
|
29
|
+
* condition menu offers. Omit to offer every family.
|
|
30
|
+
*/
|
|
31
|
+
columnType?: FilterColumnType;
|
|
7
32
|
conditionType?: ConditionType;
|
|
8
33
|
fromValue: string;
|
|
9
34
|
toValue: string;
|
|
@@ -20,6 +45,6 @@ type FilterByConditionProps = {
|
|
|
20
45
|
onChangeCondition2To(value: string): void;
|
|
21
46
|
onChangeConditionJoin(join: FilterConditionJoin): void;
|
|
22
47
|
};
|
|
23
|
-
export declare const FilterByCondition: ({ conditionType, fromValue, toValue, onChangeToValue, onChangeFromValue, onChangeConditionType, condition2Type, condition2From, condition2To, conditionJoin, onChangeCondition2Type, onChangeCondition2From, onChangeCondition2To, onChangeConditionJoin, }: FilterByConditionProps) => React.JSX.Element;
|
|
48
|
+
export declare const FilterByCondition: ({ columnType, conditionType, fromValue, toValue, onChangeToValue, onChangeFromValue, onChangeConditionType, condition2Type, condition2From, condition2To, conditionJoin, onChangeCondition2Type, onChangeCondition2From, onChangeCondition2To, onChangeConditionJoin, }: FilterByConditionProps) => React.JSX.Element;
|
|
24
49
|
export {};
|
|
25
50
|
//# sourceMappingURL=filter-by-condition.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filter-by-condition.d.ts","sourceRoot":"","sources":["../../../../components/filter-box/filter-by-condition.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"filter-by-condition.d.ts","sourceRoot":"","sources":["../../../../components/filter-box/filter-by-condition.tsx"],"names":[],"mappings":"AAcA,OAAO,KAA4B,MAAM,OAAO,CAAC;AAEjD,OAAO,EAAE,gBAAgB,EAAkB,MAAM,cAAc,CAAC;AAGhE,OAAO,KAAK,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACtE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD,iGAAiG;AACjG,eAAO,MAAM,oBAAoB,GAAI,MAAM,aAAa,GAAG,SAAS,gGAIlC,CAAC;AAEnC,eAAO,MAAM,kBAAkB,GAC7B,WAAW,KAAK,GAAG,QAAQ,EAC3B,MAAM,OAAO,GAAG,SAAS,KACxB,aAO4B,CAAC;AAchC,KAAK,cAAc,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AAExD,mHAAmH;AACnH,MAAM,MAAM,oBAAoB,GAAG;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,cAAc,EAAE,CAAC;CAC3B,CAAC;AAEF,eAAO,MAAM,uBAAuB,4BAA4B,CAAC;AAoBjE;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,GAC9B,QAAQ,SAAS,cAAc,EAAE,EACjC,YAAY,gBAAgB,GAAG,SAAS,EACxC,SAAS,aAAa,GAAG,SAAS,KACjC,oBAAoB,EAmCtB,CAAC;AAEF,KAAK,sBAAsB,GAAG;IAC5B;;;OAGG;IACH,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,qBAAqB,CAAC,CAAC,IAAI,EAAE,aAAa,GAAG,SAAS,GAAG,IAAI,CAAC;IAC9D,qEAAqE;IACrE,cAAc,CAAC,EAAE,aAAa,CAAC;IAC/B,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,mBAAmB,CAAC;IACnC,sBAAsB,CAAC,IAAI,EAAE,aAAa,GAAG,SAAS,GAAG,IAAI,CAAC;IAC9D,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5C,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C,qBAAqB,CAAC,IAAI,EAAE,mBAAmB,GAAG,IAAI,CAAC;CACxD,CAAC;AAsLF,eAAO,MAAM,iBAAiB,GAAI,yQAgB/B,sBAAsB,sBAyFxB,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.FilterByCondition = exports.top10ConditionType = exports.isTop10ConditionType = void 0;
|
|
3
|
+
exports.FilterByCondition = exports.conditionOptionsFor = exports.DATE_PERIOD_GROUP_LABEL = exports.top10ConditionType = exports.isTop10ConditionType = void 0;
|
|
4
4
|
var tslib_1 = require("tslib");
|
|
5
5
|
var ui_1 = require("@rowsncolumns/ui");
|
|
6
6
|
var react_1 = tslib_1.__importStar(require("react"));
|
|
@@ -36,8 +36,103 @@ var SECOND_CLAUSE_LABELS = config_1.CONDITION_LABELS.filter(function (_a) {
|
|
|
36
36
|
condition !== "NUMBER_ABOVE_AVERAGE" &&
|
|
37
37
|
condition !== "NUMBER_BELOW_AVERAGE";
|
|
38
38
|
});
|
|
39
|
+
exports.DATE_PERIOD_GROUP_LABEL = "All dates in the period";
|
|
40
|
+
var FOREIGN_GROUP_LABEL = "Applied filter";
|
|
41
|
+
var isDatePeriodCondition = function (type) {
|
|
42
|
+
return /^DATE_IN_[QM]\d+$/.test(type);
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Which Excel AutoFilter family a condition belongs to. Anything outside the
|
|
46
|
+
* text / number / date prefixes ("Is empty", "Errors", "Custom formula") is
|
|
47
|
+
* offered on every column.
|
|
48
|
+
*/
|
|
49
|
+
var conditionFamily = function (type) {
|
|
50
|
+
if (type.startsWith("TEXT_"))
|
|
51
|
+
return "text";
|
|
52
|
+
if (type.startsWith("NUMBER_"))
|
|
53
|
+
return "number";
|
|
54
|
+
if (type.startsWith("DATE_"))
|
|
55
|
+
return isDatePeriodCondition(type) ? "period" : "date";
|
|
56
|
+
return "generic";
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* The condition menu Excel shows for a column: one family — "Text Filters",
|
|
60
|
+
* "Number Filters" or "Date Filters" (with the sixteen period conditions
|
|
61
|
+
* grouped under "All Dates in the Period") — plus the generic items. With no
|
|
62
|
+
* `columnType` (an unclassified column) every family is offered, grouped.
|
|
63
|
+
* A `current` condition from another family (a date filter stored on a column
|
|
64
|
+
* that now holds text) stays listed and selected so the user can see and
|
|
65
|
+
* clear it instead of it silently vanishing from the menu.
|
|
66
|
+
*/
|
|
67
|
+
var conditionOptionsFor = function (labels, columnType, current) {
|
|
68
|
+
var e_1, _a;
|
|
69
|
+
var _b;
|
|
70
|
+
var families = columnType
|
|
71
|
+
? [columnType]
|
|
72
|
+
: ["text", "number", "date"];
|
|
73
|
+
var groups = [];
|
|
74
|
+
var _loop_1 = function (family) {
|
|
75
|
+
groups.push({
|
|
76
|
+
key: family,
|
|
77
|
+
options: labels.filter(function (_a) {
|
|
78
|
+
var condition = _a.condition;
|
|
79
|
+
return conditionFamily(condition) === family;
|
|
80
|
+
}),
|
|
81
|
+
});
|
|
82
|
+
if (family === "date") {
|
|
83
|
+
groups.push({
|
|
84
|
+
key: "period",
|
|
85
|
+
label: exports.DATE_PERIOD_GROUP_LABEL,
|
|
86
|
+
options: labels.filter(function (_a) {
|
|
87
|
+
var condition = _a.condition;
|
|
88
|
+
return conditionFamily(condition) === "period";
|
|
89
|
+
}),
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
try {
|
|
94
|
+
for (var families_1 = tslib_1.__values(families), families_1_1 = families_1.next(); !families_1_1.done; families_1_1 = families_1.next()) {
|
|
95
|
+
var family = families_1_1.value;
|
|
96
|
+
_loop_1(family);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
100
|
+
finally {
|
|
101
|
+
try {
|
|
102
|
+
if (families_1_1 && !families_1_1.done && (_a = families_1.return)) _a.call(families_1);
|
|
103
|
+
}
|
|
104
|
+
finally { if (e_1) throw e_1.error; }
|
|
105
|
+
}
|
|
106
|
+
groups.push({
|
|
107
|
+
key: "generic",
|
|
108
|
+
options: labels.filter(function (_a) {
|
|
109
|
+
var condition = _a.condition;
|
|
110
|
+
return conditionFamily(condition) === "generic";
|
|
111
|
+
}),
|
|
112
|
+
});
|
|
113
|
+
var offered = groups.filter(function (group) { return group.options.length > 0; });
|
|
114
|
+
if (current &&
|
|
115
|
+
!offered.some(function (group) { return group.options.some(function (_a) {
|
|
116
|
+
var condition = _a.condition;
|
|
117
|
+
return condition === current;
|
|
118
|
+
}); })) {
|
|
119
|
+
var foreign = (_b = labels.find(function (_a) {
|
|
120
|
+
var condition = _a.condition;
|
|
121
|
+
return condition === current;
|
|
122
|
+
})) !== null && _b !== void 0 ? _b : config_1.CONDITION_LABELS.find(function (_a) {
|
|
123
|
+
var condition = _a.condition;
|
|
124
|
+
return condition === current;
|
|
125
|
+
});
|
|
126
|
+
if (foreign) {
|
|
127
|
+
offered.unshift({ key: "current", label: FOREIGN_GROUP_LABEL, options: [foreign] });
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return offered;
|
|
131
|
+
};
|
|
132
|
+
exports.conditionOptionsFor = conditionOptionsFor;
|
|
39
133
|
var ConditionSelect = function (_a) {
|
|
40
|
-
var value = _a.value, labels = _a.labels, onChange = _a.onChange, ariaLabel = _a.ariaLabel;
|
|
134
|
+
var value = _a.value, labels = _a.labels, columnType = _a.columnType, onChange = _a.onChange, ariaLabel = _a.ariaLabel;
|
|
135
|
+
var groups = (0, react_1.useMemo)(function () { return (0, exports.conditionOptionsFor)(labels, columnType, value); }, [labels, columnType, value]);
|
|
41
136
|
return (react_1.default.createElement(ui_1.Select, { value: value !== null && value !== void 0 ? value : config_1.CONDITION_NONE, onValueChange: function (next) {
|
|
42
137
|
return onChange(next === config_1.CONDITION_NONE ? undefined : next);
|
|
43
138
|
} },
|
|
@@ -46,10 +141,14 @@ var ConditionSelect = function (_a) {
|
|
|
46
141
|
react_1.default.createElement(ui_1.SelectPortal, null,
|
|
47
142
|
react_1.default.createElement(ui_1.SelectContent, null,
|
|
48
143
|
react_1.default.createElement(ui_1.SelectItem, { value: config_1.CONDITION_NONE }, "None"),
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
144
|
+
groups.map(function (group) { return (react_1.default.createElement(react_1.default.Fragment, { key: group.key },
|
|
145
|
+
react_1.default.createElement(ui_1.SelectSeparator, null),
|
|
146
|
+
react_1.default.createElement(ui_1.SelectGroup, null,
|
|
147
|
+
group.label ? react_1.default.createElement(ui_1.SelectLabel, null, group.label) : null,
|
|
148
|
+
group.options.map(function (_a) {
|
|
149
|
+
var condition = _a.condition, label = _a.label;
|
|
150
|
+
return (react_1.default.createElement(ui_1.SelectItem, { key: condition, value: condition }, label));
|
|
151
|
+
})))); })))));
|
|
53
152
|
};
|
|
54
153
|
var ValueInputs = function (_a) {
|
|
55
154
|
var conditionType = _a.conditionType, fromValue = _a.fromValue, toValue = _a.toValue, onChangeFromValue = _a.onChangeFromValue, onChangeToValue = _a.onChangeToValue, namePrefix = _a.namePrefix;
|
|
@@ -104,20 +203,20 @@ var Top10Fields = function (_a) {
|
|
|
104
203
|
react_1.default.createElement(ui_1.SelectItem, { value: "percent" }, "Percent"))))));
|
|
105
204
|
};
|
|
106
205
|
var FilterByCondition = function (_a) {
|
|
107
|
-
var conditionType = _a.conditionType, fromValue = _a.fromValue, toValue = _a.toValue, onChangeToValue = _a.onChangeToValue, onChangeFromValue = _a.onChangeFromValue, onChangeConditionType = _a.onChangeConditionType, condition2Type = _a.condition2Type, condition2From = _a.condition2From, condition2To = _a.condition2To, conditionJoin = _a.conditionJoin, onChangeCondition2Type = _a.onChangeCondition2Type, onChangeCondition2From = _a.onChangeCondition2From, onChangeCondition2To = _a.onChangeCondition2To, onChangeConditionJoin = _a.onChangeConditionJoin;
|
|
206
|
+
var columnType = _a.columnType, conditionType = _a.conditionType, fromValue = _a.fromValue, toValue = _a.toValue, onChangeToValue = _a.onChangeToValue, onChangeFromValue = _a.onChangeFromValue, onChangeConditionType = _a.onChangeConditionType, condition2Type = _a.condition2Type, condition2From = _a.condition2From, condition2To = _a.condition2To, conditionJoin = _a.conditionJoin, onChangeCondition2Type = _a.onChangeCondition2Type, onChangeCondition2From = _a.onChangeCondition2From, onChangeCondition2To = _a.onChangeCondition2To, onChangeConditionJoin = _a.onChangeConditionJoin;
|
|
108
207
|
// Excel's Custom AutoFilter shows the second clause row only on request;
|
|
109
208
|
// it stays open while a second clause is stored on the column.
|
|
110
209
|
var _b = tslib_1.__read((0, react_1.useState)(function () { return condition2Type !== undefined; }), 2), showSecondClause = _b[0], setShowSecondClause = _b[1];
|
|
111
210
|
var isTop10 = (0, exports.isTop10ConditionType)(conditionType);
|
|
112
211
|
return (react_1.default.createElement(react_1.default.Fragment, null,
|
|
113
212
|
react_1.default.createElement("label", { className: "text-sm font-semibold pb-1 block" }, "Filter by condition"),
|
|
114
|
-
react_1.default.createElement(ConditionSelect, { value: conditionType, labels: config_1.CONDITION_LABELS, onChange: function (type) { return onChangeConditionType === null || onChangeConditionType === void 0 ? void 0 : onChangeConditionType(type); }, ariaLabel: "Condition" }),
|
|
213
|
+
react_1.default.createElement(ConditionSelect, { value: conditionType, labels: config_1.CONDITION_LABELS, columnType: columnType, onChange: function (type) { return onChangeConditionType === null || onChangeConditionType === void 0 ? void 0 : onChangeConditionType(type); }, ariaLabel: "Condition" }),
|
|
115
214
|
isTop10 && conditionType ? (react_1.default.createElement(Top10Fields, { conditionType: conditionType, count: fromValue, onChangeConditionType: function (type) { return onChangeConditionType === null || onChangeConditionType === void 0 ? void 0 : onChangeConditionType(type); }, onChangeCount: onChangeFromValue })) : (react_1.default.createElement(ValueInputs, { conditionType: conditionType, fromValue: fromValue, toValue: toValue, onChangeFromValue: onChangeFromValue, onChangeToValue: onChangeToValue, namePrefix: "" })),
|
|
116
215
|
conditionType && !isTop10 ? (showSecondClause ? (react_1.default.createElement("div", { className: "mt-1 flex flex-col gap-1" },
|
|
117
216
|
react_1.default.createElement("div", { role: "radiogroup", "aria-label": "Combine conditions", className: "flex gap-3 text-xs px-1" }, ["AND", "OR"].map(function (join) { return (react_1.default.createElement("label", { key: join, className: "flex items-center gap-1 cursor-pointer" },
|
|
118
217
|
react_1.default.createElement("input", { type: "radio", name: "conditionJoin", value: join, checked: conditionJoin === join, onChange: function () { return onChangeConditionJoin(join); } }),
|
|
119
218
|
join === "AND" ? "And" : "Or")); })),
|
|
120
|
-
react_1.default.createElement(ConditionSelect, { value: condition2Type, labels: SECOND_CLAUSE_LABELS, onChange: onChangeCondition2Type, ariaLabel: "Second condition" }),
|
|
219
|
+
react_1.default.createElement(ConditionSelect, { value: condition2Type, labels: SECOND_CLAUSE_LABELS, columnType: columnType, onChange: onChangeCondition2Type, ariaLabel: "Second condition" }),
|
|
121
220
|
react_1.default.createElement(ValueInputs, { conditionType: condition2Type, fromValue: condition2From, toValue: condition2To, onChangeFromValue: onChangeCondition2From, onChangeToValue: onChangeCondition2To, namePrefix: "second" }))) : (react_1.default.createElement(ui_1.Button, { type: "button", variant: "link", className: "text-xs px-1 mt-1 self-start", onClick: function () { return setShowSecondClause(true); } }, "Custom filter (And / Or)\u2026"))) : null));
|
|
122
221
|
};
|
|
123
222
|
exports.FilterByCondition = FilterByCondition;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../components/filter-box/index.tsx"],"names":[],"mappings":"AAyBA,OAAO,KAAsC,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../components/filter-box/index.tsx"],"names":[],"mappings":"AAyBA,OAAO,KAAsC,MAAM,OAAO,CAAC;AAM3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAMV,cAAc,EACd,UAAU,EAGV,SAAS,EACV,MAAM,aAAa,CAAC;AAKrB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,YAAY,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAKvD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,OAAO,CAAC;IAChB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IACnC,MAAM,EAAE,cAAc,CAAC,aAAa,CAAC,CAAC;IACtC,QAAQ,EAAE,cAAc,CAAC,eAAe,CAAC,CAAC;IAC1C,mBAAmB,EAAE,cAAc,CAAC,qBAAqB,CAAC,CAAC;IAC3D,mBAAmB,EAAE,cAAc,CAAC,qBAAqB,CAAC,CAAC;IAC3D,MAAM,EAAE,UAAU,GAAG,SAAS,CAAC;IAC/B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B,+DAA+D;IAC/D,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B,sEAAsE;IACtE,cAAc,CAAC,EAAE,aAAa,EAAE,CAAC;IACjC;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC;CAC1B,CAAC;AAGF,eAAO,MAAM,WAAW,GACtB,QAAQ,UAAU,GAAG,SAAS,KAC7B,MAAM,IAAI,SAEZ,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,GAClC,UAAU,cAAc,GAAG,SAAS,KACnC,OAQA,CAAC;AA0BJ,eAAO,MAAM,gBAAgB,4NAexB,cAAc,uBAmlBlB,CAAC;AAIF,MAAM,MAAM,cAAc,GAAG,kBAAkB,GAAG;IAChD,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;IAChC,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,SAAS;YAAW,cAAc;;CAuC9C,CAAC"}
|
|
@@ -9,8 +9,10 @@ var utils_1 = require("@rowsncolumns/utils");
|
|
|
9
9
|
var react_1 = tslib_1.__importStar(require("react"));
|
|
10
10
|
var react_2 = require("react");
|
|
11
11
|
var atoms_1 = require("../../atoms");
|
|
12
|
-
var style_1 = require("../context-menu/style");
|
|
13
12
|
var sort_dialog_atom_1 = require("../../atoms/sort-dialog.atom");
|
|
13
|
+
var config_1 = require("../../config");
|
|
14
|
+
var style_1 = require("../context-menu/style");
|
|
15
|
+
var date_tree_1 = require("./date-tree");
|
|
14
16
|
var filter_by_color_1 = require("./filter-by-color");
|
|
15
17
|
var filter_by_condition_1 = require("./filter-by-condition");
|
|
16
18
|
var filter_by_value_1 = require("./filter-by-value");
|
|
@@ -89,6 +91,9 @@ exports.FilterBoxWrapper = (0, react_2.memo)(function (_a) {
|
|
|
89
91
|
var _11 = tslib_1.__read((0, react_1.useState)((_z = filterCriteria === null || filterCriteria === void 0 ? void 0 : filterCriteria.conditionJoin) !== null && _z !== void 0 ? _z : "AND"), 2), conditionJoin = _11[0], onChangeConditionJoin = _11[1];
|
|
90
92
|
// "Sort by Color" is a collapsed sub-menu of the dropdown, like Excel's.
|
|
91
93
|
var _12 = tslib_1.__read((0, react_1.useState)(false), 2), showSortByColor = _12[0], setShowSortByColor = _12[1];
|
|
94
|
+
// Excel offers one condition family per column (Text / Number / Date
|
|
95
|
+
// Filters), read from the same displayed values the checklist shows.
|
|
96
|
+
var columnType = (0, react_2.useMemo)(function () { return (0, date_tree_1.classifyFilterColumn)(values, config_1.BLANK_LABEL); }, [values]);
|
|
92
97
|
// Auto apply (Excel parity). `pendingAutoApplyTick` counts user edits made
|
|
93
98
|
// while auto apply is on; the debounced effect below applies the newest
|
|
94
99
|
// state once per burst.
|
|
@@ -401,7 +406,7 @@ exports.FilterBoxWrapper = (0, react_2.memo)(function (_a) {
|
|
|
401
406
|
react_1.default.createElement(icons_1.Cross2Icon, null)),
|
|
402
407
|
clearLabel),
|
|
403
408
|
react_1.default.createElement("div", { className: "py-2 px-2" },
|
|
404
|
-
react_1.default.createElement(filter_by_condition_1.FilterByCondition, { conditionType: conditionType, fromValue: fromValue, toValue: toValue, onChangeConditionType: onUserChangeConditionType, onChangeFromValue: onUserChangeFromValue, onChangeToValue: onUserChangeToValue, condition2Type: condition2Type, condition2From: condition2From, condition2To: condition2To, conditionJoin: conditionJoin, onChangeCondition2Type: onUserChangeCondition2Type, onChangeCondition2From: onUserChangeCondition2From, onChangeCondition2To: onUserChangeCondition2To, onChangeConditionJoin: onUserChangeConditionJoin }),
|
|
409
|
+
react_1.default.createElement(filter_by_condition_1.FilterByCondition, { columnType: columnType, conditionType: conditionType, fromValue: fromValue, toValue: toValue, onChangeConditionType: onUserChangeConditionType, onChangeFromValue: onUserChangeFromValue, onChangeToValue: onUserChangeToValue, condition2Type: condition2Type, condition2From: condition2From, condition2To: condition2To, conditionJoin: conditionJoin, onChangeCondition2Type: onUserChangeCondition2Type, onChangeCondition2From: onUserChangeCondition2From, onChangeCondition2To: onUserChangeCondition2To, onChangeConditionJoin: onUserChangeConditionJoin }),
|
|
405
410
|
react_1.default.createElement(ui_1.Separator, { className: "mt-2 mb-2" }),
|
|
406
411
|
react_1.default.createElement(filter_by_color_1.FilterByColor, { colorFilter: colorFilter === undefined
|
|
407
412
|
? filterCriteria === null || filterCriteria === void 0 ? void 0 : filterCriteria.colorFilter
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../components/formula-input/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAiB,MAAM,kBAAkB,CAAC;AAGrE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AAE5C,OAAO,KAAiB,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../components/formula-input/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAiB,MAAM,kBAAkB,CAAC;AAGrE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AAE5C,OAAO,KAAiB,MAAM,OAAO,CAAC;AActC,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE1C,KAAK,iBAAiB,GAAG,cAAc,CAAC,OAAO,SAAS,CAAC,GAAG;IAC1D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,YAAY,sGA8JxB,CAAC"}
|
|
@@ -8,6 +8,7 @@ var react_1 = require("react");
|
|
|
8
8
|
var react_2 = tslib_1.__importStar(require("react"));
|
|
9
9
|
var atoms_1 = require("../../atoms");
|
|
10
10
|
var cell_input_context_atom_1 = require("../../atoms/cell-input-context.atom");
|
|
11
|
+
var use_isomorphic_layout_effect_1 = require("../../hooks/use-isomorphic-layout-effect");
|
|
11
12
|
var use_merge_refs_1 = require("../../hooks/use-merge.refs");
|
|
12
13
|
var cell_input_1 = require("../cell-input");
|
|
13
14
|
/**
|
|
@@ -26,15 +27,36 @@ exports.FormulaInput = (0, react_1.forwardRef)(function (_a, forwardedRef) {
|
|
|
26
27
|
var getSelectionsFromFormula = (0, atoms_1.useGetSelectionsFromFormula)();
|
|
27
28
|
var onEditorKeyDown = (0, atoms_1.useOnEditorKeyDown)();
|
|
28
29
|
var _e = (0, cell_input_context_atom_1.useCellInputContext)(), getTableNames = _e.getTableNames, getTableColumnNames = _e.getTableColumnNames, getNamedRanges = _e.getNamedRanges;
|
|
30
|
+
// Whether THIS input is the one holding formula mode on. `handleBlur` keeps the mode
|
|
31
|
+
// alive when focus moves to the grid — that is how a range gets picked — so an input
|
|
32
|
+
// that unmounts while the grid holds focus (Cancel / Save / the dialog's X / Esc) has
|
|
33
|
+
// to be the one to turn it off; otherwise the picked range keeps rendering and the
|
|
34
|
+
// grid stays in formula-selection mode with no editor left to receive the picks.
|
|
35
|
+
var ownsFormulaModeRef = (0, react_2.useRef)(false);
|
|
36
|
+
var enterFormulaMode = (0, react_1.useCallback)(function (nextValue) {
|
|
37
|
+
var formulaMode = (0, utils_1.isAFormula)(nextValue);
|
|
38
|
+
setFormulaMode(formulaMode);
|
|
39
|
+
ownsFormulaModeRef.current = formulaMode;
|
|
40
|
+
}, [setFormulaMode]);
|
|
29
41
|
var closeFormulaMode = (0, react_1.useCallback)(function () {
|
|
30
42
|
setFormulaMode(false);
|
|
31
43
|
setFormulaSelections([]);
|
|
44
|
+
ownsFormulaModeRef.current = false;
|
|
32
45
|
onCancel === null || onCancel === void 0 ? void 0 : onCancel();
|
|
33
46
|
}, [onCancel, setFormulaMode, setFormulaSelections]);
|
|
47
|
+
// Layout (not passive) cleanup: when a host re-keys its range inputs ("Add range"),
|
|
48
|
+
// the departing input must release the mode before the new one's autoFocus claims it.
|
|
49
|
+
(0, use_isomorphic_layout_effect_1.useIsomorphicLayoutEffect)(function () { return function () {
|
|
50
|
+
if (ownsFormulaModeRef.current) {
|
|
51
|
+
ownsFormulaModeRef.current = false;
|
|
52
|
+
setFormulaMode(false);
|
|
53
|
+
setFormulaSelections([]);
|
|
54
|
+
}
|
|
55
|
+
}; }, [setFormulaMode, setFormulaSelections]);
|
|
34
56
|
var handleFocus = (0, react_1.useCallback)(function (e) {
|
|
35
57
|
var _a;
|
|
36
58
|
// Set formula mode
|
|
37
|
-
|
|
59
|
+
enterFormulaMode(value);
|
|
38
60
|
// Get selections from formula
|
|
39
61
|
var selections = getSelectionsFromFormula === null || getSelectionsFromFormula === void 0 ? void 0 : getSelectionsFromFormula(value);
|
|
40
62
|
if (selections) {
|
|
@@ -46,7 +68,7 @@ exports.FormulaInput = (0, react_1.forwardRef)(function (_a, forwardedRef) {
|
|
|
46
68
|
onChange === null || onChange === void 0 ? void 0 : onChange(value);
|
|
47
69
|
onFocus === null || onFocus === void 0 ? void 0 : onFocus(e);
|
|
48
70
|
}, [
|
|
49
|
-
|
|
71
|
+
enterFormulaMode,
|
|
50
72
|
setFormulaSelections,
|
|
51
73
|
getSelectionsFromFormula,
|
|
52
74
|
value,
|
|
@@ -73,7 +95,7 @@ exports.FormulaInput = (0, react_1.forwardRef)(function (_a, forwardedRef) {
|
|
|
73
95
|
return;
|
|
74
96
|
}
|
|
75
97
|
// Set formula mode
|
|
76
|
-
|
|
98
|
+
enterFormulaMode(value);
|
|
77
99
|
// Get selections from formula
|
|
78
100
|
var selections = getSelectionsFromFormula === null || getSelectionsFromFormula === void 0 ? void 0 : getSelectionsFromFormula(value);
|
|
79
101
|
if (selections) {
|
|
@@ -82,7 +104,7 @@ exports.FormulaInput = (0, react_1.forwardRef)(function (_a, forwardedRef) {
|
|
|
82
104
|
}, [
|
|
83
105
|
activeEditor,
|
|
84
106
|
onChange,
|
|
85
|
-
|
|
107
|
+
enterFormulaMode,
|
|
86
108
|
setFormulaSelections,
|
|
87
109
|
getSelectionsFromFormula,
|
|
88
110
|
]);
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { BandingProperties, CustomTableStyle, Maybe } from "../../types";
|
|
3
|
+
import type { SpreadsheetTheme } from "../canvas-grid/typings";
|
|
4
|
+
/**
|
|
5
|
+
* The style elements a custom table style can colour — the `rowProperties` fields the grid's
|
|
6
|
+
* banding resolves (`api/banded-ranges.ts`), in the order of Excel's New Table Style dialog.
|
|
7
|
+
* The first three are what the gallery swatches show and what every created style carries; the
|
|
8
|
+
* rest are only written when the user picks a colour for them.
|
|
9
|
+
*/
|
|
10
|
+
export declare const TABLE_STYLE_ELEMENTS: readonly [{
|
|
11
|
+
readonly key: "header";
|
|
12
|
+
readonly label: "Header row";
|
|
13
|
+
readonly property: "headerColor";
|
|
14
|
+
}, {
|
|
15
|
+
readonly key: "firstBand";
|
|
16
|
+
readonly label: "First row stripe";
|
|
17
|
+
readonly property: "firstBandColor";
|
|
18
|
+
}, {
|
|
19
|
+
readonly key: "secondBand";
|
|
20
|
+
readonly label: "Second row stripe";
|
|
21
|
+
readonly property: "secondBandColor";
|
|
22
|
+
}, {
|
|
23
|
+
readonly key: "firstColumn";
|
|
24
|
+
readonly label: "First column";
|
|
25
|
+
readonly property: "firstColumnColor";
|
|
26
|
+
}, {
|
|
27
|
+
readonly key: "lastColumn";
|
|
28
|
+
readonly label: "Last column";
|
|
29
|
+
readonly property: "lastColumnColor";
|
|
30
|
+
}, {
|
|
31
|
+
readonly key: "totalRow";
|
|
32
|
+
readonly label: "Total row";
|
|
33
|
+
readonly property: "totalRowColor";
|
|
34
|
+
}];
|
|
35
|
+
export type TableStyleElementKey = (typeof TABLE_STYLE_ELEMENTS)[number]["key"];
|
|
36
|
+
/** The colour picked for each element; `undefined` leaves the element unpainted. */
|
|
37
|
+
export type TableStyleColors = Partial<Record<TableStyleElementKey, string>>;
|
|
38
|
+
/** The colours a fresh style starts from (the default palette's accent 1 and its light tint). */
|
|
39
|
+
export declare const DEFAULT_TABLE_STYLE_COLORS: TableStyleColors;
|
|
40
|
+
/**
|
|
41
|
+
* The banding a custom table style paints from its element colours. Header and the two stripes
|
|
42
|
+
* are always present (unset ones stay unpainted); the optional elements are written only when
|
|
43
|
+
* coloured, so a style that only picks the first three keeps its historical shape.
|
|
44
|
+
*/
|
|
45
|
+
export declare const buildCustomTableStyle: (name: string, colors: TableStyleColors) => CustomTableStyle;
|
|
46
|
+
/** The element colours of an existing custom style (theme colours resolved to hex). */
|
|
47
|
+
export declare const getTableStyleColors: (style: CustomTableStyle, theme: SpreadsheetTheme) => TableStyleColors;
|
|
48
|
+
/**
|
|
49
|
+
* Why a style name cannot be used, or `undefined` when it can: empty, a built-in style's name
|
|
50
|
+
* (the gallery tells the two apart by name), or another custom style's name (case-insensitive,
|
|
51
|
+
* like Excel). `currentName` is the style being edited, which may keep its own name.
|
|
52
|
+
*/
|
|
53
|
+
export declare const getTableStyleNameError: (name: string, existingNames: readonly string[], currentName?: string) => string | undefined;
|
|
54
|
+
export type TableStyleEditorProps = {
|
|
55
|
+
/** The custom style being modified; omit to create a new one. */
|
|
56
|
+
style?: CustomTableStyle;
|
|
57
|
+
/** The name a new style starts with (ignored when `style` is given). */
|
|
58
|
+
defaultName?: string;
|
|
59
|
+
theme: SpreadsheetTheme;
|
|
60
|
+
/** Names of the workbook's custom styles, for the duplicate-name check. */
|
|
61
|
+
existingNames?: readonly string[];
|
|
62
|
+
/** Create (or, when `style` is given, save) — receives the style the picks produce. */
|
|
63
|
+
onSubmit(style: CustomTableStyle): void;
|
|
64
|
+
onCancel(): void;
|
|
65
|
+
/** Offered as "Delete style" while editing; the footer omits it when absent. */
|
|
66
|
+
onDelete?(style: CustomTableStyle): void;
|
|
67
|
+
className?: string;
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Excel's New Table Style / Modify Table Style dialog, laid out for the Format as Table gallery:
|
|
71
|
+
* name row, the table elements with a colour button each (the toolbar's colour popover), a live
|
|
72
|
+
* preview of the table the picks produce, and an Excel-ordered footer.
|
|
73
|
+
*/
|
|
74
|
+
export declare const TableStyleEditor: ({ style, defaultName, theme, existingNames, onSubmit, onCancel, onDelete, className, }: TableStyleEditorProps) => React.JSX.Element;
|
|
75
|
+
/**
|
|
76
|
+
* The table a style paints: a header row over four banded rows, with the first / last column and
|
|
77
|
+
* total row emphasis when the style colours them. Uses the grid's banding rules
|
|
78
|
+
* (`api/banded-ranges.ts`): the first body row is the first stripe, the last body row is the
|
|
79
|
+
* total row, and an edge column's colour outranks the stripe under it.
|
|
80
|
+
*/
|
|
81
|
+
export declare const TableStylePreviewCard: ({ rowProperties, theme, }: {
|
|
82
|
+
rowProperties: Maybe<BandingProperties>;
|
|
83
|
+
theme: SpreadsheetTheme;
|
|
84
|
+
}) => React.JSX.Element;
|
|
85
|
+
//# sourceMappingURL=table-style-editor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"table-style-editor.d.ts","sourceRoot":"","sources":["../../../../components/toolbar/table-style-editor.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAmC,MAAM,OAAO,CAAC;AAIxD,OAAO,KAAK,EACV,iBAAiB,EAEjB,gBAAgB,EAChB,KAAK,EACN,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAG/D;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;EAW/B,CAAC;AAEH,MAAM,MAAM,oBAAoB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC;AAEhF,oFAAoF;AACpF,MAAM,MAAM,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,oBAAoB,EAAE,MAAM,CAAC,CAAC,CAAC;AAE7E,iGAAiG;AACjG,eAAO,MAAM,0BAA0B,EAAE,gBAIxC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,GAChC,MAAM,MAAM,EACZ,QAAQ,gBAAgB,KACvB,gBAUF,CAAC;AAEF,uFAAuF;AACvF,eAAO,MAAM,mBAAmB,GAC9B,OAAO,gBAAgB,EACvB,OAAO,gBAAgB,KACtB,gBAWF,CAAC;AAIF;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,GACjC,MAAM,MAAM,EACZ,eAAe,SAAS,MAAM,EAAE,EAChC,cAAc,MAAM,KACnB,MAAM,GAAG,SAaX,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,iEAAiE;IACjE,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB,wEAAwE;IACxE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,gBAAgB,CAAC;IACxB,2EAA2E;IAC3E,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,uFAAuF;IACvF,QAAQ,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACxC,QAAQ,IAAI,IAAI,CAAC;IACjB,gFAAgF;IAChF,QAAQ,CAAC,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACzC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAAI,wFAS9B,qBAAqB,sBAuIvB,CAAC;AAkEF;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,GAAI,2BAGnC;IACD,aAAa,EAAE,KAAK,CAAC,iBAAiB,CAAC,CAAC;IACxC,KAAK,EAAE,gBAAgB,CAAC;CACzB,sBA2EA,CAAC"}
|