@dloizides/ui-forms 1.9.1 → 1.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +312 -280
- package/dist/index.d.mts +67 -2
- package/dist/index.d.ts +67 -2
- package/dist/index.js +221 -157
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +197 -133
- package/dist/index.mjs.map +1 -1
- package/package.json +110 -107
package/dist/index.d.mts
CHANGED
|
@@ -94,6 +94,21 @@ interface FieldChildContext {
|
|
|
94
94
|
*/
|
|
95
95
|
describedById?: string;
|
|
96
96
|
hasError: boolean;
|
|
97
|
+
/**
|
|
98
|
+
* `id` this field's `<label for>` points at. Assign it to the control (RN `id` → DOM `id`) and
|
|
99
|
+
* clicking the label text focuses the control — a free doubling of the hit area that every
|
|
100
|
+
* field was throwing away.
|
|
101
|
+
*
|
|
102
|
+
* A plain-node child gets this injected automatically (see below); a render-function child has
|
|
103
|
+
* to place it, because only the caller knows which element inside is the actual control.
|
|
104
|
+
*/
|
|
105
|
+
controlId: string;
|
|
106
|
+
/**
|
|
107
|
+
* `id` OF the label element. For a control that `<label for>` cannot address — anything that is
|
|
108
|
+
* not a native form element, e.g. the `<div role="button">` a `SelectControl` trigger renders —
|
|
109
|
+
* point `aria-labelledby` here instead.
|
|
110
|
+
*/
|
|
111
|
+
labelId: string;
|
|
97
112
|
}
|
|
98
113
|
type FieldChildren = React.ReactNode | ((context: FieldChildContext) => React.ReactNode);
|
|
99
114
|
interface FieldProps {
|
|
@@ -356,6 +371,13 @@ declare const FormActions: ({ onSave, onCancel, saveLabel, cancelLabel, saveHint
|
|
|
356
371
|
* soft ring) for free, driven by the active theme.
|
|
357
372
|
*/
|
|
358
373
|
|
|
374
|
+
/**
|
|
375
|
+
* The private `styles.input` that used to live here — padding 10/12, radius 8, border 1 — was a
|
|
376
|
+
* verbatim duplicate of `controlStyles.input`, and `ThemedTextInput` now applies that itself. The
|
|
377
|
+
* duplication is what hid the real defect: because `FormField` supplied metrics, inputs reached
|
|
378
|
+
* through it looked correct, while every DIRECT `ThemedTextInput` caller got an unstyled 19px box
|
|
379
|
+
* and nobody could see the difference from in here.
|
|
380
|
+
*/
|
|
359
381
|
interface FormFieldProps extends Omit<TextInputProps, 'style'> {
|
|
360
382
|
label: string;
|
|
361
383
|
required?: boolean;
|
|
@@ -373,6 +395,20 @@ declare const FormField: ({ label, required, error, containerStyle, ...textInput
|
|
|
373
395
|
* It is the ONE source of truth for the input look: `FormField` renders it, and raw filter inputs
|
|
374
396
|
* (search / country / year / date fallbacks) use it directly instead of forking the styles.
|
|
375
397
|
*
|
|
398
|
+
* ## The box metrics are applied HERE, and that is new
|
|
399
|
+
*
|
|
400
|
+
* This component claimed to be the source of truth for the input look while shipping only
|
|
401
|
+
* COLOURS — no border, no radius, no padding, no height. `FormField` happened to pass its own
|
|
402
|
+
* private copy of those metrics, so every input reached through `FormField` looked right and the
|
|
403
|
+
* gap stayed invisible. Any caller that used `ThemedTextInput` directly — the documented, blessed
|
|
404
|
+
* way to use it — got a raw browser input: **19px tall**, measured in production on an admin CMS
|
|
405
|
+
* where all 10 inputs failed the hit-target floor.
|
|
406
|
+
*
|
|
407
|
+
* So `controlStyles.input` is now the component's own base, ahead of the caller's `style` in the
|
|
408
|
+
* cascade. Callers that already passed the same metrics (`FormField`) are unaffected — the values
|
|
409
|
+
* are identical, which is precisely why the duplication was invisible — and callers that pass
|
|
410
|
+
* their own overrides still win, because their `style` merges after the base.
|
|
411
|
+
*
|
|
376
412
|
* Every existing `TextInputProps` (value, placeholder, onChangeText, testID, accessibility…) is
|
|
377
413
|
* forwarded untouched; `onFocus`/`onBlur` you pass are composed with the internal focus state.
|
|
378
414
|
*/
|
|
@@ -387,6 +423,16 @@ interface ThemedTextInputProps extends TextInputProps {
|
|
|
387
423
|
describedById?: string;
|
|
388
424
|
/** Marks the field required for assistive tech (`aria-required` on web). No-op on native. */
|
|
389
425
|
requiredField?: boolean;
|
|
426
|
+
/**
|
|
427
|
+
* Opt OUT of the shared box metrics (border, radius, padding, the kit's min-height floor) and
|
|
428
|
+
* render colours only — the pre-1.11 behaviour.
|
|
429
|
+
*
|
|
430
|
+
* For the rare control that supplies its own complete box, e.g. an input embedded inside an
|
|
431
|
+
* already-bordered composite where a second border would double up. It is a prop rather than
|
|
432
|
+
* the default because the default has to be the accessible one: the previous default shipped
|
|
433
|
+
* 19px inputs to production precisely because "no metrics" was the silent fallback.
|
|
434
|
+
*/
|
|
435
|
+
unstyledBox?: boolean;
|
|
390
436
|
}
|
|
391
437
|
declare const ThemedTextInput: React.ForwardRefExoticComponent<ThemedTextInputProps & React.RefAttributes<TextInput>>;
|
|
392
438
|
|
|
@@ -576,6 +622,17 @@ interface SelectControlProps {
|
|
|
576
622
|
*/
|
|
577
623
|
accessibilityLabel: string;
|
|
578
624
|
accessibilityHint?: string;
|
|
625
|
+
/**
|
|
626
|
+
* `id` of the `Field` label naming this control — take it from the render-child context's
|
|
627
|
+
* `labelId`. The trigger is a `<div role="button">`, which `<label for>` cannot address (that
|
|
628
|
+
* only works for native form elements), so the association runs the other way: the trigger
|
|
629
|
+
* points AT the label.
|
|
630
|
+
*
|
|
631
|
+
* `accessibilityLabel` still wins for the accessible NAME, deliberately — it is the only one of
|
|
632
|
+
* the two that carries the current selection ("Status: Active"). This adds the structural link
|
|
633
|
+
* without changing a single announcement.
|
|
634
|
+
*/
|
|
635
|
+
labelledById?: string;
|
|
579
636
|
/** PRE-LOCALIZED accessibility hint applied to every option in the menu. */
|
|
580
637
|
optionHint?: string;
|
|
581
638
|
/** Merged LAST onto the anchor wrapper — the consumer always wins over the shared style. */
|
|
@@ -583,7 +640,7 @@ interface SelectControlProps {
|
|
|
583
640
|
/** Trigger is `${testID}-trigger`, menu `${testID}-menu`, options `${testID}-option-${value}`. */
|
|
584
641
|
testID: string;
|
|
585
642
|
}
|
|
586
|
-
declare function SelectControl({ options, value, onChange, placeholder, accessibilityLabel, accessibilityHint, optionHint, style, testID, }: SelectControlProps): React.ReactElement;
|
|
643
|
+
declare function SelectControl({ options, value, onChange, placeholder, accessibilityLabel, accessibilityHint, labelledById, optionHint, style, testID, }: SelectControlProps): React.ReactElement;
|
|
587
644
|
|
|
588
645
|
/**
|
|
589
646
|
* `TypeaheadControl` — a search-as-you-type combobox (aml-v2's CountryPicker, generalized). A
|
|
@@ -702,16 +759,24 @@ interface AnchoredMenuProps {
|
|
|
702
759
|
declare function AnchoredMenu({ options, selectedValue, onSelect, onDismiss, colors, optionHint, testID, anchorRef, }: AnchoredMenuProps): React.ReactElement;
|
|
703
760
|
|
|
704
761
|
declare const controlStyles: {
|
|
705
|
-
/**
|
|
762
|
+
/**
|
|
763
|
+
* The shared bordered box every dense text control renders in.
|
|
764
|
+
*
|
|
765
|
+
* `minHeight` is the kit floor: padding alone produced a ~38px box, and a bare input with no
|
|
766
|
+
* box style at all measured **19px** in production. Height is a hit-target guarantee, so it
|
|
767
|
+
* is stated rather than left to emerge from font size + padding.
|
|
768
|
+
*/
|
|
706
769
|
input: {
|
|
707
770
|
borderWidth: number;
|
|
708
771
|
borderRadius: number;
|
|
709
772
|
paddingHorizontal: number;
|
|
710
773
|
paddingVertical: number;
|
|
711
774
|
fontSize: number;
|
|
775
|
+
minHeight: number;
|
|
712
776
|
};
|
|
713
777
|
/** The select trigger: a bordered field box matching the text inputs + a chevron. */
|
|
714
778
|
selectTrigger: {
|
|
779
|
+
minHeight: number;
|
|
715
780
|
flexDirection: "row";
|
|
716
781
|
alignItems: "center";
|
|
717
782
|
justifyContent: "space-between";
|
package/dist/index.d.ts
CHANGED
|
@@ -94,6 +94,21 @@ interface FieldChildContext {
|
|
|
94
94
|
*/
|
|
95
95
|
describedById?: string;
|
|
96
96
|
hasError: boolean;
|
|
97
|
+
/**
|
|
98
|
+
* `id` this field's `<label for>` points at. Assign it to the control (RN `id` → DOM `id`) and
|
|
99
|
+
* clicking the label text focuses the control — a free doubling of the hit area that every
|
|
100
|
+
* field was throwing away.
|
|
101
|
+
*
|
|
102
|
+
* A plain-node child gets this injected automatically (see below); a render-function child has
|
|
103
|
+
* to place it, because only the caller knows which element inside is the actual control.
|
|
104
|
+
*/
|
|
105
|
+
controlId: string;
|
|
106
|
+
/**
|
|
107
|
+
* `id` OF the label element. For a control that `<label for>` cannot address — anything that is
|
|
108
|
+
* not a native form element, e.g. the `<div role="button">` a `SelectControl` trigger renders —
|
|
109
|
+
* point `aria-labelledby` here instead.
|
|
110
|
+
*/
|
|
111
|
+
labelId: string;
|
|
97
112
|
}
|
|
98
113
|
type FieldChildren = React.ReactNode | ((context: FieldChildContext) => React.ReactNode);
|
|
99
114
|
interface FieldProps {
|
|
@@ -356,6 +371,13 @@ declare const FormActions: ({ onSave, onCancel, saveLabel, cancelLabel, saveHint
|
|
|
356
371
|
* soft ring) for free, driven by the active theme.
|
|
357
372
|
*/
|
|
358
373
|
|
|
374
|
+
/**
|
|
375
|
+
* The private `styles.input` that used to live here — padding 10/12, radius 8, border 1 — was a
|
|
376
|
+
* verbatim duplicate of `controlStyles.input`, and `ThemedTextInput` now applies that itself. The
|
|
377
|
+
* duplication is what hid the real defect: because `FormField` supplied metrics, inputs reached
|
|
378
|
+
* through it looked correct, while every DIRECT `ThemedTextInput` caller got an unstyled 19px box
|
|
379
|
+
* and nobody could see the difference from in here.
|
|
380
|
+
*/
|
|
359
381
|
interface FormFieldProps extends Omit<TextInputProps, 'style'> {
|
|
360
382
|
label: string;
|
|
361
383
|
required?: boolean;
|
|
@@ -373,6 +395,20 @@ declare const FormField: ({ label, required, error, containerStyle, ...textInput
|
|
|
373
395
|
* It is the ONE source of truth for the input look: `FormField` renders it, and raw filter inputs
|
|
374
396
|
* (search / country / year / date fallbacks) use it directly instead of forking the styles.
|
|
375
397
|
*
|
|
398
|
+
* ## The box metrics are applied HERE, and that is new
|
|
399
|
+
*
|
|
400
|
+
* This component claimed to be the source of truth for the input look while shipping only
|
|
401
|
+
* COLOURS — no border, no radius, no padding, no height. `FormField` happened to pass its own
|
|
402
|
+
* private copy of those metrics, so every input reached through `FormField` looked right and the
|
|
403
|
+
* gap stayed invisible. Any caller that used `ThemedTextInput` directly — the documented, blessed
|
|
404
|
+
* way to use it — got a raw browser input: **19px tall**, measured in production on an admin CMS
|
|
405
|
+
* where all 10 inputs failed the hit-target floor.
|
|
406
|
+
*
|
|
407
|
+
* So `controlStyles.input` is now the component's own base, ahead of the caller's `style` in the
|
|
408
|
+
* cascade. Callers that already passed the same metrics (`FormField`) are unaffected — the values
|
|
409
|
+
* are identical, which is precisely why the duplication was invisible — and callers that pass
|
|
410
|
+
* their own overrides still win, because their `style` merges after the base.
|
|
411
|
+
*
|
|
376
412
|
* Every existing `TextInputProps` (value, placeholder, onChangeText, testID, accessibility…) is
|
|
377
413
|
* forwarded untouched; `onFocus`/`onBlur` you pass are composed with the internal focus state.
|
|
378
414
|
*/
|
|
@@ -387,6 +423,16 @@ interface ThemedTextInputProps extends TextInputProps {
|
|
|
387
423
|
describedById?: string;
|
|
388
424
|
/** Marks the field required for assistive tech (`aria-required` on web). No-op on native. */
|
|
389
425
|
requiredField?: boolean;
|
|
426
|
+
/**
|
|
427
|
+
* Opt OUT of the shared box metrics (border, radius, padding, the kit's min-height floor) and
|
|
428
|
+
* render colours only — the pre-1.11 behaviour.
|
|
429
|
+
*
|
|
430
|
+
* For the rare control that supplies its own complete box, e.g. an input embedded inside an
|
|
431
|
+
* already-bordered composite where a second border would double up. It is a prop rather than
|
|
432
|
+
* the default because the default has to be the accessible one: the previous default shipped
|
|
433
|
+
* 19px inputs to production precisely because "no metrics" was the silent fallback.
|
|
434
|
+
*/
|
|
435
|
+
unstyledBox?: boolean;
|
|
390
436
|
}
|
|
391
437
|
declare const ThemedTextInput: React.ForwardRefExoticComponent<ThemedTextInputProps & React.RefAttributes<TextInput>>;
|
|
392
438
|
|
|
@@ -576,6 +622,17 @@ interface SelectControlProps {
|
|
|
576
622
|
*/
|
|
577
623
|
accessibilityLabel: string;
|
|
578
624
|
accessibilityHint?: string;
|
|
625
|
+
/**
|
|
626
|
+
* `id` of the `Field` label naming this control — take it from the render-child context's
|
|
627
|
+
* `labelId`. The trigger is a `<div role="button">`, which `<label for>` cannot address (that
|
|
628
|
+
* only works for native form elements), so the association runs the other way: the trigger
|
|
629
|
+
* points AT the label.
|
|
630
|
+
*
|
|
631
|
+
* `accessibilityLabel` still wins for the accessible NAME, deliberately — it is the only one of
|
|
632
|
+
* the two that carries the current selection ("Status: Active"). This adds the structural link
|
|
633
|
+
* without changing a single announcement.
|
|
634
|
+
*/
|
|
635
|
+
labelledById?: string;
|
|
579
636
|
/** PRE-LOCALIZED accessibility hint applied to every option in the menu. */
|
|
580
637
|
optionHint?: string;
|
|
581
638
|
/** Merged LAST onto the anchor wrapper — the consumer always wins over the shared style. */
|
|
@@ -583,7 +640,7 @@ interface SelectControlProps {
|
|
|
583
640
|
/** Trigger is `${testID}-trigger`, menu `${testID}-menu`, options `${testID}-option-${value}`. */
|
|
584
641
|
testID: string;
|
|
585
642
|
}
|
|
586
|
-
declare function SelectControl({ options, value, onChange, placeholder, accessibilityLabel, accessibilityHint, optionHint, style, testID, }: SelectControlProps): React.ReactElement;
|
|
643
|
+
declare function SelectControl({ options, value, onChange, placeholder, accessibilityLabel, accessibilityHint, labelledById, optionHint, style, testID, }: SelectControlProps): React.ReactElement;
|
|
587
644
|
|
|
588
645
|
/**
|
|
589
646
|
* `TypeaheadControl` — a search-as-you-type combobox (aml-v2's CountryPicker, generalized). A
|
|
@@ -702,16 +759,24 @@ interface AnchoredMenuProps {
|
|
|
702
759
|
declare function AnchoredMenu({ options, selectedValue, onSelect, onDismiss, colors, optionHint, testID, anchorRef, }: AnchoredMenuProps): React.ReactElement;
|
|
703
760
|
|
|
704
761
|
declare const controlStyles: {
|
|
705
|
-
/**
|
|
762
|
+
/**
|
|
763
|
+
* The shared bordered box every dense text control renders in.
|
|
764
|
+
*
|
|
765
|
+
* `minHeight` is the kit floor: padding alone produced a ~38px box, and a bare input with no
|
|
766
|
+
* box style at all measured **19px** in production. Height is a hit-target guarantee, so it
|
|
767
|
+
* is stated rather than left to emerge from font size + padding.
|
|
768
|
+
*/
|
|
706
769
|
input: {
|
|
707
770
|
borderWidth: number;
|
|
708
771
|
borderRadius: number;
|
|
709
772
|
paddingHorizontal: number;
|
|
710
773
|
paddingVertical: number;
|
|
711
774
|
fontSize: number;
|
|
775
|
+
minHeight: number;
|
|
712
776
|
};
|
|
713
777
|
/** The select trigger: a bordered field box matching the text inputs + a chevron. */
|
|
714
778
|
selectTrigger: {
|
|
779
|
+
minHeight: number;
|
|
715
780
|
flexDirection: "row";
|
|
716
781
|
alignItems: "center";
|
|
717
782
|
justifyContent: "space-between";
|