diff --git a/.changeset/sixty-crabs-mate.md b/.changeset/sixty-crabs-mate.md new file mode 100644 index 0000000000..bc957db26a --- /dev/null +++ b/.changeset/sixty-crabs-mate.md @@ -0,0 +1,24 @@ +--- +"@patternfly/elements": major +--- + +``: replaces `` with PatternFly v6 design specs. + +```html +Loading... +``` + +**Breaking Changes from v5** + +- Renamed tag from `` to `` +- CSS custom properties renamed from `--pf-v5-c-spinner--*` to `--pf-v6-c-spinner--*` +- Removed `diameter` attribute (use `--pf-v6-c-spinner--diameter` CSS custom property instead) + +**New features** + +- `inline` attribute to inherit font size for inline display +- `accessible-label` attribute for screen reader text +- `value-text` attribute for progress state description +- `xs` size preset +- ARIA `progressbar` role with `aria-label` and `aria-valuetext` via ElementInternals +- `prefers-reduced-motion` support diff --git a/docs/main.mjs b/docs/main.mjs index cb8e3d725c..9b5ea371e5 100644 --- a/docs/main.mjs +++ b/docs/main.mjs @@ -2,10 +2,7 @@ import '@rhds/elements/rh-footer/rh-footer-universal.js'; import { PfV5Icon } from '@patternfly/elements/pf-v5-icon/pf-v5-icon.js'; import '@patternfly/elements/pf-v5-accordion/pf-v5-accordion.js'; import '@patternfly/elements/pf-v5-alert/pf-v5-alert.js'; -import '@patternfly/elements/pf-v6-avatar/pf-v6-avatar.js'; import '@patternfly/elements/pf-v5-back-to-top/pf-v5-back-to-top.js'; -import '@patternfly/elements/pf-v6-background-image/pf-v6-background-image.js'; -import '@patternfly/elements/pf-v6-badge/pf-v6-badge.js'; import '@patternfly/elements/pf-v5-banner/pf-v5-banner.js'; import '@patternfly/elements/pf-v5-button/pf-v5-button.js'; import '@patternfly/elements/pf-v5-card/pf-v5-card.js'; @@ -16,24 +13,27 @@ import '@patternfly/elements/pf-v5-dropdown/pf-v5-dropdown.js'; import '@patternfly/elements/pf-v5-helper-text/pf-v5-helper-text.js'; import '@patternfly/elements/pf-v5-hint/pf-v5-hint.js'; import '@patternfly/elements/pf-v5-jump-links/pf-v5-jump-links.js'; -import '@patternfly/elements/pf-v5-label/pf-v5-label.js'; import '@patternfly/elements/pf-v5-label-group/pf-v5-label-group.js'; +import '@patternfly/elements/pf-v5-label/pf-v5-label.js'; import '@patternfly/elements/pf-v5-modal/pf-v5-modal.js'; import '@patternfly/elements/pf-v5-panel/pf-v5-panel.js'; import '@patternfly/elements/pf-v5-popover/pf-v5-popover.js'; -import '@patternfly/elements/pf-v5-progress/pf-v5-progress.js'; import '@patternfly/elements/pf-v5-progress-stepper/pf-v5-progress-stepper.js'; +import '@patternfly/elements/pf-v5-progress/pf-v5-progress.js'; import '@patternfly/elements/pf-v5-search-input/pf-v5-search-input.js'; import '@patternfly/elements/pf-v5-select/pf-v5-select.js'; -import '@patternfly/elements/pf-v5-spinner/pf-v5-spinner.js'; -import '@patternfly/elements/pf-v6-switch/pf-v6-switch.js'; import '@patternfly/elements/pf-v5-table/pf-v5-table.js'; import '@patternfly/elements/pf-v5-tabs/pf-v5-tabs.js'; import '@patternfly/elements/pf-v5-text-area/pf-v5-text-area.js'; import '@patternfly/elements/pf-v5-text-input/pf-v5-text-input.js'; import '@patternfly/elements/pf-v5-tile/pf-v5-tile.js'; -import '@patternfly/elements/pf-v6-timestamp/pf-v6-timestamp.js'; import '@patternfly/elements/pf-v5-tooltip/pf-v5-tooltip.js'; +import '@patternfly/elements/pf-v6-avatar/pf-v6-avatar.js'; +import '@patternfly/elements/pf-v6-background-image/pf-v6-background-image.js'; +import '@patternfly/elements/pf-v6-badge/pf-v6-badge.js'; +import '@patternfly/elements/pf-v6-spinner/pf-v6-spinner.js'; +import '@patternfly/elements/pf-v6-switch/pf-v6-switch.js'; +import '@patternfly/elements/pf-v6-timestamp/pf-v6-timestamp.js'; // if `/v2/` path load icons from static directory if (document.location.href.includes('/v2/')) { diff --git a/elements/pf-v5-button/demo/stateful.html b/elements/pf-v5-button/demo/stateful.html index df648c15c4..c4d44c4683 100644 --- a/elements/pf-v5-button/demo/stateful.html +++ b/elements/pf-v5-button/demo/stateful.html @@ -12,7 +12,7 @@ -``` - -Or, if you are using [NPM](https://npm.im), install it - -```bash -npm install @patternfly/elements -``` - -Then once installed, import it to your application: - -```js -import '@patternfly/elements/pf-v5-spinner/pf-v5-spinner.js'; -``` - -## Usage -```html -Loading... -``` - -### Size variations - -```html -Loading... -Loading... -Loading... -Loading... -``` - -### Custom size - -```html -Loading... -``` - diff --git a/elements/pf-v5-spinner/demo/diameter.html b/elements/pf-v5-spinner/demo/diameter.html deleted file mode 100644 index a73e257c8a..0000000000 --- a/elements/pf-v5-spinner/demo/diameter.html +++ /dev/null @@ -1,15 +0,0 @@ -
-

Custom size

- Loading... -
- - - - diff --git a/elements/pf-v5-spinner/demo/index.html b/elements/pf-v5-spinner/demo/index.html deleted file mode 100644 index 21e42b03fd..0000000000 --- a/elements/pf-v5-spinner/demo/index.html +++ /dev/null @@ -1,14 +0,0 @@ -
- Loading... -
- - - - diff --git a/elements/pf-v5-spinner/demo/size.html b/elements/pf-v5-spinner/demo/size.html deleted file mode 100644 index 52fcd54998..0000000000 --- a/elements/pf-v5-spinner/demo/size.html +++ /dev/null @@ -1,18 +0,0 @@ -
-

Size variations

- Loading... - Loading... - Loading... - Loading... -
- - - - diff --git a/elements/pf-v5-spinner/docs/CHANGELOG.old.md b/elements/pf-v5-spinner/docs/CHANGELOG.old.md deleted file mode 100644 index 7cdb62967b..0000000000 --- a/elements/pf-v5-spinner/docs/CHANGELOG.old.md +++ /dev/null @@ -1,93 +0,0 @@ -# @patternfly/pfe-spinner - -## 2.0.0-next.6 - -### Patch Changes - -- ddf052bd: Added BaseSpinner.js to exports -- Updated dependencies [07ad1d3d] - - @patternfly/pfe-core@2.0.0-next.10 - -## 2.0.0-next.5 - -### Minor Changes - -- b7602c2c: BREAKING CHANGE: - - Renames `` to `` and changes it's HTML and - CSS APIs to more closely match PatternFly v4 design specs. - - ```html - - Loading... - - Loading... - ``` - -### Patch Changes - -- Updated dependencies [166ecee1] - - @patternfly/pfe-core@2.0.0-next.9 - -## 2.0.0-next.4 - -### Patch Changes - -- bfad8b4b: Updates dependencies -- Updated dependencies [bfad8b4b] - - @patternfly/pfe-core@2.0.0-next.8 - -## 2.0.0-next.3 - -### Major Changes - -- 94a8eb9a: Removes `-Height` and `-Width` CSS custom properties in favour of `size`. - - ```css - pfe-progress-indicator { - /* --pfe-progress-indicator--Height: 2rem; */ - /* --pfe-progress-indicator--Width: 2rem; */ - --pfe-progress-indicator-size: 2rem; - } - ``` - -### Patch Changes - -- Updated dependencies [34ecd410] - - @patternfly/pfe-core@2.0.0-next.6 - -## 2.0.0-next.2 - -### Patch Changes - -- 6a2a0407: [View commit message here](https://gist.github.com/heyMP/200fc0b840690541475923facba393ab) -- Updated dependencies [6a2a0407] - - @patternfly/pfe-core@2.0.0-next.4 - -## 2.0.0-next.1 - -### Patch Changes - -- 447b2d75: Remove `esbuild` export condition, as this anyways was a runtime error -- Updated dependencies [447b2d75] - - @patternfly/pfe-core@2.0.0-next.3 - -## 2.0.0-next.0 - -### Major Changes - -- 47c03201: ## 🔥 Migrate to Lit - - This release migrates `` to LitElement. - - ### Breaking Changes - - - Initial render is now [asynchronous](https://lit.dev/docs/components/lifecycle/#reactive-update-cycle). - If your code assumes that shadow DOM is ready once the element is constructed, update it to `await element.updateComplete` - - See [docs](https://patternflyelements.org/components/progress-indicator/) for more info - -### Patch Changes - -- Updated dependencies [e8788c72] - - @patternfly/pfe-core@2.0.0-next.0 diff --git a/elements/pf-v5-spinner/docs/pf-v5-spinner.md b/elements/pf-v5-spinner/docs/pf-v5-spinner.md deleted file mode 100644 index 595494f714..0000000000 --- a/elements/pf-v5-spinner/docs/pf-v5-spinner.md +++ /dev/null @@ -1,40 +0,0 @@ -{% renderInstallation %} {% endrenderInstallation %} - -{% renderOverview %} - A spinner is used to indicate to users that an action is in progress. - - Loading... -{% endrenderOverview %} - -{% band header="Usage" %} - ### Basic - {% htmlexample %} - Loading... - {% endhtmlexample %} - ### Size variations - {% htmlexample %} - Loading... - Loading... - Loading... - Loading... - {% endhtmlexample %} - - ### Custom size - {% htmlexample %} - Loading... - {% endhtmlexample %} -{% endband %} - -{% renderSlots %}{% endrenderSlots %} - -{% renderAttributes %}{% endrenderAttributes %} - -{% renderProperties %}{% endrenderProperties %} - -{% renderMethods %}{% endrenderMethods %} - -{% renderEvents %}{% endrenderEvents %} - -{% renderCssCustomProperties %}{% endrenderCssCustomProperties %} - -{% renderCssParts %}{% endrenderCssParts %} diff --git a/elements/pf-v5-spinner/docs/screenshot.png b/elements/pf-v5-spinner/docs/screenshot.png deleted file mode 100644 index 73bac9b87f..0000000000 Binary files a/elements/pf-v5-spinner/docs/screenshot.png and /dev/null differ diff --git a/elements/pf-v5-spinner/pf-v5-spinner.css b/elements/pf-v5-spinner/pf-v5-spinner.css deleted file mode 100644 index 7fe6fe2651..0000000000 --- a/elements/pf-v5-spinner/pf-v5-spinner.css +++ /dev/null @@ -1,99 +0,0 @@ -:host { - display: inline-block; - width: min-content; - min-height: 0; - aspect-ratio: 1 / 1; -} - -[hidden] { - display: none !important; -} - -svg { - overflow: hidden; - /** Width for spinner */ - width: var(--pf-v5-c-spinner--Width, - /** Diameter for spinner */ - var(--pf-v5-c-spinner--diameter, - var(--pf-global--icon--FontSize--xl, 3.375rem))); - /** Height for spinner */ - height: var(--pf-v5-c-spinner--Height, - var(--pf-v5-c-spinner--diameter, - var(--pf-global--icon--FontSize--xl, 3.375rem))); - animation: - pf-v5-c-spinner-animation-rotate - /** Animation duration for spinner */ - calc(var(--pf-v5-c-spinner--AnimationDuration, 1.4s) * 2) - /** Animation timing function for spinner */ - var(--pf-v5-c-spinner--AnimationTimingFunction, linear) infinite; -} - -circle { - width: 100%; - height: 100%; - transform-origin: 50% 50%; - stroke-linecap: round; - stroke-dasharray: 283; - stroke-dashoffset: 280; - /** Color for spinner */ - stroke: var(--pf-v5-c-spinner--Color, var(--pf-global--primary-color--100, #06c)); - /** Stroke width for spinner */ - stroke-width: var(--pf-v5-c-spinner--stroke-width, 10); - animation: - pf-v5-c-spinner-animation-dash - var(--pf-v5-c-spinner--AnimationDuration, 1.4s) - /** Path animation timing function for spinner */ - var(--pf-v5-c-spinner__path--AnimationTimingFunction, ease-in-out) infinite; -} - -:host([size="sm"]) svg { - /** Diameter for small spinner */ - --pf-v5-c-spinner--diameter: var(--pf-v5-c-spinner--m-sm--diameter, - var(--pf-global--icon--FontSize--sm, 0.625rem)); -} - -:host([size="md"]) svg { - /** Diameter for medium spinner */ - --pf-v5-c-spinner--diameter: var(--pf-v5-c-spinner--m-md--diameter, - var(--pf-global--icon--FontSize--md, 1.125rem)); -} - -:host([size="lg"]) svg { - /** Diameter for large spinner */ - --pf-v5-c-spinner--diameter: var(--pf-v5-c-spinner--m-lg--diameter, - var(--pf-global--icon--FontSize--lg, 1.5rem)); -} - -:host([size="xl"]) svg { - /** Diameter for extra large spinner */ - --pf-v5-c-spinner--diameter: var(--pf-v5-c-spinner--m-xl--diameter, - var(--pf-global--icon--FontSize--xl, 3.375rem)); -} - -@keyframes pf-v5-c-spinner-animation-rotate { - 0% { - transform: rotate(0deg); - } - 100% { - transform: rotate(360deg); - } -} - -@keyframes pf-v5-c-spinner-animation-dash { - 0% { - stroke-dashoffset: 280; - transform: rotate(0); - } - 15% { - /** Path stroke width for spinner */ - stroke-width: calc(var(--pf-v5-c-spinner__path--StrokeWidth, 10) - 4); - } - 40% { - stroke-dashoffset: 150; - stroke-dasharray: 220; - } - 100% { - stroke-dashoffset: 280; - transform: rotate(720deg); - } -} diff --git a/elements/pf-v5-spinner/pf-v5-spinner.ts b/elements/pf-v5-spinner/pf-v5-spinner.ts deleted file mode 100644 index 6e108ce929..0000000000 --- a/elements/pf-v5-spinner/pf-v5-spinner.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { LitElement, html, type TemplateResult } from 'lit'; -import { customElement } from 'lit/decorators/custom-element.js'; -import { styleMap } from 'lit/directives/style-map.js'; -import { property } from 'lit/decorators/property.js'; - -import { InternalsController } from '@patternfly/pfe-core/controllers/internals-controller.js'; - -import styles from './pf-v5-spinner.css'; - -/** - * A **spinner** is used to indicate to users that an action is in progress. For actions - * that may take a long time, use a progress bar instead. - * @alias Spinner - */ - -@customElement('pf-v5-spinner') -export class PfV5Spinner extends LitElement { - static readonly styles: CSSStyleSheet[] = [styles]; - - // eslint-disable-next-line no-unused-private-class-members - #internals = InternalsController.of(this, { role: 'progressbar' }); - - /** Preset sizes for the spinner */ - @property({ reflect: true }) size: 'sm' | 'md' | 'lg' | 'xl' = 'xl'; - - /** Custom diameter of spinner set as CSS variable */ - @property({ reflect: true }) diameter?: `${string}${'px' | '%' | 'rem' | 'em' | 'fr' | 'pt'}`; - - override render(): TemplateResult<1> { - return html` - - - - `; - } -} - -declare global { - interface HTMLElementTagNameMap { - 'pf-v5-spinner': PfV5Spinner; - } -} diff --git a/elements/pf-v5-spinner/test/pf-spinner.spec.ts b/elements/pf-v5-spinner/test/pf-spinner.spec.ts deleted file mode 100644 index 991004ff54..0000000000 --- a/elements/pf-v5-spinner/test/pf-spinner.spec.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { expect, html } from '@open-wc/testing'; -import { createFixture } from '@patternfly/pfe-tools/test/create-fixture.js'; -import { PfV5Spinner } from '@patternfly/elements/pf-v5-spinner/pf-v5-spinner.js'; - -describe('', function() { - it('imperatively instantiates', function() { - expect(document.createElement('pf-v5-spinner')).to.be.an.instanceof(PfV5Spinner); - }); - - it('should upgrade', async function() { - const element = await createFixture(html`Loading...`); - expect(element, 'pf-v5-spinner should be an instance of PfeSpinner') - .to.be.an.instanceOf(customElements.get('pf-v5-spinner')) - .and - .to.be.an.instanceOf(PfV5Spinner); - }); - - it('should properly initialize the component', async function() { - const element = await createFixture(html` - Loading... - `); - expect(element.getAttribute('size')).to.equal('xl'); - }); - - describe('basic usage', function() { - let element: PfV5Spinner; - beforeEach(async function() { - element = await createFixture(html`Loading...`); - }); - it('is accessible', async function() { - await expect(element).to.be.accessible(); - }); - }); - - describe('size attribute', function() { - let element: PfV5Spinner; - - function convertRemToPixels(rem: `${number}rem`) { - const { fontSize } = getComputedStyle(document.documentElement); - return parseFloat(rem) * parseFloat(fontSize); - } - - beforeEach(async function() { - element = await createFixture(html` - Loading... - `); - }); - - for (const [size, expected] of [ - ['sm', '0.625rem'], - ['md', '1.125rem'], - ['lg', '1.5rem'], - ['xl', '3.375rem'], - ] as const) { - it(size, async function() { - element.size = size; - await element.updateComplete; - expect(element.offsetWidth).to.equal(convertRemToPixels(expected)); - }); - } - }); - - describe('diameter attribute', function() { - it('sets the element diameter', async function() { - const customDiameterValue = 80; - const element = await createFixture(html` - Loading... - `); - - expect(element.offsetWidth).to.equal(customDiameterValue); - }); - }); -}); diff --git a/elements/pf-v6-spinner/README.md b/elements/pf-v6-spinner/README.md new file mode 100644 index 0000000000..0cca461565 --- /dev/null +++ b/elements/pf-v6-spinner/README.md @@ -0,0 +1,45 @@ +# `` + +A spinner indicates that an action is in progress. + +## Usage + +```html +Loading... +``` + +### Size variations + +```html +Loading... +Loading... +Loading... +Loading... +``` + +### Custom size + +```html +Loading... +``` + +## Divergences from React `Spinner` + +### Changed API + +| React prop | Web component | Difference | +|------------|---------------|------------| +| `diameter` | `--pf-v6-c-spinner--diameter` CSS custom property | React abstracts the CSS custom property behind a prop. In HTML, set it directly via `style`. | +| `aria-valuetext` | `value-text` attribute | Sets `aria-valuetext` on element internals. Defaults to `"Loading..."`. | +| `aria-label` | `accessible-label` attribute | Sets `aria-label` on element internals. | +| `isInline` | `inline` attribute | Boolean attribute. | +| `size` | `size` attribute | Adds `xs` preset from PatternFly CSS not exposed in React. | + +### CSS custom properties + +| Custom property | Description | +|-----------------|-------------| +| `--pf-v6-c-spinner--diameter` | Spinner diameter. | +| `--pf-v6-c-spinner--Color` | Spinner stroke color. | +| `--pf-v6-c-spinner--StrokeWidth` | Spinner stroke width. | +| `--pf-v6-c-spinner--AnimationDuration` | Animation cycle duration. | diff --git a/elements/pf-v6-spinner/demo/basic.html b/elements/pf-v6-spinner/demo/basic.html new file mode 100644 index 0000000000..38bfd69761 --- /dev/null +++ b/elements/pf-v6-spinner/demo/basic.html @@ -0,0 +1,17 @@ +--- +name: Basic +description: A basic spinner indicates that an action is in progress. +--- +
+ Loading... +
+ + + + diff --git a/elements/pf-v6-spinner/demo/custom-size.html b/elements/pf-v6-spinner/demo/custom-size.html new file mode 100644 index 0000000000..a9457a5f1d --- /dev/null +++ b/elements/pf-v6-spinner/demo/custom-size.html @@ -0,0 +1,17 @@ +--- +name: Custom size +description: A spinner can have a custom diameter using the `--pf-v6-c-spinner--diameter` CSS custom property. +--- +
+ Loading... +
+ + + + diff --git a/elements/pf-v6-spinner/demo/inline-size.html b/elements/pf-v6-spinner/demo/inline-size.html new file mode 100644 index 0000000000..ac12dc37a2 --- /dev/null +++ b/elements/pf-v6-spinner/demo/inline-size.html @@ -0,0 +1,35 @@ +--- +name: Inline size +description: An inline spinner inherits its font size, so its size will match the content around it. +--- +
+

+ Heading + Loading... +

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed hendrerit nisi in cursus maximus.

+

+ Second level + Loading... +

+

+ Curabitur accumsan turpis pharetra blandit. Quisque condimentum maximus mi, + Loading... + sit amet commodo arcu rutrum id. Proin pretium urna vel cursus venenatis. + Suspendisse potenti. +

+ + Sometimes you need small text + Loading... + +
+ + + + diff --git a/elements/pf-v6-spinner/demo/size-variations.html b/elements/pf-v6-spinner/demo/size-variations.html new file mode 100644 index 0000000000..16c5cd04af --- /dev/null +++ b/elements/pf-v6-spinner/demo/size-variations.html @@ -0,0 +1,24 @@ +--- +name: Size variations +description: Spinners can be sized using the `size` attribute with values `xs`, `sm`, `md`, `lg`, and `xl`. +--- +
+ Loading... + Loading... + Loading... + Loading... + Loading... +
+ + + + diff --git a/elements/pf-v6-spinner/docs/pf-v6-spinner.md b/elements/pf-v6-spinner/docs/pf-v6-spinner.md new file mode 100644 index 0000000000..399f373ab8 --- /dev/null +++ b/elements/pf-v6-spinner/docs/pf-v6-spinner.md @@ -0,0 +1,54 @@ +{% renderInstallation %} {% endrenderInstallation %} + +{% renderOverview %} + A spinner is an animated visual that indicates when a quick action is in progress. + + Loading... +{% endrenderOverview %} + +{% band header="Usage" %} + ### Basic + {% htmlexample %} + Loading... + {% endhtmlexample %} + + ### Size variations + Use the `size` attribute to set the spinner size to `xs`, `sm`, `md`, `lg`, or `xl`. + + {% htmlexample %} + + + + + + {% endhtmlexample %} + + ### Custom size + Use the `--pf-v6-c-spinner--diameter` CSS custom property to set a custom size. + + {% htmlexample %} + + {% endhtmlexample %} + + ### Inline + Set the `inline` attribute to inherit the font size from surrounding text. + + {% htmlexample %} +

Content is loading

+ {% endhtmlexample %} + +{% endband %} + +{% renderSlots %}{% endrenderSlots %} + +{% renderAttributes %}{% endrenderAttributes %} + +{% renderProperties %}{% endrenderProperties %} + +{% renderMethods %}{% endrenderMethods %} + +{% renderEvents %}{% endrenderEvents %} + +{% renderCssCustomProperties %}{% endrenderCssCustomProperties %} + +{% renderCssParts %}{% endrenderCssParts %} diff --git a/elements/pf-v6-spinner/docs/screenshot.png b/elements/pf-v6-spinner/docs/screenshot.png new file mode 100644 index 0000000000..7342ee09c9 Binary files /dev/null and b/elements/pf-v6-spinner/docs/screenshot.png differ diff --git a/elements/pf-v6-spinner/pf-v6-spinner.css b/elements/pf-v6-spinner/pf-v6-spinner.css new file mode 100644 index 0000000000..00b4ed7f85 --- /dev/null +++ b/elements/pf-v6-spinner/pf-v6-spinner.css @@ -0,0 +1,128 @@ +:host { + display: inline-block; + width: min-content; + min-height: 0; + aspect-ratio: 1 / 1; +} + +[hidden] { + display: none !important; +} + +svg { + overflow: hidden; + width: var(--pf-v6-c-spinner--Width, + /** Custom diameter of the spinner */ + var(--pf-v6-c-spinner--diameter, + /** Default spinner size */ + var(--pf-t--global--icon--size--2xl, 3.5rem))); + height: var(--pf-v6-c-spinner--Height, + var(--pf-v6-c-spinner--diameter, + var(--pf-t--global--icon--size--2xl, 3.5rem))); + animation: + rotate + calc( + /** Duration of one animation cycle */ + var(--pf-v6-c-spinner--AnimationDuration, 1.4s) * 2) + var(--pf-v6-c-spinner--AnimationTimingFunction, linear) infinite; +} + +circle { + width: 100%; + height: 100%; + transform-origin: 50% 50%; + stroke-linecap: round; + stroke-dasharray: 283; + stroke-dashoffset: 280; + stroke: + /** Color of the spinner stroke */ + var(--pf-v6-c-spinner--Color, + /** Brand color for spinner stroke */ + var(--pf-t--global--icon--color--brand--default, #0066cc)); + stroke-width: + /** Width of the spinner stroke */ + var(--pf-v6-c-spinner--StrokeWidth, 10); + animation: + dash + var(--pf-v6-c-spinner--AnimationDuration, 1.4s) + var(--pf-v6-c-spinner__path--AnimationTimingFunction, ease-in-out) infinite; +} + +:host([size="xs"]) svg { + --pf-v6-c-spinner--diameter: var(--pf-v6-c-spinner--m-xs--diameter, + /** Extra-small icon size for compact spinners */ + var(--pf-t--global--icon--size--sm, 0.75rem)); + + & circle { + stroke-width: var(--pf-v6-c-spinner--StrokeWidth, 15); + } +} + +:host([size="sm"]) svg { + --pf-v6-c-spinner--diameter: var(--pf-v6-c-spinner--m-sm--diameter, + /** Small icon size */ + var(--pf-t--global--icon--size--md, 0.875rem)); +} + +:host([size="md"]) svg { + --pf-v6-c-spinner--diameter: var(--pf-v6-c-spinner--m-md--diameter, + /** Medium icon size */ + var(--pf-t--global--icon--size--lg, 1rem)); +} + +:host([size="lg"]) svg { + --pf-v6-c-spinner--diameter: var(--pf-v6-c-spinner--m-lg--diameter, + /** Large icon size */ + var(--pf-t--global--icon--size--xl, 1.5rem)); +} + +:host([size="xl"]) svg { + --pf-v6-c-spinner--diameter: var(--pf-v6-c-spinner--m-xl--diameter, + /** Extra-large icon size, default spinner diameter */ + var(--pf-t--global--icon--size--2xl, 3.5rem)); +} + +:host([inline]) svg { + --pf-v6-c-spinner--diameter: var(--pf-v6-c-spinner--m-inline--diameter, 1em); +} + +@keyframes rotate { + 0% { + rotate: 0deg; + } + + 100% { + rotate: 360deg; + } +} + +@keyframes dash { + 0% { + stroke-dashoffset: 280; + rotate: 0deg; + } + + 15% { + stroke-width: calc( + /** Stroke width during mid-animation thin phase */ + var(--pf-v6-c-spinner__path--StrokeWidth, + var(--pf-v6-c-spinner--StrokeWidth, 10)) - 4); + } + + 40% { + stroke-dasharray: 220; + stroke-dashoffset: 150; + } + + 100% { + stroke-dashoffset: 280; + rotate: 720deg; + } +} + +@media (prefers-reduced-motion: reduce) { + svg, + circle { + animation-duration: 0s; + } +} diff --git a/elements/pf-v6-spinner/pf-v6-spinner.ts b/elements/pf-v6-spinner/pf-v6-spinner.ts new file mode 100644 index 0000000000..a3b55feae1 --- /dev/null +++ b/elements/pf-v6-spinner/pf-v6-spinner.ts @@ -0,0 +1,81 @@ +import { LitElement, html, type TemplateResult } from 'lit'; +import { customElement } from 'lit/decorators/custom-element.js'; +import { property } from 'lit/decorators/property.js'; + +import { InternalsController } from '@patternfly/pfe-core/controllers/internals-controller.js'; + +import styles from './pf-v6-spinner.css'; + +export type SpinnerSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl'; + +/** + * A **spinner** provides an animated indicator for in-progress actions. + * Authors SHOULD set `accessible-label` when the default "Loading..." is + * insufficient. For long-running operations, use a progress bar instead. + * Respects `prefers-reduced-motion` by disabling animation. + * + * @summary Indicates that an action is in progress + * + * @cssprop {} --pf-v6-c-spinner--diameter - Spinner diameter (overrides `size` attribute) + * @cssprop {} --pf-v6-c-spinner--Width - Spinner width + * @cssprop {} --pf-v6-c-spinner--Height - Spinner height + * @cssprop {} --pf-v6-c-spinner--Color - Spinner stroke color + * @cssprop {} --pf-v6-c-spinner--StrokeWidth - Spinner stroke width + * @cssprop {