@cloudscape-design/components 3.0.1269 → 3.0.1271
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/app-layout/visual-refresh-toolbar/state/use-feature-notifications.d.ts.map +1 -1
- package/app-layout/visual-refresh-toolbar/state/use-feature-notifications.js +13 -0
- package/app-layout/visual-refresh-toolbar/state/use-feature-notifications.js.map +1 -1
- package/app-layout/visual-refresh-toolbar/toolbar/index.js +1 -1
- package/app-layout/visual-refresh-toolbar/toolbar/index.js.map +1 -1
- package/app-layout/visual-refresh-toolbar/toolbar/trigger-button/index.d.ts +1 -1
- package/app-layout/visual-refresh-toolbar/toolbar/trigger-button/index.d.ts.map +1 -1
- package/app-layout/visual-refresh-toolbar/toolbar/trigger-button/index.js +1 -2
- package/app-layout/visual-refresh-toolbar/toolbar/trigger-button/index.js.map +1 -1
- package/app-layout/visual-refresh-toolbar/toolbar/trigger-button/styles.css.js +6 -7
- package/app-layout/visual-refresh-toolbar/toolbar/trigger-button/styles.scoped.css +20 -26
- package/app-layout/visual-refresh-toolbar/toolbar/trigger-button/styles.selectors.js +6 -7
- package/button/styles.css.js +22 -22
- package/button/styles.scoped.css +78 -78
- package/button/styles.selectors.js +22 -22
- package/date-input/internal.d.ts.map +1 -1
- package/date-input/internal.js +7 -5
- package/date-input/internal.js.map +1 -1
- package/help-panel/styles.css.js +6 -6
- package/help-panel/styles.scoped.css +70 -70
- package/help-panel/styles.selectors.js +6 -6
- package/i18n/messages/all.all.js +1 -1
- package/i18n/messages/all.all.json +1 -1
- package/i18n/messages/all.ar.js +1 -1
- package/i18n/messages/all.ar.json +1 -1
- package/i18n/messages/all.de.js +1 -1
- package/i18n/messages/all.de.json +1 -1
- package/i18n/messages/all.en-GB.js +1 -1
- package/i18n/messages/all.en-GB.json +1 -1
- package/i18n/messages/all.en.js +1 -1
- package/i18n/messages/all.en.json +1 -1
- package/i18n/messages/all.es.js +1 -1
- package/i18n/messages/all.es.json +1 -1
- package/i18n/messages/all.fr.js +1 -1
- package/i18n/messages/all.fr.json +1 -1
- package/i18n/messages/all.id.js +1 -1
- package/i18n/messages/all.id.json +1 -1
- package/i18n/messages/all.it.js +1 -1
- package/i18n/messages/all.it.json +1 -1
- package/i18n/messages/all.ja.js +1 -1
- package/i18n/messages/all.ja.json +1 -1
- package/i18n/messages/all.ko.js +1 -1
- package/i18n/messages/all.ko.json +1 -1
- package/i18n/messages/all.pt-BR.js +1 -1
- package/i18n/messages/all.pt-BR.json +1 -1
- package/i18n/messages/all.tr.js +1 -1
- package/i18n/messages/all.tr.json +1 -1
- package/i18n/messages/all.zh-CN.js +1 -1
- package/i18n/messages/all.zh-CN.json +1 -1
- package/i18n/messages/all.zh-TW.js +1 -1
- package/i18n/messages/all.zh-TW.json +1 -1
- package/i18n/messages-types.d.ts +18 -0
- package/i18n/messages-types.d.ts.map +1 -1
- package/i18n/messages-types.js.map +1 -1
- package/internal/base-component/styles.scoped.css +63 -4
- package/internal/components/token-list/styles.css.js +10 -10
- package/internal/components/token-list/styles.scoped.css +25 -25
- package/internal/components/token-list/styles.selectors.js +10 -10
- package/internal/context/reset-contexts-for-modal.d.ts.map +1 -1
- package/internal/context/reset-contexts-for-modal.js +3 -1
- package/internal/context/reset-contexts-for-modal.js.map +1 -1
- package/internal/context/token-inline-context.d.ts +6 -0
- package/internal/context/token-inline-context.d.ts.map +1 -0
- package/internal/context/token-inline-context.js +11 -0
- package/internal/context/token-inline-context.js.map +1 -0
- package/internal/environment.js +2 -2
- package/internal/environment.json +2 -2
- package/internal/generated/styles/tokens.d.ts +17 -3
- package/internal/generated/styles/tokens.js +17 -3
- package/internal/generated/theming/index.cjs +417 -9
- package/internal/generated/theming/index.cjs.d.ts +189 -0
- package/internal/generated/theming/index.d.ts +189 -0
- package/internal/generated/theming/index.js +417 -9
- package/internal/keycode.d.ts +3 -1
- package/internal/keycode.d.ts.map +1 -1
- package/internal/keycode.js +5 -0
- package/internal/keycode.js.map +1 -1
- package/internal/manifest.json +1 -1
- package/internal/utils/handle-key.d.ts +22 -1
- package/internal/utils/handle-key.d.ts.map +1 -1
- package/internal/utils/handle-key.js +62 -4
- package/internal/utils/handle-key.js.map +1 -1
- package/link/internal.d.ts.map +1 -1
- package/link/internal.js +3 -1
- package/link/internal.js.map +1 -1
- package/link/styles.css.js +21 -20
- package/link/styles.scoped.css +83 -80
- package/link/styles.selectors.js +21 -20
- package/package.json +1 -1
- package/popover/internal.d.ts.map +1 -1
- package/popover/internal.js +3 -1
- package/popover/internal.js.map +1 -1
- package/popover/styles.css.js +58 -57
- package/popover/styles.scoped.css +92 -89
- package/popover/styles.selectors.js +58 -57
- package/prompt-input/components/menu-dropdown.d.ts +33 -0
- package/prompt-input/components/menu-dropdown.d.ts.map +1 -0
- package/prompt-input/components/menu-dropdown.js +48 -0
- package/prompt-input/components/menu-dropdown.js.map +1 -0
- package/prompt-input/components/textarea-mode.d.ts +15 -0
- package/prompt-input/components/textarea-mode.d.ts.map +1 -0
- package/prompt-input/components/textarea-mode.js +8 -0
- package/prompt-input/components/textarea-mode.js.map +1 -0
- package/prompt-input/components/token-mode.d.ts +61 -0
- package/prompt-input/components/token-mode.d.ts.map +1 -0
- package/prompt-input/components/token-mode.js +37 -0
- package/prompt-input/components/token-mode.js.map +1 -0
- package/prompt-input/core/caret-controller.d.ts +73 -0
- package/prompt-input/core/caret-controller.d.ts.map +1 -0
- package/prompt-input/core/caret-controller.js +396 -0
- package/prompt-input/core/caret-controller.js.map +1 -0
- package/prompt-input/core/caret-spot-utils.d.ts +6 -0
- package/prompt-input/core/caret-spot-utils.d.ts.map +1 -0
- package/prompt-input/core/caret-spot-utils.js +52 -0
- package/prompt-input/core/caret-spot-utils.js.map +1 -0
- package/prompt-input/core/caret-utils.d.ts +25 -0
- package/prompt-input/core/caret-utils.d.ts.map +1 -0
- package/prompt-input/core/caret-utils.js +183 -0
- package/prompt-input/core/caret-utils.js.map +1 -0
- package/prompt-input/core/constants.d.ts +14 -0
- package/prompt-input/core/constants.d.ts.map +1 -0
- package/prompt-input/core/constants.js +18 -0
- package/prompt-input/core/constants.js.map +1 -0
- package/prompt-input/core/dom-utils.d.ts +60 -0
- package/prompt-input/core/dom-utils.d.ts.map +1 -0
- package/prompt-input/core/dom-utils.js +252 -0
- package/prompt-input/core/dom-utils.js.map +1 -0
- package/prompt-input/core/event-handlers.d.ts +68 -0
- package/prompt-input/core/event-handlers.d.ts.map +1 -0
- package/prompt-input/core/event-handlers.js +678 -0
- package/prompt-input/core/event-handlers.js.map +1 -0
- package/prompt-input/core/menu-state.d.ts +62 -0
- package/prompt-input/core/menu-state.d.ts.map +1 -0
- package/prompt-input/core/menu-state.js +168 -0
- package/prompt-input/core/menu-state.js.map +1 -0
- package/prompt-input/core/token-operations.d.ts +21 -0
- package/prompt-input/core/token-operations.d.ts.map +1 -0
- package/prompt-input/core/token-operations.js +273 -0
- package/prompt-input/core/token-operations.js.map +1 -0
- package/prompt-input/core/token-renderer.d.ts +26 -0
- package/prompt-input/core/token-renderer.d.ts.map +1 -0
- package/prompt-input/core/token-renderer.js +230 -0
- package/prompt-input/core/token-renderer.js.map +1 -0
- package/prompt-input/core/token-utils.d.ts +22 -0
- package/prompt-input/core/token-utils.d.ts.map +1 -0
- package/prompt-input/core/token-utils.js +262 -0
- package/prompt-input/core/token-utils.js.map +1 -0
- package/prompt-input/core/trigger-utils.d.ts +18 -0
- package/prompt-input/core/trigger-utils.d.ts.map +1 -0
- package/prompt-input/core/trigger-utils.js +174 -0
- package/prompt-input/core/trigger-utils.js.map +1 -0
- package/prompt-input/core/type-guards.d.ts +13 -0
- package/prompt-input/core/type-guards.d.ts.map +1 -0
- package/prompt-input/core/type-guards.js +36 -0
- package/prompt-input/core/type-guards.js.map +1 -0
- package/prompt-input/index.d.ts +1 -1
- package/prompt-input/index.d.ts.map +1 -1
- package/prompt-input/index.js.map +1 -1
- package/prompt-input/interfaces.d.ts +356 -7
- package/prompt-input/interfaces.d.ts.map +1 -1
- package/prompt-input/interfaces.js.map +1 -1
- package/prompt-input/internal.d.ts +1 -1
- package/prompt-input/internal.d.ts.map +1 -1
- package/prompt-input/internal.js +195 -61
- package/prompt-input/internal.js.map +1 -1
- package/prompt-input/styles.css.js +26 -17
- package/prompt-input/styles.scoped.css +152 -39
- package/prompt-input/styles.selectors.js +26 -17
- package/prompt-input/test-classes/styles.css.js +7 -6
- package/prompt-input/test-classes/styles.scoped.css +10 -6
- package/prompt-input/test-classes/styles.selectors.js +7 -6
- package/prompt-input/tokens/use-shortcuts.d.ts +37 -0
- package/prompt-input/tokens/use-shortcuts.d.ts.map +1 -0
- package/prompt-input/tokens/use-shortcuts.js +89 -0
- package/prompt-input/tokens/use-shortcuts.js.map +1 -0
- package/prompt-input/tokens/use-token-mode.d.ts +78 -0
- package/prompt-input/tokens/use-token-mode.d.ts.map +1 -0
- package/prompt-input/tokens/use-token-mode.js +817 -0
- package/prompt-input/tokens/use-token-mode.js.map +1 -0
- package/prompt-input/utils/insert-text-content-editable.d.ts +10 -0
- package/prompt-input/utils/insert-text-content-editable.d.ts.map +1 -0
- package/prompt-input/utils/insert-text-content-editable.js +133 -0
- package/prompt-input/utils/insert-text-content-editable.js.map +1 -0
- package/tabs/styles.css.js +30 -30
- package/tabs/styles.scoped.css +55 -55
- package/tabs/styles.selectors.js +30 -30
- package/test-utils/dom/hotspot/index.js +1 -2
- package/test-utils/dom/hotspot/index.js.map +1 -1
- package/test-utils/dom/prompt-input/index.d.ts +53 -1
- package/test-utils/dom/prompt-input/index.js +121 -13
- package/test-utils/dom/prompt-input/index.js.map +1 -1
- package/test-utils/dom/s3-resource-selector/index.js +2 -3
- package/test-utils/dom/s3-resource-selector/index.js.map +1 -1
- package/test-utils/selectors/hotspot/index.js +1 -2
- package/test-utils/selectors/hotspot/index.js.map +1 -1
- package/test-utils/selectors/prompt-input/index.d.ts +30 -0
- package/test-utils/selectors/prompt-input/index.js +53 -7
- package/test-utils/selectors/prompt-input/index.js.map +1 -1
- package/test-utils/selectors/s3-resource-selector/index.js +2 -3
- package/test-utils/selectors/s3-resource-selector/index.js.map +1 -1
- package/token/internal.d.ts.map +1 -1
- package/token/internal.js +17 -15
- package/token/internal.js.map +1 -1
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { isHTMLElement } from '../../internal/utils/dom';
|
|
4
|
+
import { ElementType } from './constants';
|
|
5
|
+
import { getTokenType, isCaretSpotType, isReferenceElementType } from './dom-utils';
|
|
6
|
+
import { isTextNode } from './type-guards';
|
|
7
|
+
// Module-level flag: mouse state is inherently global (one mouse per document)
|
|
8
|
+
// and must be accessible from the standalone normalizeSelection function.
|
|
9
|
+
let isMouseDown = false;
|
|
10
|
+
/** Updates the mouse-down tracking flag used to skip selection normalization during drag. */
|
|
11
|
+
export function setMouseDown(value) {
|
|
12
|
+
isMouseDown = value;
|
|
13
|
+
}
|
|
14
|
+
/** Returns whether the mouse is currently pressed. */
|
|
15
|
+
export function getMouseDown() {
|
|
16
|
+
return isMouseDown;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Checks whether a node is inside a reference element's internals or directly
|
|
20
|
+
* on the contentEditable div (not inside a paragraph). These are non-typeable
|
|
21
|
+
* positions where the caret should not rest.
|
|
22
|
+
*/
|
|
23
|
+
export function isNonTypeablePosition(node) {
|
|
24
|
+
while (node) {
|
|
25
|
+
if (node.nodeName === 'P') {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
if (isHTMLElement(node)) {
|
|
29
|
+
if (isReferenceElementType(getTokenType(node))) {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
if (node.getAttribute('contenteditable') === 'true') {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
node = node.parentNode;
|
|
37
|
+
}
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Finds the reference wrapper element that contains the given node, if any.
|
|
42
|
+
* Returns null if the node is not inside a reference element.
|
|
43
|
+
*/
|
|
44
|
+
export function findContainingReference(node) {
|
|
45
|
+
while (node) {
|
|
46
|
+
if (node.nodeName === 'P') {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
if (isHTMLElement(node) && isReferenceElementType(getTokenType(node))) {
|
|
50
|
+
return node;
|
|
51
|
+
}
|
|
52
|
+
node = node.parentNode;
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Moves a collapsed caret out of non-typeable positions into the parent paragraph.
|
|
58
|
+
* Handles caret inside reference element internals (caret spots, token container)
|
|
59
|
+
* and caret on the contentEditable div itself (clicking on padding).
|
|
60
|
+
* Some browsers (notably Firefox) may place the caret in these positions on focus
|
|
61
|
+
* or imprecise clicks.
|
|
62
|
+
*/
|
|
63
|
+
export function normalizeCollapsedCaret(selection) {
|
|
64
|
+
var _a;
|
|
65
|
+
if (!(selection === null || selection === void 0 ? void 0 : selection.rangeCount)) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const range = selection.getRangeAt(0);
|
|
69
|
+
if (!range.collapsed) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const container = range.startContainer;
|
|
73
|
+
// Walk up from the caret position to find a reference wrapper.
|
|
74
|
+
let node = isTextNode(container) ? container.parentElement : container;
|
|
75
|
+
let wrapper = null;
|
|
76
|
+
let caretSpotType = null;
|
|
77
|
+
while (node && isHTMLElement(node)) {
|
|
78
|
+
const tokenType = getTokenType(node);
|
|
79
|
+
if (isCaretSpotType(tokenType)) {
|
|
80
|
+
caretSpotType = tokenType;
|
|
81
|
+
}
|
|
82
|
+
if (isReferenceElementType(tokenType)) {
|
|
83
|
+
wrapper = node;
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
node = node.parentElement;
|
|
87
|
+
}
|
|
88
|
+
if (!wrapper) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const paragraph = wrapper.parentElement;
|
|
92
|
+
if (!paragraph) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const wrapperIndex = Array.from(paragraph.childNodes).indexOf(wrapper);
|
|
96
|
+
// If we know the caret was in the before-spot, position before the wrapper.
|
|
97
|
+
// Otherwise position after it (after-spot, token container, or wrapper itself).
|
|
98
|
+
const newOffset = caretSpotType === ElementType.CaretSpotBefore ? wrapperIndex : wrapperIndex + 1;
|
|
99
|
+
// Guard: skip if the selection is already at the target position.
|
|
100
|
+
if (range.startContainer === paragraph && range.startOffset === newOffset) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const newRange = ((_a = container.ownerDocument) !== null && _a !== void 0 ? _a : document).createRange();
|
|
104
|
+
newRange.setStart(paragraph, newOffset);
|
|
105
|
+
newRange.collapse(true);
|
|
106
|
+
selection.removeAllRanges();
|
|
107
|
+
selection.addRange(newRange);
|
|
108
|
+
}
|
|
109
|
+
/** Adjusts non-collapsed selection boundaries to exclude caret spot elements. */
|
|
110
|
+
export function normalizeSelection(selection, skipCaretSpots = false) {
|
|
111
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
112
|
+
if (!(selection === null || selection === void 0 ? void 0 : selection.rangeCount)) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
const range = selection.getRangeAt(0);
|
|
116
|
+
if (range.collapsed || isMouseDown || skipCaretSpots) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const normalizeBoundary = (container) => {
|
|
120
|
+
if (!isTextNode(container)) {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
const parent = container.parentElement;
|
|
124
|
+
if (!parent) {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
const parentType = getTokenType(parent);
|
|
128
|
+
if (!isCaretSpotType(parentType)) {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
const wrapper = parent.parentElement;
|
|
132
|
+
if (!wrapper || !isReferenceElementType(getTokenType(wrapper))) {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
const paragraph = wrapper.parentElement;
|
|
136
|
+
if (!paragraph) {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
const wrapperIndex = Array.from(paragraph.childNodes).indexOf(wrapper);
|
|
140
|
+
const newOffset = parentType === ElementType.CaretSpotBefore ? wrapperIndex : wrapperIndex + 1;
|
|
141
|
+
return { container: paragraph, offset: newOffset };
|
|
142
|
+
};
|
|
143
|
+
const normalizedStart = normalizeBoundary(range.startContainer);
|
|
144
|
+
const normalizedEnd = normalizeBoundary(range.endContainer);
|
|
145
|
+
if (!normalizedStart && !normalizedEnd) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
const newStartContainer = (_a = normalizedStart === null || normalizedStart === void 0 ? void 0 : normalizedStart.container) !== null && _a !== void 0 ? _a : range.startContainer;
|
|
149
|
+
const newStartOffset = (_b = normalizedStart === null || normalizedStart === void 0 ? void 0 : normalizedStart.offset) !== null && _b !== void 0 ? _b : range.startOffset;
|
|
150
|
+
const newEndContainer = (_c = normalizedEnd === null || normalizedEnd === void 0 ? void 0 : normalizedEnd.container) !== null && _c !== void 0 ? _c : range.endContainer;
|
|
151
|
+
const newEndOffset = (_d = normalizedEnd === null || normalizedEnd === void 0 ? void 0 : normalizedEnd.offset) !== null && _d !== void 0 ? _d : range.endOffset;
|
|
152
|
+
// Guard: skip if the selection already matches the normalized boundaries.
|
|
153
|
+
// Prevents Safari from entering an infinite selectionchange loop in RTL.
|
|
154
|
+
if (range.startContainer === newStartContainer &&
|
|
155
|
+
range.startOffset === newStartOffset &&
|
|
156
|
+
range.endContainer === newEndContainer &&
|
|
157
|
+
range.endOffset === newEndOffset) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
// Determine if the selection is backward (focus before anchor in document order).
|
|
161
|
+
// Range always has start <= end, but the user may be selecting in reverse.
|
|
162
|
+
const isBackward = selection.anchorNode === range.endContainer &&
|
|
163
|
+
selection.anchorOffset === range.endOffset &&
|
|
164
|
+
selection.focusNode === range.startContainer &&
|
|
165
|
+
selection.focusOffset === range.startOffset;
|
|
166
|
+
if (isBackward) {
|
|
167
|
+
// Preserve backward direction: collapse to anchor (end), extend to focus (start)
|
|
168
|
+
const anchorContainer = (_e = normalizedEnd === null || normalizedEnd === void 0 ? void 0 : normalizedEnd.container) !== null && _e !== void 0 ? _e : range.endContainer;
|
|
169
|
+
const anchorOffset = (_f = normalizedEnd === null || normalizedEnd === void 0 ? void 0 : normalizedEnd.offset) !== null && _f !== void 0 ? _f : range.endOffset;
|
|
170
|
+
const focusContainer = (_g = normalizedStart === null || normalizedStart === void 0 ? void 0 : normalizedStart.container) !== null && _g !== void 0 ? _g : range.startContainer;
|
|
171
|
+
const focusOffset = (_h = normalizedStart === null || normalizedStart === void 0 ? void 0 : normalizedStart.offset) !== null && _h !== void 0 ? _h : range.startOffset;
|
|
172
|
+
selection.collapse(anchorContainer, anchorOffset);
|
|
173
|
+
selection.extend(focusContainer, focusOffset);
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
const updatedRange = ((_j = range.startContainer.ownerDocument) !== null && _j !== void 0 ? _j : document).createRange();
|
|
177
|
+
updatedRange.setStart(newStartContainer, newStartOffset);
|
|
178
|
+
updatedRange.setEnd(newEndContainer, newEndOffset);
|
|
179
|
+
selection.removeAllRanges();
|
|
180
|
+
selection.addRange(updatedRange);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=caret-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"caret-utils.js","sourceRoot":"","sources":["../../../../src/prompt-input/core/caret-utils.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { isHTMLElement } from '../../internal/utils/dom';\nimport { ElementType } from './constants';\nimport { getTokenType, isCaretSpotType, isReferenceElementType } from './dom-utils';\nimport { isTextNode } from './type-guards';\n\n// Module-level flag: mouse state is inherently global (one mouse per document)\n// and must be accessible from the standalone normalizeSelection function.\nlet isMouseDown = false;\n\n/** Updates the mouse-down tracking flag used to skip selection normalization during drag. */\nexport function setMouseDown(value: boolean): void {\n isMouseDown = value;\n}\n\n/** Returns whether the mouse is currently pressed. */\nexport function getMouseDown(): boolean {\n return isMouseDown;\n}\n\n/**\n * Checks whether a node is inside a reference element's internals or directly\n * on the contentEditable div (not inside a paragraph). These are non-typeable\n * positions where the caret should not rest.\n */\nexport function isNonTypeablePosition(node: Node | null): boolean {\n while (node) {\n if (node.nodeName === 'P') {\n return false;\n }\n if (isHTMLElement(node)) {\n if (isReferenceElementType(getTokenType(node))) {\n return true;\n }\n if (node.getAttribute('contenteditable') === 'true') {\n return true;\n }\n }\n node = node.parentNode;\n }\n return false;\n}\n\n/**\n * Finds the reference wrapper element that contains the given node, if any.\n * Returns null if the node is not inside a reference element.\n */\nexport function findContainingReference(node: Node | null): HTMLElement | null {\n while (node) {\n if (node.nodeName === 'P') {\n return null;\n }\n if (isHTMLElement(node) && isReferenceElementType(getTokenType(node))) {\n return node;\n }\n node = node.parentNode;\n }\n return null;\n}\n\n/**\n * Moves a collapsed caret out of non-typeable positions into the parent paragraph.\n * Handles caret inside reference element internals (caret spots, token container)\n * and caret on the contentEditable div itself (clicking on padding).\n * Some browsers (notably Firefox) may place the caret in these positions on focus\n * or imprecise clicks.\n */\nexport function normalizeCollapsedCaret(selection: Selection | null): void {\n if (!selection?.rangeCount) {\n return;\n }\n\n const range = selection.getRangeAt(0);\n\n if (!range.collapsed) {\n return;\n }\n\n const container = range.startContainer;\n\n // Walk up from the caret position to find a reference wrapper.\n let node: Node | null = isTextNode(container) ? container.parentElement : (container as HTMLElement);\n let wrapper: HTMLElement | null = null;\n let caretSpotType: ElementType | null = null;\n\n while (node && isHTMLElement(node)) {\n const tokenType = getTokenType(node);\n\n if (isCaretSpotType(tokenType)) {\n caretSpotType = tokenType as ElementType;\n }\n\n if (isReferenceElementType(tokenType)) {\n wrapper = node;\n break;\n }\n\n node = node.parentElement;\n }\n\n if (!wrapper) {\n return;\n }\n\n const paragraph = wrapper.parentElement;\n if (!paragraph) {\n return;\n }\n\n const wrapperIndex = Array.from(paragraph.childNodes).indexOf(wrapper);\n\n // If we know the caret was in the before-spot, position before the wrapper.\n // Otherwise position after it (after-spot, token container, or wrapper itself).\n const newOffset = caretSpotType === ElementType.CaretSpotBefore ? wrapperIndex : wrapperIndex + 1;\n\n // Guard: skip if the selection is already at the target position.\n if (range.startContainer === paragraph && range.startOffset === newOffset) {\n return;\n }\n\n const newRange = (container.ownerDocument ?? document).createRange();\n newRange.setStart(paragraph, newOffset);\n newRange.collapse(true);\n selection.removeAllRanges();\n selection.addRange(newRange);\n}\n\n/** Adjusts non-collapsed selection boundaries to exclude caret spot elements. */\nexport function normalizeSelection(selection: Selection | null, skipCaretSpots: boolean = false): void {\n if (!selection?.rangeCount) {\n return;\n }\n\n const range = selection.getRangeAt(0);\n\n if (range.collapsed || isMouseDown || skipCaretSpots) {\n return;\n }\n\n const normalizeBoundary = (container: Node) => {\n if (!isTextNode(container)) {\n return null;\n }\n\n const parent = container.parentElement;\n if (!parent) {\n return null;\n }\n\n const parentType = getTokenType(parent);\n if (!isCaretSpotType(parentType)) {\n return null;\n }\n\n const wrapper = parent.parentElement;\n if (!wrapper || !isReferenceElementType(getTokenType(wrapper))) {\n return null;\n }\n\n const paragraph = wrapper.parentElement;\n if (!paragraph) {\n return null;\n }\n\n const wrapperIndex = Array.from(paragraph.childNodes).indexOf(wrapper);\n const newOffset = parentType === ElementType.CaretSpotBefore ? wrapperIndex : wrapperIndex + 1;\n\n return { container: paragraph, offset: newOffset };\n };\n\n const normalizedStart = normalizeBoundary(range.startContainer);\n const normalizedEnd = normalizeBoundary(range.endContainer);\n\n if (!normalizedStart && !normalizedEnd) {\n return;\n }\n\n const newStartContainer = normalizedStart?.container ?? range.startContainer;\n const newStartOffset = normalizedStart?.offset ?? range.startOffset;\n const newEndContainer = normalizedEnd?.container ?? range.endContainer;\n const newEndOffset = normalizedEnd?.offset ?? range.endOffset;\n\n // Guard: skip if the selection already matches the normalized boundaries.\n // Prevents Safari from entering an infinite selectionchange loop in RTL.\n if (\n range.startContainer === newStartContainer &&\n range.startOffset === newStartOffset &&\n range.endContainer === newEndContainer &&\n range.endOffset === newEndOffset\n ) {\n return;\n }\n\n // Determine if the selection is backward (focus before anchor in document order).\n // Range always has start <= end, but the user may be selecting in reverse.\n const isBackward =\n selection.anchorNode === range.endContainer &&\n selection.anchorOffset === range.endOffset &&\n selection.focusNode === range.startContainer &&\n selection.focusOffset === range.startOffset;\n\n if (isBackward) {\n // Preserve backward direction: collapse to anchor (end), extend to focus (start)\n const anchorContainer = normalizedEnd?.container ?? range.endContainer;\n const anchorOffset = normalizedEnd?.offset ?? range.endOffset;\n const focusContainer = normalizedStart?.container ?? range.startContainer;\n const focusOffset = normalizedStart?.offset ?? range.startOffset;\n\n selection.collapse(anchorContainer, anchorOffset);\n selection.extend(focusContainer, focusOffset);\n } else {\n const updatedRange = (range.startContainer.ownerDocument ?? document).createRange();\n updatedRange.setStart(newStartContainer, newStartOffset);\n updatedRange.setEnd(newEndContainer, newEndOffset);\n selection.removeAllRanges();\n selection.addRange(updatedRange);\n }\n}\n"]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare enum ElementType {
|
|
2
|
+
Reference = "reference",
|
|
3
|
+
Pinned = "pinned",
|
|
4
|
+
CaretSpotBefore = "cursor-spot-before",
|
|
5
|
+
CaretSpotAfter = "cursor-spot-after",
|
|
6
|
+
Trigger = "trigger",
|
|
7
|
+
TrailingBreak = "trailing-break",
|
|
8
|
+
}
|
|
9
|
+
export declare const SPECIAL_CHARS: {
|
|
10
|
+
ZERO_WIDTH_CHARACTER: string;
|
|
11
|
+
NEWLINE: string;
|
|
12
|
+
};
|
|
13
|
+
export declare const DEFAULT_MAX_ROWS = 3;
|
|
14
|
+
export declare const NEXT_TICK_TIMEOUT = 0;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../src/prompt-input/core/constants.ts"],"names":[],"mappings":"AAGA,oBAAY,WAAW;IACrB,SAAS,cAAc;IACvB,MAAM,WAAW;IACjB,eAAe,uBAAuB;IACtC,cAAc,sBAAsB;IACpC,OAAO,YAAY;IACnB,aAAa,mBAAmB;CACjC;AAED,eAAO,MAAM,aAAa;;;CAGzB,CAAC;AAEF,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAClC,eAAO,MAAM,iBAAiB,IAAI,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
export var ElementType;
|
|
4
|
+
(function (ElementType) {
|
|
5
|
+
ElementType["Reference"] = "reference";
|
|
6
|
+
ElementType["Pinned"] = "pinned";
|
|
7
|
+
ElementType["CaretSpotBefore"] = "cursor-spot-before";
|
|
8
|
+
ElementType["CaretSpotAfter"] = "cursor-spot-after";
|
|
9
|
+
ElementType["Trigger"] = "trigger";
|
|
10
|
+
ElementType["TrailingBreak"] = "trailing-break";
|
|
11
|
+
})(ElementType || (ElementType = {}));
|
|
12
|
+
export const SPECIAL_CHARS = {
|
|
13
|
+
ZERO_WIDTH_CHARACTER: '\u200B',
|
|
14
|
+
NEWLINE: '\n',
|
|
15
|
+
};
|
|
16
|
+
export const DEFAULT_MAX_ROWS = 3;
|
|
17
|
+
export const NEXT_TICK_TIMEOUT = 0;
|
|
18
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../../src/prompt-input/core/constants.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,sCAAsC;AAEtC,MAAM,CAAN,IAAY,WAOX;AAPD,WAAY,WAAW;IACrB,sCAAuB,CAAA;IACvB,gCAAiB,CAAA;IACjB,qDAAsC,CAAA;IACtC,mDAAoC,CAAA;IACpC,kCAAmB,CAAA;IACnB,+CAAgC,CAAA;AAClC,CAAC,EAPW,WAAW,KAAX,WAAW,QAOtB;AAED,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,oBAAoB,EAAE,QAAQ;IAC9B,OAAO,EAAE,IAAI;CACd,CAAC;AAEF,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC;AAClC,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC","sourcesContent":["// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\n\nexport enum ElementType {\n Reference = 'reference',\n Pinned = 'pinned',\n CaretSpotBefore = 'cursor-spot-before',\n CaretSpotAfter = 'cursor-spot-after',\n Trigger = 'trigger',\n TrailingBreak = 'trailing-break',\n}\n\nexport const SPECIAL_CHARS = {\n ZERO_WIDTH_CHARACTER: '\\u200B',\n NEWLINE: '\\n',\n};\n\nexport const DEFAULT_MAX_ROWS = 3;\nexport const NEXT_TICK_TIMEOUT = 0;\n"]}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { ElementType } from './constants';
|
|
2
|
+
/** Reads the data-type attribute from an element and returns it as a typed ElementType. */
|
|
3
|
+
export declare function getTokenType(element: HTMLElement): ElementType | null;
|
|
4
|
+
/** Checks if a token type represents a reference element (inline or pinned). */
|
|
5
|
+
export declare function isReferenceElementType(tokenType: ElementType | string | null): boolean;
|
|
6
|
+
/** Inserts a node immediately after a reference node in the DOM. */
|
|
7
|
+
export declare function insertAfter(newNode: Node, referenceNode: Node): void;
|
|
8
|
+
/** Creates a styled paragraph element for the contentEditable container. */
|
|
9
|
+
export declare function createParagraph(ownerDoc?: Document): HTMLParagraphElement;
|
|
10
|
+
/** Creates a trailing BR element used as a placeholder in empty paragraphs. */
|
|
11
|
+
export declare function createTrailingBreak(ownerDoc?: Document): HTMLBRElement;
|
|
12
|
+
/**
|
|
13
|
+
* Generates a unique ID for DOM elements outside of React context.
|
|
14
|
+
* Uses the same format as component-toolkit's useRandomId hook, but as a plain
|
|
15
|
+
* function since token IDs are generated during DOM manipulation (not in React renders).
|
|
16
|
+
*/
|
|
17
|
+
export declare function generateTokenId(): string;
|
|
18
|
+
/** Strips zero-width characters used for cursor positioning. */
|
|
19
|
+
export declare function stripZeroWidthCharacters(text: string): string;
|
|
20
|
+
interface TokenQueryOptions {
|
|
21
|
+
tokenType?: string | string[];
|
|
22
|
+
tokenId?: string;
|
|
23
|
+
}
|
|
24
|
+
/** Finds the first element matching the given token type and/or token ID within a container. */
|
|
25
|
+
export declare function findElement(container: HTMLElement, options: TokenQueryOptions): HTMLElement | null;
|
|
26
|
+
/** Returns all paragraph elements within a container. */
|
|
27
|
+
export declare function findAllParagraphs(container: HTMLElement | DocumentFragment): HTMLParagraphElement[];
|
|
28
|
+
/** Checks if an element has no meaningful content (ignoring whitespace and trailing BRs). */
|
|
29
|
+
export declare function isElementEffectivelyEmpty(element: HTMLElement): boolean;
|
|
30
|
+
export declare function hasOnlyTrailingBR(paragraph: HTMLElement): boolean;
|
|
31
|
+
export declare function isEmptyState(element: HTMLElement): boolean;
|
|
32
|
+
/** Resets the element to a single empty paragraph with a trailing BR. */
|
|
33
|
+
export declare function setEmptyState(element: HTMLElement): void;
|
|
34
|
+
/** Checks if a token type represents a caret spot element. */
|
|
35
|
+
export declare function isCaretSpotType(tokenType: ElementType | string | null): boolean;
|
|
36
|
+
export interface AdjacentTokenResult {
|
|
37
|
+
sibling: Node | null;
|
|
38
|
+
isReferenceToken: boolean;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Finds the adjacent sibling node in the given direction and checks if it's a reference token.
|
|
42
|
+
* @param container the node where the cursor currently sits
|
|
43
|
+
* @param offset cursor offset within the container
|
|
44
|
+
* @param direction which direction to look
|
|
45
|
+
*/
|
|
46
|
+
export declare function findAdjacentToken(container: Node, offset: number, direction: 'backward' | 'forward'): AdjacentTokenResult;
|
|
47
|
+
/**
|
|
48
|
+
* When the caret lands immediately after a trigger element (e.g. at offset 0 of the
|
|
49
|
+
* next text node, or at the paragraph-level offset right after the trigger), nudge it
|
|
50
|
+
* inside the trigger's text node so that `findActiveTrigger` detects it correctly.
|
|
51
|
+
* @param cancelledIds Set of trigger IDs that are cancelled — caret won't be nudged into these.
|
|
52
|
+
*/
|
|
53
|
+
export declare function normalizeCaretIntoTrigger(editableElement: HTMLElement, cancelledIds?: Set<string>): void;
|
|
54
|
+
/**
|
|
55
|
+
* Scrolls the caret into view within a scrollable contentEditable element.
|
|
56
|
+
* Inserts a temporary span at the selection to measure position, scrolls if
|
|
57
|
+
* needed, then removes the span.
|
|
58
|
+
*/
|
|
59
|
+
export declare function scrollCaretIntoView(element: HTMLElement): void;
|
|
60
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dom-utils.d.ts","sourceRoot":"","sources":["../../../../src/prompt-input/core/dom-utils.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,WAAW,EAAiB,MAAM,aAAa,CAAC;AAKzD,2FAA2F;AAC3F,wBAAgB,YAAY,CAAC,OAAO,EAAE,WAAW,GAAG,WAAW,GAAG,IAAI,CAarE;AACD,gFAAgF;AAChF,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,WAAW,GAAG,MAAM,GAAG,IAAI,GAAG,OAAO,CAEtF;AAED,oEAAoE;AACpE,wBAAgB,WAAW,CAAC,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,GAAG,IAAI,CAWpE;AAED,4EAA4E;AAC5E,wBAAgB,eAAe,CAAC,QAAQ,GAAE,QAAmB,GAAG,oBAAoB,CAInF;AAED,+EAA+E;AAC/E,wBAAgB,mBAAmB,CAAC,QAAQ,GAAE,QAAmB,GAAG,aAAa,CAIhF;AAID;;;;GAIG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED,gEAAgE;AAChE,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE7D;AAED,UAAU,iBAAiB;IACzB,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAeD,gGAAgG;AAChG,wBAAgB,WAAW,CAAC,SAAS,EAAE,WAAW,EAAE,OAAO,EAAE,iBAAiB,GAAG,WAAW,GAAG,IAAI,CAGlG;AAED,yDAAyD;AACzD,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,WAAW,GAAG,gBAAgB,GAAG,oBAAoB,EAAE,CAEnG;AAED,6FAA6F;AAC7F,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAiBvE;AAED,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,WAAW,GAAG,OAAO,CAEjE;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAG1D;AAED,yEAAyE;AACzE,wBAAgB,aAAa,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,CAWxD;AAED,8DAA8D;AAC9D,wBAAgB,eAAe,CAAC,SAAS,EAAE,WAAW,GAAG,MAAM,GAAG,IAAI,GAAG,OAAO,CAE/E;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,IAAI,GAAG,IAAI,CAAC;IACrB,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,IAAI,EACf,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,UAAU,GAAG,SAAS,GAChC,mBAAmB,CAqBrB;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,eAAe,EAAE,WAAW,EAAE,YAAY,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,CAuDxG;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,CA4C9D"}
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { isHTMLElement } from '../../internal/utils/dom';
|
|
4
|
+
import { getOwnerSelection } from './caret-controller';
|
|
5
|
+
import { ElementType, SPECIAL_CHARS } from './constants';
|
|
6
|
+
import { isBRElement, isTextNode } from './type-guards';
|
|
7
|
+
import styles from '../styles.css.js';
|
|
8
|
+
/** Reads the data-type attribute from an element and returns it as a typed ElementType. */
|
|
9
|
+
export function getTokenType(element) {
|
|
10
|
+
const value = element.getAttribute('data-type');
|
|
11
|
+
switch (value) {
|
|
12
|
+
case ElementType.Reference:
|
|
13
|
+
case ElementType.Pinned:
|
|
14
|
+
case ElementType.CaretSpotBefore:
|
|
15
|
+
case ElementType.CaretSpotAfter:
|
|
16
|
+
case ElementType.Trigger:
|
|
17
|
+
case ElementType.TrailingBreak:
|
|
18
|
+
return value;
|
|
19
|
+
default:
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/** Checks if a token type represents a reference element (inline or pinned). */
|
|
24
|
+
export function isReferenceElementType(tokenType) {
|
|
25
|
+
return tokenType === ElementType.Reference || tokenType === ElementType.Pinned;
|
|
26
|
+
}
|
|
27
|
+
/** Inserts a node immediately after a reference node in the DOM. */
|
|
28
|
+
export function insertAfter(newNode, referenceNode) {
|
|
29
|
+
const parent = referenceNode.parentNode;
|
|
30
|
+
if (!parent) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (referenceNode.nextSibling) {
|
|
34
|
+
parent.insertBefore(newNode, referenceNode.nextSibling);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
parent.appendChild(newNode);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/** Creates a styled paragraph element for the contentEditable container. */
|
|
41
|
+
export function createParagraph(ownerDoc = document) {
|
|
42
|
+
const p = ownerDoc.createElement('p');
|
|
43
|
+
p.className = styles.paragraph;
|
|
44
|
+
return p;
|
|
45
|
+
}
|
|
46
|
+
/** Creates a trailing BR element used as a placeholder in empty paragraphs. */
|
|
47
|
+
export function createTrailingBreak(ownerDoc = document) {
|
|
48
|
+
const br = ownerDoc.createElement('br');
|
|
49
|
+
br.setAttribute('data-id', ElementType.TrailingBreak);
|
|
50
|
+
return br;
|
|
51
|
+
}
|
|
52
|
+
let idCounter = 0;
|
|
53
|
+
/**
|
|
54
|
+
* Generates a unique ID for DOM elements outside of React context.
|
|
55
|
+
* Uses the same format as component-toolkit's useRandomId hook, but as a plain
|
|
56
|
+
* function since token IDs are generated during DOM manipulation (not in React renders).
|
|
57
|
+
*/
|
|
58
|
+
export function generateTokenId() {
|
|
59
|
+
return `${idCounter++}-${Date.now()}-${Math.round(Math.random() * 10000)}`;
|
|
60
|
+
}
|
|
61
|
+
/** Strips zero-width characters used for cursor positioning. */
|
|
62
|
+
export function stripZeroWidthCharacters(text) {
|
|
63
|
+
return text.replace(new RegExp(SPECIAL_CHARS.ZERO_WIDTH_CHARACTER, 'g'), '');
|
|
64
|
+
}
|
|
65
|
+
function buildTokenSelector(options) {
|
|
66
|
+
const { tokenType = [], tokenId } = options;
|
|
67
|
+
const types = Array.isArray(tokenType) ? tokenType : [tokenType];
|
|
68
|
+
let selector = types.length > 0 ? types.map(type => `[data-type="${type}"]`).join(', ') : '';
|
|
69
|
+
if (tokenId) {
|
|
70
|
+
selector += `[data-id="${tokenId}"]`;
|
|
71
|
+
}
|
|
72
|
+
return selector;
|
|
73
|
+
}
|
|
74
|
+
/** Finds the first element matching the given token type and/or token ID within a container. */
|
|
75
|
+
export function findElement(container, options) {
|
|
76
|
+
const selector = buildTokenSelector(options);
|
|
77
|
+
return selector ? container.querySelector(selector) : null;
|
|
78
|
+
}
|
|
79
|
+
/** Returns all paragraph elements within a container. */
|
|
80
|
+
export function findAllParagraphs(container) {
|
|
81
|
+
return Array.from(container.querySelectorAll('p'));
|
|
82
|
+
}
|
|
83
|
+
/** Checks if an element has no meaningful content (ignoring whitespace and trailing BRs). */
|
|
84
|
+
export function isElementEffectivelyEmpty(element) {
|
|
85
|
+
if (element.childNodes.length === 0) {
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
for (const child of Array.from(element.childNodes)) {
|
|
89
|
+
if (isTextNode(child)) {
|
|
90
|
+
if (child.textContent && child.textContent.trim() !== '') {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
else if (isBRElement(child)) {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
export function hasOnlyTrailingBR(paragraph) {
|
|
104
|
+
return paragraph.childNodes.length === 1 && isBRElement(paragraph.firstChild);
|
|
105
|
+
}
|
|
106
|
+
export function isEmptyState(element) {
|
|
107
|
+
const paragraphs = findAllParagraphs(element);
|
|
108
|
+
return paragraphs.length === 0 || (paragraphs.length === 1 && hasOnlyTrailingBR(paragraphs[0]));
|
|
109
|
+
}
|
|
110
|
+
/** Resets the element to a single empty paragraph with a trailing BR. */
|
|
111
|
+
export function setEmptyState(element) {
|
|
112
|
+
const paragraphs = findAllParagraphs(element);
|
|
113
|
+
if (paragraphs.length === 1 && hasOnlyTrailingBR(paragraphs[0])) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
element.textContent = '';
|
|
117
|
+
const p = createParagraph();
|
|
118
|
+
p.appendChild(createTrailingBreak());
|
|
119
|
+
element.appendChild(p);
|
|
120
|
+
}
|
|
121
|
+
/** Checks if a token type represents a caret spot element. */
|
|
122
|
+
export function isCaretSpotType(tokenType) {
|
|
123
|
+
return tokenType === ElementType.CaretSpotBefore || tokenType === ElementType.CaretSpotAfter;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Finds the adjacent sibling node in the given direction and checks if it's a reference token.
|
|
127
|
+
* @param container the node where the cursor currently sits
|
|
128
|
+
* @param offset cursor offset within the container
|
|
129
|
+
* @param direction which direction to look
|
|
130
|
+
*/
|
|
131
|
+
export function findAdjacentToken(container, offset, direction) {
|
|
132
|
+
var _a;
|
|
133
|
+
let sibling = null;
|
|
134
|
+
if (isTextNode(container)) {
|
|
135
|
+
const isAtBoundary = direction === 'backward' ? offset === 0 : offset === (((_a = container.textContent) === null || _a === void 0 ? void 0 : _a.length) || 0);
|
|
136
|
+
if (isAtBoundary) {
|
|
137
|
+
sibling = direction === 'backward' ? container.previousSibling : container.nextSibling;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
else if (isHTMLElement(container)) {
|
|
141
|
+
if (direction === 'backward') {
|
|
142
|
+
sibling = offset > 0 ? container.childNodes[offset - 1] : container.previousSibling;
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
sibling = offset < container.childNodes.length ? container.childNodes[offset] : container.nextSibling;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
const siblingType = isHTMLElement(sibling) ? getTokenType(sibling) : null;
|
|
149
|
+
const isReferenceToken = isReferenceElementType(siblingType);
|
|
150
|
+
return { sibling, isReferenceToken };
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* When the caret lands immediately after a trigger element (e.g. at offset 0 of the
|
|
154
|
+
* next text node, or at the paragraph-level offset right after the trigger), nudge it
|
|
155
|
+
* inside the trigger's text node so that `findActiveTrigger` detects it correctly.
|
|
156
|
+
* @param cancelledIds Set of trigger IDs that are cancelled — caret won't be nudged into these.
|
|
157
|
+
*/
|
|
158
|
+
export function normalizeCaretIntoTrigger(editableElement, cancelledIds) {
|
|
159
|
+
var _a, _b;
|
|
160
|
+
const selection = getOwnerSelection(editableElement);
|
|
161
|
+
if (!(selection === null || selection === void 0 ? void 0 : selection.rangeCount)) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
const range = selection.getRangeAt(0);
|
|
165
|
+
if (!range.collapsed) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
let triggerElement = null;
|
|
169
|
+
if (isTextNode(range.startContainer) && range.startOffset === 0) {
|
|
170
|
+
const prevSibling = range.startContainer.previousSibling;
|
|
171
|
+
if (isHTMLElement(prevSibling) && getTokenType(prevSibling) === ElementType.Trigger) {
|
|
172
|
+
// Only nudge when the text node is empty — a non-empty node means the
|
|
173
|
+
// cursor is legitimately positioned at the start of user text.
|
|
174
|
+
if (!range.startContainer.textContent) {
|
|
175
|
+
triggerElement = prevSibling;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
// Caret at offset 0 inside a trigger's own text node means the cursor is
|
|
179
|
+
// right before the trigger symbol. Nudge it to the end of whatever precedes
|
|
180
|
+
// this trigger — either the previous trigger or a text node boundary.
|
|
181
|
+
const parent = range.startContainer.parentElement;
|
|
182
|
+
if (!triggerElement && parent && getTokenType(parent) === ElementType.Trigger) {
|
|
183
|
+
const paragraph = parent.parentElement;
|
|
184
|
+
if (paragraph) {
|
|
185
|
+
const idx = Array.from(paragraph.childNodes).indexOf(parent);
|
|
186
|
+
if (idx > 0) {
|
|
187
|
+
range.setStart(paragraph, idx);
|
|
188
|
+
range.collapse(true);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
else if (range.startContainer === editableElement || isHTMLElement(range.startContainer)) {
|
|
195
|
+
const container = range.startContainer;
|
|
196
|
+
const childNodes = Array.from(container.childNodes);
|
|
197
|
+
const nodeBeforeCaret = childNodes[range.startOffset - 1];
|
|
198
|
+
if (isHTMLElement(nodeBeforeCaret) && getTokenType(nodeBeforeCaret) === ElementType.Trigger) {
|
|
199
|
+
triggerElement = nodeBeforeCaret;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
if (triggerElement && !(cancelledIds === null || cancelledIds === void 0 ? void 0 : cancelledIds.has(triggerElement.id))) {
|
|
203
|
+
const triggerTextNode = triggerElement.childNodes[0];
|
|
204
|
+
if (isTextNode(triggerTextNode)) {
|
|
205
|
+
range.setStart(triggerTextNode, (_b = (_a = triggerTextNode.textContent) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0);
|
|
206
|
+
range.collapse(true);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Scrolls the caret into view within a scrollable contentEditable element.
|
|
212
|
+
* Inserts a temporary span at the selection to measure position, scrolls if
|
|
213
|
+
* needed, then removes the span.
|
|
214
|
+
*/
|
|
215
|
+
export function scrollCaretIntoView(element) {
|
|
216
|
+
var _a, _b, _c, _d, _e;
|
|
217
|
+
if (element.scrollHeight <= element.clientHeight) {
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
const selection = getOwnerSelection(element);
|
|
221
|
+
if (!(selection === null || selection === void 0 ? void 0 : selection.rangeCount)) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
const ownerDoc = (_a = element.ownerDocument) !== null && _a !== void 0 ? _a : document;
|
|
225
|
+
const range = selection.getRangeAt(0);
|
|
226
|
+
const tempSpan = ownerDoc.createElement('span');
|
|
227
|
+
range.insertNode(tempSpan);
|
|
228
|
+
const spanRect = tempSpan.getBoundingClientRect();
|
|
229
|
+
const elementRect = element.getBoundingClientRect();
|
|
230
|
+
const isOutOfView = spanRect.top < elementRect.top ||
|
|
231
|
+
spanRect.bottom > elementRect.bottom ||
|
|
232
|
+
spanRect.left < elementRect.left ||
|
|
233
|
+
spanRect.right > elementRect.right;
|
|
234
|
+
if (isOutOfView) {
|
|
235
|
+
tempSpan.scrollIntoView({ block: 'nearest', inline: 'nearest' });
|
|
236
|
+
}
|
|
237
|
+
// Remove the span and merge the split text nodes back together.
|
|
238
|
+
// insertNode splits a text node into [before, span, after]. After removing
|
|
239
|
+
// the span, merge "after" into "before" to restore the original text node.
|
|
240
|
+
const prev = tempSpan.previousSibling;
|
|
241
|
+
const next = tempSpan.nextSibling;
|
|
242
|
+
tempSpan.remove();
|
|
243
|
+
if (prev && next && prev.nodeType === Node.TEXT_NODE && next.nodeType === Node.TEXT_NODE) {
|
|
244
|
+
const mergeOffset = (_c = (_b = prev.textContent) === null || _b === void 0 ? void 0 : _b.length) !== null && _c !== void 0 ? _c : 0;
|
|
245
|
+
prev.textContent = ((_d = prev.textContent) !== null && _d !== void 0 ? _d : '') + ((_e = next.textContent) !== null && _e !== void 0 ? _e : '');
|
|
246
|
+
next.remove();
|
|
247
|
+
// Place the caret at the merge point
|
|
248
|
+
range.setStart(prev, mergeOffset);
|
|
249
|
+
range.collapse(true);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
//# sourceMappingURL=dom-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dom-utils.js","sourceRoot":"","sources":["../../../../src/prompt-input/core/dom-utils.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { isHTMLElement } from '../../internal/utils/dom';\nimport { getOwnerSelection } from './caret-controller';\nimport { ElementType, SPECIAL_CHARS } from './constants';\nimport { isBRElement, isTextNode } from './type-guards';\n\nimport styles from '../styles.css.js';\n\n/** Reads the data-type attribute from an element and returns it as a typed ElementType. */\nexport function getTokenType(element: HTMLElement): ElementType | null {\n const value = element.getAttribute('data-type');\n switch (value) {\n case ElementType.Reference:\n case ElementType.Pinned:\n case ElementType.CaretSpotBefore:\n case ElementType.CaretSpotAfter:\n case ElementType.Trigger:\n case ElementType.TrailingBreak:\n return value;\n default:\n return null;\n }\n}\n/** Checks if a token type represents a reference element (inline or pinned). */\nexport function isReferenceElementType(tokenType: ElementType | string | null): boolean {\n return tokenType === ElementType.Reference || tokenType === ElementType.Pinned;\n}\n\n/** Inserts a node immediately after a reference node in the DOM. */\nexport function insertAfter(newNode: Node, referenceNode: Node): void {\n const parent = referenceNode.parentNode;\n if (!parent) {\n return;\n }\n\n if (referenceNode.nextSibling) {\n parent.insertBefore(newNode, referenceNode.nextSibling);\n } else {\n parent.appendChild(newNode);\n }\n}\n\n/** Creates a styled paragraph element for the contentEditable container. */\nexport function createParagraph(ownerDoc: Document = document): HTMLParagraphElement {\n const p = ownerDoc.createElement('p');\n p.className = styles.paragraph;\n return p;\n}\n\n/** Creates a trailing BR element used as a placeholder in empty paragraphs. */\nexport function createTrailingBreak(ownerDoc: Document = document): HTMLBRElement {\n const br = ownerDoc.createElement('br');\n br.setAttribute('data-id', ElementType.TrailingBreak);\n return br;\n}\n\nlet idCounter = 0;\n\n/**\n * Generates a unique ID for DOM elements outside of React context.\n * Uses the same format as component-toolkit's useRandomId hook, but as a plain\n * function since token IDs are generated during DOM manipulation (not in React renders).\n */\nexport function generateTokenId(): string {\n return `${idCounter++}-${Date.now()}-${Math.round(Math.random() * 10000)}`;\n}\n\n/** Strips zero-width characters used for cursor positioning. */\nexport function stripZeroWidthCharacters(text: string): string {\n return text.replace(new RegExp(SPECIAL_CHARS.ZERO_WIDTH_CHARACTER, 'g'), '');\n}\n\ninterface TokenQueryOptions {\n tokenType?: string | string[];\n tokenId?: string;\n}\n\nfunction buildTokenSelector(options: TokenQueryOptions): string {\n const { tokenType = [], tokenId } = options;\n const types = Array.isArray(tokenType) ? tokenType : [tokenType];\n\n let selector = types.length > 0 ? types.map(type => `[data-type=\"${type}\"]`).join(', ') : '';\n\n if (tokenId) {\n selector += `[data-id=\"${tokenId}\"]`;\n }\n\n return selector;\n}\n\n/** Finds the first element matching the given token type and/or token ID within a container. */\nexport function findElement(container: HTMLElement, options: TokenQueryOptions): HTMLElement | null {\n const selector = buildTokenSelector(options);\n return selector ? container.querySelector<HTMLElement>(selector) : null;\n}\n\n/** Returns all paragraph elements within a container. */\nexport function findAllParagraphs(container: HTMLElement | DocumentFragment): HTMLParagraphElement[] {\n return Array.from(container.querySelectorAll<HTMLParagraphElement>('p'));\n}\n\n/** Checks if an element has no meaningful content (ignoring whitespace and trailing BRs). */\nexport function isElementEffectivelyEmpty(element: HTMLElement): boolean {\n if (element.childNodes.length === 0) {\n return true;\n }\n\n for (const child of Array.from(element.childNodes)) {\n if (isTextNode(child)) {\n if (child.textContent && child.textContent.trim() !== '') {\n return false;\n }\n } else if (isBRElement(child)) {\n continue;\n } else {\n return false;\n }\n }\n return true;\n}\n\nexport function hasOnlyTrailingBR(paragraph: HTMLElement): boolean {\n return paragraph.childNodes.length === 1 && isBRElement(paragraph.firstChild);\n}\n\nexport function isEmptyState(element: HTMLElement): boolean {\n const paragraphs = findAllParagraphs(element);\n return paragraphs.length === 0 || (paragraphs.length === 1 && hasOnlyTrailingBR(paragraphs[0]));\n}\n\n/** Resets the element to a single empty paragraph with a trailing BR. */\nexport function setEmptyState(element: HTMLElement): void {\n const paragraphs = findAllParagraphs(element);\n\n if (paragraphs.length === 1 && hasOnlyTrailingBR(paragraphs[0])) {\n return;\n }\n\n element.textContent = '';\n const p = createParagraph();\n p.appendChild(createTrailingBreak());\n element.appendChild(p);\n}\n\n/** Checks if a token type represents a caret spot element. */\nexport function isCaretSpotType(tokenType: ElementType | string | null): boolean {\n return tokenType === ElementType.CaretSpotBefore || tokenType === ElementType.CaretSpotAfter;\n}\n\nexport interface AdjacentTokenResult {\n sibling: Node | null;\n isReferenceToken: boolean;\n}\n\n/**\n * Finds the adjacent sibling node in the given direction and checks if it's a reference token.\n * @param container the node where the cursor currently sits\n * @param offset cursor offset within the container\n * @param direction which direction to look\n */\nexport function findAdjacentToken(\n container: Node,\n offset: number,\n direction: 'backward' | 'forward'\n): AdjacentTokenResult {\n let sibling: Node | null = null;\n\n if (isTextNode(container)) {\n const isAtBoundary = direction === 'backward' ? offset === 0 : offset === (container.textContent?.length || 0);\n\n if (isAtBoundary) {\n sibling = direction === 'backward' ? container.previousSibling : container.nextSibling;\n }\n } else if (isHTMLElement(container)) {\n if (direction === 'backward') {\n sibling = offset > 0 ? container.childNodes[offset - 1] : container.previousSibling;\n } else {\n sibling = offset < container.childNodes.length ? container.childNodes[offset] : container.nextSibling;\n }\n }\n\n const siblingType = isHTMLElement(sibling) ? getTokenType(sibling) : null;\n const isReferenceToken = isReferenceElementType(siblingType);\n\n return { sibling, isReferenceToken };\n}\n\n/**\n * When the caret lands immediately after a trigger element (e.g. at offset 0 of the\n * next text node, or at the paragraph-level offset right after the trigger), nudge it\n * inside the trigger's text node so that `findActiveTrigger` detects it correctly.\n * @param cancelledIds Set of trigger IDs that are cancelled — caret won't be nudged into these.\n */\nexport function normalizeCaretIntoTrigger(editableElement: HTMLElement, cancelledIds?: Set<string>): void {\n const selection = getOwnerSelection(editableElement);\n if (!selection?.rangeCount) {\n return;\n }\n\n const range = selection.getRangeAt(0);\n if (!range.collapsed) {\n return;\n }\n\n let triggerElement: HTMLElement | null = null;\n\n if (isTextNode(range.startContainer) && range.startOffset === 0) {\n const prevSibling = range.startContainer.previousSibling;\n if (isHTMLElement(prevSibling) && getTokenType(prevSibling) === ElementType.Trigger) {\n // Only nudge when the text node is empty — a non-empty node means the\n // cursor is legitimately positioned at the start of user text.\n if (!range.startContainer.textContent) {\n triggerElement = prevSibling;\n }\n }\n\n // Caret at offset 0 inside a trigger's own text node means the cursor is\n // right before the trigger symbol. Nudge it to the end of whatever precedes\n // this trigger — either the previous trigger or a text node boundary.\n const parent = range.startContainer.parentElement;\n if (!triggerElement && parent && getTokenType(parent) === ElementType.Trigger) {\n const paragraph = parent.parentElement;\n if (paragraph) {\n const idx = Array.from(paragraph.childNodes).indexOf(parent);\n if (idx > 0) {\n range.setStart(paragraph, idx);\n range.collapse(true);\n }\n }\n return;\n }\n } else if (range.startContainer === editableElement || isHTMLElement(range.startContainer)) {\n const container = range.startContainer as HTMLElement;\n const childNodes = Array.from(container.childNodes);\n const nodeBeforeCaret = childNodes[range.startOffset - 1];\n\n if (isHTMLElement(nodeBeforeCaret) && getTokenType(nodeBeforeCaret) === ElementType.Trigger) {\n triggerElement = nodeBeforeCaret;\n }\n }\n\n if (triggerElement && !cancelledIds?.has(triggerElement.id)) {\n const triggerTextNode = triggerElement.childNodes[0];\n if (isTextNode(triggerTextNode)) {\n range.setStart(triggerTextNode, triggerTextNode.textContent?.length ?? 0);\n range.collapse(true);\n }\n }\n}\n\n/**\n * Scrolls the caret into view within a scrollable contentEditable element.\n * Inserts a temporary span at the selection to measure position, scrolls if\n * needed, then removes the span.\n */\nexport function scrollCaretIntoView(element: HTMLElement): void {\n if (element.scrollHeight <= element.clientHeight) {\n return;\n }\n\n const selection = getOwnerSelection(element);\n if (!selection?.rangeCount) {\n return;\n }\n\n const ownerDoc = element.ownerDocument ?? document;\n const range = selection.getRangeAt(0);\n\n const tempSpan = ownerDoc.createElement('span');\n range.insertNode(tempSpan);\n\n const spanRect = tempSpan.getBoundingClientRect();\n const elementRect = element.getBoundingClientRect();\n\n const isOutOfView =\n spanRect.top < elementRect.top ||\n spanRect.bottom > elementRect.bottom ||\n spanRect.left < elementRect.left ||\n spanRect.right > elementRect.right;\n\n if (isOutOfView) {\n tempSpan.scrollIntoView({ block: 'nearest', inline: 'nearest' });\n }\n\n // Remove the span and merge the split text nodes back together.\n // insertNode splits a text node into [before, span, after]. After removing\n // the span, merge \"after\" into \"before\" to restore the original text node.\n const prev = tempSpan.previousSibling;\n const next = tempSpan.nextSibling;\n tempSpan.remove();\n\n if (prev && next && prev.nodeType === Node.TEXT_NODE && next.nodeType === Node.TEXT_NODE) {\n const mergeOffset = prev.textContent?.length ?? 0;\n prev.textContent = (prev.textContent ?? '') + (next.textContent ?? '');\n next.remove();\n // Place the caret at the merge point\n range.setStart(prev, mergeOffset);\n range.collapse(true);\n }\n}\n"]}
|