create gists
This commit is contained in:
parent
79a17290d5
commit
43a8412f06
90 changed files with 1777 additions and 2107 deletions
|
|
@ -21,6 +21,55 @@ import "phoenix_html";
|
|||
import { Socket } from "phoenix";
|
||||
import { LiveSocket } from "phoenix_live_view";
|
||||
import topbar from "../vendor/topbar";
|
||||
import CodeBlockHook from "./hooks/code_block_hook";
|
||||
import {
|
||||
DropdownAnimation,
|
||||
SearchableDropdown,
|
||||
} from "./hooks/searchable_dropdown";
|
||||
|
||||
let Hooks = {
|
||||
CodeBlockHook,
|
||||
SearchableDropdown,
|
||||
DropdownAnimation,
|
||||
};
|
||||
|
||||
Hooks.ClickOutside = {
|
||||
mounted() {
|
||||
this.handleClick = (e) => {
|
||||
if (!this.el.contains(e.target)) {
|
||||
this.pushEventTo(this.el, "close_dropdown", {});
|
||||
}
|
||||
};
|
||||
document.addEventListener("click", this.handleClick);
|
||||
},
|
||||
destroyed() {
|
||||
document.removeEventListener("click", this.handleClick);
|
||||
},
|
||||
};
|
||||
|
||||
Hooks.ClientSearch = {
|
||||
mounted() {
|
||||
this.options = JSON.parse(this.el.dataset.options);
|
||||
this.searchInput = this.el.querySelector("input");
|
||||
this.optionsContainer = this.el.querySelector(
|
||||
"#dropdown-options-container",
|
||||
);
|
||||
|
||||
this.searchInput.addEventListener("input", (e) => {
|
||||
const query = e.target.value.toLowerCase();
|
||||
const options = this.el.querySelectorAll(".dropdown-option");
|
||||
|
||||
options.forEach((option) => {
|
||||
const value = option.dataset.value.toLowerCase();
|
||||
if (value.includes(query)) {
|
||||
option.style.display = "";
|
||||
} else {
|
||||
option.style.display = "none";
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
window.addEventListener("phx:copy", (event) => {
|
||||
let button = event.detail.dispatcher;
|
||||
|
|
@ -40,6 +89,7 @@ let csrfToken = document
|
|||
let liveSocket = new LiveSocket("/live", Socket, {
|
||||
longPollFallbackMs: 2500,
|
||||
params: { _csrf_token: csrfToken },
|
||||
hooks: Hooks,
|
||||
});
|
||||
|
||||
// Show progress bar on live navigation and form submits
|
||||
|
|
|
|||
59
assets/js/hooks/code_block_hook.js
Normal file
59
assets/js/hooks/code_block_hook.js
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
// assets/js/hooks/code_block_hook.js
|
||||
import { codeToHtml } from "shiki";
|
||||
|
||||
const CodeBlockHook = {
|
||||
mounted() {
|
||||
const code = this.el.dataset.code;
|
||||
const language = this.el.dataset.language;
|
||||
const highlightedLines = JSON.parse(
|
||||
this.el.dataset.highlightedLines || "[]",
|
||||
);
|
||||
|
||||
console.log(code);
|
||||
console.log("language", language);
|
||||
|
||||
const lines = code.split("\n");
|
||||
|
||||
// Convert line numbers to decorations
|
||||
const decorations = highlightedLines
|
||||
.map((line) => {
|
||||
// Convert to 0-based index and ensure valid line number
|
||||
const lineIndex = line - 1;
|
||||
if (lineIndex < 0 || lineIndex >= lines.length) return null;
|
||||
|
||||
// Get the actual line length
|
||||
const lineLength = lines[lineIndex].length;
|
||||
|
||||
return {
|
||||
// Line numbers are 0-indexed
|
||||
start: { line: lineIndex, character: 0 },
|
||||
end: { line: lineIndex, character: lineLength },
|
||||
properties: {
|
||||
// Apply both background color and a class for flexibility
|
||||
class: "highlight",
|
||||
style: "background-color: rgba(200,200,255,0.1);",
|
||||
},
|
||||
};
|
||||
})
|
||||
.filter(Boolean); // Remove any null entries from invalid line numbers
|
||||
|
||||
codeToHtml(code, {
|
||||
lang: language,
|
||||
theme: "catppuccin-mocha",
|
||||
decorations,
|
||||
}).then((html) => {
|
||||
console.log(html);
|
||||
|
||||
// Replace the code content while preserving the pre/code structure
|
||||
const tempDiv = document.createElement("div");
|
||||
tempDiv.innerHTML = html;
|
||||
const codeContent = tempDiv.querySelector("code");
|
||||
|
||||
if (codeContent) {
|
||||
this.el.querySelector("code").innerHTML = codeContent.innerHTML;
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default CodeBlockHook;
|
||||
32
assets/js/hooks/searchable_dropdown.js
Normal file
32
assets/js/hooks/searchable_dropdown.js
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
SearchableDropdown = {
|
||||
mounted() {
|
||||
this.el.addEventListener("input", (e) => {
|
||||
const query = e.target.value.toLowerCase();
|
||||
const dropdownId = this.el.dataset.dropdownId;
|
||||
const optionsContainer = document.querySelector(`#${dropdownId}-options`);
|
||||
const options = optionsContainer.querySelectorAll("li button");
|
||||
|
||||
options.forEach((option) => {
|
||||
const text = option.textContent.toLowerCase();
|
||||
option.parentElement.style.display = text.includes(query)
|
||||
? "block"
|
||||
: "none";
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
const DropdownAnimation = {
|
||||
mounted() {
|
||||
this.el.addEventListener("transitionend", (e) => {
|
||||
if (
|
||||
e.propertyName === "opacity" &&
|
||||
this.el.classList.contains("fade-out")
|
||||
) {
|
||||
this.el.classList.add("hidden");
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export { SearchableDropdown, DropdownAnimation };
|
||||
Loading…
Add table
Add a link
Reference in a new issue