Skip to content

feat(spinner): Pre-Release Bootstrap V4.2 spinners #2306

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 33 commits into from
Dec 17, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
5995b14
Create spinner.js
tmorehouse Dec 16, 2018
552fcf0
lint
tmorehouse Dec 16, 2018
a47ef88
Rename src/components/spinners/spinner.js to src/components/spinner/s…
tmorehouse Dec 16, 2018
0e78685
Create _spinner.scss
tmorehouse Dec 16, 2018
6b1c9ae
Create index.scss
tmorehouse Dec 16, 2018
34e77fa
Create index.js
tmorehouse Dec 16, 2018
3c53fd6
lint
tmorehouse Dec 16, 2018
930a984
Create spinner.spec.js
tmorehouse Dec 17, 2018
56c113c
Update index.js
tmorehouse Dec 17, 2018
39c464f
Update index.scss
tmorehouse Dec 17, 2018
d71205a
Create README.md
tmorehouse Dec 17, 2018
df4343e
Create package.json
tmorehouse Dec 17, 2018
fda41a9
Update spinner.spec.js
tmorehouse Dec 17, 2018
e955582
Update spinner.js
tmorehouse Dec 17, 2018
1465ff7
Update index.scss
tmorehouse Dec 17, 2018
ea33d8e
Update README.md
tmorehouse Dec 17, 2018
06fcf18
default inner label to null
tmorehouse Dec 17, 2018
b6aa105
Update spinner.spec.js
tmorehouse Dec 17, 2018
0bb1494
Update README.md
tmorehouse Dec 17, 2018
4ffeb2f
Update README.md
tmorehouse Dec 17, 2018
c4d4b4e
Update README.md
tmorehouse Dec 17, 2018
2513a1d
automate aria-hidden
tmorehouse Dec 17, 2018
736d4d8
Update spinner.spec.js
tmorehouse Dec 17, 2018
8bb4341
Update spinner.spec.js
tmorehouse Dec 17, 2018
ef3921e
Update spinner.spec.js
tmorehouse Dec 17, 2018
c4fb280
Update spinner.js
tmorehouse Dec 17, 2018
ef68a6b
Update spinner.spec.js
tmorehouse Dec 17, 2018
5139d32
Update README.md
tmorehouse Dec 17, 2018
04d22fb
Update README.md
tmorehouse Dec 17, 2018
fd9f095
Update spinner.js
tmorehouse Dec 17, 2018
327d2ec
Update package.json
tmorehouse Dec 17, 2018
82252e7
Update index.js
tmorehouse Dec 17, 2018
3872b24
Update package.json
tmorehouse Dec 17, 2018
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
2 changes: 2 additions & 0 deletions src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import Pagination from './pagination'
import PaginationNav from './pagination-nav'
import Popover from './popover'
import Progress from './progress'
import Spinner from './spinner'
import Table from './table'
import Tabs from './tabs'
import Tooltip from './tooltip'
Expand Down Expand Up @@ -69,6 +70,7 @@ export {
PaginationNav,
Popover,
Progress,
Spinner,
Table,
Tabs,
Tooltip
Expand Down
1 change: 1 addition & 0 deletions src/components/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@
@import "form-input/index";
@import "form-radio/index";
@import "input-group/index";
@import "spinner/index";
@import "table/index";
207 changes: 207 additions & 0 deletions src/components/spinner/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
# Spinners

> The `<b-spinner>` component can be used to show the loading state in your projects. They're rendered
only with basical HTML and CSS as a lightweight Vue functional component. Their appearance, alignment,
and sizing can be easily customized with a few built-in props and/or Bootstrap V4 utility classes.

Spinners can be placed just about anywhere, including inside buttons, alerts, and even `<b-table>`'s busy slot.

```html
<div class="text-center">
<b-spinner label="Spinning"></b-spinner>
<b-spinner type="grow" label="Spinning"></b-spinner>
<b-spinner variant="primary" label="Spinning"></b-spinner>
<b-spinner variant="primary" type="grow" label="Spinning"></b-spinner>
<b-spinner variant="success" label="Spinning"></b-spinner>
<b-spinner variant="success" type="grow" label="Spinning"></b-spinner>
</div>

<!-- spinners.vue -->
```

## Spinner types

Bootstrap V4.2 includes two types of spinners. The default spinner type is called `border`
(spinning circle border), and the optional type `grow` (a throbber style indicator).

### Border spinner
Use the default `border` type spinners for a lightweight loading indicator.

```html
<div>
<b-spinner label="Loading..."></b-spinner>
</div>

<!-- spinner-border.vue -->
```

### Grow spinner
If you don't fancy a `border` spinner, switch to the `grow` spinner by setting the prop `type` to `'grow'`.
While it doesn't technically spin, it does repeatedly grow!

```html
<div>
<b-spinner type="grow" label="Loading..."></b-spinner>
</div>

<!-- spinner-grow.vue -->
```

## Spinner color variants
Spinners use `currentColor` for their color, meaning it inherits the current font color.
You can customize the color using the standard text color variants using the `variant` prop,
or place classes or styles on the component to change it's color.

The `variant` prop translates the variant name to the bootstrap V4 class `.text-{variant}`, so if
you have custom defined text color variants, feel free to use them via the `variant` prop.


```html
<template>
<div>
<div class="text-center mb-2 d-flex justify-content-between">
<b-spinner v-for="variant in variants" :variant="variant" :key="variant"></b-spinner>
</div>
<div class="text-center d-flex justify-content-between">
<b-spinner v-for="variant in variants" :variant="variant" type="grow" :key="variant"></b-spinner>
</div>
</div>
</template>

<script>
export default {
data () {
return {
variants: [
'primary', 'secondary', 'danger', 'warning', 'success', 'info', 'light', 'dark'
]
}
}
}
</script>

<!-- spinner-variants.vue -->
```

**Why not use `border-color` utilities?** Each `border` spinner specifies a `transparent`
border for at least one side, so `.border-{color}` utilities would override that.

## Size
Set the prop `small` to `true` to make a smaller spinner that can quickly be used within other components.

```html
<div>
<b-spinner small label="Small Spinner"></b-spinner>
<b-spinner small type="grow" label="Small Spinner"></b-spinner>
</div>

<!-- spinner-sizes.vue -->
```

Or, use custom CSS or inline styles to change the dimensions as needed.

```html
<div>
<b-spinner style="width: 3rem; height: 3rem;" label="Large Spinner"></b-spinner>
<b-spinner style="width: 3rem; height: 3rem;" type="grow" label="Large Spinner"></b-spinner>
</div>

<!-- spinner-sizes-custom.vue -->
```

## Alignment

Spinners in Bootstrap are built with `rem`s, `currentColor`, and `display: inline-flex`. This means they
can easily be resized, recolored, and quickly aligned.

### Margin

Use margin utilities like `.m-5` for easy spacing.

```html
<div>
<b-spinner class="m-5" label="Busy"></b-spinner>
</div>

<!-- spinner-margin.vue -->
```

## Placement

Use flexbox utilities, float utilities, or text alignment utility classes to place spinners exactly
where you need them in any situation.

### Flex
Using flex utility classes:

```html
<div>
<div class="d-flex justify-content-center mb-3">
<b-spinner label="Loading..."></b-spinner>
</div>
<div class="d-flex align-items-center">
<strong>Loading...</strong>
<b-spinner class="ml-auto"></b-spinner>
</div>
</div>

<!-- spinner-flex.vue -->
```

### Floats
Using float utility classes:

```html
<div>
<div class="clearfix">
<b-spinner class="float-right" label="Floated Right"></b-spinner>
</div>
</div>

<!-- spinner-floats.vue -->
```

### Text align
Using text alignment utility classes:

```html
<div>
<div class="text-center">
<b-spinner variant="primary" label="Text Centered"></b-spinner>
</div>
</div>

<!-- spinner-text-align.vue -->
```

## Spinners in buttons
Use spinners within buttons to indicate an action is currently processing or taking place. You
may also swap the label text out of the spinner element and utilize button text as needed.

```html
<div>
<b-button variant="primary" disabled>
<b-spinner small></b-spinner>
<span class="sr-only">Loading...</span>
</b-button>
<b-button variant="primary" disabled>
<b-spinner small type="grow"></b-spinner>
Loading...
</b-button>
</div>

<!-- spinner-buttons.vue -->
```

## Spinner accessibility
Place a hidden label text inside the spinner for screen reader users, via the `label` prop or `label` slot.
The content will be placed _inside_ the spinner wrapped in a `<span>` element that has the class `sr-only`,
which will make the label available to screen reader users.

For accessibility purposes, each spinner will automatically have a `role="status"` attribute when
a label is provided. You can easily customize the role if required via prop `role`. The specified `role`
will not be applied when no label is provided.

As well, when no label is provided, the spinner will automatically have the attribute `aria-hidden="true"` to hide
the spinner from screen reader users.

68 changes: 68 additions & 0 deletions src/components/spinner/_spinner.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// This SCSS file can be removed once Bootstrap V4.2 is released and Boostrap-Vue is upgraded to use it.

@if variable-exists(spinner-width) == false {
// Conditionally include new SCSS

// Variable defaults
$spinner-width: 2rem !default;
$spinner-height: $spinner-width !default;
$spinner-border-width: .25em !default;

$spinner-width-sm: 1rem !default;
$spinner-height-sm: $spinner-width-sm !default;
$spinner-border-width-sm: .2em !default;

//
// Rotating border
//

@keyframes spinner-border {
to { transform: rotate(360deg); }
}

.spinner-border {
display: inline-block;
width: $spinner-width;
height: $spinner-height;
vertical-align: text-bottom;
border: $spinner-border-width solid currentColor;
border-right-color: transparent;
border-radius: 50%;
animation: spinner-border .75s linear infinite;
}

.spinner-border-sm {
width: $spinner-width-sm;
height: $spinner-height-sm;
border-width: $spinner-border-width-sm;
}

//
// Growing circle
//

@keyframes spinner-grow {
0% {
transform: scale(0);
}
50% {
opacity: 1;
}
}

.spinner-grow {
display: inline-block;
width: $spinner-width;
height: $spinner-height;
vertical-align: text-bottom;
background-color: currentColor;
border-radius: 50%;
opacity: 0;
animation: spinner-grow .75s linear infinite;
}

.spinner-grow-sm {
width: $spinner-width-sm;
height: $spinner-height-sm;
}
}
16 changes: 16 additions & 0 deletions src/components/spinner/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import bSpinner from './spinner'
import { registerComponents, vueUse } from '../../utils/plugins'

const components = {
bSpinner
}

const VuePlugin = {
install (Vue) {
registerComponents(Vue, components)
}
}

vueUse(VuePlugin)

export default VuePlugin
1 change: 1 addition & 0 deletions src/components/spinner/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@import "spinner";
15 changes: 15 additions & 0 deletions src/components/spinner/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "@bootstrap-vue/spinner",
"version": "1.0.0",
"meta": {
"title": "Spinner",
"slug": "spinner",
"component": "bSpinner",
"slots": [
{
"name": "label",
"description": "Content to place in the sr-only label"
}
]
}
}
55 changes: 55 additions & 0 deletions src/components/spinner/spinner.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { mergeData } from 'vue-functional-data-merge'

// @vue/component
export default {
name: 'BSpinner',
functional: true,
props: {
type: {
type: String,
default: 'border' // SCSS currently supports 'border' or 'grow'
},
label: {
type: String,
default: null
},
variant: {
type: String,
default: null
},
small: {
type: Boolean,
default: false
},
role: {
type: String,
default: 'status'
},
tag: {
type: String,
default: 'span'
}
},
render (h, { props, data, slots }) {
let label = h(false)
const hasLabel = slots().label || props.label
if (hasLabel) {
label = h('span', { staticClass: 'sr-only' }, hasLabel)
}
return h(
props.tag,
mergeData(data, {
attrs: {
role: hasLabel ? (props.role || 'status') : null,
'aria-hidden': hasLabel ? null : 'true'
},
class: {
[`spinner-${props.type}`]: Boolean(props.type),
[`spinner-${props.type}-sm`]: props.small,
[`text-${props.variant}`]: Boolean(props.variant)
}
}),
[label]
)
}
}
Loading