From b3bd8c3e8d367975980043e772f7cd78b7f96bc6 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Thu, 22 Oct 2020 16:21:03 +0200 Subject: Feature: support any tag separator So it allows to have multiple words tags. Breaking change: commas ',' are no longer a default separator. Fixes #594 --- assets/default/js/base.js | 28 ++++++++++++++++------------ assets/default/scss/shaarli.scss | 10 ++++++++++ 2 files changed, 26 insertions(+), 12 deletions(-) (limited to 'assets') diff --git a/assets/default/js/base.js b/assets/default/js/base.js index 66badfb2..e7bf4909 100644 --- a/assets/default/js/base.js +++ b/assets/default/js/base.js @@ -42,19 +42,20 @@ function refreshToken(basePath, callback) { xhr.send(); } -function createAwesompleteInstance(element, tags = []) { +function createAwesompleteInstance(element, separator, tags = []) { const awesome = new Awesomplete(Awesomplete.$(element)); - // Tags are separated by a space - awesome.filter = (text, input) => Awesomplete.FILTER_CONTAINS(text, input.match(/[^ ]*$/)[0]); + + // Tags are separated by separator + awesome.filter = (text, input) => Awesomplete.FILTER_CONTAINS(text, input.match(new RegExp(`[^${separator}]*$`))[0]); // Insert new selected tag in the input awesome.replace = (text) => { - const before = awesome.input.value.match(/^.+ \s*|/)[0]; - awesome.input.value = `${before}${text} `; + const before = awesome.input.value.match(new RegExp(`^.+${separator}+|`))[0]; + awesome.input.value = `${before}${text}${separator}`; }; // Highlight found items - awesome.item = (text, input) => Awesomplete.ITEM(text, input.match(/[^ ]*$/)[0]); + awesome.item = (text, input) => Awesomplete.ITEM(text, input.match(new RegExp(`[^${separator}]*$`))[0]); // Don't display already selected items - const reg = /(\w+) /g; + const reg = new RegExp(`/(\w+)${separator}/g`); let match; awesome.data = (item, input) => { while ((match = reg.exec(input))) { @@ -78,13 +79,14 @@ function createAwesompleteInstance(element, tags = []) { * @param selector CSS selector * @param tags Array of tags * @param instances List of existing awesomplete instances + * @param separator Tags separator character */ -function updateAwesompleteList(selector, tags, instances) { +function updateAwesompleteList(selector, tags, instances, separator) { if (instances.length === 0) { // First load: create Awesomplete instances const elements = document.querySelectorAll(selector); [...elements].forEach((element) => { - instances.push(createAwesompleteInstance(element, tags)); + instances.push(createAwesompleteInstance(element, separator, tags)); }); } else { // Update awesomplete tag list @@ -214,6 +216,8 @@ function init(description) { (() => { const basePath = document.querySelector('input[name="js_base_path"]').value; + const tagsSeparatorElement = document.querySelector('input[name="tags_separator"]'); + const tagsSeparator = tagsSeparatorElement ? tagsSeparatorElement.value || '\s' : '\s'; /** * Handle responsive menu. @@ -575,7 +579,7 @@ function init(description) { // Refresh awesomplete values existingTags = existingTags.map((tag) => (tag === fromtag ? totag : tag)); - awesomepletes = updateAwesompleteList('.rename-tag-input', existingTags, awesomepletes); + awesomepletes = updateAwesompleteList('.rename-tag-input', existingTags, awesomepletes, tagsSeparator); } }; xhr.send(`renametag=1&fromtag=${fromtagUrl}&totag=${encodeURIComponent(totag)}&token=${refreshedToken}`); @@ -615,14 +619,14 @@ function init(description) { refreshToken(basePath); existingTags = existingTags.filter((tagItem) => tagItem !== tag); - awesomepletes = updateAwesompleteList('.rename-tag-input', existingTags, awesomepletes); + awesomepletes = updateAwesompleteList('.rename-tag-input', existingTags, awesomepletes, tagsSeparator); } }); }); const autocompleteFields = document.querySelectorAll('input[data-multiple]'); [...autocompleteFields].forEach((autocompleteField) => { - awesomepletes.push(createAwesompleteInstance(autocompleteField)); + awesomepletes.push(createAwesompleteInstance(autocompleteField, tagsSeparator)); }); const exportForm = document.querySelector('#exportform'); diff --git a/assets/default/scss/shaarli.scss b/assets/default/scss/shaarli.scss index a7f091e9..ed774a9d 100644 --- a/assets/default/scss/shaarli.scss +++ b/assets/default/scss/shaarli.scss @@ -139,6 +139,16 @@ body, } } +.page-form, +.pure-alert { + code { + display: inline-block; + padding: 0 2px; + color: $dark-grey; + background-color: var(--background-color); + } +} + // Make pure-extras alert closable. .pure-alert-closable { .fa-times { -- cgit v1.2.3 From df9aac5b6406bf192f2e4e0987e25d0de88480df Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Thu, 5 Nov 2020 18:16:52 +0100 Subject: Tags separator: vintage theme compatibility --- assets/default/js/base.js | 2 +- assets/vintage/js/base.js | 45 ++++++++++++++++++++++++++------------------- 2 files changed, 27 insertions(+), 20 deletions(-) (limited to 'assets') diff --git a/assets/default/js/base.js b/assets/default/js/base.js index e7bf4909..37069d69 100644 --- a/assets/default/js/base.js +++ b/assets/default/js/base.js @@ -217,7 +217,7 @@ function init(description) { (() => { const basePath = document.querySelector('input[name="js_base_path"]').value; const tagsSeparatorElement = document.querySelector('input[name="tags_separator"]'); - const tagsSeparator = tagsSeparatorElement ? tagsSeparatorElement.value || '\s' : '\s'; + const tagsSeparator = tagsSeparatorElement ? tagsSeparatorElement.value || ' ' : ' '; /** * Handle responsive menu. diff --git a/assets/vintage/js/base.js b/assets/vintage/js/base.js index 66830b59..15b664ce 100644 --- a/assets/vintage/js/base.js +++ b/assets/vintage/js/base.js @@ -2,29 +2,36 @@ import Awesomplete from 'awesomplete'; import 'awesomplete/awesomplete.css'; (() => { - const awp = Awesomplete.$; const autocompleteFields = document.querySelectorAll('input[data-multiple]'); + const tagsSeparatorElement = document.querySelector('input[name="tags_separator"]'); + const tagsSeparator = tagsSeparatorElement ? tagsSeparatorElement.value || " " : " "; + [...autocompleteFields].forEach((autocompleteField) => { - const awesomplete = new Awesomplete(awp(autocompleteField)); - awesomplete.filter = (text, input) => Awesomplete.FILTER_CONTAINS(text, input.match(/[^ ]*$/)[0]); - awesomplete.replace = (text) => { - const before = awesomplete.input.value.match(/^.+ \s*|/)[0]; - awesomplete.input.value = `${before}${text} `; - }; - awesomplete.minChars = 1; + const awesome = new Awesomplete(Awesomplete.$(autocompleteField)); - autocompleteField.addEventListener('input', () => { - const proposedTags = autocompleteField.getAttribute('data-list').replace(/,/g, '').split(' '); - const reg = /(\w+) /g; - let match; - while ((match = reg.exec(autocompleteField.value)) !== null) { - const id = proposedTags.indexOf(match[1]); - if (id !== -1) { - proposedTags.splice(id, 1); + // Tags are separated by separator + awesome.filter = (text, input) => Awesomplete.FILTER_CONTAINS( + text, + input.match(new RegExp(`[^${tagsSeparator}]*$`))[0]) + ; + // Insert new selected tag in the input + awesome.replace = (text) => { + const before = awesome.input.value.match(new RegExp(`^.+${tagsSeparator}+|`))[0]; + awesome.input.value = `${before}${text}${tagsSeparator}`; + }; + // Highlight found items + awesome.item = (text, input) => Awesomplete.ITEM(text, input.match(new RegExp(`[^${tagsSeparator}]*$`))[0]); + // Don't display already selected items + const reg = new RegExp(`/(\w+)${tagsSeparator}/g`); + let match; + awesome.data = (item, input) => { + while ((match = reg.exec(input))) { + if (item === match[1]) { + return ''; } } - - awesomplete.list = proposedTags; - }); + return item; + }; + awesome.minChars = 1; }); })(); -- cgit v1.2.3 From 8a1ce1da15fdbae99b24700b06f2008c7a657603 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Thu, 5 Nov 2020 19:08:38 +0100 Subject: ESLint --- assets/default/js/base.js | 3 ++- assets/vintage/js/base.js | 10 ++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'assets') diff --git a/assets/default/js/base.js b/assets/default/js/base.js index 37069d69..dd532bb7 100644 --- a/assets/default/js/base.js +++ b/assets/default/js/base.js @@ -55,7 +55,8 @@ function createAwesompleteInstance(element, separator, tags = []) { // Highlight found items awesome.item = (text, input) => Awesomplete.ITEM(text, input.match(new RegExp(`[^${separator}]*$`))[0]); // Don't display already selected items - const reg = new RegExp(`/(\w+)${separator}/g`); + // WARNING: pseudo classes does not seem to work with string litterals... + const reg = new RegExp(`([^${separator}]+)${separator}`, 'g'); let match; awesome.data = (item, input) => { while ((match = reg.exec(input))) { diff --git a/assets/vintage/js/base.js b/assets/vintage/js/base.js index 15b664ce..55f1c37d 100644 --- a/assets/vintage/js/base.js +++ b/assets/vintage/js/base.js @@ -4,7 +4,7 @@ import 'awesomplete/awesomplete.css'; (() => { const autocompleteFields = document.querySelectorAll('input[data-multiple]'); const tagsSeparatorElement = document.querySelector('input[name="tags_separator"]'); - const tagsSeparator = tagsSeparatorElement ? tagsSeparatorElement.value || " " : " "; + const tagsSeparator = tagsSeparatorElement ? tagsSeparatorElement.value || ' ' : ' '; [...autocompleteFields].forEach((autocompleteField) => { const awesome = new Awesomplete(Awesomplete.$(autocompleteField)); @@ -12,8 +12,8 @@ import 'awesomplete/awesomplete.css'; // Tags are separated by separator awesome.filter = (text, input) => Awesomplete.FILTER_CONTAINS( text, - input.match(new RegExp(`[^${tagsSeparator}]*$`))[0]) - ; + input.match(new RegExp(`[^${tagsSeparator}]*$`))[0], + ); // Insert new selected tag in the input awesome.replace = (text) => { const before = awesome.input.value.match(new RegExp(`^.+${tagsSeparator}+|`))[0]; @@ -21,8 +21,10 @@ import 'awesomplete/awesomplete.css'; }; // Highlight found items awesome.item = (text, input) => Awesomplete.ITEM(text, input.match(new RegExp(`[^${tagsSeparator}]*$`))[0]); + // Don't display already selected items - const reg = new RegExp(`/(\w+)${tagsSeparator}/g`); + // WARNING: pseudo classes does not seem to work with string litterals... + const reg = new RegExp(`([^${tagsSeparator}]+)${tagsSeparator}`, 'g'); let match; awesome.data = (item, input) => { while ((match = reg.exec(input))) { -- cgit v1.2.3