@clubmed/trident-ui 2.0.0-beta.38 → 2.0.0-beta.40

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.
@@ -9,7 +9,7 @@ function i() {
9
9
  className: "flex items-center gap-3",
10
10
  children: [/* @__PURE__ */ n(e, {
11
11
  checked: i,
12
- onClick: () => a(!i)
12
+ onChange: (e) => a(e.target.checked)
13
13
  }), /* @__PURE__ */ n("span", {
14
14
  className: "text-b2 font-semibold",
15
15
  children: "Enable notifications"
@@ -1 +1 @@
1
- {"version":3,"file":"switch-demo.js","names":[],"sources":["../../lib/examples/switch-demo.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport { Switch } from '@/ui/forms/Switch';\n\nexport default function SwitchDemo() {\n const [isEnabled, setIsEnabled] = useState(false);\n\n return (\n <div className=\"flex items-center gap-3\">\n <Switch checked={isEnabled} onClick={() => setIsEnabled(!isEnabled)} />\n <span className=\"text-b2 font-semibold\">Enable notifications</span>\n </div>\n );\n}\n"],"mappings":";;;;;AAKA,SAAwB,IAAa;CACnC,IAAM,CAAC,GAAW,KAAgB,EAAS,GAAM;AAEjD,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,GAAD;GAAQ,SAAS;GAAW,eAAe,EAAa,CAAC,EAAA;GAAc,CAAA,EACvE,kBAAC,QAAD;GAAM,WAAU;aAAwB;GAA2B,CAAA,CAAA"}
1
+ {"version":3,"file":"switch-demo.js","names":[],"sources":["../../lib/examples/switch-demo.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport { Switch } from '@/ui/forms/Switch';\n\nexport default function SwitchDemo() {\n const [isEnabled, setIsEnabled] = useState(false);\n\n return (\n <div className=\"flex items-center gap-3\">\n <Switch checked={isEnabled} onChange={(e) => setIsEnabled(e.target.checked)} />\n <span className=\"text-b2 font-semibold\">Enable notifications</span>\n </div>\n );\n}\n"],"mappings":";;;;;AAKA,SAAwB,IAAa;CACnC,IAAM,CAAC,GAAW,KAAgB,EAAS,GAAM;AAEjD,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,GAAD;GAAQ,SAAS;GAAW,WAAW,MAAM,EAAa,EAAE,OAAO,QAAA;GAAY,CAAA,EAC/E,kBAAC,QAAD;GAAM,WAAU;aAAwB;GAA2B,CAAA,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clubmed/trident-ui",
3
- "version": "2.0.0-beta.38",
3
+ "version": "2.0.0-beta.40",
4
4
  "type": "module",
5
5
  "description": "Shared ClubMed React UI components",
6
6
  "keywords": [
@@ -90,62 +90,21 @@
90
90
  --tw-ring-color: color-mix(in oklab, var(--color-lavender) 20%, transparent);
91
91
  }
92
92
  /* Switch */
93
- label > span > input[type='checkbox'][role='switch'] + span {
94
- @apply transition-all;
93
+ input[role='switch'][data-name='Switch'] + span {
95
94
  @apply relative flex items-center transition-colors duration-200;
96
95
  @apply ring-0;
97
96
  --tw-ring-color: color-mix(in oklab, var(--color-lavender) 0%, transparent);
98
- background-color: var(--color-middleGrey);
99
- border-radius: var(--radius-pill);
100
- }
101
- label > span > input[type='checkbox'][role='switch'] + span > svg {
102
- transition: all var(--transition-duration-boop) var(--transition-fn-boop);
103
- transform: translate(calc(0% + 4px));
104
- }
105
- label > span > input[type='checkbox'][role='switch'] + span > svg circle + circle {
106
- opacity: 1;
107
- transition: opacity var(--transition-duration-boop) var(--transition-fn-boop);
108
- }
109
- label > span > input[type='checkbox'][role='switch'] + span > svg path {
110
- opacity: 0;
111
- scale: 0;
112
- transform-origin: center;
113
- transition:
114
- opacity var(--transition-duration-boop) var(--transition-fn-boop),
115
- scale var(--transition-duration-boop) var(--transition-fn-boop);
116
- }
117
- label > span > input[type='checkbox'][role='switch']:checked + span {
118
- background-color: var(--color-saffron);
119
- }
120
- label > span > input[type='checkbox'][role='switch']:checked + span > svg {
121
- transform: translate(calc(100% + 4px));
122
- }
123
- label > span > input[type='checkbox'][role='switch']:checked + span > svg circle + circle {
124
- opacity: 0;
125
- }
126
- label > span > input[type='checkbox'][role='switch']:checked + span > svg path {
127
- scale: 1;
128
- opacity: 1;
129
- }
130
- label > span > input[type='checkbox'][role='switch']:focus-visible + span {
131
- @apply ring-8;
132
- --tw-ring-color: color-mix(in oklab, var(--color-lavender) 20%, transparent);
133
97
  }
134
98
 
135
- button[role='switch'][data-name='Switch'] {
136
- @apply relative flex items-center transition-colors duration-200;
137
- @apply ring-0;
138
- --tw-ring-color: color-mix(in oklab, var(--color-lavender) 0%, transparent);
139
- }
140
- button[role='switch'][data-name='Switch'] svg {
99
+ input[role='switch'][data-name='Switch'] + span svg {
141
100
  transition: all var(--transition-duration-boop) var(--transition-fn-boop);
142
101
  transform: translate(calc(0%));
143
102
  }
144
- button[role='switch'][data-name='Switch'] svg circle + circle {
103
+ input[role='switch'][data-name='Switch'] + span svg circle + circle {
145
104
  opacity: 1;
146
105
  transition: opacity var(--transition-duration-boop) var(--transition-fn-boop);
147
106
  }
148
- button[role='switch'][data-name='Switch'] svg path {
107
+ input[role='switch'][data-name='Switch'] + span svg path {
149
108
  opacity: 0;
150
109
  scale: 0;
151
110
  transform-origin: center;
@@ -153,20 +112,17 @@
153
112
  opacity var(--transition-duration-boop) var(--transition-fn-boop),
154
113
  scale var(--transition-duration-boop) var(--transition-fn-boop);
155
114
  }
156
- button[role='switch'][data-name='Switch'][aria-checked='true'] {
157
- background-color: var(--color-saffron);
158
- }
159
- button[role='switch'][data-name='Switch'][aria-checked='true'] svg {
115
+ input[role='switch'][data-name='Switch']:checked + span svg {
160
116
  transform: translate(calc(100%));
161
117
  }
162
- button[role='switch'][data-name='Switch'][aria-checked='true'] svg circle + circle {
118
+ input[role='switch'][data-name='Switch']:checked + span svg circle + circle {
163
119
  opacity: 0;
164
120
  }
165
- button[role='switch'][data-name='Switch'][aria-checked='true'] svg path {
121
+ input[role='switch'][data-name='Switch']:checked + span svg path {
166
122
  scale: 1;
167
123
  opacity: 1;
168
124
  }
169
- button[role='switch'][data-name='Switch']:focus-visible {
125
+ input[role='switch'][data-name='Switch']:focus-visible + span {
170
126
  @apply ring-8;
171
127
  --tw-ring-color: color-mix(in oklab, var(--color-lavender) 20%, transparent);
172
128
  }
@@ -1,22 +1,17 @@
1
+ import { IconicTypes } from '@clubmed/trident-icons';
1
2
  import { FormControlProps } from './FormControl';
2
- import { IconicNames, IconicTypes } from '@clubmed/trident-icons';
3
- import { PhonePrefix } from '../helpers/phone';
3
+ import { PhoneFieldFullProps } from './PhoneFieldFullInput';
4
+ import { PhoneFieldSplitProps } from './PhoneFieldSplitInput';
4
5
  export interface PhoneValue {
5
6
  full: string;
6
7
  prefix?: string;
7
8
  number?: string;
8
9
  raw: string;
9
10
  }
10
- export interface PhoneFieldProps<Value = PhoneValue> extends FormControlProps<Value> {
11
+ export type PhoneFieldProps<Value = PhoneValue> = FormControlProps<Value> & PhoneFieldFullProps & PhoneFieldSplitProps & {
11
12
  mode?: 'full' | 'split';
12
13
  pattern?: string;
13
- prefixes?: PhonePrefix[];
14
- defaultPrefix?: string;
15
- description?: string;
16
- icon?: IconicNames;
17
14
  iconType?: IconicTypes;
18
- errorMessage?: string;
19
- dataTestId?: string;
20
15
  placeholder?: string;
21
- }
16
+ };
22
17
  export declare const PhoneField: <Value = PhoneValue>(props: PhoneFieldProps<Value>) => import("react/jsx-runtime").JSX.Element;
@@ -1,219 +1,37 @@
1
- import { t as e } from "../../chunks/clsx.js";
2
- import { useInternalStatus as t } from "../hooks/useInternalStatus.js";
3
- import { FormControl as n } from "./FormControl.js";
4
- import { countDigitPlaceholders as r, extractDigits as i, formatWithPattern as a } from "../helpers/phone/formatters.js";
5
- import { DEFAULT_PHONE_PREFIXES as o } from "../helpers/phone/defaultPrefixes.js";
6
- import { useCallback as s, useEffect as c, useId as l, useRef as u, useState as d } from "react";
7
- import { Icon as f } from "@clubmed/trident-icons";
8
- import { jsx as p, jsxs as m } from "react/jsx-runtime";
1
+ import { FormControl as e } from "./FormControl.js";
2
+ import { PhoneFieldFullInput as t } from "./PhoneFieldFullInput.js";
3
+ import { PhoneFieldSplitInput as n } from "./PhoneFieldSplitInput.js";
4
+ import { useId as r } from "react";
5
+ import "@clubmed/trident-icons";
6
+ import { jsx as i } from "react/jsx-runtime";
9
7
  //#region lib/ui/forms/PhoneField.tsx
10
- var h = o, g = (e) => {
11
- let t = e.indexOf("#"), n = t > 0 ? e.substring(0, t) : "";
12
- return {
13
- literalPrefix: n,
14
- literalPrefixDigits: i(n)
15
- };
16
- }, _ = (e, t) => t.length > 0 && e.startsWith(t) ? e.substring(t.length) : e, v = (e, t, n) => {
17
- let r = 0, i = 0;
18
- for (let a = 0; a < t.length; a++) if (/\d/.test(t[a])) {
19
- if (i < n.length) {
20
- i++;
21
- continue;
22
- }
23
- if (r++, r === e) return a + 1;
24
- }
25
- return t.length;
26
- }, y = (o) => {
27
- let y = l(), { id: b = y, name: x = b, label: S, value: C, mode: w = "full", pattern: T = "## ## ## ## ##", prefixes: E = h, defaultPrefix: D = E[0]?.code || "+1", description: O, validationStatus: k = "default", icon: A, iconType: j, errorMessage: M, disabled: N = !1, required: P = !1, hideRequiredStar: F, className: I, dataTestId: L = "PhoneField", placeholder: R = "", onChange: z, ...B } = o, V = u(null), H = u(null), U = u(null), [W, G] = d(D), [K, q] = d(""), [J, Y] = d(""), X = t({
28
- isDisabled: N,
29
- validationStatus: k
30
- });
31
- c(() => {
32
- let e = w === "full" ? V : H;
33
- U.current !== null && e.current && (e.current.setSelectionRange(U.current, U.current), U.current = null);
34
- }, [
35
- J,
36
- K,
37
- w
38
- ]), c(() => {
39
- if (C && typeof C == "object" && "full" in C) {
40
- let e = C;
41
- w === "full" ? Y(e.full || "") : (G(e.prefix || D), q(e.number || ""));
42
- }
43
- }, [
44
- C,
45
- w,
46
- D
47
- ]);
48
- let Z = s((e, t, n) => {
49
- let r = {
50
- full: e,
51
- prefix: t,
52
- number: n,
53
- raw: i(e)
54
- };
55
- z?.(x, r);
56
- }, [z, x]), Q = s((e, t, n) => {
57
- if (e.key !== "Backspace" && e.key !== "Delete") return;
58
- let r = e.currentTarget, o = r.selectionStart || 0;
59
- if (o !== (r.selectionEnd || 0)) return;
60
- let { literalPrefixDigits: s } = g(T), c = e.key === "Backspace" ? t[o - 1] : t[o];
61
- if (!c || /\d/.test(c)) return;
62
- e.preventDefault();
63
- let l = _(i(t), s), u = _(i(t.substring(0, o)), s).length, d, f;
64
- if (e.key === "Backspace") {
65
- if (u === 0) return;
66
- d = l.slice(0, u - 1) + l.slice(u), f = u - 1;
67
- } else {
68
- if (u >= l.length) return;
69
- d = l.slice(0, u) + l.slice(u + 1), f = u;
70
- }
71
- let p = a(d, T);
72
- U.current = v(f, p, s), n ? (Y(p), Z(p)) : (q(p), G((e) => (Z(`${e} ${p}`.trim(), e, p), e)));
73
- }, [T, Z]), $ = s((e, t) => {
74
- let n = e.target.value, o = e.target.selectionStart || 0, { literalPrefixDigits: s } = g(T), c = _(i(n), s), l = r(T);
75
- if (c.length > l) return;
76
- let u = _(i(n.substring(0, o)), s).length, d = a(c, T);
77
- U.current = v(u, d, s), t ? (Y(d), Z(d)) : (q(d), G((e) => (Z(`${e} ${d}`.trim(), e, d), e)));
78
- }, [T, Z]), ee = s((e) => {
79
- G(e), q((t) => (Z(`${e} ${t}`.trim(), e, t), t));
80
- }, [Z]);
81
- return /* @__PURE__ */ p(n, {
82
- id: b,
83
- label: S,
84
- className: I,
85
- description: O,
8
+ var a = (a) => {
9
+ let { id: o, name: s, value: c, label: l, className: u, description: d, mode: f = "full", dataTestId: p = "PhoneField", disabled: m, required: h, hideRequiredStar: g, validationStatus: _, errorMessage: v, onChange: y, ...b } = a, x = y, S = r();
10
+ return /* @__PURE__ */ i(e, {
11
+ id: o || S,
12
+ label: l,
13
+ className: u,
14
+ description: d,
86
15
  dataName: "PhoneField",
87
- dataTestId: L,
88
- disabled: N,
89
- required: P,
90
- hideRequiredStar: F,
91
- validationStatus: k,
92
- errorMessage: M,
93
- children: w === "full" ? /* @__PURE__ */ m("div", {
94
- className: "relative",
95
- children: [/* @__PURE__ */ p("input", {
96
- ...B,
97
- ref: V,
98
- id: b,
99
- name: x,
100
- type: "tel",
101
- disabled: N,
102
- required: P,
103
- value: J,
104
- onChange: (e) => $(e, !0),
105
- onKeyDown: (e) => Q(e, J, !0),
106
- placeholder: R,
107
- className: e("text-b3 rounded-pill w-full border overflow-hidden px-20 py-12 font-normal outline-none", {
108
- "border-middleGrey focus:border-black active:border-black": X === "default",
109
- "ps-[52px]": A,
110
- "pe-[52px]": X === "error" || X === "success",
111
- "bg-white text-black": X !== "disabled",
112
- "bg-pearl border-middleGrey": X === "disabled",
113
- "border-red": X === "error",
114
- "border-green": X === "success"
115
- }),
116
- "aria-label": x
117
- }), /* @__PURE__ */ m("div", {
118
- className: e("pointer-events-none absolute inset-0 flex items-center justify-between px-20 py-12", {
119
- "text-grey": X === "disabled",
120
- "text-red": X === "error",
121
- "text-green": X === "success"
122
- }),
123
- children: [A && /* @__PURE__ */ p(f, {
124
- name: A,
125
- width: "24px"
126
- }), /* @__PURE__ */ m("span", {
127
- className: "ms-auto flex gap-x-8",
128
- children: [X === "error" && /* @__PURE__ */ p(f, {
129
- name: "CrossDefault",
130
- width: "24px",
131
- type: j
132
- }), X === "success" && /* @__PURE__ */ p(f, {
133
- name: "CheckDefault",
134
- width: "24px",
135
- type: j
136
- })]
137
- })]
138
- })]
139
- }) : /* @__PURE__ */ m("div", {
140
- className: "flex gap-8",
141
- children: [/* @__PURE__ */ m("div", {
142
- className: e("relative rounded-pill z-0 w-[130px] flex-shrink-0", {
143
- "bg-white": X !== "disabled",
144
- "bg-pearl border-middleGrey": X === "disabled"
145
- }),
146
- children: [/* @__PURE__ */ p("select", {
147
- disabled: N,
148
- value: W,
149
- onChange: (e) => ee(e.target.value),
150
- className: e("text-b3 rounded-pill w-full border overflow-hidden px-20 py-12 font-semibold outline-none appearance-none bg-transparent", {
151
- "border-middleGrey focus:border-black active:border-black": X === "default",
152
- "text-black": X !== "disabled",
153
- "bg-pearl border-middleGrey": X === "disabled",
154
- "border-red": X === "error",
155
- "border-green": X === "success",
156
- "pe-[40px]": !0
157
- }),
158
- id: `${b}-prefix`,
159
- "aria-label": `${x}-prefix`,
160
- children: E.map((e) => /* @__PURE__ */ p("option", {
161
- value: e.code,
162
- children: e.label ?? e.code
163
- }, e.code))
164
- }), /* @__PURE__ */ p("div", {
165
- className: "pointer-events-none absolute inset-0 flex items-center justify-end px-20 py-12 -z-1",
166
- children: /* @__PURE__ */ p(f, {
167
- name: "ArrowDefaultDown",
168
- type: "svg",
169
- width: "24px",
170
- color: "black"
171
- })
172
- })]
173
- }), /* @__PURE__ */ m("div", {
174
- className: "relative flex-1",
175
- children: [/* @__PURE__ */ p("input", {
176
- ref: H,
177
- type: "tel",
178
- disabled: N,
179
- required: P,
180
- value: K,
181
- onChange: (e) => $(e, !1),
182
- onKeyDown: (e) => Q(e, K, !1),
183
- placeholder: R,
184
- className: e("text-b3 rounded-pill w-full border overflow-hidden px-20 py-12 font-normal outline-none", {
185
- "border-middleGrey focus:border-black active:border-black": X === "default",
186
- "pe-[52px]": X === "error" || X === "success",
187
- "bg-white text-black": X !== "disabled",
188
- "bg-pearl border-middleGrey": X === "disabled",
189
- "border-red": X === "error",
190
- "border-green": X === "success"
191
- }),
192
- "aria-label": `${x}-number`
193
- }), /* @__PURE__ */ p("div", {
194
- className: e("pointer-events-none absolute inset-0 flex items-center justify-end px-20 py-12", {
195
- "text-grey": X === "disabled",
196
- "text-red": X === "error",
197
- "text-green": X === "success"
198
- }),
199
- children: /* @__PURE__ */ m("span", {
200
- className: "flex gap-x-8",
201
- children: [X === "error" && /* @__PURE__ */ p(f, {
202
- name: "CrossDefault",
203
- width: "24px",
204
- type: j
205
- }), X === "success" && /* @__PURE__ */ p(f, {
206
- name: "CheckDefault",
207
- width: "24px",
208
- type: j
209
- })]
210
- })
211
- })]
212
- })]
16
+ dataTestId: p,
17
+ disabled: m,
18
+ required: h,
19
+ hideRequiredStar: g,
20
+ validationStatus: _,
21
+ errorMessage: v,
22
+ children: i(f === "full" ? t : n, {
23
+ id: o,
24
+ name: s,
25
+ value: c,
26
+ disabled: m,
27
+ required: h,
28
+ validationStatus: _,
29
+ onChange: x,
30
+ ...b
213
31
  })
214
32
  });
215
33
  };
216
34
  //#endregion
217
- export { y as PhoneField };
35
+ export { a as PhoneField };
218
36
 
219
37
  //# sourceMappingURL=PhoneField.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PhoneField.js","names":[],"sources":["../../../lib/ui/forms/PhoneField.tsx"],"sourcesContent":["import clsx from 'clsx';\nimport { useInternalStatus } from '../hooks/useInternalStatus';\nimport { FormControl, type FormControlProps } from './FormControl';\nimport { Icon, type IconicNames, type IconicTypes } from '@clubmed/trident-icons';\nimport {\n type ChangeEvent,\n type KeyboardEvent,\n useCallback,\n useEffect,\n useId,\n useRef,\n useState,\n} from 'react';\nimport {\n extractDigits,\n formatWithPattern,\n countDigitPlaceholders,\n DEFAULT_PHONE_PREFIXES,\n type PhonePrefix,\n} from '../helpers/phone';\n\nexport interface PhoneValue {\n full: string;\n prefix?: string;\n number?: string;\n raw: string;\n}\n\nexport interface PhoneFieldProps<Value = PhoneValue> extends FormControlProps<Value> {\n mode?: 'full' | 'split';\n pattern?: string;\n prefixes?: PhonePrefix[];\n defaultPrefix?: string;\n description?: string;\n icon?: IconicNames;\n iconType?: IconicTypes;\n errorMessage?: string;\n dataTestId?: string;\n placeholder?: string;\n}\n\n// Extract default to constant to prevent breaking memoization (rerender-memo-with-default-value)\nconst DEFAULT_PREFIXES = DEFAULT_PHONE_PREFIXES;\n\n// Extract literal prefix from pattern (everything before first #)\nconst getLiteralPrefixInfo = (pattern: string) => {\n const firstHashIndex = pattern.indexOf('#');\n const literalPrefix = firstHashIndex > 0 ? pattern.substring(0, firstHashIndex) : '';\n const literalPrefixDigits = extractDigits(literalPrefix);\n return { literalPrefix, literalPrefixDigits };\n};\n\n// Strip literal prefix digits from digit string\nconst stripLiteralPrefix = (allDigits: string, literalPrefixDigits: string) => {\n if (literalPrefixDigits.length > 0 && allDigits.startsWith(literalPrefixDigits)) {\n return allDigits.substring(literalPrefixDigits.length);\n }\n return allDigits;\n};\n\n// Calculate cursor position after formatting\nconst calculateCursorPosition = (\n digitsBeforeCursor: number,\n formatted: string,\n literalPrefixDigits: string,\n) => {\n let digitCount = 0;\n let skippedPrefixDigits = 0;\n\n for (let i = 0; i < formatted.length; i++) {\n if (/\\d/.test(formatted[i])) {\n if (skippedPrefixDigits < literalPrefixDigits.length) {\n skippedPrefixDigits++;\n continue;\n }\n\n digitCount++;\n if (digitCount === digitsBeforeCursor) {\n return i + 1;\n }\n }\n }\n\n return formatted.length;\n};\n\nexport const PhoneField = <Value = PhoneValue,>(props: PhoneFieldProps<Value>) => {\n const internalId = useId();\n\n const {\n id = internalId,\n name = id,\n label,\n value: externalValue,\n mode = 'full',\n pattern = '## ## ## ## ##',\n prefixes = DEFAULT_PREFIXES,\n defaultPrefix = prefixes[0]?.code || '+1',\n description,\n validationStatus = 'default',\n icon,\n iconType,\n errorMessage,\n disabled = false,\n required = false,\n hideRequiredStar,\n className,\n dataTestId = 'PhoneField',\n placeholder = '',\n onChange,\n ...rest\n } = props;\n\n const inputRef = useRef<HTMLInputElement>(null);\n const numberInputRef = useRef<HTMLInputElement>(null);\n const cursorPositionRef = useRef<number | null>(null);\n const [prefix, setPrefix] = useState(defaultPrefix);\n const [number, setNumber] = useState('');\n const [fullNumber, setFullNumber] = useState('');\n\n const internalStatus = useInternalStatus({\n isDisabled: disabled,\n validationStatus,\n });\n\n // Apply cursor position after state updates\n useEffect(() => {\n const ref = mode === 'full' ? inputRef : numberInputRef;\n if (cursorPositionRef.current !== null && ref.current) {\n ref.current.setSelectionRange(cursorPositionRef.current, cursorPositionRef.current);\n cursorPositionRef.current = null;\n }\n }, [fullNumber, number, mode]);\n\n // Initialize from external value\n useEffect(() => {\n if (externalValue && typeof externalValue === 'object' && 'full' in externalValue) {\n const phoneValue = externalValue as unknown as PhoneValue;\n if (mode === 'full') {\n setFullNumber(phoneValue.full || '');\n } else {\n setPrefix(phoneValue.prefix || defaultPrefix);\n setNumber(phoneValue.number || '');\n }\n }\n }, [externalValue, mode, defaultPrefix]);\n\n // Emit onChange with PhoneValue structure\n const emitChange = useCallback(\n (full: string, prefix?: string, number?: string) => {\n const raw = extractDigits(full);\n const phoneValue: PhoneValue = {\n full,\n prefix,\n number,\n raw,\n };\n onChange?.(name, phoneValue as Value);\n },\n [onChange, name],\n );\n\n // Unified handler for backspace/delete on non-digit characters\n const handleKeyDown = useCallback(\n (e: KeyboardEvent<HTMLInputElement>, currentValue: string, isFullMode: boolean) => {\n if (e.key !== 'Backspace' && e.key !== 'Delete') {\n return;\n }\n\n const input = e.currentTarget;\n const cursorPosition = input.selectionStart || 0;\n const selectionEnd = input.selectionEnd || 0;\n\n // If there's a selection, let default behavior handle it\n if (cursorPosition !== selectionEnd) {\n return;\n }\n\n const { literalPrefixDigits } = getLiteralPrefixInfo(pattern);\n\n // Check if we're on a non-digit character\n const targetChar =\n e.key === 'Backspace' ? currentValue[cursorPosition - 1] : currentValue[cursorPosition];\n\n if (!targetChar || /\\d/.test(targetChar)) {\n return;\n }\n\n e.preventDefault();\n\n // Extract and clean digits\n const allDigits = stripLiteralPrefix(extractDigits(currentValue), literalPrefixDigits);\n const digitsBeforeCursorStr = stripLiteralPrefix(\n extractDigits(currentValue.substring(0, cursorPosition)),\n literalPrefixDigits,\n );\n const digitsBeforeCursor = digitsBeforeCursorStr.length;\n\n // Calculate new digits based on key\n let newDigits: string;\n let targetCursorDigits: number;\n\n if (e.key === 'Backspace') {\n if (digitsBeforeCursor === 0) return;\n newDigits =\n allDigits.slice(0, digitsBeforeCursor - 1) + allDigits.slice(digitsBeforeCursor);\n targetCursorDigits = digitsBeforeCursor - 1;\n } else {\n // Delete\n if (digitsBeforeCursor >= allDigits.length) return;\n newDigits =\n allDigits.slice(0, digitsBeforeCursor) + allDigits.slice(digitsBeforeCursor + 1);\n targetCursorDigits = digitsBeforeCursor;\n }\n\n // Format and update\n const formatted = formatWithPattern(newDigits, pattern);\n const newCursor = calculateCursorPosition(targetCursorDigits, formatted, literalPrefixDigits);\n cursorPositionRef.current = newCursor;\n\n if (isFullMode) {\n setFullNumber(formatted);\n emitChange(formatted);\n } else {\n setNumber(formatted);\n setPrefix((currentPrefix) => {\n const combined = `${currentPrefix} ${formatted}`.trim();\n emitChange(combined, currentPrefix, formatted);\n return currentPrefix;\n });\n }\n },\n [pattern, emitChange],\n );\n\n // Unified handler for input changes\n const handleInputChange = useCallback(\n (e: ChangeEvent<HTMLInputElement>, isFullMode: boolean) => {\n const inputValue = e.target.value;\n const cursorPosition = e.target.selectionStart || 0;\n\n const { literalPrefixDigits } = getLiteralPrefixInfo(pattern);\n\n // Extract and clean digits\n const allDigits = stripLiteralPrefix(extractDigits(inputValue), literalPrefixDigits);\n\n // Check against pattern limit\n const maxDigits = countDigitPlaceholders(pattern);\n if (allDigits.length > maxDigits) {\n return;\n }\n\n // Count digits before cursor\n const digitsBeforeCursorInInput = stripLiteralPrefix(\n extractDigits(inputValue.substring(0, cursorPosition)),\n literalPrefixDigits,\n );\n const digitsBeforeCursor = digitsBeforeCursorInInput.length;\n\n // Format the value\n const formatted = formatWithPattern(allDigits, pattern);\n\n // Calculate cursor position\n const newCursor = calculateCursorPosition(digitsBeforeCursor, formatted, literalPrefixDigits);\n cursorPositionRef.current = newCursor;\n\n // Update state\n if (isFullMode) {\n setFullNumber(formatted);\n emitChange(formatted);\n } else {\n setNumber(formatted);\n setPrefix((currentPrefix) => {\n const combined = `${currentPrefix} ${formatted}`.trim();\n emitChange(combined, currentPrefix, formatted);\n return currentPrefix;\n });\n }\n },\n [pattern, emitChange],\n );\n\n // Split mode: handle prefix change\n const handlePrefixChange = useCallback(\n (newPrefix: string) => {\n setPrefix(newPrefix);\n setNumber((currentNumber) => {\n const combined = `${newPrefix} ${currentNumber}`.trim();\n emitChange(combined, newPrefix, currentNumber);\n return currentNumber;\n });\n },\n [emitChange],\n );\n\n const formControlProps = {\n id,\n label,\n className,\n description,\n dataName: 'PhoneField',\n dataTestId,\n disabled,\n required,\n hideRequiredStar,\n validationStatus,\n errorMessage,\n };\n\n return (\n <FormControl {...formControlProps}>\n {mode === 'full' ? (\n // Full mode: single input with pattern masking\n <div className=\"relative\">\n <input\n {...(rest as any)}\n ref={inputRef}\n id={id}\n name={name}\n type=\"tel\"\n disabled={disabled}\n required={required}\n value={fullNumber}\n onChange={(e) => handleInputChange(e, true)}\n onKeyDown={(e) => handleKeyDown(e, fullNumber, true)}\n placeholder={placeholder}\n className={clsx(\n 'text-b3 rounded-pill w-full border overflow-hidden px-20 py-12 font-normal outline-none',\n {\n 'border-middleGrey focus:border-black active:border-black':\n internalStatus === 'default',\n 'ps-[52px]': icon,\n 'pe-[52px]': internalStatus === 'error' || internalStatus === 'success',\n 'bg-white text-black': internalStatus !== 'disabled',\n 'bg-pearl border-middleGrey': internalStatus === 'disabled',\n 'border-red': internalStatus === 'error',\n 'border-green': internalStatus === 'success',\n },\n )}\n aria-label={name}\n />\n\n <div\n className={clsx(\n 'pointer-events-none absolute inset-0 flex items-center justify-between px-20 py-12',\n {\n 'text-grey': internalStatus === 'disabled',\n 'text-red': internalStatus === 'error',\n 'text-green': internalStatus === 'success',\n },\n )}\n >\n {icon && <Icon name={icon} width=\"24px\" />}\n\n <span className=\"ms-auto flex gap-x-8\">\n {internalStatus === 'error' && (\n <Icon name=\"CrossDefault\" width=\"24px\" type={iconType} />\n )}\n\n {internalStatus === 'success' && (\n <Icon name=\"CheckDefault\" width=\"24px\" type={iconType} />\n )}\n </span>\n </div>\n </div>\n ) : (\n // Split mode: prefix dropdown + number input\n <div className=\"flex gap-8\">\n <div\n className={clsx('relative rounded-pill z-0 w-[130px] flex-shrink-0', {\n 'bg-white': internalStatus !== 'disabled',\n 'bg-pearl border-middleGrey': internalStatus === 'disabled',\n })}\n >\n <select\n disabled={disabled}\n value={prefix}\n onChange={(e) => handlePrefixChange(e.target.value)}\n className={clsx(\n 'text-b3 rounded-pill w-full border overflow-hidden px-20 py-12 font-semibold outline-none appearance-none bg-transparent',\n {\n 'border-middleGrey focus:border-black active:border-black':\n internalStatus === 'default',\n 'text-black': internalStatus !== 'disabled',\n 'bg-pearl border-middleGrey': internalStatus === 'disabled',\n 'border-red': internalStatus === 'error',\n 'border-green': internalStatus === 'success',\n 'pe-[40px]': true, // Space for dropdown arrow\n },\n )}\n id={`${id}-prefix`}\n aria-label={`${name}-prefix`}\n >\n {prefixes.map((p) => (\n <option key={p.code} value={p.code}>\n {p.label ?? p.code}\n </option>\n ))}\n </select>\n\n <div className=\"pointer-events-none absolute inset-0 flex items-center justify-end px-20 py-12 -z-1\">\n <Icon name=\"ArrowDefaultDown\" type=\"svg\" width=\"24px\" color=\"black\" />\n </div>\n </div>\n\n <div className=\"relative flex-1\">\n <input\n ref={numberInputRef}\n type=\"tel\"\n disabled={disabled}\n required={required}\n value={number}\n onChange={(e) => handleInputChange(e, false)}\n onKeyDown={(e) => handleKeyDown(e, number, false)}\n placeholder={placeholder}\n className={clsx(\n 'text-b3 rounded-pill w-full border overflow-hidden px-20 py-12 font-normal outline-none',\n {\n 'border-middleGrey focus:border-black active:border-black':\n internalStatus === 'default',\n 'pe-[52px]': internalStatus === 'error' || internalStatus === 'success',\n 'bg-white text-black': internalStatus !== 'disabled',\n 'bg-pearl border-middleGrey': internalStatus === 'disabled',\n 'border-red': internalStatus === 'error',\n 'border-green': internalStatus === 'success',\n },\n )}\n aria-label={`${name}-number`}\n />\n\n <div\n className={clsx(\n 'pointer-events-none absolute inset-0 flex items-center justify-end px-20 py-12',\n {\n 'text-grey': internalStatus === 'disabled',\n 'text-red': internalStatus === 'error',\n 'text-green': internalStatus === 'success',\n },\n )}\n >\n <span className=\"flex gap-x-8\">\n {internalStatus === 'error' && (\n <Icon name=\"CrossDefault\" width=\"24px\" type={iconType} />\n )}\n\n {internalStatus === 'success' && (\n <Icon name=\"CheckDefault\" width=\"24px\" type={iconType} />\n )}\n </span>\n </div>\n </div>\n </div>\n )}\n </FormControl>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"PhoneField.js","names":[],"sources":["../../../lib/ui/forms/PhoneField.tsx"],"sourcesContent":["import { type IconicTypes } from '@clubmed/trident-icons';\nimport { FormControl, type FormControlProps } from './FormControl';\nimport { PhoneFieldFullInput, type PhoneFieldFullProps } from './PhoneFieldFullInput';\nimport { PhoneFieldSplitInput, type PhoneFieldSplitProps } from './PhoneFieldSplitInput';\nimport { useId } from 'react';\n\nexport interface PhoneValue {\n full: string;\n prefix?: string;\n number?: string;\n raw: string;\n}\n\nexport type PhoneFieldProps<Value = PhoneValue> = FormControlProps<Value> &\n PhoneFieldFullProps &\n PhoneFieldSplitProps & {\n mode?: 'full' | 'split';\n pattern?: string;\n iconType?: IconicTypes;\n placeholder?: string;\n };\n\nexport const PhoneField = <Value = PhoneValue,>(props: PhoneFieldProps<Value>) => {\n const {\n id,\n name,\n value,\n label,\n className,\n description,\n mode = 'full',\n dataTestId = 'PhoneField',\n disabled,\n required,\n hideRequiredStar,\n validationStatus,\n errorMessage,\n onChange,\n ...rest\n } = props;\n const onPhoneChange = onChange as ((name: string, value: PhoneValue) => void) | undefined;\n const internalId = useId();\n\n const formControlProps = {\n id: id || internalId,\n label,\n className,\n description,\n dataName: 'PhoneField',\n dataTestId,\n disabled,\n required,\n hideRequiredStar,\n validationStatus,\n errorMessage,\n };\n\n return (\n <FormControl {...formControlProps}>\n {mode === 'full' ? (\n <PhoneFieldFullInput\n id={id}\n name={name}\n value={value as PhoneValue | undefined}\n disabled={disabled}\n required={required}\n validationStatus={validationStatus}\n onChange={onPhoneChange}\n {...rest}\n />\n ) : (\n <PhoneFieldSplitInput\n id={id}\n name={name}\n value={value as PhoneValue | undefined}\n disabled={disabled}\n required={required}\n validationStatus={validationStatus}\n onChange={onPhoneChange}\n {...rest}\n />\n )}\n </FormControl>\n );\n};\n"],"mappings":";;;;;;;AAsBA,IAAa,KAAmC,MAAkC;CAChF,IAAM,EACJ,OACA,SACA,UACA,UACA,cACA,gBACA,UAAO,QACP,gBAAa,cACb,aACA,aACA,qBACA,qBACA,iBACA,aACA,GAAG,MACD,GACE,IAAgB,GAChB,IAAa,GAAO;AAgB1B,QACE,kBAAC,GAAD;EAdA,IAAI,KAAM;EACV;EACA;EACA;EACA,UAAU;EACV;EACA;EACA;EACA;EACA;EACA;YAMI,EADD,MAAS,SACP,IAWA,GAXD;GACM;GACE;GACC;GACG;GACA;GACQ;GAClB,UAAU;GACV,GAAI;GACJ,CAWA;EAEQ,CAAA"}
@@ -0,0 +1,16 @@
1
+ import { IconicNames, IconicTypes } from '@clubmed/trident-icons';
2
+ import { InputHTMLAttributes } from 'react';
3
+ import { FormControlProps } from './FormControl';
4
+ import { PhoneValue } from './PhoneField';
5
+ export interface PhoneFieldFullProps {
6
+ icon?: IconicNames;
7
+ }
8
+ export interface PhoneFieldFullInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'value' | 'onChange' | 'pattern'> {
9
+ value?: PhoneValue;
10
+ pattern?: string;
11
+ icon?: PhoneFieldFullProps['icon'];
12
+ iconType?: IconicTypes;
13
+ validationStatus?: FormControlProps<PhoneValue>['validationStatus'];
14
+ onChange?: (name: string, value: PhoneValue) => void;
15
+ }
16
+ export declare const PhoneFieldFullInput: ({ id, name, value: externalValue, pattern, placeholder, icon, iconType, disabled, required, validationStatus, onChange, ...inputProps }: PhoneFieldFullInputProps) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,116 @@
1
+ import { t as e } from "../../chunks/clsx.js";
2
+ import { useInternalStatus as t } from "../hooks/useInternalStatus.js";
3
+ import { calculateCursorPosition as n, extractDigits as r, formatPhoneDigits as i, formatWithPattern as a, getLiteralPrefixInfo as o, stripLiteralPrefix as s } from "../helpers/phone/formatters.js";
4
+ import { useCallback as c, useEffect as l, useId as u, useRef as d, useState as f } from "react";
5
+ import { Icon as p } from "@clubmed/trident-icons";
6
+ import { jsx as m, jsxs as h } from "react/jsx-runtime";
7
+ //#region lib/ui/forms/PhoneFieldFullInput.tsx
8
+ var g = (e) => {
9
+ let { externalValue: t, name: u, pattern: p, onChange: m } = e, h = d(null), g = d(null), [_, v] = f("");
10
+ return l(() => {
11
+ g.current !== null && h.current && (h.current.setSelectionRange(g.current, g.current), g.current = null);
12
+ }, [_]), l(() => {
13
+ t && typeof t == "object" && "full" in t && v(t.full || "");
14
+ }, [t]), {
15
+ inputRef: h,
16
+ value: _,
17
+ onKeyDown: c((e) => {
18
+ if (e.key !== "Backspace" && e.key !== "Delete") return;
19
+ let t = e.currentTarget.selectionStart || 0;
20
+ if (t !== (e.currentTarget.selectionEnd || 0)) return;
21
+ let { literalPrefixDigits: i } = o(p), c = e.key === "Backspace" ? _[t - 1] : _[t];
22
+ if (!c || /\d/.test(c)) return;
23
+ e.preventDefault();
24
+ let l = s(r(_), i), d = s(r(_.substring(0, t)), i).length, f, h;
25
+ if (e.key === "Backspace") {
26
+ if (d === 0) return;
27
+ f = l.slice(0, d - 1) + l.slice(d), h = d - 1;
28
+ } else {
29
+ if (d >= l.length) return;
30
+ f = l.slice(0, d) + l.slice(d + 1), h = d;
31
+ }
32
+ let y = a(f, p);
33
+ g.current = n(h, y, i), v(y), m?.(u, {
34
+ full: y,
35
+ raw: r(y)
36
+ });
37
+ }, [
38
+ u,
39
+ p,
40
+ _,
41
+ m
42
+ ]),
43
+ onInputChange: c((e) => {
44
+ let t = i(e.target.value, p, e.target.selectionStart || 0);
45
+ t && (g.current = t.nextCursor, v(t.formatted), m?.(u, {
46
+ full: t.formatted,
47
+ raw: r(t.formatted)
48
+ }));
49
+ }, [
50
+ u,
51
+ p,
52
+ m
53
+ ])
54
+ };
55
+ }, _ = ({ id: n, name: r, value: i, pattern: a = "## ## ## ## ##", placeholder: o = "", icon: s, iconType: c, disabled: l = !1, required: d = !1, validationStatus: f = "default", onChange: _, ...v }) => {
56
+ let y = u(), b = n ?? y, x = r ?? b, S = t({
57
+ isDisabled: l,
58
+ validationStatus: f
59
+ }), { inputRef: C, value: w, onKeyDown: T, onInputChange: E } = g({
60
+ externalValue: i,
61
+ name: x,
62
+ pattern: a,
63
+ onChange: _
64
+ });
65
+ return /* @__PURE__ */ h("div", {
66
+ className: "relative",
67
+ children: [/* @__PURE__ */ m("input", {
68
+ ...v,
69
+ ref: C,
70
+ id: b,
71
+ name: x,
72
+ type: "tel",
73
+ disabled: l,
74
+ required: d,
75
+ value: w,
76
+ onChange: E,
77
+ onKeyDown: T,
78
+ placeholder: o,
79
+ className: e("text-b3 rounded-pill w-full border overflow-hidden px-20 py-12 font-normal outline-none", {
80
+ "border-middleGrey focus:border-black active:border-black": S === "default",
81
+ "ps-[52px]": s,
82
+ "pe-[52px]": S === "error" || S === "success",
83
+ "bg-white text-black": S !== "disabled",
84
+ "bg-pearl border-middleGrey": S === "disabled",
85
+ "border-red": S === "error",
86
+ "border-green": S === "success"
87
+ }),
88
+ "aria-label": x
89
+ }), /* @__PURE__ */ h("div", {
90
+ className: e("pointer-events-none absolute inset-0 flex items-center justify-between px-20 py-12", {
91
+ "text-grey": S === "disabled",
92
+ "text-red": S === "error",
93
+ "text-green": S === "success"
94
+ }),
95
+ children: [s && /* @__PURE__ */ m(p, {
96
+ name: s,
97
+ width: "24px"
98
+ }), /* @__PURE__ */ h("span", {
99
+ className: "ms-auto flex gap-x-8",
100
+ children: [S === "error" && /* @__PURE__ */ m(p, {
101
+ name: "CrossDefault",
102
+ width: "24px",
103
+ type: c
104
+ }), S === "success" && /* @__PURE__ */ m(p, {
105
+ name: "CheckDefault",
106
+ width: "24px",
107
+ type: c
108
+ })]
109
+ })]
110
+ })]
111
+ });
112
+ };
113
+ //#endregion
114
+ export { _ as PhoneFieldFullInput };
115
+
116
+ //# sourceMappingURL=PhoneFieldFullInput.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PhoneFieldFullInput.js","names":[],"sources":["../../../lib/ui/forms/PhoneFieldFullInput.tsx"],"sourcesContent":["import clsx from 'clsx';\nimport { Icon, type IconicNames, type IconicTypes } from '@clubmed/trident-icons';\nimport {\n type ChangeEvent,\n type InputHTMLAttributes,\n type KeyboardEvent,\n useCallback,\n useEffect,\n useId,\n useRef,\n useState,\n} from 'react';\nimport {\n calculateCursorPosition,\n extractDigits,\n formatPhoneDigits,\n formatWithPattern,\n getLiteralPrefixInfo,\n stripLiteralPrefix,\n} from '../helpers/phone';\nimport { useInternalStatus } from '../hooks/useInternalStatus';\nimport type { FormControlProps } from './FormControl';\nimport type { PhoneValue } from './PhoneField';\n\nexport interface PhoneFieldFullProps {\n icon?: IconicNames;\n}\n\nconst usePhoneField = (args: {\n externalValue?: PhoneValue;\n name: string;\n pattern: string;\n onChange?: (name: string, value: PhoneValue) => void;\n}) => {\n const { externalValue, name, pattern, onChange } = args;\n const inputRef = useRef<HTMLInputElement>(null);\n const cursorPositionRef = useRef<number | null>(null);\n const [value, setValue] = useState('');\n\n useEffect(() => {\n if (cursorPositionRef.current !== null && inputRef.current) {\n inputRef.current.setSelectionRange(cursorPositionRef.current, cursorPositionRef.current);\n cursorPositionRef.current = null;\n }\n }, [value]);\n\n useEffect(() => {\n if (externalValue && typeof externalValue === 'object' && 'full' in externalValue) {\n setValue(externalValue.full || '');\n }\n }, [externalValue]);\n\n const onKeyDown = useCallback(\n (e: KeyboardEvent<HTMLInputElement>) => {\n if (e.key !== 'Backspace' && e.key !== 'Delete') return;\n const cursorPosition = e.currentTarget.selectionStart || 0;\n const selectionEnd = e.currentTarget.selectionEnd || 0;\n if (cursorPosition !== selectionEnd) return;\n\n const { literalPrefixDigits } = getLiteralPrefixInfo(pattern);\n const targetChar = e.key === 'Backspace' ? value[cursorPosition - 1] : value[cursorPosition];\n if (!targetChar || /\\d/.test(targetChar)) return;\n\n e.preventDefault();\n const allDigits = stripLiteralPrefix(extractDigits(value), literalPrefixDigits);\n const digitsBeforeCursor = stripLiteralPrefix(\n extractDigits(value.substring(0, cursorPosition)),\n literalPrefixDigits,\n ).length;\n\n let newDigits: string;\n let targetCursorDigits: number;\n if (e.key === 'Backspace') {\n if (digitsBeforeCursor === 0) return;\n newDigits =\n allDigits.slice(0, digitsBeforeCursor - 1) + allDigits.slice(digitsBeforeCursor);\n targetCursorDigits = digitsBeforeCursor - 1;\n } else {\n if (digitsBeforeCursor >= allDigits.length) return;\n newDigits =\n allDigits.slice(0, digitsBeforeCursor) + allDigits.slice(digitsBeforeCursor + 1);\n targetCursorDigits = digitsBeforeCursor;\n }\n\n const formatted = formatWithPattern(newDigits, pattern);\n cursorPositionRef.current = calculateCursorPosition(\n targetCursorDigits,\n formatted,\n literalPrefixDigits,\n );\n setValue(formatted);\n onChange?.(name, { full: formatted, raw: extractDigits(formatted) });\n },\n [name, pattern, value, onChange],\n );\n\n const onInputChange = useCallback(\n (e: ChangeEvent<HTMLInputElement>) => {\n const formattedValue = formatPhoneDigits(\n e.target.value,\n pattern,\n e.target.selectionStart || 0,\n );\n if (!formattedValue) return;\n cursorPositionRef.current = formattedValue.nextCursor;\n setValue(formattedValue.formatted);\n\n onChange?.(name, {\n full: formattedValue.formatted,\n raw: extractDigits(formattedValue.formatted),\n });\n },\n [name, pattern, onChange],\n );\n\n return { inputRef, value, onKeyDown, onInputChange };\n};\n\nexport interface PhoneFieldFullInputProps\n extends Omit<InputHTMLAttributes<HTMLInputElement>, 'value' | 'onChange' | 'pattern'> {\n value?: PhoneValue;\n pattern?: string;\n icon?: PhoneFieldFullProps['icon'];\n iconType?: IconicTypes;\n validationStatus?: FormControlProps<PhoneValue>['validationStatus'];\n onChange?: (name: string, value: PhoneValue) => void;\n}\n\nexport const PhoneFieldFullInput = ({\n id,\n name,\n value: externalValue,\n pattern = '## ## ## ## ##',\n placeholder = '',\n icon,\n iconType,\n disabled = false,\n required = false,\n validationStatus = 'default',\n onChange,\n ...inputProps\n}: PhoneFieldFullInputProps) => {\n const internalId = useId();\n const resolvedId = id ?? internalId;\n const resolvedName = name ?? resolvedId;\n\n const internalStatus = useInternalStatus({ isDisabled: disabled, validationStatus });\n const { inputRef, value, onKeyDown, onInputChange } = usePhoneField({\n externalValue,\n name: resolvedName,\n pattern,\n onChange,\n });\n\n return (\n <div className=\"relative\">\n <input\n {...(inputProps as any)}\n ref={inputRef}\n id={resolvedId}\n name={resolvedName}\n type=\"tel\"\n disabled={disabled}\n required={required}\n value={value}\n onChange={onInputChange}\n onKeyDown={onKeyDown}\n placeholder={placeholder}\n className={clsx(\n 'text-b3 rounded-pill w-full border overflow-hidden px-20 py-12 font-normal outline-none',\n {\n 'border-middleGrey focus:border-black active:border-black':\n internalStatus === 'default',\n 'ps-[52px]': icon,\n 'pe-[52px]': internalStatus === 'error' || internalStatus === 'success',\n 'bg-white text-black': internalStatus !== 'disabled',\n 'bg-pearl border-middleGrey': internalStatus === 'disabled',\n 'border-red': internalStatus === 'error',\n 'border-green': internalStatus === 'success',\n },\n )}\n aria-label={resolvedName}\n />\n <div\n className={clsx(\n 'pointer-events-none absolute inset-0 flex items-center justify-between px-20 py-12',\n {\n 'text-grey': internalStatus === 'disabled',\n 'text-red': internalStatus === 'error',\n 'text-green': internalStatus === 'success',\n },\n )}\n >\n {icon && <Icon name={icon} width=\"24px\" />}\n <span className=\"ms-auto flex gap-x-8\">\n {internalStatus === 'error' && <Icon name=\"CrossDefault\" width=\"24px\" type={iconType} />}\n {internalStatus === 'success' && (\n <Icon name=\"CheckDefault\" width=\"24px\" type={iconType} />\n )}\n </span>\n </div>\n </div>\n );\n};\n"],"mappings":";;;;;;;AA4BA,IAAM,KAAiB,MAKjB;CACJ,IAAM,EAAE,kBAAe,SAAM,YAAS,gBAAa,GAC7C,IAAW,EAAyB,KAAK,EACzC,IAAoB,EAAsB,KAAK,EAC/C,CAAC,GAAO,KAAY,EAAS,GAAG;AA8EtC,QA5EA,QAAgB;AACd,EAAI,EAAkB,YAAY,QAAQ,EAAS,YACjD,EAAS,QAAQ,kBAAkB,EAAkB,SAAS,EAAkB,QAAQ,EACxF,EAAkB,UAAU;IAE7B,CAAC,EAAM,CAAC,EAEX,QAAgB;AACd,EAAI,KAAiB,OAAO,KAAkB,YAAY,UAAU,KAClE,EAAS,EAAc,QAAQ,GAAG;IAEnC,CAAC,EAAc,CAAC,EAiEZ;EAAE;EAAU;EAAO,WA/DR,GACf,MAAuC;AACtC,OAAI,EAAE,QAAQ,eAAe,EAAE,QAAQ,SAAU;GACjD,IAAM,IAAiB,EAAE,cAAc,kBAAkB;AAEzD,OAAI,OADiB,EAAE,cAAc,gBAAgB,GAChB;GAErC,IAAM,EAAE,2BAAwB,EAAqB,EAAQ,EACvD,IAAa,EAAE,QAAQ,cAAc,EAAM,IAAiB,KAAK,EAAM;AAC7E,OAAI,CAAC,KAAc,KAAK,KAAK,EAAW,CAAE;AAE1C,KAAE,gBAAgB;GAClB,IAAM,IAAY,EAAmB,EAAc,EAAM,EAAE,EAAoB,EACzE,IAAqB,EACzB,EAAc,EAAM,UAAU,GAAG,EAAe,CAAC,EACjD,EACD,CAAC,QAEE,GACA;AACJ,OAAI,EAAE,QAAQ,aAAa;AACzB,QAAI,MAAuB,EAAG;AAG9B,IAFA,IACE,EAAU,MAAM,GAAG,IAAqB,EAAE,GAAG,EAAU,MAAM,EAAmB,EAClF,IAAqB,IAAqB;UACrC;AACL,QAAI,KAAsB,EAAU,OAAQ;AAG5C,IAFA,IACE,EAAU,MAAM,GAAG,EAAmB,GAAG,EAAU,MAAM,IAAqB,EAAE,EAClF,IAAqB;;GAGvB,IAAM,IAAY,EAAkB,GAAW,EAAQ;AAOvD,GANA,EAAkB,UAAU,EAC1B,GACA,GACA,EACD,EACD,EAAS,EAAU,EACnB,IAAW,GAAM;IAAE,MAAM;IAAW,KAAK,EAAc,EAAU;IAAE,CAAC;KAEtE;GAAC;GAAM;GAAS;GAAO;GAAS,CACjC;EAqBoC,eAnBf,GACnB,MAAqC;GACpC,IAAM,IAAiB,EACrB,EAAE,OAAO,OACT,GACA,EAAE,OAAO,kBAAkB,EAC5B;AACI,SACL,EAAkB,UAAU,EAAe,YAC3C,EAAS,EAAe,UAAU,EAElC,IAAW,GAAM;IACf,MAAM,EAAe;IACrB,KAAK,EAAc,EAAe,UAAU;IAC7C,CAAC;KAEJ;GAAC;GAAM;GAAS;GAAS,CAC1B;EAEmD;GAazC,KAAuB,EAClC,OACA,SACA,OAAO,GACP,aAAU,kBACV,iBAAc,IACd,SACA,aACA,cAAW,IACX,cAAW,IACX,sBAAmB,WACnB,aACA,GAAG,QAC2B;CAC9B,IAAM,IAAa,GAAO,EACpB,IAAa,KAAM,GACnB,IAAe,KAAQ,GAEvB,IAAiB,EAAkB;EAAE,YAAY;EAAU;EAAkB,CAAC,EAC9E,EAAE,aAAU,UAAO,cAAW,qBAAkB,EAAc;EAClE;EACA,MAAM;EACN;EACA;EACD,CAAC;AAEF,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,SAAD;GACE,GAAK;GACL,KAAK;GACL,IAAI;GACJ,MAAM;GACN,MAAK;GACK;GACA;GACH;GACP,UAAU;GACC;GACE;GACb,WAAW,EACT,2FACA;IACE,4DACE,MAAmB;IACrB,aAAa;IACb,aAAa,MAAmB,WAAW,MAAmB;IAC9D,uBAAuB,MAAmB;IAC1C,8BAA8B,MAAmB;IACjD,cAAc,MAAmB;IACjC,gBAAgB,MAAmB;IACpC,CACF;GACD,cAAY;GACZ,CAAA,EACF,kBAAC,OAAD;GACE,WAAW,EACT,sFACA;IACE,aAAa,MAAmB;IAChC,YAAY,MAAmB;IAC/B,cAAc,MAAmB;IAClC,CACF;aARH,CAUG,KAAQ,kBAAC,GAAD;IAAM,MAAM;IAAM,OAAM;IAAS,CAAA,EAC1C,kBAAC,QAAD;IAAM,WAAU;cAAhB,CACG,MAAmB,WAAW,kBAAC,GAAD;KAAM,MAAK;KAAe,OAAM;KAAO,MAAM;KAAY,CAAA,EACvF,MAAmB,aAClB,kBAAC,GAAD;KAAM,MAAK;KAAe,OAAM;KAAO,MAAM;KAAY,CAAA,CAEtD;MACH;KACF"}
@@ -0,0 +1,19 @@
1
+ import { IconicTypes } from '@clubmed/trident-icons';
2
+ import { InputHTMLAttributes } from 'react';
3
+ import { PhonePrefix } from '../helpers/phone';
4
+ import { FormControlProps } from './FormControl';
5
+ import { PhoneValue } from './PhoneField';
6
+ export interface PhoneFieldSplitProps {
7
+ prefixes?: PhonePrefix[];
8
+ defaultPrefix?: string;
9
+ }
10
+ export interface PhoneFieldSplitInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'value' | 'onChange' | 'pattern'> {
11
+ value?: PhoneValue;
12
+ pattern?: string;
13
+ prefixes?: PhoneFieldSplitProps['prefixes'];
14
+ defaultPrefix?: PhoneFieldSplitProps['defaultPrefix'];
15
+ iconType?: IconicTypes;
16
+ validationStatus?: FormControlProps<PhoneValue>['validationStatus'];
17
+ onChange?: (name: string, value: PhoneValue) => void;
18
+ }
19
+ export declare const PhoneFieldSplitInput: ({ id, name, value: externalValue, pattern, prefixes, defaultPrefix, placeholder, iconType, disabled, required, validationStatus, onChange, ...rest }: PhoneFieldSplitInputProps) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,159 @@
1
+ import { t as e } from "../../chunks/clsx.js";
2
+ import { useInternalStatus as t } from "../hooks/useInternalStatus.js";
3
+ import { calculateCursorPosition as n, extractDigits as r, formatPhoneDigits as i, formatWithPattern as a, getLiteralPrefixInfo as o, stripLiteralPrefix as s } from "../helpers/phone/formatters.js";
4
+ import { DEFAULT_PHONE_PREFIXES as c } from "../helpers/phone/defaultPrefixes.js";
5
+ import { useCallback as l, useEffect as u, useId as d, useRef as f, useState as p } from "react";
6
+ import { Icon as m } from "@clubmed/trident-icons";
7
+ import { jsx as h, jsxs as g } from "react/jsx-runtime";
8
+ //#region lib/ui/forms/PhoneFieldSplitInput.tsx
9
+ var _ = (e) => {
10
+ let { externalValue: t, name: c, pattern: d, defaultPrefix: m, onChange: h } = e, g = f(null), _ = f(null), [v, y] = p(m), [b, x] = p(""), S = l((e, t) => {
11
+ if (e === v && t === b) return;
12
+ let n = `${e} ${t}`.trim();
13
+ h?.(c, {
14
+ full: n,
15
+ prefix: e,
16
+ number: t,
17
+ raw: r(n)
18
+ });
19
+ }, [
20
+ c,
21
+ b,
22
+ h,
23
+ v
24
+ ]);
25
+ return u(() => {
26
+ _.current !== null && g.current && (g.current.setSelectionRange(_.current, _.current), _.current = null);
27
+ }, [b]), u(() => {
28
+ t && typeof t == "object" && "full" in t && (y(t.prefix || m), x(t.number || ""));
29
+ }, [t, m]), {
30
+ numberInputRef: g,
31
+ prefix: v,
32
+ number: b,
33
+ onKeyDown: l((e) => {
34
+ if (e.key !== "Backspace" && e.key !== "Delete") return;
35
+ let t = e.currentTarget.selectionStart || 0;
36
+ if (t !== (e.currentTarget.selectionEnd || 0)) return;
37
+ let { literalPrefixDigits: i } = o(d), c = e.key === "Backspace" ? b[t - 1] : b[t];
38
+ if (!c || /\d/.test(c)) return;
39
+ e.preventDefault();
40
+ let l = s(r(b), i), u = s(r(b.substring(0, t)), i).length, f, p;
41
+ if (e.key === "Backspace") {
42
+ if (u === 0) return;
43
+ f = l.slice(0, u - 1) + l.slice(u), p = u - 1;
44
+ } else {
45
+ if (u >= l.length) return;
46
+ f = l.slice(0, u) + l.slice(u + 1), p = u;
47
+ }
48
+ let m = a(f, d);
49
+ _.current = n(p, m, i), x(m), S(v, m);
50
+ }, [
51
+ S,
52
+ d,
53
+ b,
54
+ v
55
+ ]),
56
+ onInputChange: l((e) => {
57
+ let t = i(e.target.value, d, e.target.selectionStart || 0);
58
+ t && (_.current = t.nextCursor, x(t.formatted), S(v, t.formatted));
59
+ }, [
60
+ S,
61
+ d,
62
+ v
63
+ ]),
64
+ onPrefixChange: l((e) => {
65
+ y(e), S(e, b);
66
+ }, [S, b])
67
+ };
68
+ }, v = ({ id: n, name: r, value: i, pattern: a = "## ## ## ## ##", prefixes: o = c, defaultPrefix: s = o[0]?.code || "+1", placeholder: l = "", iconType: u, disabled: f = !1, required: p = !1, validationStatus: v = "default", onChange: y, ...b }) => {
69
+ let x = d(), S = n ?? x, C = r ?? S, w = t({
70
+ isDisabled: f,
71
+ validationStatus: v
72
+ }), { numberInputRef: T, prefix: E, number: D, onKeyDown: O, onInputChange: k, onPrefixChange: A } = _({
73
+ externalValue: i,
74
+ name: C,
75
+ pattern: a,
76
+ defaultPrefix: s,
77
+ onChange: y
78
+ });
79
+ return /* @__PURE__ */ g("div", {
80
+ className: "flex gap-8",
81
+ children: [/* @__PURE__ */ g("div", {
82
+ className: e("relative rounded-pill z-0 w-[130px] flex-shrink-0", {
83
+ "bg-white": w !== "disabled",
84
+ "bg-pearl border-middleGrey": w === "disabled"
85
+ }),
86
+ children: [/* @__PURE__ */ h("select", {
87
+ disabled: f,
88
+ value: E,
89
+ onChange: (e) => A(e.target.value),
90
+ className: e("text-b3 rounded-pill w-full border overflow-hidden px-20 py-12 font-semibold outline-none appearance-none bg-transparent", {
91
+ "border-middleGrey focus:border-black active:border-black": w === "default",
92
+ "text-black": w !== "disabled",
93
+ "bg-pearl border-middleGrey": w === "disabled",
94
+ "border-red": w === "error",
95
+ "border-green": w === "success",
96
+ "pe-[40px]": !0
97
+ }),
98
+ id: `${S}-prefix`,
99
+ "aria-label": `${C}-prefix`,
100
+ children: o.map((e) => /* @__PURE__ */ h("option", {
101
+ value: e.code,
102
+ children: e.label ?? e.code
103
+ }, e.code))
104
+ }), /* @__PURE__ */ h("div", {
105
+ className: "pointer-events-none absolute inset-0 flex items-center justify-end px-20 py-12 -z-1",
106
+ children: /* @__PURE__ */ h(m, {
107
+ name: "ArrowDefaultDown",
108
+ type: "svg",
109
+ width: "24px",
110
+ color: "black"
111
+ })
112
+ })]
113
+ }), /* @__PURE__ */ g("div", {
114
+ className: "relative flex-1",
115
+ children: [/* @__PURE__ */ h("input", {
116
+ ...b,
117
+ ref: T,
118
+ type: "tel",
119
+ disabled: f,
120
+ required: p,
121
+ value: D,
122
+ onChange: k,
123
+ onKeyDown: O,
124
+ placeholder: l,
125
+ className: e("text-b3 rounded-pill w-full border overflow-hidden px-20 py-12 font-normal outline-none", {
126
+ "border-middleGrey focus:border-black active:border-black": w === "default",
127
+ "pe-[52px]": w === "error" || w === "success",
128
+ "bg-white text-black": w !== "disabled",
129
+ "bg-pearl border-middleGrey": w === "disabled",
130
+ "border-red": w === "error",
131
+ "border-green": w === "success"
132
+ }),
133
+ "aria-label": `${C}-number`
134
+ }), /* @__PURE__ */ h("div", {
135
+ className: e("pointer-events-none absolute inset-0 flex items-center justify-end px-20 py-12", {
136
+ "text-grey": w === "disabled",
137
+ "text-red": w === "error",
138
+ "text-green": w === "success"
139
+ }),
140
+ children: /* @__PURE__ */ g("span", {
141
+ className: "flex gap-x-8",
142
+ children: [w === "error" && /* @__PURE__ */ h(m, {
143
+ name: "CrossDefault",
144
+ width: "24px",
145
+ type: u
146
+ }), w === "success" && /* @__PURE__ */ h(m, {
147
+ name: "CheckDefault",
148
+ width: "24px",
149
+ type: u
150
+ })]
151
+ })
152
+ })]
153
+ })]
154
+ });
155
+ };
156
+ //#endregion
157
+ export { v as PhoneFieldSplitInput };
158
+
159
+ //# sourceMappingURL=PhoneFieldSplitInput.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PhoneFieldSplitInput.js","names":[],"sources":["../../../lib/ui/forms/PhoneFieldSplitInput.tsx"],"sourcesContent":["import clsx from 'clsx';\nimport { Icon, type IconicTypes } from '@clubmed/trident-icons';\nimport {\n type ChangeEvent,\n type InputHTMLAttributes,\n type KeyboardEvent,\n useCallback,\n useEffect,\n useId,\n useRef,\n useState,\n} from 'react';\nimport {\n calculateCursorPosition,\n DEFAULT_PHONE_PREFIXES,\n extractDigits,\n formatPhoneDigits,\n formatWithPattern,\n getLiteralPrefixInfo,\n type PhonePrefix,\n stripLiteralPrefix,\n} from '../helpers/phone';\nimport { useInternalStatus } from '../hooks/useInternalStatus';\nimport type { FormControlProps } from './FormControl';\nimport type { PhoneValue } from './PhoneField';\n\ntype PhoneFieldChangeHandler = (name: string, value: PhoneValue) => void;\n\nexport interface PhoneFieldSplitProps {\n prefixes?: PhonePrefix[];\n defaultPrefix?: string;\n}\n\nconst usePhoneField = (args: {\n externalValue?: PhoneValue;\n name: string;\n pattern: string;\n defaultPrefix: string;\n onChange?: PhoneFieldChangeHandler;\n}) => {\n const { externalValue, name, pattern, defaultPrefix, onChange } = args;\n const numberInputRef = useRef<HTMLInputElement>(null);\n const cursorPositionRef = useRef<number | null>(null);\n const [prefix, setPrefix] = useState(defaultPrefix);\n const [number, setNumber] = useState('');\n\n const emitChange = useCallback(\n (nextPrefix: string, nextNumber: string) => {\n if (nextPrefix === prefix && nextNumber === number) {\n return;\n }\n\n const full = `${nextPrefix} ${nextNumber}`.trim();\n\n onChange?.(name, {\n full,\n prefix: nextPrefix,\n number: nextNumber,\n raw: extractDigits(full),\n });\n },\n [name, number, onChange, prefix],\n );\n\n useEffect(() => {\n if (cursorPositionRef.current !== null && numberInputRef.current) {\n numberInputRef.current.setSelectionRange(\n cursorPositionRef.current,\n cursorPositionRef.current,\n );\n cursorPositionRef.current = null;\n }\n }, [number]);\n\n useEffect(() => {\n if (externalValue && typeof externalValue === 'object' && 'full' in externalValue) {\n setPrefix(externalValue.prefix || defaultPrefix);\n setNumber(externalValue.number || '');\n }\n }, [externalValue, defaultPrefix]);\n\n const onKeyDown = useCallback(\n (e: KeyboardEvent<HTMLInputElement>) => {\n if (e.key !== 'Backspace' && e.key !== 'Delete') {\n return;\n }\n\n const cursorPosition = e.currentTarget.selectionStart || 0;\n const selectionEnd = e.currentTarget.selectionEnd || 0;\n\n if (cursorPosition !== selectionEnd) {\n return;\n }\n\n const { literalPrefixDigits } = getLiteralPrefixInfo(pattern);\n\n const targetChar =\n e.key === 'Backspace' ? number[cursorPosition - 1] : number[cursorPosition];\n\n if (!targetChar || /\\d/.test(targetChar)) {\n return;\n }\n\n e.preventDefault();\n\n const allDigits = stripLiteralPrefix(extractDigits(number), literalPrefixDigits);\n const digitsBeforeCursor = stripLiteralPrefix(\n extractDigits(number.substring(0, cursorPosition)),\n literalPrefixDigits,\n ).length;\n\n let newDigits: string;\n let targetCursorDigits: number;\n\n if (e.key === 'Backspace') {\n if (digitsBeforeCursor === 0) {\n return;\n }\n\n newDigits =\n allDigits.slice(0, digitsBeforeCursor - 1) + allDigits.slice(digitsBeforeCursor);\n targetCursorDigits = digitsBeforeCursor - 1;\n } else {\n if (digitsBeforeCursor >= allDigits.length) {\n return;\n }\n\n newDigits =\n allDigits.slice(0, digitsBeforeCursor) + allDigits.slice(digitsBeforeCursor + 1);\n targetCursorDigits = digitsBeforeCursor;\n }\n\n const formatted = formatWithPattern(newDigits, pattern);\n cursorPositionRef.current = calculateCursorPosition(\n targetCursorDigits,\n formatted,\n literalPrefixDigits,\n );\n\n setNumber(formatted);\n\n emitChange(prefix, formatted);\n },\n [emitChange, pattern, number, prefix],\n );\n\n const onInputChange = useCallback(\n (e: ChangeEvent<HTMLInputElement>) => {\n const formattedValue = formatPhoneDigits(\n e.target.value,\n pattern,\n e.target.selectionStart || 0,\n );\n if (!formattedValue) return;\n\n cursorPositionRef.current = formattedValue.nextCursor;\n setNumber(formattedValue.formatted);\n\n emitChange(prefix, formattedValue.formatted);\n },\n [emitChange, pattern, prefix],\n );\n\n const onPrefixChange = useCallback(\n (newPrefix: string) => {\n setPrefix(newPrefix);\n\n emitChange(newPrefix, number);\n },\n [emitChange, number],\n );\n\n return { numberInputRef, prefix, number, onKeyDown, onInputChange, onPrefixChange };\n};\n\nexport interface PhoneFieldSplitInputProps\n extends Omit<InputHTMLAttributes<HTMLInputElement>, 'value' | 'onChange' | 'pattern'> {\n value?: PhoneValue;\n pattern?: string;\n prefixes?: PhoneFieldSplitProps['prefixes'];\n defaultPrefix?: PhoneFieldSplitProps['defaultPrefix'];\n iconType?: IconicTypes;\n validationStatus?: FormControlProps<PhoneValue>['validationStatus'];\n onChange?: (name: string, value: PhoneValue) => void;\n}\n\nexport const PhoneFieldSplitInput = ({\n id,\n name,\n value: externalValue,\n pattern = '## ## ## ## ##',\n prefixes = DEFAULT_PHONE_PREFIXES,\n defaultPrefix = prefixes[0]?.code || '+1',\n placeholder = '',\n iconType,\n disabled = false,\n required = false,\n validationStatus = 'default',\n onChange,\n ...rest\n}: PhoneFieldSplitInputProps) => {\n const internalId = useId();\n const resolvedId = id ?? internalId;\n const resolvedName = name ?? resolvedId;\n\n const internalStatus = useInternalStatus({ isDisabled: disabled, validationStatus });\n const { numberInputRef, prefix, number, onKeyDown, onInputChange, onPrefixChange } =\n usePhoneField({\n externalValue,\n name: resolvedName,\n pattern,\n defaultPrefix,\n onChange,\n });\n\n return (\n <div className=\"flex gap-8\">\n <div\n className={clsx('relative rounded-pill z-0 w-[130px] flex-shrink-0', {\n 'bg-white': internalStatus !== 'disabled',\n 'bg-pearl border-middleGrey': internalStatus === 'disabled',\n })}\n >\n <select\n disabled={disabled}\n value={prefix}\n onChange={(e) => onPrefixChange(e.target.value)}\n className={clsx(\n 'text-b3 rounded-pill w-full border overflow-hidden px-20 py-12 font-semibold outline-none appearance-none bg-transparent',\n {\n 'border-middleGrey focus:border-black active:border-black':\n internalStatus === 'default',\n 'text-black': internalStatus !== 'disabled',\n 'bg-pearl border-middleGrey': internalStatus === 'disabled',\n 'border-red': internalStatus === 'error',\n 'border-green': internalStatus === 'success',\n 'pe-[40px]': true,\n },\n )}\n id={`${resolvedId}-prefix`}\n aria-label={`${resolvedName}-prefix`}\n >\n {(prefixes as PhonePrefix[]).map((p) => (\n <option key={p.code} value={p.code}>\n {p.label ?? p.code}\n </option>\n ))}\n </select>\n\n <div className=\"pointer-events-none absolute inset-0 flex items-center justify-end px-20 py-12 -z-1\">\n <Icon name=\"ArrowDefaultDown\" type=\"svg\" width=\"24px\" color=\"black\" />\n </div>\n </div>\n\n <div className=\"relative flex-1\">\n <input\n {...(rest as Record<string, unknown>)}\n ref={numberInputRef}\n type=\"tel\"\n disabled={disabled}\n required={required}\n value={number}\n onChange={onInputChange}\n onKeyDown={onKeyDown}\n placeholder={placeholder}\n className={clsx(\n 'text-b3 rounded-pill w-full border overflow-hidden px-20 py-12 font-normal outline-none',\n {\n 'border-middleGrey focus:border-black active:border-black':\n internalStatus === 'default',\n 'pe-[52px]': internalStatus === 'error' || internalStatus === 'success',\n 'bg-white text-black': internalStatus !== 'disabled',\n 'bg-pearl border-middleGrey': internalStatus === 'disabled',\n 'border-red': internalStatus === 'error',\n 'border-green': internalStatus === 'success',\n },\n )}\n aria-label={`${resolvedName}-number`}\n />\n\n <div\n className={clsx(\n 'pointer-events-none absolute inset-0 flex items-center justify-end px-20 py-12',\n {\n 'text-grey': internalStatus === 'disabled',\n 'text-red': internalStatus === 'error',\n 'text-green': internalStatus === 'success',\n },\n )}\n >\n <span className=\"flex gap-x-8\">\n {internalStatus === 'error' && (\n <Icon name=\"CrossDefault\" width=\"24px\" type={iconType} />\n )}\n {internalStatus === 'success' && (\n <Icon name=\"CheckDefault\" width=\"24px\" type={iconType} />\n )}\n </span>\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":";;;;;;;;AAiCA,IAAM,KAAiB,MAMjB;CACJ,IAAM,EAAE,kBAAe,SAAM,YAAS,kBAAe,gBAAa,GAC5D,IAAiB,EAAyB,KAAK,EAC/C,IAAoB,EAAsB,KAAK,EAC/C,CAAC,GAAQ,KAAa,EAAS,EAAc,EAC7C,CAAC,GAAQ,KAAa,EAAS,GAAG,EAElC,IAAa,GAChB,GAAoB,MAAuB;AAC1C,MAAI,MAAe,KAAU,MAAe,EAC1C;EAGF,IAAM,IAAO,GAAG,EAAW,GAAG,IAAa,MAAM;AAEjD,MAAW,GAAM;GACf;GACA,QAAQ;GACR,QAAQ;GACR,KAAK,EAAc,EAAK;GACzB,CAAC;IAEJ;EAAC;EAAM;EAAQ;EAAU;EAAO,CACjC;AA8GD,QA5GA,QAAgB;AACd,EAAI,EAAkB,YAAY,QAAQ,EAAe,YACvD,EAAe,QAAQ,kBACrB,EAAkB,SAClB,EAAkB,QACnB,EACD,EAAkB,UAAU;IAE7B,CAAC,EAAO,CAAC,EAEZ,QAAgB;AACd,EAAI,KAAiB,OAAO,KAAkB,YAAY,UAAU,MAClE,EAAU,EAAc,UAAU,EAAc,EAChD,EAAU,EAAc,UAAU,GAAG;IAEtC,CAAC,GAAe,EAAc,CAAC,EA6F3B;EAAE;EAAgB;EAAQ;EAAQ,WA3FvB,GACf,MAAuC;AACtC,OAAI,EAAE,QAAQ,eAAe,EAAE,QAAQ,SACrC;GAGF,IAAM,IAAiB,EAAE,cAAc,kBAAkB;AAGzD,OAAI,OAFiB,EAAE,cAAc,gBAAgB,GAGnD;GAGF,IAAM,EAAE,2BAAwB,EAAqB,EAAQ,EAEvD,IACJ,EAAE,QAAQ,cAAc,EAAO,IAAiB,KAAK,EAAO;AAE9D,OAAI,CAAC,KAAc,KAAK,KAAK,EAAW,CACtC;AAGF,KAAE,gBAAgB;GAElB,IAAM,IAAY,EAAmB,EAAc,EAAO,EAAE,EAAoB,EAC1E,IAAqB,EACzB,EAAc,EAAO,UAAU,GAAG,EAAe,CAAC,EAClD,EACD,CAAC,QAEE,GACA;AAEJ,OAAI,EAAE,QAAQ,aAAa;AACzB,QAAI,MAAuB,EACzB;AAKF,IAFA,IACE,EAAU,MAAM,GAAG,IAAqB,EAAE,GAAG,EAAU,MAAM,EAAmB,EAClF,IAAqB,IAAqB;UACrC;AACL,QAAI,KAAsB,EAAU,OAClC;AAKF,IAFA,IACE,EAAU,MAAM,GAAG,EAAmB,GAAG,EAAU,MAAM,IAAqB,EAAE,EAClF,IAAqB;;GAGvB,IAAM,IAAY,EAAkB,GAAW,EAAQ;AASvD,GARA,EAAkB,UAAU,EAC1B,GACA,GACA,EACD,EAED,EAAU,EAAU,EAEpB,EAAW,GAAQ,EAAU;KAE/B;GAAC;GAAY;GAAS;GAAQ;GAAO,CACtC;EA4BmD,eA1B9B,GACnB,MAAqC;GACpC,IAAM,IAAiB,EACrB,EAAE,OAAO,OACT,GACA,EAAE,OAAO,kBAAkB,EAC5B;AACI,SAEL,EAAkB,UAAU,EAAe,YAC3C,EAAU,EAAe,UAAU,EAEnC,EAAW,GAAQ,EAAe,UAAU;KAE9C;GAAC;GAAY;GAAS;GAAO,CAC9B;EAWkE,gBAT5C,GACpB,MAAsB;AAGrB,GAFA,EAAU,EAAU,EAEpB,EAAW,GAAW,EAAO;KAE/B,CAAC,GAAY,EAAO,CACrB;EAEkF;GAcxE,KAAwB,EACnC,OACA,SACA,OAAO,GACP,aAAU,kBACV,cAAW,GACX,mBAAgB,EAAS,IAAI,QAAQ,MACrC,iBAAc,IACd,aACA,cAAW,IACX,cAAW,IACX,sBAAmB,WACnB,aACA,GAAG,QAC4B;CAC/B,IAAM,IAAa,GAAO,EACpB,IAAa,KAAM,GACnB,IAAe,KAAQ,GAEvB,IAAiB,EAAkB;EAAE,YAAY;EAAU;EAAkB,CAAC,EAC9E,EAAE,mBAAgB,WAAQ,WAAQ,cAAW,kBAAe,sBAChE,EAAc;EACZ;EACA,MAAM;EACN;EACA;EACA;EACD,CAAC;AAEJ,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,OAAD;GACE,WAAW,EAAK,qDAAqD;IACnE,YAAY,MAAmB;IAC/B,8BAA8B,MAAmB;IAClD,CAAC;aAJJ,CAME,kBAAC,UAAD;IACY;IACV,OAAO;IACP,WAAW,MAAM,EAAe,EAAE,OAAO,MAAM;IAC/C,WAAW,EACT,4HACA;KACE,4DACE,MAAmB;KACrB,cAAc,MAAmB;KACjC,8BAA8B,MAAmB;KACjD,cAAc,MAAmB;KACjC,gBAAgB,MAAmB;KACnC,aAAa;KACd,CACF;IACD,IAAI,GAAG,EAAW;IAClB,cAAY,GAAG,EAAa;cAE1B,EAA2B,KAAK,MAChC,kBAAC,UAAD;KAAqB,OAAO,EAAE;eAC3B,EAAE,SAAS,EAAE;KACP,EAFI,EAAE,KAEN,CACT;IACK,CAAA,EAET,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,GAAD;KAAM,MAAK;KAAmB,MAAK;KAAM,OAAM;KAAO,OAAM;KAAU,CAAA;IAClE,CAAA,CACF;MAEN,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,SAAD;IACE,GAAK;IACL,KAAK;IACL,MAAK;IACK;IACA;IACV,OAAO;IACP,UAAU;IACC;IACE;IACb,WAAW,EACT,2FACA;KACE,4DACE,MAAmB;KACrB,aAAa,MAAmB,WAAW,MAAmB;KAC9D,uBAAuB,MAAmB;KAC1C,8BAA8B,MAAmB;KACjD,cAAc,MAAmB;KACjC,gBAAgB,MAAmB;KACpC,CACF;IACD,cAAY,GAAG,EAAa;IAC5B,CAAA,EAEF,kBAAC,OAAD;IACE,WAAW,EACT,kFACA;KACE,aAAa,MAAmB;KAChC,YAAY,MAAmB;KAC/B,cAAc,MAAmB;KAClC,CACF;cAED,kBAAC,QAAD;KAAM,WAAU;eAAhB,CACG,MAAmB,WAClB,kBAAC,GAAD;MAAM,MAAK;MAAe,OAAM;MAAO,MAAM;MAAY,CAAA,EAE1D,MAAmB,aAClB,kBAAC,GAAD;MAAM,MAAK;MAAe,OAAM;MAAO,MAAM;MAAY,CAAA,CAEtD;;IACH,CAAA,CACF;KACF"}
@@ -1,7 +1,7 @@
1
- import { ComponentPropsWithoutRef, FunctionComponent } from 'react';
1
+ import { ComponentPropsWithoutRef } from 'react';
2
2
  import { Colors } from '../types/Colors';
3
- export interface SwitchProps extends ComponentPropsWithoutRef<'button'> {
3
+ export interface SwitchProps extends Omit<ComponentPropsWithoutRef<'input'>, 'size' | 'type' | 'defaultChecked'> {
4
4
  checked: boolean;
5
5
  color?: Colors;
6
6
  }
7
- export declare const Switch: FunctionComponent<SwitchProps>;
7
+ export declare function Switch({ id, name, checked, className, color, onChange, disabled, tabIndex, children, role, ...attrs }: SwitchProps): import("react/jsx-runtime").JSX.Element;
@@ -1,42 +1,59 @@
1
1
  "use client";
2
2
  import { t as e } from "../../chunks/clsx.js";
3
3
  import { getBgColor as t } from "../helpers/colors/colors.js";
4
- import { jsx as n, jsxs as r } from "react/jsx-runtime";
4
+ import { useId as n } from "react";
5
+ import { jsx as r, jsxs as i } from "react/jsx-runtime";
5
6
  //#region lib/ui/forms/Switch.tsx
6
- var i = ({ checked: i, className: a, color: o = "saffron", onClick: s, ...c }) => {
7
- let l = (e) => {
8
- c.disabled || s?.(e);
9
- };
10
- return /* @__PURE__ */ n("button", {
11
- ...c,
12
- "aria-checked": i,
13
- className: e(a, "rounded-pill inline-flex min-w-56 items-center p-4 align-middle transition-colors", { "bg-middleGrey": !i }, { [t(o)]: i }),
14
- "data-name": "Switch",
15
- onClick: l,
16
- role: "switch",
17
- type: "button",
18
- children: /* @__PURE__ */ r("svg", {
19
- className: "size-24",
20
- viewBox: "0 0 24 24",
21
- children: [
22
- /* @__PURE__ */ n("circle", {
23
- className: "fill-white",
24
- cx: 12,
25
- cy: 12,
26
- r: 12
27
- }),
28
- /* @__PURE__ */ n("circle", {
29
- className: "fill-middleGrey",
30
- cx: 12,
31
- cy: 12,
32
- r: 4
33
- }),
34
- /* @__PURE__ */ n("path", { d: "M18.73 6c.35 0 .68.14.9.37.5.5.5 1.35 0 1.85l-8.54 8.75q-.37.38-.9.38t-.9-.38l-4.91-5.02a1.33 1.33 0 0 1 0-1.85 1.26 1.26 0 0 1 1.8 0l4.01 4.09 7.64-7.82c.23-.24.56-.37.9-.37" })
35
- ]
36
- })
7
+ function a({ id: a, name: o, checked: s, className: c, color: l = "saffron", onChange: u, disabled: d = !1, tabIndex: f = 0, children: p, role: m = "switch", ...h }) {
8
+ let g = n(), _ = a ?? g, v = o ?? _, y = p != null, b = e("inline-flex items-center relative", {
9
+ "gap-4": y,
10
+ "cursor-pointer": !d,
11
+ "cursor-not-allowed": d
37
12
  });
38
- };
13
+ return /* @__PURE__ */ i(y ? "label" : "span", {
14
+ htmlFor: _,
15
+ className: b,
16
+ children: [
17
+ /* @__PURE__ */ r("input", {
18
+ ...h,
19
+ id: _,
20
+ name: v,
21
+ checked: s,
22
+ "data-name": "Switch",
23
+ disabled: d,
24
+ role: m,
25
+ tabIndex: f,
26
+ type: "checkbox",
27
+ className: "absolute opacity-0 left-0 w-full top-0 m-0 p-0 z-1 h-full",
28
+ onChange: u
29
+ }),
30
+ /* @__PURE__ */ r("span", {
31
+ className: e(c, "rounded-pill inline-flex min-w-56 items-center p-4 align-middle transition-colors", { "bg-middleGrey": !s }, { [t(l)]: s }),
32
+ children: /* @__PURE__ */ r("svg", {
33
+ className: "size-24",
34
+ viewBox: "0 0 24 24",
35
+ children: /* @__PURE__ */ i("g", { children: [
36
+ /* @__PURE__ */ r("circle", {
37
+ cx: 12,
38
+ cy: 12,
39
+ r: 12,
40
+ fill: "var(--color-white)"
41
+ }),
42
+ /* @__PURE__ */ r("circle", {
43
+ cx: 12,
44
+ cy: 12,
45
+ r: 4,
46
+ fill: "var(--color-middleGrey)"
47
+ }),
48
+ /* @__PURE__ */ r("path", { d: "M18.73 6c.35 0 .68.14.9.37.5.5.5 1.35 0 1.85l-8.54 8.75q-.37.38-.9.38t-.9-.38l-4.91-5.02a1.33 1.33 0 0 1 0-1.85 1.26 1.26 0 0 1 1.8 0l4.01 4.09 7.64-7.82c.23-.24.56-.37.9-.37" })
49
+ ] })
50
+ })
51
+ }),
52
+ p
53
+ ]
54
+ });
55
+ }
39
56
  //#endregion
40
- export { i as Switch };
57
+ export { a as Switch };
41
58
 
42
59
  //# sourceMappingURL=Switch.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Switch.js","names":[],"sources":["../../../lib/ui/forms/Switch.tsx"],"sourcesContent":["'use client';\n\nimport clsx from 'clsx';\nimport type { ComponentPropsWithoutRef, FunctionComponent, MouseEvent } from 'react';\n\nimport { getBgColor } from '../helpers/colors/colors';\nimport type { Colors } from '../types/Colors';\n\nexport interface SwitchProps extends ComponentPropsWithoutRef<'button'> {\n checked: boolean;\n color?: Colors;\n}\n\nexport const Switch: FunctionComponent<SwitchProps> = ({\n checked,\n className,\n color = 'saffron',\n onClick,\n ...attrs\n}) => {\n const handleClick = (e: MouseEvent<HTMLButtonElement>) => {\n if (!attrs.disabled) {\n onClick?.(e);\n }\n };\n\n return (\n <button\n {...attrs}\n aria-checked={checked}\n className={clsx(\n className,\n 'rounded-pill inline-flex min-w-56 items-center p-4 align-middle transition-colors',\n { 'bg-middleGrey': !checked },\n { [getBgColor(color)]: checked },\n )}\n data-name=\"Switch\"\n onClick={handleClick}\n role=\"switch\"\n type=\"button\"\n >\n <svg className=\"size-24\" viewBox=\"0 0 24 24\">\n <circle className=\"fill-white\" cx={12} cy={12} r={12} />\n <circle className=\"fill-middleGrey\" cx={12} cy={12} r={4} />\n <path d=\"M18.73 6c.35 0 .68.14.9.37.5.5.5 1.35 0 1.85l-8.54 8.75q-.37.38-.9.38t-.9-.38l-4.91-5.02a1.33 1.33 0 0 1 0-1.85 1.26 1.26 0 0 1 1.8 0l4.01 4.09 7.64-7.82c.23-.24.56-.37.9-.37\" />\n </svg>\n </button>\n );\n};\n"],"mappings":";;;;;AAaA,IAAa,KAA0C,EACrD,YACA,cACA,WAAQ,WACR,YACA,GAAG,QACC;CACJ,IAAM,KAAe,MAAqC;AACxD,EAAK,EAAM,YACT,IAAU,EAAE;;AAIhB,QACE,kBAAC,UAAD;EACE,GAAI;EACJ,gBAAc;EACd,WAAW,EACT,GACA,qFACA,EAAE,iBAAiB,CAAC,GAAS,EAC7B,GAAG,EAAW,EAAM,GAAG,GAAS,CACjC;EACD,aAAU;EACV,SAAS;EACT,MAAK;EACL,MAAK;YAEL,kBAAC,OAAD;GAAK,WAAU;GAAU,SAAQ;aAAjC;IACE,kBAAC,UAAD;KAAQ,WAAU;KAAa,IAAI;KAAI,IAAI;KAAI,GAAG;KAAM,CAAA;IACxD,kBAAC,UAAD;KAAQ,WAAU;KAAkB,IAAI;KAAI,IAAI;KAAI,GAAG;KAAK,CAAA;IAC5D,kBAAC,QAAD,EAAM,GAAE,kLAAmL,CAAA;;;EAEtL,CAAA"}
1
+ {"version":3,"file":"Switch.js","names":[],"sources":["../../../lib/ui/forms/Switch.tsx"],"sourcesContent":["'use client';\n\nimport clsx from 'clsx';\nimport { type ComponentPropsWithoutRef, useId } from 'react';\n\nimport { getBgColor } from '../helpers/colors/colors';\nimport type { Colors } from '../types/Colors';\n\nexport interface SwitchProps\n extends Omit<ComponentPropsWithoutRef<'input'>, 'size' | 'type' | 'defaultChecked'> {\n checked: boolean;\n color?: Colors;\n}\n\nexport function Switch({\n id,\n name,\n checked,\n className,\n color = 'saffron',\n onChange,\n disabled = false,\n tabIndex = 0,\n children,\n role = 'switch',\n ...attrs\n}: SwitchProps) {\n const internalId = useId();\n const inputId = id ?? internalId;\n const inputName = name ?? inputId;\n const hasChildren = children !== undefined && children !== null;\n const rootClassName = clsx('inline-flex items-center relative', {\n 'gap-4': hasChildren,\n 'cursor-pointer': !disabled,\n 'cursor-not-allowed': disabled,\n });\n const Node = hasChildren ? 'label' : 'span';\n\n return (\n <Node htmlFor={inputId} className={rootClassName}>\n <input\n {...attrs}\n id={inputId}\n name={inputName}\n checked={checked}\n data-name=\"Switch\"\n disabled={disabled}\n role={role}\n tabIndex={tabIndex}\n type=\"checkbox\"\n className=\"absolute opacity-0 left-0 w-full top-0 m-0 p-0 z-1 h-full\"\n onChange={onChange}\n />\n\n <span\n className={clsx(\n className,\n 'rounded-pill inline-flex min-w-56 items-center p-4 align-middle transition-colors',\n { 'bg-middleGrey': !checked },\n { [getBgColor(color)]: checked },\n )}\n >\n <svg className=\"size-24\" viewBox=\"0 0 24 24\">\n <g>\n <circle cx={12} cy={12} r={12} fill=\"var(--color-white)\" />\n <circle cx={12} cy={12} r={4} fill=\"var(--color-middleGrey)\" />\n <path d=\"M18.73 6c.35 0 .68.14.9.37.5.5.5 1.35 0 1.85l-8.54 8.75q-.37.38-.9.38t-.9-.38l-4.91-5.02a1.33 1.33 0 0 1 0-1.85 1.26 1.26 0 0 1 1.8 0l4.01 4.09 7.64-7.82c.23-.24.56-.37.9-.37\" />\n </g>\n </svg>\n </span>\n {children}\n </Node>\n );\n}\n"],"mappings":";;;;;;AAcA,SAAgB,EAAO,EACrB,OACA,SACA,YACA,cACA,WAAQ,WACR,aACA,cAAW,IACX,cAAW,GACX,aACA,UAAO,UACP,GAAG,KACW;CACd,IAAM,IAAa,GAAO,EACpB,IAAU,KAAM,GAChB,IAAY,KAAQ,GACpB,IAAc,KAAuC,MACrD,IAAgB,EAAK,qCAAqC;EAC9D,SAAS;EACT,kBAAkB,CAAC;EACnB,sBAAsB;EACvB,CAAC;AAGF,QACE,kBAHW,IAAc,UAAU,QAGnC;EAAM,SAAS;EAAS,WAAW;YAAnC;GACE,kBAAC,SAAD;IACE,GAAI;IACJ,IAAI;IACJ,MAAM;IACG;IACT,aAAU;IACA;IACJ;IACI;IACV,MAAK;IACL,WAAU;IACA;IACV,CAAA;GAEF,kBAAC,QAAD;IACE,WAAW,EACT,GACA,qFACA,EAAE,iBAAiB,CAAC,GAAS,EAC7B,GAAG,EAAW,EAAM,GAAG,GAAS,CACjC;cAED,kBAAC,OAAD;KAAK,WAAU;KAAU,SAAQ;eAC/B,kBAAC,KAAD,EAAA,UAAA;MACE,kBAAC,UAAD;OAAQ,IAAI;OAAI,IAAI;OAAI,GAAG;OAAI,MAAK;OAAuB,CAAA;MAC3D,kBAAC,UAAD;OAAQ,IAAI;OAAI,IAAI;OAAI,GAAG;OAAG,MAAK;OAA4B,CAAA;MAC/D,kBAAC,QAAD,EAAM,GAAE,kLAAmL,CAAA;MACzL,EAAA,CAAA;KACA,CAAA;IACD,CAAA;GACN"}
@@ -44,3 +44,14 @@ export declare function getCursorPosition(oldValue: string, newValue: string, ol
44
44
  * @returns Number of '#' characters in pattern
45
45
  */
46
46
  export declare function countDigitPlaceholders(pattern: string): number;
47
+ export declare function getLiteralPrefixInfo(pattern: string): {
48
+ literalPrefix: string;
49
+ literalPrefixDigits: string;
50
+ };
51
+ export declare function stripLiteralPrefix(allDigits: string, literalPrefixDigits: string): string;
52
+ export declare function calculateCursorPosition(digitsBeforeCursor: number, formatted: string, literalPrefixDigits: string): number;
53
+ export declare function formatPhoneDigits(inputValue: string, pattern: string, cursorPosition: number): {
54
+ formatted: string;
55
+ nextCursor: number;
56
+ literalPrefixDigits: string;
57
+ } | null;
@@ -27,7 +27,38 @@ function n(t, n, r) {
27
27
  function r(e) {
28
28
  return (e.match(/#/g) || []).length;
29
29
  }
30
+ function i(t) {
31
+ let n = t.indexOf("#"), r = n > 0 ? t.substring(0, n) : "";
32
+ return {
33
+ literalPrefix: r,
34
+ literalPrefixDigits: e(r)
35
+ };
36
+ }
37
+ function a(e, t) {
38
+ return t.length > 0 && e.startsWith(t) ? e.substring(t.length) : e;
39
+ }
40
+ function o(e, t, n) {
41
+ let r = 0, i = 0;
42
+ for (let a = 0; a < t.length; a++) if (/\d/.test(t[a])) {
43
+ if (i < n.length) {
44
+ i++;
45
+ continue;
46
+ }
47
+ if (r++, r === e) return a + 1;
48
+ }
49
+ return t.length;
50
+ }
51
+ function s(n, s, c) {
52
+ let { literalPrefixDigits: l } = i(s), u = a(e(n), l), d = r(s);
53
+ if (u.length > d) return null;
54
+ let f = a(e(n.substring(0, c)), l).length, p = t(u, s);
55
+ return {
56
+ formatted: p,
57
+ nextCursor: o(f, p, l),
58
+ literalPrefixDigits: l
59
+ };
60
+ }
30
61
  //#endregion
31
- export { r as countDigitPlaceholders, e as extractDigits, t as formatWithPattern, n as getCursorPosition };
62
+ export { o as calculateCursorPosition, r as countDigitPlaceholders, e as extractDigits, s as formatPhoneDigits, t as formatWithPattern, n as getCursorPosition, i as getLiteralPrefixInfo, a as stripLiteralPrefix };
32
63
 
33
64
  //# sourceMappingURL=formatters.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"formatters.js","names":[],"sources":["../../../../lib/ui/helpers/phone/formatters.ts"],"sourcesContent":["/**\n * Phone number formatting utilities\n * Provides pattern-based formatting without external dependencies\n */\n\n/**\n * Extract only digits from a string\n * @param value - Input string that may contain non-digit characters\n * @returns String containing only digits (0-9)\n */\nexport function extractDigits(value: string): string {\n return value.replace(/\\D/g, '');\n}\n\n/**\n * Apply a pattern to input digits\n * Pattern uses '#' as placeholder for digits, other characters are literals\n *\n * @example\n * formatWithPattern(\"5551234567\", \"+1 (###) ###-####\")\n * // returns \"+1 (555) 123-4567\"\n *\n * @param value - String containing the digits to format\n * @param pattern - Format pattern where '#' represents a digit placeholder\n * @returns Formatted string according to pattern\n */\nexport function formatWithPattern(value: string, pattern: string): string {\n const digits = extractDigits(value);\n if (digits.length === 0) return '';\n\n let digitIndex = 0;\n let result = '';\n let started = false;\n\n for (let i = 0; i < pattern.length; i++) {\n if (pattern[i] === '#') {\n if (digitIndex < digits.length) {\n // Add any leading literals before the first digit\n if (!started) {\n // Add all literals from start until this digit\n for (let j = 0; j < i; j++) {\n if (pattern[j] !== '#') {\n result += pattern[j];\n }\n }\n started = true;\n }\n\n result += digits[digitIndex];\n digitIndex++;\n } else {\n // No more digits available\n break;\n }\n } else if (started) {\n // Literal character after we've started\n result += pattern[i];\n }\n }\n\n return result;\n}\n\n/**\n * Calculate cursor position after formatting\n * Critical for UX - ensures cursor stays in correct position relative to digits\n *\n * @example\n * // User types \"4\" at position: \"1|23\" → \"1423\"\n * // After formatting: \"(14) 23\"\n * // Cursor should be: \"(14|) 23\" (after the 4, not at end)\n *\n * @param oldValue - Previous input value\n * @param newValue - New formatted value\n * @param oldCursor - Cursor position before formatting\n * @returns New cursor position after formatting\n */\nexport function getCursorPosition(oldValue: string, newValue: string, oldCursor: number): number {\n // Count digits before cursor in old value\n const digitsBeforeCursor = extractDigits(oldValue.substring(0, oldCursor)).length;\n\n // Find position in new value where we have the same number of digits\n let digitCount = 0;\n let newCursor = 0;\n\n for (let i = 0; i < newValue.length; i++) {\n if (/\\d/.test(newValue[i])) {\n digitCount++;\n if (digitCount === digitsBeforeCursor) {\n newCursor = i + 1;\n break;\n }\n }\n }\n\n // If we didn't find enough digits, cursor goes to end\n if (digitCount < digitsBeforeCursor) {\n newCursor = newValue.length;\n }\n\n return newCursor;\n}\n\n/**\n * Count digit placeholders in a pattern\n * Used to determine maximum number of digits allowed\n *\n * @param pattern - Format pattern\n * @returns Number of '#' characters in pattern\n */\nexport function countDigitPlaceholders(pattern: string): number {\n return (pattern.match(/#/g) || []).length;\n}\n"],"mappings":";AAUA,SAAgB,EAAc,GAAuB;AACnD,QAAO,EAAM,QAAQ,OAAO,GAAG;;AAejC,SAAgB,EAAkB,GAAe,GAAyB;CACxE,IAAM,IAAS,EAAc,EAAM;AACnC,KAAI,EAAO,WAAW,EAAG,QAAO;CAEhC,IAAI,IAAa,GACb,IAAS,IACT,IAAU;AAEd,MAAK,IAAI,IAAI,GAAG,IAAI,EAAQ,QAAQ,IAClC,KAAI,EAAQ,OAAO,IACjB,KAAI,IAAa,EAAO,QAAQ;AAE9B,MAAI,CAAC,GAAS;AAEZ,QAAK,IAAI,IAAI,GAAG,IAAI,GAAG,IACrB,CAAI,EAAQ,OAAO,QACjB,KAAU,EAAQ;AAGtB,OAAU;;AAIZ,EADA,KAAU,EAAO,IACjB;OAGA;MAEO,MAET,KAAU,EAAQ;AAItB,QAAO;;AAiBT,SAAgB,EAAkB,GAAkB,GAAkB,GAA2B;CAE/F,IAAM,IAAqB,EAAc,EAAS,UAAU,GAAG,EAAU,CAAC,CAAC,QAGvE,IAAa,GACb,IAAY;AAEhB,MAAK,IAAI,IAAI,GAAG,IAAI,EAAS,QAAQ,IACnC,KAAI,KAAK,KAAK,EAAS,GAAG,KACxB,KACI,MAAe,IAAoB;AACrC,MAAY,IAAI;AAChB;;AAUN,QAJI,IAAa,MACf,IAAY,EAAS,SAGhB;;AAUT,SAAgB,EAAuB,GAAyB;AAC9D,SAAQ,EAAQ,MAAM,KAAK,IAAI,EAAE,EAAE"}
1
+ {"version":3,"file":"formatters.js","names":[],"sources":["../../../../lib/ui/helpers/phone/formatters.ts"],"sourcesContent":["/**\n * Phone number formatting utilities\n * Provides pattern-based formatting without external dependencies\n */\n\n/**\n * Extract only digits from a string\n * @param value - Input string that may contain non-digit characters\n * @returns String containing only digits (0-9)\n */\nexport function extractDigits(value: string): string {\n return value.replace(/\\D/g, '');\n}\n\n/**\n * Apply a pattern to input digits\n * Pattern uses '#' as placeholder for digits, other characters are literals\n *\n * @example\n * formatWithPattern(\"5551234567\", \"+1 (###) ###-####\")\n * // returns \"+1 (555) 123-4567\"\n *\n * @param value - String containing the digits to format\n * @param pattern - Format pattern where '#' represents a digit placeholder\n * @returns Formatted string according to pattern\n */\nexport function formatWithPattern(value: string, pattern: string): string {\n const digits = extractDigits(value);\n if (digits.length === 0) return '';\n\n let digitIndex = 0;\n let result = '';\n let started = false;\n\n for (let i = 0; i < pattern.length; i++) {\n if (pattern[i] === '#') {\n if (digitIndex < digits.length) {\n // Add any leading literals before the first digit\n if (!started) {\n // Add all literals from start until this digit\n for (let j = 0; j < i; j++) {\n if (pattern[j] !== '#') {\n result += pattern[j];\n }\n }\n started = true;\n }\n\n result += digits[digitIndex];\n digitIndex++;\n } else {\n // No more digits available\n break;\n }\n } else if (started) {\n // Literal character after we've started\n result += pattern[i];\n }\n }\n\n return result;\n}\n\n/**\n * Calculate cursor position after formatting\n * Critical for UX - ensures cursor stays in correct position relative to digits\n *\n * @example\n * // User types \"4\" at position: \"1|23\" → \"1423\"\n * // After formatting: \"(14) 23\"\n * // Cursor should be: \"(14|) 23\" (after the 4, not at end)\n *\n * @param oldValue - Previous input value\n * @param newValue - New formatted value\n * @param oldCursor - Cursor position before formatting\n * @returns New cursor position after formatting\n */\nexport function getCursorPosition(oldValue: string, newValue: string, oldCursor: number): number {\n // Count digits before cursor in old value\n const digitsBeforeCursor = extractDigits(oldValue.substring(0, oldCursor)).length;\n\n // Find position in new value where we have the same number of digits\n let digitCount = 0;\n let newCursor = 0;\n\n for (let i = 0; i < newValue.length; i++) {\n if (/\\d/.test(newValue[i])) {\n digitCount++;\n if (digitCount === digitsBeforeCursor) {\n newCursor = i + 1;\n break;\n }\n }\n }\n\n // If we didn't find enough digits, cursor goes to end\n if (digitCount < digitsBeforeCursor) {\n newCursor = newValue.length;\n }\n\n return newCursor;\n}\n\n/**\n * Count digit placeholders in a pattern\n * Used to determine maximum number of digits allowed\n *\n * @param pattern - Format pattern\n * @returns Number of '#' characters in pattern\n */\nexport function countDigitPlaceholders(pattern: string): number {\n return (pattern.match(/#/g) || []).length;\n}\n\nexport function getLiteralPrefixInfo(pattern: string): {\n literalPrefix: string;\n literalPrefixDigits: string;\n} {\n const firstHashIndex = pattern.indexOf('#');\n const literalPrefix = firstHashIndex > 0 ? pattern.substring(0, firstHashIndex) : '';\n const literalPrefixDigits = extractDigits(literalPrefix);\n return { literalPrefix, literalPrefixDigits };\n}\n\nexport function stripLiteralPrefix(allDigits: string, literalPrefixDigits: string): string {\n if (literalPrefixDigits.length > 0 && allDigits.startsWith(literalPrefixDigits)) {\n return allDigits.substring(literalPrefixDigits.length);\n }\n return allDigits;\n}\n\nexport function calculateCursorPosition(\n digitsBeforeCursor: number,\n formatted: string,\n literalPrefixDigits: string,\n): number {\n let digitCount = 0;\n let skippedPrefixDigits = 0;\n\n for (let i = 0; i < formatted.length; i++) {\n if (/\\d/.test(formatted[i])) {\n if (skippedPrefixDigits < literalPrefixDigits.length) {\n skippedPrefixDigits++;\n continue;\n }\n digitCount++;\n if (digitCount === digitsBeforeCursor) {\n return i + 1;\n }\n }\n }\n\n return formatted.length;\n}\n\nexport function formatPhoneDigits(\n inputValue: string,\n pattern: string,\n cursorPosition: number,\n): { formatted: string; nextCursor: number; literalPrefixDigits: string } | null {\n const { literalPrefixDigits } = getLiteralPrefixInfo(pattern);\n const allDigits = stripLiteralPrefix(extractDigits(inputValue), literalPrefixDigits);\n const maxDigits = countDigitPlaceholders(pattern);\n if (allDigits.length > maxDigits) return null;\n\n const digitsBeforeCursorInInput = stripLiteralPrefix(\n extractDigits(inputValue.substring(0, cursorPosition)),\n literalPrefixDigits,\n );\n const digitsBeforeCursor = digitsBeforeCursorInInput.length;\n const formatted = formatWithPattern(allDigits, pattern);\n const nextCursor = calculateCursorPosition(digitsBeforeCursor, formatted, literalPrefixDigits);\n return { formatted, nextCursor, literalPrefixDigits };\n}\n"],"mappings":";AAUA,SAAgB,EAAc,GAAuB;AACnD,QAAO,EAAM,QAAQ,OAAO,GAAG;;AAejC,SAAgB,EAAkB,GAAe,GAAyB;CACxE,IAAM,IAAS,EAAc,EAAM;AACnC,KAAI,EAAO,WAAW,EAAG,QAAO;CAEhC,IAAI,IAAa,GACb,IAAS,IACT,IAAU;AAEd,MAAK,IAAI,IAAI,GAAG,IAAI,EAAQ,QAAQ,IAClC,KAAI,EAAQ,OAAO,IACjB,KAAI,IAAa,EAAO,QAAQ;AAE9B,MAAI,CAAC,GAAS;AAEZ,QAAK,IAAI,IAAI,GAAG,IAAI,GAAG,IACrB,CAAI,EAAQ,OAAO,QACjB,KAAU,EAAQ;AAGtB,OAAU;;AAIZ,EADA,KAAU,EAAO,IACjB;OAGA;MAEO,MAET,KAAU,EAAQ;AAItB,QAAO;;AAiBT,SAAgB,EAAkB,GAAkB,GAAkB,GAA2B;CAE/F,IAAM,IAAqB,EAAc,EAAS,UAAU,GAAG,EAAU,CAAC,CAAC,QAGvE,IAAa,GACb,IAAY;AAEhB,MAAK,IAAI,IAAI,GAAG,IAAI,EAAS,QAAQ,IACnC,KAAI,KAAK,KAAK,EAAS,GAAG,KACxB,KACI,MAAe,IAAoB;AACrC,MAAY,IAAI;AAChB;;AAUN,QAJI,IAAa,MACf,IAAY,EAAS,SAGhB;;AAUT,SAAgB,EAAuB,GAAyB;AAC9D,SAAQ,EAAQ,MAAM,KAAK,IAAI,EAAE,EAAE;;AAGrC,SAAgB,EAAqB,GAGnC;CACA,IAAM,IAAiB,EAAQ,QAAQ,IAAI,EACrC,IAAgB,IAAiB,IAAI,EAAQ,UAAU,GAAG,EAAe,GAAG;AAElF,QAAO;EAAE;EAAe,qBADI,EAAc,EAAc;EACX;;AAG/C,SAAgB,EAAmB,GAAmB,GAAqC;AAIzF,QAHI,EAAoB,SAAS,KAAK,EAAU,WAAW,EAAoB,GACtE,EAAU,UAAU,EAAoB,OAAO,GAEjD;;AAGT,SAAgB,EACd,GACA,GACA,GACQ;CACR,IAAI,IAAa,GACb,IAAsB;AAE1B,MAAK,IAAI,IAAI,GAAG,IAAI,EAAU,QAAQ,IACpC,KAAI,KAAK,KAAK,EAAU,GAAG,EAAE;AAC3B,MAAI,IAAsB,EAAoB,QAAQ;AACpD;AACA;;AAGF,MADA,KACI,MAAe,EACjB,QAAO,IAAI;;AAKjB,QAAO,EAAU;;AAGnB,SAAgB,EACd,GACA,GACA,GAC+E;CAC/E,IAAM,EAAE,2BAAwB,EAAqB,EAAQ,EACvD,IAAY,EAAmB,EAAc,EAAW,EAAE,EAAoB,EAC9E,IAAY,EAAuB,EAAQ;AACjD,KAAI,EAAU,SAAS,EAAW,QAAO;CAMzC,IAAM,IAJ4B,EAChC,EAAc,EAAW,UAAU,GAAG,EAAe,CAAC,EACtD,EACD,CACoD,QAC/C,IAAY,EAAkB,GAAW,EAAQ;AAEvD,QAAO;EAAE;EAAW,YADD,EAAwB,GAAoB,GAAW,EAAoB;EAC9D;EAAqB"}
@@ -2,5 +2,5 @@
2
2
  * Phone number utilities
3
3
  * Provides formatting, validation, and default prefixes for phone number inputs
4
4
  */
5
- export { extractDigits, formatWithPattern, getCursorPosition, countDigitPlaceholders, } from './formatters';
5
+ export { extractDigits, formatWithPattern, getCursorPosition, countDigitPlaceholders, getLiteralPrefixInfo, stripLiteralPrefix, calculateCursorPosition, formatPhoneDigits, } from './formatters';
6
6
  export { DEFAULT_PHONE_PREFIXES, type PhonePrefix } from './defaultPrefixes';
@@ -1,3 +1,3 @@
1
- import { countDigitPlaceholders as e, extractDigits as t, formatWithPattern as n, getCursorPosition as r } from "./formatters.js";
2
- import { DEFAULT_PHONE_PREFIXES as i } from "./defaultPrefixes.js";
3
- export { i as DEFAULT_PHONE_PREFIXES, e as countDigitPlaceholders, t as extractDigits, n as formatWithPattern, r as getCursorPosition };
1
+ import { calculateCursorPosition as e, countDigitPlaceholders as t, extractDigits as n, formatPhoneDigits as r, formatWithPattern as i, getCursorPosition as a, getLiteralPrefixInfo as o, stripLiteralPrefix as s } from "./formatters.js";
2
+ import { DEFAULT_PHONE_PREFIXES as c } from "./defaultPrefixes.js";
3
+ export { c as DEFAULT_PHONE_PREFIXES, e as calculateCursorPosition, t as countDigitPlaceholders, n as extractDigits, r as formatPhoneDigits, i as formatWithPattern, a as getCursorPosition, o as getLiteralPrefixInfo, s as stripLiteralPrefix };