Refactor Layout Settings in FormBuilderFieldSettingsModal.vue

- Removed the old layout and appearance section and replaced it with a new compact width selector for improved usability.
- Introduced a layout tab for components that support width settings, enhancing the organization of field settings.
- Updated styles for the new compact width selection, including button groups and visual previews, to provide a more intuitive user experience.
- Ensured that the current width selection feedback is clearly displayed, improving user awareness of their choices.
This commit is contained in:
Afiq 2025-08-07 15:00:47 +08:00
parent bc7daed988
commit bf5e1630b3

View File

@ -306,93 +306,6 @@
</div>
</div>
<!-- Layout & Appearance Section -->
<div v-if="showField('width')" class="settings-section">
<div class="section-header">
<h4 class="section-title">
<Icon name="heroicons:squares-2x2" class="w-5 h-5 mr-2" />
Layout & Size
</h4>
<p class="section-description">Control how this field appears in your form</p>
</div>
<div class="section-content">
<div>
<label class="block text-sm font-medium text-gray-700 mb-3">Field Width</label>
<!-- Width Options with Visual Grid Preview -->
<div class="width-selector">
<div
v-for="option in widthOptions"
:key="option.value"
@click="setComponentWidth(option.value, option.gridColumns)"
class="width-option"
:class="{
'selected': getComponentWidthPercent() === option.value,
'recommended': isRecommendedWidth(option.type)
}"
>
<!-- Visual Grid Preview -->
<div class="grid-preview">
<div class="grid-container-mini">
<div
v-for="i in 12"
:key="i"
class="grid-cell"
:class="{
'active': i <= option.gridColumns,
'inactive': i > option.gridColumns
}"
></div>
</div>
</div>
<!-- Option Info -->
<div class="option-info">
<div class="option-name">
{{ option.name }}
<span v-if="isRecommendedWidth(option.type)" class="recommended-badge">
Recommended
</span>
</div>
<div class="option-description">{{ option.description }}</div>
<div class="option-use-case">{{ option.useCase }}</div>
</div>
</div>
</div>
<!-- Current Selection Feedback -->
<div class="current-selection-feedback">
<div class="feedback-row">
<span class="feedback-label">Current width:</span>
<span class="feedback-value">{{ getCurrentWidthOption()?.name || 'Custom' }}</span>
</div>
<div class="feedback-row">
<span class="feedback-label">Grid columns:</span>
<span class="feedback-value">{{ getCurrentGridColumns() }} of 12</span>
</div>
<!-- Visual representation -->
<div class="current-width-visual">
<div class="visual-grid">
<div
v-for="i in 12"
:key="i"
class="visual-cell"
:class="{
'filled': i <= getCurrentGridColumns(),
'empty': i > getCurrentGridColumns()
}"
></div>
</div>
<div class="visual-label">
{{ getCurrentGridColumns() }}/12 columns ({{ configModel.width || '100%' }})
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Component-Specific Settings -->
<div v-if="hasSpecificSettings" class="settings-section">
@ -2459,6 +2372,61 @@ if (this.element.querySelector('.file-upload')) {
</div>
</div>
<!-- Layout Tab -->
<div v-if="activeTab === 'layout'" class="space-y-6">
<div class="settings-section">
<div class="section-header">
<h4 class="section-title">
<Icon name="heroicons:squares-2x2" class="w-5 h-5 mr-2" />
Layout & Size
</h4>
<p class="section-description">Control how this field appears in your form</p>
</div>
<div class="section-content">
<div>
<label class="block text-sm font-medium text-gray-700 mb-3">Field Width</label>
<!-- Compact Width Selector -->
<div class="width-selector-compact">
<!-- Button Group for Width Selection -->
<div class="width-buttons">
<button
v-for="option in widthOptions"
:key="option.value"
@click="setComponentWidth(option.value, option.gridColumns)"
type="button"
class="width-button"
:class="{
'active': getComponentWidthPercent() === option.value,
'recommended': isRecommendedWidth(option.type)
}"
:title="option.useCase"
>
<div class="width-visual">
<div class="width-bar" :style="{ width: option.value + '%' }"></div>
</div>
<span class="width-label">{{ option.name }}</span>
</button>
</div>
<!-- Simple Visual Preview -->
<div class="width-preview">
<div class="preview-container">
<div class="preview-field" :style="{ width: getComponentWidthPercent() + '%' }">
<div class="preview-input"></div>
</div>
</div>
<div class="preview-info">
{{ getCurrentWidthOption()?.name || 'Custom' }} ({{ getComponentWidthPercent() }}%)
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Validation Tab -->
<div v-if="activeTab === 'validation'" class="space-y-6">
<div class="settings-section">
@ -2931,6 +2899,11 @@ const availableTabs = computed(() => {
{ id: 'basic', label: 'Basic Settings', icon: 'heroicons:cog-6-tooth' }
]
// Add layout tab for components that support width settings
if (showField('width')) {
tabs.push({ id: 'layout', label: 'Layout', icon: 'heroicons:squares-2x2' })
}
if (hasOptions.value) {
tabs.push({ id: 'options', label: 'Options', icon: 'heroicons:list-bullet' })
}
@ -4082,6 +4055,84 @@ const getDefaultPropsForType = (type) => {
@apply flex justify-between items-center py-1;
}
/* Compact Width Selection - New Styles */
.width-selector-compact {
@apply space-y-4;
}
.width-buttons {
@apply flex flex-wrap gap-2;
}
.width-button {
@apply flex-1 min-w-0 px-3 py-2 border border-gray-200 rounded-lg hover:border-blue-300 hover:bg-blue-50 transition-all duration-200 cursor-pointer text-center relative;
}
.width-button.active {
@apply border-blue-500 bg-blue-50 ring-1 ring-blue-200;
}
.width-button.recommended::before {
content: '';
@apply absolute -top-1 -right-1 w-3 h-3 bg-green-500 rounded-full;
}
.width-button.active.recommended {
@apply border-green-500 bg-green-50 ring-1 ring-green-200;
}
.width-visual {
@apply w-full h-1.5 bg-gray-100 rounded-sm mb-2 overflow-hidden;
}
.width-bar {
@apply h-full bg-blue-500 rounded-sm transition-all duration-300;
}
.width-button.active .width-bar {
@apply bg-blue-600;
}
.width-button.recommended .width-bar {
@apply bg-green-500;
}
.width-button.active.recommended .width-bar {
@apply bg-green-600;
}
.width-label {
@apply text-sm font-medium text-gray-700;
}
.width-button.active .width-label {
@apply text-blue-700;
}
.width-button.recommended .width-label {
@apply text-green-700;
}
.width-preview {
@apply mt-4 p-3 bg-gray-50 rounded-lg border;
}
.preview-container {
@apply w-full bg-white rounded border p-2 mb-2;
}
.preview-field {
@apply transition-all duration-300;
}
.preview-input {
@apply w-full h-8 border border-gray-100 rounded px-2 bg-blue-600;
}
.preview-info {
@apply text-sm text-gray-600 text-center;
}
.feedback-label {
@apply text-sm font-medium text-gray-600;
}