@ews-admin/global-design-system 1.1.14 → 1.1.16
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/README.md +4 -0
- package/dist/components/Input/Input.d.ts +22 -0
- package/dist/components/Input/Input.d.ts.map +1 -1
- package/dist/components/Input/index.d.ts +1 -1
- package/dist/components/Input/index.d.ts.map +1 -1
- package/dist/components/Logo/Logo.d.ts +3 -27
- package/dist/components/Logo/Logo.d.ts.map +1 -1
- package/dist/components/Logo/Logo.types.d.ts +41 -0
- package/dist/components/Logo/Logo.types.d.ts.map +1 -0
- package/dist/components/Logo/index.d.ts +1 -1
- package/dist/components/Logo/index.d.ts.map +1 -1
- package/dist/components/Logo/logoAssets.d.ts +1 -0
- package/dist/components/Logo/logoAssets.d.ts.map +1 -0
- package/dist/components/SearchAutocomplete/SearchAutocomplete.d.ts +1 -1
- package/dist/components/SearchAutocomplete/SearchAutocomplete.d.ts.map +1 -1
- package/dist/components/Select/Select.d.ts +3 -3
- package/dist/components/Select/Select.d.ts.map +1 -1
- package/dist/hooks/useSelectField.d.ts +4 -4
- package/dist/hooks/useSelectField.d.ts.map +1 -1
- package/dist/icons/Icon.d.ts +1 -1
- package/dist/icons/Icon.d.ts.map +1 -1
- package/dist/index.css +2 -2
- package/dist/index.d.ts +52 -14
- package/dist/index.esm.css +2 -2
- package/dist/index.esm.js +77 -23
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +76 -22
- package/dist/index.js.map +1 -1
- package/dist/styles/theme-variables.css +62 -0
- package/dist/utils/index.d.ts +2 -2
- package/dist/utils/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/Input/Input.tsx +112 -3
- package/src/components/Input/index.ts +1 -1
- package/src/components/Logo/Logo.tsx +65 -45
- package/src/components/Logo/Logo.types.ts +42 -0
- package/src/components/Logo/index.ts +1 -1
- package/src/components/SearchAutocomplete/SearchAutocomplete.tsx +1 -1
- package/src/components/Select/Select.tsx +21 -8
- package/src/hooks/useSelectField.ts +7 -2
- package/src/icons/Icon.tsx +1 -1
- package/src/styles/index.css +0 -32
- package/src/utils/index.ts +5 -3
- package/tailwind.preset.js +23 -23
package/dist/index.js
CHANGED
|
@@ -75,13 +75,15 @@ const formatNumeric = (value) => {
|
|
|
75
75
|
};
|
|
76
76
|
/**
|
|
77
77
|
* Utility function to validate phone numbers
|
|
78
|
-
* Validates phone numbers with 1-
|
|
78
|
+
* Validates phone numbers with 1-17 digits, optionally starting with + symbol
|
|
79
79
|
* @param value - Phone number string to validate
|
|
80
80
|
* @returns Boolean indicating if the phone number is valid
|
|
81
81
|
*/
|
|
82
82
|
function isValidPhoneNumber(value) {
|
|
83
83
|
const trimmedValue = value.trim();
|
|
84
|
-
|
|
84
|
+
// Allow + at the beginning, followed by 1-17 digits
|
|
85
|
+
// Or just 1-17 digits without +
|
|
86
|
+
const phoneRegex = /^(\+\d{1,17}|\d{1,17})$/;
|
|
85
87
|
return phoneRegex.test(trimmedValue);
|
|
86
88
|
}
|
|
87
89
|
|
|
@@ -455,7 +457,7 @@ const UserIcon = ({ size = 24, color = "currentColor", className = "", ...props
|
|
|
455
457
|
return jsxRuntime.jsx(User, { size: size, color: color, className: className, ...props });
|
|
456
458
|
};
|
|
457
459
|
|
|
458
|
-
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) => {
|
|
459
461
|
const inputId = id || `input-${Math.random().toString(36).substr(2, 9)}`;
|
|
460
462
|
const hasError = Boolean(error);
|
|
461
463
|
const actualVariant = hasError ? "error" : variant;
|
|
@@ -464,6 +466,20 @@ const Input = React.forwardRef(({ className, variant = "default", size = "md", l
|
|
|
464
466
|
const isPasswordInput = type === "password";
|
|
465
467
|
const shouldShowPasswordToggle = showPasswordToggle && isPasswordInput;
|
|
466
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
|
+
}, []);
|
|
467
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";
|
|
468
484
|
const variants = {
|
|
469
485
|
default: "border-ews-gray-300 focus:border-ews-primary focus:ring-ews-primary",
|
|
@@ -494,7 +510,13 @@ const Input = React.forwardRef(({ className, variant = "default", size = "md", l
|
|
|
494
510
|
}
|
|
495
511
|
}
|
|
496
512
|
// Default rendering for non-checkbox inputs
|
|
497
|
-
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: [
|
|
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 }))] }));
|
|
498
520
|
});
|
|
499
521
|
Input.displayName = "Input";
|
|
500
522
|
|
|
@@ -517,7 +539,7 @@ const Select = React.forwardRef(({ options = [], value, onChange, placeholder =
|
|
|
517
539
|
? options.filter((option) => option.label.toLowerCase().includes(searchTerm.toLowerCase()))
|
|
518
540
|
: options;
|
|
519
541
|
// Calculate dropdown position based on available space
|
|
520
|
-
const calculateDropdownPosition = () => {
|
|
542
|
+
const calculateDropdownPosition = React.useCallback(() => {
|
|
521
543
|
if (!containerRef.current)
|
|
522
544
|
return;
|
|
523
545
|
const containerRect = containerRef.current.getBoundingClientRect();
|
|
@@ -548,7 +570,7 @@ const Select = React.forwardRef(({ options = [], value, onChange, placeholder =
|
|
|
548
570
|
else {
|
|
549
571
|
setDropdownPosition("bottom");
|
|
550
572
|
}
|
|
551
|
-
};
|
|
573
|
+
}, [filteredOptions.length, searchable, maxHeight]);
|
|
552
574
|
// Alternative calculation using actual dropdown element when available
|
|
553
575
|
const calculateDropdownPositionWithElement = () => {
|
|
554
576
|
if (!containerRef.current || !dropdownRef.current)
|
|
@@ -598,7 +620,13 @@ const Select = React.forwardRef(({ options = [], value, onChange, placeholder =
|
|
|
598
620
|
calculateDropdownPositionWithElement();
|
|
599
621
|
});
|
|
600
622
|
}
|
|
601
|
-
}, [
|
|
623
|
+
}, [
|
|
624
|
+
isOpen,
|
|
625
|
+
filteredOptions.length,
|
|
626
|
+
searchable,
|
|
627
|
+
maxHeight,
|
|
628
|
+
calculateDropdownPosition,
|
|
629
|
+
]);
|
|
602
630
|
// Recalculate position on window resize
|
|
603
631
|
React.useEffect(() => {
|
|
604
632
|
const handleResize = () => {
|
|
@@ -612,7 +640,7 @@ const Select = React.forwardRef(({ options = [], value, onChange, placeholder =
|
|
|
612
640
|
window.removeEventListener("resize", handleResize);
|
|
613
641
|
window.removeEventListener("scroll", handleResize);
|
|
614
642
|
};
|
|
615
|
-
}, [isOpen]);
|
|
643
|
+
}, [isOpen, calculateDropdownPosition]);
|
|
616
644
|
// Handle keyboard navigation
|
|
617
645
|
const handleKeyDown = (event) => {
|
|
618
646
|
if (disabled)
|
|
@@ -1721,7 +1749,7 @@ const DropdownMultiSelect = ({ options, name, control, placeholder = "Select opt
|
|
|
1721
1749
|
}), error && jsxRuntime.jsx("p", { className: "mt-1 text-sm text-ews-error", children: error })] }));
|
|
1722
1750
|
};
|
|
1723
1751
|
|
|
1724
|
-
const Logo = ({ size = "md", showTagline: _showTagline = true, iconOnly = false, variant = "normal", className, onClick, }) => {
|
|
1752
|
+
const Logo = ({ size = "md", showTagline: _showTagline = true, iconOnly = false, variant = "normal", className, onClick, customSrc, alt = "MEDECINE 360 Logo", clickable = false, }) => {
|
|
1725
1753
|
const sizes = {
|
|
1726
1754
|
sm: "h-8",
|
|
1727
1755
|
md: "h-12",
|
|
@@ -1734,21 +1762,47 @@ const Logo = ({ size = "md", showTagline: _showTagline = true, iconOnly = false,
|
|
|
1734
1762
|
lg: "h-12 w-12",
|
|
1735
1763
|
xl: "h-16 w-16",
|
|
1736
1764
|
};
|
|
1737
|
-
// Get the appropriate logo image based on variant
|
|
1738
|
-
// For iconOnly, always use favicon.ico
|
|
1739
|
-
const logoSrc =
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1765
|
+
// Get the appropriate logo image based on variant or custom source
|
|
1766
|
+
// For iconOnly, always use favicon.ico unless customSrc is provided
|
|
1767
|
+
const logoSrc = customSrc ||
|
|
1768
|
+
(iconOnly
|
|
1769
|
+
? "/favicon.ico"
|
|
1770
|
+
: variant === "white"
|
|
1771
|
+
? "/image/logoWhite.png"
|
|
1772
|
+
: variant === "fullWhite"
|
|
1773
|
+
? "/image/logoFullWhite.png"
|
|
1774
|
+
: variant === "favicon"
|
|
1775
|
+
? "/favicon.ico"
|
|
1776
|
+
: "/image/logo.png");
|
|
1777
|
+
const isClickable = clickable || !!onClick;
|
|
1748
1778
|
if (iconOnly) {
|
|
1749
|
-
return (jsxRuntime.jsx("div", { className: cn("flex items-center justify-center", iconSizes[size], className), onClick: onClick, role:
|
|
1779
|
+
return (jsxRuntime.jsx("div", { className: cn("flex items-center justify-center", iconSizes[size], isClickable && "cursor-pointer", className), onClick: onClick, role: isClickable ? "button" : undefined, tabIndex: isClickable ? 0 : undefined, onKeyDown: isClickable
|
|
1780
|
+
? (e) => {
|
|
1781
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
1782
|
+
e.preventDefault();
|
|
1783
|
+
onClick?.();
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
: undefined, children: jsxRuntime.jsx("img", { src: logoSrc, alt: alt, className: "object-contain w-full h-full", onError: (e) => {
|
|
1787
|
+
// Fallback to favicon if image fails to load
|
|
1788
|
+
if (logoSrc !== "/favicon.ico") {
|
|
1789
|
+
e.target.src = "/favicon.ico";
|
|
1790
|
+
}
|
|
1791
|
+
} }) }));
|
|
1750
1792
|
}
|
|
1751
|
-
return (jsxRuntime.jsx("div", { className: cn("flex items-center", sizes[size], className), onClick: onClick, role:
|
|
1793
|
+
return (jsxRuntime.jsx("div", { className: cn("flex items-center", sizes[size], isClickable && "cursor-pointer", className), onClick: onClick, role: isClickable ? "button" : undefined, tabIndex: isClickable ? 0 : undefined, onKeyDown: isClickable
|
|
1794
|
+
? (e) => {
|
|
1795
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
1796
|
+
e.preventDefault();
|
|
1797
|
+
onClick?.();
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
: undefined, children: jsxRuntime.jsx("img", { src: logoSrc, alt: alt, className: "object-contain w-auto h-full", onError: (e) => {
|
|
1801
|
+
// Fallback to favicon if image fails to load
|
|
1802
|
+
if (logoSrc !== "/favicon.ico") {
|
|
1803
|
+
e.target.src = "/favicon.ico";
|
|
1804
|
+
}
|
|
1805
|
+
} }) }));
|
|
1752
1806
|
};
|
|
1753
1807
|
|
|
1754
1808
|
const PROMED_THEME = {
|