mirror of
https://github.com/GuySandler/CanvasRefined.git
synced 2026-09-20 20:24:52 +02:00
todolist preview
This commit is contained in:
parent
f26cac0e9c
commit
765ecbf2fa
@ -54,7 +54,7 @@
|
||||
"message": "Max. Anzahl Elemente: "
|
||||
},
|
||||
"hover_preview": {
|
||||
"message": "Bewegen Sie den Mauszeiger, um Details in der Vorschau anzuzeigen"
|
||||
"message": "Vorschau beim Überfahren"
|
||||
},
|
||||
"hide_completed": {
|
||||
"message": "Abgeschlossene ausblenden"
|
||||
|
||||
@ -84,7 +84,7 @@
|
||||
"message": "Max Items: "
|
||||
},
|
||||
"hover_preview": {
|
||||
"message": "Hover To Preview Details"
|
||||
"message": "Previews on hover"
|
||||
},
|
||||
"hide_completed": {
|
||||
"message": "Hide Completed"
|
||||
|
||||
@ -54,7 +54,7 @@
|
||||
"message": "Artículos máximos: "
|
||||
},
|
||||
"hover_preview": {
|
||||
"message": "Pasa el cursor para obtener una vista previa de los detalles"
|
||||
"message": "Vista previa al pasar el cursor"
|
||||
},
|
||||
"hide_completed": {
|
||||
"message": "Ocultar completado"
|
||||
|
||||
@ -54,7 +54,7 @@
|
||||
"message": "Nombre maximal d'articles : "
|
||||
},
|
||||
"hover_preview": {
|
||||
"message": "Survolez pour prévisualiser les détails"
|
||||
"message": "Aperçu au survol"
|
||||
},
|
||||
"hide_completed": {
|
||||
"message": "Masquer terminé"
|
||||
|
||||
@ -54,7 +54,7 @@
|
||||
"message": "Numero massimo di articoli: "
|
||||
},
|
||||
"hover_preview": {
|
||||
"message": "Passa il mouse per visualizzare l'anteprima dei dettagli"
|
||||
"message": "Anteprima al passaggio del mouse"
|
||||
},
|
||||
"hide_completed": {
|
||||
"message": "Nascondi Completato"
|
||||
|
||||
@ -54,7 +54,7 @@
|
||||
"message": "最大アイテム数: "
|
||||
},
|
||||
"hover_preview": {
|
||||
"message": "詳細をプレビューするにはマウスを移動します"
|
||||
"message": "ホバーでプレビュー"
|
||||
},
|
||||
"hide_completed": {
|
||||
"message": "完了を非表示"
|
||||
|
||||
@ -54,7 +54,7 @@
|
||||
"message": "Itens máximos: "
|
||||
},
|
||||
"hover_preview": {
|
||||
"message": "Passe o mouse para visualizar os detalhes"
|
||||
"message": "Pré-visualização ao passar o cursor"
|
||||
},
|
||||
"hide_completed": {
|
||||
"message": "Ocultar Concluído"
|
||||
|
||||
@ -54,7 +54,7 @@
|
||||
"message": "Максимальное количество предметов: "
|
||||
},
|
||||
"hover_preview": {
|
||||
"message": "Наведите курсор, чтобы просмотреть детали"
|
||||
"message": "Предпросмотр при наведении"
|
||||
},
|
||||
"hide_completed": {
|
||||
"message": "Скрыть Завершено"
|
||||
|
||||
@ -54,7 +54,7 @@
|
||||
"message": "Max artiklar: "
|
||||
},
|
||||
"hover_preview": {
|
||||
"message": "Håll muspekaren för att förhandsgranska detaljer"
|
||||
"message": "Förhandsgranskning vid hover"
|
||||
},
|
||||
"hide_completed": {
|
||||
"message": "Göm slutförd"
|
||||
|
||||
@ -54,7 +54,7 @@
|
||||
"message": "最大物品数: "
|
||||
},
|
||||
"hover_preview": {
|
||||
"message": "将鼠标悬停以预览详细信息"
|
||||
"message": "悬停预览"
|
||||
},
|
||||
"hide_completed": {
|
||||
"message": "隐藏已完成"
|
||||
|
||||
@ -54,7 +54,7 @@
|
||||
"message": "最大物品数: "
|
||||
},
|
||||
"hover_preview": {
|
||||
"message": "将鼠标悬停以预览详细信息"
|
||||
"message": "懸停預覽"
|
||||
},
|
||||
"hide_completed": {
|
||||
"message": "隐藏已完成"
|
||||
|
||||
@ -125,10 +125,10 @@
|
||||
#canvasrefined-announcement-list {margin-bottom:1.0625rem;}
|
||||
.canvasrefined-todo-item-loader {margin-top: 18px;}
|
||||
.canvasrefined-todo-item-loader .canvasrefined-skeleton-text {height:12px;margin-top: 8px;margin-bottom:0;margin-left:25px}
|
||||
.canvasrefined-hover-preview {position: absolute;top: -6px;left: -400px;display: none; background: #fff; color: #000; max-height: 300%; width: 350px; padding:12px;border-radius:5px; overflow: hidden;box-shadow: 0 2px 5px rgba(0,0,0,.3)}
|
||||
.canvasrefined-preview-text {text-overflow: ellipsis;}
|
||||
.canvasrefined-preview-title {font-weight:bold;font-size:14px;}
|
||||
.canvasrefined-hover-preview::after {content:'';position:absolute;width:100%;height: 18px;bottom:0;left:0;background:linear-gradient(0deg, #fff 50%, transparent)}
|
||||
.canvasrefined-hover-preview {position: absolute;top: -6px;left: -400px;display: none; background: var(--bcbackground-0, #fff); color: var(--bctext-0, #000); max-height: 300%; width: 350px; padding:12px;border-radius:5px; overflow: hidden;box-shadow: 0 2px 5px rgba(0,0,0,.3)}
|
||||
.canvasrefined-preview-text {text-overflow: ellipsis;color: var(--bctext-0, #000);}
|
||||
.canvasrefined-preview-title {font-weight:bold;font-size:14px;color: var(--bctext-0, #000);}
|
||||
.canvasrefined-hover-preview::after {content:'';position:absolute;width:100%;height: 18px;bottom:0;left:0;background:linear-gradient(0deg, var(--bcbackground-0, #fff) 50%, transparent)}
|
||||
.canvasrefined-link-image {height: 21px; width: 21px;padding: 0px}
|
||||
.canvasrefined-link-image:hover {filter: brightness(66%)}
|
||||
.canvasrefined-gpa-header {font-size:16px;font-weight: bold;padding: 0;margin: 0;}
|
||||
|
||||
@ -215,11 +215,6 @@
|
||||
<span class="option-name" data-i18n="better_todo">Better Todo List</span>
|
||||
</div>
|
||||
<div class="sub-options">
|
||||
<div class="sub-option" style="display:none;">
|
||||
<input type="checkbox" id="hover_preview" name="hover_preview">
|
||||
<label for="hover_preview" class="sub-text" data-i18n="hover_preview">Hover to preview
|
||||
details</label> <!-- TODO: Later -->
|
||||
</div>
|
||||
<div class="sub-option">
|
||||
<input type="checkbox" id="todo_separate_scrollbar" name="todo_separate_scrollbar">
|
||||
<label for="todo_separate_scrollbar" class="sub-text" data-i18n="todo_separate_scrollbar">Separate Scrollbar</label>
|
||||
@ -232,6 +227,10 @@
|
||||
<input type="checkbox" id="todo_full_height" name="todo_full_height">
|
||||
<label for="todo_full_height" class="sub-text" data-i18n="todo_full_height">Force Full Height</label> <!-- TO REMOVE and replace with full height -->
|
||||
</div>
|
||||
<div class="sub-option">
|
||||
<input type="checkbox" id="hover_preview" name="hover_preview">
|
||||
<label for="hover_preview" class="sub-text" data-i18n="hover_preview">Previews on hover</label>
|
||||
</div>
|
||||
<div class="sub-option">
|
||||
<input type="checkbox" id="todo_hide_feedback" name="todo_hide_feedback">
|
||||
<label for="todo_hide_feedback" class="sub-text" data-i18n="todo_hide_feedback">Hide Recent Feedback</label>
|
||||
|
||||
129
js/content.js
129
js/content.js
@ -2746,6 +2746,128 @@ function applyTodoAlternateColors() {
|
||||
todoAltStyleEl.textContent = `:root{--cr-todo-icon:${color};}`;
|
||||
}
|
||||
|
||||
// --- Hover preview (Better Todo List: "Previews on hover") ---
|
||||
// The todo list is rendered by createTodoSections -> populateAssignments /
|
||||
// populateAnnouncements (the old loadBetterTodo renderer is no longer called).
|
||||
// A single shared, body-level tooltip is reused across items so it is never
|
||||
// clipped by the sidebar's scroll/overflow containers. It reuses the
|
||||
// .canvasrefined-hover-preview class so existing light/dark styling applies.
|
||||
let todoPreviewDelay = null;
|
||||
let todoPreviewToken = 0;
|
||||
const todoPreviewCache = new Map();
|
||||
|
||||
function stripHtmlPreview(html) {
|
||||
if (!html) return "";
|
||||
return String(html).replace(/<\/?[^>]+(>|$)/g, " ").replace(/ /g, " ").replace(/\s+/g, " ").trim();
|
||||
}
|
||||
|
||||
function getTodoPreviewEl() {
|
||||
let el = document.getElementById("canvasrefined-todo-preview");
|
||||
if (el) return el;
|
||||
el = document.createElement("div");
|
||||
el.id = "canvasrefined-todo-preview";
|
||||
el.className = "canvasrefined-hover-preview";
|
||||
el.innerHTML = '<p class="canvasrefined-preview-title"></p><p class="canvasrefined-preview-text"></p>';
|
||||
el.style.position = "fixed";
|
||||
el.style.zIndex = "100001";
|
||||
el.style.width = "300px";
|
||||
el.style.maxWidth = "90vw";
|
||||
el.style.maxHeight = "260px";
|
||||
document.body.append(el);
|
||||
return el;
|
||||
}
|
||||
|
||||
function positionTodoPreview(el, anchor) {
|
||||
const r = anchor.getBoundingClientRect();
|
||||
const gap = 10;
|
||||
const pw = el.offsetWidth || 300;
|
||||
const ph = el.offsetHeight || 160;
|
||||
let left = r.left - pw - gap;
|
||||
let top = r.top;
|
||||
if (left < gap) {
|
||||
left = r.right + gap;
|
||||
if (left + pw > window.innerWidth - gap) left = Math.max(gap, window.innerWidth - pw - gap);
|
||||
}
|
||||
if (top + ph > window.innerHeight - gap) top = Math.max(gap, window.innerHeight - ph - gap);
|
||||
el.style.left = left + "px";
|
||||
el.style.top = top + "px";
|
||||
}
|
||||
|
||||
async function getTodoPreviewText(item) {
|
||||
const type = item.plannable_type;
|
||||
const id = item.plannable_id;
|
||||
const key = type + ":" + id;
|
||||
if (todoPreviewCache.has(key)) return todoPreviewCache.get(key);
|
||||
// Custom task (planner note): the description is already on the planner
|
||||
// item as item.plannable.details, so no API call is needed.
|
||||
if (type === "planner_note" || (item.planner_override && item.planner_override.custom === true)) {
|
||||
const raw = item.plannable && item.plannable.details ? item.plannable.details : "";
|
||||
const text = stripHtmlPreview(raw) || "No details given";
|
||||
todoPreviewCache.set(key, text);
|
||||
return text;
|
||||
}
|
||||
let url = null;
|
||||
let field = "description";
|
||||
if (type === "assignment") {
|
||||
url = `${domain}/api/v1/courses/${item.course_id}/assignments/${id}`;
|
||||
} else if (type === "quiz") {
|
||||
url = `${domain}/api/v1/courses/${item.course_id}/quizzes/${id}`;
|
||||
} else if (type === "discussion_topic" || type === "announcement") {
|
||||
url = `${domain}/api/v1/courses/${item.course_id}/discussion_topics/${id}`;
|
||||
field = "message";
|
||||
}
|
||||
if (!url) {
|
||||
const text = "No preview available";
|
||||
todoPreviewCache.set(key, text);
|
||||
return text;
|
||||
}
|
||||
try {
|
||||
const data = await getData(url);
|
||||
const raw = data && data[field] ? data[field] : "";
|
||||
const text = stripHtmlPreview(raw) || "No details given";
|
||||
todoPreviewCache.set(key, text);
|
||||
return text;
|
||||
} catch (e) {
|
||||
return "Couldn't load preview";
|
||||
}
|
||||
}
|
||||
|
||||
function hideTodoPreview() {
|
||||
todoPreviewToken++; // cancel any pending async text update
|
||||
const el = document.getElementById("canvasrefined-todo-preview");
|
||||
if (el) el.style.display = "none";
|
||||
}
|
||||
|
||||
async function showTodoPreview(anchor, item) {
|
||||
const token = ++todoPreviewToken;
|
||||
const el = getTodoPreviewEl();
|
||||
const title = el.querySelector(".canvasrefined-preview-title");
|
||||
const text = el.querySelector(".canvasrefined-preview-text");
|
||||
title.textContent = item.plannable && item.plannable.title ? item.plannable.title : "";
|
||||
text.textContent = "Loading…";
|
||||
el.style.display = "block";
|
||||
positionTodoPreview(el, anchor);
|
||||
const content = await getTodoPreviewText(item);
|
||||
if (token !== todoPreviewToken) return; // a newer hover (or hide) superseded this one
|
||||
if (el.style.display !== "block") return; // user already moved away
|
||||
text.textContent = content;
|
||||
positionTodoPreview(el, anchor); // reposition now that the height is known
|
||||
}
|
||||
|
||||
function attachTodoHoverPreview(anchor, item) {
|
||||
if (options.hover_preview !== true) return;
|
||||
anchor.addEventListener("mouseenter", () => {
|
||||
clearTimeout(todoPreviewDelay);
|
||||
todoPreviewDelay = setTimeout(() => {
|
||||
if (anchor.matches(":hover")) showTodoPreview(anchor, item);
|
||||
}, 250);
|
||||
});
|
||||
anchor.addEventListener("mouseleave", () => {
|
||||
clearTimeout(todoPreviewDelay);
|
||||
hideTodoPreview();
|
||||
});
|
||||
}
|
||||
|
||||
function populateAssignments(iscompleted = false) {
|
||||
const today = new Date();
|
||||
today.setHours(0,0,0,0);
|
||||
@ -2870,6 +2992,7 @@ function populateAssignments(iscompleted = false) {
|
||||
openTaskForEdit(item);
|
||||
});
|
||||
}
|
||||
attachTodoHoverPreview(assignment, item);
|
||||
});
|
||||
|
||||
if (document.getElementById("better-todo-see-more")) {
|
||||
@ -2949,6 +3072,7 @@ function populateAnnouncements() {
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
attachTodoHoverPreview(announcement, item);
|
||||
});
|
||||
}
|
||||
|
||||
@ -3634,9 +3758,10 @@ async function loadBetterTodo() {
|
||||
delay = setTimeout(async () => {
|
||||
if (listItem.classList.contains("canvasrefined-todo-hover")) {
|
||||
previewTitle.textContent = item.plannable.title;
|
||||
// custom assignment
|
||||
// custom assignment (planner note): preview its description/details
|
||||
if (customItem) {
|
||||
previewText.textContent = "Custom assignment";
|
||||
const details = item.plannable && item.plannable.details ? item.plannable.details : "";
|
||||
previewText.textContent = details === "" ? "No details given" : details.replace(/<\/?[^>]+(>|$)/g, " ");
|
||||
} else {
|
||||
console.log(item);
|
||||
let found = false;
|
||||
|
||||
@ -49,7 +49,7 @@ const exportCardColorToggles = ["gradient_cards", "disable_color_overlay"];
|
||||
const exportCardStyles = ["customCardStyles", "imageSize", "cardRoundness", "cardSpacing", "cardWidth", "cardHeight"];
|
||||
const exportLayout = ["full_width", "center_cards", "condensed_cards", "equal_height_cards", "remlogo", "hide_new_canvas", "hide_feedback", "tab_icons"];
|
||||
const exportSidebar = ["better_sidebar", "sidebar_scale"];
|
||||
const exportTodo = ["better_todo", "todo_hide_feedback", "todo_full_height", "todo_confetti", "todo_progress_rings", "todo_hr24", "todo_separate_scrollbar", "todo_alternate_colors"];
|
||||
const exportTodo = ["better_todo", "todo_hide_feedback", "todo_full_height", "todo_confetti", "todo_progress_rings", "todo_hr24", "todo_separate_scrollbar", "todo_alternate_colors", "hover_preview"];
|
||||
const exportGpa = ["gpa_calc", "gpa_calc_prepend", "gpa_calc_cumulative", "gpa_calc_weighted"];
|
||||
const exportBackground = ["customBackgroundLink", "customBackgroundScale", "customBackgroundDaily", "customBackgroundNasaDaily", "fitImageToScreen", "bg_opacity", "sidebar_opacity", "bg_blur", "sidebar_blur"];
|
||||
// Master "On/off toggles" = every visual toggle (no GPA, no dark-mode schedule,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user