@j2-health/design-system 0.2.1 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -9
- package/dist/index.cjs +6 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/src/components/barchart/BarChart.cjs +8 -1
- package/dist/src/components/barchart/BarChart.cjs.map +1 -1
- package/dist/src/components/barchart/BarChart.js +8 -1
- package/dist/src/components/barchart/BarChart.js.map +1 -1
- package/dist/src/components/filterform/FilterInput.cjs +66 -3
- package/dist/src/components/filterform/FilterInput.cjs.map +1 -1
- package/dist/src/components/filterform/FilterInput.d.ts +1 -0
- package/dist/src/components/filterform/FilterInput.js +68 -5
- package/dist/src/components/filterform/FilterInput.js.map +1 -1
- package/dist/src/components/filterform/filterHelpers.cjs +24 -1
- package/dist/src/components/filterform/filterHelpers.cjs.map +1 -1
- package/dist/src/components/filterform/filterHelpers.d.ts +18 -4
- package/dist/src/components/filterform/filterHelpers.js +24 -1
- package/dist/src/components/filterform/filterHelpers.js.map +1 -1
- package/dist/src/components/filterform/index.d.ts +3 -1
- package/dist/src/components/filterform/types.d.ts +4 -2
- package/dist/src/components/filterform/useFilterField.cjs +54 -19
- package/dist/src/components/filterform/useFilterField.cjs.map +1 -1
- package/dist/src/components/filterform/useFilterField.d.ts +17 -1
- package/dist/src/components/filterform/useFilterField.js +55 -20
- package/dist/src/components/filterform/useFilterField.js.map +1 -1
- package/dist/src/components/vennDiagram/VennDiagram.cjs +7 -1
- package/dist/src/components/vennDiagram/VennDiagram.cjs.map +1 -1
- package/dist/src/components/vennDiagram/VennDiagram.js +7 -1
- package/dist/src/components/vennDiagram/VennDiagram.js.map +1 -1
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -133,8 +133,7 @@ local checkout has its own `node_modules`.
|
|
|
133
133
|
|
|
134
134
|
### Publishing to npm
|
|
135
135
|
|
|
136
|
-
The package is published publicly (the source is public anyway)
|
|
137
|
-
tag-driven via
|
|
136
|
+
The package is published publicly (the source is public anyway) by
|
|
138
137
|
[.github/workflows/publish-workflow.yml](.github/workflows/publish-workflow.yml).
|
|
139
138
|
|
|
140
139
|
There is **no publish token**. npm authenticates the release through trusted
|
|
@@ -149,14 +148,44 @@ consequences worth knowing before you change anything here:
|
|
|
149
148
|
npm >= 11.5.1, which is exactly what Node 24.5.0 ships. The workflow asserts
|
|
150
149
|
this rather than letting it surface later as an unexplained 401.
|
|
151
150
|
|
|
152
|
-
To release a new version
|
|
151
|
+
To release a new version — maintainers only, since step 2 needs write access to
|
|
152
|
+
this repository:
|
|
153
|
+
|
|
154
|
+
1. Bump `version` in `package.json` in a pull request — on its own, or in the PR
|
|
155
|
+
carrying the change you want to release — and merge it.
|
|
156
|
+
2. Dispatch the publish workflow against `main`:
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
$ gh workflow run publish-workflow.yml --ref main
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Or, in the UI, **Actions → Publish to npm → Run workflow** with `main` selected.
|
|
163
|
+
Pick the ref carefully: the publish job is *skipped* rather than failed on any
|
|
164
|
+
other ref, so a dispatch from a feature branch reports success without
|
|
165
|
+
publishing.
|
|
166
|
+
|
|
167
|
+
That is the whole procedure. The workflow publishes the version `package.json`
|
|
168
|
+
declares and then creates the matching `vX.Y.Z` tag itself, so you do not
|
|
169
|
+
normally tag by hand — the `tag` job's own comments cover the one case that
|
|
170
|
+
needs it, where publishing succeeds and tagging fails.
|
|
171
|
+
|
|
172
|
+
`npm version` is not the tool here, despite being the obvious one: it writes the
|
|
173
|
+
bump as a local commit, and `main` requires a pull request, so that commit has
|
|
174
|
+
nowhere to push to.
|
|
175
|
+
|
|
176
|
+
Pushing a `v*` tag also triggers a release. Tag the remote `main` commit
|
|
177
|
+
explicitly: a bare `git tag` tags your current `HEAD`, and the workflow accepts
|
|
178
|
+
any tag ref, so a tag on an unmerged branch would publish that branch. Push the
|
|
179
|
+
tag explicitly too — `git push --follow-tags` silently skips lightweight tags and
|
|
180
|
+
would report success without publishing:
|
|
153
181
|
|
|
154
182
|
```bash
|
|
155
|
-
$
|
|
156
|
-
$ git
|
|
183
|
+
$ git fetch origin main
|
|
184
|
+
$ git tag -a v0.3.0 -m v0.3.0 origin/main
|
|
185
|
+
$ git push origin v0.3.0
|
|
157
186
|
```
|
|
158
187
|
|
|
159
|
-
The workflow packs the tarball and builds a scratch consumer against it,
|
|
160
|
-
|
|
161
|
-
the
|
|
162
|
-
publishing, so `--provenance` is not passed.
|
|
188
|
+
The workflow packs the tarball and builds a scratch consumer against it, runs
|
|
189
|
+
lint, tests and type-check, builds the library, and publishes; on a tag push it
|
|
190
|
+
also verifies the tag matches `package.json`. Provenance is generated
|
|
191
|
+
automatically by trusted publishing, so `--provenance` is not passed.
|
package/dist/index.cjs
CHANGED
|
@@ -24,6 +24,7 @@ const LegacyNavMenu = require("./src/components/legacyNavMenu/LegacyNavMenu.cjs"
|
|
|
24
24
|
const ListPopover = require("./src/components/listpopover/ListPopover.cjs");
|
|
25
25
|
const Menu = require("./src/components/menu/Menu.cjs");
|
|
26
26
|
const Modal = require("./src/components/modal/Modal.cjs");
|
|
27
|
+
const useFilterField = require("./src/components/filterform/useFilterField.cjs");
|
|
27
28
|
const NavMenu = require("./src/components/navMenu/NavMenu.cjs");
|
|
28
29
|
const NotificationModal = require("./src/components/notificationModal/NotificationModal.cjs");
|
|
29
30
|
const Pagination = require("./src/components/pagination/Pagination.cjs");
|
|
@@ -45,6 +46,7 @@ const UserProfile = require("./src/components/legacyNavMenu/UserProfile.cjs");
|
|
|
45
46
|
const VennDiagram = require("./src/components/vennDiagram/VennDiagram.cjs");
|
|
46
47
|
const appTheme = require("./src/appTheme.cjs");
|
|
47
48
|
const index = require("./src/components/icons/index.cjs");
|
|
49
|
+
const filterHelpers = require("./src/components/filterform/filterHelpers.cjs");
|
|
48
50
|
const tailwind = require("./tailwind.cjs");
|
|
49
51
|
const useMessage = require("./src/components/message/useMessage.cjs");
|
|
50
52
|
const useNotification = require("./src/components/notification/useNotification.cjs");
|
|
@@ -76,6 +78,8 @@ exports.LegacyNavMenu = LegacyNavMenu.LegacyNavMenu;
|
|
|
76
78
|
exports.ListPopover = ListPopover.ListPopover;
|
|
77
79
|
exports.Menu = Menu.Menu;
|
|
78
80
|
exports.Modal = Modal.Modal;
|
|
81
|
+
exports.MultiValueTextOperatorOptions = useFilterField.MultiValueTextOperatorOptions;
|
|
82
|
+
exports.operatorOptionsFor = useFilterField.operatorOptionsFor;
|
|
79
83
|
exports.NavMenu = NavMenu.NavMenu;
|
|
80
84
|
exports.NotificationModal = NotificationModal.NotificationModal;
|
|
81
85
|
exports.Pagination = Pagination.Pagination;
|
|
@@ -99,6 +103,8 @@ exports.UserProfile = UserProfile.UserProfile;
|
|
|
99
103
|
exports.VennDiagram = VennDiagram.VennDiagram;
|
|
100
104
|
exports.appTheme = appTheme.appTheme;
|
|
101
105
|
exports.icons = index;
|
|
106
|
+
exports.isMultiValueTextOperator = filterHelpers.isMultiValueTextOperator;
|
|
107
|
+
exports.normalizeMultiValues = filterHelpers.normalizeMultiValues;
|
|
102
108
|
exports.tailwindPreset = tailwind.tailwindPreset;
|
|
103
109
|
exports.useMessage = useMessage.useMessage;
|
|
104
110
|
exports.useNotification = useNotification.useNotification;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/dist/index.js
CHANGED
|
@@ -22,6 +22,7 @@ import { LegacyNavMenu } from "./src/components/legacyNavMenu/LegacyNavMenu.js";
|
|
|
22
22
|
import { ListPopover } from "./src/components/listpopover/ListPopover.js";
|
|
23
23
|
import { Menu } from "./src/components/menu/Menu.js";
|
|
24
24
|
import { Modal } from "./src/components/modal/Modal.js";
|
|
25
|
+
import { MultiValueTextOperatorOptions, operatorOptionsFor } from "./src/components/filterform/useFilterField.js";
|
|
25
26
|
import { NavMenu } from "./src/components/navMenu/NavMenu.js";
|
|
26
27
|
import { NotificationModal } from "./src/components/notificationModal/NotificationModal.js";
|
|
27
28
|
import { Pagination, withDesignSystemPaginationOverrides } from "./src/components/pagination/Pagination.js";
|
|
@@ -43,6 +44,7 @@ import { UserProfile } from "./src/components/legacyNavMenu/UserProfile.js";
|
|
|
43
44
|
import { VennDiagram } from "./src/components/vennDiagram/VennDiagram.js";
|
|
44
45
|
import { appTheme } from "./src/appTheme.js";
|
|
45
46
|
import * as index from "./src/components/icons/index.js";
|
|
47
|
+
import { isMultiValueTextOperator, normalizeMultiValues } from "./src/components/filterform/filterHelpers.js";
|
|
46
48
|
import { tailwindPreset } from "./tailwind.js";
|
|
47
49
|
import { useMessage } from "./src/components/message/useMessage.js";
|
|
48
50
|
import { useNotification } from "./src/components/notification/useNotification.js";
|
|
@@ -72,6 +74,7 @@ export {
|
|
|
72
74
|
ListPopover,
|
|
73
75
|
Menu,
|
|
74
76
|
Modal,
|
|
77
|
+
MultiValueTextOperatorOptions,
|
|
75
78
|
NavMenu,
|
|
76
79
|
NotificationModal,
|
|
77
80
|
Pagination,
|
|
@@ -94,6 +97,9 @@ export {
|
|
|
94
97
|
VennDiagram,
|
|
95
98
|
appTheme,
|
|
96
99
|
index as icons,
|
|
100
|
+
isMultiValueTextOperator,
|
|
101
|
+
normalizeMultiValues,
|
|
102
|
+
operatorOptionsFor,
|
|
97
103
|
tailwindPreset,
|
|
98
104
|
useMessage,
|
|
99
105
|
useNotification,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -142,7 +142,14 @@ const BarChart = ({
|
|
|
142
142
|
}
|
|
143
143
|
}
|
|
144
144
|
};
|
|
145
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
145
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
146
|
+
react.Chart,
|
|
147
|
+
{
|
|
148
|
+
ref: chartRef,
|
|
149
|
+
options,
|
|
150
|
+
containerProps: { className: "highcharts-light" }
|
|
151
|
+
}
|
|
152
|
+
);
|
|
146
153
|
};
|
|
147
154
|
exports.BarChart = BarChart;
|
|
148
155
|
//# sourceMappingURL=BarChart.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BarChart.cjs","sources":["../../../../src/components/barchart/BarChart.tsx"],"sourcesContent":["import { theme } from 'antd'\nimport type * as Highcharts from 'highcharts'\nimport {\n Chart as HighchartsReact,\n type HighchartsReactRefObject,\n} from '@highcharts/react'\nimport '@highcharts/react/options/Accessibility.js'\nimport '@highcharts/react/options/Exporting.js'\nimport styles from './chart.module.css'\nimport { renderToString } from 'react-dom/server'\n\ntype Series = {\n name?: string\n data: Highcharts.PointOptionsObject[] | number[]\n color?: string\n hoverColor?: string\n}\n\ntype BarChartProps = {\n categories: string[]\n series: Series[]\n min: number\n max: number | undefined | null\n tickInterval: number | undefined\n tickFormat?: (value: number) => string\n xAxisTitle?: string\n yAxisTitle?: string\n gridLineColor?: string\n gridLineDashStyle?: Highcharts.DashStyleValue\n xAxisLabelMaxLines?: number\n tooltip: (\n category: string | number | undefined,\n value: number | null | undefined,\n seriesName: string | undefined\n ) => React.ReactNode\n chartRef?: React.RefObject<HighchartsReactRefObject>\n exporting?: Highcharts.ExportingOptions\n maxBars?: number\n barsWidth?: number\n height?: number\n width?: number\n}\n\nconst BarChart = ({\n categories,\n series,\n min,\n max,\n tickInterval,\n tickFormat,\n xAxisTitle,\n yAxisTitle,\n gridLineColor,\n gridLineDashStyle,\n xAxisLabelMaxLines,\n tooltip,\n exporting,\n chartRef,\n height,\n width,\n maxBars = 50,\n barsWidth = undefined,\n}: BarChartProps) => {\n const limitedCategories = categories.slice(0, maxBars)\n const limitedSeries = series.map((s) => ({\n ...s,\n data: s.data.slice(0, maxBars),\n }))\n\n const { token } = theme.useToken()\n\n const baseFont: Highcharts.CSSObject = {\n fontSize: `${token.fontSize}px`,\n fontFamily: token.fontFamily,\n color: token.colorText,\n }\n\n const options: Highcharts.Options = {\n credits: {\n enabled: false,\n },\n chart: {\n type: 'column',\n ...(height ? { height } : {}),\n ...(width ? { width } : {}),\n },\n plotOptions: {\n column: {\n borderRadius: series[0].data.length > 5 ? '20%' : '15%',\n ...(barsWidth !== undefined ? { pointWidth: barsWidth } : {}),\n },\n },\n exporting: {\n ...exporting,\n enabled: false,\n chartOptions: {\n ...exporting?.chartOptions,\n title: {\n align: 'left',\n text: exporting?.chartOptions?.title?.text,\n style: {\n fontSize: `${token.fontSize}px`,\n fontFamily: 'sans-serif',\n color: token.colorText,\n ...exporting?.chartOptions?.title?.style,\n },\n },\n series: limitedSeries.map((s) => ({\n type: 'column',\n color: s.color ? s.color : '#253761', // --j2-blue-9\n })),\n legend: exporting?.chartOptions?.legend || { enabled: false },\n },\n },\n title: {\n text: '',\n },\n xAxis: {\n categories: limitedCategories,\n title: xAxisTitle\n ? {\n text: xAxisTitle,\n style: { ...baseFont, color: token.colorTextDescription },\n }\n : undefined,\n labels: {\n y: 30,\n style: baseFont,\n ...(xAxisLabelMaxLines\n ? {\n useHTML: true,\n formatter: function (\n this: Highcharts.AxisLabelsFormatterContextObject\n ) {\n return `<div style=\"overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: ${xAxisLabelMaxLines}; -webkit-box-orient: vertical; text-align: center;\">${this.value}</div>`\n },\n }\n : {}),\n },\n },\n yAxis: {\n min: min,\n max: max,\n endOnTick: true,\n gridLineDashStyle: gridLineDashStyle || 'Dot',\n gridLineColor: gridLineColor || 'white',\n title: yAxisTitle\n ? {\n text: yAxisTitle,\n style: { ...baseFont, color: token.colorTextDescription },\n x: -10,\n }\n : undefined,\n tickInterval: tickInterval,\n labels: {\n formatter: tickFormat\n ? ({ value }) => tickFormat(value as number)\n : undefined,\n style: baseFont,\n },\n },\n legend: {\n enabled: false,\n },\n series: limitedSeries.map((s) => ({\n data: s.data,\n name: s.name,\n type: 'column',\n color: s.color || 'var(--j2-blue-11)',\n states: {\n hover: {\n color: s.hoverColor || token.colorPrimaryTextHover,\n },\n },\n })),\n tooltip: {\n useHTML: true,\n outside: true,\n backgroundColor: 'transparent',\n shadow: false,\n padding: 0,\n style: { zIndex: 9999 },\n formatter: function (this) {\n return renderToString(\n <div className={styles.tooltip}>\n {tooltip(this.key, this.y, this.series.name)}\n </div>\n )\n },\n },\n }\n\n return <HighchartsReact
|
|
1
|
+
{"version":3,"file":"BarChart.cjs","sources":["../../../../src/components/barchart/BarChart.tsx"],"sourcesContent":["import { theme } from 'antd'\nimport type * as Highcharts from 'highcharts'\nimport {\n Chart as HighchartsReact,\n type HighchartsReactRefObject,\n} from '@highcharts/react'\nimport '@highcharts/react/options/Accessibility.js'\nimport '@highcharts/react/options/Exporting.js'\nimport styles from './chart.module.css'\nimport { renderToString } from 'react-dom/server'\n\ntype Series = {\n name?: string\n data: Highcharts.PointOptionsObject[] | number[]\n color?: string\n hoverColor?: string\n}\n\ntype BarChartProps = {\n categories: string[]\n series: Series[]\n min: number\n max: number | undefined | null\n tickInterval: number | undefined\n tickFormat?: (value: number) => string\n xAxisTitle?: string\n yAxisTitle?: string\n gridLineColor?: string\n gridLineDashStyle?: Highcharts.DashStyleValue\n xAxisLabelMaxLines?: number\n tooltip: (\n category: string | number | undefined,\n value: number | null | undefined,\n seriesName: string | undefined\n ) => React.ReactNode\n chartRef?: React.RefObject<HighchartsReactRefObject>\n exporting?: Highcharts.ExportingOptions\n maxBars?: number\n barsWidth?: number\n height?: number\n width?: number\n}\n\nconst BarChart = ({\n categories,\n series,\n min,\n max,\n tickInterval,\n tickFormat,\n xAxisTitle,\n yAxisTitle,\n gridLineColor,\n gridLineDashStyle,\n xAxisLabelMaxLines,\n tooltip,\n exporting,\n chartRef,\n height,\n width,\n maxBars = 50,\n barsWidth = undefined,\n}: BarChartProps) => {\n const limitedCategories = categories.slice(0, maxBars)\n const limitedSeries = series.map((s) => ({\n ...s,\n data: s.data.slice(0, maxBars),\n }))\n\n const { token } = theme.useToken()\n\n const baseFont: Highcharts.CSSObject = {\n fontSize: `${token.fontSize}px`,\n fontFamily: token.fontFamily,\n color: token.colorText,\n }\n\n const options: Highcharts.Options = {\n credits: {\n enabled: false,\n },\n chart: {\n type: 'column',\n ...(height ? { height } : {}),\n ...(width ? { width } : {}),\n },\n plotOptions: {\n column: {\n borderRadius: series[0].data.length > 5 ? '20%' : '15%',\n ...(barsWidth !== undefined ? { pointWidth: barsWidth } : {}),\n },\n },\n exporting: {\n ...exporting,\n enabled: false,\n chartOptions: {\n ...exporting?.chartOptions,\n title: {\n align: 'left',\n text: exporting?.chartOptions?.title?.text,\n style: {\n fontSize: `${token.fontSize}px`,\n fontFamily: 'sans-serif',\n color: token.colorText,\n ...exporting?.chartOptions?.title?.style,\n },\n },\n series: limitedSeries.map((s) => ({\n type: 'column',\n color: s.color ? s.color : '#253761', // --j2-blue-9\n })),\n legend: exporting?.chartOptions?.legend || { enabled: false },\n },\n },\n title: {\n text: '',\n },\n xAxis: {\n categories: limitedCategories,\n title: xAxisTitle\n ? {\n text: xAxisTitle,\n style: { ...baseFont, color: token.colorTextDescription },\n }\n : undefined,\n labels: {\n y: 30,\n style: baseFont,\n ...(xAxisLabelMaxLines\n ? {\n useHTML: true,\n formatter: function (\n this: Highcharts.AxisLabelsFormatterContextObject\n ) {\n return `<div style=\"overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: ${xAxisLabelMaxLines}; -webkit-box-orient: vertical; text-align: center;\">${this.value}</div>`\n },\n }\n : {}),\n },\n },\n yAxis: {\n min: min,\n max: max,\n endOnTick: true,\n gridLineDashStyle: gridLineDashStyle || 'Dot',\n gridLineColor: gridLineColor || 'white',\n title: yAxisTitle\n ? {\n text: yAxisTitle,\n style: { ...baseFont, color: token.colorTextDescription },\n x: -10,\n }\n : undefined,\n tickInterval: tickInterval,\n labels: {\n formatter: tickFormat\n ? ({ value }) => tickFormat(value as number)\n : undefined,\n style: baseFont,\n },\n },\n legend: {\n enabled: false,\n },\n series: limitedSeries.map((s) => ({\n data: s.data,\n name: s.name,\n type: 'column',\n color: s.color || 'var(--j2-blue-11)',\n states: {\n hover: {\n color: s.hoverColor || token.colorPrimaryTextHover,\n },\n },\n })),\n tooltip: {\n useHTML: true,\n outside: true,\n backgroundColor: 'transparent',\n shadow: false,\n padding: 0,\n style: { zIndex: 9999 },\n formatter: function (this) {\n return renderToString(\n <div className={styles.tooltip}>\n {tooltip(this.key, this.y, this.series.name)}\n </div>\n )\n },\n },\n }\n\n return (\n <HighchartsReact\n ref={chartRef}\n options={options}\n containerProps={{ className: 'highcharts-light' }}\n />\n )\n}\n\nexport { BarChart }\n"],"names":["theme","renderToString","jsx","styles","HighchartsReact"],"mappings":"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"}
|
|
@@ -140,7 +140,14 @@ const BarChart = ({
|
|
|
140
140
|
}
|
|
141
141
|
}
|
|
142
142
|
};
|
|
143
|
-
return /* @__PURE__ */ jsx(
|
|
143
|
+
return /* @__PURE__ */ jsx(
|
|
144
|
+
Chart,
|
|
145
|
+
{
|
|
146
|
+
ref: chartRef,
|
|
147
|
+
options,
|
|
148
|
+
containerProps: { className: "highcharts-light" }
|
|
149
|
+
}
|
|
150
|
+
);
|
|
144
151
|
};
|
|
145
152
|
export {
|
|
146
153
|
BarChart
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BarChart.js","sources":["../../../../src/components/barchart/BarChart.tsx"],"sourcesContent":["import { theme } from 'antd'\nimport type * as Highcharts from 'highcharts'\nimport {\n Chart as HighchartsReact,\n type HighchartsReactRefObject,\n} from '@highcharts/react'\nimport '@highcharts/react/options/Accessibility.js'\nimport '@highcharts/react/options/Exporting.js'\nimport styles from './chart.module.css'\nimport { renderToString } from 'react-dom/server'\n\ntype Series = {\n name?: string\n data: Highcharts.PointOptionsObject[] | number[]\n color?: string\n hoverColor?: string\n}\n\ntype BarChartProps = {\n categories: string[]\n series: Series[]\n min: number\n max: number | undefined | null\n tickInterval: number | undefined\n tickFormat?: (value: number) => string\n xAxisTitle?: string\n yAxisTitle?: string\n gridLineColor?: string\n gridLineDashStyle?: Highcharts.DashStyleValue\n xAxisLabelMaxLines?: number\n tooltip: (\n category: string | number | undefined,\n value: number | null | undefined,\n seriesName: string | undefined\n ) => React.ReactNode\n chartRef?: React.RefObject<HighchartsReactRefObject>\n exporting?: Highcharts.ExportingOptions\n maxBars?: number\n barsWidth?: number\n height?: number\n width?: number\n}\n\nconst BarChart = ({\n categories,\n series,\n min,\n max,\n tickInterval,\n tickFormat,\n xAxisTitle,\n yAxisTitle,\n gridLineColor,\n gridLineDashStyle,\n xAxisLabelMaxLines,\n tooltip,\n exporting,\n chartRef,\n height,\n width,\n maxBars = 50,\n barsWidth = undefined,\n}: BarChartProps) => {\n const limitedCategories = categories.slice(0, maxBars)\n const limitedSeries = series.map((s) => ({\n ...s,\n data: s.data.slice(0, maxBars),\n }))\n\n const { token } = theme.useToken()\n\n const baseFont: Highcharts.CSSObject = {\n fontSize: `${token.fontSize}px`,\n fontFamily: token.fontFamily,\n color: token.colorText,\n }\n\n const options: Highcharts.Options = {\n credits: {\n enabled: false,\n },\n chart: {\n type: 'column',\n ...(height ? { height } : {}),\n ...(width ? { width } : {}),\n },\n plotOptions: {\n column: {\n borderRadius: series[0].data.length > 5 ? '20%' : '15%',\n ...(barsWidth !== undefined ? { pointWidth: barsWidth } : {}),\n },\n },\n exporting: {\n ...exporting,\n enabled: false,\n chartOptions: {\n ...exporting?.chartOptions,\n title: {\n align: 'left',\n text: exporting?.chartOptions?.title?.text,\n style: {\n fontSize: `${token.fontSize}px`,\n fontFamily: 'sans-serif',\n color: token.colorText,\n ...exporting?.chartOptions?.title?.style,\n },\n },\n series: limitedSeries.map((s) => ({\n type: 'column',\n color: s.color ? s.color : '#253761', // --j2-blue-9\n })),\n legend: exporting?.chartOptions?.legend || { enabled: false },\n },\n },\n title: {\n text: '',\n },\n xAxis: {\n categories: limitedCategories,\n title: xAxisTitle\n ? {\n text: xAxisTitle,\n style: { ...baseFont, color: token.colorTextDescription },\n }\n : undefined,\n labels: {\n y: 30,\n style: baseFont,\n ...(xAxisLabelMaxLines\n ? {\n useHTML: true,\n formatter: function (\n this: Highcharts.AxisLabelsFormatterContextObject\n ) {\n return `<div style=\"overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: ${xAxisLabelMaxLines}; -webkit-box-orient: vertical; text-align: center;\">${this.value}</div>`\n },\n }\n : {}),\n },\n },\n yAxis: {\n min: min,\n max: max,\n endOnTick: true,\n gridLineDashStyle: gridLineDashStyle || 'Dot',\n gridLineColor: gridLineColor || 'white',\n title: yAxisTitle\n ? {\n text: yAxisTitle,\n style: { ...baseFont, color: token.colorTextDescription },\n x: -10,\n }\n : undefined,\n tickInterval: tickInterval,\n labels: {\n formatter: tickFormat\n ? ({ value }) => tickFormat(value as number)\n : undefined,\n style: baseFont,\n },\n },\n legend: {\n enabled: false,\n },\n series: limitedSeries.map((s) => ({\n data: s.data,\n name: s.name,\n type: 'column',\n color: s.color || 'var(--j2-blue-11)',\n states: {\n hover: {\n color: s.hoverColor || token.colorPrimaryTextHover,\n },\n },\n })),\n tooltip: {\n useHTML: true,\n outside: true,\n backgroundColor: 'transparent',\n shadow: false,\n padding: 0,\n style: { zIndex: 9999 },\n formatter: function (this) {\n return renderToString(\n <div className={styles.tooltip}>\n {tooltip(this.key, this.y, this.series.name)}\n </div>\n )\n },\n },\n }\n\n return <HighchartsReact
|
|
1
|
+
{"version":3,"file":"BarChart.js","sources":["../../../../src/components/barchart/BarChart.tsx"],"sourcesContent":["import { theme } from 'antd'\nimport type * as Highcharts from 'highcharts'\nimport {\n Chart as HighchartsReact,\n type HighchartsReactRefObject,\n} from '@highcharts/react'\nimport '@highcharts/react/options/Accessibility.js'\nimport '@highcharts/react/options/Exporting.js'\nimport styles from './chart.module.css'\nimport { renderToString } from 'react-dom/server'\n\ntype Series = {\n name?: string\n data: Highcharts.PointOptionsObject[] | number[]\n color?: string\n hoverColor?: string\n}\n\ntype BarChartProps = {\n categories: string[]\n series: Series[]\n min: number\n max: number | undefined | null\n tickInterval: number | undefined\n tickFormat?: (value: number) => string\n xAxisTitle?: string\n yAxisTitle?: string\n gridLineColor?: string\n gridLineDashStyle?: Highcharts.DashStyleValue\n xAxisLabelMaxLines?: number\n tooltip: (\n category: string | number | undefined,\n value: number | null | undefined,\n seriesName: string | undefined\n ) => React.ReactNode\n chartRef?: React.RefObject<HighchartsReactRefObject>\n exporting?: Highcharts.ExportingOptions\n maxBars?: number\n barsWidth?: number\n height?: number\n width?: number\n}\n\nconst BarChart = ({\n categories,\n series,\n min,\n max,\n tickInterval,\n tickFormat,\n xAxisTitle,\n yAxisTitle,\n gridLineColor,\n gridLineDashStyle,\n xAxisLabelMaxLines,\n tooltip,\n exporting,\n chartRef,\n height,\n width,\n maxBars = 50,\n barsWidth = undefined,\n}: BarChartProps) => {\n const limitedCategories = categories.slice(0, maxBars)\n const limitedSeries = series.map((s) => ({\n ...s,\n data: s.data.slice(0, maxBars),\n }))\n\n const { token } = theme.useToken()\n\n const baseFont: Highcharts.CSSObject = {\n fontSize: `${token.fontSize}px`,\n fontFamily: token.fontFamily,\n color: token.colorText,\n }\n\n const options: Highcharts.Options = {\n credits: {\n enabled: false,\n },\n chart: {\n type: 'column',\n ...(height ? { height } : {}),\n ...(width ? { width } : {}),\n },\n plotOptions: {\n column: {\n borderRadius: series[0].data.length > 5 ? '20%' : '15%',\n ...(barsWidth !== undefined ? { pointWidth: barsWidth } : {}),\n },\n },\n exporting: {\n ...exporting,\n enabled: false,\n chartOptions: {\n ...exporting?.chartOptions,\n title: {\n align: 'left',\n text: exporting?.chartOptions?.title?.text,\n style: {\n fontSize: `${token.fontSize}px`,\n fontFamily: 'sans-serif',\n color: token.colorText,\n ...exporting?.chartOptions?.title?.style,\n },\n },\n series: limitedSeries.map((s) => ({\n type: 'column',\n color: s.color ? s.color : '#253761', // --j2-blue-9\n })),\n legend: exporting?.chartOptions?.legend || { enabled: false },\n },\n },\n title: {\n text: '',\n },\n xAxis: {\n categories: limitedCategories,\n title: xAxisTitle\n ? {\n text: xAxisTitle,\n style: { ...baseFont, color: token.colorTextDescription },\n }\n : undefined,\n labels: {\n y: 30,\n style: baseFont,\n ...(xAxisLabelMaxLines\n ? {\n useHTML: true,\n formatter: function (\n this: Highcharts.AxisLabelsFormatterContextObject\n ) {\n return `<div style=\"overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: ${xAxisLabelMaxLines}; -webkit-box-orient: vertical; text-align: center;\">${this.value}</div>`\n },\n }\n : {}),\n },\n },\n yAxis: {\n min: min,\n max: max,\n endOnTick: true,\n gridLineDashStyle: gridLineDashStyle || 'Dot',\n gridLineColor: gridLineColor || 'white',\n title: yAxisTitle\n ? {\n text: yAxisTitle,\n style: { ...baseFont, color: token.colorTextDescription },\n x: -10,\n }\n : undefined,\n tickInterval: tickInterval,\n labels: {\n formatter: tickFormat\n ? ({ value }) => tickFormat(value as number)\n : undefined,\n style: baseFont,\n },\n },\n legend: {\n enabled: false,\n },\n series: limitedSeries.map((s) => ({\n data: s.data,\n name: s.name,\n type: 'column',\n color: s.color || 'var(--j2-blue-11)',\n states: {\n hover: {\n color: s.hoverColor || token.colorPrimaryTextHover,\n },\n },\n })),\n tooltip: {\n useHTML: true,\n outside: true,\n backgroundColor: 'transparent',\n shadow: false,\n padding: 0,\n style: { zIndex: 9999 },\n formatter: function (this) {\n return renderToString(\n <div className={styles.tooltip}>\n {tooltip(this.key, this.y, this.series.name)}\n </div>\n )\n },\n },\n }\n\n return (\n <HighchartsReact\n ref={chartRef}\n options={options}\n containerProps={{ className: 'highcharts-light' }}\n />\n )\n}\n\nexport { BarChart }\n"],"names":["HighchartsReact"],"mappings":"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"}
|
|
@@ -4,6 +4,7 @@ const jsxRuntime = require("react/jsx-runtime");
|
|
|
4
4
|
const React = require("react");
|
|
5
5
|
const useFilterField = require("./useFilterField.cjs");
|
|
6
6
|
const antd = require("antd");
|
|
7
|
+
const filterHelpers = require("./filterHelpers.cjs");
|
|
7
8
|
const cx = require("classnames");
|
|
8
9
|
function _interopNamespaceDefault(e) {
|
|
9
10
|
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
|
|
@@ -46,10 +47,13 @@ const FilterInput = ({
|
|
|
46
47
|
value: config.field,
|
|
47
48
|
disabled: config.disabled
|
|
48
49
|
}));
|
|
50
|
+
const latestFilterRef = React__namespace.useRef(filter);
|
|
51
|
+
latestFilterRef.current = filter;
|
|
49
52
|
const handleBlur = () => {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
+
setTimeout(() => {
|
|
54
|
+
const latest = latestFilterRef.current;
|
|
55
|
+
if (latest) onBlur == null ? void 0 : onBlur(latest);
|
|
56
|
+
}, 0);
|
|
53
57
|
};
|
|
54
58
|
const isInitialMount = React__namespace.useRef(true);
|
|
55
59
|
const prevFilterRef = React__namespace.useRef(filter);
|
|
@@ -57,6 +61,9 @@ const FilterInput = ({
|
|
|
57
61
|
if (isInitialMount.current) {
|
|
58
62
|
isInitialMount.current = false;
|
|
59
63
|
prevFilterRef.current = filter;
|
|
64
|
+
if (value && filter && wasCorrectedOnInit(value, filter)) {
|
|
65
|
+
onChange == null ? void 0 : onChange(filter);
|
|
66
|
+
}
|
|
60
67
|
return;
|
|
61
68
|
}
|
|
62
69
|
if (filter && filter !== prevFilterRef.current) {
|
|
@@ -101,6 +108,8 @@ const FilterInput = ({
|
|
|
101
108
|
] })
|
|
102
109
|
] });
|
|
103
110
|
};
|
|
111
|
+
const sameList = (a, b) => a.length === b.length && a.every((item, i) => item === b[i]);
|
|
112
|
+
const wasCorrectedOnInit = (persisted, current) => persisted.operator !== current.operator || !sameList(persisted.values, current.values) || persisted.errors === void 0 || !sameList(persisted.errors, current.errors ?? []);
|
|
104
113
|
const ValueInput = ({
|
|
105
114
|
valueInputConfig,
|
|
106
115
|
values,
|
|
@@ -157,6 +166,18 @@ const ValueInput = ({
|
|
|
157
166
|
] }, `number-input-${index}`)
|
|
158
167
|
) });
|
|
159
168
|
case "text":
|
|
169
|
+
if (valueInputConfig.multiValue) {
|
|
170
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
171
|
+
MultiValueTextInput,
|
|
172
|
+
{
|
|
173
|
+
values,
|
|
174
|
+
maxValues: valueInputConfig.maxValues,
|
|
175
|
+
onChange,
|
|
176
|
+
onBlur,
|
|
177
|
+
size
|
|
178
|
+
}
|
|
179
|
+
);
|
|
180
|
+
}
|
|
160
181
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
161
182
|
antd.Input,
|
|
162
183
|
{
|
|
@@ -171,5 +192,47 @@ const ValueInput = ({
|
|
|
171
192
|
return null;
|
|
172
193
|
}
|
|
173
194
|
};
|
|
195
|
+
const MULTI_VALUE_TOKEN_SEPARATORS = [",", "\n", "\r", " "];
|
|
196
|
+
const MultiValueTextInput = ({
|
|
197
|
+
values,
|
|
198
|
+
maxValues,
|
|
199
|
+
onChange,
|
|
200
|
+
onBlur,
|
|
201
|
+
size
|
|
202
|
+
}) => {
|
|
203
|
+
const chips = (values ?? []).flatMap(
|
|
204
|
+
(value) => value === null || value === void 0 ? [] : [String(value)]
|
|
205
|
+
);
|
|
206
|
+
const over = maxValues !== void 0 && chips.length > maxValues;
|
|
207
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1 w-full min-w-0", children: [
|
|
208
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
209
|
+
antd.Select,
|
|
210
|
+
{
|
|
211
|
+
mode: "tags",
|
|
212
|
+
tokenSeparators: MULTI_VALUE_TOKEN_SEPARATORS,
|
|
213
|
+
value: chips,
|
|
214
|
+
onChange: (next) => onChange(filterHelpers.normalizeMultiValues(next)),
|
|
215
|
+
onBlur,
|
|
216
|
+
open: false,
|
|
217
|
+
suffixIcon: null,
|
|
218
|
+
placeholder: "Type or paste values, separated by commas",
|
|
219
|
+
className: "w-full",
|
|
220
|
+
size,
|
|
221
|
+
status: over ? "error" : void 0,
|
|
222
|
+
"aria-label": "Values"
|
|
223
|
+
}
|
|
224
|
+
),
|
|
225
|
+
maxValues !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
226
|
+
antd.Typography.Text,
|
|
227
|
+
{
|
|
228
|
+
type: over ? "danger" : "secondary",
|
|
229
|
+
className: "text-xs",
|
|
230
|
+
role: over ? "alert" : void 0,
|
|
231
|
+
children: over ? `${filterHelpers.tooManyValuesMessage(maxValues)} — remove ${chips.length - maxValues} to apply` : `${chips.length} / ${maxValues} values`
|
|
232
|
+
}
|
|
233
|
+
)
|
|
234
|
+
] });
|
|
235
|
+
};
|
|
174
236
|
exports.FilterInput = FilterInput;
|
|
237
|
+
exports.MULTI_VALUE_TOKEN_SEPARATORS = MULTI_VALUE_TOKEN_SEPARATORS;
|
|
175
238
|
//# sourceMappingURL=FilterInput.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FilterInput.cjs","sources":["../../../../src/components/filterform/FilterInput.tsx"],"sourcesContent":["import * as React from 'react'\nimport { FilterConfig } from '.'\nimport { useFilterField } from './useFilterField'\nimport { Input, InputNumber, Select } from 'antd'\nimport { FormFilter } from './types'\nimport cx from 'classnames'\nimport { SizeType } from 'antd/es/config-provider/SizeContext'\n\ntype SelectValueInputConfig = {\n type: 'select'\n valueOptions: { label: string; value: string }[]\n}\n\ntype NumberValueInputConfig = {\n type: 'number'\n inputCount: number\n numberOptions?: { max: number; min: number; step: number }\n}\n\ntype TextValueInputConfig = {\n type: 'text'\n}\n\ntype ValueInputConfig =\n SelectValueInputConfig | NumberValueInputConfig | TextValueInputConfig\n\ntype FilterInputProps = {\n value?: FormFilter\n filterConfigs: FilterConfig[]\n onChange?: (value: FormFilter) => void\n onBlur?: (value: FormFilter) => void\n className?: string\n size?: SizeType\n}\n\nexport const FilterInput = ({\n value,\n filterConfigs,\n onChange,\n onBlur,\n className,\n size = 'middle',\n}: FilterInputProps) => {\n const {\n filter,\n operatorOptions,\n valueInputConfig,\n handleFieldChange,\n handleOperatorChange,\n handleValuesChange,\n } = useFilterField({\n filterConfigs,\n filter: value,\n })\n\n const fieldOptions = filterConfigs.map((config) => ({\n label: config.label,\n value: config.field,\n disabled: config.disabled,\n }))\n\n const handleBlur = () => {\n if (filter) {\n onBlur?.(filter)\n }\n }\n\n // Track if this is a user-driven change (not initialization)\n const isInitialMount = React.useRef(true)\n const prevFilterRef = React.useRef(filter)\n\n React.useEffect(() => {\n // Skip the initial mount to avoid calling onChange during initialization\n if (isInitialMount.current) {\n isInitialMount.current = false\n prevFilterRef.current = filter\n return\n }\n\n // Only call onChange if the filter actually changed\n if (filter && filter !== prevFilterRef.current) {\n prevFilterRef.current = filter\n onChange?.(filter)\n }\n }, [filter, onChange])\n\n return (\n <div className={cx('grid grid-cols-3 gap-2', className)}>\n <Select\n options={fieldOptions}\n value={filter?.field}\n showSearch\n optionFilterProp=\"label\"\n onChange={handleFieldChange}\n size={size}\n />\n <div className=\"col-span-2 flex gap-2\">\n <Select\n options={operatorOptions}\n value={filter?.operator}\n onChange={handleOperatorChange}\n onBlur={handleBlur}\n size={size}\n popupMatchSelectWidth={false}\n />\n {valueInputConfig && (\n <ValueInput\n valueInputConfig={valueInputConfig}\n values={filter?.values}\n onChange={handleValuesChange}\n onBlur={handleBlur}\n size={size}\n />\n )}\n </div>\n </div>\n )\n}\n\ntype ValueInputProps = {\n valueInputConfig: ValueInputConfig\n values?: (string | number | undefined | null)[]\n onChange: (value: (string | number | undefined | null)[]) => void\n onBlur: () => void\n size: SizeType\n}\n\nconst ValueInput = ({\n valueInputConfig,\n values,\n onChange,\n onBlur,\n size,\n}: ValueInputProps) => {\n const handleChange = (value: string | number | null) => {\n if (value === null) {\n onChange([])\n } else {\n onChange([value])\n }\n }\n\n const handleNumberChange = (value: string | number | null, index: number) => {\n const newValues: (string | number | undefined | null)[] = [\n ...(values ?? []),\n ]\n newValues[index] = value\n onChange(newValues)\n }\n\n switch (valueInputConfig.type) {\n case 'select':\n return (\n <Select\n options={valueInputConfig.valueOptions}\n onChange={onChange}\n showSearch\n allowClear\n mode=\"multiple\"\n onBlur={onBlur}\n value={values}\n className=\"w-full truncate\"\n size={size}\n optionFilterProp=\"label\"\n />\n )\n case 'number':\n return (\n <div className=\"flex gap-2 grow\">\n {Array.from({ length: valueInputConfig.inputCount }).map(\n (_, index) => (\n <React.Fragment key={`number-input-${index}`}>\n {index !== 0 && <span className=\"my-auto\">and</span>}\n <InputNumber\n onChange={(value) => handleNumberChange(value, index)}\n onBlur={onBlur}\n value={values?.[index]}\n className=\"basis-1/2\"\n size={size}\n {...(valueInputConfig.numberOptions ?? {})}\n />\n </React.Fragment>\n )\n )}\n </div>\n )\n case 'text':\n return (\n <Input\n onBlur={onBlur}\n value={values?.[0] ?? undefined}\n onChange={(e) => handleChange(e.target.value)}\n className=\"w-full\"\n size={size}\n />\n )\n default:\n return null\n }\n}\n"],"names":["useFilterField","React","jsx","Select","jsxs","InputNumber","Input"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"FilterInput.cjs","sources":["../../../../src/components/filterform/FilterInput.tsx"],"sourcesContent":["import * as React from 'react'\nimport { FilterConfig } from '.'\nimport { useFilterField, ValueInputConfig } from './useFilterField'\nimport { Input, InputNumber, Select, Typography } from 'antd'\nimport { FormFilter } from './types'\nimport { normalizeMultiValues, tooManyValuesMessage } from './filterHelpers'\nimport cx from 'classnames'\nimport { SizeType } from 'antd/es/config-provider/SizeContext'\n\ntype FilterInputProps = {\n value?: FormFilter\n filterConfigs: FilterConfig[]\n onChange?: (value: FormFilter) => void\n onBlur?: (value: FormFilter) => void\n className?: string\n size?: SizeType\n}\n\nexport const FilterInput = ({\n value,\n filterConfigs,\n onChange,\n onBlur,\n className,\n size = 'middle',\n}: FilterInputProps) => {\n const {\n filter,\n operatorOptions,\n valueInputConfig,\n handleFieldChange,\n handleOperatorChange,\n handleValuesChange,\n } = useFilterField({\n filterConfigs,\n filter: value,\n })\n\n const fieldOptions = filterConfigs.map((config) => ({\n label: config.label,\n value: config.field,\n disabled: config.disabled,\n }))\n\n // Mirror of the reducer's filter for callbacks that run after a render.\n const latestFilterRef = React.useRef(filter)\n latestFilterRef.current = filter\n\n // Deferred a tick, and read from the ref rather than the closure. A tags\n // `Select` commits unseparated search text as a chip inside its own blur\n // handling, so `onChange` and `onBlur` arrive in the same browser event and\n // React flushes the resulting reducer update only after both return. A\n // synchronous `onBlur` would hand the parent the filter from *before* that\n // last chip — and for a new rule the parent finalizes and unmounts this\n // input, silently dropping the value. After the flush the ref holds it.\n const handleBlur = () => {\n setTimeout(() => {\n const latest = latestFilterRef.current\n if (latest) onBlur?.(latest)\n }, 0)\n }\n\n // Track if this is a user-driven change (not initialization)\n const isInitialMount = React.useRef(true)\n const prevFilterRef = React.useRef(filter)\n\n React.useEffect(() => {\n // Skip the initial mount to avoid calling onChange during initialization\n // — unless initialization itself corrected the persisted filter (an\n // operator the field no longer offers, or a chip list that needed\n // normalizing). Then the parent must learn about it, or it would keep\n // and submit the stale filter until the user happened to touch the rule.\n if (isInitialMount.current) {\n isInitialMount.current = false\n prevFilterRef.current = filter\n if (value && filter && wasCorrectedOnInit(value, filter)) {\n onChange?.(filter)\n }\n return\n }\n\n // Only call onChange if the filter actually changed\n if (filter && filter !== prevFilterRef.current) {\n prevFilterRef.current = filter\n onChange?.(filter)\n }\n }, [filter, onChange])\n\n return (\n <div className={cx('grid grid-cols-3 gap-2', className)}>\n <Select\n options={fieldOptions}\n value={filter?.field}\n showSearch\n optionFilterProp=\"label\"\n onChange={handleFieldChange}\n size={size}\n />\n <div className=\"col-span-2 flex gap-2\">\n <Select\n options={operatorOptions}\n value={filter?.operator}\n onChange={handleOperatorChange}\n onBlur={handleBlur}\n size={size}\n popupMatchSelectWidth={false}\n />\n {valueInputConfig && (\n <ValueInput\n valueInputConfig={valueInputConfig}\n values={filter?.values}\n onChange={handleValuesChange}\n onBlur={handleBlur}\n size={size}\n />\n )}\n </div>\n </div>\n )\n}\n\nconst sameList = (a: readonly unknown[], b: readonly unknown[]) =>\n a.length === b.length && a.every((item, i) => item === b[i])\n\n// Whether initializing from a persisted filter changed anything the parent\n// holds — operator, values, or the validation result. `errors` counts: a\n// persisted rule can be unchanged yet newly invalid (a lowered `maxValues`),\n// or arrive with no `errors` at all (a plain `FilterForm` from storage), and\n// the form's validity is computed from the errors it holds, not from ours.\nconst wasCorrectedOnInit = (persisted: FormFilter, current: FormFilter) =>\n persisted.operator !== current.operator ||\n !sameList(persisted.values, current.values) ||\n persisted.errors === undefined ||\n !sameList(persisted.errors, current.errors ?? [])\n\ntype ValueInputProps = {\n valueInputConfig: ValueInputConfig\n values?: (string | number | undefined | null)[]\n onChange: (value: (string | number | undefined | null)[]) => void\n onBlur: () => void\n size: SizeType\n}\n\nconst ValueInput = ({\n valueInputConfig,\n values,\n onChange,\n onBlur,\n size,\n}: ValueInputProps) => {\n const handleChange = (value: string | number | null) => {\n if (value === null) {\n onChange([])\n } else {\n onChange([value])\n }\n }\n\n const handleNumberChange = (value: string | number | null, index: number) => {\n const newValues: (string | number | undefined | null)[] = [\n ...(values ?? []),\n ]\n newValues[index] = value\n onChange(newValues)\n }\n\n switch (valueInputConfig.type) {\n case 'select':\n return (\n <Select\n options={valueInputConfig.valueOptions}\n onChange={onChange}\n showSearch\n allowClear\n mode=\"multiple\"\n onBlur={onBlur}\n value={values}\n className=\"w-full truncate\"\n size={size}\n optionFilterProp=\"label\"\n />\n )\n case 'number':\n return (\n <div className=\"flex gap-2 grow\">\n {Array.from({ length: valueInputConfig.inputCount }).map(\n (_, index) => (\n <React.Fragment key={`number-input-${index}`}>\n {index !== 0 && <span className=\"my-auto\">and</span>}\n <InputNumber\n onChange={(value) => handleNumberChange(value, index)}\n onBlur={onBlur}\n value={values?.[index]}\n className=\"basis-1/2\"\n size={size}\n {...(valueInputConfig.numberOptions ?? {})}\n />\n </React.Fragment>\n )\n )}\n </div>\n )\n case 'text':\n if (valueInputConfig.multiValue) {\n return (\n <MultiValueTextInput\n values={values}\n maxValues={valueInputConfig.maxValues}\n onChange={onChange}\n onBlur={onBlur}\n size={size}\n />\n )\n }\n return (\n <Input\n onBlur={onBlur}\n value={values?.[0] ?? undefined}\n onChange={(e) => handleChange(e.target.value)}\n className=\"w-full\"\n size={size}\n />\n )\n default:\n return null\n }\n}\n\n// Commas, newlines (both flavours) and tabs: a column pasted from Excel or a\n// comma-separated list from an email both land as one chip per value.\nexport const MULTI_VALUE_TOKEN_SEPARATORS = [',', '\\n', '\\r', '\\t']\n\n/**\n * Chips input for the multi-value text operators (`isAnyOf` / `isNoneOf`).\n *\n * A tags Select rather than a text box split at query time: every token is\n * visible and individually removable before Apply, so a pasted value that\n * itself contains a comma (street addresses do) can be seen and fixed\n * instead of being silently shredded. The dropdown is kept closed — there\n * are no options to pick from; Enter or a separator commits a chip.\n */\nconst MultiValueTextInput = ({\n values,\n maxValues,\n onChange,\n onBlur,\n size,\n}: {\n values?: (string | number | undefined | null)[]\n maxValues?: number\n onChange: (value: (string | number | undefined | null)[]) => void\n onBlur: () => void\n size: SizeType\n}) => {\n // Rendered as stored: `useFilterField` normalizes values on the way into\n // state, so what the chips show is exactly what validation counts and\n // what `onSubmit` emits. (Numbers/nulls can't occur for a text filter;\n // the map is only for the shared `values` type.)\n const chips = (values ?? []).flatMap((value) =>\n value === null || value === undefined ? [] : [String(value)]\n )\n const over = maxValues !== undefined && chips.length > maxValues\n\n return (\n <div className=\"flex flex-col gap-1 w-full min-w-0\">\n <Select\n mode=\"tags\"\n tokenSeparators={MULTI_VALUE_TOKEN_SEPARATORS}\n value={chips}\n onChange={(next: string[]) => onChange(normalizeMultiValues(next))}\n onBlur={onBlur}\n open={false}\n suffixIcon={null}\n placeholder=\"Type or paste values, separated by commas\"\n className=\"w-full\"\n size={size}\n status={over ? 'error' : undefined}\n aria-label=\"Values\"\n />\n {maxValues !== undefined && (\n <Typography.Text\n type={over ? 'danger' : 'secondary'}\n className=\"text-xs\"\n role={over ? 'alert' : undefined}\n >\n {over\n ? `${tooManyValuesMessage(maxValues)} — remove ${\n chips.length - maxValues\n } to apply`\n : `${chips.length} / ${maxValues} values`}\n </Typography.Text>\n )}\n </div>\n )\n}\n"],"names":["useFilterField","React","jsx","Select","jsxs","InputNumber","Input","normalizeMultiValues","Typography","tooManyValuesMessage"],"mappings":"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"}
|
|
@@ -11,4 +11,5 @@ type FilterInputProps = {
|
|
|
11
11
|
size?: SizeType;
|
|
12
12
|
};
|
|
13
13
|
export declare const FilterInput: ({ value, filterConfigs, onChange, onBlur, className, size, }: FilterInputProps) => React.JSX.Element;
|
|
14
|
+
export declare const MULTI_VALUE_TOKEN_SEPARATORS: string[];
|
|
14
15
|
export {};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import { useFilterField } from "./useFilterField.js";
|
|
4
|
-
import { Select, Input, InputNumber } from "antd";
|
|
4
|
+
import { Select, Input, InputNumber, Typography } from "antd";
|
|
5
|
+
import { normalizeMultiValues, tooManyValuesMessage } from "./filterHelpers.js";
|
|
5
6
|
import cx from "classnames";
|
|
6
7
|
const FilterInput = ({
|
|
7
8
|
value,
|
|
@@ -27,10 +28,13 @@ const FilterInput = ({
|
|
|
27
28
|
value: config.field,
|
|
28
29
|
disabled: config.disabled
|
|
29
30
|
}));
|
|
31
|
+
const latestFilterRef = React.useRef(filter);
|
|
32
|
+
latestFilterRef.current = filter;
|
|
30
33
|
const handleBlur = () => {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
setTimeout(() => {
|
|
35
|
+
const latest = latestFilterRef.current;
|
|
36
|
+
if (latest) onBlur == null ? void 0 : onBlur(latest);
|
|
37
|
+
}, 0);
|
|
34
38
|
};
|
|
35
39
|
const isInitialMount = React.useRef(true);
|
|
36
40
|
const prevFilterRef = React.useRef(filter);
|
|
@@ -38,6 +42,9 @@ const FilterInput = ({
|
|
|
38
42
|
if (isInitialMount.current) {
|
|
39
43
|
isInitialMount.current = false;
|
|
40
44
|
prevFilterRef.current = filter;
|
|
45
|
+
if (value && filter && wasCorrectedOnInit(value, filter)) {
|
|
46
|
+
onChange == null ? void 0 : onChange(filter);
|
|
47
|
+
}
|
|
41
48
|
return;
|
|
42
49
|
}
|
|
43
50
|
if (filter && filter !== prevFilterRef.current) {
|
|
@@ -82,6 +89,8 @@ const FilterInput = ({
|
|
|
82
89
|
] })
|
|
83
90
|
] });
|
|
84
91
|
};
|
|
92
|
+
const sameList = (a, b) => a.length === b.length && a.every((item, i) => item === b[i]);
|
|
93
|
+
const wasCorrectedOnInit = (persisted, current) => persisted.operator !== current.operator || !sameList(persisted.values, current.values) || persisted.errors === void 0 || !sameList(persisted.errors, current.errors ?? []);
|
|
85
94
|
const ValueInput = ({
|
|
86
95
|
valueInputConfig,
|
|
87
96
|
values,
|
|
@@ -138,6 +147,18 @@ const ValueInput = ({
|
|
|
138
147
|
] }, `number-input-${index}`)
|
|
139
148
|
) });
|
|
140
149
|
case "text":
|
|
150
|
+
if (valueInputConfig.multiValue) {
|
|
151
|
+
return /* @__PURE__ */ jsx(
|
|
152
|
+
MultiValueTextInput,
|
|
153
|
+
{
|
|
154
|
+
values,
|
|
155
|
+
maxValues: valueInputConfig.maxValues,
|
|
156
|
+
onChange,
|
|
157
|
+
onBlur,
|
|
158
|
+
size
|
|
159
|
+
}
|
|
160
|
+
);
|
|
161
|
+
}
|
|
141
162
|
return /* @__PURE__ */ jsx(
|
|
142
163
|
Input,
|
|
143
164
|
{
|
|
@@ -152,7 +173,49 @@ const ValueInput = ({
|
|
|
152
173
|
return null;
|
|
153
174
|
}
|
|
154
175
|
};
|
|
176
|
+
const MULTI_VALUE_TOKEN_SEPARATORS = [",", "\n", "\r", " "];
|
|
177
|
+
const MultiValueTextInput = ({
|
|
178
|
+
values,
|
|
179
|
+
maxValues,
|
|
180
|
+
onChange,
|
|
181
|
+
onBlur,
|
|
182
|
+
size
|
|
183
|
+
}) => {
|
|
184
|
+
const chips = (values ?? []).flatMap(
|
|
185
|
+
(value) => value === null || value === void 0 ? [] : [String(value)]
|
|
186
|
+
);
|
|
187
|
+
const over = maxValues !== void 0 && chips.length > maxValues;
|
|
188
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1 w-full min-w-0", children: [
|
|
189
|
+
/* @__PURE__ */ jsx(
|
|
190
|
+
Select,
|
|
191
|
+
{
|
|
192
|
+
mode: "tags",
|
|
193
|
+
tokenSeparators: MULTI_VALUE_TOKEN_SEPARATORS,
|
|
194
|
+
value: chips,
|
|
195
|
+
onChange: (next) => onChange(normalizeMultiValues(next)),
|
|
196
|
+
onBlur,
|
|
197
|
+
open: false,
|
|
198
|
+
suffixIcon: null,
|
|
199
|
+
placeholder: "Type or paste values, separated by commas",
|
|
200
|
+
className: "w-full",
|
|
201
|
+
size,
|
|
202
|
+
status: over ? "error" : void 0,
|
|
203
|
+
"aria-label": "Values"
|
|
204
|
+
}
|
|
205
|
+
),
|
|
206
|
+
maxValues !== void 0 && /* @__PURE__ */ jsx(
|
|
207
|
+
Typography.Text,
|
|
208
|
+
{
|
|
209
|
+
type: over ? "danger" : "secondary",
|
|
210
|
+
className: "text-xs",
|
|
211
|
+
role: over ? "alert" : void 0,
|
|
212
|
+
children: over ? `${tooManyValuesMessage(maxValues)} — remove ${chips.length - maxValues} to apply` : `${chips.length} / ${maxValues} values`
|
|
213
|
+
}
|
|
214
|
+
)
|
|
215
|
+
] });
|
|
216
|
+
};
|
|
155
217
|
export {
|
|
156
|
-
FilterInput
|
|
218
|
+
FilterInput,
|
|
219
|
+
MULTI_VALUE_TOKEN_SEPARATORS
|
|
157
220
|
};
|
|
158
221
|
//# sourceMappingURL=FilterInput.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FilterInput.js","sources":["../../../../src/components/filterform/FilterInput.tsx"],"sourcesContent":["import * as React from 'react'\nimport { FilterConfig } from '.'\nimport { useFilterField } from './useFilterField'\nimport { Input, InputNumber, Select } from 'antd'\nimport { FormFilter } from './types'\nimport cx from 'classnames'\nimport { SizeType } from 'antd/es/config-provider/SizeContext'\n\ntype SelectValueInputConfig = {\n type: 'select'\n valueOptions: { label: string; value: string }[]\n}\n\ntype NumberValueInputConfig = {\n type: 'number'\n inputCount: number\n numberOptions?: { max: number; min: number; step: number }\n}\n\ntype TextValueInputConfig = {\n type: 'text'\n}\n\ntype ValueInputConfig =\n SelectValueInputConfig | NumberValueInputConfig | TextValueInputConfig\n\ntype FilterInputProps = {\n value?: FormFilter\n filterConfigs: FilterConfig[]\n onChange?: (value: FormFilter) => void\n onBlur?: (value: FormFilter) => void\n className?: string\n size?: SizeType\n}\n\nexport const FilterInput = ({\n value,\n filterConfigs,\n onChange,\n onBlur,\n className,\n size = 'middle',\n}: FilterInputProps) => {\n const {\n filter,\n operatorOptions,\n valueInputConfig,\n handleFieldChange,\n handleOperatorChange,\n handleValuesChange,\n } = useFilterField({\n filterConfigs,\n filter: value,\n })\n\n const fieldOptions = filterConfigs.map((config) => ({\n label: config.label,\n value: config.field,\n disabled: config.disabled,\n }))\n\n const handleBlur = () => {\n if (filter) {\n onBlur?.(filter)\n }\n }\n\n // Track if this is a user-driven change (not initialization)\n const isInitialMount = React.useRef(true)\n const prevFilterRef = React.useRef(filter)\n\n React.useEffect(() => {\n // Skip the initial mount to avoid calling onChange during initialization\n if (isInitialMount.current) {\n isInitialMount.current = false\n prevFilterRef.current = filter\n return\n }\n\n // Only call onChange if the filter actually changed\n if (filter && filter !== prevFilterRef.current) {\n prevFilterRef.current = filter\n onChange?.(filter)\n }\n }, [filter, onChange])\n\n return (\n <div className={cx('grid grid-cols-3 gap-2', className)}>\n <Select\n options={fieldOptions}\n value={filter?.field}\n showSearch\n optionFilterProp=\"label\"\n onChange={handleFieldChange}\n size={size}\n />\n <div className=\"col-span-2 flex gap-2\">\n <Select\n options={operatorOptions}\n value={filter?.operator}\n onChange={handleOperatorChange}\n onBlur={handleBlur}\n size={size}\n popupMatchSelectWidth={false}\n />\n {valueInputConfig && (\n <ValueInput\n valueInputConfig={valueInputConfig}\n values={filter?.values}\n onChange={handleValuesChange}\n onBlur={handleBlur}\n size={size}\n />\n )}\n </div>\n </div>\n )\n}\n\ntype ValueInputProps = {\n valueInputConfig: ValueInputConfig\n values?: (string | number | undefined | null)[]\n onChange: (value: (string | number | undefined | null)[]) => void\n onBlur: () => void\n size: SizeType\n}\n\nconst ValueInput = ({\n valueInputConfig,\n values,\n onChange,\n onBlur,\n size,\n}: ValueInputProps) => {\n const handleChange = (value: string | number | null) => {\n if (value === null) {\n onChange([])\n } else {\n onChange([value])\n }\n }\n\n const handleNumberChange = (value: string | number | null, index: number) => {\n const newValues: (string | number | undefined | null)[] = [\n ...(values ?? []),\n ]\n newValues[index] = value\n onChange(newValues)\n }\n\n switch (valueInputConfig.type) {\n case 'select':\n return (\n <Select\n options={valueInputConfig.valueOptions}\n onChange={onChange}\n showSearch\n allowClear\n mode=\"multiple\"\n onBlur={onBlur}\n value={values}\n className=\"w-full truncate\"\n size={size}\n optionFilterProp=\"label\"\n />\n )\n case 'number':\n return (\n <div className=\"flex gap-2 grow\">\n {Array.from({ length: valueInputConfig.inputCount }).map(\n (_, index) => (\n <React.Fragment key={`number-input-${index}`}>\n {index !== 0 && <span className=\"my-auto\">and</span>}\n <InputNumber\n onChange={(value) => handleNumberChange(value, index)}\n onBlur={onBlur}\n value={values?.[index]}\n className=\"basis-1/2\"\n size={size}\n {...(valueInputConfig.numberOptions ?? {})}\n />\n </React.Fragment>\n )\n )}\n </div>\n )\n case 'text':\n return (\n <Input\n onBlur={onBlur}\n value={values?.[0] ?? undefined}\n onChange={(e) => handleChange(e.target.value)}\n className=\"w-full\"\n size={size}\n />\n )\n default:\n return null\n }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"FilterInput.js","sources":["../../../../src/components/filterform/FilterInput.tsx"],"sourcesContent":["import * as React from 'react'\nimport { FilterConfig } from '.'\nimport { useFilterField, ValueInputConfig } from './useFilterField'\nimport { Input, InputNumber, Select, Typography } from 'antd'\nimport { FormFilter } from './types'\nimport { normalizeMultiValues, tooManyValuesMessage } from './filterHelpers'\nimport cx from 'classnames'\nimport { SizeType } from 'antd/es/config-provider/SizeContext'\n\ntype FilterInputProps = {\n value?: FormFilter\n filterConfigs: FilterConfig[]\n onChange?: (value: FormFilter) => void\n onBlur?: (value: FormFilter) => void\n className?: string\n size?: SizeType\n}\n\nexport const FilterInput = ({\n value,\n filterConfigs,\n onChange,\n onBlur,\n className,\n size = 'middle',\n}: FilterInputProps) => {\n const {\n filter,\n operatorOptions,\n valueInputConfig,\n handleFieldChange,\n handleOperatorChange,\n handleValuesChange,\n } = useFilterField({\n filterConfigs,\n filter: value,\n })\n\n const fieldOptions = filterConfigs.map((config) => ({\n label: config.label,\n value: config.field,\n disabled: config.disabled,\n }))\n\n // Mirror of the reducer's filter for callbacks that run after a render.\n const latestFilterRef = React.useRef(filter)\n latestFilterRef.current = filter\n\n // Deferred a tick, and read from the ref rather than the closure. A tags\n // `Select` commits unseparated search text as a chip inside its own blur\n // handling, so `onChange` and `onBlur` arrive in the same browser event and\n // React flushes the resulting reducer update only after both return. A\n // synchronous `onBlur` would hand the parent the filter from *before* that\n // last chip — and for a new rule the parent finalizes and unmounts this\n // input, silently dropping the value. After the flush the ref holds it.\n const handleBlur = () => {\n setTimeout(() => {\n const latest = latestFilterRef.current\n if (latest) onBlur?.(latest)\n }, 0)\n }\n\n // Track if this is a user-driven change (not initialization)\n const isInitialMount = React.useRef(true)\n const prevFilterRef = React.useRef(filter)\n\n React.useEffect(() => {\n // Skip the initial mount to avoid calling onChange during initialization\n // — unless initialization itself corrected the persisted filter (an\n // operator the field no longer offers, or a chip list that needed\n // normalizing). Then the parent must learn about it, or it would keep\n // and submit the stale filter until the user happened to touch the rule.\n if (isInitialMount.current) {\n isInitialMount.current = false\n prevFilterRef.current = filter\n if (value && filter && wasCorrectedOnInit(value, filter)) {\n onChange?.(filter)\n }\n return\n }\n\n // Only call onChange if the filter actually changed\n if (filter && filter !== prevFilterRef.current) {\n prevFilterRef.current = filter\n onChange?.(filter)\n }\n }, [filter, onChange])\n\n return (\n <div className={cx('grid grid-cols-3 gap-2', className)}>\n <Select\n options={fieldOptions}\n value={filter?.field}\n showSearch\n optionFilterProp=\"label\"\n onChange={handleFieldChange}\n size={size}\n />\n <div className=\"col-span-2 flex gap-2\">\n <Select\n options={operatorOptions}\n value={filter?.operator}\n onChange={handleOperatorChange}\n onBlur={handleBlur}\n size={size}\n popupMatchSelectWidth={false}\n />\n {valueInputConfig && (\n <ValueInput\n valueInputConfig={valueInputConfig}\n values={filter?.values}\n onChange={handleValuesChange}\n onBlur={handleBlur}\n size={size}\n />\n )}\n </div>\n </div>\n )\n}\n\nconst sameList = (a: readonly unknown[], b: readonly unknown[]) =>\n a.length === b.length && a.every((item, i) => item === b[i])\n\n// Whether initializing from a persisted filter changed anything the parent\n// holds — operator, values, or the validation result. `errors` counts: a\n// persisted rule can be unchanged yet newly invalid (a lowered `maxValues`),\n// or arrive with no `errors` at all (a plain `FilterForm` from storage), and\n// the form's validity is computed from the errors it holds, not from ours.\nconst wasCorrectedOnInit = (persisted: FormFilter, current: FormFilter) =>\n persisted.operator !== current.operator ||\n !sameList(persisted.values, current.values) ||\n persisted.errors === undefined ||\n !sameList(persisted.errors, current.errors ?? [])\n\ntype ValueInputProps = {\n valueInputConfig: ValueInputConfig\n values?: (string | number | undefined | null)[]\n onChange: (value: (string | number | undefined | null)[]) => void\n onBlur: () => void\n size: SizeType\n}\n\nconst ValueInput = ({\n valueInputConfig,\n values,\n onChange,\n onBlur,\n size,\n}: ValueInputProps) => {\n const handleChange = (value: string | number | null) => {\n if (value === null) {\n onChange([])\n } else {\n onChange([value])\n }\n }\n\n const handleNumberChange = (value: string | number | null, index: number) => {\n const newValues: (string | number | undefined | null)[] = [\n ...(values ?? []),\n ]\n newValues[index] = value\n onChange(newValues)\n }\n\n switch (valueInputConfig.type) {\n case 'select':\n return (\n <Select\n options={valueInputConfig.valueOptions}\n onChange={onChange}\n showSearch\n allowClear\n mode=\"multiple\"\n onBlur={onBlur}\n value={values}\n className=\"w-full truncate\"\n size={size}\n optionFilterProp=\"label\"\n />\n )\n case 'number':\n return (\n <div className=\"flex gap-2 grow\">\n {Array.from({ length: valueInputConfig.inputCount }).map(\n (_, index) => (\n <React.Fragment key={`number-input-${index}`}>\n {index !== 0 && <span className=\"my-auto\">and</span>}\n <InputNumber\n onChange={(value) => handleNumberChange(value, index)}\n onBlur={onBlur}\n value={values?.[index]}\n className=\"basis-1/2\"\n size={size}\n {...(valueInputConfig.numberOptions ?? {})}\n />\n </React.Fragment>\n )\n )}\n </div>\n )\n case 'text':\n if (valueInputConfig.multiValue) {\n return (\n <MultiValueTextInput\n values={values}\n maxValues={valueInputConfig.maxValues}\n onChange={onChange}\n onBlur={onBlur}\n size={size}\n />\n )\n }\n return (\n <Input\n onBlur={onBlur}\n value={values?.[0] ?? undefined}\n onChange={(e) => handleChange(e.target.value)}\n className=\"w-full\"\n size={size}\n />\n )\n default:\n return null\n }\n}\n\n// Commas, newlines (both flavours) and tabs: a column pasted from Excel or a\n// comma-separated list from an email both land as one chip per value.\nexport const MULTI_VALUE_TOKEN_SEPARATORS = [',', '\\n', '\\r', '\\t']\n\n/**\n * Chips input for the multi-value text operators (`isAnyOf` / `isNoneOf`).\n *\n * A tags Select rather than a text box split at query time: every token is\n * visible and individually removable before Apply, so a pasted value that\n * itself contains a comma (street addresses do) can be seen and fixed\n * instead of being silently shredded. The dropdown is kept closed — there\n * are no options to pick from; Enter or a separator commits a chip.\n */\nconst MultiValueTextInput = ({\n values,\n maxValues,\n onChange,\n onBlur,\n size,\n}: {\n values?: (string | number | undefined | null)[]\n maxValues?: number\n onChange: (value: (string | number | undefined | null)[]) => void\n onBlur: () => void\n size: SizeType\n}) => {\n // Rendered as stored: `useFilterField` normalizes values on the way into\n // state, so what the chips show is exactly what validation counts and\n // what `onSubmit` emits. (Numbers/nulls can't occur for a text filter;\n // the map is only for the shared `values` type.)\n const chips = (values ?? []).flatMap((value) =>\n value === null || value === undefined ? [] : [String(value)]\n )\n const over = maxValues !== undefined && chips.length > maxValues\n\n return (\n <div className=\"flex flex-col gap-1 w-full min-w-0\">\n <Select\n mode=\"tags\"\n tokenSeparators={MULTI_VALUE_TOKEN_SEPARATORS}\n value={chips}\n onChange={(next: string[]) => onChange(normalizeMultiValues(next))}\n onBlur={onBlur}\n open={false}\n suffixIcon={null}\n placeholder=\"Type or paste values, separated by commas\"\n className=\"w-full\"\n size={size}\n status={over ? 'error' : undefined}\n aria-label=\"Values\"\n />\n {maxValues !== undefined && (\n <Typography.Text\n type={over ? 'danger' : 'secondary'}\n className=\"text-xs\"\n role={over ? 'alert' : undefined}\n >\n {over\n ? `${tooManyValuesMessage(maxValues)} — remove ${\n chips.length - maxValues\n } to apply`\n : `${chips.length} / ${maxValues} values`}\n </Typography.Text>\n )}\n </div>\n )\n}\n"],"names":[],"mappings":"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"}
|