al_folio_core 1.0.3 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -0
- data/_includes/calendar.liquid +3 -1
- data/_includes/head.liquid +11 -0
- data/_includes/header.liquid +5 -5
- data/_includes/related_posts.liquid +37 -30
- data/_includes/scripts.liquid +13 -0
- data/_layouts/default.liquid +2 -2
- data/_sass/_components.scss +23 -0
- data/_sass/_navbar.scss +30 -0
- data/_sass/_teachings.scss +18 -0
- data/_sass/_typography.scss +4 -3
- data/_sass/_utilities.scss +210 -25
- data/assets/js/calendar-setup.js +6 -0
- data/assets/js/common.js +51 -7
- data/assets/js/jupyter_new_tab.js +31 -14
- data/assets/js/masonry.js +17 -7
- data/assets/js/table-engine.js +328 -0
- data/lib/al_folio_core/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 566d64497199ae021c6b80241c4bf25791fb6447a7e22ee0232fb74e2cd915bb
|
|
4
|
+
data.tar.gz: 149241d6a27b9e51737e6b437c339c270f64abc379e0c2664976c200af94fb37
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2eba8f9aeebd03783e40e1833ceba8990bae51bb068065603c8f9bba6d24c8286c11f14d5366021a7295cbb3e8dd5a565bbc65a752868e8dbe758d1f34a284b1
|
|
7
|
+
data.tar.gz: 6dccbdc6e0ab582dcf00a5fe0dc64ecb8418bc97f9d6051dbb214ca6a1a28f217429ca8c5d51ec0cbf472007ba4c2da1041da61d212e10c10f70bedc2723a4b4
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.0.5 - 2026-02-18
|
|
4
|
+
|
|
5
|
+
- Restored right-aligned desktop navbar menu layout with explicit core-owned alignment classes.
|
|
6
|
+
- Matched inline code typography more closely to legacy sizing/weight while preserving code-block styling.
|
|
7
|
+
- Normalized related-post recommendation links to regular font weight.
|
|
8
|
+
- Fixed Tocbot sidebar visual clashes by removing competing custom rails and scoping active/hover indicators cleanly.
|
|
9
|
+
|
|
10
|
+
## 1.0.4 - 2026-02-17
|
|
11
|
+
|
|
12
|
+
- Fixed related-posts HTML structure to render valid list markup.
|
|
13
|
+
- Restored sidebar TOC behavior and styling via Tocbot runtime integration.
|
|
14
|
+
- Added Tailwind-first vanilla table engine for `pretty_table` pages when Bootstrap compatibility is disabled.
|
|
15
|
+
- Replaced remaining jQuery-dependent runtime scripts (masonry, jupyter link handling) with vanilla JS.
|
|
16
|
+
- Improved project hover lift, teaching calendar toggle UX, and schedule/table styling parity.
|
|
17
|
+
|
|
3
18
|
## 1.0.3 - 2026-02-17
|
|
4
19
|
|
|
5
20
|
- Restricted gem packaging to tracked runtime files to prevent accidental inclusion of local/untracked artifacts.
|
data/_includes/calendar.liquid
CHANGED
|
@@ -3,9 +3,11 @@
|
|
|
3
3
|
<h2 class="mb-3"><i class="fas fa-calendar-alt"></i> Upcoming Events</h2>
|
|
4
4
|
|
|
5
5
|
<button
|
|
6
|
-
class="btn btn-sm btn-outline-primary mb-3"
|
|
6
|
+
class="btn btn-sm btn-outline-primary mb-3 calendar-toggle-btn"
|
|
7
|
+
type="button"
|
|
7
8
|
onclick="toggleCalendar()"
|
|
8
9
|
id="calendar-toggle-btn"
|
|
10
|
+
aria-pressed="false"
|
|
9
11
|
>
|
|
10
12
|
Show Calendar
|
|
11
13
|
</button>
|
data/_includes/head.liquid
CHANGED
|
@@ -33,6 +33,17 @@
|
|
|
33
33
|
>
|
|
34
34
|
{% endif %}
|
|
35
35
|
|
|
36
|
+
{% if page.toc and page.toc.sidebar %}
|
|
37
|
+
<!-- Tocbot -->
|
|
38
|
+
<link
|
|
39
|
+
defer
|
|
40
|
+
rel="stylesheet"
|
|
41
|
+
href="{{ site.third_party_libraries.tocbot.url.css }}"
|
|
42
|
+
integrity="{{ site.third_party_libraries.tocbot.integrity.css }}"
|
|
43
|
+
crossorigin="anonymous"
|
|
44
|
+
>
|
|
45
|
+
{% endif %}
|
|
46
|
+
|
|
36
47
|
<!-- Icons -->
|
|
37
48
|
{% if site.plugins contains 'al_icons' %}
|
|
38
49
|
{% include plugins/al_icons_styles.liquid %}
|
data/_includes/header.liquid
CHANGED
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
{% endif %}
|
|
27
27
|
<!-- Navbar Toggle -->
|
|
28
28
|
<button
|
|
29
|
-
class="navbar-toggler collapsed
|
|
29
|
+
class="navbar-toggler collapsed navbar-toggler-main"
|
|
30
30
|
type="button"
|
|
31
31
|
data-nav-toggle="navbarNav"
|
|
32
32
|
aria-controls="navbarNav"
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
<span class="icon-bar bottom-bar"></span>
|
|
40
40
|
</button>
|
|
41
41
|
|
|
42
|
-
<div class="collapse navbar-collapse
|
|
43
|
-
<ul class="navbar-nav
|
|
42
|
+
<div class="collapse navbar-collapse navbar-collapse-main" id="navbarNav">
|
|
43
|
+
<ul class="navbar-nav navbar-menu-list flex-nowrap">
|
|
44
44
|
{% for page in site.pages %}
|
|
45
45
|
{% if page.permalink == '/' %} {% assign about_title = page.title %} {% endif %}
|
|
46
46
|
{% endfor %}
|
|
@@ -119,7 +119,7 @@
|
|
|
119
119
|
{% if site.search_enabled %}
|
|
120
120
|
<!-- Search -->
|
|
121
121
|
<li class="nav-item">
|
|
122
|
-
<button id="search-toggle" title="Search"
|
|
122
|
+
<button id="search-toggle" type="button" title="Search" aria-label="Open search">
|
|
123
123
|
<span class="nav-link">ctrl k <i class="fa-solid fa-magnifying-glass"></i></span>
|
|
124
124
|
</button>
|
|
125
125
|
</li>
|
|
@@ -127,7 +127,7 @@
|
|
|
127
127
|
{% if site.enable_darkmode %}
|
|
128
128
|
<!-- Toogle theme mode -->
|
|
129
129
|
<li class="toggle-container">
|
|
130
|
-
<button id="light-toggle" title="Change theme">
|
|
130
|
+
<button id="light-toggle" type="button" title="Change theme" aria-label="Change color theme">
|
|
131
131
|
<i class="fa-half-sun-moon" id="light-toggle-system"></i>
|
|
132
132
|
<i class="fa-solid fa-moon" id="light-toggle-dark"></i>
|
|
133
133
|
<i class="fa-solid fa-sun" id="light-toggle-light"></i>
|
|
@@ -1,35 +1,42 @@
|
|
|
1
|
-
{% assign
|
|
2
|
-
{%
|
|
3
|
-
{%
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
<br>
|
|
9
|
-
<ul class="list-disc pl-8"></ul>
|
|
10
|
-
|
|
11
|
-
<!-- Adds related posts to the end of an article -->
|
|
12
|
-
<h2 class="text-3xl font-semibold mb-4 mt-12">Enjoy Reading This Article?</h2>
|
|
13
|
-
{% else %}
|
|
14
|
-
<h2 class="text-3xl font-semibold mb-4 mt-12">Enjoy Reading This Article?</h2>
|
|
15
|
-
{% endif %}
|
|
1
|
+
{% assign related_posts = site.related_posts | limit: site.related_blog_posts.max_related %}
|
|
2
|
+
{% if related_posts != empty %}
|
|
3
|
+
{% if page.layout == 'post' %}
|
|
4
|
+
<br>
|
|
5
|
+
<hr>
|
|
6
|
+
<br>
|
|
7
|
+
{% endif %}
|
|
16
8
|
|
|
17
|
-
|
|
18
|
-
|
|
9
|
+
<h2 class="text-3xl font-semibold mb-4 mt-12">Enjoy Reading This Article?</h2>
|
|
10
|
+
<p class="mb-2">Here are some more articles you might like to read next:</p>
|
|
19
11
|
|
|
20
|
-
<
|
|
21
|
-
{%
|
|
22
|
-
<
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
12
|
+
<ul class="list-disc pl-7 my-3 space-y-2 marker:text-[var(--global-theme-color)]">
|
|
13
|
+
{% for post in related_posts %}
|
|
14
|
+
<li class="leading-relaxed">
|
|
15
|
+
{% if post.redirect == blank %}
|
|
16
|
+
<a class="underline decoration-1 hover:text-[var(--global-hover-color)]" href="{{ post.url | relative_url }}">
|
|
17
|
+
{{- post.title -}}
|
|
18
|
+
</a>
|
|
19
|
+
{% elsif post.redirect contains '://' %}
|
|
20
|
+
<a
|
|
21
|
+
class="underline decoration-1 hover:text-[var(--global-hover-color)]"
|
|
22
|
+
href="{{ post.redirect }}"
|
|
23
|
+
target="_blank"
|
|
24
|
+
rel="noopener noreferrer"
|
|
25
|
+
>
|
|
26
|
+
{{- post.title -}}
|
|
27
|
+
</a>
|
|
28
|
+
<svg width="1rem" height="1rem" viewBox="0 0 30 30" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
|
29
|
+
<path d="M17 13.5v6H5v-12h6m3-3h6v6m0-6-9 9" class="icon_svg-stroke" stroke="#999" stroke-width="1.5" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round"></path>
|
|
30
|
+
</svg>
|
|
31
|
+
{% else %}
|
|
32
|
+
<a class="underline decoration-1 hover:text-[var(--global-hover-color)]" href="{{ post.redirect | relative_url }}">
|
|
33
|
+
{{- post.title -}}
|
|
34
|
+
</a>
|
|
35
|
+
{% endif %}
|
|
36
|
+
</li>
|
|
37
|
+
{% endfor %}
|
|
38
|
+
</ul>
|
|
39
|
+
{% endif %}
|
|
33
40
|
{% if site.newsletter.enabled and site.footer_fixed %}
|
|
34
41
|
<p class="mb-2" style="margin-top: 1.5rem !important">Subscribe to be notified of future articles:</p>
|
|
35
42
|
{% if site.plugins contains 'al_newsletter' %}
|
data/_includes/scripts.liquid
CHANGED
|
@@ -60,6 +60,19 @@
|
|
|
60
60
|
crossorigin="anonymous"
|
|
61
61
|
></script>
|
|
62
62
|
{% endif %}
|
|
63
|
+
{% if page.pretty_table and site.al_folio.compat.bootstrap.enabled != true %}
|
|
64
|
+
<script defer src="{{ '/assets/js/table-engine.js' | relative_url | bust_file_cache }}" type="text/javascript"></script>
|
|
65
|
+
{% endif %}
|
|
66
|
+
|
|
67
|
+
{% if page.toc and page.toc.sidebar %}
|
|
68
|
+
<!-- Tocbot -->
|
|
69
|
+
<script
|
|
70
|
+
defer
|
|
71
|
+
src="{{ site.third_party_libraries.tocbot.url.js }}"
|
|
72
|
+
integrity="{{ site.third_party_libraries.tocbot.integrity.js }}"
|
|
73
|
+
crossorigin="anonymous"
|
|
74
|
+
></script>
|
|
75
|
+
{% endif %}
|
|
63
76
|
|
|
64
77
|
<!-- Load Common JS -->
|
|
65
78
|
<script src="{{ '/assets/js/no_defer.js' | relative_url | bust_file_cache }}"></script>
|
data/_layouts/default.liquid
CHANGED
|
@@ -29,14 +29,14 @@
|
|
|
29
29
|
<div class="col-sm-9">{{ content }}</div>
|
|
30
30
|
<!-- sidebar, which will move to the top on a small screen -->
|
|
31
31
|
<div class="col-sm-3">
|
|
32
|
-
<nav id="toc-sidebar" class="sticky-top"></nav>
|
|
32
|
+
<nav id="toc-sidebar" class="sticky-top toc toc-sidebar" aria-label="Table of contents"></nav>
|
|
33
33
|
</div>
|
|
34
34
|
</div>
|
|
35
35
|
{% else %}
|
|
36
36
|
<div class="row">
|
|
37
37
|
<!-- sidebar, which will move to the top on a small screen -->
|
|
38
38
|
<div class="col-sm-3">
|
|
39
|
-
<nav id="toc-sidebar" class="sticky-top"></nav>
|
|
39
|
+
<nav id="toc-sidebar" class="sticky-top toc toc-sidebar" aria-label="Table of contents"></nav>
|
|
40
40
|
</div>
|
|
41
41
|
<!-- main content area -->
|
|
42
42
|
<div class="col-sm-9">{{ content }}</div>
|
data/_sass/_components.scss
CHANGED
|
@@ -53,6 +53,29 @@ ul.task-list input[type="checkbox"] {
|
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
.hoverable {
|
|
57
|
+
transition:
|
|
58
|
+
transform 0.22s ease,
|
|
59
|
+
box-shadow 0.22s ease;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.hoverable:hover {
|
|
63
|
+
transform: translateY(-4px);
|
|
64
|
+
box-shadow:
|
|
65
|
+
0 10px 22px rgba(0, 0, 0, 0.12),
|
|
66
|
+
0 4px 10px rgba(0, 0, 0, 0.1);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
@media (prefers-reduced-motion: reduce) {
|
|
70
|
+
.hoverable {
|
|
71
|
+
transition: none;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.hoverable:hover {
|
|
75
|
+
transform: none;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
56
79
|
// Profile
|
|
57
80
|
|
|
58
81
|
.profile {
|
data/_sass/_navbar.scss
CHANGED
|
@@ -9,6 +9,34 @@
|
|
|
9
9
|
border-bottom: 1px solid var(--global-divider-color);
|
|
10
10
|
background-color: var(--global-bg-color);
|
|
11
11
|
opacity: 0.95;
|
|
12
|
+
|
|
13
|
+
.navbar-collapse-main {
|
|
14
|
+
justify-content: flex-end;
|
|
15
|
+
text-align: right;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.navbar-menu-list {
|
|
19
|
+
margin-left: auto;
|
|
20
|
+
align-items: center;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@media (max-width: 575.98px) {
|
|
25
|
+
.navbar {
|
|
26
|
+
.navbar-toggler-main {
|
|
27
|
+
margin-left: auto;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.navbar-collapse-main {
|
|
31
|
+
text-align: left;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.navbar-menu-list {
|
|
35
|
+
margin-left: 0;
|
|
36
|
+
align-items: flex-start;
|
|
37
|
+
width: 100%;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
12
40
|
}
|
|
13
41
|
|
|
14
42
|
.navbar .dropdown-menu,
|
|
@@ -160,6 +188,7 @@
|
|
|
160
188
|
border: 0;
|
|
161
189
|
background-color: inherit;
|
|
162
190
|
color: var(--global-text-color);
|
|
191
|
+
cursor: pointer;
|
|
163
192
|
/* Fix footprint to prevent navbar shifting when icon changes */
|
|
164
193
|
width: 2rem;
|
|
165
194
|
height: 2rem;
|
|
@@ -183,6 +212,7 @@
|
|
|
183
212
|
border: 0;
|
|
184
213
|
background-color: inherit;
|
|
185
214
|
color: var(--global-text-color);
|
|
215
|
+
cursor: pointer;
|
|
186
216
|
|
|
187
217
|
&:hover {
|
|
188
218
|
color: var(--global-hover-color);
|
data/_sass/_teachings.scss
CHANGED
|
@@ -63,6 +63,24 @@
|
|
|
63
63
|
|
|
64
64
|
.course-schedule {
|
|
65
65
|
margin-bottom: 30px;
|
|
66
|
+
|
|
67
|
+
table.table {
|
|
68
|
+
margin-top: 0.75rem;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
table.table thead th {
|
|
72
|
+
background-color: color-mix(in srgb, var(--global-theme-color) 12%, transparent);
|
|
73
|
+
border-top: 0;
|
|
74
|
+
border-bottom: 1px solid var(--global-divider-color);
|
|
75
|
+
color: var(--global-text-color);
|
|
76
|
+
font-weight: 600;
|
|
77
|
+
letter-spacing: 0.01em;
|
|
78
|
+
padding: 0.7rem 1rem;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
table.table tbody td {
|
|
82
|
+
padding: 0.72rem 1rem;
|
|
83
|
+
}
|
|
66
84
|
}
|
|
67
85
|
|
|
68
86
|
.schedule-description {
|
data/_sass/_typography.scss
CHANGED
|
@@ -23,15 +23,16 @@ hr {
|
|
|
23
23
|
border-top: 1px solid var(--global-divider-color);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
table {
|
|
26
|
+
table:not(.table) {
|
|
27
27
|
td,
|
|
28
28
|
th {
|
|
29
29
|
font-size: 1rem;
|
|
30
|
-
padding:
|
|
30
|
+
padding: 0.45rem 1rem 0.45rem 0;
|
|
31
|
+
border-top: 1px solid var(--global-divider-color);
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
th {
|
|
34
|
-
font-weight:
|
|
35
|
+
font-weight: 600;
|
|
35
36
|
}
|
|
36
37
|
}
|
|
37
38
|
|
data/_sass/_utilities.scss
CHANGED
|
@@ -55,6 +55,12 @@ code {
|
|
|
55
55
|
word-wrap: break-word;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
:not(pre) > code {
|
|
59
|
+
font-size: 0.82em;
|
|
60
|
+
font-weight: 400;
|
|
61
|
+
padding: 2px 4px;
|
|
62
|
+
}
|
|
63
|
+
|
|
58
64
|
// Progress bars
|
|
59
65
|
|
|
60
66
|
progress {
|
|
@@ -177,33 +183,176 @@ html.transition *:after {
|
|
|
177
183
|
}
|
|
178
184
|
}
|
|
179
185
|
|
|
186
|
+
// Tailwind table engine
|
|
187
|
+
|
|
188
|
+
.af-table-shell {
|
|
189
|
+
margin: 1.1rem 0 1.4rem;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.af-table-toolbar {
|
|
193
|
+
display: flex;
|
|
194
|
+
flex-wrap: wrap;
|
|
195
|
+
align-items: center;
|
|
196
|
+
justify-content: space-between;
|
|
197
|
+
gap: 0.7rem;
|
|
198
|
+
margin-bottom: 0.6rem;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.af-table-search {
|
|
202
|
+
min-width: min(300px, 100%);
|
|
203
|
+
max-width: 100%;
|
|
204
|
+
border: 1px solid var(--global-divider-color);
|
|
205
|
+
border-radius: 0.4rem;
|
|
206
|
+
background-color: var(--global-bg-color);
|
|
207
|
+
color: var(--global-text-color);
|
|
208
|
+
font-size: 0.94rem;
|
|
209
|
+
line-height: 1.4;
|
|
210
|
+
padding: 0.42rem 0.72rem;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.af-table-search:focus-visible {
|
|
214
|
+
outline: 2px solid color-mix(in srgb, var(--global-theme-color) 35%, transparent);
|
|
215
|
+
outline-offset: 1px;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.af-table-pagination {
|
|
219
|
+
display: inline-flex;
|
|
220
|
+
align-items: center;
|
|
221
|
+
gap: 0.35rem;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.af-table-page-info {
|
|
225
|
+
color: var(--global-text-color);
|
|
226
|
+
font-size: 0.85rem;
|
|
227
|
+
font-weight: 500;
|
|
228
|
+
margin-right: 0.3rem;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.af-table-pagination button {
|
|
232
|
+
border: 1px solid var(--global-divider-color);
|
|
233
|
+
background-color: transparent;
|
|
234
|
+
border-radius: 0.35rem;
|
|
235
|
+
color: var(--global-text-color);
|
|
236
|
+
cursor: pointer;
|
|
237
|
+
font-size: 0.85rem;
|
|
238
|
+
font-weight: 500;
|
|
239
|
+
line-height: 1;
|
|
240
|
+
min-width: 2rem;
|
|
241
|
+
padding: 0.38rem 0.56rem;
|
|
242
|
+
transition:
|
|
243
|
+
background-color 0.2s ease,
|
|
244
|
+
color 0.2s ease,
|
|
245
|
+
border-color 0.2s ease;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.af-table-pagination button:hover:not(:disabled) {
|
|
249
|
+
background-color: var(--global-theme-color);
|
|
250
|
+
border-color: var(--global-theme-color);
|
|
251
|
+
color: var(--global-hover-text-color);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
.af-table-pagination button:disabled {
|
|
255
|
+
cursor: default;
|
|
256
|
+
opacity: 0.45;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
table[data-toggle="table"].af-table-enhanced {
|
|
260
|
+
margin-bottom: 0;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
table[data-toggle="table"].af-table-enhanced thead th {
|
|
264
|
+
background-color: color-mix(in srgb, var(--global-theme-color) 12%, transparent);
|
|
265
|
+
border-bottom: 1px solid var(--global-divider-color);
|
|
266
|
+
color: var(--global-text-color);
|
|
267
|
+
font-weight: 600;
|
|
268
|
+
letter-spacing: 0.01em;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
table[data-toggle="table"].af-table-enhanced th.af-sortable {
|
|
272
|
+
cursor: pointer;
|
|
273
|
+
user-select: none;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
table[data-toggle="table"].af-table-enhanced th.af-sortable:hover {
|
|
277
|
+
color: var(--global-hover-color);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
table[data-toggle="table"].af-table-enhanced tbody tr.af-row-selected {
|
|
281
|
+
background-color: color-mix(in srgb, var(--global-theme-color) 12%, transparent);
|
|
282
|
+
}
|
|
283
|
+
|
|
180
284
|
// Table of Contents
|
|
181
285
|
|
|
182
|
-
|
|
286
|
+
#toc-sidebar {
|
|
287
|
+
z-index: 1;
|
|
183
288
|
top: 5rem;
|
|
184
289
|
|
|
185
|
-
|
|
186
|
-
|
|
290
|
+
&.toc > .toc-list {
|
|
291
|
+
overflow: hidden;
|
|
292
|
+
position: relative;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
.toc-list {
|
|
296
|
+
list-style: none;
|
|
297
|
+
margin: 0;
|
|
298
|
+
padding-left: 0.9rem;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
.toc-list .toc-list {
|
|
302
|
+
margin-top: 0.15rem;
|
|
303
|
+
margin-left: 0.55rem;
|
|
304
|
+
padding-left: 0.8rem;
|
|
187
305
|
}
|
|
188
306
|
|
|
189
|
-
.
|
|
307
|
+
.toc-link {
|
|
190
308
|
color: var(--global-text-color);
|
|
191
|
-
|
|
309
|
+
display: block;
|
|
310
|
+
position: relative;
|
|
311
|
+
font-size: 0.78rem;
|
|
312
|
+
line-height: 1.4;
|
|
313
|
+
margin: 0.12rem 0;
|
|
314
|
+
padding: 0.14rem 0.35rem 0.14rem 0.55rem;
|
|
315
|
+
border-left: 0;
|
|
316
|
+
transition:
|
|
317
|
+
color 0.2s ease,
|
|
318
|
+
font-weight 0.2s ease;
|
|
319
|
+
transform: none;
|
|
320
|
+
|
|
321
|
+
&::before {
|
|
322
|
+
background-color: color-mix(in srgb, var(--global-divider-color) 85%, transparent);
|
|
323
|
+
bottom: 0;
|
|
324
|
+
left: 0;
|
|
325
|
+
margin-top: 0;
|
|
326
|
+
top: 0;
|
|
327
|
+
width: 2px;
|
|
328
|
+
}
|
|
192
329
|
|
|
193
330
|
&:hover {
|
|
194
331
|
color: var(--global-hover-color);
|
|
195
|
-
|
|
332
|
+
text-decoration: none;
|
|
333
|
+
transform: none;
|
|
334
|
+
|
|
335
|
+
&::before {
|
|
336
|
+
background-color: var(--global-hover-color);
|
|
337
|
+
}
|
|
196
338
|
}
|
|
197
339
|
}
|
|
198
340
|
|
|
199
|
-
.
|
|
341
|
+
.toc-link.is-active-link {
|
|
200
342
|
color: var(--global-theme-color);
|
|
201
|
-
|
|
202
|
-
|
|
343
|
+
font-weight: 600;
|
|
344
|
+
transform: none;
|
|
345
|
+
|
|
346
|
+
&::before {
|
|
347
|
+
background-color: var(--global-theme-color);
|
|
348
|
+
}
|
|
203
349
|
|
|
204
350
|
&:hover {
|
|
205
351
|
color: var(--global-hover-color);
|
|
206
|
-
|
|
352
|
+
|
|
353
|
+
&::before {
|
|
354
|
+
background-color: var(--global-hover-color);
|
|
355
|
+
}
|
|
207
356
|
}
|
|
208
357
|
}
|
|
209
358
|
}
|
|
@@ -211,7 +360,7 @@ nav[data-toggle="toc"] {
|
|
|
211
360
|
/* small screens */
|
|
212
361
|
@media (max-width: 576px) {
|
|
213
362
|
/* override stickyness so that the navigation does not follow scrolling */
|
|
214
|
-
|
|
363
|
+
#toc-sidebar {
|
|
215
364
|
visibility: hidden;
|
|
216
365
|
height: 0;
|
|
217
366
|
top: 0;
|
|
@@ -233,7 +382,7 @@ nav[data-toggle="toc"] {
|
|
|
233
382
|
}
|
|
234
383
|
}
|
|
235
384
|
|
|
236
|
-
code {
|
|
385
|
+
:not(pre) > code {
|
|
237
386
|
font-size: 0.875rem;
|
|
238
387
|
padding: 2px 4px;
|
|
239
388
|
}
|
|
@@ -253,7 +402,7 @@ nav[data-toggle="toc"] {
|
|
|
253
402
|
}
|
|
254
403
|
}
|
|
255
404
|
|
|
256
|
-
code {
|
|
405
|
+
:not(pre) > code {
|
|
257
406
|
font-size: 0.8rem;
|
|
258
407
|
padding: 1px 3px;
|
|
259
408
|
}
|
|
@@ -446,10 +595,6 @@ nav[data-toggle="toc"] {
|
|
|
446
595
|
z-index: 999;
|
|
447
596
|
}
|
|
448
597
|
|
|
449
|
-
#toc-sidebar {
|
|
450
|
-
z-index: 1;
|
|
451
|
-
}
|
|
452
|
-
|
|
453
598
|
.echarts {
|
|
454
599
|
height: 400px;
|
|
455
600
|
width: 100%;
|
|
@@ -574,18 +719,58 @@ figure.cover {
|
|
|
574
719
|
// Calendar
|
|
575
720
|
|
|
576
721
|
.calendar-toggle-btn {
|
|
577
|
-
|
|
578
|
-
color:
|
|
579
|
-
|
|
580
|
-
border:
|
|
581
|
-
|
|
582
|
-
font-size: 1em;
|
|
722
|
+
align-items: center;
|
|
723
|
+
background-color: transparent;
|
|
724
|
+
border: 1px solid var(--global-theme-color);
|
|
725
|
+
border-radius: 999px;
|
|
726
|
+
color: var(--global-theme-color);
|
|
583
727
|
cursor: pointer;
|
|
584
|
-
|
|
728
|
+
display: inline-flex;
|
|
729
|
+
font-size: 0.92rem;
|
|
730
|
+
font-weight: 600;
|
|
731
|
+
gap: 0.25rem;
|
|
732
|
+
padding: 0.45rem 0.95rem;
|
|
733
|
+
transition:
|
|
734
|
+
background-color 0.2s ease,
|
|
735
|
+
color 0.2s ease,
|
|
736
|
+
box-shadow 0.2s ease,
|
|
737
|
+
transform 0.16s ease;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
.calendar-toggle-btn:focus-visible {
|
|
741
|
+
outline: 2px solid color-mix(in srgb, var(--global-theme-color) 45%, transparent);
|
|
742
|
+
outline-offset: 2px;
|
|
585
743
|
}
|
|
586
744
|
|
|
587
745
|
.calendar-toggle-btn:hover {
|
|
588
|
-
background-color:
|
|
746
|
+
background-color: color-mix(in srgb, var(--global-theme-color) 14%, transparent);
|
|
747
|
+
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.09);
|
|
748
|
+
color: var(--global-theme-color);
|
|
749
|
+
transform: translateY(-1px);
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
.calendar-toggle-btn:active {
|
|
753
|
+
transform: translateY(0);
|
|
754
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
.calendar-toggle-btn[aria-pressed="true"],
|
|
758
|
+
.calendar-toggle-btn.is-open {
|
|
759
|
+
background-color: var(--global-theme-color);
|
|
760
|
+
color: var(--global-hover-text-color);
|
|
761
|
+
border-color: var(--global-theme-color);
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
.calendar-toggle-btn[aria-pressed="true"]:hover,
|
|
765
|
+
.calendar-toggle-btn.is-open:hover {
|
|
766
|
+
background-color: var(--global-hover-color);
|
|
767
|
+
border-color: var(--global-hover-color);
|
|
768
|
+
color: var(--global-hover-text-color);
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
.calendar-toggle-btn.btn-sm {
|
|
772
|
+
cursor: pointer;
|
|
773
|
+
font-size: 0.88rem;
|
|
589
774
|
}
|
|
590
775
|
|
|
591
776
|
.google-calendar-embed {
|
data/assets/js/calendar-setup.js
CHANGED
|
@@ -2,9 +2,15 @@
|
|
|
2
2
|
function toggleCalendar() {
|
|
3
3
|
const el = document.getElementById("calendar-container");
|
|
4
4
|
const btn = document.getElementById("calendar-toggle-btn");
|
|
5
|
+
if (!el || !btn) {
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
|
|
5
9
|
const isHidden = el.style.display === "none";
|
|
6
10
|
el.style.display = isHidden ? "block" : "none";
|
|
7
11
|
btn.innerText = isHidden ? "Hide Calendar" : "Show Calendar";
|
|
12
|
+
btn.setAttribute("aria-pressed", isHidden ? "true" : "false");
|
|
13
|
+
btn.classList.toggle("is-open", isHidden);
|
|
8
14
|
|
|
9
15
|
// Update calendar URL when toggling to ensure correct theme
|
|
10
16
|
if (isHidden) {
|
data/assets/js/common.js
CHANGED
|
@@ -45,8 +45,8 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
45
45
|
});
|
|
46
46
|
|
|
47
47
|
const tocSidebar = document.querySelector("#toc-sidebar");
|
|
48
|
+
const contentRoot = document.querySelector('[role="main"]') || document.querySelector("main") || document.body;
|
|
48
49
|
const buildSidebarToc = (tocRoot) => {
|
|
49
|
-
const contentRoot = document.querySelector("main") || document.body;
|
|
50
50
|
const headings = Array.from(contentRoot.querySelectorAll("h2, h3")).filter((heading) => {
|
|
51
51
|
return !heading.hasAttribute("data-toc-skip");
|
|
52
52
|
});
|
|
@@ -56,7 +56,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
const list = document.createElement("ul");
|
|
59
|
-
list.className = "
|
|
59
|
+
list.className = "toc-list";
|
|
60
60
|
|
|
61
61
|
headings.forEach((heading) => {
|
|
62
62
|
if (!heading.id) {
|
|
@@ -68,12 +68,13 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
const item = document.createElement("li");
|
|
71
|
+
item.className = "toc-list-item";
|
|
71
72
|
const link = document.createElement("a");
|
|
72
|
-
link.className = "
|
|
73
|
+
link.className = "toc-link";
|
|
73
74
|
link.href = `#${heading.id}`;
|
|
74
|
-
link.textContent = heading.textContent.trim();
|
|
75
|
+
link.textContent = heading.dataset.tocText || heading.textContent.trim();
|
|
75
76
|
if (heading.tagName.toLowerCase() === "h3") {
|
|
76
|
-
|
|
77
|
+
item.classList.add("is-collapsible");
|
|
77
78
|
}
|
|
78
79
|
|
|
79
80
|
item.appendChild(link);
|
|
@@ -88,8 +89,51 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
88
89
|
heading.setAttribute("data-toc-skip", "");
|
|
89
90
|
});
|
|
90
91
|
|
|
91
|
-
|
|
92
|
-
|
|
92
|
+
const headings = Array.from(contentRoot.querySelectorAll("h2, h3")).filter((heading) => !heading.hasAttribute("data-toc-skip"));
|
|
93
|
+
headings.forEach((heading) => {
|
|
94
|
+
if (!heading.id) {
|
|
95
|
+
heading.id = heading.textContent
|
|
96
|
+
.trim()
|
|
97
|
+
.toLowerCase()
|
|
98
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
99
|
+
.replace(/^-+|-+$/g, "");
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const applyCustomTocLabels = () => {
|
|
104
|
+
tocSidebar.querySelectorAll(".toc-link").forEach((link) => {
|
|
105
|
+
const anchor = link.getAttribute("href") || "";
|
|
106
|
+
const headingId = decodeURIComponent(anchor.replace(/^#/, ""));
|
|
107
|
+
if (!headingId) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const heading = document.getElementById(headingId);
|
|
111
|
+
const customText = heading?.dataset?.tocText;
|
|
112
|
+
if (customText) {
|
|
113
|
+
link.textContent = customText;
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
if (window.tocbot && typeof window.tocbot.init === "function" && headings.length > 0) {
|
|
119
|
+
if (typeof window.tocbot.destroy === "function") {
|
|
120
|
+
window.tocbot.destroy();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
window.tocbot.init({
|
|
124
|
+
tocSelector: "#toc-sidebar",
|
|
125
|
+
contentSelector: '[role="main"]',
|
|
126
|
+
headingSelector: "h2, h3",
|
|
127
|
+
ignoreSelector: "[data-toc-skip]",
|
|
128
|
+
hasInnerContainers: true,
|
|
129
|
+
collapseDepth: 6,
|
|
130
|
+
orderedList: false,
|
|
131
|
+
activeLinkClass: "is-active-link",
|
|
132
|
+
scrollSmooth: true,
|
|
133
|
+
scrollSmoothOffset: -80,
|
|
134
|
+
headingsOffset: 80,
|
|
135
|
+
});
|
|
136
|
+
applyCustomTocLabels();
|
|
93
137
|
} else {
|
|
94
138
|
buildSidebarToc(tocSidebar);
|
|
95
139
|
}
|
|
@@ -1,18 +1,35 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
let jupyterNotebooks = $(".jupyter-notebook-iframe-container");
|
|
4
|
-
jupyterNotebooks.each(function () {
|
|
5
|
-
let iframeBody = $(this).find("iframe").get(0).contentWindow.document.body;
|
|
6
|
-
// Get all <a> elements in the bodyElement
|
|
7
|
-
let links = $(iframeBody).find("a");
|
|
1
|
+
document.addEventListener("DOMContentLoaded", () => {
|
|
2
|
+
const notebookIframes = document.querySelectorAll(".jupyter-notebook-iframe-container iframe");
|
|
8
3
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
if (
|
|
13
|
-
|
|
14
|
-
$(this).attr("target", "_blank");
|
|
4
|
+
const updateNotebookLinks = (iframe) => {
|
|
5
|
+
try {
|
|
6
|
+
const iframeDocument = iframe.contentDocument || iframe.contentWindow?.document;
|
|
7
|
+
if (!iframeDocument || !iframeDocument.body) {
|
|
8
|
+
return;
|
|
15
9
|
}
|
|
16
|
-
|
|
10
|
+
|
|
11
|
+
iframeDocument.querySelectorAll("a[href]").forEach((link) => {
|
|
12
|
+
link.setAttribute("target", "_blank");
|
|
13
|
+
const currentRel = link.getAttribute("rel") || "";
|
|
14
|
+
const relParts = currentRel.split(/\s+/).filter(Boolean);
|
|
15
|
+
if (!relParts.includes("noopener")) {
|
|
16
|
+
relParts.push("noopener");
|
|
17
|
+
}
|
|
18
|
+
if (!relParts.includes("noreferrer")) {
|
|
19
|
+
relParts.push("noreferrer");
|
|
20
|
+
}
|
|
21
|
+
link.setAttribute("rel", relParts.join(" "));
|
|
22
|
+
});
|
|
23
|
+
} catch (_error) {
|
|
24
|
+
// Cross-origin iframe access is blocked by design.
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
notebookIframes.forEach((iframe) => {
|
|
29
|
+
if (iframe.contentDocument?.readyState === "complete") {
|
|
30
|
+
updateNotebookLinks(iframe);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
iframe.addEventListener("load", () => updateNotebookLinks(iframe));
|
|
17
34
|
});
|
|
18
35
|
});
|
data/assets/js/masonry.js
CHANGED
|
@@ -1,12 +1,22 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
document.addEventListener("DOMContentLoaded", () => {
|
|
2
|
+
const grid = document.querySelector(".grid");
|
|
3
|
+
if (!grid || typeof window.Masonry !== "function") {
|
|
4
|
+
return;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const masonry = new window.Masonry(grid, {
|
|
4
8
|
gutter: 10,
|
|
5
9
|
horizontalOrder: true,
|
|
6
10
|
itemSelector: ".grid-item",
|
|
7
11
|
});
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
|
|
13
|
+
if (typeof window.imagesLoaded === "function") {
|
|
14
|
+
const tracker = window.imagesLoaded(grid);
|
|
15
|
+
if (tracker && typeof tracker.on === "function") {
|
|
16
|
+
tracker.on("progress", () => masonry.layout());
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
masonry.layout();
|
|
12
22
|
});
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
document.addEventListener("DOMContentLoaded", () => {
|
|
2
|
+
if (window.alFolio?.compatBootstrap) {
|
|
3
|
+
return;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const asBool = (value) => String(value || "").toLowerCase() === "true";
|
|
7
|
+
|
|
8
|
+
const parseSortValue = (value) => {
|
|
9
|
+
if (typeof value === "number") {
|
|
10
|
+
return { type: "number", value };
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const text = String(value ?? "").trim();
|
|
14
|
+
const numeric = Number(text.replace(/[$,%\s,]/g, ""));
|
|
15
|
+
if (text !== "" && Number.isFinite(numeric) && /^[-+]?[$]?\d/.test(text)) {
|
|
16
|
+
return { type: "number", value: numeric };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return { type: "string", value: text.toLowerCase() };
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
class AfTableEngine {
|
|
23
|
+
constructor(table) {
|
|
24
|
+
this.table = table;
|
|
25
|
+
this.columns = Array.from(table.querySelectorAll("thead th")).map((th, index) => ({
|
|
26
|
+
element: th,
|
|
27
|
+
index,
|
|
28
|
+
field: th.dataset.field || `__col_${index}`,
|
|
29
|
+
checkbox: asBool(th.dataset.checkbox),
|
|
30
|
+
sortable: asBool(th.dataset.sortable),
|
|
31
|
+
align: th.dataset.align || "",
|
|
32
|
+
halign: th.dataset.halign || th.dataset.align || "",
|
|
33
|
+
}));
|
|
34
|
+
this.state = {
|
|
35
|
+
allRows: [],
|
|
36
|
+
filteredRows: [],
|
|
37
|
+
search: "",
|
|
38
|
+
page: 1,
|
|
39
|
+
pageSize: Number.parseInt(table.dataset.pageSize || "10", 10) || 10,
|
|
40
|
+
sortField: null,
|
|
41
|
+
sortDirection: "asc",
|
|
42
|
+
};
|
|
43
|
+
this.options = {
|
|
44
|
+
search: asBool(table.dataset.search),
|
|
45
|
+
pagination: asBool(table.dataset.pagination),
|
|
46
|
+
clickToSelect: asBool(table.dataset.clickToSelect),
|
|
47
|
+
dataUrl: table.dataset.url || "",
|
|
48
|
+
};
|
|
49
|
+
this.tbody = table.querySelector("tbody") || table.createTBody();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async init() {
|
|
53
|
+
this.table.classList.add("table", "table-hover", "af-table-enhanced");
|
|
54
|
+
this.buildShell();
|
|
55
|
+
this.bindHeaderSort();
|
|
56
|
+
this.state.allRows = await this.loadRows();
|
|
57
|
+
this.applyFiltersAndSort();
|
|
58
|
+
this.render();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
buildShell() {
|
|
62
|
+
const shell = document.createElement("div");
|
|
63
|
+
shell.className = "af-table-shell";
|
|
64
|
+
|
|
65
|
+
this.table.parentNode.insertBefore(shell, this.table);
|
|
66
|
+
shell.appendChild(this.table);
|
|
67
|
+
|
|
68
|
+
const toolbar = document.createElement("div");
|
|
69
|
+
toolbar.className = "af-table-toolbar";
|
|
70
|
+
shell.insertBefore(toolbar, this.table);
|
|
71
|
+
this.toolbar = toolbar;
|
|
72
|
+
|
|
73
|
+
if (this.options.search) {
|
|
74
|
+
const searchInput = document.createElement("input");
|
|
75
|
+
searchInput.className = "af-table-search";
|
|
76
|
+
searchInput.type = "search";
|
|
77
|
+
searchInput.placeholder = "Search table...";
|
|
78
|
+
searchInput.setAttribute("aria-label", "Search table rows");
|
|
79
|
+
searchInput.addEventListener("input", () => {
|
|
80
|
+
this.state.search = searchInput.value.trim().toLowerCase();
|
|
81
|
+
this.state.page = 1;
|
|
82
|
+
this.applyFiltersAndSort();
|
|
83
|
+
this.render();
|
|
84
|
+
});
|
|
85
|
+
toolbar.appendChild(searchInput);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (this.options.pagination) {
|
|
89
|
+
const pagination = document.createElement("div");
|
|
90
|
+
pagination.className = "af-table-pagination";
|
|
91
|
+
|
|
92
|
+
const info = document.createElement("span");
|
|
93
|
+
info.className = "af-table-page-info";
|
|
94
|
+
pagination.appendChild(info);
|
|
95
|
+
this.pageInfo = info;
|
|
96
|
+
|
|
97
|
+
const prevButton = document.createElement("button");
|
|
98
|
+
prevButton.type = "button";
|
|
99
|
+
prevButton.textContent = "Prev";
|
|
100
|
+
prevButton.addEventListener("click", () => {
|
|
101
|
+
if (this.state.page > 1) {
|
|
102
|
+
this.state.page -= 1;
|
|
103
|
+
this.render();
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
pagination.appendChild(prevButton);
|
|
107
|
+
this.prevButton = prevButton;
|
|
108
|
+
|
|
109
|
+
const nextButton = document.createElement("button");
|
|
110
|
+
nextButton.type = "button";
|
|
111
|
+
nextButton.textContent = "Next";
|
|
112
|
+
nextButton.addEventListener("click", () => {
|
|
113
|
+
if (this.state.page < this.totalPages()) {
|
|
114
|
+
this.state.page += 1;
|
|
115
|
+
this.render();
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
pagination.appendChild(nextButton);
|
|
119
|
+
this.nextButton = nextButton;
|
|
120
|
+
|
|
121
|
+
this.toolbar.appendChild(pagination);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
bindHeaderSort() {
|
|
126
|
+
this.columns.forEach((column) => {
|
|
127
|
+
if (!column.sortable || column.checkbox) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (column.halign) {
|
|
132
|
+
column.element.style.textAlign = column.halign;
|
|
133
|
+
}
|
|
134
|
+
column.element.classList.add("af-sortable");
|
|
135
|
+
column.element.addEventListener("click", () => {
|
|
136
|
+
if (this.state.sortField === column.field) {
|
|
137
|
+
this.state.sortDirection = this.state.sortDirection === "asc" ? "desc" : "asc";
|
|
138
|
+
} else {
|
|
139
|
+
this.state.sortField = column.field;
|
|
140
|
+
this.state.sortDirection = "asc";
|
|
141
|
+
}
|
|
142
|
+
this.renderHeaders();
|
|
143
|
+
this.applyFiltersAndSort();
|
|
144
|
+
this.render();
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async loadRows() {
|
|
150
|
+
if (!this.options.dataUrl) {
|
|
151
|
+
return this.readRowsFromMarkup();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
try {
|
|
155
|
+
const response = await fetch(this.options.dataUrl, { credentials: "same-origin" });
|
|
156
|
+
if (!response.ok) {
|
|
157
|
+
return this.readRowsFromMarkup();
|
|
158
|
+
}
|
|
159
|
+
const payload = await response.json();
|
|
160
|
+
if (!Array.isArray(payload)) {
|
|
161
|
+
return this.readRowsFromMarkup();
|
|
162
|
+
}
|
|
163
|
+
return payload.map((row) => this.normalizeRow(row));
|
|
164
|
+
} catch (_error) {
|
|
165
|
+
return this.readRowsFromMarkup();
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
readRowsFromMarkup() {
|
|
170
|
+
const rows = Array.from(this.tbody.querySelectorAll("tr"));
|
|
171
|
+
return rows.map((tr) => {
|
|
172
|
+
const cells = Array.from(tr.querySelectorAll("td"));
|
|
173
|
+
const row = {};
|
|
174
|
+
this.columns.forEach((column) => {
|
|
175
|
+
const cell = cells[column.index];
|
|
176
|
+
if (column.checkbox) {
|
|
177
|
+
row[column.field] = Boolean(cell?.querySelector('input[type="checkbox"]')?.checked);
|
|
178
|
+
} else {
|
|
179
|
+
row[column.field] = cell ? cell.textContent.trim() : "";
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
return row;
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
normalizeRow(row) {
|
|
187
|
+
const normalized = {};
|
|
188
|
+
this.columns.forEach((column) => {
|
|
189
|
+
if (column.checkbox) {
|
|
190
|
+
normalized[column.field] = Boolean(row[column.field]);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
normalized[column.field] = row[column.field] ?? "";
|
|
194
|
+
});
|
|
195
|
+
return normalized;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
applyFiltersAndSort() {
|
|
199
|
+
let rows = this.state.allRows.slice();
|
|
200
|
+
if (this.state.search) {
|
|
201
|
+
rows = rows.filter((row) => {
|
|
202
|
+
return this.columns.some((column) => {
|
|
203
|
+
if (column.checkbox) {
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
const value = row[column.field];
|
|
207
|
+
return String(value ?? "")
|
|
208
|
+
.toLowerCase()
|
|
209
|
+
.includes(this.state.search);
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (this.state.sortField) {
|
|
215
|
+
const direction = this.state.sortDirection === "asc" ? 1 : -1;
|
|
216
|
+
const field = this.state.sortField;
|
|
217
|
+
rows.sort((leftRow, rightRow) => {
|
|
218
|
+
const left = parseSortValue(leftRow[field]);
|
|
219
|
+
const right = parseSortValue(rightRow[field]);
|
|
220
|
+
if (left.type === "number" && right.type === "number") {
|
|
221
|
+
return (left.value - right.value) * direction;
|
|
222
|
+
}
|
|
223
|
+
return String(left.value).localeCompare(String(right.value)) * direction;
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
this.state.filteredRows = rows;
|
|
228
|
+
if (this.state.page > this.totalPages()) {
|
|
229
|
+
this.state.page = this.totalPages();
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
totalPages() {
|
|
234
|
+
if (!this.options.pagination) {
|
|
235
|
+
return 1;
|
|
236
|
+
}
|
|
237
|
+
return Math.max(1, Math.ceil(this.state.filteredRows.length / this.state.pageSize));
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
paginatedRows() {
|
|
241
|
+
if (!this.options.pagination) {
|
|
242
|
+
return this.state.filteredRows;
|
|
243
|
+
}
|
|
244
|
+
const from = (this.state.page - 1) * this.state.pageSize;
|
|
245
|
+
return this.state.filteredRows.slice(from, from + this.state.pageSize);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
renderHeaders() {
|
|
249
|
+
this.columns.forEach((column) => {
|
|
250
|
+
const label = column.element.dataset.afLabel || column.element.textContent.trim();
|
|
251
|
+
column.element.dataset.afLabel = label;
|
|
252
|
+
if (!column.sortable || column.checkbox) {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (this.state.sortField === column.field) {
|
|
257
|
+
const directionMarker = this.state.sortDirection === "asc" ? " \u2191" : " \u2193";
|
|
258
|
+
column.element.textContent = `${label}${directionMarker}`;
|
|
259
|
+
} else {
|
|
260
|
+
column.element.textContent = label;
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
render() {
|
|
266
|
+
this.renderHeaders();
|
|
267
|
+
this.tbody.replaceChildren();
|
|
268
|
+
|
|
269
|
+
this.paginatedRows().forEach((row) => {
|
|
270
|
+
const tr = document.createElement("tr");
|
|
271
|
+
let checkboxControl = null;
|
|
272
|
+
|
|
273
|
+
this.columns.forEach((column) => {
|
|
274
|
+
const td = document.createElement("td");
|
|
275
|
+
if (column.align) {
|
|
276
|
+
td.style.textAlign = column.align;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (column.checkbox) {
|
|
280
|
+
const checkbox = document.createElement("input");
|
|
281
|
+
checkbox.type = "checkbox";
|
|
282
|
+
checkbox.checked = Boolean(row[column.field]);
|
|
283
|
+
checkbox.setAttribute("aria-label", "Select row");
|
|
284
|
+
checkbox.addEventListener("change", () => {
|
|
285
|
+
row[column.field] = checkbox.checked;
|
|
286
|
+
tr.classList.toggle("af-row-selected", checkbox.checked);
|
|
287
|
+
});
|
|
288
|
+
checkboxControl = checkbox;
|
|
289
|
+
td.appendChild(checkbox);
|
|
290
|
+
tr.classList.toggle("af-row-selected", checkbox.checked);
|
|
291
|
+
} else {
|
|
292
|
+
td.textContent = String(row[column.field] ?? "");
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
tr.appendChild(td);
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
if (this.options.clickToSelect && checkboxControl) {
|
|
299
|
+
tr.addEventListener("click", (event) => {
|
|
300
|
+
if (event.target.closest('a, button, input, label, select, textarea')) {
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
checkboxControl.checked = !checkboxControl.checked;
|
|
304
|
+
checkboxControl.dispatchEvent(new Event("change", { bubbles: true }));
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
this.tbody.appendChild(tr);
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
if (this.options.pagination && this.pageInfo && this.prevButton && this.nextButton) {
|
|
312
|
+
const totalPages = this.totalPages();
|
|
313
|
+
this.pageInfo.textContent = `Page ${this.state.page} / ${totalPages}`;
|
|
314
|
+
this.prevButton.disabled = this.state.page <= 1;
|
|
315
|
+
this.nextButton.disabled = this.state.page >= totalPages;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
document.querySelectorAll('table[data-toggle="table"]').forEach((table) => {
|
|
321
|
+
if (table.dataset.afTableInitialized === "true") {
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
table.dataset.afTableInitialized = "true";
|
|
325
|
+
const engine = new AfTableEngine(table);
|
|
326
|
+
engine.init();
|
|
327
|
+
});
|
|
328
|
+
});
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: al_folio_core
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- al-folio maintainers
|
|
@@ -179,6 +179,7 @@ files:
|
|
|
179
179
|
- assets/js/nav-toggle.js
|
|
180
180
|
- assets/js/no_defer.js
|
|
181
181
|
- assets/js/progress-bar.js
|
|
182
|
+
- assets/js/table-engine.js
|
|
182
183
|
- assets/js/tabs.js
|
|
183
184
|
- assets/js/theme.js
|
|
184
185
|
- assets/js/tooltips-setup.js
|