mirror of
https://github.com/carbon-design-system/carbon-components-svelte.git
synced 2025-09-15 02:11:05 +00:00
refactor: use $$restProps API
- add ref prop for applicable components (#196) - add slot to Content Switcher `Switch` component (#183) - remove fillArray, css utilities
This commit is contained in:
parent
4e2959080b
commit
e886d772c7
288 changed files with 4681 additions and 4498 deletions
|
@ -1,40 +1,39 @@
|
|||
<script>
|
||||
let className = undefined;
|
||||
export { className as class };
|
||||
export let count = 4;
|
||||
export let open = true;
|
||||
export let style = undefined;
|
||||
|
||||
import ChevronRight16 from 'carbon-icons-svelte/lib/ChevronRight16';
|
||||
import { cx, fillArray } from '../../lib';
|
||||
import SkeletonText from '../SkeletonText';
|
||||
import ChevronRight16 from "carbon-icons-svelte/lib/ChevronRight16";
|
||||
import { SkeletonText } from "../SkeletonText";
|
||||
</script>
|
||||
|
||||
<ul
|
||||
class:bx--accordion={true}
|
||||
class:bx--skeleton={true}
|
||||
{...$$restProps}
|
||||
on:click
|
||||
on:mouseover
|
||||
on:mouseenter
|
||||
on:mouseleave
|
||||
class={cx('--accordion', '--skeleton', className)}
|
||||
{style}>
|
||||
on:mouseleave>
|
||||
{#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')} />
|
||||
<li
|
||||
class:bx--accordion__item={true}
|
||||
class:bx--accordion__item--active={true}>
|
||||
<span class:bx--accordion__heading={true}>
|
||||
<ChevronRight16 class="bx--accordion__arrow" />
|
||||
<SkeletonText class="bx--accordion__title" />
|
||||
</span>
|
||||
<div class={cx('--accordion__content')}>
|
||||
<div class="bx--accordion__content">
|
||||
<SkeletonText width="90%" />
|
||||
<SkeletonText width="80%" />
|
||||
<SkeletonText width="95%" />
|
||||
</div>
|
||||
</li>
|
||||
{/if}
|
||||
{#each fillArray(open ? count - 1 : count) as item, i (item)}
|
||||
<li class={cx('--accordion__item')}>
|
||||
<span class={cx('--accordion__heading')}>
|
||||
<ChevronRight16 class={cx('--accordion__arrow')} />
|
||||
<SkeletonText class={cx('--accordion__title')} />
|
||||
{#each Array.from({ length: open ? count - 1 : count }, (_, i) => i) as item, i (item)}
|
||||
<li class="bx--accordion__item">
|
||||
<span class="bx--accordion__heading">
|
||||
<ChevronRight16 class="bx--accordion__arrow" />
|
||||
<SkeletonText class="bx--accordion__title" />
|
||||
</span>
|
||||
</li>
|
||||
{/each}
|
||||
|
|
|
@ -4,52 +4,53 @@
|
|||
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';
|
||||
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>
|
||||
{#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}
|
||||
|
|
|
@ -1,21 +1,32 @@
|
|||
import { withKnobs, text, boolean, number } from '@storybook/addon-knobs';
|
||||
import Component from './Accordion.Story.svelte';
|
||||
import {
|
||||
withKnobs,
|
||||
text,
|
||||
boolean,
|
||||
number,
|
||||
select,
|
||||
} from "@storybook/addon-knobs";
|
||||
import Component from "./Accordion.Story.svelte";
|
||||
|
||||
export default { title: 'Accordion', decorators: [withKnobs] };
|
||||
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)
|
||||
}
|
||||
align: select(
|
||||
"Accordion heading alignment (align)",
|
||||
["start", "end"],
|
||||
"end"
|
||||
),
|
||||
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)
|
||||
}
|
||||
story: "skeleton",
|
||||
open: boolean("Show first item opened (open)", true),
|
||||
count: number("Set number of items (count)", 4),
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,28 +1,21 @@
|
|||
<script>
|
||||
let className = undefined;
|
||||
export { className as class };
|
||||
export let style = undefined;
|
||||
|
||||
export let align = "end"; // "start" | "end"
|
||||
export let skeleton = false;
|
||||
export let count = 4;
|
||||
export let open = true;
|
||||
|
||||
import { cx } from '../../lib';
|
||||
import AccordionSkeleton from './Accordion.Skeleton.svelte';
|
||||
import AccordionSkeleton from "./Accordion.Skeleton.svelte";
|
||||
</script>
|
||||
|
||||
{#if skeleton}
|
||||
<AccordionSkeleton {count} {open} class={className} {style} />
|
||||
{/if}
|
||||
|
||||
{#if !skeleton}
|
||||
<AccordionSkeleton {...$$restProps} />
|
||||
{:else}
|
||||
<ul
|
||||
class:bx--accordion={true}
|
||||
class="bx--accordion--{align}"
|
||||
{...$$restProps}
|
||||
on:click
|
||||
on:mouseover
|
||||
on:mouseenter
|
||||
on:mouseleave
|
||||
class={cx('--accordion', className)}
|
||||
{style}>
|
||||
on:mouseleave>
|
||||
<slot />
|
||||
</ul>
|
||||
{/if}
|
||||
|
|
|
@ -1,27 +1,25 @@
|
|||
<script>
|
||||
let className = undefined;
|
||||
export { className as class };
|
||||
export let iconDescription = 'Expand/Collapse';
|
||||
export let title = "title";
|
||||
export let open = false;
|
||||
export let style = undefined;
|
||||
export let title = undefined;
|
||||
export let iconDescription = "Expand/Collapse";
|
||||
|
||||
import ChevronRight16 from 'carbon-icons-svelte/lib/ChevronRight16';
|
||||
import { cx } from '../../lib';
|
||||
import ChevronRight16 from "carbon-icons-svelte/lib/ChevronRight16";
|
||||
|
||||
let animation = undefined;
|
||||
$: animation = undefined;
|
||||
</script>
|
||||
|
||||
<li
|
||||
class={cx('--accordion__item', open && '--accordion__item--active', animation && `--accordion__item--${animation}`, className)}
|
||||
class:bx--accordion__item={true}
|
||||
class:bx--accordion__item--active={open}
|
||||
class="bx--accordion__item--${animation}"
|
||||
{...$$restProps}
|
||||
on:animationend
|
||||
on:animationend={() => {
|
||||
animation = undefined;
|
||||
}}
|
||||
{style}>
|
||||
}}>
|
||||
<button
|
||||
type="button"
|
||||
class={cx('--accordion__heading')}
|
||||
class:bx--accordion__heading={true}
|
||||
title={iconDescription}
|
||||
aria-expanded={open}
|
||||
on:click
|
||||
|
@ -38,12 +36,12 @@
|
|||
open = false;
|
||||
}
|
||||
}}>
|
||||
<ChevronRight16 class={cx('--accordion__arrow')} aria-label={iconDescription} />
|
||||
<div class={cx('--accordion__title')}>
|
||||
<ChevronRight16 class="bx--accordion__arrow" aria-label={iconDescription} />
|
||||
<div class="bx--accordion__title">
|
||||
<slot name="title">{title}</slot>
|
||||
</div>
|
||||
</button>
|
||||
<div class={cx('--accordion__content')}>
|
||||
<div class="bx--accordion__content">
|
||||
<slot />
|
||||
</div>
|
||||
</li>
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import Accordion from './Accordion.svelte';
|
||||
|
||||
export default Accordion;
|
||||
export { default as AccordionItem } from './AccordionItem.svelte';
|
||||
export { default as AccordionSkeleton } from './Accordion.Skeleton.svelte';
|
||||
export { default as Accordion } from "./Accordion.svelte";
|
||||
export { default as AccordionItem } from "./AccordionItem.svelte";
|
||||
export { default as AccordionSkeleton } from "./Accordion.Skeleton.svelte";
|
||||
|
|
|
@ -1,21 +1,14 @@
|
|||
<script>
|
||||
let className = undefined;
|
||||
export { className as class };
|
||||
export let style = undefined;
|
||||
|
||||
import { cx } from '../../lib';
|
||||
</script>
|
||||
|
||||
<div
|
||||
class:bx--breadcrumb={true}
|
||||
class:bx--skeleton={true}
|
||||
{...$$restProps}
|
||||
on:click
|
||||
on:mouseover
|
||||
on:mouseenter
|
||||
on:mouseleave
|
||||
class={cx('--breadcrumb', '--skeleton', className)}
|
||||
{style}>
|
||||
on:mouseleave>
|
||||
{#each [0, 1, 2] as item, i (item)}
|
||||
<div class={cx('--breadcrumb-item')}>
|
||||
<span class={cx('--link')}> </span>
|
||||
<div class:bx--breadcrumb-item={true}>
|
||||
<span class:bx--link={true}> </span>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
|
|
@ -2,38 +2,35 @@
|
|||
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';
|
||||
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>
|
||||
{#if story === 'current page'}
|
||||
<Breadcrumb noTrailingSlash aria-label="Breadcrumb header">
|
||||
<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}
|
||||
|
|
|
@ -1,24 +1,26 @@
|
|||
import { withKnobs, boolean } from '@storybook/addon-knobs';
|
||||
import Component from './Breadcrumb.Story.svelte';
|
||||
import { withKnobs, boolean } from "@storybook/addon-knobs";
|
||||
import Component from "./Breadcrumb.Story.svelte";
|
||||
|
||||
export default { title: 'Breadcrumb', decorators: [withKnobs] };
|
||||
export default { title: "Breadcrumb", decorators: [withKnobs] };
|
||||
|
||||
export const Default = () => ({
|
||||
Component,
|
||||
props: { noTrailingSlash: boolean('No Trailing Slash (noTrailingSlash)', false) }
|
||||
props: {
|
||||
noTrailingSlash: boolean("No Trailing Slash (noTrailingSlash)", false),
|
||||
},
|
||||
});
|
||||
|
||||
export const Skeleton = () => ({
|
||||
Component,
|
||||
props: { story: 'skeleton' }
|
||||
props: { story: "skeleton" },
|
||||
});
|
||||
|
||||
export const CurrentPage = () => ({
|
||||
Component,
|
||||
props: { story: 'current page' }
|
||||
props: { story: "current page" },
|
||||
});
|
||||
|
||||
export const CurrentPageWithAriaCurrent = () => ({
|
||||
Component,
|
||||
props: { story: 'current page with aria-current' }
|
||||
props: { story: "current page with aria-current" },
|
||||
});
|
||||
|
|
|
@ -1,21 +1,17 @@
|
|||
<script>
|
||||
let className = undefined;
|
||||
export { className as class };
|
||||
export let noTrailingSlash = false;
|
||||
export let style = undefined;
|
||||
|
||||
import { cx } from '../../lib';
|
||||
</script>
|
||||
|
||||
<nav
|
||||
aria-label="Breadcrumb"
|
||||
{...$$restProps}
|
||||
on:click
|
||||
on:mouseover
|
||||
on:mouseenter
|
||||
on:mouseleave
|
||||
aria-label={$$props['aria-label'] || 'Breadcrumb'}
|
||||
class={className}
|
||||
{style}>
|
||||
<ol class={cx('--breadcrumb', noTrailingSlash && '--breadcrumb--no-trailing-slash')}>
|
||||
on:mouseleave>
|
||||
<ol
|
||||
class:bx--breadcrumb={true}
|
||||
class:bx--breadcrumb--no-trailing-slash={noTrailingSlash}>
|
||||
<slot />
|
||||
</ol>
|
||||
</nav>
|
||||
|
|
|
@ -1,18 +1,20 @@
|
|||
import { render } from '@testing-library/svelte';
|
||||
import Component from './Breadcrumb.Story.svelte';
|
||||
import { render } from "@testing-library/svelte";
|
||||
import Component from "./Breadcrumb.Story.svelte";
|
||||
|
||||
describe('Breadcrumb', () => {
|
||||
describe("Breadcrumb", () => {
|
||||
function getLastBreadcrumbItem(container) {
|
||||
const breadcrumbItems = container.querySelectorAll('.bx--breadcrumb-item');
|
||||
const breadcrumbItems = container.querySelectorAll(".bx--breadcrumb-item");
|
||||
return breadcrumbItems[breadcrumbItems.length - 1];
|
||||
}
|
||||
|
||||
test('default', () => {
|
||||
const { getByText, container, rerender } = render(Component, { noTrailingSlash: false });
|
||||
const selector = '.bx--breadcrumb--no-trailing-slash';
|
||||
expect(getByText('Breadcrumb 1')).toBeInTheDocument();
|
||||
expect(getByText('Breadcrumb 2')).toBeInTheDocument();
|
||||
expect(getByText('Breadcrumb 3')).toBeInTheDocument();
|
||||
test("default", () => {
|
||||
const { getByText, container, rerender } = render(Component, {
|
||||
noTrailingSlash: false,
|
||||
});
|
||||
const selector = ".bx--breadcrumb--no-trailing-slash";
|
||||
expect(getByText("Breadcrumb 1")).toBeInTheDocument();
|
||||
expect(getByText("Breadcrumb 2")).toBeInTheDocument();
|
||||
expect(getByText("Breadcrumb 3")).toBeInTheDocument();
|
||||
expect(container.querySelector(selector)).not.toBeInTheDocument();
|
||||
expect(container.innerHTML).toMatchSnapshot();
|
||||
|
||||
|
@ -21,19 +23,23 @@ describe('Breadcrumb', () => {
|
|||
expect(container.innerHTML).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('skeleton', () => {
|
||||
const { container } = render(Component, { story: 'skeleton' });
|
||||
test("skeleton", () => {
|
||||
const { container } = render(Component, { story: "skeleton" });
|
||||
expect(container.innerHTML).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('current page', () => {
|
||||
const { container } = render(Component, { story: 'current page' });
|
||||
test("current page", () => {
|
||||
const { container } = render(Component, { story: "current page" });
|
||||
const lastItem = getLastBreadcrumbItem(container);
|
||||
expect(lastItem.classList.contains('bx--breadcrumb-item--current')).toEqual(true);
|
||||
expect(lastItem.classList.contains("bx--breadcrumb-item--current")).toEqual(
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
test('current page with aria-current', () => {
|
||||
const { container } = render(Component, { story: 'current page with aria-current' });
|
||||
test("current page with aria-current", () => {
|
||||
const { container } = render(Component, {
|
||||
story: "current page with aria-current",
|
||||
});
|
||||
const lastItem = getLastBreadcrumbItem(container);
|
||||
expect(lastItem.querySelector('[aria-current="page"]')).toBeTruthy();
|
||||
});
|
||||
|
|
|
@ -1,28 +1,24 @@
|
|||
<script>
|
||||
let className = undefined;
|
||||
export { className as class };
|
||||
export let href = undefined;
|
||||
export let isCurrentPage = false;
|
||||
export let style = undefined;
|
||||
|
||||
import { cx } from '../../lib';
|
||||
import Link from '../Link';
|
||||
|
||||
$: ariaCurrent = $$props['aria-current'];
|
||||
import { Link } from "../Link";
|
||||
</script>
|
||||
|
||||
<li
|
||||
class:bx--breadcrumb-item={true}
|
||||
class:bx--breadcrumb-item--current={isCurrentPage && $$restProps['aria-current'] !== 'page'}
|
||||
{...$$restProps}
|
||||
on:click
|
||||
on:mouseover
|
||||
on:mouseenter
|
||||
on:mouseleave
|
||||
class={cx('--breadcrumb-item', isCurrentPage && ariaCurrent !== 'page' && '--breadcrumb-item--current', className)}
|
||||
{style}>
|
||||
on:mouseleave>
|
||||
{#if href}
|
||||
<Link {href} aria-current={ariaCurrent}>
|
||||
<Link {href} aria-current={$$restProps['aria-current']}>
|
||||
<slot />
|
||||
</Link>
|
||||
{:else}
|
||||
<slot props={{ 'aria-current': ariaCurrent, class: cx('--link') }} />
|
||||
<slot
|
||||
props={{ 'aria-current': $$restProps['aria-current'], class: 'bx--link' }} />
|
||||
{/if}
|
||||
</li>
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import Breadcrumb from './Breadcrumb.svelte';
|
||||
|
||||
export default Breadcrumb;
|
||||
export { default as BreadcrumbItem } from './BreadcrumbItem.svelte';
|
||||
export { default as BreadcrumbSkeleton } from './Breadcrumb.Skeleton.svelte';
|
||||
export { default as Breadcrumb } from "./Breadcrumb.svelte";
|
||||
export { default as BreadcrumbItem } from "./BreadcrumbItem.svelte";
|
||||
export { default as BreadcrumbSkeleton } from "./Breadcrumb.Skeleton.svelte";
|
||||
|
|
|
@ -1,31 +1,30 @@
|
|||
<script>
|
||||
let className = undefined;
|
||||
export { className as class };
|
||||
export let href = undefined;
|
||||
export let small = false;
|
||||
export let style = undefined;
|
||||
|
||||
import { cx } from '../../lib';
|
||||
</script>
|
||||
|
||||
{#if href}
|
||||
<a
|
||||
{href}
|
||||
role="button"
|
||||
class:bx--skeleton={true}
|
||||
class:bx--btn={true}
|
||||
class:bx--btn--sm={small}
|
||||
{...$$restProps}
|
||||
on:click
|
||||
on:mouseover
|
||||
on:mouseenter
|
||||
on:mouseleave
|
||||
class={cx('--skeleton', '--btn', small && '--btn--sm', className)}
|
||||
{style}
|
||||
{href}>
|
||||
on:mouseleave>
|
||||
{''}
|
||||
</a>
|
||||
{:else}
|
||||
<div
|
||||
class:bx--skeleton={true}
|
||||
class:bx--btn={true}
|
||||
class:bx--btn--sm={small}
|
||||
{...$$restProps}
|
||||
on:click
|
||||
on:mouseover
|
||||
on:mouseenter
|
||||
on:mouseleave
|
||||
class={cx('--skeleton', '--btn', small && '--btn--sm', className)}
|
||||
{style} />
|
||||
on:mouseleave />
|
||||
{/if}
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
<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';
|
||||
import Button from "./Button.svelte";
|
||||
import ButtonSkeleton from "./Button.Skeleton.svelte";
|
||||
import Add16 from "carbon-icons-svelte/lib/Add16";
|
||||
|
||||
const {
|
||||
kind,
|
||||
|
@ -39,36 +37,34 @@
|
|||
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} />
|
||||
{: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>
|
||||
<!-- svelte-ignore a11y-missing-attribute -->
|
||||
<a {...props}>Custom component</a>
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
</Layout>
|
||||
<div>
|
||||
{#if story === 'skeleton'}
|
||||
<ButtonSkeleton />
|
||||
|
||||
<ButtonSkeleton href="#" />
|
||||
|
||||
<ButtonSkeleton small />
|
||||
{:else if story === 'inline'}
|
||||
<Button />
|
||||
{:else if story === 'icon-only buttons'}
|
||||
<Button {...iconOnlyProps} />
|
||||
{:else if story === 'set of buttons'}
|
||||
<div class="bx--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>
|
||||
<!-- svelte-ignore a11y-missing-attribute -->
|
||||
<a {...props}>Custom component</a>
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
|
|
|
@ -1,62 +1,62 @@
|
|||
import { withKnobs, select, boolean, text } from '@storybook/addon-knobs';
|
||||
import Component from './Button.Story.svelte';
|
||||
import { withKnobs, select, boolean, text } from "@storybook/addon-knobs";
|
||||
import Component from "./Button.Story.svelte";
|
||||
|
||||
export default { title: 'Button', decorators: [withKnobs] };
|
||||
export default { title: "Button", decorators: [withKnobs] };
|
||||
|
||||
const kinds = {
|
||||
'Primary button (primary)': 'primary',
|
||||
'Secondary button (secondary)': 'secondary',
|
||||
'Danger button (danger)': 'danger',
|
||||
'Ghost button (ghost)': 'ghost'
|
||||
"Primary button (primary)": "primary",
|
||||
"Secondary button (secondary)": "secondary",
|
||||
"Danger button (danger)": "danger",
|
||||
"Ghost button (ghost)": "ghost",
|
||||
};
|
||||
|
||||
const sizes = {
|
||||
Default: 'default',
|
||||
Field: 'field',
|
||||
Small: 'small'
|
||||
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)
|
||||
}
|
||||
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'),
|
||||
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'
|
||||
"Tooltip position (tooltipPosition)",
|
||||
["top", "right", "bottom", "left"],
|
||||
"bottom"
|
||||
),
|
||||
tooltipAlignment: select(
|
||||
'Tooltip alignment (tooltipAlignment)',
|
||||
['start', 'center', 'end'],
|
||||
'center'
|
||||
)
|
||||
}
|
||||
"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')
|
||||
}
|
||||
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' } });
|
||||
export const Skeleton = () => ({ Component, props: { story: "skeleton" } });
|
||||
|
|
|
@ -1,50 +1,49 @@
|
|||
<script>
|
||||
let className = undefined;
|
||||
export { className as class };
|
||||
export let as = undefined;
|
||||
export let disabled = false;
|
||||
export let href = undefined;
|
||||
export let icon = undefined;
|
||||
export let iconDescription = undefined;
|
||||
export let hasIconOnly = false;
|
||||
export let kind = 'primary';
|
||||
export let size = 'default';
|
||||
export let style = undefined;
|
||||
export let tabindex = '0';
|
||||
export let kind = "primary";
|
||||
export let size = "default";
|
||||
export let tabindex = "0";
|
||||
export let tooltipAlignment = undefined;
|
||||
export let tooltipPosition = undefined;
|
||||
export let type = 'button';
|
||||
export let type = "button";
|
||||
export let ref = null;
|
||||
|
||||
import { getContext } from 'svelte';
|
||||
import { cx } from '../../lib';
|
||||
import { getContext } from "svelte";
|
||||
|
||||
const ctx = getContext('ComposedModal');
|
||||
const ctx = getContext("ComposedModal");
|
||||
|
||||
let buttonRef = undefined;
|
||||
|
||||
$: if (ctx && buttonRef) {
|
||||
ctx.declareRef(buttonRef);
|
||||
$: if (ctx && ref) {
|
||||
ctx.declareRef(ref);
|
||||
}
|
||||
$: buttonProps = {
|
||||
role: 'button',
|
||||
role: "button",
|
||||
type: href && !disabled ? undefined : type,
|
||||
tabindex,
|
||||
disabled,
|
||||
href,
|
||||
style,
|
||||
class: cx(
|
||||
'--btn',
|
||||
size === 'field' && '--btn--field',
|
||||
size === 'small' && '--btn--sm',
|
||||
kind && `--btn--${kind}`,
|
||||
disabled && '--btn--disabled',
|
||||
hasIconOnly && '--btn--icon-only',
|
||||
hasIconOnly && '--tooltip__trigger',
|
||||
hasIconOnly && '--tooltip--a11y',
|
||||
hasIconOnly && tooltipPosition && `--tooltip--${tooltipPosition}`,
|
||||
hasIconOnly && tooltipAlignment && `--tooltip--align-${tooltipAlignment}`,
|
||||
className
|
||||
)
|
||||
style: $$restProps.style,
|
||||
class: [
|
||||
"bx--btn",
|
||||
size === "field" && "bx--btn--field",
|
||||
size === "small" && "bx--btn--sm",
|
||||
kind && `bx--btn--${kind}`,
|
||||
disabled && "bx--btn--disabled",
|
||||
hasIconOnly && "bx--btn--icon-only",
|
||||
hasIconOnly && "bx--tooltip__trigger",
|
||||
hasIconOnly && "bx--tooltip--a11y",
|
||||
hasIconOnly && tooltipPosition && `bx--tooltip--${tooltipPosition}`,
|
||||
hasIconOnly &&
|
||||
tooltipAlignment &&
|
||||
`bx--tooltip--align-${tooltipAlignment}`,
|
||||
$$restProps.class
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(" ")
|
||||
};
|
||||
</script>
|
||||
|
||||
|
@ -52,30 +51,42 @@
|
|||
<slot props={buttonProps} />
|
||||
{:else if href && !disabled}
|
||||
<!-- svelte-ignore a11y-missing-attribute -->
|
||||
<a {...buttonProps} on:click on:mouseover on:mouseenter on:mouseleave>
|
||||
<a
|
||||
bind:this={ref}
|
||||
{...buttonProps}
|
||||
on:click
|
||||
on:mouseover
|
||||
on:mouseenter
|
||||
on:mouseleave>
|
||||
{#if hasIconOnly}
|
||||
<span class={cx('--assistive-text')}>{iconDescription}</span>
|
||||
<span class:bx--assistive-text={true}>{iconDescription}</span>
|
||||
{/if}
|
||||
<slot />
|
||||
{#if icon}
|
||||
<svelte:component
|
||||
this={icon}
|
||||
aria-hidden="true"
|
||||
class={cx('--btn__icon')}
|
||||
class="bx--btn__icon"
|
||||
aria-label={iconDescription} />
|
||||
{/if}
|
||||
</a>
|
||||
{:else}
|
||||
<button {...buttonProps} bind:this={buttonRef} on:click on:mouseover on:mouseenter on:mouseleave>
|
||||
<button
|
||||
bind:this={ref}
|
||||
{...buttonProps}
|
||||
on:click
|
||||
on:mouseover
|
||||
on:mouseenter
|
||||
on:mouseleave>
|
||||
{#if hasIconOnly}
|
||||
<span class={cx('--assistive-text')}>{iconDescription}</span>
|
||||
<span class:bx--assistive-text={true}>{iconDescription}</span>
|
||||
{/if}
|
||||
<slot />
|
||||
{#if icon}
|
||||
<svelte:component
|
||||
this={icon}
|
||||
aria-hidden="true"
|
||||
class={cx('--btn__icon')}
|
||||
class="bx--btn__icon"
|
||||
aria-label={iconDescription} />
|
||||
{/if}
|
||||
</button>
|
||||
|
|
|
@ -1,4 +1,2 @@
|
|||
import Button from './Button.svelte';
|
||||
|
||||
export default Button;
|
||||
export { default as ButtonSkeleton } from './Button.Skeleton.svelte';
|
||||
export { default as Button } from "./Button.svelte";
|
||||
export { default as ButtonSkeleton } from "./Button.Skeleton.svelte";
|
||||
|
|
|
@ -1,17 +1,11 @@
|
|||
<script>
|
||||
let className = undefined;
|
||||
export { className as class };
|
||||
export let style = undefined;
|
||||
|
||||
import { cx } from '../../lib';
|
||||
</script>
|
||||
|
||||
<div
|
||||
class:bx--form-item={true}
|
||||
class:bx--checkbox-wrapper={true}
|
||||
class:bx--checkbox-label={true}
|
||||
{...$$restProps}
|
||||
on:click
|
||||
on:mouseover
|
||||
on:mouseenter
|
||||
on:mouseleave
|
||||
class={cx('--form-item', '--checkbox-wrapper', '--checkbox-label', className)}
|
||||
{style}>
|
||||
<span class={cx('--checkbox-label-text', '--skeleton')} />
|
||||
on:mouseleave>
|
||||
<span class:bx--checkbox-label-text={true} class:bx--skeleton={true} />
|
||||
</div>
|
||||
|
|
|
@ -1,39 +1,47 @@
|
|||
<script>
|
||||
export let story = undefined;
|
||||
|
||||
import { cx } from '../../lib';
|
||||
import Layout from '../../internal/ui/Layout.svelte';
|
||||
import Checkbox from './Checkbox.svelte';
|
||||
import CheckboxSkeleton from './Checkbox.Skeleton.svelte';
|
||||
import Checkbox from "./Checkbox.svelte";
|
||||
import CheckboxSkeleton from "./Checkbox.Skeleton.svelte";
|
||||
|
||||
const { labelText, indeterminate, disabled, hideLabel, wrapperClassName } = $$props;
|
||||
const checkboxProps = { labelText, indeterminate, disabled, hideLabel, wrapperClassName };
|
||||
const {
|
||||
labelText,
|
||||
indeterminate,
|
||||
disabled,
|
||||
hideLabel,
|
||||
wrapperClassName
|
||||
} = $$props;
|
||||
const checkboxProps = {
|
||||
labelText,
|
||||
indeterminate,
|
||||
disabled,
|
||||
hideLabel,
|
||||
wrapperClassName
|
||||
};
|
||||
|
||||
let checked = true;
|
||||
</script>
|
||||
|
||||
<Layout>
|
||||
{#if story === 'skeleton'}
|
||||
<div>
|
||||
<CheckboxSkeleton />
|
||||
</div>
|
||||
{: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}
|
||||
id="checkbox-label-1"
|
||||
bind:checked
|
||||
on:check={({ detail }) => {
|
||||
console.log('on:check', detail);
|
||||
}} />
|
||||
<Checkbox {...checkboxProps} id="checkbox-label-2" checked />
|
||||
</fieldset>
|
||||
{/if}
|
||||
</Layout>
|
||||
{#if story === 'skeleton'}
|
||||
<div>
|
||||
<CheckboxSkeleton />
|
||||
</div>
|
||||
{:else if story === 'unchecked'}
|
||||
<fieldset class="bx--fieldset">
|
||||
<legend class="bx--label">Checkbox heading</legend>
|
||||
<Checkbox {...checkboxProps} id="checkbox-label-1" />
|
||||
<Checkbox {...checkboxProps} id="checkbox-label-2" />
|
||||
</fieldset>
|
||||
{:else}
|
||||
<fieldset class="bx--fieldset">
|
||||
<legend class="bx--label">Checkbox heading</legend>
|
||||
<Checkbox
|
||||
{...checkboxProps}
|
||||
id="checkbox-label-1"
|
||||
bind:checked
|
||||
on:check={({ detail }) => {
|
||||
console.log('on:check', detail);
|
||||
}} />
|
||||
<Checkbox {...checkboxProps} id="checkbox-label-2" checked />
|
||||
</fieldset>
|
||||
{/if}
|
||||
|
|
|
@ -1,27 +1,27 @@
|
|||
import { withKnobs, boolean, text } from '@storybook/addon-knobs';
|
||||
import Component from './Checkbox.Story.svelte';
|
||||
import { withKnobs, boolean, text } from "@storybook/addon-knobs";
|
||||
import Component from "./Checkbox.Story.svelte";
|
||||
|
||||
export default { title: 'Checkbox', decorators: [withKnobs] };
|
||||
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('Hide label (hideLabel)', false)
|
||||
}
|
||||
labelText: text("Label text (labelText)", "Checkbox label"),
|
||||
indeterminate: boolean("Intermediate (indeterminate)", false),
|
||||
disabled: boolean("Disabled (disabled)", false),
|
||||
hideLabel: boolean("Hide label (hideLabel)", false),
|
||||
},
|
||||
});
|
||||
|
||||
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('Hide label (hideLabel)', false)
|
||||
}
|
||||
story: "unchecked",
|
||||
labelText: text("Label text (labelText)", "Checkbox label"),
|
||||
indeterminate: boolean("Intermediate (indeterminate)", false),
|
||||
disabled: boolean("Disabled (disabled)", false),
|
||||
hideLabel: boolean("Hide label (hideLabel)", false),
|
||||
},
|
||||
});
|
||||
|
||||
export const Skeleton = () => ({ Component, props: { story: 'skeleton' } });
|
||||
export const Skeleton = () => ({ Component, props: { story: "skeleton" } });
|
||||
|
|
|
@ -1,48 +1,49 @@
|
|||
<script>
|
||||
let className = undefined;
|
||||
export { className as class };
|
||||
export let indeterminate = false;
|
||||
export let readonly = false;
|
||||
export let checked = false;
|
||||
export let disabled = false;
|
||||
export let labelText = "";
|
||||
export let hideLabel = false;
|
||||
export let id = Math.random();
|
||||
export let indeterminate = false;
|
||||
export let labelText = '';
|
||||
export let name = '';
|
||||
export let readonly = false;
|
||||
export let style = undefined;
|
||||
export let title = '';
|
||||
export let id = "ccs-" + Math.random().toString(36);
|
||||
export let name = "";
|
||||
export let title = undefined;
|
||||
export let ref = null;
|
||||
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { cx } from '../../lib';
|
||||
import { createEventDispatcher } from "svelte";
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
$: {
|
||||
dispatch('check', checked);
|
||||
}
|
||||
$: dispatch("check", checked);
|
||||
</script>
|
||||
|
||||
<div
|
||||
class:bx--form-item={true}
|
||||
class:bx--checkbox-wrapper={true}
|
||||
{...$$restProps}
|
||||
on:click
|
||||
on:mouseover
|
||||
on:mouseenter
|
||||
on:mouseleave
|
||||
class={cx('--form-item', '--checkbox-wrapper', className)}
|
||||
{style}>
|
||||
on:mouseleave>
|
||||
<input
|
||||
bind:this={ref}
|
||||
type="checkbox"
|
||||
class={cx('--checkbox')}
|
||||
{checked}
|
||||
{disabled}
|
||||
{id}
|
||||
{indeterminate}
|
||||
{name}
|
||||
{readonly}
|
||||
class:bx--checkbox={true}
|
||||
on:change
|
||||
on:change={() => {
|
||||
checked = !checked;
|
||||
}}
|
||||
{indeterminate}
|
||||
{disabled}
|
||||
{checked}
|
||||
{name}
|
||||
{id}
|
||||
{readonly} />
|
||||
<label class={cx('--checkbox-label')} for={id} title={title || undefined}>
|
||||
<span class={cx('--checkbox-label-text', hideLabel && '--visually-hidden')}>{labelText}</span>
|
||||
}} />
|
||||
<label class:bx--checkbox-label={true} for={id} {title}>
|
||||
<span
|
||||
class:bx--checkbox-label-text={true}
|
||||
class:bx--visually-hidden={hideLabel}>
|
||||
{labelText}
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,2 @@
|
|||
import Checkbox from './Checkbox.svelte';
|
||||
|
||||
export default Checkbox;
|
||||
export { default as CheckboxSkeleton } from './Checkbox.Skeleton.svelte';
|
||||
export { default as Checkbox } from "./Checkbox.svelte";
|
||||
export { default as CheckboxSkeleton } from "./Checkbox.Skeleton.svelte";
|
||||
|
|
|
@ -1,20 +1,18 @@
|
|||
<script>
|
||||
let className = undefined;
|
||||
export { className as class };
|
||||
export let style = undefined;
|
||||
export let type = 'single';
|
||||
|
||||
import { cx } from '../../lib';
|
||||
export let type = "single"; // "single" | "multi"
|
||||
</script>
|
||||
|
||||
<div
|
||||
class:bx--snippet={true}
|
||||
class:bx--skeleton={true}
|
||||
class:bx--snippet--single={type === 'single'}
|
||||
class:bx--snippet--multi={type === 'multi'}
|
||||
{...$$restProps}
|
||||
on:click
|
||||
on:mouseover
|
||||
on:mouseenter
|
||||
on:mouseleave
|
||||
class={cx('--snippet', '--skeleton', type === 'single' && '--snippet--single', type === 'multi' && '--snippet--multi', className)}
|
||||
{style}>
|
||||
<div class={cx('--snippet-container')}>
|
||||
on:mouseleave>
|
||||
<div class:bx--snippet-container={true}>
|
||||
{#if type === 'single'}
|
||||
<span />
|
||||
{:else if type === 'multi'}
|
||||
|
|
|
@ -1,32 +1,25 @@
|
|||
<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';
|
||||
import CodeSnippet from "./CodeSnippet.svelte";
|
||||
import CodeSnippetSkeleton from "./CodeSnippet.Skeleton.svelte";
|
||||
</script>
|
||||
|
||||
<Layout>
|
||||
<div>
|
||||
{#if story === 'skeleton'}
|
||||
<div style="width: 800px">
|
||||
<CodeSnippetSkeleton type="single" style="margin-bottom: 8px" />
|
||||
<CodeSnippetSkeleton type="multi" />
|
||||
</div>
|
||||
{:else if story === 'inline'}
|
||||
<CodeSnippet type="inline" {light} {feedback} {copyLabel}>{'node -v'}</CodeSnippet>
|
||||
{:else if story === 'single line'}
|
||||
<CodeSnippet
|
||||
type="single"
|
||||
{feedback}
|
||||
{copyButtonDescription}
|
||||
aria-label={$$props['aria-label']}>
|
||||
{'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" {feedback} {showLessText} {showMoreText}>
|
||||
{`@mixin grid-container {
|
||||
<div>
|
||||
{#if story === 'skeleton'}
|
||||
<div style="width: 800px">
|
||||
<CodeSnippetSkeleton type="single" style="margin-bottom: 8px" />
|
||||
<CodeSnippetSkeleton type="multi" />
|
||||
</div>
|
||||
{:else if story === 'inline'}
|
||||
<CodeSnippet {...$$restProps} type="inline">{'node -v'}</CodeSnippet>
|
||||
{:else if story === 'single line'}
|
||||
<CodeSnippet {...$$restProps} type="single">
|
||||
{'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 {...$$restProps} type="multi">
|
||||
{`@mixin grid-container {
|
||||
width: 100%;
|
||||
padding-right: padding(mobile);
|
||||
padding-left: padding(mobile);
|
||||
|
@ -43,11 +36,10 @@ $z-indexes: (
|
|||
dropdown : 7000,
|
||||
header : 6000,
|
||||
footer : 5000,
|
||||
hidden : - 1,
|
||||
overflowHidden: - 1,
|
||||
hidden : -1,
|
||||
overflowHidden: -1,
|
||||
floating: 10000
|
||||
);`}
|
||||
</CodeSnippet>
|
||||
{/if}
|
||||
</div>
|
||||
</Layout>
|
||||
</CodeSnippet>
|
||||
{/if}
|
||||
</div>
|
||||
|
|
|
@ -1,39 +1,52 @@
|
|||
import { withKnobs, boolean, text } from '@storybook/addon-knobs';
|
||||
import Component from './CodeSnippet.Story.svelte';
|
||||
import { withKnobs, boolean, text } from "@storybook/addon-knobs";
|
||||
import Component from "./CodeSnippet.Story.svelte";
|
||||
|
||||
export default { title: 'CodeSnippet', decorators: [withKnobs] };
|
||||
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')
|
||||
}
|
||||
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 👍'),
|
||||
story: "single line",
|
||||
light: boolean("Light variant (light)", false),
|
||||
feedback: text("Feedback text (feedback)", "Feedback Enabled 👍"),
|
||||
copyButtonDescription: text(
|
||||
'Copy icon description (copyButtonDescription)',
|
||||
'copyable code snippet'
|
||||
"Copy icon description (copyButtonDescription)",
|
||||
"copyable code snippet"
|
||||
),
|
||||
'aria-label': text('ARIA label of the container (ariaLabel)', 'Container label')
|
||||
}
|
||||
"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')
|
||||
}
|
||||
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' } });
|
||||
export const Skeleton = () => ({ Component, props: { story: "skeleton" } });
|
||||
|
|
|
@ -1,97 +1,95 @@
|
|||
<script>
|
||||
let className = undefined;
|
||||
export { className as class };
|
||||
export let type = "single"; // "single" | "inline" | "multi"
|
||||
export let code = undefined;
|
||||
export let light = false;
|
||||
export let skeleton = false;
|
||||
export let copyButtonDescription = undefined;
|
||||
export let copyLabel = undefined;
|
||||
export let feedback = undefined;
|
||||
export let feedbackTimeout = undefined;
|
||||
export let id = Math.random();
|
||||
export let light = false;
|
||||
export let showLessText = 'Show less';
|
||||
export let showMoreText = 'Show more';
|
||||
export let skeleton = false;
|
||||
export let style = undefined;
|
||||
export let type = 'single';
|
||||
export let feedback = "Copied!";
|
||||
export let feedbackTimeout = 2000;
|
||||
export let showLessText = "Show less";
|
||||
export let showMoreText = "Show more";
|
||||
export let id = "ccs-" + Math.random().toString(36);
|
||||
export let ref = null;
|
||||
|
||||
import { afterUpdate } 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';
|
||||
import CodeSnippetSkeleton from './CodeSnippet.Skeleton.svelte';
|
||||
import { afterUpdate } from "svelte";
|
||||
import ChevronDown16 from "carbon-icons-svelte/lib/ChevronDown16";
|
||||
import { Button } from "../Button";
|
||||
import { Copy } from "../Copy";
|
||||
import { CopyButton } from "../CopyButton";
|
||||
import CodeSnippetSkeleton from "./CodeSnippet.Skeleton.svelte";
|
||||
|
||||
let codeRef = undefined;
|
||||
let expanded = false;
|
||||
let showMoreLess = false;
|
||||
$: showMoreLess = false;
|
||||
$: expanded = false;
|
||||
$: expandText = expanded ? showLessText : showMoreText;
|
||||
|
||||
afterUpdate(() => {
|
||||
if (type === 'multi' && codeRef) {
|
||||
showMoreLess = codeRef.getBoundingClientRect().height > 255;
|
||||
if (type === "multi" && ref) {
|
||||
showMoreLess = ref.getBoundingClientRect().height > 255;
|
||||
}
|
||||
});
|
||||
|
||||
$: expandText = expanded ? showLessText : showMoreText;
|
||||
</script>
|
||||
|
||||
{#if skeleton}
|
||||
<CodeSnippetSkeleton class={className} {type} {style} />
|
||||
{/if}
|
||||
|
||||
{#if !skeleton}
|
||||
<CodeSnippetSkeleton {type} {...$$restProps} />
|
||||
{:else}
|
||||
{#if type === 'inline'}
|
||||
<Copy
|
||||
aria-label={$$props['aria-label'] || copyLabel}
|
||||
aria-label={copyLabel}
|
||||
aria-describedby={id}
|
||||
class={cx('--snippet', type && `--snippet--${type}`, type === 'inline' && '--btn--copy', expanded && '--snippet--expand', light && '--snippet--light', className)}
|
||||
on:click
|
||||
class="bx--snippet {type && `bx--snippet--${type}`}
|
||||
{type === 'inline' && 'bx--btn--copy'}
|
||||
{expanded && 'bx--snippet--expand'}
|
||||
{light && 'bx--snippet--light'}"
|
||||
{...$$restProps}
|
||||
on:clicks
|
||||
on:mouseover
|
||||
on:mouseenter
|
||||
on:mouseleave
|
||||
{feedback}
|
||||
{feedbackTimeout}
|
||||
{style}>
|
||||
on:mouseleave>
|
||||
<code {id}>
|
||||
<slot>{code}</slot>
|
||||
</code>
|
||||
</Copy>
|
||||
{:else}
|
||||
<div
|
||||
class:bx--snippet={true}
|
||||
class={type && `bx--snippet--${type}`}
|
||||
class:bx--btn--copy={type === 'inline'}
|
||||
class:bx--snippet--expand={expanded}
|
||||
class:bx--snippet--light={light}
|
||||
{...$$restProps}
|
||||
on:mouseover
|
||||
on:mouseenter
|
||||
on:mouseleave
|
||||
class={cx('--snippet', type && `--snippet--${type}`, type === 'inline' && '--btn--copy', expanded && '--snippet--expand', light && '--snippet--light', className)}
|
||||
{style}>
|
||||
on:mouseleave>
|
||||
<div
|
||||
role="textbox"
|
||||
tabindex="0"
|
||||
class={cx('--snippet-container')}
|
||||
aria-label={$$props['aria-label'] || copyLabel || 'code-snippet'}>
|
||||
role={type === 'single' ? 'textbox' : undefined}
|
||||
tabindex={type === 'single' ? '0' : undefined}
|
||||
class:bx--snippet-container={true}
|
||||
aria-label={$$restProps['aria-label'] || copyLabel || 'code-snippet'}>
|
||||
<code>
|
||||
<pre bind:this={codeRef}>
|
||||
<pre bind:this={ref}>
|
||||
<slot>{code}</slot>
|
||||
</pre>
|
||||
</code>
|
||||
</div>
|
||||
<CopyButton
|
||||
iconDescription={copyButtonDescription}
|
||||
class={cx('--snippet-button')}
|
||||
on:click
|
||||
{feedback}
|
||||
{feedbackTimeout} />
|
||||
{feedbackTimeout}
|
||||
iconDescription={copyButtonDescription}
|
||||
on:click
|
||||
on:animationend />
|
||||
{#if showMoreLess}
|
||||
<Button
|
||||
kind="ghost"
|
||||
size="small"
|
||||
class={cx('--snippet-btn--expand')}
|
||||
class="bx--snippet-btn--expand"
|
||||
on:click={() => {
|
||||
expanded = !expanded;
|
||||
}}>
|
||||
<span class={cx('--snippet-btn--text')}>{expandText}</span>
|
||||
<span class:bx--snippet-btn--text={true}>{expandText}</span>
|
||||
<ChevronDown16
|
||||
aria-label={expandText}
|
||||
class={cx('--icon-chevron--down', '--snippet__icon')} />
|
||||
class="bx--icon-chevron--down bx--snippet__icon"
|
||||
aria-label={expandText} />
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,2 @@
|
|||
import CodeSnippet from './CodeSnippet.svelte';
|
||||
|
||||
export default CodeSnippet;
|
||||
export { default as CodeSnippetSkeleton } from './CodeSnippet.Skeleton.svelte';
|
||||
export { default as CodeSnippet } from "./CodeSnippet.svelte";
|
||||
export { default as CodeSnippetSkeleton } from "./CodeSnippet.Skeleton.svelte";
|
||||
|
|
|
@ -1,23 +1,25 @@
|
|||
<script>
|
||||
import Layout from '../../internal/ui/Layout.svelte';
|
||||
import ToggleSmall from '../ToggleSmall';
|
||||
import Button from '../Button';
|
||||
import ComboBox from './ComboBox.svelte';
|
||||
import { ToggleSmall } from "../ToggleSmall";
|
||||
import { Button } from "../Button";
|
||||
import ComboBox from "./ComboBox.svelte";
|
||||
|
||||
let items = [
|
||||
{ id: 'option-0', text: 'Option 1' },
|
||||
{ id: 'option-1', text: 'Option 2' },
|
||||
{ id: 'option-2', text: 'Option 3' },
|
||||
{ id: 'option-3', text: 'Option 4' },
|
||||
{ id: "option-0", text: "Option 1" },
|
||||
{ id: "option-1", text: "Option 2" },
|
||||
{ id: "option-2", text: "Option 3" },
|
||||
{ id: "option-3", text: "Option 4" },
|
||||
{
|
||||
id: 'option-4',
|
||||
text: 'An example option that is really long to show what should be done to handle long text'
|
||||
id: "option-4",
|
||||
text:
|
||||
"An example option that is really long to show what should be done to handle long text"
|
||||
}
|
||||
];
|
||||
|
||||
let toggled = false;
|
||||
let value = undefined;
|
||||
let selectedIndex = -1;
|
||||
$: toggled = false;
|
||||
$: value = undefined;
|
||||
$: selectedIndex = -1;
|
||||
$: ref = null;
|
||||
$: ref && ref.focus();
|
||||
|
||||
function shouldFilterItem(item, value) {
|
||||
if (!toggled || !value) {
|
||||
|
@ -28,26 +30,34 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
<Layout>
|
||||
<p>Currently, this component does not support items as slots.</p>
|
||||
<p>
|
||||
<code>items</code>
|
||||
must be an array of objects; mandatory fields are `id` and `text`.
|
||||
</p>
|
||||
<pre style="margin-top: 1rem;">
|
||||
<code>{'items = Array<{ id: string; text: string; }>'}</code>
|
||||
</pre>
|
||||
<div style="margin-top: 2rem;">
|
||||
<ToggleSmall labelText="Enable filtering" bind:toggled />
|
||||
<Button
|
||||
size="small"
|
||||
on:click={() => {
|
||||
selectedIndex = 1;
|
||||
}}>
|
||||
Set item to 'Option 2'
|
||||
</Button>
|
||||
</div>
|
||||
<div style="width: 300px; margin-top: 2rem;">
|
||||
<ComboBox {...$$props} id="combobox" bind:value bind:selectedIndex {items} {shouldFilterItem} />
|
||||
</div>
|
||||
</Layout>
|
||||
<p>Currently, this component does not support items as slots.</p>
|
||||
<p>
|
||||
<code>items</code>
|
||||
must be an array of objects; mandatory fields are `id` and `text`.
|
||||
</p>
|
||||
<pre style="margin-top: 1rem;">
|
||||
<code>{'items = Array<{ id: string; text: string; }>'}</code>
|
||||
</pre>
|
||||
<div style="margin-top: 2rem;">
|
||||
<ToggleSmall
|
||||
labelText="Enable filtering"
|
||||
bind:toggled
|
||||
style="margin-top: 1rem;" />
|
||||
<Button
|
||||
size="small"
|
||||
on:click={() => {
|
||||
selectedIndex = 1;
|
||||
}}>
|
||||
Set item to 'Option 2'
|
||||
</Button>
|
||||
</div>
|
||||
<div style="width: 300px; margin-top: 2rem;">
|
||||
<ComboBox
|
||||
{...$$props}
|
||||
id="combobox"
|
||||
bind:ref
|
||||
bind:value
|
||||
bind:selectedIndex
|
||||
{items}
|
||||
{shouldFilterItem} />
|
||||
</div>
|
||||
|
|
|
@ -1,25 +1,27 @@
|
|||
import { withKnobs, select, boolean, text } from '@storybook/addon-knobs';
|
||||
import Component from './ComboBox.Story.svelte';
|
||||
import { withKnobs, select, boolean, text } from "@storybook/addon-knobs";
|
||||
import Component from "./ComboBox.Story.svelte";
|
||||
|
||||
export default { title: 'ComboBox', decorators: [withKnobs] };
|
||||
export default { title: "ComboBox", decorators: [withKnobs] };
|
||||
|
||||
const sizes = {
|
||||
'Extra large size (xl)': 'xl',
|
||||
'Regular size (lg)': '',
|
||||
'Small size (sm)': 'sm'
|
||||
"Extra large size (xl)": "xl",
|
||||
"Regular size (lg)": "",
|
||||
"Small size (sm)": "sm",
|
||||
};
|
||||
|
||||
export const Default = () => ({
|
||||
Component,
|
||||
props: {
|
||||
size: select('Field size (size)', sizes, ''),
|
||||
placeholder: text('Placeholder text (placeholder)', 'Filter...'),
|
||||
titleText: text('Title (titleText)', 'Combobox title'),
|
||||
helperText: text('Helper text (helperText)', 'Optional helper text here'),
|
||||
light: boolean('Light (light)', false),
|
||||
disabled: boolean('Disabled (disabled)', false),
|
||||
invalid: boolean('Invalid (invalid)', false),
|
||||
invalidText: text('Invalid text (invalidText)', 'A valid value is required'),
|
||||
name: 'combo-box-name'
|
||||
}
|
||||
size: select("Field size (size)", sizes, ""),
|
||||
placeholder: text("Placeholder text (placeholder)", "Filter..."),
|
||||
titleText: text("Title (titleText)", "Combobox title"),
|
||||
light: boolean("Light (light)", false),
|
||||
disabled: boolean("Disabled (disabled)", false),
|
||||
invalid: boolean("Invalid (invalid)", false),
|
||||
invalidText: text(
|
||||
"Invalid text (invalidText)",
|
||||
"A valid value is required"
|
||||
),
|
||||
name: "combo-box-name",
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,39 +1,36 @@
|
|||
<script>
|
||||
let className = undefined;
|
||||
export { className as class };
|
||||
export let disabled = false;
|
||||
export let helperText = '';
|
||||
export let id = Math.random();
|
||||
export let helperText = "";
|
||||
export let id = "ccs-" + Math.random().toString(36);
|
||||
export let invalid = false;
|
||||
export let invalidText = '';
|
||||
export let invalidText = "";
|
||||
export let items = [];
|
||||
export let itemToString = item => item.text || item.id;
|
||||
export let light = false;
|
||||
export let open = false;
|
||||
export let placeholder = '';
|
||||
export let placeholder = "";
|
||||
export let selectedIndex = -1;
|
||||
export let shouldFilterItem = () => true;
|
||||
export let size = undefined;
|
||||
export let style = undefined;
|
||||
export let titleText = '';
|
||||
export let titleText = "";
|
||||
export let translateWithId = undefined;
|
||||
export let value = '';
|
||||
export let value = "";
|
||||
export let name = undefined;
|
||||
export let ref = null;
|
||||
|
||||
import { afterUpdate } from 'svelte';
|
||||
import WarningFilled16 from 'carbon-icons-svelte/lib/WarningFilled16';
|
||||
import { cx } from '../../lib';
|
||||
import ListBox, {
|
||||
import { afterUpdate } from "svelte";
|
||||
import WarningFilled16 from "carbon-icons-svelte/lib/WarningFilled16";
|
||||
import {
|
||||
ListBox,
|
||||
ListBoxField,
|
||||
ListBoxMenu,
|
||||
ListBoxMenuIcon,
|
||||
ListBoxMenuItem,
|
||||
ListBoxSelection
|
||||
} from '../ListBox';
|
||||
} from "../ListBox";
|
||||
|
||||
let selectedId = undefined;
|
||||
let inputRef = undefined;
|
||||
let inputValue = '';
|
||||
let inputValue = "";
|
||||
let highlightedIndex = -1;
|
||||
|
||||
function change(direction) {
|
||||
|
@ -50,18 +47,20 @@
|
|||
|
||||
afterUpdate(() => {
|
||||
if (open) {
|
||||
inputRef.focus();
|
||||
ref.focus();
|
||||
filteredItems = items.filter(item => shouldFilterItem(item, value));
|
||||
} else {
|
||||
highlightedIndex = -1;
|
||||
inputValue = selectedItem ? selectedItem.text : '';
|
||||
inputValue = selectedItem ? selectedItem.text : "";
|
||||
}
|
||||
});
|
||||
|
||||
$: ariaLabel = $$props['aria-label'] || 'Choose an item';
|
||||
$: ariaLabel = $$props["aria-label"] || "Choose an item";
|
||||
$: menuId = `menu-${id}`;
|
||||
$: comboId = `combo-${id}`;
|
||||
$: highlightedId = items[highlightedIndex] ? items[highlightedIndex].id : undefined;
|
||||
$: highlightedId = items[highlightedIndex]
|
||||
? items[highlightedIndex].id
|
||||
: undefined;
|
||||
$: filteredItems = items.filter(item => shouldFilterItem(item, value));
|
||||
$: selectedItem = items[selectedIndex];
|
||||
$: inputValue = selectedItem ? selectedItem.text : undefined;
|
||||
|
@ -70,22 +69,26 @@
|
|||
|
||||
<svelte:body
|
||||
on:click={({ target }) => {
|
||||
if (open && inputRef && !inputRef.contains(target)) {
|
||||
if (open && ref && !ref.contains(target)) {
|
||||
open = false;
|
||||
}
|
||||
}} />
|
||||
|
||||
<div class={cx('--list-box__wrapper', className)} {style}>
|
||||
<div class:bx--list-box__wrapper={true} {...$$restProps}>
|
||||
{#if titleText}
|
||||
<label class={cx('--label', disabled && '--label--disabled')} for={id}>{titleText}</label>
|
||||
<label for={id} class:bx--label={true} class:bx--label--disabled={disabled}>
|
||||
{titleText}
|
||||
</label>
|
||||
{/if}
|
||||
{#if helperText}
|
||||
<div class={cx('--form__helper-text', disabled && '--form__helper-text--disabled')}>
|
||||
<div
|
||||
class:bx--form__helper-text={true}
|
||||
class:bx--form__helper-text--disabled={disabled}>
|
||||
{helperText}
|
||||
</div>
|
||||
{/if}
|
||||
<ListBox
|
||||
class={cx('--combo-box')}
|
||||
class="bx--combo-box"
|
||||
id={comboId}
|
||||
aria-label={ariaLabel}
|
||||
{disabled}
|
||||
|
@ -105,7 +108,7 @@
|
|||
{disabled}
|
||||
{translateWithId}>
|
||||
<input
|
||||
bind:this={inputRef}
|
||||
bind:this={ref}
|
||||
tabindex="0"
|
||||
autocomplete="off"
|
||||
aria-autocomplete="list"
|
||||
|
@ -115,7 +118,8 @@
|
|||
aria-disabled={disabled}
|
||||
aria-controls={open ? menuId : undefined}
|
||||
aria-owns={open ? menuId : undefined}
|
||||
class={cx('--text-input', inputValue === '' && '--text-input--empty')}
|
||||
class:bx--text-input={true}
|
||||
class:bx--text-input--empty={inputValue === ''}
|
||||
on:input={({ target }) => {
|
||||
inputValue = target.value;
|
||||
}}
|
||||
|
@ -139,7 +143,7 @@
|
|||
on:blur
|
||||
on:blur={({ relatedTarget }) => {
|
||||
if (relatedTarget && relatedTarget.getAttribute('role') !== 'button') {
|
||||
inputRef.focus();
|
||||
ref.focus();
|
||||
}
|
||||
}}
|
||||
{disabled}
|
||||
|
@ -147,7 +151,7 @@
|
|||
{id}
|
||||
value={inputValue} />
|
||||
{#if invalid}
|
||||
<WarningFilled16 class={cx('--list-box__invalid-icon')} />
|
||||
<WarningFilled16 class="bx--list-box__invalid-icon" />
|
||||
{/if}
|
||||
{#if inputValue}
|
||||
<ListBoxSelection
|
||||
|
@ -175,7 +179,9 @@
|
|||
highlighted={highlightedIndex === i || selectedIndex === i}
|
||||
on:click={() => {
|
||||
selectedId = item.id;
|
||||
selectedIndex = items.map(({ id }) => id).indexOf(filteredItems[i].id);
|
||||
selectedIndex = items
|
||||
.map(({ id }) => id)
|
||||
.indexOf(filteredItems[i].id);
|
||||
open = false;
|
||||
}}
|
||||
on:mouseenter={() => {
|
||||
|
|
|
@ -1,3 +1 @@
|
|||
import ComboBox from './ComboBox.svelte';
|
||||
|
||||
export default ComboBox;
|
||||
export { default as ComboBox } from "./ComboBox.svelte";
|
||||
|
|
|
@ -2,14 +2,14 @@
|
|||
export let story = undefined;
|
||||
const { modalBody } = $$props;
|
||||
|
||||
import Layout from '../../internal/ui/Layout.svelte';
|
||||
import Button from '../Button';
|
||||
import ComposedModal from './ComposedModal.svelte';
|
||||
import ModalHeader from './ModalHeader.svelte';
|
||||
import ModalBody from './ModalBody.svelte';
|
||||
import ModalFooter from './ModalFooter.svelte';
|
||||
import Layout from "../../internal/ui/Layout.svelte";
|
||||
import { Button } from "../Button";
|
||||
import ComposedModal from "./ComposedModal.svelte";
|
||||
import ModalHeader from "./ModalHeader.svelte";
|
||||
import ModalBody from "./ModalBody.svelte";
|
||||
import ModalFooter from "./ModalFooter.svelte";
|
||||
|
||||
let open = false;
|
||||
$: open = false;
|
||||
</script>
|
||||
|
||||
<Layout>
|
||||
|
@ -19,13 +19,17 @@
|
|||
<ModalBody
|
||||
{...$$props.modalBody}
|
||||
aria-label={modalBody.hasScrollingContent ? 'Modal content' : undefined}>
|
||||
<p>Please see ModalWrapper for more examples and demo of the functionality.</p>
|
||||
<p>
|
||||
Please see ModalWrapper for more examples and demo of the
|
||||
functionality.
|
||||
</p>
|
||||
{#if modalBody.hasScrollingContent}
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean id accumsan augue.
|
||||
Phasellus consequat augue vitae tellus tincidunt posuere. Curabitur justo urna,
|
||||
consectetur vel elit iaculis, ultrices condimentum risus. Nulla facilisi. Etiam
|
||||
venenatis molestie tellus. Quisque consectetur non risus eu rutrum.{' '}
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean id
|
||||
accumsan augue. Phasellus consequat augue vitae tellus tincidunt
|
||||
posuere. Curabitur justo urna, consectetur vel elit iaculis,
|
||||
ultrices condimentum risus. Nulla facilisi. Etiam venenatis molestie
|
||||
tellus. Quisque consectetur non risus eu rutrum.{' '}
|
||||
</p>
|
||||
{/if}
|
||||
</ModalBody>
|
||||
|
@ -41,28 +45,39 @@
|
|||
<ModalBody
|
||||
{...$$props.modalBody}
|
||||
aria-label={modalBody.hasScrollingContent ? 'Modal content' : undefined}>
|
||||
<p>Please see ModalWrapper for more examples and demo of the functionality.</p>
|
||||
<p>
|
||||
Please see ModalWrapper for more examples and demo of the
|
||||
functionality.
|
||||
</p>
|
||||
{#if modalBody.hasScrollingContent}
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean id accumsan augue.
|
||||
Phasellus consequat augue vitae tellus tincidunt posuere. Curabitur justo urna,
|
||||
consectetur vel elit iaculis, ultrices condimentum risus. Nulla facilisi. Etiam
|
||||
venenatis molestie tellus. Quisque consectetur non risus eu rutrum.{' '}
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean id
|
||||
accumsan augue. Phasellus consequat augue vitae tellus tincidunt
|
||||
posuere. Curabitur justo urna, consectetur vel elit iaculis,
|
||||
ultrices condimentum risus. Nulla facilisi. Etiam venenatis molestie
|
||||
tellus. Quisque consectetur non risus eu rutrum.{' '}
|
||||
</p>
|
||||
{/if}
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button kind="secondary">Cancel</Button>
|
||||
<Button kind={$$props.composedModal.danger ? 'danger' : 'primary'}>Primary</Button>
|
||||
<Button kind={$$props.composedModal.danger ? 'danger' : 'primary'}>
|
||||
Primary
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ComposedModal>
|
||||
{/if}
|
||||
|
||||
{#if story === 'title'}
|
||||
<ComposedModal {...$$props.composedModal} open on:close={() => {}} on:submit={() => {}}>
|
||||
<ComposedModal
|
||||
{...$$props.composedModal}
|
||||
open
|
||||
on:close={() => {}}
|
||||
on:submit={() => {}}>
|
||||
<ModalHeader
|
||||
{...$$props.modalHeader}
|
||||
title="Passive modal title as the message. Should be direct and 3 lines or less." />
|
||||
title="Passive modal title as the message. Should be direct and 3 lines
|
||||
or less." />
|
||||
<ModalBody {...$$props.modalBody} />
|
||||
<ModalFooter {...$$props.modalFooter} />
|
||||
</ComposedModal>
|
||||
|
@ -77,55 +92,68 @@
|
|||
Launch composed modal
|
||||
</Button>
|
||||
</div>
|
||||
<ComposedModal {...$$props.composedModal} {open} on:close={() => (open = false)}>
|
||||
<ComposedModal
|
||||
{...$$props.composedModal}
|
||||
{open}
|
||||
on:close={() => (open = false)}>
|
||||
<ModalHeader {...$$props.modalHeader} />
|
||||
<ModalBody
|
||||
{...$$props.modalBody}
|
||||
aria-label={modalBody.hasScrollingContent ? 'Modal content' : undefined}>
|
||||
<p>Please see ModalWrapper for more examples and demo of the functionality.</p>
|
||||
<p>
|
||||
Please see ModalWrapper for more examples and demo of the
|
||||
functionality.
|
||||
</p>
|
||||
{#if modalBody.hasScrollingContent}
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean id accumsan augue.
|
||||
Phasellus consequat augue vitae tellus tincidunt posuere. Curabitur justo urna,
|
||||
consectetur vel elit iaculis, ultrices condimentum risus. Nulla facilisi. Etiam
|
||||
venenatis molestie tellus. Quisque consectetur non risus eu rutrum.{' '}
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean id
|
||||
accumsan augue. Phasellus consequat augue vitae tellus tincidunt
|
||||
posuere. Curabitur justo urna, consectetur vel elit iaculis,
|
||||
ultrices condimentum risus. Nulla facilisi. Etiam venenatis molestie
|
||||
tellus. Quisque consectetur non risus eu rutrum.{' '}
|
||||
</p>
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean id accumsan augue.
|
||||
Phasellus consequat augue vitae tellus tincidunt posuere. Curabitur justo urna,
|
||||
consectetur vel elit iaculis, ultrices condimentum risus. Nulla facilisi. Etiam
|
||||
venenatis molestie tellus. Quisque consectetur non risus eu rutrum.{' '}
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean id
|
||||
accumsan augue. Phasellus consequat augue vitae tellus tincidunt
|
||||
posuere. Curabitur justo urna, consectetur vel elit iaculis,
|
||||
ultrices condimentum risus. Nulla facilisi. Etiam venenatis molestie
|
||||
tellus. Quisque consectetur non risus eu rutrum.{' '}
|
||||
</p>
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean id accumsan augue.
|
||||
Phasellus consequat augue vitae tellus tincidunt posuere. Curabitur justo urna,
|
||||
consectetur vel elit iaculis, ultrices condimentum risus. Nulla facilisi. Etiam
|
||||
venenatis molestie tellus. Quisque consectetur non risus eu rutrum.{' '}
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean id
|
||||
accumsan augue. Phasellus consequat augue vitae tellus tincidunt
|
||||
posuere. Curabitur justo urna, consectetur vel elit iaculis,
|
||||
ultrices condimentum risus. Nulla facilisi. Etiam venenatis molestie
|
||||
tellus. Quisque consectetur non risus eu rutrum.{' '}
|
||||
</p>
|
||||
<h3>Lorem ipsum</h3>
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean id accumsan augue.
|
||||
Phasellus consequat augue vitae tellus tincidunt posuere. Curabitur justo urna,
|
||||
consectetur vel elit iaculis, ultrices condimentum risus. Nulla facilisi. Etiam
|
||||
venenatis molestie tellus. Quisque consectetur non risus eu rutrum.{' '}
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean id
|
||||
accumsan augue. Phasellus consequat augue vitae tellus tincidunt
|
||||
posuere. Curabitur justo urna, consectetur vel elit iaculis,
|
||||
ultrices condimentum risus. Nulla facilisi. Etiam venenatis molestie
|
||||
tellus. Quisque consectetur non risus eu rutrum.{' '}
|
||||
</p>
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean id accumsan augue.
|
||||
Phasellus consequat augue vitae tellus tincidunt posuere. Curabitur justo urna,
|
||||
consectetur vel elit iaculis, ultrices condimentum risus. Nulla facilisi. Etiam
|
||||
venenatis molestie tellus. Quisque consectetur non risus eu rutrum.{' '}
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean id
|
||||
accumsan augue. Phasellus consequat augue vitae tellus tincidunt
|
||||
posuere. Curabitur justo urna, consectetur vel elit iaculis,
|
||||
ultrices condimentum risus. Nulla facilisi. Etiam venenatis molestie
|
||||
tellus. Quisque consectetur non risus eu rutrum.{' '}
|
||||
</p>
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean id accumsan augue.
|
||||
Phasellus consequat augue vitae tellus tincidunt posuere. Curabitur justo urna,
|
||||
consectetur vel elit iaculis, ultrices condimentum risus. Nulla facilisi. Etiam
|
||||
venenatis molestie tellus. Quisque consectetur non risus eu rutrum.{' '}
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean id
|
||||
accumsan augue. Phasellus consequat augue vitae tellus tincidunt
|
||||
posuere. Curabitur justo urna, consectetur vel elit iaculis,
|
||||
ultrices condimentum risus. Nulla facilisi. Etiam venenatis molestie
|
||||
tellus. Quisque consectetur non risus eu rutrum.{' '}
|
||||
</p>
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean id accumsan augue.
|
||||
Phasellus consequat augue vitae tellus tincidunt posuere. Curabitur justo urna,
|
||||
consectetur vel elit iaculis, ultrices condimentum risus. Nulla facilisi. Etiam
|
||||
venenatis molestie tellus. Quisque consectetur non risus eu rutrum.{' '}
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean id
|
||||
accumsan augue. Phasellus consequat augue vitae tellus tincidunt
|
||||
posuere. Curabitur justo urna, consectetur vel elit iaculis,
|
||||
ultrices condimentum risus. Nulla facilisi. Etiam venenatis molestie
|
||||
tellus. Quisque consectetur non risus eu rutrum.{' '}
|
||||
</p>
|
||||
{/if}
|
||||
</ModalBody>
|
||||
|
|
|
@ -1,135 +1,177 @@
|
|||
import { withKnobs, select, boolean, text } from '@storybook/addon-knobs';
|
||||
import Component from './ComposedModal.Story.svelte';
|
||||
import { withKnobs, select, boolean, text } from "@storybook/addon-knobs";
|
||||
import Component from "./ComposedModal.Story.svelte";
|
||||
|
||||
export default { title: 'ComposedModal', decorators: [withKnobs] };
|
||||
export default { title: "ComposedModal", decorators: [withKnobs] };
|
||||
|
||||
const sizes = {
|
||||
Default: '',
|
||||
'Extra small (xs)': 'xs',
|
||||
'Small (sm)': 'sm',
|
||||
'Large (lg)': 'lg'
|
||||
Default: "",
|
||||
"Extra small (xs)": "xs",
|
||||
"Small (sm)": "sm",
|
||||
"Large (lg)": "lg",
|
||||
};
|
||||
|
||||
export const Default = () => ({
|
||||
Component,
|
||||
props: {
|
||||
composedModal: {
|
||||
open: boolean('Open (open in <ComposedModal>)', true),
|
||||
danger: boolean('Danger mode (danger)', false),
|
||||
open: boolean("Open (open in <ComposedModal>)", true),
|
||||
danger: boolean("Danger mode (danger)", false),
|
||||
selectorPrimaryFocus: text(
|
||||
'Primary focus element selector (selectorPrimaryFocus)',
|
||||
'[data-modal-primary-focus]'
|
||||
"Primary focus element selector (selectorPrimaryFocus)",
|
||||
"[data-modal-primary-focus]"
|
||||
),
|
||||
size: select('Size (size)', sizes, 'sm')
|
||||
size: select("Size (size)", sizes, "sm"),
|
||||
},
|
||||
modalHeader: {
|
||||
label: text('Optional Label (label in <ModalHeader>)', 'Optional Label'),
|
||||
title: text('Optional title (title in <ModalHeader>)', 'Example'),
|
||||
iconDescription: text('Close icon description (iconDescription in <ModalHeader>)', 'Close')
|
||||
label: text("Optional Label (label in <ModalHeader>)", "Optional Label"),
|
||||
title: text("Optional title (title in <ModalHeader>)", "Example"),
|
||||
iconDescription: text(
|
||||
"Close icon description (iconDescription in <ModalHeader>)",
|
||||
"Close"
|
||||
),
|
||||
},
|
||||
modalBody: {
|
||||
hasScrollingContent: boolean('Modal contains scrollable content (hasScrollingContent)', true),
|
||||
'aria-label': text('ARIA label for content', 'Example modal content')
|
||||
hasScrollingContent: boolean(
|
||||
"Modal contains scrollable content (hasScrollingContent)",
|
||||
true
|
||||
),
|
||||
"aria-label": text("ARIA label for content", "Example modal content"),
|
||||
},
|
||||
modalFooter: {
|
||||
primaryButtonText: text('Primary button text (primaryButtonText in <ModalFooter>)', 'Save'),
|
||||
primaryButtonText: text(
|
||||
"Primary button text (primaryButtonText in <ModalFooter>)",
|
||||
"Save"
|
||||
),
|
||||
primaryButtonDisabled: boolean(
|
||||
'Primary button disabled (primaryButtonDisabled in <ModalFooter>)',
|
||||
"Primary button disabled (primaryButtonDisabled in <ModalFooter>)",
|
||||
false
|
||||
),
|
||||
secondaryButtonText: text('Secondary button text (secondaryButtonText in <ModalFooter>)', '')
|
||||
}
|
||||
}
|
||||
secondaryButtonText: text(
|
||||
"Secondary button text (secondaryButtonText in <ModalFooter>)",
|
||||
""
|
||||
),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const ChildNodes = () => ({
|
||||
Component,
|
||||
props: {
|
||||
story: 'child nodes',
|
||||
story: "child nodes",
|
||||
composedModal: {
|
||||
open: boolean('Open (open in <ComposedModal>)', true),
|
||||
danger: boolean('Danger mode (danger)', false),
|
||||
open: boolean("Open (open in <ComposedModal>)", true),
|
||||
danger: boolean("Danger mode (danger)", false),
|
||||
selectorPrimaryFocus: text(
|
||||
'Primary focus element selector (selectorPrimaryFocus)',
|
||||
'[data-modal-primary-focus]'
|
||||
"Primary focus element selector (selectorPrimaryFocus)",
|
||||
"[data-modal-primary-focus]"
|
||||
),
|
||||
size: select('Size (size)', sizes, 'sm')
|
||||
size: select("Size (size)", sizes, "sm"),
|
||||
},
|
||||
modalHeader: {
|
||||
label: text('Optional Label (label in <ModalHeader>)', 'Optional Label'),
|
||||
title: text('Optional title (title in <ModalHeader>)', 'Example'),
|
||||
iconDescription: text('Close icon description (iconDescription in <ModalHeader>)', 'Close')
|
||||
label: text("Optional Label (label in <ModalHeader>)", "Optional Label"),
|
||||
title: text("Optional title (title in <ModalHeader>)", "Example"),
|
||||
iconDescription: text(
|
||||
"Close icon description (iconDescription in <ModalHeader>)",
|
||||
"Close"
|
||||
),
|
||||
},
|
||||
modalBody: {
|
||||
hasScrollingContent: boolean('Modal contains scrollable content (hasScrollingContent)', true),
|
||||
'aria-label': text('ARIA label for content', 'Example modal content')
|
||||
hasScrollingContent: boolean(
|
||||
"Modal contains scrollable content (hasScrollingContent)",
|
||||
true
|
||||
),
|
||||
"aria-label": text("ARIA label for content", "Example modal content"),
|
||||
},
|
||||
modalFooter: {}
|
||||
}
|
||||
modalFooter: {},
|
||||
},
|
||||
});
|
||||
|
||||
export const TitleOnly = () => ({
|
||||
Component,
|
||||
props: {
|
||||
story: 'title',
|
||||
story: "title",
|
||||
composedModal: {
|
||||
open: boolean('Open (open in <ComposedModal>)', true),
|
||||
danger: boolean('Danger mode (danger)', false),
|
||||
open: boolean("Open (open in <ComposedModal>)", true),
|
||||
danger: boolean("Danger mode (danger)", false),
|
||||
selectorPrimaryFocus: text(
|
||||
'Primary focus element selector (selectorPrimaryFocus)',
|
||||
'[data-modal-primary-focus]'
|
||||
"Primary focus element selector (selectorPrimaryFocus)",
|
||||
"[data-modal-primary-focus]"
|
||||
),
|
||||
size: select('Size (size)', sizes, 'sm')
|
||||
size: select("Size (size)", sizes, "sm"),
|
||||
},
|
||||
modalHeader: {
|
||||
label: text('Optional Label (label in <ModalHeader>)', 'Optional Label'),
|
||||
title: text('Optional title (title in <ModalHeader>)', 'Example'),
|
||||
iconDescription: text('Close icon description (iconDescription in <ModalHeader>)', 'Close')
|
||||
label: text("Optional Label (label in <ModalHeader>)", "Optional Label"),
|
||||
title: text("Optional title (title in <ModalHeader>)", "Example"),
|
||||
iconDescription: text(
|
||||
"Close icon description (iconDescription in <ModalHeader>)",
|
||||
"Close"
|
||||
),
|
||||
},
|
||||
modalBody: {
|
||||
hasScrollingContent: boolean('Modal contains scrollable content (hasScrollingContent)', true),
|
||||
'aria-label': text('ARIA label for content', 'Example modal content')
|
||||
hasScrollingContent: boolean(
|
||||
"Modal contains scrollable content (hasScrollingContent)",
|
||||
true
|
||||
),
|
||||
"aria-label": text("ARIA label for content", "Example modal content"),
|
||||
},
|
||||
modalFooter: {
|
||||
primaryButtonText: text('Primary button text (primaryButtonText in <ModalFooter>)', 'Save'),
|
||||
primaryButtonText: text(
|
||||
"Primary button text (primaryButtonText in <ModalFooter>)",
|
||||
"Save"
|
||||
),
|
||||
primaryButtonDisabled: boolean(
|
||||
'Primary button disabled (primaryButtonDisabled in <ModalFooter>)',
|
||||
"Primary button disabled (primaryButtonDisabled in <ModalFooter>)",
|
||||
false
|
||||
),
|
||||
secondaryButtonText: text('Secondary button text (secondaryButtonText in <ModalFooter>)', '')
|
||||
}
|
||||
}
|
||||
secondaryButtonText: text(
|
||||
"Secondary button text (secondaryButtonText in <ModalFooter>)",
|
||||
""
|
||||
),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const Trigger = () => ({
|
||||
Component,
|
||||
props: {
|
||||
story: 'trigger',
|
||||
story: "trigger",
|
||||
composedModal: {
|
||||
open: boolean('Open (open in <ComposedModal>)', true),
|
||||
danger: boolean('Danger mode (danger)', false),
|
||||
open: boolean("Open (open in <ComposedModal>)", true),
|
||||
danger: boolean("Danger mode (danger)", false),
|
||||
selectorPrimaryFocus: text(
|
||||
'Primary focus element selector (selectorPrimaryFocus)',
|
||||
'[data-modal-primary-focus]'
|
||||
"Primary focus element selector (selectorPrimaryFocus)",
|
||||
"[data-modal-primary-focus]"
|
||||
),
|
||||
size: select('Size (size)', sizes, 'sm')
|
||||
size: select("Size (size)", sizes, "sm"),
|
||||
},
|
||||
modalHeader: {
|
||||
label: text('Optional Label (label in <ModalHeader>)', 'Optional Label'),
|
||||
title: text('Optional title (title in <ModalHeader>)', 'Example'),
|
||||
iconDescription: text('Close icon description (iconDescription in <ModalHeader>)', 'Close')
|
||||
label: text("Optional Label (label in <ModalHeader>)", "Optional Label"),
|
||||
title: text("Optional title (title in <ModalHeader>)", "Example"),
|
||||
iconDescription: text(
|
||||
"Close icon description (iconDescription in <ModalHeader>)",
|
||||
"Close"
|
||||
),
|
||||
},
|
||||
modalBody: {
|
||||
hasScrollingContent: boolean('Modal contains scrollable content (hasScrollingContent)', true),
|
||||
'aria-label': text('ARIA label for content', 'Example modal content')
|
||||
hasScrollingContent: boolean(
|
||||
"Modal contains scrollable content (hasScrollingContent)",
|
||||
true
|
||||
),
|
||||
"aria-label": text("ARIA label for content", "Example modal content"),
|
||||
},
|
||||
modalFooter: {
|
||||
primaryButtonText: text('Primary button text (primaryButtonText in <ModalFooter>)', 'Save'),
|
||||
primaryButtonText: text(
|
||||
"Primary button text (primaryButtonText in <ModalFooter>)",
|
||||
"Save"
|
||||
),
|
||||
primaryButtonDisabled: boolean(
|
||||
'Primary button disabled (primaryButtonDisabled in <ModalFooter>)',
|
||||
"Primary button disabled (primaryButtonDisabled in <ModalFooter>)",
|
||||
false
|
||||
),
|
||||
secondaryButtonText: text('Secondary button text (secondaryButtonText in <ModalFooter>)', '')
|
||||
}
|
||||
}
|
||||
secondaryButtonText: text(
|
||||
"Secondary button text (secondaryButtonText in <ModalFooter>)",
|
||||
""
|
||||
),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,48 +1,51 @@
|
|||
<script>
|
||||
let className = undefined;
|
||||
export { className as class };
|
||||
export let containerClass = undefined;
|
||||
export let danger = false;
|
||||
export let open = false;
|
||||
export let selectorPrimaryFocus = '[data-modal-primary-focus]';
|
||||
export let size = undefined;
|
||||
export let style = undefined;
|
||||
export let danger = false;
|
||||
export let size = undefined; // "xs" | "sm" | "lg"
|
||||
export let containerClass = "";
|
||||
export let selectorPrimaryFocus = "[data-modal-primary-focus]";
|
||||
export let ref = null;
|
||||
|
||||
import { createEventDispatcher, tick, setContext, onMount, afterUpdate } from 'svelte';
|
||||
import { cx } from '../../lib';
|
||||
import {
|
||||
createEventDispatcher,
|
||||
tick,
|
||||
setContext,
|
||||
onMount,
|
||||
afterUpdate
|
||||
} from "svelte";
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
let buttonRef = undefined;
|
||||
let outerModal = undefined;
|
||||
let innerModal = undefined;
|
||||
let buttonRef = null;
|
||||
let innerModal = null;
|
||||
|
||||
let opened = false;
|
||||
|
||||
setContext('ComposedModal', {
|
||||
setContext("ComposedModal", {
|
||||
closeModal: () => {
|
||||
open = false;
|
||||
},
|
||||
submit: () => {
|
||||
dispatch('submit');
|
||||
dispatch("submit");
|
||||
},
|
||||
declareRef: ref => {
|
||||
buttonRef = ref;
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: reuse in Modal
|
||||
function focus(element) {
|
||||
const node = (element || innerModal).querySelector(selectorPrimaryFocus) || buttonRef;
|
||||
const node =
|
||||
(element || innerModal).querySelector(selectorPrimaryFocus) || buttonRef;
|
||||
node.focus();
|
||||
}
|
||||
|
||||
$: opened = false;
|
||||
$: didOpen = open;
|
||||
|
||||
onMount(async () => {
|
||||
await tick();
|
||||
focus();
|
||||
|
||||
return () => {
|
||||
document.body.classList.remove(cx('--body--with-modal-open'));
|
||||
document.body.classList.remove("bx--body--with-modal-open");
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -50,24 +53,25 @@
|
|||
if (opened) {
|
||||
if (!open) {
|
||||
opened = false;
|
||||
dispatch('close');
|
||||
document.body.classList.add(cx('--body--with-modal-open'));
|
||||
dispatch("close");
|
||||
document.body.classList.add("bx--body--with-modal-open");
|
||||
}
|
||||
} else if (open) {
|
||||
opened = true;
|
||||
dispatch('open');
|
||||
document.body.classList.remove(cx('--body--with-modal-open'));
|
||||
dispatch("open");
|
||||
document.body.classList.remove("bx--body--with-modal-open");
|
||||
}
|
||||
});
|
||||
|
||||
$: didOpen = open;
|
||||
</script>
|
||||
|
||||
<div
|
||||
bind:this={outerModal}
|
||||
bind:this={ref}
|
||||
role="presentation"
|
||||
tabindex="-1"
|
||||
class={cx('--modal', open && 'is-visible', danger && '--modal--danger', className)}
|
||||
class:bx--modal={true}
|
||||
class:is-visible={open}
|
||||
class:bx--modal--danger={danger}
|
||||
{...$$restProps}
|
||||
on:click
|
||||
on:click={({ target }) => {
|
||||
if (!innerModal.contains(target)) {
|
||||
|
@ -83,11 +87,12 @@
|
|||
focus(currentTarget);
|
||||
didOpen = false;
|
||||
}
|
||||
}}
|
||||
{style}>
|
||||
}}>
|
||||
<div
|
||||
bind:this={innerModal}
|
||||
class={cx('--modal-container', size && `--modal-container--${size}`, containerClass)}>
|
||||
class:bx--modal-container={true}
|
||||
class="{size && `bx--modal-container--${size}`}
|
||||
{containerClass}">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,20 +1,16 @@
|
|||
<script>
|
||||
let className = undefined;
|
||||
export { className as class };
|
||||
export let hasForm = false;
|
||||
export let hasScrollingContent = false;
|
||||
export let style = undefined;
|
||||
|
||||
import { cx } from '../../lib';
|
||||
</script>
|
||||
|
||||
<div
|
||||
tabindex={hasScrollingContent ? '0' : undefined}
|
||||
role={hasScrollingContent ? 'region' : undefined}
|
||||
class={cx('--modal-content', hasForm && '--modal-content--with-form', className)}
|
||||
{style}>
|
||||
class:bx--modal-content={true}
|
||||
class:bx--modal-content--with-form={hasForm}
|
||||
{...$$restProps}>
|
||||
<slot />
|
||||
</div>
|
||||
{#if hasScrollingContent}
|
||||
<div class={cx('--modal-content--overflow-indicator')} />
|
||||
<div class:bx--modal-content--overflow-indicator={true} />
|
||||
{/if}
|
||||
|
|
|
@ -1,22 +1,18 @@
|
|||
<script>
|
||||
let className = undefined;
|
||||
export { className as class };
|
||||
export let primaryClass = undefined;
|
||||
export let primaryButtonText = '';
|
||||
export let primaryButtonText = "";
|
||||
export let primaryButtonDisabled = false;
|
||||
export let secondaryClass = undefined;
|
||||
export let secondaryButtonText = '';
|
||||
export let secondaryButtonText = "";
|
||||
export let danger = false;
|
||||
export let style = undefined;
|
||||
|
||||
import { getContext } from 'svelte';
|
||||
import { cx } from '../../lib';
|
||||
import Button from '../Button';
|
||||
import { getContext } from "svelte";
|
||||
import { Button } from "../Button";
|
||||
|
||||
const { closeModal, submit } = getContext('ComposedModal');
|
||||
const { closeModal, submit } = getContext("ComposedModal");
|
||||
</script>
|
||||
|
||||
<div class={cx('--modal-footer', className)} {style}>
|
||||
<div class:bx--modal-footer={true} {...$$restProps}>
|
||||
{#if secondaryButtonText}
|
||||
<Button kind="secondary" class={secondaryClass} on:click={closeModal}>
|
||||
{secondaryButtonText}
|
||||
|
@ -24,9 +20,9 @@
|
|||
{/if}
|
||||
{#if primaryButtonText}
|
||||
<Button
|
||||
class={primaryClass}
|
||||
kind={danger ? 'danger' : 'primary'}
|
||||
disabled={primaryButtonDisabled}
|
||||
class={primaryClass}
|
||||
on:click={submit}>
|
||||
{primaryButtonText}
|
||||
</Button>
|
||||
|
|
|
@ -1,37 +1,44 @@
|
|||
<script>
|
||||
let className = undefined;
|
||||
export { className as class };
|
||||
export let labelClass = undefined;
|
||||
export let titleClass = undefined;
|
||||
export let closeClass = undefined;
|
||||
export let closeIconClass = undefined;
|
||||
export let label = undefined;
|
||||
export let title = '';
|
||||
export let iconDescription = 'Close';
|
||||
export let style = undefined;
|
||||
export let title = "";
|
||||
export let label = "";
|
||||
export let labelClass = "";
|
||||
export let titleClass = "";
|
||||
export let closeClass = "";
|
||||
export let closeIconClass = "";
|
||||
export let iconDescription = "Close";
|
||||
|
||||
import { getContext } from 'svelte';
|
||||
import Close20 from 'carbon-icons-svelte/lib/Close20';
|
||||
import { cx } from '../../lib';
|
||||
import { getContext } from "svelte";
|
||||
import Close20 from "carbon-icons-svelte/lib/Close20";
|
||||
|
||||
const { closeModal } = getContext('ComposedModal');
|
||||
const { closeModal } = getContext("ComposedModal");
|
||||
</script>
|
||||
|
||||
<div class={cx('--modal-header', className)} {style}>
|
||||
<div class:bx--modal-header={true} {...$$restProps}>
|
||||
{#if label}
|
||||
<p class={cx('--modal-header__label', '--type-delta', labelClass)}>{label}</p>
|
||||
<p
|
||||
class:bx--modal-header__label={true}
|
||||
class:bx--type-delta={true}
|
||||
class:labelClass>
|
||||
{label}
|
||||
</p>
|
||||
{/if}
|
||||
{#if title}
|
||||
<p class={cx('--modal-header__heading', '--type-beta', titleClass)}>{title}</p>
|
||||
<p
|
||||
class:bx--modal-header__heading={true}
|
||||
class:bx--type-beta={true}
|
||||
class:titleClass>
|
||||
{title}
|
||||
</p>
|
||||
{/if}
|
||||
<slot />
|
||||
<button
|
||||
type="button"
|
||||
title={iconDescription}
|
||||
aria-label={iconDescription}
|
||||
class={cx('--modal-close', closeClass)}
|
||||
class:bx--modal-close={true}
|
||||
class:closeClass
|
||||
on:click
|
||||
on:click={closeModal}>
|
||||
<Close20 class={cx('--modal-close__icon', closeIconClass)} />
|
||||
<Close20 class="bx--modal-close__icon {closeIconClass}" />
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
import ComposedModal from './ComposedModal.svelte';
|
||||
|
||||
export default ComposedModal;
|
||||
export { default as ModalHeader } from './ModalHeader.svelte';
|
||||
export { default as ModalBody } from './ModalBody.svelte';
|
||||
export { default as ModalFooter } from './ModalFooter.svelte';
|
||||
export { default as ComposedModal } from "./ComposedModal.svelte";
|
||||
export { default as ModalHeader } from "./ModalHeader.svelte";
|
||||
export { default as ModalBody } from "./ModalBody.svelte";
|
||||
export { default as ModalFooter } from "./ModalFooter.svelte";
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
<script>
|
||||
export let story = undefined;
|
||||
|
||||
import Layout from '../../internal/ui/Layout.svelte';
|
||||
import ContentSwitcher from './ContentSwitcher.svelte';
|
||||
import Switch from './Switch.svelte';
|
||||
import Layout from "../../internal/ui/Layout.svelte";
|
||||
import ContentSwitcher from "./ContentSwitcher.svelte";
|
||||
import Switch from "./Switch.svelte";
|
||||
import Add16 from "carbon-icons-svelte/lib/Add16";
|
||||
|
||||
let selectedIndex = 0;
|
||||
$: selectedIndex = 0;
|
||||
$: console.log("bind selectedIndex", selectedIndex);
|
||||
</script>
|
||||
|
||||
<Layout>
|
||||
|
@ -26,7 +28,19 @@
|
|||
}}>
|
||||
<Switch {...$$props} text="First section" />
|
||||
<Switch {...$$props} text="Second section" />
|
||||
<Switch {...$$props} text="Third section" />
|
||||
<Switch {...$$props}>
|
||||
<div style="display: flex; align-items:center;">
|
||||
<Add16 style="margin-right: .25rem;" />
|
||||
Third section
|
||||
</div>
|
||||
</Switch>
|
||||
</ContentSwitcher>
|
||||
<div
|
||||
style="margin-top: 1.5rem"
|
||||
on:click={() => {
|
||||
selectedIndex = 1;
|
||||
}}>
|
||||
Programmatically set to second index
|
||||
</div>
|
||||
{/if}
|
||||
</Layout>
|
||||
|
|
|
@ -1,19 +1,17 @@
|
|||
import { withKnobs, boolean } from '@storybook/addon-knobs';
|
||||
import Component from './ContentSwitcher.Story.svelte';
|
||||
import { withKnobs, boolean } from "@storybook/addon-knobs";
|
||||
import Component from "./ContentSwitcher.Story.svelte";
|
||||
|
||||
export default { title: 'ContentSwitcher', decorators: [withKnobs] };
|
||||
export default { title: "ContentSwitcher", decorators: [withKnobs] };
|
||||
|
||||
export const Default = () => ({
|
||||
Component,
|
||||
props: {
|
||||
disabled: boolean('Disabled (disabled)', false)
|
||||
}
|
||||
props: { disabled: boolean("Disabled (disabled)", false) },
|
||||
});
|
||||
|
||||
export const Selected = () => ({
|
||||
Component,
|
||||
props: {
|
||||
story: 'selected',
|
||||
disabled: boolean('Disabled (disabled)', false)
|
||||
}
|
||||
story: "selected",
|
||||
disabled: boolean("Disabled (disabled)", false),
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,30 +1,30 @@
|
|||
<script>
|
||||
export let className = undefined;
|
||||
export { className as class };
|
||||
export let selectedIndex = 0;
|
||||
export let style = undefined;
|
||||
|
||||
import { createEventDispatcher, setContext } from 'svelte';
|
||||
import { writable } from 'svelte/store';
|
||||
import { cx } from '../../lib';
|
||||
import { afterUpdate, createEventDispatcher, setContext } from "svelte";
|
||||
import { writable } from "svelte/store";
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
const currentId = writable(null);
|
||||
|
||||
let currentId = writable(null);
|
||||
let currentIndex = selectedIndex;
|
||||
let switches = [];
|
||||
$: currentIndex = -1;
|
||||
$: switches = [];
|
||||
$: if (switches[currentIndex]) {
|
||||
dispatch("change", currentIndex);
|
||||
currentId.set(switches[currentIndex].id);
|
||||
}
|
||||
|
||||
setContext('ContentSwitcher', {
|
||||
setContext("ContentSwitcher", {
|
||||
currentId,
|
||||
add: ({ id, text, selected }) => {
|
||||
switches = [...switches, { id, text, selected }];
|
||||
|
||||
if (selected) {
|
||||
currentIndex = switches.length;
|
||||
selectedIndex = switches.length;
|
||||
}
|
||||
|
||||
switches = [...switches, { id, text, selected }];
|
||||
},
|
||||
update: id => {
|
||||
currentIndex = switches.map(({ id }) => id).indexOf(id);
|
||||
selectedIndex = switches.map(({ id }) => id).indexOf(id);
|
||||
},
|
||||
change: direction => {
|
||||
let index = currentIndex + direction;
|
||||
|
@ -35,24 +35,24 @@
|
|||
index = 0;
|
||||
}
|
||||
|
||||
currentIndex = index;
|
||||
selectedIndex = index;
|
||||
}
|
||||
});
|
||||
|
||||
$: if (switches[currentIndex]) {
|
||||
dispatch('change', currentIndex);
|
||||
selectedIndex = currentIndex;
|
||||
currentId.set(switches[currentIndex].id);
|
||||
}
|
||||
afterUpdate(() => {
|
||||
if (selectedIndex !== currentIndex) {
|
||||
currentIndex = selectedIndex;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div
|
||||
role="tablist"
|
||||
class:bx--content-switcher={true}
|
||||
{...$$restProps}
|
||||
on:click
|
||||
on:mouseover
|
||||
on:mouseenter
|
||||
on:mouseleave
|
||||
class={cx('--content-switcher', className)}
|
||||
{style}>
|
||||
on:mouseleave>
|
||||
<slot />
|
||||
</div>
|
||||
|
|
|
@ -1,39 +1,44 @@
|
|||
<script>
|
||||
let className = undefined;
|
||||
export { className as class };
|
||||
export let text = "Provide text";
|
||||
export let selected = false;
|
||||
export let text = 'Provide text';
|
||||
export let disabled = false;
|
||||
export let style = undefined;
|
||||
export let id = "ccs-" + Math.random().toString(36);
|
||||
export let ref = null;
|
||||
|
||||
import { afterUpdate, getContext } from 'svelte';
|
||||
import { cx } from '../../lib';
|
||||
import { afterUpdate, getContext, onDestroy } from "svelte";
|
||||
|
||||
const id = Math.random();
|
||||
const { currentId, add, update, change } = getContext('ContentSwitcher');
|
||||
const ctx = getContext("ContentSwitcher");
|
||||
|
||||
let buttonRef = undefined;
|
||||
ctx.add({ id, text, selected });
|
||||
|
||||
add({ id, text, selected });
|
||||
const unsubscribe = ctx.currentId.subscribe($ => {
|
||||
selected = $ === id;
|
||||
});
|
||||
|
||||
afterUpdate(() => {
|
||||
if (selected) {
|
||||
buttonRef.focus();
|
||||
ref.focus();
|
||||
}
|
||||
});
|
||||
|
||||
$: selected = $currentId === id;
|
||||
onDestroy(() => {
|
||||
unsubscribe();
|
||||
});
|
||||
</script>
|
||||
|
||||
<button
|
||||
bind:this={buttonRef}
|
||||
bind:this={ref}
|
||||
role="tab"
|
||||
tabindex={selected ? '0' : '-1'}
|
||||
aria-selected={selected}
|
||||
class={cx('--content-switcher-btn', selected && '--content-switcher--selected', className)}
|
||||
{disabled}
|
||||
{id}
|
||||
class:bx--content-switcher-btn={true}
|
||||
class:bx--content-switcher--selected={selected}
|
||||
{...$$restProps}
|
||||
on:click
|
||||
on:click|preventDefault={() => {
|
||||
update(id);
|
||||
ctx.update(id);
|
||||
}}
|
||||
on:mouseover
|
||||
on:mouseenter
|
||||
|
@ -41,12 +46,13 @@
|
|||
on:keydown
|
||||
on:keydown={({ key }) => {
|
||||
if (key === 'ArrowRight') {
|
||||
change(1);
|
||||
ctx.change(1);
|
||||
} else if (key === 'ArrowLeft') {
|
||||
change(-1);
|
||||
ctx.change(-1);
|
||||
}
|
||||
}}
|
||||
{disabled}
|
||||
{style}>
|
||||
<span class={cx('--content-switcher__label')}>{text}</span>
|
||||
}}>
|
||||
|
||||
<span class:bx--content-switcher__label={true}>
|
||||
<slot>{text}</slot>
|
||||
</span>
|
||||
</button>
|
||||
|
|
|
@ -1,4 +1,2 @@
|
|||
import ContentSwitcher from './ContentSwitcher.svelte';
|
||||
|
||||
export default ContentSwitcher;
|
||||
export { default as Switch } from './Switch.svelte';
|
||||
export { default as ContentSwitcher } from "./ContentSwitcher.svelte";
|
||||
export { default as Switch } from "./Switch.svelte";
|
||||
|
|
|
@ -1,38 +1,51 @@
|
|||
<script>
|
||||
let className = undefined;
|
||||
export { className as class };
|
||||
export let feedback = 'Copied!';
|
||||
export let feedback = "Copied!";
|
||||
export let feedbackTimeout = 2000;
|
||||
export let style = undefined;
|
||||
export let ref = null;
|
||||
|
||||
import { onDestroy } from 'svelte';
|
||||
import { cx } from '../../lib';
|
||||
import { onDestroy } from "svelte";
|
||||
|
||||
let timeoutId = undefined;
|
||||
$: animation = undefined;
|
||||
$: timeoutId = undefined;
|
||||
$: showFeedback = timeoutId !== undefined;
|
||||
|
||||
onDestroy(() => {
|
||||
window.clearTimeout(timeoutId);
|
||||
timeoutId = undefined;
|
||||
});
|
||||
|
||||
$: showFeedback = timeoutId !== undefined;
|
||||
</script>
|
||||
|
||||
<button
|
||||
bind:this={ref}
|
||||
type="button"
|
||||
class={className}
|
||||
aria-live="polite"
|
||||
class:bx--copy={true}
|
||||
class:bx--copy-btn--animating={animation}
|
||||
aria-label={animation ? feedback : undefined}
|
||||
{...$$restProps}
|
||||
class="{$$restProps.class}
|
||||
{animation && `bx--copy-btn--${animation}`}"
|
||||
on:click
|
||||
on:click={() => {
|
||||
timeoutId = window.setTimeout(() => {
|
||||
showFeedback = undefined;
|
||||
if (animation === 'fade-in') return;
|
||||
animation = 'fade-in';
|
||||
timeoutId = setTimeout(() => {
|
||||
animation = 'fade-out';
|
||||
}, feedbackTimeout);
|
||||
}}
|
||||
on:mouseover
|
||||
on:mouseenter
|
||||
on:mouseleave
|
||||
{style}>
|
||||
<slot />
|
||||
<div
|
||||
class={cx('--btn--copy__feedback', showFeedback && '--btn--copy__feedback--displayed')}
|
||||
data-feedback={feedback} />
|
||||
on:animationend
|
||||
on:animationend={({ animationName }) => {
|
||||
if (animationName === 'hide-feedback') {
|
||||
animation = undefined;
|
||||
}
|
||||
}}>
|
||||
<slot>
|
||||
{#if animation}{feedback || $$restProps['aria-label']}{/if}
|
||||
</slot>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class:bx--assistive-text={true}
|
||||
class:bx--copy-btn__feedback={true}>
|
||||
{feedback}
|
||||
</span>
|
||||
</button>
|
||||
|
|
|
@ -1,3 +1 @@
|
|||
import Copy from './Copy.svelte';
|
||||
|
||||
export default Copy;
|
||||
export { default as Copy } from "./Copy.svelte";
|
||||
|
|
|
@ -1,8 +1,15 @@
|
|||
<script>
|
||||
import Layout from '../../internal/ui/Layout.svelte';
|
||||
import CopyButton from './CopyButton.svelte';
|
||||
import Layout from "../../internal/ui/Layout.svelte";
|
||||
import CopyButton from "./CopyButton.svelte";
|
||||
</script>
|
||||
|
||||
<Layout>
|
||||
<CopyButton {...$$props} />
|
||||
<CopyButton
|
||||
{...$$props}
|
||||
on:click={() => {
|
||||
console.log('click');
|
||||
}}
|
||||
on:animationend={e => {
|
||||
console.log('animation end', e.animationName);
|
||||
}} />
|
||||
</Layout>
|
||||
|
|
|
@ -1,13 +1,19 @@
|
|||
import { withKnobs, text, number } from '@storybook/addon-knobs';
|
||||
import Component from './CopyButton.Story.svelte';
|
||||
import { withKnobs, text, number } from "@storybook/addon-knobs";
|
||||
import Component from "./CopyButton.Story.svelte";
|
||||
|
||||
export default { title: 'CopyButton', decorators: [withKnobs] };
|
||||
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)', 2000),
|
||||
iconDescription: text('Feedback icon description (iconDescription)', 'Copy to clipboard')
|
||||
}
|
||||
feedback: text("The text shown upon clicking (feedback)", "Copied!"),
|
||||
feedbackTimeout: number(
|
||||
"How long the text is shown upon clicking (feedbackTimeout)",
|
||||
2000
|
||||
),
|
||||
iconDescription: text(
|
||||
"Feedback icon description (iconDescription)",
|
||||
"Copy to clipboard"
|
||||
),
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,52 +1,16 @@
|
|||
<script>
|
||||
let className = undefined;
|
||||
export { className as class };
|
||||
export let feedback = 'Copied!';
|
||||
export let feedbackTimeout = 2000;
|
||||
export let iconDescription = 'Copy to clipboard';
|
||||
export let style = undefined;
|
||||
export let iconDescription = "Copy to clipboard";
|
||||
|
||||
import { afterUpdate, onDestroy } from 'svelte';
|
||||
import Copy16 from 'carbon-icons-svelte/lib/Copy16';
|
||||
import { cx } from '../../lib';
|
||||
|
||||
let animation = undefined;
|
||||
let timeoutId = undefined;
|
||||
|
||||
afterUpdate(() => {
|
||||
if (animation === 'fade-in') {
|
||||
timeoutId = window.setTimeout(() => {
|
||||
animation = 'fade-out';
|
||||
}, feedbackTimeout);
|
||||
}
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
window.clearTimeout(timeoutId);
|
||||
timeoutId = undefined;
|
||||
});
|
||||
import { Copy } from "../Copy";
|
||||
import Copy16 from "carbon-icons-svelte/lib/Copy16";
|
||||
</script>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
tabindex="0"
|
||||
<Copy
|
||||
class="bx--copy-btn"
|
||||
aria-label={iconDescription}
|
||||
title={iconDescription}
|
||||
class={cx('--copy-btn', animation && '--copy-btn--animating', animation && `--copy-btn--${animation}`, animation === 'fade-in' && '--btn--copy__feedback--displayed', className)}
|
||||
{...$$restProps}
|
||||
on:click
|
||||
on:click={() => {
|
||||
animation = 'fade-in';
|
||||
}}
|
||||
on:mouseover
|
||||
on:mouseenter
|
||||
on:mouseleave
|
||||
on:animationend
|
||||
on:animationend={({ animationName }) => {
|
||||
if (animationName === 'hide-feedback') {
|
||||
animation = undefined;
|
||||
}
|
||||
}}
|
||||
{style}>
|
||||
<span class={cx('--assistive-text', '--copy-btn__feedback')}>{feedback}</span>
|
||||
<Copy16 class={cx('--snippet__icon')} />
|
||||
</button>
|
||||
on:animationend>
|
||||
<Copy16 class="bx--snippet__icon" />
|
||||
</Copy>
|
||||
|
|
|
@ -1,3 +1 @@
|
|||
import CopyButton from './CopyButton.svelte';
|
||||
|
||||
export default CopyButton;
|
||||
export { default as CopyButton } from "./CopyButton.svelte";
|
||||
|
|
|
@ -1,81 +1,81 @@
|
|||
<script>
|
||||
export let story = undefined;
|
||||
|
||||
import Layout from '../../internal/ui/Layout.svelte';
|
||||
import DataTable from './DataTable.svelte';
|
||||
import Table from './Table.svelte';
|
||||
import TableBody from './TableBody.svelte';
|
||||
import TableCell from './TableCell.svelte';
|
||||
import TableContainer from './TableContainer.svelte';
|
||||
import TableHead from './TableHead.svelte';
|
||||
import TableHeader from './TableHeader.svelte';
|
||||
import TableRow from './TableRow.svelte';
|
||||
import Layout from "../../internal/ui/Layout.svelte";
|
||||
import DataTable from "./DataTable.svelte";
|
||||
import Table from "./Table.svelte";
|
||||
import TableBody from "./TableBody.svelte";
|
||||
import TableCell from "./TableCell.svelte";
|
||||
import TableContainer from "./TableContainer.svelte";
|
||||
import TableHead from "./TableHead.svelte";
|
||||
import TableHeader from "./TableHeader.svelte";
|
||||
import TableRow from "./TableRow.svelte";
|
||||
|
||||
let rows = [
|
||||
const rows = [
|
||||
{
|
||||
id: 'a',
|
||||
name: 'Load Balancer 3',
|
||||
protocol: 'HTTP',
|
||||
id: "a",
|
||||
name: "Load Balancer 3",
|
||||
protocol: "HTTP",
|
||||
port: 3000,
|
||||
rule: 'Round robin',
|
||||
attached_groups: 'Kevins VM Groups',
|
||||
status: 'Disabled'
|
||||
rule: "Round robin",
|
||||
attached_groups: "Kevins VM Groups",
|
||||
status: "Disabled"
|
||||
},
|
||||
{
|
||||
id: 'b',
|
||||
name: 'Load Balancer 1',
|
||||
protocol: 'HTTP',
|
||||
id: "b",
|
||||
name: "Load Balancer 1",
|
||||
protocol: "HTTP",
|
||||
port: 443,
|
||||
rule: 'Round robin',
|
||||
attached_groups: 'Maureens VM Groups',
|
||||
status: 'Starting'
|
||||
rule: "Round robin",
|
||||
attached_groups: "Maureens VM Groups",
|
||||
status: "Starting"
|
||||
},
|
||||
{
|
||||
id: 'c',
|
||||
name: 'Load Balancer 2',
|
||||
protocol: 'HTTP',
|
||||
id: "c",
|
||||
name: "Load Balancer 2",
|
||||
protocol: "HTTP",
|
||||
port: 80,
|
||||
rule: 'DNS delegation',
|
||||
attached_groups: 'Andrews VM Groups',
|
||||
status: 'Active'
|
||||
rule: "DNS delegation",
|
||||
attached_groups: "Andrews VM Groups",
|
||||
status: "Active"
|
||||
},
|
||||
{
|
||||
id: 'd',
|
||||
name: 'Load Balancer 6',
|
||||
protocol: 'HTTP',
|
||||
id: "d",
|
||||
name: "Load Balancer 6",
|
||||
protocol: "HTTP",
|
||||
port: 3000,
|
||||
rule: 'Round robin',
|
||||
attached_groups: 'Marcs VM Groups',
|
||||
status: 'Disabled'
|
||||
rule: "Round robin",
|
||||
attached_groups: "Marcs VM Groups",
|
||||
status: "Disabled"
|
||||
},
|
||||
{
|
||||
id: 'e',
|
||||
name: 'Load Balancer 4',
|
||||
protocol: 'HTTP',
|
||||
id: "e",
|
||||
name: "Load Balancer 4",
|
||||
protocol: "HTTP",
|
||||
port: 443,
|
||||
rule: 'Round robin',
|
||||
attached_groups: 'Mels VM Groups',
|
||||
status: 'Starting'
|
||||
rule: "Round robin",
|
||||
attached_groups: "Mels VM Groups",
|
||||
status: "Starting"
|
||||
},
|
||||
{
|
||||
id: 'f',
|
||||
name: 'Load Balancer 5',
|
||||
protocol: 'HTTP',
|
||||
id: "f",
|
||||
name: "Load Balancer 5",
|
||||
protocol: "HTTP",
|
||||
port: 80,
|
||||
rule: 'DNS delegation',
|
||||
attached_groups: 'Ronjas VM Groups',
|
||||
status: 'Active'
|
||||
rule: "DNS delegation",
|
||||
attached_groups: "Ronjas VM Groups",
|
||||
status: "Active"
|
||||
}
|
||||
];
|
||||
let headers = [
|
||||
{ key: 'name', value: 'Name' },
|
||||
{ key: 'protocol', value: 'Protocol' },
|
||||
{ key: 'port', value: 'Port' },
|
||||
{ key: 'rule', value: 'Rule' },
|
||||
{ key: 'attached_groups', value: 'Attached Groups' },
|
||||
{ key: 'status', value: 'Status' }
|
||||
const headers = [
|
||||
{ key: "name", value: "Name" },
|
||||
{ key: "protocol", value: "Protocol" },
|
||||
{ key: "port", value: "Port" },
|
||||
{ key: "rule", value: "Rule" },
|
||||
{ key: "attached_groups", value: "Attached Groups" },
|
||||
{ key: "status", value: "Status" }
|
||||
];
|
||||
let sortable = true;
|
||||
$: sortable = true;
|
||||
</script>
|
||||
|
||||
<Layout>
|
||||
|
@ -89,7 +89,9 @@
|
|||
<TableHead>
|
||||
<TableRow>
|
||||
{#each props.headers as header, i (header.key)}
|
||||
<TableHeader {...props.getHeaderProps({ header })}>{header.header}</TableHeader>
|
||||
<TableHeader {...props.getHeaderProps({ header })}>
|
||||
{header.header}
|
||||
</TableHeader>
|
||||
{/each}
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
|
|
|
@ -1,35 +1,35 @@
|
|||
import { withKnobs, boolean, select, text } from '@storybook/addon-knobs';
|
||||
import Component from './DataTable.Story.svelte';
|
||||
import { withKnobs, boolean, select, text } from "@storybook/addon-knobs";
|
||||
import Component from "./DataTable.Story.svelte";
|
||||
|
||||
export default { title: 'DataTable', decorators: [withKnobs] };
|
||||
export default { title: "DataTable", decorators: [withKnobs] };
|
||||
|
||||
export const Default = () => ({
|
||||
Component,
|
||||
props: {
|
||||
title: text('Optional DataTable title (title)', ''),
|
||||
description: text('Optional DataTable description (description)', ''),
|
||||
zebra: boolean('Zebra row styles (zebra)', false),
|
||||
title: text("Optional DataTable title (title)", ""),
|
||||
description: text("Optional DataTable description (description)", ""),
|
||||
zebra: boolean("Zebra row styles (zebra)", false),
|
||||
size: select(
|
||||
'Row height (size)',
|
||||
{ compact: 'compact', short: 'short', tall: 'tall', none: null },
|
||||
"Row height (size)",
|
||||
{ compact: "compact", short: "short", tall: "tall", none: null },
|
||||
null
|
||||
),
|
||||
stickyHeader: boolean('Sticky header (experimental)', false)
|
||||
}
|
||||
stickyHeader: boolean("Sticky header (experimental)", false),
|
||||
},
|
||||
});
|
||||
|
||||
export const Sortable = () => ({
|
||||
Component,
|
||||
props: {
|
||||
story: 'sortable',
|
||||
title: text('Optional DataTable title (title)', ''),
|
||||
description: text('Optional DataTable description (description)', ''),
|
||||
zebra: boolean('Zebra row styles (zebra)', false),
|
||||
story: "sortable",
|
||||
title: text("Optional DataTable title (title)", ""),
|
||||
description: text("Optional DataTable description (description)", ""),
|
||||
zebra: boolean("Zebra row styles (zebra)", false),
|
||||
size: select(
|
||||
'Row height (size)',
|
||||
{ compact: 'compact', short: 'short', tall: 'tall', none: null },
|
||||
"Row height (size)",
|
||||
{ compact: "compact", short: "short", tall: "tall", none: null },
|
||||
null
|
||||
),
|
||||
stickyHeader: boolean('Sticky header (experimental)', false)
|
||||
}
|
||||
stickyHeader: boolean("Sticky header (experimental)", false),
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,39 +1,39 @@
|
|||
<script>
|
||||
let className = undefined;
|
||||
export { className as class };
|
||||
export let title = '';
|
||||
export let description = '';
|
||||
export let size = undefined; // "compact" | "short" | "tall"
|
||||
export let title = "";
|
||||
export let description = "";
|
||||
export let zebra = false;
|
||||
export let sortable = false;
|
||||
export let stickyHeader = false;
|
||||
export let rows = [];
|
||||
export let headers = [];
|
||||
export let stickyHeader = false;
|
||||
export let size = undefined;
|
||||
export let sortable = false;
|
||||
export let style = undefined;
|
||||
|
||||
import { createEventDispatcher, setContext } from 'svelte';
|
||||
import { writable, derived } from 'svelte/store';
|
||||
import Table from './Table.svelte';
|
||||
import TableBody from './TableBody.svelte';
|
||||
import TableCell from './TableCell.svelte';
|
||||
import TableContainer from './TableContainer.svelte';
|
||||
import TableHead from './TableHead.svelte';
|
||||
import TableHeader from './TableHeader.svelte';
|
||||
import TableRow from './TableRow.svelte';
|
||||
import { createEventDispatcher, setContext } from "svelte";
|
||||
import { writable, derived } from "svelte/store";
|
||||
import Table from "./Table.svelte";
|
||||
import TableBody from "./TableBody.svelte";
|
||||
import TableCell from "./TableCell.svelte";
|
||||
import TableContainer from "./TableContainer.svelte";
|
||||
import TableHead from "./TableHead.svelte";
|
||||
import TableHeader from "./TableHeader.svelte";
|
||||
import TableRow from "./TableRow.svelte";
|
||||
|
||||
const sortDirectionMap = {
|
||||
none: "ascending",
|
||||
ascending: "descending",
|
||||
descending: "none"
|
||||
};
|
||||
const dispatch = createEventDispatcher();
|
||||
const sortDirectionMap = { none: 'ascending', ascending: 'descending', descending: 'none' };
|
||||
|
||||
let tableSortable = writable(sortable);
|
||||
let sortHeader = writable({ id: null, key: null, sortDirection: 'none' });
|
||||
let headerItems = writable([]);
|
||||
let thKeys = derived(headerItems, () =>
|
||||
const tableSortable = writable(sortable);
|
||||
const sortHeader = writable({ id: null, key: null, sortDirection: "none" });
|
||||
const headerItems = writable([]);
|
||||
const thKeys = derived(headerItems, () =>
|
||||
headers
|
||||
.map(({ key }, i) => ({ key, id: $headerItems[i] }))
|
||||
.reduce((a, c) => ({ ...a, [c.key]: c.id }), {})
|
||||
);
|
||||
|
||||
setContext('DataTable', {
|
||||
setContext("DataTable", {
|
||||
sortHeader,
|
||||
tableSortable,
|
||||
add: id => {
|
||||
|
@ -43,24 +43,29 @@
|
|||
|
||||
$: tableSortable.set(sortable);
|
||||
$: headerKeys = headers.map(({ key }) => key);
|
||||
$: rows = rows.map(row => ({ ...row, cells: headerKeys.map(key => ({ key, value: row[key] })) }));
|
||||
$: rows = rows.map(row => ({
|
||||
...row,
|
||||
cells: headerKeys.map(key => ({ key, value: row[key] }))
|
||||
}));
|
||||
$: sortedRows = rows;
|
||||
$: ascending = $sortHeader.sortDirection === 'ascending';
|
||||
$: ascending = $sortHeader.sortDirection === "ascending";
|
||||
$: sortKey = $sortHeader.key;
|
||||
$: sorting = sortable && sortKey != null;
|
||||
$: if (sorting) {
|
||||
if ($sortHeader.sortDirection === 'none') {
|
||||
if ($sortHeader.sortDirection === "none") {
|
||||
sortedRows = rows;
|
||||
} else {
|
||||
sortedRows = [...rows].sort((a, b) => {
|
||||
const itemA = ascending ? a[sortKey] : b[sortKey];
|
||||
const itemB = ascending ? b[sortKey] : a[sortKey];
|
||||
|
||||
if (typeof itemA === 'number' && typeof itemB === 'number') {
|
||||
if (typeof itemA === "number" && typeof itemB === "number") {
|
||||
return itemA - itemB;
|
||||
}
|
||||
|
||||
return itemA.toString().localeCompare(itemB.toString(), 'en', { numeric: true });
|
||||
return itemA
|
||||
.toString()
|
||||
.localeCompare(itemB.toString(), "en", { numeric: true });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -71,7 +76,7 @@
|
|||
</script>
|
||||
|
||||
<slot {props}>
|
||||
<TableContainer class={className} {title} {description} {style}>
|
||||
<TableContainer {title} {description} {...$$restProps}>
|
||||
<Table {zebra} {size} {stickyHeader} {sortable}>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
|
@ -82,7 +87,7 @@
|
|||
let active = header.key === $sortHeader.key;
|
||||
let currentSortDirection = active ? $sortHeader.sortDirection : 'none';
|
||||
let sortDirection = sortDirectionMap[currentSortDirection];
|
||||
dispatch('click:header', {header, sortDirection});
|
||||
dispatch('click:header', { header, sortDirection });
|
||||
sortHeader.set({
|
||||
id: sortDirection === 'none' ? null : $thKeys[header.key],
|
||||
key: header.key,
|
||||
|
|
|
@ -1,28 +1,39 @@
|
|||
<script>
|
||||
let className = undefined;
|
||||
export { className as class };
|
||||
export let size = undefined; // "compact" | "short" | "tall"
|
||||
export let zebra = false;
|
||||
export let size = undefined;
|
||||
export let useStaticWidth = false;
|
||||
export let shouldShowBorder = false;
|
||||
export let sortable = false;
|
||||
export let stickyHeader = false;
|
||||
export let style = undefined;
|
||||
|
||||
import { cx } from '../../lib';
|
||||
</script>
|
||||
|
||||
{#if stickyHeader}
|
||||
<section class={cx('--data-table_inner-container', className)} {style}>
|
||||
<section class:bx--data-table_inner-container={true} {...$$restProps}>
|
||||
<table
|
||||
class={cx('--data-table', size === 'compact' && '--data-table--compact', size === 'short' && '--data-table--short', size === 'tall' && '--data-table--tall', sortable && '--data-table--sort', zebra && '--data-table--zebra', useStaticWidth && '--data-table--static', !shouldShowBorder && '--data-table--no-border', stickyHeader && '--data-table--sticky-header')}>
|
||||
class:bx--data-table={true}
|
||||
class:bx--data-table--compact={size === 'compact'}
|
||||
class:bx--data-table--short={size === 'short'}
|
||||
class:bx--data-table--tall={size === 'tall'}
|
||||
class:bx--data-table--sort={sortable}
|
||||
class:bx--data-table--zebra={zebra}
|
||||
class:bx--data-table--static={useStaticWidth}
|
||||
class:bx--data-table--no-border={!shouldShowBorder}
|
||||
class:bx--data-table--sticky-header={stickyHeader}>
|
||||
<slot />
|
||||
</table>
|
||||
</section>
|
||||
{:else}
|
||||
<table
|
||||
class={cx('--data-table', size === 'compact' && '--data-table--compact', size === 'short' && '--data-table--short', size === 'tall' && '--data-table--tall', sortable && '--data-table--sort', zebra && '--data-table--zebra', useStaticWidth && '--data-table--static', !shouldShowBorder && '--data-table--no-border', stickyHeader && '--data-table--sticky-header', className)}
|
||||
{style}>
|
||||
class:bx--data-table={true}
|
||||
class:bx--data-table--compact={size === 'compact'}
|
||||
class:bx--data-table--short={size === 'short'}
|
||||
class:bx--data-table--tall={size === 'tall'}
|
||||
class:bx--data-table--sort={sortable}
|
||||
class:bx--data-table--zebra={zebra}
|
||||
class:bx--data-table--static={useStaticWidth}
|
||||
class:bx--data-table--no-border={!shouldShowBorder}
|
||||
class:bx--data-table--sticky-header={stickyHeader}
|
||||
{...$$restProps}>
|
||||
<slot />
|
||||
</table>
|
||||
{/if}
|
||||
|
|
|
@ -1,9 +1,3 @@
|
|||
<script>
|
||||
let className = undefined;
|
||||
export { className as class };
|
||||
export let style = undefined;
|
||||
</script>
|
||||
|
||||
<tbody aria-live={$$props['aria-live'] || 'polite'} class={className} {style}>
|
||||
<tbody aria-live="polite" {...$$restProps}>
|
||||
<slot />
|
||||
</tbody>
|
||||
|
|
|
@ -1,9 +1,3 @@
|
|||
<script>
|
||||
let className = undefined;
|
||||
export { className as class };
|
||||
export let style = undefined;
|
||||
</script>
|
||||
|
||||
<td on:click on:mouseover on:mouseenter on:mouseleave class={className} {style}>
|
||||
<td {...$$restProps} on:click on:mouseover on:mouseenter on:mouseleave>
|
||||
<slot />
|
||||
</td>
|
||||
|
|
|
@ -1,21 +1,17 @@
|
|||
<script>
|
||||
let className = undefined;
|
||||
export { className as class };
|
||||
export let stickyHeader = false;
|
||||
export let title = '';
|
||||
export let description = '';
|
||||
export let style = undefined;
|
||||
|
||||
import { cx } from '../../lib';
|
||||
export let title = "";
|
||||
export let description = "";
|
||||
</script>
|
||||
|
||||
<div
|
||||
class={cx('--data-table-container', stickyHeader && '--data-table--max-width', className)}
|
||||
{style}>
|
||||
class:bx--data-table-container={true}
|
||||
class:bx--data-table--max-width={stickyHeader}
|
||||
{...$$restProps}>
|
||||
{#if title}
|
||||
<div class={cx('--data-table-header')}>
|
||||
<h4 class={cx('--data-table-header__title')}>{title}</h4>
|
||||
<p class={cx('--data-table-header__description')}>{description}</p>
|
||||
<div class:bx--data-table-header={true}>
|
||||
<h4 class:bx--data-table-header__title={true}>{title}</h4>
|
||||
<p class:bx--data-table-header__description={true}>{description}</p>
|
||||
</div>
|
||||
{/if}
|
||||
<slot />
|
||||
|
|
|
@ -1,9 +1,3 @@
|
|||
<script>
|
||||
let className = undefined;
|
||||
export { className as class };
|
||||
export let style = undefined;
|
||||
</script>
|
||||
|
||||
<thead on:click on:mouseover on:mouseenter on:mouseleave class={className} {style}>
|
||||
<thead {...$$restProps} on:click on:mouseover on:mouseenter on:mouseleave>
|
||||
<slot />
|
||||
</thead>
|
||||
|
|
|
@ -1,17 +1,13 @@
|
|||
<script>
|
||||
let className = undefined;
|
||||
export { className as class };
|
||||
export let scope = 'col';
|
||||
export let translateWithId = () => '';
|
||||
export let style = undefined;
|
||||
export let scope = "col";
|
||||
export let translateWithId = () => "";
|
||||
export let id = "ccs-" + Math.random().toString(36);
|
||||
|
||||
import { getContext } from 'svelte';
|
||||
import ArrowUp20 from 'carbon-icons-svelte/lib/ArrowUp20';
|
||||
import ArrowsVertical20 from 'carbon-icons-svelte/lib/ArrowsVertical20';
|
||||
import { cx } from '../../lib';
|
||||
import { getContext } from "svelte";
|
||||
import ArrowUp20 from "carbon-icons-svelte/lib/ArrowUp20";
|
||||
import ArrowsVertical20 from "carbon-icons-svelte/lib/ArrowsVertical20";
|
||||
|
||||
const id = Math.random();
|
||||
const { sortHeader, tableSortable, add } = getContext('DataTable');
|
||||
const { sortHeader, tableSortable, add } = getContext("DataTable");
|
||||
|
||||
add(id);
|
||||
|
||||
|
@ -22,25 +18,35 @@
|
|||
|
||||
{#if $tableSortable}
|
||||
<th
|
||||
aria-sort={active ? $sortHeader.sortDirection : 'none'}
|
||||
{scope}
|
||||
{...$$restProps}
|
||||
on:mouseover
|
||||
on:mouseenter
|
||||
on:mouseleave
|
||||
class={className}
|
||||
aria-sort={active ? $sortHeader.sortDirection : 'none'}
|
||||
{scope}>
|
||||
on:mouseleave>
|
||||
<button
|
||||
class={cx('--table-sort', active && '--table-sort--active', active && $sortHeader.sortDirection === 'descending' && '--table-sort--ascending')}
|
||||
class:bx--table-sort={true}
|
||||
class:bx--table-sort--active={active}
|
||||
class:bx--table-sort--ascending={active && $sortHeader.sortDirection === 'descending'}
|
||||
on:click>
|
||||
<span class={cx('--table-header-label')}>
|
||||
<span class:bx--table-header-label={true}>
|
||||
<slot />
|
||||
</span>
|
||||
<ArrowUp20 class={cx('--table-sort__icon')} aria-label={ariaLabel} />
|
||||
<ArrowsVertical20 class={cx('--table-sort__icon-unsorted')} aria-label={ariaLabel} />
|
||||
<ArrowUp20 aria-label={ariaLabel} class="bx--table-sort__icon" />
|
||||
<ArrowsVertical20
|
||||
aria-label={ariaLabel}
|
||||
class="bx--table-sort__icon-unsorted" />
|
||||
</button>
|
||||
</th>
|
||||
{:else}
|
||||
<th on:click on:mouseover on:mouseenter on:mouseleave class={className} {style} {scope}>
|
||||
<span class={cx('--table-header-label')}>
|
||||
<th
|
||||
{scope}
|
||||
{...$$restProps}
|
||||
on:click
|
||||
on:mouseover
|
||||
on:mouseenter
|
||||
on:mouseleave>
|
||||
<span class:bx--table-header-label={true}>
|
||||
<slot />
|
||||
</span>
|
||||
</th>
|
||||
|
|
|
@ -1,20 +1,15 @@
|
|||
<script>
|
||||
let className = undefined;
|
||||
export { className as class };
|
||||
export let isSelected = false;
|
||||
export let style = undefined;
|
||||
|
||||
import { cx } from '../../lib';
|
||||
|
||||
// TODO: include ariaLabel, onExpand, isExpanded, isSelected
|
||||
</script>
|
||||
|
||||
<tr
|
||||
class:bx--data-table--selected={isSelected}
|
||||
{...$$restProps}
|
||||
on:click
|
||||
on:mouseover
|
||||
on:mouseenter
|
||||
on:mouseleave
|
||||
class={cx(isSelected && '--data-table--selected', className)}
|
||||
{style}>
|
||||
on:mouseleave>
|
||||
<slot />
|
||||
</tr>
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
import DataTable from './DataTable.svelte';
|
||||
|
||||
export default DataTable;
|
||||
export { default as Table } from './Table.svelte';
|
||||
export { default as TableBody } from './TableBody.svelte';
|
||||
export { default as TableCell } from './TableCell.svelte';
|
||||
export { default as TableContainer } from './TableContainer.svelte';
|
||||
export { default as TableHead } from './TableHead.svelte';
|
||||
export { default as TableHeader } from './TableHeader.svelte';
|
||||
export { default as TableRow } from './TableRow.svelte';
|
||||
export { default as DataTable } from "./DataTable.svelte";
|
||||
export { default as Table } from "./Table.svelte";
|
||||
export { default as TableBody } from "./TableBody.svelte";
|
||||
export { default as TableCell } from "./TableCell.svelte";
|
||||
export { default as TableContainer } from "./TableContainer.svelte";
|
||||
export { default as TableHead } from "./TableHead.svelte";
|
||||
export { default as TableHeader } from "./TableHeader.svelte";
|
||||
export { default as TableRow } from "./TableRow.svelte";
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
import { withKnobs, array, boolean } from '@storybook/addon-knobs';
|
||||
import Component from './DataTableSkeleton.Story.svelte';
|
||||
import { withKnobs, array, boolean } from "@storybook/addon-knobs";
|
||||
import Component from "./DataTableSkeleton.Story.svelte";
|
||||
|
||||
export default { title: 'DataTableSkeleton', decorators: [withKnobs] };
|
||||
export default { title: "DataTableSkeleton", decorators: [withKnobs] };
|
||||
|
||||
export const Default = () => ({
|
||||
Component,
|
||||
props: {
|
||||
headers: array(
|
||||
'Optional table headers (headers)',
|
||||
['Name', 'Protocol', 'Port', 'Rule', 'Attached Groups'],
|
||||
','
|
||||
"Optional table headers (headers)",
|
||||
["Name", "Protocol", "Port", "Rule", "Attached Groups"],
|
||||
","
|
||||
),
|
||||
zebra: boolean('Use zebra stripe (zebra)', false),
|
||||
compact: boolean('Compact variant (compact)', false)
|
||||
}
|
||||
zebra: boolean("Use zebra stripe (zebra)", false),
|
||||
compact: boolean("Compact variant (compact)", false),
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import Layout from '../../internal/ui/Layout.svelte';
|
||||
import DataTableSkeleton from './DataTableSkeleton.svelte';
|
||||
import Layout from "../../internal/ui/Layout.svelte";
|
||||
import DataTableSkeleton from "./DataTableSkeleton.svelte";
|
||||
</script>
|
||||
|
||||
<Layout>
|
||||
|
|
|
@ -1,25 +1,26 @@
|
|||
<script>
|
||||
let className = undefined;
|
||||
export { className as class };
|
||||
export let columns = 5;
|
||||
export let compact = false;
|
||||
export let headers = [];
|
||||
export let rows = 5;
|
||||
export let style = undefined;
|
||||
export let compact = false;
|
||||
export let zebra = false;
|
||||
export let headers = [];
|
||||
|
||||
import { cx, fillArray } from '../../lib';
|
||||
|
||||
$: cols = fillArray(headers.length > 0 ? headers.length : columns);
|
||||
$: cols = Array.from(
|
||||
{ length: headers.length > 0 ? headers.length : columns },
|
||||
(_, i) => i
|
||||
);
|
||||
</script>
|
||||
|
||||
<table
|
||||
class:bx--skeleton={true}
|
||||
class:bx--data-table={true}
|
||||
class:bx--data-table--zebra={zebra}
|
||||
class:bx--data-table--compact={compact}
|
||||
{...$$restProps}
|
||||
on:click
|
||||
on:mouseover
|
||||
on:mouseenter
|
||||
on:mouseleave
|
||||
class={cx('--skeleton', '--data-table', zebra && '--data-table--zebra', compact && '--data-table--compact', className)}
|
||||
{style}>
|
||||
on:mouseleave>
|
||||
<thead>
|
||||
<tr>
|
||||
{#each cols as col, i (col)}
|
||||
|
@ -35,7 +36,7 @@
|
|||
</td>
|
||||
{/each}
|
||||
</tr>
|
||||
{#each fillArray(rows - 1) as row, i (row)}
|
||||
{#each Array.from({ length: rows - 1 }, (_, i) => i) as row, i (row)}
|
||||
<tr>
|
||||
{#each cols as col, j (col)}
|
||||
<td />
|
||||
|
|
|
@ -1,3 +1 @@
|
|||
import DataTableSkeleton from './DataTableSkeleton.svelte';
|
||||
|
||||
export default DataTableSkeleton;
|
||||
export { default as DataTableSkeleton } from "./DataTableSkeleton.svelte";
|
||||
|
|
|
@ -1,20 +1,25 @@
|
|||
<script>
|
||||
let className = undefined;
|
||||
export { className as class };
|
||||
export let id = Math.random();
|
||||
export let range = false;
|
||||
export let style = undefined;
|
||||
|
||||
import { cx, fillArray } from '../../lib';
|
||||
export let id = "ccs-" + Math.random().toString(36);
|
||||
</script>
|
||||
|
||||
<div on:click on:mouseover on:mouseenter on:mouseleave class={cx('--form-item')} {style}>
|
||||
<div
|
||||
class:bx--form-item={true}
|
||||
{...$$restProps}
|
||||
on:click
|
||||
on:mouseover
|
||||
on:mouseenter
|
||||
on:mouseleave>
|
||||
<div
|
||||
class={cx('--date-picker', '--skeleton', range && '--date-picker--range', !range && '--date-picker--short', !range && '--date-picker--simple', className)}>
|
||||
{#each fillArray(range ? 2 : 1) as input, i (input)}
|
||||
<div class={cx('--date-picker-container')}>
|
||||
<label class={cx('--label')} for={id} />
|
||||
<div class={cx('--date-picker__input', '--skeleton')} />
|
||||
class:bx--date-picker={true}
|
||||
class:bx--skeleton={true}
|
||||
class:bx--date-picker--range={true}
|
||||
class:bx--date-picker--short={!range}
|
||||
class:bx--date-picker--simple={!range}>
|
||||
{#each Array.from({ length: range ? 2 : 1 }, (_, i) => i) as input, i (input)}
|
||||
<div class:bx--date-picker-container={true}>
|
||||
<label for={id} class:bx--label={true} />
|
||||
<div class:bx--date-picker__input={true} class:bx--skeleton={true} />
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
<script>
|
||||
export let story = undefined;
|
||||
|
||||
import Layout from '../../internal/ui/Layout.svelte';
|
||||
import DatePicker from './DatePicker.svelte';
|
||||
import DatePickerInput from './DatePickerInput.svelte';
|
||||
import DatePickerSkeleton from './DatePicker.Skeleton.svelte';
|
||||
import Layout from "../../internal/ui/Layout.svelte";
|
||||
import DatePicker from "./DatePicker.svelte";
|
||||
import DatePickerInput from "./DatePickerInput.svelte";
|
||||
import DatePickerSkeleton from "./DatePicker.Skeleton.svelte";
|
||||
|
||||
let datePickerType = 'simple';
|
||||
let value = '';
|
||||
$: datePickerType = "simple";
|
||||
$: value = "";
|
||||
</script>
|
||||
|
||||
<Layout>
|
||||
|
|
|
@ -1,100 +1,139 @@
|
|||
import { withKnobs, select, text, boolean } from '@storybook/addon-knobs';
|
||||
import Component from './DatePicker.Story.svelte';
|
||||
import { withKnobs, select, text, boolean } from "@storybook/addon-knobs";
|
||||
import Component from "./DatePicker.Story.svelte";
|
||||
|
||||
export default { title: 'DatePicker', decorators: [withKnobs] };
|
||||
export default { title: "DatePicker", decorators: [withKnobs] };
|
||||
|
||||
const patterns = {
|
||||
'Short (d{1,2}/d{4})': 'd{1,2}/d{4}',
|
||||
'Regular (d{1,2}/d{1,2}/d{4})': 'd{1,2}/d{1,2}/d{4}'
|
||||
"Short (d{1,2}/d{4})": "d{1,2}/d{4}",
|
||||
"Regular (d{1,2}/d{1,2}/d{4})": "d{1,2}/d{1,2}/d{4}",
|
||||
};
|
||||
|
||||
export const Default = () => ({
|
||||
Component,
|
||||
props: {
|
||||
datePicker: {
|
||||
id: 'date-picker',
|
||||
light: boolean('Light variant (light in <DatePicker>)', false),
|
||||
short: boolean('Use shorter width (short in <DatePicker>)', false)
|
||||
id: "date-picker",
|
||||
light: boolean("Light variant (light in <DatePicker>)", false),
|
||||
short: boolean("Use shorter width (short in <DatePicker>)", false),
|
||||
},
|
||||
datePickerInput: {
|
||||
id: 'date-picker-input-id',
|
||||
name: 'date-picker-input-name',
|
||||
labelText: text('Label text (labelText in <DatePickerInput>)', 'Date Picker label'),
|
||||
hideLabel: boolean('Hide label (hideLabel)', false),
|
||||
pattern: select('The date format (pattern in <DatePickerInput>)', patterns, 'd{1,2}/d{4}'),
|
||||
placeholder: text('Placeholder text (placeholder in <DatePickerInput>)', 'mm/dd/yyyy'),
|
||||
disabled: boolean('Disabled (disabled in <DatePickerInput>)', false),
|
||||
invalid: boolean('Show form validation UI (invalid in <DatePickerInput>)', false),
|
||||
id: "date-picker-input-id",
|
||||
name: "date-picker-input-name",
|
||||
labelText: text(
|
||||
"Label text (labelText in <DatePickerInput>)",
|
||||
"Date Picker label"
|
||||
),
|
||||
hideLabel: boolean("Hide label (hideLabel)", false),
|
||||
pattern: select(
|
||||
"The date format (pattern in <DatePickerInput>)",
|
||||
patterns,
|
||||
"d{1,2}/d{4}"
|
||||
),
|
||||
placeholder: text(
|
||||
"Placeholder text (placeholder in <DatePickerInput>)",
|
||||
"mm/dd/yyyy"
|
||||
),
|
||||
disabled: boolean("Disabled (disabled in <DatePickerInput>)", false),
|
||||
invalid: boolean(
|
||||
"Show form validation UI (invalid in <DatePickerInput>)",
|
||||
false
|
||||
),
|
||||
invalidText: text(
|
||||
'Form validation UI content (invalidText in <DatePickerInput>)',
|
||||
'A valid value is required'
|
||||
"Form validation UI content (invalidText in <DatePickerInput>)",
|
||||
"A valid value is required"
|
||||
),
|
||||
iconDescription: text(
|
||||
'Icon description (iconDescription in <DatePickerInput>)',
|
||||
'Icon description'
|
||||
)
|
||||
}
|
||||
}
|
||||
"Icon description (iconDescription in <DatePickerInput>)",
|
||||
"Icon description"
|
||||
),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
Default.story = { name: 'Default (simple)' };
|
||||
Default.story = { name: "Default (simple)" };
|
||||
|
||||
export const Single = () => ({
|
||||
Component,
|
||||
props: {
|
||||
story: 'single',
|
||||
story: "single",
|
||||
datePicker: {
|
||||
id: 'date-picker',
|
||||
light: boolean('Light variant (light in <DatePicker>)', false),
|
||||
dateFormat: text('The date format (dateFormat in <DatePicker>)', 'm/d/Y')
|
||||
id: "date-picker",
|
||||
light: boolean("Light variant (light in <DatePicker>)", false),
|
||||
dateFormat: text("The date format (dateFormat in <DatePicker>)", "m/d/Y"),
|
||||
},
|
||||
datePickerInput: {
|
||||
id: 'date-picker-input-id',
|
||||
labelText: text('Label text (labelText in <DatePickerInput>)', 'Date Picker label'),
|
||||
hideLabel: boolean('Hide label (hideLabel)', false),
|
||||
pattern: select('The date format (pattern in <DatePickerInput>)', patterns, 'd{1,2}/d{4}'),
|
||||
placeholder: text('Placeholder text (placeholder in <DatePickerInput>)', 'mm/dd/yyyy'),
|
||||
disabled: boolean('Disabled (disabled in <DatePickerInput>)', false),
|
||||
invalid: boolean('Show form validation UI (invalid in <DatePickerInput>)', false),
|
||||
id: "date-picker-input-id",
|
||||
labelText: text(
|
||||
"Label text (labelText in <DatePickerInput>)",
|
||||
"Date Picker label"
|
||||
),
|
||||
hideLabel: boolean("Hide label (hideLabel)", false),
|
||||
pattern: select(
|
||||
"The date format (pattern in <DatePickerInput>)",
|
||||
patterns,
|
||||
"d{1,2}/d{4}"
|
||||
),
|
||||
placeholder: text(
|
||||
"Placeholder text (placeholder in <DatePickerInput>)",
|
||||
"mm/dd/yyyy"
|
||||
),
|
||||
disabled: boolean("Disabled (disabled in <DatePickerInput>)", false),
|
||||
invalid: boolean(
|
||||
"Show form validation UI (invalid in <DatePickerInput>)",
|
||||
false
|
||||
),
|
||||
invalidText: text(
|
||||
'Form validation UI content (invalidText in <DatePickerInput>)',
|
||||
'A valid value is required'
|
||||
"Form validation UI content (invalidText in <DatePickerInput>)",
|
||||
"A valid value is required"
|
||||
),
|
||||
iconDescription: text(
|
||||
'Icon description (iconDescription in <DatePickerInput>)',
|
||||
'Icon description'
|
||||
)
|
||||
}
|
||||
}
|
||||
"Icon description (iconDescription in <DatePickerInput>)",
|
||||
"Icon description"
|
||||
),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const Range = () => ({
|
||||
Component,
|
||||
props: {
|
||||
story: 'range',
|
||||
story: "range",
|
||||
datePicker: {
|
||||
id: 'date-picker',
|
||||
light: boolean('Light variant (light in <DatePicker>)', false),
|
||||
dateFormat: text('The date format (dateFormat in <DatePicker>)', 'm/d/Y')
|
||||
id: "date-picker",
|
||||
light: boolean("Light variant (light in <DatePicker>)", false),
|
||||
dateFormat: text("The date format (dateFormat in <DatePicker>)", "m/d/Y"),
|
||||
},
|
||||
datePickerInput: {
|
||||
id: 'date-picker-input-id',
|
||||
labelText: text('Label text (labelText in <DatePickerInput>)', 'Date Picker label'),
|
||||
hideLabel: boolean('Hide label (hideLabel)', false),
|
||||
pattern: select('The date format (pattern in <DatePickerInput>)', patterns, 'd{1,2}/d{4}'),
|
||||
placeholder: text('Placeholder text (placeholder in <DatePickerInput>)', 'mm/dd/yyyy'),
|
||||
disabled: boolean('Disabled (disabled in <DatePickerInput>)', false),
|
||||
invalid: boolean('Show form validation UI (invalid in <DatePickerInput>)', false),
|
||||
id: "date-picker-input-id",
|
||||
labelText: text(
|
||||
"Label text (labelText in <DatePickerInput>)",
|
||||
"Date Picker label"
|
||||
),
|
||||
hideLabel: boolean("Hide label (hideLabel)", false),
|
||||
pattern: select(
|
||||
"The date format (pattern in <DatePickerInput>)",
|
||||
patterns,
|
||||
"d{1,2}/d{4}"
|
||||
),
|
||||
placeholder: text(
|
||||
"Placeholder text (placeholder in <DatePickerInput>)",
|
||||
"mm/dd/yyyy"
|
||||
),
|
||||
disabled: boolean("Disabled (disabled in <DatePickerInput>)", false),
|
||||
invalid: boolean(
|
||||
"Show form validation UI (invalid in <DatePickerInput>)",
|
||||
false
|
||||
),
|
||||
invalidText: text(
|
||||
'Form validation UI content (invalidText in <DatePickerInput>)',
|
||||
'A valid value is required'
|
||||
"Form validation UI content (invalidText in <DatePickerInput>)",
|
||||
"A valid value is required"
|
||||
),
|
||||
iconDescription: text(
|
||||
'Icon description (iconDescription in <DatePickerInput>)',
|
||||
'Icon description'
|
||||
)
|
||||
}
|
||||
}
|
||||
"Icon description (iconDescription in <DatePickerInput>)",
|
||||
"Icon description"
|
||||
),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const Skeleton = () => ({ Component, props: { story: 'skeleton' } });
|
||||
export const Skeleton = () => ({ Component, props: { story: "skeleton" } });
|
||||
|
|
|
@ -1,39 +1,42 @@
|
|||
<script>
|
||||
let className = undefined;
|
||||
export { className as class };
|
||||
export let appendTo = document.body;
|
||||
export let dateFormat = 'm/d/Y';
|
||||
export let datePickerType = 'simple';
|
||||
export let id = Math.random();
|
||||
export let dateFormat = "m/d/Y";
|
||||
export let datePickerType = "simple";
|
||||
export let id = "ccs-" + Math.random().toString(36);
|
||||
export let light = false;
|
||||
export let locale = 'en';
|
||||
export let locale = "en";
|
||||
export let maxDate = null;
|
||||
export let minDate = null;
|
||||
export let short = false;
|
||||
export let style = undefined;
|
||||
export let value = '';
|
||||
export let value = "";
|
||||
|
||||
import { createEventDispatcher, setContext, afterUpdate, onDestroy } from 'svelte';
|
||||
import { writable, derived } from 'svelte/store';
|
||||
import { createCalendar } from './createCalendar';
|
||||
import { cx } from '../../lib';
|
||||
import {
|
||||
createEventDispatcher,
|
||||
setContext,
|
||||
afterUpdate,
|
||||
onDestroy
|
||||
} from "svelte";
|
||||
import { writable, derived } from "svelte/store";
|
||||
import { createCalendar } from "./createCalendar";
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
let inputs = writable([]);
|
||||
let inputIds = derived(inputs, _ => _.map(({ id }) => id));
|
||||
let labelTextEmpty = derived(inputs, _ => _.filter(({ labelText }) => !!labelText).length === 0);
|
||||
let inputValue = writable(value);
|
||||
let mode = writable(datePickerType);
|
||||
let range = derived(mode, _ => _ === 'range');
|
||||
let hasCalendar = derived(mode, _ => _ === 'single' || _ === 'range');
|
||||
const inputs = writable([]);
|
||||
const inputIds = derived(inputs, _ => _.map(({ id }) => id));
|
||||
const labelTextEmpty = derived(
|
||||
inputs,
|
||||
_ => _.filter(({ labelText }) => !!labelText).length === 0
|
||||
);
|
||||
const inputValue = writable(value);
|
||||
const mode = writable(datePickerType);
|
||||
const range = derived(mode, _ => _ === "range");
|
||||
const hasCalendar = derived(mode, _ => _ === "single" || _ === "range");
|
||||
|
||||
let calendar = undefined;
|
||||
let datePickerRef = undefined;
|
||||
let inputRef = undefined;
|
||||
let inputRefTo = undefined;
|
||||
|
||||
setContext('DatePicker', {
|
||||
setContext("DatePicker", {
|
||||
range,
|
||||
inputValue,
|
||||
hasCalendar,
|
||||
|
@ -48,12 +51,12 @@
|
|||
}
|
||||
},
|
||||
updateValue: ({ type, value }) => {
|
||||
if ((!calendar && type === 'input') || type === 'change') {
|
||||
if ((!calendar && type === "input") || type === "change") {
|
||||
inputValue.set(value);
|
||||
}
|
||||
|
||||
if (!calendar && type === 'change') {
|
||||
dispatch('change', value);
|
||||
if (!calendar && type === "change") {
|
||||
dispatch("change", value);
|
||||
}
|
||||
},
|
||||
blurInput: relatedTarget => {
|
||||
|
@ -68,7 +71,7 @@
|
|||
(
|
||||
calendar.selectedDateElem ||
|
||||
calendar.todayDateElem ||
|
||||
calendar.calendarContainer.querySelector('.flatpickr-day[tabindex]') ||
|
||||
calendar.calendarContainer.querySelector(".flatpickr-day[tabindex]") ||
|
||||
calendar.calendarContainer
|
||||
).focus();
|
||||
}
|
||||
|
@ -133,11 +136,21 @@
|
|||
}
|
||||
}} />
|
||||
|
||||
<div on:click on:mouseover on:mouseenter on:mouseleave class={cx('--form-item', className)} {style}>
|
||||
<div
|
||||
class:bx--form-item={true}
|
||||
{...$$restProps}
|
||||
on:click
|
||||
on:mouseover
|
||||
on:mouseenter
|
||||
on:mouseleave>
|
||||
<div
|
||||
bind:this={datePickerRef}
|
||||
class={cx('--date-picker', short && '--date-picker--short', light && '--date-picker--light', datePickerType && `--date-picker--${datePickerType}`, datePickerType === 'range' && $labelTextEmpty && '--date-picker--nolabel')}
|
||||
{id}>
|
||||
{id}
|
||||
class:bx--date-picker={true}
|
||||
class:bx--date-picker--short={short}
|
||||
class:bx--date-picker--light={light}
|
||||
class="{datePickerType && `--date-picker--${datePickerType}`}
|
||||
{datePickerType === 'range' && $labelTextEmpty && '--date-picker--nolabel'}">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,22 +1,19 @@
|
|||
<script>
|
||||
let className = undefined;
|
||||
export { className as class };
|
||||
export let type = "text";
|
||||
export let placeholder = "";
|
||||
export let pattern = "\\d{1,2}\\/\\d{1,2}\\/\\d{4}";
|
||||
export let disabled = false;
|
||||
export let iconDescription = "";
|
||||
export let id = "ccs-" + Math.random().toString(36);
|
||||
export let labelText = "";
|
||||
export let hideLabel = false;
|
||||
export let iconDescription = '';
|
||||
export let id = Math.random();
|
||||
export let invalid = false;
|
||||
export let invalidText = '';
|
||||
export let labelText = '';
|
||||
export let pattern = '\\d{1,2}\\/\\d{1,2}\\/\\d{4}';
|
||||
export let placeholder = '';
|
||||
export let style = undefined;
|
||||
export let type = 'text';
|
||||
export let invalidText = "";
|
||||
export let name = undefined;
|
||||
export let ref = null;
|
||||
|
||||
import { getContext, onMount } from 'svelte';
|
||||
import Calendar16 from 'carbon-icons-svelte/lib/Calendar16';
|
||||
import { cx } from '../../lib';
|
||||
import { getContext, onMount } from "svelte";
|
||||
import Calendar16 from "carbon-icons-svelte/lib/Calendar16";
|
||||
|
||||
const {
|
||||
range,
|
||||
|
@ -28,32 +25,33 @@
|
|||
openCalendar,
|
||||
focusCalendar,
|
||||
inputValue
|
||||
} = getContext('DatePicker');
|
||||
|
||||
let inputRef = undefined;
|
||||
} = getContext("DatePicker");
|
||||
|
||||
add({ id, labelText });
|
||||
|
||||
onMount(() => {
|
||||
declareRef({ id, ref: inputRef });
|
||||
declareRef({ id, ref });
|
||||
});
|
||||
</script>
|
||||
|
||||
<div
|
||||
class={cx('--date-picker-container', !labelText && '--date-picker--nolabel', className)}
|
||||
{style}>
|
||||
class:bx--date-picker-container={true}
|
||||
class:bx--date-picker--nolabel={!labelText}
|
||||
{...$$restProps}>
|
||||
{#if labelText}
|
||||
<label
|
||||
class={cx('--label', hideLabel && '--visually-hidden', disabled && '--label--disabled')}
|
||||
for={id}>
|
||||
for={id}
|
||||
class:bx--label={true}
|
||||
class:bx--visually-hidden={hideLabel}
|
||||
class:bx--label--disabled={disabled}>
|
||||
{labelText}
|
||||
</label>
|
||||
{/if}
|
||||
<div class={cx('--date-picker-input__wrapper')}>
|
||||
<div class:bx--date-picker-input__wrapper={true}>
|
||||
<input
|
||||
bind:this={inputRef}
|
||||
bind:this={ref}
|
||||
data-invalid={invalid || undefined}
|
||||
class={cx('--date-picker__input')}
|
||||
class:bx--date-picker__input={true}
|
||||
on:input
|
||||
on:input={({ target }) => {
|
||||
updateValue({ type: 'input', value: target.value });
|
||||
|
@ -81,13 +79,13 @@
|
|||
{#if $hasCalendar}
|
||||
<Calendar16
|
||||
role="img"
|
||||
class={cx('--date-picker__icon')}
|
||||
class="bx--date-picker__icon"
|
||||
aria-label={iconDescription}
|
||||
title={iconDescription}
|
||||
on:click={openCalendar} />
|
||||
{/if}
|
||||
</div>
|
||||
{#if invalid}
|
||||
<div class={cx('--form-requirement')}>{invalidText}</div>
|
||||
<div class:bx--form-requirement={true}>{invalidText}</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
|
|
@ -1,40 +1,47 @@
|
|||
import flatpickr from 'flatpickr';
|
||||
import l10n from 'flatpickr/dist/l10n/index.js';
|
||||
import rangePlugin from 'flatpickr/dist/plugins/rangePlugin';
|
||||
import { cx } from '../../lib';
|
||||
import flatpickr from "flatpickr";
|
||||
import l10n from "flatpickr/dist/l10n/index.js";
|
||||
import rangePlugin from "flatpickr/dist/plugins/rangePlugin";
|
||||
|
||||
function updateClasses(instance) {
|
||||
const { calendarContainer, days, daysContainer, weekdayContainer, selectedDates } = instance;
|
||||
const {
|
||||
calendarContainer,
|
||||
days,
|
||||
daysContainer,
|
||||
weekdayContainer,
|
||||
selectedDates,
|
||||
} = instance;
|
||||
|
||||
calendarContainer.classList.add(cx('--date-picker__calendar'));
|
||||
calendarContainer.querySelector('.flatpickr-month').classList.add(cx('--date-picker__month'));
|
||||
calendarContainer.classList.add("bx--date-picker__calendar");
|
||||
calendarContainer
|
||||
.querySelector(".flatpickr-month")
|
||||
.classList.add("bx--date-picker__month");
|
||||
|
||||
weekdayContainer.classList.add(cx('--date-picker__weekdays'));
|
||||
weekdayContainer.querySelectorAll('.flatpickr-weekday').forEach(node => {
|
||||
node.classList.add(cx('--date-picker__weekday'));
|
||||
weekdayContainer.classList.add("bx--date-picker__weekdays");
|
||||
weekdayContainer.querySelectorAll(".flatpickr-weekday").forEach((node) => {
|
||||
node.classList.add("bx--date-picker__weekday");
|
||||
});
|
||||
|
||||
daysContainer.classList.add(cx('--date-picker__days'));
|
||||
days.querySelectorAll('.flatpickr-day').forEach(node => {
|
||||
node.classList.add(cx('--date-picker__day'));
|
||||
if (node.classList.contains('today') && selectedDates.length > 0) {
|
||||
node.classList.add('no-border');
|
||||
} else if (node.classList.contains('today') && selectedDates.length === 0) {
|
||||
node.classList.remove('no-border');
|
||||
daysContainer.classList.add("bx--date-picker__days");
|
||||
days.querySelectorAll(".flatpickr-day").forEach((node) => {
|
||||
node.classList.add("bx--date-picker__day");
|
||||
if (node.classList.contains("today") && selectedDates.length > 0) {
|
||||
node.classList.add("no-border");
|
||||
} else if (node.classList.contains("today") && selectedDates.length === 0) {
|
||||
node.classList.remove("no-border");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updateMonthNode(instance) {
|
||||
const monthText = instance.l10n.months.longhand[instance.currentMonth];
|
||||
const staticMonthNode = instance.monthNav.querySelector('.cur-month');
|
||||
const staticMonthNode = instance.monthNav.querySelector(".cur-month");
|
||||
|
||||
if (staticMonthNode) {
|
||||
staticMonthNode.textContent = monthText;
|
||||
} else {
|
||||
const monthSelectNode = instance.monthsDropdownContainer;
|
||||
const span = document.createElement('span');
|
||||
span.setAttribute('class', 'cur-month');
|
||||
const span = document.createElement("span");
|
||||
span.setAttribute("class", "cur-month");
|
||||
span.textContent = monthText;
|
||||
monthSelectNode.parentNode.replaceChild(span, monthSelectNode);
|
||||
}
|
||||
|
@ -43,10 +50,11 @@ function updateMonthNode(instance) {
|
|||
function createCalendar({ options, base, input, dispatch }) {
|
||||
let locale = options.locale;
|
||||
|
||||
if (options.locale === 'en' && l10n && l10n.en) {
|
||||
if (options.locale === "en" && l10n && l10n.en) {
|
||||
l10n.en.weekdays.shorthand.forEach((_, index) => {
|
||||
const shorthand = _.slice(0, 2);
|
||||
l10n.en.weekdays.shorthand[index] = shorthand === 'Th' ? 'Th' : shorthand.charAt(0);
|
||||
l10n.en.weekdays.shorthand[index] =
|
||||
shorthand === "Th" ? "Th" : shorthand.charAt(0);
|
||||
});
|
||||
|
||||
locale = l10n.en;
|
||||
|
@ -58,27 +66,27 @@ function createCalendar({ options, base, input, dispatch }) {
|
|||
disableMobile: true,
|
||||
clickOpens: true,
|
||||
locale,
|
||||
plugins: [options.mode === 'range' && new rangePlugin({ position: 'left', input })].filter(
|
||||
Boolean
|
||||
),
|
||||
plugins: [
|
||||
options.mode === "range" && new rangePlugin({ position: "left", input }),
|
||||
].filter(Boolean),
|
||||
nextArrow:
|
||||
'<svg width="16px" height="16px" viewBox="0 0 16 16"><polygon points="11,8 6,13 5.3,12.3 9.6,8 5.3,3.7 6,3 "/><rect width="16" height="16" style="fill: none" /></svg>',
|
||||
prevArrow:
|
||||
'<svg width="16px" height="16px" viewBox="0 0 16 16"><polygon points="5,8 10,3 10.7,3.7 6.4,8 10.7,12.3 10,13 "/><rect width="16" height="16" style="fill: none" /></svg>',
|
||||
onChange: () => {
|
||||
dispatch('change');
|
||||
dispatch("change");
|
||||
},
|
||||
onClose: () => {
|
||||
dispatch('close');
|
||||
dispatch("close");
|
||||
},
|
||||
onMonthChange: (s, d, instance) => {
|
||||
updateMonthNode(instance);
|
||||
},
|
||||
onOpen: (s, d, instance) => {
|
||||
dispatch('open');
|
||||
dispatch("open");
|
||||
updateClasses(instance);
|
||||
updateMonthNode(instance);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,2 @@
|
|||
import DatePicker from './DatePicker.svelte';
|
||||
|
||||
export default DatePicker;
|
||||
export { default as DatePickerInput } from './DatePickerInput.svelte';
|
||||
export { default as DatePicker } from "./DatePicker.svelte";
|
||||
export { default as DatePickerInput } from "./DatePickerInput.svelte";
|
||||
|
|
|
@ -1,20 +1,19 @@
|
|||
<script>
|
||||
let className = undefined;
|
||||
export { className as class };
|
||||
export let inline = false;
|
||||
export let style = undefined;
|
||||
|
||||
import { cx } from '../../lib';
|
||||
</script>
|
||||
|
||||
<div
|
||||
class:bx--skeleton={true}
|
||||
class:bx--dropdown-v2={true}
|
||||
class:bx--list-box={true}
|
||||
class:bx--form-item={true}
|
||||
class:bx--list-box--inline={inline}
|
||||
{...$$restProps}
|
||||
on:click
|
||||
on:mouseover
|
||||
on:mouseenter
|
||||
on:mouseleave
|
||||
class={cx('--skeleton', '--dropdown-v2', '--list-box', '--form-item', inline && '--list-box--inline', className)}
|
||||
{style}>
|
||||
<div role="button" class={cx('--list-box__field')}>
|
||||
<span class={cx('--list-box__label')} />
|
||||
on:mouseleave>
|
||||
<div role="button" class:bx--list-box__field={true}>
|
||||
<span class:bx--list-box__label={true} />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,19 +1,18 @@
|
|||
<script>
|
||||
export let story = undefined;
|
||||
|
||||
import Layout from '../../internal/ui/Layout.svelte';
|
||||
import Button from '../Button';
|
||||
import Dropdown from './Dropdown.svelte';
|
||||
import DropdownSkeleton from './Dropdown.Skeleton.svelte';
|
||||
import Layout from "../../internal/ui/Layout.svelte";
|
||||
import { Button } from "../Button";
|
||||
import Dropdown from "./Dropdown.svelte";
|
||||
import DropdownSkeleton from "./Dropdown.Skeleton.svelte";
|
||||
|
||||
let items = [
|
||||
{ id: 'option-0', text: 'Option 1' },
|
||||
{ id: 'option-1', text: 'Option 2' },
|
||||
{ id: 'option-2', text: 'Option 3' },
|
||||
{ id: 'option-3', text: 'Option 4' }
|
||||
$: items = [
|
||||
{ id: "option-0", text: "Option 1" },
|
||||
{ id: "option-1", text: "Option 2" },
|
||||
{ id: "option-2", text: "Option 3" },
|
||||
{ id: "option-3", text: "Option 4" }
|
||||
];
|
||||
|
||||
let selectedIndex = -1;
|
||||
$: selectedIndex = -1;
|
||||
</script>
|
||||
|
||||
<Layout>
|
||||
|
|
|
@ -1,35 +1,41 @@
|
|||
import { withKnobs, select, text, boolean } from '@storybook/addon-knobs';
|
||||
import Component from './Dropdown.Story.svelte';
|
||||
import { withKnobs, select, text, boolean } from "@storybook/addon-knobs";
|
||||
import Component from "./Dropdown.Story.svelte";
|
||||
|
||||
export default { title: 'Dropdown', decorators: [withKnobs] };
|
||||
export default { title: "Dropdown", decorators: [withKnobs] };
|
||||
|
||||
const types = {
|
||||
'Default (default)': 'default',
|
||||
'Inline (inline)': 'inline'
|
||||
"Default (default)": "default",
|
||||
"Inline (inline)": "inline",
|
||||
};
|
||||
|
||||
const sizes = {
|
||||
'Extra large size (xl)': 'xl',
|
||||
'Regular size (lg)': '',
|
||||
'Small size (sm)': 'sm'
|
||||
"Extra large size (xl)": "xl",
|
||||
"Regular size (lg)": "",
|
||||
"Small size (sm)": "sm",
|
||||
};
|
||||
|
||||
export const Default = () => ({
|
||||
Component,
|
||||
props: {
|
||||
id: text('Dropdown id', 'carbon-dropdown-id'),
|
||||
name: text('Dropdown name', 'carbon-dropdown-name'),
|
||||
type: select('Dropdown type (type)', types, 'default'),
|
||||
size: select('Field size (size)', sizes, '') || undefined,
|
||||
label: text('Label (label)', 'Dropdown menu options'),
|
||||
'aria-label': text('Aria Label (aria-label)', 'Dropdown'),
|
||||
disabled: boolean('Disabled (disabled)', false),
|
||||
light: boolean('Light variant (light)', false),
|
||||
titleText: text('Title (titleText)', 'This is not a dropdown title.'),
|
||||
helperText: text('Helper text (helperText)', 'This is not some helper text.'),
|
||||
invalid: boolean('Show form validation UI (invalid)', false),
|
||||
invalidText: text('Form validation UI content (invalidText)', 'A valid value is required')
|
||||
}
|
||||
id: text("Dropdown id", "carbon-dropdown-id"),
|
||||
name: text("Dropdown name", "carbon-dropdown-name"),
|
||||
type: select("Dropdown type (type)", types, "default"),
|
||||
size: select("Field size (size)", sizes, "") || undefined,
|
||||
label: text("Label (label)", "Dropdown menu options"),
|
||||
"aria-label": text("Aria Label (aria-label)", "Dropdown"),
|
||||
disabled: boolean("Disabled (disabled)", false),
|
||||
light: boolean("Light variant (light)", false),
|
||||
titleText: text("Title (titleText)", "This is not a dropdown title."),
|
||||
helperText: text(
|
||||
"Helper text (helperText)",
|
||||
"This is not some helper text."
|
||||
),
|
||||
invalid: boolean("Show form validation UI (invalid)", false),
|
||||
invalidText: text(
|
||||
"Form validation UI content (invalidText)",
|
||||
"A valid value is required"
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
export const Skeleton = () => ({ Component, props: { story: 'skeleton' } });
|
||||
export const Skeleton = () => ({ Component, props: { story: "skeleton" } });
|
||||
|
|
|
@ -1,40 +1,35 @@
|
|||
<script>
|
||||
let className = undefined;
|
||||
export { className as class };
|
||||
export let disabled = false;
|
||||
export let helperText = '';
|
||||
export let id = Math.random();
|
||||
export let name = undefined;
|
||||
export let selectedIndex = -1;
|
||||
export let open = false;
|
||||
export let inline = false;
|
||||
export let light = false;
|
||||
export let disabled = false;
|
||||
export let invalid = false;
|
||||
export let invalidText = '';
|
||||
export let items = [];
|
||||
export let itemToString = item => item.text || item.id;
|
||||
export let type = "default"; // "default" | "inline"
|
||||
export let size = undefined; // "sm" | "lg" | "xl"
|
||||
export let id = "ccs-" + Math.random().toString(36);
|
||||
export let name = undefined;
|
||||
export let invalidText = "";
|
||||
export let helperText = "";
|
||||
export let label = undefined;
|
||||
export let light = false;
|
||||
export let open = false;
|
||||
export let selectedIndex = -1;
|
||||
export let size = undefined;
|
||||
export let style = undefined;
|
||||
export let titleText = '';
|
||||
export let titleText = "";
|
||||
export let translateWithId = undefined;
|
||||
export let type = 'default';
|
||||
export let ref = null;
|
||||
|
||||
import { setContext } from 'svelte';
|
||||
import WarningFilled16 from 'carbon-icons-svelte/lib/WarningFilled16';
|
||||
import { cx } from '../../lib';
|
||||
import ListBox, { ListBoxField, ListBoxMenu, ListBoxMenuIcon, ListBoxMenuItem } from '../ListBox';
|
||||
import { setContext } from "svelte";
|
||||
import WarningFilled16 from "carbon-icons-svelte/lib/WarningFilled16";
|
||||
import {
|
||||
ListBox,
|
||||
ListBoxMenu,
|
||||
ListBoxMenuIcon,
|
||||
ListBoxMenuItem
|
||||
} from "../ListBox";
|
||||
|
||||
let selectedId = undefined;
|
||||
let fieldRef = undefined;
|
||||
let highlightedIndex = -1;
|
||||
|
||||
setContext('Dropdown', {
|
||||
declareRef: ({ ref }) => {
|
||||
fieldRef = ref;
|
||||
}
|
||||
});
|
||||
|
||||
function change(direction) {
|
||||
let index = highlightedIndex + direction;
|
||||
|
||||
|
@ -47,7 +42,7 @@
|
|||
highlightedIndex = index;
|
||||
}
|
||||
|
||||
$: inline = type === 'inline';
|
||||
$: inline = type === "inline";
|
||||
$: selectedItem = items[selectedIndex];
|
||||
$: if (!open) {
|
||||
highlightedIndex = -1;
|
||||
|
@ -56,19 +51,27 @@
|
|||
|
||||
<svelte:body
|
||||
on:click={({ target }) => {
|
||||
if (open && fieldRef && !fieldRef.contains(target)) {
|
||||
if (open && ref && !ref.contains(target)) {
|
||||
open = false;
|
||||
}
|
||||
}} />
|
||||
|
||||
<div
|
||||
class={cx('--dropdown__wrapper', '--list-box__wrapper', inline && '--dropdown__wrapper--inline', inline && '--list-box__wrapper--inline', inline && invalid && '--dropdown__wrapper--inline--invalid', inline && invalid && '--list-box__wrapper--inline--invalid', className)}
|
||||
{style}>
|
||||
class:bx--dropdown__wrapper={true}
|
||||
class:bx--list-box__wrapper={true}
|
||||
class:bx--dropdown__wrapper--inline={inline}
|
||||
class:bx--list-box__wrapper--inline={inline}
|
||||
class:bx--dropdown__wrapper--inline--invalid={inline && invalid}
|
||||
{...$$restProps}>
|
||||
{#if titleText}
|
||||
<label for={id} class={cx('--label', disabled && '--label--disabled')}>{titleText}</label>
|
||||
<label for={id} class:bx--label={true} class:bx--label--disabled={disabled}>
|
||||
{titleText}
|
||||
</label>
|
||||
{/if}
|
||||
{#if !inline && helperText}
|
||||
<div class={cx('--form__helper-text', disabled && '--form__helper-text--disabled')}>
|
||||
<div
|
||||
class:bx--form__helper-text={true}
|
||||
class:bx--form__helper-text--disabled={disabled}>
|
||||
{helperText}
|
||||
</div>
|
||||
{/if}
|
||||
|
@ -78,9 +81,13 @@
|
|||
{id}
|
||||
{name}
|
||||
aria-label={$$props['aria-label']}
|
||||
class={cx('--dropdown', invalid && '--dropdown--invalid', open && '--dropdown--open', inline && '--dropdown--inline', disabled && '--dropdown--disabled', light && '--dropdown--light')}
|
||||
class="bx--dropdown {invalid && 'bx--dropdown--invalid'}
|
||||
{open && 'bx--dropdown--open'}
|
||||
{inline && 'bx--dropdown--inline'}
|
||||
{disabled && 'bx--dropdown--disabled'}
|
||||
{light && 'bx--dropdown--light'}"
|
||||
on:click={({ target }) => {
|
||||
open = fieldRef.contains(target) ? !open : false;
|
||||
open = ref.contains(target) ? !open : false;
|
||||
}}
|
||||
{disabled}
|
||||
{open}
|
||||
|
@ -88,9 +95,11 @@
|
|||
{invalidText}
|
||||
{light}>
|
||||
{#if invalid}
|
||||
<WarningFilled16 class={cx('--list-box__invalid-icon')} />
|
||||
<WarningFilled16 class="bx--list-box__invalid-icon" />
|
||||
{/if}
|
||||
<ListBoxField
|
||||
<button
|
||||
bind:this={ref}
|
||||
class:bx--list-box__field={true}
|
||||
tabindex="0"
|
||||
role="button"
|
||||
aria-expanded={open}
|
||||
|
@ -103,7 +112,7 @@
|
|||
}
|
||||
} else if (key === 'Tab') {
|
||||
open = false;
|
||||
fieldRef.blur();
|
||||
ref.blur();
|
||||
} else if (key === 'ArrowDown') {
|
||||
change(1);
|
||||
} else if (key === 'ArrowUp') {
|
||||
|
@ -112,17 +121,17 @@
|
|||
}}
|
||||
on:blur={({ relatedTarget }) => {
|
||||
if (relatedTarget) {
|
||||
fieldRef.focus();
|
||||
ref.focus();
|
||||
}
|
||||
}}
|
||||
{disabled}
|
||||
{translateWithId}
|
||||
{id}>
|
||||
<span class={cx('--list-box__label')}>
|
||||
<span class="bx--list-box__label">
|
||||
{#if selectedItem}{itemToString(selectedItem)}{:else}{label}{/if}
|
||||
</span>
|
||||
<ListBoxMenuIcon {open} {translateWithId} />
|
||||
</ListBoxField>
|
||||
</button>
|
||||
{#if open}
|
||||
<ListBoxMenu aria-labelledby={id} {id}>
|
||||
{#each items as item, i (item.id || i)}
|
||||
|
|
|
@ -1,4 +1,2 @@
|
|||
import Dropdown from './Dropdown.svelte';
|
||||
|
||||
export default Dropdown;
|
||||
export { default as DropdownSkeleton } from './Dropdown.Skeleton.svelte';
|
||||
export { default as Dropdown } from "./Dropdown.svelte";
|
||||
export { default as DropdownSkeleton } from "./Dropdown.Skeleton.svelte";
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
<script>
|
||||
let className = undefined;
|
||||
export { className as class };
|
||||
export let style = undefined;
|
||||
|
||||
import { cx } from '../../lib';
|
||||
import { ButtonSkeleton } from '../Button';
|
||||
import SkeletonText from '../SkeletonText';
|
||||
import { ButtonSkeleton } from "../Button";
|
||||
import { SkeletonText } from "../SkeletonText";
|
||||
</script>
|
||||
|
||||
<div on:click on:mouseover on:mouseenter on:mouseleave class={cx('--form-item', className)} {style}>
|
||||
<div
|
||||
class:bx--form-item={true}
|
||||
{...$$restProps}
|
||||
on:click
|
||||
on:mouseover
|
||||
on:mouseenter
|
||||
on:mouseleave>
|
||||
<SkeletonText heading width="100px" />
|
||||
<SkeletonText width="225px" class={cx('--label-description')} />
|
||||
<SkeletonText width="225px" class="bx--label-description" />
|
||||
<ButtonSkeleton />
|
||||
</div>
|
||||
|
|
|
@ -1,18 +1,16 @@
|
|||
<script>
|
||||
export let story = undefined;
|
||||
|
||||
import Layout from '../../internal/ui/Layout.svelte';
|
||||
import { cx } from '../../lib';
|
||||
import Button from '../Button';
|
||||
import FileUploader from './FileUploader.svelte';
|
||||
import FileUploaderButton from './FileUploaderButton.svelte';
|
||||
import FileUploaderDropContainer from './FileUploaderDropContainer.svelte';
|
||||
import FileUploaderItem from './FileUploaderItem.svelte';
|
||||
import FileUploaderSkeleton from './FileUploader.Skeleton.svelte';
|
||||
|
||||
let fileUploader = undefined;
|
||||
let files = [];
|
||||
import Layout from "../../internal/ui/Layout.svelte";
|
||||
import { Button } from "../Button";
|
||||
import FileUploader from "./FileUploader.svelte";
|
||||
import FileUploaderButton from "./FileUploaderButton.svelte";
|
||||
import FileUploaderDropContainer from "./FileUploaderDropContainer.svelte";
|
||||
import FileUploaderItem from "./FileUploaderItem.svelte";
|
||||
import FileUploaderSkeleton from "./FileUploader.Skeleton.svelte";
|
||||
|
||||
$: fileUploader = undefined;
|
||||
$: files = [];
|
||||
$: disabled = files.length === 0;
|
||||
</script>
|
||||
|
||||
|
@ -36,7 +34,7 @@
|
|||
console.log('click');
|
||||
}} />
|
||||
{:else if story === 'uploader'}
|
||||
<div class={cx('--file__container')}>
|
||||
<div class="bx--file__container">
|
||||
<FileUploader
|
||||
bind:this={fileUploader}
|
||||
{...$$props}
|
||||
|
|
|
@ -1,98 +1,116 @@
|
|||
import { withKnobs, text, select, boolean, array } from '@storybook/addon-knobs';
|
||||
import Component from './FileUploader.Story.svelte';
|
||||
import {
|
||||
withKnobs,
|
||||
text,
|
||||
select,
|
||||
boolean,
|
||||
array,
|
||||
} from "@storybook/addon-knobs";
|
||||
import Component from "./FileUploader.Story.svelte";
|
||||
|
||||
export default { title: 'FileUploader', decorators: [withKnobs] };
|
||||
export default { title: "FileUploader", decorators: [withKnobs] };
|
||||
|
||||
const buttonKinds = {
|
||||
'Primary (primary)': 'primary',
|
||||
'Secondary (secondary)': 'secondary',
|
||||
'Danger (danger)': 'danger',
|
||||
'Ghost (ghost)': 'ghost',
|
||||
'Tertiary (tertiary)': 'tertiary'
|
||||
"Primary (primary)": "primary",
|
||||
"Secondary (secondary)": "secondary",
|
||||
"Danger (danger)": "danger",
|
||||
"Ghost (ghost)": "ghost",
|
||||
"Tertiary (tertiary)": "tertiary",
|
||||
};
|
||||
|
||||
const filenameStatuses = {
|
||||
'Edit (edit)': 'edit',
|
||||
'Complete (complete)': 'complete',
|
||||
'Uploading (uploading)': 'uploading'
|
||||
"Edit (edit)": "edit",
|
||||
"Complete (complete)": "complete",
|
||||
"Uploading (uploading)": "uploading",
|
||||
};
|
||||
|
||||
export const FileUploaderButton = () => ({
|
||||
Component,
|
||||
props: {
|
||||
story: 'button',
|
||||
kind: select('Button kind (kind)', buttonKinds, 'primary'),
|
||||
labelText: text('Label text (labelText)', 'Add files'),
|
||||
name: text('Form item name: (name)', ''),
|
||||
multiple: boolean('Supports multiple files (multiple)', true),
|
||||
disabled: boolean('Disabled (disabled)', false),
|
||||
story: "button",
|
||||
kind: select("Button kind (kind)", buttonKinds, "primary"),
|
||||
labelText: text("Label text (labelText)", "Add files"),
|
||||
name: text("Form item name: (name)", ""),
|
||||
multiple: boolean("Supports multiple files (multiple)", true),
|
||||
disabled: boolean("Disabled (disabled)", false),
|
||||
disableLabelChanges: boolean(
|
||||
'Prevent the label from being replaced with file selected file (disableLabelChanges)',
|
||||
"Prevent the label from being replaced with file selected file (disableLabelChanges)",
|
||||
false
|
||||
),
|
||||
role: text('ARIA role of the button (role)', 'button'),
|
||||
tabindex: text('Tab index (tabindex)', '0')
|
||||
}
|
||||
role: text("ARIA role of the button (role)", "button"),
|
||||
tabindex: text("Tab index (tabindex)", "0"),
|
||||
},
|
||||
});
|
||||
|
||||
FileUploaderButton.story = { name: 'FileUploaderButton' };
|
||||
FileUploaderButton.story = { name: "FileUploaderButton" };
|
||||
|
||||
export const FileUploader = () => ({
|
||||
Component,
|
||||
props: {
|
||||
story: 'uploader',
|
||||
labelTitle: text('The label title (labelTitle)', 'Upload'),
|
||||
story: "uploader",
|
||||
labelTitle: text("The label title (labelTitle)", "Upload"),
|
||||
labelDescription: text(
|
||||
'The label description (labelDescription)',
|
||||
'only .jpg files at 500mb or less'
|
||||
"The label description (labelDescription)",
|
||||
"only .jpg files at 500mb or less"
|
||||
),
|
||||
buttonLabel: text('The button label (buttonLabel)', 'Add files'),
|
||||
status: select('Status for file name (status)', filenameStatuses, 'edit'),
|
||||
accept: array('Accepted file extensions (accept)', ['.jpg', '.png'], ','),
|
||||
name: text('Form item name: (name)', ''),
|
||||
multiple: boolean('Supports multiple files (multiple)', true),
|
||||
iconDescription: text('Close button icon description (iconDescription)', 'Clear file')
|
||||
}
|
||||
buttonLabel: text("The button label (buttonLabel)", "Add files"),
|
||||
status: select("Status for file name (status)", filenameStatuses, "edit"),
|
||||
accept: array("Accepted file extensions (accept)", [".jpg", ".png"], ","),
|
||||
name: text("Form item name: (name)", ""),
|
||||
multiple: boolean("Supports multiple files (multiple)", true),
|
||||
iconDescription: text(
|
||||
"Close button icon description (iconDescription)",
|
||||
"Clear file"
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
FileUploader.story = { name: 'FileUploader' };
|
||||
FileUploader.story = { name: "FileUploader" };
|
||||
|
||||
export const FileUploaderItem = () => ({
|
||||
Component,
|
||||
props: {
|
||||
story: 'item',
|
||||
name: text('Filename (name)', 'README.md'),
|
||||
status: select('Status for file name (status)', filenameStatuses, 'edit'),
|
||||
iconDescription: text('Close button icon description (iconDescription)', 'Clear file'),
|
||||
invalid: boolean('Invalid (invalid)', false),
|
||||
errorSubject: text('Error subject (errorSubject)', 'File size exceeds limit'),
|
||||
story: "item",
|
||||
name: text("Filename (name)", "README.md"),
|
||||
status: select("Status for file name (status)", filenameStatuses, "edit"),
|
||||
iconDescription: text(
|
||||
"Close button icon description (iconDescription)",
|
||||
"Clear file"
|
||||
),
|
||||
invalid: boolean("Invalid (invalid)", false),
|
||||
errorSubject: text(
|
||||
"Error subject (errorSubject)",
|
||||
"File size exceeds limit"
|
||||
),
|
||||
errorBody: text(
|
||||
'Error body (errorBody)',
|
||||
'500kb max file size. Select a new file and try again.'
|
||||
)
|
||||
}
|
||||
"Error body (errorBody)",
|
||||
"500kb max file size. Select a new file and try again."
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
FileUploaderItem.story = { name: 'FileUploaderItem' };
|
||||
FileUploaderItem.story = { name: "FileUploaderItem" };
|
||||
|
||||
export const FileUploaderDropContainer = () => ({
|
||||
Component,
|
||||
props: {
|
||||
story: 'drop container',
|
||||
labelText: text('Label text (labelText)', 'Drag and drop files here or click to upload'),
|
||||
name: text('Form item name (name)', ''),
|
||||
multiple: boolean('Supports multiple files (multiple)', true),
|
||||
accept: array(
|
||||
'Accepted MIME types or file extensions (accept)',
|
||||
['image/jpeg', 'image/png'],
|
||||
','
|
||||
story: "drop container",
|
||||
labelText: text(
|
||||
"Label text (labelText)",
|
||||
"Drag and drop files here or click to upload"
|
||||
),
|
||||
disabled: boolean('Disabled (disabled)', false),
|
||||
role: text('ARIA role of the button (role)', ''),
|
||||
tabindex: text('Tab index (tabindex)', '0')
|
||||
}
|
||||
name: text("Form item name (name)", ""),
|
||||
multiple: boolean("Supports multiple files (multiple)", true),
|
||||
accept: array(
|
||||
"Accepted MIME types or file extensions (accept)",
|
||||
["image/jpeg", "image/png"],
|
||||
","
|
||||
),
|
||||
disabled: boolean("Disabled (disabled)", false),
|
||||
role: text("ARIA role of the button (role)", ""),
|
||||
tabindex: text("Tab index (tabindex)", "0"),
|
||||
},
|
||||
});
|
||||
|
||||
FileUploaderDropContainer.story = { name: 'FileUploaderDropContainer' };
|
||||
FileUploaderDropContainer.story = { name: "FileUploaderDropContainer" };
|
||||
|
||||
export const Skeleton = () => ({ Component, props: { story: 'skeleton' } });
|
||||
export const Skeleton = () => ({ Component, props: { story: "skeleton" } });
|
||||
|
|
|
@ -1,62 +1,63 @@
|
|||
<script>
|
||||
let className = undefined;
|
||||
export { className as class };
|
||||
export const clearFiles = () => (files = []);
|
||||
export let status = "uploading"; // "uploading" | "edit" | "complete"
|
||||
export let accept = [];
|
||||
export let buttonLabel = '';
|
||||
export let files = [];
|
||||
export let iconDescription = 'Provide icon description';
|
||||
export let kind = 'primary';
|
||||
export let labelDescription = '';
|
||||
export let labelTitle = '';
|
||||
export const clearFiles = () => (files = []);
|
||||
export let buttonLabel = "";
|
||||
export let iconDescription = "Provide icon description";
|
||||
export let kind = "primary";
|
||||
export let labelDescription = "";
|
||||
export let labelTitle = "";
|
||||
export let multiple = false;
|
||||
export let name = '';
|
||||
export let status = 'uploading';
|
||||
export let style = undefined;
|
||||
export let name = "";
|
||||
|
||||
import { createEventDispatcher, afterUpdate } from 'svelte';
|
||||
import { cx } from '../../lib';
|
||||
import Filename from './Filename.svelte';
|
||||
import FileUploaderButton from './FileUploaderButton.svelte';
|
||||
import { createEventDispatcher, afterUpdate } from "svelte";
|
||||
import Filename from "./Filename.svelte";
|
||||
import FileUploaderButton from "./FileUploaderButton.svelte";
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
let prevFiles = [];
|
||||
$: prevFiles = [];
|
||||
|
||||
afterUpdate(() => {
|
||||
if (files.length > prevFiles.length) {
|
||||
dispatch('add', files);
|
||||
dispatch("add", files);
|
||||
} else {
|
||||
dispatch(
|
||||
'remove',
|
||||
prevFiles.filter(_ => !files.includes(_))
|
||||
);
|
||||
dispatch("remove", prevFiles.filter(_ => !files.includes(_)));
|
||||
}
|
||||
|
||||
prevFiles = [...files];
|
||||
});
|
||||
</script>
|
||||
|
||||
<div on:click on:mouseover on:mouseenter on:mouseleave class={cx('--form-item', className)} {style}>
|
||||
<strong class={cx('--file--label')}>{labelTitle}</strong>
|
||||
<p class={cx('--label-description')}>{labelDescription}</p>
|
||||
<div
|
||||
class:bx--form-item={true}
|
||||
{...$$restProps}
|
||||
on:click
|
||||
on:mouseover
|
||||
on:mouseenter
|
||||
on:mouseleave>
|
||||
<strong class:bx--file--label={true}>{labelTitle}</strong>
|
||||
<p class:bx--label-description={true}>{labelDescription}</p>
|
||||
<FileUploaderButton
|
||||
disableLabelChanges
|
||||
labelText={buttonLabel}
|
||||
on:change
|
||||
on:change={({ target }) => {
|
||||
files = [...target.files].map(({ name }) => name);
|
||||
}}
|
||||
{accept}
|
||||
{name}
|
||||
{multiple}
|
||||
{kind} />
|
||||
<div class={cx('--file-container')}>
|
||||
{kind}
|
||||
on:change
|
||||
on:change={({ target }) => {
|
||||
files = [...target.files].map(({ name }) => name);
|
||||
}} />
|
||||
<div class:bx--file-container={true}>
|
||||
{#each files as name, i (name)}
|
||||
<span class={cx('--file__selected-file')}>
|
||||
<p class={cx('--file-filename')}>{name}</p>
|
||||
<span class={cx('--file__state-container')}>
|
||||
<span class:bx--file__selected-file={true}>
|
||||
<p class:bx--file-filename={true}>{name}</p>
|
||||
<span class:bx--file__state-container={true}>
|
||||
<Filename
|
||||
{iconDescription}
|
||||
{status}
|
||||
on:keydown
|
||||
on:keydown={({ key }) => {
|
||||
if (key === ' ' || key === 'Enter') {
|
||||
|
@ -66,9 +67,7 @@
|
|||
on:click
|
||||
on:click={() => {
|
||||
files = files.filter((_, index) => index !== i);
|
||||
}}
|
||||
{iconDescription}
|
||||
{status} />
|
||||
}} />
|
||||
</span>
|
||||
</span>
|
||||
{/each}
|
||||
|
|
|
@ -1,42 +1,44 @@
|
|||
<script>
|
||||
let className = undefined;
|
||||
export { className as class };
|
||||
export let accept = [];
|
||||
export let multiple = false;
|
||||
export let disabled = false;
|
||||
export let disableLabelChanges = false;
|
||||
export let id = Math.random();
|
||||
export let kind = 'primary';
|
||||
export let labelText = 'Add file';
|
||||
export let multiple = false;
|
||||
export let name = '';
|
||||
export let role = 'button';
|
||||
export let style = undefined;
|
||||
export let tabindex = '0';
|
||||
|
||||
import { cx } from '../../lib';
|
||||
|
||||
let inputRef = undefined;
|
||||
export let kind = "primary"; // Button.kind
|
||||
export let labelText = "Add file";
|
||||
export let role = "button";
|
||||
export let tabindex = "0";
|
||||
export let id = "ccs-" + Math.random().toString(36);
|
||||
export let name = "";
|
||||
export let ref = null;
|
||||
</script>
|
||||
|
||||
<label
|
||||
tabindex={disabled ? '-1' : tabindex}
|
||||
aria-disabled={disabled}
|
||||
class={cx('--btn', '--btn--sm', kind && `--btn--${kind}`, disabled && '--btn--disabled', className)}
|
||||
for={id}
|
||||
tabindex={disabled ? '-1' : tabindex}
|
||||
class:bx--btn={true}
|
||||
class:bx--btn--sm={true}
|
||||
class:bx--btn--disabled={disabled}
|
||||
class={kind && `bx--btn--${kind}`}
|
||||
on:keydown
|
||||
on:keydown={({ key }) => {
|
||||
if (key === ' ' || key === 'Enter') {
|
||||
inputRef.click();
|
||||
ref.click();
|
||||
}
|
||||
}}
|
||||
{style}>
|
||||
}}>
|
||||
<span {role}>{labelText}</span>
|
||||
</label>
|
||||
<input
|
||||
bind:this={inputRef}
|
||||
bind:this={ref}
|
||||
type="file"
|
||||
tabindex="-1"
|
||||
class={cx('--visually-hidden')}
|
||||
{accept}
|
||||
{disabled}
|
||||
{id}
|
||||
{multiple}
|
||||
{name}
|
||||
class:bx--visually-hidden={true}
|
||||
{...$$restProps}
|
||||
on:change|stopPropagation
|
||||
on:change|stopPropagation={({ target }) => {
|
||||
const files = target.files;
|
||||
|
@ -48,9 +50,4 @@
|
|||
on:click
|
||||
on:click={({ target }) => {
|
||||
target.value = null;
|
||||
}}
|
||||
{id}
|
||||
{disabled}
|
||||
{multiple}
|
||||
{accept}
|
||||
{name} />
|
||||
}} />
|
||||
|
|
|
@ -1,28 +1,25 @@
|
|||
<script>
|
||||
let className = undefined;
|
||||
export { className as class };
|
||||
export let accept = [];
|
||||
export let disabled = false;
|
||||
export let id = Math.random();
|
||||
export let labelText = 'Add file';
|
||||
export let multiple = false;
|
||||
export let name = '';
|
||||
export let role = 'button';
|
||||
export let style = undefined;
|
||||
export let tabindex = '0';
|
||||
export let validateFiles = files => files;
|
||||
export let labelText = "Add file";
|
||||
export let role = "button";
|
||||
export let tabindex = "0";
|
||||
export let id = "ccs-" + Math.random().toString(36);
|
||||
export let name = "";
|
||||
export let ref = null;
|
||||
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { cx } from '../../lib';
|
||||
import { createEventDispatcher } from "svelte";
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
let over = false;
|
||||
let inputRef = undefined;
|
||||
$: over = false;
|
||||
</script>
|
||||
|
||||
<div
|
||||
class={cx('--file')}
|
||||
class:bx--file={true}
|
||||
{...$$restProps}
|
||||
on:dragover
|
||||
on:dragover|preventDefault|stopPropagation={({ dataTransfer }) => {
|
||||
if (!disabled) {
|
||||
|
@ -43,27 +40,33 @@
|
|||
over = false;
|
||||
dispatch('add', validateFiles(dataTransfer.files));
|
||||
}
|
||||
}}
|
||||
{style}>
|
||||
}}>
|
||||
<label
|
||||
class={cx('--file-browse-btn', disabled && '--file-browse-btn--disabled')}
|
||||
for={id}
|
||||
{tabindex}
|
||||
class:bx--file-browse-btn={true}
|
||||
class:bx--file-browse-btn--disabled={disabled}
|
||||
on:keydown
|
||||
on:keydown={({ key }) => {
|
||||
if (key === ' ' || key === 'Enter') {
|
||||
inputRef.click();
|
||||
ref.click();
|
||||
}
|
||||
}}
|
||||
{tabindex}>
|
||||
}}>
|
||||
<div
|
||||
class={cx('--file__drop-container', over && '--file__drop-container--drag-over', className)}
|
||||
{role}>
|
||||
{role}
|
||||
class:bx--file__drop-container={true}
|
||||
class:bx--file__drop-container--drag-over={over}>
|
||||
{labelText}
|
||||
<input
|
||||
bind:this={inputRef}
|
||||
bind:this={ref}
|
||||
type="file"
|
||||
tabindex="-1"
|
||||
class={cx('--file-input')}
|
||||
{id}
|
||||
{disabled}
|
||||
{accept}
|
||||
{name}
|
||||
{multiple}
|
||||
class:bx--file-input={true}
|
||||
on:change
|
||||
on:change={({ target }) => {
|
||||
dispatch('add', validateFiles(target.files));
|
||||
|
@ -71,12 +74,7 @@
|
|||
on:click
|
||||
on:click={({ target }) => {
|
||||
target.value = null;
|
||||
}}
|
||||
{id}
|
||||
{disabled}
|
||||
{accept}
|
||||
{name}
|
||||
{multiple} />
|
||||
}} />
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
|
|
|
@ -1,30 +1,27 @@
|
|||
<script>
|
||||
let className = undefined;
|
||||
export { className as class };
|
||||
export let errorBody = '';
|
||||
export let errorSubject = '';
|
||||
export let iconDescription = '';
|
||||
export let id = Math.random();
|
||||
export let errorBody = "";
|
||||
export let errorSubject = "";
|
||||
export let iconDescription = "";
|
||||
export let id = "ccs-" + Math.random().toString(36);
|
||||
export let invalid = false;
|
||||
export let name = '';
|
||||
export let status = 'uploading';
|
||||
export let style = undefined;
|
||||
export let name = "";
|
||||
export let status = "uploading";
|
||||
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { cx } from '../../lib';
|
||||
import Filename from './Filename.svelte';
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import Filename from "./Filename.svelte";
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
</script>
|
||||
|
||||
<span
|
||||
class:bx--file__selected-file={true}
|
||||
class:bx--file__selected-file--invalid={invalid}
|
||||
{...$$restProps}
|
||||
on:mouseover
|
||||
on:mouseenter
|
||||
on:mouseleave
|
||||
class={cx('--file__selected-file', invalid && '--file__selected-file--invalid', className)}
|
||||
{style}>
|
||||
<p class={cx('--file-filename')}>{name}</p>
|
||||
<span class={cx('--file__state-container')}>
|
||||
on:mouseleave>
|
||||
<p class:bx--file-filename={true}>{name}</p>
|
||||
<span class:bx--file__state-container={true}>
|
||||
<Filename
|
||||
on:keydown={({ key }) => {
|
||||
if (key === ' ' || key === 'Enter') {
|
||||
|
@ -39,10 +36,10 @@
|
|||
{invalid} />
|
||||
</span>
|
||||
{#if invalid && errorSubject}
|
||||
<div class={cx('--form-requirement')}>
|
||||
<div class={cx('--form-requirement__title')}>{errorSubject}</div>
|
||||
<div class:bx--form-requirement={true}>
|
||||
<div class:bx--form-requirement__title={true}>{errorSubject}</div>
|
||||
{#if errorBody}
|
||||
<p class={cx('--form-requirement__supplement')}>{errorBody}</p>
|
||||
<p class:bx--form-requirement__supplement={true}>{errorBody}</p>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
@ -1,42 +1,39 @@
|
|||
<script>
|
||||
let className = undefined;
|
||||
export { className as class };
|
||||
export let iconDescription = '';
|
||||
export let iconDescription = "";
|
||||
export let invalid = false;
|
||||
export let status = 'uploading';
|
||||
export let style = undefined;
|
||||
export let tabindex = '0';
|
||||
export let status = "uploading"; // "uploading" | "edit" | "complete"
|
||||
|
||||
import Close16 from 'carbon-icons-svelte/lib/Close16';
|
||||
import CheckmarkFilled16 from 'carbon-icons-svelte/lib/CheckmarkFilled16';
|
||||
import WarningFilled16 from 'carbon-icons-svelte/lib/WarningFilled16';
|
||||
import { cx } from '../../lib';
|
||||
import Loading from '../Loading';
|
||||
import Close16 from "carbon-icons-svelte/lib/Close16";
|
||||
import CheckmarkFilled16 from "carbon-icons-svelte/lib/CheckmarkFilled16";
|
||||
import WarningFilled16 from "carbon-icons-svelte/lib/WarningFilled16";
|
||||
import { Loading } from "../Loading";
|
||||
</script>
|
||||
|
||||
{#if status === 'uploading'}
|
||||
<Loading small description={iconDescription} withOverlay={false} class={className} {style} />
|
||||
<Loading
|
||||
description={iconDescription}
|
||||
{...$$restProps}
|
||||
small
|
||||
withOverlay={false} />
|
||||
{/if}
|
||||
|
||||
{#if status === 'edit'}
|
||||
{#if invalid}
|
||||
<WarningFilled16 class={cx('--file-invalid')} />
|
||||
<WarningFilled16 class="bx--file-invalid" />
|
||||
{/if}
|
||||
<Close16
|
||||
class={cx('--file-close', className)}
|
||||
aria-label={iconDescription}
|
||||
title={iconDescription}
|
||||
class="bx--file-close"
|
||||
{...$$restProps}
|
||||
on:click
|
||||
on:keydown
|
||||
{tabindex}
|
||||
{style} />
|
||||
on:keydown />
|
||||
{/if}
|
||||
|
||||
{#if status === 'complete'}
|
||||
<CheckmarkFilled16
|
||||
class={cx('--file-complete', className)}
|
||||
aria-label={iconDescription}
|
||||
title={iconDescription}
|
||||
{tabindex}
|
||||
{style} />
|
||||
class="bx--file-complete"
|
||||
{...$$restProps} />
|
||||
{/if}
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
import FileUploader from './FileUploader.svelte';
|
||||
|
||||
export default FileUploader;
|
||||
export { default as FileUploaderButton } from './FileUploaderButton.svelte';
|
||||
export { default as FileUploaderItem } from './FileUploaderItem.svelte';
|
||||
export { default as FileUploaderDropContainer } from './FileUploaderDropContainer.svelte';
|
||||
export { default as Filename } from './Filename.svelte';
|
||||
export { default as FileUploader } from "./FileUploader.svelte";
|
||||
export { default as FileUploaderButton } from "./FileUploaderButton.svelte";
|
||||
export { default as FileUploaderItem } from "./FileUploaderItem.svelte";
|
||||
export { default as FileUploaderDropContainer } from "./FileUploaderDropContainer.svelte";
|
||||
export { default as Filename } from "./Filename.svelte";
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
<script>
|
||||
import Layout from '../../internal/ui/Layout.svelte';
|
||||
import Checkbox from '../Checkbox';
|
||||
import FormGroup from '../FormGroup';
|
||||
import FileUploader from '../FileUploader';
|
||||
import NumberInput from '../NumberInput';
|
||||
import RadioButton from '../RadioButton';
|
||||
import RadioButtonGroup from '../RadioButtonGroup';
|
||||
import Button from '../Button';
|
||||
import Search from '../Search';
|
||||
import Select, { SelectItem } from '../Select';
|
||||
import TextArea from '../TextArea';
|
||||
import TextInput from '../TextInput';
|
||||
import Toggle from '../Toggle';
|
||||
import Form from './Form.svelte';
|
||||
import Layout from "../../internal/ui/Layout.svelte";
|
||||
import { Checkbox } from "../Checkbox";
|
||||
import { FormGroup } from "../FormGroup";
|
||||
import { FileUploader } from "../FileUploader";
|
||||
import { NumberInput } from "../NumberInput";
|
||||
import { RadioButton } from "../RadioButton";
|
||||
import { RadioButtonGroup } from "../RadioButtonGroup";
|
||||
import { Button } from "../Button";
|
||||
import { Search } from "../Search";
|
||||
import { Select, SelectItem } from "../Select";
|
||||
import { TextArea } from "../TextArea";
|
||||
import { TextInput } from "../TextInput";
|
||||
import { Toggle } from "../Toggle";
|
||||
import Form from "./Form.svelte";
|
||||
</script>
|
||||
|
||||
<Layout>
|
||||
|
@ -25,42 +25,74 @@
|
|||
<Checkbox id="checkbox-1" labelText="Checkbox Label" />
|
||||
<Checkbox id="checkbox-2" labelText="Checkbox Label" disabled />
|
||||
</FormGroup>
|
||||
<NumberInput id="number-input-1" label="Number Input" min={0} max={100} value={50} step={10} />
|
||||
<NumberInput
|
||||
id="number-input-1"
|
||||
label="Number Input"
|
||||
min={0}
|
||||
max={100}
|
||||
value={50}
|
||||
step={10} />
|
||||
<FormGroup legendText="Toggle heading">
|
||||
<Toggle id="toggle-1" />
|
||||
<Toggle id="toggle-2" disabled />
|
||||
</FormGroup>
|
||||
<FormGroup legendText="File Uploader">
|
||||
<FileUploader id="file-1" buttonLabel="Add files" labelDescription="Choose Files..." />
|
||||
<FileUploader
|
||||
id="file-1"
|
||||
buttonLabel="Add files"
|
||||
labelDescription="Choose Files..." />
|
||||
</FormGroup>
|
||||
<FormGroup legendText="Radio Button heading">
|
||||
<RadioButtonGroup name="radio-button-group" defaultSelected="default-selected">
|
||||
<RadioButton id="radio-1" value="standard" labelText="Standard Radio Button" />
|
||||
<RadioButtonGroup
|
||||
name="radio-button-group"
|
||||
defaultSelected="default-selected">
|
||||
<RadioButton
|
||||
id="radio-1"
|
||||
value="standard"
|
||||
labelText="Standard Radio Button" />
|
||||
<RadioButton
|
||||
id="radio-2"
|
||||
value="default-selected"
|
||||
labelText="Default Selected Radio Button" />
|
||||
<RadioButton id="radio-3" value="blue" labelText="Standard Radio Button" />
|
||||
<RadioButton id="radio-4" value="disabled" labelText="Disabled Radio Button" disabled />
|
||||
<RadioButton
|
||||
id="radio-3"
|
||||
value="blue"
|
||||
labelText="Standard Radio Button" />
|
||||
<RadioButton
|
||||
id="radio-4"
|
||||
value="disabled"
|
||||
labelText="Disabled Radio Button"
|
||||
disabled />
|
||||
</RadioButtonGroup>
|
||||
</FormGroup>
|
||||
<FormGroup legendText="Search">
|
||||
<Search id="search-1" labelText="Search" placeholder="Search" />
|
||||
</FormGroup>
|
||||
<Select id="select-1" defaultValue="placeholder-item">
|
||||
<SelectItem disabled hidden value="placeholder-item" text="Choose an option" />
|
||||
<SelectItem
|
||||
disabled
|
||||
hidden
|
||||
value="placeholder-item"
|
||||
text="Choose an option" />
|
||||
<SelectItem value="option-1" text="Option 1" />
|
||||
<SelectItem value="option-2" text="Option 2" />
|
||||
<SelectItem value="option-3" text="Option 3" />
|
||||
</Select>
|
||||
<TextInput id="text-input-1" labelText="Text Input label" placeholder="Placeholder text" />
|
||||
<TextInput id="text-input-2" type="password" labelText="Password" required />
|
||||
<TextInput
|
||||
id="text-input-1"
|
||||
labelText="Text Input label"
|
||||
placeholder="Placeholder text" />
|
||||
<TextInput
|
||||
id="text-input-2"
|
||||
type="password"
|
||||
labelText="Password"
|
||||
required />
|
||||
<TextInput
|
||||
id="text-input-3"
|
||||
type="password"
|
||||
labelText="Password"
|
||||
invalidText="Your password must be at least 6 characters as well as contain at least one
|
||||
uppercase, one lowercase, and one number."
|
||||
invalidText="Your password must be at least 6 characters as well as
|
||||
contain at least one uppercase, one lowercase, and one number."
|
||||
required
|
||||
invalid />
|
||||
<TextArea
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import { withKnobs, text, boolean } from '@storybook/addon-knobs';
|
||||
import Component from './Form.Story.svelte';
|
||||
import { withKnobs, text, boolean } from "@storybook/addon-knobs";
|
||||
import Component from "./Form.Story.svelte";
|
||||
|
||||
export default { title: 'Form', decorators: [withKnobs] };
|
||||
export default { title: "Form", decorators: [withKnobs] };
|
||||
|
||||
export const Default = () => ({
|
||||
Component,
|
||||
props: {
|
||||
legendText: text('Text in <legend> (legendText)', 'Checkbox heading'),
|
||||
message: boolean('Show form requirement (message)', false),
|
||||
messageText: text('Form requirement text (messageText)', ''),
|
||||
invalid: boolean('Mark as invalid (invalid)', false)
|
||||
}
|
||||
legendText: text("Text in <legend> (legendText)", "Checkbox heading"),
|
||||
message: boolean("Show form requirement (message)", false),
|
||||
messageText: text("Form requirement text (messageText)", ""),
|
||||
invalid: boolean("Mark as invalid (invalid)", false),
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,18 +1,10 @@
|
|||
<script>
|
||||
let className = undefined;
|
||||
export { className as class };
|
||||
export let style = undefined;
|
||||
|
||||
import { cx } from '../../lib';
|
||||
</script>
|
||||
|
||||
<form
|
||||
class:bx--form={true}
|
||||
{...$$restProps}
|
||||
on:click
|
||||
on:mouseover
|
||||
on:mouseenter
|
||||
on:mouseleave
|
||||
on:submit|preventDefault
|
||||
class={cx('--form', className)}
|
||||
{style}>
|
||||
on:submit|preventDefault>
|
||||
<slot />
|
||||
</form>
|
||||
|
|
|
@ -1,3 +1 @@
|
|||
import Form from './Form.svelte';
|
||||
|
||||
export default Form;
|
||||
export { default as Form } from "./Form.svelte";
|
||||
|
|
|
@ -1,26 +1,21 @@
|
|||
<script>
|
||||
let className = undefined;
|
||||
export { className as class };
|
||||
export let legendText = "";
|
||||
export let invalid = false;
|
||||
export let legendText = '';
|
||||
export let message = false;
|
||||
export let messageText = '';
|
||||
export let style = undefined;
|
||||
|
||||
import { cx } from '../../lib';
|
||||
export let messageText = "";
|
||||
</script>
|
||||
|
||||
<fieldset
|
||||
data-invalid={invalid || undefined}
|
||||
class={cx('--fieldset', className)}
|
||||
class:bx--fieldset={true}
|
||||
{...$$restProps}
|
||||
on:click
|
||||
on:mouseover
|
||||
on:mouseenter
|
||||
on:mouseleave
|
||||
{style}>
|
||||
<legend class={cx('--label', className)}>{legendText}</legend>
|
||||
on:mouseleave>
|
||||
<legend class:bx--label={true}>{legendText}</legend>
|
||||
<slot />
|
||||
{#if message}
|
||||
<div class={cx('--form__requirements')}>{messageText}</div>
|
||||
<div class:bx--form__requirement={true}>{messageText}</div>
|
||||
{/if}
|
||||
</fieldset>
|
||||
|
|
|
@ -1,3 +1 @@
|
|||
import FormGroup from './FormGroup.svelte';
|
||||
|
||||
export default FormGroup;
|
||||
export { default as FormGroup } from "./FormGroup.svelte";
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import Layout from '../../internal/ui/Layout.svelte';
|
||||
import NumberInput from '../NumberInput';
|
||||
import FormItem from './FormItem.svelte';
|
||||
import Layout from "../../internal/ui/Layout.svelte";
|
||||
import { NumberInput } from "../NumberInput";
|
||||
import FormItem from "./FormItem.svelte";
|
||||
</script>
|
||||
|
||||
<Layout>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { withKnobs } from '@storybook/addon-knobs';
|
||||
import Component from './FormItem.Story.svelte';
|
||||
import { withKnobs } from "@storybook/addon-knobs";
|
||||
import Component from "./FormItem.Story.svelte";
|
||||
|
||||
export default { title: 'FormItem', decorators: [withKnobs] };
|
||||
export default { title: "FormItem", decorators: [withKnobs] };
|
||||
|
||||
export const Default = () => ({ Component });
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
<script>
|
||||
let className = undefined;
|
||||
export { className as class };
|
||||
export let style = undefined;
|
||||
|
||||
import { cx } from '../../lib';
|
||||
</script>
|
||||
|
||||
<div on:click on:mouseover on:mouseenter on:mouseleave class={cx('--form-item', className)} {style}>
|
||||
<div
|
||||
class:bx--form-item={true}
|
||||
{...$$restProps}
|
||||
on:click
|
||||
on:mouseover
|
||||
on:mouseenter
|
||||
on:mouseleave>
|
||||
<slot />
|
||||
</div>
|
||||
|
|
|
@ -1,3 +1 @@
|
|||
import FormItem from './FormItem.svelte';
|
||||
|
||||
export default FormItem;
|
||||
export { default as FormItem } from "./FormItem.svelte";
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<script>
|
||||
export let story = undefined;
|
||||
|
||||
import Layout from '../../internal/ui/Layout.svelte';
|
||||
import Tooltip from '../Tooltip';
|
||||
import FormLabel from './FormLabel.svelte';
|
||||
import Layout from "../../internal/ui/Layout.svelte";
|
||||
import { Tooltip } from "../Tooltip";
|
||||
import FormLabel from "./FormLabel.svelte";
|
||||
</script>
|
||||
|
||||
<Layout>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { withKnobs } from '@storybook/addon-knobs';
|
||||
import Component from './FormLabel.Story.svelte';
|
||||
import { withKnobs } from "@storybook/addon-knobs";
|
||||
import Component from "./FormLabel.Story.svelte";
|
||||
|
||||
export default { title: 'FormLabel', decorators: [withKnobs] };
|
||||
export default { title: "FormLabel", decorators: [withKnobs] };
|
||||
|
||||
export const Default = () => ({ Component });
|
||||
|
||||
export const WithTooltip = () => ({ Component, props: { story: 'tooltip' } });
|
||||
export const WithTooltip = () => ({ Component, props: { story: "tooltip" } });
|
||||
|
|
|
@ -1,19 +1,14 @@
|
|||
<script>
|
||||
let className = undefined;
|
||||
export { className as class };
|
||||
export let id = Math.random();
|
||||
export let style = undefined;
|
||||
|
||||
import { cx } from '../../lib';
|
||||
export let id = "ccs-" + Math.random().toString(36);
|
||||
</script>
|
||||
|
||||
<label
|
||||
class:bx--label={true}
|
||||
for={id}
|
||||
{...$$restProps}
|
||||
on:click
|
||||
on:mouseover
|
||||
on:mouseenter
|
||||
on:mouseleave
|
||||
for={id}
|
||||
class={cx('--label', className)}
|
||||
{style}>
|
||||
on:mouseleave>
|
||||
<slot />
|
||||
</label>
|
||||
|
|
|
@ -1,3 +1 @@
|
|||
import FormLabel from './FormLabel.svelte';
|
||||
|
||||
export default FormLabel;
|
||||
export { default as FormLabel } from "./FormLabel.svelte";
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue