commit
5edba0bd6a
12 changed files with 72163 additions and 0 deletions
@ -0,0 +1,2 @@ |
|||
# csv-data-generator |
|||
A client side website that allows the user to create CSV data that is meaningful that can be used as data source for any data requirements |
@ -0,0 +1 @@ |
|||
./csv-data-generator.sh |
@ -0,0 +1,5 @@ |
|||
#!/usr/bin/env bash |
|||
|
|||
dir="$(dirname $(readlink -f $0))" |
|||
cd "$dir" |
|||
serve ./ |
After Width: | Height: | Size: 694 KiB |
After Width: | Height: | Size: 510 KiB |
@ -0,0 +1,143 @@ |
|||
<!DOCTYPE html> |
|||
<html lang="en"> |
|||
|
|||
<head> |
|||
<meta charset="UTF-8"> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|||
<title>CSV File Generator</title> |
|||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/all.min.css" |
|||
integrity="sha256-+N4/V/SbAFiW1MPBCXnfnP9QSN3+Keu+NlB+0ev/YKQ=" crossorigin="anonymous" /> |
|||
<link rel="stylesheet" href="./style.css"> |
|||
</head> |
|||
|
|||
<body> |
|||
<!-- Intro container with a brief description of the Application --> |
|||
<div class="introduction"> |
|||
<h1>CSV File Generator</h1> |
|||
<p class='intro-details'>Welcome to the one stop solution for all your data generating needs. The CSV Data |
|||
Generator can generate a large amount of meaningul data depending on your data requirements in almost no |
|||
time. |
|||
<br><br> |
|||
It's a simple 4 step quick process |
|||
<br> |
|||
  Step 1) Add as many columns as you want |
|||
<br> |
|||
  Step 2) Set the type of data you want for each column |
|||
<br> |
|||
  Step 3) Put in the number of rows you want to generate |
|||
<br> |
|||
  Step 4) Generate the File |
|||
<br><br> |
|||
No Installations, No Sign-ins, No Hiccups. |
|||
<br> |
|||
<small class="intro-footer">This works best on a Chrome/Firefox Browser</small> |
|||
</p> |
|||
<button class="btn" id='getStarted'>Get Started</button> |
|||
</div> |
|||
<!-- Main Content of the Website --> |
|||
<div id="main-content"> |
|||
<!-- Header --> |
|||
<div class="header-section cover"> |
|||
<h1>CSV File Generator</h1> |
|||
<div class="info">Easy, Customizable, Open Source, Fast & Free</div> |
|||
</div> |
|||
<!-- Main Container with the table, buttons and QnA --> |
|||
<div class="main-container"> |
|||
<div class="data-input-container"> |
|||
<div class="row header" id="tableColNames"> |
|||
<div class="column">Column Name</div> |
|||
<div class="column">Type of Data</div> |
|||
<div class="column actions"></div> |
|||
</div> |
|||
</div> |
|||
<div class="columns"> |
|||
</div> |
|||
<div class="config"> |
|||
<div class='row-count-input'> |
|||
<label for="row-count">Number of Rows</label> |
|||
<div class="help-tip"> |
|||
<p>Enter the total number of rows that you want to generate</p> |
|||
</div> |
|||
<input type="number" class="row-count" value="1000" name="row-count" id="row-count" max="1000"> |
|||
|
|||
</div> |
|||
<div class='buttons'> |
|||
<!-- <button class="btn" id="view-sample">View Sample</button> --> |
|||
<button class="btn" id="generate-button">Generate File</button> |
|||
<!-- <button class="btn" id="generate-SQL">Generate SQL Script</button> --> |
|||
</div> |
|||
</div> |
|||
<div class="additional-details"> |
|||
<h3>Why generate test data?</h3> |
|||
<p>You should always have a good |
|||
volume of mocked up data when testing your application. This will help catch performance issues in |
|||
your code |
|||
early on in the cycle as well as issues with the UI. Maybe you need to use a larger width to display |
|||
the name of your customer because you saw that some customers have long names which are getting |
|||
trimmed. Or maybe you see that you dont have enough space in your DB for the addresses you want to |
|||
store. |
|||
</p> |
|||
<h3>Is having realistic data really important?</h3> |
|||
<p>If you are trying to showcase your project as a real time application, it adds a lot of value to |
|||
actually show realistic names, addresses and data instead of random alphabets typed out in a hurry |
|||
by you. Creating |
|||
data and testing it on your app will also add realstic issues like an apostrophe in a name or |
|||
address which your code might not be able to handle. |
|||
</p> |
|||
<h3>Why use CSV Data Generator?</h3> |
|||
<p>The CSV Data Generator is Light, Easy and Customizable with absolutely NO registration. Other data |
|||
generators available either do not have the various options provided here OR try and get you to sign |
|||
in and pay for the more advanced features which are available here for absolutely free!! |
|||
<br> |
|||
This is a light-weight solution and all the data gets created entirely on the client side using |
|||
Vanilla Javascript. |
|||
</p> |
|||
<h3>How many rows/columns can I generate?</h3> |
|||
<p>The CSV Data Generator is a light weight application that has been built using vanilla Javascript. |
|||
You can create as much data as you like but your browser could crash if you are trying to create 1 |
|||
trillion records for a bunch of columns! So go easy :) |
|||
</p> |
|||
<h3>How can I see the code?</h3> |
|||
<p>Head right down to the github repository in the footer and check it out |
|||
</p> |
|||
</div> |
|||
</div> |
|||
<!-- The popup displaying all the options --> |
|||
<div class="select-input-background"> |
|||
|
|||
<div class="select-input-container"> |
|||
<div class="select-input-classification"> |
|||
<h2>Classification of Data</h2> |
|||
</div> |
|||
<div class="select-input-options"> |
|||
</div> |
|||
<button class="close" id="closeInputSelector">X</button> |
|||
</div> |
|||
</div> |
|||
<!-- Footer with contact details --> |
|||
<footer> |
|||
<section id="contact" class="contact"> |
|||
<div class="profile-picture"> |
|||
<a href="http://www.markbdsouza.com" target="_blank"> |
|||
<img class="display-pic" src="images/Mark DSouza.jpeg" alt="Mark Dsouza"> |
|||
</a> |
|||
</div> |
|||
<div class="contact-section"> |
|||
<span class="">This open source application was developed by <br> Mark Dsouza </span> |
|||
<div class="contact-details"> |
|||
<a href="https://www.linkedin.com/in/mark-dsouza-44692095/" class="" target="_blank"><i |
|||
class="fab fa-linkedin-in"></i></a> |
|||
<a href="https://twitter.com/MarkBDsouza" class="" target="_blank"><i |
|||
class="fab fa-twitter"></i></a> |
|||
<a href="https://github.com/markbdsouza/csv-data-generator" class="" target="_blank"> |
|||
<i class="fab fa-github"></i></a> |
|||
</div> |
|||
</div> |
|||
</section> |
|||
</footer> |
|||
</div> |
|||
<script src="./scripts/faker.js" type="text/javascript"></script> |
|||
<script src="./scripts/script.js" type="module"></script> |
|||
</body> |
|||
|
|||
</html> |
@ -0,0 +1,32 @@ |
|||
function generateDataArrayForColumn(totalRowCount, columnConfig) { |
|||
let result = []; |
|||
let columnCountArray = Array.from(Array(totalRowCount), (x, i) => i); |
|||
columnCountArray.forEach((x, i) => { |
|||
result.push(columnConfig.getData()); |
|||
}); |
|||
return result; |
|||
} |
|||
|
|||
function createHeaders(dataConfig) { |
|||
let headers = dataConfig.map((a) => a.columnName); |
|||
return headers; |
|||
} |
|||
|
|||
export function createExcelRowData(totalRowCount, dataConfig) { |
|||
const rows = []; |
|||
rows.push(createHeaders(dataConfig)); |
|||
let createDataSet = []; |
|||
dataConfig.forEach((columnConfig) => { |
|||
createDataSet.push(generateDataArrayForColumn(totalRowCount, columnConfig)); |
|||
}); |
|||
|
|||
for (let i = 0; i < totalRowCount; i++) { |
|||
let rowData = []; |
|||
createDataSet.forEach((columnData) => { |
|||
rowData.push(columnData[i]); |
|||
}); |
|||
rows.push(rowData); |
|||
} |
|||
|
|||
return rows; |
|||
} |
File diff suppressed because it is too large
@ -0,0 +1,34 @@ |
|||
function downloadFile(csvFile, filename) { |
|||
var blob = new Blob([csvFile], { type: 'text/csv;charset=utf-8;' }); |
|||
if (navigator.msSaveBlob) { |
|||
//for IE
|
|||
navigator.msSaveBlob(blob, filename); |
|||
} else { |
|||
//Chrome and Firefox
|
|||
var link = document.createElement('a'); |
|||
if (link.download !== undefined) { |
|||
var url = URL.createObjectURL(blob); |
|||
link.setAttribute('href', url); |
|||
link.setAttribute('download', filename); |
|||
link.style.visibility = 'hidden'; |
|||
document.body.appendChild(link); |
|||
link.click(); |
|||
document.body.removeChild(link); |
|||
} |
|||
} |
|||
} |
|||
|
|||
export function generateFile(rows) { |
|||
let csvFile = ''; |
|||
var d = new Date(); |
|||
|
|||
let filename = `CSV_Data_${d.getFullYear()}_${ |
|||
d.getMonth() + 1 |
|||
}_${d.getDate()} ${d.getHours()}:${d.getMinutes()}.csv`;
|
|||
|
|||
rows.forEach(function (rowArray) { |
|||
let row = rowArray.join(','); |
|||
csvFile += row + '\r\n'; |
|||
}); |
|||
downloadFile(csvFile, filename); |
|||
} |
@ -0,0 +1,481 @@ |
|||
export const ALL_OPTIONS = [ |
|||
// name
|
|||
{ |
|||
name: 'First Name', |
|||
value: 'firstName', |
|||
type: 'Name', |
|||
getData() { |
|||
return faker.name.firstName(); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'Last Name', |
|||
value: 'lastName', |
|||
type: 'Name', |
|||
getData() { |
|||
return faker.name.lastName(); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'Full Name', |
|||
value: 'findName', |
|||
type: 'Name', |
|||
getData() { |
|||
return faker.name.findName(); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'Job Title', |
|||
value: 'jobTitle', |
|||
type: 'Name', |
|||
getData() { |
|||
return faker.name.jobTitle(); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'Name Prefix', |
|||
value: 'prefix', |
|||
type: 'Name', |
|||
getData() { |
|||
return faker.name.prefix(); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'Name Suffix', |
|||
value: 'suffix', |
|||
type: 'Name', |
|||
getData() { |
|||
return faker.name.suffix(); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'Name Title', |
|||
value: 'title', |
|||
type: 'Name', |
|||
getData() { |
|||
return faker.name.title(); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'Job Descriptor', |
|||
value: 'jobDescriptor', |
|||
type: 'Name', |
|||
getData() { |
|||
return faker.name.jobDescriptor(); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'Job Area', |
|||
value: 'jobArea', |
|||
type: 'Name', |
|||
getData() { |
|||
return faker.name.jobArea(); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'Job Type', |
|||
value: 'jobType', |
|||
type: 'Name', |
|||
getData() { |
|||
return faker.name.jobType(); |
|||
}, |
|||
}, |
|||
|
|||
//Address
|
|||
{ |
|||
name: 'Street Name', |
|||
value: 'streetName', |
|||
type: 'Address', |
|||
getData() { |
|||
return faker.address.streetName(); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'Street Address', |
|||
value: 'streetAddress', |
|||
type: 'Address', |
|||
getData() { |
|||
return faker.address.streetAddress(); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'Secondary Address', |
|||
value: 'secondaryAddress', |
|||
type: 'Address', |
|||
getData() { |
|||
return faker.address.secondaryAddress(); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'Zip Code', |
|||
value: 'zipCode', |
|||
type: 'Address', |
|||
getData() { |
|||
return faker.address.zipCode(); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'County', |
|||
value: 'county', |
|||
type: 'Address', |
|||
getData() { |
|||
return faker.address.county(); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'City', |
|||
value: 'city', |
|||
type: 'Address', |
|||
getData() { |
|||
return faker.address.city(); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'State', |
|||
value: 'state', |
|||
type: 'Address', |
|||
getData() { |
|||
return faker.address.state(); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'State Abbr', |
|||
value: 'stateAbbr', |
|||
type: 'Address', |
|||
getData() { |
|||
return faker.address.stateAbbr(); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'Country', |
|||
value: 'country', |
|||
type: 'Address', |
|||
getData() { |
|||
return faker.address.country(); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'Country Code', |
|||
value: 'countryCode', |
|||
type: 'Address', |
|||
getData() { |
|||
return faker.address.countryCode(); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'Latitude', |
|||
value: 'latitude', |
|||
type: 'Address', |
|||
getData() { |
|||
return faker.address.latitude(); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'Longitude', |
|||
value: 'longitude', |
|||
type: 'Address', |
|||
getData() { |
|||
return faker.address.longitude(); |
|||
}, |
|||
}, |
|||
//Company
|
|||
|
|||
{ |
|||
name: 'Company Name', |
|||
value: 'companyName', |
|||
// type: 'company',
|
|||
type: 'Finance', |
|||
getData() { |
|||
return faker.company.companyName(); |
|||
}, |
|||
}, |
|||
// {
|
|||
// name: 'bs',
|
|||
// value: 'bs',
|
|||
// type: 'company',
|
|||
// getData() {
|
|||
// return faker.company.bs();
|
|||
// },
|
|||
// },
|
|||
// {
|
|||
// name: 'companySuffix',
|
|||
// value: 'companySuffix',
|
|||
// type: 'company',
|
|||
// getData() {
|
|||
// return faker.company.companySuffix();
|
|||
// },
|
|||
// },
|
|||
// {
|
|||
// name: 'catchPhraseAdjective',
|
|||
// value: 'catchPhraseAdjective',
|
|||
// type: 'company',
|
|||
// getData() {
|
|||
// return faker.company.catchPhraseAdjective();
|
|||
// },
|
|||
// },
|
|||
|
|||
//finance
|
|||
{ |
|||
name: 'Account', |
|||
value: 'account', |
|||
type: 'Finance', |
|||
getData() { |
|||
return faker.finance.account(); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'Account Name', |
|||
value: 'accountName', |
|||
type: 'Finance', |
|||
getData() { |
|||
return faker.finance.accountName(); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'Currency Code', |
|||
value: 'currencyCode', |
|||
type: 'Finance', |
|||
getData() { |
|||
return faker.finance.currencyCode(); |
|||
}, |
|||
}, |
|||
|
|||
{ |
|||
name: 'Currency Name', |
|||
value: 'currencyName', |
|||
type: 'Finance', |
|||
getData() { |
|||
return faker.finance.currencyName(); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'Currency Symbol', |
|||
value: 'currencySymbol', |
|||
type: 'Finance', |
|||
getData() { |
|||
return faker.finance.currencySymbol(); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'Bitcoin Address', |
|||
value: 'bitcoinAddress', |
|||
type: 'Finance', |
|||
getData() { |
|||
return faker.finance.bitcoinAddress(); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'IBAN', |
|||
value: 'iban', |
|||
type: 'Finance', |
|||
getData() { |
|||
return faker.finance.iban(); |
|||
}, |
|||
}, |
|||
//internet
|
|||
{ |
|||
name: 'Email', |
|||
value: 'email', |
|||
type: 'Internet', |
|||
getData() { |
|||
return faker.internet.email(); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'User Name', |
|||
value: 'userName', |
|||
type: 'Internet', |
|||
getData() { |
|||
return faker.internet.userName(); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'URL', |
|||
value: 'url', |
|||
type: 'Internet', |
|||
getData() { |
|||
return faker.internet.url(); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'Domain Name', |
|||
value: 'domainName', |
|||
type: 'Internet', |
|||
getData() { |
|||
return faker.internet.domainName(); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'IP Address', |
|||
value: 'ip', |
|||
type: 'Internet', |
|||
getData() { |
|||
return faker.internet.ip(); |
|||
}, |
|||
}, |
|||
|
|||
//Date
|
|||
{ |
|||
name: 'Past Date', |
|||
value: 'past', |
|||
type: 'Date', |
|||
getData() { |
|||
return faker.date.past(); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'Future Date', |
|||
value: 'future', |
|||
type: 'Date', |
|||
getData() { |
|||
return faker.date.future(); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'Recent Date', |
|||
value: 'recent', |
|||
type: 'Date', |
|||
getData() { |
|||
return faker.date.recent(); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'Month', |
|||
value: 'month', |
|||
type: 'Date', |
|||
getData() { |
|||
return faker.date.month(); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'Weekday', |
|||
value: 'weekday', |
|||
type: 'Date', |
|||
getData() { |
|||
return faker.date.weekday(); |
|||
}, |
|||
}, |
|||
//random
|
|||
|
|||
{ |
|||
name: 'UUID', |
|||
value: 'uuid', |
|||
type: 'Other', |
|||
getData() { |
|||
return faker.random.uuid(); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'Boolean', |
|||
value: 'boolean', |
|||
type: 'Other', |
|||
getData() { |
|||
return faker.random.boolean(); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'word', |
|||
value: 'word', |
|||
type: 'Other', |
|||
getData() { |
|||
return faker.random.word(); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'Random Character', |
|||
value: 'randomCharacter', |
|||
type: 'Other', |
|||
getData() { |
|||
let characters = |
|||
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; |
|||
return characters[Math.floor(Math.random() * characters.length)]; |
|||
}, |
|||
}, |
|||
// {
|
|||
// name: 'Sequence Generated Integer',
|
|||
// value: 'index',
|
|||
// index: 0,
|
|||
// type: 'a0',
|
|||
// getData: function () {
|
|||
// return this.index;
|
|||
// },
|
|||
// },
|
|||
{ |
|||
name: 'Random Integer', |
|||
value: 'randomInteger', |
|||
type: 'Other', |
|||
getData() { |
|||
return Math.floor(Math.random() * 10000); |
|||
}, |
|||
}, |
|||
|
|||
//Commerce
|
|||
{ |
|||
name: 'Color', |
|||
value: 'color', |
|||
// type: 'commerce',
|
|||
type: 'Other', |
|||
getData() { |
|||
return faker.commerce.color(); |
|||
}, |
|||
}, |
|||
// {
|
|||
// name: 'Price',
|
|||
// value: 'price',
|
|||
// type: 'commerce',
|
|||
// getData() {
|
|||
// return faker.commerce.price();
|
|||
// },
|
|||
// },
|
|||
{ |
|||
name: 'Product', |
|||
value: 'product', |
|||
type: 'Other', |
|||
getData() { |
|||
return faker.commerce.product(); |
|||
}, |
|||
}, |
|||
//database
|
|||
// {
|
|||
// name: 'DataBase Column',
|
|||
// value: 'column',
|
|||
// type: 'database',
|
|||
// getData() {
|
|||
// return faker.database.column();
|
|||
// },
|
|||
// },
|
|||
|
|||
{ |
|||
name: 'Database Column Type', |
|||
value: 'type', |
|||
type: 'Other', |
|||
getData() { |
|||
return faker.database.type(); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'Database Collation', |
|||
value: 'collation', |
|||
type: 'Other', |
|||
getData() { |
|||
return faker.database.collation(); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'Database Engine', |
|||
value: 'engine', |
|||
type: 'Other', |
|||
getData() { |
|||
return faker.database.engine(); |
|||
}, |
|||
}, |
|||
|
|||
{ |
|||
name: 'Phone', |
|||
value: 'phone', |
|||
type: 'Other', |
|||
getData() { |
|||
return faker.phone.phoneNumber(); |
|||
}, |
|||
}, |
|||
]; |
@ -0,0 +1,189 @@ |
|||
import { generateFile } from './filegeneration.js'; |
|||
import { createExcelRowData } from './datageneration.js'; |
|||
import { ALL_OPTIONS } from './options.js'; |
|||
|
|||
const generateBtn = document.getElementById('generate-button'); |
|||
const rowCountEl = document.getElementById('row-count'); |
|||
const dataInputContainer = document.querySelector('.data-input-container'); |
|||
const introduction = document.querySelector('.introduction'); |
|||
const getStarted = document.getElementById('getStarted'); |
|||
const mainContent = document.getElementById('main-content'); |
|||
const closeBtnForInputSelector = document.getElementById('closeInputSelector'); |
|||
const inputSelectorBkg = document.querySelector('.select-input-background'); |
|||
const selectGroupingOptions = document.querySelector( |
|||
'.select-input-classification' |
|||
); |
|||
const selectInputContainer = document.querySelector('.select-input-options'); |
|||
|
|||
const ROWS_TO_START_WITH = 8; |
|||
let DOMrows = []; |
|||
let i = 0; |
|||
let selectedRow; |
|||
|
|||
function addValuesToOptions(filter) { |
|||
selectInputContainer.innerHTML = ''; |
|||
ALL_OPTIONS.forEach((option) => { |
|||
if (!filter || option.type === filter) { |
|||
let optionEl = document.createElement('button'); |
|||
optionEl.classList.add('btn'); |
|||
optionEl.classList.add('selectOption'); |
|||
optionEl.dataset.value = option.value; |
|||
optionEl.innerText = option.name; |
|||
optionEl.addEventListener('click', (e) => { |
|||
//add value to current row
|
|||
selectedRow.querySelector( |
|||
'.inputSelection' |
|||
).innerHTML = `${e.target.textContent} <i class="fa fa-angle-down"></i>`; |
|||
selectedRow.querySelector('.inputSelection').dataset.value = |
|||
e.target.dataset.value; |
|||
inputSelectorBkg.classList.remove('show'); |
|||
}); |
|||
selectInputContainer.appendChild(optionEl); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
function returnGetDataFunction(selectedOptionName) { |
|||
let selectedOptionObject = ALL_OPTIONS.filter((option) => { |
|||
return option.value === selectedOptionName; |
|||
}); |
|||
return selectedOptionObject[0].getData; |
|||
} |
|||
|
|||
function fetchDOMValues() { |
|||
let dataConfig = []; |
|||
const rows = document.querySelectorAll('.row'); |
|||
rows.forEach((row, index) => { |
|||
// to exclude the header which is the first row
|
|||
|
|||
if (index >= 1) { |
|||
dataConfig.push({ |
|||
columnName: row.querySelector('.columnName').value, |
|||
type: row.querySelector('.inputSelection').dataset.value, |
|||
getData: returnGetDataFunction( |
|||
row.querySelector('.inputSelection').dataset.value |
|||
), |
|||
maxLength: 100, |
|||
minLength: 0, |
|||
nullable: false, |
|||
}); |
|||
} |
|||
}); |
|||
return dataConfig; |
|||
} |
|||
|
|||
function convertToColumnName(name) { |
|||
return name.toUpperCase().split(' ').join('_'); |
|||
} |
|||
|
|||
function addToDOMRows(rowClicked) { |
|||
let randomOption = |
|||
ALL_OPTIONS[Math.floor(Math.random() * ALL_OPTIONS.length)]; |
|||
let newRow = document.createElement('div'); |
|||
newRow.classList.add('row'); |
|||
newRow.classList.add('hide'); |
|||
newRow.innerHTML = `
|
|||
<div class="column"> |
|||
<input type="text" class="columnName" value="${convertToColumnName( |
|||
randomOption.name |
|||
)}"> |
|||
</div> |
|||
<div class="column"> |
|||
<button class="inputSelection" data-value="${randomOption.value}">${ |
|||
randomOption.name |
|||
}<i class="fa fa-angle-down"></i> </button> |
|||
</div> |
|||
<div class="column actions"> |
|||
<button class="add"> <i class="far fa-plus-square fa-2x add"></i><span class="tooltiptext">Add a New Row</span></button> |
|||
<button class="delete"> <i class="fas fa-trash-alt fa-2x delete"></i><span class="tooltiptext">Delete Existing Row</span></button> |
|||
</div>`; |
|||
|
|||
DOMrows.push(newRow); |
|||
if (rowClicked) { |
|||
rowClicked.insertAdjacentElement('afterend', newRow); |
|||
} else { |
|||
dataInputContainer.insertAdjacentElement('beforeend', newRow); |
|||
} |
|||
setTimeout(() => { |
|||
newRow.classList.remove('hide'); |
|||
}, 100); |
|||
i++; |
|||
} |
|||
|
|||
function displayInputSelection(row) { |
|||
inputSelectorBkg.classList.add('show'); |
|||
selectedRow = row; |
|||
} |
|||
|
|||
function deleteRowFromDOM(parentRow) { |
|||
parentRow.classList.add('hide'); |
|||
setTimeout(() => { |
|||
dataInputContainer.removeChild(parentRow); |
|||
DOMrows = DOMrows.filter((row) => { |
|||
return row !== parentRow; |
|||
}); |
|||
if (DOMrows.length === 0) { |
|||
addToDOMRows(); |
|||
} |
|||
}, 600); |
|||
} |
|||
|
|||
function loadMainContainer() { |
|||
introduction.classList.add('hide'); |
|||
mainContent.style.display = 'inline'; |
|||
setTimeout(() => { |
|||
introduction.style.display = 'none'; |
|||
}, 1000); |
|||
} |
|||
|
|||
function documentClick(e) { |
|||
const parentRow = e.target.closest('.row'); |
|||
if (e.target.classList.contains('add')) { |
|||
addToDOMRows(parentRow); |
|||
} |
|||
if (e.target.classList.contains('delete')) { |
|||
deleteRowFromDOM(parentRow); |
|||
} |
|||
if (e.target.classList.contains('inputSelection')) { |
|||
displayInputSelection(parentRow); |
|||
} |
|||
} |
|||
|
|||
function fetchDataAndGenerateFile() { |
|||
let rowCount = +rowCountEl.value; |
|||
let dataConfig = fetchDOMValues(); |
|||
let rows = createExcelRowData(rowCount, dataConfig); |
|||
generateFile(rows); |
|||
} |
|||
|
|||
//event listeners
|
|||
generateBtn.addEventListener('click', fetchDataAndGenerateFile); |
|||
document.addEventListener('click', documentClick); |
|||
getStarted.addEventListener('click', loadMainContainer); |
|||
closeBtnForInputSelector.addEventListener('click', () => { |
|||
inputSelectorBkg.classList.remove('show'); |
|||
}); |
|||
|
|||
for (let i = 0; i < ROWS_TO_START_WITH; i++) { |
|||
addToDOMRows(); |
|||
} |
|||
addValuesToOptions(); |
|||
|
|||
let GROUPED_OPTIONS = {}; |
|||
ALL_OPTIONS.forEach((option) => { |
|||
if (!GROUPED_OPTIONS[option.type]) { |
|||
GROUPED_OPTIONS[option.type] = []; |
|||
} |
|||
GROUPED_OPTIONS[option.type].push(option); |
|||
}); |
|||
|
|||
for (let key in GROUPED_OPTIONS) { |
|||
let group = document.createElement('div'); |
|||
group.className = 'optionGroup'; |
|||
group.innerText = `${key}(${GROUPED_OPTIONS[key].length})`; |
|||
group.addEventListener('click', () => { |
|||
console.log('clicked'); |
|||
addValuesToOptions(key); |
|||
}); |
|||
selectGroupingOptions.appendChild(group); |
|||
} |
@ -0,0 +1,621 @@ |
|||
@import url('https://fonts.googleapis.com/css2?family=Lato&display=swap'); |
|||
|
|||
:root { |
|||
--primary-color: rgb(17, 153, 158); |
|||
--secondary-color: rgb(228, 249, 245); |
|||
--btn-bg: rgba(255, 77, 77, 0.4); |
|||
--text-color: rgba(10, 10, 10, 1); |
|||
--option-1: rgb(48, 227, 202); |
|||
--option-1-faded: rgba(48, 227, 202, 0.2); |
|||
--option-2: rgb(64, 81, 78); |
|||
--option-2-faded: rgba(64, 81, 78, 0.6); |
|||
--background-img: linear-gradient( |
|||
to top left, |
|||
var(--primary-color), |
|||
var(--option-2) |
|||
); |
|||
} |
|||
|
|||
* { |
|||
box-sizing: border-box; |
|||
margin: 0; |
|||
padding: 0; |
|||
} |
|||
|
|||
html { |
|||
font-size: 10px; |
|||
} |
|||
|
|||
body { |
|||
position: relative; |
|||
min-height: 100vh; |
|||
height: 100%; |
|||
font-family: 'Lato', sans-serif; |
|||
background-color: var(--secondary-color); |
|||
color: var(--text-color); |
|||
margin: 0; |
|||
padding: 0; |
|||
} |
|||
|
|||
/* Intro container with a brief description of the Application */ |
|||
.introduction { |
|||
position: absolute; |
|||
top: 0; |
|||
left: 0; |
|||
right: 0; |
|||
height: 100vh; |
|||
z-index: 1; |
|||
margin: 0; |
|||
color: white; |
|||
display: flex; |
|||
flex-direction: column; |
|||
justify-content: center; |
|||
align-items: center; |
|||
background: url('./images/campaign-creators-pypeCEaJeZY-unsplash.jpg') center |
|||
center no-repeat; |
|||
z-index: 1; |
|||
transition: all 1s ease-out; |
|||
opacity: 1; |
|||
} |
|||
|
|||
.introduction.hide { |
|||
opacity: 0; |
|||
} |
|||
|
|||
.introduction::after { |
|||
content: ''; |
|||
position: absolute; |
|||
top: 0; |
|||
left: 0; |
|||
right: 0; |
|||
bottom: 0; |
|||
background-color: rgba(0, 0, 0, 0.6); |
|||
} |
|||
|
|||
.introduction button { |
|||
z-index: 1; |
|||
display: block; |
|||
width: 40%; |
|||
padding: 2rem; |
|||
font-size: 2rem; |
|||
} |
|||
|
|||
.introduction h1 { |
|||
z-index: 1; |
|||
font-size: 4rem; |
|||
} |
|||
|
|||
.introduction .intro-details { |
|||
width: 50%; |
|||
text-align: justify; |
|||
line-height: 1.7; |
|||
font-size: 2rem; |
|||
padding: 3rem 0 2rem; |
|||
color: white; |
|||
z-index: 1; |
|||
} |
|||
|
|||
.introduction .intro-footer { |
|||
position: absolute; |
|||
bottom: 5px; |
|||
left: 20px; |
|||
} |
|||
|
|||
#main-content { |
|||
display: none; |
|||
} |
|||
|
|||
/*Header */ |
|||
.header-section { |
|||
display: Flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
margin-bottom: 1rem; |
|||
padding: 2rem 5rem; |
|||
background-image: var(--background-img); |
|||
color: var(--secondary-color); |
|||
font-weight: bold; |
|||
transition: all 1s ease-in; |
|||
transform: translateY(0); |
|||
font-size: 2rem; |
|||
} |
|||
|
|||
.header-section .info { |
|||
letter-spacing: 1.3px; |
|||
} |
|||
|
|||
/* The main container holds the configuration and the table container*/ |
|||
.main-container { |
|||
display: Flex; |
|||
flex-direction: column; |
|||
justify-content: center; |
|||
align-items: center; |
|||
padding: 1rem; |
|||
} |
|||
|
|||
/* Number of rows and generate File Button */ |
|||
.config { |
|||
padding: 2rem 3rem; |
|||
border-radius: 1rem; |
|||
margin-bottom: 1rem; |
|||
font-size: 1.6rem; |
|||
display: flex; |
|||
flex-direction: row; |
|||
position: relative; |
|||
} |
|||
|
|||
.config .row-count-input { |
|||
display: flex; |
|||
flex-direction: column; |
|||
padding: 5px; |
|||
position: relative; |
|||
width: inherit; |
|||
} |
|||
|
|||
.config input { |
|||
margin-top: 0.5rem; |
|||
width: 80%; |
|||
} |
|||
|
|||
.config .buttons { |
|||
margin-top: 1.5rem; |
|||
} |
|||
/* End of number of rows and generate file Button */ |
|||
|
|||
/* container and the entire column/row set */ |
|||
.data-input-container { |
|||
display: flex; |
|||
flex-direction: column; |
|||
width: 100%; |
|||
align-items: center; |
|||
} |
|||
|
|||
.header { |
|||
font-size: 2rem; |
|||
} |
|||
|
|||
.row { |
|||
display: flex; |
|||
max-width: 600px; |
|||
width: 75%; |
|||
align-items: center; |
|||
justify-content: center; |
|||
transition: all 0.6s ease-in-out; |
|||
transform: translateX(0%); |
|||
opacity: 1; |
|||
position: relative; |
|||
align-items: flex-start; |
|||
box-shadow: 2px 0px 3px var(--option-2); |
|||
} |
|||
.row.hide { |
|||
transform: translateX(-100%); |
|||
opacity: 0; |
|||
} |
|||
.row:nth-child(odd) { |
|||
background-color: var(--option-2-faded); |
|||
} |
|||
.row:nth-child(even) { |
|||
background-color: var(--option-1-faded); |
|||
} |
|||
.row:first-child { |
|||
background-color: var(--option-2); |
|||
color: rgba(223, 249, 251, 1); |
|||
border-top-left-radius: 10px; |
|||
border-top-right-radius: 10px; |
|||
} |
|||
.row:last-child { |
|||
border-bottom-left-radius: 10px; |
|||
border-bottom-right-radius: 10px; |
|||
box-shadow: 2px 2px 3px var(--option-2); |
|||
} |
|||
|
|||
.column { |
|||
width: 40%; |
|||
padding: 1rem; |
|||
margin: 0 1rem; |
|||
align-items: center; |
|||
justify-content: center; |
|||
text-align: center; |
|||
height: initial; |
|||
position: relative; |
|||
} |
|||
|
|||
.column.actions { |
|||
display: flex; |
|||
width: 20%; |
|||
align-items: center; |
|||
justify-content: center; |
|||
text-align: center; |
|||
} |
|||
|
|||
.actions button:first-child { |
|||
margin-right: 2rem; |
|||
} |
|||
|
|||
.actions button { |
|||
cursor: pointer; |
|||
display: relative; |
|||
border: 0; |
|||
margin: 0; |
|||
background-color: transparent; |
|||
outline: 0; |
|||
} |
|||
|
|||
.btn { |
|||
padding: 1rem 3rem; |
|||
margin: 1rem; |
|||
border-radius: 1rem; |
|||
cursor: pointer; |
|||
background-image: var(--background-img); |
|||
color: white; |
|||
font-size: 1.6rem; |
|||
border: 0; |
|||
border: 2px solid var(--btn-bg); |
|||
transform: translateX(0); |
|||
transition: transform 0.5s ease-in; |
|||
outline: 0; |
|||
box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.4); |
|||
} |
|||
|
|||
.btn:hover, |
|||
.btn:focus { |
|||
border: 2px solid var(--option-2-faded); |
|||
transform: translateY(-5px); |
|||
} |
|||
|
|||
/* Tool tip for Number of Rows */ |
|||
.help-tip { |
|||
position: absolute; |
|||
top: 18px; |
|||
right: 18px; |
|||
text-align: center; |
|||
background-color: #bcdbea; |
|||
border-radius: 50%; |
|||
width: 24px; |
|||
height: 24px; |
|||
font-size: 1.4rem; |
|||
line-height: 26px; |
|||
cursor: default; |
|||
} |
|||
|
|||
.help-tip:before { |
|||
content: '?'; |
|||
font-weight: bold; |
|||
color: #fff; |
|||
} |
|||
|
|||
.help-tip:hover p { |
|||
display: block; |
|||
transform-origin: 100% 0%; |
|||
-webkit-animation: fadeIn 0.3s ease-in-out; |
|||
animation: fadeIn 0.3s ease-in-out; |
|||
} |
|||
|
|||
.help-tip p { |
|||
/* The tooltip */ |
|||
display: none; |
|||
text-align: left; |
|||
background-image: var(--background-img); |
|||
padding: 1rem; |
|||
width: 300px; |
|||
position: absolute; |
|||
border-radius: 3px; |
|||
box-shadow: 1px 5px 5px rgba(0, 0, 0, 0.2); |
|||
right: -4px; |
|||
color: #fff; |
|||
font-size: 1, 5rem; |
|||
line-height: 1.4; |
|||
z-index: 2; |
|||
} |
|||
|
|||
.help-tip p:before { |
|||
/* The pointer of the tooltip */ |
|||
position: absolute; |
|||
content: ''; |
|||
width: 0; |
|||
height: 0; |
|||
border: 6px solid transparent; |
|||
border-bottom-color: #1e2021; |
|||
right: 10px; |
|||
top: -12px; |
|||
} |
|||
|
|||
.help-tip p:after { |
|||
/* Prevents the tooltip from being hidden */ |
|||
width: 100%; |
|||
height: 40px; |
|||
content: ''; |
|||
position: absolute; |
|||
top: -40px; |
|||
left: 0; |
|||
} |
|||
|
|||
/* For the Column Name and Option Grid Elements */ |
|||
input, |
|||
.inputSelection { |
|||
border-radius: 5px; |
|||
padding: 0.5rem 1rem; |
|||
font-size: 1.4rem; |
|||
width: 100%; |
|||
margin: 0; |
|||
border-radius: 0.5em; |
|||
box-shadow: 0 1px 0 1px rgba(0, 0, 0, 0.04); |
|||
background-color: #fff; |
|||
outline: none; |
|||
position: relative; |
|||
} |
|||
|
|||
.inputSelection { |
|||
cursor: pointer; |
|||
} |
|||
|
|||
input:focus, |
|||
.inputSelection:focus { |
|||
border-color: #aaa; |
|||
box-shadow: 0 0 1px 3px rgba(59, 153, 252, 0.7); |
|||
box-shadow: 0 0 0 3px -moz-mac-focusring; |
|||
color: #000; |
|||
} |
|||
|
|||
.inputSelection i { |
|||
position: absolute; |
|||
right: 0; |
|||
transform: translateX(-100%) translateY(15%); |
|||
pointer-events: none; |
|||
} |
|||
/* End of Column Name and Option Grid Elements */ |
|||
|
|||
/* CSS for the tool tip for Add and Delete Row */ |
|||
.actions button .tooltiptext { |
|||
visibility: hidden; |
|||
width: fit-content; |
|||
background-color: black; |
|||
color: #fff; |
|||
text-align: center; |
|||
padding: 8px 8px; |
|||
border-radius: 6px; |
|||
position: absolute; |
|||
z-index: 1000; |
|||
transform: translateX(-120%) translateY(40%); |
|||
} |
|||
|
|||
.actions button:hover .tooltiptext, |
|||
.actions button:focus .tooltiptext { |
|||
visibility: visible; |
|||
} |
|||
|
|||
/* Second section of the main container containing the questions and answers */ |
|||
.additional-details { |
|||
font-size: 1.5rem; |
|||
padding: 1rem 5rem; |
|||
line-height: 1.6; |
|||
} |
|||
|
|||
.additional-details h3, |
|||
.additional-details p { |
|||
margin-top: 1.5rem; |
|||
} |
|||
/* End of Second Section */ |
|||
|
|||
/* Option selector popup Start */ |
|||
.select-input-background { |
|||
display: none; |
|||
} |
|||
|
|||
.select-input-background.show { |
|||
position: absolute; |
|||
width: 100%; |
|||
height: 100%; |
|||
top: 0; |
|||
background-color: rgba(0, 0, 0, 0.4); |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: top; |
|||
} |
|||
|
|||
.select-input-background.show .select-input-container { |
|||
justify-content: space-between; |
|||
margin: auto; |
|||
background-color: rgba(255, 255, 255, 1); |
|||
border-radius: 1rem; |
|||
display: flex; |
|||
position: relative; |
|||
width: 70%; |
|||
top: 20vh; |
|||
position: absolute; |
|||
} |
|||
|
|||
.optionGroup { |
|||
font-size: 1.6rem; |
|||
cursor: pointer; |
|||
padding: 0.5rem 0 0.5rem; |
|||
} |
|||
|
|||
.select-input-container .selectOption { |
|||
margin: 0.3rem; |
|||
padding: 0.8rem 1.6rem; |
|||
} |
|||
|
|||
.select-input-container .select-input-classification { |
|||
height: max-content; |
|||
width: 25%; |
|||
padding: 1rem 2.4rem; |
|||
} |
|||
|
|||
.select-input-classification h2 { |
|||
padding-bottom: 1rem; |
|||
font-size: 1.8rem; |
|||
} |
|||
|
|||
.select-input-container .close { |
|||
cursor: pointer; |
|||
position: absolute; |
|||
top: 0; |
|||
right: 0; |
|||
font-size: 1.4rem; |
|||
font-weight: bold; |
|||
padding: 1rem 1.2rem; |
|||
border-radius: 50%; |
|||
background-color: #d9455f; |
|||
transform: translateX(50%) translateY(-50%) scale(1); |
|||
outline: none; |
|||
transition: all 1s ease-in; |
|||
outline: none; |
|||
} |
|||
|
|||
.select-input-background.show .select-input-container .close:hover { |
|||
transform: translateX(50%) translateY(-50%) scale(1.3); |
|||
} |
|||
|
|||
.select-input-options { |
|||
width: 75%; |
|||
padding: 1rem 2.6rem; |
|||
border-left: 4px solid var(--primary-color); |
|||
box-shadow: 2px 2px 5px var(--option-2); |
|||
border-radius: 1rem; |
|||
background-color: rgba(0, 0, 0, 0.2); |
|||
margin: 1rem 1rem 1rem 2rem; |
|||
} |
|||
/* Option selector popup End */ |
|||
|
|||
/* Footer Start */ |
|||
footer { |
|||
width: 100%; |
|||
background-image: var(--background-img); |
|||
color: var(--secondary-color); |
|||
margin: 0 auto; |
|||
align-items: center; |
|||
justify-content: center; |
|||
text-align: center; |
|||
border: 1px solid; |
|||
padding: 1rem 1rem 0.5rem 1rem; |
|||
margin-top: 2rem; |
|||
font-size: 2rem; |
|||
} |
|||
|
|||
footer i { |
|||
font-size: 3rem; |
|||
padding: 1rem 3rem; |
|||
color: var(--secondary-color); |
|||
} |
|||
|
|||
footer .display-pic { |
|||
height: 10rem; |
|||
border-radius: 50%; |
|||
border-style: solid; |
|||
border-color: #393e46; |
|||
border-width: 3px; |
|||
margin-right: 3rem; |
|||
} |
|||
|
|||
footer .contact { |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
} |
|||
|
|||
footer .contact-details { |
|||
margin-top: 1rem; |
|||
} |
|||
/* Footer End */ |
|||
|
|||
/* CSS animation */ |
|||
@-webkit-keyframes fadeIn { |
|||
0% { |
|||
opacity: 0; |
|||
transform: scale(0.6); |
|||
} |
|||
|
|||
100% { |
|||
opacity: 100%; |
|||
transform: scale(1); |
|||
} |
|||
} |
|||
|
|||
@keyframes fadeIn { |
|||
0% { |
|||
opacity: 0; |
|||
} |
|||
100% { |
|||
opacity: 100%; |
|||
} |
|||
} |
|||
|
|||
/* On screens that are 992px wide or less*/ |
|||
@media screen and (max-width: 992px) { |
|||
html { |
|||
font-size: 9px; |
|||
} |
|||
|
|||
.introduction .intro-details { |
|||
width: 70%; |
|||
line-height: 1.6; |
|||
font-size: 1.8rem; |
|||
padding: 3rem 0 2rem; |
|||
} |
|||
.header-section { |
|||
font-size: 1.8rem; |
|||
padding: 2rem 1rem; |
|||
flex-direction: column; |
|||
} |
|||
.row { |
|||
width: 80%; |
|||
box-shadow: 2px 0px 3px var(--option-2); |
|||
} |
|||
.select-input-background.show .select-input-container { |
|||
width: 90%; |
|||
} |
|||
.select-input-container .btn { |
|||
padding: 1.2rem; |
|||
margin: 0.5rem; |
|||
} |
|||
.select-input-options { |
|||
padding: 1rem; |
|||
width: 75%; |
|||
} |
|||
.select-input-classification { |
|||
width: 25%; |
|||
} |
|||
} |
|||
|
|||
/* On screens that are 600px wide or less*/ |
|||
@media screen and (max-width: 600px) { |
|||
html { |
|||
font-size: 8px; |
|||
} |
|||
|
|||
.introduction .intro-details { |
|||
width: 80%; |
|||
line-height: 1.6; |
|||
font-size: 1.8rem; |
|||
padding: 3rem 0 2rem; |
|||
} |
|||
|
|||
.header-section { |
|||
flex-direction: column; |
|||
font-size: 1.8rem; |
|||
} |
|||
|
|||
.header-section .info { |
|||
margin-top: 1rem; |
|||
} |
|||
|
|||
.row { |
|||
width: 90%; |
|||
} |
|||
.select-input-container { |
|||
width: 80%; |
|||
} |
|||
.select-input-container .btn { |
|||
padding: 1rem; |
|||
margin: 0.3rem; |
|||
} |
|||
|
|||
.select-input-options { |
|||
padding: 1rem; |
|||
width: 75%; |
|||
} |
|||
.select-input-classification { |
|||
width: 25%; |
|||
} |
|||
} |
Loading…
Reference in new issue