@gechiui/components
Version:
UI components for GeChiUI.
354 lines (340 loc) • 8.82 kB
JavaScript
/**
* External dependencies
*/
import classnames from 'classnames';
import { colord } from 'colord';
/**
* GeChiUI dependencies
*/
import { useInstanceId } from '@gechiui/compose';
import { useEffect, useRef, useState, useMemo } from '@gechiui/element';
import { __, sprintf } from '@gechiui/i18n';
import { plus } from '@gechiui/icons';
import { LEFT, RIGHT } from '@gechiui/keycodes';
/**
* Internal dependencies
*/
import Button from '../button';
import { ColorPicker } from '../color-picker';
import { VisuallyHidden } from '../visually-hidden';
import { CustomColorPickerDropdown } from '../color-palette';
import {
addControlPoint,
clampPercent,
removeControlPoint,
updateControlPointColor,
updateControlPointColorByPosition,
updateControlPointPosition,
getHorizontalRelativeGradientPosition,
} from './utils';
import {
GRADIENT_MARKERS_WIDTH,
MINIMUM_SIGNIFICANT_MOVE,
KEYBOARD_CONTROL_POINT_VARIATION,
} from './constants';
function ControlPointButton( { isOpen, position, color, ...additionalProps } ) {
const instanceId = useInstanceId( ControlPointButton );
const descriptionId = `components-custom-gradient-picker__control-point-button-description-${ instanceId }`;
return (
<>
<Button
aria-label={ sprintf(
// translators: %1$s: gradient position e.g: 70, %2$s: gradient color code e.g: rgb(52,121,151).
__(
'渐变色控制点在%1$s%%位置处,颜色代码为%2$s。'
),
position,
color
) }
aria-describedby={ descriptionId }
aria-haspopup="true"
aria-expanded={ isOpen }
className={ classnames(
'components-custom-gradient-picker__control-point-button',
{
'is-active': isOpen,
}
) }
style={ {
left: `${ position }%`,
} }
{ ...additionalProps }
/>
<VisuallyHidden id={ descriptionId }>
{ __(
'使用您的左右方向键或使用鼠标拖放来修改渐变位置。按下按钮来修改颜色或移除控制点。'
) }
</VisuallyHidden>
</>
);
}
function GradientColorPickerDropdown( {
isRenderedInSidebar,
gradientPickerDomRef,
...props
} ) {
const popoverProps = useMemo( () => {
const result = {
className:
'components-custom-gradient-picker__color-picker-popover',
position: 'top',
};
if ( isRenderedInSidebar ) {
result.anchorRef = gradientPickerDomRef.current;
result.position = 'bottom left';
}
return result;
}, [ gradientPickerDomRef.current, isRenderedInSidebar ] );
return (
<CustomColorPickerDropdown
isRenderedInSidebar={ isRenderedInSidebar }
popoverProps={ popoverProps }
{ ...props }
/>
);
}
function ControlPoints( {
disableRemove,
disableAlpha,
gradientPickerDomRef,
ignoreMarkerPosition,
value: controlPoints,
onChange,
onStartControlPointChange,
onStopControlPointChange,
__experimentalIsRenderedInSidebar,
} ) {
const controlPointMoveState = useRef();
const onMouseMove = ( event ) => {
const relativePosition = getHorizontalRelativeGradientPosition(
event.clientX,
gradientPickerDomRef.current,
GRADIENT_MARKERS_WIDTH
);
const {
initialPosition,
index,
significantMoveHappened,
} = controlPointMoveState.current;
if (
! significantMoveHappened &&
Math.abs( initialPosition - relativePosition ) >=
MINIMUM_SIGNIFICANT_MOVE
) {
controlPointMoveState.current.significantMoveHappened = true;
}
onChange(
updateControlPointPosition( controlPoints, index, relativePosition )
);
};
const cleanEventListeners = () => {
if (
window &&
window.removeEventListener &&
controlPointMoveState.current &&
controlPointMoveState.current.listenersActivated
) {
window.removeEventListener( 'mousemove', onMouseMove );
window.removeEventListener( 'mouseup', cleanEventListeners );
onStopControlPointChange();
controlPointMoveState.current.listenersActivated = false;
}
};
useEffect( () => {
return () => {
cleanEventListeners();
};
}, [] );
return controlPoints.map( ( point, index ) => {
const initialPosition = point?.position;
return (
ignoreMarkerPosition !== initialPosition && (
<GradientColorPickerDropdown
gradientPickerDomRef={ gradientPickerDomRef }
isRenderedInSidebar={ __experimentalIsRenderedInSidebar }
key={ index }
onClose={ onStopControlPointChange }
renderToggle={ ( { isOpen, onToggle } ) => (
<ControlPointButton
key={ index }
onClick={ () => {
if (
controlPointMoveState.current &&
controlPointMoveState.current
.significantMoveHappened
) {
return;
}
if ( isOpen ) {
onStopControlPointChange();
} else {
onStartControlPointChange();
}
onToggle();
} }
onMouseDown={ () => {
if ( window && window.addEventListener ) {
controlPointMoveState.current = {
initialPosition,
index,
significantMoveHappened: false,
listenersActivated: true,
};
onStartControlPointChange();
window.addEventListener(
'mousemove',
onMouseMove
);
window.addEventListener(
'mouseup',
cleanEventListeners
);
}
} }
onKeyDown={ ( event ) => {
if ( event.keyCode === LEFT ) {
// Stop propagation of the key press event to avoid focus moving
// to another editor area.
event.stopPropagation();
onChange(
updateControlPointPosition(
controlPoints,
index,
clampPercent(
point.position -
KEYBOARD_CONTROL_POINT_VARIATION
)
)
);
} else if ( event.keyCode === RIGHT ) {
// Stop propagation of the key press event to avoid focus moving
// to another editor area.
event.stopPropagation();
onChange(
updateControlPointPosition(
controlPoints,
index,
clampPercent(
point.position +
KEYBOARD_CONTROL_POINT_VARIATION
)
)
);
}
} }
isOpen={ isOpen }
position={ point.position }
color={ point.color }
/>
) }
renderContent={ ( { onClose } ) => (
<>
<ColorPicker
enableAlpha={ ! disableAlpha }
color={ point.color }
onChange={ ( color ) => {
onChange(
updateControlPointColor(
controlPoints,
index,
colord( color ).toRgbString()
)
);
} }
/>
{ ! disableRemove && controlPoints.length > 2 && (
<Button
className="components-custom-gradient-picker__remove-control-point"
onClick={ () => {
onChange(
removeControlPoint(
controlPoints,
index
)
);
onClose();
} }
variant="link"
>
{ __( '移除控制点' ) }
</Button>
) }
</>
) }
/>
)
);
} );
}
function InsertPoint( {
value: controlPoints,
onChange,
onOpenInserter,
onCloseInserter,
insertPosition,
disableAlpha,
__experimentalIsRenderedInSidebar,
gradientPickerDomRef,
} ) {
const [ alreadyInsertedPoint, setAlreadyInsertedPoint ] = useState( false );
return (
<GradientColorPickerDropdown
gradientPickerDomRef={ gradientPickerDomRef }
isRenderedInSidebar={ __experimentalIsRenderedInSidebar }
className="components-custom-gradient-picker__inserter"
onClose={ () => {
onCloseInserter();
} }
renderToggle={ ( { isOpen, onToggle } ) => (
<Button
aria-expanded={ isOpen }
aria-haspopup="true"
onClick={ () => {
if ( isOpen ) {
onCloseInserter();
} else {
setAlreadyInsertedPoint( false );
onOpenInserter();
}
onToggle();
} }
className="components-custom-gradient-picker__insert-point"
icon={ plus }
style={ {
left:
insertPosition !== null
? `${ insertPosition }%`
: undefined,
} }
/>
) }
renderContent={ () => (
<ColorPicker
enableAlpha={ ! disableAlpha }
onChange={ ( color ) => {
if ( ! alreadyInsertedPoint ) {
onChange(
addControlPoint(
controlPoints,
insertPosition,
colord( color ).toRgbString()
)
);
setAlreadyInsertedPoint( true );
} else {
onChange(
updateControlPointColorByPosition(
controlPoints,
insertPosition,
colord( color ).toRgbString()
)
);
}
} }
/>
) }
/>
);
}
ControlPoints.InsertPoint = InsertPoint;
export default ControlPoints;