feat: initial commit

This commit is contained in:
Eric Liu 2019-12-15 11:20:52 -08:00
commit 72dc38ea56
119 changed files with 14925 additions and 1 deletions

View file

@ -0,0 +1,39 @@
<script>
let className = undefined;
export { className as class };
export let open = true;
export let count = 4;
export let props = {};
import ChevronRight16 from 'carbon-icons-svelte/lib/ChevronRight16';
import { cx } from '../../lib';
import SkeletonText from '../SkeletonText';
const _class = cx('--accordion', '--skeleton', className);
const skeletonItems = Array.from({ length: open ? count - 1 : count });
</script>
<ul class={_class} {...props}>
{#if open}
<li class={cx('--accordion__item', '--accordion__item--active')}>
<span class={cx('--accordion__heading')}>
<ChevronRight16 class={cx('--accordion__arrow')} />
<SkeletonText class={cx('--accordion__title')} />
</span>
<div class={cx('--accordion__content')}>
<SkeletonText width="90%" />
<SkeletonText width="80%" />
<SkeletonText width="95%" />
</div>
</li>
{/if}
{#each skeletonItems as item}
<li class={cx('--accordion__item')}>
<span class={cx('--accordion__heading')}>
<ChevronRight16 class={cx('--accordion__arrow')} />
<SkeletonText class={cx('--accordion__title')} />
</span>
</li>
{/each}
</ul>

View file

@ -0,0 +1,55 @@
<script>
export let story = undefined;
export let title = undefined;
export let open = undefined;
export let count = undefined;
import Layout from '../../internal/ui/Layout.svelte';
import Accordion from './Accordion.svelte';
import AccordionItem from './AccordionItem.svelte';
import AccordionSkeleton from './Accordion.Skeleton.svelte';
</script>
<Layout>
{#if story === 'skeleton'}
<div style="width: 500px">
<AccordionSkeleton {open} {count} />
</div>
{:else}
<Accordion>
<AccordionItem {title} {open}>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt
ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation
ullamco laboris nisi ut aliquip ex ea commodo consequat.
</p>
</AccordionItem>
<AccordionItem title="Section 2 title">
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt
ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation
ullamco laboris nisi ut aliquip ex ea commodo consequat.
</p>
</AccordionItem>
<AccordionItem title="Section 3 title">
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt
ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation
ullamco laboris nisi ut aliquip ex ea commodo consequat.
</p>
</AccordionItem>
<AccordionItem>
<div slot="title">
Section 4 title (
<em>the title can be a node</em>
)
</div>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt
ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation
ullamco laboris nisi ut aliquip ex ea commodo consequat.
</p>
</AccordionItem>
</Accordion>
{/if}
</Layout>

View file

@ -0,0 +1,21 @@
import { withKnobs, text, boolean, number } from '@storybook/addon-knobs';
import Component from './Accordion.Story.svelte';
export default { title: 'Accordion', decorators: [withKnobs] };
export const Default = () => ({
Component,
props: {
title: text('The title (title)', 'Section 1 title'),
open: boolean('Open the section (open)', false)
}
});
export const Skeleton = () => ({
Component,
props: {
story: 'skeleton',
open: boolean('Show first item opened (open)', true),
count: number('Set number of items (count)', 4)
}
});

View file

@ -0,0 +1,11 @@
<script>
let className = undefined;
export { className as class };
export let props = {};
import { cx } from '../../lib';
</script>
<ul {...props} class={cx('--accordion', className)}>
<slot />
</ul>

View file

@ -0,0 +1,62 @@
<script>
// NOTE: no 'renderExpando'; use 'expando' named slot
// TODO: change 'expando' to something more intuitive?
let className = undefined;
export { className as class };
export let title = undefined;
export let iconDescription = 'Expand/Collapse';
export let open = false;
export let props = {};
import { createEventDispatcher } from 'svelte';
import ChevronRight16 from 'carbon-icons-svelte/lib/ChevronRight16';
import { cx } from '../../lib';
const dispatch = createEventDispatcher();
let animation = undefined;
function handleAnimationEnd(event) {
animation = undefined;
}
function handleClick(event) {
animation = open ? 'collapsing' : 'expanding';
open = !open;
dispatch('headingclick', { open, event });
}
function handleKeyDown(event) {
if (open && event.key === 'Escape') {
open = false;
}
}
$: _class = cx(
'--accordion__item',
open && '--accordion__item--active',
animation && `--accordion__item--${animation}`,
className
);
$: accordionItemProps = {
type: 'button',
class: cx('--accordion__heading'),
title: iconDescription,
'aria-expanded': open,
onClick: handleClick,
onKeyDown: handleKeyDown
};
</script>
<li class={_class} {...props} on:animationend on:animationend={handleAnimationEnd}>
<slot name="expando" props={accordionItemProps}>
<button {...accordionItemProps} on:click={handleClick} on:keydown={handleKeyDown}>
<ChevronRight16 class={cx('--accordion__arrow')} aria-label={iconDescription} />
<div class={cx('--accordion__title')}>
<slot name="title">{title}</slot>
</div>
</button>
</slot>
<div class={cx('--accordion__content')}>
<slot />
</div>
</li>

View file

@ -0,0 +1,5 @@
import Accordion from './Accordion.svelte';
export default Accordion;
export { default as AccordionItem } from './AccordionItem.svelte';
export { default as AccordionSkeleton } from './Accordion.Skeleton.svelte';

View file

@ -0,0 +1,17 @@
<script>
let className = undefined;
export { className as class };
export let props = {};
import { cx } from '../../lib';
const _class = cx('--breadcrumb', '--skeleton', className);
</script>
<div {...props} class={_class}>
{#each [0, 1, 2] as item, i (item)}
<div class={cx('--breadcrumb-item')}>
<span class={cx('--link')}>&nbsp;</span>
</div>
{/each}
</div>

View file

@ -0,0 +1,39 @@
<script>
export let story = undefined;
export let noTrailingSlash = undefined;
import Layout from '../../internal/ui/Layout.svelte';
import Breadcrumb from './Breadcrumb.svelte';
import BreadcrumbItem from './BreadcrumbItem.svelte';
import BreadcrumbSkeleton from './Breadcrumb.Skeleton.svelte';
</script>
<Layout>
{#if story === 'current page'}
<Breadcrumb noTrailingSlash>
<BreadcrumbItem let:props>
<a {...props} href="/#">Breadcrumb 1</a>
</BreadcrumbItem>
<BreadcrumbItem href="#">Breadcrumb 2</BreadcrumbItem>
<BreadcrumbItem href="#" isCurrentPage>Breadcrumb 3</BreadcrumbItem>
</Breadcrumb>
{:else if story === 'current page with aria-current'}
<Breadcrumb noTrailingSlash>
<BreadcrumbItem let:props>
<a {...props} href="/#">Breadcrumb 1</a>
</BreadcrumbItem>
<BreadcrumbItem href="#">Breadcrumb 2</BreadcrumbItem>
<BreadcrumbItem href="#" aria-current="page">Breadcrumb 3</BreadcrumbItem>
</Breadcrumb>
{:else if story === 'skeleton'}
<BreadcrumbSkeleton />
{:else}
<Breadcrumb {noTrailingSlash}>
<BreadcrumbItem let:props>
<a {...props} href="/#">Breadcrumb 1</a>
</BreadcrumbItem>
<BreadcrumbItem href="#">Breadcrumb 2</BreadcrumbItem>
<BreadcrumbItem href="#">Breadcrumb 3</BreadcrumbItem>
</Breadcrumb>
{/if}
</Layout>

View file

@ -0,0 +1,24 @@
import { withKnobs, boolean } from '@storybook/addon-knobs';
import Component from './Breadcrumb.Story.svelte';
export default { title: 'Breadcrumb', decorators: [withKnobs] };
export const Default = () => ({
Component,
props: { noTrailingSlash: boolean('No Trailing Slash (noTrailingSlash)', false) }
});
export const Skeleton = () => ({
Component,
props: { story: 'skeleton' }
});
export const CurrentPage = () => ({
Component,
props: { story: 'current page' }
});
export const CurrentPageWithAriaCurrent = () => ({
Component,
props: { story: 'current page with aria-current' }
});

View file

@ -0,0 +1,16 @@
<script>
let className = undefined;
export { className as class };
export let noTrailingSlash = false;
import { cx } from '../../lib';
const ariaLabel = $$props['aria-label'] || 'Breadcrumb';
const _class = cx('--breadcrumb', noTrailingSlash && '--breadcrumb--no-trailing-slash');
</script>
<nav class={className} aria-label={ariaLabel}>
<ol class={_class}>
<slot />
</ol>
</nav>

View file

@ -0,0 +1,31 @@
<script>
let className = undefined;
export { className as class };
export let href = undefined;
export let isCurrentPage = false;
export let props = {};
import { cx } from '../../lib';
import Link from '../Link';
const ariaCurrent = $$props['aria-current'];
$: _class = cx(
'--breadcrumb-item',
isCurrentPage && ariaCurrent !== 'page' && '--breadcrumb-item--current',
className
);
$: itemProps = { 'aria-current': ariaCurrent, class: cx('--link') };
</script>
{#if href}
<li class={_class} {...props}>
<Link {href} props={itemProps}>
<slot />
</Link>
</li>
{:else}
<li class={_class} {...props}>
<slot props={itemProps} />
</li>
{/if}

View file

@ -0,0 +1,5 @@
import Breadcrumb from './Breadcrumb.svelte';
export default Breadcrumb;
export { default as BreadcrumbItem } from './BreadcrumbItem.svelte';
export { default as BreadcrumbSkeleton } from './Breadcrumb.Skeleton.svelte';

View file

@ -0,0 +1,17 @@
<script>
let className = undefined;
export { className as class };
export let small = false;
export let href = undefined;
export let props = {};
import { cx } from '../../lib';
const _class = cx('--skeleton', '--btn', small && '--btn--sm', className);
</script>
{#if href}
<a {...props} class={_class} {href} role="button">{''}</a>
{:else}
<div {...props} class={_class} />
{/if}

View file

@ -0,0 +1,73 @@
<script>
export let story = undefined;
import { cx } from '../../lib';
import Layout from '../../internal/ui/Layout.svelte';
import Button from './Button.svelte';
import ButtonSkeleton from './Button.Skeleton.svelte';
import Add16 from 'carbon-icons-svelte/lib/Add16';
const {
kind,
disabled,
size,
renderIcon,
iconDescription,
small,
tooltipPosition,
tooltipAlignment
} = $$props;
const regularProps = {
kind,
disabled,
size,
iconDescription,
small
};
const iconOnlyProps = {
kind,
disabled,
size,
renderIcon: Add16,
iconDescription,
tooltipPosition,
tooltipAlignment
};
const setProps = { disabled, small, size, iconDescription };
</script>
<Layout>
<div>
{#if story === 'skeleton'}
<ButtonSkeleton />
&nbsp;
<ButtonSkeleton href="#" />
&nbsp;
<ButtonSkeleton small />
{:else if story === 'inline'}
<Button />
{:else if story === 'icon-only buttons'}
<Button {...iconOnlyProps} hasIconOnly />
{:else if story === 'set of buttons'}
<div class={cx('--btn-set')}>
<Button kind="secondary" {...setProps}>Secondary button</Button>
<Button kind="primary" {...setProps}>Primary button</Button>
</div>
{:else}
<Button {...regularProps}>Button</Button>
&nbsp;
<Button {...regularProps} href="#">Link</Button>
&nbsp;
<Button {...regularProps} as let:props>
<p {...props}>Element</p>
</Button>
&nbsp;
<Button {...regularProps} as let:props>
<a href="#link" {...props}>Custom component</a>
</Button>
{/if}
</div>
</Layout>

View file

@ -0,0 +1,64 @@
import { withKnobs, select, boolean, text } from '@storybook/addon-knobs';
import Component from './Button.Story.svelte';
export default { title: 'Button', decorators: [withKnobs] };
// TODO: add selectable renderIcon for Default, Icon-only stories
const kinds = {
'Primary button (primary)': 'primary',
'Secondary button (secondary)': 'secondary',
'Danger button (danger)': 'danger',
'Ghost button (ghost)': 'ghost'
};
const sizes = {
Default: 'default',
Field: 'field',
Small: 'small'
};
export const Default = () => ({
Component,
props: {
kind: select('Button kind (kind)', kinds, 'primary'),
disabled: boolean('Disabled (disabled)', false),
size: select('Button size (size)', sizes, 'default'),
iconDescription: text('Icon description (iconDescription)', 'Button icon'),
small: boolean('Small (small) - Deprecated in favor of `size`', false)
}
});
export const IconOnlyButtons = () => ({
Component,
props: {
story: 'icon-only buttons',
kind: select('Button kind (kind)', kinds, 'primary'),
disabled: boolean('Disabled (disabled)', false),
size: select('Button size (size)', sizes, 'default'),
iconDescription: text('Icon description (iconDescription)', 'Button icon'),
tooltipPosition: select(
'Tooltip position (tooltipPosition)',
['top', 'right', 'bottom', 'left'],
'bottom'
),
tooltipAlignment: select(
'Tooltip alignment (tooltipAlignment)',
['start', 'center', 'end'],
'center'
)
}
});
export const SetOfButtons = () => ({
Component,
props: {
story: 'set of buttons',
disabled: boolean('Disabled (disabled)', false),
small: boolean('Small (small)', false),
size: select('Button size (size)', sizes, 'default'),
iconDescription: text('Icon description (iconDescription)', 'Button icon')
}
});
export const Skeleton = () => ({ Component, props: { story: 'skeleton' } });

View file

@ -0,0 +1,82 @@
<script>
let className = undefined;
export { className as class };
export let as = undefined;
export let disabled = false;
export let size = 'default';
export let small = false;
export let kind = 'primary';
export let href = undefined;
export let tabindex = '0';
export let type = 'button';
export let renderIcon = undefined;
export let iconDescription = undefined;
export let hasIconOnly = false;
export let tooltipPosition = undefined;
export let tooltipAlignment = undefined;
export let props = {};
import { cx } from '../../lib';
const _class = cx(
'--btn',
size === 'field' && '--btn--field',
(size === 'small' || small) && '--btn--sm',
kind === 'primary' && '--btn--primary',
kind === 'danger' && '--btn--danger',
kind === 'secondary' && '--btn--secondary',
kind === 'ghost' && '--btn--ghost',
kind === 'danger--primary' && '--btn--danger--primary',
kind === 'tertiary' && '--btn--tertiary',
disabled && '--btn--disabled',
hasIconOnly && '--btn--icon-only',
hasIconOnly && '--tooltip__trigger',
hasIconOnly && '--tooltip--a11y',
hasIconOnly && tooltipPosition && `--tooltip--${tooltipPosition}`,
hasIconOnly && tooltipAlignment && `--tooltip--align-${tooltipAlignment}`,
className
);
const buttonProps = {
...props,
tabindex,
class: _class,
disabled,
type: href && !disabled ? undefined : type,
role: 'button',
href
};
</script>
{#if as}
<slot props={buttonProps} />
{:else}
{#if href && !disabled}
<a {...buttonProps} {href} on:click on:mouseover on:mouseenter on:mouseleave>
{#if hasIconOnly}
<span class={cx('--assistive-text')}>{iconDescription}</span>
{/if}
<slot />
{#if renderIcon}
<svelte:component
this={renderIcon}
aria-hidden="true"
class={cx('--btn__icon')}
aria-label={iconDescription} />
{/if}
</a>
{:else}
<button {...buttonProps} on:click on:mouseover on:mouseenter on:mouseleave>
{#if hasIconOnly}
<span class={cx('--assistive-text')}>{iconDescription}</span>
{/if}
<slot />
{#if renderIcon}
<svelte:component
this={renderIcon}
aria-hidden="true"
class={cx('--btn__icon')}
aria-label={iconDescription} />
{/if}
</button>
{/if}
{/if}

View file

@ -0,0 +1,4 @@
import Button from './Button.svelte';
export default Button;
export { default as ButtonSkeleton } from './Button.Skeleton.svelte';

View file

@ -0,0 +1,13 @@
<script>
let className = undefined;
export { className as class };
export let props = {};
import { cx } from '../../lib';
const _class = cx('--form-item', '--checkbox-wrapper', className);
</script>
<div {...props} class={_class}>
<span class={cx('--checkbox-label', '--skeleton')} />
</div>

View file

@ -0,0 +1,35 @@
<script>
export let story = undefined;
const { labelText, indeterminate, disabled, hideLabel, wrapperClassName } = $$props;
import { cx } from '../../lib';
import Layout from '../../internal/ui/Layout.svelte';
import Checkbox from './Checkbox.svelte';
import CheckboxSkeleton from './Checkbox.Skeleton.svelte';
const checkboxProps = {
labelText,
indeterminate,
disabled,
hideLabel,
wrapperClassName
};
</script>
<Layout>
{#if story === 'skeleton'}
<CheckboxSkeleton />
{:else if story === 'unchecked'}
<fieldset class={cx('--fieldset')}>
<legend class={cx('--label')}>Checkbox heading</legend>
<Checkbox {...checkboxProps} id="checkbox-label-1" />
<Checkbox {...checkboxProps} id="checkbox-label-2" />
</fieldset>
{:else}
<fieldset class={cx('--fieldset')}>
<legend class={cx('--label')}>Checkbox heading</legend>
<Checkbox {...checkboxProps} checked id="checkbox-label-1" />
<Checkbox {...checkboxProps} checked id="checkbox-label-2" />
</fieldset>
{/if}
</Layout>

View file

@ -0,0 +1,32 @@
import { withKnobs, boolean, text } from '@storybook/addon-knobs';
import Component from './Checkbox.Story.svelte';
export default { title: 'Checkbox', decorators: [withKnobs] };
export const Checked = () => ({
Component,
props: {
labelText: text('Label text (labelText)', 'Checkbox label'),
indeterminate: boolean('Intermediate (indeterminate)', false),
disabled: boolean('Disabled (disabled)', false),
hideLabel: boolean('No label (hideLabel)', false),
wrapperClass: text('Wrapper CSS class name (wrapperClass)', '')
}
});
export const Unchecked = () => ({
Component,
props: {
story: 'unchecked',
labelText: text('Label text (labelText)', 'Checkbox label'),
indeterminate: boolean('Intermediate (indeterminate)', false),
disabled: boolean('Disabled (disabled)', false),
hideLabel: boolean('No label (hideLabel)', false),
wrapperClass: text('Wrapper CSS class name (wrapperClass)', '')
}
});
export const Skeleton = () => ({
Component,
props: { story: 'skeleton' }
});

View file

@ -0,0 +1,41 @@
<script>
let className = undefined;
export { className as class };
export let checked = false;
export let indeterminate = false;
export let disabled = false;
export let id = undefined;
export let labelText = undefined;
export let hideLabel = false;
export let title = '';
export let wrapperClassName = undefined;
export { wrapperClassName as wrapperClass };
export let props = {};
import { createEventDispatcher } from 'svelte';
import { cx } from '../../lib';
const dispatch = createEventDispatcher();
const _labelClass = cx('--checkbox-label', className);
const _innerLabelClass = cx('--checkbox-label-text', hideLabel && '--visually-hidden');
const _wrapperClass = cx('--form-item', '--checkbox-wrapper', wrapperClassName);
function handleChange(event) {
dispatch('change', { checked: event.target.checked, id, event });
}
</script>
<div class={_wrapperClass}>
<input
{...props}
type="checkbox"
class={cx('--checkbox')}
on:change={handleChange}
{indeterminate}
{disabled}
{checked}
{id} />
<label for={id} class={_labelClass} title={title || null}>
<span class={_innerLabelClass}>{labelText}</span>
</label>
</div>

View file

@ -0,0 +1,4 @@
import Checkbox from './Checkbox.svelte';
export default Checkbox;
export { default as CheckboxSkeleton } from './Checkbox.Skeleton.svelte';

View file

@ -0,0 +1,32 @@
<script>
let className = undefined;
export { className as class };
export let type = 'single';
export let props = {};
import { cx } from '../../lib';
const _class = cx(
'--snippet',
'--skeleton',
type === 'single' && '--snippet--single',
type === 'multi' && '--snippet--multi',
className
);
</script>
{#if type === 'single'}
<div {...props} class={_class}>
<div class={cx('--snippet-container')}>
<span />
</div>
</div>
{:else if type === 'multi'}
<div {...props} class={_class}>
<div class={cx('--snippet-container')}>
<span />
<span />
<span />
</div>
</div>
{/if}

View file

@ -0,0 +1,57 @@
<script>
export let story = undefined;
const { light, feedback, copyLabel, copyButtonDescription, showLessText, showMoreText } = $$props;
import Layout from '../../internal/ui/Layout.svelte';
import CodeSnippet from './CodeSnippet.svelte';
import CodeSnippetSkeleton from './CodeSnippet.Skeleton.svelte';
const inlineProps = { light, feedback, copyLabel };
const singleLineProps = {
feedback,
copyButtonDescription,
'aria-label': $$props['aria-label']
};
const multiLineProps = { feedback, showLessText, showMoreText };
</script>
<Layout>
<div>
{#if story === 'skeleton'}
<div style="width: 800px">
<CodeSnippetSkeleton type="single" props={{ style: 'margin-bottom: 8px' }} />
<CodeSnippetSkeleton type="multi" />
</div>
{:else if story === 'inline'}
<CodeSnippet type="inline" {...inlineProps}>{'node -v'}</CodeSnippet>
{:else if story === 'single line'}
<CodeSnippet type="single" {...singleLineProps}>
{'node -v Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis, veritatis voluptate id incidunt molestiae officia possimus, quasi itaque alias, architecto hic, dicta fugit? Debitis delectus quidem explicabo vitae fuga laboriosam!'}
</CodeSnippet>
{:else if story === 'multi line'}
<CodeSnippet type="multi" {...multiLineProps}>
{`@mixin grid-container {
width: 100%;
padding-right: padding(mobile);
padding-left: padding(mobile);
@include breakpoint(bp--xs--major) {
padding-right: padding(xs);
padding-left: padding(xs);
}
}
$z-indexes: (
modal : 9000,
overlay : 8000,
dropdown : 7000,
header : 6000,
footer : 5000,
hidden : - 1,
overflowHidden: - 1,
floating: 10000
);`}
</CodeSnippet>
{/if}
</div>
</Layout>

View file

@ -0,0 +1,39 @@
import { withKnobs, boolean, text } from '@storybook/addon-knobs';
import Component from './CodeSnippet.Story.svelte';
export default { title: 'CodeSnippet', decorators: [withKnobs] };
export const Inline = () => ({
Component,
props: {
story: 'inline',
light: boolean('Light variant (light)', false),
feedback: text('Feedback text (feedback)', 'Feedback Enabled 👍'),
copyLabel: text('ARIA label for the snippet/copy button (copyLabel)', 'copyable code snippet')
}
});
export const SingleLine = () => ({
Component,
props: {
story: 'single line',
feedback: text('Feedback text (feedback)', 'Feedback Enabled 👍'),
copyButtonDescription: text(
'Copy icon description (copyButtonDescription)',
'copyable code snippet'
),
'aria-label': text('ARIA label of the container (ariaLabel)', 'Container label')
}
});
export const MultiLine = () => ({
Component,
props: {
story: 'multi line',
feedback: text('Feedback text (feedback)', 'Feedback Enabled 👍'),
showMoreText: text('Text for "show more" button (showMoreText)', 'Show more'),
showLessText: text('Text for "show less" button (showLessText)', 'Show less')
}
});
export const Skeleton = () => ({ Component, props: { story: 'skeleton' } });

View file

@ -0,0 +1,83 @@
<script>
let className = undefined;
export { className as class };
export let type = 'single';
export let feedback = undefined;
export let copyButtonDescription = undefined;
export let copyLabel = undefined;
export let showMoreText = 'Show more';
export let showLessText = 'Show less';
export let light = false;
export let props = {};
import { onMount } from 'svelte';
import ChevronDown16 from 'carbon-icons-svelte/lib/ChevronDown16';
import { cx } from '../../lib';
import Button from '../Button';
import Copy from '../Copy';
import CopyButton from '../CopyButton';
const id = Math.random();
let codeRef = undefined;
let expandedCode = false;
let shouldShowMoreLessBtn = false;
onMount(() => {
if (codeRef) {
const { height } = codeRef.getBoundingClientRect();
shouldShowMoreLessBtn = type === 'multi' && height > 255;
}
});
$: _class = cx(
'--snippet',
type && `--snippet--${type}`,
expandedCode && '--snippet--expand',
light && '--snippet--light',
className
);
$: expandCodeBtnText = expandedCode ? showLessText : showMoreText;
</script>
{#if type === 'inline'}
<Copy
on:click
aria-label={copyLabel || $$props['aria-label']}
aria-describedby={id}
class={_class}
{feedback}
{props}>
<code {id}>
<slot />
</code>
</Copy>
{:else}
<div {...props} class={_class}>
<div
role="textbox"
tabindex="0"
class={cx('--snippet-container')}
aria-label={$$props['aria-label'] || copyLabel || 'code-snippet'}>
<code>
<pre bind:this={codeRef}>
<slot />
</pre>
</code>
</div>
<CopyButton on:click {feedback} iconDescription={copyButtonDescription} />
{#if shouldShowMoreLessBtn}
<Button
kind="ghost"
size="small"
class={cx('--snippet-btn--expand')}
on:click={() => {
expandedCode = !expandedCode;
}}>
<span class={cx('--snippet-btn--text')}>{expandCodeBtnText}</span>
<ChevronDown16
aria-label={expandCodeBtnText}
class={cx('--icon-chevron--down', '--snippet__icon')} />
</Button>
{/if}
</div>
{/if}

View file

@ -0,0 +1,4 @@
import CodeSnippet from './CodeSnippet.svelte';
export default CodeSnippet;
export { default as CodeSnippetSkeleton } from './CodeSnippet.Skeleton.svelte';

View file

@ -0,0 +1,45 @@
<script>
let className = undefined;
export { className as class };
export let feedback = 'Copied!';
export let feedbackTimeout = 2000;
export let props = {};
import { createEventDispatcher, onDestroy } from 'svelte';
import { cx } from '../../lib';
const dispatch = createEventDispatcher();
let timeoutId = undefined;
let showFeedback = false;
function handleClick(event) {
showFeedback = true;
timeoutId = setTimeout(() => {
showFeedback = false;
}, feedbackTimeout);
}
onDestroy(() => {
if (timeoutId !== undefined) {
clearTimeout(timeoutId);
timeoutId = undefined;
}
});
$: _class = cx('--btn--copy__feedback', showFeedback && '--btn--copy__feedback--displayed');
</script>
<button
{...props}
type="button"
class={className}
on:click
on:click={handleClick}
on:mouseover
on:mouseenter
on:mouseleave>
<slot />
<div class={_class} data-feedback={feedback} />
</button>

View file

@ -0,0 +1,3 @@
import Copy from './Copy.svelte';
export default Copy;

View file

@ -0,0 +1,10 @@
<script>
import Layout from '../../internal/ui/Layout.svelte';
import CopyButton from './CopyButton.svelte';
</script>
<Layout>
<div style="position: relative;">
<CopyButton {...$$props} />
</div>
</Layout>

View file

@ -0,0 +1,13 @@
import { withKnobs, text, number } from '@storybook/addon-knobs';
import Component from './CopyButton.Story.svelte';
export default { title: 'CopyButton', decorators: [withKnobs] };
export const Default = () => ({
Component,
props: {
feedback: text('The text shown upon clicking (feedback)', 'Copied!'),
feedbackTimeout: number('How long the text is shown upon clicking (feedbackTimeout)', 3000),
iconDescription: text('Feedback icon description (iconDescription)', 'Copy to clipboard')
}
});

View file

@ -0,0 +1,62 @@
<script>
let className = undefined;
export { className as class };
export let iconDescription = 'Copy to clipboard';
export let feedback = 'Copied!';
export let feedbackTimeout = 2000;
export let props = {};
import { createEventDispatcher, onDestroy } from 'svelte';
import Copy16 from 'carbon-icons-svelte/lib/Copy16';
import { cx } from '../../lib';
const dispatch = createEventDispatcher();
let animation = undefined;
let timeoutId = undefined;
onDestroy(() => {
if (timeoutId !== undefined) {
clearTimeout(timeoutId);
timeoutId = undefined;
}
});
function handleClick(event) {
animation = 'fade-in';
timeoutId = setTimeout(() => {
animation = 'fade-out';
}, feedbackTimeout);
}
function handleAnimationEnd(event) {
if (event.animationName === 'hide-feedback') {
animation = undefined;
}
}
$: _class = cx(
'--snippet-button', // TODO: deprecated?
'--copy-btn',
animation && '--copy-btn--animating',
animation && `--copy-btn--${animation}`,
className
);
</script>
<button
{...props}
type="button"
aria-label={iconDescription}
title={iconDescription}
class={_class}
on:click
on:click={handleClick}
on:mouseover
on:mouseenter
on:mouseleave
on:animationend
on:animationend={handleAnimationEnd}>
<span class={cx('--assistive-text', '--copy-btn__feedback')}>{feedback}</span>
<Copy16 class={cx('--snippet__icon')} />
</button>

View file

@ -0,0 +1,3 @@
import CopyButton from './CopyButton.svelte';
export default CopyButton;

View file

@ -0,0 +1,67 @@
<script>
export let story = undefined;
export let status = undefined;
export let iconDescription = undefined;
export let description = undefined;
export let successDelay = undefined;
import Layout from '../../internal/ui/Layout.svelte';
import Button from '../Button';
import InlineLoading from './InlineLoading.svelte';
const props = { status, iconDescription, description, successDelay };
let isSubmitting = false;
let success = false;
let ariaLive = 'off';
let loadingDescription = 'Submitting...';
function handleSubmit() {
isSubmitting = true;
ariaLive = 'assertive';
setTimeout(() => {
isSubmitting = false;
loadingDescription = 'Submitted!';
success = true;
setTimeout(() => {
success = false;
isSubmitting = false;
loadingDescription = 'Submitting...';
ariaLive = 'off';
}, 1500);
}, 2000);
}
$: disabled = isSubmitting || success;
</script>
<style>
.wrapper {
display: flex;
width: 300px;
}
:global(.loader) {
margin-left: 1rem;
}
</style>
<Layout>
{#if story === 'ux-example'}
<div class="wrapper">
<Button kind="secondary" {disabled}>Cancel</Button>
{#if disabled}
<InlineLoading
class="loader"
description={loadingDescription}
status={success ? 'finished' : 'active'}
aria-live={ariaLive} />
{:else}
<Button on:click={handleSubmit}>Submit</Button>
{/if}
</div>
{:else}
<InlineLoading {...props} />
{/if}
</Layout>

View file

@ -0,0 +1,30 @@
import { withKnobs, select, text, number } from '@storybook/addon-knobs';
import Component from './InlineLoading.Story.svelte';
export default { title: 'InlineLoading', decorators: [withKnobs] };
export const Default = () => ({
Component,
props: {
status: select(
'Loading status (status)',
['inactive', 'active', 'finished', 'error'],
'active'
),
iconDescription: text('Icon description (iconDescription)', 'Active loading indicator'),
description: text('Loading progress description (description)', 'Loading data...'),
successDelay: number(
'The duration for successful state before `onSuccess` fires (successDelay)',
1500
)
}
});
export const UxExample = () => ({
Component,
props: { story: 'ux-example' }
});
UxExample.story = {
name: 'UX Example'
};

View file

@ -0,0 +1,52 @@
<script>
let className = undefined;
export { className as class };
export let success = false; // TODO: deprecate
export let status = success ? 'finished' : 'active';
export let description = undefined;
export let iconDescription = undefined;
export let successDelay = 1500;
export let props = {};
import { createEventDispatcher, onDestroy } from 'svelte';
import CheckmarkFilled16 from 'carbon-icons-svelte/lib/CheckmarkFilled16';
import Error20 from 'carbon-icons-svelte/lib/Error20';
import { cx } from '../../lib';
import Loading from '../Loading';
const dispatch = createEventDispatcher();
const _class = cx('--inline-loading', className);
let timeoutId = undefined;
onDestroy(() => {
if (timeoutId !== undefined) {
clearTimeout(timeoutId);
timeoutId = undefined;
}
});
$: if (status === 'finished') {
timeoutId = setTimeout(() => {
dispatch('success');
}, successDelay);
}
</script>
<div {...props} class={_class} aria-live={$$props['aria-live'] || 'assertive'}>
<div class={cx('--inline-loading__animation')}>
{#if status === 'error'}
<Error20 class={cx('--inline-loading--error')} />
{:else if status === 'finished'}
<CheckmarkFilled16 class={cx('--inline-loading__checkmark-container')} />
{:else if status === 'inactive' || status === 'active'}
<Loading
small
description={iconDescription}
withOverlay={false}
active={status === 'active'} />
{/if}
</div>
{#if description}
<div class={cx('--inline-loading__text')}>{description}</div>
{/if}
</div>

View file

@ -0,0 +1,3 @@
import InlineLoading from './InlineLoading.svelte';
export default InlineLoading;

View file

@ -0,0 +1,14 @@
<script>
export let href = undefined;
export let inline = undefined;
export let disabled = undefined;
import Layout from '../../internal/ui/Layout.svelte';
import Link from './Link.svelte';
</script>
<Layout>
<div>
<Link {href} {inline} {disabled}>Link</Link>
</div>
</Layout>

View file

@ -0,0 +1,13 @@
import { withKnobs, boolean, text } from '@storybook/addon-knobs';
import Component from './Link.Story.svelte';
export default { title: 'Link', decorators: [withKnobs] };
export const Default = () => ({
Component,
props: {
href: text('The link href (href)', '#'),
inline: boolean('Use the in-line variant (inline)', false),
disabled: boolean('Disabled (disabled)', false)
}
});

View file

@ -0,0 +1,27 @@
<script>
let className = undefined;
export { className as class };
export let href = undefined;
export let disabled = false;
export let inline = false;
export let props = {};
import { cx } from '../../lib';
const _class = cx(
'--link',
disabled && '--link--disabled',
inline && '--link--inline',
className
);
</script>
{#if disabled}
<p {...props} class={_class}>
<slot />
</p>
{:else}
<a {...props} class={_class} {href}>
<slot />
</a>
{/if}

View file

@ -0,0 +1,3 @@
import Link from './Link.svelte';
export default Link;

View file

@ -0,0 +1,13 @@
<script>
let className = undefined;
export { className as class };
export let props = {};
import { cx } from '../../lib';
const _class = cx('--list__item', className);
</script>
<li {...props} class={_class}>
<slot />
</li>

View file

@ -0,0 +1,3 @@
import ListItem from './ListItem.svelte';
export default ListItem;

View file

@ -0,0 +1,8 @@
<script>
import Layout from '../../internal/ui/Layout.svelte';
import Loading from './Loading.svelte';
</script>
<Layout>
<Loading {...$$props} />
</Layout>

View file

@ -0,0 +1,14 @@
import { withKnobs, boolean, text } from '@storybook/addon-knobs';
import Component from './Loading.Story.svelte';
export default { title: 'Loading', decorators: [withKnobs] };
export const Default = () => ({
Component,
props: {
active: boolean('Active (active)', true),
withOverlay: boolean('With overlay (withOverlay)', false),
small: boolean('Small (small)', false),
description: text('Description (description)', 'Active loading indicator')
}
});

View file

@ -0,0 +1,57 @@
<script>
let className = undefined;
export { className as class };
export let active = true;
export let withOverlay = true;
export let small = false;
export let description = 'Active loading indicator';
export let props = {};
import { cx } from '../../lib';
const loadingId = `loading-id-${Math.random()}`;
const spinnerRadius = small ? '26.8125' : '37.5';
const _class = cx(
'--loading',
small && '--loading--small',
!active && '--loading--stop',
className
);
const _overlayClass = cx('--loading-overlay', !active && '--loading-overlay--stop');
</script>
{#if withOverlay}
<div class={_overlayClass}>
<div
{...props}
aria-atomic="true"
aria-labelledby={loadingId}
aria-live={active ? 'assertive' : 'off'}
class={_class}>
<label id={loadingId} class={cx('--visually-hidden')}>{description}</label>
<svg class={cx('--loading__svg')} viewBox="-75 -75 150 150">
<title>{description}</title>
{#if small}
<circle class={cx('--loading__background')} cx="0" cy="0" r={spinnerRadius} />
{/if}
<circle class={cx('--loading__stroke')} cx="0" cy="0" r={spinnerRadius} />
</svg>
</div>
</div>
{:else}
<div
{...props}
aria-atomic="true"
aria-labelledby={loadingId}
aria-live={active ? 'assertive' : 'off'}
class={_class}>
<label id={loadingId} class={cx('--visually-hidden')}>{description}</label>
<svg class={cx('--loading__svg')} viewBox="-75 -75 150 150">
<title>{description}</title>
{#if small}
<circle class={cx('--loading__background')} cx="0" cy="0" r={spinnerRadius} />
{/if}
<circle class={cx('--loading__stroke')} cx="0" cy="0" r={spinnerRadius} />
</svg>
</div>
{/if}

View file

@ -0,0 +1,3 @@
import Loading from './Loading.svelte';
export default Loading;

View file

@ -0,0 +1,37 @@
<script>
export let story = undefined;
import Layout from '../../internal/ui/Layout.svelte';
import ListItem from '../ListItem';
import OrderedList from './OrderedList.svelte';
</script>
<Layout>
<div>
{#if story === 'nested'}
<OrderedList>
<ListItem>
Unordered List level 1
<OrderedList nested>
<ListItem>Ordered List level 2</ListItem>
<ListItem>
Ordered List level 2
<OrderedList nested>
<ListItem>Ordered List level 2</ListItem>
<ListItem>Ordered List level 2</ListItem>
</OrderedList>
</ListItem>
</OrderedList>
</ListItem>
<ListItem>Ordered List level 1</ListItem>
<ListItem>Ordered List level 1</ListItem>
</OrderedList>
{:else}
<OrderedList>
<ListItem>Ordered List level 1</ListItem>
<ListItem>Ordered List level 1</ListItem>
<ListItem>Ordered List level 1</ListItem>
</OrderedList>
{/if}
</div>
</Layout>

View file

@ -0,0 +1,8 @@
import { withKnobs } from '@storybook/addon-knobs';
import Component from './OrderedList.Story.svelte';
export default { title: 'Ordered List', decorators: [withKnobs] };
export const Default = () => ({ Component });
export const Nested = () => ({ Component, props: { story: 'nested' } });

View file

@ -0,0 +1,14 @@
<script>
let className = undefined;
export { className as class };
export let nested = false;
export let props = {};
import { cx } from '../../lib';
const _class = cx('--list--ordered', nested && '--list--nested', className);
</script>
<ol {...props} class={_class}>
<slot />
</ol>

View file

@ -0,0 +1,3 @@
import OrderedList from './OrderedList.svelte';
export default OrderedList;

View file

@ -0,0 +1,10 @@
<script>
import Layout from '../../internal/ui/Layout.svelte';
import SkeletonText from './SkeletonText.svelte';
</script>
<Layout>
<div style="width: 300px">
<SkeletonText {...$$props} />
</div>
</Layout>

View file

@ -0,0 +1,18 @@
import { withKnobs, select, boolean, number } from '@storybook/addon-knobs';
import Component from './SkeletonText.Story.svelte';
export default { title: 'SkeletonText', decorators: [withKnobs] };
export const Default = () => ({
Component,
props: {
heading: boolean('Skeleton text at a larger size (heading)'),
paragraph: boolean('Use multiple lines of text (paragraph)'),
lineCount: number('The number of lines in a paragraph (lineCount)', 3),
width: select(
'Width (in px or %) of single line of text or max-width of paragraph lines (width)',
{ '100%': '100%', '250px': '250px' },
'100%'
)
}
});

View file

@ -0,0 +1,37 @@
<script>
let className = undefined;
export { className as class };
export let paragraph = false;
export let lineCount = 3;
export let width = '100%';
export let heading = false;
export let props = {};
import { cx } from '../../lib';
const randoms = [0.973051493507435, 0.15334737213558558, 0.5671034553053769];
const _class = cx('--skeleton__text', heading && '--skeleton__heading', className);
const widthNum = parseInt(width, 10);
const widthPx = width.includes('px');
const widthPercent = width.includes('%');
let lines = [];
$: if (paragraph) {
for (let i = 0; i < lineCount; i++) {
const min = widthPx ? widthNum - 75 : 0;
const max = widthPx ? widthNum : 75;
const randomWidth = Math.floor(randoms[i % 3] * (max - min + 1)) + min + 'px';
lines = [...lines, { width: widthPx ? randomWidth : `calc(${width} - ${randomWidth})` }];
}
}
</script>
{#if paragraph}
<div>
{#each lines as { width }}
<p {...props} class={_class} style={`width: ${width};`} />
{/each}
</div>
{:else}
<p {...props} class={_class} style={`width: ${width};`} />
{/if}

View file

@ -0,0 +1,3 @@
import SkeletonText from './SkeletonText.svelte';
export default SkeletonText;

View file

@ -0,0 +1,5 @@
<script>
import { cx } from '../../lib';
</script>
<span class={cx('--tag', '--skeleton')} />

View file

@ -0,0 +1,23 @@
<script>
export let story = undefined;
export let type = undefined;
export let disabled = undefined;
export let filter = undefined;
export let slot = undefined;
import Layout from '../../internal/ui/Layout.svelte';
import Tag from './Tag.svelte';
import TagSkeleton from './Tag.Skeleton.svelte';
</script>
<Layout>
<div>
{#if story === 'filter'}
<Tag {filter}>{slot}</Tag>
{:else if story === 'skeleton'}
<TagSkeleton />
{:else}
<Tag {disabled} {type}>{slot}</Tag>
{/if}
</div>
</Layout>

View file

@ -0,0 +1,29 @@
import { withKnobs, select, boolean, text } from '@storybook/addon-knobs';
import Component from './Tag.Story.svelte';
import { TYPES } from './constants';
export default { title: 'Tag', decorators: [withKnobs] };
export const Default = () => ({
Component,
props: {
type: select(
'Tag type (type)',
Object.keys(TYPES).reduce((items, item) => ({ ...items, [`${item} (${item})`]: item }), {}),
'red'
),
disabled: boolean('Disabled (disabled)', false),
slot: text('Content ($$slot)', 'This is not a tag')
}
});
export const Filter = () => ({
Component,
props: {
story: 'filter',
filter: true,
slot: text('Content ($$slot)', 'This is not a tag')
}
});
export const Skeleton = () => ({ Component, props: { story: 'skeleton' } });

View file

@ -0,0 +1,40 @@
<script>
let className = undefined;
export { className as class };
export let type = undefined;
export let disabled = false;
export let filter = false;
export let title = 'Clear filter';
export let props = {};
import Close16 from 'carbon-icons-svelte/lib/Close16';
import { cx } from '../../lib';
import { TYPES } from './constants';
const _class = cx(
'--tag',
type && `--tag--${type}`,
disabled && '--tag--disabled',
filter && '--tag--filter',
className
);
</script>
{#if filter}
<span
{...props}
tabindex="0"
on:click
on:mouseover
on:mouseenter
on:mouseleave
class={_class}
{title}>
<slot>{TYPES[type]}</slot>
<Close16 aria-label={title} />
</span>
{:else}
<span {...props} on:click on:mouseover on:mouseenter on:mouseleave class={_class}>
<slot>{TYPES[type]}</slot>
</span>
{/if}

View file

@ -0,0 +1,12 @@
export const TYPES = {
red: 'Red',
magenta: 'Magenta',
purple: 'Purple',
blue: 'Blue',
cyan: 'Cyan',
teal: 'Teal',
green: 'Green',
gray: 'Gray',
'cool-gray': 'Cool-Gray',
'warm-gray': 'Warm-Gray'
};

View file

@ -0,0 +1,4 @@
import Tag from './Tag.svelte';
export default Tag;
export { default as TagSkeleton } from './Tag.Skeleton.svelte';

View file

@ -0,0 +1,17 @@
<script>
let className = undefined;
export { className as class };
export let hideLabel = false;
export let props = {};
import { cx } from '../../lib';
const _class = cx('--form-item', className);
</script>
<div {...props} class={_class}>
{#if !hideLabel}
<span class={cx('--label', '--skeleton')} />
{/if}
<div class={cx('--skeleton', '--text-area')} />
</div>

View file

@ -0,0 +1,17 @@
<script>
export let story = undefined;
import Layout from '../../internal/ui/Layout.svelte';
import TextArea from './TextArea.svelte';
import TextAreaSkeleton from './TextArea.Skeleton.svelte';
let value = '';
</script>
<Layout>
{#if story === 'skeleton'}
<TextAreaSkeleton {...$$props} />
{:else}
<TextArea {...$$props} bind:value />
{/if}
</Layout>

View file

@ -0,0 +1,23 @@
import { withKnobs, boolean, text, number } from '@storybook/addon-knobs';
import Component from './TextArea.Story.svelte';
export default { title: 'TextArea', decorators: [withKnobs] };
export const Default = () => ({
Component,
props: {
id: 'text-area',
disabled: boolean('Disabled (disabled)', false),
light: boolean('Light variant (light)', false),
hideLabel: boolean('No label (hideLabel)', false),
labelText: text('Label text (labelText)', 'Text Area label'),
invalid: boolean('Show form validation UI (invalid)', false),
invalidText: text('Content of form validation UI (invalidText)', 'A valid value is required'),
helperText: text('Helper text (helperText)', 'Optional helper text.'),
placeholder: text('Placeholder text (placeholder)', 'Placeholder text.'),
cols: number('Columns (columns)', 50),
rows: number('Rows (rows)', 4)
}
});
export const Skeleton = () => ({ Component, props: { story: 'skeleton' } });

View file

@ -0,0 +1,89 @@
<script>
let className = undefined;
export { className as class };
export let cols = 50;
export let disabled = false;
export let id = Math.random();
export let labelText = undefined;
export let placeholder = '';
export let rows = 4;
export let value = undefined;
export let invalid = false;
export let invalidText = undefined;
export let helperText = undefined;
export let hideLabel = false;
export let light = false;
export let props = {};
import { createEventDispatcher } from 'svelte';
import WarningFilled16 from 'carbon-icons-svelte/lib/WarningFilled16';
import { cx } from '../../lib';
const dispatch = createEventDispatcher();
const errorId = `${id}-error`;
const _labelClass = cx(
'--label',
hideLabel && '--visually-hidden',
disabled && '--label--disabled'
);
const _helperTextClass = cx('--form__helper-text', disabled && '--form__helper-text--disabled');
const _textAreaClass = cx(
'--text-area',
light && '--text-area--light',
invalid && '--text-area--invalid',
className
);
function handleClick(event) {
if (!disabled) {
dispatch('click', event);
}
}
function handleChange(event) {
if (!disabled) {
dispatch('change', event);
}
}
function handleInput(event) {
if (!disabled) {
dispatch('input', event);
}
}
</script>
<div class={cx('--form-item')}>
{#if labelText && !hideLabel}
<label for={id} class={_labelClass}>{labelText}</label>
{/if}
{#if helperText}
<div class={_helperTextClass}>{helperText}</div>
{/if}
<div class={cx('--text-area__wrapper')} data-invalid={invalid || undefined}>
{#if invalid}
<WarningFilled16 class={cx('--text-area__invalid-icon')} />
{/if}
<textarea
{...props}
on:click
on:click={handleClick}
on:change
on:change={handleChange}
on:input
on:input={handleInput}
class={_textAreaClass}
aria-invalid={invalid || undefined}
aria-describedby={invalid ? errorId : undefined}
{disabled}
{id}
{cols}
{rows}
{value}
{placeholder}
{value} />
</div>
{#if invalid}
<div class={cx('--form-requirement')} id={errorId}>{invalidText}</div>
{/if}
</div>

View file

@ -0,0 +1,4 @@
import TextArea from './TextArea.svelte';
export default TextArea;
export { default as TextAreaSkeleton } from './TextArea.Skeleton.svelte';

View file

@ -0,0 +1,24 @@
<script>
let className = undefined;
export { className as class };
export let id = undefined;
export let labelText = undefined;
export let props = {};
import { cx } from '../../lib';
const _class = cx('--form-item', className);
const ariaLabel = labelText ? null : $$props['aria-label'] || 'Toggle is loading';
</script>
<div {...props} class={_class}>
<input type="checkbox" class={cx('--toggle --skeleton')} {id} />
<label class={cx('--toggle__label', '--skeleton')} aria-label={ariaLabel} for={id}>
{#if labelText}
<span class={cx('--toggle__label-text')}>{labelText}</span>
{/if}
<span class={cx('--toggle__text--left')} />
<span class={cx('--toggle__appearance')} />
<span class={cx('--toggle__text--right')} />
</label>
</div>

View file

@ -0,0 +1,17 @@
<script>
export let story = undefined;
import Layout from '../../internal/ui/Layout.svelte';
import Toggle from './Toggle.svelte';
import ToggleSkeleton from './Toggle.Skeleton.svelte';
</script>
<Layout>
{#if story === 'toggled'}
<Toggle {...$$props} id="toggle-1" toggled />
{:else if story === 'skeleton'}
<ToggleSkeleton />
{:else}
<Toggle {...$$props} id="toggle-1" />
{/if}
</Layout>

View file

@ -0,0 +1,31 @@
import { withKnobs, boolean, text } from '@storybook/addon-knobs';
import Component from './Toggle.Story.svelte';
export default { title: 'Toggle', decorators: [withKnobs] };
export const Default = () => ({
Component,
props: {
labelText: text('Label toggle input control (labelText)', ''),
'aria-label': text('ARIA label of the toggle (aria-label)', ''),
labelA: text('Label for untoggled state (labelA)', 'Off'),
labelB: text('Label for toggled state (labelB)', 'On'),
disabled: boolean('Disabled (disabled)', false)
}
});
Default.story = { name: 'Default (untoggled)' };
export const Toggled = () => ({
Component,
props: {
story: 'toggled',
labelText: text('Label toggle input control (labelText)', ''),
'aria-label': text('ARIA label of the toggle (aria-label)', ''),
labelA: text('Label for untoggled state (labelA)', 'Off'),
labelB: text('Label for toggled state (labelB)', 'On'),
disabled: boolean('Disabled (disabled)', false)
}
});
export const Skeleton = () => ({ Component, props: { story: 'skeleton' } });

View file

@ -0,0 +1,56 @@
<script>
let className = undefined;
export { className as class };
export let id = Math.random();
export let toggled = false;
export let disabled = false;
export let labelText = undefined;
export let labelA = 'Off';
export let labelB = 'On';
export let props = {};
import { createEventDispatcher } from 'svelte';
import { cx } from '../../lib';
const dispatch = createEventDispatcher();
const _class = cx('--form-item', className);
const ariaLabel = labelText ? undefined : $$props['aria-label'] || 'Toggle';
let inputRef = undefined;
function handleChange(event) {
dispatch('change', event);
dispatch('toggle', { checked: inputRef.checked, id, event });
}
function handleKeyUp(event) {
if (event.key === 'Enter') {
if (inputRef) {
inputRef.checked = !inputRef.checked;
}
handleChange(event);
}
}
</script>
<div class={_class}>
<input
{...props}
type="checkbox"
class={cx('--toggle-input')}
bind:this={inputRef}
checked={toggled}
{disabled}
{id}
on:change
on:change={handleChange}
on:keyup
on:keyup={handleKeyUp} />
<label class={cx('--toggle-input__label')} for={id} aria-label={ariaLabel}>
{labelText}
<span class={cx('--toggle__switch')}>
<span aria-hidden="true" class={cx('--toggle__text--off')}>{labelA}</span>
<span aria-hidden="true" class={cx('--toggle__text--on')}>{labelB}</span>
</span>
</label>
</div>

View file

@ -0,0 +1,4 @@
import Toggle from './Toggle.svelte';
export default Toggle;
export { default as ToggleSkeleton } from './Toggle.Skeleton.svelte';

View file

@ -0,0 +1,26 @@
<script>
let className = undefined;
export { className as class };
export let id = undefined;
export let labelText = undefined;
export let props = {};
import { cx } from '../../lib';
const _class = cx('--form-item', className);
const ariaLabel = labelText ? undefined : $$props['aria-label'] || 'Toggle is loading';
</script>
<div {...props} class={_class}>
<input type="checkbox" class={cx('--toggle', '--toggle--small', '--skeleton')} {id} />
<label class={cx('--toggle__label --skeleton')} for={id}>
{#if labelText}
<span class={cx('--toggle__label-text')}>{labelText}</span>
{/if}
<span class={cx('--toggle__appearance')}>
<svg class={cx('--toggle__check')} width="6" height="5" viewBox="0 0 6 5">
<path d="M2.2403 2.7299L4.9245 0 6 1.1117 2.2384 5 0 2.6863 1.0612 1.511z" />
</svg>
</span>
</label>
</div>

View file

@ -0,0 +1,17 @@
<script>
export let story = undefined;
import Layout from '../../internal/ui/Layout.svelte';
import ToggleSmall from './ToggleSmall.svelte';
import ToggleSmallSkeleton from './ToggleSmall.Skeleton.svelte';
</script>
<Layout>
{#if story === 'toggled'}
<ToggleSmall {...$$props} id="toggle-1" toggled />
{:else if story === 'skeleton'}
<ToggleSmallSkeleton />
{:else}
<ToggleSmall {...$$props} id="toggle-1" />
{/if}
</Layout>

View file

@ -0,0 +1,31 @@
import { withKnobs, boolean, text } from '@storybook/addon-knobs';
import Component from './ToggleSmall.Story.svelte';
export default { title: 'ToggleSmall', decorators: [withKnobs] };
export const Default = () => ({
Component,
props: {
labelText: text('Label toggle input control (labelText)', ''),
'aria-label': text('ARIA label of the toggle (aria-label)', ''),
labelA: text('Label for untoggled state (labelA)', 'Off'),
labelB: text('Label for toggled state (labelB)', 'On'),
disabled: boolean('Disabled (disabled)', false)
}
});
Default.story = { name: 'Default (untoggled)' };
export const Toggled = () => ({
Component,
props: {
story: 'toggled',
labelText: text('Label toggle input control (labelText)', ''),
'aria-label': text('ARIA label of the toggle (aria-label)', ''),
labelA: text('Label for untoggled state (labelA)', 'Off'),
labelB: text('Label for toggled state (labelB)', 'On'),
disabled: boolean('Disabled (disabled)', false)
}
});
export const Skeleton = () => ({ Component, props: { story: 'skeleton' } });

View file

@ -0,0 +1,60 @@
<script>
let className = undefined;
export { className as class };
export let id = Math.random();
export let toggled = false;
export let disabled = false;
export let labelText = undefined;
export let labelA = 'Off';
export let labelB = 'On';
export let props = {};
import { createEventDispatcher } from 'svelte';
import { cx } from '../../lib';
const dispatch = createEventDispatcher();
const _class = cx('--form-item', className);
const ariaLabel = labelText ? undefined : $$props['aria-label'] || 'Toggle';
let inputRef = undefined;
function handleChange(event) {
dispatch('change', event);
dispatch('toggle', { checked: inputRef.checked, id, event });
}
function handleKeyUp(event) {
if (event.key === 'Enter') {
if (inputRef) {
inputRef.checked = !inputRef.checked;
}
handleChange(event);
}
}
</script>
<div class={_class}>
<input
{...props}
type="checkbox"
class={cx('--toggle-input', '--toggle-input--small')}
bind:this={inputRef}
checked={toggled}
{disabled}
{id}
on:change
on:change={handleChange}
on:keyup
on:keyup={handleKeyUp} />
<label class={cx('--toggle-input__label')} for={id} aria-label={ariaLabel}>
{labelText}
<span class={cx('--toggle__switch')}>
<svg width="6" height="5" viewBox="0 0 6 5" class={cx('--toggle__check')}>
<path d="M2.2 2.7L5 0 6 1 2.2 5 0 2.7 1 1.5z" />
</svg>
<span aria-hidden="true" class={cx('--toggle__text--off')}>{labelA}</span>
<span aria-hidden="true" class={cx('--toggle__text--on')}>{labelB}</span>
</span>
</label>
</div>

View file

@ -0,0 +1,4 @@
import ToggleSmall from './ToggleSmall.svelte';
export default ToggleSmall;
export { default as ToggleSmallSkeleton } from './ToggleSmall.Skeleton.svelte';

View file

@ -0,0 +1,10 @@
<script>
import Layout from '../../internal/ui/Layout.svelte';
import TooltipDefinition from './TooltipDefinition.svelte';
</script>
<Layout>
<div>
<TooltipDefinition {...$$props}>Defintion Tooltip</TooltipDefinition>
</div>
</Layout>

View file

@ -0,0 +1,30 @@
import { withKnobs, select, text } from '@storybook/addon-knobs';
import Component from './TooltipDefinition.Story.svelte';
export default { title: 'TooltipDefinition', decorators: [withKnobs] };
const directions = {
'Top (top)': 'top',
'Right (right)': 'right',
'Bottom (bottom)': 'bottom',
'Left (left)': 'left'
};
const alignments = {
'Start (start)': 'start',
'Center (center)': 'center',
'End (end)': 'end'
};
export const Default = () => ({
Component,
props: {
triggerClass: text('Trigger element CSS class name (triggerClassName)', ''),
direction: select('Tooltip direction (direction)', directions, 'bottom'),
align: select('Tooltip alignment to trigger button (align)', alignments, 'start'),
tooltipText: text(
'Tooltip content (tooltipText)',
'Brief description of the dotted, underlined word above.'
)
}
});

View file

@ -0,0 +1,36 @@
<script>
let className = undefined;
export { className as class };
export let direction = 'bottom';
export let align = 'center';
export let id = Math.random();
export let triggerClassName = undefined;
export { triggerClassName as triggerClass };
export let tooltipText = '';
export let props = {};
import { cx } from '../../lib';
const _class = cx('--tooltip--definition', '--tooltip--a11y', className);
const _triggerClass = cx(
'--tooltip__trigger',
'--tooltip--a11y',
'--tooltip__trigger--definition',
`--tooltip--${direction}`,
`--tooltip--align-${align}`,
triggerClassName
);
</script>
<div {...props} class={_class}>
<button
on:click
on:mouseover
on:mouseenter
on:mouseleave
class={_triggerClass}
aria-describedby={id}>
<slot />
</button>
<div role="tooltip" class={cx('--assistive-text')} {id}>{tooltipText}</div>
</div>

View file

@ -0,0 +1,3 @@
import TooltipDefinition from './TooltipDefinition.svelte';
export default TooltipDefinition;

View file

@ -0,0 +1,13 @@
<script>
import Filter16 from 'carbon-icons-svelte/lib/Filter16';
import Layout from '../../internal/ui/Layout.svelte';
import TooltipIcon from './TooltipIcon.svelte';
</script>
<Layout>
<div>
<TooltipIcon {...$$props}>
<Filter16 />
</TooltipIcon>
</div>
</Layout>

View file

@ -0,0 +1,26 @@
import { withKnobs, select, text } from '@storybook/addon-knobs';
import Component from './TooltipIcon.Story.svelte';
export default { title: 'TooltipIcon', decorators: [withKnobs] };
const directions = {
'Top (top)': 'top',
'Right (right)': 'right',
'Bottom (bottom)': 'bottom',
'Left (left)': 'left'
};
const alignments = {
'Start (start)': 'start',
'Center (center)': 'center',
'End (end)': 'end'
};
export const Default = () => ({
Component,
props: {
direction: select('Tooltip direction (direction)', directions, 'bottom'),
align: select('Tooltip alignment (align)', alignments, 'center'),
tooltipText: text('Tooltip content (tooltipText)', 'Filter')
}
});

View file

@ -0,0 +1,31 @@
<script>
let className = undefined;
export { className as class };
export let direction = 'bottom';
export let align = 'center';
export let id = Math.random();
export let tooltipText = '';
export let props = {};
import { cx } from '../../lib';
const _class = cx(
'--tooltip__trigger',
'--tooltip--a11y',
`--tooltip--${direction}`,
`--tooltip--align-${align}`,
className
);
</script>
<button
{...props}
on:click
on:mouseover
on:mouseenter
on:mouseleave
class={_class}
aria-describedby={id}>
<span class={cx('--assistive-text')} {id}>{tooltipText}</span>
<slot />
</button>

View file

@ -0,0 +1,3 @@
import TooltipIcon from './TooltipIcon.svelte';
export default TooltipIcon;

View file

@ -0,0 +1,37 @@
<script>
export let story = undefined;
import Layout from '../../internal/ui/Layout.svelte';
import ListItem from '../ListItem';
import UnorderedList from './UnorderedList.svelte';
</script>
<Layout>
<div>
{#if story === 'nested'}
<UnorderedList>
<ListItem>
Unordered List level 1
<UnorderedList nested>
<ListItem>Unordered List level 2</ListItem>
<ListItem>
Unordered List level 2
<UnorderedList nested>
<ListItem>Unordered List level 2</ListItem>
<ListItem>Unordered List level 2</ListItem>
</UnorderedList>
</ListItem>
</UnorderedList>
</ListItem>
<ListItem>Unordered List level 1</ListItem>
<ListItem>Unordered List level 1</ListItem>
</UnorderedList>
{:else}
<UnorderedList>
<ListItem>Unordered List level 1</ListItem>
<ListItem>Unordered List level 1</ListItem>
<ListItem>Unordered List level 1</ListItem>
</UnorderedList>
{/if}
</div>
</Layout>

View file

@ -0,0 +1,8 @@
import { withKnobs } from '@storybook/addon-knobs';
import Component from './UnorderedList.Story.svelte';
export default { title: 'Unordered List', decorators: [withKnobs] };
export const Default = () => ({ Component });
export const Nested = () => ({ Component, props: { story: 'nested' } });

View file

@ -0,0 +1,14 @@
<script>
let className = undefined;
export { className as class };
export let nested = false;
export let props = {};
import { cx } from '../../lib';
const _class = cx('--list--unordered', nested && '--list--nested', className);
</script>
<ul {...props} class={_class}>
<slot />
</ul>

View file

@ -0,0 +1,3 @@
import UnorderedList from './UnorderedList.svelte';
export default UnorderedList;

54
src/index.js Normal file
View file

@ -0,0 +1,54 @@
import Accordion, { AccordionItem, AccordionSkeleton } from './components/Accordion';
import Breadcrumb, { BreadcrumbItem, BreadcrumbSkeleton } from './components/Breadcrumb';
import Button, { ButtonSkeleton } from './components/Button';
import Checkbox, { CheckboxSkeleton } from './components/Checkbox';
import Copy from './components/Copy';
import CopyButton from './components/CopyButton';
import CodeSnippet, { CodeSnippetSkeleton } from './components/CodeSnippet';
import InlineLoading from './components/InlineLoading';
import Loading from './components/Loading';
import Link from './components/Link';
import ListItem from './components/ListItem';
import OrderedList from './components/OrderedList';
import SkeletonText from './components/SkeletonText';
import Tag, { TagSkeleton } from './components/Tag';
import TextArea, { TextAreaSkeleton } from './components/TextArea';
import Toggle, { ToggleSkeleton } from './components/Toggle';
import ToggleSmall, { ToggleSmallSkeleton } from './components/ToggleSmall';
import TooltipDefinition from './components/TooltipDefinition';
import TooltipIcon from './components/TooltipIcon';
import UnorderedList from './components/UnorderedList';
export {
Accordion,
AccordionItem,
AccordionSkeleton,
Breadcrumb,
BreadcrumbItem,
BreadcrumbSkeleton,
Button,
ButtonSkeleton,
Checkbox,
CheckboxSkeleton,
CodeSnippet,
CodeSnippetSkeleton,
Copy,
CopyButton,
InlineLoading,
Loading,
Link,
ListItem,
OrderedList,
SkeletonText,
Tag,
TagSkeleton,
TextArea,
TextAreaSkeleton,
Toggle,
ToggleSkeleton,
ToggleSmall,
ToggleSmallSkeleton,
TooltipDefinition,
TooltipIcon,
UnorderedList
};

View file

@ -0,0 +1,11 @@
<style>
.layout {
padding: 3em;
display: flex;
flex-direction: column;
}
</style>
<div class="layout">
<slot />
</div>

19
src/lib/cx.js Normal file
View file

@ -0,0 +1,19 @@
const prefix = 'bx';
function cx(...items) {
const classes = [];
items.forEach(item => {
if (typeof item === 'string') {
if (item.slice(0, 2) === '--') {
classes.push(`${prefix}${item}`);
} else {
classes.push(item);
}
}
});
return classes.join(' ');
}
export { cx };

1
src/lib/index.js Normal file
View file

@ -0,0 +1 @@
export { cx } from './cx';