Skip to content

Commit 4c6e01d

Browse files
Fix SelectField menu icons & toggle collapsing btn
Fix SelectField toggle button not collapsing.
1 parent f3bda28 commit 4c6e01d

File tree

5 files changed

+61
-52
lines changed

5 files changed

+61
-52
lines changed

.changeset/bright-donuts-appear.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte-ux': patch
3+
---
4+
5+
Fix SelectField MenuOption icons no longer being passed to MenuItems. Fix SelectField `fieldClasses` not being incorporated into the inner `TextField` properly. Update SelectField docs page with colored example that differentiates icon & input color, both in the field and the menu items.

packages/svelte-ux/src/lib/components/SelectField.svelte

+36-21
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@
1919
import type { MenuOption } from '$lib/types/options';
2020
import type { ScrollIntoViewOptions } from '$lib/actions';
2121
22+
type LogReason<T extends Event = any> = {
23+
reason: string,
24+
event?: T
25+
}
26+
2227
const dispatch = createEventDispatcher<{
2328
change: { value: any; option: any };
2429
inputChange: string;
@@ -58,7 +63,7 @@
5863
root?: string;
5964
field?: string | ComponentProps<TextField>['classes'];
6065
options?: string;
61-
option?: string;
66+
option?: string | ComponentProps<MenuItem>['classes'];
6267
selected?: string;
6368
group?: string;
6469
empty?: string;
@@ -68,6 +73,9 @@
6873
let fieldClasses: ComponentProps<TextField>['classes'];
6974
$: fieldClasses = typeof(classes.field) === "string" ? { root: classes.field } : classes.field;
7075
76+
let optionClasses: ComponentProps<MenuItem>['classes'];
77+
$: optionClasses = typeof(classes.option) === "string" ? { root: classes.option } : classes.option;
78+
7179
// Menu props
7280
export let placement: Placement = 'bottom-start';
7381
export let autoPlacement = true;
@@ -199,12 +207,11 @@
199207
200208
searchText = e.detail.inputValue as string;
201209
dispatch('inputChange', searchText);
202-
show();
210+
show({ reason: "onChange", event: e });
203211
}
204212
205-
function onFocus() {
206-
logger.debug('onFocus');
207-
show();
213+
function onFocus(event: FocusEvent) {
214+
show({ reason: "onFocus", event });
208215
}
209216
210217
function onBlur(e: FocusEvent|CustomEvent<any>) {
@@ -218,7 +225,7 @@
218225
fe.relatedTarget !== menuOptionsEl?.offsetParent && // click on scroll bar
219226
!fe.relatedTarget.closest('menu > [slot=actions]') // click on action item
220227
) {
221-
hide('blur');
228+
hide({ reason: 'blur', event: e });
222229
} else {
223230
logger.debug('ignoring blur');
224231
}
@@ -237,7 +244,7 @@
237244
break;
238245
239246
case 'ArrowDown':
240-
show();
247+
show({ reason: `onKeyDown: '${e.key}'`, event: e });
241248
if (highlightIndex < filteredOptions.length - 1) {
242249
highlightIndex++;
243250
} else {
@@ -247,7 +254,7 @@
247254
break;
248255
249256
case 'ArrowUp':
250-
show();
257+
show({ reason: `onKeyDown: '${e.key}'`, event: e });
251258
if (highlightIndex > 0) {
252259
highlightIndex--;
253260
} else {
@@ -259,7 +266,7 @@
259266
case 'Escape':
260267
if (open) {
261268
inputEl?.focus();
262-
hide('escape');
269+
hide({ reason: 'escape', event: e });
263270
}
264271
break;
265272
}
@@ -274,15 +281,15 @@
274281
}
275282
}
276283
277-
function onClick() {
278-
logger.debug('onClick');
279-
show();
284+
function onClick(event: MouseEvent) {
285+
show({ reason: 'onClick', event });
280286
}
281287
282-
function show() {
283-
logger.debug('show');
288+
function show<T extends LogReason = any>(reason: string|T = '') {
289+
const doShow = !disabled && !readonly;
290+
logger.debug('show', { ...(typeof(reason) === "string" ? { reason } : reason), openBefore: open, openAfter: doShow });
284291
285-
if (!disabled && !readonly) {
292+
if (doShow) {
286293
if (open === false && clearSearchOnOpen) {
287294
searchText = ''; // Show all options on open
288295
}
@@ -291,8 +298,8 @@
291298
}
292299
}
293300
294-
function hide(reason = '') {
295-
logger.debug('hide', { reason });
301+
function hide<T extends LogReason = any>(reason: string|T = '') {
302+
logger.debug('hide', { ...(typeof(reason) === "string" ? { reason } : reason), openBefore: open, openAfter: false });
296303
open = false;
297304
highlightIndex = -1;
298305
}
@@ -384,8 +391,8 @@
384391
on:keydown={onKeyDown}
385392
on:keypress={onKeyPress}
386393
actions={fieldActions}
387-
classes={{ container: inlineOptions ? 'border-none shadow-none hover:shadow-none group-focus-within:shadow-none' : undefined }}
388-
class={cls('h-full', theme.field, fieldClasses)}
394+
classes={{ ...(fieldClasses ?? {}), container: inlineOptions ? 'border-none shadow-none hover:shadow-none group-focus-within:shadow-none' : undefined }}
395+
class={cls('h-full', theme.field)}
389396
role="combobox"
390397
aria-expanded={open ? "true" : "false"}
391398
aria-autocomplete={!inlineOptions ? "list" : undefined}
@@ -417,7 +424,11 @@
417424
icon={toggleIcon}
418425
class="text-black/50 p-1 transform {open ? 'rotate-180' : ''}"
419426
tabindex="-1"
420-
on:click={() => {logger.debug("toggleIcon clicked")}}
427+
on:click={(e) => {
428+
logger.debug("toggleIcon clicked", { event: e, open })
429+
const func = !open ? show : hide;
430+
func({ reason: "toggleIcon", event: e });
431+
}}
421432
/>
422433
{/if}
423434
</span>
@@ -434,7 +445,7 @@
434445
{disableTransition}
435446
moveFocus={false}
436447
bind:open
437-
on:close={() => hide('menu on:close')}
448+
on:close={e => hide({ reason: 'menu on:close', event: e})}
438449
{...menuProps}
439450
>
440451
<!-- TODO: Rework into hierarchy of snippets in v2.0 -->
@@ -447,13 +458,15 @@
447458
<svelte:fragment slot="option" let:option let:index>
448459
<slot name="option" {option} {index} {selected} {value} {highlightIndex}>
449460
<MenuItem
461+
classes={optionClasses}
450462
class={cls(
451463
index === highlightIndex && '[:not(.group:hover)>&]:bg-black/5',
452464
option === selected && (classes.selected || 'font-semibold'),
453465
option.group ? 'px-4' : 'px-2',
454466
theme.option,
455467
classes.option
456468
)}
469+
icon={option.icon}
457470
scrollIntoView={{ condition: index === highlightIndex, onlyIfNeeded: inlineOptions, ...scrollIntoView }}
458471
role="option"
459472
aria-selected={option === selected ? "true" : "false"}
@@ -485,13 +498,15 @@
485498
<svelte:fragment slot="option" let:option let:index>
486499
<slot name="option" {option} {index} {selected} {value} {highlightIndex}>
487500
<MenuItem
501+
classes={optionClasses}
488502
class={cls(
489503
index === highlightIndex && '[:not(.group:hover)>&]:bg-black/5',
490504
option === selected && (classes.selected || 'font-semibold'),
491505
option.group ? 'px-4' : 'px-2',
492506
theme.option,
493507
classes.option
494508
)}
509+
icon={option.icon}
495510
scrollIntoView={{ condition: index === highlightIndex, onlyIfNeeded: inlineOptions, ...scrollIntoView }}
496511
role="option"
497512
aria-selected={option === selected ? "true" : "false"}

packages/svelte-ux/src/lib/components/TextField.svelte

+2-1
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
error?: string;
6666
prepend?: string;
6767
append?: string;
68+
icon?: string;
6869
} = {};
6970
const theme = getComponentTheme('TextField');
7071
@@ -248,7 +249,7 @@
248249
<slot name="prepend" />
249250
{#if icon}
250251
<span class="mr-3">
251-
<Icon data={asIconData(icon)} class="text-black/50" />
252+
<Icon data={asIconData(icon)} class={cls("text-black/50", classes.icon)} />
252253
</span>
253254
{/if}
254255
</div>

packages/svelte-ux/src/lib/components/_SelectListOptions.svelte

-3
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,7 @@
2424
2525
export let classes: {
2626
root?: string;
27-
option?: string;
28-
selected?: string;
2927
group?: string;
30-
empty?: string;
3128
} = {};
3229
3330
const theme = getComponentTheme('SelectField');

packages/svelte-ux/src/routes/docs/components/SelectField/+page.svelte

+18-27
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,17 @@
1313
1414
import { delay } from '$lib/utils/promise';
1515
import { cls } from '$lib/utils/styles';
16-
import Icon from '$lib/components/Icon.svelte';
1716
import type { MenuOption } from '$lib/types/options';
1817
19-
let options: MenuOption[] = [
18+
let graphicOptions: MenuOption[] = [
2019
{ label: 'One', value: 1, icon: mdiMagnify },
2120
{ label: 'Two', value: 2, icon: mdiPlus },
2221
{ label: 'Three', value: 3, icon: mdiPencil },
2322
{ label: 'Four', value: 4, icon: mdiAccount },
2423
];
2524
25+
let options: MenuOption[] = graphicOptions.map(o => { return { ...o, icon: undefined }; });
26+
2627
const optionsWithGroup: MenuOption[] = [
2728
{ label: 'One', value: 1, group: 'First' },
2829
{ label: 'Two', value: 2, group: 'First' },
@@ -169,31 +170,6 @@
169170
</SelectField>
170171
</Preview>
171172

172-
<h2>option slot with icon (field icon updates based on selected option)</h2>
173-
174-
<Preview>
175-
<SelectField
176-
{options}
177-
bind:value
178-
activeOptionIcon={true}
179-
on:change={(e) => console.log('on:change', e.detail)}
180-
>
181-
<div slot="option" let:option let:index let:selected let:highlightIndex>
182-
<MenuItem
183-
class={cls(
184-
index === highlightIndex && 'bg-black/5',
185-
option === selected && 'font-semibold',
186-
option.group ? 'px-4' : 'px-2'
187-
)}
188-
scrollIntoView={index === highlightIndex}
189-
icon={{ data: option.icon, style: 'color: #0000FF;' }}
190-
>
191-
{option.label}
192-
</MenuItem>
193-
</div>
194-
</SelectField>
195-
</Preview>
196-
197173
<h2>option with action</h2>
198174

199175
<Preview>
@@ -365,6 +341,21 @@
365341
<SelectField {options} icon={mdiMagnify} />
366342
</Preview>
367343

344+
<h2>Icon and options with icons (field icon updates based on selected option)</h2>
345+
346+
<Preview>
347+
<SelectField
348+
options={graphicOptions}
349+
bind:value
350+
activeOptionIcon={true}
351+
classes={{
352+
field: { input: 'text-blue-600', icon: 'text-pink-300' },
353+
option: { root: 'text-blue-600', icon: 'text-pink-300' }
354+
}}
355+
on:change={(e) => console.log('on:change', e.detail)}
356+
/>
357+
</Preview>
358+
368359
<h2>Rounded</h2>
369360

370361
<Preview>

0 commit comments

Comments
 (0)