@@ -10,24 +10,40 @@ including this file, may be copied, modified, propagated, or distributed
10
10
except according to the terms contained in the LICENSE file.
11
11
-->
12
12
<template>
13
- <modal :state="state" @hide="$emit(' hide') " backdrop>
13
+ <modal ref="modal" :state="state" @hide="hide" backdrop>
14
14
<template slot="title">Create Form</template>
15
15
<template slot="body">
16
16
<alert v-bind="alert" @close="alert.state = false"/>
17
- <form @submit.prevent="submit">
18
- <div class="form-group">
19
- <label for="form-new-xml">Form XML *</label>
20
- <textarea v-model="xml" id="form-new-xml" class="form-control"
21
- required :disabled="awaitingResponse" rows="10">
22
- </textarea>
17
+ <div class="modal-introduction">
18
+ <p>To create a form, upload an XForms XML file.</p>
19
+ <p>
20
+ If you don’t already have one, there are
21
+ <doc-link>tools available</doc-link> to help you design your form.
22
+ </p>
23
+ </div>
24
+ <div id="drop-zone" :class="dropZoneClass" @dragover.prevent="dragover"
25
+ @dragleave="dragleave" @drop.prevent="drop">
26
+ <div :style="pointerEvents">
27
+ Drop a file here, or
28
+ <input type="file" ref="input" class="hidden"
29
+ @change="readFile($event.target.files)">
30
+ <button type="button" class="btn btn-primary" :disabled="!droppable"
31
+ @click="clickFileButton">
32
+ choose one
33
+ </button>
34
+ to upload.
23
35
</div>
24
- <button type="submit" class="btn btn-primary" :disabled="awaitingResponse">
36
+ <div id="form-new-filename" :style="pointerEvents">{{ filename }}</div>
37
+ </div>
38
+ <div class="modal-actions">
39
+ <button type="button" class="btn btn-primary"
40
+ :disabled="awaitingResponse" @click="create">
25
41
Create <spinner :state="awaitingResponse"/>
26
42
</button>
27
- <button type="button" class="btn btn-default " @click="$emit(' hide') ">
43
+ <button type="button" class="btn btn-link " @click="hide">
28
44
Close
29
45
</button>
30
- </form >
46
+ </div >
31
47
</template>
32
48
</modal>
33
49
</template>
@@ -36,6 +52,8 @@ except according to the terms contained in the LICENSE file.
36
52
import alert from '../../mixins/alert';
37
53
import request from '../../mixins/request';
38
54
55
+ const NO_FILE_MESSAGE = 'Please choose a file.';
56
+
39
57
export default {
40
58
name: 'FormNew',
41
59
mixins: [alert(), request()],
@@ -49,22 +67,134 @@ export default {
49
67
return {
50
68
alert: alert.blank(),
51
69
requestId: null,
52
- xml: ''
70
+ // true if a dragged file is over the drop zone and false if not.
71
+ isOverDropZone: false,
72
+ reading: false,
73
+ filename: '',
74
+ xml: null
53
75
};
54
76
},
77
+ computed: {
78
+ droppable() {
79
+ return !(this.reading || this.awaitingResponse);
80
+ },
81
+ dropZoneClass() {
82
+ return {
83
+ 'text-center': true,
84
+ droppable: this.droppable,
85
+ dragover: this.isOverDropZone
86
+ };
87
+ },
88
+ // Used to prevent child elements of #drop-zone from triggering dragleave
89
+ // events upon hover. Does not work for IE9 or 10.
90
+ pointerEvents() {
91
+ return this.isOverDropZone ? { 'pointer-events': 'none' } : {};
92
+ }
93
+ },
55
94
methods: {
56
- submit() {
95
+ hide() {
96
+ this.$emit('hide');
97
+ if (this.alert.type === 'info' && this.alert.message === NO_FILE_MESSAGE)
98
+ this.alert = alert.blank();
99
+ },
100
+ readFile(files) {
101
+ if (files.length === 0) return;
102
+ const file = files[0];
103
+ const reader = new FileReader();
104
+ reader.onloadstart = () => {
105
+ this.reading = true;
106
+ };
107
+ reader.onload = (event) => {
108
+ this.alert = alert.blank();
109
+ this.filename = file.name;
110
+ this.xml = event.target.result;
111
+ };
112
+ reader.onerror = () => {
113
+ this.alert = alert.danger('Something went wrong while reading the file.');
114
+ this.filename = '';
115
+ this.xml = null;
116
+ };
117
+ reader.onabort = request.onerror;
118
+ reader.onloadend = () => {
119
+ this.$refs.input.value = '';
120
+ this.reading = false;
121
+ };
122
+ reader.readAsText(file);
123
+ },
124
+ clickFileButton() {
125
+ this.$refs.input.click();
126
+ // Focus the modal rather than blurring the button so that the escape key
127
+ // still hides the modal.
128
+ this.$refs.modal.$el.focus();
129
+ },
130
+ dragover(event) {
131
+ // Putting this line in a dragenter listener did not work. Is it possible
132
+ // that a child element of #drop-zone could trigger a dragleave event
133
+ // before the dragenter listener is called?
134
+ this.isOverDropZone = true;
135
+ // eslint-disable-next-line no-param-reassign
136
+ if (this.droppable) event.dataTransfer.dropEffect = 'copy';
137
+ },
138
+ dragleave() {
139
+ this.isOverDropZone = false;
140
+ },
141
+ drop(event) {
142
+ if (this.droppable) this.readFile(event.dataTransfer.files);
143
+ this.isOverDropZone = false;
144
+ },
145
+ checkStateBeforeRequest() {
146
+ if (this.xml == null) {
147
+ this.alert = alert.info(NO_FILE_MESSAGE);
148
+ return false;
149
+ } else if (this.reading) {
150
+ this.alert = alert.info('The file is still being processed.');
151
+ return false;
152
+ }
153
+ return true;
154
+ },
155
+ create() {
156
+ if (!this.checkStateBeforeRequest()) return;
57
157
const headers = { 'Content-Type': 'application/xml' };
58
158
this
59
159
.post('/forms', this.xml, { headers })
60
160
.then(form => {
61
161
this.$emit('hide');
62
- this.alert = alert.blank();
63
- this.xml = '';
64
- this.$emit('create', form);
162
+ // Wait for the modal to hide.
163
+ this.$nextTick(() => {
164
+ const name = form.name || form.xmlFormId;
165
+ this.$alert = alert.success(`The form “${name}” was created successfully.`);
166
+ this.$router.push(`/forms/${form.xmlFormId}/submissions`);
167
+ });
65
168
})
66
169
.catch(() => {});
67
170
}
68
171
}
69
172
};
70
173
</script>
174
+
175
+ <style lang="sass">
176
+ @import '../../../assets/scss/variables';
177
+
178
+ #drop-zone {
179
+ background-color: $color-input-background;
180
+ border: 1px dashed $color-subpanel-border;
181
+
182
+ &.dragover {
183
+ opacity: 0.65;
184
+ }
185
+
186
+ &:not(.droppable) {
187
+ cursor: not-allowed;
188
+ opacity: 0.65;
189
+ }
190
+
191
+ div {
192
+ margin-bottom: 5px;
193
+ margin-top: 5px;
194
+ }
195
+
196
+ #form-new-filename {
197
+ font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
198
+ }
199
+ }
200
+ </style>
0 commit comments