-
Notifications
You must be signed in to change notification settings - Fork 399
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
45276d6
commit 58d9efc
Showing
12 changed files
with
309 additions
and
191 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,76 +1,101 @@ | ||
(() => { | ||
class Copy { | ||
private DOM: { | ||
el: HTMLElement | null; | ||
}; | ||
btnClicked: HTMLElement | null = null; | ||
btnClickedIcons: HTMLElement[] = []; | ||
class Copy { | ||
private DOM: { | ||
el: HTMLElement | null; | ||
}; | ||
private static FEEDBACK_DELAY = 1500; | ||
|
||
constructor() { | ||
this.DOM = { | ||
el: document.querySelector("main"), | ||
}; | ||
private btnClicked: HTMLElement | null = null; | ||
private btnClickedIcons: HTMLElement[] = []; | ||
|
||
if (this.DOM.el) this.init(); | ||
} | ||
private static SELECTORS = { | ||
button: "[data-copy-btn]", | ||
icon: `[data-copy-icon] > use`, | ||
content: (id: string) => `[data-copy-content="${id}"]`, | ||
}; | ||
|
||
private init() { | ||
this.bindEvents(); | ||
} | ||
constructor() { | ||
this.DOM = { | ||
el: document.querySelector<HTMLElement>("main"), | ||
}; | ||
|
||
private bindEvents() { | ||
this.DOM.el?.addEventListener("click", this.handleClick.bind(this)); | ||
if (this.DOM.el) { | ||
this.init(); | ||
} else { | ||
console.warn("Copy: Main container not found."); | ||
} | ||
} | ||
|
||
private handleClick(event: Event) { | ||
const target = event.target as HTMLElement; | ||
const button = target.closest<HTMLElement>("[data-copy-btn]"); | ||
private init(): void { | ||
this.bindEvents(); | ||
} | ||
|
||
if (!button) return; | ||
private bindEvents(): void { | ||
this.DOM.el?.addEventListener("click", this.handleClick.bind(this)); | ||
} | ||
|
||
this.btnClicked = button; | ||
this.btnClickedIcons = Array.from(button.querySelectorAll<HTMLElement>(`[data-copy-icon] > use`)); | ||
private handleClick(event: Event): void { | ||
const target = event.target as HTMLElement; | ||
const button = target.closest<HTMLElement>(Copy.SELECTORS.button); | ||
|
||
const contentId = button.getAttribute("data-copy-btn"); | ||
if (!contentId) return; | ||
if (!button) return; | ||
|
||
const codeBlock = this.DOM.el?.querySelector<HTMLElement>(`[data-copy-content="${contentId}"]`); | ||
if (codeBlock) this.copyToClipboard(codeBlock); | ||
} | ||
this.btnClicked = button; | ||
this.btnClickedIcons = Array.from(button.querySelectorAll<HTMLElement>(Copy.SELECTORS.icon)); | ||
|
||
private sanitizeContent(codeBlock: HTMLElement): string { | ||
const html = codeBlock.innerHTML.replace(/<span class="chroma-ln">.*?<\/span>/g, ""); | ||
const tempDiv = document.createElement("div"); | ||
tempDiv.innerHTML = html; | ||
return tempDiv.textContent?.trim() || ""; | ||
const contentId = button.getAttribute("data-copy-btn"); | ||
if (!contentId) { | ||
console.warn("Copy: No content ID found on the button."); | ||
return; | ||
} | ||
|
||
private toggleIcons() { | ||
this.btnClickedIcons.forEach((icon) => { | ||
icon.classList.toggle("hidden"); | ||
}); | ||
const codeBlock = this.DOM.el?.querySelector<HTMLElement>(Copy.SELECTORS.content(contentId)); | ||
if (codeBlock) { | ||
this.copyToClipboard(codeBlock); | ||
} else { | ||
console.warn(`Copy: No content found for ID "${contentId}".`); | ||
} | ||
} | ||
|
||
private sanitizeContent(codeBlock: HTMLElement): string { | ||
const html = codeBlock.innerHTML.replace(/<span class="chroma-ln">.*?<\/span>/g, ""); | ||
const tempDiv = document.createElement("div"); | ||
tempDiv.innerHTML = html; | ||
return tempDiv.textContent?.trim() || ""; | ||
} | ||
|
||
private toggleIcons(): void { | ||
this.btnClickedIcons.forEach((icon) => { | ||
icon.classList.toggle("hidden"); | ||
}); | ||
} | ||
|
||
private showFeedback() { | ||
if (!this.btnClicked) return; | ||
private showFeedback(): void { | ||
if (!this.btnClicked) return; | ||
|
||
this.toggleIcons(); | ||
window.setTimeout(() => { | ||
this.toggleIcons(); | ||
window.setTimeout(() => { | ||
this.toggleIcons(); | ||
}, 1500); | ||
}, Copy.FEEDBACK_DELAY); | ||
} | ||
|
||
private async copyToClipboard(codeBlock: HTMLElement): Promise<void> { | ||
const sanitizedText = this.sanitizeContent(codeBlock); | ||
|
||
if (!navigator.clipboard) { | ||
console.error("Copy: Clipboard API is not supported in this browser."); | ||
this.showFeedback(); | ||
return; | ||
} | ||
|
||
async copyToClipboard(codeBlock: HTMLElement) { | ||
const sanitizedText = this.sanitizeContent(codeBlock); | ||
try { | ||
await navigator.clipboard.writeText(sanitizedText); | ||
this.showFeedback(); | ||
} catch (err) { | ||
console.error("Copy error: ", err); | ||
this.showFeedback(); | ||
} | ||
try { | ||
await navigator.clipboard.writeText(sanitizedText); | ||
console.info("Copy: Text copied successfully."); | ||
this.showFeedback(); | ||
} catch (err) { | ||
console.error("Copy: Error while copying text.", err); | ||
this.showFeedback(); | ||
} | ||
} | ||
} | ||
|
||
document.addEventListener("DOMContentLoaded", () => new Copy()); | ||
})(); | ||
export default () => new Copy(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
(() => { | ||
interface Module { | ||
selector: string; | ||
path: string; | ||
} | ||
|
||
const modules: Record<string, Module> = { | ||
copy: { | ||
selector: "[data-copy-btn]", | ||
path: "/public/js/copy.js", | ||
}, | ||
help: { | ||
selector: "#data", | ||
path: "/public/js/realmhelp.js", | ||
}, | ||
searchBar: { | ||
selector: "#header-searchbar", | ||
path: "/public/js/searchbar.js", | ||
}, | ||
}; | ||
|
||
const loadModuleIfExists = async ({ selector, path }: Module): Promise<void> => { | ||
const element = document.querySelector(selector); | ||
if (element) { | ||
try { | ||
const module = await import(path); | ||
module.default(); | ||
} catch (err) { | ||
console.error(`Error while loading script ${path}:`, err); | ||
} | ||
} else { | ||
console.info(`Module not loaded: no element matches selector "${selector}"`); | ||
} | ||
}; | ||
|
||
const initModules = async (): Promise<void> => { | ||
const promises = Object.values(modules).map((module) => loadModuleIfExists(module)); | ||
await Promise.all(promises); | ||
}; | ||
|
||
document.addEventListener("DOMContentLoaded", initModules); | ||
})(); |
Oops, something went wrong.