I am trying to trigger the button when mouse clicked it or a key is pressed and I get confused about communication between components. How should I call pressDown() in KeyButton component from its father component, or is there a better way to implement this function?
Here's my attempt
Container of button
<template>
<key-button :message="'Msg'" :callback="pressKey" ></key-button>
</template>
<script setup>
import KeyButton from "./KeyButton.vue";
import {ref,onMounted} from "vue";
onMounted(()=>{
addEventListener('keypress',(e)=>{
//trigger button
});
})
const pressKey = () => {
//exec when button clicked
}
</script>
KeyButton Component
<template>
<button class="button" :class="{'animate': active}" v-on="{mousedown:pressDown,animationend:triggerAnim}">{{props.message}}</button>
</template>
<script setup>
import {ref,defineProps} from 'vue';
const props = defineProps({
message: String,
callback: Function
})
const active = ref(false);
//Function to trigger button
const pressDown = ()=>{
props.callback();
triggerAnim();
}
const triggerAnim = ()=>{
active.value = !active.value;
}
</script>
<style scoped>
button{
display: flex;
height: 5rem;
width: 5rem;
justify-content: center;
align-items: center;
font-size: 2rem;
color: white;
border-color: deepskyblue;
border-width: 0.15rem;
border-radius: 50%;
background-color: lightskyblue;
margin-bottom: 1rem;
margin-left: 2rem;
outline: none !important;
}
.animate{
animation: zoom 0.2s;
}
#keyframes zoom {
0%{
transform: scale(1);
}
10%{
transform: scale(0.9);
}
100%{
transform: scale(1);
}
}
</style>
You shouldn't pass methods as props in vue as this creates interdependencies among the components and makes them less reusable.
Instead of passing the method you should emit an event from the KeyButton Component on keypress and listen to it in the parent component, like so:
// KeyButton Component
<button #click="$emit('button-pressed')" />
// Parent
<KeyButton #button-pressed="pressKey" />
You should not pass callbacks as props between components. Vue has a pattern to share functions between components: enter link description here, provide/inject pattern.
Although, I suggest you follow the approach Aside gave to you, and work with event handling provided by vue by emitting an event on child component to the parent.
Related
I am creating a custom select menu in svelte and have encountered an issue while trying to edit the transform property of the downward icon for the select menu.
I am creating a form with multiple select menus imported to one parent component. Whilst clicking on the second select menu box used in a parent component file, only the first select menu box's icon gets transformed, while no change occurs in the position of the icon of the second select menu box.
I think that this is because both the imported components(select menu) share the same class during compilation. If my assumption is correct, is there any way to instantiate an instance of the select menu component in the parent component every time the select component is used?
<script>
export let displayText;
let clickCount = 0;
</script>
<div
class="selection"
on:click={() => {
clickCount++;
clickCount == 1
? (document.querySelector(".arrow").style.transform = "rotate(180deg)")
: (document.querySelector(".arrow").style.transform = "rotate(0deg)");
clickCount == 2 ? (clickCount = 0) : (clickCount = clickCount);
}}
>
<span>{displayText}</span>
<div class="arrow" />
</div>
<style>
.selection {
display: flex;
border-radius: 5px;
padding-left: 10px;
padding: 10px;
font-weight: bold;
background-color: #363636;
font-size: 14px;
align-items: center;
cursor: pointer;
}
.selection .arrow {
transition: all 0.5s;
width: 0;
height: 0;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-top: 8px solid white;
margin-left: 10px;
}
</style>
The problem is the document.querySelector(".arrow") that will always find the first element with the class. Svelte has other tools to avoid that and target and manipulate element inside the component without querying the DOM. Here you can use the class: directive
REPL
<script>
export let displayText;
let open = false
</script>
<div class="selection"
on:click={() => open = !open}
>
<span>{displayText} - open = {open}</span>
<div class="arrow"
class:rotated={open}
/>
</div>
<style>
.selection {
...
}
.selection .arrow {
...
}
.rotated {
transform: rotate(180deg);
}
</style>
I'm trying to hide the slide thumb. I tried to do it without using a library but then I think that it should be better to use material-ui because maybe it would be easier but I'm here asking help.
Here is my code:
import * as React from "react";
import Slider from "#mui/material/Slider";
import "./style.css";
export default function ContinuousSlider() {
const [value, setValue] = React.useState(30);
const handleChange = (event, newValue) => {
setValue(newValue);
};
return (
<Slider
aria-label="Volume"
value={value}
onChange={handleChange}
focusVisible={false}
/>
);
}
style:
.MuiSlider-thumb {
background-color: orange;
box-shadow: none;
width: 0px;
height: 0px;
}
.MuiSlider-rail {
background-color: orange;
border: none;
}
.MuiSlider-track {
background-color: red;
border: none;
}
.MuiSlider-rail {
background-color: green;
}
working code here
result:
on focus
I was able to hide the main thumb but not the "secondary thumb". I don't know how to call it, the light blue one that appears clicking on the thumb.
How can I remove it?
I want the following style always, even when user drag the thumb:
You could add style override for hover (pseudo-class) and active state (for MUI it is .Mui-active)
.MuiSlider-thumb:is(:hover, .Mui-active) {
display: none;
}
Demo
I'm using React and trying to fetch some of my anime into the home banner using Swiper
I don't know why when I refresh my page, it'll only render at half of the swiper.
Here's how it display:
However, if I press the next or back button, it'll display normally again.
Here's my code in my Home Component:
import { useState, useEffect } from "react"
import ReactHlsPlayer from 'react-hls-player';
import './home.css'
import { supabase } from '../../supabaseClient'
import { Swiper, SwiperSlide } from 'swiper/react'
import SwiperCore, { Pagination,Navigation } from 'swiper';
import 'swiper/css'
SwiperCore.use([Pagination,Navigation]);
function Home(){
useEffect(async ()=>{
fetchAnime()
}, [])
const [animeDetail, setAnimeDetail] = useState([])
async function fetchAnime() {
const { data } = await supabase
.from ('anime')
.select ()
setAnimeDetail(data)
}
return (
<>
<div className="spacer">
</div>
<div className="home-section">
<h2>Home Page</h2>
<Swiper
centeredSlides={true}
slidesPerView={7}
spaceBetween={10}
loop={true}
pagination={false}
navigation={true} className="mySwiper">
{animeDetail.map((element, i)=>
(
<SwiperSlide key = {i}><img src={element.anime_image}/></SwiperSlide>
)
)}
</Swiper>
</div>
</>
)
}
export default Home
And here's my Home CSS, sorry if it's a bit messy, I'm still trying it here and there, but I'm stuck.
.swiper {
width: 100%;
height: 100%;
margin-left: auto;
margin-right: auto;
}
.swiper-wrapper{
width: 100%
}
.swiper-slide {
text-align: center;
font-size: 18px;
display: flex;
justify-content: center;
align-items: center;
max-width: 280px;
}
.swiper-slide img {
display: block;
box-sizing: border-box;
border: none;
max-height: 350px;
min-height: 350px;
-o-object-fit: cover;
object-fit: cover;
transition: all .3s ease;
opacity: 0.5
}
.swiper-slide-active {
transform: scale(1.2);
z-index: 2
}
.swiper-slide-active img{
transition: all .3 ease;
opacity: 1
}
And if someone figures it out, please help me a bit, I tried to whenever the item in the middle is in active, it'll pop out bigger, but I can't do it with transform: scale(number) - I have tried it (It does get bigger when it's active but it doesn't display bigger at the height, I haven't figured it out some ways at the moment)
you have to set the initialSlide prop on the Swiper element to an index of the slide in the middle. so that the slider starts from there.
Also in this case, you can set the centeredSlides prop to true as the active slide remains in the middle.
Here's a possible fix for your issue and for everyone else that comes here looking for answers.
Use conditional rendering of the Swiper component, something like this:
{animeDetail.length>0 && <Swiper
centeredSlides={true}
slidesPerView={7}
spaceBetween={10}
loop={true}
pagination={false}
navigation={true} className="mySwiper">
{animeDetail.map((element, i)=>
(
<SwiperSlide key = {i}><img src={element.anime_image}/></SwiperSlide>
)
)}
</Swiper>}
I have a styled-component that receives props to determine what animation to use. This is controlling an arrow icon that when active rotates 'open' and when inactive remains 'closed'.
Here is what the styled-component and two keyframes animations look like:
const rotate_down = keyframes`
from {
transform: rotate(0deg);
}
to {
transform: rotate(90deg);
}
`;
const rotate_up = keyframes`
from {
transform: rotate(90deg);
}
to {
transform: rotate(0deg);
}
`;
const OpenUserSettings = styled.div`
cursor: pointer;
width: 20px;
height: 20px;
transition: 0.3s;
animation: ${props => (props.rotate ? rotate_down : rotate_up)} 0.5s ease
forwards;
margin-top: 2px;
box-sizing: border-box;
display: flex;
justify-content: center;
align-items: center;
& > img {
width: 5px;
}
`
The passed in rotate prop is a boolean value that is toggled via an onClick handler in the React component:
<OpenUserSettings
rotate={arrowDown}
onClick={() => {
this.setState(prevState => ({
arrowDown: !prevState.arrowDown
}));
}}
>
<img src={OpenArrow} alt="menu drop down arrow" />
</OpenUserSettings>
This works, and when the arrow is clicked, rotate prop is passed into OpenUserSettings and it successfully toggles between the rotate_up and rotate_down keyframes.
Now my problem is that when the React component first mounts, the arrowDown default is set to false meaning that the rotate prop is going to be false. This causes the styled-component to set the animation to rotate_up the first time mounting. I figured this would be hard to visualize so check out this to see what I am describing:
You can see when the page is refreshed the rotate_up animation is firing very quickly. I need the arrow to stay closed, but I do not need the rotate_up animation to fire when first loaded to close it. Is this a situation for something like react-transition-group where I can control the initial enter or is it something that can be handled with logic?
My first contribution to stack overflow, how exciting!
To assure the animation does not render on mounting, initialise your state with something else as true/false, I remember setting my state to null initially.
In your styled component you can pass null / true / false as a prop and define a function outside your component that performs checks on the null / true / false. (I used TypeScript, so don't copy the types)
function showAnimationOrNot(status: boolean | null) {
if (status === true) {
return `animation: nameAnimationIn 1s ease forwards;`
} else if (status === false) {
return `animation: nameAnimationOut 1s ease-out forwards;`
} else if (status === null) {
return ""
}
}
In your styled component simply add a line:
${(props: PropsDisplay) => show(props.show)};
This is how I did it, I am sure there are more elegant ways, but it did the trick of preforming the animation on mounting.
In return if somebody knows how to get styled component syntax highlighting inside the return of functions like the one I used here (from the example above):
return `animation: comeout 1s ease-out forwards;`
Do let me know! looking for that! Thank you and good luck!
Keep on animating! :)
I think you'll have an easier time with a CSS transform/transition here, rather than an animation. Here's the code:
const rotateDeg = props.rotate ? '90deg' : '0deg';
const OpenUserSettings = styled.div`
cursor: pointer;
width: 20px;
height: 20px;
transition: all 0.3s ease;
transform: rotate(${rotateDeg});
margin-top: 2px;
box-sizing: border-box;
display: flex;
justify-content: center;
align-items: center;
& > img {
width: 5px;
}
`
I didn't quite understand what position you wanted the arrow in, so you might need to flop 0deg and 90deg and I think it might actually be -90deg, but hopefully this helps.
Edit
Your question was actually how to stop an animation. You can pause a css animation with animation-play-state. In this case you could have had the component mount with animation-play-state: paused; and then added an additional prop to change that to running on first click, but that seems unnecessarily complicated IMO.
More on animation-play-state.
I use this package: https://github.com/greyby/vue-spinner for showing a spinner.
<template>
<pulse-loader :loading="loading" :color="color" :size="size"></pulse-loader>
</template>
<script>
import { PulseLoader } from 'vue-spinner/dist/vue-spinner.min.js';
export default {
components: { PulseLoader },
data () {
return {
loading: true,
color: "black",
size: "10"
}
}
}
</script>
For some reason the spinner is not showing???!?! There are no erros in my console!
You should not be importing from the dist folder.
Please, try importing the vue component source, doing as shown in the documentation:
import PulseLoader from 'vue-spinner/src/PulseLoader.vue'
Docs: https://github.com/greyby/vue-spinner#es6
UPDATE:
Considering Browserify restriction on applying transforms in files inside node_modules, then you could try the code snippet provided in the mentioned GitHub issue:
var PulseLoader = require('vue-spinner/dist/vue-spinner.min').PulseLoader;
The website I was working on had a custom CSS-file. It was missing the correct styles. Possibly because it was for an older version of Bootstrap.
Make sure that there is a definition for .spinner-border anywhere in your styles. If not, find out why not and fix it.
I have copied the style from the source-code of the Vue examples page for a quick fix.
#keyframes spinner-border {
to { transform: rotate(360deg); }
}
.spinner-border {
display: inline-block;
width: 2rem;
height: 2rem;
vertical-align: text-bottom;
border: .25em solid currentColor;
border-right-color: transparent;
border-radius: 50%;
-webkit-animation: spinner-border .75s linear infinite;
animation: spinner-border .75s linear infinite;
}