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

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?

Related

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

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

Buttons stop working when I add new div element

I am using sveltekit, typscript, and tailwind for this. Ok, I have a website that I am making and I have buttons that have no background and are formated like this:
When opened
- Label
stuff
when closed
+ Label
It worked and all but when I added a new div to be right next to those buttons the buttons stopped working completely. It would not even show the pointer hand.
Code for buttons:
<script lang="ts">
let expanded: boolean = true;
export let item: string = '';
export let value: string[] = [];
import Page from '../Components/page.svelte';
let pagen: string = 'About';
</script>
<Page bind:page={pagen} run="" />
<div class="dropdown flex flex-col mt-5">
<button
class="dropdown-toggle"
on:click={() => {
expanded = !expanded;
}}
>
<span class="dropdown-label text-gray-400 ml-5 flex text-lg"
>{expanded ? `- ${item}` : `+ ${item}`}</span
>
</button>
<div class="dropdown-content flex" style:display={expanded ? 'block' : 'none'}>
{#each value as val}
<div class="dropdown-item text-gray-400 ml-10">
<p class="inline-block text-gray-400">#</p>
<button
on:click={() => {
pagen = `{val}`;
}}>{val}</button
>
</div>
{/each}
</div>
</div>
The pagen that is binded is for the div that goes right next to it. (this is not important I think...)
Code for the page:
<script lang="ts">
export let page: string = 'About';
export let run: string = '';
</script>
{#if run == 'true'}
<div class="w-screen h-screen -z-10">
<div class="page ml-80 bg-gray-800 h-screen">
{#if page == 'About'}
<div class="flex flex-col items-center justify-center">
<h1 class="text-gray-400 text-3xl font-bold tracking-wider mt-
10">Some Title</h1>
<p class="text-gray-400 text-2xl font-bold tracking-wider">Some
Label</p>
</div>
{/if}
</div>
</div>
{/if}
These are both components in svelte and are imported in index.svelte. Buttons on top and page on bottom.
Helpful Images:
The website is being styled to look like discord.
Any help is greatly appreciated!
There are a few things I'm noticing that may help you get this working:
Nothing may be showing because values may be empty. When I added values = [ "a", "b", "c" ] I saw them no problem (see repl).
You're not setting run, so Page will never render. Also, using run as a string is weird since it appears it is a boolean?
If you're using Tailwind, instead use conditional classes:
<div class="dropdown-content flex" style:display={expanded ? 'block' : 'none'}>
<!-- should be: -->
<div class="dropdown-content flex" style:block={expanded} style:hidden={!expanded}>
Stylistic recommendation:
<span class="dropdown-label text-gray-400 ml-5 flex text-lg">
{expanded ? `- ${item}` : `+ ${item}`}
</span>
<!-- simpler: -->
<span class="dropdown-label text-gray-400 ml-5 flex text-lg">
{expanded ? "-" : "+"} {item}
</span>
Here is a repl with what I believe is working code.

Element value inside Bootstrap accordion doesn't validate empty

I have some cards dynamically generated with a loop. In one part, I have an accordion element that is triggered with a button (Bootstrap method). I change the background color of that button when the element accordion-body, which is contenteditable true, has value in it after losing focus. The problem is, I want to remove the button color if the accordion-body is empty, but the element accordion-body seems to still have a value after losing focus even when the user deletes all content.
This is the accordion code:
Why is it that $(this).val() never evaluates empty? I had a console message in the if statements, it never goes to the else validation.
$('.accordion-body').on('focusout', function() {
var style = $(this).closest('.accordion-item').find('button[id=infobtn]').attr('style');
//it will return string
style += ';background-color: #f4cccc;';
if ($(this).val() != '') {
$(this).closest('.accordion-item').find('button[id=infobtn]').attr('style', style);
} else {
$(this).closest('.accordion-item').find('button[id=infobtn]').attr('style');
}
});
<script> src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
<span class="mySpan">
<div class="accordion accordion-flush py-0" id="accordionFlushExampleOne">
<div class="accordion-item py-0">
<h2 class="accordion-header" id="flush-headingOne">
<button class="accordion-button collapsed" id="infobtn" name="infobtn" style="height:25px; <?php if (!empty($additionalInfo)) echo " background-color: #f4cccc; " ?>" type="btn btn-sm" data-bs-toggle="collapse" data-bs-target="#flush-collapseOne>" aria-expanded="false"
aria-controls="flush-collapseOne">Additional Info
</button>
</h2>
<div id="flush-collapseOne" class="accordion-collapse collapse" aria-labelledby="flush-headingOne" data-bs-parent="#accordionFlushExampleOne">
<div class="accordion-body fw-bold" id="additionalInfo" name="additionalInfo" contenteditable="true">
<?php echo "$additionalInfo" ?>
</div>
</div>
</div>
</div>
</span>
Giving HTML block elements the attribute contenteditable="true" is responsible for that behaviour.
Removing the content of such an element in the browser, will set a replacement <br> instead in there.
Thus, if ($(this).val() != '') will never be true, because your containing element will never be empty.
Changing the containing div to an inline element such as span fixes the issue and your conditonal logic will get to work.
HTML - swap <div> with <span>
<span class="accordion-body fw-bold" id="additionalInfo" name="additionalInfo" contenteditable="true">
<?php echo "$additionalInfo" ?>
</span>
HTML & JQuery - inner <span> within <div> to preserve bootstrap accordion design
<div class="accordion-body fw-bold" id="additionalInfo" name="additionalInfo" contenteditable="true">
<span contenteditable="true">
<?php echo "$additionalInfo" ?>
</span>
</div>
$('.accordion-body span').on('focusout', function() {...
Thanks for all the recommendations and information about this matter. #bitski suggestion took me to the right direction. I tried all the options given, and somehow my accordion-body element always received the <br> value, it was never empty. I did some research and found a way to reset the content after losing focus (I ended up using blur instead).
So my accordion-body field looks like this now:
<div class="accordion-body fw-bold" id="additionalInfo" name="additionalInfo" contenteditable="true" onblur="event.target.scroll(0,0)">
And my jquery looks like this:
$('.accordion-body').on('blur', function() {
var style = $(this).closest('.accordion-item').find('button[id=infobtn]').attr('style'); //it will return string
style += ';background-color: #f4cccc;';
if ($(this).text().trim().length != 0) {
$(this).closest('.accordion-item').find('button[id=infobtn]').attr('style', style);
} else {
style += ';background-color: #FFFFFF'; //When field is empty, change the button background color back to white
$(this).closest('.accordion-item').find('button[id=infobtn]').attr('style', style);
}
});
That did the trick! Hope this helps someone else.

dropdown multiple options issue

I have a list of contacts, I would like to display the phoneNumbers in inputs with dropdown.
<div id="retrievedContactsDiv" data-bind="foreach: Model.userContacts.contacts()">
<!-- ko if: ($data.phoneNumbers().length >= 1) -->
<div class="control-group span3 offset3 ">
<div class="input-append btn-group">
<input id="appendedInputButton" type="text" data-bind="value:$data.phoneNumbers()[0].phoneNumber()">
<a class="btn btn-primary dropdown-toggle" data-toggle="dropdown" href="#">
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<!-- ko foreach: $data.phoneNumbers() -->
<li><span data-bind="text: $data.phoneNumber()"></span> </li>
<!-- /ko -->
</ul>
</div>
</div>
<!-- /ko -->
</div>
JS
$('.dropdown-menu li span').click(function(){
var elementVal=$(this).text();
$('#appendedInputButton').val('');
$('#appendedInputButton').val(elementVal);
});
My problem is that I can't have same id, if I use class all the phoneNumbers of my contacts list will be modified, How can I do it to display correct value if the user use dropdown ?
http://imageup.fr/uploads/1377867794.jpeg
Use jQuery smart selectors to find the closest common parent and then find the field inside it.
$('.dropdown-menu li span').click(function(){
var elementVal = $(this).text();
$(this).closest('.input-append').find('#appendedInputButton').val(elementVal);
});
Actually this is also wrong (in terms of HTML) because you're stuck with duplicate IDs. I would rather suggest you do the same with a class instead of id, it'll work exactly the same with the suggested JS above.

Side by side images with no gap?

http://i.stack.imgur.com/o2Kht.png
Basically I want these buttons to be side by side.
I've put them in a container but I cannot get them side by side like the example below.
<head>
<link rel="stylesheet" type="text/css" href="mystyle.css"> <!-- Linking Style Sheets -->
</head>
<body>
<style type="text/css">
<!-- Background image -->
</style>
<div class="img-wrapper-center" id="titlebar">
<img src="titlebar.png" id="img1"/>
</div>
</div>
<div id="bodycontainer">
<div id="text">
Welcome to the shop, you can add your items to to the cart below
</div>
<div id="formcontainer">
<div id="myForm">
<form name="theForm" action="shop.html" method="post" onSubmit="return checkWholeForm(theForm)">
<input type="radio" name="ram" value="yes">yes<br>
<input type="radio" name="ram" value="no">no
<br><br>
<input type="radio" name="cpu" value="yes">yes<br>
<input type="radio" name="cpu" value="no">no
<br><br>
<input type="radio" name="harddrive" value="yes">yes<br>
<input type="radio" name="harddrive" value="no">no
<br><br>
</div>
<div id="submitbuttons">
<input type="submit" value="Submit">
<input type="reset">
</div>
</div>
</form>
<div id="buttoncontainer">
<div class="homebtn">
<a href="..\home.html" onmouseover="SwapOut()" onmouseout="SwapBack()"><img name="homebtn" src="homebuttonup.png"/>
</a>
<div class="shopbtn">
<a href="shop.html" onmouseover="SwapOutshop()" onmouseout="SwapBackshop()"><img name="shopbtn" src="shopbuttonup.png"/>
</a>
</div>
</div>
</body>
<!-- Start of rollover script -->
<script>
Rollimage = new Array()
Rollimage[0]= new Image(121,153)
Rollimage[0].src = "homebuttonup.png"
Rollimage[1] = new Image(121,153)
Rollimage[1].src = "homebutton.png"
function SwapOut() {
document.homebtn.src = Rollimage[1].src;
return true;
}
function SwapBack() {
document.homebtn.src = Rollimage[0].src;
return true;
}
Rollimageshop = new Array()
Rollimageshop[0]= new Image(121,153)
Rollimageshop[0].src = "shopbuttonup.png"
Rollimageshop[1] = new Image(121,153)
Rollimageshop[1].src = "shopbutton.png"
function SwapOutshop() {
document.shopbtn.src = Rollimageshop[1].src;
return true;
}
function SwapBackshop() {
document.shopbtn.src = Rollimageshop[0].src;
return true;
}
</script>
<!-- end of rollover script -->
<!-- Start of form validation -->
<script>
function checkWholeForm(theForm) {
var reason = '';
reason += checkRadioButtons(theForm.ram);
if (reason != '') {
alert(reason);
return false;
}
return true;
}
function checkRadioButtons(radiobuttons) {
var checkvalue = "";
var i=0;
var error = "";
var amountOfRadioButtons = radiobuttons.length;
for (i=0; i<amountOfRadioButtons; i++) {
if (radiobuttons[i].checked) {
checkvalue = theForm.ram[i].value;
}
}
if (!(checkvalue)) {
error = "Please choose an option for RAM.\n"
}
return error;
}
</script>
<!-- End of form validation -->
Sorry for delay, rofl im new to adding code to this so i did not know how to ident it.
.homebtn, .shopbtn{
display:inline; /*or inline-block*/
}
I made a jsfiddle here using images from google image search because I don't have access to your images, but the css should be quite similar for your own uses.
You should also fix your html for the button container
<div id="buttoncontainer">
<div class="homebtn">
<a href="..\home.html" onmouseover="SwapOut()" onmouseout="SwapBack()">
<img name="homebtn" src="homebuttonup.png"/>
</a>
</div>
<div class="shopbtn">
<a href="shop.html" onmouseover="SwapOutshop()" onmouseout="SwapBackshop()">
<img name="shopbtn" src="shopbuttonup.png"/>
</a>
</div>
</div>
You currently have one button nested inside of the other.
Looking at your code, I can spot a few areas that can be improved.
First, you're using JavaScript to handle the button rollovers. Unless there is a specific reason to be doing this, it isn't necessary. A more efficient way to do this would be to use pure CSS for the button rollovers.
Second, you have the images embedded in the link itself:
<a href="..\home.html" onmouseover="SwapOut()" onmouseout="SwapBack()"><img name="homebtn" src="homebuttonup.png"/>
</a>
If you were to actually use CSS based navigation, you wouldn't need to do this. The benefit is less code and ultimately page size and load time. Keep in mind, every image loading is a separate HTTP Request to the server. This equals more bandwidth in the long run. Your website may not be effected by this type of stuff, but it's good practice; best practices.
I have created a JSFiddle to show you how I would approach your navigation. One other thing I would add is putting all of the navigation imagery into a Sprite.
http://jsfiddle.net/cbPRv/