Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions packages/craftcms-cp/.storybook/preview.css
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,20 @@
font-size: 12px;
color: rgba(0, 0, 0, 0.5);
}

table {
width: 100%;
border-collapse: collapse;
table-layout: fixed;
text-align: left;
}

table th,
table td {
border: 1px solid rgba(0, 0, 0, 0.2);
padding: 0.5em;
}

table th {
background-color: rgba(0, 0, 0, 0.05);
}
2 changes: 1 addition & 1 deletion packages/craftcms-cp/scripts/generate-colors.js
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ ${buildColorableTokens()}
${buildSemanticTokens()}
}

${[...availableColors, ...semanticColors].map((c) => buildStyleBlock(c)).join('\n')}
${[...availableColors, ...Object.values(semanticColors)].map((c) => buildStyleBlock(c)).join('\n')}
`;
}

Expand Down
2 changes: 1 addition & 1 deletion packages/craftcms-cp/src/actions/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type {VariantKey} from '@src/types';
import type {VariantKey} from '@src/constants/variants';

export type BaseAction =
| {type: 'clipboard'; value: string}
Expand Down
15 changes: 3 additions & 12 deletions packages/craftcms-cp/src/components/action-item/action-item.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,13 @@
import {html, LitElement, nothing} from 'lit';
import {property, state} from 'lit/decorators.js';
import styles from './action-item.styles.js';
import {
type AsyncState,
AsyncStates,
Variant,
type VariantKey,
} from '@src/types';
import {type AsyncState, AsyncStates} from '@src/types';
import variantsStyles from '@src/styles/variants.styles';
import {classMap} from 'lit/directives/class-map.js';

import '../shortcut/shortcut.js';
import {
type ActionFeedback,
type BaseAction,
type FeedbackData,
runAction,
} from '@src/actions';
import {type ActionFeedback, type BaseAction, type FeedbackData, runAction,} from '@src/actions';
import {Variant, type VariantKey} from '@src/constants/variants';

/**
* @summary Either a link or button typically used in a menu.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@ import type {Meta, StoryObj} from '@storybook/web-components-vite';
import {html} from 'lit';

import './callout.js';
import {Appearance, Variant} from '@src/types';

const variants = Object.values(Variant);
const appearances = Object.values(Appearance);
import {appearances} from '@src/constants/appearances.js';
import {variants} from '@src/constants/variants.js';

// More on how to set up stories at: https://storybook.js.org/docs/writing-stories
const meta = {
Expand Down
8 changes: 2 additions & 6 deletions packages/craftcms-cp/src/components/callout/callout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,8 @@ import {type CSSResultGroup, html, LitElement, nothing} from 'lit';
import {property} from 'lit/decorators.js';
import styles from './callout.styles.js';
import '../icon/icon.js';
import {
Appearance,
type AppearanceKey,
Variant,
type VariantKey,
} from '@src/types/index.js';
import {Appearance, type AppearanceKey} from '@src/constants/appearances';
import {Variant, type VariantKey} from '@src/constants/variants';
import variantsStyles from '@src/styles/variants.styles.js';

export default class CraftCallout extends LitElement {
Expand Down
186 changes: 186 additions & 0 deletions packages/craftcms-cp/src/components/indicator/Indicator.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
import {Canvas, Meta} from '@storybook/addon-docs/blocks';
import IndicatorMeta, {ArbitraryColor, ArbitrarySize, Default,} from './indicator.stories';

<Meta of={IndicatorMeta} name="Docs" />

# Indicator

`<craft-indicator>` is a small dot used to visually represent the status of an
object — for example, the live/draft state of an entry or a "has updates" marker
in a list.

<Canvas of={Default} />

## Usage

```html
<craft-indicator fill="success" label="Live"></craft-indicator>
```

The element renders a single `role="img"` dot. It has no slot — all of its
appearance is driven by attributes.

## Properties

<table>
<thead>
<tr>
<th>Property</th>
<th>Attribute</th>
<th>Type</th>
<th>Default</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<code>fill</code>
</td>
<td>
<code>fill</code>
</td>
<td>
<code>string</code>
</td>
<td>
<code>var(--c-color-fill-loud)</code>
</td>
<td>
The dot color. Accepts a semantic keyword (see below) or any CSS color.
</td>
</tr>
<tr>
<td>
<code>appearance</code>
</td>
<td>
<code>appearance</code>
</td>
<td>
<code>'filled' | 'outlined' | 'filled-outlined'</code>
</td>
<td>
<code>'filled-outlined'</code>
</td>
<td>
How the dot is drawn — see <a href="#appearance">Appearance</a>.
</td>
</tr>
<tr>
<td>
<code>size</code>
</td>
<td>
<code>size</code>
</td>
<td>
<code>'md' | 'lg'</code>
</td>
<td>
<code>'md'</code>
</td>
<td>
<code>md</code> ≈ 0.6em, <code>lg</code> ≈ 1em. Scales with the
surrounding font size.
</td>
</tr>
<tr>
<td>
<code>label</code>
</td>
<td>
<code>label</code>
</td>
<td>
<code>string | null</code>
</td>
<td>
<code>null</code>
</td>
<td>
Accessible name, exposed as <code>aria-label</code>. Provide it for
non-decorative dots.
</td>
</tr>
</tbody>
</table>

## Fill

`fill` sets the dot's color. A recognized **status variant** (`default`,
`success`, `warning`, `danger`, `info`) or **palette swatch** (`red`, `orange`,
`amber`, `yellow`, `lime`, `green`, `emerald`, `teal`, `cyan`, `sky`, `blue`,
`indigo`, `violet`, `purple`, `fuchsia`, `pink`, `rose`, `gray`, `white`,
`black`) resolves to the matching `--c-color-<name>-fill-loud` design token. Any
other value — a hex code, `rgb()`, a gradient, or a custom property — is used
verbatim.

Prefer the variant and swatch names so indicators stay consistent with the rest
of the palette; reach for an arbitrary value only when a color falls outside it.

<Canvas of={ArbitraryColor} />

## Appearance

`appearance` controls how the dot is drawn:

<table>
<thead>
<tr>
<th>Value</th>
<th>Renders</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<code>filled-outlined</code> (default)
</td>
<td>Filled dot with a subtle dark outline.</td>
</tr>
<tr>
<td>
<code>filled</code>
</td>
<td>Filled dot with no outline.</td>
</tr>
<tr>
<td>
<code>outlined</code>
</td>
<td>
Hollow dot — a 2px ring in the fill color over a transparent center.
</td>
</tr>
</tbody>
</table>

## Sizing

`size` switches between two preset sizes. Because the dimensions are defined in
`em`, the dot scales with the font size of its container — set `font-size` on a
wrapper to fine-tune it.

<Canvas of={ArbitrarySize} />

## Accessibility

- The dot is exposed as `role="img"`, and `label` becomes its `aria-label`.
- A status dot conveys meaning through color alone, so set `label` whenever the
indicator isn't purely decorative. If it only repeats adjacent visible text,
it's fine to leave `label` unset.

## Styling

At render time the resolved color and size are written to the `--fill` and
`--size` custom properties on the inner dot, so there's nothing to override
there directly. Because the dot is sized in `em`, set `font-size` on the host to
scale it:

```css
craft-indicator {
/* the dot is sized in em — change the font size to scale it */
font-size: 1.25rem;
}
```
97 changes: 83 additions & 14 deletions packages/craftcms-cp/src/components/indicator/indicator.stories.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,101 @@
import type {Meta, StoryObj} from '@storybook/web-components-vite';

import {html} from 'lit';
import {html, nothing} from 'lit';

import './indicator.js';
import {Variant} from '@src/types';
import type CraftIndicator from './indicator.js';
import {Variant} from '@src/constants/variants';

const appearances = ['filled-outlined', 'filled', 'outlined'];

/**
* Renders the indicator in each appearance, with a label, so every story shares
* the same markup.
*/
const renderIndicators = (
label: string,
attrs: {fill?: string; size?: CraftIndicator['size']} = {}
) => html`
<tr>
<td>${label}</td>
${appearances.map(
(appearance) => html`
<td>
<craft-indicator
fill="${attrs.fill ?? nothing}"
size="${attrs.size ?? nothing}"
appearance="${appearance}"
></craft-indicator>
</td>
`
)}
</tr>
`;

// More on how to set up stories at: https://storybook.js.org/docs/writing-stories
const meta = {
title: 'Components/Indicator',
component: 'craft-indicator',
args: {},
render: function () {
return html`
<div class="stack">
${Object.values(Variant).map(
(variant) =>
html`<craft-indicator variant="${variant}"></craft-indicator>`
render: () => html`
<table>
<thead>
<th></th>
${appearances.map((appearance) => html`<th>${appearance}</th>`)}
</thead>
<tbody>
${Object.values(Variant).map((variant) =>
renderIndicators(variant, {fill: variant})
)}
<craft-indicator variant="empty"></craft-indicator>
</div>
`;
},
} satisfies Meta<any>;
</tbody>
</table>
`,
} satisfies Meta<CraftIndicator>;

export default meta;
type Story = StoryObj<any>;
type Story = StoryObj<CraftIndicator>;

// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
export const Default: Story = {
args: {},
};

const arbitraryValues = [
'#2c61de',
'rgba(30, 40, 40, 0.2)',
'linear-gradient(red, blue)',
];

export const ArbitraryColor: Story = {
render: () => html`
<table>
<thead>
<th></th>
${appearances.map((appearance) => html`<th>${appearance}</th>`)}
</thead>
<tbody>
${arbitraryValues.map((value) =>
renderIndicators(value, {fill: value})
)}
</tbody>
</table>
`,
};

const sizes: Array<CraftIndicator['size']> = ['md', 'lg'];

export const ArbitrarySize: Story = {
render: () => html`
<table>
<thead>
<th></th>
${appearances.map((appearance) => html`<th>${appearance}</th>`)}
</thead>
<tbody>
${sizes.map((size) =>
renderIndicators(size === 'md' ? 'md (default)' : size, {size})
)}
</tbody>
</table>
`,
};
Loading
Loading