@wordpress/components 32.6.0 → 33.0.1-next.v.202604201441.0
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/CHANGELOG.md +15 -0
- package/build/autocomplete/get-autocomplete-match.cjs +11 -2
- package/build/autocomplete/get-autocomplete-match.cjs.map +2 -2
- package/build/autocomplete/index.cjs +42 -11
- package/build/autocomplete/index.cjs.map +2 -2
- package/build/external-link/index.cjs +1 -1
- package/build/external-link/index.cjs.map +2 -2
- package/build/menu/styles.cjs +15 -15
- package/build/menu/styles.cjs.map +2 -2
- package/build/navigable-container/container.cjs +72 -110
- package/build/navigable-container/container.cjs.map +2 -2
- package/build-module/autocomplete/get-autocomplete-match.mjs +11 -2
- package/build-module/autocomplete/get-autocomplete-match.mjs.map +2 -2
- package/build-module/autocomplete/index.mjs +42 -11
- package/build-module/autocomplete/index.mjs.map +2 -2
- package/build-module/external-link/index.mjs +1 -1
- package/build-module/external-link/index.mjs.map +2 -2
- package/build-module/menu/styles.mjs +15 -15
- package/build-module/menu/styles.mjs.map +2 -2
- package/build-module/navigable-container/container.mjs +73 -111
- package/build-module/navigable-container/container.mjs.map +2 -2
- package/build-types/autocomplete/get-autocomplete-match.d.ts +10 -1
- package/build-types/autocomplete/get-autocomplete-match.d.ts.map +1 -1
- package/build-types/autocomplete/index.d.ts.map +1 -1
- package/build-types/base-control/stories/index.story.d.ts.map +1 -1
- package/build-types/button/stories/index.story.d.ts.map +1 -1
- package/build-types/checkbox-control/stories/index.story.d.ts.map +1 -1
- package/build-types/color-indicator/stories/index.story.d.ts.map +1 -1
- package/build-types/color-palette/stories/index.story.d.ts.map +1 -1
- package/build-types/color-picker/stories/index.story.d.ts.map +1 -1
- package/build-types/combobox-control/stories/index.story.d.ts.map +1 -1
- package/build-types/composite/stories/index.story.d.ts.map +1 -1
- package/build-types/custom-select-control/stories/index.story.d.ts.map +1 -1
- package/build-types/disabled/stories/index.story.d.ts.map +1 -1
- package/build-types/drop-zone/stories/index.story.d.ts.map +1 -1
- package/build-types/dropdown/stories/index.story.d.ts.map +1 -1
- package/build-types/external-link/index.d.ts.map +1 -1
- package/build-types/external-link/stories/index.story.d.ts.map +1 -1
- package/build-types/form-file-upload/stories/index.story.d.ts.map +1 -1
- package/build-types/form-toggle/stories/index.story.d.ts.map +1 -1
- package/build-types/form-token-field/stories/index.story.d.ts.map +1 -1
- package/build-types/gradient-picker/stories/index.story.d.ts.map +1 -1
- package/build-types/icon/stories/index.story.d.ts.map +1 -1
- package/build-types/keyboard-shortcuts/stories/index.story.d.ts.map +1 -1
- package/build-types/menu-group/stories/index.story.d.ts.map +1 -1
- package/build-types/menu-item/stories/index.story.d.ts.map +1 -1
- package/build-types/menu-items-choice/stories/index.story.d.ts.map +1 -1
- package/build-types/modal/stories/index.story.d.ts.map +1 -1
- package/build-types/navigable-container/container.d.ts +3 -8
- package/build-types/navigable-container/container.d.ts.map +1 -1
- package/build-types/navigable-container/types.d.ts +1 -5
- package/build-types/navigable-container/types.d.ts.map +1 -1
- package/build-types/navigation/stories/utils/more-examples.d.ts.map +1 -1
- package/build-types/navigator/stories/index.story.d.ts.map +1 -1
- package/build-types/notice/stories/index.story.d.ts.map +1 -1
- package/build-types/panel/stories/index.story.d.ts.map +1 -1
- package/build-types/popover/stories/index.story.d.ts.map +1 -1
- package/build-types/progress-bar/stories/index.story.d.ts.map +1 -1
- package/build-types/radio-control/stories/index.story.d.ts.map +1 -1
- package/build-types/range-control/stories/index.story.d.ts.map +1 -1
- package/build-types/resizable-box/stories/index.story.d.ts.map +1 -1
- package/build-types/sandbox/stories/index.story.d.ts.map +1 -1
- package/build-types/scroll-lock/stories/index.story.d.ts.map +1 -1
- package/build-types/search-control/stories/index.story.d.ts.map +1 -1
- package/build-types/select-control/stories/index.story.d.ts.map +1 -1
- package/build-types/shortcut/stories/index.story.d.ts.map +1 -1
- package/build-types/slot-fill/stories/index.story.d.ts.map +1 -1
- package/build-types/snackbar/stories/index.story.d.ts.map +1 -1
- package/build-types/spinner/stories/index.story.d.ts.map +1 -1
- package/build-types/text-control/stories/index.story.d.ts.map +1 -1
- package/build-types/text-highlight/stories/index.story.d.ts.map +1 -1
- package/build-types/textarea-control/stories/index.story.d.ts.map +1 -1
- package/build-types/toggle-control/stories/index.story.d.ts.map +1 -1
- package/build-types/tooltip/stories/index.story.d.ts.map +1 -1
- package/build-types/tree-select/stories/index.story.d.ts.map +1 -1
- package/build-types/visually-hidden/stories/index.story.d.ts.map +1 -1
- package/package.json +21 -21
- package/src/autocomplete/get-autocomplete-match.ts +25 -4
- package/src/autocomplete/index.tsx +69 -21
- package/src/autocomplete/test/get-autocomplete-match.ts +97 -75
- package/src/base-control/stories/index.story.tsx +1 -0
- package/src/button/stories/index.story.tsx +1 -0
- package/src/checkbox-control/stories/index.story.tsx +1 -0
- package/src/color-indicator/stories/index.story.tsx +1 -0
- package/src/color-palette/stories/index.story.tsx +1 -0
- package/src/color-picker/stories/index.story.tsx +1 -0
- package/src/combobox-control/stories/index.story.tsx +1 -0
- package/src/composite/stories/index.story.tsx +1 -0
- package/src/confirm-dialog/stories/index.story.tsx +1 -1
- package/src/custom-select-control/stories/index.story.tsx +1 -0
- package/src/disabled/stories/index.story.tsx +1 -0
- package/src/drop-zone/stories/index.story.tsx +1 -0
- package/src/dropdown/stories/index.story.tsx +1 -0
- package/src/external-link/index.tsx +1 -6
- package/src/external-link/stories/index.story.tsx +1 -0
- package/src/form-file-upload/stories/index.story.tsx +1 -0
- package/src/form-toggle/stories/index.story.tsx +1 -0
- package/src/form-token-field/stories/index.story.tsx +1 -0
- package/src/gradient-picker/stories/index.story.tsx +1 -0
- package/src/icon/stories/index.story.tsx +1 -0
- package/src/input-control/stories/index.story.tsx +1 -1
- package/src/item-group/stories/index.story.tsx +1 -1
- package/src/keyboard-shortcuts/stories/index.story.tsx +1 -0
- package/src/menu/styles.ts +1 -1
- package/src/menu-group/stories/index.story.tsx +1 -0
- package/src/menu-item/stories/index.story.tsx +1 -0
- package/src/menu-items-choice/stories/index.story.tsx +1 -0
- package/src/mobile/link-settings/index.native.js +1 -1
- package/src/modal/stories/index.story.tsx +1 -0
- package/src/navigable-container/container.tsx +120 -141
- package/src/navigable-container/test/navigable-menu.tsx +24 -0
- package/src/navigable-container/types.ts +1 -5
- package/src/navigation/stories/utils/more-examples.tsx +2 -1
- package/src/navigator/stories/index.story.tsx +1 -0
- package/src/notice/stories/index.story.tsx +1 -0
- package/src/number-control/stories/index.story.tsx +1 -1
- package/src/panel/stories/index.story.tsx +1 -0
- package/src/popover/stories/index.story.tsx +1 -0
- package/src/progress-bar/stories/index.story.tsx +1 -0
- package/src/radio-control/stories/index.story.tsx +1 -0
- package/src/range-control/stories/index.story.tsx +1 -0
- package/src/resizable-box/stories/index.story.tsx +1 -0
- package/src/sandbox/stories/index.story.tsx +1 -0
- package/src/scroll-lock/stories/index.story.tsx +1 -0
- package/src/search-control/stories/index.story.tsx +1 -0
- package/src/select-control/stories/index.story.tsx +1 -0
- package/src/shortcut/stories/index.story.tsx +1 -0
- package/src/slot-fill/stories/index.story.tsx +1 -0
- package/src/snackbar/stories/index.story.tsx +1 -0
- package/src/spinner/stories/index.story.tsx +1 -0
- package/src/text-control/stories/index.story.tsx +1 -0
- package/src/text-highlight/stories/index.story.tsx +1 -0
- package/src/textarea-control/stories/index.story.tsx +1 -0
- package/src/toggle-control/stories/index.story.tsx +1 -0
- package/src/toggle-group-control/stories/index.story.tsx +1 -1
- package/src/tooltip/stories/index.story.tsx +1 -0
- package/src/tree-grid/stories/index.story.tsx +1 -1
- package/src/tree-select/stories/index.story.tsx +1 -0
- package/src/truncate/stories/index.story.tsx +1 -1
- package/src/unit-control/stories/index.story.tsx +1 -1
- package/src/visually-hidden/stories/index.story.tsx +1 -0
- package/build/card/context.cjs +0 -36
- package/build/card/context.cjs.map +0 -7
- package/build-module/card/context.mjs +0 -10
- package/build-module/card/context.mjs.map +0 -7
- package/build-types/card/context.d.ts +0 -3
- package/build-types/card/context.d.ts.map +0 -1
- package/src/card/context.ts +0 -9
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 33.0.0-next.0 (2026-04-20)
|
|
6
|
+
|
|
7
|
+
### Enhancements
|
|
8
|
+
|
|
9
|
+
- `Menu`: Remove `cursor: not-allowed` and added pointer styles to menu ([#70412](https://github.com/WordPress/gutenberg/pull/70412))
|
|
10
|
+
|
|
11
|
+
### Breaking Changes
|
|
12
|
+
|
|
13
|
+
- `ExternalLink`: No longer adds `noreferrer` to the `rel` attribute. `noopener` is still applied. Consumers relying on the previous behavior should pass `rel="noopener noreferrer"` explicitly ([#26968](https://github.com/WordPress/gutenberg/pull/26968)).
|
|
14
|
+
|
|
15
|
+
### Internal
|
|
16
|
+
|
|
17
|
+
- `NavigableContainer`: Refactor from class component to function component with hooks ([#77171](https://github.com/WordPress/gutenberg/pull/77171)).
|
|
18
|
+
|
|
5
19
|
## 32.6.0 (2026-04-15)
|
|
6
20
|
|
|
7
21
|
### Documentation
|
|
@@ -17,6 +31,7 @@
|
|
|
17
31
|
- `Autocomplete`: Fix value comparison to avoid resetting block inserter in RTC ([#76980](https://github.com/WordPress/gutenberg/pull/76980)).
|
|
18
32
|
- `ValidatedRangeControl`: Fix `aria-label` rendered as `[object Object]` when `required` or `markWhenOptional` is set ([#77042](https://github.com/WordPress/gutenberg/pull/77042)).
|
|
19
33
|
- `Autocomplete`: Fix matching logic to prefer longest overlapping trigger ([#77018](https://github.com/WordPress/gutenberg/pull/77018)).
|
|
34
|
+
- `Autocomplete`: Skip stale triggers from completed mentions ([#77185](https://github.com/WordPress/gutenberg/pull/77185)).
|
|
20
35
|
|
|
21
36
|
### Enhancements
|
|
22
37
|
|
|
@@ -34,7 +34,13 @@ __export(get_autocomplete_match_exports, {
|
|
|
34
34
|
});
|
|
35
35
|
module.exports = __toCommonJS(get_autocomplete_match_exports);
|
|
36
36
|
var import_remove_accents = __toESM(require("remove-accents"));
|
|
37
|
-
function getAutocompleteMatch(textContent, completers,
|
|
37
|
+
function getAutocompleteMatch(textContent, completers, options) {
|
|
38
|
+
const {
|
|
39
|
+
matchCount,
|
|
40
|
+
isBackspacing,
|
|
41
|
+
getTextAfterSelection,
|
|
42
|
+
lastCompletion
|
|
43
|
+
} = options;
|
|
38
44
|
if (!textContent) {
|
|
39
45
|
return null;
|
|
40
46
|
}
|
|
@@ -66,7 +72,7 @@ function getAutocompleteMatch(textContent, completers, filteredOptionsLength, is
|
|
|
66
72
|
if (textWithoutTrigger.length > 50) {
|
|
67
73
|
return null;
|
|
68
74
|
}
|
|
69
|
-
const mismatch =
|
|
75
|
+
const mismatch = matchCount === 0;
|
|
70
76
|
const wordsFromTrigger = textWithoutTrigger.split(/\s/);
|
|
71
77
|
const hasOneTriggerWord = wordsFromTrigger.length === 1;
|
|
72
78
|
const matchingWhileBackspacing = isBackspacing && wordsFromTrigger.length <= 3;
|
|
@@ -79,6 +85,9 @@ function getAutocompleteMatch(textContent, completers, filteredOptionsLength, is
|
|
|
79
85
|
if (/^\s/.test(textWithoutTrigger) || /\s\s+$/.test(textWithoutTrigger)) {
|
|
80
86
|
return null;
|
|
81
87
|
}
|
|
88
|
+
if (lastCompletion && lastCompletion.name === completer.name && textWithoutTrigger.trimEnd() === lastCompletion.value) {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
82
91
|
return {
|
|
83
92
|
completer,
|
|
84
93
|
filterValue: (0, import_remove_accents.default)(textWithoutTrigger)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/autocomplete/get-autocomplete-match.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * External dependencies\n */\nimport removeAccents from 'remove-accents';\n\n/**\n * Internal dependencies\n */\n\nexport function getAutocompleteMatch(textContent, completers,
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,4BAA0B;AAMnB,SAAS,qBAAqB,aAAa,YAAY,
|
|
4
|
+
"sourcesContent": ["/**\n * External dependencies\n */\nimport removeAccents from 'remove-accents';\n\n/**\n * Internal dependencies\n */\n\nexport function getAutocompleteMatch(textContent, completers, options) {\n const {\n matchCount,\n isBackspacing,\n getTextAfterSelection,\n lastCompletion\n } = options;\n if (!textContent) {\n return null;\n }\n\n // Find the completer whose trigger prefix ends closest to the cursor\n // (rightmost end position). Comparing end positions instead of start\n // positions correctly resolves overlapping prefixes like \"@\" and \"@@\".\n let completer = null;\n let triggerIndex = -1;\n let matchedEndIndex = -1;\n let matchedPrefixLength = 0;\n for (const currentCompleter of completers) {\n const currentIndex = textContent.lastIndexOf(currentCompleter.triggerPrefix);\n if (currentIndex < 0) {\n continue;\n }\n const currentEndIndex = currentIndex + currentCompleter.triggerPrefix.length;\n if (currentEndIndex > matchedEndIndex || currentEndIndex === matchedEndIndex && currentCompleter.triggerPrefix.length > matchedPrefixLength) {\n completer = currentCompleter;\n triggerIndex = currentIndex;\n matchedEndIndex = currentEndIndex;\n matchedPrefixLength = currentCompleter.triggerPrefix.length;\n }\n }\n if (!completer) {\n return null;\n }\n const {\n allowContext,\n triggerPrefix\n } = completer;\n const textWithoutTrigger = textContent.slice(triggerIndex + triggerPrefix.length);\n\n // Prevent matching with an extremely long string, which causes\n // the editor to slow-down significantly. This could happen, for\n // example, if `matchingWhileBackspacing` is true and one of the\n // \"words\" ends up being too long. Returning null here intentionally\n // resets the autocompleter state in the caller.\n if (textWithoutTrigger.length > 50) {\n return null;\n }\n const mismatch = matchCount === 0;\n const wordsFromTrigger = textWithoutTrigger.split(/\\s/);\n\n // Allow matching when typing a trigger + the match string or when\n // clicking in an existing trigger word on the page.\n // E.g. \"Some text @a\" \u2014 \"@a\" is detected as a trigger word.\n const hasOneTriggerWord = wordsFromTrigger.length === 1;\n\n // Allow matching when backspacing near a trigger word (up to 3\n // words from the trigger character). This lets us recover from a\n // mismatch when backspacing while still imposing sane limits.\n // E.g. \"Some text @marcelo sekkkk\" \u2014 backspacing \"kkkk\" re-shows\n // the popup once the text matches again.\n const matchingWhileBackspacing = isBackspacing && wordsFromTrigger.length <= 3;\n if (mismatch && !(matchingWhileBackspacing || hasOneTriggerWord)) {\n return null;\n }\n if (allowContext && !allowContext(textContent.slice(0, triggerIndex), getTextAfterSelection())) {\n return null;\n }\n if (/^\\s/.test(textWithoutTrigger) || /\\s\\s+$/.test(textWithoutTrigger)) {\n return null;\n }\n\n // After a completion whose value starts with the trigger prefix\n // (e.g. @username), the trigger remains in the text and would\n // re-activate the autocompleter. Suppress the match when the\n // filter value still corresponds to the recently completed text.\n if (lastCompletion && lastCompletion.name === completer.name && textWithoutTrigger.trimEnd() === lastCompletion.value) {\n return null;\n }\n return {\n completer,\n filterValue: removeAccents(textWithoutTrigger)\n };\n}"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,4BAA0B;AAMnB,SAAS,qBAAqB,aAAa,YAAY,SAAS;AACrE,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AACJ,MAAI,CAAC,aAAa;AAChB,WAAO;AAAA,EACT;AAKA,MAAI,YAAY;AAChB,MAAI,eAAe;AACnB,MAAI,kBAAkB;AACtB,MAAI,sBAAsB;AAC1B,aAAW,oBAAoB,YAAY;AACzC,UAAM,eAAe,YAAY,YAAY,iBAAiB,aAAa;AAC3E,QAAI,eAAe,GAAG;AACpB;AAAA,IACF;AACA,UAAM,kBAAkB,eAAe,iBAAiB,cAAc;AACtE,QAAI,kBAAkB,mBAAmB,oBAAoB,mBAAmB,iBAAiB,cAAc,SAAS,qBAAqB;AAC3I,kBAAY;AACZ,qBAAe;AACf,wBAAkB;AAClB,4BAAsB,iBAAiB,cAAc;AAAA,IACvD;AAAA,EACF;AACA,MAAI,CAAC,WAAW;AACd,WAAO;AAAA,EACT;AACA,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,EACF,IAAI;AACJ,QAAM,qBAAqB,YAAY,MAAM,eAAe,cAAc,MAAM;AAOhF,MAAI,mBAAmB,SAAS,IAAI;AAClC,WAAO;AAAA,EACT;AACA,QAAM,WAAW,eAAe;AAChC,QAAM,mBAAmB,mBAAmB,MAAM,IAAI;AAKtD,QAAM,oBAAoB,iBAAiB,WAAW;AAOtD,QAAM,2BAA2B,iBAAiB,iBAAiB,UAAU;AAC7E,MAAI,YAAY,EAAE,4BAA4B,oBAAoB;AAChE,WAAO;AAAA,EACT;AACA,MAAI,gBAAgB,CAAC,aAAa,YAAY,MAAM,GAAG,YAAY,GAAG,sBAAsB,CAAC,GAAG;AAC9F,WAAO;AAAA,EACT;AACA,MAAI,MAAM,KAAK,kBAAkB,KAAK,SAAS,KAAK,kBAAkB,GAAG;AACvE,WAAO;AAAA,EACT;AAMA,MAAI,kBAAkB,eAAe,SAAS,UAAU,QAAQ,mBAAmB,QAAQ,MAAM,eAAe,OAAO;AACrH,WAAO;AAAA,EACT;AACA,SAAO;AAAA,IACL;AAAA,IACA,iBAAa,sBAAAA,SAAc,kBAAkB;AAAA,EAC/C;AACF;",
|
|
6
6
|
"names": ["removeAccents"]
|
|
7
7
|
}
|
|
@@ -102,9 +102,11 @@ function useAutocomplete({
|
|
|
102
102
|
autocompleter
|
|
103
103
|
} = state;
|
|
104
104
|
const backspacingRef = (0, import_element.useRef)(false);
|
|
105
|
+
const prevRecordTextRef = (0, import_element.useRef)("");
|
|
106
|
+
const lastCompletionRef = (0, import_element.useRef)(null);
|
|
105
107
|
function insertCompletion(replacement) {
|
|
106
108
|
if (autocompleter === null) {
|
|
107
|
-
return;
|
|
109
|
+
return "";
|
|
108
110
|
}
|
|
109
111
|
const end = record.start;
|
|
110
112
|
const start = end - autocompleter.triggerPrefix.length - filterValue.length;
|
|
@@ -112,21 +114,37 @@ function useAutocomplete({
|
|
|
112
114
|
html: (0, import_element.renderToString)(replacement)
|
|
113
115
|
});
|
|
114
116
|
onChange((0, import_rich_text.insert)(record, toInsert, start, end));
|
|
117
|
+
return (0, import_rich_text.getTextContent)(toInsert);
|
|
115
118
|
}
|
|
116
119
|
function select(option) {
|
|
120
|
+
if (option.isDisabled || !autocompleter) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
117
123
|
const {
|
|
118
124
|
getOptionCompletion
|
|
119
|
-
} = autocompleter
|
|
120
|
-
if (
|
|
125
|
+
} = autocompleter;
|
|
126
|
+
if (!getOptionCompletion) {
|
|
127
|
+
dispatch({
|
|
128
|
+
type: "RESET"
|
|
129
|
+
});
|
|
130
|
+
contentRef.current?.focus();
|
|
121
131
|
return;
|
|
122
132
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
133
|
+
const completionObject = getCompletionObject(getOptionCompletion(option.value, filterValue));
|
|
134
|
+
if ("replace" === completionObject.action) {
|
|
135
|
+
onReplace([completionObject.value]);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
if ("insert-at-caret" === completionObject.action) {
|
|
139
|
+
const completionText = insertCompletion(completionObject.value);
|
|
140
|
+
if (completionText.startsWith(autocompleter.triggerPrefix)) {
|
|
141
|
+
const afterPrefix = completionText.slice(autocompleter.triggerPrefix.length);
|
|
142
|
+
if (afterPrefix) {
|
|
143
|
+
lastCompletionRef.current = {
|
|
144
|
+
name: autocompleter.name,
|
|
145
|
+
value: afterPrefix
|
|
146
|
+
};
|
|
147
|
+
}
|
|
130
148
|
}
|
|
131
149
|
}
|
|
132
150
|
dispatch({
|
|
@@ -192,10 +210,17 @@ function useAutocomplete({
|
|
|
192
210
|
return "";
|
|
193
211
|
}, [record]);
|
|
194
212
|
(0, import_element.useEffect)(() => {
|
|
213
|
+
const isTextChange = record.text !== prevRecordTextRef.current;
|
|
214
|
+
prevRecordTextRef.current = record.text;
|
|
195
215
|
function getTextAfterSelection() {
|
|
196
216
|
return textContent ? (0, import_rich_text.getTextContent)((0, import_rich_text.slice)(record, void 0, (0, import_rich_text.getTextContent)(record).length)) : "";
|
|
197
217
|
}
|
|
198
|
-
const match = (0, import_get_autocomplete_match.getAutocompleteMatch)(textContent, completers,
|
|
218
|
+
const match = (0, import_get_autocomplete_match.getAutocompleteMatch)(textContent, completers, {
|
|
219
|
+
matchCount: filteredOptions.length,
|
|
220
|
+
isBackspacing: backspacingRef.current,
|
|
221
|
+
getTextAfterSelection,
|
|
222
|
+
lastCompletion: lastCompletionRef.current
|
|
223
|
+
});
|
|
199
224
|
if (!match) {
|
|
200
225
|
if (autocompleter) {
|
|
201
226
|
dispatch({
|
|
@@ -208,6 +233,12 @@ function useAutocomplete({
|
|
|
208
233
|
completer,
|
|
209
234
|
filterValue: query
|
|
210
235
|
} = match;
|
|
236
|
+
if (!autocompleter && !isTextChange) {
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
if (lastCompletionRef.current && lastCompletionRef.current.name === completer.name) {
|
|
240
|
+
lastCompletionRef.current = null;
|
|
241
|
+
}
|
|
211
242
|
dispatch({
|
|
212
243
|
type: "MATCH",
|
|
213
244
|
completer,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/autocomplete/index.tsx"],
|
|
4
|
-
"sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { renderToString, useEffect, useMemo, useReducer, useRef } from '@wordpress/element';\nimport { useInstanceId, useMergeRefs, useRefEffect } from '@wordpress/compose';\nimport { create, slice, insert, isCollapsed, getTextContent } from '@wordpress/rich-text';\nimport { speak } from '@wordpress/a11y';\nimport { isAppleOS } from '@wordpress/keycodes';\n\n/**\n * Internal dependencies\n */\nimport { AutocompleterUI } from './autocompleter-ui';\nimport { getAutocompleteMatch } from './get-autocomplete-match';\nimport { withIgnoreIMEEvents } from '../utils/with-ignore-ime-events';\nimport getNodeText from '../utils/get-node-text';\nimport { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from \"react/jsx-runtime\";\nconst EMPTY_FILTERED_OPTIONS = [];\n\n// Used for generating the instance ID\nconst AUTOCOMPLETE_HOOK_REFERENCE = {};\nfunction getCompletionObject(completion) {\n if (completion !== null && typeof completion === 'object' && 'action' in completion && completion.action !== undefined && 'value' in completion && completion.value !== undefined) {\n return completion;\n }\n return {\n action: 'insert-at-caret',\n value: completion\n };\n}\nconst initialState = {\n selectedIndex: 0,\n filteredOptions: EMPTY_FILTERED_OPTIONS,\n filterValue: '',\n autocompleter: null\n};\nfunction autocompleteReducer(state, action) {\n switch (action.type) {\n case 'RESET':\n return initialState;\n case 'SELECT':\n return {\n ...state,\n selectedIndex: action.index\n };\n case 'OPTIONS':\n return {\n ...state,\n filteredOptions: action.options,\n selectedIndex: action.options.length === state.filteredOptions.length ? state.selectedIndex : 0\n };\n case 'MATCH':\n return {\n ...state,\n autocompleter: action.completer,\n filterValue: action.query\n };\n }\n}\nexport function useAutocomplete({\n record,\n onChange,\n onReplace,\n completers,\n contentRef\n}) {\n const instanceId = useInstanceId(AUTOCOMPLETE_HOOK_REFERENCE);\n const [state, dispatch] = useReducer(autocompleteReducer, initialState);\n const {\n selectedIndex,\n filteredOptions,\n filterValue,\n autocompleter\n } = state;\n const backspacingRef = useRef(false);\n function insertCompletion(replacement) {\n if (autocompleter === null) {\n return;\n }\n const end = record.start;\n const start = end - autocompleter.triggerPrefix.length - filterValue.length;\n const toInsert = create({\n html: renderToString(replacement)\n });\n onChange(insert(record, toInsert, start, end));\n }\n function select(option) {\n const {\n getOptionCompletion\n } = autocompleter || {};\n if (option.isDisabled) {\n return;\n }\n if (getOptionCompletion) {\n const completionObject = getCompletionObject(getOptionCompletion(option.value, filterValue));\n if ('replace' === completionObject.action) {\n onReplace([completionObject.value]);\n // When replacing, the component will unmount, so don't reset\n // state (below) on an unmounted component.\n return;\n } else if ('insert-at-caret' === completionObject.action) {\n insertCompletion(completionObject.value);\n }\n }\n\n // Reset autocomplete state after insertion rather than before\n // so insertion events don't cause the completion menu to redisplay.\n dispatch({\n type: 'RESET'\n });\n\n // Make sure that the content remains focused after making a selection\n // and that the text cursor position is not lost.\n contentRef.current?.focus();\n }\n function onChangeOptions(options) {\n dispatch({\n type: 'OPTIONS',\n options\n });\n }\n function handleKeyDown(event) {\n backspacingRef.current = event.key === 'Backspace';\n if (!autocompleter) {\n return;\n }\n if (filteredOptions.length === 0) {\n return;\n }\n if (event.defaultPrevented) {\n return;\n }\n switch (event.key) {\n case 'ArrowUp':\n case 'ArrowDown':\n {\n const offset = event.key === 'ArrowUp' ? -1 : 1;\n const newIndex = (selectedIndex + offset + filteredOptions.length) % filteredOptions.length;\n dispatch({\n type: 'SELECT',\n index: newIndex\n });\n // See the related PR as to why this is necessary: https://github.com/WordPress/gutenberg/pull/54902.\n if (isAppleOS()) {\n speak(getNodeText(filteredOptions[newIndex].label), 'assertive');\n }\n break;\n }\n case 'Escape':\n dispatch({\n type: 'RESET'\n });\n event.preventDefault();\n break;\n case 'Enter':\n select(filteredOptions[selectedIndex]);\n break;\n case 'ArrowLeft':\n case 'ArrowRight':\n dispatch({\n type: 'RESET'\n });\n return;\n default:\n return;\n }\n\n // Any handled key should prevent original behavior. This relies on\n // the early return in the default case.\n event.preventDefault();\n }\n\n // textContent is a primitive (string), memoizing is not strictly necessary\n // but this is a preemptive performance improvement, since the autocompleter\n // is a potential bottleneck for the editor type metric.\n const textContent = useMemo(() => {\n if (isCollapsed(record)) {\n return getTextContent(slice(record, 0));\n }\n return '';\n }, [record]);\n useEffect(() => {\n function getTextAfterSelection() {\n return textContent ? getTextContent(slice(record, undefined, getTextContent(record).length)) : '';\n }\n const match = getAutocompleteMatch(textContent, completers, filteredOptions.length, backspacingRef.current, getTextAfterSelection);\n if (!match) {\n if (autocompleter) {\n dispatch({\n type: 'RESET'\n });\n }\n return;\n }\n const {\n completer,\n filterValue: query\n } = match;\n dispatch({\n type: 'MATCH',\n completer,\n query\n });\n // We want to avoid introducing unexpected side effects.\n // See https://github.com/WordPress/gutenberg/pull/41820\n }, [textContent]);\n const {\n key: selectedKey = ''\n } = filteredOptions[selectedIndex] || {};\n const {\n className\n } = autocompleter || {};\n const isExpanded = !!autocompleter && filteredOptions.length > 0;\n const listBoxId = isExpanded ? `components-autocomplete-listbox-${instanceId}` : undefined;\n const activeId = isExpanded ? `components-autocomplete-item-${instanceId}-${selectedKey}` : null;\n const hasSelection = record.start !== undefined;\n const showPopover = !!textContent && hasSelection && !!autocompleter;\n return {\n listBoxId,\n activeId,\n onKeyDown: withIgnoreIMEEvents(handleKeyDown),\n popover: showPopover && /*#__PURE__*/_jsx(AutocompleterUI, {\n autocompleter: autocompleter,\n className: className,\n filterValue: filterValue,\n instanceId: instanceId,\n listBoxId: listBoxId,\n selectedIndex: selectedIndex,\n onChangeOptions: onChangeOptions,\n onSelect: select,\n contentRef: contentRef,\n reset: () => dispatch({\n type: 'RESET'\n })\n }, autocompleter.name + autocompleter.triggerPrefix)\n };\n}\n\n/**\n * Checks whether two records represent the same user-visible state\n * (same text content and cursor position).\n */\nfunction recordValuesMatch(a, b) {\n return a.text === b.text && a.start === b.start && a.end === b.end;\n}\n\n/**\n * Tracks the last record whose value differed from the current one.\n * Used to determine whether the user has actually typed something\n */\nexport function useLastDifferentValue(value) {\n const history = useRef([]);\n const lastEntry = history.current[history.current.length - 1];\n\n // Only add to history if the value is meaningfully different from\n // the most recent entry (analogous to Set.add being a no-op for\n // duplicate references in the original implementation).\n if (!lastEntry || !recordValuesMatch(value, lastEntry)) {\n history.current.push(value);\n }\n\n // Keep the history size to 2.\n if (history.current.length > 2) {\n history.current.shift();\n }\n return history.current[0];\n}\nexport function useAutocompleteProps(options) {\n const ref = useRef(null);\n const onKeyDownRef = useRef(undefined);\n const {\n record\n } = options;\n const previousRecord = useLastDifferentValue(record);\n const {\n popover,\n listBoxId,\n activeId,\n onKeyDown\n } = useAutocomplete({\n ...options,\n contentRef: ref\n });\n onKeyDownRef.current = onKeyDown;\n const mergedRefs = useMergeRefs([ref, useRefEffect(element => {\n function _onKeyDown(event) {\n onKeyDownRef.current?.(event);\n }\n element.addEventListener('keydown', _onKeyDown);\n return () => {\n element.removeEventListener('keydown', _onKeyDown);\n };\n }, [])]);\n\n // We only want to show the popover if the user has typed something.\n const didUserInput = record.text !== previousRecord?.text;\n if (!didUserInput) {\n return {\n ref: mergedRefs\n };\n }\n return {\n ref: mergedRefs,\n children: popover,\n 'aria-autocomplete': listBoxId ? 'list' : undefined,\n 'aria-owns': listBoxId,\n 'aria-activedescendant': activeId\n };\n}\nexport default function Autocomplete({\n children,\n isSelected,\n ...options\n}) {\n const {\n popover,\n ...props\n } = useAutocomplete(options);\n return /*#__PURE__*/_jsxs(_Fragment, {\n children: [children(props), isSelected && popover]\n });\n}"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,qBAAuE;AACvE,qBAA0D;AAC1D,uBAAmE;AACnE,kBAAsB;AACtB,sBAA0B;AAK1B,8BAAgC;AAChC,oCAAqC;AACrC,oCAAoC;AACpC,2BAAwB;AACxB,yBAAkE;AAClE,IAAM,yBAAyB,CAAC;AAGhC,IAAM,8BAA8B,CAAC;AACrC,SAAS,oBAAoB,YAAY;AACvC,MAAI,eAAe,QAAQ,OAAO,eAAe,YAAY,YAAY,cAAc,WAAW,WAAW,UAAa,WAAW,cAAc,WAAW,UAAU,QAAW;AACjL,WAAO;AAAA,EACT;AACA,SAAO;AAAA,IACL,QAAQ;AAAA,IACR,OAAO;AAAA,EACT;AACF;AACA,IAAM,eAAe;AAAA,EACnB,eAAe;AAAA,EACf,iBAAiB;AAAA,EACjB,aAAa;AAAA,EACb,eAAe;AACjB;AACA,SAAS,oBAAoB,OAAO,QAAQ;AAC1C,UAAQ,OAAO,MAAM;AAAA,IACnB,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,QACL,GAAG;AAAA,QACH,eAAe,OAAO;AAAA,MACxB;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,GAAG;AAAA,QACH,iBAAiB,OAAO;AAAA,QACxB,eAAe,OAAO,QAAQ,WAAW,MAAM,gBAAgB,SAAS,MAAM,gBAAgB;AAAA,MAChG;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,GAAG;AAAA,QACH,eAAe,OAAO;AAAA,QACtB,aAAa,OAAO;AAAA,MACtB;AAAA,EACJ;AACF;AACO,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAG;AACD,QAAM,iBAAa,8BAAc,2BAA2B;AAC5D,QAAM,CAAC,OAAO,QAAQ,QAAI,2BAAW,qBAAqB,YAAY;AACtE,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AACJ,QAAM,qBAAiB,uBAAO,KAAK;AACnC,WAAS,iBAAiB,aAAa;AACrC,QAAI,kBAAkB,MAAM;AAC1B;AAAA,
|
|
4
|
+
"sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { renderToString, useEffect, useMemo, useReducer, useRef } from '@wordpress/element';\nimport { useInstanceId, useMergeRefs, useRefEffect } from '@wordpress/compose';\nimport { create, slice, insert, isCollapsed, getTextContent } from '@wordpress/rich-text';\nimport { speak } from '@wordpress/a11y';\nimport { isAppleOS } from '@wordpress/keycodes';\n\n/**\n * Internal dependencies\n */\nimport { AutocompleterUI } from './autocompleter-ui';\nimport { getAutocompleteMatch } from './get-autocomplete-match';\nimport { withIgnoreIMEEvents } from '../utils/with-ignore-ime-events';\nimport getNodeText from '../utils/get-node-text';\nimport { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from \"react/jsx-runtime\";\nconst EMPTY_FILTERED_OPTIONS = [];\n\n// Used for generating the instance ID\nconst AUTOCOMPLETE_HOOK_REFERENCE = {};\nfunction getCompletionObject(completion) {\n if (completion !== null && typeof completion === 'object' && 'action' in completion && completion.action !== undefined && 'value' in completion && completion.value !== undefined) {\n return completion;\n }\n return {\n action: 'insert-at-caret',\n value: completion\n };\n}\nconst initialState = {\n selectedIndex: 0,\n filteredOptions: EMPTY_FILTERED_OPTIONS,\n filterValue: '',\n autocompleter: null\n};\nfunction autocompleteReducer(state, action) {\n switch (action.type) {\n case 'RESET':\n return initialState;\n case 'SELECT':\n return {\n ...state,\n selectedIndex: action.index\n };\n case 'OPTIONS':\n return {\n ...state,\n filteredOptions: action.options,\n selectedIndex: action.options.length === state.filteredOptions.length ? state.selectedIndex : 0\n };\n case 'MATCH':\n return {\n ...state,\n autocompleter: action.completer,\n filterValue: action.query\n };\n }\n}\nexport function useAutocomplete({\n record,\n onChange,\n onReplace,\n completers,\n contentRef\n}) {\n const instanceId = useInstanceId(AUTOCOMPLETE_HOOK_REFERENCE);\n const [state, dispatch] = useReducer(autocompleteReducer, initialState);\n const {\n selectedIndex,\n filteredOptions,\n filterValue,\n autocompleter\n } = state;\n const backspacingRef = useRef(false);\n const prevRecordTextRef = useRef('');\n const lastCompletionRef = useRef(null);\n function insertCompletion(replacement) {\n if (autocompleter === null) {\n return '';\n }\n const end = record.start;\n const start = end - autocompleter.triggerPrefix.length - filterValue.length;\n const toInsert = create({\n html: renderToString(replacement)\n });\n onChange(insert(record, toInsert, start, end));\n return getTextContent(toInsert);\n }\n function select(option) {\n if (option.isDisabled || !autocompleter) {\n return;\n }\n const {\n getOptionCompletion\n } = autocompleter;\n if (!getOptionCompletion) {\n dispatch({\n type: 'RESET'\n });\n contentRef.current?.focus();\n return;\n }\n const completionObject = getCompletionObject(getOptionCompletion(option.value, filterValue));\n if ('replace' === completionObject.action) {\n onReplace([completionObject.value]);\n // When replacing, the component will unmount, so don't reset\n // state (below) on an unmounted component.\n return;\n }\n if ('insert-at-caret' === completionObject.action) {\n const completionText = insertCompletion(completionObject.value);\n // When the completion value starts with the trigger prefix\n // (e.g. @username), the trigger stays in the text and would\n // re-activate the autocompleter. Store the completed text so\n // the effect can suppress the stale re-match.\n if (completionText.startsWith(autocompleter.triggerPrefix)) {\n const afterPrefix = completionText.slice(autocompleter.triggerPrefix.length);\n if (afterPrefix) {\n lastCompletionRef.current = {\n name: autocompleter.name,\n value: afterPrefix\n };\n }\n }\n }\n\n // Reset autocomplete state after insertion rather than before\n // so insertion events don't cause the completion menu to redisplay.\n dispatch({\n type: 'RESET'\n });\n\n // Make sure that the content remains focused after making a selection\n // and that the text cursor position is not lost.\n contentRef.current?.focus();\n }\n function onChangeOptions(options) {\n dispatch({\n type: 'OPTIONS',\n options\n });\n }\n function handleKeyDown(event) {\n backspacingRef.current = event.key === 'Backspace';\n if (!autocompleter) {\n return;\n }\n if (filteredOptions.length === 0) {\n return;\n }\n if (event.defaultPrevented) {\n return;\n }\n switch (event.key) {\n case 'ArrowUp':\n case 'ArrowDown':\n {\n const offset = event.key === 'ArrowUp' ? -1 : 1;\n const newIndex = (selectedIndex + offset + filteredOptions.length) % filteredOptions.length;\n dispatch({\n type: 'SELECT',\n index: newIndex\n });\n // See the related PR as to why this is necessary: https://github.com/WordPress/gutenberg/pull/54902.\n if (isAppleOS()) {\n speak(getNodeText(filteredOptions[newIndex].label), 'assertive');\n }\n break;\n }\n case 'Escape':\n dispatch({\n type: 'RESET'\n });\n event.preventDefault();\n break;\n case 'Enter':\n select(filteredOptions[selectedIndex]);\n break;\n case 'ArrowLeft':\n case 'ArrowRight':\n dispatch({\n type: 'RESET'\n });\n return;\n default:\n return;\n }\n\n // Any handled key should prevent original behavior. This relies on\n // the early return in the default case.\n event.preventDefault();\n }\n\n // textContent is a primitive (string), memoizing is not strictly necessary\n // but this is a preemptive performance improvement, since the autocompleter\n // is a potential bottleneck for the editor type metric.\n const textContent = useMemo(() => {\n if (isCollapsed(record)) {\n return getTextContent(slice(record, 0));\n }\n return '';\n }, [record]);\n useEffect(() => {\n const isTextChange = record.text !== prevRecordTextRef.current;\n prevRecordTextRef.current = record.text;\n function getTextAfterSelection() {\n return textContent ? getTextContent(slice(record, undefined, getTextContent(record).length)) : '';\n }\n const match = getAutocompleteMatch(textContent, completers, {\n matchCount: filteredOptions.length,\n isBackspacing: backspacingRef.current,\n getTextAfterSelection,\n lastCompletion: lastCompletionRef.current\n });\n if (!match) {\n if (autocompleter) {\n dispatch({\n type: 'RESET'\n });\n }\n return;\n }\n const {\n completer,\n filterValue: query\n } = match;\n\n // Don't re-activate a dismissed autocompleter on cursor-only\n // movement. `textContent` (text before cursor) changes with the\n // caret, so the effect re-runs, but `record.text` does not.\n // Complements the render-time `didUserInput` gate in\n // `useAutocompleteProps` for callers using this hook directly.\n if (!autocompleter && !isTextChange) {\n return;\n }\n\n // Clear stale completion ref when the user types a new trigger\n // for the same completer (the previous completion is no longer\n // relevant). Must be after the cursor-only check so that mere\n // cursor movement doesn't discard the suppression state.\n if (lastCompletionRef.current && lastCompletionRef.current.name === completer.name) {\n lastCompletionRef.current = null;\n }\n dispatch({\n type: 'MATCH',\n completer,\n query\n });\n // We want to avoid introducing unexpected side effects.\n // See https://github.com/WordPress/gutenberg/pull/41820\n }, [textContent]);\n const {\n key: selectedKey = ''\n } = filteredOptions[selectedIndex] || {};\n const {\n className\n } = autocompleter || {};\n const isExpanded = !!autocompleter && filteredOptions.length > 0;\n const listBoxId = isExpanded ? `components-autocomplete-listbox-${instanceId}` : undefined;\n const activeId = isExpanded ? `components-autocomplete-item-${instanceId}-${selectedKey}` : null;\n const hasSelection = record.start !== undefined;\n const showPopover = !!textContent && hasSelection && !!autocompleter;\n return {\n listBoxId,\n activeId,\n onKeyDown: withIgnoreIMEEvents(handleKeyDown),\n popover: showPopover && /*#__PURE__*/_jsx(AutocompleterUI, {\n autocompleter: autocompleter,\n className: className,\n filterValue: filterValue,\n instanceId: instanceId,\n listBoxId: listBoxId,\n selectedIndex: selectedIndex,\n onChangeOptions: onChangeOptions,\n onSelect: select,\n contentRef: contentRef,\n reset: () => dispatch({\n type: 'RESET'\n })\n }, autocompleter.name + autocompleter.triggerPrefix)\n };\n}\n\n/**\n * Checks whether two records represent the same user-visible state\n * (same text content and cursor position).\n */\nfunction recordValuesMatch(a, b) {\n return a.text === b.text && a.start === b.start && a.end === b.end;\n}\n\n/**\n * Tracks the last record whose value differed from the current one.\n * Used to determine whether the user has actually typed something\n */\nexport function useLastDifferentValue(value) {\n const history = useRef([]);\n const lastEntry = history.current[history.current.length - 1];\n\n // Only add to history if the value is meaningfully different from\n // the most recent entry (analogous to Set.add being a no-op for\n // duplicate references in the original implementation).\n if (!lastEntry || !recordValuesMatch(value, lastEntry)) {\n history.current.push(value);\n }\n\n // Keep the history size to 2.\n if (history.current.length > 2) {\n history.current.shift();\n }\n return history.current[0];\n}\nexport function useAutocompleteProps(options) {\n const ref = useRef(null);\n const onKeyDownRef = useRef(undefined);\n const {\n record\n } = options;\n const previousRecord = useLastDifferentValue(record);\n const {\n popover,\n listBoxId,\n activeId,\n onKeyDown\n } = useAutocomplete({\n ...options,\n contentRef: ref\n });\n onKeyDownRef.current = onKeyDown;\n const mergedRefs = useMergeRefs([ref, useRefEffect(element => {\n function _onKeyDown(event) {\n onKeyDownRef.current?.(event);\n }\n element.addEventListener('keydown', _onKeyDown);\n return () => {\n element.removeEventListener('keydown', _onKeyDown);\n };\n }, [])]);\n\n // We only want to show the popover if the user has typed something.\n const didUserInput = record.text !== previousRecord?.text;\n if (!didUserInput) {\n return {\n ref: mergedRefs\n };\n }\n return {\n ref: mergedRefs,\n children: popover,\n 'aria-autocomplete': listBoxId ? 'list' : undefined,\n 'aria-owns': listBoxId,\n 'aria-activedescendant': activeId\n };\n}\nexport default function Autocomplete({\n children,\n isSelected,\n ...options\n}) {\n const {\n popover,\n ...props\n } = useAutocomplete(options);\n return /*#__PURE__*/_jsxs(_Fragment, {\n children: [children(props), isSelected && popover]\n });\n}"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["getNodeText", "_jsx", "_jsxs", "_Fragment"]
|
|
7
7
|
}
|
|
@@ -46,7 +46,7 @@ function UnforwardedExternalLink(props, ref) {
|
|
|
46
46
|
rel = "",
|
|
47
47
|
...additionalProps
|
|
48
48
|
} = props;
|
|
49
|
-
const optimizedRel = [...new Set([...rel.split(" "), "external", "
|
|
49
|
+
const optimizedRel = [...new Set([...rel.split(" "), "external", "noopener"].filter(Boolean))].join(" ");
|
|
50
50
|
const classes = (0, import_clsx.default)("components-external-link", className);
|
|
51
51
|
const isInternalAnchor = !!href?.startsWith("#");
|
|
52
52
|
const onClickHandler = (event) => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/external-link/index.tsx"],
|
|
4
|
-
"sourcesContent": ["/**\n * External dependencies\n */\nimport clsx from 'clsx';\n/**\n * WordPress dependencies\n */\nimport { __, isRTL } from '@wordpress/i18n';\nimport { forwardRef } from '@wordpress/element';\n\n/**\n * Internal dependencies\n */\nimport { jsx as _jsx, jsxs as _jsxs } from \"react/jsx-runtime\";\nfunction UnforwardedExternalLink(props, ref) {\n const {\n href,\n children,\n className,\n rel = '',\n ...additionalProps\n } = props;\n const optimizedRel = [...new Set([...rel.split(' '), 'external', '
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,kBAAiB;AAIjB,kBAA0B;AAC1B,qBAA2B;AAK3B,yBAA2C;AAC3C,SAAS,wBAAwB,OAAO,KAAK;AAC3C,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA,MAAM;AAAA,IACN,GAAG;AAAA,EACL,IAAI;AACJ,QAAM,eAAe,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,IAAI,MAAM,GAAG,GAAG,YAAY,
|
|
4
|
+
"sourcesContent": ["/**\n * External dependencies\n */\nimport clsx from 'clsx';\n/**\n * WordPress dependencies\n */\nimport { __, isRTL } from '@wordpress/i18n';\nimport { forwardRef } from '@wordpress/element';\n\n/**\n * Internal dependencies\n */\nimport { jsx as _jsx, jsxs as _jsxs } from \"react/jsx-runtime\";\nfunction UnforwardedExternalLink(props, ref) {\n const {\n href,\n children,\n className,\n rel = '',\n ...additionalProps\n } = props;\n const optimizedRel = [...new Set([...rel.split(' '), 'external', 'noopener'].filter(Boolean))].join(' ');\n const classes = clsx('components-external-link', className);\n /* Anchor links are perceived as external links.\n This constant helps check for on page anchor links,\n to prevent them from being opened in the editor. */\n const isInternalAnchor = !!href?.startsWith('#');\n const onClickHandler = event => {\n if (isInternalAnchor) {\n event.preventDefault();\n }\n if (props.onClick) {\n props.onClick(event);\n }\n };\n return /*#__PURE__*/ /* eslint-disable react/jsx-no-target-blank */_jsxs(\"a\", {\n ...additionalProps,\n className: classes,\n href: href,\n onClick: onClickHandler,\n target: \"_blank\",\n rel: optimizedRel,\n ref: ref,\n children: [/*#__PURE__*/_jsx(\"span\", {\n className: \"components-external-link__contents\",\n children: children\n }), /*#__PURE__*/_jsx(\"span\", {\n className: clsx('components-external-link__icon',\n // This class prevents the arrow from being replaced by a Twemoji image.\n 'wp-exclude-emoji'),\n \"aria-label\": /* translators: accessibility text */\n __('(opens in a new tab)'),\n children: isRTL() ? '\\u2196' : '\\u2197'\n })]\n })\n /* eslint-enable react/jsx-no-target-blank */;\n}\n\n/**\n * Link to an external resource.\n *\n * ```jsx\n * import { ExternalLink } from '@wordpress/components';\n *\n * const MyExternalLink = () => (\n * <ExternalLink href=\"https://wordpress.org\">WordPress.org</ExternalLink>\n * );\n * ```\n */\nexport const ExternalLink = forwardRef(UnforwardedExternalLink);\nExternalLink.displayName = 'ExternalLink';\nexport default ExternalLink;"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,kBAAiB;AAIjB,kBAA0B;AAC1B,qBAA2B;AAK3B,yBAA2C;AAC3C,SAAS,wBAAwB,OAAO,KAAK;AAC3C,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA,MAAM;AAAA,IACN,GAAG;AAAA,EACL,IAAI;AACJ,QAAM,eAAe,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,IAAI,MAAM,GAAG,GAAG,YAAY,UAAU,EAAE,OAAO,OAAO,CAAC,CAAC,EAAE,KAAK,GAAG;AACvG,QAAM,cAAU,YAAAA,SAAK,4BAA4B,SAAS;AAI1D,QAAM,mBAAmB,CAAC,CAAC,MAAM,WAAW,GAAG;AAC/C,QAAM,iBAAiB,WAAS;AAC9B,QAAI,kBAAkB;AACpB,YAAM,eAAe;AAAA,IACvB;AACA,QAAI,MAAM,SAAS;AACjB,YAAM,QAAQ,KAAK;AAAA,IACrB;AAAA,EACF;AACA;AAAA;AAAA,IAAmE,uCAAAC,MAAM,KAAK;AAAA,MAC5E,GAAG;AAAA,MACH,WAAW;AAAA,MACX;AAAA,MACA,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,KAAK;AAAA,MACL;AAAA,MACA,UAAU,CAAc,uCAAAC,KAAK,QAAQ;AAAA,QACnC,WAAW;AAAA,QACX;AAAA,MACF,CAAC,GAAgB,uCAAAA,KAAK,QAAQ;AAAA,QAC5B,eAAW,YAAAF;AAAA,UAAK;AAAA;AAAA,UAEhB;AAAA,QAAkB;AAAA,QAClB;AAAA;AAAA,cACA,gBAAG,sBAAsB;AAAA;AAAA,QACzB,cAAU,mBAAM,IAAI,WAAW;AAAA,MACjC,CAAC,CAAC;AAAA,IACJ,CAAC;AAAA;AAEH;AAaO,IAAM,mBAAe,2BAAW,uBAAuB;AAC9D,aAAa,cAAc;AAC3B,IAAO,wBAAQ;",
|
|
6
6
|
"names": ["clsx", "_jsxs", "_jsx"]
|
|
7
7
|
}
|