Skip to content
Snippets Groups Projects
Commit 4eeeff70 authored by Reiter, Christoph's avatar Reiter, Christoph :snake:
Browse files

Merge branch 'resource-select' into 'master'

Add a new resource-select package/web-component

See merge request !161
parents c595eccf 433d5c85
No related branches found
No related tags found
1 merge request!161Add a new resource-select package/web-component
Pipeline #96382 passed
{
"select": {
"placeholder": "Bitte wählen Sie einen Eintrag aus",
"loading": "Wird geladen..."
}
}
/**
* Content from https://github.com/select2/select2/blob/master/src/js/select2/i18n/en.js
*/
export default function () {
// English
return {
errorLoading: function () {
return 'The results could not be loaded.';
},
inputTooLong: function (args) {
var overChars = args.input.length - args.maximum;
var message = 'Please delete ' + overChars + ' character';
if (overChars != 1) {
message += 's';
}
return message;
},
inputTooShort: function (args) {
var remainingChars = args.minimum - args.input.length;
var message = 'Please enter ' + remainingChars + ' or more characters';
return message;
},
loadingMore: function () {
return 'Loading more results…';
},
maximumSelected: function (args) {
var message = 'You can only select ' + args.maximum + ' item';
if (args.maximum != 1) {
message += 's';
}
return message;
},
noResults: function () {
return 'No results found';
},
searching: function () {
return 'Searching…';
},
removeAllItems: function () {
return 'Remove selection';
},
};
}
{
"select": {
"placeholder": "Please select an entry",
"loading": "Loading..."
}
}
import {ResourceSelect} from './resource-select.js';
export {ResourceSelect};
import $ from 'jquery';
import select2 from 'select2';
import select2CSSPath from 'select2/dist/css/select2.min.css';
import {createInstance} from './i18n.js';
import {css, html} from 'lit';
import * as commonUtils from '@dbp-toolkit/common/utils';
import * as commonStyles from '@dbp-toolkit/common/styles';
import select2LangDe from '@dbp-toolkit/resource-select/src/i18n/de/select2';
import select2LangEn from '@dbp-toolkit/resource-select/src/i18n/en/select2';
import {AdapterLitElement} from '@dbp-toolkit/provider/src/adapter-lit-element';
import * as hydra from './hydra.js';
export class ResourceSelect extends AdapterLitElement {
constructor() {
super();
this._i18n = createInstance();
this._resources = [];
this._url = null;
// For some reason using the same ID on the whole page twice breaks select2 (regardless if they are in different custom elements)
this._selectId = 'select-resource-' + commonUtils.makeId(24);
this.auth = {};
this.lang = this._i18n.language;
this.entryPointUrl = null;
this.resourcePath = null;
this.value = null;
this.valueObject = null;
this._onDocumentClicked = this._onDocumentClicked.bind(this);
select2(window, $);
}
static get properties() {
return {
...super.properties,
lang: {type: String},
auth: {type: Object},
entryPointUrl: {type: String, attribute: 'entry-point-url'},
resourcePath: {type: String, attribute: 'resource-path'},
value: {type: String, reflect: true},
};
}
_getSelect2() {
return this._$('#' + this._selectId);
}
_$(selector) {
return $(this.renderRoot.querySelector(selector));
}
_IsSelect2Initialized(elm) {
return elm !== null && elm.hasClass('select2-hidden-accessible');
}
connectedCallback() {
super.connectedCallback();
document.addEventListener('click', this._onDocumentClicked);
this._updateAll();
}
disconnectedCallback() {
document.removeEventListener('click', this._onDocumentClicked);
super.disconnectedCallback();
}
_onDocumentClicked(ev) {
// Close the popup when clicking outside of select2
if (!ev.composedPath().includes(this)) {
const $select = this._getSelect2();
if ($select.length && this._IsSelect2Initialized($select)) {
$select.select2('close');
}
}
}
_clearSelect2() {
const $select = this._getSelect2();
console.assert($select.length, 'select2 missing');
// we need to destroy Select2 and remove the event listeners before we can initialize it again
if (this._IsSelect2Initialized($select)) {
$select.off('select2:select');
$select.empty().trigger('change');
$select.select2('destroy');
}
}
_getUrl() {
if (this.entryPointUrl === null) {
return null;
}
let url = this.entryPointUrl;
if (this.resourcePath !== null) {
url = new URL(this.resourcePath, this.entryPointUrl).href;
}
let detail = {
url: url,
};
const event = new CustomEvent('build-url', {
detail: detail,
});
this.dispatchEvent(event);
return detail.url;
}
_getText(resource) {
let detail = {
text: resource.name ?? null,
object: resource,
};
const event = new CustomEvent('format-resource', {
detail: detail,
});
this.dispatchEvent(event);
return detail.text;
}
_setValue(value) {
let changed = false;
changed = this.value !== value;
this.value = value;
let found = null;
for (let res of this._resources) {
if (res['@id'] === this.value) {
found = res;
break;
}
}
changed = changed || this.valueObject !== found;
this.valueObject = found;
if (!changed) {
return;
}
const event = new CustomEvent('change', {
bubbles: true,
composed: true,
detail: {
value: this.value,
object: this.valueObject,
},
});
this.dispatchEvent(event);
}
async _updateAll() {
this._setValue(this.value);
if (!this.auth.token) {
await this._setSelect2Loading();
return;
}
await this._updateResources();
await this._updateSelect2();
}
async _setSelect2Loading() {
await this.updateComplete;
const i18n = this._i18n;
const $select = this._getSelect2();
console.assert($select.length, 'select2 missing');
// Show an empty select until we load the resources
this._clearSelect2();
$select.select2({
width: '100%',
language: this.lang === 'de' ? select2LangDe() : select2LangEn(),
placeholder: i18n.t('select.loading'),
data: [],
disabled: true,
});
}
async _updateResources() {
let url = this._getUrl();
if (url === null || url === this._url) {
return;
}
this._resources = await hydra.getCollection(url, this.auth.token);
this._url = url;
this._setValue(this.value);
}
async _updateSelect2() {
await this.updateComplete;
const i18n = this._i18n;
const $select = this._getSelect2();
console.assert($select.length, 'select2 missing');
const data = this._resources.map((item) => {
return {id: item['@id'], text: this._getText(item)};
});
data.sort((a, b) => {
return a.text < b.text ? -1 : a.text > b.text ? 1 : 0;
});
this._clearSelect2();
$select
.select2({
width: '100%',
language: this.lang === 'de' ? select2LangDe() : select2LangEn(),
placeholder: i18n.t('select.placeholder'),
dropdownParent: this._$('#select-resource-dropdown'),
data: data,
disabled: false,
})
.on('select2:select', () => {
let id = $select.select2('data')[0].id;
this._setValue(id);
});
// If none is selected, default to the first one
if (this.value === null && data.length) {
this._setValue(data[0].id);
}
// Apply the selection
$select.val(this.value).trigger('change');
}
update(changedProperties) {
if (changedProperties.has('lang')) {
this._i18n.changeLanguage(this.lang);
}
if (
changedProperties.has('lang') ||
changedProperties.has('value') ||
changedProperties.has('resourcePath') ||
changedProperties.has('entryPointUrl') ||
changedProperties.has('auth')
) {
this._updateAll();
}
super.update(changedProperties);
}
static get styles() {
return [
commonStyles.getThemeCSS(),
commonStyles.getGeneralCSS(),
commonStyles.getNotificationCSS(),
commonStyles.getSelect2CSS(),
// language=css
css``,
];
}
render() {
const select2CSS = commonUtils.getAssetURL(select2CSSPath);
return html`
<link rel="stylesheet" href="${select2CSS}" />
<div class="select">
<div class="select2-control control">
<select
id="${this._selectId}"
name="select-resources"
class="select"
style="visibility: hidden;"></select>
</div>
<div id="select-resource-dropdown"></div>
</div>
`;
}
}
import {assert} from '@esm-bundle/chai';
import '../src/dbp-resource-select.js';
import '../src/demo.js';
suite('dbp-resource-select basics', () => {
let node;
setup(async () => {
node = document.createElement('dbp-resource-select');
document.body.appendChild(node);
await node.updateComplete;
});
teardown(() => {
node.remove();
});
test('should render', () => {
assert.isNotNull(node.shadowRoot);
});
});
suite('dbp-resource-select-demo basics', () => {
let node;
setup(async () => {
node = document.createElement('dbp-resource-select-demo');
document.body.appendChild(node);
await node.updateComplete;
});
teardown(() => {
node.remove();
});
test('should render', () => {
assert.isNotNull(node.shadowRoot);
});
});
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment