I have a problem with overlapping elements in a JSX app - html

I created a button that adds emails inputs in a form (for invites), but at a certain number of inputs, they overlaps others elements on the page.
Here's some css :
.App {
height: 100%;
}
html,
body,
#root {
margin: 0;
height: 100%;
font-family: "Montserrat";
}
.page {
height: 80%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.content {
margin-top: 50px;
width: 400px;
}
(It's a JSX app)
Here's a screenshot of the page: https://paste.pics/ab860613d5fd6eb82dfe59894ee24bc0
Thanks for your help !

You can add a 'container' div around your input's.. From there you can use height and width props along with the overflow: 'scroll' attribute.
Something like this:
const { useState } = React;
// Create items
const NEW_INVITES = [...Array(10).keys()].map((item, index) => {
return {
id: index + 1,
value: index + 1
}
});
function InviteInput(props) {
const { value, onChange } = props;
const handleChange = e => {
onChange && onChange(e);
};
return (
<li>
<input
value={value}
onChange={handleChange}
className="form-input"
type="email"
placeholder="nom#exemple.com"
name="invites"
required
/>
</li>
);
}
function AddInviteButton(props) {
return (
<button onClick={props.onClick}>
Ajouter une autre personne // (Add another person)
</button>
);
}
function InviteForm({ onSubmit, height, width, addBorder }) {
const [nbInvites, setNbInvites] = useState(NEW_INVITES);
let containerRef;
const onAddInviteClick = () => {
let id = nbInvites.length + 1;
setNbInvites([
...nbInvites,
{
id,
value: id
}
]);
setTimeout((cr) => {
cr.scrollIntoView({ behavior: "smooth" });
}, 100, containerRef);
};
const handleChange = (event, index) => {
let newstate = [...nbInvites];
newstate[index].value = event.target.value;
setNbInvites(newstate);
};
const handleSubmit = event => {
onSubmit(event, nbInvites);
};
const invitesContainer = {
height,
width,
overflow: 'scroll',
border: addBorder === true ? '1px solid black' : '',
}
return (
<div>
<div style={invitesContainer}>
{nbInvites.map((item, index) => {
return (
<InviteInput
key={index}
value={item.value}
onChange={e => handleChange(e, index)}
/>
);
})}
<div ref={r => containerRef = r}/>
</div>
<AddInviteButton onClick={onAddInviteClick} />
<br />
<button onClick={handleSubmit}>Submit</button>
</div>
);
}
function App() {
const [formVals, setFormVals] = useState();
const doSubmit = (event, formValues) => {
setFormVals(formValues);
};
return (
<div className="page">
<h2 className="box-title">
Qui sont les eleves de cette classe ? // (Who are the students in this
class?)
</h2>
<p>
Vous pourrez toujours en ajouter par la suite // (You can always add
some later)
</p>
<InviteForm onSubmit={doSubmit} height="100px" width="200px" addBorder />
{formVals ? <pre>{JSON.stringify(formVals, null, 2)}</pre> : ""}
</div>
);
}
ReactDOM.render(<App />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.9.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>

Related

How to resolve import and export error in reportWebVitals.js

I am new to coding with React.js and monday.com. I am trying to create a weather app that works with monday.com. I want the app to use the location column on the monday.com board to display the weather data for a specific city. My api seems to work just fine and gives the weather results when I enter a city into my app. The below error occurred once I started to try and get the app to work with the monday.com board.
I have an import/export error. I have checked all my opening and closing brackets but they all seem correct. Please advise how I can fix this problem. Thanks!
Error:
Error in ./src/reportWebVitals.js
Syntax error: C:/Users/E7470/weather-app/src/reportWebVitals.js: 'import' and 'export' may only appear at the top level (3:4)
1 |
const reportWebVitals = onPerfEntry => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
> 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
| ^
4 | getCLS(onPerfEntry);
5 | getFID(onPerfEntry);
6 | getFCP(onPerfEntry);
# ./src/index.js 19:23-51
App.js:
import React, { useState } from 'react';
import './App.css';
import mondaySdk from 'monday-sdk-js';
import 'monday-ui-react-core/dist/main.css';
//Explore more Monday React Components here: https://style.monday.com/
import AttentionBox from 'monday-ui-react-core/dist/AttentionBox.js';
const monday = mondaySdk();
function App() {
const apiKey = 'API KEY'; //I inserted my api key here
const [weatherData, setWeatherData] = useState([{}]);
const [city, setCity] = useState('');
const [context, setContext] = useState();
const [weatherArr, setWeatherArr] = useState();
useEffect(() => {
const getContext = async () => {
try {
monday.listen('context', (res) => {
console.log(res.data);
getBoards(res.data);
setContext(res.data);
});
monday.listen("settings", (res) => {
console.log(res.data);
setMeasurementUnit(res.data.dropdown)
})
//board id (Add board Id Here eg.328567743)
} catch (err) {
throw new Error(err);
}
};
getContext();
}, []);
const getBoards = async (context) => {
try {
var arr = [];
var boards = [];
for( const boardId of context.boardIds){
var res = await monday.api(`query { boards(limit:1, ids:[${boardId}]) {
name
items {
name
column_values {
id
value
text
}
}
}
}`);
boards.push(res.data.boards);
}
for (const board of boards ){
console.log(boards)
console.log(board)
for (const item of board[0].items){
var location = item.column_values[0].text;
var latLng = item.column_values[0].value
var itemObj = {
location,
latLng
}
const getWeather = (event) => {
if (event.key == 'Enter') {
fetch(
`https://api.openweathermap.org/data/2.5/weather?q=${city}&units=imperil&APPID=${apiKey}` //api given in video tutorial
)
.then((response) => response.json())
.then((data) => {
setWeatherData(data);
setCity('');
});
}
};
setWeatherArr(arr);
} catch (err) {
throw new Error(err);
}
};
return (
<div className="container">
<input
className="input"
placeholder="Enter City..."
onChange={(e) => setCity(e.target.value)}
value={city}
onKeyPress={getWeather}
/>
{typeof weatherData.main === 'undefined' ? (
<div>
<p>Welcome to weather app! Enter the city you want the weather of.</p>
</div>
) : (
<div className="weather-data">
<p className="city">{weatherData.name}</p>
<p className="temp">{Math.round(weatherData.main.temp)}℉</p>
<p className="weather">{weatherData.weather[0].main}</p>
</div>
)}
{weatherData.cod === '404' ? <p>City not found.</p> : <></>}
</div>
);
}
export default App;
App.css:
.container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 25px;
}
.input {
padding: 15px;
width: 80%;
margin: auto;
border: 1px solid lightgrey;
border-radius: 6px;
font-size: 16px;
}
.input:focus{
outline: none;
}
.weather-data{
margin-top: 30px;
display: flex;
flex-direction: column;
align-items: center;
}
.city{
font-size: 30px;
font-weight: 200;
}
.temp{
font-size: 90px;
padding: 10px;
border: 1px solid lightgray;
border-radius: 12px;
}
.weather{
font-size: 30px;
font-weight: 200;
}

Render raw HTML inside a react div

I have this raw HTML that is stored in the database and I'm trying to render it inside a div.
const style = {
border: '2px solid #eee',
height: '270px',
width: '100%',
overflow: 'auto',
padding: '10px'
}
const test = () => {
const [contentFromDB, setContentFromDB] = useState("");
useEffect(() => {
async function fetchData() {
setContentFromDB(await myAPI.getData())
}
}, []);
return (
<>
<div className="row">
<h3>Test Render</h3>
<div style={style}>
{contentFromDB}
</div>
</div>
</>
)
}
export default test;
The problem is that it is keeping all the HTML tags, it is rendering as a string (image below). Is there a way to render the raw HTML inside my div?
You can use dangerouslySetInnerHTML to achieve this.
const style = {
border: '2px solid #eee',
height: '270px',
width: '100%',
overflow: 'auto',
padding: '10px'
}
const test = () => {
const [contentFromDB, setContentFromDB] = useState("");
useEffect(() => {
async function fetchData() {
setContentFromDB(await myAPI.getData())
}
}, []);
return (
<>
<div className="row">
<h3>Test Render</h3>
<div style={style}>
<div dangerouslySetInnerHTML={{__html:contentFromDB}} />
</div>
</div>
</>
)
}
export default test;

Conditional rendering of HTML elements based on boolean value React

building a component that takes a value (attribute.readonly), and only displays the first 40 characters, unless the <Chevron onClick is triggered which means I then want to show fullDescription as opposed to the shortHeading - ideally this would render similar to how it does currently only doubling the height of the Accordion for example, to allow the rest of the content to fit. I know I'm still a bit off but some pointers for my next steps/ ideas for improving what I have already would be really appreciated!
// #flow
import styled from "styled-components";
import chevron from "../Assets/chevron-icon.svg";
type Props = { attribute: AttributeType, className?: string };
const Accordion = styled.div`
background-color: #e5e9eb;
height: 56px;
width: 612px;
border-radius: 2px;
border: 1px solid #27282a;
margin-bottom: 48px;
display: flex;
align-items: center;
span {
padding-left: 24px;
}
`;
const Chevron = styled.img`
height: 40px;
width: 40px;
float: right;
margin-right: 12px;
`;
const ExpandableString = ({ attribute, className }: Props) => {
const fullDescription = attribute.readonlyvalue;
const shortHeading = fullDescription.substring(0, 40) + '...';
const isExpanded = false;
function toggleContent() {
isExpanded = true;
}
return (
<Accordion className={className}>
<span>{shortHeading}</span>
<Chevron onClick={toggleContent} src={chevron} alt="Expand or collapse content" />
</Accordion>
);
};
export default ExpandableString;
Put your data in the state and toggle it
const ExpandableString = ({ attribute, className }: Props) => {
const [isExpanded, setIsExpanded] = React.useState(false);
function toggleContent() {
setIsExpanded(prev => !prev);
}
return (
<Accordion className={className}>
<span>{shortHeading}</span>
<Chevron onClick={toggleContent} src={chevron} alt="Expand or collapse content" />
</Accordion>
);
};
Then, according to the isExpanded state, you could conditionally render your React component.

socket.io - show the users in the correct div

I'm pretty new to express and socket.io and I'm trying to achieve a little website:
What is it supposed to do:
You can connect to the website and enter a username
You have to select a column where you want to write (it's stored in var column)
Once on the page with the four column, you can see your username at the top of your column and start doing things there.
The other users see you in the correct column.
What it is not doing:
Actually the three points above are working quite well, my issue is with the last point :
The other users see you in the correct column.
My code is somehow not displaying every user in the correct column, in fact, it's displaying them in the same column as you are
Here is the code
$(document).ready(function () {
var socket = io();
var username = prompt("premier utilisateur : ", "nom");
var column = prompt("colonne ", "1,2,3 ou 4");
var gdhb = "";
socket.emit("new user entered his name");
socket.emit("nomUser", username);
if (column === "1") { column = ".one"; gdhb = ".dir1" }
if (column === "2") { column = ".two"; gdhb = ".dir2" }
if (column === "3") { column = ".three"; gdhb = ".dir3" }
if (column === "4") { column = ".four"; gdhb = ".dir4" }
socket.emit("user chose a column");
socket.emit("columnUser", column);
$(column).append($("<p class='username'>" + username + "</p>"))
$(document.body).click(function (b) {
var verbes = [
"appuie",
"bouscule",
"pousse"
];
var adverbes = [
"puis",
"ensuite",
"pour finir",
"alors"
];
var verbe = verbes[Math.floor(Math.random() * verbes.length)];
var adverbe = adverbes[Math.floor(Math.random() * adverbes.length)];
var verbadv = verbe + " " + adverbe;
console.log(verbadv);
socket.emit("verbadverbe");
socket.emit("verbadv", verbadv);
var div = $("<div />", {
"class": "document"
})
.css({
"left": b.pageX + 'px',
"top": b.pageY + 'px'
})
.append($("<p>" + verbadv + "</p>"))
.appendTo(column);
});
$(document.body).contextmenu(function (rc) {
var div = $("<div />", {
"class": "document"
})
.css({
"left": rc.pageX + 'px',
"top": rc.pageY + 'px'
})
.append($("<p>recule</p>"))
.appendTo(column);
});
var direction = "";
var oldx = 0;
var oldy = 0;
mousemovemethod = function (e) {
if (e.pageX > oldx && e.pageY == oldy) {
direction = "gauche";
}
else if (e.pageX == oldx && e.pageY > oldy) {
direction = "bas";
}
else if (e.pageX == oldx && e.pageY < oldy) {
direction = "haut";
}
else if (e.pageX < oldx && e.pageY == oldy) {
direction = "droite";
}
$(gdhb).append($("<p class='direction' id='direction'>" + direction + "</p>"))
$(".direction").prev().remove();
oldx = e.pageX;
oldy = e.pageY;
}
document.addEventListener('mousemove', mousemovemethod);
socket.on("columnUser", function (column) {
socket.on("nomUser", function (username) {
$(column).append($("<p class='username'>" + username + "</p>"));
socket.on("verbadv", function (verbadv) {
var div = $("<div />", {
"class": "document"
})
.append($("<p>" + verbadv + "</p>"))
.appendTo(column);
});
});
});
});
and the index.js :
const path = require('path');
const http = require('http');
const express = require('express');
const socketio = require('socket.io');
const app = express();
const server = http.createServer(app);
const io = socketio(server);
app.use(express.static(path.join(__dirname, 'public')));
io.on('connection', (socket) => {
console.log('Nouvel utilisateur')
socket.on("nomUser", (username) => {
console.log(username);
io.emit("nomUser", username);
});
socket.on("verbadv", (verbadv) => {
console.log(verbadv);
io.emit("verbadv", verbadv);
});
socket.on("columnUser", (column) => {
console.log(column);
io.emit("columnUser", column);
});
});
server.listen(3000, () => {
console.log('listen on 3000');
})
Also if it's needed to understand better, here is the css
body {
font-family: sans-serif;
font-size: 1.3rem;
margin: 0;
background-color: DarkSlateGray;
}
.wrapper {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-gap: 0px;
grid-auto-rows: minmax(100vh, auto);
height: 100vh;
}
.one,
.two,
.three,
.four {
-ms-overflow-style: none; /* Internet Explorer 10+ */
scrollbar-width: none; /* Firefox */
position: relative;
overflow: scroll;
height: 100%;
background-color: tan;
}
.one {
grid-column: 1 / 2;
}
.two {
grid-column: 2 / 3;
}
.three {
grid-column: 3 / 4;
}
.four {
grid-column: 4 / 4;
}
.one::-webkit-scrollbar,
.two::-webkit-scrollbar,
.three::-webkit-scrollbar,
.four::-webkit-scrollbar {
display: none; /* Safari and Chrome */
}
.note {
text-align: center;
width: 100px;
height: 30px;
}
.note p{
filter: drop-shadow(0 0 0.75rem black);
}
.document{
text-align: center;
}
.document p{
padding: 0;
margin: 0;
}
.username{
text-align: center;
padding: 0;
margin: 0;
}
.direction{
position: fixed;
bottom : 0;
width: 25vw;
text-align: center;
}
Thanks a lot for the precious help.
i've solved your problem with sockets. See at my solution.
client.js
function columnIndexIsValid(index, columnsQuantity) {
return index >= 0 && index <= columnsQuantity;
}
function fullNameIsValid(fullName) {
return typeof fullName === 'string' && fullName.length > 2;
}
function reloadPage() {
window.location.reload();
}
function rand(min, max) {
return Math.floor(min + Math.random() * (max - 1 - min));
}
function getRandomColour(colours = []) {
const colour = colours[rand(0, colours.length)];
return `#${colour}`;
}
function getUserHtml(user) {
return `<div class="column__users-list__item" data-item-id="${user.id}">${user.fullName}</div>`;
}
function getDrawnUsersNodes() {
return $('.column__item');
}
function canIRenderUsers(usersQuantity) {
const $renderedUsersQuantity = getDrawnUsersNodes().length;
return $renderedUsersQuantity < usersQuantity;
}
function renderUserHtmlToNode($node, html) {
$node.html($node.html() + html);
}
function getColumnUsersList(columnNode) {
const $column = $(columnNode);
return $column.find('.column__users-list');
}
function removeDrawnUserById(userId) {
$(`[data-item-id=${userId}]`).remove();
}
class DrawnUsers {
constructor() {
this.users = new Map();
}
getUserById(id) {
return this.users.get(id);
}
add(id, state) {
this.users.set(id, state);
}
removeById(id) {
this.users.delete(id);
}
exists(id) {
return this.users.has(id);
}
}
class Storage {
static setItem(key, value) {
localStorage.setItem(key, value);
}
static getItem(key) {
return localStorage.getItem(key) || null;
}
}
function generateUserId() {
return `user-${rand(rand(0, 10000), rand(20000, 50000))}`;
}
class UserState {
constructor() {
this.state = {};
}
get() {
return {
data: this.state,
};
}
set fullName(fullName) {
this.state.fullName = fullName;
}
get fullName() {
return this.state.fullName;
}
set id(id) {
this.state.id = id;
}
get id() {
return this.state.id;
}
set columnIndex(columnIndex) {
this.state.columnIndex = columnIndex - 1;
}
get columnIndex() {
return this.state.columnIndex;
}
}
$(document).ready(function () {
const drawnUsers = new DrawnUsers();
const colours = ['F2994A', 'F2C94C', '6FCF97', '2F80ED', '56CCF2', 'DFA2F5'];
const $columns = $('.column');
const $container = $('.container');
const userState = new UserState();
$columns.each(function () {
const $self = $(this);
$self.css({ 'background-color': getRandomColour(colours) });
});
userState.fullName = prompt('Type your fullName');
userState.columnIndex = +prompt('Type your column number');
if (
!fullNameIsValid(userState.fullName) ||
!columnIndexIsValid(userState.columnIndex, $columns.length)
) {
return reloadPage();
}
$container.addClass('active');
const socket = io('ws://localhost:3000');
socket.on('connect', () => {
const generatedUserId = generateUserId();
userState.id = Storage.getItem('userId') || generatedUserId;
Storage.setItem('userId', userState.id);
socket.emit('connected', userState.get());
socket.emit('addUser', userState.get());
socket.on('updateCurrentUsers', ({ data }) => {
const { users } = data;
if (!users || !canIRenderUsers(users.length)) {
return;
}
users.forEach((user) => {
const $column = $columns[user.columnIndex];
if ($column) {
if (!drawnUsers.exists(user.id)) {
drawnUsers.add(user.id);
renderUserHtmlToNode(
getColumnUsersList($column),
getUserHtml(user)
);
}
}
});
});
socket.on('newUser', ({ data }) => {
console.log('[debug] newUser: ', data);
const $column = $columns[data.columnIndex];
if (!$column) {
return;
}
if (drawnUsers.exists(data.id)) {
drawnUsers.removeById(data.id);
removeDrawnUserById(data.id);
} else {
drawnUsers.add(data.id);
renderUserHtmlToNode(getColumnUsersList($column), getUserHtml(data));
}
});
socket.on('disconnect', () => {
socket.open();
});
});
});
server.js
const path = require('path');
const http = require('http');
const express = require('express');
const socketIo = require('socket.io');
const app = express();
const server = http.createServer(app);
const io = socketIo(server);
app.use(express.static(path.join(__dirname, 'public')));
io.on('connection', (socket) => {
console.log('[debug] Client was connected successfully to server');
socket.on('connected', (user) => {
console.log('connected user data', user.data);
socket.data = user.data;
const users = Array.from(io.sockets.sockets.values()).map(
({ data }) => data
);
console.log('users', users);
socket.emit('updateCurrentUsers', {
data: {
users,
},
});
});
socket.on('addUser', ({ data }) => {
socket.data.columnIndex = data.columnIndex;
socket.broadcast.emit('newUser', {
data,
});
const users = Array.from(io.sockets.sockets.values()).map(
({ data }) => data
);
socket.broadcast.emit('updateCurrentUsers', {
data: {
users,
},
});
});
});
server.listen(3000, () => {
console.log('listen on 3000');
});
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="./index.css">
<title>Users Columns</title>
</head>
<body>
<div class="container">
<div class="column">
<span class="column__index">#1</span>
<div class="column__users-list"></div>
</div>
<div class="column">
<span class="column__index">#2</span>
<div class="column__users-list"></div>
</div>
<div class="column">
<span class="column__index">#3</span>
<div class="column__users-list"></div>
</div>
<div class="column">
<span class="column__index">#4</span>
<div class="column__users-list"></div>
</div>
</div>
<script
src="https://code.jquery.com/jquery-3.5.1.js"
integrity="sha256-QWo7LDvxbWT2tbbQ97B53yJnYU3WhH/C8ycbRAkjPDc="
crossorigin="anonymous"></script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/3.0.1/socket.io.js"
integrity="sha512-vGcPDqyonHb0c11UofnOKdSAt5zYRpKI4ow+v6hat4i96b7nHSn8PQyk0sT5L9RECyksp+SztCPP6bqeeGaRKg=="
crossorigin="anonymous"></script>
<script src="./client.js"></script>
</body>
</html>
index.css
html, body {
box-sizing: border-box;
}
body {
margin: 0;
padding: 0;
}
.container {
display: none;
}
.container.active {
display: flex;
flex-direction: row;
height: 100vh;
}
.container .column {
width: calc(100% / 4);
text-align: center;
padding: 20px 0;
}
.container .column .column__users-list {
display: flex;
flex-direction: column;
}
.container .column .column__index {
color: #FFF;
font-size: 36px;
letter-spacing: 8px;
}
hey) Let's look on that case more attentive, you have created a variable what contains a column index. right? This variable does store inside var variable. That is global variable for your script. I think you need to store each user column info inside each socket. If you don't know, each socket contains some info inside itself like id and other what can be used in your project if you'd like. But for this case you need to do so:
Ask new user what column he would like to choose.
Write to his socket data/info his column index.
When you do broadcast (It's when you send an object of data to each socket in room all each socket at all) just take this column index and draw this user in correct position. But make sure what your var column has a correct value. I can advice you to use const/let in javascript :)

Drag one Element and drop other Element

i'm trying to implement drag and drop with dynamicllay created containers and droppable items
on click of a button droppable section will get created dynamically
there will be unordered list of elements with static text like Email filed, Phone number field , multile options,etcc..
ondrag of any of these list item i want to create new element dynamically based on the static filed type(email,phone,miltiple checkbox)
need to insert new element on drop i have created a partially working code but somehow this is not working properly i suspect something i need to do with dataTransfer object.
This is partially working i was able to create element dynamically and append it to the dom but but while dragging around not working as i expected. any help would be appreciated.
let draggables = document.querySelectorAll('.draggable');
let containers = document.querySelectorAll('.container');
let mainContainer = document.getElementById('main-container');
let afterElement;
let elementToInsert;
let count = 0;
let addsec = document.getElementById('addsection');
addsec.addEventListener('click',function(e){
let container = document.createElement('div');
container.classList.add('container');
let section = document.createElement('h1');
section.innerHTML = `Section ${count++}`;
container.appendChild(section);
mainContainer.appendChild(container)
init();
})
draggables.forEach(draggable => {
draggable.addEventListener('dragstart', (e) => {
e.stopPropagation();
console.log("drag start",e,draggable);
if(draggable && draggable.getAttribute('name')){
let element = draggable.getAttribute('name');
switch(element){
case 'text-field':
elementToInsert = document.createElement('input');
elementToInsert.classList.add('draggable');
elementToInsert.setAttribute('draggable',true);
elementToInsert.setAttribute('placeholder','Text');
elementToInsert.setAttribute('disabled',true);
break;
case 'email-field':
elementToInsert = document.createElement('input');
elementToInsert.classList.add('draggable');
elementToInsert.setAttribute('draggable',true);
elementToInsert.setAttribute('placeholder','Email');
elementToInsert.setAttribute('disabled',true);
break;
case 'phone-field':
elementToInsert = document.createElement('input');
elementToInsert.classList.add('draggable');
elementToInsert.setAttribute('draggable',true);
elementToInsert.setAttribute('placeholder','Phone');
elementToInsert.setAttribute('disabled',true);
break;
default:
elementToInsert = draggable;
break;
}
}else{
elementToInsert = draggable;
}
elementToInsert.addEventListener('dragstart',function(ev){
ev.dataTransfer.setData('elementid',ev.target.id);
})
elementToInsert.setAttribute('id',`field-${Date.now()}`);
draggable.classList.add('dragging')
})
draggable.addEventListener('dragend', () => {
draggable.classList.remove('dragging')
})
})
function init(){
draggables = document.querySelectorAll('.draggable');
containers = document.querySelectorAll('.container');
console.log("draggables",draggables,containers);
containers.forEach(container => {
container.addEventListener('dragover', e => {
e.preventDefault()
let data = e.dataTransfer.getData("elementid");
console.log("eee",data);
afterElement = getDragAfterElement(container, e.clientY);
if (afterElement == null) {
container.appendChild(elementToInsert)
} else {
container.insertBefore(elementToInsert, afterElement)
}
})
container.addEventListener('drop', e => {
e.preventDefault()
let data = e.dataTransfer.getData("elementid");
// console.log("ff",data);
draggables = document.querySelectorAll('.draggable');
// init();
})
container.addEventListener('dragleave', e => {
e.preventDefault();
})
container.addEventListener('dragenter', e => {
e.preventDefault();
})
})
}
function getDragAfterElement(container, y) {
const draggableElements = [...container.querySelectorAll('.draggable:not(.dragging)')]
return draggableElements.reduce((closest, child) => {
const box = child.getBoundingClientRect()
const offset = y - box.top - box.height / 2
if (offset < 0 && offset > closest.offset) {
return { offset: offset, element: child }
} else {
return closest
}
}, { offset: Number.NEGATIVE_INFINITY }).element
}
body {
margin: 0;
}
h1{
color:#fff;
}
.container {
background-color: #333;
padding: 1rem;
margin: 1rem;
width: 200px;
}
.draggable {
padding: 1rem;
background-color: white;
border: 1px solid black;
cursor: move;
}
.draggable.dragging {
opacity: .5;
}
#main-container{
display: flex;
}
input{
display: block;
width:83%;
height: 0px;
}
.main{
display: flex;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
<link rel="stylesheet" type="text/css" href="index.css" >
</head>
<body>
<div class="main">
<div>
<div class="draggable" name="text-field" draggable="true">Text Field</div>
<div class="draggable" name="email-field" draggable="true">Email Field</div>
<div class="draggable" name="phone-field" draggable="true">Phone Field</div>
</div>
<div id="main-container">
<!-- <div class="container">
<h1>Section 1</h1>
<p class="draggable" draggable="true">1</p>
<p class="draggable" draggable="true">2</p>
</div>
<div class="container">
<h1>Section 2</h1>
<p class="draggable" draggable="true">3</p>
<p class="draggable" draggable="true">4</p>
</div> -->
</div>
</div>
<button id="addsection">Add section</button>
<script src="index.js"></script>
</body>
</html>
Finally i figured not sure this is the right way but it is working for my requirement
function CustomDragAndDrop(){
this.draggables = undefined;
this.containers = undefined;
this.placeholder = undefined;
this.elementToInsert = undefined;
this.afterElement = undefined;
this.sectionCount = 0;
this.mainContainer = undefined;
this.existingElement = undefined;
this.addSection = function(){
let container = document.createElement('div');
container.classList.add('container');
let section = document.createElement('h1');
section.innerHTML = `Section ${this.sectionCount++}`;
container.appendChild(section);
console.log("this main container",this.mainContainer);
this.mainContainer.appendChild(container);
this.addEventListenersForContainer(container);
this.updateContainers();
}
this.updateContainers = function(){
this.containers = document.querySelectorAll('.container');
}
this.updateDraggables = function(){
this.draggables = document.querySelectorAll('.draggable');
}
this.addEventListeners = function(name){
console.log("adding event listeners",this.containers,this.draggables);
if(name === 'containers' && this.containers){
this.containers.forEach(container => {
container.addEventListener('dragover',e => this.onDragHover(e,container,false));
container.addEventListener('drop',e => this.onDragDrop(e),false);
container.addEventListener('dragleave',e => this.onDragLeave(e),false);
container.addEventListener('dragenter',e => this.onDragEnter(e),false);
});
}else if('draggables' && this.draggables){
this.draggables.forEach(draggable => {
draggable.addEventListener('dragstart',e => this.onDragStart(e,draggable),false);
draggable.addEventListener('dragend', e => this.onDragEnd(e,draggable),false);
});
}else if(this.draggables && this.containers){
this.containers.forEach(container => {
container.addEventListener('dragover',e => this.onDragHover(e,container),false);
container.addEventListener('drop', e => this.onDragDrop(e),false);
container.addEventListener('dragleave',e => this.onDragLeave(e),false);
container.addEventListener('dragenter',e => this.onDragEnter(e),false);
});
this.draggables.forEach(draggable => {
draggable.addEventListener('dragstart',e => this.onDragStart(e),false);
draggable.addEventListener('dragend', e => this.onDragEnd(e),false);
});
}
console.log("draggables",this.draggables);
console.log("this.containers",this.containers);
}
this.removeEventListeners = function(name){
if(name === 'container' && this.containers){
this.containers.forEach(container => {
container.removeEventListener('dragover',e => this.onDragHover(e,container),false);
container.removeEventListener('drop', e => this.onDragDrop(e),false);
container.removeEventListener('dragleave',e => this.onDragLeave(e),false);
container.removeEventListener('dragenter',e => this.onDragEnter(e),false);
});
}else if(name === 'draggables' && this.draggables){
this.draggables.forEach(draggable => {
draggable.removeEventListener('dragstart',e => this.onDragStart(e,draggable),false);
draggable.removeEventListener('dragend', e => this.onDragEnd(e,draggable),false);
});
}else if(this.draggables && this.containers){
this.containers.forEach(container => {
container.removeEventListener('dragover',e => this.onDragHover(e,container),false);
container.removeEventListener('drop',e => this.onDragDrop(e),false);
container.removeEventListener('dragleave',e => this.onDragLeave(e),false);
container.removeEventListener('dragenter',e => this.onDragEnter(e),false);
});
this.draggables.forEach(draggable => {
draggable.removeEventListener('dragstart',e => this.onDragStart(e),false);
draggable.removeEventListener('dragend',e => this.onDragEnd(e),false);
});
}
}
this.createPlaceHolder = function(){
let placeholder = document.createElement('div');
placeholder.style.height = '50px';
placeholder.style.borderRadius = '5px';
placeholder.style.backgroundColor = '#eee';
placeholder.style.margin = '10px 0';
this.placeholder = placeholder;
}
this.getDragAfterElement = function(container, y){
const draggableElements = [...container.querySelectorAll('.draggable:not(.dragging)')]
return draggableElements.reduce((closest, child) => {
const box = child.getBoundingClientRect()
const offset = y - box.top - box.height / 2
if (offset < 0 && offset > closest.offset) {
return { offset: offset, element: child }
} else {
return closest
}
}, { offset: Number.NEGATIVE_INFINITY }).element
}
this.onDragStart = function(e,draggable){
e.stopPropagation();
let elementToInsert;
console.log("drag start",draggable);
// e.dataTransfer.setData('elementid',e.target.id);
if(draggable && draggable.getAttribute('name')){
let element = draggable.getAttribute('name');
switch(element){
case 'text-field':
elementToInsert = document.createElement('input');
elementToInsert.classList.add('draggable');
elementToInsert.setAttribute('draggable',true);
elementToInsert.setAttribute('placeholder','Text');
elementToInsert.setAttribute('disabled',true);
elementToInsert.setAttribute('id',`field-${Date.now()}`);
this.elementToInsert = elementToInsert;
this.existingElement = false;
break;
case 'email-field':
elementToInsert = document.createElement('input');
elementToInsert.classList.add('draggable');
elementToInsert.setAttribute('draggable',true);
elementToInsert.setAttribute('placeholder','Email');
elementToInsert.setAttribute('disabled',true);
elementToInsert.setAttribute('id',`field-${Date.now()}`);
this.elementToInsert = elementToInsert;
this.existingElement = false;
break;
case 'phone-field':
elementToInsert = document.createElement('input');
elementToInsert.classList.add('draggable');
elementToInsert.setAttribute('draggable',true);
elementToInsert.setAttribute('placeholder','Phone');
elementToInsert.setAttribute('disabled',true);
elementToInsert.setAttribute('id',`field-${Date.now()}`);
this.elementToInsert = elementToInsert;
this.existingElement = false;
break;
default:
this.elementToInsert = draggable;
this.existingElement = true;
break;
}
}else{
this.existingElement = true;
this.elementToInsert = draggable;
}
this.placeholder.setAttribute("id",`placeholder-${Date.now()}`);
draggable.classList.add('dragging');
}
this.onDragEnd = function(e,draggable){
draggable.classList.remove('dragging');
console.log("drag end",this.elementToInsert);
if(!this.existingElement){
this.addEventListenerForDraggableItem(this.elementToInsert);
this.updateDraggables('draggables');
}else{
console.log("existing ele",this.elementToInsert);
this.elementToInsert.classList.remove('dragging');
}
}
this.addEventListenerForDraggableItem = function(element){
console.log("ele",element);
element.addEventListener('dragstart',e => this.onDragStart(e,element));
element.addEventListener('dragend',e => this.onDragEnd(e,element));
}
this.addEventListenersForContainer = function(container){
container.addEventListener('dragover',e => this.onDragHover(e,container,false));
container.addEventListener('drop',e => this.onDragDrop(e),false);
container.addEventListener('dragleave',e => this.onDragLeave(e),false);
container.addEventListener('dragenter',e => this.onDragEnter(e),false);
}
this.onDragHover = function(e,container){
e.preventDefault();
this.afterElement = this.getDragAfterElement(container, e.clientY);
if (this.afterElement == null) {
container.appendChild(this.placeholder)
} else {
container.insertBefore(this.placeholder, this.afterElement)
}
}
this.onDragEnter = function(e){
e.preventDefault();
}
this.onDragLeave = function(e){
e.preventDefault();
console.log("on drag leave");
}
this.onDragDrop = function(e){
e.preventDefault()
// let data = e.dataTransfer.getData("elementid");
this.placeholder.replaceWith(this.elementToInsert);
}
this.init = function(){
try{
this.mainContainer = document.getElementById('main-container');
this.updateContainers();
this.updateDraggables();
this.addEventListeners();
this.createPlaceHolder();
}catch(e){
console.log(e);
}
}
}
let customDragnDrop = new CustomDragAndDrop();
customDragnDrop.init();
body {
margin: 0;
}
h1{
color:#fff;
}
.container {
background-color: #333;
padding: 1rem;
margin: 1rem;
width: 200px;
}
.draggable {
padding: 1rem;
background-color: white;
border: 1px solid black;
cursor: move;
}
.draggable.dragging {
opacity: .5;
}
#main-container{
display: flex;
}
input{
display: block;
width:83%;
height: 0px;
}
.main{
display: flex;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
<link rel="stylesheet" type="text/css" href="index.css" >
</head>
<body>
<div class="main">
<div>
<div class="draggable" id="text" name="text-field" draggable="true">Text Field</div>
<div class="draggable" id="email" name="email-field" draggable="true">Email Field</div>
<div class="draggable" id="phone" name="phone-field" draggable="true">Phone Field</div>
</div>
<div id="main-container">
<!-- <div class="container">
<h1>Section 1</h1>
<p class="draggable" draggable="true">1</p>
<p class="draggable" draggable="true">2</p>
</div>
<div class="container">
<h1>Section 2</h1>
<p class="draggable" draggable="true">3</p>
<p class="draggable" draggable="true">4</p>
</div> -->
</div>
</div>
<button onclick="customDragnDrop.addSection()">Add section</button>
<script src="index.js"></script>
</body>
</html>