@12-apps/ui 6.24.6 → 6.24.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.
@@ -1,13 +1,13 @@
1
1
  "use client";
2
2
  import {
3
3
  DataViewsGrid
4
- } from "../../../../chunk-53PDVLRK.js";
5
- import "../../../../chunk-LWRMKF7D.js";
4
+ } from "../../../../chunk-6OORHAE7.js";
5
+ import "../../../../chunk-H5VEJDEK.js";
6
6
  import "../../../../chunk-BPLFM2GL.js";
7
7
  import "../../../../chunk-E55CJV65.js";
8
8
  import "../../../../chunk-PN5CQKA2.js";
9
9
  import "../../../../chunk-5OIH36KX.js";
10
- import "../../../../chunk-I3E2QHTH.js";
10
+ import "../../../../chunk-6NSIZ2ZO.js";
11
11
  import "../../../../chunk-5WNQJ5KS.js";
12
12
  import "../../../../chunk-EJQW2IYW.js";
13
13
  import "../../../../chunk-6LGWJFQK.js";
@@ -24,7 +24,7 @@ import "../../../../chunk-IAGOUTI6.js";
24
24
  import "../../../../chunk-EMMPNB6D.js";
25
25
  import "../../../../chunk-KX75HM5U.js";
26
26
  import "../../../../chunk-XKV5ZIBG.js";
27
- import "../../../../chunk-A7Q5AQVD.js";
27
+ import "../../../../chunk-HZVFAGLZ.js";
28
28
  import "../../../../chunk-OGGHD64H.js";
29
29
  import "../../../../chunk-CZPU5TW3.js";
30
30
  import "../../../../chunk-6VP2L3DJ.js";
@@ -1,16 +1,16 @@
1
1
  "use client";
2
2
  import {
3
3
  DataViewsTableBase
4
- } from "../../../../chunk-ZXOT3U6U.js";
4
+ } from "../../../../chunk-QPLWDSYG.js";
5
5
  import "../../../../chunk-4X4KM4ZB.js";
6
- import "../../../../chunk-53PDVLRK.js";
6
+ import "../../../../chunk-6OORHAE7.js";
7
7
  import "../../../../chunk-ZOLAYP2D.js";
8
- import "../../../../chunk-LWRMKF7D.js";
8
+ import "../../../../chunk-H5VEJDEK.js";
9
9
  import "../../../../chunk-BPLFM2GL.js";
10
10
  import "../../../../chunk-E55CJV65.js";
11
11
  import "../../../../chunk-PN5CQKA2.js";
12
12
  import "../../../../chunk-5OIH36KX.js";
13
- import "../../../../chunk-I3E2QHTH.js";
13
+ import "../../../../chunk-6NSIZ2ZO.js";
14
14
  import "../../../../chunk-5WNQJ5KS.js";
15
15
  import "../../../../chunk-EJQW2IYW.js";
16
16
  import "../../../../chunk-6LGWJFQK.js";
@@ -27,7 +27,7 @@ import "../../../../chunk-IAGOUTI6.js";
27
27
  import "../../../../chunk-EMMPNB6D.js";
28
28
  import "../../../../chunk-KX75HM5U.js";
29
29
  import "../../../../chunk-XKV5ZIBG.js";
30
- import "../../../../chunk-A7Q5AQVD.js";
30
+ import "../../../../chunk-HZVFAGLZ.js";
31
31
  import "../../../../chunk-OGGHD64H.js";
32
32
  import "../../../../chunk-CZPU5TW3.js";
33
33
  import "../../../../chunk-6VP2L3DJ.js";
@@ -1,12 +1,12 @@
1
1
  "use client";
2
2
  import {
3
3
  GridShell
4
- } from "../../../../chunk-LWRMKF7D.js";
4
+ } from "../../../../chunk-H5VEJDEK.js";
5
5
  import "../../../../chunk-BPLFM2GL.js";
6
6
  import "../../../../chunk-E55CJV65.js";
7
7
  import "../../../../chunk-PN5CQKA2.js";
8
8
  import "../../../../chunk-5OIH36KX.js";
9
- import "../../../../chunk-I3E2QHTH.js";
9
+ import "../../../../chunk-6NSIZ2ZO.js";
10
10
  import "../../../../chunk-5WNQJ5KS.js";
11
11
  import "../../../../chunk-EJQW2IYW.js";
12
12
  import "../../../../chunk-6LGWJFQK.js";
@@ -23,7 +23,7 @@ import "../../../../chunk-IAGOUTI6.js";
23
23
  import "../../../../chunk-EMMPNB6D.js";
24
24
  import "../../../../chunk-KX75HM5U.js";
25
25
  import "../../../../chunk-XKV5ZIBG.js";
26
- import "../../../../chunk-A7Q5AQVD.js";
26
+ import "../../../../chunk-HZVFAGLZ.js";
27
27
  import "../../../../chunk-OGGHD64H.js";
28
28
  import "../../../../chunk-CZPU5TW3.js";
29
29
  import "../../../../chunk-6VP2L3DJ.js";
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import {
3
3
  InlineFilterControls
4
- } from "../../../../chunk-I3E2QHTH.js";
4
+ } from "../../../../chunk-6NSIZ2ZO.js";
5
5
  import "../../../../chunk-5WNQJ5KS.js";
6
6
  import "../../../../chunk-EJQW2IYW.js";
7
7
  import "../../../../chunk-6LGWJFQK.js";
@@ -11,7 +11,7 @@ import "../../../../chunk-FV36LFPX.js";
11
11
  import "../../../../chunk-GCZJH4XL.js";
12
12
  import "../../../../chunk-F4RR4666.js";
13
13
  import "../../../../chunk-CBOB5AV6.js";
14
- import "../../../../chunk-A7Q5AQVD.js";
14
+ import "../../../../chunk-HZVFAGLZ.js";
15
15
  import "../../../../chunk-KHG57NAE.js";
16
16
  import "../../../../chunk-4NS4PJJH.js";
17
17
  import "../../../../chunk-J3JGO6Y6.js";
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  CollapsedSearch,
4
4
  InlineKeyword
5
- } from "../../../../chunk-A7Q5AQVD.js";
5
+ } from "../../../../chunk-HZVFAGLZ.js";
6
6
  import "../../../../chunk-KHG57NAE.js";
7
7
  import "../../../../chunk-6DPAWXZ3.js";
8
8
  import "../../../../chunk-PZ5AY32C.js";
@@ -14,7 +14,7 @@ import {
14
14
  import {
15
15
  CollapsedSearch,
16
16
  InlineKeyword
17
- } from "./chunk-A7Q5AQVD.js";
17
+ } from "./chunk-HZVFAGLZ.js";
18
18
 
19
19
  // src/components/data-display/DataViews/data-views-inline-bar.tsx
20
20
  import Box from "@mui/material/Box/index.js";
@@ -220,4 +220,4 @@ function FilterSlots({
220
220
  export {
221
221
  InlineFilterControls
222
222
  };
223
- //# sourceMappingURL=chunk-I3E2QHTH.js.map
223
+ //# sourceMappingURL=chunk-6NSIZ2ZO.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  GridShell
3
- } from "./chunk-LWRMKF7D.js";
3
+ } from "./chunk-H5VEJDEK.js";
4
4
  import {
5
5
  useDataViewsState
6
6
  } from "./chunk-TX5V42AY.js";
@@ -27,4 +27,4 @@ function DataViewsGrid(props) {
27
27
  export {
28
28
  DataViewsGrid
29
29
  };
30
- //# sourceMappingURL=chunk-53PDVLRK.js.map
30
+ //# sourceMappingURL=chunk-6OORHAE7.js.map
@@ -10,7 +10,7 @@ import {
10
10
  } from "./chunk-5OIH36KX.js";
11
11
  import {
12
12
  InlineFilterControls
13
- } from "./chunk-I3E2QHTH.js";
13
+ } from "./chunk-6NSIZ2ZO.js";
14
14
  import {
15
15
  GridHeaderRow
16
16
  } from "./chunk-JSH3HFDQ.js";
@@ -237,4 +237,4 @@ function GridShell(props) {
237
237
  export {
238
238
  GridShell
239
239
  };
240
- //# sourceMappingURL=chunk-LWRMKF7D.js.map
240
+ //# sourceMappingURL=chunk-H5VEJDEK.js.map
@@ -11,32 +11,47 @@ import SearchIcon from "@mui/icons-material/Search";
11
11
  import IconButton from "@mui/material/IconButton/index.js";
12
12
  import InputAdornment from "@mui/material/InputAdornment/index.js";
13
13
  import TextField from "@mui/material/TextField/index.js";
14
- import { useEffect, useRef, useState } from "react";
14
+ import { useCallback, useEffect, useRef, useState } from "react";
15
15
  import { jsx, jsxs } from "react/jsx-runtime";
16
16
  var SEARCH_DEBOUNCE_MS = 350;
17
17
  function boxWidth(fill) {
18
18
  return { flex: 1, minWidth: fill ? 0 : 200, maxWidth: fill ? "none" : 384 };
19
19
  }
20
- function InlineKeyword({
21
- value,
22
- onChange,
23
- testId,
24
- autoFocus,
25
- onEscape,
26
- fill = false
27
- }) {
28
- const copy = useDataViewsCopy();
20
+ function useKeywordDraft(value, onChange) {
29
21
  const [draft, setDraft] = useState(value);
30
22
  const prev = useRef(value);
31
23
  if (prev.current !== value) {
32
24
  prev.current = value;
33
25
  if (draft !== value) setDraft(value);
34
26
  }
27
+ const commit = useRef(onChange);
28
+ useEffect(() => {
29
+ commit.current = onChange;
30
+ });
35
31
  useEffect(() => {
36
32
  if (draft === value) return void 0;
37
- const timer = setTimeout(() => onChange(draft), SEARCH_DEBOUNCE_MS);
33
+ const timer = setTimeout(() => commit.current(draft), SEARCH_DEBOUNCE_MS);
38
34
  return () => clearTimeout(timer);
39
- }, [draft, value, onChange]);
35
+ }, [draft, value]);
36
+ const commitNow = useCallback(
37
+ (typed) => {
38
+ setDraft(typed);
39
+ if (typed !== value) commit.current(typed);
40
+ },
41
+ [value]
42
+ );
43
+ return { draft, setDraft, commitNow };
44
+ }
45
+ function InlineKeyword({
46
+ value,
47
+ onChange,
48
+ testId,
49
+ autoFocus,
50
+ onEscape,
51
+ fill = false
52
+ }) {
53
+ const copy = useDataViewsCopy();
54
+ const { draft, setDraft, commitNow } = useKeywordDraft(value, onChange);
40
55
  return /* @__PURE__ */ jsx(
41
56
  TextField,
42
57
  {
@@ -53,7 +68,7 @@ function InlineKeyword({
53
68
  }
54
69
  if (event.key !== "Enter") return;
55
70
  event.preventDefault();
56
- if (draft !== value) onChange(draft);
71
+ commitNow(event.target instanceof HTMLInputElement ? event.target.value : draft);
57
72
  },
58
73
  placeholder: copy.search.placeholder,
59
74
  inputProps: { "aria-label": copy.search.allColumnsLabel, "data-testid": testId },
@@ -123,4 +138,4 @@ export {
123
138
  InlineKeyword,
124
139
  CollapsedSearch
125
140
  };
126
- //# sourceMappingURL=chunk-A7Q5AQVD.js.map
141
+ //# sourceMappingURL=chunk-HZVFAGLZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/data-display/DataViews/data-views-search.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * THE INLINE BAR'S SEARCH — the one control on the toolbar that cannot be\n * guessed from an icon's position, which is why it is the LAST thing the\n * degradation ladder takes away. See `data-views-overflow.ts`.\n */\nimport CloseIcon from \"@mui/icons-material/Close\";\nimport SearchIcon from \"@mui/icons-material/Search\";\nimport IconButton from \"@mui/material/IconButton/index.js\";\nimport InputAdornment from \"@mui/material/InputAdornment/index.js\";\nimport TextField from \"@mui/material/TextField/index.js\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport { Box } from \"../../../mui/Box\";\nimport { useDataViewsCopy } from \"./data-views-copy-context\";\n\n/** How long the box waits after the last keystroke before it queries. */\nconst SEARCH_DEBOUNCE_MS = 350;\n\n/**\n * How wide the keyword box is allowed to be.\n *\n * The 200px floor is right while the box shares the row: a search you cannot\n * read six characters in is not a search. It is WRONG once the box has been\n * expanded out of its collapsed state on a narrow screen, where the cluster was\n * sized for a magnifier — the floor cannot be met there, so the row scrolled\n * sideways instead (154px of overhang at 320px). In that mode the box shrinks,\n * and if what is left would still be unreadable the bar hands it the whole\n * cluster instead (`searchTakeover`).\n */\nfunction boxWidth(fill: boolean): { flex: number; minWidth: number; maxWidth: number | \"none\" } {\n return { flex: 1, minWidth: fill ? 0 : 200, maxWidth: fill ? \"none\" : 384 };\n}\n\n/**\n * The box's own draft, and the two ways it reaches `onChange`.\n *\n * Extracted from {@link InlineKeyword} so the commit policy reads in one place\n * rather than split across a render body — and because it is where both halves\n * of a defect that lost searches outright had to be fixed.\n *\n * ## The debounce must not be keyed on the handler's identity\n *\n * Every caller builds `onChange` inline — the grid's own bar passes\n * `(value) => c.patch({ search: value })`, rebuilt on each render. Keying the\n * effect on it re-armed the timer on every render, and a busy admin page\n * re-renders more often than every 350ms as its queries land, so the timer\n * never survived to fire and the term was NEVER committed: measured in one\n * adopter as a dead `?q=` on a server-mode grid, where a 15-second loop pressed\n * Enter over and over and the URL never moved. So the handler is read through a\n * ref and the effect depends on `[draft, value]` alone.\n *\n * ## An immediate commit must read the BOX, not the draft\n *\n * A keystroke can land before React has processed the edit before it — a fast\n * typist, and every Playwright `fill()` followed by `press('Enter')`. `draft` is\n * then still the value from before the edit, and comparing THAT to `value` said\n * \"nothing changed\" and dropped the keystroke, leaving the term on screen with\n * an unfiltered list behind it. `commitNow` takes what the caller read off the\n * element and syncs the draft to it, which also keeps the box from being reset\n * to a stale draft by the next render when a host ignores the commit.\n */\nfunction useKeywordDraft(\n value: string,\n onChange: (next: string) => void,\n): { draft: string; setDraft: (next: string) => void; commitNow: (typed: string) => void } {\n const [draft, setDraft] = useState(value);\n const prev = useRef(value);\n // An external change (a saved view, the URL, \"Limpar\") wins over the draft.\n if (prev.current !== value) {\n prev.current = value;\n if (draft !== value) setDraft(value);\n }\n const commit = useRef(onChange);\n useEffect(() => {\n commit.current = onChange;\n });\n useEffect(() => {\n if (draft === value) return undefined;\n const timer = setTimeout(() => commit.current(draft), SEARCH_DEBOUNCE_MS);\n return () => clearTimeout(timer);\n }, [draft, value]);\n const commitNow = useCallback(\n (typed: string) => {\n setDraft(typed);\n if (typed !== value) commit.current(typed);\n },\n [value],\n );\n return { draft, setDraft, commitNow };\n}\n\n/**\n * The bar's keyword box.\n *\n * DEBOUNCED rather than committed on Enter/blur. In server mode every commit is\n * a round trip, and a box that only fires on blur makes the operator press a\n * key they were not told about to see any result at all — they type, nothing\n * happens, and the list looks broken. 350ms is long enough that a word costs\n * one query, short enough to feel like live search.\n *\n * `Escape` clears; the X clears without leaving the box. Both exist because\n * clearing by selecting-and-deleting is three gestures for the most common\n * thing anyone does to a search box.\n */\nexport function InlineKeyword({\n value,\n onChange,\n testId,\n autoFocus,\n onEscape,\n fill = false,\n}: {\n value: string;\n onChange: (value: string) => void;\n testId?: string;\n autoFocus?: boolean;\n /** Called on Escape with the box already empty — the collapsed bar closes it. */\n onEscape?: () => void;\n /** Take whatever width there is, with no minimum — see {@link boxWidth}. */\n fill?: boolean;\n}): React.JSX.Element {\n const copy = useDataViewsCopy();\n const { draft, setDraft, commitNow } = useKeywordDraft(value, onChange);\n\n return (\n <TextField\n size=\"small\"\n value={draft}\n autoFocus={autoFocus}\n onChange={(event) => setDraft(event.target.value)}\n onKeyDown={(event) => {\n if (event.key === \"Escape\") {\n event.preventDefault();\n if (draft === \"\") onEscape?.();\n setDraft(\"\");\n return;\n }\n // Enter still commits IMMEDIATELY, ahead of the debounce — someone who\n // presses it has decided, and waiting out the timer reads as a stall.\n if (event.key !== \"Enter\") return;\n event.preventDefault();\n // The BOX, not `draft` — see {@link useKeywordDraft}.\n commitNow(event.target instanceof HTMLInputElement ? event.target.value : draft);\n }}\n placeholder={copy.search.placeholder}\n inputProps={{ \"aria-label\": copy.search.allColumnsLabel, \"data-testid\": testId }}\n InputProps={{\n startAdornment: (\n <InputAdornment position=\"start\">\n <SearchIcon sx={{ fontSize: 16, color: \"text.secondary\" }} />\n </InputAdornment>\n ),\n endAdornment: draft ? (\n <InputAdornment position=\"end\">\n <IconButton\n size=\"small\"\n aria-label={copy.search.clear}\n data-testid={testId ? `${testId}-clear` : undefined}\n onClick={() => setDraft(\"\")}\n sx={{ p: 0.25 }}\n >\n <CloseIcon sx={{ fontSize: 14 }} />\n </IconButton>\n </InputAdornment>\n ) : undefined,\n }}\n // Takes the free space and gives it up first — step 3 of the ladder,\n // which needs no flag because flex does it (see `computeSplit`).\n sx={boxWidth(fill)}\n />\n );\n}\n\n/**\n * Step 4 of the ladder: the search as a magnifier.\n *\n * Carries a dot when a search is APPLIED. Collapsing a control that is\n * currently filtering the list, with nothing saying so, is how an operator ends\n * up staring at 3 rows of 214 and reading it as missing data.\n */\nexport function CollapsedSearch({\n active,\n onOpen,\n testId,\n}: {\n active: boolean;\n onOpen: () => void;\n testId?: string;\n}): React.JSX.Element {\n const copy = useDataViewsCopy();\n return (\n <IconButton\n size=\"small\"\n onClick={onOpen}\n aria-label={copy.search.open}\n aria-expanded={false}\n data-testid={testId ? `${testId}-collapsed` : undefined}\n sx={{\n position: \"relative\",\n border: 1,\n borderRadius: 1,\n borderColor: active ? \"primary.main\" : \"divider\",\n color: active ? \"primary.main\" : \"text.secondary\",\n bgcolor: active ? \"action.selected\" : \"background.paper\",\n }}\n >\n <SearchIcon sx={{ fontSize: 18 }} />\n {active && (\n <Box\n component=\"span\"\n sx={{\n position: \"absolute\",\n top: 2,\n right: 2,\n width: 6,\n height: 6,\n borderRadius: \"50%\",\n bgcolor: \"primary.main\",\n }}\n />\n )}\n </IconButton>\n );\n}\n\n"],"mappings":"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","names":["default"]}
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-4X4KM4ZB.js";
4
4
  import {
5
5
  DataViewsGrid
6
- } from "./chunk-53PDVLRK.js";
6
+ } from "./chunk-6OORHAE7.js";
7
7
  import {
8
8
  viewStateKey
9
9
  } from "./chunk-ZOLAYP2D.js";
@@ -178,4 +178,4 @@ function renderViewDialogs(ctl, fields, columns, views, testIdPrefix, onSaved) {
178
178
  export {
179
179
  DataViewsTableBase
180
180
  };
181
- //# sourceMappingURL=chunk-ZXOT3U6U.js.map
181
+ //# sourceMappingURL=chunk-QPLWDSYG.js.map
@@ -3,11 +3,11 @@ import {
3
3
  } from "../chunk-JWW2Z3RH.js";
4
4
  import {
5
5
  DataViewsTableBase
6
- } from "../chunk-ZXOT3U6U.js";
6
+ } from "../chunk-QPLWDSYG.js";
7
7
  import "../chunk-4X4KM4ZB.js";
8
8
  import {
9
9
  DataViewsGrid
10
- } from "../chunk-53PDVLRK.js";
10
+ } from "../chunk-6OORHAE7.js";
11
11
  import {
12
12
  BaseListCard
13
13
  } from "../chunk-WR5JFDQ5.js";
@@ -22,14 +22,14 @@ import {
22
22
  DropIndicator,
23
23
  useDragItem
24
24
  } from "../chunk-JCJGJX7V.js";
25
- import "../chunk-LWRMKF7D.js";
25
+ import "../chunk-H5VEJDEK.js";
26
26
  import "../chunk-BPLFM2GL.js";
27
27
  import {
28
28
  DataViewsBoard
29
29
  } from "../chunk-E55CJV65.js";
30
30
  import "../chunk-PN5CQKA2.js";
31
31
  import "../chunk-5OIH36KX.js";
32
- import "../chunk-I3E2QHTH.js";
32
+ import "../chunk-6NSIZ2ZO.js";
33
33
  import "../chunk-5WNQJ5KS.js";
34
34
  import "../chunk-EJQW2IYW.js";
35
35
  import "../chunk-6LGWJFQK.js";
@@ -46,7 +46,7 @@ import "../chunk-IAGOUTI6.js";
46
46
  import "../chunk-EMMPNB6D.js";
47
47
  import "../chunk-KX75HM5U.js";
48
48
  import "../chunk-XKV5ZIBG.js";
49
- import "../chunk-A7Q5AQVD.js";
49
+ import "../chunk-HZVFAGLZ.js";
50
50
  import "../chunk-OGGHD64H.js";
51
51
  import {
52
52
  DEFAULT_RAILS,
@@ -1 +1 @@
1
- {"version":3,"file":"data-views-search.d.ts","sourceRoot":"","sources":["../../../../../src/components/data-display/DataViews/data-views-search.tsx"],"names":[],"mappings":"AAmCA;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,QAAQ,EACR,MAAM,EACN,SAAS,EACT,QAAQ,EACR,IAAY,GACb,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,iFAAiF;IACjF,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,4EAA4E;IAC5E,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CA6DpB;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,EAC9B,MAAM,EACN,MAAM,EACN,MAAM,GACP,EAAE;IACD,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAmCpB"}
1
+ {"version":3,"file":"data-views-search.d.ts","sourceRoot":"","sources":["../../../../../src/components/data-display/DataViews/data-views-search.tsx"],"names":[],"mappings":"AA6FA;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,QAAQ,EACR,MAAM,EACN,SAAS,EACT,QAAQ,EACR,IAAY,GACb,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,iFAAiF;IACjF,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,4EAA4E;IAC5E,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAmDpB;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,EAC9B,MAAM,EACN,MAAM,EACN,MAAM,GACP,EAAE;IACD,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAmCpB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@12-apps/ui",
3
- "version": "6.24.6",
3
+ "version": "6.24.7",
4
4
  "license": "MIT",
5
5
  "packageManager": "pnpm@10.34.5",
6
6
  "type": "module",
@@ -10,7 +10,7 @@ import SearchIcon from "@mui/icons-material/Search";
10
10
  import IconButton from "@mui/material/IconButton/index.js";
11
11
  import InputAdornment from "@mui/material/InputAdornment/index.js";
12
12
  import TextField from "@mui/material/TextField/index.js";
13
- import { useEffect, useRef, useState } from "react";
13
+ import { useCallback, useEffect, useRef, useState } from "react";
14
14
 
15
15
  import { Box } from "../../../mui/Box";
16
16
  import { useDataViewsCopy } from "./data-views-copy-context";
@@ -33,6 +33,64 @@ function boxWidth(fill: boolean): { flex: number; minWidth: number; maxWidth: nu
33
33
  return { flex: 1, minWidth: fill ? 0 : 200, maxWidth: fill ? "none" : 384 };
34
34
  }
35
35
 
36
+ /**
37
+ * The box's own draft, and the two ways it reaches `onChange`.
38
+ *
39
+ * Extracted from {@link InlineKeyword} so the commit policy reads in one place
40
+ * rather than split across a render body — and because it is where both halves
41
+ * of a defect that lost searches outright had to be fixed.
42
+ *
43
+ * ## The debounce must not be keyed on the handler's identity
44
+ *
45
+ * Every caller builds `onChange` inline — the grid's own bar passes
46
+ * `(value) => c.patch({ search: value })`, rebuilt on each render. Keying the
47
+ * effect on it re-armed the timer on every render, and a busy admin page
48
+ * re-renders more often than every 350ms as its queries land, so the timer
49
+ * never survived to fire and the term was NEVER committed: measured in one
50
+ * adopter as a dead `?q=` on a server-mode grid, where a 15-second loop pressed
51
+ * Enter over and over and the URL never moved. So the handler is read through a
52
+ * ref and the effect depends on `[draft, value]` alone.
53
+ *
54
+ * ## An immediate commit must read the BOX, not the draft
55
+ *
56
+ * A keystroke can land before React has processed the edit before it — a fast
57
+ * typist, and every Playwright `fill()` followed by `press('Enter')`. `draft` is
58
+ * then still the value from before the edit, and comparing THAT to `value` said
59
+ * "nothing changed" and dropped the keystroke, leaving the term on screen with
60
+ * an unfiltered list behind it. `commitNow` takes what the caller read off the
61
+ * element and syncs the draft to it, which also keeps the box from being reset
62
+ * to a stale draft by the next render when a host ignores the commit.
63
+ */
64
+ function useKeywordDraft(
65
+ value: string,
66
+ onChange: (next: string) => void,
67
+ ): { draft: string; setDraft: (next: string) => void; commitNow: (typed: string) => void } {
68
+ const [draft, setDraft] = useState(value);
69
+ const prev = useRef(value);
70
+ // An external change (a saved view, the URL, "Limpar") wins over the draft.
71
+ if (prev.current !== value) {
72
+ prev.current = value;
73
+ if (draft !== value) setDraft(value);
74
+ }
75
+ const commit = useRef(onChange);
76
+ useEffect(() => {
77
+ commit.current = onChange;
78
+ });
79
+ useEffect(() => {
80
+ if (draft === value) return undefined;
81
+ const timer = setTimeout(() => commit.current(draft), SEARCH_DEBOUNCE_MS);
82
+ return () => clearTimeout(timer);
83
+ }, [draft, value]);
84
+ const commitNow = useCallback(
85
+ (typed: string) => {
86
+ setDraft(typed);
87
+ if (typed !== value) commit.current(typed);
88
+ },
89
+ [value],
90
+ );
91
+ return { draft, setDraft, commitNow };
92
+ }
93
+
36
94
  /**
37
95
  * The bar's keyword box.
38
96
  *
@@ -64,18 +122,7 @@ export function InlineKeyword({
64
122
  fill?: boolean;
65
123
  }): React.JSX.Element {
66
124
  const copy = useDataViewsCopy();
67
- const [draft, setDraft] = useState(value);
68
- const prev = useRef(value);
69
- // An external change (a saved view, the URL, "Limpar") wins over the draft.
70
- if (prev.current !== value) {
71
- prev.current = value;
72
- if (draft !== value) setDraft(value);
73
- }
74
- useEffect(() => {
75
- if (draft === value) return undefined;
76
- const timer = setTimeout(() => onChange(draft), SEARCH_DEBOUNCE_MS);
77
- return () => clearTimeout(timer);
78
- }, [draft, value, onChange]);
125
+ const { draft, setDraft, commitNow } = useKeywordDraft(value, onChange);
79
126
 
80
127
  return (
81
128
  <TextField
@@ -94,7 +141,8 @@ export function InlineKeyword({
94
141
  // presses it has decided, and waiting out the timer reads as a stall.
95
142
  if (event.key !== "Enter") return;
96
143
  event.preventDefault();
97
- if (draft !== value) onChange(draft);
144
+ // The BOX, not `draft` — see {@link useKeywordDraft}.
145
+ commitNow(event.target instanceof HTMLInputElement ? event.target.value : draft);
98
146
  }}
99
147
  placeholder={copy.search.placeholder}
100
148
  inputProps={{ "aria-label": copy.search.allColumnsLabel, "data-testid": testId }}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/data-display/DataViews/data-views-search.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * THE INLINE BAR'S SEARCH — the one control on the toolbar that cannot be\n * guessed from an icon's position, which is why it is the LAST thing the\n * degradation ladder takes away. See `data-views-overflow.ts`.\n */\nimport CloseIcon from \"@mui/icons-material/Close\";\nimport SearchIcon from \"@mui/icons-material/Search\";\nimport IconButton from \"@mui/material/IconButton/index.js\";\nimport InputAdornment from \"@mui/material/InputAdornment/index.js\";\nimport TextField from \"@mui/material/TextField/index.js\";\nimport { useEffect, useRef, useState } from \"react\";\n\nimport { Box } from \"../../../mui/Box\";\nimport { useDataViewsCopy } from \"./data-views-copy-context\";\n\n/** How long the box waits after the last keystroke before it queries. */\nconst SEARCH_DEBOUNCE_MS = 350;\n\n/**\n * How wide the keyword box is allowed to be.\n *\n * The 200px floor is right while the box shares the row: a search you cannot\n * read six characters in is not a search. It is WRONG once the box has been\n * expanded out of its collapsed state on a narrow screen, where the cluster was\n * sized for a magnifier — the floor cannot be met there, so the row scrolled\n * sideways instead (154px of overhang at 320px). In that mode the box shrinks,\n * and if what is left would still be unreadable the bar hands it the whole\n * cluster instead (`searchTakeover`).\n */\nfunction boxWidth(fill: boolean): { flex: number; minWidth: number; maxWidth: number | \"none\" } {\n return { flex: 1, minWidth: fill ? 0 : 200, maxWidth: fill ? \"none\" : 384 };\n}\n\n/**\n * The bar's keyword box.\n *\n * DEBOUNCED rather than committed on Enter/blur. In server mode every commit is\n * a round trip, and a box that only fires on blur makes the operator press a\n * key they were not told about to see any result at all — they type, nothing\n * happens, and the list looks broken. 350ms is long enough that a word costs\n * one query, short enough to feel like live search.\n *\n * `Escape` clears; the X clears without leaving the box. Both exist because\n * clearing by selecting-and-deleting is three gestures for the most common\n * thing anyone does to a search box.\n */\nexport function InlineKeyword({\n value,\n onChange,\n testId,\n autoFocus,\n onEscape,\n fill = false,\n}: {\n value: string;\n onChange: (value: string) => void;\n testId?: string;\n autoFocus?: boolean;\n /** Called on Escape with the box already empty — the collapsed bar closes it. */\n onEscape?: () => void;\n /** Take whatever width there is, with no minimum — see {@link boxWidth}. */\n fill?: boolean;\n}): React.JSX.Element {\n const copy = useDataViewsCopy();\n const [draft, setDraft] = useState(value);\n const prev = useRef(value);\n // An external change (a saved view, the URL, \"Limpar\") wins over the draft.\n if (prev.current !== value) {\n prev.current = value;\n if (draft !== value) setDraft(value);\n }\n useEffect(() => {\n if (draft === value) return undefined;\n const timer = setTimeout(() => onChange(draft), SEARCH_DEBOUNCE_MS);\n return () => clearTimeout(timer);\n }, [draft, value, onChange]);\n\n return (\n <TextField\n size=\"small\"\n value={draft}\n autoFocus={autoFocus}\n onChange={(event) => setDraft(event.target.value)}\n onKeyDown={(event) => {\n if (event.key === \"Escape\") {\n event.preventDefault();\n if (draft === \"\") onEscape?.();\n setDraft(\"\");\n return;\n }\n // Enter still commits IMMEDIATELY, ahead of the debounce — someone who\n // presses it has decided, and waiting out the timer reads as a stall.\n if (event.key !== \"Enter\") return;\n event.preventDefault();\n if (draft !== value) onChange(draft);\n }}\n placeholder={copy.search.placeholder}\n inputProps={{ \"aria-label\": copy.search.allColumnsLabel, \"data-testid\": testId }}\n InputProps={{\n startAdornment: (\n <InputAdornment position=\"start\">\n <SearchIcon sx={{ fontSize: 16, color: \"text.secondary\" }} />\n </InputAdornment>\n ),\n endAdornment: draft ? (\n <InputAdornment position=\"end\">\n <IconButton\n size=\"small\"\n aria-label={copy.search.clear}\n data-testid={testId ? `${testId}-clear` : undefined}\n onClick={() => setDraft(\"\")}\n sx={{ p: 0.25 }}\n >\n <CloseIcon sx={{ fontSize: 14 }} />\n </IconButton>\n </InputAdornment>\n ) : undefined,\n }}\n // Takes the free space and gives it up first — step 3 of the ladder,\n // which needs no flag because flex does it (see `computeSplit`).\n sx={boxWidth(fill)}\n />\n );\n}\n\n/**\n * Step 4 of the ladder: the search as a magnifier.\n *\n * Carries a dot when a search is APPLIED. Collapsing a control that is\n * currently filtering the list, with nothing saying so, is how an operator ends\n * up staring at 3 rows of 214 and reading it as missing data.\n */\nexport function CollapsedSearch({\n active,\n onOpen,\n testId,\n}: {\n active: boolean;\n onOpen: () => void;\n testId?: string;\n}): React.JSX.Element {\n const copy = useDataViewsCopy();\n return (\n <IconButton\n size=\"small\"\n onClick={onOpen}\n aria-label={copy.search.open}\n aria-expanded={false}\n data-testid={testId ? `${testId}-collapsed` : undefined}\n sx={{\n position: \"relative\",\n border: 1,\n borderRadius: 1,\n borderColor: active ? \"primary.main\" : \"divider\",\n color: active ? \"primary.main\" : \"text.secondary\",\n bgcolor: active ? \"action.selected\" : \"background.paper\",\n }}\n >\n <SearchIcon sx={{ fontSize: 18 }} />\n {active && (\n <Box\n component=\"span\"\n sx={{\n position: \"absolute\",\n top: 2,\n right: 2,\n width: 6,\n height: 6,\n borderRadius: \"50%\",\n bgcolor: \"primary.main\",\n }}\n />\n )}\n </IconButton>\n );\n}\n\n"],"mappings":"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","names":["default"]}