update textcomplete

This commit is contained in:
Yuri Kuznetsov
2025-06-24 19:24:27 +03:00
parent f7523ecb62
commit 7724c3b625
8 changed files with 737 additions and 304 deletions
@@ -55,10 +55,6 @@
"exportsTo": "$",
"exportsAs": "ui"
},
"jquery-textcomplete": {
"exportsTo": "$.fn",
"exportsAs": "textcomplete"
},
"autocomplete": {
"exportsTo": "$.fn",
"exportsAs": "autocomplete"
@@ -80,6 +76,12 @@
"@shopify/draggable": {
"devPath": "client/lib/original/shopify-draggable.js"
},
"@textcomplete/core": {
"devPath": "client/lib/original/textcomplete-core.js"
},
"@textcomplete/textarea": {
"devPath": "client/lib/original/textcomplete-textarea.js"
},
"autonumeric": {},
"intl-tel-input": {
"exportsTo": "window",
+9 -3
View File
@@ -119,7 +119,7 @@ class TextFieldView extends BaseFieldView {
previewButtonElement
/**
* @private
* @protected
* @type {HTMLTextAreaElement}
*/
textAreaElement
@@ -151,6 +151,8 @@ class TextFieldView extends BaseFieldView {
if (this.textAreaElement) {
this.textAreaElement.removeEventListener('keydown', this.onKeyDownMarkdownBind);
this.textAreaElement = undefined;
}
});
@@ -354,6 +356,12 @@ class TextFieldView extends BaseFieldView {
}
afterRender() {
this.textAreaElement = undefined;
if (this.mode === this.MODE_EDIT) {
this.textAreaElement = this.element ? this.element.querySelector('textarea') : undefined;
}
super.afterRender();
if (this.isReadMode()) {
@@ -397,8 +405,6 @@ class TextFieldView extends BaseFieldView {
this.previewButtonElement = this.element ?
this.element.querySelector('a[data-action="previewText"]') : undefined;
this.textAreaElement = this.element ? this.element.querySelector('textarea') : undefined;
const textAreaElement = this.textAreaElement
if (this.params.attachmentField && textAreaElement) {
+76 -28
View File
@@ -27,11 +27,17 @@
************************************************************************/
import TextFieldView from 'views/fields/text';
// noinspection ES6UnusedImports
import Textcomplete from 'lib!jquery-textcomplete';
import {Textcomplete} from '@textcomplete/core';
import {TextareaEditor} from '@textcomplete/textarea';
class NotePostFieldView extends TextFieldView {
/**
* @private
* @type {Textcomplete}
*/
textcomplete
setup() {
super.setup();
@@ -40,6 +46,14 @@ class NotePostFieldView extends TextFieldView {
this.addHandler('paste', 'textarea', (/** ClipboardEvent */event) => this.handlePaste(event));
}
onRemove() {
super.onRemove();
if (this.textcomplete) {
this.textcomplete.destroy();
}
}
/**
* @return {HTMLTextAreaElement}
*/
@@ -139,35 +153,69 @@ class NotePostFieldView extends TextFieldView {
return url;
};
// noinspection JSUnresolvedReference
this.$element.textcomplete([{
match: /(^|\s)@(\w[\w@.-]*)$/,
index: 2, // @todo Revise.
search: (term, callback) => {
if (term.length === 0) {
callback([]);
const editor = new TextareaEditor(this.textAreaElement);
return;
let bypass = false;
this.textcomplete = new Textcomplete(
editor,
[
{
match: /(^|\s)@(\w[\w@.-]*)$/,
index: 2,
search: (term, callback) => {
if (term.length === 0 || bypass) {
callback([]);
return;
}
Espo.Ajax.getRequest(buildUserListUrl(term))
.then(data => callback(data.list));
},
template: mention => {
const avatar = this.getHelper().getAvatarHtml(mention.id, 'medium', 16, 'avatar-link');
const name = this.getHelper().escapeString(mention.name);
const username = this.getHelper().escapeString(mention.userName);
return `${avatar + name} <span class="text-muted">@${username}</span>`;
},
replace: it => {
return '$1@' + it.userName + '';
},
}
],
{
dropdown: {
item: {
className: "textcomplete-item",
activeClassName: "textcomplete-item active",
},
style: {
zIndex: '1100',
},
}
Espo.Ajax.getRequest(buildUserListUrl(term))
.then(data => callback(data.list));
},
template: mention => {
return this.getHelper().getAvatarHtml(mention.id, 'medium', 16, 'avatar-link') +
this.getHelper().escapeString(mention.name) +
' <span class="text-muted">@' +
this.getHelper().escapeString(mention.userName) + '</span>';
},
replace: o => {
return '$1@' + o.userName + '';
},
}], {zIndex: 1100});
this.on('remove', () => {
if (this.$element.length) {
this.$element.textcomplete('destroy');
}
);
this.textcomplete.on('select', () => {
bypass = true;
setTimeout(() => {
bypass = false;
}, 100);
});
this.textAreaElement.addEventListener('blur', () => {
bypass = true;
setTimeout(() => {
bypass = false;
}, 200);
setTimeout(() => {
this.textcomplete?.hide();
}, 150);
});
}
-12
View File
@@ -2496,14 +2496,6 @@ div.field,
}
}
.textcomplete-item {
> a {
img.avatar {
margin-right: 5px;
}
}
}
td.cell {
> img.avatar-link {
margin-right: var(--6px);
@@ -2636,10 +2628,6 @@ div[data-scope="ApiUser"] {
}
}
.textcomplete-item a {
cursor: default;
}
.complex-text-container.cut {
position: relative;
+30
View File
@@ -311,3 +311,33 @@ ul.dropdown-menu {
display: none;
}
}
.textcomplete-dropdown {
.textcomplete-item {
> span {
display: block;
cursor: pointer;
padding: var(--4px) var(--20px);
overflow: hidden;
text-overflow: ellipsis;
img.avatar {
margin-right: var(--5px);
}
}
&:hover {
> span {
background-color: var(--dropdown-link-hover-bg);
}
}
}
&:not(:has(> li:hover)) {
.textcomplete-item {
&.active {
background-color: var(--gray-lighter);
}
}
}
}
+12 -5
View File
@@ -58,11 +58,6 @@
"amdId": "autocomplete",
"suppressAmd": true
},
{
"src": "node_modules/jquery-textcomplete/dist/jquery.textcomplete.js",
"bundle": true,
"amdId": "jquery-textcomplete"
},
{
"src": "node_modules/bootstrap/dist/js/bootstrap-for-espo.js",
"bundle": true
@@ -135,6 +130,18 @@
"amdId": "@shopify/draggable",
"prepareCommand": "npx rollup node_modules/@shopify/draggable/build/esm/index.mjs --format amd --file client/lib/original/shopify-draggable.js --amd.id @shopify/draggable"
},
{
"src": "client/lib/original/textcomplete-core.js",
"bundle": true,
"amdId": "@textcomplete/core",
"prepareCommand": "npx rollup node_modules/@textcomplete/core/dist/index.js --format amd --file client/lib/original/textcomplete-core.js --amd.id @textcomplete/core --plugin @rollup/plugin-node-resolve --plugin @rollup/plugin-commonjs"
},
{
"src": "client/lib/original/textcomplete-textarea.js",
"bundle": true,
"amdId": "@textcomplete/textarea",
"prepareCommand": "npx rollup node_modules/@textcomplete/textarea/dist/index.js --format amd --file client/lib/original/textcomplete-textarea.js --amd.id @textcomplete/textarea --plugin @rollup/plugin-node-resolve --plugin @rollup/plugin-commonjs --external @textcomplete/core"
},
{
"src": "node_modules/summernote/dist/summernote.js",
"amdId": "summernote",
+599 -250
View File
File diff suppressed because it is too large Load Diff
+5 -2
View File
@@ -19,6 +19,8 @@
"author": "Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko",
"license": "AGPL-3.0-or-later",
"devDependencies": {
"@rollup/plugin-commonjs": "^28.0.6",
"@rollup/plugin-node-resolve": "^16.0.1",
"archiver": "^5.3.0",
"chromedriver": "^127.0.1",
"espo-frontend-build-tools": "github:espocrm/frontend-build-tools#0.2.2",
@@ -37,13 +39,15 @@
"jasmine-core": "^5.2.0",
"js-yaml": "^3.13.1",
"pofile": "^1.1.3",
"rollup": "^4.43.0",
"rollup": "^4.44.0",
"tar": "^6.1.11"
},
"dependencies": {
"@fullcalendar/moment": "^6.1.8",
"@fullcalendar/moment-timezone": "^6.1.8",
"@shopify/draggable": "^1.1.4",
"@textcomplete/core": "^0.1.13",
"@textcomplete/textarea": "^0.1.13",
"ace-builds": "^1.4.12",
"autobahn-espo": "github:yurikuzn/autobahn-espo#0.1.0",
"autonumeric": "^4.6.0",
@@ -65,7 +69,6 @@
"handlebars": "^4.7.7",
"intl-tel-input": "^18.2.1",
"jquery": "^3.7.0",
"jquery-textcomplete": "^1.8.5",
"jquery-ui-espo": "github:yurikuzn/jquery-ui-espo#0.2.2",
"jquery-ui-touch-punch": "^0.2.3",
"js-base64": "^3.7.2",