From e33cf854a97f2c249a812bdcd55e473fe767eed6 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Thu, 8 Aug 2019 16:08:13 -0500 Subject: [PATCH] feat(spinner): add circular spinner for MD default (#19052) --- core/api.txt | 8 +- .../infinite-scroll-content/readme.md | 8 +- core/src/components/loading/readme.md | 28 +++--- .../components/refresher-content/readme.md | 12 +-- core/src/components/spinner/readme.md | 12 +-- .../src/components/spinner/spinner-configs.ts | 88 +++++++++++-------- .../components/spinner/spinner-interface.ts | 5 ++ core/src/components/spinner/spinner.scss | 42 ++++++++- core/src/components/spinner/spinner.tsx | 41 +++++---- .../components/spinner/test/basic/index.html | 36 +++++--- .../components/spinner/test/color/index.html | 35 ++++---- .../spinner/test/standalone/index.html | 3 +- 12 files changed, 200 insertions(+), 118 deletions(-) diff --git a/core/api.txt b/core/api.txt index d23334bb6b..519178c90b 100644 --- a/core/api.txt +++ b/core/api.txt @@ -417,7 +417,7 @@ ion-infinite-scroll,method,complete,complete() => Promise ion-infinite-scroll,event,ionInfinite,void,true ion-infinite-scroll-content,none -ion-infinite-scroll-content,prop,loadingSpinner,"bubbles" | "circles" | "crescent" | "dots" | "lines" | "lines-small" | null | undefined,undefined,false,false +ion-infinite-scroll-content,prop,loadingSpinner,"bubbles" | "circles" | "circular" | "crescent" | "dots" | "lines" | "lines-small" | null | undefined,undefined,false,false ion-infinite-scroll-content,prop,loadingText,string | undefined,undefined,false,false ion-input,scoped @@ -585,7 +585,7 @@ ion-loading,prop,leaveAnimation,((Animation: Animation, baseEl: any, opts?: any) ion-loading,prop,message,string | undefined,undefined,false,false ion-loading,prop,mode,"ios" | "md",undefined,false,false ion-loading,prop,showBackdrop,boolean,true,false,false -ion-loading,prop,spinner,"bubbles" | "circles" | "crescent" | "dots" | "lines" | "lines-small" | null | undefined,undefined,false,false +ion-loading,prop,spinner,"bubbles" | "circles" | "circular" | "crescent" | "dots" | "lines" | "lines-small" | null | undefined,undefined,false,false ion-loading,prop,translucent,boolean,false,false,false ion-loading,method,dismiss,dismiss(data?: any, role?: string | undefined) => Promise ion-loading,method,onDidDismiss,onDidDismiss() => Promise> @@ -892,7 +892,7 @@ ion-refresher,event,ionStart,void,true ion-refresher-content,none ion-refresher-content,prop,pullingIcon,null | string | undefined,undefined,false,false ion-refresher-content,prop,pullingText,string | undefined,undefined,false,false -ion-refresher-content,prop,refreshingSpinner,"bubbles" | "circles" | "crescent" | "dots" | "lines" | "lines-small" | null | undefined,undefined,false,false +ion-refresher-content,prop,refreshingSpinner,"bubbles" | "circles" | "circular" | "crescent" | "dots" | "lines" | "lines-small" | null | undefined,undefined,false,false ion-refresher-content,prop,refreshingText,string | undefined,undefined,false,false ion-reorder,shadow @@ -1098,7 +1098,7 @@ ion-slides,css-prop,--bullet-background-active ion-spinner,shadow ion-spinner,prop,color,string | undefined,undefined,false,false ion-spinner,prop,duration,number | undefined,undefined,false,false -ion-spinner,prop,name,"bubbles" | "circles" | "crescent" | "dots" | "lines" | "lines-small" | undefined,undefined,false,false +ion-spinner,prop,name,"bubbles" | "circles" | "circular" | "crescent" | "dots" | "lines" | "lines-small" | undefined,undefined,false,false ion-spinner,prop,paused,boolean,false,false,false ion-spinner,css-prop,--color diff --git a/core/src/components/infinite-scroll-content/readme.md b/core/src/components/infinite-scroll-content/readme.md index b2e4180145..ee3ca1caf6 100644 --- a/core/src/components/infinite-scroll-content/readme.md +++ b/core/src/components/infinite-scroll-content/readme.md @@ -58,10 +58,10 @@ The `ion-infinite-scroll-content` component is not supported in React. ## Properties -| Property | Attribute | Description | Type | Default | -| ---------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ----------- | -| `loadingSpinner` | `loading-spinner` | An animated SVG spinner that shows while loading. | `"bubbles" \| "circles" \| "crescent" \| "dots" \| "lines" \| "lines-small" \| null \| undefined` | `undefined` | -| `loadingText` | `loading-text` | Optional text to display while loading. `loadingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `` would become `<Ionic>` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) | `string \| undefined` | `undefined` | +| Property | Attribute | Description | Type | Default | +| ---------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | ----------- | +| `loadingSpinner` | `loading-spinner` | An animated SVG spinner that shows while loading. | `"bubbles" \| "circles" \| "circular" \| "crescent" \| "dots" \| "lines" \| "lines-small" \| null \| undefined` | `undefined` | +| `loadingText` | `loading-text` | Optional text to display while loading. `loadingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `` would become `<Ionic>` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) | `string \| undefined` | `undefined` | ## Dependencies diff --git a/core/src/components/loading/readme.md b/core/src/components/loading/readme.md index fa1b414454..7c4191d2e4 100644 --- a/core/src/components/loading/readme.md +++ b/core/src/components/loading/readme.md @@ -174,20 +174,20 @@ export default { ## Properties -| Property | Attribute | Description | Type | Default | -| ----------------- | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ----------- | -| `animated` | `animated` | If `true`, the loading indicator will animate. | `boolean` | `true` | -| `backdropDismiss` | `backdrop-dismiss` | If `true`, the loading indicator will be dismissed when the backdrop is clicked. | `boolean` | `false` | -| `cssClass` | `css-class` | Additional classes to apply for custom CSS. If multiple classes are provided they should be separated by spaces. | `string \| string[] \| undefined` | `undefined` | -| `duration` | `duration` | Number of milliseconds to wait before dismissing the loading indicator. | `number` | `0` | -| `enterAnimation` | -- | Animation to use when the loading indicator is presented. | `((Animation: Animation, baseEl: any, opts?: any) => Promise) \| undefined` | `undefined` | -| `keyboardClose` | `keyboard-close` | If `true`, the keyboard will be automatically dismissed when the overlay is presented. | `boolean` | `true` | -| `leaveAnimation` | -- | Animation to use when the loading indicator is dismissed. | `((Animation: Animation, baseEl: any, opts?: any) => Promise) \| undefined` | `undefined` | -| `message` | `message` | Optional text content to display in the loading indicator. | `string \| undefined` | `undefined` | -| `mode` | `mode` | The mode determines which platform styles to use. | `"ios" \| "md"` | `undefined` | -| `showBackdrop` | `show-backdrop` | If `true`, a backdrop will be displayed behind the loading indicator. | `boolean` | `true` | -| `spinner` | `spinner` | The name of the spinner to display. | `"bubbles" \| "circles" \| "crescent" \| "dots" \| "lines" \| "lines-small" \| null \| undefined` | `undefined` | -| `translucent` | `translucent` | If `true`, the loading indicator will be translucent. Only applies when the mode is `"ios"` and the device supports [`backdrop-filter`](https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter#Browser_compatibility). | `boolean` | `false` | +| Property | Attribute | Description | Type | Default | +| ----------------- | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | ----------- | +| `animated` | `animated` | If `true`, the loading indicator will animate. | `boolean` | `true` | +| `backdropDismiss` | `backdrop-dismiss` | If `true`, the loading indicator will be dismissed when the backdrop is clicked. | `boolean` | `false` | +| `cssClass` | `css-class` | Additional classes to apply for custom CSS. If multiple classes are provided they should be separated by spaces. | `string \| string[] \| undefined` | `undefined` | +| `duration` | `duration` | Number of milliseconds to wait before dismissing the loading indicator. | `number` | `0` | +| `enterAnimation` | -- | Animation to use when the loading indicator is presented. | `((Animation: Animation, baseEl: any, opts?: any) => Promise) \| undefined` | `undefined` | +| `keyboardClose` | `keyboard-close` | If `true`, the keyboard will be automatically dismissed when the overlay is presented. | `boolean` | `true` | +| `leaveAnimation` | -- | Animation to use when the loading indicator is dismissed. | `((Animation: Animation, baseEl: any, opts?: any) => Promise) \| undefined` | `undefined` | +| `message` | `message` | Optional text content to display in the loading indicator. | `string \| undefined` | `undefined` | +| `mode` | `mode` | The mode determines which platform styles to use. | `"ios" \| "md"` | `undefined` | +| `showBackdrop` | `show-backdrop` | If `true`, a backdrop will be displayed behind the loading indicator. | `boolean` | `true` | +| `spinner` | `spinner` | The name of the spinner to display. | `"bubbles" \| "circles" \| "circular" \| "crescent" \| "dots" \| "lines" \| "lines-small" \| null \| undefined` | `undefined` | +| `translucent` | `translucent` | If `true`, the loading indicator will be translucent. Only applies when the mode is `"ios"` and the device supports [`backdrop-filter`](https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter#Browser_compatibility). | `boolean` | `false` | ## Events diff --git a/core/src/components/refresher-content/readme.md b/core/src/components/refresher-content/readme.md index ded005be06..16a9354ff0 100644 --- a/core/src/components/refresher-content/readme.md +++ b/core/src/components/refresher-content/readme.md @@ -9,12 +9,12 @@ The refresher content contains the text, icon and spinner to display during a pu ## Properties -| Property | Attribute | Description | Type | Default | -| ------------------- | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ----------- | -| `pullingIcon` | `pulling-icon` | A static icon to display when you begin to pull down | `null \| string \| undefined` | `undefined` | -| `pullingText` | `pulling-text` | The text you want to display when you begin to pull down. `pullingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `` would become `<Ionic>` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) | `string \| undefined` | `undefined` | -| `refreshingSpinner` | `refreshing-spinner` | An animated SVG spinner that shows when refreshing begins | `"bubbles" \| "circles" \| "crescent" \| "dots" \| "lines" \| "lines-small" \| null \| undefined` | `undefined` | -| `refreshingText` | `refreshing-text` | The text you want to display when performing a refresh. `refreshingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `` would become `<Ionic>` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) | `string \| undefined` | `undefined` | +| Property | Attribute | Description | Type | Default | +| ------------------- | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | ----------- | +| `pullingIcon` | `pulling-icon` | A static icon to display when you begin to pull down | `null \| string \| undefined` | `undefined` | +| `pullingText` | `pulling-text` | The text you want to display when you begin to pull down. `pullingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `` would become `<Ionic>` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) | `string \| undefined` | `undefined` | +| `refreshingSpinner` | `refreshing-spinner` | An animated SVG spinner that shows when refreshing begins | `"bubbles" \| "circles" \| "circular" \| "crescent" \| "dots" \| "lines" \| "lines-small" \| null \| undefined` | `undefined` | +| `refreshingText` | `refreshing-text` | The text you want to display when performing a refresh. `refreshingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `` would become `<Ionic>` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) | `string \| undefined` | `undefined` | ## Dependencies diff --git a/core/src/components/spinner/readme.md b/core/src/components/spinner/readme.md index 4e2fe43235..935523fa33 100644 --- a/core/src/components/spinner/readme.md +++ b/core/src/components/spinner/readme.md @@ -110,12 +110,12 @@ export const SpinnerExample: React.FunctionComponent = () => ( ## Properties -| Property | Attribute | Description | Type | Default | -| ---------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | ----------- | -| `color` | `color` | The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics). | `string \| undefined` | `undefined` | -| `duration` | `duration` | Duration of the spinner animation in milliseconds. The default varies based on the spinner. | `number \| undefined` | `undefined` | -| `name` | `name` | The name of the SVG spinner to use. If a name is not provided, the platform's default spinner will be used. | `"bubbles" \| "circles" \| "crescent" \| "dots" \| "lines" \| "lines-small" \| undefined` | `undefined` | -| `paused` | `paused` | If `true`, the spinner's animation will be paused. | `boolean` | `false` | +| Property | Attribute | Description | Type | Default | +| ---------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | ----------- | +| `color` | `color` | The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics). | `string \| undefined` | `undefined` | +| `duration` | `duration` | Duration of the spinner animation in milliseconds. The default varies based on the spinner. | `number \| undefined` | `undefined` | +| `name` | `name` | The name of the SVG spinner to use. If a name is not provided, the platform's default spinner will be used. | `"bubbles" \| "circles" \| "circular" \| "crescent" \| "dots" \| "lines" \| "lines-small" \| undefined` | `undefined` | +| `paused` | `paused` | If `true`, the spinner's animation will be paused. | `boolean` | `false` | ## CSS Custom Properties diff --git a/core/src/components/spinner/spinner-configs.ts b/core/src/components/spinner/spinner-configs.ts index 0bae7f258c..07ac173fbf 100644 --- a/core/src/components/spinner/spinner-configs.ts +++ b/core/src/components/spinner/spinner-configs.ts @@ -2,41 +2,6 @@ import { SpinnerConfigs } from './spinner-interface'; const spinners = { - 'lines': { - dur: 1000, - lines: 12, - fn: (dur: number, index: number, total: number) => { - const transform = `rotate(${ 30 * index + (index < 6 ? 180 : -180) }deg)`; - const animationDelay = `${ (dur * index / total) - dur }ms`; - - return { - y1: 17, - y2: 29, - style: { - 'transform': transform, - 'animation-delay': animationDelay, - } - }; - } - }, - - 'lines-small': { - dur: 1000, - lines: 12, - fn: (dur: number, index: number, total: number) => { - const transform = `rotate(${30 * index + (index < 6 ? 180 : -180)}deg)`; - const animationDelay = `${ (dur * index / total) - dur }ms`; - return { - y1: 12, - y2: 20, - style: { - 'transform': transform, - 'animation-delay': animationDelay, - } - }; - } - }, - 'bubbles': { dur: 1000, circles: 9, @@ -72,6 +37,23 @@ const spinners = { } }, + 'circular': { + dur: 1400, + elmDuration: true, + circles: 1, + fn: () => { + return { + r: 20, + cx: 44, + cy: 44, + fill: 'none', + viewBox: '22 22 44 44', + transform: 'translate(0,0)', + style: {} + }; + } + }, + 'crescent': { dur: 750, circles: 1, @@ -96,7 +78,43 @@ const spinners = { } }; } + }, + + 'lines': { + dur: 1000, + lines: 12, + fn: (dur: number, index: number, total: number) => { + const transform = `rotate(${ 30 * index + (index < 6 ? 180 : -180) }deg)`; + const animationDelay = `${ (dur * index / total) - dur }ms`; + + return { + y1: 17, + y2: 29, + style: { + 'transform': transform, + 'animation-delay': animationDelay, + } + }; + } + }, + + 'lines-small': { + dur: 1000, + lines: 12, + fn: (dur: number, index: number, total: number) => { + const transform = `rotate(${30 * index + (index < 6 ? 180 : -180)}deg)`; + const animationDelay = `${ (dur * index / total) - dur }ms`; + return { + y1: 12, + y2: 20, + style: { + 'transform': transform, + 'animation-delay': animationDelay, + } + }; + } } + }; export const SPINNERS: SpinnerConfigs = spinners; diff --git a/core/src/components/spinner/spinner-interface.ts b/core/src/components/spinner/spinner-interface.ts index 64f193b6de..43bf24b752 100644 --- a/core/src/components/spinner/spinner-interface.ts +++ b/core/src/components/spinner/spinner-interface.ts @@ -9,6 +9,7 @@ export interface SpinnerConfig { dur: number; circles?: number; lines?: number; + elmDuration?: boolean; fn: (dur: number, index: number, total: number) => SpinnerData; } @@ -16,5 +17,9 @@ export interface SpinnerData { r?: number; y1?: number; y2?: number; + cx?: number; + cy?: number; style: any; + viewBox?: string; + transform?: string; } diff --git a/core/src/components/spinner/spinner.scss b/core/src/components/spinner/spinner.scss index 3f78d98672..ea1b75b4a3 100644 --- a/core/src/components/spinner/spinner.scss +++ b/core/src/components/spinner/spinner.scss @@ -99,10 +99,29 @@ svg { } +// Spinner: circular +// -------------------------------------------------- + +:host(.spinner-circular) { + animation: spinner-circular linear infinite; +} + +:host(.spinner-circular) circle { + animation: spinner-circular-inner ease-in-out infinite; + stroke: currentColor; + stroke-dasharray: 80px, 200px; + stroke-dashoffset: 0px; + stroke-width: 3.6; + fill: none; +} + + // Spinner: paused // -------------------------------------------------- -:host(.spinner-paused) svg { +:host(.spinner-paused), +:host(.spinner-paused) svg, +:host(.spinner-paused) circle { animation-play-state: paused; } @@ -162,3 +181,24 @@ svg { opacity: .9; } } + +@keyframes spinner-circular { + 100% { + transform: rotate(360deg); + } +} + +@keyframes spinner-circular-inner { + 0% { + stroke-dasharray: 1px, 200px; + stroke-dashoffset: 0px; + } + 50% { + stroke-dasharray: 100px, 200px; + stroke-dashoffset: -15px; + } + 100% { + stroke-dasharray: 100px, 200px; + stroke-dashoffset: -125px; + } +} diff --git a/core/src/components/spinner/spinner.tsx b/core/src/components/spinner/spinner.tsx index 85cc88bdb8..f0bf0d78dd 100644 --- a/core/src/components/spinner/spinner.tsx +++ b/core/src/components/spinner/spinner.tsx @@ -38,19 +38,20 @@ export class Spinner implements ComponentInterface { @Prop() paused = false; private getName(): SpinnerTypes { - const name = this.name || config.get('spinner'); + const spinnerName = this.name || config.get('spinner'); const mode = getIonMode(this); - if (name) { - return name; + if (spinnerName) { + return spinnerName; } - return (mode === 'ios') ? 'lines' : 'crescent'; + return (mode === 'ios') ? 'lines' : 'circular'; } render() { - const mode = getIonMode(this); - const name = this.getName(); - const spinner = SPINNERS[name] || SPINNERS['lines']; - const duration = (typeof this.duration === 'number' && this.duration > 10 ? this.duration : spinner.dur); + const self = this; + const mode = getIonMode(self); + const spinnerName = self.getName(); + const spinner = SPINNERS[spinnerName] || SPINNERS['lines']; + const duration = (typeof self.duration === 'number' && self.duration > 10 ? self.duration : spinner.dur); const svgs: any[] = []; if (spinner.circles !== undefined) { @@ -67,11 +68,13 @@ export class Spinner implements ComponentInterface { return ( {svgs} @@ -81,21 +84,27 @@ export class Spinner implements ComponentInterface { const buildCircle = (spinner: SpinnerConfig, duration: number, index: number, total: number) => { const data = spinner.fn(duration, index, total); - data.style['animation-duration'] = `${duration}ms`; + data.style['animation-duration'] = duration + 'ms'; return ( - - + + ); }; const buildLine = (spinner: SpinnerConfig, duration: number, index: number, total: number) => { const data = spinner.fn(duration, index, total); - data.style['animation-duration'] = `${duration}ms`; + data.style['animation-duration'] = duration + 'ms'; return ( - + ); diff --git a/core/src/components/spinner/test/basic/index.html b/core/src/components/spinner/test/basic/index.html index 7a9d81f1ca..50b151cabe 100644 --- a/core/src/components/spinner/test/basic/index.html +++ b/core/src/components/spinner/test/basic/index.html @@ -27,62 +27,70 @@ - Default Spinner + Platform Default Spinner - Lines + lines - Lines Small + lines-small + + + + circular - Dots + dots - Bubbles + bubbles - Circles + circles - Crescent + crescent - Paused Default Spinner + Paused Platform Default Spinner - Paused Lines + lines - Paused Lines Small + lines-small + + + + circular - Paused Dots + dots - Paused Bubbles + bubbles - Paused Circles + circles - Paused Crescent + crescent diff --git a/core/src/components/spinner/test/color/index.html b/core/src/components/spinner/test/color/index.html index 10e829a83a..16e61c2f69 100644 --- a/core/src/components/spinner/test/color/index.html +++ b/core/src/components/spinner/test/color/index.html @@ -22,40 +22,41 @@ - - Spinner Loading Indicators - - Show Default Spinner + Platform Default Spinner - Show Lines (Primary) + lines (primary) - - Show Lines Small (Secondary) + + lines-small (secondary) - - Show Dots (Danger) + + circular (tertiary) - - Show Bubbles (Light) + + dots (success) - - Show Circles (Dark) + + bubbles (warning) - - Show Crescent (Primary) + + circles (danger) - - Show Paused Default Spinner (Secondary) + + crescent (dark) + + + + lines (paused, medium) diff --git a/core/src/components/spinner/test/standalone/index.html b/core/src/components/spinner/test/standalone/index.html index b3c0627936..5d98d92d0e 100644 --- a/core/src/components/spinner/test/standalone/index.html +++ b/core/src/components/spinner/test/standalone/index.html @@ -14,7 +14,8 @@ - + +