Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

text-field-edit

Package Overview
Dependencies
Maintainers
1
Versions
12
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

text-field-edit - npm Package Compare versions

Comparing version 4.0.0 to 4.1.0

15

index.d.ts

@@ -0,12 +1,14 @@

/** Call a function after focusing a field and then restore the previous focus afterwards if necessary */
declare function withFocus<T>(field: HTMLElement, callback: () => T): T;
/** Inserts `text` at the cursor’s position, replacing any selection, with **undo** support and by firing the `input` event. */
export declare function insertTextIntoField(field: HTMLTextAreaElement | HTMLInputElement, text: string): void;
export declare function insertTextIntoField(field: HTMLElement, text: string): void;
/** Replaces the entire content, equivalent to `field.value = text` but with **undo** support and by firing the `input` event. */
export declare function setFieldText(field: HTMLTextAreaElement | HTMLInputElement, text: string): void;
export declare function setFieldText(field: HTMLElement, text: string): void;
/** Get the selected text in a field or an empty string if nothing is selected. */
export declare function getFieldSelection(field: HTMLTextAreaElement | HTMLInputElement): string;
export declare function getFieldSelection(field: HTMLElement): string;
/** Adds the `wrappingText` before and after field’s selection (or cursor). If `endWrappingText` is provided, it will be used instead of `wrappingText` at on the right. */
export declare function wrapFieldSelection(field: HTMLTextAreaElement | HTMLInputElement, wrap: string, wrapEnd?: string): void;
type ReplacerCallback = (substring: string, ...args: any[]) => string;
export declare function wrapFieldSelection(field: HTMLElement, wrap: string, wrapEnd?: string): void;
type ReplacerCallback = (substring: string, ...arguments_: any[]) => string;
/** Finds and replaces strings and regex in the field’s value, like `field.value = field.value.replace()` but better */
export declare function replaceFieldText(field: HTMLTextAreaElement | HTMLInputElement, searchValue: string | RegExp, replacer: string | ReplacerCallback, cursor?: 'select' | 'after-replacement'): void;
export declare function replaceFieldText(field: HTMLInputElement | HTMLTextAreaElement, searchValue: string | RegExp, replacer: string | ReplacerCallback, cursor?: 'select' | 'after-replacement'): void;
/** @deprecated Import `insertTextIntoField` instead */

@@ -30,1 +32,2 @@ export declare const insert: typeof insertTextIntoField;

export default textFieldEdit;
export { withFocus as _TEST_ONLY_withFocus };

135

index.js

@@ -1,8 +0,25 @@

/** Inserts `text` at the cursor’s position, replacing any selection, with **undo** support and by firing the `input` event. */
export function insertTextIntoField(field, text) {
function isNativeField(field) {
return field instanceof HTMLInputElement || field instanceof HTMLTextAreaElement;
}
/** Call a function after focusing a field and then restore the previous focus afterwards if necessary */
function withFocus(field, callback) {
const document = field.ownerDocument;
const initialFocus = document.activeElement;
if (initialFocus !== field) {
if (initialFocus === field) {
return callback();
}
try {
field.focus();
return callback();
}
finally {
field.blur(); // Supports `intialFocus === body`
if (initialFocus instanceof HTMLElement) {
initialFocus.focus();
}
}
}
// This will insert into the focused field. It shouild always be called inside withFocus.
// Use this one locally if there are multiple `insertTextIntoField` or `document.execCommand` calls
function insertTextWhereverTheFocusIs(document, text) {
if (text === '') {

@@ -15,45 +32,104 @@ // https://github.com/fregante/text-field-edit/issues/16

}
if (initialFocus === document.body) {
field.blur();
}
else if (initialFocus instanceof HTMLElement && initialFocus !== field) {
initialFocus.focus();
}
}
/** Inserts `text` at the cursor’s position, replacing any selection, with **undo** support and by firing the `input` event. */
export function insertTextIntoField(field, text) {
withFocus(field, () => {
insertTextWhereverTheFocusIs(field.ownerDocument, text);
});
}
/** Replaces the entire content, equivalent to `field.value = text` but with **undo** support and by firing the `input` event. */
export function setFieldText(field, text) {
field.select();
insertTextIntoField(field, text);
if (isNativeField(field)) {
field.select();
insertTextIntoField(field, text);
}
else {
const document = field.ownerDocument;
withFocus(field, () => {
document.execCommand('selectAll', false, text);
insertTextWhereverTheFocusIs(document, text);
});
}
}
/** Get the selected text in a field or an empty string if nothing is selected. */
export function getFieldSelection(field) {
return field.value.slice(field.selectionStart, field.selectionEnd);
if (isNativeField(field)) {
return field.value.slice(field.selectionStart, field.selectionEnd);
}
const selection = field.ownerDocument.getSelection();
if (selection && field.contains(selection.anchorNode)) {
// The selection is inside the field
return selection.toString();
}
return '';
}
/** Adds the `wrappingText` before and after field’s selection (or cursor). If `endWrappingText` is provided, it will be used instead of `wrappingText` at on the right. */
export function wrapFieldSelection(field, wrap, wrapEnd) {
function wrapFieldSelectionNative(field, wrap, wrapEnd) {
const { selectionStart, selectionEnd } = field;
const selection = getFieldSelection(field);
insertTextIntoField(field, wrap + selection + (wrapEnd ?? wrap));
insertTextIntoField(field, wrap + selection + wrapEnd);
// Restore the selection around the previously-selected text
field.selectionStart = selectionStart + wrap.length;
field.selectionEnd = selectionEnd + wrap.length;
field.selectionEnd = selectionEnd + wrapEnd.length;
}
function collapseCursor(selection, range, toStart) {
const alteredRange = range.cloneRange();
alteredRange.collapse(toStart);
selection.removeAllRanges();
selection.addRange(alteredRange);
}
function wrapFieldSelectionContentEditable(field, before, after) {
const document = field.ownerDocument;
const selection = document.getSelection();
const selectionRange = selection.getRangeAt(0);
if (after) {
collapseCursor(selection, selectionRange, false);
insertTextIntoField(field, after);
}
if (before) {
collapseCursor(selection, selectionRange, true);
insertTextIntoField(field, before);
// The text added by at the beginning is included in the new selection, while wrapEnd isn't.
// This nudges the selection after the newly-inserted text.
selectionRange.setStart(selectionRange.startContainer, selectionRange.startOffset + before.length);
}
if (after ?? before) {
// Restore selection
selection.removeAllRanges();
selection.addRange(selectionRange);
}
}
/** Adds the `wrappingText` before and after field’s selection (or cursor). If `endWrappingText` is provided, it will be used instead of `wrappingText` at on the right. */
export function wrapFieldSelection(field, wrap,
// TODO: Ensure that it works regardless of direction
wrapEnd = wrap) {
if (isNativeField(field)) {
wrapFieldSelectionNative(field, wrap, wrapEnd);
}
else {
wrapFieldSelectionContentEditable(field, wrap, wrapEnd);
}
}
/** Finds and replaces strings and regex in the field’s value, like `field.value = field.value.replace()` but better */
export function replaceFieldText(field, searchValue, replacer, cursor = 'select') {
if (!isNativeField(field)) {
throw new TypeError('replaceFieldText only supports input and textarea fields');
}
/** Keeps track of how much each match offset should be adjusted */
let drift = 0;
field.value.replace(searchValue, (...args) => {
// Select current match to replace it later
const matchStart = drift + args.at(-2);
const matchLength = args[0].length;
field.selectionStart = matchStart;
field.selectionEnd = matchStart + matchLength;
const replacement = typeof replacer === 'string' ? replacer : replacer(...args);
insertTextIntoField(field, replacement);
if (cursor === 'select') {
// Select replacement. Without this, the cursor would be after the replacement
withFocus(field, () => {
field.value.replace(searchValue, (...arguments_) => {
// Select current match to replace it later
const matchStart = drift + arguments_.at(-2);
const matchLength = arguments_[0].length;
field.selectionStart = matchStart;
}
drift += replacement.length - matchLength;
return replacement;
field.selectionEnd = matchStart + matchLength;
const replacement = typeof replacer === 'string' ? replacer : replacer(...arguments_);
insertTextWhereverTheFocusIs(field.ownerDocument, replacement);
if (cursor === 'select') {
// Select replacement. Without this, the cursor would be after the replacement
field.selectionStart = matchStart;
}
drift += replacement.length - matchLength;
return replacement;
});
});

@@ -80,1 +156,2 @@ }

export default textFieldEdit;
export { withFocus as _TEST_ONLY_withFocus };
{
"name": "text-field-edit",
"version": "4.0.0",
"version": "4.1.0",
"description": "Insert text in a `<textarea>` and `<input>` (including Undo in most browsers)",

@@ -39,6 +39,6 @@ "keywords": [

"test": "npm-run-all --silent build --parallel test:*",
"test:blink": "browserify -p esmify test.js | tape-run --browser chrome",
"test:gecko": "browserify -p esmify test.js | tape-run --browser firefox",
"test:blink": "browserify -p esmify index.test.js | tape-run --browser chrome",
"test:gecko": "browserify -p esmify index.test.js | tape-run --browser firefox",
"test:lint": "xo",
"watch": "tsc --watch"
"watch": "tsc --watch --noEmitOnError false"
},

@@ -48,18 +48,14 @@ "xo": {

"browser"
],
"rules": {
"@typescript-eslint/no-unnecessary-type-assertion": "off",
"@typescript-eslint/prefer-nullish-coalescing": "off",
"@typescript-eslint/prefer-readonly-parameter-types": "off"
}
]
},
"devDependencies": {
"@sindresorhus/tsconfig": "^4.0.0",
"@sindresorhus/tsconfig": "^5.0.0",
"@types/tape": "^5.6.4",
"browserify": "^17.0.0",
"esmify": "^2.1.1",
"npm-run-all": "^4.1.5",
"tape": "^5.6.6",
"tape-run": "^10.0.0",
"typescript": "^5.2.2",
"xo": "^0.56.0"
"tape": "^5.7.5",
"tape-run": "^11.0.0",
"typescript": "^5.3.3",
"xo": "^0.57.0"
},

@@ -66,0 +62,0 @@ "engines": {

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc