@ews-admin/global-design-system 1.14.0 → 1.17.1

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
@@ -27428,7 +27428,7 @@ const __iconNode$2X = [
27428
27428
  ],
27429
27429
  ["circle", { cx: "7.5", cy: "7.5", r: ".5", fill: "currentColor", key: "kqv944" }]
27430
27430
  ];
27431
- const Tag = createLucideIcon("tag", __iconNode$2X);
27431
+ const Tag$1 = createLucideIcon("tag", __iconNode$2X);
27432
27432
 
27433
27433
  /**
27434
27434
  * @license lucide-react v0.544.0 - ISC
@@ -32377,7 +32377,7 @@ var index = /*#__PURE__*/Object.freeze({
32377
32377
  Tablet: Tablet,
32378
32378
  TabletSmartphone: TabletSmartphone,
32379
32379
  Tablets: Tablets,
32380
- Tag: Tag,
32380
+ Tag: Tag$1,
32381
32381
  Tags: Tags,
32382
32382
  Tally1: Tally1,
32383
32383
  Tally2: Tally2,
@@ -32673,7 +32673,7 @@ const Input = React.forwardRef(({ className, variant = "default", size = "md", l
32673
32673
  });
32674
32674
  Input.displayName = "Input";
32675
32675
 
32676
- const Select = React.forwardRef(({ options = [], value, onChange, placeholder = "Select an option...", label, helperText, error, isError = false, size = "md", disabled = false, required = false, searchable = false, multiple: _multiple = false, selectClassName, containerClassName, dropdownClassName, maxHeight = 200, clearable = false, renderOption, renderValue, ...props }, ref) => {
32676
+ const Select = React.forwardRef(({ options = [], value, onChange, placeholder = "Select an option...", label, helperText, error, isError = false, size = "md", disabled = false, required = false, searchable = false, multiple: _multiple = false, selectClassName, containerClassName, dropdownClassName, maxHeight = 200, clearable = false, renderOption, renderValue, renderTrigger, ...props }, ref) => {
32677
32677
  const generatedId = React.useId();
32678
32678
  const selectId = `select-${generatedId}`;
32679
32679
  const hasError = isError || !!error;
@@ -32706,15 +32706,6 @@ const Select = React.forwardRef(({ options = [], value, onChange, placeholder =
32706
32706
  const spaceAbove = containerRect.top;
32707
32707
  // Add some buffer (20px) to prevent edge cases
32708
32708
  const buffer = 20;
32709
- console.log("Position calculation:", {
32710
- spaceBelow,
32711
- spaceAbove,
32712
- dropdownHeight,
32713
- viewportHeight,
32714
- containerBottom: containerRect.bottom,
32715
- shouldOpenTop: spaceBelow < dropdownHeight + buffer &&
32716
- spaceAbove > dropdownHeight + buffer,
32717
- });
32718
32709
  // If there's not enough space below but enough space above, position on top
32719
32710
  if (spaceBelow < dropdownHeight + buffer &&
32720
32711
  spaceAbove > dropdownHeight + buffer) {
@@ -32734,13 +32725,6 @@ const Select = React.forwardRef(({ options = [], value, onChange, placeholder =
32734
32725
  const spaceBelow = viewportHeight - containerRect.bottom;
32735
32726
  const spaceAbove = containerRect.top;
32736
32727
  const actualDropdownHeight = dropdownRect.height;
32737
- console.log("Position calculation with element:", {
32738
- spaceBelow,
32739
- spaceAbove,
32740
- actualDropdownHeight,
32741
- viewportHeight,
32742
- containerBottom: containerRect.bottom,
32743
- });
32744
32728
  // If there's not enough space below but enough space above, position on top
32745
32729
  if (spaceBelow < actualDropdownHeight &&
32746
32730
  spaceAbove > actualDropdownHeight) {
@@ -32896,7 +32880,7 @@ const Select = React.forwardRef(({ options = [], value, onChange, placeholder =
32896
32880
  // Text color
32897
32881
  "text-ews-gray-900",
32898
32882
  // Padding for icons
32899
- "pr-10", selectClassName), ...props, children: jsxRuntime.jsxs("div", { className: "flex justify-between items-center", children: [jsxRuntime.jsx("div", { className: "flex-1 min-w-0", children: selectedOption ? (renderValue ? (renderValue(selectedOption)) : (jsxRuntime.jsx("span", { className: "truncate", children: selectedOption.label }))) : (jsxRuntime.jsx("span", { className: "text-ews-gray-500", children: placeholder })) }), jsxRuntime.jsxs("div", { className: "flex items-center ml-2 space-x-1", children: [clearable && selectedOption && !disabled && (jsxRuntime.jsx("button", { type: "button", onClick: handleClear, className: "p-1 rounded hover:bg-ews-gray-100", children: jsxRuntime.jsx(X, { className: cn(iconSizeClasses[size], "text-ews-gray-400") }) })), jsxRuntime.jsx(ChevronDown, { className: cn(iconSizeClasses[size], hasError ? "text-ews-error" : "text-ews-gray-400", disabled && "text-ews-gray-300", isOpen && "rotate-180 transition-transform") })] })] }) }), isOpen && (jsxRuntime.jsxs("div", { ref: dropdownRef, role: "listbox", className: cn("absolute z-50 w-full bg-white rounded-md border shadow-lg border-ews-gray-300", "focus:outline-none", dropdownPosition === "top"
32883
+ "pr-10", selectClassName), ...props, children: jsxRuntime.jsx("div", { className: "flex justify-between items-center", children: renderTrigger ? (renderTrigger()) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { className: "flex-1 min-w-0", children: selectedOption ? (renderValue ? (renderValue(selectedOption)) : (jsxRuntime.jsx("span", { className: "truncate", children: selectedOption.label }))) : (jsxRuntime.jsx("span", { className: "text-ews-gray-500", children: placeholder })) }), jsxRuntime.jsxs("div", { className: "flex items-center ml-2 space-x-1", children: [clearable && selectedOption && !disabled && (jsxRuntime.jsx("button", { type: "button", onClick: handleClear, className: "p-1 rounded hover:bg-ews-gray-100", children: jsxRuntime.jsx(X, { className: cn(iconSizeClasses[size], "text-ews-gray-400") }) })), jsxRuntime.jsx(ChevronDown, { className: cn(iconSizeClasses[size], hasError ? "text-ews-error" : "text-ews-gray-400", disabled && "text-ews-gray-300", isOpen && "rotate-180 transition-transform") })] })] })) }) }), isOpen && (jsxRuntime.jsxs("div", { ref: dropdownRef, role: "listbox", className: cn("absolute z-50 w-full bg-white rounded-md border shadow-lg border-ews-gray-300", "focus:outline-none", dropdownPosition === "top"
32900
32884
  ? "bottom-full mb-1"
32901
32885
  : "top-full mt-1", dropdownClassName), style: { maxHeight: `${maxHeight}px` }, children: [searchable && (jsxRuntime.jsx("div", { className: "p-2 border-b border-ews-gray-200", children: jsxRuntime.jsxs("div", { className: "relative", children: [jsxRuntime.jsx(Search, { className: "absolute left-3 top-1/2 w-4 h-4 transform -translate-y-1/2 text-ews-gray-400" }), jsxRuntime.jsx(Input, { ref: inputRef, type: "text", value: searchTerm, onChange: (e) => setSearchTerm(e.target.value), placeholder: "Search options...", leftIcon: jsxRuntime.jsx(Search, { className: "w-4 h-4 text-ews-gray-400" }) })] }) })), jsxRuntime.jsx("div", { className: "overflow-auto py-1", style: { maxHeight: `${maxHeight - (searchable ? 60 : 0)}px` }, children: filteredOptions.length === 0 ? (jsxRuntime.jsx("div", { className: "px-3 py-2 text-sm text-ews-gray-500", children: searchTerm ? "No options found" : "No options available" })) : (filteredOptions.map((option, index) => {
32902
32886
  const isSelected = option.value === value;
@@ -33309,6 +33293,7 @@ const Modal = ({ isOpen, onClose, title, children, variant = "info", size = "md"
33309
33293
  return (jsxRuntime.jsxs("div", { className: "flex fixed inset-0 z-50 justify-center items-center", children: [jsxRuntime.jsx("div", { className: "absolute inset-0 backdrop-blur-sm bg-black/50", onClick: handleOverlayClick }), jsxRuntime.jsxs("div", { className: cn("relative w-full bg-white rounded-lg shadow-xl transition-all transform", "duration-200 animate-in fade-in-0 zoom-in-95", getSizeClasses(), "mx-4", className), role: "dialog", "aria-modal": "true", "aria-labelledby": "modal-title", children: [jsxRuntime.jsxs("div", { className: cn("flex items-center justify-between p-6 border-b", variantStyles.borderColor), children: [jsxRuntime.jsxs("div", { className: "flex items-center space-x-3", children: [jsxRuntime.jsx("div", { className: cn("p-2 rounded-full", variantStyles.iconBg), children: variantStyles.icon }), jsxRuntime.jsx("h2", { id: "modal-title", className: cn("text-lg font-semibold", variantStyles.titleColor), children: title })] }), jsxRuntime.jsx("button", { onClick: onClose, className: "p-1 text-gray-400 transition-colors hover:text-gray-600", "aria-label": "Close modal", children: jsxRuntime.jsx(X, { className: "w-5 h-5" }) })] }), jsxRuntime.jsx("div", { className: cn("p-6", contentClassName), children: jsxRuntime.jsx("div", { className: "leading-relaxed text-gray-700", children: error && variant === "error" ? (jsxRuntime.jsxs("div", { className: "space-y-3", children: [jsxRuntime.jsx("p", { children: error.message }), error.fields && error.fields.length > 0 && (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("p", { className: "font-semibold text-gray-900", children: "Erreurs de champ:" }), jsxRuntime.jsx("ul", { className: "mt-2 space-y-1", children: error.fields.map((field, index) => (jsxRuntime.jsxs("li", { className: "text-ews-error", children: ["\u2022 ", field.path, ": ", field.message] }, index))) })] }))] })) : (children) }) }), (primaryAction || secondaryAction) && (jsxRuntime.jsxs("div", { className: "flex justify-end items-center p-6 pt-0 space-x-3", children: [secondaryAction && (jsxRuntime.jsx(Button, { variant: "outline", onClick: onSecondaryAction || onClose, disabled: isLoading, children: secondaryAction })), primaryAction && (jsxRuntime.jsx(Button, { variant: variant === "error" ? "error" : "ews-primary", onClick: onPrimaryAction, loading: isLoading, children: primaryAction }))] }))] })] }));
33310
33294
  };
33311
33295
 
33296
+ const ACCEPTED_MIME_TYPES = ["image/jpeg", "image/png", "image/webp"];
33312
33297
  const SIZE_CLASSES = {
33313
33298
  sm: "h-10 w-10",
33314
33299
  md: "h-16 w-16",
@@ -33344,28 +33329,34 @@ function UploadProgressBar({ progress, isLoading, }) {
33344
33329
  const indeterminate = isLoading && progress === 0;
33345
33330
  return (jsxRuntime.jsx("div", { className: "mt-2 h-1.5 w-32 rounded-full bg-gray-200", children: jsxRuntime.jsx("div", { className: cn("h-full rounded-full bg-ews-primary transition-all duration-300", indeterminate && "animate-pulse"), style: { width: indeterminate ? "35%" : `${progress}%` } }) }));
33346
33331
  }
33347
- const ProfileImageUpload = ({ imageUrl, altText, readOnly = false, size = "lg", uploadProgress = 0, isLoading = false, showDeleteButton = true, accept = "image/*", maxFileSizeMB = 3, onFileSelect, onFileSizeExceeded, onDeleteConfirm, deleteConfirmTitle, deleteConfirmMessage, deleteConfirmLabel, cancelLabel, }) => {
33332
+ const ProfileImageUpload = ({ imageUrl, altText, readOnly = false, size = "lg", uploadProgress = 0, isLoading = false, showDeleteButton = true, acceptedFormats = [...ACCEPTED_MIME_TYPES], maxFileSizeMB = 5, aspectRatioHint, onFileSelect, onFileSizeExceeded, onInvalidFormat, onDeleteConfirm, deleteConfirmTitle, deleteConfirmMessage, deleteConfirmLabel, cancelLabel, }) => {
33348
33333
  const fileInputRef = React.useRef(null);
33349
33334
  const [isHovered, setIsHovered] = React.useState(false);
33350
33335
  const [showConfirm, setShowConfirm] = React.useState(false);
33336
+ const acceptAttr = acceptedFormats.join(",");
33351
33337
  const handleEditClick = () => fileInputRef.current?.click();
33352
33338
  const handleFileChange = (e) => {
33353
33339
  const file = e.target.files?.[0];
33354
33340
  if (!file)
33355
33341
  return;
33342
+ if (acceptedFormats.indexOf(file.type) === -1) {
33343
+ onInvalidFormat?.();
33344
+ e.target.value = "";
33345
+ return;
33346
+ }
33356
33347
  if (file.size > maxFileSizeMB * 1024 * 1024) {
33357
33348
  onFileSizeExceeded?.();
33358
33349
  e.target.value = "";
33359
33350
  return;
33360
33351
  }
33361
33352
  onFileSelect(file);
33362
- e.target.value = ""; // allow re-selecting the same file
33353
+ e.target.value = "";
33363
33354
  };
33364
33355
  const handleConfirmDelete = () => {
33365
33356
  setShowConfirm(false);
33366
33357
  onDeleteConfirm();
33367
33358
  };
33368
- return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { className: "inline-flex flex-col items-center", children: [jsxRuntime.jsxs("div", { className: "relative", onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), children: [jsxRuntime.jsx("img", { src: imageUrl, alt: altText, className: cn("rounded-full border-4 border-white object-cover", SIZE_CLASSES[size]) }), jsxRuntime.jsx("div", { className: cn("absolute bottom-0 right-0 rounded-full border-4 border-white bg-green-400", INDICATOR_CLASSES[size]) }), jsxRuntime.jsx("input", { type: "file", ref: fileInputRef, accept: accept, className: "hidden", onChange: handleFileChange }), isHovered && !readOnly && !isLoading && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("button", { type: "button", onClick: handleEditClick, className: "absolute left-0 top-0 flex h-8 w-8 cursor-pointer items-center justify-center rounded-full bg-ews-primary transition-colors hover:bg-ews-secondary", "aria-label": "Edit profile image", children: jsxRuntime.jsx(Pencil, { className: "h-4 w-4 text-white" }) }), showDeleteButton && (jsxRuntime.jsx("button", { type: "button", onClick: () => setShowConfirm(true), className: "absolute left-0 top-10 flex h-8 w-8 cursor-pointer items-center justify-center rounded-full bg-red-600 transition-colors hover:bg-red-700", "aria-label": "Delete profile image", children: jsxRuntime.jsx(Trash, { className: "h-4 w-4 text-white" }) }))] }))] }), jsxRuntime.jsx(UploadProgressBar, { progress: uploadProgress, isLoading: isLoading })] }), jsxRuntime.jsx(Modal, { isOpen: showConfirm, onClose: () => setShowConfirm(false), title: deleteConfirmTitle, variant: "error", size: "sm", primaryAction: deleteConfirmLabel, secondaryAction: cancelLabel, onPrimaryAction: handleConfirmDelete, onSecondaryAction: () => setShowConfirm(false), children: jsxRuntime.jsx("p", { children: deleteConfirmMessage }) })] }));
33359
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { className: "inline-flex flex-col items-center", children: [jsxRuntime.jsxs("div", { className: "relative", onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), children: [jsxRuntime.jsx("img", { src: imageUrl, alt: altText, className: cn("rounded-full border-4 border-white object-cover", SIZE_CLASSES[size]) }), jsxRuntime.jsx("div", { className: cn("absolute bottom-0 right-0 rounded-full border-4 border-white bg-green-400", INDICATOR_CLASSES[size]) }), jsxRuntime.jsx("input", { type: "file", ref: fileInputRef, accept: acceptAttr, className: "hidden", onChange: handleFileChange }), isHovered && !readOnly && !isLoading && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("button", { type: "button", onClick: handleEditClick, className: "absolute left-0 top-0 flex h-8 w-8 cursor-pointer items-center justify-center rounded-full bg-ews-primary transition-colors hover:bg-ews-secondary", "aria-label": "Edit profile image", children: jsxRuntime.jsx(Pencil, { className: "h-4 w-4 text-white" }) }), showDeleteButton && (jsxRuntime.jsx("button", { type: "button", onClick: () => setShowConfirm(true), className: "absolute left-0 top-10 flex h-8 w-8 cursor-pointer items-center justify-center rounded-full bg-red-600 transition-colors hover:bg-red-700", "aria-label": "Delete profile image", children: jsxRuntime.jsx(Trash, { className: "h-4 w-4 text-white" }) }))] }))] }), jsxRuntime.jsx(UploadProgressBar, { progress: uploadProgress, isLoading: isLoading }), aspectRatioHint && (jsxRuntime.jsxs("p", { className: "mt-1 text-xs text-gray-400", children: ["Ratio: ", aspectRatioHint] }))] }), jsxRuntime.jsx(Modal, { isOpen: showConfirm, onClose: () => setShowConfirm(false), title: deleteConfirmTitle, variant: "error", size: "sm", primaryAction: deleteConfirmLabel, secondaryAction: cancelLabel, onPrimaryAction: handleConfirmDelete, onSecondaryAction: () => setShowConfirm(false), children: jsxRuntime.jsx("p", { children: deleteConfirmMessage }) })] }));
33369
33360
  };
33370
33361
 
33371
33362
  const DropdownMultiSelect = ({ options, name, control, placeholder = "Select options", searchPlaceholder = "Search...", onChange, value: controlledValue, defaultValue, onValidate, disabled = false, error, label, className, }) => {
@@ -33717,6 +33708,28 @@ function RegionSelect({ country = exports.SupportedCountry.SENEGAL, value, onCha
33717
33708
  return (jsxRuntime.jsx(Select, { options: options, value: value, onChange: (val) => onChange?.(val), label: label, placeholder: placeholder, error: error, isError: isError, disabled: disabled, required: required, searchable: true, containerClassName: containerClassName }));
33718
33709
  }
33719
33710
 
33711
+ const Tag = ({ label, variant = "plain", className, }) => {
33712
+ const variantClass = variant === "primary"
33713
+ ? "bg-ews-primary text-white"
33714
+ : variant === "secondary"
33715
+ ? "bg-ews-secondary text-white"
33716
+ : "bg-gray-200 text-gray-800";
33717
+ return (jsxRuntime.jsx("span", { className: cn("inline-block rounded-md px-2.5 py-1 text-sm font-medium", variantClass, className), children: label }));
33718
+ };
33719
+
33720
+ const DEFAULT_MAX = 3;
33721
+ const TagList = ({ items, maxVisible = DEFAULT_MAX, showMoreLabel, showLessLabel, variant = "secondary", className, renderTag, }) => {
33722
+ const [expanded, setExpanded] = React.useState(false);
33723
+ const hasMore = items.length > maxVisible;
33724
+ const visible = expanded ? items : items.slice(0, maxVisible);
33725
+ const handleToggle = (e) => {
33726
+ e.preventDefault();
33727
+ e.stopPropagation();
33728
+ setExpanded(prev => !prev);
33729
+ };
33730
+ return (jsxRuntime.jsxs("div", { className: cn("flex flex-wrap items-center gap-2", className), children: [visible.map((label, i) => renderTag ? (renderTag(label, i)) : (jsxRuntime.jsx(Tag, { label: label, variant: variant }, i))), hasMore && !expanded && (jsxRuntime.jsx("span", { className: "text-sm text-gray-400", children: "..." })), hasMore && (jsxRuntime.jsx("button", { onClick: handleToggle, className: "text-xs text-ews-primary hover:underline", children: expanded ? showLessLabel : showMoreLabel }))] }));
33731
+ };
33732
+
33720
33733
  const SpecialtySearchAutocomplete = ({ selectedSpecialties = [], onSpecialtiesChange, specialties: availableSpecialties, placeholder = "Search and select medical specialties...", className = "", disabled = false, maxSelections, showSelectedCount = true, title = "Medical Specialties", label = "Select Specialties", getSelectedCountText, }) => {
33721
33734
  const [filteredSpecialties, setFilteredSpecialties] = React.useState([]);
33722
33735
  const [isLoading, setIsLoading] = React.useState(false);
@@ -37480,7 +37493,7 @@ exports.LucideTableRowsSplit = TableRowsSplit;
37480
37493
  exports.LucideTablet = Tablet;
37481
37494
  exports.LucideTabletSmartphone = TabletSmartphone;
37482
37495
  exports.LucideTablets = Tablets;
37483
- exports.LucideTag = Tag;
37496
+ exports.LucideTag = Tag$1;
37484
37497
  exports.LucideTags = Tags;
37485
37498
  exports.LucideTally1 = Tally1;
37486
37499
  exports.LucideTally2 = Tally2;
@@ -38929,7 +38942,8 @@ exports.TabletSmartphoneIcon = TabletSmartphone;
38929
38942
  exports.Tablets = Tablets;
38930
38943
  exports.TabletsIcon = Tablets;
38931
38944
  exports.Tag = Tag;
38932
- exports.TagIcon = Tag;
38945
+ exports.TagIcon = Tag$1;
38946
+ exports.TagList = TagList;
38933
38947
  exports.Tags = Tags;
38934
38948
  exports.TagsIcon = Tags;
38935
38949
  exports.Tally1 = Tally1;