Creating options menu HTML and CSS

This commit is contained in:
Jean Viscogliosi-Pate 2024-12-20 09:25:42 -08:00
parent 7bdf568b2a
commit 438f015f62
8 changed files with 499 additions and 43 deletions

48
src/global.css Normal file
View File

@ -0,0 +1,48 @@
:root {
--background-color: white;
--text-color: black;
--large-text: 16px;
--small-text: 14px;
}
body {
background-color: var(--background-color);
color: var(--text-color);
width: 350px;
padding-left: 10px;
padding-right: 10px;
font-family: 'Inter', sans-serif;
font-size: var(--small-text);
margin: auto;
overflow-y: scroll;
}
button {
display: flex;
justify-content: center;
align-items: center;
border: 0;
border-radius: 30px;
width: 100%;
font-size: var(--large-text);
padding: 6px;
margin-top: 6px;
margin-bottom: 6px;
gap: 8px;
text-align: center;
cursor: pointer;
}
button:hover {
transition-duration: 0.3s;
}
.visually-hidden {
clip: rect(0 0 0 0);
clip-path: inset(50%);
height: 1px;
overflow: hidden;
position: absolute;
white-space: nowrap;
width: 1px;
}

View File

@ -9,11 +9,16 @@
"browser_action": { "browser_action": {
"default_title": "MetamorPOV", "default_title": "MetamorPOV",
"default_icon": { "default_icon": {
"32": "img/mpov-32.png", "16": "img/mpov-16.png",
"48": "img/mpov-48.png" "32": "img/mpov-32.png",
"48": "img/mpov-48.png",
"64": "img/mpov-64.png",
"96": "img/mpov-96.png",
"128": "img/mpov-128.png"
}, },
"default_popup": "popup/popup.html" "default_popup": "popup/popup.html"
}, },
"permissions": [ "permissions": [
"storage", "storage",
"tabs" "tabs"
@ -39,8 +44,14 @@
] ]
} }
], ],
"options_ui": {
"page": "options/options.html"
},
"background": { "background": {
"page": "popup/popup.html" "page": "popup/popup.html"
}, },
"homepage_url": "https://git.viscogliosi-pate.com/jean/metamorpov" "homepage_url": "https://git.viscogliosi-pate.com/jean/metamorpov"
} }

View File

@ -0,0 +1,210 @@
document.addEventListener('DOMContentLoaded', function () {
// event listeners
document.getElementById('change-name-form').addEventListener('submit', changeName)
document.getElementById('replace-other-words-form').addEventListener('submit', replaceOther)
document.getElementById('show-saved').addEventListener('click', loadSaved)
document.getElementById('refresh-replacements').addEventListener('click', refreshReplacements)
document.getElementById('enable-observer').addEventListener('click', toggleMutationObserver)
document.getElementById('toggle').addEventListener('click', togglePauseDomain)
// set settings buttons
setMutationObserverKey()
setPauseDomainKey()
});
const changeName = () => {
const person = document.getElementById('change-name-form-text').value
if (person) {
chrome.storage.sync.set({'person': person}, chrome.tabs.reload())
}
}
const replaceOther = () => {
const input_word = document.getElementById('replace-word').value
const replacement = document.getElementById('replace-with').value
const is_case_sensitive = document.getElementById('is-case-sensitive').checked
if (input_word && replacement) {
if (document.getElementById('is-perm').checked) {
const obj = {}
obj[input_word] = replacement
obj[`${input_word}_case_sensitive`] = is_case_sensitive
chrome.storage.sync.set(obj)
}
chrome.tabs.query({ active: true, currentWindow: true }, tabs => {
chrome.tabs.sendMessage(
tabs[0].id,
{
input_word: input_word,
replace_value: replacement,
case_sensitive: is_case_sensitive
})
})
}
}
const refreshReplacements = () => {
chrome.tabs.query({ active: true, currentWindow: true }, tabs => {
chrome.tabs.sendMessage(
tabs[0].id,
{
refresh: true
})
})
}
const loadSaved = () => {
const list = document.getElementById('saved-items-list')
list.innerHTML = ''
chrome.storage.sync.get(null, loadSavedItemsWrapper(false))
chrome.storage.local.get(null, loadSavedItemsWrapper(true))
}
const loadSavedItemsWrapper = isLocal => items => loadSavedItems(items, isLocal)
const loadSavedItems = (items, isLocal) => {
const list = document.getElementById('saved-items-list')
for (var key in items) {
if (key !== DEACTIVATE_KEY && key !== MUTATION_OBSERVER_KEY && key !== PAUSED_KEY && !key.endsWith('_case_sensitive')) {
const label = key === 'person' ? 'Y/N' : key
const case_sensitive = !!items[`${key}_case_sensitive`]
const case_sensitive_string = case_sensitive ? 'case sensitive' : 'not case sensitive'
const representative = `${label} -> ${items[key]} (${case_sensitive_string})`
const list_item = createListItem(key, representative, 'one-saved-item', isLocal)
list.appendChild(list_item)
}
}
}
const createListItem = (id, text, className, isLocal) => {
const text_node = document.createTextNode(text)
const list_node = document.createElement('LI')
list_node.appendChild(text_node)
list_node.className = className
list_node.id = id
list_node.addEventListener('click', () => {
if (isLocal) {
chrome.storage.local.remove(id)
} else {
chrome.storage.sync.remove(id)
}
list_node.className += ' strikethrough'
})
return list_node
}
const setDeactivateKey = () => {
chrome.storage.sync.get(DEACTIVATE_KEY, obj => {
const is_deactivated = obj[DEACTIVATE_KEY]
toggleDeactivateLabel(is_deactivated)
if (is_deactivated) {
const other_elements = document.getElementsByClassName('fade-when-deactivate')
const deactivate_only_elements = document.getElementsByClassName('fade-when-deactivate-only')
Array.from([...other_elements, ...deactivate_only_elements]).forEach(element => {
element.style.opacity = '0.5'
})
const input_elements = document.getElementsByTagName('INPUT')
Array.from(input_elements).forEach(input => {
if (input.id !== 'deactivate') {
input.disabled = 'disabled'
}
})
}
})
}
const setMutationObserverKey = () => {
chrome.storage.sync.get(MUTATION_OBSERVER_KEY, obj => {
const is_enabled = obj[MUTATION_OBSERVER_KEY]
toggleMutationObserverLabel(is_enabled)
})
}
const setPauseDomainKey = () => {
chrome.tabs.query({ active: true, currentWindow: true }, tabs => {
const hostname = new URL(tabs[0].url).hostname
document.getElementById('this-url').innerHTML = hostname
const storage_key = {}
storage_key[PAUSED_KEY] = []
chrome.storage.sync.get(storage_key, obj => {
const hostnames = obj[PAUSED_KEY]
const is_paused = hostnames.indexOf(hostname) !== -1
togglePauseDomainLabel(is_paused)
if (is_paused) {
const other_elements = document.getElementsByClassName('fade-when-deactivate')
const pause_only_other_elements = document.getElementsByClassName('fade-when-pause')
Array.from([...other_elements, ...pause_only_other_elements]).forEach(element => {
element.style.opacity = '0.5'
})
const input_elements = document.getElementsByTagName('INPUT')
Array.from(input_elements).forEach(input => {
if (input.id !== 'pause' && input.id !== 'deactivate') {
input.disabled = 'disabled'
}
})
}
})
})
}
const toggleDeactivateLabel = (isDeactivated) => {
document.getElementById('deactivate').checked = isDeactivated;
}
const toggleMutationObserverLabel = (isEnabled) => {
document.getElementById('enable-observer').checked = isEnabled;
}
const togglePauseDomainLabel = (isPaused) => {
document.getElementById('pause').checked = isPaused;
}
const toggleMutationObserver = () => {
chrome.storage.sync.get(MUTATION_OBSERVER_KEY, obj => {
const was_enabled = obj[MUTATION_OBSERVER_KEY]
if (was_enabled) {
chrome.storage.sync.remove(MUTATION_OBSERVER_KEY)
} else {
const new_object = {}
new_object[MUTATION_OBSERVER_KEY] = true
chrome.storage.sync.set(new_object)
}
chrome.tabs.reload()
})
}
const togglePauseDomain = () => {
chrome.tabs.query({ active: true, currentWindow: true }, tabs => {
const hostname = new URL(tabs[0].url).hostname
const storage_key = {}
storage_key[PAUSED_KEY] = []
chrome.storage.sync.get(storage_key, obj => {
const hostnames = obj[PAUSED_KEY]
const was_paused = hostnames.indexOf(hostname) !== -1
var new_hostnames;
if (was_paused) {
new_hostnames = hostnames.filter(h => h !== hostname)
} else {
new_hostnames = hostnames
new_hostnames.push(hostname)
}
const new_obj = {}
new_obj[PAUSED_KEY] = new_hostnames
chrome.storage.sync.set(new_obj)
chrome.tabs.reload()
window.close()
})
})
}

89
src/options/options.css Normal file
View File

@ -0,0 +1,89 @@
@import url("../global.css");
input {
border: 0;
border-radius: 30px;
font-size: var(--large-text);
padding: 2px 8px 2px 8px;
gap: 8px;
box-sizing: border-box;
width: 100%;
background-color: rgb(233, 233, 237);
}
select {
border: 0;
border-radius: 30px;
font-size: var(--large-text);
padding: 2px 2px 2px 8px;
gap: 8px;
}
label {
font-size: var(--large-text);
}
.primary-prompt {
display: flex;
align-items: center;
text-wrap: nowrap;
gap: 6px;
margin-top: 20px;
}
.primary-promt input {
font-size: 50px;
}
#prn-other {
display: grid;
gap: 6px;
margin-top: 12px;
margin-left: 18px;
}
.prn {
display: grid;
grid-template-columns: 1fr 2fr;
align-items: center;
gap: 6px;
}
.prn label {
font-size: var(--small-text);
text-align: right;
text-wrap: nowrap;
}
.prn input {
font-size: var(--small-text);
}
.prn select {
font-size: var(--small-text);
width: min-content;
}
.replace {
display: flex;
align-items: center;
gap: 6px;
margin-top: 6px;
}
.replace img {
cursor: pointer;
visibility: hidden;
}
#replace-1 {
text-align: right;
}
#button-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
margin-top: 20px;
margin-bottom: 20px;
}

126
src/options/options.html Normal file
View File

@ -0,0 +1,126 @@
<!DOCTYPE html>
<meta charset="UTF-8">
<html>
<head>
<link href='options.css' rel='stylesheet' type='text/css'>
<script src="options.js" type="text/javascript"></script>
</head>
<body>
<main>
<h1 class="visually-hidden">Options</h1>
<form>
<h2 class="visually-hidden">Name</h2>
<div class="primary-prompt">
<label for="y-n">My name is</label>
<input type="text" id="y-n" name="name">
</div>
<h2 class="visually-hidden">Pronouns</h2>
<div class="primary-prompt">
<label for="prn-preset">My pronouns are</label>
<select id="prn-preset" name="preset">
<option value=""></option>
<option value="he">he/him</option>
<option value="she">she/her</option>
<option value="they">they/them</option>
<option value="other">other</option>
</select>
</div>
<div id="prn-other">
<div class="prn">
<label for="prn-s">Subjective</label>
<input type="text" id="prn-s" name="subjective" placeholder="e.g. he, they">
</div>
<div class="prn">
<label for="prn-o">Objective</label>
<input type="text" id="prn-o" name="objective" placeholder="e.g. her, them">
</div>
<div class="prn">
<label for="prn-p">Possessive</label>
<input type="text" id="prn-p" name="possessive" placeholder="e.g. his, their">
</div>
<div class="prn">
<label for="prn-a">Adjective</label>
<input type="text" id="prn-a" name="adjective" placeholder="e.g. hers, theirs">
</div>
<div class="prn">
<label for="prn-r">Reflexive</label>
<input type="text" id="prn-r" name="reflexive" placeholder="e.g. himself, themself">
</div>
<div class="prn">
<label for="prn-h-abbr">Honorific abbr.</label>
<input type="text" id="prn-h-abbr" name="honorific-abbr" placeholder="e.g. Ms., Mx.">
</div>
<div class="prn">
<label for="prn-h">Honorific</label>
<input type="text" id="prn-h" name="honorific" placeholder="e.g. mister, mix">
</div>
<div class="prn">
<label for="prn-n-adult">Adult noun</label>
<input type="text" id="prn-n-adult" name="adult-noun" placeholder="e.g. woman, person">
</div>
<div class="prn">
<label for="prn-n-child">Child noun</label>
<input type="text" id="prn-n-child" name="child-noun" placeholder="e.g. boy, kid">
</div>
<div class="prn">
<label for="prn-plural">Conjugates</label>
<select id="prn-plural" name="conjugates">
<option value="singular">singular</option>
<option value="plural">plural</option>
</select>
</div>
</div>
<h2 class="visually-hidden">Perspective</h2>
<div class="primary-prompt">
<label for="pov">I prefer to read in</label>
<select id="pov" name="pov">
<option value=""></option>
<option value="first">first-person</option>
<option value="second">second-person</option>
<option value="third">third-person</option>
</select>
</div>
<h2 class="visually-hidden">Also replace</h2>
<label class="primary-prompt" for="replace-1">I also want to replace...</label>
<div class="replace">
<input type="text" id="replace-1" name="replace-1">
<label for="replace-with-1">with</label>
<input type="text" id="replace-with-1" name="replace-with-1">
<img src="../img/delete.svg" alt="Clear fields">
</div>
<div id="button-grid">
<button>
<img src="../img/save.svg" alt="">
Save
</button>
<button>
<img src="../img/revert.svg" alt="">
Revert
</button>
</div>
</form>
</main>
</body>
</html>

3
src/options/options.js Normal file
View File

@ -0,0 +1,3 @@
// Event listeners
document.getElementById("y-n").addEventListener("submit", changeName)

View File

@ -1,20 +1,7 @@
:root { @import url("../global.css");
--background-color: white;
--text-color: black;
}
body {
background-color: var(--background-color);
color: var(--text-color);
width: 350px;
padding-left: 10px;
padding-right: 10px;
font-family: 'Inter', sans-serif;
font-size: 12pt;
}
#hostname { #hostname {
font-size: 14pt; font-size: var(--large-text);
font-weight: 400; font-weight: 400;
text-align: center; text-align: center;
} }
@ -32,7 +19,7 @@ body {
} }
#display-enabled h2 { #display-enabled h2 {
font-size: 14pt; font-size: var(--large-text);
font-weight: 400; font-weight: 400;
margin-right: 15%; margin-right: 15%;
} }
@ -52,26 +39,6 @@ body {
margin-top: 0; margin-top: 0;
} }
button {
display: flex;
justify-content: center;
align-items: center;
border: 0;
border-radius: 30px;
width: 100%;
font-size: 12pt;
padding: 6px;
margin-top: 6px;
margin-bottom: 6px;
gap: 8px;
text-align: center;
cursor: pointer;
}
button:hover {
transition-duration: 0.3s;
}
ul { ul {
text-align: right; text-align: right;
list-style: none; list-style: none;

View File

@ -33,10 +33,12 @@
</div> </div>
</main> </main>
<button id="configure"> <a href="../options/options.html">
<img src="../img/configure.svg" alt=""/> <button id="configure">
Configure name, pronouns, and POV <img src="../img/configure.svg" alt=""/>
</button> Configure name, pronouns, and POV
</button>
</a>
<button id="refresh"> <button id="refresh">
<img src="../img/refresh.svg" alt=""/> <img src="../img/refresh.svg" alt=""/>
@ -45,7 +47,7 @@
<ul class="links"> <ul class="links">
<li><a href="https://git.viscogliosi-pate.com/jean/metamorpov/wiki" target="_blank">How to write for MetamorPOV</a></li> <li><a href="https://git.viscogliosi-pate.com/jean/metamorpov/wiki" target="_blank">How to write for MetamorPOV</a></li>
<li><a href="https://git.viscogliosi-pate.com/jean/metamorpov/issues" target="_blank">Report an issue</a></li> <li><a href="mailto:metamorpov@viscogliosi-pate.com" target="_blank">Report an issue</a></li>
<li><a href="https://git.viscogliosi-pate.com/jean/metamorpov" target="_blank">Source code</a></li> <li><a href="https://git.viscogliosi-pate.com/jean/metamorpov" target="_blank">Source code</a></li>
</ul> </ul>
</body> </body>