Is there a component in Framework7 Vue for an Autocomplete? - vue-component

Framework7 Vue
The linked to documentation does not list a component for an Autocomplete.
Is there a way of using the existing components to generate an Autocomplete in Vue?
Rendered HTML that is required for an Autocomplete:
<ul>
<li>
<a href="#" id="autocomplete-standalone" class="item-link item-content autocomplete-opener">
<input type="hidden">
<div class="item-inner">
<div class="item-title">Fruit</div>
<div class="item-after"></div>
</div>
</a>
</li>
</ul>

Maybe that's it. I still work.
<template lang='pug'>
f7-page
f7-navbar(title='About', back-link='Back', sliding='')
f7-block-title Multiple Values Dropdown Autocomplete
.list-block
ul
li
a#autocomplete-standalone-multiple.item-link.item-content.autocomplete-opener(href='#')
input(type='hidden')
.item-inner
.item-title Favorite Fruite
.item-after
</template>
<script>
export default {
mounted() {
const myApp = this.$f7;
const $$ = this.$$;
// Fruits data demo array
const fruits = 'Apple Apricot Avocado Banana Melon Orange Peach Pear Pineapple'.split(
' '
);
// Multiple Values Dropdown Autocomplete
myApp.autocomplete({
openIn: 'page', //open in page
opener: $$('#autocomplete-standalone-multiple'), //link that opens autocomplete
multiple: true, //allow multiple values
source: function(autocomplete, query, render) {
var results = [];
if (query.length === 0) {
render(results);
return;
}
// Find matched items
for (var i = 0; i < fruits.length; i++) {
if (fruits[i].toLowerCase().indexOf(query.toLowerCase()) >= 0)
results.push(fruits[i]);
}
// Render items by passing array with result items
render(results);
},
onChange: function(autocomplete, value) {
// Add item text value to item-after
$$('#autocomplete-standalone-multiple')
.find('.item-after')
.text(value.join(', '));
// Add item value to input value
$$('#autocomplete-standalone-multiple')
.find('input')
.val(value.join(', '));
}
});
}
};
</script>

The autocomplete property of F7-Vue f7-list-input & f7-input (search for autocomplete) might give you a start. Using the F7 autocomplete property configuration.
You might also want to look into smart-select to give you some more mobile style of an autocomplete replacement.

Related

Adding a hover transition in VueJS

I have this VueJS component that displays a star-shaped button
<template >
<div id="tenseFav">
<button>
<img :src="imgForFavButton">
</button>
</div>
</template>
The computed property imgForFavButton returns a different image depending on if the item is in the list of favorites or not.
computed: {
imgForFavButton() {
const isFav = this.favs.has(this.id);
return isFav ? starOn : starOff;
},
},
I want to add animation on hover, so the image is the one with the yellow star and maybe some transition effects. I am not sure if I have to do this on CSS or VueJS.
On VueJS, I have no event to control the hover natively so I have to use something like:
<template>
<div id="tenseFav">
<button #click="emisor">
<img
#mouseover="hover = true"
#mouseleave="hover = false"
:src="imgForFavButton">
</button>
</div>
</template>
and then in the computed property use an if:
computed: {
imgForFavButton() {
if (this.hover === true) {
return starOn;
}
const isFav = this.favs.has(this.id);
return isFav ? starOn : starOff;
},
},
This works but it looks a bit hacky.
Is there a better way in 2022 for this kind of thing?
I feel that I will face a lot of problems trying to add a transition effect to this.

Add color on every button Click React js

Hello guys I currently have a buttons like category. I want that when I click a button it will have a color, and when I click it again it will turn to it's original color which is white. When I click 2 button both will have dark color, then click again to remove single color.
this is my div when I'm adding a the category id
<div className={classes.scrollMenu}>
{categories.map((category) => {
return (
<>
<Button
key={category._id}
className={classes.button}
onClick={(e) => {
let values = {
price: [],
category: [category._id],
}
}}
>
{category.name}
</Button>
</>
)
})}
</div>
This is the image that when I click single button it will color one button.
Thank you
code Solution: https://codesandbox.io/s/stoic-meadow-y5cei?file=/src/App.js
App.js
import "./styles.css";
import React, { useState } from "react";
export default function App() {
let categories = ["one", "two", "three"];
const [activeFilter, setActiveFilter] = useState(["one"]);
const categoryOnClick = (category) => {
activeFilter.includes(category)
? removeCategory(category)
: setCategory(category);
};
const setCategory = (category) => {
setActiveFilter([...activeFilter, category]);
};
const removeCategory = (category) => {
const index = activeFilter.findIndex((cat) => cat === category);
activeFilter.splice(index, 1);
setActiveFilter([...activeFilter]);
};
return (
<div className="chip-list my-3">
{categories.map((category, index) => {
return (
<button
key={index}
className={`${activeFilter.includes(category) ? "active" : ""}`}
onClick={() => categoryOnClick(category)}
>
<span>{category}</span>
</button>
);
})}
</div>
);
}
css
.active {
background-color: black;
color: white;
}
check if this solution works for you
used useState hook to hold the state of buttons which you will select
.active class will apply to the button which is selected
On click of that button we will check if the button is already selected or not if selected removeCategory() function run
or if button is not selected then setCategory() function will run and it will update the state
if you need clarification please let me know thanks
Few tips to start with:
Fragment is unnecessary when wrapping single DOM element
Inline function initialisation inside a render is a bad thing. On each new re-render, it allocates extra client memory to newly initialised function. That means, for every map object you will have that many functions, that gets newly created and referenced on each reload
You can easily go with single line return statement of arrow function here. () => <hi> instead of () => { return <hi> }
As for solutions, there are quite a few ways to change button colour during execution. I will suggest the most simple (in my opinion) way to do it. Just have classname variable, then add subclass that will style button accordingly.
Example:
By default it has class name of .button, after click you simply add styling and it ends up having .button .button--red, all is left to do, declaration in css.
.button {
style button here
. . .
add additional stylings here
. . .
&.button--red { color: red }
}
As for how handler should look like, if that is what you asking. Button could be used in your new component let's say, named StyledButton or ColourfulButton that will have internal state to handle what kind of colour is represented.

Removing a class from "untoggled" items and assigning it to selected item in React

I made a toggle component (Accordion to be exact)
I am mapping through an array of objects and listing them like:
{object.map((o) => (
<Accordion key={o.id} title={o.question} className="item">
<div className="text"> { o.answer } <div/>
</Accordion>
))}
It renders something like this:
> Question 1
> Question 2
> Question 3
Now, every time I click a question, it toggles down to show the answer. All this works fine(I used hooks).
I want to be able to change the opacity of all the un toggled elements in this list when ONE of the questions is opened.
So if I open question 2, it becomes the "current item" and the opacity of question 2 and its answer should be 100% and all others(question1 an question3) should dim out or turn 50% opacity.. I am able to do it using :hover using css but that only works on hover.
Basically in theory, I should be able to select an item and remove the base class from all other items except the selected one. I don't know how to do that in reality. Help.
I feel like I'm missing something obvious.
const Accordion = ({ title, children, opened = false }) => {
const [show, setShow] = useState(opened);
const rotation = classnames('icon', {
'rotate': show,
});
const content = classnames('contents', {
'closed': !show,
});
useEffect(() => {
setShow(opened);
}, [opened]);
const toggle = useCallback(() => {
setShow(!show);
}, [show]);
return (
<div className='titleContainer' onClick={toggle}>
<div className={rotations}>
<i className='icon' />
</div>
<h5 className='title'>{title}</h5>
</div>
<div className={content}>{children}</div>
);
};
I finally understand what you mean, I think this is the answer:
const questionColor = (questionIndex, activeQuestion) => {
if (activeQuestion !== null && activeQuestion !== questionIndex) {
return "rgba(0,0,0,0.1)";
} else return "rgba(0,0,0,1)";
};
Working solution here:
https://codesandbox.io/s/cocky-hellman-fxrmc

Svelte: How to fetchMore if a user has scrolled to end of page

I have a Component which fetches posts from a graphql server. It shall fetch more posts if the user is at the bottom of the page. How can I check if he has reached it? I looked on clientHeight, innerHeight and outerHeight but only clientHeight made any sense to work with from those. Probalby I have to substract somethin from it but I don't know how what exactly and what I am missing?
<script>
import {getClient, query} from 'svelte-apollo';
import ContentWrapper from '../layout/ContentWrapper.svelte';
import {GET_POSTS} from "../services/posts/postsApi";
let pageInfo;
let first = 2;
let after;
const client = getClient();
let posts = query(client, {query: GET_POSTS, variables: {first}});
const fetchMore = () => {
posts.fetchMore({
variables: {first, after},
updateQuery: (previousResult, {fetchMoreResult}) => {
const newEdges = fetchMoreResult.getPosts.edges;
const pageInfo = fetchMoreResult.getPosts.pageInfo;
return newEdges.length
? {
getPosts: {
__typename: previousResult.getPosts.__typename,
edges: [...previousResult.getPosts.edges, ...newEdges],
pageInfo
}
}
: previousResult;
}
})
};
const setState = queryData => {
pageInfo = queryData.getPosts.pageInfo;
after = pageInfo.endCursor;
return '';
};
let y;
let clientHeight;
const checkScrollPosition = () => {
if(clientHeight <= y)
fetchMore();
}
</script>
<svelte:window on:scroll={checkScrollPosition} bind:scrollY={y} />
<ContentWrapper>
<div slot="header">
<h4>Page of Pagination</h4>
</div>
<div slot="body" bind:clientHeight={clientHeight}>
{#await $posts}
<h3>Loading...</h3>
{:then result}
{setState(result.data)}
{#each result.data.getPosts.edges as post}
<div class="card">
<div class="card-content">
<p class="card-text">{post.node.body}</p>
</div>
</div>
{/each}
<div id="paginator">
{#if pageInfo}
{#if pageInfo.hasNextPage}
<div class="btn" on:click={fetchMore}>Next</div>
{/if}
{/if}
</div>
{:catch error}
{console.log(error)}
{/await}
</div>
</ContentWrapper>
The Intersection Observer is what you are looking for in the question. The technology is supported by all modern browsers. With it, you can find whether some element is visible on a screen and, after that, fetch more items.
You can take a look at the Svelte IntersectionObserver component implementation right here: it uses both the technology the old-fashioned way with window.innerHeight
Well, here's a simple workaround that I used, in case the height of your scrollable div is fixed for all screen sizes.
So, First of all, enclose it in a div and in CSS, set the overflow: scroll and the height: 600px (any fixed height)
Then on localhost, run this function in console of your browser's dev tools, getX() and note down the value that is returned (We'll call it 'x'):
const getX = () => {
let myDiv = document.querySelector('my-div-id')
myDiv.scrollTop = myDiv.scrollHeight;
return myDiv.scrollHeight - myDiv.scrollTop;
}
Then in your Svelte component:
<sript>
let x = 585 // the value returned by getX() function
let contentLength = 10; // number of items that load on the page initially
const loadOnScroll = (elem) => {
if (elem.scrollTop >= (elem.scrollHeight - x) ) {
contentLength += 10; // load more as user scrolls to bottom
}
}
</script>
<div id="my-div-id"
on:scroll="{ () => loadOnScroll(document.querySelector('my-div-id')) }" >
{#each items as item, i}
{#if i < contentLength}
<ul>
<li>
<!-- Do something with item -->
</li>
</ul>
{/if}
{/each}
</div>
This will load 10 more fetched items from your API each time the user scrolls all the way to the bottom of the div. I know it looks kinda scattered, but hey, it works ;)

Vue.js: How to change a class when a user has clicked on a link?

I am trying to add a class to an element depending on whether the user has clicked on a link. There is a similar question here but it is not working as I wanted it to be.
I created a component which has its own internal data object which has the property, isShownNavigation: false. So when a user clicks on the a I change isShownNavigation: true and expect my css class isClicked to be added. Alas that is not happening - isShownNavigation stays false in the component when I displayed it {{isShownNavigation}} but I can see in the console that my method is working when clicked.
I imported my header component to the App. Code is below.
Header Component
<template>
<header class="header">
<a
href="#"
v-bind:class="{isClicked: isShowNavigation}"
v-on:click="showNavigation">
Click
</a>
</header>
</template>
<script>
export default {
name: 'header-component',
methods: {
showNavigation: () => {
this.isShowNavigation = !this.isShowNavigation
}
},
data: () => {
return {
isShowNavigation: false
}
}
}
</script>
Application
<template>
<div id="app">
<header-component></header-component>
</div>
</template>
<script>
import HeaderComponent from './components/Header.vue'
export default {
name: 'app',
components: {
'header-component': HeaderComponent
}
}
</script>
I am using the pwa template from https://github.com/vuejs-templates/pwa.
Thanks.
Don't use fat arrow functions to define your methods, data, computed, etc. When you do, this will not be bound to the Vue. Try
export default {
name: 'header-component',
methods: {
showNavigation(){
this.isShowNavigation = !this.isShowNavigation
}
},
data(){
return {
isShowNavigation: false
}
}
}
See VueJS: why is “this” undefined? In this case, you could also really just get rid of the showNavigation method and set that value directly in your template if you wanted to.
<a
href="#"
v-bind:class="{isClicked: isShowNavigation}"
v-on:click="isShowNavigation = true">
Click
</a>
Finally, if/when you end up with more than one link in your header, you will want to have a clicked property associated with each link, or an active link property instead of one global clicked property.

Resources