Add Muriel Styled Spinner (https://github.com/woocommerce/woocommerce-admin/pull/2289)
* Add Muriel Styled Spinner * Handle PR feedback: Fix ordering of example components, fix tabbing/spacing in stepper example file, and fix base muriel colors for the stepper and profile wizard.
This commit is contained in:
parent
0e1db89f67
commit
c921593cfd
|
@ -25,6 +25,28 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.woocommerce-stepper .woocommerce-stepper__step {
|
||||||
|
.woocommerce-stepper__step-label {
|
||||||
|
color: $muriel-gray-800;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-active,
|
||||||
|
&.is-complete {
|
||||||
|
.woocommerce-stepper__step-icon {
|
||||||
|
background: $muriel-gray-900;
|
||||||
|
color: $muriel-white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.woocommerce-stepper__step-label {
|
||||||
|
color: $muriel-gray-900;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.woocommerce-spinner__circle {
|
||||||
|
stroke: $muriel-gray-900;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.woocommerce-profile-wizard__header {
|
.woocommerce-profile-wizard__header {
|
||||||
height: 56px;
|
height: 56px;
|
||||||
border-bottom: 1px solid $muriel-gray-50;
|
border-bottom: 1px solid $muriel-gray-50;
|
||||||
|
@ -111,6 +133,7 @@
|
||||||
.woocommerce-profile-wizard__plugins-actions {
|
.woocommerce-profile-wizard__plugins-actions {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
margin-left: 64px;
|
margin-left: 64px;
|
||||||
|
min-height: 28px;
|
||||||
|
|
||||||
button {
|
button {
|
||||||
display: initial;
|
display: initial;
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
{ "component": "SearchListControl" },
|
{ "component": "SearchListControl" },
|
||||||
{ "component": "Section" },
|
{ "component": "Section" },
|
||||||
{ "component": "SegmentedSelection" },
|
{ "component": "SegmentedSelection" },
|
||||||
|
{ "component": "Spinner" },
|
||||||
{ "component": "SplitButton" },
|
{ "component": "SplitButton" },
|
||||||
{ "component": "Stepper" },
|
{ "component": "Stepper" },
|
||||||
{ "component": "Summary", "render": "MySummaryList" },
|
{ "component": "Summary", "render": "MySummaryList" },
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
- EllipsisMenu component (breaking change): Remove `children` prop in favor of a render prop `renderContent` so that function arguments `isOpen`, `onToggle`, and `onClose` can be passed down.
|
- EllipsisMenu component (breaking change): Remove `children` prop in favor of a render prop `renderContent` so that function arguments `isOpen`, `onToggle`, and `onClose` can be passed down.
|
||||||
- Chart has a new prop named `yBelow1Format` which overrides the `yFormat` for values between -1 and 1 (not included).
|
- Chart has a new prop named `yBelow1Format` which overrides the `yFormat` for values between -1 and 1 (not included).
|
||||||
- Add new component `<Stepper />` for showing a list of steps and progress.
|
- Add new component `<Stepper />` for showing a list of steps and progress.
|
||||||
|
- Add new `<Spinner />` component.
|
||||||
- Card component: updated default Muriel design.
|
- Card component: updated default Muriel design.
|
||||||
- Card component: new `description` prop.
|
- Card component: new `description` prop.
|
||||||
- Card component: new `isInactive` prop.
|
- Card component: new `isInactive` prop.
|
||||||
|
|
|
@ -40,6 +40,7 @@ export { default as SearchListItem } from './search-list-control/item';
|
||||||
export { default as SectionHeader } from './section-header';
|
export { default as SectionHeader } from './section-header';
|
||||||
export { default as SegmentedSelection } from './segmented-selection';
|
export { default as SegmentedSelection } from './segmented-selection';
|
||||||
export { default as SplitButton } from './split-button';
|
export { default as SplitButton } from './split-button';
|
||||||
|
export { default as Spinner } from './spinner';
|
||||||
export { default as Stepper } from './stepper';
|
export { default as Stepper } from './stepper';
|
||||||
export { default as SummaryList } from './summary';
|
export { default as SummaryList } from './summary';
|
||||||
export { default as SummaryListPlaceholder } from './summary/placeholder';
|
export { default as SummaryListPlaceholder } from './summary/placeholder';
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
```jsx
|
||||||
|
import { MySpinner } from '@woocommerce/components';
|
||||||
|
|
||||||
|
const MySpinner = () => (
|
||||||
|
<div>
|
||||||
|
<Spinner />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
```
|
|
@ -0,0 +1,39 @@
|
||||||
|
/** @format */
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { Component } from '@wordpress/element';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import classnames from 'classnames';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spinner - An indeterminate circular progress indicator.
|
||||||
|
*/
|
||||||
|
class Spinner extends Component {
|
||||||
|
render() {
|
||||||
|
const { className } = this.props;
|
||||||
|
const classes = classnames( 'woocommerce-spinner', className );
|
||||||
|
return (
|
||||||
|
<svg className={ classes } viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<circle
|
||||||
|
className="woocommerce-spinner__circle"
|
||||||
|
fill="none"
|
||||||
|
strokeWidth="5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
cx="50"
|
||||||
|
cy="50"
|
||||||
|
r="30"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Spinner.propTypes = {
|
||||||
|
/**
|
||||||
|
* Additional class name to style the component.
|
||||||
|
*/
|
||||||
|
className: PropTypes.string,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Spinner;
|
|
@ -0,0 +1,38 @@
|
||||||
|
@keyframes rotate {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(270deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes growAndShrink {
|
||||||
|
0%,
|
||||||
|
100% {
|
||||||
|
stroke-dashoffset: 200;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
stroke-dashoffset: 50;
|
||||||
|
transform: rotate(135deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(450deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.woocommerce-spinner {
|
||||||
|
animation: rotate 2s linear infinite;
|
||||||
|
width: 40px;
|
||||||
|
min-width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
max-height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.woocommerce-spinner__circle {
|
||||||
|
stroke-dasharray: 200;
|
||||||
|
stroke-dashoffset: 0;
|
||||||
|
transform-origin: center;
|
||||||
|
animation: growAndShrink 2s ease-in-out infinite;
|
||||||
|
stroke: $muriel-gray-900;
|
||||||
|
}
|
|
@ -2,73 +2,82 @@
|
||||||
import { Stepper } from '@woocommerce/components';
|
import { Stepper } from '@woocommerce/components';
|
||||||
|
|
||||||
const MyStepper = withState( {
|
const MyStepper = withState( {
|
||||||
currentStep: 'first',
|
currentStep: 'first',
|
||||||
isComplete: false,
|
isComplete: false,
|
||||||
} )( ( { currentStep, isComplete, setState } ) => {
|
isPending: false,
|
||||||
const steps = [
|
} )( ( { currentStep, isComplete, isPending, setState } ) => {
|
||||||
{
|
const steps = [
|
||||||
label: 'First',
|
{
|
||||||
key: 'first',
|
label: 'First',
|
||||||
},
|
key: 'first',
|
||||||
{
|
},
|
||||||
label: 'Second',
|
{
|
||||||
key: 'second',
|
label: 'Second',
|
||||||
},
|
key: 'second',
|
||||||
{
|
},
|
||||||
label: 'Third',
|
{
|
||||||
key: 'third',
|
label: 'Third',
|
||||||
},
|
key: 'third',
|
||||||
{
|
},
|
||||||
label: 'Fourth',
|
{
|
||||||
key: 'fourth',
|
label: 'Fourth',
|
||||||
},
|
key: 'fourth',
|
||||||
];
|
},
|
||||||
const currentIndex = steps.findIndex( s => currentStep === s.key );
|
];
|
||||||
|
|
||||||
if ( isComplete ) {
|
const currentIndex = steps.findIndex( s => currentStep === s.key );
|
||||||
steps.forEach( s => s.isComplete = true );
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
if ( isComplete ) {
|
||||||
<div>
|
steps.forEach( s => s.isComplete = true );
|
||||||
{ isComplete ? (
|
}
|
||||||
<button onClick={ () => setState( { currentStep: 'first', isComplete: false } ) } >
|
|
||||||
Reset
|
|
||||||
</button>
|
|
||||||
) : (
|
|
||||||
<div>
|
|
||||||
<button
|
|
||||||
onClick={ () => setState( { currentStep: steps[ currentIndex - 1 ]['key'] } ) }
|
|
||||||
disabled={ currentIndex < 1 }
|
|
||||||
>
|
|
||||||
Previous step
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={ () => setState( { currentStep: steps[ currentIndex + 1 ]['key'] } ) }
|
|
||||||
disabled={ currentIndex >= steps.length - 1 }
|
|
||||||
>
|
|
||||||
Next step
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={ () => setState( { isComplete: true } ) }
|
|
||||||
disabled={ currentIndex !== steps.length - 1 }
|
|
||||||
>
|
|
||||||
Complete
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
) }
|
|
||||||
|
|
||||||
<Stepper
|
return (
|
||||||
steps={ steps }
|
<div>
|
||||||
currentStep={ currentStep }
|
{ isComplete ? (
|
||||||
|
<button onClick={ () => setState( { currentStep: 'first', isComplete: false } ) } >
|
||||||
|
Reset
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
onClick={ () => setState( { currentStep: steps[ currentIndex - 1 ]['key'] } ) }
|
||||||
|
disabled={ currentIndex < 1 }
|
||||||
|
>
|
||||||
|
Previous step
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={ () => setState( { currentStep: steps[ currentIndex + 1 ]['key'] } ) }
|
||||||
|
disabled={ currentIndex >= steps.length - 1 }
|
||||||
|
>
|
||||||
|
Next step
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={ () => setState( { isComplete: true } ) }
|
||||||
|
disabled={ currentIndex !== steps.length - 1 }
|
||||||
|
>
|
||||||
|
Complete
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={ () => setState( { isPending: ! isPending } ) }
|
||||||
|
>
|
||||||
|
Toggle Spinner
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
) }
|
||||||
|
|
||||||
|
<Stepper
|
||||||
|
steps={ steps }
|
||||||
|
currentStep={ currentStep }
|
||||||
|
isPending={ isPending }
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Stepper
|
||||||
|
isPending={ isPending }
|
||||||
|
direction="vertical"
|
||||||
|
steps={ steps }
|
||||||
|
currentStep={ currentStep }
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
<Stepper
|
|
||||||
direction="vertical"
|
|
||||||
steps={ steps }
|
|
||||||
currentStep={ currentStep }
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
} );
|
} );
|
||||||
```
|
```
|
||||||
|
|
|
@ -5,11 +5,11 @@
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { Component, Fragment } from '@wordpress/element';
|
import { Component, Fragment } from '@wordpress/element';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Spinner } from '@wordpress/components';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
|
import Spinner from '../spinner';
|
||||||
import CheckIcon from './check-icon';
|
import CheckIcon from './check-icon';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -32,8 +32,6 @@ class Stepper extends Component {
|
||||||
'is-complete': 'undefined' !== typeof isComplete ? isComplete : currentIndex > i,
|
'is-complete': 'undefined' !== typeof isComplete ? isComplete : currentIndex > i,
|
||||||
} );
|
} );
|
||||||
|
|
||||||
// @todo Update Spinner Styles
|
|
||||||
// https://material.io/design/components/progress-indicators.html
|
|
||||||
const icon = currentStep === key && isPending ? <Spinner /> : (
|
const icon = currentStep === key && isPending ? <Spinner /> : (
|
||||||
<div className="woocommerce-stepper__step-icon">
|
<div className="woocommerce-stepper__step-icon">
|
||||||
<span className="woocommerce-stepper__step-number">{ i + 1 }</span>
|
<span className="woocommerce-stepper__step-number">{ i + 1 }</span>
|
||||||
|
@ -47,7 +45,7 @@ class Stepper extends Component {
|
||||||
className={ stepClassName }
|
className={ stepClassName }
|
||||||
>
|
>
|
||||||
{ icon }
|
{ icon }
|
||||||
<span className="woocommerce-stepper_step-label">
|
<span className="woocommerce-stepper__step-label">
|
||||||
{ label }
|
{ label }
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
.woocommerce-stepper {
|
.woocommerce-stepper {
|
||||||
background: #fff;
|
background: $muriel-white;
|
||||||
box-shadow: $muriel-box-shadow-1dp;
|
box-shadow: $muriel-box-shadow-1dp;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-around;
|
justify-content: space-around;
|
||||||
|
@ -15,26 +15,33 @@
|
||||||
padding: $gap-small;
|
padding: $gap-small;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
color: $muriel-gray-900;
|
color: $muriel-gray-900;
|
||||||
|
height: 48px;
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:not(.is-vertical).woocommerce-stepper_step-label {
|
.woocommerce-spinner {
|
||||||
margin: 0 4px 0 4px;
|
display: block;
|
||||||
|
margin-left: -$gap-smaller;
|
||||||
|
margin-right: $gap-smallest;
|
||||||
|
}
|
||||||
|
|
||||||
|
.woocommerce-spinner__circle {
|
||||||
|
stroke: $muriel-hot-blue-500;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.is-active,
|
&.is-active,
|
||||||
&.is-complete {
|
&.is-complete {
|
||||||
.woocommerce-stepper__step-icon,
|
.woocommerce-stepper__step-icon {
|
||||||
.components-spinner {
|
background: $muriel-hot-blue-500;
|
||||||
background: $muriel-hot-orange-700;
|
color: $muriel-white;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.is-active {
|
&.is-active {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
.woocommerce-stepper_step-label {
|
.woocommerce-stepper__step-label {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,8 +56,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.woocommerce-stepper__step-icon,
|
.woocommerce-stepper__step-icon {
|
||||||
.components-spinner {
|
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
@ -58,15 +64,11 @@
|
||||||
height: 24px;
|
height: 24px;
|
||||||
min-width: 24px;
|
min-width: 24px;
|
||||||
margin-right: $gap-small;
|
margin-right: $gap-small;
|
||||||
background: $muriel-gray-300;
|
background: $muriel-gray-50;
|
||||||
color: #fff;
|
color: $muriel-gray-600;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.components-spinner {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.woocommerce-stepper__step-divider {
|
.woocommerce-stepper__step-divider {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
border-bottom: 1px solid $muriel-gray-50;
|
border-bottom: 1px solid $muriel-gray-50;
|
||||||
|
@ -77,7 +79,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
@include breakpoint( '<782px' ) {
|
@include breakpoint( '<782px' ) {
|
||||||
.woocommerce-stepper_step-label {
|
.woocommerce-stepper__step-label {
|
||||||
display: none;
|
display: none;
|
||||||
padding-top: 24px;
|
padding-top: 24px;
|
||||||
}
|
}
|
||||||
|
@ -97,7 +99,7 @@
|
||||||
margin-left: 24px;
|
margin-left: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.woocommerce-stepper_step-label {
|
.woocommerce-stepper__step-label {
|
||||||
display: initial;
|
display: initial;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
@import 'segmented-selection/style.scss';
|
@import 'segmented-selection/style.scss';
|
||||||
@import 'split-button/style.scss';
|
@import 'split-button/style.scss';
|
||||||
@import 'stepper/style.scss';
|
@import 'stepper/style.scss';
|
||||||
|
@import 'spinner/style.scss';
|
||||||
@import 'summary/style.scss';
|
@import 'summary/style.scss';
|
||||||
@import 'table/style.scss';
|
@import 'table/style.scss';
|
||||||
@import 'tag/style.scss';
|
@import 'tag/style.scss';
|
||||||
|
|
Loading…
Reference in New Issue