mirror of
https://github.com/carbon-design-system/carbon-components-svelte.git
synced 2025-09-15 02:11:05 +00:00
feat: initial commit
This commit is contained in:
parent
bde7dd644f
commit
72dc38ea56
119 changed files with 14925 additions and 1 deletions
39
src/components/Accordion/Accordion.Skeleton.svelte
Normal file
39
src/components/Accordion/Accordion.Skeleton.svelte
Normal 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>
|
55
src/components/Accordion/Accordion.Story.svelte
Normal file
55
src/components/Accordion/Accordion.Story.svelte
Normal 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>
|
21
src/components/Accordion/Accordion.stories.js
Normal file
21
src/components/Accordion/Accordion.stories.js
Normal 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)
|
||||
}
|
||||
});
|
11
src/components/Accordion/Accordion.svelte
Normal file
11
src/components/Accordion/Accordion.svelte
Normal 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>
|
62
src/components/Accordion/AccordionItem.svelte
Normal file
62
src/components/Accordion/AccordionItem.svelte
Normal 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>
|
5
src/components/Accordion/index.js
Normal file
5
src/components/Accordion/index.js
Normal 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';
|
17
src/components/Breadcrumb/Breadcrumb.Skeleton.svelte
Normal file
17
src/components/Breadcrumb/Breadcrumb.Skeleton.svelte
Normal 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')}> </span>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
39
src/components/Breadcrumb/Breadcrumb.Story.svelte
Normal file
39
src/components/Breadcrumb/Breadcrumb.Story.svelte
Normal 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>
|
24
src/components/Breadcrumb/Breadcrumb.stories.js
Normal file
24
src/components/Breadcrumb/Breadcrumb.stories.js
Normal 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' }
|
||||
});
|
16
src/components/Breadcrumb/Breadcrumb.svelte
Normal file
16
src/components/Breadcrumb/Breadcrumb.svelte
Normal 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>
|
31
src/components/Breadcrumb/BreadcrumbItem.svelte
Normal file
31
src/components/Breadcrumb/BreadcrumbItem.svelte
Normal 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}
|
5
src/components/Breadcrumb/index.js
Normal file
5
src/components/Breadcrumb/index.js
Normal 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';
|
17
src/components/Button/Button.Skeleton.svelte
Normal file
17
src/components/Button/Button.Skeleton.svelte
Normal 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}
|
73
src/components/Button/Button.Story.svelte
Normal file
73
src/components/Button/Button.Story.svelte
Normal 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 />
|
||||
|
||||
<ButtonSkeleton href="#" />
|
||||
|
||||
<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>
|
||||
|
||||
<Button {...regularProps} href="#">Link</Button>
|
||||
|
||||
<Button {...regularProps} as let:props>
|
||||
<p {...props}>Element</p>
|
||||
</Button>
|
||||
|
||||
<Button {...regularProps} as let:props>
|
||||
<a href="#link" {...props}>Custom component</a>
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
</Layout>
|
64
src/components/Button/Button.stories.js
Normal file
64
src/components/Button/Button.stories.js
Normal 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' } });
|
82
src/components/Button/Button.svelte
Normal file
82
src/components/Button/Button.svelte
Normal 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}
|
4
src/components/Button/index.js
Normal file
4
src/components/Button/index.js
Normal file
|
@ -0,0 +1,4 @@
|
|||
import Button from './Button.svelte';
|
||||
|
||||
export default Button;
|
||||
export { default as ButtonSkeleton } from './Button.Skeleton.svelte';
|
13
src/components/Checkbox/Checkbox.Skeleton.svelte
Normal file
13
src/components/Checkbox/Checkbox.Skeleton.svelte
Normal 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>
|
35
src/components/Checkbox/Checkbox.Story.svelte
Normal file
35
src/components/Checkbox/Checkbox.Story.svelte
Normal 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>
|
32
src/components/Checkbox/Checkbox.stories.js
Normal file
32
src/components/Checkbox/Checkbox.stories.js
Normal 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' }
|
||||
});
|
41
src/components/Checkbox/Checkbox.svelte
Normal file
41
src/components/Checkbox/Checkbox.svelte
Normal 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>
|
4
src/components/Checkbox/index.js
Normal file
4
src/components/Checkbox/index.js
Normal file
|
@ -0,0 +1,4 @@
|
|||
import Checkbox from './Checkbox.svelte';
|
||||
|
||||
export default Checkbox;
|
||||
export { default as CheckboxSkeleton } from './Checkbox.Skeleton.svelte';
|
32
src/components/CodeSnippet/CodeSnippet.Skeleton.svelte
Normal file
32
src/components/CodeSnippet/CodeSnippet.Skeleton.svelte
Normal 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}
|
57
src/components/CodeSnippet/CodeSnippet.Story.svelte
Normal file
57
src/components/CodeSnippet/CodeSnippet.Story.svelte
Normal 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>
|
39
src/components/CodeSnippet/CodeSnippet.stories.js
Normal file
39
src/components/CodeSnippet/CodeSnippet.stories.js
Normal 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' } });
|
83
src/components/CodeSnippet/CodeSnippet.svelte
Normal file
83
src/components/CodeSnippet/CodeSnippet.svelte
Normal 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}
|
4
src/components/CodeSnippet/index.js
Normal file
4
src/components/CodeSnippet/index.js
Normal file
|
@ -0,0 +1,4 @@
|
|||
import CodeSnippet from './CodeSnippet.svelte';
|
||||
|
||||
export default CodeSnippet;
|
||||
export { default as CodeSnippetSkeleton } from './CodeSnippet.Skeleton.svelte';
|
45
src/components/Copy/Copy.svelte
Normal file
45
src/components/Copy/Copy.svelte
Normal 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>
|
3
src/components/Copy/index.js
Normal file
3
src/components/Copy/index.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
import Copy from './Copy.svelte';
|
||||
|
||||
export default Copy;
|
10
src/components/CopyButton/CopyButton.Story.svelte
Normal file
10
src/components/CopyButton/CopyButton.Story.svelte
Normal 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>
|
13
src/components/CopyButton/CopyButton.stories.js
Normal file
13
src/components/CopyButton/CopyButton.stories.js
Normal 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')
|
||||
}
|
||||
});
|
62
src/components/CopyButton/CopyButton.svelte
Normal file
62
src/components/CopyButton/CopyButton.svelte
Normal 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>
|
3
src/components/CopyButton/index.js
Normal file
3
src/components/CopyButton/index.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
import CopyButton from './CopyButton.svelte';
|
||||
|
||||
export default CopyButton;
|
67
src/components/InlineLoading/InlineLoading.Story.svelte
Normal file
67
src/components/InlineLoading/InlineLoading.Story.svelte
Normal 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>
|
30
src/components/InlineLoading/InlineLoading.stories.js
Normal file
30
src/components/InlineLoading/InlineLoading.stories.js
Normal 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'
|
||||
};
|
52
src/components/InlineLoading/InlineLoading.svelte
Normal file
52
src/components/InlineLoading/InlineLoading.svelte
Normal 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>
|
3
src/components/InlineLoading/index.js
Normal file
3
src/components/InlineLoading/index.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
import InlineLoading from './InlineLoading.svelte';
|
||||
|
||||
export default InlineLoading;
|
14
src/components/Link/Link.Story.svelte
Normal file
14
src/components/Link/Link.Story.svelte
Normal 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>
|
13
src/components/Link/Link.stories.js
Normal file
13
src/components/Link/Link.stories.js
Normal 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)
|
||||
}
|
||||
});
|
27
src/components/Link/Link.svelte
Normal file
27
src/components/Link/Link.svelte
Normal 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}
|
3
src/components/Link/index.js
Normal file
3
src/components/Link/index.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
import Link from './Link.svelte';
|
||||
|
||||
export default Link;
|
13
src/components/ListItem/ListItem.svelte
Normal file
13
src/components/ListItem/ListItem.svelte
Normal 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>
|
3
src/components/ListItem/index.js
Normal file
3
src/components/ListItem/index.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
import ListItem from './ListItem.svelte';
|
||||
|
||||
export default ListItem;
|
8
src/components/Loading/Loading.Story.svelte
Normal file
8
src/components/Loading/Loading.Story.svelte
Normal file
|
@ -0,0 +1,8 @@
|
|||
<script>
|
||||
import Layout from '../../internal/ui/Layout.svelte';
|
||||
import Loading from './Loading.svelte';
|
||||
</script>
|
||||
|
||||
<Layout>
|
||||
<Loading {...$$props} />
|
||||
</Layout>
|
14
src/components/Loading/Loading.stories.js
Normal file
14
src/components/Loading/Loading.stories.js
Normal 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')
|
||||
}
|
||||
});
|
57
src/components/Loading/Loading.svelte
Normal file
57
src/components/Loading/Loading.svelte
Normal 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}
|
3
src/components/Loading/index.js
Normal file
3
src/components/Loading/index.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
import Loading from './Loading.svelte';
|
||||
|
||||
export default Loading;
|
37
src/components/OrderedList/OrderedList.Story.svelte
Normal file
37
src/components/OrderedList/OrderedList.Story.svelte
Normal 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>
|
8
src/components/OrderedList/OrderedList.stories.js
Normal file
8
src/components/OrderedList/OrderedList.stories.js
Normal 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' } });
|
14
src/components/OrderedList/OrderedList.svelte
Normal file
14
src/components/OrderedList/OrderedList.svelte
Normal 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>
|
3
src/components/OrderedList/index.js
Normal file
3
src/components/OrderedList/index.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
import OrderedList from './OrderedList.svelte';
|
||||
|
||||
export default OrderedList;
|
10
src/components/SkeletonText/SkeletonText.Story.svelte
Normal file
10
src/components/SkeletonText/SkeletonText.Story.svelte
Normal 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>
|
18
src/components/SkeletonText/SkeletonText.stories.js
Normal file
18
src/components/SkeletonText/SkeletonText.stories.js
Normal 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%'
|
||||
)
|
||||
}
|
||||
});
|
37
src/components/SkeletonText/SkeletonText.svelte
Normal file
37
src/components/SkeletonText/SkeletonText.svelte
Normal 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}
|
3
src/components/SkeletonText/index.js
Normal file
3
src/components/SkeletonText/index.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
import SkeletonText from './SkeletonText.svelte';
|
||||
|
||||
export default SkeletonText;
|
5
src/components/Tag/Tag.Skeleton.svelte
Normal file
5
src/components/Tag/Tag.Skeleton.svelte
Normal file
|
@ -0,0 +1,5 @@
|
|||
<script>
|
||||
import { cx } from '../../lib';
|
||||
</script>
|
||||
|
||||
<span class={cx('--tag', '--skeleton')} />
|
23
src/components/Tag/Tag.Story.svelte
Normal file
23
src/components/Tag/Tag.Story.svelte
Normal 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>
|
29
src/components/Tag/Tag.stories.js
Normal file
29
src/components/Tag/Tag.stories.js
Normal 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' } });
|
40
src/components/Tag/Tag.svelte
Normal file
40
src/components/Tag/Tag.svelte
Normal 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}
|
12
src/components/Tag/constants.js
Normal file
12
src/components/Tag/constants.js
Normal 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'
|
||||
};
|
4
src/components/Tag/index.js
Normal file
4
src/components/Tag/index.js
Normal file
|
@ -0,0 +1,4 @@
|
|||
import Tag from './Tag.svelte';
|
||||
|
||||
export default Tag;
|
||||
export { default as TagSkeleton } from './Tag.Skeleton.svelte';
|
17
src/components/TextArea/TextArea.Skeleton.svelte
Normal file
17
src/components/TextArea/TextArea.Skeleton.svelte
Normal 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>
|
17
src/components/TextArea/TextArea.Story.svelte
Normal file
17
src/components/TextArea/TextArea.Story.svelte
Normal 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>
|
23
src/components/TextArea/TextArea.stories.js
Normal file
23
src/components/TextArea/TextArea.stories.js
Normal 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' } });
|
89
src/components/TextArea/TextArea.svelte
Normal file
89
src/components/TextArea/TextArea.svelte
Normal 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>
|
4
src/components/TextArea/index.js
Normal file
4
src/components/TextArea/index.js
Normal file
|
@ -0,0 +1,4 @@
|
|||
import TextArea from './TextArea.svelte';
|
||||
|
||||
export default TextArea;
|
||||
export { default as TextAreaSkeleton } from './TextArea.Skeleton.svelte';
|
24
src/components/Toggle/Toggle.Skeleton.svelte
Normal file
24
src/components/Toggle/Toggle.Skeleton.svelte
Normal 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>
|
17
src/components/Toggle/Toggle.Story.svelte
Normal file
17
src/components/Toggle/Toggle.Story.svelte
Normal 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>
|
31
src/components/Toggle/Toggle.stories.js
Normal file
31
src/components/Toggle/Toggle.stories.js
Normal 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' } });
|
56
src/components/Toggle/Toggle.svelte
Normal file
56
src/components/Toggle/Toggle.svelte
Normal 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>
|
4
src/components/Toggle/index.js
Normal file
4
src/components/Toggle/index.js
Normal file
|
@ -0,0 +1,4 @@
|
|||
import Toggle from './Toggle.svelte';
|
||||
|
||||
export default Toggle;
|
||||
export { default as ToggleSkeleton } from './Toggle.Skeleton.svelte';
|
26
src/components/ToggleSmall/ToggleSmall.Skeleton.svelte
Normal file
26
src/components/ToggleSmall/ToggleSmall.Skeleton.svelte
Normal 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>
|
17
src/components/ToggleSmall/ToggleSmall.Story.svelte
Normal file
17
src/components/ToggleSmall/ToggleSmall.Story.svelte
Normal 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>
|
31
src/components/ToggleSmall/ToggleSmall.stories.js
Normal file
31
src/components/ToggleSmall/ToggleSmall.stories.js
Normal 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' } });
|
60
src/components/ToggleSmall/ToggleSmall.svelte
Normal file
60
src/components/ToggleSmall/ToggleSmall.svelte
Normal 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>
|
4
src/components/ToggleSmall/index.js
Normal file
4
src/components/ToggleSmall/index.js
Normal file
|
@ -0,0 +1,4 @@
|
|||
import ToggleSmall from './ToggleSmall.svelte';
|
||||
|
||||
export default ToggleSmall;
|
||||
export { default as ToggleSmallSkeleton } from './ToggleSmall.Skeleton.svelte';
|
|
@ -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>
|
|
@ -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.'
|
||||
)
|
||||
}
|
||||
});
|
36
src/components/TooltipDefinition/TooltipDefinition.svelte
Normal file
36
src/components/TooltipDefinition/TooltipDefinition.svelte
Normal 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>
|
3
src/components/TooltipDefinition/index.js
Normal file
3
src/components/TooltipDefinition/index.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
import TooltipDefinition from './TooltipDefinition.svelte';
|
||||
|
||||
export default TooltipDefinition;
|
13
src/components/TooltipIcon/TooltipIcon.Story.svelte
Normal file
13
src/components/TooltipIcon/TooltipIcon.Story.svelte
Normal 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>
|
26
src/components/TooltipIcon/TooltipIcon.stories.js
Normal file
26
src/components/TooltipIcon/TooltipIcon.stories.js
Normal 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')
|
||||
}
|
||||
});
|
31
src/components/TooltipIcon/TooltipIcon.svelte
Normal file
31
src/components/TooltipIcon/TooltipIcon.svelte
Normal 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>
|
3
src/components/TooltipIcon/index.js
Normal file
3
src/components/TooltipIcon/index.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
import TooltipIcon from './TooltipIcon.svelte';
|
||||
|
||||
export default TooltipIcon;
|
37
src/components/UnorderedList/UnorderedList.Story.svelte
Normal file
37
src/components/UnorderedList/UnorderedList.Story.svelte
Normal 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>
|
8
src/components/UnorderedList/UnorderedList.stories.js
Normal file
8
src/components/UnorderedList/UnorderedList.stories.js
Normal 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' } });
|
14
src/components/UnorderedList/UnorderedList.svelte
Normal file
14
src/components/UnorderedList/UnorderedList.svelte
Normal 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>
|
3
src/components/UnorderedList/index.js
Normal file
3
src/components/UnorderedList/index.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
import UnorderedList from './UnorderedList.svelte';
|
||||
|
||||
export default UnorderedList;
|
54
src/index.js
Normal file
54
src/index.js
Normal 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
|
||||
};
|
11
src/internal/ui/Layout.svelte
Normal file
11
src/internal/ui/Layout.svelte
Normal 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
19
src/lib/cx.js
Normal 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
1
src/lib/index.js
Normal file
|
@ -0,0 +1 @@
|
|||
export { cx } from './cx';
|
Loading…
Add table
Add a link
Reference in a new issue