@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/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