diff --git a/ds-css-v2/demo-components/form/index.html b/ds-css-v2/demo-components/form/index.html index 02243357..0c8ad303 100644 --- a/ds-css-v2/demo-components/form/index.html +++ b/ds-css-v2/demo-components/form/index.html @@ -7,6 +7,7 @@ + @@ -104,6 +105,38 @@

Password (Hash) Field

+
+
+
+ + Required. + +
+ + +
+
+
+ +
+

Input Group

+
+ + +
+
+ + +
+
+ + +
+
+
+
@@ -207,37 +240,43 @@

Numeric Switch Field

- - Required. Country code first, then local number. + + Required. (HH:MM Format) UTC-6 America/New York -
-
-

Phone Number Field

+

Time Picker Field (Default)

- +
- +
+
+ + +
@@ -254,5 +293,7 @@

Phone Number Field

+ + \ No newline at end of file diff --git a/ds-css-v2/demo-components/input-group/index.html b/ds-css-v2/demo-components/input-group/index.html new file mode 100644 index 00000000..5cd25165 --- /dev/null +++ b/ds-css-v2/demo-components/input-group/index.html @@ -0,0 +1,263 @@ + + + + + + CBP Theme Boilerplate + + + + + + +
+
+
+

Input Group Components

+
+
+
+
+
+ + Required. + +
+ + +
+
+
+ +
+

Input Text Group

+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+
+
+ + Required. + +
+ + +
+
+
+ Pepperoni +
+ +
+
+
+ Mushroom +
+ +
+
+
+
+
+ +
+

Input Group

+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+
+
+ + +
+ + +
+
+
+ +
+

Search Field

+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+ + +
+
+
+
+ +
+

Search Field w/ Predictive Text

+
+ + +
+
+ + +
+
+ + +
+
+
+ + + +
+
+
+ + Accepted files: .doc, .docx, .pdf, .jpg, .png, .gif. Max File size 10mb per file. Up to 5 files may be uploaded. + + + +
+
+ +
+

File Upload

+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+ Made with ♥️ by Creative Services +
+
+ + + + + + + + + + + + \ No newline at end of file diff --git a/ds-css-v2/index.html b/ds-css-v2/index.html index ed1a2e5f..baa1eb65 100644 --- a/ds-css-v2/index.html +++ b/ds-css-v2/index.html @@ -60,6 +60,10 @@

Compl Dropdown + + Input Group + + Breadcrumb diff --git a/ds-css-v2/js/cbp-components-js/demo.js b/ds-css-v2/js/cbp-components-js/demo.js new file mode 100644 index 00000000..f5b75210 --- /dev/null +++ b/ds-css-v2/js/cbp-components-js/demo.js @@ -0,0 +1,61 @@ +const findDesc = (element) => { + const wrapper = element.closest(".cbp-form-wrapper"); + const descriptions = wrapper.querySelectorAll(".cbp-form__description"); + + return descriptions; +} + +const invalidateInputGroup = (event, id, type = 'element') => { + const { target: { checked } } = event; + const isComponent = type === 'component'; + const element = !isComponent && document.getElementById(id); + const component = isComponent && document.getElementById(id); + const input = component.querySelector('input'); + const desc = findDesc(component) + + if (checked) { + desc[0].hidden = true; + desc[1].hidden = false; + input.setCustomValidity("invalid"); + isComponent && component.classList.add("cbp-field-group--error"); + } else { + desc[0].hidden = false; + desc[1].hidden = true; + input.setCustomValidity(""); + isComponent && component.classList.remove("cbp-field-group--error"); + } +} + +const disableInputGroup = (event, id, type = 'element') => { + const { target: { checked } } = event; + const isComponent = type === 'component'; + const component = isComponent && document.getElementById(id); + const input = component.querySelector('input'); + const btn = component.querySelector('button'); + + if (checked) { + input.disabled = true; + btn.disabled = true; + } else { + input.disabled = false; + btn.disabled = false; + } +} + +const readOnlyInputGroup = (event, id, type = 'element') => { + const { target: { checked } } = event; + const isComponent = type === 'component'; + const component = isComponent && document.getElementById(id); + const input = component.querySelector('input'); + const btn = component.querySelector('button'); + + if (checked) { + component.classList.add('cbp-field-group--readonly'); + input.readOnly = true; + btn.disabled = true; + } else { + component.classList.remove('cbp-field-group--readonly'); + input.readOnly = false; + btn.disabled = false; + } +} \ No newline at end of file diff --git a/ds-css-v2/js/cbp-components-js/fileupload.js b/ds-css-v2/js/cbp-components-js/fileupload.js new file mode 100644 index 00000000..fae8d2c5 --- /dev/null +++ b/ds-css-v2/js/cbp-components-js/fileupload.js @@ -0,0 +1,133 @@ +// MDN Doc (https://developer.mozilla.org/en-US/docs/Web/API/File_API/Using_files_from_web_applications) + +const fileuploads = document.querySelectorAll(".cbp-form__file"); + +const fileUtil = { + createUpload: (key, name, progressValue, progressMax, files) => { + const fileProgress = document.createElement("div"); + const fileName = document.createElement("span"); + const cancelIcon = document.createElement("i"); + const cancelBtn = document.createElement("button"); + const progressBar = document.createElement("progress"); + + fileProgress.className = "cbp-form__upload"; + fileProgress.id = `${key}_${name}`; + + fileName.innerText = name; + cancelIcon.className = "fa fa-times"; + cancelBtn.appendChild(cancelIcon); + + cancelBtn.onclick = (e) => { + fileUtil.cancelUpload(key, files) + }; + + progressBar.value = progressValue; + progressBar.max = progressMax; + + fileProgress.appendChild(fileName); + fileProgress.appendChild(cancelBtn); + fileProgress.appendChild(progressBar); + + return fileProgress; + }, + createErrorUpload: (key, name) => { + const errorUpload = document.createElement("div"); + const fileContainer = document.createElement("div"); + const errorFile = document.createElement("span"); + const cancelBtn = document.createElement("button"); + const divider = document.createElement("hr"); + const errorMessage = document.createElement("span"); + const errorIcon = document.createElement("i"); + const cancelIcon = document.createElement("i"); + + errorUpload.id = `${key}_${name}`; + errorFile.innerText = name; + cancelIcon.className = 'fa fa-times'; + cancelBtn.appendChild = cancelIcon + + errorIcon.className = "fas fa-exclamation-triangle"; + errorMessage.appendChild(cancelIcon); + errorMessage.innerText = 'File size exceeds the limit.' + + fileContainer.appendChild(errorFile); + fileContainer.appendChild(cancelBtn); + + errorUpload.appendChild(fileContainer); + errorUpload.appendChild(divider); + errorUpload.appendChild(errorMessage) + + return errorUpload; + }, + cancelUpload: (key, files, input) => { + const list = new DataTransfer(); + const fileArray = Object.entries(files) + const filteredList = fileArray.filter(([listkey, value]) => listkey != key); + const filteredFiles = filteredList.map(arr => arr[1]); + + filteredFiles.forEach(file => list.items.add(file)); + + input.files = list.items; + }, +}; + +class FileUploader { + constructor(element) { + this.formWrapper = element.closest('.cbp-form-wrapper'); + this.element = element; + this.input = element.querySelector("input[type=file]"); + + this.handleInput(this.input, this.reader); + } + + handleEvent(event, key, value, files) { + if (event.type === "loadstart") { + const upload = fileUtil.createUpload( + key, + value.name, + event.loaded, + event.total, + files + ); + this.element.insertAdjacentElement("afterend", upload); + } + + if (event.type === "progress") { + // update progress bar value + const upload = document.getElementById(`${key}_${value.name}`); + const progress = upload.querySelector("progress"); + + progress.setAttribute("value", event.loaded); + } + + if (event.type === "error") { + const upload = fileUtil.createErrorUpload(key, value.name); + + this.element.insertAdjacentElement("afterend", upload); + } + } + + handleReader(files) { + for (const [key, value] of Object.entries(files)) { + const reader = new FileReader(); + + reader.addEventListener("loadstart", (e) => this.handleEvent(e, key, value, files)); + reader.addEventListener("load", (e) => this.handleEvent(e, key, value, files)); + reader.addEventListener("loadend", (e) => this.handleEvent(e, key, value, files)); + reader.addEventListener("progress", (e) => this.handleEvent(e, key, value, files)); + reader.addEventListener("error", (e) => this.handleEvent(e, key, value, files)); + reader.addEventListener("abort", this.handleEvent); + + reader.readAsDataURL(value); + } + } + + handleInput(input, reader) { + input.addEventListener("change", (e) => { + this.handleReader(e.target.files); + }); + } +} + +fileuploads.forEach((upload) => { + new FileUploader(upload); +}); diff --git a/ds-css-v2/js/cbp-components-js/timepicker.js b/ds-css-v2/js/cbp-components-js/timepicker.js new file mode 100644 index 00000000..279d7ec5 --- /dev/null +++ b/ds-css-v2/js/cbp-components-js/timepicker.js @@ -0,0 +1,110 @@ +const allTimeFields = document.querySelectorAll('.cbp-form__time'); + +/** + * Time Picker component needs to have an input mask in place for + * hh:mm formatting as well as disabling non-number (time) inputs + */ + +const maskOptions = (hour = 12) => { + return { + overwrite: true, + autofix: true, + mask: "HH:MM", + blocks: { + HH: { + mask: IMask.MaskedRange, + placeholderChar: "HH", + from: 0, + to: hour, + maxLength: 2, + }, + MM: { + mask: IMask.MaskedRange, + placeholderChar: "MM", + from: 0, + to: 59, + maxLength: 2, + }, + }, + }; +}; + +class TimeField { + constructor(node) { + this.node = node; + this.inputWrapper = this.node.querySelector('.cbp-form__time--input'); + this.input = this.handleInput(this.node); + this.timePeriod = this.node.querySelector(".cbp-btn__time--period"); + this.timePeriodActive; + this.timeMask; + this.buttons = this.timePeriod.children; + + this.handleDefaultPeriod(this.input) + + this.timePeriod.addEventListener('click', e => this.handleBtns(e, this.buttons, this.input)) + + this.inputWrapper.addEventListener('click', e => this.handleClick(e, this.input)) + } + + handleClick(e, input) { + if (e.target.classList.contains('minutes')) { + return; + } else { + input.focus(); + } + } + + handleInput(node) { + return node.querySelector("input[type=text]") || node.querySelector("input[type=number]"); + } + + handleBtns(e, buttons, input) { + [...buttons].forEach((btn) => btn.classList.remove("active")); + e.target.classList.add("active"); + + this.timePeriodActive = e.target; + + if (this.timePeriodActive.innerText === 'AM' || this.timePeriodActive.innerText === 'PM') { + if (this.input.type === "text") { + this.timeMask.updateOptions(maskOptions()); + } else { + input.max = 12; + } + } else { + if (this.input.type === "text") { + this.timeMask.updateOptions(maskOptions(23)); + } else { + input.max = 23; + } + } + } + + handleTextMask(timePeriodActive) { + if (timePeriodActive.innerText === 'AM' || timePeriodActive.innerText === 'PM') { + this.timeMask = IMask(this.input, maskOptions()); + } else { + this.timeMask = IMask(this.input, maskOptions(23)); + } + } + + handleNumInput(timePeriodActive) { + if (timePeriodActive.innerText === 'AM' || timePeriodActive.innerText === 'PM') { + this.input.max = 12; + } else { + this.input.max = 23; + } + } + + handleDefaultPeriod(input) { + const [defaultPeriod] = [...this.buttons].filter(btn => btn.textContent.includes(input.getAttribute("data-time-period"))); + defaultPeriod.classList.add("active"); + + this.timePeriodActive = defaultPeriod; + + this.input.type === "text" ? this.handleTextMask(this.timePeriodActive) : this.handleNumInput(this.timePeriodActive); + } +} + +allTimeFields.forEach(field => { + new TimeField(field); +}) \ No newline at end of file diff --git a/ds-css-v2/js/demo/timepicker-demo.js b/ds-css-v2/js/demo/timepicker-demo.js new file mode 100644 index 00000000..bb757a3b --- /dev/null +++ b/ds-css-v2/js/demo/timepicker-demo.js @@ -0,0 +1,74 @@ +class TimePickerDemo { + #id = 'time-picker-demo'; + #component = document.getElementById(this.#id) +} + +console.log(TimePickerDemo.component) + +const tpDemo = (e, id) => { + const component = document.getElementById(id); + const parent = component.closest('.cbp-form-wrapper'); + const input = component.querySelector('input[type=text]') || component.querySelector('input[type=number]'); + const inputs = component.querySelectorAll('input[type=number]'); + const description = parent.querySelector('.cbp-form__description'); + const errorMessage = parent.querySelector('.cbp-form__description--error'); + const timePeriod = parent.querySelector('.cbp-btn__time--period'); + + if (e.target.checked) { + switch (e.target.name) { + case "invalid": + description.hidden = true; + errorMessage.hidden = false; + + component.classList.add("error"); + break; + + case "disable": + component.classList.add("disabled"); + input.setAttribute('disabled', true); + + inputs[0].setAttribute('disabled', true); + inputs[1].setAttribute('disabled', true); + + [...timePeriod.children].forEach((btn) => { + btn.setAttribute('disabled', true); + }); + break; + + case "readonly": + component.classList.add('readonly') + input.setAttribute('readonly', true); + + inputs[0].setAttribute('readonly', true); + inputs[1].setAttribute('readonly', true); + + [...timePeriod.children].forEach(btn => { + btn.setAttribute('disabled', true); + }) + break; + default: + break; + } + } else { + component.className = 'cbp-form__time'; + description.hidden = false; + errorMessage.hidden = true; + input.removeAttribute('readonly'); + input.removeAttribute('disabled'); + + inputs[0].removeAttribute('readonly'); + inputs[1].removeAttribute('readonly'); + + inputs[0].removeAttribute('disabled'); + inputs[1].removeAttribute('disabled'); + + [...timePeriod.children].forEach(btn => { + btn.removeAttribute('disabled', false); + }); + + [...timePeriod.children].forEach(btn => { + btn.removeAttribute('readonly', false); + }) + } + +} \ No newline at end of file diff --git a/ds-css-v2/js/utilities/autocomplete.js b/ds-css-v2/js/utilities/autocomplete.js new file mode 100644 index 00000000..185326e5 --- /dev/null +++ b/ds-css-v2/js/utilities/autocomplete.js @@ -0,0 +1,334 @@ +const countries = [ + "Afghanistan", + "Albania", + "Algeria", + "Andorra", + "Angola", + "Anguilla", + "Antigua & Barbuda", + "Argentina", + "Armenia", + "Aruba", + "Australia", + "Austria", + "Azerbaijan", + "Bahamas", + "Bahrain", + "Bangladesh", + "Barbados", + "Belarus", + "Belgium", + "Belize", + "Benin", + "Bermuda", + "Bhutan", + "Bolivia", + "Bosnia & Herzegovina", + "Botswana", + "Brazil", + "British Virgin Islands", + "Brunei", + "Bulgaria", + "Burkina Faso", + "Burundi", + "Cambodia", + "Cameroon", + "Canada", + "Cape Verde", + "Cayman Islands", + "Central Arfrican Republic", + "Chad", + "Chile", + "China", + "Colombia", + "Congo", + "Cook Islands", + "Costa Rica", + "Cote D Ivoire", + "Croatia", + "Cuba", + "Curacao", + "Cyprus", + "Czech Republic", + "Denmark", + "Djibouti", + "Dominica", + "Dominican Republic", + "Ecuador", + "Egypt", + "El Salvador", + "Equatorial Guinea", + "Eritrea", + "Estonia", + "Ethiopia", + "Falkland Islands", + "Faroe Islands", + "Fiji", + "Finland", + "France", + "French Polynesia", + "French West Indies", + "Gabon", + "Gambia", + "Georgia", + "Germany", + "Ghana", + "Gibraltar", + "Greece", + "Greenland", + "Grenada", + "Guam", + "Guatemala", + "Guernsey", + "Guinea", + "Guinea Bissau", + "Guyana", + "Haiti", + "Honduras", + "Hong Kong", + "Hungary", + "Iceland", + "India", + "Indonesia", + "Iran", + "Iraq", + "Ireland", + "Isle of Man", + "Israel", + "Italy", + "Jamaica", + "Japan", + "Jersey", + "Jordan", + "Kazakhstan", + "Kenya", + "Kiribati", + "Kosovo", + "Kuwait", + "Kyrgyzstan", + "Laos", + "Latvia", + "Lebanon", + "Lesotho", + "Liberia", + "Libya", + "Liechtenstein", + "Lithuania", + "Luxembourg", + "Macau", + "Macedonia", + "Madagascar", + "Malawi", + "Malaysia", + "Maldives", + "Mali", + "Malta", + "Marshall Islands", + "Mauritania", + "Mauritius", + "Mexico", + "Micronesia", + "Moldova", + "Monaco", + "Mongolia", + "Montenegro", + "Montserrat", + "Morocco", + "Mozambique", + "Myanmar", + "Namibia", + "Nauro", + "Nepal", + "Netherlands", + "Netherlands Antilles", + "New Caledonia", + "New Zealand", + "Nicaragua", + "Niger", + "Nigeria", + "North Korea", + "Norway", + "Oman", + "Pakistan", + "Palau", + "Palestine", + "Panama", + "Papua New Guinea", + "Paraguay", + "Peru", + "Philippines", + "Poland", + "Portugal", + "Puerto Rico", + "Qatar", + "Reunion", + "Romania", + "Russia", + "Rwanda", + "Saint Pierre & Miquelon", + "Samoa", + "San Marino", + "Sao Tome and Principe", + "Saudi Arabia", + "Senegal", + "Serbia", + "Seychelles", + "Sierra Leone", + "Singapore", + "Slovakia", + "Slovenia", + "Solomon Islands", + "Somalia", + "South Africa", + "South Korea", + "South Sudan", + "Spain", + "Sri Lanka", + "St Kitts & Nevis", + "St Lucia", + "St Vincent", + "Sudan", + "Suriname", + "Swaziland", + "Sweden", + "Switzerland", + "Syria", + "Taiwan", + "Tajikistan", + "Tanzania", + "Thailand", + "Timor L'Este", + "Togo", + "Tonga", + "Trinidad & Tobago", + "Tunisia", + "Turkey", + "Turkmenistan", + "Turks & Caicos", + "Tuvalu", + "Uganda", + "Ukraine", + "United Arab Emirates", + "United Kingdom", + "United States of America", + "Uruguay", + "Uzbekistan", + "Vanuatu", + "Vatican City", + "Venezuela", + "Vietnam", + "Virgin Islands (US)", + "Yemen", + "Zambia", + "Zimbabwe", +]; + + +// Make sure the form has the autocomplete function switched off +function autocomplete(inp, arr) { + /*the autocomplete function takes two arguments, + the text field element and an array of possible autocompleted values:*/ + var currentFocus; + /*execute a function when someone writes in the text field:*/ + inp.addEventListener("input", function (e) { + debugger + var a, + b, + i, + val = this.value; + /*close any already open lists of autocompleted values*/ + closeAllLists(); + if (!val) { + return false; + } + currentFocus = -1; + /*create a DIV element that will contain the items (values):*/ + a = document.createElement("DIV"); + a.setAttribute("id", this.id + "autocomplete-list"); + a.setAttribute("class", "autocomplete-items"); + /*append the DIV element as a child of the autocomplete container:*/ + this.parentNode.appendChild(a); + /*for each item in the array...*/ + for (i = 0; i < arr.length; i++) { + /*check if the item starts with the same letters as the text field value:*/ + if (arr[i].substr(0, val.length).toUpperCase() == val.toUpperCase()) { + /*create a DIV element for each matching element:*/ + b = document.createElement("DIV"); + /*make the matching letters bold:*/ + b.innerHTML = "" + arr[i].substr(0, val.length) + ""; + b.innerHTML += arr[i].substr(val.length); + /*insert a input field that will hold the current array item's value:*/ + b.innerHTML += ""; + /*execute a function when someone clicks on the item value (DIV element):*/ + b.addEventListener("click", function (e) { + /*insert the value for the autocomplete text field:*/ + inp.value = this.getElementsByTagName("input")[0].value; + /*close the list of autocompleted values, + (or any other open lists of autocompleted values:*/ + closeAllLists(); + }); + a.appendChild(b); + } + } + }); + /*execute a function presses a key on the keyboard:*/ + inp.addEventListener("keydown", function (e) { + var x = document.getElementById(this.id + "autocomplete-list"); + if (x) x = x.getElementsByTagName("div"); + if (e.keyCode == 40) { + /*If the arrow DOWN key is pressed, + increase the currentFocus variable:*/ + currentFocus++; + /*and and make the current item more visible:*/ + addActive(x); + } else if (e.keyCode == 38) { + //up + /*If the arrow UP key is pressed, + decrease the currentFocus variable:*/ + currentFocus--; + /*and and make the current item more visible:*/ + addActive(x); + } else if (e.keyCode == 13) { + /*If the ENTER key is pressed, prevent the form from being submitted,*/ + e.preventDefault(); + if (currentFocus > -1) { + /*and simulate a click on the "active" item:*/ + if (x) x[currentFocus].click(); + } + } + }); + function addActive(x) { + /*a function to classify an item as "active":*/ + if (!x) return false; + /*start by removing the "active" class on all items:*/ + removeActive(x); + if (currentFocus >= x.length) currentFocus = 0; + if (currentFocus < 0) currentFocus = x.length - 1; + /*add class "autocomplete-active":*/ + x[currentFocus].classList.add("autocomplete-active"); + } + function removeActive(x) { + /*a function to remove the "active" class from all autocomplete items:*/ + for (var i = 0; i < x.length; i++) { + x[i].classList.remove("autocomplete-active"); + } + } + function closeAllLists(elmnt) { + /*close all autocomplete lists in the document, + except the one passed as an argument:*/ + var x = document.getElementsByClassName("autocomplete-items"); + for (var i = 0; i < x.length; i++) { + if (elmnt != x[i] && elmnt != inp) { + x[i].parentNode.removeChild(x[i]); + } + } + } + /*execute a function when someone clicks in the document:*/ + document.addEventListener("click", function (e) { + closeAllLists(e.target); + }); +} + + +/*initiate the autocomplete function on the "myInput" element, and pass along the countries array as possible autocomplete values:*/ +autocomplete(document.getElementById("myInput"), countries); \ No newline at end of file diff --git a/ds-css-v2/js/utilities/new-autocomplete.js b/ds-css-v2/js/utilities/new-autocomplete.js new file mode 100644 index 00000000..fd73b29e --- /dev/null +++ b/ds-css-v2/js/utilities/new-autocomplete.js @@ -0,0 +1,51 @@ +const getCountries = async () => { + try { + const response = await fetch("https://restcountries.com/v3.1/all"); + const data = await response.json(); + return data; + } catch (e) { + throw new Error(e); + } +}; + +const filterList = (arr, value) => ( + arr.filter(item => item.substr(0, value.length).toLowerCase() == value.toLowerCase()) +) + +const createListItem = (arr) => { + arr.forEach(element => { + const item = document.createElement('LI'); + item.innerHTML = element; + + return item; + }); +} + +const handleInput = (e, arr) => { + const { target: { value } } = e; + const parent = e.target.closest('.autocomplete'); + const list = filterList(arr, value); + const menu = list.length > 0 && document.createElement('UL'); + + if (value.length === 0) { + console.log('Remove Menu!'); + } else { + parent.insertAdjacentElement('afterend', menu); + + list.forEach(element => { + const item = document.createElement('LI'); + item.innerHTML = element; + + menu.appendChild(item); + }); + } +} + +const autocomplete2 = async (input) => { + const countries = await getCountries(); + const countriesName = countries.map(items => items.name.common).sort(); + + input.addEventListener('input', (e) => handleInput(e, countriesName)) +}; + +// autocomplete2(document.getElementById('autocomplete-demo')) diff --git a/ds-css-v2/styles/sass/cbp-components/_all.scss b/ds-css-v2/styles/sass/cbp-components/_all.scss index a8eba474..18dff4cb 100644 --- a/ds-css-v2/styles/sass/cbp-components/_all.scss +++ b/ds-css-v2/styles/sass/cbp-components/_all.scss @@ -1,12 +1,15 @@ @import './accordion'; +@import './autocomplete'; @import './badges-chips-tags'; @import './breadcrumb'; @import './buttons'; @import './checkbox'; @import './dropdown'; @import './expand'; +@import './fileupload'; @import './footer'; @import './form'; +@import './input-group'; @import './link'; @import './modal'; @import './overflow-menu'; @@ -14,4 +17,5 @@ @import './radio'; @import './select'; @import './slider'; +@import './timepicker'; @import './toggle'; \ No newline at end of file diff --git a/ds-css-v2/styles/sass/cbp-components/_autocomplete.scss b/ds-css-v2/styles/sass/cbp-components/_autocomplete.scss new file mode 100644 index 00000000..11eb5fd3 --- /dev/null +++ b/ds-css-v2/styles/sass/cbp-components/_autocomplete.scss @@ -0,0 +1,31 @@ +.cbp-autocomplete { + background-color: $cbp-white; + border: 1px solid $cbp-ui-neutral-base; + border-top: 0; + border-top-left-radius: 0; + border-top-right-radius: 0; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; + margin-top: 0; + max-height: 216px; + padding-left: 0; +} + +.cbp-autocomplete__item { + height: 100%; + list-style-type: none; + padding-left: 16px; + width: 100%; +} + +.cbp-autocomplete__item > a { + color: $cbp-text-darkest; + height: 100%; + line-height: 0.7; + text-decoration: none; + width: 100%; + + &:hover { + background-color: $cbp-ui-neutral-darker; + } +} \ No newline at end of file diff --git a/ds-css-v2/styles/sass/cbp-components/_fileupload.scss b/ds-css-v2/styles/sass/cbp-components/_fileupload.scss new file mode 100644 index 00000000..8f5fdd7d --- /dev/null +++ b/ds-css-v2/styles/sass/cbp-components/_fileupload.scss @@ -0,0 +1,254 @@ +.cbp-form__file { + align-items: center; + background-color: transparent; + border: 1px dashed $cbp-ui-neutral-base; + border-radius: 3px; + cursor: pointer; + display: flex; + font-style: italic; + height: 72px; + margin-bottom: 16px; + padding: 0 12px; + position: relative; + width: fit-content; + + i { + color: $cbp-text-base; + font-size: 30px; + margin-right: 12px; + } + + input[type=file] { + left: 0; + opacity: 0; + overflow: hidden; + position: absolute; + top: 0; + } + + &:hover { + border: 2px dashed $cbp-ui-neutral-darker; + outline: 4px solid $cbp-ui-neutral-light; + outline-offset: -6px; + + & > .cbp-btn__secondary { + background-color: $cbp-ui-neutral-darker; + } + } +} + +.cbp-form__file:focus-within { + border: 2px dashed $cbp-focus-dark; + outline: 4px solid $cbp-focus-light; + outline-offset: -6px; +} + +.cbp-form__file:focus-within > .cbp-btn__secondary { + @extend .btn-focus; +} + +.cbp-form__file:focus-within > .cbp-btn__secondary { + background-color: $cbp-focus-dark; +} + +.cbp-form__file > .cbp-btn__secondary { + margin-left: 6px; + pointer-events: none; + & > i { + color: $cbp-text-lightest; + font-size: 14px; + margin-right: 8px; + } +} + +/* DISABLED */ +.cbp-form__file.disabled { + border: 1px dashed $cbp-disabled-dark; + color: $cbp-disabled-dark; + + & > i { + color: $cbp-disabled-dark; + } + + & > button { + background-color: $cbp-disabled-dark; + color: $cbp-disabled-light; + font-style: italic; + + & > i { + color: inherit; + } + } + + &:hover { + outline: none; + } +} + +.cbp-form__file.read-only { + border: 1px dashed $cbp-ui-neutral-light; + color: $cbp-text-dark; + + & > i { + color: $cbp-text-light; + } + + & > button { + background-color: $cbp-ui-neutral-light; + color: $cbp-text-lightest; + font-style: italic; + + & > i { + color: inherit; + } + } + + &:hover { + outline: none; + + & > button { + background-color: $cbp-ui-neutral-light; + color: $cbp-text-lightest; + } + } +} + +.cbp-form__upload { + align-items: center; + background-color: $cbp-base-neutral-lighter; + border-radius: 3px; + display: flex; + font-style: italic; + height: 40px; + margin-bottom: 16px; + padding: 12px; + position: relative; + width: 333px; + + & > button { + background-color: transparent; + border: 0; + border-radius: 50%; + font-size: 14px; + height: 28px; + position: absolute; + right: 12px; + width: 28px; + + & > i { + color: $cbp-ui-neutral-dark; + } + + &:hover { + background-color: $cbp-ui-neutral-darker; + + & > i { + color: $cbp-ui-neutral-lighter; + } + } + + &:focus { + @extend .btn-focus; + + & > i { + color: $cbp-ui-neutral-lighter; + } + } + } +} + +.cbp-form__upload > progress { + background-color: transparent; + border: 0; // FF + position: absolute; + bottom: 0; + left: 0; + height: 4px; + width: 100%; +} + +.cbp-form__upload > progress::-webkit-progress-bar { + border-bottom-left-radius: 3px; + border-bottom-right-radius: 3px; +} + +.cbp-form__upload > progress[value]::-webkit-progress-bar { + background-color: $cbp-base-neutral-lighter; +} + +.cbp-form__upload > progress[value]::-webkit-progress-value { + background-color: $cbp-info-base; + border-bottom-left-radius: 3px !important; +} + +.cbp-form__upload > progress[value]::-moz-progress-bar { + background-color: $cbp-info-base; + border-bottom-left-radius: 3px; +} + +.cbp-form__upload--error { + align-items: baseline; + background-color: $cbp-ui-danger-lighter; + border: 2px solid $cbp-ui-danger-base; + flex-direction: column; + height: 72px; + + & > progress { + display: none; + } + + & > hr { + border: none; + border-top: 1px solid $cbp-base-neutral-lighter; + color: $cbp-base-neutral-lighter; + height: 1px; + margin: 8px 0; + width: 100%; + } +} + +.cbp-form__upload--error > div:first-child { + align-items: center; + position: relative; + width: 100%; + + & > button { + background-color: transparent; + border: 0; + border-radius: 50%; + font-size: 14px; + height: 28px; + position: absolute; + right: -1px; + top: -6px; + width: 28px; + + & > i { + color: $cbp-ui-danger-base; + } + + &:hover { + background-color: $cbp-ui-neutral-darker; + + & > i { + color: $cbp-ui-neutral-lighter + } + } + + &:focus { + @extend .btn-focus; + + & > i { + color: $cbp-ui-neutral-lighter + } + } + } +} + +.cbp-form__upload--error > span:last-child { + color: $cbp-ui-danger-base; + + & > i { + margin-right: 4px; + } +} \ No newline at end of file diff --git a/ds-css-v2/styles/sass/cbp-components/_input-group.scss b/ds-css-v2/styles/sass/cbp-components/_input-group.scss new file mode 100644 index 00000000..270846e3 --- /dev/null +++ b/ds-css-v2/styles/sass/cbp-components/_input-group.scss @@ -0,0 +1,141 @@ +.cbp-field-group { + display: flex; + + & > input[type=password], + & > input[type=text] { + @extend .cbp-form__input; + border-right: 0; + border-top-right-radius: 0; + border-bottom-right-radius: 0; + flex: 1 1 auto; + width: auto; + + &:disabled { + cursor: pointer; + } + } + + & > button { + @extend .cbp-btn; + @extend .cbp-btn__secondary; + border-bottom-left-radius: 0; + border-top-left-radius: 0; + display: inline-block; + margin-left: -2px; + white-space: nowrap; + + &:disabled { + background-color: $cbp-disabled-dark; + color: $cbp-disabled-light; + } + } + + & > .square { + @extend .cbp-btn; + @extend .cbp-btn__square; + @extend .cbp-btn__square-secondary; + border-bottom-left-radius: 0; + border-top-left-radius: 0; + margin-left: -2px; + + &:disabled { + background-color: $cbp-disabled-dark; + color: $cbp-disabled-light; + } + } +} + +.cbp-field-group--error { + & > button { + @extend .cbp-btn__square-danger; + } + + & > .square { + @extend .cbp-btn__square-danger; + } +} + +.cbp-field-group--readonly { + & > button, + & > .square { + background-color: $cbp-ui-neutral-light; + color: $cbp-text-lightest; + cursor: not-allowed; + + &:hover:not([disabled]) { + background-color: $cbp-ui-neutral-light; + } + + &:disabled { + background-color: $cbp-base-neutral-light; + color: $cbp-text-lightest; + } + } +} + +.autocomplete { + position: relative; + + input[type="text"] { + margin-bottom: 0; + } +} + +.autocomplete-items { + background-color: $cbp-white; + border: 1px solid $cbp-ui-neutral-base; + border-top: none; + border-top-left-radius: 0px; + border-top-right-radius: 0px; + border-bottom-left-radius: 3px; + border-bottom-right-radius: 3px; + left: 0; + right: 0; + max-height: 216px; + overflow-y: scroll; + position: absolute; + top: 100%; + z-index: 99; +} + +.autocomplete-items div { + align-items: center; + background-color: $cbp-white; + border-bottom: 1px solid $cbp-ui-neutral-lighter; + color: $cbp-text-dark; + cursor: pointer; + display: flex; + font-size: 1rem; + font-style: italic; + font-weight: 400; + height: 36px; + letter-spacing: 0.3px; + padding-left: 16px; + + & > strong { + color: $cbp-text-darkest; + font-style: normal; + font-weight: 400; + } +} + +/*when hovering an item:*/ +.autocomplete-items div:hover { + background-color: $cbp-ui-neutral-darker; + color: $cbp-text-base; + & > strong { + color: $cbp-text-lightest; + font-weight: 700; + } +} + +/*when navigating through the items using the arrow keys:*/ +.autocomplete-active { + background-color: $cbp-focus-dark !important; + color: $cbp-text-base !important; + + & > strong { + color: $cbp-text-lightest !important; + font-weight: 700 !important; + } +} \ No newline at end of file diff --git a/ds-css-v2/styles/sass/cbp-components/_timepicker.scss b/ds-css-v2/styles/sass/cbp-components/_timepicker.scss new file mode 100644 index 00000000..0326918b --- /dev/null +++ b/ds-css-v2/styles/sass/cbp-components/_timepicker.scss @@ -0,0 +1,238 @@ +/* 2022: Input