Input WYSIWYG: Added dynamic options for entity selector popups
So that multiple elements on the page can share the same popup, with different search options.
This commit is contained in:
parent
c07aa056c2
commit
2fbed3919b
|
@ -15,8 +15,15 @@ export class EntitySelectorPopup extends Component {
|
||||||
window.$events.listen('entity-select-confirm', this.handleConfirmedSelection.bind(this));
|
window.$events.listen('entity-select-confirm', this.handleConfirmedSelection.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
show(callback, searchText = '') {
|
/**
|
||||||
|
* Show the selector popup.
|
||||||
|
* @param {Function} callback
|
||||||
|
* @param {String} searchText
|
||||||
|
* @param {EntitySelectorSearchOptions} searchOptions
|
||||||
|
*/
|
||||||
|
show(callback, searchText = '', searchOptions = {}) {
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
|
this.getSelector().configureSearchOptions(searchOptions);
|
||||||
this.getPopup().show();
|
this.getPopup().show();
|
||||||
|
|
||||||
if (searchText) {
|
if (searchText) {
|
||||||
|
|
|
@ -1,6 +1,13 @@
|
||||||
import {onChildEvent} from '../services/dom';
|
import {onChildEvent} from '../services/dom';
|
||||||
import {Component} from './component';
|
import {Component} from './component';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef EntitySelectorSearchOptions
|
||||||
|
* @property entityTypes string
|
||||||
|
* @property entityPermission string
|
||||||
|
* @property searchEndpoint string
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Entity Selector
|
* Entity Selector
|
||||||
*/
|
*/
|
||||||
|
@ -8,21 +15,35 @@ export class EntitySelector extends Component {
|
||||||
|
|
||||||
setup() {
|
setup() {
|
||||||
this.elem = this.$el;
|
this.elem = this.$el;
|
||||||
this.entityTypes = this.$opts.entityTypes || 'page,book,chapter';
|
|
||||||
this.entityPermission = this.$opts.entityPermission || 'view';
|
|
||||||
this.searchEndpoint = this.$opts.searchEndpoint || '/search/entity-selector';
|
|
||||||
|
|
||||||
this.input = this.$refs.input;
|
this.input = this.$refs.input;
|
||||||
this.searchInput = this.$refs.search;
|
this.searchInput = this.$refs.search;
|
||||||
this.loading = this.$refs.loading;
|
this.loading = this.$refs.loading;
|
||||||
this.resultsContainer = this.$refs.results;
|
this.resultsContainer = this.$refs.results;
|
||||||
|
|
||||||
|
this.searchOptions = {
|
||||||
|
entityTypes: this.$opts.entityTypes || 'page,book,chapter',
|
||||||
|
entityPermission: this.$opts.entityPermission || 'view',
|
||||||
|
searchEndpoint: this.$opts.searchEndpoint || '',
|
||||||
|
};
|
||||||
|
|
||||||
this.search = '';
|
this.search = '';
|
||||||
this.lastClick = 0;
|
this.lastClick = 0;
|
||||||
|
|
||||||
this.setupListeners();
|
this.setupListeners();
|
||||||
this.showLoading();
|
this.showLoading();
|
||||||
this.initialLoad();
|
|
||||||
|
if (this.searchOptions.searchEndpoint) {
|
||||||
|
this.initialLoad();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {EntitySelectorSearchOptions} options
|
||||||
|
*/
|
||||||
|
configureSearchOptions(options) {
|
||||||
|
Object.assign(this.searchOptions, options);
|
||||||
|
this.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
setupListeners() {
|
setupListeners() {
|
||||||
|
@ -103,6 +124,10 @@ export class EntitySelector extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
initialLoad() {
|
initialLoad() {
|
||||||
|
if (!this.searchOptions.searchEndpoint) {
|
||||||
|
throw new Error('Search endpoint not set for entity-selector load');
|
||||||
|
}
|
||||||
|
|
||||||
window.$http.get(this.searchUrl()).then(resp => {
|
window.$http.get(this.searchUrl()).then(resp => {
|
||||||
this.resultsContainer.innerHTML = resp.data;
|
this.resultsContainer.innerHTML = resp.data;
|
||||||
this.hideLoading();
|
this.hideLoading();
|
||||||
|
@ -110,10 +135,15 @@ export class EntitySelector extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
searchUrl() {
|
searchUrl() {
|
||||||
return `${this.searchEndpoint}?types=${encodeURIComponent(this.entityTypes)}&permission=${encodeURIComponent(this.entityPermission)}`;
|
const query = `types=${encodeURIComponent(this.searchOptions.entityTypes)}&permission=${encodeURIComponent(this.searchOptions.entityPermission)}`;
|
||||||
|
return `${this.searchOptions.searchEndpoint}?${query}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
searchEntities(searchTerm) {
|
searchEntities(searchTerm) {
|
||||||
|
if (!this.searchOptions.searchEndpoint) {
|
||||||
|
throw new Error('Search endpoint not set for entity-selector load');
|
||||||
|
}
|
||||||
|
|
||||||
this.input.value = '';
|
this.input.value = '';
|
||||||
const url = `${this.searchUrl()}&term=${encodeURIComponent(searchTerm)}`;
|
const url = `${this.searchUrl()}&term=${encodeURIComponent(searchTerm)}`;
|
||||||
window.$http.get(url).then(resp => {
|
window.$http.get(url).then(resp => {
|
||||||
|
|
|
@ -14,6 +14,8 @@ export class PagePicker extends Component {
|
||||||
this.defaultDisplay = this.$refs.defaultDisplay;
|
this.defaultDisplay = this.$refs.defaultDisplay;
|
||||||
this.buttonSep = this.$refs.buttonSeperator;
|
this.buttonSep = this.$refs.buttonSeperator;
|
||||||
|
|
||||||
|
this.selectorEndpoint = this.$opts.selectorEndpoint;
|
||||||
|
|
||||||
this.value = this.input.value;
|
this.value = this.input.value;
|
||||||
this.setupListeners();
|
this.setupListeners();
|
||||||
}
|
}
|
||||||
|
@ -33,6 +35,10 @@ export class PagePicker extends Component {
|
||||||
const selectorPopup = window.$components.first('entity-selector-popup');
|
const selectorPopup = window.$components.first('entity-selector-popup');
|
||||||
selectorPopup.show(entity => {
|
selectorPopup.show(entity => {
|
||||||
this.setValue(entity.id, entity.name);
|
this.setValue(entity.id, entity.name);
|
||||||
|
}, '', {
|
||||||
|
searchEndpoint: this.selectorEndpoint,
|
||||||
|
entityTypes: 'page',
|
||||||
|
entityPermission: 'view',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -73,7 +73,11 @@ export class Actions {
|
||||||
const selectedText = selectionText || entity.name;
|
const selectedText = selectionText || entity.name;
|
||||||
const newText = `[${selectedText}](${entity.link})`;
|
const newText = `[${selectedText}](${entity.link})`;
|
||||||
this.#replaceSelection(newText, newText.length, selectionRange);
|
this.#replaceSelection(newText, newText.length, selectionRange);
|
||||||
}, selectionText);
|
}, selectionText, {
|
||||||
|
searchEndpoint: '/search/entity-selector',
|
||||||
|
entityTypes: 'page,book,chapter,bookshelf',
|
||||||
|
entityPermission: 'view',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show draw.io if enabled and handle save.
|
// Show draw.io if enabled and handle save.
|
||||||
|
|
|
@ -85,7 +85,11 @@ function filePickerCallback(callback, value, meta) {
|
||||||
text: entity.name,
|
text: entity.name,
|
||||||
title: entity.name,
|
title: entity.name,
|
||||||
});
|
});
|
||||||
}, selectionText);
|
}, selectionText, {
|
||||||
|
searchEndpoint: '/search/entity-selector',
|
||||||
|
entityTypes: 'page,book,chapter,bookshelf',
|
||||||
|
entityPermission: 'view',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (meta.filetype === 'image') {
|
if (meta.filetype === 'image') {
|
||||||
|
|
|
@ -58,6 +58,10 @@ export function register(editor) {
|
||||||
|
|
||||||
editor.selection.collapse(false);
|
editor.selection.collapse(false);
|
||||||
editor.focus();
|
editor.focus();
|
||||||
}, selectionText);
|
}, selectionText, {
|
||||||
|
searchEndpoint: '/search/entity-selector',
|
||||||
|
entityTypes: 'page,book,chapter,bookshelf',
|
||||||
|
entityPermission: 'view',
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,6 +53,7 @@
|
||||||
'name' => 'default_template_id',
|
'name' => 'default_template_id',
|
||||||
'placeholder' => trans('entities.books_default_template_select'),
|
'placeholder' => trans('entities.books_default_template_select'),
|
||||||
'value' => $book->default_template_id ?? null,
|
'value' => $book->default_template_id ?? null,
|
||||||
|
'selectorEndpoint' => '/search/entity-selector-templates',
|
||||||
])
|
])
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -65,5 +66,5 @@
|
||||||
<button type="submit" class="button">{{ trans('entities.books_save') }}</button>
|
<button type="submit" class="button">{{ trans('entities.books_save') }}</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@include('entities.selector-popup', ['entityTypes' => 'page', 'selectorEndpoint' => '/search/entity-selector-templates'])
|
@include('entities.selector-popup')
|
||||||
@include('form.editor-translations')
|
@include('form.editor-translations')
|
|
@ -27,5 +27,5 @@
|
||||||
<button type="submit" class="button">{{ trans('entities.chapters_save') }}</button>
|
<button type="submit" class="button">{{ trans('entities.chapters_save') }}</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@include('entities.selector-popup', ['entityTypes' => 'page', 'selectorEndpoint' => '/search/entity-selector-templates'])
|
@include('entities.selector-popup')
|
||||||
@include('form.editor-translations')
|
@include('form.editor-translations')
|
|
@ -5,7 +5,7 @@
|
||||||
<div class="popup-title">{{ trans('entities.entity_select') }}</div>
|
<div class="popup-title">{{ trans('entities.entity_select') }}</div>
|
||||||
<button refs="popup@hide" type="button" class="popup-header-close">@icon('close')</button>
|
<button refs="popup@hide" type="button" class="popup-header-close">@icon('close')</button>
|
||||||
</div>
|
</div>
|
||||||
@include('entities.selector', ['name' => 'entity-selector'])
|
@include('entities.selector', ['name' => 'entity-selector', 'selectorEndpoint' => ''])
|
||||||
<div class="popup-footer">
|
<div class="popup-footer">
|
||||||
<button refs="entity-selector-popup@select" type="button" disabled class="button">{{ trans('common.select') }}</button>
|
<button refs="entity-selector-popup@select" type="button" disabled class="button">{{ trans('common.select') }}</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
|
|
||||||
{{--Depends on entity selector popup--}}
|
{{--Depends on entity selector popup--}}
|
||||||
<div component="page-picker">
|
<div component="page-picker"
|
||||||
|
option:page-picker:selector-endpoint="{{ $selectorEndpoint }}">
|
||||||
<div class="input-base overflow-hidden height-auto">
|
<div class="input-base overflow-hidden height-auto">
|
||||||
<span @if($value) hidden @endif refs="page-picker@default-display" class="text-muted italic">{{ $placeholder }}</span>
|
<span @if($value) hidden @endif refs="page-picker@default-display" class="text-muted italic">{{ $placeholder }}</span>
|
||||||
<a @if(!$value) hidden @endif href="{{ url('/link/' . $value) }}" target="_blank" rel="noopener" class="text-page" refs="page-picker@display">#{{$value}}, {{$value ? \BookStack\Entities\Models\Page::query()->visible()->find($value)->name ?? '' : '' }}</a>
|
<a @if(!$value) hidden @endif href="{{ url('/link/' . $value) }}" target="_blank" rel="noopener" class="text-page" refs="page-picker@display">#{{$value}}, {{$value ? \BookStack\Entities\Models\Page::query()->visible()->find($value)->name ?? '' : '' }}</a>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
@section('card')
|
@section('card')
|
||||||
<h1 id="customization" class="list-heading">{{ trans('settings.app_customization') }}</h1>
|
<h1 id="customization" class="list-heading">{{ trans('settings.app_customization') }}</h1>
|
||||||
<form action="{{ url("/settings/customization") }}" method="POST" enctype="multipart/form-data">
|
<form action="{{ url("/settings/customization") }}" method="POST" enctype="multipart/form-data">
|
||||||
{!! csrf_field() !!}
|
{{ csrf_field() }}
|
||||||
<input type="hidden" name="section" value="customization">
|
<input type="hidden" name="section" value="customization">
|
||||||
|
|
||||||
<div class="setting-list">
|
<div class="setting-list">
|
||||||
|
@ -133,7 +133,12 @@
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<div refs="setting-homepage-control@page-picker-container" style="display: none;" class="mt-m">
|
<div refs="setting-homepage-control@page-picker-container" style="display: none;" class="mt-m">
|
||||||
@include('form.page-picker', ['name' => 'setting-app-homepage', 'placeholder' => trans('settings.app_homepage_select'), 'value' => setting('app-homepage')])
|
@include('form.page-picker', [
|
||||||
|
'name' => 'setting-app-homepage',
|
||||||
|
'placeholder' => trans('settings.app_homepage_select'),
|
||||||
|
'value' => setting('app-homepage'),
|
||||||
|
'selectorEndpoint' => '/search/entity-selector',
|
||||||
|
])
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -168,5 +173,5 @@
|
||||||
@endsection
|
@endsection
|
||||||
|
|
||||||
@section('after-content')
|
@section('after-content')
|
||||||
@include('entities.selector-popup', ['entityTypes' => 'page'])
|
@include('entities.selector-popup')
|
||||||
@endsection
|
@endsection
|
||||||
|
|
|
@ -89,5 +89,5 @@
|
||||||
<button type="submit" class="button">{{ trans('entities.shelves_save') }}</button>
|
<button type="submit" class="button">{{ trans('entities.shelves_save') }}</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@include('entities.selector-popup', ['entityTypes' => 'page', 'selectorEndpoint' => '/search/entity-selector-templates'])
|
@include('entities.selector-popup')
|
||||||
@include('form.editor-translations')
|
@include('form.editor-translations')
|
Loading…
Reference in New Issue