Skip to content

Commit 1d9f88d

Browse files
committed
feat: migrate components in chips to runes
1 parent 69055f6 commit 1d9f88d

17 files changed

+684
-316
lines changed

MIGRATING.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ This doc contains information that will help you migrate your code from an older
1313
- The deprecated "MDC" events have been removed. All event names should be migrated to the corresponding "SMUI" event names.
1414
- The "Fixation" theme now uses Tahoma as its large header font.
1515
- Select no longer defaults the value to an empty string, meaning you must either set the value given to it to an empty string or provide a key function that returns an empty string for an undefined value.
16-
- The TabBar now exprects a `tab` snippet instead of using `let:tab`.
16+
- TabBar now expects a `tab` snippet instead of using `let:tab`.
17+
- Chips' Set now expects a `chip` snippet instead of using `let:chip`.
18+
- Chips' Set key function is now required to return `string`, not `string | number`.
1719

1820
## Changes
1921

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ Svelte 5 Runes mode is being migrated to slowly. This is the todo list of compon
167167
- Inputs and Controls
168168
- [ ] Autocomplete
169169
- [x] Checkbox
170-
- [ ] Chips
170+
- [x] Chips
171171
- [ ] Chip Input
172172
- [x] Floating Label
173173
- [x] Form Field

packages/chips/src/Checkmark.svelte

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<svelte:options runes={false} />
1+
<svelte:options runes />
22

33
<span
44
bind:this={element}
@@ -7,33 +7,46 @@
77
[className]: true,
88
'mdc-chip__checkmark': true,
99
})}
10-
{...$$restProps}
10+
{...restProps}
1111
>
12-
<svg class="mdc-chip__checkmark-svg" viewBox="-2 -3 30 30">
13-
<path
14-
class="mdc-chip__checkmark-path"
15-
fill="none"
16-
stroke="black"
17-
d="M1.73,12.91 8.1,19.28 22.79,4.59"
18-
/>
19-
</svg>
12+
{#if children}
13+
{@render children?.()}
14+
{:else}
15+
<svg class="mdc-chip__checkmark-svg" viewBox="-2 -3 30 30">
16+
<path
17+
class="mdc-chip__checkmark-path"
18+
fill="none"
19+
stroke="black"
20+
d="M1.73,12.91 8.1,19.28 22.79,4.59"
21+
/>
22+
</svg>
23+
{/if}
2024
</span>
2125

2226
<script lang="ts">
27+
import type { Snippet } from 'svelte';
2328
import type { SmuiAttrs } from '@smui/common';
2429
import type { ActionArray } from '@smui/common/internal';
2530
import { classMap, useActions } from '@smui/common/internal';
2631
2732
type OwnProps = {
33+
/**
34+
* An array of Action or [Action, ActionProps] to be applied to the element.
35+
*/
2836
use?: ActionArray;
37+
/**
38+
* A space separated list of CSS classes.
39+
*/
2940
class?: string;
30-
};
31-
type $$Props = OwnProps & SmuiAttrs<'span', keyof OwnProps>;
3241
33-
// Remember to update $$Props if you add/remove/rename props.
34-
export let use: ActionArray = [];
35-
let className = '';
36-
export { className as class };
42+
children?: Snippet;
43+
};
44+
let {
45+
use = [],
46+
class: className = '',
47+
children,
48+
...restProps
49+
}: OwnProps & SmuiAttrs<'span', keyof OwnProps> = $props();
3750
3851
let element: HTMLSpanElement;
3952

packages/chips/src/Chip.svelte

Lines changed: 115 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
<svelte:options runes={false} />
1+
<svelte:options runes />
22

3-
<svelte:component
4-
this={component}
3+
<MyComponent
54
{tag}
65
bind:this={element}
76
use={[
@@ -29,61 +28,74 @@
2928
.concat([style])
3029
.join(' ')}
3130
role="row"
32-
{...$$restProps}
31+
{...restProps}
3332
ontransitionend={(e: TransitionEvent) => {
3433
if (instance) {
3534
instance.handleTransitionEnd(e);
3635
}
37-
$$restProps.ontransitionend?.(e);
36+
restProps.ontransitionend?.(e);
3837
}}
3938
onclick={(e: MouseEvent) => {
4039
if (instance) {
4140
instance.handleClick();
4241
}
43-
$$restProps.onclick?.(e);
42+
restProps.onclick?.(e);
4443
}}
4544
onkeydown={(e: KeyboardEvent) => {
4645
if (instance) {
4746
instance.handleKeydown(e);
4847
}
49-
$$restProps.onkeydown?.(e);
48+
restProps.onkeydown?.(e);
5049
}}
5150
onfocusin={(e: FocusEvent) => {
5251
if (instance) {
5352
instance.handleFocusIn(e);
5453
}
55-
$$restProps.onfocusin?.(e);
54+
restProps.onfocusin?.(e);
5655
}}
5756
onfocusout={(e: FocusEvent) => {
5857
if (instance) {
5958
instance.handleFocusOut(e);
6059
}
61-
$$restProps.onfocusout?.(e);
60+
restProps.onfocusout?.(e);
6261
}}
63-
onSMUIChipTrailingActionInteraction={(e: CustomEvent) => {
62+
onSMUIChipTrailingActionInteraction={(
63+
e: CustomEvent<{
64+
trigger: InteractionTrigger;
65+
}> & { currentTarget: EventTarget & SmuiElementMap[TagName] },
66+
) => {
6467
if (instance) {
6568
instance.handleTrailingActionInteraction();
6669
}
67-
$$restProps.onSMUIChipTrailingActionInteraction?.(e);
70+
restProps.onSMUIChipTrailingActionInteraction?.(e);
6871
}}
69-
onSMUIChipTrailingActionNavigation={(e: CustomEvent) => {
72+
onSMUIChipTrailingActionNavigation={(
73+
e: CustomEvent<{ key: string }> & {
74+
currentTarget: EventTarget & SmuiElementMap[TagName];
75+
},
76+
) => {
7077
if (instance) {
7178
instance.handleTrailingActionNavigation(e);
7279
}
73-
$$restProps.onSMUIChipTrailingActionNavigation?.(e);
80+
restProps.onSMUIChipTrailingActionNavigation?.(e);
7481
}}
7582
>
7683
{#if ripple && !$nonInteractive}
7784
<div class="mdc-chip__ripple"></div>
7885
{/if}
79-
<slot />
86+
{@render children?.()}
8087
{#if touch}
8188
<div class="mdc-chip__touch"></div>
8289
{/if}
83-
</svelte:component>
90+
</MyComponent>
8491

85-
<script lang="ts" generics="TagName extends SmuiEveryElement = 'div'">
92+
<script
93+
lang="ts"
94+
generics="ChipKey extends Object | string | number, TagName extends SmuiEveryElement = 'div'"
95+
>
96+
import type { InteractionTrigger } from '@material/chips/deprecated/trailingaction/constants';
8697
import { deprecated } from '@material/chips';
98+
import type { Snippet } from 'svelte';
8799
import { onMount, setContext, getContext } from 'svelte';
88100
import { writable } from 'svelte/store';
89101
import type { ActionArray } from '@smui/common/internal';
@@ -104,40 +116,73 @@
104116
const { MDCChipFoundation } = deprecated;
105117
106118
type OwnProps = {
119+
/**
120+
* An array of Action or [Action, ActionProps] to be applied to the element.
121+
*/
107122
use?: ActionArray;
123+
/**
124+
* A space separated list of CSS classes.
125+
*/
108126
class?: string;
127+
/**
128+
* A list of CSS styles.
129+
*/
109130
style?: string;
110-
chip: any;
131+
/**
132+
* The chip object this chip is for.
133+
*/
134+
chip: ChipKey;
135+
/**
136+
* Whether to show a ripple animation.
137+
*/
111138
ripple?: boolean;
139+
/**
140+
* Whether to use touch styling
141+
*/
112142
touch?: boolean;
143+
/**
144+
* Whether this chip should be removed when user clicks the trailing icon.
145+
*/
113146
shouldRemoveOnTrailingIconClick?: boolean;
147+
/**
148+
* Whether primary action should focus when user clicks the chip.
149+
*/
114150
shouldFocusPrimaryActionOnClick?: boolean;
151+
/**
152+
* The component to use to render the element.
153+
*/
115154
component?: SmuiComponent<SmuiElementMap[TagName]>;
155+
/**
156+
* The tag name of the element to create.
157+
*/
116158
tag?: TagName;
159+
160+
children?: Snippet;
117161
};
118-
type $$Props = OwnProps & SmuiAttrs<TagName, keyof OwnProps>;
119-
120-
// Remember to update $$Props if you add/remove/rename props.
121-
export let use: ActionArray = [];
122-
let className = '';
123-
export { className as class };
124-
export let style = '';
125-
let chipId: any;
126-
export { chipId as chip };
127-
export let ripple = true;
128-
export let touch = false;
129-
export let shouldRemoveOnTrailingIconClick = true;
130-
export let shouldFocusPrimaryActionOnClick = true;
162+
let {
163+
use = [],
164+
class: className = '',
165+
style = '',
166+
chip: chipId,
167+
ripple = true,
168+
touch = false,
169+
shouldRemoveOnTrailingIconClick = true,
170+
shouldFocusPrimaryActionOnClick = true,
171+
component: MyComponent = SmuiElement,
172+
tag = 'div' as TagName,
173+
children,
174+
...restProps
175+
}: OwnProps & SmuiAttrs<TagName, keyof OwnProps> = $props();
131176
132177
let element: ReturnType<SmuiComponent<SmuiElementMap[TagName]>>;
133-
let instance: deprecated.MDCChipFoundation;
134-
let internalClasses: { [k: string]: boolean } = {};
135-
let leadingIconClasses: { [k: string]: boolean } = {};
136-
let internalStyles: { [k: string]: string } = {};
178+
let instance: deprecated.MDCChipFoundation | undefined = $state();
179+
let internalClasses: { [k: string]: boolean } = $state({});
180+
let leadingIconClasses: { [k: string]: boolean } = $state({});
181+
let internalStyles: { [k: string]: string } = $state({});
137182
const initialSelectedStore = getContext<SvelteStore<boolean>>(
138183
'SMUI:chips:chip:initialSelected',
139184
);
140-
let selected = $initialSelectedStore;
185+
let selected = $state($initialSelectedStore);
141186
let primaryActionAccessor: SMUIChipsPrimaryActionAccessor | undefined =
142187
undefined;
143188
let trailingActionAccessor: SMUIChipsTrailingActionAccessor | undefined =
@@ -148,50 +193,53 @@
148193
const choice = getContext<SvelteStore<boolean>>('SMUI:chips:choice');
149194
const index = getContext<SvelteStore<number>>('SMUI:chips:chip:index');
150195
151-
export let component: SmuiComponent<SmuiElementMap[TagName]> = SmuiElement;
152-
export let tag: SmuiEveryElement | undefined =
153-
component === SmuiElement ? 'div' : undefined;
154-
155196
const shouldRemoveOnTrailingIconClickStore = writable(
156197
shouldRemoveOnTrailingIconClick,
157198
);
158-
$: $shouldRemoveOnTrailingIconClickStore = shouldRemoveOnTrailingIconClick;
199+
$effect(() => {
200+
$shouldRemoveOnTrailingIconClickStore = shouldRemoveOnTrailingIconClick;
201+
});
159202
setContext(
160203
'SMUI:chips:chip:shouldRemoveOnTrailingIconClick',
161204
shouldRemoveOnTrailingIconClickStore,
162205
);
206+
163207
const isSelectedStore = writable(selected);
164-
$: $isSelectedStore = selected;
208+
$effect(() => {
209+
$isSelectedStore = selected;
210+
});
165211
setContext('SMUI:chips:chip:isSelected', isSelectedStore);
212+
166213
const leadingIconClassesStore = writable(leadingIconClasses);
167-
$: $leadingIconClassesStore = leadingIconClasses;
214+
$effect(() => {
215+
$leadingIconClassesStore = leadingIconClasses;
216+
});
168217
setContext('SMUI:chips:chip:leadingIconClasses', leadingIconClassesStore);
218+
169219
setContext(
170220
'SMUI:chips:chip:focusable',
171221
($choice && selected) || $index === 0,
172222
);
173223
174-
if (!chipId) {
175-
throw new Error(
176-
'The chip property is required! It should be passed down from the Set to the Chip.',
177-
);
178-
}
179-
180-
$: if (
181-
instance &&
182-
instance.getShouldRemoveOnTrailingIconClick() !==
183-
shouldRemoveOnTrailingIconClick
184-
) {
185-
instance.setShouldRemoveOnTrailingIconClick(
186-
shouldRemoveOnTrailingIconClick,
187-
);
188-
}
224+
$effect(() => {
225+
if (
226+
instance &&
227+
instance.getShouldRemoveOnTrailingIconClick() !==
228+
shouldRemoveOnTrailingIconClick
229+
) {
230+
instance.setShouldRemoveOnTrailingIconClick(
231+
shouldRemoveOnTrailingIconClick,
232+
);
233+
}
234+
});
189235
190-
$: if (instance) {
191-
instance.setShouldFocusPrimaryActionOnClick(
192-
shouldFocusPrimaryActionOnClick,
193-
);
194-
}
236+
$effect(() => {
237+
if (instance) {
238+
instance.setShouldFocusPrimaryActionOnClick(
239+
shouldFocusPrimaryActionOnClick,
240+
);
241+
}
242+
});
195243
196244
setContext(
197245
'SMUI:chips:primary-action:mount',
@@ -316,7 +364,7 @@
316364
return () => {
317365
SMUIChipsChipUnmount && SMUIChipsChipUnmount(accessor);
318366
319-
instance.destroy();
367+
instance?.destroy();
320368
};
321369
});
322370
@@ -372,19 +420,19 @@
372420
shouldNotifyClients: boolean,
373421
) {
374422
selected = value;
375-
instance.setSelectedFromChipSet(selected, shouldNotifyClients);
423+
instance?.setSelectedFromChipSet(selected, shouldNotifyClients);
376424
}
377425
378426
function focusPrimaryAction() {
379-
instance.focusPrimaryAction();
427+
instance?.focusPrimaryAction();
380428
}
381429
382430
function focusTrailingAction() {
383-
instance.focusTrailingAction();
431+
instance?.focusTrailingAction();
384432
}
385433
386434
function removeFocus() {
387-
instance.removeFocus();
435+
instance?.removeFocus();
388436
}
389437
390438
export function getElement() {

0 commit comments

Comments
 (0)