Skip to content

Commit

Permalink
dropdown and chipTag components
Browse files Browse the repository at this point in the history
  • Loading branch information
jparez committed Jan 13, 2025
1 parent 8a1f568 commit adf4f13
Show file tree
Hide file tree
Showing 5 changed files with 269 additions and 3 deletions.
166 changes: 165 additions & 1 deletion src/plugins/util/components.js
Original file line number Diff line number Diff line change
Expand Up @@ -246,4 +246,168 @@ const textInput = (() => {
return {createTextInput};
})();

module.exports = {switchButton, slider, textInput};
const dropdownSelect = (() => {
/**
* Creates a custom dropdown component.
* @param {Object} options - Configuration options for the dropdown.
* @param {Array} [options.items] - The initial list of items to display in the dropdown menu.
* @param {Function} [options.onChange] - Optional callback triggered when the selected value changes.
* @param {string} [options.value] - Initial selected value.
* @returns {Object} - Dropdown component with methods to interact with it.
*/
const createDropdown = ({items = [], onChange = null, value = ''}) => {
let selectedValue = value || 'Select...';
const dropdownDiv = document.createElement('div');
dropdownDiv.className = 'dropdown';

// Create the div displaying the selected value
const selectedValueDiv = document.createElement('div');
selectedValueDiv.className = 'dropdown-selected';
selectedValueDiv.textContent = value || 'Select...';
dropdownDiv.appendChild(selectedValueDiv);

// Create the menu div where options will be appended
const dropdownMenuDiv = document.createElement('div');
dropdownMenuDiv.className = 'dropdown-menu';
dropdownDiv.appendChild(dropdownMenuDiv);

/**
* Synchronizes the width of the dropdown menu to match the selected value display.
*/
const synchronizeMenuWidth = () => {
const width = selectedValueDiv.offsetWidth;
dropdownMenuDiv.style.width = `${width}px`;
};

/**
* Gets the current selected value from the dropdown.
* @returns {string} - The current selected value.
*/
const getValue = () => selectedValue;

/**
* Sets the selected value in the dropdown and triggers onChange if necessary.
* @param {object} item - The new selected item.
* @param {boolean} [triggerOnChange=false] - Whether to trigger the onChange callback.
*/
const setValue = (item, triggerOnChange = false) => {
let itemValue, valueToDisplay;
if (typeof item === 'string') {
itemValue = item;
valueToDisplay = item;
} else if (typeof item === 'object' && 'value' in item) {
itemValue = item.value;
if (item.valueToDisplay) {
valueToDisplay = item.valueToDisplay;
} else {
valueToDisplay = item.value;
}
} else if (typeof item === 'object') {
itemValue = item.element.textContent;
valueToDisplay = item.element.textContent;
}

// Only update if the new value is different from the current one
if (selectedValueDiv.innerHTML === itemValue || selectedValueDiv.innerHTML === valueToDisplay) {
return;
}
selectedValueDiv.innerHTML = valueToDisplay || itemValue;
selectedValue = itemValue;
// Trigger onChange callback if provided
if (triggerOnChange && onChange) {
onChange(selectedValue);
}
};

/**
* Updates the options displayed in the dropdown menu.
* Clears current options and appends the new ones.
* @param {Array} newItems - The new list of items to display in the dropdown.
*/
const updateOptions = (newItems) => {
// Clear current options before appending new ones
dropdownMenuDiv.innerHTML = '';

// Iterate through newItems to create and append dropdown options
newItems.forEach((item) => {
const optionDiv = document.createElement('div');
optionDiv.className = 'dropdown-item';

// Check if the item is a string, object with label, or a custom element
if (typeof item === 'string') {
optionDiv.innerHTML = item;
} else if (typeof item === 'object' && item.label) {
optionDiv.innerHTML = item.label;
} else if (typeof item === 'object' && item.element && item.element instanceof HTMLElement) {
optionDiv.appendChild(item.element);
}

// Add event listener for option click
optionDiv.addEventListener('click', () => {
setValue(item, true);
dropdownDiv.classList.remove('open');
});

dropdownMenuDiv.appendChild(optionDiv);
});
};

// Initialize dropdown with provided items
updateOptions(items);

// Toggle dropdown visibility when the selected value div is clicked
selectedValueDiv.addEventListener('click', () => {
synchronizeMenuWidth();
dropdownDiv.classList.toggle('open');
});

// Close the dropdown if the user clicks outside of it
document.addEventListener('click', (event) => {
if (!dropdownDiv.contains(event.target)) {
dropdownDiv.classList.remove('open');
}
});

// Return the dropdown element and helper methods for interaction
return {
element: dropdownDiv,
getValue,
setValue,
updateOptions, // Expose method to dynamically update options
};
};

// Expose createDropdown method for external usage
return {createDropdown};
})();

const chipTag = (() => {
let tagDiv = null;
const createChip = ({type= 'success', text = 'Success'}) => {
tagDiv = document.createElement('div');
tagDiv.className = 'gm-tag-'+type;

const container = document.createElement('div');
container.className = 'gm-tag-container';
container.textContent = text;
tagDiv.appendChild(container);

const setType = (newType) => {
tagDiv.className = 'gm-tag-'+newType;
};

const setValue = (newText) => {
container.textContent = newText;
};

return {
element: tagDiv,
setType,
setValue,
};
};

return {createChip};
})();

module.exports = {switchButton, slider, textInput, dropdownSelect, chipTag};
6 changes: 4 additions & 2 deletions src/scss/base/_variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
--gm-text-color: #ffffff;
--gm-primary-color: #e6195e;
--gm-secondary-color: #292929;
--gm-third-color: #1a1a1a;
--gm-fourth-color: #C4C4C4;
--gm-success-color: #11b920;
--gm-warning-color: #ffcc00;
--gm-error-color: #ff0000;
Expand All @@ -23,9 +25,9 @@
--gm-btn-text-color: var(--gm-text-color);
--gm-btn-bg-color: var(--gm-primary-color);
--gm-btn-bg-color-hover: var(--gm-primary-color);
--gm-btn-bg-color-disabled: rgba(179, 179, 179, 0.24);
--gm-btn-bg-color-disabled: rgba(179, 179, 179, 0.24); // TODO
--gm-btn-bg-color-disabled-hover: #828282;
--gm-btn-color-disabled: #c4c4c4;
--gm-btn-color-disabled: var(--gm-fourth-color);

/** Input **/
--gm-input-text-color: var(--gm-text-color);
Expand Down
31 changes: 31 additions & 0 deletions src/scss/components/_chipTag.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@

.gm-tag{
&-success{
.gm-tag-container{
display: flex;
align-items: center;
border-radius: 8px;
padding: 2px 6px;
color: var(--gm-success-color);
background-color: color-mix(in srgb, var(--gm-success-color), transparent 80%);

&::after{
padding-left: 5px;
content: ' ';
background-color: var(--gm-success-color);
mask-image: url('../assets/images/ic_check.svg');
-webkit-mask-image: url('../assets/images/ic_check.svg');
-webkit-mask-size: contain;
mask-size: contain;
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-position: center;
mask-position: center;
width: 13px;
height: 13px;
display: block;
margin-left: 5px;
}
}
}
}
67 changes: 67 additions & 0 deletions src/scss/components/_dropdown.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
.dropdown {
position: relative;
font-family: Arial, sans-serif;
&.open {
.dropdown-menu {
display: block;
}
.dropdown-selected {
border-color: var(--gm-primary-color);
border-width: 2px;
&::after {
transform: rotate(-45deg);
}

}
}

.dropdown-selected {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 0px 15px 0px;
cursor: pointer;

transition: transform 0.2s ease;
transition: border-color 0.2s ease;
border-bottom: 1px solid var(--gm-fourth-color);

&::after {
content: '';
width: 8px;
height: 8px;
margin-right: 5px;
border: solid var(--gm-text-color);
border-width: 2px 2px 0 0;
transform: rotate(135deg);
transform-origin: center;
transition: transform 0.2s ease;
}
}

.dropdown-menu {
display: none;
position: absolute;
top: 100%;
left: 0;
z-index: 10;
margin-top: 5px;

padding: 10px 0;
gap: 6px;
border-radius: 4px;
background-color: var(--gm-third-color);
box-shadow: 0px 1px 4px 0px var(--gm-secondary-color);

.dropdown-item {
padding: 16px 20px;
cursor: pointer;
}

.dropdown-item:hover {
background: var(--gm-primary-color);
}
}

}

2 changes: 2 additions & 0 deletions src/scss/main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,5 @@
@import 'components/_slider';
@import 'components/_switchButton';
@import 'components/_textInput';
@import 'components/_dropdown';
@import 'components/_chipTag';

0 comments on commit adf4f13

Please sign in to comment.