mirror of
https://github.com/carbon-design-system/carbon-components-svelte.git
synced 2025-09-15 10:21:05 +00:00
chore: lift components folder
This commit is contained in:
parent
76df51674d
commit
2200b29b92
301 changed files with 57 additions and 76 deletions
157
src/ComposedModal/ComposedModal.Story.svelte
Normal file
157
src/ComposedModal/ComposedModal.Story.svelte
Normal file
|
@ -0,0 +1,157 @@
|
|||
<script>
|
||||
export let story = undefined;
|
||||
const { modalBody } = $$props;
|
||||
|
||||
import { Button } from "../Button";
|
||||
import ComposedModal from "./ComposedModal.svelte";
|
||||
import ModalHeader from "./ModalHeader.svelte";
|
||||
import ModalBody from "./ModalBody.svelte";
|
||||
import ModalFooter from "./ModalFooter.svelte";
|
||||
|
||||
$: open = false;
|
||||
</script>
|
||||
|
||||
{#if story === undefined}
|
||||
<ComposedModal {...$$props.composedModal}>
|
||||
<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>
|
||||
{#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.{' '}
|
||||
</p>
|
||||
{/if}
|
||||
</ModalBody>
|
||||
<ModalFooter {...$$props.modalFooter} />
|
||||
</ComposedModal>
|
||||
{/if}
|
||||
|
||||
{#if story === 'child nodes'}
|
||||
<ComposedModal {...$$props.composedModal}>
|
||||
<ModalHeader {...$$props.modalHeader}>
|
||||
<h1>Testing</h1>
|
||||
</ModalHeader>
|
||||
<ModalBody
|
||||
{...$$props.modalBody}
|
||||
aria-label={modalBody.hasScrollingContent ? 'Modal content' : undefined}>
|
||||
<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.{' '}
|
||||
</p>
|
||||
{/if}
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button kind="secondary">Cancel</Button>
|
||||
<Button kind={$$props.composedModal.danger ? 'danger' : 'primary'}>
|
||||
Primary
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ComposedModal>
|
||||
{/if}
|
||||
|
||||
{#if story === 'title'}
|
||||
<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." />
|
||||
<ModalBody {...$$props.modalBody} />
|
||||
<ModalFooter {...$$props.modalFooter} />
|
||||
</ComposedModal>
|
||||
{/if}
|
||||
|
||||
{#if story === 'trigger'}
|
||||
<div>
|
||||
<Button
|
||||
on:click={() => {
|
||||
open = true;
|
||||
}}>
|
||||
Launch composed modal
|
||||
</Button>
|
||||
</div>
|
||||
<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>
|
||||
{#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.{' '}
|
||||
</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.{' '}
|
||||
</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.{' '}
|
||||
</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.{' '}
|
||||
</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.{' '}
|
||||
</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.{' '}
|
||||
</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.{' '}
|
||||
</p>
|
||||
{/if}
|
||||
</ModalBody>
|
||||
<ModalFooter {...$$props.modalFooter} />
|
||||
</ComposedModal>
|
||||
{/if}
|
177
src/ComposedModal/ComposedModal.stories.js
Normal file
177
src/ComposedModal/ComposedModal.stories.js
Normal file
|
@ -0,0 +1,177 @@
|
|||
import { withKnobs, select, boolean, text } from "@storybook/addon-knobs";
|
||||
import Component from "./ComposedModal.Story.svelte";
|
||||
|
||||
export default { title: "ComposedModal", decorators: [withKnobs] };
|
||||
|
||||
const sizes = {
|
||||
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),
|
||||
selectorPrimaryFocus: text(
|
||||
"Primary focus element selector (selectorPrimaryFocus)",
|
||||
"[data-modal-primary-focus]"
|
||||
),
|
||||
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"
|
||||
),
|
||||
},
|
||||
modalBody: {
|
||||
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"
|
||||
),
|
||||
primaryButtonDisabled: boolean(
|
||||
"Primary button disabled (primaryButtonDisabled in <ModalFooter>)",
|
||||
false
|
||||
),
|
||||
secondaryButtonText: text(
|
||||
"Secondary button text (secondaryButtonText in <ModalFooter>)",
|
||||
""
|
||||
),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const ChildNodes = () => ({
|
||||
Component,
|
||||
props: {
|
||||
story: "child nodes",
|
||||
composedModal: {
|
||||
open: boolean("Open (open in <ComposedModal>)", true),
|
||||
danger: boolean("Danger mode (danger)", false),
|
||||
selectorPrimaryFocus: text(
|
||||
"Primary focus element selector (selectorPrimaryFocus)",
|
||||
"[data-modal-primary-focus]"
|
||||
),
|
||||
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"
|
||||
),
|
||||
},
|
||||
modalBody: {
|
||||
hasScrollingContent: boolean(
|
||||
"Modal contains scrollable content (hasScrollingContent)",
|
||||
true
|
||||
),
|
||||
"aria-label": text("ARIA label for content", "Example modal content"),
|
||||
},
|
||||
modalFooter: {},
|
||||
},
|
||||
});
|
||||
|
||||
export const TitleOnly = () => ({
|
||||
Component,
|
||||
props: {
|
||||
story: "title",
|
||||
composedModal: {
|
||||
open: boolean("Open (open in <ComposedModal>)", true),
|
||||
danger: boolean("Danger mode (danger)", false),
|
||||
selectorPrimaryFocus: text(
|
||||
"Primary focus element selector (selectorPrimaryFocus)",
|
||||
"[data-modal-primary-focus]"
|
||||
),
|
||||
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"
|
||||
),
|
||||
},
|
||||
modalBody: {
|
||||
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"
|
||||
),
|
||||
primaryButtonDisabled: boolean(
|
||||
"Primary button disabled (primaryButtonDisabled in <ModalFooter>)",
|
||||
false
|
||||
),
|
||||
secondaryButtonText: text(
|
||||
"Secondary button text (secondaryButtonText in <ModalFooter>)",
|
||||
""
|
||||
),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const Trigger = () => ({
|
||||
Component,
|
||||
props: {
|
||||
story: "trigger",
|
||||
composedModal: {
|
||||
open: boolean("Open (open in <ComposedModal>)", true),
|
||||
danger: boolean("Danger mode (danger)", false),
|
||||
selectorPrimaryFocus: text(
|
||||
"Primary focus element selector (selectorPrimaryFocus)",
|
||||
"[data-modal-primary-focus]"
|
||||
),
|
||||
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"
|
||||
),
|
||||
},
|
||||
modalBody: {
|
||||
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"
|
||||
),
|
||||
primaryButtonDisabled: boolean(
|
||||
"Primary button disabled (primaryButtonDisabled in <ModalFooter>)",
|
||||
false
|
||||
),
|
||||
secondaryButtonText: text(
|
||||
"Secondary button text (secondaryButtonText in <ModalFooter>)",
|
||||
""
|
||||
),
|
||||
},
|
||||
},
|
||||
});
|
98
src/ComposedModal/ComposedModal.svelte
Normal file
98
src/ComposedModal/ComposedModal.svelte
Normal file
|
@ -0,0 +1,98 @@
|
|||
<script>
|
||||
export let open = false;
|
||||
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";
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
let buttonRef = null;
|
||||
let innerModal = null;
|
||||
|
||||
setContext("ComposedModal", {
|
||||
closeModal: () => {
|
||||
open = false;
|
||||
},
|
||||
submit: () => {
|
||||
dispatch("submit");
|
||||
},
|
||||
declareRef: ref => {
|
||||
buttonRef = ref;
|
||||
}
|
||||
});
|
||||
|
||||
function focus(element) {
|
||||
const node =
|
||||
(element || innerModal).querySelector(selectorPrimaryFocus) || buttonRef;
|
||||
node.focus();
|
||||
}
|
||||
|
||||
$: opened = false;
|
||||
$: didOpen = open;
|
||||
|
||||
onMount(async () => {
|
||||
await tick();
|
||||
focus();
|
||||
|
||||
return () => {
|
||||
document.body.classList.remove("bx--body--with-modal-open");
|
||||
};
|
||||
});
|
||||
|
||||
afterUpdate(() => {
|
||||
if (opened) {
|
||||
if (!open) {
|
||||
opened = false;
|
||||
dispatch("close");
|
||||
document.body.classList.add("bx--body--with-modal-open");
|
||||
}
|
||||
} else if (open) {
|
||||
opened = true;
|
||||
dispatch("open");
|
||||
document.body.classList.remove("bx--body--with-modal-open");
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div
|
||||
bind:this={ref}
|
||||
role="presentation"
|
||||
tabindex="-1"
|
||||
class:bx--modal={true}
|
||||
class:is-visible={open}
|
||||
class:bx--modal--danger={danger}
|
||||
{...$$restProps}
|
||||
on:click
|
||||
on:click={({ target }) => {
|
||||
if (!innerModal.contains(target)) {
|
||||
open = false;
|
||||
}
|
||||
}}
|
||||
on:mouseover
|
||||
on:mouseenter
|
||||
on:mouseleave
|
||||
on:transitionend
|
||||
on:transitionend={({ currentTarget }) => {
|
||||
if (didOpen) {
|
||||
focus(currentTarget);
|
||||
didOpen = false;
|
||||
}
|
||||
}}>
|
||||
<div
|
||||
bind:this={innerModal}
|
||||
class:bx--modal-container={true}
|
||||
class="{size && `bx--modal-container--${size}`}
|
||||
{containerClass}">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
16
src/ComposedModal/ModalBody.svelte
Normal file
16
src/ComposedModal/ModalBody.svelte
Normal file
|
@ -0,0 +1,16 @@
|
|||
<script>
|
||||
export let hasForm = false;
|
||||
export let hasScrollingContent = false;
|
||||
</script>
|
||||
|
||||
<div
|
||||
tabindex={hasScrollingContent ? '0' : undefined}
|
||||
role={hasScrollingContent ? 'region' : undefined}
|
||||
class:bx--modal-content={true}
|
||||
class:bx--modal-content--with-form={hasForm}
|
||||
{...$$restProps}>
|
||||
<slot />
|
||||
</div>
|
||||
{#if hasScrollingContent}
|
||||
<div class:bx--modal-content--overflow-indicator={true} />
|
||||
{/if}
|
31
src/ComposedModal/ModalFooter.svelte
Normal file
31
src/ComposedModal/ModalFooter.svelte
Normal file
|
@ -0,0 +1,31 @@
|
|||
<script>
|
||||
export let primaryClass = undefined;
|
||||
export let primaryButtonText = "";
|
||||
export let primaryButtonDisabled = false;
|
||||
export let secondaryClass = undefined;
|
||||
export let secondaryButtonText = "";
|
||||
export let danger = false;
|
||||
|
||||
import { getContext } from "svelte";
|
||||
import { Button } from "../Button";
|
||||
|
||||
const { closeModal, submit } = getContext("ComposedModal");
|
||||
</script>
|
||||
|
||||
<div class:bx--modal-footer={true} {...$$restProps}>
|
||||
{#if secondaryButtonText}
|
||||
<Button kind="secondary" class={secondaryClass} on:click={closeModal}>
|
||||
{secondaryButtonText}
|
||||
</Button>
|
||||
{/if}
|
||||
{#if primaryButtonText}
|
||||
<Button
|
||||
kind={danger ? 'danger' : 'primary'}
|
||||
disabled={primaryButtonDisabled}
|
||||
class={primaryClass}
|
||||
on:click={submit}>
|
||||
{primaryButtonText}
|
||||
</Button>
|
||||
{/if}
|
||||
<slot />
|
||||
</div>
|
44
src/ComposedModal/ModalHeader.svelte
Normal file
44
src/ComposedModal/ModalHeader.svelte
Normal file
|
@ -0,0 +1,44 @@
|
|||
<script>
|
||||
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";
|
||||
|
||||
const { closeModal } = getContext("ComposedModal");
|
||||
</script>
|
||||
|
||||
<div class:bx--modal-header={true} {...$$restProps}>
|
||||
{#if label}
|
||||
<p
|
||||
class:bx--modal-header__label={true}
|
||||
class:bx--type-delta={true}
|
||||
class:labelClass>
|
||||
{label}
|
||||
</p>
|
||||
{/if}
|
||||
{#if title}
|
||||
<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:bx--modal-close={true}
|
||||
class:closeClass
|
||||
on:click
|
||||
on:click={closeModal}>
|
||||
<Close20 class="bx--modal-close__icon {closeIconClass}" />
|
||||
</button>
|
||||
</div>
|
4
src/ComposedModal/index.js
Normal file
4
src/ComposedModal/index.js
Normal file
|
@ -0,0 +1,4 @@
|
|||
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";
|
Loading…
Add table
Add a link
Reference in a new issue