Quasar, Input Clearable, Show "clear" button only when in focus - quasar

I'm using q-input from Quasar framework. I made the "clear" button from this website -> https://quasar.dev/vue-components/input#clearable
How to make the "clear" button is hidden when there is no focus in the "input" field.
And make the "clear" button is displayed when you focus in the "input" field. (using Quasar framework)
The "clear" button: is a small icon of a cross that shows in the input field.
HTML
<div id="q-app" style="min-height: 100vh;">
<div class="q-pa-md">
<div class="q-gutter-y-md column" style="max-width: 300px">
<!-- equivalent -->
<q-input color="orange" filled v-model="text" label="Label">
<template v-if="text" v-slot:append>
<q-icon name="cancel" #click.stop.prevent="text = null" class="cursor-pointer"></q-icon>
</template>
</q-input>
</div>
<div class="q-gutter-y-md column" style="max-width: 300px">
<!-- equivalent -->
<q-input color="orange" filled v-model="text" label="Label">
<template v-if="text" v-slot:append>
<q-icon name="cancel" #click.stop.prevent="text = null" class="cursor-pointer"></q-icon>
</template>
</q-input>
</div>
<div class="q-gutter-y-md column" style="max-width: 300px">
<!-- equivalent -->
<q-input color="orange" filled v-model="text" label="Label">
<template v-if="text" v-slot:append>
<q-icon name="cancel" #click.stop.prevent="text = null" class="cursor-pointer"></q-icon>
</template>
</q-input>
</div>
</div>
</div>
JS + Quasar
const { ref } = Vue
const app = Vue.createApp({
setup () {
return {
text: ref('Some text')
}
}
})
app.use(Quasar, { config: {} })
app.mount('#q-app')
Now the input fields look like this (The "clear" button is displayed regardless of focus):
I want it to look like this (The "clear" button only in the field in which there is focus)
How can this be done ?

Please refer following code.
<q-input color="orange" filled v-model="text" label="Label" #focus="focus=true" #blur="focus=false">
<template v-slot:append>
<q-icon name="cancel" v-if="text && focus" #click.stop.prevent="text = null"
class="cursor-pointer"></q-icon>
</template>
</q-input>
https://codepen.io/Pratik__007/pen/RwBoLQb

Related

How can I fix input width and label spacing to match surrounding lightning inputs

I have a LWC, there is a section which comprises of filters. Most of them are standard lightning-inputs, but two of them, are custom lookup components. The problem is, I can't find a way to make them align and look the same.. even if internally it is applying slds css classes (to some extent). This is how it's looking right now:
As you can see, every lightning input is aligned correctly, but then the two lookup component's inputs are wider and with less space between the label and the input.
This is the lookup html code:
<template>
<label if:true={label} class={getLabelClass} for="combobox">
<abbr if:true={required} title="required" class="slds-required">*</abbr>
{label}
</label>
<div class={getContainerClass}>
<div class={getDropdownClass} aria-expanded={hasResults} aria-haspopup="listbox" role="combobox">
<!-- Search input start -->
<div class={getComboboxClass} role="none">
<!-- Text input -->
<template if:false={useInput}>
<slot
aria-autocomplete="list"
aria-controls="listbox"
role="textbox"
id="combobox"
onchange={handleInput}
oninput={handleInput}
onkeydown={handleKeyDown}
onslotchange={handleSlotChange}
></slot>
</template>
<template if:true={useInput}>
<input onfocus={handleFocus}
onblur={handleBlur}
oninput={handleInput}
class="slds-input"
style="color: black;"/>
</template>
<template if:true={isLookup}>
<!-- Search icon -->
<lightning-icon
icon-name="utility:search"
size="x-small"
alternative-text="Search icon"
class={getSearchIconClass}
></lightning-icon>
<button
title="Remove selected option"
type="button"
onclick={handleClearSelection}
class={getClearSelectionButtonClass}
disabled={disabled}
>
<lightning-icon
icon-name="utility:close"
size="x-small"
alternative-text="Remove selected option"
class="slds-button__icon"
></lightning-icon>
</button>
</template>
</div>
<!-- Search input end -->
<!-- Result list box start -->
<div
id="listbox"
role="listbox"
>
<ul class={getListboxClass} role="presentation">
<!-- Spinner to display when waiting for results of search -->
<div if:true={loading}>
<lightning-spinner variant="brand" alternative-text="Loading" size="small"></lightning-spinner>
</div>
<template for:each={_searchResults} for:item="result">
<li data-id={result.id} onmousedown={handleResultClick} class="slds-listbox__item" key={result.id} role="presentation">
<div class="slds-media slds-listbox__option slds-listbox__option_plain slds-media_small" role="option">
<c-lookup-result template={_template} result={result}></c-lookup-result>
</div>
</li>
</template>
<!-- Result list end -->
<!-- No results start -->
<template if:false={hasResults}>
<li role="presentation">
<span class="slds-media slds-listbox__option_entity" role="option">
<span if:false={loading} class="slds-media__body">No results.</span>
<span if:true={loading} class="slds-media__body">Loading...</span>
</span>
</li>
</template>
<!-- No results end -->
</ul>
</div>
<!-- Result list box end -->
</div>
</div>
</template>
In my case, "useInput" is true
These are the getters:
get getContainerClass() {
let css = 'slds-combobox_container'; // slds-has-inline-listbox // sacado porque moleasta
if (this._hasFocus && this.hasResults) {
css += 'slds-has-input-focus ';
}
if (this.errors.length > 0) {
css += 'has-custom-error';
}
return css;
}
get getDropdownClass() {
let css = 'slds-combobox slds-dropdown-trigger slds-dropdown-trigger_click ';
if (this._hasFocus && (this._currentConfig || this.loading)) {
css += 'slds-is-open';
}
return css;
}
get getComboboxClass() {
let css = 'slds-combobox__form-element slds-input-has-icon ';
return css += this.hasSelection() ? 'slds-input-has-icon_left-right' : 'slds-input-has-icon_right';
}
get getListboxClass() {
return (
'slds-listbox slds-listbox_vertical slds-dropdown slds-dropdown_fluid ' +
(this.scrollAfterNItems ? 'slds-dropdown_length-with-icon-' + this.scrollAfterNItems : '')
);
}
get getClearSelectionButtonClass() {
return (
'slds-button slds-button_icon slds-input__icon slds-input__icon_left ' +
(this.hasSelection() ? '' : 'slds-hide')
);
}
get getSearchIconClass() {
let css = 'slds-input__icon slds-input__icon_right ';
if (!this.isMultiEntry) {
css += this.hasSelection() ? 'slds-hide' : '';
}
return css;
}
get getLabelClass() {
return this.variant === VARIANT_LABEL_HIDDEN
? 'slds-form-element__label slds-assistive-text'
: 'slds-form-element__label';
}
get input() {
return this._input;
}
This is my code for what's on the image:
<template if:true={filters}>
<div class="slds-grid slds-gutters">
<div class="slds-col slds-size_1-of-2">
<c-lwc-filter filter={filters.Nombre} input-variant="label-inline"></c-lwc-filter>
<c-lwc-filter filter={filters.Codigo} input-variant="label-inline"></c-lwc-filter>
<c-lwc-filter filter={filters.CodigoFabricante} input-variant="label-inline"></c-lwc-filter>
</div>
<div class="slds-col slds-size_1-of-2">
<c-lwc-filter filter={filters.CodigoBarra} input-variant="label-inline"></c-lwc-filter>
<div class="slds-form-element slds-form-element_horizontal">
<c-lookup label="Familia" is-lookup=true use-input=true configs={configsFamilia} onremoveselection={removeSelection} data-name="_familiaId"></c-lookup>
</div>
<div class="slds-form-element slds-form-element_horizontal">
<c-lookup label="Marca" is-lookup=true use-input=true configs={configsMarca} onremoveselection={removeSelection} data-name="_marcaId"></c-lookup>
</div>
<c-lwc-filter filter={filters.Modelo} input-variant="label-inline"></c-lwc-filter>
</div>
</div>
</template>
(the lwcFilter components are lightning-inputs internally)
I got the label to be inline, wrapping my custom lookup lwc inside a div with slds-form-element_horizontal class. But the problem is, as the "input" inside the lookup is not just a normal input, it is wider and as I said before, the spacing between the label and said input is very small.
How can I make them look the same as the base lightning-inputs? And for it to work on every screen size? (because I tried using margin or padding but it doesn't look the same with smaller / bigger screens)
Any help would be appreciated!
Have you tried to include slds-form-element__control in getContainerClass()?
Something like this:
get getContainerClass() {
let css = 'slds-combobox_container slds-form-element__control'; // slds-has-inline-listbox // sacado porque moleasta
// ... rest of logic
return css;
}
Does it break anything?

How to have two v-model directive inside ValidationProvider in VeeValidate?

I want to create a custom Date Picker component with Vue version 2 and VeeValidate. I am using vuetify and v-menu component from there: https://vuetifyjs.com/en/components/menus/ . The problem is that ValidationProvider which is responsible for validating fields accepts only one v-model inside of it and I have two: one for triggering v-menu and second for gathering value to v-text-field from v-date-picker. When I remove this v-model from v-menu validation works but I cannot close menu after I choose some values in date-picker.
<ValidationProvider v-slot="{ validate, errors }">
<label>...</label>
<v-menu v-model="menu">
<template v-slot:activator="{ on, attrs }">
<v-text-field
v-bind="attrs"
:value="formatDate"
readonly
v-on="on"
#input="$emit('input', $event) || validate($event)"
/>
</template>
<v-date-picker
:value="value"
no-title
#input="$emit('input', $event) || validate($event)"
#change="menu = false"
/>
</v-menu>
<span
v-if="errors.length > 0"
>{{ errors[0] }}</span>
</ValidationProvider>

how to style a selected mat-button in an angular application

I have an Angular application where the user has multiple choices with buttons and when a button is pressed another component will display. The component depends on the user's choice.
One thing I'm trying to implement is the styling of the buttons to make it clear which choice has been selected.
<div fxLayout="row" fxLayoutAlign="space-between center" fxFill>
<div *ngFor="let button of buttons">
<button
mat-stroked-button
*ngIf="button.type === 'button'"
(click)="buttonPressed()"
ngxScrollTo
>
{{ button.text }}
</button>
</div>
<div fxLayout="row" fxLayoutAlign="space-between center">
<div *ngIf="item.hasSomeProperty | isTypeButton">
<button mat-mini-fab (click)="buttonPressed()">
<mat-icon>close</mat-icon>
</button>
</div>
</div>
I have also attached a picture of what im trying to achieve here:
Any help would be much appreciated.
Simply use [ngClass] or [ngStyle]:
<div *ngFor="let button of buttons">
<button
mat-stroked-button
*ngIf="button.type === 'button'"
[ngClass]="{'disabledButton': !button.selected}"
(click)="buttonPressed(button)"
ngxScrollTo
>
{{ button.text }}
</button>
</div>
Assuming that your button model contains the "selected" property (or you have some other model storing information which button was actually clicked):
buttonPressed(button: Button) {
// Mark only button as selected
this.buttons.forEach(b => b.selected = b === button);
}
And of course add some class in the css:
.disabledButton {
opacity: 0.75;
}
Note - writing this from top of my head (since no stackblitz was provided) so some adjustments might be needed.

How to show/hide in Angular2

I have a component that show/hide element by clicking a button.
This is my html
<div *ngFor="let history of histories | sortdate: '-dateModified'">
<p><b>{{ history.remarks }}</b> - <i>{{history.dateModified | date:'short'}}</i></p>
<a href="google.com"
[class.datatable-icon-right]="history.$$expanded"
[class.datatable-icon-down]="!history.$$expanded"
title="Expand/Collapse Row"
(click)="toggleExpandRow(history)"></a>
<!-- hide/show this by clicking the button above.-->
<div *ngFor="let step of history.steps; let i = index">
<b>{{i+1}}.</b> {{step}}
<span class="clear"></span>
</div>
<hr />
</div>
and my .ts
toggleExpandRow(row) {
console.log('Toggled Expand Row!', row);
//row
return false;
}
trying to search but, can't find any same sample.
On jquery, I can do this, but on Angular2, I am having hard time to figure this.
There are two options:
1- You can use the hidden directive to show or hide any element
<div [hidden]="!edited" class="alert alert-success box-msg" role="alert">
<strong>List Saved!</strong> Your changes has been saved.
</div>
2- You can use the ngIf control directive to add or remove the element. This is different of the hidden directive because it does not show / hide the element, but it add / remove from the DOM. You can loose unsaved data of the element. It can be the better choice for an edit component that is cancelled.
<div *ngIf="edited" class="alert alert-success box-msg" role="alert">
<strong>List Saved!</strong> Your changes has been saved.
</div>
Use the ngIf in your repeated rows. Create a boolean property called showStep to indicate whether the row should be expanded or not.
<div *ngFor="let step of history.steps; let i = index" ngIf="history.showStep">
<b>{{i+1}}.</b> {{step}}
<span class="clear"></span>
</div>
Then, in your .ts file:
toggleExpandRow(history) {
history.showStep = !history.showStep
//note the same porperty of showStep that is used in your html
}
Extra:
In fact, to save a few lines of codes, you don't even need the toggleExpandRow function at all. You can do it inline in your html:
//other attributes omitted for brevity
<a (click)="history.showStep = !history.showStep">

Knockout Clone Whole Item In foreach

I am trying to clone elements when clicking a button. I was trying to use ko.toJS. On page load it works fine, but when I want clone the items, it is unable to bind the items (like, value, Text, etc.).
Here is the HTML:
<div class="stockItems-inner" data-bind="foreach: StockItems">
<div data-bind="if: Type=='Input'">
<div class="stock_container_input">
<input type="text" data-bind="value: Value" />
</div>
</div>
<div data-bind="if: Type=='Radio'">
<div class="stock_container_control">
<div data-bind="foreach: Options">
<div class="stockLbl">
<input type="radio" data-bind="text: Text, checked:$parent.Value, attr:{'id':Id, 'name': $parent.Text, 'value': Value}" />
<label data-bind="attr:{'for':Id}, text: Text"></label>
</div>
</div>
</div>
</div>
</div>
<div class="addItem">
<button type="button" data-bind="click: CloneItem"><img src="images/add.png" alt="" /></button>
</div>
The View Model:
ConfigurationStockViewModel = function() {
var self = this;
this.StockItems = ko.observableArray();
this.ApplyData = function(data){
self.StockItems(data.Items);
}
this.CloneItem = function(StockItems){
self.StockItems.push(ko.toJS(StockItems));
};
};
When clicking the button, an error is thrown: Unable to process binding. I am using JSON data for binding.
Not exactly sure what end result you want without working code, but sounds like you want to clone the last item in array and add to array?
If so, I think you have an error - your add button click binding will never pass anything to the function you defined, since it is outside the foreach. You need something like this:
this.CloneItem = function() {
var toClone = self.StockItems()[self.StockItems().length - 1]
self.StockItems.push(toClone);
};
Here is a simplified example without radio buttons, etc:
http://jsfiddle.net/5J47L/