@@ -7,6 +7,7 @@ import {handleGlobalEnterQuickSubmit} from './comp/QuickSubmit.js';
7
7
import { svg } from '../svg.js' ;
8
8
import { hideElem , showElem , toggleElem } from '../utils/dom.js' ;
9
9
import { htmlEscape } from 'escape-goat' ;
10
+ import { createTippy } from '../modules/tippy.js' ;
10
11
11
12
const { appUrl, csrfToken, i18n} = window . config ;
12
13
@@ -60,6 +61,81 @@ export function initGlobalButtonClickOnEnter() {
60
61
} ) ;
61
62
}
62
63
64
+ async function formFetchAction ( e ) {
65
+ if ( ! e . target . classList . contains ( 'form-fetch-action' ) ) return ;
66
+
67
+ e . preventDefault ( ) ;
68
+ const formEl = e . target ;
69
+ if ( formEl . classList . contains ( 'is-loading' ) ) return ;
70
+
71
+ formEl . classList . add ( 'is-loading' ) ;
72
+ if ( formEl . clientHeight < 50 ) {
73
+ formEl . classList . add ( 'small-loading-icon' ) ;
74
+ }
75
+
76
+ const formMethod = formEl . getAttribute ( 'method' ) || 'get' ;
77
+ const formActionUrl = formEl . getAttribute ( 'action' ) ;
78
+ const formData = new FormData ( formEl ) ;
79
+ const [ submitterName , submitterValue ] = [ e . submitter ?. getAttribute ( 'name' ) , e . submitter ?. getAttribute ( 'value' ) ] ;
80
+ if ( submitterName ) {
81
+ formData . append ( submitterName , submitterValue || '' ) ;
82
+ }
83
+
84
+ let reqUrl = formActionUrl ;
85
+ const reqOpt = { method : formMethod . toUpperCase ( ) , headers : { 'X-Csrf-Token' : csrfToken } } ;
86
+ if ( formMethod . toLowerCase ( ) === 'get' ) {
87
+ const params = new URLSearchParams ( ) ;
88
+ for ( const [ key , value ] of formData ) {
89
+ params . append ( key , value . toString ( ) ) ;
90
+ }
91
+ const pos = reqUrl . indexOf ( '?' ) ;
92
+ if ( pos !== - 1 ) {
93
+ reqUrl = reqUrl . slice ( 0 , pos ) ;
94
+ }
95
+ reqUrl += `?${ params . toString ( ) } ` ;
96
+ } else {
97
+ reqOpt . body = formData ;
98
+ }
99
+
100
+ let errorTippy ;
101
+ const onError = ( msg ) => {
102
+ formEl . classList . remove ( 'is-loading' , 'small-loading-icon' ) ;
103
+ if ( errorTippy ) errorTippy . destroy ( ) ;
104
+ errorTippy = createTippy ( formEl , {
105
+ content : msg ,
106
+ interactive : true ,
107
+ showOnCreate : true ,
108
+ hideOnClick : true ,
109
+ role : 'alert' ,
110
+ theme : 'form-fetch-error' ,
111
+ trigger : 'manual' ,
112
+ arrow : false ,
113
+ } ) ;
114
+ } ;
115
+
116
+ const doRequest = async ( ) => {
117
+ try {
118
+ const resp = await fetch ( reqUrl , reqOpt ) ;
119
+ if ( resp . status === 200 ) {
120
+ const { redirect} = await resp . json ( ) ;
121
+ formEl . classList . remove ( 'dirty' ) ; // remove the areYouSure check before reloading
122
+ if ( redirect ) {
123
+ window . location . href = redirect ;
124
+ } else {
125
+ window . location . reload ( ) ;
126
+ }
127
+ } else {
128
+ onError ( `server error: ${ resp . status } ` ) ;
129
+ }
130
+ } catch ( e ) {
131
+ onError ( e . error ) ;
132
+ }
133
+ } ;
134
+
135
+ // TODO: add "confirm" support like "link-action" in the future
136
+ await doRequest ( ) ;
137
+ }
138
+
63
139
export function initGlobalCommon ( ) {
64
140
// Semantic UI modules.
65
141
const $uiDropdowns = $ ( '.ui.dropdown' ) ;
@@ -114,6 +190,8 @@ export function initGlobalCommon() {
114
190
if ( btn . classList . contains ( 'loading' ) ) return e . preventDefault ( ) ;
115
191
btn . classList . add ( 'loading' ) ;
116
192
} ) ;
193
+
194
+ document . addEventListener ( 'submit' , formFetchAction ) ;
117
195
}
118
196
119
197
export function initGlobalDropzone ( ) {
@@ -182,7 +260,7 @@ function linkAction(e) {
182
260
const $this = $ ( e . target ) ;
183
261
const redirect = $this . attr ( 'data-redirect' ) ;
184
262
185
- const request = ( ) => {
263
+ const doRequest = ( ) => {
186
264
$this . prop ( 'disabled' , true ) ;
187
265
$ . post ( $this . attr ( 'data-url' ) , {
188
266
_csrf : csrfToken
@@ -201,7 +279,7 @@ function linkAction(e) {
201
279
202
280
const modalConfirmHtml = htmlEscape ( $this . attr ( 'data-modal-confirm' ) || '' ) ;
203
281
if ( ! modalConfirmHtml ) {
204
- request ( ) ;
282
+ doRequest ( ) ;
205
283
return ;
206
284
}
207
285
@@ -220,7 +298,7 @@ function linkAction(e) {
220
298
$modal . appendTo ( document . body ) ;
221
299
$modal . modal ( {
222
300
onApprove ( ) {
223
- request ( ) ;
301
+ doRequest ( ) ;
224
302
} ,
225
303
onHidden ( ) {
226
304
$modal . remove ( ) ;
0 commit comments