-
Notifications
You must be signed in to change notification settings - Fork 151
Feature/dynamic field icons #1701
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 9 commits
da2f17f
901641a
dfa34d2
8a70e94
fc205eb
464b990
511e0e7
66e6262
c38558f
5c7835b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -24,16 +24,20 @@ class="wpuf-group wpuf-rounded-lg hover:!wpuf-bg-green-50 wpuf-transition wpuf-d | |||||||||||||||||||||||||||||
| :class="parseInt(editing_form_id) === parseInt(field.id) ? 'wpuf-bg-green-50 wpuf-border-green-400' : 'wpuf-border-transparent'" | ||||||||||||||||||||||||||||||
| class="wpuf-flex wpuf-justify-between wpuf-p-6 wpuf-rounded-t-md wpuf-border-t wpuf-border-r wpuf-border-l wpuf-border-dashed group-hover:wpuf-border-green-400 group-hover:wpuf-cursor-pointer !wpuf-pb-3"> | ||||||||||||||||||||||||||||||
| <div v-if="!(is_full_width(field.template) || is_pro_preview(field.template))" class="wpuf-w-1/4 wpuf-flex wpuf-items-center"> | ||||||||||||||||||||||||||||||
| <span v-if="field.show_icon === 'yes' && field.field_icon && field.icon_position === 'left_label'" | ||||||||||||||||||||||||||||||
| class="wpuf-field-label-icon wpuf-inline-flex wpuf-items-center wpuf-mr-1"> | ||||||||||||||||||||||||||||||
| <i :class="[field.field_icon, 'wpuf-field-icon']"></i> | ||||||||||||||||||||||||||||||
| </span> | ||||||||||||||||||||||||||||||
| <label | ||||||||||||||||||||||||||||||
| v-if="!is_invisible(field)" | ||||||||||||||||||||||||||||||
| :for="'wpuf-' + field.name ? field.name : 'cls'" | ||||||||||||||||||||||||||||||
| class="wpuf-block wpuf-text-sm wpuf-font-medium wpuf-leading-6 wpuf-text-gray-900"> | ||||||||||||||||||||||||||||||
| {{ field.label }} <span v-if="field.required && 'yes' === field.required" | ||||||||||||||||||||||||||||||
| class="required">*</span> | ||||||||||||||||||||||||||||||
| </label> | ||||||||||||||||||||||||||||||
|
Comment on lines
31
to
37
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix ternary precedence in the label “for” binding The current expression always evaluates the truthy left side of the ternary and returns field.name without the wpuf- prefix. - :for="'wpuf-' + field.name ? field.name : 'cls'"
+ :for="'wpuf-' + (field.name ? field.name : 'cls')"📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||
| <span v-if="field.icon && field.show_icon === 'yes'" | ||||||||||||||||||||||||||||||
| class="wpuf-field-label-icon wpuf-inline-flex wpuf-items-center wpuf-ml-2" | ||||||||||||||||||||||||||||||
| v-html="field.icon"> | ||||||||||||||||||||||||||||||
| <span v-if="field.show_icon === 'yes' && field.field_icon && field.icon_position === 'right_label'" | ||||||||||||||||||||||||||||||
| class="wpuf-field-label-icon wpuf-inline-flex wpuf-items-center wpuf-ml-2"> | ||||||||||||||||||||||||||||||
| <i :class="[field.field_icon, 'wpuf-field-icon']"></i> | ||||||||||||||||||||||||||||||
| </span> | ||||||||||||||||||||||||||||||
|
Comment on lines
+38
to
41
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Symmetry for right-aligned icon: respect label visibility; aria-hidden Match the left icon fix. - <span v-if="field.show_icon === 'yes' && field.field_icon && field.icon_position === 'right_label'"
+ <span v-if="field.show_icon === 'yes' && field.field_icon && field.icon_position === 'right_label' && !is_invisible(field)"
class="wpuf-field-label-icon wpuf-inline-flex wpuf-items-center wpuf-ml-2">
- <i :class="[field.field_icon, 'wpuf-field-icon']"></i>
+ <i :class="[field.field_icon, 'wpuf-field-icon']" aria-hidden="true"></i>
</span>📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||
| <div | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -17,12 +17,16 @@ | |||||||||||||||||
| data-source="stage" | ||||||||||||||||||
| > | ||||||||||||||||||
| <div v-if="!is_full_width(field.template)" class="wpuf-label"> | ||||||||||||||||||
| <span v-if="field.show_icon === 'yes' && field.field_icon && field.icon_position === 'left_label'" | ||||||||||||||||||
| class="wpuf-field-label-icon wpuf-inline-flex wpuf-items-center wpuf-mr-1"> | ||||||||||||||||||
| <i :class="[field.field_icon, 'wpuf-field-icon']"></i> | ||||||||||||||||||
| </span> | ||||||||||||||||||
|
Comment on lines
+20
to
+23
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Hide icons when label is hidden; add aria-hidden Match v4-1 fixes for left icon. - <span v-if="field.show_icon === 'yes' && field.field_icon && field.icon_position === 'left_label'"
+ <span v-if="field.show_icon === 'yes' && field.field_icon && field.icon_position === 'left_label' && !is_invisible(field)"
class="wpuf-field-label-icon wpuf-inline-flex wpuf-items-center wpuf-mr-1">
- <i :class="[field.field_icon, 'wpuf-field-icon']"></i>
+ <i :class="[field.field_icon, 'wpuf-field-icon']" aria-hidden="true"></i>
</span>📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||
| <label v-if="!is_invisible(field)" :for="'wpuf-' + field.name ? field.name : 'cls'"> | ||||||||||||||||||
| {{ field.label }} <span v-if="field.required && 'yes' === field.required" class="required">*</span> | ||||||||||||||||||
| </label> | ||||||||||||||||||
|
Comment on lines
24
to
26
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix ternary precedence in the label “for” binding Ensure the wpuf- prefix is applied correctly. - <label v-if="!is_invisible(field)" :for="'wpuf-' + field.name ? field.name : 'cls'">
+ <label v-if="!is_invisible(field)" :for="'wpuf-' + (field.name ? field.name : 'cls')">📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||
| <span v-if="field.icon && field.show_icon === 'yes'" | ||||||||||||||||||
| class="wpuf-field-label-icon wpuf-inline-flex wpuf-items-center wpuf-ml-2" | ||||||||||||||||||
| v-html="field.icon"> | ||||||||||||||||||
| <span v-if="field.show_icon === 'yes' && field.field_icon && field.icon_position === 'right_label'" | ||||||||||||||||||
| class="wpuf-field-label-icon wpuf-inline-flex wpuf-items-center wpuf-ml-2"> | ||||||||||||||||||
| <i :class="[field.field_icon, 'wpuf-field-icon']"></i> | ||||||||||||||||||
| </span> | ||||||||||||||||||
|
Comment on lines
+27
to
30
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Right icon symmetry (visibility + aria) Same adjustment for the right-aligned icon. - <span v-if="field.show_icon === 'yes' && field.field_icon && field.icon_position === 'right_label'"
+ <span v-if="field.show_icon === 'yes' && field.field_icon && field.icon_position === 'right_label' && !is_invisible(field)"
class="wpuf-field-label-icon wpuf-inline-flex wpuf-items-center wpuf-ml-2">
- <i :class="[field.field_icon, 'wpuf-field-icon']"></i>
+ <i :class="[field.field_icon, 'wpuf-field-icon']" aria-hidden="true"></i>
</span>📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||
| </div> | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,109 @@ | ||
| Vue.component('field-icon_selector', { | ||
| template: '#tmpl-wpuf-field-icon_selector', | ||
|
|
||
| mixins: [ | ||
| wpuf_mixins.option_field_mixin | ||
| ], | ||
|
|
||
| mounted: function() { | ||
| document.addEventListener('click', this.handleClickOutside); | ||
| }, | ||
|
|
||
| data: function () { | ||
| return { | ||
| showIconPicker: false, | ||
| searchTerm: '', | ||
| icons: wpuf_form_builder.icons || [] | ||
| }; | ||
| }, | ||
|
|
||
| computed: { | ||
| value: { | ||
| get: function () { | ||
| return this.editing_form_field[this.option_field.name]; | ||
| }, | ||
|
|
||
| set: function (value) { | ||
| this.$store.commit('update_editing_form_field', { | ||
| editing_field_id: this.editing_form_field.id, | ||
| field_name: this.option_field.name, | ||
| value: value | ||
| }); | ||
| } | ||
| }, | ||
|
|
||
| selectedIconDisplay: function() { | ||
| if (this.value) { | ||
| var icon = this.icons.find(function(item) { | ||
| return item.class === this.value; | ||
| }.bind(this)); | ||
| return icon ? icon.name : this.value; | ||
| } | ||
| return 'Select an icon'; | ||
| }, | ||
|
|
||
| filteredIcons: function() { | ||
| var self = this; | ||
| if (!this.icons.length) return []; | ||
|
|
||
| if (!this.searchTerm) return this.icons; | ||
|
|
||
| var searchLower = this.searchTerm.toLowerCase(); | ||
| return this.icons.filter(function(icon) { | ||
| return icon.name.toLowerCase().indexOf(searchLower) !== -1 || | ||
| icon.keywords.toLowerCase().indexOf(searchLower) !== -1; | ||
| }); | ||
| } | ||
| }, | ||
|
|
||
| watch: { | ||
| 'editing_form_field.show_icon': function(newVal, oldVal) { | ||
| // When show_icon changes from 'no' to 'yes' and field_icon is empty or 'fas fa-0' | ||
| if (newVal === 'yes' && oldVal === 'no') { | ||
| if (!this.editing_form_field.field_icon || this.editing_form_field.field_icon === 'fas fa-0') { | ||
| // Set a proper default icon based on field type | ||
| var defaultIcons = wpuf_form_builder.defaultIcons || {}; | ||
|
|
||
| // Get the field type/template | ||
| var fieldType = this.editing_form_field.template || this.editing_form_field.input_type || 'text'; | ||
|
|
||
| // Set the default icon based on field type | ||
| var defaultIcon = defaultIcons[fieldType] || 'fa-solid fa-circle'; | ||
|
|
||
| this.$store.commit('update_editing_form_field', { | ||
| editing_field_id: this.editing_form_field.id, | ||
| field_name: 'field_icon', | ||
| value: defaultIcon | ||
| }); | ||
| } | ||
| } | ||
| } | ||
| }, | ||
|
|
||
| methods: { | ||
|
|
||
| selectIcon: function(iconClass) { | ||
| this.value = iconClass; | ||
| this.showIconPicker = false; | ||
| }, | ||
|
|
||
| clearIcon: function() { | ||
| this.value = ''; | ||
| this.showIconPicker = false; | ||
| }, | ||
|
|
||
| togglePicker: function() { | ||
| this.showIconPicker = !this.showIconPicker; | ||
| }, | ||
|
|
||
| handleClickOutside: function(event) { | ||
| if (!this.$el.contains(event.target)) { | ||
| this.showIconPicker = false; | ||
| } | ||
| } | ||
| }, | ||
|
|
||
| beforeDestroy: function() { | ||
| document.removeEventListener('click', this.handleClickOutside); | ||
| } | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| <div class="panel-field-opt panel-field-opt-icon-selector"> | ||
| <div class="wpuf-flex"> | ||
| <label v-if="option_field.title" class="!wpuf-mb-0"> | ||
| {{ option_field.title }} <help-text v-if="option_field.help_text" :text="option_field.help_text"></help-text> | ||
| </label> | ||
| </div> | ||
|
|
||
| <div class="option-fields-section wpuf-relative"> | ||
| <div | ||
| @click.stop="togglePicker" | ||
| class="wpuf-w-full wpuf-min-w-full !wpuf-py-[10px] !wpuf-px-[14px] wpuf-text-gray-700 wpuf-font-medium !wpuf-shadow-sm wpuf-border !wpuf-border-gray-300 !wpuf-rounded-[6px] hover:!wpuf-text-gray-700 wpuf-flex wpuf-justify-between wpuf-items-center !wpuf-text-base wpuf-cursor-pointer" | ||
| > | ||
| <div class="wpuf-flex wpuf-items-center wpuf-gap-2"> | ||
| <i v-if="value" :class="value" class="wpuf-text-gray-600"></i> | ||
| <span>{{ selectedIconDisplay }}</span> | ||
| </div> | ||
| <div class="wpuf-flex wpuf-items-center wpuf-gap-1"> | ||
| <i v-if="value" @click.stop="clearIcon" class="fa fa-times wpuf-text-gray-500 hover:wpuf-text-red-500 wpuf-cursor-pointer wpuf-p-1"></i> | ||
| <i :class="showIconPicker ? 'fa-angle-up' : 'fa-angle-down'" class="fa wpuf-text-base"></i> | ||
| </div> | ||
| </div> | ||
|
|
||
| <div | ||
| v-if="showIconPicker" | ||
| @click.stop | ||
| class="wpuf-absolute wpuf-bg-white wpuf-border wpuf-border-gray-300 wpuf-rounded-lg wpuf-w-full wpuf-z-50 wpuf-mt-1 wpuf-shadow-lg wpuf-right-0" | ||
| style="max-height: 300px; min-width: 320px; max-width: 400px;" | ||
| > | ||
| <!-- Search --> | ||
| <div class="wpuf-p-3 wpuf-border-b wpuf-border-gray-200"> | ||
| <input | ||
| v-model="searchTerm" | ||
| type="text" | ||
| placeholder="Search icons... (e.g., user, email, home)" | ||
| class="wpuf-w-full !wpuf-px-4 !wpuf-py-1.5 wpuf-border wpuf-border-gray-300 wpuf-rounded wpuf-text-sm wpuf-text-gray-900 placeholder:wpuf-text-gray-400 wpuf-shadow focus:!wpuf-shadow-none" | ||
| > | ||
| <div class="wpuf-text-xs wpuf-text-gray-500 wpuf-mt-1"> | ||
| {{ filteredIcons.length }} icons {{ searchTerm ? 'found' : 'available' }} | ||
| </div> | ||
| </div> | ||
|
|
||
| <!-- Icons Grid --> | ||
| <div class="wpuf-icon-grid-container" style="max-height: 210px; overflow-y: auto; padding: 10px;"> | ||
| <!-- Icons Grid --> | ||
| <div v-if="filteredIcons.length > 0" class="wpuf-icon-grid" style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px;"> | ||
| <div | ||
| v-for="icon in filteredIcons" | ||
| :key="icon.class" | ||
| @click="selectIcon(icon.class)" | ||
| :class="['wpuf-icon-grid-item', { 'selected': value === icon.class }]" | ||
| :title="icon.name + ' - ' + icon.keywords" | ||
| style="padding: 10px 5px; text-align: center; border: 1px solid #e0e0e0; border-radius: 4px; cursor: pointer; transition: all 0.2s; min-height: 60px; display: flex; flex-direction: column; align-items: center; justify-content: center;" | ||
| > | ||
| <i :class="icon.class" style="font-size: 18px; margin-bottom: 4px; color: #555;"></i> | ||
| <div style="font-size: 10px; color: #666; line-height: 1.2; word-break: break-word; max-width: 100%;">{{ icon.name }}</div> | ||
| </div> | ||
| </div> | ||
|
|
||
| <!-- No Results --> | ||
| <div v-else class="wpuf-text-center wpuf-py-8 wpuf-text-gray-500"> | ||
| <div style="font-size: 16px; margin-bottom: 8px;">🔍 No icons found</div> | ||
| <div style="font-size: 12px;">Try searching with different keywords like "user", "email", "home"</div> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| </div> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -185,6 +185,16 @@ | |
|
|
||
| // add new form field element | ||
| add_form_field_element: function (state, payload) { | ||
| // Initialize icon properties for new fields to ensure Vue reactivity | ||
| if (!payload.field.hasOwnProperty('show_icon')) { | ||
| payload.field.show_icon = 'no'; | ||
| } | ||
| if (!payload.field.hasOwnProperty('field_icon')) { | ||
| payload.field.field_icon = ''; | ||
| } | ||
| if (!payload.field.hasOwnProperty('icon_position')) { | ||
| payload.field.icon_position = 'left_label'; | ||
| } | ||
|
Comment on lines
+188
to
+197
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Mirror normalization here to avoid non-reactive writes Same rationale as in assets/js/wpuf-form-builder.js. Normalize in set_form_fields and in nested add/clone paths so toggling icon settings on older forms remains reactive. @@
- set_form_fields: function (state, form_fields) {
- Vue.set(state, 'form_fields', form_fields);
- },
+ set_form_fields: function (state, form_fields) {
+ form_fields.forEach(ensureIconDefaultsRecursively);
+ Vue.set(state, 'form_fields', form_fields);
+ },
@@
add_column_inner_field_element: function (state, payload) {
@@
- } else {
+ } else {
+ ensureIconDefaults(payload.field);
state.form_fields[columnFieldIndex].inner_fields[payload.toWhichColumn].splice( payload.toIndex, 0, payload.field );
}
},
@@
add_repeat_inner_field_element: function (state, payload) {
@@
- state.form_fields[repeatFieldIndex].inner_fields.splice(payload.toIndex, 0, payload.field);
+ ensureIconDefaults(payload.field);
+ state.form_fields[repeatFieldIndex].inner_fields.splice(payload.toIndex, 0, payload.field);
},
@@
clone_form_field_element: function (state, payload) {
@@
- clone.is_new = true;
+ clone.is_new = true;
+ ensureIconDefaultsRecursively(clone);
state.form_fields.splice(index, 0, clone);
},Add helpers (once in this file): function ensureIconDefaults(obj) {
if (!obj) return;
if (!obj.hasOwnProperty('show_icon')) Vue.set(obj, 'show_icon', 'no');
if (!obj.hasOwnProperty('field_icon')) Vue.set(obj, 'field_icon', '');
if (!obj.hasOwnProperty('icon_position')) Vue.set(obj, 'icon_position', 'left_label');
}
function ensureIconDefaultsRecursively(field) {
ensureIconDefaults(field);
if (field && field.template === 'column_field' && field.inner_fields) {
Object.keys(field.inner_fields).forEach(col => (field.inner_fields[col] || []).forEach(ensureIconDefaultsRecursively));
}
if (field && field.template === 'repeat_field' && Array.isArray(field.inner_fields)) {
field.inner_fields.forEach(ensureIconDefaultsRecursively);
}
}🤖 Prompt for AI Agents |
||
|
|
||
| state.form_fields.splice(payload.toIndex, 0, payload.field); | ||
| var sprintf = wp.i18n.sprintf; | ||
|
|
@@ -729,6 +739,24 @@ | |
| }, | ||
|
|
||
| mounted: function () { | ||
| // Ensure all fields have proper default values for icon settings when editing existing forms | ||
| this.$store.state.form_fields.forEach(function(field) { | ||
| // If show_icon is not set in the database, default it to 'no' | ||
| if (field.show_icon === undefined || field.show_icon === null) { | ||
| field.show_icon = 'no'; | ||
| } | ||
|
|
||
| // If field_icon is not set in the database, set it to default icon fas fa-0 | ||
| if (field.field_icon === undefined || field.field_icon === null) { | ||
| field.field_icon = 'fas fa-0'; | ||
| } | ||
|
|
||
| // If icon_position is not set, default it to 'left_label' | ||
| if (field.icon_position === undefined || field.icon_position === null) { | ||
| field.icon_position = 'left_label'; | ||
| } | ||
| }); | ||
|
|
||
| // Check if there are hidden custom taxonomy fields and show warning | ||
| if (wpuf_form_builder.has_hidden_taxonomies && !wpuf_form_builder.is_pro_active) { | ||
| var self = this; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Hide label icons when the label itself is hidden; add aria-hidden
Currently icons render even when the label is invisible. Gate by !is_invisible(field) and mark decorative icons as aria-hidden.
📝 Committable suggestion
🤖 Prompt for AI Agents