update navigation & pagination to toolsPanel (#3)

* update navigation & pagination to toolsPanel

* add resetAll for toolsPanel
This commit is contained in:
Punit Verma 2024-12-31 14:32:13 +05:30 committed by GitHub
parent 974e4a112a
commit 1a61bc7214
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 393 additions and 308 deletions

View file

@ -15,82 +15,81 @@ import {
} from '@wordpress/components';

/**
* Render Color Picker
* @param root0
* @param root0.label
* @param root0.colorValue
* @param root0.onChangeColor
* Renders a color control dropdown for selecting colors.
*
* @param {Object} props - The component props.
* @param {string} props.label - The label for the color control.
* @param {Object} props.colorValue - The current color values. Should include `default` and optionally `hover` (if `hasHover` is true).
* @param {Function} props.onChangeColor - Callback function to handle color changes. Accepts an object with updated color values.
* @param {boolean} props.hasHover - Determines if hover color support is enabled. If true, a tab for hover colors is displayed.
*
* @returns {JSX.Element} The rendered ColorControlDropdown component.
*/
function ColorControlDropdown( { label, colorValue = {}, onChangeColor } ) {
const [ activeTab, setActiveTab ] = useState( 'default' );
const hasHover = 'hover' in colorValue; // Determine if hover is provided
function ColorControlDropdown({ label, colorValue = {}, onChangeColor, hasHover = false }) {
const [activeTab, setActiveTab] = useState('default');

return (
<Dropdown
popoverProps={ {
popoverProps={{
placement: 'left-start',
offset: 36,
shift: true,
} }
}}
contentClassName="slider_color_popover"
renderToggle={ ( { isOpen, onToggle } ) => (
renderToggle={({ isOpen, onToggle }) => (
<Button
className={ `slider_color_button ${
isOpen ? 'isOpen' : ''
}` }
aria-expanded={ isOpen }
onClick={ onToggle }
className={`slider_color_button ${isOpen ? 'isOpen' : ''}`}
aria-expanded={isOpen}
onClick={onToggle}
>
<HStack justify="left">
<ZStack offset={ 10 }>
<ColorIndicator colorValue={ colorValue.default } />
{ hasHover && (
<ColorIndicator
colorValue={ colorValue.hover }
/>
) }
<ZStack offset={10}>
<ColorIndicator colorValue={colorValue.default} />
{hasHover && (
<ColorIndicator colorValue={colorValue.hover} />
)}
</ZStack>
<Text>{ label }</Text>
<Text>{label}</Text>
</HStack>
</Button>
) }
renderContent={ () =>
)}
renderContent={() =>
hasHover ? (
<TabPanel
onSelect={ ( tab ) => setActiveTab( tab ) }
tabs={ [
onSelect={(tab) => setActiveTab(tab)}
tabs={[
{
name: 'default',
title: __( 'Default', 'slider-block' ),
title: __('Default', 'slider-block'),
},
{
name: 'hover',
title: __( 'Hover', 'slider-block' ),
title: __('Hover', 'slider-block'),
},
] }
]}
>
{ ( tab ) => (
{(tab) => (
<ColorPalette
__experimentalIsRenderedInSidebar
value={ colorValue[ tab.name ] || '' }
onChange={ ( color ) => {
onChangeColor( {
value={colorValue[tab.name] || ''}
onChange={(color) => {
onChangeColor({
...colorValue,
[ tab.name ]: color,
} );
} }
[tab.name]: color,
});
}}
enableAlpha
/>
) }
)}
</TabPanel>
) : (
<ColorPalette
className="ls-color-pallete-container"
__experimentalIsRenderedInSidebar
value={ colorValue.default || '' }
onChange={ ( color ) => {
onChangeColor( { ...colorValue, default: color } );
} }
value={colorValue.default || ''}
onChange={(color) => {
onChangeColor({ ...colorValue, default: color });
}}
enableAlpha
/>
)

View file

@ -122,16 +122,6 @@
},
"navigationColor": {
"type": "object",
"default": {
"arrowColor": {
"default": "",
"hover": ""
},
"backgroundColor": {
"default": "",
"hover": ""
}
},
"properties": {
"arrowColor": {
"type": "object",
@ -161,34 +151,19 @@
"type": "object"
},
"navigationSize": {
"type": "string",
"default": "40px"
"type": "string"
},
"navigationOffset": {
"type": "object",
"default": {
"top": "50%",
"bottom": "0px",
"left": "10px",
"right": "10px"
}
"type": "object"
},
"navigationBorderRadius": {
"type": "string",
"default": "4px"
"type": "string"
},
"paginationSize": {
"type": "string",
"default": "8px"
"type": "string"
},
"paginationOffset": {
"type": "object",
"default": {
"top": "auto",
"bottom": "8px",
"left": "0px",
"right": "0px"
}
"type": "object"
},
"paginationColor": {
"type": "object",

View file

@ -23,6 +23,8 @@ import {
ToolbarGroup,
__experimentalVStack as VStack,
__experimentalHeading as Heading,
__experimentalToolsPanel as ToolsPanel,
__experimentalToolsPanelItem as ToolsPanelItem,
__experimentalToggleGroupControl as ToggleGroupControl,
__experimentalToggleGroupControlOption as ToggleGroupControlOption,
} from '@wordpress/components';
@ -43,22 +45,22 @@ const DEFAULT_BLOCK = {
/**
* Slider component.
*/
const Slider = memo( ( { attributes, innerBlocksProps, innerBlocks } ) => {
const Slider = memo(({ attributes, innerBlocksProps, innerBlocks }) => {
const editorDeviceType = useSelect(
( select ) => select( 'core/editor' ).getDeviceType(),
(select) => select('core/editor').getDeviceType(),
[]
);
const swiperContainerRef = useRef( null );
const swiperInstanceRef = useRef( null );
const swiperContainerRef = useRef(null);
const swiperInstanceRef = useRef(null);

useEffect( () => {
if ( swiperContainerRef.current && innerBlocks.length > 0 ) {
useEffect(() => {
if (swiperContainerRef.current && innerBlocks.length > 0) {
// Clear existing Swiper classes
swiperContainerRef.current.className = 'swiper';

// Destroy the existing Swiper instance, if any
if ( swiperInstanceRef.current ) {
swiperInstanceRef.current.destroy( true, true );
if (swiperInstanceRef.current) {
swiperInstanceRef.current.destroy(true, true);
swiperInstanceRef.current = null;
}

@ -75,28 +77,28 @@ const Slider = memo( ( { attributes, innerBlocksProps, innerBlocks } ) => {

// Cleanup on unmount
return () => {
if ( swiperInstanceRef.current ) {
swiperInstanceRef.current.destroy( true, true );
if (swiperInstanceRef.current) {
swiperInstanceRef.current.destroy(true, true);
}
};
}, [ editorDeviceType, attributes, innerBlocks.length ] );
}, [editorDeviceType, attributes, innerBlocks.length]);

// Inline styles for navigation
const navigationStyles = generateNavigationStyles( attributes );
const navigationStyles = generateNavigationStyles(attributes);
const applyPadding = innerBlocks.length >= 2 ? '100px' : '';

return (
<div
{ ...useBlockProps( {
{...useBlockProps({
style: { ...navigationStyles, padding: applyPadding },
} ) }
})}
>
<div ref={ swiperContainerRef }>
<div { ...innerBlocksProps } />
<div ref={swiperContainerRef}>
<div {...innerBlocksProps} />
</div>
</div>
);
} );
});

/**
* The edit function describes the structure of your block in the context of the
@ -109,9 +111,9 @@ const Slider = memo( ( { attributes, innerBlocksProps, innerBlocks } ) => {
*
* @return {JSX.Element} The component rendering for the block editor.
*/
export default function Edit( { clientId, attributes, setAttributes } ) {
export default function Edit({ clientId, attributes, setAttributes }) {
const { allowedBlocks } = attributes;
const { insertBlock } = useDispatch( blockEditorStore );
const { insertBlock } = useDispatch(blockEditorStore);

const innerBlocksProps = useInnerBlocksProps(
{ className: 'swiper-wrapper' },
@ -125,409 +127,504 @@ export default function Edit( { clientId, attributes, setAttributes } ) {

// Check if inner blocks exist using useSelect
const innerBlocks = useSelect(
( select ) => select( blockEditorStore ).getBlocks( clientId ),
[ clientId ]
(select) => select(blockEditorStore).getBlocks(clientId),
[clientId]
);

const hasInnerBlocks = innerBlocks.length > 0;

const addSlide = () => {
const block = createBlock( 'lubus/slide' );
insertBlock( block, innerBlocks.length, clientId, false );
const block = createBlock('lubus/slide');
insertBlock(block, innerBlocks.length, clientId, false);
};

return hasInnerBlocks ? (
<>
<Slider
attributes={ attributes }
innerBlocksProps={ innerBlocksProps }
innerBlocks={ innerBlocks }
attributes={attributes}
innerBlocksProps={innerBlocksProps}
innerBlocks={innerBlocks}
/>
<BlockControls>
<ToolbarGroup>
<ToolbarButton icon="plus" onClick={ addSlide }>
{ __( 'Add Slide', 'slider-block' ) }
<ToolbarButton icon="plus" onClick={addSlide}>
{__('Add Slide', 'slider-block')}
</ToolbarButton>
</ToolbarGroup>
</BlockControls>
<InspectorControls>
<PanelBody title={ __( 'Settings', 'slider-block' ) }>
<VStack style={ { marginBottom: '8px' } }>
<PanelBody title={__('Settings', 'slider-block')}>
<VStack style={{ marginBottom: '8px' }}>
<ResponsiveDropdown
label="Slides Per View"
attributes={ attributes }
setAttributes={ setAttributes }
attributes={attributes}
setAttributes={setAttributes}
responsiveKey="slidesPerView"
/>
<RangeControl
__nextHasNoMarginBottom
__next40pxDefaultSize
help={ __(
help={__(
"Number of slides visible at the same time on slider's container.",
'slider-block'
) }
)}
value={
attributes.slidesPerView[
attributes.slidesPerView.activeDevice
attributes.slidesPerView.activeDevice
]
}
min={ 1 }
max={ 30 }
onChange={ ( value ) =>
setAttributes( {
min={1}
max={30}
onChange={(value) =>
setAttributes({
slidesPerView: {
...attributes.slidesPerView,
[ attributes.slidesPerView
.activeDevice ]: value,
[attributes.slidesPerView
.activeDevice]: value,
},
} )
})
}
/>
</VStack>
<VStack style={ { marginBottom: '16px' } }>
<VStack style={{ marginBottom: '16px' }}>
<ResponsiveDropdown
label={ __( 'Slides Spacing', 'slider-block' ) }
attributes={ attributes }
setAttributes={ setAttributes }
label={__('Slides Spacing', 'slider-block')}
attributes={attributes}
setAttributes={setAttributes}
responsiveKey="slidesSpacing"
/>
<RangeControl
__nextHasNoMarginBottom
__next40pxDefaultSize
help={ __(
help={__(
'Adjust the spacing between slides.',
'slider-block'
) }
initialPosition={ 30 }
)}
initialPosition={30}
value={
attributes.slidesSpacing[
attributes.slidesSpacing.activeDevice
attributes.slidesSpacing.activeDevice
]
}
min={ 0 }
onChange={ ( value ) =>
setAttributes( {
min={0}
onChange={(value) =>
setAttributes({
slidesSpacing: {
...attributes.slidesSpacing,
[ attributes.slidesSpacing
.activeDevice ]: value,
[attributes.slidesSpacing
.activeDevice]: value,
},
} )
})
}
/>
</VStack>
<RangeControl
__nextHasNoMarginBottom
__next40pxDefaultSize
help={ __(
help={__(
'Set the duration of transition between slides.',
'slider-block'
) }
label={ __( 'Speed (ms)', 'slider-block' ) }
min={ 100 } // minimum speed in ms
max={ 10000 } // maximum speed in ms
step={ 100 }
value={ attributes.speed }
onChange={ ( value ) =>
setAttributes( { speed: value } )
)}
label={__('Speed (ms)', 'slider-block')}
min={100} // minimum speed in ms
max={10000} // maximum speed in ms
step={100}
value={attributes.speed}
onChange={(value) =>
setAttributes({ speed: value })
}
/>
<ToggleGroupControl
isBlock
__nextHasNoMarginBottom
__next40pxDefaultSize
label={ __( 'Effects', 'slider-block' ) }
value={ attributes.effects }
onChange={ ( value ) =>
setAttributes( { effects: value } )
label={__('Effects', 'slider-block')}
value={attributes.effects}
onChange={(value) =>
setAttributes({ effects: value })
}
help={ __(
help={__(
'Select how slides transition.',
'slider-block'
) }
)}
>
<ToggleGroupControlOption
label={ __( 'Slide', 'slider-block' ) }
label={__('Slide', 'slider-block')}
value="slide"
/>
<ToggleGroupControlOption
label={ __( 'Fade', 'slider-block' ) }
label={__('Fade', 'slider-block')}
value="fade"
/>
</ToggleGroupControl>
<ToggleControl
__nextHasNoMarginBottom
className='responsive_field_control'
help={ __(
help={__(
'Enable navigation arrows to manually move between slides.',
'slider-block'
) }
)}
checked={
attributes.navigation[
attributes.navigation.activeDevice
attributes.navigation.activeDevice
]
}
label={
<ResponsiveDropdown
label={ __( 'Navigation', 'slider-block' ) }
attributes={ attributes }
setAttributes={ setAttributes }
label={__('Navigation', 'slider-block')}
attributes={attributes}
setAttributes={setAttributes}
responsiveKey="navigation"
/>
}
onChange={ ( value ) =>
setAttributes( {
onChange={(value) =>
setAttributes({
navigation: {
...attributes.navigation,
[ attributes.navigation.activeDevice ]:
[attributes.navigation.activeDevice]:
value,
},
} )
})
}
/>
<ToggleControl
__nextHasNoMarginBottom
className='responsive_field_control'
help={ __(
help={__(
'Enable pagination indicators to show slide positions.',
'slider-block'
) }
)}
checked={
attributes.pagination[
attributes.pagination.activeDevice
attributes.pagination.activeDevice
]
}
label={
<ResponsiveDropdown
label={ __( 'Pagination', 'slider-block' ) }
attributes={ attributes }
setAttributes={ setAttributes }
label={__('Pagination', 'slider-block')}
attributes={attributes}
setAttributes={setAttributes}
responsiveKey="pagination"
/>
}
onChange={ ( value ) =>
setAttributes( {
onChange={(value) =>
setAttributes({
pagination: {
...attributes.pagination,
[ attributes.pagination.activeDevice ]:
[attributes.pagination.activeDevice]:
value,
},
} )
})
}
/>
<ToggleControl
__nextHasNoMarginBottom
help={ __(
help={__(
'Enable loop to continuously cycle through slides.',
'slider-block'
) }
checked={ attributes.loop }
label={ __( 'Loop', 'slider-block' ) }
onChange={ ( value ) =>
setAttributes( { loop: value } )
)}
checked={attributes.loop}
label={__('Loop', 'slider-block')}
onChange={(value) =>
setAttributes({ loop: value })
}
/>
<ToggleControl
__nextHasNoMarginBottom
help={ __(
help={__(
'Enable automatic slide transition.',
'slider-block'
) }
checked={ attributes.autoplay }
label={ __( 'Autoplay', 'slider-block' ) }
onChange={ ( value ) =>
setAttributes( { autoplay: value } )
)}
checked={attributes.autoplay}
label={__('Autoplay', 'slider-block')}
onChange={(value) =>
setAttributes({ autoplay: value })
}
/>
{ attributes.autoplay && (
{attributes.autoplay && (
<RangeControl
__nextHasNoMarginBottom
__next40pxDefaultSize
help={ __(
help={__(
'Set the delay between slides in milliseconds.',
'slider-block'
) }
label={ __( 'Delay (ms)', 'slider-block' ) }
min={ 100 } // minimum delay in ms
max={ 10000 } // maximum delay in ms
step={ 100 }
value={ attributes.delay }
onChange={ ( value ) =>
setAttributes( { delay: value } )
)}
label={__('Delay (ms)', 'slider-block')}
min={100} // minimum delay in ms
max={10000} // maximum delay in ms
step={100}
value={attributes.delay}
onChange={(value) =>
setAttributes({ delay: value })
}
/>
) }
)}
</PanelBody>
</InspectorControls>
<InspectorControls group="styles">
<PanelBody
title={ __( 'Navigation', 'slider-block' ) }
initialOpen={ true }
<ToolsPanel
label={__('Navigation', 'slider-block')}
resetAll={() =>
setAttributes({
navigationSize: undefined,
navigationColor: {
arrow: { default: undefined, hover: undefined },
background: { default: undefined, hover: undefined },
},
navigationPadding: undefined,
navigationOffset: undefined,
navigationBorderRadius: undefined,
})
}
>
<VStack spacing={ 4 }>
<ToolsPanelItem
label={__('Size', 'slider-block')}
isShownByDefault
hasValue={() => !!attributes.navigationSize}
onDeselect={() => setAttributes({ navigationSize: undefined })}
>
<FontSizePicker
__next40pxDefaultSize
withSlider
withReset={ false }
onChange={ ( size ) =>
setAttributes( { navigationSize: size } )
withReset={false}
onChange={(size) =>
setAttributes({ navigationSize: size })
}
value={ attributes.navigationSize }
value={attributes.navigationSize}
/>
<VStack spacing={ 0 }>
</ToolsPanelItem>
<ToolsPanelItem
label={__('Color', 'slider-block')}
isShownByDefault
hasValue={() =>
!!attributes?.navigationColor?.arrowColor?.default ||
!!attributes?.navigationColor?.arrowColor?.hover ||
!!attributes?.navigationColor?.backgroundColor?.default ||
!!attributes?.navigationColor?.backgroundColor?.hover
}
onDeselect={() =>
setAttributes({
navigationColor: {
arrow: {
default: undefined,
hover: undefined,
},
background: {
default: undefined,
hover: undefined,
},
},
})
}
>
<VStack spacing={0}>
<Heading
lineHeight={ 1 }
level={ 3 }
weight={ 500 }
lineHeight={1}
level={3}
weight={500}
upperCase
>
Color
</Heading>
<VStack
className="slider_color-support-panel"
spacing={ 0 }
spacing={0}
>
<ColorControlDropdown
label={ __( 'Arrow', 'slider-block' ) }
label={__('Arrow', 'slider-block')}
colorValue={
attributes?.navigationColor
?.arrowColor || {}
}
onChangeColor={ ( newColor ) =>
setAttributes( {
onChangeColor={(newColor) =>
setAttributes({
navigationColor: {
...attributes.navigationColor,
arrowColor: newColor,
},
} )
})
}
hasHover={true}
/>
<ColorControlDropdown
label={ __( 'Background', 'slider-block' ) }
label={__('Background', 'slider-block')}
colorValue={
attributes?.navigationColor
?.backgroundColor || {}
}
onChangeColor={ ( newColor ) =>
setAttributes( {
onChangeColor={(newColor) =>
setAttributes({
navigationColor: {
...attributes?.navigationColor,
backgroundColor: newColor,
},
} )
})
}
hasHover={true}
/>
</VStack>
</VStack>
</ToolsPanelItem>
<ToolsPanelItem
label={__('Padding', 'slider-block')}
hasValue={() => !!attributes.navigationPadding}
onDeselect={() => setAttributes({ navigationPadding: undefined })}
>
<SpacingSizesControl
values={ attributes.navigationPadding }
onChange={ ( value ) =>
setAttributes( { navigationPadding: value } )
values={attributes.navigationPadding}
onChange={(value) =>
setAttributes({ navigationPadding: value })
}
label={ __( 'Padding', 'slider-block' ) }
allowReset={ false }
splitOnAxis={ true }
label={__('Padding', 'slider-block')}
allowReset={false}
splitOnAxis={true}
/>
</ToolsPanelItem>
<ToolsPanelItem
label={__('Offset', 'slider-block')}
hasValue={() => !!attributes.navigationOffset}
onDeselect={() => setAttributes({ navigationOffset: undefined })}
>
<SpacingSizesControl
values={ attributes.navigationOffset }
onChange={ ( value ) =>
setAttributes( { navigationOffset: value } )
values={attributes.navigationOffset}
onChange={(value) =>
setAttributes({ navigationOffset: value })
}
label={ __( 'Offset', 'slider-block' ) }
minimumCustomValue={ -Infinity }
allowReset={ false }
splitOnAxis={ true }
label={__('Offset', 'slider-block')}
minimumCustomValue={-Infinity}
allowReset={false}
splitOnAxis={true}
/>
</ToolsPanelItem>
<ToolsPanelItem
label={__('Radius', 'slider-block')}
hasValue={() => !!attributes.navigationBorderRadius}
onDeselect={() => setAttributes({ navigationBorderRadius: undefined })}
>
<BorderRadiusControl
values={ attributes.navigationBorderRadius }
onChange={ ( value ) =>
setAttributes( {
values={attributes.navigationBorderRadius}
onChange={(value) =>
setAttributes({
navigationBorderRadius: value,
} )
})
}
/>
</VStack>
</PanelBody>
</ToolsPanelItem>
</ToolsPanel>
</InspectorControls>
<InspectorControls group="styles">
<PanelBody
title={ __( 'Pagination', 'slider-block' ) }
initialOpen={ true }
<ToolsPanel
label={__('Pagination', 'slider-block')}
resetAll={() =>
setAttributes({
paginationSize: undefined,
paginationColor: {
activeColor: undefined,
inactiveColor: undefined,
},
paginationOffset: undefined,
})
}
>
<VStack spacing={ 4 }>
<ToolsPanelItem
label={__('Size', 'slider-block')}
isShownByDefault
hasValue={() => !!attributes.paginationSize}
onDeselect={() => setAttributes({ paginationSize: undefined })}
>
<FontSizePicker
__next40pxDefaultSize
withSlider
withReset={ false }
onChange={ ( size ) =>
setAttributes( { paginationSize: size } )
withReset={false}
onChange={(size) =>
setAttributes({ paginationSize: size })
}
value={ attributes.paginationSize }
value={attributes.paginationSize}
/>
<VStack spacing={ 0 }>
</ToolsPanelItem>
<ToolsPanelItem
label={__('Color', 'slider-block')}
isShownByDefault
hasValue={() => !!attributes?.paginationColor?.activeColor || !!attributes?.paginationColor?.inactiveColor}
onDeselect={() =>
setAttributes({
paginationColor: {
activeColor: undefined,
inactiveColor: undefined,
},
})
}
>
<VStack spacing={0}>
<Heading
lineHeight={ 1 }
level={ 3 }
weight={ 500 }
lineHeight={1}
level={3}
weight={500}
upperCase
>
Color
</Heading>
<VStack
className="slider_color-support-panel"
spacing={ 0 }
spacing={0}
>
<ColorControlDropdown
label={ __( 'Active', 'slider-block' ) }
label={__('Active', 'slider-block')}
colorValue={
attributes?.paginationColor
?.activeColor || {}
}
onChangeColor={ ( newColor ) =>
setAttributes( {
onChangeColor={(newColor) =>
setAttributes({
paginationColor: {
...attributes.paginationColor,
activeColor: newColor,
},
} )
})
}
/>
<ColorControlDropdown
label={ __( 'Inactive', 'slider-block' ) }
label={__('Inactive', 'slider-block')}
colorValue={
attributes?.paginationColor
?.inactiveColor || {}
}
onChangeColor={ ( newColor ) =>
setAttributes( {
onChangeColor={(newColor) =>
setAttributes({
paginationColor: {
...attributes?.paginationColor,
inactiveColor: newColor,
},
} )
})
}
/>
</VStack>
</VStack>
</ToolsPanelItem>
<ToolsPanelItem
label={__('Offset', 'slider-block')}
hasValue={() => !!attributes.paginationOffset}
onDeselect={() => setAttributes({ paginationOffset: undefined })}
>
<SpacingSizesControl
values={ attributes.paginationOffset }
onChange={ ( value ) =>
setAttributes( { paginationOffset: value } )
values={attributes.paginationOffset}
onChange={(value) =>
setAttributes({ paginationOffset: value })
}
label={ __( 'Offset', 'slider-block' ) }
minimumCustomValue={ -Infinity }
allowReset={ false }
splitOnAxis={ true }
label={__('Offset', 'slider-block')}
minimumCustomValue={-Infinity}
allowReset={false}
splitOnAxis={true}
/>
</VStack>
</PanelBody>
</ToolsPanelItem>
</ToolsPanel>
</InspectorControls>
</>
) : (
<Placeholder
clientId={ clientId }
attributes={ attributes }
setAttributes={ setAttributes }
clientId={clientId}
attributes={attributes}
setAttributes={setAttributes}
/>
);
}

View file

@ -2,45 +2,47 @@
* Resolves a spacing size value into a usable CSS value.
*
* @param {string|number} value - The input spacing size value.
* @param {string|number} defaultValue - The default value.
* @return {string} - A valid CSS spacing size value.
*/
const resolveSpacingSizeValue = ( value ) => {
if ( typeof value === 'string' ) {
if ( value.startsWith( 'var:' ) ) {
const resolveSpacingSizeValue = (value, defaultValue = '0px') => {
if (typeof value === 'string') {
if (value.startsWith('var:')) {
// Convert "var:some|value" into "var(--wp--some--value)"
const cssVariable = value
.replace( 'var:', '--wp--' )
.replace( /\|/g, '--' );
return `var(${ cssVariable })`;
.replace('var:', '--wp--')
.replace(/\|/g, '--');
return `var(${cssVariable})`;
}
return value; // If it's a valid CSS string, return as-is
}

if ( typeof value === 'number' ) {
return `${ value }px`; // Convert numbers to pixel values
if (typeof value === 'number') {
return `${value}px`; // Convert numbers to pixel values
}

// Fallback to '0px' if value is invalid or undefined
return '0px';
// use defaultValue if value is invalid or undefined
return defaultValue;
};

/**
* Generates a border-radius string from either a string or an object.
*
* @param {string|object} borderRadius - The border radius definition.
* @param {string|number} defaultValue - The default value.
* @return {string} - A valid CSS border-radius value.
*/
const getBorderRadiusStyles = ( borderRadius ) => {
if ( typeof borderRadius === 'string' ) {
const getBorderRadiusStyles = (borderRadius, defaultValue = '0px') => {
if (typeof borderRadius === 'string') {
return borderRadius;
}

// If it's an object, return a four-value shorthand for border-radius
const topLeft = borderRadius?.topLeft || '0px';
const topRight = borderRadius?.topRight || '0px';
const bottomRight = borderRadius?.bottomRight || '0px';
const bottomLeft = borderRadius?.bottomLeft || '0px';
return `${ topLeft } ${ topRight } ${ bottomRight } ${ bottomLeft }`;
const topLeft = borderRadius?.topLeft || defaultValue;
const topRight = borderRadius?.topRight || defaultValue;
const bottomRight = borderRadius?.bottomRight || defaultValue;
const bottomLeft = borderRadius?.bottomLeft || defaultValue;
return `${topLeft} ${topRight} ${bottomRight} ${bottomLeft}`;
};

/**
@ -51,101 +53,113 @@ const getBorderRadiusStyles = ( borderRadius ) => {
*
* @return {Object} - An object with CSS variable definitions for the navigation.
*/
export const generateNavigationStyles = ( attributes = {} ) => {
export const generateNavigationStyles = (attributes = {}) => {
const styles = {};

// Helper function to add a style only if the value is valid
const addStyle = ( key, value ) => {
if ( value ) {
styles[ key ] = value;
// Helper function to add a style with a fallback to default values
const addStyle = (key, value, defaultValue = '0px') => {
if (value !== undefined && value !== null) {
styles[key] = value;
} else if (defaultValue) {
styles[key] = defaultValue;
}
};

addStyle(
'--navigation-arrow-color',
attributes?.navigationColor?.arrowColor?.default
attributes?.navigationColor?.arrowColor?.default,
'#000'
);
addStyle(
'--navigation-background-color',
attributes?.navigationColor?.backgroundColor?.default
attributes?.navigationColor?.backgroundColor?.default,
'transparent'
);
addStyle(
'--navigation-arrow-hover-color',
attributes?.navigationColor?.arrowColor?.hover
attributes?.navigationColor?.arrowColor?.hover,
'#333'
);
addStyle(
'--navigation-background-hover-color',
attributes?.navigationColor?.backgroundColor?.hover
attributes?.navigationColor?.backgroundColor?.hover,
'transparent'
);
addStyle(
'--swiper-navigation-size',
attributes?.navigationSize,
'40px'
);
addStyle( '--swiper-navigation-size', attributes?.navigationSize );
addStyle(
'--navigation-border-radius',
getBorderRadiusStyles( attributes?.navigationBorderRadius )
getBorderRadiusStyles(attributes?.navigationBorderRadius, '4px'),
);

// Padding styles
// Padding styles with defaults
addStyle(
'--navigation-padding-top',
resolveSpacingSizeValue( attributes?.navigationPadding?.top )
resolveSpacingSizeValue(attributes?.navigationPadding?.top, '10px')
);
addStyle(
'--navigation-padding-right',
resolveSpacingSizeValue( attributes?.navigationPadding?.right )
resolveSpacingSizeValue(attributes?.navigationPadding?.right, '10px')
);
addStyle(
'--navigation-padding-bottom',
resolveSpacingSizeValue( attributes?.navigationPadding?.bottom )
resolveSpacingSizeValue(attributes?.navigationPadding?.bottom, '10px')
);
addStyle(
'--navigation-padding-left',
resolveSpacingSizeValue( attributes?.navigationPadding?.left )
resolveSpacingSizeValue(attributes?.navigationPadding?.left, '10px')
);

// Pagination styles
addStyle( '--pagination-size', attributes?.paginationSize );
addStyle('--pagination-size', attributes?.paginationSize, '8px');
addStyle(
'--pagination-active-color',
attributes?.paginationColor?.activeColor?.default
attributes?.paginationColor?.activeColor?.default,
'#000'
);
addStyle(
'--pagination-inactive-color',
attributes?.paginationColor?.inactiveColor?.default
attributes?.paginationColor?.inactiveColor?.default,
'#ccc'
);

// Pagination offset styles
// Pagination offset styles with defaults
addStyle(
'--pagination-offset-top',
resolveSpacingSizeValue( attributes?.paginationOffset?.top )
resolveSpacingSizeValue(attributes?.paginationOffset?.top, 'auto')
);
addStyle(
'--pagination-offset-right',
resolveSpacingSizeValue( attributes?.paginationOffset?.right )
resolveSpacingSizeValue(attributes?.paginationOffset?.right)
);
addStyle(
'--pagination-offset-bottom',
resolveSpacingSizeValue( attributes?.paginationOffset?.bottom )
resolveSpacingSizeValue(attributes?.paginationOffset?.bottom, '8px')
);
addStyle(
'--pagination-offset-left',
resolveSpacingSizeValue( attributes?.paginationOffset?.left )
resolveSpacingSizeValue(attributes?.paginationOffset?.left)
);

// Navigation offset styles
// Navigation offset styles with defaults
addStyle(
'--navigation-offset-top',
resolveSpacingSizeValue( attributes?.navigationOffset?.top )
resolveSpacingSizeValue(attributes?.navigationOffset?.top, '50%')
);
addStyle(
'--navigation-offset-right',
resolveSpacingSizeValue( attributes?.navigationOffset?.right )
resolveSpacingSizeValue(attributes?.navigationOffset?.right, '10px')
);
addStyle(
'--navigation-offset-bottom',
resolveSpacingSizeValue( attributes?.navigationOffset?.bottom )
resolveSpacingSizeValue(attributes?.navigationOffset?.bottom)
);
addStyle(
'--navigation-offset-left',
resolveSpacingSizeValue( attributes?.navigationOffset?.left )
resolveSpacingSizeValue(attributes?.navigationOffset?.left, '10px')
);

return styles;