@clubmed/trident-ui 2.0.0-beta.4 → 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.
- package/chunks/Carousel.context.js +1548 -0
- package/chunks/Carousel.context.js.map +1 -0
- package/chunks/ChatMessage.js +14395 -0
- package/chunks/ChatMessage.js.map +1 -0
- package/chunks/DateField.js +177 -63
- package/chunks/DateField.js.map +1 -1
- package/chunks/Tooltip.js +4461 -0
- package/chunks/Tooltip.js.map +1 -0
- package/chunks/chunk.js +11 -4
- package/chunks/client.js +18953 -0
- package/chunks/client.js.map +1 -0
- package/examples/button-demo.js +2 -2
- package/examples/button-demo.js.map +1 -1
- package/examples/chat-input-demo.d.ts +1 -0
- package/examples/chat-input-demo.js +21 -0
- package/examples/chat-input-demo.js.map +1 -0
- package/examples/chat-message-demo.d.ts +1 -0
- package/examples/chat-message-demo.js +20 -0
- package/examples/chat-message-demo.js.map +1 -0
- package/examples/chat-typing-indicator-demo.d.ts +1 -0
- package/examples/chat-typing-indicator-demo.js +14 -0
- package/examples/chat-typing-indicator-demo.js.map +1 -0
- package/examples/chat-window-demo.d.ts +1 -0
- package/examples/chat-window-demo.js +45 -0
- package/examples/chat-window-demo.js.map +1 -0
- package/examples/checkbox-demo.js +16 -20
- package/examples/checkbox-demo.js.map +1 -1
- package/examples/checkbox-select-demo.d.ts +1 -0
- package/examples/checkbox-select-demo.js +101 -0
- package/examples/checkbox-select-demo.js.map +1 -0
- package/examples/checkboxes-demo.js +6 -6
- package/examples/checkboxes-demo.js.map +1 -1
- package/examples/checkboxes-with-form-control-demo.js +4 -4
- package/examples/choice-expander-demo.js +4 -67
- package/examples/choice-expander-demo.js.map +1 -1
- package/examples/dropdown-demo.js +9 -1
- package/examples/dropdown-demo.js.map +1 -1
- package/examples/elastic-height-demo.js +2 -1
- package/examples/elastic-height-demo.js.map +1 -1
- package/examples/frame-demo.d.ts +1 -0
- package/examples/frame-demo.js +36 -0
- package/examples/frame-demo.js.map +1 -0
- package/examples/frame-horizontal-demo.d.ts +1 -0
- package/examples/frame-horizontal-demo.js +44 -0
- package/examples/frame-horizontal-demo.js.map +1 -0
- package/examples/frame-no-image-demo.d.ts +1 -0
- package/examples/frame-no-image-demo.js +47 -0
- package/examples/frame-no-image-demo.js.map +1 -0
- package/examples/frame-vertical-demo.d.ts +1 -0
- package/examples/frame-vertical-demo.js +44 -0
- package/examples/frame-vertical-demo.js.map +1 -0
- package/examples/frame-with-tag-demo.d.ts +1 -0
- package/examples/frame-with-tag-demo.js +48 -0
- package/examples/frame-with-tag-demo.js.map +1 -0
- package/examples/scrollbar-demo.d.ts +1 -0
- package/examples/scrollbar-demo.js +40 -0
- package/examples/scrollbar-demo.js.map +1 -0
- package/examples/slider-frame-demo.d.ts +1 -0
- package/examples/slider-frame-demo.js +38 -0
- package/examples/slider-frame-demo.js.map +1 -0
- package/examples/slider-frame-with-button-demo.d.ts +1 -0
- package/examples/slider-frame-with-button-demo.js +46 -0
- package/examples/slider-frame-with-button-demo.js.map +1 -0
- package/examples/slider-frame-with-tag-demo.d.ts +1 -0
- package/examples/slider-frame-with-tag-demo.js +50 -0
- package/examples/slider-frame-with-tag-demo.js.map +1 -0
- package/examples/switch-demo.js +1 -1
- package/examples/switch-demo.js.map +1 -1
- package/examples/tooltip-demo.d.ts +1 -0
- package/examples/tooltip-demo.js +103 -0
- package/examples/tooltip-demo.js.map +1 -0
- package/package.json +10 -3
- package/styles/animate.css +19 -19
- package/styles/components.css +82 -0
- package/styles/theme.css +23 -18
- package/styles/ui/button.css +19 -25
- package/styles/ui/chat-button.css +68 -1
- package/styles/ui/controls.css +9 -63
- package/styles/ui/range.css +1 -1
- package/styles/variants.css +0 -2
- package/ui/AdvancedToast.js +11 -11
- package/ui/AdvancedToast.js.map +1 -1
- package/ui/BasicToast.js +1 -1
- package/ui/BasicToast.js.map +1 -1
- package/ui/Breadcrumb.d.ts +2 -2
- package/ui/Breadcrumb.js +31 -35
- package/ui/Breadcrumb.js.map +1 -1
- package/ui/ChatButton.js +1 -1
- package/ui/ChatButton.js.map +1 -1
- package/ui/ChatInput.d.ts +10 -0
- package/ui/ChatInput.js +149 -0
- package/ui/ChatInput.js.map +1 -0
- package/ui/ChatMessage.d.ts +6 -0
- package/ui/ChatMessage.js +2 -0
- package/ui/ChatTypingIndicator.d.ts +2 -0
- package/ui/ChatTypingIndicator.js +30 -0
- package/ui/ChatTypingIndicator.js.map +1 -0
- package/ui/ChatWindow.d.ts +21 -0
- package/ui/ChatWindow.js +108 -0
- package/ui/ChatWindow.js.map +1 -0
- package/ui/ChoiceExpander.js +1 -1
- package/ui/ChoiceExpander.js.map +1 -1
- package/ui/Dropdown.d.ts +20 -3
- package/ui/Dropdown.js +57 -23
- package/ui/Dropdown.js.map +1 -1
- package/ui/ElasticHeight.d.ts +0 -1
- package/ui/ElasticHeight.js +14 -23
- package/ui/ElasticHeight.js.map +1 -1
- package/ui/Loader.d.ts +5 -4
- package/ui/Loader.js +11 -10
- package/ui/Loader.js.map +1 -1
- package/ui/Popin.js +2 -2
- package/ui/Popin.js.map +1 -1
- package/ui/Scrollbar.d.ts +14 -0
- package/ui/Scrollbar.js +68 -0
- package/ui/Scrollbar.js.map +1 -0
- package/ui/SidebarLayout.d.ts +3 -1
- package/ui/SidebarLayout.js +32 -24
- package/ui/SidebarLayout.js.map +1 -1
- package/ui/Tooltip.d.ts +8 -0
- package/ui/Tooltip.js +2 -0
- package/ui/arrows/Arrows.d.ts +1 -0
- package/ui/arrows/Arrows.js +19 -17
- package/ui/arrows/Arrows.js.map +1 -1
- package/ui/buttons/Button.type.d.ts +1 -1
- package/ui/buttons/Button.type.js.map +1 -1
- package/ui/carousel/Carousel.context.d.ts +27 -0
- package/ui/carousel/Carousel.context.js +2 -0
- package/ui/carousel/Carousel.d.ts +3 -0
- package/ui/carousel/Carousel.js +12 -0
- package/ui/carousel/Carousel.js.map +1 -0
- package/ui/carousel/CarouselBody.d.ts +6 -0
- package/ui/carousel/CarouselBody.js +21 -0
- package/ui/carousel/CarouselBody.js.map +1 -0
- package/ui/carousel/CarouselItem.d.ts +6 -0
- package/ui/carousel/CarouselItem.js +21 -0
- package/ui/carousel/CarouselItem.js.map +1 -0
- package/ui/carousel/CarouselNext.d.ts +8 -0
- package/ui/carousel/CarouselNext.js +23 -0
- package/ui/carousel/CarouselNext.js.map +1 -0
- package/ui/carousel/CarouselPrevious.d.ts +8 -0
- package/ui/carousel/CarouselPrevious.js +23 -0
- package/ui/carousel/CarouselPrevious.js.map +1 -0
- package/ui/contexts/TridentUIConfig.js +1 -1
- package/ui/forms/CheckboxSelect.d.ts +31 -0
- package/ui/forms/CheckboxSelect.js +119 -0
- package/ui/forms/CheckboxSelect.js.map +1 -0
- package/ui/forms/Filter.d.ts +8 -4
- package/ui/forms/Filter.js +23 -45
- package/ui/forms/Filter.js.map +1 -1
- package/ui/forms/FormControl.js +6 -6
- package/ui/forms/NumberField.d.ts +10 -10
- package/ui/forms/NumberField.js +8 -8
- package/ui/forms/PhoneField.d.ts +5 -10
- package/ui/forms/PhoneField.js +29 -211
- package/ui/forms/PhoneField.js.map +1 -1
- package/ui/forms/PhoneFieldFullInput.d.ts +16 -0
- package/ui/forms/PhoneFieldFullInput.js +116 -0
- package/ui/forms/PhoneFieldFullInput.js.map +1 -0
- package/ui/forms/PhoneFieldSplitInput.d.ts +19 -0
- package/ui/forms/PhoneFieldSplitInput.js +159 -0
- package/ui/forms/PhoneFieldSplitInput.js.map +1 -0
- package/ui/forms/Select.js +4 -4
- package/ui/forms/Switch.d.ts +3 -3
- package/ui/forms/Switch.js +51 -34
- package/ui/forms/Switch.js.map +1 -1
- package/ui/forms/TextField.js +4 -4
- package/ui/forms/TextField.js.map +1 -1
- package/ui/forms/checkboxes/Checkbox.d.ts +10 -12
- package/ui/forms/checkboxes/Checkbox.js +38 -55
- package/ui/forms/checkboxes/Checkbox.js.map +1 -1
- package/ui/forms/checkboxes/Checkboxes.js +2 -2
- package/ui/forms/checkboxes/Checkboxes.js.map +1 -1
- package/ui/forms/password/Password.js +4 -4
- package/ui/frame/Frame.d.ts +8 -0
- package/ui/frame/Frame.js +16 -0
- package/ui/frame/Frame.js.map +1 -0
- package/ui/frame/FrameLabelsWrapper.d.ts +5 -0
- package/ui/frame/FrameLabelsWrapper.js +12 -0
- package/ui/frame/FrameLabelsWrapper.js.map +1 -0
- package/ui/frame/FrameTitle.d.ts +7 -0
- package/ui/frame/FrameTitle.js +25 -0
- package/ui/frame/FrameTitle.js.map +1 -0
- package/ui/frame/FrameWrapper.d.ts +6 -0
- package/ui/frame/FrameWrapper.js +14 -0
- package/ui/frame/FrameWrapper.js.map +1 -0
- package/ui/frame/SliderFrame.d.ts +14 -0
- package/ui/frame/SliderFrame.js +50 -0
- package/ui/frame/SliderFrame.js.map +1 -0
- package/ui/helpers/colors/colors.d.ts +4 -0
- package/ui/helpers/colors/colors.js +6 -1
- package/ui/helpers/colors/colors.js.map +1 -1
- package/ui/helpers/phone/defaultPrefixes.js +28 -28
- package/ui/helpers/phone/defaultPrefixes.js.map +1 -1
- package/ui/helpers/phone/formatters.d.ts +11 -0
- package/ui/helpers/phone/formatters.js +32 -1
- package/ui/helpers/phone/formatters.js.map +1 -1
- package/ui/helpers/phone/index.d.ts +1 -1
- package/ui/helpers/phone/index.js +3 -3
- package/ui/hooks/useValue.js +1 -1
- package/ui/hooks/useValue.js.map +1 -1
- package/ui/tabs/Tabs.js +1 -1
- package/ui/tabs/Tabs.js.map +1 -1
- package/ui/Breadcrumb.themes.d.ts +0 -8
- package/ui/Breadcrumb.themes.js +0 -16
- package/ui/Breadcrumb.themes.js.map +0 -1
package/ui/forms/PhoneField.js
CHANGED
|
@@ -1,219 +1,37 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
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
|
|
11
|
-
let
|
|
12
|
-
return {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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:
|
|
88
|
-
disabled:
|
|
89
|
-
required:
|
|
90
|
-
hideRequiredStar:
|
|
91
|
-
validationStatus:
|
|
92
|
-
errorMessage:
|
|
93
|
-
children:
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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-[120px] 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-[52px]": !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.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 {
|
|
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-[120px] 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-[52px]': 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.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":"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"}
|
|
@@ -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
|