My v-select components should have a fixed width (60px), they fit in a table cell, and I want to prevent them from changing the width after value selected.
They change the width and drop-down arrow moves to the right after selection, so if there a way to decrease the size of an icon or its padding/margin it might be helpful.
Don't really know how to get props of this arrow and how this calls.
Here is the reproducible
https://codesandbox.io/s/competent-dew-eixq2?file=/src/components/Playground.vue
EDIT: Add snippet
<!DOCTYPE html>
<html>
<head>
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/#mdi/font#4.x/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify#2.0.1/dist/vuetify.min.css" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
<style>
.select {
max-width: 60px;
max-height: 60px;
font-size: 11px;
}
.col {
max-width: 60px;
max-height: 60px;
}
</style>
</head>
<body>
<div id="app">
<v-app>
<v-row>
<div class="col" v-for="col in cols" :key="col">
<v-select class="select" :items="variants" item-value="name" item-text="name" label="" dense outlined hide-details single-line v-model="selected">
</v-select>
</div>
</v-row>
</v-app>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.10/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.0.1/dist/vuetify.js"></script>
<script>
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: {
selected: "",
cols: [1, 2, 3, 4, 5],
variants: [{
id: 0,
name: ""
},
{
id: 1,
name: "1:0"
},
{
id: 2,
name: "0:1"
},
{
id: 3,
name: "1:0 B"
},
{
id: 4,
name: "0:1 B"
},
{
id: 6,
name: "1:0 R"
},
{
id: 7,
name: "0:1 R"
},
{
id: 8,
name: "1:0 F"
},
{
id: 9,
name: "0:1 F"
},
],
},
})
</script>
</body>
</html>
The basic problem is that v-select has some styling (specifically padding and margin) that does not work very well at small widths.
This is the innerHTML of the rendered v-select, with the styles that need reducing
<div class="select...">
<div class="v-input__control">
<div role="button" class="v-input__slot"> <!-- padding-right: 12px; -->
<div class="v-select__slot">
<div class="v-select__selections">
<div class="v-select__selection--comma"> <!-- margin-right: 4px; -->
1:0 B
</div>
</div>
<div class="v-input__append-inner"> <!-- padding-left: 4px; -->
<div class="v-input__icon v-input__icon--append"> <!-- width: 24px; min-width: 24px;-->
<i aria-hidden="true" class="v-icon..."></i>
</div>
</div>
</div>
</div>
</div>
</div>
Aside from those innerHTML changes, you want a fixed width of 60px per column so change
<style>
.select {
max-width: 60px;
...
}
to
<style>
.select {
width: 60px;
...
}
Adjusting Vuetify inner styles
Looking at the Vuetify issues around styling, there's suggestions of using un-scoped style blocks, or deep-scoped style blocks, but neither worked for me. Vuetify have said they are working on a revamp of the way styles are applied to overcome the issues.
Fortunately Vue itself has tools to do it in javascript.
These are the key steps
add a reference to the v-select so that it's accessible in javascript
add a change handler to the v-select so that elements can be re-styled when the user selects something
define the style adjustments in an object so you can easily tweak them
add a method to apply the styles
call the method in mounted() and the v-select #change handler
use Vue.nextTick() to allow Vuetify to style first, then our custom styles are applied
Here's the adjusted code snippet. I put in some severely minimal padding and margins, and maximized the space for the selected value (to avoid text wrapping). You may want to play with the styles as I'm not sure I understood all of the requirements.
<!DOCTYPE html>
<html>
<head>
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/#mdi/font#4.x/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify#2.0.1/dist/vuetify.min.css" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
<style>
.select {
width: 60px;
max-height: 60px;
font-size: 11px;
}
.col {
max-width: 60px;
max-height: 60px;
}
</style>
</head>
<body>
<div id="app">
<v-app>
<v-row>
<div class="col" v-for="col in cols" :key="col">
<v-select ref="select" #change="applyCustomStyles"
class="select" :items="variants" item-value="name" item-text="name" label="" dense outlined hide-details single-line v-model="selected">
</v-select>
</div>
</v-row>
</v-app>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.10/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.0.1/dist/vuetify.js"></script>
<script>
const customStyles = {
".v-input__slot": {
padding: "0 0 0 4px",
},
".v-select__selections": {
width: "27px",
},
".v-select__selection--comma": {
margin: "7px 0 7px 0",
},
".v-input__append-inner": {
"padding-left": "0",
},
".v-input__icon": {
width: "14px",
"min-width": "14px",
},
};
new Vue({
el: '#app',
vuetify: new Vuetify(),
mounted() {
this.applyCustomStyles();
},
methods: {
applyCustomStyles() {
Vue.nextTick(() => {
this.$refs.select.forEach((vSelect) => {
Object.entries(customStyles).forEach(([selector, styles]) => {
Object.entries(styles).forEach(([style, value]) => {
vSelect.$el.querySelector(selector).style[style] = value;
});
});
});
});
},
},
data: {
selected: "",
cols: [1, 2, 3, 4, 5],
variants: [{
id: 0,
name: ""
},
{
id: 1,
name: "1:0"
},
{
id: 2,
name: "0:1"
},
{
id: 3,
name: "1:0 B"
},
{
id: 4,
name: "0:1 B"
},
{
id: 6,
name: "1:0 R"
},
{
id: 7,
name: "0:1 R"
},
{
id: 8,
name: "1:0 F"
},
{
id: 9,
name: "0:1 F"
},
],
},
})
</script>
</body>
</html>
Why not a <style> block?
There are suggestions of using an un-scoped style block to override the Vuetify styles.
For example,
<style>
.select .v-input__slot {
padding-right: 4px
}
...
</style>
The problem is the Vuetify styles are getting applied after those declared on the component.
You can do it by applying greater specificity than Vuetify uses, e.g.
<style>
.select.v-text-field.v-text-field--enclosed:not(.v-text-field--rounded)>.v-input__control>.v-input__slot {
padding-right: 4px
}
</style>
gives you this
so you can hunt out all the existing places where a change is necessary and copy the Vuetify selector.
The problems might occur when a new version of Vuetify is used, or the shape of the component is changed. To me, the javascript solution looks more manageable.
1. If you don't care about the appended icon, you can remove it with the use of append-icon prop (pass empty value):
<v-select
class="select"
...
append-icon=""
></v-select>
2. You can override the slot for the icon with your own content:
<v-select
class="select"
...
>
<template #append>
<div class="my-custom-icon">...</div>
</template>
</v-select>
Then add style to your my-custom-icon class to make it appear in one place:
.my-custom-icon {
position: absolute;
left: ...;
right: ...;
}
3. Make use of overflow: hidden property:
<style> // don't add "scoped" attribute, otherwise the style won't be applied
.select .v-input__control {
overflow: hidden;
}
</style>
Hello I have facing same problem but as I am using custom made select from vuetify v-select i can not overwrite CSS because it will change drop-down in whole project and this will be the case for all devloper working on projects
So simple solution will be make container div and give it min-width max width and width as you see fit it will solve your size change issue with table cell
It worked for me as i set my drop-down min width to max-width of container so it will be same same width
Ex
<div :class='container'>
<v-select />
</div>
<style>
. container {
max-width: 9rem // in my case it's the min width for my default selected option
}
</style>
Related
I use transtion in vue2. I added transition in css. Vue template show two box. One way use v-for and array, another way is use variable. btn2 is effective but btn1 not.
<style lang="sass">
.item
width: 120px
height: 120px
background-color: bisque
transition: margin-left 500ms
</style>
<template>
<div>
<div class="item" v-for="item in list" :key="item.index" :style="{marginLeft: item.index + 'px'}">{{ item.value }}</div>
<div class="item" :style="{marginLeft: left + 'px'}">123</div>
<button #click="addone">btn1</button>
<button #click="addtwo">btn2</button>
</div>
</template>
<script>
export default {
name: 'Heap',
data() {
return {
left: 100,
list: [
{
value: 12,
index: 10
}
]
}
},
methods: {
addone() {
this.list[0]['index']+=10
},
addtwo() {
this.left+=10
}
}
}
</script>
You are using the code :key="item.index" on your first div. Your code then updates that same index.
When a key's value changes, the component it is attached to re-renders. You are not seeing the animation occur because instead of dynamically incrementing the CSS, you are effectively just re-rendering the element with the new CSS.
The purpose of a key is to help Vue keep track of the identity of a given node in a list. It lets Vue know which nodes it can keep and patch up and which ones need to be rendered again.
You should use a static, non-changing value as a key where possible. In the following example I have added an id property to your object and used that as the key.
<style lang="sass">
.item
width: 120px
height: 120px
background-color: bisque
transition: margin-left 500ms
</style>
<template>
<div>
<div
v-for="item in list"
:key="item.id"
class="item"
:style="{marginLeft: item.index.toString() + 'px'}"
>
{{ item.value }}
</div>
<div class="item" :style="{marginLeft: left.toString() + 'px'}">123</div>
<button #click="addone">btn1</button>
<button #click="addtwo">btn2</button>
</div>
</template>
<script>
export default {
name: 'Example',
data() {
return {
left: 100,
list: [
{
id: '1',
value: 12,
index: 10,
},
],
};
},
methods: {
addone() {
this.list[0].index += 10;
},
addtwo() {
this.left += 10;
},
},
};
</script>
I'm trying to change the background-color of a table row that was generated by Buefy.
I don't understand why I'm unable to change the color of the table row, but I can change its children color.
I tried both ways, setting the whole class name or just the class name of the element that I want:
.table.is-hoverable tbody tr.detail:not(.is-selected):hover {
background-color: rgb(218, 11, 11) !important;
}
.detail {
background-color: rgb(218, 11, 11) !important;
}
I can, for example change the color of an inner element:
.media {
background-color: yellow !important;
}
Full code
<template>
<section class="hero">
<div class="hero-body">
<div class="container">
<b-table
:data="downloads"
ref="table"
:loading="isLoading"
hoverable
detailed
detail-key="version"
selectable
#select="toggle"
>
<template slot-scope="props">
<b-table-column
class="is-unselectable"
cell-class="has-pointer-cursor"
field="version"
label="Version"
width="70"
>
<span class="tag is-info">{{ props.row.version }}</span>
</b-table-column>
<b-table-column
class="is-unselectable"
cell-class="has-pointer-cursor"
field="download_count"
label="Download Count"
sortable
numeric
>{{ props.row.download_count.toLocaleString() }}</b-table-column>
<b-table-column
class="is-unselectable"
cell-class="has-pointer-cursor"
field="release_date"
label="Release Date"
sortable
centered
>{{props.row.release_date ? new Date(props.row.release_date).toLocaleDateString() : "unknown"}}</b-table-column>
</template>
<template slot="empty">
<section v-if="!isLoading" class="section">
<div class="content has-text-grey has-text-centered">
<p>Looks like it was not possible to load the data.</p>
</div>
</section>
</template>
<template slot="detail" slot-scope="props">
<article class="media">
<div class="media-content">Some text.
<hr>Some other text.
</div>
</article>
</template>
<template slot="footer">
<div v-if="!isLoading" class="has-text-right">This is a footer.</div>
</template>
</b-table>
</div>
</div>
</section>
</template>
<script>
export default {
name: "HelloWorld",
data() {
return {
downloads: [],
isLoading: true
};
},
mounted() {
this.downloads = [
{ version: "1.5", download_count: 500, release_date: new Date() },
{ version: "1.4", download_count: 400, release_date: new Date() },
{ version: "1.3", download_count: 300, release_date: new Date() },
{ version: "1.2", download_count: 200, release_date: new Date() },
{ version: "1.1", download_count: 100, release_date: new Date() }
];
this.isLoading = false;
},
methods: {
toggle(row) {
this.$refs.table.toggleDetails(row);
}
}
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style lang="scss" scoped>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
//This is how I expect it should work.
.table.is-hoverable tbody tr.detail:not(.is-selected):hover {
background-color: rgb(218, 11, 11) !important;
}
//Applying the property to the
.detail {
background-color: rgb(218, 11, 11) !important;
}
.media {
background-color: yellow !important;
}
</style>
Reproduction link:
https://codesandbox.io/s/misty-sea-bmyvc?file=/src/components/HelloWorld.vue
Your CSS is scoped. In the end those classes are assorted with a [data-xxx] selector so they are applied only on this component.
.detail[data-xxx] won't override the lib's CSS, you need just .detail
So put this CSS code into a separated simple <style lang="scss"> and let your component's own CSS in <style lang="scss" scoped>
In the following example, is there any way to use CSS to truncate the text in the cell (rather than have it wrap around or overflow)? Ideally, the CSS should look something like this:
.truncate {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
But as you can see below, this just causes the cell to consume all the space:
<!DOCTYPE html>
<html>
<head>
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/#mdi/font#4.x/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.min.css" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
<style>
.truncate {
display: inline-block;
/* width: 200px; */
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
</style>
</head>
<body>
<div id="app">
<v-app>
<v-content>
<v-data-table :headers="headers" :items="items">
<template v-slot:item.name="{ item }">
<span class="truncate">{{ item.name}}</span>
</template>
</v-data-table>
</v-content>
</v-app>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.js"></script>
<script>
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: {
headers: [
{ text: 'Name', value: 'name', width: "75%" },
{ text: 'Calories', value: 'calories', width: "25%" },
],
items: [
{ name: 'Frozen Yogurt', calories: 159, },
{ name: 'Ice cream sandwich with a really, really, really long name that keeps going on and on and on forever so there is no space left', calories: 237, },
{ name: 'Eclair', calories: 262, },
], }
})
</script>
</body>
</html>
It is possible to achieve the desired effect by specifying a fixed width in pixels, but I would like the table and its columns to be responsive.
If you apply the truncating to the td instead, and applying the magic hack max-width: 1px you will get your desired result.
In the example below, in order to apply the class to the td you have to use the item slot and create the row yourself.
<!DOCTYPE html>
<html>
<head>
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/#mdi/font#4.x/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.min.css" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
<style>
.truncate {
max-width: 1px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
</style>
</head>
<body>
<div id="app">
<v-app>
<v-content>
<v-data-table :headers="headers" :items="items">
<template v-slot:item="{ item }">
<tr>
<td class="truncate">{{ item.name}}</td>
<td>{{ item.calories}}</td>
</tr>
</template>
</v-data-table>
</v-content>
</v-app>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.js"></script>
<script>
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: {
headers: [{
text: 'Name',
value: 'name',
width: "75%"
},
{
text: 'Calories',
value: 'calories',
width: "25%"
},
],
items: [{
name: 'Frozen Yogurt',
calories: 159,
},
{
name: 'Ice cream sandwich with a really, really, really long name that keeps going on and on and on forever so there is no space left',
calories: 237,
},
{
name: 'Eclair',
calories: 262,
},
],
}
})
</script>
</body>
</html>
It worked for me by setting the css max-with value to a responsive vw value:
.truncate {
max-width: 50vw;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
Your code works if you just change the SPAN to a DIV and uncomment the width in the css.
<!DOCTYPE html>
<html>
<head>
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/#mdi/font#4.x/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.min.css" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
<style>
.truncate {
display: inline-block;
width: 200px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
</style>
</head>
<body>
<div id="app">
<v-app>
<v-content>
<v-data-table :headers="headers" :items="items">
<template v-slot:item.name="{ item }">
<div class="truncate">{{ item.name}}</div>
</template>
</v-data-table>
</v-content>
</v-app>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.js"></script>
<script>
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: {
headers: [
{ text: 'Name', value: 'name', width: "75%" },
{ text: 'Calories', value: 'calories', width: "25%" },
],
items: [
{ name: 'Frozen Yogurt', calories: 159, },
{ name: 'Ice cream sandwich with a really, really, really long name that keeps going on and on and on forever so there is no space left', calories: 237, },
{ name: 'Eclair', calories: 262, },
], }
})
</script>
</body>
</html>
I have the following VUE component:
<template>
<div>
<div class="bottom-footer">
{{msg}}
</div>
</div>
</template>
<script>
export default {
name: 'LayoutFooter',
data () {
return {
msg: 'my test'
}
},
mounted () {
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.bottom-footer {
height: 200px;
background-color: #A7BFE8;
}
</scoped>
VUE is completely ignoring my scoped CSS. When page is rendered its simply not applied. There are no console errors. Ive tried removing the scoped attribute and its still ignored. Any ideas why VUE is doing this?
<style scoped>
.bottom-footer {
height: 200px;
background-color: #A7BFE8;
}
</style>
you need to close style
I'm trying to put a highchart in a flexbox container. The chart grows with the container just fine, but it doesn't shrink when the container shrinks.
My project uses angularJS and highcharts-ng, although I'm guessing the problem lies with flexbox itself. It's the same in Chrome and IE at least.
Here's a plunkr that illustrates the issue. If you open the embedded view you'll notice when you make the window wider, both charts reflow to fit. But when you make it narrower, the top chart (in a flexbox), remains unchanged.
I put in the reflow button to broadcast the reflow event for highcharts-ng, but it seems to have no effect.
I'm looking for a workaround or any solution that will still let me use flexbox.
Below is the code from Plunkr. It is based on another plunk by the author of highcharts-ng that solved a similar issue for bootstrap containers.
index.html
<!DOCTYPE html>
<html ng-app="highChartTest">
<head>
<link data-require="bootstrap#3.3.2" data-semver="3.3.2" rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css" />
<link rel="stylesheet" href="style.css" />
</head>
<body ng-controller="MyHighChart as c">
<div class="page-container">
<div class="side-panel">
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li><button ng-click="onReflowButtonPressed()" class="autocompare">Reflow</button></li>
</ul>
</div>
<div class="main-panel">
<highchart id="highchart01" config="c.config" height="300"></highchart>
</div>
</div>
<highchart id="highchart02" config="c.config" height="300"></highchart>
<script data-require="jquery#2.1.3" data-semver="2.1.3" src="http://code.jquery.com/jquery-2.1.3.min.js"></script>
<script data-require="angular.js#1.3.15" data-semver="1.3.15" src="https://code.angularjs.org/1.3.15/angular.js"></script>
<script data-require="ui-bootstrap-tpls-0.12.0.min.js#*" data-semver="0.12.0" src="https://rawgit.com/angular-ui/bootstrap/gh-pages/ui-bootstrap-tpls-0.12.0.min.js"></script>
<script data-require="highcharts#4.0.1" data-semver="4.0.1" src="//cdnjs.cloudflare.com/ajax/libs/highcharts/4.0.1/highcharts.js"></script>
<script data-require="highcharts-ng#0.0.9" data-semver="0.0.9" src="https://rawgit.com/pablojim/highcharts-ng/master/dist/highcharts-ng.min.js"></script>
<script src="app.js"></script>
</body>
</html>
style.css
.page-container {
margin: 20px;
border: 1px solid black;
display: flex;
}
.side-panel {
width: 200px;
flex-shrink: 0;
background-color: #ddd;
}
.main-panel {
margin: 10px;
padding: 20px;
flex-grow: 1;
border: 1px solid blue;
}
.highcharts-container {
width: 100%;
border: 1px solid red;
}
app.js
(function() {
angular.module('highChartTest', ['ui.bootstrap', 'highcharts-ng'])
.controller('MyHighChart', ['$scope', '$timeout', MyHighChart]);
function MyHighChart($scope, $timeout) {
$scope.onReflowButtonPressed = function(){
console.log("broadcasting reflow");
$scope.$broadcast('highchartsng.reflow');
}
var chartId = 'yieldColumns01';
this.widget = {
chartId: chartId,
cols: '12',
title: 'Reflow Test'
};
this.config = {
options: {
chart: { type: 'column' },
legend: { enabled: false},
},
title: { enabled: false, text: ''},
xAxis: { categories: ['A', 'B', 'C', 'D', 'E', ] },
yAxis: { max: 100},
series: [{
name: '2014',
data: [90.9, 66.1, 55.0, 53.2, 51.2]
}],
size: { height: '300' },
// function to trigger reflow in bootstrap containers
// see: http://jsfiddle.net/pgbc988d/ and https://github.com/pablojim/highcharts-ng/issues/211
func: function(chart) {
$timeout(function() {
chart.reflow();
// //The below is an event that will trigger all instances of charts to reflow
//$scope.$broadcast('highchartsng.reflow');
}, 0);
}
};
}
})();
You can either set a non-relative width or flex-basis on the .main-panel container to help Highcharts figure out the parent element's dimensions.
I've also removed the width: 100% on .highcharts-container since it wasn't doing anything.
The original Plunkr works on Safari 9.0.1, so this could be due to differences in how browsers implement the flexbox spec (e.g. related issue with width/height: 100%)