feat(dashboard-v2): implement TextInputIcon component
This commit is contained in:
parent
a93e4e93f8
commit
aa6db3d115
|
@ -0,0 +1,10 @@
|
||||||
|
import { withIconProps } from "../withIconProps";
|
||||||
|
|
||||||
|
export const SearchIcon = withIconProps(({ size, ...props }) => (
|
||||||
|
<svg width={size} height={size} viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M9,0a9,9,0,0,1,7,14.62l3.68,3.67a1,1,0,0,1-1.32,1.5l-.1-.08L14.62,16A9,9,0,1,1,9,0ZM9,2a7,7,0,1,0,4.87,12l.07-.09.09-.07A7,7,0,0,0,9,2Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
));
|
|
@ -9,3 +9,4 @@ export * from "./icons/CircledErrorIcon";
|
||||||
export * from "./icons/CircledProgressIcon";
|
export * from "./icons/CircledProgressIcon";
|
||||||
export * from "./icons/CircledArrowUpIcon";
|
export * from "./icons/CircledArrowUpIcon";
|
||||||
export * from "./icons/PlusIcon";
|
export * from "./icons/PlusIcon";
|
||||||
|
export * from "./icons/SearchIcon";
|
||||||
|
|
|
@ -4,7 +4,7 @@ const propTypes = {
|
||||||
/**
|
/**
|
||||||
* Size of the icon's bounding box.
|
* Size of the icon's bounding box.
|
||||||
*/
|
*/
|
||||||
size: PropTypes.number,
|
size: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultProps = {
|
const defaultProps = {
|
||||||
|
|
|
@ -1,20 +1,45 @@
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
|
import cn from "classnames";
|
||||||
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
import { PlusIcon } from "../Icons";
|
||||||
|
|
||||||
|
export const TextInputIcon = ({ className, icon, placeholder, onChange }) => {
|
||||||
|
const inputRef = useRef();
|
||||||
|
const [focused, setFocused] = useState(false);
|
||||||
|
const [value, setValue] = useState("");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
onChange(value);
|
||||||
|
}, [value, onChange]);
|
||||||
|
|
||||||
/**
|
|
||||||
* Primary UI component for user interaction
|
|
||||||
*/
|
|
||||||
export const TextInputIcon = ({ icon, position, placeholder }) => {
|
|
||||||
return (
|
return (
|
||||||
<div className={"flex flex-row items-center px-textInputIcon h-textInput rounded-full bg-palette-100"}>
|
<div
|
||||||
{position === "left" ? <div className={"w-buttonIconLg h-buttonIconLg"}>{icon}</div> : null}
|
className={cn(
|
||||||
<input
|
"grid-flow-col inline-grid grid-cols-[2rem_1fr_1.5rem] items-center rounded-full bg-palette-100 px-4 py-2",
|
||||||
placeholder={placeholder}
|
className,
|
||||||
className={
|
{
|
||||||
"w-full focus:outline-none mx-textInputHorizontal rounded-full bg-transparent " +
|
"outline outline-1 outline-primary": focused,
|
||||||
"placeholder-palette-400 text-content tracking-inputPlaceholder text-textInput"
|
|
||||||
}
|
}
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div>{icon}</div>
|
||||||
|
<input
|
||||||
|
ref={inputRef}
|
||||||
|
value={value}
|
||||||
|
onFocus={() => setFocused(true)}
|
||||||
|
onBlur={() => setFocused(false)}
|
||||||
|
onChange={(event) => setValue(event.target.value)}
|
||||||
|
placeholder={placeholder}
|
||||||
|
className="focus:outline-none bg-transparent placeholder:text-palette-400"
|
||||||
/>
|
/>
|
||||||
{position === "right" ? <div className={"w-buttonIconLg h-buttonIconLg"}>{icon}</div> : null}
|
{value && (
|
||||||
|
<PlusIcon
|
||||||
|
size={14}
|
||||||
|
role="button"
|
||||||
|
className="justify-self-end text-palette-400 rotate-45"
|
||||||
|
onClick={() => setValue("")}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -23,13 +48,13 @@ TextInputIcon.propTypes = {
|
||||||
/**
|
/**
|
||||||
* Icon to place in text input
|
* Icon to place in text input
|
||||||
*/
|
*/
|
||||||
icon: PropTypes.element,
|
icon: PropTypes.element.isRequired,
|
||||||
/**
|
|
||||||
* Side to place icon
|
|
||||||
*/
|
|
||||||
position: PropTypes.oneOf(["left", "right"]),
|
|
||||||
/**
|
/**
|
||||||
* Input placeholder
|
* Input placeholder
|
||||||
*/
|
*/
|
||||||
placeholder: PropTypes.string,
|
placeholder: PropTypes.string,
|
||||||
|
/**
|
||||||
|
* Function to be called whenever value changes
|
||||||
|
*/
|
||||||
|
onChange: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { TextInputIcon } from "./TextInputIcon";
|
import { TextInputIcon } from "./TextInputIcon";
|
||||||
import { CogIcon } from "../Icons";
|
import { SearchIcon } from "../Icons";
|
||||||
|
import { Panel } from "../Panel";
|
||||||
|
|
||||||
// More on default export: https://storybook.js.org/docs/react/writing-stories/introduction#default-export
|
// More on default export: https://storybook.js.org/docs/react/writing-stories/introduction#default-export
|
||||||
export default {
|
export default {
|
||||||
|
@ -9,19 +10,21 @@ export default {
|
||||||
};
|
};
|
||||||
|
|
||||||
// More on component templates: https://storybook.js.org/docs/react/writing-stories/introduction#using-args
|
// More on component templates: https://storybook.js.org/docs/react/writing-stories/introduction#using-args
|
||||||
const Template = (args) => <TextInputIcon {...args} />;
|
const Template = (args) => (
|
||||||
|
<Panel>
|
||||||
|
<TextInputIcon {...args} />
|
||||||
|
</Panel>
|
||||||
|
);
|
||||||
|
|
||||||
export const IconLeft = Template.bind({});
|
export const IconLeft = Template.bind({});
|
||||||
// More on args: https://storybook.js.org/docs/react/writing-stories/args
|
// More on args: https://storybook.js.org/docs/react/writing-stories/args
|
||||||
IconLeft.args = {
|
IconLeft.args = {
|
||||||
icon: <CogIcon />,
|
icon: <SearchIcon size={20} />,
|
||||||
position: "left",
|
|
||||||
placeholder: "Search",
|
placeholder: "Search",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IconRight = Template.bind({});
|
export const IconRight = Template.bind({});
|
||||||
IconRight.args = {
|
IconRight.args = {
|
||||||
icon: <CogIcon />,
|
icon: <SearchIcon size={20} />,
|
||||||
position: "right",
|
|
||||||
placeholder: "Search",
|
placeholder: "Search",
|
||||||
};
|
};
|
||||||
|
|
|
@ -28,6 +28,7 @@ module.exports = {
|
||||||
borderColor: (theme) => ({ ...theme("colors"), ...colors }),
|
borderColor: (theme) => ({ ...theme("colors"), ...colors }),
|
||||||
textColor: (theme) => ({ ...theme("colors"), ...colors }),
|
textColor: (theme) => ({ ...theme("colors"), ...colors }),
|
||||||
placeholderColor: (theme) => ({ ...theme("colors"), ...colors }),
|
placeholderColor: (theme) => ({ ...theme("colors"), ...colors }),
|
||||||
|
outlineColor: (theme) => ({ ...theme("colors"), ...colors }),
|
||||||
extend: {
|
extend: {
|
||||||
fontFamily: {
|
fontFamily: {
|
||||||
sans: ["Sora", ...defaultTheme.fontFamily.sans],
|
sans: ["Sora", ...defaultTheme.fontFamily.sans],
|
||||||
|
|
Reference in New Issue