@sneekin/ui 0.2.2 → 0.3.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/README.md CHANGED
@@ -57,6 +57,26 @@ The code entry step shows the destination with an inline way to change it,
57
57
  and submits itself on the sixth digit — no separate verify tap needed on
58
58
  mobile.
59
59
 
60
+ ## Phone entry
61
+
62
+ By default the identify step opens in phone mode with a country chip —
63
+ flag, dial code, searchable picker — and a "Use email instead" link that
64
+ swaps it for a plain email field. The chip guesses the country from the
65
+ visitor's browser locale, then updates itself if they paste a full
66
+ international number (anything starting with `+`):
67
+
68
+ ```tsx
69
+ <SneekOtpLogin defaultCountry="GB" {...handlers} />
70
+ ```
71
+
72
+ Lock the field to just one identifier — no chip, no toggle:
73
+
74
+ ```tsx
75
+ <SneekOtpLogin identifierMode="email" {...handlers} />
76
+ ```
77
+
78
+ `identifierMode` is `'both' | 'phone' | 'email'`, default `'both'`.
79
+
60
80
  Swap the mark, drop the sub-heading, or remove the Sneek line:
61
81
 
62
82
  ```tsx
@@ -81,8 +101,10 @@ control carries a stable class (`.sneek-otp-input`, `.sneek-otp-button`,
81
101
  | `onSuccess` | `(result: TResult) => void` | — |
82
102
  | `title` | `string` | `'Sign in'` |
83
103
  | `subtitle` | `ReactNode \| null` | `'We will send you a one-time code.'` |
84
- | `identifierLabel` | `string` | `'Email or mobile'` |
85
- | `identifierPlaceholder` | `string` | `'you@company.com or +91…'` |
104
+ | `identifierMode` | `'both' \| 'phone' \| 'email'` | `'both'` |
105
+ | `defaultCountry` | ISO 3166-1 alpha-2, e.g. `'GB'` | guessed from locale, else India |
106
+ | `identifierLabel` | `string` | `'Mobile number'` / `'Email address'`, per mode |
107
+ | `identifierPlaceholder` | `string` | email mode only; `'you@company.com'` |
86
108
  | `theme` | `'auto' \| 'light' \| 'dark'` | `'auto'` |
87
109
  | `accentColor` | `string` | Sneek orange, tuned per mode |
88
110
  | `logo` | `ReactNode \| null` | Sneek mark |
@@ -1,6 +1,7 @@
1
1
  import { type CSSProperties, type ReactNode } from 'react';
2
2
  import { type SneekOtpHandlers } from './use-sneek-otp';
3
3
  export type SneekTheme = 'auto' | 'light' | 'dark';
4
+ export type SneekIdentifierMode = 'phone' | 'email' | 'both';
4
5
  export interface SneekOtpLoginProps<TResult = unknown> extends SneekOtpHandlers<TResult> {
5
6
  /** Heading above the form. @default 'Sign in' */
6
7
  title?: string;
@@ -8,9 +9,20 @@ export interface SneekOtpLoginProps<TResult = unknown> extends SneekOtpHandlers<
8
9
  verifyTitle?: string;
9
10
  /** Sub-heading. Pass `null` to remove it. */
10
11
  subtitle?: ReactNode;
11
- /** Label for the identifier input. */
12
+ /**
13
+ * Which identifiers a visitor can sign in with. `'both'` starts on phone
14
+ * with a country chip and offers a link to switch to email.
15
+ * @default 'both'
16
+ */
17
+ identifierMode?: SneekIdentifierMode;
18
+ /**
19
+ * ISO 3166-1 alpha-2 country to preselect for the phone chip. Leave unset
20
+ * to guess from the browser's locale, falling back to India.
21
+ */
22
+ defaultCountry?: string;
23
+ /** Overrides the auto label ("Mobile number" / "Email address"). */
12
24
  identifierLabel?: string;
13
- /** Placeholder for the identifier input. */
25
+ /** Overrides the auto placeholder. Only used in email mode. */
14
26
  identifierPlaceholder?: string;
15
27
  /**
16
28
  * Colour scheme. `auto` follows the visitor's system setting.
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Country calling codes for the phone-mode chip in the identify step.
3
+ *
4
+ * Flags are computed from the ISO 3166-1 alpha-2 code (two regional-indicator
5
+ * codepoints) rather than hand-typed emoji, so there is no chance of a flag
6
+ * silently drifting from its country. Where a dial code is shared by several
7
+ * territories (+1, +7, +44, +61...), the first entry is the one auto-detect
8
+ * and the default resolve to — the largest or best-known user of that code.
9
+ */
10
+ export interface Country {
11
+ iso2: string;
12
+ name: string;
13
+ dial: string;
14
+ }
15
+ export declare const COUNTRIES: readonly Country[];
16
+ export declare const DEFAULT_COUNTRY_ISO2 = "IN";
17
+ export declare function flagEmoji(iso2: string): string;
18
+ export declare function countryByIso2(iso2: string): Country | undefined;
19
+ /**
20
+ * Longest-dial-code-first match against a digit string (no '+'). Tries 3, 2,
21
+ * then 1-digit prefixes so e.g. "44" resolves to the UK rather than being cut
22
+ * short at a 1-digit code that happens to also exist.
23
+ */
24
+ export declare function countryByDialPrefix(digits: string): Country | undefined;
25
+ /** Best-effort guess from the browser's locale region, e.g. "en-GB" -> GB. */
26
+ export declare function detectCountryFromLocale(): Country | undefined;
@@ -0,0 +1,12 @@
1
+ import { type Country } from './countries';
2
+ export interface CountryPickerProps {
3
+ country: Country;
4
+ onSelect: (country: Country) => void;
5
+ disabled?: boolean;
6
+ }
7
+ /**
8
+ * The flag + dial-code chip in front of the phone number field. Opens a
9
+ * searchable list on click; the number field itself keeps auto-detecting
10
+ * from pasted content independently of this — this is the manual path.
11
+ */
12
+ export declare function CountryPicker({ country, onSelect, disabled }: CountryPickerProps): import("react").JSX.Element;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export { useSneekOtp, type SneekOtpHandlers, type UseSneekOtp, } from './use-sneek-otp';
2
- export { SneekOtpLogin, type SneekOtpLoginProps, type SneekTheme, } from './component';
2
+ export { SneekOtpLogin, type SneekOtpLoginProps, type SneekTheme, type SneekIdentifierMode, } from './component';
3
3
  export { SNEEK_ACCENT_DARK, SNEEK_ACCENT_LIGHT, } from './theme';
4
+ export { COUNTRIES, DEFAULT_COUNTRY_ISO2, flagEmoji, countryByIso2, countryByDialPrefix, detectCountryFromLocale, type Country, } from './countries';
4
5
  export { createFetchHandlers, type FetchHandlerOptions, } from './fetch-handlers';
5
6
  export { otpReducer, initialOtpState, formatChannels, type SneekChannel, type SneekOtpStep, type SneekOtpStatus, type SneekOtpState, type SneekOtpAction, type RequestOtpResult, } from './state';