@okjavis/nodebb-theme-javis 3.0.0 → 3.0.2
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.
- package/package.json +1 -1
- package/scss/_base.scss +114 -0
- package/scss/_cards.scss +22 -69
- package/scss/_composer.scss +140 -41
- package/scss/_feed.scss +121 -64
- package/scss/_header.scss +12 -9
- package/scss/_sidebar-user.scss +25 -5
- package/scss/_sidebar.scss +128 -74
- package/static/lib/theme.js +49 -9
- package/templates/feed.tpl +148 -0
- package/templates/partials/header/brand.tpl +53 -0
- package/templates/partials/topics_list.tpl +11 -20
- package/templates/category.tpl +0 -95
package/scss/_sidebar.scss
CHANGED
|
@@ -6,13 +6,13 @@
|
|
|
6
6
|
// ===========================================================
|
|
7
7
|
// JAVIS SIDEBAR LOGO
|
|
8
8
|
// ===========================================================
|
|
9
|
-
// Height should match the sticky header (
|
|
9
|
+
// Height should match the sticky header (56px) for visual alignment
|
|
10
10
|
.javis-sidebar-logo {
|
|
11
11
|
display: flex;
|
|
12
12
|
align-items: center;
|
|
13
13
|
justify-content: center;
|
|
14
|
-
height:
|
|
15
|
-
min-height:
|
|
14
|
+
height: 56px; // Match header height (8px + 36px + 8px + border)
|
|
15
|
+
min-height: 56px;
|
|
16
16
|
padding: 0 $jv-space-4; // Only horizontal padding
|
|
17
17
|
margin-bottom: $jv-space-3; // 12px space below border before nav items
|
|
18
18
|
border-bottom: 1px solid $jv-border-subtle;
|
|
@@ -26,17 +26,17 @@
|
|
|
26
26
|
text-decoration: none;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
// Icon logo (collapsed state) -
|
|
29
|
+
// Icon logo (collapsed state) - 36px for better visibility
|
|
30
30
|
.javis-logo-icon {
|
|
31
|
-
width:
|
|
32
|
-
height:
|
|
31
|
+
width: 36px;
|
|
32
|
+
height: 36px;
|
|
33
33
|
object-fit: contain;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
// Full logo (expanded state) - max 140px width,
|
|
36
|
+
// Full logo (expanded state) - max 140px width, 36px height
|
|
37
37
|
.javis-logo-full {
|
|
38
38
|
max-width: 140px;
|
|
39
|
-
height:
|
|
39
|
+
height: 36px;
|
|
40
40
|
object-fit: contain;
|
|
41
41
|
}
|
|
42
42
|
}
|
|
@@ -50,6 +50,7 @@
|
|
|
50
50
|
// ===========================================================
|
|
51
51
|
// LEFT SIDEBAR (Main Navigation)
|
|
52
52
|
// ===========================================================
|
|
53
|
+
// Reddit-style wider sidebar (200px) with expanded state as default
|
|
53
54
|
|
|
54
55
|
// Override Bootstrap's bg-light class on sidebar
|
|
55
56
|
.sidebar.bg-light,
|
|
@@ -59,9 +60,18 @@ nav.sidebar.bg-light {
|
|
|
59
60
|
|
|
60
61
|
.sidebar {
|
|
61
62
|
background: $jv-surface !important; // Pure white - cohesive with header
|
|
62
|
-
border-right:
|
|
63
|
+
border-right: none !important; // Clean look - no vertical divider
|
|
63
64
|
padding-top: 0 !important; // Remove top padding so logo sits at top edge
|
|
64
65
|
|
|
66
|
+
// Desktop: Use 270px width when expanded (Reddit-style)
|
|
67
|
+
@media (min-width: 992px) {
|
|
68
|
+
&.open {
|
|
69
|
+
min-width: 270px !important;
|
|
70
|
+
max-width: 270px !important;
|
|
71
|
+
width: 270px !important;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
65
75
|
// Nav items - proper spacing between items
|
|
66
76
|
.nav-item {
|
|
67
77
|
margin-bottom: $jv-space-2; // 4px between nav items - compact like Slack/Discord
|
|
@@ -498,7 +508,8 @@ div[data-widget-area] {
|
|
|
498
508
|
// RIGHT SIDEBAR WIDGET AREA (Reddit-style)
|
|
499
509
|
// ===========================================================
|
|
500
510
|
div[data-widget-area="right"],
|
|
501
|
-
.col-lg-3.col-sm-12
|
|
511
|
+
div[data-widget-area="right"].col-lg-3.col-sm-12,
|
|
512
|
+
.col-lg-3.col-sm-12[data-widget-area="right"] {
|
|
502
513
|
display: flex;
|
|
503
514
|
flex-direction: column;
|
|
504
515
|
|
|
@@ -506,33 +517,49 @@ div[data-widget-area="right"],
|
|
|
506
517
|
position: sticky;
|
|
507
518
|
top: 72px; // Adjust based on your header height
|
|
508
519
|
align-self: flex-start;
|
|
509
|
-
max-height: calc(100vh - 100px); // Account for header + padding
|
|
510
|
-
overflow-y: auto; // Allow scrolling if content is too tall
|
|
511
|
-
|
|
512
|
-
// Smooth scrollbar styling (webkit browsers)
|
|
513
|
-
&::-webkit-scrollbar {
|
|
514
|
-
width: 6px;
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
&::-webkit-scrollbar-track {
|
|
518
|
-
background: transparent;
|
|
519
|
-
}
|
|
520
520
|
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
&:hover {
|
|
526
|
-
background: rgba(0, 0, 0, 0.25);
|
|
527
|
-
}
|
|
528
|
-
}
|
|
521
|
+
// CRITICAL: Remove all overflow/scroll from sidebar and ALL descendants
|
|
522
|
+
overflow: visible !important;
|
|
523
|
+
max-height: none !important;
|
|
529
524
|
|
|
530
525
|
// Widget card container
|
|
531
526
|
> div {
|
|
532
527
|
background: $jv-surface;
|
|
533
528
|
border: 1px solid $jv-border-subtle;
|
|
534
529
|
border-radius: $jv-radius-lg;
|
|
535
|
-
overflow: hidden;
|
|
530
|
+
overflow: hidden; // Hide overflow on card itself
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// Trending Now widget - needs scrollable content
|
|
534
|
+
> div:has(#recent_posts),
|
|
535
|
+
> div:has(ul[id*="recent"]),
|
|
536
|
+
> div:has(.widget-posts) {
|
|
537
|
+
overflow: hidden !important; // Card clips content
|
|
538
|
+
|
|
539
|
+
// Inner scrollable container
|
|
540
|
+
> div {
|
|
541
|
+
max-height: 500px !important;
|
|
542
|
+
overflow-y: auto !important;
|
|
543
|
+
overflow-x: hidden !important;
|
|
544
|
+
|
|
545
|
+
// Scrollbar styling
|
|
546
|
+
&::-webkit-scrollbar {
|
|
547
|
+
width: 6px;
|
|
548
|
+
}
|
|
549
|
+
&::-webkit-scrollbar-track {
|
|
550
|
+
background: rgba(0, 0, 0, 0.03);
|
|
551
|
+
border-radius: 3px;
|
|
552
|
+
}
|
|
553
|
+
&::-webkit-scrollbar-thumb {
|
|
554
|
+
background: rgba(0, 0, 0, 0.15);
|
|
555
|
+
border-radius: 3px;
|
|
556
|
+
&:hover {
|
|
557
|
+
background: rgba(0, 0, 0, 0.25);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
scrollbar-width: thin;
|
|
561
|
+
scrollbar-color: rgba(0, 0, 0, 0.15) rgba(0, 0, 0, 0.03);
|
|
562
|
+
}
|
|
536
563
|
}
|
|
537
564
|
|
|
538
565
|
// Widget header/title - clean and simple
|
|
@@ -542,14 +569,19 @@ div[data-widget-area="right"],
|
|
|
542
569
|
text-transform: uppercase;
|
|
543
570
|
letter-spacing: 0.5px;
|
|
544
571
|
color: $jv-text-soft;
|
|
545
|
-
padding: $jv-space-4 $jv-space-
|
|
572
|
+
padding: $jv-space-4 0 $jv-space-3 0 !important; // top right bottom left - no horizontal padding (body handles it)
|
|
546
573
|
margin: 0 !important;
|
|
547
574
|
border-bottom: none !important; // Removed border
|
|
548
575
|
}
|
|
549
576
|
|
|
577
|
+
// Remove hr dividers after widget headers
|
|
578
|
+
hr {
|
|
579
|
+
display: none !important;
|
|
580
|
+
}
|
|
581
|
+
|
|
550
582
|
// Add spacing between widgets
|
|
551
583
|
> div + div {
|
|
552
|
-
margin-top: $jv-space-
|
|
584
|
+
margin-top: $jv-space-3; // Reduced from 16px to 12px
|
|
553
585
|
}
|
|
554
586
|
}
|
|
555
587
|
|
|
@@ -563,30 +595,35 @@ div[data-widget-area="right"] {
|
|
|
563
595
|
border: none !important;
|
|
564
596
|
border-radius: 0 !important;
|
|
565
597
|
overflow: visible !important;
|
|
566
|
-
|
|
567
|
-
//
|
|
568
|
-
h5 {
|
|
569
|
-
padding: $jv-space-3 0 !important;
|
|
570
|
-
border-bottom: none !important;
|
|
571
|
-
}
|
|
598
|
+
padding: 0 $jv-space-4 !important; // Horizontal padding for alignment
|
|
599
|
+
margin-bottom: 0 !important; // Remove extra margin
|
|
572
600
|
}
|
|
573
601
|
|
|
574
602
|
.popular-tags {
|
|
575
603
|
padding: 0 !important;
|
|
576
|
-
margin: 0;
|
|
604
|
+
margin: 0 !important; // Reset all margins
|
|
605
|
+
|
|
606
|
+
// Override Bootstrap mb-3 class
|
|
607
|
+
&.mb-3 {
|
|
608
|
+
margin-bottom: $jv-space-2 !important; // Space between widgets (reduced to 8px)
|
|
609
|
+
}
|
|
577
610
|
}
|
|
578
611
|
|
|
579
612
|
// Override Bootstrap's grid - make it a flowing wrap layout
|
|
580
|
-
.tag-list
|
|
613
|
+
.tag-list,
|
|
614
|
+
.tag-list.row {
|
|
581
615
|
display: flex !important;
|
|
582
616
|
flex-wrap: wrap !important;
|
|
583
617
|
gap: $jv-space-2 !important; // 8px gap between pills
|
|
584
618
|
padding: 0 !important;
|
|
585
619
|
margin: 0 !important;
|
|
620
|
+
margin-left: -$jv-space-3 !important; // Offset to align first pill's text with header (compensate for pill padding)
|
|
621
|
+
margin-bottom: $jv-space-2 !important; // Space between widgets (8px)
|
|
586
622
|
|
|
587
623
|
// Reset Bootstrap row/column styles
|
|
588
624
|
&.row {
|
|
589
|
-
margin:
|
|
625
|
+
margin-left: -$jv-space-3 !important;
|
|
626
|
+
margin-bottom: $jv-space-2 !important;
|
|
590
627
|
display: flex !important;
|
|
591
628
|
flex-wrap: wrap !important;
|
|
592
629
|
}
|
|
@@ -672,43 +709,13 @@ div[data-widget-area="right"] {
|
|
|
672
709
|
}
|
|
673
710
|
|
|
674
711
|
// ===========================================================
|
|
675
|
-
// TRENDING POSTS WIDGET - Widget Essentials
|
|
712
|
+
// TRENDING POSTS WIDGET - Widget Essentials / Recent Posts
|
|
676
713
|
// ===========================================================
|
|
677
714
|
|
|
678
|
-
//
|
|
679
|
-
.overflow-hidden {
|
|
680
|
-
overflow: visible !important;
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
// The wrapper div after h5 (Trending Posts header) - make it scrollable
|
|
684
|
-
h5 + div {
|
|
685
|
-
max-height: 420px;
|
|
686
|
-
overflow-y: auto !important;
|
|
687
|
-
overflow-x: hidden !important;
|
|
688
|
-
|
|
689
|
-
// Scrollbar styling
|
|
690
|
-
&::-webkit-scrollbar {
|
|
691
|
-
width: 6px;
|
|
692
|
-
}
|
|
693
|
-
&::-webkit-scrollbar-track {
|
|
694
|
-
background: transparent;
|
|
695
|
-
}
|
|
696
|
-
&::-webkit-scrollbar-thumb {
|
|
697
|
-
background: rgba(0, 0, 0, 0.15);
|
|
698
|
-
border-radius: 3px;
|
|
699
|
-
&:hover {
|
|
700
|
-
background: rgba(0, 0, 0, 0.25);
|
|
701
|
-
}
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
// Recent posts container - no scroll here, parent wrapper handles it
|
|
715
|
+
// Recent posts container styling
|
|
706
716
|
#recent_posts,
|
|
707
717
|
ul[id*="recent"] {
|
|
708
|
-
|
|
709
|
-
overflow: visible !important;
|
|
710
|
-
padding: $jv-space-3 !important;
|
|
711
|
-
padding-bottom: $jv-space-5 !important;
|
|
718
|
+
padding: $jv-space-3 $jv-space-4 $jv-space-4 $jv-space-4 !important;
|
|
712
719
|
margin: 0;
|
|
713
720
|
list-style: none;
|
|
714
721
|
display: flex !important;
|
|
@@ -829,4 +836,51 @@ div[data-widget-area="right"] {
|
|
|
829
836
|
}
|
|
830
837
|
}
|
|
831
838
|
}
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
// ===========================================================
|
|
842
|
+
// CRITICAL: Override ALL overflow rules on right sidebar
|
|
843
|
+
// This MUST be at the end to have highest specificity
|
|
844
|
+
// The selector [data-widget-area=right] gets overflow-y:auto from somewhere
|
|
845
|
+
// ===========================================================
|
|
846
|
+
|
|
847
|
+
// Override the base selector that has overflow-y: auto
|
|
848
|
+
[data-widget-area="right"] {
|
|
849
|
+
overflow: visible !important;
|
|
850
|
+
overflow-y: visible !important;
|
|
851
|
+
overflow-x: visible !important;
|
|
852
|
+
max-height: none !important;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
// More specific selectors to ensure override
|
|
856
|
+
div[data-widget-area="right"],
|
|
857
|
+
.col-lg-3[data-widget-area="right"],
|
|
858
|
+
.col-sm-12[data-widget-area="right"] {
|
|
859
|
+
overflow: visible !important;
|
|
860
|
+
overflow-y: visible !important;
|
|
861
|
+
overflow-x: visible !important;
|
|
862
|
+
max-height: none !important;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
// Override Bootstrap's overflow-hidden utility
|
|
866
|
+
div[data-widget-area="right"] .overflow-hidden,
|
|
867
|
+
[data-widget-area="right"] .overflow-hidden {
|
|
868
|
+
overflow: visible !important;
|
|
869
|
+
overflow-x: visible !important;
|
|
870
|
+
overflow-y: visible !important;
|
|
871
|
+
max-height: none !important;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
// Hide any scrollbar that might appear (as backup)
|
|
875
|
+
[data-widget-area="right"]::-webkit-scrollbar,
|
|
876
|
+
div[data-widget-area="right"]::-webkit-scrollbar {
|
|
877
|
+
display: none !important;
|
|
878
|
+
width: 0 !important;
|
|
879
|
+
height: 0 !important;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
[data-widget-area="right"],
|
|
883
|
+
div[data-widget-area="right"] {
|
|
884
|
+
scrollbar-width: none !important;
|
|
885
|
+
-ms-overflow-style: none !important;
|
|
832
886
|
}
|
package/static/lib/theme.js
CHANGED
|
@@ -12,9 +12,12 @@
|
|
|
12
12
|
$(document).ready(function() {
|
|
13
13
|
console.log('JAVIS Community Theme initialized');
|
|
14
14
|
|
|
15
|
-
//
|
|
15
|
+
// Initialize sidebar toggle handler
|
|
16
16
|
initSidebarToggle();
|
|
17
17
|
|
|
18
|
+
// Auto-expand sidebar on desktop (Reddit-style default)
|
|
19
|
+
initSidebarAutoExpand();
|
|
20
|
+
|
|
18
21
|
// Initialize image carousels for posts with multiple images
|
|
19
22
|
initPostImageCarousels();
|
|
20
23
|
|
|
@@ -198,30 +201,33 @@
|
|
|
198
201
|
console.log('JAVIS: Created carousel with ' + $images.length + ' images');
|
|
199
202
|
}
|
|
200
203
|
|
|
204
|
+
/**
|
|
205
|
+
* Initialize sidebar toggle click handler
|
|
206
|
+
* Ensures the sidebar can be toggled and saves user preference
|
|
207
|
+
*/
|
|
201
208
|
function initSidebarToggle() {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
if (!toggleEl.length) {
|
|
209
|
+
var $toggle = $('[component="sidebar/toggle"]');
|
|
210
|
+
if (!$toggle.length) {
|
|
205
211
|
console.log('JAVIS: Sidebar toggle element not found');
|
|
206
212
|
return;
|
|
207
213
|
}
|
|
208
214
|
|
|
209
215
|
// Remove any existing handlers to avoid duplicates, then add our handler
|
|
210
|
-
|
|
216
|
+
$toggle.off('click.javis').on('click.javis', function(e) {
|
|
211
217
|
e.preventDefault();
|
|
212
218
|
e.stopPropagation();
|
|
213
219
|
|
|
214
|
-
var
|
|
215
|
-
|
|
220
|
+
var $sidebar = $('.sidebar-left');
|
|
221
|
+
$sidebar.toggleClass('open');
|
|
216
222
|
|
|
217
|
-
console.log('JAVIS: Sidebar toggled, open:',
|
|
223
|
+
console.log('JAVIS: Sidebar toggled, open:', $sidebar.hasClass('open'));
|
|
218
224
|
|
|
219
225
|
// Save preference if user is logged in
|
|
220
226
|
if (typeof app !== 'undefined' && app.user && app.user.uid) {
|
|
221
227
|
require(['api'], function(api) {
|
|
222
228
|
api.put('/users/' + app.user.uid + '/settings', {
|
|
223
229
|
settings: {
|
|
224
|
-
openSidebars:
|
|
230
|
+
openSidebars: $sidebar.hasClass('open') ? 'on' : 'off',
|
|
225
231
|
},
|
|
226
232
|
}).catch(function(err) {
|
|
227
233
|
console.warn('JAVIS: Could not save sidebar preference', err);
|
|
@@ -235,6 +241,40 @@
|
|
|
235
241
|
console.log('JAVIS: Sidebar toggle initialized');
|
|
236
242
|
}
|
|
237
243
|
|
|
244
|
+
/**
|
|
245
|
+
* Auto-expand sidebar on desktop (Reddit-style default)
|
|
246
|
+
* The sidebar will be expanded by default on desktop, but users can still toggle it.
|
|
247
|
+
* Their preference is saved and respected on subsequent visits.
|
|
248
|
+
*/
|
|
249
|
+
function initSidebarAutoExpand() {
|
|
250
|
+
// Only run on desktop (min-width: 992px)
|
|
251
|
+
if ($(window).width() < 992) {
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
var $sidebar = $('.sidebar-left');
|
|
256
|
+
if (!$sidebar.length) {
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Check if user has a saved preference (via config.theme.openSidebars)
|
|
261
|
+
// If they've explicitly closed the sidebar before, respect that
|
|
262
|
+
var hasUserPreference = typeof config !== 'undefined' &&
|
|
263
|
+
config.theme &&
|
|
264
|
+
typeof config.theme.openSidebars !== 'undefined';
|
|
265
|
+
|
|
266
|
+
// If sidebar is not already open and user hasn't explicitly closed it, expand it
|
|
267
|
+
if (!$sidebar.hasClass('open')) {
|
|
268
|
+
// If no user preference set, auto-expand (first-time visitors)
|
|
269
|
+
// If user preference is 'on', also expand
|
|
270
|
+
if (!hasUserPreference || config.theme.openSidebars === 'on' || config.theme.openSidebars === true) {
|
|
271
|
+
$sidebar.addClass('open');
|
|
272
|
+
$(window).trigger('action:sidebar.toggle');
|
|
273
|
+
console.log('JAVIS: Auto-expanded sidebar (Reddit-style default)');
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
238
278
|
/**
|
|
239
279
|
* Fix the bookmark alert bug - NodeBB shows "Click here to return to last read post"
|
|
240
280
|
* even when the bookmark position isn't meaningfully ahead of the current position.
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
JAVIS Community Theme - Feed Page
|
|
3
|
+
Overrides nodebb-plugin-feed/public/templates/feed.tpl
|
|
4
|
+
Structure: Content first, Image second, Action bar third (separated)
|
|
5
|
+
-->
|
|
6
|
+
<div data-widget-area="header">
|
|
7
|
+
{{{each widgets.header}}}
|
|
8
|
+
{{widgets.header.html}}
|
|
9
|
+
{{{end}}}
|
|
10
|
+
</div>
|
|
11
|
+
<style>.feed .post-body .content > p:last-child { margin-bottom: 0px; }</style>
|
|
12
|
+
<div class="feed">
|
|
13
|
+
<div class="row">
|
|
14
|
+
<div data-widget-area="left" class="col-lg-3 col-sm-12 {{{ if !widgets.left.length }}}hidden{{{ end }}}">
|
|
15
|
+
{{{each widgets.left}}}
|
|
16
|
+
{{widgets.left.html}}
|
|
17
|
+
{{{end}}}
|
|
18
|
+
</div>
|
|
19
|
+
{{{ if ((widgets.left.length && widgets.right.length) || (!widgets.left.length && !widgets.right.length))}}}
|
|
20
|
+
<div class="col-lg-6 col-sm-12 mx-auto">
|
|
21
|
+
{{{ end }}}
|
|
22
|
+
{{{ if (widgets.left.length && !widgets.right.length) }}}
|
|
23
|
+
<div class="col-lg-6 col-sm-12 me-auto">
|
|
24
|
+
{{{ end }}}
|
|
25
|
+
{{{ if (!widgets.left.length && widgets.right.length) }}}
|
|
26
|
+
<div class="col-lg-6 col-sm-12 ms-auto">
|
|
27
|
+
{{{ end }}}
|
|
28
|
+
|
|
29
|
+
<div class="d-flex justify-content-between py-2 mb-2 gap-1">
|
|
30
|
+
{{{ if canPost }}}
|
|
31
|
+
<button id="new_topic" class="btn btn-primary btn-sm">[[category:new-topic-button]]</button>
|
|
32
|
+
{{{ end }}}
|
|
33
|
+
{{{ if (!loggedIn && !canPost) }}}
|
|
34
|
+
<a href="{config.relative_path}/login" class="btn btn-primary btn-sm">[[category:guest-login-post]]</a>
|
|
35
|
+
{{{ end }}}
|
|
36
|
+
|
|
37
|
+
<div class="d-flex justify-content-end gap-1">
|
|
38
|
+
<!-- IMPORT partials/category/filter-dropdown-right.tpl -->
|
|
39
|
+
|
|
40
|
+
<div id="options-dropdown" class="btn-group dropdown dropdown-right bottom-sheet">
|
|
41
|
+
<button type="button" class="btn btn-ghost btn-sm d-flex align-items-center gap-2 ff-secondary dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
|
42
|
+
<i class="fa fa-fw fa-gear text-primary"></i>
|
|
43
|
+
</button>
|
|
44
|
+
<ul class="dropdown-menu p-1 text-sm" role="menu">
|
|
45
|
+
<li class="py-1 px-3">
|
|
46
|
+
<div class="form-check form-switch d-flex px-0 align-items-center justify-content-between gap-3">
|
|
47
|
+
<label class="form-check-label text-nowrap" for="showAllPosts">[[feed:show-all-posts]]</label>
|
|
48
|
+
<input class="form-check-input float-none m-0 pointer" type="checkbox" role="switch" id="showAllPosts" {{{ if showAllPosts }}}checked{{{ end }}}>
|
|
49
|
+
</div>
|
|
50
|
+
</li>
|
|
51
|
+
{{{ if loggedIn }}}
|
|
52
|
+
<li class="py-1 px-3">
|
|
53
|
+
<div class="form-check form-switch d-flex px-0 align-items-center justify-content-between gap-3">
|
|
54
|
+
<label class="form-check-label text-nowrap" for="showFollowedUsers">[[feed:followed-users-only]]</label>
|
|
55
|
+
<input class="form-check-input float-none m-0 pointer" type="checkbox" role="switch" id="showFollowedUsers" {{{ if showFollowed }}}checked{{{ end }}}>
|
|
56
|
+
</div>
|
|
57
|
+
</li>
|
|
58
|
+
{{{ end }}}
|
|
59
|
+
</ul>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
|
|
64
|
+
{{{ if !posts.length }}}
|
|
65
|
+
<div class="alert alert-warning text-center">[[feed:no-posts-found]] {{{ if !following.length }}}[[feed:are-you-following-anyone]] {{{ end }}}</div>
|
|
66
|
+
{{{ end }}}
|
|
67
|
+
|
|
68
|
+
<ul component="posts" class="list-unstyled" data-nextstart="{nextStart}">
|
|
69
|
+
{{{ each posts }}}
|
|
70
|
+
<li component="post" class="shadow-sm mb-3 rounded-2 border posts-list-item {{{ if ./deleted }}} deleted{{{ else }}}{{{ if ./topic.deleted }}} deleted{{{ end }}}{{{ end }}}{{{ if ./topic.scheduled }}} scheduled{{{ end }}}" data-pid="{./pid}" data-uid="{./uid}">
|
|
71
|
+
|
|
72
|
+
<!-- 1. CONTENT (first) -->
|
|
73
|
+
<div class="d-flex gap-2 p-3">
|
|
74
|
+
<div class="d-none d-lg-block">
|
|
75
|
+
<a class="lh-1 text-decoration-none" href="{config.relative_path}/user/{./user.userslug}">{buildAvatar(./user, "40px", true, "not-responsive")}</a>
|
|
76
|
+
</div>
|
|
77
|
+
<div class="post-body d-flex flex-column gap-2 flex-grow-1 hover-parent" style="min-width: 0px;">
|
|
78
|
+
{{{ if ./isMainPost }}}
|
|
79
|
+
<a class="lh-1 topic-title fw-semibold fs-5 text-reset text-break d-block" href="{config.relative_path}/topic/{./topic.slug}">
|
|
80
|
+
{./topic.title}
|
|
81
|
+
</a>
|
|
82
|
+
{{{ end }}}
|
|
83
|
+
|
|
84
|
+
<div class="d-flex gap-1 post-info text-sm align-items-center">
|
|
85
|
+
<div class="post-author d-flex align-items-center gap-1">
|
|
86
|
+
<a class="d-inline d-lg-none lh-1 text-decoration-none" href="{config.relative_path}/user/{./user.userslug}">{buildAvatar(./user, "16px", true, "not-responsive")}</a>
|
|
87
|
+
<a class="lh-normal fw-semibold text-nowrap" href="{config.relative_path}/user/{./user.userslug}">{./user.displayname}</a>
|
|
88
|
+
</div>
|
|
89
|
+
{{{ if !./isMainPost}}}{./repliedString}{{{ else }}}<span class="timeago text-muted lh-normal" title="{./timestampISO}"></span>{{{ end}}}
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
<div component="post/content" class="content text-sm text-break position-relative truncate-post-content">
|
|
93
|
+
<a href="{config.relative_path}/post/{./pid}" class="stretched-link"></a>
|
|
94
|
+
{./content}
|
|
95
|
+
</div>
|
|
96
|
+
<div class="position-relative hover-visible">
|
|
97
|
+
<button component="show/more" class="btn btn-light btn-sm rounded-pill position-absolute start-50 translate-middle-x bottom-0 z-1 hidden ff-secondary">[[feed:see-more]]</button>
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
|
|
102
|
+
<!-- 2. IMAGE (second) -->
|
|
103
|
+
{{{ if (showThumbs && ./topic.thumbs.length)}}}
|
|
104
|
+
<div class="p-1 position-relative">
|
|
105
|
+
<div class="overflow-hidden rounded-1" style="max-height: 300px;">
|
|
106
|
+
<a href="{config.relative_path}/topic/{./topic.slug}">
|
|
107
|
+
<img class="w-100" src="{./topic.thumbs.0.url}">
|
|
108
|
+
</a>
|
|
109
|
+
</div>
|
|
110
|
+
|
|
111
|
+
<div class="position-absolute end-0 bottom-0 p-3 d-flex gap-2 align-items-center pe-none">
|
|
112
|
+
{{{ each ./topic.thumbs }}}
|
|
113
|
+
{{{ if (@index != 0) }}}
|
|
114
|
+
<img class="rounded-1" style="max-height: 64px; object-fit: contain;" src="{./url}">
|
|
115
|
+
{{{ end }}}
|
|
116
|
+
{{{ end }}}
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
119
|
+
{{{ end }}}
|
|
120
|
+
|
|
121
|
+
<!-- 3. ACTION BAR (third - separated from content) -->
|
|
122
|
+
<div class="feed-action-bar d-flex justify-content-between px-3 py-2 border-top">
|
|
123
|
+
<a href="{config.relative_path}/post/{{{ if ./topic.teaserPid }}}{./topic.teaserPid}{{{ else }}}{./pid}{{{ end }}}" class="btn btn-link btn-sm text-body {{{ if !./isMainPost }}}invisible{{{ end }}}"><i class="fa-fw fa-regular fa-message text-muted"></i> {humanReadableNumber(./topic.postcount)}</a>
|
|
124
|
+
|
|
125
|
+
<a href="#" data-pid="{./pid}" data-action="bookmark" data-bookmarked="{./bookmarked}" data-bookmarks="{./bookmarks}" class="btn btn-link btn-sm text-body"><i class="fa-fw fa-bookmark {{{ if ./bookmarked }}}fa text-primary{{{ else }}}fa-regular text-muted{{{ end }}}"></i> <span component="bookmark-count">{humanReadableNumber(./bookmarks)}</span></a>
|
|
126
|
+
|
|
127
|
+
<a href="#" data-pid="{./pid}" data-action="upvote" data-upvoted="{./upvoted}" data-upvotes="{./upvotes}" class="btn btn-link btn-sm text-body"><i class="fa-fw fa-heart {{{ if ./upvoted }}}fa text-danger{{{ else }}}fa-regular text-muted{{{ end }}}"></i> <span component="upvote-count">{humanReadableNumber(./upvotes)}</span></a>
|
|
128
|
+
|
|
129
|
+
<a href="#" data-pid="{./pid}" data-is-main="{./isMainPost}" data-tid="{./tid}" data-action="reply" class="btn btn-link btn-sm text-body"><i class="fa-fw fa fa-reply text-muted"></i> [[topic:reply]]</a>
|
|
130
|
+
</div>
|
|
131
|
+
</li>
|
|
132
|
+
{{{ end }}}
|
|
133
|
+
</ul>
|
|
134
|
+
</div>
|
|
135
|
+
|
|
136
|
+
<div data-widget-area="right" class="col-lg-3 col-sm-12 {{{ if !widgets.right.length }}}hidden{{{ end }}}">
|
|
137
|
+
{{{each widgets.right}}}
|
|
138
|
+
{{widgets.right.html}}
|
|
139
|
+
{{{end}}}
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
|
|
144
|
+
<div data-widget-area="footer">
|
|
145
|
+
{{{each widgets.footer}}}
|
|
146
|
+
{{widgets.footer.html}}
|
|
147
|
+
{{{end}}}
|
|
148
|
+
</div>
|
|
@@ -1,11 +1,64 @@
|
|
|
1
1
|
{{{ if widgets.brand-header.length }}}
|
|
2
2
|
<div class="container-lg px-md-4 brand-container">
|
|
3
3
|
<div class="col-12 d-flex border-bottom pb-3 {{{ if config.theme.centerHeaderElements }}}justify-content-center{{{ end }}}">
|
|
4
|
+
<!-- Search widget area (left/center) -->
|
|
4
5
|
<div data-widget-area="brand-header" class="flex-fill gap-3 p-2 align-self-center">
|
|
5
6
|
{{{each widgets.brand-header}}}
|
|
6
7
|
{{./html}}
|
|
7
8
|
{{{end}}}
|
|
8
9
|
</div>
|
|
10
|
+
|
|
11
|
+
<!-- Right side: Notifications -->
|
|
12
|
+
<div class="header-right-actions d-flex align-items-center gap-2 ms-auto">
|
|
13
|
+
{{{ if config.loggedIn }}}
|
|
14
|
+
<!-- Notifications dropdown (logged-in users) -->
|
|
15
|
+
<div class="dropdown" component="notifications">
|
|
16
|
+
<a data-bs-toggle="dropdown" href="#" role="button" class="nav-link d-flex align-items-center position-relative p-2 rounded-circle" aria-haspopup="true" aria-expanded="false" aria-label="[[global:header.notifications]]" style="width: 40px; height: 40px; justify-content: center;">
|
|
17
|
+
<span class="position-relative">
|
|
18
|
+
<i component="notifications/icon" class="fa fa-fw fa-lg {{{ if unreadCount.notification}}}fa-bell{{{ else }}}fa-bell-o{{{ end }}}" style="color: #6b7280;"></i>
|
|
19
|
+
<span component="notifications/count" class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger {{{ if !unreadCount.notification }}}hidden{{{ end }}}" style="font-size: 10px; min-width: 18px;">{unreadCount.notification}</span>
|
|
20
|
+
</span>
|
|
21
|
+
</a>
|
|
22
|
+
<ul class="notifications-dropdown dropdown-menu dropdown-menu-end p-1 shadow" role="menu" style="min-width: 320px;">
|
|
23
|
+
<li>
|
|
24
|
+
<div component="notifications/list" class="list-container notification-list overscroll-behavior-contain pe-1 ff-base ghost-scrollbar" style="max-height: 400px; overflow-y: auto;">
|
|
25
|
+
<div class="mb-2 p-1">
|
|
26
|
+
<div class="d-flex gap-1 justify-content-between">
|
|
27
|
+
<div class="d-flex gap-2 flex-grow-1 placeholder-wave">
|
|
28
|
+
<div class="placeholder rounded-circle" style="width: 32px; height: 32px;"></div>
|
|
29
|
+
<div class="flex-grow-1">
|
|
30
|
+
<div class="d-flex flex-column">
|
|
31
|
+
<div class="text-sm">
|
|
32
|
+
<span class="placeholder placeholder-sm col-4"></span>
|
|
33
|
+
<span class="placeholder placeholder-sm col-6"></span>
|
|
34
|
+
<span class="placeholder placeholder-sm col-7"></span>
|
|
35
|
+
</div>
|
|
36
|
+
<div class="text-xs">
|
|
37
|
+
<div class="placeholder placeholder-xs col-6"></div>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
</li>
|
|
46
|
+
<li class="dropdown-divider"></li>
|
|
47
|
+
<li>
|
|
48
|
+
<div class="d-flex justify-content-center gap-1 flex-wrap p-1">
|
|
49
|
+
<a role="button" href="#" class="btn btn-sm btn-light mark-all-read flex-fill text-nowrap text-truncate"><i class="fa fa-check-double"></i> [[notifications:mark-all-read]]</a>
|
|
50
|
+
<a class="btn btn-sm btn-primary flex-fill text-nowrap text-truncate" href="{relative_path}/notifications"><i class="fa fa-list"></i> [[notifications:see-all]]</a>
|
|
51
|
+
</div>
|
|
52
|
+
</li>
|
|
53
|
+
</ul>
|
|
54
|
+
</div>
|
|
55
|
+
{{{ else }}}
|
|
56
|
+
<!-- Notification icon for guests (redirects to login) -->
|
|
57
|
+
<a href="{relative_path}/login" class="nav-link d-flex align-items-center position-relative p-2 rounded-circle guest-notification-btn" aria-label="[[global:header.notifications]]" title="Sign in to view notifications" style="width: 40px; height: 40px; justify-content: center;">
|
|
58
|
+
<i class="fa fa-fw fa-lg fa-bell-o" style="color: #6b7280;"></i>
|
|
59
|
+
</a>
|
|
60
|
+
{{{ end }}}
|
|
61
|
+
</div>
|
|
9
62
|
</div>
|
|
10
63
|
</div>
|
|
11
64
|
{{{ end }}}
|
|
@@ -69,37 +69,28 @@
|
|
|
69
69
|
</div>
|
|
70
70
|
{{{ end }}}
|
|
71
71
|
|
|
72
|
-
<!--
|
|
72
|
+
<!-- Thumbnail Preview (if exists) -->
|
|
73
73
|
{{{ if ./thumbs.length }}}
|
|
74
|
-
<
|
|
75
|
-
<
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
<img src="{./thumbs.0.url}" alt="" loading="lazy" />
|
|
84
|
-
{{{ if ./thumbs.1 }}}
|
|
85
|
-
<span class="thumb-count">+{subtract(./thumbs.length, 1)}</span>
|
|
86
|
-
{{{ end }}}
|
|
87
|
-
</a>
|
|
88
|
-
</div>
|
|
89
|
-
{{{ else }}}
|
|
90
|
-
<!-- Teaser/Preview Content (no image) -->
|
|
74
|
+
<a class="topic-thumbnail" href="{config.relative_path}/topic/{./slug}{{{ if ./bookmark }}}/{./bookmark}{{{ end }}}">
|
|
75
|
+
<img src="{./thumbs.0.url}" alt="" loading="lazy" />
|
|
76
|
+
{{{ if ./thumbs.1 }}}
|
|
77
|
+
<span class="thumb-count">+{subtract(./thumbs.length, 1)}</span>
|
|
78
|
+
{{{ end }}}
|
|
79
|
+
</a>
|
|
80
|
+
{{{ end }}}
|
|
81
|
+
|
|
82
|
+
<!-- Teaser/Preview Content -->
|
|
91
83
|
{{{ if ./teaser.content }}}
|
|
92
84
|
<div class="topic-teaser">
|
|
93
85
|
{./teaser.content}
|
|
94
86
|
</div>
|
|
95
87
|
{{{ end }}}
|
|
96
|
-
{{{ end }}}
|
|
97
88
|
|
|
98
89
|
<!-- Action Bar -->
|
|
99
90
|
<div class="topic-actions">
|
|
100
91
|
<a href="{config.relative_path}/topic/{./slug}" class="action-btn">
|
|
101
92
|
<i class="fa-regular fa-comment"></i>
|
|
102
|
-
<span>{humanReadableNumber(./postcount, 0)}
|
|
93
|
+
<span>{humanReadableNumber(./postcount, 0)} Comments</span>
|
|
103
94
|
</a>
|
|
104
95
|
<button class="action-btn share-btn" data-url="{config.relative_path}/topic/{./slug}">
|
|
105
96
|
<i class="fa fa-share"></i>
|