From 66c3de9c3b35cdd1d5aaf136567a2858f855a3e6 Mon Sep 17 00:00:00 2001 From: Cory LaViska Date: Wed, 22 Jul 2020 16:02:49 -0400 Subject: [PATCH] Add card component --- docs/_sidebar.md | 1 + docs/components/card.md | 151 ++++++++++++++++++++++++++++++++++ src/components.d.ts | 13 +++ src/components/card/card.scss | 65 +++++++++++++++ src/components/card/card.tsx | 76 +++++++++++++++++ 5 files changed, 306 insertions(+) create mode 100644 docs/components/card.md create mode 100644 src/components/card/card.scss create mode 100644 src/components/card/card.tsx diff --git a/docs/_sidebar.md b/docs/_sidebar.md index d55eecb4..86dd8f06 100644 --- a/docs/_sidebar.md +++ b/docs/_sidebar.md @@ -12,6 +12,7 @@ - [Avatar](/components/avatar.md) - [Badge](/components/badge.md) - [Button](/components/button.md) + - [Card](/components/card.md) - [Checkbox](/components/checkbox.md) - [Color Picker](/components/color-picker.md) - [Details](/components/details.md) diff --git a/docs/components/card.md b/docs/components/card.md new file mode 100644 index 00000000..3c4278d1 --- /dev/null +++ b/docs/components/card.md @@ -0,0 +1,151 @@ +# Card + +[component-header:sl-card] + +Cards can be used to group related subjects in a container. + +```html preview + + A kitten sits patiently between a terracotta pot and decorative grasses. + + Mittens
+ This kitten is as cute as he is playful. Bring him home today!
+ 6 weeks old + +
+ More Info + +
+
+ + +``` + +## Examples + +## Basic Card + +Basic cards aren't very exciting, but they can display any content you want them to. + +```html preview + + This is just a basic card. No image, no header, and no footer. Just your content. + + + +``` + +## Card with Header + +Headers can be used to display titles and other card options. + +```html preview + +
+ Header Title + + + + + Edit + + + Rename + Duplicate + + Delete + + +
+ + This card has a header. You can put all sorts of things in it! +
+ + +``` + +## Card with Footer + +Footers can be used to display actions, summaries, or other relevant content. + +```html preview + + This card has a footer. You can put all sorts of things in it! + +
+ + Preview +
+
+ + +``` + +## Images + +Cards accept an `image` slot. The image is displayed atop the card and stretches to fit. + +```html preview + + A kitten walks towards camera on top of pallet. + This is a kitten, but not just any kitten. This kitten likes walking along pallets. + + + +``` + +[component-metadata:sl-card] diff --git a/src/components.d.ts b/src/components.d.ts index 0682cb36..b75392e8 100644 --- a/src/components.d.ts +++ b/src/components.d.ts @@ -106,6 +106,8 @@ export namespace Components { */ "value": string; } + interface SlCard { + } interface SlCheckbox { /** * Set to true to draw the checkbox in a checked state. @@ -904,6 +906,12 @@ declare global { prototype: HTMLSlButtonElement; new (): HTMLSlButtonElement; }; + interface HTMLSlCardElement extends Components.SlCard, HTMLStencilElement { + } + var HTMLSlCardElement: { + prototype: HTMLSlCardElement; + new (): HTMLSlCardElement; + }; interface HTMLSlCheckboxElement extends Components.SlCheckbox, HTMLStencilElement { } var HTMLSlCheckboxElement: { @@ -1071,6 +1079,7 @@ declare global { "sl-avatar": HTMLSlAvatarElement; "sl-badge": HTMLSlBadgeElement; "sl-button": HTMLSlButtonElement; + "sl-card": HTMLSlCardElement; "sl-checkbox": HTMLSlCheckboxElement; "sl-color-picker": HTMLSlColorPickerElement; "sl-details": HTMLSlDetailsElement; @@ -1209,6 +1218,8 @@ declare namespace LocalJSX { */ "value"?: string; } + interface SlCard { + } interface SlCheckbox { /** * Set to true to draw the checkbox in a checked state. @@ -2074,6 +2085,7 @@ declare namespace LocalJSX { "sl-avatar": SlAvatar; "sl-badge": SlBadge; "sl-button": SlButton; + "sl-card": SlCard; "sl-checkbox": SlCheckbox; "sl-color-picker": SlColorPicker; "sl-details": SlDetails; @@ -2111,6 +2123,7 @@ declare module "@stencil/core" { "sl-avatar": LocalJSX.SlAvatar & JSXBase.HTMLAttributes; "sl-badge": LocalJSX.SlBadge & JSXBase.HTMLAttributes; "sl-button": LocalJSX.SlButton & JSXBase.HTMLAttributes; + "sl-card": LocalJSX.SlCard & JSXBase.HTMLAttributes; "sl-checkbox": LocalJSX.SlCheckbox & JSXBase.HTMLAttributes; "sl-color-picker": LocalJSX.SlColorPicker & JSXBase.HTMLAttributes; "sl-details": LocalJSX.SlDetails & JSXBase.HTMLAttributes; diff --git a/src/components/card/card.scss b/src/components/card/card.scss new file mode 100644 index 00000000..45b8eb5f --- /dev/null +++ b/src/components/card/card.scss @@ -0,0 +1,65 @@ +@import 'component'; + +/** + * @prop --border-color: The card's border color, including borders that occur inside the card. + * @prop --border-radius: The border radius for card edges. + * @prop --border-width: The width of card borders. + * @prop --padding: The padding to use for card sections. + */ +:host { + --border-color: var(--sl-color-gray-90); + --border-radius: var(--sl-border-radius-medium); + --border-width: 1px; + --padding: var(--sl-spacing-large); + + display: inline-block; +} + +.card { + display: flex; + flex-direction: column; + background-color: var(--sl-color-white); + box-shadow: var(--sl-shadow-x-small); + border: solid var(--border-width) var(--border-color); + border-radius: var(--border-radius); +} + +.card__image { + border-top-left-radius: calc(var(--border-radius) - var(--border-width)); + border-top-right-radius: calc(var(--border-radius) - var(--border-width)); + border-top-left-radius: var(--border-radius); + border-top-right-radius: var(--border-radius); + margin: calc(-1 * var(--border-width)); + overflow: hidden; + + ::slotted(img) { + display: block; + width: 100%; + } +} + +.card:not(.card--has-image) .card__image { + display: none; +} + +.card__header { + border-bottom: solid var(--border-width) var(--border-color); + padding: calc(var(--padding) / 2) var(--padding); +} + +.card:not(.card--has-header) .card__header { + display: none; +} + +.card__body { + padding: var(--padding); +} + +.card--has-footer .card__footer { + border-top: solid var(--border-width) var(--border-color); + padding: var(--padding); +} + +.card:not(.card--has-footer) .card__footer { + display: none; +} diff --git a/src/components/card/card.tsx b/src/components/card/card.tsx new file mode 100644 index 00000000..ecdd88f0 --- /dev/null +++ b/src/components/card/card.tsx @@ -0,0 +1,76 @@ +import { Component, Element, State, h } from '@stencil/core'; +import { hasSlot } from '../../utilities/slot'; + +/** + * @since 2.0 + * @status stable + * + * @slot - The card's body. + * @slot header - The card's header. + * @slot footer - The card's footer. + * @slot image - The card's image. + * + * @part base - The component's base wrapper. + * @part image - The card's image, if present. + * @part header - The card's header, if present. + * @part body - The card's body. + * @part footer - The card's footer, if present. + */ + +@Component({ + tag: 'sl-card', + styleUrl: 'card.scss', + shadow: true +}) +export class Card { + @Element() host: HTMLSlCardElement; + + @State() hasFooter = false; + @State() hasImage = false; + @State() hasHeader = false; + + componentWillLoad() { + this.updateSlots(); + this.host.shadowRoot.addEventListener('slotchange', this.updateSlots); + } + + componentDidUnload() { + this.host.shadowRoot.removeEventListener('slotchange', this.updateSlots); + } + + updateSlots() { + this.hasFooter = hasSlot(this.host, 'footer'); + this.hasImage = hasSlot(this.host, 'image'); + this.hasHeader = hasSlot(this.host, 'header'); + } + + render() { + return ( +
+
+ +
+ +
+ +
+ +
+ +
+ + +
+ ); + } +}