Vuetify use v-dialog components inside v-card-actions without causing padding issues - vuejs2

You can see the issue I'm having here:
https://codepen.io/ryanrapini/pen/LYeWZKR?editors=1010
Essentially, I have several dialogs which I have contained into their own custom "vue components" i.e. order-dialog.vue. In an ideal world, I could simply include this <order-dialog> component wherever I need it, and it would render the activator button and then handle all of the dialog state inside the component, so the parent doesn't need to worry.
Unfortunately, if I do this in a v-card-actions section I get spacing issues. Is this a bug with Vuetify or am I doing something wrong?
I thought that by moving the activator out of the <v-dialog> tag it might fix the issue, but it still doesn't unless I break my component up into a v-dialog component and a separate activator, which means I now need to manage the state of the dialog in the parent.
Thanks for any help.

I don't think you are doing something wrong, and I'm not sure that it's a Vuetify bug.
This comes from CSS rule in Vuetify library:
.v-application--is-ltr .v-card__actions>.v-btn.v-btn+.v-btn {
margin-left: 8px;
}
I think the authors assumed that this block should contain only buttons. But in your case (in 2nd and 3rd approach) an output HTML looks like this:
<div class="v-card__actions">
<button class="v-btn ...">
...
</button>
<div class="v-dialog__container"><!----></div>
<button type="button" class="v-btn ...">
...
</button>
<button type="button" class="v-btn ...">
...
</button>
</div>
So v-dialog__container breaks this rule.
You can fix you issue, by example, with an additional rule:
.v-application--is-ltr .v-card__actions>.v-btn:not(:first-child) {
margin-left: 8px !important;
}
Or you can also apply helper classes (ml-2) into specific buttons.

Related

How to properly create a popup component in Vue 3

As part of becoming a better Vue programmer, I am trying to implement a popup similar to Popper with a clean and Vueish architecture. Here is a simple schematic that I came up with:
So basically there is a target component, which is the reference for the popup's position. The popup can be positioned above, below, right and left of the target, therefore I will need to have access to the target element in my popup. Also, the target can be an arbitrary component. It can be a simple button or span, but also something much more complex.
Then there is the popup itself, which will be put into a modal at the end of the body, It contains the actual content. The content again can be an arbitrary component.
I have a working implementation of the popup, but the basic structure seems to be far from perfect. I am using two slots, one for the target element and one for the content.
Here is what I have come up with so far for the template:
<template>
<div ref="targetContainer">
<slot name="target"></slot>
</div>
<teleport to="body">
<div v-show="show" class="modal" ref="modal">
<div ref="popover" class="popover" :style="{top: popoverTop + 'px', left: popoverLeft + 'px'}">
<slot name="content"></slot>
</div>
</div>
</teleport>
</template>
There are several issues with this that I am not really happy with.
Using the popup is not very simple
When using this popup in another component, two <template> tags are rquired. This is ungly and not very intuitive. A very simple use case looks like this:
<modal :show="showPopup" #close="showPopup=false">
<template v-slot:target>
<button #click="showPopup=true"></button>
</template>
<template v-slot:content>
<div>Hello World!</div>
</template>
</modal>
The target is wrapped in another <div>
This is done to get access to the target element, that I need for the layout. In mounted() I am referencing the target element like this:
let targetElement = this.$refs.targetContainer.children[0];
Is this really the best way to do this? I would like to get rid of the wrapping <div> element, which just asks for unintended side effects.
The best solution would be to get rid of one slot and somehow reference the target element in another way because I only need its layout information, it does not have to be rendered inside the popover component.
Can someone point me in the right direction?
Here is my solution, which was inspired by a comment on my question and which I think is worth sharing.
Instead of putting the target element into a slot, I am now passing its ref as a prop, which makes things much cleaner.
The popover component's template now looks like this.
<template>
<teleport to="body">
<div v-show="show" class="modal" ref="modal">
<div ref="popover" class="popover" :style="{top: popoverTop + 'px', left: popoverLeft + 'px'}">
<slot ref="content"></slot>
</div>
</div>
</teleport>
</template>
I has a targetRefprop, so the component can be simply used like this:
<div ref="myTargetElement" #click="isPopupVisible=true">
</div>
<modal :show="isPopupVisible" #close="isPopupVisible=false" targetRef="myTargetElement">
<!-- popup content goes here -->
</modal>
And after mounting I can access the target element like this:
let targetElement = this.$parent.$refs[this.targetRef];
I like this solution a lot. However, ideas, advice or words of caution are still highly welcome.

Cannot catch scroll-event in vue.js component

I have this.
<template>
<md-dialog :md-active.sync="show"
#md-closed="hideModal"
#md-clicked-outside="hideModal"
class="modal-tabs"
#keypress.enter.prevent="handleEnter"
>
<md-dialog-content ref="my-modal" v-on:scroll.native="handleScrolling" #click.native="handleScrolling">
<!--content-->
</md-dialog-content>
</md-dialog>
</template>
and handler function is
handleScrolling(): void {
console.log('scroll is on');
this.$root.$emit('scrollingModal', this.$refs['my-modal'].$el.scrollTop);
}
It easily called by clicking on content but cannot be called by scrolling content. Why? Regards.
In order for v-on:scroll to be triggered the element first needs to by overflowing with a scroll style declared.
Try doing this:
<div style="max-height: 300px; overflow: scroll;" v-on:scroll="handleScrolling()">
<md-dialog-content ref="my-modal" #click.native="handleScrolling">
<!--content-->
</md-dialog-content>
</div>
This should show you a proof of concept. As you scroll inside that div you should see the messages being logged.
This problem solved by wrapping content with md-tabs and md-tab tags. In my case, to achieve scroll event triggering i have changed code above to:
<template>
<md-dialog :md-active.sync="show"
#md-closed="hideModal"
#md-clicked-outside="hideModal"
class="modal-tabs"
#keypress.enter.prevent="handleEnter"
>
<md-dialog-content ref="edit-contact-modal" v-on:scroll.native="handleScrolling">
<md-tabs>
<md-tab><!--here md-tab in fact is first custom div of mine-->
<!--content-->
</md-tab>
</md-tabs>
</md-dialog-content>
</md-dialog>
</template>

Make class !important

In Less is there a simple way to make all attributes in a class !important? My usecase is that I will be dynamically inserting a 'tag' (div) into existing divs that will always have inline styling.
Example:
<div class="text-widget ui-sortable" style="font-size: 5em;>
<div class="tag"><span>Click me to drag widget. My font size should never change</span></div>
<p>I am a text widget that can be dragged. When I am deselected my tag above will be deleted</p>
</div>
So .tag properties need to be !important to avoid getting the text widgets css. Is there a simple way in less to make all properties !important? Something like...
.tag !important {
.... lots of properties that I dont want to add !important to each one.
}
This is in reply to #sazr's question to my comment.
If you think you need to use !important then your CSS is very complex and often has too many top level rules that affect too many things. Sometimes this is because you are trying to create generic CSS that is applied throughout your page, sometimes it is because you are creating rules that have such a large value for specificity that you can't figure out another way to force the style you want on your element.
Learning what specificity is and how it works is the most important thing for a CSS developer. If you don't truly understand that then you are doomed to need !important to resolve issues that elude you.
Look at this chart that I took from here: http://www.standardista.com/css3/css-specificity/
Notice the image associated with !important. It is the nuclear option and should be used as a last resort. Although I use it on every rule for #media print to not have to worry about my printouts.
Using some kind of name spacing with your CSS will help reduce the death spiral that can be caused by too many non-specific selectors or too many selectors that are so specific that you can no longer override those rules.
A select like this:
#page1 .outershell .innershell .form button {
background-color: green;
}
has a specificity of 1,3,1
If you have this layout:
<div id="page1">
<div class="outershell">
<div class="innershell">
<form class="form">
<button>Click me</button>
</form>
</div>
</div>
</div>
And you want to change the button's background color to red then you have to create a new selector with a higher specificity.
This won't work
.form button {
background-color: red;
}
Since it only has a specificity of 0,1,1
#page1 .form button {
background-color: red;
}
This only has a value of 1,1,1
So you need to use two ID selectors, a fourth class selector or a second element selector. Or you can place the exact same selector after the first and then all of your buttons after that declaration will be red.
But that won't change any other buttons to red. So with this layout:
<div id="page1">
<div class="outershell">
<div class="innershell">
<form class="form">
<button>Click me</button>
</form>
</div>
<div class="secondshell">
<button>Not me</button>
</div>
</div>
</div>
the button "Not me" will not be red, or even green.
Things I do
I do not ever us ID selectors unless I must to override existing CSS.
I do not use !important except for #media print and I use it for everything in my print output. That way I know my specificity for the print output and I do not worry about some other CSS selector ruining my printouts.
I avoid deep selectors. Most of my selectors have a specificity value of 0,1,0 to 0,2,0 or 0,1,2
I use attributes for state values and attribute selectors to reduce the amount of JS I need to write, allowing CSS to do the work for me.
BEM to the rescue
OK. Some people don't like BEM. But it has save my life from the complexities of CSS. I have not had a single CSS specificity problem since I started using it, except when dealing with older CSS and even then I find it easy to repair.
BEM is not just about CSS, it is also about formatting your DOM in a sensible way to help the CSS work for you instead of you having to work for it.
Using this DOM:
<style>
.form-box--btn {
background-color: red;
}
</style>
<div>
<div>
<div class="form-box">
<form class="form-box--form">
<button class="form-box--btn">Click me</button>
</form>
</div>
<div class="other-thing">
<button class="form-box--btn">Me too</button>
</div>
</div>
</div>
I KNOW that my two buttons are red. And as long as everyone working on this page agrees to follow the rules we never run into a problem with the button changing color.
I know that is a simplistic example, but reading more about specificity, BEM and name-spacing will tell you much more than I can in this post.
Some light reading
Here are a few links that talk more about specificity, BEM and name spacing:
https://uxengineer.com/css-specificity-avoid-important-css/
http://getbem.com/
https://www.smashingmagazine.com/2007/07/css-specificity-things-you-should-know/
https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity
https://zellwk.com/blog/css-architecture-1/
Make a class for it:
.tagStyle (#bg: #00FF00) {
background: #bg;
/* Other properties */
}
.tag {
.tagStyle() !important;
}

Vue: What is the cleanest way to select a component via CSS?

I have a bar component. It is used like this:
<template>
<div>
<!-- stuff -->
<bar></bar>
<!-- stuff -->
<!-- stuff -->
<!-- stuff -->
<bar></bar>
<!-- stuff -->
<bar></bar>
<!-- stuff -->
</div>
</template>
<style lang="scss" scoped>
#media (max-width: 1300px) {
// this selector doesn't work, but it would be nice if it did
bar {
display: none;
}
}
</style>
I would like to hide the bar elements when the screen is 1300px or narrower. It would be nice if there was a bar element selector, just like there are p and h1 element selectors. However, there doesn't seem to be, and I have to add class="bar" in order to select them.
My question is if there is a cleaner way to select the bar elements.
It wouldn't be good to add the CSS code inside of the bar component because when the bars are used inside of other components, I don't want to hide them at all.
A class seems to be the best way to go. Put it on the root element of the component if you want it to be universal for the component, or only on the component tag if you want it to be specific to that use.
Also, there is no reason you couldn't use a custom tag as the root element of your component; as long as the tag didn't map to a component, it would be left in the DOM, and you could use it for CSS selection. I don't recommend it, though, as I don't think this use case is a good reason for introducing a new tag.
If your component template looked like this, for example:
<template>
<bar-container>
Hi there
</bar-container>
</template>
and you had no bar-container component defined, you would be able to use CSS to select bar-container, which would be the container element for every bar component. But it's just as easy to use <div class="bar-container"> instead.
After better understanding the problem at hand, would this work?
<div class="someClass">
<bar v-bind:width="draw.probability" type="draw" ref="myComponent"></bar>
</div>

Nested Custom Elements in Aurelia not rendering as expected

I have a custom element that defines a general purpose UI widget frame with various bindable default options, a template part for adding some additional 'toolbar' options and general-purpose <content /> for the body.
I then have another custom element for some administrative functionality. The latter element should present itself as a widget, and it too has various template parts.
However, if I try to embed the former widget element into the latter administrative element none of the content gets rendered.
Here's a simplified example:
eg-block (Widget) element
<template>
<div style="padding: 10px; background-color: #bbffff">
<content></content>
</div>
</template>
eg-list (Admin) element
<template>
<require from="./eg-block"></require>
<eg-block>
<div>Start of List</div>
<content></content>
<template replaceable part="list-part">Default List Part</template>
<div>End of List</div>
</eg-block>
</template>
Containing Page
<template>
<require from="./eg-list"></require>
<eg-list>
<template replace-part="list-part">Replaced List Part content</template>
<div>Replaced regular content</div>
</eg-list>
</template>
I was hoping the results of that to be:
<div style="padding: 10px; background-color: #bbffff">
<div>Start of List</div>
<div>Replaced regular content</div>
<div>Replaced List Part content</div>
<div>End of List</div>
</div>
But instead it gives me:
<div style="padding: 10px; background-color: #bbffff">
<div>Start of List</div>
<div>End of List</div>
<div>Default List Part</div>
</div>
So it doesn't render the list's content or replaced template part that is specified in the containing page. But additionally, the default content of the list's template part is actually rendered after the list.
Is this the expected behaviour? And if so, is there any way to retain the use of the widget/block element within the admin/list element but to have it render the way I was hoping?
I'm mostly copy/pasting my answer from this question here, but here goes:
Let me preface this answer by saying that content projection is changing completely (and for the better) in Aurelia RC1. We are moving to slot based content projection to match up with the newest version of the shadow DOM specs. This spec is much more powerful than the selector based setup that Aurelia has current (which is based on an earlier version of the Shadow DOM spec). This is the only breaking change we have planned between now and the full 1.0 of Aurelia.
So everything I'm telling you will be obsolete very soon.
In the meantime, the element in your custom element view needs to be at the root of the template. As to the why Aurelia is acting this way, well it's a bug:-) It has been fixed in the new implementation.
We just released a blog post regarding the new slot implementation, if you'd like to see how things will work.