first commit

This commit is contained in:
2025-06-10 15:28:23 +05:30
commit e22a2dc275
699 changed files with 100382 additions and 0 deletions

View File

@@ -0,0 +1,35 @@
import React from "react";
import { EyeDroperIcon } from "../../icons/ExportCommonIcons";
interface EyeDropInputProps {
label: string;
value: string;
onChange: (value: string) => void;
}
const EyeDropInput: React.FC<EyeDropInputProps> = ({
label = "Object",
value,
onChange,
}) => {
const handleEyeDropClick = () => {
const simulatedValue = "picked_value"; // Replace with actual eye dropper logic
onChange(simulatedValue);
};
return (
<div className="eye-dropper-input-container">
<div className="label">{label}</div>
<div className="input-container">
<input disabled type="text" value={value}/>
{/* <input type="text" value={activeValue ?? "null"} disabled /> */}
{/* <input type="button" value="Clear" onClick={handleEyeDropClick}/> */}
<button id="eye-picker" className="eye-picker-button" onClick={handleEyeDropClick}>
<EyeDroperIcon isActive={false} />
</button>
</div>
</div>
);
};
export default EyeDropInput;

View File

@@ -0,0 +1,93 @@
import React, { useEffect, useState } from "react";
import * as CONSTANTS from "../../../types/world/worldConstants";
interface InputToggleProps {
label: string; // Represents the toggle state (on/off)
min?: number;
max?: number;
onClick?: () => void; // Function to handle toggle clicks
onChange?: (value: number) => void; // Function to handle toggle clicks
disabled?: boolean;
value?: number;
onPointerUp?: (value: number) => void;
}
const InputRange: React.FC<InputToggleProps> = ({
label,
onClick,
onChange,
min,
max,
disabled,
value,
onPointerUp,
}) => {
const [rangeValue, setRangeValue] = useState<number>(value ? value : 5);
function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
const newValue = parseInt(e.target.value); // Parse the value to an integer
setRangeValue(newValue); // Update the local state
if (onChange) {
onChange(newValue); // Call the onChange function if it exists
}
}
useEffect(() => {
value && setRangeValue(value);
}, [value]);
function handlePointerUp(e: React.PointerEvent<HTMLInputElement>) {
const newValue = parseInt(e.currentTarget.value, 10); // Parse value correctly
if (onPointerUp) {
onPointerUp(newValue); // Call the callback function if it exists
}
}
function handlekey(e: React.KeyboardEvent<HTMLInputElement>) {
const newValue = parseInt(e.currentTarget.value, 10); // Parse value correctly
if (onPointerUp) {
onPointerUp(newValue); // Call the callback function if it exists
}
}
return (
<div className="input-range-container">
<label
htmlFor={`range-input ${value}`}
className="label"
onClick={onClick}
>
{label}
</label>
<div className="input-container">
<input
id={`range-input ${value}`}
type="range"
min={min}
max={max}
onChange={handleChange}
disabled={disabled}
value={rangeValue}
onPointerUp={handlePointerUp}
/>
<input
type="number"
min={min}
className="input-value"
max={max}
value={rangeValue}
onChange={handleChange}
disabled={disabled}
onKeyUp={(e) => {
if (e.key === "ArrowUp" || e.key === "ArrowDown") {
console.log("e.key: ", e.key);
handlekey(e);
}
}}
/>
</div>
</div>
);
};
export default InputRange;

View File

@@ -0,0 +1,55 @@
import React from "react";
interface InputToggleProps {
label: string; // Represents the toggle state (on/off)
onClick?: () => void; // Function to handle toggle clicks
value?: boolean;
inputKey: string;
}
// Update InputToggle.tsx to be fully controlled
const InputToggle: React.FC<InputToggleProps> = ({
label,
onClick,
value = false,
inputKey,
}) => {
// Remove internal state and use the value prop directly
function handleOnClick() {
if (onClick) onClick();
}
return (
<div className="input-toggle-container">
<label htmlFor={`toogle-input-${inputKey}`} className="label">
{label}
</label>
<button
id="check-box"
className={"check-box"}
onClick={handleOnClick}
style={{
background: value ? "var(--background-color-accent)" : "",
outline: value ? "" : "1px solid var(--border-color)",
}}
>
<div
className="check-box-style"
style={{
left: value ? "16px" : "2px",
background: value ? "" : "var(--text-disabled)",
}}
></div>
<input
type="checkbox"
name=""
id={`toogle-input-${inputKey}`}
checked={value}
readOnly
/>
</button>
</div>
);
};
export default InputToggle;

View File

@@ -0,0 +1,91 @@
import React, { useState } from "react";
import RenameInput from "./RenameInput";
type InputWithDropDownProps = {
label: string;
value: string;
min?: number;
max?: number;
step?: number;
defaultValue?: string;
options?: string[]; // Array of dropdown options
activeOption?: string; // The currently active dropdown option
onClick?: () => void;
onChange: (newValue: string) => void;
editableLabel?: boolean;
placeholder?: string; // New placeholder prop
};
const InputWithDropDown: React.FC<InputWithDropDownProps> = ({
label,
value,
min,
max,
step,
defaultValue,
options,
activeOption,
onClick,
onChange,
editableLabel = false,
placeholder = "Inherit", // Default empty placeholder
}) => {
const separatedWords = label
.split(/(?=[A-Z])/)
.map((word) => word.trim())
.toString();
const [openDropdown, setOpenDropdown] = useState(false);
return (
<div className="value-field-container">
{editableLabel ? (
<RenameInput value={label} />
) : (
<label htmlFor={separatedWords} className="label">
{label}
</label>
)}
<div className="input default" id={separatedWords}>
<input
min={min}
max={max}
step={step}
type="number"
defaultValue={value}
// value={value}
onChange={(e) => {
onChange(e.target.value);
}}
placeholder={placeholder} // Added placeholder prop
/>
{activeOption && (
<div
className="dropdown"
onClick={() => {
setOpenDropdown(true);
}}
>
<div className="active-option">{activeOption}</div>
{options && openDropdown && (
<div className="dropdown-options-list">
{options.map((option, index) => (
<div
key={index}
className={"dropdown-option"}
onClick={onClick}
>
{option}
</div>
))}
</div>
)}
</div>
)}
</div>
</div>
);
};
export default InputWithDropDown;

View File

@@ -0,0 +1,31 @@
import React from "react";
interface LabeledButtonProps {
label: string; // Label for the button
onClick?: () => void; // Function to call when the button is clicked
disabled?: boolean; // Optional prop to disable the button
value?: string;
}
const LabeledButton: React.FC<LabeledButtonProps> = ({
label,
onClick,
disabled = false,
value = "Click here",
}) => {
return (
<div className="labeled-button-container">
<div className="label">{label}</div>
<button
id="label-button"
className="button"
onClick={onClick}
disabled={disabled}
>
{value}
</button>
</div>
);
};
export default LabeledButton;

View File

@@ -0,0 +1,49 @@
import React, { useState, useEffect } from "react";
import RegularDropDown from "./RegularDropDown";
type LabledDropdownProps = {
defaultOption: string; // Initial active option
options: string[]; // Array of dropdown options
label?: string; // Customizable label text
onSelect?: (option: string) => void; // Callback when option is selected
className?: string; // Additional className for styling
disabled?: boolean; // Disable dropdown
search?: boolean; // Enable/disable search functionality
};
const LabledDropdown: React.FC<LabledDropdownProps> = ({
defaultOption,
options,
label = "Type",
onSelect,
className = "",
search = false
}) => {
const [activeOption, setActiveOption] = useState(defaultOption);
// Update active option if defaultOption changes
useEffect(() => {
setActiveOption(defaultOption);
}, [defaultOption]);
const handleSelect = (option: string) => {
setActiveOption(option);
if (onSelect) {
onSelect(option);
}
};
return (
<div className={`value-field-container ${className}`}>
<div className="label">{label}</div>
<RegularDropDown
header={activeOption}
options={options}
onSelect={handleSelect}
search={search}
/>
</div>
);
};
export default LabledDropdown;

View File

@@ -0,0 +1,74 @@
import React, { useState } from "react";
const MultiEmailInvite: React.FC = () => {
const [emails, setEmails] = useState<string[]>([]);
const [inputValue, setInputValue] = useState("");
const handleAddEmail = () => {
const trimmedEmail = inputValue.trim();
// Validate email
if (!trimmedEmail || !validateEmail(trimmedEmail)) {
alert("Please enter a valid email address.");
return;
}
// Check for duplicates
if (emails.includes(trimmedEmail)) {
alert("This email has already been added.");
return;
}
// Add email to the list
setEmails([...emails, trimmedEmail]);
setInputValue(""); // Clear the input field after adding
};
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Enter" || e.key === ",") {
e.preventDefault();
handleAddEmail();
}
};
const handleRemoveEmail = (emailToRemove: string) => {
setEmails(emails.filter((email) => email !== emailToRemove));
};
const validateEmail = (email: string) => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
};
const [inputFocus, setInputFocus] = useState(false);
return (
<div className="multi-email-invite-input-container">
<div className={`multi-email-invite-input${inputFocus ? " active" : ""}`}>
{emails.map((email, index) => (
<div key={index} className="entered-emails">
{email}
<span onClick={() => handleRemoveEmail(email)}>&times;</span>
</div>
))}
<input
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
onFocus={() => setInputFocus(true)}
onBlur={() => setInputFocus(false)}
onKeyDown={handleKeyDown}
placeholder="Enter email and press Enter or comma to seperate"
/>
</div>
<div onClick={handleAddEmail} className="invite-button">
Invite
</div>
<div className="users-list-container">
{/* list available users */}
</div>
</div>
);
};
export default MultiEmailInvite;

View File

@@ -0,0 +1,176 @@
import React, { useState, useRef, useEffect } from "react";
import { ArrowIcon } from "../../icons/ExportCommonIcons";
// Dropdown Item Component
const DropdownItem = ({
label,
onClick,
disabled = false,
}: {
label: string;
onClick: () => void;
disabled?: boolean;
}) => (
<div
className={`dropdown-item ${disabled ? "disabled" : ""}`}
onClick={() => {
if (!disabled) onClick();
}}
style={{
cursor: disabled ? "not-allowed" : "default",
opacity: disabled ? 0.5 : 1,
}}
>
{label}
</div>
);
// Nested Dropdown Component
const NestedDropdown = ({
label,
fields,
onSelect,
disabledFields = [],
}: {
label: string;
fields: string[];
onSelect: (selectedData: { name: string; fields: string }) => void;
disabledFields?: string[];
}) => {
const [open, setOpen] = useState(false);
return (
<div className="nested-dropdown">
<div
className={`dropdown-trigger ${open ? "open" : ""}`}
onClick={() => setOpen(!open)}
>
{label}
<div
className="arrow-container"
style={{ rotate: open ? "" : "-90deg" }}
>
<ArrowIcon />
</div>
</div>
{open && (
<div className="submenu">
{fields.map((field) => {
const isDisabled = disabledFields.includes(`${label}-${field}`);
return (
<DropdownItem
key={field}
label={field}
onClick={() => onSelect({ name: label, fields: field })}
disabled={isDisabled}
/>
);
})}
</div>
)}
</div>
);
};
// Props type for MultiLevelDropdown
interface MultiLevelDropdownProps {
data: Record<string, any>;
onSelect: (selectedData: { name: string; fields: string }) => void;
onUnselect: () => void;
selectedValue?: { name: string; fields: string };
allSelections?: Record<string, { name: string; fields: string }>;
isLoading?: boolean;
}
// Main Multi-Level Dropdown Component
const MultiLevelDropdown = ({
data,
onSelect,
onUnselect,
selectedValue,
allSelections = {},
isLoading = false,
}: MultiLevelDropdownProps) => {
const [open, setOpen] = useState(false);
const dropdownRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (
dropdownRef.current &&
!dropdownRef.current.contains(event.target as Node)
) {
setOpen(false);
}
};
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, []);
// Handle item selection
const handleItemSelect = (selectedData: { name: string; fields: string }) => {
onSelect(selectedData);
setOpen(false);
};
// Handle unselect
const handleItemUnselect = () => {
onUnselect();
setOpen(false);
};
// Determine the display label
const displayLabel = selectedValue
? `${selectedValue.name} - ${selectedValue.fields}`
: "Dropdown trigger";
// Build list of disabled selections
const disabledFieldsList = Object.values(allSelections)
.filter(
(sel) =>
!(
sel.name === selectedValue?.name &&
sel.fields === selectedValue?.fields
)
)
.map((sel) => `${sel.name}-${sel.fields}`);
return (
<div className="multi-level-dropdown" ref={dropdownRef}>
<button
id="multi-level-drop"
className={`dropdown-button ${open ? "open" : ""}`}
onClick={() => setOpen(!open)}
>
<div className="label">{displayLabel}</div>
<span className="icon"></span>
</button>
{open && (
<div className="dropdown-menu">
<div className="dropdown-content">
{isLoading ? (
<div className="loading" />
) : (
<>
<DropdownItem label="Unselect" onClick={handleItemUnselect} />
{Object.entries(data).map(([key, value]) => (
<NestedDropdown
key={key}
label={key}
fields={Object.keys(value)}
onSelect={handleItemSelect}
disabledFields={disabledFieldsList}
/>
))}
</>
)}
</div>
</div>
)}
</div>
);
};
export default MultiLevelDropdown;

View File

@@ -0,0 +1,75 @@
import React, { useState } from "react";
import { ArrowIcon } from "../../icons/ExportCommonIcons";
import LabledDropdown from "./LabledDropdown";
interface PreviewSelectionWithUploadProps {
preview?: boolean;
upload?: boolean;
label?: string;
onSelect: (option: string) => void;
defaultOption: string;
options: string[];
}
const PreviewSelectionWithUpload: React.FC<PreviewSelectionWithUploadProps> = ({
preview = false,
upload = false,
onSelect,
label,
defaultOption,
options,
}) => {
const [showPreview, setShowPreview] = useState(false);
return (
<div className="preview-selection-with-upload-wrapper">
{preview && (
<>
<button
id="preview-selection-button"
className="input-header-container"
onClick={() => setShowPreview(!showPreview)}
>
<div className="input-header">Preview</div>
<div
className="arrow-container"
style={{ rotate: showPreview ? "0deg" : "90deg" }}
>
<ArrowIcon />
</div>
</button>
{showPreview && (
<div className="canvas-wrapper">
<div className="canvas-container"></div>
</div>
)}
</>
)}
{upload && (
<div className="asset-selection-container">
<div className="upload-custom-asset-button">
<div className="title">Upload Product</div>
<input
type="file"
accept=".glb, .gltf"
id="simulation-product-upload"
/>
<label
className="upload-button"
htmlFor="simulation-product-upload"
>
Upload here
</label>
</div>
</div>
)}
<LabledDropdown
label={label}
defaultOption={defaultOption}
options={options}
onSelect={onSelect}
/>
</div>
);
};
export default PreviewSelectionWithUpload;

View File

@@ -0,0 +1,127 @@
import React, { useState, useEffect, useRef } from "react";
interface DropdownProps {
header: string;
options: string[];
onSelect: (option: string) => void;
search?: boolean;
onClick?: () => void;
onChange?: () => void;
}
const RegularDropDown: React.FC<DropdownProps> = ({
header,
options,
onSelect,
search = true,
onClick,
onChange,
}) => {
const [isOpen, setIsOpen] = useState(false);
const [selectedOption, setSelectedOption] = useState<string | null>(null);
const [searchTerm, setSearchTerm] = useState(""); // State to store search term
const [filteredOptions, setFilteredOptions] = useState<string[]>(options); // State for filtered options
const dropdownRef = useRef<HTMLDivElement>(null); // Ref for the dropdown container
// Reset selectedOption when the dropdown closes
useEffect(() => {
if (!isOpen) {
setSelectedOption(null);
setSearchTerm(""); // Clear the search term when the dropdown closes
setFilteredOptions(options); // Reset filtered options when the dropdown closes
}
}, [isOpen, options]);
// Reset selectedOption when the header prop changes
useEffect(() => {
setSelectedOption(null);
setSearchTerm(""); // Reset search term if header changes
setFilteredOptions(options); // Reset options if header changes
}, [header, options]);
// Close dropdown if clicked outside
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (
dropdownRef.current &&
!dropdownRef.current.contains(event.target as Node)
) {
setIsOpen(false);
}
};
document.addEventListener("click", handleClickOutside);
return () => {
document.removeEventListener("click", handleClickOutside);
};
}, []);
// Toggle the dropdown
const toggleDropdown = () => {
setIsOpen((prev) => !prev);
};
// Handle option selection
const handleOptionClick = (option: string) => {
setSelectedOption(option);
onSelect(option);
setIsOpen(false);
};
// Handle search input change
const handleSearchChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const term = event.target.value;
setSearchTerm(term);
// Filter options based on the search term
const filtered = options.filter((option) =>
option.toLowerCase().includes(term.toLowerCase())
);
setFilteredOptions(filtered);
};
return (
<div className="regularDropdown-container" ref={dropdownRef}>
{/* Dropdown Header */}
<div className="dropdown-header flex-sb" onClick={toggleDropdown}>
<div className="key">{selectedOption || header}</div>
<div className="icon"></div>
</div>
{/* Dropdown Options */}
{isOpen && (
<div className="dropdown-options">
{/* Search Bar */}
{search && (
<div className="dropdown-search">
<input
type="text"
placeholder="Search..."
value={searchTerm}
onChange={handleSearchChange}
/>
</div>
)}
{/* Filtered Options */}
{filteredOptions.length > 0 ? (
filteredOptions.map((option, index) => (
<div
className="option"
key={index}
onClick={() => handleOptionClick(option)}
>
{option}
</div>
))
) : (
<div className="no-options">No options found</div>
)}
</div>
)}
</div>
);
};
export default RegularDropDown;

View File

@@ -0,0 +1,80 @@
import React, { useState, useRef, useEffect } from "react";
// interface RenameInputProps {
// value: string;
// onRename?: (newText: string) => void;
// }
interface RenameInputProps {
value: string;
onRename?: (newText: string) => void;
checkDuplicate?: (name: string) => boolean;
}
const RenameInput: React.FC<RenameInputProps> = ({ value, onRename, checkDuplicate }) => {
const [isEditing, setIsEditing] = useState(false);
const [text, setText] = useState(value);
const [isDuplicate, setIsDuplicate] = useState(false);
const inputRef = useRef<HTMLInputElement | null>(null);
useEffect(() => {
setText(value);
}, [value]);
useEffect(() => {
if (checkDuplicate) {
setIsDuplicate(checkDuplicate(text));
}
}, [text, checkDuplicate]);
const handleDoubleClick = () => {
setIsEditing(true);
setTimeout(() => inputRef.current?.focus(), 0);
};
const handleBlur = () => {
if(isDuplicate) return
setIsEditing(false);
if (onRename && !isDuplicate) {
onRename(text);
}
};
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setText(e.target.value);
};
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Enter" && !isDuplicate) {
setIsEditing(false);
if (onRename) {
onRename(text);
}
}
};
return (
<>
{isEditing ? (
<>
<input
ref={inputRef}
type="text"
value={text}
onChange={handleChange}
onBlur={handleBlur}
onKeyDown={handleKeyDown}
className={`rename-input ${isDuplicate ? "input-error" : ""}`}
/>
{/* {isDuplicate && <div className="error-msg">Name already exists!</div>} */}
</>
) : (
<span onDoubleClick={handleDoubleClick} className="input-value">
{text}
</span>
)}
</>
);
};
export default RenameInput

View File

@@ -0,0 +1,71 @@
import React, { ChangeEvent, useState } from "react";
import { CloseIcon, SearchIcon } from "../../icons/ExportCommonIcons";
interface SearchProps {
value?: string; // The current value of the search input
placeholder?: string; // Placeholder text for the input
onChange: (value: string) => void; // Callback function to handle input changes
}
const Search: React.FC<SearchProps> = ({
value = "",
placeholder = "Search",
onChange,
}) => {
// State to track the input value and focus status
const [inputValue, setInputValue] = useState(value);
const [isFocused, setIsFocused] = useState(false);
const handleInputChange = (event: ChangeEvent<HTMLInputElement>) => {
const newValue = event.target.value;
setInputValue(newValue);
onChange(newValue); // Call the onChange prop with the new value
};
const handleClear = () => {
setInputValue("");
onChange(""); // Clear the input value
};
const handleFocus = () => {
setIsFocused(true); // Set focus state to true
};
const handleBlur = () => {
setIsFocused(false); // Set focus state to false
};
return (
<div className="search-wrapper">
<div
className={`search-container ${
isFocused || inputValue ? "active" : ""
}`}
>
<div className="icon-container">
<SearchIcon />
</div>
<input
type="text"
className="search-input"
value={inputValue}
placeholder={placeholder}
onChange={handleInputChange}
onFocus={handleFocus}
onBlur={handleBlur}
/>
{inputValue && (
<button
id="clear-button"
className="clear-button"
onClick={handleClear}
>
<CloseIcon />
</button>
)}
</div>
</div>
);
};
export default Search;

View File

@@ -0,0 +1,31 @@
import React from "react";
interface ToggleHeaderProps {
options: string[]; // Array of strings representing the options
activeOption: string; // The currently active option
handleClick: (option: string) => void; // Function to handle click events
}
const ToggleHeader: React.FC<ToggleHeaderProps> = ({
options,
activeOption,
handleClick,
}) => {
return (
<div className="toggle-header-container">
{options.map((option, index) => (
<button
key={`${index}-${option}`}
className={`toggle-header-item ${
option === activeOption ? "active" : ""
}`}
onClick={() => handleClick(option)} // Call handleClick when an option is clicked
>
{option}
</button>
))}
</div>
);
};
export default ToggleHeader;