UNPKG

5.12 kBJavaScriptView Raw
1;(function () {
2 let loadingStatesUndoQueue = []
3
4 function loadingStateContainer(target) {
5 return htmx.closest(target, '[data-loading-states]') || document.body
6 }
7
8 function mayProcessUndoCallback(target, callback) {
9 if (document.body.contains(target)) {
10 callback()
11 }
12 }
13
14 function mayProcessLoadingStateByPath(elt, requestPath) {
15 const pathElt = htmx.closest(elt, '[data-loading-path]')
16 if (!pathElt) {
17 return true
18 }
19
20 return pathElt.getAttribute('data-loading-path') === requestPath
21 }
22
23 function queueLoadingState(sourceElt, targetElt, doCallback, undoCallback) {
24 const delayElt = htmx.closest(sourceElt, '[data-loading-delay]')
25 if (delayElt) {
26 const delayInMilliseconds =
27 delayElt.getAttribute('data-loading-delay') || 200
28 const timeout = setTimeout(function () {
29 doCallback()
30
31 loadingStatesUndoQueue.push(function () {
32 mayProcessUndoCallback(targetElt, undoCallback)
33 })
34 }, delayInMilliseconds)
35
36 loadingStatesUndoQueue.push(function () {
37 mayProcessUndoCallback(targetElt, function () { clearTimeout(timeout) })
38 })
39 } else {
40 doCallback()
41 loadingStatesUndoQueue.push(function () {
42 mayProcessUndoCallback(targetElt, undoCallback)
43 })
44 }
45 }
46
47 function getLoadingStateElts(loadingScope, type, path) {
48 return Array.from(htmx.findAll(loadingScope, "[" + type + "]")).filter(
49 function (elt) { return mayProcessLoadingStateByPath(elt, path) }
50 )
51 }
52
53 function getLoadingTarget(elt) {
54 if (elt.getAttribute('data-loading-target')) {
55 return Array.from(
56 htmx.findAll(elt.getAttribute('data-loading-target'))
57 )
58 }
59 return [elt]
60 }
61
62 htmx.defineExtension('loading-states', {
63 onEvent: function (name, evt) {
64 if (name === 'htmx:beforeRequest') {
65 const container = loadingStateContainer(evt.target)
66
67 const loadingStateTypes = [
68 'data-loading',
69 'data-loading-class',
70 'data-loading-class-remove',
71 'data-loading-disable',
72 'data-loading-aria-busy',
73 ]
74
75 let loadingStateEltsByType = {}
76
77 loadingStateTypes.forEach(function (type) {
78 loadingStateEltsByType[type] = getLoadingStateElts(
79 container,
80 type,
81 evt.detail.pathInfo.requestPath
82 )
83 })
84
85 loadingStateEltsByType['data-loading'].forEach(function (sourceElt) {
86 getLoadingTarget(sourceElt).forEach(function (targetElt) {
87 queueLoadingState(
88 sourceElt,
89 targetElt,
90 function () {
91 targetElt.style.display =
92 sourceElt.getAttribute('data-loading') ||
93 'inline-block' },
94 function () { targetElt.style.display = 'none' }
95 )
96 })
97 })
98
99 loadingStateEltsByType['data-loading-class'].forEach(
100 function (sourceElt) {
101 const classNames = sourceElt
102 .getAttribute('data-loading-class')
103 .split(' ')
104
105 getLoadingTarget(sourceElt).forEach(function (targetElt) {
106 queueLoadingState(
107 sourceElt,
108 targetElt,
109 function () {
110 classNames.forEach(function (className) {
111 targetElt.classList.add(className)
112 })
113 },
114 function() {
115 classNames.forEach(function (className) {
116 targetElt.classList.remove(className)
117 })
118 }
119 )
120 })
121 }
122 )
123
124 loadingStateEltsByType['data-loading-class-remove'].forEach(
125 function (sourceElt) {
126 const classNames = sourceElt
127 .getAttribute('data-loading-class-remove')
128 .split(' ')
129
130 getLoadingTarget(sourceElt).forEach(function (targetElt) {
131 queueLoadingState(
132 sourceElt,
133 targetElt,
134 function () {
135 classNames.forEach(function (className) {
136 targetElt.classList.remove(className)
137 })
138 },
139 function() {
140 classNames.forEach(function (className) {
141 targetElt.classList.add(className)
142 })
143 }
144 )
145 })
146 }
147 )
148
149 loadingStateEltsByType['data-loading-disable'].forEach(
150 function (sourceElt) {
151 getLoadingTarget(sourceElt).forEach(function (targetElt) {
152 queueLoadingState(
153 sourceElt,
154 targetElt,
155 function() { targetElt.disabled = true },
156 function() { targetElt.disabled = false }
157 )
158 })
159 }
160 )
161
162 loadingStateEltsByType['data-loading-aria-busy'].forEach(
163 function (sourceElt) {
164 getLoadingTarget(sourceElt).forEach(function (targetElt) {
165 queueLoadingState(
166 sourceElt,
167 targetElt,
168 function () { targetElt.setAttribute("aria-busy", "true") },
169 function () { targetElt.removeAttribute("aria-busy") }
170 )
171 })
172 }
173 )
174 }
175
176 if (name === 'htmx:beforeOnLoad') {
177 while (loadingStatesUndoQueue.length > 0) {
178 loadingStatesUndoQueue.shift()()
179 }
180 }
181 },
182 })
183})()