@youngonesworks/ui 0.1.115 → 0.1.117
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/dist/components/phoneInput/PhoneInput.stories.d.ts +9 -0
- package/dist/components/phoneInput/index.d.ts +15 -0
- package/dist/components/select/index.d.ts +1 -1
- package/dist/hooks/phone/usePhoneNumber.d.ts +13 -0
- package/dist/hooks/phone/usePhoneNumberPrefix.d.ts +1 -0
- package/dist/index.cjs +214 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +214 -4
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
package/dist/index.d.ts
CHANGED
|
@@ -61,6 +61,7 @@ export { TruncatedText } from './components/truncatedText';
|
|
|
61
61
|
export { UnorderedList } from './components/unorderedList';
|
|
62
62
|
export { UnorderedListItem } from './components/unorderedListItem';
|
|
63
63
|
export { UnstyledButton } from './components/unstyledButton';
|
|
64
|
+
export { PhoneInput } from './components/phoneInput';
|
|
64
65
|
export { ProfileMenu } from './components/profileMenu';
|
|
65
|
-
export { WysiwygEditor } from './components/wysiwygEditor';
|
|
66
|
+
export { type IWysiwygEditorImperativeHandle, WysiwygEditor } from './components/wysiwygEditor';
|
|
66
67
|
export * from './theme';
|
package/dist/index.js
CHANGED
|
@@ -13,6 +13,8 @@ import { addMonths, format, setMonth } from "date-fns";
|
|
|
13
13
|
import { enGB, fr, nl, nlBE } from "date-fns/locale";
|
|
14
14
|
import { createPortal } from "react-dom";
|
|
15
15
|
import { Tooltip as Tooltip$1 } from "react-tooltip";
|
|
16
|
+
import { usePhoneNumber } from "@hooks/phone/usePhoneNumber";
|
|
17
|
+
import { usePhoneNumberPrefix } from "@hooks/phone/usePhoneNumberPrefix";
|
|
16
18
|
import { Placeholder } from "@tiptap/extension-placeholder";
|
|
17
19
|
import { Underline } from "@tiptap/extension-underline";
|
|
18
20
|
import { EditorContent, useEditor } from "@tiptap/react";
|
|
@@ -2449,9 +2451,7 @@ function Select({ id, options, placeholder, label, errorText, hideError = false,
|
|
|
2449
2451
|
case "Enter":
|
|
2450
2452
|
case " ":
|
|
2451
2453
|
event.preventDefault();
|
|
2452
|
-
if (focusedIndex >= 0 && filteredOptions[focusedIndex])
|
|
2453
|
-
handleSelect(filteredOptions[focusedIndex].value);
|
|
2454
|
-
}
|
|
2454
|
+
if (focusedIndex >= 0 && filteredOptions[focusedIndex]) handleSelect(filteredOptions[focusedIndex].value);
|
|
2455
2455
|
break;
|
|
2456
2456
|
}
|
|
2457
2457
|
}, [
|
|
@@ -3056,6 +3056,216 @@ const UnorderedListItem = ({ children, actionItem, className, header = false,...
|
|
|
3056
3056
|
})]
|
|
3057
3057
|
});
|
|
3058
3058
|
|
|
3059
|
+
//#endregion
|
|
3060
|
+
//#region src/components/phoneInput/index.tsx
|
|
3061
|
+
const CountrySelector = ({ searchPlaceholder, defaultCountry, ref }) => {
|
|
3062
|
+
const phoneNumberPrefixes = usePhoneNumberPrefix(defaultCountry);
|
|
3063
|
+
const [selectedCountry, setSelectedCountry] = useState(phoneNumberPrefixes.find((country) => country.code === defaultCountry) || null);
|
|
3064
|
+
const [openDropdown, setOpenDropdown] = useState(false);
|
|
3065
|
+
const [searchQuery, setSearchQuery] = useState("");
|
|
3066
|
+
const [filteredCountries, setFilteredCountries] = useState(phoneNumberPrefixes);
|
|
3067
|
+
const [highlightedIndex, setHighlightedIndex] = useState(-1);
|
|
3068
|
+
const optionRefs = useRef([]);
|
|
3069
|
+
const { refs, floatingStyles, context } = useFloating({
|
|
3070
|
+
open: openDropdown,
|
|
3071
|
+
onOpenChange: (isOpen) => {
|
|
3072
|
+
setOpenDropdown(isOpen);
|
|
3073
|
+
if (isOpen) setHighlightedIndex(-1);
|
|
3074
|
+
},
|
|
3075
|
+
middleware: [
|
|
3076
|
+
offset(4),
|
|
3077
|
+
flip(),
|
|
3078
|
+
shift()
|
|
3079
|
+
],
|
|
3080
|
+
whileElementsMounted: autoUpdate,
|
|
3081
|
+
placement: "bottom-start"
|
|
3082
|
+
});
|
|
3083
|
+
const click = useClick(context);
|
|
3084
|
+
const dismiss = useDismiss(context);
|
|
3085
|
+
const role = useRole(context, { role: "combobox" });
|
|
3086
|
+
const { getReferenceProps, getFloatingProps } = useInteractions([
|
|
3087
|
+
click,
|
|
3088
|
+
dismiss,
|
|
3089
|
+
role
|
|
3090
|
+
]);
|
|
3091
|
+
const performSearch = useCallback((query) => {
|
|
3092
|
+
if (!query) return phoneNumberPrefixes;
|
|
3093
|
+
const lowerQuery = query.toLowerCase();
|
|
3094
|
+
return phoneNumberPrefixes.filter((country) => country.name.toLowerCase().includes(lowerQuery) || country.dial_code.toLowerCase().includes(lowerQuery) || country.code.toLowerCase().includes(lowerQuery));
|
|
3095
|
+
}, [phoneNumberPrefixes]);
|
|
3096
|
+
useEffect(() => {
|
|
3097
|
+
const results = performSearch(searchQuery);
|
|
3098
|
+
setFilteredCountries(results);
|
|
3099
|
+
setHighlightedIndex(-1);
|
|
3100
|
+
}, [searchQuery]);
|
|
3101
|
+
useEffect(() => {
|
|
3102
|
+
optionRefs.current = optionRefs.current.slice(0, filteredCountries.length);
|
|
3103
|
+
}, [filteredCountries]);
|
|
3104
|
+
useEffect(() => {
|
|
3105
|
+
if (highlightedIndex >= 0 && optionRefs.current[highlightedIndex]) {
|
|
3106
|
+
optionRefs.current[highlightedIndex]?.scrollIntoView({
|
|
3107
|
+
behavior: "auto",
|
|
3108
|
+
block: "nearest"
|
|
3109
|
+
});
|
|
3110
|
+
}
|
|
3111
|
+
}, [highlightedIndex]);
|
|
3112
|
+
const handleSelect = (country) => {
|
|
3113
|
+
setSelectedCountry(country);
|
|
3114
|
+
setOpenDropdown(false);
|
|
3115
|
+
};
|
|
3116
|
+
const handleKeyDown = (e) => {
|
|
3117
|
+
if (filteredCountries.length === 0) return;
|
|
3118
|
+
switch (e.key) {
|
|
3119
|
+
case "ArrowDown":
|
|
3120
|
+
e.preventDefault();
|
|
3121
|
+
setHighlightedIndex((prev) => prev < filteredCountries.length - 1 ? prev + 1 : 0);
|
|
3122
|
+
break;
|
|
3123
|
+
case "ArrowUp":
|
|
3124
|
+
e.preventDefault();
|
|
3125
|
+
setHighlightedIndex((prev) => prev > 0 ? prev - 1 : filteredCountries.length - 1);
|
|
3126
|
+
break;
|
|
3127
|
+
case "Enter": {
|
|
3128
|
+
e.preventDefault();
|
|
3129
|
+
if (highlightedIndex >= 0) handleSelect(filteredCountries[highlightedIndex]);
|
|
3130
|
+
break;
|
|
3131
|
+
}
|
|
3132
|
+
case "Escape":
|
|
3133
|
+
e.preventDefault();
|
|
3134
|
+
setOpenDropdown(false);
|
|
3135
|
+
break;
|
|
3136
|
+
}
|
|
3137
|
+
};
|
|
3138
|
+
useImperativeHandle(ref, () => ({
|
|
3139
|
+
updateCountry: (countryCode) => {
|
|
3140
|
+
const country = phoneNumberPrefixes.find((c) => c.dial_code === countryCode);
|
|
3141
|
+
if (country) setSelectedCountry(country);
|
|
3142
|
+
},
|
|
3143
|
+
getSelectedCountry: () => selectedCountry
|
|
3144
|
+
}));
|
|
3145
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
3146
|
+
className: "relative",
|
|
3147
|
+
children: [/* @__PURE__ */ jsx("button", {
|
|
3148
|
+
type: "button",
|
|
3149
|
+
ref: refs.setReference,
|
|
3150
|
+
...getReferenceProps(),
|
|
3151
|
+
className: cn("flex h-10 items-center justify-between px-2 text-sm transition-colors"),
|
|
3152
|
+
"aria-haspopup": "listbox",
|
|
3153
|
+
"aria-expanded": openDropdown,
|
|
3154
|
+
"aria-label": `Country selector. Currently selected: ${selectedCountry?.name || "None"} ${selectedCountry?.dial_code || ""}`,
|
|
3155
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
3156
|
+
className: "flex items-center gap-1",
|
|
3157
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
3158
|
+
className: "text-xs font-medium",
|
|
3159
|
+
children: selectedCountry?.dial_code
|
|
3160
|
+
})
|
|
3161
|
+
})
|
|
3162
|
+
}), openDropdown && /* @__PURE__ */ jsx(FloatingPortal, { children: /* @__PURE__ */ jsx(FloatingFocusManager, {
|
|
3163
|
+
context,
|
|
3164
|
+
modal: false,
|
|
3165
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
3166
|
+
ref: refs.setFloating,
|
|
3167
|
+
style: {
|
|
3168
|
+
...floatingStyles,
|
|
3169
|
+
width: "280px"
|
|
3170
|
+
},
|
|
3171
|
+
...getFloatingProps(),
|
|
3172
|
+
role: "listbox",
|
|
3173
|
+
"aria-label": "Country selection",
|
|
3174
|
+
className: cn("z-[999] rounded-md border border-gray-200 bg-white shadow-lg", "overflow-hidden animate-in fade-in"),
|
|
3175
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
3176
|
+
className: "p-2 border-b border-gray-100",
|
|
3177
|
+
children: /* @__PURE__ */ jsx(TextInput, {
|
|
3178
|
+
placeholder: searchPlaceholder,
|
|
3179
|
+
value: searchQuery,
|
|
3180
|
+
onChange: (e) => setSearchQuery(e.target.value),
|
|
3181
|
+
onKeyDown: handleKeyDown,
|
|
3182
|
+
autoFocus: true,
|
|
3183
|
+
"aria-label": "Search countries",
|
|
3184
|
+
role: "searchbox"
|
|
3185
|
+
})
|
|
3186
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
3187
|
+
className: "max-h-[200px] overflow-auto p-1",
|
|
3188
|
+
children: filteredCountries.length > 0 ? filteredCountries.map((country, index) => /* @__PURE__ */ jsxs("button", {
|
|
3189
|
+
ref: (el) => optionRefs.current[index] = el,
|
|
3190
|
+
type: "button",
|
|
3191
|
+
role: "option",
|
|
3192
|
+
"aria-selected": selectedCountry?.code === country.code,
|
|
3193
|
+
onClick: () => handleSelect(country),
|
|
3194
|
+
className: cn("flex w-full items-center gap-3 rounded px-3 py-2 text-sm hover:bg-gray-50", selectedCountry?.code === country.code && "bg-gray-50", highlightedIndex === index && "bg-gray-50 border border-gray-100"),
|
|
3195
|
+
children: [
|
|
3196
|
+
/* @__PURE__ */ jsx("span", {
|
|
3197
|
+
className: "text-base",
|
|
3198
|
+
children: country.flag
|
|
3199
|
+
}),
|
|
3200
|
+
/* @__PURE__ */ jsx("span", {
|
|
3201
|
+
className: "flex-1 text-left truncate",
|
|
3202
|
+
children: country.name
|
|
3203
|
+
}),
|
|
3204
|
+
/* @__PURE__ */ jsx("span", {
|
|
3205
|
+
className: "text-xs font-medium text-gray-600",
|
|
3206
|
+
children: country.dial_code
|
|
3207
|
+
})
|
|
3208
|
+
]
|
|
3209
|
+
}, country.code + country.name + country.dial_code)) : /* @__PURE__ */ jsx("div", {
|
|
3210
|
+
className: "px-3 py-2 text-sm text-gray-500 text-center",
|
|
3211
|
+
children: "No countries found"
|
|
3212
|
+
})
|
|
3213
|
+
})]
|
|
3214
|
+
})
|
|
3215
|
+
}) })]
|
|
3216
|
+
});
|
|
3217
|
+
};
|
|
3218
|
+
const PhoneInput = ({ placeholder, searchPlaceHolder, defaultCountry = "NL", invalidMessage, defaultValue, onChange, ref,...rest }) => {
|
|
3219
|
+
const inputRef = useRef(null);
|
|
3220
|
+
const [phoneNumber, setPhoneNumber] = useState(defaultValue || "");
|
|
3221
|
+
const { validatePhone, stripCountryCode, getCountryCode, formatToInternational } = usePhoneNumber();
|
|
3222
|
+
const countrySelectorRef = useRef(null);
|
|
3223
|
+
const [error, setError] = useState("");
|
|
3224
|
+
const filterPhoneInput = (value) => value.replace(/[^0-9+()]/g, "");
|
|
3225
|
+
const handlePhoneNumberChange = (e) => {
|
|
3226
|
+
const filteredValue = filterPhoneInput(e.target.value);
|
|
3227
|
+
if (error) setError("");
|
|
3228
|
+
if (filteredValue.startsWith("+") && filteredValue.length > 1) {
|
|
3229
|
+
const countryCode = getCountryCode(filteredValue);
|
|
3230
|
+
if (countryCode) countrySelectorRef.current?.updateCountry(countryCode);
|
|
3231
|
+
const strippedNumber = stripCountryCode(filteredValue);
|
|
3232
|
+
setPhoneNumber(strippedNumber);
|
|
3233
|
+
return;
|
|
3234
|
+
} else setPhoneNumber(filteredValue);
|
|
3235
|
+
};
|
|
3236
|
+
const handleBlur = () => {
|
|
3237
|
+
if (phoneNumber.trim()) {
|
|
3238
|
+
const selectedCountry = countrySelectorRef.current?.getSelectedCountry();
|
|
3239
|
+
const validationResult = validatePhone(phoneNumber, { country: selectedCountry?.code });
|
|
3240
|
+
if (validationResult.isValid) {
|
|
3241
|
+
const formattedNumber = formatToInternational(phoneNumber, { country: selectedCountry?.code });
|
|
3242
|
+
const displayNumber = stripCountryCode(formattedNumber);
|
|
3243
|
+
const fullNumber = selectedCountry?.dial_code + displayNumber;
|
|
3244
|
+
setPhoneNumber(displayNumber);
|
|
3245
|
+
onChange(fullNumber);
|
|
3246
|
+
} else setError(invalidMessage);
|
|
3247
|
+
}
|
|
3248
|
+
};
|
|
3249
|
+
return /* @__PURE__ */ jsx(TextInput, {
|
|
3250
|
+
...rest,
|
|
3251
|
+
value: phoneNumber,
|
|
3252
|
+
onChange: handlePhoneNumberChange,
|
|
3253
|
+
onBlur: handleBlur,
|
|
3254
|
+
leftSection: /* @__PURE__ */ jsx(CountrySelector, {
|
|
3255
|
+
ref: countrySelectorRef,
|
|
3256
|
+
searchPlaceholder: searchPlaceHolder,
|
|
3257
|
+
defaultCountry
|
|
3258
|
+
}),
|
|
3259
|
+
placeholder,
|
|
3260
|
+
error,
|
|
3261
|
+
ref: ref || inputRef,
|
|
3262
|
+
"aria-label": "Phone number input",
|
|
3263
|
+
inputMode: "tel",
|
|
3264
|
+
autoComplete: "tel"
|
|
3265
|
+
});
|
|
3266
|
+
};
|
|
3267
|
+
PhoneInput.displayName = "PhoneInput";
|
|
3268
|
+
|
|
3059
3269
|
//#endregion
|
|
3060
3270
|
//#region src/components/profileMenu/index.tsx
|
|
3061
3271
|
const ProfileMenu = ({ title, metaTitle, icon, content, disabled = false, classNames }) => {
|
|
@@ -3355,5 +3565,5 @@ const setCSSVariable = (variable, value) => {
|
|
|
3355
3565
|
};
|
|
3356
3566
|
|
|
3357
3567
|
//#endregion
|
|
3358
|
-
export { AccordionItem, AccordionWrapper, ActionIcon, Alert, AppleAppButtonIcon, AutoCompleteInput, Avatar, AvatarIndicator, Badge, BigBadge, BreadCrumb, Button, CSS_VARIABLE_KEYS, Checkbox, DatePickerInput, Divider, FavouriteButton, Filters, GoogleAppButtonIcon, HR, HamburgerMenuButton, Island, KebabMenu, Label, Loader, LogoBlack, Modal, NavButtons, NumberField, NumberedStepper, PageUnavailable, PasswordInput, Popover, ProfileMenu, ProgressBar, RadioButton, Rating, RegionSelector, Reviews, ScrollToTop, SearchInput, Select, SettingsCard, Skeleton, SkillPill, Stepper, StickyMobileButtonWrapper, Table, TableCell, TableHeader, TableHeaderItem, TableHeaderRow, TableRow, TabsBadge, TabsWrapper, TextInput, Textarea, ThemeIcon, TimeInput, Toggle, Tooltip, TruncatedText, UnorderedList, UnorderedListItem, UnstyledButton, WysiwygEditor, buttonVariants, getCSSVariable, setCSSVariable };
|
|
3568
|
+
export { AccordionItem, AccordionWrapper, ActionIcon, Alert, AppleAppButtonIcon, AutoCompleteInput, Avatar, AvatarIndicator, Badge, BigBadge, BreadCrumb, Button, CSS_VARIABLE_KEYS, Checkbox, DatePickerInput, Divider, FavouriteButton, Filters, GoogleAppButtonIcon, HR, HamburgerMenuButton, Island, KebabMenu, Label, Loader, LogoBlack, Modal, NavButtons, NumberField, NumberedStepper, PageUnavailable, PasswordInput, PhoneInput, Popover, ProfileMenu, ProgressBar, RadioButton, Rating, RegionSelector, Reviews, ScrollToTop, SearchInput, Select, SettingsCard, Skeleton, SkillPill, Stepper, StickyMobileButtonWrapper, Table, TableCell, TableHeader, TableHeaderItem, TableHeaderRow, TableRow, TabsBadge, TabsWrapper, TextInput, Textarea, ThemeIcon, TimeInput, Toggle, Tooltip, TruncatedText, UnorderedList, UnorderedListItem, UnstyledButton, WysiwygEditor, buttonVariants, getCSSVariable, setCSSVariable };
|
|
3359
3569
|
//# sourceMappingURL=index.js.map
|