@dloizides/ui-forms 1.7.0 → 1.8.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/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { StyleProp, TextStyle, ViewStyle, TextInputProps, TextInput } from 'react-native';
2
+ import { StyleProp, TextStyle, ViewStyle, TextInputProps, TextInput, View } from 'react-native';
3
3
 
4
4
  /**
5
5
  * The TWO legitimate label voices, per the shared forms contract
@@ -351,4 +351,277 @@ interface FormCheckboxProps {
351
351
  }
352
352
  declare const FormCheckbox: ({ label, value, onValueChange, onChange, hint, description, disabled, containerStyle, testID, accessibilityLabel, accessibilityHint, }: FormCheckboxProps) => React.ReactElement;
353
353
 
354
- export { type ChipOption, ChipSelector, type ChipSelectorProps, type ChipVariant, DEFAULT_OPTIONAL_LABEL, Field, type FieldChildContext, type FieldChildren, type FieldLabelVariant, type FieldMarker, type FieldProps, FormActions, type FormActionsProps, FormCheckbox, type FormCheckboxProps, FormField, type FormFieldProps, FormSwitch, type FormSwitchProps, type ThemedInput, ThemedTextInput, type ThemedTextInputProps, type UseThemedInputOptions, useThemedInput };
354
+ /**
355
+ * Shared value types for the dense CONTROLS (select / typeahead / date-range) promoted out of
356
+ * `@dloizides/ui-tables`' private filter bar.
357
+ *
358
+ * They live here, not in `ui-tables`, because a control's option list and a date range are form
359
+ * concepts — a filter bar is only one surface that happens to compose them. `ui-tables` aliases
360
+ * its long-standing public names (`FilterOption`, `DateRangeValue`) onto these, so there is ONE
361
+ * definition and the two can never drift into structurally-compatible-but-separate twins.
362
+ */
363
+ /** A single choice in a select / typeahead. `label` is PRE-LOCALIZED by the caller. */
364
+ interface ControlOption {
365
+ readonly label: string;
366
+ readonly value: string;
367
+ }
368
+ /** The inclusive from/to value of a date-range control (each an ISO `YYYY-MM-DD` string, or ''). */
369
+ interface DateRangeValue {
370
+ readonly from: string;
371
+ readonly to: string;
372
+ }
373
+
374
+ /**
375
+ * `SelectControl` — a single-select enum/status dropdown. A bordered trigger shows the selected
376
+ * option's label (or the placeholder when unset) plus a chevron; pressing it opens the shared
377
+ * {@link AnchoredMenu}. Selecting fires `onChange` and closes.
378
+ *
379
+ * Promoted out of `@dloizides/ui-tables`' private `Filters/fields/SelectField`, where six portals
380
+ * could see it working and none could import it — which is why the fleet grew SIX independent
381
+ * select implementations.
382
+ *
383
+ * The control is LABEL-FREE by design: wrap it in a `Field` (with `labelVariant="control"` on a
384
+ * dense surface) to get the label, hint and error slots. That keeps one label implementation for
385
+ * every control instead of one per control.
386
+ *
387
+ * i18n: this package never calls `t`. `accessibilityLabel`, `placeholder`, `accessibilityHint`
388
+ * and `optionHint` are PRE-LOCALIZED strings supplied by the caller — the only shape that can
389
+ * serve both aml-v2's positional `{0}` `@dloizides/i18n` and the other six portals' i18next
390
+ * `{{p1}}` from one component.
391
+ */
392
+
393
+ interface SelectControlProps {
394
+ options: readonly ControlOption[];
395
+ /** The selected option's `value`. `''` (or any unmatched value) renders the placeholder. */
396
+ value: string;
397
+ onChange: (value: string) => void;
398
+ /** PRE-LOCALIZED text shown when the value matches no option. */
399
+ placeholder?: string;
400
+ /**
401
+ * PRE-LOCALIZED accessible NAME of the trigger. It REPLACES the trigger's visible text for a
402
+ * screen reader, so it must carry the SELECTION too — "Status: Active", not just "Status",
403
+ * which announces the control but silently hides its value.
404
+ */
405
+ accessibilityLabel: string;
406
+ accessibilityHint?: string;
407
+ /** PRE-LOCALIZED accessibility hint applied to every option in the menu. */
408
+ optionHint?: string;
409
+ /** Merged LAST onto the anchor wrapper — the consumer always wins over the shared style. */
410
+ style?: StyleProp<ViewStyle>;
411
+ /** Trigger is `${testID}-trigger`, menu `${testID}-menu`, options `${testID}-option-${value}`. */
412
+ testID: string;
413
+ }
414
+ declare function SelectControl({ options, value, onChange, placeholder, accessibilityLabel, accessibilityHint, optionHint, style, testID, }: SelectControlProps): React.ReactElement;
415
+
416
+ /**
417
+ * `TypeaheadControl` — a search-as-you-type combobox (aml-v2's CountryPicker, generalized). A
418
+ * text input plus a floating {@link AnchoredMenu} of ranked suggestions ({@link suggestOptions});
419
+ * picking one fills the canonical label.
420
+ *
421
+ * Free typing is PRESERVED: the value is the raw text, so a caller can accept "CY", "cyprus" or
422
+ * "Cyprus (CY)" and normalise it themselves at query time, exactly as aml does. An optional
423
+ * PRE-LOCALIZED `error` shows a persistent banner and tints the border.
424
+ *
425
+ * Promoted out of `@dloizides/ui-tables`' private `Filters/fields/TypeaheadField`.
426
+ *
427
+ * Label-free by design — wrap in a `Field` for the label/hint slots.
428
+ */
429
+
430
+ /** Default min characters before suggestions surface. */
431
+ declare const DEFAULT_TYPEAHEAD_MIN_CHARS = 1;
432
+ /** Default cap on rendered suggestions. */
433
+ declare const DEFAULT_TYPEAHEAD_MAX_SUGGESTIONS = 8;
434
+ interface TypeaheadControlProps {
435
+ options: readonly ControlOption[];
436
+ /** The raw text in the box — NOT necessarily an option value. */
437
+ value: string;
438
+ onChange: (value: string) => void;
439
+ /** Called on Enter/submit (after the menu closes). Omit if the surface has nothing to commit. */
440
+ onSubmit?: () => void;
441
+ placeholder?: string;
442
+ /** PRE-LOCALIZED accessible name of the input. */
443
+ accessibilityLabel: string;
444
+ accessibilityHint?: string;
445
+ /** PRE-LOCALIZED accessibility hint applied to every suggestion. */
446
+ optionHint?: string;
447
+ /** PRE-LOCALIZED inline error (e.g. "Unrecognised country"). Shows a persistent banner. */
448
+ error?: string;
449
+ /** Minimum characters before suggestions surface. Default 1. */
450
+ minChars?: number;
451
+ /** Cap on rendered suggestions. Default 8. */
452
+ maxSuggestions?: number;
453
+ /** Merged LAST onto the anchor wrapper. */
454
+ style?: StyleProp<ViewStyle>;
455
+ /** Input is `${testID}-input`, menu `${testID}-menu`, error `${testID}-error`. */
456
+ testID: string;
457
+ }
458
+ declare function TypeaheadControl({ options, value, onChange, onSubmit, placeholder, accessibilityLabel, accessibilityHint, optionHint, error, minChars, maxSuggestions, style, testID, }: TypeaheadControlProps): React.ReactElement;
459
+
460
+ /**
461
+ * `DateRangeControl` — an inclusive from/to pair (aml cases `from`/`to`, kefi audit `from`/`to`).
462
+ * Each side is a `YYYY-MM-DD` text input; on web the browser's native date UI is offered via
463
+ * `type="date"` (a react-native-web `TextInput` passes unknown props to the DOM input). Editing
464
+ * either side patches ONLY that side, so the other survives the edit.
465
+ *
466
+ * Promoted out of `@dloizides/ui-tables`' private `Filters/fields/DateRangeField`, which is why
467
+ * the fleet grew FIVE independent date fields.
468
+ *
469
+ * Label-free by design — the From/To sub-labels name the two HALVES; wrap in a `Field` for the
470
+ * range's own label.
471
+ */
472
+
473
+ interface DateRangeControlProps {
474
+ value: DateRangeValue;
475
+ onChange: (value: DateRangeValue) => void;
476
+ /** Called on Enter/submit in either side. */
477
+ onSubmit?: () => void;
478
+ /** PRE-LOCALIZED sub-label for the FROM input (e.g. "From"). Also its accessible name. */
479
+ fromLabel: string;
480
+ /** PRE-LOCALIZED sub-label for the TO input (e.g. "To"). Also its accessible name. */
481
+ toLabel: string;
482
+ fromPlaceholder?: string;
483
+ toPlaceholder?: string;
484
+ /** PRE-LOCALIZED hint applied to BOTH inputs. */
485
+ accessibilityHint?: string;
486
+ /** Merged LAST onto the row wrapper. */
487
+ style?: StyleProp<ViewStyle>;
488
+ /** Row is `${testID}-range`, inputs `${testID}-from` / `${testID}-to`. */
489
+ testID: string;
490
+ }
491
+ declare function DateRangeControl({ value, onChange, onSubmit, fromLabel, toLabel, fromPlaceholder, toPlaceholder, accessibilityHint, style, testID, }: DateRangeControlProps): React.ReactElement;
492
+
493
+ /**
494
+ * `AnchoredMenu` — the floating option list shared by `SelectControl` and `TypeaheadControl`.
495
+ *
496
+ * Renders an in-tree, absolutely-positioned popover under its anchor (the pattern aml-v2's
497
+ * CountryPicker proved: `position:absolute; top:100%; zIndex; elevation`), so it floats over
498
+ * sibling fields without a portal. On web it dismisses on outside-click / Escape; on native the
499
+ * caller closes it on blur/select. Purely presentational + theme-flat, so it stays reusable.
500
+ *
501
+ * Promoted verbatim out of `@dloizides/ui-tables`' private `Filters/components/AnchoredMenu`.
502
+ *
503
+ * Deliberately in-tree (no `ui-layout` dependency). That was a considered choice in `ui-tables`
504
+ * and it still holds here: `ui-forms` is a low-level primitive and must not take a runtime
505
+ * dependency on the higher-level `ui-layout` `ModalDropdown` — that would invert the graph for
506
+ * every consumer. Apps needing the responsive modal/bottom-sheet variant inject it instead.
507
+ */
508
+
509
+ /** The four theme colours the menu paints with — passed in, so the menu stays theme-flat. */
510
+ interface AnchoredMenuColors {
511
+ text: string;
512
+ border: string;
513
+ surface: string;
514
+ brand: string;
515
+ }
516
+ interface AnchoredMenuProps {
517
+ options: readonly ControlOption[];
518
+ /** The currently-selected value, highlighted in the list ('' = none). */
519
+ selectedValue: string;
520
+ onSelect: (value: string) => void;
521
+ onDismiss: () => void;
522
+ colors: AnchoredMenuColors;
523
+ /** PRE-LOCALIZED accessibility hint applied to every option. */
524
+ optionHint: string;
525
+ /** Base testID: the menu is `${testID}-menu`, each option `${testID}-option-${value}`. */
526
+ testID: string;
527
+ /** Node whose clicks count as "inside" (the anchor) so its own press doesn't self-dismiss. */
528
+ anchorRef: React.RefObject<View | null>;
529
+ }
530
+ declare function AnchoredMenu({ options, selectedValue, onSelect, onDismiss, colors, optionHint, testID, anchorRef, }: AnchoredMenuProps): React.ReactElement;
531
+
532
+ declare const controlStyles: {
533
+ /** The shared bordered box every dense text control renders in. */
534
+ input: {
535
+ borderWidth: number;
536
+ borderRadius: number;
537
+ paddingHorizontal: number;
538
+ paddingVertical: number;
539
+ fontSize: number;
540
+ };
541
+ /** The select trigger: a bordered field box matching the text inputs + a chevron. */
542
+ selectTrigger: {
543
+ flexDirection: "row";
544
+ alignItems: "center";
545
+ justifyContent: "space-between";
546
+ gap: number;
547
+ borderWidth: number;
548
+ borderRadius: number;
549
+ paddingHorizontal: number;
550
+ paddingVertical: number;
551
+ };
552
+ selectTriggerText: {
553
+ fontSize: number;
554
+ flexShrink: number;
555
+ };
556
+ chevron: {
557
+ fontSize: number;
558
+ };
559
+ dateRow: {
560
+ flexDirection: "row";
561
+ gap: number;
562
+ };
563
+ dateCol: {
564
+ gap: number;
565
+ flex: number;
566
+ };
567
+ /**
568
+ * The micro-caption above a sub-control (a date range's From/To) and beside a switch. Note it
569
+ * is deliberately NOT the `control` label voice: it carries no `700`/uppercase, because it
570
+ * names a HALF of a control that already has a `control`-voice label above it. Uppercasing it
571
+ * too would make one field shout twice.
572
+ */
573
+ subLabel: {
574
+ fontSize: number;
575
+ letterSpacing: number;
576
+ };
577
+ /** Positioning context for a floating `AnchoredMenu` (`position:absolute; top:100%`). */
578
+ anchor: {
579
+ position: "relative";
580
+ };
581
+ menu: {
582
+ position: "absolute";
583
+ top: "100%";
584
+ left: number;
585
+ marginTop: number;
586
+ minWidth: number;
587
+ maxHeight: number;
588
+ borderWidth: number;
589
+ borderRadius: number;
590
+ paddingVertical: number;
591
+ zIndex: number;
592
+ boxShadow: string;
593
+ elevation: number;
594
+ overflow: "hidden";
595
+ };
596
+ option: {
597
+ paddingHorizontal: number;
598
+ paddingVertical: number;
599
+ };
600
+ optionText: {
601
+ fontSize: number;
602
+ };
603
+ error: {
604
+ fontSize: number;
605
+ marginTop: number;
606
+ };
607
+ };
608
+
609
+ /**
610
+ * `suggestOptions` — the ranked substring matcher behind `TypeaheadControl`, generalized from
611
+ * aml-v2's `suggestCountries` and promoted verbatim out of `@dloizides/ui-tables`' private
612
+ * `Filters/utils/suggest.ts`.
613
+ *
614
+ * Ranks: value exact (0) > value prefix (1) > label prefix (2) > label contains (3); ties broken
615
+ * by `label.localeCompare`. Empty/short queries return `[]` — nothing surfaces until `minChars`
616
+ * is typed, mirroring a native `<datalist>`.
617
+ */
618
+
619
+ /**
620
+ * Rank + cap the options matching `query`. Returns `[]` until `query` (trimmed) is at least
621
+ * `minChars` long, then the best `limit` matches, ranked as documented above.
622
+ */
623
+ declare function suggestOptions(options: readonly ControlOption[], query: string, minChars: number, limit: number): readonly ControlOption[];
624
+ /** True when `text` is non-empty yet matches no option's value or label exactly (case-insensitive). */
625
+ declare function isUnmatched(options: readonly ControlOption[], text: string): boolean;
626
+
627
+ 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, FormActions, type FormActionsProps, FormCheckbox, type FormCheckboxProps, FormField, type FormFieldProps, FormSwitch, type FormSwitchProps, SelectControl, type SelectControlProps, type ThemedInput, ThemedTextInput, type ThemedTextInputProps, TypeaheadControl, type TypeaheadControlProps, type UseThemedInputOptions, controlStyles, isUnmatched, suggestOptions, useThemedInput };