@rowsncolumns/spreadsheet 10.4.1 → 10.4.4
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/atoms/sheet-context.atom.d.ts +2 -0
- package/dist/cjs/atoms/sheet-context.atom.d.ts.map +1 -1
- package/dist/cjs/atoms/sheet-context.atom.js +8 -2
- package/dist/cjs/atoms/sort-dialog.atom.d.ts +6 -2
- package/dist/cjs/atoms/sort-dialog.atom.d.ts.map +1 -1
- package/dist/cjs/components/canvas-grid/index.d.ts.map +1 -1
- package/dist/cjs/components/canvas-grid/index.js +4 -2
- package/dist/cjs/components/canvas-grid/typings.d.ts +10 -0
- package/dist/cjs/components/canvas-grid/typings.d.ts.map +1 -1
- 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 +21 -5
- 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/sheet-tabs/index.d.ts.map +1 -1
- package/dist/cjs/components/sheet-tabs/index.js +19 -5
- package/dist/cjs/components/sort-range-dialog/index.d.ts +20 -3
- package/dist/cjs/components/sort-range-dialog/index.d.ts.map +1 -1
- package/dist/cjs/components/sort-range-dialog/index.js +20 -6
- package/dist/cjs/components/toolbar/cell-style-selector.d.ts +9 -0
- package/dist/cjs/components/toolbar/cell-style-selector.d.ts.map +1 -1
- package/dist/cjs/components/toolbar/cell-style-selector.js +12 -3
- package/dist/cjs/components/toolbar/merge-cells-selector.d.ts +27 -1
- package/dist/cjs/components/toolbar/merge-cells-selector.d.ts.map +1 -1
- package/dist/cjs/components/toolbar/merge-cells-selector.js +106 -8
- 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/config.d.ts +6 -0
- package/dist/cjs/config.d.ts.map +1 -1
- package/dist/cjs/config.js +7 -1
- package/dist/cjs/hooks/use-conditional-formatting.d.ts +8 -0
- package/dist/cjs/hooks/use-conditional-formatting.d.ts.map +1 -1
- package/dist/cjs/hooks/use-conditional-formatting.js +128 -77
- package/dist/cjs/hooks/use-editable.d.ts.map +1 -1
- package/dist/cjs/hooks/use-editable.js +59 -1
- package/dist/cjs/hooks/use-selection.d.ts +6 -1
- package/dist/cjs/hooks/use-selection.d.ts.map +1 -1
- package/dist/cjs/hooks/use-selection.js +37 -1
- package/dist/cjs/release-date.js +1 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/cjs/types.d.ts +64 -0
- package/dist/cjs/types.d.ts.map +1 -1
- package/dist/esm/atoms/sheet-context.atom.d.ts +2 -0
- package/dist/esm/atoms/sheet-context.atom.d.ts.map +1 -1
- package/dist/esm/atoms/sheet-context.atom.js +3 -0
- package/dist/esm/atoms/sort-dialog.atom.d.ts +6 -2
- package/dist/esm/atoms/sort-dialog.atom.d.ts.map +1 -1
- package/dist/esm/components/canvas-grid/index.d.ts.map +1 -1
- package/dist/esm/components/canvas-grid/index.js +3 -1
- package/dist/esm/components/canvas-grid/typings.d.ts +10 -0
- package/dist/esm/components/canvas-grid/typings.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 +21 -5
- 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/sheet-tabs/index.d.ts.map +1 -1
- package/dist/esm/components/sheet-tabs/index.js +20 -6
- package/dist/esm/components/sort-range-dialog/index.d.ts +20 -3
- package/dist/esm/components/sort-range-dialog/index.d.ts.map +1 -1
- package/dist/esm/components/sort-range-dialog/index.js +17 -5
- package/dist/esm/components/toolbar/cell-style-selector.d.ts +9 -0
- package/dist/esm/components/toolbar/cell-style-selector.d.ts.map +1 -1
- package/dist/esm/components/toolbar/cell-style-selector.js +11 -2
- package/dist/esm/components/toolbar/merge-cells-selector.d.ts +27 -1
- package/dist/esm/components/toolbar/merge-cells-selector.d.ts.map +1 -1
- package/dist/esm/components/toolbar/merge-cells-selector.js +94 -10
- 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/config.d.ts +6 -0
- package/dist/esm/config.d.ts.map +1 -1
- package/dist/esm/config.js +6 -0
- package/dist/esm/hooks/use-conditional-formatting.d.ts +8 -0
- package/dist/esm/hooks/use-conditional-formatting.d.ts.map +1 -1
- package/dist/esm/hooks/use-conditional-formatting.js +50 -13
- package/dist/esm/hooks/use-editable.d.ts.map +1 -1
- package/dist/esm/hooks/use-editable.js +48 -2
- package/dist/esm/hooks/use-selection.d.ts +6 -1
- package/dist/esm/hooks/use-selection.d.ts.map +1 -1
- package/dist/esm/hooks/use-selection.js +37 -1
- package/dist/esm/release-date.js +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/esm/types.d.ts +64 -0
- package/dist/esm/types.d.ts.map +1 -1
- package/dist/spreadsheet.min.css +1 -1
- package/package.json +5 -5
|
@@ -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,uBA8lBlB,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.
|
|
@@ -135,13 +140,21 @@ exports.FilterBoxWrapper = (0, react_2.memo)(function (_a) {
|
|
|
135
140
|
// filter box is currently anchored on (that's the column we computed
|
|
136
141
|
// distinct colors/icons for). Other columns expose value + custom-list
|
|
137
142
|
// sort only.
|
|
143
|
+
// Excel labels sort levels by the header row — a table's column names, or
|
|
144
|
+
// the first row of a filtered range (an AutoFilter range always starts on
|
|
145
|
+
// its header row). A blank header falls back to the column letter.
|
|
146
|
+
var getFormattedValue = (0, atoms_1.useGetFormattedValue)();
|
|
138
147
|
var sortDialogColumns = (0, react_2.useMemo)(function () {
|
|
139
|
-
var _a
|
|
148
|
+
var _a, _b;
|
|
149
|
+
var _c = filter.range, startColumnIndex = _c.startColumnIndex, endColumnIndex = _c.endColumnIndex, startRowIndex = _c.startRowIndex;
|
|
140
150
|
var cols = [];
|
|
141
151
|
for (var c = startColumnIndex; c <= endColumnIndex; c++) {
|
|
142
152
|
var dim = c - startColumnIndex;
|
|
143
153
|
var isActive = dim === dimensionIndex;
|
|
144
|
-
|
|
154
|
+
var header = (0, exports.isTableView)(filter)
|
|
155
|
+
? (_b = (_a = filter.columns) === null || _a === void 0 ? void 0 : _a[dim]) === null || _b === void 0 ? void 0 : _b.name
|
|
156
|
+
: getFormattedValue === null || getFormattedValue === void 0 ? void 0 : getFormattedValue(sheetId, startRowIndex, c);
|
|
157
|
+
cols.push(tslib_1.__assign({ dimensionIndex: dim, label: "Column ".concat((0, utils_1.number2Alpha)(c)), header: header !== null && header !== void 0 ? header : undefined }, (isActive
|
|
145
158
|
? {
|
|
146
159
|
availableCellColors: availableCellColors,
|
|
147
160
|
availableFontColors: availableFontColors,
|
|
@@ -151,7 +164,9 @@ exports.FilterBoxWrapper = (0, react_2.memo)(function (_a) {
|
|
|
151
164
|
}
|
|
152
165
|
return cols;
|
|
153
166
|
}, [
|
|
154
|
-
filter
|
|
167
|
+
filter,
|
|
168
|
+
sheetId,
|
|
169
|
+
getFormattedValue,
|
|
155
170
|
dimensionIndex,
|
|
156
171
|
availableCellColors,
|
|
157
172
|
availableFontColors,
|
|
@@ -369,6 +384,7 @@ exports.FilterBoxWrapper = (0, react_2.memo)(function (_a) {
|
|
|
369
384
|
openSortDialog({
|
|
370
385
|
columns: sortDialogColumns,
|
|
371
386
|
customLists: customLists,
|
|
387
|
+
initialHasHeader: true,
|
|
372
388
|
onApply: onApplySort,
|
|
373
389
|
});
|
|
374
390
|
onClose();
|
|
@@ -401,7 +417,7 @@ exports.FilterBoxWrapper = (0, react_2.memo)(function (_a) {
|
|
|
401
417
|
react_1.default.createElement(icons_1.Cross2Icon, null)),
|
|
402
418
|
clearLabel),
|
|
403
419
|
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 }),
|
|
420
|
+
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
421
|
react_1.default.createElement(ui_1.Separator, { className: "mt-2 mb-2" }),
|
|
406
422
|
react_1.default.createElement(filter_by_color_1.FilterByColor, { colorFilter: colorFilter === undefined
|
|
407
423
|
? 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
|
]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../components/sheet-tabs/index.tsx"],"names":[],"mappings":"AAqBA,OAAO,KAAyB,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../components/sheet-tabs/index.tsx"],"names":[],"mappings":"AAqBA,OAAO,KAAyB,MAAM,OAAO,CAAC;AAK9C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAIzC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,GAAI,CAAC,SAAS,KAAK,EAAE,QAAQ,CAAC,EAAE,KAClB,MACjD,CAAC;AAEF;;;;GAIG;AACH,iBAAS,kBAAkB,CAAC,CAAC,SAAS,KAAK,EAAE,EAC3C,MAAM,EACN,aAAa,EACb,aAAa,EACb,eAAe,EACf,WAAiC,EACjC,aAAa,EACb,kBAAkB,EAClB,WAAW,EACX,WAAW,EACX,WAAW,EACX,mBAAmB,EACnB,qBAAqB,EACrB,qBAAqB,EACrB,WAAW,EACX,aAAa,EACb,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EAChB,YAAY,EACZ,iBAAiB,EACjB,KAAK,EACL,QAAQ,EACR,sBAA8B,GAC/B,EAAE,cAAc,CAAC,CAAC,CAAC,qBAkVnB;AAED,eAAO,MAAM,SAAS,EAA+B,OAAO,kBAAkB,CAAC"}
|
|
@@ -11,6 +11,7 @@ var utils_1 = require("@rowsncolumns/utils");
|
|
|
11
11
|
var react_1 = require("react");
|
|
12
12
|
var react_2 = tslib_1.__importStar(require("react"));
|
|
13
13
|
var validation_1 = require("../../api/validation");
|
|
14
|
+
var validation_alert_atom_1 = require("../../atoms/validation-alert.atom");
|
|
14
15
|
var use_isomorphic_layout_effect_1 = require("../../hooks/use-isomorphic-layout-effect");
|
|
15
16
|
var context_menu_1 = require("./context-menu");
|
|
16
17
|
var sheet_tab_1 = require("./sheet-tab");
|
|
@@ -57,16 +58,29 @@ function SheetTabsComponent(_a) {
|
|
|
57
58
|
}
|
|
58
59
|
return !(0, validation_1.isProtectedSheet)(sheetId, protectedRanges);
|
|
59
60
|
}, [protectedRanges, readonly]);
|
|
61
|
+
var showValidationAlert = (0, validation_alert_atom_1.useShowValidationAlert)();
|
|
60
62
|
var canRename = (0, react_1.useCallback)(function (sheetId, newName) {
|
|
61
|
-
|
|
62
|
-
if ((0, utils_1.isNil)(newName) || newName.trim().length === 0) {
|
|
63
|
+
if ((0, utils_1.isNil)(newName) || !canEditSheet(sheetId)) {
|
|
63
64
|
return false;
|
|
64
65
|
}
|
|
65
|
-
|
|
66
|
+
// Excel's worksheet-name rules (rowsncolumns/spreadsheet#939 U-01): at most 31 characters,
|
|
67
|
+
// none of `\ / ? * [ ] :`, no leading / trailing apostrophe, not `History`, and unique
|
|
68
|
+
// case-insensitively (a sheet may re-case its own title). Every refusal shows Excel's own
|
|
69
|
+
// dialog; a blank name is the editor's "nothing typed" and just restores the title.
|
|
70
|
+
var validation = (0, utils_1.validateSheetName)(newName, sheets, sheetId);
|
|
71
|
+
if (!validation.ok) {
|
|
72
|
+
if (validation.reason !== "blank") {
|
|
73
|
+
void showValidationAlert({
|
|
74
|
+
style: "stop",
|
|
75
|
+
title: "Error",
|
|
76
|
+
message: validation.message,
|
|
77
|
+
confirm: false,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
66
80
|
return false;
|
|
67
81
|
}
|
|
68
|
-
return
|
|
69
|
-
}, [canEditSheet, sheets]);
|
|
82
|
+
return true;
|
|
83
|
+
}, [canEditSheet, sheets, showValidationAlert]);
|
|
70
84
|
var canDeleteSheet = (0, react_1.useCallback)(function (sheetId) {
|
|
71
85
|
if (!canEditSheet(sheetId)) {
|
|
72
86
|
return false;
|
|
@@ -10,7 +10,13 @@ import type { SortSpecs } from "../../types";
|
|
|
10
10
|
*/
|
|
11
11
|
export type SortDialogColumn = {
|
|
12
12
|
dimensionIndex: number;
|
|
13
|
+
/** Positional label ("Column B") — the fallback when the header cell is blank. */
|
|
13
14
|
label: string;
|
|
15
|
+
/**
|
|
16
|
+
* Text of the column's first-row cell. Shown instead of `label` while
|
|
17
|
+
* "My data has headers" is on (Excel labels sort levels by header).
|
|
18
|
+
*/
|
|
19
|
+
header?: string | null;
|
|
14
20
|
availableCellColors?: string[];
|
|
15
21
|
availableFontColors?: string[];
|
|
16
22
|
availableIcons?: AvailableIcon[];
|
|
@@ -33,13 +39,24 @@ export type SortDialogProps = {
|
|
|
33
39
|
* first column.
|
|
34
40
|
*/
|
|
35
41
|
initialSpecs?: SortSpecs[];
|
|
42
|
+
/**
|
|
43
|
+
* Initial state of Excel's "My data has headers". The toggle renders when
|
|
44
|
+
* this is set or any column carries a `header`; a host that omits both keeps
|
|
45
|
+
* the header-less dialog.
|
|
46
|
+
*/
|
|
47
|
+
initialHasHeader?: boolean;
|
|
36
48
|
/**
|
|
37
49
|
* Emits the assembled ordered `SortSpecs[]` (primary first, then
|
|
38
|
-
* tiebreakers). The host routes these to
|
|
50
|
+
* tiebreakers) plus the header choice. The host routes these to
|
|
51
|
+
* `onSortTable` / `onSortRange` (trimming the header row when `hasHeader`).
|
|
39
52
|
* The dialog never touches state directly.
|
|
40
53
|
*/
|
|
41
|
-
onApply(specs: SortSpecs[]
|
|
54
|
+
onApply(specs: SortSpecs[], options: {
|
|
55
|
+
hasHeader: boolean;
|
|
56
|
+
}): void;
|
|
42
57
|
};
|
|
58
|
+
/** The label a sort level shows for a column: its header while the data has headers, else its letter. */
|
|
59
|
+
export declare const sortColumnLabel: (column: Pick<SortDialogColumn, "label" | "header">, hasHeader: boolean) => string;
|
|
43
60
|
/**
|
|
44
61
|
* Custom multi-level sort dialog. Mirrors Excel's "Sort…" dialog and
|
|
45
62
|
* Google Sheets' "Sort range — advanced". Each row is a sort level:
|
|
@@ -51,7 +68,7 @@ export type SortDialogProps = {
|
|
|
51
68
|
* `SortSpecs[]` via `onApply`; the host disambiguates table vs range and
|
|
52
69
|
* routes through its existing sort callbacks.
|
|
53
70
|
*/
|
|
54
|
-
export declare const SortRangeDialog: React.MemoExoticComponent<({ open, onOpenChange, columns, customLists, initialSpecs, onApply, }: SortDialogProps) => React.JSX.Element>;
|
|
71
|
+
export declare const SortRangeDialog: React.MemoExoticComponent<({ open, onOpenChange, columns, customLists, initialSpecs, initialHasHeader, onApply, }: SortDialogProps) => React.JSX.Element>;
|
|
55
72
|
/**
|
|
56
73
|
* Host-mounted, atom-driven wrapper around {@link SortRangeDialog}. Mount this
|
|
57
74
|
* once in the host (alongside the other editor dialogs); the filter box opens
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../components/sort-range-dialog/index.tsx"],"names":[],"mappings":"AAyBA,OAAO,KAA0D,MAAM,OAAO,CAAC;AAE/E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,OAAO,KAAK,EAAa,SAAS,EAAE,MAAM,aAAa,CAAC;AAQxD;;;;;;GAMG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B,cAAc,CAAC,EAAE,aAAa,EAAE,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,2BAA2B;IAC3B,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAAC;IAClC,oCAAoC;IACpC,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC;IACzB;;;;OAIG;IACH,YAAY,CAAC,EAAE,SAAS,EAAE,CAAC;IAC3B;;;;OAIG;IACH,OAAO,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../components/sort-range-dialog/index.tsx"],"names":[],"mappings":"AAyBA,OAAO,KAA0D,MAAM,OAAO,CAAC;AAE/E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,OAAO,KAAK,EAAa,SAAS,EAAE,MAAM,aAAa,CAAC;AAQxD;;;;;;GAMG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,cAAc,EAAE,MAAM,CAAC;IACvB,kFAAkF;IAClF,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B,cAAc,CAAC,EAAE,aAAa,EAAE,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,2BAA2B;IAC3B,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAAC;IAClC,oCAAoC;IACpC,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC;IACzB;;;;OAIG;IACH,YAAY,CAAC,EAAE,SAAS,EAAE,CAAC;IAC3B;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;;;;;OAKG;IACH,OAAO,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,OAAO,EAAE;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;CACpE,CAAC;AAEF,yGAAyG;AACzG,eAAO,MAAM,eAAe,GAC1B,QAAQ,IAAI,CAAC,gBAAgB,EAAE,OAAO,GAAG,QAAQ,CAAC,EAClD,WAAW,OAAO,KACjB,MAGF,CAAC;AA2DF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,eAAe,qHASvB,eAAe,uBAiWnB,CAAC;AAIF;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB;;;CAgB/B,CAAC"}
|