Skip to content

Commit d93a6ad

Browse files
committed
feat: add jump-to-page dialog on pagination ellipsis
- Replace pagination windowing algorithm with a tighter 5-item window (curr-1 / curr / curr+1, clamped at edges) and hide-on-mobile for pages more than 1 step away from the current page - Ellipsis spans now carry the pagination-jump-trigger class and open a native <dialog> on click instead of being static decorations - Add <dialog id="pagination-jump-dialog"> with number input, Go button, and Enter-key hint; data-total / data-first-url / data-format-url attributes on <nav> give the JS all it needs to build the target URL - Add assets/ts/pagination.ts: setupPaginationJump() with Fisher-Yates backdrop-click and ESC handling, animated open/close via .closing class - Wire setupPaginationJump() into Stack.init() in main.ts - Extend pagination.scss with .hide-on-mobile, .pagination-jump-trigger hover style, and the full #pagination-jump-dialog styling + keyframe scale-up/scale-down animations - Add [pagination] i18n keys (jumpToPage / jump / pressEnter) to en / zh / zh-hant-tw / zh-hant-hk / ja
1 parent f13c478 commit d93a6ad

9 files changed

Lines changed: 493 additions & 92 deletions

File tree

Lines changed: 245 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,259 @@
11
.pagination {
2+
display: flex;
3+
@include card-style;
4+
overflow: hidden;
5+
6+
.hide-on-mobile {
7+
display: none;
8+
9+
@include respond(md) {
10+
display: flex;
11+
}
12+
}
13+
14+
.pagination-link {
15+
display: none;
16+
17+
@include respond(md) {
18+
display: flex;
19+
}
20+
}
21+
22+
.page-link {
23+
padding: 12px 0;
24+
margin: 0;
25+
border-radius: 0;
226
display: flex;
3-
@include card-style;
4-
overflow: hidden;
27+
align-items: center;
28+
justify-content: center;
29+
flex: 1 1 auto;
30+
min-width: 36px;
31+
color: var(--card-text-color-secondary);
32+
text-decoration: none;
533

6-
.page-link {
7-
padding: 16px 0;
8-
margin: 0;
9-
border-radius: 0;
34+
@include respond(md) {
35+
min-width: 48px;
36+
padding: 16px 0;
37+
}
38+
39+
&:first-child,
40+
&:last-child {
41+
flex: 0 0 40px;
42+
padding: 12px 8px;
43+
44+
@include respond(md) {
45+
flex: 0 0 48px;
46+
padding: 16px;
47+
}
48+
}
49+
50+
&:hover:not(.current):not(.disabled) {
51+
background-color: var(--card-background-selected);
52+
opacity: 0.8;
53+
}
54+
55+
&.current {
56+
background-color: var(--card-background-selected);
57+
color: var(--card-text-color-main);
58+
font-weight: bold;
59+
cursor: default;
60+
}
61+
62+
&.disabled {
63+
opacity: 0.3;
64+
cursor: not-allowed;
65+
pointer-events: none;
66+
color: var(--card-text-color-secondary);
67+
}
68+
69+
svg {
70+
width: 20px;
71+
height: 20px;
72+
}
73+
74+
&.pagination-jump-trigger {
75+
cursor: pointer;
76+
77+
&:hover {
78+
background-color: var(--card-background-selected);
79+
opacity: 0.8;
80+
}
81+
}
82+
}
83+
}
84+
85+
#pagination-jump-dialog {
86+
border: none;
87+
border-radius: var(--card-border-radius);
88+
background-color: var(--card-background);
89+
padding: 24px;
90+
box-shadow: var(--shadow-l2);
91+
color: var(--body-text-color);
92+
margin: auto; // Center dialog
93+
width: 100%;
94+
max-width: 320px;
95+
96+
&::backdrop {
97+
background-color: rgba(0, 0, 0, 0.4);
98+
backdrop-filter: blur(2px);
99+
opacity: 0;
100+
transition: opacity 0.3s ease;
101+
}
102+
103+
&[open] {
104+
animation: scale-up 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards;
105+
106+
&::backdrop {
107+
opacity: 1;
108+
}
109+
}
110+
111+
&.closing {
112+
animation: scale-down 0.2s ease forwards;
113+
114+
&::backdrop {
115+
opacity: 0;
116+
transition: opacity 0.2s ease;
117+
}
118+
}
119+
120+
.pagination-jump-form {
121+
display: flex;
122+
flex-direction: column;
123+
gap: 16px;
124+
125+
header {
126+
display: flex;
127+
align-items: baseline;
128+
justify-content: flex-start;
129+
gap: 8px;
130+
margin-bottom: 2px;
131+
132+
label {
10133
display: flex;
11-
align-items: center;
12-
justify-content: center;
13-
flex: 1 1 auto;
14-
min-width: 48px;
15-
color: var(--card-text-color-secondary);
16-
text-decoration: none;
17-
18-
&:first-child,
19-
&:last-child {
20-
flex: 0 0 48px;
21-
padding: 16px;
22-
}
134+
align-items: baseline;
135+
gap: 8px;
136+
cursor: pointer;
137+
}
138+
139+
.title {
140+
font-size: 1.8rem;
141+
font-weight: bold;
142+
color: var(--card-text-color-main);
143+
margin: 0;
144+
}
145+
146+
.subtitle {
147+
font-size: 1.4rem;
148+
color: var(--card-text-color-tertiary);
149+
}
150+
}
23151

24-
&:hover:not(.current):not(.disabled) {
25-
background-color: var(--card-background-selected);
26-
opacity: 0.8;
152+
.pagination-jump-input-group {
153+
display: flex;
154+
gap: 12px;
155+
align-items: stretch;
156+
157+
input[type="number"] {
158+
flex: 1;
159+
width: 100%;
160+
min-width: 0;
161+
padding: 0 16px;
162+
height: 44px; // Explicit height for perfect alignment
163+
border: 1px solid var(--card-separator-color);
164+
border-radius: 8px;
165+
background-color: var(--body-background);
166+
color: var(--card-text-color-main);
167+
font-size: 1.6rem;
168+
outline: none;
169+
transition: all 0.3s ease;
170+
171+
/* Hide spinner */
172+
-moz-appearance: textfield;
173+
&::-webkit-outer-spin-button,
174+
&::-webkit-inner-spin-button {
175+
-webkit-appearance: none;
176+
margin: 0;
27177
}
28178

29-
&.current {
30-
background-color: var(--card-background-selected);
31-
color: var(--card-text-color-main);
32-
font-weight: bold;
33-
cursor: default;
179+
&:focus {
180+
border-color: var(--accent-color);
181+
box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.05);
34182
}
183+
}
35184

36-
&.disabled {
37-
opacity: 0.3;
38-
cursor: not-allowed;
39-
pointer-events: none;
40-
color: var(--card-text-color-secondary);
185+
.btn-primary {
186+
flex-shrink: 0;
187+
display: inline-flex;
188+
align-items: center;
189+
justify-content: center;
190+
padding: 0 24px;
191+
height: 44px; // Match input height
192+
background-color: var(--accent-color);
193+
color: var(--accent-color-text);
194+
border: none;
195+
border-radius: 8px;
196+
font-size: 1.5rem;
197+
font-weight: bold;
198+
cursor: pointer;
199+
transition: all 0.2s ease;
200+
201+
&:hover {
202+
background-color: var(--accent-color-darker);
203+
transform: translateY(-1px);
204+
box-shadow: var(--shadow-l1);
41205
}
42206

43-
svg {
44-
width: 20px;
45-
height: 20px;
207+
&:active {
208+
transform: translateY(0);
209+
box-shadow: none;
46210
}
211+
}
212+
}
213+
214+
.pagination-jump-hint {
215+
display: flex;
216+
align-items: center;
217+
justify-content: flex-start;
218+
gap: 8px;
219+
font-size: 1.3rem;
220+
color: var(--card-text-color-tertiary);
221+
margin-top: -6px;
222+
223+
kbd {
224+
background-color: var(--body-background);
225+
border: 1px solid var(--card-separator-color);
226+
border-radius: 4px;
227+
padding: 3px 6px;
228+
font-family: var(--base-font-family);
229+
font-size: 1.1rem;
230+
box-shadow: 0 2px 0 var(--card-separator-color);
231+
color: var(--card-text-color-main);
232+
font-weight: 600;
233+
line-height: 1;
234+
}
235+
}
236+
}
237+
238+
@keyframes scale-up {
239+
from {
240+
opacity: 0;
241+
transform: scale(0.9);
242+
}
243+
to {
244+
opacity: 1;
245+
transform: scale(1);
246+
}
247+
}
248+
249+
@keyframes scale-down {
250+
from {
251+
opacity: 1;
252+
transform: scale(1);
253+
}
254+
to {
255+
opacity: 0;
256+
transform: scale(0.95);
47257
}
258+
}
48259
}

assets/ts/main.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import createElement from './createElement';
1010
import StackColorScheme from './colorScheme';
1111
import { setupScrollspy } from './scrollspy';
1212
import { setupSmoothAnchors } from './smoothAnchors';
13+
import { setupPaginationJump } from './pagination';
1314

1415
let Stack = {
1516
init: () => {
@@ -24,6 +25,8 @@ let Stack = {
2425
setupScrollspy();
2526
}
2627

28+
setupPaginationJump();
29+
2730
/**
2831
* Add copy button to code block
2932
*/

0 commit comments

Comments
 (0)