@rowsncolumns/spreadsheet 10.4.0 → 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.
Files changed (42) hide show
  1. package/dist/cjs/components/filter-box/date-tree.d.ts +23 -5
  2. package/dist/cjs/components/filter-box/date-tree.d.ts.map +1 -1
  3. package/dist/cjs/components/filter-box/date-tree.js +76 -11
  4. package/dist/cjs/components/filter-box/filter-by-condition.d.ts +26 -1
  5. package/dist/cjs/components/filter-box/filter-by-condition.d.ts.map +1 -1
  6. package/dist/cjs/components/filter-box/filter-by-condition.js +108 -9
  7. package/dist/cjs/components/filter-box/index.d.ts.map +1 -1
  8. package/dist/cjs/components/filter-box/index.js +7 -2
  9. package/dist/cjs/components/formula-input/index.d.ts.map +1 -1
  10. package/dist/cjs/components/formula-input/index.js +26 -4
  11. package/dist/cjs/components/toolbar/table-style-editor.d.ts +85 -0
  12. package/dist/cjs/components/toolbar/table-style-editor.d.ts.map +1 -0
  13. package/dist/cjs/components/toolbar/table-style-editor.js +235 -0
  14. package/dist/cjs/components/toolbar/table-style-selector.d.ts +7 -7
  15. package/dist/cjs/components/toolbar/table-style-selector.d.ts.map +1 -1
  16. package/dist/cjs/components/toolbar/table-style-selector.js +77 -87
  17. package/dist/cjs/release-date.js +1 -1
  18. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  19. package/dist/cjs/types.d.ts +10 -0
  20. package/dist/cjs/types.d.ts.map +1 -1
  21. package/dist/esm/components/filter-box/date-tree.d.ts +23 -5
  22. package/dist/esm/components/filter-box/date-tree.d.ts.map +1 -1
  23. package/dist/esm/components/filter-box/date-tree.js +62 -10
  24. package/dist/esm/components/filter-box/filter-by-condition.d.ts +26 -1
  25. package/dist/esm/components/filter-box/filter-by-condition.d.ts.map +1 -1
  26. package/dist/esm/components/filter-box/filter-by-condition.js +78 -12
  27. package/dist/esm/components/filter-box/index.d.ts.map +1 -1
  28. package/dist/esm/components/filter-box/index.js +7 -2
  29. package/dist/esm/components/formula-input/index.d.ts.map +1 -1
  30. package/dist/esm/components/formula-input/index.js +26 -4
  31. package/dist/esm/components/toolbar/table-style-editor.d.ts +85 -0
  32. package/dist/esm/components/toolbar/table-style-editor.d.ts.map +1 -0
  33. package/dist/esm/components/toolbar/table-style-editor.js +189 -0
  34. package/dist/esm/components/toolbar/table-style-selector.d.ts +7 -7
  35. package/dist/esm/components/toolbar/table-style-selector.d.ts.map +1 -1
  36. package/dist/esm/components/toolbar/table-style-selector.js +74 -81
  37. package/dist/esm/release-date.js +1 -1
  38. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  39. package/dist/esm/types.d.ts +10 -0
  40. package/dist/esm/types.d.ts.map +1 -1
  41. package/dist/spreadsheet.min.css +1 -1
  42. package/package.json +10 -10
@@ -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 — digits separated by `/ - .` or a month name — which keeps plain
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). Newest first at every level, matching Excel.
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. `blankLabel` (the "(Blanks)" entry) is skipped; callers
23
- * render it beside the tree.
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;;;;;;;;GAQG;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;AAqBF,0FAA0F;AAC1F,eAAO,MAAM,oBAAoB,GAAI,OAAO,MAAM,KAAG,IAAI,GAAG,SAS3D,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,aAAa,GACxB,QAAQ,SAAS,MAAM,EAAE,EACzB,YAAY,MAAM,KACjB,YAAY,EAAE,GAAG,SAgDnB,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"}
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 — digits separated by `/ - .` or a month name — which keeps plain
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). Newest first at every level, matching Excel.
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
- var DATE_SHAPE = /(\d[/.\-]\d)|([A-Za-z]{3,}.*\d)|(\d.*[A-Za-z]{3,})/;
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. `blankLabel` (the "(Blanks)" entry) is skipped; callers
49
- * render it beside the tree.
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 desc = function (a, b) { return b - a; };
100
- return tslib_1.__spreadArray([], tslib_1.__read(years.keys()), false).sort(desc).map(function (y) {
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(desc).map(function (m) {
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(desc).map(function (d) { return ({
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":"AAWA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAKxC,OAAO,KAAK,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAEtE,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,sBAAsB,GAAG;IAC5B,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;AAsKF,eAAO,MAAM,iBAAiB,GAAI,6PAe/B,sBAAsB,sBAuFxB,CAAC"}
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
- labels.map(function (_a) {
50
- var condition = _a.condition, label = _a.label;
51
- return (react_1.default.createElement(ui_1.SelectItem, { key: condition, value: condition }, label));
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;AAI3D,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,uBA4kBlB,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"}
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;AAatC,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,sGAiIxB,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
- setFormulaMode((0, utils_1.isAFormula)(value));
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
- setFormulaMode,
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
- setFormulaMode((0, utils_1.isAFormula)(value));
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
- setFormulaMode,
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"}