@antscorp/antsomi-ui 1.3.5-beta.831 → 1.3.5-beta.832
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/es/components/molecules/EmojiCollections/CommonCollection/index.js +18 -4
- package/es/components/molecules/EmojiCollections/LineCollection/index.js +18 -4
- package/es/components/molecules/EmojiCollections/ViberCollection/index.js +17 -4
- package/es/components/molecules/EmojiPopover/EmojiPopover.js +1 -1
- package/es/components/molecules/TagifyInput/TagifyInput.js +23 -11
- package/es/components/molecules/TagifyInput/utils.d.ts +21 -18
- package/es/components/molecules/TagifyInput/utils.js +24 -21
- package/es/locales/en/google-sheet.json +1 -1
- package/es/locales/ja/google-sheet.json +1576 -1576
- package/es/locales/vi/google-sheet.json +55 -55
- package/package.json +1 -2
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
/* eslint-disable no-param-reassign */
|
|
3
3
|
// Libraries
|
|
4
|
-
import { memo, useCallback, useEffect, useMemo } from 'react';
|
|
4
|
+
import { memo, useCallback, useEffect, useMemo, useState } from 'react';
|
|
5
5
|
import { useImmer } from 'use-immer';
|
|
6
6
|
import _ from 'lodash';
|
|
7
7
|
// Components & Styled
|
|
@@ -18,6 +18,7 @@ const { Text } = Typography;
|
|
|
18
18
|
const emojiListParsed = JSON.parse(JSON.stringify(ICON_EMOJI_COMMON));
|
|
19
19
|
const CommonCollection = ({ onEmojiClick }) => {
|
|
20
20
|
// States
|
|
21
|
+
const [visibleCount, setVisibleCount] = useState(40);
|
|
21
22
|
const [state, setState] = useImmer({
|
|
22
23
|
collectionActive: SMILEYS_BODY,
|
|
23
24
|
txtSearch: '',
|
|
@@ -47,10 +48,22 @@ const CommonCollection = ({ onEmojiClick }) => {
|
|
|
47
48
|
});
|
|
48
49
|
}
|
|
49
50
|
}, [setState]);
|
|
51
|
+
const onScroll = useCallback((e, collectionLength) => {
|
|
52
|
+
const { scrollTop, scrollHeight, clientHeight } = e.target;
|
|
53
|
+
if (scrollHeight - scrollTop <= clientHeight * 1.5) {
|
|
54
|
+
setVisibleCount(prev => {
|
|
55
|
+
const newCount = prev + 30;
|
|
56
|
+
if (newCount >= collectionLength)
|
|
57
|
+
return collectionLength;
|
|
58
|
+
return newCount;
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}, []);
|
|
50
62
|
const handleChangeSelect = useCallback((newCollection) => {
|
|
51
63
|
setState(draft => {
|
|
52
64
|
draft.collectionActive = newCollection;
|
|
53
65
|
});
|
|
66
|
+
setVisibleCount(prev => prev + 10);
|
|
54
67
|
}, [setState]);
|
|
55
68
|
useEffect(() => () => {
|
|
56
69
|
setState(() => ({
|
|
@@ -72,13 +85,14 @@ const CommonCollection = ({ onEmojiClick }) => {
|
|
|
72
85
|
(_.isArray(emojiCollection) && emojiCollection.length === 0)) {
|
|
73
86
|
return (_jsx(EmptyEmoji, { image: _jsx(Button, { type: "text", shape: "round", icon: _jsx(EmojiSmileIcon, { color: globalToken?.bw5, style: { width: 30, height: 30 } }), style: { width: 60, height: 60, background: globalToken?.bw2 } }), imageStyle: { height: 60, marginBottom: 15 }, description: _jsx("span", { style: { color: globalToken?.bw8 }, children: "No emoji matches your keyword" }) }));
|
|
74
87
|
}
|
|
75
|
-
const
|
|
88
|
+
const list = emojiCollection.slice(0, Math.min(visibleCount, emojiCollection.length));
|
|
89
|
+
const content = list.map((item) => {
|
|
76
90
|
const { emoji, slug = '', unicode_version: unicodeVersion = '' } = item;
|
|
77
91
|
const key = `${slug}-${unicodeVersion}`;
|
|
78
92
|
return (_jsx(Emoji, { type: "text", onClick: () => onEmojiClick(emoji), children: _jsx("span", { style: { fontSize: '30px' }, children: emoji }) }, key));
|
|
79
93
|
});
|
|
80
|
-
return (_jsx(Scrollbars, { autoHeight: true, autoHeightMax: 150, children: _jsx(Flex, { wrap: "wrap", children: content }) }));
|
|
81
|
-
}, [emojiCollection, onEmojiClick]);
|
|
94
|
+
return (_jsx(Scrollbars, { autoHeight: true, autoHeightMax: 150, onScroll: e => onScroll(e, emojiCollection.length), children: _jsx(Flex, { wrap: "wrap", children: content }) }));
|
|
95
|
+
}, [emojiCollection, visibleCount, onScroll, onEmojiClick]);
|
|
82
96
|
return (_jsxs(WrapperCollection, { children: [_jsxs(Flex, { gap: 20, justify: "space-between", align: "flex-end", children: [_jsx(Input, { placeholder: "Search...", suffix: _jsx(SearchIcon, { color: globalToken?.bw8 }), styles: { affixWrapper: { minWidth: 280 } }, onChange: handleChangeInput }), _jsx(Select, { placeholder: "Select collection", value: state.collectionActive, style: { width: 200 }, options: collectionOptions, optionRender: renderOption, onChange: handleChangeSelect })] }), _jsx(EmojiList, { children: renderCommonCollection() })] }));
|
|
83
97
|
};
|
|
84
98
|
CommonCollection.defaultProps = {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
/* eslint-disable no-param-reassign */
|
|
3
3
|
// Libraries
|
|
4
|
-
import { memo, useCallback, useEffect, useMemo } from 'react';
|
|
4
|
+
import { memo, useCallback, useEffect, useMemo, useState } from 'react';
|
|
5
5
|
import _ from 'lodash';
|
|
6
6
|
import { useImmer } from 'use-immer';
|
|
7
7
|
// Components & Styled
|
|
@@ -14,6 +14,7 @@ import { LINE_MESSAGE_EMOJIS, TAB_LINE_MESSAGE_EMOJIS } from './constants';
|
|
|
14
14
|
import { globalToken } from '@antscorp/antsomi-ui/es/constants';
|
|
15
15
|
const LineCollection = ({ onEmojiClick }) => {
|
|
16
16
|
// States
|
|
17
|
+
const [visibleCount, setVisibleCount] = useState(40);
|
|
17
18
|
const [state, setState] = useImmer({
|
|
18
19
|
collectionActive: TAB_LINE_MESSAGE_EMOJIS[0]?.key || '',
|
|
19
20
|
});
|
|
@@ -23,7 +24,19 @@ const LineCollection = ({ onEmojiClick }) => {
|
|
|
23
24
|
setState(draft => {
|
|
24
25
|
draft.collectionActive = newCollection;
|
|
25
26
|
});
|
|
27
|
+
setVisibleCount(prev => prev + 10);
|
|
26
28
|
}, [setState]);
|
|
29
|
+
const onScroll = useCallback((e, collectionLength) => {
|
|
30
|
+
const { scrollTop, scrollHeight, clientHeight } = e.target;
|
|
31
|
+
if (scrollHeight - scrollTop <= clientHeight * 1.5) {
|
|
32
|
+
setVisibleCount(prev => {
|
|
33
|
+
const newCount = prev + 30;
|
|
34
|
+
if (newCount >= collectionLength)
|
|
35
|
+
return collectionLength;
|
|
36
|
+
return newCount;
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}, []);
|
|
27
40
|
useEffect(() => () => {
|
|
28
41
|
setState(draft => {
|
|
29
42
|
draft.collectionActive = TAB_LINE_MESSAGE_EMOJIS[0]?.key || '';
|
|
@@ -40,13 +53,14 @@ const LineCollection = ({ onEmojiClick }) => {
|
|
|
40
53
|
if (_.isArray(emojiList) && !emojiList.length) {
|
|
41
54
|
return (_jsx(EmptyEmoji, { image: _jsx(Button, { type: "text", shape: "round", icon: _jsx(EmojiSmileIcon, { color: globalToken?.bw5, style: { width: 30, height: 30 } }), style: { width: 60, height: 60, background: globalToken?.bw2 } }), imageStyle: { height: 60, marginBottom: 15 }, description: _jsx("span", { style: { color: globalToken?.bw8 }, children: "No emoji matches your keyword" }) }));
|
|
42
55
|
}
|
|
43
|
-
const
|
|
56
|
+
const list = emojiList.slice(0, Math.min(visibleCount, emojiList.length));
|
|
57
|
+
const content = list.map((emojiId) => {
|
|
44
58
|
const src = getLinkLineURLImage(state.collectionActive, emojiId);
|
|
45
59
|
const code = `$((${PREFIX_PATTERN_LINE_MESSAGE}:${state.collectionActive}:${emojiId}))`;
|
|
46
60
|
return (_jsx(Emoji, { type: "text", icon: _jsx(EmojiImage, { src: src, alt: emojiId }), onClick: () => onEmojiClick(code) }, emojiId));
|
|
47
61
|
});
|
|
48
|
-
return (_jsx(Scrollbars, { autoHeight: true, autoHeightMax: 150, children: _jsx(Flex, { wrap: "wrap", children: content }) }));
|
|
49
|
-
}, [emojiList, state.collectionActive, onEmojiClick]);
|
|
62
|
+
return (_jsx(Scrollbars, { autoHeight: true, autoHeightMax: 150, onScroll: e => onScroll(e, emojiList.length), children: _jsx(Flex, { wrap: "wrap", children: content }) }));
|
|
63
|
+
}, [emojiList, visibleCount, state.collectionActive, onEmojiClick, onScroll]);
|
|
50
64
|
return (_jsx(WrapperCollection, { children: _jsxs(Flex, { vertical: true, children: [renderLineCategory(), _jsx(EmojiList, { children: renderLineEmoji() })] }) }));
|
|
51
65
|
};
|
|
52
66
|
LineCollection.defaultProps = {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
/* eslint-disable no-param-reassign */
|
|
3
3
|
// Libraries
|
|
4
|
-
import { memo, useCallback, useEffect, useMemo } from 'react';
|
|
4
|
+
import { memo, useCallback, useEffect, useMemo, useState } from 'react';
|
|
5
5
|
import { useImmer } from 'use-immer';
|
|
6
6
|
import _ from 'lodash';
|
|
7
7
|
// Hooks
|
|
@@ -16,6 +16,7 @@ import { globalToken } from '@antscorp/antsomi-ui/es/constants';
|
|
|
16
16
|
const listEmoji = Object.values(iconsViber);
|
|
17
17
|
const ViberCollection = ({ onEmojiClick }) => {
|
|
18
18
|
// States
|
|
19
|
+
const [visibleCount, setVisibleCount] = useState(40);
|
|
19
20
|
const [state, setState] = useImmer({
|
|
20
21
|
txtSearch: '',
|
|
21
22
|
});
|
|
@@ -36,6 +37,17 @@ const ViberCollection = ({ onEmojiClick }) => {
|
|
|
36
37
|
});
|
|
37
38
|
}
|
|
38
39
|
}, [setState]);
|
|
40
|
+
const onScroll = useCallback((e, collectionLength) => {
|
|
41
|
+
const { scrollTop, scrollHeight, clientHeight } = e.target;
|
|
42
|
+
if (scrollHeight - scrollTop <= clientHeight * 1.5) {
|
|
43
|
+
setVisibleCount(prev => {
|
|
44
|
+
const newCount = prev + 30;
|
|
45
|
+
if (newCount >= collectionLength)
|
|
46
|
+
return collectionLength;
|
|
47
|
+
return newCount;
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}, []);
|
|
39
51
|
useEffect(() => () => {
|
|
40
52
|
setState(draft => {
|
|
41
53
|
draft.txtSearch = '';
|
|
@@ -45,14 +57,15 @@ const ViberCollection = ({ onEmojiClick }) => {
|
|
|
45
57
|
if (_.isArray(listEmojiMemoized) && !listEmojiMemoized.length) {
|
|
46
58
|
return (_jsx(EmptyEmoji, { image: _jsx(Button, { type: "text", shape: "round", icon: _jsx(EmojiSmileIcon, { color: globalToken?.bw5, style: { width: 30, height: 30 } }), style: { width: 60, height: 60, background: globalToken?.bw2 } }), imageStyle: { height: 60, marginBottom: 15 }, description: _jsx("span", { style: { color: globalToken?.bw8 }, children: "No emoji matches your keyword" }) }));
|
|
47
59
|
}
|
|
48
|
-
const
|
|
60
|
+
const list = listEmojiMemoized.slice(0, Math.min(visibleCount, listEmojiMemoized.length));
|
|
61
|
+
const content = list.map(emojiFileName => {
|
|
49
62
|
const src = getImageSourceViberEmoji(emojiFileName);
|
|
50
63
|
const [emoji] = emojiFileName.split('.');
|
|
51
64
|
const code = `(${emoji})`;
|
|
52
65
|
return (_jsx(Emoji, { type: "text", icon: _jsx(EmojiImage, { src: src, alt: emojiFileName }), onClick: () => onEmojiClick(code) }, emojiFileName));
|
|
53
66
|
});
|
|
54
|
-
return (_jsx(Scrollbars, { autoHeight: true, autoHeightMax: 150, children: _jsx(Flex, { wrap: "wrap", children: content }) }));
|
|
55
|
-
}, [listEmojiMemoized, onEmojiClick]);
|
|
67
|
+
return (_jsx(Scrollbars, { autoHeight: true, autoHeightMax: 150, onScroll: e => onScroll(e, listEmojiMemoized.length), children: _jsx(Flex, { wrap: "wrap", children: content }) }));
|
|
68
|
+
}, [listEmojiMemoized, visibleCount, onEmojiClick, onScroll]);
|
|
56
69
|
return (_jsx(WrapperCollection, { children: _jsxs(Flex, { vertical: true, children: [_jsx(Input, { placeholder: "Search...", suffix: _jsx(SearchIcon, { color: globalToken?.bw8 }), onChange: handleChangeInput }), _jsx(EmojiList, { children: renderViberCollection() })] }) }));
|
|
57
70
|
};
|
|
58
71
|
ViberCollection.defaultProps = {
|
|
@@ -66,7 +66,7 @@ const EmojiPopover = ({ disabled, collections, isForceHide, children, onEmojiCli
|
|
|
66
66
|
children: renderCollection(collection.key),
|
|
67
67
|
}));
|
|
68
68
|
}, [collections, renderCollection]);
|
|
69
|
-
return (_jsx(EmojiPopoverStyled, { content: _jsx(EmojiTabs, { activeKey: state.collectionActive, onTabClick: handleClickCollection, items: emojiCollections }), placement: "topLeft", trigger: "click", style: { padding: 15 }, arrow: false, rootClassName: "antsomi-emoji-popover", open: state.isOpen, onOpenChange: handleOpenChange, children: children || (_jsx(Button, { type: "link", icon: _jsx(EmojiSmileIcon, {}), disabled: disabled, onClick: () => handleOpenChange(true) })) }));
|
|
69
|
+
return (_jsx(EmojiPopoverStyled, { content: _jsx(EmojiTabs, { activeKey: state.collectionActive, onTabClick: handleClickCollection, items: emojiCollections }), placement: "topLeft", trigger: "click", fresh: false, style: { padding: 15 }, arrow: false, rootClassName: "antsomi-emoji-popover", open: state.isOpen, onOpenChange: handleOpenChange, children: children || (_jsx(Button, { type: "link", icon: _jsx(EmojiSmileIcon, {}), disabled: disabled, onClick: () => handleOpenChange(true) })) }));
|
|
70
70
|
};
|
|
71
71
|
EmojiPopover.defaultProps = {
|
|
72
72
|
disabled: false,
|
|
@@ -17,7 +17,7 @@ import '@yaireo/tagify/dist/tagify.css';
|
|
|
17
17
|
// Styled
|
|
18
18
|
import { TagTextArea, TagifyWrapper, WrapperPlaceHolder } from './styled';
|
|
19
19
|
// Utils
|
|
20
|
-
import { parseTagStringToTagify, convertInputStringToOriginal, emojiManufacturer, getEmojiTag, isPersonalizeTagType, generateTagContent, unescapeString, hasLineBreak,
|
|
20
|
+
import { parseTagStringToTagify, convertInputStringToOriginal, emojiManufacturer, getEmojiTag, isPersonalizeTagType, generateTagContent, unescapeString, hasLineBreak, selectRange, isTagClickable, findURLInTextNodes, } from './utils';
|
|
21
21
|
import { acceptablePatternChecking, detectURLRegex, getCachedRegex, getPersonalizeTagInfo, patternHandlers, } from './patternHandlers';
|
|
22
22
|
// Constants
|
|
23
23
|
import { DETECT_LINK, EMOJI, INVALID_TAG, PERSONALIZE_PTN, PROMOTION_CODE, READONLY_TAG, SHORT_LINK, SHORT_LINK_PTN, defaultCssVariables, tagifyDefaultProps, } from './constants';
|
|
@@ -120,22 +120,34 @@ const TagifyInput = forwardRef((props, ref) => {
|
|
|
120
120
|
// If the last range is lost, need to select the last text node of the input
|
|
121
121
|
const { input: inputElement } = tagifyRef.current.DOM;
|
|
122
122
|
if (inputElement) {
|
|
123
|
-
// Get all child nodes that are of type TEXT_NODE
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
123
|
+
// Get all child nodes that are of type TEXT_NODE or ELEMENT_NODE with nodeName TAG
|
|
124
|
+
const nodeList = Array.from(inputElement.childNodes).filter((node) => {
|
|
125
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
126
|
+
return node.nodeName === 'TAG';
|
|
127
|
+
}
|
|
128
|
+
return node.nodeType === Node.TEXT_NODE;
|
|
129
|
+
});
|
|
130
|
+
if (nodeList.length && selection) {
|
|
131
|
+
const lastNode = nodeList[nodeList.length - 1];
|
|
132
|
+
// In case have the last text nodes
|
|
133
|
+
// -> need to place the caret at the end of the last text
|
|
134
|
+
if (lastNode.nodeType === Node.TEXT_NODE && lastNode?.textContent) {
|
|
135
|
+
const textNodeLength = lastNode.textContent.length;
|
|
131
136
|
// Place the caret at the end of the last text
|
|
132
|
-
|
|
137
|
+
selectRange(lastNode, textNodeLength, textNodeLength);
|
|
138
|
+
onSyncSelectionStateTagify();
|
|
139
|
+
}
|
|
140
|
+
else if (lastNode.nodeType === Node.ELEMENT_NODE && lastNode?.nodeName === 'TAG') {
|
|
141
|
+
// In case no text nodes but have an element node
|
|
142
|
+
const lastNodeExceptBrLength = inputElement.childNodes.length - 1; // Exclude the last <br>
|
|
143
|
+
// Place the caret at the end of the last element node
|
|
144
|
+
selectRange(inputElement, lastNodeExceptBrLength, lastNodeExceptBrLength);
|
|
133
145
|
onSyncSelectionStateTagify();
|
|
134
146
|
}
|
|
135
147
|
}
|
|
136
148
|
else if (DOM?.scope?.classList?.contains(empty)) {
|
|
137
149
|
// In case no text nodes and the input is empty
|
|
138
|
-
|
|
150
|
+
selectRange(inputElement, 0, 0);
|
|
139
151
|
onSyncSelectionStateTagify();
|
|
140
152
|
}
|
|
141
153
|
}
|
|
@@ -82,31 +82,34 @@ export declare const isValidTagType: (str: string) => boolean;
|
|
|
82
82
|
*/
|
|
83
83
|
export declare const hasLineBreak: (str: string) => boolean;
|
|
84
84
|
/**
|
|
85
|
-
* Selects a range of text within a given
|
|
85
|
+
* Selects a range of text within a given DOM node.
|
|
86
86
|
*
|
|
87
|
-
* @param {Node}
|
|
88
|
-
* @param {number} start - The starting index of the selection
|
|
89
|
-
* @param {number} end - The ending index of the selection
|
|
90
|
-
*
|
|
91
|
-
* @throws {Error} If there's an issue creating the range or applying the selection.
|
|
87
|
+
* @param {Node} node - The DOM node containing the text to be selected.
|
|
88
|
+
* @param {number} start - The starting index position of the selection.
|
|
89
|
+
* @param {number} end - The ending index position of the selection.
|
|
92
90
|
*
|
|
93
91
|
* @example
|
|
94
92
|
* const paragraph = document.querySelector('p');
|
|
95
|
-
*
|
|
96
|
-
*
|
|
93
|
+
* if (paragraph.firstChild) {
|
|
94
|
+
* selectRange(paragraph.firstChild, 5, 10);
|
|
95
|
+
* }
|
|
97
96
|
*
|
|
98
97
|
* @description
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
* security reasons
|
|
98
|
+
* Creates a new DOM Range from the given start and end positions within the provided node
|
|
99
|
+
* and applies it to the current window selection. This visually selects the specified
|
|
100
|
+
* portion of text in the browser.
|
|
101
|
+
*
|
|
102
|
+
* Important considerations:
|
|
103
|
+
* - The node should be a text node or a node that can contain text content
|
|
104
|
+
* - The start and end indices must be valid positions within the node's text content
|
|
105
|
+
* - The selection may not be visible if the containing element is not focusable
|
|
106
|
+
* - Some browsers may prevent programmatic selection for security reasons
|
|
107
|
+
* - Any errors during selection are caught and logged to the console
|
|
108
|
+
*
|
|
109
|
+
* Note that this function only creates a visual selection - it does not copy the text
|
|
110
|
+
* or modify it in any way.
|
|
108
111
|
*/
|
|
109
|
-
export declare function
|
|
112
|
+
export declare function selectRange(node: Node, start: number, end: number): void;
|
|
110
113
|
/**
|
|
111
114
|
* Determines whether a tag is clickable based on its properties and the Tagify instance settings.
|
|
112
115
|
*
|
|
@@ -458,35 +458,38 @@ export const isValidTagType = (str) => Object.values(TAG_TYPE).includes(str);
|
|
|
458
458
|
*/
|
|
459
459
|
export const hasLineBreak = (str) => str.includes('\n');
|
|
460
460
|
/**
|
|
461
|
-
* Selects a range of text within a given
|
|
461
|
+
* Selects a range of text within a given DOM node.
|
|
462
462
|
*
|
|
463
|
-
* @param {Node}
|
|
464
|
-
* @param {number} start - The starting index of the selection
|
|
465
|
-
* @param {number} end - The ending index of the selection
|
|
466
|
-
*
|
|
467
|
-
* @throws {Error} If there's an issue creating the range or applying the selection.
|
|
463
|
+
* @param {Node} node - The DOM node containing the text to be selected.
|
|
464
|
+
* @param {number} start - The starting index position of the selection.
|
|
465
|
+
* @param {number} end - The ending index position of the selection.
|
|
468
466
|
*
|
|
469
467
|
* @example
|
|
470
468
|
* const paragraph = document.querySelector('p');
|
|
471
|
-
*
|
|
472
|
-
*
|
|
469
|
+
* if (paragraph.firstChild) {
|
|
470
|
+
* selectRange(paragraph.firstChild, 5, 10);
|
|
471
|
+
* }
|
|
473
472
|
*
|
|
474
473
|
* @description
|
|
475
|
-
*
|
|
476
|
-
*
|
|
477
|
-
*
|
|
478
|
-
*
|
|
479
|
-
*
|
|
480
|
-
*
|
|
481
|
-
*
|
|
482
|
-
*
|
|
483
|
-
* security reasons
|
|
474
|
+
* Creates a new DOM Range from the given start and end positions within the provided node
|
|
475
|
+
* and applies it to the current window selection. This visually selects the specified
|
|
476
|
+
* portion of text in the browser.
|
|
477
|
+
*
|
|
478
|
+
* Important considerations:
|
|
479
|
+
* - The node should be a text node or a node that can contain text content
|
|
480
|
+
* - The start and end indices must be valid positions within the node's text content
|
|
481
|
+
* - The selection may not be visible if the containing element is not focusable
|
|
482
|
+
* - Some browsers may prevent programmatic selection for security reasons
|
|
483
|
+
* - Any errors during selection are caught and logged to the console
|
|
484
|
+
*
|
|
485
|
+
* Note that this function only creates a visual selection - it does not copy the text
|
|
486
|
+
* or modify it in any way.
|
|
484
487
|
*/
|
|
485
|
-
export function
|
|
488
|
+
export function selectRange(node, start, end) {
|
|
486
489
|
try {
|
|
487
490
|
const range = document.createRange();
|
|
488
|
-
range.setStart(
|
|
489
|
-
range.setEnd(
|
|
491
|
+
range.setStart(node, start);
|
|
492
|
+
range.setEnd(node, end);
|
|
490
493
|
const selection = window.getSelection();
|
|
491
494
|
if (selection) {
|
|
492
495
|
selection.removeAllRanges();
|
|
@@ -495,7 +498,7 @@ export function selectTextRange(textNode, start, end) {
|
|
|
495
498
|
}
|
|
496
499
|
catch (error) {
|
|
497
500
|
// eslint-disable-next-line no-console
|
|
498
|
-
console.log('Error selecting
|
|
501
|
+
console.log('Error selecting range', error, { node, start, end });
|
|
499
502
|
}
|
|
500
503
|
}
|
|
501
504
|
/**
|
|
@@ -1288,7 +1288,7 @@
|
|
|
1288
1288
|
"_TITL_TOTAL_TESTING_INPUT": "Total testing input",
|
|
1289
1289
|
"_TITL_TRACKED_TIME": "Tracked time",
|
|
1290
1290
|
"_TITL_TREAT_AS_CONVERSION_OBECJT": "Treat as Conversion Object",
|
|
1291
|
-
"_TOOLTIP__TITL_TREAT_AS_CONVERSION_OBECJT": "
|
|
1291
|
+
"_TOOLTIP__TITL_TREAT_AS_CONVERSION_OBECJT": "Loading...",
|
|
1292
1292
|
"_TITL_TRIGGER_EVENT": "{{trigger_event_name}}, where:",
|
|
1293
1293
|
"_TITL_TRIGGER_TIME": "Trigger time",
|
|
1294
1294
|
"_TITL_UNTITLED_MEASURE": "Untitled Measure",
|