From 734ea42ff6bc2f767a3ad3e809ff4ce57617c54c Mon Sep 17 00:00:00 2001 From: David Wheatley Date: Fri, 9 Apr 2021 00:42:32 +0100 Subject: [PATCH] Replace spin.js with a CSS-only loading spinner (#2764) * Create CSS only loading indicator * Core mods to fix Loading Indicator usage * Remove extra whitespace * Attrs interface extends ComponentAttrs and is exported * Add doc block about custom styling --- .../src/common/components/LoadingIndicator.js | 43 ------------- .../common/components/LoadingIndicator.tsx | 61 +++++++++++++++++++ framework/core/less/common/Checkbox.less | 26 ++++++-- .../core/less/common/LoadingIndicator.less | 61 ++++++++++++++++--- framework/core/less/forum/ActivityPage.less | 2 +- framework/core/less/forum/DiscussionList.less | 7 ++- 6 files changed, 140 insertions(+), 60 deletions(-) delete mode 100644 framework/core/js/src/common/components/LoadingIndicator.js create mode 100644 framework/core/js/src/common/components/LoadingIndicator.tsx diff --git a/framework/core/js/src/common/components/LoadingIndicator.js b/framework/core/js/src/common/components/LoadingIndicator.js deleted file mode 100644 index 918c6f820..000000000 --- a/framework/core/js/src/common/components/LoadingIndicator.js +++ /dev/null @@ -1,43 +0,0 @@ -import Component from '../Component'; -import { Spinner } from 'spin.js'; - -/** - * The `LoadingIndicator` component displays a loading spinner with spin.js. - * - * ### Attrs - * - * - `size` The spin.js size preset to use. Defaults to 'small'. - * - * All other attrs will be assigned as attributes on the DOM element. - */ -export default class LoadingIndicator extends Component { - view() { - const attrs = Object.assign({}, this.attrs); - - attrs.className = 'LoadingIndicator ' + (attrs.className || ''); - delete attrs.size; - - return
{m.trust(' ')}
; - } - - oncreate(vnode) { - super.oncreate(vnode); - - const options = { zIndex: 'auto', color: this.$().css('color') }; - - switch (this.attrs.size) { - case 'large': - Object.assign(options, { lines: 10, length: 8, width: 4, radius: 8 }); - break; - - case 'tiny': - Object.assign(options, { lines: 8, length: 2, width: 2, radius: 3 }); - break; - - default: - Object.assign(options, { lines: 8, length: 4, width: 3, radius: 5 }); - } - - new Spinner(options).spin(this.element); - } -} diff --git a/framework/core/js/src/common/components/LoadingIndicator.tsx b/framework/core/js/src/common/components/LoadingIndicator.tsx new file mode 100644 index 000000000..f781f6d1f --- /dev/null +++ b/framework/core/js/src/common/components/LoadingIndicator.tsx @@ -0,0 +1,61 @@ +import Component, { ComponentAttrs } from '../Component'; +import classList from '../utils/classList'; + +export interface LoadingIndicatorAttrs extends ComponentAttrs { + /** + * Custom classes fro the loading indicator's container. + */ + className?: string; + /** + * Custom classes for the loading indicator's container. + */ + containerClassName?: string; + /** + * Optional size to specify for the loading indicator. + */ + size?: 'large' | 'medium' | 'small'; + /** + * Optional attributes to apply to the loading indicator's container. + */ + containerAttrs?: Partial; +} + +/** + * The `LoadingIndicator` component displays a simple CSS-based loading spinner. + * + * To set a custom color, use the CSS `color` property. + * + * To increase spacing around the spinner, use the CSS `height` property on the + * spinner's **container**. + * + * To apply a custom size to the loading indicator, set the `--size` and + * `--thickness` custom properties on the loading indicator itself. + * + * If you really want to change how this looks as part of your custom theme, + * you can override the `border-radius` and `border` then set either a + * background image, or use `content: "\"` (e.g. `content: "\f1ce"`) + * and `font-family: 'Font Awesome 5 Free'` to set an FA icon if you'd rather. + * + * ### Attrs + * + * - `containerClassName` Class name(s) to apply to the indicator's parent + * - `className` Class name(s) to apply to the indicator itself + * - `size` Size of the loading indicator + * - `containerAttrs` Optional attrs to be applied to the container DOM element + * + * All other attrs will be assigned as attributes on the DOM element. + */ +export default class LoadingIndicator extends Component { + view() { + const { size, ...attrs } = this.attrs; + + attrs.className = classList({ LoadingIndicator: true, [attrs.className || '']: true }); + attrs.containerClassName = classList({ 'LoadingIndicator-container': true, [attrs.containerClassName || '']: true }); + + return ( +
+
+
+ ); + } +} diff --git a/framework/core/less/common/Checkbox.less b/framework/core/less/common/Checkbox.less index 1b1b29613..a7dbac58b 100644 --- a/framework/core/less/common/Checkbox.less +++ b/framework/core/less/common/Checkbox.less @@ -15,13 +15,9 @@ float: left; margin-left: -65px; margin-top: -4px; - - .LoadingIndicator { - display: inline-block; - margin-left: 20px; - } } } + .Checkbox--switch .Checkbox-display { width: 50px; height: 28px; @@ -31,8 +27,28 @@ background: @control-bg; .transition(background-color 0.2s); + .LoadingIndicator { + --size: 22px !important; + + &-container { + height: 22px; + } + } + .on& { background: #58a400; + + .LoadingIndicator-container { + // Show loading indicator over the switch button + justify-content: flex-end; + } + } + + .off& { + .LoadingIndicator-container { + // Show loading indicator over the switch button + justify-content: flex-start; + } } &:before { diff --git a/framework/core/less/common/LoadingIndicator.less b/framework/core/less/common/LoadingIndicator.less index 3f53505a3..0446e5f1a 100644 --- a/framework/core/less/common/LoadingIndicator.less +++ b/framework/core/less/common/LoadingIndicator.less @@ -2,13 +2,58 @@ // Loading Indicators .LoadingIndicator { - position: relative; - color: @muted-color; + @spin-time: 750ms; + --size: 24px; + --thickness: 2px; + + &-container[data-size="large"] & { + --size: 32px; + --thickness: 3px; + } + + &-container[data-size="tiny"] & { + --size: 18px; + } + + // Use the value of `color` to maintain backwards compatibility + border-color: currentColor; + border-width: var(--thickness); + border-style: solid; + border-top-color: transparent; + border-radius: 50%; + + width: var(--size); + height: var(--size); + + animation: spin @spin-time linear infinite; + + //
container around the spinner + // Used for positioning + &-container { + color: @muted-color; + + // Center vertically and horizontally + // Allows people to set `height` and it'll stay centered within the new height + display: flex; + align-items: center; + justify-content: center; + + &--block { + height: 100px; + } + + &--inline { + display: inline-block; + } + } } -.LoadingIndicator--inline { - display: inline-block; - width: 25px; -} -.LoadingIndicator--block { - height: 100px; + +@keyframes spin { + from { + transform: rotate(0); + } + + to { + transform: rotate(1turn); + } } diff --git a/framework/core/less/forum/ActivityPage.less b/framework/core/less/forum/ActivityPage.less index feea2b20a..d051c753e 100644 --- a/framework/core/less/forum/ActivityPage.less +++ b/framework/core/less/forum/ActivityPage.less @@ -2,7 +2,7 @@ text-align: center; margin-top: 10px; - .LoadingIndicator { + .LoadingIndicator-container { height: 46px; } } diff --git a/framework/core/less/forum/DiscussionList.less b/framework/core/less/forum/DiscussionList.less index f6a0d768d..2ca828234 100644 --- a/framework/core/less/forum/DiscussionList.less +++ b/framework/core/less/forum/DiscussionList.less @@ -10,9 +10,10 @@ .DiscussionList-loadMore { text-align: center; margin-top: 10px; -} -.DiscussionList-loadMore .LoadingIndicator { - height: 46px; + + .LoadingIndicator-container { + height: 46px; + } } @media @phone {