diff --git a/packages/craftcms-cp/src/components/badge-indicator/badge-indicator.stories.ts b/packages/craftcms-cp/src/components/badge-indicator/badge-indicator.stories.ts new file mode 100644 index 00000000000..73dfcba5e84 --- /dev/null +++ b/packages/craftcms-cp/src/components/badge-indicator/badge-indicator.stories.ts @@ -0,0 +1,46 @@ +import type {Meta, StoryObj} from '@storybook/web-components-vite'; + +import {html} from 'lit'; + +import './badge-indicator.js'; + +// More on how to set up stories at: https://storybook.js.org/docs/writing-stories +const meta = { + title: 'Components/Badge Indicator', + component: 'craft-badge-indicator', + argTypes: {}, + render: (args) => html` + + `, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args +export const DotBadge: Story = { + name: 'Dot Badge', + args: { + srText: 'Has Notifications' + }, + argTypes: { + number: { + control: { type: null } + }, + }, + render: (args) => html` + + `, +} + +export const NumberedBadge: Story = { + name: 'Numbered Badge', + args: { + number: 5, + srText: 'updates' + }, + render: (args) => html` + + `, +} + diff --git a/packages/craftcms-cp/src/components/badge-indicator/badge-indicator.styles.ts b/packages/craftcms-cp/src/components/badge-indicator/badge-indicator.styles.ts new file mode 100644 index 00000000000..7fbbd2ede58 --- /dev/null +++ b/packages/craftcms-cp/src/components/badge-indicator/badge-indicator.styles.ts @@ -0,0 +1,26 @@ +import {css} from 'lit'; + +export default css` + .badge-indicator { + display: inline-flex; + width: var(--c-size-icon-xs); + height: var(--c-size-icon-xs); + justify-content: center; + align-items: center; + background-color: var(--c-color-accent-bg-emphasis); + color: white; + border-radius: var(--c-radius-full); + } + + .badge-indicator--with-number { + padding: 8px; + + } + + .number { + font-size: var(--c-text-xs); + font-weight: 600; + line-height: 1; + display: inline-block; + } +`; diff --git a/packages/craftcms-cp/src/components/badge-indicator/badge-indicator.ts b/packages/craftcms-cp/src/components/badge-indicator/badge-indicator.ts new file mode 100644 index 00000000000..4023c56d0a0 --- /dev/null +++ b/packages/craftcms-cp/src/components/badge-indicator/badge-indicator.ts @@ -0,0 +1,73 @@ +import {html, css, LitElement, nothing} from 'lit'; +import {property} from 'lit/decorators.js'; +import styles from './badge-indicator.styles.js'; +import {classMap} from 'lit/directives/class-map.js'; +import '@shoelace-style/shoelace/dist/components/visually-hidden/visually-hidden.js'; + +/** + * @summary A badge indicator component. Used in various places to indicate that + * something is new or has been updated. The indicator can have an optional + * notification count. + */ + +export default class CraftBadgeIndicator extends LitElement { + static override styles = [styles]; + + /** Number of notifications */ + @property() number: number | null = null; + + /** Accessible text for screen reader users */ + @property() srText: string | null = null; + + @property() + override id: string; + + constructor() { + super(); + this.id = this.id || Math.floor(Math.random() * 1000000000).toString(); + } + + private truncatedNumber() { + if (!this.number) { + return null; + } + + if (this.number > 99) { + return '99+'; + } else { + return this.number.toString(); + } + } + override render() { + const hasNumber = this.number !== null; + const badgeId = this.id ?? nothing; + const labelId = badgeId ? `${badgeId}-label` : nothing; + + return html` +
+ ${hasNumber + ? html`${this.truncatedNumber()}` + : nothing} + ${this.srText} +
+ `; + } +} + +if (!customElements.get('craft-badge-indicator')) { + customElements.define('craft-badge-indicator', CraftBadgeIndicator); +} + +declare global { + interface HTMLElementTagNameMap { + 'craft-badge-indicator': CraftBadgeIndicator; + } +} diff --git a/packages/craftcms-cp/src/components/indicator/indicator.ts b/packages/craftcms-cp/src/components/indicator/indicator.ts index 99f9e240c16..8d37645216f 100644 --- a/packages/craftcms-cp/src/components/indicator/indicator.ts +++ b/packages/craftcms-cp/src/components/indicator/indicator.ts @@ -36,7 +36,9 @@ export default class CraftIndicator extends LitElement { 'indicator--warning': this.variant === Variant.Warning, 'indicator--info': this.variant === Variant.Info, })}" - >`; + > + + `; } } diff --git a/packages/craftcms-cp/src/components/nav-item/nav-item.stories.ts b/packages/craftcms-cp/src/components/nav-item/nav-item.stories.ts index 52592b95062..d02801a7724 100644 --- a/packages/craftcms-cp/src/components/nav-item/nav-item.stories.ts +++ b/packages/craftcms-cp/src/components/nav-item/nav-item.stories.ts @@ -3,7 +3,7 @@ import type {Meta, StoryObj} from '@storybook/web-components-vite'; import {html} from 'lit'; import '../icon/icon.js'; -import '../navigation/navigation.js'; +import '../nav-list/nav-list.js'; import '../button/button.js'; import './nav-item.js'; diff --git a/packages/craftcms-cp/src/styles/shared/tokens.css b/packages/craftcms-cp/src/styles/shared/tokens.css index fa624db52f7..d34cbd69a30 100644 --- a/packages/craftcms-cp/src/styles/shared/tokens.css +++ b/packages/craftcms-cp/src/styles/shared/tokens.css @@ -3,6 +3,7 @@ --c-text-lg: calc(16rem / 16); --c-text-base: calc(14rem / 16); --c-text-sm: calc(11rem / 16); + --c-text-xs: calc(9rem / 16); --c-leading-normal: 1.42;