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
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"
}
39 changes: 39 additions & 0 deletions packages/web-components/docs/api-report.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,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 @@ -1351,6 +1352,44 @@ export const spacingVerticalXXS: CSSDesignToken<string>;
// @public (undocumented)
export const spacingVerticalXXXL: CSSDesignToken<string>;

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

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

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

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

// @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
export type SpinnerSize = ValuesOf<typeof SpinnerSize>;

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

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

// @public (undocumented)
export const strokeWidthThick: CSSDesignToken<string>;

Expand Down
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,6 +1,7 @@
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';

export * from './theme/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,
});
36 changes: 36 additions & 0 deletions packages/web-components/src/spinner/spinner.options.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { ValuesOf } from '@microsoft/fast-foundation';

/**
* SpinnerAppearance constants
* @public
*/
export const SpinnerAppearance = {
primary: 'primary',
inverted: 'inverted',
} as const;

/**
* A Spinner's appearance can be either primary or inverted
* @public
*/
export type SpinnerAppearance = ValuesOf<typeof SpinnerAppearance>;

/**
* SpinnerSize constants
* @public
*/
export const SpinnerSize = {
tiny: 'tiny',
extraSmall: 'extra-small',
small: 'small',
medium: 'medium',
large: 'large',
extraLarge: 'extra-large',
huge: 'huge',
} as const;

/**
* A Spinner's size can be either small, tiny, extra-small, medium, large, extra-large, or huge
* @public
*/
export type SpinnerSize = ValuesOf<typeof SpinnerSize>;
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;
}