Merge pull request #46 from metonym/tile

feat(tile): complete RadioTile, forward events
This commit is contained in:
Eric Liu 2019-12-20 09:12:44 -08:00 committed by GitHub
commit 8f1271088f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 153 additions and 102 deletions

View file

@ -43,6 +43,16 @@ Currently, the following components are supported:
- TextInput - TextInput
- TextInputSkeleton - TextInputSkeleton
- PasswordInput - PasswordInput
- Tile
- ClickableTile
- SelectableTile
- ExpandableTile
- TileAboveTheFoldContent
- TileBelowTheFoldContent
- RadioTile
- Tile
- TileGroup
- RadioTile
- Toggle - Toggle
- ToggleSkeleton - ToggleSkeleton
- ToggleSmall - ToggleSmall

View file

@ -5,28 +5,10 @@
export let rel = undefined; export let rel = undefined;
export let light = false; export let light = false;
export let clicked = false; export let clicked = false;
export let props = {}; export let style = undefined;
import { createEventDispatcher, tick } from 'svelte';
import { cx } from '../../lib'; import { cx } from '../../lib';
const dispatch = createEventDispatcher();
async function handleClick(event) {
clicked = !clicked;
await tick();
dispatch('click', event);
}
async function handleKeyDown(event) {
if (event.key === ' ' || event.key === 'Enter') {
clicked = !clicked;
await tick();
}
dispatch('keydown', event);
}
$: _class = cx( $: _class = cx(
'--link', '--link',
'--tile', '--tile',
@ -37,6 +19,23 @@
); );
</script> </script>
<a {...props} class={_class} on:click={handleClick} on:keydown={handleKeyDown} {href} {rel}> <a
class={_class}
on:click
on:click={() => {
clicked = !clicked;
}}
on:keydown
on:keydown={event => {
if (event.key === ' ' || event.key === 'Enter') {
clicked = !clicked;
}
}}
on:mouseover
on:mouseenter
on:mouseleave
{style}
{href}
{rel}>
<slot /> <slot />
</a> </a>

View file

@ -7,9 +7,9 @@
export let tileExpandedIconText = 'Interact to collapse Tile'; export let tileExpandedIconText = 'Interact to collapse Tile';
export let tileMaxHeight = 0; export let tileMaxHeight = 0;
export let tilePadding = 0; export let tilePadding = 0;
export let tabIndex = 0; export let tabindex = '0';
export let light = false; export let light = false;
export let props = {}; export let style = undefined;
import { createEventDispatcher, tick, onMount } from 'svelte'; import { createEventDispatcher, tick, onMount } from 'svelte';
import ChevronDown16 from 'carbon-icons-svelte/lib/ChevronDown16'; import ChevronDown16 from 'carbon-icons-svelte/lib/ChevronDown16';
@ -63,14 +63,19 @@
</script> </script>
<div <div
{...props}
bind:this={tile} bind:this={tile}
style={tileStyle} style={tileStyle}
class={_class} class={_class}
on:click
on:click={handleClick} on:click={handleClick}
on:keypress
on:keypress={handleKeyPress} on:keypress={handleKeyPress}
tabindex={tabIndex} on:mouseover
{id}> on:mouseenter
on:mouseleave
{tabindex}
{id}
{style}>
<div bind:this={tileContent}> <div bind:this={tileContent}>
<div bind:this={aboveTheFold} class={cx('--tile-content')}> <div bind:this={aboveTheFold} class={cx('--tile-content')}>
<slot name="above" /> <slot name="above" />

View file

@ -1,5 +1,4 @@
<script> <script>
// TODO: compose as "children" components
let className = undefined; let className = undefined;
export { className as class }; export { className as class };
export let checked = false; export let checked = false;
@ -7,29 +6,19 @@
export let name = ''; export let name = '';
export let iconDescription = 'Tile checkmark'; export let iconDescription = 'Tile checkmark';
export let value = ''; export let value = '';
export let tabIndex = 0; export let tabindex = '0';
export let light = false; export let light = false;
export let props = {}; export let style = undefined;
import { createEventDispatcher } from 'svelte'; import { getContext } from 'svelte';
import CheckmarkFilled16 from 'carbon-icons-svelte/lib/CheckmarkFilled16'; import CheckmarkFilled16 from 'carbon-icons-svelte/lib/CheckmarkFilled16';
import { cx } from '../../lib'; import { cx } from '../../lib';
const dispatch = createEventDispatcher(); const { addTile, updateSelected, selected } = getContext('TileGroup');
function handleChange(event) { addTile({ id, value, checked });
dispatch('change', event);
}
function handleKeyDown(event) {
if (event.key === ' ' || event.key === 'Enter') {
event.preventDefault();
handleChange(event);
}
dispatch('keydown', event);
}
$: checked = value === $selected.value;
$: _class = cx( $: _class = cx(
'--tile', '--tile',
'--tile--selectable', '--tile--selectable',
@ -40,16 +29,32 @@
</script> </script>
<input <input
{...props}
type="radio" type="radio"
class={cx('--tile-input')} class={cx('--tile-input')}
on:change on:change
on:change={handleChange} on:change={() => {
updateSelected({ id, value });
}}
{id} {id}
{name} {name}
{value} {value}
{checked} /> {checked} />
<label for={id} class={_class} tabindex={tabIndex} on:keydown={handleKeyDown}> <label
for={id}
class={_class}
on:click
on:mouseover
on:mouseenter
on:mouseleave
on:keydown
on:keydown={event => {
if (event.key === ' ' || event.key === 'Enter') {
event.preventDefault();
updateSelected({ id, value });
}
}}
{tabindex}
{style}>
<span class={cx('--tile__checkmark')}> <span class={cx('--tile__checkmark')}>
<CheckmarkFilled16 aria-label={iconDescription} title={iconDescription} /> <CheckmarkFilled16 aria-label={iconDescription} title={iconDescription} />
</span> </span>

View file

@ -1,5 +1,4 @@
<script> <script>
// TODO: emit current selected tile
let className = undefined; let className = undefined;
export { className as class }; export { className as class };
export let selected = false; export let selected = false;
@ -8,40 +7,19 @@
export let title = 'title'; export let title = 'title';
export let name = ''; export let name = '';
export let iconDescription = 'Tile checkmark'; export let iconDescription = 'Tile checkmark';
export let tabIndex = 0; export let tabindex = '0';
export let light = false; export let light = false;
export let props = {}; export let style = undefined;
import { createEventDispatcher, tick } from 'svelte'; import { createEventDispatcher } from 'svelte';
import CheckmarkFilled16 from 'carbon-icons-svelte/lib/CheckmarkFilled16'; import CheckmarkFilled16 from 'carbon-icons-svelte/lib/CheckmarkFilled16';
import { cx } from '../../lib'; import { cx } from '../../lib';
let input = undefined;
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
function handleChange(event) { $: if (selected) {
dispatch('change', event); dispatch('select', id);
} }
async function handleClick(event) {
if (event.target !== input) {
selected = !selected;
await tick();
}
dispatch('click', event);
}
async function handleKeyDown(event) {
if (event.key === ' ' || event.key === 'Enter') {
event.preventDefault();
selected = !selected;
await tick();
}
dispatch('keydown', event);
}
$: _class = cx( $: _class = cx(
'--tile', '--tile',
'--tile--selectable', '--tile--selectable',
@ -52,23 +30,34 @@
</script> </script>
<input <input
bind:this={input}
type="checkbox" type="checkbox"
tabindex={-1} tabindex="-1"
class={cx('--tile-input')} class={cx('--tile-input')}
on:change={handleChange} on:change
checked={selected} checked={selected}
{id} {id}
{value} {value}
{name} {name}
{title} /> {title} />
<label <label
{...props}
for={id} for={id}
class={_class} class={_class}
tabindex={tabIndex} on:click
on:click|preventDefault={handleClick} on:click|preventDefault={() => {
on:keydown={handleKeyDown}> selected = !selected;
}}
on:mouseover
on:mouseenter
on:mouseleave
on:keydown
on:keydown={event => {
if (event.key === ' ' || event.key === 'Enter') {
event.preventDefault();
selected = !selected;
}
}}
{tabindex}
{style}>
<span class={cx('--tile__checkmark')}> <span class={cx('--tile__checkmark')}>
<CheckmarkFilled16 aria-label={iconDescription} title={iconDescription} /> <CheckmarkFilled16 aria-label={iconDescription} title={iconDescription} />
</span> </span>

View file

@ -17,7 +17,7 @@
{ value: 'selected', id: 'tile-3', labelText: 'Selectable Tile' } { value: 'selected', id: 'tile-3', labelText: 'Selectable Tile' }
]; ];
let selected = radioTiles[1].value; let defaultSelected = radioTiles[1];
</script> </script>
<Layout> <Layout>
@ -28,24 +28,14 @@
<ClickableTile {...$$props}>Clickable Tile</ClickableTile> <ClickableTile {...$$props}>Clickable Tile</ClickableTile>
{:else if story === 'multi-select'} {:else if story === 'multi-select'}
<div role="group" aria-label="selectable tiles"> <div role="group" aria-label="selectable tiles">
<SelectableTile id="tile-1" name="tiles" {...$$props}>Multi-select Tile</SelectableTile> <SelectableTile {...$$props} id="tile-1" name="tiles">Multi-select Tile</SelectableTile>
<SelectableTile id="tile-2" name="tiles" {...$$props}>Multi-select Tile</SelectableTile> <SelectableTile {...$$props} id="tile-2" name="tiles">Multi-select Tile</SelectableTile>
<SelectableTile id="tile-3" name="tiles" {...$$props}>Multi-select Tile</SelectableTile> <SelectableTile {...$$props} id="tile-3" name="tiles">Multi-select Tile</SelectableTile>
</div> </div>
{:else if story === 'selectable'} {:else if story === 'selectable'}
<TileGroup legend="Selectable Tile Group"> <TileGroup legend="Selectable Tile Group" bind:defaultSelected>
{#each radioTiles as { value, id, labelText }, i (id)} {#each radioTiles as { value, id, labelText }, i (id)}
<RadioTile <RadioTile {...$$props} {value} {id} {labelText}>Selectable Tile</RadioTile>
{...$$props}
checked={selected === value}
on:change={() => {
selected = value;
}}
{value}
{id}
{labelText}>
Selectable Tile
</RadioTile>
{/each} {/each}
</TileGroup> </TileGroup>
{:else if story === 'expandable'} {:else if story === 'expandable'}

View file

@ -2,13 +2,13 @@
let className = undefined; let className = undefined;
export { className as class }; export { className as class };
export let light = false; export let light = false;
export let props = {}; export let style = undefined;
import { cx } from '../../lib'; import { cx } from '../../lib';
$: _class = cx('--tile', light && '--tile--light', className); const _class = cx('--tile', light && '--tile--light', className);
</script> </script>
<div class={_class} {...props}> <div on:click on:mouseover on:mouseenter on:mouseleave class={_class} {style}>
<slot /> <slot />
</div> </div>

View file

@ -1,7 +1,14 @@
<script> <script>
import { cx } from '../../lib'; import { cx } from '../../lib';
export let style = undefined;
</script> </script>
<span class={cx('--tile-content__above-the-fold')}> <span
on:click
on:mouseover
on:mouseenter
on:mouseleave
class={cx('--tile-content__above-the-fold')}
{style}>
<slot /> <slot />
</span> </span>

View file

@ -1,7 +1,14 @@
<script> <script>
import { cx } from '../../lib'; import { cx } from '../../lib';
export let style = undefined;
</script> </script>
<span class={cx('--tile-content__below-the-fold')}> <span
on:click
on:mouseover
on:mouseenter
on:mouseleave
class={cx('--tile-content__below-the-fold')}
{style}>
<slot /> <slot />
</span> </span>

View file

@ -1,15 +1,54 @@
<script> <script>
let className = undefined; let className = undefined;
export { className as class }; export { className as class };
export let defaultSelected = { value: undefined };
export let disabled = false; export let disabled = false;
export let legend = ''; export let legend = '';
export let style = undefined;
import { createEventDispatcher, setContext } from 'svelte';
import { writable } from 'svelte/store';
import { cx } from '../../lib'; import { cx } from '../../lib';
$: _class = cx('--tile-group', className); const dispatch = createEventDispatcher();
let tiles = [];
let selected = writable(defaultSelected);
setContext('TileGroup', {
selected,
addTile: tile => {
tiles = [...tiles, tile];
},
updateSelected: tile => {
selected.set(tile);
}
});
$: {
const checkedTiles = tiles.filter(tile => tile.checked);
if (checkedTiles.length > 1) {
console.warn('Multiple RadioTiles cannot be checked.');
if (defaultSelected.value) {
console.warn('Using `defaultSelected`:', defaultSelected);
} else {
console.warn('Using `RadioTile`:', checkedTiles[0]);
selected.set(checkedTiles[0]);
}
} else if (checkedTiles.length === 1) {
selected.set(checkedTiles[0]);
tiles = [];
}
defaultSelected = $selected;
dispatch('select', $selected);
}
const _class = cx('--tile-group', className);
</script> </script>
<fieldset class={_class} {disabled}> <fieldset class={_class} {disabled} {style}>
{#if legend} {#if legend}
<legend>{legend}</legend> <legend>{legend}</legend>
{/if} {/if}