feat: initial ACRIB WordPress deployment
- WordPress 6.9.4 (es_ES) with Kadence theme - Homepage: Hero, La Asociación, Pilares, Beneficios, Eventos, Miembros, Hazte Miembro, Contacto - Brand identity: #13294b navy, #a12932 burgundy, #c69c48 gold - Fonts: Raleway (headings) + Source Sans 3 (body) + Lato (UI) - Plugins: Kadence Blocks, Polylang, Contact Form 7 - Custom CSS with full brand styling and responsive layout - HTTPS enforced via wp-config.php proxy detection
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,122 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import classnames from 'classnames';
|
||||
|
||||
/**
|
||||
* WordPress dependencies
|
||||
*/
|
||||
import { Icon, check } from '@wordpress/icons';
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
const {
|
||||
Button,
|
||||
Dropdown,
|
||||
Tooltip,
|
||||
} = wp.components;
|
||||
|
||||
function Option( {
|
||||
className,
|
||||
isSelected,
|
||||
selectedIconProps,
|
||||
tooltipText,
|
||||
...additionalProps
|
||||
} ) {
|
||||
const optionButton = (
|
||||
<Button
|
||||
isPressed={ isSelected }
|
||||
className={ classnames(
|
||||
className,
|
||||
'components-circular-option-picker__option'
|
||||
) }
|
||||
{ ...additionalProps }
|
||||
/>
|
||||
);
|
||||
return (
|
||||
<div className="components-circular-option-picker__option-wrapper">
|
||||
{ tooltipText ? (
|
||||
<Tooltip text={ tooltipText }>{ optionButton }</Tooltip>
|
||||
) : (
|
||||
optionButton
|
||||
) }
|
||||
{ isSelected && (
|
||||
<Icon
|
||||
icon={ check }
|
||||
{ ...( selectedIconProps ? selectedIconProps : {} ) }
|
||||
/>
|
||||
) }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function DropdownLinkAction( {
|
||||
buttonProps,
|
||||
className,
|
||||
dropdownProps,
|
||||
linkText,
|
||||
} ) {
|
||||
return (
|
||||
<Dropdown
|
||||
className={ classnames(
|
||||
'components-circular-option-picker__dropdown-link-action',
|
||||
className
|
||||
) }
|
||||
renderToggle={ ( { isOpen, onToggle } ) => (
|
||||
<Button
|
||||
aria-expanded={ isOpen }
|
||||
onClick={ onToggle }
|
||||
isLink
|
||||
{ ...buttonProps }
|
||||
>
|
||||
{ linkText }
|
||||
</Button>
|
||||
) }
|
||||
{ ...dropdownProps }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function ButtonAction( { className, children, ...additionalProps } ) {
|
||||
return (
|
||||
<Button
|
||||
className={ classnames(
|
||||
'components-circular-option-picker__clear',
|
||||
className
|
||||
) }
|
||||
isSmall
|
||||
isSecondary
|
||||
{ ...additionalProps }
|
||||
>
|
||||
{ children }
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
export default function CircularOptionPicker( {
|
||||
actions,
|
||||
className,
|
||||
options,
|
||||
children,
|
||||
} ) {
|
||||
return (
|
||||
<div
|
||||
className={ classnames(
|
||||
'components-circular-option-picker',
|
||||
className
|
||||
) }
|
||||
>
|
||||
{ options }
|
||||
{ children }
|
||||
{ actions && (
|
||||
<div className="components-circular-option-picker__custom-clear-wrapper">
|
||||
{ actions }
|
||||
</div>
|
||||
) }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
CircularOptionPicker.Option = Option;
|
||||
CircularOptionPicker.ButtonAction = ButtonAction;
|
||||
CircularOptionPicker.DropdownLinkAction = DropdownLinkAction;
|
||||
@@ -0,0 +1,229 @@
|
||||
import { createRoot } from '@wordpress/element';
|
||||
|
||||
import BackgroundComponent from './background-component.js';
|
||||
|
||||
export const BackgroundControl = wp.customize.MediaControl.extend( {
|
||||
renderContent: function renderContent() {
|
||||
let control = this;
|
||||
let root = createRoot( control.container[0] );
|
||||
root.render( <BackgroundComponent control={control} customizer={ wp.customize }/> );
|
||||
},
|
||||
initialize: function( id, options ) {
|
||||
var control = this,
|
||||
args = options || {};
|
||||
|
||||
args.params = args.params || {};
|
||||
if ( ! args.params.type ) {
|
||||
args.params.type = 'kadence-basic';
|
||||
}
|
||||
if ( ! args.params.content ) {
|
||||
args.params.content = jQuery( '<li></li>' );
|
||||
args.params.content.attr( 'id', 'customize-control-' + id.replace( /]/g, '' ).replace( /\[/g, '-' ) );
|
||||
args.params.content.attr( 'class', 'customize-control customize-control-' + args.params.type );
|
||||
}
|
||||
control.propertyElements = [];
|
||||
wp.customize.Control.prototype.initialize.call( control, id, args );
|
||||
},
|
||||
|
||||
/**
|
||||
* Add bidirectional data binding links between inputs and the setting(s).
|
||||
*
|
||||
* This is copied from wp.customize.Control.prototype.initialize(). It
|
||||
* should be changed in Core to be applied once the control is embedded.
|
||||
*
|
||||
* @private
|
||||
* @returns {null}
|
||||
*/
|
||||
_setUpSettingRootLinks: function() {
|
||||
var control = this,
|
||||
nodes = control.container.find( '[data-customize-setting-link]' );
|
||||
|
||||
nodes.each( function() {
|
||||
var node = jQuery( this );
|
||||
|
||||
wp.customize( node.data( 'customizeSettingLink' ), function( setting ) {
|
||||
var element = new wp.customize.Element( node );
|
||||
control.elements.push( element );
|
||||
element.sync( setting );
|
||||
element.set( setting() );
|
||||
} );
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Add bidirectional data binding links between inputs and the setting properties.
|
||||
*
|
||||
* @private
|
||||
* @returns {null}
|
||||
*/
|
||||
_setUpSettingPropertyLinks: function() {
|
||||
var control = this,
|
||||
nodes;
|
||||
|
||||
if ( ! control.setting ) {
|
||||
return;
|
||||
}
|
||||
|
||||
nodes = control.container.find( '[data-customize-setting-property-link]' );
|
||||
|
||||
nodes.each( function() {
|
||||
var node = jQuery( this ),
|
||||
element,
|
||||
propertyName = node.data( 'customizeSettingPropertyLink' );
|
||||
|
||||
element = new wp.customize.Element( node );
|
||||
control.propertyElements.push( element );
|
||||
element.set( control.setting()[ propertyName ] );
|
||||
|
||||
element.bind( function( newPropertyValue ) {
|
||||
var newSetting = control.setting();
|
||||
if ( newPropertyValue === newSetting[ propertyName ] ) {
|
||||
return;
|
||||
}
|
||||
newSetting = _.clone( newSetting );
|
||||
newSetting[ propertyName ] = newPropertyValue;
|
||||
control.setting.set( newSetting );
|
||||
} );
|
||||
control.setting.bind( function( newValue ) {
|
||||
if ( newValue[ propertyName ] !== element.get() ) {
|
||||
element.set( newValue[ propertyName ] );
|
||||
}
|
||||
} );
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
ready: function() {
|
||||
var control = this;
|
||||
// Shortcut so that we don't have to use _.bind every time we add a callback.
|
||||
_.bindAll( control, 'openFrame', 'select' );
|
||||
// Bind events, with delegation to facilitate re-rendering.
|
||||
control.container.on( 'click keydown', '.upload-button', function( e ) {
|
||||
let event = new CustomEvent( 'kadenceOpenMediaModal', {
|
||||
'detail': false,
|
||||
} );
|
||||
document.dispatchEvent( event );
|
||||
control.openFrame( e );
|
||||
} );
|
||||
|
||||
control._setUpSettingRootLinks();
|
||||
control._setUpSettingPropertyLinks();
|
||||
|
||||
wp.customize.Control.prototype.ready.call( control );
|
||||
|
||||
control.setting.bind( control.renderContent() );
|
||||
|
||||
control.deferred.embedded.done( function() {
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Embed the control in the document.
|
||||
*
|
||||
* Override the embed() method to do nothing,
|
||||
* so that the control isn't embedded on load,
|
||||
* unless the containing section is already expanded.
|
||||
*
|
||||
* @returns {null}
|
||||
*/
|
||||
embed: function() {
|
||||
var control = this,
|
||||
sectionId = control.section();
|
||||
|
||||
if ( ! sectionId ) {
|
||||
return;
|
||||
}
|
||||
|
||||
wp.customize.section( sectionId, function( section ) {
|
||||
if ( section.expanded() || wp.customize.settings.autofocus.control === control.id ) {
|
||||
control.actuallyEmbed();
|
||||
} else {
|
||||
section.expanded.bind( function( expanded ) {
|
||||
if ( expanded ) {
|
||||
control.actuallyEmbed();
|
||||
}
|
||||
} );
|
||||
}
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Deferred embedding of control when actually
|
||||
*
|
||||
* This function is called in Section.onChangeExpanded() so the control
|
||||
* will only get embedded when the Section is first expanded.
|
||||
*
|
||||
* @returns {null}
|
||||
*/
|
||||
actuallyEmbed: function() {
|
||||
var control = this;
|
||||
if ( 'resolved' === control.deferred.embedded.state() ) {
|
||||
return;
|
||||
}
|
||||
control.renderContent();
|
||||
control.deferred.embedded.resolve(); // This triggers control.ready().
|
||||
// Fire event after control is initialized.
|
||||
control.container.trigger( 'init' );
|
||||
},
|
||||
|
||||
/**
|
||||
* This is not working with autofocus.
|
||||
*
|
||||
* @param {object} [args] Args.
|
||||
* @returns {null}
|
||||
*/
|
||||
focus: function( args ) {
|
||||
var control = this;
|
||||
control.actuallyEmbed();
|
||||
wp.customize.Control.prototype.focus.call( control, args );
|
||||
},
|
||||
/**
|
||||
* Callback handler for when an attachment is selected in the media modal.
|
||||
* Gets the selected image information, and sets it within the control.
|
||||
*/
|
||||
select: function() {
|
||||
// Get the attachment from the modal frame.
|
||||
var node,
|
||||
attachment = this.frame.state().get( 'selection' ).first().toJSON(),
|
||||
device = wp.customize.previewedDevice.get(),
|
||||
settings = this.setting();
|
||||
// This is a hack to make the popup background work, I don't like this and need to find a better solution.
|
||||
if ( this.id && this.id === 'header_popup_background' ) {
|
||||
if ( device === 'tablet' || device === 'mobile' ) {
|
||||
if ( undefined !== settings['desktop'] && undefined !== settings['desktop'].type && settings['desktop'].type === 'image' ) {
|
||||
if ( undefined !== settings[device] && undefined !== settings[device].type && settings[device].type === 'image' ) {
|
||||
// Leave this alone
|
||||
} else {
|
||||
device = 'desktop';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( undefined === this.params.attachment ) {
|
||||
this.params.attachment = {};
|
||||
}
|
||||
if ( undefined === this.params.input_attrs.attachments ) {
|
||||
this.params.input_attrs.attachments = {};
|
||||
}
|
||||
this.params.input_attrs.attachments[ device ] = attachment;
|
||||
if ( undefined === settings[ device ] ) {
|
||||
settings[ device ] = {};
|
||||
}
|
||||
if ( undefined === settings[ device ].image ) {
|
||||
settings[ device ].image = {};
|
||||
}
|
||||
settings[ device ].image.url = attachment.url;
|
||||
// Set the Customizer setting; the callback takes care of rendering.
|
||||
let event = new CustomEvent( 'kadenceOpenMediaModal', {
|
||||
'detail': true,
|
||||
} );
|
||||
document.dispatchEvent( event );
|
||||
this.setting.set( {
|
||||
...this.setting.get(),
|
||||
...settings,
|
||||
flag: ! this.setting.get().flag
|
||||
} );
|
||||
},
|
||||
} );
|
||||
@@ -0,0 +1,112 @@
|
||||
/* jshint esversion: 6 */
|
||||
|
||||
const { Component, Fragment } = wp.element;
|
||||
import { isString } from "lodash";
|
||||
import KadenceColorPicker from "../../common/color-picker";
|
||||
import SwatchesControl from "../../common/swatches";
|
||||
|
||||
import {
|
||||
getGradientWithColorAtPositionChanged,
|
||||
getGradientWithColorStopAdded,
|
||||
} from "./utils";
|
||||
|
||||
class KadenceGradientColorPicker extends Component {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.state = {
|
||||
color: this.props.color ? this.props.color : "",
|
||||
alreadyInsertedPoint: false,
|
||||
};
|
||||
}
|
||||
render() {
|
||||
const getColorValue = () => {
|
||||
let color;
|
||||
const paletteIndex = this.state.color?.match(/\d+$/)?.[0] - 1;
|
||||
if (
|
||||
undefined !== this.state.color &&
|
||||
"" !== this.state.color &&
|
||||
null !== this.state.color &&
|
||||
isString(this.state.color) &&
|
||||
this.state.color.includes("palette")
|
||||
) {
|
||||
color = this.props.activePalette[paletteIndex]?.color;
|
||||
} else {
|
||||
color = this.state.color;
|
||||
}
|
||||
return color;
|
||||
};
|
||||
return (
|
||||
<div className="kadence-background-color-wrap">
|
||||
<KadenceColorPicker
|
||||
color={getColorValue()}
|
||||
onChangeComplete={(color) => {
|
||||
let rgb = {
|
||||
type: "rgba",
|
||||
value: "",
|
||||
};
|
||||
if (undefined !== color.rgb) {
|
||||
this.setState({ color: color.rgb });
|
||||
rgb.value = color.rgb;
|
||||
} else {
|
||||
this.setState({ color: color.hex });
|
||||
rgb.type = "literal";
|
||||
rgb.value = color.hex;
|
||||
}
|
||||
//console.log( rgb );
|
||||
let newGradient;
|
||||
if (this.state.alreadyInsertedPoint) {
|
||||
newGradient = getGradientWithColorAtPositionChanged(
|
||||
this.props.gradientAST,
|
||||
this.props.insertPosition,
|
||||
rgb
|
||||
);
|
||||
} else {
|
||||
newGradient = getGradientWithColorStopAdded(
|
||||
this.props.gradientAST,
|
||||
this.props.insertPosition,
|
||||
rgb
|
||||
);
|
||||
this.setState({ alreadyInsertedPoint: true });
|
||||
}
|
||||
this.props.onChange(newGradient);
|
||||
}}
|
||||
/>
|
||||
<SwatchesControl
|
||||
colors={this.props.activePalette}
|
||||
isPalette={
|
||||
undefined !== this.state.color &&
|
||||
"" !== this.state.color &&
|
||||
isString(this.state.color) &&
|
||||
this.state.color.includes("palette")
|
||||
? this.state.color
|
||||
: ""
|
||||
}
|
||||
onClick={(color, palette) => {
|
||||
this.setState({ color: palette });
|
||||
let rgb = {
|
||||
type: "literal",
|
||||
value: "var(--global-" + palette + ")",
|
||||
};
|
||||
let newGradient;
|
||||
if (this.state.alreadyInsertedPoint) {
|
||||
newGradient = getGradientWithColorAtPositionChanged(
|
||||
this.props.gradientAST,
|
||||
this.props.insertPosition,
|
||||
rgb
|
||||
);
|
||||
} else {
|
||||
newGradient = getGradientWithColorStopAdded(
|
||||
this.props.gradientAST,
|
||||
this.props.insertPosition,
|
||||
rgb
|
||||
);
|
||||
this.setState({ alreadyInsertedPoint: true });
|
||||
}
|
||||
this.props.onChange(newGradient);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
export default KadenceGradientColorPicker;
|
||||
@@ -0,0 +1,20 @@
|
||||
export const INSERT_POINT_WIDTH = 23;
|
||||
export const GRADIENT_MARKERS_WIDTH = 18;
|
||||
export const MINIMUM_DISTANCE_BETWEEN_INSERTER_AND_MARKER =
|
||||
( INSERT_POINT_WIDTH + GRADIENT_MARKERS_WIDTH ) / 2;
|
||||
export const MINIMUM_ABSOLUTE_LEFT_POSITION = 5;
|
||||
export const MINIMUM_DISTANCE_BETWEEN_POINTS = 0;
|
||||
export const MINIMUM_DISTANCE_BETWEEN_INSERTER_AND_POINT = 10;
|
||||
export const KEYBOARD_CONTROL_POINT_VARIATION = MINIMUM_DISTANCE_BETWEEN_INSERTER_AND_POINT;
|
||||
export const MINIMUM_SIGNIFICANT_MOVE = 5;
|
||||
export const DEFAULT_GRADIENT =
|
||||
'linear-gradient(135deg, rgba(6, 147, 227, 1) 0%, rgb(155, 81, 224) 100%)';
|
||||
export const COLOR_POPOVER_PROPS = {
|
||||
className: 'components-custom-gradient-picker__color-picker-popover kadence-popover-color',
|
||||
position: 'top',
|
||||
};
|
||||
export const DEFAULT_LINEAR_GRADIENT_ANGLE = 180;
|
||||
export const HORIZONTAL_GRADIENT_ORIENTATION = {
|
||||
type: 'angular',
|
||||
value: 90,
|
||||
};
|
||||
@@ -0,0 +1,298 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import classnames from 'classnames';
|
||||
|
||||
/**
|
||||
* WordPress dependencies
|
||||
*/
|
||||
const {
|
||||
useRef,
|
||||
Component,
|
||||
useEffect,
|
||||
} = wp.element;
|
||||
import { __, sprintf } from '@wordpress/i18n';
|
||||
const {
|
||||
useInstanceId,
|
||||
} = wp.compose;
|
||||
|
||||
import KadenceGradientColorPicker from './edit-color-picker';
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
const {
|
||||
Button,
|
||||
ColorPicker,
|
||||
Dropdown,
|
||||
VisuallyHidden,
|
||||
KeyboardShortcuts,
|
||||
} = wp.components;
|
||||
import {
|
||||
getGradientWithColorAtIndexChanged,
|
||||
getGradientWithControlPointRemoved,
|
||||
getGradientWithPositionAtIndexChanged,
|
||||
getGradientWithPositionAtIndexDecreased,
|
||||
getGradientWithPositionAtIndexIncreased,
|
||||
getHorizontalRelativeGradientPosition,
|
||||
isControlPointOverlapping,
|
||||
} from './utils';
|
||||
import {
|
||||
COLOR_POPOVER_PROPS,
|
||||
GRADIENT_MARKERS_WIDTH,
|
||||
MINIMUM_SIGNIFICANT_MOVE,
|
||||
} from './constants';
|
||||
|
||||
class ControlPointKeyboardMove extends Component {
|
||||
constructor() {
|
||||
super( ...arguments );
|
||||
this.increase = this.increase.bind( this );
|
||||
this.decrease = this.decrease.bind( this );
|
||||
this.shortcuts = {
|
||||
right: this.increase,
|
||||
left: this.decrease,
|
||||
};
|
||||
}
|
||||
increase( event ) {
|
||||
// Stop propagation of the key press event to avoid focus moving
|
||||
// to another editor area.
|
||||
event.stopPropagation();
|
||||
const { gradientIndex, onChange, gradientAST } = this.props;
|
||||
onChange(
|
||||
getGradientWithPositionAtIndexIncreased(
|
||||
gradientAST,
|
||||
gradientIndex
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
decrease( event ) {
|
||||
// Stop propagation of the key press event to avoid focus moving
|
||||
// to another editor area.
|
||||
event.stopPropagation();
|
||||
const { gradientIndex, onChange, gradientAST } = this.props;
|
||||
onChange(
|
||||
getGradientWithPositionAtIndexDecreased(
|
||||
gradientAST,
|
||||
gradientIndex
|
||||
)
|
||||
);
|
||||
}
|
||||
render() {
|
||||
const { children } = this.props;
|
||||
return (
|
||||
<KeyboardShortcuts shortcuts={ this.shortcuts }>
|
||||
{ children }
|
||||
</KeyboardShortcuts>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function ControlPointButton( {
|
||||
isOpen,
|
||||
position,
|
||||
color,
|
||||
onChange,
|
||||
gradientIndex,
|
||||
gradientAST,
|
||||
...additionalProps
|
||||
} ) {
|
||||
const instanceId = useInstanceId( ControlPointButton );
|
||||
const descriptionId = `components-custom-gradient-picker__control-point-button-description-${ instanceId }`;
|
||||
return (
|
||||
<ControlPointKeyboardMove
|
||||
onChange={ onChange }
|
||||
gradientIndex={ gradientIndex }
|
||||
gradientAST={ gradientAST }
|
||||
>
|
||||
<Button
|
||||
aria-label={ sprintf(
|
||||
// translators: %1$s: gradient position e.g: 70%, %2$s: gradient color code e.g: rgb(52,121,151).
|
||||
__(
|
||||
'Gradient control point at position %1$s with color code %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 }>
|
||||
{ __(
|
||||
'Use your left or right arrow keys or drag and drop with the mouse to change the gradient position. Press the button to change the color or remove the control point.'
|
||||
) }
|
||||
</VisuallyHidden>
|
||||
</ControlPointKeyboardMove>
|
||||
);
|
||||
}
|
||||
|
||||
export default function ControlPoints( {
|
||||
gradientPickerDomRef,
|
||||
ignoreMarkerPosition,
|
||||
markerPoints,
|
||||
onChange,
|
||||
gradientAST,
|
||||
activePalette,
|
||||
onStartControlPointChange,
|
||||
onStopControlPointChange,
|
||||
} ) {
|
||||
const controlPointMoveState = useRef();
|
||||
|
||||
const onMouseMove = ( event ) => {
|
||||
const relativePosition = getHorizontalRelativeGradientPosition(
|
||||
event.clientX,
|
||||
gradientPickerDomRef.current,
|
||||
GRADIENT_MARKERS_WIDTH
|
||||
);
|
||||
const {
|
||||
gradientAST: referenceGradientAST,
|
||||
position,
|
||||
significantMoveHappened,
|
||||
} = controlPointMoveState.current;
|
||||
if ( ! significantMoveHappened ) {
|
||||
const initialPosition =
|
||||
referenceGradientAST.colorStops[ position ].length.value;
|
||||
if (
|
||||
Math.abs( initialPosition - relativePosition ) >=
|
||||
MINIMUM_SIGNIFICANT_MOVE
|
||||
) {
|
||||
controlPointMoveState.current.significantMoveHappened = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
! isControlPointOverlapping(
|
||||
referenceGradientAST,
|
||||
relativePosition,
|
||||
position
|
||||
)
|
||||
) {
|
||||
onChange(
|
||||
getGradientWithPositionAtIndexChanged(
|
||||
referenceGradientAST,
|
||||
position,
|
||||
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 markerPoints.map(
|
||||
( point, index ) =>
|
||||
point &&
|
||||
ignoreMarkerPosition !== point.positionValue && (
|
||||
<Dropdown
|
||||
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 = {
|
||||
gradientAST,
|
||||
position: index,
|
||||
significantMoveHappened: false,
|
||||
listenersActivated: true,
|
||||
};
|
||||
onStartControlPointChange();
|
||||
window.addEventListener(
|
||||
'mousemove',
|
||||
onMouseMove
|
||||
);
|
||||
window.addEventListener(
|
||||
'mouseup',
|
||||
cleanEventListeners
|
||||
);
|
||||
}
|
||||
} }
|
||||
isOpen={ isOpen }
|
||||
position={ point.position }
|
||||
color={ point.color }
|
||||
onChange={ onChange }
|
||||
gradientAST={ gradientAST }
|
||||
gradientIndex={ index }
|
||||
/>
|
||||
) }
|
||||
renderContent={ ( { onClose } ) => (
|
||||
<>
|
||||
<KadenceGradientColorPicker
|
||||
color={ point.color }
|
||||
activePalette={ activePalette }
|
||||
onChange={ ( value ) => {
|
||||
onChange(
|
||||
getGradientWithColorAtIndexChanged(
|
||||
gradientAST,
|
||||
index,
|
||||
value
|
||||
)
|
||||
);
|
||||
} }
|
||||
/>
|
||||
<Button
|
||||
className="components-custom-gradient-picker__remove-control-point"
|
||||
onClick={ () => {
|
||||
onChange(
|
||||
getGradientWithControlPointRemoved(
|
||||
gradientAST,
|
||||
index
|
||||
)
|
||||
);
|
||||
onClose();
|
||||
} }
|
||||
isLink
|
||||
>
|
||||
{ __( 'Remove Control Point' ) }
|
||||
</Button>
|
||||
</>
|
||||
) }
|
||||
popoverProps={ COLOR_POPOVER_PROPS }
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,260 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
|
||||
import { some } from 'lodash';
|
||||
import classnames from 'classnames';
|
||||
|
||||
/**
|
||||
* WordPress dependencies
|
||||
*/
|
||||
const {
|
||||
useRef,
|
||||
useReducer,
|
||||
useState
|
||||
} = wp.element;
|
||||
import { plusCircle } from '@wordpress/icons';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import {
|
||||
Button,
|
||||
Dropdown,
|
||||
ColorPicker,
|
||||
} from '@wordpress/components';
|
||||
import KadenceGradientColorPicker from './color-picker';
|
||||
|
||||
import ControlPoints from './control-points';
|
||||
import {
|
||||
INSERT_POINT_WIDTH,
|
||||
COLOR_POPOVER_PROPS,
|
||||
MINIMUM_DISTANCE_BETWEEN_INSERTER_AND_POINT,
|
||||
} from './constants';
|
||||
import { serializeGradient } from './serializer';
|
||||
import {
|
||||
getGradientWithColorAtPositionChanged,
|
||||
getGradientWithColorStopAdded,
|
||||
getHorizontalRelativeGradientPosition,
|
||||
getMarkerPoints,
|
||||
getGradientParsed,
|
||||
getLinearGradientRepresentationOfARadial,
|
||||
} from './utils';
|
||||
|
||||
function InsertPoint( {
|
||||
onChange,
|
||||
gradientAST,
|
||||
onOpenInserter,
|
||||
onCloseInserter,
|
||||
insertPosition,
|
||||
activePalette,
|
||||
} ) {
|
||||
const [ alreadyInsertedPoint, setAlreadyInsertedPoint ] = useState( false );
|
||||
return (
|
||||
<Dropdown
|
||||
className="components-custom-gradient-picker__inserter"
|
||||
onClose={ () => {
|
||||
onCloseInserter();
|
||||
} }
|
||||
renderToggle={ ( { isOpen, onToggle } ) => (
|
||||
<Button
|
||||
aria-expanded={ isOpen }
|
||||
onClick={ () => {
|
||||
if ( isOpen ) {
|
||||
onCloseInserter();
|
||||
} else {
|
||||
setAlreadyInsertedPoint( false );
|
||||
onOpenInserter();
|
||||
}
|
||||
onToggle();
|
||||
} }
|
||||
className="components-custom-gradient-picker__insert-point"
|
||||
icon={ plusCircle }
|
||||
style={ {
|
||||
left:
|
||||
insertPosition !== null
|
||||
? `${ insertPosition }%`
|
||||
: undefined,
|
||||
} }
|
||||
/>
|
||||
) }
|
||||
renderContent={ () => (
|
||||
<KadenceGradientColorPicker
|
||||
color={''}
|
||||
onChange={ ( value ) => onChange( value ) }
|
||||
activePalette={ activePalette }
|
||||
gradientAST={ gradientAST }
|
||||
insertPosition={ insertPosition }
|
||||
/>
|
||||
) }
|
||||
popoverProps={ COLOR_POPOVER_PROPS }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function customGradientBarReducer( state, action ) {
|
||||
switch ( action.type ) {
|
||||
case 'MOVE_INSERTER':
|
||||
if ( state.id === 'IDLE' || state.id === 'MOVING_INSERTER' ) {
|
||||
return {
|
||||
id: 'MOVING_INSERTER',
|
||||
insertPosition: action.insertPosition,
|
||||
};
|
||||
}
|
||||
break;
|
||||
case 'STOP_INSERTER_MOVE':
|
||||
if ( state.id === 'MOVING_INSERTER' ) {
|
||||
return {
|
||||
id: 'IDLE',
|
||||
};
|
||||
}
|
||||
break;
|
||||
case 'OPEN_INSERTER':
|
||||
if ( state.id === 'MOVING_INSERTER' ) {
|
||||
return {
|
||||
id: 'INSERTING_CONTROL_POINT',
|
||||
insertPosition: state.insertPosition,
|
||||
};
|
||||
}
|
||||
break;
|
||||
case 'CLOSE_INSERTER':
|
||||
if ( state.id === 'INSERTING_CONTROL_POINT' ) {
|
||||
return {
|
||||
id: 'IDLE',
|
||||
};
|
||||
}
|
||||
break;
|
||||
case 'START_CONTROL_CHANGE':
|
||||
if ( state.id === 'IDLE' ) {
|
||||
return {
|
||||
id: 'MOVING_CONTROL_POINT',
|
||||
};
|
||||
}
|
||||
break;
|
||||
case 'STOP_CONTROL_CHANGE':
|
||||
if ( state.id === 'MOVING_CONTROL_POINT' ) {
|
||||
return {
|
||||
id: 'IDLE',
|
||||
};
|
||||
}
|
||||
break;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
const customGradientBarReducerInitialState = { id: 'IDLE' };
|
||||
|
||||
export default function CustomGradientBar( { value, onChange, activePalette } ) {
|
||||
const { gradientAST, gradientValue, hasGradient } = getGradientParsed(
|
||||
value
|
||||
);
|
||||
|
||||
const onGradientStructureChange = ( newGradientStructure ) => {
|
||||
onChange( serializeGradient( newGradientStructure ) );
|
||||
};
|
||||
|
||||
const gradientPickerDomRef = useRef();
|
||||
const markerPoints = getMarkerPoints( gradientAST );
|
||||
|
||||
const [ gradientBarState, gradientBarStateDispatch ] = useReducer(
|
||||
customGradientBarReducer,
|
||||
customGradientBarReducerInitialState
|
||||
);
|
||||
const onMouseEnterAndMove = ( event ) => {
|
||||
const insertPosition = getHorizontalRelativeGradientPosition(
|
||||
event.clientX,
|
||||
gradientPickerDomRef.current,
|
||||
INSERT_POINT_WIDTH
|
||||
);
|
||||
|
||||
// If the insert point is close to an existing control point don't show it.
|
||||
if (
|
||||
some( markerPoints, ( { positionValue } ) => {
|
||||
return (
|
||||
Math.abs( insertPosition - positionValue ) <
|
||||
MINIMUM_DISTANCE_BETWEEN_INSERTER_AND_POINT
|
||||
);
|
||||
} )
|
||||
) {
|
||||
if ( gradientBarState.id === 'MOVING_INSERTER' ) {
|
||||
gradientBarStateDispatch( { type: 'STOP_INSERTER_MOVE' } );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
gradientBarStateDispatch( { type: 'MOVE_INSERTER', insertPosition } );
|
||||
};
|
||||
|
||||
const onMouseLeave = () => {
|
||||
gradientBarStateDispatch( { type: 'STOP_INSERTER_MOVE' } );
|
||||
};
|
||||
|
||||
const isMovingInserter = gradientBarState.id === 'MOVING_INSERTER';
|
||||
const isInsertingControlPoint =
|
||||
gradientBarState.id === 'INSERTING_CONTROL_POINT';
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ gradientPickerDomRef }
|
||||
className={ classnames(
|
||||
'components-custom-gradient-picker__gradient-bar',
|
||||
{ 'has-gradient': hasGradient }
|
||||
) }
|
||||
onMouseEnter={ onMouseEnterAndMove }
|
||||
onMouseMove={ onMouseEnterAndMove }
|
||||
// On radial gradients the bar should display a linear gradient.
|
||||
// On radial gradients the bar represents a slice of the gradient from the center until the outside.
|
||||
style={ {
|
||||
background:
|
||||
gradientAST.type === 'radial-gradient'
|
||||
? getLinearGradientRepresentationOfARadial(
|
||||
gradientAST
|
||||
)
|
||||
: gradientValue,
|
||||
} }
|
||||
onMouseLeave={ onMouseLeave }
|
||||
>
|
||||
<div className="components-custom-gradient-picker__markers-container">
|
||||
{ ( isMovingInserter || isInsertingControlPoint ) && (
|
||||
<InsertPoint
|
||||
insertPosition={ gradientBarState.insertPosition }
|
||||
onChange={ onGradientStructureChange }
|
||||
gradientAST={ gradientAST }
|
||||
onOpenInserter={ () => {
|
||||
gradientBarStateDispatch( {
|
||||
type: 'OPEN_INSERTER',
|
||||
} );
|
||||
} }
|
||||
onCloseInserter={ () => {
|
||||
gradientBarStateDispatch( {
|
||||
type: 'CLOSE_INSERTER',
|
||||
} );
|
||||
} }
|
||||
activePalette={ activePalette }
|
||||
/>
|
||||
) }
|
||||
<ControlPoints
|
||||
gradientPickerDomRef={ gradientPickerDomRef }
|
||||
ignoreMarkerPosition={
|
||||
isInsertingControlPoint
|
||||
? gradientBarState.insertPosition
|
||||
: undefined
|
||||
}
|
||||
markerPoints={ markerPoints }
|
||||
onChange={ onGradientStructureChange }
|
||||
gradientAST={ gradientAST }
|
||||
onStartControlPointChange={ () => {
|
||||
gradientBarStateDispatch( {
|
||||
type: 'START_CONTROL_CHANGE',
|
||||
} );
|
||||
} }
|
||||
onStopControlPointChange={ () => {
|
||||
gradientBarStateDispatch( {
|
||||
type: 'STOP_CONTROL_CHANGE',
|
||||
} );
|
||||
} }
|
||||
activePalette={ activePalette }
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,340 @@
|
||||
// Copyright (c) 2014 Rafael Caricio. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
export default function CustomGradientParser( code ) {
|
||||
var tokens = {
|
||||
linearGradient: /^(\-(webkit|o|ms|moz)\-)?(linear\-gradient)/i,
|
||||
repeatingLinearGradient: /^(\-(webkit|o|ms|moz)\-)?(repeating\-linear\-gradient)/i,
|
||||
radialGradient: /^(\-(webkit|o|ms|moz)\-)?(radial\-gradient)/i,
|
||||
repeatingRadialGradient: /^(\-(webkit|o|ms|moz)\-)?(repeating\-radial\-gradient)/i,
|
||||
sideOrCorner: /^to (left (top|bottom)|right (top|bottom)|left|right|top|bottom)/i,
|
||||
extentKeywords: /^(closest\-side|closest\-corner|farthest\-side|farthest\-corner|contain|cover)/,
|
||||
positionKeywords: /^(left|center|right|top|bottom)/i,
|
||||
pixelValue: /^(-?(([0-9]*\.[0-9]+)|([0-9]+\.?)))px/,
|
||||
percentageValue: /^(-?(([0-9]*\.[0-9]+)|([0-9]+\.?)))\%/,
|
||||
emValue: /^(-?(([0-9]*\.[0-9]+)|([0-9]+\.?)))em/,
|
||||
angleValue: /^(-?(([0-9]*\.[0-9]+)|([0-9]+\.?)))deg/,
|
||||
startCall: /^\(/,
|
||||
endCall: /^\)/,
|
||||
comma: /^,/,
|
||||
hexColor: /^\#([0-9a-fA-F]+)/,
|
||||
literalColor: /^([a-zA-Z]+)/,
|
||||
varColor: /^var([0-9a-zA-Z\\)\\(-]+)/,
|
||||
rgbColor: /^rgb/i,
|
||||
rgbaColor: /^rgba/i,
|
||||
number: /^(([0-9]*\.[0-9]+)|([0-9]+\.?))/
|
||||
};
|
||||
|
||||
var input = code.toString();
|
||||
|
||||
function error(msg) {
|
||||
var err = new Error(input + ': ' + msg);
|
||||
err.source = input;
|
||||
throw err;
|
||||
}
|
||||
|
||||
function getAST() {
|
||||
var ast = matchListDefinitions();
|
||||
|
||||
if (input.length > 0) {
|
||||
error('Invalid input not EOF');
|
||||
}
|
||||
|
||||
return ast;
|
||||
}
|
||||
|
||||
function matchListDefinitions() {
|
||||
return matchListing(matchDefinition);
|
||||
}
|
||||
|
||||
function matchDefinition() {
|
||||
return matchGradient(
|
||||
'linear-gradient',
|
||||
tokens.linearGradient,
|
||||
matchLinearOrientation) ||
|
||||
|
||||
matchGradient(
|
||||
'repeating-linear-gradient',
|
||||
tokens.repeatingLinearGradient,
|
||||
matchLinearOrientation) ||
|
||||
|
||||
matchGradient(
|
||||
'radial-gradient',
|
||||
tokens.radialGradient,
|
||||
matchListRadialOrientations) ||
|
||||
|
||||
matchGradient(
|
||||
'repeating-radial-gradient',
|
||||
tokens.repeatingRadialGradient,
|
||||
matchListRadialOrientations);
|
||||
}
|
||||
|
||||
function matchGradient(gradientType, pattern, orientationMatcher) {
|
||||
return matchCall(pattern, function(captures) {
|
||||
|
||||
var orientation = orientationMatcher();
|
||||
if (orientation) {
|
||||
if (!scan(tokens.comma)) {
|
||||
error('Missing comma before color stops');
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
type: gradientType,
|
||||
orientation: orientation,
|
||||
colorStops: matchListing(matchColorStop)
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function matchCall(pattern, callback) {
|
||||
var captures = scan(pattern);
|
||||
if (captures) {
|
||||
if (!scan(tokens.startCall)) {
|
||||
error('Missing (');
|
||||
}
|
||||
|
||||
var result = callback(captures);
|
||||
|
||||
if (!scan(tokens.endCall)) {
|
||||
error('Missing )');
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
function matchLinearOrientation() {
|
||||
return matchSideOrCorner() ||
|
||||
matchAngle();
|
||||
}
|
||||
|
||||
function matchSideOrCorner() {
|
||||
return match('directional', tokens.sideOrCorner, 1);
|
||||
}
|
||||
|
||||
function matchAngle() {
|
||||
return match('angular', tokens.angleValue, 1);
|
||||
}
|
||||
|
||||
function matchListRadialOrientations() {
|
||||
var radialOrientations,
|
||||
radialOrientation = matchRadialOrientation(),
|
||||
lookaheadCache;
|
||||
|
||||
if (radialOrientation) {
|
||||
radialOrientations = [];
|
||||
radialOrientations.push(radialOrientation);
|
||||
|
||||
lookaheadCache = input;
|
||||
if (scan(tokens.comma)) {
|
||||
radialOrientation = matchRadialOrientation();
|
||||
if (radialOrientation) {
|
||||
radialOrientations.push(radialOrientation);
|
||||
} else {
|
||||
input = lookaheadCache;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return radialOrientations;
|
||||
}
|
||||
|
||||
function matchRadialOrientation() {
|
||||
var radialType = matchCircle() ||
|
||||
matchEllipse();
|
||||
|
||||
if (radialType) {
|
||||
radialType.at = matchAtPosition();
|
||||
} else {
|
||||
var extent = matchExtentKeyword();
|
||||
if (extent) {
|
||||
radialType = extent;
|
||||
var positionAt = matchAtPosition();
|
||||
if (positionAt) {
|
||||
radialType.at = positionAt;
|
||||
}
|
||||
} else {
|
||||
var defaultPosition = matchPositioning();
|
||||
if (defaultPosition) {
|
||||
radialType = {
|
||||
type: 'default-radial',
|
||||
at: defaultPosition
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return radialType;
|
||||
}
|
||||
|
||||
function matchCircle() {
|
||||
var circle = match('shape', /^(circle)/i, 0);
|
||||
|
||||
if (circle) {
|
||||
circle.style = matchLength() || matchExtentKeyword();
|
||||
}
|
||||
|
||||
return circle;
|
||||
}
|
||||
|
||||
function matchEllipse() {
|
||||
var ellipse = match('shape', /^(ellipse)/i, 0);
|
||||
|
||||
if (ellipse) {
|
||||
ellipse.style = matchDistance() || matchExtentKeyword();
|
||||
}
|
||||
|
||||
return ellipse;
|
||||
}
|
||||
|
||||
function matchExtentKeyword() {
|
||||
return match('extent-keyword', tokens.extentKeywords, 1);
|
||||
}
|
||||
|
||||
function matchAtPosition() {
|
||||
if (match('position', /^at/, 0)) {
|
||||
var positioning = matchPositioning();
|
||||
|
||||
if (!positioning) {
|
||||
error('Missing positioning value');
|
||||
}
|
||||
|
||||
return positioning;
|
||||
}
|
||||
}
|
||||
|
||||
function matchPositioning() {
|
||||
var location = matchCoordinates();
|
||||
|
||||
if (location.x || location.y) {
|
||||
return {
|
||||
type: 'position',
|
||||
value: location
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function matchCoordinates() {
|
||||
return {
|
||||
x: matchDistance(),
|
||||
y: matchDistance()
|
||||
};
|
||||
}
|
||||
|
||||
function matchListing(matcher) {
|
||||
var captures = matcher(),
|
||||
result = [];
|
||||
|
||||
if (captures) {
|
||||
result.push(captures);
|
||||
while (scan(tokens.comma)) {
|
||||
captures = matcher();
|
||||
if (captures) {
|
||||
result.push(captures);
|
||||
} else {
|
||||
error('One extra comma');
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function matchColorStop() {
|
||||
var color = matchColor();
|
||||
|
||||
if (!color) {
|
||||
error('Expected color definition');
|
||||
}
|
||||
|
||||
color.length = matchDistance();
|
||||
return color;
|
||||
}
|
||||
|
||||
function matchColor() {
|
||||
return matchHexColor() ||
|
||||
matchVarColor() ||
|
||||
matchRGBAColor() ||
|
||||
matchRGBColor() ||
|
||||
matchLiteralColor();
|
||||
}
|
||||
|
||||
function matchLiteralColor() {
|
||||
return match('literal', tokens.literalColor, 0);
|
||||
}
|
||||
function matchVarColor() {
|
||||
return match('literal', tokens.varColor, 0);
|
||||
}
|
||||
function matchHexColor() {
|
||||
return match('hex', tokens.hexColor, 1);
|
||||
}
|
||||
function matchRGBColor() {
|
||||
return matchCall(tokens.rgbColor, function() {
|
||||
return {
|
||||
type: 'rgb',
|
||||
value: matchListing(matchNumber)
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function matchRGBAColor() {
|
||||
return matchCall(tokens.rgbaColor, function() {
|
||||
return {
|
||||
type: 'rgba',
|
||||
value: matchListing(matchNumber)
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function matchNumber() {
|
||||
return scan(tokens.number)[1];
|
||||
}
|
||||
|
||||
function matchDistance() {
|
||||
return match('%', tokens.percentageValue, 1) ||
|
||||
matchPositionKeyword() ||
|
||||
matchLength();
|
||||
}
|
||||
|
||||
function matchPositionKeyword() {
|
||||
return match('position-keyword', tokens.positionKeywords, 1);
|
||||
}
|
||||
|
||||
function matchLength() {
|
||||
return match('px', tokens.pixelValue, 1) ||
|
||||
match('em', tokens.emValue, 1);
|
||||
}
|
||||
|
||||
function match(type, pattern, captureIndex) {
|
||||
var captures = scan(pattern);
|
||||
if (captures) {
|
||||
return {
|
||||
type: type,
|
||||
value: captures[captureIndex]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function scan(regexp) {
|
||||
var captures,
|
||||
blankCaptures;
|
||||
|
||||
blankCaptures = /^[\n\r\t\s]+/.exec(input);
|
||||
if (blankCaptures) {
|
||||
consume(blankCaptures[0].length);
|
||||
}
|
||||
|
||||
captures = regexp.exec(input);
|
||||
if (captures) {
|
||||
consume(captures[0].length);
|
||||
}
|
||||
|
||||
return captures;
|
||||
}
|
||||
|
||||
function consume(size) {
|
||||
input = input.substr(size);
|
||||
}
|
||||
return getAST();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/* jshint esversion: 6 */
|
||||
|
||||
const { Component, Fragment } = wp.element;
|
||||
import { isString } from "lodash";
|
||||
import KadenceColorPicker from "../../common/color-picker";
|
||||
import SwatchesControl from "../../common/swatches";
|
||||
|
||||
class KadenceGradientColorPicker extends Component {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.state = {
|
||||
color: this.props.color ? this.props.color : "",
|
||||
};
|
||||
}
|
||||
render() {
|
||||
const getColorValue = () => {
|
||||
let color;
|
||||
const paletteIndex = this.state.color?.match(/\d+$/)?.[0] - 1;
|
||||
if (
|
||||
undefined !== this.state.color &&
|
||||
"" !== this.state.color &&
|
||||
isString(this.state.color) &&
|
||||
this.state.color.includes("palette") &&
|
||||
this.props.activePalette &&
|
||||
this.props.activePalette[paletteIndex]
|
||||
) {
|
||||
color = this.props.activePalette[paletteIndex]?.color;
|
||||
} else {
|
||||
color = this.state.color;
|
||||
}
|
||||
return color;
|
||||
};
|
||||
return (
|
||||
<div className="kadence-background-color-wrap">
|
||||
<KadenceColorPicker
|
||||
color={getColorValue()}
|
||||
onChangeComplete={(color) => {
|
||||
let rgb = {
|
||||
type: "rgba",
|
||||
value: "",
|
||||
};
|
||||
if (undefined !== color.rgb) {
|
||||
this.setState({ color: color.rgb });
|
||||
rgb.value = color.rgb;
|
||||
} else {
|
||||
this.setState({ color: color.hex });
|
||||
rgb.type = "literal";
|
||||
rgb.value = color.hex;
|
||||
}
|
||||
|
||||
this.props.onChange(rgb);
|
||||
}}
|
||||
/>
|
||||
<SwatchesControl
|
||||
colors={this.props.activePalette}
|
||||
isPalette={
|
||||
undefined !== this.state.color &&
|
||||
"" !== this.state.color &&
|
||||
isString(this.state.color) &&
|
||||
this.state.color.includes("palette")
|
||||
? this.state.color
|
||||
: ""
|
||||
}
|
||||
onClick={(color, palette) => {
|
||||
this.setState({
|
||||
color: "var(--global-" + palette + ")",
|
||||
});
|
||||
let rgb = {
|
||||
type: "literal",
|
||||
value: "var(--global-" + palette + ")",
|
||||
};
|
||||
this.props.onChange(rgb);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
export default KadenceGradientColorPicker;
|
||||
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* 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 (
|
||||
<SVG
|
||||
fill="none"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
width="20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<LinearGradient
|
||||
id={ linerGradientId }
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="10"
|
||||
x2="10"
|
||||
y1="1"
|
||||
y2="19"
|
||||
>
|
||||
<Stop offset="0" stopColor="#000000" />
|
||||
<Stop offset="1" stopColor="#ffffff" />
|
||||
</LinearGradient>
|
||||
<Path d="m1 1h18v18h-18z" fill={ `url(#${ linerGradientId })` } />
|
||||
</SVG>
|
||||
);
|
||||
} );
|
||||
|
||||
export const RadialGradientIcon = withInstanceId( ( { instanceId } ) => {
|
||||
const radialGradientId = `radial-gradient-${ instanceId }`;
|
||||
return (
|
||||
<SVG
|
||||
fill="none"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
width="20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<RadialGradient
|
||||
id={ radialGradientId }
|
||||
cx="0"
|
||||
cy="0"
|
||||
gradientTransform="matrix(0 9 -9 0 10 10)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
r="1"
|
||||
>
|
||||
<Stop offset="0" stopColor="#000000" />
|
||||
<Stop offset="1" stopColor="#ffffff" />
|
||||
</RadialGradient>
|
||||
<Circle
|
||||
cx="10"
|
||||
cy="10"
|
||||
fill={ `url(#${ radialGradientId })` }
|
||||
r="9"
|
||||
/>
|
||||
</SVG>
|
||||
);
|
||||
} );
|
||||
@@ -0,0 +1,123 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { get, omit } from 'lodash';
|
||||
|
||||
/**
|
||||
* WordPress dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { LinearGradientIcon, RadialGradientIcon } from './icons';
|
||||
import CustomGradientBar from './custom-gradient-bar';
|
||||
import { getGradientParsed } from './utils';
|
||||
import { serializeGradient } from './serializer';
|
||||
import {
|
||||
DEFAULT_LINEAR_GRADIENT_ANGLE,
|
||||
HORIZONTAL_GRADIENT_ORIENTATION,
|
||||
} from './constants';
|
||||
|
||||
const {
|
||||
ToolbarGroup,
|
||||
AnglePickerControl,
|
||||
BaseControl,
|
||||
} = wp.components;
|
||||
|
||||
const GradientAnglePicker = ( { gradientAST, hasGradient, onChange } ) => {
|
||||
const angle = get(
|
||||
gradientAST,
|
||||
[ 'orientation', 'value' ],
|
||||
DEFAULT_LINEAR_GRADIENT_ANGLE
|
||||
);
|
||||
const onAngleChange = ( newAngle ) => {
|
||||
onChange(
|
||||
serializeGradient( {
|
||||
...gradientAST,
|
||||
orientation: {
|
||||
type: 'angular',
|
||||
value: newAngle,
|
||||
},
|
||||
} )
|
||||
);
|
||||
};
|
||||
return (
|
||||
<AnglePickerControl
|
||||
value={ hasGradient ? angle : '' }
|
||||
onChange={ onAngleChange }
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const GradientTypePicker = ( { gradientAST, hasGradient, onChange } ) => {
|
||||
const { type } = gradientAST;
|
||||
const onSetLinearGradient = () => {
|
||||
onChange(
|
||||
serializeGradient( {
|
||||
...gradientAST,
|
||||
...( gradientAST.orientation
|
||||
? {}
|
||||
: { orientation: HORIZONTAL_GRADIENT_ORIENTATION } ),
|
||||
type: 'linear-gradient',
|
||||
} )
|
||||
);
|
||||
};
|
||||
|
||||
const onSetRadialGradient = () => {
|
||||
onChange(
|
||||
serializeGradient( {
|
||||
...omit( gradientAST, [ 'orientation' ] ),
|
||||
type: 'radial-gradient',
|
||||
} )
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<BaseControl className="components-custom-gradient-picker__type-picker">
|
||||
<BaseControl.VisualLabel>{ __( 'Type' ) }</BaseControl.VisualLabel>
|
||||
<ToolbarGroup
|
||||
className="components-custom-gradient-picker__toolbar"
|
||||
controls={ [
|
||||
{
|
||||
icon: <LinearGradientIcon />,
|
||||
title: __( 'Linear Gradient' ),
|
||||
isActive: hasGradient && type === 'linear-gradient',
|
||||
onClick: onSetLinearGradient,
|
||||
},
|
||||
{
|
||||
icon: <RadialGradientIcon />,
|
||||
title: __( 'Radial Gradient' ),
|
||||
isActive: hasGradient && type === 'radial-gradient',
|
||||
onClick: onSetRadialGradient,
|
||||
},
|
||||
] }
|
||||
/>
|
||||
</BaseControl>
|
||||
);
|
||||
};
|
||||
|
||||
export default function CustomGradientPicker( { value, onChange, activePalette } ) {
|
||||
const { gradientAST, hasGradient } = getGradientParsed( value );
|
||||
const { type } = gradientAST;
|
||||
return (
|
||||
<div className="components-custom-gradient-picker">
|
||||
<CustomGradientBar value={ value } onChange={ onChange } activePalette={ activePalette } />
|
||||
<div className="components-custom-gradient-picker__ui-line">
|
||||
<GradientTypePicker
|
||||
gradientAST={ gradientAST }
|
||||
hasGradient={ hasGradient }
|
||||
onChange={ onChange }
|
||||
/>
|
||||
{ type === 'linear-gradient' && (
|
||||
<GradientAnglePicker
|
||||
gradientAST={ gradientAST }
|
||||
hasGradient={ hasGradient }
|
||||
onChange={ onChange }
|
||||
/>
|
||||
) }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { get, omit } from 'lodash';
|
||||
/**
|
||||
* WordPress dependencies
|
||||
*/
|
||||
import { PanelBody, RadioControl, RangeControl } from '@wordpress/components';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { useState } from '@wordpress/element';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { colorsUtils } from '../mobile/color-settings/utils';
|
||||
import { performLayoutAnimation } from '../mobile/layout-animation';
|
||||
import { getGradientParsed } from './utils';
|
||||
import { serializeGradient } from './serializer';
|
||||
import {
|
||||
DEFAULT_LINEAR_GRADIENT_ANGLE,
|
||||
HORIZONTAL_GRADIENT_ORIENTATION,
|
||||
} from './constants';
|
||||
import styles from './style.scss';
|
||||
|
||||
function CustomGradientPicker( { currentValue, setColor, isGradientColor } ) {
|
||||
const [ gradientOrientation, setGradientOrientation ] = useState(
|
||||
HORIZONTAL_GRADIENT_ORIENTATION
|
||||
);
|
||||
|
||||
const { getGradientType, gradients, gradientOptions } = colorsUtils;
|
||||
const { gradientAST } = getGradientParsed( currentValue );
|
||||
const gradientType = getGradientType( currentValue );
|
||||
|
||||
function isLinearGradient( type ) {
|
||||
return type === gradients.linear;
|
||||
}
|
||||
|
||||
function getGradientColor( type ) {
|
||||
const orientation = get( gradientAST, [ 'orientation' ] );
|
||||
|
||||
if ( orientation ) {
|
||||
setGradientOrientation( orientation );
|
||||
}
|
||||
|
||||
return serializeGradient(
|
||||
isLinearGradient( type )
|
||||
? {
|
||||
...gradientAST,
|
||||
...( gradientAST.orientation
|
||||
? {}
|
||||
: {
|
||||
orientation: gradientOrientation,
|
||||
} ),
|
||||
type,
|
||||
}
|
||||
: {
|
||||
...omit( gradientAST, [ 'orientation' ] ),
|
||||
type,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function onGradientTypeChange( type ) {
|
||||
const gradientColor = getGradientColor( type );
|
||||
performLayoutAnimation();
|
||||
setColor( gradientColor );
|
||||
}
|
||||
|
||||
function setGradientAngle( value ) {
|
||||
const gradientColor = serializeGradient( {
|
||||
...gradientAST,
|
||||
orientation: {
|
||||
type: 'angular',
|
||||
value,
|
||||
},
|
||||
} );
|
||||
|
||||
if ( isGradientColor && gradientColor !== currentValue ) {
|
||||
setColor( gradientColor );
|
||||
}
|
||||
}
|
||||
|
||||
function getGradientAngle() {
|
||||
return get(
|
||||
gradientAST,
|
||||
[ 'orientation', 'value' ],
|
||||
DEFAULT_LINEAR_GRADIENT_ANGLE
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<PanelBody title={ __( 'Gradient Type' ) }>
|
||||
<RadioControl
|
||||
selected={ gradientType }
|
||||
options={ gradientOptions }
|
||||
onChange={ onGradientTypeChange }
|
||||
/>
|
||||
</PanelBody>
|
||||
{ isLinearGradient( gradientType ) && (
|
||||
<PanelBody style={ styles.angleControl }>
|
||||
<RangeControl
|
||||
label={ __( 'Angle' ) }
|
||||
minimumValue={ 0 }
|
||||
maximumValue={ 360 }
|
||||
value={ getGradientAngle() }
|
||||
onChange={ setGradientAngle }
|
||||
/>
|
||||
</PanelBody>
|
||||
) }
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default CustomGradientPicker;
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { compact, get } from 'lodash';
|
||||
|
||||
export function serializeGradientColor( { type, value } ) {
|
||||
if ( type === 'literal' || type === 'hex' ) {
|
||||
return value;
|
||||
}
|
||||
return `${ type }(${ value.join( ',' ) })`;
|
||||
}
|
||||
|
||||
export function serializeGradientPosition( { type, value } ) {
|
||||
return `${ value }${ type }`;
|
||||
}
|
||||
|
||||
export function serializeGradientColorStop( { type, value, length } ) {
|
||||
return `${ serializeGradientColor( {
|
||||
type,
|
||||
value,
|
||||
} ) } ${ serializeGradientPosition( length ) }`;
|
||||
}
|
||||
|
||||
export function serializeGradientOrientation( orientation ) {
|
||||
if ( ! orientation || orientation.type !== 'angular' ) {
|
||||
return;
|
||||
}
|
||||
return `${ orientation.value }deg`;
|
||||
}
|
||||
|
||||
export function serializeGradient( { type, orientation, colorStops } ) {
|
||||
const serializedOrientation = serializeGradientOrientation( orientation );
|
||||
//console.log( colorStops );
|
||||
const serializedColorStops = colorStops
|
||||
.sort( ( colorStop1, colorStop2 ) => {
|
||||
return (
|
||||
get( colorStop1, [ 'length', 'value' ], 0 ) -
|
||||
get( colorStop2, [ 'length', 'value' ], 0 )
|
||||
);
|
||||
} )
|
||||
.map( serializeGradientColorStop );
|
||||
|
||||
// console.log(`${ type }(${ compact( [
|
||||
// serializedOrientation,
|
||||
// ...serializedColorStops,
|
||||
// ] ).join( ',' ) })`);
|
||||
|
||||
|
||||
return `${ type }(${ compact( [
|
||||
serializedOrientation,
|
||||
...serializedColorStops,
|
||||
] ).join( ',' ) })`;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
.angleControl {
|
||||
padding-top: (if(variable-exists(grid-unit-20), $grid-unit-20, 20px) / 2);
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
@use 'sass:meta';
|
||||
|
||||
$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: (if(meta.variable-exists(grid-unit-10), $grid-unit-10, 10px));
|
||||
}
|
||||
|
||||
.components-custom-gradient-picker__gradient-bar:not(.has-gradient) {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
$button-size-small: 24px;
|
||||
$white: #fff;
|
||||
.components-custom-gradient-picker__gradient-bar {
|
||||
width: 100%;
|
||||
height: $button-size-small;
|
||||
border-radius: $button-size-small;
|
||||
margin-bottom: (if(meta.variable-exists(grid-unit-10), $grid-unit-10, 10px));
|
||||
padding-left: $components-custom-gradient-picker__padding;
|
||||
padding-right: $button-size-small - $components-custom-gradient-picker__padding;
|
||||
|
||||
.components-custom-gradient-picker__markers-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.components-custom-gradient-picker__insert-point {
|
||||
border-radius: 50%;
|
||||
background: $white;
|
||||
padding: 2px;
|
||||
min-width: $button-size-small;
|
||||
width: $button-size-small;
|
||||
height: $button-size-small;
|
||||
position: relative;
|
||||
|
||||
svg {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.components-custom-gradient-picker__control-point-button {
|
||||
border: 2px solid $white;
|
||||
border-radius: 50%;
|
||||
height: 18px;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
width: 18px;
|
||||
top: $components-custom-gradient-picker__padding;
|
||||
|
||||
&.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.components-custom-gradient-picker__color-picker-popover .components-custom-gradient-picker__remove-control-point {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.components-custom-gradient-picker__inserter {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.components-custom-gradient-picker__liner-gradient-indicator {
|
||||
display: inline-block;
|
||||
flex: 0 auto;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.components-custom-gradient-picker__ui-line {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.components-custom-gradient-picker .components-custom-gradient-picker__ui-line {
|
||||
.components-base-control.components-angle-picker,
|
||||
.components-base-control.components-custom-gradient-picker__type-picker {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.components-custom-gradient-picker .components-custom-gradient-picker__toolbar {
|
||||
border: none;
|
||||
|
||||
// Work-around to target the inner button containers rendered by <ToolbarGroup />
|
||||
> div + div {
|
||||
margin-left: 1px;
|
||||
}
|
||||
|
||||
button {
|
||||
&.is-pressed {
|
||||
$dark-gray-200: #999;
|
||||
> svg {
|
||||
background: $white;
|
||||
border: 1px solid $dark-gray-200;
|
||||
border-radius: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import classnames from 'classnames';
|
||||
import { flatMap } from 'lodash';
|
||||
|
||||
/**
|
||||
* WordPress dependencies
|
||||
*/
|
||||
import { useContext } from '@wordpress/element';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import ToolbarButton from '../toolbar-button';
|
||||
import ToolbarGroupCollapsed from './toolbar-group-collapsed';
|
||||
import ToolbarContext from '../toolbar-context';
|
||||
|
||||
const ToolbarGroupContainer = ( { className, children, ...props } ) => (
|
||||
<div className={ className } { ...props }>
|
||||
{ children }
|
||||
</div>
|
||||
);
|
||||
|
||||
/**
|
||||
* Renders a collapsible group of controls
|
||||
*
|
||||
* The `controls` prop accepts an array of sets. A set is an array of controls.
|
||||
* Controls have the following shape:
|
||||
*
|
||||
* ```
|
||||
* {
|
||||
* icon: string,
|
||||
* title: string,
|
||||
* subscript: string,
|
||||
* onClick: Function,
|
||||
* isActive: boolean,
|
||||
* isDisabled: boolean
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* For convenience it is also possible to pass only an array of controls. It is
|
||||
* then assumed this is the only set.
|
||||
*
|
||||
* Either `controls` or `children` is required, otherwise this components
|
||||
* renders nothing.
|
||||
*
|
||||
* @param {Object} props Component props.
|
||||
* @param {Array} [props.controls] The controls to render in this toolbar.
|
||||
* @param {WPElement} [props.children] Any other things to render inside the toolbar besides the controls.
|
||||
* @param {string} [props.className] Class to set on the container div.
|
||||
* @param {boolean} [props.isCollapsed] Turns ToolbarGroup into a dropdown menu.
|
||||
* @param {string} [props.title] ARIA label for dropdown menu if is collapsed.
|
||||
*/
|
||||
function ToolbarGroup( {
|
||||
controls = [],
|
||||
children,
|
||||
className,
|
||||
isCollapsed,
|
||||
title,
|
||||
...props
|
||||
} ) {
|
||||
// It'll contain state if `ToolbarGroup` is being used within
|
||||
// `<Toolbar accessibilityLabel="label" />`
|
||||
const accessibleToolbarState = useContext( ToolbarContext );
|
||||
|
||||
if ( ( ! controls || ! controls.length ) && ! children ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const finalClassName = classnames(
|
||||
// Unfortunately, there's legacy code referencing to `.components-toolbar`
|
||||
// So we can't get rid of it
|
||||
accessibleToolbarState
|
||||
? 'components-toolbar-group'
|
||||
: 'components-toolbar',
|
||||
className
|
||||
);
|
||||
|
||||
// Normalize controls to nested array of objects (sets of controls)
|
||||
let controlSets = controls;
|
||||
if ( ! Array.isArray( controlSets[ 0 ] ) ) {
|
||||
controlSets = [ controlSets ];
|
||||
}
|
||||
|
||||
return (
|
||||
<ToolbarGroupContainer className={ finalClassName } { ...props }>
|
||||
{ flatMap( controlSets, ( controlSet, indexOfSet ) =>
|
||||
controlSet.map( ( control, indexOfControl ) => (
|
||||
<ToolbarButton
|
||||
key={ [ indexOfSet, indexOfControl ].join() }
|
||||
containerClassName={
|
||||
indexOfSet > 0 && indexOfControl === 0
|
||||
? 'has-left-divider'
|
||||
: null
|
||||
}
|
||||
{ ...control }
|
||||
/>
|
||||
) )
|
||||
) }
|
||||
{ children }
|
||||
</ToolbarGroupContainer>
|
||||
);
|
||||
}
|
||||
|
||||
export default ToolbarGroup;
|
||||
@@ -0,0 +1,296 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { findIndex, map, some } from 'lodash';
|
||||
import gradientParser from 'gradient-parser';
|
||||
import CustomGradientParser from './custom-parser';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import {
|
||||
DEFAULT_GRADIENT,
|
||||
INSERT_POINT_WIDTH,
|
||||
MINIMUM_ABSOLUTE_LEFT_POSITION,
|
||||
MINIMUM_DISTANCE_BETWEEN_POINTS,
|
||||
KEYBOARD_CONTROL_POINT_VARIATION,
|
||||
HORIZONTAL_GRADIENT_ORIENTATION,
|
||||
} from './constants';
|
||||
import {
|
||||
serializeGradientColor,
|
||||
serializeGradientPosition,
|
||||
serializeGradient,
|
||||
} from './serializer';
|
||||
|
||||
function tinyColorRgbToGradientColorStop( { type, value } ) {
|
||||
//console.log( type );
|
||||
//console.log( value );
|
||||
if ( type === 'literal' ) {
|
||||
return {
|
||||
type: 'literal',
|
||||
value: value,
|
||||
};
|
||||
} else if ( type === 'rgb' ) {
|
||||
return {
|
||||
type: 'rgb',
|
||||
value: [ value.r, value.g, value.b ],
|
||||
};
|
||||
}
|
||||
return {
|
||||
type: 'rgba',
|
||||
value: [ value.r, value.g, value.b, value.a ],
|
||||
};
|
||||
}
|
||||
|
||||
export function getGradientWithColorStopAdded(
|
||||
gradientAST,
|
||||
relativePosition,
|
||||
rgbaColor
|
||||
) {
|
||||
const colorStop = tinyColorRgbToGradientColorStop( rgbaColor );
|
||||
colorStop.length = {
|
||||
type: '%',
|
||||
value: relativePosition,
|
||||
};
|
||||
return {
|
||||
...gradientAST,
|
||||
colorStops: [ ...gradientAST.colorStops, colorStop ],
|
||||
};
|
||||
}
|
||||
|
||||
export function getGradientWithPositionAtIndexChanged(
|
||||
gradientAST,
|
||||
index,
|
||||
relativePosition
|
||||
) {
|
||||
return {
|
||||
...gradientAST,
|
||||
colorStops: gradientAST.colorStops.map(
|
||||
( colorStop, colorStopIndex ) => {
|
||||
if ( colorStopIndex !== index ) {
|
||||
return colorStop;
|
||||
}
|
||||
return {
|
||||
...colorStop,
|
||||
length: {
|
||||
...colorStop.length,
|
||||
value: relativePosition,
|
||||
},
|
||||
};
|
||||
}
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
export function isControlPointOverlapping(
|
||||
gradientAST,
|
||||
position,
|
||||
initialIndex
|
||||
) {
|
||||
const initialPosition = parseInt(
|
||||
gradientAST.colorStops[ initialIndex ].length.value
|
||||
);
|
||||
const minPosition = Math.min( initialPosition, position );
|
||||
const maxPosition = Math.max( initialPosition, position );
|
||||
|
||||
return some( gradientAST.colorStops, ( { length }, index ) => {
|
||||
const itemPosition = parseInt( length.value );
|
||||
return (
|
||||
index !== initialIndex &&
|
||||
( Math.abs( itemPosition - position ) <
|
||||
MINIMUM_DISTANCE_BETWEEN_POINTS ||
|
||||
( minPosition < itemPosition && itemPosition < maxPosition ) )
|
||||
);
|
||||
} );
|
||||
}
|
||||
|
||||
function getGradientWithPositionAtIndexSummed(
|
||||
gradientAST,
|
||||
index,
|
||||
valueToSum
|
||||
) {
|
||||
const currentPosition = gradientAST.colorStops[ index ].length.value;
|
||||
const newPosition = Math.max(
|
||||
0,
|
||||
Math.min( 100, parseInt( currentPosition ) + valueToSum )
|
||||
);
|
||||
if ( isControlPointOverlapping( gradientAST, newPosition, index ) ) {
|
||||
return gradientAST;
|
||||
}
|
||||
return getGradientWithPositionAtIndexChanged(
|
||||
gradientAST,
|
||||
index,
|
||||
newPosition
|
||||
);
|
||||
}
|
||||
|
||||
export function getGradientWithPositionAtIndexIncreased( gradientAST, index ) {
|
||||
return getGradientWithPositionAtIndexSummed(
|
||||
gradientAST,
|
||||
index,
|
||||
KEYBOARD_CONTROL_POINT_VARIATION
|
||||
);
|
||||
}
|
||||
|
||||
export function getGradientWithPositionAtIndexDecreased( gradientAST, index ) {
|
||||
return getGradientWithPositionAtIndexSummed(
|
||||
gradientAST,
|
||||
index,
|
||||
-KEYBOARD_CONTROL_POINT_VARIATION
|
||||
);
|
||||
}
|
||||
|
||||
export function getGradientWithColorAtIndexChanged(
|
||||
gradientAST,
|
||||
index,
|
||||
rgbaColor
|
||||
) {
|
||||
return {
|
||||
...gradientAST,
|
||||
colorStops: gradientAST.colorStops.map(
|
||||
( colorStop, colorStopIndex ) => {
|
||||
if ( colorStopIndex !== index ) {
|
||||
return colorStop;
|
||||
}
|
||||
return {
|
||||
...colorStop,
|
||||
...tinyColorRgbToGradientColorStop( rgbaColor ),
|
||||
};
|
||||
}
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
export function getGradientWithColorAtPositionChanged(
|
||||
gradientAST,
|
||||
relativePositionValue,
|
||||
rgbaColor
|
||||
) {
|
||||
const index = findIndex( gradientAST.colorStops, ( colorStop ) => {
|
||||
return (
|
||||
colorStop &&
|
||||
colorStop.length &&
|
||||
colorStop.length.type === '%' &&
|
||||
colorStop.length.value === relativePositionValue.toString()
|
||||
);
|
||||
} );
|
||||
return getGradientWithColorAtIndexChanged( gradientAST, index, rgbaColor );
|
||||
}
|
||||
|
||||
export function getGradientWithControlPointRemoved( gradientAST, index ) {
|
||||
return {
|
||||
...gradientAST,
|
||||
colorStops: gradientAST.colorStops.filter( ( elem, elemIndex ) => {
|
||||
return elemIndex !== index;
|
||||
} ),
|
||||
};
|
||||
}
|
||||
|
||||
export function getHorizontalRelativeGradientPosition(
|
||||
mouseXCoordinate,
|
||||
containerElement,
|
||||
positionedElementWidth
|
||||
) {
|
||||
if ( ! containerElement ) {
|
||||
return;
|
||||
}
|
||||
const { x, width } = containerElement.getBoundingClientRect();
|
||||
const absolutePositionValue =
|
||||
mouseXCoordinate -
|
||||
x -
|
||||
MINIMUM_ABSOLUTE_LEFT_POSITION -
|
||||
positionedElementWidth / 2;
|
||||
const availableWidth =
|
||||
width - MINIMUM_ABSOLUTE_LEFT_POSITION - INSERT_POINT_WIDTH;
|
||||
return Math.round(
|
||||
Math.min(
|
||||
Math.max( ( absolutePositionValue * 100 ) / availableWidth, 0 ),
|
||||
100
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the marker points from a gradient AST.
|
||||
*
|
||||
* @param {Object} gradientAST An object representing the gradient AST.
|
||||
*
|
||||
* @return {Array.<{color: string, position: string, positionValue: number}>}
|
||||
* An array of markerPoint objects.
|
||||
* color: A string with the color code ready to be used in css style e.g: "rgba( 1, 2 , 3, 0.5)".
|
||||
* position: A string with the position ready to be used in css style e.g: "70%".
|
||||
* positionValue: A number with the relative position value e.g: 70.
|
||||
*/
|
||||
export function getMarkerPoints( gradientAST ) {
|
||||
if ( ! gradientAST ) {
|
||||
return [];
|
||||
}
|
||||
return map( gradientAST.colorStops, ( colorStop ) => {
|
||||
if (
|
||||
! colorStop ||
|
||||
! colorStop.length ||
|
||||
colorStop.length.type !== '%'
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
color: serializeGradientColor( colorStop ),
|
||||
position: serializeGradientPosition( colorStop.length ),
|
||||
positionValue: parseInt( colorStop.length.value ),
|
||||
};
|
||||
} );
|
||||
}
|
||||
|
||||
export function getLinearGradientRepresentationOfARadial( gradientAST ) {
|
||||
return serializeGradient( {
|
||||
type: 'linear-gradient',
|
||||
orientation: HORIZONTAL_GRADIENT_ORIENTATION,
|
||||
colorStops: gradientAST.colorStops,
|
||||
} );
|
||||
}
|
||||
|
||||
const DIRECTIONAL_ORIENTATION_ANGLE_MAP = {
|
||||
top: 0,
|
||||
'top right': 45,
|
||||
'right top': 45,
|
||||
right: 90,
|
||||
'right bottom': 135,
|
||||
'bottom right': 135,
|
||||
bottom: 180,
|
||||
'bottom left': 225,
|
||||
'left bottom': 225,
|
||||
left: 270,
|
||||
'top left': 315,
|
||||
'left top': 315,
|
||||
};
|
||||
|
||||
export function getGradientParsed( value ) {
|
||||
let hasGradient = !! value;
|
||||
// gradientAST will contain the gradient AST as parsed by gradient-parser npm module.
|
||||
// More information of its structure available at https://www.npmjs.com/package/gradient-parser#ast.
|
||||
let gradientAST;
|
||||
let gradientValue;
|
||||
try {
|
||||
gradientAST = CustomGradientParser( value || DEFAULT_GRADIENT )[ 0 ];
|
||||
gradientValue = value || DEFAULT_GRADIENT;
|
||||
} catch ( error ) {
|
||||
hasGradient = false;
|
||||
gradientAST = CustomGradientParser( DEFAULT_GRADIENT )[ 0 ];
|
||||
gradientValue = DEFAULT_GRADIENT;
|
||||
}
|
||||
|
||||
if (
|
||||
gradientAST.orientation &&
|
||||
gradientAST.orientation.type === 'directional'
|
||||
) {
|
||||
gradientAST.orientation.type = 'angular';
|
||||
gradientAST.orientation.value = DIRECTIONAL_ORIENTATION_ANGLE_MAP[
|
||||
gradientAST.orientation.value
|
||||
].toString();
|
||||
}
|
||||
return {
|
||||
hasGradient,
|
||||
gradientAST,
|
||||
gradientValue,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { map } from 'lodash';
|
||||
|
||||
/**
|
||||
* WordPress dependencies
|
||||
*/
|
||||
|
||||
import { __, sprintf } from '@wordpress/i18n';
|
||||
|
||||
const {
|
||||
useCallback,
|
||||
useMemo
|
||||
} = wp.element;
|
||||
const {
|
||||
BaseControl
|
||||
} = wp.components;
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import CircularOptionPicker from './circular-option-picker.js';
|
||||
import CustomGradientPicker from './custom-gradient-picker';
|
||||
|
||||
export default function KadenceGradientPicker( {
|
||||
className,
|
||||
gradients,
|
||||
onChange,
|
||||
value,
|
||||
clearable = true,
|
||||
disableCustomGradients = false,
|
||||
activePalette,
|
||||
} ) {
|
||||
const clearGradient = useCallback( () => onChange( undefined ), [
|
||||
onChange,
|
||||
] );
|
||||
const gradientOptions = useMemo( () => {
|
||||
return map( gradients, ( { gradient, name } ) => (
|
||||
<CircularOptionPicker.Option
|
||||
key={ gradient }
|
||||
value={ gradient }
|
||||
isSelected={ value === gradient }
|
||||
tooltipText={
|
||||
name ||
|
||||
// translators: %s: gradient code e.g: "linear-gradient(90deg, rgba(98,16,153,1) 0%, rgba(172,110,22,1) 100%);".
|
||||
sprintf( __( 'Gradient code: %s' ), gradient )
|
||||
}
|
||||
style={ { color: 'rgba( 0,0,0,0 )', background: gradient } }
|
||||
onClick={
|
||||
value === gradient
|
||||
? clearGradient
|
||||
: () => onChange( gradient )
|
||||
}
|
||||
aria-label={
|
||||
name
|
||||
? // translators: %s: The name of the gradient e.g: "Angular red to blue".
|
||||
sprintf( __( 'Gradient: %s' ), name )
|
||||
: // translators: %s: gradient code e.g: "linear-gradient(90deg, rgba(98,16,153,1) 0%, rgba(172,110,22,1) 100%);".
|
||||
sprintf( __( 'Gradient code: %s' ), gradient )
|
||||
}
|
||||
/>
|
||||
) );
|
||||
}, [ gradients, value, onChange, clearGradient ] );
|
||||
return (
|
||||
<CircularOptionPicker
|
||||
className={ className }
|
||||
options={ gradientOptions }
|
||||
actions={
|
||||
clearable && (
|
||||
<CircularOptionPicker.ButtonAction
|
||||
onClick={ clearGradient }
|
||||
>
|
||||
{ __( 'Clear' ) }
|
||||
</CircularOptionPicker.ButtonAction>
|
||||
)
|
||||
}
|
||||
>
|
||||
{ ! disableCustomGradients && (
|
||||
<CustomGradientPicker value={ value } onChange={ onChange } activePalette={ activePalette } />
|
||||
) }
|
||||
</CircularOptionPicker>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user