Bugzilla – Attachment 188814 Details for
Bug 41151
Vue Additional Fields UX Improvements
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 41151: Modernize clear button UX in additional fields
Bug-41151-Modernize-clear-button-UX-in-additional-.patch (text/plain), 14.06 KB, created by
Martin Renvoize (ashimema)
on 2025-10-31 11:44:22 UTC
(
hide
)
Description:
Bug 41151: Modernize clear button UX in additional fields
Filename:
MIME Type:
Creator:
Martin Renvoize (ashimema)
Created:
2025-10-31 11:44:22 UTC
Size:
14.06 KB
patch
obsolete
>From bcba4fec8ea81902f91b83d3d48e526a91ef4919 Mon Sep 17 00:00:00 2001 >From: Martin Renvoize <martin.renvoize@openfifth.co.uk> >Date: Fri, 31 Oct 2025 09:30:52 +0000 >Subject: [PATCH] Bug 41151: Modernize clear button UX in additional fields >MIME-Version: 1.0 >Content-Type: text/plain; charset=UTF-8 >Content-Transfer-Encoding: 8bit > >This patch improves the UX of the clear functionality for additional >fields by: > >- Replacing the separate "Clear" button with an inline clear icon (Ã) > that appears inside the input field when there is content >- Adding proper spacing between the input field and "New" button > using flexbox layout >- Styling the "New" button with Bootstrap button classes for > consistency >- Making the clear button only visible when the field has content > >This provides a more modern, intuitive interface pattern similar to >standard search inputs and maintains better visual consistency with >other form elements in Koha. >--- > .../vue/components/AdditionalFieldsEntry.vue | 320 +++++++++++------- > 1 file changed, 206 insertions(+), 114 deletions(-) > >diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/components/AdditionalFieldsEntry.vue b/koha-tmpl/intranet-tmpl/prog/js/vue/components/AdditionalFieldsEntry.vue >index e7d3dcc2636..71108676e31 100644 >--- a/koha-tmpl/intranet-tmpl/prog/js/vue/components/AdditionalFieldsEntry.vue >+++ b/koha-tmpl/intranet-tmpl/prog/js/vue/components/AdditionalFieldsEntry.vue >@@ -1,131 +1,136 @@ > <template> >- <fieldset >- v-if="available_fields.length" >- class="rows" >- id="additional_fields" >- > >- <legend>{{ $__("Additional fields") }}</legend> >- <ol> >+ <template v-if="available_fields.length"> >+ <li class="additional-fields-header"> >+ <label></label> >+ <h3>{{ $__("Additional fields") }}</h3> >+ </li> >+ <template >+ v-for="available_field in available_fields" >+ v-bind:key="available_field.extended_attribute_type_id" >+ > > <template >- v-for="available_field in available_fields" >- v-bind:key="available_field.extended_attribute_type_id" >+ v-if=" >+ available_field.authorised_value_category_name && >+ !available_field.repeatable >+ " > > >- <template >- v-if=" >- available_field.authorised_value_category_name && >- !available_field.repeatable >- " >- > >- <li> >- <label >- :for=" >- `additional_field_` + >- available_field.extended_attribute_type_id >- " >- >{{ available_field.name }}: >- </label> >- <v-select >- :id=" >- `additional_field_` + >- available_field.extended_attribute_type_id >- " >- :name="available_field.name" >- v-model=" >- current_additional_fields_values[ >- available_field.extended_attribute_type_id >- ] >- " >- :options=" >- av_options[ >- available_field >- .authorised_value_category_name >- ] >- " >- /> >- </li> >- </template> >- <template >- v-if=" >- available_field.authorised_value_category_name && >- available_field.repeatable >- " >- > >- <li> >- <label >- :for=" >- `additional_field_` + >+ <li> >+ <label >+ :for=" >+ `additional_field_` + >+ available_field.extended_attribute_type_id >+ " >+ >{{ available_field.name }}: >+ </label> >+ <v-select >+ :id=" >+ `additional_field_` + >+ available_field.extended_attribute_type_id >+ " >+ :name="available_field.name" >+ v-model=" >+ current_additional_fields_values[ > available_field.extended_attribute_type_id >- " >- >{{ available_field.name }}: >- </label> >- <v-select >- :id=" >- `additional_field_` + >+ ] >+ " >+ :options=" >+ av_options[ >+ available_field.authorised_value_category_name >+ ] >+ " >+ /> >+ </li> >+ </template> >+ <template >+ v-if=" >+ available_field.authorised_value_category_name && >+ available_field.repeatable >+ " >+ > >+ <li> >+ <label >+ :for=" >+ `additional_field_` + >+ available_field.extended_attribute_type_id >+ " >+ >{{ available_field.name }}: >+ </label> >+ <v-select >+ :id=" >+ `additional_field_` + >+ available_field.extended_attribute_type_id >+ " >+ :name="available_field.name" >+ :multiple="available_field.repeatable" >+ v-model=" >+ current_additional_fields_values[ > available_field.extended_attribute_type_id >- " >- :name="available_field.name" >- :multiple="available_field.repeatable" >- v-model=" >- current_additional_fields_values[ >- available_field.extended_attribute_type_id >- ] >- " >- :options=" >- av_options[ >- available_field >- .authorised_value_category_name >- ] >- " >- /> >- </li> >- </template> >+ ] >+ " >+ :options=" >+ av_options[ >+ available_field.authorised_value_category_name >+ ] >+ " >+ /> >+ </li> >+ </template> > >- <template >- v-if="!available_field.authorised_value_category_name" >+ <template v-if="!available_field.authorised_value_category_name"> >+ <li >+ v-for="current in current_additional_fields_values[ >+ available_field.extended_attribute_type_id >+ ]" >+ v-bind:key="current.id" > > >- <li >- v-for="current in current_additional_fields_values[ >+ <label >+ :for=" >+ `additional_field_` + > available_field.extended_attribute_type_id >- ]" >- v-bind:key="current.id" >- > >- <label >- :for=" >+ " >+ >{{ available_field.name }}: >+ </label> >+ <span class="input-group"> >+ <input >+ type="text" >+ v-model="current.value" >+ :id=" > `additional_field_` + > available_field.extended_attribute_type_id > " >- >{{ available_field.name }}: >- </label> >- <input type="text" v-model="current.value" /> >+ class="input-with-clear" >+ /> >+ <button >+ v-if="current.value" >+ type="button" >+ class="clear-button" >+ @click="clearField(current, $event)" >+ :aria-label="$__('Clear')" >+ tabindex="-1" >+ > >+ <i class="fa fa-times"></i> >+ </button> >+ </span> >+ <template v-if="available_field.repeatable"> > <a > href="#" >- class="clear_attribute" >- @click="clearField(current, $event)" >+ class="btn btn-default btn-sm" >+ @click=" >+ cloneField(available_field, current, $event) >+ " > > >- <i class="fa fa-fw fa-trash-can"></i> >- {{ $__("Clear") }} >+ <i class="fa fa-fw fa-plus"></i> >+ {{ $__("New") }} > </a> >- <template v-if="available_field.repeatable"> >- <a >- href="#" >- class="clone_attribute" >- @click=" >- cloneField(available_field, current, $event) >- " >- > >- <i class="fa fa-fw fa-plus"></i> >- {{ $__("New") }} >- </a> >- </template> >- </li> >- </template> >+ </template> >+ </li> > </template> >- </ol> >- </fieldset> >+ </template> >+ </template> > </template> > > <script> >-import { onBeforeMount, watch, ref, reactive } from "vue"; >+import { onBeforeMount, watch, ref, reactive, nextTick } from "vue"; > import { APIClient } from "../fetch/api-client.js"; > > export default { >@@ -149,17 +154,33 @@ export default { > }); > > const clearField = (current_field, event) => { >- event.preventDefault(); >+ if (event) { >+ event.preventDefault(); >+ } > current_field.value = ""; > }; > const cloneField = (available_field, current, event) => { > event.preventDefault(); >- current_additional_fields_values[ >- available_field.extended_attribute_type_id >- ].push({ >- value: current.value, >+ const fieldArray = >+ current_additional_fields_values[ >+ available_field.extended_attribute_type_id >+ ]; >+ fieldArray.push({ >+ value: "", > label: available_field.name, > }); >+ >+ // Focus the newly created field after DOM updates >+ nextTick(() => { >+ const fieldId = `additional_field_${available_field.extended_attribute_type_id}`; >+ const inputs = document.querySelectorAll( >+ `input[id="${fieldId}"]` >+ ); >+ if (inputs.length > 0) { >+ // Focus the last input (the newly added one) >+ inputs[inputs.length - 1].focus(); >+ } >+ }); > }; > > const updateParentAdditionalFieldValues = >@@ -311,3 +332,74 @@ export default { > emits: ["additional-fields-changed"], > }; > </script> >+ >+<style scoped> >+/* Header for additional fields section */ >+.additional-fields-header { >+ border-top: 1px solid #ddd; >+ margin-top: 1em; >+ padding-top: 1em; >+} >+ >+.additional-fields-header h3 { >+ margin: 0; >+ font-size: 110%; >+ font-weight: bold; >+ color: #696969; >+} >+ >+.additional-fields-header label { >+ /* Empty label for alignment with other fields */ >+ width: 10rem; >+} >+ >+/* Input group container for positioning */ >+.input-group { >+ position: relative; >+ display: inline; >+} >+ >+/* Input with clear button gets padding for the button */ >+input.input-with-clear { >+ padding-right: 2rem; >+} >+ >+.clear-button { >+ position: absolute; >+ right: 0.25rem; >+ top: 50%; >+ transform: translateY(-50%); >+ background: transparent; >+ border: none; >+ color: #999; >+ cursor: pointer; >+ padding: 0.25rem 0.5rem; >+ line-height: 1; >+ transition: color 0.15s ease-in-out; >+ z-index: 10; >+ height: 1.5rem; >+ display: flex; >+ align-items: center; >+ justify-content: center; >+ margin: 0; >+} >+ >+.clear-button:hover { >+ color: #333; >+} >+ >+.clear-button:focus { >+ outline: 2px solid #007bff; >+ outline-offset: 2px; >+ border-radius: 2px; >+} >+ >+.clear-button i { >+ font-size: 1rem; >+} >+ >+/* New button spacing */ >+.btn.btn-sm { >+ margin-left: 0.5rem; >+} >+</style> >-- >2.51.1
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 41151
:
188814
|
188815
|
188816
|
188817
|
188818
|
188819
|
188820
|
188821
|
188822
|
188874
|
188875
|
188876
|
188877
|
188878
|
188879
|
188880