diff --git a/packages/browser-extension/src/_locales/de/messages.json b/packages/browser-extension/src/_locales/de/messages.json index 3c5bfda..0eb6fa4 100644 --- a/packages/browser-extension/src/_locales/de/messages.json +++ b/packages/browser-extension/src/_locales/de/messages.json @@ -5,6 +5,21 @@ "contextMenu_reportOption": { "message": "Webseite melden..." }, + "options_clearButton": { + "message": "Liste leeren" + }, + "options_exclusionListTitle": { + "message": "Ausschlussliste" + }, + "options_exportButton": { + "message": "Liste exportieren" + }, + "options_filterPlaceholder": { + "message": "Nach dem Eintippen ENTER drücken, um zu filtern" + }, + "options_importButton": { + "message": "Liste importieren" + }, "popup_contributeOption": { "message": "Tragen Sie zu diesem Projekt bei" }, diff --git a/packages/browser-extension/src/_locales/en/messages.json b/packages/browser-extension/src/_locales/en/messages.json index 5c6d67a..b9082e4 100644 --- a/packages/browser-extension/src/_locales/en/messages.json +++ b/packages/browser-extension/src/_locales/en/messages.json @@ -5,6 +5,21 @@ "contextMenu_reportOption": { "message": "Report site..." }, + "options_clearButton": { + "message": "Clear list" + }, + "options_exclusionListTitle": { + "message": "Exclusion list" + }, + "options_exportButton": { + "message": "Export list" + }, + "options_filterPlaceholder": { + "message": "Press ENTER to filter after typing" + }, + "options_importButton": { + "message": "Import list" + }, "popup_contributeOption": { "message": "Contribute to this project" }, diff --git a/packages/browser-extension/src/_locales/es/messages.json b/packages/browser-extension/src/_locales/es/messages.json index dbbc5a3..b06a079 100644 --- a/packages/browser-extension/src/_locales/es/messages.json +++ b/packages/browser-extension/src/_locales/es/messages.json @@ -5,6 +5,21 @@ "contextMenu_reportOption": { "message": "Reportar sitio..." }, + "options_clearButton": { + "message": "Borrar lista" + }, + "options_exclusionListTitle": { + "message": "Lista de exclusión" + }, + "options_exportButton": { + "message": "Exportar lista" + }, + "options_filterPlaceholder": { + "message": "Presiona ENTER para filtrar después de escribir" + }, + "options_importButton": { + "message": "Importar lista" + }, "popup_contributeOption": { "message": "Contribuye a este proyecto" }, diff --git a/packages/browser-extension/src/_locales/fr/messages.json b/packages/browser-extension/src/_locales/fr/messages.json index 085f0ec..d7fc993 100644 --- a/packages/browser-extension/src/_locales/fr/messages.json +++ b/packages/browser-extension/src/_locales/fr/messages.json @@ -5,6 +5,21 @@ "contextMenu_reportOption": { "message": "Signaler le site..." }, + "options_clearButton": { + "message": "Effacer la liste" + }, + "options_exclusionListTitle": { + "message": "Liste d'exclusion" + }, + "options_exportButton": { + "message": "Exporter la liste" + }, + "options_filterPlaceholder": { + "message": "Appuyez sur ENTRÉE pour filtrer après avoir tapé" + }, + "options_importButton": { + "message": "Importer la liste" + }, "popup_contributeOption": { "message": "Contribuez à ce projet" }, diff --git a/packages/browser-extension/src/_locales/it/messages.json b/packages/browser-extension/src/_locales/it/messages.json index 2f7a7de..c1534a1 100644 --- a/packages/browser-extension/src/_locales/it/messages.json +++ b/packages/browser-extension/src/_locales/it/messages.json @@ -5,6 +5,21 @@ "contextMenu_reportOption": { "message": "Segnala sito..." }, + "options_clearButton": { + "message": "Cancella elenco" + }, + "options_exclusionListTitle": { + "message": "Elenco di esclusione" + }, + "options_exportButton": { + "message": "Esporta elenco" + }, + "options_filterPlaceholder": { + "message": "Premi INVIO per filtrare dopo aver digitato" + }, + "options_importButton": { + "message": "Importa elenco" + }, "popup_contributeOption": { "message": "Contribuisci a questo progetto" }, diff --git a/packages/browser-extension/src/_locales/pt_BR/messages.json b/packages/browser-extension/src/_locales/pt_BR/messages.json index b2ea0e0..eb19af5 100644 --- a/packages/browser-extension/src/_locales/pt_BR/messages.json +++ b/packages/browser-extension/src/_locales/pt_BR/messages.json @@ -5,6 +5,21 @@ "contextMenu_reportOption": { "message": "Reportar site..." }, + "options_clearButton": { + "message": "Limpar lista" + }, + "options_exclusionListTitle": { + "message": "Lista de exclusão" + }, + "options_exportButton": { + "message": "Exportar lista" + }, + "options_filterPlaceholder": { + "message": "Pressione ENTER para filtrar após digitar" + }, + "options_importButton": { + "message": "Importar lista" + }, "popup_contributeOption": { "message": "Contribua para este projeto" }, diff --git a/packages/browser-extension/src/_locales/pt_PT/messages.json b/packages/browser-extension/src/_locales/pt_PT/messages.json index b2ea0e0..eb19af5 100644 --- a/packages/browser-extension/src/_locales/pt_PT/messages.json +++ b/packages/browser-extension/src/_locales/pt_PT/messages.json @@ -5,6 +5,21 @@ "contextMenu_reportOption": { "message": "Reportar site..." }, + "options_clearButton": { + "message": "Limpar lista" + }, + "options_exclusionListTitle": { + "message": "Lista de exclusão" + }, + "options_exportButton": { + "message": "Exportar lista" + }, + "options_filterPlaceholder": { + "message": "Pressione ENTER para filtrar após digitar" + }, + "options_importButton": { + "message": "Importar lista" + }, "popup_contributeOption": { "message": "Contribua para este projeto" }, diff --git a/packages/browser-extension/src/_locales/ro/messages.json b/packages/browser-extension/src/_locales/ro/messages.json index 8889b57..deeeafa 100644 --- a/packages/browser-extension/src/_locales/ro/messages.json +++ b/packages/browser-extension/src/_locales/ro/messages.json @@ -5,6 +5,21 @@ "contextMenu_reportOption": { "message": "Raportați site-ul..." }, + "options_clearButton": { + "message": "Ștergeți lista" + }, + "options_exclusionListTitle": { + "message": "Listă de excludere" + }, + "options_exportButton": { + "message": "Exportați lista" + }, + "options_filterPlaceholder": { + "message": "Apăsați ENTER pentru a filtra după ce ați tastat" + }, + "options_importButton": { + "message": "Importați lista" + }, "popup_contributeOption": { "message": "Contribuie la acest proiect" }, diff --git a/packages/browser-extension/src/_locales/ru/messages.json b/packages/browser-extension/src/_locales/ru/messages.json index 42dc71b..b12a04a 100644 --- a/packages/browser-extension/src/_locales/ru/messages.json +++ b/packages/browser-extension/src/_locales/ru/messages.json @@ -5,6 +5,21 @@ "contextMenu_reportOption": { "message": "Сообщить о сайте..." }, + "options_clearButton": { + "message": "Очистить список" + }, + "options_exclusionListTitle": { + "message": "Список исключений" + }, + "options_exportButton": { + "message": "Экспорт списка" + }, + "options_filterPlaceholder": { + "message": "Нажмите ENTER для фильтрации после ввода" + }, + "options_importButton": { + "message": "Импорт списка" + }, "popup_contributeOption": { "message": "Внести свой вклад в этот проект" }, diff --git a/packages/browser-extension/src/options.html b/packages/browser-extension/src/options.html new file mode 100644 index 0000000..3c9f0ca --- /dev/null +++ b/packages/browser-extension/src/options.html @@ -0,0 +1,110 @@ + + + + Cookie Dialog Monster > Exclusion List + + + + + + +
+
+

+ Cookie Dialog Monster > +

+
+
+
+
+ + + +
+ + + +
+ + + diff --git a/packages/browser-extension/src/popup.html b/packages/browser-extension/src/popup.html index 3005f9b..a314d83 100644 --- a/packages/browser-extension/src/popup.html +++ b/packages/browser-extension/src/popup.html @@ -1,7 +1,6 @@ - @@ -10,6 +9,24 @@

Cookie Dialog Monster

+
@@ -17,10 +34,10 @@ aria-hidden="true" fill="none" height="32" - stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" + stroke="currentColor" viewBox="0 0 24 24" width="32" > @@ -34,10 +51,10 @@ aria-hidden="true" fill="none" height="32" - stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" + stroke="currentColor" viewBox="0 0 24 24" width="32" > @@ -57,10 +74,10 @@ aria-hidden="true" fill="none" height="32" - stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" + stroke="currentColor" viewBox="0 0 24 24" width="32" > @@ -75,10 +92,10 @@ aria-hidden="true" fill="none" height="32" - stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" + stroke="currentColor" viewBox="0 0 24 24" width="32" > diff --git a/packages/browser-extension/src/scripts/background.js b/packages/browser-extension/src/scripts/background.js index 595d54c..54ce3a2 100644 --- a/packages/browser-extension/src/scripts/background.js +++ b/packages/browser-extension/src/scripts/background.js @@ -5,13 +5,6 @@ const apiUrl = 'https://api.cookie-dialog-monster.com/rest/v2'; -/** - * @description Initial state - * @type {{ enabled: boolean }} - */ - -const initial = { enabled: true }; - /** * @description Context menu identifier * @type {string} @@ -90,31 +83,65 @@ chrome.runtime.onMessage.addListener((message, sender, callback) => { switch (message.type) { case 'DISABLE_ICON': - if (isPage && tabId) chrome.action.setIcon({ path: '/assets/icons/disabled.png', tabId }); + if (isPage && tabId) { + chrome.action.setIcon({ path: '/assets/icons/disabled.png', tabId }); + } break; case 'ENABLE_ICON': - if (isPage && tabId) chrome.action.setIcon({ path: '/assets/icons/enabled.png', tabId }); + if (isPage && tabId) { + chrome.action.setIcon({ path: '/assets/icons/enabled.png', tabId }); + } break; case 'ENABLE_POPUP': - if (isPage && tabId) chrome.action.setPopup({ popup: '/popup.html', tabId }); + if (isPage && tabId) { + chrome.action.setPopup({ popup: '/popup.html', tabId }); + } break; case 'GET_DATA': - storage.get('data', ({ data }) => (data ? callback(data) : refreshData(callback))); + storage.get('data', ({ data }) => { + if (data) { + callback(data); + } else { + refreshData(callback); + } + }); return true; - case 'GET_STATE': - if (hostname) storage.get(hostname, (state) => callback(state[hostname] ?? initial)); + case 'GET_EXCLUSION_LIST': + storage.get(null, (exclusions) => { + const exclusionList = Object.entries(exclusions || {}).flatMap((x) => + x[0] !== 'data' && !x[1]?.enabled ? [x[0]] : [] + ); + callback(exclusionList); + }); + return true; + case 'GET_HOSTNAME_STATE': + if (hostname) { + storage.get(hostname, (state) => { + callback(state[hostname] ?? { enabled: true }); + }); + } return true; case 'GET_TAB': - chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => callback(tabs[0])); + chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => { + callback(tabs[0]); + }); return true; case 'INSERT_DIALOG_CSS': - if (isPage && tabId) script.insertCSS({ files: ['styles/dialog.css'], target: { tabId } }); + if (isPage && tabId) { + script.insertCSS({ files: ['styles/dialog.css'], target: { tabId } }); + } break; case 'REPORT': - if (tabId) report(message, sender.tab); + if (tabId) { + report(message, sender.tab); + } break; - case 'UPDATE_STATE': - if (hostname) storage.set({ [hostname]: message.state }); + case 'SET_HOSTNAME_STATE': + if (hostname && message.state.enabled === false) { + storage.set({ [hostname]: message.state }); + } else if (hostname) { + storage.remove(hostname); + } break; default: break; diff --git a/packages/browser-extension/src/scripts/content.js b/packages/browser-extension/src/scripts/content.js index 477871d..1b7587b 100644 --- a/packages/browser-extension/src/scripts/content.js +++ b/packages/browser-extension/src/scripts/content.js @@ -254,7 +254,7 @@ window.addEventListener('run', () => { */ (async () => { - state = (await dispatch({ hostname, type: 'GET_STATE' })) ?? state; + state = (await dispatch({ hostname, type: 'GET_HOSTNAME_STATE' })) ?? state; dispatch({ type: 'ENABLE_POPUP' }); if (state.enabled) { diff --git a/packages/browser-extension/src/scripts/options.js b/packages/browser-extension/src/scripts/options.js new file mode 100644 index 0000000..42be9a6 --- /dev/null +++ b/packages/browser-extension/src/scripts/options.js @@ -0,0 +1,248 @@ +/** + * @description Shortcut to send messages to background script + */ + +const dispatch = chrome.runtime.sendMessage; + +/** + * @description Exclusion list, URLs where the user prefers to disable the extension + * @type {string[]} + */ + +let exclusionList = []; + +/** + * @description Renders exclusion items into exclusion list + * @returns {void} + */ + +const createList = () => { + const emptyItemElement = document.getElementById('exclusion-list-item-empty'); + const exclusionListElement = document.getElementById('exclusion-list'); + const exclusionListItemTemplateElement = document.getElementById('exclusion-list-item-template'); + + Array.from(exclusionListElement.querySelectorAll('[data-value]')).forEach((exclusionItem) => { + exclusionItem.remove(); + }); + + if (exclusionList.length) { + for (const exclusionValue of exclusionList) { + const ariaLabelOrTitle = `Delete ${exclusionValue}`; + const itemElement = exclusionListItemTemplateElement.cloneNode(true); + const deleteButtonElement = itemElement.getElementsByTagName('button')[0]; + + deleteButtonElement.addEventListener('click', handleDeleteClick); + deleteButtonElement.setAttribute('aria-label', ariaLabelOrTitle); + deleteButtonElement.setAttribute('title', ariaLabelOrTitle); + itemElement.removeAttribute('id'); + itemElement.getElementsByTagName('span')[0].innerText = exclusionValue; + itemElement.setAttribute('data-value', exclusionValue); + itemElement.style.removeProperty('display'); + exclusionListElement.appendChild(itemElement); + } + } else { + emptyItemElement.innerText = "You don't have any exclusions yet"; + emptyItemElement.style.removeProperty('display'); + } +}; + +/** + * @async + * @description Clear all items from the exclusion list + * @returns {Promise} + */ + +const handleClearClick = async () => { + const filterInputElement = document.getElementById('filter-input'); + + for (const exclusionValue of exclusionList) { + const state = { enabled: true }; + await dispatch({ hostname: exclusionValue, state, type: 'SET_HOSTNAME_STATE' }); + } + + exclusionList = []; + createList(); + updateList(filterInputElement.value.trim()); +}; + +/** + * @async + * @description Setup handlers and items + */ + +const handleContentLoaded = async () => { + exclusionList = await dispatch({ type: 'GET_EXCLUSION_LIST' }); + createList(); + + const clearButtonElement = document.getElementById('clear-button'); + clearButtonElement.addEventListener('click', handleClearClick); + + const exportButtonElement = document.getElementById('export-button'); + exportButtonElement.addEventListener('click', handleExportClick); + + const fileInputElement = document.getElementById('file-input'); + fileInputElement.addEventListener('change', handleFileChange); + + const filterInputElement = document.getElementById('filter-input'); + filterInputElement.addEventListener('keydown', handleFilterKeyDown); + + const importButtonElement = document.getElementById('import-button'); + importButtonElement.addEventListener('click', handleImportClick); + + translate(); +}; + +/** + * @async + * @description Deletes the clicked element from the exclusion list + * @param {MouseEvent} event + * @returns {Promise} + */ + +const handleDeleteClick = async (event) => { + const filterInputElement = document.getElementById('filter-input'); + const { value } = event.currentTarget.parentElement.dataset; + const state = { enabled: true }; + + await dispatch({ hostname: value, state, type: 'SET_HOSTNAME_STATE' }); + exclusionList = exclusionList.filter((exclusionValue) => exclusionValue !== value); + itemElement.remove(); + updateList(filterInputElement.value.trim()); +}; + +/** + * @description Exports a file with the current exclusion list + * @returns {void} + */ + +const handleExportClick = () => { + const anchor = document.createElement('a'); + const text = exclusionList.join('\n'); + const blob = new Blob([text], { type: 'octet/stream' }); + const url = window.URL.createObjectURL(blob); + + anchor.href = url; + anchor.download = `${new Date().valueOf()}.cdm`; + anchor.click(); + window.URL.revokeObjectURL(url); +}; + +/** + * @description Processes a file and sends the updates + * @param {InputEvent} event + * @returns {void} + */ + +const handleFileChange = (event) => { + const file = event.currentTarget.files[0]; + const filterInputElement = document.getElementById('filter-input'); + const reader = new FileReader(); + + reader.addEventListener('load', async (event) => { + const newExclusionList = event.currentTarget.result.split('\n').filter((x) => x.trim()); + + for (const exclusionValue of newExclusionList) { + const state = { enabled: false }; + await dispatch({ hostname: exclusionValue, state, type: 'SET_HOSTNAME_STATE' }); + } + + if (newExclusionList.length) { + exclusionList = [...new Set([...exclusionList, ...newExclusionList])].sort(); + createList(); + updateList(filterInputElement.value.trim()); + } + }); + + event.currentTarget.value = ''; + reader.readAsText(file); +}; + +/** + * @description Applies filter to the exclusion list when the user presses ENTER key + * @param {KeyboardEvent} event + * @returns {void} + */ + +const handleFilterKeyDown = (event) => { + if (event.key === 'Enter') { + const filterValue = event.currentTarget.value.trim(); + updateList(filterValue); + } +}; + +/** + * @description Shallow clicks an hidden input to open the file explorer + * @returns {void} + */ + +const handleImportClick = () => { + const fileInputElement = document.getElementById('file-input'); + fileInputElement.click(); +}; + +/** + * @description Applies translations to tags with i18n data attribute + * @returns {void} + */ + +const translate = () => { + const nodes = document.querySelectorAll('[data-i18n]'); + + for (let i = nodes.length; i--; ) { + const node = nodes[i]; + const { i18n, i18nAriaLabel, i18nPlaceholder } = node.dataset; + + if (i18n) { + node.innerHTML = chrome.i18n.getMessage(i18n); + } + + if (i18nAriaLabel) { + node.setAttribute('aria-label', i18nAriaLabel); + } + + if (i18nPlaceholder) { + node.setAttribute('placeholder', i18nPlaceholder); + } + } +}; + +/** + * @description Updates exclusion items in DOM + * @param {string | undefined} filterValue + * @returns {void} + */ + +const updateList = (filterValue) => { + const emptyItemElement = document.getElementById('exclusion-list-item-empty'); + const exclusionListElement = document.getElementById('exclusion-list'); + const exclusionListElements = exclusionListElement.querySelectorAll(`[data-value]`); + + if (exclusionListElements.length) { + let isEmpty = true; + emptyItemElement.style.setProperty('display', 'none'); + + for (const exclusionItemElement of Array.from(exclusionListElements)) { + if (exclusionItemElement.matches(`[data-value*="${filterValue}"]`) || !filterValue) { + exclusionItemElement.style.removeProperty('display'); + isEmpty = false; + } else { + exclusionItemElement.style.setProperty('display', 'none'); + } + } + + if (isEmpty) { + emptyItemElement.innerText = 'No exclusions found'; + emptyItemElement.style.removeProperty('display'); + } + } else { + emptyItemElement.innerText = "You don't have any exclusions yet"; + emptyItemElement.style.removeProperty('display'); + } +}; + +/** + * @description Listen to document ready + * @listens document#DOMContentLoaded + */ + +document.addEventListener('DOMContentLoaded', handleContentLoaded); diff --git a/packages/browser-extension/src/scripts/popup.js b/packages/browser-extension/src/scripts/popup.js index ef1f342..79369e7 100644 --- a/packages/browser-extension/src/scripts/popup.js +++ b/packages/browser-extension/src/scripts/popup.js @@ -64,6 +64,7 @@ let state = { enabled: true }; /** * @async * @description Setup stars handlers and result message links + * @returns {Promise} */ const handleContentLoaded = async () => { @@ -72,27 +73,30 @@ const handleContentLoaded = async () => { hostname = tab?.url ? new URL(tab.url).hostname.split('.').slice(-3).join('.').replace('www.', '') : undefined; - state = (await dispatch({ hostname, type: 'GET_STATE' })) ?? state; + state = (await dispatch({ hostname, type: 'GET_HOSTNAME_STATE' })) ?? state; - const host = document.getElementById('host'); - host.innerText = hostname ?? 'unknown'; + const hostTextElement = document.getElementById('host'); + hostTextElement.innerText = hostname ?? 'unknown'; - const contribute = document.getElementById('contribute-option'); - contribute?.addEventListener('click', handleLinkRedirect); + const contributeButtonElement = document.getElementById('contribute-option'); + contributeButtonElement?.addEventListener('click', handleLinkRedirect); - const help = document.getElementById('help-option'); - help?.addEventListener('click', handleLinkRedirect); + const helpButtonElement = document.getElementById('help-option'); + helpButtonElement?.addEventListener('click', handleLinkRedirect); - const power = document.getElementById('power-option'); - power?.addEventListener('click', handlePowerToggle); - if (state.enabled) power?.setAttribute('data-value', 'on'); - else power?.setAttribute('data-value', 'off'); + const powerButtonElement = document.getElementById('power-option'); + powerButtonElement?.addEventListener('click', handlePowerToggle); + if (state.enabled) powerButtonElement?.setAttribute('data-value', 'on'); + else powerButtonElement?.setAttribute('data-value', 'off'); - const rate = document.getElementById('rate-option'); - rate?.addEventListener('click', handleLinkRedirect); - if (isEdge) rate?.setAttribute('data-href', edgeUrl); - else if (isChromium) rate?.setAttribute('data-href', chromeUrl); - else if (isFirefox) rate?.setAttribute('data-href', firefoxUrl); + const rateButtonElement = document.getElementById('rate-option'); + rateButtonElement?.addEventListener('click', handleLinkRedirect); + if (isEdge) rateButtonElement?.setAttribute('data-href', edgeUrl); + else if (isChromium) rateButtonElement?.setAttribute('data-href', chromeUrl); + else if (isFirefox) rateButtonElement?.setAttribute('data-href', firefoxUrl); + + const settingsButtonElement = document.getElementById('settings-button'); + settingsButtonElement.addEventListener('click', handleSettingsClick); translate(); }; @@ -101,6 +105,7 @@ const handleContentLoaded = async () => { * @async * @description Opens a new tab * @param {MouseEvent} event + * @returns {Promise} */ const handleLinkRedirect = async (event) => { @@ -112,20 +117,32 @@ const handleLinkRedirect = async (event) => { }; /** + * @async * @description Disables or enables extension on current page * @param {MouseEvent} event + * @returns {Promise} */ const handlePowerToggle = async (event) => { state = { enabled: !state.enabled }; - dispatch({ hostname, state, type: 'UPDATE_STATE' }); + dispatch({ hostname, state, type: 'SET_HOSTNAME_STATE' }); if (state.enabled) event.currentTarget.setAttribute('data-value', 'on'); else event.currentTarget.setAttribute('data-value', 'off'); await chrome.tabs.reload({ bypassCache: true }); }; +/** + * @description Opens options page + * @returns {void} + */ + +const handleSettingsClick = () => { + chrome.runtime.openOptionsPage(); +}; + /** * @description Applies translations to tags with i18n data attribute + * @returns {void} */ const translate = () => { @@ -133,9 +150,19 @@ const translate = () => { for (let i = nodes.length; i--; ) { const node = nodes[i]; - const { i18n } = node.dataset; + const { i18n, i18nAriaLabel, i18nPlaceholder } = node.dataset; - node.innerHTML = chrome.i18n.getMessage(i18n); + if (i18n) { + node.innerHTML = chrome.i18n.getMessage(i18n); + } + + if (i18nAriaLabel) { + node.setAttribute('aria-label', i18nAriaLabel); + } + + if (i18nPlaceholder) { + node.setAttribute('placeholder', i18nPlaceholder); + } } }; diff --git a/packages/browser-extension/src/styles/options.css b/packages/browser-extension/src/styles/options.css new file mode 100644 index 0000000..1b97cff --- /dev/null +++ b/packages/browser-extension/src/styles/options.css @@ -0,0 +1,137 @@ +:root { + --color-error: #cc0000; + --color-primary: #3dd9eb; + --color-secondary: #34495e; + --color-success: #5cb85c; + --color-tertiary: #6b7280; + --color-transparent: transparent; + --color-warning: #ffdf00; + --color-white: #ffffff; +} + +body { + box-sizing: border-box; + color: var(--color-tertiary); + display: flex; + flex-direction: column; + font-family: Inter, Arial, Helvetica, sans-serif; + min-height: 100vh; +} + +body * { + box-sizing: border-box; + font-family: inherit; +} + +button { + align-items: center; + background-color: var(--color-white); + border: none; + border-radius: 4px; + color: var(--color-secondary); + display: inline-flex; + gap: 4px; + outline: none; + transition: 0.4s; +} + +button[data-variant='large'] { + direction: rtl; + padding: 8px; +} + +button:focus, +button:hover { + background-color: var(--color-secondary); + color: var(--color-white); +} + +footer { + background-color: var(--color-secondary); + font-size: 12px; + height: 4px; + margin-top: auto; + text-align: center; +} + +header { + background-color: var(--color-secondary); + color: var(--color-white); + font-size: 16px !important; + height: 48px; +} + +header > div { + align-items: center; + display: flex; + height: 100%; + justify-content: space-between; + margin: auto 0px; +} + +main input { + -webkit-appearance: none; + appearance: none; + background-color: var(--color-white); + border: none; + border-bottom: 1px solid var(--color-tertiary); + border-radius: 0px; + color: var(--color-secondary); + font-size: 16px; + height: 42px; + outline: none; + padding: 0px 8px; + width: 100%; +} + +main input::placeholder { + color: var(--color-tertiary); + opacity: 1; +} + +main input:focus, +main input:hover { + border-bottom: 1px solid var(--color-primary); +} + +header > div, +main { + margin: 0px auto; + max-width: 768px; + padding: 16px; + width: 100%; +} + +.button-group { + display: flex; + justify-content: flex-end; + gap: 4px; + margin-bottom: 4px; +} + +#exclusion-list { + font-size: 14px; + list-style: none; + padding: 0px; +} + +#exclusion-list > li { + align-items: center; + border-radius: 4px; + display: flex; + justify-content: space-between; + padding: 8px; + transition: 0.4s; +} + +#exclusion-list > li:focus-within, +#exclusion-list > li:hover { + background-color: var(--color-secondary); + color: var(--color-white); +} + +#exclusion-list > li > button { + background-color: var(--color-white); + color: var(--color-error); + padding: 4px; +} diff --git a/packages/browser-extension/src/styles/popup.css b/packages/browser-extension/src/styles/popup.css index 8a0da66..1e8be98 100644 --- a/packages/browser-extension/src/styles/popup.css +++ b/packages/browser-extension/src/styles/popup.css @@ -21,6 +21,25 @@ body * { box-sizing: border-box; } +button { + align-items: center; + background-color: var(--color-secondary); + border: none; + border-radius: 4px; + color: var(--color-white); + display: inline-flex; + gap: 4px; + outline: none; + padding: 2px; + transition: 0.4s; +} + +button:focus, +button:hover { + background-color: var(--color-white); + color: var(--color-secondary); +} + footer { background-color: var(--color-secondary); font-size: 12px;