@primer/react 38.6.3-rc.f8f5fddc3 → 38.7.0-rc.b626e5d83

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.

Potentially problematic release.


This version of @primer/react might be problematic. Click here for more details.

Files changed (78) hide show
  1. package/CHANGELOG.md +48 -1
  2. package/dist/ActionBar/ActionBar.d.ts +8 -0
  3. package/dist/ActionBar/ActionBar.d.ts.map +1 -1
  4. package/dist/ActionBar/ActionBar.js +83 -53
  5. package/dist/ActionBar/index.d.ts +1 -0
  6. package/dist/ActionBar/index.d.ts.map +1 -1
  7. package/dist/Autocomplete/Autocomplete.d.ts +0 -3
  8. package/dist/Autocomplete/Autocomplete.d.ts.map +1 -1
  9. package/dist/Autocomplete/Autocomplete.js +69 -23
  10. package/dist/Autocomplete/AutocompleteContext.d.ts +23 -3
  11. package/dist/Autocomplete/AutocompleteContext.d.ts.map +1 -1
  12. package/dist/Autocomplete/AutocompleteContext.js +20 -1
  13. package/dist/Autocomplete/AutocompleteInput.d.ts.map +1 -1
  14. package/dist/Autocomplete/AutocompleteInput.js +8 -5
  15. package/dist/Autocomplete/AutocompleteMenu.d.ts.map +1 -1
  16. package/dist/Autocomplete/AutocompleteMenu.js +123 -121
  17. package/dist/{BaseStyles-79fd37c4.css → BaseStyles-7e59cc50.css} +2 -2
  18. package/dist/BaseStyles-7e59cc50.css.map +1 -0
  19. package/dist/BaseStyles.module.css.js +1 -1
  20. package/dist/Dialog/{Dialog-1a61e61a.css → Dialog-92b5e3b7.css} +2 -2
  21. package/dist/Dialog/Dialog-92b5e3b7.css.map +1 -0
  22. package/dist/Dialog/Dialog.d.ts.map +1 -1
  23. package/dist/Dialog/Dialog.js +21 -5
  24. package/dist/Dialog/Dialog.module.css.js +1 -1
  25. package/dist/FeatureFlags/DefaultFeatureFlags.d.ts.map +1 -1
  26. package/dist/FeatureFlags/DefaultFeatureFlags.js +1 -0
  27. package/dist/FeatureFlags/FeatureFlags.d.ts +6 -0
  28. package/dist/FeatureFlags/FeatureFlags.d.ts.map +1 -1
  29. package/dist/FeatureFlags/FeatureFlags.js +41 -32
  30. package/dist/FilteredActionList/FilteredActionList.d.ts +6 -1
  31. package/dist/FilteredActionList/FilteredActionList.d.ts.map +1 -1
  32. package/dist/FilteredActionList/FilteredActionList.js +4 -2
  33. package/dist/PageLayout/{PageLayout-c092f3db.css → PageLayout-51007c87.css} +2 -2
  34. package/dist/PageLayout/PageLayout-51007c87.css.map +1 -0
  35. package/dist/PageLayout/PageLayout.d.ts.map +1 -1
  36. package/dist/PageLayout/PageLayout.js +399 -313
  37. package/dist/PageLayout/PageLayout.module.css.js +1 -1
  38. package/dist/PageLayout/paneUtils.d.ts +11 -0
  39. package/dist/PageLayout/paneUtils.d.ts.map +1 -0
  40. package/dist/PageLayout/paneUtils.js +34 -0
  41. package/dist/PageLayout/usePaneWidth.d.ts +2 -1
  42. package/dist/PageLayout/usePaneWidth.d.ts.map +1 -1
  43. package/dist/PageLayout/usePaneWidth.js +56 -44
  44. package/dist/SelectPanel/SelectPanel.d.ts +6 -1
  45. package/dist/SelectPanel/SelectPanel.d.ts.map +1 -1
  46. package/dist/SelectPanel/SelectPanel.js +3 -1
  47. package/dist/TextInput/TextInput-dff8f842.css +2 -0
  48. package/dist/TextInput/TextInput-dff8f842.css.map +1 -0
  49. package/dist/TextInput/TextInput.d.ts +5 -0
  50. package/dist/TextInput/TextInput.d.ts.map +1 -1
  51. package/dist/TextInput/TextInput.js +125 -51
  52. package/dist/TextInput/TextInput.module.css.js +5 -0
  53. package/dist/Textarea/{TextArea-54099020.css → TextArea-53e27580.css} +2 -2
  54. package/dist/Textarea/TextArea-53e27580.css.map +1 -0
  55. package/dist/Textarea/TextArea.module.css.js +2 -2
  56. package/dist/Textarea/Textarea.d.ts +10 -0
  57. package/dist/Textarea/Textarea.d.ts.map +1 -1
  58. package/dist/Textarea/Textarea.js +215 -69
  59. package/dist/ToggleSwitch/ToggleSwitch-40bab513.css +2 -0
  60. package/dist/ToggleSwitch/ToggleSwitch-40bab513.css.map +1 -0
  61. package/dist/ToggleSwitch/ToggleSwitch.module.css.js +1 -1
  62. package/dist/hooks/useFocusZone.d.ts +4 -0
  63. package/dist/hooks/useFocusZone.d.ts.map +1 -1
  64. package/dist/internal/utils/hasInteractiveNodes.d.ts.map +1 -1
  65. package/dist/internal/utils/hasInteractiveNodes.js +22 -16
  66. package/dist/utils/__tests__/character-counter.test.d.ts +2 -0
  67. package/dist/utils/__tests__/character-counter.test.d.ts.map +1 -0
  68. package/dist/utils/character-counter.d.ts +27 -0
  69. package/dist/utils/character-counter.d.ts.map +1 -0
  70. package/dist/utils/character-counter.js +65 -0
  71. package/generated/components.json +16 -0
  72. package/package.json +9 -9
  73. package/dist/BaseStyles-79fd37c4.css.map +0 -1
  74. package/dist/Dialog/Dialog-1a61e61a.css.map +0 -1
  75. package/dist/PageLayout/PageLayout-c092f3db.css.map +0 -1
  76. package/dist/Textarea/TextArea-54099020.css.map +0 -1
  77. package/dist/ToggleSwitch/ToggleSwitch-d1bd60b0.css +0 -2
  78. package/dist/ToggleSwitch/ToggleSwitch-d1bd60b0.css.map +0 -1
@@ -1,4 +1,4 @@
1
- import './PageLayout-c092f3db.css';
1
+ import './PageLayout-51007c87.css';
2
2
 
3
3
  var classes = {"paneMaxWidthDiffBreakpoint":"1280","paneMaxWidthDiffDefault":"511","PageLayoutRoot":"prc-PageLayout-PageLayoutRoot--KH-d","PageLayoutWrapper":"prc-PageLayout-PageLayoutWrapper-2BhU2","PageLayoutContent":"prc-PageLayout-PageLayoutContent-BneH9","HorizontalDivider":"prc-PageLayout-HorizontalDivider-JLVqp","VerticalDivider":"prc-PageLayout-VerticalDivider-9QRmK","Header":"prc-PageLayout-Header-0of-R","HeaderContent":"prc-PageLayout-HeaderContent-gdFfN","HeaderHorizontalDivider":"prc-PageLayout-HeaderHorizontalDivider-odAHl","ContentWrapper":"prc-PageLayout-ContentWrapper-gR9eG","Content":"prc-PageLayout-Content-xWL-A","PaneWrapper":"prc-PageLayout-PaneWrapper-pHPop","PaneVerticalDivider":"prc-PageLayout-PaneVerticalDivider-le57g","Pane":"prc-PageLayout-Pane-AyzHK","PaneHorizontalDivider":"prc-PageLayout-PaneHorizontalDivider-9tbnE","FooterWrapper":"prc-PageLayout-FooterWrapper-9cB8y","FooterHorizontalDivider":"prc-PageLayout-FooterHorizontalDivider-W-RaS","FooterContent":"prc-PageLayout-FooterContent-HUS0V","DraggableHandle":"prc-PageLayout-DraggableHandle-9s6B4"};
4
4
 
@@ -0,0 +1,11 @@
1
+ type DraggingStylesParams = {
2
+ handle: HTMLElement | null;
3
+ pane: HTMLElement | null;
4
+ contentWrapper: HTMLElement | null;
5
+ };
6
+ /** Apply visual feedback and performance optimizations during drag */
7
+ export declare function setDraggingStyles({ handle, pane, contentWrapper }: DraggingStylesParams): void;
8
+ /** Remove drag styles and restore normal state */
9
+ export declare function removeDraggingStyles({ handle, pane, contentWrapper }: DraggingStylesParams): void;
10
+ export {};
11
+ //# sourceMappingURL=paneUtils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paneUtils.d.ts","sourceRoot":"","sources":["../../src/PageLayout/paneUtils.ts"],"names":[],"mappings":"AAAA,KAAK,oBAAoB,GAAG;IAC1B,MAAM,EAAE,WAAW,GAAG,IAAI,CAAA;IAC1B,IAAI,EAAE,WAAW,GAAG,IAAI,CAAA;IACxB,cAAc,EAAE,WAAW,GAAG,IAAI,CAAA;CACnC,CAAA;AAID,sEAAsE;AACtE,wBAAgB,iBAAiB,CAAC,EAAC,MAAM,EAAE,IAAI,EAAE,cAAc,EAAC,EAAE,oBAAoB,QAWrF;AAED,kDAAkD;AAClD,wBAAgB,oBAAoB,CAAC,EAAC,MAAM,EAAE,IAAI,EAAE,cAAc,EAAC,EAAE,oBAAoB,QAOxF"}
@@ -0,0 +1,34 @@
1
+ const DATA_DRAGGING_ATTR = 'data-dragging';
2
+
3
+ /** Apply visual feedback and performance optimizations during drag */
4
+ function setDraggingStyles({
5
+ handle,
6
+ pane,
7
+ contentWrapper
8
+ }) {
9
+ // Handle visual feedback (must be inline for instant response)
10
+ // Use CSS variable to control ::before pseudo-element background color.
11
+ // This avoids cascade conflicts between inline styles and pseudo-element backgrounds.
12
+ handle === null || handle === void 0 ? void 0 : handle.style.setProperty('--draggable-handle--bg-color', 'var(--bgColor-accent-emphasis)');
13
+ handle === null || handle === void 0 ? void 0 : handle.style.setProperty('--draggable-handle--drag-opacity', '1');
14
+ handle === null || handle === void 0 ? void 0 : handle.style.setProperty('--draggable-handle--transition', 'none');
15
+
16
+ // Set attribute for CSS containment (O(1) direct selector, not descendant)
17
+ pane === null || pane === void 0 ? void 0 : pane.setAttribute(DATA_DRAGGING_ATTR, 'true');
18
+ contentWrapper === null || contentWrapper === void 0 ? void 0 : contentWrapper.setAttribute(DATA_DRAGGING_ATTR, 'true');
19
+ }
20
+
21
+ /** Remove drag styles and restore normal state */
22
+ function removeDraggingStyles({
23
+ handle,
24
+ pane,
25
+ contentWrapper
26
+ }) {
27
+ handle === null || handle === void 0 ? void 0 : handle.style.removeProperty('--draggable-handle--bg-color');
28
+ handle === null || handle === void 0 ? void 0 : handle.style.removeProperty('--draggable-handle--drag-opacity');
29
+ handle === null || handle === void 0 ? void 0 : handle.style.removeProperty('--draggable-handle--transition');
30
+ pane === null || pane === void 0 ? void 0 : pane.removeAttribute(DATA_DRAGGING_ATTR);
31
+ contentWrapper === null || contentWrapper === void 0 ? void 0 : contentWrapper.removeAttribute(DATA_DRAGGING_ATTR);
32
+ }
33
+
34
+ export { removeDraggingStyles, setDraggingStyles };
@@ -13,6 +13,7 @@ export type UsePaneWidthOptions = {
13
13
  widthStorageKey: string;
14
14
  paneRef: React.RefObject<HTMLDivElement | null>;
15
15
  handleRef: React.RefObject<HTMLDivElement | null>;
16
+ contentWrapperRef: React.RefObject<HTMLDivElement | null>;
16
17
  };
17
18
  export type UsePaneWidthResult = {
18
19
  /** Current width for React state (used in ARIA attributes) */
@@ -66,6 +67,6 @@ export declare const updateAriaValues: (handle: HTMLElement | null, values: {
66
67
  * Handles initialization from storage, clamping on viewport resize, and provides
67
68
  * functions to save and reset width.
68
69
  */
69
- export declare function usePaneWidth({ width, minWidth, resizable, widthStorageKey, paneRef, handleRef, }: UsePaneWidthOptions): UsePaneWidthResult;
70
+ export declare function usePaneWidth({ width, minWidth, resizable, widthStorageKey, paneRef, handleRef, contentWrapperRef, }: UsePaneWidthOptions): UsePaneWidthResult;
70
71
  export {};
71
72
  //# sourceMappingURL=usePaneWidth.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"usePaneWidth.d.ts","sourceRoot":"","sources":["../../src/PageLayout/usePaneWidth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAwB,MAAM,OAAO,CAAA;AAQ5C,KAAK,WAAW,GAAG,GAAG,MAAM,IAAI,CAAA;AAEhC,MAAM,MAAM,kBAAkB,GAAG;IAC/B,GAAG,EAAE,WAAW,CAAA;IAChB,OAAO,EAAE,WAAW,CAAA;IACpB,GAAG,EAAE,WAAW,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAA;AAEpD,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,EAAE,SAAS,GAAG,kBAAkB,CAAA;IACrC,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,OAAO,CAAA;IAClB,eAAe,EAAE,MAAM,CAAA;IACvB,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAA;IAC/C,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAA;CAClD,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,8DAA8D;IAC9D,YAAY,EAAE,MAAM,CAAA;IACpB,wDAAwD;IACxD,eAAe,EAAE,KAAK,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAA;IAC/C,iCAAiC;IACjC,YAAY,EAAE,MAAM,CAAA;IACpB,8DAA8D;IAC9D,YAAY,EAAE,MAAM,CAAA;IACpB,6CAA6C;IAC7C,eAAe,EAAE,MAAM,MAAM,CAAA;IAC7B,yDAAyD;IACzD,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAClC,6BAA6B;IAC7B,eAAe,EAAE,MAAM,MAAM,CAAA;CAC9B,CAAA;AAKD;;;GAGG;AACH,eAAO,MAAM,sBAAsB,QAA6C,CAAA;AAIhF;;;GAGG;AACH,eAAO,MAAM,qBAAqB,MAAM,CAAA;AAExC;;;GAGG;AACH,eAAO,MAAM,cAAc,IAAI,CAAA;AAE/B,6CAA6C;AAC7C,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CAAyC,CAAA;AAKhG,eAAO,MAAM,oBAAoB,GAAI,OAAO,SAAS,GAAG,kBAAkB,KAAG,KAAK,IAAI,kBAGrF,CAAA;AAED,eAAO,MAAM,WAAW,GAAI,OAAO,SAAS,GAAG,kBAAkB,KAAG,KAAK,IAAI,SAE5E,CAAA;AAED,eAAO,MAAM,mBAAmB,GAAI,GAAG,SAAS,GAAG,kBAAkB,KAAG,MAOvE,CAAA;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI,GAAG,MAAM,CAI3E;AAID,eAAO,MAAM,gBAAgB,GAC3B,QAAQ,WAAW,GAAG,IAAI,EAC1B,QAAQ;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAC,SASvD,CAAA;AAKD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,QAAQ,EACR,SAAS,EACT,eAAe,EACf,OAAO,EACP,SAAS,GACV,EAAE,mBAAmB,GAAG,kBAAkB,CA8L1C"}
1
+ {"version":3,"file":"usePaneWidth.d.ts","sourceRoot":"","sources":["../../src/PageLayout/usePaneWidth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAwB,MAAM,OAAO,CAAA;AAQ5C,KAAK,WAAW,GAAG,GAAG,MAAM,IAAI,CAAA;AAEhC,MAAM,MAAM,kBAAkB,GAAG;IAC/B,GAAG,EAAE,WAAW,CAAA;IAChB,OAAO,EAAE,WAAW,CAAA;IACpB,GAAG,EAAE,WAAW,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAA;AAEpD,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,EAAE,SAAS,GAAG,kBAAkB,CAAA;IACrC,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,OAAO,CAAA;IAClB,eAAe,EAAE,MAAM,CAAA;IACvB,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAA;IAC/C,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAA;IACjD,iBAAiB,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAA;CAC1D,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,8DAA8D;IAC9D,YAAY,EAAE,MAAM,CAAA;IACpB,wDAAwD;IACxD,eAAe,EAAE,KAAK,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAA;IAC/C,iCAAiC;IACjC,YAAY,EAAE,MAAM,CAAA;IACpB,8DAA8D;IAC9D,YAAY,EAAE,MAAM,CAAA;IACpB,6CAA6C;IAC7C,eAAe,EAAE,MAAM,MAAM,CAAA;IAC7B,yDAAyD;IACzD,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAClC,6BAA6B;IAC7B,eAAe,EAAE,MAAM,MAAM,CAAA;CAC9B,CAAA;AAKD;;;GAGG;AACH,eAAO,MAAM,sBAAsB,QAA6C,CAAA;AAIhF;;;GAGG;AACH,eAAO,MAAM,qBAAqB,MAAM,CAAA;AAExC;;;GAGG;AACH,eAAO,MAAM,cAAc,IAAI,CAAA;AAE/B,6CAA6C;AAC7C,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CAAyC,CAAA;AAKhG,eAAO,MAAM,oBAAoB,GAAI,OAAO,SAAS,GAAG,kBAAkB,KAAG,KAAK,IAAI,kBAGrF,CAAA;AAED,eAAO,MAAM,WAAW,GAAI,OAAO,SAAS,GAAG,kBAAkB,KAAG,KAAK,IAAI,SAE5E,CAAA;AAED,eAAO,MAAM,mBAAmB,GAAI,GAAG,SAAS,GAAG,kBAAkB,KAAG,MAOvE,CAAA;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI,GAAG,MAAM,CAI3E;AAID,eAAO,MAAM,gBAAgB,GAC3B,QAAQ,WAAW,GAAG,IAAI,EAC1B,QAAQ;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAC,SASvD,CAAA;AAKD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,QAAQ,EACR,SAAS,EACT,eAAe,EACf,OAAO,EACP,SAAS,EACT,iBAAiB,GAClB,EAAE,mBAAmB,GAAG,kBAAkB,CA2M1C"}
@@ -44,7 +44,7 @@ const isCustomWidthOptions = width => {
44
44
  return width.default !== undefined;
45
45
  };
46
46
  const isPaneWidth = width => {
47
- return ['small', 'medium', 'large'].includes(width);
47
+ return width === 'small' || width === 'medium' || width === 'large';
48
48
  };
49
49
  const getDefaultPaneWidth = w => {
50
50
  if (isPaneWidth(w)) {
@@ -92,7 +92,8 @@ function usePaneWidth({
92
92
  resizable,
93
93
  widthStorageKey,
94
94
  paneRef,
95
- handleRef
95
+ handleRef,
96
+ contentWrapperRef
96
97
  }) {
97
98
  // Derive constraints from width configuration
98
99
  const isCustomWidth = isCustomWidthOptions(width);
@@ -145,7 +146,10 @@ function usePaneWidth({
145
146
  const getDefaultWidth = React.useCallback(() => getDefaultPaneWidth(width), [width]);
146
147
  const saveWidth = React.useCallback(value => {
147
148
  currentWidthRef.current = value;
148
- setCurrentWidth(value);
149
+ // Visual update already done via inline styles - React state sync is non-urgent
150
+ startTransition(() => {
151
+ setCurrentWidth(value);
152
+ });
149
153
  try {
150
154
  localStorage.setItem(widthStorageKey, value.toString());
151
155
  } catch {
@@ -161,24 +165,15 @@ function usePaneWidth({
161
165
  });
162
166
 
163
167
  // Update CSS variable, refs, and ARIA on mount and window resize.
164
- // Strategy:
165
- // 1. Throttled (16ms): Update --pane-max-width CSS variable for immediate visual clamp
166
- // 2. Debounced (150ms): Sync refs, ARIA, and React state when resize stops
168
+ // Strategy: Only sync when resize stops (debounced) to avoid layout thrashing on large DOMs
167
169
  useIsomorphicLayoutEffect(() => {
168
- var _paneRef$current4;
170
+ var _paneRef$current3;
169
171
  if (!resizable) return;
170
172
  let lastViewportWidth = window.innerWidth;
171
173
 
172
- // Quick CSS-only update for immediate visual feedback (throttled)
173
- const updateCSSOnly = () => {
174
- var _paneRef$current;
175
- const actualMax = getMaxPaneWidthRef.current();
176
- (_paneRef$current = paneRef.current) === null || _paneRef$current === void 0 ? void 0 : _paneRef$current.style.setProperty('--pane-max-width', `${actualMax}px`);
177
- };
178
-
179
174
  // Full sync of refs, ARIA, and state (debounced, runs when resize stops)
180
175
  const syncAll = () => {
181
- var _paneRef$current2;
176
+ var _paneRef$current;
182
177
  const currentViewportWidth = window.innerWidth;
183
178
 
184
179
  // Only call getComputedStyle if we crossed the breakpoint (expensive)
@@ -187,30 +182,30 @@ function usePaneWidth({
187
182
  if (crossedBreakpoint) {
188
183
  maxWidthDiffRef.current = getPaneMaxWidthDiff(paneRef.current);
189
184
  }
190
- const actualMax_0 = getMaxPaneWidthRef.current();
185
+ const actualMax = getMaxPaneWidthRef.current();
191
186
 
192
187
  // Update CSS variable for visual clamping (may already be set by throttled update)
193
- (_paneRef$current2 = paneRef.current) === null || _paneRef$current2 === void 0 ? void 0 : _paneRef$current2.style.setProperty('--pane-max-width', `${actualMax_0}px`);
188
+ (_paneRef$current = paneRef.current) === null || _paneRef$current === void 0 ? void 0 : _paneRef$current.style.setProperty('--pane-max-width', `${actualMax}px`);
194
189
 
195
190
  // Track if we clamped current width
196
- const wasClamped = currentWidthRef.current > actualMax_0;
191
+ const wasClamped = currentWidthRef.current > actualMax;
197
192
  if (wasClamped) {
198
- var _paneRef$current3;
199
- currentWidthRef.current = actualMax_0;
200
- (_paneRef$current3 = paneRef.current) === null || _paneRef$current3 === void 0 ? void 0 : _paneRef$current3.style.setProperty('--pane-width', `${actualMax_0}px`);
193
+ var _paneRef$current2;
194
+ currentWidthRef.current = actualMax;
195
+ (_paneRef$current2 = paneRef.current) === null || _paneRef$current2 === void 0 ? void 0 : _paneRef$current2.style.setProperty('--pane-width', `${actualMax}px`);
201
196
  }
202
197
 
203
198
  // Update ARIA via DOM - cheap, no React re-render
204
199
  updateAriaValues(handleRef.current, {
205
- max: actualMax_0,
200
+ max: actualMax,
206
201
  current: currentWidthRef.current
207
202
  });
208
203
 
209
204
  // Defer state updates so parent re-renders see accurate values
210
205
  startTransition(() => {
211
- setMaxPaneWidth(actualMax_0);
206
+ setMaxPaneWidth(actualMax);
212
207
  if (wasClamped) {
213
- setCurrentWidth(actualMax_0);
208
+ setCurrentWidth(actualMax);
214
209
  }
215
210
  });
216
211
  };
@@ -219,7 +214,7 @@ function usePaneWidth({
219
214
  maxWidthDiffRef.current = getPaneMaxWidthDiff(paneRef.current);
220
215
  const initialMax = getMaxPaneWidthRef.current();
221
216
  setMaxPaneWidth(initialMax);
222
- (_paneRef$current4 = paneRef.current) === null || _paneRef$current4 === void 0 ? void 0 : _paneRef$current4.style.setProperty('--pane-max-width', `${initialMax}px`);
217
+ (_paneRef$current3 = paneRef.current) === null || _paneRef$current3 === void 0 ? void 0 : _paneRef$current3.style.setProperty('--pane-max-width', `${initialMax}px`);
223
218
  updateAriaValues(handleRef.current, {
224
219
  min: minPaneWidth,
225
220
  max: initialMax,
@@ -229,35 +224,51 @@ function usePaneWidth({
229
224
  // For custom widths, max is fixed - no need to listen to resize
230
225
  if (customMaxWidth !== null) return;
231
226
 
232
- // Throttle CSS updates (16ms 60fps), debounce full sync (150ms)
233
- const THROTTLE_MS = 16;
234
- const DEBOUNCE_MS = 150;
227
+ // Throttle approach for window resize - provides immediate visual feedback for small DOMs
228
+ // while still limiting update frequency
229
+ const THROTTLE_MS = 16; // ~60fps
230
+ const DEBOUNCE_MS = 150; // Delay before removing containment after resize stops
231
+ let lastUpdateTime = 0;
232
+ let pendingUpdate = false;
235
233
  let rafId = null;
236
234
  let debounceId = null;
237
- let lastThrottleTime = 0;
235
+ let isResizing = false;
236
+ const startResizeOptimizations = () => {
237
+ var _paneRef$current4, _contentWrapperRef$cu;
238
+ if (isResizing) return;
239
+ isResizing = true;
240
+ (_paneRef$current4 = paneRef.current) === null || _paneRef$current4 === void 0 ? void 0 : _paneRef$current4.setAttribute('data-dragging', 'true');
241
+ (_contentWrapperRef$cu = contentWrapperRef.current) === null || _contentWrapperRef$cu === void 0 ? void 0 : _contentWrapperRef$cu.setAttribute('data-dragging', 'true');
242
+ };
243
+ const endResizeOptimizations = () => {
244
+ var _paneRef$current5, _contentWrapperRef$cu2;
245
+ if (!isResizing) return;
246
+ isResizing = false;
247
+ (_paneRef$current5 = paneRef.current) === null || _paneRef$current5 === void 0 ? void 0 : _paneRef$current5.removeAttribute('data-dragging');
248
+ (_contentWrapperRef$cu2 = contentWrapperRef.current) === null || _contentWrapperRef$cu2 === void 0 ? void 0 : _contentWrapperRef$cu2.removeAttribute('data-dragging');
249
+ };
238
250
  const handleResize = () => {
251
+ // Apply containment on first resize event (stays applied until resize stops)
252
+ startResizeOptimizations();
239
253
  const now = Date.now();
240
-
241
- // Throttled CSS update for immediate visual feedback
242
- if (now - lastThrottleTime >= THROTTLE_MS) {
243
- lastThrottleTime = now;
244
- updateCSSOnly();
245
- } else if (rafId === null) {
246
- // Schedule next frame if we're within throttle window
254
+ if (now - lastUpdateTime >= THROTTLE_MS) {
255
+ lastUpdateTime = now;
256
+ syncAll();
257
+ } else if (!pendingUpdate) {
258
+ pendingUpdate = true;
247
259
  rafId = requestAnimationFrame(() => {
260
+ pendingUpdate = false;
248
261
  rafId = null;
249
- lastThrottleTime = Date.now();
250
- updateCSSOnly();
262
+ lastUpdateTime = Date.now();
263
+ syncAll();
251
264
  });
252
265
  }
253
266
 
254
- // Debounced full sync (refs, ARIA, state) when resize stops
255
- if (debounceId !== null) {
256
- clearTimeout(debounceId);
257
- }
267
+ // Debounce the cleanup remove containment after resize stops
268
+ if (debounceId !== null) clearTimeout(debounceId);
258
269
  debounceId = setTimeout(() => {
259
270
  debounceId = null;
260
- syncAll();
271
+ endResizeOptimizations();
261
272
  }, DEBOUNCE_MS);
262
273
  };
263
274
 
@@ -266,9 +277,10 @@ function usePaneWidth({
266
277
  return () => {
267
278
  if (rafId !== null) cancelAnimationFrame(rafId);
268
279
  if (debounceId !== null) clearTimeout(debounceId);
280
+ endResizeOptimizations();
269
281
  window.removeEventListener('resize', handleResize);
270
282
  };
271
- }, [resizable, customMaxWidth, minPaneWidth, paneRef, handleRef]);
283
+ }, [resizable, customMaxWidth, minPaneWidth, paneRef, handleRef, contentWrapperRef]);
272
284
  return {
273
285
  currentWidth,
274
286
  currentWidthRef,
@@ -50,6 +50,11 @@ interface SelectPanelBaseProps {
50
50
  */
51
51
  disableFullscreenOnNarrow?: boolean;
52
52
  showSelectAll?: boolean;
53
+ /**
54
+ * Set to true to allow focus to move to elements that are dynamically prepended to the container.
55
+ * Default is false.
56
+ */
57
+ focusPrependedElements?: boolean;
53
58
  }
54
59
  type SelectPanelVariantProps = {
55
60
  variant?: 'anchored';
@@ -59,7 +64,7 @@ type SelectPanelVariantProps = {
59
64
  onCancel: () => void;
60
65
  };
61
66
  export type SelectPanelProps = SelectPanelBaseProps & Omit<FilteredActionListProps, 'selectionVariant' | 'variant' | 'message'> & Pick<AnchoredOverlayProps, 'open' | 'height' | 'width' | 'align'> & AnchoredOverlayWrapperAnchorProps & (SelectPanelSingleSelection | SelectPanelMultiSelection) & SelectPanelVariantProps;
62
- declare function Panel({ open, onOpenChange, renderAnchor, anchorRef: externalAnchorRef, placeholder, placeholderText, inputLabel, selected, title, subtitle, onSelectedChange, filterValue: externalFilterValue, onFilterChange: externalOnFilterChange, items, footer, textInputProps, overlayProps, loading, initialLoadingType, className, height, width, id, message, notice, onCancel, variant, secondaryAction, showSelectedOptionsFirst, disableFullscreenOnNarrow, align, showSelectAll, ...listProps }: SelectPanelProps): JSX.Element;
67
+ declare function Panel({ open, onOpenChange, renderAnchor, anchorRef: externalAnchorRef, placeholder, placeholderText, inputLabel, selected, title, subtitle, onSelectedChange, filterValue: externalFilterValue, onFilterChange: externalOnFilterChange, items, footer, textInputProps, overlayProps, loading, initialLoadingType, className, height, width, id, message, notice, onCancel, variant, secondaryAction, showSelectedOptionsFirst, disableFullscreenOnNarrow, align, showSelectAll, focusPrependedElements, ...listProps }: SelectPanelProps): JSX.Element;
63
68
  declare const SecondaryButton: React.FC<ButtonProps>;
64
69
  declare const SecondaryLink: React.FC<LinkButtonProps & ButtonProps>;
65
70
  export declare const SelectPanel: typeof Panel & {
@@ -1 +1 @@
1
- {"version":3,"file":"SelectPanel.d.ts","sourceRoot":"","sources":["../../src/SelectPanel/SelectPanel.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"SelectPanel.d.ts","sourceRoot":"","sources":["../../src/SelectPanel/SelectPanel.tsx"],"names":[],"mappings":"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"}
@@ -107,6 +107,7 @@ function Panel({
107
107
  disableFullscreenOnNarrow,
108
108
  align,
109
109
  showSelectAll = false,
110
+ focusPrependedElements,
110
111
  ...listProps
111
112
  }) {
112
113
  var _listProps$groupMetad;
@@ -679,7 +680,8 @@ function Panel({
679
680
  description: typeof (message === null || message === void 0 ? void 0 : message.body) === 'string' ? message.body : EMPTY_MESSAGE.description
680
681
  },
681
682
  fullScreenOnNarrow: usingFullScreenOnNarrow,
682
- className: clsx(className, classes.FilteredActionList)
683
+ className: clsx(className, classes.FilteredActionList),
684
+ focusPrependedElements: focusPrependedElements
683
685
  }), footer ? /*#__PURE__*/jsx("div", {
684
686
  className: classes.Footer,
685
687
  children: footer
@@ -0,0 +1,2 @@
1
+ .prc-TextInput-CharacterCounter-vJZBe{align-items:center;color:var(--fgColor-muted,var(--color-fg-muted));display:flex;gap:var(--control-xsmall-gap,.25rem)}.prc-TextInput-CharacterCounter--error-HA0kN{color:var(--fgColor-danger,var(--color-danger-fg))}
2
+ /*# sourceMappingURL=TextInput-dff8f842.css.map */
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/TextInput/TextInput.module.css.js"],"names":[],"mappings":"AAAA,sCAEE,kBAAmB,CAEnB,gDAA2B,CAH3B,YAAa,CAEb,oCAEF,CAEA,6CACE,kDACF","file":"TextInput-dff8f842.css","sourcesContent":[".CharacterCounter {\n display: flex;\n align-items: center;\n gap: var(--control-xsmall-gap);\n color: var(--fgColor-muted);\n}\n\n.CharacterCounter--error {\n color: var(--fgColor-danger);\n}\n"]}
@@ -28,6 +28,11 @@ export type TextInputNonPassthroughProps = {
28
28
  * A visual that renders inside the input after the typing area
29
29
  */
30
30
  trailingAction?: React.ReactElement<React.HTMLProps<HTMLButtonElement>>;
31
+ /**
32
+ * Optional character limit for the input. If provided, a character counter will be displayed below the input.
33
+ * When the limit is exceeded, validation styling will be applied.
34
+ */
35
+ characterLimit?: number;
31
36
  } & Partial<Pick<StyledWrapperProps, 'block' | 'contrast' | 'disabled' | 'monospace' | 'width' | 'maxWidth' | 'minWidth' | 'variant' | 'size' | 'validationStatus'>>;
32
37
  export type TextInputProps = Merge<React.ComponentPropsWithoutRef<'input'>, TextInputNonPassthroughProps>;
33
38
  declare const _default: PolymorphicForwardRefComponent<"input", TextInputProps> & {
@@ -1 +1 @@
1
- {"version":3,"file":"TextInput.d.ts","sourceRoot":"","sources":["../../src/TextInput/TextInput.tsx"],"names":[],"mappings":"AACA,OAAO,KAAqC,MAAM,OAAO,CAAA;AAEzD,OAAO,KAAK,EAAC,mBAAmB,IAAI,8BAA8B,EAAC,MAAM,sBAAsB,CAAA;AAK/F,OAAO,KAAK,EAAC,KAAK,EAAC,MAAM,gBAAgB,CAAA;AACzC,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,yCAAyC,CAAA;AAM/E,MAAM,MAAM,4BAA4B,GAAG;IACzC,uEAAuE;IACvE,IAAI,CAAC,EAAE,KAAK,CAAC,WAAW,CAAA;IACxB,2DAA2D;IAC3D,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;;;;QAKI;IACJ,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,UAAU,CAAA;IAChD,0DAA0D;IAC1D,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;OAEG;IACH,aAAa,CAAC,EAAE,KAAK,CAAC,WAAW,GAAG,KAAK,CAAC,SAAS,CAAA;IACnD;;OAEG;IACH,cAAc,CAAC,EAAE,KAAK,CAAC,WAAW,GAAG,KAAK,CAAC,SAAS,CAAA;IACpD;;OAEG;IACH,cAAc,CAAC,EAAE,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC,CAAA;CACxE,GAAG,OAAO,CACT,IAAI,CACF,kBAAkB,EAChB,OAAO,GACP,UAAU,GACV,UAAU,GACV,WAAW,GACX,OAAO,GACP,UAAU,GACV,UAAU,GACV,SAAS,GACT,MAAM,GACN,kBAAkB,CACrB,CACF,CAAA;AAED,MAAM,MAAM,cAAc,GAAG,KAAK,CAAC,KAAK,CAAC,wBAAwB,CAAC,OAAO,CAAC,EAAE,4BAA4B,CAAC,CAAA;;;;;;;;;;;AA+IzG,wBAGE"}
1
+ {"version":3,"file":"TextInput.d.ts","sourceRoot":"","sources":["../../src/TextInput/TextInput.tsx"],"names":[],"mappings":"AACA,OAAO,KAAwD,MAAM,OAAO,CAAA;AAE5E,OAAO,KAAK,EAAC,mBAAmB,IAAI,8BAA8B,EAAC,MAAM,sBAAsB,CAAA;AAO/F,OAAO,KAAK,EAAC,KAAK,EAAC,MAAM,gBAAgB,CAAA;AACzC,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,yCAAyC,CAAA;AAQ/E,MAAM,MAAM,4BAA4B,GAAG;IACzC,uEAAuE;IACvE,IAAI,CAAC,EAAE,KAAK,CAAC,WAAW,CAAA;IACxB,2DAA2D;IAC3D,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;;;;QAKI;IACJ,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,UAAU,CAAA;IAChD,0DAA0D;IAC1D,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;OAEG;IACH,aAAa,CAAC,EAAE,KAAK,CAAC,WAAW,GAAG,KAAK,CAAC,SAAS,CAAA;IACnD;;OAEG;IACH,cAAc,CAAC,EAAE,KAAK,CAAC,WAAW,GAAG,KAAK,CAAC,SAAS,CAAA;IACpD;;OAEG;IACH,cAAc,CAAC,EAAE,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC,CAAA;IACvE;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB,GAAG,OAAO,CACT,IAAI,CACF,kBAAkB,EAChB,OAAO,GACP,UAAU,GACV,UAAU,GACV,WAAW,GACX,OAAO,GACP,UAAU,GACV,UAAU,GACV,SAAS,GACT,MAAM,GACN,kBAAkB,CACrB,CACF,CAAA;AAED,MAAM,MAAM,cAAc,GAAG,KAAK,CAAC,KAAK,CAAC,wBAAwB,CAAC,OAAO,CAAC,EAAE,4BAA4B,CAAC,CAAA;;;;;;;;;;;AAiOzG,wBAGE"}
@@ -1,13 +1,17 @@
1
- import React, { useState, useId, useCallback } from 'react';
1
+ import React, { useState, useRef, useId, useCallback, useEffect } from 'react';
2
2
  import { isValidElementType } from 'react-is';
3
3
  import { clsx } from 'clsx';
4
+ import { AlertFillIcon } from '@primer/octicons-react';
5
+ import classes from './TextInput.module.css.js';
4
6
  import TextInputInnerVisualSlot from '../internal/components/TextInputInnerVisualSlot.js';
5
7
  import { TextInputWrapper } from '../internal/components/TextInputWrapper.js';
6
8
  import TextInputAction from '../internal/components/TextInputInnerAction.js';
7
9
  import UnstyledTextInput from '../internal/components/UnstyledTextInput.js';
8
10
  import VisuallyHidden from '../_VisuallyHidden.js';
9
- import { jsxs, jsx } from 'react/jsx-runtime';
11
+ import { CharacterCounter } from '../utils/character-counter.js';
12
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
10
13
  import { useProvidedRefOrCreate } from '../hooks/useProvidedRefOrCreate.js';
14
+ import Text from '../Text/Text.js';
11
15
 
12
16
  // using forwardRef is important so that other components can autofocus the input
13
17
  const TextInput = /*#__PURE__*/React.forwardRef(({
@@ -35,10 +39,19 @@ const TextInput = /*#__PURE__*/React.forwardRef(({
35
39
  // end deprecated props
36
40
  type = 'text',
37
41
  required,
42
+ characterLimit,
43
+ onChange,
44
+ value,
45
+ defaultValue,
38
46
  ...inputProps
39
47
  }, ref) => {
40
48
  const [isInputFocused, setIsInputFocused] = useState(false);
41
49
  const inputRef = useProvidedRefOrCreate(ref);
50
+ const [characterCount, setCharacterCount] = useState('');
51
+ const [isOverLimit, setIsOverLimit] = useState(false);
52
+ const [screenReaderMessage, setScreenReaderMessage] = useState('');
53
+ const characterCounterRef = useRef(null);
54
+
42
55
  // this class is necessary to style FilterSearch, plz no touchy!
43
56
  const wrapperClasses = clsx(className, 'TextInput-wrapper');
44
57
  const showLeadingLoadingIndicator = loading && (loaderPosition === 'leading' || Boolean(LeadingVisual && loaderPosition !== 'trailing'));
@@ -65,56 +78,117 @@ const TextInput = /*#__PURE__*/React.forwardRef(({
65
78
  setIsInputFocused(false);
66
79
  onBlur && onBlur(e_1);
67
80
  }, [onBlur]);
68
- return /*#__PURE__*/jsxs(TextInputWrapper, {
69
- block: block,
70
- className: wrapperClasses,
71
- validationStatus: validationStatus,
72
- contrast: contrast,
73
- disabled: disabled,
74
- monospace: monospace,
75
- size: sizeProp,
76
- width: widthProp,
77
- minWidth: minWidthProp,
78
- maxWidth: maxWidthProp,
79
- variant: variantProp,
80
- hasLeadingVisual: Boolean(LeadingVisual || showLeadingLoadingIndicator),
81
- hasTrailingVisual: Boolean(TrailingVisual || showTrailingLoadingIndicator),
82
- hasTrailingAction: Boolean(trailingAction),
83
- isInputFocused: isInputFocused,
84
- onClick: focusInput,
85
- "aria-busy": Boolean(loading),
86
- children: [IconComponent && /*#__PURE__*/jsx(IconComponent, {
87
- className: "TextInput-icon"
88
- }), /*#__PURE__*/jsx(TextInputInnerVisualSlot, {
89
- visualPosition: "leading",
90
- showLoadingIndicator: showLeadingLoadingIndicator,
91
- hasLoadingIndicator: typeof loading === 'boolean',
92
- id: leadingVisualId,
93
- children: typeof LeadingVisual !== 'string' && isValidElementType(LeadingVisual) ? /*#__PURE__*/jsx(LeadingVisual, {}) : LeadingVisual
94
- }), /*#__PURE__*/jsx(UnstyledTextInput
95
- // @ts-expect-error it needs a non nullable ref
96
- , {
97
- ref: inputRef,
81
+
82
+ // Initialize character counter
83
+ useEffect(() => {
84
+ if (characterLimit) {
85
+ characterCounterRef.current = new CharacterCounter({
86
+ onCountUpdate: (count, overLimit, message) => {
87
+ setCharacterCount(message);
88
+ setIsOverLimit(overLimit);
89
+ },
90
+ onScreenReaderAnnounce: message_0 => {
91
+ setScreenReaderMessage(message_0);
92
+ }
93
+ });
94
+ return () => {
95
+ var _characterCounterRef$;
96
+ (_characterCounterRef$ = characterCounterRef.current) === null || _characterCounterRef$ === void 0 ? void 0 : _characterCounterRef$.cleanup();
97
+ characterCounterRef.current = null;
98
+ };
99
+ }
100
+ }, [characterLimit]);
101
+
102
+ // Update character count when value changes or on mount
103
+ useEffect(() => {
104
+ if (characterLimit && characterCounterRef.current) {
105
+ const currentValue = value !== undefined ? String(value) : defaultValue !== undefined ? String(defaultValue) : '';
106
+ characterCounterRef.current.updateCharacterCount(currentValue.length, characterLimit);
107
+ }
108
+ }, [value, defaultValue, characterLimit]);
109
+
110
+ // Handle input change with character counter
111
+ const handleInputChange = useCallback(e_2 => {
112
+ if (characterLimit && characterCounterRef.current) {
113
+ characterCounterRef.current.updateCharacterCount(e_2.target.value.length, characterLimit);
114
+ }
115
+ onChange === null || onChange === void 0 ? void 0 : onChange(e_2);
116
+ }, [onChange, characterLimit]);
117
+ const characterCountId = useId();
118
+ const characterCountStaticMessageId = useId();
119
+ const isValid = isOverLimit ? 'error' : validationStatus;
120
+ return /*#__PURE__*/jsxs(Fragment, {
121
+ children: [/*#__PURE__*/jsxs(TextInputWrapper, {
122
+ block: block,
123
+ className: wrapperClasses,
124
+ validationStatus: isValid,
125
+ contrast: contrast,
98
126
  disabled: disabled,
99
- onFocus: handleInputFocus,
100
- onBlur: handleInputBlur,
101
- type: type,
102
- "aria-required": required,
103
- "aria-invalid": validationStatus === 'error' ? 'true' : undefined,
104
- ...inputProps,
105
- "aria-describedby": inputDescribedBy,
106
- "data-component": "input"
107
- }), loading && /*#__PURE__*/jsx(VisuallyHidden, {
108
- id: loadingId,
109
- children: loaderText
110
- }), /*#__PURE__*/jsx(TextInputInnerVisualSlot, {
111
- visualPosition: "trailing",
112
- showLoadingIndicator: showTrailingLoadingIndicator,
113
- hasLoadingIndicator: typeof loading === 'boolean',
114
- id: trailingVisualId,
115
- "data-testid": "text-input-trailing-visual",
116
- children: typeof TrailingVisual !== 'string' && isValidElementType(TrailingVisual) ? /*#__PURE__*/jsx(TrailingVisual, {}) : TrailingVisual
117
- }), trailingAction]
127
+ monospace: monospace,
128
+ size: sizeProp,
129
+ width: widthProp,
130
+ minWidth: minWidthProp,
131
+ maxWidth: maxWidthProp,
132
+ variant: variantProp,
133
+ hasLeadingVisual: Boolean(LeadingVisual || showLeadingLoadingIndicator),
134
+ hasTrailingVisual: Boolean(TrailingVisual || showTrailingLoadingIndicator),
135
+ hasTrailingAction: Boolean(trailingAction),
136
+ isInputFocused: isInputFocused,
137
+ onClick: focusInput,
138
+ "aria-busy": Boolean(loading),
139
+ children: [IconComponent && /*#__PURE__*/jsx(IconComponent, {
140
+ className: "TextInput-icon"
141
+ }), /*#__PURE__*/jsx(TextInputInnerVisualSlot, {
142
+ visualPosition: "leading",
143
+ showLoadingIndicator: showLeadingLoadingIndicator,
144
+ hasLoadingIndicator: typeof loading === 'boolean',
145
+ id: leadingVisualId,
146
+ children: typeof LeadingVisual !== 'string' && isValidElementType(LeadingVisual) ? /*#__PURE__*/jsx(LeadingVisual, {}) : LeadingVisual
147
+ }), /*#__PURE__*/jsx(UnstyledTextInput
148
+ // @ts-expect-error it needs a non nullable ref
149
+ , {
150
+ ref: inputRef,
151
+ disabled: disabled,
152
+ onFocus: handleInputFocus,
153
+ onBlur: handleInputBlur,
154
+ onChange: handleInputChange,
155
+ type: type,
156
+ "aria-required": required,
157
+ "aria-invalid": isValid === 'error' ? 'true' : undefined,
158
+ value: value,
159
+ defaultValue: defaultValue,
160
+ ...inputProps,
161
+ "aria-describedby": characterLimit ? [characterCountStaticMessageId, inputDescribedBy].filter(Boolean).join(' ') || undefined : inputDescribedBy,
162
+ "data-component": "input"
163
+ }), loading && /*#__PURE__*/jsx(VisuallyHidden, {
164
+ id: loadingId,
165
+ children: loaderText
166
+ }), /*#__PURE__*/jsx(TextInputInnerVisualSlot, {
167
+ visualPosition: "trailing",
168
+ showLoadingIndicator: showTrailingLoadingIndicator,
169
+ hasLoadingIndicator: typeof loading === 'boolean',
170
+ id: trailingVisualId,
171
+ "data-testid": "text-input-trailing-visual",
172
+ children: typeof TrailingVisual !== 'string' && isValidElementType(TrailingVisual) ? /*#__PURE__*/jsx(TrailingVisual, {}) : TrailingVisual
173
+ }), trailingAction]
174
+ }), characterLimit && /*#__PURE__*/jsxs(Fragment, {
175
+ children: [/*#__PURE__*/jsx(VisuallyHidden, {
176
+ "aria-live": "polite",
177
+ role: "status",
178
+ children: screenReaderMessage
179
+ }), /*#__PURE__*/jsxs(VisuallyHidden, {
180
+ id: characterCountStaticMessageId,
181
+ children: ["You can enter up to ", characterLimit, " ", characterLimit === 1 ? 'character' : 'characters']
182
+ }), /*#__PURE__*/jsxs(Text, {
183
+ "aria-hidden": "true",
184
+ id: characterCountId,
185
+ size: "small",
186
+ className: clsx(classes.CharacterCounter, isOverLimit && classes['CharacterCounter--error']),
187
+ children: [isOverLimit && /*#__PURE__*/jsx(AlertFillIcon, {
188
+ size: 16
189
+ }), characterCount]
190
+ })]
191
+ })]
118
192
  });
119
193
  });
120
194
  TextInput.displayName = 'TextInput';
@@ -0,0 +1,5 @@
1
+ import './TextInput-dff8f842.css';
2
+
3
+ var classes = {"CharacterCounter":"prc-TextInput-CharacterCounter-vJZBe","CharacterCounter--error":"prc-TextInput-CharacterCounter--error-HA0kN"};
4
+
5
+ export { classes as default };
@@ -1,2 +1,2 @@
1
- .prc-Textarea-TextArea-snlco{appearance:none;background-color:transparent;border:0;color:inherit;font-family:inherit;font-size:inherit;resize:both;width:100%}.prc-Textarea-TextArea-snlco:focus{outline:0}.prc-Textarea-TextArea-snlco[data-resize=none]{resize:none}.prc-Textarea-TextArea-snlco[data-resize=both]{resize:both}.prc-Textarea-TextArea-snlco[data-resize=horizontal]{resize:horizontal}.prc-Textarea-TextArea-snlco[data-resize=vertical]{resize:vertical}.prc-Textarea-TextArea-snlco:disabled{resize:none}
2
- /*# sourceMappingURL=TextArea-54099020.css.map */
1
+ .prc-Textarea-TextArea-snlco{appearance:none;background-color:transparent;border:0;color:inherit;font-family:inherit;font-size:inherit;resize:both;width:100%}.prc-Textarea-TextArea-snlco:focus{outline:0}.prc-Textarea-TextArea-snlco[data-resize=none]{resize:none}.prc-Textarea-TextArea-snlco[data-resize=both]{resize:both}.prc-Textarea-TextArea-snlco[data-resize=horizontal]{resize:horizontal}.prc-Textarea-TextArea-snlco[data-resize=vertical]{resize:vertical}.prc-Textarea-TextArea-snlco:disabled{resize:none}.prc-Textarea-CharacterCounter-qnOXd{align-items:center;color:var(--fgColor-muted,var(--color-fg-muted));display:flex;gap:var(--control-xsmall-gap,.25rem)}.prc-Textarea-CharacterCounter--error-eewrs{color:var(--fgColor-danger,var(--color-danger-fg))}
2
+ /*# sourceMappingURL=TextArea-53e27580.css.map */
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/Textarea/TextArea.module.css.js"],"names":[],"mappings":"AAAA,6BAQE,eAAgB,CAFhB,4BAA6B,CAC7B,QAAS,CAHT,aAAc,CAFd,mBAAoB,CACpB,iBAAkB,CAElB,WAAY,CAJZ,UAQF,CAEA,mCACE,SACF,CAEA,+CACE,WACF,CAEA,+CACE,WACF,CAEA,qDACE,iBACF,CAEA,mDACE,eACF,CAEA,sCACE,WACF,CAEA,qCAEE,kBAAmB,CAEnB,gDAA2B,CAH3B,YAAa,CAEb,oCAEF,CAEA,4CACE,kDACF","file":"TextArea-53e27580.css","sourcesContent":[".TextArea {\n width: 100%;\n font-family: inherit;\n font-size: inherit;\n color: inherit;\n resize: both;\n background-color: transparent;\n border: 0;\n appearance: none;\n}\n\n.TextArea:focus {\n outline: 0;\n}\n\n.TextArea[data-resize='none'] {\n resize: none;\n}\n\n.TextArea[data-resize='both'] {\n resize: both;\n}\n\n.TextArea[data-resize='horizontal'] {\n resize: horizontal;\n}\n\n.TextArea[data-resize='vertical'] {\n resize: vertical;\n}\n\n.TextArea:disabled {\n resize: none;\n}\n\n.CharacterCounter {\n display: flex;\n align-items: center;\n gap: var(--control-xsmall-gap);\n color: var(--fgColor-muted);\n}\n\n.CharacterCounter--error {\n color: var(--fgColor-danger);\n}\n"]}
@@ -1,5 +1,5 @@
1
- import './TextArea-54099020.css';
1
+ import './TextArea-53e27580.css';
2
2
 
3
- var classes = {"TextArea":"prc-Textarea-TextArea-snlco"};
3
+ var classes = {"TextArea":"prc-Textarea-TextArea-snlco","CharacterCounter":"prc-Textarea-CharacterCounter-qnOXd","CharacterCounter--error":"prc-Textarea-CharacterCounter--error-eewrs"};
4
4
 
5
5
  export { classes as default };
@@ -42,6 +42,11 @@ export type TextareaProps = {
42
42
  * CSS styles to apply to the Textarea
43
43
  */
44
44
  style?: React.CSSProperties;
45
+ /**
46
+ * Optional character limit for the textarea. If provided, a character counter will be displayed below the textarea.
47
+ * When the limit is exceeded, validation styling will be applied.
48
+ */
49
+ characterLimit?: number;
45
50
  } & TextareaHTMLAttributes<HTMLTextAreaElement>;
46
51
  /**
47
52
  * An accessible, native textarea component that supports validation states.
@@ -84,6 +89,11 @@ declare const Textarea: React.ForwardRefExoticComponent<{
84
89
  * CSS styles to apply to the Textarea
85
90
  */
86
91
  style?: React.CSSProperties;
92
+ /**
93
+ * Optional character limit for the textarea. If provided, a character counter will be displayed below the textarea.
94
+ * When the limit is exceeded, validation styling will be applied.
95
+ */
96
+ characterLimit?: number;
87
97
  } & TextareaHTMLAttributes<HTMLTextAreaElement> & React.RefAttributes<HTMLTextAreaElement>>;
88
98
  declare const _default: WithSlotMarker<typeof Textarea>;
89
99
  export default _default;