style: redesign subtask and notes inline UI

- Subtasks: slide-in animation, left border accent, hover highlights,
  mini progress bar in indicator badge, scale effect on checkbox hover
- Notes: slide-in animation, cleaner toolbar with SVG icons instead of
  emoji, proper dark mode support, focus ring on editor, max-height cap
- Both use CSS variables for consistent theming

v1.40.1

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
mARTin 2026-03-17 10:47:55 +01:00
parent 85982f06a5
commit 2d386362ca
3 changed files with 128 additions and 99 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "my-weekly-todo-list", "name": "my-weekly-todo-list",
"version": "1.40.0", "version": "1.40.1",
"description": "A web-based weekly task management application that organizes to-dos and calendar events in a single, intuitive weekly view", "description": "A web-based weekly task management application that organizes to-dos and calendar events in a single, intuitive weekly view",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {

View File

@ -772,24 +772,34 @@ h3 {
/* Inline Notes */ /* Inline Notes */
.weekly-notes-inline { .weekly-notes-inline {
width: 100%; width: 100%;
margin-top: 0.5rem; margin-top: 6px;
padding-left: 0; padding: 0;
padding-right: 0.5rem; animation: notesSlideIn 0.2s ease-out;
}
@keyframes notesSlideIn {
from { opacity: 0; transform: translateY(-4px); }
to { opacity: 1; transform: translateY(0); }
} }
.weekly-notes-editor-inline { .weekly-notes-editor-inline {
width: 100%; width: 100%;
min-height: 80px; min-height: 60px;
padding: 0.5rem; max-height: 200px;
border: 1px solid #ddd; /* Light border for editor */ padding: 8px 10px;
border-radius: 4px; border: 1px solid var(--weekly-border, #e5e7eb);
border-radius: 6px;
font-family: inherit; font-family: inherit;
font-size: 0.875rem; font-size: 0.8rem;
line-height: 1.5; line-height: 1.5;
color: var(--weekly-text); color: var(--weekly-text);
background: #fdfdfd; background: var(--weekly-bg, #fff);
resize: vertical; resize: vertical;
outline: none; outline: none;
transition: border-color 0.15s, box-shadow 0.15s;
}
.dark-mode .weekly-notes-editor-inline {
background: rgba(255, 255, 255, 0.04);
border-color: rgba(255, 255, 255, 0.1);
} }
/* Floating Notes Popup */ /* Floating Notes Popup */
@ -868,7 +878,10 @@ h3 {
.weekly-notes-editor-inline:focus { .weekly-notes-editor-inline:focus {
border-color: var(--weekly-teal); border-color: var(--weekly-teal);
background: #fff; box-shadow: 0 0 0 2px rgba(0, 154, 154, 0.1);
}
.dark-mode .weekly-notes-editor-inline:focus {
box-shadow: 0 0 0 2px rgba(0, 154, 154, 0.15);
} }
/* Rolling Indicator */ /* Rolling Indicator */
@ -931,12 +944,14 @@ h3 {
.notes-toolbar { .notes-toolbar {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 0.5rem; gap: 2px;
margin-bottom: 0.5rem; margin-bottom: 4px;
padding: 0.25rem 0.5rem; padding: 3px 4px;
background: #f8f8f8; background: var(--weekly-settings-toggle-bg, #f3f4f6);
border-radius: 4px; border-radius: 6px;
border: 1px solid #eee; }
.dark-mode .notes-toolbar {
background: rgba(255, 255, 255, 0.06);
} }
.notes-toolbar-btn { .notes-toolbar-btn {
@ -944,19 +959,23 @@ h3 {
border: none; border: none;
cursor: pointer; cursor: pointer;
color: var(--weekly-text-light); color: var(--weekly-text-light);
font-size: 0.85rem; font-size: 0.75rem;
padding: 2px 4px; padding: 3px 7px;
border-radius: 3px; border-radius: 4px;
min-width: 20px; min-width: 24px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
font-weight: 600; font-weight: 600;
transition: background 0.1s, color 0.1s;
} }
.notes-toolbar-btn:hover { .notes-toolbar-btn:hover {
background: rgba(0,0,0,0.05); background: var(--weekly-bg, #fff);
color: var(--weekly-text); color: var(--weekly-text);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
}
.dark-mode .notes-toolbar-btn:hover {
background: rgba(255, 255, 255, 0.1);
} }
/* Action Buttons Styling - Float above task on hover */ /* Action Buttons Styling - Float above task on hover */
@ -1051,21 +1070,37 @@ h3 {
} }
/* Sub-tasks */ /* Sub-tasks */
/* Subtask list — animated expand/collapse */
.subtask-list { .subtask-list {
list-style: none; list-style: none;
padding: 0.125rem 0 0.125rem 1.25rem; padding: 4px 0 4px 12px;
margin: 0; margin: 2px 0 0 0;
border-left: 2px solid var(--weekly-border, #e5e7eb);
margin-left: 6px;
animation: subtaskSlideIn 0.2s ease-out;
}
@keyframes subtaskSlideIn {
from { opacity: 0; transform: translateY(-4px); }
to { opacity: 1; transform: translateY(0); }
} }
.subtask-item { .subtask-item {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 0.375rem; gap: 6px;
padding: 0.125rem 0; padding: 3px 6px 3px 4px;
font-size: 0.8125rem; font-size: 0.8rem;
color: var(--weekly-text); color: var(--weekly-text);
position: relative; position: relative;
cursor: grab; cursor: grab;
border-radius: 4px;
transition: background 0.1s;
}
.subtask-item:hover {
background: rgba(0, 0, 0, 0.03);
}
.dark-mode .subtask-item:hover {
background: rgba(255, 255, 255, 0.04);
} }
.subtask-item:active { .subtask-item:active {
cursor: grabbing; cursor: grabbing;
@ -1073,7 +1108,7 @@ h3 {
.subtask-item.completed .subtask-title { .subtask-item.completed .subtask-title {
text-decoration: line-through; text-decoration: line-through;
opacity: 0.5; opacity: 0.4;
} }
.subtask-checkbox { .subtask-checkbox {
@ -1085,12 +1120,12 @@ h3 {
display: flex; display: flex;
align-items: center; align-items: center;
flex-shrink: 0; flex-shrink: 0;
transition: color 0.15s, transform 0.15s;
} }
.subtask-checkbox:hover { .subtask-checkbox:hover {
color: var(--weekly-teal); color: var(--weekly-teal);
transform: scale(1.15);
} }
.subtask-item.completed .subtask-checkbox { .subtask-item.completed .subtask-checkbox {
color: var(--weekly-teal); color: var(--weekly-teal);
} }
@ -1102,8 +1137,8 @@ h3 {
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
transition: color 0.1s;
} }
.subtask-title:hover { .subtask-title:hover {
color: var(--weekly-teal); color: var(--weekly-teal);
} }
@ -1113,7 +1148,7 @@ h3 {
border: none; border: none;
background: transparent; background: transparent;
outline: none; outline: none;
font-size: 0.8125rem; font-size: 0.8rem;
font-family: inherit; font-family: inherit;
color: var(--weekly-text); color: var(--weekly-text);
padding: 0; padding: 0;
@ -1122,25 +1157,28 @@ h3 {
.subtask-delete-btn { .subtask-delete-btn {
background: none; background: none;
border: none; border: none;
padding: 0.125rem; padding: 2px;
cursor: pointer; cursor: pointer;
color: var(--weekly-text-muted, #999); color: var(--weekly-text-muted, #999);
opacity: 0; opacity: 0;
transition: opacity 0.15s; transition: opacity 0.15s, color 0.15s;
display: flex; display: flex;
align-items: center; align-items: center;
border-radius: 3px;
} }
.subtask-item:hover .subtask-delete-btn { .subtask-item:hover .subtask-delete-btn {
opacity: 1; opacity: 0.6;
} }
.subtask-delete-btn:hover { .subtask-delete-btn:hover {
color: #e53e3e; color: #ef4444;
opacity: 1 !important;
background: rgba(239, 68, 68, 0.08);
} }
.subtask-add-row { .subtask-add-row {
padding: 0.125rem 0 0.25rem 1.25rem; padding: 2px 0 4px 12px;
margin-left: 6px;
border-left: 2px solid var(--weekly-border, #e5e7eb);
} }
.subtask-add-input { .subtask-add-input {
@ -1148,15 +1186,15 @@ h3 {
border: none; border: none;
background: transparent; background: transparent;
outline: none; outline: none;
font-size: 0.8125rem; font-size: 0.8rem;
font-family: inherit; font-family: inherit;
color: var(--weekly-text); color: var(--weekly-text);
padding: 0.125rem 0; padding: 2px 0;
} }
.subtask-add-input::placeholder { .subtask-add-input::placeholder {
color: var(--weekly-text-muted, #999); color: var(--weekly-text-muted, #999);
font-style: italic; font-style: italic;
opacity: 0.7;
} }
/* Task Input */ /* Task Input */

View File

@ -8943,6 +8943,7 @@ function TaskItem({
const total = task.subTasks!.length; const total = task.subTasks!.length;
const allDone = completed === total; const allDone = completed === total;
const expanded = isSubTasksOpen || isSubTaskInputOpen; const expanded = isSubTasksOpen || isSubTaskInputOpen;
const pct = total > 0 ? (completed / total) * 100 : 0;
return ( return (
<button <button
onClick={(e) => { onClick={(e) => {
@ -8954,25 +8955,46 @@ function TaskItem({
style={{ style={{
display: "inline-flex", display: "inline-flex",
alignItems: "center", alignItems: "center",
gap: "3px", gap: "4px",
padding: "2px 7px", padding: "2px 8px 2px 6px",
borderRadius: "10px", borderRadius: "12px",
background: expanded ? "rgba(99, 102, 241, 0.15)" : allDone ? "rgba(34, 197, 94, 0.15)" : "rgba(0,0,0,0.08)", background: "transparent",
color: expanded ? "#6366f1" : allDone ? "#22c55e" : "#555", color: allDone ? "var(--weekly-teal, #0d9488)" : "var(--weekly-text-light, #9ca3af)",
border: expanded ? "1px solid rgba(99, 102, 241, 0.3)" : allDone ? "1px solid rgba(34, 197, 94, 0.3)" : "1px solid rgba(0,0,0,0.12)", border: "none",
cursor: "pointer", cursor: "pointer",
fontSize: "0.7rem", fontSize: "0.7rem",
fontWeight: 600, fontWeight: 600,
lineHeight: 1, lineHeight: 1,
flexShrink: 0, flexShrink: 0,
whiteSpace: "nowrap", whiteSpace: "nowrap",
marginTop: "2px", transition: "color 0.15s",
}} }}
> >
<svg viewBox="0 0 24 24" width="10" height="10" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round" style={{ transform: expanded ? "rotate(90deg)" : "rotate(0deg)", transition: "transform 0.2s" }}> <svg viewBox="0 0 24 24" width="10" height="10" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" style={{ transform: expanded ? "rotate(90deg)" : "rotate(0deg)", transition: "transform 0.2s" }}>
<polyline points="9 18 15 12 9 6" /> <polyline points="9 18 15 12 9 6" />
</svg> </svg>
{completed}/{total} {/* Mini progress bar */}
<span style={{
display: "inline-block",
width: "20px",
height: "3px",
borderRadius: "2px",
background: "var(--weekly-border, #e5e7eb)",
position: "relative",
overflow: "hidden",
}}>
<span style={{
position: "absolute",
left: 0,
top: 0,
height: "100%",
width: `${pct}%`,
borderRadius: "2px",
background: allDone ? "var(--weekly-teal, #0d9488)" : "#6366f1",
transition: "width 0.3s ease",
}} />
</span>
<span style={{ opacity: 0.8 }}>{completed}/{total}</span>
</button> </button>
); );
})()} })()}
@ -8989,14 +9011,15 @@ function TaskItem({
style={{ style={{
display: "inline-flex", display: "inline-flex",
alignItems: "center", alignItems: "center",
padding: "2px 4px", gap: "2px",
borderRadius: "3px", padding: "2px 5px",
background: isNotesOpen ? "rgba(245, 158, 11, 0.12)" : "rgba(0,0,0,0.05)", borderRadius: "4px",
color: isNotesOpen ? "#f59e0b" : "#888", background: isNotesOpen ? "rgba(245, 158, 11, 0.1)" : "transparent",
color: isNotesOpen ? "#f59e0b" : "var(--weekly-text-muted, #999)",
border: "none", border: "none",
cursor: "pointer", cursor: "pointer",
lineHeight: 1, lineHeight: 1,
marginTop: "2px", transition: "background 0.15s, color 0.15s",
}} }}
> >
<svg viewBox="0 0 24 24" width="11" height="11" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"> <svg viewBox="0 0 24 24" width="11" height="11" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
@ -9005,6 +9028,9 @@ function TaskItem({
<line x1="16" y1="13" x2="8" y2="13" /> <line x1="16" y1="13" x2="8" y2="13" />
<line x1="16" y1="17" x2="8" y2="17" /> <line x1="16" y1="17" x2="8" y2="17" />
</svg> </svg>
<svg viewBox="0 0 24 24" width="8" height="8" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" style={{ transform: isNotesOpen ? "rotate(90deg)" : "rotate(0deg)", transition: "transform 0.2s" }}>
<polyline points="9 18 15 12 9 6" />
</svg>
</button> </button>
)} )}
@ -9253,50 +9279,15 @@ function TaskItem({
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
> >
<div className="notes-toolbar"> <div className="notes-toolbar">
<button <button className="notes-toolbar-btn" onClick={() => insertMarkdown("**", "**")} title="Bold" style={{ fontWeight: 700 }}>B</button>
className="notes-toolbar-btn" <button className="notes-toolbar-btn" onClick={() => insertMarkdown("*", "*")} title="Italic" style={{ fontStyle: "italic" }}>I</button>
onClick={() => insertMarkdown("**", "**")} <button className="notes-toolbar-btn" onClick={() => insertMarkdown("[", "](url)")} title="Link">
title="Bold" <svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>
>
B
</button> </button>
<button <button className="notes-toolbar-btn" onClick={() => insertMarkdown("- ")} title="List">
className="notes-toolbar-btn" <svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/></svg>
onClick={() => insertMarkdown("*", "*")}
title="Italic"
>
i
</button> </button>
<button <span style={{ marginLeft: "auto", fontSize: "0.65rem", color: "var(--weekly-text-muted, #999)", opacity: 0.6 }}>md</span>
className="notes-toolbar-btn"
onClick={() => insertMarkdown("[", "](url)")}
title="Link"
>
🔗
</button>
<button
className="notes-toolbar-btn"
onClick={() => insertMarkdown("- ")}
title="List"
>
</button>
<button
className="notes-toolbar-btn"
onClick={() => insertMarkdown("![alt text](", ")")}
title="Image"
>
🖼
</button>
<span
style={{
marginLeft: "auto",
fontSize: "0.75rem",
color: "#999",
}}
>
Markdown supported
</span>
</div> </div>
<textarea <textarea
ref={notesRef} ref={notesRef}