Enhance Button Customization in Form Builder
- Updated ComponentPreview.vue to support custom button styles, allowing users to define background color, text color, border color, border width, border radius, and hover effects for buttons. - Enhanced FormBuilderFieldSettingsModal.vue with new fields for customizing button appearance, including color pickers and number inputs for border properties. - Implemented a color preview feature in the settings modal to visualize button styles based on user selections, improving usability and customization options. - Added utility functions for generating custom button styles dynamically, ensuring consistent styling across the application.
This commit is contained in:
parent
3abaf7afe5
commit
8a6f87ebf1
@ -336,26 +336,66 @@
|
||||
{{ component.props.label }}
|
||||
</label>
|
||||
|
||||
<!-- Link Button -->
|
||||
<a v-if="component.props.linkType && component.props.linkType !== 'none' && getButtonLink()"
|
||||
:href="getButtonLink()"
|
||||
:target="component.props.linkTarget || '_self'"
|
||||
class="inline-block">
|
||||
<RsButton :type="component.props.buttonType || 'button'" :variant="component.props.variant || 'primary'"
|
||||
:size="component.props.size || 'md'" :disabled="component.props.disabled || false"
|
||||
<!-- Custom Button with Custom Colors -->
|
||||
<div v-if="component.props.variant === 'custom'" class="inline-block">
|
||||
<!-- Link Button with Custom Colors -->
|
||||
<a v-if="component.props.linkType && component.props.linkType !== 'none' && getButtonLink()"
|
||||
:href="getButtonLink()"
|
||||
:target="component.props.linkTarget || '_self'"
|
||||
class="inline-block">
|
||||
<button
|
||||
:type="component.props.buttonType || 'button'"
|
||||
:disabled="component.props.disabled || false"
|
||||
:class="getButtonSizeClass(component.props.size)"
|
||||
:style="getCustomButtonStyles(component.props)"
|
||||
:data-hover-effect="component.props.customHoverEffect || 'none'"
|
||||
class="button-component custom-button"
|
||||
@click="handleButtonClick"
|
||||
>
|
||||
<span v-if="component.props.showButtonText !== false">{{ component.props.buttonText || component.props.label || 'Button' }}</span>
|
||||
<Icon v-if="component.props.icon" :name="component.props.icon" class="w-4 h-4" :class="{'ml-2': component.props.showButtonText !== false}" />
|
||||
</button>
|
||||
</a>
|
||||
|
||||
<!-- Regular Button with Custom Colors (no link) -->
|
||||
<button
|
||||
v-else
|
||||
:type="component.props.buttonType || 'button'"
|
||||
:disabled="component.props.disabled || false"
|
||||
:class="getButtonSizeClass(component.props.size)"
|
||||
:style="getCustomButtonStyles(component.props)"
|
||||
:data-hover-effect="component.props.customHoverEffect || 'none'"
|
||||
class="button-component custom-button"
|
||||
@click="handleButtonClick"
|
||||
>
|
||||
<span v-if="component.props.showButtonText !== false">{{ component.props.buttonText || component.props.label || 'Button' }}</span>
|
||||
<Icon v-if="component.props.icon" :name="component.props.icon" class="w-4 h-4" :class="{'ml-2': component.props.showButtonText !== false}" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Standard Button (non-custom colors) -->
|
||||
<div v-else class="inline-block">
|
||||
<!-- Link Button -->
|
||||
<a v-if="component.props.linkType && component.props.linkType !== 'none' && getButtonLink()"
|
||||
:href="getButtonLink()"
|
||||
:target="component.props.linkTarget || '_self'"
|
||||
class="inline-block">
|
||||
<RsButton :type="component.props.buttonType || 'button'" :variant="component.props.variant || 'primary'"
|
||||
:size="component.props.size || 'md'" :disabled="component.props.disabled || false"
|
||||
class="button-component">
|
||||
<span v-if="component.props.showButtonText !== false">{{ component.props.buttonText || component.props.label || 'Button' }}</span>
|
||||
<Icon v-if="component.props.icon" :name="component.props.icon" class="w-4 h-4" :class="{'ml-2': component.props.showButtonText !== false}" />
|
||||
</RsButton>
|
||||
</a>
|
||||
|
||||
<!-- Regular Button (no link) -->
|
||||
<RsButton v-else :type="component.props.buttonType || 'button'" :variant="component.props.variant || 'primary'"
|
||||
:size="component.props.size || 'md'" :disabled="component.props.disabled || false" @click="handleButtonClick"
|
||||
class="button-component">
|
||||
<span v-if="component.props.showButtonText !== false">{{ component.props.buttonText || component.props.label || 'Button' }}</span>
|
||||
<Icon v-if="component.props.icon" :name="component.props.icon" class="w-4 h-4" :class="{'ml-2': component.props.showButtonText !== false}" />
|
||||
</RsButton>
|
||||
</a>
|
||||
|
||||
<!-- Regular Button (no link) -->
|
||||
<RsButton v-else :type="component.props.buttonType || 'button'" :variant="component.props.variant || 'primary'"
|
||||
:size="component.props.size || 'md'" :disabled="component.props.disabled || false" @click="handleButtonClick"
|
||||
class="button-component">
|
||||
<span v-if="component.props.showButtonText !== false">{{ component.props.buttonText || component.props.label || 'Button' }}</span>
|
||||
<Icon v-if="component.props.icon" :name="component.props.icon" class="w-4 h-4" :class="{'ml-2': component.props.showButtonText !== false}" />
|
||||
</RsButton>
|
||||
</div>
|
||||
|
||||
<div v-if="component.props.help" class="mt-1 text-xs text-gray-500">
|
||||
{{ component.props.help }}
|
||||
@ -1382,6 +1422,61 @@ const getButtonLink = () => {
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
// Custom button styling functions
|
||||
const getCustomButtonStyles = (props) => {
|
||||
if (!props || props.variant !== 'custom') return {};
|
||||
|
||||
const styles = {
|
||||
backgroundColor: props.customBackgroundColor || '#3b82f6',
|
||||
color: props.customTextColor || '#ffffff',
|
||||
border: 'none',
|
||||
cursor: 'pointer',
|
||||
transition: 'all 0.2s ease-in-out'
|
||||
};
|
||||
|
||||
// Add border if specified
|
||||
if (props.customBorderColor && props.customBorderWidth) {
|
||||
styles.border = `${props.customBorderWidth}px solid ${props.customBorderColor}`;
|
||||
}
|
||||
|
||||
// Add border radius
|
||||
if (props.customBorderRadius) {
|
||||
styles.borderRadius = `${props.customBorderRadius}px`;
|
||||
}
|
||||
|
||||
// Add hover effects
|
||||
const hoverEffect = props.customHoverEffect;
|
||||
if (hoverEffect && hoverEffect !== 'none') {
|
||||
switch (hoverEffect) {
|
||||
case 'darken':
|
||||
styles[':hover'] = { filter: 'brightness(0.9)' };
|
||||
break;
|
||||
case 'lighten':
|
||||
styles[':hover'] = { filter: 'brightness(1.1)' };
|
||||
break;
|
||||
case 'scale':
|
||||
styles[':hover'] = { transform: 'scale(1.05)' };
|
||||
break;
|
||||
case 'glow':
|
||||
styles[':hover'] = {
|
||||
boxShadow: `0 0 10px ${props.customBackgroundColor || '#3b82f6'}`
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return styles;
|
||||
};
|
||||
|
||||
const getButtonSizeClass = (size) => {
|
||||
const sizeClasses = {
|
||||
'sm': 'px-3 py-1.5 text-sm',
|
||||
'md': 'px-4 py-2 text-sm',
|
||||
'lg': 'px-6 py-3 text-base'
|
||||
};
|
||||
return sizeClasses[size] || sizeClasses['md'];
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@ -1659,4 +1754,42 @@ const getButtonLink = () => {
|
||||
background-color: #dbeafe !important;
|
||||
border-color: #3b82f6 !important;
|
||||
}
|
||||
|
||||
/* Custom button styles */
|
||||
.custom-button {
|
||||
font-weight: 500;
|
||||
border-radius: 6px;
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.custom-button:hover {
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.custom-button:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.custom-button:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
/* Custom button hover effects */
|
||||
.custom-button[data-hover-effect="darken"]:hover {
|
||||
filter: brightness(0.9);
|
||||
}
|
||||
|
||||
.custom-button[data-hover-effect="lighten"]:hover {
|
||||
filter: brightness(1.1);
|
||||
}
|
||||
|
||||
.custom-button[data-hover-effect="scale"]:hover {
|
||||
transform: scale(1.05) translateY(-1px);
|
||||
}
|
||||
|
||||
.custom-button[data-hover-effect="glow"]:hover {
|
||||
box-shadow: 0 0 15px rgba(59, 130, 246, 0.5);
|
||||
}
|
||||
</style>
|
@ -503,13 +503,111 @@
|
||||
{ label: 'Secondary (Gray)', value: 'secondary' },
|
||||
{ label: 'Success (Green)', value: 'success' },
|
||||
{ label: 'Danger (Red)', value: 'danger' },
|
||||
{ label: 'Warning (Orange)', value: 'warning' }
|
||||
{ label: 'Warning (Orange)', value: 'warning' },
|
||||
{ label: 'Custom Color', value: 'custom' }
|
||||
]"
|
||||
help="Visual appearance of the button"
|
||||
:classes="{ outer: 'field-wrapper' }"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Custom Color Picker -->
|
||||
<div v-if="configModel.variant === 'custom'" class="space-y-3">
|
||||
<h5 class="text-sm font-medium text-gray-700 border-b pb-2">Custom Color Settings</h5>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<FormKit
|
||||
type="color"
|
||||
label="Background Color"
|
||||
name="customBackgroundColor"
|
||||
v-model="configModel.customBackgroundColor"
|
||||
help="Choose the button background color"
|
||||
:classes="{ outer: 'field-wrapper' }"
|
||||
/>
|
||||
|
||||
<FormKit
|
||||
type="color"
|
||||
label="Text Color"
|
||||
name="customTextColor"
|
||||
v-model="configModel.customTextColor"
|
||||
help="Choose the button text color"
|
||||
:classes="{ outer: 'field-wrapper' }"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<FormKit
|
||||
type="color"
|
||||
label="Border Color"
|
||||
name="customBorderColor"
|
||||
v-model="configModel.customBorderColor"
|
||||
help="Choose the button border color"
|
||||
:classes="{ outer: 'field-wrapper' }"
|
||||
/>
|
||||
|
||||
<FormKit
|
||||
type="number"
|
||||
label="Border Width (px)"
|
||||
name="customBorderWidth"
|
||||
v-model="configModel.customBorderWidth"
|
||||
help="Set the border width in pixels"
|
||||
:classes="{ outer: 'field-wrapper' }"
|
||||
:min="0"
|
||||
:max="10"
|
||||
placeholder="2"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<FormKit
|
||||
type="number"
|
||||
label="Border Radius (px)"
|
||||
name="customBorderRadius"
|
||||
v-model="configModel.customBorderRadius"
|
||||
help="Set the corner roundness in pixels"
|
||||
:classes="{ outer: 'field-wrapper' }"
|
||||
:min="0"
|
||||
:max="50"
|
||||
placeholder="6"
|
||||
/>
|
||||
|
||||
<FormKit
|
||||
type="select"
|
||||
label="Hover Effect"
|
||||
name="customHoverEffect"
|
||||
v-model="configModel.customHoverEffect"
|
||||
:options="[
|
||||
{ label: 'None', value: 'none' },
|
||||
{ label: 'Darken', value: 'darken' },
|
||||
{ label: 'Lighten', value: 'lighten' },
|
||||
{ label: 'Scale', value: 'scale' },
|
||||
{ label: 'Glow', value: 'glow' }
|
||||
]"
|
||||
help="Choose hover animation effect"
|
||||
:classes="{ outer: 'field-wrapper' }"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Color Preview -->
|
||||
<div class="mt-4 p-3 bg-gray-50 border border-gray-200 rounded-lg">
|
||||
<h6 class="text-sm font-medium text-gray-700 mb-2">Color Preview</h6>
|
||||
<div class="flex items-center space-x-3">
|
||||
<button
|
||||
class="px-4 py-2 rounded font-medium transition-all duration-200"
|
||||
:style="getCustomButtonStyles()"
|
||||
disabled
|
||||
>
|
||||
{{ configModel.buttonText || configModel.label || 'Button Preview' }}
|
||||
</button>
|
||||
<div class="text-xs text-gray-600">
|
||||
<div>Background: {{ configModel.customBackgroundColor || '#3b82f6' }}</div>
|
||||
<div>Text: {{ configModel.customTextColor || '#ffffff' }}</div>
|
||||
<div>Border: {{ configModel.customBorderColor || 'transparent' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<FormKit
|
||||
type="select"
|
||||
@ -2348,6 +2446,49 @@ const getIframeUrlPreview = () => {
|
||||
return queryString ? `${baseUrl}?${queryString}` : baseUrl
|
||||
}
|
||||
|
||||
// Generate custom button styles for preview
|
||||
const getCustomButtonStyles = () => {
|
||||
const styles = {
|
||||
backgroundColor: configModel.value.customBackgroundColor || '#3b82f6',
|
||||
color: configModel.value.customTextColor || '#ffffff',
|
||||
border: 'none',
|
||||
cursor: 'pointer'
|
||||
}
|
||||
|
||||
// Add border if specified
|
||||
if (configModel.value.customBorderColor && configModel.value.customBorderWidth) {
|
||||
styles.border = `${configModel.value.customBorderWidth}px solid ${configModel.value.customBorderColor}`
|
||||
}
|
||||
|
||||
// Add border radius
|
||||
if (configModel.value.customBorderRadius) {
|
||||
styles.borderRadius = `${configModel.value.customBorderRadius}px`
|
||||
}
|
||||
|
||||
// Add hover effects
|
||||
const hoverEffect = configModel.value.customHoverEffect
|
||||
if (hoverEffect && hoverEffect !== 'none') {
|
||||
switch (hoverEffect) {
|
||||
case 'darken':
|
||||
styles[':hover'] = { filter: 'brightness(0.9)' }
|
||||
break
|
||||
case 'lighten':
|
||||
styles[':hover'] = { filter: 'brightness(1.1)' }
|
||||
break
|
||||
case 'scale':
|
||||
styles[':hover'] = { transform: 'scale(1.05)' }
|
||||
break
|
||||
case 'glow':
|
||||
styles[':hover'] = {
|
||||
boxShadow: `0 0 10px ${configModel.value.customBackgroundColor || '#3b82f6'}`
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return styles
|
||||
}
|
||||
|
||||
// Type changing functionality
|
||||
const compatibilityGroups = {
|
||||
// Text-based inputs (can switch between each other)
|
||||
|
Loading…
x
Reference in New Issue
Block a user