mirror of
https://github.com/bitwarden/browser
synced 2025-12-06 00:13:28 +00:00
[CL-427] Add skeleton loading components to the CL (#16728)
This commit is contained in:
@@ -25,6 +25,11 @@ export const formatArgsForCodeSnippet = <ComponentType extends Record<string, an
|
|||||||
const formattedArray = value.map((v) => `'${v}'`).join(", ");
|
const formattedArray = value.map((v) => `'${v}'`).join(", ");
|
||||||
return `[${key}]="[${formattedArray}]"`;
|
return `[${key}]="[${formattedArray}]"`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (typeof value === "number") {
|
||||||
|
return `[${key}]="${value}"`;
|
||||||
|
}
|
||||||
|
|
||||||
return `${key}="${value}"`;
|
return `${key}="${value}"`;
|
||||||
})
|
})
|
||||||
.join(" ");
|
.join(" ");
|
||||||
|
|||||||
@@ -29,6 +29,9 @@ import {
|
|||||||
SearchModule,
|
SearchModule,
|
||||||
SectionComponent,
|
SectionComponent,
|
||||||
ScrollLayoutDirective,
|
ScrollLayoutDirective,
|
||||||
|
SkeletonComponent,
|
||||||
|
SkeletonTextComponent,
|
||||||
|
SkeletonGroupComponent,
|
||||||
} from "@bitwarden/components";
|
} from "@bitwarden/components";
|
||||||
|
|
||||||
import { PopupRouterCacheService } from "../view-cache/popup-router-cache.service";
|
import { PopupRouterCacheService } from "../view-cache/popup-router-cache.service";
|
||||||
@@ -335,6 +338,9 @@ export default {
|
|||||||
SectionComponent,
|
SectionComponent,
|
||||||
IconButtonModule,
|
IconButtonModule,
|
||||||
BadgeModule,
|
BadgeModule,
|
||||||
|
SkeletonComponent,
|
||||||
|
SkeletonTextComponent,
|
||||||
|
SkeletonGroupComponent,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{
|
||||||
@@ -594,6 +600,34 @@ export const Loading: Story = {
|
|||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const SkeletonLoading: Story = {
|
||||||
|
render: (args) => ({
|
||||||
|
props: { ...args, data: Array(8) },
|
||||||
|
template: /* HTML */ `
|
||||||
|
<extension-container>
|
||||||
|
<popup-tab-navigation>
|
||||||
|
<popup-page>
|
||||||
|
<popup-header slot="header" pageTitle="Page Header"></popup-header>
|
||||||
|
<div>
|
||||||
|
<div class="tw-sr-only" role="status">Loading...</div>
|
||||||
|
<div class="tw-flex tw-flex-col tw-gap-4">
|
||||||
|
<bit-skeleton-text class="tw-w-1/3"></bit-skeleton-text>
|
||||||
|
@for (num of data; track $index) {
|
||||||
|
<bit-skeleton-group>
|
||||||
|
<bit-skeleton class="tw-size-8" slot="start"></bit-skeleton>
|
||||||
|
<bit-skeleton-text [lines]="2" class="tw-w-1/2"></bit-skeleton-text>
|
||||||
|
</bit-skeleton-group>
|
||||||
|
<bit-skeleton class="tw-w-full tw-h-[1px]"></bit-skeleton>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</popup-page>
|
||||||
|
</popup-tab-navigation>
|
||||||
|
</extension-container>
|
||||||
|
`,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
export const TransparentHeader: Story = {
|
export const TransparentHeader: Story = {
|
||||||
render: (args) => ({
|
render: (args) => ({
|
||||||
props: args,
|
props: args,
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ export * from "./search";
|
|||||||
export * from "./section";
|
export * from "./section";
|
||||||
export * from "./select";
|
export * from "./select";
|
||||||
export * from "./shared/compact-mode.service";
|
export * from "./shared/compact-mode.service";
|
||||||
|
export * from "./skeleton";
|
||||||
export * from "./table";
|
export * from "./table";
|
||||||
export * from "./tabs";
|
export * from "./tabs";
|
||||||
export * from "./toast";
|
export * from "./toast";
|
||||||
|
|||||||
3
libs/components/src/skeleton/index.ts
Normal file
3
libs/components/src/skeleton/index.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export * from "./skeleton.component";
|
||||||
|
export * from "./skeleton-text.component";
|
||||||
|
export * from "./skeleton-group.component";
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<div class="tw-flex tw-flex-row tw-justify-between tw-gap-2">
|
||||||
|
<div class="tw-flex tw-gap-2 tw-w-full">
|
||||||
|
<ng-content select="[slot=start]"></ng-content>
|
||||||
|
<ng-content></ng-content>
|
||||||
|
</div>
|
||||||
|
<ng-content select="[slot=end]"></ng-content>
|
||||||
|
</div>
|
||||||
18
libs/components/src/skeleton/skeleton-group.component.ts
Normal file
18
libs/components/src/skeleton/skeleton-group.component.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { CommonModule } from "@angular/common";
|
||||||
|
import { Component } from "@angular/core";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Arranges skeleton loaders into a pre-arranged group that mimics the table and item components.
|
||||||
|
*
|
||||||
|
* Pass skeleton loaders into the start, default, and end content slots. The content within each slot
|
||||||
|
* is fully customizable.
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: "bit-skeleton-group",
|
||||||
|
templateUrl: "./skeleton-group.component.html",
|
||||||
|
imports: [CommonModule],
|
||||||
|
host: {
|
||||||
|
class: "tw-block",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
export class SkeletonGroupComponent {}
|
||||||
73
libs/components/src/skeleton/skeleton-group.stories.ts
Normal file
73
libs/components/src/skeleton/skeleton-group.stories.ts
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import { Meta, moduleMetadata, StoryObj } from "@storybook/angular";
|
||||||
|
|
||||||
|
import { SharedModule } from "../shared/shared.module";
|
||||||
|
|
||||||
|
import { SkeletonGroupComponent } from "./skeleton-group.component";
|
||||||
|
import { SkeletonTextComponent } from "./skeleton-text.component";
|
||||||
|
import { SkeletonComponent } from "./skeleton.component";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: "Component Library/Skeleton/Skeleton Group",
|
||||||
|
component: SkeletonGroupComponent,
|
||||||
|
decorators: [
|
||||||
|
moduleMetadata({
|
||||||
|
imports: [SharedModule, SkeletonTextComponent, SkeletonComponent],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
} as Meta<SkeletonGroupComponent>;
|
||||||
|
|
||||||
|
type Story = StoryObj<SkeletonGroupComponent>;
|
||||||
|
|
||||||
|
export const Default: Story = {
|
||||||
|
render: (args) => ({
|
||||||
|
props: args,
|
||||||
|
template: /*html*/ `
|
||||||
|
<bit-skeleton-group>
|
||||||
|
<bit-skeleton class="tw-size-8" slot="start"></bit-skeleton>
|
||||||
|
<bit-skeleton-text [lines]="2" class="tw-w-1/2"></bit-skeleton-text>
|
||||||
|
<bit-skeleton-text [lines]="1" slot="end" class="tw-w-1/4"></bit-skeleton-text>
|
||||||
|
</bit-skeleton-group>
|
||||||
|
`,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const NoEndSlot: Story = {
|
||||||
|
render: (args) => ({
|
||||||
|
props: args,
|
||||||
|
template: /*html*/ `
|
||||||
|
<bit-skeleton-group>
|
||||||
|
<bit-skeleton class="tw-size-8" slot="start"></bit-skeleton>
|
||||||
|
<bit-skeleton-text [lines]="2" class="tw-w-1/2"></bit-skeleton-text>
|
||||||
|
</bit-skeleton-group>
|
||||||
|
`,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const NoStartSlot: Story = {
|
||||||
|
render: (args) => ({
|
||||||
|
props: args,
|
||||||
|
template: /*html*/ `
|
||||||
|
<bit-skeleton-group>
|
||||||
|
<bit-skeleton-text [lines]="2" class="tw-w-1/2"></bit-skeleton-text>
|
||||||
|
<bit-skeleton-text [lines]="1" slot="end" class="tw-w-1/4"></bit-skeleton-text>
|
||||||
|
</bit-skeleton-group>
|
||||||
|
`,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CustomContent: Story = {
|
||||||
|
render: (args) => ({
|
||||||
|
props: args,
|
||||||
|
template: /*html*/ `
|
||||||
|
<bit-skeleton-group>
|
||||||
|
<bit-skeleton class="tw-size-12" slot="start" edgeShape="circle"></bit-skeleton>
|
||||||
|
<bit-skeleton-text [lines]="3" class="tw-w-full"></bit-skeleton-text>
|
||||||
|
<div slot="end" class="tw-flex tw-flex-row tw-gap-1">
|
||||||
|
<bit-skeleton class="tw-size-4" slot="start"></bit-skeleton>
|
||||||
|
<bit-skeleton class="tw-size-4" slot="start"></bit-skeleton>
|
||||||
|
<bit-skeleton class="tw-size-4" slot="start"></bit-skeleton>
|
||||||
|
</div>
|
||||||
|
</bit-skeleton-group>
|
||||||
|
`,
|
||||||
|
}),
|
||||||
|
};
|
||||||
11
libs/components/src/skeleton/skeleton-text.component.html
Normal file
11
libs/components/src/skeleton/skeleton-text.component.html
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<div class="tw-w-full tw-flex tw-flex-col tw-gap-2">
|
||||||
|
@for (line of this.linesArray(); track $index; let last = $last, first = $first) {
|
||||||
|
<bit-skeleton
|
||||||
|
class="tw-h-3"
|
||||||
|
[ngClass]="{
|
||||||
|
'tw-w-full': first || !last,
|
||||||
|
'tw-w-1/3': !first && last,
|
||||||
|
}"
|
||||||
|
></bit-skeleton>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
31
libs/components/src/skeleton/skeleton-text.component.ts
Normal file
31
libs/components/src/skeleton/skeleton-text.component.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { CommonModule } from "@angular/common";
|
||||||
|
import { Component, computed, input } from "@angular/core";
|
||||||
|
|
||||||
|
import { SkeletonComponent } from "./skeleton.component";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specific skeleton component used to represent lines of text. It uses the `bit-skeleton`
|
||||||
|
* under the hood.
|
||||||
|
*
|
||||||
|
* Customize the number of lines represented with the `lines` input. Customize the width
|
||||||
|
* by applying a class to the `bit-skeleton-text` element (i.e. `tw-w-1/2`).
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: "bit-skeleton-text",
|
||||||
|
templateUrl: "./skeleton-text.component.html",
|
||||||
|
imports: [CommonModule, SkeletonComponent],
|
||||||
|
host: {
|
||||||
|
class: "tw-block",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
export class SkeletonTextComponent {
|
||||||
|
/**
|
||||||
|
* The number of text lines to display
|
||||||
|
*/
|
||||||
|
readonly lines = input<number>(1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array-transformed version of the `lines` to loop over
|
||||||
|
*/
|
||||||
|
protected linesArray = computed(() => [...Array(this.lines()).keys()]);
|
||||||
|
}
|
||||||
48
libs/components/src/skeleton/skeleton-text.stories.ts
Normal file
48
libs/components/src/skeleton/skeleton-text.stories.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import { Meta, moduleMetadata, StoryObj } from "@storybook/angular";
|
||||||
|
|
||||||
|
import { SharedModule } from "../shared/shared.module";
|
||||||
|
|
||||||
|
import { SkeletonTextComponent } from "./skeleton-text.component";
|
||||||
|
|
||||||
|
import { formatArgsForCodeSnippet } from ".storybook/format-args-for-code-snippet";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: "Component Library/Skeleton/Skeleton Text",
|
||||||
|
component: SkeletonTextComponent,
|
||||||
|
decorators: [
|
||||||
|
moduleMetadata({
|
||||||
|
imports: [SharedModule],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
args: {
|
||||||
|
lines: 1,
|
||||||
|
},
|
||||||
|
argTypes: {
|
||||||
|
lines: {
|
||||||
|
control: { type: "number", min: 1 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as Meta<SkeletonTextComponent>;
|
||||||
|
|
||||||
|
type Story = StoryObj<SkeletonTextComponent>;
|
||||||
|
|
||||||
|
export const Text: Story = {
|
||||||
|
render: (args) => ({
|
||||||
|
props: args,
|
||||||
|
template: /*html*/ `
|
||||||
|
<bit-skeleton-text ${formatArgsForCodeSnippet<SkeletonTextComponent>(args)}></bit-skeleton-text>
|
||||||
|
`,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const TextMultiline: Story = {
|
||||||
|
render: (args) => ({
|
||||||
|
props: args,
|
||||||
|
template: /*html*/ `
|
||||||
|
<bit-skeleton-text ${formatArgsForCodeSnippet<SkeletonTextComponent>(args)}></bit-skeleton-text>
|
||||||
|
`,
|
||||||
|
}),
|
||||||
|
args: {
|
||||||
|
lines: 5,
|
||||||
|
},
|
||||||
|
};
|
||||||
8
libs/components/src/skeleton/skeleton.component.html
Normal file
8
libs/components/src/skeleton/skeleton.component.html
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<div
|
||||||
|
class="tw-size-full tw-bg-secondary-100 tw-animate-pulse"
|
||||||
|
[ngClass]="{
|
||||||
|
'tw-rounded': edgeShape() === 'box',
|
||||||
|
'tw-rounded-full': edgeShape() === 'circle',
|
||||||
|
}"
|
||||||
|
aria-hidden="true"
|
||||||
|
></div>
|
||||||
26
libs/components/src/skeleton/skeleton.component.ts
Normal file
26
libs/components/src/skeleton/skeleton.component.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { CommonModule } from "@angular/common";
|
||||||
|
import { Component, input } from "@angular/core";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basic skeleton loading component that can be used to represent content that is loading.
|
||||||
|
* Use for layout-level elements and text, not for interactive elements.
|
||||||
|
*
|
||||||
|
* Customize the shape's edges with the `edgeShape` input. Customize the shape's size by
|
||||||
|
* applying classes to the `bit-skeleton` element (i.e. `tw-w-40 tw-h-8`).
|
||||||
|
*
|
||||||
|
* If you're looking to represent lines of text, use the `bit-skeleton-text` helper component.
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: "bit-skeleton",
|
||||||
|
templateUrl: "./skeleton.component.html",
|
||||||
|
imports: [CommonModule],
|
||||||
|
host: {
|
||||||
|
class: "tw-block",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
export class SkeletonComponent {
|
||||||
|
/**
|
||||||
|
* The shape of the corners of the skeleton element
|
||||||
|
*/
|
||||||
|
readonly edgeShape = input<"box" | "circle">("box");
|
||||||
|
}
|
||||||
112
libs/components/src/skeleton/skeleton.mdx
Normal file
112
libs/components/src/skeleton/skeleton.mdx
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
import { Meta, Canvas, Source } from "@storybook/addon-docs";
|
||||||
|
|
||||||
|
import * as skeletonStories from "./skeleton.stories";
|
||||||
|
import * as skeletonTextStories from "./skeleton-text.stories";
|
||||||
|
import * as skeletonGroupStories from "./skeleton-group.stories";
|
||||||
|
|
||||||
|
<Meta title="Component Library/Skeleton" />
|
||||||
|
|
||||||
|
# Skeleton Loading
|
||||||
|
|
||||||
|
The skeleton component can be used as an alternative loading indicator to the spinner by mimicking
|
||||||
|
the content that will be loaded such as text, images, or video. It can be used to represent layout
|
||||||
|
components as well, but should not be used for interactive elements like form controls or buttons.
|
||||||
|
|
||||||
|
## Skeleton Loading Components
|
||||||
|
|
||||||
|
There are three components that can be used to create a skeleton loading page.
|
||||||
|
|
||||||
|
### Skeleton
|
||||||
|
|
||||||
|
Basic skeleton loading component that can be used to represent content that is loading. Use for
|
||||||
|
non-text shapes.
|
||||||
|
|
||||||
|
#### Customizing
|
||||||
|
|
||||||
|
The basic skeleton component is fully customizable in shape and edge appearance to allow consumers
|
||||||
|
to more accurately represent the content.
|
||||||
|
|
||||||
|
**Inputs**
|
||||||
|
|
||||||
|
| Input | Description | Accepted options | Default |
|
||||||
|
| ----------- | --------------------------------------------------- | ---------------- | ------- |
|
||||||
|
| `edgeShape` | configure whether corners are fully rounded or boxy | `box`, `circle` | `box` |
|
||||||
|
|
||||||
|
**Classes**
|
||||||
|
|
||||||
|
Customize the shape's size by applying tailwind size classes to the `bit-skeleton` element (example
|
||||||
|
`tw-h-3 tw-w-12`). Please refer to the tailwind docs for all height/width options, and note that
|
||||||
|
custom values are possible with tailwind as well.
|
||||||
|
|
||||||
|
<Canvas of={skeletonStories.BoxEdgeShape} />
|
||||||
|
<Canvas of={skeletonStories.CircleEdgeShape} />
|
||||||
|
|
||||||
|
### Skeleton Text
|
||||||
|
|
||||||
|
Specific skeleton component used to represent lines of text.
|
||||||
|
|
||||||
|
#### Customizing
|
||||||
|
|
||||||
|
The number of lines of text in the skeleton is configurable.
|
||||||
|
|
||||||
|
**Inputs**
|
||||||
|
|
||||||
|
| Input | Description | Accepted options | Default |
|
||||||
|
| ------- | --------------------------------------------- | ---------------- | ------- |
|
||||||
|
| `lines` | configure how many lines of text are rendered | any `number` | `1` |
|
||||||
|
|
||||||
|
<Canvas of={skeletonTextStories.Text} />
|
||||||
|
<Canvas of={skeletonTextStories.TextMultiline} />
|
||||||
|
|
||||||
|
### Skeleton Group
|
||||||
|
|
||||||
|
Arranges skeleton loaders into a pre-arranged group that mimics the table and item components.
|
||||||
|
|
||||||
|
#### Customizing
|
||||||
|
|
||||||
|
**Slots**
|
||||||
|
|
||||||
|
Use the following slots to render `<bit-skeleton>` and/or `bit-skeleton-text` elements.
|
||||||
|
|
||||||
|
| Slot | Description |
|
||||||
|
| -------------- | ----------------------------------------------------------------------------------------------- |
|
||||||
|
| `slot="start"` | content that should appear horizontally before the default content; will not grow to fill space |
|
||||||
|
| default | main content area; grows to fill the horizontal space |
|
||||||
|
| `slot="end"` | content that should appear horizontally after the default content; will not grow to fill space |
|
||||||
|
|
||||||
|
<Canvas of={skeletonGroupStories.Default} />
|
||||||
|
|
||||||
|
## Display Considerations
|
||||||
|
|
||||||
|
For pages that load quickly, we want to avoid the skeleton flashing in and out. To avoid this, we
|
||||||
|
recommend the following display guidelines:
|
||||||
|
|
||||||
|
- After the loading is initiated (by page load or by user action), wait 1 second to display the
|
||||||
|
skeleton loader.
|
||||||
|
- After waiting 1s, render the loading skeleton.
|
||||||
|
- Ideally the skeleton disappears after 10 seconds, but we do not enforce a max duration. Add a max
|
||||||
|
duration at your discretion.
|
||||||
|
|
||||||
|
## Accessibility
|
||||||
|
|
||||||
|
Because there are typically multiple skeleton loaders present on a page that is using skeleton
|
||||||
|
loading, the individual skeleton loaders should not announce themselves or be present to
|
||||||
|
screenreaders, as this would overwhelm the user with multiple identical announcements. Thus, the
|
||||||
|
skeleton components are hidden from screenreaders.
|
||||||
|
|
||||||
|
Instead, the recommended strategy is to use a page-level announcement for screenreaders:
|
||||||
|
|
||||||
|
- We recommend using the
|
||||||
|
[Angular CDK LiveAnnouncer](https://material.angular.dev/cdk/a11y/overview#liveannouncer) to first
|
||||||
|
announce that content is loading when the skeleton loader is displayed, and then to announce that
|
||||||
|
content has loaded. The announcements should be localized, and the politeness level should be set
|
||||||
|
to `polite`.
|
||||||
|
|
||||||
|
- Alternatively, you may wish to render your own `role="status"` element or a custom `aria-live`
|
||||||
|
region in the template to accomplish the announcements detailed above.
|
||||||
|
|
||||||
|
## Example with Browser Extension
|
||||||
|
|
||||||
|
To see a full-page example of what skeleton loading might look like using all three skeleton
|
||||||
|
components, check the
|
||||||
|
[Popup Layout Skeleton Loading story](?path=/docs/browser-popup-layout--skeleton-loading).
|
||||||
50
libs/components/src/skeleton/skeleton.stories.ts
Normal file
50
libs/components/src/skeleton/skeleton.stories.ts
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import { Meta, moduleMetadata, StoryObj } from "@storybook/angular";
|
||||||
|
|
||||||
|
import { formatArgsForCodeSnippet } from "../../../../.storybook/format-args-for-code-snippet";
|
||||||
|
import { SharedModule } from "../shared/shared.module";
|
||||||
|
|
||||||
|
import { SkeletonComponent } from "./skeleton.component";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: "Component Library/Skeleton/Skeleton",
|
||||||
|
component: SkeletonComponent,
|
||||||
|
decorators: [
|
||||||
|
moduleMetadata({
|
||||||
|
imports: [SharedModule],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
args: {
|
||||||
|
edgeShape: "box",
|
||||||
|
},
|
||||||
|
argTypes: {
|
||||||
|
edgeShape: {
|
||||||
|
control: { type: "radio" },
|
||||||
|
options: ["box", "circle"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as Meta<SkeletonComponent>;
|
||||||
|
|
||||||
|
type Story = StoryObj<SkeletonComponent>;
|
||||||
|
|
||||||
|
export const BoxEdgeShape: Story = {
|
||||||
|
render: (args) => ({
|
||||||
|
props: args,
|
||||||
|
template: /*html*/ `
|
||||||
|
<div class="tw-mb-4">Examples of different size shapes with edgeShape={{ edgeShape }}</div>
|
||||||
|
<div class="tw-flex tw-flex-row tw-gap-8 tw-items-center">
|
||||||
|
<bit-skeleton ${formatArgsForCodeSnippet<SkeletonComponent>(args)} class="tw-size-32"></bit-skeleton>
|
||||||
|
<bit-skeleton ${formatArgsForCodeSnippet<SkeletonComponent>(args)} class="tw-w-40 tw-h-5"></bit-skeleton>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
}),
|
||||||
|
args: {
|
||||||
|
edgeShape: "box",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CircleEdgeShape: Story = {
|
||||||
|
...BoxEdgeShape,
|
||||||
|
args: {
|
||||||
|
edgeShape: "circle",
|
||||||
|
},
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user