@gechiui/components
Version:
UI components for GeChiUI.
190 lines (165 loc) • 4.88 kB
JavaScript
/**
* External dependencies
*/
import { isEmpty, isNumber } from 'lodash';
/**
* GeChiUI dependencies
*/
import { __ } from '@gechiui/i18n';
/**
* Internal dependencies
*/
import { parseUnit } from '../unit-control/utils';
export const LABELS = {
all: __( '全部' ),
top: __( '顶部' ),
bottom: __( '底部' ),
left: __( '左' ),
right: __( '右' ),
mixed: __( '混合' ),
vertical: __( '垂直' ),
horizontal: __( '水平' ),
};
export const DEFAULT_VALUES = {
top: null,
right: null,
bottom: null,
left: null,
};
export const DEFAULT_VISUALIZER_VALUES = {
top: false,
right: false,
bottom: false,
left: false,
};
export const ALL_SIDES = [ 'top', 'right', 'bottom', 'left' ];
/**
* Gets an items with the most occurrence within an array
* https://stackoverflow.com/a/20762713
*
* @param {Array<any>} arr Array of items to check.
* @return {any} The item with the most occurrences.
*/
function mode( arr ) {
return arr
.sort(
( a, b ) =>
arr.filter( ( v ) => v === a ).length -
arr.filter( ( v ) => v === b ).length
)
.pop();
}
/**
* Gets the 'all' input value and unit from values data.
*
* @param {Object} values Box values.
* @param {Array} availableSides Available box sides to evaluate.
*
* @return {string} A value + unit for the 'all' input.
*/
export function getAllValue( values = {}, availableSides = ALL_SIDES ) {
const sides = normalizeSides( availableSides );
const parsedValues = sides.map( ( side ) => parseUnit( values[ side ] ) );
const allValues = parsedValues.map( ( value ) => value[ 0 ] );
const allUnits = parsedValues.map( ( value ) => value[ 1 ] );
const value = allValues.every( ( v ) => v === allValues[ 0 ] )
? allValues[ 0 ]
: '';
const unit = mode( allUnits );
/**
* The isNumber check is important. On reset actions, the incoming value
* may be null or an empty string.
*
* Also, the value may also be zero (0), which is considered a valid unit value.
*
* isNumber() is more specific for these cases, rather than relying on a
* simple truthy check.
*/
const allValue = isNumber( value ) ? `${ value }${ unit }` : null;
return allValue;
}
/**
* Determine the most common unit selection to use as a fallback option.
*
* @param {Object} selectedUnits Current unit selections for individual sides.
* @return {string} Most common unit selection.
*/
export function getAllUnitFallback( selectedUnits ) {
if ( ! selectedUnits || typeof selectedUnits !== 'object' ) {
return undefined;
}
const filteredUnits = Object.values( selectedUnits ).filter( Boolean );
return mode( filteredUnits );
}
/**
* Checks to determine if values are mixed.
*
* @param {Object} values Box values.
* @param {Array} sides Available box sides to evaluate.
*
* @return {boolean} Whether values are mixed.
*/
export function isValuesMixed( values = {}, sides = ALL_SIDES ) {
const allValue = getAllValue( values, sides );
const isMixed = isNaN( parseFloat( allValue ) );
return isMixed;
}
/**
* Checks to determine if values are defined.
*
* @param {Object} values Box values.
*
* @return {boolean} Whether values are mixed.
*/
export function isValuesDefined( values ) {
return (
values !== undefined &&
! isEmpty(
Object.values( values ).filter(
// Switching units when input is empty causes values only
// containing units. This gives false positive on mixed values
// unless filtered.
( value ) => !! value && /\d/.test( value )
)
)
);
}
/**
* Get initial selected side, factoring in whether the sides are linked,
* and whether the vertical / horizontal directions are grouped via splitOnAxis.
*
* @param {boolean} isLinked Whether the box control's fields are linked.
* @param {boolean} splitOnAxis Whether splitting by horizontal or vertical axis.
* @return {string} The initial side.
*/
export function getInitialSide( isLinked, splitOnAxis ) {
let initialSide = 'all';
if ( ! isLinked ) {
initialSide = splitOnAxis ? 'vertical' : 'top';
}
return initialSide;
}
/**
* Normalizes provided sides configuration to an array containing only top,
* right, bottom and left. This essentially just maps `horizontal` or `vertical`
* to their appropriate sides to facilitate correctly determining value for
* all input control.
*
* @param {Array} sides Available sides for box control.
* @return {Array} Normalized sides configuration.
*/
export function normalizeSides( sides ) {
const filteredSides = [];
if ( ! sides?.length ) {
return ALL_SIDES;
}
if ( sides.includes( 'vertical' ) ) {
filteredSides.push( ...[ 'top', 'bottom' ] );
} else if ( sides.includes( 'horizontal' ) ) {
filteredSides.push( ...[ 'left', 'right' ] );
} else {
const newSides = ALL_SIDES.filter( ( side ) => sides.includes( side ) );
filteredSides.push( ...newSides );
}
return filteredSides;
}