@povio/ui 2.2.9-rc.31 → 2.2.9-rc.33

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.
@@ -11,10 +11,11 @@ import { getStaticCalendarDateSegments } from "../shared/staticDateTimeSegments.
11
11
  import { FormField } from "../../FormField/FormField.js";
12
12
  import { TooltipWrapper } from "../../shared/TooltipWrapper.js";
13
13
  import { InputFrame } from "../../Skeleton/InputFrame.js";
14
+ import { useStaticInputHandoff } from "../../shared/useStaticInputHandoff.js";
14
15
  import { useDebounceCallback } from "../../../../hooks/useDebounceCallback.js";
15
16
  import { jsx, jsxs } from "react/jsx-runtime";
16
17
  import { clsx } from "clsx";
17
- import { useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
18
+ import { useImperativeHandle, useMemo, useRef, useState } from "react";
18
19
  import { useDatePicker, useLocale } from "react-aria";
19
20
  import { mergeRefs } from "@react-aria/utils";
20
21
  import { Controller, useWatch } from "react-hook-form";
@@ -176,8 +177,15 @@ var DatePicker = ({ fullIso = true, minValue, maxValue, renderStaticInput, ...pr
176
177
  const { locale } = useLocale();
177
178
  const shouldForceLeadingZeros = props.shouldForceLeadingZeros ?? ui.dateInput.shouldForceLeadingZeros;
178
179
  const [renderInput, setRenderInput] = useState(!(renderStaticInput ?? ui.renderStaticInput));
179
- const [shouldFocus, setShouldFocus] = useState(false);
180
180
  const inputRef = useRef(null);
181
+ const { renderRealInput } = useStaticInputHandoff({
182
+ inputRef,
183
+ renderInput,
184
+ setRenderInput,
185
+ getFocusTarget: (input) => {
186
+ return (input.getContainer?.() ?? input).querySelector("input, button, [tabindex]:not([tabindex='-1'])");
187
+ }
188
+ });
181
189
  const watchedValue = "formControl" in props && props.formControl ? useWatch({
182
190
  control: props.formControl.control,
183
191
  name: props.formControl.name
@@ -201,13 +209,6 @@ var DatePicker = ({ fullIso = true, minValue, maxValue, renderStaticInput, ...pr
201
209
  minValue: typeof minValue === "string" ? parseCalendarDate(minValue) : minValue,
202
210
  maxValue: typeof maxValue === "string" ? parseCalendarDate(maxValue) : maxValue
203
211
  };
204
- useEffect(() => {
205
- if (!renderInput || !shouldFocus) return;
206
- requestAnimationFrame(() => {
207
- ((inputRef.current?.getContainer?.() ?? inputRef.current)?.querySelector("input, button, [tabindex]:not([tabindex='-1'])"))?.focus();
208
- });
209
- setShouldFocus(false);
210
- }, [renderInput, shouldFocus]);
211
212
  if (!renderInput) {
212
213
  const dateValue = rawValue ? parseCalendarDate(rawValue) : null;
213
214
  const as = props.as ?? ui.input.as;
@@ -215,6 +216,8 @@ var DatePicker = ({ fullIso = true, minValue, maxValue, renderStaticInput, ...pr
215
216
  const isDisabled = isFormControlDisabled || !!props.isDisabled;
216
217
  const todayIcon = props.todayIcon ?? ui.dateInput.todayIcon;
217
218
  const todayIconButtonSize = props.todayIconButtonSize ?? ui.dateInput.todayIconButtonSize;
219
+ const todayIconPlacement = props.todayIconPlacement ?? ui.dateInput.todayIconPlacement;
220
+ const staticTodayIcon = renderDatePickerTodayIcon(todayIcon, todayIconButtonSize);
218
221
  const disableManualEntry = props.disableManualEntry ?? ui.dateInput.disableManualEntry;
219
222
  const showDropdown = !props.disableDropdown || disableManualEntry;
220
223
  const staticSegments = getStaticCalendarDateSegments({
@@ -243,11 +246,9 @@ var DatePicker = ({ fullIso = true, minValue, maxValue, renderStaticInput, ...pr
243
246
  dataHasSelection: dateValue !== null
244
247
  },
245
248
  renderStatic: true,
246
- onStaticInteract: (focus) => {
247
- setShouldFocus(focus);
248
- setRenderInput(true);
249
- },
250
- actionContent: renderDatePickerTodayIcon(todayIcon, todayIconButtonSize),
249
+ onStaticInteract: renderRealInput,
250
+ actionContent: staticTodayIcon,
251
+ actionContentPlacement: todayIconPlacement === "fieldLabel" ? "content-row" : "content-wrapper",
251
252
  trailingContent: showDropdown ? ui.dateInput.calendarIcon : void 0,
252
253
  children: staticSegments
253
254
  });
@@ -12,11 +12,12 @@ import { getStaticDateRangeSegments } from "../shared/staticDateTimeSegments.js"
12
12
  import { FormField } from "../../FormField/FormField.js";
13
13
  import { TooltipWrapper } from "../../shared/TooltipWrapper.js";
14
14
  import { InputFrame } from "../../Skeleton/InputFrame.js";
15
+ import { useStaticInputHandoff } from "../../shared/useStaticInputHandoff.js";
15
16
  import { useDebounceCallback } from "../../../../hooks/useDebounceCallback.js";
16
17
  import { RangeCalendar } from "../shared/RangeCalendar.js";
17
18
  import { jsx, jsxs } from "react/jsx-runtime";
18
19
  import { clsx } from "clsx";
19
- import { useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
20
+ import { useCallback, useImperativeHandle, useMemo, useRef, useState } from "react";
20
21
  import { Button } from "react-aria-components";
21
22
  import { useDateRangePicker, useLocale as useLocale$1 } from "react-aria";
22
23
  import { mergeRefs } from "@react-aria/utils";
@@ -552,8 +553,15 @@ var DateRangePicker = ({ fullIso = true, minValue, maxValue, renderStaticInput,
552
553
  const { locale } = useLocale$1();
553
554
  const shouldForceLeadingZeros = props.shouldForceLeadingZeros ?? ui.dateInput.shouldForceLeadingZeros;
554
555
  const [renderInput, setRenderInput] = useState(!(renderStaticInput ?? ui.renderStaticInput));
555
- const [shouldFocus, setShouldFocus] = useState(false);
556
556
  const inputRef = useRef(null);
557
+ const { renderRealInput } = useStaticInputHandoff({
558
+ inputRef,
559
+ renderInput,
560
+ setRenderInput,
561
+ getFocusTarget: (input) => {
562
+ return (input.getContainer?.() ?? input).querySelector("input, button, [tabindex]:not([tabindex='-1'])");
563
+ }
564
+ });
557
565
  const watchedValue = "formControl" in props && props.formControl ? useWatch({
558
566
  control: props.formControl.control,
559
567
  name: props.formControl.name
@@ -599,13 +607,6 @@ var DateRangePicker = ({ fullIso = true, minValue, maxValue, renderStaticInput,
599
607
  minValue: typeof minValue === "string" ? parseCalendarDate(minValue) : minValue,
600
608
  maxValue: typeof maxValue === "string" ? parseCalendarDate(maxValue) : maxValue
601
609
  };
602
- useEffect(() => {
603
- if (!renderInput || !shouldFocus) return;
604
- requestAnimationFrame(() => {
605
- ((inputRef.current?.getContainer?.() ?? inputRef.current)?.querySelector("input, button, [tabindex]:not([tabindex='-1'])"))?.focus();
606
- });
607
- setShouldFocus(false);
608
- }, [renderInput, shouldFocus]);
609
610
  if (!renderInput) {
610
611
  const startDateValue = parseCalendarDate(rawValue?.start ?? null);
611
612
  const endDateValue = parseCalendarDate(rawValue?.end ?? null);
@@ -615,6 +616,8 @@ var DateRangePicker = ({ fullIso = true, minValue, maxValue, renderStaticInput,
615
616
  const isDisabled = isFormControlDisabled || !!props.isDisabled;
616
617
  const todayIcon = props.todayIcon ?? ui.dateInput.todayIcon;
617
618
  const todayIconButtonSize = props.todayIconButtonSize ?? ui.dateInput.todayIconButtonSize;
619
+ const todayIconPlacement = props.todayIconPlacement ?? ui.dateInput.todayIconPlacement;
620
+ const staticTodayIcon = renderDatePickerTodayIcon(todayIcon, todayIconButtonSize);
618
621
  const disableManualEntry = props.disableManualEntry ?? ui.dateInput.disableManualEntry;
619
622
  const showDropdown = !props.disableDropdown || disableManualEntry;
620
623
  const staticSegments = getStaticDateRangeSegments({
@@ -644,11 +647,9 @@ var DateRangePicker = ({ fullIso = true, minValue, maxValue, renderStaticInput,
644
647
  dataHasSelection: hasProvidedRangeValue
645
648
  },
646
649
  renderStatic: true,
647
- onStaticInteract: (focus) => {
648
- setShouldFocus(focus);
649
- setRenderInput(true);
650
- },
651
- actionContent: renderDatePickerTodayIcon(todayIcon, todayIconButtonSize),
650
+ onStaticInteract: renderRealInput,
651
+ actionContent: staticTodayIcon,
652
+ actionContentPlacement: todayIconPlacement === "fieldLabel" ? "content-row" : "content-wrapper",
652
653
  trailingContent: showDropdown ? ui.dateInput.calendarIcon : void 0,
653
654
  children: staticSegments
654
655
  });
@@ -11,6 +11,7 @@ import { getStaticCalendarDateTimeSegments } from "../shared/staticDateTimeSegme
11
11
  import { FormField } from "../../FormField/FormField.js";
12
12
  import { TooltipWrapper } from "../../shared/TooltipWrapper.js";
13
13
  import { InputFrame } from "../../Skeleton/InputFrame.js";
14
+ import { useStaticInputHandoff } from "../../shared/useStaticInputHandoff.js";
14
15
  import { useDebounceCallback } from "../../../../hooks/useDebounceCallback.js";
15
16
  import { jsx, jsxs } from "react/jsx-runtime";
16
17
  import { clsx } from "clsx";
@@ -197,8 +198,15 @@ var DateTimePicker = ({ fullIso = true, renderStaticInput, ...props }) => {
197
198
  const { locale } = useLocale();
198
199
  const shouldForceLeadingZeros = props.shouldForceLeadingZeros ?? ui.dateInput.shouldForceLeadingZeros;
199
200
  const [renderInput, setRenderInput] = useState(!(renderStaticInput ?? ui.renderStaticInput));
200
- const [shouldFocus, setShouldFocus] = useState(false);
201
201
  const inputRef = useRef(null);
202
+ const { renderRealInput } = useStaticInputHandoff({
203
+ inputRef,
204
+ renderInput,
205
+ setRenderInput,
206
+ getFocusTarget: (input) => {
207
+ return (input.getContainer?.() ?? input).querySelector("input, button, [tabindex]:not([tabindex='-1'])");
208
+ }
209
+ });
202
210
  const watchedValue = "formControl" in props && props.formControl ? useWatch({
203
211
  control: props.formControl.control,
204
212
  name: props.formControl.name
@@ -215,13 +223,6 @@ var DateTimePicker = ({ fullIso = true, renderStaticInput, ...props }) => {
215
223
  if (isoString == null) return isoString;
216
224
  return DateTimeUtils.fromUTCISOToCalendarDateTime(isoString);
217
225
  };
218
- useEffect(() => {
219
- if (!renderInput || !shouldFocus) return;
220
- requestAnimationFrame(() => {
221
- ((inputRef.current?.getContainer?.() ?? inputRef.current)?.querySelector("input, button, [tabindex]:not([tabindex='-1'])"))?.focus();
222
- });
223
- setShouldFocus(false);
224
- }, [renderInput, shouldFocus]);
225
226
  if (!renderInput) {
226
227
  const dateTimeValue = rawValue ? parseDateValue(rawValue) : null;
227
228
  const as = props.as ?? ui.input.as;
@@ -229,6 +230,8 @@ var DateTimePicker = ({ fullIso = true, renderStaticInput, ...props }) => {
229
230
  const isDisabled = isFormControlDisabled || !!props.isDisabled;
230
231
  const todayIcon = props.todayIcon ?? ui.dateInput.todayIcon;
231
232
  const todayIconButtonSize = props.todayIconButtonSize ?? ui.dateInput.todayIconButtonSize;
233
+ const todayIconPlacement = props.todayIconPlacement ?? ui.dateInput.todayIconPlacement;
234
+ const staticTodayIcon = renderDatePickerTodayIcon(todayIcon, todayIconButtonSize);
232
235
  const disableManualEntry = props.disableManualEntry ?? ui.dateInput.disableManualEntry;
233
236
  const showDropdown = !props.disableDropdown || disableManualEntry;
234
237
  const staticSegments = getStaticCalendarDateTimeSegments({
@@ -257,11 +260,9 @@ var DateTimePicker = ({ fullIso = true, renderStaticInput, ...props }) => {
257
260
  dataHasSelection: dateTimeValue !== null
258
261
  },
259
262
  renderStatic: true,
260
- onStaticInteract: (focus) => {
261
- setShouldFocus(focus);
262
- setRenderInput(true);
263
- },
264
- actionContent: renderDatePickerTodayIcon(todayIcon, todayIconButtonSize),
263
+ onStaticInteract: renderRealInput,
264
+ actionContent: staticTodayIcon,
265
+ actionContentPlacement: todayIconPlacement === "fieldLabel" ? "content-row" : "content-wrapper",
265
266
  trailingContent: showDropdown ? ui.dateInput.dateTimeIcon : void 0,
266
267
  children: staticSegments
267
268
  });
@@ -7,6 +7,7 @@ import { getStaticTimeSegments } from "../shared/staticDateTimeSegments.js";
7
7
  import { FormField } from "../../FormField/FormField.js";
8
8
  import { TooltipWrapper } from "../../shared/TooltipWrapper.js";
9
9
  import { InputFrame } from "../../Skeleton/InputFrame.js";
10
+ import { useStaticInputHandoff } from "../../shared/useStaticInputHandoff.js";
10
11
  import { useDebounceCallback } from "../../../../hooks/useDebounceCallback.js";
11
12
  import { TimePickerInput } from "../shared/TimePickerInput.js";
12
13
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -142,8 +143,13 @@ var TimePicker = ({ renderStaticInput, ...props }) => {
142
143
  const ui = UIConfig.useConfig();
143
144
  const { locale } = useLocale();
144
145
  const [renderInput, setRenderInput] = useState(!(renderStaticInput ?? ui.renderStaticInput));
145
- const [shouldFocus, setShouldFocus] = useState(false);
146
146
  const inputRef = useRef(null);
147
+ const { renderRealInput } = useStaticInputHandoff({
148
+ inputRef,
149
+ renderInput,
150
+ setRenderInput,
151
+ getFocusTarget: (input) => input.querySelector("input, button, [tabindex]:not([tabindex='-1'])")
152
+ });
147
153
  const watchedValue = "formControl" in props && props.formControl ? useWatch({
148
154
  control: props.formControl.control,
149
155
  name: props.formControl.name
@@ -161,13 +167,6 @@ var TimePicker = ({ renderStaticInput, ...props }) => {
161
167
  if (isoString == null) return isoString;
162
168
  return toTime(DateTimeUtils.fromISOtoZonedDateTime(isoString));
163
169
  };
164
- useEffect(() => {
165
- if (!renderInput || !shouldFocus) return;
166
- requestAnimationFrame(() => {
167
- (inputRef.current?.querySelector("input, button, [tabindex]:not([tabindex='-1'])"))?.focus();
168
- });
169
- setShouldFocus(false);
170
- }, [renderInput, shouldFocus]);
171
170
  if (!renderInput) {
172
171
  const timeValue = rawValue ? parseTimeValue(rawValue) : null;
173
172
  const as = props.as ?? ui.input.as;
@@ -198,10 +197,7 @@ var TimePicker = ({ renderStaticInput, ...props }) => {
198
197
  dataHasSelection: timeValue !== null
199
198
  },
200
199
  renderStatic: true,
201
- onStaticInteract: (focus) => {
202
- setShouldFocus(focus);
203
- setRenderInput(true);
204
- },
200
+ onStaticInteract: renderRealInput,
205
201
  trailingContent: showDropdown ? ui.dateInput.timeIcon : void 0,
206
202
  children: staticSegments
207
203
  });
@@ -1,5 +1,5 @@
1
1
  import { DOMAttributes } from '@react-types/shared';
2
- import { ReactNode } from 'react';
2
+ import { LabelHTMLAttributes, ReactNode } from 'react';
3
3
  export interface FormFieldHeaderProps {
4
4
  label: string;
5
5
  tooltipText?: string;
@@ -9,6 +9,6 @@ export interface FormFieldHeaderProps {
9
9
  isHeaderHidden?: boolean;
10
10
  isDisabled?: boolean;
11
11
  className?: string;
12
- labelProps?: DOMAttributes<HTMLLabelElement>;
12
+ labelProps?: DOMAttributes<HTMLLabelElement> & LabelHTMLAttributes<HTMLLabelElement>;
13
13
  }
14
14
  export declare const FormFieldHeader: ({ label, tooltipText, helperText, isRequired, rightContent, isHeaderHidden, isDisabled, className, labelProps, }: FormFieldHeaderProps) => import("react/jsx-runtime").JSX.Element;
@@ -1,5 +1,5 @@
1
1
  import { DOMAttributes } from '@react-types/shared';
2
- import { RefCallback } from 'react';
2
+ import { LabelHTMLAttributes, RefCallback } from 'react';
3
3
  import { InputSizeProps } from '../shared/input.cva';
4
4
  import { LabelBaseProps } from '../shared/label.cva';
5
5
  export interface FormFieldLabelProps extends LabelBaseProps, InputSizeProps {
@@ -7,7 +7,7 @@ export interface FormFieldLabelProps extends LabelBaseProps, InputSizeProps {
7
7
  isRequired?: boolean;
8
8
  isDisabled?: boolean;
9
9
  ref?: RefCallback<HTMLOrSVGElement>;
10
- labelProps?: DOMAttributes<HTMLLabelElement>;
10
+ labelProps?: DOMAttributes<HTMLLabelElement> & LabelHTMLAttributes<HTMLLabelElement>;
11
11
  className?: string;
12
12
  }
13
13
  export declare const FormFieldLabel: ({ ref, as, label, isRequired, isDisabled, labelProps, className, }: FormFieldLabelProps) => import("react/jsx-runtime").JSX.Element;
@@ -4,6 +4,7 @@ import { inputBase } from "../../shared/input.cva.js";
4
4
  import { FormField } from "../../FormField/FormField.js";
5
5
  import { TooltipWrapper } from "../../shared/TooltipWrapper.js";
6
6
  import { InputFrame } from "../../Skeleton/InputFrame.js";
7
+ import { useStaticInputHandoff } from "../../shared/useStaticInputHandoff.js";
7
8
  import { InputContent } from "../shared/InputContent.js";
8
9
  import { getStaticNumberDisplayValue } from "../shared/numberStatic.utils.js";
9
10
  import { jsx } from "react/jsx-runtime";
@@ -124,19 +125,18 @@ var NumberInput = ({ renderStaticInput, ...props }) => {
124
125
  const ui = UIConfig.useConfig();
125
126
  const { locale } = useLocale();
126
127
  const [renderInput, setRenderInput] = useState(!(renderStaticInput ?? ui.renderStaticInput));
127
- const [shouldFocus, setShouldFocus] = useState(false);
128
128
  const inputRef = useRef(null);
129
+ const { shouldFocus, renderRealInput, replayStaticInputChange } = useStaticInputHandoff({
130
+ inputRef,
131
+ renderInput,
132
+ setRenderInput
133
+ });
129
134
  const watchedValue = "formControl" in props && props.formControl ? useWatch({
130
135
  control: props.formControl.control,
131
136
  name: props.formControl.name
132
137
  }) : props.value;
133
138
  let isFormControlDisabled = false;
134
139
  if ("formControl" in props && props.formControl) isFormControlDisabled = !!props.formControl.control._options?.disabled;
135
- useEffect(() => {
136
- if (!renderInput || !shouldFocus) return;
137
- requestAnimationFrame(() => inputRef.current?.focus());
138
- setShouldFocus(false);
139
- }, [renderInput, shouldFocus]);
140
140
  if (!renderInput) {
141
141
  const staticValue = watchedValue ?? props.value ?? props.defaultValue;
142
142
  const formatOptions = props.formatOptions ?? ui.numberInput.formatOptions;
@@ -161,19 +161,16 @@ var NumberInput = ({ renderStaticInput, ...props }) => {
161
161
  className: clsx("group w-full", as === "inline" && "h-full", props.className),
162
162
  dataAttributes,
163
163
  renderStatic: true,
164
- onStaticInteract: (focus) => {
165
- setShouldFocus(focus);
166
- setRenderInput(true);
167
- },
164
+ onStaticInteract: renderRealInput,
168
165
  inputClassName: props.inputClassName,
169
166
  children: (dataAttributeProps) => /* @__PURE__ */ jsx("input", {
170
167
  ref: inputRef,
171
- readOnly: true,
172
168
  disabled: isDisabled,
173
169
  tabIndex: -1,
174
170
  value: displayValue,
175
171
  placeholder: as === "floating" ? "" : props.placeholder,
176
172
  className: "w-full bg-transparent outline-none placeholder:text-text-default-3 disabled:text-interactive-text-secondary-disabled",
173
+ onChange: (event) => replayStaticInputChange(event.target.value),
177
174
  ...dataAttributeProps,
178
175
  "data-rac": true
179
176
  })
@@ -3,11 +3,12 @@ import { UIConfig } from "../../../../config/uiConfig.context.js";
3
3
  import { InputClear } from "../../shared/InputClear.js";
4
4
  import { inputBase, inputSize } from "../../shared/input.cva.js";
5
5
  import { InputFrame } from "../../Skeleton/InputFrame.js";
6
+ import { useStaticInputHandoff } from "../../shared/useStaticInputHandoff.js";
6
7
  import { getStaticNumberDisplayValue } from "../shared/numberStatic.utils.js";
7
8
  import { NumberRangeField } from "./NumberRangeField.js";
8
9
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
9
10
  import { clsx } from "clsx";
10
- import { useEffect, useState } from "react";
11
+ import { useRef, useState } from "react";
11
12
  import { useLocale } from "react-aria-components";
12
13
  import { useFocusVisible, useFocusWithin, useHover } from "react-aria";
13
14
  import { Controller, useWatch } from "react-hook-form";
@@ -122,17 +123,17 @@ var NumberRangeInput = ({ renderStaticInput, ...props }) => {
122
123
  const ui = UIConfig.useConfig();
123
124
  const { locale } = useLocale();
124
125
  const [renderInput, setRenderInput] = useState(!(renderStaticInput ?? ui.renderStaticInput));
125
- const [shouldFocus, setShouldFocus] = useState(false);
126
+ const { shouldFocus, renderRealInput } = useStaticInputHandoff({
127
+ inputRef: useRef(null),
128
+ renderInput,
129
+ setRenderInput
130
+ });
126
131
  const watchedValue = "formControl" in props && props.formControl ? useWatch({
127
132
  control: props.formControl.control,
128
133
  name: props.formControl.name
129
134
  }) : props.value;
130
135
  let isFormControlDisabled = false;
131
136
  if ("formControl" in props && props.formControl) isFormControlDisabled = !!props.formControl.control._options?.disabled;
132
- useEffect(() => {
133
- if (!renderInput || !shouldFocus) return;
134
- setShouldFocus(false);
135
- }, [renderInput, shouldFocus]);
136
137
  if (!renderInput) {
137
138
  const staticValue = normalizeRangeValue(watchedValue, props.minValue, props.maxValue);
138
139
  const formatOptions = props.formatOptions ?? ui.numberInput.formatOptions;
@@ -165,13 +166,12 @@ var NumberRangeInput = ({ renderStaticInput, ...props }) => {
165
166
  contentWrapperClassName: "flex w-full items-center gap-input-gap-input-text-to-elements",
166
167
  dataAttributes,
167
168
  renderStatic: true,
168
- onStaticInteract: (focus) => {
169
- setShouldFocus(focus);
170
- setRenderInput(true);
171
- },
169
+ onStaticInteract: renderRealInput,
172
170
  isClearable: false,
173
- children: (dataAttributeProps) => /* @__PURE__ */ jsxs(Fragment, { children: [
171
+ labelPlacement: "content-row",
172
+ children: ({ id, ...dataAttributeProps }) => /* @__PURE__ */ jsxs(Fragment, { children: [
174
173
  /* @__PURE__ */ jsx("input", {
174
+ id,
175
175
  readOnly: true,
176
176
  disabled: isDisabled,
177
177
  tabIndex: -1,
@@ -190,6 +190,7 @@ var NumberRangeInput = ({ renderStaticInput, ...props }) => {
190
190
  children: "-"
191
191
  }),
192
192
  /* @__PURE__ */ jsx("input", {
193
+ id: `${id}-max`,
193
194
  readOnly: true,
194
195
  disabled: isDisabled,
195
196
  tabIndex: -1,
@@ -4,6 +4,7 @@ import { inputBase } from "../../shared/input.cva.js";
4
4
  import { FormField } from "../../FormField/FormField.js";
5
5
  import { TooltipWrapper } from "../../shared/TooltipWrapper.js";
6
6
  import { InputFrame } from "../../Skeleton/InputFrame.js";
7
+ import { useStaticInputHandoff } from "../../shared/useStaticInputHandoff.js";
7
8
  import { InputContent } from "../shared/InputContent.js";
8
9
  import { jsx } from "react/jsx-runtime";
9
10
  import { clsx } from "clsx";
@@ -119,8 +120,12 @@ var TextInputBase = (props) => {
119
120
  var TextInput = ({ renderStaticInput, ...props }) => {
120
121
  const ui = UIConfig.useConfig();
121
122
  const [renderInput, setRenderInput] = useState(!(renderStaticInput ?? ui.renderStaticInput));
122
- const [shouldFocus, setShouldFocus] = useState(false);
123
123
  const inputRef = useRef(null);
124
+ const { shouldFocus, renderRealInput, replayStaticInputChange } = useStaticInputHandoff({
125
+ inputRef,
126
+ renderInput,
127
+ setRenderInput
128
+ });
124
129
  const watchedValue = "formControl" in props && props.formControl ? useWatch({
125
130
  control: props.formControl.control,
126
131
  name: props.formControl.name
@@ -130,11 +135,6 @@ var TextInput = ({ renderStaticInput, ...props }) => {
130
135
  const staticValue = watchedValue ?? props.value ?? props.defaultValue ?? "";
131
136
  const inputType = props.type;
132
137
  if (!renderInput && inputType === "password" && `${staticValue}`.length > 0) setRenderInput(true);
133
- useEffect(() => {
134
- if (!renderInput || !shouldFocus) return;
135
- requestAnimationFrame(() => inputRef.current?.focus());
136
- setShouldFocus(false);
137
- }, [renderInput, shouldFocus]);
138
138
  if (!renderInput) {
139
139
  const as = props.as ?? ui.input.as;
140
140
  let isDisabled = !!props.isDisabled;
@@ -158,20 +158,17 @@ var TextInput = ({ renderStaticInput, ...props }) => {
158
158
  className: clsx("group w-full", as === "inline" && "h-full", props.className),
159
159
  dataAttributes,
160
160
  renderStatic: true,
161
- onStaticInteract: (focus) => {
162
- setShouldFocus(focus);
163
- setRenderInput(true);
164
- },
161
+ onStaticInteract: renderRealInput,
165
162
  inputClassName: props.inputClassName,
166
163
  children: (dataAttributeProps) => /* @__PURE__ */ jsx("input", {
167
164
  ref: inputRef,
168
165
  type: inputType === "hidden" ? "password" : inputType,
169
- readOnly: true,
170
166
  disabled: isDisabled,
171
167
  tabIndex: -1,
172
168
  value: displayValue,
173
169
  placeholder: as === "floating" ? "" : props.placeholder,
174
170
  className: "w-full bg-transparent outline-none placeholder:text-text-default-3 disabled:text-interactive-text-secondary-disabled",
171
+ onChange: (event) => replayStaticInputChange(event.target.value),
175
172
  ...dataAttributeProps,
176
173
  "data-rac": true
177
174
  })
@@ -1,11 +1,12 @@
1
1
  import { ArrowDropDownIcon } from "../../../../assets/icons/ArrowDropDown.js";
2
2
  import { UIConfig } from "../../../../config/uiConfig.context.js";
3
3
  import { InputFrame } from "../../Skeleton/InputFrame.js";
4
+ import { useStaticInputHandoff } from "../../shared/useStaticInputHandoff.js";
4
5
  import { SelectBase } from "../shared/SelectBase.js";
5
6
  import { getStaticSelectValue } from "../shared/staticSelect.utils.js";
6
7
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
8
  import { clsx } from "clsx";
8
- import { useEffect, useRef, useState } from "react";
9
+ import { useRef, useState } from "react";
9
10
  import { mergeRefs } from "@react-aria/utils";
10
11
  import { Controller, useWatch } from "react-hook-form";
11
12
  //#region src/components/inputs/Selection/Autocomplete/Autocomplete.tsx
@@ -37,8 +38,13 @@ function _Autocomplete(props) {
37
38
  function Autocomplete({ renderStaticInput, ...props }) {
38
39
  const ui = UIConfig.useConfig();
39
40
  const [renderInput, setRenderInput] = useState(!(renderStaticInput ?? ui.renderStaticInput));
40
- const [shouldFocus, setShouldFocus] = useState(false);
41
41
  const rootRef = useRef(null);
42
+ const { renderRealInput, replayStaticInputChange } = useStaticInputHandoff({
43
+ inputRef: rootRef,
44
+ renderInput,
45
+ setRenderInput,
46
+ getFocusTarget: (root) => root.querySelector("input, [data-type='select-trigger'], button, [tabindex]")
47
+ });
42
48
  const currentValue = ("formControl" in props && props.formControl ? useWatch({
43
49
  control: props.formControl.control,
44
50
  name: props.formControl.name
@@ -46,13 +52,6 @@ function Autocomplete({ renderStaticInput, ...props }) {
46
52
  let isFormControlDisabled = false;
47
53
  if ("formControl" in props && props.formControl) isFormControlDisabled = !!props.formControl.control._options?.disabled;
48
54
  if (!renderInput && !!props.customTrigger) setRenderInput(true);
49
- useEffect(() => {
50
- if (!renderInput || !shouldFocus) return;
51
- requestAnimationFrame(() => {
52
- (rootRef.current?.querySelector("input, [data-type='select-trigger'], button, [tabindex]"))?.focus();
53
- });
54
- setShouldFocus(false);
55
- }, [renderInput, shouldFocus]);
56
55
  if (!renderInput) {
57
56
  const as = props.as ?? ui.input.as;
58
57
  let isDisabled = !!props.isDisabled;
@@ -94,22 +93,19 @@ function Autocomplete({ renderStaticInput, ...props }) {
94
93
  labelPlacement: "content-row",
95
94
  dataAttributes,
96
95
  renderStatic: true,
97
- onStaticInteract: (focus) => {
98
- setShouldFocus(focus);
99
- setRenderInput(true);
100
- },
96
+ onStaticInteract: renderRealInput,
101
97
  leadingContent: props.leadingContent && /* @__PURE__ */ jsx("div", {
102
98
  className: "ml-input-side-default flex shrink-0 items-center",
103
99
  children: props.leadingContent
104
100
  }),
105
101
  trailingContent,
106
102
  children: (dataAttributeProps) => /* @__PURE__ */ jsxs(Fragment, { children: [isMultiple && !isEmpty && displayValue, /* @__PURE__ */ jsx("input", {
107
- readOnly: true,
108
103
  disabled: isDisabled,
109
104
  tabIndex: -1,
110
105
  value: inputValue,
111
106
  placeholder: isMultiple && shouldRenderPlaceholderAfterValue ? props.placeholder : placeholder ?? void 0,
112
107
  className: clsx("w-full flex-1 bg-transparent outline-none placeholder:text-text-default-3 disabled:text-interactive-text-secondary-disabled", props.inputClassName),
108
+ onChange: (event) => replayStaticInputChange(event.target.value),
113
109
  ...dataAttributeProps,
114
110
  "data-rac": true
115
111
  })] })
@@ -1,11 +1,12 @@
1
1
  import { ArrowDropDownIcon } from "../../../../assets/icons/ArrowDropDown.js";
2
2
  import { UIConfig } from "../../../../config/uiConfig.context.js";
3
3
  import { InputFrame } from "../../Skeleton/InputFrame.js";
4
+ import { useStaticInputHandoff } from "../../shared/useStaticInputHandoff.js";
4
5
  import { SelectBase } from "../shared/SelectBase.js";
5
6
  import { getStaticSelectValue } from "../shared/staticSelect.utils.js";
6
7
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
8
  import { clsx } from "clsx";
8
- import { useEffect, useRef, useState } from "react";
9
+ import { useRef, useState } from "react";
9
10
  import { mergeRefs } from "@react-aria/utils";
10
11
  import { Controller, useWatch } from "react-hook-form";
11
12
  //#region src/components/inputs/Selection/Select/Select.tsx
@@ -32,8 +33,13 @@ function _Select(props) {
32
33
  function Select({ renderStaticInput, ...props }) {
33
34
  const ui = UIConfig.useConfig();
34
35
  const [renderInput, setRenderInput] = useState(!(renderStaticInput ?? ui.renderStaticInput));
35
- const [shouldFocus, setShouldFocus] = useState(false);
36
36
  const rootRef = useRef(null);
37
+ const { renderRealInput, replayStaticInputChange } = useStaticInputHandoff({
38
+ inputRef: rootRef,
39
+ renderInput,
40
+ setRenderInput,
41
+ getFocusTarget: (root) => root.querySelector("[data-type='select-trigger'], input, button, [tabindex]")
42
+ });
37
43
  const currentValue = ("formControl" in props && props.formControl ? useWatch({
38
44
  control: props.formControl.control,
39
45
  name: props.formControl.name
@@ -42,13 +48,6 @@ function Select({ renderStaticInput, ...props }) {
42
48
  if ("formControl" in props && props.formControl) isFormControlDisabled = !!props.formControl.control._options?.disabled;
43
49
  if (!renderInput && props.selectionMode !== "multiple" && !!props.showSelectionContent && currentValue != null) setRenderInput(true);
44
50
  if (!renderInput && !!props.customTrigger) setRenderInput(true);
45
- useEffect(() => {
46
- if (!renderInput || !shouldFocus) return;
47
- requestAnimationFrame(() => {
48
- (rootRef.current?.querySelector("[data-type='select-trigger'], input, button, [tabindex]"))?.focus();
49
- });
50
- setShouldFocus(false);
51
- }, [renderInput, shouldFocus]);
52
51
  if (!renderInput) {
53
52
  const as = props.as ?? ui.input.as;
54
53
  let isDisabled = !!props.isDisabled;
@@ -95,10 +94,7 @@ function Select({ renderStaticInput, ...props }) {
95
94
  labelPlacement: "content-row",
96
95
  dataAttributes,
97
96
  renderStatic: true,
98
- onStaticInteract: (focus) => {
99
- setShouldFocus(focus);
100
- setRenderInput(true);
101
- },
97
+ onStaticInteract: renderRealInput,
102
98
  leadingContent: props.leadingContent && /* @__PURE__ */ jsx("div", {
103
99
  className: "ml-input-side-default flex shrink-0 items-center",
104
100
  children: props.leadingContent
@@ -106,12 +102,12 @@ function Select({ renderStaticInput, ...props }) {
106
102
  showClear: false,
107
103
  trailingContent,
108
104
  children: (dataAttributeProps) => /* @__PURE__ */ jsxs(Fragment, { children: [isMultiple && !isEmpty && displayValue, isSearchable ? /* @__PURE__ */ jsx("input", {
109
- readOnly: true,
110
105
  disabled: isDisabled,
111
106
  tabIndex: -1,
112
107
  value: inputValue,
113
108
  placeholder: shouldRenderPlaceholderAfterValue ? props.placeholder : placeholder ?? void 0,
114
109
  className: clsx("w-full flex-1 bg-transparent outline-none placeholder:text-text-default-3 disabled:text-interactive-text-secondary-disabled", props.inputClassName),
110
+ onChange: (event) => replayStaticInputChange(event.target.value),
115
111
  ...dataAttributeProps,
116
112
  "data-rac": true
117
113
  }) : /* @__PURE__ */ jsx("button", {
@@ -33,8 +33,12 @@ type InputFrameDataAttributeProps = {
33
33
  "data-is-filled"?: boolean;
34
34
  "data-is-required"?: boolean;
35
35
  };
36
- type InputFrameChildren = ReactNode | ((dataAttributeProps: InputFrameDataAttributeProps) => ReactNode);
36
+ type InputFrameChildProps = InputFrameDataAttributeProps & {
37
+ id: string;
38
+ };
39
+ type InputFrameChildren = ReactNode | ((childProps: InputFrameChildProps) => ReactNode);
37
40
  type InputFrameLabelPlacement = "content-wrapper" | "content-row";
41
+ type InputFrameActionContentPlacement = "content-wrapper" | "content-row";
38
42
  export interface InputFrameProps extends InputVariantProps, Partial<FormFieldProps> {
39
43
  ref?: Ref<HTMLDivElement>;
40
44
  children: InputFrameChildren;
@@ -44,6 +48,7 @@ export interface InputFrameProps extends InputVariantProps, Partial<FormFieldPro
44
48
  leadingContent?: ReactNode;
45
49
  leadingIcon?: InputFrameIcon;
46
50
  actionContent?: ReactNode;
51
+ actionContentPlacement?: InputFrameActionContentPlacement;
47
52
  trailingContent?: ReactNode;
48
53
  trailingIcon?: InputFrameIcon;
49
54
  trailingAction?: ReactNode;
@@ -58,6 +63,7 @@ export interface InputFrameProps extends InputVariantProps, Partial<FormFieldPro
58
63
  onClear?: () => void;
59
64
  dataAttributes?: InputFrameDataAttributes;
60
65
  typographySize?: TypographyVariantProps["size"];
66
+ id?: string;
61
67
  inputClassName?: string;
62
68
  contentClassName?: string;
63
69
  contentWrapperClassName?: string;
@@ -10,7 +10,7 @@ import { FormField } from "../FormField/FormField.js";
10
10
  import { TooltipWrapper } from "../shared/TooltipWrapper.js";
11
11
  import { jsx, jsxs } from "react/jsx-runtime";
12
12
  import { clsx } from "clsx";
13
- import { isValidElement } from "react";
13
+ import { isValidElement, useId } from "react";
14
14
  //#region src/components/inputs/Skeleton/InputFrame.tsx
15
15
  var InputFrame = (props) => {
16
16
  const ui = UIConfig.useConfig();
@@ -18,7 +18,8 @@ var InputFrame = (props) => {
18
18
  const inputSizeCva = UIStyle.useCva("input.sizeCva", inputSize);
19
19
  const inputContentWrapperCva = UIStyle.useCva("input.contentWrapperCva", inputContentWrapper);
20
20
  const typographyCva = UIStyle.useCva("typography.cva", typography);
21
- const { ref, children, formFieldRef, labelProps, headerProps, error, label, tooltipText, helperText, isRequired, rightContent, isDisabled, isHeaderHidden, hideLabel: hideLabelProp, headerClassName, errorClassName, className, leadingContent, leadingIcon, actionContent, trailingContent, trailingIcon, trailingAction, unit, isLoading, action, isClearable: isClearableProp, showClear, renderStatic, onStaticInteract, clearClassName, onClear, dataAttributes, typographySize, inputClassName, contentClassName, contentWrapperClassName, labelPlacement = "content-wrapper", trailingClassName, variant: variantProp, as: asProp, size: sizeProp } = props;
21
+ const generatedInputId = useId();
22
+ const { ref, children, formFieldRef, labelProps: labelPropsProp, headerProps, error, label, tooltipText, helperText, isRequired, rightContent, isDisabled, isHeaderHidden, hideLabel: hideLabelProp, headerClassName, errorClassName, className, leadingContent, leadingIcon, actionContent, actionContentPlacement = "content-wrapper", trailingContent, trailingIcon, trailingAction, unit, isLoading, action, isClearable: isClearableProp, showClear, renderStatic, onStaticInteract, clearClassName, onClear, dataAttributes, typographySize, id: idProp, inputClassName, contentClassName, contentWrapperClassName, labelPlacement = "content-wrapper", trailingClassName, variant: variantProp, as: asProp, size: sizeProp } = props;
22
23
  const as = asProp ?? ui.input.as;
23
24
  const size = sizeProp ?? ui.input.size;
24
25
  const variant = variantProp ?? ui.input.variant;
@@ -29,6 +30,12 @@ var InputFrame = (props) => {
29
30
  const hasTrailingContent = hasClear || !!trailingContent || !!unit || !!isLoading || !!action || !!trailingIcon || !!trailingAction;
30
31
  const isTrailingInteractive = !!action || !!trailingAction || !!isTrailingIconElement || hasClear && !!showClear || !!trailingContent;
31
32
  const dataAttributeProps = getInputFrameDataAttributeProps(dataAttributes);
33
+ const baseLabelProps = headerProps?.labelProps ?? labelPropsProp;
34
+ const inputId = idProp ?? baseLabelProps?.htmlFor ?? `input-frame_${generatedInputId}`;
35
+ const labelProps = {
36
+ ...baseLabelProps,
37
+ htmlFor: inputId
38
+ };
32
39
  const formFieldProps = {
33
40
  error,
34
41
  label: label ?? "",
@@ -43,7 +50,10 @@ var InputFrame = (props) => {
43
50
  errorClassName,
44
51
  className
45
52
  };
46
- const resolvedHeaderProps = headerProps ?? {
53
+ const resolvedHeaderProps = headerProps ? {
54
+ ...headerProps,
55
+ labelProps
56
+ } : {
47
57
  label: label ?? "",
48
58
  tooltipText,
49
59
  helperText,
@@ -107,12 +117,16 @@ var InputFrame = (props) => {
107
117
  children: renderIconVisual(leadingIcon)
108
118
  }),
109
119
  labelPlacement === "content-row" && labelContent,
120
+ actionContentPlacement === "content-row" && actionContent,
110
121
  /* @__PURE__ */ jsxs("div", {
111
122
  className: resolvedContentWrapperClassName,
112
123
  children: [
113
124
  labelPlacement === "content-wrapper" && labelContent,
114
- actionContent,
115
- typeof children === "function" ? children(dataAttributeProps) : children
125
+ actionContentPlacement === "content-wrapper" && actionContent,
126
+ typeof children === "function" ? children({
127
+ ...dataAttributeProps,
128
+ id: inputId
129
+ }) : children
116
130
  ]
117
131
  })
118
132
  ]
@@ -0,0 +1,13 @@
1
+ import { Dispatch, RefObject, SetStateAction } from 'react';
2
+ interface StaticInputHandoffOptions<TElement extends HTMLElement> {
3
+ inputRef: RefObject<TElement | null>;
4
+ renderInput: boolean;
5
+ setRenderInput: Dispatch<SetStateAction<boolean>>;
6
+ getFocusTarget?: (element: TElement) => HTMLElement | null | undefined;
7
+ }
8
+ export declare const useStaticInputHandoff: <TElement extends HTMLElement = HTMLInputElement>({ inputRef, renderInput, setRenderInput, getFocusTarget, }: StaticInputHandoffOptions<TElement>) => {
9
+ shouldFocus: boolean;
10
+ renderRealInput: (focus: boolean) => void;
11
+ replayStaticInputChange: (value: string) => void;
12
+ };
13
+ export {};
@@ -0,0 +1,43 @@
1
+ import { useEffect, useRef, useState } from "react";
2
+ //#region src/components/inputs/shared/useStaticInputHandoff.ts
3
+ var useStaticInputHandoff = ({ inputRef, renderInput, setRenderInput, getFocusTarget }) => {
4
+ const [shouldFocus, setShouldFocus] = useState(false);
5
+ const pendingStaticChangeRef = useRef(null);
6
+ useEffect(() => {
7
+ if (!renderInput || !shouldFocus) return;
8
+ const frame = requestAnimationFrame(() => {
9
+ const input = inputRef.current;
10
+ const pendingValue = pendingStaticChangeRef.current;
11
+ const focusTarget = input ? getFocusTarget?.(input) ?? input : null;
12
+ if (focusTarget instanceof HTMLInputElement && pendingValue != null) {
13
+ (Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value")?.set)?.call(focusTarget, pendingValue);
14
+ focusTarget.dispatchEvent(new Event("input", { bubbles: true }));
15
+ pendingStaticChangeRef.current = null;
16
+ }
17
+ focusTarget?.focus();
18
+ setShouldFocus(false);
19
+ });
20
+ return () => {
21
+ cancelAnimationFrame(frame);
22
+ };
23
+ }, [
24
+ getFocusTarget,
25
+ inputRef,
26
+ renderInput,
27
+ shouldFocus
28
+ ]);
29
+ return {
30
+ shouldFocus,
31
+ renderRealInput: (focus) => {
32
+ setShouldFocus(focus);
33
+ setRenderInput(true);
34
+ },
35
+ replayStaticInputChange: (value) => {
36
+ pendingStaticChangeRef.current = value;
37
+ setShouldFocus(true);
38
+ setRenderInput(true);
39
+ }
40
+ };
41
+ };
42
+ //#endregion
43
+ export { useStaticInputHandoff };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@povio/ui",
3
- "version": "2.2.9-rc.31",
3
+ "version": "2.2.9-rc.33",
4
4
  "type": "module",
5
5
  "module": "dist/index.js",
6
6
  "types": "dist/index.d.ts",