feat(contained-list): add ContainedList, ContainedListItem (#1971)

This commit is contained in:
Eric Liu 2024-05-08 09:14:36 -07:00 committed by GitHub
commit b6df277647
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 484 additions and 2 deletions

View file

@ -1,6 +1,6 @@
# Component Index
> 169 components exported from carbon-components-svelte@1.0.0-next.1.
> 171 components exported from carbon-components-svelte@1.0.0-next.1.
## Components
@ -23,6 +23,8 @@
- [`Column`](#column)
- [`ComboBox`](#combobox)
- [`ComposedModal`](#composedmodal)
- [`ContainedList`](#containedlist)
- [`ContainedListItem`](#containedlistitem)
- [`Content`](#content)
- [`ContentSwitcher`](#contentswitcher)
- [`ContextMenu`](#contextmenu)
@ -723,6 +725,53 @@ export interface ComboBoxItem {
| close | dispatched | <code>null</code> |
| open | dispatched | <code>null</code> |
## `ContainedList`
### Props
| Prop name | Required | Kind | Reactive | Type | Default value | Description |
| :-------- | :------- | :--------------- | :------- | ----------------------------------------------------- | ------------------------------------------------ | ------------------------------------------------------- |
| kind | No | <code>let</code> | No | <code>"on-page" &#124; "disclosed"</code> | <code>"on-page"</code> | -- |
| labelText | No | <code>let</code> | No | <code>string</code> | <code>""</code> | Specify the label text |
| size | No | <code>let</code> | No | <code>"sm" &#124; "md" &#124; "lg" &#124; "xl"</code> | <code>"md"</code> | Specify the size of the list |
| inset | No | <code>let</code> | No | <code>boolean</code> | <code>false</code> | Set to `true` for lines between list items to be inset. |
| id | No | <code>let</code> | No | <code>string</code> | <code>"ccs-" + Math.random().toString(36)</code> | Set an id for the list |
### Slots
| Slot name | Default | Props | Fallback |
| :-------- | :------ | :---- | :----------------------- |
| -- | Yes | -- | -- |
| action | No | -- | -- |
| labelText | No | -- | <code>{labelText}</code> |
### Events
None.
## `ContainedListItem`
### Props
| Prop name | Required | Kind | Reactive | Type | Default value | Description |
| :---------- | :------- | :--------------- | :------- | --------------------------------------------------------- | ---------------------- | ------------------------------------------------------------------------------ |
| interactive | No | <code>let</code> | No | <code>boolean</code> | <code>false</code> | Set to `true` to render a `button` element instead of a `div` |
| disabled | No | <code>let</code> | No | <code>boolean</code> | <code>false</code> | Set to `true` to disable the list item. |
| icon | No | <code>let</code> | No | <code>typeof import("svelte").SvelteComponent<any></code> | <code>undefined</code> | Specify the icon to render<br />Icon is rendered to the left of the label text |
### Slots
| Slot name | Default | Props | Fallback |
| :-------- | :------ | :---- | :------- |
| -- | Yes | -- | -- |
| action | No | -- | -- |
### Events
| Event name | Type | Detail |
| :--------- | :-------- | :----- |
| click | forwarded | -- |
## `Content`
### Props

View file

@ -1,5 +1,5 @@
{
"total": 169,
"total": 171,
"components": [
{
"moduleName": "Accordion",
@ -1847,6 +1847,134 @@
"typedefs": [],
"rest_props": { "type": "Element", "name": "div" }
},
{
"moduleName": "ContainedList",
"filePath": "src/ContainedList/ContainedList.svelte",
"props": [
{
"name": "kind",
"kind": "let",
"type": "\"on-page\" | \"disclosed\"",
"value": "\"on-page\"",
"isFunction": false,
"isFunctionDeclaration": false,
"isRequired": false,
"constant": false,
"reactive": false
},
{
"name": "labelText",
"kind": "let",
"description": "Specify the label text",
"type": "string",
"value": "\"\"",
"isFunction": false,
"isFunctionDeclaration": false,
"isRequired": false,
"constant": false,
"reactive": false
},
{
"name": "size",
"kind": "let",
"description": "Specify the size of the list",
"type": "\"sm\" | \"md\" | \"lg\" | \"xl\"",
"value": "\"md\"",
"isFunction": false,
"isFunctionDeclaration": false,
"isRequired": false,
"constant": false,
"reactive": false
},
{
"name": "inset",
"kind": "let",
"description": "Set to `true` for lines between list items to be inset.",
"type": "boolean",
"value": "false",
"isFunction": false,
"isFunctionDeclaration": false,
"isRequired": false,
"constant": false,
"reactive": false
},
{
"name": "id",
"kind": "let",
"description": "Set an id for the list",
"type": "string",
"value": "\"ccs-\" + Math.random().toString(36)",
"isFunction": false,
"isFunctionDeclaration": false,
"isRequired": false,
"constant": false,
"reactive": false
}
],
"moduleExports": [],
"slots": [
{ "name": "__default__", "default": true, "slot_props": "{}" },
{ "name": "action", "default": false, "slot_props": "{}" },
{
"name": "labelText",
"default": false,
"fallback": "{labelText}",
"slot_props": "{}"
}
],
"events": [],
"typedefs": []
},
{
"moduleName": "ContainedListItem",
"filePath": "src/ContainedList/ContainedListItem.svelte",
"props": [
{
"name": "interactive",
"kind": "let",
"description": "Set to `true` to render a `button` element instead of a `div`",
"type": "boolean",
"value": "false",
"isFunction": false,
"isFunctionDeclaration": false,
"isRequired": false,
"constant": false,
"reactive": false
},
{
"name": "disabled",
"kind": "let",
"description": "Set to `true` to disable the list item.",
"type": "boolean",
"value": "false",
"isFunction": false,
"isFunctionDeclaration": false,
"isRequired": false,
"constant": false,
"reactive": false
},
{
"name": "icon",
"kind": "let",
"description": "Specify the icon to render\nIcon is rendered to the left of the label text",
"type": "typeof import(\"svelte\").SvelteComponent<any>",
"isFunction": false,
"isFunctionDeclaration": false,
"isRequired": false,
"constant": false,
"reactive": false
}
],
"moduleExports": [],
"slots": [
{ "name": "__default__", "default": true, "slot_props": "{}" },
{ "name": "action", "default": false, "slot_props": "{}" }
],
"events": [
{ "type": "forwarded", "name": "click", "element": "svelte:element" }
],
"typedefs": []
},
{
"moduleName": "Content",
"filePath": "src/UIShell/Content.svelte",

View file

@ -0,0 +1,122 @@
<script>
import { Button, Search, ContainedList, ContainedListItem } from "carbon-components-svelte";
import Add from "carbon-icons-svelte/lib/Add.svelte";
import Close from "carbon-icons-svelte/lib/Close.svelte";
import Preview from "../../components/Preview.svelte";
</script>
## Default
The `ContainedList` component is used to display a list of items in a container.
It uses a `md` size by default.
<ContainedList labelText="List title">
<ContainedListItem>Item 1</ContainedListItem>
<ContainedListItem>Item 2</ContainedListItem>
<ContainedListItem>Item 3</ContainedListItem>
</ContainedList>
## Small size
<ContainedList labelText="List title" size="sm">
<ContainedListItem>Item 1</ContainedListItem>
<ContainedListItem>Item 2</ContainedListItem>
<ContainedListItem>Item 3</ContainedListItem>
</ContainedList>
## Large size
<ContainedList labelText="List title" size="lg">
<ContainedListItem>Item 1</ContainedListItem>
<ContainedListItem>Item 2</ContainedListItem>
<ContainedListItem>Item 3</ContainedListItem>
</ContainedList>
## Extra-large size
<ContainedList labelText="List title" size="xl">
<ContainedListItem>Item 1</ContainedListItem>
<ContainedListItem>Item 2</ContainedListItem>
<ContainedListItem>Item 3</ContainedListItem>
</ContainedList>
## Inset items
<ContainedList labelText="List title" inset>
<ContainedListItem>Item 1</ContainedListItem>
<ContainedListItem>Item 2</ContainedListItem>
<ContainedListItem>Item 3</ContainedListItem>
</ContainedList>
## Disclosed
Set `kind="disclosed"` for the list title to be sticky positioned.
When scrolling, the list title will stick to the top of the container.
<div style:height="200px" style:overflow-y="auto">
<ContainedList labelText="List title 1" kind="disclosed">
{#each Array.from({ length: 6 }) as i}
<ContainedListItem>Item</ContainedListItem>
{/each}
</ContainedList>
<ContainedList labelText="List title 2" kind="disclosed">
{#each Array.from({ length: 6 }) as i}
<ContainedListItem>Item</ContainedListItem>
{/each}
</ContainedList>
</div>
## With icons
<ContainedList labelText="List title">
<ContainedListItem icon={Add}>Item 1</ContainedListItem>
<ContainedListItem icon={Add}>Item 2</ContainedListItem>
<ContainedListItem icon={Add}>Item 3</ContainedListItem>
</ContainedList>
## With expandable search
Use the `action` slot to add an expandable search.
<ContainedList labelText="List title">
<Search slot="action" expandable />
<ContainedListItem>Item 1</ContainedListItem>
<ContainedListItem>Item 2</ContainedListItem>
<ContainedListItem>Item 3</ContainedListItem>
</ContainedList>
## Interactive items
Set `interactive` to make the items render as a `button`.
<ContainedList labelText="List title">
<ContainedListItem interactive on:click={() => console.log('click')}>
Item 1
</ContainedListItem>
<ContainedListItem interactive disabled>Item 2</ContainedListItem>
<ContainedListItem interactive>Item 3</ContainedListItem>
</ContainedList>
## Interactive items with actions
Similarly to `ContainedList`, you can add an action to `ContainedListItem` using the `action` slot.
<ContainedList labelText="List title">
<ContainedListItem interactive on:click={() => console.log('click')}>
Item 1
<Button
slot="action"
kind="ghost"
iconDescription="Dismiss"
icon={Close}
on:click={() => console.log('click close')}
/>
</ContainedListItem>
<ContainedListItem interactive disabled>Item 2</ContainedListItem>
<ContainedListItem interactive>
Item 3
</ContainedListItem>
</ContainedList>

View file

@ -0,0 +1,54 @@
<script>
// @ts-check
/** @type {"on-page" | "disclosed"} */
export let kind = "on-page";
/** Specify the label text */
export let labelText = "";
/**
* Specify the size of the list
* @type {"sm" | "md" | "lg" | "xl"}
*/
export let size = "md";
/** Set to `true` for lines between list items to be inset. */
export let inset = false;
/** Set an id for the list */
export let id = "ccs-" + Math.random().toString(36);
$: labelId = `label-${id}`;
</script>
<div
class:bx--contained-list="{true}"
class:bx--contained-list--inset-rulers="{inset}"
class:bx--contained-list--sm="{size === 'sm'}"
class:bx--contained-list--md="{size === 'md'}"
class:bx--contained-list--lg="{size === 'lg'}"
class:bx--contained-list--xl="{size === 'xl'}"
class:bx--layout--size-sm="{size === 'sm'}"
class:bx--layout--size-md="{size === 'md'}"
class:bx--layout--size-lg="{size === 'lg'}"
class:bx--layout--size-xl="{size === 'xl'}"
class:bx--contained-list--on-page="{kind === 'on-page'}"
class:bx--contained-list--disclosed="{kind === 'disclosed'}"
>
<div class:bx--contained-list__header="{true}">
<div id="{labelId}" class:bx--contained-list__label="{true}">
<slot name="labelText">
{labelText}
</slot>
</div>
{#if $$slots.action}
<div class:bx--contained-list__action="{true}">
<slot name="action" />
</div>
{/if}
</div>
<ul role="list" aria-labelledby="{labelId}">
<slot />
</ul>
</div>

View file

@ -0,0 +1,49 @@
<script>
// @ts-check
/** Set to `true` to render a `button` element instead of a `div` */
export let interactive = false;
/** Set to `true` to disable the list item. */
export let disabled = false;
/**
* Specify the icon to render
* Icon is rendered to the left of the label text
* @type {typeof import("svelte").SvelteComponent<any>}
*/
export let icon = undefined;
$: tag = interactive ? "button" : "div";
$: props = {
type: interactive ? "button" : undefined,
disabled: interactive ? disabled : undefined,
};
</script>
<li
class:bx--contained-list-item="{true}"
class:bx--contained-list-item--clickable="{interactive}"
class:bx--contained-list-item--with-icon="{icon}"
class:bx--contained-list-item--with-action="{$$slots.action}"
>
<!-- svelte-ignore a11y-no-static-element-interactions -->
<svelte:element
this="{tag}"
{...props}
on:click
class:bx--contained-list-item__content="{true}"
>
{#if icon}
<div class:bx--contained-list-item__icon="{true}">
<svelte:component this="{icon}" />
</div>
{/if}
<slot />
</svelte:element>
{#if $$slots.action}
<div class:bx--contained-list__action="{true}">
<slot name="action" />
</div>
{/if}
</li>

View file

@ -0,0 +1,2 @@
export { default as ContainedList } from "./ContainedList.svelte";
export { default as ContainedListItem } from "./ContainedListItem.svelte";

View file

@ -22,6 +22,7 @@ export {
ModalBody,
ModalFooter,
} from "./ComposedModal";
export { ContainedList, ContainedListItem } from "./ContainedList";
export { CodeSnippet, CodeSnippetSkeleton } from "./CodeSnippet";
export {
DataTable,

View file

@ -0,0 +1,9 @@
<script lang="ts">
import { ContainedList, ContainedListItem } from "../types";
</script>
<ContainedList labelText="Title" kind="on-page">
<div slot="labelText">Title</div>
<ContainedListItem interactive disabled on:click>Item 1</ContainedListItem>
<svelte:fragment slot="action" />
</ContainedList>

View file

@ -0,0 +1,38 @@
import type { SvelteComponentTyped } from "svelte";
export interface ContainedListProps {
/**
* @default "on-page"
*/
kind?: "on-page" | "disclosed";
/**
* Specify the label text
* @default ""
*/
labelText?: string;
/**
* Specify the size of the list
* @default "md"
*/
size?: "sm" | "md" | "lg" | "xl";
/**
* Set to `true` for lines between list items to be inset.
* @default false
*/
inset?: boolean;
/**
* Set an id for the list
* @default "ccs-" + Math.random().toString(36)
*/
id?: string;
}
export default class ContainedList extends SvelteComponentTyped<
ContainedListProps,
Record<string, any>,
{ default: {}; action: {}; labelText: {} }
> {}

View file

@ -0,0 +1,28 @@
import type { SvelteComponentTyped } from "svelte";
export interface ContainedListItemProps {
/**
* Set to `true` to render a `button` element instead of a `div`
* @default false
*/
interactive?: boolean;
/**
* Set to `true` to disable the list item.
* @default false
*/
disabled?: boolean;
/**
* Specify the icon to render
* Icon is rendered to the left of the label text
* @default undefined
*/
icon?: typeof import("svelte").SvelteComponent<any>;
}
export default class ContainedListItem extends SvelteComponentTyped<
ContainedListItemProps,
{ click: WindowEventMap["click"] },
{ default: {}; action: {} }
> {}

2
types/index.d.ts vendored
View file

@ -26,6 +26,8 @@ export { default as ComposedModal } from "./ComposedModal/ComposedModal.svelte";
export { default as ModalHeader } from "./ComposedModal/ModalHeader.svelte";
export { default as ModalBody } from "./ComposedModal/ModalBody.svelte";
export { default as ModalFooter } from "./ComposedModal/ModalFooter.svelte";
export { default as ContainedList } from "./ContainedList/ContainedList.svelte";
export { default as ContainedListItem } from "./ContainedList/ContainedListItem.svelte";
export { default as CodeSnippet } from "./CodeSnippet/CodeSnippet.svelte";
export { default as CodeSnippetSkeleton } from "./CodeSnippet/CodeSnippetSkeleton.svelte";
export { default as DataTable } from "./DataTable/DataTable.svelte";