Compare commits

...

11 Commits

16 changed files with 633 additions and 87 deletions

12
.postcssrc Normal file
View File

@ -0,0 +1,12 @@
{
"plugins": {
"autoprefixer": {
"browsers": [
">1%",
"last 4 versions",
"Firefox ESR",
"not ie < 9"
]
}
}
}

View File

@ -26,6 +26,19 @@
</a>
</section>
<footer id="searchOptions" style="display:none;">
<div class="split">
<div class="quarter category">
<h3>Search For</h3>
</div>
<div class="three-quarter options">
<label>Case-Sensitive
<input type="checkbox" id="searchCaseSensitive">
</label>
<label>Exact Words
<input type="checkbox" id="searchExactWords">
</label>
</div>
</div>
<div class="split">
<div class="quarter category">
<h3>Include in Search</h3>
@ -55,6 +68,7 @@
<main>
<aside id="sideColumn">
<div id="mobileWordFormShow">+</div>
<form id="wordForm">
<label>Word<span class="red">*</span><br>
<input id="wordName">
@ -68,7 +82,7 @@
<label>Definition<span class="red">*</span><br>
<input id="wordDefinition" placeholder="Equivalent words">
</label>
<label>Details<span class="red">*</span><a class="label-button">Maximize</a><br>
<label>Details<span class="red">*</span><a class="label-button maximize-button">Maximize</a><br>
<textarea id="wordDetails" placeholder="Markdown formatting allowed"></textarea>
</label>
<div id="wordErrorMessage"></div>
@ -136,7 +150,7 @@
<label>Specification<br>
<input id="editSpecification">
</label>
<label>Description<a class="label-button">Maximize</a><br>
<label>Description<a class="label-button maximize-button">Maximize</a><br>
<textarea id="editDescription"></textarea>
</label>
</section>
@ -188,11 +202,11 @@
<textarea id="editExceptions"></textarea>
</label>
<h3>Orthography</h3>
<label>Notes<a class="label-button">Maximize</a><br>
<label>Notes<a class="label-button maximize-button">Maximize</a><br>
<textarea id="editOrthography"></textarea>
</label>
<h3>Grammar</h3>
<label>Notes<a class="label-button">Maximize</a><br>
<label>Notes<a class="label-button maximize-button">Maximize</a><br>
<textarea id="editGrammar"></textarea>
</label>
</section>

View File

@ -11,6 +11,7 @@
"bundle": "parcel build index.html"
},
"devDependencies": {
"autoprefixer": "^9.5.1",
"parcel-bundler": "^1.12.3",
"sass": "^1.19.0"
},

View File

@ -1,5 +1,18 @@
import { DEFAULT_PAGE_SIZE } from '../constants';
import { renderWords } from "./render";
export function getPaginationData(words) {
const numWords = words.length;
const pageSize = window.localStorage.getItem('pageSize') ? parseInt(window.localStorage.getItem('pageSize')) : DEFAULT_PAGE_SIZE;
const pages = Math.floor(numWords / pageSize);
const currentPage = window.hasOwnProperty('currentPage') ? window.currentPage : 0;
const pageStart = currentPage * pageSize;
const pageEnd = typeof words[pageStart + pageSize] !== 'undefined'
? pageStart + pageSize : words.length - 1;
return { numWords, pageSize, pages, currentPage, pageStart, pageEnd, };
}
export function goToPage(page) {
if (typeof page.target !== 'undefined') {
page = page.target.value;

View File

@ -3,8 +3,9 @@ import { removeTags, slugify } from '../helpers';
import { getWordsStats, wordExists } from './utilities';
import { getMatchingSearchWords, highlightSearchTerm, getSearchFilters, getSearchTerm } from './search';
import { showSection } from './displayToggles';
import { setupSearchFilters, setupWordOptionButtons, setupPagination, setupWordOptionSelections, setupEditFormButtons } from './setupListeners';
import { DEFAULT_PAGE_SIZE } from '../constants';
import { setupSearchFilters, setupWordOptionButtons, setupPagination, setupWordOptionSelections, setupWordEditFormButtons, setupMaximizeModal } from './setupListeners';
import { getPaginationData } from './pagination';
import { getOpenEditForms } from './wordManagement';
export function renderAll() {
renderDictionaryDetails();
@ -108,12 +109,19 @@ export function renderWords() {
const words = getMatchingSearchWords();
let wordsHTML = '';
const pageSize = window.localStorage.getItem('pageSize') ? parseInt(window.localStorage.getItem('pageSize')) : DEFAULT_PAGE_SIZE;
const currentPage = window.hasOwnProperty('currentPage') ? window.currentPage : 0;
const start = currentPage * pageSize;
const end = typeof words[start + pageSize] !== 'undefined' ? start + pageSize : words.length - 1;
const openEditForms = getOpenEditForms();
words.slice(start, end).forEach(originalWord => {
if (openEditForms.length > 0) {
// Clone the dom nodes
openEditForms.forEach((wordFormId, index) => {
openEditForms[index] = document.getElementById(wordFormId.toString()).cloneNode(true);
});
}
// const { pageStart, pageEnd } = getPaginationData(words);
// words.slice(pageStart, pageEnd).forEach(originalWord => {
words.forEach(originalWord => {
let detailsMarkdown = removeTags(originalWord.longDefinition);
const references = detailsMarkdown.match(/\{\{.+?\}\}/g);
if (references && Array.isArray(references)) {
@ -155,6 +163,16 @@ export function renderWords() {
});
document.getElementById('entries').innerHTML = wordsHTML;
if (openEditForms.length > 0) {
// Clone the dom nodes
openEditForms.forEach(editForm => {
const entryElement = document.getElementById(editForm.id);
entryElement.parentNode.replaceChild(editForm, entryElement);
});
setupWordEditFormButtons();
}
setupWordOptionButtons();
setupWordOptionSelections();
@ -165,23 +183,20 @@ export function renderWords() {
resultsText += !filters.allPartsOfSpeechChecked ? ' (Filtered)' : '';
document.getElementById('searchResults').innerHTML = resultsText;
renderPagination();
// renderPagination(words);
}
export function renderPagination() {
const numWords = window.currentDictionary.words.length;
const pageSize = window.localStorage.getItem('pageSize') ? parseInt(window.localStorage.getItem('pageSize')) : DEFAULT_PAGE_SIZE;
const pages = Math.floor(numWords / pageSize);
const currentPage = window.hasOwnProperty('currentPage') ? window.currentPage : 0;
export function renderPagination(filteredWords) {
const paginationData = getPaginationData(filteredWords);
if (pages > 0) {
let paginationHTML = (currentPage > 0 ? '<span class="button prev-button">&laquo; Previous</span>' : '')
if (paginationData.pages > 0) {
let paginationHTML = (paginationData.currentPage > 0 ? '<span class="button prev-button">&laquo; Previous</span>' : '')
+ '<select class="page-selector">';
for (let i = 0; i < pages; i++) {
paginationHTML += `<option value="${i}"${currentPage === i ? ' selected' : ''}>Page ${i + 1}</option>`;
for (let i = 0; i < paginationData.pages; i++) {
paginationHTML += `<option value="${i}"${paginationData.currentPage === i ? ' selected' : ''}>Page ${i + 1}</option>`;
}
paginationHTML += '</select>'
+ (currentPage < pages - 1 ? '<span class="button next-button">Next &raquo;</span>' : '');
+ (paginationData.currentPage < paginationData.pages - 1 ? '<span class="button next-button">Next &raquo;</span>' : '');
Array.from(document.getElementsByClassName('pagination')).forEach(pagination => {
pagination.innerHTML = paginationHTML;
@ -191,8 +206,8 @@ export function renderPagination() {
}
}
export function renderEditForm() {
const wordId = parseInt(this.id.replace('edit_', ''));
export function renderEditForm(wordId = false) {
wordId = typeof wordId.target === 'undefined' ? wordId : parseInt(this.id.replace('edit_', ''));
const word = window.currentDictionary.words.find(w => w.wordId === wordId);
if (word) {
const editForm = `<form id="editForm_${wordId}" class="edit-form">
@ -210,7 +225,7 @@ export function renderEditForm() {
<label>Definition<span class="red">*</span><br>
<input id="wordDefinition_${wordId}" value="${word.simpleDefinition}" placeholder="Equivalent words">
</label>
<label>Details<span class="red">*</span><a class="label-button">Maximize</a><br>
<label>Details<span class="red">*</span><a class="label-button maximize-button">Maximize</a><br>
<textarea id="wordDetails_${wordId}" placeholder="Markdown formatting allowed">${word.longDefinition}</textarea>
</label>
<div id="wordErrorMessage_${wordId}"></div>
@ -219,6 +234,28 @@ export function renderEditForm() {
</form>`;
document.getElementById(wordId.toString()).innerHTML = editForm;
setupEditFormButtons();
setupWordEditFormButtons();
}
}
}
export function renderMaximizedTextbox(maximizeButton) {
maximizeButton = typeof maximizeButton.target === 'undefined' ? maximizeButton : maximizeButton.target;
const label = maximizeButton.parentElement.innerText.replace(/(\*|Maximize)/g, '').trim();
const textBox = maximizeButton.parentElement.querySelector('textarea');
const modalElement = document.createElement('section');
modalElement.classList.add('modal');
modalElement.innerHTML = `<section class="modal maximize-modal"><div class="modal-background"></div>
<div class="modal-content">
<a class="close-button">&times;&#xFE0E;</a>
<header><h3>${label}</h3></header>
<section>
<textarea>${textBox.value}</textarea>
</section>
<footer><a class="button done-button">Done</a></footer>
</div>
</section>`;
document.body.appendChild(modalElement);
setupMaximizeModal(modalElement, textBox);
}

View File

@ -6,6 +6,8 @@ export function getSearchTerm() {
export function getSearchFilters() {
const filters = {
caseSensitive: document.getElementById('searchCaseSensitive').checked,
exact: document.getElementById('searchExactWords').checked,
name: document.getElementById('searchIncludeName').checked,
definition: document.getElementById('searchIncludeDefinition').checked,
details: document.getElementById('searchIncludeDetails').checked,
@ -26,31 +28,48 @@ export function getSearchFilters() {
}
export function getMatchingSearchWords() {
const searchTerm = getSearchTerm();
let searchTerm = getSearchTerm();
const filters = getSearchFilters();
const matchingWords = window.currentDictionary.words.slice().filter(word => {
if (!filters.allPartsOfSpeechChecked) {
const partOfSpeech = word.partOfSpeech === '' ? 'Unclassified' : word.partOfSpeech;
console.log('partOfSpeech', partOfSpeech);
return filters.partsOfSpeech.hasOwnProperty(partOfSpeech) && filters.partsOfSpeech[partOfSpeech];
}
return true;
}).filter(word => {
const isInName = filters.name && new RegExp(searchTerm, 'g').test(word.name);
const isInDefinition = filters.definition && new RegExp(searchTerm, 'g').test(word.simpleDefinition);
const isInDetails = filters.details && new RegExp(searchTerm, 'g').test(word.longDefinition);
return searchTerm === '' || isInName || isInDefinition || isInDetails;
});
return matchingWords;
if (searchTerm !== '' || !filters.allPartsOfSpeechChecked) {
const matchingWords = window.currentDictionary.words.slice().filter(word => {
if (!filters.allPartsOfSpeechChecked) {
const partOfSpeech = word.partOfSpeech === '' ? 'Unclassified' : word.partOfSpeech;
console.log('partOfSpeech', partOfSpeech);
return filters.partsOfSpeech.hasOwnProperty(partOfSpeech) && filters.partsOfSpeech[partOfSpeech];
}
return true;
}).filter(word => {
searchTerm = filters.caseSensitive ? searchTerm : searchTerm.toLowerCase();
const name = filters.caseSensitive ? word.name : word.name.toLowerCase();
const simpleDefinition = filters.caseSensitive ? word.simpleDefinition : word.simpleDefinition.toLowerCase();
const longDefinition = filters.caseSensitive ? word.longDefinition : word.longDefinition.toLowerCase();
const isInName = filters.name
&& (filters.exact
? searchTerm == name
: new RegExp(searchTerm, 'g').test(name));
const isInDefinition = filters.definition
&& (filters.exact
? searchTerm == simpleDefinition
: new RegExp(searchTerm, 'g').test(simpleDefinition));
const isInDetails = filters.details && new RegExp(searchTerm, 'g').test(longDefinition);
return searchTerm === '' || isInName || isInDefinition || isInDetails;
});
return matchingWords;
}
return window.currentDictionary.words
}
export function highlightSearchTerm(word) {
const searchTerm = getSearchTerm();
if (searchTerm) {
const filters = getSearchFilters();
const regexMethod = 'g' + (filters.caseSensitive ? '' : 'i');
const markedUpWord = cloneObject(word);
markedUpWord.name = markedUpWord.name.replace(new RegExp(searchTerm, 'g'), `<mark>${searchTerm}</mark>`);
markedUpWord.simpleDefinition = markedUpWord.simpleDefinition.replace(new RegExp(searchTerm, 'g'), `<mark>${searchTerm}</mark>`);
markedUpWord.longDefinition = markedUpWord.longDefinition.replace(new RegExp(searchTerm, 'g'), `<mark>${searchTerm}</mark>`);
markedUpWord.name = markedUpWord.name.replace(new RegExp(`(${searchTerm})`, regexMethod), `<mark>$1</mark>`);
markedUpWord.simpleDefinition = markedUpWord.simpleDefinition.replace(new RegExp(`(${searchTerm})`, regexMethod), `<mark>$1</mark>`);
markedUpWord.longDefinition = markedUpWord.longDefinition.replace(new RegExp(`(${searchTerm})`, regexMethod), `<mark>$1</mark>`);
return markedUpWord;
}
return word;

View File

@ -1,6 +1,6 @@
import {showSection} from './displayToggles';
import { renderWords, renderWordOptions, destroyWordOptions, renderEditForm } from './render';
import { validateWord, addWord } from './wordManagement';
import { renderWords, renderEditForm, renderMaximizedTextbox } from './render';
import { validateWord, addWord, confirmEditWord, cancelEditWord, confirmDeleteWord } from './wordManagement';
import { removeTags } from '../helpers';
import { getNextId } from './utilities';
import { openEditModal, save, saveAndClose } from './dictionaryManagement';
@ -10,6 +10,7 @@ export default function setupListeners() {
setupDetailsTabs();
setupSearchBar();
setupWordForm();
setupMobileWordFormButton();
}
function setupDetailsTabs() {
@ -73,12 +74,16 @@ function setupEditFormInteractions() {
function setupEditFormButtons() {
document.getElementById('editSave').addEventListener('click', () => save());
document.getElementById('editSaveAndClose').addEventListener('click', () => saveAndClose());
setupMaximizeButtons();
}
function setupSearchBar() {
const searchBox = document.getElementById('searchBox'),
clearSearchButton = document.getElementById('clearSearchButton'),
openSearchModal = document.getElementById('openSearchModal');
openSearchModal = document.getElementById('openSearchModal'),
searchExactWords = document.getElementById('searchExactWords'),
searchIncludeDetails = document.getElementById('searchIncludeDetails');
searchBox.addEventListener('change', () => {
renderWords();
});
@ -92,6 +97,17 @@ function setupSearchBar() {
});
openSearchModal.addEventListener('click', () => {
document.getElementById('searchModal').style.display = 'block';
searchBox.focus();
});
searchExactWords.addEventListener('change', () => {
if (searchExactWords.checked) {
searchIncludeDetails.checked = false;
searchIncludeDetails.disabled = true;
} else {
searchIncludeDetails.disabled = false;
searchIncludeDetails.checked = true;
}
});
}
@ -131,6 +147,8 @@ function setupWordForm() {
addWord(word);
}
});
setupMaximizeButtons();
}
export function setupWordOptionButtons() {
@ -167,22 +185,41 @@ export function setupWordOptionSelections() {
break;
}
case 'Delete': {
option.removeEventListener('click', confirmDeleteWord);
option.addEventListener('click', confirmDeleteWord);
break;
}
}
});
}
export function setupEditFormButtons() {
export function setupWordEditFormButtons() {
const saveChangesButtons = document.getElementsByClassName('edit-save-changes');
const cancelChangesButtons = document.getElementsByClassName('edit-cancel');
Array.from(saveChangesButtons).forEach(button => {
button.removeEventListener('click', renderEditForm);
button.addEventListener('click', renderEditForm);
button.removeEventListener('click', confirmEditWord);
button.addEventListener('click', confirmEditWord);
});
Array.from(cancelChangesButtons).forEach(button => {
button.removeEventListener('click', renderWords);
button.addEventListener('click', renderWords);
button.removeEventListener('click', cancelEditWord);
button.addEventListener('click', cancelEditWord);
});
setupMaximizeButtons();
}
export function setupMobileWordFormButton() {
const mobileButton = document.getElementById('mobileWordFormShow'),
wordForm = document.getElementById('wordForm');
mobileButton.addEventListener('click', () => {
if (mobileButton.innerText === '+') {
wordForm.style.display = 'block';
mobileButton.innerHTML = '&times;&#xFE0E;';
} else {
wordForm.style.display = '';
mobileButton.innerHTML = '+';
}
});
}
@ -205,3 +242,29 @@ export function setupPagination() {
pageSelector.addEventListener('change', goToPage);
});
}
export function setupMaximizeButtons() {
const maximizeButtons = document.getElementsByClassName('maximize-button');
Array.from(maximizeButtons).forEach(button => {
button.removeEventListener('click', renderMaximizedTextbox);
button.addEventListener('click', renderMaximizedTextbox);
});
}
export function setupMaximizeModal(modal, textBox) {
const closeElements = modal.querySelectorAll('.modal-background, .close-button, .done-button'),
maximizedTextBox = modal.querySelector('textarea');
Array.from(closeElements).forEach(close => {
close.addEventListener('click', () => {
modal.parentElement.removeChild(modal);
});
});
maximizedTextBox.addEventListener('change', () => {
textBox.value = maximizedTextBox.value;
})
setTimeout(() => {
modal.querySelector('textarea').focus();
}, 1);
}

View File

@ -1,6 +1,7 @@
import { renderWords } from "./render";
import { wordExists } from "./utilities";
import removeDiacritics from "./StackOverflow/removeDiacritics";
import { removeTags } from "../helpers";
export function validateWord(word, wordId = false) {
const errorElementId = wordId === false ? 'wordErrorMessage' : 'wordErrorMessage_' + wordId,
@ -18,7 +19,9 @@ export function validateWord(word, wordId = false) {
if (!allowDuplicates) {
const foundDuplicate = wordExists(word.name, true);
if (foundDuplicate !== false) {
errorMessage += `<p class="bold red">"<a href="#${foundDuplicate}">${word.name}</a>" already exists, and "Allow Duplicates" is turned off.${!caseSensitive ? ' <em>(Case sensitivity is turned also off)</em>' : ''}</p>`;
if (wordId !== false && foundDuplicate !== wordId) {
errorMessage += `<p class="bold red">"<a href="#${foundDuplicate}">${word.name}</a>" already exists, and "Prevent Duplicate Words" is turned on.${!caseSensitive ? ' <em>(Case sensitivity is turned off)</em>' : ''}</p>`;
}
}
}
@ -26,11 +29,10 @@ export function validateWord(word, wordId = false) {
return errorMessage === '';
}
export function addWord(word, render = true) {
export function sortWords(render) {
const { sortByDefinition } = window.currentDictionary.settings;
const sortBy = sortByDefinition ? 'simpleDefinition' : 'name';
window.currentDictionary.words.push(word);
window.currentDictionary.words.sort((wordA, wordB) => {
if (removeDiacritics(wordA[sortBy]).toLowerCase() === removeDiacritics(wordB[sortBy]).toLowerCase()) return 0;
return removeDiacritics(wordA[sortBy]).toLowerCase() > removeDiacritics(wordB[sortBy]).toLowerCase() ? 1 : -1;
@ -40,3 +42,87 @@ export function addWord(word, render = true) {
renderWords();
}
}
export function addWord(word, render = true) {
window.currentDictionary.words.push(word);
sortWords(render);
}
export function deleteWord(wordId) {
const wordIndex = window.currentDictionary.words.findIndex(word => word.wordId === wordId);
if (wordIndex > -1) {
window.currentDictionary.words.splice(wordIndex, 1);
}
sortWords(true);
}
export function updateWord(word, wordId) {
const wordIndex = window.currentDictionary.words.findIndex(word => word.wordId === wordId);
if (wordIndex < 0) {
console.error('Could not find word to update');
} else {
window.currentDictionary.words[wordIndex] = word;
sortWords(true);
}
}
export function confirmEditWord() {
const wordId = parseInt(this.id.replace('editWordButton_', ''));
const name = document.getElementById('wordName_' + wordId).value,
pronunciation = document.getElementById('wordPronunciation_' + wordId).value,
partOfSpeech = document.getElementById('wordPartOfSpeech_' + wordId).value,
definition = document.getElementById('wordDefinition_' + wordId).value,
details = document.getElementById('wordDetails_' + wordId).value;
const word = {
name: removeTags(name).trim(),
pronunciation: removeTags(pronunciation).trim(),
partOfSpeech: removeTags(partOfSpeech).trim(),
simpleDefinition: removeTags(definition).trim(),
longDefinition: removeTags(details).trim(),
wordId,
};
if (validateWord(word, wordId)) {
if (confirm(`Are you sure you want to save changes to "${word.name}"?`)) {
document.getElementById('editForm_' + wordId).classList.add('done');
updateWord(word, wordId);
}
}
}
export function cancelEditWord() {
const wordId = parseInt(this.parentElement.id.replace('editForm_', ''));
if (confirm(`Are you sure you want to cancel?\n(Any changes will be lost!)`)) {
document.getElementById('editForm_' + wordId).classList.add('done');
renderWords();
}
}
export function confirmDeleteWord(wordId) {
wordId = typeof wordId.target === 'undefined' ? wordId : parseInt(wordId.target.id.replace('delete_', ''));
const word = window.currentDictionary.words.find(w => w.wordId === wordId);
if (!word) {
console.error('Something went wrong! Couldn\'t find word with id of ' + wordId);
} else {
if (confirm(`Are you sure you want to delete "${word.name}"?`)) {
if (confirm(`Just to double-check:\nDo you really want to delete "${word.name}"?\n\nYou won't be able to undo it!`)) {
deleteWord(wordId);
}
}
}
}
export function getOpenEditForms() {
const openEditForms = document.getElementsByClassName('edit-form');
const formsToReopen = [];
Array.from(openEditForms).forEach(form => {
if (!form.classList.contains('done')) {
formsToReopen.push(parseInt(form.id.replace('editForm_', '')));
}
});
return formsToReopen;
}

View File

@ -5,6 +5,9 @@
@import 'styles/containers';
@import 'styles/structure';
@import 'styles/elements';
@import 'styles/mobile/containers';
@import 'styles/mobile/structure';
@import 'styles/mobile/elements';
html, body {
font-family: $font;

View File

@ -12,6 +12,7 @@ header {
main {
display: block;
width: 90%;
max-width: 1000px;
min-height: 400px;
margin: 0 auto;

View File

@ -11,6 +11,8 @@ p, span {
label {
display: block;
font-weight: bold;
line-height: 120%;
cursor: pointer;
&:not(:last-child) {
margin-bottom: $general-padding;
@ -27,13 +29,19 @@ label {
}
.label-button {
@extend .button;
font-size: 80%;
font-weight: normal;
float: right;
cursor: pointer;
line-height: 80% !important;
padding: 3px 3px 5px;
}
}
.tag {
display: inline-block;
padding: 3px 9px;
border: $border;
border-radius: 3px;
@ -66,6 +74,7 @@ span .tag {
.button {
@extend .tag;
cursor: pointer;
user-select: none;
}
.modal {
@ -87,10 +96,6 @@ span .tag {
.modal-content {
position: absolute;
// top: 10%;
// left: 20%;
// bottom: 10%;
// right: 20%;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
@ -108,6 +113,7 @@ span .tag {
right: 10px;
font-size: 200%;
cursor: pointer;
user-select: none;
}
}
}

View File

@ -1,7 +1,7 @@
#top {
#title {
display: inline-block;
margin: 3px 20px 3px 0;
margin: 3px $general-padding 3px 0;
}
#openSearchModal {
@ -25,7 +25,7 @@
max-height: unset;
margin: 0 auto;
width: 90%;
padding: 20px;
padding: $general-padding $general-padding ($general-padding / 2);
label {
display: inline;
@ -43,6 +43,16 @@
bottom: unset;
right: unset;
background-color: $white;
padding: ($general-padding / 2) $general-padding ($general-padding * 0.25);
font-size: 90%;
.split {
margin-bottom: 10px;
&:last-child {
margin-bottom: 0;
}
}
}
}
@ -51,6 +61,7 @@
}
.category {
margin-right: 8px;
text-align: right;
* {
margin: 0;
@ -59,22 +70,30 @@
.options {
label {
display: inline-block;
margin-right: 5px;
margin-bottom: 5px;
}
}
}
}
#mobileWordFormShow {
display: none;
}
#wordForm {
position: fixed;
top: auto;
width: 24%;
max-width: 275px;
padding: 10px;
background-color: $light;
border: $border;
border-radius: 5px;
max-height: 80%;
overflow-y: auto;
box-shadow: 4px 4px 5px 0px rgba(50, 50, 50, 0.75);
}
.edit-form {
@ -83,40 +102,52 @@
}
#detailsSection {
padding: 20px;
padding: $general-padding;
background-color: $white;
border: $border;
border-radius: 5px;
nav li {
display: inline-block;
list-style: none;
margin: 0;
padding: 10px 20px;
border: $border;
background-color: $light;
cursor: pointer;
&:first-child {
border-radius: 5px 0 0 5px;
}
&:last-child {
border-radius: 0 5px 5px 0;
}
&:not(:first-child) {
border-left: none;
}
#dictionaryName {
margin-top: 0;
}
&.active {
background-color: #bababa;
nav ul {
padding-left: 0;
li {
display: inline-block;
list-style: none;
margin: 0;
padding: 10px $general-padding;
border: $border;
background-color: $light;
cursor: pointer;
&:first-child {
border-radius: 5px 0 0 5px;
}
&:last-child {
border-radius: 0 5px 5px 0;
}
&:not(:first-child) {
border-left: none;
}
&.active {
background-color: #bababa;
}
}
}
#detailsPanel {
background-color: $white;
padding: 20px;
padding: $general-padding;
max-height: 400px;
overflow-y: auto;
h3 {
margin-top: 0;
}
}
}
@ -130,7 +161,7 @@
}
.pronunciation {
margin: 0 20px;
margin: 0 $general-padding;
}
.part-of-speech {
@ -205,6 +236,16 @@
}
#editDescription {
width: 100%;
height: 350px;
height: 280px;
}
}
.maximize-modal {
textarea {
width: 100%;
max-width: 100%;
min-height: 400px;
border: none;
padding: $general-padding;
}
}

View File

@ -0,0 +1,39 @@
@media (max-width: 750px) {
html, body {
font-size: 90%;
line-height: 90%;
}
header {
&#top {
margin-bottom: $general-padding;
}
}
main {
#sideColumn {
display: block;
width: 0;
height: block;
margin: 0;
overflow: visible;
}
#mainColumn {
width: 100%;
margin: 0;
}
article {
dl {
padding: 0 ($general-padding / 2);
dd {
margin: 0 ($general-padding * 0.75);
}
}
}
}
}

View File

@ -0,0 +1,90 @@
@media (max-width: 750px) {
.tag {
padding: 2px 6px;
font-size: 90%;
line-height: 120%;
&.small {
font-size: 70%;
line-height: 100%;
}
}
.button {
@extend .tag;
cursor: pointer;
user-select: none;
}
.tabs {
li {
padding: 5px;
}
}
.modal {
z-index: 10;
.modal-content .close-button {
top: 5px;
right: 5px;
}
}
.split {
display: block;
div {
display: block;
margin-right: unset;
&:last-child {
margin-left: unset;
}
}
&.two div,
&.three div,
div.third {
width: 100%;
margin: 10px auto;
}
div.quarter,
div.three-quarter {
display: inline-block;
}
div.quarter {
width: 30%;
}
div.three-quarter {
width: 66%;
}
}
.pagination {
position: relative;
text-align: center;
margin: $general-padding 0;
.page-selector {
padding: 5px 10px;
}
.prev-button,
.next-button {
position: absolute;
}
.prev-button {
left: 2.5%;
}
.next-button {
right: 2.5%;
}
}
}

View File

@ -0,0 +1,85 @@
@media (max-width: 750px) {
#top {
#openSearchModal {
width: 40%;
}
#searchModal {
.modal-content {
width: 100%;
padding: $general-padding ($general-padding / 2) ($general-padding / 4);
#searchBox {
margin-bottom: 5px;
}
section+footer {
padding: ($general-padding / 2) 0;
}
}
}
}
#mobileWordFormShow {
position: fixed;
top: $header-height;
left: 0;
width: 32px;
height: 32px;
display: block;
background-color: #00de00;
border: $border;
border-radius: 0 3px 3px 0;
color: $white;
font-size: 30px;
line-height: 24px;
font-weight: bold;
text-align: center;
cursor: pointer;
z-index: 1;
user-select: none;
}
#wordForm {
display: none;
width: 95%;
max-width: unset;
top: $header-height + 32px;
left: 0;
right: 3%;
z-index: 1;
}
#detailsSection {
nav ul li {
padding: 5px 10px;
}
#detailsPanel {
max-height: 300px;
}
}
.entry {
.pronunciation {
margin: 0 ($general-padding / 2);
}
}
#editModal {
$nav-font-height: 16px;
footer {
.button {
font-size: $nav-font-height - 2px;
line-height: $nav-font-height;
}
}
#editDescription {
width: 100%;
height: 260px;
}
}
}

View File

@ -943,6 +943,18 @@ atob@^2.1.1:
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
autoprefixer@^9.5.1:
version "9.5.1"
resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.5.1.tgz#243b1267b67e7e947f28919d786b50d3bb0fb357"
integrity sha512-KJSzkStUl3wP0D5sdMlP82Q52JLy5+atf2MHAre48+ckWkXgixmfHyWmA77wFDy6jTHU6mIgXv6hAQ2mf1PjJQ==
dependencies:
browserslist "^4.5.4"
caniuse-lite "^1.0.30000957"
normalize-range "^0.1.2"
num2fraction "^1.2.2"
postcss "^7.0.14"
postcss-value-parser "^3.3.1"
aws-sign2@~0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
@ -1133,7 +1145,7 @@ browserify-zlib@^0.2.0:
dependencies:
pako "~1.0.5"
browserslist@^4.0.0, browserslist@^4.1.0, browserslist@^4.3.4:
browserslist@^4.0.0, browserslist@^4.1.0, browserslist@^4.3.4, browserslist@^4.5.4:
version "4.5.6"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.5.6.tgz#ea42e8581ca2513fa7f371d4dd66da763938163d"
integrity sha512-o/hPOtbU9oX507lIqon+UvPYqpx3mHc8cV3QemSBTXwkG8gSQSK6UKvXcE/DcleU3+A59XTUHyCvZ5qGy8xVAg==
@ -1225,6 +1237,11 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000963:
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000963.tgz#5be481d5292f22aff5ee0db4a6c049b65b5798b1"
integrity sha512-n4HUiullc7Lw0LyzpeLa2ffP8KxFBGdxqD/8G3bSL6oB758hZ2UE2CVK+tQN958tJIi0/tfpjAc67aAtoHgnrQ==
caniuse-lite@^1.0.30000957:
version "1.0.30000967"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000967.tgz#a5039577806fccee80a04aaafb2c0890b1ee2f73"
integrity sha512-rUBIbap+VJfxTzrM4akJ00lkvVb5/n5v3EGXfWzSH5zT8aJmGzjA8HWhJ4U6kCpzxozUSnB+yvAYDRPY6mRpgQ==
caseless@~0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
@ -3296,6 +3313,11 @@ normalize-path@^3.0.0:
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
normalize-range@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942"
integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=
normalize-url@^3.0.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559"
@ -3336,6 +3358,11 @@ nth-check@^1.0.2:
dependencies:
boolbase "~1.0.0"
num2fraction@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede"
integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=
number-is-nan@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
@ -3975,6 +4002,15 @@ postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.11, postcss@^7.0.5:
source-map "^0.6.1"
supports-color "^6.1.0"
postcss@^7.0.14:
version "7.0.16"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.16.tgz#48f64f1b4b558cb8b52c88987724359acb010da2"
integrity sha512-MOo8zNSlIqh22Uaa3drkdIAgUGEL+AD1ESiSdmElLUmE2uVDo1QloiT/IfW9qRw8Gw+Y/w69UVMGwbufMSftxA==
dependencies:
chalk "^2.4.2"
source-map "^0.6.1"
supports-color "^6.1.0"
posthtml-parser@^0.3.3:
version "0.3.3"
resolved "https://registry.yarnpkg.com/posthtml-parser/-/posthtml-parser-0.3.3.tgz#3fe986fca9f00c0f109d731ba590b192f26e776d"