-
Notifications
You must be signed in to change notification settings - Fork 4.7k
CustomGradientPicker: Update Type and Angle controls #23802
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
Changes from 1 commit
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
3ab4a34
Refactor AnglePickerControl styles
54fff5c
Update SelectControl. Integrate new SelectControl with AnglePickerCon…
c42e591
Improve Gradient Select and radio control UI
2fe29f8
Update snapshots
af55622
Update snapshot
8f267a1
Merge branch 'master' into update/gradient-controls
5a269d1
Merge branch 'master' into update/gradient-controls
7aad049
Update snapshot
83d7135
Add label for gradient picker type
c57142b
Fixed sizing.
jasmussen 10ee400
Improve focus.
jasmussen 9eaa34a
Harmonize spacing.
jasmussen cf671f0
Fix so swatches align with the gradient control below.
jasmussen File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next
Next commit
Refactor AnglePickerControl styles
- Loading branch information
There are no files selected for viewing
93 changes: 93 additions & 0 deletions
93
packages/components/src/angle-picker-control/angle-circle.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 */ | ||
| <CircleRoot | ||
| ref={ angleCircleRef } | ||
| onMouseDown={ startDrag } | ||
| className="components-angle-picker-control__angle-circle" | ||
| style={ isDragging ? { cursor: 'grabbing' } : undefined } | ||
| { ...props } | ||
| > | ||
| <CircleIndicatorWrapper | ||
| style={ | ||
| value ? { transform: `rotate(${ value }deg)` } : undefined | ||
| } | ||
| className="components-angle-picker-control__angle-circle-indicator-wrapper" | ||
| > | ||
| <CircleIndicator className="components-angle-picker-control__angle-circle-indicator" /> | ||
| </CircleIndicatorWrapper> | ||
| </CircleRoot> | ||
| /* 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; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 */ | ||
| <div | ||
| ref={ angleCircleRef } | ||
| onMouseDown={ startDrag } | ||
| className="components-angle-picker-control__angle-circle" | ||
| style={ isDragging ? { cursor: 'grabbing' } : undefined } | ||
| { ...props } | ||
| > | ||
| <div | ||
| style={ | ||
| value ? { transform: `rotate(${ value }deg)` } : undefined | ||
| } | ||
| className="components-angle-picker-control__angle-circle-indicator-wrapper" | ||
| > | ||
| <span className="components-angle-picker-control__angle-circle-indicator" /> | ||
| </div> | ||
| </div> | ||
| /* 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 ( | ||
| <BaseControl | ||
| className={ classes } | ||
| id={ id } | ||
| label={ label } | ||
| id={ inputId } | ||
| className="components-angle-picker-control" | ||
| { ...props } | ||
| > | ||
| <AngleCircle | ||
| value={ value } | ||
| onChange={ onChange } | ||
| aria-hidden="true" | ||
| /> | ||
| <input | ||
| className="components-angle-picker-control__input-field" | ||
| type="number" | ||
| id={ inputId } | ||
| onChange={ ( event ) => { | ||
| const unprocessedValue = event.target.value; | ||
| const inputValue = | ||
| unprocessedValue !== '' | ||
| ? parseInt( event.target.value, 10 ) | ||
| : 0; | ||
| onChange( inputValue ); | ||
| } } | ||
| value={ value } | ||
| min={ 0 } | ||
| max={ 360 } | ||
| step="1" | ||
| /> | ||
| <Root gap={ 3 }> | ||
| <NumberControlWrapper> | ||
| <NumberControl | ||
| className="components-angle-picker-control__input-field" | ||
| id={ id } | ||
| max={ 360 } | ||
| min={ 0 } | ||
| onChange={ handleOnNumberChange } | ||
| step="1" | ||
| value={ value } | ||
| /> | ||
| </NumberControlWrapper> | ||
| <FlexBlock> | ||
| <AngleCircle | ||
| aria-hidden="true" | ||
| value={ value } | ||
| onChange={ onChange } | ||
| /> | ||
| </FlexBlock> | ||
| </Root> | ||
| </BaseControl> | ||
| ); | ||
| } |
This file was deleted.
Oops, something went wrong.
53 changes: 53 additions & 0 deletions
53
packages/components/src/angle-picker-control/styles/angle-picker-control-styles.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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; | ||
| float: left; | ||
| height: ${ CIRCLE_SIZE }px; | ||
| 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; | ||
| `; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think we should have a new
stylesfolder? Even though you're usingcss-in-jsit's still the equivalent ofstyle.scsswhich in many components I checked ( didn't check them all :) ) are in the root folder. So it seems to me we could keep it consistent. What do you think?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point! It may no longer be necessary. I originally introduced this structure to make it distinctly different from the
style.scssduring the early proposal/experimental phase.I'm happy to keep the
style.jsfiles at the root level. It's the same convention I've followed in another React component library project:https://github.com/ItsJonQ/g2/tree/master/packages/components/src/Button