diff --git a/packages/block-editor/src/components/colors-gradients/style.scss b/packages/block-editor/src/components/colors-gradients/style.scss
index cd5992a613cfc..7bc533fd8ec60 100644
--- a/packages/block-editor/src/components/colors-gradients/style.scss
+++ b/packages/block-editor/src/components/colors-gradients/style.scss
@@ -1,11 +1,11 @@
.block-editor-color-gradient-control {
- &__color-indicator {
- margin-bottom: $grid-unit-10;
+ .block-editor-color-gradient-control__color-indicator {
+ margin-bottom: $grid-unit-15;
}
- &__button-tabs {
+ .block-editor-color-gradient-control__button-tabs {
display: block;
- margin-bottom: $grid-unit-10;
+ margin-bottom: $grid-unit-15;
}
}
diff --git a/packages/block-editor/src/components/gradient-picker/index.js b/packages/block-editor/src/components/gradient-picker/index.js
index f698432d99025..cf1ebac3ed0d9 100644
--- a/packages/block-editor/src/components/gradient-picker/index.js
+++ b/packages/block-editor/src/components/gradient-picker/index.js
@@ -6,7 +6,7 @@ import { pick } from 'lodash';
/**
* WordPress dependencies
*/
-import { __experimentalGradientPicker } from '@wordpress/components';
+import { __experimentalGradientPicker as GradientPicker } from '@wordpress/components';
import { useSelect } from '@wordpress/data';
function GradientPickerWithGradients( props ) {
@@ -19,7 +19,7 @@ function GradientPickerWithGradients( props ) {
[]
);
return (
- <__experimentalGradientPicker
+ ;
}
diff --git a/packages/block-editor/src/components/responsive-block-control/test/__snapshots__/index.js.snap b/packages/block-editor/src/components/responsive-block-control/test/__snapshots__/index.js.snap
index dd8516642921c..f7952517ad626 100644
--- a/packages/block-editor/src/components/responsive-block-control/test/__snapshots__/index.js.snap
+++ b/packages/block-editor/src/components/responsive-block-control/test/__snapshots__/index.js.snap
@@ -1,3 +1,3 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`Basic rendering should render with required props 1`] = `"
"`;
+exports[`Basic rendering should render with required props 1`] = `""`;
diff --git a/packages/components/src/angle-picker-control/angle-circle.js b/packages/components/src/angle-picker-control/angle-circle.js
new file mode 100644
index 0000000000000..6122cda215daf
--- /dev/null
+++ b/packages/components/src/angle-picker-control/angle-circle.js
@@ -0,0 +1,93 @@
+/**
+ * WordPress dependencies
+ */
+import { useEffect, useRef } from '@wordpress/element';
+import { __experimentalUseDragging as useDragging } from '@wordpress/compose';
+
+/**
+ * Internal dependencies
+ */
+import {
+ CircleRoot,
+ CircleIndicatorWrapper,
+ CircleIndicator,
+} from './styles/angle-picker-control-styles';
+
+function AngleCircle( { value, onChange, ...props } ) {
+ const angleCircleRef = useRef();
+ const angleCircleCenter = useRef();
+ const previousCursorValue = useRef();
+
+ const setAngleCircleCenter = () => {
+ const rect = angleCircleRef.current.getBoundingClientRect();
+ angleCircleCenter.current = {
+ x: rect.x + rect.width / 2,
+ y: rect.y + rect.height / 2,
+ };
+ };
+
+ const changeAngleToPosition = ( event ) => {
+ const { x: centerX, y: centerY } = angleCircleCenter.current;
+ // Prevent (drag) mouse events from selecting and accidentally
+ // triggering actions from other elements.
+ event.preventDefault();
+
+ onChange( getAngle( centerX, centerY, event.clientX, event.clientY ) );
+ };
+
+ const { startDrag, isDragging } = useDragging( {
+ onDragStart: ( event ) => {
+ setAngleCircleCenter();
+ changeAngleToPosition( event );
+ },
+ onDragMove: changeAngleToPosition,
+ onDragEnd: changeAngleToPosition,
+ } );
+
+ useEffect( () => {
+ if ( isDragging ) {
+ if ( previousCursorValue.current === undefined ) {
+ previousCursorValue.current = document.body.style.cursor;
+ }
+ document.body.style.cursor = 'grabbing';
+ } else {
+ document.body.style.cursor = previousCursorValue.current || null;
+ previousCursorValue.current = undefined;
+ }
+ }, [ isDragging ] );
+
+ return (
+ /* eslint-disable jsx-a11y/no-static-element-interactions */
+
+
+
+
+
+ /* eslint-enable jsx-a11y/no-static-element-interactions */
+ );
+}
+
+function getAngle( centerX, centerY, pointX, pointY ) {
+ const y = pointY - centerY;
+ const x = pointX - centerX;
+
+ const angleInRadians = Math.atan2( y, x );
+ const angleInDeg = Math.round( angleInRadians * ( 180 / Math.PI ) ) + 90;
+ if ( angleInDeg < 0 ) {
+ return 360 + angleInDeg;
+ }
+ return angleInDeg;
+}
+
+export default AngleCircle;
diff --git a/packages/components/src/angle-picker-control/index.js b/packages/components/src/angle-picker-control/index.js
index 9350c61f74f15..9d6e52c5b7e84 100644
--- a/packages/components/src/angle-picker-control/index.js
+++ b/packages/components/src/angle-picker-control/index.js
@@ -1,116 +1,74 @@
+/**
+ * External dependencies
+ */
+import classnames from 'classnames';
+
/**
* WordPress dependencies
*/
-import { useRef } from '@wordpress/element';
-import {
- useInstanceId,
- __experimentalUseDragging as useDragging,
-} from '@wordpress/compose';
-import { __ } from '@wordpress/i18n';
+import { useInstanceId } from '@wordpress/compose';
/**
* Internal dependencies
*/
import BaseControl from '../base-control';
-
-function getAngle( centerX, centerY, pointX, pointY ) {
- const y = pointY - centerY;
- const x = pointX - centerX;
-
- const angleInRadians = Math.atan2( y, x );
- const angleInDeg = Math.round( angleInRadians * ( 180 / Math.PI ) ) + 90;
- if ( angleInDeg < 0 ) {
- return 360 + angleInDeg;
- }
- return angleInDeg;
-}
-
-const AngleCircle = ( { value, onChange, ...props } ) => {
- const angleCircleRef = useRef();
- const angleCircleCenter = useRef();
-
- const setAngleCircleCenter = () => {
- const rect = angleCircleRef.current.getBoundingClientRect();
- angleCircleCenter.current = {
- x: rect.x + rect.width / 2,
- y: rect.y + rect.height / 2,
- };
- };
-
- const changeAngleToPosition = ( event ) => {
- const { x: centerX, y: centerY } = angleCircleCenter.current;
- // Prevent (drag) mouse events from selecting and accidentally
- // triggering actions from other elements.
- event.preventDefault();
-
- onChange( getAngle( centerX, centerY, event.clientX, event.clientY ) );
- };
-
- const { startDrag, isDragging } = useDragging( {
- onDragStart: ( event ) => {
- setAngleCircleCenter();
- changeAngleToPosition( event );
- },
- onDragMove: changeAngleToPosition,
- onDragEnd: changeAngleToPosition,
- } );
- return (
- /* eslint-disable jsx-a11y/no-static-element-interactions */
-
- /* eslint-enable jsx-a11y/no-static-element-interactions */
- );
-};
+import { FlexBlock } from '../flex';
+import NumberControl from '../number-control';
+import AngleCircle from './angle-circle';
+import {
+ Root,
+ NumberControlWrapper,
+} from './styles/angle-picker-control-styles';
export default function AnglePickerControl( {
+ className,
+ id: idProp,
value,
onChange,
- label = __( 'Angle' ),
+ label,
+ ...props
} ) {
- const instanceId = useInstanceId( AnglePickerControl );
- const inputId = `components-angle-picker-control__input-${ instanceId }`;
+ const instanceId = useInstanceId(
+ AnglePickerControl,
+ 'components-angle-picker-control__input'
+ );
+ const id = idProp || instanceId;
+
+ const handleOnNumberChange = ( unprocessedValue ) => {
+ const inputValue =
+ unprocessedValue !== '' ? parseInt( unprocessedValue, 10 ) : 0;
+ onChange( inputValue );
+ };
+
+ const classes = classnames( 'components-angle-picker-control', className );
+
return (
-
- {
- const unprocessedValue = event.target.value;
- const inputValue =
- unprocessedValue !== ''
- ? parseInt( event.target.value, 10 )
- : 0;
- onChange( inputValue );
- } }
- value={ value }
- min={ 0 }
- max={ 360 }
- step="1"
- />
+
+
+
+
+
+
+
+
);
}
diff --git a/packages/components/src/angle-picker-control/style.scss b/packages/components/src/angle-picker-control/style.scss
deleted file mode 100644
index a581517902da0..0000000000000
--- a/packages/components/src/angle-picker-control/style.scss
+++ /dev/null
@@ -1,43 +0,0 @@
-.components-angle-picker-control {
- width: 50%;
- &.components-base-control .components-base-control__label {
- display: block;
- }
-}
-
-.components-angle-picker-control__input-field[type="number"] {
- @include input-control;
- width: calc(100% - #{$button-size});
- max-width: 100px;
-}
-
-.components-angle-picker-control__angle-circle {
- width: $button-size - ( 2 * $grid-unit-05 );
- height: $button-size - ( 2 * $grid-unit-05 );
- border: 2px solid $dark-gray-500;
- border-radius: 50%;
- float: left;
- margin-right: $grid-unit-05;
- cursor: grab;
-}
-
-.components-angle-picker-control__angle-circle-indicator-wrapper {
- position: relative;
- width: 100%;
- height: 100%;
-}
-
-.components-angle-picker-control__angle-circle-indicator {
- width: 1px;
- height: 1px;
- border-radius: 50%;
- border: 3px solid $dark-gray-500;
- display: block;
- position: absolute;
- top: -($button-size - (2 * $grid-unit-05)) / 2;
- bottom: 0;
- left: 0;
- right: 0;
- margin: auto;
- background: $dark-gray-500;
-}
diff --git a/packages/components/src/angle-picker-control/styles/angle-picker-control-styles.js b/packages/components/src/angle-picker-control/styles/angle-picker-control-styles.js
new file mode 100644
index 0000000000000..f63f9bc6d1b00
--- /dev/null
+++ b/packages/components/src/angle-picker-control/styles/angle-picker-control-styles.js
@@ -0,0 +1,53 @@
+/**
+ * External dependencies
+ */
+import styled from '@emotion/styled';
+
+/**
+ * Internal dependencies
+ */
+import { Flex, FlexItem } from '../../flex';
+import { color } from '../../utils/style-mixins';
+
+const CIRCLE_SIZE = 30;
+
+export const Root = styled( Flex )`
+ max-width: 200px;
+`;
+
+export const NumberControlWrapper = styled( FlexItem )`
+ width: 80px;
+`;
+
+export const CircleRoot = styled.div`
+ border-radius: 50%;
+ border: 1px solid ${ color( 'ui.borderLight' ) };
+ box-sizing: border-box;
+ cursor: grab;
+ height: ${ CIRCLE_SIZE }px;
+ overflow: hidden;
+ width: ${ CIRCLE_SIZE }px;
+`;
+
+export const CircleIndicatorWrapper = styled.div`
+ box-sizing: border-box;
+ position: relative;
+ width: 100%;
+ height: 100%;
+`;
+
+export const CircleIndicator = styled.div`
+ background: ${ color( 'ui.border' ) };
+ border-radius: 50%;
+ border: 3px solid ${ color( 'ui.border' ) };
+ bottom: 0;
+ box-sizing: border-box;
+ display: block;
+ height: 1px;
+ left: 0;
+ margin: auto;
+ position: absolute;
+ right: 0;
+ top: -${ CIRCLE_SIZE / 2 }px;
+ width: 1px;
+`;
diff --git a/packages/components/src/circular-option-picker/style.scss b/packages/components/src/circular-option-picker/style.scss
index f82cde47eddb3..23003048b137e 100644
--- a/packages/components/src/circular-option-picker/style.scss
+++ b/packages/components/src/circular-option-picker/style.scss
@@ -3,7 +3,6 @@ $color-palette-circle-spacing: 12px;
.components-circular-option-picker {
display: inline-block;
- margin-top: 0.6rem;
width: 100%;
.components-circular-option-picker__custom-clear-wrapper {
@@ -16,7 +15,7 @@ $color-palette-circle-spacing: 12px;
display: inline-block;
height: $color-palette-circle-size;
width: $color-palette-circle-size;
- margin-right: $color-palette-circle-spacing;
+ margin-right: $color-palette-circle-spacing + $grid-unit-05;
margin-bottom: $color-palette-circle-spacing;
vertical-align: top;
transform: scale(1);
@@ -32,6 +31,11 @@ $color-palette-circle-spacing: 12px;
height: 100%;
width: 100%;
}
+
+ // Remove right margin on every 6th item so it aligns with gradient control.
+ &:nth-child(6n+6) {
+ margin-right: 0;
+ }
}
.components-circular-option-picker__option-wrapper::before {
diff --git a/packages/components/src/custom-gradient-picker/constants.js b/packages/components/src/custom-gradient-picker/constants.js
index bb5f826f5ccd4..2fe6b4a6f06dd 100644
--- a/packages/components/src/custom-gradient-picker/constants.js
+++ b/packages/components/src/custom-gradient-picker/constants.js
@@ -1,3 +1,8 @@
+/**
+ * WordPress dependencies
+ */
+import { __ } from '@wordpress/i18n';
+
export const INSERT_POINT_WIDTH = 23;
export const GRADIENT_MARKERS_WIDTH = 18;
export const MINIMUM_DISTANCE_BETWEEN_INSERTER_AND_MARKER =
@@ -16,3 +21,8 @@ export const HORIZONTAL_GRADIENT_ORIENTATION = {
type: 'angular',
value: 90,
};
+
+export const GRADIENT_OPTIONS = [
+ { value: 'linear-gradient', label: __( 'Linear' ) },
+ { value: 'radial-gradient', label: __( 'Radial' ) },
+];
diff --git a/packages/components/src/custom-gradient-picker/custom-gradient-bar.js b/packages/components/src/custom-gradient-picker/custom-gradient-bar.js
index 6bf81d83f0082..18c6927aaa40c 100644
--- a/packages/components/src/custom-gradient-picker/custom-gradient-bar.js
+++ b/packages/components/src/custom-gradient-picker/custom-gradient-bar.js
@@ -9,7 +9,7 @@ import classnames from 'classnames';
* WordPress dependencies
*/
import { useRef, useReducer, useState } from '@wordpress/element';
-import { plusCircle } from '@wordpress/icons';
+import { plus } from '@wordpress/icons';
/**
* Internal dependencies
@@ -60,7 +60,7 @@ function InsertPoint( {
onToggle();
} }
className="components-custom-gradient-picker__insert-point"
- icon={ plusCircle }
+ icon={ plus }
style={ {
left:
insertPosition !== null
diff --git a/packages/components/src/custom-gradient-picker/icons.js b/packages/components/src/custom-gradient-picker/icons.js
deleted file mode 100644
index 137bf2cb4d89d..0000000000000
--- a/packages/components/src/custom-gradient-picker/icons.js
+++ /dev/null
@@ -1,72 +0,0 @@
-/**
- * WordPress dependencies
- */
-import { withInstanceId } from '@wordpress/compose';
-import {
- Circle,
- LinearGradient,
- Path,
- RadialGradient,
- Stop,
- SVG,
-} from '@wordpress/primitives';
-
-/**
- * Internal dependencies
- */
-export const LinearGradientIcon = withInstanceId( ( { instanceId } ) => {
- const linerGradientId = `linear-gradient-${ instanceId }`;
- return (
-
- );
-} );
-
-export const RadialGradientIcon = withInstanceId( ( { instanceId } ) => {
- const radialGradientId = `radial-gradient-${ instanceId }`;
- return (
-
- );
-} );
diff --git a/packages/components/src/custom-gradient-picker/index.js b/packages/components/src/custom-gradient-picker/index.js
index 1c9a26ab3f8e2..57220e5b40cf2 100644
--- a/packages/components/src/custom-gradient-picker/index.js
+++ b/packages/components/src/custom-gradient-picker/index.js
@@ -12,16 +12,17 @@ import { __ } from '@wordpress/i18n';
* Internal dependencies
*/
import AnglePickerControl from '../angle-picker-control';
-import { LinearGradientIcon, RadialGradientIcon } from './icons';
import CustomGradientBar from './custom-gradient-bar';
-import BaseControl from '../base-control';
+import { Flex, FlexItem } from '../flex';
+import SelectControl from '../select-control';
import { getGradientParsed } from './utils';
import { serializeGradient } from './serializer';
-import ToolbarGroup from '../toolbar-group';
import {
DEFAULT_LINEAR_GRADIENT_ANGLE,
HORIZONTAL_GRADIENT_ORIENTATION,
+ GRADIENT_OPTIONS,
} from './constants';
+import { SelectWrapper } from './styles/custom-gradient-picker-styles';
const GradientAnglePicker = ( { gradientAST, hasGradient, onChange } ) => {
const angle = get(
@@ -71,27 +72,23 @@ const GradientTypePicker = ( { gradientAST, hasGradient, onChange } ) => {
);
};
+ const handleOnChange = ( next ) => {
+ if ( next === 'linear-gradient' ) {
+ onSetLinearGradient();
+ }
+ if ( next === 'radial-gradient' ) {
+ onSetRadialGradient();
+ }
+ };
+
return (
-
- { __( 'Type' ) }
- ,
- title: __( 'Linear Gradient' ),
- isActive: hasGradient && type === 'linear-gradient',
- onClick: onSetLinearGradient,
- },
- {
- icon: ,
- title: __( 'Radial Gradient' ),
- isActive: hasGradient && type === 'radial-gradient',
- onClick: onSetRadialGradient,
- },
- ] }
- />
-
+
);
};
@@ -101,20 +98,27 @@ export default function CustomGradientPicker( { value, onChange } ) {
return (
-
-
- { type === 'linear-gradient' && (
-
+
+
+
+ { type === 'linear-gradient' && (
+
+
+
) }
-
+
);
}
diff --git a/packages/components/src/custom-gradient-picker/style.scss b/packages/components/src/custom-gradient-picker/style.scss
index 61f15bfbb1414..972a3045b34ff 100644
--- a/packages/components/src/custom-gradient-picker/style.scss
+++ b/packages/components/src/custom-gradient-picker/style.scss
@@ -1,20 +1,17 @@
-$components-custom-gradient-picker__padding: 3px; // 24px container, 18px handles inside, that leaves 6px padding, half of which is 3.
-
-.components-custom-gradient-picker {
- margin-top: $grid-unit-10;
-}
+$components-custom-gradient-picker__padding: 6px; // 36px container, 24px handles inside, that leaves 12px padding, half of which is 6.
.components-custom-gradient-picker__gradient-bar:not(.has-gradient) {
opacity: 0.4;
}
.components-custom-gradient-picker__gradient-bar {
+ margin-top: $grid-unit-15;
width: 100%;
- height: $button-size-small;
- border-radius: $button-size-small;
- margin-bottom: $grid-unit-10;
+ height: $button-size;
+ border-radius: $button-size;
+ margin-bottom: $grid-unit-15;
padding-left: $components-custom-gradient-picker__padding;
- padding-right: $button-size-small - $components-custom-gradient-picker__padding;
+ padding-right: $button-size - $components-custom-gradient-picker__padding;
.components-custom-gradient-picker__markers-container {
position: relative;
@@ -24,10 +21,12 @@ $components-custom-gradient-picker__padding: 3px; // 24px container, 18px handle
border-radius: 50%;
background: $white;
padding: 2px;
+ top: $components-custom-gradient-picker__padding;
min-width: $button-size-small;
width: $button-size-small;
height: $button-size-small;
position: relative;
+ color: $gray-900;
svg {
height: 100%;
@@ -36,21 +35,20 @@ $components-custom-gradient-picker__padding: 3px; // 24px container, 18px handle
}
.components-custom-gradient-picker__control-point-button {
- border: 2px solid $white;
+ border: 2px solid transparent;
+ box-shadow: inset 0 0 0 $border-width-focus $white;
border-radius: 50%;
- height: 18px;
+ height: $button-size-small;
+ width: $button-size-small;
padding: 0;
position: absolute;
- width: 18px;
top: $components-custom-gradient-picker__padding;
+ &:focus,
&.is-active {
- background: #fafafa;
- color: #23282d;
- border-color: #999;
box-shadow:
0 0 0 1px $white,
- 0 0 0 3px var(--wp-admin-theme-color);
+ 0 0 0 3px $gray-900;
}
}
}
@@ -73,12 +71,9 @@ $components-custom-gradient-picker__padding: 3px; // 24px container, 18px handle
height: 20px;
}
-.components-custom-gradient-picker__ui-line {
- display: flex;
- justify-content: space-between;
-}
-
.components-custom-gradient-picker .components-custom-gradient-picker__ui-line {
+ margin-bottom: $grid-unit-20;
+
.components-base-control.components-angle-picker,
.components-base-control.components-custom-gradient-picker__type-picker {
margin-bottom: 0;
diff --git a/packages/components/src/custom-gradient-picker/styles/custom-gradient-picker-styles.js b/packages/components/src/custom-gradient-picker/styles/custom-gradient-picker-styles.js
new file mode 100644
index 0000000000000..f3d39295b163f
--- /dev/null
+++ b/packages/components/src/custom-gradient-picker/styles/custom-gradient-picker-styles.js
@@ -0,0 +1,12 @@
+/**
+ * External dependencies
+ */
+import styled from '@emotion/styled';
+/**
+ * Internal dependencies
+ */
+import { FlexItem } from '../../flex';
+
+export const SelectWrapper = styled( FlexItem )`
+ width: 110px;
+`;
diff --git a/packages/components/src/dimension-control/test/__snapshots__/index.test.js.snap b/packages/components/src/dimension-control/test/__snapshots__/index.test.js.snap
index 9e100abfd43dc..f25202bad70ea 100644
--- a/packages/components/src/dimension-control/test/__snapshots__/index.test.js.snap
+++ b/packages/components/src/dimension-control/test/__snapshots__/index.test.js.snap
@@ -1,7 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`DimensionControl rendering renders with custom sizes 1`] = `
- {
const base = 4;
const value = typeof gap === 'number' ? base * gap : base;
const dir = isReversed ? 'left' : 'right';
- const padding = `padding-${ dir }`;
+ const margin = `margin-${ dir }`;
return css`
> * {
- ${ padding }: ${ value }px;
+ ${ margin }: ${ value }px;
&:last-child {
- ${ padding }: 0;
+ ${ margin }: 0;
}
}
`;
diff --git a/packages/components/src/input-control/index.js b/packages/components/src/input-control/index.js
index ad3b7b79eb47d..3a054c28b84de 100644
--- a/packages/components/src/input-control/index.js
+++ b/packages/components/src/input-control/index.js
@@ -13,10 +13,8 @@ import { useState, forwardRef } from '@wordpress/element';
/**
* Internal dependencies
*/
-import Backdrop from './backdrop';
+import InputBase from './input-base';
import InputField from './input-field';
-import Label from './label';
-import { Container, Root, Prefix, Suffix } from './styles/input-control-styles';
import { isValueEmpty } from '../utils/values';
function useUniqueId( idProp ) {
@@ -29,7 +27,6 @@ function useUniqueId( idProp ) {
export function InputControl(
{
__unstableStateReducer: stateReducer = ( state ) => state,
- children,
className,
disabled = false,
hideLabelFromVision = false,
@@ -74,74 +71,46 @@ export function InputControl(
! hideLabelFromVision && isFloatingLabel && label;
return (
-
-
-
- { prefix && (
-
- { prefix }
-
- ) }
-
- { suffix && (
-
- { suffix }
-
- ) }
-
- { children }
-
-
+ stateReducer={ stateReducer }
+ value={ value }
+ />
+
);
}
-export default forwardRef( InputControl );
+const ForwardedComponent = forwardRef( InputControl );
+
+export default ForwardedComponent;
diff --git a/packages/components/src/input-control/input-base.js b/packages/components/src/input-control/input-base.js
new file mode 100644
index 0000000000000..b946175b2b043
--- /dev/null
+++ b/packages/components/src/input-control/input-base.js
@@ -0,0 +1,102 @@
+/**
+ * WordPress dependencies
+ */
+import { useInstanceId } from '@wordpress/compose';
+import { forwardRef } from '@wordpress/element';
+
+/**
+ * Internal dependencies
+ */
+import Backdrop from './backdrop';
+import Label from './label';
+import {
+ Container,
+ Root,
+ Prefix,
+ Suffix,
+ LabelWrapper,
+} from './styles/input-control-styles';
+
+function useUniqueId( idProp ) {
+ const instanceId = useInstanceId( InputBase );
+ const id = `input-base-control-${ instanceId }`;
+
+ return idProp || id;
+}
+
+export function InputBase(
+ {
+ children,
+ className,
+ disabled = false,
+ hideLabelFromVision = false,
+ id: idProp,
+ isFloatingLabel = false,
+ isFilled = false,
+ isFocused = false,
+ label,
+ prefix,
+ size = 'default',
+ suffix,
+ ...props
+ },
+ ref
+) {
+ const id = useUniqueId( idProp );
+
+ const isFloating = isFloatingLabel ? isFilled || isFocused : false;
+ const isFloatingLabelSet =
+ ! hideLabelFromVision && isFloatingLabel && label;
+
+ return (
+
+
+
+
+
+ { prefix && (
+
+ { prefix }
+
+ ) }
+ { children }
+ { suffix && (
+
+ { suffix }
+
+ ) }
+
+
+
+ );
+}
+
+export default forwardRef( InputBase );
diff --git a/packages/components/src/input-control/styles/input-control-styles.js b/packages/components/src/input-control/styles/input-control-styles.js
index dd58d7f8275f2..4ac83a36645ef 100644
--- a/packages/components/src/input-control/styles/input-control-styles.js
+++ b/packages/components/src/input-control/styles/input-control-styles.js
@@ -7,7 +7,7 @@ import styled from '@emotion/styled';
/**
* Internal dependencies
*/
-import Flex from '../../flex';
+import Flex, { FlexItem } from '../../flex';
import Text from '../../text';
import { color, rtl, reduceMotion } from '../../utils/style-mixins';
@@ -187,6 +187,7 @@ export const Input = styled.input`
box-shadow: none !important;
color: ${ color( 'black' ) };
display: block;
+ margin: 0;
outline: none;
padding-left: 8px;
padding-right: 8px;
@@ -268,7 +269,6 @@ const labelTruncation = ( { isFloating } ) => {
if ( isFloating ) return '';
return css`
- max-width: calc( 100% - 10px );
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
@@ -301,6 +301,10 @@ const BaseLabel = styled( Text )`
export const Label = ( props ) => ;
+export const LabelWrapper = styled( FlexItem )`
+ max-width: calc( 100% - 10px );
+`;
+
const fieldsetTopStyles = ( { isFloatingLabel } ) => {
const top = isFloatingLabel ? -5 : 0;
return css( { top } );
diff --git a/packages/components/src/select-control/index.js b/packages/components/src/select-control/index.js
index 8ad65f890e7ac..369cc079f7f84 100644
--- a/packages/components/src/select-control/index.js
+++ b/packages/components/src/select-control/index.js
@@ -1,31 +1,68 @@
/**
* External dependencies
*/
-import { isEmpty } from 'lodash';
+import { isEmpty, noop } from 'lodash';
+import classNames from 'classnames';
/**
* WordPress dependencies
*/
import { useInstanceId } from '@wordpress/compose';
+import { useState, forwardRef } from '@wordpress/element';
+import { Icon, chevronDown } from '@wordpress/icons';
/**
* Internal dependencies
*/
import BaseControl from '../base-control';
+import InputBase from '../input-control/input-base';
+import { Select, DownArrowWrapper } from './styles/select-control-styles';
-export default function SelectControl( {
- help,
- label,
- multiple = false,
- onChange,
- options = [],
- className,
- hideLabelFromVision,
- ...props
-} ) {
+function useUniqueId( idProp ) {
const instanceId = useInstanceId( SelectControl );
const id = `inspector-select-control-${ instanceId }`;
- const onChangeValue = ( event ) => {
+
+ return idProp || id;
+}
+
+function SelectControl(
+ {
+ className,
+ disabled = false,
+ help,
+ hideLabelFromVision,
+ id: idProp,
+ isFloatingLabel = false,
+ label,
+ multiple = false,
+ onBlur = noop,
+ onChange = noop,
+ onFocus = noop,
+ options = [],
+ size = 'default',
+ value: valueProp,
+ ...props
+ },
+ ref
+) {
+ const [ isFocused, setIsFocused ] = useState( false );
+ const id = useUniqueId( idProp );
+ const helpId = help ? `${ id }__help` : undefined;
+
+ // Disable reason: A select with an onchange throws a warning
+ if ( isEmpty( options ) ) return null;
+
+ const handleOnBlur = ( event ) => {
+ onBlur( event );
+ setIsFocused( false );
+ };
+
+ const handleOnFocus = ( event ) => {
+ onFocus( event );
+ setIsFocused( true );
+ };
+
+ const handleOnChange = ( event ) => {
if ( multiple ) {
const selectedOptions = [ ...event.target.options ].filter(
( { selected } ) => selected
@@ -34,41 +71,74 @@ export default function SelectControl( {
onChange( newValues );
return;
}
- onChange( event.target.value );
+
+ onChange( event.target.value, { event } );
};
- // Disable reason: A select with an onchange throws a warning
+ const isFilled = true;
+ const isFloating = isFloatingLabel ? isFilled || isFocused : false;
+ const isFloatingLabelSet =
+ ! hideLabelFromVision && isFloatingLabel && label;
+
+ const classes = classNames( 'components-select-control', className );
/* eslint-disable jsx-a11y/no-onchange */
return (
- ! isEmpty( options ) && (
-
+
+
+
+ }
>
-
-
- )
+ { options.map( ( option, index ) => {
+ const key =
+ option.id ||
+ `${ option.label }-${ option.value }-${ index }`;
+
+ return (
+
+ );
+ } ) }
+
+
+
);
/* eslint-enable jsx-a11y/no-onchange */
}
+
+const ForwardedComponent = forwardRef( SelectControl );
+
+export default ForwardedComponent;
diff --git a/packages/components/src/select-control/stories/index.js b/packages/components/src/select-control/stories/index.js
index dec6d48405e5a..f72e5af016585 100644
--- a/packages/components/src/select-control/stories/index.js
+++ b/packages/components/src/select-control/stories/index.js
@@ -1,7 +1,7 @@
/**
* External dependencies
*/
-import { boolean, object, text } from '@storybook/addon-knobs';
+import { boolean, object, select, text } from '@storybook/addon-knobs';
/**
* WordPress dependencies
@@ -31,29 +31,28 @@ const SelectControlWithState = ( props ) => {
};
export const _default = () => {
- const label = text( 'Label', 'Label Text' );
- const hideLabelFromVision = boolean( 'Hide Label From Vision', false );
- const help = text(
- 'Help Text',
- 'Help text to explain the select control.'
- );
- const multiple = boolean( 'Allow Multiple Selection', false );
- const options = object( 'Options', [
- { value: null, label: 'Select an Option', disabled: true },
- { value: 'a', label: 'Option A' },
- { value: 'b', label: 'Option B' },
- { value: 'c', label: 'Option C' },
- ] );
- const className = text( 'Class Name', '' );
+ const props = {
+ disabled: boolean( 'disabled', false ),
+ help: text( 'help', 'Help text to explain the select control.' ),
+ hideLabelFromVision: boolean( 'hideLabelFromVision', false ),
+ isFloatingLabel: boolean( 'isFloatingLabel', false ),
+ label: text( 'label', 'Value' ),
+ multiple: boolean( 'multiple', false ),
+ options: object( 'Options', [
+ { value: null, label: 'Select an Option', disabled: true },
+ { value: 'a', label: 'Option A' },
+ { value: 'b', label: 'Option B' },
+ { value: 'c', label: 'Option C' },
+ ] ),
+ size: select(
+ 'size',
+ {
+ default: 'default',
+ small: 'small',
+ },
+ 'default'
+ ),
+ };
- return (
-
- );
+ return ;
};
diff --git a/packages/components/src/select-control/styles/select-control-styles.js b/packages/components/src/select-control/styles/select-control-styles.js
new file mode 100644
index 0000000000000..4d4ab42178c1f
--- /dev/null
+++ b/packages/components/src/select-control/styles/select-control-styles.js
@@ -0,0 +1,98 @@
+/**
+ * External dependencies
+ */
+import { css } from '@emotion/core';
+import styled from '@emotion/styled';
+
+/**
+ * Internal dependencies
+ */
+import { color, rtl } from '../../utils/style-mixins';
+
+const disabledStyles = ( { disabled } ) => {
+ if ( ! disabled ) return '';
+
+ return css( {
+ color: color( 'ui.textDisabled' ),
+ } );
+};
+
+const fontSizeStyles = ( { size } ) => {
+ const sizes = {
+ default: '13px',
+ small: '11px',
+ };
+
+ const fontSize = sizes[ size ];
+ const fontSizeMobile = '16px';
+
+ if ( ! fontSize ) return '';
+
+ return css`
+ font-size: ${ fontSizeMobile };
+
+ @media ( min-width: 600px ) {
+ font-size: ${ fontSize };
+ }
+ `;
+};
+
+const sizeStyles = ( { size } ) => {
+ const sizes = {
+ default: {
+ height: 30,
+ lineHeight: 1,
+ minHeight: 30,
+ },
+ small: {
+ height: 24,
+ lineHeight: 1,
+ minHeight: 24,
+ },
+ };
+
+ const style = sizes[ size ] || sizes.default;
+
+ return css( style );
+};
+
+// TODO: Resolve need to use &&& to increase specificity
+// https://github.com/WordPress/gutenberg/issues/18483
+
+export const Select = styled.select`
+ &&& {
+ appearance: none;
+ background: transparent;
+ box-sizing: border-box;
+ border: none;
+ box-shadow: none !important;
+ color: ${ color( 'black' ) };
+ display: block;
+ margin: 0;
+ outline: none;
+ width: 100%;
+
+ ${ disabledStyles };
+ ${ fontSizeStyles };
+ ${ sizeStyles };
+
+ ${ rtl( { paddingLeft: 8, paddingRight: 24 } )() }
+ }
+`;
+
+export const DownArrowWrapper = styled.div`
+ align-items: center;
+ bottom: 0;
+ box-sizing: border-box;
+ display: flex;
+ padding: 0 4px;
+ pointer-events: none;
+ position: absolute;
+ top: 0;
+
+ ${ rtl( { right: 0 } )() }
+
+ svg {
+ display: block;
+ }
+`;
diff --git a/packages/components/src/style.scss b/packages/components/src/style.scss
index 36c406c3c7a41..034198da732e3 100644
--- a/packages/components/src/style.scss
+++ b/packages/components/src/style.scss
@@ -1,5 +1,4 @@
@import "./animate/style.scss";
-@import "./angle-picker-control/style.scss";
@import "./autocomplete/style.scss";
@import "./base-control/style.scss";
@import "./button-group/style.scss";