derafu-js 0.1.0 → 0.1.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 +4 -4
- package/src/derafu.js +5 -0
- package/src/tabs.js +73 -24
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "derafu-js",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Why use a JS framework if we can do all from scratch?",
|
|
5
5
|
"main": "src/derafu.js",
|
|
6
6
|
"files": [
|
|
@@ -28,11 +28,11 @@
|
|
|
28
28
|
"license": "MIT",
|
|
29
29
|
"repository": {
|
|
30
30
|
"type": "git",
|
|
31
|
-
"url": "git+https://github.com/derafu/
|
|
31
|
+
"url": "git+https://github.com/derafu/js.git"
|
|
32
32
|
},
|
|
33
|
-
"homepage": "https://github.com/derafu/
|
|
33
|
+
"homepage": "https://github.com/derafu/js#readme",
|
|
34
34
|
"bugs": {
|
|
35
|
-
"url": "https://github.com/derafu/
|
|
35
|
+
"url": "https://github.com/derafu/js/issues"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"eslint": "^8.57.0",
|
package/src/derafu.js
CHANGED
|
@@ -134,6 +134,11 @@ if (typeof Form !== 'undefined') {
|
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
+
// Make derafu available globally for browser usage.
|
|
138
|
+
if (typeof window !== 'undefined') {
|
|
139
|
+
window.derafu = derafu;
|
|
140
|
+
}
|
|
141
|
+
|
|
137
142
|
// Export module for use in Node.js.
|
|
138
143
|
if (typeof module === 'object' && module.exports) {
|
|
139
144
|
module.exports = derafu;
|
package/src/tabs.js
CHANGED
|
@@ -29,7 +29,9 @@ Tabs.init = function () {
|
|
|
29
29
|
'use strict';
|
|
30
30
|
|
|
31
31
|
// Open the first tab by default.
|
|
32
|
-
const firstTabLink = document.querySelector(
|
|
32
|
+
const firstTabLink = document.querySelector(
|
|
33
|
+
'.nav-tabs [data-bs-toggle="tab"]',
|
|
34
|
+
);
|
|
33
35
|
if (firstTabLink) {
|
|
34
36
|
new bootstrap.Tab(firstTabLink).show();
|
|
35
37
|
}
|
|
@@ -38,13 +40,23 @@ Tabs.init = function () {
|
|
|
38
40
|
Tabs.handleURLDeeplink();
|
|
39
41
|
|
|
40
42
|
// Update URL when clicking on each tab.
|
|
41
|
-
const tabLinks = document.querySelectorAll('
|
|
43
|
+
const tabLinks = document.querySelectorAll('[data-bs-toggle="tab"]');
|
|
42
44
|
tabLinks.forEach(tabLink => {
|
|
43
45
|
tabLink.addEventListener('click', function () {
|
|
44
|
-
|
|
46
|
+
const tabId =
|
|
47
|
+
tabLink.getAttribute('aria-controls') ||
|
|
48
|
+
(
|
|
49
|
+
tabLink.getAttribute('data-bs-target') ||
|
|
50
|
+
tabLink.getAttribute('href') ||
|
|
51
|
+
''
|
|
52
|
+
).replace('#', '');
|
|
53
|
+
Tabs.deeplink(tabId);
|
|
45
54
|
});
|
|
46
55
|
});
|
|
47
56
|
|
|
57
|
+
// Inject animation styles for deeplink highlighting.
|
|
58
|
+
Tabs._injectStyles();
|
|
59
|
+
|
|
48
60
|
// Add magic links, modal button actions, and form elements within tabs.
|
|
49
61
|
for (const tabPane of document.querySelectorAll('div.tab-pane')) {
|
|
50
62
|
if (tabPane.id) {
|
|
@@ -69,6 +81,58 @@ Tabs.init = function () {
|
|
|
69
81
|
}, true);*/
|
|
70
82
|
};
|
|
71
83
|
|
|
84
|
+
/**
|
|
85
|
+
* Injects the CSS keyframe animation for deeplink highlighting once into the
|
|
86
|
+
* document head. Uses Bootstrap CSS variables so colors adapt to any theme.
|
|
87
|
+
*
|
|
88
|
+
* @returns {void}
|
|
89
|
+
*/
|
|
90
|
+
Tabs._injectStyles = function () {
|
|
91
|
+
'use strict';
|
|
92
|
+
if (document.getElementById('derafu-tabs-styles')) return;
|
|
93
|
+
const style = document.createElement('style');
|
|
94
|
+
style.id = 'derafu-tabs-styles';
|
|
95
|
+
style.textContent =
|
|
96
|
+
'@keyframes deeplink-pulse {' +
|
|
97
|
+
'0% { box-shadow: 0 0 0 4px rgba(var(--bs-primary-rgb), 0.5); }' +
|
|
98
|
+
'100% { box-shadow: 0 0 0 0 rgba(var(--bs-primary-rgb), 0); }' +
|
|
99
|
+
'}' +
|
|
100
|
+
'.deeplink-highlight {' +
|
|
101
|
+
'animation: deeplink-pulse 2s ease-out forwards;' +
|
|
102
|
+
'}';
|
|
103
|
+
document.head.appendChild(style);
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Highlights an element using a pulse animation. For cards the element itself
|
|
108
|
+
* is targeted; for form elements the nearest Bootstrap container is used
|
|
109
|
+
* (.input-group > .form-floating > parentElement) so that prepend/append
|
|
110
|
+
* addons and floating labels are included in the visual feedback.
|
|
111
|
+
* The highlight class is removed automatically when the animation ends.
|
|
112
|
+
*
|
|
113
|
+
* @param {HTMLElement} element - The element to highlight.
|
|
114
|
+
* @param {boolean} isCard - Whether the element is a card.
|
|
115
|
+
* @returns {void}
|
|
116
|
+
*/
|
|
117
|
+
Tabs._highlightElement = function (element, isCard) {
|
|
118
|
+
'use strict';
|
|
119
|
+
const target = isCard
|
|
120
|
+
? element
|
|
121
|
+
: element.closest('.input-group') ||
|
|
122
|
+
element.closest('.form-floating') ||
|
|
123
|
+
element.parentElement;
|
|
124
|
+
if (!target) return;
|
|
125
|
+
if (isCard) target.classList.add('border-primary');
|
|
126
|
+
target.classList.add('deeplink-highlight');
|
|
127
|
+
target.addEventListener(
|
|
128
|
+
'animationend',
|
|
129
|
+
function () {
|
|
130
|
+
target.classList.remove('deeplink-highlight', 'border-primary');
|
|
131
|
+
},
|
|
132
|
+
{ once: true },
|
|
133
|
+
);
|
|
134
|
+
};
|
|
135
|
+
|
|
72
136
|
/**
|
|
73
137
|
* Handles opening tabs and focusing on specific elements based on URL.
|
|
74
138
|
* @returns {void}
|
|
@@ -106,7 +170,7 @@ Tabs.handleTabDeeplink = function (tabElement, elementId) {
|
|
|
106
170
|
`${tabElement_id}_${elementId}-card`,
|
|
107
171
|
);
|
|
108
172
|
if (cardElement) {
|
|
109
|
-
|
|
173
|
+
Tabs._highlightElement(cardElement, true);
|
|
110
174
|
typeof UI !== 'undefined'
|
|
111
175
|
? UI.scroll(cardElement)
|
|
112
176
|
: cardElement.scrollIntoView({ behavior: 'smooth' });
|
|
@@ -124,21 +188,9 @@ Tabs.handleTabDeeplink = function (tabElement, elementId) {
|
|
|
124
188
|
}
|
|
125
189
|
modalInstance.show();
|
|
126
190
|
}
|
|
127
|
-
// If it's a
|
|
128
|
-
else if (
|
|
129
|
-
element.tagName.toLowerCase() === 'select' &&
|
|
130
|
-
element.dataset.select2Id
|
|
131
|
-
) {
|
|
132
|
-
element.parentElement
|
|
133
|
-
.querySelector('span.select2-selection')
|
|
134
|
-
.classList.add('deeplink', 'border', 'border-danger');
|
|
135
|
-
typeof UI !== 'undefined'
|
|
136
|
-
? UI.scroll(element)
|
|
137
|
-
: element.scrollIntoView({ behavior: 'smooth' });
|
|
138
|
-
}
|
|
139
|
-
// If it's not a modal or select2, apply border and scroll.
|
|
191
|
+
// If it's a form element, highlight its container and scroll.
|
|
140
192
|
else {
|
|
141
|
-
|
|
193
|
+
Tabs._highlightElement(element, false);
|
|
142
194
|
typeof UI !== 'undefined'
|
|
143
195
|
? UI.scroll(element)
|
|
144
196
|
: element.scrollIntoView({ behavior: 'smooth' });
|
|
@@ -249,12 +301,9 @@ Tabs.deeplink = function (deeplink) {
|
|
|
249
301
|
history.replaceState(null, null, url);
|
|
250
302
|
})();
|
|
251
303
|
|
|
252
|
-
//
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
'.deeplink.border.border-danger',
|
|
256
|
-
)) {
|
|
257
|
-
element.classList.remove('deeplink', 'border', 'border-danger');
|
|
304
|
+
// Cancel any in-progress highlight so only one marked element exists at a time.
|
|
305
|
+
for (const element of document.querySelectorAll('.deeplink-highlight')) {
|
|
306
|
+
element.classList.remove('deeplink-highlight', 'border-primary');
|
|
258
307
|
}
|
|
259
308
|
|
|
260
309
|
// Handle opening tabs based on URL.
|