Merge pull request #54 from metonym/progress-indicator

feat(component): add ProgressIndicator
This commit is contained in:
Eric Liu 2019-12-21 11:06:27 -08:00 committed by GitHub
commit 2ed45f8fae
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 244 additions and 0 deletions

View file

@ -39,6 +39,9 @@ Currently, the following components are supported:
- NotificationIcon
- NotificationTextDetails
- OrderedList
- ProgressIndicator
- ProgressIndicatorSkeleton
- ProgressStep
- RadioButton
- RadioButtonSkeleton
- Search

View file

@ -0,0 +1,24 @@
<script>
let className = undefined;
export { className as class };
export let vertical = false;
export let style = undefined;
import { cx } from '../../lib';
const _class = cx('--progress', vertical && '--progress--vertical', '--skeleton', className);
</script>
<ul on:click on:mouseover on:mouseenter on:mouseleave class={_class} {style}>
{#each [0, 1, 2, 3] as item, i (item)}
<li class={cx('--progress-step', '--progress-step--incomplete')}>
<div class={cx('--progress-step-button', '--progress-step-button--unclickable')}>
<svg>
<path d="M 7, 7 m -7, 0 a 7,7 0 1,0 14,0 a 7,7 0 1,0 -14,0" />
</svg>
<p class={cx('--progress-label')} />
<span class={cx('--progress-line')} />
</div>
</li>
{/each}
</ul>

View file

@ -0,0 +1,55 @@
<script>
export let story = undefined;
import Layout from '../../internal/ui/Layout.svelte';
import ProgressIndicator from './ProgressIndicator.svelte';
import ProgressStep from './ProgressStep.svelte';
import ProgressIndicatorSkeleton from './ProgressIndicator.Skeleton.svelte';
</script>
<Layout>
<div>
{#if story === 'skeleton'}
<ProgressIndicatorSkeleton {...$$props} />
{:else if story === 'interactive'}
<ProgressIndicator {...$$props}>
<ProgressStep description="Step 1: Register a onChange event" let:props>
<div {...props}>Click me</div>
</ProgressStep>
<ProgressStep
label="Really long label"
description="The progress indicator will listen for clicks on the steps" />
<ProgressStep
label="Tooltip and really long label"
description="The progress indicator will listen for clicks on the steps" />
</ProgressIndicator>
{:else}
<ProgressIndicator {...$$props}>
<ProgressStep
label="First step"
description="Step 1: Getting started with Carbon Design System"
secondaryLabel="Optional label" />
<ProgressStep
label="Second step with tooltip"
description="Step 2: Getting started with Carbon Design System"
secondaryLabel="Optional label">
Label
</ProgressStep>
<ProgressStep
label="Third step with tooltip"
description="Step 3: Getting started with Carbon Design System">
Label
</ProgressStep>
<ProgressStep
label="Fourth step"
description="Step 4: Getting started with Carbon Design System"
invalid
secondaryLabel="Example invalid step" />
<ProgressStep
label="Fifth step"
description="Step 5: Getting started with Carbon Design System"
disabled />
</ProgressIndicator>
{/if}
</div>
</Layout>

View file

@ -0,0 +1,29 @@
import { withKnobs, boolean, text, number } from '@storybook/addon-knobs';
import Component from './ProgressIndicator.Story.svelte';
export default { title: 'ProgressIndicator', decorators: [withKnobs] };
export const Default = () => ({
Component,
props: {
currentIndex: number('Current progress (currentIndex)', 1),
vertical: boolean('Vertical orientation (vertical)', false)
}
});
export const Interactive = () => ({
Component,
props: {
story: 'interactive',
currentIndex: number('Current progress (currentIndex)', 1),
vertical: boolean('Vertical orientation (vertical)', false)
}
});
export const Skeleton = () => ({
Component,
props: {
story: 'skeleton',
vertical: boolean('Vertical orientation (vertical)', false)
}
});

View file

@ -0,0 +1,39 @@
<script>
let className = undefined;
export { className as class };
export let currentIndex = 0;
export let vertical = false;
export let style = undefined;
import { createEventDispatcher, setContext } from 'svelte';
import { writable, derived } from 'svelte/store';
import { cx } from '../../lib';
const dispatch = createEventDispatcher();
const _class = cx('--progress', vertical && '--progress--vertical', className);
let steps = writable([]);
let stepsById = derived(steps, $steps => $steps.reduce((a, c) => ({ ...a, [c.id]: c }), {}));
setContext('ProgressIndicator', {
steps,
stepsById,
add: step => {
steps.update(_ => [
..._,
{
...step,
index: _.length,
current: _.length === currentIndex,
complete: _.length <= currentIndex
}
]);
},
change: index => {
dispatch('change', index);
}
});
</script>
<ul on:click on:mouseover on:mouseenter on:mouseleave class={_class} {style}>
<slot />
</ul>

View file

@ -0,0 +1,82 @@
<script>
let className = undefined;
export { className as class };
export let label = '';
export let current = false;
export let complete = false;
export let description = '';
export let invalid = false;
export let secondaryLabel = '';
export let disabled = false;
export let style = undefined;
import { createEventDispatcher, getContext } from 'svelte';
import Warning16 from 'carbon-icons-svelte/lib/Warning16';
import CheckmarkOutline16 from 'carbon-icons-svelte/lib/CheckmarkOutline16';
import { cx } from '../../lib';
const dispatch = createEventDispatcher();
const id = Math.random();
const { stepsById, add, change } = getContext('ProgressIndicator');
add({ id, disabled });
$: step = $stepsById[id];
$: {
current = step.current;
complete = step.complete;
}
$: _class = cx(
'--progress-step',
current && '--progress-step--current',
complete && '--progress-step--complete',
!complete && !current && '--progress-step--incomplete',
disabled && '--progress-step--disabled',
className
);
$: _buttonClass = cx('--progress-step-button', current && '--progress-step-button--unclickable');
</script>
<li aria-disabled={disabled} class={_class} {style}>
<div
role="button"
class={_buttonClass}
tabindex={current ? '-1' : '0'}
on:click={() => {
change(step.index);
}}
on:mouseover
on:mouseenter
on:mouseleave
on:keydown
on:keydown={event => {
if (event.key === ' ' || event.key === 'Enter') {
change(step.index);
}
}}>
{#if invalid}
<Warning16 class={cx('--progress__warning')} />
{:else if current}
<svg>
<path d="M 7, 7 m -7, 0 a 7,7 0 1,0 14,0 a 7,7 0 1,0 -14,0" />
<title>{description}</title>
</svg>
{:else if complete}
<CheckmarkOutline16 title={description} />
{:else}
<svg>
<title>{description}</title>
<path
d="M8 1C4.1 1 1 4.1 1 8s3.1 7 7 7 7-3.1 7-7-3.1-7-7-7zm0 13c-3.3 0-6-2.7-6-6s2.7-6 6-6 6
2.7 6 6-2.7 6-6 6z" />
</svg>
{/if}
<slot props={{ class: cx('--progress-label') }}>
<p class={cx('--progress-label')}>{label}</p>
</slot>
{#if secondaryLabel}
<p class={cx('--progress-optional')}>{secondaryLabel}</p>
{/if}
<span class={cx('--progress-line')} />
</div>
</li>

View file

@ -0,0 +1,5 @@
import ProgressIndicator from './ProgressIndicator.svelte';
export default ProgressIndicator;
export { default as ProgressIndicatorSkeleton } from './ProgressIndicator.Skeleton.svelte';
export { default as ProgressStep } from './ProgressStep.svelte';

View file

@ -20,6 +20,10 @@ import {
NotificationTextDetails
} from './components/Notification';
import OrderedList from './components/OrderedList';
import ProgressIndicator, {
ProgressIndicatorSkeleton,
ProgressStep
} from './components/ProgressIndicator';
import RadioButton, { RadioButtonSkeleton } from './components/RadioButton';
import Search, { SearchSkeleton } from './components/Search';
import SkeletonPlaceholder from './components/SkeletonPlaceholder';
@ -71,6 +75,9 @@ export {
NotificationTextDetails,
OrderedList,
PasswordInput,
ProgressIndicator,
ProgressIndicatorSkeleton,
ProgressStep,
RadioButton,
RadioButtonSkeleton,
Search,