Type to create a new filter
const [typedChips, setTypedChips] = useState<string[]>([]);
const [inputValue, setInputValue] = useState("");<GoabxFormItem label="Type to create a chip" mb="m">
<GoabxInput
name="chipInput"
value={inputValue}
onChange={(e) => setInputValue(e.value.trim())}
onKeyPress={(e) => {
if (e.key === "Enter" && e.value.trim()) {
setTypedChips([...typedChips, e.value.trim()]);
setTimeout(() => setInputValue(""), 0);
} else if (e.key === "Backspace" && !e.value.trim() && typedChips.length > 0) {
setTypedChips(typedChips.slice(0, -1));
}
}}
width="100%"
/>
</GoabxFormItem>
<div>
{typedChips.map((chip, index) => (
<GoabxFilterChip
key={index}
content={chip}
mb="xs"
mr="xs"
onClick={() => setTypedChips(typedChips.filter((c) => c !== chip))}
/>
))}
</div>typedChips: string[] = [];
inputValue = "";
handleInputChange(detail: GoabInputOnChangeDetail): void {
const newValue = detail.value.trim();
this.inputValue = newValue;
}
handleInputKeyPress(detail: GoabInputOnKeyPressDetail): void {
const newValue = detail.value.trim();
if (detail.key === "Enter" && newValue !== "") {
this.addChip();
} else if (!this.inputValue && this.typedChips.length > 0 && detail.key === "Backspace") {
this.typedChips.pop();
}
}
addChip(): void {
if (this.inputValue.trim()) {
this.typedChips.push(this.inputValue.trim());
this.inputValue = "";
}
}
removeTypedChip(chip: string): void {
this.typedChips = this.typedChips.filter((c) => c !== chip);
}<goabx-form-item label="Type to create a chip" mb="m">
<goabx-input
[value]="inputValue"
(onChange)="handleInputChange($event)"
(onKeyPress)="handleInputKeyPress($event)"
width="100%">
</goabx-input>
</goabx-form-item>
<div *ngIf="typedChips.length > 0">
<goabx-filter-chip
*ngFor="let typedChip of typedChips"
[content]="typedChip"
mb="xs"
mr="xs"
(onClick)="removeTypedChip(typedChip)">
</goabx-filter-chip>
</div>const typedChips = [];
const input = document.getElementById("chip-input");
const addBtn = document.getElementById("add-btn");
const container = document.getElementById("chips-container");
let currentValue = "";
function renderChips() {
container.innerHTML = "";
typedChips.forEach((chip) => {
const chipEl = document.createElement("goa-filter-chip");
chipEl.setAttribute("version", "2");
chipEl.setAttribute("content", chip);
chipEl.setAttribute("mb", "xs");
chipEl.setAttribute("mr", "xs");
chipEl.addEventListener("_click", () => removeChip(chip));
container.appendChild(chipEl);
});
}
function addChip() {
if (currentValue.trim()) {
typedChips.push(currentValue.trim());
currentValue = "";
input.setAttribute("value", "");
renderChips();
}
}
function removeChip(chip) {
const index = typedChips.indexOf(chip);
if (index > -1) {
typedChips.splice(index, 1);
renderChips();
}
}
input.addEventListener("_change", (e) => {
currentValue = e.detail.value || "";
});
addBtn.addEventListener("_click", () => {
addChip();
});<goa-form-item version="2" label="Type to create a chip" mb="m">
<goa-block gap="xs" direction="row">
<div style="flex: 1">
<goa-input version="2" id="chip-input" width="100%"></goa-input>
</div>
<goa-button version="2" id="add-btn" type="secondary">Add</goa-button>
</goa-block>
</goa-form-item>
<div id="chips-container"></div>Allow users to type custom filter values and create filter chips by pressing Enter, with the ability to remove chips using Backspace or by clicking them.
When to use
Use this pattern when:
- Users need to create custom filter values not from a predefined list
- Free-form text filtering is appropriate for the data
- Users may want to quickly add multiple related filters
Considerations
- Clear the input after a chip is created
- Allow Backspace to delete the last chip when the input is empty
- Provide visual feedback as chips are added
- Consider input validation before creating chips
- Show chips inline with the input for clear association