al_folio_core 1.0.3 → 1.0.4
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 +8 -0
- data/_includes/calendar.liquid +3 -1
- data/_includes/head.liquid +11 -0
- data/_includes/header.liquid +2 -2
- 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 +2 -0
- data/_sass/_teachings.scss +18 -0
- data/_sass/_typography.scss +4 -3
- data/_sass/_utilities.scss +178 -20
- 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: af6ef2628760004262fc0a4af6b03c55e4cd4c25c7be638d7bdd6c60954cf4b1
|
|
4
|
+
data.tar.gz: b26e87c086ddd099bcfd91bc14cfcbfa84b9340cd70ddb1548586951151d4d61
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: da5cc4a13562f97f0c0a7930620c616a1cd95d8022d070beb6cac66cbe91aa16f964dca91df7ee55fbe2f75ace2c03a454d064b782f2d33c267e59da8c9b7c71
|
|
7
|
+
data.tar.gz: cdc0e5b066d1aa470af9abacd227129b32bed674c43ef4d7b3a8d4c790d58b68323273290528df572bc85c9a7a69d30760e70af285eda1a60c41059c2dad3e2e
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.0.4 - 2026-02-17
|
|
4
|
+
|
|
5
|
+
- Fixed related-posts HTML structure to render valid list markup.
|
|
6
|
+
- Restored sidebar TOC behavior and styling via Tocbot runtime integration.
|
|
7
|
+
- Added Tailwind-first vanilla table engine for `pretty_table` pages when Bootstrap compatibility is disabled.
|
|
8
|
+
- Replaced remaining jQuery-dependent runtime scripts (masonry, jupyter link handling) with vanilla JS.
|
|
9
|
+
- Improved project hover lift, teaching calendar toggle UX, and schedule/table styling parity.
|
|
10
|
+
|
|
3
11
|
## 1.0.3 - 2026-02-17
|
|
4
12
|
|
|
5
13
|
- 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
|
@@ -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="font-semibold 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="font-semibold 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="font-semibold 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-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-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
|
@@ -160,6 +160,7 @@
|
|
|
160
160
|
border: 0;
|
|
161
161
|
background-color: inherit;
|
|
162
162
|
color: var(--global-text-color);
|
|
163
|
+
cursor: pointer;
|
|
163
164
|
/* Fix footprint to prevent navbar shifting when icon changes */
|
|
164
165
|
width: 2rem;
|
|
165
166
|
height: 2rem;
|
|
@@ -183,6 +184,7 @@
|
|
|
183
184
|
border: 0;
|
|
184
185
|
background-color: inherit;
|
|
185
186
|
color: var(--global-text-color);
|
|
187
|
+
cursor: pointer;
|
|
186
188
|
|
|
187
189
|
&:hover {
|
|
188
190
|
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
|
@@ -177,29 +177,151 @@ html.transition *:after {
|
|
|
177
177
|
}
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
+
// Tailwind table engine
|
|
181
|
+
|
|
182
|
+
.af-table-shell {
|
|
183
|
+
margin: 1.1rem 0 1.4rem;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.af-table-toolbar {
|
|
187
|
+
display: flex;
|
|
188
|
+
flex-wrap: wrap;
|
|
189
|
+
align-items: center;
|
|
190
|
+
justify-content: space-between;
|
|
191
|
+
gap: 0.7rem;
|
|
192
|
+
margin-bottom: 0.6rem;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.af-table-search {
|
|
196
|
+
min-width: min(300px, 100%);
|
|
197
|
+
max-width: 100%;
|
|
198
|
+
border: 1px solid var(--global-divider-color);
|
|
199
|
+
border-radius: 0.4rem;
|
|
200
|
+
background-color: var(--global-bg-color);
|
|
201
|
+
color: var(--global-text-color);
|
|
202
|
+
font-size: 0.94rem;
|
|
203
|
+
line-height: 1.4;
|
|
204
|
+
padding: 0.42rem 0.72rem;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.af-table-search:focus-visible {
|
|
208
|
+
outline: 2px solid color-mix(in srgb, var(--global-theme-color) 35%, transparent);
|
|
209
|
+
outline-offset: 1px;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.af-table-pagination {
|
|
213
|
+
display: inline-flex;
|
|
214
|
+
align-items: center;
|
|
215
|
+
gap: 0.35rem;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.af-table-page-info {
|
|
219
|
+
color: var(--global-text-color);
|
|
220
|
+
font-size: 0.85rem;
|
|
221
|
+
font-weight: 500;
|
|
222
|
+
margin-right: 0.3rem;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.af-table-pagination button {
|
|
226
|
+
border: 1px solid var(--global-divider-color);
|
|
227
|
+
background-color: transparent;
|
|
228
|
+
border-radius: 0.35rem;
|
|
229
|
+
color: var(--global-text-color);
|
|
230
|
+
cursor: pointer;
|
|
231
|
+
font-size: 0.85rem;
|
|
232
|
+
font-weight: 500;
|
|
233
|
+
line-height: 1;
|
|
234
|
+
min-width: 2rem;
|
|
235
|
+
padding: 0.38rem 0.56rem;
|
|
236
|
+
transition:
|
|
237
|
+
background-color 0.2s ease,
|
|
238
|
+
color 0.2s ease,
|
|
239
|
+
border-color 0.2s ease;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
.af-table-pagination button:hover:not(:disabled) {
|
|
243
|
+
background-color: var(--global-theme-color);
|
|
244
|
+
border-color: var(--global-theme-color);
|
|
245
|
+
color: var(--global-hover-text-color);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.af-table-pagination button:disabled {
|
|
249
|
+
cursor: default;
|
|
250
|
+
opacity: 0.45;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
table[data-toggle="table"].af-table-enhanced {
|
|
254
|
+
margin-bottom: 0;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
table[data-toggle="table"].af-table-enhanced thead th {
|
|
258
|
+
background-color: color-mix(in srgb, var(--global-theme-color) 12%, transparent);
|
|
259
|
+
border-bottom: 1px solid var(--global-divider-color);
|
|
260
|
+
color: var(--global-text-color);
|
|
261
|
+
font-weight: 600;
|
|
262
|
+
letter-spacing: 0.01em;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
table[data-toggle="table"].af-table-enhanced th.af-sortable {
|
|
266
|
+
cursor: pointer;
|
|
267
|
+
user-select: none;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
table[data-toggle="table"].af-table-enhanced th.af-sortable:hover {
|
|
271
|
+
color: var(--global-hover-color);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
table[data-toggle="table"].af-table-enhanced tbody tr.af-row-selected {
|
|
275
|
+
background-color: color-mix(in srgb, var(--global-theme-color) 12%, transparent);
|
|
276
|
+
}
|
|
277
|
+
|
|
180
278
|
// Table of Contents
|
|
181
279
|
|
|
182
|
-
|
|
280
|
+
#toc-sidebar {
|
|
281
|
+
z-index: 1;
|
|
183
282
|
top: 5rem;
|
|
283
|
+
padding-left: 0.35rem;
|
|
284
|
+
border-left: 1px solid var(--global-divider-color);
|
|
285
|
+
|
|
286
|
+
.toc-list {
|
|
287
|
+
list-style: none;
|
|
288
|
+
margin: 0;
|
|
289
|
+
padding-left: 0;
|
|
290
|
+
}
|
|
184
291
|
|
|
185
|
-
.
|
|
186
|
-
|
|
292
|
+
.toc-list .toc-list {
|
|
293
|
+
border-left: 1px solid color-mix(in srgb, var(--global-divider-color) 85%, transparent);
|
|
294
|
+
margin-top: 0.15rem;
|
|
295
|
+
margin-left: 0.2rem;
|
|
296
|
+
padding-left: 0.75rem;
|
|
187
297
|
}
|
|
188
298
|
|
|
189
|
-
.
|
|
299
|
+
.toc-link {
|
|
190
300
|
color: var(--global-text-color);
|
|
191
|
-
|
|
301
|
+
display: block;
|
|
302
|
+
font-size: 0.82rem;
|
|
303
|
+
line-height: 1.38;
|
|
304
|
+
margin: 0.12rem 0;
|
|
305
|
+
padding: 0.13rem 0.4rem 0.13rem 0.55rem;
|
|
306
|
+
border-left: 2px solid transparent;
|
|
307
|
+
transition:
|
|
308
|
+
border-color 0.2s ease,
|
|
309
|
+
color 0.2s ease,
|
|
310
|
+
transform 0.2s ease;
|
|
192
311
|
|
|
193
312
|
&:hover {
|
|
194
313
|
color: var(--global-hover-color);
|
|
195
314
|
border-left-color: var(--global-hover-color);
|
|
315
|
+
text-decoration: none;
|
|
316
|
+
transform: translateX(2px);
|
|
196
317
|
}
|
|
197
318
|
}
|
|
198
319
|
|
|
199
|
-
.
|
|
320
|
+
.toc-link.is-active-link {
|
|
200
321
|
color: var(--global-theme-color);
|
|
201
322
|
border-left-color: var(--global-theme-color);
|
|
202
|
-
font-
|
|
323
|
+
font-weight: 600;
|
|
324
|
+
transform: translateX(3px);
|
|
203
325
|
|
|
204
326
|
&:hover {
|
|
205
327
|
color: var(--global-hover-color);
|
|
@@ -211,7 +333,7 @@ nav[data-toggle="toc"] {
|
|
|
211
333
|
/* small screens */
|
|
212
334
|
@media (max-width: 576px) {
|
|
213
335
|
/* override stickyness so that the navigation does not follow scrolling */
|
|
214
|
-
|
|
336
|
+
#toc-sidebar {
|
|
215
337
|
visibility: hidden;
|
|
216
338
|
height: 0;
|
|
217
339
|
top: 0;
|
|
@@ -446,10 +568,6 @@ nav[data-toggle="toc"] {
|
|
|
446
568
|
z-index: 999;
|
|
447
569
|
}
|
|
448
570
|
|
|
449
|
-
#toc-sidebar {
|
|
450
|
-
z-index: 1;
|
|
451
|
-
}
|
|
452
|
-
|
|
453
571
|
.echarts {
|
|
454
572
|
height: 400px;
|
|
455
573
|
width: 100%;
|
|
@@ -574,18 +692,58 @@ figure.cover {
|
|
|
574
692
|
// Calendar
|
|
575
693
|
|
|
576
694
|
.calendar-toggle-btn {
|
|
577
|
-
|
|
578
|
-
color:
|
|
579
|
-
|
|
580
|
-
border:
|
|
581
|
-
|
|
582
|
-
font-size: 1em;
|
|
695
|
+
align-items: center;
|
|
696
|
+
background-color: transparent;
|
|
697
|
+
border: 1px solid var(--global-theme-color);
|
|
698
|
+
border-radius: 999px;
|
|
699
|
+
color: var(--global-theme-color);
|
|
583
700
|
cursor: pointer;
|
|
584
|
-
|
|
701
|
+
display: inline-flex;
|
|
702
|
+
font-size: 0.92rem;
|
|
703
|
+
font-weight: 600;
|
|
704
|
+
gap: 0.25rem;
|
|
705
|
+
padding: 0.45rem 0.95rem;
|
|
706
|
+
transition:
|
|
707
|
+
background-color 0.2s ease,
|
|
708
|
+
color 0.2s ease,
|
|
709
|
+
box-shadow 0.2s ease,
|
|
710
|
+
transform 0.16s ease;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
.calendar-toggle-btn:focus-visible {
|
|
714
|
+
outline: 2px solid color-mix(in srgb, var(--global-theme-color) 45%, transparent);
|
|
715
|
+
outline-offset: 2px;
|
|
585
716
|
}
|
|
586
717
|
|
|
587
718
|
.calendar-toggle-btn:hover {
|
|
588
|
-
background-color:
|
|
719
|
+
background-color: color-mix(in srgb, var(--global-theme-color) 14%, transparent);
|
|
720
|
+
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.09);
|
|
721
|
+
color: var(--global-theme-color);
|
|
722
|
+
transform: translateY(-1px);
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
.calendar-toggle-btn:active {
|
|
726
|
+
transform: translateY(0);
|
|
727
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
.calendar-toggle-btn[aria-pressed="true"],
|
|
731
|
+
.calendar-toggle-btn.is-open {
|
|
732
|
+
background-color: var(--global-theme-color);
|
|
733
|
+
color: var(--global-hover-text-color);
|
|
734
|
+
border-color: var(--global-theme-color);
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
.calendar-toggle-btn[aria-pressed="true"]:hover,
|
|
738
|
+
.calendar-toggle-btn.is-open:hover {
|
|
739
|
+
background-color: var(--global-hover-color);
|
|
740
|
+
border-color: var(--global-hover-color);
|
|
741
|
+
color: var(--global-hover-text-color);
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
.calendar-toggle-btn.btn-sm {
|
|
745
|
+
cursor: pointer;
|
|
746
|
+
font-size: 0.88rem;
|
|
589
747
|
}
|
|
590
748
|
|
|
591
749
|
.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.4
|
|
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
|