@pushword/js-helper 0.0.123 → 0.0.127
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 +12 -11
- package/src/ShowMore.js +324 -0
- package/src/alpine.js +5 -0
- package/src/app.css +8 -63
- package/src/app.js +5 -1
- package/src/helpers.js +9 -13
- package/src/tailwind.prose.scss +28 -28
- package/src/utility.css +62 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pushword/js-helper",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.127",
|
|
4
4
|
"description": "Pushword front end helpers. ",
|
|
5
5
|
"author": "Robin@PiedWeb <contact@piedweb.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -8,18 +8,20 @@
|
|
|
8
8
|
"prettier": "prettier ./src/*.{js,scss} --write"
|
|
9
9
|
},
|
|
10
10
|
"dependencies": {
|
|
11
|
-
"
|
|
12
|
-
"@tailwindcss/vite": "^4.1.16",
|
|
13
|
-
"vite-plugin-static-copy": "^3.1.4",
|
|
14
|
-
"vite-plugin-symfony": "^8.2.2",
|
|
15
|
-
"@tailwindcss/typography": "^0.5",
|
|
16
|
-
"tailwindcss-animated": "^2",
|
|
11
|
+
"140.css": "^1.0",
|
|
17
12
|
"@tailwindcss/forms": "^0.5",
|
|
18
|
-
"
|
|
13
|
+
"@tailwindcss/typography": "^0.5",
|
|
14
|
+
"@tailwindcss/vite": "^4.1",
|
|
15
|
+
"alpinejs": "^3.15.2",
|
|
19
16
|
"codemirror": "^6.0",
|
|
20
17
|
"core-js": "^3.38",
|
|
21
18
|
"easymde": "^2.18",
|
|
22
|
-
"glightbox": "^3.3"
|
|
19
|
+
"glightbox": "^3.3",
|
|
20
|
+
"tailwindcss-animated": "^2",
|
|
21
|
+
"vite": "^7.1.12",
|
|
22
|
+
"vite-plugin-compression2": "^2.3.1",
|
|
23
|
+
"vite-plugin-static-copy": "^3.1",
|
|
24
|
+
"vite-plugin-symfony": "^8.2"
|
|
23
25
|
},
|
|
24
26
|
"repository": {
|
|
25
27
|
"type": "git",
|
|
@@ -31,6 +33,5 @@
|
|
|
31
33
|
"bugs": {
|
|
32
34
|
"url": "https://github.com/Pushword/Pushword/issues"
|
|
33
35
|
},
|
|
34
|
-
"homepage": "https://pushword.piedweb.com"
|
|
35
|
-
"gitHead": "242ec54483dca1be93d7a62d8d554c9c08008c28"
|
|
36
|
+
"homepage": "https://pushword.piedweb.com"
|
|
36
37
|
}
|
package/src/ShowMore.js
ADDED
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ShowMore - Expand/collapse content blocks
|
|
3
|
+
*
|
|
4
|
+
* Features:
|
|
5
|
+
* - [x] Open/close show-more blocks
|
|
6
|
+
* 1. Page loads → blocks are fully visible (no max-height - best for SEO)
|
|
7
|
+
* 2. On scroll → `addClassForNormalUser` adds `max-h-[250px]` via `data-acinb`, collapsing them
|
|
8
|
+
* Exception → blocks previously opened by user (in localStorage) stay open
|
|
9
|
+
* - [x] Auto-open when URL contains hash pointing inside block
|
|
10
|
+
* - [x] Auto-open when page loads with scroll position (browser back/refresh)
|
|
11
|
+
* - [x] Auto-open on hash change (SPA navigation)
|
|
12
|
+
* - [x] Ctrl+F: Auto-open when browser finds text in collapsed block
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const STORAGE_KEY = 'showmore_opened'
|
|
16
|
+
|
|
17
|
+
const ShowMore = {
|
|
18
|
+
_initialized: false,
|
|
19
|
+
_scrollPos: 0,
|
|
20
|
+
_userClosed: new WeakSet(), // Track blocks manually closed by user
|
|
21
|
+
_openedIds: new Set(), // IDs of blocks user has ever opened
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Load opened block IDs from localStorage
|
|
25
|
+
*/
|
|
26
|
+
_loadOpenedIds() {
|
|
27
|
+
try {
|
|
28
|
+
const stored = localStorage.getItem(STORAGE_KEY)
|
|
29
|
+
if (stored) {
|
|
30
|
+
JSON.parse(stored).forEach((id) => this._openedIds.add(id))
|
|
31
|
+
}
|
|
32
|
+
} catch (e) {
|
|
33
|
+
// localStorage not available or corrupted
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Save opened block ID to localStorage
|
|
39
|
+
*/
|
|
40
|
+
_saveOpenedId(id) {
|
|
41
|
+
if (!id) return
|
|
42
|
+
this._openedIds.add(id)
|
|
43
|
+
this._persistOpenedIds()
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Remove closed block ID from localStorage
|
|
48
|
+
*/
|
|
49
|
+
_removeOpenedId(id) {
|
|
50
|
+
if (!id) return
|
|
51
|
+
this._openedIds.delete(id)
|
|
52
|
+
this._persistOpenedIds()
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Persist opened IDs to localStorage
|
|
57
|
+
*/
|
|
58
|
+
_persistOpenedIds() {
|
|
59
|
+
try {
|
|
60
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify([...this._openedIds]))
|
|
61
|
+
} catch (e) {
|
|
62
|
+
// localStorage not available
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Check if user has ever opened this block
|
|
68
|
+
*/
|
|
69
|
+
_hasUserOpened(wrapper) {
|
|
70
|
+
const input = wrapper.querySelector('input.show-hide-input')
|
|
71
|
+
return input && this._openedIds.has(input.id)
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Open a show-more block
|
|
76
|
+
* @param {HTMLElement} el - Element inside the .show-more wrapper (typically .show-more-btn)
|
|
77
|
+
* @param {boolean} auto - Whether this is an automatic open (not user-initiated)
|
|
78
|
+
*/
|
|
79
|
+
open(el, auto = false) {
|
|
80
|
+
const wrapper = el.closest('.show-more')
|
|
81
|
+
if (!wrapper) return
|
|
82
|
+
|
|
83
|
+
// Don't auto-open if user manually closed this block
|
|
84
|
+
if (auto && this._userClosed.has(wrapper)) return
|
|
85
|
+
|
|
86
|
+
const content = wrapper.children[1]
|
|
87
|
+
if (!content) return
|
|
88
|
+
|
|
89
|
+
this._scrollPos = wrapper.getBoundingClientRect().top + window.scrollY
|
|
90
|
+
content.classList.remove('overflow-hidden')
|
|
91
|
+
content.style.maxHeight = content.scrollHeight + 'px'
|
|
92
|
+
wrapper.dataset.showMoreOpen = 'true'
|
|
93
|
+
|
|
94
|
+
// Toggle checkbox to update arrow icon state
|
|
95
|
+
const checkbox = wrapper.querySelector('input.show-hide-input')
|
|
96
|
+
if (checkbox) {
|
|
97
|
+
checkbox.checked = true
|
|
98
|
+
// Save to localStorage when user manually opens (not auto)
|
|
99
|
+
if (!auto) {
|
|
100
|
+
this._saveOpenedId(checkbox.id)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Close a show-more block
|
|
107
|
+
* @param {HTMLElement} el - Element inside the .show-more wrapper
|
|
108
|
+
*/
|
|
109
|
+
close(el) {
|
|
110
|
+
const wrapper = el.closest('.show-more')
|
|
111
|
+
if (!wrapper) return
|
|
112
|
+
|
|
113
|
+
const content = wrapper.children[1]
|
|
114
|
+
if (!content) return
|
|
115
|
+
|
|
116
|
+
// Mark as user-closed to prevent auto-reopening
|
|
117
|
+
this._userClosed.add(wrapper)
|
|
118
|
+
|
|
119
|
+
content.classList.add('overflow-hidden')
|
|
120
|
+
content.style.maxHeight = '250px'
|
|
121
|
+
delete wrapper.dataset.showMoreOpen
|
|
122
|
+
|
|
123
|
+
// Toggle checkbox to update arrow icon state
|
|
124
|
+
const checkbox = wrapper.querySelector('input.show-hide-input')
|
|
125
|
+
if (checkbox) {
|
|
126
|
+
checkbox.checked = false
|
|
127
|
+
// Remove from localStorage when user closes
|
|
128
|
+
this._removeOpenedId(checkbox.id)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (this._scrollPos) {
|
|
132
|
+
window.scrollTo({ top: Math.max(0, this._scrollPos - 20), behavior: 'smooth' })
|
|
133
|
+
} else {
|
|
134
|
+
wrapper.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Check if block is open
|
|
140
|
+
* @param {HTMLElement} wrapper
|
|
141
|
+
* @returns {boolean}
|
|
142
|
+
*/
|
|
143
|
+
isOpen(wrapper) {
|
|
144
|
+
return wrapper.dataset.showMoreOpen === 'true'
|
|
145
|
+
},
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Open the show-more block containing the given element
|
|
149
|
+
* @param {HTMLElement} element - Element inside a show-more block
|
|
150
|
+
* @param {boolean} auto - Whether this is an automatic open
|
|
151
|
+
*/
|
|
152
|
+
openContaining(element, auto = true) {
|
|
153
|
+
if (!element) return
|
|
154
|
+
|
|
155
|
+
const wrapper = element.closest('.show-more')
|
|
156
|
+
if (!wrapper || this.isOpen(wrapper)) return
|
|
157
|
+
|
|
158
|
+
// Use the wrapper itself as fallback if btn not found yet (hidden)
|
|
159
|
+
const btn = wrapper.querySelector('.show-more-btn') || wrapper
|
|
160
|
+
this.open(btn, auto)
|
|
161
|
+
},
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Open show-more block containing the hash target and scroll to it
|
|
165
|
+
* @param {string} hash - The hash (with #) to navigate to
|
|
166
|
+
*/
|
|
167
|
+
scrollToHash(hash) {
|
|
168
|
+
if (!hash) return
|
|
169
|
+
|
|
170
|
+
try {
|
|
171
|
+
const target = document.querySelector(hash)
|
|
172
|
+
if (target) {
|
|
173
|
+
this.openContaining(target)
|
|
174
|
+
setTimeout(() => {
|
|
175
|
+
target.scrollIntoView({ behavior: 'smooth' })
|
|
176
|
+
}, 100)
|
|
177
|
+
}
|
|
178
|
+
} catch (e) {
|
|
179
|
+
// Invalid selector, ignore
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Check if a show-more block is currently collapsed
|
|
185
|
+
* @param {HTMLElement} wrapper - The .show-more wrapper element
|
|
186
|
+
* @returns {boolean}
|
|
187
|
+
*/
|
|
188
|
+
isCollapsed(wrapper) {
|
|
189
|
+
const content = wrapper.children[1]
|
|
190
|
+
return content && content.classList.contains('overflow-hidden')
|
|
191
|
+
},
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Open all visible collapsed show-more blocks
|
|
195
|
+
* Useful when page loads with scroll position
|
|
196
|
+
*/
|
|
197
|
+
openVisibleBlocks() {
|
|
198
|
+
document.querySelectorAll('.show-more').forEach((wrapper) => {
|
|
199
|
+
if (!this.isCollapsed(wrapper)) return
|
|
200
|
+
if (this._userClosed.has(wrapper)) return
|
|
201
|
+
// Only auto-open if user has previously opened this block
|
|
202
|
+
if (!this._hasUserOpened(wrapper)) return
|
|
203
|
+
|
|
204
|
+
const rect = wrapper.getBoundingClientRect()
|
|
205
|
+
// Check if block intersects with viewport (with some margin)
|
|
206
|
+
const isVisible = rect.top < window.innerHeight + 100 && rect.bottom > -100
|
|
207
|
+
|
|
208
|
+
if (isVisible) {
|
|
209
|
+
this.openContaining(wrapper, true)
|
|
210
|
+
}
|
|
211
|
+
})
|
|
212
|
+
},
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Initialize ShowMore functionality
|
|
216
|
+
* Safe to call multiple times - will only initialize once
|
|
217
|
+
*/
|
|
218
|
+
init() {
|
|
219
|
+
if (this._initialized) return
|
|
220
|
+
this._initialized = true
|
|
221
|
+
|
|
222
|
+
// Load previously opened block IDs from localStorage
|
|
223
|
+
this._loadOpenedIds()
|
|
224
|
+
|
|
225
|
+
// Open if URL has hash pointing inside a show-more block
|
|
226
|
+
if (location.hash) {
|
|
227
|
+
this.scrollToHash(location.hash)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Open if page loaded with scroll (e.g., browser back/refresh)
|
|
231
|
+
// Listen for scroll events to catch smooth scroll restoration
|
|
232
|
+
let scrollCheckCount = 0
|
|
233
|
+
const maxScrollChecks = 10
|
|
234
|
+
const checkScrollAndOpen = () => {
|
|
235
|
+
if (window.scrollY > 0) {
|
|
236
|
+
this.openVisibleBlocks()
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Immediate check
|
|
241
|
+
checkScrollAndOpen()
|
|
242
|
+
|
|
243
|
+
// Listen for scroll events (catches smooth scroll restoration)
|
|
244
|
+
const onScrollRestore = () => {
|
|
245
|
+
scrollCheckCount++
|
|
246
|
+
checkScrollAndOpen()
|
|
247
|
+
if (scrollCheckCount >= maxScrollChecks) {
|
|
248
|
+
window.removeEventListener('scroll', onScrollRestore)
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
window.addEventListener('scroll', onScrollRestore, { passive: true })
|
|
252
|
+
|
|
253
|
+
// Also use scrollend event if available (modern browsers)
|
|
254
|
+
if ('onscrollend' in window) {
|
|
255
|
+
window.addEventListener(
|
|
256
|
+
'scrollend',
|
|
257
|
+
() => {
|
|
258
|
+
checkScrollAndOpen()
|
|
259
|
+
window.removeEventListener('scroll', onScrollRestore)
|
|
260
|
+
},
|
|
261
|
+
{ once: true },
|
|
262
|
+
)
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Fallback: remove listener after 2 seconds
|
|
266
|
+
setTimeout(() => {
|
|
267
|
+
window.removeEventListener('scroll', onScrollRestore)
|
|
268
|
+
}, 2000)
|
|
269
|
+
|
|
270
|
+
// Handle hash changes (SPA navigation)
|
|
271
|
+
window.addEventListener('hashchange', () => {
|
|
272
|
+
if (location.hash) {
|
|
273
|
+
this.scrollToHash(location.hash)
|
|
274
|
+
}
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
// Handle clicks on anchor links pointing to content inside show-more
|
|
278
|
+
document.addEventListener('click', (e) => {
|
|
279
|
+
const link = e.target.closest('a[href^="#"]')
|
|
280
|
+
if (link) {
|
|
281
|
+
const hash = link.getAttribute('href')
|
|
282
|
+
if (hash && hash.length > 1) {
|
|
283
|
+
// Delay to let default navigation happen first
|
|
284
|
+
setTimeout(() => this.scrollToHash(hash), 10)
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
})
|
|
288
|
+
|
|
289
|
+
// Ctrl+F: open all collapsed blocks so browser can find text
|
|
290
|
+
document.addEventListener('keydown', (e) => {
|
|
291
|
+
if ((e.ctrlKey || e.metaKey) && e.key === 'f') {
|
|
292
|
+
this.openAllCollapsed()
|
|
293
|
+
}
|
|
294
|
+
})
|
|
295
|
+
},
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Open all collapsed show-more blocks
|
|
299
|
+
* Useful for Ctrl+F to make all content searchable
|
|
300
|
+
*/
|
|
301
|
+
openAllCollapsed() {
|
|
302
|
+
document.querySelectorAll('.show-more').forEach((wrapper) => {
|
|
303
|
+
if (this.isCollapsed(wrapper)) {
|
|
304
|
+
this.openContaining(wrapper, true)
|
|
305
|
+
}
|
|
306
|
+
})
|
|
307
|
+
},
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Auto-init when DOM is ready
|
|
311
|
+
export function initShowMore() {
|
|
312
|
+
if (document.readyState === 'loading') {
|
|
313
|
+
document.addEventListener('DOMContentLoaded', () => ShowMore.init())
|
|
314
|
+
} else {
|
|
315
|
+
ShowMore.init()
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Expose globally for inline onclick handlers in templates
|
|
320
|
+
if (typeof window !== 'undefined') {
|
|
321
|
+
window.ShowMore = ShowMore
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
export default ShowMore
|
package/src/alpine.js
ADDED
package/src/app.css
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
@import 'tailwindcss';
|
|
2
|
-
|
|
3
2
|
@import 'tailwindcss-animated';
|
|
4
|
-
|
|
5
3
|
@plugin '@tailwindcss/typography';
|
|
4
|
+
@import './tailwind.prose.scss';
|
|
5
|
+
@import './utility.css'; /* node_modules/@pushword/js-helper/src/utility.css */
|
|
6
6
|
/* @plugin "@tailwindcss/forms"; */
|
|
7
7
|
|
|
8
|
+
@import './../node_modules/glightbox/dist/css/glightbox.css';
|
|
9
|
+
|
|
8
10
|
/*
|
|
9
11
|
Tailwind content sources for all packages
|
|
10
12
|
-----------------------------------------
|
|
@@ -12,6 +14,8 @@ Tailwind content sources for all packages
|
|
|
12
14
|
|
|
13
15
|
@source "./../../../../vendor/pushword/**/templates/";
|
|
14
16
|
@source "./../../**/templates/";
|
|
17
|
+
@source "./../../**/src/templates/";
|
|
18
|
+
@source "./../../conversation/src/templates/";
|
|
15
19
|
@source "./../../../../templates";
|
|
16
20
|
@source "./../../../../var/TailwindGeneratorCache/*";
|
|
17
21
|
@source "./../../../../vendor/pushword/core/src/Twig/**.php";
|
|
@@ -19,65 +23,6 @@ Tailwind content sources for all packages
|
|
|
19
23
|
@source "./../../../../vendor/pushword/admin/src/templates/markdown_cheatsheet.html.twig";
|
|
20
24
|
@source "./../../admin/src/templates/markdown_cheatsheet.html.twig";
|
|
21
25
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
/* Custom minHeight values */
|
|
25
|
-
--min-height-screen-1-4: 25vh;
|
|
26
|
-
--min-height-screen-3-4: 75vh;
|
|
27
|
-
--min-height-screen-1-3: 33vh;
|
|
28
|
-
--min-height-screen-2-3: 66vh;
|
|
29
|
-
--min-height-screen-1-2: 50vh;
|
|
30
|
-
|
|
31
|
-
/* Custom colors */
|
|
32
|
-
--color-primary: var(--primary);
|
|
33
|
-
--color-secondary: var(--secondary);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/* Custom variants for first-letter */
|
|
37
|
-
@variant first-letter (&:first-letter);
|
|
38
|
-
|
|
39
|
-
/* Custom variants for first-child */
|
|
40
|
-
@variant first-child (&:first-child);
|
|
41
|
-
|
|
42
|
-
/* Custom utilities - Bleed plugin replacement */
|
|
43
|
-
@utility bleed {
|
|
44
|
-
width: 100vw;
|
|
45
|
-
margin-inline-start: 50%;
|
|
46
|
-
margin-inline-end: unset;
|
|
47
|
-
transform: translateX(-50%);
|
|
48
|
-
max-width: none;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
@utility bleed-disable {
|
|
52
|
-
width: inherit;
|
|
53
|
-
margin-inline-start: inherit;
|
|
54
|
-
margin-inline-end: inherit;
|
|
55
|
-
transform: default;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/* Custom utilities - Justify safe center */
|
|
59
|
-
@utility justify-safe-center {
|
|
60
|
-
justify-content: safe center;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/* Typography customization */
|
|
64
|
-
.prose a:not(.not-prose a),
|
|
65
|
-
.prose span[data-rot]:not(.not-prose span[data-rot]),
|
|
66
|
-
.prose .link:not(.not-prose .link) {
|
|
67
|
-
text-decoration: none;
|
|
68
|
-
color: var(--primary);
|
|
69
|
-
font-weight: 500;
|
|
70
|
-
border-bottom: 1px solid;
|
|
71
|
-
}
|
|
72
|
-
.prose a:hover:not(.not-prose a:hover),
|
|
73
|
-
.prose span[data-rot]:hover:not(.not-prose span[data-rot]:hover),
|
|
74
|
-
.prose .link:hover:not(.not-prose .link:hover) {
|
|
75
|
-
opacity: 0.75;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
.img-stretched {
|
|
79
|
-
@apply w-screen relative left-1/2 right-1/2 mr-[-50vw] ml-[-50vw];
|
|
80
|
-
}
|
|
81
|
-
.img-background {
|
|
82
|
-
@apply px-6 sm:px-12 py-1 text-center bg-gray-200 rounded;
|
|
26
|
+
[x-cloak] {
|
|
27
|
+
display: none !important;
|
|
83
28
|
}
|
package/src/app.js
CHANGED
|
@@ -9,10 +9,14 @@ import {
|
|
|
9
9
|
convertFormFromRot13,
|
|
10
10
|
} from './helpers.js'
|
|
11
11
|
import { allClickable } from './clickable.js'
|
|
12
|
+
import { initShowMore } from './ShowMore.js'
|
|
12
13
|
|
|
13
14
|
//import { HorizontalScroll } from '@pushword/js-helper/src/horizontalScroll.js';
|
|
14
15
|
//window.HorizontalScroll = HorizontalScroll;
|
|
15
16
|
|
|
17
|
+
// Initialize ShowMore (exposes window.ShowMore and sets up event listeners)
|
|
18
|
+
initShowMore()
|
|
19
|
+
|
|
16
20
|
let lightbox
|
|
17
21
|
function onDomChanged() {
|
|
18
22
|
liveBlock()
|
|
@@ -29,8 +33,8 @@ function onDomChanged() {
|
|
|
29
33
|
}
|
|
30
34
|
|
|
31
35
|
function onPageLoaded() {
|
|
32
|
-
onDomChanged()
|
|
33
36
|
lightbox = new Glightbox()
|
|
37
|
+
onDomChanged()
|
|
34
38
|
}
|
|
35
39
|
|
|
36
40
|
document.addEventListener('DOMContentLoaded', onPageLoaded())
|
package/src/helpers.js
CHANGED
|
@@ -223,18 +223,9 @@ export function addClassForNormalUser(attribute = 'data-acinb') {
|
|
|
223
223
|
}
|
|
224
224
|
},
|
|
225
225
|
)
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
// open show more block if exists
|
|
230
|
-
const showMoreTarget = targetElement.closest('.show-more')
|
|
231
|
-
if (showMoreTarget) {
|
|
232
|
-
showMoreTarget.querySelector('.show-more-btn label').click()
|
|
233
|
-
}
|
|
234
|
-
targetElement.scrollIntoView({
|
|
235
|
-
behavior: 'smooth',
|
|
236
|
-
})
|
|
237
|
-
}
|
|
226
|
+
// Handle hash navigation - delegate to ShowMore if available
|
|
227
|
+
if (window.location.hash && window.ShowMore) {
|
|
228
|
+
window.ShowMore.scrollToHash(window.location.hash)
|
|
238
229
|
}
|
|
239
230
|
}
|
|
240
231
|
}
|
|
@@ -260,7 +251,12 @@ export async function uncloakLinks(
|
|
|
260
251
|
var href = element.getAttribute(attribute)
|
|
261
252
|
element.removeAttribute(attribute)
|
|
262
253
|
for (var i = 0, n = element.attributes.length; i < n; i++) {
|
|
263
|
-
|
|
254
|
+
const attr = element.attributes[i]
|
|
255
|
+
if (attr.nodeName.startsWith('@') || attr.nodeName.startsWith(':')) {
|
|
256
|
+
console.log("You can't use @alpine.js attribute on", element)
|
|
257
|
+
continue
|
|
258
|
+
}
|
|
259
|
+
link.setAttribute(attr.nodeName, attr.nodeValue)
|
|
264
260
|
}
|
|
265
261
|
link.innerHTML = element.innerHTML
|
|
266
262
|
link.setAttribute('href', responsiveImage(convertShortchutForLink(rot13ToText(href))))
|
package/src/tailwind.prose.scss
CHANGED
|
@@ -3,32 +3,32 @@
|
|
|
3
3
|
*/
|
|
4
4
|
.text-color-inherit,
|
|
5
5
|
.link-text {
|
|
6
|
-
|
|
6
|
+
color: inherit !important;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
.no-decoration,
|
|
10
10
|
.ninja {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
text-decoration: none !important;
|
|
12
|
+
color: inherit !important;
|
|
13
|
+
cursor: auto !important;
|
|
14
|
+
border-bottom: 0 !important;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
.link-btn {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
18
|
+
padding: 6px 12px;
|
|
19
|
+
margin-bottom: 0;
|
|
20
|
+
border-radius: 3px;
|
|
21
|
+
border: 1px solid transparent;
|
|
22
|
+
cursor: pointer;
|
|
23
|
+
color: #fff !important;
|
|
24
|
+
background-color: var(--primary);
|
|
25
|
+
border-color: var(--primary);
|
|
26
|
+
outline: none;
|
|
27
|
+
text-decoration: none !important;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
.link-btn:hover {
|
|
31
|
-
|
|
31
|
+
color: #fff !important;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
/**
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
*/
|
|
37
37
|
|
|
38
38
|
html {
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
font-family: var(--font-family);
|
|
40
|
+
scroll-behavior: smooth;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
/**
|
|
@@ -46,21 +46,21 @@ html {
|
|
|
46
46
|
|
|
47
47
|
.show-hide-input:checked ~ ul,
|
|
48
48
|
.show-hide-input:checked ~ nav {
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
max-height: 100vh;
|
|
50
|
+
padding-top: 1rem;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
/** Pure CSS accordion and show more
|
|
54
54
|
Example https: //play.tailwindcss.com/VxdXWMH64M
|
|
55
55
|
|
|
56
56
|
**/
|
|
57
|
-
.show-hide-input:checked~div {
|
|
58
|
-
|
|
57
|
+
.show-hide-input:checked ~ div {
|
|
58
|
+
max-height: 100%;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
.show-more
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
61
|
+
.show-more > .show-hide-input:checked ~ div.show-more-btn {
|
|
62
|
+
max-height: 0;
|
|
63
|
+
overflow: hidden;
|
|
64
|
+
padding-top: 0;
|
|
65
|
+
margin-top: 0;
|
|
66
|
+
}
|
package/src/utility.css
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/* Tailwind v4 CSS-first configuration */
|
|
2
|
+
@theme {
|
|
3
|
+
/* Custom minHeight values */
|
|
4
|
+
--min-height-screen-1-4: 25vh;
|
|
5
|
+
--min-height-screen-3-4: 75vh;
|
|
6
|
+
--min-height-screen-1-3: 33vh;
|
|
7
|
+
--min-height-screen-2-3: 66vh;
|
|
8
|
+
--min-height-screen-1-2: 50vh;
|
|
9
|
+
|
|
10
|
+
/* Custom colors */
|
|
11
|
+
--color-primary: var(--primary);
|
|
12
|
+
--color-secondary: var(--secondary);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/* Custom variants for first-letter */
|
|
16
|
+
@variant first-letter (&:first-letter);
|
|
17
|
+
|
|
18
|
+
/* Custom variants for first-child */
|
|
19
|
+
@variant first-child (&:first-child);
|
|
20
|
+
|
|
21
|
+
/* Custom utilities - Bleed plugin replacement */
|
|
22
|
+
@utility bleed {
|
|
23
|
+
width: 100vw;
|
|
24
|
+
margin-inline-start: 50%;
|
|
25
|
+
margin-inline-end: unset;
|
|
26
|
+
transform: translateX(-50%);
|
|
27
|
+
max-width: none;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@utility bleed-disable {
|
|
31
|
+
width: inherit;
|
|
32
|
+
margin-inline-start: inherit;
|
|
33
|
+
margin-inline-end: inherit;
|
|
34
|
+
transform: default;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* Custom utilities - Justify safe center */
|
|
38
|
+
@utility justify-safe-center {
|
|
39
|
+
justify-content: safe center;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* Typography customization */
|
|
43
|
+
.prose a:not(.not-prose a),
|
|
44
|
+
.prose span[data-rot]:not(.not-prose span[data-rot]),
|
|
45
|
+
.prose .link:not(.not-prose .link) {
|
|
46
|
+
text-decoration: none;
|
|
47
|
+
color: var(--primary);
|
|
48
|
+
font-weight: 500;
|
|
49
|
+
border-bottom: 1px solid;
|
|
50
|
+
}
|
|
51
|
+
.prose a:hover:not(.not-prose a:hover),
|
|
52
|
+
.prose span[data-rot]:hover:not(.not-prose span[data-rot]:hover),
|
|
53
|
+
.prose .link:hover:not(.not-prose .link:hover) {
|
|
54
|
+
opacity: 0.75;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.img-stretched {
|
|
58
|
+
@apply w-screen relative left-1/2 right-1/2 mr-[-50vw] ml-[-50vw];
|
|
59
|
+
}
|
|
60
|
+
.img-background {
|
|
61
|
+
@apply px-6 sm:px-12 py-1 text-center bg-gray-200 rounded;
|
|
62
|
+
}
|