Skip to content

Commit c551452

Browse files
committed
Merge branch 'edu-rosado-master'
2 parents da2d137 + 78950d4 commit c551452

File tree

7 files changed

+151
-47
lines changed

7 files changed

+151
-47
lines changed

MIGRATING.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,14 @@ https://github.com/material-components/material-components-web/blob/master/CHANG
1414
- Internally, they are replaced with the `SmuiElement` component exported from `@smui/common`. It takes a `tag` prop and creates an element dynamically with that tag name. You shouldn't ever need to use `SmuiElement` directly.
1515
- The "\*ComponentDev" types (like `MenuComponentDev`) are gone. You can now use the component as its type. Components that can take a `component` or `tag` prop (like `Button`) have required generic arguments that you can get around by using "InstanceType", like `let button: InstanceType<typeof Button>;`.
1616
- If you're using `classAdderBuilder`, you need to use `keyof SmuiElementMap` instead of `string` as its generic argument.
17+
- The `dispatch` function in `@smui/common` will now throw an error if either the `Event` object is not available or the `element` is not provided.
1718

1819
## Changes
1920

2021
### Components
2122

23+
- Autocomplete
24+
- The `SMUIAutocomplete:selected` event is now cancelable.
2225
- Banner
2326
- New `autoClose` prop.
2427
- New `SMUIBanner:actionClicked` event. It is fired when an action is clicked and `autoClose` is `false`.
@@ -32,6 +35,8 @@ https://github.com/material-components/material-components-web/blob/master/CHANG
3235
- List
3336
- New `disabledItemsFocusable` prop.
3437
- New `wrapper` prop on Item. This should be used for items that only act as the container of a nested list.
38+
- Menu
39+
- New `SMUIMenu:closedProgrammatically` event.
3540
- Menu Surface
3641
- New `SMUIMenuSurface:opening` event.
3742
- New `openBottomBias` prop.

packages/autocomplete/src/Autocomplete.svelte

Lines changed: 56 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
'smui-autocomplete__menu': true,
3636
})}
3737
managed
38-
open={menuOpen}
38+
bind:open={menuOpen}
3939
bind:anchorElement={element}
4040
anchor={menu$anchor}
4141
anchorCorner={menu$anchorCorner}
@@ -212,45 +212,36 @@
212212
!!matches.length &&
213213
!(matches.length === 1 && matches[0] === value)));
214214
215-
let previousText: string | undefined = undefined;
215+
let previousText = text;
216216
$: if (previousText !== text) {
217217
if (!combobox && value != null && getOptionLabel(value) !== text) {
218218
deselectOption(value, false);
219219
}
220220
221-
(async () => {
222-
loading = true;
223-
error = false;
224-
try {
225-
const searchResult = await search(text);
226-
if (searchResult !== false) {
227-
matches = searchResult;
228-
if (selectOnExactMatch) {
229-
const exactMatch = matches.find(
230-
(match) => getOptionLabel(match) === text
231-
);
232-
if (exactMatch && value !== exactMatch) {
233-
selectOption(exactMatch);
234-
}
235-
}
236-
}
237-
} catch (e: any) {
238-
error = true;
239-
}
240-
loading = false;
241-
})();
221+
performSearch();
242222
243223
previousText = text;
244224
}
245225
226+
$: if (options) {
227+
// Set search results on init and refresh search results when `options` is
228+
// changed.
229+
performSearch();
230+
}
231+
246232
let previousValue = value;
247233
$: if (!combobox && previousValue !== value) {
248234
// If the value changes from outside, update the text.
249235
text = getOptionLabel(value);
250236
previousValue = value;
251-
} else if (combobox) {
252-
// If the text changes, update value if we're a combobox.
237+
} else if (combobox && previousValue !== value) {
238+
// An update came from the outside.
239+
text = value;
240+
previousValue = value;
241+
} else if (combobox && value !== text) {
242+
// An update came from the user.
253243
value = text;
244+
previousValue = value;
254245
}
255246
256247
let previousFocusedIndex: number | undefined = undefined;
@@ -288,32 +279,70 @@
288279
previousFocusedIndex = focusedIndex;
289280
}
290281
282+
async function performSearch() {
283+
loading = true;
284+
error = false;
285+
try {
286+
const searchResult = await search(text);
287+
if (searchResult !== false) {
288+
matches = searchResult;
289+
if (selectOnExactMatch) {
290+
const exactMatch = matches.find(
291+
(match) => getOptionLabel(match) === text
292+
);
293+
if (exactMatch && value !== exactMatch) {
294+
selectOption(exactMatch);
295+
}
296+
}
297+
}
298+
} catch (e: any) {
299+
error = true;
300+
}
301+
loading = false;
302+
}
303+
291304
function handleListAccessor(event: CustomEvent<SMUIListAccessor>) {
292305
if (!listAccessor) {
293306
listAccessor = event.detail;
294307
}
295308
}
296309
297310
function selectOption(option: any, setText = true) {
311+
const event = dispatch(element, 'SMUIAutocomplete:selected', option, {
312+
bubbles: true,
313+
cancelable: true,
314+
});
315+
316+
if (event.defaultPrevented) {
317+
return;
318+
}
319+
298320
if (setText) {
299321
text = getOptionLabel(option);
300322
}
301323
value = option;
302324
if (!setText) {
303325
previousValue = option;
304326
}
305-
dispatch(element, 'SMUIAutocomplete:selected', option);
306327
}
307328
308329
function deselectOption(option: any, setText = true) {
330+
const event = dispatch(element, 'SMUIAutocomplete:deselected', option, {
331+
bubbles: true,
332+
cancelable: true,
333+
});
334+
335+
if (event.defaultPrevented) {
336+
return;
337+
}
338+
309339
if (setText) {
310340
text = '';
311341
}
312342
value = undefined;
313343
if (!setText) {
314344
previousValue = undefined;
315345
}
316-
dispatch(element, 'SMUIAutocomplete:deselected', option);
317346
}
318347
319348
function toggleOption(option: any) {

packages/common/src/internal/dispatch.ts

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,29 @@ export function dispatch<T extends any = any>(
66
/** This is an internal thing used by SMUI to duplicate some SMUI events as MDC events. */
77
duplicateEventForMDC = false
88
) {
9-
if (typeof Event !== 'undefined' && element) {
10-
const event: CustomEvent<T> = new CustomEvent(eventType, {
11-
...eventInit,
12-
detail,
13-
});
14-
element?.dispatchEvent(event);
15-
if (duplicateEventForMDC && eventType.startsWith('SMUI')) {
16-
const duplicateEvent: CustomEvent<T> = new CustomEvent(
17-
eventType.replace(/^SMUI/g, () => 'MDC'),
18-
{
19-
...eventInit,
20-
detail,
21-
}
22-
);
23-
element?.dispatchEvent(duplicateEvent);
24-
if (duplicateEvent.defaultPrevented) {
25-
event.preventDefault();
9+
if (typeof Event === 'undefined') {
10+
throw new Error('Event not defined.');
11+
}
12+
if (!element) {
13+
throw new Error('Tried to dipatch event without element.');
14+
}
15+
const event: CustomEvent<T> = new CustomEvent(eventType, {
16+
...eventInit,
17+
detail,
18+
});
19+
element?.dispatchEvent(event);
20+
if (duplicateEventForMDC && eventType.startsWith('SMUI')) {
21+
const duplicateEvent: CustomEvent<T> = new CustomEvent(
22+
eventType.replace(/^SMUI/g, () => 'MDC'),
23+
{
24+
...eventInit,
25+
detail,
2626
}
27+
);
28+
element?.dispatchEvent(duplicateEvent);
29+
if (duplicateEvent.defaultPrevented) {
30+
event.preventDefault();
2731
}
28-
return event;
2932
}
33+
return event;
3034
}

packages/menu/src/Menu.svelte

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,10 @@
7878
listAccessor.getAttributeFromElementIndex(index, attr),
7979
elementContainsClass: (element, className) =>
8080
element.classList.contains(className),
81-
closeSurface: (skipRestoreFocus) =>
82-
menuSurfaceAccessor.closeProgrammatic(skipRestoreFocus),
81+
closeSurface: (skipRestoreFocus) => {
82+
menuSurfaceAccessor.closeProgrammatic(skipRestoreFocus);
83+
dispatch(getElement(), 'SMUIMenu:closedProgrammatically');
84+
},
8385
getElementIndex: (element) =>
8486
listAccessor
8587
.getOrderedList()

packages/site/src/routes/demo/autocomplete/+page.svelte

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,13 @@
3333
Adding entries
3434
</Demo>
3535

36+
<Demo component={AddToList} file="autocomplete/_AddToList.svelte">
37+
Add entries to a list
38+
<svelte:fragment slot="subtitle">
39+
Leave the menu open and don't fill the textbox upon selection.
40+
</svelte:fragment>
41+
</Demo>
42+
3643
<Demo component={Async} file="autocomplete/_Async.svelte">
3744
Async options loading
3845
<svelte:fragment slot="subtitle">
@@ -54,6 +61,7 @@
5461
import Combobox from './_Combobox.svelte';
5562
import Objects from './_Objects.svelte';
5663
import AddEntries from './_AddEntries.svelte';
64+
import AddToList from './_AddToList.svelte';
5765
import Async from './_Async.svelte';
5866
import Manual from './_Manual.svelte';
5967
</script>
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<div>
2+
<div class="status">
3+
<pre style="display: inline-block;">Selected:</pre>
4+
<Set style="display: inline-block;" bind:chips={selected} let:chip>
5+
<Chip {chip}>
6+
<Text tabindex={0}>{chip}</Text>
7+
<TrailingAction icon$class="material-icons">cancel</TrailingAction>
8+
</Chip>
9+
</Set>
10+
</div>
11+
12+
<Autocomplete
13+
bind:this={selector}
14+
options={available}
15+
bind:value
16+
label="Fruit"
17+
on:SMUIAutocomplete:selected={handleSelection}
18+
/>
19+
</div>
20+
21+
<script lang="ts">
22+
import Autocomplete from '@smui-extra/autocomplete';
23+
24+
import Chip, { Set, TrailingAction, Text } from '@smui/chips';
25+
26+
let fruits = ['Apple', 'Orange', 'Banana', 'Mango'];
27+
let selected: string[] = [];
28+
29+
$: available = fruits.filter((value) => !selected.includes(value));
30+
31+
let selector: Autocomplete;
32+
let value = '';
33+
34+
function handleSelection(event: CustomEvent<string>) {
35+
// Don't actually select the item.
36+
event.preventDefault();
37+
38+
// You could also set value back to '' here.
39+
selected.push(event.detail);
40+
// Make sure the chips get updated.
41+
selected = selected;
42+
43+
selector.focus();
44+
}
45+
</script>

packages/site/src/routes/demo/autocomplete/_Combobox.svelte

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,21 @@
11
<div>
22
<Autocomplete combobox options={fruits} bind:value label="Fruit" />
33
<pre class="status">Selected: {value || ''}</pre>
4+
5+
<div style="margin-top: 1em;">
6+
<div>Programmatically select:</div>
7+
<Button on:click={() => (value = 'Dragonfruit')}>
8+
<Label>Dragonfruit</Label>
9+
</Button>
10+
<Button on:click={() => (value = 'Elderberry')}>
11+
<Label>Elderberry</Label>
12+
</Button>
13+
</div>
414
</div>
515

616
<script lang="ts">
717
import Autocomplete from '@smui-extra/autocomplete';
18+
import Button, { Label } from '@smui/button';
819
920
let fruits = [
1021
'Apple',

0 commit comments

Comments
 (0)