@dloizides/ui-forms 1.13.4 → 1.14.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.
- package/CHANGELOG.md +22 -0
- package/dist/index.d.mts +54 -3
- package/dist/index.d.ts +54 -3
- package/dist/index.js +326 -147
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +327 -150
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.14.0
|
|
4
|
+
|
|
5
|
+
**`FormField` gained an opt-in `recentKey` — remember the last N submitted values and offer them on focus.**
|
|
6
|
+
|
|
7
|
+
A search / filter / lookup box that the user retypes the same handful of values into now gets a
|
|
8
|
+
"recent values" dropdown for free. Set `recentKey="<namespace>"` on a `FormField` and the values it
|
|
9
|
+
submits are remembered (per key) and surfaced in a menu on focus; picking one fills the input. A
|
|
10
|
+
value is recorded on submit (Enter) **and** on blur-with-content, so both "hit enter" and "typed then
|
|
11
|
+
moved on" are captured. The dropdown reuses the kit's `AnchoredMenu` (portalled to `document.body`, so
|
|
12
|
+
it is never painted behind a table / card / the next field) with full a11y (menu name, per-row label +
|
|
13
|
+
hint + role).
|
|
14
|
+
|
|
15
|
+
Persistence is `localStorage` under `@dloizides/ui-forms:recent:<recentKey>`; the list de-dupes
|
|
16
|
+
case-insensitively, promotes a repeat to the front, and caps at `recentMax` (default 10). On native /
|
|
17
|
+
SSR / when storage is disabled it is a silent no-op (recents are always `[]`), and every storage
|
|
18
|
+
access is wrapped so a quota or privacy-mode failure can never throw into render.
|
|
19
|
+
|
|
20
|
+
**Additive and backward-compatible.** Leave `recentKey` unset — the default — and the field behaves
|
|
21
|
+
exactly as before: nothing rendered, no state/refs/effects, no storage touched. NEVER set it on a
|
|
22
|
+
password / secure field. The engine is also exported standalone as `useRecentValues(key, max?)` for
|
|
23
|
+
bespoke recents surfaces, and `AnchoredMenu` gained an optional `accessibilityLabel` for naming the menu.
|
|
24
|
+
|
|
3
25
|
## 1.13.3
|
|
4
26
|
|
|
5
27
|
**Placeholder text was too dark — it read like an already-filled value.**
|
package/dist/index.d.mts
CHANGED
|
@@ -394,8 +394,21 @@ interface FormFieldProps extends Omit<TextInputProps, 'style'> {
|
|
|
394
394
|
required?: boolean;
|
|
395
395
|
error?: string;
|
|
396
396
|
containerStyle?: ViewStyle;
|
|
397
|
+
/**
|
|
398
|
+
* OPT-IN "recent values" behaviour. When set, the values submitted through this field are
|
|
399
|
+
* remembered (per this key) and offered in a dropdown on focus; picking one fills the input.
|
|
400
|
+
* Values are recorded on submit (Enter) and on blur-with-content. Persisted to `localStorage`
|
|
401
|
+
* under `@dloizides/ui-forms:recent:<recentKey>`; a silent no-op on native / when storage is
|
|
402
|
+
* unavailable.
|
|
403
|
+
*
|
|
404
|
+
* Leave it UNSET (the default) and the field behaves exactly as before — nothing rendered, no
|
|
405
|
+
* storage touched. NEVER set it on a password / secure field: recents must not persist secrets.
|
|
406
|
+
*/
|
|
407
|
+
recentKey?: string;
|
|
408
|
+
/** Cap on remembered values when {@link recentKey} is set (most-recent-first). Default 10. */
|
|
409
|
+
recentMax?: number;
|
|
397
410
|
}
|
|
398
|
-
declare const FormField: ({ label, labelHidden, required, error, containerStyle, ...textInputProps }: FormFieldProps) => React.ReactElement;
|
|
411
|
+
declare const FormField: ({ label, labelHidden, required, error, containerStyle, recentKey, recentMax, ...textInputProps }: FormFieldProps) => React.ReactElement;
|
|
399
412
|
|
|
400
413
|
/**
|
|
401
414
|
* `ThemedTextInput` — the shared, readable + lively text-input primitive. A thin `<TextInput>`
|
|
@@ -479,6 +492,38 @@ interface ThemedInput {
|
|
|
479
492
|
}
|
|
480
493
|
declare function useThemedInput(options?: UseThemedInputOptions): ThemedInput;
|
|
481
494
|
|
|
495
|
+
interface RecentValues {
|
|
496
|
+
/** The remembered values, most-recent-first (never longer than `max`; `[]` when storage is unavailable). */
|
|
497
|
+
recents: string[];
|
|
498
|
+
/** Fold a value into the list (trim / de-dupe / promote / cap) and persist. Empty values are ignored. */
|
|
499
|
+
remember: (value: string) => void;
|
|
500
|
+
/** Forget every remembered value for this key and clear it from storage. */
|
|
501
|
+
clear: () => void;
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* @param key logical name for this input's history (namespaced under `@dloizides/ui-forms:recent:`).
|
|
505
|
+
* @param max most-recent-first cap on remembered values. Default {@link DEFAULT_MAX_RECENTS}.
|
|
506
|
+
*/
|
|
507
|
+
declare function useRecentValues(key: string, max?: number): RecentValues;
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* `recentValuesCore` — the pure + storage-facing internals of {@link useRecentValues}, kept out of
|
|
511
|
+
* the hook file so the list algebra (trim / de-dupe / promote / cap) and the `localStorage` access
|
|
512
|
+
* are each unit-testable in isolation.
|
|
513
|
+
*
|
|
514
|
+
* ## RN-web storage reality
|
|
515
|
+
*
|
|
516
|
+
* `ui-forms` runs on react-native-web, so a component using this may mount on web (where
|
|
517
|
+
* `window.localStorage` exists), on native (where it does NOT), or under SSR / a hardened browser
|
|
518
|
+
* where touching `window.localStorage` THROWS (disabled cookies, privacy mode, quota). Every access
|
|
519
|
+
* therefore goes through {@link getStorage}, which returns `null` rather than throwing, and every
|
|
520
|
+
* read/write is wrapped so a storage failure degrades to an in-memory-only no-op instead of
|
|
521
|
+
* exploding inside render. On native / when storage is unavailable the feature is simply inert and
|
|
522
|
+
* the recents list is always `[]`.
|
|
523
|
+
*/
|
|
524
|
+
/** Default cap on remembered values (most-recent-first). */
|
|
525
|
+
declare const DEFAULT_MAX_RECENTS = 10;
|
|
526
|
+
|
|
482
527
|
/**
|
|
483
528
|
* Chip/pill selector for selecting one or multiple options.
|
|
484
529
|
*
|
|
@@ -770,6 +815,12 @@ interface AnchoredMenuProps {
|
|
|
770
815
|
colors: AnchoredMenuColors;
|
|
771
816
|
/** PRE-LOCALIZED accessibility hint applied to every option. */
|
|
772
817
|
optionHint: string;
|
|
818
|
+
/**
|
|
819
|
+
* PRE-LOCALIZED accessible NAME for the menu container itself (e.g. "Recent values"). Optional and
|
|
820
|
+
* additive — omitted, the menu keeps only its `role="menu"` as before, so existing callers are
|
|
821
|
+
* unaffected. Supplied, a screen reader announces the list's purpose, not just "menu".
|
|
822
|
+
*/
|
|
823
|
+
accessibilityLabel?: string;
|
|
773
824
|
/** Base testID: the menu is `${testID}-menu`, each option `${testID}-option-${value}`. */
|
|
774
825
|
testID: string;
|
|
775
826
|
/** Node whose clicks count as "inside" (the anchor) so its own press doesn't self-dismiss. */
|
|
@@ -788,7 +839,7 @@ interface AnchoredMenuProps {
|
|
|
788
839
|
*/
|
|
789
840
|
usePortal?: boolean;
|
|
790
841
|
}
|
|
791
|
-
declare function AnchoredMenu({ options, selectedValue, onSelect, onDismiss, colors, optionHint, testID, anchorRef, usePortal, }: AnchoredMenuProps): React.ReactElement;
|
|
842
|
+
declare function AnchoredMenu({ options, selectedValue, onSelect, onDismiss, colors, optionHint, accessibilityLabel, testID, anchorRef, usePortal, }: AnchoredMenuProps): React.ReactElement;
|
|
792
843
|
|
|
793
844
|
declare const controlStyles: {
|
|
794
845
|
/**
|
|
@@ -893,4 +944,4 @@ declare function suggestOptions(options: readonly ControlOption[], query: string
|
|
|
893
944
|
/** True when `text` is non-empty yet matches no option's value or label exactly (case-insensitive). */
|
|
894
945
|
declare function isUnmatched(options: readonly ControlOption[], text: string): boolean;
|
|
895
946
|
|
|
896
|
-
export { AnchoredMenu, type AnchoredMenuColors, type AnchoredMenuProps, type ChipOption, ChipSelector, type ChipSelectorProps, type ChipVariant, type ControlOption, DEFAULT_OPTIONAL_LABEL, DEFAULT_TYPEAHEAD_MAX_SUGGESTIONS, DEFAULT_TYPEAHEAD_MIN_CHARS, DateRangeControl, type DateRangeControlProps, type DateRangeValue, Field, type FieldChildContext, type FieldChildren, type FieldLabelVariant, type FieldMarker, type FieldProps, type FieldSpacing, FormActions, type FormActionsProps, FormCell, type FormCellProps, FormCheckbox, type FormCheckboxProps, FormField, type FormFieldProps, FormGrid, type FormGridProps, FormSection, type FormSectionProps, FormSwitch, type FormSwitchProps, SelectControl, type SelectControlProps, type ThemedInput, ThemedTextInput, type ThemedTextInputProps, TypeaheadControl, type TypeaheadControlProps, type UseThemedInputOptions, controlStyles, isUnmatched, suggestOptions, useThemedInput };
|
|
947
|
+
export { AnchoredMenu, type AnchoredMenuColors, type AnchoredMenuProps, type ChipOption, ChipSelector, type ChipSelectorProps, type ChipVariant, type ControlOption, DEFAULT_MAX_RECENTS, DEFAULT_OPTIONAL_LABEL, DEFAULT_TYPEAHEAD_MAX_SUGGESTIONS, DEFAULT_TYPEAHEAD_MIN_CHARS, DateRangeControl, type DateRangeControlProps, type DateRangeValue, Field, type FieldChildContext, type FieldChildren, type FieldLabelVariant, type FieldMarker, type FieldProps, type FieldSpacing, FormActions, type FormActionsProps, FormCell, type FormCellProps, FormCheckbox, type FormCheckboxProps, FormField, type FormFieldProps, FormGrid, type FormGridProps, FormSection, type FormSectionProps, FormSwitch, type FormSwitchProps, type RecentValues, SelectControl, type SelectControlProps, type ThemedInput, ThemedTextInput, type ThemedTextInputProps, TypeaheadControl, type TypeaheadControlProps, type UseThemedInputOptions, controlStyles, isUnmatched, suggestOptions, useRecentValues, useThemedInput };
|
package/dist/index.d.ts
CHANGED
|
@@ -394,8 +394,21 @@ interface FormFieldProps extends Omit<TextInputProps, 'style'> {
|
|
|
394
394
|
required?: boolean;
|
|
395
395
|
error?: string;
|
|
396
396
|
containerStyle?: ViewStyle;
|
|
397
|
+
/**
|
|
398
|
+
* OPT-IN "recent values" behaviour. When set, the values submitted through this field are
|
|
399
|
+
* remembered (per this key) and offered in a dropdown on focus; picking one fills the input.
|
|
400
|
+
* Values are recorded on submit (Enter) and on blur-with-content. Persisted to `localStorage`
|
|
401
|
+
* under `@dloizides/ui-forms:recent:<recentKey>`; a silent no-op on native / when storage is
|
|
402
|
+
* unavailable.
|
|
403
|
+
*
|
|
404
|
+
* Leave it UNSET (the default) and the field behaves exactly as before — nothing rendered, no
|
|
405
|
+
* storage touched. NEVER set it on a password / secure field: recents must not persist secrets.
|
|
406
|
+
*/
|
|
407
|
+
recentKey?: string;
|
|
408
|
+
/** Cap on remembered values when {@link recentKey} is set (most-recent-first). Default 10. */
|
|
409
|
+
recentMax?: number;
|
|
397
410
|
}
|
|
398
|
-
declare const FormField: ({ label, labelHidden, required, error, containerStyle, ...textInputProps }: FormFieldProps) => React.ReactElement;
|
|
411
|
+
declare const FormField: ({ label, labelHidden, required, error, containerStyle, recentKey, recentMax, ...textInputProps }: FormFieldProps) => React.ReactElement;
|
|
399
412
|
|
|
400
413
|
/**
|
|
401
414
|
* `ThemedTextInput` — the shared, readable + lively text-input primitive. A thin `<TextInput>`
|
|
@@ -479,6 +492,38 @@ interface ThemedInput {
|
|
|
479
492
|
}
|
|
480
493
|
declare function useThemedInput(options?: UseThemedInputOptions): ThemedInput;
|
|
481
494
|
|
|
495
|
+
interface RecentValues {
|
|
496
|
+
/** The remembered values, most-recent-first (never longer than `max`; `[]` when storage is unavailable). */
|
|
497
|
+
recents: string[];
|
|
498
|
+
/** Fold a value into the list (trim / de-dupe / promote / cap) and persist. Empty values are ignored. */
|
|
499
|
+
remember: (value: string) => void;
|
|
500
|
+
/** Forget every remembered value for this key and clear it from storage. */
|
|
501
|
+
clear: () => void;
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* @param key logical name for this input's history (namespaced under `@dloizides/ui-forms:recent:`).
|
|
505
|
+
* @param max most-recent-first cap on remembered values. Default {@link DEFAULT_MAX_RECENTS}.
|
|
506
|
+
*/
|
|
507
|
+
declare function useRecentValues(key: string, max?: number): RecentValues;
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* `recentValuesCore` — the pure + storage-facing internals of {@link useRecentValues}, kept out of
|
|
511
|
+
* the hook file so the list algebra (trim / de-dupe / promote / cap) and the `localStorage` access
|
|
512
|
+
* are each unit-testable in isolation.
|
|
513
|
+
*
|
|
514
|
+
* ## RN-web storage reality
|
|
515
|
+
*
|
|
516
|
+
* `ui-forms` runs on react-native-web, so a component using this may mount on web (where
|
|
517
|
+
* `window.localStorage` exists), on native (where it does NOT), or under SSR / a hardened browser
|
|
518
|
+
* where touching `window.localStorage` THROWS (disabled cookies, privacy mode, quota). Every access
|
|
519
|
+
* therefore goes through {@link getStorage}, which returns `null` rather than throwing, and every
|
|
520
|
+
* read/write is wrapped so a storage failure degrades to an in-memory-only no-op instead of
|
|
521
|
+
* exploding inside render. On native / when storage is unavailable the feature is simply inert and
|
|
522
|
+
* the recents list is always `[]`.
|
|
523
|
+
*/
|
|
524
|
+
/** Default cap on remembered values (most-recent-first). */
|
|
525
|
+
declare const DEFAULT_MAX_RECENTS = 10;
|
|
526
|
+
|
|
482
527
|
/**
|
|
483
528
|
* Chip/pill selector for selecting one or multiple options.
|
|
484
529
|
*
|
|
@@ -770,6 +815,12 @@ interface AnchoredMenuProps {
|
|
|
770
815
|
colors: AnchoredMenuColors;
|
|
771
816
|
/** PRE-LOCALIZED accessibility hint applied to every option. */
|
|
772
817
|
optionHint: string;
|
|
818
|
+
/**
|
|
819
|
+
* PRE-LOCALIZED accessible NAME for the menu container itself (e.g. "Recent values"). Optional and
|
|
820
|
+
* additive — omitted, the menu keeps only its `role="menu"` as before, so existing callers are
|
|
821
|
+
* unaffected. Supplied, a screen reader announces the list's purpose, not just "menu".
|
|
822
|
+
*/
|
|
823
|
+
accessibilityLabel?: string;
|
|
773
824
|
/** Base testID: the menu is `${testID}-menu`, each option `${testID}-option-${value}`. */
|
|
774
825
|
testID: string;
|
|
775
826
|
/** Node whose clicks count as "inside" (the anchor) so its own press doesn't self-dismiss. */
|
|
@@ -788,7 +839,7 @@ interface AnchoredMenuProps {
|
|
|
788
839
|
*/
|
|
789
840
|
usePortal?: boolean;
|
|
790
841
|
}
|
|
791
|
-
declare function AnchoredMenu({ options, selectedValue, onSelect, onDismiss, colors, optionHint, testID, anchorRef, usePortal, }: AnchoredMenuProps): React.ReactElement;
|
|
842
|
+
declare function AnchoredMenu({ options, selectedValue, onSelect, onDismiss, colors, optionHint, accessibilityLabel, testID, anchorRef, usePortal, }: AnchoredMenuProps): React.ReactElement;
|
|
792
843
|
|
|
793
844
|
declare const controlStyles: {
|
|
794
845
|
/**
|
|
@@ -893,4 +944,4 @@ declare function suggestOptions(options: readonly ControlOption[], query: string
|
|
|
893
944
|
/** True when `text` is non-empty yet matches no option's value or label exactly (case-insensitive). */
|
|
894
945
|
declare function isUnmatched(options: readonly ControlOption[], text: string): boolean;
|
|
895
946
|
|
|
896
|
-
export { AnchoredMenu, type AnchoredMenuColors, type AnchoredMenuProps, type ChipOption, ChipSelector, type ChipSelectorProps, type ChipVariant, type ControlOption, DEFAULT_OPTIONAL_LABEL, DEFAULT_TYPEAHEAD_MAX_SUGGESTIONS, DEFAULT_TYPEAHEAD_MIN_CHARS, DateRangeControl, type DateRangeControlProps, type DateRangeValue, Field, type FieldChildContext, type FieldChildren, type FieldLabelVariant, type FieldMarker, type FieldProps, type FieldSpacing, FormActions, type FormActionsProps, FormCell, type FormCellProps, FormCheckbox, type FormCheckboxProps, FormField, type FormFieldProps, FormGrid, type FormGridProps, FormSection, type FormSectionProps, FormSwitch, type FormSwitchProps, SelectControl, type SelectControlProps, type ThemedInput, ThemedTextInput, type ThemedTextInputProps, TypeaheadControl, type TypeaheadControlProps, type UseThemedInputOptions, controlStyles, isUnmatched, suggestOptions, useThemedInput };
|
|
947
|
+
export { AnchoredMenu, type AnchoredMenuColors, type AnchoredMenuProps, type ChipOption, ChipSelector, type ChipSelectorProps, type ChipVariant, type ControlOption, DEFAULT_MAX_RECENTS, DEFAULT_OPTIONAL_LABEL, DEFAULT_TYPEAHEAD_MAX_SUGGESTIONS, DEFAULT_TYPEAHEAD_MIN_CHARS, DateRangeControl, type DateRangeControlProps, type DateRangeValue, Field, type FieldChildContext, type FieldChildren, type FieldLabelVariant, type FieldMarker, type FieldProps, type FieldSpacing, FormActions, type FormActionsProps, FormCell, type FormCellProps, FormCheckbox, type FormCheckboxProps, FormField, type FormFieldProps, FormGrid, type FormGridProps, FormSection, type FormSectionProps, FormSwitch, type FormSwitchProps, type RecentValues, SelectControl, type SelectControlProps, type ThemedInput, ThemedTextInput, type ThemedTextInputProps, TypeaheadControl, type TypeaheadControlProps, type UseThemedInputOptions, controlStyles, isUnmatched, suggestOptions, useRecentValues, useThemedInput };
|