asma-ui-core 3.80.3 → 3.81.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/Readme.md +32 -0
- package/dist/asma-ui-core.css +1 -1
- package/dist/components/inputs/phone-field/CountryCodeOptions.js +39 -0
- package/dist/components/inputs/phone-field/CountryCodeSelect.js +145 -0
- package/dist/components/inputs/phone-field/StyledCountryFlag.js +25 -0
- package/dist/components/inputs/phone-field/StyledPhoneField.js +74 -0
- package/dist/components/inputs/phone-field/useCountryPicker.js +55 -0
- package/dist/components/inputs/search-field/StyledSearchField.js +28 -24
- package/dist/components/inputs/slider/StyledSlider.js +98 -81
- package/dist/components/inputs/slider/StyledSlider.module.js +5 -5
- package/dist/index.js +117 -113
- package/dist/src/components/inputs/phone-field/CountryCodeOptions.d.ts +20 -0
- package/dist/src/components/inputs/phone-field/CountryCodeSelect.d.ts +30 -0
- package/dist/src/components/inputs/phone-field/StyledCountryFlag.d.ts +34 -0
- package/dist/src/components/inputs/phone-field/StyledPhoneField.d.ts +81 -0
- package/dist/src/components/inputs/phone-field/index.d.ts +3 -0
- package/dist/src/components/inputs/phone-field/types.d.ts +24 -0
- package/dist/src/components/inputs/phone-field/useCountryPicker.d.ts +30 -0
- package/dist/src/components/inputs/phone-field/useCountryPicker.test.d.ts +1 -0
- package/dist/src/components/inputs/select/StyledSelectItem.d.ts +2 -1
- package/dist/src/index.d.ts +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { PhoneCountryChoice, RenderCountryFlag } from './types';
|
|
2
|
+
export interface CountryCodeOptionsProps {
|
|
3
|
+
dataTest: string;
|
|
4
|
+
/** Already filtered — the caller owns the query. */
|
|
5
|
+
visible: readonly PhoneCountryChoice[];
|
|
6
|
+
selectedIso2: string;
|
|
7
|
+
activeIndex: number;
|
|
8
|
+
optionId: (index: number) => string;
|
|
9
|
+
onSelect: (iso2: string) => void;
|
|
10
|
+
listId: string;
|
|
11
|
+
/** Accessible name for the list. Consumer copy, same string that titles the mobile sheet. */
|
|
12
|
+
label: string;
|
|
13
|
+
renderFlag?: RenderCountryFlag;
|
|
14
|
+
className?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* The country listbox. Presentation only — filtering, the active row and the keyboard live in
|
|
18
|
+
* [[useCountryPicker]], so the desktop trigger and the mobile sheet render the same list.
|
|
19
|
+
*/
|
|
20
|
+
export declare const CountryCodeOptions: ({ dataTest, visible, selectedIso2, activeIndex, optionId, onSelect, listId, label, renderFlag, className, }: CountryCodeOptionsProps) => JSX.Element;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { PhoneCountryChoice, RenderCountryFlag } from './types';
|
|
2
|
+
export interface CountryCodeSelectProps {
|
|
3
|
+
dataTest: string;
|
|
4
|
+
countries: readonly PhoneCountryChoice[];
|
|
5
|
+
value: string;
|
|
6
|
+
onChange: (iso2: string) => void;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
/** Title of the mobile picker sheet; supplied by the consumer — this library ships no copy. */
|
|
9
|
+
selectCountryLabel: string;
|
|
10
|
+
/** Placeholder of the mobile sheet's search box. Consumer-supplied. */
|
|
11
|
+
searchPlaceholder: string;
|
|
12
|
+
renderFlag?: RenderCountryFlag;
|
|
13
|
+
/** `id` of the field label, so the trigger inherits the field's accessible name. */
|
|
14
|
+
labelledBy?: string;
|
|
15
|
+
/** Surface class shared with the number input so the pair matches. */
|
|
16
|
+
className?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Collapsed country trigger (flag + calling code + chevron) and the picker it opens.
|
|
20
|
+
*
|
|
21
|
+
* Two presentations, one list and one keyboard contract ([[useCountryPicker]]):
|
|
22
|
+
*
|
|
23
|
+
* - **Desktop** — the trigger *is* the combobox. Opening it turns the calling-code slot into an
|
|
24
|
+
* input and typing filters the list below, which is what Figma draws (the open trigger carries a
|
|
25
|
+
* text cursor, and the menu holds rows only — node 8565:284950 has no search field).
|
|
26
|
+
* - **Mobile** — a full-screen sheet with its own search box, per node 8699:112423. The break is
|
|
27
|
+
* `StyledDialog`'s own `fullScreen ?? isMobile` default (≤743 px), so the two never disagree
|
|
28
|
+
* about what "mobile" means.
|
|
29
|
+
*/
|
|
30
|
+
export declare const CountryCodeSelect: ({ dataTest, countries, value, onChange, disabled, selectCountryLabel, searchPlaceholder, renderFlag, labelledBy, className, }: CountryCodeSelectProps) => JSX.Element;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export interface StyledCountryFlagProps {
|
|
2
|
+
/** ISO 3166-1 alpha-2 code, e.g. `'NO'`. Rendered as `data-country` so tests can assert which flag appeared. */
|
|
3
|
+
iso2: string;
|
|
4
|
+
/**
|
|
5
|
+
* URL of the flag artwork, resolved by the consumer. Required because this package is loaded
|
|
6
|
+
* from the kernel CDN and cannot know where the consuming app hosts its assets — so the
|
|
7
|
+
* artwork ships with the app, and the app's bundler is what turns it into an address.
|
|
8
|
+
*/
|
|
9
|
+
src: string;
|
|
10
|
+
/**
|
|
11
|
+
* `'eager'` for a flag that is on screen the moment it mounts — the collapsed trigger, and any
|
|
12
|
+
* row of an already-open picker. Defaults to `'lazy'`, which is right for a long scrolling list
|
|
13
|
+
* where fetching all 245 up front would move megabytes nobody looks at.
|
|
14
|
+
*/
|
|
15
|
+
loading?: 'eager' | 'lazy';
|
|
16
|
+
className?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* @figmaNode none — the Design System has no flag component; the phone field's Figma frames draw
|
|
20
|
+
* flag artwork directly.
|
|
21
|
+
*
|
|
22
|
+
* Real artwork rather than emoji: Windows ships no regional-indicator glyphs, so `🇳🇴` renders as
|
|
23
|
+
* the letters "NO" in Chrome and Edge there.
|
|
24
|
+
*
|
|
25
|
+
* One file per country rather than one sprite, which is a measured choice: the full flag set is
|
|
26
|
+
* 2.0 MB raw / 619 kB gzipped, dominated by a handful of ornate coats of arms (Serbia alone is
|
|
27
|
+
* 182 kB), so a sprite would charge every user the whole set to look at ten rows. Per-file fetches
|
|
28
|
+
* only what is on screen — and Norway, the collapsed default, is 318 bytes.
|
|
29
|
+
*
|
|
30
|
+
* The box is painted before the artwork arrives and stays painted if it never does. Sizes vary by
|
|
31
|
+
* two orders of magnitude across the set, so a row holding a heavy flag would otherwise sit empty
|
|
32
|
+
* — or show the browser's broken-image glyph — long enough to read as a defect.
|
|
33
|
+
*/
|
|
34
|
+
export declare const StyledCountryFlag: ({ iso2, src, loading, className, }: StyledCountryFlagProps) => JSX.Element;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { FieldSize } from '../field-styles';
|
|
3
|
+
import { PhoneCountryChoice, RenderCountryFlag } from './types';
|
|
4
|
+
export interface StyledPhoneFieldProps {
|
|
5
|
+
/** @figmaProp none — test hook */
|
|
6
|
+
dataTest: string;
|
|
7
|
+
/** @figmaProp Label above the control pair */
|
|
8
|
+
label?: ReactNode;
|
|
9
|
+
countries: readonly PhoneCountryChoice[];
|
|
10
|
+
/** Selected country as an ISO 3166-1 alpha-2 code. */
|
|
11
|
+
country: string;
|
|
12
|
+
onCountryChange: (iso2: string) => void;
|
|
13
|
+
/**
|
|
14
|
+
* National significant number as **digits only** — no dial code, no separators. Grouping is a
|
|
15
|
+
* display concern applied through `formatNationalNumber`, so the value a consumer stores and
|
|
16
|
+
* the value it reads back are the same string.
|
|
17
|
+
*/
|
|
18
|
+
value: string;
|
|
19
|
+
/** Receives the typed national number as digits only. */
|
|
20
|
+
onChange: (nationalNumber: string) => void;
|
|
21
|
+
onBlur?: () => void;
|
|
22
|
+
/** @figmaProp State = true→"Error" — reddens the number input only, per Figma. */
|
|
23
|
+
error?: boolean;
|
|
24
|
+
/** @figmaProp Helper text element */
|
|
25
|
+
helperText?: ReactNode;
|
|
26
|
+
reserveHelperText?: boolean;
|
|
27
|
+
/** @figmaProp State = true→"Disabled" */
|
|
28
|
+
disabled?: boolean;
|
|
29
|
+
/** @figmaProp State = true→"Read only" — renders the number as text, not as inputs. */
|
|
30
|
+
readOnly?: boolean;
|
|
31
|
+
/** Pre-formatted international number for the read-only state, e.g. `'+47 48 01 23 45'`. */
|
|
32
|
+
readOnlyText?: string;
|
|
33
|
+
/**
|
|
34
|
+
* Click-to-call target for the read-only state, e.g. `phoneTelHref(value)` from
|
|
35
|
+
* `asma-core-helpers/phone`. Supplied by the consumer because building a `tel:` URI is a phone
|
|
36
|
+
* rule, and this library owns none — without it the number renders as plain text.
|
|
37
|
+
*/
|
|
38
|
+
readOnlyHref?: string;
|
|
39
|
+
required?: boolean;
|
|
40
|
+
/** @figmaProp Placeholder of the number input */
|
|
41
|
+
placeholder?: string;
|
|
42
|
+
/**
|
|
43
|
+
* Per-country grouping for the number input, e.g. `formatNationalAsYouType` from
|
|
44
|
+
* `asma-core-helpers/phone`. Injected rather than built in: this library owns no phone rules
|
|
45
|
+
* and no country metadata (ADR-0017 DEC-001).
|
|
46
|
+
*/
|
|
47
|
+
formatNationalNumber?: (nationalNumber: string, iso2: string) => string;
|
|
48
|
+
renderFlag?: RenderCountryFlag;
|
|
49
|
+
/** Title of the country picker. Consumer-supplied — this library ships no user-facing copy. */
|
|
50
|
+
selectCountryLabel: string;
|
|
51
|
+
/** Placeholder of the country search box. Consumer-supplied. */
|
|
52
|
+
searchPlaceholder: string;
|
|
53
|
+
id?: string;
|
|
54
|
+
name?: string;
|
|
55
|
+
size?: FieldSize;
|
|
56
|
+
/**
|
|
57
|
+
* Applied to the surface of **both** controls, so the pair cannot drift apart visually —
|
|
58
|
+
* e.g. `'bg-white'` where the field sits on a tinted panel.
|
|
59
|
+
*/
|
|
60
|
+
fieldClassName?: string;
|
|
61
|
+
helperTextClassName?: string;
|
|
62
|
+
className?: string;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* @figmaNode y2whTsnmk1J2dy5fwbM7GK#8523-109515
|
|
66
|
+
* Figma "Phone number input": a country trigger (flag + code + chevron) and a separate number
|
|
67
|
+
* input side by side under one label, with a single helper row beneath the pair. The Figma
|
|
68
|
+
* **Property 1** variants (Default/Error/Disabled/Read-only/Menu) map to `error`/`disabled`/
|
|
69
|
+
* `readOnly` and the picker's own open state.
|
|
70
|
+
*
|
|
71
|
+
* Country data, grouping, validation and every user-facing string are supplied by the consumer —
|
|
72
|
+
* this component renders the state it is told about and owns no rules (ADR-0017 DEC-001/REQ-009).
|
|
73
|
+
* Pair it with `asma-core-helpers/phone`, which provides the country list, the formatter and the
|
|
74
|
+
* validator.
|
|
75
|
+
*
|
|
76
|
+
* ponytail: reformatting is applied on every keystroke, so the caret lands at the end of the
|
|
77
|
+
* number after an edit in the middle of it. Phone numbers are short and typed left to right, so
|
|
78
|
+
* this is a deliberate ceiling; the upgrade path is the caret-preserving `formatMaskedValue` in
|
|
79
|
+
* `helpers/inputMask.ts`, which today only handles fixed-width masks.
|
|
80
|
+
*/
|
|
81
|
+
export declare const StyledPhoneField: ({ dataTest, label, countries, country, onCountryChange, value, onChange, onBlur, error, helperText, reserveHelperText, disabled, readOnly, readOnlyText, readOnlyHref, required, placeholder, formatNationalNumber, renderFlag, selectCountryLabel, searchPlaceholder, id, name, size, fieldClassName, helperTextClassName, className, }: StyledPhoneFieldProps) => JSX.Element;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* One row of the country picker.
|
|
4
|
+
*
|
|
5
|
+
* Declared structurally rather than imported from `asma-core-helpers/phone` on purpose:
|
|
6
|
+
* this package is a kernel member served to the whole fleet, and pulling a phone-metadata
|
|
7
|
+
* dependency into it would put ~20 kB gz of country data in every app's shared bundle.
|
|
8
|
+
* `PhoneCountryOption` from that subpath satisfies this shape, so consumers pass it directly.
|
|
9
|
+
*
|
|
10
|
+
* @see asma-modules/_docs/adr/adr-g-0017-frontend-form-state-and-validation-ownership.md:49 — DEC-001, this library owns presentation only
|
|
11
|
+
*/
|
|
12
|
+
export interface PhoneCountryChoice {
|
|
13
|
+
/** ISO 3166-1 alpha-2 code, e.g. `'NO'`. */
|
|
14
|
+
iso2: string;
|
|
15
|
+
/** Calling code without the leading `+`, e.g. `'47'`. */
|
|
16
|
+
dialCode: string;
|
|
17
|
+
/** Country name, already localized by the consumer. */
|
|
18
|
+
name: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Renders the flag for a country. `loading` is `'eager'` where the flag is on screen the moment it
|
|
22
|
+
* mounts (the collapsed trigger, and the rows of an already-open picker) — see `StyledCountryFlag`.
|
|
23
|
+
*/
|
|
24
|
+
export type RenderCountryFlag = (iso2: string, loading?: 'eager' | 'lazy') => ReactNode;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { KeyboardEvent } from 'react';
|
|
2
|
+
import { PhoneCountryChoice } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Matches on localized name **or** calling code, so both `"Norway"` and `"47"` narrow the list
|
|
5
|
+
* to +47 (ASMA-7485). A leading `+` is ignored so pasting `+47` works.
|
|
6
|
+
*/
|
|
7
|
+
export declare function matchesCountryQuery(country: PhoneCountryChoice, query: string): boolean;
|
|
8
|
+
export interface UseCountryPickerOptions {
|
|
9
|
+
countries: readonly PhoneCountryChoice[];
|
|
10
|
+
selectedIso2: string;
|
|
11
|
+
query: string;
|
|
12
|
+
listId: string;
|
|
13
|
+
onSelect: (iso2: string) => void;
|
|
14
|
+
onDismiss: () => void;
|
|
15
|
+
}
|
|
16
|
+
export interface CountryPicker {
|
|
17
|
+
visible: readonly PhoneCountryChoice[];
|
|
18
|
+
activeIndex: number;
|
|
19
|
+
optionId: (index: number) => string;
|
|
20
|
+
handleKeyDown: (event: KeyboardEvent<HTMLElement>) => void;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Filtering, active-row tracking and keyboard behaviour for the country picker — shared by the
|
|
24
|
+
* desktop trigger (which is itself the combobox) and the mobile sheet (which has its own search
|
|
25
|
+
* box), so the two presentations cannot drift apart on what typing or arrowing does.
|
|
26
|
+
*
|
|
27
|
+
* The active row is tracked by country rather than by index: filtering renumbers every index, and
|
|
28
|
+
* an index would need an effect to stay in range — a re-render behind the list it describes.
|
|
29
|
+
*/
|
|
30
|
+
export declare function useCountryPicker({ countries, selectedIso2, query, listId, onSelect, onDismiss, }: UseCountryPickerOptions): CountryPicker;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { MouseEvent, ReactNode } from 'react';
|
|
2
2
|
export interface StyledSelectItemProps {
|
|
3
|
-
id?: string;
|
|
4
3
|
value?: unknown;
|
|
4
|
+
/** Needed when a parent listbox points `aria-activedescendant` at this row. */
|
|
5
|
+
id?: string;
|
|
5
6
|
children?: ReactNode;
|
|
6
7
|
selected?: boolean;
|
|
7
8
|
active?: boolean;
|
package/dist/src/index.d.ts
CHANGED
|
@@ -26,6 +26,7 @@ export * from './components/inputs/switch';
|
|
|
26
26
|
export * from './components/inputs/radio-button';
|
|
27
27
|
export * from './components/inputs/label';
|
|
28
28
|
export * from './components/inputs/dynamic-select';
|
|
29
|
+
export * from './components/inputs/phone-field';
|
|
29
30
|
export * from './datetime/components/date-picker';
|
|
30
31
|
export * from './datetime/components/time-picker';
|
|
31
32
|
export * from './datetime/helpers/date.helper';
|