@spaethtech/svelte-ui 0.7.1-dev.39.d6b0fa5 → 0.7.1-dev.40.3d22ac0
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.
|
@@ -154,7 +154,9 @@ examples):
|
|
|
154
154
|
|
|
155
155
|
- **Form:** `Button` `ButtonDropdown` `Input` `Select` `List` `TextArea` `Checkbox` `Toggle` `Radio`
|
|
156
156
|
`Rating` · **`FieldGroup`** (fieldset wrapper for radio/checkbox/toggle sets)
|
|
157
|
-
- **Specialized inputs:** `PasswordInput` `EmailInput` `SearchInput` `NumberInput`
|
|
157
|
+
- **Specialized inputs:** `PasswordInput` `EmailInput` `SearchInput` `NumberInput` (formatting +
|
|
158
|
+
`percent`/`stepper`/`clamp`/`liveFormat`) `PhoneInput` (stores E.164; dep-free, inject
|
|
159
|
+
`parse`/`format` for per-country)
|
|
158
160
|
- **Date / time:** `DatePicker` `Calendar` `TimePicker` `TimeSpinner` `TimeRangeInput` `DateTimeInput`
|
|
159
161
|
- **Data:** `DataTable` `Query` — driven by the headless layer at **`@spaethtech/svelte-ui/data`**
|
|
160
162
|
(query-language parser/AST, `createGrid`, `DataGrid<T>`, `DataSet`).
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Input from "./Input.svelte";
|
|
3
|
+
import Phone from "~icons/mdi/phone-outline";
|
|
4
|
+
import type { HTMLInputAttributes } from "svelte/elements";
|
|
5
|
+
import type { Size } from "../types/sizes.js";
|
|
6
|
+
import type { Responsive } from "../types/responsive.js";
|
|
7
|
+
import type { Variant } from "../types/variants.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* PhoneInput — type a phone number in ANY format; the bound `value` is stored in **E.164**
|
|
11
|
+
* (`+15551234567`): a leading `+`, country calling code, then the national number, digits only.
|
|
12
|
+
* The field shows what you type while editing and snaps to canonical E.164 on blur.
|
|
13
|
+
*
|
|
14
|
+
* **Dependency-free** by design — it normalises the E.164 *shape* only and does NOT do per-country
|
|
15
|
+
* formatting/validation (that needs a phone library + country data, which we don't bundle). To add
|
|
16
|
+
* that, inject `parse` / `format` / `validate` (e.g. backed by `libphonenumber-js`) — the component
|
|
17
|
+
* stays dep-free; you choose the library.
|
|
18
|
+
*/
|
|
19
|
+
interface Props extends Omit<HTMLInputAttributes, "type" | "value" | "size"> {
|
|
20
|
+
/** Bound value in E.164 (`+15551234567`), normalised from whatever the user types. */
|
|
21
|
+
value: string;
|
|
22
|
+
class?: string;
|
|
23
|
+
inputClass?: string;
|
|
24
|
+
/** Validates the normalised E.164 value. Overrides the built-in E.164-shape check. */
|
|
25
|
+
validate?: (value: string) => boolean | string;
|
|
26
|
+
valid?: boolean;
|
|
27
|
+
touched?: boolean;
|
|
28
|
+
element?: HTMLInputElement;
|
|
29
|
+
size?: Responsive<Size>;
|
|
30
|
+
/** Shared axes — forwarded to the underlying Input. */
|
|
31
|
+
variant?: Variant;
|
|
32
|
+
borderless?: boolean;
|
|
33
|
+
required?: boolean;
|
|
34
|
+
/** Calling code (digits, no `+`) prepended when the typed number has no leading `+`. Default `"1"`
|
|
35
|
+
* (North America). Set to the calling code of the numbers your users enter without a `+`. */
|
|
36
|
+
defaultCallingCode?: string;
|
|
37
|
+
/** Normalise typed text → stored E.164. Override to plug a library (e.g. libphonenumber-js). */
|
|
38
|
+
parse?: (input: string, defaultCallingCode: string) => string;
|
|
39
|
+
/** Format the stored E.164 → field display (default: shown as-is). Override for a national /
|
|
40
|
+
* pretty format from your phone library. */
|
|
41
|
+
format?: (value: string) => string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
let {
|
|
45
|
+
value = $bindable(""),
|
|
46
|
+
valid = $bindable(true),
|
|
47
|
+
touched = $bindable(false),
|
|
48
|
+
element = $bindable(),
|
|
49
|
+
validate,
|
|
50
|
+
required = false,
|
|
51
|
+
defaultCallingCode = "1",
|
|
52
|
+
parse,
|
|
53
|
+
format,
|
|
54
|
+
placeholder = "(555) 123-4567",
|
|
55
|
+
...restProps
|
|
56
|
+
}: Props = $props();
|
|
57
|
+
|
|
58
|
+
let displayValue = $state("");
|
|
59
|
+
let focused = $state(false);
|
|
60
|
+
|
|
61
|
+
// Default normalisation: an explicit international `+…` is kept (digits only after the `+`);
|
|
62
|
+
// otherwise the default calling code is prepended. SHAPE only — no per-country length check.
|
|
63
|
+
function defaultParse(raw: string, dcc: string): string {
|
|
64
|
+
const trimmed = (raw ?? "").trim();
|
|
65
|
+
if (!trimmed) return "";
|
|
66
|
+
const digits = trimmed.replace(/\D/g, "");
|
|
67
|
+
if (!digits) return "";
|
|
68
|
+
return trimmed.includes("+") ? `+${digits}` : `+${dcc}${digits}`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const toE164 = (raw: string) => (parse ?? defaultParse)(raw, defaultCallingCode);
|
|
72
|
+
const toDisplay = (v: string) => (v ? (format ? format(v) : v) : "");
|
|
73
|
+
|
|
74
|
+
// Reflect external value changes into the field while the user isn't editing.
|
|
75
|
+
$effect(() => {
|
|
76
|
+
if (!focused) displayValue = toDisplay(value);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const defaultValidator = (v: string) => {
|
|
80
|
+
if (!v) return required ? "Phone number is required" : true;
|
|
81
|
+
// E.164 shape: `+`, a non-zero country digit, then 6–14 more digits (7–15 total).
|
|
82
|
+
if (!/^\+[1-9]\d{6,14}$/.test(v)) return "Enter a valid phone number";
|
|
83
|
+
return true;
|
|
84
|
+
};
|
|
85
|
+
const phoneValidator = $derived(validate ?? defaultValidator);
|
|
86
|
+
|
|
87
|
+
function handleInput(raw: string) {
|
|
88
|
+
displayValue = raw;
|
|
89
|
+
value = toE164(raw);
|
|
90
|
+
}
|
|
91
|
+
function handleFocus() {
|
|
92
|
+
focused = true;
|
|
93
|
+
displayValue = toDisplay(value);
|
|
94
|
+
}
|
|
95
|
+
function handleBlur() {
|
|
96
|
+
focused = false;
|
|
97
|
+
displayValue = toDisplay(value); // snap the field to the canonical (or your `format`) form
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
let stringValue = $derived(displayValue);
|
|
101
|
+
</script>
|
|
102
|
+
|
|
103
|
+
<Input
|
|
104
|
+
bind:value={stringValue}
|
|
105
|
+
bind:valid
|
|
106
|
+
bind:touched
|
|
107
|
+
bind:element
|
|
108
|
+
type="tel"
|
|
109
|
+
inputmode="tel"
|
|
110
|
+
autocomplete="tel"
|
|
111
|
+
{required}
|
|
112
|
+
validate={(val) => phoneValidator(toE164(val))}
|
|
113
|
+
oninput={(e) => handleInput((e.target as HTMLInputElement).value)}
|
|
114
|
+
onfocus={handleFocus}
|
|
115
|
+
onblur={handleBlur}
|
|
116
|
+
{placeholder}
|
|
117
|
+
{...restProps}
|
|
118
|
+
>
|
|
119
|
+
{#snippet icon()}<Phone />{/snippet}
|
|
120
|
+
</Input>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { HTMLInputAttributes } from "svelte/elements";
|
|
2
|
+
import type { Size } from "../types/sizes.js";
|
|
3
|
+
import type { Responsive } from "../types/responsive.js";
|
|
4
|
+
import type { Variant } from "../types/variants.js";
|
|
5
|
+
/**
|
|
6
|
+
* PhoneInput — type a phone number in ANY format; the bound `value` is stored in **E.164**
|
|
7
|
+
* (`+15551234567`): a leading `+`, country calling code, then the national number, digits only.
|
|
8
|
+
* The field shows what you type while editing and snaps to canonical E.164 on blur.
|
|
9
|
+
*
|
|
10
|
+
* **Dependency-free** by design — it normalises the E.164 *shape* only and does NOT do per-country
|
|
11
|
+
* formatting/validation (that needs a phone library + country data, which we don't bundle). To add
|
|
12
|
+
* that, inject `parse` / `format` / `validate` (e.g. backed by `libphonenumber-js`) — the component
|
|
13
|
+
* stays dep-free; you choose the library.
|
|
14
|
+
*/
|
|
15
|
+
interface Props extends Omit<HTMLInputAttributes, "type" | "value" | "size"> {
|
|
16
|
+
/** Bound value in E.164 (`+15551234567`), normalised from whatever the user types. */
|
|
17
|
+
value: string;
|
|
18
|
+
class?: string;
|
|
19
|
+
inputClass?: string;
|
|
20
|
+
/** Validates the normalised E.164 value. Overrides the built-in E.164-shape check. */
|
|
21
|
+
validate?: (value: string) => boolean | string;
|
|
22
|
+
valid?: boolean;
|
|
23
|
+
touched?: boolean;
|
|
24
|
+
element?: HTMLInputElement;
|
|
25
|
+
size?: Responsive<Size>;
|
|
26
|
+
/** Shared axes — forwarded to the underlying Input. */
|
|
27
|
+
variant?: Variant;
|
|
28
|
+
borderless?: boolean;
|
|
29
|
+
required?: boolean;
|
|
30
|
+
/** Calling code (digits, no `+`) prepended when the typed number has no leading `+`. Default `"1"`
|
|
31
|
+
* (North America). Set to the calling code of the numbers your users enter without a `+`. */
|
|
32
|
+
defaultCallingCode?: string;
|
|
33
|
+
/** Normalise typed text → stored E.164. Override to plug a library (e.g. libphonenumber-js). */
|
|
34
|
+
parse?: (input: string, defaultCallingCode: string) => string;
|
|
35
|
+
/** Format the stored E.164 → field display (default: shown as-is). Override for a national /
|
|
36
|
+
* pretty format from your phone library. */
|
|
37
|
+
format?: (value: string) => string;
|
|
38
|
+
}
|
|
39
|
+
declare const PhoneInput: import("svelte").Component<Props, {}, "element" | "value" | "valid" | "touched">;
|
|
40
|
+
type PhoneInput = ReturnType<typeof PhoneInput>;
|
|
41
|
+
export default PhoneInput;
|
package/dist/index.d.ts
CHANGED
|
@@ -21,6 +21,7 @@ export { default as PasswordInput } from "./components/PasswordInput.svelte";
|
|
|
21
21
|
export { default as EmailInput } from "./components/EmailInput.svelte";
|
|
22
22
|
export { default as SearchInput } from "./components/SearchInput.svelte";
|
|
23
23
|
export { default as NumberInput } from "./components/NumberInput.svelte";
|
|
24
|
+
export { default as PhoneInput } from "./components/PhoneInput.svelte";
|
|
24
25
|
export { default as DateTimeInput } from "./components/DateTimeInput.svelte";
|
|
25
26
|
export { default as Calendar } from "./components/Calendar.svelte";
|
|
26
27
|
export { default as DatePicker } from "./components/DatePicker.svelte";
|
package/dist/index.js
CHANGED
|
@@ -24,6 +24,7 @@ export { default as PasswordInput } from "./components/PasswordInput.svelte";
|
|
|
24
24
|
export { default as EmailInput } from "./components/EmailInput.svelte";
|
|
25
25
|
export { default as SearchInput } from "./components/SearchInput.svelte";
|
|
26
26
|
export { default as NumberInput } from "./components/NumberInput.svelte";
|
|
27
|
+
export { default as PhoneInput } from "./components/PhoneInput.svelte";
|
|
27
28
|
export { default as DateTimeInput } from "./components/DateTimeInput.svelte";
|
|
28
29
|
export { default as Calendar } from "./components/Calendar.svelte";
|
|
29
30
|
export { default as DatePicker } from "./components/DatePicker.svelte";
|
package/docs/components.md
CHANGED
|
@@ -207,6 +207,20 @@ blur/submit).
|
|
|
207
207
|
and **`percent`** (display ×100 `%`, store the fraction; `min`/`max`/`step` in fraction units).
|
|
208
208
|
- Dep-free by design — for locale/`Intl` or currency codes, format upstream or via `validate`.
|
|
209
209
|
|
|
210
|
+
### PhoneInput
|
|
211
|
+
|
|
212
|
+
Type any format; stores the bound value as **E.164** (`+15551234567`). Field shows what you type,
|
|
213
|
+
snaps to canonical on blur.
|
|
214
|
+
|
|
215
|
+
- **Location**: `src/lib/components/PhoneInput.svelte`
|
|
216
|
+
- **Value**: `string` (E.164)
|
|
217
|
+
- **Props**: `defaultCallingCode` (digits, default `"1"` — prepended when input has no `+`), `required`,
|
|
218
|
+
`validate` (validates the E.164), and the injection hooks **`parse`** (typed → E.164) / **`format`**
|
|
219
|
+
(E.164 → display).
|
|
220
|
+
- **Dep-free** — normalises the E.164 *shape* only (no per-country validation/formatting). For that,
|
|
221
|
+
inject `parse`/`format`/`validate` backed by a phone library (e.g. `libphonenumber-js`); the
|
|
222
|
+
component bundles none.
|
|
223
|
+
|
|
210
224
|
## Navigation
|
|
211
225
|
|
|
212
226
|
### TabStrip
|