|
1 | 1 | <svelte:component
|
2 | 2 | this={component}
|
3 | 3 | bind:this={element}
|
4 |
| - use={[forwardEvents, ...use]} |
5 |
| - forwardEvents={forwardedEvents} |
| 4 | + use={[ |
| 5 | + [ |
| 6 | + Ripple, |
| 7 | + { |
| 8 | + ripple, |
| 9 | + unbounded: false, |
| 10 | + addClass, |
| 11 | + removeClass, |
| 12 | + addStyle, |
| 13 | + }, |
| 14 | + ], |
| 15 | + forwardEvents, |
| 16 | + ...use, |
| 17 | + ]} |
| 18 | + forwardEvents={[ |
| 19 | + 'MDCChipTrailingAction:interaction', |
| 20 | + 'MDCChipTrailingAction:navigation', |
| 21 | + 'SMUI:chip:primary-action:mount', |
| 22 | + 'SMUI:chip:primary-action:unmount', |
| 23 | + 'SMUI:chip:trailing-action:mount', |
| 24 | + ...forwardedEvents, |
| 25 | + ]} |
6 | 26 | class={classMap({
|
7 | 27 | [className]: true,
|
8 | 28 | 'mdc-chip': true,
|
9 | 29 | 'mdc-chip--selected': selected,
|
10 | 30 | 'mdc-chip--touch': touch,
|
| 31 | + ...internalClasses, |
11 | 32 | })}
|
| 33 | + style={Object.entries(internalStyles) |
| 34 | + .map(([name, value]) => `${name}: ${value};`) |
| 35 | + .concat([style]) |
| 36 | + .join(' ')} |
12 | 37 | role="row"
|
13 |
| - on:MDCChip:selection={handleSelection} |
| 38 | + on:transitionend={(event) => instance && instance.handleTransitionEnd(event)} |
| 39 | + on:click={() => instance && instance.handleClick()} |
| 40 | + on:keydown={(event) => instance && instance.handleKeydown(event)} |
| 41 | + on:focusin={(event) => instance && instance.handleFocusIn(event)} |
| 42 | + on:focusout={(event) => instance && instance.handleFocusOut(event)} |
| 43 | + on:MDCChipTrailingAction:interaction={() => |
| 44 | + instance && instance.handleTrailingActionInteraction()} |
| 45 | + on:MDCChipTrailingAction:navigation={(event) => |
| 46 | + instance && instance.handleTrailingActionNavigation(event)} |
| 47 | + on:SMUI:chip:primary-action:mount={(event) => |
| 48 | + (primaryActionAccessor = event.detail)} |
| 49 | + on:SMUI:chip:primary-action:unmount={() => |
| 50 | + (primaryActionAccessor = undefined)} |
| 51 | + on:SMUI:chip:trailing-action:mount={(event) => |
| 52 | + (trailingActionAccessor = event.detail)} |
| 53 | + on:SMUI:chip:trailing-action:unmount={() => |
| 54 | + (trailingActionAccessor = undefined)} |
14 | 55 | {...exclude($$props, [
|
15 | 56 | 'use',
|
16 | 57 | 'class',
|
17 |
| - 'component', |
| 58 | + 'style', |
| 59 | + 'chip', |
18 | 60 | 'ripple',
|
19 | 61 | 'touch',
|
20 |
| - 'selected', |
21 | 62 | 'shouldRemoveOnTrailingIconClick',
|
| 63 | + 'shouldFocusPrimaryActionOnClick', |
| 64 | + 'component', |
22 | 65 | ])}
|
23 | 66 | >
|
24 |
| - {#if ripple} |
25 |
| - <div class="mdc-chip__ripple" /> |
26 |
| - {/if} |
| 67 | + <div class="mdc-chip__ripple" /> |
27 | 68 | <slot />
|
28 | 69 | {#if touch}
|
29 | 70 | <div class="mdc-chip__touch" />
|
30 | 71 | {/if}
|
31 | 72 | </svelte:component>
|
32 | 73 |
|
33 | 74 | <script>
|
34 |
| - import { MDCChip } from '@material/chips'; |
35 |
| - import { onMount, setContext, getContext } from 'svelte'; |
| 75 | + import { MDCChipFoundation } from '@material/chips'; |
| 76 | + import { onMount, setContext, getContext, tick } from 'svelte'; |
36 | 77 | import { writable } from 'svelte/store';
|
37 | 78 | import { get_current_component } from 'svelte/internal';
|
38 | 79 | import {
|
39 | 80 | forwardEventsBuilder,
|
40 | 81 | classMap,
|
41 | 82 | exclude,
|
| 83 | + dispatch, |
42 | 84 | } from '@smui/common/internal.js';
|
| 85 | + import Ripple from '@smui/ripple/bare.js'; |
43 | 86 | import Div from '@smui/common/Div.svelte';
|
44 | 87 |
|
45 | 88 | const forwardedEvents = [
|
|
48 | 91 | 'MDCChip:removal',
|
49 | 92 | 'MDCChip:trailingIconInteraction',
|
50 | 93 | 'MDCChip:navigation',
|
| 94 | + 'SMUI:chip:mount', |
| 95 | + 'SMUI:chip:unmount', |
51 | 96 | ];
|
52 | 97 | const forwardEvents = forwardEventsBuilder(
|
53 | 98 | get_current_component(),
|
|
57 | 102 | export let use = [];
|
58 | 103 | let className = '';
|
59 | 104 | export { className as class };
|
| 105 | + export let style = ''; |
| 106 | + let chipId; |
| 107 | + export { chipId as chip }; |
60 | 108 | export let ripple = true;
|
61 | 109 | export let touch = false;
|
62 |
| - export let selected = false; |
63 | 110 | export let shouldRemoveOnTrailingIconClick = true;
|
| 111 | + export let shouldFocusPrimaryActionOnClick = true; |
64 | 112 |
|
65 | 113 | let element;
|
66 |
| - let chip; |
| 114 | + let instance; |
| 115 | + let internalClasses = {}; |
| 116 | + let leadingIconClasses = {}; |
| 117 | + let internalStyles = {}; |
| 118 | + const initialSelectedStore = getContext('SMUI:chip:initialSelected'); |
| 119 | + let selected = $initialSelectedStore; |
| 120 | + let primaryActionAccessor; |
| 121 | + let trailingActionAccessor; |
| 122 | + let accessor = { |
| 123 | + chipId, |
| 124 | + get selected() { |
| 125 | + return selected; |
| 126 | + }, |
| 127 | + focusPrimaryAction, |
| 128 | + focusTrailingAction, |
| 129 | + removeFocus, |
| 130 | + setSelectedFromChipSet, |
| 131 | + }; |
67 | 132 |
|
68 | 133 | export let component = Div;
|
69 | 134 |
|
|
78 | 143 | const isSelectedStore = writable(selected);
|
79 | 144 | $: $isSelectedStore = selected;
|
80 | 145 | setContext('SMUI:chip:isSelected', isSelectedStore);
|
| 146 | + const leadingIconClassesStore = writable(leadingIconClasses); |
| 147 | + $: $leadingIconClassesStore = leadingIconClasses; |
| 148 | + setContext('SMUI:chip:leadingIconClasses', leadingIconClassesStore); |
81 | 149 |
|
82 |
| - const selectedStore = getContext('SMUI:chip:selected'); |
83 |
| - let previousSelected = selected; |
84 |
| - $: if (chip && ($selectedStore || previousSelected !== selected)) { |
85 |
| - if (selected !== chip.selected) { |
86 |
| - if (previousSelected !== selected) { |
87 |
| - // Update MDC on Svelte selected change. |
88 |
| - chip.selected = selected; |
89 |
| - } else { |
90 |
| - // Update selected on MDC selection change. |
91 |
| - selected = chip.selected; |
92 |
| - } |
93 |
| - } |
94 |
| - previousSelected = selected; |
| 150 | + if (!chipId) { |
| 151 | + throw new Error( |
| 152 | + 'The chip property is required! It should be passed down from the Set to the Chip.' |
| 153 | + ); |
95 | 154 | }
|
96 | 155 |
|
97 | 156 | $: if (
|
98 |
| - chip && |
99 |
| - chip.shouldRemoveOnTrailingIconClick !== shouldRemoveOnTrailingIconClick |
| 157 | + instance && |
| 158 | + instance.getShouldRemoveOnTrailingIconClick() !== |
| 159 | + shouldRemoveOnTrailingIconClick |
100 | 160 | ) {
|
101 |
| - chip.shouldRemoveOnTrailingIconClick = shouldRemoveOnTrailingIconClick; |
| 161 | + instance.setShouldRemoveOnTrailingIconClick( |
| 162 | + shouldRemoveOnTrailingIconClick |
| 163 | + ); |
| 164 | + } |
| 165 | +
|
| 166 | + $: if (instance) { |
| 167 | + instance.setShouldFocusPrimaryActionOnClick( |
| 168 | + shouldFocusPrimaryActionOnClick |
| 169 | + ); |
102 | 170 | }
|
103 | 171 |
|
104 | 172 | onMount(() => {
|
105 |
| - element.setChip = setChip; |
| 173 | + instance = new MDCChipFoundation({ |
| 174 | + addClass, |
| 175 | + addClassToLeadingIcon: addLeadingIconClass, |
| 176 | + eventTargetHasClass: (target, className) => |
| 177 | + target ? target.classList.contains(className) : false, |
| 178 | + focusPrimaryAction: () => { |
| 179 | + if (primaryActionAccessor) { |
| 180 | + primaryActionAccessor.focus(); |
| 181 | + } |
| 182 | + }, |
| 183 | + focusTrailingAction: () => { |
| 184 | + if (trailingActionAccessor) { |
| 185 | + trailingActionAccessor.focus(); |
| 186 | + } |
| 187 | + }, |
| 188 | + getAttribute: (attr) => getElement().getAttribute(attr), |
| 189 | + getCheckmarkBoundingClientRect: () => { |
| 190 | + const target = getElement().querySelector('.mdc-chip__checkmark'); |
| 191 | + if (target) { |
| 192 | + return target.getBoundingClientRect(); |
| 193 | + } |
| 194 | + return null; |
| 195 | + }, |
| 196 | + getComputedStyleValue: getStyle, |
| 197 | + getRootBoundingClientRect: () => getElement().getBoundingClientRect(), |
| 198 | + hasClass, |
| 199 | + hasLeadingIcon: () => { |
| 200 | + const target = getElement().querySelector('.mdc-chip__icon--leading'); |
| 201 | + return !!target; |
| 202 | + }, |
| 203 | + isRTL: () => |
| 204 | + getComputedStyle(getElement()).getPropertyValue('direction') === 'rtl', |
| 205 | + isTrailingActionNavigable: () => { |
| 206 | + if (trailingActionAccessor) { |
| 207 | + return trailingActionAccessor.isNavigable(); |
| 208 | + } |
| 209 | + return false; |
| 210 | + }, |
| 211 | + notifyInteraction: () => |
| 212 | + dispatch(getElement(), 'MDCChip:interaction', { chipId }), |
| 213 | + notifyNavigation: (key, source) => |
| 214 | + dispatch(getElement(), 'MDCChip:navigation', { chipId, key, source }), |
| 215 | + notifyRemoval: (removedAnnouncement) => { |
| 216 | + dispatch(getElement(), 'MDCChip:removal', { |
| 217 | + chipId, |
| 218 | + removedAnnouncement, |
| 219 | + }); |
| 220 | + }, |
| 221 | + notifySelection: (selected, shouldIgnore) => |
| 222 | + dispatch(getElement(), 'MDCChip:selection', { |
| 223 | + chipId, |
| 224 | + selected, |
| 225 | + shouldIgnore, |
| 226 | + }), |
| 227 | + notifyTrailingIconInteraction: () => |
| 228 | + dispatch(getElement(), 'MDCChip:trailingIconInteraction', { chipId }), |
| 229 | + notifyEditStart: () => { |
| 230 | + /* Not Implemented. */ |
| 231 | + }, |
| 232 | + notifyEditFinish: () => { |
| 233 | + /* Not Implemented. */ |
| 234 | + }, |
| 235 | + removeClass, |
| 236 | + removeClassFromLeadingIcon: removeLeadingIconClass, |
| 237 | + removeTrailingActionFocus: () => { |
| 238 | + if (trailingActionAccessor) { |
| 239 | + trailingActionAccessor.removeFocus(); |
| 240 | + } |
| 241 | + }, |
| 242 | + setPrimaryActionAttr: (attr, value) => { |
| 243 | + if (primaryActionAccessor) { |
| 244 | + primaryActionAccessor.addAttr(attr, value); |
| 245 | + } |
| 246 | + }, |
| 247 | + setStyleProperty: addStyle, |
| 248 | + }); |
| 249 | +
|
| 250 | + dispatch(getElement(), 'SMUI:chip:mount', accessor); |
| 251 | +
|
| 252 | + instance.init(); |
| 253 | +
|
| 254 | + return () => { |
| 255 | + dispatch(getElement(), 'SMUI:chip:unmount', accessor); |
| 256 | +
|
| 257 | + instance.destroy(); |
| 258 | + }; |
106 | 259 | });
|
107 | 260 |
|
108 |
| - function setChip(component) { |
109 |
| - chip = component; |
110 |
| - if (!ripple) { |
111 |
| - chip.ripple && chip.ripple.destroy(); |
| 261 | + function hasClass(className) { |
| 262 | + return className in internalClasses |
| 263 | + ? internalClasses[className] |
| 264 | + : getElement().classList.contains(className); |
| 265 | + } |
| 266 | +
|
| 267 | + function addClass(className) { |
| 268 | + if (!internalClasses[className]) { |
| 269 | + internalClasses[className] = true; |
| 270 | + } |
| 271 | + } |
| 272 | +
|
| 273 | + function removeClass(className) { |
| 274 | + if (!(className in internalClasses) || internalClasses[className]) { |
| 275 | + internalClasses[className] = false; |
| 276 | + } |
| 277 | + } |
| 278 | +
|
| 279 | + function addLeadingIconClass(className) { |
| 280 | + if (!leadingIconClasses[className]) { |
| 281 | + leadingIconClasses[className] = true; |
| 282 | + } |
| 283 | + } |
| 284 | +
|
| 285 | + function removeLeadingIconClass(className) { |
| 286 | + if (!(className in leadingIconClasses) || leadingIconClasses[className]) { |
| 287 | + leadingIconClasses[className] = false; |
| 288 | + } |
| 289 | + } |
| 290 | +
|
| 291 | + function addStyle(name, value) { |
| 292 | + if (internalStyles[name] !== value) { |
| 293 | + internalStyles[name] = value; |
112 | 294 | }
|
113 |
| - selected = chip.selected; |
114 | 295 | }
|
115 | 296 |
|
116 |
| - function handleSelection(e) { |
117 |
| - selected = e.detail.selected; |
| 297 | + function getStyle(name) { |
| 298 | + return name in internalStyles |
| 299 | + ? internalStyles[name] |
| 300 | + : getComputedStyle(getElement()).getPropertyValue(name); |
| 301 | + } |
| 302 | +
|
| 303 | + function setSelectedFromChipSet(value, shouldNotifyClients) { |
| 304 | + selected = value; |
| 305 | + instance.setSelectedFromChipSet(selected, shouldNotifyClients); |
| 306 | + } |
| 307 | +
|
| 308 | + function focusPrimaryAction() { |
| 309 | + instance.focusPrimaryAction(); |
| 310 | + } |
| 311 | +
|
| 312 | + function focusTrailingAction() { |
| 313 | + instance.focusTrailingAction(); |
| 314 | + } |
| 315 | +
|
| 316 | + function removeFocus() { |
| 317 | + instance.removeFocus(); |
118 | 318 | }
|
119 | 319 |
|
120 | 320 | export function getElement() {
|
|
0 commit comments