Skip to content

Commit a47b7c7

Browse files
authored
MultiSelect / Checkbox / Radio improvements (#310)
* Add related for MultiSelect* * [Checkbox / Radio] Add `fullWidth` prop. Add `input` to classes, and class names to all internal elements for easier devtool recognition and CSS targeting * [MultiSelectOption] Improve default styling (align with SelectField) including `:hover`. Leverage recent Checkbox fullWidth changes for better pointer targets (full width and height). Support passing all `Checkbox` classes. * [MultiSelect] Remove default outer padding (add to MultiSelectMenu) and add `classes.action`
1 parent e68ce78 commit a47b7c7

File tree

14 files changed

+90
-20
lines changed

14 files changed

+90
-20
lines changed

.changeset/dirty-geckos-give.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"svelte-ux": patch
3+
---
4+
5+
[Checkbox / Radio] Add `fullWidth` prop. Add `input` to classes, and class names to all internal elements for easier devtool recognition and CSS targeting

.changeset/green-rockets-rule.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"svelte-ux": patch
3+
---
4+
5+
[MultiSelect] Remove default outer padding (add to MultiSelectMenu) and add `classes.action`

.changeset/hot-trainers-grow.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"svelte-ux": patch
3+
---
4+
5+
[MultiSelectOption] Improve default styling (align with SelectField) including `:hover`. Leverage recent Checkbox fullWidth changes for better pointer targets (full width and height). Support passing all `Checkbox` classes.

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

+11-4
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,13 @@
1414
export let indeterminate = false;
1515
export let required = false;
1616
export let disabled = false;
17-
export let circle = false;
17+
export let fullWidth = false;
1818
export let size: 'xs' | 'sm' | 'md' | 'lg' = 'sm';
19+
export let circle = false;
1920
2021
export let classes: {
2122
root?: string;
23+
input?: string;
2224
checkbox?: string;
2325
label?: string;
2426
icon?: string;
@@ -51,7 +53,8 @@
5153
<div
5254
class={cls(
5355
'Checkbox',
54-
'inline-flex items-center',
56+
fullWidth ? 'flex' : 'inline-flex',
57+
'items-center',
5558
settingsClasses.root,
5659
classes.root,
5760
$$props.class
@@ -65,13 +68,14 @@
6568
on:change={onChange}
6669
on:change
6770
{value}
68-
class="peer appearance-none absolute"
71+
class={cls('input', 'peer appearance-none absolute', settingsClasses.input, classes.input)}
6972
{required}
7073
{disabled}
7174
/>
7275
<label
7376
for={id}
7477
class={cls(
78+
'checkbox',
7579
'inline-grid place-items-center border-2',
7680
circle ? 'rounded-full' : 'rounded',
7781
'peer-disabled:opacity-50 transition-shadow duration-300',
@@ -90,6 +94,7 @@
9094
<Icon
9195
path={indeterminate ? mdiMinus : mdiCheck}
9296
class={cls(
97+
'icon',
9398
'pointer-events-none text-primary-content transition-transform',
9499
checked ? 'scale-100' : 'scale-0',
95100
settingsClasses.icon,
@@ -108,7 +113,9 @@
108113
<label
109114
for={id}
110115
class={cls(
111-
'peer-disabled:opacity-50 pl-1',
116+
'label',
117+
'flex-1',
118+
'pl-1 peer-disabled:opacity-50',
112119
{
113120
xs: 'text-xs', // 12px
114121
sm: 'text-sm', // 14px

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

+14-4
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
4242
export let classes: {
4343
root?: string;
44+
actions?: string;
4445
} = {};
4546
const settingsClasses = getComponentClasses('MultiSelect');
4647
@@ -112,7 +113,7 @@
112113
</script>
113114

114115
{#if inlineSearch}
115-
<div class="border-b border-surface-content/10 p-4 pb-2">
116+
<div class="border-b border-surface-content/10 pb-2">
116117
<TextField
117118
{placeholder}
118119
iconRight={mdiMagnify}
@@ -122,7 +123,7 @@
122123
</div>
123124
{/if}
124125

125-
<div class={cls('overflow-auto py-1 px-4', settingsClasses.root, classes.root, $$restProps.class)}>
126+
<div class={cls('overflow-auto', settingsClasses.root, classes.root, $$restProps.class)}>
126127
<slot name="beforeOptions" selection={$selection} />
127128

128129
<!-- initially selected options -->
@@ -209,15 +210,24 @@
209210
</div>
210211
{:else}
211212
{#if !filteredSelectedOptions.length}
212-
<div class="text-surface-content/50 text-xs py-2">There are no matching items.</div>
213+
<div class="text-surface-content/50 text-xs py-2 px-4 mb-1">
214+
There are no matching items.
215+
</div>
213216
{/if}
214217
{/each}
215218
</InfiniteScroll>
216219

217220
<slot name="afterOptions" selection={$selection} />
218221
</div>
219222

220-
<div class="grid grid-cols-[auto,1fr,auto] border-t border-surface-content/10 px-4 py-2">
223+
<div
224+
class={cls(
225+
'actions',
226+
'grid grid-cols-[auto,1fr,auto] border-t border-surface-content/10 pt-2',
227+
settingsClasses.actions,
228+
classes.actions
229+
)}
230+
>
221231
<slot name="actions" selection={$selection} {searchText}>
222232
<div />
223233
</slot>

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
4949
export let classes: {
5050
root?: string;
51-
menu?: string;
51+
multiSelectMenu?: ComponentProps<MultiSelectMenu<Option>>['classes'];
5252
field?: string;
5353
actions?: string;
5454
} = {};
@@ -207,7 +207,7 @@
207207
{labelProp}
208208
{valueProp}
209209
{searchText}
210-
{classes}
210+
classes={{ ...settingsClasses.multiSelectMenu, ...classes.multiSelectMenu }}
211211
matchWidth
212212
bind:open
213213
on:change={onSelectChange}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
{...$$restProps}
4949
classes={{
5050
root: cls('MultiSelectMenu', settingsClasses.root, classes.root, $$restProps.class),
51-
menu: cls('flex flex-col', settingsClasses.menu, classes.menu),
51+
menu: cls('flex flex-col p-2', settingsClasses.menu, classes.menu),
5252
}}
5353
bind:menuItemsEl
5454
>

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

+10-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
<script lang="ts">
2+
import type { ComponentProps } from 'svelte';
3+
24
import Checkbox from './Checkbox.svelte';
35
import { cls } from '../utils/styles.js';
46
import { getComponentClasses } from './theme.js';
@@ -9,7 +11,7 @@
911
1012
export let classes: {
1113
root?: string;
12-
checkbox?: string;
14+
checkbox?: ComponentProps<Checkbox>['classes'];
1315
container?: string;
1416
} = {};
1517
const settingsClasses = getComponentClasses('MultiSelectOption');
@@ -18,7 +20,7 @@
1820
<div
1921
class={cls(
2022
'MultiSelectOption',
21-
'grid grid-cols-[1fr,auto] py-2',
23+
'grid grid-cols-[1fr,auto]',
2224
settingsClasses.root,
2325
classes.root,
2426
$$props.class
@@ -29,7 +31,12 @@
2931
bind:indeterminate
3032
on:change
3133
{disabled}
32-
class={cls(settingsClasses.checkbox, classes.checkbox)}
34+
classes={{
35+
root: 'px-2 rounded hover:bg-surface-content/5',
36+
label: 'py-2',
37+
...settingsClasses.checkbox,
38+
...classes.checkbox,
39+
}}
3340
>
3441
<div
3542
class={cls(

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

+13-6
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@
1313
export let checked = false;
1414
export let required = false;
1515
export let disabled = false;
16+
export let fullWidth = false;
1617
export let size: 'xs' | 'sm' | 'md' | 'lg' = 'sm';
1718
1819
export let classes: {
1920
root?: string;
20-
checkbox?: string;
21+
input?: string;
22+
radio?: string;
2123
label?: string;
2224
icon?: string;
2325
} = {};
@@ -29,7 +31,8 @@
2931
<div
3032
class={cls(
3133
'Radio',
32-
'inline-flex items-center',
34+
fullWidth ? 'flex' : 'inline-flex',
35+
'items-center',
3336
settingsClasses.root,
3437
classes.root,
3538
$$props.class
@@ -42,13 +45,14 @@
4245
bind:group
4346
on:change
4447
{value}
45-
class="peer appearance-none absolute"
48+
class={cls('input', 'peer appearance-none absolute', settingsClasses.input, classes.input)}
4649
{required}
4750
{disabled}
4851
/>
4952
<label
5053
for={id}
5154
class={cls(
55+
'radio',
5256
'inline-grid place-items-center border-2 rounded-full bg-surface-100',
5357
'peer-disabled:opacity-50 transition-shadow duration-300',
5458
!disabled &&
@@ -59,13 +63,14 @@
5963
? 'border-surface-content/30'
6064
: 'border-primary'
6165
: 'border-surface-content/30',
62-
settingsClasses.checkbox,
63-
classes.checkbox
66+
settingsClasses.radio,
67+
classes.radio
6468
)}
6569
>
6670
<Icon
6771
path={mdiCheckboxBlankCircle}
6872
class={cls(
73+
'icon',
6974
'pointer-events-none transition-transform',
7075
disabled ? 'text-surface-content/30 border-surface-content/30' : 'text-primary',
7176
checked ? 'scale-100' : 'scale-0',
@@ -85,7 +90,9 @@
8590
<label
8691
for={id}
8792
class={cls(
88-
'peer-disabled:opacity-50 pl-1',
93+
'label',
94+
'flex-1',
95+
'pl-1 peer-disabled:opacity-50',
8996
{
9097
xs: 'text-xs', // 12px
9198
sm: 'text-sm', // 14px

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

+9
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,15 @@
4848
<Checkbox checked>Label</Checkbox>
4949
</Preview>
5050

51+
<h2>Full width</h2>
52+
53+
<Preview>
54+
<Checkbox fullWidth>One</Checkbox>
55+
<Checkbox fullWidth>Two</Checkbox>
56+
<Checkbox fullWidth>Three</Checkbox>
57+
<Checkbox fullWidth>Four (disabled)</Checkbox>
58+
</Preview>
59+
5160
<h2>Long labels</h2>
5261

5362
<Preview>

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

+5
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ export async function load() {
88
api,
99
source,
1010
pageSource,
11+
related: [
12+
'components/InfiniteScroll',
13+
'components/MultiSelectField',
14+
'components/MultiSelectMenu',
15+
],
1116
},
1217
};
1318
}

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

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export async function load() {
88
api,
99
source,
1010
pageSource,
11+
related: ['components/MultiSelect', 'components/MultiSelectMenu', 'components/TextField'],
1112
},
1213
};
1314
}

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

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export async function load() {
88
api,
99
source,
1010
pageSource,
11+
related: ['components/Menu', 'components/MultiSelect', 'components/MultiSelectField'],
1112
},
1213
};
1314
}

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

+8
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@
3131
<Radio name="label" bind:group value={3}>Third</Radio>
3232
</Preview>
3333

34+
<h2>Full width</h2>
35+
36+
<Preview>
37+
<Radio name="label" bind:group value={1} fullWidth>First</Radio>
38+
<Radio name="label" bind:group value={2} fullWidth>Second</Radio>
39+
<Radio name="label" bind:group value={3} fullWidth>Third</Radio>
40+
</Preview>
41+
3442
<h2>Long labels</h2>
3543

3644
<Preview>

0 commit comments

Comments
 (0)