Skip to content

Instantly share code, notes, and snippets.

@Caellian
Created May 22, 2023 13:44
Show Gist options
  • Save Caellian/7c20bb4c75c255b63fcf9c5c160a488e to your computer and use it in GitHub Desktop.
Save Caellian/7c20bb4c75c255b63fcf9c5c160a488e to your computer and use it in GitHub Desktop.
Obsidian dictionary view
.dictionary-view {
display: contents;
}
.dictionary-view * {
box-sizing: border-box;
padding: 0;
margin: 0;
}
.dictionary-view .search-bar {
display: flex;
margin-inline: auto;
align-items: center;
width: fit-content;
max-width: 100%;
height: fit-content;
gap: 0.25rem;
padding: 0.5rem;
padding-left: 2rem;
background-color: var(--background-secondary-alt);
border-radius: 100vh;
}
.dictionary-view .search-bar input[type="search"] {
display: inline-block;
background-color: var(--color-base-10);
width: 100%;
max-width: 40vw;
border: none;
outline: none;
border-bottom: 2px solid var(--color-accent);
border-radius: 0.2rem 0.2rem 0 0;
font-size: 1.2rem;
}
.dictionary-view .search-bar svg {
height: 2rem;
aspect-ratio: 1 / 1;
fill: var(--color-accent);
}
.dictionary-view table {
width: 100%;
}
.dictionary-view ul.results {
display: flex;
flex-direction: column;
gap: 0.2rem;
list-style: none;
}
.dictionary-view h6.letter-group {
font-size: 2rem;
color: var(--color-accent);
margin-block: 1rem;
padding-inline: 0.5rem;
border-bottom: 2px solid var(--color-base-10);
}
.dictionary-view .word {
display: grid;
grid-template-columns: 2fr auto 5fr;
}
.dictionary-view .word .name {
text-decoration: none;
}
.dictionary-view .word .name span {
font-weight: bold;
}
.dictionary-view .word .icon {
fill: var(--background-secondary-alt);
width: 2rem;
height: 2rem;
}
.dictionary-view .word .icon.menu-right {
fill: var(--color-accent);
}
.dictionary-view .word .translations {
display: flex;
gap: 1rem;
align-items: flex-start;
padding: 0.5rem;
height: min-content;
background-color: var(--color-base-10);
border-radius: 0.25rem;
}
.dictionary-view .word .translations .icon.translate {
display: inline-block;
fill: var(--color-blue);
flex-shrink: 0;
width: 1.25em;
height: 1.25em;
}
.dictionary-view .word .translations ul {
flex-grow: 1;
display: flex;
flex-wrap: wrap;
gap: 0.25rem;
list-style: none;
}
.dictionary-view .word .translations ul li {
background-color: var(--color-base-20);
padding: 0.25rem 0.5rem;
border-radius: 0.25rem;
}
const {} = input;
const IconMagnify =
'<svg class="icon magnify" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>magnify</title><path d="M9.5,3A6.5,6.5 0 0,1 16,9.5C16,11.11 15.41,12.59 14.44,13.73L14.71,14H15.5L20.5,19L19,20.5L14,15.5V14.71L13.73,14.44C12.59,15.41 11.11,16 9.5,16A6.5,6.5 0 0,1 3,9.5A6.5,6.5 0 0,1 9.5,3M9.5,5C7,5 5,7 5,9.5C5,12 7,14 9.5,14C12,14 14,12 14,9.5C14,7 12,5 9.5,5Z" /></svg>';
const IconTranslate =
'<svg class="icon translate" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>translate</title><path d="M12.87,15.07L10.33,12.56L10.36,12.53C12.1,10.59 13.34,8.36 14.07,6H17V4H10V2H8V4H1V6H12.17C11.5,7.92 10.44,9.75 9,11.35C8.07,10.32 7.3,9.19 6.69,8H4.69C5.42,9.63 6.42,11.17 7.67,12.56L2.58,17.58L4,19L9,14L12.11,17.11L12.87,15.07M18.5,10H16.5L12,22H14L15.12,19H19.87L21,22H23L18.5,10M15.88,17L17.5,12.67L19.12,17H15.88Z" /></svg>';
const IconMenuRight =
'<svg class="icon menu-right" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>menu-right</title><path d="M10,17L15,12L10,7V17Z" /></svg>';
/**
*
*/
class Word {
constructor(value, definition, translations) {
this.value = value;
this.definition = definition;
this.translations = translations;
}
}
function renderTranslation(word, filter = "") {
let [key, value, source] = word;
let wordName = "";
if (filter === "") {
wordName = `<a class="name internal-link" href="${source}">${key}</a>`;
} else {
const index = key.toLowerCase().indexOf(filter.toLowerCase());
const before = key.slice(0, index);
const found = key.slice(index, index + filter.length);
const after = key.slice(index + filter.length);
wordName = `<a class="name internal-link" href="${source}">${before}<span>${found}</span>${after}</a>`;
}
const valueList = value
.split(",")
.map((it) => `<li>${it.trim()}</li>`)
.join("");
return `<li class="word">${wordName}${IconMenuRight}<div class="translations">${IconTranslate}<ul>${valueList}</ul></div></li>`;
}
function buildDictionary() {
const translationFiles = dv.pagePaths("#translation");
const words = {};
for (const source of translationFiles) {
// if d source and target language ok
const pageLists = dv.page(source).file.lists;
for (const line of pageLists) {
let text = line.text;
if (text.toLowerCase().includes("#translation") && text.includes("->")) {
text = text.replace("#translation", "");
let [key, value] = text.split("->");
key = key.trim();
value = value.trim();
let first = key.toLowerCase().charAt(0);
if (first in words) {
words[first].push([key, value, source]);
} else {
words[first] = [[key, value, source]];
}
}
}
}
return {
words,
render: function (filter = "") {
var result = [];
for (const letter of Object.keys(words)) {
const drawn = words[letter].filter(([key]) =>
key.toLowerCase().includes(filter.toLowerCase())
);
if (drawn.length > 0) {
result.push(`<h6 class="letter-group">${letter.toUpperCase()}</h6>`);
for (const word of drawn) {
result.push(renderTranslation(word, filter));
}
}
}
return result.join("");
},
};
}
const dictionary = buildDictionary();
const content = dv.el(
"div",
`<div class="search-bar"><input type="search"/>${IconMagnify}</div><ul class="results">${dictionary.render()}</ul>`,
{
cls: "dictionary-view",
}
);
const resultList = content.querySelector(".results");
function onSearchChange(e) {
resultList.innerHTML = `${dictionary.render(e.target.value)}`;
}
const searchInput = content.querySelector('input[type="search"]');
searchInput.addEventListener("input", onSearchChange);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment