adb-shared 6.2.3 → 6.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/adb-shared.mjs +160 -74
- package/fesm2022/adb-shared.mjs.map +1 -1
- package/package.json +1 -1
- package/types/adb-shared.d.ts +8 -6
package/fesm2022/adb-shared.mjs
CHANGED
|
@@ -1780,46 +1780,90 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
|
|
|
1780
1780
|
}]
|
|
1781
1781
|
}] });
|
|
1782
1782
|
|
|
1783
|
+
const FORMAT_MARKERS = {
|
|
1784
|
+
italic: ['*', '_'],
|
|
1785
|
+
bold: ['**', '__']
|
|
1786
|
+
};
|
|
1787
|
+
const DEFAULT_MARKER = {
|
|
1788
|
+
italic: '*',
|
|
1789
|
+
bold: '**'
|
|
1790
|
+
};
|
|
1783
1791
|
class AdbRichEditorComponent {
|
|
1784
1792
|
constructor(el) {
|
|
1785
1793
|
this.el = el;
|
|
1794
|
+
this.text = '';
|
|
1786
1795
|
this.hasTaxon = false;
|
|
1787
1796
|
this.hasReference = false;
|
|
1788
1797
|
this.rows = 3;
|
|
1789
|
-
//
|
|
1798
|
+
// ---------------------------------------
|
|
1799
|
+
// ControlValueAccessor
|
|
1800
|
+
// ---------------------------------------
|
|
1790
1801
|
this.onChange = () => { };
|
|
1791
1802
|
this.onTouched = () => { };
|
|
1792
1803
|
}
|
|
1804
|
+
getTextarea() {
|
|
1805
|
+
return this.el.nativeElement.querySelector('textarea');
|
|
1806
|
+
}
|
|
1793
1807
|
onDoubleClick() {
|
|
1794
|
-
const textarea = this.
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1808
|
+
const textarea = this.getTextarea();
|
|
1809
|
+
if (!textarea)
|
|
1810
|
+
return;
|
|
1811
|
+
const start = textarea.selectionStart;
|
|
1812
|
+
let end = textarea.selectionEnd;
|
|
1813
|
+
while (end > start && textarea.value[end - 1] === ' ') {
|
|
1814
|
+
end--;
|
|
1799
1815
|
}
|
|
1800
|
-
textarea.setSelectionRange(
|
|
1816
|
+
textarea.setSelectionRange(start, end);
|
|
1801
1817
|
}
|
|
1802
1818
|
onItalicClick() {
|
|
1803
|
-
this.applyFormatting('
|
|
1819
|
+
this.applyFormatting('italic');
|
|
1804
1820
|
}
|
|
1805
1821
|
onBoldClick() {
|
|
1806
|
-
this.applyFormatting('
|
|
1807
|
-
}
|
|
1808
|
-
applyFormatting(
|
|
1809
|
-
const textarea = this.
|
|
1810
|
-
if (textarea)
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1822
|
+
this.applyFormatting('bold');
|
|
1823
|
+
}
|
|
1824
|
+
applyFormatting(type) {
|
|
1825
|
+
const textarea = this.getTextarea();
|
|
1826
|
+
if (!textarea)
|
|
1827
|
+
return;
|
|
1828
|
+
const start = textarea.selectionStart;
|
|
1829
|
+
const end = textarea.selectionEnd;
|
|
1830
|
+
const value = textarea.value;
|
|
1831
|
+
if (start === end) {
|
|
1832
|
+
const marker = DEFAULT_MARKER[type];
|
|
1833
|
+
const newValue = value.slice(0, start) +
|
|
1834
|
+
marker +
|
|
1835
|
+
marker +
|
|
1836
|
+
value.slice(start);
|
|
1837
|
+
this.updateValue(newValue);
|
|
1838
|
+
const cursor = start + marker.length;
|
|
1821
1839
|
textarea.focus();
|
|
1840
|
+
textarea.setSelectionRange(cursor, cursor);
|
|
1841
|
+
return;
|
|
1822
1842
|
}
|
|
1843
|
+
const existingMarker = this.findWrappingMarker(value, start, end, FORMAT_MARKERS[type]);
|
|
1844
|
+
const marker = existingMarker ?? DEFAULT_MARKER[type];
|
|
1845
|
+
const newValue = existingMarker
|
|
1846
|
+
? value.slice(0, start - marker.length) +
|
|
1847
|
+
value.slice(start, end) +
|
|
1848
|
+
value.slice(end + marker.length)
|
|
1849
|
+
: value.slice(0, start) +
|
|
1850
|
+
marker +
|
|
1851
|
+
value.slice(start, end) +
|
|
1852
|
+
marker +
|
|
1853
|
+
value.slice(end);
|
|
1854
|
+
this.updateValue(newValue);
|
|
1855
|
+
const cursorStart = existingMarker
|
|
1856
|
+
? start - marker.length
|
|
1857
|
+
: start + marker.length;
|
|
1858
|
+
const cursorEnd = existingMarker
|
|
1859
|
+
? end - marker.length
|
|
1860
|
+
: end + marker.length;
|
|
1861
|
+
textarea.focus();
|
|
1862
|
+
textarea.setSelectionRange(cursorStart, cursorEnd);
|
|
1863
|
+
}
|
|
1864
|
+
findWrappingMarker(value, start, end, markers) {
|
|
1865
|
+
return (markers.find(marker => value.substring(start - marker.length, start) === marker &&
|
|
1866
|
+
value.substring(end, end + marker.length) === marker) ?? null);
|
|
1823
1867
|
}
|
|
1824
1868
|
onTaxonClick() {
|
|
1825
1869
|
this.replaceMarked('[Name](taxon:0)');
|
|
@@ -1828,34 +1872,33 @@ class AdbRichEditorComponent {
|
|
|
1828
1872
|
this.replaceMarked('[Name](reference:0)');
|
|
1829
1873
|
}
|
|
1830
1874
|
replaceMarked(replaceTag) {
|
|
1831
|
-
const textarea = this.
|
|
1832
|
-
if (textarea)
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
this.onChange(this.text);
|
|
1875
|
+
const textarea = this.getTextarea();
|
|
1876
|
+
if (!textarea)
|
|
1877
|
+
return;
|
|
1878
|
+
const start = textarea.selectionStart;
|
|
1879
|
+
const end = textarea.selectionEnd;
|
|
1880
|
+
const value = textarea.value;
|
|
1881
|
+
const before = start > 0 ? value[start - 1] : '';
|
|
1882
|
+
const after = end < value.length ? value[end] : '';
|
|
1883
|
+
const spaceBefore = before && !/\s/.test(before) ? ' ' : '';
|
|
1884
|
+
const spaceAfter = after && !/\s/.test(after) ? ' ' : '';
|
|
1885
|
+
const newValue = value.slice(0, start) +
|
|
1886
|
+
spaceBefore +
|
|
1887
|
+
replaceTag +
|
|
1888
|
+
spaceAfter +
|
|
1889
|
+
value.slice(end);
|
|
1890
|
+
this.updateValue(newValue);
|
|
1891
|
+
const nameStart = newValue.indexOf('Name', start);
|
|
1892
|
+
if (nameStart === -1)
|
|
1893
|
+
return;
|
|
1894
|
+
const nameEnd = nameStart + 'Name'.length;
|
|
1895
|
+
requestAnimationFrame(() => {
|
|
1896
|
+
textarea.focus();
|
|
1897
|
+
textarea.setSelectionRange(nameStart, nameEnd);
|
|
1898
|
+
});
|
|
1856
1899
|
}
|
|
1857
1900
|
writeValue(value) {
|
|
1858
|
-
this.text = value;
|
|
1901
|
+
this.text = value ?? '';
|
|
1859
1902
|
}
|
|
1860
1903
|
registerOnChange(fn) {
|
|
1861
1904
|
this.onChange = fn;
|
|
@@ -1864,21 +1907,35 @@ class AdbRichEditorComponent {
|
|
|
1864
1907
|
this.onTouched = fn;
|
|
1865
1908
|
}
|
|
1866
1909
|
setDisabledState(isDisabled) {
|
|
1910
|
+
// Intentionally left minimal
|
|
1911
|
+
// Disabled state should be handled via template binding
|
|
1867
1912
|
}
|
|
1868
|
-
|
|
1913
|
+
onTextChange() {
|
|
1914
|
+
this.onChange(this.text);
|
|
1915
|
+
}
|
|
1916
|
+
updateValue(value) {
|
|
1917
|
+
this.text = value;
|
|
1918
|
+
this.onChange(this.text);
|
|
1869
1919
|
}
|
|
1920
|
+
ngOnDestroy() { }
|
|
1870
1921
|
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AdbRichEditorComponent, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1871
|
-
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.8", type: AdbRichEditorComponent, isStandalone: false, selector: "adb-rich-editor", inputs: { hasTaxon: "hasTaxon", hasReference: "hasReference", rows: "rows" }, providers: [
|
|
1872
|
-
|
|
1922
|
+
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.8", type: AdbRichEditorComponent, isStandalone: false, selector: "adb-rich-editor", inputs: { hasTaxon: "hasTaxon", hasReference: "hasReference", rows: "rows" }, providers: [
|
|
1923
|
+
{
|
|
1924
|
+
provide: NG_VALUE_ACCESSOR,
|
|
1925
|
+
useExisting: forwardRef((() => AdbRichEditorComponent)),
|
|
1873
1926
|
multi: true
|
|
1874
|
-
}
|
|
1927
|
+
}
|
|
1928
|
+
], ngImport: i0, template: "<div>\r\n <div class=\"d-flex justify-content-end gap-3 mb-1\">\r\n <button class=\"btn btn-secondary\" (click)=\"onItalicClick()\" title=\"Italic\" aria-label=\"Italic\" type=\"button\"><span class=\"fas fa-italic\"></span></button>\r\n <button class=\"btn btn-secondary\" (click)=\"onBoldClick()\" title=\"Italic\" aria-label=\"strong\" type=\"button\"><span class=\"fas fa-bold\"></span></button>\r\n @if (hasTaxon) {\r\n <button class=\"btn btn-secondary\" (click)=\"onTaxonClick()\" title=\"Taxon\" aria-label=\"Taxon\" type=\"button\"><span class=\"fas fa-bug\"></span></button>\r\n }\r\n @if (hasReference) {\r\n <button class=\"btn btn-secondary\" (click)=\"onReferenceClick()\" title=\"Reference\" aria-label=\"Reference\" type=\"button\"><span class=\"fas fa-asterisk\"></span></button>\r\n }\r\n </div>\r\n <textarea class=\"form-control\" [(ngModel)]=\"text\" (ngModelChange)=\"onTextChange()\" [rows]=\"rows\" (dblclick)=\"onDoubleClick()\"></textarea>\r\n</div>\r\n", dependencies: [{ kind: "directive", type: i4.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] }); }
|
|
1875
1929
|
}
|
|
1876
1930
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AdbRichEditorComponent, decorators: [{
|
|
1877
1931
|
type: Component,
|
|
1878
|
-
args: [{ selector: 'adb-rich-editor', providers: [
|
|
1879
|
-
|
|
1932
|
+
args: [{ selector: 'adb-rich-editor', providers: [
|
|
1933
|
+
{
|
|
1934
|
+
provide: NG_VALUE_ACCESSOR,
|
|
1935
|
+
useExisting: forwardRef((() => AdbRichEditorComponent)),
|
|
1880
1936
|
multi: true
|
|
1881
|
-
}
|
|
1937
|
+
}
|
|
1938
|
+
], standalone: false, template: "<div>\r\n <div class=\"d-flex justify-content-end gap-3 mb-1\">\r\n <button class=\"btn btn-secondary\" (click)=\"onItalicClick()\" title=\"Italic\" aria-label=\"Italic\" type=\"button\"><span class=\"fas fa-italic\"></span></button>\r\n <button class=\"btn btn-secondary\" (click)=\"onBoldClick()\" title=\"Italic\" aria-label=\"strong\" type=\"button\"><span class=\"fas fa-bold\"></span></button>\r\n @if (hasTaxon) {\r\n <button class=\"btn btn-secondary\" (click)=\"onTaxonClick()\" title=\"Taxon\" aria-label=\"Taxon\" type=\"button\"><span class=\"fas fa-bug\"></span></button>\r\n }\r\n @if (hasReference) {\r\n <button class=\"btn btn-secondary\" (click)=\"onReferenceClick()\" title=\"Reference\" aria-label=\"Reference\" type=\"button\"><span class=\"fas fa-asterisk\"></span></button>\r\n }\r\n </div>\r\n <textarea class=\"form-control\" [(ngModel)]=\"text\" (ngModelChange)=\"onTextChange()\" [rows]=\"rows\" (dblclick)=\"onDoubleClick()\"></textarea>\r\n</div>\r\n" }]
|
|
1882
1939
|
}], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { hasTaxon: [{
|
|
1883
1940
|
type: Input
|
|
1884
1941
|
}], hasReference: [{
|
|
@@ -1891,49 +1948,78 @@ const RICH_MODULE_CONFIG = new InjectionToken('richModule.config');
|
|
|
1891
1948
|
|
|
1892
1949
|
class RichTextComponent {
|
|
1893
1950
|
constructor(config) {
|
|
1894
|
-
this.config = config;
|
|
1895
1951
|
this.text = '';
|
|
1896
1952
|
this.parts = [];
|
|
1897
1953
|
this.taxonUrl = config.taxonUrl;
|
|
1898
1954
|
}
|
|
1899
1955
|
ngOnChanges() {
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1956
|
+
this.parts = this.text
|
|
1957
|
+
? this.parseCustomMarkdown(this.text)
|
|
1958
|
+
: [];
|
|
1903
1959
|
}
|
|
1904
1960
|
parseCustomMarkdown(input) {
|
|
1905
1961
|
const parts = [];
|
|
1906
|
-
const
|
|
1907
|
-
const
|
|
1962
|
+
const boldRegex = /(\*\*|__)(.+?)\1/g;
|
|
1963
|
+
const italicRegex = /(\*|_)([^*_]+?)\1/g;
|
|
1908
1964
|
const taxonRegex = /\[([^\]]+)\]\(taxon:([^)]+)\)/g;
|
|
1909
1965
|
const termRegex = /\[([^\]]+)\]\(term:([^)]+)\)/g;
|
|
1910
1966
|
const referenceRegex = /\[([^\]]+)\]\(reference:([^)]+)\)/g;
|
|
1911
|
-
const combinedRegex = new RegExp(
|
|
1967
|
+
const combinedRegex = new RegExp([
|
|
1968
|
+
boldRegex.source,
|
|
1969
|
+
italicRegex.source,
|
|
1970
|
+
taxonRegex.source,
|
|
1971
|
+
termRegex.source,
|
|
1972
|
+
referenceRegex.source
|
|
1973
|
+
].join('|'), 'g');
|
|
1912
1974
|
let lastIndex = 0;
|
|
1913
1975
|
let match;
|
|
1914
1976
|
while ((match = combinedRegex.exec(input)) !== null) {
|
|
1915
1977
|
if (match.index > lastIndex) {
|
|
1916
|
-
parts.push({
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1978
|
+
parts.push({
|
|
1979
|
+
type: 'text',
|
|
1980
|
+
content: input.slice(lastIndex, match.index)
|
|
1981
|
+
});
|
|
1920
1982
|
}
|
|
1921
|
-
|
|
1922
|
-
parts.push({
|
|
1983
|
+
if (match[2]) {
|
|
1984
|
+
parts.push({
|
|
1985
|
+
type: 'bold',
|
|
1986
|
+
content: match[2]
|
|
1987
|
+
});
|
|
1923
1988
|
}
|
|
1924
|
-
else if (match[
|
|
1925
|
-
parts.push({
|
|
1989
|
+
else if (match[4]) {
|
|
1990
|
+
parts.push({
|
|
1991
|
+
type: 'italic',
|
|
1992
|
+
content: match[4]
|
|
1993
|
+
});
|
|
1926
1994
|
}
|
|
1927
1995
|
else if (match[5]) {
|
|
1928
|
-
parts.push({
|
|
1996
|
+
parts.push({
|
|
1997
|
+
type: 'taxon',
|
|
1998
|
+
content: match[5],
|
|
1999
|
+
id: match[6]
|
|
2000
|
+
});
|
|
1929
2001
|
}
|
|
1930
2002
|
else if (match[7]) {
|
|
1931
|
-
parts.push({
|
|
2003
|
+
parts.push({
|
|
2004
|
+
type: 'term',
|
|
2005
|
+
content: match[7],
|
|
2006
|
+
id: match[8]
|
|
2007
|
+
});
|
|
2008
|
+
}
|
|
2009
|
+
else if (match[9]) {
|
|
2010
|
+
parts.push({
|
|
2011
|
+
type: 'reference',
|
|
2012
|
+
content: match[9],
|
|
2013
|
+
id: match[10]
|
|
2014
|
+
});
|
|
1932
2015
|
}
|
|
1933
2016
|
lastIndex = combinedRegex.lastIndex;
|
|
1934
2017
|
}
|
|
1935
2018
|
if (lastIndex < input.length) {
|
|
1936
|
-
parts.push({
|
|
2019
|
+
parts.push({
|
|
2020
|
+
type: 'text',
|
|
2021
|
+
content: input.slice(lastIndex)
|
|
2022
|
+
});
|
|
1937
2023
|
}
|
|
1938
2024
|
return parts;
|
|
1939
2025
|
}
|