Input Components
Explore different input types and features—like dropdowns, tags, editable text, and more
import { useState } from "react";
import { HvInput, HvOption, HvSelect } from "@hitachivantara/uikit-react-core";
export default function Demo() {
const [selectedCountry, setSelectedCountry] = useState("Portugal");
const [formattedPhoneNumber, setFormattedPhoneNumber] = useState("");
return (
<HvInput
label="Dropdown prefix"
className="w-300px"
onChange={(_, value) => {
setFormattedPhoneNumber(
countries.find((c) => c.label === selectedCountry)?.format(value) ||
"",
);
}}
value={formattedPhoneNumber}
startAdornment={
<HvSelect
value={selectedCountry}
variant="secondaryGhost"
classes={{
root: "w-100px! border-r-1! border-r-border! rounded-none!",
select: "font-normal border-none! rounded-none!",
}}
onChange={(evt, val) => {
setSelectedCountry(val || "");
}}
enablePortal
>
{countries.map((country) => (
<HvOption key={country.label} value={country.label}>
{country.flag} {country.code}
</HvOption>
))}
</HvSelect>
}
/>
);
}
function format(value: string) {
// Apply formatting pattern: "XX XXX XX XX"
return value
.replace(/\D/g, "")
.slice(0, 9)
.replace(/(\d{2})(\d{3})?(\d{2})?(\d{2})?/, (match, p1, p2, p3, p4) => {
return [p1, p2, p3, p4].filter(Boolean).join(" ");
});
}
const countries = [
{ flag: "🇮🇳", format, code: "+91", label: "India" },
{ flag: "🇵🇹", format, code: "+351", label: "Portugal" },
{ flag: "🇬🇧", format, code: "+44", label: "United Kingdom" },
{ flag: "🇺🇸", format, code: "+1", label: "United States" },
];
import { useRef, useState } from "react";
import { HvAdornment, HvInput } from "@hitachivantara/uikit-react-core";
import { Copy, Success } from "@hitachivantara/uikit-react-icons";
export default function Demo() {
const [copied, setCopied] = useState(false);
const inputRef = useRef<HTMLInputElement>(null);
const handleCopy = () => {
navigator.clipboard.writeText(inputRef.current?.value || "");
setCopied(true);
setTimeout(() => {
setCopied(false);
}, 1000);
};
return (
<HvInput
label="Copy to clipboard"
ref={inputRef}
className="w-300px"
defaultValue="Copy this text"
endAdornment={
<HvAdornment
icon={
<>
<Success
color="positive"
className={`transition-all ${copied ? "scale-100 opacity-100" : "scale-0 opacity-0"}`}
/>
<Copy
className={`absolute transition-all ${copied ? "scale-0 opacity-0" : "scale-100 opacity-100"}`}
/>
</>
}
onClick={handleCopy}
/>
}
/>
);
}
Select colors
import { useRef, useState } from "react";
import { flushSync } from "react-dom";
import {
HvBaseDropdown,
HvLabel,
HvPanel,
HvTag,
} from "@hitachivantara/uikit-react-core";
const colors = [
"Blue",
"Red",
"Green",
"Yellow",
"Purple",
"White",
"Black",
"Orange",
"Pink",
"Brown",
"Gray",
"Cyan",
"Magenta",
"Lime",
"Teal",
"Lavender",
];
export default function Demo() {
const containerRef = useRef<HTMLDivElement>(null);
const focusTarget = useRef<HTMLDivElement>(null);
const [selectedColors, setSelectedColors] = useState<string[]>([]);
const scrollToEnd = () => {
containerRef.current?.scrollTo({
left: containerRef.current.scrollWidth,
behavior: "smooth",
});
};
const handleAddColor = (color: string) => {
flushSync(() => {
setSelectedColors((prev) => [...prev.filter((c) => c !== color), color]);
});
scrollToEnd();
};
const handleRemoveColor = (color: string) => {
flushSync(() => {
setSelectedColors((prev) => prev.filter((c) => c !== color));
});
scrollToEnd();
};
return (
<div className="w-300px">
<HvLabel
label="Tags dropdown input"
id="tags-dropdown-input"
showGutter
/>
<HvBaseDropdown
aria-labelledby="tags-dropdown-input"
onContainerCreation={() => focusTarget.current?.focus()}
placeholder={
selectedColors.length ? (
<div
className="flex gap-xs overflow-scroll px-1px h-full items-center"
ref={containerRef}
>
{selectedColors.map((color) => (
<HvTag
key={color}
label={color}
onDelete={() => handleRemoveColor(color)}
/>
))}
</div>
) : (
"Select colors"
)
}
>
<div ref={focusTarget} tabIndex={-1} />
<HvPanel className="flex gap-xs flex-wrap">
{colors
.filter((color) => !selectedColors.includes(color))
.map((color) => (
<HvTag
key={color}
label={color}
onClick={() => handleAddColor(color)}
/>
))}
</HvPanel>
</HvBaseDropdown>
</div>
);
}
import { useEffect, useRef, useState } from "react";
import { ClickAwayListener, Popper } from "@mui/base";
import {
HvAdornment,
HvPanel,
HvTag,
HvTagsInput,
HvTypography,
} from "@hitachivantara/uikit-react-core";
import { DropDownXS } from "@hitachivantara/uikit-react-icons";
const colors = [
"Blue",
"Red",
"Green",
"Yellow",
"Purple",
"White",
"Black",
"Orange",
"Pink",
"Brown",
"Gray",
"Cyan",
"Magenta",
"Lime",
"Teal",
"Lavender",
];
const lastUsed = ["Blue", "Red", "Green"];
export default function Demo() {
const containerRef = useRef<HTMLDivElement>(null);
const [selectedColors, setSelectedColors] = useState<string[]>([]);
const [open, setOpen] = useState(false);
useEffect(() => {
if (containerRef.current) {
containerRef.current.scrollLeft = containerRef.current.scrollWidth;
}
}, [selectedColors]);
const handleAddColor = (color: string) => {
setSelectedColors((prev) => {
const newColors = prev.filter((c) => c !== color);
return newColors.length ? [...newColors, color] : [color];
});
};
const handleRemoveColor = (color: string) => {
setSelectedColors((prev) => prev.filter((c) => c !== color));
};
return (
<div className="w-300px">
<HvTagsInput
label="Tags with suggestions"
ref={containerRef}
onChange={(event, value) => {
setSelectedColors(
value.map((v) => (typeof v === "string" ? v : (v.label as string))),
);
}}
onDelete={(_, value) => {
handleRemoveColor(value as string);
}}
value={selectedColors}
commitTagOn={["Comma", "Enter"]}
endAdornment={
<HvAdornment
tabIndex={0}
icon={<DropDownXS rotate={open} size="xs" />}
onClick={() => setOpen((o) => !o)}
/>
}
classes={{
tagsList: "h-32px pr-0! overflow-hidden",
}}
/>
<Popper
anchorEl={containerRef.current}
open={open}
placement="bottom-start"
>
<ClickAwayListener onClickAway={() => setOpen(false)}>
<HvPanel className="grid gap-xs w-300px my-2px border rounded-large">
<HvTypography variant="caption1">Last Used:</HvTypography>
<div className="flex gap-xs">
{lastUsed.map((color, idx) => (
<HvTag
autoFocus={idx === 0}
key={color}
label={color}
onClick={() => handleAddColor(color)}
/>
))}
</div>
<HvTypography variant="caption1">More colors:</HvTypography>
<div className="flex flex-wrap gap-xs">
{colors
.filter(
(color) =>
!selectedColors.includes(color) &&
!lastUsed.includes(color),
)
.map((color) => (
<HvTag
key={color}
label={color}
onClick={() => handleAddColor(color)}
/>
))}
</div>
</HvPanel>
</ClickAwayListener>
</Popper>
</div>
);
}
import { useState } from "react";
import { ClickAwayListener, Popper } from "@mui/base";
import {
HvAdornment,
HvInput,
HvPanel,
HvTag,
HvTypography,
} from "@hitachivantara/uikit-react-core";
import { Add } from "@hitachivantara/uikit-react-icons";
const colors = [
"Blue",
"Red",
"Green",
"Yellow",
"Purple",
"White",
"Black",
"Orange",
"Pink",
"Brown",
"Gray",
"Cyan",
"Magenta",
"Lime",
"Teal",
"Lavender",
];
export default function Demo() {
const [anchorEl, setAnchorEl] = useState<HTMLElement>();
const [selectedColors, setSelectedColors] = useState<string[]>([]);
const open = Boolean(anchorEl);
const handleAddColor = (color: string) => {
setSelectedColors((prev) => [...new Set([...prev, color])]);
};
const handleRemoveColor = (color: string) => {
setSelectedColors((prev) => prev.filter((c) => c !== color));
};
return (
<>
<div className="w-300px">
<HvInput
label="Tags with suggestions and empty input"
onEnter={(event, value) => {
handleAddColor(value);
}}
startAdornment={
<HvAdornment
tabIndex={0}
icon={<Add rotate={open} size="xs" />}
onClick={(evt) => setAnchorEl(evt.currentTarget)}
/>
}
className="mb-sm"
/>
<Popper anchorEl={anchorEl} open={open} placement="bottom-start">
<ClickAwayListener onClickAway={() => setAnchorEl(undefined)}>
<HvPanel className="grid gap-xs w-300px my-2px border rounded-large">
<HvTypography variant="caption1">More colors:</HvTypography>
<div className="flex flex-wrap gap-xs">
{colors
.filter((color) => !selectedColors.includes(color))
.map((color) => (
<HvTag
key={color}
label={color}
onClick={() => handleAddColor(color)}
/>
))}
</div>
</HvPanel>
</ClickAwayListener>
</Popper>
</div>
<div className="flex flex-wrap gap-xs">
{selectedColors.map((color) => (
<HvTag
key={color}
label={color}
onDelete={() => handleRemoveColor(color)}
/>
))}
</div>
</>
);
}
Inline Editable Text
Name:John Doe
import { useRef, useState } from "react";
import {
HvBaseInput,
HvButton,
HvIconButton,
HvTypography,
} from "@hitachivantara/uikit-react-core";
import { Check, Close, Edit } from "@hitachivantara/uikit-react-icons";
export default function Demo() {
const [editing, setEditing] = useState(false);
const inputRef = useRef<HTMLInputElement>(null);
const [cachedValue, setCachedValue] = useState("John Doe");
return editing ? (
<form
className="flex items-center gap-xs w-260px"
onSubmit={(evt) => {
evt.preventDefault();
setCachedValue(inputRef.current?.value || "");
setEditing(false);
}}
onReset={() => setEditing(false)}
>
<HvBaseInput ref={inputRef} autoFocus defaultValue={cachedValue} />
<HvIconButton type="reset" size="xs" variant="negative" title="Cancel">
<Close />
</HvIconButton>
<HvIconButton type="submit" size="xs" variant="positive" title="Save">
<Check />
</HvIconButton>
</form>
) : (
<div className="flex items-center gap-xs w-260px">
<span className="capitalize">Name:</span>
<HvTypography variant="label">{cachedValue}</HvTypography>
<HvButton icon title="Edit" onClick={() => setEditing(true)}>
<Edit />
</HvButton>
</div>
);
}
Label left side
import {
HvFormElement,
HvInput,
HvLabel,
} from "@hitachivantara/uikit-react-core";
export default function Demo() {
return (
<HvFormElement required className="flex gap-xs">
<HvLabel label="Name" htmlFor="username-input" className="h-fit mt-5px" />
<HvInput id="username" className="w-300px" required />
</HvFormElement>
);
}