Replace jQuery toggle animations with CSS-based animations
- Use CSS keyframes (slideDown/slideUp) for 800ms door-like animations - JS only toggles classes and updates button text at correct timing - Preview toggle uses native <details> with animated open/close - Arrow indicators on right side (hide preview ▲ / show preview ▼) - No-JS fallback preserved (links navigate to dedicated pages)
This commit is contained in:
parent
9c74e6256a
commit
ef8f382976
7 changed files with 157 additions and 156 deletions
|
|
@ -411,120 +411,112 @@ form.node-action {
|
|||
opacity: 0.8 !important;
|
||||
}
|
||||
|
||||
/* Pure CSS toggle using <details> element with animations */
|
||||
.toggle-summary {
|
||||
.remark-box-div-main {
|
||||
margin-bottom: 35px;
|
||||
}
|
||||
|
||||
.remark-box-div {
|
||||
display: none;
|
||||
overflow: hidden;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.edit-box-div {
|
||||
display: none;
|
||||
overflow: hidden;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
/* CSS animation when toggled open via JS */
|
||||
.remark-box-div.toggle-open,
|
||||
.edit-box-div.toggle-open {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
max-height: 1000px;
|
||||
animation: slideDown 0.8s ease-out forwards;
|
||||
}
|
||||
|
||||
.remark-box-div.toggle-closing,
|
||||
.edit-box-div.toggle-closing {
|
||||
display: block;
|
||||
max-height: 1000px;
|
||||
animation: slideUp 0.8s ease-out forwards;
|
||||
}
|
||||
|
||||
@keyframes slideDown {
|
||||
from {
|
||||
max-height: 0;
|
||||
}
|
||||
to {
|
||||
max-height: 1000px;
|
||||
}
|
||||
}
|
||||
|
||||
.my-namespaces-div {
|
||||
display: none;
|
||||
position: absolute;
|
||||
background-color: #ffffff;
|
||||
z-index: 1;
|
||||
overflow: hidden;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.my-namespaces-div.toggle-open {
|
||||
display: block;
|
||||
animation: slideDown 0.8s ease-out;
|
||||
}
|
||||
|
||||
/* Preview toggle with CSS animation */
|
||||
.preview-details {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.preview-details[open] > .preview {
|
||||
overflow: hidden;
|
||||
animation: slideDown 0.8s ease-out forwards;
|
||||
}
|
||||
|
||||
.preview-details.closing > .preview {
|
||||
overflow: hidden;
|
||||
max-height: 1000px;
|
||||
animation: slideUp 0.8s ease-out forwards;
|
||||
}
|
||||
|
||||
@keyframes slideUp {
|
||||
from {
|
||||
max-height: 1000px;
|
||||
}
|
||||
to {
|
||||
max-height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.preview-toggle {
|
||||
cursor: pointer;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.toggle-summary::-webkit-details-marker {
|
||||
.preview-toggle::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.toggle-summary::marker {
|
||||
display: none;
|
||||
content: "";
|
||||
}
|
||||
|
||||
/* Toggle text switching: show/hide different text based on open state */
|
||||
.toggle-summary .when-open {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.toggle-summary .when-closed {
|
||||
.preview-toggle .when-open {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
details[open] > .toggle-summary .when-open {
|
||||
.preview-toggle .when-closed {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.preview-details:not([open]) .when-open {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.preview-details:not([open]) .when-closed {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
details[open] > .toggle-summary .when-closed {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Hidden summary for main remark box (always open) */
|
||||
.toggle-summary-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Fallback links hidden when JS not needed */
|
||||
.toggle-fallback {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Animated details content using CSS grid technique */
|
||||
.edit-box-details,
|
||||
.remark-box-details,
|
||||
.node-children-details {
|
||||
--details-transition-duration: 0.3s;
|
||||
}
|
||||
|
||||
.edit-box-details > .details-content,
|
||||
.remark-box-details > .details-content,
|
||||
.node-children-details > .details-content {
|
||||
display: grid;
|
||||
grid-template-rows: 0fr;
|
||||
transition: grid-template-rows var(--details-transition-duration) ease-out;
|
||||
}
|
||||
|
||||
.edit-box-details[open] > .details-content,
|
||||
.remark-box-details[open] > .details-content,
|
||||
.node-children-details[open] > .details-content {
|
||||
grid-template-rows: 1fr;
|
||||
}
|
||||
|
||||
.edit-box-details > .details-content > .details-content-inner,
|
||||
.remark-box-details > .details-content > .details-content-inner,
|
||||
.node-children-details > .details-content > .details-content-inner {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Edit box details styling */
|
||||
.edit-box-details {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
/* Remark box details styling */
|
||||
.remark-box-details {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.remark-box-details-main {
|
||||
margin-bottom: 35px;
|
||||
}
|
||||
|
||||
/* Namespace switcher dropdown */
|
||||
.my-namespaces-details {
|
||||
display: inline;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.my-namespaces-content {
|
||||
position: absolute;
|
||||
background-color: #ffffff;
|
||||
z-index: 1;
|
||||
padding: 10px;
|
||||
border: 1px solid #ddd;
|
||||
min-width: 120px;
|
||||
/* Fade in animation for dropdown */
|
||||
animation: fadeIn 0.2s ease-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(-5px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
/* Node children collapse/expand */
|
||||
.node-children-details {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.node-children-details > .toggle-summary {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
#remarkbox-footer {
|
||||
font-size: 0.8em;
|
||||
font-weight: bold;
|
||||
|
|
|
|||
|
|
@ -37,19 +37,39 @@ function sendPreview(textarea, div, mathjax=false){
|
|||
}
|
||||
}
|
||||
|
||||
// CSS-based toggle for smoother animations.
|
||||
function toggle(target, button, off_text, on_text="hide"){
|
||||
var el = document.getElementById(target);
|
||||
var btn = document.getElementById(button);
|
||||
if (el.classList.contains('toggle-open')) {
|
||||
// Animate close, then update text
|
||||
el.classList.add('toggle-closing');
|
||||
setTimeout(function() {
|
||||
el.classList.remove('toggle-open');
|
||||
el.classList.remove('toggle-closing');
|
||||
btn.textContent = off_text;
|
||||
}, 800);
|
||||
} else {
|
||||
// Update text immediately when opening
|
||||
btn.textContent = on_text;
|
||||
el.classList.add('toggle-open');
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-focus textarea when details element opens (progressive enhancement).
|
||||
// Falls back gracefully if browser doesn't support the required APIs.
|
||||
// Animate <details> close for preview-details elements.
|
||||
if (typeof document.addEventListener === 'function') {
|
||||
document.addEventListener('toggle', function(e) {
|
||||
var details = e.target;
|
||||
if (details.tagName !== 'DETAILS' || !details.open) return;
|
||||
|
||||
// Find textarea inside the details element and focus it.
|
||||
var textarea = details.querySelector('textarea');
|
||||
if (textarea && typeof textarea.focus === 'function') {
|
||||
// Small delay to let the CSS transition start.
|
||||
setTimeout(function() { textarea.focus(); }, 50);
|
||||
document.addEventListener('click', function(e) {
|
||||
var summary = e.target.closest('.preview-toggle');
|
||||
if (!summary) return;
|
||||
var details = summary.parentElement;
|
||||
if (!details || !details.classList.contains('preview-details')) return;
|
||||
if (details.open && !details.classList.contains('closing')) {
|
||||
e.preventDefault();
|
||||
details.classList.add('closing');
|
||||
setTimeout(function() {
|
||||
details.open = false;
|
||||
details.classList.remove('closing');
|
||||
}, 800);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,9 +35,10 @@
|
|||
{{ snippets.namespace_home_uri(request.namespace) }}
|
||||
|
||||
{%- if request.mode != 'embed' and request.user.authenticated and request.user.namespaces %}
|
||||
<details id="my-namespaces-details" class="my-namespaces-details">
|
||||
<summary class="toggle-summary" id="my-namespaces-link">(switch)</summary>
|
||||
<div class="my-namespaces-content">
|
||||
<a href="{{ domain_app }}/u/namespaces" onclick="toggle('my-namespaces-div', 'my-namespaces-link', '(switch)', '(switch)'); return false;" id="my-namespaces-link">(switch)</a>
|
||||
|
||||
<div id="my-namespaces-div" class="my-namespaces-div">
|
||||
|
||||
{% for namespace in request.user.namespaces %}
|
||||
{% if namespace != request.namespace %}
|
||||
{{ snippets.namespace_home_uri(namespace) }}
|
||||
|
|
@ -50,7 +51,6 @@
|
|||
<a href="{{ domain_app }}/setup" class="button">setup</a>
|
||||
</center>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
{%- endif %}
|
||||
|
||||
|
|
|
|||
|
|
@ -46,19 +46,13 @@ page: {{ request.page_number }}
|
|||
{{ snippets.actions(node) }}
|
||||
</div>
|
||||
|
||||
<details id="edit-box-{{ node.id }}" class="edit-box-details">
|
||||
<summary class="action toggle-summary"><span class="when-closed">edit</span><span class="when-open">hide</span></summary>
|
||||
<div class="details-content"><div class="details-content-inner">
|
||||
<div id="edit-box-{{ node.id }}" class="edit-box-div">
|
||||
{{ forms.edit(node) }}
|
||||
</div></div>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
<details id="remark-box-{{ node.id }}" class="remark-box-details">
|
||||
<summary class="remark toggle-summary"><span class="when-closed">remark</span><span class="when-open">hide</span></summary>
|
||||
<div class="details-content"><div class="details-content-inner">
|
||||
<div id="remark-box-{{ node.id }}" class="remark-box-div">
|
||||
{{ forms.reply(node, node) }}
|
||||
</div></div>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
{% endfor -%}
|
||||
|
||||
|
|
|
|||
|
|
@ -75,19 +75,13 @@
|
|||
</div>
|
||||
{% endif %}
|
||||
|
||||
<details id="edit-box-{{ request.node.id }}" class="edit-box-details">
|
||||
<summary class="action toggle-summary"><span class="when-closed">edit</span><span class="when-open">hide</span></summary>
|
||||
<div class="details-content"><div class="details-content-inner">
|
||||
<div id="edit-box-{{ request.node.id }}" class="edit-box-div">
|
||||
{{ forms.edit(request.node) }}
|
||||
</div></div>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
<details id="remark-box-{{ request.node.id }}" class="remark-box-details remark-box-details-main" open>
|
||||
<summary class="toggle-summary toggle-summary-hidden"></summary>
|
||||
<div class="details-content"><div class="details-content-inner">
|
||||
<div id="remark-box-{{ request.node.id }}" class="remark-box-div-main not-hidden-remark-box-div">
|
||||
{{ forms.reply(request.node, request.root_node) }}
|
||||
</div></div>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
|
||||
{%- if request.node.id and request.node_graph[request.node.id] -%}
|
||||
|
|
@ -154,29 +148,20 @@
|
|||
<a href="{{ request.link_prefix }}/{{ parent.id }}#{{ parent.id }}" class="load-more shown-on-tablet">load more <span style="color: #292929; font-weight: normal;">({{children_ids | length}} remarks)</span></a>
|
||||
{% endif %}
|
||||
|
||||
<details id="edit-box-{{ parent.id }}" class="edit-box-details">
|
||||
<summary class="action toggle-summary"><span class="when-closed">edit</span><span class="when-open">hide</span></summary>
|
||||
<div class="details-content"><div class="details-content-inner">
|
||||
<div id="edit-box-{{ parent.id }}" class="edit-box-div">
|
||||
{{ forms.edit(parent) }}
|
||||
</div></div>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
<details id="remark-box-{{ parent.id }}" class="remark-box-details">
|
||||
<summary class="remark toggle-summary"><span class="when-closed">remark</span><span class="when-open">hide</span></summary>
|
||||
<div class="details-content"><div class="details-content-inner">
|
||||
<div id="remark-box-{{ parent.id }}" class="remark-box-div">
|
||||
{{ forms.reply(parent, request.root_node) }}
|
||||
</div></div>
|
||||
</details>
|
||||
|
||||
</div>
|
||||
|
||||
{#- nest children in this node's div for convo collapsing. -#}
|
||||
<details id="node-children-{{ parent.id }}" class="node-children-details" open>
|
||||
<summary class="action link toggle-summary"><span class="when-closed">expand [+]</span><span class="when-open">collapse [-]</span></summary>
|
||||
<div class="details-content"><div class="details-content-inner">
|
||||
<div id="node-children-{{ parent.id }}">
|
||||
{%- if children_ids %}
|
||||
{{ loop(children_ids) }}
|
||||
{% endif -%}
|
||||
</div></div>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
{# close the class="node" div #}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{% macro reply(node, root) %}
|
||||
|
||||
{% if root.locked %}
|
||||
<br>
|
||||
<p>This thread was locked to prevent additional comments.</p>
|
||||
|
|
@ -25,8 +26,8 @@
|
|||
{% set submit_button_value = 'save message' %}
|
||||
{% include 'submit.j2' %}
|
||||
|
||||
<details open>
|
||||
<summary class="action link" float="right">hide preview</summary>
|
||||
<details class="preview-details" open>
|
||||
<summary class="action link preview-toggle"><span class="when-open">hide preview ▲</span><span class="when-closed">show preview ▼</span></summary>
|
||||
<div id='preview-{{ node.id }}' name='preview' class='preview js-only'></div>
|
||||
</details>
|
||||
|
||||
|
|
@ -38,9 +39,11 @@
|
|||
</form>
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% endmacro %}
|
||||
|
||||
{% macro edit(node) %}
|
||||
|
||||
<form method="post" action="{{ request.link_prefix }}/{{ node.id }}/edit" onsubmit="submit.disabled = true; return true;">
|
||||
|
||||
{% if node.title %}
|
||||
|
|
@ -67,6 +70,7 @@
|
|||
{% set submit_button_value = 'save message' %}
|
||||
{% include 'submit.j2' %}
|
||||
</form>
|
||||
|
||||
{% endmacro %}
|
||||
|
||||
{% macro pay_what_you_can() %}
|
||||
|
|
|
|||
|
|
@ -84,7 +84,9 @@
|
|||
|
||||
{% macro button_remark(node, root_node) %}
|
||||
{% if root_node and not root_node.locked %}
|
||||
<a href="{{ request.link_prefix }}/{{ node.id }}" id="remark-link-{{ node.id }}" class="remark toggle-fallback">remark</a>
|
||||
<a href="{{ request.link_prefix }}/{{ node.id }}"
|
||||
onclick="toggle('remark-box-{{ node.id }}', 'remark-link-{{ node.id }}', 'remark', 'hide'); document.getElementById('textarea-{{ node.id }}').focus(); return false;"
|
||||
id="remark-link-{{ node.id }}" class="remark">remark</a>
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
|
|
@ -97,6 +99,9 @@
|
|||
{% endmacro %}
|
||||
|
||||
{% macro button_collapse(node) %}
|
||||
<button
|
||||
onclick="toggle('node-children-{{ node.id }}', 'collapse-link-{{ node.id }}', 'expand [+]', 'collapse [-]');"
|
||||
id="collapse-link-{{ node.id }}" class="action link js-only">collapse [-]</button>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro permalinks(node, parent_node=None, root_node=None) %}
|
||||
|
|
@ -206,7 +211,8 @@
|
|||
{{ enable_node(node=node) }}
|
||||
{% else %}
|
||||
<a href="{{ request.link_prefix }}/{{ node.id }}/edit"
|
||||
id="edit-link-{{ node.id }}" class="action toggle-fallback">edit</a>
|
||||
onclick="toggle('edit-box-{{ node.id }}', 'edit-link-{{ node.id }}', 'edit', 'hide'); document.getElementById('edit-textarea-{{ node.id }}').focus(); return false;"
|
||||
id="edit-link-{{ node.id }}" class="action">edit</a>
|
||||
|
||||
{{ disable_node(node=node) }}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue