Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "Adds Spinner as a web component",
"packageName": "@fluentui/web-components",
"email": "[email protected]",
"dependentChangeType": "patch"
}
42 changes: 42 additions & 0 deletions packages/web-components/docs/api-report.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { ElementViewTemplate } from '@microsoft/fast-element';
import { FASTElement } from '@microsoft/fast-element';
import { FASTElementDefinition } from '@microsoft/fast-element';
import { FASTProgress } from '@microsoft/fast-foundation';
import { FASTProgressRing } from '@microsoft/fast-foundation';
import { StartEnd } from '@microsoft/fast-foundation';
import { StartEndOptions } from '@microsoft/fast-foundation';
import { StaticallyComposableHTML } from '@microsoft/fast-foundation';
Expand Down Expand Up @@ -221,6 +222,44 @@ export const ProgressBarValidationState: {
// @public
export type ProgressBarValidationState = ValuesOf<typeof ProgressBarValidationState>;

// @public
export class Spinner extends FASTProgressRing {
appearance: SpinnerAppearance;
size: SpinnerSize;
}

// @public
export const SpinnerAppearance: {
readonly primary: "primary";
readonly inverted: "inverted";
};

// @public
export type SpinnerAppearance = Values<typeof SpinnerAppearance>;

// @public
export const SpinnerDefinition: FASTElementDefinition<typeof Spinner>;

// @public
export type SpinnerSize = Values<typeof SpinnerSize>;

// @public
export const SpinnerSize: {
readonly tiny: "tiny";
readonly extraSmall: "extra-small";
readonly small: "small";
readonly medium: "medium";
readonly large: "large";
readonly extraLarge: "extra-large";
readonly huge: "huge";
};

// @public (undocumented)
export const SpinnerStyles: ElementStyles;

// @public (undocumented)
export const SpinnerTemplate: ElementViewTemplate<Spinner>;

// @public
class Text_2 extends FASTElement {
align: TextAlign;
Expand Down Expand Up @@ -296,6 +335,9 @@ export const TextWeight: {
// @public
export type TextWeight = ValuesOf<typeof TextWeight>;

// @public (undocumented)
export type Values<T> = T[keyof T];

// (No @packageDocumentation comment for this package)

```
4 changes: 4 additions & 0 deletions packages/web-components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@
"./progress-bar": {
"types": "./dist/esm/progress-bar/define.d.ts",
"default": "./dist/esm/progress-bar/define.js"
},
"./spinner": {
"types": "./dist/esm/spinner/define.d.ts",
"default": "./dist/esm/spinner/define.js"
}
},
"scripts": {
Expand Down
1 change: 1 addition & 0 deletions packages/web-components/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './badge/index.js';
export * from './counter-badge/index.js';
export * from './progress-bar/index.js';
export * from './spinner/index.js';
export * from './text/index.js';
4 changes: 4 additions & 0 deletions packages/web-components/src/spinner/define.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { FluentDesignSystem } from '../fluent-design-system.js';
import { definition } from './spinner.definition.js';

definition.define(FluentDesignSystem.registry);
5 changes: 5 additions & 0 deletions packages/web-components/src/spinner/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export * from './spinner.js';
export * from './spinner.options.js';
export { template as SpinnerTemplate } from './spinner.template.js';
export { styles as SpinnerStyles } from './spinner.styles.js';
export { definition as SpinnerDefinition } from './spinner.definition.js';
19 changes: 19 additions & 0 deletions packages/web-components/src/spinner/spinner.definition.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { FluentDesignSystem } from '../fluent-design-system.js';
import { Spinner } from './spinner.js';
import { styles } from './spinner.styles.js';
import { template } from './spinner.template.js';

/**
* The Fluent Spinner Element. Implements {@link @microsoft/fast-foundation#ProgressRing },
* {@link @microsoft/fast-foundation#progress-ringTemplate}
*
*
* @public
* @remarks
* HTML Element: \<fluent-spinner\>
*/
export const definition = Spinner.compose({
name: `${FluentDesignSystem.prefix}-spinner`,
template,
styles,
});
48 changes: 48 additions & 0 deletions packages/web-components/src/spinner/spinner.options.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
export type Values<T> = T[keyof T];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@procload I still see this, can we remove is it's duplicative of the above import?

Suggested change
export type Values<T> = T[keyof T];

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doh! Will do.

/**
* Spinner appearance options
* @public
*/
export const SpinnerAppearance = {
/**
* Use the primary appearance when the Spinner is on a light background
*
*/
primary: 'primary',

/**
* Use the inverted appearance when the Spinner is on a dark background
*
*/
inverted: 'inverted',
} as const;

/**
* The type for Spinner size option
* @public
*/
export type SpinnerSize = Values<typeof SpinnerSize>;

/**
* The type for Spinner appearance option
* @public
*/
export type SpinnerAppearance = Values<typeof SpinnerAppearance>;

/**
* Spinner size options
* @public
*/
export const SpinnerSize = {
/**
* A Spinner's size can be small, tiny, extra-small, medium, large, extra-large, or huge
*
*/
tiny: 'tiny',
extraSmall: 'extra-small',
small: 'small',
medium: 'medium',
large: 'large',
extraLarge: 'extra-large',
huge: 'huge',
} as const;
53 changes: 53 additions & 0 deletions packages/web-components/src/spinner/spinner.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { html } from '@microsoft/fast-element';
import type { Args, Meta } from '@storybook/html';
import { renderComponent } from '../__test__/helpers.js';
import { SpinnerAppearance, SpinnerSize } from './spinner.options.js';
import './define.js';

type SpinnerStoryArgs = Args;
type SpinnerStoryMeta = Meta<SpinnerStoryArgs>;

const storyTemplate = html<SpinnerStoryArgs>`
<fluent-spinner appearance=${x => x.appearance} size=${x => x.size}></fluent-spinner>
`;

export default {
title: 'Components/Spinner',
argTypes: {
appearance: {
description: 'The appearance of the spinner',
table: {
defaultValue: { summary: 'primary' },
},
control: {
type: 'select',
options: Object.values(SpinnerAppearance),
},
defaultValue: 'primary',
},
size: {
description: 'The size of the spinner',
table: {
defaultValue: { summary: 'medium' },
},
control: {
type: 'select',
options: Object.values(SpinnerSize),
},
defaultValue: 'medium',
},
},
parameters: {
status: {
type: 'experimental',
},
},
} as SpinnerStoryMeta;

export const Spinner = renderComponent(storyTemplate).bind({});

export const SpinnerInverted = renderComponent(html<SpinnerStoryArgs>`
<div style="background-color: #0f6cbd; padding: 20px;">
<fluent-spinner appearance="inverted" size="medium"></fluent-spinner>
</div>
`);
96 changes: 96 additions & 0 deletions packages/web-components/src/spinner/spinner.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { css } from '@microsoft/fast-element';
import { display } from '@microsoft/fast-foundation';
import { colorBrandStroke1, colorBrandStroke2, colorNeutralStrokeOnBrand2 } from '../theme/design-tokens.js';

export const styles = css`
${display('flex')}

:host {
display: flex;
align-items: center;
height: 32px;
width: 32px;
}
:host([size='tiny']) {
height: 20px;
width: 20px;
}
:host([size='extra-small']) {
height: 24px;
width: 24px;
}
:host([size='small']) {
height: 28px;
width: 28px;
}
:host([size='large']) {
height: 36px;
width: 36px;
}
:host([size='extra-large']) {
height: 40px;
width: 40px;
}
:host([size='huge']) {
height: 44px;
width: 44px;
}
.progress {
height: 100%;
width: 100%;
}

.background {
fill: none;
stroke: ${colorBrandStroke2};
stroke-width: 1.5px;
}

:host([appearance='inverted']) .background {
stroke: rgba(255, 255, 255, 0.2);
}

.determinate {
stroke: ${colorBrandStroke1};
fill: none;
stroke-width: 1.5px;
stroke-linecap: round;
transform-origin: 50% 50%;
transform: rotate(-90deg);
transition: all 0.2s ease-in-out;
}

:host([appearance='inverted']) .determinite {
stroke: ${colorNeutralStrokeOnBrand2};
}

.indeterminate-indicator-1 {
stroke: ${colorBrandStroke1};
fill: none;
stroke-width: 1.5px;
stroke-linecap: round;
transform-origin: 50% 50%;
transform: rotate(-90deg);
transition: all 0.2s ease-in-out;
animation: spin-infinite 3s cubic-bezier(0.53, 0.21, 0.29, 0.67) infinite;
}

:host([appearance='inverted']) .indeterminate-indicator-1 {
stroke: ${colorNeutralStrokeOnBrand2};
}

@keyframes spin-infinite {
0% {
stroke-dasharray: 0.01px 43.97px;
transform: rotate(0deg);
}
50% {
stroke-dasharray: 21.99px 21.99px;
transform: rotate(450deg);
}
100% {
stroke-dasharray: 0.01px 43.97px;
transform: rotate(1080deg);
}
}
`;
24 changes: 24 additions & 0 deletions packages/web-components/src/spinner/spinner.template.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import type { ElementViewTemplate } from '@microsoft/fast-element';
import { progressRingTemplate } from '@microsoft/fast-foundation';
import { Spinner } from './spinner.js';

export const template: ElementViewTemplate<Spinner> = progressRingTemplate({
indeterminateIndicator: `
<svg class="progress" part="progress" viewBox="0 0 16 16">
<circle
class="background"
part="background"
cx="8px"
cy="8px"
r="7px"
></circle>
<circle
class="indeterminate-indicator-1"
part="indeterminate-indicator-1"
cx="8px"
cy="8px"
r="7px"
></circle>
</svg>
`,
});
30 changes: 30 additions & 0 deletions packages/web-components/src/spinner/spinner.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { attr } from '@microsoft/fast-element';
import { FASTProgressRing } from '@microsoft/fast-foundation';
import type { SpinnerAppearance, SpinnerSize } from './spinner.options.js';

/**
* The base class used for constructing a fluent-spinner custom element
* @public
*/
export class Spinner extends FASTProgressRing {
/**
* The size of the spinner
*
* @public
* @default 'medium'
* @remarks
* HTML Attribute: size
*/
@attr
public size: SpinnerSize;

/**
* The appearance of the spinner
* @public
* @default 'primary'
* @remarks
* HTML Attribute: appearance
*/
@attr
public appearance: SpinnerAppearance;
}