Skip to content

Commit 33bba58

Browse files
committed
Migrated to select2 input for flysystem picker. Allows for infinite scroll pagination and coherence with yii2 picker used with flysystem
1 parent 4d34898 commit 33bba58

File tree

2 files changed

+116
-214
lines changed

2 files changed

+116
-214
lines changed

src/JsonEditorPluginsAsset.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
namespace dmstr\jsoneditor;
1111

12+
use kartik\select2\Select2Asset;
13+
use kartik\select2\ThemeKrajeeBs3Asset;
1214
use yii\web\AssetBundle;
1315
use dosamigos\selectize\SelectizeAsset;
1416
use yii\web\JqueryAsset;
@@ -31,7 +33,8 @@ class JsonEditorPluginsAsset extends AssetBundle
3133
public $depends = [
3234
JsonEditorAsset::class,
3335
SelectizeAsset::class,
36+
Select2Asset::class,
37+
ThemeKrajeeBs3Asset::class,
3438
JqueryAsset::class
3539
];
36-
3740
}

src/assets/editors/flysystem.js

Lines changed: 112 additions & 213 deletions
Original file line numberDiff line numberDiff line change
@@ -1,256 +1,155 @@
1-
var StringEditor = JSONEditor.defaults.editors.string;
1+
var StringEditor = JSONEditor.defaults.editors.string
22

33
class FlysystemEditor extends StringEditor {
44
setValue(value) {
5-
if (value === null) {
6-
value = '';
7-
}
5+
value = value || ''
6+
if (this.value === value) return
87

9-
if (this.value === value) {
10-
return;
11-
}
12-
this.input.value = value;
13-
this.value = value;
14-
this.onChange();
15-
}
16-
17-
register() {
18-
super.register();
19-
if (!this.input) return;
20-
this.input.setAttribute('name', this.formname);
21-
}
22-
23-
unregister() {
24-
super.unregister();
25-
if (!this.input) return;
26-
this.input.removeAttribute('name');
27-
}
28-
29-
getNumColumns() {
30-
if (!this.enum_options) return 3;
31-
var longest_text = this.getTitle().length;
32-
for (var i = 0; i < this.enum_options.length; i++) {
33-
longest_text = Math.max(longest_text, this.enum_options[i].length + 4);
34-
}
35-
return Math.min(12, Math.max(longest_text / 7, 2));
36-
}
37-
38-
getValue() {
39-
if (!this.value) {
40-
this.value = '';
41-
}
42-
return this.value;
8+
this.input.value = value
9+
this.value = value
10+
this.setOption(value)
11+
this.onChange()
4312
}
4413

4514
build() {
46-
var self = this;
47-
48-
if (!this.options.compact) {
49-
this.header = this.label = this.theme.getFormInputLabel(this.getTitle());
50-
}
51-
52-
if (this.schema.description) {
53-
this.description = this.theme.getFormInputDescription(this.schema.description);
54-
}
15+
this.selectInput = this.theme.getSelectInput()
16+
this.input = this.theme.getFormInputField('hidden')
5517

56-
if (this.options.infoText) {
57-
this.infoButton = this.theme.getInfoButton(this.options.infoText);
58-
}
59-
60-
if (this.options.compact) {
61-
this.container.classList.add('compact');
62-
}
63-
64-
this.input = this.theme.getFormInputField('text');
65-
66-
67-
if (this.schema.readOnly || this.schema.readonly) {
68-
this.always_disabled = true;
69-
this.input.disabled = true;
70-
}
71-
72-
this.input.addEventListener('change', function (e) {
73-
e.preventDefault();
74-
e.stopPropagation();
75-
self.onInputChange();
76-
});
77-
78-
this.control = this.theme.getFormControl(this.label, this.input, this.description, this.infoButton);
79-
this.container.appendChild(this.control);
80-
81-
self.jsoneditor.on('ready', function () {
82-
self.initSelectize();
83-
});
84-
85-
self.jsoneditor.on('addRow', function () {
86-
self.initSelectize();
87-
});
88-
89-
self.jsoneditor.on('moveRow', function () {
90-
self.initSelectize();
91-
});
92-
93-
self.jsoneditor.on('deleteRow', function () {
94-
self.initSelectize();
95-
});
18+
this.input.addEventListener('change', (e) => {
19+
e.preventDefault()
20+
e.stopPropagation()
21+
this.onInputChange()
22+
})
9623

24+
this.control = this.theme.getFormControl(this.label, this.input, this.description, this.infoButton)
25+
this.container.appendChild(this.control)
26+
this.control.appendChild(this.selectInput)
9727
}
9828

9929
postBuild() {
100-
super.postBuild();
101-
this.initSelectize();
102-
this.theme.afterInputReady(this.input);
103-
}
104-
105-
hasThumbnailPreview(item) {
106-
return (item && item.extraMetadata && item.extraMetadata.thumbnail)
30+
super.postBuild()
31+
this.initSelect2()
32+
this.theme.afterInputReady(this.input)
10733
}
10834

109-
hasImageExtension (path) {
35+
hasImageExtension(path) {
11036
if (typeof path !== 'string') {
11137
throw 'path is not a string!'
11238
}
11339

114-
var imageExtensions = ['jpg', 'jpeg', 'gif', 'svg', 'png', 'bmp']
40+
const imageExtensions = ['jpg', 'jpeg', 'gif', 'svg', 'png', 'bmp'].map(ext => ext.toLowerCase())
41+
const extension = path.split('.').pop().toLowerCase()
42+
43+
return imageExtensions.includes(extension)
44+
}
45+
46+
initSelect2() {
47+
this.destroySelect2()
48+
this.searchUrl = this.schema?.searchUrl || '/filemanager/api/search'
49+
this.streamUrl = this.schema?.streamUrl || '/filemanager/api/stream'
50+
51+
this.select2instance = $(this.selectInput).select2({
52+
theme: 'krajee-bs3',
53+
ajax: {
54+
cache: true,
55+
url: this.searchUrl,
56+
dataType: 'json',
57+
delay: 250,
58+
data: (params) => ({
59+
q: params.term,
60+
page: params.page || 0 // Send the page number to the server
61+
}),
62+
processResults: (data, params) => {
63+
params.page = params.page || 0
64+
65+
return {
66+
results: data.results.map(item => ({
67+
id: item.fullPath
68+
})),
69+
pagination: {
70+
more: data.pagination.more // Indicate if there are more results https://select2.org/data-sources/ajax#pagination
71+
}
72+
}
73+
}
74+
},
75+
error: (jqXHR, textStatus, errorThrown) => {
76+
console.error('AJAX error:', textStatus, errorThrown)
77+
},
78+
placeholder: 'Search for a file...',
79+
allowClear: true,
80+
debug: true,
81+
minimumInputLength: 0,
82+
templateResult: (item) => {
83+
if (item.loading) return item.id
84+
if (this.hasImageExtension(item.id)) {
85+
return $(`<span><img src='${this.streamUrl}?path=${item.id}' style='max-width: 70px; max-height: 70px;' /> ${item.id}</span>`)
86+
}
87+
return $(`<span><i class='fa fa-file' style='font-size: 50px;'></i> ${item.id}</span>`)
88+
},
89+
templateSelection: (item) => {
90+
if (!item.id) {
91+
return item.text;
92+
}
11593

116-
imageExtensions = imageExtensions.map(extension => {
117-
return extension.toLowerCase()
94+
if (item.id && this.hasImageExtension(item.id)) {
95+
return $(`<span><img src='${this.streamUrl}?path=${item.id}' style='max-width: 20px; max-height: 20px;' /> ${item.id}</span>`)
96+
}
97+
return $(`<span><i class='fa fa-file'></i> ${item.id}</span>`)
98+
},
99+
escapeMarkup: (markup) => markup
118100
})
119101

120-
var extension = path.split('.').pop().toLowerCase()
121-
122-
return (imageExtensions.indexOf(extension) !== -1)
123-
}
124-
125-
initSelectize() {
126-
var self = this;
127-
this.destroySelectize();
128-
this.searchUrl = '/filemanager/api/search';
129-
this.streamUrl = '/filemanager/api/stream';
130-
131-
if (this.schema && this.schema.searchUrl) {
132-
this.searchUrl = this.schema.searchUrl;
133-
}
134-
135-
if (this.schema && this.schema.streamUrl) {
136-
this.streamUrl = this.schema.streamUrl;
137-
}
102+
this.select2instance.on('select2:select', (e) => {
103+
this.input.value = e.params.data.id
104+
this.onInputChange()
105+
})
138106

139-
var firstLoad = false;
107+
this.select2instance.on('select2:clear', (e) => {
108+
this.input.value = ''
109+
this.onInputChange()
140110

141-
this.selectize = $(this.input).selectize({
142-
valueField: 'fullPath',
143-
labelField: 'name',
144-
searchField: 'name',
145-
placeholder: 'Search for a file...',
146-
maxItems: 1,
147-
plugins: ['remove_button'],
148-
preload: false,
149-
options: [],
150-
create: true,
151-
persist: false,
152-
render: {
153-
item: function (item, escape) {
154-
if (self.hasImageExtension(item.fullPath)) {
155-
return '<div class="" style="height: 70px">' +
156-
'<img class="pull-left img-responsive" alt="flysystem image" style="max-width: 100px; max-height: 70px" src="' + escape(self.streamUrl) + '?path=' + escape(item.fullPath) + '" />' +
157-
'<span class="">' + escape(item.name) + '</span><br/>' +
158-
'</div>';
159-
}
111+
setTimeout(() => {
112+
this.select2instance.select2('close');
113+
})
114+
})
160115

161-
return '<span><i class="fa fa-file"></i> ' + escape(item.name) + '</span>';
162-
},
163-
option: function (item, escape) {
164-
if (self.hasThumbnailPreview(item)) {
165-
return '<div class="col-xs-6 col-sm-4 col-md-3 col-lg-2" style="height: 150px">' +
166-
'<img class="img-responsive" alt="flysystem image" style="max-height: 100px" src="' + escape(item.extraMetadata.thumbnail) + '" />' +
167-
'<span class="">' + escape(item.name) + '</span>' +
168-
'</div>';
169-
}
116+
this.setOption(this.input.value)
117+
}
170118

171-
return '<span><i class="fa fa-file"> ' + escape(item.name) + '</span>';
172-
}
173-
},
174-
onLoad: function () {
175-
var selectize = this
176-
setTimeout(function () {
177-
selectize.open()
178-
}, 0)
179-
},
180-
load: function (query, callback) {
181-
var selectize = this;
182-
$.ajax({
183-
url: self.searchUrl,
184-
type: 'GET',
185-
dataType: 'json',
186-
data: {
187-
q: query
188-
},
189-
error: function (e) {
190-
console.log('error', e)
191-
},
192-
success: function (data) {
193-
callback(data);
194-
if (!firstLoad) {
195-
selectize.setValue(self.input.value);
196-
firstLoad = true;
197-
self.onInputChange();
198-
}
199-
}
200-
});
201-
},
202-
onChange: function () {
203-
self.input.value = this.getValue();
204-
self.onInputChange();
205-
}
206-
});
119+
setOption(value) {
120+
const option = new Option(value, value, true, true)
121+
$(this.selectInput).append(option)
207122
}
208123

209124
onInputChange() {
210-
this.value = this.input.value;
211-
this.onChange(true);
212-
}
125+
this.value = this.select2instance.val()
213126

214-
enable() {
215-
if (!this.always_disabled) {
216-
this.input.disabled = false;
217-
if (this.selectize) {
218-
this.selectize[0].selectize.unlock();
219-
}
220-
super.enable();
127+
if (this.value !== '') {
128+
this.setOption(this.value)
221129
}
222-
}
223130

224-
disable(always_disabled) {
225-
if (always_disabled) this.always_disabled = true;
226-
this.input.disabled = true;
227-
if (this.selectize) {
228-
this.selectize[0].selectize.lock();
229-
}
230-
super.disable();
131+
this.onChange(true)
231132
}
232133

233134
destroy() {
234-
this.destroySelectize();
235-
if (this.label && this.label.parentNode) this.label.parentNode.removeChild(this.label);
236-
if (this.description && this.description.parentNode) this.description.parentNode.removeChild(this.description);
237-
if (this.input && this.input.parentNode) this.input.parentNode.removeChild(this.input);
238-
super.destroy();
135+
this.destroySelect2()
136+
if (this.label && this.label.parentNode) this.label.parentNode.removeChild(this.label)
137+
if (this.description && this.description.parentNode) this.description.parentNode.removeChild(this.description)
138+
if (this.input && this.input.parentNode) this.input.parentNode.removeChild(this.input)
139+
super.destroy()
239140
}
240141

241-
destroySelectize() {
242-
if (this.selectize) {
243-
this.selectize[0].selectize.destroy();
244-
this.selectize = null;
142+
destroySelect2() {
143+
if ($(this.input).data('select2')) {
144+
$(this.input).select2('destroy')
245145
}
246146
}
247147
}
248148

249-
JSONEditor.defaults.editors.flysystem = FlysystemEditor;
149+
JSONEditor.defaults.editors.flysystem = FlysystemEditor
250150

251-
// Make it compatible with old widgets
252151
JSONEditor.defaults.resolvers.unshift(function (schema) {
253-
if (schema.type === "string" && schema.format === "flysystem") {
254-
return "flysystem";
152+
if (schema.type === 'string' && schema.format === 'flysystem') {
153+
return 'flysystem'
255154
}
256-
});
155+
})

0 commit comments

Comments
 (0)