Skip to content

Commit 93a9773

Browse files
authored
Merge pull request #2450 from skeletonlabs/dev
Merge for release - Jan 30, 2024
2 parents 765e876 + 6e0c3db commit 93a9773

File tree

13 files changed

+156
-55
lines changed

13 files changed

+156
-55
lines changed

.changeset/famous-pots-trade.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@skeletonlabs/skeleton": patch
3+
---
4+
5+
bugfix: Removed recursive-tree-view's unused props for `disabled` and `open`

.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: InputChips now allow you to add/remove chips programatically.

.changeset/nasty-terms-allow.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@skeletonlabs/skeleton": patch
3+
---
4+
5+
bugfix: CodeBlock copy button type is now `button` instead of default (`submit`)

.changeset/real-cougars-invent.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@skeletonlabs/skeleton": patch
3+
---
4+
5+
bugfix: added `label` prop to InputChips to provide aria-label for a11y

.changeset/swift-insects-rush.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@skeletonlabs/skeleton": patch
3+
---
4+
5+
chore: Applied `@html` interpretation to Autocomplete empty state

packages/plugin/src/styles/components/modals.css

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
}
1717

1818
/* Provides initial focus selection styles on opening the modal */
19-
.modal *:focus:not([tabindex='-1']):not(.input):not(.textarea):not(.select):not(.input-group) {
19+
.modal *:focus:not([tabindex='-1']):not(.input):not(.textarea):not(.select):not(.input-group):not(.input-group input) {
2020
outline-style: auto;
2121
@apply outline-[-webkit-focus-ring-color];
2222
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,6 @@
174174
</ul>
175175
</nav>
176176
{:else}
177-
<div class="autocomplete-empty {classesEmpty}">{emptyState}</div>
177+
<div class="autocomplete-empty {classesEmpty}">{@html emptyState}</div>
178178
{/if}
179179
</div>

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

+79-36
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
@@ -86,6 +89,10 @@
8689
/** Provide arbitrary classes to style the input field region. */
8790
export let regionInput: CssClasses = '';
8891
92+
// Props (A11y)
93+
/** Provide the ARIA label for the select input. */
94+
export let label = 'Chips select';
95+
8996
// Props (transition)
9097
/**
9198
* Enable/Disable transitions
@@ -170,27 +177,53 @@
170177
inputValid = true;
171178
}
172179
173-
function validate(): boolean {
174-
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;
175202
// Format: trim value
176-
input = input.trim();
177-
// Custom validation
178-
if (validation !== undefined && !validation(input)) return false;
179-
// Maximum
180-
if (max !== -1 && value.length >= max) return false;
181-
// Minimum Character Length
182-
if (minlength !== -1 && input.length < minlength) return false;
183-
// Maximum Character Length
184-
if (maxlength !== -1 && input.length > maxlength) return false;
185-
// Whitelist (if available)
186-
if (whitelist.length > 0 && !whitelist.includes(input)) return false;
187-
// Value is unique
188-
if (allowDuplicates === false && value.includes(input)) return false;
189-
// State is valid
190-
return true;
203+
chip = chip !== '' ? chip.trim() : input.trim();
204+
return validateCustom(chip) && validateCount() && validateLength(chip) && validateWhiteList(chip) && validateDuplicates(chip);
191205
}
192206
193-
function addChip(event: SvelteEvent<SubmitEvent, HTMLFormElement>): void {
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;
224+
}
225+
226+
function addChipInternally(event: SvelteEvent<SubmitEvent, HTMLFormElement>): void {
194227
event.preventDefault();
195228
// Validate
196229
inputValid = validate();
@@ -200,30 +233,42 @@
200233
dispatch('invalid', { event, input });
201234
return;
202235
}
203-
// Format: to lowercase (if enabled)
204-
input = allowUpperCase ? input : input.toLowerCase();
205-
// Append value to array
206-
value.push(input);
207-
value = value;
208-
chipValues.push({ val: input, id: Math.random() });
209-
chipValues = chipValues;
236+
addChipCommon(input);
210237
/** @event {{ event: Event, chipIndex: number, chipValue: string }} add - Fires when a chip is added. */
211238
dispatch('add', { event, chipIndex: value.length - 1, chipValue: input });
212239
// Clear input value
213240
input = '';
214241
}
215242
216-
function removeChip(event: SvelteEvent<MouseEvent, HTMLButtonElement>, chipIndex: number, chipValue: string): void {
243+
function removeChipInternally(event: SvelteEvent<MouseEvent, HTMLButtonElement>, chipIndex: number, chipValue: string): void {
217244
if ($$restProps.disabled) return;
218-
// Remove value from array
219-
value.splice(chipIndex, 1);
220-
value = value;
221-
chipValues.splice(chipIndex, 1);
222-
chipValues = chipValues;
245+
removeChipCommon(chipValue);
223246
/** @event {{ event: Event, chipIndex: number, chipValue: string }} remove - Fires when a chip is removed. */
224247
dispatch('remove', { event, chipIndex, chipValue });
225248
}
226249
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+
227272
// State
228273
$: classesInvalid = inputValid === false ? invalid : '';
229274
// Reactive
@@ -241,15 +286,15 @@
241286
<div class="input-chip {classesBase}" class:opacity-50={$$restProps.disabled}>
242287
<!-- NOTE: Don't use `hidden` as it prevents `required` from operating -->
243288
<div class="h-0 overflow-hidden">
244-
<select bind:this={selectElement} bind:value {name} multiple {required} tabindex="-1">
289+
<select bind:this={selectElement} bind:value {name} multiple {required} aria-label={label} tabindex="-1">
245290
<!-- NOTE: options are required! -->
246291
{#each value as option}<option value={option}>{option}</option>{/each}
247292
</select>
248293
</div>
249294
<!-- Chip Wrapper -->
250295
<div class="input-chip-wrapper {classesChipWrapper}">
251296
<!-- Input Field -->
252-
<form on:submit={addChip}>
297+
<form on:submit={addChipInternally}>
253298
<input
254299
type="text"
255300
bind:value={input}
@@ -275,9 +320,7 @@
275320
<button
276321
type="button"
277322
class="chip {chips}"
278-
on:click={(e) => {
279-
removeChip(e, i, val);
280-
}}
323+
on:click={(e) => removeChipInternally(e, i, val)}
281324
on:click
282325
on:keypress
283326
on:keydown

packages/skeleton/src/lib/components/TreeView/RecursiveTreeView.svelte

+1-14
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,6 @@
4343
export let spacing: CssClasses = 'space-y-1';
4444
4545
// Props (children)
46-
/** Set open by default on load. */
47-
export let open = false;
48-
/** Set the tree disabled state */
49-
export let disabled = false;
5046
/** Provide classes to set the tree item padding styles. */
5147
export let padding: CssClasses = 'py-4 px-4';
5248
/** Provide classes to set the tree children indentation */
@@ -77,11 +73,9 @@
7773
export let labelledby = '';
7874
7975
// Context API
80-
setContext('open', open);
8176
setContext('selection', selection);
8277
setContext('multiple', multiple);
8378
setContext('relational', relational);
84-
setContext('disabled', disabled);
8579
setContext('padding', padding);
8680
setContext('indent', indent);
8781
setContext('hover', hover);
@@ -114,14 +108,7 @@
114108
$: classesBase = `${width} ${spacing} ${$$props.class ?? ''}`;
115109
</script>
116110

117-
<div
118-
class="tree {classesBase}"
119-
data-testid="tree"
120-
role="tree"
121-
aria-multiselectable={multiple}
122-
aria-label={labelledby}
123-
aria-disabled={disabled}
124-
>
111+
<div class="tree {classesBase}" data-testid="tree" role="tree" aria-multiselectable={multiple} aria-label={labelledby}>
125112
{#if nodes && nodes.length > 0}
126113
<RecursiveTreeViewItem
127114
{nodes}

packages/skeleton/src/lib/utilities/CodeBlock/CodeBlock.svelte

+1-1
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@
9494
<!-- Language -->
9595
<span class="codeblock-language">{languageFormatter(language)}</span>
9696
<!-- Copy Button -->
97-
<button class="codeblock-btn {button}" on:click={onCopyClick} use:clipboard={code}>
97+
<button type="button" class="codeblock-btn {button}" on:click={onCopyClick} use:clipboard={code}>
9898
{!copyState ? buttonLabel : buttonCopied}
9999
</button>
100100
</header>

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>

sites/skeleton.dev/src/routes/(inner)/components/progress-radials/+page.svelte

+1-1
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@
135135
<svelte:fragment slot="source">
136136
<CodeBlock
137137
language="html"
138-
code={`<ProgressRadial ... stroke={${strokeProps.value}} meter="stroke-primary-500" track="stroke-primary-500/30" strokeLinecap={${strokeLinecap}} />`}
138+
code={`<ProgressRadial ... stroke={${strokeProps.value}} meter="stroke-primary-500" track="stroke-primary-500/30" strokeLinecap="${strokeLinecap}" />`}
139139
/>
140140
</svelte:fragment>
141141
</DocsPreview>

sites/skeleton.dev/src/routes/(inner)/utilities/popups/+page.svelte

+1-1
Original file line numberDiff line numberDiff line change
@@ -587,7 +587,7 @@ const popupState: PopupSettings = {
587587
<span class="capitalize">{comboboxValue ?? 'Trigger'}</span>
588588
<i class="fa-solid fa-caret-down opacity-50" />
589589
</button>
590-
<div class="card w-48 shadow-xl py-2" data-popup="popupCombobox">
590+
<div class="card w-48 shadow-xl py-2 overflow-hidden" data-popup="popupCombobox">
591591
<ListBox rounded="rounded-none">
592592
<ListBoxItem bind:group={comboboxValue} name="medium" value="books">Books</ListBoxItem>
593593
<ListBoxItem bind:group={comboboxValue} name="medium" value="movies">Movies</ListBoxItem>

0 commit comments

Comments
 (0)