asma-ui-core 3.109.0 → 3.111.0

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.
Files changed (59) hide show
  1. package/dist/asma-ui-core.css +1 -1
  2. package/dist/components/feedback/minimizable-dialog/MinimizableDialog.js +41 -39
  3. package/dist/components/feedback/minimizable-dialog/v2/MinimizableDialogV2.js +62 -60
  4. package/dist/components/inputs/dynamic-select/components/DynamicInteractiveChipGroup.js +61 -58
  5. package/dist/components/inputs/dynamic-select/components/DynamicSelectAutocomplete.js +74 -71
  6. package/dist/components/inputs/dynamic-select/components/DynamicSelectReadOnlyValue.js +13 -10
  7. package/dist/components/inputs/field-styles.js +16 -6
  8. package/dist/components/inputs/form-layout/StyledFieldPair.js +22 -0
  9. package/dist/components/inputs/form-layout/StyledFieldPair.module.js +8 -0
  10. package/dist/components/inputs/form-layout/StyledFormColumn.js +13 -0
  11. package/dist/components/inputs/input-field/StyledInputField.js +235 -198
  12. package/dist/components/inputs/phone-field/StyledPhoneField.js +41 -39
  13. package/dist/components/inputs/search-field/StyledSearchField.js +22 -20
  14. package/dist/components/inputs/select/StyledSelect.js +110 -108
  15. package/dist/components/inputs/select-autocomplete/StyledSelectAutocomplete.js +111 -108
  16. package/dist/components/inputs/slider/StyledSlider.js +2 -2
  17. package/dist/components/inputs/textarea/StyledTextarea.js +50 -52
  18. package/dist/components/inputs/textarea/StyledTextarea.module.js +3 -3
  19. package/dist/components/miscellaneous/StyledFormControl.js +29 -25
  20. package/dist/components/miscellaneous/StyledInputLabel.js +28 -18
  21. package/dist/datetime/components/date-picker/components/BaseDatePickerInput.js +137 -122
  22. package/dist/datetime/components/date-picker/components/DatePickerInputRangeCompact.js +17 -15
  23. package/dist/datetime/components/date-picker/components/DatePickerInputRangeCompact.module.js +2 -2
  24. package/dist/datetime/components/date-picker/components/DatePickerInputSingle.js +9 -8
  25. package/dist/datetime/components/date-picker/components/StyledCalendarPickerSelectMonth.js +17 -14
  26. package/dist/datetime/components/date-picker/components/StyledCalendarPickerSelectYear.js +14 -9
  27. package/dist/datetime/components/time-picker/StyledTimePicker.js +78 -74
  28. package/dist/datetime/components/time-picker/TimePickerInput.js +43 -41
  29. package/dist/datetime/components/time-picker/helpers/timePickerWidth.js +12 -0
  30. package/dist/helpers/CharacterCounter.js +21 -0
  31. package/dist/hooks/useBackCloses.hook.js +67 -38
  32. package/dist/hooks/useTopLayer.hook.js +30 -26
  33. package/dist/index.js +284 -273
  34. package/dist/src/components/inputs/dynamic-select/components/DynamicSelectReadOnlyValue.d.ts +2 -2
  35. package/dist/src/components/inputs/dynamic-select/types.d.ts +3 -0
  36. package/dist/src/components/inputs/field-styles.d.ts +22 -0
  37. package/dist/src/components/inputs/form-layout/StyledFieldPair.d.ts +18 -0
  38. package/dist/src/components/inputs/form-layout/StyledFormColumn.d.ts +12 -0
  39. package/dist/src/components/inputs/form-layout/index.d.ts +4 -0
  40. package/dist/src/components/inputs/input-field/StyledInputField.d.ts +19 -4
  41. package/dist/src/components/inputs/select/StyledSelect.d.ts +7 -2
  42. package/dist/src/components/inputs/select-autocomplete/StyledSelectAutocomplete.d.ts +5 -1
  43. package/dist/src/components/inputs/textarea/StyledTextarea.d.ts +6 -0
  44. package/dist/src/components/miscellaneous/StyledFormControl.d.ts +5 -2
  45. package/dist/src/components/miscellaneous/StyledInputLabel.d.ts +7 -5
  46. package/dist/src/datetime/components/date-picker/components/BaseDatePickerInput.d.ts +2 -0
  47. package/dist/src/datetime/components/date-picker/types.d.ts +3 -0
  48. package/dist/src/datetime/components/time-picker/helpers/timePickerWidth.d.ts +7 -0
  49. package/dist/src/datetime/components/time-picker/types.d.ts +3 -1
  50. package/dist/src/helpers/CharacterCounter.d.ts +16 -0
  51. package/dist/src/helpers/CharacterCounter.test.d.ts +1 -0
  52. package/dist/src/hooks/useBackCloses.hook.d.ts +47 -2
  53. package/dist/src/hooks/useTopLayer.hook.d.ts +6 -0
  54. package/dist/src/index.d.ts +2 -0
  55. package/dist/tailwind.config.d.ts +14 -0
  56. package/package.json +1 -1
  57. package/src/styles/fieldWidthTokens.css +8 -0
  58. package/src/styles/index.css +1 -0
  59. package/tw-configs/twConfigs.json +14 -0
@@ -1,5 +1,5 @@
1
1
  import { DynamicSelectOption, StyledDynamicSelectProps } from '../types';
2
- type DynamicSelectReadOnlyValueProps<TOption extends DynamicSelectOption> = Pick<StyledDynamicSelectProps<TOption>, 'dataTest' | 'options' | 'title' | 'titleSize' | 'valueKey' | 'labelKey' | 'renderLabel'> & {
2
+ type DynamicSelectReadOnlyValueProps<TOption extends DynamicSelectOption> = Pick<StyledDynamicSelectProps<TOption>, 'dataTest' | 'options' | 'title' | 'titleSize' | 'width' | 'valueKey' | 'labelKey' | 'renderLabel'> & {
3
3
  value: TOption | null;
4
4
  };
5
5
  /**
@@ -11,5 +11,5 @@ type DynamicSelectReadOnlyValueProps<TOption extends DynamicSelectOption> = Pick
11
11
  * buttons, and read-only draws neither. No value → `-`, the same placeholder the chip variant shows.
12
12
  * Multiple selects never reach this component.
13
13
  */
14
- export declare const DynamicSelectReadOnlyValue: <TOption extends DynamicSelectOption>({ dataTest, options, value, title, titleSize, valueKey, labelKey, renderLabel, }: DynamicSelectReadOnlyValueProps<TOption>) => JSX.Element;
14
+ export declare const DynamicSelectReadOnlyValue: <TOption extends DynamicSelectOption>({ dataTest, options, value, title, titleSize, width, valueKey, labelKey, renderLabel, }: DynamicSelectReadOnlyValueProps<TOption>) => JSX.Element;
15
15
  export {};
@@ -1,3 +1,4 @@
1
+ import { FieldWidth } from '../field-styles';
1
2
  import { StyledSelectAutocompleteProps } from '../select-autocomplete';
2
3
  import { default as React } from 'react';
3
4
  export type DynamicSelectOptionPrimitive = string | number | boolean;
@@ -42,6 +43,8 @@ interface DynamicSelectCommonProps<TOption extends DynamicSelectOption> {
42
43
  title?: React.ReactNode;
43
44
  /** @figmaProp none — title typography: `'base'` Body Base SemiBold 16/24 (default), `'lg'` Section title 18/28. */
44
45
  titleSize?: 'base' | 'lg';
46
+ /** Field width pattern step. The pattern keeps a dynamic select Full (WID-5); Full is the default. */
47
+ width?: FieldWidth;
45
48
  /** @figmaProp none — app size (chips/buttons). Defaults to `'medium'`. */
46
49
  size?: 'small' | 'medium';
47
50
  /**
@@ -11,6 +11,28 @@ export interface SingleLineInputLayoutOptions {
11
11
  export declare const singleLineInputLayoutStyle: ({ paddingLeft, paddingRight, }?: SingleLineInputLayoutOptions) => CSSProperties;
12
12
  /** Label vertical anchor — inline so duplicate Tailwind `top-*` utilities cannot shift it. */
13
13
  export declare const floatingLabelLayoutStyle: (shrink: boolean) => CSSProperties;
14
+ /** Field width pattern (ASMA-8489): XS 8rem, S 12.5rem, M 20rem, Full up to the 37.5rem form column. */
15
+ export type FieldWidth = 'xs' | 's' | 'm' | 'full';
16
+ /** `width: 100%; max-width: <step>`, so a field shrinks in a narrower container and never overflows it. */
17
+ export declare const fieldWidthStyle: (width: FieldWidth) => CSSProperties;
18
+ /**
19
+ * Full screen is the one place a field may be wider than 600 px (WID-10): Full fields and the form
20
+ * column fill the dialog's content width, while XS, S and M keep their step.
21
+ */
22
+ export declare const fullScreenFieldWidthVars: {
23
+ '--field-width-full': string;
24
+ '--form-column-width': string;
25
+ };
26
+ /** The step a character limit picks (WID-2): up to 9 characters XS, 15 S, 27 M, longer Full. */
27
+ export declare const fieldWidthForMaxLength: (maxLength: number) => FieldWidth;
28
+ export type FieldLabelPlacement = 'above' | 'floating';
29
+ /**
30
+ * The label above a field, as `StyledPhoneField` draws it. It wraps and is never truncated (TRUNC-4).
31
+ * Put it on a wrapper around the `<label>`: apps in the shell ship `label { padding: 0 !important }`.
32
+ */
33
+ export declare const fieldLabelAboveClass: ({ disabled }?: {
34
+ disabled?: boolean;
35
+ }) => string;
14
36
  /**
15
37
  * Shared outlined-field styling so `StyledInputField`, `StyledInputLabel` (+ `StyledFormControl`)
16
38
  * and `StyledSelect` render an identical border + floating label without MUI. TASK-401.
@@ -0,0 +1,18 @@
1
+ import { ReactNode } from 'react';
2
+ export interface StyledFieldPairProps {
3
+ /** The two fields, first field first, so focus and screen readers reach it first. */
4
+ children: ReactNode;
5
+ /**
6
+ * A group label for parts that only make sense together, such as country code and number.
7
+ * Renders the pair as a `fieldset` with this `legend`; each field still keeps its own label.
8
+ */
9
+ legend?: ReactNode;
10
+ className?: string;
11
+ dataTest?: string;
12
+ }
13
+ /**
14
+ * Two fields that are parts of one answer, side by side (field width pattern WID-9): first name + last
15
+ * name, postal code + city, date + time, from + to, a value + its unit. Each field keeps its width step;
16
+ * a Full field fills the rest of the row and is never narrower than S. Stacks when the row is too narrow.
17
+ */
18
+ export declare const StyledFieldPair: ({ children, legend, className, dataTest }: StyledFieldPairProps) => JSX.Element;
@@ -0,0 +1,12 @@
1
+ import { ReactNode } from 'react';
2
+ export interface StyledFormColumnProps {
3
+ children?: ReactNode;
4
+ className?: string;
5
+ dataTest?: string;
6
+ }
7
+ /**
8
+ * The one form column (field width pattern WID-1): at most 37.5rem (600 px), left-aligned, and as wide
9
+ * as its container when that is narrower. Fields, labels, hints and errors all sit inside it.
10
+ * A full-screen `MinimizableDialog` widens it to the dialog (WID-10).
11
+ */
12
+ export declare const StyledFormColumn: ({ children, className, dataTest }: StyledFormColumnProps) => JSX.Element;
@@ -0,0 +1,4 @@
1
+ export * from './StyledFieldPair';
2
+ export * from './StyledFormColumn';
3
+ export { fieldLabelAboveClass, fieldWidthForMaxLength, fieldWidthStyle } from '../field-styles';
4
+ export type { FieldLabelPlacement, FieldWidth } from '../field-styles';
@@ -1,5 +1,6 @@
1
1
  import { ChangeEvent, CSSProperties, FocusEvent, HTMLAttributes, InputHTMLAttributes, ReactNode, Ref } from 'react';
2
- import { FieldSize } from '../field-styles';
2
+ import { UiCoreLocale } from '../../../helpers/uiCoreLocale';
3
+ import { FieldLabelPlacement, FieldSize, FieldWidth } from '../field-styles';
3
4
  interface InputSlot {
4
5
  startAdornment?: ReactNode;
5
6
  endAdornment?: ReactNode;
@@ -21,8 +22,13 @@ type HtmlInputSlot = InputHTMLAttributes<HTMLInputElement> & {
21
22
  export interface StyledInputFieldProps {
22
23
  /** @figmaProp none — test hook */
23
24
  dataTest: string;
24
- /** @figmaProp Label element (floating) */
25
+ /** @figmaProp Label element */
25
26
  label?: ReactNode;
27
+ /**
28
+ * `above` (default): the label sits above the field and wraps. `floating`: inside the field, only
29
+ * where a label above has no room, such as the search field (field width pattern §10).
30
+ */
31
+ labelPlacement?: FieldLabelPlacement;
26
32
  /** @figmaProp Filled = (value != '')→"on" | else→"off" */
27
33
  value?: string | number;
28
34
  /** @figmaProp Filled = (defaultValue != '')→"on" | else→"off" */
@@ -36,6 +42,12 @@ export interface StyledInputFieldProps {
36
42
  helperText?: ReactNode;
37
43
  reserveHelperText?: boolean;
38
44
  expandHelperText?: boolean;
45
+ /** Character limit; also readable from `slotProps.htmlInput.maxLength`. */
46
+ maxLength?: number;
47
+ /** Shows the count against `maxLength` as the user types, for free text with a limit. */
48
+ counter?: boolean;
49
+ /** Language of the counter text. */
50
+ locale?: UiCoreLocale;
39
51
  /** @figmaProp State = true→"Disabled" */
40
52
  disabled?: boolean;
41
53
  /** @figmaProp State = true→"Read only" */
@@ -61,6 +73,9 @@ export interface StyledInputFieldProps {
61
73
  size?: FieldSize;
62
74
  /** Accepted for API parity; the design always renders outlined. */
63
75
  variant?: string;
76
+ /** Field width pattern step, `width: 100%; max-width: <step>`. Full fills the form column. */
77
+ width?: FieldWidth;
78
+ /** @deprecated Fields are Full by default; use `width`. */
64
79
  fullWidth?: boolean;
65
80
  className?: string;
66
81
  style?: CSSProperties;
@@ -83,7 +98,7 @@ export interface StyledInputFieldProps {
83
98
  onClick?: (event: React.MouseEvent<HTMLDivElement>) => void;
84
99
  }
85
100
  /**
86
- * Outlined text field with a floating label (replaces MUI `TextField`). Self-contained: manages its
101
+ * Outlined text field with its label above it (replaces MUI `TextField`). Self-contained: manages its
87
102
  * own focus/value state to drive the shared field styling ([[field-styles]]); supports multiline,
88
103
  * start/end adornments, an optional clear button, and error/helper text. Public props preserved
89
104
  *
@@ -91,5 +106,5 @@ export interface StyledInputFieldProps {
91
106
  * the visual gate. Multiline auto-grows between `minRows` and `maxRows` (a fixed `rows` pins the height)
92
107
  * and scrolls once the content exceeds the cap; `readOnly` ignores the cap and shows the whole value.
93
108
  */
94
- export declare const StyledInputField: ({ dataTest, label, value, defaultValue, onChange, onBlur, onFocus, error, helperText, reserveHelperText, expandHelperText, disabled, readOnly, required, allowClear, onClear, type, placeholder, name, id, autoComplete, autoFocus, multiline, rows, minRows, maxRows, size, fullWidth, className, style, sx, inputRef, slotProps, InputProps, onKeyDown, onClick, variant: _variant, }: StyledInputFieldProps) => JSX.Element;
109
+ export declare const StyledInputField: ({ dataTest, label, labelPlacement, value, defaultValue, onChange, onBlur, onFocus, error, helperText, reserveHelperText, expandHelperText, maxLength, counter, locale, disabled, readOnly, required, allowClear, onClear, type, placeholder, name, id, autoComplete, autoFocus, multiline, rows, minRows, maxRows, size, width, fullWidth: _fullWidth, className, style, sx, inputRef, slotProps, InputProps, onKeyDown, onClick, variant: _variant, }: StyledInputFieldProps) => JSX.Element;
95
110
  export {};
@@ -1,5 +1,5 @@
1
1
  import { CSSProperties, FocusEventHandler, ReactNode } from 'react';
2
- import { FieldSize } from '../field-styles';
2
+ import { FieldSize, FieldWidth } from '../field-styles';
3
3
  export interface SelectChangeEvent<T = unknown> {
4
4
  target: {
5
5
  value: T;
@@ -44,6 +44,9 @@ export interface StyledSelectProps {
44
44
  displayEmpty?: boolean;
45
45
  multiple?: boolean;
46
46
  renderValue?: (value: unknown) => ReactNode;
47
+ /** Field width pattern step; Full by default. `variant="standard"` keeps its content width. */
48
+ width?: FieldWidth;
49
+ /** @deprecated Fields are Full by default; use `width`. */
47
50
  fullWidth?: boolean;
48
51
  className?: string;
49
52
  style?: CSSProperties;
@@ -57,6 +60,8 @@ export interface StyledSelectProps {
57
60
  * above). Without it (and no `name`/placeholder/value text), the trigger has no accessible name
58
61
  * at all — a common gap when a floating/external label isn't referenced. */
59
62
  labelId?: string;
63
+ /** Marks the trigger `aria-required`; also read from a surrounding `StyledFormControl`. */
64
+ required?: boolean;
60
65
  children?: ReactNode;
61
66
  MenuProps?: {
62
67
  className?: string;
@@ -70,4 +75,4 @@ export interface StyledSelectProps {
70
75
  * portalled `role="listbox"`. Reports open/filled into a surrounding `StyledFormControl` so its
71
76
  * TASK-402.
72
77
  */
73
- export declare const StyledSelect: ({ dataTest, value, defaultValue, onChange, error, errorText, helperText, reserveHelperText, expandHelperText, allowClear, disabled, readOnly, name, placeholder, displayEmpty, multiple, renderValue, fullWidth, className, style, sx, variant, children, MenuProps, onFocus, onBlur, labelId, }: StyledSelectProps) => JSX.Element;
78
+ export declare const StyledSelect: ({ dataTest, value, defaultValue, onChange, error, errorText, helperText, reserveHelperText, expandHelperText, allowClear, disabled, readOnly, name, placeholder, displayEmpty, multiple, renderValue, width, fullWidth: _fullWidth, className, style, sx, variant, children, MenuProps, onFocus, onBlur, labelId, required, }: StyledSelectProps) => JSX.Element;
@@ -1,4 +1,5 @@
1
1
  import { CSSProperties, HTMLAttributes, KeyboardEvent, ReactNode, SyntheticEvent } from 'react';
2
+ import { FieldWidth } from '../field-styles';
2
3
  export type AutocompleteChangeReason = 'createOption' | 'selectOption' | 'removeOption' | 'clear' | 'blur';
3
4
  export type AutocompleteInputChangeReason = 'input' | 'reset' | 'clear';
4
5
  export interface AutocompleteRenderOptionState {
@@ -105,6 +106,9 @@ export interface StyledSelectAutocompleteProps<T, Multiple extends boolean | und
105
106
  multiple?: Multiple;
106
107
  allowSelectAll?: boolean;
107
108
  selectAllLabel?: string;
109
+ /** Field width pattern step; Full by default. */
110
+ width?: FieldWidth;
111
+ /** @deprecated Fields are Full by default; use `width`. */
108
112
  fullWidth?: boolean;
109
113
  /** Accepted for MUI parity; single-select already omits selected options via filtering. */
110
114
  filterSelectedOptions?: boolean;
@@ -132,5 +136,5 @@ export interface StyledSelectAutocompleteProps<T, Multiple extends boolean | und
132
136
  * ponytail: `freeSolo` is accepted (arbitrary input is emitted via onInputChange) but not turned
133
137
  * into option values; exact keyboard-parity edge cases are Chromatic/axe-gated.
134
138
  */
135
- export declare function StyledSelectAutocomplete<T, Multiple extends boolean | undefined = false, DisableClearable extends boolean | undefined = false, FreeSolo extends boolean | undefined = false>({ dataTest, options, renderInput, value, onChange, getOptionLabel, getOptionKey, isOptionEqualToValue, renderOption, renderValue, filterOptions, loading, loadingText, noOptionsText, disabled, readOnly, error, helperText, size, rowSize, disableClearable, disableCloseOnSelect, popupIcon, autoHeight, multiple, allowSelectAll, selectAllLabel, fullWidth, classes, inputValue: controlledInput, onInputChange, open: controlledOpen, onOpen, onClose, className, wrapperClassName, getOptionDisabled, slotProps, }: StyledSelectAutocompleteProps<T, Multiple, DisableClearable, FreeSolo>): JSX.Element;
139
+ export declare function StyledSelectAutocomplete<T, Multiple extends boolean | undefined = false, DisableClearable extends boolean | undefined = false, FreeSolo extends boolean | undefined = false>({ dataTest, options, renderInput, value, onChange, getOptionLabel, getOptionKey, isOptionEqualToValue, renderOption, renderValue, filterOptions, loading, loadingText, noOptionsText, disabled, readOnly, error, helperText, size, rowSize, disableClearable, disableCloseOnSelect, popupIcon, autoHeight, multiple, allowSelectAll, selectAllLabel, width, fullWidth: _fullWidth, classes, inputValue: controlledInput, onInputChange, open: controlledOpen, onOpen, onClose, className, wrapperClassName, getOptionDisabled, slotProps, }: StyledSelectAutocompleteProps<T, Multiple, DisableClearable, FreeSolo>): JSX.Element;
136
140
  export {};
@@ -1,4 +1,6 @@
1
1
  import { default as React, ChangeEvent, MutableRefObject, ReactNode } from 'react';
2
+ import { UiCoreLocale } from '../../../helpers/uiCoreLocale';
3
+ import { FieldWidth } from '../field-styles';
2
4
  export interface TextareaCommonProps {
3
5
  id?: string;
4
6
  /** @figmaProp Filled — the field text (Body Base 16/lh24, delta-800). */
@@ -12,6 +14,10 @@ export interface TextareaCommonProps {
12
14
  containerClassName?: string;
13
15
  className?: string;
14
16
  dataTest?: string;
17
+ /** Field width pattern step; a text area is Full. */
18
+ width?: FieldWidth;
19
+ /** Language of the character counter. */
20
+ locale?: UiCoreLocale;
15
21
  }
16
22
  export interface TextAreaNotEditableProps {
17
23
  variant?: 'not_editable' | 'view_only';
@@ -1,11 +1,14 @@
1
1
  import { CSSProperties, FocusEventHandler, MouseEventHandler, ReactNode } from 'react';
2
- import { FieldSize } from '../inputs/field-styles';
2
+ import { FieldSize, FieldWidth } from '../inputs/field-styles';
3
3
  export interface FormControlProps {
4
4
  children?: ReactNode;
5
5
  error?: boolean;
6
6
  disabled?: boolean;
7
7
  required?: boolean;
8
8
  size?: FieldSize;
9
+ /** Field width pattern step of the field it wraps; Full by default. */
10
+ width?: FieldWidth;
11
+ /** @deprecated Fields are Full by default; use `width`. */
9
12
  fullWidth?: boolean;
10
13
  /** Accepted for API parity; the design always renders outlined. */
11
14
  variant?: string;
@@ -24,4 +27,4 @@ export interface FormControlProps {
24
27
  * focus/filled state so `StyledInputLabel` can float and `StyledFormHelperText` can colour itself;
25
28
  * `StyledSelect` reports its state into this context. Public props preserved (DEC-003). TASK-401.
26
29
  */
27
- export declare const StyledFormControl: ({ children, error, disabled, required, size, fullWidth, className, style, sx, id, onFocus, onBlur, onClick, }: FormControlProps) => JSX.Element;
30
+ export declare const StyledFormControl: ({ children, error, disabled, required, size, width, fullWidth: _fullWidth, className, style, sx, id, onFocus, onBlur, onClick, }: FormControlProps) => JSX.Element;
@@ -1,5 +1,5 @@
1
1
  import { ReactNode } from 'react';
2
- import { FieldSize } from '../inputs/field-styles';
2
+ import { FieldLabelPlacement, FieldSize } from '../inputs/field-styles';
3
3
  export interface InputLabelProps {
4
4
  children?: ReactNode;
5
5
  htmlFor?: string;
@@ -10,11 +10,13 @@ export interface InputLabelProps {
10
10
  size?: FieldSize;
11
11
  /** Force the shrunk (floated) state; otherwise derived from the surrounding FormControl. */
12
12
  shrink?: boolean;
13
+ /** `above` (default) or `floating`, as on `StyledInputField`. */
14
+ labelPlacement?: FieldLabelPlacement;
13
15
  className?: string;
14
16
  }
15
17
  /**
16
- * Floating field label (replaces MUI `InputLabel`). Reads focus/filled/error/size from the
17
- * surrounding `StyledFormControl`; `shrink` lifts it onto the border. Public props preserved
18
- * (DEC-003). TASK-401.
18
+ * Field label (replaces MUI `InputLabel`), above the field by default. The `floating` placement reads
19
+ * focus/filled/error/size from the surrounding `StyledFormControl`; `shrink` lifts it onto the border.
20
+ * Public props preserved (DEC-003). TASK-401.
19
21
  */
20
- export declare const StyledInputLabel: ({ children, htmlFor, id, required, disabled, error, size, shrink, className, }: InputLabelProps) => JSX.Element;
22
+ export declare const StyledInputLabel: ({ children, htmlFor, id, required, disabled, error, size, shrink, labelPlacement, className, }: InputLabelProps) => JSX.Element;
@@ -1,3 +1,4 @@
1
+ import { FieldWidth } from '../../../../components/inputs/field-styles';
1
2
  import { AriaAttributes } from 'react';
2
3
  import { DayPickerProps as ReactDayPickerProps, Matcher } from 'react-day-picker';
3
4
  export interface IBaseDatePickerInput {
@@ -25,5 +26,6 @@ export interface IBaseDatePickerInput {
25
26
  required?: boolean;
26
27
  minDate?: Date;
27
28
  inputAriaProps?: AriaAttributes;
29
+ width?: FieldWidth;
28
30
  }
29
31
  export declare const BaseDatePickerInput: React.FC<IBaseDatePickerInput>;
@@ -1,5 +1,6 @@
1
1
  import { AriaAttributes } from 'react';
2
2
  import { DayPicker, Matcher } from 'react-day-picker';
3
+ import { FieldWidth } from '../../../components/inputs/field-styles';
3
4
  export type CalendarProps = React.ComponentProps<typeof DayPicker>;
4
5
  type CommonDatePickerProps = {
5
6
  dateFormat?: string;
@@ -13,6 +14,8 @@ type CommonDatePickerProps = {
13
14
  validateOnCalendarClose?: boolean;
14
15
  onValidatedOnce?: () => void;
15
16
  required?: boolean;
17
+ /** Field width pattern step of each date field, calendar button included. Defaults to S. */
18
+ width?: FieldWidth;
16
19
  } & CalendarProps;
17
20
  export interface DatePickerSingleFieldProps {
18
21
  label?: string;
@@ -0,0 +1,7 @@
1
+ import { CSSProperties } from 'react';
2
+ import { StyledTimePickerProps } from '../types';
3
+ /** The step box of the picker root; a legacy numeric `width` is applied on the input instead. */
4
+ export declare const timePickerWidthProps: (width: StyledTimePickerProps["width"]) => {
5
+ "data-field-width"?: string;
6
+ style?: CSSProperties;
7
+ };
@@ -1,4 +1,5 @@
1
1
  import { AriaAttributes, ReactNode } from 'react';
2
+ import { FieldWidth } from '../../../components/inputs/field-styles';
2
3
  /**
3
4
  * @param notBeforeTime - Start time of the range. Selected time must be after this value.
4
5
  */
@@ -10,7 +11,8 @@ export interface StyledTimePickerProps {
10
11
  readOnly?: boolean;
11
12
  inputClassName?: string;
12
13
  dataTest: string;
13
- width?: number;
14
+ /** Field width pattern step; defaults to XS. A number (a fixed `px` width) is deprecated: use a step. */
15
+ width?: FieldWidth | number;
14
16
  error?: boolean;
15
17
  helperText?: ReactNode;
16
18
  label?: string;
@@ -0,0 +1,16 @@
1
+ import { UiCoreLocale } from './uiCoreLocale';
2
+ /** Screen readers hear the count only once the user is close to the limit, not on every keystroke. */
3
+ export declare const isNearCharacterLimit: (length: number, limit: number) => boolean;
4
+ export interface CharacterCounterProps {
5
+ /** Id of the visible count, for the field's `aria-describedby`. */
6
+ id: string;
7
+ length: number;
8
+ limit: number;
9
+ locale?: UiCoreLocale;
10
+ className?: string;
11
+ }
12
+ /**
13
+ * The count of a free-text field with a character limit, such as *12 av 100 tegn* (field width
14
+ * pattern WID-2, Q12), so typed text is never cut without notice.
15
+ */
16
+ export declare const CharacterCounter: ({ id, length, limit, locale, className }: CharacterCounterProps) => JSX.Element;
@@ -0,0 +1 @@
1
+ export {};
@@ -67,6 +67,17 @@ declare global {
67
67
  * {@link useBackCloses} for the React lifecycle wrapper every consumer actually uses.
68
68
  */
69
69
  export declare function registerOverlayBackCloser(requestClose: OverlayBackRequestClose, owner?: Element | null, parentOwner?: Element | null, ownerRef?: RefObject<Element | null>, parentOwnerRef?: RefObject<Element | null>): () => void;
70
+ /**
71
+ * Declares "overlays rendered below me in the React tree are nested inside me", which is what makes
72
+ * one Back press close the inner one first (BACK-2).
73
+ *
74
+ * Needed because an overlay is usually portalled out of its parent's DOM subtree, so DOM
75
+ * containment cannot carry the relationship — only the React tree still knows it. Wrap your
76
+ * surface's children in `<OverlayBackOwnerContext.Provider value={yourOwnerRef}>`, passing the same
77
+ * ref you gave {@link useBackCloses} as `ownerRef`. Every ui-core overlay already does this; an app
78
+ * wiring up an overlay of its own should too, otherwise a ui-core overlay opened inside it is
79
+ * ordered by registration time alone and Back may close the outer surface first.
80
+ */
70
81
  export declare const OverlayBackOwnerContext: import('react').Context<RefObject<Element | null> | undefined>;
71
82
  /**
72
83
  * Act on the topmost registered overlay, honouring the close contract's latch: a top entry whose
@@ -83,6 +94,16 @@ export declare function requestCloseTopOverlayBack(event?: Event): false | void
83
94
  export declare function getOverlayBackStackSize(): number;
84
95
  /** `true` when a live `asma-core-helpers` history adapter (PAT-002) has published `overlayBack.v === 1`. */
85
96
  export declare function hasOverlayBackAdapter(): boolean;
97
+ /**
98
+ * `true` while the history adapter is holding a route change, waiting for the top overlay to close
99
+ * before it navigates (design-plan rule 1 — `push` is deferred, not cancelled).
100
+ *
101
+ * A surface that is *meant* to outlive navigation — the shell-level docked minimizable panels — tells
102
+ * the adapter so by returning `false` from its closer in this window. The adapter reads that as
103
+ * fail-open: it strips the guard, runs the navigation, and leaves the surface open. Anything that
104
+ * should close on navigation simply ignores this and closes as usual.
105
+ */
106
+ export declare function isOverlayBackNavigationPending(): boolean;
86
107
  /** Native close-watcher events are part of the feature and must respect the host kill switch. */
87
108
  export declare function isOverlayBackEnabled(): boolean;
88
109
  /**
@@ -92,10 +113,34 @@ export declare function isOverlayBackEnabled(): boolean;
92
113
  * `asma-core-helpers`' own listener (CON-002/CON-009) and duplicate work the adapter already owns. The
93
114
  * adapter registration itself (see {@link registerOverlayBackCloser}) is driven purely by the stack's
94
115
  * empty/non-empty transitions, not by this hook.
116
+ *
117
+ * Pass `ownerRef` wherever the surface has a stable root element: it is what places the entry
118
+ * relative to a nested child (`isOwnedBy`) and to an open native modal
119
+ * ({@link isCoveredByOpenModalDialog}).
95
120
  */
96
- export declare function useBackCloses({ active, requestClose, ownerRef, }: {
121
+ export interface UseBackClosesOptions {
122
+ /**
123
+ * Register while `true`, unregister while `false` — normally the surface's own "is showing"
124
+ * state. Keep it off for a state that is visible but not dismissable as an overlay (a minimized
125
+ * panel), so Back reaches whatever is behind it instead.
126
+ */
97
127
  active: boolean;
128
+ /**
129
+ * The surface's existing close path — the same function its ✕ or Escape already calls. Never a
130
+ * synthetic key event. Return nothing for a plain dismissal; return a Promise to latch the
131
+ * surface while an async close runs (Back does nothing until it settles); return `false` to
132
+ * refuse this press, which is also how a surface that must survive navigation opts out of the
133
+ * adapter's deferred route change.
134
+ */
98
135
  requestClose: OverlayBackRequestClose;
136
+ /**
137
+ * The surface's own root element — the one that is actually painted on top. Pass it wherever
138
+ * there is one. It is what lets Back order this surface against an overlay nested inside it and
139
+ * against an open native modal `<dialog>`; without it the entry is ordered by registration
140
+ * time alone. For a surface promoted into the top layer via the Popover API, this must be the
141
+ * element carrying the `popover` attribute, not a wrapper around it.
142
+ */
99
143
  ownerRef?: RefObject<Element | null>;
100
- }): void;
144
+ }
145
+ export declare function useBackCloses({ active, requestClose, ownerRef }: UseBackClosesOptions): void;
101
146
  export {};
@@ -65,6 +65,12 @@ export declare function registerOpenModalDialog(dialog: HTMLDialogElement): () =
65
65
  * modal is open. Stale entries (a dialog torn down without its cleanup running) are skipped rather
66
66
  * than trusted, so a missed unregister can never strand an overlay in a detached subtree.
67
67
  */
68
+ /**
69
+ * The newest connected modal `<dialog>` for which `accept` returns true.
70
+ * `getTopmostOpenModalDialog` is the `dialog.open` case. Back arbitration passes a wider accept,
71
+ * because a forced native close sets `open` to false before React unregisters the dialog.
72
+ */
73
+ export declare function findTopmostConnectedModalDialog(accept: (dialog: HTMLDialogElement) => boolean): HTMLDialogElement | undefined;
68
74
  export declare function getTopmostOpenModalDialog(): HTMLDialogElement | undefined;
69
75
  /**
70
76
  * Reactive {@link getTopmostOpenModalDialog} — re-renders the caller when a modal `<dialog>` opens or
@@ -27,6 +27,7 @@ export * from './components/inputs/radio-button';
27
27
  export * from './components/inputs/label';
28
28
  export * from './components/inputs/dynamic-select';
29
29
  export * from './components/inputs/phone-field';
30
+ export * from './components/inputs/form-layout';
30
31
  export * from './datetime/components/date-picker';
31
32
  export * from './datetime/components/time-picker';
32
33
  export * from './datetime/helpers/date.helper';
@@ -58,4 +59,5 @@ export * from './components/feedback/minimizable-dialog';
58
59
  export * from './components/custom/pathfinder-card/PathfinderCard';
59
60
  export * from './hooks/usePopupState';
60
61
  export * from './hooks/useRouteHeadingFocus';
62
+ export { useBackCloses, OverlayBackOwnerContext, type OverlayBackRequestClose, type UseBackClosesOptions, } from './hooks/useBackCloses.hook';
61
63
  export { Paper, ClickAwayListener, Avatar, Skeleton, Container, Stack, FormLabel, Fade, Popper, } from './components/mui-compat';
@@ -170,6 +170,20 @@ declare const _default: {
170
170
  };
171
171
  };
172
172
  };
173
+ width: {
174
+ "field-xs": string;
175
+ "field-s": string;
176
+ "field-m": string;
177
+ "field-full": string;
178
+ "form-column": string;
179
+ };
180
+ maxWidth: {
181
+ "field-xs": string;
182
+ "field-s": string;
183
+ "field-m": string;
184
+ "field-full": string;
185
+ "form-column": string;
186
+ };
173
187
  };
174
188
  };
175
189
  darkMode: "media";
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "3.109.0",
6
+ "version": "3.111.0",
7
7
  "type": "module",
8
8
  "sideEffects": [
9
9
  "**/*.css",
@@ -0,0 +1,8 @@
1
+ :root {
2
+ /* Field width pattern (ASMA-8489, WID-1/WID-3): four widths only, in rem so they grow with text. */
3
+ --field-width-xs: 8rem; /* 128px: postal code, year, age, time */
4
+ --field-width-s: 12.5rem; /* 200px: date, phone number, national ID, amount */
5
+ --field-width-m: 20rem; /* 320px: IBAN, long codes */
6
+ --form-column-width: 37.5rem; /* 600px: the one form column */
7
+ --field-width-full: var(--form-column-width);
8
+ }
@@ -4,6 +4,7 @@
4
4
  @import 'color-variables/fretexTokens.css';
5
5
  @import 'color-variables/jadeTokens.css';
6
6
  @import 'typographyTokens.css';
7
+ @import 'fieldWidthTokens.css';
7
8
 
8
9
  @tailwind base;
9
10
  @tailwind components;
@@ -163,5 +163,19 @@
163
163
  "transform": "rotate(360deg)"
164
164
  }
165
165
  }
166
+ },
167
+ "width": {
168
+ "field-xs": "var(--field-width-xs)",
169
+ "field-s": "var(--field-width-s)",
170
+ "field-m": "var(--field-width-m)",
171
+ "field-full": "var(--field-width-full)",
172
+ "form-column": "var(--form-column-width)"
173
+ },
174
+ "maxWidth": {
175
+ "field-xs": "var(--field-width-xs)",
176
+ "field-s": "var(--field-width-s)",
177
+ "field-m": "var(--field-width-m)",
178
+ "field-full": "var(--field-width-full)",
179
+ "form-column": "var(--form-column-width)"
166
180
  }
167
181
  }