Skip to content

Commit 69b6825

Browse files
authored
feat/input-chip-add-remove-manually (#2347)
1 parent 3ff56db commit 69b6825

File tree

3 files changed

+125
-35
lines changed

3 files changed

+125
-35
lines changed

.changeset/flat-icons-do.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@skeletonlabs/skeleton": minor
3+
---
4+
5+
feat: InputChip - Implemented function to add/remove chips programatically.

packages/skeleton/src/lib/components/InputChip/InputChip.svelte

+74-35
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@
2828
type InputChipEvent = {
2929
add: { event: SubmitEvent; chipIndex: number; chipValue: string };
3030
remove: { event: MouseEvent; chipIndex: number; chipValue: string };
31+
addManually: { chipIndex: number; chipValue: string };
32+
removeManually: { chipValue: string };
3133
invalid: { event: SubmitEvent; input: string };
34+
invalidManually: { input: string };
3235
};
3336
const dispatch = createEventDispatcher<InputChipEvent>();
3437
@@ -174,27 +177,53 @@
174177
inputValid = true;
175178
}
176179
177-
function validate(): boolean {
178-
if (!input) return false;
180+
function validateCustom(chip: string) {
181+
return validation === undefined || validation(chip);
182+
}
183+
184+
function validateCount() {
185+
return max === -1 || value.length < max;
186+
}
187+
188+
function validateLength(chip: string) {
189+
return (minlength === -1 || chip.length >= minlength) && (maxlength === -1 || chip.length <= maxlength);
190+
}
191+
192+
function validateWhiteList(chip: string) {
193+
return whitelist.length === 0 || whitelist.includes(chip);
194+
}
195+
196+
function validateDuplicates(chip: string) {
197+
return allowDuplicates || !value.includes(chip);
198+
}
199+
200+
function validate(chip: string = ''): boolean {
201+
if (!chip && !input) return false;
179202
// Format: trim value
180-
input = input.trim();
181-
// Custom validation
182-
if (validation !== undefined && !validation(input)) return false;
183-
// Maximum
184-
if (max !== -1 && value.length >= max) return false;
185-
// Minimum Character Length
186-
if (minlength !== -1 && input.length < minlength) return false;
187-
// Maximum Character Length
188-
if (maxlength !== -1 && input.length > maxlength) return false;
189-
// Whitelist (if available)
190-
if (whitelist.length > 0 && !whitelist.includes(input)) return false;
191-
// Value is unique
192-
if (allowDuplicates === false && value.includes(input)) return false;
193-
// State is valid
194-
return true;
203+
chip = chip !== '' ? chip.trim() : input.trim();
204+
return validateCustom(chip) && validateCount() && validateLength(chip) && validateWhiteList(chip) && validateDuplicates(chip);
205+
}
206+
207+
function addChipCommon(chip: string) {
208+
// Format: to lowercase (if enabled)
209+
chip = allowUpperCase ? chip : chip.toLowerCase();
210+
// Append value to array
211+
value.push(chip);
212+
value = value;
213+
chipValues.push({ val: chip, id: Math.random() });
214+
chipValues = chipValues;
215+
}
216+
217+
function removeChipCommon(chip: string) {
218+
let chipIndex = value.indexOf(chip);
219+
// Remove value from array
220+
value.splice(chipIndex, 1);
221+
value = value;
222+
chipValues.splice(chipIndex, 1);
223+
chipValues = chipValues;
195224
}
196225
197-
function addChip(event: SvelteEvent<SubmitEvent, HTMLFormElement>): void {
226+
function addChipInternally(event: SvelteEvent<SubmitEvent, HTMLFormElement>): void {
198227
event.preventDefault();
199228
// Validate
200229
inputValid = validate();
@@ -204,30 +233,42 @@
204233
dispatch('invalid', { event, input });
205234
return;
206235
}
207-
// Format: to lowercase (if enabled)
208-
input = allowUpperCase ? input : input.toLowerCase();
209-
// Append value to array
210-
value.push(input);
211-
value = value;
212-
chipValues.push({ val: input, id: Math.random() });
213-
chipValues = chipValues;
236+
addChipCommon(input);
214237
/** @event {{ event: Event, chipIndex: number, chipValue: string }} add - Fires when a chip is added. */
215238
dispatch('add', { event, chipIndex: value.length - 1, chipValue: input });
216239
// Clear input value
217240
input = '';
218241
}
219242
220-
function removeChip(event: SvelteEvent<MouseEvent, HTMLButtonElement>, chipIndex: number, chipValue: string): void {
243+
function removeChipInternally(event: SvelteEvent<MouseEvent, HTMLButtonElement>, chipIndex: number, chipValue: string): void {
221244
if ($$restProps.disabled) return;
222-
// Remove value from array
223-
value.splice(chipIndex, 1);
224-
value = value;
225-
chipValues.splice(chipIndex, 1);
226-
chipValues = chipValues;
245+
removeChipCommon(chipValue);
227246
/** @event {{ event: Event, chipIndex: number, chipValue: string }} remove - Fires when a chip is removed. */
228247
dispatch('remove', { event, chipIndex, chipValue });
229248
}
230249
250+
// Export functions
251+
export function addChip(chip: string) {
252+
// Validate
253+
inputValid = validate(chip);
254+
// When the onInvalid hook is present
255+
if (inputValid === false) {
256+
/** @event {{ input: string }} invalidManually - Fires when the manually added value is invalid. */
257+
dispatch('invalidManually', { input: chip });
258+
return;
259+
}
260+
addChipCommon(chip);
261+
/** @event {{ chipIndex: number, chipValue: string }} addManually - Fires when a chip is added manually. */
262+
dispatch('addManually', { chipIndex: value.length - 1, chipValue: chip });
263+
}
264+
265+
export function removeChip(chip: string) {
266+
if ($$restProps.disabled) return;
267+
removeChipCommon(chip);
268+
/** @event {{ chipValue: string }} removeManually - Fires when a chip is removed manually. */
269+
dispatch('removeManually', { chipValue: chip });
270+
}
271+
231272
// State
232273
$: classesInvalid = inputValid === false ? invalid : '';
233274
// Reactive
@@ -253,7 +294,7 @@
253294
<!-- Chip Wrapper -->
254295
<div class="input-chip-wrapper {classesChipWrapper}">
255296
<!-- Input Field -->
256-
<form on:submit={addChip}>
297+
<form on:submit={addChipInternally}>
257298
<input
258299
type="text"
259300
bind:value={input}
@@ -279,9 +320,7 @@
279320
<button
280321
type="button"
281322
class="chip {chips}"
282-
on:click={(e) => {
283-
removeChip(e, i, val);
284-
}}
323+
on:click={(e) => removeChipInternally(e, i, val)}
285324
on:click
286325
on:keypress
287326
on:keydown

sites/skeleton.dev/src/routes/(inner)/components/input-chips/+page.svelte

+46
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
3131
let musicalGenres = ['rock', 'r&b', 'pop'];
3232
let musicalGenresWhitelist = ['rock', 'pop', 'hip-hop', 'metal', 'techno', 'r&b'];
33+
let manualList: string[] = [];
34+
let manualInputChip: InputChip;
3335
const toastStore = getToastStore();
3436
3537
function isValidEmail(value: string): boolean {
@@ -161,6 +163,50 @@ function onInvalidHandler(event: any): void {
161163
</svelte:fragment>
162164
</DocsPreview>
163165
</section>
166+
<!-- Add/Remove Chips Programatically -->
167+
<section class="space-y-4">
168+
<h2 class="h2">Add / Remove Chips</h2>
169+
<p>
170+
To add or remove chips programatically, use the exported functions <code class="code">addChip</code> and
171+
<code class="code">removeChip</code> after binding the ChipInput.
172+
</p>
173+
<DocsPreview background="neutral">
174+
<svelte:fragment slot="preview">
175+
<InputChip
176+
bind:this={manualInputChip}
177+
bind:value={manualList}
178+
name="chips-example-programatic"
179+
placeholder="Enter number..."
180+
chips="variant-filled-tertiary"
181+
/>
182+
</svelte:fragment>
183+
<svelte:fragment slot="footer">
184+
<div class="text-center">
185+
<button type="button" class="btn variant-filled" on:click={() => manualInputChip.removeChip(manualList[-1])}
186+
>Remove LastChip</button
187+
>
188+
<button
189+
type="button"
190+
class="btn variant-filled"
191+
on:click={() => manualInputChip.addChip((Math.floor(Math.random() * 10000) + 1).toString())}>Add Random Number</button
192+
>
193+
</div>
194+
</svelte:fragment>
195+
<svelte:fragment slot="source">
196+
<CodeBlock
197+
language="ts"
198+
code={`
199+
let inputChip: InputChip;
200+
// add chip manually
201+
inputChip.addChip('chipValue');
202+
// remove chip manually
203+
inputChip.removeChip('chipValue');
204+
`}
205+
/>
206+
<CodeBlock language="html" code={`<InputChip ... bind:this={inputChip} />`} />
207+
</svelte:fragment>
208+
</DocsPreview>
209+
</section>
164210
<!-- Additional Settings -->
165211
<section class="space-y-4">
166212
<h2 class="h2">Additional Settings</h2>

0 commit comments

Comments
 (0)