Button stays focused (hover) after clicked - twitter-bootstrap-3

I'm using bootstrap and vue 2.0.
It works normally when I don't click the button. It focus on #mouseenter and unfocus on #mouseleave like this.
But when I clicked button, it stay focused like this until I make another click anywhere, even in another window.
How can I fix this? Here is HTML and CSS code.
Button html:
<button class="btn btn-primary" v-on:click="search()"
type="button">{{$lang.ticketsSearch}}</button>
Button css:
.btn {
display: inline-block;
padding: 6px 12px;
margin-bottom: 0;
font-size: 14px;
font-weight: normal;
line-height: 1.42857143;
text-align: center;
white-space: nowrap;
vertical-align: middle;
-ms-touch-action: manipulation;
touch-action: manipulation;
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
background-image: none;
border: 1px solid transparent;
border-radius: 4px;
}
.btn:focus,
.btn:active:focus,
.btn.active:focus,
.btn.focus,
.btn:active.focus,
.btn.active.focus {
outline: 5px auto -webkit-focus-ring-color;
outline-offset: -2px;
}
.btn:hover,
.btn:focus,
.btn.focus {
color: #333;
text-decoration: none;
}
.btn:active,
.btn.active {
background-image: none;
outline: 0;
-webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
}
.btn.disabled,
.btn[disabled],
fieldset[disabled] .btn {
cursor: not-allowed;
filter: alpha(opacity=65);
-webkit-box-shadow: none;
box-shadow: none;
opacity: .65;
}
a.btn.disabled,
fieldset[disabled] a.btn {
pointer-events: none;
}
.btn-primary {
color: #fff;
background-color: #337ab7;
border-color: #2e6da4;
}
.btn-primary:focus,
.btn-primary.focus {
color: #fff;
background-color: #286090;
border-color: #122b40;
}
.btn-primary:hover {
color: #fff;
background-color: #286090;
border-color: #204d74;
}
.btn-primary:active,
.btn-primary.active,
.open > .dropdown-toggle.btn-primary {
color: #fff;
background-color: #286090;
border-color: #204d74;
}
.btn-primary:active:hover,
.btn-primary.active:hover,
.open > .dropdown-toggle.btn-primary:hover,
.btn-primary:active:focus,
.btn-primary.active:focus,
.open > .dropdown-toggle.btn-primary:focus,
.btn-primary:active.focus,
.btn-primary.active.focus,
.open > .dropdown-toggle.btn-primary.focus {
color: #fff;
background-color: #204d74;
border-color: #122b40;
}
.btn-primary:active,
.btn-primary.active,
.open > .dropdown-toggle.btn-primary {
background-image: none;
}
.btn-primary.disabled:hover,
.btn-primary[disabled]:hover,
fieldset[disabled] .btn-primary:hover,
.btn-primary.disabled:focus,
.btn-primary[disabled]:focus,
fieldset[disabled] .btn-primary:focus,
.btn-primary.disabled.focus,
.btn-primary[disabled].focus,
fieldset[disabled] .btn-primary.focus {
background-color: #337ab7;
border-color: #2e6da4;
}
.btn-primary .badge {
color: #337ab7;
background-color: #fff;
}

When that button is clicked, it takes on the :focus state. If you don't want it to behave like that, you'd have to overwrite the focus state, but that's probably not a good idea. You could look into programmatically putting the focus on something else.
In this particular case to remove the "green" when you click and move away, you should only need to do:
.btn-primary:focus,
.btn-primary.focus {
color: #fff;
background-color: #000; /*this is where the colour was green*/
border-color: #122b40;
}

Related

How to enter multiple email inputs in one field?

I would like to create an input field that accept multiple email addresses (for example to send an invitation). How can I achieve this without the help of jQuery or an external plugin or package?
I used to rely on bootstrap-tagsinput but I want to get rid of it, but I have no idea how to achieve the same thing without it.
This solution is quite a carbon copy of how the bootstrap tags input behaves but uses vanilla js only and some style rules.
It captures the click event on the container to create a text input inside that when will loose focus, will create a .tag span inside its parent with its original value (unless that value is empty spaces).
You may also change that condition so that it will create the tag only if the typed text matches a regular expression describing a valid email address.
const emailInput = document.getElementById('emailInputContainer');
//creates a tag element with the given text
function createTag(text){
const tag = document.createElement('span');
tag.classList.add('tag');
tag.innerText = text;
const remove = document.createElement('span');
remove.classList.add('remove');
tag.append(remove);
remove.addEventListener('click', (event)=>{
event.currentTarget.parentElement.remove();
});
return tag;
}
//creates and returns an input element
function createNewInput(){
const newInput = document.createElement('input');
newInput.classList.add('tempinput');
newInput.addEventListener('focusout', (event)=>{
const target = event.currentTarget;
if(target.value.trim().length > 0){
const tag = createTag(target.value);
target.parentElement.append(tag);
}
target.remove();
});
return newInput;
}
//adds the click event listener to the input container
emailInput.addEventListener('click', (event)=>{
const target = event.currentTarget;
const newInput = createNewInput();
target.append(newInput);
newInput.focus();
});
body{
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
#emailInputContainer{
border: solid 1px;
background-color: #fff;
border: 1px solid #ccc;
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
display: inline-block;
padding: 4px 6px;
color: #555;
vertical-align: middle;
border-radius: 4px;
line-height: 22px;
cursor: text;
width: 100%;
height: 1.5em;
}
.tag{
padding: 2px 5px;
margin-right: 5px;
background: #5bc0de;
color: white;
display: inline;
padding: .2em .6em .3em;
font-size: 75%;
font-weight: 700;
line-height: 1;
color: #fff;
text-align: center;
white-space: nowrap;
vertical-align: baseline;
border-radius: .25em;
}
.tempinput{
border: none;
outline: none;
}
.tag > .remove{
margin-left: 8px;
cursor: pointer;
}
.tag > .remove::after{
content: "x";
padding: 0px 0px;
}
<div id="emailInputContainer" tabindex="0">
</div>

Apply rules to list of parent classes

I have the below LESS stylesheet and I know there has to be a better way to organize this. Is the only option to create a map containing the classes and a mixin perhaps to repeat the styles?
// child div is injected by JS
.ddemrcontent > span, .blocksmarttemplate > span, .blocktoken > span {
display: inline-flex;
align-items: center;
min-height: 1.7rem;
margin-top: 0.2rem;
padding-left: 0.2rem;
}
.ddfreetext {
display: flex;
min-height: 1.7rem;
margin-top: 0.2rem;
}
.ddemrcontent > span:hover, .blocksmarttemplate > span:hover, .blocktoken > span:hover {
text-decoration: underline;
cursor: pointer;
}
.ddemrcontent > span {
border-left: 4px solid cadetblue;
}
.blocksmarttemplate > span {
border-left: 4px solid burlywood;
}
.blocktoken > span {
border-left: 4px solid #8a7090;
}
.ddfreetext {
border: 1px dashed black;
}
UPDATE
Here is the best I've been able to come up with. Since the & parent selector won't apply to each distinct parent selector (that are comma delimited) I think I am forced to use a mixin and call it to apply the rules for each parent I have.
Would love to hear if there's still a better way.
.dyndoccontent(#color) {
& > span {
display: inline-flex;
align-items: center;
min-height: 1.7rem;
margin-top: 0.2rem;
padding-left: 0.2rem;
border-left: 4px solid #color;
&:hover {
text-decoration: underline;
cursor: pointer;
}
}
}
// child div is injected by JS
.ddemrcontent {
.dyndoccontent(cadetblue);
}
.blocksmarttemplate {
.dyndoccontent(burlywood);
}
.blocktoken {
.dyndoccontent(#8a7090);
}
.ddfreetext {
display: flex;
min-height: 1.7rem;
margin-top: 0.2rem;
border: 1px dashed black;
}
I would definitely recommend mixin if you have multiple parts in your less files which use the same styles.
For you example i would go for a more nested way:
// child div is injected by JS
.ddemrcontent, .blocksmarttemplate, .blocktoken {
& > span {
display: inline-flex;
align-items: center;
min-height: 1.7rem;
margin-top: 0.2rem;
padding-left: 0.2rem;
&:hover {
text-decoration: underline;
cursor: pointer;
}
}
}
.ddfreetext {
border: 1px dashed black;
display: flex;
min-height: 1.7rem;
margin-top: 0.2rem;
}
.ddemrcontent > span {
border-left: 4px solid cadetblue;
}
.blocksmarttemplate > span {
border-left: 4px solid burlywood;
}
.blocktoken > span {
border-left: 4px solid #8a7090;
}

Cannot set Axios properly with Vue 2 and Nuxt

I am quite new to vue.js, using vue 2 and nuxt.js. I am trying to build an authentication in a website. There is a backend api running in local.
I have a form that receives my user's data. The data is then stored in the store using vuex. I want to make a POST Api call using axios, which retrieves the data stored from the store to the API running localy.
Unfortunately, I cannot get axios to run this properly...
It seems that the sendData function cant have access to the store. I tried many things : this.$store.user, this.user, $store...
Thanks a lot for your help!
4th Component
<template>
<form class="form-horizontal" >
<!-- firstname input-->
<div class="field">
<label class="label" for="contact_first_name">Prénom</label>
<div class="control">
<input id="contact_first_name" name="contact_first_name" type="contact_first_name" placeholder="Prénom" class="input " required="" v-model="companycontact.contact_first_name">
</div>
</div>
<!-- name input-->
<div class="field">
<label class="label" for="contact_last_name">Nom</label>
<div class="control">
<input id="contact_last_name" name="contact_last_name" type="contact_last_name" placeholder="Nom" class="input " required="" v-model="companycontact.contact_last_name">
</div>
</div>
<!-- service choice -->
<div class="field">
<label class="label" for="Service">Service</label>
<div class="control">
<div class="formfield-select">
<select id="service-choice" name="service-choice" class="input" v-model="companycontact.contact_service">
<option v-for="(service, index) in services" :key="index" :value="service.value">
{{ service.text }}
</option>
</select>
</div>
</div>
<div class="next-back-button">
<nuxt-link to="/SignUpCompanyOnline" class="button is-rounded back-button" #click.native="saveStore()">Retour</nuxt-link>
<nuxt-link to="/SignUpCompanyIg" class="button is-rounded next-button" #click.native="saveStore(); sendData()">Suivant</nuxt-link>
</div>
</div>
</form>
</template>
<script>
export default {
name: 'SignUpCompanyContact',
data () {
return {
companycontact: {
contact_first_name: this.contact_first_name,
contact_last_name: this.contact_last_name,
services: this.services,
},
services: [
{ text: 'Communication', value: 'COM' },
{ text: 'Direction', value: 'DIR' },
{ text: 'Marketing', value: 'MAR' },
{ text: 'Autre', value: 'OTH' },
],
}
},
methods: {
saveStore() {
this.$store.commit('signUp/setContactServiceInsta', { companycontact: this.companycontact })
},
async sendData () {
// console.log(this.user.email)
// console.log(this.$store.user.email)
// console.log(user.email)
// console.log($store)
/* let data = {
email: this.user.email,
password: this.password,
nb_employees: this.numberemployee,
name: this.name,
insta_account: this.insta_account,
sale_type: this.option,
sector_type: this.sector,
website_url: this.website_url,
contact_first_name: this.contact_first_name,
contact_last_name: this.contact_last_name,
contact_service: this.contact_service
}
// console.log(data);
this.$axios.post('http://localhost:8000/api/companies/', data )
.then(response => console.log(response))
.catch(error => console.log(error.response)) */
}
}
}
</script>
<style>
.form-example {
padding-bottom: 20px;
}
.form-logo{
width: 30px;
height: 30px;
border-bottom:2px solid #D8D8D8;
}
input {
width: 300px;
}
input[placeholder] {
width: 300px;
}
/* contact firstname lastname */
input[type=contact_last_name] {
width: 100%;
padding: 12px 20px;
margin: 8px 0;
box-sizing: border-box;
border: none;
border-bottom: 2px solid #D8D8D8;
background-image: url(../../assets/icons/user-lightgrey.svg);
background-size: 30px;
background-position-y: 4px;
background-repeat: no-repeat;
text-indent: 20px;
}
input[type=contact_last_name]:focus {
border-bottom: 2px solid #7F7F7F;
outline: none;
background-image: url(../../assets/icons/user-darkgrey.svg);
-webkit-box-shadow: 0px 14px 8px -8px rgba(0,0,0,0.40);
-moz-box-shadow: 0px 14px 8px -8px rgba(0,0,0,0.40);
box-shadow: 0px 14px 8px -8px rgba(0,0,0,0.40);
transition-duration: .3s;
border-radius: 1px;
}
input[type=contact_first_name] {
width: 100%;
padding: 12px 20px;
margin: 8px 0;
box-sizing: border-box;
border: none;
border-bottom: 2px solid #D8D8D8;
background-image: url(../../assets/icons/user-lightgrey.svg);
background-size: 30px;
background-position-y: 4px;
background-repeat: no-repeat;
text-indent: 20px;
}
input[type=contact_first_name]:focus {
border-bottom: 2px solid #7F7F7F;
outline: none;
background-image: url(../../assets/icons/user-darkgrey.svg);
-webkit-box-shadow: 0px 14px 8px -8px rgba(0,0,0,0.40);
-moz-box-shadow: 0px 14px 8px -8px rgba(0,0,0,0.40);
box-shadow: 0px 14px 8px -8px rgba(0,0,0,0.40);
transition-duration: .3s;
border-radius: 1px;
}
/* service */
#service-choice {
padding-left: 20px !important;
margin: 8px 0;
box-sizing: border-box;
color: #4A4A4A !important;
border: 1px solid #fff !important;
border-bottom: 2px solid #D8D8D8 !important;
background-image: url(../../assets/icons/sector-lightgrey.svg);
background-size: 30px;
background-repeat: no-repeat;
text-indent: 20px;
outline: none !important;
}
#service-choice:focus {
border-bottom: 2px solid #7F7F7F;
outline: none;
color: #4A4A4A !important;
background-image: url(../../assets/icons/sector-darkgrey.svg);
border-radius: 1px;
}
/* submit button */
.submit-button {
text-decoration: none;
outline: none;
background-color: #00CC99;
border: 1px solid #00CC99;
font-size: 20px;
color: #fff;
font-weight: normal;
padding: 10px 20px;
margin-top: 40px;
font-family: 'Montserrat', sans-serif;
-webkit-box-shadow: 0px 0px 44px -9px rgba(0,0,0,0.40);
-moz-box-shadow: 0px 0px 44px -9px rgba(0,0,0,0.40);
box-shadow: 0px 0px 44px -9px rgba(0,0,0,0.40);
}
.submit-button:hover {
background-color: #fff;
border: 1px solid #00CC99;
color: #00CC99;
transition: background-color .3s;
}
/* next button / back button */
.next-back-button {
display: flex;
justify-content: space-evenly;
flex-direction: row;
width: 50%;
}
.next-button {
text-decoration: none;
outline: none;
background-color: #00CC99;
border: 1px solid #00CC99;
font-size: 20px;
color: #fff;
font-weight: normal;
padding: 10px 20px;
margin-top: 40px;
font-family: 'Montserrat', sans-serif;
}
.next-button:hover {
background-color: #fff;
border: 1px solid #00CC99;
color: #00CC99;
transition: background-color .3s;
}
.back-button {
text-decoration: none;
outline: none;
background-color: #ffffff;
border: 1px solid #D8D8D8;
font-size: 20px;
color: #D8D8D8;
font-weight: normal;
padding: 10px 20px;
margin-top: 40px;
font-family: 'Montserrat', sans-serif;
outline: none;
}
.back-button:hover {
background-color: #fff;
border: 1px solid #999999;
color: #999999;
transition: background-color .3s;
}
</style>
VUEX store :
export const state = () => ({
user: {},
company: {},
companydetails: {},
companycontact: {},
validationcode: {},
})
export const mutations = {
setEmailPassword(state, { user }) {
console.log(user)
state.user.email = user.email
state.user.password = user.password
},
setCompanyEmployeeInsta(state, { company }) {
console.log(company)
state.company.name = company.name
state.company.insta_account = company.insta_account
state.company.selected = company.selected
},
setWebsiteCanalSector(state, { companydetails }) {
console.log(companydetails)
state.companydetails.website_url = companydetails.website_url
state.companydetails.option = companydetails.option
state.companydetails.sector = companydetails.sector
},
setContactServiceInsta(state, { companycontact }) {
console.log(companycontact)
state.companycontact.contact_first_name = companycontact.contact_first_name
state.companycontact.contact_last_name = companycontact.contact_last_name
state.companycontact.contact_service = companycontact.contact_service
},
setValidationCode(state, { validationcode }) {
console.log(validationcode)
state.validationcode.contact_first_name = validationcode.contact_first_name
},
}

styling npm vuejs-paginate component

I am using the npm package https://www.npmjs.com/package/vuejs-paginate
to handle pagination in a vuejs application.
I would like to style this pagination component.
My styling successfully sets the background of page number buttons yellow when the user hovers over them, but fails to set the background of the current page to green. Why?
Here is my component tag with the props.
<paginate
:pageCount="totalPages"
:click-handler="paginateCallback"
:prevText="'Prev'"
:nextText="'Next'"
:containerClass="'pagination'"
class="pagination"
v-model="pageNumber"
></paginate>
And here is the css...
.pagination a {
float: left;
padding: 8px 16px;
text-decoration: none;
border: 1px solid #ddd;
background-color: white;
}
.pagination a.active {
background-color: green;
}
.pagination a:hover:not(.active) {background-color: yellow;}
.pagination a:first-child {
border-top-left-radius: 5px;
border-bottom-left-radius: 5px;
}
.pagination a:last-child {
border-top-right-radius: 5px;
border-bottom-right-radius: 5px;
}
By the way, in case it is relevant information, the application uses bootstrap-vue elsewhere.
Thanks to the first answer below, I was able to resolve this.
Here is the working css after adding the active-class prop to the component...
.pagination li {
float: left;
padding: 8px 16px;
text-decoration: none;
border: 1px solid #ddd;
color: white;
background-color: white;
font-size: 1em;
}
.pagination li.pagination-active {
background-color: green;
}
.pagination li:hover:not(.active) {background-color: yellow;}
Now, however, there is a border around the number of the active page button until the user clicks again anywhere on the page. How can we eliminate this border?
As the documentations says: there is an active class prop that you can set and style that class. see the props in the link above.
<paginate
:pageCount="totalPages"
:click-handler="paginateCallback"
:prevText="'Prev'"
:nextText="'Next'"
:active-class="myActiveBtn"
:containerClass="'pagination'"
class="pagination"
v-model="pageNumber"
></paginate>
style:
.myActiveBtn{
background-color: green;
}

which files are necessary for bootstrap dropdown to work?

I am avoiding including the entire bootstrap source code in my app. All I need at this point is the bootstrap dropdown classes. I am trying to make a simple dropdown that looks like this...
<div class="dropdown">
<a data-toggle="dropdown" class="dropdown-toggle">display dropdown</a>
<ul
class="dropdown-menu dropdown-menu-right dropdown-menu-arrow dropdown-scrollable dropdown-content"
>
<li class="dropdown-item">Option 1</li>
<li class="dropdown-item">Option 2</li>
</ul>
</div>
I copied dropdown.scss from the tabler.io library.
This is all it contains...
.dropdown {
display: inline-block;
color: orange;
}
.dropdown-menu {
box-shadow: $dropdown-box-shadow;
min-width: 12rem;
}
.dropdown-item {
color: $text-muted-dark;
z-index: 1000;
}
.dropdown-menu-arrow {
&:before {
position: absolute;
top: -6px;
left: 12px;
display: inline-block;
border-right: 5px solid transparent;
border-bottom: 5px solid $border-color;
border-left: 5px solid transparent;
border-bottom-color: rgba(0, 0, 0, 0.2);
content: "";
}
&:after {
position: absolute;
top: -5px;
left: 12px;
display: inline-block;
border-right: 5px solid transparent;
border-bottom: 5px solid #fff;
border-left: 5px solid transparent;
content: "";
}
&.dropdown-menu-right {
&:before,
&:after {
left: auto;
right: 12px;
}
}
}
.dropdown-toggle {
user-select: none;
cursor: pointer;
&:after {
vertical-align: 0.155em;
}
&:empty:after {
margin-left: 0;
}
}
.dropdown-icon {
color: $text-muted;
margin-right: 0.5rem;
margin-left: -0.5rem;
width: 1em;
display: inline-block;
text-align: center;
vertical-align: -1px;
}
I know that my code is referencing this stylesheet because the font color is orange and the stylesheet's first rule includes color: orange.
I also know that my code detects the user clicking on the text because I tested by adding #click.prevent="doSomething()" where doSomething() simply console logged a message.The message did indeed print out in the Chrome dev tools console.
However, when I click on the words display dropdown, the dropdown menu does not open.
I understand there are javascript files I may need. Which files are those and how can I make sure my code uses this file? This is a Vuejs app using Nuxt.