@ews-admin/global-design-system 1.1.15 → 1.1.17

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.js CHANGED
@@ -457,7 +457,7 @@ const UserIcon = ({ size = 24, color = "currentColor", className = "", ...props
457
457
  return jsxRuntime.jsx(User, { size: size, color: color, className: className, ...props });
458
458
  };
459
459
 
460
- const Input = React.forwardRef(({ className, variant = "default", size = "md", label, helperText, error, leftIcon, rightIcon, fullWidth = false, showPasswordToggle = false, required = false, id, type = "text", ...props }, ref) => {
460
+ const Input = React.forwardRef(({ className, variant = "default", size = "md", label, helperText, error, leftIcon, rightIcon, fullWidth = false, showPasswordToggle = false, required = false, countryCodeSelect, id, type = "text", ...props }, ref) => {
461
461
  const inputId = id || `input-${Math.random().toString(36).substr(2, 9)}`;
462
462
  const hasError = Boolean(error);
463
463
  const actualVariant = hasError ? "error" : variant;
@@ -466,6 +466,20 @@ const Input = React.forwardRef(({ className, variant = "default", size = "md", l
466
466
  const isPasswordInput = type === "password";
467
467
  const shouldShowPasswordToggle = showPasswordToggle && isPasswordInput;
468
468
  const actualType = isPasswordInput && showPassword ? "text" : type;
469
+ // Country code dropdown state
470
+ const [isDropdownOpen, setIsDropdownOpen] = React.useState(false);
471
+ const dropdownRef = React.useRef(null);
472
+ // Close dropdown when clicking outside
473
+ React.useEffect(() => {
474
+ const handleClickOutside = (event) => {
475
+ if (dropdownRef.current &&
476
+ !dropdownRef.current.contains(event.target)) {
477
+ setIsDropdownOpen(false);
478
+ }
479
+ };
480
+ document.addEventListener("mousedown", handleClickOutside);
481
+ return () => document.removeEventListener("mousedown", handleClickOutside);
482
+ }, []);
469
483
  const baseStyles = "block w-full rounded-md border transition-colors focus:outline-none focus:ring-2 focus:ring-offset-0 hover:border-ews-primary";
470
484
  const variants = {
471
485
  default: "border-ews-gray-300 focus:border-ews-primary focus:ring-ews-primary",
@@ -496,7 +510,13 @@ const Input = React.forwardRef(({ className, variant = "default", size = "md", l
496
510
  }
497
511
  }
498
512
  // Default rendering for non-checkbox inputs
499
- return (jsxRuntime.jsxs("div", { className: cn("space-y-1", fullWidth ? "w-full" : "w-auto"), children: [label && (jsxRuntime.jsxs("label", { htmlFor: inputId, className: "block text-sm font-medium text-ews-gray-700", children: [label, required && jsxRuntime.jsx("span", { className: "ml-1 text-ews-error", children: "*" })] })), jsxRuntime.jsxs("div", { className: "relative", children: [leftIcon && (jsxRuntime.jsx("div", { className: "flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none", children: jsxRuntime.jsx("span", { className: cn("text-ews-gray-400", iconSizes[size]), children: leftIcon }) })), jsxRuntime.jsx("input", { id: inputId, type: actualType, className: cn(baseStyles, variants[actualVariant], sizes[size], leftIcon && "pl-10", (rightIcon || shouldShowPasswordToggle) && "pr-10", className), ref: ref, ...props }), rightIcon && !shouldShowPasswordToggle && (jsxRuntime.jsx("div", { className: "flex absolute inset-y-0 right-0 items-center pr-3 pointer-events-none", children: jsxRuntime.jsx("span", { className: cn("text-ews-gray-400", iconSizes[size]), children: rightIcon }) })), shouldShowPasswordToggle && (jsxRuntime.jsx("button", { type: "button", className: "flex absolute inset-y-0 right-0 items-center pr-3", onClick: () => setShowPassword(!showPassword), tabIndex: -1, children: jsxRuntime.jsx("span", { className: cn("transition-colors text-ews-gray-400 hover:text-ews-gray-600", iconSizes[size]), children: showPassword ? jsxRuntime.jsx(EyeOff, { size: 16 }) : jsxRuntime.jsx(Eye, { size: 16 }) }) }))] }), (error || helperText) && (jsxRuntime.jsx("p", { className: cn("text-sm", error ? "text-ews-error" : "text-ews-gray-500"), children: error || helperText }))] }));
513
+ return (jsxRuntime.jsxs("div", { className: cn("space-y-1", fullWidth ? "w-full" : "w-auto"), children: [label && (jsxRuntime.jsxs("label", { htmlFor: inputId, className: "block text-sm font-medium text-ews-gray-700", children: [label, required && jsxRuntime.jsx("span", { className: "ml-1 text-ews-error", children: "*" })] })), jsxRuntime.jsxs("div", { className: "relative", children: [countryCodeSelect && (jsxRuntime.jsx("div", { className: "absolute inset-y-0 left-0 flex items-center pl-1 z-10", ref: dropdownRef, children: jsxRuntime.jsxs("div", { className: "relative", children: [jsxRuntime.jsxs("button", { type: "button", onClick: () => setIsDropdownOpen(!isDropdownOpen), className: cn("flex items-center gap-1 px-2 py-1 rounded text-ews-gray-700", "hover:bg-ews-gray-50 transition-colors cursor-pointer", "focus:outline-none focus:ring-2 focus:ring-ews-primary focus:ring-offset-0", "border-0 bg-transparent text-base"), children: [jsxRuntime.jsx("span", { className: "font-medium", children: countryCodeSelect.value }), jsxRuntime.jsx(ChevronDown, { className: cn("w-4 h-4 text-ews-gray-400 transition-transform", isDropdownOpen && "rotate-180") })] }), isDropdownOpen && (jsxRuntime.jsx("div", { className: cn("absolute top-full left-0 mt-1 bg-white rounded-md border shadow-lg", "border-ews-gray-300 min-w-[120px] z-50"), style: { maxHeight: "200px", overflowY: "auto" }, children: countryCodeSelect.options.map((item) => {
514
+ const isSelected = item.code === countryCodeSelect.value;
515
+ return (jsxRuntime.jsxs("div", { onClick: () => {
516
+ countryCodeSelect.onChange(item.code);
517
+ setIsDropdownOpen(false);
518
+ }, className: cn("px-3 py-2 text-sm cursor-pointer transition-colors", isSelected && "bg-ews-primary text-white", !isSelected && "hover:bg-ews-gray-50"), children: [jsxRuntime.jsx("span", { className: "font-medium", children: item.code }), item.country && (jsxRuntime.jsx("span", { className: cn("ml-2 text-xs", isSelected ? "text-white/80" : "text-ews-gray-500"), children: item.country }))] }, item.code));
519
+ }) }))] }) })), leftIcon && !countryCodeSelect && (jsxRuntime.jsx("div", { className: "flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none", children: jsxRuntime.jsx("span", { className: cn("text-ews-gray-400", iconSizes[size]), children: leftIcon }) })), jsxRuntime.jsx("input", { id: inputId, type: actualType, className: cn(baseStyles, variants[actualVariant], sizes[size], countryCodeSelect && "pl-24", leftIcon && !countryCodeSelect && "pl-10", (rightIcon || shouldShowPasswordToggle) && "pr-10", className), ref: ref, ...props }), rightIcon && !shouldShowPasswordToggle && (jsxRuntime.jsx("div", { className: "flex absolute inset-y-0 right-0 items-center pr-3 pointer-events-none", children: jsxRuntime.jsx("span", { className: cn("text-ews-gray-400", iconSizes[size]), children: rightIcon }) })), shouldShowPasswordToggle && (jsxRuntime.jsx("button", { type: "button", className: "flex absolute inset-y-0 right-0 items-center pr-3", onClick: () => setShowPassword(!showPassword), tabIndex: -1, children: jsxRuntime.jsx("span", { className: cn("transition-colors text-ews-gray-400 hover:text-ews-gray-600", iconSizes[size]), children: showPassword ? jsxRuntime.jsx(EyeOff, { size: 16 }) : jsxRuntime.jsx(Eye, { size: 16 }) }) }))] }), (error || helperText) && (jsxRuntime.jsx("p", { className: cn("text-sm", error ? "text-ews-error" : "text-ews-gray-500"), children: error || helperText }))] }));
500
520
  });
501
521
  Input.displayName = "Input";
502
522
 
@@ -1083,33 +1103,68 @@ function useWatch(props) {
1083
1103
  const _defaultValue = React.useRef(defaultValue);
1084
1104
  const _compute = React.useRef(compute);
1085
1105
  const _computeFormValues = React.useRef(undefined);
1106
+ const _prevControl = React.useRef(control);
1107
+ const _prevName = React.useRef(name);
1086
1108
  _compute.current = compute;
1087
- const defaultValueMemo = React.useMemo(() => control._getWatch(name, _defaultValue.current), [control, name]);
1088
- const [value, updateValue] = React.useState(_compute.current ? _compute.current(defaultValueMemo) : defaultValueMemo);
1089
- useIsomorphicLayoutEffect(() => control._subscribe({
1090
- name,
1091
- formState: {
1092
- values: true,
1093
- },
1094
- exact,
1095
- callback: (formState) => {
1096
- if (!disabled) {
1097
- const formValues = generateWatchOutput(name, control._names, formState.values || control._formValues, false, _defaultValue.current);
1098
- if (_compute.current) {
1099
- const computedFormValues = _compute.current(formValues);
1100
- if (!deepEqual(computedFormValues, _computeFormValues.current)) {
1101
- updateValue(computedFormValues);
1102
- _computeFormValues.current = computedFormValues;
1103
- }
1104
- }
1105
- else {
1106
- updateValue(formValues);
1109
+ const [value, updateValue] = React.useState(() => {
1110
+ const defaultValue = control._getWatch(name, _defaultValue.current);
1111
+ return _compute.current ? _compute.current(defaultValue) : defaultValue;
1112
+ });
1113
+ const getCurrentOutput = React.useCallback((values) => {
1114
+ const formValues = generateWatchOutput(name, control._names, values || control._formValues, false, _defaultValue.current);
1115
+ return _compute.current ? _compute.current(formValues) : formValues;
1116
+ }, [control._formValues, control._names, name]);
1117
+ const refreshValue = React.useCallback((values) => {
1118
+ if (!disabled) {
1119
+ const formValues = generateWatchOutput(name, control._names, values || control._formValues, false, _defaultValue.current);
1120
+ if (_compute.current) {
1121
+ const computedFormValues = _compute.current(formValues);
1122
+ if (!deepEqual(computedFormValues, _computeFormValues.current)) {
1123
+ updateValue(computedFormValues);
1124
+ _computeFormValues.current = computedFormValues;
1107
1125
  }
1108
1126
  }
1109
- },
1110
- }), [control, disabled, name, exact]);
1127
+ else {
1128
+ updateValue(formValues);
1129
+ }
1130
+ }
1131
+ }, [control._formValues, control._names, disabled, name]);
1132
+ useIsomorphicLayoutEffect(() => {
1133
+ if (_prevControl.current !== control ||
1134
+ !deepEqual(_prevName.current, name)) {
1135
+ _prevControl.current = control;
1136
+ _prevName.current = name;
1137
+ refreshValue();
1138
+ }
1139
+ return control._subscribe({
1140
+ name,
1141
+ formState: {
1142
+ values: true,
1143
+ },
1144
+ exact,
1145
+ callback: (formState) => {
1146
+ refreshValue(formState.values);
1147
+ },
1148
+ });
1149
+ }, [control, exact, name, refreshValue]);
1111
1150
  React.useEffect(() => control._removeUnmounted());
1112
- return value;
1151
+ // If name or control changed for this render, synchronously reflect the
1152
+ // latest value so callers (like useController) see the correct value
1153
+ // immediately on the same render.
1154
+ // Optimize: Check control reference first before expensive deepEqual
1155
+ const controlChanged = _prevControl.current !== control;
1156
+ const prevName = _prevName.current;
1157
+ // Cache the computed output to avoid duplicate calls within the same render
1158
+ // We include shouldReturnImmediate in deps to ensure proper recomputation
1159
+ const computedOutput = React.useMemo(() => {
1160
+ if (disabled) {
1161
+ return null;
1162
+ }
1163
+ const nameChanged = !controlChanged && !deepEqual(prevName, name);
1164
+ const shouldReturnImmediate = controlChanged || nameChanged;
1165
+ return shouldReturnImmediate ? getCurrentOutput() : null;
1166
+ }, [disabled, controlChanged, name, prevName, getCurrentOutput]);
1167
+ return computedOutput !== null ? computedOutput : value;
1113
1168
  }
1114
1169
 
1115
1170
  /**
@@ -1237,7 +1292,7 @@ function useController(props) {
1237
1292
  };
1238
1293
  updateMounted(name, true);
1239
1294
  if (_shouldUnregisterField) {
1240
- const value = cloneObject(get(control._options.defaultValues, name));
1295
+ const value = cloneObject(get(control._options.defaultValues, name, _props.current.defaultValue));
1241
1296
  set(control._defaultValues, name, value);
1242
1297
  if (isUndefined(get(control._formValues, name))) {
1243
1298
  set(control._formValues, name, value);