@eightshift/frontend-libs
Version:
A collection of useful frontend utility modules. powered by Eightshift
196 lines (174 loc) • 6.48 kB
JavaScript
import React, { useState, Fragment } from 'react';
import { __, sprintf } from '@wordpress/i18n';
import { getDefaultBreakpointNames } from '../../helpers';
import { icons } from '@eightshift/ui-components/icons';
import { clsx, upperFirst } from '@eightshift/ui-components/utilities';
import { AnimatedVisibility, BaseControl, Button, RichLabel, ToggleButton } from '@eightshift/ui-components';
/**
* A component that displays options adjustable across screen sizes.
*
* @param {object} props - Responsive options.
* @param {React.Component} props.label - Option label.
* @param {React.Component?} [props.help] - Optional help text to show below the component.
* @param {React.Component} props.icon - Option icon.
* @param {React.Component?} [props.subtitle] - Subtitle below the label.
* @param {array} props.children - Items to show.
* @param {array<string>} [props.breakpoints] - Breakpoints to show (default: `large`, `desktop`, `tablet` and `mobile`)
* @param {array<string>} [props.breakpointLabels] - If provided, labels for breakpoints will use the provided names instead of using the breakpoint name itself.
* @param {string?} [props.additionalClasses] - If provided, passes additional classes through to the component.
* @param {boolean} [props.inline=false] - If `true`, the control is rendered inline and the options are more compact. Having label, subtitle, icon or help on the child component is not advised.
* @param {array<{callback: function, isActive: boolean}>} [props.inheritButton] - If provided, an 'Inherit' button is shown on each breakpoint except the first one. For each breakpoint a `callback` function (function that sets/unsets the "inherit" value, usually `undefined`) and a `isActive` flag (`true` if inheriting from parent) need to be provided.
*/
export const Responsive = (props) => {
const {
label,
help,
icon,
subtitle,
children = [],
breakpoints = getDefaultBreakpointNames(),
inheritButton = [],
breakpointLabels,
additionalClasses,
inline = false,
hidden,
} = props;
if (hidden) {
return null;
}
const fallbackBreakpointLabels = breakpoints.map((v) => upperFirst(v));
const [isOpen, setIsOpen] = useState(false);
return (
<BaseControl
icon={icon}
label={label}
help={help}
subtitle={subtitle}
className={clsx('es:space-y-2', additionalClasses)}
actions={
<>
{inline && (
<AnimatedVisibility
visible={!isOpen}
transition='scaleFade'
>
{children[0]}
</AnimatedVisibility>
)}
<ToggleButton
aria-label={
isOpen
? __('Close responsive overrides', 'eightshift-frontend-libs')
: __('Open responsive overrides', 'eightshift-frontend-libs')
}
tooltip={
isOpen
? __('Close responsive overrides', 'eightshift-frontend-libs')
: __('Open responsive overrides', 'eightshift-frontend-libs')
}
onChange={setIsOpen}
icon={icons.responsiveOverridesAlt}
/>
</>
}
>
{children.map((child, index) => {
const breakpointLabel = breakpointLabels?.at(index) ?? fallbackBreakpointLabels.at(index);
const previousBreakpointLabel =
index === 0 ? '' : (breakpointLabels?.at(index - 1) ?? fallbackBreakpointLabels.at(index - 1));
const breakpointIcon = icons[`screen${upperFirst(breakpoints[index])}`];
const currentInheritButton = inheritButton?.at(index);
const inheritButtonComponent = (
<Button
icon={icons.inherit}
onPress={currentInheritButton?.callback}
className='es:w-full'
size='large'
>
{currentInheritButton?.isActive && (
<RichLabel
label={
<div
dangerouslySetInnerHTML={{
__html: sprintf(
__('Using value from <span class="es-font-weight-600">%s</span>', 'eightshift-frontend-libs'),
previousBreakpointLabel,
),
}}
/>
}
subtitle={__('Click to set value ', 'eightshift-frontend-libs')}
/>
)}
{!currentInheritButton?.isActive && (
<div
dangerouslySetInnerHTML={{
__html: sprintf(
__('Use value from <span class="es-font-weight-600">%s</span>', 'eightshift-frontend-libs'),
previousBreakpointLabel,
),
}}
/>
)}
</Button>
);
if (inline) {
if (!isOpen) {
return null;
}
return (
<AnimatedVisibility
visible={isOpen}
className={clsx(isOpen && index !== children.length - 1 && 'es:mb-2')}
key={index}
>
<BaseControl
icon={breakpointIcon}
label={
index === 0
? sprintf(__('%s (default)', 'eightshift-frontend-libs'), breakpointLabel)
: breakpointLabel
}
actions={
index > 0 && (
<AnimatedVisibility visible={currentInheritButton ? !currentInheritButton.isActive : true}>
{child}
</AnimatedVisibility>
)
}
inlineLabel={index === 0}
>
{index === 0 && child}
{index > 0 && currentInheritButton && inheritButtonComponent}
</BaseControl>
</AnimatedVisibility>
);
}
return (
<Fragment key={index}>
<AnimatedVisibility visible={isOpen}>
<RichLabel
icon={breakpointIcon}
label={
index === 0
? sprintf(__('%s (default)', 'eightshift-frontend-libs'), breakpointLabel)
: breakpointLabel
}
className='es:mb-2'
/>
{index > 0 && currentInheritButton && inheritButtonComponent}
</AnimatedVisibility>
{index === 0 && !isOpen && child}
{index === 0 && isOpen && <div className=''>{child}</div>}
<AnimatedVisibility
visible={index > 0 && isOpen && (currentInheritButton ? !currentInheritButton.isActive : true)}
className={clsx(isOpen && index !== children.length - 1 && 'es:mb-2')}
>
{child}
</AnimatedVisibility>
</Fragment>
);
})}
</BaseControl>
);
};