@crestbase/web-shared-ui 1.0.1 → 1.0.3

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.
Files changed (91) hide show
  1. package/README.md +117 -4
  2. package/dist/components/button/CircleBtn.d.ts +9 -0
  3. package/dist/components/button/CircleBtn.js +5 -0
  4. package/dist/components/button/ShadowButton.d.ts +8 -0
  5. package/dist/components/button/ShadowButton.js +5 -0
  6. package/dist/components/button/index.d.ts +12 -3
  7. package/dist/components/button/index.js +17 -3
  8. package/dist/components/core/Accordion.d.ts +11 -0
  9. package/dist/components/core/Accordion.js +7 -0
  10. package/dist/components/core/AddressInput.d.ts +34 -0
  11. package/dist/components/core/AddressInput.js +88 -0
  12. package/dist/components/core/BottomSheet.d.ts +10 -0
  13. package/dist/components/core/BottomSheet.js +15 -0
  14. package/dist/components/core/Calendar.d.ts +17 -0
  15. package/dist/components/core/Calendar.js +440 -0
  16. package/dist/components/core/CalendarExtended.d.ts +36 -0
  17. package/dist/components/core/CalendarExtended.js +154 -0
  18. package/dist/components/core/Checkbox.d.ts +13 -0
  19. package/dist/components/core/Checkbox.js +10 -0
  20. package/dist/components/core/CustomDropdown.d.ts +19 -0
  21. package/dist/components/core/CustomDropdown.js +41 -0
  22. package/dist/components/core/Modal.d.ts +12 -0
  23. package/dist/components/core/Modal.js +12 -0
  24. package/dist/components/core/NumberInput.d.ts +12 -0
  25. package/dist/components/core/NumberInput.js +34 -0
  26. package/dist/components/core/NumberInputWithCommas.d.ts +10 -0
  27. package/dist/components/core/NumberInputWithCommas.js +19 -0
  28. package/dist/components/core/OptionsDropdown.d.ts +35 -0
  29. package/dist/components/core/OptionsDropdown.js +50 -0
  30. package/dist/components/core/OtpInput.d.ts +9 -0
  31. package/dist/components/core/OtpInput.js +54 -0
  32. package/dist/components/core/PreviewArea.d.ts +18 -0
  33. package/dist/components/core/PreviewArea.js +31 -0
  34. package/dist/components/core/SelectionList.d.ts +16 -0
  35. package/dist/components/core/SelectionList.js +9 -0
  36. package/dist/components/core/SquareCheckbox.d.ts +10 -0
  37. package/dist/components/core/SquareCheckbox.js +5 -0
  38. package/dist/components/core/TabNavigation.d.ts +19 -0
  39. package/dist/components/core/TabNavigation.js +11 -0
  40. package/dist/components/core/Textarea.d.ts +10 -0
  41. package/dist/components/core/Textarea.js +8 -0
  42. package/dist/components/core/Toggle.d.ts +10 -0
  43. package/dist/components/core/Toggle.js +11 -0
  44. package/dist/components/core/Upload.d.ts +28 -0
  45. package/dist/components/core/Upload.js +76 -0
  46. package/dist/components/core/UploadArea.d.ts +14 -0
  47. package/dist/components/core/UploadArea.js +13 -0
  48. package/dist/components/core/input.d.ts +11 -0
  49. package/dist/components/core/input.js +24 -0
  50. package/dist/components/index.d.ts +41 -1
  51. package/dist/components/index.js +25 -1
  52. package/dist/components/navigation/Logo.d.ts +7 -0
  53. package/dist/components/navigation/Logo.js +7 -0
  54. package/dist/components/notification/GlobalNotificationProvider.d.ts +4 -0
  55. package/dist/components/notification/GlobalNotificationProvider.js +6 -0
  56. package/dist/components/notification/NotificationPopup.d.ts +1 -0
  57. package/dist/components/notification/NotificationPopup.js +73 -0
  58. package/dist/components/notification/notificationStore.d.ts +15 -0
  59. package/dist/components/notification/notificationStore.js +12 -0
  60. package/dist/components/svg/Crestbase.d.ts +6 -0
  61. package/dist/components/svg/Crestbase.js +5 -0
  62. package/dist/components/svg/CrestbaseColored.d.ts +6 -0
  63. package/dist/components/svg/CrestbaseColored.js +5 -0
  64. package/dist/components/svg/UploadIcon.d.ts +5 -0
  65. package/dist/components/svg/UploadIcon.js +4 -0
  66. package/dist/hooks/index.d.ts +4 -0
  67. package/dist/hooks/index.js +4 -0
  68. package/dist/hooks/useFileUpload.d.ts +35 -0
  69. package/dist/hooks/useFileUpload.js +219 -0
  70. package/dist/hooks/useFileUploadWithNotifications.d.ts +36 -0
  71. package/dist/hooks/useFileUploadWithNotifications.js +39 -0
  72. package/dist/hooks/useResponsive.d.ts +38 -0
  73. package/dist/hooks/useResponsive.js +97 -0
  74. package/dist/index.d.ts +3 -1
  75. package/dist/index.js +3 -1
  76. package/dist/styles.css +2 -0
  77. package/dist/types/property.d.ts +17 -0
  78. package/dist/types/property.js +2 -0
  79. package/dist/utils/fileValidation.d.ts +21 -0
  80. package/dist/utils/fileValidation.js +82 -0
  81. package/dist/utils/googleMapsLoader.d.ts +40 -0
  82. package/dist/utils/googleMapsLoader.js +108 -0
  83. package/dist/utils/index.d.ts +3 -0
  84. package/dist/utils/index.js +13 -0
  85. package/package.json +22 -12
  86. package/dist/components/button/index.d.ts.map +0 -1
  87. package/dist/components/button/index.js.map +0 -1
  88. package/dist/components/index.d.ts.map +0 -1
  89. package/dist/components/index.js.map +0 -1
  90. package/dist/index.d.ts.map +0 -1
  91. package/dist/index.js.map +0 -1
@@ -0,0 +1,10 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ const Checkbox = ({ checked, onChange, label, name, required = false, disabled = false, error, className = "", }) => {
4
+ return (_jsxs("div", { className: `w-full text-sm ${className}`, children: [label && (_jsx("label", { htmlFor: name, className: "block mb-2 text-xs text-gray-500", children: label })), _jsx("div", { className: "relative", children: _jsxs("div", { className: "w-full p-0 px-3 h-13.5 sm:h-auto sm:p-3 border border-gray-200 rounded-xl bg-gray-50 focus-within:border-gray-300 focus-within:bg-white flex items-center justify-between", children: [_jsx("span", { className: "text-base sm:text-xs text-gray-400", children: checked ? "Yes" : "No" }), _jsxs("label", { className: "relative cursor-pointer", children: [_jsx("input", { id: name, name: name, type: "checkbox", required: required, disabled: disabled, checked: checked, onChange: onChange, className: "sr-only", autoComplete: "off" }), _jsx("div", { className: [
5
+ "w-6 h-6 rounded-lg border flex items-center justify-center transition-colors",
6
+ disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer",
7
+ checked ? "bg-black border-black" : "bg-white border-gray-300",
8
+ ].join(" "), children: checked && (_jsx("svg", { className: "w-3.5 h-3.5 text-white", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M20 6L9 17l-5-5" }) })) })] })] }) }), error && _jsx("p", { className: "mt-1 text-sm text-red-500", children: error })] }));
9
+ };
10
+ export default Checkbox;
@@ -0,0 +1,19 @@
1
+ import React from "react";
2
+ export interface DropdownOption {
3
+ value: string;
4
+ label?: string;
5
+ }
6
+ export interface CustomDropdownProps {
7
+ label?: string;
8
+ placeholder?: string;
9
+ options: DropdownOption[];
10
+ value?: string;
11
+ onChange: (value: string) => void;
12
+ error?: string;
13
+ className?: string;
14
+ labelClass?: string;
15
+ minimal?: boolean;
16
+ disabled?: boolean;
17
+ }
18
+ declare const _default: (props: CustomDropdownProps) => React.JSX.Element;
19
+ export default _default;
@@ -0,0 +1,41 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useRef, useState } from "react";
4
+ import { ChevronDownIcon, X } from "lucide-react";
5
+ const CustomDropdown = ({ label, placeholder = "Select an option", options, value, onChange, error, className = "", labelClass = "", minimal, disabled = false, }) => {
6
+ const [isOpen, setIsOpen] = useState(false);
7
+ const dropdownRef = useRef(null);
8
+ const selectedOption = options.find((option) => option.value === value);
9
+ useEffect(() => {
10
+ const handleClickOutside = (event) => {
11
+ if (dropdownRef.current &&
12
+ !dropdownRef.current.contains(event.target)) {
13
+ setIsOpen(false);
14
+ }
15
+ };
16
+ document.addEventListener("mousedown", handleClickOutside);
17
+ return () => {
18
+ document.removeEventListener("mousedown", handleClickOutside);
19
+ };
20
+ }, []);
21
+ const handleOptionClick = (optionValue) => {
22
+ if (disabled)
23
+ return;
24
+ onChange(optionValue);
25
+ setIsOpen(false);
26
+ };
27
+ const handleClear = (e) => {
28
+ if (disabled)
29
+ return;
30
+ e.stopPropagation();
31
+ onChange("");
32
+ setIsOpen(false);
33
+ };
34
+ return (_jsxs("div", { className: `w-full ${minimal ? "text-[11px]" : "text-base sm:text-xs"}`, ref: dropdownRef, children: [label && (_jsx("label", { className: `${labelClass} block mb-2 text-[10px] text-gray-500`, children: label })), _jsxs("div", { className: "relative", children: [_jsxs("button", { type: "button", onClick: () => !disabled && setIsOpen(!isOpen), disabled: disabled, className: `${minimal
35
+ ? "flex gap-1 items-center"
36
+ : "w-full p-0 px-4 sm:p-4 rounded-xl bg-gray-50 border outline-none text-left flex items-center justify-between h-13.5 sm:h-auto"} ${error ? "border-red-500" : "border-gray-200"} ${disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer"} ${className}`, children: [_jsx("span", { className: `${selectedOption ? "text-gray-900" : "text-gray-400"}`, children: selectedOption ? selectedOption.label : placeholder }), !disabled && (_jsx(ChevronDownIcon, { className: `w-4 h-4 text-gray-800 transition-transform duration-200 ${isOpen ? "rotate-180" : ""}` }))] }), isOpen && !disabled && (_jsx("div", { className: "absolute z-50 min-w-full mt-1 bg-white border border-gray-200 rounded-xl shadow-lg max-h-60 overflow-y-auto", children: options.map((option) => (_jsxs("div", { className: `flex items-center justify-between hover:bg-gray-50 transition-colors ${value === option.value
37
+ ? "bg-blue-50 text-blue-600"
38
+ : "text-gray-900"} first:rounded-t-xl last:rounded-b-xl`, children: [_jsx("button", { type: "button", onClick: () => handleOptionClick(option.value), className: "flex-1 text-left text-nowrap px-4 py-3", children: option.label }), value === option.value && (_jsx("button", { type: "button", onClick: handleClear, className: "p-0.5 hover:bg-gray-100 rounded-full transition-colors ml-2", children: _jsx(X, { className: "w-3 h-3 text-gray-400 hover:text-gray-600" }) }))] }, option.value))) }))] }), error && _jsx("p", { className: "mt-1 text-red-500", children: error })] }));
39
+ };
40
+ CustomDropdown.displayName = "CustomDropdown";
41
+ export default CustomDropdown;
@@ -0,0 +1,12 @@
1
+ import React from "react";
2
+ export interface ModalProps {
3
+ isOpen: boolean;
4
+ onClose: () => void;
5
+ children: React.ReactNode;
6
+ allowBackdropClose?: boolean;
7
+ blurClass?: string;
8
+ containerClassName?: string;
9
+ className?: string;
10
+ }
11
+ declare const _default: (props: ModalProps) => React.JSX.Element;
12
+ export default _default;
@@ -0,0 +1,12 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ const Modal = ({ isOpen, onClose, children, allowBackdropClose = true, blurClass = "backdrop-blur-xs", containerClassName = "z-50 mx-auto w-full flex justify-center items-start text-black", className = "py-12", }) => {
4
+ if (!isOpen)
5
+ return null;
6
+ return (_jsxs("div", { className: `fixed inset-0 z-[80] flex items-start justify-center ${className}`, children: [_jsx("button", { className: `absolute inset-0 bg-black/70 ${blurClass}`, onClick: allowBackdropClose ? onClose : undefined }), _jsx("div", { role: "dialog", className: containerClassName, onClick: (e) => {
7
+ if (e.target === e.currentTarget && allowBackdropClose) {
8
+ onClose();
9
+ }
10
+ }, children: children })] }));
11
+ };
12
+ export default Modal;
@@ -0,0 +1,12 @@
1
+ import React from "react";
2
+ export interface NumberInputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "className" | "type"> {
3
+ label?: string;
4
+ error?: string;
5
+ className?: string;
6
+ onIncrement?: () => void;
7
+ onDecrement?: () => void;
8
+ }
9
+ declare const _default: (props: NumberInputProps & {
10
+ ref?: React.Ref<HTMLInputElement>;
11
+ }) => React.JSX.Element;
12
+ export default _default;
@@ -0,0 +1,34 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { forwardRef } from "react";
4
+ import { ChevronDownIcon, ChevronUpIcon } from "lucide-react";
5
+ const NumberInput = forwardRef(({ label, name, placeholder, required = false, error, disabled = false, autoFocus = false, className = "", value, onChange, onIncrement, onDecrement, ...props }, ref) => {
6
+ const handleIncrement = () => {
7
+ if (onIncrement) {
8
+ onIncrement();
9
+ }
10
+ else if (onChange) {
11
+ const currentValue = Number(value) || 0;
12
+ const event = {
13
+ target: { value: String(currentValue + 1) },
14
+ };
15
+ onChange(event);
16
+ }
17
+ };
18
+ const handleDecrement = () => {
19
+ if (onDecrement) {
20
+ onDecrement();
21
+ }
22
+ else if (onChange) {
23
+ const currentValue = Number(value) || 0;
24
+ const newValue = Math.max(0, currentValue - 1);
25
+ const event = {
26
+ target: { value: String(newValue) },
27
+ };
28
+ onChange(event);
29
+ }
30
+ };
31
+ return (_jsxs("div", { className: `w-full text-sm ${className}`, children: [label && (_jsx("label", { htmlFor: name, className: "block mb-2 text-xs text-gray-500", children: label })), _jsxs("div", { className: "relative", children: [_jsx("input", { ref: ref, id: name, name: name, type: "number", placeholder: placeholder, required: required, disabled: disabled, autoFocus: autoFocus, value: value, onChange: onChange, autoComplete: "off", className: `w-full text-base sm:text-xs h-13.5 sm:h-auto p-0 px-4 sm:p-4 border border-gray-200 rounded-xl bg-gray-50 focus:border-gray-300 focus:bg-white outline-none pr-12 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none ${error ? "border-red-500" : "border-gray-200"} text-black`, ...props }), _jsxs("div", { className: "absolute right-3 top-1/2 transform -translate-y-1/2 flex flex-col", children: [_jsx("button", { type: "button", onClick: handleIncrement, disabled: disabled, className: "text-gray-400 hover:text-gray-600 disabled:opacity-50 disabled:cursor-not-allowed p-0.5", children: _jsx(ChevronUpIcon, { className: "w-3 h-3" }) }), _jsx("button", { type: "button", onClick: handleDecrement, disabled: disabled, className: "text-gray-400 hover:text-gray-600 disabled:opacity-50 disabled:cursor-not-allowed p-0.5", children: _jsx(ChevronDownIcon, { className: "w-3 h-3" }) })] })] }), error && _jsx("p", { className: "mt-1 text-xs text-red-500", children: error })] }));
32
+ });
33
+ NumberInput.displayName = "NumberInput";
34
+ export default NumberInput;
@@ -0,0 +1,10 @@
1
+ import React from "react";
2
+ import type { InputProps } from "./input";
3
+ export interface NumberInputWithCommasProps extends Omit<InputProps, "type" | "onChange"> {
4
+ onChange: (value: string) => void;
5
+ value?: string;
6
+ }
7
+ declare const _default: (props: NumberInputWithCommasProps & {
8
+ ref?: React.Ref<HTMLInputElement>;
9
+ }) => React.JSX.Element;
10
+ export default _default;
@@ -0,0 +1,19 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { forwardRef, useEffect, useState } from "react";
4
+ import { formatNumberWithCommas, getCleanNumber } from "../../utils";
5
+ const NumberInputWithCommas = forwardRef(({ label, name, placeholder, required = false, error, disabled = false, autoFocus = false, className = "", hideLabel = false, value = "", onChange, ...props }, ref) => {
6
+ const [displayValue, setDisplayValue] = useState(formatNumberWithCommas(value));
7
+ const handleInputChange = (e) => {
8
+ const inputValue = e.target.value;
9
+ const formatted = formatNumberWithCommas(inputValue);
10
+ setDisplayValue(formatted);
11
+ onChange(getCleanNumber(formatted));
12
+ };
13
+ useEffect(() => {
14
+ setDisplayValue(formatNumberWithCommas(value));
15
+ }, [value]);
16
+ return (_jsxs("div", { className: `w-full text-sm ${className}`, children: [label && !hideLabel && (_jsx("label", { htmlFor: name, className: "block mb-2 text-xs text-gray-500", children: label })), _jsx("div", { className: "relative", children: _jsxs("div", { className: "relative flex items-center", children: [_jsx("span", { className: "absolute left-5 mt-0.5/2 sm:left-4 top-1/2 transform -translate-y-1/2 text-xs text-gray-600 pointer-events-none", children: "\u20A6" }), _jsx("input", { ref: ref, id: name, name: name, type: "text", inputMode: "numeric", placeholder: placeholder, required: required, disabled: disabled, autoFocus: autoFocus, value: displayValue, onChange: handleInputChange, autoComplete: "off", className: `w-full text-base sm:text-xs h-13.5 sm:-h-auto p-0 px-4 sm:p-4 pl-8 sm:pl-7 border border-gray-200 rounded-xl bg-gray-50 focus:border-gray-300 focus:bg-white outline-none placeholder-gray-400 ${error ? "border-red-500" : "border-gray-200"} text-black`, ...props })] }) }), error && _jsx("p", { className: "mt-1 text-xs text-red-500", children: error })] }));
17
+ });
18
+ NumberInputWithCommas.displayName = "NumberInputWithCommas";
19
+ export default NumberInputWithCommas;
@@ -0,0 +1,35 @@
1
+ import React, { ReactNode } from "react";
2
+ export type DropdownRenderHelpers = {
3
+ /** Close the dropdown programmatically */
4
+ close: () => void;
5
+ };
6
+ export interface OptionItem {
7
+ id: string;
8
+ label: string;
9
+ onClick: () => void;
10
+ disabled?: boolean;
11
+ /** Optional icon node shown before the label */
12
+ icon?: ReactNode;
13
+ }
14
+ export interface OptionsDropdownProps {
15
+ /**
16
+ * Array of options to render as the dropdown menu. When `dropdownContent` is provided,
17
+ * this array is optional and ignored. Kept for backward compatibility.
18
+ */
19
+ options?: OptionItem[];
20
+ trigger?: ReactNode;
21
+ listClassName?: string;
22
+ optionClassName?: string;
23
+ containerClassName?: string;
24
+ disabled?: boolean;
25
+ /**
26
+ * Custom content to render inside the dropdown. Accepts either a React node
27
+ * or a render function. When a function is provided, it will receive a
28
+ * helpers object with a `close()` method that can be used to close the
29
+ * dropdown programmatically (e.g., from an internal button).
30
+ * Backward compatible with previous ReactNode usage.
31
+ */
32
+ dropdownContent?: ReactNode | ((helpers: DropdownRenderHelpers) => ReactNode);
33
+ }
34
+ declare const _default: (props: OptionsDropdownProps) => React.JSX.Element;
35
+ export default _default;
@@ -0,0 +1,50 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { MoreVertical } from "lucide-react";
4
+ import { useEffect, useRef, useState } from "react";
5
+ const OptionsDropdown = ({ options = [], trigger, listClassName = "", optionClassName = "", containerClassName = "", disabled = false, dropdownContent, }) => {
6
+ const [isOpen, setIsOpen] = useState(false);
7
+ const dropdownRef = useRef(null);
8
+ // Close dropdown when clicking outside
9
+ useEffect(() => {
10
+ const handleClickOutside = (event) => {
11
+ if (dropdownRef.current &&
12
+ !dropdownRef.current.contains(event.target)) {
13
+ setIsOpen(false);
14
+ }
15
+ };
16
+ if (isOpen) {
17
+ document.addEventListener("mousedown", handleClickOutside);
18
+ }
19
+ return () => {
20
+ document.removeEventListener("mousedown", handleClickOutside);
21
+ };
22
+ }, [isOpen]);
23
+ const handleToggle = () => {
24
+ if (!disabled) {
25
+ setIsOpen(!isOpen);
26
+ }
27
+ };
28
+ const handleOptionClick = (option) => {
29
+ if (!option.disabled) {
30
+ option.onClick();
31
+ setIsOpen(false);
32
+ }
33
+ };
34
+ const defaultTrigger = (_jsx("button", { className: "p-2 hover:bg-gray-100 rounded-full transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer", onClick: handleToggle, disabled: disabled, type: "button", children: _jsx(MoreVertical, { className: "size-5" }) }));
35
+ return (_jsxs("div", { className: `relative inline-flex items-center ${containerClassName}`, ref: dropdownRef, children: [_jsx("div", { onClick: (e) => {
36
+ e.preventDefault();
37
+ e.stopPropagation();
38
+ handleToggle();
39
+ }, className: "cursor-pointer", children: trigger || defaultTrigger }), isOpen && (_jsx("div", { className: `${listClassName} absolute right-0 top-full mt-2 bg-white border border-gray-100 rounded-xl divide-y divide-gray-100 shadow-lg z-50 min-w-48 overflow-hidden flex flex-col`, children: dropdownContent ? (_jsx("div", { onClick: (e) => {
40
+ // allow interactive content without closing automatically
41
+ e.stopPropagation();
42
+ }, children: typeof dropdownContent === "function"
43
+ ? dropdownContent({ close: () => setIsOpen(false) })
44
+ : dropdownContent })) : (options.map((option) => (_jsxs("button", { onClick: (e) => {
45
+ e.preventDefault();
46
+ e.stopPropagation();
47
+ handleOptionClick(option);
48
+ }, disabled: option.disabled, className: `w-full text-left px-3 py-2.5 cursor-pointer text-xs text-gray-500 hover:text-black hover:bg-gray-100 transition-colors duration-200 flex items-center gap-3 ${optionClassName}`, children: [option.icon ? (_jsx("span", { className: "inline-flex items-center", children: option.icon })) : null, _jsx("span", { children: option.label })] }, option.id)))) }))] }));
49
+ };
50
+ export default OptionsDropdown;
@@ -0,0 +1,9 @@
1
+ import React from "react";
2
+ export interface OtpInputProps {
3
+ length?: number;
4
+ onOtpChange: (otp: string) => void;
5
+ label?: string;
6
+ size?: "sm" | "md" | "lg";
7
+ }
8
+ declare const _default: (props: OtpInputProps) => React.JSX.Element;
9
+ export default _default;
@@ -0,0 +1,54 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useRef, useState } from "react";
4
+ const OtpInput = ({ length = 6, onOtpChange, label = "Enter OTP", size = "md" }) => {
5
+ const [otp, setOtp] = useState(new Array(length).fill(""));
6
+ const inputRefs = useRef(new Array(length).fill(null));
7
+ const getSizeClasses = (s) => {
8
+ switch (s) {
9
+ case "sm":
10
+ return "size-10 md:size-12 sm:text-lg";
11
+ case "md":
12
+ return "size-14 md:size-16 sm:text-xl";
13
+ case "lg":
14
+ return "size-16 md:size-20 sm:text-2xl";
15
+ default:
16
+ return "size-14 md:size-16 sm:text-xl";
17
+ }
18
+ };
19
+ const handleOtpChange = (index, value) => {
20
+ if (value.length <= 1 && /^\d*$/.test(value)) {
21
+ const newOtp = [...otp];
22
+ newOtp[index] = value;
23
+ setOtp(newOtp);
24
+ onOtpChange(newOtp.join(""));
25
+ if (value && index < length - 1) {
26
+ inputRefs.current[index + 1]?.focus();
27
+ }
28
+ }
29
+ };
30
+ const handleKeyDown = (index, e) => {
31
+ if (e.key === "Backspace" && !otp[index] && index > 0) {
32
+ inputRefs.current[index - 1]?.focus();
33
+ }
34
+ };
35
+ const handlePaste = (e) => {
36
+ e.preventDefault();
37
+ const pastedData = e.clipboardData.getData("text");
38
+ const pastedOtp = pastedData.replace(/\D/g, "").slice(0, length);
39
+ if (pastedOtp.length > 0) {
40
+ const newOtp = new Array(length).fill("");
41
+ for (let i = 0; i < pastedOtp.length && i < length; i++) {
42
+ newOtp[i] = pastedOtp[i];
43
+ }
44
+ setOtp(newOtp);
45
+ onOtpChange(newOtp.join(""));
46
+ const nextEmptyIndex = Math.min(pastedOtp.length, length - 1);
47
+ inputRefs.current[nextEmptyIndex]?.focus();
48
+ }
49
+ };
50
+ return (_jsxs("div", { children: [_jsx("label", { className: "block mb-1 font-medium text-xs text-gray-500", children: label }), _jsx("div", { className: "flex gap-2 md:gap-4 mb-4", children: otp.map((digit, index) => (_jsx("input", { ref: (el) => {
51
+ inputRefs.current[index] = el;
52
+ }, id: `otp-${index}`, type: "text", inputMode: "numeric", pattern: "[0-9]*", value: digit, onChange: (e) => handleOtpChange(index, e.target.value), onKeyDown: (e) => handleKeyDown(index, e), onPaste: handlePaste, className: `${getSizeClasses(size)} text-center font-semibold border border-gray-300 rounded-xl focus:border-primaryblue focus:outline-none bg-gray-50 text-base text-black`, maxLength: 1, autoComplete: "off" }, index))) })] }));
53
+ };
54
+ export default OtpInput;
@@ -0,0 +1,18 @@
1
+ import React from "react";
2
+ interface ExistingFile {
3
+ name: string;
4
+ url: string;
5
+ _id: string;
6
+ id: string;
7
+ }
8
+ interface PreviewAreaProps {
9
+ allPreviewUrls: string[];
10
+ currentFiles: File[];
11
+ currentExistingFiles: ExistingFile[];
12
+ currentPreviewUrls: string[];
13
+ uploadedFiles: string[];
14
+ onRemoveFile: (index: number) => void;
15
+ isVideoFile: (file: ExistingFile | File) => boolean;
16
+ }
17
+ declare const PreviewArea: React.FC<PreviewAreaProps>;
18
+ export default PreviewArea;
@@ -0,0 +1,31 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ const PreviewArea = ({ allPreviewUrls, currentFiles, currentExistingFiles, currentPreviewUrls, uploadedFiles, onRemoveFile, isVideoFile, }) => {
4
+ if (allPreviewUrls.length === 0)
5
+ return null;
6
+ const renderSinglePreview = () => {
7
+ const isNewFile = currentFiles.length > 0;
8
+ const file = isNewFile ? currentFiles[0] : currentExistingFiles[0];
9
+ const isVideo = isVideoFile(file);
10
+ const url = isNewFile ? currentPreviewUrls[0] : allPreviewUrls[0];
11
+ return (_jsxs("div", { className: "w-full h-full relative group", children: [isVideo ? (_jsx("video", { src: url, className: "w-full h-full object-cover", controls: true, muted: true })) : (_jsx("img", { src: url, alt: isNewFile ? "Preview" : "File preview", className: "w-full h-full object-cover" })), _jsx("button", { type: "button", onClick: (e) => {
12
+ e.stopPropagation();
13
+ onRemoveFile(0);
14
+ }, className: "absolute top-2 right-2 bg-red-500 hover:bg-red-600 text-white rounded-full w-6 h-6 flex items-center justify-center text-sm font-bold transition-colors opacity-0 hover:opacity-100 group-hover:opacity-100", children: "\u00D7" }), isVideo && (_jsx("div", { className: "absolute top-2 left-2 text-white text-xs bg-black bg-opacity-50 px-2 py-1 rounded", children: "Video" }))] }));
15
+ };
16
+ const renderGridPreview = () => {
17
+ return (_jsx("div", { className: "p-4 h-full", children: _jsx("div", { className: "grid grid-cols-2 gap-3 h-full overflow-y-auto", children: allPreviewUrls.map((url, index) => {
18
+ const isNewFile = currentFiles.length > 0;
19
+ const file = isNewFile ? currentFiles[index] : currentExistingFiles[index];
20
+ const isVideo = isVideoFile(file);
21
+ const fileName = isNewFile ? file?.name || "" : file.name;
22
+ const isUploaded = isNewFile && uploadedFiles?.includes(fileName);
23
+ return (_jsxs("div", { className: "relative group", children: [isVideo ? (_jsx("video", { src: url, className: "w-full h-20 object-cover rounded-lg border border-gray-200", controls: true, muted: true })) : (_jsx("img", { src: url, alt: `Preview ${index + 1}`, className: "w-full h-20 object-cover rounded-lg border border-gray-200" })), isUploaded && (_jsx("div", { className: "absolute top-2 right-2 bg-green-500 text-white text-xs px-2 py-1 rounded", children: "Uploaded" })), _jsx("button", { type: "button", onClick: (e) => {
24
+ e.stopPropagation();
25
+ onRemoveFile(index);
26
+ }, className: "absolute top-1 right-1 bg-red-500 hover:bg-red-600 text-white rounded-full w-5 h-5 flex items-center justify-center text-xs font-bold transition-colors opacity-0 group-hover:opacity-100", children: "\u00D7" }), _jsx("div", { className: "absolute inset-0 bg-black bg-opacity-0 group-hover:bg-opacity-20 transition-all rounded-lg flex items-center justify-center", children: isVideo && (_jsx("div", { className: "text-white text-xs bg-black bg-opacity-50 px-2 py-1 rounded", children: "Video" })) })] }, index));
27
+ }) }) }));
28
+ };
29
+ return (_jsx("div", { className: "flex-1", children: _jsx("div", { className: "h-25 sm:h-[220px] min-h-[160px] border border-gray-200 rounded-xl bg-white overflow-hidden", children: allPreviewUrls.length === 1 ? renderSinglePreview() : renderGridPreview() }) }));
30
+ };
31
+ export default PreviewArea;
@@ -0,0 +1,16 @@
1
+ import React from "react";
2
+ export interface SelectionOption {
3
+ id: string;
4
+ title: string;
5
+ subtitle?: string;
6
+ icon?: React.ReactNode;
7
+ }
8
+ export interface SelectionListProps {
9
+ label?: string;
10
+ options: SelectionOption[];
11
+ selectedValue: string;
12
+ onSelectionChange: (value: string) => void;
13
+ className?: string;
14
+ }
15
+ declare const _default: (props: SelectionListProps) => React.JSX.Element;
16
+ export default _default;
@@ -0,0 +1,9 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ const RadioIcon = ({ selected }) => (_jsx("span", { className: `ml-4 size-4 flex rounded-full border-1 p-[2.5px] ${selected ? "border-blue-500" : "border-gray-300 bg-gray-50"}`, children: selected ? (_jsx("span", { className: "block size-full rounded-full bg-blue-500" })) : null }));
4
+ const SelectionList = ({ label, options, selectedValue, onSelectionChange, className = "", }) => {
5
+ return (_jsx("div", { className: `space-y-6 ${className}`, children: _jsxs("div", { className: "", children: [label && (_jsx("div", { className: "p-2 pt-0 rounded-full text-xs text-gray-500", children: _jsx("div", { children: label }) })), _jsx("div", { className: "space-y-3", children: options.map((option) => (_jsxs("label", { className: `relative flex items-center p-4 border rounded-xl cursor-pointer transition-colors ${selectedValue === option.id
6
+ ? "border-blue-500 bg-blue-50"
7
+ : "border-gray-200 bg-gray-50 hover:bg-gray-100"}`, onClick: () => onSelectionChange(option.id), children: [_jsx("input", { type: "radio", value: option.id, checked: selectedValue === option.id, onChange: () => onSelectionChange(option.id), className: "absolute opacity-0 w-full h-full cursor-pointer" }), _jsxs("div", { className: "flex items-center gap-3 flex-1", children: [option.icon && (_jsx("span", { className: "inline-block p-2 bg-blue-100 rounded-full", children: option.icon })), _jsxs("div", { className: "flex-1", children: [_jsx("p", { className: "font-semibold text-sm text-gray-900", children: option.title }), option.subtitle && (_jsx("p", { className: "text-[11px] text-gray-500", children: option.subtitle }))] }), _jsx(RadioIcon, { selected: selectedValue === option.id })] })] }, option.id))) })] }) }));
8
+ };
9
+ export default SelectionList;
@@ -0,0 +1,10 @@
1
+ import React from "react";
2
+ export interface SquareCheckboxProps {
3
+ label: string;
4
+ checked: boolean;
5
+ onChange: (checked: boolean) => void;
6
+ disabled?: boolean;
7
+ className?: string;
8
+ }
9
+ declare const _default: (props: SquareCheckboxProps) => React.JSX.Element;
10
+ export default _default;
@@ -0,0 +1,5 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ const SquareCheckbox = ({ label, checked, onChange, disabled = false, className, }) => {
3
+ return (_jsx("div", { className: `inline-flex items-center gap-2 select-none ${disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer"} ${className ?? ""}`, children: _jsxs("label", { className: "relative inline-flex items-center gap-2", children: [_jsx("input", { type: "checkbox", checked: checked, disabled: disabled, onChange: (e) => onChange(e.target.checked), className: "sr-only", autoComplete: "off" }), _jsx("div", { className: `w-10 h-10 rounded-lg border flex items-center justify-center transition-colors ${checked ? "bg-black border-black" : "bg-gray-50 border-gray-300"}`, children: checked && (_jsx("svg", { className: "w-4.5 h-4.5 text-white", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M20 6L9 17l-5-5" }) })) }), _jsx("span", { className: "text-xs text-gray-700", children: label })] }) }));
4
+ };
5
+ export default SquareCheckbox;
@@ -0,0 +1,19 @@
1
+ import React from "react";
2
+ interface Tab {
3
+ id: string;
4
+ label: string;
5
+ }
6
+ interface TabNavigationProps {
7
+ tabs: Tab[];
8
+ activeTab: string;
9
+ onTabChange: (tabId: string) => void;
10
+ selectedFiles: Record<string, File[]>;
11
+ existingFilesByTab: Record<string, {
12
+ name: string;
13
+ url: string;
14
+ _id: string;
15
+ id: string;
16
+ }[]>;
17
+ }
18
+ declare const TabNavigation: React.FC<TabNavigationProps>;
19
+ export default TabNavigation;
@@ -0,0 +1,11 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ const TabNavigation = ({ tabs, activeTab, onTabChange, selectedFiles, existingFilesByTab, }) => {
4
+ return (_jsx("div", { className: "flex gap-4 mb-4 overflow-x-auto scrollbar-hide pb-2", children: tabs.map((tab) => (_jsxs("button", { type: "button", onClick: () => onTabChange(tab.id), className: `px-3 py-1 text-[11px] rounded-lg transition-colors cursor-pointer flex items-center flex-shrink-0 whitespace-nowrap ${activeTab === tab.id
5
+ ? "bg-blue-600 text-white"
6
+ : "bg-blue-50 text-blue-600 hover:bg-blue-200"} ${(selectedFiles[tab.id]?.length > 0 || existingFilesByTab[tab.id]?.length > 0) &&
7
+ activeTab === tab.id
8
+ ? "ring-2 ring-blue-300"
9
+ : ""}`, children: [tab.label, (selectedFiles[tab.id]?.length > 0 || existingFilesByTab[tab.id]?.length > 0) && (_jsx("span", { className: `ml-2 text-xs ${activeTab === tab.id ? "bg-white" : "bg-primaryblue"} size-1.5 rounded-full inline-block` }))] }, tab.id))) }));
10
+ };
11
+ export default TabNavigation;
@@ -0,0 +1,10 @@
1
+ import React from "react";
2
+ export interface TextareaProps extends Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, "className"> {
3
+ label?: string;
4
+ error?: string;
5
+ className?: string;
6
+ }
7
+ declare const _default: (props: TextareaProps & {
8
+ ref?: React.Ref<HTMLTextAreaElement>;
9
+ }) => React.JSX.Element;
10
+ export default _default;
@@ -0,0 +1,8 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { forwardRef } from "react";
4
+ const Textarea = forwardRef(({ label, name, placeholder, required = false, error, disabled = false, className = "", rows = 4, ...props }, ref) => {
5
+ return (_jsxs("div", { className: `w-full text-sm ${className}`, children: [label && (_jsx("label", { htmlFor: name, className: "block mb-2 text-xs text-gray-500", children: label })), _jsx("div", { className: "relative", children: _jsx("textarea", { ref: ref, id: name, name: name, placeholder: placeholder, required: required, disabled: disabled, rows: rows, autoComplete: "off", className: `w-full h-25 sm:h-auto text-base sm:text-xs p-3 sm:p-4 border border-gray-200 rounded-xl bg-gray-50 focus:border-gray-300 focus:bg-white outline-none placeholder-gray-400 resize-vertical min-h-[160px] ${error ? "border-red-500" : "border-gray-200"} text-black`, ...props }) }), error && _jsx("p", { className: "mt-1 text-sm text-red-500", children: error })] }));
6
+ });
7
+ Textarea.displayName = "Textarea";
8
+ export default Textarea;
@@ -0,0 +1,10 @@
1
+ import React from "react";
2
+ export interface ToggleProps {
3
+ checked: boolean;
4
+ onChange: (checked: boolean) => void;
5
+ label?: string;
6
+ disabled?: boolean;
7
+ className?: string;
8
+ }
9
+ declare const _default: (props: ToggleProps) => React.JSX.Element;
10
+ export default _default;
@@ -0,0 +1,11 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ const Toggle = ({ checked, onChange, label, disabled = false, className = "", }) => {
4
+ const handleClick = () => {
5
+ if (disabled)
6
+ return;
7
+ onChange(!checked);
8
+ };
9
+ return (_jsxs("div", { className: `flex items-center gap-3 ${className}`, children: [_jsx("button", { type: "button", role: "switch", "aria-checked": checked, onClick: handleClick, disabled: disabled, className: `relative inline-flex h-5 w-9 items-center rounded-full transition-colors focus:outline-none border border-gray-200 ${checked ? "bg-primaryblue" : "bg-gray-200"} ${disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer"}`, children: _jsx("span", { className: `inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${checked ? "translate-x-4" : "translate-x-1"}` }) }), label && _jsx("span", { className: "text-xs text-black", children: label })] }));
10
+ };
11
+ export default Toggle;
@@ -0,0 +1,28 @@
1
+ import React from "react";
2
+ import { type ExistingFile } from "../../hooks/useFileUpload";
3
+ export interface UploadRef extends HTMLInputElement {
4
+ markFilesAsUploaded: (tabId: string, fileNames: string[]) => void;
5
+ getUnuploadedFiles: (tabId: string) => File[];
6
+ getAllSelectedFiles: () => Record<string, File[]>;
7
+ getUploadedFiles: () => Record<string, string[]>;
8
+ getFileChanges: () => {
9
+ newFiles: Record<string, File[]>;
10
+ existingFiles: Record<string, ExistingFile[]>;
11
+ removedFiles: Record<string, ExistingFile[]>;
12
+ };
13
+ }
14
+ export interface UploadProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "className" | "type"> {
15
+ label?: string;
16
+ error?: string;
17
+ className?: string;
18
+ onFileSelect?: (files: FileList | null, tabId?: string) => void;
19
+ tabs?: {
20
+ id: string;
21
+ label: string;
22
+ }[];
23
+ uploadedFiles?: Record<string, string[]>;
24
+ onUploadStatusChange?: (tabId: string, uploadedFileNames: string[]) => void;
25
+ existingFiles?: ExistingFile[];
26
+ }
27
+ declare const _default: (props: UploadProps) => React.JSX.Element;
28
+ export default _default;