@planningcenter/tapestry 4.10.0-rc.5 → 4.10.0-rc.7
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/components/DataTable/dateFilter.d.ts +7 -1
- package/dist/components/DataTable/dateFilter.d.ts.map +1 -1
- package/dist/components/DataTable/dateFilter.js +43 -8
- package/dist/components/DataTable/dateFilter.js.map +1 -1
- package/dist/components/DataTable/numberFilter.js +4 -1
- package/dist/components/DataTable/numberFilter.js.map +1 -1
- package/dist/reactRender.css +798 -794
- package/dist/reactRender.css.map +1 -1
- package/dist/reactRenderLegacy.css +798 -794
- package/dist/reactRenderLegacy.css.map +1 -1
- package/dist/unstable.css +4 -0
- package/dist/unstable.css.map +1 -1
- package/package.json +2 -2
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type DatePickerDateValue } from "../date-picker";
|
|
1
2
|
import type { FilterItemConfig, FilterNodeItem } from "./FilterTypes";
|
|
2
3
|
/** ISO `YYYY-MM-DD` strings; a pair for `between`. */
|
|
3
4
|
export type DateFilterValue = string | [string, string];
|
|
@@ -81,6 +82,11 @@ declare const ALL_DATE_OPERATORS: ({
|
|
|
81
82
|
readonly name: "between";
|
|
82
83
|
})[];
|
|
83
84
|
export type DateOperatorName = (typeof ALL_DATE_OPERATORS)[number]["name"];
|
|
84
|
-
|
|
85
|
+
/** Inclusive bounds on the dates the filter's pickers allow. */
|
|
86
|
+
export interface DateLimits {
|
|
87
|
+
max?: DatePickerDateValue;
|
|
88
|
+
min?: DatePickerDateValue;
|
|
89
|
+
}
|
|
90
|
+
export declare function dateFilter({ excludes, includes, max, min, ...config }: FilterItemConfig<DateOperatorName> & DateLimits): FilterNodeItem;
|
|
85
91
|
export {};
|
|
86
92
|
//# sourceMappingURL=dateFilter.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dateFilter.d.ts","sourceRoot":"","sources":["../../../src/components/DataTable/dateFilter.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"dateFilter.d.ts","sourceRoot":"","sources":["../../../src/components/DataTable/dateFilter.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,mBAAmB,EAAE,MAAM,yBAAyB,CAAA;AAW9E,OAAO,KAAK,EAEV,gBAAgB,EAChB,cAAc,EAEf,MAAM,eAAe,CAAA;AAGtB,sDAAsD;AACtD,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;AAIvD,eAAO,MAAM,OAAO;;;;;;CAIS,CAAA;AAC7B,eAAO,MAAM,EAAE;;;;;;CAIc,CAAA;AAC7B,eAAO,MAAM,EAAE;;;;;;CAIc,CAAA;AAC7B,eAAO,MAAM,GAAG;;;;;;CAIa,CAAA;AAC7B,eAAO,MAAM,GAAG;;;;;;CAIa,CAAA;AAC7B,eAAO,MAAM,UAAU;;;;;;CAIM,CAAA;AAE7B,QAAA,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAA0C,CAAA;AAGlE,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAA;AAuB1E,gEAAgE;AAChE,MAAM,WAAW,UAAU;IACzB,GAAG,CAAC,EAAE,mBAAmB,CAAA;IACzB,GAAG,CAAC,EAAE,mBAAmB,CAAA;CAC1B;AAcD,wBAAgB,UAAU,CAAC,EACzB,QAAQ,EACR,QAAQ,EACR,GAAG,EACH,GAAG,EACH,GAAG,MAAM,EACV,EAAE,gBAAgB,CAAC,gBAAgB,CAAC,GAAG,UAAU,GAAG,cAAc,CAiBlE"}
|
|
@@ -41,10 +41,32 @@ const ALL_DATE_OPERATORS = [DATE_IS, LT, GT, LTE, GTE, DATE_RANGE];
|
|
|
41
41
|
const DEFAULT_DATE_OPERATORS = [DATE_IS, LT, GT, DATE_RANGE];
|
|
42
42
|
const formatter = new $12a3c853105e5a70$export$ad991b66133851cf(new Intl.DateTimeFormat().resolvedOptions().locale, { dateStyle: "medium" });
|
|
43
43
|
const formatDate = (iso) => formatter.format($58246871e4652552$export$6b862160d295c8e(iso).toDate($ad063034c8620db8$export$aa8b41735afcabd2()));
|
|
44
|
-
|
|
44
|
+
const toDateValue = (date) => typeof date === "string" ? $58246871e4652552$export$6b862160d295c8e(date) : date;
|
|
45
|
+
function earlier(a, b) {
|
|
46
|
+
if (a === undefined || b === undefined)
|
|
47
|
+
return a ?? b;
|
|
48
|
+
return toDateValue(a).compare(toDateValue(b)) <= 0 ? a : b;
|
|
49
|
+
}
|
|
50
|
+
function later(a, b) {
|
|
51
|
+
if (a === undefined || b === undefined)
|
|
52
|
+
return a ?? b;
|
|
53
|
+
return toDateValue(a).compare(toDateValue(b)) >= 0 ? a : b;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The pickers only flag typed dates outside the limits as invalid, so the
|
|
57
|
+
* editors clear the value rather than commit them.
|
|
58
|
+
*/
|
|
59
|
+
function toValidDate(date, { max, min }) {
|
|
60
|
+
if (!date)
|
|
61
|
+
return undefined;
|
|
62
|
+
const isWithinLimits = (min === undefined || date.compare(toDateValue(min)) >= 0) &&
|
|
63
|
+
(max === undefined || date.compare(toDateValue(max)) <= 0);
|
|
64
|
+
return isWithinLimits ? date.toString() : undefined;
|
|
65
|
+
}
|
|
66
|
+
function dateFilter({ excludes, includes, max, min, ...config }) {
|
|
45
67
|
const filter = {
|
|
46
68
|
...config,
|
|
47
|
-
createActiveFilterInstance: (activeFilter, operator) => new DateActiveFilter(filter, operator, activeFilter.value),
|
|
69
|
+
createActiveFilterInstance: (activeFilter, operator) => new DateActiveFilter(filter, operator, activeFilter.value, { max, min }),
|
|
48
70
|
operators: resolveOperators(ALL_DATE_OPERATORS, {
|
|
49
71
|
default: DEFAULT_DATE_OPERATORS,
|
|
50
72
|
excludes,
|
|
@@ -54,6 +76,11 @@ function dateFilter({ excludes, includes, ...config }) {
|
|
|
54
76
|
return filter;
|
|
55
77
|
}
|
|
56
78
|
class DateActiveFilter extends ActiveFilterInstance {
|
|
79
|
+
limits;
|
|
80
|
+
constructor(filter, operator, value, limits) {
|
|
81
|
+
super(filter, operator, value);
|
|
82
|
+
this.limits = limits;
|
|
83
|
+
}
|
|
57
84
|
formatValue() {
|
|
58
85
|
if (Array.isArray(this.value)) {
|
|
59
86
|
const [start, end] = this.value;
|
|
@@ -70,7 +97,7 @@ class DateActiveFilter extends ActiveFilterInstance {
|
|
|
70
97
|
const range = Array.isArray(this.value)
|
|
71
98
|
? this.value
|
|
72
99
|
: [this.value, this.value];
|
|
73
|
-
return (React__default.createElement(DateRangeEditor, { label: label, value: range, onCommit: (value) => {
|
|
100
|
+
return (React__default.createElement(DateRangeEditor, { label: label, max: this.limits.max, min: this.limits.min, value: range, onCommit: (value) => {
|
|
74
101
|
const complete = value.length === 2 && value.every((v) => v !== undefined);
|
|
75
102
|
onChange({ value }, { complete });
|
|
76
103
|
} }));
|
|
@@ -80,18 +107,26 @@ class DateActiveFilter extends ActiveFilterInstance {
|
|
|
80
107
|
// eslint-disable-next-line jsx-a11y/no-autofocus -- the editor only ever renders inside a chip popover that just opened, per ARIA dialog practices
|
|
81
108
|
, {
|
|
82
109
|
// eslint-disable-next-line jsx-a11y/no-autofocus -- the editor only ever renders inside a chip popover that just opened, per ARIA dialog practices
|
|
83
|
-
autoFocus: true, hideLabel: true,
|
|
110
|
+
autoFocus: true, hideLabel: true,
|
|
111
|
+
// Uncontrolled so a typed date outside the limits stays visible
|
|
112
|
+
// (and flagged invalid) after the value is cleared.
|
|
113
|
+
defaultValue: single, label: label, max: this.limits.max, min: this.limits.min, onChange: (date) => {
|
|
114
|
+
const value = toValidDate(date, this.limits);
|
|
115
|
+
onChange({ value }, { complete: value !== undefined });
|
|
116
|
+
} }));
|
|
84
117
|
}
|
|
85
118
|
}
|
|
86
|
-
function DateRangeEditor({ label, onCommit, value: [start, end], }) {
|
|
87
|
-
|
|
119
|
+
function DateRangeEditor({ label, max, min, onCommit, value: [start, end], }) {
|
|
120
|
+
const startLimits = { max: earlier(max, end), min };
|
|
121
|
+
const endLimits = { max, min: later(min, start) };
|
|
122
|
+
return (React__default.createElement(Flex, { align: "center", gap: 1 },
|
|
88
123
|
React__default.createElement(DatePicker
|
|
89
124
|
// eslint-disable-next-line jsx-a11y/no-autofocus -- the editor only ever renders inside a chip popover that just opened, per ARIA dialog practices
|
|
90
125
|
, {
|
|
91
126
|
// eslint-disable-next-line jsx-a11y/no-autofocus -- the editor only ever renders inside a chip popover that just opened, per ARIA dialog practices
|
|
92
|
-
autoFocus: true, hideLabel: true, label: `${label} from`,
|
|
127
|
+
autoFocus: true, hideLabel: true, defaultValue: start, label: `${label} from`, max: startLimits.max, min: startLimits.min, onChange: (date) => onCommit([toValidDate(date, startLimits), end]) }),
|
|
93
128
|
React__default.createElement("div", null, "and"),
|
|
94
|
-
React__default.createElement(DatePicker, { hideLabel: true, label: `${label} to`,
|
|
129
|
+
React__default.createElement(DatePicker, { hideLabel: true, defaultValue: end, label: `${label} to`, max: endLimits.max, min: endLimits.min, onChange: (date) => onCommit([start, toValidDate(date, endLimits)]) })));
|
|
95
130
|
}
|
|
96
131
|
|
|
97
132
|
export { DATE_IS, DATE_RANGE, GT, GTE, LT, LTE, dateFilter };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dateFilter.js","sources":["../../../src/components/DataTable/dateFilter.tsx"],"sourcesContent":["import { DatePicker } from \"@components/date-picker\"\nimport { Flex } from \"@components/flex\"\nimport {\n DateFormatter,\n getLocalTimeZone,\n parseDate,\n} from \"@internationalized/date\"\nimport React from \"react\"\n\nimport { ActiveFilterInstance } from \"./ActiveFilterInstance\"\nimport type {\n FilterChange,\n FilterItemConfig,\n FilterNodeItem,\n Operator,\n} from \"./FilterTypes\"\nimport { resolveOperators } from \"./resolveOperators\"\n\n/** ISO `YYYY-MM-DD` strings; a pair for `between`. */\nexport type DateFilterValue = string | [string, string]\n\nexport const DATE_IS = {\n inputType: { type: \"date\" },\n label: \"is\",\n name: \"eq\",\n} as const satisfies Operator\nexport const LT = {\n inputType: { type: \"date\" },\n label: \"is before\",\n name: \"lt\",\n} as const satisfies Operator\nexport const GT = {\n inputType: { type: \"date\" },\n label: \"is after\",\n name: \"gt\",\n} as const satisfies Operator\nexport const LTE = {\n inputType: { type: \"date\" },\n label: \"is on or before\",\n name: \"lte\",\n} as const satisfies Operator\nexport const GTE = {\n inputType: { type: \"date\" },\n label: \"is on or after\",\n name: \"gte\",\n} as const satisfies Operator\nexport const DATE_RANGE = {\n inputType: { type: \"date-range\" },\n label: \"is between\",\n name: \"between\",\n} as const satisfies Operator\n\nconst ALL_DATE_OPERATORS = [DATE_IS, LT, GT, LTE, GTE, DATE_RANGE]\nconst DEFAULT_DATE_OPERATORS = [DATE_IS, LT, GT, DATE_RANGE]\n\nexport type DateOperatorName = (typeof ALL_DATE_OPERATORS)[number][\"name\"]\n\nconst formatter = new DateFormatter(\n new Intl.DateTimeFormat().resolvedOptions().locale,\n { dateStyle: \"medium\" }\n)\n\nconst formatDate = (iso: string) =>\n formatter.format(parseDate(iso).toDate(getLocalTimeZone()))\n\nexport function dateFilter({\n excludes,\n includes,\n ...config\n}: FilterItemConfig<DateOperatorName>): FilterNodeItem {\n const filter: FilterNodeItem = {\n ...config,\n createActiveFilterInstance: (activeFilter, operator) =>\n new DateActiveFilter(\n filter,\n operator,\n activeFilter.value as DateFilterValue\n ),\n operators: resolveOperators(ALL_DATE_OPERATORS, {\n default: DEFAULT_DATE_OPERATORS,\n excludes,\n includes,\n }),\n }\n return filter\n}\n\nclass DateActiveFilter extends ActiveFilterInstance<DateFilterValue> {\n formatValue() {\n if (Array.isArray(this.value)) {\n const [start, end] = this.value\n if (start === undefined || end === undefined) {\n return [start, end].filter((date) => date !== undefined).map(formatDate)\n }\n return [formatDate(start), \"and\", formatDate(end)]\n }\n return this.value === undefined ? \"\" : formatDate(this.value)\n }\n\n renderEditor(onChange: FilterChange) {\n const { label } = this.operator\n\n if (this.operator.inputType.type === \"date-range\") {\n const range: [string, string] = Array.isArray(this.value)\n ? this.value\n : [this.value, this.value]\n return (\n <DateRangeEditor\n label={label}\n value={range}\n onCommit={(value) => {\n const complete =\n value.length === 2 && value.every((v) => v !== undefined)\n onChange({ value }, { complete })\n }}\n />\n )\n }\n const single = Array.isArray(this.value) ? this.value[0] : this.value\n return (\n <DatePicker\n // eslint-disable-next-line jsx-a11y/no-autofocus -- the editor only ever renders inside a chip popover that just opened, per ARIA dialog practices\n autoFocus\n hideLabel\n label={label}\n value={single}\n onChange={(date) => date && onChange({ value: date.toString() })}\n />\n )\n }\n}\n\nfunction DateRangeEditor({\n label,\n onCommit,\n value: [start, end],\n}: {\n label: string\n onCommit: (value: [string, string]) => void\n value: [string, string]\n}) {\n return (\n <Flex gap={1}>\n <DatePicker\n // eslint-disable-next-line jsx-a11y/no-autofocus -- the editor only ever renders inside a chip popover that just opened, per ARIA dialog practices\n autoFocus\n hideLabel\n label={`${label} from`}\n value={start}\n onChange={(date) => date && onCommit([date.toString(), end])}\n />\n <div>and</div>\n <DatePicker\n hideLabel\n label={`${label} to`}\n value={end}\n onChange={(date) => date && onCommit([start, date.toString()])}\n />\n </Flex>\n )\n}\n"],"names":["DateFormatter","parseDate","getLocalTimeZone","React"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"dateFilter.js","sources":["../../../src/components/DataTable/dateFilter.tsx"],"sourcesContent":["import { DatePicker, type DatePickerDateValue } from \"@components/date-picker\"\nimport { Flex } from \"@components/flex\"\nimport {\n DateFormatter,\n type DateValue,\n getLocalTimeZone,\n parseDate,\n} from \"@internationalized/date\"\nimport React from \"react\"\n\nimport { ActiveFilterInstance } from \"./ActiveFilterInstance\"\nimport type {\n FilterChange,\n FilterItemConfig,\n FilterNodeItem,\n Operator,\n} from \"./FilterTypes\"\nimport { resolveOperators } from \"./resolveOperators\"\n\n/** ISO `YYYY-MM-DD` strings; a pair for `between`. */\nexport type DateFilterValue = string | [string, string]\n\ntype DateRange = [string | undefined, string | undefined]\n\nexport const DATE_IS = {\n inputType: { type: \"date\" },\n label: \"is\",\n name: \"eq\",\n} as const satisfies Operator\nexport const LT = {\n inputType: { type: \"date\" },\n label: \"is before\",\n name: \"lt\",\n} as const satisfies Operator\nexport const GT = {\n inputType: { type: \"date\" },\n label: \"is after\",\n name: \"gt\",\n} as const satisfies Operator\nexport const LTE = {\n inputType: { type: \"date\" },\n label: \"is on or before\",\n name: \"lte\",\n} as const satisfies Operator\nexport const GTE = {\n inputType: { type: \"date\" },\n label: \"is on or after\",\n name: \"gte\",\n} as const satisfies Operator\nexport const DATE_RANGE = {\n inputType: { type: \"date-range\" },\n label: \"is between\",\n name: \"between\",\n} as const satisfies Operator\n\nconst ALL_DATE_OPERATORS = [DATE_IS, LT, GT, LTE, GTE, DATE_RANGE]\nconst DEFAULT_DATE_OPERATORS = [DATE_IS, LT, GT, DATE_RANGE]\n\nexport type DateOperatorName = (typeof ALL_DATE_OPERATORS)[number][\"name\"]\n\nconst formatter = new DateFormatter(\n new Intl.DateTimeFormat().resolvedOptions().locale,\n { dateStyle: \"medium\" }\n)\n\nconst formatDate = (iso: string) =>\n formatter.format(parseDate(iso).toDate(getLocalTimeZone()))\n\nconst toDateValue = (date: DatePickerDateValue) =>\n typeof date === \"string\" ? parseDate(date) : date\n\nfunction earlier(a?: DatePickerDateValue, b?: DatePickerDateValue) {\n if (a === undefined || b === undefined) return a ?? b\n return toDateValue(a).compare(toDateValue(b)) <= 0 ? a : b\n}\n\nfunction later(a?: DatePickerDateValue, b?: DatePickerDateValue) {\n if (a === undefined || b === undefined) return a ?? b\n return toDateValue(a).compare(toDateValue(b)) >= 0 ? a : b\n}\n\n/** Inclusive bounds on the dates the filter's pickers allow. */\nexport interface DateLimits {\n max?: DatePickerDateValue\n min?: DatePickerDateValue\n}\n\n/**\n * The pickers only flag typed dates outside the limits as invalid, so the\n * editors clear the value rather than commit them.\n */\nfunction toValidDate(date: DateValue | null, { max, min }: DateLimits) {\n if (!date) return undefined\n const isWithinLimits =\n (min === undefined || date.compare(toDateValue(min)) >= 0) &&\n (max === undefined || date.compare(toDateValue(max)) <= 0)\n return isWithinLimits ? date.toString() : undefined\n}\n\nexport function dateFilter({\n excludes,\n includes,\n max,\n min,\n ...config\n}: FilterItemConfig<DateOperatorName> & DateLimits): FilterNodeItem {\n const filter: FilterNodeItem = {\n ...config,\n createActiveFilterInstance: (activeFilter, operator) =>\n new DateActiveFilter(\n filter,\n operator,\n activeFilter.value as DateFilterValue,\n { max, min }\n ),\n operators: resolveOperators(ALL_DATE_OPERATORS, {\n default: DEFAULT_DATE_OPERATORS,\n excludes,\n includes,\n }),\n }\n return filter\n}\n\nclass DateActiveFilter extends ActiveFilterInstance<DateFilterValue> {\n constructor(\n filter: FilterNodeItem,\n operator: Operator,\n value: DateFilterValue,\n readonly limits: DateLimits\n ) {\n super(filter, operator, value)\n }\n\n formatValue() {\n if (Array.isArray(this.value)) {\n const [start, end] = this.value\n if (start === undefined || end === undefined) {\n return [start, end].filter((date) => date !== undefined).map(formatDate)\n }\n return [formatDate(start), \"and\", formatDate(end)]\n }\n return this.value === undefined ? \"\" : formatDate(this.value)\n }\n\n renderEditor(onChange: FilterChange) {\n const { label } = this.operator\n\n if (this.operator.inputType.type === \"date-range\") {\n const range: DateRange = Array.isArray(this.value)\n ? this.value\n : [this.value, this.value]\n return (\n <DateRangeEditor\n label={label}\n max={this.limits.max}\n min={this.limits.min}\n value={range}\n onCommit={(value) => {\n const complete =\n value.length === 2 && value.every((v) => v !== undefined)\n onChange({ value }, { complete })\n }}\n />\n )\n }\n const single = Array.isArray(this.value) ? this.value[0] : this.value\n return (\n <DatePicker\n // eslint-disable-next-line jsx-a11y/no-autofocus -- the editor only ever renders inside a chip popover that just opened, per ARIA dialog practices\n autoFocus\n hideLabel\n // Uncontrolled so a typed date outside the limits stays visible\n // (and flagged invalid) after the value is cleared.\n defaultValue={single}\n label={label}\n max={this.limits.max}\n min={this.limits.min}\n onChange={(date) => {\n const value = toValidDate(date, this.limits)\n onChange({ value }, { complete: value !== undefined })\n }}\n />\n )\n }\n}\n\nfunction DateRangeEditor({\n label,\n max,\n min,\n onCommit,\n value: [start, end],\n}: DateLimits & {\n label: string\n onCommit: (value: DateRange) => void\n value: DateRange\n}) {\n const startLimits = { max: earlier(max, end), min }\n const endLimits = { max, min: later(min, start) }\n\n return (\n <Flex align=\"center\" gap={1}>\n <DatePicker\n // eslint-disable-next-line jsx-a11y/no-autofocus -- the editor only ever renders inside a chip popover that just opened, per ARIA dialog practices\n autoFocus\n hideLabel\n defaultValue={start}\n label={`${label} from`}\n max={startLimits.max}\n min={startLimits.min}\n onChange={(date) => onCommit([toValidDate(date, startLimits), end])}\n />\n <div>and</div>\n <DatePicker\n hideLabel\n defaultValue={end}\n label={`${label} to`}\n max={endLimits.max}\n min={endLimits.min}\n onChange={(date) => onCommit([start, toValidDate(date, endLimits)])}\n />\n </Flex>\n )\n}\n"],"names":["DateFormatter","parseDate","getLocalTimeZone","React"],"mappings":"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"}
|
|
@@ -84,7 +84,10 @@ function NumberEditor({ labels, onCommit, values: initialValues, }) {
|
|
|
84
84
|
onCommit(values.current);
|
|
85
85
|
};
|
|
86
86
|
const changed = () => values.current.some((value, index) => value !== lastCommitted.current[index]);
|
|
87
|
-
|
|
87
|
+
const isRange = labels.length > 1;
|
|
88
|
+
return (React__default.createElement(Flex, { align: "center",
|
|
89
|
+
// Popover content aligns to start; stretch so both inputs fit
|
|
90
|
+
alignSelf: isRange ? "stretch" : undefined, className: isRange ? "tds-data-table-filters-number-range" : undefined, gap: 1, onBlur: () => {
|
|
88
91
|
if (changed())
|
|
89
92
|
commit();
|
|
90
93
|
} }, labels.map((label, index) => (React__default.createElement(React__default.Fragment, { key: label },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"numberFilter.js","sources":["../../../src/components/DataTable/numberFilter.tsx"],"sourcesContent":["import { Flex } from \"@components/flex\"\nimport { NumberStepper } from \"@components/number-stepper\"\nimport React, { useRef } from \"react\"\n\nimport { ActiveFilterInstance } from \"./ActiveFilterInstance\"\nimport type {\n FilterChange,\n FilterItemConfig,\n FilterNodeItem,\n Operator,\n} from \"./FilterTypes\"\nimport { resolveOperators } from \"./resolveOperators\"\n\nexport type NumberFilterValue = number | [number, number]\n\nexport const GTE = {\n inputType: { type: \"number\" },\n label: \">=\",\n name: \"gte\",\n} as const satisfies Operator\nexport const GT = {\n inputType: { type: \"number\" },\n label: \">\",\n name: \"gt\",\n} as const satisfies Operator\nexport const LT = {\n inputType: { type: \"number\" },\n label: \"<\",\n name: \"lt\",\n} as const satisfies Operator\nexport const EQ = {\n inputType: { type: \"number\" },\n label: \"is\",\n name: \"eq\",\n} as const satisfies Operator\nexport const LTE = {\n inputType: { type: \"number\" },\n label: \"<=\",\n name: \"lte\",\n} as const satisfies Operator\nexport const NUMBER_RANGE = {\n inputType: { type: \"number-range\" },\n label: \"between\",\n name: \"between\",\n} as const satisfies Operator\n\nconst ALL_NUMBER_OPERATORS = [EQ, GT, GTE, LT, LTE, NUMBER_RANGE]\nconst DEFAULT_NUMBER_OPERATORS = [GTE, LTE, NUMBER_RANGE]\n\nexport type NumberOperatorName = (typeof ALL_NUMBER_OPERATORS)[number][\"name\"]\n\nexport function numberFilter({\n excludes,\n includes,\n ...config\n}: FilterItemConfig<NumberOperatorName>): FilterNodeItem {\n const filter: FilterNodeItem = {\n ...config,\n createActiveFilterInstance: (activeFilter, operator) =>\n new NumberActiveFilter(\n filter,\n operator,\n activeFilter.value as NumberFilterValue\n ),\n operators: resolveOperators(ALL_NUMBER_OPERATORS, {\n default: DEFAULT_NUMBER_OPERATORS,\n excludes,\n includes,\n }),\n }\n return filter\n}\n\nclass NumberActiveFilter extends ActiveFilterInstance<NumberFilterValue> {\n formatValue() {\n return [this.value]\n .flat()\n .filter((value) => value !== undefined)\n .join(\" and \")\n }\n\n renderEditor(onChange: FilterChange) {\n const { inputType, label, name } = this.operator\n const [start, end] = [this.value].flat()\n const { initialValues, labels, toValue } =\n inputType.type === \"number-range\"\n ? {\n initialValues: [start, end],\n labels: [`${label} from`, `${label} to`],\n toValue: (committed: NumberInputValue[]) => committed,\n }\n : {\n initialValues: [start],\n labels: [label],\n toValue: (committed: NumberInputValue[]) => committed[0],\n }\n return (\n <NumberEditor\n key={name}\n labels={labels}\n values={initialValues}\n onCommit={(committed) =>\n onChange(\n { value: toValue(committed) },\n { complete: committed.every((value) => value !== undefined) }\n )\n }\n />\n )\n }\n}\n\ntype NumberInputValue = number | undefined\n\n// React Aria parses typed text in its own blur and Enter handlers, after ours,\n// so parsed values go in a ref and commit from the wrapper's blur (which\n// bubbles after parsing) or Enter's keyup.\nfunction NumberEditor({\n labels,\n onCommit,\n values: initialValues,\n}: {\n labels: string[]\n onCommit: (values: NumberInputValue[]) => void\n values: NumberInputValue[]\n}) {\n const values = useRef(initialValues)\n const lastCommitted = useRef(initialValues)\n const shouldCommitOnKeyUp = useRef(false)\n const commit = () => {\n lastCommitted.current = values.current\n onCommit(values.current)\n }\n const changed = () =>\n values.current.some(\n (value, index) => value !== lastCommitted.current[index]\n )\n return (\n <Flex\n gap={1}\n onBlur={() => {\n if (changed()) commit()\n }}\n >\n {labels.map((label, index) => (\n <React.Fragment key={label}>\n {index > 0 && <span>and</span>}\n <NumberStepper\n // eslint-disable-next-line jsx-a11y/no-autofocus -- the editor only ever renders inside a chip popover that just opened, per ARIA dialog practices\n autoFocus={index === 0}\n defaultValue={initialValues[index]}\n hideLabel\n label={label}\n onChange={(value) => {\n const next = [...values.current]\n next[index] = Number.isNaN(value) ? undefined : value\n values.current = next\n }}\n onKeyDown={(e) => {\n shouldCommitOnKeyUp.current =\n e.key === \"Enter\" && !e.nativeEvent.isComposing\n }}\n onKeyUp={(e) => {\n if (e.key !== \"Enter\" || !shouldCommitOnKeyUp.current) return\n shouldCommitOnKeyUp.current = false\n commit()\n }}\n />\n </React.Fragment>\n ))}\n </Flex>\n )\n}\n"],"names":["React"],"mappings":"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
|
|
1
|
+
{"version":3,"file":"numberFilter.js","sources":["../../../src/components/DataTable/numberFilter.tsx"],"sourcesContent":["import { Flex } from \"@components/flex\"\nimport { NumberStepper } from \"@components/number-stepper\"\nimport React, { useRef } from \"react\"\n\nimport { ActiveFilterInstance } from \"./ActiveFilterInstance\"\nimport type {\n FilterChange,\n FilterItemConfig,\n FilterNodeItem,\n Operator,\n} from \"./FilterTypes\"\nimport { resolveOperators } from \"./resolveOperators\"\n\nexport type NumberFilterValue = number | [number, number]\n\nexport const GTE = {\n inputType: { type: \"number\" },\n label: \">=\",\n name: \"gte\",\n} as const satisfies Operator\nexport const GT = {\n inputType: { type: \"number\" },\n label: \">\",\n name: \"gt\",\n} as const satisfies Operator\nexport const LT = {\n inputType: { type: \"number\" },\n label: \"<\",\n name: \"lt\",\n} as const satisfies Operator\nexport const EQ = {\n inputType: { type: \"number\" },\n label: \"is\",\n name: \"eq\",\n} as const satisfies Operator\nexport const LTE = {\n inputType: { type: \"number\" },\n label: \"<=\",\n name: \"lte\",\n} as const satisfies Operator\nexport const NUMBER_RANGE = {\n inputType: { type: \"number-range\" },\n label: \"between\",\n name: \"between\",\n} as const satisfies Operator\n\nconst ALL_NUMBER_OPERATORS = [EQ, GT, GTE, LT, LTE, NUMBER_RANGE]\nconst DEFAULT_NUMBER_OPERATORS = [GTE, LTE, NUMBER_RANGE]\n\nexport type NumberOperatorName = (typeof ALL_NUMBER_OPERATORS)[number][\"name\"]\n\nexport function numberFilter({\n excludes,\n includes,\n ...config\n}: FilterItemConfig<NumberOperatorName>): FilterNodeItem {\n const filter: FilterNodeItem = {\n ...config,\n createActiveFilterInstance: (activeFilter, operator) =>\n new NumberActiveFilter(\n filter,\n operator,\n activeFilter.value as NumberFilterValue\n ),\n operators: resolveOperators(ALL_NUMBER_OPERATORS, {\n default: DEFAULT_NUMBER_OPERATORS,\n excludes,\n includes,\n }),\n }\n return filter\n}\n\nclass NumberActiveFilter extends ActiveFilterInstance<NumberFilterValue> {\n formatValue() {\n return [this.value]\n .flat()\n .filter((value) => value !== undefined)\n .join(\" and \")\n }\n\n renderEditor(onChange: FilterChange) {\n const { inputType, label, name } = this.operator\n const [start, end] = [this.value].flat()\n const { initialValues, labels, toValue } =\n inputType.type === \"number-range\"\n ? {\n initialValues: [start, end],\n labels: [`${label} from`, `${label} to`],\n toValue: (committed: NumberInputValue[]) => committed,\n }\n : {\n initialValues: [start],\n labels: [label],\n toValue: (committed: NumberInputValue[]) => committed[0],\n }\n return (\n <NumberEditor\n key={name}\n labels={labels}\n values={initialValues}\n onCommit={(committed) =>\n onChange(\n { value: toValue(committed) },\n { complete: committed.every((value) => value !== undefined) }\n )\n }\n />\n )\n }\n}\n\ntype NumberInputValue = number | undefined\n\n// React Aria parses typed text in its own blur and Enter handlers, after ours,\n// so parsed values go in a ref and commit from the wrapper's blur (which\n// bubbles after parsing) or Enter's keyup.\nfunction NumberEditor({\n labels,\n onCommit,\n values: initialValues,\n}: {\n labels: string[]\n onCommit: (values: NumberInputValue[]) => void\n values: NumberInputValue[]\n}) {\n const values = useRef(initialValues)\n const lastCommitted = useRef(initialValues)\n const shouldCommitOnKeyUp = useRef(false)\n const commit = () => {\n lastCommitted.current = values.current\n onCommit(values.current)\n }\n const changed = () =>\n values.current.some(\n (value, index) => value !== lastCommitted.current[index]\n )\n const isRange = labels.length > 1\n return (\n <Flex\n align=\"center\"\n // Popover content aligns to start; stretch so both inputs fit\n alignSelf={isRange ? \"stretch\" : undefined}\n className={isRange ? \"tds-data-table-filters-number-range\" : undefined}\n gap={1}\n onBlur={() => {\n if (changed()) commit()\n }}\n >\n {labels.map((label, index) => (\n <React.Fragment key={label}>\n {index > 0 && <span>and</span>}\n <NumberStepper\n // eslint-disable-next-line jsx-a11y/no-autofocus -- the editor only ever renders inside a chip popover that just opened, per ARIA dialog practices\n autoFocus={index === 0}\n defaultValue={initialValues[index]}\n hideLabel\n label={label}\n onChange={(value) => {\n const next = [...values.current]\n next[index] = Number.isNaN(value) ? undefined : value\n values.current = next\n }}\n onKeyDown={(e) => {\n shouldCommitOnKeyUp.current =\n e.key === \"Enter\" && !e.nativeEvent.isComposing\n }}\n onKeyUp={(e) => {\n if (e.key !== \"Enter\" || !shouldCommitOnKeyUp.current) return\n shouldCommitOnKeyUp.current = false\n commit()\n }}\n />\n </React.Fragment>\n ))}\n </Flex>\n )\n}\n"],"names":["React"],"mappings":"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"}
|