Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ChipInput component #808

Merged
merged 9 commits into from
Oct 10, 2023
Merged
76 changes: 76 additions & 0 deletions apps/design-system/src/components/ChipInput.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { useState } from "react";
import { ChipInput } from "@asyncapi/studio-ui";

const meta = {
component: ChipInput,
parameters: {
layout: 'fullscreen',
backgrounds: {
default: 'light'
}
},
};

export default meta;

export const Default = () => {
const [currentChips, setCurrentChips] = useState<string[]>([]);

return (
<div className="p-4 bg-gray-100 flex items-center">
<ChipInput
name="chip-input-default"
id="chip-input-id-default"
chips={currentChips}
onChange={setCurrentChips}
/>
</div>
);
};

export const WithTags = () => {
const [currentChips, setCurrentChips] = useState<string[]>(['production', 'platform']);

return (
<div className="p-4 bg-gray-100 flex items-center">
<ChipInput
name="chip-input-chip-text"
id="chip-input-id-chip-text"
chips={currentChips}
onChange={setCurrentChips}
/>
</div>
);
};

export const WithSomeDefaultText = () => {
const [currentChips, setCurrentChips] = useState<string[]>(['production', 'platform']);

return (
<div className="p-4 bg-gray-100 flex items-center">
<ChipInput
name="chip-input-default-text-in-input"
id="chip-input-id-default-text-in-input"
chips={currentChips}
onChange={setCurrentChips}
defaultValue="registr"
/>
</div>
);
};

export const WithCustomPlaceholder = () => {
const [currentChips, setCurrentChips] = useState<string[]>([]);

return (
<div className="p-4 bg-gray-100 flex items-center">
<ChipInput
name="chip-input-custom-placeholder"
id="chip-input-id-placeholder"
chips={currentChips}
onChange={setCurrentChips}
placeholder="Enter a custom chip"
/>
</div>
);
};
80 changes: 80 additions & 0 deletions packages/ui/components/ChipInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { FunctionComponent, KeyboardEvent, useRef } from 'react';

interface ChipInputProps {
name: string;
id: string;
className?: string;
chips: string[];
onChange: (chips: string[]) => void;
isDisabled?: boolean;
placeholder?: string;
defaultValue?: string;
}

export const ChipInput: FunctionComponent<ChipInputProps> = ({
name,
id,
className,
chips,
onChange,
isDisabled = false,
placeholder = 'Add Tags',
defaultValue = ''
}) => {
const inputRef = useRef<HTMLInputElement>(null);
const firstChipRef = useRef<HTMLDivElement>(null);

const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {
if (event.key === 'Enter' && event.currentTarget.value.trim()) {
onChange([...chips, event.currentTarget.value.trim()]);
event.currentTarget.value = '';
} else if (event.key === 'Backspace' && !event.currentTarget.value) {
onChange(chips.slice(0, -1));
} else if (event.key === 'Tab' && !event.shiftKey) {
event.preventDefault();
firstChipRef.current?.focus();
}
};

const handleChipKeyDown = (index: number) => (event: KeyboardEvent<HTMLDivElement>) => {
if (event.key === 'Backspace') {
const updatedChips = [...chips];
updatedChips.splice(index, 1);
onChange(updatedChips);
}
};

const handleDelete = (chipToDelete: string) => () => {
const updatedChips = chips.filter(chip => chip !== chipToDelete);
onChange(updatedChips);
};

return (
<div className={`${className} flex flex-wrapitems-center p-1 bg-gray-900 rounded border border-gray-800`} style={{ width: '862px', height: '46px' }}>
{chips.map((chip, index) => (
<div
key={chip}
className="m-1 bg-gray-100 text-gray-900 rounded px-2 py-1 flex items-center border border-gray-400 focus:border-blue-500 focus:border-2 focus:outline-none"
style={{ height: '28px', borderStyle: 'solid' }}
tabIndex={0}
onKeyDown={handleChipKeyDown(index)}
ref={index === 0 ? firstChipRef : undefined}
>
<span>{chip}</span>
<button onClick={handleDelete(chip)} tabIndex={-1} className="ml-1 text-gray-400 focus:outline-none">×</button>
</div>
))}
<input
ref={inputRef}
name={name}
id={id}
type="text"
onKeyDown={handleKeyDown}
className="p-1 bg-gray-900 text-white rounded outline-none"
placeholder={placeholder}
disabled={isDisabled}
defaultValue={defaultValue}
/>
</div>
);
};
1 change: 1 addition & 0 deletions packages/ui/components/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import './styles.css'

// components
export * from './ChipInput'
export * from './EditorSwitch'
export * from './DropdownMenu'
export * from './Logo'
Expand Down