diff --git a/src/components/common/VnInput.vue b/src/components/common/VnInput.vue index 57a495ac3..3cecf760a 100644 --- a/src/components/common/VnInput.vue +++ b/src/components/common/VnInput.vue @@ -141,13 +141,16 @@ const handleInsertMode = (e) => { <QIcon name="close" size="xs" - v-if=" - hover && - value && - !$attrs.disabled && - !$attrs.readonly && - $props.clearable - " + :style="{ + visibility: + hover && + value && + !$attrs.disabled && + !$attrs.readonly && + $props.clearable + ? 'visible' + : 'hidden', + }" @click=" () => { value = null; diff --git a/src/components/ui/VnSearchbar.vue b/src/components/ui/VnSearchbar.vue index a2d3b9ee1..bfaa76588 100644 --- a/src/components/ui/VnSearchbar.vue +++ b/src/components/ui/VnSearchbar.vue @@ -1,14 +1,16 @@ <script setup> -import { onMounted, ref, watch } from 'vue'; +import { onMounted, ref, computed, watch } from 'vue'; import { useQuasar } from 'quasar'; import { useArrayData } from 'composables/useArrayData'; import VnInput from 'src/components/common/VnInput.vue'; import { useI18n } from 'vue-i18n'; import { useStateStore } from 'src/stores/useStateStore'; +import { useRoute } from 'vue-router'; const quasar = useQuasar(); const { t } = useI18n(); const state = useStateStore(); +const route = useRoute(); const props = defineProps({ dataKey: { @@ -83,6 +85,17 @@ if (props.redirect) }; let arrayData = useArrayData(props.dataKey, arrayDataProps); let store = arrayData.store; +const to = computed(() => { + const url = { path: route.path, query: { ...(route.query ?? {}) } }; + const searchUrl = arrayData.store.searchUrl; + const currentFilter = { + ...arrayData.store.currentFilter, + search: searchText.value || undefined, + }; + + if (searchUrl) url.query[searchUrl] = JSON.stringify(currentFilter); + return url; +}); watch( () => props.dataKey, @@ -132,23 +145,32 @@ async function search() { <template> <Teleport to="#searchbar" v-if="state.isHeaderMounted()"> <QForm @submit="search" id="searchbarForm"> + <RouterLink + :to="to" + @click=" + !$event.shiftKey && !$event.ctrlKey && search(); + $refs.input.focus(); + " + > + <QIcon + v-if="!quasar.platform.is.mobile" + class="cursor-pointer" + name="search" + size="sm" + > + <QTooltip>{{ t('link') }}</QTooltip> + </QIcon> + </RouterLink> <VnInput id="searchbar" + ref="input" v-model.trim="searchText" :placeholder="t(props.label)" dense - standout autofocus - data-cy="vnSearchBar" + data-cy="vn-searchbar" + borderless > - <template #prepend> - <QIcon - v-if="!quasar.platform.is.mobile" - class="cursor-pointer" - name="search" - @click="search" - /> - </template> <template #append> <QIcon v-if="props.info && $q.screen.gt.xs" @@ -173,20 +195,52 @@ async function search() { .q-field { transition: width 0.36s; } -</style> -<style lang="scss"> +:deep(.q-field__native) { + padding-top: 10px; + padding-left: 5px; +} + +:deep(.q-field--dark .q-field__native:focus) { + color: black; +} + +:deep(.q-field--focused) { + .q-icon { + color: black; + } +} + .cursor-info { cursor: help; } -#searchbar { - .q-field--standout.q-field--highlighted .q-field__control { + +.q-form { + display: flex; + align-items: center; + border-radius: 4px; + padding: 0 5px; + background-color: var(--vn-search-color); + + &:hover { + background-color: var(--vn-search-color-hover); + } + &:focus-within { background-color: white; - color: black; - .q-field__native, + .q-icon { - color: black !important; + color: black; } } } + +.q-icon { + color: var(--vn-label-color); +} </style> +<i18n> +en: + link: click to search, ctrl + click to open in a new tab, shift + click to open in a new window +es: + link: clic para buscar, ctrl + clic para abrir en una nueva pestaƱa, shift + clic para abrir en una nueva ventana +</i18n> diff --git a/src/composables/useArrayData.js b/src/composables/useArrayData.js index fd6e3a9b3..1f4234a00 100644 --- a/src/composables/useArrayData.js +++ b/src/composables/useArrayData.js @@ -76,26 +76,7 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) { cancelRequest(); canceller = new AbortController(); - - const filter = { - limit: store.limit, - }; - let userParams = { ...store.userParams }; - - Object.assign(filter, store.userFilter); - - let where; - if (filter?.where || store.filter?.where) - where = Object.assign(filter?.where ?? {}, store.filter?.where ?? {}); - Object.assign(filter, store.filter); - filter.where = where; - const params = { filter }; - - Object.assign(params, userParams); - if (params.filter) params.filter.skip = store.skip; - if (store?.order && typeof store?.order == 'string') store.order = [store.order]; - if (store.order?.length) params.filter.order = [...store.order]; - else delete params.filter.order; + const { params, limit } = getCurrentFilter(); store.currentFilter = JSON.parse(JSON.stringify(params)); delete store.currentFilter.filter.include; @@ -121,7 +102,6 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) { params, }); - const { limit } = filter; store.hasMoreData = limit && response.data.length >= limit; processData(response.data, { map: !!store.mapKey, append }); @@ -291,6 +271,31 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) { router.replace(newUrl); } + function getCurrentFilter() { + const filter = { + limit: store.limit, + }; + + let userParams = { ...store.userParams }; + + Object.assign(filter, store.userFilter); + + let where; + if (filter?.where || store.filter?.where) + where = Object.assign(filter?.where ?? {}, store.filter?.where ?? {}); + Object.assign(filter, store.filter); + filter.where = where; + const params = { filter }; + + Object.assign(params, userParams); + if (params.filter) params.filter.skip = store.skip; + if (store?.order && typeof store?.order == 'string') store.order = [store.order]; + if (store.order?.length) params.filter.order = [...store.order]; + else delete params.filter.order; + + return { filter, params, limit: filter.limit }; + } + function processData(data, { map = true, append = true }) { if (!append) { store.data = []; @@ -323,6 +328,7 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) { fetch, applyFilter, addFilter, + getCurrentFilter, addFilterWhere, addOrder, deleteOrder, diff --git a/src/css/app.scss b/src/css/app.scss index 9060d2fa7..d4790a6b8 100644 --- a/src/css/app.scss +++ b/src/css/app.scss @@ -10,6 +10,8 @@ body.body--light { --vn-text-color: black; --vn-label-color: #5f5f5f; --vn-accent-color: #e7e3e3; + --vn-search-color: #d4d4d4; + --vn-search-color-hover: #cfcfcf; --vn-empty-tag: #acacac; --vn-black-text-color: black; --vn-text-color-contrast: white; @@ -28,6 +30,8 @@ body.body--dark { --vn-text-color: white; --vn-label-color: #a8a8a8; --vn-accent-color: #424242; + --vn-search-color: #4b4b4b; + --vn-search-color-hover: #5b5b5b; --vn-empty-tag: #2d2d2d; --vn-black-text-color: black; --vn-text-color-contrast: black; diff --git a/test/cypress/integration/Order/orderCatalog.spec.js b/test/cypress/integration/Order/orderCatalog.spec.js index 88ec33025..cffc47f91 100644 --- a/test/cypress/integration/Order/orderCatalog.spec.js +++ b/test/cypress/integration/Order/orderCatalog.spec.js @@ -41,7 +41,7 @@ describe('OrderCatalog', () => { } }); cy.get( - '[data-cy="vnSearchBar"] > .q-field > .q-field__inner > .q-field__control' + '[data-cy="vn-searchbar"] > .q-field > .q-field__inner > .q-field__control' ).type('{enter}'); cy.get(':nth-child(1) > [data-cy="catalogFilterCategory"]').click(); cy.dataCy('catalogFilterValueDialogBtn').last().click(); diff --git a/test/cypress/integration/ticket/ticketList.spec.js b/test/cypress/integration/ticket/ticketList.spec.js index fa5f46de7..e273825c0 100644 --- a/test/cypress/integration/ticket/ticketList.spec.js +++ b/test/cypress/integration/ticket/ticketList.spec.js @@ -9,9 +9,9 @@ describe('TicketList', () => { }); const searchResults = (search) => { - cy.dataCy('vnSearchBar').find('input').focus(); - if (search) cy.dataCy('vnSearchBar').find('input').type(search); - cy.dataCy('vnSearchBar').find('input').type('{enter}'); + cy.dataCy('vn-searchbar').find('input').focus(); + if (search) cy.dataCy('vn-searchbar').find('input').type(search); + cy.dataCy('vn-searchbar').find('input').type('{enter}'); cy.dataCy('ticketListTable').should('exist'); cy.get(firstRow).should('exist'); }; diff --git a/test/cypress/support/commands.js b/test/cypress/support/commands.js index f698bfd6f..f166a0ed1 100755 --- a/test/cypress/support/commands.js +++ b/test/cypress/support/commands.js @@ -274,15 +274,11 @@ Cypress.Commands.add('openLeftMenu', (element) => { Cypress.Commands.add('clearSearchbar', (element) => { if (element) cy.waitForElement(element); - cy.get( - '#searchbar > form > div:nth-child(1) > label > div:nth-child(1) input' - ).clear(); + cy.get('[data-cy="vn-searchbar"]').clear(); }); -Cypress.Commands.add('typeSearchbar', (value) => { - cy.get('#searchbar > form > div:nth-child(1) > label > div:nth-child(1) input').type( - value - ); +Cypress.Commands.add('writeSearchbar', (value) => { + cy.get('[data-cy="vn-searchbar"]').type(value); }); Cypress.Commands.add('validateContent', (selector, expectedValue) => {