Custom Gutenberg Block with Slider - block

I'm creating a Gutenberg block for a slider using Boostrap. I can't figure out how I can go about inserting the "active" class only on the first post inside the loop, any suggestions would be appreciated, thanks.
This is my Edit file:
import { Component, RawHTML } from "#wordpress/element";
import { RichText, BlockControls, InspectorControls, AlignmentToolbar} from "#wordpress/editor";
import { __ } from "#wordpress/i18n";
import { withSelect } from "#wordpress/data";
import { decodeEntities } from "#wordpress/html-entities";
import { Toolbar, PanelBody, BaseControl, ColorPicker, FontSizePicker, RangeControl, TextControl, SelectControl } from "#wordpress/components";
import { dateI18n, format, __experimentalGetSettings } from '#wordpress/date';
class SliderEdit extends Component {
onChangeCategories = (categories) => {
this.props.setAttributes({postCategories: categories.join(',')})
}
onChangeNumberOfPosts = (numberOfPosts) => {
this.props.setAttributes({numberOfPosts})
}
render() {
const { className, attributes, setAttributes, categories, posts} = this.props;
const {
postCategories,
numberOfPosts,
} = attributes;
const dateFormat = __experimentalGetSettings().formats.date;
return(
<>
<InspectorControls>
<PanelBody
title={__('Loop Settings', 'df-slider-b')}
>
<div class="df-cat-multiple">
<SelectControl
multiple
label={__("Categories","df-blocks")}
help={__('You can select multiple categories!','df-blocks')}
onChange={this.onChangeCategories}
options={categories && categories.map(category => ({value: category.id, label: category.name}))}
value={postCategories && postCategories.split(',')}
/>
</div>
<RangeControl
label={__("Number of Posts","df-blocks")}
help={__('Set -1 to get all posts','df-blocks')}
value={ numberOfPosts }
onChange={ this.onChangeNumberOfPosts }
min={-1}
max={10}
/>
</PanelBody>
</InspectorControls>
{(posts && posts.length > 0) ?
<div id="carouselDFControls" class="df_height_carousel_block carousel slide" data-ride="carousel">
<div class="carousel-inner">
{posts.map( post=> (
<>
{post && post._embedded && post._embedded['wp:featuredmedia'] &&
<div class="carousel-item active">
<img src={ post._embedded['wp:featuredmedia'][0].source_url } />
</div>
}
</>
))}
<a class="carousel-control-prev df-carousel-control-prev" href="#carouselDFControls" role="button" data-slide="prev">
<i class="icon-arrow-left"></i>
</a>
<a class="carousel-control-next df-carousel-control-next" href="#carouselDFControls" role="button" data-slide="next">
<i class="icon-arrow-right"></i>
</a>
</div>
</div>
: <div>{posts ? __("No posts found","df-blocks") : __("Loading...","df-blocks")}</div>
}
</>
)
}
}
export default withSelect(
(select, props) => {
const { attributes } = props;
const { numberOfPosts, postCategories } = attributes;
let query = { per_page: numberOfPosts}
if(postCategories) {
query['categories'] = postCategories;
}
return {
posts: select('core').getEntityRecords('postType', 'post',{_embed: true} ),
posts: select('core').getEntityRecords('postType', 'post', query ),
categories: select('core').getEntityRecords('taxonomy','category', {per_page: -1})
}
}
)(SliderEdit);
If I don't add the "active" class in the first post the carousel doesn't work.
Regards
Denis

You can use the index param in your map function. If the index is 0 it is the first item in the loop.
{ posts.map( ( post, index ) => (
<div className={ `carousel-item${ index === 0 ? ' active' : '' }` }>
...
</div>
) }
Also, you want to use className instead of class in React.

Related

How to toggle show hide functionality for multiple fields in formik?

import React, { Fragment, useState, useEffect } from "react";
import { useSelector, useDispatch, connect } from "react-redux";
import { useNavigate } from "react-router";
import { Field, Form, Formik } from "formik";
import VisibilityIcon from "#material-ui/icons/Visibility";
import VisibilityOffIcon from "#material-ui/icons/VisibilityOff";
import { changePassword } from "../../Redux/Actions/changePasswordAction";
import { passwordErrors, roles } from "../Shared/constants";
import Alert from "../Modal/modal";
import "./ChangePassword.css";
function ChangePassword() {
const [showPassword1, setShowPassword1] = useState(false);
const [showPassword2, setShowPassword2] = useState(false);
const [showPassword3, setShowPassword3] = useState(false);
const alert = useSelector((state) => state.alert);
enter code here
const role_id = localStorage.getItem("role_id");
//Redux Dispatch:
const dispatch = useDispatch();
//Used to navigate
const navigation = useNavigate();
const passwords = {
currentPassword: "",
newPassword: "",
confirmNewPassword: "",
};
// function toggleShowPassword(clickedField) {
// if (clickedField === showPassword1) {
// setShowPassword1(!showPassword1) &&
// setShowPassword2(showPassword2) &&
// setShowPassword3(showPassword3);
// } else if (clickedField === showPassword2) {
// setShowPassword2(!showPassword2) &&
// setShowPassword1(showPassword1) &&
// setShowPassword3(showPassword3);
// } else {
// setShowPassword3(!showPassword3) &&
// setShowPassword1(showPassword1) &&
// setShowPassword2(showPassword2);
// }
// }
function toggleShowPassword1() {
setShowPassword1(!showPassword1);
}
function toggleShowPassword2() {
setShowPassword2(!showPassword2);
}
function toggleShowPassword3() {
setShowPassword3(!showPassword3);
}
//Alert
useEffect(() => {
if (Number(role_id) === roles.TRAINER_ROLE_ID) {
if (alert.type === "success_clear") {
navigation("/trainer-profile");
}
} else {
if (alert.type === "success_clear") {
navigation("/user-profile");
}
}
}, [alert, navigation, role_id]);
function validate(passwords) {
const errors = {};
if (passwords.currentPassword === "") {
errors.currentPassword = passwordErrors.PASSWORD;
}
if (passwords.newPassword === passwords.currentPassword) {
errors.newPassword = passwordErrors.OLD_NEW_SAME;
}
if (passwords.newPassword === "") {
errors.newPassword = passwordErrors.PASSWORD;
} else if (
!/^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!##%&])(?=.{8,})/.test(
passwords.newPassword
)
) {
errors.newPassword = passwordErrors.PASSWORD_INVALID;
}
if (passwords.confirmNewPassword === "") {
errors.confirmNewPassword = passwordErrors.CONFIRM_PASSWORD;
} else if (passwords.confirmNewPassword !== passwords.newPassword) {
errors.confirmNewPassword = passwordErrors.PASSWORDS_UNMATCHED;
}
return errors;
}
function handleChangePassword(passwords) {
const id = localStorage.getItem("user_id");
const passwordsData = {
oldPassword: passwords.currentPassword,
password: passwords.confirmNewPassword,
};
dispatch(changePassword(id, passwordsData));
}
return (
<Fragment>
<div className="row">
<div className="col">
<span className="main-heading mx-4">Change Account Password</span>
<div className="mx-4">
<hr></hr>
</div>
</div>
</div>
{alert.message && <Alert show={true} />}
<div className="passwords">
<Formik
initialValues={passwords}
validate={(values) => validate(values)}
onSubmit={(values) => handleChangePassword(values)}
>
{({ errors, touched }) => (
<Form>
<div className="row">
<div className="col-md">
<div className="form-group mb-4">
<label htmlFor="currentPassword" className="cp-label">
Current Password
</label>
<div className="input-group">
<Field
type={showPassword1 ? "text" : "password"}
id="currentPassword"
name="currentPassword"
placeholder="Enter your Current password"
className={
errors.currentPassword && touched.currentPassword
? "form-control primary-input-field is-invalid pass"
: "form-control primary-input-field pass"
}
/>
<span
className="input-group-text"
id="basic-addon2"
// onClick={() => toggleShowPassword(showPassword1)}
onClick={toggleShowPassword1}
>
{showPassword1 ? (
<VisibilityIcon
fontSize="small"
className="iconColor"
/>
) : (
<VisibilityOffIcon
fontSize="small"
className="iconColor"
/>
)}
</span>
{touched.currentPassword && errors.currentPassword ? (
<div className="invalid-feedback">
{errors.currentPassword}
</div>
) : null}
</div>
</div>
</div>
</div>
<div className="row">
<div className="col-md">
<div className="form-group mb-4">
<label htmlFor="newPassword" className="cp-label">
New Password
</label>
<div className="input-group">
<Field
type={showPassword2 ? "text" : "password"}
id="newPassword"
name="newPassword"
placeholder="Enter your New password"
className={
errors.newPassword && touched.newPassword
? "form-control primary-input-field is-invalid pass"
: "form-control primary-input-field pass"
}
/>
<span
className="input-group-text"
id="basic-addon2"
// onClick={() => toggleShowPassword(showPassword2)}
onClick={toggleShowPassword2}
>
{showPassword2 ? (
<VisibilityIcon
fontSize="small"
className="iconColor"
/>
) : (
<VisibilityOffIcon
fontSize="small"
className="iconColor"
/>
)}
</span>
{touched.newPassword && errors.newPassword ? (
<div className="invalid-feedback">
{errors.newPassword}
</div>
) : null}
</div>
</div>
</div>
</div>
<div className="row">
<div className="col-md">
<div className="form-group mb-4">
<label htmlFor="confirmNewPassword" className="cp-label">
Confirm New Password
</label>
<div className="input-group">
<Field
type={showPassword3 ? "text" : "password"}
id="confirmNewPassword"
name="confirmNewPassword"
placeholder="Confirm your New password"
className={
errors.confirmNewPassword &&
touched.confirmNewPassword
? "form-control primary-input-field is-invalid pass"
: "form-control primary-input-field pass"
}
/>
<span
className="input-group-text"
id="basic-addon2"
// onClick={() => toggleShowPassword(showPassword3)}
onClick={toggleShowPassword3}
>
{showPassword3 ? (
<VisibilityIcon
fontSize="small"
className="iconColor"
/>
) : (
<VisibilityOffIcon
fontSize="small"
className="iconColor"
/>
)}
</span>
{touched.confirmNewPassword &&
errors.confirmNewPassword ? (
<div className="invalid-feedback">
{errors.confirmNewPassword}
</div>
) : null}
</div>
</div>
</div>
</div>
<div className="row mt-4">
<div className="col text-end">
<button className="btn primary-button" type="submit">
Change Password
</button>
</div>
</div>
</Form>
)}
</Formik>
</div>
</Fragment>
);
}
const mapStateToProps = (state) => ({
changePassword: state.changePassword,
});
export default connect(mapStateToProps, {
changePassword,
})(ChangePassword);
Please someone help me to apply only one function that can toggle show and hide for three fields, I have already tried one function but it's not working. I couldn't know what to do ? I need one function there to toggle eye symbol when it is clicked, but with the function I have used it's changing and I am pressing for one field and another field's being shown, please someone explain me

vue 3 composition api hooks and data rendering

I would like to show data in template when the page loads after i fetch the data from a database.
The data is returned with async/await.
The select menus I use for filtering are now reactive only once I select a value from the menu. But I would like the full(unfiltered) data array to show when the page first loads and before any filters are applied.
How can I wait untill the data is fully fetched besore passing it to my template ?
<template>
<div class="mp" style="width: 100vw;">
<div class="col-2">
<span class="lbl">FILTER1</span>
<select v-model="selectedMA" class="form-select" >
<option value="">all</option>
<option value="Germany">Germany</option>
</select>
</div>
<div class="col-2">
<span class="lbl">FILTER2</span>
<select v-model="selectedMA2" class="form-select" >
<option value="">all</option>
<option value="Tier1">Tier1</option>
</select>
</div>
<p>DATA :</p>
<p> {{actorListTest2}} </p>
</div>
</template>
<script >
import {watchEffect, ref, computed} from 'vue'
import getActorDocs from '../composables/getActorDocs'
export default {
setup(){
const {actorDocs, loadActors} = getActorDocs()
const selectedMA = ref("")
const selectedMA2 = ref("")
loadActors();
var filteredActors = actorDocs
const actorListTest2 = computed(() => {
if(selectedMA == null && selectedMA2 == null){return filteredActors}
if(selectedMA.value == "" && selectedMA2.value == ""){return filteredActors}
else {
return filteredActors.filter(obj => {
return (!selectedMA2.value.length || obj.type == selectedMA2.value)
&& (!selectedMA.value.length || obj.country == selectedMA.value)
})}
});
watchEffect(() => console.log(actorListTest2.value))
return {filteredActors, actorListTest2, selectedMA, selectedMA2, actorDocs}
}//setup
}
</script>
As requested, here is getActorDocs():
import {ref} from 'vue'
import { projectFirestore } from '../firebase/config'
import { collection, getDocs } from "firebase/firestore";
const getActorDocs = () => {
const actorDocs = []
const error = ref(null)
const loadActors = async () => {
try {
const querySnapshot = await getDocs(collection(projectFirestore, "actors"));
querySnapshot.docs.map(doc => {
actorDocs.push(doc.data())
})
} catch (err) {
error.value = err.message
console.log(error.value)
}
}
return { actorDocs, error, loadActors}
}
export default getActorDocs

How to pass index in Vue3 v-for Array Refs

In document, it gives code:
<div v-for="item in list" :ref="setItemRef"></div>
export default {
setup() {
let itemRefs = []
const setItemRef = el => {
if (el) {
itemRefs.push(el)
}
}
onBeforeUpdate(() => {
itemRefs = []
})
onUpdated(() => {
console.log(itemRefs)
})
return {
setItemRef
}
}
}
But I also want to pass the v-for index into handler, just like this:
// fake code
<div v-for="(item, index) in navItems" :key="index":ref="setNavItemRefs($el, index)">
<span>{{ item }}</span>
</div>
How can I bind this index?
Try out to define an inline handler like :
<div v-for="item in list" :ref="(el)=>setItemRef(el,index)"></div>
and
const setItemRef = (el,index) => {
if (el) {
itemRefs.push(el)
}
}

How to make increment/decrement cart count in vue

I've created a function for increment and decrement so that I can change the count in the cards.count.
The function for increment count works like this
but the functions for decrement count, it doesn't work.
When I click the function for decrement, this output will appear
Error in v-on handler: "TypeError: Cannot read property 'product_id' of undefined"
I don't know why there is an error in the product_id, even though the increment function also has a product_id.
I hope you can help me figured out the problem
<template>
<div v-if="cartList && cartList.length > 0">
<div
class="item-loop container col"
v-for="(cards, index) in cartList"
:key="generateKey(cards.product_id, cards.count)"
>
<div class="items row">
<div class="image col-4">
<img class="img-fluid pt-2" :src="cards.product_img" />
</div>
<div class="content text-left col-8">
<button
v-on:click="cartList.splice(index, 1)"
type="button"
class="close"
data-dismiss="modal"
aria-label="Close"
>
<span aria-hidden="true">×</span>
</button>
<h5 class="text-left">{{ cards.product_name }}</h5>
<div class="prices" :key="cards.product_id">
<div class="value-cart form-group row">
<font-awesome-icon
:icon="['far', 'minus-square']"
size="2x"
class="minus-button"
#click="min(cards.product_id)"
/>
<input
type="number"
class="input-pm bg-light form-control p-0 text-center angka"
:value="cards.count"
/>
<font-awesome-icon
:icon="['far', 'plus-square']"
size="2x"
class="plus-button"
#click="plus(cards.product_id)"
/>
</div>
<p>Rp. {{ cards.product_price * cards.count }}</p>
</div>
</div>
</div>
</div>
<div class="cart-order">
<div class="order-total">
<div class="row">
<h4 class="col-6 font-weight-bold text-left">Total</h4>
<h5 class="col-6 font-weight-bold text-right">
Rp. {{ totalprice }}
</h5>
</div>
</div>
<p class="text-left"><strong>*Not including tax(10%)</strong></p>
<b-button
class="mt-3"
variant="primary"
#click="invoice()"
v-b-modal="'modal-checkout'"
block
>Checkout</b-button
>
<b-button class="mt-2" variant="danger" #click="resetcart" block>
Cancel </b-button
><br /><br />
</div>
</div>
</template>
export default {
components: {
Features,
Card,
},
data() {
return {
datamenu: {},
cartList: [],
invoiceid: 0,
tax: 0,
searchMenu: null,
menu: null,
formCheck: {
amount: 0,
invoice: "",
cashier: "abiwardani",
menu_name: "",
},
};
},
methods: {
plus(product_id) {
let result = this.cartList.find((res) => {
if (res.product_id == product_id) {
return res.product_id;
}
});
if (result) {
for (let i = 0; i < this.cartList.length; i++) {
if (this.cartList[i].product_id == product_id) {
const newFoodObject = {
...this.cartList[i],
count: this.cartList[i].count + 1,
};
console.log("plus");
this.$set(this.cartList, i, newFoodObject);
}
}
}
},
min(product_id) {
let result = this.cartList.find((res) => {
if (res.product_id == product_id) {
return res.product_id;
}
});
if (result) {
for (let i = this.cartList.length; i > 0; i--) {
if (this.cartList[i].product_id == product_id && this.cartList[i].count > 0){
const newFoodObject = {
...this.cartList[i],
count: this.cartList[i].count - 1,
};
this.$set(this.cartList, i, newFoodObject);
}
}
}
},
generateKey(key1, key2) {
return `${key1}-${key2}`;
},
},
mounted() {
axios
.get(process.env.VUE_APP_URL + "product")
.then((res) => {
this.datamenu = res.data.result;
})
.catch((err) => {
console.log(err);
});
}
Option 1:
i should start with length-1 and should go up to 0
min(product_id) {
let result = this.cartList.find((res) => {
if (res.product_id == product_id) {
return res.product_id;
}
});
if (result) {
for (let i = this.cartList.length-1; i >= 0; i--) {
if (this.cartList[i].product_id == product_id && this.cartList[i].count > 0){
const newFoodObject = {
...this.cartList[i],
count: this.cartList[i].count - 1,
};
this.$set(this.cartList, i, newFoodObject);
}
}
}
Option 2:
You do not need 2 methods.. just have one
updateQty(product_id,mode) {
let result = this.cartList.find((res) => {
if (res.product_id == product_id) {
return res.product_id;
}
});
if (result) {
for (let i = 0; i < this.cartList.length; i++) {
if (this.cartList[i].product_id == product_id) {
const newFoodObject = {
...this.cartList[i],
count: mode === 'INCRE' ? this.cartList[i].count + 1 : this.cartList[i].count - 1,
};
console.log("plus");
this.$set(this.cartList, i, newFoodObject);
}
}
}
},
use it like
<font-awesome-icon
:icon="['far', 'minus-square']"
size="2x"
class="minus-button"
#click="updateQty(cards.product_id,'INCRE')"
/>
and
<font-awesome-icon
:icon="['far', 'minus-square']"
size="2x"
class="minus-button"
#click="updateQty(cards.product_id,'DECRE')"
/>
Your problem is in the final expression of your for statement inside your min() method.
Change 'i--' with 'i++' to increase your index in each iteration. This will avoid accessing the 'cartList' array with a negative index (which is causing the problem, as it gets undefined):
min(product_id) {
let result = this.cartList.find((res) => {
if (res.product_id == product_id) {
return res.product_id
}
})
if (result) {
for (let i = this.cartList.length; i > 0; i++) {
👆
...
Here is how I did that
HTML:
<button #click="increment()">+</button>
<input :value="amount" />
<button #click="decrement()">-</button>
JS:
data() {
return {
amount: 0,
};
},
methods: {
increment() {
this.amount++;
},
decrement() {
this.amount--;
},
},

How can i paginate table

I have the following code to display data from json file, i have more that 500 records i want to display 10 records per page. Here is my project in [code pen][1] . I tried react-pagination library but that doesn't work. what is the best way to do this? Open to use any library recommended -- i tried almost all of them.
here is how my code looks like
I'm sure there are a hundred different ways of doing it, but just to teach the idea of the mechanics, here is a very manual version:
{this.state.filteredData
.slice(this.state.activePage * 10, (this.state.activePage + 1) * 10)
.map(results => ( ...
))}
.....
{/*Pagination goes here */}
<button onClick={() => {this.setState({activePage: this.state.activePage - 1})}} >
prev</button>
<button onClick={() => {this.setState({activePage: this.state.activePage + 1})}} >
next</button>
That is, you take only a slice of the data before mapping it into DOM elements, and the buttons for advancing or going back just select the slice by setting the activePage state variable you already had.
You could have something along the lines of an index and offset and then create chunks of your array.
Give this a try:
import React, { Component } from "react";
import { render } from "react-dom";
import Hello from "./Hello";
import cardData from "./response.json";
import "./style.css";
class App extends Component {
constructor() {
super();
const offset = 5;
console.log(cardData);
this.state = {
name: "React",
index: 0,
offset,
chunks: this.chunkArray(cardData.data.Table, offset)
};
}
chunkArray(inputArray, chunkSize){
console.log("inputArray:: ", inputArray);
const results = [];
while (inputArray.length) {
results.push(inputArray.splice(0, chunkSize));
}
console.log("results:: ", results);
return results;
}
handleClick(index) {
this.setState({
index
})
}
render() {
console.log(this.state.chunks);
return (
<div>
{this.state.chunks && this.state.chunks[this.state.index].map(results => (
<div className="col-sm-3">
<h3>
{results.first_name} {results.last_name}
</h3>
<h3>{results.manager}</h3>
<div className="row">
<div className="col-md-3 col-sm-6"> {results.Department}</div>
</div>
<a
to={{ pathname: `/cards/${results.id}`, state: results }}
className={`card-wrapper restore-${results.id}`}
href={`/cards/${results.id}`}
>
View Detail
</a>
</div>
))}
<br />
{ this.state.chunks && this.state.chunks.map((item, index) => <button onClick={() => this.handleClick(index)}>{index + 1}</button>) }
</div>
);
}
}
render(<App />, document.getElementById("root"));
Here's a Working Code Sample Demo for your ref.
If you're using hooks, this will work otherwise it can be easily adapted. Basically, just store the index of where you are and then get the data you need based on that index:
const [index, setIndex] = React.useState(0);
const PAGE_SIZE = 10;
const tableData = cardData.data.Table.slice(index, index + PAGE_SIZE);
const table = {tableData.map(results => (
<div className="col-sm-3">
<h3>
{results.first_name} {results.last_name}
</h3>
<h3 >{results.manager}</h3>
<div className="row">
<div className="col-md-3 col-sm-6"> {results.Department}</div>
</div>
<Link
to={{ pathname: `/cards/${results.id}`, state: results }}
className={`card-wrapper restore-${results.id}`}
>
View Detail
</Link>
</div>
))}
const navigation = (
<div>
<div disabled={index <= 0 ? true : false} onClick={() => setIndex(index - PAGE_SIZE)}>Prev</div>
<div disabled={index <= cardData.data.Table.length ? true : false} onClick={() => setIndex(index + PAGE_SIZE)}>Next</div>
</div>
);