const gridContainer = document.querySelector('.grid-container'); const gridControls = document.querySelector('.grid-controls'); const gridSingleInputs = Array.from(document.getElementsByClassName('grid-single-input')); const gridItemSingleInputs = Array.from(document.getElementsByClassName('grid-item-input')); const clearButton = document.querySelector('.clear-fields'); // Attach event listeners to single inputs and parent of multiple fields. function addListeners() { gridSingleInputs.forEach((input) => { input.addEventListener('blur', handleSingleFieldChange); }); gridItemSingleInputs.forEach((input) => { if (input.id.includes('sel')) { input.addEventListener('change', handleSingleItemChange); } else { input.addEventListener('blur', handleSingleItemChange); }; }); gridControls.addEventListener('change', handleFieldsChange); gridContainer.addEventListener('click', getGridItem); clearButton.addEventListener('click', clearFields); } // Handle change on multiple fields view and call correct method to set vals. function handleFieldsChange(e) { const control = e.target.id; if(control.startsWith('col')) { setGridTemplateProp(e.target, 'column'); } else if (control.startsWith('row')) { setGridTemplateProp(e.target, 'row'); } else if (control.startsWith('gap')) { setGridGap(e.target); } else if (control.startsWith('justify') || control.startsWith('align')) { setItemAndContentAlignment(e.target); } } // Container Single Field handler function handleSingleFieldChange(e) { const inputValue = e.target.value.toLowerCase(); const inputField = e.target.id; let propName; switch (inputField) { case 'col-single-input': propName = 'grid-template-columns'; break; case 'auto-col-single-input': propName = 'grid-auto-columns'; break; case 'row-single-input': propName = 'grid-template-rows'; break; case 'auto-row-single-input': propName = 'grid-auto-rows'; break; case 'auto-flow-single-input': propName = 'grid-auto-flow'; break; case 'gap-single-input': propName = 'grid-gap'; break; case 'areas-single-input': propName = 'grid-template-areas'; default: break; } assignStyles(propName, inputValue); } // Single Item Event Handler function handleSingleItemChange(e) { // Select which grid-item to manipulate by users click//id title const dynamicItemId = document.getElementById('grid-item--dynamic').textContent; const stripId = dynamicItemId.slice(1); const gridItem = document.getElementById(`${stripId}`); // Get fields user clicked on. const inputField = e.target.id; const selectField = document.getElementById(inputField); let inputValue; let propName; switch (inputField) { case 'column-item-input': inputValue = e.target.value.toLowerCase(); propName = 'grid-column'; break; case 'row-item-input': inputValue = e.target.value.toLowerCase(); propName = 'grid-row'; break; case 'grid-area-input': inputValue = e.target.value.toLowerCase(); propName = 'grid-area'; break; case 'justify-self-sel': inputValue = selectField.options[selectField.selectedIndex].value; propName = 'justify-self'; break; case 'align-self-sel': inputValue = selectField.options[selectField.selectedIndex].value; propName = 'align-self'; break; default: break; } assignStyles(propName, inputValue, gridItem); } function getGridItem(e) { const itemId = e.target.id; if (e.target && itemId.startsWith('grid__item')) { const gridItem = document.getElementById(e.target.id); const itemId = e.target.id; const itemHolder = document.getElementById('grid-item--dynamic'); itemHolder.textContent = `.${itemId}`; } } // Handles assigning the user's styles to the Grid element (container / items) function assignStyles(propName, valueString, gridElement = gridContainer) { Object.assign(gridElement.style, { [propName]: valueString }); } // Set either grid-template-columns or grid-template-rows styles depending on argument passed. function setGridTemplateProp(control, gridTemplateProp) { // Set some variables determined by if user has changed a grid-template column or row property const templateProp = (gridTemplateProp == 'column') ? 'col' : 'row'; const propVal = `${templateProp}Val`; const propUnit = `${templateProp}Unit`; // Get all the fields from either row or columns to be able to loop over each one. const fieldsContainer = document.querySelector(`.multiple-${templateProp}s`); const templateControls = Array.from(fieldsContainer.getElementsByClassName('controls__row')); let propName = (gridTemplateProp == 'column') ? 'grid-template-columns' : 'grid-template-rows'; let userInputValues = {}; // Get input values from fields. for (let i = 0, j = templateControls.length; i < j; i++) { const selectOption = document.getElementById(`${templateProp}-sel-${i+1}`); Object.assign(userInputValues, { [propVal + (i+1)] : document.getElementById(`${templateProp}-input-${i+1}`).value, [propUnit + (i+1)] : selectOption.options[selectOption.selectedIndex].value, }); } // If columnUnitN value is `auto`, set value to empty string Object.keys(userInputValues).forEach((key, i) => { if (userInputValues[key] == 'auto') { // get number from this columns key name to be able to set prev keys value to ''; const controlId = `${propVal}${key.slice(key.length - 1)}`; userInputValues[controlId] = ''; } }) // Get current style of grid containers properties for grid template column/row. const currentStyle = (gridTemplateProp == 'column') ? gridContainer.style.gridTemplateColumns : gridContainer.style.gridTemplateRows; const currentValues = currentStyle.split(' '); // Set each column as a value string consisting of it's value and unit. let storeUserValues = {}; for (let i = 0, j = (Object.keys(userInputValues).length / 2); i < j; i++) { storeUserValues[propVal + (i+1)] = `${userInputValues[propVal + (i+1)]}${userInputValues[propUnit + (i+1)]}`; } // get number off input id to determine which field user touched. const controlId = control.id.slice(control.id.length -1); // Update default value string definition to value user has supplied - index of column determined by controlId. Object.keys(storeUserValues).forEach((col) => { if (col.endsWith(controlId)) { currentValues[controlId - 1] = storeUserValues[col]; } }); // Use the updated values to pass on and update css property const valueString = currentValues.join(' '); assignStyles(propName, valueString); } function setGridGap(control) { const fieldsContainer = document.querySelector('.multiple-gaps'); const templateControls = Array.from(fieldsContainer.getElementsByClassName('controls__row')); const propName = 'grid-gap'; let userInputValues = {}; // Get input values from fields. for (let i = 0, j = templateControls.length; i < j; i++) { const gapRowSel = document.getElementById(`gap-row-sel`); const gapColSel = document.getElementById(`gap-col-sel`); Object.assign(userInputValues, { [`rowGapVal`] : document.getElementById(`gap-row-input`).value, [`rowGapUnit`] : gapRowSel.options[gapRowSel.selectedIndex].value, [`colGapVal`] : document.getElementById(`gap-col-input`).value, [`colGapUnit`] : gapColSel.options[gapColSel.selectedIndex].value, }); } // get default style of grid-gap (note: row comes before column for the shorthand). const currentStyle = gridContainer.style.gridGap; const currentValues = currentStyle.split(' '); // If user has entered fields, use those for gap values, otherwise use defaults. let rowGap = (userInputValues.rowGapVal !== '') ? userInputValues.rowGapVal : currentValues[0]; let colGap = (userInputValues.colGapVal !== '') ? userInputValues.colGapVal : currentValues[1]; // check if using the default values - remove unit abberviation at end rowGap = rowGap.replace(/px|%/,''); colGap = colGap.replace(/px|%/,''); // Set value and unit together in object const storeUserValues = { gridRowGap: `${rowGap}${userInputValues.rowGapUnit}`, gridColGap: `${colGap}${userInputValues.colGapUnit}`, }; const valueString = `${storeUserValues.gridRowGap} ${storeUserValues.gridColGap}`; assignStyles(propName, valueString); } // Set Justify Items / Align Items propertys function setItemAndContentAlignment(control) { const selectControl = document.getElementById(control.id); const selectedOption = selectControl.options[selectControl.selectedIndex].value; let propName; switch (control.id) { case 'justify-items-sel': propName = 'justify-items'; break; case 'align-items-sel': propName = 'align-items'; break; case 'justify-content-sel': propName = 'justify-content'; break; case 'align-content-sel': propName = 'align-content'; break; default: break; } assignStyles(propName, selectedOption); } function setBasicDefaults() { const gridDefaults = { 'grid-template-columns': '1fr 150px 1fr', 'grid-template-rows': '100px', 'grid-auto-rows': '60px', 'grid-gap': '5px 5px', }; Object.keys(gridDefaults).forEach((key) => { const [ propName, valueString ] = [key, gridDefaults[key]] assignStyles(propName, valueString) }); } function addControl() { // if e.target.id.beginsWith col // set control with ids on input and sel with that id. // check length of current parent container to get numeric ID // use this for add row and column. // out same control__row } function clearFields() { const gridItems = Array.from(document.getElementsByClassName('grid__item')); gridContainer.setAttribute(`style`, `grid-template-columns: initial; grid-template-rows: initial; grid-auto-columns: initial; grid-auto-rows: initial; grid-gap: initial;` ); gridItems.forEach((gridItem) => { gridItem.setAttribute(`style`, `grid-area: initial;` ); }) } setBasicDefaults(); addListeners(); // -------------------------------------- // Toggle Views const toggleControls = document.querySelector('.toggle-controls'); const containerControls = document.querySelector('.grid-controls--container'); const itemControls = document.querySelector('.grid-controls--items'); toggleControls.addEventListener('click', function(){ containerControls.classList.toggle('slide'); itemControls.classList.toggle('slide'); }); const toggleView = document.querySelector('.toggle-view'); const singleView = document.querySelector('.single-input'); const fieldsCol = document.querySelector('.multiple-cols'); const fieldsRow = document.querySelector('.multiple-rows'); const fieldsGap = document.querySelector('.multiple-gaps'); const allLines = Array.from(document.getElementsByClassName('seperator')); toggleView.addEventListener('click', function(){ fieldsCol.classList.toggle('hidden'); fieldsRow.classList.toggle('hidden'); fieldsGap.classList.toggle('hidden'); singleView.classList.toggle('visible'); allLines.forEach((seperator) => { seperator.classList.toggle('hidden'); }) }); // Reset values on user input whens hidden.

!