Initial
This commit is contained in:
156
SpacingControl.js
Normal file
156
SpacingControl.js
Normal file
@ -0,0 +1,156 @@
|
||||
import { useState } from '@wordpress/element';
|
||||
|
||||
import {
|
||||
BaseControl,
|
||||
Button,
|
||||
RangeControl,
|
||||
__experimentalHStack as HStack,
|
||||
__experimentalVStack as VStack,
|
||||
__experimentalUnitControl as UnitControl,
|
||||
Icon
|
||||
} from '@wordpress/components';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { sidesHorizontal, sidesVertical, sidesTop, sidesBottom, sidesLeft, sidesRight, settings } from '@wordpress/icons';
|
||||
|
||||
export default function SpacingControl({ label, value = {}, onChange, themeSpacing = {} }) {
|
||||
const [verticalAdvanced, setVerticalAdvanced] = useState(false);
|
||||
const [horizontalAdvanced, setHorizontalAdvanced] = useState(false);
|
||||
const [advancedMode, setAdvancedMode] = useState(false); // full advanced toggle
|
||||
|
||||
const parseValue = (val) => (val ? parseInt(val) : 0);
|
||||
|
||||
const handleChange = (sides, val) => {
|
||||
const unit = val.toString().replace(/[0-9]/g, '') || 'px';
|
||||
const newValue = { ...value };
|
||||
sides.forEach((side) => {
|
||||
newValue[side] = `${parseInt(val)}${unit}`;
|
||||
});
|
||||
onChange(newValue);
|
||||
};
|
||||
|
||||
// --- Generate marks for RangeControl from theme.json spacing ---
|
||||
const spacingMarks = themeSpacing?.spacingSizes?.map((s) => ({
|
||||
value: parseInt(s.size), // you may need to strip "px" or clamp()
|
||||
label: s.name,
|
||||
})) || [];
|
||||
|
||||
// --- Advanced 4-row mode ---
|
||||
if (advancedMode) {
|
||||
const sides = [
|
||||
{ key: 'top', sideIcon: sidesTop },
|
||||
{ key: 'bottom', sideIcon: sidesBottom },
|
||||
{ key: 'left', sideIcon: sidesLeft },
|
||||
{ key: 'right', sideIcon: sidesRight },
|
||||
];
|
||||
|
||||
return (
|
||||
<BaseControl label={label}>
|
||||
<VStack gap={16}>
|
||||
<Button variant="secondary" onClick={() => setAdvancedMode(false)}>
|
||||
{__('Back to Simple Mode', 'directory-listings')}
|
||||
</Button>
|
||||
|
||||
{sides.map(({ key, sideIcon }) => (
|
||||
<VStack key={key} gap={4}>
|
||||
<HStack align="center" gap={8}>
|
||||
<Icon icon={sideIcon} />
|
||||
<div style={{ flex: 1 }}>
|
||||
<RangeControl
|
||||
value={parseValue(value[key] || 0)}
|
||||
onChange={(val) => handleChange([key], val)}
|
||||
min={0}
|
||||
max={Math.max(...spacingMarks.map((m) => m.value))}
|
||||
step={1}
|
||||
marks={spacingMarks}
|
||||
withInputField={false}
|
||||
/>
|
||||
</div>
|
||||
<UnitControl
|
||||
value={value[key] || '0px'}
|
||||
onChange={(val) => handleChange([key], val)}
|
||||
min={0}
|
||||
max={Math.max(...spacingMarks.map((m) => m.value))}
|
||||
/>
|
||||
</HStack>
|
||||
</VStack>
|
||||
))}
|
||||
</VStack>
|
||||
</BaseControl>
|
||||
);
|
||||
}
|
||||
|
||||
// --- Simple vertical + horizontal mode ---
|
||||
return (
|
||||
<BaseControl label={label}>
|
||||
<VStack gap={16}>
|
||||
<Button variant="secondary" onClick={() => setAdvancedMode(true)}>
|
||||
{__('Advanced Mode', 'directory-listings')}
|
||||
</Button>
|
||||
|
||||
{/* Vertical */}
|
||||
<VStack gap={4}>
|
||||
<HStack align="center" gap={8}>
|
||||
<Icon icon={sidesVertical} />
|
||||
{verticalAdvanced && (
|
||||
<UnitControl
|
||||
value={value.top || '0px'}
|
||||
onChange={(val) => handleChange(['top', 'bottom'], val)}
|
||||
min={0}
|
||||
max={Math.max(...spacingMarks.map((m) => m.value))}
|
||||
/>
|
||||
)}
|
||||
<div style={{ flex: 1 }}>
|
||||
<RangeControl
|
||||
value={parseValue(value.top || 0)}
|
||||
onChange={(val) => handleChange(['top', 'bottom'], val)}
|
||||
min={0}
|
||||
max={Math.max(...spacingMarks.map((m) => m.value))}
|
||||
step={1}
|
||||
marks={spacingMarks}
|
||||
withInputField={false}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
icon={settings}
|
||||
isSecondary
|
||||
onClick={() => setVerticalAdvanced(!verticalAdvanced)}
|
||||
aria-label={__('Toggle vertical spacing input', 'directory-listings')}
|
||||
/>
|
||||
</HStack>
|
||||
</VStack>
|
||||
|
||||
{/* Horizontal */}
|
||||
<VStack gap={4}>
|
||||
<HStack align="center" gap={8}>
|
||||
<Icon icon={sidesHorizontal} />
|
||||
{horizontalAdvanced && (
|
||||
<UnitControl
|
||||
value={value.left || '0px'}
|
||||
onChange={(val) => handleChange(['left', 'right'], val)}
|
||||
min={0}
|
||||
max={Math.max(...spacingMarks.map((m) => m.value))}
|
||||
/>
|
||||
)}
|
||||
<div style={{ flex: 1 }}>
|
||||
<RangeControl
|
||||
value={parseValue(value.left || 0)}
|
||||
onChange={(val) => handleChange(['left', 'right'], val)}
|
||||
min={0}
|
||||
max={Math.max(...spacingMarks.map((m) => m.value))}
|
||||
step={1}
|
||||
marks={spacingMarks}
|
||||
withInputField={false}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
icon={settings}
|
||||
isSecondary
|
||||
onClick={() => setHorizontalAdvanced(!horizontalAdvanced)}
|
||||
aria-label={__('Toggle horizontal spacing input', 'directory-listings')}
|
||||
/>
|
||||
</HStack>
|
||||
</VStack>
|
||||
</VStack>
|
||||
</BaseControl>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user